/
reds
/
OpenDAW
Обзор
Документация
Войти
/
reds
/
OpenDAW
Код
Запросы
0
Задачи
Вики
Пакеты
0
Релизы
0
CI/CD
Аналитика
Безопасность
develop
src/audio/WavReader.cpp
307 строк
10 KB
OpenDAW Developer
feat(v0.5.0): Add WAV Playback and MIDI Synthesizer
21 фев 2026, 03:00
21 фев 2026, 03:00
95e7c6e
Код
Авторство
О чём код?
#include <opendaw/audio/WavReader.h> #include <fstream> #include <iostream> #include <cstring> #include <cmath> namespace opendaw { // ============================================================================ // WavReader Implementation // ============================================================================ bool WavReader::load(const std::string& path) { clear(); filePath_ = path; std::ifstream file(path, std::ios::binary); if (!file) { error_ = "Cannot open file: " + path; return false; } if (!parseHeader(file)) { return false; } if (!parseData(file)) { return false; } info_.duration = static_cast<double>(info_.numSamples) / info_.sampleRate; std::cout << "[WavReader] Loaded: " << path << std::endl; std::cout << " Sample rate: " << info_.sampleRate << " Hz" << std::endl; std::cout << " Channels: " << info_.numChannels << std::endl; std::cout << " Bits: " << info_.bitsPerSample << std::endl; std::cout << " Duration: " << info_.duration << " s" << std::endl; return true; } bool WavReader::parseHeader(std::ifstream& file) { // RIFF header char riff[4]; file.read(riff, 4); if (strncmp(riff, "RIFF", 4) != 0) { error_ = "Not a RIFF file"; return false; } uint32_t fileSize; file.read(reinterpret_cast<char*>(&fileSize), 4); char wave[4]; file.read(wave, 4); if (strncmp(wave, "WAVE", 4) != 0) { error_ = "Not a WAVE file"; return false; } // Find fmt chunk char chunkId[4]; uint32_t chunkSize; while (file.read(chunkId, 4)) { file.read(reinterpret_cast<char*>(&chunkSize), 4); if (strncmp(chunkId, "fmt ", 4) == 0) { // fmt chunk found uint16_t audioFormat; file.read(reinterpret_cast<char*>(&audioFormat), 2); if (audioFormat != 1 && audioFormat != 3) { // 1 = PCM, 3 = IEEE float error_ = "Unsupported audio format: " + std::to_string(audioFormat); return false; } file.read(reinterpret_cast<char*>(&info_.numChannels), 2); file.read(reinterpret_cast<char*>(&info_.sampleRate), 4); file.seekg(4, std::ios::cur); // Byte rate file.seekg(2, std::ios::cur); // Block align file.read(reinterpret_cast<char*>(&info_.bitsPerSample), 2); // Skip remaining fmt chunk data int remaining = chunkSize - 16; if (remaining > 0) { file.seekg(remaining, std::ios::cur); } return true; } else { // Skip this chunk file.seekg(chunkSize, std::ios::cur); } } error_ = "fmt chunk not found"; return false; } bool WavReader::parseData(std::ifstream& file) { char chunkId[4]; uint32_t chunkSize; while (file.read(chunkId, 4)) { file.read(reinterpret_cast<char*>(&chunkSize), 4); if (strncmp(chunkId, "data", 4) == 0) { // data chunk found info_.numSamples = chunkSize / (info_.numChannels * info_.bitsPerSample / 8); // Read raw data std::vector<uint8_t> rawData(chunkSize); file.read(reinterpret_cast<char*>(rawData.data()), chunkSize); // Convert to float convertToFloat(rawData.data(), info_.numSamples * info_.numChannels); return true; } else { // Skip this chunk file.seekg(chunkSize, std::ios::cur); } } error_ = "data chunk not found"; return false; } void WavReader::convertToFloat(const uint8_t* data, size_t numSamples) { samples_.resize(info_.numChannels); size_t bytesPerSample = info_.bitsPerSample / 8; size_t bytesPerFrame = bytesPerSample * info_.numChannels; for (int ch = 0; ch < info_.numChannels; ++ch) { samples_[ch].resize(numSamples / info_.numChannels); } const float scale = 1.0f / (1 << (info_.bitsPerSample - 1)); for (size_t i = 0; i < numSamples; ++i) { int channel = i % info_.numChannels; int sampleIndex = i / info_.numChannels; int32_t sample = 0; if (info_.bitsPerSample == 16) { sample = static_cast<int16_t>( data[i * 2] | (data[i * 2 + 1] << 8) ); } else if (info_.bitsPerSample == 24) { sample = static_cast<int32_t>( data[i * 3] | (data[i * 3 + 1] << 8) | (data[i * 3 + 2] << 16) ); // Sign extend if (sample & 0x800000) { sample |= 0xFF000000; } } else if (info_.bitsPerSample == 32) { sample = static_cast<int32_t>( data[i * 4] | (data[i * 4 + 1] << 8) | (data[i * 4 + 2] << 16) | (data[i * 4 + 3] << 24) ); } samples_[channel][sampleIndex] = static_cast<float>(sample) * scale; } } const std::vector<float>& WavReader::getSamples(int channel) const { if (channel < 0 || channel >= static_cast<int>(samples_.size())) { static std::vector<float> empty; return empty; } return samples_[channel]; } AudioBuffer<float> WavReader::getAudioBuffer() const { AudioBuffer<float> buffer(info_.numChannels, getNumSamples()); for (int ch = 0; ch < info_.numChannels; ++ch) { const auto& channelSamples = samples_[ch]; float* dest = buffer.getChannel(ch); std::copy(channelSamples.begin(), channelSamples.end(), dest); } return buffer; } void WavReader::clear() { info_ = WavInfo(); samples_.clear(); error_.clear(); filePath_.clear(); } // ============================================================================ // WavWriter Implementation // ============================================================================ bool WavWriter::save(const std::string& path, const AudioBuffer<float>& buffer, uint32_t sampleRate, uint16_t bitsPerSample) { std::ofstream file(path, std::ios::binary); if (!file) { return false; } WavInfo info; info.sampleRate = sampleRate; info.numChannels = buffer.getNumChannels(); info.bitsPerSample = bitsPerSample; info.numSamples = buffer.getNumSamples(); if (!writeHeader(file, info)) { return false; } if (!writeData(file, buffer)) { return false; } std::cout << "[WavWriter] Saved: " << path << std::endl; return true; } bool WavWriter::writeHeader(std::ofstream& file, const WavInfo& info) { uint32_t dataSize = info.numSamples * info.numChannels * info.bitsPerSample / 8; uint32_t fileSize = 36 + dataSize; // RIFF header file.write("RIFF", 4); file.write(reinterpret_cast<const char*>(&fileSize), 4); file.write("WAVE", 4); // fmt chunk file.write("fmt ", 4); uint32_t fmtSize = 16; file.write(reinterpret_cast<const char*>(&fmtSize), 4); uint16_t audioFormat = 1; // PCM file.write(reinterpret_cast<const char*>(&audioFormat), 2); file.write(reinterpret_cast<const char*>(&info.numChannels), 2); file.write(reinterpret_cast<const char*>(&info.sampleRate), 4); uint32_t byteRate = info.sampleRate * info.numChannels * info.bitsPerSample / 8; file.write(reinterpret_cast<const char*>(&byteRate), 4); uint16_t blockAlign = info.numChannels * info.bitsPerSample / 8; file.write(reinterpret_cast<const char*>(&blockAlign), 2); file.write(reinterpret_cast<const char*>(&info.bitsPerSample), 2); // data chunk header (will write data later) file.write("data", 4); file.write(reinterpret_cast<const char*>(&dataSize), 4); return file.good(); } bool WavWriter::writeData(std::ofstream& file, const AudioBuffer<float>& buffer) { std::vector<uint8_t> data; convertFromFloat(data, buffer, 16); // Default to 16-bit file.write(reinterpret_cast<const char*>(data.data()), data.size()); return file.good(); } void WavWriter::convertFromFloat(std::vector<uint8_t>& data, const AudioBuffer<float>& buffer, uint16_t bitsPerSample) { size_t numSamples = buffer.getNumSamples() * buffer.getNumChannels(); size_t bytesPerSample = bitsPerSample / 8; data.resize(numSamples * bytesPerSample); const float scale = static_cast<float>(1 << (bitsPerSample - 1)); for (int ch = 0; ch < buffer.getNumChannels(); ++ch) { const float* channelData = buffer.getChannel(ch); for (size_t i = 0; i < buffer.getNumSamples(); ++i) { size_t sampleIndex = i * buffer.getNumChannels() + ch; size_t byteIndex = sampleIndex * bytesPerSample; float sample = channelData[i]; sample = std::clamp(sample, -1.0f, 1.0f); int32_t intSample = static_cast<int32_t>(sample * scale); if (bitsPerSample == 16) { data[byteIndex] = intSample & 0xFF; data[byteIndex + 1] = (intSample >> 8) & 0xFF; } else if (bitsPerSample == 24) { data[byteIndex] = intSample & 0xFF; data[byteIndex + 1] = (intSample >> 8) & 0xFF; data[byteIndex + 2] = (intSample >> 16) & 0xFF; } else if (bitsPerSample == 32) { data[byteIndex] = intSample & 0xFF; data[byteIndex + 1] = (intSample >> 8) & 0xFF; data[byteIndex + 2] = (intSample >> 16) & 0xFF; data[byteIndex + 3] = (intSample >> 24) & 0xFF; } } } } } // namespace opendaw