diff options
Diffstat (limited to 'source/core')
-rw-r--r-- | source/core/CMakeLists.txt | 4 | ||||
-rw-r--r-- | source/core/StarAudio.cpp | 191 | ||||
-rw-r--r-- | source/core/StarBuffer.cpp | 13 | ||||
-rw-r--r-- | source/core/StarBuffer.hpp | 4 | ||||
-rw-r--r-- | source/core/StarCompression.cpp | 12 | ||||
-rw-r--r-- | source/core/StarCompression.hpp | 2 | ||||
-rw-r--r-- | source/core/StarFile.cpp | 11 | ||||
-rw-r--r-- | source/core/StarFile.hpp | 2 | ||||
-rw-r--r-- | source/core/StarIODevice.hpp | 3 | ||||
-rw-r--r-- | source/core/StarIODeviceCallbacks.cpp | 39 | ||||
-rw-r--r-- | source/core/StarIODeviceCallbacks.hpp | 37 |
11 files changed, 238 insertions, 80 deletions
diff --git a/source/core/CMakeLists.txt b/source/core/CMakeLists.txt index 3d00baf..63f86cf 100644 --- a/source/core/CMakeLists.txt +++ b/source/core/CMakeLists.txt @@ -10,6 +10,7 @@ SET (star_core_HEADERS StarAssetPath.hpp StarAtomicSharedPtr.hpp StarAudio.hpp + StarIODeviceCallbacks.hpp StarBTree.hpp StarBTreeDatabase.hpp StarBiMap.hpp @@ -141,6 +142,7 @@ SET (star_core_SOURCES StarBuffer.cpp StarByteArray.cpp StarColor.cpp + StarIODeviceCallbacks.cpp StarCompression.cpp StarCurve25519.cpp StarDataStream.cpp @@ -235,4 +237,4 @@ IF(STAR_USE_JEMALLOC AND JEMALLOC_IS_PREFIXED) SET_SOURCE_FILES_PROPERTIES(StarMemory.cpp PROPERTIES COMPILE_DEFINITIONS STAR_JEMALLOC_IS_PREFIXED ) -ENDIF()
\ No newline at end of file +ENDIF() 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) { diff --git a/source/core/StarBuffer.cpp b/source/core/StarBuffer.cpp index 5b5b2e4..e8184e3 100644 --- a/source/core/StarBuffer.cpp +++ b/source/core/StarBuffer.cpp @@ -2,6 +2,7 @@ #include "StarMathCommon.hpp" #include "StarIODevice.hpp" #include "StarFormat.hpp" +#include "StarLogging.hpp" namespace Star { @@ -273,6 +274,18 @@ void ExternalBuffer::reset(char const* externalData, size_t len) { m_size = len; } +IODevicePtr Buffer::clone() { + auto cloned = make_shared<Buffer>(*this); + // Reset position to 0 while preserving mode and data + cloned->seek(0); + return cloned; +} + +IODevicePtr ExternalBuffer::clone() { + Logger::info("Cloning ExternalBuffer from position {}"); + return make_shared<ExternalBuffer>(*this); +} + size_t ExternalBuffer::doRead(size_t pos, char* data, size_t len) { if (len == 0) return 0; diff --git a/source/core/StarBuffer.hpp b/source/core/StarBuffer.hpp index 0f9864a..4870b53 100644 --- a/source/core/StarBuffer.hpp +++ b/source/core/StarBuffer.hpp @@ -34,6 +34,8 @@ public: String deviceName() const override; StreamOffset size() override; + + IODevicePtr clone() override; ByteArray& data(); ByteArray const& data() const; @@ -95,6 +97,8 @@ public: String deviceName() const override; StreamOffset size() override; + + IODevicePtr clone() override; // Returns a pointer to the beginning of the Buffer. char const* ptr() const; diff --git a/source/core/StarCompression.cpp b/source/core/StarCompression.cpp index 58b43ef..d99745c 100644 --- a/source/core/StarCompression.cpp +++ b/source/core/StarCompression.cpp @@ -233,4 +233,16 @@ void CompressedFile::close() { setMode(IOMode::Closed); } +IODevicePtr CompressedFile::clone() { + auto cloned = make_shared<CompressedFile>(m_filename); + cloned->setCompression(m_compression); + if (isOpen()) { + // Open with same mode + cloned->open(mode()); + // Seek to same position + cloned->seek(pos()); + } + return cloned; +} + } diff --git a/source/core/StarCompression.hpp b/source/core/StarCompression.hpp index 3322662..5ffe791 100644 --- a/source/core/StarCompression.hpp +++ b/source/core/StarCompression.hpp @@ -49,6 +49,8 @@ public: void sync() override; void close() override; + IODevicePtr clone() override; + private: String m_filename; void* m_file; diff --git a/source/core/StarFile.cpp b/source/core/StarFile.cpp index 9153960..75813b4 100644 --- a/source/core/StarFile.cpp +++ b/source/core/StarFile.cpp @@ -243,4 +243,15 @@ String File::deviceName() const { return m_filename; } +IODevicePtr File::clone() { + auto cloned = make_shared<File>(m_filename); + if (isOpen()) { + // Open with same mode + cloned->open(mode()); + // Seek to same position + cloned->seek(pos()); + } + return cloned; +} + } diff --git a/source/core/StarFile.hpp b/source/core/StarFile.hpp index fb489ab..36c8390 100644 --- a/source/core/StarFile.hpp +++ b/source/core/StarFile.hpp @@ -127,6 +127,8 @@ public: String deviceName() const override; + IODevicePtr clone() override; + private: static void* fopen(char const* filename, IOMode mode); static void fseek(void* file, StreamOffset offset, IOSeek seek); diff --git a/source/core/StarIODevice.hpp b/source/core/StarIODevice.hpp index b375e81..7bc2a04 100644 --- a/source/core/StarIODevice.hpp +++ b/source/core/StarIODevice.hpp @@ -70,6 +70,9 @@ public: // Default implementation is a no-op virtual void sync(); + // Returns a clone of this device with the same mode + virtual IODevicePtr clone() = 0; + // Default implementation just prints address of generic IODevice virtual String deviceName() const; diff --git a/source/core/StarIODeviceCallbacks.cpp b/source/core/StarIODeviceCallbacks.cpp new file mode 100644 index 0000000..ca8b692 --- /dev/null +++ b/source/core/StarIODeviceCallbacks.cpp @@ -0,0 +1,39 @@ +#include "StarIODeviceCallbacks.hpp" +#include "vorbis/vorbisfile.h" + +namespace Star { + +IODeviceCallbacks::IODeviceCallbacks(IODevicePtr device) + : m_device(std::move(device)) { + if (!m_device->isOpen()) + m_device->open(IOMode::Read); +} + +IODevicePtr const& IODeviceCallbacks::device() const { + return m_device; +} + +size_t IODeviceCallbacks::readFunc(void* ptr, size_t size, size_t nmemb, void* datasource) { + auto* callbacks = static_cast<IODeviceCallbacks*>(datasource); + return callbacks->m_device->read((char*)ptr, size * nmemb) / size; +} + +int IODeviceCallbacks::seekFunc(void* datasource, ogg_int64_t offset, int whence) { + auto* callbacks = static_cast<IODeviceCallbacks*>(datasource); + callbacks->m_device->seek(offset, (IOSeek)whence); + return 0; +} + +long int IODeviceCallbacks::tellFunc(void* datasource) { + auto* callbacks = static_cast<IODeviceCallbacks*>(datasource); + return (long int)callbacks->m_device->pos(); +} + +void IODeviceCallbacks::setupOggCallbacks(ov_callbacks& callbacks) { + callbacks.read_func = readFunc; + callbacks.seek_func = seekFunc; + callbacks.tell_func = tellFunc; + callbacks.close_func = nullptr; +} + +}
\ No newline at end of file diff --git a/source/core/StarIODeviceCallbacks.hpp b/source/core/StarIODeviceCallbacks.hpp new file mode 100644 index 0000000..8e2f9ed --- /dev/null +++ b/source/core/StarIODeviceCallbacks.hpp @@ -0,0 +1,37 @@ +#pragma once + +#include "StarIODevice.hpp" +#include "vorbis/codec.h" +#include "vorbis/vorbisfile.h" + +namespace Star { + +// Provides callbacks for interfacing IODevice with ogg vorbis callbacks +class IODeviceCallbacks { +public: + explicit IODeviceCallbacks(IODevicePtr device); + + // No copying + IODeviceCallbacks(IODeviceCallbacks const&) = delete; + IODeviceCallbacks& operator=(IODeviceCallbacks const&) = delete; + + // Moving is ok + IODeviceCallbacks(IODeviceCallbacks&&) = default; + IODeviceCallbacks& operator=(IODeviceCallbacks&&) = default; + + // Get the underlying device + IODevicePtr const& device() const; + + // Callback functions for Ogg Vorbis + static size_t readFunc(void* ptr, size_t size, size_t nmemb, void* datasource); + static int seekFunc(void* datasource, ogg_int64_t offset, int whence); + static long int tellFunc(void* datasource); + + // Sets up callbacks for Ogg Vorbis + void setupOggCallbacks(ov_callbacks& callbacks); + +private: + IODevicePtr m_device; +}; + +} |