OpenShot Library | libopenshot 1.1.1
Loading...
Searching...
No Matches
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)
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.
This struct hold information about Audio Devices.
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