diff --git a/bindings/python/openshot.i b/bindings/python/openshot.i index 755b195b..c8b28373 100644 --- a/bindings/python/openshot.i +++ b/bindings/python/openshot.i @@ -41,10 +41,20 @@ %shared_ptr(juce::AudioBuffer) %shared_ptr(openshot::Frame) +/* Instantiate the required template specializations */ +%template() std::map; +%template() std::pair; +%template() std::vector; +%template() std::pair; +%template() std::pair; +%template() std::pair; +%template() std::vector>; + %{ #include "OpenShotVersion.h" #include "ReaderBase.h" #include "WriterBase.h" +#include "AudioDevices.h" #include "CacheBase.h" #include "CacheDisk.h" #include "CacheMemory.h" @@ -79,7 +89,6 @@ #include "TimelineBase.h" #include "Timeline.h" #include "ZmqLogger.h" -#include "AudioDeviceInfo.h" %} @@ -116,12 +125,7 @@ } } -/* Instantiate the required template specializations */ -%template() std::map; -%template() std::pair; -%template() std::vector; -%template() std::pair; -%template() std::pair; + /* Wrap std templates (list, vector, etc...) */ %template(ClipList) std::list; @@ -131,6 +135,8 @@ %template(FieldVector) std::vector; %template(MappedFrameVector) std::vector; %template(MetadataMap) std::map; + +/* Deprecated */ %template(AudioDeviceInfoVector) std::vector; /* Make openshot.Fraction more Pythonic */ @@ -256,6 +262,7 @@ %include "OpenShotVersion.h" %include "ReaderBase.h" %include "WriterBase.h" +%include "AudioDevices.h" %include "CacheBase.h" %include "CacheDisk.h" %include "CacheMemory.h" @@ -290,7 +297,6 @@ %include "TimelineBase.h" %include "Timeline.h" %include "ZmqLogger.h" -%include "AudioDeviceInfo.h" #ifdef USE_OPENCV %include "ClipProcessingJobs.h" diff --git a/bindings/ruby/openshot.i b/bindings/ruby/openshot.i index bc8432ab..8fdb152b 100644 --- a/bindings/ruby/openshot.i +++ b/bindings/ruby/openshot.i @@ -41,12 +41,14 @@ %shared_ptr(juce::AudioBuffer) %shared_ptr(openshot::Frame) -/* Template specializations */ +/* Instantiate the required template specializations */ %template() std::map; %template() std::pair; %template() std::vector; %template() std::pair; %template() std::pair; +%template() std::pair; +%template() std::vector>; %{ /* Ruby and FFmpeg define competing RSHIFT macros, @@ -60,6 +62,7 @@ #include "OpenShotVersion.h" #include "ReaderBase.h" #include "WriterBase.h" +#include "AudioDevices.h" #include "CacheBase.h" #include "CacheDisk.h" #include "CacheMemory.h" @@ -94,7 +97,6 @@ #include "TimelineBase.h" #include "Timeline.h" #include "ZmqLogger.h" -#include "AudioDeviceInfo.h" /* Move FFmpeg's RSHIFT to FF_RSHIFT, if present */ #ifdef RSHIFT @@ -115,9 +117,22 @@ %} #endif +/* Wrap std templates (list, vector, etc...) */ +%template(ClipList) std::list; +%template(EffectBaseList) std::list; +%template(CoordinateVector) std::vector; +%template(PointsVector) std::vector; +%template(FieldVector) std::vector; +%template(MappedFrameVector) std::vector; +%template(MetadataMap) std::map; + +/* Deprecated */ +%template(AudioDeviceInfoVector) std::vector; + %include "OpenShotVersion.h" %include "ReaderBase.h" %include "WriterBase.h" +%include "AudioDevices.h" %include "CacheBase.h" %include "CacheDisk.h" %include "CacheMemory.h" @@ -173,7 +188,6 @@ %include "TimelineBase.h" %include "Timeline.h" %include "ZmqLogger.h" -%include "AudioDeviceInfo.h" #ifdef USE_IMAGEMAGICK %include "ImageReader.h" @@ -181,7 +195,6 @@ %include "TextReader.h" #endif - /* Effects */ %include "effects/Bars.h" %include "effects/Blur.h" @@ -200,12 +213,3 @@ %include "effects/Wave.h" -/* Wrap std templates (list, vector, etc...) */ -%template(ClipList) std::list; -%template(EffectBaseList) std::list; -%template(CoordinateVector) std::vector; -%template(PointsVector) std::vector; -%template(FieldVector) std::vector; -%template(MappedFrameVector) std::vector; -%template(MetadataMap) std::map; -%template(AudioDeviceInfoVector) std::vector; diff --git a/src/AudioDeviceInfo.h b/src/AudioDeviceInfo.h deleted file mode 100644 index 53378fc4..00000000 --- a/src/AudioDeviceInfo.h +++ /dev/null @@ -1,29 +0,0 @@ -/** - * @file - * @brief Header file for Audio Device Info struct - * @author Jonathan Thomas - * - * @ref License - */ - -// Copyright (c) 2008-2019 OpenShot Studios, LLC -// -// SPDX-License-Identifier: LGPL-3.0-or-later - -#ifndef OPENSHOT_AUDIODEVICEINFO_H -#define OPENSHOT_AUDIODEVICEINFO_H - - -/** - * @brief This struct hold information about Audio Devices - * - * The type and name of the audio device. - */ -namespace openshot { - struct AudioDeviceInfo - { - std::string name; - std::string type; - }; -} -#endif diff --git a/src/AudioDevices.cpp b/src/AudioDevices.cpp new file mode 100644 index 00000000..b004877f --- /dev/null +++ b/src/AudioDevices.cpp @@ -0,0 +1,41 @@ +/** + * @file + * @brief Utility methods for identifying audio devices + * @author Jonathan Thomas + * @author FeRD (Frank Dana) + * + * @ref License + */ + +// Copyright (c) 2008-2019 OpenShot Studios, LLC +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#include "AudioDevices.h" + +#include + +using namespace openshot; + +using AudioDeviceList = std::vector>; + +// Build a list of devices found, and return +AudioDeviceList AudioDevices::getNames() { + // A temporary device manager, used to scan device names. + // Its initialize() is never called, and devices are not opened. + std::unique_ptr + manager(new juce::AudioDeviceManager()); + + m_devices.clear(); + + auto &types = manager->getAvailableDeviceTypes(); + for (auto* t : types) { + t->scanForDevices(); + const auto names = t->getDeviceNames(); + for (const auto& name : names) { + m_devices.emplace_back( + name.toStdString(), t->getTypeName().toStdString()); + } + } + return m_devices; +} diff --git a/src/AudioDevices.h b/src/AudioDevices.h new file mode 100644 index 00000000..13165d4d --- /dev/null +++ b/src/AudioDevices.h @@ -0,0 +1,47 @@ +/** + * @file + * @brief Header file for Audio Device Info struct + * @author Jonathan Thomas + * + * @ref License + */ + +// Copyright (c) 2008-2019 OpenShot Studios, LLC +// +// SPDX-License-Identifier: LGPL-3.0-or-later + +#ifndef OPENSHOT_AUDIODEVICEINFO_H +#define OPENSHOT_AUDIODEVICEINFO_H + +#include +#include + +namespace openshot { +/** + * @brief This struct hold information about Audio Devices + * + * The type and name of the audio device. + */ +struct +AudioDeviceInfo { + std::string name; + std::string type; +}; + +using AudioDeviceList = std::vector>; + + /// A class which probes the available audio devices +class AudioDevices +{ +public: + AudioDevices() = default; + + /// Return a vector of std::pair<> objects holding the + /// device name and type for each audio device detected + AudioDeviceList getNames(); +private: + AudioDeviceList m_devices; +}; + +} +#endif diff --git a/src/AudioReaderSource.cpp b/src/AudioReaderSource.cpp index 7a1a96e3..67359a69 100644 --- a/src/AudioReaderSource.cpp +++ b/src/AudioReaderSource.cpp @@ -19,19 +19,14 @@ using namespace std; using namespace openshot; // Constructor that reads samples from a reader -AudioReaderSource::AudioReaderSource( - ReaderBase *audio_reader, int64_t starting_frame_number, int buffer_size -) : - position(0), - size(buffer_size), - buffer(new juce::AudioBuffer(audio_reader->info.channels, buffer_size)), - speed(1), - reader(audio_reader), - frame_number(starting_frame_number), - frame_position(0), - estimated_frame(0) -{ - // Zero the buffer contents +AudioReaderSource::AudioReaderSource(ReaderBase *audio_reader, int64_t starting_frame_number, int buffer_size) + : reader(audio_reader), frame_number(starting_frame_number), + size(buffer_size), position(0), frame_position(0), estimated_frame(0), speed(1) { + + // Initialize an audio buffer (based on reader) + buffer = new juce::AudioBuffer(reader->info.channels, size); + + // initialize the audio samples to zero (silence) buffer->clear(); } @@ -61,7 +56,7 @@ void AudioReaderSource::GetMoreSamplesFromReader() estimated_frame = frame_number; // Init new buffer - juce::AudioBuffer *new_buffer = new juce::AudioSampleBuffer(reader->info.channels, size); + auto *new_buffer = new juce::AudioBuffer(reader->info.channels, size); new_buffer->clear(); // Move the remaining samples into new buffer (if any) @@ -130,7 +125,7 @@ void AudioReaderSource::GetMoreSamplesFromReader() } // Reverse an audio buffer -juce::AudioBuffer* AudioReaderSource::reverse_buffer(juce::AudioSampleBuffer* buffer) +juce::AudioBuffer* AudioReaderSource::reverse_buffer(juce::AudioBuffer* buffer) { int number_of_samples = buffer->getNumSamples(); int channels = buffer->getNumChannels(); @@ -139,7 +134,7 @@ juce::AudioBuffer* AudioReaderSource::reverse_buffer(juce::AudioSampleBuf ZmqLogger::Instance()->AppendDebugMethod("AudioReaderSource::reverse_buffer", "number_of_samples", number_of_samples, "channels", channels); // Reverse array (create new buffer to hold the reversed version) - juce::AudioBuffer *reversed = new juce::AudioSampleBuffer(channels, number_of_samples); + auto *reversed = new juce::AudioBuffer(channels, number_of_samples); reversed->clear(); for (int channel = 0; channel < channels; channel++) @@ -229,7 +224,7 @@ void AudioReaderSource::getNextAudioBlock(const juce::AudioSourceChannelInfo& in } // Prepare to play this audio source -void AudioReaderSource::prepareToPlay(int, double) { } +void AudioReaderSource::prepareToPlay(int, double) {} // Release all resources void AudioReaderSource::releaseResources() { } diff --git a/src/AudioReaderSource.h b/src/AudioReaderSource.h index 2d04d210..32356567 100644 --- a/src/AudioReaderSource.h +++ b/src/AudioReaderSource.h @@ -47,7 +47,7 @@ namespace openshot void GetMoreSamplesFromReader(); /// Reverse an audio buffer (for backwards audio) - juce::AudioBuffer* reverse_buffer(juce::AudioSampleBuffer* buffer); + juce::AudioBuffer* reverse_buffer(juce::AudioBuffer* buffer); public: diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 70797f0d..7043e746 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -45,6 +45,7 @@ add_feature_info("IWYU (include-what-you-use)" ENABLE_IWYU "Scan all source file # Main library sources set(OPENSHOT_SOURCES AudioBufferSource.cpp + AudioDevices.cpp AudioReaderSource.cpp AudioResampler.cpp CacheBase.cpp diff --git a/src/Clip.cpp b/src/Clip.cpp index f5f2724a..7fdd9555 100644 --- a/src/Clip.cpp +++ b/src/Clip.cpp @@ -461,7 +461,7 @@ void Clip::reverse_buffer(juce::AudioBuffer* buffer) int channels = buffer->getNumChannels(); // Reverse array (create new buffer to hold the reversed version) - juce::AudioBuffer *reversed = new juce::AudioSampleBuffer(channels, number_of_samples); + auto *reversed = new juce::AudioBuffer(channels, number_of_samples); reversed->clear(); for (int channel = 0; channel < channels; channel++) @@ -479,7 +479,7 @@ void Clip::reverse_buffer(juce::AudioBuffer* buffer) buffer->addFrom(channel, 0, reversed->getReadPointer(channel), number_of_samples, 1.0f); delete reversed; - reversed = NULL; + reversed = nullptr; } // Adjust the audio and image of a time mapped frame @@ -496,7 +496,7 @@ void Clip::get_time_mapped_frame(std::shared_ptr frame, int64_t frame_num const std::lock_guard lock(getFrameMutex); // create buffer and resampler - juce::AudioBuffer *samples = NULL; + juce::AudioBuffer *samples = nullptr; if (!resampler) resampler = new AudioResampler(); @@ -516,7 +516,7 @@ void Clip::get_time_mapped_frame(std::shared_ptr frame, int64_t frame_num if (time.GetRepeatFraction(frame_number).den > 1) { // SLOWING DOWN AUDIO // Resample data, and return new buffer pointer - juce::AudioBuffer *resampled_buffer = NULL; + juce::AudioBuffer *resampled_buffer = nullptr; // SLOW DOWN audio (split audio) samples = new juce::AudioBuffer(channels, number_of_samples); @@ -548,7 +548,7 @@ void Clip::get_time_mapped_frame(std::shared_ptr frame, int64_t frame_num number_of_samples, 1.0f); // Clean up - resampled_buffer = NULL; + resampled_buffer = nullptr; } else if (abs(delta) > 1 && abs(delta) < 100) { @@ -571,8 +571,8 @@ void Clip::get_time_mapped_frame(std::shared_ptr frame, int64_t frame_num delta_frame <= new_frame_number; delta_frame++) { // buffer to hold detal samples int number_of_delta_samples = GetOrCreateFrame(delta_frame)->GetAudioSamplesCount(); - juce::AudioBuffer *delta_samples = new juce::AudioSampleBuffer(channels, - number_of_delta_samples); + auto *delta_samples = new juce::AudioBuffer(channels, + number_of_delta_samples); delta_samples->clear(); for (int channel = 0; channel < channels; channel++) @@ -591,7 +591,7 @@ void Clip::get_time_mapped_frame(std::shared_ptr frame, int64_t frame_num // Clean up delete delta_samples; - delta_samples = NULL; + delta_samples = nullptr; // Increment start position start += number_of_delta_samples; @@ -615,8 +615,8 @@ void Clip::get_time_mapped_frame(std::shared_ptr frame, int64_t frame_num delta_frame >= new_frame_number; delta_frame--) { // buffer to hold delta samples int number_of_delta_samples = GetOrCreateFrame(delta_frame)->GetAudioSamplesCount(); - juce::AudioBuffer *delta_samples = new juce::AudioSampleBuffer(channels, - number_of_delta_samples); + auto *delta_samples = new juce::AudioBuffer(channels, + number_of_delta_samples); delta_samples->clear(); for (int channel = 0; channel < channels; channel++) @@ -654,7 +654,7 @@ void Clip::get_time_mapped_frame(std::shared_ptr frame, int64_t frame_num frame->AddAudio(true, channel, 0, buffer->getReadPointer(channel), number_of_samples, 1.0f); // Clean up - buffer = NULL; + buffer = nullptr; } else { // Use the samples on this frame (but maybe reverse them if needed) @@ -678,7 +678,7 @@ void Clip::get_time_mapped_frame(std::shared_ptr frame, int64_t frame_num } delete samples; - samples = NULL; + samples = nullptr; } } } diff --git a/src/Qt/AudioPlaybackThread.cpp b/src/Qt/AudioPlaybackThread.cpp index 2cde742e..fc9711b3 100644 --- a/src/Qt/AudioPlaybackThread.cpp +++ b/src/Qt/AudioPlaybackThread.cpp @@ -14,6 +14,12 @@ #include "AudioPlaybackThread.h" #include "Settings.h" +#include "../ReaderBase.h" +#include "../RendererBase.h" +#include "../AudioReaderSource.h" +#include "../AudioDevices.h" +#include "../Settings.h" + #include // for std::this_thread::sleep_for #include // for std::chrono::milliseconds @@ -31,39 +37,46 @@ namespace openshot if (!m_pInstance) { // Create the actual instance of device manager only once m_pInstance = new AudioDeviceManagerSingleton; + auto* mgr = &m_pInstance->audioDeviceManager; - // Get preferred audio device name (if any) - juce::String preferred_audio_device = juce::String(Settings::Instance()->PLAYBACK_AUDIO_DEVICE_NAME.c_str()); + // Get preferred audio device name and type (if any) + auto selected_device = juce::String( + Settings::Instance()->PLAYBACK_AUDIO_DEVICE_NAME); + auto selected_type = juce::String( + Settings::Instance()->PLAYBACK_AUDIO_DEVICE_TYPE); + + if (selected_type.isEmpty() && !selected_device.isEmpty()) { + // Look up type for the selected device + for (const auto t : mgr->getAvailableDeviceTypes()) { + for (const auto n : t->getDeviceNames()) { + if (selected_device.trim().equalsIgnoreCase(n.trim())) { + selected_type = t->getTypeName(); + break; + } + } + if(!selected_type.isEmpty()) + break; + } + } + + if (!selected_type.isEmpty()) + m_pInstance->audioDeviceManager.setCurrentAudioDeviceType(selected_type, true); // Initialize audio device only 1 time juce::String audio_error = m_pInstance->audioDeviceManager.initialise ( - 0, /* number of input channels */ - 2, /* number of output channels */ - 0, /* no XML settings.. */ - true, /* select default device on failure */ - preferred_audio_device /* preferredDefaultDeviceName */); + 0, // number of input channels + 2, // number of output channels + nullptr, // no XML settings.. + true, // select default device on failure + selected_device // preferredDefaultDeviceName + ); // Persist any errors detected if (audio_error.isNotEmpty()) { - m_pInstance->initialise_error = audio_error.toRawUTF8(); + m_pInstance->initialise_error = audio_error.toStdString(); } else { m_pInstance->initialise_error = ""; } - - // Get all audio device names - for (int i = 0; i < m_pInstance->audioDeviceManager.getAvailableDeviceTypes().size(); ++i) - { - const AudioIODeviceType* t = m_pInstance->audioDeviceManager.getAvailableDeviceTypes()[i]; - const juce::StringArray deviceNames = t->getDeviceNames (); - - for (int j = 0; j < deviceNames.size (); ++j ) - { - juce::String deviceName = deviceNames[j]; - juce::String typeName = t->getTypeName(); - openshot::AudioDeviceInfo deviceInfo = {deviceName.toRawUTF8(), typeName.toRawUTF8()}; - m_pInstance->audio_device_names.push_back(deviceInfo); - } - } } return m_pInstance; diff --git a/src/Qt/AudioPlaybackThread.h b/src/Qt/AudioPlaybackThread.h index cd8192f4..ac9819da 100644 --- a/src/Qt/AudioPlaybackThread.h +++ b/src/Qt/AudioPlaybackThread.h @@ -17,8 +17,10 @@ #include "ReaderBase.h" #include "RendererBase.h" #include "AudioReaderSource.h" -#include "AudioDeviceInfo.h" +#include "AudioDevices.h" +#include "AudioReaderSource.h" +#include #include #include #include @@ -26,24 +28,27 @@ namespace openshot { - /** - * @brief Singleton wrapper for AudioDeviceManager (to prevent multiple instances). - */ - class AudioDeviceManagerSingleton { - private: +// Forward decls +class ReaderBase; +class Frame; +class PlayerPrivate; +class QtPlayer; + +/** + * @brief Singleton wrapper for AudioDeviceManager (to prevent multiple instances). + */ +class AudioDeviceManagerSingleton { +private: /// Default constructor (Don't allow user to create an instance of this singleton) AudioDeviceManagerSingleton(){ initialise_error=""; }; /// Private variable to keep track of singleton instance static AudioDeviceManagerSingleton * m_pInstance; - public: +public: /// Error found during JUCE initialise method std::string initialise_error; - /// List of valid audio device names - std::vector audio_device_names; - /// Override with no channels and no preferred audio device static AudioDeviceManagerSingleton * Instance(); @@ -102,22 +107,23 @@ namespace openshot /// Get Speed (The speed and direction to playback a reader (1=normal, 2=fast, 3=faster, -1=rewind, etc...) int getSpeed() const { if (source) return source->getSpeed(); else return 1; } - /// Get Audio Error (if any) - std::string getError() - { - return AudioDeviceManagerSingleton::Instance()->initialise_error; - } + /// Get Audio Error (if any) + std::string getError() + { + return AudioDeviceManagerSingleton::Instance()->initialise_error; + } - /// Get Audio Device Names (if any) - std::vector getAudioDeviceNames() - { - return AudioDeviceManagerSingleton::Instance()->audio_device_names; - }; + /// Get Audio Device Names (if any) + std::vector> getAudioDeviceNames() + { + AudioDevices devs; + return devs.getNames(); + }; friend class PlayerPrivate; friend class QtPlayer; - }; +}; -} +} // namespace openshot #endif // OPENSHOT_AUDIO_PLAYBACK_THREAD_H diff --git a/src/Qt/PlayerPrivate.cpp b/src/Qt/PlayerPrivate.cpp index e09d7415..2cd8c6db 100644 --- a/src/Qt/PlayerPrivate.cpp +++ b/src/Qt/PlayerPrivate.cpp @@ -26,7 +26,7 @@ namespace openshot , audioPlayback(new openshot::AudioPlaybackThread()) , videoPlayback(new openshot::VideoPlaybackThread(rb)) , videoCache(new openshot::VideoCacheThread()) - , speed(1), reader(NULL), last_video_position(1), max_sleep_ms(3000) + , speed(1), reader(NULL), last_video_position(1), max_sleep_ms(125000) { } // Destructor @@ -56,13 +56,13 @@ namespace openshot using std::chrono::duration_cast; // Types for storing time durations in whole and fractional milliseconds - using ms = std::chrono::milliseconds; - using double_ms = std::chrono::duration; - - // Calculate on-screen time for a single frame in milliseconds - const auto frame_duration = double_ms(1000.0 / reader->info.fps.ToDouble()); + using micro_sec = std::chrono::microseconds; + using double_micro_sec = std::chrono::duration; while (!threadShouldExit()) { + // Calculate on-screen time for a single frame in milliseconds + const auto frame_duration = double_micro_sec(1000000.0 / reader->info.fps.ToDouble()); + // Get the start time (to track how long a frame takes to render) const auto time1 = std::chrono::high_resolution_clock::now(); @@ -101,16 +101,16 @@ namespace openshot const auto time2 = std::chrono::high_resolution_clock::now(); // Determine how many milliseconds it took to render the frame - const auto render_time = double_ms(time2 - time1); + const auto render_time = double_micro_sec(time2 - time1); // Calculate the amount of time to sleep (by subtracting the render time) - auto sleep_time = duration_cast(frame_duration - render_time); + auto sleep_time = duration_cast(frame_duration - render_time); // Debug ZmqLogger::Instance()->AppendDebugMethod("PlayerPrivate::run (determine sleep)", "video_frame_diff", video_frame_diff, "video_position", video_position, "audio_position", audio_position, "speed", speed, "render_time(ms)", render_time.count(), "sleep_time(ms)", sleep_time.count()); // Adjust drift (if more than a few frames off between audio and video) - if (video_frame_diff > 0 && reader->info.has_audio && reader->info.has_video) { + if (video_frame_diff > 6 && reader->info.has_audio && reader->info.has_video) { // Since the audio and video threads are running independently, // they will quickly get out of sync. To fix this, we calculate // how far ahead or behind the video frame is, and adjust the amount @@ -118,12 +118,20 @@ namespace openshot // If a frame is ahead of the audio, we sleep for longer. // If a frame is behind the audio, we sleep less (or not at all), // in order for the video to catch up. - sleep_time += duration_cast(video_frame_diff * frame_duration); + sleep_time += duration_cast((video_frame_diff / 2) * frame_duration); } - - else if (video_frame_diff < -10 && reader->info.has_audio && reader->info.has_video) { - // Skip frame(s) to catch up to the audio (if more than 10 frames behind) - video_position += std::fabs(video_frame_diff) / 2; // Seek forward 1/2 the difference + else if (video_frame_diff < -3 && reader->info.has_audio && reader->info.has_video) { + // Video frames are a bit behind, sleep less, we need to display frames more quickly + sleep_time = duration_cast(sleep_time * 0.75); // Sleep a little less + } + else if (video_frame_diff < -9 && reader->info.has_audio && reader->info.has_video) { + // Video frames are very behind, no sleep, we need to display frames more quickly + sleep_time = sleep_time.zero(); // Don't sleep now... immediately go to next position + } + else if (video_frame_diff < -12 && reader->info.has_audio && reader->info.has_video) { + // Video frames are very behind, jump forward the entire distance (catch up with the audio position) + // Skip frame(s) to catch up to the audio + video_position += std::fabs(video_frame_diff); sleep_time = sleep_time.zero(); // Don't sleep now... immediately go to next position } diff --git a/src/Qt/VideoCacheThread.cpp b/src/Qt/VideoCacheThread.cpp index 2c9d8e7b..60d94d1d 100644 --- a/src/Qt/VideoCacheThread.cpp +++ b/src/Qt/VideoCacheThread.cpp @@ -77,10 +77,9 @@ namespace openshot using ms = std::chrono::milliseconds; using double_ms = std::chrono::duration; - // Calculate on-screen time for a single frame in milliseconds - const auto frame_duration = double_ms(1000.0 / reader->info.fps.ToDouble()); - while (!threadShouldExit() && is_playing) { + // Calculate on-screen time for a single frame in milliseconds + const auto frame_duration = double_ms(1000.0 / reader->info.fps.ToDouble()); // Cache frames before the other threads need them // Cache frames up to the max frames. Reset to current position diff --git a/src/QtPlayer.cpp b/src/QtPlayer.cpp index 1b80e78c..04663d6c 100644 --- a/src/QtPlayer.cpp +++ b/src/QtPlayer.cpp @@ -11,15 +11,20 @@ // // SPDX-License-Identifier: LGPL-3.0-or-later +#include "QtPlayer.h" + +#include "AudioDevices.h" #include "Clip.h" #include "FFmpegReader.h" #include "Timeline.h" -#include "QtPlayer.h" #include "Qt/PlayerPrivate.h" #include "Qt/VideoRenderer.h" namespace openshot { + + using AudioDeviceList = std::vector>; + // Delegating constructor QtPlayer::QtPlayer() : QtPlayer::QtPlayer(new VideoRenderer()) @@ -59,12 +64,9 @@ namespace openshot } /// Get Audio Devices from JUCE - std::vector QtPlayer::GetAudioDeviceNames() { - if (reader && threads_started) { - return p->audioPlayback->getAudioDeviceNames(); - } else { - return std::vector(); - } + AudioDeviceList QtPlayer::GetAudioDeviceNames() { + AudioDevices devs; + return devs.getNames(); } void QtPlayer::SetSource(const std::string &source) @@ -219,4 +221,4 @@ namespace openshot void QtPlayer::Volume(float new_volume) { volume = new_volume; } -} \ No newline at end of file +} diff --git a/src/QtPlayer.h b/src/QtPlayer.h index bf179ea1..2cba7927 100644 --- a/src/QtPlayer.h +++ b/src/QtPlayer.h @@ -16,12 +16,15 @@ #include #include + #include "PlayerBase.h" #include "Qt/PlayerPrivate.h" #include "RendererBase.h" namespace openshot { + using AudioDeviceList = std::vector>; + /** * @brief This class is used to playback a video from a reader. * @@ -46,7 +49,7 @@ namespace openshot std::string GetError(); /// Get Audio Devices from JUCE - std::vector GetAudioDeviceNames(); + AudioDeviceList GetAudioDeviceNames(); /// Play the video void Play(); diff --git a/src/Settings.cpp b/src/Settings.cpp index b5c0511e..2eda5ec9 100644 --- a/src/Settings.cpp +++ b/src/Settings.cpp @@ -34,6 +34,7 @@ Settings *Settings::Instance() m_pInstance->HW_DE_DEVICE_SET = 0; m_pInstance->HW_EN_DEVICE_SET = 0; m_pInstance->PLAYBACK_AUDIO_DEVICE_NAME = ""; + m_pInstance->PLAYBACK_AUDIO_DEVICE_TYPE = ""; m_pInstance->DEBUG_TO_STDERR = false; auto env_debug = std::getenv("LIBOPENSHOT_DEBUG"); if (env_debug != nullptr) diff --git a/src/Settings.h b/src/Settings.h index 938a19db..5cbbdf03 100644 --- a/src/Settings.h +++ b/src/Settings.h @@ -85,6 +85,9 @@ namespace openshot { /// The audio device name to use during playback std::string PLAYBACK_AUDIO_DEVICE_NAME = ""; + /// The device type for the playback audio devices + std::string PLAYBACK_AUDIO_DEVICE_TYPE = ""; + /// The current install path of OpenShot (needs to be set when using Timeline(path), since certain /// paths depend on the location of OpenShot transitions and files) std::string PATH_OPENSHOT_INSTALL = ""; diff --git a/src/audio_effects/STFT.cpp b/src/audio_effects/STFT.cpp index ba5d6816..ccc931ed 100644 --- a/src/audio_effects/STFT.cpp +++ b/src/audio_effects/STFT.cpp @@ -29,7 +29,7 @@ void STFT::process(juce::AudioBuffer &block) current_output_buffer_write_position = output_buffer_write_position; current_output_buffer_read_position = output_buffer_read_position; current_samples_since_last_FFT = samples_since_last_FFT; - + for (int sample = 0; sample < num_samples; ++sample) { const float input_sample = channel_data[sample]; @@ -82,7 +82,7 @@ void STFT::updateFftSize(const int new_fft_size) frequency_domain_buffer.realloc(fft_size); frequency_domain_buffer.clear(fft_size); - + input_buffer_write_position = 0; output_buffer_write_position = 0; output_buffer_read_position = 0; @@ -130,7 +130,7 @@ void STFT::updateWindow(const int new_window_type) break; } } - + float window_sum = 0.0f; for (int sample = 0; sample < fft_size; ++sample) window_sum += fft_window[sample]; @@ -189,4 +189,4 @@ void STFT::synthesis(const int channel) current_output_buffer_write_position += hop_size; if (current_output_buffer_write_position >= output_buffer_length) current_output_buffer_write_position = 0; -} \ No newline at end of file +}