Mercurial > otakunoraifu
diff music2/music.cc @ 0:223b71206888
Initial import
author | thib |
---|---|
date | Fri, 01 Aug 2008 16:32:45 +0000 |
parents | |
children | c4af1e9ab8d1 |
line wrap: on
line diff
new file mode 100644 --- /dev/null +++ b/music2/music.cc @@ -0,0 +1,416 @@ +/* + * Copyright (c) 2004-2006 Kazunori "jagarl" Ueno + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. The name of the author may not be used to endorse or promote products + * derived from this software without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR + * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES + * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, + * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT + * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF + * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ + +/* music.cc SDL_mixer を用いた音楽再生ルーチン */ + + +#include <string.h> +#include <stdio.h> +#include <unistd.h> +#include <fcntl.h> +#include <ctype.h> +#include <signal.h> +#include"system/system_config.h" +#include"system/file.h" +#include "music.h" +#include<SDL.h> +#include<SDL_mixer.h> +#include"wavfile.h" + +using namespace std; + +int pcm_enable = 0; +Mix_Chunk *play_chunk[MIX_PCM_SIZE]; + +MuSys::MuSys(AyuSysConfig& _config) : config(_config), movie_id(-1), music_enable(1) { + int i; + for (i=0; i<MIX_PCM_SIZE; i++) + play_chunk[i] = 0; + cdrom_track[0] = 0; + effec_track[0] = 0; +} + + +// #define delete fprintf(stderr,"smus.cc: %d.",__LINE__), delete + +void bgm_start(const char* path, int loop_pt); +void effec_start(int chn, const char* path, int loop, int fadein_time); +void bgm_fadeout(int time); + +void MuSys::PlayCDROM(char* name, int play_count) { + char wave[128]; wave[127] = '\0'; wave[0] = '\0'; + + strcpy(cdrom_track, name); + + StopCDROM(0); + strcpy(cdrom_track, name); + + /* name -> track */ + int track =config.track_name.CDTrack(name); + if (track == -1) track = atoi(name); + if (config.track_name.WaveTrack(name) != 0) strncpy(wave, config.track_name.WaveTrack(name), 127); + if (wave[0] == 0 && track != 0) { /* DSTRACK が見つからない場合、CDTRACKを使用する */ + sprintf(wave, "audio_%02d",track); + } + if (wave == 0) return; + // BGM 再生 + if (!pcm_enable) return; + if (play_count == 0) + bgm_start(wave, -1); + else + bgm_start(wave, config.track_name.TrackStart(name)); + return; +} + +void MuSys::StopCDROM(int time) +{ + cdrom_track[0] = '\0'; + if (!pcm_enable) return; + bgm_fadeout(time); +} + +void MuSys::PlaySE(const char* se, int loop_flag, int channel) { + if (! pcm_enable) return; + if (loop_flag) + effec_start(MIX_PCM_EFFEC, se, 10000, 0); + else + effec_start(MIX_PCM_EFFEC, se, 0, 0); + return; +} +void MuSys::PlaySE(int number) { + if (! pcm_enable) return; + const char* se_name = config.track_name.SETrack(number); + if (se_name == 0) return; + effec_start(MIX_PCM_EFFEC, se_name, 0, 0); + return; +} +void MuSys::StopSE(int time) { + if (! pcm_enable) return; + if (time == 0) + Mix_HaltChannel(MIX_PCM_EFFEC); + else + Mix_FadeOutChannel(MIX_PCM_EFFEC, time); +} +bool MuSys::IsStopSE(void) { + if (! pcm_enable) return true; + if (Mix_Playing(MIX_PCM_EFFEC) != 0) return false; + return true; +} + +void MuSys::StopKoe(int time) { + if (! pcm_enable) return; + if (time == 0) Mix_HaltChannel(MIX_PCM_KOE); + else Mix_FadeOutChannel(MIX_PCM_KOE, time); +} + +void MuSys::InitMusic(void) +{ + if (music_enable != 1) return; + cdrom_track[0] = '\0'; + if ( Mix_OpenAudio( 44100, MIX_DEFAULT_FORMAT, MIX_DEFAULT_CHANNELS, DEFAULT_AUDIOBUF ) < 0 ){ +// if ( Mix_OpenAudio( 48000, MIX_DEFAULT_FORMAT, MIX_DEFAULT_CHANNELS, DEFAULT_AUDIOBUF ) < 0 ){ + return; + } + int freq, channels; Uint16 format; + if ( Mix_QuerySpec(&freq, &format, &channels) ) { + WAVFILE::freq = freq; + WAVFILE::format = format; + WAVFILE::channels = channels; + } + pcm_enable = 1; + Mix_AllocateChannels( MIX_PCM_SIZE); + music_enable = 2; + return; +} +void MuSys::FinalizeMusic(void) +{ + if (music_enable != 2) return; + int i; + for (i=0; i<MIX_PCM_SIZE; i++) { + Mix_HaltChannel(i); + if (play_chunk[i]) { + Mix_FreeChunk(play_chunk[i]); + } + play_chunk[i] = 0; + } + Mix_HaltMusic(); + Mix_HookMusic(0,0); + Mix_CloseAudio(); + pcm_enable = 0; + music_enable = 1; +} + +/************************************************************************* +** +** ファイル読み込み / 外部コマンド呼び出し +*/ + +struct WavChunk { + WAVFILE* wav; + int loop_pt; + static void callback(void* userdata, Uint8* stream, int len); +}; +WavChunk wav_playing; +static int fadetime_total; +static int fadecount; + +void WavChunk::callback(void *userdata, Uint8 *stream, int len) +{ + WavChunk* chunk = (WavChunk*)userdata; + int count; + if (chunk->loop_pt == -2) { // 再生終了後 + memset(stream, 0, len); + return; + } + count = chunk->wav->Read( (char*)stream, 4, len/4); + + if (count != len/4) { + memset(stream+count*4, 0, len-count*4); + // 最後まで再生した + if (chunk->loop_pt == -1) { // 終了 + chunk->loop_pt = -2; + } else { + chunk->wav->Seek(chunk->loop_pt); + chunk->wav->Read( (char*)(stream+count*4), 4, len/4-count); + } + } + if (fadetime_total) { + // 音楽を停止中 (fade out) + int count_total = fadetime_total*(WAVFILE::freq/1000); + if (fadecount > count_total || fadetime_total == 1) { // 音楽停止 + chunk->loop_pt = -2; + memset(stream, 0, len); + return; + } + // int cur_vol = 256*(count_total-fadecount)/count_total; + int cur_vol = SDL_MIX_MAXVOLUME*(count_total-fadecount)/count_total; + char* stream_dup = new char[len]; + memcpy(stream_dup, stream, len); + memset(stream, 0, len); + SDL_MixAudio(stream, (Uint8*)stream_dup, len, cur_vol); + fadecount += len/4; + } + return; +} +void bgm_fadeout(int time) { + fadecount = 0; + if (time <= 0) time = 1; + fadetime_total = time; +} + +static SDL_RWops* OpenSDLRW(const char* path); +static WAVFILE* OpenWaveFile(const char* path); +void bgm_start(const char* path, int loop_pt) { + if (! pcm_enable) return; +fprintf(stderr,"bgm start %s\n",path); + WAVFILE* wav = OpenWaveFile(path); + if (wav == 0) return; + Mix_PauseMusic(); + Mix_HaltMusic(); + Mix_HookMusic(0,0); + /* 前に再生していたのを終了 */ + if (wav_playing.wav) { + delete wav_playing.wav; + wav_playing.wav = 0; + } + wav_playing.wav = wav; + wav_playing.loop_pt = loop_pt; + fadetime_total = 0; + fadecount = 0; + Mix_HookMusic( &(WavChunk::callback), (void*)&wav_playing); + Mix_VolumeMusic(128); + return; +} + +void effec_start(int chn, const char* path, int loop, int fadein_time) { + if (! pcm_enable) return; + SDL_RWops* op = OpenSDLRW(path); + if (op == 0) { // ファイルが見付からない + return; + } + Mix_Pause(chn); + + if (play_chunk[chn]) { + Mix_FreeChunk(play_chunk[chn]); + } + play_chunk[chn] = Mix_LoadWAV_RW(op, 1); + if (fadein_time <= 0) { + Mix_Volume(chn, 128); + Mix_PlayChannel(chn, play_chunk[chn],loop); + } else { + Mix_Volume(chn, 128); + Mix_FadeInChannel(chn, play_chunk[chn],loop,fadein_time); + } + return; +} + +void MuSys::PlayKoe(const char* path) { + if (! pcm_enable) return; + static char* playing_koedata = 0; + int len = 0; + AvgKoeInfo koeinfo; + int chn = MIX_PCM_KOE; + + Mix_Pause(chn); + Mix_HaltChannel(chn); // これで RWop が解放されるはず… + if (play_chunk[chn]) { + Mix_FreeChunk(play_chunk[chn]); + play_chunk[chn] = 0; + } + + if (playing_koedata) { + delete[] playing_koedata; + playing_koedata = 0; + } + + koeinfo = OpenKoeFile(path); + + if (koeinfo.stream == 0) return; + playing_koedata = decode_koe(koeinfo, &len); + fclose(koeinfo.stream); + if (playing_koedata == 0) { + return; + } + Mix_Volume(chn, 128); + play_chunk[chn] = Mix_LoadWAV_RW(SDL_RWFromMem(playing_koedata, len+0x2c), 1); + Mix_PlayChannel(chn, play_chunk[chn],0); + return; +} +AvgKoeInfo OpenKoeFile(const char* path) { + int radix = 10000; + /* if (global_system.Version() >= 2) */ radix *= 10; + AvgKoeInfo info; + info.stream = 0; info.length = 0; info.offset = 0; + if (isdigit(path[0]) && strchr(path,'.') == 0) { // 数値 (拡張子等なし) + /* avg32 形式の音声アーカイブのキャッシュを検索 */ + int pointer = atoi(path); + int file_no = pointer / radix; + int index = pointer % radix; + info = FindKoe(file_no, index); + } else { // ファイル + int length; + ARCINFO* arcinfo = file_searcher.Find(FILESEARCH::KOE,path,".WPD"); + if (arcinfo == 0) return info; + info.stream = arcinfo->OpenFile(&length); + info.rate = 22050; + info.length = length; + info.offset = ftell(info.stream); + info.type = koe_unknown; + delete arcinfo; + } + return info; +} + +static SDL_RWops* OpenSDLRW(const char* path) { + char cmdline_buf[1024]; + /* まず wav ファイルを探す */ + ARCINFO* info = file_searcher.Find(FILESEARCH::WAV,path,".wav"); + if (info == 0) { + info = file_searcher.Find(FILESEARCH::WAV,path,".nwa"); + if (info == 0) info = file_searcher.Find(FILESEARCH::BGM,path,"nwa"); + if (info) { // read NWA file + + static char* nwa_buffer = 0; + int dummy; + FILE* f = info->OpenFile(&dummy); + static char* d = 0; + int sz; + if (d != 0) delete[] d; + d = NWAFILE::ReadAll(f, sz); + return SDL_RWFromMem(d, sz); + } + } + if (info == 0) info = file_searcher.Find(FILESEARCH::BGM,path,"wav"); + if (info == 0) info = file_searcher.Find(FILESEARCH::WAV,path,".ogg"); + if (info) { + int dummy; + FILE* f = info->OpenFile(&dummy); + delete info; + if (f == 0) return 0; + SDL_RWops* op = SDL_RWFromFP(f, 1); + return op; + } + return 0; +} + +static WAVFILE* OpenWaveFile(const char* path) { + char cmdline_buf[1024]; + /* まず wav ファイルを探す */ + ARCINFO* info = file_searcher.Find(FILESEARCH::WAV,path,".wav"); + if (info == 0) info = file_searcher.Find(FILESEARCH::BGM,path,"wav"); + if (info) { + int size; + FILE* f = info->OpenFile(&size); + delete info; + if (f == 0) return 0; + WAVFILE* w = WAVFILE::MakeConverter(new WAVFILE_Stream(f, size)); + return w; + } + /* 次に nwa ファイル */ + info = file_searcher.Find(FILESEARCH::WAV,path,".nwa"); + if (info == 0) info = file_searcher.Find(FILESEARCH::BGM,path,"nwa"); + if (info) { + int size; + FILE* f = info->OpenFile(&size); + delete info; + if (f == 0) return 0; + WAVFILE* w = WAVFILE::MakeConverter(new NWAFILE(f)); + return w; + } + + /* 次に mp3 ファイル */ + info = file_searcher.Find(FILESEARCH::WAV,path,".mp3"); + if (info == 0) info = file_searcher.Find(FILESEARCH::BGM,path,"mp3"); + if (info) { + int size; + FILE* f = info->OpenFile(&size); + delete info; + if (f == 0) return 0; + MP3FILE* w = new MP3FILE(f, size); + if (w->pimpl) { + return WAVFILE::MakeConverter(w); + } + delete w; + } + + /* 次に ogg ファイル */ + info = file_searcher.Find(FILESEARCH::WAV,path,".ogg"); + if (info == 0) info = file_searcher.Find(FILESEARCH::BGM,path,"ogg"); + if (info) { + int size; + FILE* f = info->OpenFile(&size); + delete info; + if (f == 0) return 0; + OggFILE* w = new OggFILE(f, size); + if (w->pimpl) { + return WAVFILE::MakeConverter(w); + } + delete w; + } + return 0; +} +