AudioData LoadSoundCodec(std::string filename) { std::string ext = FS::Path::Extension(filename); // if filename has extension, try to load it if (ext != "") { // look for the correct loader and use it for (int i = 0; i < numSoundLoaders; i++) { if (ext == soundLoaders[i].ext) { // if file exists, load it if (FS::PakPath::FileExists(filename)) { return soundLoaders[i].SoundLoader(filename); } } } } // if filename does not have extension or there is no file with such extension // or if there is no codec available for this file format, // try and find a suitable match using all the sound file formats supported // prioritize with the pak priority int bestLoader = -1; const FS::PakInfo* bestPak = nullptr; std::string strippedname = FS::Path::StripExtension(filename); for (int i = 0; i < numSoundLoaders; i++) { std::string altName = Str::Format("%s%s", strippedname, soundLoaders[i].ext); const FS::PakInfo* pak = FS::PakPath::LocateFile(altName); // We found a file and its pak is better than the best pak we have // this relies on how the filesystem works internally and should be moved // to a more explicit interface once there is one. (FIXME) if (pak != nullptr && (bestPak == nullptr || pak < bestPak )) { bestPak = pak; bestLoader = i; } } if (bestLoader >= 0) { std::string altName = Str::Format("%s%s", strippedname, soundLoaders[bestLoader].ext ); return soundLoaders[bestLoader].SoundLoader(altName); } if (FS::PakPath::FileExists(filename)) { audioLogs.Warn("No codec available for opening %s.", filename); return AudioData(); } audioLogs.Debug("Sound file %s not found.", filename); return AudioData(); }
AudioData LoadOggCodec(std::string filename) { std::string audioFile; try { audioFile = FS::PakPath::ReadFile(filename); } catch (std::system_error& err) { audioLogs.Warn("Failed to open %s: %s", filename, err.what()); return AudioData(); } OggDataSource dataSource = {&audioFile, 0}; std::unique_ptr<OggVorbis_File> vorbisFile(new OggVorbis_File); if (ov_open_callbacks(&dataSource, vorbisFile.get(), nullptr, 0, Ogg_Callbacks) != 0) { audioLogs.Warn("Error while reading %s", filename); ov_clear(vorbisFile.get()); return AudioData(); } if (ov_streams(vorbisFile.get()) != 1) { audioLogs.Warn("Unsupported number of streams in %s.", filename); ov_clear(vorbisFile.get()); return AudioData(); } vorbis_info* oggInfo = ov_info(vorbisFile.get(), 0); if (!oggInfo) { audioLogs.Warn("Could not read vorbis_info in %s.", filename); ov_clear(vorbisFile.get()); return AudioData(); } const int sampleWidth = 2; int sampleRate = oggInfo->rate; int numberOfChannels = oggInfo->channels; char buffer[4096]; int bytesRead = 0; int bitStream = 0; std::vector<char> samples; while ((bytesRead = ov_read(vorbisFile.get(), buffer, 4096, 0, sampleWidth, 1, &bitStream)) > 0) { std::copy_n(buffer, bytesRead, std::back_inserter(samples)); } ov_clear(vorbisFile.get()); char* rawSamples = new char[samples.size()]; std::copy_n(samples.data(), samples.size(), rawSamples); return AudioData(sampleRate, sampleWidth, numberOfChannels, samples.size(), rawSamples); }
AudioData LoadOpusCodec(std::string filename) { std::string audioFile; try { audioFile = FS::PakPath::ReadFile(filename); } catch (std::system_error& err) { audioLogs.Warn("Failed to open %s: %s", filename, err.what()); return AudioData(); } OpusDataSource dataSource = {&audioFile, 0}; OggOpusFile* opusFile = op_open_callbacks(&dataSource, &Opus_Callbacks, nullptr, 0, nullptr); if (!opusFile) { audioLogs.Warn("Error while reading %s", filename); return AudioData(); } const OpusHead* opusInfo = op_head(opusFile, -1); if (!opusInfo) { op_free(opusFile); audioLogs.Warn("Could not read OpusHead in %s", filename); return AudioData(); } if (opusInfo->stream_count != 1) { op_free(opusFile); audioLogs.Warn("Only one stream is supported in Opus files: %s", filename); return AudioData(); } if (opusInfo->channel_count != 1 && opusInfo->channel_count != 2) { op_free(opusFile); audioLogs.Warn("Only mono and stereo Opus files are supported: %s", filename); return AudioData(); } const int sampleWidth = 2; int sampleRate = 48000; int numberOfChannels = opusInfo->channel_count; // The buffer is big enough to hold 120ms worth of samples per channel opus_int16* buffer = new opus_int16[numberOfChannels * 5760]; int samplesPerChannelRead = 0; std::vector<opus_int16> samples; while ((samplesPerChannelRead = op_read(opusFile, buffer, sampleWidth * numberOfChannels * 5760, nullptr)) > 0) { std::copy_n(buffer, samplesPerChannelRead * numberOfChannels, std::back_inserter(samples)); } op_free(opusFile); char* rawSamples = new char[sampleWidth * samples.size()]; std::copy_n(reinterpret_cast<char*>(samples.data()), sampleWidth * samples.size(), rawSamples); return AudioData(sampleRate, sampleWidth, numberOfChannels, samples.size() * sampleWidth, rawSamples); }
AudioData LoadWavCodec(std::string filename) { std::string audioFile; try { audioFile = std::move(FS::PakPath::ReadFile(filename)); } catch (std::system_error& err) { audioLogs.Warn("Failed to open %s: %s", filename, err.what()); return AudioData(); } std::string format = audioFile.substr(8, 4); if (format != "WAVE") { audioLogs.Warn("The format label in %s is not \"WAVE\".", filename); return AudioData(); } std::string chunk1ID = audioFile.substr(12, 4); if (chunk1ID != "fmt ") { audioLogs.Warn("The Chunk1ID in %s is not \"fmt\".", filename); return AudioData(); } int numChannels = PackChars(audioFile, 22, 2); if (numChannels != 1 && numChannels != 2) { audioLogs.Warn("%s has an unsupported number of channels.", filename); return AudioData(); } int sampleRate = PackChars(audioFile, 24, 4); int byteDepth = PackChars(audioFile, 34, 2) / 8; if (byteDepth != 1 && byteDepth != 2) { audioLogs.Warn("%s has an unsupported bytedepth.", filename); return AudioData(); } //TODO find the position of "data" std::size_t dataOffset{audioFile.find("data", 36)}; if (dataOffset == std::string::npos) { audioLogs.Warn("Could not find the data chunk in %s", filename); return AudioData(); } std::string chunk2ID = audioFile.substr(dataOffset, 4); int size = PackChars(audioFile, dataOffset + 4, 4); if (size <= 0 || sampleRate <=0 ){ audioLogs.Warn("Error in reading %s.", filename); return AudioData(); } char* data = new char[size]; std::copy_n(audioFile.data() + dataOffset + 8, size, data); return AudioData(sampleRate, byteDepth, numChannels, size, data); }