OpenShot Library | libopenshot 0.5.0
Loading...
Searching...
No Matches
AudioPlaybackThread.cpp
Go to the documentation of this file.
1
9
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"
20#include "../AudioDevices.h"
21#include "../Settings.h"
22#include "../ZmqLogger.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)
39 AudioDeviceManagerSingleton *AudioDeviceManagerSingleton::Instance()
40 {
42 }
43
44 // Create or Get an instance of the device manager singleton (with custom sample rate & channels)
45 AudioDeviceManagerSingleton *AudioDeviceManagerSingleton::Instance(int rate, int 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 ZmqLogger::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 ZmqLogger::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 ZmqLogger::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 ZmqLogger::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 ZmqLogger::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 ZmqLogger::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 ZmqLogger::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 }
208
209 // Set the reader object
210 void AudioPlaybackThread::Reader(openshot::ReaderBase *reader) {
211 if (source)
212 source->Reader(reader);
213 else {
214 // Create new audio source reader
215 auto starting_frame = 1;
216 source = new AudioReaderSource(reader, starting_frame);
217 }
218
219 // Set local vars
220 sampleRate = reader->info.sample_rate;
221 numChannels = reader->info.channels;
222
223 ZmqLogger::Instance()->AppendDebugMethod("AudioPlaybackThread::Reader", "rate", sampleRate, "channel", numChannels);
224
225 // Set video cache thread
226 source->setVideoCache(videoCache);
227
228 // Mark as 'playing'
229 Play();
230 }
231
232 // Get the current frame object (which is filling the buffer)
233 std::shared_ptr<openshot::Frame> AudioPlaybackThread::getFrame()
234 {
235 if (source) return source->getFrame();
236 return std::shared_ptr<openshot::Frame>();
237 }
238
239 // Seek the audio thread
240 void AudioPlaybackThread::Seek(int64_t new_position)
241 {
242 if (source) {
243 source->Seek(new_position);
244 }
245 }
246
247 // Override Play and Stop to notify of state changes
248 void AudioPlaybackThread::Play() {
249 is_playing = true;
250 NotifyTransportStateChanged();
251 }
252
253 void AudioPlaybackThread::Stop() {
254 is_playing = false;
255 NotifyTransportStateChanged();
256 }
257
258 void AudioPlaybackThread::NotifyTransportStateChanged()
259 {
260 std::lock_guard<std::mutex> lock(transportMutex);
261 transportCondition.notify_all();
262 }
263
264 // Start audio thread
265 void AudioPlaybackThread::run()
266 {
267 while (!threadShouldExit())
268 {
269 if (source && !transport.isPlaying() && is_playing) {
270 // Start new audio device (or get existing one)
271 AudioDeviceManagerSingleton *audioInstance =
272 AudioDeviceManagerSingleton::Instance(sampleRate, numChannels);
273
274 // Add callback
275 audioInstance->audioDeviceManager.addAudioCallback(&player);
276
277 // Create TimeSliceThread for audio buffering
278 time_thread.startThread(Priority::high);
279
280 // Connect source to transport
281 transport.setSource(
282 source,
283 0, // No read ahead buffer
284 &time_thread,
285 0, // Sample rate correction (none)
286 numChannels); // max channels
287 transport.setPosition(0);
288 transport.setGain(1.0);
289
290 // Connect transport to mixer and player
291 mixer.addInputSource(&transport, false);
292 player.setSource(&mixer);
293
294 // Start the transport
295 transport.start();
296
297 while (!threadShouldExit() && transport.isPlaying() && is_playing) {
298 // Wait until transport state changes or thread should exit
299 std::unique_lock<std::mutex> lock(transportMutex);
300 transportCondition.wait_for(lock, std::chrono::milliseconds(10), [this]() {
301 return threadShouldExit() || !transport.isPlaying() || !is_playing;
302 });
303 }
304
305 // Stop audio and shutdown transport
306 Stop();
307 transport.stop();
308
309 // Kill previous audio
310 transport.setSource(NULL);
311
312 player.setSource(NULL);
313 audioInstance->audioDeviceManager.removeAudioCallback(&player);
314
315 // Remove source
316 delete source;
317 source = NULL;
318
319 // Stop time slice thread
320 time_thread.stopThread(-1);
321 }
322 }
323
324 }
325}
Header file for Audio Device Info struct.
Source file for AudioPlaybackThread class.
Header file for AudioReaderSource class.
Header file for ReaderBase class.
Header file for RendererBase class.
Header file for global Settings class.
Header file for ZeroMQ-based Logger class.
Singleton wrapper for AudioDeviceManager (to prevent multiple instances).
static AudioDeviceManagerSingleton * Instance()
Override with default sample rate & channels (44100, 2) and no preferred audio device.
juce::AudioDeviceManager audioDeviceManager
Public device manager property.
openshot::ReaderInfo info
Information about the current media file.
Definition ReaderBase.h:88
int PLAYBACK_AUDIO_BUFFER_SIZE
Size of playback buffer before audio playback starts.
Definition Settings.h:107
std::string PLAYBACK_AUDIO_DEVICE_NAME
The audio device name to use during playback.
Definition Settings.h:101
std::string PLAYBACK_AUDIO_DEVICE_TYPE
The device type for the playback audio devices.
Definition Settings.h:104
static Settings * Instance()
Create or get an instance of this logger singleton (invoke the class with this method).
Definition Settings.cpp:23
Handles prefetching and caching of video/audio frames for smooth playback.
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.
static ZmqLogger * Instance()
Create or get an instance of this logger singleton (invoke the class with this method).
Definition ZmqLogger.cpp:35
This namespace is the default namespace for all code in the openshot library.
Definition Compressor.h:29
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