OpenShot Library | libopenshot 0.3.3
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"
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
29using namespace juce;
30
31namespace openshot
32{
33 // Global reference to device manager
34 AudioDeviceManagerSingleton *AudioDeviceManagerSingleton::m_pInstance = NULL;
35
36 // Create or Get audio device singleton with default settings (44100, 2)
41
42 // Create or Get an instance of the device manager singleton (with custom sample rate & channels)
44 {
45 static std::mutex mutex;
46 std::lock_guard<std::mutex> lock(mutex);
47
48 if (!m_pInstance) {
49 // Create the actual instance of device manager only once
50 m_pInstance = new AudioDeviceManagerSingleton;
51 auto* mgr = &m_pInstance->audioDeviceManager;
52 AudioIODevice *foundAudioIODevice = NULL;
53 m_pInstance->initialise_error = "";
54 m_pInstance->currentAudioDevice.name = "";
55 m_pInstance->currentAudioDevice.type = "";
56 m_pInstance->defaultSampleRate = 0.0;
57
58 std::stringstream constructor_title;
59 constructor_title << "AudioDeviceManagerSingleton::Instance (default audio device type: " <<
60 Settings::Instance()->PLAYBACK_AUDIO_DEVICE_TYPE << ", default audio device name: " <<
62 ZmqLogger::Instance()->AppendDebugMethod(constructor_title.str(), "channels", channels);
63
64 // Get preferred audio device type and name (if any - these can be blank)
67
68 // Find missing device type (if needed)
69 if (requested_device.type.isEmpty() && !requested_device.name.isEmpty()) {
70 for (const auto t : mgr->getAvailableDeviceTypes()) {
71 t->scanForDevices();
72 for (const auto n : t->getDeviceNames()) {
73 if (requested_device.name.trim().equalsIgnoreCase(n.trim())) {
74 requested_device.type = t->getTypeName();
75 break;
76 }
77 }
78 }
79 }
80
81 // Populate all possible device types and device names (starting with the user's requested settings)
82 std::vector<openshot::AudioDeviceInfo> devices{ { requested_device } };
83 for (const auto t : mgr->getAvailableDeviceTypes()) {
84 t->scanForDevices();
85 for (const auto n : t->getDeviceNames()) {
86 AudioDeviceInfo device = { t->getTypeName(), n.trim() };
87 devices.push_back(device);
88 }
89 }
90
91 // Loop through all device combinations (starting with the requested one)
92 for (auto attempt_device : devices) {
93 m_pInstance->currentAudioDevice = attempt_device;
94
95 // Resets everything to a default device setup
96 m_pInstance->audioDeviceManager.initialiseWithDefaultDevices(0, channels);
97
98 // Set device type (if any)
99 if (!attempt_device.type.isEmpty()) {
100 m_pInstance->audioDeviceManager.setCurrentAudioDeviceType(attempt_device.type, true);
101 }
102
103 // Settings for audio device playback
104 AudioDeviceManager::AudioDeviceSetup deviceSetup = AudioDeviceManager::AudioDeviceSetup();
105 deviceSetup.inputChannels = 0;
106 deviceSetup.outputChannels = channels;
107
108 // Loop through common sample rates, starting with the user's requested rate
109 // Not all sample rates are supported by audio devices, for example, many VMs
110 // do not support 48000 causing no audio device to be found.
111 int possible_rates[] { rate, 48000, 44100, 22050 };
112 for(int attempt_rate : possible_rates) {
113 std::stringstream title_rate;
114 title_rate << "AudioDeviceManagerSingleton::Instance (attempt audio device name: " << attempt_device.name << ")";
115 ZmqLogger::Instance()->AppendDebugMethod(title_rate.str(), "rate", attempt_rate, "channels", channels);
116
117 // Update the audio device setup for the current sample rate
118 m_pInstance->defaultSampleRate = attempt_rate;
119 deviceSetup.sampleRate = attempt_rate;
120 m_pInstance->audioDeviceManager.setAudioDeviceSetup(deviceSetup, true);
121
122 // Open the audio device with specific sample rate (if possible)
123 // Not all sample rates are supported by audio devices
124 juce::String audio_error = m_pInstance->audioDeviceManager.initialise(
125 0, // number of input channels
126 channels, // number of output channels
127 nullptr, // no XML settings..
128 true, // select default device on failure
129 attempt_device.name, // preferredDefaultDeviceName
130 &deviceSetup // sample_rate & channels
131 );
132
133 // Persist any errors detected
134 m_pInstance->initialise_error = audio_error.toStdString();
135
136 if (!m_pInstance->initialise_error.empty()) {
137 std::stringstream title_error;
138 title_error << "AudioDeviceManagerSingleton::Instance (audio device error: " <<
139 m_pInstance->initialise_error << ")";
140 ZmqLogger::Instance()->AppendDebugMethod(title_error.str(), "rate", attempt_rate, "channels", channels);
141 }
142
143 // Determine if audio device was opened successfully, and matches the attempted sample rate
144 // If all rates fail to match, a default audio device and sample rate will be opened if possible
145 foundAudioIODevice = m_pInstance->audioDeviceManager.getCurrentAudioDevice();
146 if (foundAudioIODevice && foundAudioIODevice->getCurrentSampleRate() == attempt_rate) {
147 // Successfully tested a sample rate
148 std::stringstream title_found;
149 title_found << "AudioDeviceManagerSingleton::Instance (successful audio device found: " <<
150 foundAudioIODevice->getTypeName() << ", name: " << foundAudioIODevice->getName() << ")";
151 ZmqLogger::Instance()->AppendDebugMethod(title_found.str(), "rate", attempt_rate, "channels", channels);
152 break;
153 }
154 }
155
156 if (foundAudioIODevice) {
157 // Successfully opened an audio device
158 break;
159 }
160 }
161
162 ZmqLogger::Instance()->AppendDebugMethod("AudioDeviceManagerSingleton::Instance (audio device initialization completed)");
163 }
164 return m_pInstance;
165 }
166
167 // Close audio device
169 {
170 // Close Audio Device
171 audioDeviceManager.closeAudioDevice();
172 audioDeviceManager.removeAllChangeListeners();
173 audioDeviceManager.dispatchPendingMessages();
174
175 delete m_pInstance;
176 m_pInstance = NULL;
177 }
178
179 // Constructor
180 AudioPlaybackThread::AudioPlaybackThread(openshot::VideoCacheThread* cache)
181 : juce::Thread("audio-playback")
182 , player()
183 , transport()
184 , mixer()
185 , source(NULL)
186 , sampleRate(0.0)
187 , numChannels(0)
188 , is_playing(false)
189 , time_thread("audio-buffer")
190 , videoCache(cache)
191 {
192 }
193
194 // Destructor
195 AudioPlaybackThread::~AudioPlaybackThread()
196 {
197 }
198
199 // Set the reader object
200 void AudioPlaybackThread::Reader(openshot::ReaderBase *reader) {
201 if (source)
202 source->Reader(reader);
203 else {
204 // Create new audio source reader
205 auto starting_frame = 1;
206 source = new AudioReaderSource(reader, starting_frame);
207 }
208
209 // Set local vars
210 sampleRate = reader->info.sample_rate;
211 numChannels = reader->info.channels;
212
213 ZmqLogger::Instance()->AppendDebugMethod("AudioPlaybackThread::Reader", "rate", sampleRate, "channel", numChannels);
214
215 // Set video cache thread
216 source->setVideoCache(videoCache);
217
218 // Mark as 'playing'
219 Play();
220 }
221
222 // Get the current frame object (which is filling the buffer)
223 std::shared_ptr<openshot::Frame> AudioPlaybackThread::getFrame()
224 {
225 if (source) return source->getFrame();
226 return std::shared_ptr<openshot::Frame>();
227 }
228
229 // Seek the audio thread
230 void AudioPlaybackThread::Seek(int64_t new_position)
231 {
232 if (source) {
233 source->Seek(new_position);
234 }
235 }
236
237 // Play the audio
238 void AudioPlaybackThread::Play() {
239 // Start playing
240 is_playing = true;
241 }
242
243 // Stop the audio
244 void AudioPlaybackThread::Stop() {
245 // Stop playing
246 is_playing = false;
247 }
248
249 // Start audio thread
250 void AudioPlaybackThread::run()
251 {
252 while (!threadShouldExit())
253 {
254 if (source && !transport.isPlaying() && is_playing) {
255 // Start new audio device (or get existing one)
256 AudioDeviceManagerSingleton *audioInstance =
257 AudioDeviceManagerSingleton::Instance(sampleRate, numChannels);
258
259 // Add callback
260 audioInstance->audioDeviceManager.addAudioCallback(&player);
261
262 // Create TimeSliceThread for audio buffering
263 time_thread.startThread();
264
265 // Connect source to transport
266 transport.setSource(
267 source,
268 0, // No read ahead buffer
269 &time_thread,
270 0, // Sample rate correction (none)
271 numChannels); // max channels
272 transport.setPosition(0);
273 transport.setGain(1.0);
274
275 // Connect transport to mixer and player
276 mixer.addInputSource(&transport, false);
277 player.setSource(&mixer);
278
279 // Start the transport
280 transport.start();
281
282 while (!threadShouldExit() && transport.isPlaying() && is_playing)
283 std::this_thread::sleep_for(std::chrono::milliseconds(2));
284
285 // Stop audio and shutdown transport
286 Stop();
287 transport.stop();
288
289 // Kill previous audio
290 transport.setSource(NULL);
291
292 player.setSource(NULL);
293 audioInstance->audioDeviceManager.removeAudioCallback(&player);
294
295 // Remove source
296 delete source;
297 source = NULL;
298
299 // Stop time slice thread
300 time_thread.stopThread(-1);
301 }
302 }
303
304 }
305}
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).
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.
void Seek(int64_t new_position)
Seek to a specific frame.
void Reader(ReaderBase *audio_reader)
Set Reader.
std::shared_ptr< Frame > getFrame() const
Return the current frame object.
void setVideoCache(openshot::VideoCacheThread *newCache)
Set playback video cache thread (for pre-roll reference)
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:88
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
The video cache class.
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