#include #include #include #include namespace XCEngine { namespace Resources { namespace { struct WAVHeader { uint32_t sampleRate = 44100; uint32_t channels = 2; uint32_t bitsPerSample = 16; uint32_t dataSize = 0; uint32_t dataOffset = 44; }; bool ParseWAVHeader(const uint8_t* data, size_t size, WAVHeader& header) { if (size < 44) { return false; } if (data[0] != 'R' || data[1] != 'I' || data[2] != 'F' || data[3] != 'F') { return false; } if (data[8] != 'W' || data[9] != 'A' || data[10] != 'V' || data[11] != 'E') { return false; } if (data[12] != 'f' || data[13] != 'm' || data[14] != 't' || data[15] != ' ') { return false; } uint32_t subchunk1Size = *reinterpret_cast(&data[16]); if (subchunk1Size < 16) { return false; } uint16_t audioFormat = *reinterpret_cast(&data[20]); if (audioFormat != 1) { return false; } header.channels = *reinterpret_cast(&data[22]); header.sampleRate = *reinterpret_cast(&data[24]); header.bitsPerSample = *reinterpret_cast(&data[34]); if (data[36] != 'd' || data[37] != 'a' || data[38] != 't' || data[39] != 'a') { return false; } header.dataSize = *reinterpret_cast(&data[40]); header.dataOffset = 44; if (header.dataOffset + header.dataSize > size) { return false; } return true; } } // namespace AudioLoader::AudioLoader() = default; AudioLoader::~AudioLoader() = default; Containers::Array AudioLoader::GetSupportedExtensions() const { Containers::Array extensions; extensions.PushBack("wav"); extensions.PushBack("ogg"); extensions.PushBack("mp3"); extensions.PushBack("flac"); extensions.PushBack("aiff"); extensions.PushBack("aif"); return extensions; } bool AudioLoader::CanLoad(const Containers::String& path) const { Containers::String ext = GetExtension(path); return ext == "wav" || ext == "ogg" || ext == "mp3" || ext == "flac" || ext == "aiff" || ext == "aif"; } LoadResult AudioLoader::Load(const Containers::String& path, const ImportSettings* settings) { Containers::Array data = ReadFileData(path); if (data.Empty()) { return LoadResult("Failed to read audio file: " + path); } AudioClip* audioClip = new AudioClip(); audioClip->m_path = path; audioClip->m_name = path; audioClip->m_guid = ResourceGUID::Generate(path); AudioFormat format = DetectAudioFormat(path, data); if (format == AudioFormat::WAV) { if (!ParseWAVData(data, audioClip)) { delete audioClip; return LoadResult("Failed to parse WAV data"); } } audioClip->SetAudioFormat(format); audioClip->SetAudioData(data); audioClip->m_isValid = true; audioClip->m_memorySize = sizeof(AudioClip) + audioClip->m_name.Length() + audioClip->m_path.Length() + audioClip->GetAudioData().Size(); return LoadResult(audioClip); } ImportSettings* AudioLoader::GetDefaultSettings() const { return nullptr; } bool AudioLoader::ParseWAVData(const Containers::Array& data, AudioClip* audioClip) { WAVHeader header; if (!ParseWAVHeader(data.Data(), data.Size(), header)) { return false; } audioClip->SetSampleRate(header.sampleRate); audioClip->SetChannels(header.channels); audioClip->SetBitsPerSample(header.bitsPerSample); uint32_t bytesPerSample = header.bitsPerSample / 8; uint32_t totalSamples = header.dataSize / (bytesPerSample * header.channels); float duration = static_cast(totalSamples) / (header.sampleRate * header.channels); audioClip->SetDuration(duration); return true; } AudioFormat AudioLoader::DetectAudioFormat(const Containers::String& path, const Containers::Array& data) { Containers::String ext = GetExtension(path); if (ext == "wav") return AudioFormat::WAV; if (ext == "ogg") return AudioFormat::OGG; if (ext == "mp3") return AudioFormat::MP3; if (ext == "flac") return AudioFormat::FLAC; if (data.Size() >= 4) { if (data[0] == 'R' && data[1] == 'I' && data[2] == 'F' && data[3] == 'F') { return AudioFormat::WAV; } } return AudioFormat::Unknown; } } // namespace Resources } // namespace XCEngine