OpenShot Library | libopenshot 0.2.7
AudioReaderSource.cpp
Go to the documentation of this file.
1/**
2 * @file
3 * @brief Source file for AudioReaderSource class
4 * @author Jonathan Thomas <jonathan@openshot.org>
5 *
6 * @ref License
7 */
8
9/* LICENSE
10 *
11 * Copyright (c) 2008-2019 OpenShot Studios, LLC
12 * <http://www.openshotstudios.com/>. This file is part of
13 * OpenShot Library (libopenshot), an open-source project dedicated to
14 * delivering high quality video editing and animation solutions to the
15 * world. For more information visit <http://www.openshot.org/>.
16 *
17 * OpenShot Library (libopenshot) is free software: you can redistribute it
18 * and/or modify it under the terms of the GNU Lesser General Public License
19 * as published by the Free Software Foundation, either version 3 of the
20 * License, or (at your option) any later version.
21 *
22 * OpenShot Library (libopenshot) is distributed in the hope that it will be
23 * useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
24 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
25 * GNU Lesser General Public License for more details.
26 *
27 * You should have received a copy of the GNU Lesser General Public License
28 * along with OpenShot Library. If not, see <http://www.gnu.org/licenses/>.
29 */
30
31#include "AudioReaderSource.h"
32#include "Exceptions.h"
33
34using namespace std;
35using namespace openshot;
36
37// Constructor that reads samples from a reader
38AudioReaderSource::AudioReaderSource(ReaderBase *audio_reader, int64_t starting_frame_number, int buffer_size)
39 : reader(audio_reader), frame_number(starting_frame_number),
40 size(buffer_size), position(0), frame_position(0), estimated_frame(0), speed(1) {
41
42 // Initialize an audio buffer (based on reader)
43 buffer = new juce::AudioSampleBuffer(reader->info.channels, size);
44
45 // initialize the audio samples to zero (silence)
46 buffer->clear();
47}
48
49// Destructor
51{
52 // Clear and delete the buffer
53 delete buffer;
54 buffer = NULL;
55}
56
57// Get more samples from the reader
58void AudioReaderSource::GetMoreSamplesFromReader()
59{
60 // Determine the amount of samples needed to fill up this buffer
61 int amount_needed = position; // replace these used samples
62 int amount_remaining = size - amount_needed; // these are unused samples, and need to be carried forward
63 if (!frame) {
64 // If no frame, load entire buffer
65 amount_needed = size;
66 amount_remaining = 0;
67 }
68
69 // Debug
70 ZmqLogger::Instance()->AppendDebugMethod("AudioReaderSource::GetMoreSamplesFromReader", "amount_needed", amount_needed, "amount_remaining", amount_remaining);
71
72 // Init estimated buffer equal to the current frame position (before getting more samples)
73 estimated_frame = frame_number;
74
75 // Init new buffer
76 juce::AudioSampleBuffer *new_buffer = new juce::AudioSampleBuffer(reader->info.channels, size);
77 new_buffer->clear();
78
79 // Move the remaining samples into new buffer (if any)
80 if (amount_remaining > 0) {
81 for (int channel = 0; channel < buffer->getNumChannels(); channel++)
82 new_buffer->addFrom(channel, 0, *buffer, channel, position, amount_remaining);
83
84 position = amount_remaining;
85 } else
86 // reset position to 0
87 position = 0;
88
89 // Loop through frames until buffer filled
90 while (amount_needed > 0 && speed == 1 && frame_number >= 1 && frame_number <= reader->info.video_length) {
91
92 // Get the next frame (if position is zero)
93 if (frame_position == 0) {
94 try {
95 // Get frame object
96 frame = reader->GetFrame(frame_number);
97 frame_number = frame_number + speed;
98
99 } catch (const ReaderClosed & e) {
100 break;
101 } catch (const OutOfBoundsFrame & e) {
102 break;
103 }
104 }
105
106 bool frame_completed = false;
107 int amount_to_copy = 0;
108 if (frame)
109 amount_to_copy = frame->GetAudioSamplesCount() - frame_position;
110 if (amount_to_copy > amount_needed) {
111 // Don't copy too many samples (we don't want to overflow the buffer)
112 amount_to_copy = amount_needed;
113 amount_needed = 0;
114 } else {
115 // Not enough to fill the buffer (so use the entire frame)
116 amount_needed -= amount_to_copy;
117 frame_completed = true;
118 }
119
120 // Load all of its samples into the buffer
121 if (frame)
122 for (int channel = 0; channel < new_buffer->getNumChannels(); channel++)
123 new_buffer->addFrom(channel, position, *frame->GetAudioSampleBuffer(), channel, frame_position, amount_to_copy);
124
125 // Adjust remaining samples
126 position += amount_to_copy;
127 if (frame_completed)
128 // Reset frame buffer position (which will load a new frame on the next loop)
129 frame_position = 0;
130 else
131 // Continue tracking the current frame's position
132 frame_position += amount_to_copy;
133 }
134
135 // Delete old buffer
136 buffer->clear();
137 delete buffer;
138
139 // Replace buffer and reset position
140 buffer = new_buffer;
141 position = 0;
142}
143
144// Reverse an audio buffer
145juce::AudioSampleBuffer* AudioReaderSource::reverse_buffer(juce::AudioSampleBuffer* buffer)
146{
147 int number_of_samples = buffer->getNumSamples();
148 int channels = buffer->getNumChannels();
149
150 // Debug
151 ZmqLogger::Instance()->AppendDebugMethod("AudioReaderSource::reverse_buffer", "number_of_samples", number_of_samples, "channels", channels);
152
153 // Reverse array (create new buffer to hold the reversed version)
154 juce::AudioSampleBuffer *reversed = new juce::AudioSampleBuffer(channels, number_of_samples);
155 reversed->clear();
156
157 for (int channel = 0; channel < channels; channel++)
158 {
159 int n=0;
160 for (int s = number_of_samples - 1; s >= 0; s--, n++)
161 reversed->getWritePointer(channel)[n] = buffer->getWritePointer(channel)[s];
162 }
163
164 // Copy the samples back to the original array
165 buffer->clear();
166 // Loop through channels, and get audio samples
167 for (int channel = 0; channel < channels; channel++)
168 // Get the audio samples for this channel
169 buffer->addFrom(channel, 0, reversed->getReadPointer(channel), number_of_samples, 1.0f);
170
171 delete reversed;
172 reversed = NULL;
173
174 // return pointer or passed in object (so this method can be chained together)
175 return buffer;
176}
177
178// Get the next block of audio samples
179void AudioReaderSource::getNextAudioBlock(const juce::AudioSourceChannelInfo& info)
180{
181 int buffer_samples = buffer->getNumSamples();
182 int buffer_channels = buffer->getNumChannels();
183
184 if (info.numSamples > 0) {
185 int number_to_copy = 0;
186
187 // Do we need more samples?
188 if (speed == 1) {
189 // Only refill buffers if speed is normal
190 if ((reader && reader->IsOpen() && !frame) or
191 (reader && reader->IsOpen() && buffer_samples - position < info.numSamples))
192 // Refill buffer from reader
193 GetMoreSamplesFromReader();
194 } else {
195 // Fill buffer with silence and clear current frame
196 info.buffer->clear();
197 return;
198 }
199
200 // Determine how many samples to copy
201 if (position + info.numSamples <= buffer_samples)
202 {
203 // copy the full amount requested
204 number_to_copy = info.numSamples;
205 }
206 else if (position > buffer_samples)
207 {
208 // copy nothing
209 number_to_copy = 0;
210 }
211 else if (buffer_samples - position > 0)
212 {
213 // only copy what is left in the buffer
214 number_to_copy = buffer_samples - position;
215 }
216 else
217 {
218 // copy nothing
219 number_to_copy = 0;
220 }
221
222
223 // Determine if any samples need to be copied
224 if (number_to_copy > 0)
225 {
226 // Debug
227 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);
228
229 // Loop through each channel and copy some samples
230 for (int channel = 0; channel < buffer_channels; channel++)
231 info.buffer->copyFrom(channel, info.startSample, *buffer, channel, position, number_to_copy);
232
233 // Update the position of this audio source
234 position += number_to_copy;
235 }
236
237 // Adjust estimate frame number (the estimated frame number that is being played)
238 estimated_samples_per_frame = Frame::GetSamplesPerFrame(estimated_frame, reader->info.fps, reader->info.sample_rate, buffer_channels);
239 estimated_frame += double(info.numSamples) / double(estimated_samples_per_frame);
240 }
241}
242
243// Prepare to play this audio source
245
246// Release all resources
248
249// Set the next read position of this source
251{
252 // set position (if the new position is in range)
253 if (newPosition >= 0 && newPosition < buffer->getNumSamples())
254 position = newPosition;
255}
256
257// Get the next read position of this source
259{
260 // return the next read position
261 return position;
262}
263
264// Get the total length (in samples) of this audio source
266{
267 // Get the length
268 if (reader)
269 return reader->info.sample_rate * reader->info.duration;
270 else
271 return 0;
272}
273
274// Determines if this audio source should repeat when it reaches the end
276{
277 // return if this source is looping
278 return repeat;
279}
280
281// Set if this audio source should repeat when it reaches the end
282void AudioReaderSource::setLooping (bool shouldLoop)
283{
284 // Set the repeat flag
285 repeat = shouldLoop;
286}
287
288// Update the internal buffer used by this source
289void AudioReaderSource::setBuffer (juce::AudioSampleBuffer *audio_buffer)
290{
291 buffer = audio_buffer;
293}
Header file for AudioReaderSource class.
#define int64
Definition: Clip.h:35
Header file for all Exception classes.
juce::int64 getNextReadPosition() const
Get the next read position of this source.
void setNextReadPosition(juce::int64 newPosition)
Set the next read position of this source.
void releaseResources()
Release all resources.
bool isLooping() const
Determines if this audio source should repeat when it reaches the end.
juce::int64 getTotalLength() const
Get the total length (in samples) of this audio source.
void setLooping(bool shouldLoop)
Set if this audio source should repeat when it reaches the end.
void prepareToPlay(int, double)
Prepare to play this audio source.
void setBuffer(juce::AudioSampleBuffer *audio_buffer)
Update the internal buffer used by this source.
void getNextAudioBlock(const juce::AudioSourceChannelInfo &info)
Get the next block of audio samples.
int GetSamplesPerFrame(openshot::Fraction fps, int sample_rate, int channels)
Calculate the # of samples per video frame (for the current frame number)
Definition: Frame.cpp:536
Exception for frames that are out of bounds.
Definition: Exceptions.h:286
This abstract class is the base class, used by all readers in libopenshot.
Definition: ReaderBase.h:98
virtual bool IsOpen()=0
Determine if reader is open or closed.
openshot::ReaderInfo info
Information about the current media file.
Definition: ReaderBase.h:111
virtual std::shared_ptr< openshot::Frame > GetFrame(int64_t number)=0
Exception when a reader is closed, and a frame is requested.
Definition: Exceptions.h:338
void AppendDebugMethod(std::string method_name, std::string arg1_name="", float arg1_value=-1.0, std::string arg2_name="", float arg2_value=-1.0, std::string arg3_name="", float arg3_value=-1.0, std::string arg4_name="", float arg4_value=-1.0, std::string arg5_name="", float arg5_value=-1.0, std::string arg6_name="", float arg6_value=-1.0)
Append debug information.
Definition: ZmqLogger.cpp:190
static ZmqLogger * Instance()
Create or get an instance of this logger singleton (invoke the class with this method)
Definition: ZmqLogger.cpp:52
This namespace is the default namespace for all code in the openshot library.
Definition: Compressor.h:47
float duration
Length of time (in seconds)
Definition: ReaderBase.h:65
int channels
The number of audio channels used in the audio stream.
Definition: ReaderBase.h:83
openshot::Fraction fps
Frames per second, as a fraction (i.e. 24/1 = 24 fps)
Definition: ReaderBase.h:70
int sample_rate
The number of audio samples per second (44100 is a common sample rate)
Definition: ReaderBase.h:82