From 8c1be8f270cdeb57ad2b5e6c7d2ee9ed9209947f Mon Sep 17 00:00:00 2001 From: Andrew Belt Date: Mon, 5 Mar 2018 20:37:37 -0500 Subject: [PATCH] Add buffer methods to ring buffer, change int types to size_t in ring buffer, add ScrollWidget::scrollTo() --- compile.mk | 8 ++- include/dsp/ringbuffer.hpp | 101 ++++++++++++++++++++++-------------- include/ui.hpp | 1 + src/Core/AudioInterface.cpp | 4 +- src/app/ModuleBrowser.cpp | 11 ++++ src/ui/ScrollWidget.cpp | 12 +++-- 6 files changed, 85 insertions(+), 52 deletions(-) diff --git a/compile.mk b/compile.mk index 358a60f5..45e126ad 100644 --- a/compile.mk +++ b/compile.mk @@ -1,10 +1,12 @@ ifdef VERSION FLAGS += -DVERSION=$(VERSION) endif + RACK_DIR ?= . +include $(RACK_DIR)/arch.mk # Generate dependency files alongside the object files -FLAGS += -MMD +FLAGS += -MMD -MP FLAGS += -g # Optimization FLAGS += -O3 -march=nocona -ffast-math -fno-finite-math-only @@ -15,12 +17,9 @@ endif CXXFLAGS += -std=c++11 -include $(RACK_DIR)/arch.mk - ifeq ($(ARCH), lin) FLAGS += -DARCH_LIN endif - ifeq ($(ARCH), mac) FLAGS += -DARCH_MAC CXXFLAGS += -stdlib=libc++ @@ -29,7 +28,6 @@ ifeq ($(ARCH), mac) FLAGS += $(MAC_SDK_FLAGS) LDFLAGS += $(MAC_SDK_FLAGS) endif - ifeq ($(ARCH), win) FLAGS += -DARCH_WIN FLAGS += -D_USE_MATH_DEFINES diff --git a/include/dsp/ringbuffer.hpp b/include/dsp/ringbuffer.hpp index 439c053d..73506e91 100644 --- a/include/dsp/ringbuffer.hpp +++ b/include/dsp/ringbuffer.hpp @@ -8,56 +8,76 @@ namespace rack { /** A simple cyclic buffer. S must be a power of 2. -push() is constant time O(1) +Thread-safe for single producers and consumers. */ -template +template struct RingBuffer { T data[S]; - int start = 0; - int end = 0; + size_t start = 0; + size_t end = 0; - int mask(int i) const { - return i & (S - 1); + size_t mask(size_t i) const { + return i & (S - 1); } void push(T t) { - int i = mask(end++); + size_t i = mask(end++); data[i] = t; } + void pushBuffer(const T *t, int n) { + size_t i = mask(end); + size_t e1 = i + n; + size_t e2 = (e1 < S) ? e1 : S; + memcpy(&data[i], t, sizeof(T) * (e2 - i)); + if (e1 > S) { + memcpy(data, &t[S - i], sizeof(T) * (e1 - S)); + } + end += n; + } T shift() { return data[mask(start++)]; } + void shiftBuffer(T *t, size_t n) { + size_t i = mask(start); + size_t s1 = i + n; + size_t s2 = (s1 < S) ? s1 : S; + memcpy(t, &data[i], sizeof(T) * (s2 - i)); + if (s1 > S) { + memcpy(&t[S - i], data, sizeof(T) * (s1 - S)); + } + start += n; + } void clear() { start = end; } bool empty() const { - return start >= end; + return start == end; } bool full() const { - return end - start >= S; + return end - start == S; } - int size() const { + size_t size() const { return end - start; } - int capacity() const { + size_t capacity() const { return S - size(); } }; /** A cyclic buffer which maintains a valid linear array of size S by keeping a copy of the buffer in adjacent memory. S must be a power of 2. -push() is constant time O(2) relative to RingBuffer +Thread-safe for single producers and consumers? */ -template +template struct DoubleRingBuffer { T data[S*2]; - int start = 0; - int end = 0; + size_t start = 0; + size_t end = 0; - int mask(int i) const { + size_t mask(size_t i) const { return i & (S - 1); } void push(T t) { - int i = mask(end++); + size_t i = mask(end++); data[i] = t; data[i + S] = t; } @@ -68,15 +88,15 @@ struct DoubleRingBuffer { start = end; } bool empty() const { - return start >= end; + return start == end; } bool full() const { - return end - start >= S; + return end - start == S; } - int size() const { + size_t size() const { return end - start; } - int capacity() const { + size_t capacity() const { return S - size(); } /** Returns a pointer to S consecutive elements for appending. @@ -86,16 +106,16 @@ struct DoubleRingBuffer { T *endData() { return &data[mask(end)]; } - void endIncr(int n) { - int e = mask(end); - int e1 = e + n; - int e2 = min(e1, S); + void endIncr(size_t n) { + size_t e = mask(end); + size_t e1 = e + n; + size_t e2 = (e1 < S) ? e1 : S; // Copy data forward - memcpy(data + S + e, data + e, sizeof(T) * (e2 - e)); + memcpy(&data[S + e], &data[e], sizeof(T) * (e2 - e)); if (e1 > S) { // Copy data backward from the doubled block to the main block - memcpy(data, data + S, sizeof(T) * (e1 - S)); + memcpy(data, &data[S], sizeof(T) * (e1 - S)); } end += n; } @@ -105,7 +125,7 @@ struct DoubleRingBuffer { const T *startData() const { return &data[mask(start)]; } - void startIncr(int n) { + void startIncr(size_t n) { start += n; } }; @@ -114,17 +134,18 @@ struct DoubleRingBuffer { The linear array of S elements are moved back to the start of the block once it outgrows past the end. This happens every N - S pushes, so the push() time is O(1 + S / (N - S)). For example, a float buffer of size 64 in a block of size 1024 is nearly as efficient as RingBuffer. +Not thread-safe. */ -template +template struct AppleRingBuffer { T data[N]; - int start = 0; - int end = 0; + size_t start = 0; + size_t end = 0; void returnBuffer() { // move end block to beginning - // may overlap, but that's okay - int s = size(); + // may overlap, but memmove handles that correctly + size_t s = size(); memmove(data, &data[start], sizeof(T) * s); start = 0; end = s; @@ -139,20 +160,20 @@ struct AppleRingBuffer { return data[start++]; } bool empty() const { - return start >= end; + return start == end; } bool full() const { - return end - start >= S; + return end - start == S; } - int size() const { + size_t size() const { return end - start; } - int capacity() const { + size_t capacity() const { return S - size(); } /** Returns a pointer to S consecutive elements for appending, requesting to append n elements. */ - T *endData(int n) { + T *endData(size_t n) { if (end + n > N) { returnBuffer(); } @@ -161,7 +182,7 @@ struct AppleRingBuffer { /** Actually increments the end position Must be called after endData(), and `n` must be at most the `n` passed to endData() */ - void endIncr(int n) { + void endIncr(size_t n) { end += n; } /** Returns a pointer to S consecutive elements for consumption @@ -170,7 +191,7 @@ struct AppleRingBuffer { const T *startData() const { return &data[start]; } - void startIncr(int n) { + void startIncr(size_t n) { // This is valid as long as n < S start += n; } diff --git a/include/ui.hpp b/include/ui.hpp index d70a41f8..e5938a07 100644 --- a/include/ui.hpp +++ b/include/ui.hpp @@ -183,6 +183,7 @@ struct ScrollWidget : OpaqueWidget { Vec offset; ScrollWidget(); + void scrollTo(Rect r); void draw(NVGcontext *vg) override; void step() override; void onMouseMove(EventMouseMove &e) override; diff --git a/src/Core/AudioInterface.cpp b/src/Core/AudioInterface.cpp index c7f4ab78..2359a428 100644 --- a/src/Core/AudioInterface.cpp +++ b/src/Core/AudioInterface.cpp @@ -58,7 +58,7 @@ struct AudioInterfaceIO : AudioIO { if (numOutputs > 0) { std::unique_lock lock(audioMutex); auto cond = [&] { - return outputBuffer.size() >= length; + return (outputBuffer.size() >= (size_t) length); }; if (audioCv.wait_for(lock, audioTimeout, cond)) { // Consume audio block @@ -187,7 +187,7 @@ void AudioInterface::step() { // Wait until outputs are needed std::unique_lock lock(audioIO.engineMutex); auto cond = [&] { - return audioIO.outputBuffer.size() < audioIO.blockSize; + return (audioIO.outputBuffer.size() < (size_t) audioIO.blockSize); }; if (audioIO.engineCv.wait_for(lock, audioTimeout, cond)) { // Push converted output diff --git a/src/app/ModuleBrowser.cpp b/src/app/ModuleBrowser.cpp index 8c1dbe82..c08ccdc7 100644 --- a/src/app/ModuleBrowser.cpp +++ b/src/app/ModuleBrowser.cpp @@ -258,6 +258,15 @@ struct BrowserList : List { } return NULL; } + + void scrollSelected() { + BrowserListItem *item = getSelectedItem(); + if (item) { + ScrollWidget *parentScroll = dynamic_cast(parent->parent); + if (parentScroll) + parentScroll->scrollTo(item->box); + } + } }; @@ -477,10 +486,12 @@ void SearchModuleField::onKey(EventKey &e) { } break; case GLFW_KEY_UP: { moduleBrowser->moduleList->selected--; + moduleBrowser->moduleList->scrollSelected(); e.consumed = true; } break; case GLFW_KEY_DOWN: { moduleBrowser->moduleList->selected++; + moduleBrowser->moduleList->scrollSelected(); e.consumed = true; } break; case GLFW_KEY_ENTER: { diff --git a/src/ui/ScrollWidget.cpp b/src/ui/ScrollWidget.cpp index 37e1b27e..312f0a98 100644 --- a/src/ui/ScrollWidget.cpp +++ b/src/ui/ScrollWidget.cpp @@ -5,9 +5,6 @@ namespace rack { -static const float SCROLL_SPEED = 1.3; - - /** Parent must be a ScrollWidget */ struct ScrollBar : OpaqueWidget { enum Orientation { @@ -36,9 +33,9 @@ struct ScrollBar : OpaqueWidget { ScrollWidget *scrollWidget = dynamic_cast(parent); assert(scrollWidget); if (orientation == HORIZONTAL) - scrollWidget->offset.x += SCROLL_SPEED * e.mouseRel.x; + scrollWidget->offset.x += e.mouseRel.x; else - scrollWidget->offset.y += SCROLL_SPEED * e.mouseRel.y; + scrollWidget->offset.y += e.mouseRel.y; } void onDragEnd(EventDragEnd &e) override { @@ -63,6 +60,11 @@ ScrollWidget::ScrollWidget() { addChild(verticalScrollBar); } +void ScrollWidget::scrollTo(Rect r) { + Rect bound = Rect::fromMinMax(r.getBottomRight().minus(box.size), r.pos); + offset = offset.clamp2(bound); +} + void ScrollWidget::draw(NVGcontext *vg) { nvgScissor(vg, 0, 0, box.size.x, box.size.y); Widget::draw(vg);