OpenShot Library | libopenshot 1.1.1
AudioPlaybackThread.cpp
Go to the documentation of this file.
1
10// Copyright (c) 2008-2019 OpenShot Studios, LLC
11//
12// SPDX-License-Identifier: LGPL-3.0-or-later
13
14#include "AudioPlaybackThread.h"
15#include "Settings.h"
16
17#include "../ReaderBase.h"
18#include "../RendererBase.h"
19#include "../AudioReaderSource.h"
20#include "../AudioDevices.h"
21#include "../Settings.h"
22#include "../Logger.h"
23
24#include <mutex>
25#include <thread> // for std::this_thread::sleep_for
26#include <chrono> // for std::chrono::milliseconds
27#include <sstream>
28#include <condition_variable>
29#include <mutex>
30
31using namespace juce;
32
33namespace openshot
34{
35 // Global reference to device manager
36 AudioDeviceManagerSingleton *AudioDeviceManagerSingleton::m_pInstance = NULL;
37
38 // Create or Get audio device singleton with default settings (44100, 2)
40 {
42 }
43
44 // Create or Get an instance of the device manager singleton (with custom sample rate & channels)
46 {
47 static std::mutex mutex;
48 std::lock_guard<std::mutex> lock(mutex);
49
50 if (!m_pInstance) {
51 // Create the actual instance of device manager only once
52 m_pInstance = new AudioDeviceManagerSingleton;
53 auto* mgr = &m_pInstance->audioDeviceManager;
54 AudioIODevice *foundAudioIODevice = NULL;
55 m_pInstance->initialise_error = "";
56 m_pInstance->currentAudioDevice.name = "";
57 m_pInstance->currentAudioDevice.type = "";
58 m_pInstance->defaultSampleRate = 0.0;
59
60 std::stringstream constructor_title;
61 constructor_title << "AudioDeviceManagerSingleton::Instance (default audio device type: " <<
62 Settings::Instance()->PLAYBACK_AUDIO_DEVICE_TYPE << ", default audio device name: " <<
64 Logger::Instance()->AppendDebugMethod(constructor_title.str(), "channels", channels, "buffer", Settings::Instance()->PLAYBACK_AUDIO_BUFFER_SIZE);
65
66 // Get preferred audio device type and name (if any - these can be blank)
69
70 // Find missing device type (if needed)
71 if (requested_device.type.isEmpty() && !requested_device.name.isEmpty()) {
72 for (const auto t : mgr->getAvailableDeviceTypes()) {
73 t->scanForDevices();
74 for (const auto n : t->getDeviceNames()) {
75 if (requested_device.name.trim().equalsIgnoreCase(n.trim())) {
76 requested_device.type = t->getTypeName();
77 break;
78 }
79 }
80 }
81 }
82
83 // Populate all possible device types and device names (starting with the user's requested settings)
84 std::vector<openshot::AudioDeviceInfo> devices{ { requested_device } };
85 for (const auto t : mgr->getAvailableDeviceTypes()) {
86 std::stringstream type_debug;
87 type_debug << "AudioDeviceManagerSingleton::Instance (iterate audio device type: " << t->getTypeName() << ")";
88 Logger::Instance()->AppendDebugMethod(type_debug.str(), "rate", rate, "channels", channels);
89
90 t->scanForDevices();
91 for (const auto n : t->getDeviceNames()) {
92 AudioDeviceInfo device = { t->getTypeName(), n.trim() };
93 devices.push_back(device);
94 std::stringstream device_debug;
95 device_debug << "AudioDeviceManagerSingleton::Instance (iterate audio device name: " << device.name << ", type: " << t->getTypeName() << ")";
96 Logger::Instance()->AppendDebugMethod(device_debug.str(), "rate", rate, "channels", channels);
97 }
98 }
99
100 // Loop through all device combinations (starting with the requested one)
101 for (auto attempt_device : devices) {
102 m_pInstance->currentAudioDevice = attempt_device;
103
104 // Resets everything to a default device setup
105 m_pInstance->audioDeviceManager.initialiseWithDefaultDevices(0, channels);
106
107 // Set device type (if any)
108 if (!attempt_device.type.isEmpty()) {
109 m_pInstance->audioDeviceManager.setCurrentAudioDeviceType(attempt_device.type, true);
110 }
111
112 // Settings for audio device playback
113 AudioDeviceManager::AudioDeviceSetup deviceSetup = AudioDeviceManager::AudioDeviceSetup();
114 deviceSetup.inputChannels = 0;
115 deviceSetup.outputChannels = channels;
116 deviceSetup.bufferSize = Settings::Instance()->PLAYBACK_AUDIO_BUFFER_SIZE;
117
118 // Loop through common sample rates, starting with the user's requested rate
119 // Not all sample rates are supported by audio devices, for example, many VMs
120 // do not support 48000 causing no audio device to be found.
121 int possible_rates[] { rate, 48000, 44100, 22050 };
122 for(int attempt_rate : possible_rates) {
123 std::stringstream title_rate;
124 title_rate << "AudioDeviceManagerSingleton::Instance (attempt audio device name: " << attempt_device.name << ")";
125 Logger::Instance()->AppendDebugMethod(title_rate.str(), "rate", attempt_rate, "channels", channels);
126
127 // Update the audio device setup for the current sample rate
128 m_pInstance->defaultSampleRate = attempt_rate;
129 deviceSetup.sampleRate = attempt_rate;
130 m_pInstance->audioDeviceManager.setAudioDeviceSetup(deviceSetup, true);
131
132 // Open the audio device with specific sample rate (if possible)
133 // Not all sample rates are supported by audio devices
134 juce::String audio_error = m_pInstance->audioDeviceManager.initialise(
135 0, // number of input channels
136 channels, // number of output channels
137 nullptr, // no XML settings..
138 true, // select default device on failure
139 attempt_device.name, // preferredDefaultDeviceName
140 &deviceSetup // sample_rate & channels
141 );
142
143 // Persist any errors detected
144 m_pInstance->initialise_error = audio_error.toStdString();
145
146 if (!m_pInstance->initialise_error.empty()) {
147 std::stringstream title_error;
148 title_error << "AudioDeviceManagerSingleton::Instance (audio device error: " <<
149 m_pInstance->initialise_error << ")";
150 Logger::Instance()->AppendDebugMethod(title_error.str(), "rate", attempt_rate, "channels", channels);
151 }
152
153 // Determine if audio device was opened successfully, and matches the attempted sample rate
154 // If all rates fail to match, a default audio device and sample rate will be opened if possible
155 foundAudioIODevice = m_pInstance->audioDeviceManager.getCurrentAudioDevice();
156 if (foundAudioIODevice && foundAudioIODevice->getCurrentSampleRate() == attempt_rate) {
157 // Successfully tested a sample rate
158 std::stringstream title_found;
159 title_found << "AudioDeviceManagerSingleton::Instance (successful audio device found: " <<
160 foundAudioIODevice->getTypeName() << ", name: " << foundAudioIODevice->getName() << ")";
161 Logger::Instance()->AppendDebugMethod(title_found.str(), "rate", attempt_rate, "channels", channels);
162 break;
163 }
164 }
165
166 if (foundAudioIODevice) {
167 // Successfully opened an audio device
168 break;
169 }
170 }
171
172 Logger::Instance()->AppendDebugMethod("AudioDeviceManagerSingleton::Instance (audio device initialization completed)");
173 }
174 return m_pInstance;
175 }
176
177 // Close audio device
179 {
180 // Close Audio Device
181 audioDeviceManager.closeAudioDevice();
182 audioDeviceManager.removeAllChangeListeners();
183 audioDeviceManager.dispatchPendingMessages();
184
185 delete m_pInstance;
186 m_pInstance = NULL;
187 }
188
189 // Constructor
190 AudioPlaybackThread::AudioPlaybackThread(openshot::VideoCacheThread* cache)
191 : juce::Thread("audio-playback")
192 , player()
193 , transport()
194 , mixer()
195 , source(NULL)
196 , sampleRate(0.0)
197 , numChannels(0)
198 , is_playing(false)
199 , time_thread("audio-buffer")
200 , videoCache(cache)
201 {
202 }
203
204 // Destructor
205 AudioPlaybackThread::~AudioPlaybackThread()
206 {
207 // Detach the JUCE callback in run() before releasing its source.
208 signalThreadShouldExit();
209 NotifyTransportStateChanged();
210 stopThread(-1);
211 delete source;
212 }
213
214 // Set the reader object
215 void AudioPlaybackThread::Reader(openshot::ReaderBase *reader) {
216 if (source) {
217 // Caller must stop/join playback before replacing the borrowed reader.
218 source->Reader(reader);
219 source->Seek(1);
220 }
221 else {
222 // Create new audio source reader
223 auto starting_frame = 1;
224 source = new AudioReaderSource(reader, starting_frame);
225 }
226
227 // Set local vars
228 sampleRate = reader->info.sample_rate;
229 numChannels = reader->info.channels;
230
231 Logger::Instance()->AppendDebugMethod("AudioPlaybackThread::Reader", "rate", sampleRate, "channel", numChannels);
232
233 // Set video cache thread
234 source->setVideoCache(videoCache);
235
236 // Mark as 'playing'
237 Play();
238 }
239
240 // Get the current frame object (which is filling the buffer)
241 std::shared_ptr<openshot::Frame> AudioPlaybackThread::getFrame()
242 {
243 if (source) return source->getFrame();
244 return std::shared_ptr<openshot::Frame>();
245 }
246
247 // Seek the audio thread
248 void AudioPlaybackThread::Seek(int64_t new_position)
249 {
250 if (source) {
251 source->Seek(new_position);
252 }
253 }
254
255 // Override Play and Stop to notify of state changes
256 void AudioPlaybackThread::Play() {
257 is_playing = true;
258 NotifyTransportStateChanged();
259 }
260
261 void AudioPlaybackThread::Stop() {
262 is_playing = false;
263 if (source) source->Seek(1);
264 NotifyTransportStateChanged();
265 }
266
267 void AudioPlaybackThread::NotifyTransportStateChanged()
268 {
269 std::lock_guard<std::mutex> lock(transportMutex);
270 transportCondition.notify_all();
271 }
272
273 // Start audio thread
274 void AudioPlaybackThread::run()
275 {
276 while (!threadShouldExit())
277 {
278 if (source && !transport.isPlaying() && is_playing) {
279 // Start new audio device (or get existing one)
280 AudioDeviceManagerSingleton *audioInstance =
281 AudioDeviceManagerSingleton::Instance(sampleRate, numChannels);
282
283 // Add callback
284 audioInstance->audioDeviceManager.addAudioCallback(&player);
285
286 // Create TimeSliceThread for audio buffering
287 time_thread.startThread(Priority::high);
288
289 // Connect source to transport
290 transport.setSource(
291 source,
292 0, // No read ahead buffer
293 &time_thread,
294 0, // Sample rate correction (none)
295 numChannels); // max channels
296 transport.setPosition(0);
297 transport.setGain(1.0);
298
299 // Connect transport to mixer and player
300 mixer.addInputSource(&transport, false);
301 player.setSource(&mixer);
302
303 // Start the transport
304 transport.start();
305
306 while (!threadShouldExit() && transport.isPlaying() && is_playing) {
307 // Wait until transport state changes or thread should exit
308 std::unique_lock<std::mutex> lock(transportMutex);
309 transportCondition.wait_for(lock, std::chrono::milliseconds(10), [this]() {
310 return threadShouldExit() || !transport.isPlaying() || !is_playing;
311 });
312 }
313
314 // Stop audio and shutdown transport
315 Stop();
316 transport.stop();
317
318 // Kill previous audio
319 transport.setSource(NULL);
320
321 player.setSource(NULL);
322 audioInstance->audioDeviceManager.removeAudioCallback(&player);
323
324 // Keep the source alive across Stop/Play. Control callers can
325 // enqueue a seek without racing source destruction. The worker
326 // detaches it above; the owner deletes it after joining.
327 mixer.removeInputSource(&transport);
328
329 // Stop time slice thread
330 time_thread.stopThread(-1);
331 } else {
332 std::unique_lock<std::mutex> lock(transportMutex);
333 transportCondition.wait_for(lock, std::chrono::milliseconds(10), [this] {
334 return threadShouldExit() || (source && is_playing.load());
335 });
336 }
337 }
338
339 }
340}
Source file for AudioPlaybackThread class.
Header file for global Settings class.
Singleton wrapper for AudioDeviceManager (to prevent multiple instances).
double defaultSampleRate
Default sample rate (as detected)
static AudioDeviceManagerSingleton * Instance()
Override with default sample rate & channels (44100, 2) and no preferred audio device.
juce::AudioDeviceManager audioDeviceManager
Public device manager property.
AudioDeviceInfo currentAudioDevice
Current open audio device (or last attempted device - if none were successful)
std::string initialise_error
Error found during JUCE initialise method.
std::shared_ptr< Frame > getFrame() const
Return the current frame object.
void Reader(ReaderBase *audio_reader)
Set Reader.
void Seek(int64_t new_position)
Seek to a specific frame.
void setVideoCache(openshot::VideoCacheThread *newCache)
Set playback video cache thread (for pre-roll reference)
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)
Definition: Logger.cpp:168
static Logger * Instance()
Definition: Logger.cpp:55
This abstract class is the base class, used by all readers in libopenshot.
Definition: ReaderBase.h:76
openshot::ReaderInfo info
Information about the current media file.
Definition: ReaderBase.h:91
int PLAYBACK_AUDIO_BUFFER_SIZE
Size of playback buffer before audio playback starts.
Definition: Settings.h:119
std::string PLAYBACK_AUDIO_DEVICE_NAME
The audio device name to use during playback.
Definition: Settings.h:113
std::string PLAYBACK_AUDIO_DEVICE_TYPE
The device type for the playback audio devices.
Definition: Settings.h:116
static Settings * Instance()
Create or get an instance of this logger singleton (invoke the class with this method)
Definition: Settings.cpp:43
Handles prefetching and caching of video/audio frames for smooth playback.
This namespace is the default namespace for all code in the openshot library.
Definition: AnimatedCurve.h:24
This struct hold information about Audio Devices.
Definition: AudioDevices.h:27
int channels
The number of audio channels used in the audio stream.
Definition: ReaderBase.h:61
int sample_rate
The number of audio samples per second (44100 is a common sample rate)
Definition: ReaderBase.h:60