Re: [yoshimi-user] Multiple Jack outputs patch for yoshimi (Identation FIXED)

  • From: Andrew Deryabin <andrewderyabin@xxxxxxxxx>
  • To: yoshimi-user@xxxxxxxxxxxxxxxxxxxxx
  • Date: Wed, 01 May 2013 02:06:47 +0000

30.04.2013 20:33, Will J Godfrey пишет:

On Tue, 30 Apr 2013 08:08:01 +0000
Andrew Deryabin <andrewderyabin@xxxxxxxxx> wrote:

29.04.2013 20:15, Kristian Amlie пишет:
On 04/29/13 21:47, Will J Godfrey wrote:
We really now need more people to test this on different machine
configurations, with different material, and routing through external effects.
I'll try to have another go at this myself during this week (if I can find
time).
I'll try to use this in my synthrack during this week (Labour Day on
Wednesday, yey!).

The only thing I'm not too happy with is using 'mix' instead of the original
left and right. Although I can see it is technically more 'correct' it breaks
compatibility with older versions, especially for people who have scripts set
up.
On the other hand, maybe this is a good way of making people aware that
there is a change and that they now have separate outputs available. I'm
sure many people would take them into use if they saw them. I don't have
a strong opinion either way though.

Finally, to avoid scaring those less brave, can the master left and right be at
the top of the list instead of the bottom. That way, those who want to do a
quick connect won't be put off. People who are going to use the individual
outputs will be running up and down the list anyway so it won't make any
difference to them.
Yes, I forgot to mention it, that bugged me too.

Hi, All!

Thanks for testing! I'm glad that my patch works as expected. I've made
a little modification regarding order of jack output channels:
1. Changed the registration order of mixed channels - now they are in
the first place.
2. Renamed mix_l,mix_r to allmix_l,allmix_r because qjackctl orders
channels by name, not by registration order.

So, now mixed channels, allmix_l and allmix_r are in the head of the list.

Attached modified patch in message.
This wording is a definite improvement, and I confess I didn't realise that
qjackctl did a sort on the channel names :(

However, I think I have an idea for wording that should satisfy everyone.

Keep the original 'left' and 'right' but then use 'track_1_l' and 'track_1_r'
etc. This preserves backward compatibility, will sort correctly and is pretty
clear as to what it all means without being too much of a semantic stretch.


As a point of interest, in the early 1980s I wrote a timetabling program (in
BBC BASIC) for a local school, and learned first-hand that people are quite
happy to to have new names for new features, but change something they already
know and they look at you like you were caught drowning kittens!


Yes, Will, I agree with you.

Changing usual things is a pain for end users. I've corrected output channel names. Sending corected patch again.

Now mixed channels are called 'left' and 'right' as before. Separate channel outputs are called 'track_X_l' and 'track_X_r'.



From 8c5b2f26c7c6bfb84ebc528d083c9c21bc64d559 Mon Sep 17 00:00:00 2001
From: Andrew Deryabin <andrewderyabin@xxxxxxxxx>
Date: Wed, 1 May 2013 05:56:23 +0400
Subject: [PATCH 1/1] Add-support-for-multiple-jack-outputs

---
src/Misc/SynthEngine.cpp | 107 +++++++++++++++++++++++++++++----------------
src/Misc/SynthEngine.h | 2 +-
src/MusicIO/JackEngine.cpp | 65 ++++++++++++++++++++-------
src/MusicIO/JackEngine.h | 4 +-
src/MusicIO/MusicIO.cpp | 73 +++++++++++++++++++------------
src/MusicIO/MusicIO.h | 4 +-
src/version.txt | 2 +-
7 files changed, 168 insertions(+), 89 deletions(-)

diff --git a/src/Misc/SynthEngine.cpp b/src/Misc/SynthEngine.cpp
index 5bfb064..205098b 100644
--- a/src/Misc/SynthEngine.cpp
+++ b/src/Misc/SynthEngine.cpp
@@ -357,22 +357,26 @@ void SynthEngine::partonoff(int npart, int what)


// Master audio out (the final sound)
-void SynthEngine::MasterAudio(float *outl, float *outr)
+void SynthEngine::MasterAudio(float *outl [NUM_MIDI_PARTS], float *outr
[NUM_MIDI_PARTS])
{
- memset(outl, 0, bufferbytes);
- memset(outr, 0, bufferbytes);
+ int npart;
+ for (npart = 0; npart < (NUM_MIDI_PARTS + 1); ++npart)
+ {
+ memset(outl[npart], 0, bufferbytes);
+ memset(outr[npart], 0, bufferbytes);
+ }
if (isMuted())
return;

actionLock(lock);

- // Compute part samples and store them npart]->partoutl,partoutr
- int npart;
+ // Compute part samples and store them ->partoutl,partoutr
for (npart = 0; npart < NUM_MIDI_PARTS; ++npart)
if (part[npart]->Penabled)
{
part[npart]->ComputePartSmps();
}
+
// Insertion effects
int nefx;
for (nefx = 0; nefx < NUM_INS_EFX; ++nefx)
@@ -397,15 +401,12 @@ void SynthEngine::MasterAudio(float *outl, float *outr)
float oldvol_r = part[npart]->oldvolumer;
float newvol_l = part[npart]->pannedVolLeft();
float newvol_r = part[npart]->pannedVolRight();
- if (aboveAmplitudeThreshold(oldvol_l, newvol_l)
- || aboveAmplitudeThreshold(oldvol_r, newvol_r))
+ if (aboveAmplitudeThreshold(oldvol_l, newvol_l) ||
aboveAmplitudeThreshold(oldvol_r, newvol_r))
{ // the volume or the panning has changed and needs interpolation
for (int i = 0; i < buffersize; ++i)
{
- float vol_l = interpolateAmplitude(oldvol_l, newvol_l, i,
- buffersize);
- float vol_r = interpolateAmplitude(oldvol_r, newvol_r, i,
- buffersize);
+ float vol_l = interpolateAmplitude(oldvol_l, newvol_l, i,
buffersize);
+ float vol_r = interpolateAmplitude(oldvol_r, newvol_r, i,
buffersize);
part[npart]->partoutl[i] *= vol_l;
part[npart]->partoutr[i] *= vol_r;
}
@@ -466,18 +467,28 @@ void SynthEngine::MasterAudio(float *outl, float *outr)
float outvol = sysefx[nefx]->sysefxgetvolume();
for (int i = 0; i < buffersize; ++i)
{
- outl[i] += tmpmixl[i] * outvol;
- outr[i] += tmpmixr[i] * outvol;
+ outl[NUM_MIDI_PARTS][i] += tmpmixl[i] * outvol;
+ outr[NUM_MIDI_PARTS][i] += tmpmixr[i] * outvol;
}
}

- // Mix all parts
+ // Copy all parts
+ for (npart = 0; npart < NUM_MIDI_PARTS; ++npart)
+ {
+ for (int i = 0; i < buffersize; ++i)
+ {
+ outl[npart][i] = part[npart]->partoutl[i];
+ outr[npart][i] = part[npart]->partoutr[i];
+ }
+ }
+
+ // Mix all parts to mixed outputs
for (npart = 0; npart < NUM_MIDI_PARTS; ++npart)
{
for (int i = 0; i < buffersize; ++i)
{ // the volume did not change
- outl[i] += part[npart]->partoutl[i];
- outr[i] += part[npart]->partoutr[i];
+ outl[NUM_MIDI_PARTS][i] += outl[npart][i];
+ outr[NUM_MIDI_PARTS][i] += outr[npart][i];
}
}

@@ -486,7 +497,7 @@ void SynthEngine::MasterAudio(float *outl, float *outr)
{
if (Pinsparts[nefx] == -2)
{
- insefx[nefx]->out(outl, outr);
+ insefx[nefx]->out(outl[NUM_MIDI_PARTS], outr[NUM_MIDI_PARTS]);
}
}

@@ -502,41 +513,61 @@ void SynthEngine::MasterAudio(float *outl, float *outr)
vupeakLock(unlock);

float absval;
+ //Per output master volume and fade
+ for (npart = 0; npart < NUM_MIDI_PARTS; ++npart)
+ {
+ for (int idx = 0; idx < buffersize; ++idx)
+ {
+ outl[npart][idx] *= volume; // apply Master Volume
+ outr[npart][idx] *= volume;
+
+ if (shutup) // fade-out
+ {
+ float fade = (float) (buffersize - idx) / (float) buffersize;
+ outl[npart][idx] *= fade;
+ outr[npart][idx] *= fade;
+ }
+ }
+ }
+
+ //Master volume and clip calculation for mixed outputs
for (int idx = 0; idx < buffersize; ++idx)
{
- outl[idx] *= volume; // apply Master Volume
- outr[idx] *= volume;
+ outl[NUM_MIDI_PARTS][idx] *= volume; // apply Master Volume
+ outr[NUM_MIDI_PARTS][idx] *= volume;

- if ((absval = fabsf(outl[idx])) > vuoutpeakl) // Peak computation (for
vumeters)
+ if ((absval = fabsf(outl[NUM_MIDI_PARTS][idx])) > vuoutpeakl) // Peak
computation (for vumeters)
vuoutpeakl = absval;
- if ((absval = fabsf(outr[idx])) > vuoutpeakr)
+ if ((absval = fabsf(outr[NUM_MIDI_PARTS][idx])) > vuoutpeakr)
vuoutpeakr = absval;
- vurmspeakl += outl[idx] * outl[idx]; // RMS Peak
- vurmspeakr += outr[idx] * outr[idx];
+ vurmspeakl += outl[NUM_MIDI_PARTS][idx] * outl[NUM_MIDI_PARTS][idx];
// RMS Peak
+ vurmspeakr += outr[NUM_MIDI_PARTS][idx] * outr[NUM_MIDI_PARTS][idx];

// check for clips
- if (outl[idx] > 1.0f)
+ if (outl[NUM_MIDI_PARTS][idx] > 1.0f)
clippedL = true;
- else if (outl[idx] < -1.0f)
+ else if (outl[NUM_MIDI_PARTS][idx] < -1.0f)
clippedL = true;
- if (outr[idx] > 1.0f)
+ if (outr[NUM_MIDI_PARTS][idx] > 1.0f)
clippedR = true;
- else if (outr[idx] < -1.0f)
+ else if (outr[NUM_MIDI_PARTS][idx] < -1.0f)
clippedR = true;

if (shutup) // fade-out
{
- float fade = (float)(buffersize - idx) / (float)buffersize;
- outl[idx] *= fade;
- outr[idx] *= fade;
+ float fade = (float) (buffersize - idx) / (float) buffersize;
+ outl[NUM_MIDI_PARTS][idx] *= fade;
+ outr[NUM_MIDI_PARTS][idx] *= fade;
}
}
if (shutup)
ShutUp();

vupeakLock(lock);
- if (vumaxoutpeakl < vuoutpeakl) vumaxoutpeakl = vuoutpeakl;
- if (vumaxoutpeakr < vuoutpeakr) vumaxoutpeakr = vuoutpeakr;
+ if (vumaxoutpeakl < vuoutpeakl)
+ vumaxoutpeakl = vuoutpeakl;
+ if (vumaxoutpeakr < vuoutpeakr)
+ vumaxoutpeakr = vuoutpeakr;

vurmspeakl = sqrtf(vurmspeakl / buffersize);
vurmspeakr = sqrtf(vurmspeakr / buffersize);
@@ -560,14 +591,14 @@ void SynthEngine::MasterAudio(float *outl, float *outr)
else if (fakepeakpart[npart] > 1)
fakepeakpart[npart]--;
}
- vuOutPeakL = vuoutpeakl;
- vuOutPeakR = vuoutpeakr;
+ vuOutPeakL = vuoutpeakl;
+ vuOutPeakR = vuoutpeakr;
vuMaxOutPeakL = vumaxoutpeakl;
vuMaxOutPeakR = vumaxoutpeakr;
- vuRmsPeakL = vurmspeakl;
- vuRmsPeakR = vurmspeakr;
- vuClippedL = clippedL;
- vuClippedR = clippedR;
+ vuRmsPeakL = vurmspeakl;
+ vuRmsPeakR = vurmspeakr;
+ vuClippedL = clippedL;
+ vuClippedR = clippedR;
vupeakLock(unlock);
}

diff --git a/src/Misc/SynthEngine.h b/src/Misc/SynthEngine.h
index d8f5700..168ed8b 100644
--- a/src/Misc/SynthEngine.h
+++ b/src/Misc/SynthEngine.h
@@ -70,7 +70,7 @@ class SynthEngine : private SynthHelper, MiscFuncs
float numRandom(void);
unsigned int random(void);
void ShutUp(void);
- void MasterAudio(float *outl, float *outr);
+ void MasterAudio(float *outl [NUM_MIDI_PARTS], float *outr
[NUM_MIDI_PARTS]);
void partonoff(int npart, int what);
void Mute(void) { __sync_or_and_fetch(&muted, 0xFF); }
void Unmute(void) { __sync_and_and_fetch(&muted, 0); }
diff --git a/src/MusicIO/JackEngine.cpp b/src/MusicIO/JackEngine.cpp
index 5eba968..cdca380 100644
--- a/src/MusicIO/JackEngine.cpp
+++ b/src/MusicIO/JackEngine.cpp
@@ -23,6 +23,7 @@
#include <jack/thread.h>
#include <fcntl.h>
#include <sys/stat.h>
+#include <sstream>

using namespace std;

@@ -33,7 +34,7 @@ JackEngine::JackEngine() : jackClient(NULL)
{
audio.jackSamplerate = 0;
audio.jackNframes = 0;
- for (int i = 0; i < 2; ++i)
+ for (int i = 0; i < (2 * NUM_MIDI_PARTS + 2); ++i)
{
audio.ports[i] = NULL;
audio.portBuffs[i] = NULL;
@@ -116,6 +117,7 @@ bool JackEngine::openJackClient(string server)

bool JackEngine::Start(void)
{
+ bool jackPortsRegistered = true;
jack_set_error_function(_errorCallback);
jack_set_xrun_callback(jackClient, _xrunCallback, this);
#if defined(JACK_SESSION)
@@ -132,8 +134,8 @@ bool JackEngine::Start(void)

if (midi.port && !Runtime.startThread(&midi.pThread, _midiThread, this,
true, true))
{
- Runtime.Log("Failed to start jack midi thread");
- goto bail_out;
+ Runtime.Log("Failed to start jack midi thread");
+ goto bail_out;
}

if (!latencyPrep())
@@ -142,9 +144,16 @@ bool JackEngine::Start(void)
goto bail_out;
}

- if (!jack_activate(jackClient)
- && NULL != audio.ports[0]
- && NULL != audio.ports[1])
+ for (int port = 0; port < (2 * NUM_MIDI_PARTS + 2); ++port)
+ {
+ if (!audio.ports[port])
+ {
+ jackPortsRegistered = false;
+ break;
+ }
+ }
+
+ if (!jack_activate(jackClient) && jackPortsRegistered)
{
if (!Runtime.restoreJackSession && Runtime.connectJackaudio &&
!connectJackPorts())
{
@@ -170,7 +179,7 @@ void JackEngine::Close(void)
if (NULL != jackClient)
{
int chk;
- for (int chan = 0; chan < 2; ++chan)
+ for (int chan = 0; chan < (2*NUM_MIDI_PARTS+2); ++chan)
{
if (NULL != audio.ports[chan])
jack_port_unregister(jackClient, audio.ports[chan]);
@@ -197,12 +206,28 @@ void JackEngine::Close(void)

bool JackEngine::openAudio(void)
{
- const char *portnames[] = { "left", "right" };
- for (int port = 0; port < 2; ++port)
- audio.ports[port] = jack_port_register(jackClient, portnames[port],
- JACK_DEFAULT_AUDIO_TYPE,
- JackPortIsOutput, 0);
- if (audio.ports[0] && audio.ports[1])
+ //Register mixer outputs for all channels
+ audio.ports[2 * NUM_MIDI_PARTS] = jack_port_register(jackClient, "left",
JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+ audio.ports[2 * NUM_MIDI_PARTS + 1] = jack_port_register(jackClient,
"right", JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+
+ for (int port = 0; port < 2 * NUM_MIDI_PARTS; ++port)
+ {
+ stringstream portName;
+ portName << "track_" << ((port / 2) + 1) << ((port % 2) ? "_r" : "_l");
+ audio.ports[port] = jack_port_register(jackClient,
portName.str().c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
+ }
+
+ bool jackPortsRegistered = true;
+ for (int port = 0; port < (2 * NUM_MIDI_PARTS + 2); ++port)
+ {
+ if (!audio.ports[port])
+ {
+ jackPortsRegistered = false;
+ break;
+ }
+ }
+
+ if (jackPortsRegistered)
return prepBuffers(false) && latencyPrep();
else
Runtime.Log("Failed to register jack audio ports");
@@ -242,7 +267,7 @@ bool JackEngine::connectJackPorts(void)
return false;
}
int ret;
- for (int port = 0; port < 2 && NULL != audio.ports[port]; ++port)
+ for (int port = 0; (port < (2 * NUM_MIDI_PARTS)) && (NULL !=
audio.ports[port]); ++port)
{
const char *port_name = jack_port_name(audio.ports[port]);
if ((ret = jack_connect(jackClient, port_name, playback_ports[port])))
@@ -297,7 +322,7 @@ int JackEngine::processCallback(jack_nframes_t nframes)

bool JackEngine::processAudio(jack_nframes_t nframes)
{
- for (int port = 0; port < 2; ++port)
+ for (int port = 0; port < (2*NUM_MIDI_PARTS+2); ++port)
{
audio.portBuffs[port] =
(float*)jack_port_get_buffer(audio.ports[port], nframes);
@@ -308,8 +333,14 @@ bool JackEngine::processAudio(jack_nframes_t nframes)
}
}
getAudio();
- memcpy(audio.portBuffs[0], zynLeft, sizeof(float) * nframes);
- memcpy(audio.portBuffs[1], zynRight, sizeof(float) * nframes);
+ for (int port = 0; port < NUM_MIDI_PARTS; ++port)
+ {
+ memcpy(audio.portBuffs[port * 2], zynLeft[port], sizeof(float) *
nframes);
+ memcpy(audio.portBuffs[port * 2 + 1], zynRight[port], sizeof(float) *
nframes);
+ }
+ //And mixed outputs
+ memcpy(audio.portBuffs[2 * NUM_MIDI_PARTS], zynLeft[NUM_MIDI_PARTS],
sizeof(float) * nframes);
+ memcpy(audio.portBuffs[2 * NUM_MIDI_PARTS + 1], zynRight[NUM_MIDI_PARTS],
sizeof(float) * nframes);
return true;
}

diff --git a/src/MusicIO/JackEngine.h b/src/MusicIO/JackEngine.h
index b43632b..5b55671 100644
--- a/src/MusicIO/JackEngine.h
+++ b/src/MusicIO/JackEngine.h
@@ -79,8 +79,8 @@ class JackEngine : public MusicIO
struct {
unsigned int jackSamplerate;
unsigned int jackNframes;
- jack_port_t *ports[2];
- float *portBuffs[2];
+ jack_port_t *ports[2*NUM_MIDI_PARTS+2];
+ float *portBuffs[2*NUM_MIDI_PARTS+2];
} audio;

struct {
diff --git a/src/MusicIO/MusicIO.cpp b/src/MusicIO/MusicIO.cpp
index 926e22e..b3ea477 100644
--- a/src/MusicIO/MusicIO.cpp
+++ b/src/MusicIO/MusicIO.cpp
@@ -29,20 +29,30 @@ using namespace std;
#include "MusicIO/MusicIO.h"

MusicIO::MusicIO() :
- zynLeft(NULL),
- zynRight(NULL),
interleavedShorts(NULL),
rtprio(25)
-{ }
+{
+ memset(zynLeft, 0, sizeof(float) * (NUM_MIDI_PARTS + 1));
+ memset(zynRight, 0, sizeof(float) * (NUM_MIDI_PARTS + 1));
+}

MusicIO::~MusicIO()
{
- if (zynLeft)
- fftwf_free(zynLeft);
- if (zynRight)
- fftwf_free(zynRight);
+ for (int npart = 0; npart < (NUM_MIDI_PARTS + 1); ++npart)
+ {
+ if (zynLeft[npart])
+ {
+ fftwf_free(zynLeft[npart]);
+ zynLeft[npart] = NULL;
+ }
+ if (zynRight[npart])
+ {
+ fftwf_free(zynRight[npart]);
+ zynRight[npart] = NULL;
+ }
+ }
if (interleavedShorts)
- delete [] interleavedShorts;
+ delete[] interleavedShorts;
}


@@ -53,10 +63,10 @@ void MusicIO::InterleaveShorts(void)
double scaled;
for (int frame = 0; frame < buffersize; ++frame)
{ // with a grateful nod to libsamplerate ...
- scaled = zynLeft[frame] * (8.0 * 0x10000000);
- interleavedShorts[idx++] = (short int)(lrint(scaled) >> 16);
- scaled = zynRight[frame] * (8.0 * 0x10000000);
- interleavedShorts[idx++] = (short int)(lrint(scaled) >> 16);
+ scaled = zynLeft[NUM_MIDI_PARTS][frame] * (8.0 * 0x10000000);
+ interleavedShorts[idx++] = (short int) (lrint(scaled) >> 16);
+ scaled = zynRight[NUM_MIDI_PARTS][frame] * (8.0 * 0x10000000);
+ interleavedShorts[idx++] = (short int) (lrint(scaled) >> 16);
}
}

@@ -143,37 +153,44 @@ bool MusicIO::prepBuffers(bool with_interleaved)
int buffersize = getBuffersize();
if (buffersize > 0)
{
- if (!(zynLeft = (float*)fftwf_malloc(buffersize * sizeof(float))))
- goto bail_out;
- if (!(zynRight = (float*)fftwf_malloc(buffersize * sizeof(float))))
- goto bail_out;
- memset(zynLeft, 0, buffersize * sizeof(float));
- memset(zynRight, 0, buffersize * sizeof(float));
+ for (int part = 0; part < (NUM_MIDI_PARTS + 1); part++)
+ {
+ if (!(zynLeft[part] = (float*) fftwf_malloc(buffersize *
sizeof(float))))
+ goto bail_out;
+ if (!(zynRight[part] = (float*) fftwf_malloc(buffersize *
sizeof(float))))
+ goto bail_out;
+ memset(zynLeft[part], 0, buffersize * sizeof(float));
+ memset(zynRight[part], 0, buffersize * sizeof(float));
+
+ }
if (with_interleaved)
{
interleavedShorts = new short int[buffersize * 2];
if (NULL == interleavedShorts)
goto bail_out;
- memset(interleavedShorts, 0, sizeof(short int) * buffersize * 2);
+ memset(interleavedShorts, 0, sizeof(short int) * buffersize * 2);
}
return true;
}

bail_out:
Runtime.Log("Failed to allocate audio buffers, size " +
asString(buffersize));
- if (zynLeft)
- {
- fftwf_free(zynLeft);
- zynLeft = NULL;
- }
- if (zynRight)
+ for (int part = 0; part < (NUM_MIDI_PARTS + 1); part++)
{
- fftwf_free(zynRight);
- zynRight = NULL;
+ if (zynLeft[part])
+ {
+ fftwf_free(zynLeft[part]);
+ zynLeft[part] = NULL;
+ }
+ if (zynRight[part])
+ {
+ fftwf_free(zynRight[part]);
+ zynRight[part] = NULL;
+ }
}
if (interleavedShorts)
{
- delete [] interleavedShorts;
+ delete[] interleavedShorts;
interleavedShorts = NULL;
}
return false;
diff --git a/src/MusicIO/MusicIO.h b/src/MusicIO/MusicIO.h
index b8facf8..6c8471d 100644
--- a/src/MusicIO/MusicIO.h
+++ b/src/MusicIO/MusicIO.h
@@ -42,8 +42,8 @@ class MusicIO : virtual protected MiscFuncs
void setMidiNote(unsigned char chan, unsigned char note);
void setMidiNote(unsigned char chan, unsigned char note, unsigned char
velocity);

- float *zynLeft;
- float *zynRight;
+ float *zynLeft [NUM_MIDI_PARTS + 1];
+ float *zynRight [NUM_MIDI_PARTS + 1];
short int *interleavedShorts;
int rtprio;
};
diff --git a/src/version.txt b/src/version.txt
index 3eefcb9..afaf360 100644
--- a/src/version.txt
+++ b/src/version.txt
@@ -1 +1 @@
-1.0.0
+1.0.0
\ No newline at end of file
--
1.8.2.2

Other related posts: