diff --git a/au4/src/au3wrap/CMakeLists.txt b/au4/src/au3wrap/CMakeLists.txt index 1be7b848dbb5d6e1b5a8d8611d516908bcf6b06f..6275a7f4b312b8126990dc6864a1acfc20a64644 100644 --- a/au4/src/au3wrap/CMakeLists.txt +++ b/au4/src/au3wrap/CMakeLists.txt @@ -31,6 +31,8 @@ set(MODULE_SRC ${CMAKE_CURRENT_LIST_DIR}/audacity3playback.cpp ${CMAKE_CURRENT_LIST_DIR}/audacity3playback.h ${CMAKE_CURRENT_LIST_DIR}/iau3wavepainter.h + ${CMAKE_CURRENT_LIST_DIR}/iaudacity3playback.h + ${CMAKE_CURRENT_LIST_DIR}/iaudacity3audiooutput.h ${CMAKE_CURRENT_LIST_DIR}/mocks/au3settingsmock.cpp ${CMAKE_CURRENT_LIST_DIR}/mocks/au3settingsmock.h @@ -47,6 +49,8 @@ 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/audacity3audiooutput.cpp + ${CMAKE_CURRENT_LIST_DIR}/internal/audacity3audiooutput.h ) # ================================== diff --git a/au4/src/au3wrap/au3wrapmodule.cpp b/au4/src/au3wrap/au3wrapmodule.cpp index 0712bd38544da1fc2dbc6bf5b852376e426e6f12..139dfe386a1b183b6930309a1817e149edc674d9 100644 --- a/au4/src/au3wrap/au3wrapmodule.cpp +++ b/au4/src/au3wrap/au3wrapmodule.cpp @@ -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/audacity3playback.cpp b/au4/src/au3wrap/audacity3playback.cpp index 5da201f0ee388351e3dc63a9e26f005c07ce8f05..a6858fbfd5ef2cd2121dba84e2c7f00524b3edeb 100644 --- a/au4/src/au3wrap/audacity3playback.cpp +++ b/au4/src/au3wrap/audacity3playback.cpp @@ -14,10 +14,17 @@ #include "wxtypes_convert.h" +#include "internal/audacity3audiooutput.h" + #include "log.h" using namespace au::au3; +void Audacity3Playback::init() +{ + m_audioOutputPtr = std::make_shared<Audacity3AudioOutput>(); +} + void Audacity3Playback::play() { //! NOTE: copied from ProjectAudioManager::PlayPlayRegion @@ -264,6 +271,11 @@ muse::async::Channel<au::audio::PlaybackStatus> Audacity3Playback::playbackStatu return m_playbackStatusChanged; } +IAudacity3AudioOutputPtr Audacity3Playback::audioOutput() const +{ + return m_audioOutputPtr; +} + AudacityProject& Audacity3Playback::projectRef() const { AudacityProject* project = reinterpret_cast<AudacityProject*>(globalContext()->currentProject()->au3ProjectPtr()); diff --git a/au4/src/au3wrap/audacity3playback.h b/au4/src/au3wrap/audacity3playback.h index dd998d893e850f8a20d928db1d06cc85bf678125..18d849849cef49914c0ef2ec12e2220444daa30c 100644 --- a/au4/src/au3wrap/audacity3playback.h +++ b/au4/src/au3wrap/audacity3playback.h @@ -11,6 +11,7 @@ #include "context/iglobalcontext.h" #include "iaudacity3playback.h" +#include "iaudacity3audiooutput.h" class AudacityProject; class TrackList; @@ -22,6 +23,8 @@ class Audacity3Playback : public IAudacity3Playback, public muse::async::Asyncab 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; + IAudacity3AudioOutputPtr audioOutput() const override; + private: AudacityProject& projectRef() const; @@ -43,6 +48,8 @@ private: mutable muse::async::Channel<audio::msecs_t> m_playbackPositionMsecsChanged; mutable muse::async::Channel<audio::PlaybackStatus> m_playbackStatusChanged; + + IAudacity3AudioOutputPtr m_audioOutputPtr; }; } diff --git a/au4/src/au3wrap/iaudacity3audiooutput.h b/au4/src/au3wrap/iaudacity3audiooutput.h new file mode 100644 index 0000000000000000000000000000000000000000..2500fcd1bb5f03e5a2d4ee05441178beae8b8e2e --- /dev/null +++ b/au4/src/au3wrap/iaudacity3audiooutput.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 IAudacity3AudioOutput +{ +public: + virtual ~IAudacity3AudioOutput() = 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 IAudacity3AudioOutputPtr = std::shared_ptr<IAudacity3AudioOutput>; +} + +#endif // AU_AU3WRAP_IAUDACITYAUDIOOUTPUT_H diff --git a/au4/src/au3wrap/iaudacity3playback.h b/au4/src/au3wrap/iaudacity3playback.h index 2ccd6563b7496ec8bf233169e6c39fe10e5a7bfd..1cddd4c73f07c2b7dfd567140debe6358d6f61fb 100644 --- a/au4/src/au3wrap/iaudacity3playback.h +++ b/au4/src/au3wrap/iaudacity3playback.h @@ -11,7 +11,7 @@ #include "playback/audiotypes.h" -class AudacityProject; +#include "iaudacity3audiooutput.h" namespace au::au3 { class IAudacity3Playback : MODULE_EXPORT_INTERFACE @@ -32,6 +32,8 @@ public: virtual muse::async::Channel<audio::msecs_t> playbackPositionMsecs() const = 0; virtual muse::async::Channel<audio::PlaybackStatus> playbackStatusChanged() const = 0; + + virtual std::shared_ptr<IAudacity3AudioOutput> audioOutput() const = 0; }; using IAudacity3PlaybackPtr = std::shared_ptr<IAudacity3Playback>; } diff --git a/au4/src/au3wrap/internal/audacity3audiooutput.cpp b/au4/src/au3wrap/internal/audacity3audiooutput.cpp new file mode 100644 index 0000000000000000000000000000000000000000..71f33102442e339ed27117e4ed38ae5b8cc610f0 --- /dev/null +++ b/au4/src/au3wrap/internal/audacity3audiooutput.cpp @@ -0,0 +1,59 @@ +/* +* Audacity: A Digital Audio Editor +*/ + +#include "audacity3audiooutput.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> Audacity3AudioOutput::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 Audacity3AudioOutput::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> Audacity3AudioOutput::playbackVolumeChanged() const +{ + return m_playbackVolumeChanged; +} + +muse::async::Promise<au::audio::AudioSignalChanges> Audacity3AudioOutput::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/audacity3audiooutput.h b/au4/src/au3wrap/internal/audacity3audiooutput.h new file mode 100644 index 0000000000000000000000000000000000000000..0d71433acd2563362c9cd625b7bbefdd10ee4cea --- /dev/null +++ b/au4/src/au3wrap/internal/audacity3audiooutput.h @@ -0,0 +1,27 @@ +/* +* Audacity: A Digital Audio Editor +*/ + +#ifndef AU_AU3WRAP_AUDIOOUTPUT_H +#define AU_AU3WRAP_AUDIOOUTPUT_H + +#include "global/async/asyncable.h" + +#include "../iaudacity3audiooutput.h" + +namespace au::au3 { +class Audacity3AudioOutput : public IAudacity3AudioOutput, 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_AUDIOOUTPUT_H diff --git a/au4/src/playback/qml/Audacity/Playback/PlaybackToolBar.qml b/au4/src/playback/qml/Audacity/Playback/PlaybackToolBar.qml index fac3914e542f274a489b753494343a33ead714d8..cf17b7f5730b04d09d74049e52f97b0050affbf8 100644 --- a/au4/src/playback/qml/Audacity/Playback/PlaybackToolBar.qml +++ b/au4/src/playback/qml/Audacity/Playback/PlaybackToolBar.qml @@ -157,8 +157,6 @@ Item { Connections { target: btn.mouseArea - // Make sure we only connect to `pressAndHold` if necessary - // See https://github.com/musescore/MuseScore/issues/16012 enabled: btn.hasMenu && !menuLoader.isMenuOpened function onPressAndHold() { @@ -210,7 +208,13 @@ Item { id: playbackLevelComp PlaybackLevel { + property var item: loader.itemData + width: 128 + + onVolumeLevelChangeRequested: function(level) { + item.level = level + } } } } diff --git a/au4/src/playback/qml/Audacity/Playback/internal/PlaybackLevel.qml b/au4/src/playback/qml/Audacity/Playback/internal/PlaybackLevel.qml index 3ae99a9df6ae9b076114f3c66f1e2e50c7c0d2d8..e442cf28b7da6323d05e344b25bd3dcfdf765626 100644 --- a/au4/src/playback/qml/Audacity/Playback/internal/PlaybackLevel.qml +++ b/au4/src/playback/qml/Audacity/Playback/internal/PlaybackLevel.qml @@ -15,6 +15,8 @@ Item { height: 32 + signal volumeLevelChangeRequested(var level) + RowLayout { anchors.fill: parent anchors.margins: 6 @@ -35,7 +37,7 @@ Item { Layout.alignment: Qt.AlighVCenter onVolumeLevelMoved: function(level) { - volumeLevel = Math.round(level * 10) / 10 + 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 index da21eeb1a902dd027a92477c00c4166ee8d811dc..b302f7b6765ed26bed0d552314b944cd53bb06a4 100644 --- a/au4/src/playback/qml/Audacity/Playback/internal/VolumeSlider.qml +++ b/au4/src/playback/qml/Audacity/Playback/internal/VolumeSlider.qml @@ -1,24 +1,6 @@ /* - * 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/>. - */ +* Audacity: A Digital Audio Editor +*/ import QtQuick 2.15 import QtQuick.Controls 2.15 @@ -69,7 +51,7 @@ Slider { readonly property real indicatorHeight: 6 readonly property real rulerLineWidth: root.width - handleWidth - readonly property real rulerLineHeight: 2 // todo + readonly property real rulerLineHeight: 2 readonly property int rulerYPos: margin + indicatorHeight + spacing / 2 @@ -84,7 +66,7 @@ Slider { NavigationControl { id: navCtrl - name: root.objectName != "" ? root.objectName : "VolumeSlider" + name: root.objectName !== "" ? root.objectName : "VolumeSlider" enabled: root.enabled && root.visible accessible.role: MUAccessible.Range @@ -201,8 +183,7 @@ Slider { onMoved: { navigation.requestActiveByInteraction() - var newLevel = convertor.volumeLevelFromLocal(value) - root.volumeLevelMoved(newLevel) + root.volumeLevelMoved(value) } Component.onCompleted: { diff --git a/au4/src/playback/view/toolbars/playbacktoolbarlevelitem.cpp b/au4/src/playback/view/toolbars/playbacktoolbarlevelitem.cpp index 3ae7102a222dc351787ed08f8e1dcc6cf4df61d4..3639154e1ff33711be3b30e8befe9ac96268db9f 100644 --- a/au4/src/playback/view/toolbars/playbacktoolbarlevelitem.cpp +++ b/au4/src/playback/view/toolbars/playbacktoolbarlevelitem.cpp @@ -24,10 +24,43 @@ #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 @@ -41,6 +74,5 @@ void PlaybackToolBarLevelItem::setLevel(int newLevel) return; } - m_level = newLevel; - emit levelChanged(); + playback()->audioOutput()->setPlaybackVolume(localVolumeToAu3(newLevel)); } diff --git a/au4/src/playback/view/toolbars/playbacktoolbarlevelitem.h b/au4/src/playback/view/toolbars/playbacktoolbarlevelitem.h index 8fe2453a809b7cd97963705985f7f4249b768f42..8ee35fc1cba2103b5786698c400b3438b16d2c52 100644 --- a/au4/src/playback/view/toolbars/playbacktoolbarlevelitem.h +++ b/au4/src/playback/view/toolbars/playbacktoolbarlevelitem.h @@ -6,6 +6,9 @@ #include <QString> +#include "modularity/ioc.h" +#include "au3wrap/iaudacity3playback.h" + #include "playbacktoolbarabstractitem.h" namespace au::playback { @@ -15,6 +18,8 @@ class PlaybackToolBarLevelItem : public PlaybackToolBarAbstractItem Q_PROPERTY(int level READ level WRITE setLevel NOTIFY levelChanged FINAL) + muse::Inject<au3::IAudacity3Playback> playback; + public: explicit PlaybackToolBarLevelItem(const muse::ui::UiAction& action, const ItemType& type, QObject* parent = nullptr); diff --git a/au4/src/playback/view/toolbars/playbacktoolbarmodel.cpp b/au4/src/playback/view/toolbars/playbacktoolbarmodel.cpp index a4a2d43383c78aaeb48d7dbfadb8e9b3fe4a1260..e5cb829ad89363284cf43d9b73cdd9d50ba4a77c 100644 --- a/au4/src/playback/view/toolbars/playbacktoolbarmodel.cpp +++ b/au4/src/playback/view/toolbars/playbacktoolbarmodel.cpp @@ -6,6 +6,7 @@ #include "internal/playbackuiactions.h" #include "view/toolbars/playbacktoolbarabstractitem.h" +#include "view/toolbars/playbacktoolbarlevelitem.h" using namespace muse::uicomponents; using namespace muse::ui; @@ -82,8 +83,8 @@ QVariant PlaybackToolBarModel::data(const QModelIndex& index, int role) const return QVariant(); } - const PlaybackToolBarAbstractItem* item = m_items[row]; - const ActionCode actionCode = item->action().code; + PlaybackToolBarAbstractItem* item = m_items[row]; + ActionCode actionCode = item->action().code; switch (role) { case ItemRole: return QVariant::fromValue(item); @@ -149,13 +150,12 @@ void PlaybackToolBarModel::updateActions() 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; } - PlaybackToolBarAbstractItem* item = makeItem(uiActionsRegister()->action(citem.action), QString::number(section)); + PlaybackToolBarAbstractItem* item = makeItem(uiActionsRegister()->action(citem.action)); if (citem.action == PLAY_ACTION_CODE) { item->setAction(playAction()); @@ -176,11 +176,17 @@ bool PlaybackToolBarModel::isMenuSecondary(const muse::actions::ActionCode& acti return false; } -PlaybackToolBarAbstractItem* PlaybackToolBarModel::makeItem(const muse::ui::UiAction& action, const QString& section) +PlaybackToolBarAbstractItem* PlaybackToolBarModel::makeItem(const muse::ui::UiAction& action) { - PlaybackToolBarAbstractItem* item = new PlaybackToolBarAbstractItem(action, itemType(action.code), this); - item->setSection(section); - 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 b3a7a12e3d2c3d05373ded945ec9df499a0dd1a8..7813cbb21f63766ce608c2441376fefd1ab7f9e8 100644 --- a/au4/src/playback/view/toolbars/playbacktoolbarmodel.h +++ b/au4/src/playback/view/toolbars/playbacktoolbarmodel.h @@ -64,7 +64,7 @@ private: bool isMenuSecondary(const muse::actions::ActionCode& actionCode) const; - PlaybackToolBarAbstractItem* makeItem(const muse::ui::UiAction& action, const QString& section); + PlaybackToolBarAbstractItem* makeItem(const muse::ui::UiAction& action); muse::ui::UiAction playAction() const;