Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 7 additions & 8 deletions cmake/copy_resources.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,13 @@ function(copy_resources target plugin_path)
# Windows: Copy DLLs for plugin formats
message(STATUS "Copying Windows runtime DLLs for ${target}")

# Set DLL paths
set(GPAC_DLL "${CMAKE_SOURCE_DIR}/third_party/gpac/lib/Windows/${CMAKE_BUILD_TYPE}/libgpac.dll")
set(CRYPTOMD_DLL "${CMAKE_SOURCE_DIR}/third_party/gpac/lib/Windows/${CMAKE_BUILD_TYPE}/libcryptoMD.dll")
set(LIBSSLMD_DLL "${CMAKE_SOURCE_DIR}/third_party/gpac/lib/Windows/${CMAKE_BUILD_TYPE}/libsslMD.dll")
set(IAMF_TOOLS_DLL "${CMAKE_SOURCE_DIR}/third_party/iamftools/lib/Windows/${CMAKE_BUILD_TYPE}/iamf_tools.dll")
set(OPENSVC_DECODER "${CMAKE_SOURCE_DIR}/third_party/OpenSVC/lib/Windows/${CMAKE_BUILD_TYPE}/OpenSVCDecoder.dll")
string(TOLOWER "${CMAKE_CFG_INTDIR}" ZMQ_CFG)
set(ZMQ_DLL "${CMAKE_BINARY_DIR}/_deps/zeromq-build/bin/${ZMQ_CFG}/libzmq-v143-mt$<$<CONFIG:Debug>:-gd>-4_3_6.dll")
# Set DLL paths using generator expressions for multi-config generators
set(GPAC_DLL "${CMAKE_SOURCE_DIR}/third_party/gpac/lib/Windows/$<CONFIG>/libgpac.dll")
set(CRYPTOMD_DLL "${CMAKE_SOURCE_DIR}/third_party/gpac/lib/Windows/$<CONFIG>/libcryptoMD.dll")
set(LIBSSLMD_DLL "${CMAKE_SOURCE_DIR}/third_party/gpac/lib/Windows/$<CONFIG>/libsslMD.dll")
set(IAMF_TOOLS_DLL "${CMAKE_SOURCE_DIR}/third_party/iamftools/lib/Windows/$<CONFIG>/iamf_tools.dll")
set(OPENSVC_DECODER "${CMAKE_SOURCE_DIR}/third_party/OpenSVC/lib/Windows/$<CONFIG>/OpenSVCDecoder.dll")
set(ZMQ_DLL "${CMAKE_BINARY_DIR}/_deps/zeromq-build/bin/$<CONFIG>/libzmq-v143-mt$<$<CONFIG:Debug>:-gd>-4_3_6.dll")

if("${target}" MATCHES ".*_VST3$")
set(PLUGIN_BINARY_DIR "${plugin_path}/Contents/x86_64-win")
Expand Down
82 changes: 58 additions & 24 deletions common/components/src/AudioFilePlayer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -232,6 +232,7 @@ void AudioFilePlayer::resized() {
}

void AudioFilePlayer::update() {
std::lock_guard<std::mutex> lock(pbeMutex_);
if (playbackEngine_) {
const IAMFFileReader::StreamData kData = playbackEngine_->getStreamData();
const float kDuration_s =
Expand Down Expand Up @@ -265,9 +266,18 @@ void AudioFilePlayer::valueTreePropertyChanged(
triggerAsyncUpdate();
} else if (property == FilePlayback::kPlaybackFile) {
attemptCreatePlaybackEngine();
} else if (property == FileExport::kExportCompleted &&
fer_.get().getExportCompleted()) {
attemptCreatePlaybackEngine();
} else if (property == FileExport::kExportCompleted) {
// When this property is false a new export is starting, so we want to
// destroy the player and wait until export is complete.
// When this property is true we want to attempt to create the playback
// engine again.
if (fer_.get().getExportCompleted()) {
auto safeThis = juce::Component::SafePointer<AudioFilePlayer>(this);
juce::MessageManager::callAsync(
[safeThis]() { safeThis->attemptCreatePlaybackEngine(); });
} else {
cancelCreatePlaybackEngine();
}
}
}

Expand All @@ -293,61 +303,85 @@ void AudioFilePlayer::updateComponentVisibility() {
if (spinner_) spinner_->setVisible(kBuffering);
}

void AudioFilePlayer::cancelCreatePlaybackEngine() {
isBeingDestroyed_ = true;
if (playbackEngineLoaderThread_.joinable()) {
playbackEngineLoaderThread_.join();
}
// Wait for async callback to complete
std::unique_lock<std::mutex> lock(pbeMutex_);
pbeCv_.wait(lock, [this] { return !isLoadingPlaybackEngine_; });
isBeingDestroyed_ = false;
}

void AudioFilePlayer::attemptCreatePlaybackEngine() {
auto playbackState = fer_.get();
const std::filesystem::path kFileToLoad(
playbackState.getExportFile().toStdString());
cancelCreatePlaybackEngine();

// If the file doesn't exist or it's a new file, we set the player to a
// stopped state
auto fe = fer_.get();
const std::filesystem::path kFileToLoad(fe.getExportFile().toStdString());
if (kFileToLoad.empty() || kFileToLoad.extension() != ".iamf" ||
!std::filesystem::exists(kFileToLoad)) {
auto playbackState = fpbr_.get();
playbackState.setPlayState(FilePlayback::kStop);
fpbr_.update(playbackState);
return;
}

auto fpb = fpbr_.get();
fpb.setPlayState(FilePlayback::kBuffering);
fpbr_.update(fpb);

createPlaybackEngine(kFileToLoad);
}

void AudioFilePlayer::createPlaybackEngine(
const std::filesystem::path iamfPath) {
// Join any existing thread before starting a new one
isBeingDestroyed_ = true;
if (playbackEngineLoaderThread_.joinable()) {
playbackEngineLoaderThread_.join();
}
isBeingDestroyed_ = false;

auto playbackState = fpbr_.get();
playbackState.setPlayState(FilePlayback::kBuffering);
fpbr_.update(playbackState);

const juce::String kDevice = fpbr_.get().getPlaybackDevice();

if (playbackEngine_) {
playbackEngine_->stop();
}

playbackEngineLoaderThread_ = std::thread([this, iamfPath, kDevice]() {
isLoadingPlaybackEngine_ = true;

IAMFPlaybackDevice::Result res = IAMFPlaybackDevice::create(
iamfPath, kDevice, isBeingDestroyed_, fpbr_, deviceManager_);

auto safeThis = juce::Component::SafePointer<AudioFilePlayer>(this);

if (isBeingDestroyed_) {
isLoadingPlaybackEngine_ = false;
return;
}
juce::MessageManager::callAsync(
[safeThis, device = res.device.release(), error = res.error]() {
if (safeThis) {
if (safeThis && !safeThis->isBeingDestroyed_) {
safeThis->onPlaybackEngineCreated(IAMFPlaybackDevice::Result{
std::unique_ptr<IAMFPlaybackDevice>(device), error});
} else {
delete device;
}

// Always reset the loading flag and notify waiters
if (safeThis) {
std::lock_guard<std::mutex> lock(safeThis->pbeMutex_);
safeThis->isLoadingPlaybackEngine_ = false;
safeThis->pbeCv_.notify_all();
}
});
});
}

void AudioFilePlayer::onPlaybackEngineCreated(IAMFPlaybackDevice::Result res) {
std::lock_guard<std::mutex> lock(pbeMutex_);
if (!res.device && res.error == IAMFPlaybackDevice::Error::kInvalidIAMFFile) {
// Failed to create playback engine - reset state to disabled
playbackEngine_ = nullptr;
auto fpb = fpbr_.get();
fpb.setPlayState(FilePlayback::kDisabled);
fpbr_.update(fpb);
} else if (res.error == IAMFPlaybackDevice::kEarlyAbortRequested) {
} else if (res.error == IAMFPlaybackDevice::kEarlyAbortRequested ||
isBeingDestroyed_) {
// Do nothing - destruction was requested
playbackEngine_ = nullptr;
} else {
playbackEngine_ = std::move(res.device);
// Update play state from buffering to ready
Expand Down
7 changes: 6 additions & 1 deletion common/components/src/AudioFilePlayer.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
#include <juce_gui_extra/juce_gui_extra.h>

#include <atomic>
#include <condition_variable>
#include <filesystem>
#include <memory>
#include <thread>
Expand Down Expand Up @@ -52,8 +53,9 @@ class AudioFilePlayer : public juce::Component,
class Spinner;

void updateComponentVisibility();
void createPlaybackEngine(const std::filesystem::path iamfPath);
void cancelCreatePlaybackEngine();
void attemptCreatePlaybackEngine();
void createPlaybackEngine(const std::filesystem::path iamfPath);
void onPlaybackEngineCreated(IAMFPlaybackDevice::Result res);

// Components
Expand All @@ -71,5 +73,8 @@ class AudioFilePlayer : public juce::Component,
juce::AudioDeviceManager deviceManager_;
std::unique_ptr<IAMFPlaybackDevice> playbackEngine_;
std::thread playbackEngineLoaderThread_;
std::mutex pbeMutex_;
std::condition_variable pbeCv_;
std::atomic_bool isBeingDestroyed_ = false;
std::atomic_bool isLoadingPlaybackEngine_ = false;
};
2 changes: 1 addition & 1 deletion common/data_structures/src/FileExport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ FileExport::FileExport()
lpcm_sample_size_(24),
sample_tally_(0),
profile_(FileProfile::BASE),
exportCompleted_(false) {}
exportCompleted_(true) {}

FileExport::FileExport(int startTime, int endTime, juce::String exportFile,
juce::String exportFolder,
Expand Down
13 changes: 3 additions & 10 deletions rendererplugin/src/screens/FileExportScreen.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -485,7 +485,6 @@ FileExportScreen::FileExportScreen(MainEditor& editor,
return;
}
config.setManualExport(!config.getManualExport());
repository_->update(config);
if (config.getManualExport()) {
startTimer_.setEnabled(false);
endTimer_.setEnabled(false);
Expand All @@ -501,7 +500,7 @@ FileExportScreen::FileExportScreen(MainEditor& editor,
exportVideoFolder_.setEnabled(false);
browseVideoButton_.setEnabled(false);
browseVideoSourceButton_.setEnabled(false);

config.setExportCompleted(false);
} else {
startTimer_.setEnabled(true);
endTimer_.setEnabled(true);
Expand All @@ -517,15 +516,9 @@ FileExportScreen::FileExportScreen(MainEditor& editor,
exportVideoFolder_.setEnabled(true);
browseVideoButton_.setEnabled(true);
browseVideoSourceButton_.setEnabled(true);

if (!config.getExportCompleted()) {
// Normally this is handled by the file export processor
// But in manual button cases where the processor is destroyed we need
// to flag it here
config.setExportCompleted(true);
repository_->update(config);
}
config.setExportCompleted(true);
}
repository_->update(config);
repaint();
};
LOG_ANALYTICS(RendererProcessor::instanceId_, "FileExportScreen initiated.");
Expand Down