uint32 Sound::preFetchCompSpeech(uint32 speechId, uint16 **buf) { int cd = _vm->_resman->getCD(); uint32 numSamples; SoundFileHandle *fh = (cd == 1) ? &_speechFile[0] : &_speechFile[1]; Audio::AudioStream *input = getAudioStream(fh, "speech", cd, speechId, &numSamples); if (!input) return 0; *buf = NULL; // Decompress data into speech buffer. uint32 bufferSize = 2 * numSamples; *buf = (uint16 *)malloc(bufferSize); if (!*buf) { delete input; fh->file.close(); return 0; } uint32 readSamples = input->readBuffer((int16 *)*buf, numSamples); fh->file.close(); delete input; return 2 * readSamples; }
void Sound::playSoundData(Audio::SoundHandle *handle, byte *soundData, uint sound, int pan, int vol, bool loop) { byte *buffer, flags; uint16 compType; int blockAlign, rate; int size = READ_LE_UINT32(soundData + 4); Common::MemoryReadStream stream(soundData, size); if (!Audio::loadWAVFromStream(stream, size, rate, flags, &compType, &blockAlign)) { error("playSoundData: Not a valid WAV data"); } // The Feeble Files originally used DirectSound, which specifies volume // and panning differently than ScummVM does, using a logarithmic scale // rather than a linear one. // // Volume is a value between -10,000 and 0. // Panning is a value between -10,000 and 10,000. // // In both cases, the -10,000 represents -100 dB. When panning, only // one speaker's volume is affected - just like in ScummVM - with // negative values affecting the left speaker, and positive values // affecting the right speaker. Thus -10,000 means the left speaker is // silent. int v, p; vol = CLIP(vol, -10000, 0); pan = CLIP(pan, -10000, 10000); if (vol) { v = (int)((double)Audio::Mixer::kMaxChannelVolume * pow(10.0, (double)vol / 2000.0) + 0.5); } else { v = Audio::Mixer::kMaxChannelVolume; } if (pan < 0) { p = (int)(255.0 * pow(10.0, (double)pan / 2000.0) + 127.5); } else if (pan > 0) { p = (int)(255.0 * pow(10.0, (double)pan / -2000.0) - 127.5); } else { p = 0; } if (loop == true) flags |= Audio::Mixer::FLAG_LOOP; if (compType == 2) { Audio::AudioStream *sndStream = Audio::makeADPCMStream(&stream, size, Audio::kADPCMMS, rate, (flags & Audio::Mixer::FLAG_STEREO) ? 2 : 1, blockAlign); buffer = (byte *)malloc(size * 4); size = sndStream->readBuffer((int16*)buffer, size * 2); size *= 2; // 16bits. delete sndStream; } else { buffer = (byte *)malloc(size); memcpy(buffer, soundData + stream.pos(), size); } _mixer->playRaw(handle, buffer, size, rate, flags | Audio::Mixer::FLAG_AUTOFREE, -1, v, p); }
bool Console::cmdRawToWav(int argc, const char **argv) { if (argc != 3) { debugPrintf("Use %s <rawFilePath> <wavFileName> to dump a .RAW file to .WAV\n", argv[0]); return true; } Common::File file; if (!_engine->getSearchManager()->openFile(file, argv[1])) { warning("File not found: %s", argv[1]); return true; } Audio::AudioStream *audioStream = makeRawZorkStream(argv[1], _engine); Common::DumpFile output; output.open(argv[2]); output.writeUint32BE(MKTAG('R', 'I', 'F', 'F')); output.writeUint32LE(file.size() * 2 + 36); output.writeUint32BE(MKTAG('W', 'A', 'V', 'E')); output.writeUint32BE(MKTAG('f', 'm', 't', ' ')); output.writeUint32LE(16); output.writeUint16LE(1); uint16 numChannels; if (audioStream->isStereo()) { numChannels = 2; output.writeUint16LE(2); } else { numChannels = 1; output.writeUint16LE(1); } output.writeUint32LE(audioStream->getRate()); output.writeUint32LE(audioStream->getRate() * numChannels * 2); output.writeUint16LE(numChannels * 2); output.writeUint16LE(16); output.writeUint32BE(MKTAG('d', 'a', 't', 'a')); output.writeUint32LE(file.size() * 2); int16 *buffer = new int16[file.size()]; audioStream->readBuffer(buffer, file.size()); #ifndef SCUMM_LITTLE_ENDIAN for (int i = 0; i < file.size(); ++i) buffer[i] = TO_LE_16(buffer[i]); #endif output.write(buffer, file.size() * 2); delete[] buffer; return true; }
bool LoopingAudioStream::endOfData() const { if (!_stream) return true; if (_loop) return false; return _stream->endOfData(); }
extern "C" int SimpleRate_readFudge(Audio::AudioStream &input, int16 *a, int b) { #ifdef DEBUG_RATECONV debug("Reading ptr=%x n%d", a, b); #endif return input.readBuffer(a, b); }
void AVIDecoder::AVIAudioTrack::skipAudio(const Audio::Timestamp &time, const Audio::Timestamp &frameTime) { Audio::Timestamp timeDiff = time.convertToFramerate(_wvInfo.samplesPerSec) - frameTime.convertToFramerate(_wvInfo.samplesPerSec); int skipFrames = timeDiff.totalNumberOfFrames(); if (skipFrames <= 0) return; Audio::AudioStream *audioStream = getAudioStream(); if (!audioStream) return; if (audioStream->isStereo()) skipFrames *= 2; int16 *tempBuffer = new int16[skipFrames]; audioStream->readBuffer(tempBuffer, skipFrames); delete[] tempBuffer; }
extern "C" int SimpleRate_readFudge(Audio::AudioStream &input, int16 *a, int b) { #ifdef DEBUG_RATECONV fprintf(stderr, "Reading ptr=%x n%d\n", a, b); fflush(stderr); #endif return input.readBuffer(a, b); }
void AppendableSnd::queueBuffer(Common::SeekableReadStream *bufferIn) { assert (_as); // Setup the ADPCM decoder uint32 sizeIn = bufferIn->size(); Audio::AudioStream *adpcm = makeDecoder(bufferIn, sizeIn); // Setup the output buffer uint32 sizeOut = sizeIn * 2; byte *bufferOut = new byte[sizeOut * 2]; // Decode to raw samples sizeOut = adpcm->readBuffer((int16 *)bufferOut, sizeOut); assert (adpcm->endOfData()); delete adpcm; // Queue the decoded samples _as->queueBuffer(bufferOut, sizeOut * 2); }
int LoopingAudioStream::readBuffer(int16 *buffer, const int numSamples) { if (!_loop) { return _stream->readBuffer(buffer, numSamples); } int16 *buf = buffer; int samplesLeft = numSamples; while (samplesLeft > 0) { int len = _stream->readBuffer(buf, samplesLeft); if (len < samplesLeft) { delete _stream; _stream = _parent->makeAudioStream(_loopSound); } samplesLeft -= len; buf += len; } return numSamples; }
void convertRawToWav(const Common::String &inputFile, ZVision *engine, const Common::String &outputFile) { Common::File file; if (!file.open(inputFile)) return; Audio::AudioStream *audioStream = makeRawZorkStream(inputFile, engine); Common::DumpFile output; output.open(outputFile); output.writeUint32BE(MKTAG('R', 'I', 'F', 'F')); output.writeUint32LE(file.size() * 2 + 36); output.writeUint32BE(MKTAG('W', 'A', 'V', 'E')); output.writeUint32BE(MKTAG('f', 'm', 't', ' ')); output.writeUint32LE(16); output.writeUint16LE(1); uint16 numChannels; if (audioStream->isStereo()) { numChannels = 2; output.writeUint16LE(2); } else { numChannels = 1; output.writeUint16LE(1); } output.writeUint32LE(audioStream->getRate()); output.writeUint32LE(audioStream->getRate() * numChannels * 2); output.writeUint16LE(numChannels * 2); output.writeUint16LE(16); output.writeUint32BE(MKTAG('d', 'a', 't', 'a')); output.writeUint32LE(file.size() * 2); int16 *buffer = new int16[file.size()]; audioStream->readBuffer(buffer, file.size()); output.write(buffer, file.size() * 2); delete[] buffer; }
AudioStreamWrapper(Audio::AudioStream *stream) { _stream = stream; int rate = _stream->getRate(); // A file where the sample rate claims to be 11025 Hz is // probably compressed with the old tool. We force the real // sample rate, which is 11840 Hz. // // However, a file compressed with the newer tool is not // guaranteed to have a sample rate of 11840 Hz. LAME will // automatically resample it to 12000 Hz. So in all other // cases, we use the rate from the file. if (rate == 11025) _rate = 11840; else _rate = rate; }
bool SndRes::load(ResourceContext *context, uint32 resourceId, SoundBuffer &buffer, bool onlyHeader) { Audio::AudioStream *voxStream; size_t soundResourceLength; bool result = false; GameSoundTypes resourceType = kSoundPCM; byte *data = 0; int rate = 0, size = 0; Common::File* file; if (resourceId == (uint32)-1) { return false; } #ifdef ENABLE_IHNM //TODO: move to resource_res so we can use normal "getResourceData" and "getFile" methods if (_vm->getGameId() == GID_IHNM && _vm->isMacResources()) { char soundFileName[40]; int dirIndex = resourceId / 64; if ((context->fileType() & GAME_VOICEFILE) != 0) { if (_voiceSerial == 0) { sprintf(soundFileName, "Voices/VoicesS/Voices%d/VoicesS%03x", dirIndex, resourceId); } else { sprintf(soundFileName, "Voices/Voices%d/Voices%d/Voices%d%03x", _voiceSerial, dirIndex, _voiceSerial, resourceId); } } else { sprintf(soundFileName, "SFX/SFX%d/SFX%03x", dirIndex, resourceId); } file = new Common::File(); file->open(soundFileName); soundResourceLength = file->size(); } else #endif { ResourceData* resourceData = context->getResourceData(resourceId); file = context->getFile(resourceData); file->seek(resourceData->offset); soundResourceLength = resourceData->size; } Common::SeekableReadStream& readS = *file; bool uncompressedSound = false; if (soundResourceLength >= 8) { byte header[8]; readS.read(&header, 8); readS.seek(readS.pos() - 8); if (!memcmp(header, "Creative", 8)) { resourceType = kSoundVOC; } else if (!memcmp(header, "RIFF", 4) != 0) { resourceType = kSoundWAV; } else if (!memcmp(header, "FORM", 4) != 0) { resourceType = kSoundAIFF; } else if (!memcmp(header, "ajkg", 4) != 0) { resourceType = kSoundShorten; } // If patch data exists for sound resource 4 (used in ITE intro), don't treat this sound as compressed // Patch data for this resource is in file p2_a.iaf or p2_a.voc if (_vm->getGameId() == GID_ITE && resourceId == 4 && context->getResourceData(resourceId)->patchData != NULL) uncompressedSound = true; // FIXME: Currently, the SFX.RES file in IHNM cannot be compressed if (_vm->getGameId() == GID_IHNM && (context->fileType() & GAME_SOUNDFILE)) uncompressedSound = true; if (context->isCompressed() && !uncompressedSound) { if (header[0] == char(0)) { resourceType = kSoundMP3; } else if (header[0] == char(1)) { resourceType = kSoundOGG; } else if (header[0] == char(2)) { resourceType = kSoundFLAC; } } } // Default sound type is 16-bit signed PCM, used in ITE by PCM and VOX files buffer.isCompressed = context->isCompressed(); buffer.soundType = resourceType; buffer.originalSize = 0; // Set default flags and frequency for PCM, VOC and VOX files, which got no header buffer.flags = Audio::FLAG_16BITS; buffer.frequency = 22050; if (_vm->getGameId() == GID_ITE) { if (_vm->getFeatures() & GF_8BIT_UNSIGNED_PCM) { // older ITE demos buffer.flags |= Audio::FLAG_UNSIGNED; buffer.flags &= ~Audio::FLAG_16BITS; } else { // Voice files in newer ITE demo versions are OKI ADPCM (VOX) encoded if (!uncompressedSound && !scumm_stricmp(context->fileName(), "voicesd.rsc")) resourceType = kSoundVOX; } } buffer.buffer = NULL; // Check for LE sounds if (!context->isBigEndian()) buffer.flags |= Audio::FLAG_LITTLE_ENDIAN; if ((context->fileType() & GAME_VOICEFILE) && (_vm->getFeatures() & GF_LE_VOICES)) buffer.flags |= Audio::FLAG_LITTLE_ENDIAN; // Older Mac versions of ITE were Macbinary packed int soundOffset = (context->fileType() & GAME_MACBINARY) ? 36 : 0; switch (resourceType) { case kSoundPCM: buffer.size = soundResourceLength - soundOffset; if (!onlyHeader) { buffer.buffer = (byte *) malloc(buffer.size); if (soundOffset > 0) readS.skip(soundOffset); readS.read(buffer.buffer, buffer.size); } result = true; break; case kSoundVOX: buffer.size = soundResourceLength * 4; if (!onlyHeader) { voxStream = Audio::makeADPCMStream(&readS, DisposeAfterUse::NO, soundResourceLength, Audio::kADPCMOki); buffer.buffer = (byte *)malloc(buffer.size); voxStream->readBuffer((int16*)buffer.buffer, soundResourceLength * 2); delete voxStream; } result = true; break; case kSoundWAV: case kSoundAIFF: case kSoundShorten: case kSoundVOC: if (resourceType == kSoundWAV) { result = Audio::loadWAVFromStream(readS, size, rate, buffer.flags); } else if (resourceType == kSoundAIFF) { result = Audio::loadAIFFFromStream(readS, size, rate, buffer.flags); #ifdef ENABLE_SAGA2 } else if (resourceType == kSoundShorten) { result = loadShortenFromStream(readS, size, rate, buffer.flags); #endif } else if (resourceType == kSoundVOC) { data = Audio::loadVOCFromStream(readS, size, rate); result = (data != NULL); if (onlyHeader) free(data); buffer.flags |= Audio::FLAG_UNSIGNED; buffer.flags &= ~Audio::FLAG_16BITS; buffer.flags &= ~Audio::FLAG_STEREO; } if (result) { buffer.frequency = rate; buffer.size = size; if (!onlyHeader) { if (resourceType == kSoundVOC) { buffer.buffer = data; } else { buffer.buffer = (byte *)malloc(size); readS.read(buffer.buffer, size); } } } break; case kSoundMP3: case kSoundOGG: case kSoundFLAC: ResourceData *resourceData; resourceData = context->getResourceData(resourceId); // Read compressed sfx header readS.readByte(); // Skip compression identifier byte buffer.frequency = readS.readUint16LE(); buffer.originalSize = readS.readUint32LE(); if (readS.readByte() == 8) // read sample bits buffer.flags &= ~Audio::FLAG_16BITS; if (readS.readByte() != 0) // read stereo flag buffer.flags |= Audio::FLAG_STEREO; buffer.size = soundResourceLength; buffer.soundType = resourceType; buffer.fileOffset = resourceData->offset + 9; // skip compressed sfx header: byte + uint16 + uint32 + byte + byte if (!onlyHeader) { buffer.buffer = (byte *)malloc(buffer.size); readS.read(buffer.buffer, buffer.size); } result = true; break; default: error("SndRes::load Unknown sound type"); } if (_vm->getGameId() == GID_IHNM && _vm->isMacResources()) { delete file; } // In ITE CD De some voices are absent and contain just 5 bytes header // Round it to even number so soundmanager will not crash. // See bug #1256701 buffer.size &= ~(0x1); return result; }
bool VideoDecoder::AudioTrack::endOfTrack() const { Audio::AudioStream *stream = getAudioStream(); return !stream || !g_system->getMixer()->isSoundHandleActive(_handle) || stream->endOfData(); }
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); } } }
uint getChannels() const { return _stream->getChannels(); }
bool isStereo() const { return _stream ? _stream->isStereo() : 0; }
int getRate() const { return _stream ? _stream->getRate() : 22050; }
bool endOfStream() const { return _stream->endOfStream(); }
bool endOfData() const { return _stream->endOfData(); }
bool isStereo() const { return _stream->isStereo(); }
int readBuffer(int16 *buffer, const int numSamples) { return _stream->readBuffer(buffer, numSamples); }
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); } } }