Browse Source

Add buffer methods to ring buffer, change int types to size_t in ring buffer, add ScrollWidget::scrollTo()

tags/v0.6.0
Andrew Belt 6 years ago
parent
commit
8c1be8f270
6 changed files with 85 additions and 52 deletions
  1. +3
    -5
      compile.mk
  2. +61
    -40
      include/dsp/ringbuffer.hpp
  3. +1
    -0
      include/ui.hpp
  4. +2
    -2
      src/Core/AudioInterface.cpp
  5. +11
    -0
      src/app/ModuleBrowser.cpp
  6. +7
    -5
      src/ui/ScrollWidget.cpp

+ 3
- 5
compile.mk View File

@@ -1,10 +1,12 @@
ifdef VERSION ifdef VERSION
FLAGS += -DVERSION=$(VERSION) FLAGS += -DVERSION=$(VERSION)
endif endif

RACK_DIR ?= . RACK_DIR ?= .
include $(RACK_DIR)/arch.mk


# Generate dependency files alongside the object files # Generate dependency files alongside the object files
FLAGS += -MMD
FLAGS += -MMD -MP
FLAGS += -g FLAGS += -g
# Optimization # Optimization
FLAGS += -O3 -march=nocona -ffast-math -fno-finite-math-only FLAGS += -O3 -march=nocona -ffast-math -fno-finite-math-only
@@ -15,12 +17,9 @@ endif
CXXFLAGS += -std=c++11 CXXFLAGS += -std=c++11




include $(RACK_DIR)/arch.mk

ifeq ($(ARCH), lin) ifeq ($(ARCH), lin)
FLAGS += -DARCH_LIN FLAGS += -DARCH_LIN
endif endif

ifeq ($(ARCH), mac) ifeq ($(ARCH), mac)
FLAGS += -DARCH_MAC FLAGS += -DARCH_MAC
CXXFLAGS += -stdlib=libc++ CXXFLAGS += -stdlib=libc++
@@ -29,7 +28,6 @@ ifeq ($(ARCH), mac)
FLAGS += $(MAC_SDK_FLAGS) FLAGS += $(MAC_SDK_FLAGS)
LDFLAGS += $(MAC_SDK_FLAGS) LDFLAGS += $(MAC_SDK_FLAGS)
endif endif

ifeq ($(ARCH), win) ifeq ($(ARCH), win)
FLAGS += -DARCH_WIN FLAGS += -DARCH_WIN
FLAGS += -D_USE_MATH_DEFINES FLAGS += -D_USE_MATH_DEFINES


+ 61
- 40
include/dsp/ringbuffer.hpp View File

@@ -8,56 +8,76 @@ namespace rack {


/** A simple cyclic buffer. /** A simple cyclic buffer.
S must be a power of 2. S must be a power of 2.
push() is constant time O(1)
Thread-safe for single producers and consumers.
*/ */
template <typename T, int S>
template <typename T, size_t S>
struct RingBuffer { struct RingBuffer {
T data[S]; 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) { void push(T t) {
int i = mask(end++);
size_t i = mask(end++);
data[i] = t; 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() { T shift() {
return data[mask(start++)]; 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() { void clear() {
start = end; start = end;
} }
bool empty() const { bool empty() const {
return start >= end;
return start == end;
} }
bool full() const { bool full() const {
return end - start >= S;
return end - start == S;
} }
int size() const {
size_t size() const {
return end - start; return end - start;
} }
int capacity() const {
size_t capacity() const {
return S - size(); 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. /** 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. S must be a power of 2.
push() is constant time O(2) relative to RingBuffer
Thread-safe for single producers and consumers?
*/ */
template <typename T, int S>
template <typename T, size_t S>
struct DoubleRingBuffer { struct DoubleRingBuffer {
T data[S*2]; 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); return i & (S - 1);
} }
void push(T t) { void push(T t) {
int i = mask(end++);
size_t i = mask(end++);
data[i] = t; data[i] = t;
data[i + S] = t; data[i + S] = t;
} }
@@ -68,15 +88,15 @@ struct DoubleRingBuffer {
start = end; start = end;
} }
bool empty() const { bool empty() const {
return start >= end;
return start == end;
} }
bool full() const { bool full() const {
return end - start >= S;
return end - start == S;
} }
int size() const {
size_t size() const {
return end - start; return end - start;
} }
int capacity() const {
size_t capacity() const {
return S - size(); return S - size();
} }
/** Returns a pointer to S consecutive elements for appending. /** Returns a pointer to S consecutive elements for appending.
@@ -86,16 +106,16 @@ struct DoubleRingBuffer {
T *endData() { T *endData() {
return &data[mask(end)]; 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 // 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) { if (e1 > S) {
// Copy data backward from the doubled block to the main block // 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; end += n;
} }
@@ -105,7 +125,7 @@ struct DoubleRingBuffer {
const T *startData() const { const T *startData() const {
return &data[mask(start)]; return &data[mask(start)];
} }
void startIncr(int n) {
void startIncr(size_t n) {
start += 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. 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)). 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. For example, a float buffer of size 64 in a block of size 1024 is nearly as efficient as RingBuffer.
Not thread-safe.
*/ */
template <typename T, int S, int N>
template <typename T, size_t S, size_t N>
struct AppleRingBuffer { struct AppleRingBuffer {
T data[N]; T data[N];
int start = 0;
int end = 0;
size_t start = 0;
size_t end = 0;


void returnBuffer() { void returnBuffer() {
// move end block to beginning // 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); memmove(data, &data[start], sizeof(T) * s);
start = 0; start = 0;
end = s; end = s;
@@ -139,20 +160,20 @@ struct AppleRingBuffer {
return data[start++]; return data[start++];
} }
bool empty() const { bool empty() const {
return start >= end;
return start == end;
} }
bool full() const { bool full() const {
return end - start >= S;
return end - start == S;
} }
int size() const {
size_t size() const {
return end - start; return end - start;
} }
int capacity() const {
size_t capacity() const {
return S - size(); return S - size();
} }
/** Returns a pointer to S consecutive elements for appending, requesting to append n elements. /** 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) { if (end + n > N) {
returnBuffer(); returnBuffer();
} }
@@ -161,7 +182,7 @@ struct AppleRingBuffer {
/** Actually increments the end position /** Actually increments the end position
Must be called after endData(), and `n` must be at most the `n` passed to endData() 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; end += n;
} }
/** Returns a pointer to S consecutive elements for consumption /** Returns a pointer to S consecutive elements for consumption
@@ -170,7 +191,7 @@ struct AppleRingBuffer {
const T *startData() const { const T *startData() const {
return &data[start]; return &data[start];
} }
void startIncr(int n) {
void startIncr(size_t n) {
// This is valid as long as n < S // This is valid as long as n < S
start += n; start += n;
} }


+ 1
- 0
include/ui.hpp View File

@@ -183,6 +183,7 @@ struct ScrollWidget : OpaqueWidget {
Vec offset; Vec offset;


ScrollWidget(); ScrollWidget();
void scrollTo(Rect r);
void draw(NVGcontext *vg) override; void draw(NVGcontext *vg) override;
void step() override; void step() override;
void onMouseMove(EventMouseMove &e) override; void onMouseMove(EventMouseMove &e) override;


+ 2
- 2
src/Core/AudioInterface.cpp View File

@@ -58,7 +58,7 @@ struct AudioInterfaceIO : AudioIO {
if (numOutputs > 0) { if (numOutputs > 0) {
std::unique_lock<std::mutex> lock(audioMutex); std::unique_lock<std::mutex> lock(audioMutex);
auto cond = [&] { auto cond = [&] {
return outputBuffer.size() >= length;
return (outputBuffer.size() >= (size_t) length);
}; };
if (audioCv.wait_for(lock, audioTimeout, cond)) { if (audioCv.wait_for(lock, audioTimeout, cond)) {
// Consume audio block // Consume audio block
@@ -187,7 +187,7 @@ void AudioInterface::step() {
// Wait until outputs are needed // Wait until outputs are needed
std::unique_lock<std::mutex> lock(audioIO.engineMutex); std::unique_lock<std::mutex> lock(audioIO.engineMutex);
auto cond = [&] { auto cond = [&] {
return audioIO.outputBuffer.size() < audioIO.blockSize;
return (audioIO.outputBuffer.size() < (size_t) audioIO.blockSize);
}; };
if (audioIO.engineCv.wait_for(lock, audioTimeout, cond)) { if (audioIO.engineCv.wait_for(lock, audioTimeout, cond)) {
// Push converted output // Push converted output


+ 11
- 0
src/app/ModuleBrowser.cpp View File

@@ -258,6 +258,15 @@ struct BrowserList : List {
} }
return NULL; return NULL;
} }

void scrollSelected() {
BrowserListItem *item = getSelectedItem();
if (item) {
ScrollWidget *parentScroll = dynamic_cast<ScrollWidget*>(parent->parent);
if (parentScroll)
parentScroll->scrollTo(item->box);
}
}
}; };




@@ -477,10 +486,12 @@ void SearchModuleField::onKey(EventKey &e) {
} break; } break;
case GLFW_KEY_UP: { case GLFW_KEY_UP: {
moduleBrowser->moduleList->selected--; moduleBrowser->moduleList->selected--;
moduleBrowser->moduleList->scrollSelected();
e.consumed = true; e.consumed = true;
} break; } break;
case GLFW_KEY_DOWN: { case GLFW_KEY_DOWN: {
moduleBrowser->moduleList->selected++; moduleBrowser->moduleList->selected++;
moduleBrowser->moduleList->scrollSelected();
e.consumed = true; e.consumed = true;
} break; } break;
case GLFW_KEY_ENTER: { case GLFW_KEY_ENTER: {


+ 7
- 5
src/ui/ScrollWidget.cpp View File

@@ -5,9 +5,6 @@
namespace rack { namespace rack {




static const float SCROLL_SPEED = 1.3;


/** Parent must be a ScrollWidget */ /** Parent must be a ScrollWidget */
struct ScrollBar : OpaqueWidget { struct ScrollBar : OpaqueWidget {
enum Orientation { enum Orientation {
@@ -36,9 +33,9 @@ struct ScrollBar : OpaqueWidget {
ScrollWidget *scrollWidget = dynamic_cast<ScrollWidget*>(parent); ScrollWidget *scrollWidget = dynamic_cast<ScrollWidget*>(parent);
assert(scrollWidget); assert(scrollWidget);
if (orientation == HORIZONTAL) if (orientation == HORIZONTAL)
scrollWidget->offset.x += SCROLL_SPEED * e.mouseRel.x;
scrollWidget->offset.x += e.mouseRel.x;
else else
scrollWidget->offset.y += SCROLL_SPEED * e.mouseRel.y;
scrollWidget->offset.y += e.mouseRel.y;
} }


void onDragEnd(EventDragEnd &e) override { void onDragEnd(EventDragEnd &e) override {
@@ -63,6 +60,11 @@ ScrollWidget::ScrollWidget() {
addChild(verticalScrollBar); 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) { void ScrollWidget::draw(NVGcontext *vg) {
nvgScissor(vg, 0, 0, box.size.x, box.size.y); nvgScissor(vg, 0, 0, box.size.x, box.size.y);
Widget::draw(vg); Widget::draw(vg);


Loading…
Cancel
Save