Passing an argument to a SynthDef doesn't register when synth is initialized - supercollider

I have a simple SynthDef where I want to use the CCIn class, like so:
(
SynthDef(\lfo_sin, {|bus, amp, myArg|
var m = CCIn.new;
var v = [300, 700, \exp].asSpec;
var sig = SinOsc.ar(m.kr(0, myArg, v), mul:amp);
Out.ar(bus, sig);
}).add;
)
When I instantiate it like so y = Synth(\lfo_sin, [\bus, 0, \amp, 1, \myArg, 71]);, to match with the MIDI CC on my MIDI controller, I am not able to use the CCIn.kr method like I should.
If I however directly type in the MIDI CC when I define the SynthDef like so:
(
SynthDef(\lfo_sin2, {|bus, amp|
var m = CCIn.new;
var v = [300, 700, \exp].asSpec;
var sig = SinOsc.ar(m.kr(0, 71, v), mul:amp);
Out.ar(bus, sig);
}).add;
)
everything runs like it should and I am able to control the frequency using my MIDI controller.
Why does this behavior happen and how can I modify my code so that I can pass in the MIDI CC when initializing the synth or setting the argument afterwards?

If you take a look at the source for CCIn, you can see it's doing something a bit more complex than an ordinary UGen:
kr { |chan = 0, num = 0, spec = \amp, lag = 0.05|
var outArray = [chan, num, spec, lag].flop.collect{ |args|
var ch, n, sp, lg;
# ch, n, sp, lg = args;
(sp.asSpec.map( In.kr(this.prGetBus(ch, n).index) ).lag3(lg))
};
if (outArray.size>1) {^outArray} {^(outArray[0])} //fix to work with muliout
Specifically, this...
this.prGetBus(ch, n)
is using the provided channel and number (ch and n) to look up the Bus from which it can read the MIDI data (see prGetBus). It's doing this lookup as part of BUILDING the SynthDef, not RUNNING the Synth, so once it's been built the bus it's reading from is pretty much fixed. The CCIn quark obscures some fairly complex things under the hood in order to behave as a simple UGen, so it's unlikely you'll easily be able to get the behavior you're looking for.
Here are a few alternatives.
1. Write your MIDI data to a bus yourself
// One for each cc number
~ccBusses = 127.collect({
Bus.control(s, 1);
});
// A midi responder that sets the value of the right bus
MIDIdef.cc(\cc, {
|value, cc|
~ccBusses[cc].set(value);
}, ccNum: (0..127) ) // meaning: all cc values
// Once those are set up, to map a cc to a new synth use:
Synth(\mySynth, args:[\freq, ~ccBusses[10].asMap]);
2. Using the Connection quark
// Create a value between 100..2400, controlled by MIDI
~freq = MIDIControlValue(spec:ControlSpec(100, 2400));
~freq.cc_(10); // cc number 10
// Run your synth
~note = Synth(\mySynth, args:[\freq, ~freq]);
// Connect the value of ~freq to the \freq argument of your synth. Now, MIDI changes will be broadcast to your synth.
~freq.signal(\value).connectTo(~note.argSlot(\freq));

Related

bpf_prog_test_run() causes unexpected packet data

I try to perform a test run for an XDP BPF program. The BPF program uses the bpf_xdp_adjust_meta() helper, to adjust the meta data.
I tried:
to run bpf_prog_test_run()
to run bpf_prog_test_run_xattr()
1. bpf_prog_test_run()
(The first time I tried my bpf program's debug messages told me that adjusting the data_meta field failed.) Now it can adjust the data_meta, but the iph.ihl field is apparently not set to 5.
2. bpf_prog_test_xattr()
This always returns -1, so something failed.
The Code
packet:
struct ipv4_packet pkt_v4 = {
.eth.h_proto = __bpf_constant_htons(ETH_P_IP),
.iph.ihl = 5,
.iph.daddr = __bpf_constant_htonl(33554442),
.iph.saddr = __bpf_constant_htonl(50331658),
.iph.protocol = IPPROTO_TCP,
.iph.tot_len = __bpf_constant_htons(MAGIC_BYTES),
.tcp.urg_ptr = 123,
.tcp.doff = 5,
};
test attribute:
__u32 size, retval, duration;
char data_out[128];
struct xdp_md ctx_in, ctx_out;
struct bpf_prog_test_run_attr test_attr = {
.prog_fd = prog_fd,
.repeat = 100,
.data_in = &pkt_v4,
.data_size_in = sizeof(&pkt_v4),
.data_out = &data_out,
.data_size_out = sizeof(data_out),
.ctx_in = &ctx_in,
.ctx_size_in = sizeof(ctx_in),
.ctx_out = &ctx_out,
.ctx_size_out = sizeof(ctx_out),
.retval = &retval,
.duration = &duration,
};
test execution:
bpf_prog_test_run(main_prog_fd, 1, &pkt_v4, sizeof(pkt_v4), &data_out, &size, &retval, &duration) -> iph.ihl field is 0.
bpf_prog_test_run_xattr(&test_attr) -> returns -1.
Note
The program was already successfully attached to the hook point of a real network interface and ran as intended. I just replaced the code that attaches the program to the hook point with the above code for testing.
The struct ipv4_packet pkt_v4 was not packed.
When I replace __packed with __attribute__ ((__packed__)) it works.
For information what happens without packing, see for example this question.
Basically the compiler adds padding bytes which leads to the fields in the packet being in different places than expected.

Connecting multiple stages of parallel synths, with array of buses, in superCollider

When I have 2 stages of multiple parallel synths, I am able to connect it with an array of buses. (Thanks to Dan S for the answer to a previous question). When there is a 3 stage, this doesn't seem to work.
(
SynthDef(\siny, { arg freq, outBus=0; Out.ar( outBus, SinOsc.ar(freq!2,0,0.2) ) } ).send(s);
SynthDef(\filter, { arg cFreq,q=0.8, inBus, outBus=0; Out.ar( outBus, BPF.ar(In.ar(inBus), cFreq!2, 1/q ) ) } ).send(s);
)
(
var z = [100,500,1000,1500,200];
~sourceOut = z.collect{ Bus.audio(s) };
~sineOut = z.collect{ Bus.audio(s) };
~sine_Group = ParGroup.new;
~myGroup = ParGroup.new;
{
z.do({ arg val, index; Synth( \siny, [\freq: val, \outBus: ~sourceOut[index]], ~sine_Group ) });
z.do({ arg val, index; Synth.after(~sine_Group, \filter, [\inBus: ~sourceOut[index], \outBus: ~sineOut[index],\cFreq: 200, \q: 20 ], ~myGroup) });
z.do({ arg val, index; Synth.after(~myGroup, \filter, [\inBus: ~sineOut[index], \cFreq: 200, \q: 20]) });
}.play;
)
Another harm that I am doing here is, everytime I stop and run the synth, new instances of busses are created and eventually run out of audio buses. How can I solve this?
There are a number of issues with your code (some irrelevant with your question):
a. send(s) is a reminiscent of the past, these days simply add is preferable - you may consult the documentation for their differences.
b. it's not a good practice to mix local variables var with environmental ones (such as ~sourceOut) unless there is some good reason.
c. .collect(func) (or directly collect{/*function body here*/}) when send to an array results to another array where each element is the result of the func with the respective element of the original array passed as an argument. So here:
var z = [100,500,1000,1500,200];
~sourceOut = z.collect{ Bus.audio(s) };
~sineOut = z.collect{ Bus.audio(s) };
you don't use the original numbers anywhere, you just create two arrays containing 5 Audio buses each. Directly doing so would be less confusing and more explicit:
~sourceOut = Array.fill(5,{Bus.audio(s)});
~sineOut = Array.fill(5,{Bus.audio(s)});
d. there's no point in calling z.do three times.. you can call it just once and put the rest in the same function.
e. check you routings... You have two parallel groups and you're merely saying that I want these synths on that group and these others after that other group.. What you really want to say is that I want these Synths after those Synths on that group.
f. you don't free your Buses or your Synths when done... this is a memory leak :)
g. you call play on a function that want to evaluate really... you should call value instead.
This code does produces sound and cleans up everything when don, note however that the result is pretty boring because you've filtered all the higher-end.
s.waitForBoot({ // this is a routine
var frequencies = [100,500,1000,1500,200];
var sources = Array.fill(frequencies.size,{Bus.audio(s)});
var filters = Array.fill(frequencies.size,{Bus.audio(s)});
var parGroups = Array.fill(2,{ParGroup.new});
var sineSynths;
var firstOrderFilters;
var secondOrderFilters;
SynthDef(\siny, {
arg freq, outBus=0;
Out.ar( outBus, SinOsc.ar(freq!2,0,0.2));
}).add;
SynthDef(\filter, {
arg cFreq,q=0.8, inBus, outBus=0;
Out.ar( outBus, BPF.ar(In.ar(inBus), cFreq!2, 1/q ) )
}).add;
s.sync; // wait for the synthdefs to load
frequencies.do{ arg freq, index;
sineSynths = sineSynths.add( Synth(\siny, [\freq: freq.postln, \outBus: sources[index]],
parGroups[0])); // create synths and add them in the sineSynths Array
firstOrderFilters = firstOrderFilters.add(
Synth.after(sineSynths[index], \filter, [\inBus: sources[index],
\outBus: filters[index], \cFreq: 200, \q: 20 ], parGroups[1]));
secondOrderFilters = secondOrderFilters.add(
Synth.after(firstOrderFilters[index], \filter, [\inBus: filters[index],
\outBus: 0, \cFreq: 200, \q: 20 ], parGroups[1]));
};
10.wait; // wait 10 seconds;
// clean up everything
sineSynths.do{arg i; i.free};
firstOrderFilters.do{arg i; i.free};
secondOrderFilters.do{arg i; i.free};
sources.do{arg i; i.free};
filters.do{arg i; i.free};
});

Where is the callback function in Apple's PlaySequence project?

I want to bounce a midi file offline, and as the PlaySequence example does exactly this, I am trying to understand it.
I keep reading everywhere that you need a callback function to do anything in CoreAudio, yet I cannot see any in this project.
I paste the loop containing the AudioUnitRender, thanks for your help!
CAStreamBasicDescription clientFormat = CAStreamBasicDescription();
size = sizeof(clientFormat);
FailIf ((result = AudioUnitGetProperty (outputUnit,
kAudioUnitProperty_StreamFormat,
kAudioUnitScope_Output, 0,
&clientFormat, &size)), fail, "AudioUnitGetProperty: kAudioUnitProperty_StreamFormat");
size = sizeof(clientFormat);
FailIf ((result = ExtAudioFileSetProperty(outfile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat)), fail, "ExtAudioFileSetProperty: kExtAudioFileProperty_ClientDataFormat");
{
MusicTimeStamp currentTime;
AUOutputBL outputBuffer (clientFormat, numFrames);
AudioTimeStamp tStamp;
memset (&tStamp, 0, sizeof(AudioTimeStamp));
tStamp.mFlags = kAudioTimeStampSampleTimeValid;
int i = 0;
int numTimesFor10Secs = (int)(10. / (numFrames / srate));
do {
outputBuffer.Prepare();
AudioUnitRenderActionFlags actionFlags = 0;
FailIf ((result = AudioUnitRender (outputUnit, &actionFlags, &tStamp, 0, numFrames, outputBuffer.ABL())), fail, "AudioUnitRender");
tStamp.mSampleTime += numFrames;
FailIf ((result = ExtAudioFileWrite(outfile, numFrames, outputBuffer.ABL())), fail, "ExtAudioFileWrite");
FailIf ((result = MusicPlayerGetTime (player, &currentTime)), fail, "MusicPlayerGetTime");
if (shouldPrint && (++i % numTimesFor10Secs == 0))
printf ("current time: %6.2f beats\n", currentTime);
} while (currentTime < sequenceLength);
}
I had a look at the project and you're right, it does not have a rendercallback. Render callbacks have their place withing coreaudio audiounit effect processing. The call to setup a render callback looks like this:
inline OSStatus SetInputCallback (CAAudioUnit &inUnit, AURenderCallbackStruct &inInputCallback)
{
return inUnit.SetProperty (kAudioUnitProperty_SetRenderCallback,
kAudioUnitScope_Input,
0,
&inInputCallback,
sizeof(inInputCallback));
}
However this code is just one big main() which setups up the sequence, augraph, music player and then the filewriter WriteOutputFile in case there's an outputfile to bounce to.
I recommend you set some breakpoints at key methods, and walk through the code, watching what it does and looking at variables.
EDIT: Note, that in setting up your rendercallback on iOS on your RemoteIO (which doubles as input & output units), getting the correct stream format on the correct scope & bus element numbers in your setproperty calls can be tricky. Refer to this from the Apple docs.

How to use scalatest to check that certain events happen in a particular order?

I need to test that my library processes certain (user-defined) events in the proper order. Currently, I'm doing something very simple. I create a buffer and let each of the events append a different value to it:
val buf = new collection.mutable.ArrayBuffer[Int];
val ev1 = () => { buf += 1; }
val ev2 = () => { buf += 2; }
//
// ... library runs the events ...
//
// check that ev2 ocurred before ev1
buf should be (ArrayBuffer(2, 1))
Is there a better and clearer way?
Update: Meanwhile I created a tiny toolkit that helps me with the tests. The main class Event allows to wrap computations and functions and registers when a computation occurred with respect to other events. I have only a little insight into scalatest so I don't know how to integrate it better - if you know, please suggest.
I know it's almost the same, but you could make your code a bit cleaner. If you need to test event order multiple times you could define a trait like this:
trait EventOrderTester {
val buf = ArrayBuffer.empty[Int]
def ev(order: Int): () => Unit = () => buf += order
lazy val expected = buf.sorted
}
Then you can define tests like this:
"my test" in new EventOrderTester {
x.addListener1(ev(2))
x.addListener2(ev(1))
//
// ... library runs the events ...
//
// check that listener2 ocurred before listener1
buf should be(expected)
}

Core MIDI: when I send a MIDIPacketList using MIDISend() only the first packet is being sent

I am trying to send a MIDIPacketList containing two packets that describe controller position change message relating to a x-y style controller.
The function i'm trying to implement takes the an x and y position, and then creates the packets and sends them to the selected target device as follows:
- (void)matrixCtrlSetPosX:(int)posX PosY:()posY {
MIDIPacketList packetList;
packetList.numPackets = 2;
packetList.packet[0].length = 3;
packetList.packet[0].data[0] = 0xB0; // status: controller change
packetList.packet[0].data[1] = 0x32; // controller number 50
packetList.packet[0].data[2] = (Byte)posX; // value (x position)
packetList.packet[0].timeStamp = 0;
packetList.packet[1].length = 3;
packetList.packet[1].data[0] = 0xB0; // status: controller change
packetList.packet[1].data[1] = 0x33; // controller number 51
packetList.packet[1].data[2] = (Byte)posY; // value (y position)
packetList.packet[1].timeStamp = 0;
CheckError(MIDISend(_outputPort, _destinationEndpoint, &packetList), "Couldn't send MIDI packet list");
}
The problem I am having is that the program only appears to be sending out the first packet.
I have tried splitting the output into two separate MIDIPacketLists and two making two calls to MIDISend(), which does work, but I am sure that there must be something trivial I am missing out in building the midi packet list so that the two messages can be sent in one call to MIDISend(). I just cannot seem to figure out what the problem is here! Anyone here had experience doing this, or am I going about this the wrong way entirely?
Just declaring the MIDIPacketList doesn't allocate memory or set up the structure. There's a process to adding packets to the list. Here's a quick and dirty example:
- (void)matrixCtrlSetPosX:(int)posX PosY:(int)posY {
MIDITimeStamp timestamp = 0;
const ByteCount MESSAGELENGTH = 6;
Byte buffer[1024]; // storage space for MIDI Packets
MIDIPacketList *packetlist = (MIDIPacketList*)buffer;
MIDIPacket *currentpacket = MIDIPacketListInit(packetlist);
Byte msgs[MESSAGELENGTH] = {0xB0, 0x32, (Byte)posX, 0xB0, 0x33, (Byte)posY};
currentpacket = MIDIPacketListAdd(packetlist, sizeof(buffer),
currentpacket, timestamp, MESSAGELENGTH, msgs);
CheckError(MIDISend(_outputPort, _destinationEndpoint, packetlist), "Couldn't send MIDI packet list");
}
I adapted this code from testout.c found here

Resources