Loading...
Searching...
No Matches
AudioPlaybackThread.cpp
Go to the documentation of this file.
// Copyright (c) 2008-2019 OpenShot Studios, LLC
//
// SPDX-License-Identifier: LGPL-3.0-or-later
#include "AudioPlaybackThread.h"
#include "Settings.h"
#include "../ReaderBase.h"
#include "../RendererBase.h"
#include "../AudioReaderSource.h"
#include "../AudioDevices.h"
#include "../Settings.h"
#include "../ZmqLogger.h"
#include <mutex>
#include <thread> // for std::this_thread::sleep_for
#include <chrono> // for std::chrono::milliseconds
#include <sstream>
using namespace juce;
namespace openshot
{
// Global reference to device manager
AudioDeviceManagerSingleton *AudioDeviceManagerSingleton::m_pInstance = NULL;
// Create or Get audio device singleton with default settings (44100, 2)
AudioDeviceManagerSingleton *AudioDeviceManagerSingleton::Instance()
{
return AudioDeviceManagerSingleton::Instance(44100, 2);
}
// Create or Get an instance of the device manager singleton (with custom sample rate & channels)
AudioDeviceManagerSingleton *AudioDeviceManagerSingleton::Instance(int rate, int channels)
{
static std::mutex mutex;
std::lock_guard<std::mutex> lock(mutex);
if (!m_pInstance) {
// Create the actual instance of device manager only once
m_pInstance = new AudioDeviceManagerSingleton;
auto* mgr = &m_pInstance->audioDeviceManager;
AudioIODevice *foundAudioIODevice = NULL;
m_pInstance->initialise_error = "";
m_pInstance->currentAudioDevice.name = "";
m_pInstance->currentAudioDevice.type = "";
m_pInstance->defaultSampleRate = 0.0;
std::stringstream constructor_title;
constructor_title << "AudioDeviceManagerSingleton::Instance (default audio device type: " <<
Settings::Instance()->PLAYBACK_AUDIO_DEVICE_TYPE << ", default audio device name: " <<
Settings::Instance()->PLAYBACK_AUDIO_DEVICE_NAME << ")";
ZmqLogger::Instance()->AppendDebugMethod(constructor_title.str(), "channels", channels);
// Get preferred audio device type and name (if any - these can be blank)
openshot::AudioDeviceInfo requested_device = {Settings::Instance()->PLAYBACK_AUDIO_DEVICE_TYPE,
Settings::Instance()->PLAYBACK_AUDIO_DEVICE_NAME};
// Find missing device type (if needed)
if (requested_device.type.isEmpty() && !requested_device.name.isEmpty()) {
for (const auto t : mgr->getAvailableDeviceTypes()) {
t->scanForDevices();
for (const auto n : t->getDeviceNames()) {
if (requested_device.name.trim().equalsIgnoreCase(n.trim())) {
requested_device.type = t->getTypeName();
break;
}
}
}
}
// Populate all possible device types and device names (starting with the user's requested settings)
std::vector<openshot::AudioDeviceInfo> devices{ { requested_device } };
for (const auto t : mgr->getAvailableDeviceTypes()) {
t->scanForDevices();
for (const auto n : t->getDeviceNames()) {
AudioDeviceInfo device = { t->getTypeName(), n.trim() };
devices.push_back(device);
}
}
// Loop through all device combinations (starting with the requested one)
for (auto attempt_device : devices) {
m_pInstance->currentAudioDevice = attempt_device;
// Resets everything to a default device setup
m_pInstance->audioDeviceManager.initialiseWithDefaultDevices(0, channels);
// Set device type (if any)
if (!attempt_device.type.isEmpty()) {
m_pInstance->audioDeviceManager.setCurrentAudioDeviceType(attempt_device.type, true);
}
// Settings for audio device playback
AudioDeviceManager::AudioDeviceSetup deviceSetup = AudioDeviceManager::AudioDeviceSetup();
deviceSetup.inputChannels = 0;
deviceSetup.outputChannels = channels;
// Loop through common sample rates, starting with the user's requested rate
// Not all sample rates are supported by audio devices, for example, many VMs
// do not support 48000 causing no audio device to be found.
int possible_rates[] { rate, 48000, 44100, 22050 };
for(int attempt_rate : possible_rates) {
std::stringstream title_rate;
title_rate << "AudioDeviceManagerSingleton::Instance (attempt audio device name: " << attempt_device.name << ")";
ZmqLogger::Instance()->AppendDebugMethod(title_rate.str(), "rate", attempt_rate, "channels", channels);
// Update the audio device setup for the current sample rate
m_pInstance->defaultSampleRate = attempt_rate;
deviceSetup.sampleRate = attempt_rate;
m_pInstance->audioDeviceManager.setAudioDeviceSetup(deviceSetup, true);
// Open the audio device with specific sample rate (if possible)
// Not all sample rates are supported by audio devices
juce::String audio_error = m_pInstance->audioDeviceManager.initialise(
0, // number of input channels
channels, // number of output channels
nullptr, // no XML settings..
true, // select default device on failure
attempt_device.name, // preferredDefaultDeviceName
&deviceSetup // sample_rate & channels
);
// Persist any errors detected
m_pInstance->initialise_error = audio_error.toStdString();
if (!m_pInstance->initialise_error.empty()) {
std::stringstream title_error;
title_error << "AudioDeviceManagerSingleton::Instance (audio device error: " <<
m_pInstance->initialise_error << ")";
ZmqLogger::Instance()->AppendDebugMethod(title_error.str(), "rate", attempt_rate, "channels", channels);
}
// Determine if audio device was opened successfully, and matches the attempted sample rate
// If all rates fail to match, a default audio device and sample rate will be opened if possible
foundAudioIODevice = m_pInstance->audioDeviceManager.getCurrentAudioDevice();
if (foundAudioIODevice && foundAudioIODevice->getCurrentSampleRate() == attempt_rate) {
// Successfully tested a sample rate
std::stringstream title_found;
title_found << "AudioDeviceManagerSingleton::Instance (successful audio device found: " <<
foundAudioIODevice->getTypeName() << ", name: " << foundAudioIODevice->getName() << ")";
ZmqLogger::Instance()->AppendDebugMethod(title_found.str(), "rate", attempt_rate, "channels", channels);
break;
}
}
if (foundAudioIODevice) {
// Successfully opened an audio device
break;
}
}
ZmqLogger::Instance()->AppendDebugMethod("AudioDeviceManagerSingleton::Instance (audio device initialization completed)");
}
return m_pInstance;
}
// Close audio device
void AudioDeviceManagerSingleton::CloseAudioDevice()
{
// Close Audio Device
audioDeviceManager.closeAudioDevice();
audioDeviceManager.removeAllChangeListeners();
audioDeviceManager.dispatchPendingMessages();
delete m_pInstance;
m_pInstance = NULL;
}
// Constructor
AudioPlaybackThread::AudioPlaybackThread(openshot::VideoCacheThread* cache)
: juce::Thread("audio-playback")
, player()
, transport()
, mixer()
, source(NULL)
, sampleRate(0.0)
, numChannels(0)
, is_playing(false)
, time_thread("audio-buffer")
, videoCache(cache)
{
}
// Destructor
AudioPlaybackThread::~AudioPlaybackThread()
{
}
// Set the reader object
void AudioPlaybackThread::Reader(openshot::ReaderBase *reader) {
if (source)
source->Reader(reader);
else {
// Create new audio source reader
auto starting_frame = 1;
source = new AudioReaderSource(reader, starting_frame);
}
// Set local vars
sampleRate = reader->info.sample_rate;
numChannels = reader->info.channels;
ZmqLogger::Instance()->AppendDebugMethod("AudioPlaybackThread::Reader", "rate", sampleRate, "channel", numChannels);
// Set video cache thread
source->setVideoCache(videoCache);
// Mark as 'playing'
Play();
}
// Get the current frame object (which is filling the buffer)
std::shared_ptr<openshot::Frame> AudioPlaybackThread::getFrame()
{
if (source) return source->getFrame();
return std::shared_ptr<openshot::Frame>();
}
// Seek the audio thread
void AudioPlaybackThread::Seek(int64_t new_position)
{
if (source) {
source->Seek(new_position);
}
}
// Play the audio
void AudioPlaybackThread::Play() {
// Start playing
is_playing = true;
}
// Stop the audio
void AudioPlaybackThread::Stop() {
// Stop playing
is_playing = false;
}
// Start audio thread
void AudioPlaybackThread::run()
{
while (!threadShouldExit())
{
if (source && !transport.isPlaying() && is_playing) {
// Start new audio device (or get existing one)
AudioDeviceManagerSingleton *audioInstance =
AudioDeviceManagerSingleton::Instance(sampleRate, numChannels);
// Add callback
audioInstance->audioDeviceManager.addAudioCallback(&player);
// Create TimeSliceThread for audio buffering
time_thread.startThread();
// Connect source to transport
transport.setSource(
source,
0, // No read ahead buffer
&time_thread,
0, // Sample rate correction (none)
numChannels); // max channels
transport.setPosition(0);
transport.setGain(1.0);
// Connect transport to mixer and player
mixer.addInputSource(&transport, false);
player.setSource(&mixer);
// Start the transport
transport.start();
while (!threadShouldExit() && transport.isPlaying() && is_playing)
std::this_thread::sleep_for(std::chrono::milliseconds(2));
// Stop audio and shutdown transport
Stop();
transport.stop();
// Kill previous audio
transport.setSource(NULL);
player.setSource(NULL);
audioInstance->audioDeviceManager.removeAudioCallback(&player);
// Remove source
delete source;
source = NULL;
// Stop time slice thread
time_thread.stopThread(-1);
}
}
}
}
AudioPlaybackThread.h
Source file for AudioPlaybackThread class.
Settings.h
Header file for global Settings class.
openshot::AudioDeviceManagerSingleton
Singleton wrapper for AudioDeviceManager (to prevent multiple instances).
Definition AudioPlaybackThread.h:42
openshot::AudioDeviceManagerSingleton::defaultSampleRate
double defaultSampleRate
Default sample rate (as detected)
Definition AudioPlaybackThread.h:55
openshot::AudioDeviceManagerSingleton::CloseAudioDevice
void CloseAudioDevice()
Close audio device.
Definition AudioPlaybackThread.cpp:168
openshot::AudioDeviceManagerSingleton::Instance
static AudioDeviceManagerSingleton * Instance()
Override with default sample rate & channels (44100, 2) and no preferred audio device.
Definition AudioPlaybackThread.cpp:37
openshot::AudioDeviceManagerSingleton::audioDeviceManager
juce::AudioDeviceManager audioDeviceManager
Public device manager property.
Definition AudioPlaybackThread.h:68
openshot::AudioDeviceManagerSingleton::currentAudioDevice
AudioDeviceInfo currentAudioDevice
Current open audio device (or last attempted device - if none were successful)
Definition AudioPlaybackThread.h:58
openshot::AudioDeviceManagerSingleton::initialise_error
std::string initialise_error
Error found during JUCE initialise method.
Definition AudioPlaybackThread.h:52
openshot::AudioReaderSource::Seek
void Seek(int64_t new_position)
Seek to a specific frame.
Definition AudioReaderSource.h:97
openshot::AudioReaderSource::Reader
void Reader(ReaderBase *audio_reader)
Set Reader.
Definition AudioReaderSource.h:92
openshot::AudioReaderSource::getFrame
std::shared_ptr< Frame > getFrame() const
Return the current frame object.
Definition AudioReaderSource.h:81
openshot::AudioReaderSource::setVideoCache
void setVideoCache(openshot::VideoCacheThread *newCache)
Set playback video cache thread (for pre-roll reference)
Definition AudioReaderSource.h:89
openshot::ReaderBase
This abstract class is the base class, used by all readers in libopenshot.
Definition ReaderBase.h:76
openshot::ReaderBase::info
openshot::ReaderInfo info
Information about the current media file.
Definition ReaderBase.h:88
openshot::Settings::PLAYBACK_AUDIO_DEVICE_NAME
std::string PLAYBACK_AUDIO_DEVICE_NAME
The audio device name to use during playback.
Definition Settings.h:101
openshot::Settings::PLAYBACK_AUDIO_DEVICE_TYPE
std::string PLAYBACK_AUDIO_DEVICE_TYPE
The device type for the playback audio devices.
Definition Settings.h:104
openshot::Settings::Instance
static Settings * Instance()
Create or get an instance of this logger singleton (invoke the class with this method)
Definition Settings.cpp:23
openshot::VideoCacheThread
The video cache class.
Definition VideoCacheThread.h:30
openshot::ZmqLogger::AppendDebugMethod
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.
Definition ZmqLogger.cpp:178
openshot::ZmqLogger::Instance
static ZmqLogger * Instance()
Create or get an instance of this logger singleton (invoke the class with this method)
Definition ZmqLogger.cpp:35
juce
Definition Robotization.h:29
openshot
This namespace is the default namespace for all code in the openshot library.
Definition Compressor.h:29
openshot::AudioDeviceInfo
This struct hold information about Audio Devices.
Definition AudioDevices.h:27
openshot::AudioDeviceInfo::name
juce::String name
Definition AudioDevices.h:29
openshot::AudioDeviceInfo::type
juce::String type
Definition AudioDevices.h:28
openshot::ReaderInfo::channels
int channels
The number of audio channels used in the audio stream.
Definition ReaderBase.h:61
openshot::ReaderInfo::sample_rate
int sample_rate
The number of audio samples per second (44100 is a common sample rate)
Definition ReaderBase.h:60