view music2/music.cc @ 67:419761c8d9b9

Add configure option to build scn2kdump.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Wed, 23 Feb 2011 01:48:51 +0100
parents 4416cfac86ae
children
line wrap: on
line source

/*
 * 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;

MuSys * MuSys::_singleton = NULL;

MuSys* MuSys::GetInstance(void)
{
	if (_singleton == NULL)
		_singleton = new MuSys;
	return _singleton;
}

void MuSys::Quit(void)
{
	if (_singleton != NULL) {
		_singleton->FinalizeMusic();
		delete _singleton;
		_singleton = NULL;
	}
}

MuSys::MuSys() : movie_id(-1), music_enable(1) {
	int i;
	config = AyuSysConfig::GetInstance();
	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) {
	config->GetParam("#VOLMOD", 4, &volmod[0], &volmod[1], &volmod[2], &volmod[3]);

	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) != NULL)
		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 == NULL) 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;
}

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;
	int *volmod;
	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;
	}

	char* stream_dup = new char[len];
	count = chunk->wav->Read( (char*)stream_dup, 4, len/4);

	if (count != len/4) {
		// 最後まで再生した
		if (chunk->loop_pt == -1) { // 終了
			chunk->loop_pt = -2;
			memset(stream_dup+count*4, 0, len-count*4);
		} else {
			chunk->wav->Seek(chunk->loop_pt);
			chunk->wav->Read( (char*)(stream_dup+count*4), 4, len/4-count);
		}
	}
	
	int cur_vol = (*chunk->volmod)*SDL_MIX_MAXVOLUME/255;
	
	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);
			delete[] stream_dup;
			return;
		}
		cur_vol = cur_vol*(count_total-fadecount)/count_total;
		fadecount += len/4;
	}

	SDL_MixAudio(stream, (Uint8*)stream_dup, len, cur_vol);
	delete[] stream_dup;

	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) {
	MuSys* mu = MuSys::GetInstance();

	if (!mu->pcm_enable) return;
fprintf(stderr,"bgm start %s\n",path);
	WAVFILE* wav = OpenWaveFile(path);
	if (wav == NULL) return;
	Mix_PauseMusic();
	Mix_HaltMusic();
	Mix_HookMusic(0,0);
	/* 前に再生していたのを終了 */
	if (wav_playing.wav != NULL) {
		delete wav_playing.wav;
		wav_playing.wav = NULL;
	}
	wav_playing.wav = wav;
	wav_playing.loop_pt = loop_pt;
	wav_playing.volmod = mu->volmod;
	fadetime_total = 0;
	fadecount = 0;
	Mix_HookMusic( &(WavChunk::callback), (void*)&wav_playing);
}

void effec_start(int chn, const char* path, int loop, int fadein_time) {
	MuSys* mu = MuSys::GetInstance();

	if (!mu->pcm_enable) return;

	SDL_RWops* op = OpenSDLRW(path);
	if (op == NULL) { // ファイルが見付からない
		return;
	}
	Mix_Pause(chn);

	if (mu->play_chunk[chn] != NULL) {
		Mix_FreeChunk(mu->play_chunk[chn]);
	}
	mu->play_chunk[chn] = Mix_LoadWAV_RW(op, 1);
	if (fadein_time <= 0) {
		Mix_Volume(chn, mu->volmod[3]*SDL_MIX_MAXVOLUME/255);
		Mix_PlayChannel(chn, mu->play_chunk[chn], loop);
	} else {
		Mix_Volume(chn, mu->volmod[3]*SDL_MIX_MAXVOLUME/255);
		Mix_FadeInChannel(chn, mu->play_chunk[chn], loop, fadein_time);
	}
}

void MuSys::PlayKoe(const char* path) {
	if (!pcm_enable) return;

	MuSys* mu = MuSys::GetInstance();

	static char* playing_koedata = NULL;
	int len = 0;
	AvgKoeInfo koeinfo;
	int chn = MIX_PCM_KOE;

	Mix_Pause(chn);
	Mix_HaltChannel(chn); // これで RWop が解放されるはず…
	if (mu->play_chunk[chn] != NULL) {
		Mix_FreeChunk(mu->play_chunk[chn]);
		mu->play_chunk[chn] = NULL;
	}

	if (playing_koedata != NULL) {
		free(playing_koedata);
		playing_koedata = NULL;
	}

	koeinfo = OpenKoeFile(path);

	if (koeinfo.stream == NULL) return;
	playing_koedata = decode_koe(koeinfo, &len);
	fclose(koeinfo.stream);
	if (playing_koedata == NULL) {
		return;
	}
	Mix_Volume(chn, mu->volmod[1]*SDL_MIX_MAXVOLUME/255);
	mu->play_chunk[chn] = Mix_LoadWAV_RW(SDL_RWFromMem(playing_koedata, len+0x2c), 1);
	Mix_PlayChannel(chn, mu->play_chunk[chn], 0);
}

AvgKoeInfo OpenKoeFile(const char* path) {
	int radix = 10000;
	/* if (global_system.Version() >= 2) */ radix *= 10;
	AvgKoeInfo info;
	info.stream = NULL;
	info.length = 0;
	info.offset = 0;
	if (isdigit(path[0]) && strchr(path,'.') == NULL) { // 数値 (拡張子等なし)
		/* avg32 形式の音声アーカイブのキャッシュを検索 */
		int pointer = atoi(path);
		int file_no = pointer / radix;
		int index = pointer % radix;
		info = FindKoe(file_no, index);
	} else { // ファイル
		int length;
		ARCINFO* arcinfo = FileSearcher::GetInstance()->Find(FileSearcher::KOE, path, ".WPD");
		if (arcinfo == NULL) 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) {
	/* まず wav ファイルを探す */
	FileSearcher* file_searcher = FileSearcher::GetInstance();
	ARCINFO* info = file_searcher->Find(FileSearcher::WAV, path, ".wav");
	if (info == NULL) {
		info = file_searcher->Find(FileSearcher::WAV, path, ".nwa");
		if (info == NULL) info = file_searcher->Find(FileSearcher::BGM, path, "nwa");
		if (info != NULL) { // read NWA file
			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 == NULL) info = file_searcher->Find(FileSearcher::BGM, path, "wav");
	if (info == NULL) info = file_searcher->Find(FileSearcher::WAV, path, ".ogg");
	if (info != NULL) {
		int dummy;
		FILE* f = info->OpenFile(&dummy);
		delete info;
		if (f == NULL) return NULL;
		SDL_RWops* op = SDL_RWFromFP(f, 1);
		return op;
	}
	return NULL;
}

static WAVFILE* OpenWaveFile(const char* path) {
	/* まず wav ファイルを探す */
	FileSearcher* file_searcher = FileSearcher::GetInstance();
	ARCINFO* info = file_searcher->Find(FileSearcher::WAV, path, ".wav");
	if (info == NULL) info = file_searcher->Find(FileSearcher::BGM, path, "wav");
	if (info != NULL) {
		int size;
		FILE* f = info->OpenFile(&size);
		delete info;
		if (f == NULL) return NULL;
		WAVFILE* w = WAVFILE::MakeConverter(new WAVFILE_Stream(f, size));
		return w;
	}
	/* 次に nwa ファイル */
	info = file_searcher->Find(FileSearcher::WAV, path, ".nwa");
	if (info == NULL) info = file_searcher->Find(FileSearcher::BGM, path, "nwa");
	if (info != NULL) {
		int size;
		FILE* f = info->OpenFile(&size);
		delete info;
		if (f == NULL) return NULL;
		WAVFILE* w = WAVFILE::MakeConverter(new NWAFILE(f));
		return w;
	}

	/* 次に mp3 ファイル */
	info = file_searcher->Find(FileSearcher::WAV, path, ".mp3");
	if (info == NULL) info = file_searcher->Find(FileSearcher::BGM, path, "mp3");
	if (info != NULL) {
		int size;
		FILE* f = info->OpenFile(&size);
		delete info;
		if (f == NULL) return NULL;
		MP3FILE* w = new MP3FILE(f, size);
		if (w->pimpl != NULL) {
			return WAVFILE::MakeConverter(w);
		}
		delete w;
	}

	/* 次に ogg ファイル */
	info = file_searcher->Find(FileSearcher::WAV, path, ".ogg");
	if (info == NULL) info = file_searcher->Find(FileSearcher::BGM, path, "ogg");
	if (info != NULL) {
		int size;
		FILE* f = info->OpenFile(&size);
		delete info;
		if (f == NULL) return NULL;
		OggFILE* w = new OggFILE(f, size);
		if (w->pimpl != NULL) {
			return WAVFILE::MakeConverter(w);
		}
		delete w;
	}
	return NULL;
}