1 /* ScummVM - Graphic Adventure Engine
2  *
3  * ScummVM is the legal property of its developers, whose names
4  * are too numerous to list here. Please refer to the COPYRIGHT
5  * file distributed with this source distribution.
6  *
7  * This program is free software; you can redistribute it and/or
8  * modify it under the terms of the GNU General Public License
9  * as published by the Free Software Foundation; either version 2
10  * of the License, or (at your option) any later version.
11  *
12  * This program is distributed in the hope that it will be useful,
13  * but WITHOUT ANY WARRANTY; without even the implied warranty of
14  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15  * GNU General Public License for more details.
16  *
17  * You should have received a copy of the GNU General Public License
18  * along with this program; if not, write to the Free Software
19  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
20  *
21  */
22 
23 #include "kyra/sound/sound_intern.h"
24 #include "kyra/resource/resource.h"
25 
26 #include "audio/softsynth/fmtowns_pc98/towns_pc98_driver.h"
27 
28 #include "common/config-manager.h"
29 
30 #include "backends/audiocd/audiocd.h"
31 
32 #include "audio/audiostream.h"
33 #include "audio/decoders/raw.h"
34 
35 namespace Kyra {
36 
SoundTownsPC98_v2(KyraEngine_v1 * vm,Audio::Mixer * mixer)37 SoundTownsPC98_v2::SoundTownsPC98_v2(KyraEngine_v1 *vm, Audio::Mixer *mixer) :
38 	Sound(vm, mixer), _currentSFX(0), _musicTrackData(0), _sfxTrackData(0), _lastTrack(-1), _driver(0), _useFmSfx(false), _currentResourceSet(0) {
39 	memset(&_resInfo, 0, sizeof(_resInfo));
40 }
41 
~SoundTownsPC98_v2()42 SoundTownsPC98_v2::~SoundTownsPC98_v2() {
43 	delete[] _musicTrackData;
44 	delete[] _sfxTrackData;
45 	delete _driver;
46 	for (int i = 0; i < 3; i++)
47 		initAudioResourceInfo(i, 0);
48 }
49 
init()50 bool SoundTownsPC98_v2::init() {
51 	_driver = new TownsPC98_AudioDriver(_mixer, _vm->gameFlags().platform == Common::kPlatformPC98 ?
52 		TownsPC98_AudioDriver::kType86 : TownsPC98_AudioDriver::kTypeTowns);
53 
54 	if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
55 		if (_resInfo[_currentResourceSet])
56 			if (_resInfo[_currentResourceSet]->cdaTableSize) {
57 				if (!_vm->existExtractedCDAudioFiles()
58 				    && !_vm->isDataAndCDAudioReadFromSameCD()) {
59 					_vm->warnMissingExtractedCDAudio();
60 				}
61 			}
62 
63 		// Initialize CD for audio
64 		bool hasRealCD = g_system->getAudioCDManager()->open();
65 
66 		// FIXME: While checking for 'track1.XXX(X)' looks like
67 		// a good idea, we should definitely not be doing this
68 		// here. Basically our filenaming scheme could change
69 		// or we could add support for other audio formats. Also
70 		// this misses the possibility that we play the tracks
71 		// right off CD. So we should find another way to
72 		// check if we have access to CD audio.
73 		Resource *r = _vm->resource();
74 		if (_musicEnabled &&
75 		    (hasRealCD || r->exists("track1.mp3") || r->exists("track1.ogg") || r->exists("track1.flac") || r->exists("track1.fla")
76 		     || r->exists("track01.mp3") || r->exists("track01.ogg") || r->exists("track01.flac") || r->exists("track01.fla")))
77 				_musicEnabled = 2;
78 		else
79 			_musicEnabled = 1;
80 		_useFmSfx = false;
81 
82 	} else {
83 		_useFmSfx = true;
84 	}
85 
86 	bool reslt = _driver->init();
87 	updateVolumeSettings();
88 	return reslt;
89 }
90 
initAudioResourceInfo(int set,void * info)91 void SoundTownsPC98_v2::initAudioResourceInfo(int set, void *info) {
92 	if (set >= kMusicIntro && set <= kMusicFinale) {
93 		delete _resInfo[set];
94 		_resInfo[set] = info ? new SoundResourceInfo_TownsPC98V2(*(SoundResourceInfo_TownsPC98V2*)info) : 0;
95 	}
96 }
97 
selectAudioResourceSet(int set)98 void SoundTownsPC98_v2::selectAudioResourceSet(int set) {
99 	if (set >= kMusicIntro && set <= kMusicFinale) {
100 		if (_resInfo[set])
101 			_currentResourceSet = set;
102 	}
103 }
104 
hasSoundFile(uint file) const105 bool SoundTownsPC98_v2::hasSoundFile(uint file) const {
106 	if (file < res()->fileListSize)
107 		return (res()->fileList[file] != 0);
108 	return false;
109 }
110 
loadSoundFile(Common::String file)111 void SoundTownsPC98_v2::loadSoundFile(Common::String file) {
112 	delete[] _sfxTrackData;
113 	_sfxTrackData = _vm->resource()->fileData(file.c_str(), 0);
114 }
115 
process()116 void SoundTownsPC98_v2::process() {
117 	g_system->getAudioCDManager()->update();
118 }
119 
playTrack(uint8 track)120 void SoundTownsPC98_v2::playTrack(uint8 track) {
121 	if (track == _lastTrack && _musicEnabled)
122 		return;
123 
124 	int trackNum = -1;
125 	if (_vm->gameFlags().platform == Common::kPlatformFMTowns) {
126 		for (uint i = 0; i < res()->cdaTableSize; i++) {
127 			if (track == (uint8)READ_LE_UINT16(&res()->cdaTable[i * 2])) {
128 				trackNum = (int)READ_LE_UINT16(&res()->cdaTable[i * 2 + 1]) - 1;
129 				break;
130 			}
131 		}
132 	}
133 
134 	beginFadeOut();
135 
136 	Common::String musicFile = res()->pattern ? Common::String::format(res()->pattern, track) : (res()->fileList ? res()->fileList[track] : 0);
137 	if (musicFile.empty())
138 		return;
139 
140 	delete[] _musicTrackData;
141 
142 	_musicTrackData = _vm->resource()->fileData(musicFile.c_str(), 0);
143 	_driver->loadMusicData(_musicTrackData, true);
144 
145 	if (_musicEnabled == 2 && trackNum != -1) {
146 		g_system->getAudioCDManager()->play(trackNum+1, _driver->looping() ? -1 : 1, 0, 0);
147 		g_system->getAudioCDManager()->update();
148 	} else if (_musicEnabled) {
149 		_driver->cont();
150 	}
151 
152 	_lastTrack = track;
153 }
154 
haltTrack()155 void SoundTownsPC98_v2::haltTrack() {
156 	_lastTrack = -1;
157 	g_system->getAudioCDManager()->stop();
158 	g_system->getAudioCDManager()->update();
159 	_driver->reset();
160 }
161 
beginFadeOut()162 void SoundTownsPC98_v2::beginFadeOut() {
163 	if (!_driver->musicPlaying())
164 		return;
165 
166 	for (int i = 0; i < 20; i++) {
167 		_driver->fadeStep();
168 		_vm->delay(32);
169 	}
170 
171 	haltTrack();
172 }
173 
voicePlay(const char * file,Audio::SoundHandle * handle,uint8 volume,uint8 priority,bool)174 int32 SoundTownsPC98_v2::voicePlay(const char *file, Audio::SoundHandle *handle, uint8 volume, uint8 priority, bool) {
175 	static const uint16 rates[] = { 0x10E1, 0x0CA9, 0x0870, 0x0654, 0x0438, 0x032A, 0x021C, 0x0194 };
176 	static const char patternHOF[] = "%s.PCM";
177 	static const char patternLOL[] = "%s.VOC";
178 
179 	int h = 0;
180 	if (_currentSFX) {
181 		while (h < kNumChannelHandles && _mixer->isSoundHandleActive(_soundChannels[h].handle))
182 			h++;
183 
184 		if (h >= kNumChannelHandles) {
185 			h = 0;
186 			while (h < kNumChannelHandles && _soundChannels[h].priority > priority)
187 				++h;
188 			if (h < kNumChannelHandles)
189 				voiceStop(&_soundChannels[h].handle);
190 		}
191 
192 		if (h >= kNumChannelHandles)
193 			return 0;
194 	}
195 
196 	Common::String fileName = Common::String::format( _vm->game() == GI_LOL ? patternLOL : patternHOF, file);
197 
198 	uint8 *data = _vm->resource()->fileData(fileName.c_str(), 0);
199 	uint8 *src = data;
200 	if (!src)
201 		return 0;
202 
203 	uint16 sfxRate = rates[READ_LE_UINT16(src)];
204 	src += 2;
205 	bool compressed = (READ_LE_UINT16(src) & 1) ? true : false;
206 	src += 2;
207 	uint32 outsize = READ_LE_UINT32(src);
208 	uint8 *sfx = (uint8 *)malloc(outsize);
209 	uint8 *dst = sfx;
210 	src += 4;
211 
212 	if (compressed) {
213 		for (uint32 i = outsize; i;) {
214 			uint8 cnt = *src++;
215 			if (cnt & 0x80) {
216 				cnt &= 0x7F;
217 				memset(dst, *src++, cnt);
218 			} else {
219 				memcpy(dst, src, cnt);
220 				src += cnt;
221 			}
222 			dst += cnt;
223 			i -= cnt;
224 		}
225 	} else {
226 		memcpy(dst, src, outsize);
227 	}
228 
229 	for (uint32 i = 0; i < outsize; i++) {
230 		uint8 cmd = sfx[i];
231 		if (cmd & 0x80) {
232 			cmd = ~cmd;
233 		} else {
234 			cmd |= 0x80;
235 			if (cmd == 0xFF)
236 				cmd--;
237 		}
238 		if (cmd < 0x80)
239 			cmd = 0x80 - cmd;
240 		sfx[i] = cmd;
241 	}
242 
243 	_currentSFX = Audio::makeRawStream(sfx, outsize, sfxRate * 10, Audio::FLAG_UNSIGNED | Audio::FLAG_LITTLE_ENDIAN);
244 	_mixer->playStream(Audio::Mixer::kSFXSoundType, &_soundChannels[h].handle, _currentSFX, -1, volume);
245 	_soundChannels[h].priority = priority;
246 	if (handle)
247 		*handle = _soundChannels[h].handle;
248 
249 	delete[] data;
250 	return 1;
251 }
252 
playSoundEffect(uint16 track,uint8)253 void SoundTownsPC98_v2::playSoundEffect(uint16 track, uint8) {
254 	if (!_useFmSfx || !_sfxTrackData)
255 		return;
256 
257 	_driver->loadSoundEffectData(_sfxTrackData, track);
258 }
259 
updateVolumeSettings()260 void SoundTownsPC98_v2::updateVolumeSettings() {
261 	if (!_driver)
262 		return;
263 
264 	bool mute = false;
265 	if (ConfMan.hasKey("mute"))
266 		mute = ConfMan.getBool("mute");
267 
268 	_driver->setMusicVolume((mute ? 0 : ConfMan.getInt("music_volume")));
269 	_driver->setSoundEffectVolume((mute ? 0 : ConfMan.getInt("sfx_volume")));
270 }
271 
272 } // End of namespace Kyra
273 
274