diff options
Diffstat (limited to 'source/core/StarAudio.cpp')
-rw-r--r-- | source/core/StarAudio.cpp | 191 |
1 files changed, 112 insertions, 79 deletions
diff --git a/source/core/StarAudio.cpp b/source/core/StarAudio.cpp index a1f2e62..1b7e78e 100644 --- a/source/core/StarAudio.cpp +++ b/source/core/StarAudio.cpp @@ -6,10 +6,13 @@ #include "StarAudio.hpp" #include "StarBuffer.hpp" +#include "StarIODeviceCallbacks.hpp" #include "StarFile.hpp" #include "StarFormat.hpp" #include "StarLogging.hpp" #include "StarDataStreamDevices.hpp" +#include "StarSha256.hpp" +#include "StarEncode.hpp" namespace Star { @@ -35,9 +38,10 @@ float amplitudeToPerceptual(float amp, float normalizedMax, float range, float b namespace { struct WaveData { - ByteArrayPtr byteArray; + IODevicePtr device; unsigned channels; unsigned sampleRate; + size_t dataSize; // get the data size from the header to avoid id3 tag }; template <typename T> @@ -141,59 +145,59 @@ namespace { device->size(), wavDataSize + wavDataOffset)); } - ByteArrayPtr pcmData = make_shared<ByteArray>(); - pcmData->resize(wavDataSize); - - // Copy across data and perform and endianess conversion if needed - device->readFull(pcmData->ptr(), pcmData->size()); - for (size_t i = 0; i < pcmData->size() / 2; ++i) - fromByteOrder(ByteOrder::LittleEndian, pcmData->ptr() + i * 2, 2); - - return WaveData{std::move(pcmData), wavChannels, wavSampleRate}; + // Return the original device positioned at the PCM data + // Note: This means the caller owns handling endianness conversion + device->seek(wavDataOffset); + + return WaveData{device, wavChannels, wavSampleRate, wavDataSize}; } } class CompressedAudioImpl { public: - static size_t readFunc(void* ptr, size_t size, size_t nmemb, void* datasource) { - return static_cast<ExternalBuffer*>(datasource)->read((char*)ptr, size * nmemb) / size; - } - static int seekFunc(void* datasource, ogg_int64_t offset, int whence) { - static_cast<ExternalBuffer*>(datasource)->seek(offset, (IOSeek)whence); - return 0; - }; + CompressedAudioImpl(CompressedAudioImpl const& impl) + : m_audioData(impl.m_audioData->clone()) // Clone instead of sharing + , m_deviceCallbacks(m_audioData) // Pass reference to cloned data + , m_vorbisInfo(nullptr) { + setupCallbacks(); - static long int tellFunc(void* datasource) { - return (long int)static_cast<ExternalBuffer*>(datasource)->pos(); - }; + // Make sure data stream is ready to be read + m_audioData->open(IOMode::Read); + m_audioData->seek(0); + + // Add error checking to see what's happening with the clone + if (!m_audioData->isOpen()) + throw AudioException("Failed to open cloned audio device"); - CompressedAudioImpl(CompressedAudioImpl const& impl) { - m_audioData = impl.m_audioData; - m_memoryFile.reset(m_audioData->ptr(), m_audioData->size()); - m_vorbisInfo = nullptr; + auto size = m_audioData->size(); + if (size <= 0) + throw AudioException("Cloned audio device has no data"); } - CompressedAudioImpl(IODevicePtr audioData) { - audioData->open(IOMode::Read); - audioData->seek(0); - m_audioData = make_shared<ByteArray>(audioData->readBytes((size_t)audioData->size())); - m_memoryFile.reset(m_audioData->ptr(), m_audioData->size()); - m_vorbisInfo = nullptr; + CompressedAudioImpl(IODevicePtr audioData) + : m_audioData(audioData->clone()) // Clone instead of taking ownership + , m_deviceCallbacks(m_audioData) // Pass reference + , m_vorbisInfo(nullptr) { + setupCallbacks(); + m_audioData->open(IOMode::Read); + m_audioData->seek(0); } ~CompressedAudioImpl() { ov_clear(&m_vorbisFile); } - bool open() { - m_callbacks.read_func = readFunc; - m_callbacks.seek_func = seekFunc; - m_callbacks.tell_func = tellFunc; - m_callbacks.close_func = NULL; + void setupCallbacks() { + m_deviceCallbacks.setupOggCallbacks(m_callbacks); + } - if (ov_open_callbacks(&m_memoryFile, &m_vorbisFile, NULL, 0, m_callbacks) < 0) + bool open() { + int result = ov_open_callbacks(&m_deviceCallbacks, &m_vorbisFile, NULL, 0, m_callbacks); + if (result < 0) { + Logger::error("Failed to open ogg stream: error code {}", result); return false; + } m_vorbisInfo = ov_info(&m_vorbisFile, -1); return true; @@ -252,14 +256,14 @@ public: } while (read == OV_HOLE); if (read < 0) throw AudioException::format("Error in Audio::read ({})", read); - + // read in bytes, returning number of int16_t samples. return read / 2; } - + private: - ByteArrayConstPtr m_audioData; - ExternalBuffer m_memoryFile; + IODevicePtr m_audioData; + IODeviceCallbacks m_deviceCallbacks; ov_callbacks m_callbacks; OggVorbis_File m_vorbisFile; vorbis_info* m_vorbisInfo; @@ -267,41 +271,49 @@ private: class UncompressedAudioImpl { public: - UncompressedAudioImpl(UncompressedAudioImpl const& impl) { - m_channels = impl.m_channels; - m_sampleRate = impl.m_sampleRate; - m_audioData = impl.m_audioData; - m_memoryFile.reset(m_audioData->ptr(), m_audioData->size()); - } - + UncompressedAudioImpl(UncompressedAudioImpl const& impl) + : m_device(impl.m_device->clone()) + , m_channels(impl.m_channels) + , m_sampleRate(impl.m_sampleRate) + , m_dataSize(impl.m_dataSize) + , m_dataStart(impl.m_dataStart) + + { + StreamOffset initialPos = m_device->pos(); // Store initial position + if (!m_device->isOpen()) + m_device->open(IOMode::Read); + m_device->seek(initialPos); // Restore position after open + } + UncompressedAudioImpl(CompressedAudioImpl& impl) { m_channels = impl.channels(); m_sampleRate = impl.sampleRate(); + // Create a memory buffer to store decompressed data + auto memDevice = make_shared<Buffer>(); + int16_t buffer[1024]; - Buffer uncompressBuffer; while (true) { size_t ramt = impl.readPartial(buffer, 1024); - - if (ramt == 0) { - // End of stream reached + if (ramt == 0) break; - } else { - uncompressBuffer.writeFull((char*)buffer, ramt * 2); - } + memDevice->writeFull((char*)buffer, ramt * 2); } - m_audioData = make_shared<ByteArray>(uncompressBuffer.takeData()); - m_memoryFile.reset(m_audioData->ptr(), m_audioData->size()); + m_device = memDevice; } - UncompressedAudioImpl(ByteArrayConstPtr data, unsigned channels, unsigned sampleRate) { - m_channels = channels; - m_sampleRate = sampleRate; - m_audioData = std::move(data); - m_memoryFile.reset(m_audioData->ptr(), m_audioData->size()); + UncompressedAudioImpl(IODevicePtr device, unsigned channels, unsigned sampleRate, size_t dataSize) + : m_device(std::move(device)) + , m_channels(channels) + , m_sampleRate(sampleRate) + , m_dataSize(dataSize) + , m_dataStart((size_t)m_device->pos()) // Store current position as data start + { + if (!m_device->isOpen()) + m_device->open(IOMode::Read); } - + bool open() { return true; } @@ -319,7 +331,7 @@ public: } uint64_t totalSamples() { - return m_memoryFile.dataSize() / 2 / m_channels; + return m_device->size() / 2 / m_channels; } void seekTime(double time) { @@ -327,7 +339,7 @@ public: } void seekSample(uint64_t pos) { - m_memoryFile.seek(pos * 2 * m_channels); + m_device->seek(pos * 2 * m_channels); } double currentTime() { @@ -335,20 +347,41 @@ public: } uint64_t currentSample() { - return m_memoryFile.pos() / 2 / m_channels; + return m_device->pos() / 2 / m_channels; } + size_t readPartial(int16_t* buffer, size_t bufferSize) { if (bufferSize != NPos) bufferSize = bufferSize * 2; - return m_memoryFile.read((char*)buffer, bufferSize) / 2; + + // Calculate remaining valid data + size_t currentPos = m_device->pos() - m_dataStart; + size_t remainingBytes = m_dataSize - currentPos; + + // Limit read to remaining valid data + if (bufferSize > remainingBytes) + bufferSize = remainingBytes; + + if (bufferSize == 0) + return 0; + + size_t bytesRead = m_device->read((char*)buffer, bufferSize); + + // Handle endianness conversion + for (size_t i = 0; i < bytesRead / 2; ++i) + fromByteOrder(ByteOrder::LittleEndian, ((char*)buffer) + i * 2, 2); + + return bytesRead / 2; + } private: + IODevicePtr m_device; unsigned m_channels; unsigned m_sampleRate; - ByteArrayConstPtr m_audioData; - ExternalBuffer m_memoryFile; + size_t m_dataSize; + size_t m_dataStart; }; Audio::Audio(IODevicePtr device, String name) { @@ -358,7 +391,7 @@ Audio::Audio(IODevicePtr device, String name) { if (isUncompressed(device)) { WaveData data = parseWav(device); - m_uncompressed = make_shared<UncompressedAudioImpl>(std::move(data.byteArray), data.channels, data.sampleRate); + m_uncompressed = make_shared<UncompressedAudioImpl>(std::move(data.device), data.channels, data.sampleRate, data.dataSize); } else { m_compressed = make_shared<CompressedAudioImpl>(device); if (!m_compressed->open()) @@ -375,16 +408,16 @@ Audio::Audio(Audio&& audio) { } Audio& Audio::operator=(Audio const& audio) { - if (audio.m_uncompressed) { - m_uncompressed = make_shared<UncompressedAudioImpl>(*audio.m_uncompressed); - m_uncompressed->open(); - } else { - m_compressed = make_shared<CompressedAudioImpl>(*audio.m_compressed); - m_compressed->open(); - } - - seekSample(audio.currentSample()); - return *this; + if (audio.m_uncompressed) { + m_uncompressed = make_shared<UncompressedAudioImpl>(*audio.m_uncompressed); + m_uncompressed->open(); + } else { + m_compressed = make_shared<CompressedAudioImpl>(*audio.m_compressed); + if (!m_compressed->open()) // Check the return value + throw AudioException("Failed to open compressed audio stream during copy"); + seekSample(audio.currentSample()); // Only seek after successful open + } + return *this; } Audio& Audio::operator=(Audio&& audio) { |