/* ScummVM - Graphic Adventure Engine * * ScummVM is the legal property of its developers, whose names * are too numerous to list here. Please refer to the COPYRIGHT * file distributed with this source distribution. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . * */ //define MIX_INIT_FLUIDSYNTH MIX_INIT_MID // renamed with SDL2_mixer >= 2.0.2 #include "audio/mixer.h" #include "audio/audiostream.h" #include "audio/mididrv.h" #include "audio/midiparser.h" #include "audio/decoders/raw.h" #include "audio/decoders/wave.h" #include "common/memstream.h" #include "awe/aifc_player.h" #include "awe/sound.h" #include "awe/sfx_player.h" namespace Awe { void Sound::playMusic(const char *path, int loops) { warning("TODO: playMusic"); } void Sound::playSfxMusic(int num) { _mixer->playStream(Audio::Mixer::kMusicSoundType, &_musicHandle, _sfxStream, -1, 255, 0, DisposeAfterUse::YES, true); _sfx->play(_mixer->getOutputRate()); } void Sound::playAifcMusic(const char *path, uint32 offset) { warning("TODO: playAifcMusic"); } void Sound::stopMusic() { warning("TODO: stopMusic"); } void Sound::stopAifcMusic() { warning("TODO: stopAifcMusic"); } void Sound::stopAll() { stopSfxMusic(); _mixer->stopAll(); } void Sound::setPlayer(SfxPlayer *player) { _sfx = player; _sfxStream = new SfxMusicStream(player); } void Sound::stopSfxMusic() { if (_mixer->isSoundHandleActive(_musicHandle)) { _mixer->stopHandle(_musicHandle); } } void Sound::preloadSoundAiff(byte num, const byte *data) { warning("TODO: preloadSoundAiff"); } void Sound::playSoundRaw(byte channel, const byte *data, size_t size, int freq, byte volume) { assert(channel < MAX_CHANNELS); // Used for looping sounds, e.g. the waterfall // uint16 length = READ_BE_UINT16(data) * 2; // uint16 loopLength = READ_BE_UINT16(data + 2) * 2; Common::MemoryReadStream *stream = new Common::MemoryReadStream(data + 8, size - 8); Audio::AudioStream *sound = Audio::makeRawStream(stream, freq, 0, DisposeAfterUse::YES); _mixer->playStream(Audio::Mixer::kSFXSoundType, &_channels[channel], sound, -1, 255, 0, DisposeAfterUse::YES); } void Sound::playSoundWav(byte channel, const byte *data, size_t size, uint16 freq, byte volume, byte loop) { assert(channel < MAX_CHANNELS); Common::MemoryReadStream *stream = new Common::MemoryReadStream(data, size); Audio::AudioStream *sound = Audio::makeWAVStream( stream, DisposeAfterUse::YES); _mixer->playStream(Audio::Mixer::kSFXSoundType, &_channels[channel], sound, -1, 255, 0, DisposeAfterUse::YES); } void Sound::playSoundAiff(byte channel, byte num, byte volume) { warning("TODO: playSoundAiff"); } void Sound::stopSound(byte channel) { assert(channel < MAX_CHANNELS); _mixer->stopHandle(_channels[channel]); } #ifdef TODO enum { TAG_RIFF = 0x46464952, TAG_WAVE = 0x45564157, TAG_fmt = 0x20746D66, TAG_data = 0x61746164 }; static const bool kAmigaStereoChannels = false; // 0,3:left 1,2:right static int16 toS16(int a) { return ((a << 8) | a) - 32768; } static int16 mixS16(int sample1, int sample2) { const int sample = sample1 + sample2; return sample < -32768 ? -32768 : ((sample > 32767 ? 32767 : sample)); } struct MixerChannel { const byte *_data; Frac _pos; uint32 _len; uint32 _loopLen, _loopPos; int _volume; void (MixerChannel:: *_mixWav)(int16 *sample, int count); void initRaw(const byte *data, int freq, int volume, int mixingFreq) { _data = data + 8; _pos.reset(freq, mixingFreq); const int len = READ_BE_UINT16(data) * 2; _loopLen = READ_BE_UINT16(data + 2) * 2; _loopPos = _loopLen ? len : 0; _len = len; _volume = volume; } void initWav(const byte *data, int freq, int volume, int mixingFreq, int len, bool bits16, bool stereo, bool loop) { _data = data; _pos.reset(freq, mixingFreq); _len = len; _loopLen = loop ? len : 0; _loopPos = 0; _volume = volume; _mixWav = bits16 ? (stereo ? &MixerChannel::mixWav<16, true> : &MixerChannel::mixWav<16, false>) : (stereo ? &MixerChannel::mixWav<8, true> : &MixerChannel::mixWav<8, false>); } void mixRaw(int16 &sample) { if (_data) { uint32 pos = _pos.getInt(); _pos.offset += _pos.inc; if (_loopLen != 0) { if (pos >= _loopPos + _loopLen) { pos = _loopPos; _pos.offset = (_loopPos << Frac::BITS) + _pos.inc; } } else { if (pos >= _len) { _data = 0; return; } } sample = mixS16(sample, toS16(_data[pos] ^ 0x80) * _volume / 64); } } template void mixWav(int16 *samples, int count) { for (int i = 0; i < count; i += 2) { uint32 pos = _pos.getInt(); _pos.offset += _pos.inc; if (pos >= _len) { if (_loopLen != 0) { pos = 0; _pos.offset = _pos.inc; } else { _data = 0; break; } } if (stereo) { pos *= 2; } int valueL; if (bits == 8) { // U8 valueL = toS16(_data[pos]) * _volume / 64; } else { // S16 valueL = ((int16)READ_LE_UINT16(&_data[pos * sizeof(int16)])) * _volume / 64; } *samples = mixS16(*samples, valueL); ++samples; int valueR; if (!stereo) { valueR = valueL; } else { if (bits == 8) { // U8 valueR = toS16(_data[pos + 1]) * _volume / 64; } else { // S16 valueR = ((int16)READ_LE_UINT16(&_data[(pos + 1) * sizeof(int16)])) * _volume / 64; } } *samples = mixS16(*samples, valueR); ++samples; } } }; static const byte *loadWav(const byte *data, int &freq, int &len, bool &bits16, bool &stereo) { uint32 riffMagic = READ_LE_UINT32(data); if (riffMagic != TAG_RIFF) return 0; uint32 riffLength = READ_LE_UINT32(data + 4); uint32 waveMagic = READ_LE_UINT32(data + 8); if (waveMagic != TAG_WAVE) return 0; uint32 offset = 12; uint32 chunkMagic, chunkLength = 0; // find fmt chunk do { offset += chunkLength + (chunkLength & 1); if (offset >= riffLength) return 0; chunkMagic = READ_LE_UINT32(data + offset); chunkLength = READ_LE_UINT32(data + offset + 4); offset += 8; } while (chunkMagic != TAG_fmt); if (chunkLength < 14) return 0; if (offset + chunkLength >= riffLength) return 0; // read format int formatTag = READ_LE_UINT16(data + offset); int channels = READ_LE_UINT16(data + offset + 2); int samplesPerSec = READ_LE_UINT32(data + offset + 4); int bitsPerSample = 0; if (chunkLength >= 16) { bitsPerSample = READ_LE_UINT16(data + offset + 14); } else if (formatTag == 1 && channels != 0) { int blockAlign = READ_LE_UINT16(data + offset + 12); bitsPerSample = (blockAlign * 8) / channels; } // check supported format if ((formatTag != 1) || // PCM (channels != 1 && channels != 2) || // mono or stereo (bitsPerSample != 8 && bitsPerSample != 16)) { // 8bit or 16bit warning("Unsupported wave file"); return 0; } // find data chunk do { offset += chunkLength + (chunkLength & 1); if (offset >= riffLength) return 0; chunkMagic = READ_LE_UINT32(data + offset); chunkLength = READ_LE_UINT32(data + offset + 4); offset += 8; } while (chunkMagic != TAG_data); uint32 lengthSamples = chunkLength; if (offset + lengthSamples - 4 > riffLength) { lengthSamples = riffLength + 4 - offset; } if (channels == 2) lengthSamples >>= 1; if (bitsPerSample == 16) lengthSamples >>= 1; freq = samplesPerSec; len = lengthSamples; bits16 = (bitsPerSample == 16); stereo = (channels == 2); return data + offset; } struct Mixer_impl { static const int kMixFreq = 44100; static const SDL_AudioFormat kMixFormat = AUDIO_S16SYS; static const int kMixSoundChannels = 2; static const int kMixBufSize = 4096; static const int kMixChannels = 4; Mix_Chunk *_sounds[kMixChannels]; Mix_Music *_music; MixerChannel _channels[kMixChannels]; SfxPlayer *_sfx; std::map _preloads; // AIFF preloads (3DO) void init(MixerType mixerType) { memset(_sounds, 0, sizeof(_sounds)); _music = 0; memset(_channels, 0, sizeof(_channels)); for (int i = 0; i < kMixChannels; ++i) { _channels[i]._mixWav = &MixerChannel::mixWav<8, false>; } _sfx = 0; Mix_Init(MIX_INIT_OGG | MIX_INIT_FLUIDSYNTH); if (Mix_OpenAudio(kMixFreq, kMixFormat, kMixSoundChannels, kMixBufSize) < 0) { warning("Mix_OpenAudio failed: %s", Mix_GetError()); } switch (mixerType) { case kMixerTypeRaw: Mix_HookMusic(mixAudio, this); break; case kMixerTypeWav: Mix_SetPostMix(mixAudioWav, this); break; case kMixerTypeAiff: Mix_AllocateChannels(kMixChannels); break; default: break; } } void quit() { stopAll(); Mix_CloseAudio(); Mix_Quit(); } void update() { for (int i = 0; i < kMixChannels; ++i) { if (_sounds[i] && !Mix_Playing(i)) { freeSound(i); } } } void playSoundRaw(byte channel, const byte *data, int freq, byte volume) { SDL_LockAudio(); _channels[channel].initRaw(data, freq, volume, kMixFreq); SDL_UnlockAudio(); } void playSoundWav(byte channel, const byte *data, int freq, byte volume, bool loop) { int wavFreq, len; bool bits16, stereo; const byte *wavData = loadWav(data, wavFreq, len, bits16, stereo); if (!wavData) return; if (wavFreq == 22050 || wavFreq == 44100 || wavFreq == 48000) { freq = (int)(freq * (wavFreq / 9943.0f)); } SDL_LockAudio(); _channels[channel].initWav(wavData, freq, volume, kMixFreq, len, bits16, stereo, loop); SDL_UnlockAudio(); } void playSound(byte channel, int volume, Mix_Chunk *chunk, int loops = 0) { stopSound(channel); if (chunk) { Mix_PlayChannel(channel, chunk, loops); } setChannelVolume(channel, volume); _sounds[channel] = chunk; } void stopSound(byte channel) { SDL_LockAudio(); _channels[channel]._data = 0; SDL_UnlockAudio(); Mix_HaltChannel(channel); freeSound(channel); } void freeSound(int channel) { Mix_FreeChunk(_sounds[channel]); _sounds[channel] = 0; } void setChannelVolume(byte channel, byte volume) { SDL_LockAudio(); _channels[channel]._volume = volume; SDL_UnlockAudio(); Mix_Volume(channel, volume * MIX_MAX_VOLUME / 63); } void playMusic(const char *path, int loops = 0) { stopMusic(); _music = Mix_LoadMUS(path); if (_music) { Mix_VolumeMusic(MIX_MAX_VOLUME / 2); Mix_PlayMusic(_music, loops); } else { warning("Failed to load music '%s', %s", path, Mix_GetError()); } } void stopMusic() { Mix_HaltMusic(); Mix_FreeMusic(_music); _music = 0; } static void mixAifcPlayer(void *data, byte *s16buf, int len) { ((AifcPlayer *)data)->readSamples((int16 *)s16buf, len / 2); } void playAifcMusic(AifcPlayer *aifc) { Mix_HookMusic(mixAifcPlayer, aifc); } void stopAifcMusic() { Mix_HookMusic(0, 0); } void playSfxMusic(SfxPlayer *sfx) { SDL_LockAudio(); _sfx = sfx; _sfx->play(kMixFreq); SDL_UnlockAudio(); } void stopSfxMusic() { SDL_LockAudio(); if (_sfx) { _sfx->stop(); _sfx = 0; } SDL_UnlockAudio(); } void mixChannels(int16 *samples, int count) { if (kAmigaStereoChannels) { for (int i = 0; i < count; i += 2) { _channels[0].mixRaw(*samples); _channels[3].mixRaw(*samples); ++samples; _channels[1].mixRaw(*samples); _channels[2].mixRaw(*samples); ++samples; } } else { for (int i = 0; i < count; i += 2) { for (int j = 0; j < kMixChannels; ++j) { _channels[j].mixRaw(samples[i]); } samples[i + 1] = samples[i]; } } } static void mixAudio(void *data, byte *s16buf, int len) { memset(s16buf, 0, len); Mixer_impl *mixer = (Mixer_impl *)data; mixer->mixChannels((int16 *)s16buf, len / sizeof(int16)); if (mixer->_sfx) { mixer->_sfx->readSamples((int16 *)s16buf, len / sizeof(int16)); } } void mixChannelsWav(int16 *samples, int count) { for (int i = 0; i < kMixChannels; ++i) { if (_channels[i]._data) { (_channels[i].*_channels[i]._mixWav)(samples, count); } } } static void mixAudioWav(void *data, byte *s16buf, int len) { Mixer_impl *mixer = (Mixer_impl *)data; mixer->mixChannelsWav((int16 *)s16buf, len / sizeof(int16)); } void stopAll() { for (int i = 0; i < kMixChannels; ++i) { stopSound(i); } stopMusic(); stopSfxMusic(); for (std::map::iterator it = _preloads.begin(); it != _preloads.end(); ++it) { debugC(kDebugSound, "Flush preload %d", it->first); Mix_FreeChunk(it->second); } _preloads.clear(); } void preloadSoundAiff(int num, const byte *data) { if (_preloads.find(num) != _preloads.end()) { warning("AIFF sound %d is already preloaded", num); } else { const uint32 size = READ_BE_UINT32(data + 4) + 8; SDL_RWops *rw = SDL_RWFromConstMem(data, size); Mix_Chunk *chunk = Mix_LoadWAV_RW(rw, 1); _preloads[num] = chunk; } } void playSoundAiff(int channel, int num, int volume) { if (_preloads.find(num) == _preloads.end()) { warning("AIFF sound %d is not preloaded", num); } else { Mix_Chunk *chunk = _preloads[num]; Mix_PlayChannel(channel, chunk, 0); Mix_Volume(channel, volume * MIX_MAX_VOLUME / 63); } } }; Mixer::Mixer(SfxPlayer *sfx) : _aifc(0), _sfx(sfx) { } void Mixer::init(MixerType mixerType) { _impl = new Mixer_impl(); _impl->init(mixerType); } void Mixer::quit() { stopAll(); if (_impl) { _impl->quit(); delete _impl; } delete _aifc; } void Mixer::update() { if (_impl) { _impl->update(); } } void Mixer::playSoundRaw(byte channel, const byte *data, uint16 freq, byte volume) { debugC(kDebugSound, "Mixer::playChannel(%d, %d, %d)", channel, freq, volume); if (_impl) { return _impl->playSoundRaw(channel, data, freq, volume); } } void Mixer::playSoundWav(byte channel, const byte *data, uint16 freq, byte volume, byte loop) { debugC(kDebugSound, "Mixer::playSoundWav(%d, %d, %d)", channel, volume, loop); if (_impl) { return _impl->playSoundWav(channel, data, freq, volume, loop); } } void Mixer::stopSound(byte channel) { debugC(kDebugSound, "Mixer::stopChannel(%d)", channel); if (_impl) { return _impl->stopSound(channel); } } void Mixer::setChannelVolume(byte channel, byte volume) { debugC(kDebugSound, "Mixer::setChannelVolume(%d, %d)", channel, volume); if (_impl) { return _impl->setChannelVolume(channel, volume); } } void Mixer::playMusic(const char *path, byte loop) { debugC(kDebugSound, "Mixer::playMusic(%s, %d)", path, loop); if (_impl) { return _impl->playMusic(path, (loop != 0) ? -1 : 0); } } void Mixer::stopMusic() { debugC(kDebugSound, "Mixer::stopMusic()"); if (_impl) { return _impl->stopMusic(); } } void Mixer::playAifcMusic(const char *path, uint32 offset) { debugC(kDebugSound, "Mixer::playAifcMusic(%s)", path); if (!_aifc) { _aifc = new AifcPlayer(); } if (_impl) { _impl->stopAifcMusic(); if (_aifc->play(Mixer_impl::kMixFreq, path, offset)) { _impl->playAifcMusic(_aifc); } } } void Mixer::stopAifcMusic() { debugC(kDebugSound, "Mixer::stopAifcMusic()"); if (_impl && _aifc) { _aifc->stop(); _impl->stopAifcMusic(); } } void Mixer::playSfxMusic(int num) { debugC(kDebugSound, "Mixer::playSfxMusic(%d)", num); if (_impl && _sfx) { return _impl->playSfxMusic(_sfx); } } void Mixer::stopSfxMusic() { debugC(kDebugSound, "Mixer::stopSfxMusic()"); if (_impl && _sfx) { return _impl->stopSfxMusic(); } } void Mixer::stopAll() { debugC(kDebugSound, "Mixer::stopAll()"); if (_impl) { return _impl->stopAll(); } } void Mixer::preloadSoundAiff(byte num, const byte *data) { debugC(kDebugSound, "Mixer::preloadSoundAiff(num:%d, data:%p)", num, data); if (_impl) { return _impl->preloadSoundAiff(num, data); } } void Mixer::playSoundAiff(byte channel, byte num, byte volume) { debugC(kDebugSound, "Mixer::playSoundAiff()"); if (_impl) { return _impl->playSoundAiff(channel, num, volume); } } #endif } // namespace Awe