Веб-сайт самохостера Lotigara

summaryrefslogtreecommitdiff
path: root/source/core/StarAudio.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'source/core/StarAudio.cpp')
-rw-r--r--source/core/StarAudio.cpp191
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) {