From 3c39f256bcb2b4140d66609b7442261407f32ec4 Mon Sep 17 00:00:00 2001 From: Nicolas Pope <nicolas.pope@utu.fi> Date: Thu, 30 Jul 2020 10:20:50 +0300 Subject: [PATCH] Audio mixer GUI and more vectorisation --- applications/gui2/src/modules/camera.cpp | 13 +++++ applications/gui2/src/modules/camera.hpp | 3 + applications/gui2/src/views/camera.cpp | 2 +- applications/gui2/src/widgets/soundctrl.cpp | 40 +++++++++++++- applications/gui2/src/widgets/soundctrl.hpp | 5 +- components/audio/include/ftl/audio/buffer.hpp | 26 ++++++++- components/audio/include/ftl/audio/mixer.hpp | 55 ++++++++++++------- components/audio/src/speaker.cpp | 12 +--- components/audio/test/mixer_unit.cpp | 16 ++++-- .../streams/include/ftl/streams/feed.hpp | 12 ++++ .../streams/include/ftl/streams/renderer.hpp | 3 + components/streams/src/baserender.hpp | 4 ++ components/streams/src/feed.cpp | 9 +++ components/streams/src/renderer.cpp | 5 ++ .../streams/src/renderers/openvr_render.cpp | 5 +- .../streams/src/renderers/openvr_render.hpp | 2 - .../streams/src/renderers/screen_render.cpp | 5 +- .../streams/src/renderers/screen_render.hpp | 3 - 18 files changed, 169 insertions(+), 51 deletions(-) diff --git a/applications/gui2/src/modules/camera.cpp b/applications/gui2/src/modules/camera.cpp index a47d35f1a..c362d6a48 100644 --- a/applications/gui2/src/modules/camera.cpp +++ b/applications/gui2/src/modules/camera.cpp @@ -3,6 +3,7 @@ #include "../views/camera3d.hpp" #include <ftl/rgbd/capabilities.hpp> +#include <ftl/streams/renderer.hpp> #include <chrono> #include <opencv2/imgproc.hpp> @@ -330,6 +331,18 @@ std::string Camera::getActiveSourceURI() { return ""; } +ftl::audio::StereoMixerF<100> *Camera::mixer() { + if (mixer_) return mixer_; + if (movable_) { + auto *rend = io->feed()->getRenderer(frame_id_); + if (rend) { + mixer_ = &(rend->mixer()); + return mixer_; + } + } + return nullptr; +} + bool Camera::isRecording() { return io->feed()->isRecording(); } diff --git a/applications/gui2/src/modules/camera.hpp b/applications/gui2/src/modules/camera.hpp index d4da36f3b..bdcb6dcaa 100644 --- a/applications/gui2/src/modules/camera.hpp +++ b/applications/gui2/src/modules/camera.hpp @@ -7,6 +7,7 @@ #include <ftl/render/colouriser.hpp> #include <ftl/render/overlay.hpp> #include <ftl/codecs/touch.hpp> +#include <ftl/audio/mixer.hpp> namespace ftl { namespace gui2 { @@ -49,6 +50,7 @@ public: ftl::render::Colouriser* colouriser() { return colouriser_.get(); }; ftl::overlay::Overlay* overlay() { return overlay_.get(); } + ftl::audio::StereoMixerF<100> *mixer(); void drawOverlay(NVGcontext *ctx); @@ -90,6 +92,7 @@ private: std::map<ftl::data::Message,std::string> messages_; CameraView* view = nullptr; + ftl::audio::StereoMixerF<100> *mixer_ = nullptr; MUTEX mtx_; diff --git a/applications/gui2/src/views/camera.cpp b/applications/gui2/src/views/camera.cpp index 565381ebc..76ff8efab 100644 --- a/applications/gui2/src/views/camera.cpp +++ b/applications/gui2/src/views/camera.cpp @@ -204,7 +204,7 @@ MediaPanel::MediaPanel(nanogui::Widget *parent, ftl::gui2::Camera* ctrl) : this->setTheme(theme); // Volume control - button_volume = new ftl::gui2::VolumeButton(this); + button_volume = new ftl::gui2::VolumeButton(this, ctrl_->mixer()); button_volume->setValue(ctrl_->volume()); button_volume->setCallback([ctrl = ctrl_](float v){ ctrl->setVolume(v); }); diff --git a/applications/gui2/src/widgets/soundctrl.cpp b/applications/gui2/src/widgets/soundctrl.cpp index 87ca183a5..4a8d6a08d 100644 --- a/applications/gui2/src/widgets/soundctrl.cpp +++ b/applications/gui2/src/widgets/soundctrl.cpp @@ -9,8 +9,8 @@ using ftl::gui2::PopupButton; using ftl::gui2::VolumeButton; using ftl::gui2::Screen; -VolumeButton::VolumeButton(nanogui::Widget *parent) : - ftl::gui2::PopupButton(parent, "", ENTYPO_ICON_SOUND) { +VolumeButton::VolumeButton(nanogui::Widget *parent, ftl::audio::StereoMixerF<100> *mixer) : + ftl::gui2::PopupButton(parent, "", ENTYPO_ICON_SOUND), mixer_(mixer) { setChevronIcon(-1); muted_ = false; @@ -27,6 +27,42 @@ VolumeButton::VolumeButton(nanogui::Widget *parent) : setValue(value); if (cb_) { cb_(value); } }); + + if (mixer) { + auto *mixbut = new nanogui::Button(mPopup, "Mixer", ENTYPO_ICON_SOUND_MIX); + mPopup->setAnchorHeight(70); + + auto *mixer_widget = new nanogui::Widget(mPopup); + mixer_widget->setLayout(new nanogui::GroupLayout(0, 6, 14, 0)); + mixer_widget->setVisible(false); + + // Add mixer slider for each track in mixer. + for (int t=0; t<mixer->tracks(); ++t) { + auto *label = new nanogui::Label(mixer_widget, mixer->name(t)); + label->setFontSize(12); + auto *mixslider = new nanogui::Slider(mixer_widget); + mixslider->setHighlightColor(dynamic_cast<Screen*>(screen())->getColor("highlight1")); + mixslider->setHeight(20); + mixslider->setValue(mixer->gain(t)); + mixslider->setHighlightedRange({0.0f, mixer->gain(t)}); + + mixslider->setCallback([this,t,mixslider](float value) { + mixslider->setValue(value); + mixslider->setHighlightedRange({0.0f, value}); + mixer_->setGain(t, value); + }); + } + + mixbut->setCallback([this,mixer_widget]() { + mixer_widget->setVisible(!mixer_widget->visible()); + if (mixer_widget->visible()) { + mPopup->setAnchorHeight(70+mixer_widget->childCount()*20); + } else { + mPopup->setAnchorHeight(70); + } + screen()->performLayout(); + }); + } } VolumeButton::~VolumeButton() { diff --git a/applications/gui2/src/widgets/soundctrl.hpp b/applications/gui2/src/widgets/soundctrl.hpp index 1db40ec5c..5495edd5c 100644 --- a/applications/gui2/src/widgets/soundctrl.hpp +++ b/applications/gui2/src/widgets/soundctrl.hpp @@ -1,6 +1,7 @@ #pragma once #include <nanogui/entypo.h> +#include <ftl/audio/mixer.hpp> #include "popupbutton.hpp" @@ -9,7 +10,7 @@ namespace gui2 { class VolumeButton : public ftl::gui2::PopupButton { public: - VolumeButton(nanogui::Widget *parent); + VolumeButton(nanogui::Widget *parent, ftl::audio::StereoMixerF<100> *mixer); virtual ~VolumeButton(); // callback, new value passed in argument @@ -38,6 +39,8 @@ private: nanogui::Slider* slider_; std::function<void(float)> cb_; + ftl::audio::StereoMixerF<100> *mixer_; + float scroll_step_ = 0.02f; float value_; bool muted_; diff --git a/components/audio/include/ftl/audio/buffer.hpp b/components/audio/include/ftl/audio/buffer.hpp index 545e15bf1..e3001d63d 100644 --- a/components/audio/include/ftl/audio/buffer.hpp +++ b/components/audio/include/ftl/audio/buffer.hpp @@ -3,6 +3,7 @@ #include <vector> #include <cmath> +#include <Eigen/Eigen> //#define LOGURU_REPLACE_GLOG 1 //#include <loguru.hpp> @@ -36,6 +37,9 @@ class Buffer { float delay() const { return cur_delay_ / static_cast<float>(rate_); } + inline void setGain(float g) { gain_ = g; } + inline float gain() const { return gain_; } + virtual void reset() { cur_delay_ = req_delay_; } @@ -49,6 +53,7 @@ class Buffer { float req_delay_; int channels_; int frame_size_; + float gain_ = 1.0f; }; //static constexpr int kBufferCount = 100; @@ -78,12 +83,27 @@ class FixedBuffer : public ftl::audio::Buffer<T> { } inline void readFrame(T *d) { - T *out = d; + T* __restrict out = d; + //if ((size_t(out) & 0x1f) == 0) out_alignment_ = 32; + //else if ((size_t(out) & 0xf) == 0) out_alignment_ = 16; + //else if ((size_t(out) & 0x7) == 0) out_alignment_ = 8; + if (read_position_ < 0 || read_position_ >= write_position_-1) { for (size_t i=0; i<CHAN*FRAME; ++i) *out++ = 0; } else { - T *in = &data_[(read_position_++) % SIZE][0]; - for (size_t i=0; i<CHAN*FRAME; ++i) *out++ = *in++; + const T* __restrict in = data_[(read_position_++) % SIZE]; + + // 16 byte aligned, use SIMD intrinsics + if ((size_t(out) & 0xf) == 0) { + for (size_t i=0; i<CHAN*FRAME; i += 4) { + Eigen::Map<Eigen::Matrix<float,4,1>,Eigen::Aligned16> vout(out+i); + const Eigen::Map<const Eigen::Matrix<float,4,1>,Eigen::Aligned16> vin(in+i); + vout = vin*this->gain_; + } + // Not aligned + } else { + for (size_t i=0; i<CHAN*FRAME; ++i) *out++ = this->gain_ * (*in++); + } } } diff --git a/components/audio/include/ftl/audio/mixer.hpp b/components/audio/include/ftl/audio/mixer.hpp index b8e41d16e..5d496217a 100644 --- a/components/audio/include/ftl/audio/mixer.hpp +++ b/components/audio/include/ftl/audio/mixer.hpp @@ -21,20 +21,19 @@ namespace audio { template <typename T, int CHAN, int FRAME, int SIZE> class FixedMixer : public ftl::audio::Buffer<T> { public: - FixedMixer() : Buffer<T>(CHAN, FRAME, 44100), write_position_(0), read_position_(0), offset_(0) { resize(1); } - explicit FixedMixer(int tracks) : Buffer<T>(CHAN, FRAME, 44100), write_position_(0), read_position_(0), offset_(0) { resize(tracks); } - FixedMixer(int tracks, int rate) : Buffer<T>(CHAN, FRAME, rate), write_position_(0), read_position_(0), offset_(0) { resize(tracks); } + FixedMixer() : Buffer<T>(CHAN, FRAME, 44100) { } + explicit FixedMixer(int rate) : Buffer<T>(CHAN, FRAME, rate) { } inline int maxFrames() const { return SIZE; } inline void readFrame(T *d) { - T *out = d; + T* __restrict out = d; if (read_position_ >= write_position_) { - for (size_t i=0; i<CHAN*FRAME; ++i) *out++ = 0; + std::fill(out, out+CHAN*FRAME, T(0)); } else { - T *in = data_[(read_position_++) % SIZE]; - for (size_t i=0; i<CHAN*FRAME; ++i) *out++ = *in++; + const T* __restrict in = data_[(read_position_++) % SIZE]; + std::copy(in, in+CHAN*FRAME, out); } } @@ -49,7 +48,7 @@ class FixedMixer : public ftl::audio::Buffer<T> { void write(const std::vector<T> &in) override; inline void write(int track, const std::vector<T> &in) { - tracks_[track].write(in); + tracks_.at(track).write(in); } void mix(); @@ -58,7 +57,7 @@ class FixedMixer : public ftl::audio::Buffer<T> { void reset() override { Buffer<T>::reset(); - write_position_ = 0; //int(this->cur_delay_); + write_position_ = 0; read_position_ = 0; } @@ -66,22 +65,25 @@ class FixedMixer : public ftl::audio::Buffer<T> { inline int readPosition() const { return read_position_; } inline int tracks() const { return track_num_; } - inline void setDelay(int track, float d) { tracks_[track].setDelay(d); } - inline float delay(int track) const { return tracks_[track].delay(); } + inline void setDelay(int track, float d) { tracks_.at(track).setDelay(d); } + inline float delay(int track) const { return tracks_.at(track).delay(); } - inline void setGain(float g) { gain_ = g; } - inline float gain() const { return gain_; } + inline void setGain(int track, float g) { tracks_.at(track).setGain(g); } + inline float gain(int track) const { return tracks_.at(track).gain(); } - void resize(int tracks); + //void resize(int tracks); + + int add(const std::string &name); + + const std::string &name(int track) const { return names_.at(track); } private: int track_num_=0; - int write_position_; - int read_position_; - int offset_; - float gain_ = 1.0f; + int write_position_=0; + int read_position_=0; alignas(32) T data_[SIZE][CHAN*FRAME]; std::vector<ftl::audio::FixedBuffer<T,CHAN,FRAME,SIZE>> tracks_; + std::vector<std::string> names_; }; // ==== Implementations ======================================================== @@ -93,6 +95,8 @@ void FixedMixer<T,CHAN,FRAME,SIZE>::write(const std::vector<T> &in) { template <typename T, int CHAN, int FRAME, int SIZE> void FixedMixer<T,CHAN,FRAME,SIZE>::mix() { + if (track_num_ == 0) return; + // Add together up to most recent frame int min_write = std::numeric_limits<int>::max(); for (auto &t : tracks_) { @@ -112,10 +116,10 @@ void FixedMixer<T,CHAN,FRAME,SIZE>::mix() { // For each track, accumulate the samples for (auto &t : tracks_) { const Eigen::Map<Eigen::Matrix<float,8,1>,Eigen::Aligned32> v2(&t.data(wp)[i]); - v1 += v2; + v1 += t.gain()*v2; } - v1 *= gain_; + v1 *= this->gain_; } ++write_position_; @@ -127,12 +131,13 @@ void FixedMixer<T,CHAN,FRAME,SIZE>::read(std::vector<T> &out, int count) { out.resize(FRAME*count*CHAN); T *ptr = out.data(); for (int i=0; i<count; ++i) { + // TODO: Do mix here directly readFrame(ptr); ptr += FRAME*CHAN; } } -template <typename T, int CHAN, int FRAME, int SIZE> +/*template <typename T, int CHAN, int FRAME, int SIZE> void FixedMixer<T,CHAN,FRAME,SIZE>::resize(int t) { if (track_num_ == t) return; @@ -142,6 +147,14 @@ void FixedMixer<T,CHAN,FRAME,SIZE>::resize(int t) { auto &tr = tracks_.emplace_back(); tr.setWritePosition(write_position_); } +}*/ + +template <typename T, int CHAN, int FRAME, int SIZE> +int FixedMixer<T,CHAN,FRAME,SIZE>::add(const std::string &name) { + names_.push_back(name); + auto &tr = tracks_.emplace_back(); + tr.setWritePosition(write_position_); + return track_num_++; } // ==== Common forms =========================================================== diff --git a/components/audio/src/speaker.cpp b/components/audio/src/speaker.cpp index 4c17df9ec..7db2e8426 100644 --- a/components/audio/src/speaker.cpp +++ b/components/audio/src/speaker.cpp @@ -137,16 +137,7 @@ void Speaker::queue(int64_t ts, ftl::audio::Frame &frame) { //LOG(INFO) << "Buffer Fullness (" << ts << "): " << buffer_->size() << " - " << audio.size(); for (const auto &d : audio) { - if (volume_ != 1.0) { - auto data = d.data(); - for (auto &v : data) { - v = v * volume_; - } - buffer_->write(data); - } - else { - buffer_->write(d.data()); - } + buffer_->write(d.data()); } //LOG(INFO) << "Audio delay: " << buffer_.delay() << "s"; } @@ -164,6 +155,7 @@ void Speaker::setDelay(int64_t ms) { void Speaker::setVolume(float value) { // TODO: adjust volume using system mixer volume_ = std::max(0.0f, std::min(1.0f, value)); + if (buffer_) buffer_->setGain(volume_); } float Speaker::volume() { diff --git a/components/audio/test/mixer_unit.cpp b/components/audio/test/mixer_unit.cpp index 233914446..b32b9cdb4 100644 --- a/components/audio/test/mixer_unit.cpp +++ b/components/audio/test/mixer_unit.cpp @@ -5,7 +5,10 @@ using ftl::audio::StereoMixerF; TEST_CASE("Audio Mixer Stereo Float", "") { SECTION("Add two in sync tracks") { - StereoMixerF<100> mixer(2); + StereoMixerF<100> mixer; + + mixer.add("Track1"); + mixer.add("Track2"); // Three 960 sample stereo frames std::vector<float> in1(960*2*3); @@ -36,7 +39,10 @@ TEST_CASE("Audio Mixer Stereo Float", "") { } SECTION("Add two out of sync tracks") { - StereoMixerF<100> mixer(2); + StereoMixerF<100> mixer; + + mixer.add("Track1"); + mixer.add("Track2"); // Three 960 sample stereo frames std::vector<float> in1(960*2*3); @@ -89,7 +95,9 @@ TEST_CASE("Audio Mixer Stereo Float", "") { TEST_CASE("Audio Mixer Stereo Float Dynamic Tracks", "") { SECTION("Add one track after write") { - StereoMixerF<100> mixer(1); + StereoMixerF<100> mixer; + + mixer.add("Track1"); // Three 960 sample stereo frames std::vector<float> in1(960*2*3); @@ -104,7 +112,7 @@ TEST_CASE("Audio Mixer Stereo Float Dynamic Tracks", "") { std::vector<float> in2(960*2*3); for (int i=0; i<960*2*3; ++i) in2[i] = float(i)+2.0f; - mixer.resize(2); + mixer.add("Track2"); mixer.write(0, in1); mixer.write(1, in2); mixer.mix(); diff --git a/components/streams/include/ftl/streams/feed.hpp b/components/streams/include/ftl/streams/feed.hpp index 53bdffa23..62234701b 100644 --- a/components/streams/include/ftl/streams/feed.hpp +++ b/components/streams/include/ftl/streams/feed.hpp @@ -98,6 +98,18 @@ public: */ std::string getSourceURI(ftl::data::FrameID id); + /** + * Get the renderer used to generate a frameset. This will return a nullptr + * if the frameset is not from a local renderer. + */ + ftl::render::Source *getRenderer(ftl::data::FrameID id); + + /** + * Get the RGB-Depth source for a frame, if the source is a local device. + * It will return a nullptr if there is no local source object. + */ + ftl::rgbd::Source *getRGBD(ftl::data::FrameID id); + void remove(const std::string &str); void remove(uint32_t id); diff --git a/components/streams/include/ftl/streams/renderer.hpp b/components/streams/include/ftl/streams/renderer.hpp index c922fe20d..441fd32ce 100644 --- a/components/streams/include/ftl/streams/renderer.hpp +++ b/components/streams/include/ftl/streams/renderer.hpp @@ -6,6 +6,7 @@ #include <ftl/render/renderer.hpp> #include <ftl/render/CUDARender.hpp> #include <ftl/streams/feed.hpp> +#include <ftl/audio/mixer.hpp> namespace ftl { namespace render { @@ -28,6 +29,8 @@ class Source : public ftl::Configurable, public ftl::data::DiscreteSource { static bool supports(const std::string &uri); + ftl::audio::StereoMixerF<100> &mixer(); + private: ftl::stream::Feed *feed_; ftl::render::BaseSourceImpl *impl_; diff --git a/components/streams/src/baserender.hpp b/components/streams/src/baserender.hpp index b8d3e7093..51c6ce09c 100644 --- a/components/streams/src/baserender.hpp +++ b/components/streams/src/baserender.hpp @@ -5,6 +5,7 @@ #include <ftl/cuda_util.hpp> #include <ftl/rgbd/camera.hpp> #include <ftl/rgbd/frame.hpp> +#include <ftl/audio/mixer.hpp> namespace ftl{ namespace render { @@ -27,8 +28,11 @@ class BaseSourceImpl { ftl::render::Source *host() { return host_; } + inline ftl::audio::StereoMixerF<100> &mixer() { return mixer_; } + protected: ftl::render::Source *host_; + ftl::audio::StereoMixerF<100> mixer_; }; } diff --git a/components/streams/src/feed.cpp b/components/streams/src/feed.cpp index b6c15f2b9..5f71e7fcf 100644 --- a/components/streams/src/feed.cpp +++ b/components/streams/src/feed.cpp @@ -966,6 +966,15 @@ void Feed::render() { } } +ftl::render::Source *Feed::getRenderer(ftl::data::FrameID id) { + auto i = renderers_.find(id.frameset()); + if (i != renderers_.end()) { + return i->second; + } else { + return nullptr; + } +} + uint32_t Feed::getID(const std::string &source) { return fsid_lookup_.at(source); } diff --git a/components/streams/src/renderer.cpp b/components/streams/src/renderer.cpp index 5da8355ed..439ea2f2e 100644 --- a/components/streams/src/renderer.cpp +++ b/components/streams/src/renderer.cpp @@ -27,6 +27,11 @@ Source::~Source() { if (impl_) delete impl_; } +ftl::audio::StereoMixerF<100> &Source::mixer() { + if (!impl_) throw FTL_Error("No implementation"); + return impl_->mixer(); +} + bool Source::supports(const std::string &puri) { ftl::URI uri(puri); if (!uri.isValid() || uri.getScheme() != ftl::URI::SCHEME_DEVICE) return false; diff --git a/components/streams/src/renderers/openvr_render.cpp b/components/streams/src/renderers/openvr_render.cpp index 0f578740e..e5ac8c0f8 100644 --- a/components/streams/src/renderers/openvr_render.cpp +++ b/components/streams/src/renderers/openvr_render.cpp @@ -387,8 +387,9 @@ bool OpenVRRender::retrieve(ftl::data::Frame &frame_out) { if (f.hasChannel(Channel::AudioStereo)) { // Map a mixer track to this frame auto &mixmap = mixmap_[f.id().id]; - if (mixmap.track == -1) mixmap.track = tracks_++; - mixer_.resize(tracks_); + if (mixmap.track == -1) { + mixmap.track = mixer_.add(f.name()); + } // Do mix but must not mix same frame multiple times if (mixmap.last_timestamp != f.timestamp()) { diff --git a/components/streams/src/renderers/openvr_render.hpp b/components/streams/src/renderers/openvr_render.hpp index 1423dd8dc..9accd58a4 100644 --- a/components/streams/src/renderers/openvr_render.hpp +++ b/components/streams/src/renderers/openvr_render.hpp @@ -61,8 +61,6 @@ class OpenVRRender : public ftl::render::BaseSourceImpl { int track=-1; }; - int tracks_=0; - ftl::audio::StereoMixerF<100> mixer_; std::unordered_map<uint32_t, AudioMixerMapping> mixmap_; bool initVR(); diff --git a/components/streams/src/renderers/screen_render.cpp b/components/streams/src/renderers/screen_render.cpp index a125f770c..36da28dac 100644 --- a/components/streams/src/renderers/screen_render.cpp +++ b/components/streams/src/renderers/screen_render.cpp @@ -163,8 +163,9 @@ bool ScreenRender::retrieve(ftl::data::Frame &frame_out) { if (f.hasChannel(Channel::AudioStereo)) { // Map a mixer track to this frame auto &mixmap = mixmap_[f.id().id]; - if (mixmap.track == -1) mixmap.track = tracks_++; - mixer_.resize(tracks_); + if (mixmap.track == -1) { + mixmap.track = mixer_.add(f.name()); + } // Do mix but must not mix same frame multiple times if (mixmap.last_timestamp != f.timestamp()) { diff --git a/components/streams/src/renderers/screen_render.hpp b/components/streams/src/renderers/screen_render.hpp index a24bb7476..91a867cf0 100644 --- a/components/streams/src/renderers/screen_render.hpp +++ b/components/streams/src/renderers/screen_render.hpp @@ -6,7 +6,6 @@ #include <ftl/render/renderer.hpp> #include <ftl/render/CUDARender.hpp> #include <ftl/streams/feed.hpp> -#include <ftl/audio/mixer.hpp> #include "../baserender.hpp" @@ -42,8 +41,6 @@ class ScreenRender : public ftl::render::BaseSourceImpl { int track=-1; }; - int tracks_=0; - ftl::audio::StereoMixerF<100> mixer_; std::unordered_map<uint32_t, AudioMixerMapping> mixmap_; }; -- GitLab