diff --git a/src/audio/audio_synthesis.c b/src/audio/audio_synthesis.c index f360229a..a9f16173 100644 --- a/src/audio/audio_synthesis.c +++ b/src/audio/audio_synthesis.c @@ -1042,7 +1042,7 @@ Acmd* AudioSynth_ProcessNote(s32 noteIndex, NoteSubEu* noteSub, NoteSynthesisSta numSamplesProcessed += numSamplesToLoadAdj; dmemUncompressedAddrOffset1 = numSamplesToLoadAdj; - if (((synthState->samplePosInt * 2) + (numSamplesToLoadAdj)*SAMPLE_SIZE) < bookSample->size) { + if (((synthState->samplePosInt * 2) + (numSamplesToLoadAdj) * SAMPLE_SIZE) < bookSample->size) { bytesToRead = (numSamplesToLoadAdj) * SAMPLE_SIZE; } else { bytesToRead = bookSample->size - (synthState->samplePosInt * 2); diff --git a/src/port/Engine.cpp b/src/port/Engine.cpp index aaebe67a..dcedb08d 100644 --- a/src/port/Engine.cpp +++ b/src/port/Engine.cpp @@ -244,6 +244,9 @@ GameEngine::GameEngine() { loader->RegisterResourceFactory(std::make_shared(), RESOURCE_FORMAT_BINARY, "SoundFont", static_cast(SF64::ResourceType::SoundFont), 0); + loader->RegisterResourceFactory(std::make_shared(), RESOURCE_FORMAT_XML, + "SoundFont", static_cast(SF64::ResourceType::SoundFont), 0); + prevAltAssets = CVarGetInteger("gEnhancements.Mods.AlternateAssets", 0); gEnableGammaBoost = CVarGetInteger("gGraphics.GammaMode", 0) == 0; context->GetResourceManager()->SetAltAssetsEnabled(prevAltAssets); diff --git a/src/port/resource/importers/audio/SampleFactory.cpp b/src/port/resource/importers/audio/SampleFactory.cpp index 8289266a..994a9131 100644 --- a/src/port/resource/importers/audio/SampleFactory.cpp +++ b/src/port/resource/importers/audio/SampleFactory.cpp @@ -101,12 +101,14 @@ static void Mp3DecoderWorker(std::shared_ptr sample, std::shared_ptrBuffer.get()->data(), sampleFile->Buffer.get()->size(), nullptr); + drmp3_init_memory(&mp3, sampleFile->Buffer->data(), sampleFile->Buffer->size(), nullptr); numFrames = drmp3_get_pcm_frame_count(&mp3); drwav_uint64 channels = mp3.channels; drwav_uint64 sampleRate = mp3.sampleRate; - sample->mSample.sampleAddr = new uint8_t[numFrames * channels * 2]; + sample->mSample.tuning = (float)(sampleRate * channels) / 32000.0f; + sample->mSample.size = numFrames * channels * 2; + sample->mSample.sampleAddr = new uint8_t[sample->mSample.size]; drmp3_read_pcm_frames_s16(&mp3, numFrames, (int16_t*)sample->mSample.sampleAddr); } @@ -131,6 +133,7 @@ static void OggDecoderWorker(std::shared_ptr sample, std::shared_ptrmSample.sampleAddr = new uint8_t[toRead]; + sample->mSample.tuning = (float)(sampleRate * numChannels) / 32000.0f; do { read = ov_read(&vf, dataBuff, 4096, 0, 2, 1, &bitStream); memcpy(sample->mSample.sampleAddr + pos, dataBuff, read); @@ -199,12 +202,13 @@ std::shared_ptr ResourceFactoryXMLSampleV0::ReadResource(std::s drwav_uint64 numFrames; drwav_bool32 ret = - drwav_init_memory(&wav, sampleFile->Buffer.get()->data(), sampleFile->Buffer.get()->size(), nullptr); + drwav_init_memory(&wav, sampleFile->Buffer->data(), sampleFile->Buffer->size(), nullptr); drwav_get_length_in_pcm_frames(&wav, &numFrames); - sample->mSample.tuning = (wav.sampleRate * wav.channels) / 32000.0f; - sample->mSample.sampleAddr = new uint8_t[numFrames * wav.channels * 2]; + sample->mSample.tuning = (float)(wav.sampleRate * wav.channels) / 32000.0f; + sample->mSample.size = numFrames * wav.channels * 2; + sample->mSample.sampleAddr = new uint8_t[sample->mSample.size]; drwav_read_pcm_frames_s16(&wav, numFrames, (int16_t*)sample->mSample.sampleAddr); return sample; diff --git a/src/port/resource/importers/audio/SoundFontFactory.cpp b/src/port/resource/importers/audio/SoundFontFactory.cpp index 431f9410..1fda2854 100644 --- a/src/port/resource/importers/audio/SoundFontFactory.cpp +++ b/src/port/resource/importers/audio/SoundFontFactory.cpp @@ -1,5 +1,7 @@ #include "SoundFontFactory.h" #include "../ResourceUtil.h" +#include "utils/StringHelper.h" +#include #include "port/resource/type/audio/SoundFont.h" namespace SF64 { @@ -29,4 +31,214 @@ std::shared_ptr ResourceFactoryBinarySoundFontV0::ReadResource( return font; } + +int8_t ResourceFactoryXMLSoundFontV0::MediumStrToInt(const char* str) { + if (!strcmp("Ram", str)) { + return MEDIUM_RAM; + } else if (!strcmp("Unk", str)) { + return MEDIUM_UNK; + } else if (!strcmp("Cart", str)) { + return MEDIUM_CART; + } else if (!strcmp("Disk", str)) { + return MEDIUM_DISK_DRIVE; + // 4 is skipped + } else { + throw std::runtime_error( + StringHelper::Sprintf("Bad medium value. Got %s, expected Ram, Unk, Cart, or Disk.", str)); + } +} + +int8_t ResourceFactoryXMLSoundFontV0::CachePolicyToInt(const char* str) { + if (!strcmp("Temporary", str)) { + return CACHE_TEMPORARY; + } else if (!strcmp("Persistent", str)) { + return CACHE_PERSISTENT; + } else if (!strcmp("Either", str)) { + return CACHE_EITHER; + } else if (!strcmp("Permanent", str)) { + return CACHE_PERMANENT; + } else { + throw std::runtime_error(StringHelper::Sprintf( + "Bad cache policy value. Got %s, expected Temporary, Persistent, Either, or Permanent.", str)); + } +} + +void ResourceFactoryXMLSoundFontV0::ParseDrums(SoundFont* soundFont, tinyxml2::XMLElement* element) { + element = (tinyxml2::XMLElement*)element->FirstChildElement(); + // No drums + if (element == nullptr) { + soundFont->mFont.drums = nullptr; + soundFont->mFont.numDrums = 0; + return; + } + + do { + auto drum = new DrumData; + std::vector envelopes; + drum->adsrDecayIndex = element->IntAttribute("ReleaseRate"); + drum->pan = element->IntAttribute("Pan"); + drum->isRelocated = element->IntAttribute("Loaded"); + drum->tunedSample.tuning = element->FloatAttribute("Tuning"); + const char* sampleStr = element->Attribute("SampleRef"); + + if (sampleStr != nullptr && sampleStr[0] != 0) { + auto res = Ship::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(sampleStr); + drum->tunedSample.sample = static_cast(res ? res->GetRawPointer() : nullptr); + } else { + drum->tunedSample.sample = nullptr; + } + + element = (tinyxml2::XMLElement*)element->FirstChildElement(); + if (!strcmp(element->Name(), "Envelopes")) { + // element = (tinyxml2::XMLElement*)element->FirstChildElement(); + unsigned int envCount = 0; + envelopes = ParseEnvelopes(soundFont, element, &envCount); + element = (tinyxml2::XMLElement*)element->Parent(); + drum->envelope = new EnvelopePointData[envelopes.size()]; + memcpy(drum->envelope, envelopes.data(), envelopes.size() * sizeof(EnvelopePointData)); + } else { + drum->envelope = nullptr; + } + + if (drum->tunedSample.sample == nullptr) { + soundFont->mDrums.push_back(nullptr); + } else { + soundFont->mDrums.push_back(drum); + } + + element = element->NextSiblingElement(); + } while (element != nullptr); + + soundFont->mFont.numDrums = soundFont->mDrums.size(); + soundFont->mFont.drums = soundFont->mDrums.data(); +} + +void ResourceFactoryXMLSoundFontV0::ParseInstruments(SoundFont* soundFont, tinyxml2::XMLElement* element) { + element = element->FirstChildElement(); + do { + auto instrument = new InstrumentData; + unsigned int envCount = 0; + std::vector envelopes; + + int isValid = element->BoolAttribute("IsValid"); + instrument->isRelocated = element->IntAttribute("Loaded"); + instrument->normalRangeLo = element->IntAttribute("NormalRangeLo"); + instrument->normalRangeHi = element->IntAttribute("NormalRangeHi"); + instrument->adsrDecayIndex = element->IntAttribute("ReleaseRate"); + tinyxml2::XMLElement* instrumentElement = element->FirstChildElement(); + tinyxml2::XMLElement* instrumentElementCopy = instrumentElement; + + if (instrumentElement != nullptr && !strcmp(instrumentElement->Name(), "Envelopes")) { + envelopes = ParseEnvelopes(soundFont, instrumentElement, &envCount); + instrument->envelope = new EnvelopePointData[envelopes.size()]; + memcpy(instrument->envelope, envelopes.data(), envelopes.size() * sizeof(EnvelopePointData)); + instrumentElement = instrumentElement->NextSiblingElement(); + } + + if (instrumentElement != nullptr && !strcmp("LowNotesSound", instrumentElement->Name())) { + instrument->lowPitchTunedSample.tuning = instrumentElement->FloatAttribute("Tuning"); + const char* sampleStr = instrumentElement->Attribute("SampleRef"); + if (sampleStr != nullptr && sampleStr[0] != 0) { + auto res = static_pointer_cast( + Ship::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(sampleStr, true)); + auto sample = static_cast(res ? res->GetRawPointer() : nullptr); + instrument->lowPitchTunedSample.sample = sample; + if (sample != nullptr && sample->tuning != 0.0f) { + instrument->lowPitchTunedSample.tuning = sample->tuning; + } + } + instrumentElement = instrumentElement->NextSiblingElement(); + } + + if (instrumentElement != nullptr && !strcmp("NormalNotesSound", instrumentElement->Name())) { + instrument->normalPitchTunedSample.tuning = instrumentElement->FloatAttribute("Tuning"); + const char* sampleStr = instrumentElement->Attribute("SampleRef"); + if (sampleStr != nullptr && sampleStr[0] != 0) { + auto res = static_pointer_cast( + Ship::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(sampleStr, true)); + auto sample = static_cast(res ? res->GetRawPointer() : nullptr); + instrument->normalPitchTunedSample.sample = sample; + if (sample != nullptr && sample->tuning != 0.0f) { + instrument->normalPitchTunedSample.tuning = sample->tuning; + } + } + instrumentElement = instrumentElement->NextSiblingElement(); + } + + if (instrumentElement != nullptr && !strcmp("HighNotesSound", instrumentElement->Name())) { + instrument->highPitchTunedSample.tuning = instrumentElement->FloatAttribute("Tuning"); + const char* sampleStr = instrumentElement->Attribute("SampleRef"); + if (sampleStr != nullptr && sampleStr[0] != 0) { + auto res = static_pointer_cast( + Ship::Context::GetInstance()->GetResourceManager()->LoadResourceProcess(sampleStr, true)); + auto sample = static_cast(res ? res->GetRawPointer() : nullptr); + instrument->highPitchTunedSample.sample = sample; + if (sample != nullptr && sample->tuning != 0.0f) { + instrument->highPitchTunedSample.tuning = sample->tuning; + } + } + instrumentElement = instrumentElement->NextSiblingElement(); + } + + soundFont->mInstruments.push_back(instrument); + + element = instrumentElementCopy; + element = (tinyxml2::XMLElement*)element->Parent(); + element = element->NextSiblingElement(); + } while (element != nullptr); + + soundFont->mFont.instruments = soundFont->mInstruments.data(); + soundFont->mFont.numInstruments = soundFont->mInstruments.size(); +} + +std::vector ResourceFactoryXMLSoundFontV0::ParseEnvelopes(SoundFont* soundFont, + tinyxml2::XMLElement* element, + unsigned int* count) { + std::vector envelopes; + unsigned int total = 0; + element = element->FirstChildElement("Envelope"); + while (element != nullptr) { + EnvelopePointData env = { + .delay = (s16)element->IntAttribute("Delay"), + .arg = (s16)element->IntAttribute("Arg"), + }; + envelopes.emplace_back(env); + element = element->NextSiblingElement("Envelope"); + total++; + } + *count = total; + return envelopes; +} + +std::shared_ptr ResourceFactoryXMLSoundFontV0::ReadResource(std::shared_ptr file) { + if (!FileHasValidFormatAndReader(file)) { + return nullptr; + } + auto audioSoundFont = std::make_shared(file->InitData); + auto child = std::get>(file->Reader)->FirstChildElement(); + // Header data + memset(&audioSoundFont->mFont, 0, sizeof(audioSoundFont->mFont)); + + auto shortData1 = child->IntAttribute("Data1"); + auto shortData2 = child->IntAttribute("Data2"); + + audioSoundFont->mFont.numInstruments = (shortData2 >> 8) & 0xFFu; + audioSoundFont->mFont.numDrums = shortData2 & 0xFFu; + audioSoundFont->mFont.sampleBankId1 = (shortData1 >> 8) & 0xFFu; + audioSoundFont->mFont.sampleBankId2 = shortData1 & 0xFFu; + + child = (tinyxml2::XMLElement*)child->FirstChildElement(); + + while (child != nullptr) { + const char* name = child->Name(); + + if (!strcmp(name, "Drums")) { + ParseDrums(audioSoundFont.get(), child); + } else if (!strcmp(name, "Instruments")) { + ParseInstruments(audioSoundFont.get(), child); + } + child = child->NextSiblingElement(); + } + return audioSoundFont; +} } // namespace LUS diff --git a/src/port/resource/importers/audio/SoundFontFactory.h b/src/port/resource/importers/audio/SoundFontFactory.h index 04823a55..b5afda9a 100644 --- a/src/port/resource/importers/audio/SoundFontFactory.h +++ b/src/port/resource/importers/audio/SoundFontFactory.h @@ -1,11 +1,26 @@ #pragma once #include "Resource.h" +#include "ResourceFactoryXML.h" #include "ResourceFactoryBinary.h" +#include "port/resource/type/audio/SoundFont.h" namespace SF64 { class ResourceFactoryBinarySoundFontV0 : public Ship::ResourceFactoryBinary { public: std::shared_ptr ReadResource(std::shared_ptr file) override; }; + +class ResourceFactoryXMLSoundFontV0 : public Ship::ResourceFactoryXML { + public: + std::shared_ptr ReadResource(std::shared_ptr file) override; + static int8_t MediumStrToInt(const char* str); + static int8_t CachePolicyToInt(const char* str); + + private: + void ParseDrums(SoundFont* soundFont, tinyxml2::XMLElement* element); + void ParseInstruments(SoundFont* soundFont, tinyxml2::XMLElement* element); + std::vector ParseEnvelopes(SoundFont* soundFont, tinyxml2::XMLElement* element, + unsigned int* count); +}; }; // namespace LUS diff --git a/tools/Torch b/tools/Torch index 053d97a4..27af7233 160000 --- a/tools/Torch +++ b/tools/Torch @@ -1 +1 @@ -Subproject commit 053d97a433f3cfc9607b7cedb512d2e7ee1dc78a +Subproject commit 27af72331ceba7703f0382bb0316320baed377a3