diff --git a/au4/src/appshell/qml/ProjectPage/ProjectPage.qml b/au4/src/appshell/qml/ProjectPage/ProjectPage.qml index 1ee9c98c8cc56a1d56fe4bda39d3ba4c0d16c40f..637fc97b96c54176aea76174199ef15a031e4cd5 100644 --- a/au4/src/appshell/qml/ProjectPage/ProjectPage.qml +++ b/au4/src/appshell/qml/ProjectPage/ProjectPage.qml @@ -148,7 +148,6 @@ DockPage { ] PlaybackToolBar { - orientation: playbackToolBar.orientation floating: playbackToolBar.floating maximumWidth: playbackToolBar.width diff --git a/au4/src/au3wrap/CMakeLists.txt b/au4/src/au3wrap/CMakeLists.txt index 1be7b848dbb5d6e1b5a8d8611d516908bcf6b06f..68cc8f094e85ebb52f2a4641060666692159f7d7 100644 --- a/au4/src/au3wrap/CMakeLists.txt +++ b/au4/src/au3wrap/CMakeLists.txt @@ -26,11 +26,11 @@ set(MODULE_SRC ${CMAKE_CURRENT_LIST_DIR}/au3wrapmodule.h ${CMAKE_CURRENT_LIST_DIR}/wxtypes_convert.h - ${CMAKE_CURRENT_LIST_DIR}/audacity3project.cpp - ${CMAKE_CURRENT_LIST_DIR}/audacity3project.h - ${CMAKE_CURRENT_LIST_DIR}/audacity3playback.cpp - ${CMAKE_CURRENT_LIST_DIR}/audacity3playback.h + ${CMAKE_CURRENT_LIST_DIR}/au3project.cpp + ${CMAKE_CURRENT_LIST_DIR}/au3project.h ${CMAKE_CURRENT_LIST_DIR}/iau3wavepainter.h + ${CMAKE_CURRENT_LIST_DIR}/iau3playback.h + ${CMAKE_CURRENT_LIST_DIR}/iau3audiooutput.h ${CMAKE_CURRENT_LIST_DIR}/mocks/au3settingsmock.cpp ${CMAKE_CURRENT_LIST_DIR}/mocks/au3settingsmock.h @@ -47,6 +47,10 @@ set(MODULE_SRC ${CMAKE_CURRENT_LIST_DIR}/internal/au3wavepainter.h ${CMAKE_CURRENT_LIST_DIR}/internal/WaveformScale.cpp ${CMAKE_CURRENT_LIST_DIR}/internal/WaveformScale.h + ${CMAKE_CURRENT_LIST_DIR}/internal/au3playback.cpp + ${CMAKE_CURRENT_LIST_DIR}/internal/au3playback.h + ${CMAKE_CURRENT_LIST_DIR}/internal/au3audiooutput.cpp + ${CMAKE_CURRENT_LIST_DIR}/internal/au3audiooutput.h ) # ================================== diff --git a/au4/src/au3wrap/audacity3project.cpp b/au4/src/au3wrap/au3project.cpp similarity index 81% rename from au4/src/au3wrap/audacity3project.cpp rename to au4/src/au3wrap/au3project.cpp index ac2826c97be9fd1eea720ab1db2b8b487057ff4d..a81a1b8ee052e60b51ff2f722d0190f5ed071bbe 100644 --- a/au4/src/au3wrap/audacity3project.cpp +++ b/au4/src/au3wrap/au3project.cpp @@ -1,4 +1,4 @@ -#include "audacity3project.h" +#include "au3project.h" #include "libraries/lib-project/Project.h" #include "libraries/lib-project-file-io/ProjectFileIO.h" @@ -35,26 +35,26 @@ static au::processing::TrackType trackType(const Track* track) return au::processing::TrackType::Undefined; } -struct au::au3::Audacity3ProjectData +struct au::au3::Au3ProjectData { std::shared_ptr<AudacityProject> project; AudacityProject& projectRef() { return *project.get(); } }; -Audacity3Project::Audacity3Project() +Au3Project::Au3Project() { - m_data = std::make_shared<Audacity3ProjectData>(); + m_data = std::make_shared<Au3ProjectData>(); } -std::shared_ptr<Audacity3Project> Audacity3Project::create() +std::shared_ptr<Au3Project> Au3Project::create() { - std::shared_ptr<Audacity3Project> p = std::make_shared<Audacity3Project>(); + std::shared_ptr<Au3Project> p = std::make_shared<Au3Project>(); p->m_data->project = AudacityProject::Create(); return p; } -bool Audacity3Project::load(const muse::io::path_t& filePath) +bool Au3Project::load(const muse::io::path_t& filePath) { auto& projectFileIO = ProjectFileIO::Get(m_data->projectRef()); std::string sstr = filePath.toStdString(); @@ -70,7 +70,7 @@ bool Audacity3Project::load(const muse::io::path_t& filePath) return bParseSuccess; } -bool Audacity3Project::save(const muse::io::path_t& filePath, const bool fromSaveAs) +bool Au3Project::save(const muse::io::path_t& filePath, const bool fromSaveAs) { //! TODO AU4 // auto& projectFileIO = ProjectFileIO::Get(m_data->projectRef()); @@ -81,13 +81,13 @@ bool Audacity3Project::save(const muse::io::path_t& filePath, const bool fromSav return false; } -void Audacity3Project::close() +void Au3Project::close() { auto& projectFileIO = ProjectFileIO::Get(m_data->projectRef()); projectFileIO.CloseProject(); } -std::string Audacity3Project::title() const +std::string Au3Project::title() const { if (!m_data->project) { return std::string(); @@ -96,7 +96,7 @@ std::string Audacity3Project::title() const return wxToStdSting(m_data->project->GetProjectName()); } -muse::async::NotifyList<au::processing::Track> Audacity3Project::trackList() const +muse::async::NotifyList<au::processing::Track> Au3Project::trackList() const { muse::async::NotifyList<au::processing::Track> au4tracks; au4tracks.setNotify(m_trackChangedNotifier.notify()); @@ -116,7 +116,7 @@ muse::async::NotifyList<au::processing::Track> Audacity3Project::trackList() con return au4tracks; } -muse::async::NotifyList<au::processing::Clip> Audacity3Project::clipList(const au::processing::TrackId& trackId) const +muse::async::NotifyList<au::processing::Clip> Au3Project::clipList(const au::processing::TrackId& trackId) const { const WaveTrack* waveTrack = DomAccessor::findWaveTrack(m_data->projectRef(), TrackId(trackId)); IF_ASSERT_FAILED(waveTrack) { @@ -134,7 +134,7 @@ muse::async::NotifyList<au::processing::Clip> Audacity3Project::clipList(const a return clips; } -uintptr_t Audacity3Project::au3ProjectPtr() const +uintptr_t Au3Project::au3ProjectPtr() const { return reinterpret_cast<uintptr_t>(m_data->project.get()); } diff --git a/au4/src/au3wrap/audacity3project.h b/au4/src/au3wrap/au3project.h similarity index 68% rename from au4/src/au3wrap/audacity3project.h rename to au4/src/au3wrap/au3project.h index 64612d0af54abb1e15929ba8af1ba85008c70175..bec0d4fb55950b3a8371830dc32111a0dd514cb2 100644 --- a/au4/src/au3wrap/audacity3project.h +++ b/au4/src/au3wrap/au3project.h @@ -1,5 +1,5 @@ -#ifndef AU_AU3WRAP_AUDACITY3PROJECT_H -#define AU_AU3WRAP_AUDACITY3PROJECT_H +#ifndef AU_AU3WRAP_AU3PROJECT_H +#define AU_AU3WRAP_AU3PROJECT_H #include <memory> #include <string> @@ -10,14 +10,14 @@ #include "processing/dom/track.h" namespace au::au3 { -struct Audacity3ProjectData; -class Audacity3Project +struct Au3ProjectData; +class Au3Project { public: - Audacity3Project(); + Au3Project(); - static std::shared_ptr<Audacity3Project> create(); + static std::shared_ptr<Au3Project> create(); bool load(const muse::io::path_t& filePath); bool save(const muse::io::path_t& fileName, const bool fromSaveAs); @@ -32,9 +32,9 @@ public: private: - std::shared_ptr<Audacity3ProjectData> m_data; + std::shared_ptr<Au3ProjectData> m_data; mutable muse::async::ChangedNotifier<processing::Track> m_trackChangedNotifier; }; } -#endif // AU_AU3WRAP_AUDACITY3PROJECT_H +#endif // AU_AU3WRAP_AU3PROJECT_H diff --git a/au4/src/au3wrap/au3wrapmodule.cpp b/au4/src/au3wrap/au3wrapmodule.cpp index 0712bd38544da1fc2dbc6bf5b852376e426e6f12..f297356adb5d3dbed28af84f4191e9ff3cd26552 100644 --- a/au4/src/au3wrap/au3wrapmodule.cpp +++ b/au4/src/au3wrap/au3wrapmodule.cpp @@ -29,12 +29,12 @@ #include "mocks/au3settingsmock.h" +#include "modularity/ioc.h" + #include "internal/wxlogwrap.h" #include "internal/processinginteraction.h" #include "internal/au3wavepainter.h" - -#include "modularity/ioc.h" -#include "audacity3playback.h" +#include "internal/au3playback.h" #include "log.h" @@ -48,9 +48,9 @@ std::string Au3WrapModule::moduleName() const void Au3WrapModule::registerExports() { - m_playback = std::make_shared<Audacity3Playback>(); + m_playback = std::make_shared<Au3Playback>(); - ioc()->registerExport<IAudacity3Playback>(moduleName(), m_playback); + ioc()->registerExport<IAu3Playback>(moduleName(), m_playback); ioc()->registerExport<processing::IProcessingInteraction>(moduleName(), new ProcessingInteraction()); ioc()->registerExport<IAu3WavePainter>(moduleName(), new Au3WavePainter()); } @@ -69,6 +69,8 @@ void Au3WrapModule::onInit(const muse::IApplication::RunMode&) if (!ok) { LOGE() << "failed init sql"; } + + m_playback->init(); } void Au3WrapModule::onDeinit() diff --git a/au4/src/au3wrap/au3wrapmodule.h b/au4/src/au3wrap/au3wrapmodule.h index 55e2e963f07ef4450eb6f08e537ae000ac2c7a31..22b73ecf110ab38100770a19300ad2360171ee68 100644 --- a/au4/src/au3wrap/au3wrapmodule.h +++ b/au4/src/au3wrap/au3wrapmodule.h @@ -26,7 +26,7 @@ namespace au::au3 { class WxLogWrap; -class Audacity3Playback; +class Au3Playback; class Au3WrapModule : public muse::modularity::IModuleSetup { public: @@ -40,7 +40,7 @@ private: WxLogWrap* m_wxLog = nullptr; - std::shared_ptr<Audacity3Playback> m_playback; + std::shared_ptr<Au3Playback> m_playback; }; } diff --git a/au4/src/au3wrap/iau3audiooutput.h b/au4/src/au3wrap/iau3audiooutput.h new file mode 100644 index 0000000000000000000000000000000000000000..0518e7b841ba1637bb9b3d0de1fb2c1e598b89e8 --- /dev/null +++ b/au4/src/au3wrap/iau3audiooutput.h @@ -0,0 +1,31 @@ +/* +* Audacity: A Digital Audio Editor +*/ + +#ifndef AU_AU3WRAP_IAUDACITYAUDIOOUTPUT_H +#define AU_AU3WRAP_IAUDACITYAUDIOOUTPUT_H + +#include <memory> + +#include "global/async/promise.h" +#include "global/async/channel.h" + +#include "playback/audiotypes.h" + +namespace au::au3 { +class IAu3AudioOutput +{ +public: + virtual ~IAu3AudioOutput() = default; + + virtual muse::async::Promise<float> playbackVolume() const = 0; + virtual void setPlaybackVolume(float volume) = 0; + virtual muse::async::Channel<float> playbackVolumeChanged() const = 0; + + virtual muse::async::Promise<au::audio::AudioSignalChanges> playbackSignalChanges() const = 0; +}; + +using IAu3AudioOutputPtr = std::shared_ptr<IAu3AudioOutput>; +} + +#endif // AU_AU3WRAP_IAUDACITYAUDIOOUTPUT_H diff --git a/au4/src/au3wrap/iaudacity3playback.h b/au4/src/au3wrap/iau3playback.h similarity index 68% rename from au4/src/au3wrap/iaudacity3playback.h rename to au4/src/au3wrap/iau3playback.h index 2ccd6563b7496ec8bf233169e6c39fe10e5a7bfd..d4999b5dd2773676697ada7207b76ca9c85dea13 100644 --- a/au4/src/au3wrap/iaudacity3playback.h +++ b/au4/src/au3wrap/iau3playback.h @@ -1,8 +1,8 @@ /* * Audacity: A Digital Audio Editor */ -#ifndef AU_AU3WRAP_IAUDACITY3PLAYBACK_H -#define AU_AU3WRAP_IAUDACITY3PLAYBACK_H +#ifndef AU_AU3WRAP_IAU3PLAYBACK_H +#define AU_AU3WRAP_IAU3PLAYBACK_H #include "global/async/promise.h" #include "global/async/channel.h" @@ -11,14 +11,14 @@ #include "playback/audiotypes.h" -class AudacityProject; +#include "iau3audiooutput.h" namespace au::au3 { -class IAudacity3Playback : MODULE_EXPORT_INTERFACE +class IAu3Playback : MODULE_EXPORT_INTERFACE { - INTERFACE_ID(IAudacity3Playback) + INTERFACE_ID(IAu3Playback) public: - virtual ~IAudacity3Playback() = default; + virtual ~IAu3Playback() = default; virtual void play() = 0; virtual void seek(const audio::msecs_t newPositionMsecs) = 0; @@ -32,8 +32,10 @@ public: virtual muse::async::Channel<audio::msecs_t> playbackPositionMsecs() const = 0; virtual muse::async::Channel<audio::PlaybackStatus> playbackStatusChanged() const = 0; + + virtual std::shared_ptr<IAu3AudioOutput> audioOutput() const = 0; }; -using IAudacity3PlaybackPtr = std::shared_ptr<IAudacity3Playback>; +using IAu3PlaybackPtr = std::shared_ptr<IAu3Playback>; } -#endif // AU_AU3WRAP_IAUDACITY3PLAYBACK_H +#endif // AU_AU3WRAP_IAU3PLAYBACK_H diff --git a/au4/src/au3wrap/internal/au3audiooutput.cpp b/au4/src/au3wrap/internal/au3audiooutput.cpp new file mode 100644 index 0000000000000000000000000000000000000000..bded98ce0f94c589332d33f29c40ce2b515273e8 --- /dev/null +++ b/au4/src/au3wrap/internal/au3audiooutput.cpp @@ -0,0 +1,59 @@ +/* +* Audacity: A Digital Audio Editor +*/ + +#include "au3audiooutput.h" + +#include "types/ret.h" +#include "global/async/async.h" + +#include "libraries/lib-audio-io/AudioIO.h" + +#include "log.h" + +using namespace muse; +using namespace muse::async; +using namespace au::au3; + +muse::async::Promise<au::audio::volume_dbfs_t> Au3AudioOutput::playbackVolume() const +{ + return muse::async::Promise<float>([](auto resolve, auto /*reject*/) { + float inputVolume; + float outputVolume; + int inputSource; + + auto gAudioIO = AudioIO::Get(); + gAudioIO->GetMixer(&inputSource, &inputVolume, &outputVolume); + + return resolve(outputVolume); + }); +} + +void Au3AudioOutput::setPlaybackVolume(float volume) +{ + muse::async::Async::call(this, [this, volume]() { + float inputVolume; + float outputVolume; + int inputSource; + + auto gAudioIO = AudioIO::Get(); + gAudioIO->GetMixer(&inputSource, &inputVolume, &outputVolume); + + gAudioIO->SetMixer(inputSource, inputVolume, volume); + + m_playbackVolumeChanged.send(volume); + }); +} + +muse::async::Channel<au::audio::volume_dbfs_t> Au3AudioOutput::playbackVolumeChanged() const +{ + return m_playbackVolumeChanged; +} + +muse::async::Promise<au::audio::AudioSignalChanges> Au3AudioOutput::playbackSignalChanges() const +{ + return muse::async::Promise<audio::AudioSignalChanges>([](auto, auto reject) { + muse::Ret ret = make_ret(muse::Ret::Code::NotImplemented); + return reject(ret.code(), ret.text()); + }); +} diff --git a/au4/src/au3wrap/internal/au3audiooutput.h b/au4/src/au3wrap/internal/au3audiooutput.h new file mode 100644 index 0000000000000000000000000000000000000000..76e60f0408b883b35101b7367420460995414e0c --- /dev/null +++ b/au4/src/au3wrap/internal/au3audiooutput.h @@ -0,0 +1,27 @@ +/* +* Audacity: A Digital Audio Editor +*/ + +#ifndef AU_AU3WRAP_AU3AUDIOOUTPUT_H +#define AU_AU3WRAP_AU3AUDIOOUTPUT_H + +#include "global/async/asyncable.h" + +#include "../iau3audiooutput.h" + +namespace au::au3 { +class Au3AudioOutput : public IAu3AudioOutput, public muse::async::Asyncable +{ +public: + muse::async::Promise<float> playbackVolume() const override; + void setPlaybackVolume(float volume) override; + muse::async::Channel<float> playbackVolumeChanged() const override; + + muse::async::Promise<au::audio::AudioSignalChanges> playbackSignalChanges() const override; + +private: + mutable muse::async::Channel<float> m_playbackVolumeChanged; +}; +} + +#endif // AU_AU3WRAP_AU3AUDIOOUTPUT_H diff --git a/au4/src/au3wrap/audacity3playback.cpp b/au4/src/au3wrap/internal/au3playback.cpp similarity index 89% rename from au4/src/au3wrap/audacity3playback.cpp rename to au4/src/au3wrap/internal/au3playback.cpp index 5da201f0ee388351e3dc63a9e26f005c07ce8f05..eb77d3b827b88ca2140e4763d0c0d38b20582984 100644 --- a/au4/src/au3wrap/audacity3playback.cpp +++ b/au4/src/au3wrap/internal/au3playback.cpp @@ -1,7 +1,7 @@ /* * Audacity: A Digital Audio Editor */ -#include "audacity3playback.h" +#include "au3playback.h" #include "libraries/lib-time-frequency-selection/SelectedRegion.h" #include "libraries/lib-track/Track.h" @@ -14,11 +14,18 @@ #include "wxtypes_convert.h" +#include "internal/au3audiooutput.h" + #include "log.h" using namespace au::au3; -void Audacity3Playback::play() +void Au3Playback::init() +{ + m_audioOutputPtr = std::make_shared<Au3AudioOutput>(); +} + +void Au3Playback::play() { //! NOTE: copied from ProjectAudioManager::PlayPlayRegion @@ -179,7 +186,7 @@ void Audacity3Playback::play() } } -void Audacity3Playback::seek(const audio::msecs_t newPositionMsecs) +void Au3Playback::seek(const audio::msecs_t newPositionMsecs) { AudacityProject& project = projectRef(); @@ -187,7 +194,7 @@ void Audacity3Playback::seek(const audio::msecs_t newPositionMsecs) playRegion.SetStart(newPositionMsecs); } -void Audacity3Playback::stop() +void Au3Playback::stop() { //! NOTE: copied from ProjectAudioManager::Stop bool stopStream = true; @@ -210,7 +217,7 @@ void Audacity3Playback::stop() gAudioIO->SetPaused(false); } -void Audacity3Playback::pause() +void Au3Playback::pause() { if (!canStopAudioStream()) { return; @@ -221,7 +228,7 @@ void Audacity3Playback::pause() gAudioIO->SetPaused(true); } -void Audacity3Playback::resume() +void Au3Playback::resume() { if (!canStopAudioStream()) { return; @@ -232,13 +239,13 @@ void Audacity3Playback::resume() gAudioIO->SetPaused(false); } -void Audacity3Playback::setDuration(const audio::msecs_t durationMsec) +void Au3Playback::setDuration(const audio::msecs_t durationMsec) { UNUSED(durationMsec); NOT_IMPLEMENTED; } -muse::async::Promise<bool> Audacity3Playback::setLoop(const audio::msecs_t fromMsec, const audio::msecs_t toMsec) +muse::async::Promise<bool> Au3Playback::setLoop(const audio::msecs_t fromMsec, const audio::msecs_t toMsec) { UNUSED(fromMsec); UNUSED(toMsec); @@ -250,27 +257,32 @@ muse::async::Promise<bool> Audacity3Playback::setLoop(const audio::msecs_t fromM }); } -void Audacity3Playback::resetLoop() +void Au3Playback::resetLoop() { } -muse::async::Channel<au::audio::msecs_t> Audacity3Playback::playbackPositionMsecs() const +muse::async::Channel<au::audio::msecs_t> Au3Playback::playbackPositionMsecs() const { return m_playbackPositionMsecsChanged; } -muse::async::Channel<au::audio::PlaybackStatus> Audacity3Playback::playbackStatusChanged() const +muse::async::Channel<au::audio::PlaybackStatus> Au3Playback::playbackStatusChanged() const { return m_playbackStatusChanged; } -AudacityProject& Audacity3Playback::projectRef() const +IAu3AudioOutputPtr Au3Playback::audioOutput() const +{ + return m_audioOutputPtr; +} + +AudacityProject& Au3Playback::projectRef() const { AudacityProject* project = reinterpret_cast<AudacityProject*>(globalContext()->currentProject()->au3ProjectPtr()); return *project; } -bool Audacity3Playback::canStopAudioStream() const +bool Au3Playback::canStopAudioStream() const { auto gAudioIO = AudioIO::Get(); AudacityProject& project = projectRef(); @@ -279,7 +291,7 @@ bool Audacity3Playback::canStopAudioStream() const || gAudioIO->GetOwningProject().get() == &project; } -TransportSequences Audacity3Playback::makeTransportTracks(TrackList& trackList, bool selectedOnly, bool nonWaveToo) +TransportSequences Au3Playback::makeTransportTracks(TrackList& trackList, bool selectedOnly, bool nonWaveToo) { TransportSequences result; { diff --git a/au4/src/au3wrap/audacity3playback.h b/au4/src/au3wrap/internal/au3playback.h similarity index 78% rename from au4/src/au3wrap/audacity3playback.h rename to au4/src/au3wrap/internal/au3playback.h index dd998d893e850f8a20d928db1d06cc85bf678125..8a9ca9a636b2fd4846ca3e4eb4843c873757fce9 100644 --- a/au4/src/au3wrap/audacity3playback.h +++ b/au4/src/au3wrap/internal/au3playback.h @@ -1,8 +1,8 @@ /* * Audacity: A Digital Audio Editor */ -#ifndef AU_AU3WRAP_AUDACITY3PLAYBACK_H -#define AU_AU3WRAP_AUDACITY3PLAYBACK_H +#ifndef AU_AU3WRAP_AU3PLAYBACK_H +#define AU_AU3WRAP_AU3PLAYBACK_H #include "async/asyncable.h" @@ -10,18 +10,21 @@ #include "actions/iactionsdispatcher.h" #include "context/iglobalcontext.h" -#include "iaudacity3playback.h" +#include "iau3playback.h" +#include "iau3audiooutput.h" class AudacityProject; class TrackList; struct TransportSequences; namespace au::au3 { -class Audacity3Playback : public IAudacity3Playback, public muse::async::Asyncable +class Au3Playback : public IAu3Playback, public muse::async::Asyncable { muse::Inject<au::context::IGlobalContext> globalContext; public: + void init(); + void play() override; void seek(const audio::msecs_t newPositionMsecs) override; void stop() override; @@ -35,6 +38,8 @@ public: muse::async::Channel<audio::msecs_t> playbackPositionMsecs() const override; muse::async::Channel<audio::PlaybackStatus> playbackStatusChanged() const override; + IAu3AudioOutputPtr audioOutput() const override; + private: AudacityProject& projectRef() const; @@ -43,7 +48,9 @@ private: mutable muse::async::Channel<audio::msecs_t> m_playbackPositionMsecsChanged; mutable muse::async::Channel<audio::PlaybackStatus> m_playbackStatusChanged; + + IAu3AudioOutputPtr m_audioOutputPtr; }; } -#endif // AU_AU3WRAP_AUDACITY3PLAYBACK_H +#endif // AU_AU3WRAP_AU3PLAYBACK_H diff --git a/au4/src/playback/CMakeLists.txt b/au4/src/playback/CMakeLists.txt index fa21a273bc4644274fbb16254b4564f0a09dbeae..72dc27b16f76495852803944c49542c7875a2978 100644 --- a/au4/src/playback/CMakeLists.txt +++ b/au4/src/playback/CMakeLists.txt @@ -22,10 +22,15 @@ set(MODULE_SRC ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarmodel.cpp ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarmodel.h - ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarcustomisemodel.h + ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarabstractitem.cpp + ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarabstractitem.h + ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarlevelitem.cpp + ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarlevelitem.h + ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarcustomisemodel.cpp - ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarcustomiseitem.h + ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarcustomisemodel.h ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarcustomiseitem.cpp + ${CMAKE_CURRENT_LIST_DIR}/view/toolbars/playbacktoolbarcustomiseitem.h ) diff --git a/au4/src/playback/internal/playbackcontroller.cpp b/au4/src/playback/internal/playbackcontroller.cpp index 6a0ea48263052b385ba56752a534d06b0c51ef22..b32fedac43f0ef78aaee5cb0e436101193f3a188 100644 --- a/au4/src/playback/internal/playbackcontroller.cpp +++ b/au4/src/playback/internal/playbackcontroller.cpp @@ -13,7 +13,7 @@ using namespace muse::actions; static const ActionCode PLAY_CODE("play"); static const ActionCode STOP_CODE("stop"); -static const ActionCode REWIND_CODE("rewind"); +static const ActionCode REWIND_START_CODE("rewind-start"); static const ActionCode LOOP_CODE("loop"); static const ActionCode LOOP_IN_CODE("loop-in"); static const ActionCode LOOP_OUT_CODE("loop-out"); @@ -31,7 +31,7 @@ void PlaybackController::init() { dispatcher()->reg(this, PLAY_CODE, this, &PlaybackController::togglePlay); dispatcher()->reg(this, STOP_CODE, this, &PlaybackController::pause); - dispatcher()->reg(this, REWIND_CODE, this, &PlaybackController::rewind); + dispatcher()->reg(this, REWIND_START_CODE, this, &PlaybackController::rewindToStart); dispatcher()->reg(this, LOOP_CODE, this, &PlaybackController::toggleLoopPlayback); // dispatcher()->reg(this, LOOP_IN_CODE, [this]() { addLoopBoundary(LoopBoundaryType::LoopIn); }); // dispatcher()->reg(this, LOOP_OUT_CODE, [this]() { addLoopBoundary(LoopBoundaryType::LoopOut); }); @@ -187,7 +187,7 @@ void PlaybackController::play() setCurrentPlaybackStatus(PlaybackStatus::Running); } -void PlaybackController::rewind(const ActionData& args) +void PlaybackController::rewindToStart(const ActionData& args) { //! NOTE: In Audacity 3 we can't rewind while playing stop(); diff --git a/au4/src/playback/internal/playbackcontroller.h b/au4/src/playback/internal/playbackcontroller.h index c132abd12294cefa8fbbb5402256fb84c6792708..03e08218202533bf083b75b89c64bfd46351c74c 100644 --- a/au4/src/playback/internal/playbackcontroller.h +++ b/au4/src/playback/internal/playbackcontroller.h @@ -11,7 +11,7 @@ #include "context/iglobalcontext.h" #include "iinteractive.h" -#include "au3wrap/iaudacity3playback.h" +#include "au3wrap/iau3playback.h" #include "../iplaybackcontroller.h" @@ -21,7 +21,7 @@ class PlaybackController : public IPlaybackController, public muse::actions::Act INJECT_STATIC(muse::actions::IActionsDispatcher, dispatcher) INJECT_STATIC(au::context::IGlobalContext, globalContext) INJECT_STATIC(muse::IInteractive, interactive) - INJECT_STATIC(au3::IAudacity3Playback, au3Playback) + INJECT_STATIC(au3::IAu3Playback, au3Playback) public: void init(); @@ -70,7 +70,7 @@ private: void seekRangeSelection(); void togglePlay(); - void rewind(const muse::actions::ActionData& args); + void rewindToStart(const muse::actions::ActionData& args); void play(); void pause(); void stop(); diff --git a/au4/src/playback/internal/playbackuiactions.cpp b/au4/src/playback/internal/playbackuiactions.cpp index 2bc35468ba7ce8043829c3797b3af9a4832dc0af..8dd454030ed2e5d36a0ccb68b993f189b5e083dc 100644 --- a/au4/src/playback/internal/playbackuiactions.cpp +++ b/au4/src/playback/internal/playbackuiactions.cpp @@ -19,7 +19,7 @@ const UiActionList PlaybackUiActions::m_mainActions = { au::context::CTX_NOTATION_FOCUSED, TranslatableString("action", "Play"), TranslatableString("action", "Play"), - IconCode::Code::PLAY + IconCode::Code::PLAY_FILL ), UiAction("stop", au::context::UiCtxNotationOpened, @@ -28,12 +28,19 @@ const UiActionList PlaybackUiActions::m_mainActions = { TranslatableString("action", "Stop playback"), IconCode::Code::STOP ), - UiAction("rewind", + UiAction("rewind-start", au::context::UiCtxNotationOpened, au::context::CTX_NOTATION_FOCUSED, - TranslatableString("action", "Rewind"), - TranslatableString("action", "Rewind"), - IconCode::Code::REWIND + TranslatableString("action", "Rewind to start"), + TranslatableString("action", "Rewind to start"), + IconCode::Code::REWIND_START_FILL + ), + UiAction("rewind-end", + au::context::UiCtxNotationOpened, + au::context::CTX_NOTATION_FOCUSED, + TranslatableString("action", "Rewind to end"), + TranslatableString("action", "Rewind to end"), + IconCode::Code::REWIND_END_FILL ), UiAction("loop", au::context::UiCtxNotationOpened, @@ -43,13 +50,34 @@ const UiActionList PlaybackUiActions::m_mainActions = { IconCode::Code::LOOP, Checkable::Yes ), - UiAction("playback-setup", + UiAction("audio-setup", au::context::UiCtxNotationOpened, au::context::CTX_NOTATION_FOCUSED, - TranslatableString("action", "Playback setup"), - TranslatableString("action", "Open playback setup dialog"), - IconCode::Code::NONE - ) + TranslatableString("action", "Audio setup"), + TranslatableString("action", "Open audio setup dialog"), + IconCode::Code::CONFIGURE + ), + UiAction("playback-level", + au::context::UiCtxNotationOpened, + au::context::CTX_NOTATION_FOCUSED, + TranslatableString("action", "Playback level"), + TranslatableString("action", "Set playback level"), + IconCode::Code::AUDIO // todo + ), + UiAction("playback-time", + au::context::UiCtxNotationOpened, + au::context::CTX_NOTATION_FOCUSED, + TranslatableString("action", "Playback time"), + TranslatableString("action", "Set playback time"), + IconCode::Code::AUDIO // todo + ), + UiAction("record", + au::context::UiCtxNotationOpened, + au::context::CTX_NOTATION_FOCUSED, + TranslatableString("action", "Record"), + TranslatableString("action", "Record"), + IconCode::Code::RECORD_FILL + ), }; const UiActionList PlaybackUiActions::m_settingsActions = { @@ -164,11 +192,24 @@ const muse::ui::ToolConfig& PlaybackUiActions::defaultPlaybackToolConfig() static ToolConfig config; if (!config.isValid()) { config.items = { - { "rewind", true }, { "play", true }, + { "rewind-start", true }, + { "rewind-end", true }, + { "record", true }, { "loop", true }, - { "loop-in", true }, - { "loop-out", true }, + { "", true }, + { "envelope", true }, + { "zoomin", true }, + { "zoomout", true }, + { "fit-selection", true }, + { "fit-project", true }, + { "zoom", true }, + { "trim-audio-outside-selection", true }, + { "silence-audio-selection", true }, + { "", true }, + { "playback-time", true }, + { "", true }, + { "playback-level", true } }; } return config; diff --git a/au4/src/playback/playback.qrc b/au4/src/playback/playback.qrc index e4ac2169f5f8c8a28abec4aaa485fed62c0f3ccf..42ec2b460b3c65afdce5ec3036d7fdc2a66ffb3a 100644 --- a/au4/src/playback/playback.qrc +++ b/au4/src/playback/playback.qrc @@ -6,5 +6,7 @@ <file>qml/Audacity/Playback/internal/PlaybackToolBarActionDelegate.qml</file> <file>qml/Audacity/Playback/internal/PlaybackToolBarCustomisePopup.qml</file> <file>qml/Audacity/Playback/qmldir</file> + <file>qml/Audacity/Playback/internal/PlaybackLevel.qml</file> + <file>qml/Audacity/Playback/internal/VolumeSlider.qml</file> </qresource> </RCC> diff --git a/au4/src/playback/playbackmodule.cpp b/au4/src/playback/playbackmodule.cpp index 1a0c12ae529fb513c9969e1d921e3ff71f875ec5..92f49c7ed164b5215a4caaf631449edc8eb86e01 100644 --- a/au4/src/playback/playbackmodule.cpp +++ b/au4/src/playback/playbackmodule.cpp @@ -14,6 +14,7 @@ #include "internal/playbackuiactions.h" #include "view/toolbars/playbacktoolbarmodel.h" +#include "view/toolbars/playbacktoolbarabstractitem.h" #include "view/toolbars/playbacktoolbarcustomisemodel.h" #include "view/toolbars/playbacktoolbarcustomiseitem.h" @@ -58,6 +59,7 @@ void PlaybackModule::registerUiTypes() { // toolbars qmlRegisterType<PlaybackToolBarModel>("Audacity.Playback", 1, 0, "PlaybackToolBarModel"); + qmlRegisterUncreatableType<PlaybackToolBarAbstractItem>("Muse.Ui", 1, 0, "PlaybackToolBarItem", "Cannot create an PlaybackToolBarItem"); qmlRegisterType<PlaybackToolBarCustomiseModel>("Audacity.Playback", 1, 0, "PlaybackToolBarCustomiseModel"); qmlRegisterUncreatableType<PlaybackToolBarCustomiseItem>("Audacity.Playback", 1, 0, "PlaybackToolBarCustomiseItem", "Cannot create"); diff --git a/au4/src/playback/qml/Audacity/Playback/PlaybackToolBar.qml b/au4/src/playback/qml/Audacity/Playback/PlaybackToolBar.qml index bebe85ddcd268c323611357148748da280e36466..cf17b7f5730b04d09d74049e52f97b0050affbf8 100644 --- a/au4/src/playback/qml/Audacity/Playback/PlaybackToolBar.qml +++ b/au4/src/playback/qml/Audacity/Playback/PlaybackToolBar.qml @@ -13,15 +13,13 @@ import "internal" Item { id: root - property alias orientation: gridView.orientation - property bool floating: false property int maximumWidth: 0 property int maximumHeight: 0 - width: gridView.isHorizontal ? childrenRect.width : 76 - height: !gridView.isHorizontal ? childrenRect.height : 40 + width: gridView.width + /*spacing*/ 4 + customizeButton.width + height: gridView.height property NavigationPanel navigationPanel: NavigationPanel { name: "PlaybackToolBar" @@ -33,178 +31,206 @@ Item { id: toolbarModel } - QtObject { - id: prv - - function resolveHorizontalGridViewWidth() { - if (root.floating) { - return gridView.contentWidth - } - - var requiredFreeSpace = gridView.cellWidth * 3 + gridView.rowSpacing * 4 - - if (root.maximumWidth - gridView.contentWidth < requiredFreeSpace) { - return gridView.contentWidth - requiredFreeSpace - } - - return gridView.contentWidth - } - - function resolveVerticalGridViewHeight() { - if (root.floating) { - return gridView.contentHeight - } - - var requiredFreeSpace = gridView.cellHeight * 3 + gridView.rowSpacing * 4 - - if (root.maximumHeight - gridView.contentHeight < requiredFreeSpace) { - return gridView.contentHeight - requiredFreeSpace - } - - return gridView.contentHeight - } - } - Component.onCompleted: { toolbarModel.load() } - GridViewSectional { + Flow { id: gridView - sectionRole: "section" - - rowSpacing: 4 - columnSpacing: 4 - - cellWidth: 32 - cellHeight: cellWidth - clip: true - model: toolbarModel + spacing: 4 - sectionDelegate: SeparatorLine { - orientation: gridView.orientation - visible: itemIndex !== 0 - } - - itemDelegate: FlatButton { - id: btn - - property var item: Boolean(itemModel) ? itemModel.itemRole : null - property var hasMenu: Boolean(item) && item.subitems.length !== 0 + Repeater { + model: toolbarModel - width: gridView.cellWidth - height: gridView.cellWidth + Loader { + id: loader - accentButton: (Boolean(item) && item.checked) || menuLoader.isMenuOpened - transparent: !accentButton + property var itemData: Boolean(model) ? model.item : null + property var itemOrder: Boolean(model) ? model.order : 0 + property var itemIsMenuSecondary: Boolean(model) ? model.isMenuSecondary : false - icon: Boolean(item) ? item.icon : IconCode.NONE - iconFont: ui.theme.toolbarIconsFont + property var itemSize: { + if (!Boolean(loader.itemData)) { + return null + } - toolTipTitle: Boolean(item) ? item.title : "" - toolTipDescription: Boolean(item) ? item.description : "" - toolTipShortcut: Boolean(item) ? item.shortcuts : "" + switch(loader.itemData.type) { + case PlaybackToolBarItem.SECTION: return Qt.size(1, 32) + case PlaybackToolBarItem.ACTION: return Qt.size(32, 32) + case PlaybackToolBarItem.PLAYBACK_LEVEL: return Qt.size(128, 32) + } - navigation.panel: root.navigationPanel - navigation.name: Boolean(item) ? item.id : "" - navigation.order: Boolean(itemModel) ? itemModel.order : 0 - isClickOnKeyNavTriggered: false - navigation.onTriggered: { - if (menuLoader.isMenuOpened || hasMenu) { - toggleMenuOpened() - } else { - handleMenuItem() + return null } - } - mouseArea.acceptedButtons: hasMenu && itemModel.isMenuSecondary - ? Qt.LeftButton | Qt.RightButton - : Qt.LeftButton + width: Boolean(item) ? item.width : 0 + height: 32 - function toggleMenuOpened() { - menuLoader.toggleOpened(item.subitems) - } - - function handleMenuItem() { - Qt.callLater(toolbarModel.handleMenuItem, item.id) - } + sourceComponent: { + if (!Boolean(loader.itemData)) { + return null + } - onClicked: function(mouse) { - if (menuLoader.isMenuOpened // If already menu open, close it - || (hasMenu // Or if can open menu - && (!itemModel.isMenuSecondary // And _should_ open menu - || mouse.button === Qt.RightButton))) { - toggleMenuOpened() - return - } + switch(loader.itemData.type) { + case PlaybackToolBarItem.SECTION: return sectionComp + case PlaybackToolBarItem.ACTION: return actionComp + case PlaybackToolBarItem.PLAYBACK_LEVEL: return playbackLevelComp + } - if (mouse.button === Qt.LeftButton) { - handleMenuItem() + return null } - } - Connections { - target: btn.mouseArea + Component { + id: sectionComp - // Make sure we only connect to `pressAndHold` if necessary - // See https://github.com/musescore/MuseScore/issues/16012 - enabled: btn.hasMenu && !menuLoader.isMenuOpened - - function onPressAndHold() { - if (menuLoader.isMenuOpened || !btn.hasMenu) { - return + SeparatorLine { + orientation: Qt.Vertical } - - btn.toggleMenuOpened() } - } - Canvas { - visible: Boolean(itemModel) && itemModel.isMenuSecondary - - property color fillColor: ui.theme.fontPrimaryColor - onFillColorChanged: { - requestPaint() + Component { + id: actionComp + + FlatButton { + id: btn + + property var item: loader.itemData + property int order: loader.itemOrder + property var isMenuSecondary: loader.itemIsMenuSecondary + + property var hasMenu: Boolean(item) && item.subitems.length !== 0 + + width: 32 + height: width + + accentButton: item.checked || menuLoader.isMenuOpened + transparent: !accentButton + + icon: item.icon + iconFont: ui.theme.toolbarIconsFont + + toolTipTitle: item.title + toolTipDescription: item.description + toolTipShortcut: item.shortcuts + + navigation.panel: root.navigationPanel + navigation.name: item.id + navigation.order: order + isClickOnKeyNavTriggered: false + navigation.onTriggered: { + if (menuLoader.isMenuOpened || hasMenu) { + toggleMenuOpened() + } else { + handleMenuItem() + } + } + + mouseArea.acceptedButtons: hasMenu && isMenuSecondary + ? Qt.LeftButton | Qt.RightButton + : Qt.LeftButton + + function toggleMenuOpened() { + menuLoader.toggleOpened(item.subitems) + } + + function handleMenuItem() { + Qt.callLater(toolbarModel.handleMenuItem, item.id) + } + + onClicked: function(mouse) { + if (menuLoader.isMenuOpened // If already menu open, close it + || (hasMenu // Or if can open menu + && (!isMenuSecondary // And _should_ open menu + || mouse.button === Qt.RightButton))) { + toggleMenuOpened() + return + } + + if (mouse.button === Qt.LeftButton) { + handleMenuItem() + } + } + + Connections { + target: btn.mouseArea + + enabled: btn.hasMenu && !menuLoader.isMenuOpened + + function onPressAndHold() { + if (menuLoader.isMenuOpened || !btn.hasMenu) { + return + } + + btn.toggleMenuOpened() + } + } + + Canvas { + visible: isMenuSecondary + + property color fillColor: ui.theme.fontPrimaryColor + onFillColorChanged: { + requestPaint() + } + + width: 4 + height: 4 + + anchors.margins: 2 + anchors.right: parent.right + anchors.bottom: parent.bottom + + onPaint: { + const ctx = getContext("2d"); + ctx.fillStyle = fillColor; + ctx.moveTo(width, 0); + ctx.lineTo(width, height); + ctx.lineTo(0, height); + ctx.closePath(); + ctx.fill(); + } + } + + StyledMenuLoader { + id: menuLoader + + onHandleMenuItem: function(itemId) { + toolbarModel.handleMenuItem(itemId) + } + } + } } - width: 4 - height: 4 - - anchors.margins: 2 - anchors.right: parent.right - anchors.bottom: parent.bottom - - onPaint: { - const ctx = getContext("2d"); - ctx.fillStyle = fillColor; - ctx.moveTo(width, 0); - ctx.lineTo(width, height); - ctx.lineTo(0, height); - ctx.closePath(); - ctx.fill(); - } - } + Component { + id: playbackLevelComp + + PlaybackLevel { + property var item: loader.itemData - StyledMenuLoader { - id: menuLoader + width: 128 - onHandleMenuItem: function(itemId) { - toolbarModel.handleMenuItem(itemId) + onVolumeLevelChangeRequested: function(level) { + item.level = level + } + } } } } + } FlatButton { id: customizeButton anchors.margins: 4 + anchors.left: gridView.right + anchors.verticalCenter: root.verticalCenter - width: gridView.cellWidth - height: gridView.cellHeight + width: 32 + height: width icon: IconCode.SETTINGS_COG iconFont: ui.theme.toolbarIconsFont @@ -226,45 +252,4 @@ Item { anchorItem: !root.floating ? ui.rootItem : null } } - - states: [ - State { - when: gridView.isHorizontal - - PropertyChanges { - target: gridView - width: prv.resolveHorizontalGridViewWidth() - height: root.height - sectionWidth: 1 - sectionHeight: root.height - rows: 1 - columns: gridView.noLimit - } - - AnchorChanges { - target: customizeButton - anchors.left: gridView.right - anchors.verticalCenter: root.verticalCenter - } - }, - State { - when: !gridView.isHorizontal - - PropertyChanges { - target: gridView - width: root.width - height: prv.resolveVerticalGridViewHeight() - sectionWidth: root.width - sectionHeight: 1 - rows: gridView.noLimit - columns: 2 - } - - AnchorChanges { - target: customizeButton - anchors.top: gridView.bottom - anchors.right: parent.right - } - } - ] } diff --git a/au4/src/playback/qml/Audacity/Playback/internal/PlaybackLevel.qml b/au4/src/playback/qml/Audacity/Playback/internal/PlaybackLevel.qml new file mode 100644 index 0000000000000000000000000000000000000000..e442cf28b7da6323d05e344b25bd3dcfdf765626 --- /dev/null +++ b/au4/src/playback/qml/Audacity/Playback/internal/PlaybackLevel.qml @@ -0,0 +1,44 @@ +/* +* Audacity: A Digital Audio Editor +*/ +import QtQuick 2.15 +import QtQuick.Layouts 2.15 + +import Muse.UiComponents 1.0 +import Muse.Ui 1.0 + +Item { + id: root + + property real volumeLevel: 0.0 + property real readableVolumeLevel: Math.round(root.volumeLevel * 10) / 10 + + height: 32 + + signal volumeLevelChangeRequested(var level) + + RowLayout { + anchors.fill: parent + anchors.margins: 6 + + spacing: 4 + + StyledIconLabel { + Layout.preferredWidth: 16 + Layout.preferredHeight: Layout.preferredWidth + Layout.alignment: Qt.AlighVCenter + + iconCode: IconCode.AUDIO + } + + VolumeSlider { + Layout.fillWidth: true + Layout.preferredHeight: 18 + Layout.alignment: Qt.AlighVCenter + + onVolumeLevelMoved: function(level) { + root.volumeLevelChangeRequested(Math.round(level * 10) / 10) + } + } + } +} diff --git a/au4/src/playback/qml/Audacity/Playback/internal/VolumeSlider.qml b/au4/src/playback/qml/Audacity/Playback/internal/VolumeSlider.qml new file mode 100644 index 0000000000000000000000000000000000000000..b302f7b6765ed26bed0d552314b944cd53bb06a4 --- /dev/null +++ b/au4/src/playback/qml/Audacity/Playback/internal/VolumeSlider.qml @@ -0,0 +1,192 @@ +/* +* Audacity: A Digital Audio Editor +*/ + +import QtQuick 2.15 +import QtQuick.Controls 2.15 + +import Muse.Ui 1.0 +import Muse.UiComponents 1.0 + +Slider { + id: root + + property real leftCurrentVolumePressure: -60.0 + property real leftRecentPeak: -60 + property real leftMaxPeak: -60 + + property real rightCurrentVolumePressure: -60.0 + property real rightRecentPeak: -60 + property real rightMaxPeak: -60 + + property real maxDisplayedVolumePressure: 0.0 + + property real volumeLevel: 0.0 + property real readableVolumeLevel: Math.round(root.volumeLevel * 10) / 10 + + property alias navigation: navCtrl + + signal volumeLevelMoved(var level) + + from: -60 + to: 0 + value: root.volumeLevel + stepSize: 0.1 + orientation: Qt.Horizontal + wheelEnabled: true + + signal increaseRequested() + signal decreaseRequested() + + QtObject { + id: prv + + property int margin: 2 + readonly property int spacing: 2 + + property var gradient: null + readonly property int overloadWidth: 4 + + readonly property real indicatorWidth: root.width - handleWidth - overloadWidth + readonly property real indicatorHeight: 6 + + readonly property real rulerLineWidth: root.width - handleWidth + readonly property real rulerLineHeight: 2 + + readonly property int rulerYPos: margin + indicatorHeight + spacing / 2 + + readonly property int fullValueRangeLength: Math.abs(root.from) + Math.abs(root.to) + readonly property real divisionPixels: rulerLineWidth / fullValueRangeLength + + readonly property real handleWidth: 14 + readonly property real handleHeight: 14 + + property real dragStartOffset: 0.0 + } + + NavigationControl { + id: navCtrl + name: root.objectName !== "" ? root.objectName : "VolumeSlider" + enabled: root.enabled && root.visible + + accessible.role: MUAccessible.Range + accessible.visualItem: root + + accessible.value: root.readableVolumeLevel + accessible.minimumValue: root.from + accessible.maximumValue: root.to + accessible.stepSize: root.stepSize + + onNavigationEvent: function(event) { + switch(event.type) { + case NavigationEvent.Left: + root.decreaseRequested() + event.accepted = true + break + case NavigationEvent.Right: + root.increaseRequested() + event.accepted = true + break + } + } + } + + background: Canvas { + id: bgCanvas + + height: root.height + width: root.width + + NavigationFocusBorder { + navigationCtrl: navCtrl + } + + function drawMeter(ctx, originHPos, originVPos, volumePressure) { + ctx.fillStyle = "#4D4D4D" + ctx.fillRect(originHPos, originVPos, prv.indicatorWidth , prv.indicatorHeight) + + var isClipping = volumePressure >= root.maxDisplayedVolumePressure + + ctx.fillStyle = isClipping ? "#FF1C1C" : "#666666" + ctx.fillRect(originHPos + prv.indicatorWidth, originVPos, prv.overloadWidth, prv.indicatorHeight) + + ctx.fillStyle = ui.theme.accentColor + ctx.fillRect(originHPos, originVPos, prv.divisionPixels * (prv.fullValueRangeLength - Math.abs(volumePressure)), prv.indicatorHeight) + } + + onPaint: { + var ctx = bgCanvas.context + + if (!ctx) { + ctx = getContext("2d") + } + + ctx.clearRect(0, 0, bgCanvas.height, bgCanvas.width) + + var xPos = prv.handleWidth/2 + var yPos = prv.margin + + drawMeter(ctx, prv.handleWidth/2, yPos, root.leftCurrentVolumePressure) + + yPos += prv.indicatorHeight + prv.spacing + + drawMeter(ctx, xPos, yPos, root.rightCurrentVolumePressure) + } + } + + handle: Item { + x: root.position * prv.rulerLineWidth + y: prv.rulerYPos - prv.handleHeight / 2 + implicitWidth: prv.handleWidth + implicitHeight: prv.handleHeight + + MouseArea { + anchors.fill: parent + + onDoubleClicked: { + // Double click resets the volume + root.volumeLevelMoved(0.0) + } + + // The MouseArea steals mouse press events from the slider. + // There is really no way to prevent that. + // (if you set mouse.accepted to false in the onPressed handler, + // the MouseArea won't receive doubleClick events). + // So we use this workaround. + + preventStealing: true // Don't let a Flickable steal the mouse + + onPressed: function(mouse) { + prv.dragStartOffset = mouse.x + } + + onPositionChanged: function(mouse) { + let mousePosInRoot = mapToItem(root, mouse.x - prv.dragStartOffset, 0).x + let newPosZeroToOne = mousePosInRoot / prv.rulerLineWidth + + let newPosClamped = Math.max(0.0, Math.min(newPosZeroToOne, 1.0)) + let localNewValue = root.valueAt(newPosClamped) + root.volumeLevelMoved(localNewValue) + } + } + + Rectangle { + id: handleRect + anchors.fill: parent + radius: width / 2 + color: "white" + border.color: "black" + opacity: 0.3 + } + } + + onMoved: { + navigation.requestActiveByInteraction() + + root.volumeLevelMoved(value) + } + + Component.onCompleted: { + bgCanvas.requestPaint() + } +} diff --git a/au4/src/playback/view/toolbars/playbacktoolbarabstractitem.cpp b/au4/src/playback/view/toolbars/playbacktoolbarabstractitem.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0981491528bee15f84da10bd8f4061989fbb3182 --- /dev/null +++ b/au4/src/playback/view/toolbars/playbacktoolbarabstractitem.cpp @@ -0,0 +1,32 @@ +/* +* Audacity: A Digital Audio Editor +*/ +#include "playbacktoolbarabstractitem.h" + +using namespace au::playback; +using namespace muse::uicomponents; + +PlaybackToolBarAbstractItem::PlaybackToolBarAbstractItem(const muse::ui::UiAction& action, const ItemType& type, QObject* parent) + : muse::uicomponents::MenuItem(action, parent), m_type(type) +{ +} + +PlaybackToolBarAbstractItem::ItemType PlaybackToolBarAbstractItem::type() const +{ + return m_type; +} + +void PlaybackToolBarAbstractItem::setType(ItemType type) +{ + if (m_type == type) { + return; + } + + m_type = type; + emit typeChanged(); +} + +int PlaybackToolBarAbstractItem::type_property() const +{ + return static_cast<int>(m_type); +} diff --git a/au4/src/playback/view/toolbars/playbacktoolbarabstractitem.h b/au4/src/playback/view/toolbars/playbacktoolbarabstractitem.h new file mode 100644 index 0000000000000000000000000000000000000000..a9e9dafb9016e7611f3e7efff5e1d3bb3bf0bfe6 --- /dev/null +++ b/au4/src/playback/view/toolbars/playbacktoolbarabstractitem.h @@ -0,0 +1,42 @@ +/* +* Audacity: A Digital Audio Editor +*/ +#ifndef AU_PLAYBACK_PLAYBACKTOOLBARABSTRACTITEM_H +#define AU_PLAYBACK_PLAYBACKTOOLBARABSTRACTITEM_H + +#include <QString> + +#include "uicomponents/view/menuitem.h" + +namespace au::playback { +class PlaybackToolBarAbstractItem : public muse::uicomponents::MenuItem +{ + Q_OBJECT + + Q_PROPERTY(int type READ type_property NOTIFY typeChanged FINAL) + +public: + enum ItemType { + UNDEFINED = -1, + SECTION, + ACTION, + PLAYBACK_LEVEL + }; + Q_ENUM(ItemType) + + explicit PlaybackToolBarAbstractItem(const muse::ui::UiAction& action, const ItemType& type, QObject* parent = nullptr); + + ItemType type() const; + void setType(ItemType type); + +signals: + void typeChanged(); + +private: + int type_property() const; + + ItemType m_type = ItemType::UNDEFINED; +}; +} + +#endif // AU_PLAYBACK_PLAYBACKTOOLBARABSTRACTITEM_H diff --git a/au4/src/playback/view/toolbars/playbacktoolbaractionitem.cpp b/au4/src/playback/view/toolbars/playbacktoolbaractionitem.cpp new file mode 100644 index 0000000000000000000000000000000000000000..d379457a6a0363afe263f893b14a430c21431577 --- /dev/null +++ b/au4/src/playback/view/toolbars/playbacktoolbaractionitem.cpp @@ -0,0 +1,72 @@ +/* +* Audacity: A Digital Audio Editor +*/ +#include "playbacktoolbarcustomiseitem.h" + +using namespace au::playback; +using namespace muse::uicomponents; + +PlaybackToolBarCustomiseItem::PlaybackToolBarCustomiseItem(const ItemType& type, QObject* parent) + : SelectableItemListModel::Item(parent), m_type(type) +{ +} + +QString PlaybackToolBarCustomiseItem::id() const +{ + return m_id; +} + +QString PlaybackToolBarCustomiseItem::title() const +{ + return m_title; +} + +PlaybackToolBarCustomiseItem::ItemType PlaybackToolBarCustomiseItem::type() const +{ + return m_type; +} + +void PlaybackToolBarCustomiseItem::setTitle(QString title) +{ + if (m_title == title) { + return; + } + + m_title = title; + emit titleChanged(); +} + +void PlaybackToolBarCustomiseItem::setId(const QString& id) +{ + m_id = id; +} + +int PlaybackToolBarCustomiseItem::icon() const +{ + return static_cast<int>(m_icon); +} + +bool PlaybackToolBarCustomiseItem::checked() const +{ + return m_checked; +} + +void PlaybackToolBarCustomiseItem::setIcon(muse::ui::IconCode::Code icon) +{ + if (m_icon == icon) { + return; + } + + m_icon = icon; + emit iconChanged(); +} + +void PlaybackToolBarCustomiseItem::setChecked(bool checked) +{ + if (m_checked == checked) { + return; + } + + m_checked = checked; + emit checkedChanged(m_checked); +} diff --git a/au4/src/playback/view/toolbars/playbacktoolbaractionitem.h b/au4/src/playback/view/toolbars/playbacktoolbaractionitem.h new file mode 100644 index 0000000000000000000000000000000000000000..b7146785527e0de6dfbda7430d76d048e8f1f655 --- /dev/null +++ b/au4/src/playback/view/toolbars/playbacktoolbaractionitem.h @@ -0,0 +1,60 @@ +/* +* Audacity: A Digital Audio Editor +*/ +#ifndef AU_PROJECTSCENE_PLAYBACKTOOLBARCUSTOMISEITEM_H +#define AU_PROJECTSCENE_PLAYBACKTOOLBARCUSTOMISEITEM_H + +#include <QString> + +#include "ui/view/iconcodes.h" +#include "uicomponents/view/selectableitemlistmodel.h" + +namespace au::playback { +class PlaybackToolBarCustomiseItem : public muse::uicomponents::SelectableItemListModel::Item +{ + Q_OBJECT + + Q_PROPERTY(ItemType type READ type CONSTANT) + Q_PROPERTY(QString title READ title WRITE setTitle NOTIFY titleChanged) + Q_PROPERTY(int icon READ icon NOTIFY iconChanged) + Q_PROPERTY(bool checked READ checked WRITE setChecked NOTIFY checkedChanged) + +public: + enum ItemType { + UNDEFINED = -1, + ACTION, + SEPARATOR + }; + Q_ENUM(ItemType) + + explicit PlaybackToolBarCustomiseItem(const ItemType& type, QObject* parent = nullptr); + + ItemType type() const; + QString title() const; + int icon() const; + bool checked() const; + + Q_INVOKABLE QString id() const; + void setId(const QString& id); + +public slots: + void setTitle(QString title); + void setIcon(muse::ui::IconCode::Code icon); + void setChecked(bool checked); + +signals: + void titleChanged(); + void iconChanged(); + void checkedChanged(bool checked); + +private: + + QString m_id; + ItemType m_type = ItemType::UNDEFINED; + QString m_title; + muse::ui::IconCode::Code m_icon = muse::ui::IconCode::Code::NONE; + bool m_checked = false; +}; +} + +#endif // AU_PROJECTSCENE_PLAYBACKTOOLBARCUSTOMISEITEM_H diff --git a/au4/src/playback/view/toolbars/playbacktoolbarlevelitem.cpp b/au4/src/playback/view/toolbars/playbacktoolbarlevelitem.cpp new file mode 100644 index 0000000000000000000000000000000000000000..3639154e1ff33711be3b30e8befe9ac96268db9f --- /dev/null +++ b/au4/src/playback/view/toolbars/playbacktoolbarlevelitem.cpp @@ -0,0 +1,78 @@ +/* + * SPDX-License-Identifier: GPL-3.0-only + * MuseScore-CLA-applies + * + * MuseScore + * Music Composition & Notation + * + * Copyright (C) 2021 MuseScore BVBA and others + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 3 as + * published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <https://www.gnu.org/licenses/>. + */ +#include "playbacktoolbarlevelitem.h" + +#include <QVariantMap> + +using namespace au::playback; +using namespace au::au3; + +static int au3VolumeToLocal(float volume) +{ + //! convert from range 0-1 to -60-0 + float old_max = 1; + float old_min = 0; + int old_range = old_max - old_min; + + int new_max = 0; + int new_min = -60; + int new_range = new_max - new_min; + + return (((volume - old_min) * new_range) / old_range) + new_min; +} + +static float localVolumeToAu3(int volume) +{ + //! convert from range -60-0 to 0-1 + float old_max = 0; + float old_min = -60; + int old_range = old_max - old_min; + + int new_max = 1; + int new_min = 0; + int new_range = new_max - new_min; + + return (((volume - old_min) * new_range) / old_range) + new_min; +} + +PlaybackToolBarLevelItem::PlaybackToolBarLevelItem(const muse::ui::UiAction& action, const ItemType& type, QObject* parent) + : PlaybackToolBarAbstractItem(action, type, parent) +{ + playback()->audioOutput()->playbackVolumeChanged().onReceive(this, [this](audio::volume_dbfs_t volume){ + m_level = au3VolumeToLocal(volume); + emit levelChanged(); + }); +} + +int PlaybackToolBarLevelItem::level() const +{ + return m_level; +} + +void PlaybackToolBarLevelItem::setLevel(int newLevel) +{ + if (m_level == newLevel) { + return; + } + + playback()->audioOutput()->setPlaybackVolume(localVolumeToAu3(newLevel)); +} diff --git a/au4/src/playback/view/toolbars/playbacktoolbarlevelitem.h b/au4/src/playback/view/toolbars/playbacktoolbarlevelitem.h new file mode 100644 index 0000000000000000000000000000000000000000..0e2fbd194617e9291f1dd0a7ec355cc017820308 --- /dev/null +++ b/au4/src/playback/view/toolbars/playbacktoolbarlevelitem.h @@ -0,0 +1,37 @@ +/* +* Audacity: A Digital Audio Editor +*/ +#ifndef AU_PLAYBACK_PLAYBACKTOOLBARLEVELITEM_H +#define AU_PLAYBACK_PLAYBACKTOOLBARLEVELITEM_H + +#include <QString> + +#include "modularity/ioc.h" +#include "au3wrap/iau3playback.h" + +#include "playbacktoolbarabstractitem.h" + +namespace au::playback { +class PlaybackToolBarLevelItem : public PlaybackToolBarAbstractItem +{ + Q_OBJECT + + Q_PROPERTY(int level READ level WRITE setLevel NOTIFY levelChanged FINAL) + + muse::Inject<au3::IAu3Playback> playback; + +public: + explicit PlaybackToolBarLevelItem(const muse::ui::UiAction& action, const ItemType& type, QObject* parent = nullptr); + + int level() const; + void setLevel(int newLevel); + +signals: + void levelChanged(); + +private: + int m_level = 0; +}; +} + +#endif // AU_PLAYBACK_PLAYBACKTOOLBARLEVELITEM_H diff --git a/au4/src/playback/view/toolbars/playbacktoolbarmodel.cpp b/au4/src/playback/view/toolbars/playbacktoolbarmodel.cpp index 8447a4430c53b7fdaea7742ed4f8b2db88348aba..e5cb829ad89363284cf43d9b73cdd9d50ba4a77c 100644 --- a/au4/src/playback/view/toolbars/playbacktoolbarmodel.cpp +++ b/au4/src/playback/view/toolbars/playbacktoolbarmodel.cpp @@ -5,7 +5,8 @@ #include "internal/playbackuiactions.h" -#include "log.h" +#include "view/toolbars/playbacktoolbarabstractitem.h" +#include "view/toolbars/playbacktoolbarlevelitem.h" using namespace muse::uicomponents; using namespace muse::ui; @@ -14,10 +15,29 @@ using namespace au::playback; static const QString TOOLBAR_NAME("playbackToolBar"); +static const ActionCode SECTION_CODE(""); static const ActionCode PLAY_ACTION_CODE("play"); +static const ActionCode PLAYBACK_LEVEL("playback-level"); + +static PlaybackToolBarAbstractItem::ItemType itemType(const ActionCode& actionCode) +{ + if (actionCode == SECTION_CODE) { + return PlaybackToolBarAbstractItem::ItemType::SECTION; + } + + if (actionCode == PLAYBACK_LEVEL) { + return PlaybackToolBarAbstractItem::ItemType::PLAYBACK_LEVEL; + } + + return PlaybackToolBarAbstractItem::ItemType::ACTION; +} PlaybackToolBarModel::PlaybackToolBarModel(QObject* parent) - : AbstractMenuModel(parent) + : QAbstractListModel(parent) +{ +} + +void PlaybackToolBarModel::load() { uiConfiguration()->toolConfigChanged(TOOLBAR_NAME).onNotify(this, [this]() { load(); @@ -26,37 +46,59 @@ PlaybackToolBarModel::PlaybackToolBarModel(QObject* parent) context()->currentProjectChanged().onNotify(this, [this]() { onProjectChanged(); }); -} -void PlaybackToolBarModel::load() -{ - AbstractMenuModel::load(); updateActions(); setupConnections(); } +void PlaybackToolBarModel::handleMenuItem(const QString& itemId) +{ + PlaybackToolBarAbstractItem* item = findItem(itemId.toStdString()); + dispatcher()->dispatch(item->action().code, item->args()); +} + +QVariantMap PlaybackToolBarModel::get(int index) +{ + QVariantMap result; + + QHash<int, QByteArray> names = roleNames(); + QHashIterator<int, QByteArray> i(names); + while (i.hasNext()) { + i.next(); + QModelIndex idx = this->index(index, 0); + QVariant data = idx.data(i.key()); + result[i.value()] = data; + } + + return result; +} + QVariant PlaybackToolBarModel::data(const QModelIndex& index, int role) const { TRACEFUNC; int row = index.row(); - if (!isIndexValid(row)) { + if (!index.isValid() || row >= rowCount()) { return QVariant(); } - const MenuItem* item = items()[row]; + PlaybackToolBarAbstractItem* item = m_items[row]; + ActionCode actionCode = item->action().code; + switch (role) { - case IsMenuSecondaryRole: return isMenuSecondary(item->action().code); + case ItemRole: return QVariant::fromValue(item); + case IsMenuSecondaryRole: return isMenuSecondary(actionCode); case OrderRole: return row; case SectionRole: return item->section(); - default: return AbstractMenuModel::data(index, role); + default: return QVariant(); } } QHash<int, QByteArray> PlaybackToolBarModel::roleNames() const { - QHash<int, QByteArray> roles = AbstractMenuModel::roleNames(); + QHash<int, QByteArray> roles; + roles[ItemRole] = "item"; roles[IsMenuSecondaryRole] = "isMenuSecondary"; roles[OrderRole] = "order"; roles[SectionRole] = "section"; @@ -64,13 +106,27 @@ QHash<int, QByteArray> PlaybackToolBarModel::roleNames() const return roles; } -void PlaybackToolBarModel::onActionsStateChanges(const muse::actions::ActionCodeList& codes) +int PlaybackToolBarModel::rowCount(const QModelIndex&) const { - AbstractMenuModel::onActionsStateChanges(codes); + return m_items.size(); +} +PlaybackToolBarAbstractItem* PlaybackToolBarModel::findItem(const muse::actions::ActionCode& actionCode) +{ + for (PlaybackToolBarAbstractItem* item : m_items) { + if (item->action().code == actionCode) { + return item; + } + } + + return nullptr; +} + +void PlaybackToolBarModel::onActionsStateChanges(const muse::actions::ActionCodeList& codes) +{ if (containsAction(codes, PLAY_ACTION_CODE)) { - MenuItem& item = findItem(PLAY_ACTION_CODE); - item.setAction(playAction()); + PlaybackToolBarAbstractItem* item = findItem(PLAY_ACTION_CODE); + item->setAction(playAction()); } } @@ -83,47 +139,34 @@ void PlaybackToolBarModel::setupConnections() void PlaybackToolBarModel::onProjectChanged() { - updateState(); } void PlaybackToolBarModel::updateActions() { - MenuItemList items; + m_items.clear(); + + beginResetModel(); muse::ui::ToolConfig playbackConfig = uiConfiguration()->toolConfig(TOOLBAR_NAME, PlaybackUiActions::defaultPlaybackToolConfig()); - int section = 0; for (const muse::ui::ToolConfig::Item& citem : playbackConfig.items) { if (!citem.show) { continue; } - if (citem.action.empty()) { - section++; - continue; - } - - MenuItem* item = makeActionItem(uiActionsRegister()->action(citem.action), QString::number(section)); + PlaybackToolBarAbstractItem* item = makeItem(uiActionsRegister()->action(citem.action)); if (citem.action == PLAY_ACTION_CODE) { item->setAction(playAction()); } - items << item; + m_items << item; } - setItems(items); -} + endResetModel(); -void PlaybackToolBarModel::updateState() -{ - for (int i = 0; i < rowCount(); ++i) { - MenuItem& item = this->item(i); - muse::ui::UiActionState state = item.state(); - state.checked = false; - item.setState(state); - } + emit itemsChanged(); } bool PlaybackToolBarModel::isMenuSecondary(const muse::actions::ActionCode& actionCode) const @@ -133,13 +176,17 @@ bool PlaybackToolBarModel::isMenuSecondary(const muse::actions::ActionCode& acti return false; } -MenuItem* PlaybackToolBarModel::makeActionItem(const muse::ui::UiAction& action, const QString& section, - const muse::uicomponents::MenuItemList& subitems) +PlaybackToolBarAbstractItem* PlaybackToolBarModel::makeItem(const muse::ui::UiAction& action) { - MenuItem* item = new MenuItem(action, this); - item->setSection(section); - item->setSubitems(subitems); - return item; + PlaybackToolBarAbstractItem::ItemType type = itemType(action.code); + + switch (type) { + case PlaybackToolBarAbstractItem::PLAYBACK_LEVEL: return new PlaybackToolBarLevelItem(action, type, this); + default: + break; + } + + return new PlaybackToolBarAbstractItem(action, type, this); } UiAction PlaybackToolBarModel::playAction() const diff --git a/au4/src/playback/view/toolbars/playbacktoolbarmodel.h b/au4/src/playback/view/toolbars/playbacktoolbarmodel.h index 8da212a22a4da83e74b4ef2d4c560e66e4b78de1..7813cbb21f63766ce608c2441376fefd1ab7f9e8 100644 --- a/au4/src/playback/view/toolbars/playbacktoolbarmodel.h +++ b/au4/src/playback/view/toolbars/playbacktoolbarmodel.h @@ -5,54 +5,70 @@ #define AU_PROJECTSCENE_PLAYBACKTOOLBARMODEL_H #include <QHash> +#include <QAbstractListModel> + +#include "async/asyncable.h" #include "modularity/ioc.h" #include "context/iglobalcontext.h" #include "ui/iuiactionsregister.h" #include "ui/iuiconfiguration.h" +#include "actions/iactionsdispatcher.h" #include "playback/iplaybackcontroller.h" -#include "uicomponents/view/abstractmenumodel.h" - namespace au::playback { -class PlaybackToolBarModel : public muse::uicomponents::AbstractMenuModel +class PlaybackToolBarAbstractItem; +class PlaybackToolBarModel : public QAbstractListModel, public muse::async::Asyncable { Q_OBJECT - muse::Inject<au::context::IGlobalContext> context; muse::Inject<muse::ui::IUiActionsRegister> actionsRegister; muse::Inject<muse::ui::IUiConfiguration> uiConfiguration; + muse::Inject<muse::ui::IUiActionsRegister> uiActionsRegister; + muse::Inject<muse::actions::IActionsDispatcher> dispatcher; + muse::Inject<au::context::IGlobalContext> context; muse::Inject<au::playback::IPlaybackController> controller; + Q_PROPERTY(int length READ rowCount NOTIFY itemsChanged) + public: explicit PlaybackToolBarModel(QObject* parent = nullptr); - Q_INVOKABLE void load() override; + Q_INVOKABLE void load(); + Q_INVOKABLE void handleMenuItem(const QString& itemId); + Q_INVOKABLE QVariantMap get(int index); QVariant data(const QModelIndex& index, int role) const override; QHash<int, QByteArray> roleNames() const override; + int rowCount(const QModelIndex& parent = QModelIndex()) const override; + +signals: + void itemsChanged(); private: enum InputRoles { - IsMenuSecondaryRole = AbstractMenuModel::Roles::UserRole + 1, + ItemRole = Qt::UserRole + 1, + IsMenuSecondaryRole, OrderRole, - SectionRole + SectionRole, }; - void onActionsStateChanges(const muse::actions::ActionCodeList& codes) override; + PlaybackToolBarAbstractItem* findItem(const muse::actions::ActionCode& actionCode); + + void onActionsStateChanges(const muse::actions::ActionCodeList& codes); void setupConnections(); void onProjectChanged(); void updateActions(); - void updateState(); bool isMenuSecondary(const muse::actions::ActionCode& actionCode) const; - muse::uicomponents::MenuItem* makeActionItem(const muse::ui::UiAction& action, const QString& section, - const muse::uicomponents::MenuItemList& subitems = {}); + PlaybackToolBarAbstractItem* makeItem(const muse::ui::UiAction& action); muse::ui::UiAction playAction() const; + + QList<PlaybackToolBarAbstractItem*> m_items; }; } diff --git a/au4/src/processing/dom/processingproject.cpp b/au4/src/processing/dom/processingproject.cpp index 5fb6220c01704fc408c88773aa508aa9e4e34aff..0ed866eda3033cdaefc5d1ee86518a5e98f3875f 100644 --- a/au4/src/processing/dom/processingproject.cpp +++ b/au4/src/processing/dom/processingproject.cpp @@ -1,6 +1,6 @@ #include "processingproject.h" -#include "au3wrap/audacity3project.h" +#include "au3wrap/au3project.h" #include "log.h" @@ -9,7 +9,7 @@ using namespace au::processing; ProcessingProject::ProcessingProject() {} -void ProcessingProject::setAudacity3Project(std::shared_ptr<au::au3::Audacity3Project> au3) +void ProcessingProject::setAudacity3Project(std::shared_ptr<au::au3::Au3Project> au3) { m_au3 = au3; } diff --git a/au4/src/processing/dom/processingproject.h b/au4/src/processing/dom/processingproject.h index aa3e97d409710fa1cd4ecf6de89af1313ef14920..cc959a42c709cb5f9f4f4d71fdfe7046f55eaf08 100644 --- a/au4/src/processing/dom/processingproject.h +++ b/au4/src/processing/dom/processingproject.h @@ -8,7 +8,7 @@ #include "track.h" namespace au::au3 { -class Audacity3Project; +class Au3Project; } namespace au::processing { @@ -18,8 +18,8 @@ class ProcessingProject public: ProcessingProject(); - void setAudacity3Project(std::shared_ptr<au::au3::Audacity3Project> au3); - const std::shared_ptr<au::au3::Audacity3Project>& audacity3Project() const { return m_au3; } + void setAudacity3Project(std::shared_ptr<au::au3::Au3Project> au3); + const std::shared_ptr<au::au3::Au3Project>& audacity3Project() const { return m_au3; } muse::async::NotifyList<Track> trackList() const; muse::async::NotifyList<Clip> clipList(const TrackId& trackId) const; @@ -31,7 +31,7 @@ public: private: - std::shared_ptr<au::au3::Audacity3Project> m_au3; + std::shared_ptr<au::au3::Au3Project> m_au3; mutable std::map<TrackId, muse::async::ChangedNotifier<Clip>> m_clipsChanged; }; diff --git a/au4/src/project/internal/audacityproject.cpp b/au4/src/project/internal/audacityproject.cpp index 3e08239d8554113472a4983ab0f2e83c99d4f10f..d354698fae266af7cee2adf18dc83832ee0ee6a6 100644 --- a/au4/src/project/internal/audacityproject.cpp +++ b/au4/src/project/internal/audacityproject.cpp @@ -1,6 +1,6 @@ #include "audacityproject.h" -#include "au3wrap/audacity3project.h" +#include "au3wrap/au3project.h" #include "iprojectautosaver.h" #include "projecterrors.h" #include "global/io/ioretcodes.h" @@ -61,7 +61,7 @@ muse::Ret Audacity4Project::doLoad(const io::path_t& path, bool forceMode, const UNUSED(forceMode); UNUSED(format); - m_au3Project = au3::Audacity3Project::create(); + m_au3Project = au3::Au3Project::create(); bool isLoaded = m_au3Project->load(path); if (!isLoaded) { LOGE() << "Failed load:" << path; diff --git a/au4/src/project/internal/audacityproject.h b/au4/src/project/internal/audacityproject.h index 2c05517004aae166eb3058b714e9fbb6c66d61d8..aa7405182f693aa599168f98372f66677b89cbc3 100644 --- a/au4/src/project/internal/audacityproject.h +++ b/au4/src/project/internal/audacityproject.h @@ -2,12 +2,12 @@ #define AU_PROJECT_AUDACITYPROJECT_H #include "../iaudacityproject.h" -#include "au3wrap/audacity3project.h" +#include "au3wrap/au3project.h" #include "modularity/ioc.h" #include "io/ifilesystem.h" namespace au::au3 { -class Audacity3Project; +class Au3Project; } namespace au::project { @@ -27,7 +27,7 @@ namespace au::project { //! //! There is also a project from AU3 (::AudacityProject), //! through which we receive a lot of data using the AU3 implementation. -//! And a wrapper in AU4 for this project (au::au3::Audacity3Project) +//! And a wrapper in AU4 for this project (au::au3::Au3Project) //! A wrapper is needed for two reasons: //! * Technical - the AU3 code is strongly linked at the linking level, //! it cannot be used in parts, therefore, to avoid duplication of symbols, @@ -85,7 +85,7 @@ private: bool m_isImported = false; bool m_needAutoSave = false; - std::shared_ptr<au::au3::Audacity3Project> m_au3Project; + std::shared_ptr<au::au3::Au3Project> m_au3Project; au::processing::ProcessingProjectPtr m_processingProject; }; diff --git a/au4/src/projectscene/CMakeLists.txt b/au4/src/projectscene/CMakeLists.txt index a8e7db89658f9aa53fbf5d00b9e79ef7dcb293d2..8374b846d75b88077c3b92e8715427c833c6dc76 100644 --- a/au4/src/projectscene/CMakeLists.txt +++ b/au4/src/projectscene/CMakeLists.txt @@ -34,6 +34,9 @@ set(MODULE_SRC ${CMAKE_CURRENT_LIST_DIR}/internal/projectsceneconfiguration.cpp ${CMAKE_CURRENT_LIST_DIR}/internal/projectsceneconfiguration.h + ${CMAKE_CURRENT_LIST_DIR}/internal/projectsceneuiactions.cpp + ${CMAKE_CURRENT_LIST_DIR}/internal/projectsceneuiactions.h + ${CMAKE_CURRENT_LIST_DIR}/view/trackspanel/trackslistmodel.cpp ${CMAKE_CURRENT_LIST_DIR}/view/trackspanel/trackslistmodel.h ${CMAKE_CURRENT_LIST_DIR}/view/trackspanel/trackitem.cpp diff --git a/au4/src/projectscene/internal/projectsceneuiactions.cpp b/au4/src/projectscene/internal/projectsceneuiactions.cpp new file mode 100644 index 0000000000000000000000000000000000000000..8daaecc45e3349bbf17ffe6ef1c6082dc458a089 --- /dev/null +++ b/au4/src/projectscene/internal/projectsceneuiactions.cpp @@ -0,0 +1,104 @@ +/* +* Audacity: A Digital Audio Editor +*/ +#include "projectsceneuiactions.h" + +#include "ui/view/iconcodes.h" +#include "context/uicontext.h" +#include "context/shortcutcontext.h" +#include "types/translatablestring.h" + +#include "log.h" + +using namespace au::projectscene; +using namespace muse; +using namespace muse::ui; +using namespace muse::actions; + +const UiActionList ProjectSceneUiActions::m_actions = { + UiAction("envelope", + au::context::UiCtxNotationOpened, + au::context::CTX_NOTATION_OPENED, + TranslatableString("action", "Evelope tool"), + TranslatableString("action", "Evelope tool"), + IconCode::Code::ENVELOPE + ), + UiAction("zoom", + au::context::UiCtxNotationOpened, + au::context::CTX_ANY, + TranslatableString("action", "Zoom"), + TranslatableString("action", "Zoom"), + IconCode::Code::ZOOM_TOGGLE + ), + UiAction("zoomin", + au::context::UiCtxNotationOpened, + au::context::CTX_ANY, + TranslatableString("action", "Zoom in"), + TranslatableString("action", "Zoom in"), + IconCode::Code::ZOOM_IN + ), + UiAction("zoomout", + au::context::UiCtxNotationOpened, + au::context::CTX_ANY, + TranslatableString("action", "Zoom out"), + TranslatableString("action", "Zoom out"), + IconCode::Code::ZOOM_OUT + ), + UiAction("fit-selection", + au::context::UiCtxNotationOpened, + au::context::CTX_ANY, + TranslatableString("action", "Fit selection to width"), + TranslatableString("action", "Fit selection to width"), + IconCode::Code::FIT_SELECTION + ), + UiAction("fit-project", + au::context::UiCtxNotationOpened, + au::context::CTX_ANY, + TranslatableString("action", "Fit project to width"), + TranslatableString("action", "Fit project to width"), + IconCode::Code::FIT_PROJECT + ), + UiAction("trim-audio-outside-selection", + au::context::UiCtxNotationOpened, + au::context::CTX_ANY, + TranslatableString("action", "Trim audio outside selection"), + TranslatableString("action", "Trim audio outside selection"), + IconCode::Code::TRIM_AUDIO_OUTSIDE_SELECTION + ), + UiAction("silence-audio-selection", + au::context::UiCtxNotationOpened, + au::context::CTX_ANY, + TranslatableString("action", "Silence audio selection"), + TranslatableString("action", "Silence audio selection"), + IconCode::Code::SILENCE_AUDIO_SELECTION + ), +}; + +const UiActionList& ProjectSceneUiActions::actionsList() const +{ + return m_actions; +} + +bool ProjectSceneUiActions::actionEnabled(const muse::ui::UiAction& act) const +{ + UNUSED(act); + return true; +} + +muse::async::Channel<ActionCodeList> ProjectSceneUiActions::actionEnabledChanged() const +{ + static async::Channel<ActionCodeList> ch; + return ch; +} + +bool ProjectSceneUiActions::actionChecked(const muse::ui::UiAction& act) const +{ + UNUSED(act); + return false; +} + +muse::async::Channel<ActionCodeList> ProjectSceneUiActions::actionCheckedChanged() const +{ + static async::Channel<ActionCodeList> ch; + return ch; +} diff --git a/au4/src/projectscene/internal/projectsceneuiactions.h b/au4/src/projectscene/internal/projectsceneuiactions.h new file mode 100644 index 0000000000000000000000000000000000000000..c55ca2a14dbf20adf68afd523582520dbb53e343 --- /dev/null +++ b/au4/src/projectscene/internal/projectsceneuiactions.h @@ -0,0 +1,31 @@ +/* +* Audacity: A Digital Audio Editor +*/ +#ifndef AU_PROJECTSCENE_PROJECTSCENEUIACTIONS_H +#define AU_PROJECTSCENE_PROJECTSCENEUIACTIONS_H + +#include "ui/iuiactionsmodule.h" +#include "modularity/ioc.h" +#include "context/iuicontextresolver.h" +#include "async/asyncable.h" + +namespace au::projectscene { +class ProjectSceneUiActions : public muse::ui::IUiActionsModule, public muse::async::Asyncable +{ + muse::Inject<mu::context::IUiContextResolver> uicontextResolver; + +public: + const muse::ui::UiActionList& actionsList() const override; + + bool actionEnabled(const muse::ui::UiAction& act) const override; + muse::async::Channel<muse::actions::ActionCodeList> actionEnabledChanged() const override; + + bool actionChecked(const muse::ui::UiAction& act) const override; + muse::async::Channel<muse::actions::ActionCodeList> actionCheckedChanged() const override; + +private: + static const muse::ui::UiActionList m_actions; +}; +} + +#endif // AU_PROJECTSCENE_PROJECTSCENEUIACTIONS_H diff --git a/au4/src/projectscene/projectscenemodule.cpp b/au4/src/projectscene/projectscenemodule.cpp index 75d4ae108fa1b680338a4ddffdbcdcc5a8d49540..901e8578ea157452305ef819f568ad48e267dd73 100644 --- a/au4/src/projectscene/projectscenemodule.cpp +++ b/au4/src/projectscene/projectscenemodule.cpp @@ -25,6 +25,10 @@ #include "types/projectscenetypes.h" +#include "ui/iuiactionsregister.h" + +#include "internal/projectsceneuiactions.h" + #include "internal/projectsceneconfiguration.h" #include "view/toolbars/projecttoolbarmodel.h" @@ -37,6 +41,7 @@ using namespace au::projectscene; using namespace muse::modularity; +using namespace muse::ui; static void projectscene_init_qrc() { @@ -55,6 +60,7 @@ void ProjectSceneModule::registerResources() void ProjectSceneModule::registerExports() { + m_uiActions = std::make_shared<ProjectSceneUiActions>(); m_configuration = std::make_shared<ProjectSceneConfiguration>(); ioc()->registerExport<IProjectSceneConfiguration>(moduleName(), m_configuration); @@ -62,6 +68,10 @@ void ProjectSceneModule::registerExports() void ProjectSceneModule::resolveImports() { + auto ar = ioc()->resolve<IUiActionsRegister>(moduleName()); + if (ar) { + ar->reg(m_uiActions); + } } void ProjectSceneModule::registerUiTypes() diff --git a/au4/src/projectscene/projectscenemodule.h b/au4/src/projectscene/projectscenemodule.h index 14f8bf61d6af8ee2db366ac8c4c094f11f11a725..51b9be9cb2c11889551c87bb0ed330fa23228979 100644 --- a/au4/src/projectscene/projectscenemodule.h +++ b/au4/src/projectscene/projectscenemodule.h @@ -9,6 +9,7 @@ #include "modularity/imodulesetup.h" namespace au::projectscene { +class ProjectSceneUiActions; class ProjectSceneConfiguration; class ProjectSceneModule : public muse::modularity::IModuleSetup { @@ -22,7 +23,7 @@ public: void onInit(const muse::IApplication::RunMode& mode) override; private: - + std::shared_ptr<ProjectSceneUiActions> m_uiActions; std::shared_ptr<ProjectSceneConfiguration> m_configuration; }; } diff --git a/au4/src/projectscene/qml/Audacity/ProjectScene/internal/TrackItem.qml b/au4/src/projectscene/qml/Audacity/ProjectScene/internal/TrackItem.qml index 66c8383e4b9658867a66c4dc2226b7543126b52d..66d3820b968ed9309b7995b1f822f8e3bc2d59ab 100644 --- a/au4/src/projectscene/qml/Audacity/ProjectScene/internal/TrackItem.qml +++ b/au4/src/projectscene/qml/Audacity/ProjectScene/internal/TrackItem.qml @@ -55,7 +55,7 @@ ListItemBlank { spacing: 8 StyledIconLabel { - iconCode: IconCode.AUDIO + iconCode: IconCode.MICROPHONE } StyledTextLabel {