You've already forked libopenshot
mirror of
https://github.com/OpenShot/libopenshot.git
synced 2026-03-02 08:53:52 -08:00
Big refactor of AudioReaderSource, to be simpler, and only fill the audio samples requested (removed double/internal buffer complexity). Also, we now initialize the audio device manager at a specific sampleRate and # of channels correctly. Removed 'latency' adjustment in PrivatePlayer (no longer needed with no read-ahead buffer). Increased the min_frames_required on the video cache to 24.
This commit is contained in:
@@ -13,217 +13,82 @@
|
||||
#include "AudioReaderSource.h"
|
||||
#include "Exceptions.h"
|
||||
#include "Frame.h"
|
||||
#include "ZmqLogger.h"
|
||||
|
||||
|
||||
using namespace std;
|
||||
using namespace openshot;
|
||||
|
||||
// Constructor that reads samples from a reader
|
||||
AudioReaderSource::AudioReaderSource(ReaderBase *audio_reader, int64_t starting_frame_number, int buffer_size)
|
||||
: reader(audio_reader), frame_number(starting_frame_number), videoCache(NULL),
|
||||
size(buffer_size), position(0), frame_position(0), estimated_frame(0), speed(1) {
|
||||
|
||||
// Initialize an audio buffer (based on reader)
|
||||
buffer = new juce::AudioBuffer<float>(reader->info.channels, size);
|
||||
|
||||
// initialize the audio samples to zero (silence)
|
||||
buffer->clear();
|
||||
AudioReaderSource::AudioReaderSource(ReaderBase *audio_reader, int64_t starting_frame_number)
|
||||
: reader(audio_reader), frame_position(starting_frame_number), videoCache(NULL), frame(NULL),
|
||||
sample_position(0), speed(1), stream_position(0) {
|
||||
}
|
||||
|
||||
// Destructor
|
||||
AudioReaderSource::~AudioReaderSource()
|
||||
{
|
||||
delete buffer;
|
||||
buffer = NULL;
|
||||
}
|
||||
|
||||
// Get more samples from the reader
|
||||
void AudioReaderSource::GetMoreSamplesFromReader()
|
||||
{
|
||||
// Determine the amount of samples needed to fill up this buffer
|
||||
int amount_needed = position; // replace these used samples
|
||||
int amount_remaining = size - amount_needed; // these are unused samples, and need to be carried forward
|
||||
if (!frame) {
|
||||
// If no frame, load entire buffer
|
||||
amount_needed = size;
|
||||
amount_remaining = 0;
|
||||
}
|
||||
|
||||
// Debug
|
||||
ZmqLogger::Instance()->AppendDebugMethod("AudioReaderSource::GetMoreSamplesFromReader", "amount_needed", amount_needed, "amount_remaining", amount_remaining);
|
||||
|
||||
// Init estimated buffer equal to the current frame position (before getting more samples)
|
||||
estimated_frame = frame_number;
|
||||
|
||||
// Init new buffer
|
||||
auto *new_buffer = new juce::AudioBuffer<float>(reader->info.channels, size);
|
||||
new_buffer->clear();
|
||||
|
||||
// Move the remaining samples into new buffer (if any)
|
||||
if (amount_remaining > 0) {
|
||||
for (int channel = 0; channel < buffer->getNumChannels(); channel++)
|
||||
new_buffer->addFrom(channel, 0, *buffer, channel, position, amount_remaining);
|
||||
|
||||
position = amount_remaining;
|
||||
} else
|
||||
// reset position to 0
|
||||
position = 0;
|
||||
|
||||
// Loop through frames until buffer filled
|
||||
while (amount_needed > 0 && speed == 1 && frame_number >= 1 && frame_number <= reader->info.video_length) {
|
||||
|
||||
// Get the next frame (if position is zero)
|
||||
if (frame_position == 0) {
|
||||
try {
|
||||
// Get frame object
|
||||
frame = reader->GetFrame(frame_number);
|
||||
frame_number = frame_number + speed;
|
||||
|
||||
} catch (const ReaderClosed & e) {
|
||||
break;
|
||||
} catch (const OutOfBoundsFrame & e) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
bool frame_completed = false;
|
||||
int amount_to_copy = 0;
|
||||
if (frame)
|
||||
amount_to_copy = frame->GetAudioSamplesCount() - frame_position;
|
||||
if (amount_to_copy > amount_needed) {
|
||||
// Don't copy too many samples (we don't want to overflow the buffer)
|
||||
amount_to_copy = amount_needed;
|
||||
amount_needed = 0;
|
||||
} else {
|
||||
// Not enough to fill the buffer (so use the entire frame)
|
||||
amount_needed -= amount_to_copy;
|
||||
frame_completed = true;
|
||||
}
|
||||
|
||||
// Load all of its samples into the buffer
|
||||
if (frame)
|
||||
for (int channel = 0; channel < new_buffer->getNumChannels(); channel++)
|
||||
new_buffer->addFrom(channel, position, *frame->GetAudioSampleBuffer(), channel, frame_position, amount_to_copy);
|
||||
|
||||
// Adjust remaining samples
|
||||
position += amount_to_copy;
|
||||
if (frame_completed)
|
||||
// Reset frame buffer position (which will load a new frame on the next loop)
|
||||
frame_position = 0;
|
||||
else
|
||||
// Continue tracking the current frame's position
|
||||
frame_position += amount_to_copy;
|
||||
}
|
||||
|
||||
// Delete old buffer
|
||||
buffer->clear();
|
||||
delete buffer;
|
||||
|
||||
// Replace buffer and reset position
|
||||
buffer = new_buffer;
|
||||
position = 0;
|
||||
}
|
||||
|
||||
// Reverse an audio buffer
|
||||
juce::AudioBuffer<float>* AudioReaderSource::reverse_buffer(juce::AudioBuffer<float>* buffer)
|
||||
{
|
||||
int number_of_samples = buffer->getNumSamples();
|
||||
int channels = buffer->getNumChannels();
|
||||
|
||||
// Debug
|
||||
ZmqLogger::Instance()->AppendDebugMethod("AudioReaderSource::reverse_buffer", "number_of_samples", number_of_samples, "channels", channels);
|
||||
|
||||
// Reverse array (create new buffer to hold the reversed version)
|
||||
auto *reversed = new juce::AudioBuffer<float>(channels, number_of_samples);
|
||||
reversed->clear();
|
||||
|
||||
for (int channel = 0; channel < channels; channel++)
|
||||
{
|
||||
int n=0;
|
||||
for (int s = number_of_samples - 1; s >= 0; s--, n++)
|
||||
reversed->getWritePointer(channel)[n] = buffer->getWritePointer(channel)[s];
|
||||
}
|
||||
|
||||
// Copy the samples back to the original array
|
||||
buffer->clear();
|
||||
// Loop through channels, and get audio samples
|
||||
for (int channel = 0; channel < channels; channel++)
|
||||
// Get the audio samples for this channel
|
||||
buffer->addFrom(channel, 0, reversed->getReadPointer(channel), number_of_samples, 1.0f);
|
||||
|
||||
delete reversed;
|
||||
reversed = NULL;
|
||||
|
||||
// return pointer or passed in object (so this method can be chained together)
|
||||
return buffer;
|
||||
}
|
||||
|
||||
// Get the next block of audio samples
|
||||
void AudioReaderSource::getNextAudioBlock(const juce::AudioSourceChannelInfo& info)
|
||||
{
|
||||
int buffer_samples = buffer->getNumSamples();
|
||||
int buffer_channels = buffer->getNumChannels();
|
||||
|
||||
if (info.numSamples > 0) {
|
||||
int number_to_copy = 0;
|
||||
int remaining_samples = info.numSamples;
|
||||
int remaining_position = info.startSample;
|
||||
|
||||
// Do we need more samples?
|
||||
if (speed == 1 && videoCache->isReady()) {
|
||||
// Only refill buffers if speed is normal
|
||||
if ((reader && reader->IsOpen() && !frame) or
|
||||
(reader && reader->IsOpen() && buffer_samples - position < info.numSamples))
|
||||
// Refill buffer from reader
|
||||
GetMoreSamplesFromReader();
|
||||
} else {
|
||||
// Fill buffer with silence and clear current frame
|
||||
// Pause and fill buffer with silence (wait for pre-roll)
|
||||
if (speed != 1 || !videoCache->isReady()) {
|
||||
info.buffer->clear();
|
||||
|
||||
// Empty internal buffer also
|
||||
buffer->clear();
|
||||
position = 0;
|
||||
return;
|
||||
}
|
||||
|
||||
// Determine how many samples to copy
|
||||
if (position + info.numSamples <= buffer_samples)
|
||||
{
|
||||
// copy the full amount requested
|
||||
number_to_copy = info.numSamples;
|
||||
}
|
||||
else if (position > buffer_samples)
|
||||
{
|
||||
// copy nothing
|
||||
number_to_copy = 0;
|
||||
}
|
||||
else if (buffer_samples - position > 0)
|
||||
{
|
||||
// only copy what is left in the buffer
|
||||
number_to_copy = buffer_samples - position;
|
||||
}
|
||||
else
|
||||
{
|
||||
// copy nothing
|
||||
number_to_copy = 0;
|
||||
}
|
||||
while (remaining_samples > 0) {
|
||||
|
||||
try {
|
||||
// Get current frame object
|
||||
if (reader) {
|
||||
frame = reader->GetFrame(frame_position);
|
||||
}
|
||||
}
|
||||
catch (const ReaderClosed & e) { }
|
||||
catch (const OutOfBoundsFrame & e) { }
|
||||
|
||||
// Determine if any samples need to be copied
|
||||
if (number_to_copy > 0)
|
||||
{
|
||||
// Debug
|
||||
ZmqLogger::Instance()->AppendDebugMethod("AudioReaderSource::getNextAudioBlock", "number_to_copy", number_to_copy, "buffer_samples", buffer_samples, "buffer_channels", buffer_channels, "info.numSamples", info.numSamples, "speed", speed, "position", position);
|
||||
// Get audio samples
|
||||
if (reader && frame) {
|
||||
if (sample_position + remaining_samples <= frame->GetAudioSamplesCount()) {
|
||||
// Success, we have enough samples
|
||||
for (int channel = 0; channel < frame->GetAudioChannelsCount(); channel++) {
|
||||
if (channel < info.buffer->getNumChannels()) {
|
||||
info.buffer->addFrom(channel, remaining_position, *frame->GetAudioSampleBuffer(),
|
||||
channel, sample_position, remaining_samples);
|
||||
}
|
||||
}
|
||||
sample_position += remaining_samples;
|
||||
remaining_position += remaining_samples;
|
||||
remaining_samples = 0;
|
||||
|
||||
// Loop through each channel and copy some samples
|
||||
for (int channel = 0; channel < buffer_channels; channel++)
|
||||
info.buffer->copyFrom(channel, info.startSample, *buffer, channel, position, number_to_copy);
|
||||
} else if (sample_position + remaining_samples > frame->GetAudioSamplesCount()) {
|
||||
// Not enough samples, take what we can
|
||||
int amount_to_copy = frame->GetAudioSamplesCount() - sample_position;
|
||||
for (int channel = 0; channel < frame->GetAudioChannelsCount(); channel++) {
|
||||
if (channel < info.buffer->getNumChannels()) {
|
||||
info.buffer->addFrom(channel, remaining_position, *frame->GetAudioSampleBuffer(), channel,
|
||||
sample_position, amount_to_copy);
|
||||
}
|
||||
}
|
||||
sample_position += amount_to_copy;
|
||||
remaining_position += amount_to_copy;
|
||||
remaining_samples -= amount_to_copy;
|
||||
}
|
||||
|
||||
// Update the position of this audio source
|
||||
position += number_to_copy;
|
||||
// Increment frame position (if samples are all used up)
|
||||
if (sample_position == frame->GetAudioSamplesCount()) {
|
||||
frame_position += speed;
|
||||
sample_position = 0; // reset for new frame
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
// Adjust estimate frame number (the estimated frame number that is being played)
|
||||
estimated_samples_per_frame = Frame::GetSamplesPerFrame(estimated_frame, reader->info.fps, reader->info.sample_rate, buffer_channels);
|
||||
estimated_frame += double(info.numSamples) / double(estimated_samples_per_frame);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -233,21 +98,6 @@ void AudioReaderSource::prepareToPlay(int, double) {}
|
||||
// Release all resources
|
||||
void AudioReaderSource::releaseResources() { }
|
||||
|
||||
// Set the next read position of this source
|
||||
void AudioReaderSource::setNextReadPosition (juce::int64 newPosition)
|
||||
{
|
||||
// set position (if the new position is in range)
|
||||
if (newPosition >= 0 && newPosition < buffer->getNumSamples())
|
||||
position = newPosition;
|
||||
}
|
||||
|
||||
// Get the next read position of this source
|
||||
juce::int64 AudioReaderSource::getNextReadPosition() const
|
||||
{
|
||||
// return the next read position
|
||||
return position;
|
||||
}
|
||||
|
||||
// Get the total length (in samples) of this audio source
|
||||
juce::int64 AudioReaderSource::getTotalLength() const
|
||||
{
|
||||
@@ -257,24 +107,3 @@ juce::int64 AudioReaderSource::getTotalLength() const
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
// Determines if this audio source should repeat when it reaches the end
|
||||
bool AudioReaderSource::isLooping() const
|
||||
{
|
||||
// return if this source is looping
|
||||
return repeat;
|
||||
}
|
||||
|
||||
// Set if this audio source should repeat when it reaches the end
|
||||
void AudioReaderSource::setLooping (bool shouldLoop)
|
||||
{
|
||||
// Set the repeat flag
|
||||
repeat = shouldLoop;
|
||||
}
|
||||
|
||||
// Update the internal buffer used by this source
|
||||
void AudioReaderSource::setBuffer (juce::AudioBuffer<float> *audio_buffer)
|
||||
{
|
||||
buffer = audio_buffer;
|
||||
setNextReadPosition(0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user