Checkpoint point, pre instantiation of supplementary LV2pedalboards.
@@ -7,9 +7,9 @@
|
||||
viewBox="0 0 16.933332 16.933334"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:export-filename="logo192.png"
|
||||
inkscape:export-xdpi="288"
|
||||
inkscape:export-ydpi="288"
|
||||
inkscape:export-filename="../vite/public/logo192.png"
|
||||
inkscape:export-xdpi="288.00006"
|
||||
inkscape:export-ydpi="288.00006"
|
||||
inkscape:version="1.4.3 (1:1.4.3+202512261035+0d15f75042)"
|
||||
sodipodi:docname="Pi-Icon-Inkscape.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
@@ -25,18 +25,18 @@
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="true"
|
||||
inkscape:document-units="px"
|
||||
showgrid="false"
|
||||
inkscape:lockguides="true"
|
||||
inkscape:zoom="14.609375"
|
||||
inkscape:cx="32"
|
||||
inkscape:cy="32"
|
||||
inkscape:window-width="2141"
|
||||
inkscape:window-height="1161"
|
||||
inkscape:zoom="3.1914063"
|
||||
inkscape:cx="61.101591"
|
||||
inkscape:cy="46.844553"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1043"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1">
|
||||
inkscape:current-layer="layer2">
|
||||
<inkscape:grid
|
||||
id="grid2"
|
||||
units="px"
|
||||
@@ -50,16 +50,55 @@
|
||||
opacity="0.14901961"
|
||||
empspacing="4"
|
||||
enabled="true"
|
||||
visible="true"
|
||||
visible="false"
|
||||
dotted="true" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs1" />
|
||||
id="defs1">
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB;"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter35"
|
||||
x="-0.081083745"
|
||||
y="-0.081083748"
|
||||
width="1.1621675"
|
||||
height="1.1790599">
|
||||
<feFlood
|
||||
result="flood"
|
||||
in="SourceGraphic"
|
||||
flood-opacity="0.670588"
|
||||
flood-color="rgb(0,0,0)"
|
||||
id="feFlood34" />
|
||||
<feGaussianBlur
|
||||
result="blur"
|
||||
in="SourceGraphic"
|
||||
stdDeviation="0.400000"
|
||||
id="feGaussianBlur34" />
|
||||
<feOffset
|
||||
result="offset"
|
||||
in="blur"
|
||||
dx="0.000000"
|
||||
dy="0.200000"
|
||||
id="feOffset34" />
|
||||
<feComposite
|
||||
result="comp1"
|
||||
operator="out"
|
||||
in="flood"
|
||||
in2="offset"
|
||||
id="feComposite34" />
|
||||
<feComposite
|
||||
result="comp2"
|
||||
operator="atop"
|
||||
in="comp1"
|
||||
in2="SourceGraphic"
|
||||
id="feComposite35" />
|
||||
</filter>
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
style="display:inline">
|
||||
style="display:none">
|
||||
<circle
|
||||
style="font-variation-settings:'GRAD' 15, 'opsz' 144, 'wdth' 132, 'wght' 200;fill:#5e2edf;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3"
|
||||
@@ -88,4 +127,20 @@
|
||||
d="M 12.164115,4.1470337 7.9597209,7.8558512 8.9829142,9.0702474 13.214697,5.3371419 C 12.920982,4.8932514 12.566824,4.4925879 12.164115,4.1470337 Z" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="Layer 2">
|
||||
<circle
|
||||
style="font-variation-settings:'GRAD' 15, 'opsz' 144, 'wdth' 132, 'wght' 200;display:inline;fill:#7f2edf;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle34"
|
||||
cx="8.4666653"
|
||||
cy="8.4666653"
|
||||
r="8.4666653" />
|
||||
<path
|
||||
id="path10"
|
||||
style="font-variation-settings:'GRAD' 15, 'opsz' 144, 'wdth' 132, 'wght' 200;baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.925474;stroke-linejoin:round;stroke-opacity:1;paint-order:stroke fill markers;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||
inkscape:label="Dial only"
|
||||
d="m 8.4589152,2.5468613 c -3.2597966,0 -5.9198054,2.6600088 -5.9198054,5.9198054 0,3.2597973 2.6600088,5.9198053 5.9198054,5.9198053 3.2597968,0 5.9198058,-2.660008 5.9198058,-5.9198053 0,-0.797827 -0.159653,-1.5596729 -0.448123,-2.2554373 l -0.382123,0.3366895 -1.250152,1.1023713 v 4.782e-4 c 0.05509,0.2630316 0.08417,0.5358673 0.08417,0.8158983 0,2.1754753 -1.748106,3.9221463 -3.9235808,3.9221463 -2.1754745,0 -3.9216678,-1.746671 -3.9216678,-3.9221463 0,-2.1754745 1.7461933,-3.9235808 3.9216678,-3.9235808 0.4532168,0 0.8876009,0.075865 1.2917591,0.2156917 L 10.998434,3.657841 11.38008,3.3211515 C 10.517327,2.8287984 9.5196885,2.5468613 8.4589152,2.5468613 Z M 12.305975,3.972531 7.9318813,7.8310695 8.9964706,9.0946117 13.39926,5.2107259 C 13.093696,4.7489266 12.724932,4.3320266 12.305975,3.972531 Z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.4 KiB After Width: | Height: | Size: 6.1 KiB |
@@ -7,9 +7,9 @@
|
||||
viewBox="0 0 16.933332 16.933334"
|
||||
version="1.1"
|
||||
id="svg1"
|
||||
inkscape:export-filename="../vite/public/logo512.png"
|
||||
inkscape:export-xdpi="768"
|
||||
inkscape:export-ydpi="768"
|
||||
inkscape:export-filename="../vite/public/logo192.png"
|
||||
inkscape:export-xdpi="288.00006"
|
||||
inkscape:export-ydpi="288.00006"
|
||||
inkscape:version="1.4.3 (1:1.4.3+202512261035+0d15f75042)"
|
||||
sodipodi:docname="Pi-Icon-Inkscape.svg"
|
||||
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
||||
@@ -25,18 +25,18 @@
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pagecheckerboard="0"
|
||||
inkscape:deskcolor="#d1d1d1"
|
||||
inkscape:document-units="mm"
|
||||
showgrid="true"
|
||||
inkscape:document-units="px"
|
||||
showgrid="false"
|
||||
inkscape:lockguides="true"
|
||||
inkscape:zoom="14.609375"
|
||||
inkscape:cx="32"
|
||||
inkscape:cy="32"
|
||||
inkscape:window-width="2141"
|
||||
inkscape:window-height="1161"
|
||||
inkscape:zoom="3.1914063"
|
||||
inkscape:cx="61.101591"
|
||||
inkscape:cy="46.844553"
|
||||
inkscape:window-width="1920"
|
||||
inkscape:window-height="1043"
|
||||
inkscape:window-x="0"
|
||||
inkscape:window-y="0"
|
||||
inkscape:window-maximized="1"
|
||||
inkscape:current-layer="layer1">
|
||||
inkscape:current-layer="layer2">
|
||||
<inkscape:grid
|
||||
id="grid2"
|
||||
units="px"
|
||||
@@ -50,25 +50,64 @@
|
||||
opacity="0.14901961"
|
||||
empspacing="4"
|
||||
enabled="true"
|
||||
visible="true"
|
||||
visible="false"
|
||||
dotted="true" />
|
||||
</sodipodi:namedview>
|
||||
<defs
|
||||
id="defs1" />
|
||||
id="defs1">
|
||||
<filter
|
||||
style="color-interpolation-filters:sRGB;"
|
||||
inkscape:label="Drop Shadow"
|
||||
id="filter35"
|
||||
x="-0.081083745"
|
||||
y="-0.081083748"
|
||||
width="1.1621675"
|
||||
height="1.1790599">
|
||||
<feFlood
|
||||
result="flood"
|
||||
in="SourceGraphic"
|
||||
flood-opacity="0.670588"
|
||||
flood-color="rgb(0,0,0)"
|
||||
id="feFlood34" />
|
||||
<feGaussianBlur
|
||||
result="blur"
|
||||
in="SourceGraphic"
|
||||
stdDeviation="0.400000"
|
||||
id="feGaussianBlur34" />
|
||||
<feOffset
|
||||
result="offset"
|
||||
in="blur"
|
||||
dx="0.000000"
|
||||
dy="0.200000"
|
||||
id="feOffset34" />
|
||||
<feComposite
|
||||
result="comp1"
|
||||
operator="out"
|
||||
in="flood"
|
||||
in2="offset"
|
||||
id="feComposite34" />
|
||||
<feComposite
|
||||
result="comp2"
|
||||
operator="atop"
|
||||
in="comp1"
|
||||
in2="SourceGraphic"
|
||||
id="feComposite35" />
|
||||
</filter>
|
||||
</defs>
|
||||
<g
|
||||
inkscape:label="Layer 1"
|
||||
inkscape:groupmode="layer"
|
||||
id="layer1"
|
||||
style="display:inline">
|
||||
style="display:none">
|
||||
<circle
|
||||
style="font-variation-settings:'GRAD' 15, 'opsz' 144, 'wdth' 132, 'wght' 200;fill:#5e2edf;fill-opacity:1;stroke:none;stroke-width:2.07592;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
style="font-variation-settings:'GRAD' 15, 'opsz' 144, 'wdth' 132, 'wght' 200;fill:#5e2edf;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path3"
|
||||
cx="8.4666653"
|
||||
cy="8.4666653"
|
||||
r="8.3036709" />
|
||||
r="8.4666653" />
|
||||
<g
|
||||
id="g5"
|
||||
transform="matrix(1.0057192,0,0,1.0057192,-0.04986822,-0.04842228)">
|
||||
transform="matrix(1.1241207,0,0,1.1241207,-1.0580763,-1.0508884)">
|
||||
<circle
|
||||
style="font-variation-settings:'GRAD' 15, 'opsz' 144, 'wdth' 132, 'wght' 200;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:1.5875;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="path4"
|
||||
@@ -76,7 +115,7 @@
|
||||
cy="8.4666653"
|
||||
r="4.8967609" />
|
||||
<ellipse
|
||||
style="font-variation-settings:'GRAD' 15, 'opsz' 144, 'wdth' 132, 'wght' 200;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.774042;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
style="font-variation-settings:'GRAD' 15, 'opsz' 144, 'wdth' 132, 'wght' 200;fill:none;fill-opacity:1;stroke:#ffffff;stroke-width:0.396875;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:2;stroke-dasharray:none;stroke-opacity:1;paint-order:stroke fill markers"
|
||||
id="path5"
|
||||
cx="8.4666662"
|
||||
cy="8.4666653"
|
||||
@@ -88,4 +127,20 @@
|
||||
d="M 12.164115,4.1470337 7.9597209,7.8558512 8.9829142,9.0702474 13.214697,5.3371419 C 12.920982,4.8932514 12.566824,4.4925879 12.164115,4.1470337 Z" />
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
inkscape:groupmode="layer"
|
||||
id="layer2"
|
||||
inkscape:label="Layer 2">
|
||||
<circle
|
||||
style="font-variation-settings:'GRAD' 15, 'opsz' 144, 'wdth' 132, 'wght' 200;display:inline;fill:#7f2edf;fill-opacity:1;stroke:none;stroke-width:2.11667;stroke-linecap:butt;stroke-linejoin:round;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="circle34"
|
||||
cx="8.4666653"
|
||||
cy="8.4666653"
|
||||
r="8.4666653" />
|
||||
<path
|
||||
id="path10"
|
||||
style="font-variation-settings:'GRAD' 15, 'opsz' 144, 'wdth' 132, 'wght' 200;baseline-shift:baseline;display:inline;overflow:visible;opacity:1;vector-effect:none;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.925474;stroke-linejoin:round;stroke-opacity:1;paint-order:stroke fill markers;enable-background:accumulate;stop-color:#000000;stop-opacity:1"
|
||||
inkscape:label="Dial only"
|
||||
d="m 8.4589152,2.5468613 c -3.2597966,0 -5.9198054,2.6600088 -5.9198054,5.9198054 0,3.2597973 2.6600088,5.9198053 5.9198054,5.9198053 3.2597968,0 5.9198058,-2.660008 5.9198058,-5.9198053 0,-0.797827 -0.159653,-1.5596729 -0.448123,-2.2554373 l -0.382123,0.3366895 -1.250152,1.1023713 v 4.782e-4 c 0.05509,0.2630316 0.08417,0.5358673 0.08417,0.8158983 0,2.1754753 -1.748106,3.9221463 -3.9235808,3.9221463 -2.1754745,0 -3.9216678,-1.746671 -3.9216678,-3.9221463 0,-2.1754745 1.7461933,-3.9235808 3.9216678,-3.9235808 0.4532168,0 0.8876009,0.075865 1.2917591,0.2156917 L 10.998434,3.657841 11.38008,3.3211515 C 10.517327,2.8287984 9.5196885,2.5468613 8.4589152,2.5468613 Z M 12.305975,3.972531 7.9318813,7.8310695 8.9964706,9.0946117 13.39926,5.2107259 C 13.093696,4.7489266 12.724932,4.3320266 12.305975,3.972531 Z" />
|
||||
</g>
|
||||
</svg>
|
||||
|
||||
|
Before Width: | Height: | Size: 3.5 KiB After Width: | Height: | Size: 6.1 KiB |
|
After Width: | Height: | Size: 9.7 KiB |
@@ -273,7 +273,7 @@ namespace pipedal
|
||||
auto inAvail = snd_pcm_avail_update(this->captureHandle);
|
||||
auto outAvail = snd_pcm_avail_update(this->playbackHandle);
|
||||
|
||||
auto total = (inAvail >= 0? inAvail: 0) + (outAvail >= 0 ? outAvail: 0) + framesInBuffer;
|
||||
auto total = (inAvail >= 0 ? inAvail : 0) + (outAvail >= 0 ? outAvail : 0) + framesInBuffer;
|
||||
bufferTraces[bufferTraceIndex++] = {
|
||||
time,
|
||||
inAvail,
|
||||
@@ -332,23 +332,26 @@ namespace pipedal
|
||||
|
||||
std::vector<std::vector<float>> allocatedBuffers;
|
||||
|
||||
std::set<int64_t> usedOutputChannels;
|
||||
std::vector<float*> deviceCaptureBuffers;
|
||||
std::vector<float*> devicePlaybackBuffers;
|
||||
std::vector<float *> deviceCaptureBuffers;
|
||||
std::vector<float *> devicePlaybackBuffers;
|
||||
float *zeroInputBuffer = nullptr;
|
||||
float *discardOutputBuffer = nullptr;
|
||||
std::vector<float *> mainCaptureBuffers;
|
||||
std::vector<float *> mainPlaybackBuffers;
|
||||
std::vector<float *> mainInsertPlaybackBuffers;
|
||||
std::vector<float *> mainVuPlaybackBuffers;
|
||||
|
||||
std::vector<float *> auxCaptureBuffers;
|
||||
std::vector<float *> auxPlaybackBuffers;
|
||||
std::vector<float *> auxVuPlaybackBuffers;
|
||||
|
||||
std::vector<float *> sendCaptureBuffers;
|
||||
std::vector<float *> sendPlaybackBuffers;
|
||||
|
||||
struct OutputBufferMix {
|
||||
struct OutputBufferMix
|
||||
{
|
||||
size_t outputChannel = 0;
|
||||
std::vector<float*> inputBuffers;
|
||||
std::vector<float *> inputBuffers;
|
||||
float *outputBuffer = nullptr;
|
||||
};
|
||||
|
||||
@@ -362,7 +365,9 @@ namespace pipedal
|
||||
void validate_capture_handle()
|
||||
{ // leftover debugging for a buffer overrun :-/
|
||||
#ifdef DEBUG
|
||||
if (snd_pcm_type(captureHandle) != SND_PCM_TYPE_HW)
|
||||
auto pcmType = snd_pcm_type(captureHandle);
|
||||
|
||||
if (pcmType != SND_PCM_TYPE_HW && pcmType != SND_PCM_TYPE_NULL)
|
||||
{
|
||||
throw std::runtime_error("Capture handle has been overwritten");
|
||||
}
|
||||
@@ -1130,27 +1135,9 @@ namespace pipedal
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void TestFormatEncodeDecode(snd_pcm_format_t captureFormat);
|
||||
|
||||
private:
|
||||
void AllocateBuffers(std::vector<float *> &buffers, size_t n)
|
||||
{
|
||||
buffers.resize(n);
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
buffers[i] = new float[this->bufferSize];
|
||||
for (size_t j = 0; j < this->bufferSize; ++j)
|
||||
{
|
||||
buffers[i][j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ChannelSelection channelSelection;
|
||||
bool open = false;
|
||||
virtual void Open(const JackServerSettings &jackServerSettings, const ChannelSelection &channelSelection)
|
||||
virtual void Open(const JackServerSettings &jackServerSettings, const ChannelSelection &channelSelection) override
|
||||
{
|
||||
this->isDummyDriver = jackServerSettings.IsDummyAudioDevice();
|
||||
terminateAudio_ = false;
|
||||
if (open)
|
||||
{
|
||||
@@ -1174,6 +1161,27 @@ namespace pipedal
|
||||
}
|
||||
}
|
||||
|
||||
public:
|
||||
void TestFormatEncodeDecode(snd_pcm_format_t captureFormat);
|
||||
|
||||
private:
|
||||
void AllocateBuffers(std::vector<float *> &buffers, size_t n)
|
||||
{
|
||||
buffers.resize(n);
|
||||
for (size_t i = 0; i < n; ++i)
|
||||
{
|
||||
buffers[i] = new float[this->bufferSize];
|
||||
for (size_t j = 0; j < this->bufferSize; ++j)
|
||||
{
|
||||
buffers[i][j] = 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ChannelSelection channelSelection;
|
||||
|
||||
bool open = false;
|
||||
|
||||
void RestartAlsa()
|
||||
{
|
||||
std::lock_guard lock{restartMutex};
|
||||
@@ -1200,13 +1208,13 @@ namespace pipedal
|
||||
throw std::runtime_error("Unable to restart the audio stream.");
|
||||
}
|
||||
int err;
|
||||
|
||||
|
||||
if ((err = snd_pcm_start(captureHandle)) < 0)
|
||||
{
|
||||
Lv2Log::error(SS("Unable to restart ALSA capture: " << snd_strerror(err)));
|
||||
throw PiPedalStateException("Unable to restart ALSA capture.");
|
||||
}
|
||||
TraceBufferPositions(0,'+');
|
||||
TraceBufferPositions(0, '+');
|
||||
audioRunning = true;
|
||||
}
|
||||
|
||||
@@ -1280,15 +1288,11 @@ namespace pipedal
|
||||
<< ", " << GetAlsaFormatDescription(this->captureFormat)
|
||||
<< ", " << this->sampleRate
|
||||
<< ", " << this->bufferSize << "x" << this->numberOfBuffers
|
||||
<< ", " << "device in: " << this->DeviceInputBufferCount()
|
||||
<< ", " << "device out: " << this->DeviceOutputBufferCount()
|
||||
<< ", main in: " << this->MainInputBufferCount()
|
||||
<< ", main out: " << this->MainOutputBufferCount()
|
||||
<< ", aux in: " << this->AuxInputBufferCount()
|
||||
<< ", aux out: " << this->AuxOutputBufferCount()
|
||||
<< ", send in: " << this->SendInputBufferCount()
|
||||
<< ", send out: " << this->SendOutputBufferCount()
|
||||
);
|
||||
<< ", " << "device: " << this->DeviceInputBufferCount() << "/" << this->DeviceOutputBufferCount()
|
||||
<< ", main: " << this->MainInputBufferCount() << "/" << this->MainOutputBufferCount()
|
||||
<< ", main ins: " << this->MainOutputBufferCount() << "/" << this->MainOutputBufferCount()
|
||||
<< ", aux: " << this->AuxInputBufferCount() << "/" << this->AuxOutputBufferCount()
|
||||
<< ", send: " << this->SendOutputBufferCount() << "/" << this->SendInputBufferCount());
|
||||
return result;
|
||||
}
|
||||
void PreparePlaybackFunctions(snd_pcm_format_t playbackFormat)
|
||||
@@ -1357,10 +1361,9 @@ namespace pipedal
|
||||
|
||||
int err;
|
||||
|
||||
std::string inputName = jackServerSettings.GetAlsaInputDevice();
|
||||
std::string inputName = jackServerSettings.GetAlsaInputDevice();
|
||||
std::string outputName = jackServerSettings.GetAlsaOutputDevice();
|
||||
|
||||
|
||||
this->numberOfBuffers = jackServerSettings.GetNumberOfBuffers();
|
||||
this->bufferSize = jackServerSettings.GetBufferSize();
|
||||
this->user_threshold = jackServerSettings.GetBufferSize();
|
||||
@@ -1403,7 +1406,7 @@ namespace pipedal
|
||||
{
|
||||
snd_pcm_nonblock(playbackHandle, 0);
|
||||
}
|
||||
|
||||
|
||||
this->alsa_device_name = inputName;
|
||||
err = snd_pcm_open(&captureHandle, inputName.c_str(), SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK);
|
||||
|
||||
@@ -1494,6 +1497,10 @@ namespace pipedal
|
||||
|
||||
memset(rawPlaybackBuffer.data(), 0, rawPlaybackBuffer.size());
|
||||
int retry = 0;
|
||||
if (this->isDummyDriver)
|
||||
{
|
||||
return; // dummy driver is insatiable.
|
||||
}
|
||||
while (true)
|
||||
{
|
||||
auto avail = snd_pcm_avail(this->playbackHandle);
|
||||
@@ -1513,7 +1520,7 @@ namespace pipedal
|
||||
{
|
||||
throw PiPedalStateException(SS("Audio playback failed. " << snd_strerror(err)));
|
||||
}
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(10));
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1));
|
||||
continue;
|
||||
}
|
||||
if (avail == 0)
|
||||
@@ -1658,7 +1665,7 @@ namespace pipedal
|
||||
auto frame_bytes = this->captureFrameSize;
|
||||
do
|
||||
{
|
||||
TraceBufferPositions(framesRead,'1');
|
||||
TraceBufferPositions(framesRead, '1');
|
||||
|
||||
framesRead = snd_pcm_readi(handle, buffer, frames);
|
||||
if (framesRead < 0)
|
||||
@@ -1676,7 +1683,7 @@ namespace pipedal
|
||||
}
|
||||
} while (frames > 0);
|
||||
|
||||
TraceBufferPositions(framesRead,'2');
|
||||
TraceBufferPositions(framesRead, '2');
|
||||
|
||||
return framesRead;
|
||||
}
|
||||
@@ -1727,6 +1734,7 @@ namespace pipedal
|
||||
}
|
||||
|
||||
private:
|
||||
bool isDummyDriver = false;
|
||||
long WriteBuffer(snd_pcm_t *handle, uint8_t *buf, size_t frames)
|
||||
{
|
||||
long framesRead;
|
||||
@@ -1827,17 +1835,30 @@ namespace pipedal
|
||||
// Perform any neccessary mixing of outputs.
|
||||
for (auto &bufferMix : outputBufferMixes)
|
||||
{
|
||||
float *outputBuffer = bufferMix.outputBuffer;
|
||||
for (auto &mixBuffer : bufferMix.inputBuffers)
|
||||
float * PIPEDAL_RESTRICT outputBuffer = bufferMix.outputBuffer;
|
||||
float * PIPEDAL_RESTRICT inputBuffer = bufferMix.inputBuffers[0];
|
||||
for (size_t i = 0; i < framesRead; ++i)
|
||||
{
|
||||
outputBuffer[i] = inputBuffer[i];
|
||||
}
|
||||
for (size_t nInput = 1; nInput < bufferMix.inputBuffers.size(); ++nInput)
|
||||
{
|
||||
float * PIPEDAL_RESTRICT inputBuffer = bufferMix.inputBuffers[nInput];
|
||||
for (size_t i = 0; i < framesRead; ++i)
|
||||
{
|
||||
outputBuffer[i] += mixBuffer[i];
|
||||
outputBuffer[i] += inputBuffer[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// final downmix.
|
||||
(this->*copyOutputFn)(framesRead);
|
||||
|
||||
if (this->driverHost)
|
||||
{
|
||||
driverHost->OnRealtimeUpdateDeviceVus(framesRead);
|
||||
}
|
||||
|
||||
cpuUse.AddSample(ProfileCategory::Driver);
|
||||
// process.
|
||||
|
||||
@@ -1850,6 +1871,10 @@ namespace pipedal
|
||||
recover_from_output_underrun(captureHandle, playbackHandle, err, framesRead);
|
||||
framesRead = 0;
|
||||
}
|
||||
if (isDummyDriver)
|
||||
{
|
||||
std::this_thread::sleep_for(std::chrono::milliseconds(1000 / 30));
|
||||
}
|
||||
cpuUse.AddSample(ProfileCategory::Write);
|
||||
}
|
||||
}
|
||||
@@ -1889,41 +1914,39 @@ namespace pipedal
|
||||
|
||||
bool alsaActive = false;
|
||||
|
||||
|
||||
PIPEDAL_NON_INLINE void AllocateInputChannels(
|
||||
const std::vector<int64_t> &channelSelection,
|
||||
std::vector<float*> &channelBuffers
|
||||
) {
|
||||
std::vector<float *> &channelBuffers)
|
||||
{
|
||||
size_t nChannels = channelSelection.size();
|
||||
if (nChannels == 0)
|
||||
{
|
||||
channelBuffers.resize(1);
|
||||
channelBuffers[0] = zeroInputBuffer;
|
||||
channelBuffers.resize(0);
|
||||
return;
|
||||
}
|
||||
|
||||
channelBuffers.resize(nChannels);
|
||||
for (size_t i = 0; i < nChannels; ++i)
|
||||
for (size_t i = 0; i < nChannels; ++i)
|
||||
{
|
||||
int64_t deviceChannel = channelSelection[i];
|
||||
if (deviceChannel == -1 || deviceChannel >= captureChannels)
|
||||
if (deviceChannel == -1 || deviceChannel >= captureChannels)
|
||||
{
|
||||
channelBuffers[i] = zeroInputBuffer;
|
||||
}
|
||||
else
|
||||
}
|
||||
else
|
||||
{
|
||||
channelBuffers[i] = deviceCaptureBuffers[deviceChannel];
|
||||
channelBuffers[i] = deviceCaptureBuffers[deviceChannel];
|
||||
}
|
||||
}
|
||||
}
|
||||
PIPEDAL_NON_INLINE void AddMixBuffer(int64_t outputChannel, float *mixBuffer)
|
||||
PIPEDAL_NON_INLINE float *AddMixBuffer(int64_t outputChannel, float *mixBuffer)
|
||||
{
|
||||
for (auto&bufferMix: outputBufferMixes)
|
||||
for (auto &bufferMix : outputBufferMixes)
|
||||
{
|
||||
if (bufferMix.outputChannel == outputChannel)
|
||||
{
|
||||
bufferMix.inputBuffers.push_back(mixBuffer);
|
||||
return;
|
||||
return bufferMix.outputBuffer;
|
||||
}
|
||||
}
|
||||
OutputBufferMix newMix;
|
||||
@@ -1931,53 +1954,103 @@ namespace pipedal
|
||||
newMix.outputBuffer = devicePlaybackBuffers[outputChannel];
|
||||
newMix.inputBuffers.push_back(mixBuffer);
|
||||
outputBufferMixes.push_back(std::move(newMix));
|
||||
return devicePlaybackBuffers[outputChannel];
|
||||
}
|
||||
PIPEDAL_NON_INLINE void AllocateOutputChannels(
|
||||
const std::vector<int64_t> &channelSelection,
|
||||
std::vector<float*> &channelBuffers
|
||||
) {
|
||||
std::vector<float *> &channelBuffers)
|
||||
{
|
||||
size_t nChannels = channelSelection.size();
|
||||
|
||||
if (nChannels == 0)
|
||||
{
|
||||
channelBuffers.resize(1);
|
||||
if (discardOutputBuffer != nullptr)
|
||||
{
|
||||
discardOutputBuffer = AllocateAudioBuffer();
|
||||
}
|
||||
channelBuffers[0] = discardOutputBuffer;
|
||||
channelBuffers.resize(0);
|
||||
return;
|
||||
}
|
||||
channelBuffers.resize(nChannels);
|
||||
for (size_t i = 0; i < nChannels; ++i)
|
||||
for (size_t i = 0; i < nChannels; ++i)
|
||||
{
|
||||
int64_t deviceChannel = channelSelection[i];
|
||||
if (deviceChannel == -1 || deviceChannel >= playbackChannels)
|
||||
if (deviceChannel == -1)
|
||||
{
|
||||
if (discardOutputBuffer == nullptr)
|
||||
channelBuffers[i] = this->GetDiscardOutputBuffer();
|
||||
;
|
||||
}
|
||||
else
|
||||
{
|
||||
float *mixBuffer = AllocateAudioBuffer();
|
||||
channelBuffers[i] = mixBuffer;
|
||||
}
|
||||
}
|
||||
}
|
||||
PIPEDAL_NON_INLINE void AllocateOutputChannels(
|
||||
const std::vector<int64_t> &channelSelection,
|
||||
std::vector<float *> &channelBuffers,
|
||||
std::vector<float *> &vuBuffers,
|
||||
const std::set<int64_t> &summedChannels)
|
||||
{
|
||||
size_t nChannels = channelSelection.size();
|
||||
|
||||
if (nChannels == 0)
|
||||
{
|
||||
channelBuffers.resize(0);
|
||||
vuBuffers.resize(0);
|
||||
return;
|
||||
}
|
||||
channelBuffers.resize(nChannels);
|
||||
vuBuffers.resize(nChannels);
|
||||
for (size_t i = 0; i < nChannels; ++i)
|
||||
{
|
||||
int64_t deviceChannel = channelSelection[i];
|
||||
if (deviceChannel == -1 || deviceChannel >= playbackChannels)
|
||||
{
|
||||
channelBuffers[i] = GetDiscardOutputBuffer();
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!summedChannels.contains(deviceChannel))
|
||||
{
|
||||
discardOutputBuffer = AllocateAudioBuffer();
|
||||
vuBuffers[i] = channelBuffers[i] = this->devicePlaybackBuffers[deviceChannel];
|
||||
}
|
||||
channelBuffers[i] = discardOutputBuffer;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!usedOutputChannels.contains(deviceChannel))
|
||||
else
|
||||
{
|
||||
usedOutputChannels.insert(deviceChannel);
|
||||
channelBuffers[i] = deviceCaptureBuffers[deviceChannel];
|
||||
|
||||
} else {
|
||||
// Used once already. We need to mix down.
|
||||
float *mixBuffer = AllocateAudioBuffer();
|
||||
channelBuffers[i] = mixBuffer;
|
||||
AddMixBuffer(deviceChannel, mixBuffer);
|
||||
vuBuffers[i] = AddMixBuffer(deviceChannel, mixBuffer);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
std::set<int64_t> ComputeSummedChannels()
|
||||
{
|
||||
std::set<int64_t> usedOutputChannels;
|
||||
std::set<int64_t> result;
|
||||
for (auto ch : channelSelection.mainOutputChannels())
|
||||
{
|
||||
if (usedOutputChannels.contains(ch))
|
||||
{
|
||||
result.insert(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
usedOutputChannels.insert(ch);
|
||||
}
|
||||
}
|
||||
for (auto ch : channelSelection.auxOutputChannels())
|
||||
{
|
||||
if (usedOutputChannels.contains(ch))
|
||||
{
|
||||
result.insert(ch);
|
||||
}
|
||||
else
|
||||
{
|
||||
usedOutputChannels.insert(ch);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
bool activated = false;
|
||||
PIPEDAL_NON_INLINE virtual void Activate()
|
||||
@@ -1990,10 +2063,8 @@ namespace pipedal
|
||||
activated = true;
|
||||
|
||||
// Reset previously allocated buffers.
|
||||
usedOutputChannels.clear();
|
||||
allocatedBuffers.resize(0);
|
||||
|
||||
|
||||
// Allocate device capture buffers.
|
||||
zeroInputBuffer = AllocateAudioBuffer();
|
||||
deviceCaptureBuffers.resize(captureChannels);
|
||||
@@ -2007,27 +2078,33 @@ namespace pipedal
|
||||
devicePlaybackBuffers[i] = AllocateAudioBuffer();
|
||||
}
|
||||
|
||||
std::set<int64_t> summedOutputChannels = ComputeSummedChannels();
|
||||
|
||||
AllocateInputChannels(
|
||||
channelSelection.mainInputChannels(),
|
||||
this->mainCaptureBuffers);
|
||||
AllocateOutputChannels(
|
||||
channelSelection.mainOutputChannels(),
|
||||
this->mainPlaybackBuffers
|
||||
);
|
||||
this->mainPlaybackBuffers);
|
||||
AllocateOutputChannels(
|
||||
channelSelection.mainOutputChannels(),
|
||||
this->mainInsertPlaybackBuffers,
|
||||
this->mainVuPlaybackBuffers,
|
||||
summedOutputChannels);
|
||||
AllocateInputChannels(
|
||||
channelSelection.auxInputChannels(),
|
||||
this->auxCaptureBuffers);
|
||||
AllocateOutputChannels(
|
||||
channelSelection.auxOutputChannels(),
|
||||
this->auxPlaybackBuffers
|
||||
);
|
||||
this->auxPlaybackBuffers,
|
||||
this->auxVuPlaybackBuffers,
|
||||
summedOutputChannels);
|
||||
AllocateInputChannels(
|
||||
channelSelection.sendInputChannels(),
|
||||
this->sendCaptureBuffers);
|
||||
AllocateOutputChannels(
|
||||
channelSelection.sendOutputChannels(),
|
||||
this->sendPlaybackBuffers
|
||||
);
|
||||
this->sendPlaybackBuffers);
|
||||
|
||||
audioThread = std::make_unique<std::jthread>([this]()
|
||||
{ AudioThread(); });
|
||||
@@ -2058,23 +2135,74 @@ namespace pipedal
|
||||
AlsaSequencer::ptr alsaSequencer;
|
||||
|
||||
public:
|
||||
virtual const ChannelSelection &GetChannelSelection() const override
|
||||
{
|
||||
return channelSelection;
|
||||
}
|
||||
|
||||
virtual void SetAlsaSequencer(AlsaSequencer::ptr alsaSequencer) override
|
||||
{
|
||||
this->alsaSequencer = alsaSequencer;
|
||||
}
|
||||
|
||||
virtual size_t DeviceInputBufferCount() const override {
|
||||
virtual size_t DeviceInputBufferCount() const override
|
||||
{
|
||||
return deviceCaptureBuffers.size();
|
||||
}
|
||||
virtual size_t DeviceOutputBufferCount() const override {
|
||||
virtual size_t DeviceOutputBufferCount() const override
|
||||
{
|
||||
return devicePlaybackBuffers.size();
|
||||
}
|
||||
|
||||
virtual float *GetDeviceInputBuffer(size_t channel) const override
|
||||
{
|
||||
if (channel >= deviceCaptureBuffers.size())
|
||||
return nullptr;
|
||||
return deviceCaptureBuffers[channel];
|
||||
}
|
||||
virtual float *GetDeviceOutputBuffer(size_t channel) const override
|
||||
{
|
||||
if (channel >= devicePlaybackBuffers.size())
|
||||
return nullptr;
|
||||
return devicePlaybackBuffers[channel];
|
||||
}
|
||||
|
||||
virtual float *GetZeroInputBuffer()
|
||||
{
|
||||
if (zeroInputBuffer == nullptr)
|
||||
{
|
||||
zeroInputBuffer = AllocateAudioBuffer();
|
||||
}
|
||||
return zeroInputBuffer;
|
||||
}
|
||||
virtual float *GetDiscardOutputBuffer()
|
||||
{
|
||||
if (discardOutputBuffer == nullptr)
|
||||
{
|
||||
discardOutputBuffer = AllocateAudioBuffer();
|
||||
}
|
||||
return discardOutputBuffer;
|
||||
}
|
||||
|
||||
virtual std::vector<float *> &DeviceInputBuffers() override { return this->deviceCaptureBuffers; }
|
||||
virtual std::vector<float *> &DeviceOutputBuffers() override { return this->devicePlaybackBuffers; }
|
||||
|
||||
virtual std::vector<float *> &MainInputBuffers() override { return this->mainCaptureBuffers; }
|
||||
virtual std::vector<float *> &MainOutputBuffers() override { return this->mainPlaybackBuffers; }
|
||||
virtual std::vector<float *> &MainInsertOutputBuffers() override { return this->mainInsertPlaybackBuffers; }
|
||||
virtual std::vector<float *> &MainVuOutputBuffers() override { return this->mainVuPlaybackBuffers; }
|
||||
|
||||
virtual std::vector<float *> &AuxInputBuffers() override { return this->auxCaptureBuffers; }
|
||||
virtual std::vector<float *> &AuxOutputBuffers() override { return this->auxPlaybackBuffers; }
|
||||
virtual std::vector<float *> &AuxVuOutputBuffers() override { return this->auxVuPlaybackBuffers; }
|
||||
|
||||
virtual const std::vector<float *> &SendInputBuffers() const override { return this->sendCaptureBuffers; }
|
||||
virtual const std::vector<float *> &SendOutputBuffers() const override { return this->sendPlaybackBuffers; }
|
||||
|
||||
virtual size_t MainInputBufferCount() const { return mainCaptureBuffers.size(); }
|
||||
virtual float *GetMainInputBuffer(size_t channel) override
|
||||
{
|
||||
if (channel >= (int64_t)mainCaptureBuffers.size())
|
||||
if (channel >= (int64_t)mainCaptureBuffers.size())
|
||||
{
|
||||
throw std::runtime_error("Argument out of range.");
|
||||
}
|
||||
@@ -2085,8 +2213,10 @@ namespace pipedal
|
||||
{
|
||||
return auxCaptureBuffers[channel];
|
||||
}
|
||||
virtual size_t AuxOutputBufferCount() const {
|
||||
return auxPlaybackBuffers.size(); }
|
||||
virtual size_t AuxOutputBufferCount() const
|
||||
{
|
||||
return auxPlaybackBuffers.size();
|
||||
}
|
||||
virtual float *GetAuxOutputBuffer(size_t channel) override
|
||||
{
|
||||
return auxPlaybackBuffers[channel];
|
||||
@@ -2116,11 +2246,25 @@ namespace pipedal
|
||||
{
|
||||
return mainPlaybackBuffers[channel];
|
||||
}
|
||||
virtual size_t MainInsertOutputBufferCount() const override
|
||||
{
|
||||
return mainInsertPlaybackBuffers.size();
|
||||
}
|
||||
virtual float *GetMainInsertOutputBuffer(size_t channel) const
|
||||
{
|
||||
if (channel >= (int64_t)mainCaptureBuffers.size())
|
||||
{
|
||||
throw std::runtime_error("GetMainInsertOutputBuffer: Argument out of range.");
|
||||
}
|
||||
|
||||
float* AllocateAudioBuffer() {
|
||||
return mainInsertPlaybackBuffers[channel];
|
||||
}
|
||||
|
||||
float *AllocateAudioBuffer()
|
||||
{
|
||||
std::vector<float> buffer;
|
||||
buffer.resize(this->bufferSize);
|
||||
float*pBuffer = buffer.data();
|
||||
float *pBuffer = buffer.data();
|
||||
allocatedBuffers.push_back(std::move(buffer));
|
||||
return pBuffer;
|
||||
}
|
||||
@@ -2173,19 +2317,19 @@ namespace pipedal
|
||||
std::vector<std::string> &inputAudioPorts,
|
||||
std::vector<std::string> &outputAudioPorts)
|
||||
{
|
||||
if (jackServerSettings.IsDummyAudioDevice())
|
||||
{
|
||||
auto nChannels = GetDummyAudioChannels(jackServerSettings.GetAlsaInputDevice());
|
||||
// if (jackServerSettings.IsDummyAudioDevice())
|
||||
// {
|
||||
// auto nChannels = GetDummyAudioChannels(jackServerSettings.GetAlsaInputDevice());
|
||||
|
||||
inputAudioPorts.clear();
|
||||
outputAudioPorts.clear();
|
||||
for (uint32_t i = 0; i < nChannels; ++i)
|
||||
{
|
||||
inputAudioPorts.push_back(std::string(SS("system::capture_" << i)));
|
||||
outputAudioPorts.push_back(std::string(SS("system::playback_" << i)));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
// inputAudioPorts.clear();
|
||||
// outputAudioPorts.clear();
|
||||
// for (uint32_t i = 0; i < nChannels; ++i)
|
||||
// {
|
||||
// inputAudioPorts.push_back(std::string(SS("system::capture_" << i)));
|
||||
// outputAudioPorts.push_back(std::string(SS("system::playback_" << i)));
|
||||
// }
|
||||
// return true;
|
||||
// }
|
||||
|
||||
snd_pcm_t *playbackHandle = nullptr;
|
||||
snd_pcm_t *captureHandle = nullptr;
|
||||
@@ -2422,7 +2566,7 @@ namespace pipedal
|
||||
}
|
||||
}
|
||||
|
||||
void AlsaFormatEncodeDecodeTest(AudioDriverHost *testDriverHost)
|
||||
void test::AlsaFormatEncodeDecodeTest(AudioDriverHost *testDriverHost)
|
||||
{
|
||||
static snd_pcm_format_t formats[] = {
|
||||
snd_pcm_format_t::SND_PCM_FORMAT_S16_LE,
|
||||
@@ -2444,7 +2588,7 @@ namespace pipedal
|
||||
alsaDriver->TestFormatEncodeDecode(format);
|
||||
}
|
||||
}
|
||||
void MidiDecoderTest()
|
||||
void test::MidiDecoderTest()
|
||||
{
|
||||
#ifdef JUNK
|
||||
AlsaDriverImpl::AlsaMidiDeviceImpl midiState;
|
||||
@@ -2513,37 +2657,41 @@ namespace pipedal
|
||||
{
|
||||
using namespace std;
|
||||
int savedPrecision = cout.precision();
|
||||
auto savedFlags = cout.flags();
|
||||
auto savedFlags = cout.flags();
|
||||
|
||||
size_t ix = bufferTraceIndex;
|
||||
if (ix < nEntries)
|
||||
{
|
||||
ix = ix + bufferTraces.size()-nEntries;
|
||||
} else {
|
||||
ix = ix + bufferTraces.size() - nEntries;
|
||||
}
|
||||
else
|
||||
{
|
||||
ix -= nEntries;
|
||||
}
|
||||
uint64_t t0;
|
||||
if (bufferTraceIndex == 0) {
|
||||
t0 = bufferTraces[bufferTraces.size()-1].time;
|
||||
} else {
|
||||
t0 = bufferTraces[bufferTraceIndex-1].time;
|
||||
if (bufferTraceIndex == 0)
|
||||
{
|
||||
t0 = bufferTraces[bufferTraces.size() - 1].time;
|
||||
}
|
||||
else
|
||||
{
|
||||
t0 = bufferTraces[bufferTraceIndex - 1].time;
|
||||
}
|
||||
while (ix != bufferTraceIndex)
|
||||
{
|
||||
auto&bufferTrace = bufferTraces[ix];
|
||||
auto &bufferTrace = bufferTraces[ix];
|
||||
|
||||
if (bufferTrace.time != 0)
|
||||
{
|
||||
int64_t dt = (int64_t)bufferTrace.time - (int64_t)t0;
|
||||
|
||||
|
||||
cout << bufferTrace.code << " "
|
||||
<< fixed << setprecision(3) << dt*0.001
|
||||
<< " " << "inAvail: " << bufferTrace.inAvail
|
||||
<< " " << "outAvail: " << bufferTrace.outAvail
|
||||
<< " " << "buffered: " << bufferTrace.buffered
|
||||
<< " " << "total: " << bufferTrace.total
|
||||
<< endl;
|
||||
cout << bufferTrace.code << " "
|
||||
<< fixed << setprecision(3) << dt * 0.001
|
||||
<< " " << "inAvail: " << bufferTrace.inAvail
|
||||
<< " " << "outAvail: " << bufferTrace.outAvail
|
||||
<< " " << "buffered: " << bufferTrace.buffered
|
||||
<< " " << "total: " << bufferTrace.total
|
||||
<< endl;
|
||||
}
|
||||
|
||||
++ix;
|
||||
@@ -2557,4 +2705,51 @@ namespace pipedal
|
||||
cout.flags(savedFlags);
|
||||
}
|
||||
|
||||
AlsaDeviceInfo MakeDummyDeviceInfo(uint32_t channels)
|
||||
{
|
||||
AlsaDeviceInfo result;
|
||||
constexpr int DUMMY_DEVICE_ID_OFFSET = 100974;
|
||||
result.cardId_ = DUMMY_DEVICE_ID_OFFSET + channels;
|
||||
result.id_ = SS("dummy:channels_" << channels);
|
||||
result.name_ = SS("Dummy Device (" << channels << " channels)");
|
||||
result.longName_ = result.name_;
|
||||
result.sampleRates_.push_back(44100);
|
||||
result.sampleRates_.push_back(48000);
|
||||
result.minBufferSize_ = 16;
|
||||
result.maxBufferSize_ = 1024;
|
||||
result.supportsCapture_ = true;
|
||||
result.supportsPlayback_ = true;
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t GetDummyAudioChannels(const std::string &deviceName)
|
||||
{
|
||||
uint32_t channels;
|
||||
int pos = deviceName.find_last_of('_');
|
||||
if (pos == std::string::npos)
|
||||
{
|
||||
throw std::runtime_error("Invalid dummy device name");
|
||||
}
|
||||
std::istringstream ss(deviceName.substr(pos + 1));
|
||||
ss >> channels;
|
||||
return channels;
|
||||
}
|
||||
AudioDriver *CreateDummyAudioDriver(AudioDriverHost *driverHost, const JackServerSettings &jackServerSettings, const ChannelSelection &channelSelection)
|
||||
{
|
||||
auto dummyServerSettings = jackServerSettings;
|
||||
dummyServerSettings.UseDummyAudioDevice();
|
||||
auto dummyChannelSelection = channelSelection;
|
||||
AudioDriver *driver = new AlsaDriverImpl(driverHost);
|
||||
try
|
||||
{
|
||||
driver->Open(jackServerSettings, channelSelection);
|
||||
return driver;
|
||||
}
|
||||
catch (const std::exception &e)
|
||||
{
|
||||
delete driver;
|
||||
throw;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
@@ -38,10 +38,17 @@ namespace pipedal {
|
||||
|
||||
AudioDriver* CreateAlsaDriver(AudioDriverHost*driverHost);
|
||||
|
||||
// test only.
|
||||
void AlsaFormatEncodeDecodeTest(AudioDriverHost*driverHost);
|
||||
void MidiDecoderTest();
|
||||
namespace test {
|
||||
// Private use, test only.
|
||||
void AlsaFormatEncodeDecodeTest(AudioDriverHost*driverHost);
|
||||
void MidiDecoderTest();
|
||||
}
|
||||
|
||||
void FreeAlsaGlobals(); // for valgrind. Free the Alsa configuration cache.
|
||||
|
||||
// AlsaDeviceInfo MakeDummyDeviceInfo(uint32_t channels);
|
||||
uint32_t GetDummyAudioChannels(const std::string &deviceName);
|
||||
// std::shared_ptr<AudioDriver> CreateDummyAudioDriver(AudioDriverHost*driverHost,const std::string&deviceId);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -63,6 +63,8 @@ public:
|
||||
delete[] outputBuffers;
|
||||
}
|
||||
|
||||
bool OnRealtimeUpdateDeviceVus(size_t nFrames) override { return false; }
|
||||
|
||||
void initializeChannelSelection(JackConfiguration &jackConfiguration, ChannelSelection &channelSelection)
|
||||
{
|
||||
size_t nInputs = std::max(jackConfiguration.inputAudioPorts().size(),size_t(2));
|
||||
@@ -85,7 +87,7 @@ public:
|
||||
void Test()
|
||||
{
|
||||
|
||||
AlsaFormatEncodeDecodeTest(this);
|
||||
test::AlsaFormatEncodeDecodeTest(this);
|
||||
|
||||
JackServerSettings serverSettings("hw:M2","hw:M2", 48000, 32, 3);
|
||||
|
||||
@@ -295,5 +297,5 @@ TEST_CASE("alsa_midi_test", "[alsa_midi_test]")
|
||||
{
|
||||
AlsaTester alsaDriver(AlsaTester::TestType::Oscillator);
|
||||
|
||||
MidiDecoderTest();
|
||||
test::MidiDecoderTest();
|
||||
}
|
||||
|
||||
@@ -27,6 +27,7 @@
|
||||
#include <functional>
|
||||
#include "AlsaSequencer.hpp"
|
||||
#include "MidiEvent.hpp"
|
||||
#include "DeviceVus.hpp"
|
||||
|
||||
|
||||
|
||||
@@ -37,15 +38,14 @@ namespace pipedal {
|
||||
|
||||
class ChannelSelection;
|
||||
|
||||
|
||||
class AudioDriverHost {
|
||||
public:
|
||||
virtual void OnProcess(size_t nFrames) = 0;
|
||||
virtual bool OnRealtimeUpdateDeviceVus(size_t nFrames) = 0;
|
||||
|
||||
virtual void OnUnderrun() = 0;
|
||||
virtual void OnAlsaDriverStopped() = 0;
|
||||
virtual void OnAudioTerminated() = 0;
|
||||
|
||||
|
||||
};
|
||||
class AudioDriver {
|
||||
public:
|
||||
@@ -60,12 +60,33 @@ namespace pipedal {
|
||||
virtual size_t GetMidiInputEventCount() = 0;
|
||||
virtual MidiEvent*GetMidiEvents() = 0;
|
||||
|
||||
virtual const ChannelSelection &GetChannelSelection() const = 0;
|
||||
|
||||
|
||||
virtual std::vector<float*> &DeviceInputBuffers() = 0;
|
||||
virtual std::vector<float*> &DeviceOutputBuffers() = 0;
|
||||
|
||||
virtual std::vector<float*> &MainInputBuffers() = 0;
|
||||
virtual std::vector<float*> &MainOutputBuffers() = 0;
|
||||
virtual std::vector<float*>&MainInsertOutputBuffers() = 0;
|
||||
virtual std::vector<float*>&MainVuOutputBuffers() = 0;
|
||||
|
||||
virtual std::vector<float*>&AuxInputBuffers() = 0;
|
||||
virtual std::vector<float*>&AuxOutputBuffers() = 0;
|
||||
virtual std::vector<float*>&AuxVuOutputBuffers() = 0;
|
||||
|
||||
virtual size_t DeviceInputBufferCount() const = 0;
|
||||
virtual size_t DeviceOutputBufferCount() const = 0;
|
||||
virtual float* GetDeviceInputBuffer(size_t channel) const = 0;
|
||||
virtual float* GetDeviceOutputBuffer(size_t channel) const = 0;
|
||||
|
||||
virtual size_t MainInputBufferCount() const = 0;
|
||||
virtual float*GetMainInputBuffer(size_t channel) = 0;
|
||||
virtual size_t MainOutputBufferCount() const = 0;
|
||||
virtual float*GetMainOutputBuffer(size_t channe) = 0;
|
||||
virtual float*GetMainOutputBuffer(size_t channel) = 0;
|
||||
|
||||
virtual size_t MainInsertOutputBufferCount() const = 0;
|
||||
virtual float*GetMainInsertOutputBuffer(size_t channe) const = 0;
|
||||
|
||||
|
||||
virtual size_t AuxInputBufferCount() const = 0;
|
||||
@@ -75,9 +96,13 @@ namespace pipedal {
|
||||
|
||||
virtual size_t SendInputBufferCount() const = 0;
|
||||
virtual float*GetSendInputBuffer(size_t channel) = 0;
|
||||
virtual const std::vector<float*>&SendInputBuffers() const = 0;
|
||||
virtual size_t SendOutputBufferCount() const = 0;
|
||||
virtual float*GetSendOutputBuffer(size_t channel) = 0;
|
||||
virtual const std::vector<float*>&SendOutputBuffers() const = 0;
|
||||
|
||||
virtual float*GetZeroInputBuffer() = 0;
|
||||
virtual float*GetDiscardOutputBuffer() = 0;
|
||||
|
||||
virtual void Open(const JackServerSettings & jackServerSettings,const ChannelSelection &channelSelection) = 0;
|
||||
virtual void SetAlsaSequencer(AlsaSequencer::ptr alsaSequencer) = 0;
|
||||
|
||||
@@ -348,7 +348,8 @@ bool SystemMidiBinding::IsTriggered(const MidiEvent &event)
|
||||
if (currentBinding.switchControlType() == SwitchControlTypeT::TRIGGER_ON_RISING_EDGE)
|
||||
{
|
||||
result = value >= 0x64 && lastControlValue < 0x64;
|
||||
} else if (currentBinding.switchControlType() == SwitchControlTypeT::TRIGGER_ON_ANY)
|
||||
}
|
||||
else if (currentBinding.switchControlType() == SwitchControlTypeT::TRIGGER_ON_ANY)
|
||||
{
|
||||
result = true;
|
||||
}
|
||||
@@ -529,8 +530,6 @@ private:
|
||||
std::shared_ptr<Lv2Pedalboard> currentMainInsertPedalboard;
|
||||
std::shared_ptr<Lv2Pedalboard> currentAuxInsertPedalboard;
|
||||
|
||||
|
||||
|
||||
std::vector<std::shared_ptr<Lv2Pedalboard>> activePedalboards; // pedalboards that have been sent to the audio queue.
|
||||
Lv2Pedalboard *realtimeActivePedalboard = nullptr;
|
||||
Lv2RoutingInserts realtimeRoutingInserts;
|
||||
@@ -544,7 +543,7 @@ private:
|
||||
|
||||
std::string GetAtomObjectType(uint8_t *pData)
|
||||
{
|
||||
LV2_Atom_Object *pAtom = (LV2_Atom_Object *)pData;
|
||||
LV2_Atom_Object *pAtom = (LV2_Atom_Object *)pData;
|
||||
if (pAtom->atom.type != uris.atom_Object)
|
||||
{
|
||||
throw std::invalid_argument("Not an Lv2 Object");
|
||||
@@ -629,7 +628,7 @@ private:
|
||||
{
|
||||
for (size_t i = 0; i < audioDriver->MainOutputBufferCount(); ++i)
|
||||
{
|
||||
float * out = (float *)audioDriver->GetMainOutputBuffer(i);
|
||||
float *out = (float *)audioDriver->GetMainOutputBuffer(i);
|
||||
if (out)
|
||||
{
|
||||
ZeroBuffer(out, nframes);
|
||||
@@ -638,7 +637,7 @@ private:
|
||||
}
|
||||
RealtimeVuBuffers *realtimeVuBuffers = nullptr;
|
||||
size_t vuSamplesPerUpdate = 0;
|
||||
int64_t vuSamplesRemaining = 0;
|
||||
int64_t realtimeVuSamplesRemaining = 0;
|
||||
|
||||
void freeRealtimeVuConfiguration()
|
||||
{
|
||||
@@ -664,11 +663,15 @@ private:
|
||||
|
||||
virtual void SetSystemMidiBindings(const std::vector<MidiBinding> &bindings);
|
||||
|
||||
void writeVu()
|
||||
void realtimeWriteVus()
|
||||
{
|
||||
// throttling: we send one; but won't send another until the host thread
|
||||
// acknowledges receipt.
|
||||
|
||||
if (!realtimeVuBuffers)
|
||||
{
|
||||
return;
|
||||
}
|
||||
if (!realtimeVuBuffers->waitingForAcknowledge)
|
||||
{
|
||||
auto pResult = realtimeVuBuffers->GetResult(currentSample);
|
||||
@@ -678,19 +681,27 @@ private:
|
||||
}
|
||||
}
|
||||
|
||||
void processMonitorPortSubscriptions(uint32_t nframes)
|
||||
void processMonitorPortSubscriptions(
|
||||
PedalboardType pedalboardType,
|
||||
Lv2Pedalboard *pedalboard,
|
||||
uint32_t nframes)
|
||||
{
|
||||
for (size_t i = 0; i < this->realtimeMonitorPortSubscriptions->subscriptions.size(); ++i)
|
||||
{
|
||||
auto &portSubscription = realtimeMonitorPortSubscriptions->subscriptions[i];
|
||||
|
||||
if (pedalboardType != portSubscription.pedalboardType)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
portSubscription.samplesToNextCallback -= portSubscription.sampleRate;
|
||||
if (portSubscription.samplesToNextCallback < 0)
|
||||
{
|
||||
portSubscription.samplesToNextCallback += portSubscription.sampleRate;
|
||||
if (!portSubscription.waitingForAck)
|
||||
{
|
||||
float value = realtimeActivePedalboard->GetControlOutputValue(
|
||||
float value = pedalboard->GetControlOutputValue(
|
||||
portSubscription.instanceIndex,
|
||||
portSubscription.portIndex);
|
||||
if (value != portSubscription.lastValue)
|
||||
@@ -852,7 +863,7 @@ private:
|
||||
{
|
||||
this->realtimeVuBuffers->waitingForAcknowledge = false;
|
||||
}
|
||||
vuSamplesRemaining = vuSamplesPerUpdate;
|
||||
realtimeVuSamplesRemaining = vuSamplesPerUpdate;
|
||||
|
||||
break;
|
||||
}
|
||||
@@ -971,7 +982,7 @@ private:
|
||||
return (event.size == 3 && event.buffer[0] == 0xB0 && event.buffer[1] == 0x00);
|
||||
}
|
||||
|
||||
bool onMidiEvent(Lv2EventBufferWriter &eventBufferWriter, Lv2EventBufferWriter::LV2_EvBuf_Iterator &iterator, MidiEvent &event)
|
||||
bool ProcessMidiMonitor(Lv2EventBufferWriter &eventBufferWriter, Lv2EventBufferWriter::LV2_EvBuf_Iterator &iterator, MidiEvent &event)
|
||||
{
|
||||
// eventBufferWriter.writeMidiEvent(iterator, 0, event.size, event.buffer);
|
||||
|
||||
@@ -995,7 +1006,7 @@ private:
|
||||
}
|
||||
if (isNote || isControl)
|
||||
{
|
||||
MidiNotifyBody notifyBody (event.buffer[0],event.buffer[1], event.buffer[2]);
|
||||
MidiNotifyBody notifyBody(event.buffer[0], event.buffer[1], event.buffer[2]);
|
||||
realtimeWriter.OnMidiListen(notifyBody);
|
||||
}
|
||||
}
|
||||
@@ -1108,7 +1119,7 @@ private:
|
||||
}
|
||||
else
|
||||
{
|
||||
onMidiEvent(eventBufferWriter, iterator, event);
|
||||
ProcessMidiMonitor(eventBufferWriter, iterator, event);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1151,7 +1162,69 @@ private:
|
||||
}
|
||||
}
|
||||
}
|
||||
void ProcessMidiInput()
|
||||
bool GetAudioDriverBuffers(PedalboardType pedalboardType,
|
||||
float **inputBuffers,
|
||||
float **outputBuffers)
|
||||
{
|
||||
bool buffersValid = true;
|
||||
if (!audioDriver)
|
||||
{
|
||||
inputBuffers[0] = nullptr;
|
||||
outputBuffers[0] = nullptr;
|
||||
return false;
|
||||
}
|
||||
switch (pedalboardType)
|
||||
{
|
||||
case PedalboardType::MainPedalboard:
|
||||
for (int i = 0; i < audioDriver->MainInputBufferCount(); ++i)
|
||||
{
|
||||
float *input = (float *)audioDriver->GetMainInputBuffer(i);
|
||||
if (input == nullptr)
|
||||
{
|
||||
buffersValid = false;
|
||||
}
|
||||
inputBuffers[i] = input;
|
||||
}
|
||||
inputBuffers[audioDriver->MainInputBufferCount()] = nullptr;
|
||||
|
||||
for (int i = 0; i < audioDriver->MainOutputBufferCount(); ++i)
|
||||
{
|
||||
float *output = audioDriver->GetMainOutputBuffer(i);
|
||||
if (output == nullptr)
|
||||
{
|
||||
buffersValid = false;
|
||||
}
|
||||
outputBuffers[i] = output;
|
||||
}
|
||||
outputBuffers[audioDriver->MainOutputBufferCount()] = nullptr;
|
||||
break;
|
||||
case PedalboardType::MainInserts:
|
||||
for (int i = 0; i < audioDriver->MainInputBufferCount(); ++i)
|
||||
{
|
||||
float *input = (float *)audioDriver->GetMainInputBuffer(i);
|
||||
if (input == nullptr)
|
||||
{
|
||||
buffersValid = false;
|
||||
}
|
||||
inputBuffers[i] = input;
|
||||
}
|
||||
inputBuffers[audioDriver->MainInputBufferCount()] = nullptr;
|
||||
|
||||
for (int i = 0; i < audioDriver->MainOutputBufferCount(); ++i)
|
||||
{
|
||||
float *output = audioDriver->GetMainOutputBuffer(i);
|
||||
if (output == nullptr)
|
||||
{
|
||||
buffersValid = false;
|
||||
}
|
||||
outputBuffers[i] = output;
|
||||
}
|
||||
outputBuffers[audioDriver->MainOutputBufferCount()] = nullptr;
|
||||
break;
|
||||
}
|
||||
return buffersValid;
|
||||
}
|
||||
void ProcessGlobalMidiInput()
|
||||
{
|
||||
Lv2EventBufferWriter eventBufferWriter(this->eventBufferUrids);
|
||||
Lv2EventBufferWriter::LV2_EvBuf_Iterator iterator = eventBufferWriter.begin();
|
||||
@@ -1190,11 +1263,253 @@ private:
|
||||
Lv2Log::info("Audio thread terminated.");
|
||||
}
|
||||
|
||||
PIPEDAL_NON_INLINE void ProcessLv2Pedalboard(PedalboardType pedalboardType, size_t nframes)
|
||||
{
|
||||
Lv2Pedalboard *pedalboard = nullptr;
|
||||
|
||||
std::vector<float *> *pInputBuffers;
|
||||
std::vector<float *> *pOutputBuffers;
|
||||
switch (pedalboardType)
|
||||
{
|
||||
case PedalboardType::MainPedalboard:
|
||||
pedalboard = this->realtimeActivePedalboard;
|
||||
pInputBuffers = &(audioDriver->MainInputBuffers());
|
||||
if (this->realtimeRoutingInserts.mainInserts != nullptr)
|
||||
{
|
||||
pOutputBuffers = &(audioDriver->MainOutputBuffers());
|
||||
}
|
||||
else
|
||||
{
|
||||
pOutputBuffers = &(audioDriver->MainInsertOutputBuffers());
|
||||
}
|
||||
break;
|
||||
case PedalboardType::MainInserts:
|
||||
|
||||
pedalboard = this->realtimeRoutingInserts.mainInserts;
|
||||
pInputBuffers = &(audioDriver->MainOutputBuffers());
|
||||
pOutputBuffers = &(audioDriver->MainInsertOutputBuffers());
|
||||
break;
|
||||
case PedalboardType::AuxInserts:
|
||||
pedalboard = this->realtimeRoutingInserts.auxInserts;
|
||||
pInputBuffers = &(audioDriver->AuxInputBuffers());
|
||||
pOutputBuffers = &(audioDriver->AuxOutputBuffers());
|
||||
break;
|
||||
}
|
||||
if (pInputBuffers->size() == 0 || pOutputBuffers->size() == 0)
|
||||
{
|
||||
return;
|
||||
}
|
||||
float *inputBuffers[3];
|
||||
float *outputBuffers[3];
|
||||
bool buffersValid = true;
|
||||
inputBuffers[0] = pInputBuffers->at(0);
|
||||
inputBuffers[1] = pInputBuffers->size() >= 2 ? pInputBuffers->at(1) : nullptr;
|
||||
inputBuffers[2] = nullptr;
|
||||
|
||||
outputBuffers[0] = pOutputBuffers->at(0);
|
||||
outputBuffers[1] = pOutputBuffers->size() >= 2 ? pOutputBuffers->at(1) : nullptr;
|
||||
outputBuffers[2] = nullptr;
|
||||
|
||||
if (pedalboard != nullptr)
|
||||
{
|
||||
pedalboard->ProcessParameterRequests(pParameterRequests, nframes);
|
||||
|
||||
pedalboard->Run(inputBuffers, outputBuffers, (uint32_t)nframes, &realtimeWriter);
|
||||
pedalboard->GatherPatchProperties(pParameterRequests);
|
||||
pedalboard->GatherPathPatchProperties(this);
|
||||
|
||||
if (this->realtimeMonitorPortSubscriptions != nullptr)
|
||||
{
|
||||
processMonitorPortSubscriptions(pedalboardType, pedalboard, nframes);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (pedalboardType)
|
||||
{
|
||||
case PedalboardType::MainPedalboard:
|
||||
{
|
||||
// zero output buffers.
|
||||
size_t ix = 0;
|
||||
while (outputBuffers[ix])
|
||||
{
|
||||
float *outputBuffer = outputBuffers[ix];
|
||||
for (size_t i = 0; i < nframes; ++i)
|
||||
{
|
||||
outputBuffer[i] = 0;
|
||||
}
|
||||
++ix;
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PedalboardType::MainInserts:
|
||||
// do nothing. Main pedalboard wrote to Main insert outputs directly in this case.
|
||||
break;
|
||||
case PedalboardType::AuxInserts:
|
||||
// Copy inputs to outputs.
|
||||
{
|
||||
if (outputBuffers[0] != nullptr && inputBuffers[0] != nullptr)
|
||||
{
|
||||
float *PIPEDAL_RESTRICT outputBuffer = outputBuffers[0];
|
||||
float *PIPEDAL_RESTRICT inputBuffer = inputBuffers[0];
|
||||
for (size_t i = 0; i < nframes; ++i)
|
||||
{
|
||||
outputBuffer[i] = inputBuffer[i];
|
||||
}
|
||||
}
|
||||
if (outputBuffers[1] != nullptr && inputBuffers[0] != nullptr)
|
||||
{
|
||||
float *PIPEDAL_RESTRICT outputBuffer = outputBuffers[1];
|
||||
float *PIPEDAL_RESTRICT inputBuffer = inputBuffers[1] != nullptr ? inputBuffers[1] : inputBuffers[0];
|
||||
for (size_t i = 0; i < nframes; ++i)
|
||||
{
|
||||
outputBuffer[i] = inputBuffer[i];
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PedalboardType::Invalid:
|
||||
throw std::runtime_error("Invalid argument.");
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void GetMasterChannels(PedalboardType pedalboardType, float **masterInputBuffers, float **masterOutputBuffers)
|
||||
{
|
||||
size_t inputIx = 0;
|
||||
size_t outputIx = 0;
|
||||
|
||||
masterInputBuffers[0] = nullptr;
|
||||
masterInputBuffers[1] = nullptr;
|
||||
masterOutputBuffers[0] = nullptr;
|
||||
masterOutputBuffers[1] = nullptr;
|
||||
|
||||
const ChannelSelection &channelSelection = audioDriver->GetChannelSelection();
|
||||
|
||||
switch (pedalboardType)
|
||||
{
|
||||
case PedalboardType::MainPedalboard:
|
||||
for (auto nChannel : channelSelection.mainInputChannels())
|
||||
{
|
||||
masterInputBuffers[inputIx++] = audioDriver->GetDeviceInputBuffer(nChannel);
|
||||
}
|
||||
for (auto nChannel : channelSelection.mainOutputChannels())
|
||||
{
|
||||
masterOutputBuffers[outputIx++] = audioDriver->GetDeviceOutputBuffer(nChannel);
|
||||
}
|
||||
break;
|
||||
case PedalboardType::MainInserts:
|
||||
if (this->realtimeActivePedalboard)
|
||||
{
|
||||
for (float *buffer : this->realtimeActivePedalboard->GetoutputBuffers())
|
||||
{
|
||||
masterInputBuffers[inputIx++] = buffer;
|
||||
}
|
||||
for (auto nChannel : channelSelection.mainOutputChannels())
|
||||
{
|
||||
masterOutputBuffers[outputIx++] = audioDriver->GetDeviceOutputBuffer(nChannel);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PedalboardType::AuxInserts:
|
||||
for (auto nChannel : channelSelection.auxInputChannels())
|
||||
{
|
||||
switch (nChannel)
|
||||
{
|
||||
case ChannelRouterSettings::MAIN_OUT_LEFT_CHANNEL:
|
||||
{
|
||||
float *pBuffer = nullptr;
|
||||
if (this->realtimeActivePedalboard)
|
||||
{
|
||||
auto &mainOutputBuffers = realtimeActivePedalboard->GetoutputBuffers();
|
||||
if (mainOutputBuffers.size() >= 1)
|
||||
{
|
||||
pBuffer = mainOutputBuffers[0];
|
||||
}
|
||||
}
|
||||
if (pBuffer == nullptr)
|
||||
{
|
||||
pBuffer = audioDriver->GetZeroInputBuffer();
|
||||
}
|
||||
masterInputBuffers[inputIx++] = pBuffer;
|
||||
}
|
||||
break;
|
||||
case ChannelRouterSettings::MAIN_OUT_RIGHT_CHANNEL:
|
||||
{
|
||||
float *pBuffer = nullptr;
|
||||
if (this->realtimeActivePedalboard)
|
||||
{
|
||||
auto &mainOutputBuffers = realtimeActivePedalboard->GetoutputBuffers();
|
||||
if (mainOutputBuffers.size() == 1)
|
||||
{
|
||||
pBuffer = mainOutputBuffers[0];
|
||||
}
|
||||
else if (mainOutputBuffers.size() == 2)
|
||||
{
|
||||
pBuffer = mainOutputBuffers[0];
|
||||
}
|
||||
}
|
||||
if (pBuffer == nullptr)
|
||||
{
|
||||
pBuffer = audioDriver->GetZeroInputBuffer();
|
||||
}
|
||||
masterInputBuffers[inputIx++] = pBuffer;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
masterInputBuffers[outputIx++] = audioDriver->GetDeviceInputBuffer(nChannel);
|
||||
};
|
||||
}
|
||||
for (auto nChannel : channelSelection.auxOutputChannels())
|
||||
{
|
||||
masterOutputBuffers[outputIx++] = audioDriver->GetDeviceOutputBuffer(nChannel);
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
throw std::runtime_error("GetMasterChannels: Argument out of range.");
|
||||
}
|
||||
}
|
||||
bool OnRealtimeUpdateDeviceVus(size_t nFrames) override
|
||||
{
|
||||
// all lv2pedalboards processed, and all channels downmixed.
|
||||
// Now we can do the real vu update work!
|
||||
|
||||
float *masterInputBuffers[2];
|
||||
float *masterOutputBuffers[2];
|
||||
|
||||
if (this->realtimeActivePedalboard != nullptr)
|
||||
{
|
||||
GetMasterChannels(PedalboardType::MainPedalboard, masterInputBuffers, masterOutputBuffers);
|
||||
realtimeActivePedalboard->ComputeVus(this->realtimeVuBuffers, nFrames, masterInputBuffers, masterOutputBuffers);
|
||||
}
|
||||
if (this->realtimeRoutingInserts.mainInserts)
|
||||
{
|
||||
GetMasterChannels(PedalboardType::MainInserts, masterInputBuffers, masterOutputBuffers);
|
||||
this->realtimeRoutingInserts.mainInserts->ComputeVus(this->realtimeVuBuffers, nFrames, masterInputBuffers, masterOutputBuffers);
|
||||
}
|
||||
if (this->realtimeRoutingInserts.auxInserts)
|
||||
{
|
||||
GetMasterChannels(PedalboardType::AuxInserts, masterInputBuffers, masterOutputBuffers);
|
||||
this->realtimeRoutingInserts.auxInserts->ComputeVus(this->realtimeVuBuffers, nFrames, masterInputBuffers, masterOutputBuffers);
|
||||
}
|
||||
|
||||
// periodically send updates.
|
||||
realtimeVuSamplesRemaining -= nFrames;
|
||||
if (realtimeVuSamplesRemaining <= 0)
|
||||
{
|
||||
realtimeWriteVus();
|
||||
realtimeVuSamplesRemaining = vuSamplesPerUpdate;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
virtual void OnProcess(size_t nframes)
|
||||
{
|
||||
try
|
||||
{
|
||||
float * restrict in , * restrict out;
|
||||
float *restrict in, *restrict out;
|
||||
|
||||
Lv2Pedalboard *pedalboard = nullptr;
|
||||
pedalboard = this->realtimeActivePedalboard;
|
||||
@@ -1202,6 +1517,15 @@ private:
|
||||
{
|
||||
pedalboard->ResetAtomBuffers();
|
||||
}
|
||||
if (this->realtimeRoutingInserts.auxInserts)
|
||||
{
|
||||
this->realtimeRoutingInserts.auxInserts->ResetAtomBuffers();
|
||||
}
|
||||
if (this->realtimeRoutingInserts.mainInserts)
|
||||
{
|
||||
this->realtimeRoutingInserts.mainInserts->ResetAtomBuffers();
|
||||
}
|
||||
|
||||
while (true)
|
||||
{
|
||||
|
||||
@@ -1212,71 +1536,18 @@ private:
|
||||
// else a new pedalboard was installed. start again.
|
||||
pedalboard = this->realtimeActivePedalboard;
|
||||
}
|
||||
|
||||
bool processed = false;
|
||||
|
||||
if (pedalboard != nullptr)
|
||||
{
|
||||
ProcessMidiInput();
|
||||
float *inputBuffers[4];
|
||||
float *outputBuffers[4];
|
||||
bool buffersValid = true;
|
||||
for (int i = 0; i < audioDriver->MainInputBufferCount(); ++i)
|
||||
{
|
||||
float *input = (float *)audioDriver->GetMainInputBuffer(i);
|
||||
if (input == nullptr)
|
||||
{
|
||||
buffersValid = false;
|
||||
break;
|
||||
}
|
||||
inputBuffers[i] = input;
|
||||
}
|
||||
inputBuffers[audioDriver->MainInputBufferCount()] = nullptr;
|
||||
|
||||
for (int i = 0; i < audioDriver->MainOutputBufferCount(); ++i)
|
||||
{
|
||||
float *output = audioDriver->GetMainOutputBuffer(i);
|
||||
if (output == nullptr)
|
||||
{
|
||||
buffersValid = false;
|
||||
break;
|
||||
}
|
||||
outputBuffers[i] = output;
|
||||
}
|
||||
outputBuffers[audioDriver->MainOutputBufferCount()] = nullptr;
|
||||
|
||||
if (buffersValid)
|
||||
{
|
||||
pedalboard->ProcessParameterRequests(pParameterRequests,nframes);
|
||||
|
||||
processed = pedalboard->Run(inputBuffers, outputBuffers, (uint32_t)nframes, &realtimeWriter);
|
||||
if (processed)
|
||||
{
|
||||
if (this->realtimeVuBuffers != nullptr)
|
||||
{
|
||||
pedalboard->ComputeVus(this->realtimeVuBuffers, (uint32_t)nframes, inputBuffers, outputBuffers);
|
||||
|
||||
vuSamplesRemaining -= nframes;
|
||||
if (vuSamplesRemaining <= 0)
|
||||
{
|
||||
writeVu();
|
||||
vuSamplesRemaining += vuSamplesPerUpdate;
|
||||
}
|
||||
}
|
||||
if (this->realtimeMonitorPortSubscriptions != nullptr)
|
||||
{
|
||||
processMonitorPortSubscriptions(nframes);
|
||||
}
|
||||
}
|
||||
pedalboard->GatherPatchProperties(pParameterRequests);
|
||||
pedalboard->GatherPathPatchProperties(this);
|
||||
}
|
||||
ProcessGlobalMidiInput();
|
||||
}
|
||||
|
||||
if (!processed)
|
||||
ProcessLv2Pedalboard(PedalboardType::MainPedalboard, nframes);
|
||||
if (this->realtimeRoutingInserts.mainInserts != nullptr) // prevent zero-ing of main outputs if there's no mainInserts
|
||||
{
|
||||
ZeroOutputBuffers(nframes);
|
||||
ProcessLv2Pedalboard(PedalboardType::MainInserts, nframes);
|
||||
}
|
||||
ProcessLv2Pedalboard(PedalboardType::AuxInserts, nframes);
|
||||
|
||||
if (pParameterRequests != nullptr)
|
||||
{
|
||||
@@ -1319,13 +1590,12 @@ public:
|
||||
|
||||
this->alsaDeviceMonitor->StartMonitoring(
|
||||
[this](
|
||||
AlsaSequencerDeviceMonitor::MonitorAction action,
|
||||
AlsaSequencerDeviceMonitor::MonitorAction action,
|
||||
int client,
|
||||
const std::string &clientName)
|
||||
{
|
||||
HandleAlsaSequencerDevicesChanged(action, client, clientName);
|
||||
}
|
||||
);
|
||||
const std::string &clientName)
|
||||
{
|
||||
HandleAlsaSequencerDevicesChanged(action, client, clientName);
|
||||
});
|
||||
}
|
||||
virtual ~AudioHostImpl()
|
||||
{
|
||||
@@ -1359,10 +1629,10 @@ public:
|
||||
}
|
||||
else if (action == AlsaSequencerDeviceMonitor::MonitorAction::DeviceAdded)
|
||||
{
|
||||
pNotifyCallbacks->OnAlsaSequencerDeviceAdded(client,clientName);
|
||||
pNotifyCallbacks->OnAlsaSequencerDeviceAdded(client, clientName);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
void HandleAudioTerminatedAbnormally()
|
||||
{
|
||||
Lv2Log::error("Audio processing terminated unexpectedly.");
|
||||
@@ -1509,7 +1779,7 @@ public:
|
||||
}
|
||||
else if (command == RingBufferCommand::SendVuUpdate)
|
||||
{
|
||||
const std::vector<VuUpdate> *updates = nullptr;
|
||||
const std::vector<VuUpdateX> *updates = nullptr;
|
||||
hostReader.read(&updates);
|
||||
|
||||
if (this->pNotifyCallbacks)
|
||||
@@ -1564,7 +1834,7 @@ public:
|
||||
if (property != nullptr && value != nullptr && property->type == uris.atom_URID)
|
||||
{
|
||||
LV2_URID propertyUrid = ((LV2_Atom_URID *)property)->body;
|
||||
if (this->pNotifyCallbacks)
|
||||
if (this->pNotifyCallbacks)
|
||||
{
|
||||
this->pNotifyCallbacks->OnPatchSetReply(instanceId, propertyUrid, value);
|
||||
}
|
||||
@@ -1603,7 +1873,6 @@ public:
|
||||
{
|
||||
OnActivePedalboardReleased(body.auxInserts);
|
||||
}
|
||||
|
||||
}
|
||||
else if (command == RingBufferCommand::FreeSnapshot)
|
||||
{
|
||||
@@ -1743,16 +2012,8 @@ public:
|
||||
}
|
||||
isOpen = true;
|
||||
|
||||
if (jackServerSettings.IsDummyAudioDevice())
|
||||
{
|
||||
this->isDummyAudioDriver = true;
|
||||
this->audioDriver = std::unique_ptr<AudioDriver>(CreateDummyAudioDriver(this, jackServerSettings.GetAlsaInputDevice()));
|
||||
}
|
||||
else
|
||||
{
|
||||
this->isDummyAudioDriver = false;
|
||||
this->audioDriver = std::unique_ptr<AudioDriver>(CreateAlsaDriver(this));
|
||||
}
|
||||
this->isDummyAudioDriver = jackServerSettings.IsDummyAudioDevice();
|
||||
this->audioDriver = std::unique_ptr<AudioDriver>(CreateAlsaDriver(this));
|
||||
|
||||
this->currentSample = 0;
|
||||
this->underruns = 0;
|
||||
@@ -1839,24 +2100,23 @@ public:
|
||||
}
|
||||
|
||||
virtual void SetChannelRoutingInserts(
|
||||
const std::shared_ptr<Lv2Pedalboard> &mainInserts,
|
||||
const std::shared_ptr<Lv2Pedalboard> &auxInserts
|
||||
) override {
|
||||
if (active && mainInserts)
|
||||
const std::shared_ptr<Lv2Pedalboard> &mainInserts,
|
||||
const std::shared_ptr<Lv2Pedalboard> &auxInserts) override
|
||||
{
|
||||
if (active && mainInserts)
|
||||
{
|
||||
mainInserts->Activate();
|
||||
this->activePedalboards.push_back(mainInserts);
|
||||
}
|
||||
if (active && auxInserts)
|
||||
}
|
||||
if (active && auxInserts)
|
||||
{
|
||||
auxInserts->Activate();
|
||||
this->activePedalboards.push_back(auxInserts);
|
||||
}
|
||||
Lv2RoutingInserts routingInserts { mainInserts: mainInserts.get(), auxInserts: auxInserts.get()};
|
||||
Lv2RoutingInserts routingInserts{mainInserts : mainInserts.get(), auxInserts : auxInserts.get()};
|
||||
hostWriter.ReplaceRoutingInserts(routingInserts);
|
||||
}
|
||||
|
||||
|
||||
virtual void SetBypass(uint64_t instanceId, bool enabled)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
@@ -1871,7 +2131,6 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
virtual void SetPluginPreset(uint64_t instanceId, const std::vector<ControlValue> &values)
|
||||
{
|
||||
std::lock_guard guard(mutex);
|
||||
@@ -1942,7 +2201,6 @@ public:
|
||||
|
||||
virtual void SetAlsaSequencerConfiguration(const AlsaSequencerConfiguration &alsaSequencerConfiguration) override;
|
||||
|
||||
|
||||
void OnNotifyPathPatchPropertyReceived(
|
||||
int64_t instanceId,
|
||||
const std::string &pathPatchPropertyUri,
|
||||
@@ -1975,45 +2233,63 @@ public:
|
||||
|
||||
PIPEDAL_NON_INLINE RealtimePedalboardItemIndex GetRealtimeItemIndex(int64_t instanceId)
|
||||
{
|
||||
Pedalboard::PedalboardType instanceType = Pedalboard::GetInstanceTypeFromInstanceId(instanceId);
|
||||
PedalboardType instanceType = Pedalboard::GetPedalboardTypeFromInstanceId(instanceId);
|
||||
int64_t index = -1;
|
||||
switch (instanceType)
|
||||
{
|
||||
case Pedalboard::PedalboardType::MainPedalboard:
|
||||
if (this->currentPedalboard) {
|
||||
if (instanceId == Pedalboard::START_CONTROL_ID) {
|
||||
index = Pedalboard::START_CONTROL_ID;
|
||||
} else if (instanceId == Pedalboard::END_CONTROL_ID) {
|
||||
index = Pedalboard::END_CONTROL_ID;
|
||||
} else {
|
||||
index = this->currentPedalboard->GetIndexOfInstanceId(instanceId);
|
||||
}
|
||||
case PedalboardType::MainPedalboard:
|
||||
if (this->currentPedalboard)
|
||||
{
|
||||
if (instanceId == Pedalboard::START_CONTROL_ID)
|
||||
{
|
||||
index = Pedalboard::START_CONTROL_ID;
|
||||
}
|
||||
break;
|
||||
case Pedalboard::PedalboardType::MainInsert:
|
||||
if (this->currentMainInsertPedalboard) {
|
||||
if (instanceId == Pedalboard::MAIN_INSERT_START_CONTROL_ID) {
|
||||
index = Pedalboard::MAIN_INSERT_START_CONTROL_ID;
|
||||
} else if (instanceId == Pedalboard::MAIN_INSERT_END_CONTROL_ID) {
|
||||
index = Pedalboard::END_CONTROL_ID;
|
||||
} else {
|
||||
index = this->currentMainInsertPedalboard->GetIndexOfInstanceId(instanceId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case Pedalboard::PedalboardType::AuxInsert:
|
||||
if (this->currentAuxInsertPedalboard) {
|
||||
if (instanceId == Pedalboard::AUX_INSERT_START_CONTROL_ID) {
|
||||
index = Pedalboard::START_CONTROL_ID;
|
||||
} else if (instanceId == Pedalboard::AUX_INSERT_END_CONTROL_ID) {
|
||||
index = Pedalboard::END_CONTROL_ID;
|
||||
} else {
|
||||
index = this->currentAuxInsertPedalboard->GetIndexOfInstanceId(instanceId);
|
||||
}
|
||||
else if (instanceId == Pedalboard::END_CONTROL_ID)
|
||||
{
|
||||
index = Pedalboard::END_CONTROL_ID;
|
||||
}
|
||||
break;
|
||||
else
|
||||
{
|
||||
index = this->currentPedalboard->GetIndexOfInstanceId(instanceId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PedalboardType::MainInserts:
|
||||
if (this->currentMainInsertPedalboard)
|
||||
{
|
||||
if (instanceId == Pedalboard::MAIN_INSERT_START_CONTROL_ID)
|
||||
{
|
||||
index = Pedalboard::MAIN_INSERT_START_CONTROL_ID;
|
||||
}
|
||||
else if (instanceId == Pedalboard::MAIN_INSERT_END_CONTROL_ID)
|
||||
{
|
||||
index = Pedalboard::END_CONTROL_ID;
|
||||
}
|
||||
else
|
||||
{
|
||||
index = this->currentMainInsertPedalboard->GetIndexOfInstanceId(instanceId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case PedalboardType::AuxInserts:
|
||||
if (this->currentAuxInsertPedalboard)
|
||||
{
|
||||
if (instanceId == Pedalboard::AUX_INSERT_START_CONTROL_ID)
|
||||
{
|
||||
index = Pedalboard::START_CONTROL_ID;
|
||||
}
|
||||
else if (instanceId == Pedalboard::AUX_INSERT_END_CONTROL_ID)
|
||||
{
|
||||
index = Pedalboard::END_CONTROL_ID;
|
||||
}
|
||||
else
|
||||
{
|
||||
index = this->currentAuxInsertPedalboard->GetIndexOfInstanceId(instanceId);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
RealtimePedalboardItemIndex result { instanceType, index};
|
||||
RealtimePedalboardItemIndex result{instanceType, index};
|
||||
return result;
|
||||
}
|
||||
virtual void SetVuSubscriptions(const std::vector<int64_t> &instanceIds)
|
||||
@@ -2034,54 +2310,79 @@ public:
|
||||
for (size_t i = 0; i < instanceIds.size(); ++i)
|
||||
{
|
||||
int64_t instanceId = instanceIds[i];
|
||||
auto effect = this->currentPedalboard->GetEffect(instanceId);
|
||||
if (effect)
|
||||
PedalboardType pedalboardType = Pedalboard::GetPedalboardTypeFromInstanceId(instanceId);
|
||||
std::shared_ptr<Lv2Pedalboard> pedalboard;
|
||||
switch (pedalboardType)
|
||||
{
|
||||
RealtimePedalboardItemIndex index = this->GetRealtimeItemIndex(instanceIds[i]);
|
||||
case PedalboardType::MainPedalboard:
|
||||
pedalboard = this->currentPedalboard;
|
||||
break;
|
||||
case PedalboardType::MainInserts:
|
||||
pedalboard = this->currentMainInsertPedalboard;
|
||||
break;
|
||||
case PedalboardType::AuxInserts:
|
||||
pedalboard = this->currentAuxInsertPedalboard;
|
||||
break;
|
||||
default:
|
||||
throw std::runtime_error("SetVuSubscriptions: Value out of range.");
|
||||
break;
|
||||
}
|
||||
int64_t effectIndex = -1;
|
||||
if (pedalboard != nullptr)
|
||||
{
|
||||
effectIndex = pedalboard->GetIndexOfInstanceId(instanceId);
|
||||
}
|
||||
|
||||
if (effectIndex != -1)
|
||||
{
|
||||
IEffect *effect = pedalboard->GetEffect(instanceId);
|
||||
RealtimePedalboardItemIndex index = RealtimePedalboardItemIndex(pedalboardType, effectIndex);
|
||||
vuConfig->enabledIndexes.push_back(index);
|
||||
VuUpdate v;
|
||||
VuUpdateX v;
|
||||
v.pedalboardType(pedalboardType);
|
||||
v.instanceId_ = instanceId;
|
||||
// Display mono VUs if a stereo device is being fed identical L/R inputs.
|
||||
v.isStereoInput_ = effect->GetNumberOfInputAudioBuffers() >= 2
|
||||
&& effect->GetAudioInputBuffer(0) != effect->GetAudioInputBuffer(1);
|
||||
v.isStereoInput_ = effect->GetNumberOfInputAudioBuffers() >= 2 && effect->GetAudioInputBuffer(0) != effect->GetAudioInputBuffer(1);
|
||||
v.isStereoOutput_ = effect->GetNumberOfOutputAudioBuffers() >= 2;
|
||||
|
||||
vuConfig->vuUpdateWorkingData.push_back(v);
|
||||
vuConfig->vuUpdateResponseData.push_back(v);
|
||||
}
|
||||
else if (
|
||||
instanceId == Pedalboard::START_CONTROL_ID || instanceId == Pedalboard::END_CONTROL_ID
|
||||
|| instanceId == Pedalboard::MAIN_INSERT_START_CONTROL_ID || instanceId == Pedalboard::MAIN_INSERT_END_CONTROL_ID
|
||||
|| instanceId == Pedalboard::AUX_INSERT_START_CONTROL_ID || instanceId == Pedalboard::AUX_INSERT_END_CONTROL_ID
|
||||
)
|
||||
instanceId == Pedalboard::START_CONTROL_ID ||
|
||||
instanceId == Pedalboard::END_CONTROL_ID ||
|
||||
instanceId == Pedalboard::MAIN_INSERT_START_CONTROL_ID ||
|
||||
instanceId == Pedalboard::MAIN_INSERT_END_CONTROL_ID ||
|
||||
instanceId == Pedalboard::AUX_INSERT_START_CONTROL_ID ||
|
||||
instanceId == Pedalboard::AUX_INSERT_END_CONTROL_ID)
|
||||
{
|
||||
auto index = GetRealtimeItemIndex(instanceId);
|
||||
VuUpdate v;
|
||||
VuUpdateX v;
|
||||
vuConfig->enabledIndexes.push_back(index);
|
||||
|
||||
|
||||
v.instanceId_ = instanceId;
|
||||
size_t nChannels = 0;
|
||||
|
||||
switch (instanceId)
|
||||
switch (instanceId)
|
||||
{
|
||||
case Pedalboard::START_CONTROL_ID:
|
||||
nChannels = this->pHost->GetChannelSelection().mainInputChannels().size();
|
||||
break;
|
||||
case Pedalboard::END_CONTROL_ID:
|
||||
nChannels = this->pHost->GetChannelSelection().mainOutputChannels().size();
|
||||
break;
|
||||
case Pedalboard::MAIN_INSERT_START_CONTROL_ID:
|
||||
nChannels = this->pHost->GetChannelSelection().mainOutputChannels().size();
|
||||
break;
|
||||
case Pedalboard::MAIN_INSERT_END_CONTROL_ID:
|
||||
nChannels = this->pHost->GetChannelSelection().mainOutputChannels().size();
|
||||
break;
|
||||
case Pedalboard::AUX_INSERT_START_CONTROL_ID:
|
||||
nChannels = this->pHost->GetChannelSelection().auxInputChannels().size();
|
||||
break;
|
||||
case Pedalboard::AUX_INSERT_END_CONTROL_ID:
|
||||
nChannels = this->pHost->GetChannelSelection().auxOutputChannels().size();
|
||||
break;
|
||||
case Pedalboard::START_CONTROL_ID:
|
||||
nChannels = this->pHost->GetChannelSelection().mainInputChannels().size();
|
||||
break;
|
||||
case Pedalboard::END_CONTROL_ID:
|
||||
nChannels = this->pHost->GetChannelSelection().mainOutputChannels().size();
|
||||
break;
|
||||
case Pedalboard::MAIN_INSERT_START_CONTROL_ID:
|
||||
nChannels = this->pHost->GetChannelSelection().mainOutputChannels().size();
|
||||
break;
|
||||
case Pedalboard::MAIN_INSERT_END_CONTROL_ID:
|
||||
nChannels = this->pHost->GetChannelSelection().mainOutputChannels().size();
|
||||
break;
|
||||
case Pedalboard::AUX_INSERT_START_CONTROL_ID:
|
||||
nChannels = this->pHost->GetChannelSelection().auxInputChannels().size();
|
||||
break;
|
||||
case Pedalboard::AUX_INSERT_END_CONTROL_ID:
|
||||
nChannels = this->pHost->GetChannelSelection().auxOutputChannels().size();
|
||||
break;
|
||||
}
|
||||
v.isStereoInput_ = v.isStereoOutput_ = nChannels > 1;
|
||||
vuConfig->vuUpdateWorkingData.push_back(v);
|
||||
@@ -2094,10 +2395,25 @@ public:
|
||||
}
|
||||
}
|
||||
|
||||
Lv2Pedalboard *GetPedalboard(PedalboardType pedalboardType)
|
||||
{
|
||||
switch (pedalboardType)
|
||||
{
|
||||
case PedalboardType::MainPedalboard:
|
||||
return this->currentPedalboard.get();
|
||||
case PedalboardType::MainInserts:
|
||||
return this->realtimeRoutingInserts.mainInserts;
|
||||
case PedalboardType::AuxInserts:
|
||||
return this->realtimeRoutingInserts.auxInserts;
|
||||
default:
|
||||
throw std::runtime_error("AudioHostImpl::GetPedalboard: invalid argument.");
|
||||
}
|
||||
}
|
||||
RealtimeMonitorPortSubscription MakeRealtimeSubscription(const MonitorPortSubscription &subscription)
|
||||
{
|
||||
RealtimeMonitorPortSubscription result;
|
||||
result.subscriptionHandle = subscription.subscriptionHandle;
|
||||
result.pedalboardType = Pedalboard::GetPedalboardTypeFromInstanceId(subscription.subscriptionHandle);
|
||||
result.instanceIndex = this->currentPedalboard->GetIndexOfInstanceId(subscription.instanceid);
|
||||
IEffect *pEffect = this->currentPedalboard->GetEffect(subscription.instanceid);
|
||||
|
||||
@@ -2459,8 +2775,6 @@ void AudioHostImpl::SetAlsaSequencerConfiguration(const AlsaSequencerConfigurati
|
||||
this->alsaSequencer->SetConfiguration(alsaSequencerConfiguration);
|
||||
}
|
||||
|
||||
|
||||
|
||||
JSON_MAP_BEGIN(JackHostStatus)
|
||||
JSON_MAP_REFERENCE(JackHostStatus, active)
|
||||
JSON_MAP_REFERENCE(JackHostStatus, errorMessage)
|
||||
|
||||
@@ -148,6 +148,7 @@ namespace pipedal
|
||||
{
|
||||
public:
|
||||
int64_t subscriptionHandle;
|
||||
PedalboardType pedalboardType;
|
||||
int64_t instanceid;
|
||||
std::string key;
|
||||
float updateInterval;
|
||||
@@ -159,7 +160,7 @@ namespace pipedal
|
||||
public:
|
||||
virtual void OnNotifyLv2StateChanged(uint64_t instanceId) = 0;
|
||||
virtual bool OnNotifyMaybeLv2StateChanged(uint64_t instanceId) = 0;
|
||||
virtual void OnNotifyVusSubscription(const std::vector<VuUpdate> &updates) = 0;
|
||||
virtual void OnNotifyVusSubscription(const std::vector<VuUpdateX> &updates) = 0;
|
||||
virtual void OnNotifyMonitorPort(const MonitorPortUpdate &update) = 0;
|
||||
virtual void OnNotifyMidiValueChanged(int64_t instanceId, int portIndex, float value) = 0;
|
||||
virtual void OnNotifyMidiListen(uint8_t cc0, uint8_t cc1, uint8_t cc2) = 0;
|
||||
|
||||
@@ -240,7 +240,7 @@ namespace pipedal
|
||||
{
|
||||
// zero length? We can never have a zero-length bank.
|
||||
// Add a default preset and make it the selected preset.
|
||||
Pedalboard pedalboard = Pedalboard::MakeDefault(Pedalboard::PedalboardType::MainPedalboard);
|
||||
Pedalboard pedalboard = Pedalboard::MakeDefault(PedalboardType::MainPedalboard);
|
||||
this->addPreset(pedalboard);
|
||||
newSelection = presets_[0]->instanceId();
|
||||
}
|
||||
|
||||
@@ -129,8 +129,8 @@ void ChannelSelection::normalizeChannelSelection() {
|
||||
|
||||
|
||||
ChannelRouterSettings::ChannelRouterSettings()
|
||||
: mainInserts_(Pedalboard::PedalboardType::MainInsert)
|
||||
, auxInserts_(Pedalboard::PedalboardType::AuxInsert)
|
||||
: mainInserts_(PedalboardType::MainInserts)
|
||||
, auxInserts_(PedalboardType::AuxInserts)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -33,6 +33,7 @@ namespace pipedal
|
||||
static constexpr int64_t CHANNEL_ROUTER_MAIN_INSERT_ID = -4; // Reserved Instance ID for Router Main Inserts.
|
||||
static constexpr int64_t CHANNEL_ROUTER_AUX_INSERT_ID = -5; // Reserved Instance ID for Router Aux inserts.
|
||||
|
||||
|
||||
bool configured_ = false;
|
||||
int64_t channelRouterPresetId_ = -1;
|
||||
bool changed_ = false;
|
||||
@@ -51,6 +52,10 @@ namespace pipedal
|
||||
std::vector<ControlValue> controlValues_;
|
||||
|
||||
public:
|
||||
|
||||
static constexpr int64_t MAIN_OUT_LEFT_CHANNEL = -2;
|
||||
static constexpr int64_t MAIN_OUT_RIGHT_CHANNEL = -3;
|
||||
|
||||
using self = ChannelRouterSettings;
|
||||
using ptr = std::shared_ptr<self>;
|
||||
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright (c) 2026 Robin E. R. Davies
|
||||
* All rights reserved.
|
||||
|
||||
* Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
* of this software and associated documentation files (the "Software"), to deal
|
||||
* in the Software without restriction, including without limitation the rights
|
||||
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
* copies of the Software, and to permit persons to whom the Software is
|
||||
* furnished to do so, subject to the following conditions:
|
||||
|
||||
* The above copyright notice and this permission notice shall be included in all
|
||||
* copies or substantial portions of the Software.
|
||||
|
||||
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
* SOFTWARE.
|
||||
*/
|
||||
|
||||
#pragma once
|
||||
#include <cstdint>
|
||||
#include "json.hpp"
|
||||
|
||||
|
||||
namespace pipedal {
|
||||
class RealtimeDeviceVus {
|
||||
public:
|
||||
static constexpr size_t MAX_CHANNELS = 64;
|
||||
size_t inputChannels;
|
||||
size_t outputChannels;
|
||||
float values[MAX_CHANNELS*2];
|
||||
|
||||
size_t MessageSize() const {
|
||||
return sizeof(RealtimeDeviceVus)-sizeof(float)*(
|
||||
MAX_CHANNELS*2-
|
||||
(inputChannels+outputChannels+1)/2*2);
|
||||
}
|
||||
};
|
||||
};
|
||||
@@ -58,6 +58,8 @@
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
#ifdef JUNK
|
||||
|
||||
namespace pipedal
|
||||
{
|
||||
|
||||
@@ -524,3 +526,4 @@ uint32_t pipedal::GetDummyAudioChannels(const std::string &deviceName)
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -24,6 +24,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#ifdef JUNK
|
||||
#include <lilv/lilv.h>
|
||||
#include "AudioDriver.hpp"
|
||||
#include "JackServerSettings.hpp"
|
||||
@@ -36,4 +37,5 @@ namespace pipedal {
|
||||
AudioDriver* CreateDummyAudioDriver(AudioDriverHost*driverHost,const std::string&deviceId);
|
||||
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -130,12 +130,8 @@ void JackConfiguration::AlsaInitialize(
|
||||
this->isValid_ = false;
|
||||
this->errorStatus_ = "";
|
||||
|
||||
if (jackServerSettings.IsDummyAudioDevice())
|
||||
{
|
||||
this->inputMidiDevices_.clear();
|
||||
} else {
|
||||
this->inputMidiDevices_ = GetAlsaSequencers(); // NB: Sequencers, not rawmidi devices, anymore.
|
||||
}
|
||||
this->inputMidiDevices_ = GetAlsaSequencers(); // NB: Sequencers, not rawmidi devices, anymore.
|
||||
|
||||
if (jackServerSettings.IsValid())
|
||||
{
|
||||
this->blockLength_ = jackServerSettings.GetBufferSize();
|
||||
|
||||
@@ -155,6 +155,8 @@ void JackServerSettings::FixUpDeviceNames()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
JSON_MAP_BEGIN(JackServerSettings)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, valid)
|
||||
JSON_MAP_REFERENCE(JackServerSettings, isOnboarding)
|
||||
|
||||
@@ -60,7 +60,7 @@ namespace pipedal
|
||||
{
|
||||
FixUpDeviceNames();
|
||||
}
|
||||
|
||||
|
||||
uint64_t GetSampleRate() const { return sampleRate_; }
|
||||
|
||||
uint32_t GetBufferSize() const { return bufferSize_; }
|
||||
@@ -78,17 +78,17 @@ namespace pipedal
|
||||
void UseDummyAudioDevice() {
|
||||
this->valid_ = true;
|
||||
if (sampleRate_ == 0) sampleRate_ = 48000;
|
||||
this->alsaDevice_ = "dummy:channels_2";
|
||||
this->alsaInputDevice_ = "dummy:channels_2";
|
||||
this->alsaInputDeviceName_ = "Stopped";
|
||||
this->alsaOutputDevice_ = "dummy:channels_2";
|
||||
this->alsaOutputDeviceName_ = "Stopped";
|
||||
bufferSize_ = 256;
|
||||
numberOfBuffers_ = 6;
|
||||
|
||||
this->alsaDevice_ = "";
|
||||
this->alsaInputDevice_ = "null";
|
||||
this->alsaInputDeviceName_ = "null";
|
||||
this->alsaOutputDevice_ = "null";
|
||||
this->alsaOutputDeviceName_ = "null";
|
||||
}
|
||||
bool IsDummyAudioDevice() const {
|
||||
return
|
||||
this->alsaDevice_.starts_with("__DUMMY_AUDIO__")
|
||||
|| this->alsaDevice_.starts_with("dummy:")
|
||||
|| this->alsaInputDevice_.starts_with("dummy:");
|
||||
return alsaInputDevice_ == "null" && alsaOutputDevice_ == "null";
|
||||
}
|
||||
|
||||
bool IsValid() const { return valid_; }
|
||||
|
||||
@@ -29,6 +29,7 @@
|
||||
#include "Lv2Log.hpp"
|
||||
#include "CrashGuard.hpp"
|
||||
#include "restrict.hpp"
|
||||
#include "AudioDriver.hpp"
|
||||
|
||||
using namespace pipedal;
|
||||
|
||||
@@ -571,42 +572,46 @@ void Lv2Pedalboard::SetBypass(int effectIndex, bool enabled)
|
||||
effect->SetBypass(enabled);
|
||||
}
|
||||
|
||||
void Lv2Pedalboard::ComputeVus(RealtimeVuBuffers *vuConfiguration, uint32_t samples, float **inputBuffers, float **outputBuffers)
|
||||
|
||||
void Lv2Pedalboard::ComputeVus(RealtimeVuBuffers *realtimeVuBuffers, uint32_t samples, float**masterInputBuffers, float**masterOutputBuffers)
|
||||
{
|
||||
for (size_t i = 0; i < vuConfiguration->enabledIndexes.size(); ++i)
|
||||
if (realtimeVuBuffers == nullptr)
|
||||
{
|
||||
auto& rtIndex = vuConfiguration->enabledIndexes[i];
|
||||
return;
|
||||
}
|
||||
for (size_t i = 0; i < realtimeVuBuffers->enabledIndexes.size(); ++i)
|
||||
{
|
||||
auto& rtIndex = realtimeVuBuffers->enabledIndexes[i];
|
||||
if (rtIndex.instanceType != this->pedalboardType)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
int index = rtIndex.index;
|
||||
VuUpdate *pUpdate = &vuConfiguration->vuUpdateWorkingData[i];
|
||||
VuUpdateX *pUpdate = &realtimeVuBuffers->vuUpdateWorkingData[i];
|
||||
if (index == Pedalboard::START_CONTROL_ID)
|
||||
{
|
||||
if (this->pedalboardInputBuffers.size() > 1)
|
||||
if (this->pedalboardInputBuffers.size() == 2)
|
||||
{
|
||||
GetInputBuffers();
|
||||
pUpdate->AccumulateInputs(inputBuffers[0], inputBuffers[1], samples);
|
||||
pUpdate->AccumulateInputs(masterInputBuffers[0], masterInputBuffers[1], samples);
|
||||
pUpdate->AccumulateOutputs(&(this->pedalboardInputBuffers[0][0]), &(this->pedalboardInputBuffers[1][0]), samples); // after outputVolume applied.
|
||||
}
|
||||
else
|
||||
else if (this->pedalboardInputBuffers.size() == 1)
|
||||
{
|
||||
pUpdate->AccumulateInputs(inputBuffers[0], samples);
|
||||
pUpdate->AccumulateInputs(masterInputBuffers[0], samples);
|
||||
pUpdate->AccumulateOutputs(&(this->pedalboardInputBuffers[0][0]), samples); // after input volume applied.
|
||||
}
|
||||
}
|
||||
else if (index == Pedalboard::END_CONTROL_ID)
|
||||
{
|
||||
if (this->pedalboardOutputBuffers.size() > 1)
|
||||
if (this->pedalboardOutputBuffers.size() == 2)
|
||||
{
|
||||
pUpdate->AccumulateInputs(&(this->pedalboardOutputBuffers[0][0]), &(this->pedalboardOutputBuffers[1][0]), samples);
|
||||
pUpdate->AccumulateOutputs(outputBuffers[0], outputBuffers[1], samples);
|
||||
pUpdate->AccumulateOutputs(masterOutputBuffers[0], masterOutputBuffers[1], samples);
|
||||
}
|
||||
else
|
||||
else if (this->pedalboardOutputBuffers.size() == 1)
|
||||
{
|
||||
pUpdate->AccumulateInputs(&(this->pedalboardOutputBuffers[0][0]), samples);
|
||||
pUpdate->AccumulateOutputs(outputBuffers[0], samples);
|
||||
pUpdate->AccumulateOutputs(masterOutputBuffers[0], samples);
|
||||
}
|
||||
}
|
||||
else
|
||||
@@ -1029,11 +1034,11 @@ void Lv2Pedalboard::handleTapTempo(uint8_t value, const MidiTimestamp& timestamp
|
||||
size_t Lv2Pedalboard::GetNumberOfAudioInputChannels() const {
|
||||
switch (pedalboardType)
|
||||
{
|
||||
case Pedalboard::PedalboardType::MainPedalboard:
|
||||
case PedalboardType::MainPedalboard:
|
||||
return pHost->GetChannelSelection().mainInputChannels().size();
|
||||
case Pedalboard::PedalboardType::MainInsert:
|
||||
case PedalboardType::MainInserts:
|
||||
return pHost->GetChannelSelection().mainOutputChannels().size();
|
||||
case Pedalboard::PedalboardType::AuxInsert:
|
||||
case PedalboardType::AuxInserts:
|
||||
return pHost->GetChannelSelection().auxInputChannels().size();
|
||||
default:
|
||||
throw std::runtime_error("Invalid argument");
|
||||
@@ -1043,11 +1048,11 @@ size_t Lv2Pedalboard::GetNumberOfAudioInputChannels() const {
|
||||
size_t Lv2Pedalboard::GetNumberOfAudioOutputChannels() const {
|
||||
switch (pedalboardType)
|
||||
{
|
||||
case Pedalboard::PedalboardType::MainPedalboard:
|
||||
case PedalboardType::MainPedalboard:
|
||||
return pHost->GetChannelSelection().mainOutputChannels().size();
|
||||
case Pedalboard::PedalboardType::MainInsert:
|
||||
case PedalboardType::MainInserts:
|
||||
return pHost->GetChannelSelection().mainOutputChannels().size();
|
||||
case Pedalboard::PedalboardType::AuxInsert:
|
||||
case PedalboardType::AuxInserts:
|
||||
return pHost->GetChannelSelection().auxOutputChannels().size();
|
||||
default:
|
||||
throw std::runtime_error("Invalid argument");
|
||||
|
||||
@@ -31,6 +31,7 @@
|
||||
namespace pipedal
|
||||
{
|
||||
|
||||
class AudioDriver;
|
||||
class IPatchWriterCallback;
|
||||
class RealtimeVuBuffers;
|
||||
class RealtimePatchPropertyRequest;
|
||||
@@ -51,7 +52,7 @@ namespace pipedal
|
||||
class Lv2Pedalboard
|
||||
{
|
||||
private:
|
||||
Pedalboard::PedalboardType pedalboardType;
|
||||
PedalboardType pedalboardType;
|
||||
IHost *pHost = nullptr;
|
||||
size_t currentFrameOffset = 0;
|
||||
DbDezipper inputVolume;
|
||||
@@ -173,7 +174,7 @@ namespace pipedal
|
||||
void SetOutputVolume(float value) { this->outputVolume.SetTarget(value); }
|
||||
void SetBypass(int effectIndex, bool enabled);
|
||||
|
||||
void ComputeVus(RealtimeVuBuffers *vuConfiguration, uint32_t samples, float **inputBuffers, float **outputBuffers);
|
||||
void ComputeVus(RealtimeVuBuffers *vuConfiguration, uint32_t samples, float**masterInputBuffers, float **masterOutputBuffers);
|
||||
|
||||
float GetControlOutputValue(int effectIndex, int portIndex);
|
||||
|
||||
|
||||
@@ -517,10 +517,10 @@ Pedalboard::Pedalboard(PedalboardType instanceType)
|
||||
case PedalboardType::MainPedalboard:
|
||||
nextInstanceId_ = 0;
|
||||
break;
|
||||
case PedalboardType::MainInsert:
|
||||
case PedalboardType::MainInserts:
|
||||
nextInstanceId_ = MAIN_INSERT_INSTANCE_BASE;;
|
||||
break;
|
||||
case PedalboardType::AuxInsert:
|
||||
case PedalboardType::AuxInserts:
|
||||
nextInstanceId_ = AUX_INSERT_INSTANCE_BASE;;
|
||||
break;
|
||||
}
|
||||
|
||||
@@ -25,13 +25,14 @@
|
||||
#include "StateInterface.hpp"
|
||||
#include "atom_object.hpp"
|
||||
|
||||
namespace pipedal {
|
||||
namespace pipedal
|
||||
{
|
||||
class SnapshotValue;
|
||||
class Snapshot;
|
||||
class PluginHost;
|
||||
|
||||
#define SPLIT_PEDALBOARD_ITEM_URI "uri://two-play/pipedal/pedalboard#Split"
|
||||
#define EMPTY_PEDALBOARD_ITEM_URI "uri://two-play/pipedal/pedalboard#Empty"
|
||||
|
||||
#define SPLIT_PEDALBOARD_ITEM_URI "uri://two-play/pipedal/pedalboard#Split"
|
||||
#define EMPTY_PEDALBOARD_ITEM_URI "uri://two-play/pipedal/pedalboard#Empty"
|
||||
|
||||
#define SPLIT_SPLITTYPE_KEY "splitType"
|
||||
#define SPLIT_SELECT_KEY "select"
|
||||
@@ -41,280 +42,284 @@ namespace pipedal {
|
||||
#define SPLIT_PANR_KEY "panR"
|
||||
#define SPLIT_VOLR_KEY "volR"
|
||||
|
||||
|
||||
#define GETTER_SETTER_REF(name) \
|
||||
const decltype(name##_)& name() const { return name##_;} \
|
||||
#define GETTER_SETTER_REF(name) \
|
||||
const decltype(name##_) &name() const { return name##_; } \
|
||||
void name(const decltype(name##_) &value) { name##_ = value; }
|
||||
|
||||
#define GETTER_SETTER_VEC(name) \
|
||||
decltype(name##_)& name() { return name##_;} \
|
||||
const decltype(name##_)& name() const { return name##_;} \
|
||||
void name(decltype(name##_)&&value) { this->name##_ = std::move(value); }\
|
||||
void name(const decltype(name##_)&value) { this->name##_ = value; }
|
||||
#define GETTER_SETTER_VEC(name) \
|
||||
decltype(name##_) &name() { return name##_; } \
|
||||
const decltype(name##_) &name() const { return name##_; } \
|
||||
void name(decltype(name##_) &&value) { this->name##_ = std::move(value); } \
|
||||
void name(const decltype(name##_) &value) { this->name##_ = value; }
|
||||
|
||||
|
||||
|
||||
#define GETTER_SETTER(name) \
|
||||
decltype(name##_) name() const { return name##_;} \
|
||||
#define GETTER_SETTER(name) \
|
||||
decltype(name##_) name() const { return name##_; } \
|
||||
void name(decltype(name##_) value) { name##_ = value; }
|
||||
|
||||
class ControlValue {
|
||||
private:
|
||||
std::string key_;
|
||||
float value_;
|
||||
public:
|
||||
ControlValue()
|
||||
class ControlValue
|
||||
{
|
||||
private:
|
||||
std::string key_;
|
||||
float value_;
|
||||
|
||||
}
|
||||
ControlValue(const char*key, float value)
|
||||
:key_(key)
|
||||
, value_(value)
|
||||
{
|
||||
|
||||
}
|
||||
ControlValue(const std::string&key, float value)
|
||||
:key_(key)
|
||||
, value_(value)
|
||||
{
|
||||
|
||||
}
|
||||
GETTER_SETTER_REF(key)
|
||||
GETTER_SETTER_REF(value)
|
||||
|
||||
DECLARE_JSON_MAP(ControlValue);
|
||||
|
||||
|
||||
};
|
||||
|
||||
class PedalboardItem {
|
||||
public:
|
||||
using PropertyMap = std::map<std::string,atom_object>;
|
||||
int64_t instanceId_ = 0;
|
||||
std::string uri_;
|
||||
std::string pluginName_;
|
||||
bool isEnabled_ = true;
|
||||
std::vector<ControlValue> controlValues_;
|
||||
std::vector<PedalboardItem> topChain_;
|
||||
std::vector<PedalboardItem> bottomChain_;
|
||||
std::vector<MidiBinding> midiBindings_;
|
||||
std::optional<MidiChannelBinding> midiChannelBinding_;
|
||||
std::string vstState_;
|
||||
uint32_t stateUpdateCount_ = 0;
|
||||
Lv2PluginState lv2State_;
|
||||
std::string lilvPresetUri_;
|
||||
std::map<std::string,std::string> pathProperties_;
|
||||
std::string title_;
|
||||
bool useModUi_ = false;
|
||||
std::string iconColor_;
|
||||
int64_t sideChainInputId_ = -1;
|
||||
|
||||
// non persistent state.
|
||||
PropertyMap patchProperties;
|
||||
public:
|
||||
ControlValue*GetControlValue(const std::string&symbol);
|
||||
const ControlValue*GetControlValue(const std::string&symbol) const;
|
||||
bool SetControlValue(const std::string&key, float value);
|
||||
|
||||
bool IsStructurallyIdentical(const PedalboardItem&other) const;
|
||||
|
||||
void ApplySnapshotValue(SnapshotValue*snapshotValue);
|
||||
void ApplyDefaultValues(PluginHost&pluginHost);
|
||||
bool hasLv2State() const {
|
||||
return lv2State_.isValid_ != 0;
|
||||
}
|
||||
GETTER_SETTER(instanceId)
|
||||
GETTER_SETTER_REF(uri)
|
||||
GETTER_SETTER_REF(vstState);
|
||||
GETTER_SETTER_REF(pluginName)
|
||||
GETTER_SETTER(isEnabled)
|
||||
GETTER_SETTER_VEC(controlValues)
|
||||
GETTER_SETTER_VEC(topChain)
|
||||
GETTER_SETTER_VEC(bottomChain)
|
||||
GETTER_SETTER_VEC(midiBindings)
|
||||
GETTER_SETTER_REF(midiChannelBinding)
|
||||
GETTER_SETTER_REF(pathProperties)
|
||||
GETTER_SETTER(stateUpdateCount)
|
||||
GETTER_SETTER_REF(lv2State)
|
||||
GETTER_SETTER_REF(title)
|
||||
GETTER_SETTER_REF(iconColor)
|
||||
GETTER_SETTER(useModUi)
|
||||
GETTER_SETTER(sideChainInputId)
|
||||
|
||||
Lv2PluginState&lv2State() { return lv2State_; } // non-const version.
|
||||
GETTER_SETTER_REF(lilvPresetUri)
|
||||
|
||||
PropertyMap&PatchProperties() { return patchProperties; }
|
||||
const PropertyMap&PatchProperties() const { return patchProperties; }
|
||||
|
||||
|
||||
bool isSplit() const
|
||||
{
|
||||
return uri_ == SPLIT_PEDALBOARD_ITEM_URI;
|
||||
}
|
||||
bool isEmpty() const {
|
||||
return uri_ == EMPTY_PEDALBOARD_ITEM_URI;
|
||||
}
|
||||
|
||||
void AddToSnapshotFromCurrentSettings(Snapshot&snapshot) const;
|
||||
void AddResetsForMissingProperties(Snapshot&snapshot, size_t*index) const;
|
||||
|
||||
|
||||
// virtual void write_members(json_writer&writer) const {
|
||||
// writer.write_member("instanceId",instanceId_);
|
||||
// writer.write_member("uri",uri_);
|
||||
// writer.write_member("pluginName",pluginName_);
|
||||
// writer.write_member("isEnabled",isEnabled_);
|
||||
// if (isSplit())
|
||||
// {
|
||||
// writer.write_member("topChain",topChain_);
|
||||
// writer.write_member("bottomChain",bottomChain_);
|
||||
// }
|
||||
// }
|
||||
|
||||
DECLARE_JSON_MAP(PedalboardItem);
|
||||
|
||||
};
|
||||
|
||||
class SnapshotValue {
|
||||
public:
|
||||
uint64_t instanceId_;
|
||||
bool isEnabled_ = true;
|
||||
std::vector<ControlValue> controlValues_;
|
||||
Lv2PluginState lv2State_;
|
||||
std::map<std::string,std::string> pathProperties_;
|
||||
DECLARE_JSON_MAP(SnapshotValue);
|
||||
|
||||
ControlValue*GetControlValue(const std::string& key) {
|
||||
for (ControlValue&controlValue: controlValues_) {
|
||||
if (controlValue.key() == key) {
|
||||
return &controlValue;
|
||||
}
|
||||
public:
|
||||
ControlValue()
|
||||
{
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
void SetControlValue(const std::string& key,float value) {
|
||||
for (ControlValue&controlValue: controlValues_) {
|
||||
if (controlValue.key() == key) {
|
||||
controlValue.value(value);
|
||||
return;
|
||||
}
|
||||
ControlValue(const char *key, float value)
|
||||
: key_(key), value_(value)
|
||||
{
|
||||
}
|
||||
controlValues_.push_back(ControlValue{key,value});
|
||||
}
|
||||
|
||||
};
|
||||
ControlValue(const std::string &key, float value)
|
||||
: key_(key), value_(value)
|
||||
{
|
||||
}
|
||||
GETTER_SETTER_REF(key)
|
||||
GETTER_SETTER_REF(value)
|
||||
|
||||
class Snapshot {
|
||||
public:
|
||||
std::string name_;
|
||||
std::string color_;
|
||||
bool isModified_ = false;
|
||||
std::vector<SnapshotValue> values_;
|
||||
|
||||
DECLARE_JSON_MAP(Snapshot);
|
||||
|
||||
};
|
||||
|
||||
class Pedalboard {
|
||||
std::string name_;
|
||||
float input_volume_db_ = 0;
|
||||
float output_volume_db_ = 0;
|
||||
|
||||
std::vector<PedalboardItem> items_;
|
||||
uint64_t nextInstanceId_ = 0;
|
||||
uint64_t NextInstanceId() { return ++nextInstanceId_; }
|
||||
|
||||
std::vector<std::shared_ptr<Snapshot>> snapshots_;
|
||||
int64_t selectedSnapshot_ = -1;
|
||||
|
||||
int64_t selectedPlugin_ = -1;
|
||||
|
||||
|
||||
static constexpr int64_t TWO_POWER_52 = 4503599627370496; // Maximum Javascript integer value.
|
||||
static constexpr int64_t TWO_POWER_48 = 281474976710656; // Number of possible instance IDs for Main Pedalboards.
|
||||
static constexpr int64_t MAX_INSTANCE_ID = TWO_POWER_48;
|
||||
|
||||
public:
|
||||
|
||||
enum class PedalboardType {
|
||||
MainPedalboard,
|
||||
MainInsert,
|
||||
AuxInsert
|
||||
DECLARE_JSON_MAP(ControlValue);
|
||||
};
|
||||
static constexpr int64_t MAIN_INSERT_INSTANCE_BASE = TWO_POWER_52-2*MAX_INSTANCE_ID;
|
||||
static constexpr int64_t AUX_INSERT_INSTANCE_BASE = TWO_POWER_52-1*MAX_INSTANCE_ID;
|
||||
|
||||
static constexpr int64_t CHANNEL_ROUTER_CONTROLS_INSTANCE_ID = -4; // Reserved Instance ID for Router Controls.
|
||||
class PedalboardItem
|
||||
{
|
||||
public:
|
||||
using PropertyMap = std::map<std::string, atom_object>;
|
||||
int64_t instanceId_ = 0;
|
||||
std::string uri_;
|
||||
std::string pluginName_;
|
||||
bool isEnabled_ = true;
|
||||
std::vector<ControlValue> controlValues_;
|
||||
std::vector<PedalboardItem> topChain_;
|
||||
std::vector<PedalboardItem> bottomChain_;
|
||||
std::vector<MidiBinding> midiBindings_;
|
||||
std::optional<MidiChannelBinding> midiChannelBinding_;
|
||||
std::string vstState_;
|
||||
uint32_t stateUpdateCount_ = 0;
|
||||
Lv2PluginState lv2State_;
|
||||
std::string lilvPresetUri_;
|
||||
std::map<std::string, std::string> pathProperties_;
|
||||
std::string title_;
|
||||
bool useModUi_ = false;
|
||||
std::string iconColor_;
|
||||
int64_t sideChainInputId_ = -1;
|
||||
|
||||
static constexpr int64_t START_CONTROL_ID = -2; // synthetic PedalboardItem for input volume.
|
||||
static constexpr int64_t END_CONTROL_ID = -3; // synthetic PedalboardItem for output volume.
|
||||
static constexpr int64_t MAIN_INSERT_START_CONTROL_ID = MAIN_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -2;
|
||||
static constexpr int64_t MAIN_INSERT_END_CONTROL_ID = MAIN_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -3;
|
||||
static constexpr int64_t AUX_INSERT_START_CONTROL_ID = AUX_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -2;
|
||||
static constexpr int64_t AUX_INSERT_END_CONTROL_ID = AUX_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID -3;
|
||||
// non persistent state.
|
||||
PropertyMap patchProperties;
|
||||
|
||||
static PedalboardType GetInstanceTypeFromInstanceId(int64_t instanceId) {
|
||||
if (instanceId == START_CONTROL_ID) return PedalboardType::MainPedalboard;
|
||||
if (instanceId == END_CONTROL_ID) return PedalboardType::MainPedalboard;
|
||||
public:
|
||||
ControlValue *GetControlValue(const std::string &symbol);
|
||||
const ControlValue *GetControlValue(const std::string &symbol) const;
|
||||
bool SetControlValue(const std::string &key, float value);
|
||||
|
||||
if (instanceId >= MAIN_INSERT_INSTANCE_BASE) {
|
||||
return PedalboardType::MainInsert;
|
||||
} else if (instanceId >= AUX_INSERT_INSTANCE_BASE) {
|
||||
return PedalboardType::AuxInsert;
|
||||
} else {
|
||||
return PedalboardType::MainPedalboard;
|
||||
bool IsStructurallyIdentical(const PedalboardItem &other) const;
|
||||
|
||||
void ApplySnapshotValue(SnapshotValue *snapshotValue);
|
||||
void ApplyDefaultValues(PluginHost &pluginHost);
|
||||
bool hasLv2State() const
|
||||
{
|
||||
return lv2State_.isValid_ != 0;
|
||||
}
|
||||
}
|
||||
PedalboardType GetInstanceType() const {
|
||||
return GetInstanceTypeFromInstanceId(this->nextInstanceId_); // instanceId is irrelevant here.
|
||||
}
|
||||
GETTER_SETTER(instanceId)
|
||||
GETTER_SETTER_REF(uri)
|
||||
GETTER_SETTER_REF(vstState);
|
||||
GETTER_SETTER_REF(pluginName)
|
||||
GETTER_SETTER(isEnabled)
|
||||
GETTER_SETTER_VEC(controlValues)
|
||||
GETTER_SETTER_VEC(topChain)
|
||||
GETTER_SETTER_VEC(bottomChain)
|
||||
GETTER_SETTER_VEC(midiBindings)
|
||||
GETTER_SETTER_REF(midiChannelBinding)
|
||||
GETTER_SETTER_REF(pathProperties)
|
||||
GETTER_SETTER(stateUpdateCount)
|
||||
GETTER_SETTER_REF(lv2State)
|
||||
GETTER_SETTER_REF(title)
|
||||
GETTER_SETTER_REF(iconColor)
|
||||
GETTER_SETTER(useModUi)
|
||||
GETTER_SETTER(sideChainInputId)
|
||||
|
||||
Lv2PluginState &lv2State() { return lv2State_; } // non-const version.
|
||||
GETTER_SETTER_REF(lilvPresetUri)
|
||||
|
||||
PropertyMap &PatchProperties() { return patchProperties; }
|
||||
const PropertyMap &PatchProperties() const { return patchProperties; }
|
||||
|
||||
Pedalboard(PedalboardType instanceType = PedalboardType::MainPedalboard);
|
||||
bool isSplit() const
|
||||
{
|
||||
return uri_ == SPLIT_PEDALBOARD_ITEM_URI;
|
||||
}
|
||||
bool isEmpty() const
|
||||
{
|
||||
return uri_ == EMPTY_PEDALBOARD_ITEM_URI;
|
||||
}
|
||||
|
||||
// deep copy, breaking shared pointers.
|
||||
Pedalboard DeepCopy();
|
||||
|
||||
bool SetControlValue(int64_t pedalItemId, const std::string &symbol, float value);
|
||||
bool SetItemTitle(int64_t pedalItemId, const std::string &title, const std::string&iconColor);
|
||||
bool SetItemEnabled(int64_t pedalItemId, bool enabled);
|
||||
bool SetItemUseModUi(int64_t pedalItemId, bool enabled);
|
||||
void SetCurrentSnapshotModified(bool modified);
|
||||
void AddToSnapshotFromCurrentSettings(Snapshot &snapshot) const;
|
||||
void AddResetsForMissingProperties(Snapshot &snapshot, size_t *index) const;
|
||||
|
||||
bool IsStructureIdentical(const Pedalboard &other) const; // caan we just send a snapshot-style uddate instead of reloading plugins? All settings are ignored.
|
||||
Snapshot MakeSnapshotFromCurrentSettings(const Pedalboard &previousPedalboard);
|
||||
// virtual void write_members(json_writer&writer) const {
|
||||
// writer.write_member("instanceId",instanceId_);
|
||||
// writer.write_member("uri",uri_);
|
||||
// writer.write_member("pluginName",pluginName_);
|
||||
// writer.write_member("isEnabled",isEnabled_);
|
||||
// if (isSplit())
|
||||
// {
|
||||
// writer.write_member("topChain",topChain_);
|
||||
// writer.write_member("bottomChain",bottomChain_);
|
||||
// }
|
||||
// }
|
||||
|
||||
PedalboardItem*GetItem(int64_t pedalItemId);
|
||||
const PedalboardItem*GetItem(int64_t pedalItemId) const;
|
||||
std::vector<PedalboardItem*>GetAllPlugins();
|
||||
DECLARE_JSON_MAP(PedalboardItem);
|
||||
};
|
||||
|
||||
bool HasItem(int64_t pedalItemid) const { return GetItem(pedalItemid) != nullptr; }
|
||||
bool ApplySnapshot(int64_t snapshotIndex, PluginHost &pluginHost);
|
||||
class SnapshotValue
|
||||
{
|
||||
public:
|
||||
uint64_t instanceId_;
|
||||
bool isEnabled_ = true;
|
||||
std::vector<ControlValue> controlValues_;
|
||||
Lv2PluginState lv2State_;
|
||||
std::map<std::string, std::string> pathProperties_;
|
||||
DECLARE_JSON_MAP(SnapshotValue);
|
||||
|
||||
GETTER_SETTER_REF(name)
|
||||
GETTER_SETTER_VEC(items)
|
||||
GETTER_SETTER(input_volume_db)
|
||||
GETTER_SETTER(output_volume_db)
|
||||
GETTER_SETTER_VEC(snapshots)
|
||||
GETTER_SETTER(selectedSnapshot)
|
||||
GETTER_SETTER(selectedPlugin)
|
||||
ControlValue *GetControlValue(const std::string &key)
|
||||
{
|
||||
for (ControlValue &controlValue : controlValues_)
|
||||
{
|
||||
if (controlValue.key() == key)
|
||||
{
|
||||
return &controlValue;
|
||||
}
|
||||
}
|
||||
return nullptr;
|
||||
}
|
||||
void SetControlValue(const std::string &key, float value)
|
||||
{
|
||||
for (ControlValue &controlValue : controlValues_)
|
||||
{
|
||||
if (controlValue.key() == key)
|
||||
{
|
||||
controlValue.value(value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
controlValues_.push_back(ControlValue{key, value});
|
||||
}
|
||||
};
|
||||
|
||||
class Snapshot
|
||||
{
|
||||
public:
|
||||
std::string name_;
|
||||
std::string color_;
|
||||
bool isModified_ = false;
|
||||
std::vector<SnapshotValue> values_;
|
||||
|
||||
DECLARE_JSON_MAP(Pedalboard);
|
||||
DECLARE_JSON_MAP(Snapshot);
|
||||
};
|
||||
|
||||
PedalboardItem MakeEmptyItem();
|
||||
PedalboardItem MakeSplit();
|
||||
enum class PedalboardType
|
||||
{
|
||||
Invalid = 0,
|
||||
MainPedalboard = 1,
|
||||
MainInserts = 2,
|
||||
AuxInserts = 3
|
||||
};
|
||||
|
||||
class Pedalboard
|
||||
{
|
||||
std::string name_;
|
||||
float input_volume_db_ = 0;
|
||||
float output_volume_db_ = 0;
|
||||
|
||||
static Pedalboard MakeDefault(Pedalboard::PedalboardType instanceType = Pedalboard::PedalboardType::MainPedalboard);
|
||||
};
|
||||
std::vector<PedalboardItem> items_;
|
||||
uint64_t nextInstanceId_ = 0;
|
||||
uint64_t NextInstanceId() { return ++nextInstanceId_; }
|
||||
|
||||
std::vector<std::shared_ptr<Snapshot>> snapshots_;
|
||||
int64_t selectedSnapshot_ = -1;
|
||||
|
||||
int64_t selectedPlugin_ = -1;
|
||||
|
||||
static constexpr int64_t TWO_POWER_52 = 4503599627370496; // Maximum Javascript integer value.
|
||||
static constexpr int64_t TWO_POWER_48 = 281474976710656; // Number of possible instance IDs for Main Pedalboards.
|
||||
static constexpr int64_t MAX_INSTANCE_ID = TWO_POWER_48;
|
||||
|
||||
public:
|
||||
static constexpr int64_t MAIN_INSERT_INSTANCE_BASE = TWO_POWER_52 - 2 * MAX_INSTANCE_ID;
|
||||
static constexpr int64_t AUX_INSERT_INSTANCE_BASE = TWO_POWER_52 - 1 * MAX_INSTANCE_ID;
|
||||
|
||||
static constexpr int64_t CHANNEL_ROUTER_CONTROLS_INSTANCE_ID = -4; // Reserved Instance ID for Router Controls.
|
||||
|
||||
static constexpr int64_t START_CONTROL_ID = -2; // synthetic PedalboardItem for input volume.
|
||||
static constexpr int64_t END_CONTROL_ID = -3; // synthetic PedalboardItem for output volume.
|
||||
static constexpr int64_t MAIN_INSERT_START_CONTROL_ID = MAIN_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID - 2;
|
||||
static constexpr int64_t MAIN_INSERT_END_CONTROL_ID = MAIN_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID - 3;
|
||||
static constexpr int64_t AUX_INSERT_START_CONTROL_ID = AUX_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID - 2;
|
||||
static constexpr int64_t AUX_INSERT_END_CONTROL_ID = AUX_INSERT_INSTANCE_BASE + MAX_INSTANCE_ID - 3;
|
||||
|
||||
static PedalboardType GetPedalboardTypeFromInstanceId(int64_t instanceId)
|
||||
{
|
||||
if (instanceId == START_CONTROL_ID)
|
||||
return PedalboardType::MainPedalboard;
|
||||
if (instanceId == END_CONTROL_ID)
|
||||
return PedalboardType::MainPedalboard;
|
||||
|
||||
if (instanceId >= MAIN_INSERT_INSTANCE_BASE)
|
||||
{
|
||||
return PedalboardType::MainInserts;
|
||||
}
|
||||
else if (instanceId >= AUX_INSERT_INSTANCE_BASE)
|
||||
{
|
||||
return PedalboardType::AuxInserts;
|
||||
}
|
||||
else
|
||||
{
|
||||
return PedalboardType::MainPedalboard;
|
||||
}
|
||||
}
|
||||
PedalboardType GetInstanceType() const
|
||||
{
|
||||
return GetPedalboardTypeFromInstanceId(this->nextInstanceId_); // instanceId is irrelevant here.
|
||||
}
|
||||
|
||||
Pedalboard(PedalboardType instanceType = PedalboardType::MainPedalboard);
|
||||
|
||||
// deep copy, breaking shared pointers.
|
||||
Pedalboard DeepCopy();
|
||||
|
||||
bool SetControlValue(int64_t pedalItemId, const std::string &symbol, float value);
|
||||
bool SetItemTitle(int64_t pedalItemId, const std::string &title, const std::string &iconColor);
|
||||
bool SetItemEnabled(int64_t pedalItemId, bool enabled);
|
||||
bool SetItemUseModUi(int64_t pedalItemId, bool enabled);
|
||||
void SetCurrentSnapshotModified(bool modified);
|
||||
|
||||
bool IsStructureIdentical(const Pedalboard &other) const; // caan we just send a snapshot-style uddate instead of reloading plugins? All settings are ignored.
|
||||
Snapshot MakeSnapshotFromCurrentSettings(const Pedalboard &previousPedalboard);
|
||||
|
||||
PedalboardItem *GetItem(int64_t pedalItemId);
|
||||
const PedalboardItem *GetItem(int64_t pedalItemId) const;
|
||||
std::vector<PedalboardItem *> GetAllPlugins();
|
||||
|
||||
bool HasItem(int64_t pedalItemid) const { return GetItem(pedalItemid) != nullptr; }
|
||||
bool ApplySnapshot(int64_t snapshotIndex, PluginHost &pluginHost);
|
||||
|
||||
GETTER_SETTER_REF(name)
|
||||
GETTER_SETTER_VEC(items)
|
||||
GETTER_SETTER(input_volume_db)
|
||||
GETTER_SETTER(output_volume_db)
|
||||
GETTER_SETTER_VEC(snapshots)
|
||||
GETTER_SETTER(selectedSnapshot)
|
||||
GETTER_SETTER(selectedPlugin)
|
||||
|
||||
DECLARE_JSON_MAP(Pedalboard);
|
||||
|
||||
PedalboardItem MakeEmptyItem();
|
||||
PedalboardItem MakeSplit();
|
||||
|
||||
static Pedalboard MakeDefault(PedalboardType instanceType = PedalboardType::MainPedalboard);
|
||||
};
|
||||
|
||||
#undef GETTER_SETTER_REF
|
||||
#undef GETTER_SETTER_VEC
|
||||
#undef GETTER_SETTER
|
||||
|
||||
|
||||
|
||||
} // namespace pipedal
|
||||
@@ -169,6 +169,10 @@ public:
|
||||
delete[] inputBuffers;
|
||||
delete[] outputBuffers;
|
||||
}
|
||||
virtual bool OnRealtimeUpdateDeviceVus(size_t nFrames) {
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<int64_t> SelectChannels(const std::vector<std::string> &available, const std::vector<int> &selection)
|
||||
{
|
||||
std::vector<int64_t> result;
|
||||
|
||||
@@ -30,3 +30,13 @@
|
||||
#define PIPEDAL_NON_INLINE
|
||||
#endif
|
||||
#endif
|
||||
|
||||
#ifndef PIPEDAL_RESTRICT
|
||||
#if defined(__GNUC__) || defined(__clang__)
|
||||
#define PIPEDAL_RESTRICT __restrict__
|
||||
#elif defined(_MSC_VER)
|
||||
#define PIPEDAL_RESTRICT __restrict
|
||||
#else
|
||||
#define PIPEDAL_RESTRICT
|
||||
#endif
|
||||
#endif
|
||||
|
||||
@@ -82,7 +82,7 @@ PiPedalModel::PiPedalModel()
|
||||
this->updater = Updater::Create();
|
||||
this->updater->Start();
|
||||
this->currentUpdateStatus = updater->GetCurrentStatus();
|
||||
this->pedalboard = Pedalboard::MakeDefault(Pedalboard::PedalboardType::MainPedalboard);
|
||||
this->pedalboard = Pedalboard::MakeDefault(PedalboardType::MainPedalboard);
|
||||
|
||||
this->jackServerSettings = this->storage.GetJackServerSettings();
|
||||
|
||||
@@ -394,7 +394,7 @@ void PiPedalModel::Load()
|
||||
if (CrashGuard::HasCrashed())
|
||||
{
|
||||
// ignore the current preset, and load a blank pedalboard in order to avoid a potential plugin crash.
|
||||
this->pedalboard = Pedalboard::MakeDefault(Pedalboard::PedalboardType::MainPedalboard);
|
||||
this->pedalboard = Pedalboard::MakeDefault(PedalboardType::MainPedalboard);
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1732,7 +1732,7 @@ void PiPedalModel::OnNotifyMidiValueChanged(int64_t instanceId, int portIndex, f
|
||||
}
|
||||
}
|
||||
|
||||
void PiPedalModel::OnNotifyVusSubscription(const std::vector<VuUpdate> &updates)
|
||||
void PiPedalModel::OnNotifyVusSubscription(const std::vector<VuUpdateX> &updates)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
for (size_t i = 0; i < updates.size(); ++i)
|
||||
@@ -1746,6 +1746,22 @@ void PiPedalModel::OnNotifyVusSubscription(const std::vector<VuUpdate> &updates)
|
||||
}
|
||||
}
|
||||
|
||||
static bool isStartOrEndControl(int64_t controlId)
|
||||
{
|
||||
switch (controlId)
|
||||
{
|
||||
case Pedalboard::START_CONTROL_ID:
|
||||
case Pedalboard::END_CONTROL_ID:
|
||||
case Pedalboard::MAIN_INSERT_START_CONTROL_ID:
|
||||
case Pedalboard::MAIN_INSERT_END_CONTROL_ID:
|
||||
case Pedalboard::AUX_INSERT_START_CONTROL_ID:
|
||||
case Pedalboard::AUX_INSERT_END_CONTROL_ID:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
void PiPedalModel::UpdateRealtimeVuSubscriptions()
|
||||
{
|
||||
std::set<int64_t> addedInstances;
|
||||
@@ -1753,7 +1769,7 @@ void PiPedalModel::UpdateRealtimeVuSubscriptions()
|
||||
for (int i = 0; i < activeVuSubscriptions.size(); ++i)
|
||||
{
|
||||
auto instanceId = activeVuSubscriptions[i].instanceid;
|
||||
if (pedalboard.HasItem(instanceId) || instanceId == Pedalboard::START_CONTROL_ID || instanceId == Pedalboard::END_CONTROL_ID)
|
||||
if (pedalboard.HasItem(instanceId) || isStartOrEndControl(instanceId))
|
||||
{
|
||||
addedInstances.insert(activeVuSubscriptions[i].instanceid);
|
||||
}
|
||||
@@ -1778,8 +1794,15 @@ int64_t PiPedalModel::MonitorPort(int64_t instanceId, const std::string &key, fl
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> lock(mutex);
|
||||
int64_t subscriptionId = ++nextSubscriptionId;
|
||||
PedalboardType pedalboardType = Pedalboard::GetPedalboardTypeFromInstanceId(instanceId);
|
||||
activeMonitorPortSubscriptions.push_back(
|
||||
MonitorPortSubscription{subscriptionId, instanceId, key, updateInterval, onUpdate});
|
||||
MonitorPortSubscription{
|
||||
subscriptionId,
|
||||
pedalboardType,
|
||||
instanceId,
|
||||
key,
|
||||
updateInterval,
|
||||
onUpdate});
|
||||
|
||||
UpdateRealtimeMonitorPortSubscriptions();
|
||||
|
||||
|
||||
@@ -75,7 +75,7 @@ namespace pipedal
|
||||
virtual void OnSelectedSnapshotChanged(int64_t selectedSnapshot) = 0;
|
||||
virtual void OnPluginPresetsChanged(const std::string &pluginUri) = 0;
|
||||
virtual void OnChannelRouterSettingsChanged(int64_t clientId, const ChannelRouterSettings &channelRouterSettings) = 0;
|
||||
virtual void OnVuMeterUpdate(const std::vector<VuUpdate> &updates) = 0;
|
||||
virtual void OnVuMeterUpdate(const std::vector<VuUpdateX> &updates) = 0;
|
||||
virtual void OnBankIndexChanged(const BankIndex &bankIndex) = 0;
|
||||
virtual void OnJackServerSettingsChanged(const JackServerSettings &jackServerSettings) = 0;
|
||||
virtual void OnJackConfigurationChanged(const JackConfiguration &jackServerConfiguration) = 0;
|
||||
@@ -251,7 +251,7 @@ namespace pipedal
|
||||
private: // IAudioHostCallbacks
|
||||
virtual void OnNotifyLv2StateChanged(uint64_t instanceId) override;
|
||||
virtual bool OnNotifyMaybeLv2StateChanged(uint64_t instanceId) override;
|
||||
virtual void OnNotifyVusSubscription(const std::vector<VuUpdate> &updates) override;
|
||||
virtual void OnNotifyVusSubscription(const std::vector<VuUpdateX> &updates) override;
|
||||
virtual void OnNotifyMonitorPort(const MonitorPortUpdate &update) override;
|
||||
virtual void OnNotifyMidiValueChanged(int64_t instanceId, int portIndex, float value) override;
|
||||
virtual void OnNotifyMidiListen(uint8_t cc0, uint8_t cc1, uint8_t cc2) override;
|
||||
|
||||
@@ -2371,15 +2371,15 @@ private:
|
||||
int updateRequestOutstanding = 0;
|
||||
bool vuUpdateDropped = false;
|
||||
|
||||
virtual void OnVuMeterUpdate(const std::vector<VuUpdate> &updates)
|
||||
virtual void OnVuMeterUpdate(const std::vector<VuUpdateX> &updates)
|
||||
{
|
||||
std::lock_guard<std::recursive_mutex> guard(subscriptionMutex);
|
||||
if (updateRequestOutstanding < 5) // throttle to accomodate a web page that can't keep up.
|
||||
if (updateRequestOutstanding < 1) // throttle to accomodate a web page that can't keep up.
|
||||
{
|
||||
vuUpdateDropped = false;
|
||||
for (int i = 0; i < updates.size(); ++i)
|
||||
{
|
||||
const VuUpdate &vuUpdate = updates[i];
|
||||
const VuUpdateX &vuUpdate = updates[i];
|
||||
bool interested = false;
|
||||
for (int i = 0; i < this->activeVuSubscriptions.size(); ++i)
|
||||
{
|
||||
@@ -2392,7 +2392,7 @@ private:
|
||||
if (interested)
|
||||
{
|
||||
updateRequestOutstanding++;
|
||||
this->Request<bool, VuUpdate>(
|
||||
this->Request<bool, VuUpdateX>(
|
||||
"onVuUpdate",
|
||||
vuUpdate,
|
||||
[this](const bool &result)
|
||||
|
||||
@@ -106,11 +106,11 @@ namespace pipedal
|
||||
|
||||
struct RealtimePedalboardItemIndex {
|
||||
RealtimePedalboardItemIndex()
|
||||
: instanceType(Pedalboard::PedalboardType::MainPedalboard), index(-1)
|
||||
: instanceType(PedalboardType::MainPedalboard), index(-1)
|
||||
{
|
||||
}
|
||||
RealtimePedalboardItemIndex(
|
||||
Pedalboard::PedalboardType instanceType,
|
||||
PedalboardType instanceType,
|
||||
int64_t index)
|
||||
: instanceType(instanceType), index(index)
|
||||
{
|
||||
@@ -118,7 +118,7 @@ namespace pipedal
|
||||
RealtimePedalboardItemIndex(const RealtimePedalboardItemIndex& other) = default;
|
||||
RealtimePedalboardItemIndex& operator=(const RealtimePedalboardItemIndex& other) = default;
|
||||
|
||||
Pedalboard::PedalboardType instanceType = Pedalboard::PedalboardType::MainPedalboard;
|
||||
PedalboardType instanceType = PedalboardType::MainPedalboard;
|
||||
int64_t index = -1;
|
||||
};
|
||||
|
||||
@@ -150,6 +150,7 @@ namespace pipedal
|
||||
{
|
||||
public:
|
||||
RealtimeMonitorPortSubscription() { delete callbackPtr; }
|
||||
PedalboardType pedalboardType = PedalboardType::MainPedalboard;
|
||||
int64_t subscriptionHandle;
|
||||
int instanceIndex = 0;
|
||||
int portIndex = 0;
|
||||
@@ -174,7 +175,7 @@ namespace pipedal
|
||||
}
|
||||
bool waitingForAcknowledge = false;
|
||||
|
||||
const std::vector<VuUpdate> *GetResult(size_t currentSample)
|
||||
const std::vector<VuUpdateX> *GetResult(size_t currentSample)
|
||||
{
|
||||
for (size_t i = 0; i < vuUpdateWorkingData.size(); ++i)
|
||||
{
|
||||
@@ -186,8 +187,8 @@ namespace pipedal
|
||||
}
|
||||
|
||||
std::vector<RealtimePedalboardItemIndex> enabledIndexes;
|
||||
std::vector<VuUpdate> vuUpdateWorkingData;
|
||||
std::vector<VuUpdate> vuUpdateResponseData;
|
||||
std::vector<VuUpdateX> vuUpdateWorkingData;
|
||||
std::vector<VuUpdateX> vuUpdateResponseData;
|
||||
|
||||
void Reset()
|
||||
{
|
||||
@@ -494,7 +495,7 @@ namespace pipedal
|
||||
write(RingBufferCommand::SendMonitorPortUpdate, body);
|
||||
}
|
||||
|
||||
void SendVuUpdate(const std::vector<VuUpdate> *pUpdates)
|
||||
void SendVuUpdate(const std::vector<VuUpdateX> *pUpdates)
|
||||
{
|
||||
write(RingBufferCommand::SendVuUpdate, pUpdates);
|
||||
}
|
||||
|
||||
@@ -472,7 +472,7 @@ void Storage::LoadBankIndex()
|
||||
if (bankIndex.entries().size() == 0)
|
||||
{
|
||||
currentBank.clear();
|
||||
Pedalboard defaultPedalboard = Pedalboard::MakeDefault(Pedalboard::PedalboardType::MainPedalboard);
|
||||
Pedalboard defaultPedalboard = Pedalboard::MakeDefault(PedalboardType::MainPedalboard);
|
||||
int64_t instanceId = currentBank.addPreset(defaultPedalboard);
|
||||
currentBank.selectedPreset(instanceId);
|
||||
|
||||
|
||||
@@ -23,14 +23,15 @@
|
||||
using namespace pipedal;
|
||||
|
||||
|
||||
JSON_MAP_BEGIN(VuUpdate)
|
||||
JSON_MAP_REFERENCE(VuUpdate,instanceId)
|
||||
JSON_MAP_REFERENCE(VuUpdate,sampleTime)
|
||||
JSON_MAP_REFERENCE(VuUpdate,isStereoInput)
|
||||
JSON_MAP_REFERENCE(VuUpdate,isStereoOutput)
|
||||
JSON_MAP_REFERENCE(VuUpdate,inputMaxValueL)
|
||||
JSON_MAP_REFERENCE(VuUpdate,inputMaxValueR)
|
||||
JSON_MAP_REFERENCE(VuUpdate,outputMaxValueL)
|
||||
JSON_MAP_REFERENCE(VuUpdate,outputMaxValueR)
|
||||
JSON_MAP_BEGIN(VuUpdateX)
|
||||
JSON_MAP_REFERENCE(VuUpdateX,pedalboardType)
|
||||
JSON_MAP_REFERENCE(VuUpdateX,instanceId)
|
||||
JSON_MAP_REFERENCE(VuUpdateX,sampleTime)
|
||||
JSON_MAP_REFERENCE(VuUpdateX,isStereoInput)
|
||||
JSON_MAP_REFERENCE(VuUpdateX,isStereoOutput)
|
||||
JSON_MAP_REFERENCE(VuUpdateX,inputMaxValueL)
|
||||
JSON_MAP_REFERENCE(VuUpdateX,inputMaxValueR)
|
||||
JSON_MAP_REFERENCE(VuUpdateX,outputMaxValueL)
|
||||
JSON_MAP_REFERENCE(VuUpdateX,outputMaxValueR)
|
||||
JSON_MAP_END()
|
||||
|
||||
|
||||
@@ -20,12 +20,14 @@
|
||||
#pragma once
|
||||
|
||||
#include "json.hpp"
|
||||
#include "Pedalboard.hpp"
|
||||
|
||||
namespace pipedal
|
||||
{
|
||||
class VuUpdate
|
||||
class VuUpdateX
|
||||
{
|
||||
public:
|
||||
int pedalboardType_ = (int)PedalboardType::Invalid;
|
||||
int64_t instanceId_ = 0;
|
||||
long sampleTime_ = 0;
|
||||
bool isStereoInput_ = false;
|
||||
@@ -36,6 +38,9 @@ namespace pipedal
|
||||
float outputMaxValueR_ = 0;
|
||||
|
||||
public:
|
||||
|
||||
PedalboardType pedalboardType() const { return (PedalboardType)pedalboardType_; }
|
||||
void pedalboardType(PedalboardType value) { pedalboardType_ = (int)value; }
|
||||
void reset() {
|
||||
inputMaxValueL_ = 0;
|
||||
inputMaxValueR_ = 0;
|
||||
@@ -45,7 +50,10 @@ namespace pipedal
|
||||
|
||||
void AccumulateVu(float *value,float *input, uint32_t samples)
|
||||
{
|
||||
|
||||
if (input == nullptr) {
|
||||
*value = 0;
|
||||
return;
|
||||
}
|
||||
float v = *value;
|
||||
for (uint32_t i = 0; i < samples; ++i)
|
||||
{
|
||||
@@ -65,7 +73,6 @@ namespace pipedal
|
||||
{
|
||||
AccumulateVu(&inputMaxValueL_,inputL,samples);
|
||||
AccumulateVu(&inputMaxValueR_,inputR,samples);
|
||||
|
||||
}
|
||||
void AccumulateOutputs(float* output, uint32_t samples)
|
||||
{
|
||||
@@ -78,6 +85,6 @@ namespace pipedal
|
||||
AccumulateVu(&outputMaxValueR_,outputR,samples);
|
||||
}
|
||||
|
||||
DECLARE_JSON_MAP(VuUpdate);
|
||||
DECLARE_JSON_MAP(VuUpdateX);
|
||||
};
|
||||
}
|
||||
|
Before Width: | Height: | Size: 9.5 KiB After Width: | Height: | Size: 9.7 KiB |
|
Before Width: | Height: | Size: 27 KiB After Width: | Height: | Size: 28 KiB |
@@ -15,10 +15,24 @@ function isActiveChannel(channels: number[]): boolean {
|
||||
return channels[1] !== -1 || channels[0] !== -1;
|
||||
}
|
||||
|
||||
function chName(ch: number): string {
|
||||
function chName(ch: number, maxChannels: number): string {
|
||||
if (ch === -1) {
|
||||
return "None";
|
||||
}
|
||||
if (ch === MAIN_OUT_LEFT_CHANNEL) {
|
||||
return "Main Out L";
|
||||
}
|
||||
if (ch === MAIN_OUT_RIGHT_CHANNEL) {
|
||||
return "Main Out R";
|
||||
}
|
||||
if (maxChannels <= 2) {
|
||||
if (ch === 0) {
|
||||
return "Left";
|
||||
}
|
||||
if (ch === 1) {
|
||||
return "Right";
|
||||
}
|
||||
}
|
||||
return "Ch" + (ch + 1);
|
||||
}
|
||||
function channelPairName(channels: number[], maxChannels: number, input: boolean): string {
|
||||
@@ -39,17 +53,14 @@ function channelPairName(channels: number[], maxChannels: number, input: boolean
|
||||
if (channels[0] === 0 && (channels[0] === channels[1] || channels[1] === -1)) {
|
||||
return "Left";
|
||||
}
|
||||
if (channels[1] === 1 && (channels[0] === channels[1])) {
|
||||
if (channels[1] === 1 && (channels[0] === channels[1] || channels[0] === -1)) {
|
||||
return "Right";
|
||||
}
|
||||
if (channels[0] === 1 && channels[1] === 0) {
|
||||
return "Right,Left";
|
||||
}
|
||||
}
|
||||
if (channels[0] === channels[1]) {
|
||||
return chName(channels[0]);
|
||||
if (channels[0] === channels[1] || channels[1] === -1) {
|
||||
return chName(channels[0],maxChannels);
|
||||
}
|
||||
return '[' + chName(channels[0]) + " " + chName(channels[1]) + "]";
|
||||
return '[' + chName(channels[0],maxChannels) + " " + chName(channels[1],maxChannels) + "]";
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -19,6 +19,7 @@
|
||||
|
||||
import React from 'react';
|
||||
import { useState } from 'react';
|
||||
import VuMeter from './VuMeter';
|
||||
import HelpOutlineIcon from '@mui/icons-material/HelpOutline';
|
||||
import Divider from '@mui/material/Divider';
|
||||
import MoreVertIcon from '@mui/icons-material/MoreVert';
|
||||
@@ -45,8 +46,8 @@ import { PiPedalModel, PiPedalModelFactory } from './PiPedalModel';
|
||||
import { JackConfiguration } from './Jack';
|
||||
|
||||
|
||||
let debugInputChannels: number | null = 1;
|
||||
let debugOutputChannels: number | null = 1;
|
||||
let debugInputChannels: number | null = null;
|
||||
let debugOutputChannels: number | null = null;
|
||||
|
||||
|
||||
export interface ChannelRouterSettingsDialogProps {
|
||||
@@ -539,11 +540,25 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
</Select>
|
||||
)
|
||||
}
|
||||
let Vu = () => {
|
||||
let Vu = (routeType: RouteType, input: boolean) => {
|
||||
let instanceId: number;
|
||||
switch (routeType) {
|
||||
case RouteType.Main:
|
||||
instanceId = input ? Pedalboard.START_CONTROL : Pedalboard.MAIN_INSERT_END_CONTROL_ID;
|
||||
break;
|
||||
case RouteType.Aux:
|
||||
instanceId = input ? Pedalboard.AUX_INSERT_START_CONTROL_ID : Pedalboard.AUX_INSERT_END_CONTROL_ID;
|
||||
break;
|
||||
case RouteType.Send:
|
||||
instanceId = input ? Pedalboard.SEND_RETURN_CONTROL : Pedalboard.SEND_OUTPUT_CONTROL;
|
||||
|
||||
}
|
||||
return (
|
||||
<div style={{}} >
|
||||
<div style={{ width: 8, height: 48, background: "black" }}></div>
|
||||
{/* <VuMeter instanceId={Pedalboard.CHANNEL_ROUTER_MAIN_INSERT_ID} display={"input"} /> */}
|
||||
|
||||
<VuMeter instanceId={instanceId}
|
||||
display={input? "input": "output"}
|
||||
height={48} />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
@@ -603,7 +618,7 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
|
||||
let PresetSelect = (width: number | string | undefined) => {
|
||||
return (
|
||||
<div style={{ display: "flex", flexDirection: "row", marginTop: 2, alignItems: "center" }}>
|
||||
<div style={{ display: "flex", flexDirection: "row", alignItems: "center", width: width }}>
|
||||
<div style={{ flex: "0 0 auto" }}>
|
||||
<IconButtonEx tooltip="Save current preset"
|
||||
style={{ flex: "0 0 auto", color: "#FFFFFF" }}
|
||||
@@ -622,7 +637,7 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
{GeneratePresetMenuItems()}
|
||||
</Select>
|
||||
</div>
|
||||
<IconButtonEx tooltip="Presets" aria-label="more-presets" style={{ marginRight: 24 }}>
|
||||
<IconButtonEx tooltip="Presets" aria-label="more-presets" style={{ marginRight: 8 }}>
|
||||
<MoreVertIcon style={{ opacity: 0.6 }} onClick={() => { }} />
|
||||
</IconButtonEx>
|
||||
</div>
|
||||
@@ -658,14 +673,14 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
<td style={cellLeft}>
|
||||
{ChannelSelect(RouteType.Main, 0, true)}
|
||||
</td>
|
||||
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
||||
<td rowSpan={2} style={cellLeft}>{Vu(RouteType.Main, true)}</td>
|
||||
<td rowSpan={2} style={cellLeft}>
|
||||
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
|
||||
Inserts
|
||||
</Button>
|
||||
</td>
|
||||
|
||||
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
||||
<td rowSpan={2} style={cellLeft}>{Vu(RouteType.Main, false)}</td>
|
||||
<td style={cellLeft}>
|
||||
{ChannelSelect(RouteType.Main, 0, false)}
|
||||
</td>
|
||||
@@ -691,18 +706,17 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
<td style={cellLeft}>
|
||||
{(ChannelSelect(RouteType.Aux, 0, true))}
|
||||
</td>
|
||||
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
||||
<td rowSpan={2} style={cellLeft}>{Vu(RouteType.Aux, true)}</td>
|
||||
<td rowSpan={2} style={cellLeft}>
|
||||
<Button variant="outlined" style={{
|
||||
textTransform: "none", borderRadius: 24,
|
||||
|
||||
}}
|
||||
disabled={auxInsertDisabled}>
|
||||
Inserts
|
||||
</Button>
|
||||
</td>
|
||||
|
||||
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
||||
<td rowSpan={2} style={cellLeft}>{Vu(RouteType.Aux, false)}</td>
|
||||
<td style={cellLeft}>
|
||||
{(ChannelSelect(RouteType.Aux, 0, false))}
|
||||
</td>
|
||||
@@ -728,11 +742,11 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
<td style={cellLeft}>
|
||||
{(ChannelSelect(RouteType.Send, 0, false))}
|
||||
</td>
|
||||
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
||||
<td rowSpan={2} style={cellLeft}>{Vu(RouteType.Send,false)}</td>
|
||||
<td rowSpan={2} style={cellLeft}>
|
||||
{/* No button */}
|
||||
</td>
|
||||
<td rowSpan={2} style={cellLeft}>{Vu()}</td>
|
||||
<td rowSpan={2} style={cellLeft}>{Vu(RouteType.Send,true)}</td>
|
||||
<td style={cellLeft}>
|
||||
{(ChannelSelect(RouteType.Send, 0, true))}
|
||||
</td>
|
||||
@@ -798,7 +812,7 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
||||
<td style={cellPortraitControlStrip}>{Vu(RouteType.Main,true)}</td>
|
||||
<td style={cellPortraitControlStrip}>
|
||||
<div style={{ marginLeft: 8, marginRight: 8 }}>
|
||||
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
|
||||
@@ -807,7 +821,7 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
||||
<td style={cellPortraitControlStrip}>{Vu(RouteType.Main,false)}</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
@@ -862,7 +876,7 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
||||
<td style={cellPortraitControlStrip}>{Vu(RouteType.Aux, true)}</td>
|
||||
<td style={cellPortraitControlStrip}>
|
||||
<div style={{ marginLeft: 8, marginRight: 8 }}>
|
||||
<Button variant="outlined" style={{
|
||||
@@ -875,7 +889,7 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
||||
<td style={cellPortraitControlStrip}>{Vu(RouteType.Aux, false)}</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
@@ -929,7 +943,7 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
||||
<td style={cellPortraitControlStrip}>{Vu(RouteType.Send, false)}</td>
|
||||
<td style={cellPortraitControlStrip}>
|
||||
<div style={{ marginLeft: 8, marginRight: 8, visibility: "hidden" }}>
|
||||
<Button variant="outlined" style={{ textTransform: "none", borderRadius: 24 }}>
|
||||
@@ -938,7 +952,7 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
</div>
|
||||
</td>
|
||||
|
||||
<td style={cellPortraitControlStrip}>{Vu()}</td>
|
||||
<td style={cellPortraitControlStrip}>{Vu(RouteType.Send, true)}</td>
|
||||
</tr>
|
||||
|
||||
</tbody>
|
||||
@@ -1000,11 +1014,11 @@ function ChannelRouterSettingsDialog(props: ChannelRouterSettingsDialogProps) {
|
||||
{
|
||||
landscape ? (
|
||||
<>
|
||||
<div style={{ flexGrow: 1 }}> </div>
|
||||
<div style={{ flexShrink: 1, maxWidth: 440, display: "relative", marginLeft: 16, marginRight: 16 }}>
|
||||
{PresetSelect(200)}
|
||||
<div style={{ flexGrow: 1 }}></div>
|
||||
<div style={{ flexShrink: 0, display: "relative", marginLeft: 8, marginRight: 8 }}>
|
||||
{PresetSelect("325px")}
|
||||
</div>
|
||||
<div style={{ flexGrow: 1 }}> </div>
|
||||
<div style={{ flexGrow: 1 }}></div>
|
||||
</>
|
||||
|
||||
) : (
|
||||
|
||||
@@ -381,6 +381,8 @@ export class Pedalboard implements Deserializable<Pedalboard> {
|
||||
|
||||
static readonly START_CONTROL = -2; // synthetic PedalboardItem for input volume.
|
||||
static readonly END_CONTROL = -3; // synthetic PedalboardItem for output volume.
|
||||
static readonly SEND_OUTPUT_CONTROL = -4; // synthetic PedalboardItem for send output volume.
|
||||
static readonly SEND_RETURN_CONTROL = -5; // synthetic PedalboardItem for send return volume.
|
||||
static readonly MAIN_INSERT_START_CONTROL_ID = Pedalboard.MAIN_INSERT_INSTANCE_BASE + -2 + MAX_INSTANCE_ID;
|
||||
static readonly MAIN_INSERT_END_CONTROL_ID = Pedalboard.MAIN_INSERT_INSTANCE_BASE + -3 + MAX_INSTANCE_ID;
|
||||
static readonly AUX_INSERT_START_CONTROL_ID = Pedalboard.AUX_INSERT_INSTANCE_BASE + -2 + MAX_INSTANCE_ID
|
||||
|
||||
@@ -2517,6 +2517,7 @@ export class PiPedalModel //implements PiPedalModel
|
||||
} else {
|
||||
let newTarget = new VuSubscriptionTarget();
|
||||
newTarget.subscribers.push(result);
|
||||
this.vuSubscriptions[instanceId] = newTarget;
|
||||
|
||||
this.webSocket.request<number>("addVuSubscription", instanceId)
|
||||
.then((subscriptionHandle) => {
|
||||
@@ -2526,6 +2527,9 @@ export class PiPedalModel //implements PiPedalModel
|
||||
} else {
|
||||
newTarget.serverSubscriptionHandle = subscriptionHandle;
|
||||
}
|
||||
}).catch((error) => {
|
||||
// failed to subscribe.
|
||||
this.vuSubscriptions[instanceId] = undefined;
|
||||
});
|
||||
|
||||
this.vuSubscriptions[instanceId] = newTarget;
|
||||
|
||||
@@ -200,6 +200,7 @@ interface VuMeterProps extends WithStyles<typeof styles> {
|
||||
instanceId: number;
|
||||
display: "input" | "output";
|
||||
theme: Theme;
|
||||
height: number | undefined;
|
||||
displayText?: boolean;
|
||||
}
|
||||
|
||||
@@ -462,6 +463,20 @@ export const VuMeter =
|
||||
render() {
|
||||
let displayText = this.props.displayText??false;
|
||||
const classes = withStyles.getClasses(this.props);
|
||||
if (this.props.height !== undefined)
|
||||
{
|
||||
let height = this.props.height;
|
||||
let scale = height / DISPLAY_HEIGHT;
|
||||
return (
|
||||
<div style={{ height: height, transform: `scale(1.0, ${scale})`, transformOrigin: "top left" }}>
|
||||
<div className={this.state.isStereo? classes.stereoTextFrame: classes.monoTextFrame}>
|
||||
{
|
||||
this.renderVus()
|
||||
}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
if (displayText)
|
||||
{
|
||||
return (
|
||||
@@ -526,7 +541,7 @@ export const VuMeter =
|
||||
componentDidMount() {
|
||||
this.model.state.addOnChangedHandler(this.onStateChanged);
|
||||
|
||||
this.addVuSubscription();
|
||||
//this.addVuSubscription();
|
||||
}
|
||||
componentWillUnmount() {
|
||||
this.removeVuSubscription();
|
||||
|
||||
@@ -14,7 +14,7 @@ export default defineConfig({
|
||||
target: 'http://localhost:8080',
|
||||
changeOrigin: false,
|
||||
},
|
||||
'/var': {
|
||||
'^/var/(?!config\\.json$).*': {
|
||||
target: 'http://localhost:8080',
|
||||
changeOrigin: false,
|
||||
},
|
||||
|
||||