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 {