@@ -180,6 +180,7 @@ public: | |||||
float getValue() const noexcept; | float getValue() const noexcept; | ||||
void setValue(float value, bool sendCallback = false) noexcept; | void setValue(float value, bool sendCallback = false) noexcept; | ||||
void setDefault(float def) noexcept; | |||||
void setStartPos(const Point<int>& startPos) noexcept; | void setStartPos(const Point<int>& startPos) noexcept; | ||||
void setStartPos(int x, int y) noexcept; | void setStartPos(int x, int y) noexcept; | ||||
@@ -203,7 +204,9 @@ private: | |||||
float fMaximum; | float fMaximum; | ||||
float fStep; | float fStep; | ||||
float fValue; | float fValue; | ||||
float fValueDef; | |||||
float fValueTmp; | float fValueTmp; | ||||
bool fUsingDefault; | |||||
bool fDragging; | bool fDragging; | ||||
bool fInverted; | bool fInverted; | ||||
@@ -24,9 +24,11 @@ | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// Forward class names | // Forward class names | ||||
#ifdef DISTRHO_DEFINES_H_INCLUDED | |||||
START_NAMESPACE_DISTRHO | START_NAMESPACE_DISTRHO | ||||
class UI; | class UI; | ||||
END_NAMESPACE_DISTRHO | END_NAMESPACE_DISTRHO | ||||
#endif | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
@@ -175,6 +177,22 @@ public: | |||||
oldSize(0, 0) {} | oldSize(0, 0) {} | ||||
}; | }; | ||||
/** | |||||
Widget position changed event. | |||||
@a pos The new absolute position of the widget. | |||||
@a oldPos The previous absolute position of the widget. | |||||
@see onPositionChanged | |||||
*/ | |||||
struct PositionChangedEvent { | |||||
Point<int> pos; | |||||
Point<int> oldPos; | |||||
/** Constuctor */ | |||||
PositionChangedEvent() noexcept | |||||
: pos(0, 0), | |||||
oldPos(0, 0) {} | |||||
}; | |||||
/** | /** | ||||
Constructor. | Constructor. | ||||
*/ | */ | ||||
@@ -362,6 +380,11 @@ protected: | |||||
*/ | */ | ||||
virtual void onResize(const ResizeEvent&); | virtual void onResize(const ResizeEvent&); | ||||
/** | |||||
A function called when the widget's absolute position is changed. | |||||
*/ | |||||
virtual void onPositionChanged(const PositionChangedEvent&); | |||||
private: | private: | ||||
struct PrivateData; | struct PrivateData; | ||||
PrivateData* const pData; | PrivateData* const pData; | ||||
@@ -373,7 +396,9 @@ private: | |||||
friend class NanoWidget; | friend class NanoWidget; | ||||
friend class Window; | friend class Window; | ||||
friend class StandaloneWindow; | friend class StandaloneWindow; | ||||
#ifdef DISTRHO_DEFINES_H_INCLUDED | |||||
friend class DISTRHO_NAMESPACE::UI; | friend class DISTRHO_NAMESPACE::UI; | ||||
#endif | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Widget) | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(Widget) | ||||
}; | }; | ||||
@@ -19,9 +19,11 @@ | |||||
#include "Geometry.hpp" | #include "Geometry.hpp" | ||||
#ifdef DISTRHO_DEFINES_H_INCLUDED | |||||
START_NAMESPACE_DISTRHO | START_NAMESPACE_DISTRHO | ||||
class UIExporter; | class UIExporter; | ||||
END_NAMESPACE_DISTRHO | END_NAMESPACE_DISTRHO | ||||
#endif | |||||
START_NAMESPACE_DGL | START_NAMESPACE_DGL | ||||
@@ -129,7 +131,9 @@ private: | |||||
friend class Application; | friend class Application; | ||||
friend class Widget; | friend class Widget; | ||||
friend class StandaloneWindow; | friend class StandaloneWindow; | ||||
#ifdef DISTRHO_DEFINES_H_INCLUDED | |||||
friend class DISTRHO_NAMESPACE::UIExporter; | friend class DISTRHO_NAMESPACE::UIExporter; | ||||
#endif | |||||
virtual void _addWidget(Widget* const widget); | virtual void _addWidget(Widget* const widget); | ||||
virtual void _removeWidget(Widget* const widget); | virtual void _removeWidget(Widget* const widget); | ||||
@@ -649,7 +649,9 @@ ImageSlider::ImageSlider(Window& parent, const Image& image) noexcept | |||||
fMaximum(1.0f), | fMaximum(1.0f), | ||||
fStep(0.0f), | fStep(0.0f), | ||||
fValue(0.5f), | fValue(0.5f), | ||||
fValueDef(fValue), | |||||
fValueTmp(fValue), | fValueTmp(fValue), | ||||
fUsingDefault(false), | |||||
fDragging(false), | fDragging(false), | ||||
fInverted(false), | fInverted(false), | ||||
fValueIsSet(false), | fValueIsSet(false), | ||||
@@ -670,7 +672,9 @@ ImageSlider::ImageSlider(Widget* widget, const Image& image) noexcept | |||||
fMaximum(1.0f), | fMaximum(1.0f), | ||||
fStep(0.0f), | fStep(0.0f), | ||||
fValue(0.5f), | fValue(0.5f), | ||||
fValueDef(fValue), | |||||
fValueTmp(fValue), | fValueTmp(fValue), | ||||
fUsingDefault(false), | |||||
fDragging(false), | fDragging(false), | ||||
fInverted(false), | fInverted(false), | ||||
fValueIsSet(false), | fValueIsSet(false), | ||||
@@ -743,6 +747,12 @@ void ImageSlider::setInverted(bool inverted) noexcept | |||||
repaint(); | repaint(); | ||||
} | } | ||||
void ImageSlider::setDefault(float value) noexcept | |||||
{ | |||||
fValueDef = value; | |||||
fUsingDefault = true; | |||||
} | |||||
void ImageSlider::setRange(float min, float max) noexcept | void ImageSlider::setRange(float min, float max) noexcept | ||||
{ | { | ||||
fMinimum = min; | fMinimum = min; | ||||
@@ -830,6 +840,13 @@ bool ImageSlider::onMouse(const MouseEvent& ev) | |||||
if (! fSliderArea.contains(ev.pos)) | if (! fSliderArea.contains(ev.pos)) | ||||
return false; | return false; | ||||
if ((ev.mod & kModifierShift) != 0 && fUsingDefault) | |||||
{ | |||||
setValue(fValueDef, true); | |||||
fValueTmp = fValue; | |||||
return true; | |||||
} | |||||
float vper; | float vper; | ||||
const int x = ev.pos.getX(); | const int x = ev.pos.getX(); | ||||
const int y = ev.pos.getY(); | const int y = ev.pos.getY(); | ||||
@@ -151,20 +151,12 @@ const Point<int>& Widget::getAbsolutePos() const noexcept | |||||
void Widget::setAbsoluteX(int x) noexcept | void Widget::setAbsoluteX(int x) noexcept | ||||
{ | { | ||||
if (pData->absolutePos.getX() == x) | |||||
return; | |||||
pData->absolutePos.setX(x); | |||||
pData->parent.repaint(); | |||||
setAbsolutePos(Point<int>(x, getAbsoluteY())); | |||||
} | } | ||||
void Widget::setAbsoluteY(int y) noexcept | void Widget::setAbsoluteY(int y) noexcept | ||||
{ | { | ||||
if (pData->absolutePos.getY() == y) | |||||
return; | |||||
pData->absolutePos.setY(y); | |||||
pData->parent.repaint(); | |||||
setAbsolutePos(Point<int>(getAbsoluteX(), y)); | |||||
} | } | ||||
void Widget::setAbsolutePos(int x, int y) noexcept | void Widget::setAbsolutePos(int x, int y) noexcept | ||||
@@ -177,7 +169,13 @@ void Widget::setAbsolutePos(const Point<int>& pos) noexcept | |||||
if (pData->absolutePos == pos) | if (pData->absolutePos == pos) | ||||
return; | return; | ||||
PositionChangedEvent ev; | |||||
ev.oldPos = pData->absolutePos; | |||||
ev.pos = pos; | |||||
pData->absolutePos = pos; | pData->absolutePos = pos; | ||||
onPositionChanged(ev); | |||||
pData->parent.repaint(); | pData->parent.repaint(); | ||||
} | } | ||||
@@ -245,6 +243,10 @@ void Widget::onResize(const ResizeEvent&) | |||||
{ | { | ||||
} | } | ||||
void Widget::onPositionChanged(const PositionChangedEvent&) | |||||
{ | |||||
} | |||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
END_NAMESPACE_DGL | END_NAMESPACE_DGL |
@@ -32,6 +32,8 @@ | |||||
#if defined(DISTRHO_OS_WINDOWS) | #if defined(DISTRHO_OS_WINDOWS) | ||||
# include "pugl/pugl_win.cpp" | # include "pugl/pugl_win.cpp" | ||||
# undef max | |||||
# undef min | |||||
#elif defined(DISTRHO_OS_MAC) | #elif defined(DISTRHO_OS_MAC) | ||||
# define PuglWindow DISTRHO_JOIN_MACRO(PuglWindow, DGL_NAMESPACE) | # define PuglWindow DISTRHO_JOIN_MACRO(PuglWindow, DGL_NAMESPACE) | ||||
# define PuglOpenGLView DISTRHO_JOIN_MACRO(PuglOpenGLView, DGL_NAMESPACE) | # define PuglOpenGLView DISTRHO_JOIN_MACRO(PuglOpenGLView, DGL_NAMESPACE) | ||||
@@ -573,7 +575,7 @@ struct Window::PrivateData { | |||||
#if defined(DISTRHO_OS_WINDOWS) | #if defined(DISTRHO_OS_WINDOWS) | ||||
const int winFlags = WS_POPUPWINDOW | WS_CAPTION | (fResizable ? WS_SIZEBOX : 0x0); | const int winFlags = WS_POPUPWINDOW | WS_CAPTION | (fResizable ? WS_SIZEBOX : 0x0); | ||||
RECT wr = { 0, 0, static_cast<long>(width), static_cast<long>(height) }; | |||||
RECT wr = { 0, 0, static_cast<LONG>(width), static_cast<LONG>(height) }; | |||||
AdjustWindowRectEx(&wr, fUsingEmbed ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST); | AdjustWindowRectEx(&wr, fUsingEmbed ? WS_CHILD : winFlags, FALSE, WS_EX_TOPMOST); | ||||
SetWindowPos(hwnd, 0, 0, 0, wr.right-wr.left, wr.bottom-wr.top, | SetWindowPos(hwnd, 0, 0, 0, wr.right-wr.left, wr.bottom-wr.top, | ||||
@@ -111,7 +111,11 @@ puglCreateWindow(PuglView* view, const char* title) | |||||
static int wc_count = 0; | static int wc_count = 0; | ||||
char classNameBuf[256]; | char classNameBuf[256]; | ||||
std::srand((std::time(NULL))); | std::srand((std::time(NULL))); | ||||
#ifdef __WINE__ | |||||
std::snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d-%d", title, std::rand(), ++wc_count); | |||||
#else | |||||
_snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d-%d", title, std::rand(), ++wc_count); | _snprintf(classNameBuf, sizeof(classNameBuf), "%s_%d-%d", title, std::rand(), ++wc_count); | ||||
#endif | |||||
classNameBuf[sizeof(classNameBuf)-1] = '\0'; | classNameBuf[sizeof(classNameBuf)-1] = '\0'; | ||||
impl->wc.style = CS_OWNDC; | impl->wc.style = CS_OWNDC; | ||||
@@ -26,13 +26,14 @@ | |||||
START_NAMESPACE_DISTRHO | START_NAMESPACE_DISTRHO | ||||
#if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||||
static const writeMidiFunc writeMidiCallback = nullptr; | |||||
#endif | |||||
#if DISTRHO_PLUGIN_HAS_UI | #if DISTRHO_PLUGIN_HAS_UI | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
// Carla UI | // Carla UI | ||||
#if ! DISTRHO_PLUGIN_WANT_MIDI_OUTPUT | |||||
static const writeMidiFunc writeMidiCallback = nullptr; | |||||
#endif | |||||
#if ! DISTRHO_PLUGIN_WANT_STATE | #if ! DISTRHO_PLUGIN_WANT_STATE | ||||
static const setStateFunc setStateCallback = nullptr; | static const setStateFunc setStateCallback = nullptr; | ||||
#endif | #endif | ||||
@@ -71,18 +71,21 @@ static const writeMidiFunc writeMidiCallback = nullptr; | |||||
void strncpy(char* const dst, const char* const src, const size_t size) | void strncpy(char* const dst, const char* const src, const size_t size) | ||||
{ | { | ||||
std::strncpy(dst, src, size-1); | |||||
DISTRHO_SAFE_ASSERT_RETURN(size > 0,); | |||||
std::memcpy(dst, src, std::min(std::strlen(src), size-1)); | |||||
dst[size-1] = '\0'; | dst[size-1] = '\0'; | ||||
} | } | ||||
void snprintf_param(char* const dst, const float value, const size_t size) | void snprintf_param(char* const dst, const float value, const size_t size) | ||||
{ | { | ||||
DISTRHO_SAFE_ASSERT_RETURN(size > 0,); | |||||
std::snprintf(dst, size-1, "%f", value); | std::snprintf(dst, size-1, "%f", value); | ||||
dst[size-1] = '\0'; | dst[size-1] = '\0'; | ||||
} | } | ||||
void snprintf_iparam(char* const dst, const int32_t value, const size_t size) | void snprintf_iparam(char* const dst, const int32_t value, const size_t size) | ||||
{ | { | ||||
DISTRHO_SAFE_ASSERT_RETURN(size > 0,); | |||||
std::snprintf(dst, size-1, "%d", value); | std::snprintf(dst, size-1, "%d", value); | ||||
dst[size-1] = '\0'; | dst[size-1] = '\0'; | ||||
} | } | ||||
@@ -23,12 +23,14 @@ START_NAMESPACE_DISTRHO | |||||
DistrhoPluginKars::DistrhoPluginKars() | DistrhoPluginKars::DistrhoPluginKars() | ||||
: Plugin(paramCount, 0, 0), // 0 programs, 0 states | : Plugin(paramCount, 0, 0), // 0 programs, 0 states | ||||
fSustain(false), | fSustain(false), | ||||
fRelease(0.01), | |||||
fVolume(75.0f), | |||||
fSampleRate(getSampleRate()), | fSampleRate(getSampleRate()), | ||||
fBlockStart(0) | fBlockStart(0) | ||||
{ | { | ||||
for (int i=kMaxNotes; --i >= 0;) | for (int i=kMaxNotes; --i >= 0;) | ||||
{ | { | ||||
fNotes[i].index = i; | |||||
fNotes[i].voice = i; | |||||
fNotes[i].setSampleRate(fSampleRate); | fNotes[i].setSampleRate(fSampleRate); | ||||
} | } | ||||
} | } | ||||
@@ -38,15 +40,35 @@ DistrhoPluginKars::DistrhoPluginKars() | |||||
void DistrhoPluginKars::initParameter(uint32_t index, Parameter& parameter) | void DistrhoPluginKars::initParameter(uint32_t index, Parameter& parameter) | ||||
{ | { | ||||
if (index != 0) | |||||
return; | |||||
parameter.hints = kParameterIsAutomable|kParameterIsBoolean; | |||||
parameter.name = "Sustain"; | |||||
parameter.symbol = "sustain"; | |||||
parameter.ranges.def = 0.0f; | |||||
parameter.ranges.min = 0.0f; | |||||
parameter.ranges.max = 1.0f; | |||||
switch (index) | |||||
{ | |||||
case paramSustain: | |||||
parameter.hints = kParameterIsAutomable|kParameterIsBoolean; | |||||
parameter.name = "Sustain"; | |||||
parameter.symbol = "sustain"; | |||||
parameter.ranges.def = 0.0f; | |||||
parameter.ranges.min = 0.0f; | |||||
parameter.ranges.max = 1.0f; | |||||
break; | |||||
case paramRelease: | |||||
parameter.hints = kParameterIsAutomable; | |||||
parameter.name = "Release"; | |||||
parameter.symbol = "release"; | |||||
parameter.unit = "s"; | |||||
parameter.ranges.def = 0.01f; | |||||
parameter.ranges.min = 0.0f; | |||||
parameter.ranges.max = 5.0f; | |||||
break; | |||||
case paramVolume: | |||||
parameter.hints = kParameterIsAutomable; | |||||
parameter.name = "Volume"; | |||||
parameter.symbol = "volume"; | |||||
parameter.unit = "%"; | |||||
parameter.ranges.def = 75.0f; | |||||
parameter.ranges.min = 0.0f; | |||||
parameter.ranges.max = 100.0f; | |||||
break; | |||||
} | |||||
} | } | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -54,18 +76,30 @@ void DistrhoPluginKars::initParameter(uint32_t index, Parameter& parameter) | |||||
float DistrhoPluginKars::getParameterValue(uint32_t index) const | float DistrhoPluginKars::getParameterValue(uint32_t index) const | ||||
{ | { | ||||
if (index != 0) | |||||
return 0.0f; | |||||
switch (index) | |||||
{ | |||||
case paramSustain: return fSustain ? 1.0f : 0.0f; | |||||
case paramRelease: return fRelease; | |||||
case paramVolume: return fVolume; | |||||
} | |||||
return fSustain ? 1.0f : 0.0f; | |||||
return 0.0f; | |||||
} | } | ||||
void DistrhoPluginKars::setParameterValue(uint32_t index, float value) | void DistrhoPluginKars::setParameterValue(uint32_t index, float value) | ||||
{ | { | ||||
if (index != 0) | |||||
return; | |||||
fSustain = value > 0.5f; | |||||
switch (index) | |||||
{ | |||||
case paramSustain: | |||||
fSustain = value > 0.5f; | |||||
break; | |||||
case paramRelease: | |||||
fRelease = value; | |||||
break; | |||||
case paramVolume: | |||||
fVolume = value; | |||||
break; | |||||
} | |||||
} | } | ||||
// ----------------------------------------------------------------------- | // ----------------------------------------------------------------------- | ||||
@@ -83,19 +117,112 @@ void DistrhoPluginKars::activate() | |||||
} | } | ||||
} | } | ||||
/** | |||||
Handy class to help keep audio buffer in sync with incoming MIDI events. | |||||
To use it, create a local variable (on the stack) and call nextEvent() until it returns false. | |||||
@code | |||||
for (AudioMidiSyncHelper amsh(outputs, frames, midiEvents, midiEventCount); amsh.nextEvent();) | |||||
{ | |||||
float* const outL = amsh.outputs[0]; | |||||
float* const outR = amsh.outputs[1]; | |||||
for (uint32_t i=0; i<amsh.midiEventCount; ++i) | |||||
{ | |||||
const MidiEvent& ev(amsh.midiEvents[i]); | |||||
// ... do something with the midi event | |||||
} | |||||
renderSynth(outL, outR, amsh.frames); | |||||
} | |||||
@endcode | |||||
Some important notes when using this class: | |||||
1. MidiEvent::frame retains its original value, but it is useless, do not use it. | |||||
2. The class variables names are be the same as the default ones in the run function. | |||||
Keep that in mind and try to avoid typos. :) | |||||
*/ | |||||
class AudioMidiSyncHelper { | |||||
public: | |||||
/** Parameters from the run function, adjusted for event sync */ | |||||
float** outputs; | |||||
uint32_t frames; | |||||
const MidiEvent* midiEvents; | |||||
uint32_t midiEventCount; | |||||
/** | |||||
Constructor, using values from the run function. | |||||
*/ | |||||
AudioMidiSyncHelper(float** const o, uint32_t f, const MidiEvent* m, uint32_t mc) | |||||
: outputs(o), | |||||
frames(0), | |||||
midiEvents(m), | |||||
midiEventCount(0), | |||||
remainingFrames(f), | |||||
remainingMidiEventCount(mc) {} | |||||
/** | |||||
Process a batch of events untill no more are available. | |||||
You must not read any more values from this class after this function returns false. | |||||
*/ | |||||
bool nextEvent() | |||||
{ | |||||
if (remainingMidiEventCount == 0) | |||||
{ | |||||
if (remainingFrames == 0) | |||||
return false; | |||||
for (uint32_t i=0; i<DISTRHO_PLUGIN_NUM_OUTPUTS; ++i) | |||||
outputs[i] += frames; | |||||
if (midiEventCount != 0) | |||||
midiEvents += midiEventCount; | |||||
frames = remainingFrames; | |||||
midiEvents = nullptr; | |||||
midiEventCount = 0; | |||||
remainingFrames = 0; | |||||
return true; | |||||
} | |||||
const uint32_t eventFrame = midiEvents[0].frame; | |||||
DISTRHO_SAFE_ASSERT_RETURN(eventFrame < remainingFrames, false); | |||||
midiEventCount = 1; | |||||
for (uint32_t i=1; i<remainingMidiEventCount; ++i) | |||||
{ | |||||
if (midiEvents[i].frame != eventFrame) | |||||
{ | |||||
midiEventCount = i; | |||||
break; | |||||
} | |||||
} | |||||
frames = remainingFrames - eventFrame; | |||||
remainingFrames -= frames; | |||||
remainingMidiEventCount -= midiEventCount; | |||||
return true; | |||||
} | |||||
private: | |||||
/** @internal */ | |||||
uint32_t remainingFrames; | |||||
uint32_t remainingMidiEventCount; | |||||
}; | |||||
void DistrhoPluginKars::run(const float**, float** outputs, uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount) | void DistrhoPluginKars::run(const float**, float** outputs, uint32_t frames, const MidiEvent* midiEvents, uint32_t midiEventCount) | ||||
{ | { | ||||
uint8_t note, velo; | uint8_t note, velo; | ||||
float* out = outputs[0]; | |||||
std::memset(outputs[0], 0, sizeof(float)*frames); | |||||
for (uint32_t count, pos=0, curEventIndex=0; pos<frames;) | |||||
for (AudioMidiSyncHelper amsh(outputs, frames, midiEvents, midiEventCount); amsh.nextEvent();) | |||||
{ | { | ||||
for (;curEventIndex < midiEventCount && pos >= midiEvents[curEventIndex].frame; ++curEventIndex) | |||||
for (uint32_t i=0; i<amsh.midiEventCount; ++i) | |||||
{ | { | ||||
if (midiEvents[curEventIndex].size > MidiEvent::kDataSize) | |||||
if (amsh.midiEvents[i].size > MidiEvent::kDataSize) | |||||
continue; | continue; | ||||
const uint8_t* data = midiEvents[curEventIndex].data; | |||||
const uint8_t* data = amsh.midiEvents[i].data; | |||||
const uint8_t status = data[0] & 0xF0; | const uint8_t status = data[0] & 0xF0; | ||||
switch (status) | switch (status) | ||||
@@ -106,7 +233,7 @@ void DistrhoPluginKars::run(const float**, float** outputs, uint32_t frames, con | |||||
DISTRHO_SAFE_ASSERT_BREAK(note < 128); // kMaxNotes | DISTRHO_SAFE_ASSERT_BREAK(note < 128); // kMaxNotes | ||||
if (velo > 0) | if (velo > 0) | ||||
{ | { | ||||
fNotes[note].on = fBlockStart + midiEvents[curEventIndex].frame; | |||||
fNotes[note].on = fBlockStart + amsh.midiEvents[i].frame; | |||||
fNotes[note].off = kNoteNull; | fNotes[note].off = kNoteNull; | ||||
fNotes[note].velocity = velo; | fNotes[note].velocity = velo; | ||||
break; | break; | ||||
@@ -115,35 +242,26 @@ void DistrhoPluginKars::run(const float**, float** outputs, uint32_t frames, con | |||||
case 0x80: | case 0x80: | ||||
note = data[1]; | note = data[1]; | ||||
DISTRHO_SAFE_ASSERT_BREAK(note < 128); // kMaxNotes | DISTRHO_SAFE_ASSERT_BREAK(note < 128); // kMaxNotes | ||||
fNotes[note].off = fBlockStart + midiEvents[curEventIndex].frame; | |||||
fNotes[note].off = fBlockStart + amsh.midiEvents[i].frame; | |||||
break; | break; | ||||
} | } | ||||
} | } | ||||
if (curEventIndex < midiEventCount && midiEvents[curEventIndex].frame < frames) | |||||
count = midiEvents[curEventIndex].frame - pos; | |||||
else | |||||
count = frames - pos; | |||||
std::memset(out+pos, 0, sizeof(float)*count); | |||||
//for (uint32_t i=0; i<count; ++i) | |||||
// out[pos + i] = 0.0f; | |||||
float* const out = amsh.outputs[0]; | |||||
for (int i=kMaxNotes; --i >= 0;) | for (int i=kMaxNotes; --i >= 0;) | ||||
{ | { | ||||
if (fNotes[i].on != kNoteNull) | if (fNotes[i].on != kNoteNull) | ||||
addSamples(out, i, pos, count); | |||||
addSamples(out, i, amsh.frames); | |||||
} | } | ||||
pos += count; | |||||
} | } | ||||
fBlockStart += frames; | fBlockStart += frames; | ||||
} | } | ||||
void DistrhoPluginKars::addSamples(float* out, int voice, uint32_t offset, uint32_t count) | |||||
void DistrhoPluginKars::addSamples(float* out, int voice, uint32_t frames) | |||||
{ | { | ||||
const uint32_t start = fBlockStart + offset; | |||||
const uint32_t start = fBlockStart; | |||||
Note& note(fNotes[voice]); | Note& note(fNotes[voice]); | ||||
@@ -162,7 +280,7 @@ void DistrhoPluginKars::addSamples(float* out, int voice, uint32_t offset, uint3 | |||||
float gain, sample; | float gain, sample; | ||||
uint32_t index, size; | uint32_t index, size; | ||||
for (uint32_t i=0, s=start-note.on; i<count; ++i, ++s) | |||||
for (uint32_t i=0, s=start-note.on; i<frames; ++i, ++s) | |||||
{ | { | ||||
gain = vgain; | gain = vgain; | ||||
@@ -170,8 +288,8 @@ void DistrhoPluginKars::addSamples(float* out, int voice, uint32_t offset, uint3 | |||||
{ | { | ||||
// reuse index and size to save some performance. | // reuse index and size to save some performance. | ||||
// actual values are release and dist | // actual values are release and dist | ||||
index = 1 + uint32_t(0.01 * fSampleRate); // release, not index | |||||
size = i + start - note.off; // dist, not size | |||||
index = 1 + uint32_t(fRelease * fSampleRate); // release, not index | |||||
size = i + start - note.off; // dist, not size | |||||
if (size > index) | if (size > index) | ||||
{ | { | ||||
@@ -198,7 +316,7 @@ void DistrhoPluginKars::addSamples(float* out, int voice, uint32_t offset, uint3 | |||||
note.wavetable[index] = sample/2; | note.wavetable[index] = sample/2; | ||||
} | } | ||||
out[offset+i] += gain * sample; | |||||
out[i] += gain * sample * (fVolume / 100.0f); | |||||
} | } | ||||
} | } | ||||
@@ -32,6 +32,8 @@ public: | |||||
enum Parameters | enum Parameters | ||||
{ | { | ||||
paramSustain = 0, | paramSustain = 0, | ||||
paramRelease, | |||||
paramVolume, | |||||
paramCount | paramCount | ||||
}; | }; | ||||
@@ -97,6 +99,8 @@ protected: | |||||
private: | private: | ||||
bool fSustain; | bool fSustain; | ||||
float fRelease; | |||||
float fVolume; | |||||
double fSampleRate; | double fSampleRate; | ||||
uint32_t fBlockStart; | uint32_t fBlockStart; | ||||
@@ -104,16 +108,16 @@ private: | |||||
uint32_t on; | uint32_t on; | ||||
uint32_t off; | uint32_t off; | ||||
uint8_t velocity; | uint8_t velocity; | ||||
float index; | |||||
float size; | |||||
int sizei; | |||||
float* wavetable; | |||||
float voice; | |||||
float size; | |||||
int sizei; | |||||
float* wavetable; | |||||
Note() noexcept | Note() noexcept | ||||
: on(kNoteNull), | : on(kNoteNull), | ||||
off(kNoteNull), | off(kNoteNull), | ||||
velocity(0), | velocity(0), | ||||
index(0.0f), | |||||
voice(0.0f), | |||||
size(0.0f), | size(0.0f), | ||||
wavetable(nullptr) {} | wavetable(nullptr) {} | ||||
@@ -131,7 +135,7 @@ private: | |||||
if (wavetable != nullptr) | if (wavetable != nullptr) | ||||
delete[] wavetable; | delete[] wavetable; | ||||
const float frequency = 440.0f * std::pow(2.0f, (index - 69.0f) / 12.0f); | |||||
const float frequency = 440.0f * std::pow(2.0f, (voice - 69.0f) / 12.0f); | |||||
size = sampleRate / frequency; | size = sampleRate / frequency; | ||||
sizei = int(size)+1; | sizei = int(size)+1; | ||||
wavetable = new float[sizei]; | wavetable = new float[sizei]; | ||||
@@ -140,7 +144,7 @@ private: | |||||
} fNotes[kMaxNotes]; | } fNotes[kMaxNotes]; | ||||
void addSamples(float* out, int voice, uint32_t offset, uint32_t count); | |||||
void addSamples(float* out, int voice, uint32_t frames); | |||||
DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(DistrhoPluginKars) | DISTRHO_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR(DistrhoPluginKars) | ||||
}; | }; | ||||
@@ -23,6 +23,12 @@ FILES_UI = \ | |||||
include ../../dpf/Makefile.plugins.mk | include ../../dpf/Makefile.plugins.mk | ||||
# -------------------------------------------------------------- | |||||
# Extra flags | |||||
BASE_FLAGS += $(shell pkg-config --cflags libprojectM) | |||||
LINK_FLAGS += $(shell pkg-config --libs libprojectM) -lpthread | |||||
# -------------------------------------------------------------- | # -------------------------------------------------------------- | ||||
# Enable all possible plugin types | # Enable all possible plugin types | ||||