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"
19#include "../AudioReaderSource.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}
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.
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