//============================================================================ //---------------------------------------------------------------------------- // Sound.c //---------------------------------------------------------------------------- //============================================================================ #include "PLResources.h" #include "PLSound.h" #include "Externs.h" #include "MemoryManager.h" #include "SoundSync.h" #include "VirtualDirectory.h" #include "WaveFormat.h" #define kBaseBufferSoundID 1000 #define kMaxSounds 64 void CallBack0 (PortabilityLayer::AudioChannel *); void CallBack1 (PortabilityLayer::AudioChannel *); void CallBack2 (PortabilityLayer::AudioChannel *); PLError_t LoadBufferSounds (void); void DumpBufferSounds (void); PLError_t OpenSoundChannels (void); void CloseSoundChannels (void); THandle ParseAndConvertSound(const THandle &handle); PortabilityLayer::AudioChannel *channel0, *channel1, *channel2; Ptr theSoundData[kMaxSounds]; short numSoundsLoaded; Boolean soundLoaded[kMaxSounds], dontLoadSounds; Boolean channelOpen, isSoundOn, failedSound; //============================================================== Functions //-------------------------------------------------------------- PlayPrioritySound void PlayPrioritySound (short which, short priority) { short lowestPriority, whosLowest; if (failedSound || dontLoadSounds) return; SoundSyncState ss = SoundSync_ReadAll(); if ((priority == kTriggerPriority) && ((ss.priority0 == kTriggerPriority) || ((ss.priority1 == kTriggerPriority)) || ((ss.priority2 == kTriggerPriority)))) return; whosLowest = 0; lowestPriority = ss.priority0; if (ss.priority1 < lowestPriority) { lowestPriority = ss.priority1; whosLowest = 1; } if (ss.priority2 < lowestPriority) { lowestPriority = ss.priority2; whosLowest = 2; } if (priority >= lowestPriority) { PlayExclusiveSoundChannel(whosLowest, which, lowestPriority, priority); } } //-------------------------------------------------------------- FlushAnyTriggerPlaying void FlushAnyTriggerPlaying (void) { SoundSyncState ss = SoundSync_ReadAll(); if (ss.priority0 == kTriggerPriority) { channel0->ClearAllCommands(); channel0->Stop(); } if (ss.priority1 == kTriggerPriority) { channel1->ClearAllCommands(); channel1->Stop(); } if (ss.priority2 == kTriggerPriority) { channel2->ClearAllCommands(); channel2->Stop(); } } //-------------------------------------------------------------- CallBack0 void CallBack0(PortabilityLayer::AudioChannel *theChannel) { SoundSync_ClearPriority(0); } //-------------------------------------------------------------- CallBack1 void CallBack1(PortabilityLayer::AudioChannel *theChannel) { SoundSync_ClearPriority(1); } //-------------------------------------------------------------- CallBack2 void CallBack2(PortabilityLayer::AudioChannel *theChannel) { SoundSync_ClearPriority(2); } //-------------------------------------------------------------- PlaySound0 void PlayExclusiveSoundChannel(short channelIndex, short soundID, short oldPriority, short newPriority) { if (failedSound || dontLoadSounds) return; PortabilityLayer::AudioChannel *channel = nil; PortabilityLayer::AudioChannelCallback_t callback = nil; switch (channelIndex) { case 0: channel = channel0; callback = CallBack0; break; case 1: channel = channel1; callback = CallBack1; break; case 2: channel = channel2; callback = CallBack2; break; default: return; } if (isSoundOn) { if (oldPriority != 0) { // Flush the queue and stop the channel, which will remove the pending callback channel->ClearAllCommands(); channel->Stop(); SoundSync_ClearPriority(channelIndex); } SoundSync_PutPriority(channelIndex, newPriority); bool succeeded = channel->AddBuffer(theSoundData[soundID], false); succeeded &= channel->AddCallback(callback, false); if (!succeeded) SoundSync_ClearPriority(channelIndex); } } //-------------------------------------------------------------- ParseAndConvertSound //-------------------------------------------------------------- LoadTriggerSound PLError_t LoadTriggerSound (short soundID) { Handle theSound; long soundDataSize; PLError_t theErr; if ((dontLoadSounds) || (theSoundData[kMaxSounds - 1] != nil)) theErr = PLErrors::kFileNotFound; else { // FlushAnyTriggerPlaying(); theErr = PLErrors::kNone; theSound = ParseAndConvertSound(GetResource('snd ', soundID)); if (theSound == nil) { theErr = PLErrors::kFileNotFound; } else { soundDataSize = GetHandleSize(theSound); theSoundData[kMaxSounds - 1] = PortabilityLayer::MemoryManager::GetInstance()->Alloc(soundDataSize); if (theSoundData[kMaxSounds - 1] == nil) { theSound.Dispose(); theErr = PLErrors::kOutOfMemory; } else { BlockMove((Byte*)(*theSound), theSoundData[kMaxSounds - 1], soundDataSize); theSound.Dispose(); } } } return (theErr); } //-------------------------------------------------------------- DumpTriggerSound void DumpTriggerSound (void) { if (theSoundData[kMaxSounds - 1] != nil) PortabilityLayer::MemoryManager::GetInstance()->Release(theSoundData[kMaxSounds - 1]); theSoundData[kMaxSounds - 1] = nil; } //-------------------------------------------------------------- LoadBufferSounds PLError_t LoadBufferSounds (void) { Handle theSound; long soundDataSize; PLError_t theErr; short i; theErr = PLErrors::kNone; for (i = 0; i < kMaxSounds - 1; i++) { theSound = ParseAndConvertSound(GetResource('snd ', i + kBaseBufferSoundID)); if (theSound == nil) return (PLErrors::kOutOfMemory); soundDataSize = GetHandleSize(theSound); theSoundData[i] = PortabilityLayer::MemoryManager::GetInstance()->Alloc(soundDataSize); if (theSoundData[i] == nil) return (PLErrors::kOutOfMemory); BlockMove(*theSound, theSoundData[i], soundDataSize); theSound.Dispose(); } theSoundData[kMaxSounds - 1] = nil; return (theErr); } //-------------------------------------------------------------- DumpBufferSounds void DumpBufferSounds (void) { short i; for (i = 0; i < kMaxSounds; i++) { if (theSoundData[i] != nil) PortabilityLayer::MemoryManager::GetInstance()->Release(theSoundData[i]); theSoundData[i] = nil; } } //-------------------------------------------------------------- OpenSoundChannels PLError_t OpenSoundChannels (void) { if (channelOpen) return PLErrors::kAudioError; channel0 = PortabilityLayer::SoundSystem::GetInstance()->CreateChannel(); if (channel0) channelOpen = true; else return PLErrors::kAudioError; channel1 = PortabilityLayer::SoundSystem::GetInstance()->CreateChannel(); if (channel1) channelOpen = true; else return PLErrors::kAudioError; channel2 = PortabilityLayer::SoundSystem::GetInstance()->CreateChannel(); if (channel2) channelOpen = true; else return PLErrors::kAudioError; return PLErrors::kNone; } //-------------------------------------------------------------- CloseSoundChannels void CloseSoundChannels (void) { if (!channelOpen) return; if (channel0 != nil) channel0->Destroy(false); channel0 = nil; if (channel1 != nil) channel1->Destroy(false); channel1 = nil; if (channel2 != nil) channel2->Destroy(false); channel2 = nil; channelOpen = false; } //-------------------------------------------------------------- InitSound void InitSound (void) { PLError_t theErr; if (dontLoadSounds) return; failedSound = false; channel0 = nil; channel1 = nil; channel2 = nil; SoundSync_ClearPriority(0); SoundSync_ClearPriority(1); SoundSync_ClearPriority(2); theErr = LoadBufferSounds(); if (theErr != PLErrors::kNone) { YellowAlert(kYellowFailedSound, theErr); failedSound = true; } if (!failedSound) { theErr = OpenSoundChannels(); if (theErr != PLErrors::kNone) { YellowAlert(kYellowFailedSound, theErr); failedSound = true; } } } //-------------------------------------------------------------- KillSound void KillSound (void) { if (dontLoadSounds) return; DumpBufferSounds(); CloseSoundChannels(); } //-------------------------------------------------------------- SoundBytesNeeded long SoundBytesNeeded (void) { Handle theSound; long totalBytes; short i; totalBytes = 0L; SetResLoad(false); for (i = 0; i < kMaxSounds - 1; i++) { theSound = GetResource('snd ', i + kBaseBufferSoundID); if (theSound == nil) { SetResLoad(true); return -1; } totalBytes += GetMaxResourceSize(theSound); // ReleaseResource(theSound); } SetResLoad(true); return totalBytes; } //-------------------------------------------------------------- TellHerNoSounds void TellHerNoSounds (void) { #define kNoMemForSoundsAlert 1039 short hitWhat; // CenterAlert(kNoMemForSoundsAlert); hitWhat = Alert(kNoMemForSoundsAlert, nil); } //-------------------------------------------------------------- BitchAboutSM3 void BitchAboutSM3 (void) { #define kNoSoundManager3Alert 1030 short hitWhat; // CenterAlert(kNoSoundManager3Alert); hitWhat = Alert(kNoSoundManager3Alert, nil); } //-------------------------------------------------------------- ParseAndConvertSound THandle ParseAndConvertSoundChecked(const THandle &handle) { const uint8_t *dataStart = static_cast(*handle); const size_t size = handle.MMBlock()->m_size; if (size < sizeof(PortabilityLayer::RIFFTag)) return THandle(); PortabilityLayer::RIFFTag mainRiffTag; memcpy(&mainRiffTag, dataStart, sizeof(PortabilityLayer::RIFFTag)); if (mainRiffTag.m_tag != PortabilityLayer::WaveConstants::kRiffChunkID) return THandle(); const uint32_t riffSize = mainRiffTag.m_chunkSize; if (riffSize < 4 || riffSize - 4 > size - sizeof(PortabilityLayer::RIFFTag)) return THandle(); const uint8_t *riffStart = dataStart + sizeof(PortabilityLayer::RIFFTag); const uint8_t *riffEnd = riffStart + riffSize; const uint8_t *formatTagLoc = nullptr; const uint8_t *dataTagLoc = nullptr; LEUInt32_t waveMarker; memcpy(&waveMarker, riffStart, 4); if (waveMarker != PortabilityLayer::WaveConstants::kWaveChunkID) return THandle(); const uint8_t *tagSearchLoc = riffStart + 4; // Find tags while (tagSearchLoc != riffEnd) { if (riffEnd - tagSearchLoc < sizeof(PortabilityLayer::RIFFTag)) return THandle(); PortabilityLayer::RIFFTag riffTag; memcpy(&riffTag, tagSearchLoc, sizeof(PortabilityLayer::RIFFTag)); if (riffTag.m_tag == PortabilityLayer::WaveConstants::kFormatChunkID) formatTagLoc = tagSearchLoc; else if (riffTag.m_tag == PortabilityLayer::WaveConstants::kDataChunkID) dataTagLoc = tagSearchLoc; const uint32_t riffTagSizeUnpadded = riffTag.m_chunkSize; if (riffTagSizeUnpadded == 0xffffffffU) return THandle(); const uint32_t riffTagSizePadded = riffTagSizeUnpadded + (riffTagSizeUnpadded & 1); tagSearchLoc += sizeof(PortabilityLayer::RIFFTag); if (riffEnd - tagSearchLoc < riffTagSizePadded) return THandle(); tagSearchLoc += riffTagSizePadded; } if (formatTagLoc == nullptr || dataTagLoc == nullptr) return THandle(); PortabilityLayer::RIFFTag fmtTag; memcpy(&fmtTag, formatTagLoc, sizeof(PortabilityLayer::RIFFTag)); const uint8_t *formatContents = formatTagLoc + sizeof(PortabilityLayer::RIFFTag); PortabilityLayer::RIFFTag dataTag; memcpy(&dataTag, dataTagLoc, sizeof(PortabilityLayer::RIFFTag)); const uint8_t *dataContents = dataTagLoc + sizeof(PortabilityLayer::RIFFTag); PortabilityLayer::WaveFormatChunkV3 formatChunkV3; memset(&formatChunkV3, 0, sizeof(formatChunkV3)); int formatChunkVersion = 0; size_t copyableSize = 0; if (fmtTag.m_chunkSize >= sizeof(PortabilityLayer::WaveFormatChunkV3)) { formatChunkVersion = 3; copyableSize = sizeof(PortabilityLayer::WaveFormatChunkV3); } else if (fmtTag.m_chunkSize >= sizeof(PortabilityLayer::WaveFormatChunkV2)) { formatChunkVersion = 2; copyableSize = sizeof(PortabilityLayer::WaveFormatChunkV2); } else if (fmtTag.m_chunkSize >= sizeof(PortabilityLayer::WaveFormatChunkV1)) { formatChunkVersion = 1; copyableSize = sizeof(PortabilityLayer::WaveFormatChunkV1); } else return THandle(); memcpy(&formatChunkV3, formatContents, copyableSize); const PortabilityLayer::WaveFormatChunkV2 formatChunkV2 = formatChunkV3.m_v2; const PortabilityLayer::WaveFormatChunkV1 formatChunkV1 = formatChunkV2.m_v1; if (formatChunkV1.m_bitsPerSample != 8) return THandle(); if (formatChunkV1.m_formatCode != PortabilityLayer::WaveConstants::kFormatPCM || formatChunkV1.m_numChannels != 1 || formatChunkV1.m_blockAlignmentBytes != 1 || formatChunkV1.m_bitsPerSample != 8) return THandle(); THandle convertedHandle = THandle(PortabilityLayer::MemoryManager::GetInstance()->AllocHandle(4 + dataTag.m_chunkSize)); if (!convertedHandle) return THandle(); uint8_t *handleData = static_cast(*convertedHandle); uint32_t dataSize = dataTag.m_chunkSize; memcpy(handleData, &dataSize, 4); memcpy(handleData + 4, dataContents, dataSize); return convertedHandle; } THandle ParseAndConvertSound(const THandle &handle) { if (!handle) return THandle(); THandle converted = ParseAndConvertSoundChecked(handle); handle.Dispose(); return converted; }