2014-01-28 17:17:38 -06:00
|
|
|
/**
|
|
|
|
|
* @file
|
|
|
|
|
* @brief Source file for AudioReaderSource class
|
|
|
|
|
* @author Jonathan Thomas <jonathan@openshot.org>
|
|
|
|
|
*
|
2019-06-09 08:31:04 -04:00
|
|
|
* @ref License
|
|
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
/* LICENSE
|
2014-01-28 17:17:38 -06:00
|
|
|
*
|
2019-06-11 06:48:32 -04:00
|
|
|
* Copyright (c) 2008-2019 OpenShot Studios, LLC
|
2014-03-29 18:49:22 -05:00
|
|
|
* <http://www.openshotstudios.com/>. This file is part of
|
|
|
|
|
* OpenShot Library (libopenshot), an open-source project dedicated to
|
|
|
|
|
* delivering high quality video editing and animation solutions to the
|
|
|
|
|
* world. For more information visit <http://www.openshot.org/>.
|
2014-01-28 17:17:38 -06:00
|
|
|
*
|
2014-03-29 18:49:22 -05:00
|
|
|
* OpenShot Library (libopenshot) is free software: you can redistribute it
|
2014-07-11 16:52:14 -05:00
|
|
|
* and/or modify it under the terms of the GNU Lesser General Public License
|
2014-03-29 18:49:22 -05:00
|
|
|
* as published by the Free Software Foundation, either version 3 of the
|
|
|
|
|
* License, or (at your option) any later version.
|
2014-01-28 17:17:38 -06:00
|
|
|
*
|
2014-03-29 18:49:22 -05:00
|
|
|
* OpenShot Library (libopenshot) is distributed in the hope that it will be
|
|
|
|
|
* useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
2014-07-11 16:52:14 -05:00
|
|
|
* GNU Lesser General Public License for more details.
|
2014-01-28 17:17:38 -06:00
|
|
|
*
|
2014-07-11 16:52:14 -05:00
|
|
|
* You should have received a copy of the GNU Lesser General Public License
|
2014-03-29 18:49:22 -05:00
|
|
|
* along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
|
2014-01-28 17:17:38 -06:00
|
|
|
*/
|
|
|
|
|
|
|
|
|
|
#include "../include/AudioReaderSource.h"
|
|
|
|
|
|
|
|
|
|
using namespace std;
|
|
|
|
|
using namespace openshot;
|
|
|
|
|
|
|
|
|
|
// Constructor that reads samples from a reader
|
2017-09-28 16:03:01 -05:00
|
|
|
AudioReaderSource::AudioReaderSource(ReaderBase *audio_reader, int64_t starting_frame_number, int buffer_size)
|
2014-01-29 00:18:40 -06:00
|
|
|
: reader(audio_reader), frame_number(starting_frame_number), original_frame_number(starting_frame_number),
|
2014-03-23 01:12:29 -05:00
|
|
|
size(buffer_size), position(0), frame_position(0), estimated_frame(0), speed(1) {
|
2014-01-28 17:17:38 -06:00
|
|
|
|
|
|
|
|
// Initialize an audio buffer (based on reader)
|
|
|
|
|
buffer = new juce::AudioSampleBuffer(reader->info.channels, size);
|
|
|
|
|
|
|
|
|
|
// initialize the audio samples to zero (silence)
|
|
|
|
|
buffer->clear();
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Destructor
|
|
|
|
|
AudioReaderSource::~AudioReaderSource()
|
|
|
|
|
{
|
|
|
|
|
// Clear and delete the buffer
|
|
|
|
|
delete buffer;
|
|
|
|
|
buffer = NULL;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
// Get more samples from the reader
|
2015-06-01 00:20:14 -07:00
|
|
|
void AudioReaderSource::GetMoreSamplesFromReader()
|
|
|
|
|
{
|
2014-01-28 17:17:38 -06:00
|
|
|
// Determine the amount of samples needed to fill up this buffer
|
2014-01-29 00:18:40 -06:00
|
|
|
int amount_needed = position; // replace these used samples
|
|
|
|
|
int amount_remaining = size - amount_needed; // these are unused samples, and need to be carried forward
|
2014-01-28 17:17:38 -06:00
|
|
|
if (!frame) {
|
|
|
|
|
// If no frame, load entire buffer
|
|
|
|
|
amount_needed = size;
|
2014-01-29 00:18:40 -06:00
|
|
|
amount_remaining = 0;
|
2014-01-28 17:17:38 -06:00
|
|
|
}
|
|
|
|
|
|
2016-04-21 01:39:17 -05:00
|
|
|
// Debug
|
|
|
|
|
ZmqLogger::Instance()->AppendDebugMethod("AudioReaderSource::GetMoreSamplesFromReader", "amount_needed", amount_needed, "amount_remaining", amount_remaining, "", -1, "", -1, "", -1, "", -1);
|
|
|
|
|
|
2014-02-13 03:16:18 -06:00
|
|
|
// Init estimated buffer equal to the current frame position (before getting more samples)
|
|
|
|
|
estimated_frame = frame_number;
|
|
|
|
|
|
2014-01-28 17:17:38 -06:00
|
|
|
// Init new buffer
|
|
|
|
|
juce::AudioSampleBuffer *new_buffer = new juce::AudioSampleBuffer(reader->info.channels, size);
|
2014-01-29 00:18:40 -06:00
|
|
|
new_buffer->clear();
|
2014-01-28 17:17:38 -06:00
|
|
|
|
|
|
|
|
// Move the remaining samples into new buffer (if any)
|
2014-01-29 00:18:40 -06:00
|
|
|
if (amount_remaining > 0) {
|
2014-01-28 17:17:38 -06:00
|
|
|
for (int channel = 0; channel < buffer->getNumChannels(); channel++)
|
2014-01-31 23:07:36 +08:00
|
|
|
new_buffer->addFrom(channel, 0, *buffer, channel, position, amount_remaining);
|
2015-02-04 23:56:43 -06:00
|
|
|
|
2014-01-29 00:18:40 -06:00
|
|
|
position = amount_remaining;
|
|
|
|
|
} else
|
|
|
|
|
// reset position to 0
|
|
|
|
|
position = 0;
|
2014-01-28 17:17:38 -06:00
|
|
|
|
|
|
|
|
// Loop through frames until buffer filled
|
2015-02-04 23:56:43 -06:00
|
|
|
while (amount_needed > 0 && speed == 1 && frame_number >= 1 && frame_number <= reader->info.video_length) {
|
2014-01-28 17:17:38 -06:00
|
|
|
|
2014-01-29 00:18:40 -06:00
|
|
|
// Get the next frame (if position is zero)
|
|
|
|
|
if (frame_position == 0) {
|
|
|
|
|
try {
|
|
|
|
|
// Get frame object
|
2015-06-01 00:20:14 -07:00
|
|
|
frame = reader->GetFrame(frame_number);
|
2014-03-23 01:12:29 -05:00
|
|
|
frame_number = frame_number + speed;
|
2014-01-28 17:17:38 -06:00
|
|
|
|
2014-01-29 00:18:40 -06:00
|
|
|
} catch (const ReaderClosed & e) {
|
|
|
|
|
break;
|
|
|
|
|
} catch (const TooManySeeks & e) {
|
|
|
|
|
break;
|
|
|
|
|
} catch (const OutOfBoundsFrame & e) {
|
|
|
|
|
break;
|
|
|
|
|
}
|
2014-01-28 17:17:38 -06:00
|
|
|
}
|
|
|
|
|
|
2014-01-29 00:18:40 -06:00
|
|
|
bool frame_completed = false;
|
2015-10-01 14:21:21 -05:00
|
|
|
int amount_to_copy = 0;
|
|
|
|
|
if (frame)
|
|
|
|
|
amount_to_copy = frame->GetAudioSamplesCount() - frame_position;
|
2014-01-29 00:18:40 -06:00
|
|
|
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;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-31 11:27:29 -06:00
|
|
|
// Load all of its samples into the buffer
|
2015-10-01 14:21:21 -05:00
|
|
|
if (frame)
|
|
|
|
|
for (int channel = 0; channel < new_buffer->getNumChannels(); channel++)
|
|
|
|
|
new_buffer->addFrom(channel, position, *frame->GetAudioSampleBuffer(), channel, frame_position, amount_to_copy);
|
2014-01-28 17:17:38 -06:00
|
|
|
|
|
|
|
|
// Adjust remaining samples
|
2014-01-29 00:18:40 -06:00
|
|
|
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;
|
2014-01-28 17:17:38 -06:00
|
|
|
}
|
2014-01-29 00:18:40 -06:00
|
|
|
|
|
|
|
|
// Delete old buffer
|
|
|
|
|
buffer->clear();
|
|
|
|
|
delete buffer;
|
|
|
|
|
|
|
|
|
|
// Replace buffer and reset position
|
|
|
|
|
buffer = new_buffer;
|
|
|
|
|
position = 0;
|
2014-01-28 17:17:38 -06:00
|
|
|
}
|
|
|
|
|
|
2014-03-23 01:12:29 -05:00
|
|
|
// Reverse an audio buffer
|
|
|
|
|
juce::AudioSampleBuffer* AudioReaderSource::reverse_buffer(juce::AudioSampleBuffer* buffer)
|
|
|
|
|
{
|
|
|
|
|
int number_of_samples = buffer->getNumSamples();
|
|
|
|
|
int channels = buffer->getNumChannels();
|
|
|
|
|
|
2016-04-21 01:39:17 -05:00
|
|
|
// Debug
|
|
|
|
|
ZmqLogger::Instance()->AppendDebugMethod("AudioReaderSource::reverse_buffer", "number_of_samples", number_of_samples, "channels", channels, "", -1, "", -1, "", -1, "", -1);
|
|
|
|
|
|
2014-03-23 01:12:29 -05:00
|
|
|
// Reverse array (create new buffer to hold the reversed version)
|
|
|
|
|
AudioSampleBuffer *reversed = new juce::AudioSampleBuffer(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++)
|
2015-02-04 23:56:43 -06:00
|
|
|
reversed->getWritePointer(channel)[n] = buffer->getWritePointer(channel)[s];
|
2014-03-23 01:12:29 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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
|
2015-02-04 23:56:43 -06:00
|
|
|
buffer->addFrom(channel, 0, reversed->getReadPointer(channel), number_of_samples, 1.0f);
|
2014-03-23 01:12:29 -05:00
|
|
|
|
|
|
|
|
delete reversed;
|
|
|
|
|
reversed = NULL;
|
|
|
|
|
|
|
|
|
|
// return pointer or passed in object (so this method can be chained together)
|
|
|
|
|
return buffer;
|
|
|
|
|
}
|
|
|
|
|
|
2014-01-28 17:17:38 -06:00
|
|
|
// Get the next block of audio samples
|
2016-04-21 01:39:17 -05:00
|
|
|
void AudioReaderSource::getNextAudioBlock(const AudioSourceChannelInfo& info)
|
2014-01-28 17:17:38 -06:00
|
|
|
{
|
|
|
|
|
int buffer_samples = buffer->getNumSamples();
|
|
|
|
|
int buffer_channels = buffer->getNumChannels();
|
|
|
|
|
|
|
|
|
|
if (info.numSamples > 0) {
|
|
|
|
|
int number_to_copy = 0;
|
|
|
|
|
|
|
|
|
|
// Do we need more samples?
|
2015-12-24 16:44:45 -06:00
|
|
|
if (speed == 1) {
|
|
|
|
|
// 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
|
|
|
|
|
info.buffer->clear();
|
|
|
|
|
return;
|
|
|
|
|
}
|
2014-01-28 17:17:38 -06:00
|
|
|
|
|
|
|
|
// Determine how many samples to copy
|
2014-02-10 17:16:33 -06:00
|
|
|
if (position + info.numSamples <= buffer_samples)
|
2014-01-28 17:17:38 -06:00
|
|
|
{
|
|
|
|
|
// copy the full amount requested
|
|
|
|
|
number_to_copy = info.numSamples;
|
|
|
|
|
}
|
2014-02-10 17:16:33 -06:00
|
|
|
else if (position > buffer_samples)
|
2014-01-28 17:17:38 -06:00
|
|
|
{
|
|
|
|
|
// copy nothing
|
|
|
|
|
number_to_copy = 0;
|
|
|
|
|
}
|
2014-02-10 17:16:33 -06:00
|
|
|
else if (buffer_samples - position > 0)
|
2014-01-28 17:17:38 -06:00
|
|
|
{
|
|
|
|
|
// only copy what is left in the buffer
|
2014-02-10 17:16:33 -06:00
|
|
|
number_to_copy = buffer_samples - position;
|
2014-01-28 17:17:38 -06:00
|
|
|
}
|
|
|
|
|
else
|
|
|
|
|
{
|
|
|
|
|
// copy nothing
|
|
|
|
|
number_to_copy = 0;
|
|
|
|
|
}
|
|
|
|
|
|
2015-02-04 23:56:43 -06:00
|
|
|
|
2014-01-28 17:17:38 -06:00
|
|
|
// Determine if any samples need to be copied
|
|
|
|
|
if (number_to_copy > 0)
|
|
|
|
|
{
|
2016-04-21 01:39:17 -05:00
|
|
|
// 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);
|
|
|
|
|
|
2014-01-28 17:17:38 -06:00
|
|
|
// Loop through each channel and copy some samples
|
|
|
|
|
for (int channel = 0; channel < buffer_channels; channel++)
|
2014-02-10 17:16:33 -06:00
|
|
|
info.buffer->copyFrom(channel, info.startSample, *buffer, channel, position, number_to_copy);
|
2014-01-28 17:17:38 -06:00
|
|
|
|
|
|
|
|
// Update the position of this audio source
|
|
|
|
|
position += number_to_copy;
|
|
|
|
|
}
|
|
|
|
|
|
2014-02-13 03:16:18 -06:00
|
|
|
// Adjust estimate frame number (the estimated frame number that is being played)
|
2015-03-08 21:42:53 -05:00
|
|
|
estimated_samples_per_frame = Frame::GetSamplesPerFrame(estimated_frame, reader->info.fps, reader->info.sample_rate, buffer_channels);
|
2015-12-24 16:44:45 -06:00
|
|
|
estimated_frame += double(info.numSamples) / double(estimated_samples_per_frame);
|
2014-01-28 17:17:38 -06:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Prepare to play this audio source
|
|
|
|
|
void AudioReaderSource::prepareToPlay(int, double) { }
|
|
|
|
|
|
|
|
|
|
// Release all resources
|
|
|
|
|
void AudioReaderSource::releaseResources() { }
|
|
|
|
|
|
|
|
|
|
// Set the next read position of this source
|
2017-09-28 16:03:01 -05:00
|
|
|
void AudioReaderSource::setNextReadPosition (int64 newPosition)
|
2014-01-28 17:17:38 -06:00
|
|
|
{
|
|
|
|
|
// 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
|
2017-09-28 16:03:01 -05:00
|
|
|
int64 AudioReaderSource::getNextReadPosition() const
|
2014-01-28 17:17:38 -06:00
|
|
|
{
|
|
|
|
|
// return the next read position
|
|
|
|
|
return position;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Get the total length (in samples) of this audio source
|
2017-09-28 16:03:01 -05:00
|
|
|
int64 AudioReaderSource::getTotalLength() const
|
2014-01-28 17:17:38 -06:00
|
|
|
{
|
|
|
|
|
// Get the length
|
2014-02-10 17:16:33 -06:00
|
|
|
if (reader)
|
|
|
|
|
return reader->info.sample_rate * reader->info.duration;
|
|
|
|
|
else
|
|
|
|
|
return 0;
|
2014-01-28 17:17:38 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 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 (AudioSampleBuffer *audio_buffer)
|
|
|
|
|
{
|
|
|
|
|
buffer = audio_buffer;
|
|
|
|
|
setNextReadPosition(0);
|
|
|
|
|
}
|