void SoundHE::playHESound(int soundID, int heOffset, int heChannel, int heFlags) { Audio::RewindableAudioStream *stream = 0; byte *ptr, *spoolPtr; int size = -1; int priority, rate; byte flags = Audio::FLAG_UNSIGNED; Audio::Mixer::SoundType type = Audio::Mixer::kSFXSoundType; if (soundID > _vm->_numSounds) type = Audio::Mixer::kMusicSoundType; else if (soundID == 1) type = Audio::Mixer::kSpeechSoundType; if (heChannel == -1) heChannel = (_vm->VAR_RESERVED_SOUND_CHANNELS != 0xFF) ? findFreeSoundChannel() : 1; debug(5,"playHESound: soundID %d heOffset %d heChannel %d heFlags %d", soundID, heOffset, heChannel, heFlags); if (soundID >= 10000) { // Special codes, used in pjgames return; } if (soundID > _vm->_numSounds) { int music_offs; Common::File musicFile; Common::String buf(_vm->generateFilename(-4)); if (musicFile.open(buf) == false) { warning("playHESound: Can't open music file %s", buf.c_str()); return; } if (!getHEMusicDetails(soundID, music_offs, size)) { debug(0, "playHESound: musicID %d not found", soundID); return; } musicFile.seek(music_offs, SEEK_SET); _mixer->stopHandle(_heSoundChannels[heChannel]); spoolPtr = _vm->_res->createResource(rtSpoolBuffer, heChannel, size); assert(spoolPtr); musicFile.read(spoolPtr, size); musicFile.close(); if (_vm->_game.heversion == 70) { stream = Audio::makeRawStream(spoolPtr, size, 11025, flags, DisposeAfterUse::NO); _mixer->playStream(type, &_heSoundChannels[heChannel], stream, soundID); return; } } if (soundID > _vm->_numSounds) { ptr = _vm->getResourceAddress(rtSpoolBuffer, heChannel); } else { ptr = _vm->getResourceAddress(rtSound, soundID); } if (!ptr) { return; } // Support for sound in later HE games if (READ_BE_UINT32(ptr) == MKTAG('R','I','F','F') || READ_BE_UINT32(ptr) == MKTAG('W','S','O','U')) { uint16 compType; int blockAlign; int codeOffs = -1; priority = (soundID > _vm->_numSounds) ? 255 : *(ptr + 18); byte *sbngPtr = findSoundTag(MKTAG('S','B','N','G'), ptr); if (sbngPtr != NULL) { codeOffs = sbngPtr - ptr + 8; } if (_mixer->isSoundHandleActive(_heSoundChannels[heChannel])) { int curSnd = _heChannel[heChannel].sound; if (curSnd == 1 && soundID != 1) return; if (curSnd != 0 && curSnd != 1 && soundID != 1 && _heChannel[heChannel].priority > priority) return; } if (READ_BE_UINT32(ptr) == MKTAG('W','S','O','U')) ptr += 8; size = READ_LE_UINT32(ptr + 4); Common::MemoryReadStream memStream(ptr, size); if (!Audio::loadWAVFromStream(memStream, size, rate, flags, &compType, &blockAlign)) { error("playHESound: Not a valid WAV file (%d)", soundID); } assert(heOffset >= 0 && heOffset < size); // FIXME: Disabled sound offsets, due to asserts been triggered heOffset = 0; _vm->setHETimer(heChannel + 4); _heChannel[heChannel].sound = soundID; _heChannel[heChannel].priority = priority; _heChannel[heChannel].rate = rate; _heChannel[heChannel].sbngBlock = (codeOffs != -1) ? 1 : 0; _heChannel[heChannel].codeOffs = codeOffs; memset(_heChannel[heChannel].soundVars, 0, sizeof(_heChannel[heChannel].soundVars)); // TODO: Extra sound flags if (heFlags & 1) { _heChannel[heChannel].timer = 0; } else { _heChannel[heChannel].timer = size * 1000 / rate; } _mixer->stopHandle(_heSoundChannels[heChannel]); if (compType == 17) { Audio::AudioStream *voxStream = Audio::makeADPCMStream(&memStream, DisposeAfterUse::NO, size, Audio::kADPCMMSIma, rate, (flags & Audio::FLAG_STEREO) ? 2 : 1, blockAlign); // FIXME: Get rid of this crude hack to turn a ADPCM stream into a raw stream. // It seems it is only there to allow looping -- if that is true, we certainly // can do without it, using a LoopingAudioStream. byte *sound = (byte *)malloc(size * 4); /* On systems where it matters, malloc will return * even addresses, so the use of (void *) in the * following cast shuts the compiler from warning * unnecessarily. */ size = voxStream->readBuffer((int16*)(void *)sound, size * 2); size *= 2; // 16bits. delete voxStream; _heChannel[heChannel].rate = rate; if (_heChannel[heChannel].timer) _heChannel[heChannel].timer = size * 1000 / rate; // makeADPCMStream returns a stream in native endianness, but RawMemoryStream // defaults to big endian. If we're on a little endian system, set the LE flag. #ifdef SCUMM_LITTLE_ENDIAN flags |= Audio::FLAG_LITTLE_ENDIAN; #endif stream = Audio::makeRawStream(sound + heOffset, size - heOffset, rate, flags); } else { stream = Audio::makeRawStream(ptr + memStream.pos() + heOffset, size - heOffset, rate, flags, DisposeAfterUse::NO); } _mixer->playStream(type, &_heSoundChannels[heChannel], Audio::makeLoopingAudioStream(stream, (heFlags & 1) ? 0 : 1), soundID); } // Support for sound in Humongous Entertainment games else if (READ_BE_UINT32(ptr) == MKTAG('D','I','G','I') || READ_BE_UINT32(ptr) == MKTAG('T','A','L','K')) { byte *sndPtr = ptr; int codeOffs = -1; priority = (soundID > _vm->_numSounds) ? 255 : *(ptr + 18); rate = READ_LE_UINT16(ptr + 22); // Skip DIGI/TALK (8) and HSHD (24) blocks ptr += 32; if (_mixer->isSoundHandleActive(_heSoundChannels[heChannel])) { int curSnd = _heChannel[heChannel].sound; if (curSnd == 1 && soundID != 1) return; if (curSnd != 0 && curSnd != 1 && soundID != 1 && _heChannel[heChannel].priority > priority) return; } if (READ_BE_UINT32(ptr) == MKTAG('S','B','N','G')) { codeOffs = ptr - sndPtr + 8; ptr += READ_BE_UINT32(ptr + 4); } assert(READ_BE_UINT32(ptr) == MKTAG('S','D','A','T')); size = READ_BE_UINT32(ptr + 4) - 8; if (heOffset < 0 || heOffset > size) { // Occurs when making fireworks in puttmoon heOffset = 0; } size -= heOffset; if (_overrideFreq) { // Used by the piano in Fatty Bear's Birthday Surprise rate = _overrideFreq; _overrideFreq = 0; } _vm->setHETimer(heChannel + 4); _heChannel[heChannel].sound = soundID; _heChannel[heChannel].priority = priority; _heChannel[heChannel].rate = rate; _heChannel[heChannel].sbngBlock = (codeOffs != -1) ? 1 : 0; _heChannel[heChannel].codeOffs = codeOffs; memset(_heChannel[heChannel].soundVars, 0, sizeof(_heChannel[heChannel].soundVars)); // TODO: Extra sound flags if (heFlags & 1) { _heChannel[heChannel].timer = 0; } else { _heChannel[heChannel].timer = size * 1000 / rate; } _mixer->stopHandle(_heSoundChannels[heChannel]); stream = Audio::makeRawStream(ptr + heOffset + 8, size, rate, flags, DisposeAfterUse::NO); _mixer->playStream(type, &_heSoundChannels[heChannel], Audio::makeLoopingAudioStream(stream, (heFlags & 1) ? 0 : 1), soundID); } // Support for PCM music in 3DO versions of Humongous Entertainment games else if (READ_BE_UINT32(ptr) == MKTAG('M','R','A','W')) { priority = *(ptr + 18); rate = READ_LE_UINT16(ptr + 22); // Skip DIGI (8) and HSHD (24) blocks ptr += 32; assert(READ_BE_UINT32(ptr) == MKTAG('S','D','A','T')); size = READ_BE_UINT32(ptr + 4) - 8; byte *sound = (byte *)malloc(size); memcpy(sound, ptr + 8, size); _mixer->stopID(_currentMusic); _currentMusic = soundID; stream = Audio::makeRawStream(sound, size, rate, 0); _mixer->playStream(Audio::Mixer::kMusicSoundType, NULL, stream, soundID); } else if (READ_BE_UINT32(ptr) == MKTAG('M','I','D','I')) { if (_vm->_imuse) { // This is used in the DOS version of Fatty Bear's // Birthday Surprise to change the note on the piano // when not using a digitized instrument. _vm->_imuse->stopSound(_currentMusic); _currentMusic = soundID; _vm->_imuse->startSoundWithNoteOffset(soundID, heOffset); } } }
void SoundHE::playHESound(int soundID, int heOffset, int heChannel, int heFlags) { byte *ptr, *spoolPtr; int size = -1; int priority, rate; byte flags = Audio::Mixer::FLAG_UNSIGNED; Audio::Mixer::SoundType type = Audio::Mixer::kSFXSoundType; if (soundID > _vm->_numSounds) type = Audio::Mixer::kMusicSoundType; else if (soundID == 1) type = Audio::Mixer::kSpeechSoundType; if (heChannel == -1) heChannel = (_vm->VAR_RESERVED_SOUND_CHANNELS != 0xFF) ? findFreeSoundChannel() : 1; debug(5,"playHESound: soundID %d heOffset %d heChannel %d heFlags %d", soundID, heOffset, heChannel, heFlags); if (soundID >= 10000) { // Special codes, used in pjgames return; } if (soundID > _vm->_numSounds) { int music_offs; Common::File musicFile; Common::String buf(_vm->generateFilename(-4)); if (musicFile.open(buf) == false) { warning("playHESound: Can't open music file %s", buf.c_str()); return; } if (!getHEMusicDetails(soundID, music_offs, size)) { debug(0, "playHESound: musicID %d not found", soundID); return; } musicFile.seek(music_offs, SEEK_SET); _mixer->stopHandle(_heSoundChannels[heChannel]); spoolPtr = _vm->_res->createResource(rtSpoolBuffer, heChannel, size); assert(spoolPtr); musicFile.read(spoolPtr, size); musicFile.close(); if (_vm->_game.heversion == 70) { _mixer->playRaw(type, &_heSoundChannels[heChannel], spoolPtr, size, 11025, flags, soundID); return; } } if (soundID > _vm->_numSounds) { ptr = _vm->getResourceAddress(rtSpoolBuffer, heChannel); } else { ptr = _vm->getResourceAddress(rtSound, soundID); } if (!ptr) { return; } // Support for sound in later HE games if (READ_BE_UINT32(ptr) == MKID_BE('RIFF') || READ_BE_UINT32(ptr) == MKID_BE('WSOU')) { uint16 compType; int blockAlign; char *sound; int codeOffs = -1; priority = (soundID > _vm->_numSounds) ? 255 : *(ptr + 18); byte *sbngPtr = findSoundTag(MKID_BE('SBNG'), ptr); if (sbngPtr != NULL) { codeOffs = sbngPtr - ptr + 8; } if (_mixer->isSoundHandleActive(_heSoundChannels[heChannel])) { int curSnd = _heChannel[heChannel].sound; if (curSnd == 1 && soundID != 1) return; if (curSnd != 0 && curSnd != 1 && soundID != 1 && _heChannel[heChannel].priority > priority) return; } if (READ_BE_UINT32(ptr) == MKID_BE('WSOU')) ptr += 8; size = READ_LE_UINT32(ptr + 4); Common::MemoryReadStream stream(ptr, size); if (!Audio::loadWAVFromStream(stream, size, rate, flags, &compType, &blockAlign)) { error("playHESound: Not a valid WAV file (%d)", soundID); } assert(heOffset >= 0 && heOffset < size); // FIXME: Disabled sound offsets, due to asserts been triggered heOffset = 0; _vm->setHETimer(heChannel + 4); _heChannel[heChannel].sound = soundID; _heChannel[heChannel].priority = priority; _heChannel[heChannel].rate = rate; _heChannel[heChannel].sbngBlock = (codeOffs != -1) ? 1 : 0; _heChannel[heChannel].codeOffs = codeOffs; memset(_heChannel[heChannel].soundVars, 0, sizeof(_heChannel[heChannel].soundVars)); // TODO: Extra sound flags if (heFlags & 1) { flags |= Audio::Mixer::FLAG_LOOP; _heChannel[heChannel].timer = 0; } else { _heChannel[heChannel].timer = size * 1000 / rate; } _mixer->stopHandle(_heSoundChannels[heChannel]); if (compType == 17) { Audio::AudioStream *voxStream = Audio::makeADPCMStream(&stream, false, size, Audio::kADPCMMSIma, rate, (flags & Audio::Mixer::FLAG_STEREO) ? 2 : 1, blockAlign); sound = (char *)malloc(size * 4); size = voxStream->readBuffer((int16*)sound, size * 2); size *= 2; // 16bits. delete voxStream; _heChannel[heChannel].rate = rate; if (_heChannel[heChannel].timer) _heChannel[heChannel].timer = size * 1000 / rate; flags |= Audio::Mixer::FLAG_AUTOFREE; _mixer->playRaw(type, &_heSoundChannels[heChannel], sound + heOffset, size - heOffset, rate, flags, soundID); } else { _mixer->playRaw(type, &_heSoundChannels[heChannel], ptr + stream.pos() + heOffset, size - heOffset, rate, flags, soundID); } } // Support for sound in Humongous Entertainment games else if (READ_BE_UINT32(ptr) == MKID_BE('DIGI') || READ_BE_UINT32(ptr) == MKID_BE('TALK')) { byte *sndPtr = ptr; int codeOffs = -1; priority = (soundID > _vm->_numSounds) ? 255 : *(ptr + 18); rate = READ_LE_UINT16(ptr + 22); // Skip DIGI/TALK (8) and HSHD (24) blocks ptr += 32; if (_mixer->isSoundHandleActive(_heSoundChannels[heChannel])) { int curSnd = _heChannel[heChannel].sound; if (curSnd == 1 && soundID != 1) return; if (curSnd != 0 && curSnd != 1 && soundID != 1 && _heChannel[heChannel].priority > priority) return; } if (READ_BE_UINT32(ptr) == MKID_BE('SBNG')) { codeOffs = ptr - sndPtr + 8; ptr += READ_BE_UINT32(ptr + 4); } assert(READ_BE_UINT32(ptr) == MKID_BE('SDAT')); size = READ_BE_UINT32(ptr + 4) - 8; if (heOffset < 0 || heOffset > size) { // Occurs when making fireworks in puttmoon heOffset = 0; } size -= heOffset; if (_overrideFreq) { // Used by the piano in Fatty Bear's Birthday Surprise rate = _overrideFreq; _overrideFreq = 0; } _vm->setHETimer(heChannel + 4); _heChannel[heChannel].sound = soundID; _heChannel[heChannel].priority = priority; _heChannel[heChannel].rate = rate; _heChannel[heChannel].sbngBlock = (codeOffs != -1) ? 1 : 0; _heChannel[heChannel].codeOffs = codeOffs; memset(_heChannel[heChannel].soundVars, 0, sizeof(_heChannel[heChannel].soundVars)); // TODO: Extra sound flags if (heFlags & 1) { flags |= Audio::Mixer::FLAG_LOOP; _heChannel[heChannel].timer = 0; } else { _heChannel[heChannel].timer = size * 1000 / rate; } _mixer->stopHandle(_heSoundChannels[heChannel]); _mixer->playRaw(type, &_heSoundChannels[heChannel], ptr + heOffset + 8, size, rate, flags, soundID); } // Support for PCM music in 3DO versions of Humongous Entertainment games else if (READ_BE_UINT32(ptr) == MKID_BE('MRAW')) { priority = *(ptr + 18); rate = READ_LE_UINT16(ptr + 22); // Skip DIGI (8) and HSHD (24) blocks ptr += 32; assert(READ_BE_UINT32(ptr) == MKID_BE('SDAT')); size = READ_BE_UINT32(ptr + 4) - 8; flags = Audio::Mixer::FLAG_AUTOFREE; byte *sound = (byte *)malloc(size); memcpy(sound, ptr + 8, size); _mixer->stopID(_currentMusic); _currentMusic = soundID; _mixer->playRaw(Audio::Mixer::kMusicSoundType, NULL, sound, size, rate, flags, soundID); } else if (READ_BE_UINT32(ptr) == MKID_BE('MIDI')) { if (_vm->_imuse) { _vm->_imuse->stopSound(_currentMusic); _currentMusic = soundID; _vm->_imuse->startSound(soundID); } } }