Midi file as a filter frequency control. Without using a Synthdef

Hello,
Newb on SC, I would like to know how can I simply use the notes output of a midi file to control a parameter inside a function, and without using a Synthdef. (The problem of using a SynthDef is that I don’t want that the midi files trigs the Synthdef for each new notes it plays but I would like that the notes could control any parameters inside my function without adding an enveloppe) - for a continuum. As an example, I would like that the midi files notes control the frequency of the filter, how can I incorportate it, in this following code? Thanks a lot.

m = SimpleMIDIFile.read( “~/Desktop/toccata1.mid” );
(
{ var freqfrommidifile;
snd;

freqfrommidifile= //.....?
snd= Resonz.ar(PinkNoise.ar,freqfrommidifile);
Out.ar(0,snd!2);

}.play;
)

(Side note: {...}.play is a SynthDef – you can never get any sound out of SC without a SynthDef – it’s just that it’s hidden from you here.)

Take a look at Demand UGens.

hjh

Oh ok, thanks for this side note :slight_smile: I didn’t get to make it worked even if I understood the principle of Demand. How can I just want to extract and use the note value of the Midi file (and choosing its playback speed) to control any paramaters of my function, without the need to use an envelope or a trig ? My confusion comes from Max I think where you can output the notevalue as an integer box and plug it everywhere you want. The SimpleMIDIFile is quite confusing for me.
Could you please post a code using my example?
Thanks a lot

My confusion comes from Max I think where you can output the notevalue as an integer box and plug it everywhere you want…

It’s quite helpful that you said this, thanks. Part of the issue, then, is that you’re very familiar with the Max methodology (idiomatic usage) but not familiar with SC’s idiomatic usage.

One tricky thing in SC is: What is handled in the language (client) and what is handled in the server?

In general (IMO, some users might disagree) you’ll have the easiest time if you treat the server as a dumb animal whose job is just to do the heavy lifting of audio processing. The server (IMO) should not be making flow of control or timing decisions. It should just respond to OSC commands. (Demand UGens try to add some sequencing capabilities into the server, but they aren’t as powerful as client-side patterns – I scarcely ever use them except for LFO types of behavior that I can’t get any other way.)

MIDI files are way outside of that purview – and, in fact, SimpleMIDIFile is totally a client-side construct.

So I’d recommend a code design where all processing of the MIDI file data, and sequencing, take place in the client, sending messages to the server.

I’d recommend writing the synth like this:

(
// you need to save the Synth object in a variable reference
// {}.play, freestanding, means you can't do anything to control it anymore
// a = {}.play means "a.something" can do stuff to it
a = {
	// external input into the synth should be an 'arg'
	// not a 'var'
	arg freqfrommidifile = 440;
	
	var snd;
	
	// because it's an arg, the synth doesn't have to do any heavy
	// thinking about what the freq should be.
	// It just obeys what it gets from outside. So delete this.
	// freqfrommidifile = //.....?

	snd = Resonz.ar(PinkNoise.ar, freqfrommidifile);
	Out.ar(0, snd ! 2);
}.play;
)

To play the MIDI data, I’ll recommend piggybacking on the existing MIDI “note” pattern – because this already handles the timing.

This pattern produces events with the default “event type,” \note – which, as you said, you don’t want.

But, you can add a wrapper around the pattern to change the \note events into type \set, which changes the value of an argument of an existing synth (instead of playing new notes).

m = SimpleMIDIFile.read("/home/dlm/share/sc3-plugs-hjh/external_libraries/stk/projects/examples/midifiles/bwv772.mid");

p = Pbindf(
	m.p,  // this is the original MIDI note pattern
	
	// argument is named "freqfrommidifile"; event needs to match
	// The expression reads the MIDI note number and converts to Hz
	\freqfrommidifile, Pkey(\midinote).midicps,

	\type, \set,  // new event type
	\id, a.nodeID,  // which synth to modify?
	\args, [\freqfrommidifile]  // which argument names to send?
);

q = p.play;

q.stop;

TBH I wouldn’t expect any new user to figure this out alone. (IMO patterns are the best way to do sequencing in SC, but they aren’t the first thing that you learn, and manipulation of patterns as above is certainly not the first thing that you learn about patterns…) Also TBH, SimpleMIDIFile isn’t a core part of the library and it’s a bit idiosyncratic, so, admittedly, it’s a bit tricky.

Hope that helps,
hjh

1 Like

Thanks James, that’s exactly that and everything works well. I think that my 10 yrs of Max built so much subconscious reflex that put me a lot of time on the wrong way here. Two last things:
Is there a way if I’m using a midi files with multiple voices (for example 4 ) to kind of split the file output into 4 part and to assign each parts to a different parameters of the synth ie \freqfrommidifile1, \freqfrommidifile2,\freqfrommidifile3 etc…(using the same code you provide)
Also I found that the Demand UGen could be really convenient as well to control paramaters using MIDI data, is there a way to feed an array with the notes and timing value of the SimpleMIDIfile in order to get its content in the form of a Dseq([1, 2, 3, 4, 5] etc…
Thanks a lot for your time.

Hi,

I am interested in using midi-files as well. I can extract individual notes from chords as below. May be useful for you…

However: noticed that the check in the “while loop” throws an error at the end of midi file.

//extracting notes from the midi file

f = SimpleMIDIFile.read("/Users...path to midifile/Track2.mid");

x = f.p;

y = x.iter.all(()).postcs;

//tests..
y[8].midinote;
y[8].delta;
y[5].delta;


(
SynthDef(\vartest, { |out, freq = 440, amp = 0.2, a = 0.01, r = 1|
    // the EnvGen with doneAction: Done.freeSelf frees the synth automatically when done
    Out.ar(out, Pulse.ar(freq, 0.2, EnvGen.kr(Env.perc(a, r, amp), doneAction: 2)));
}, variants: (alpha: [a: 0.5, r: 0.5], beta: [a: 3, r: 0.01], gamma: [a: 0.01, r: 4])
).add;
)


(


~z=0;


  r = Task({


    while {
	y[~z].delta.notNil;

    } {



		//notes and delta are extracted - 

		Synth(\vartest, [freq: y[~z].midinote.value.midicps]);


		postln("next "+ ~z);

		y[~z].delta.yield;
		~z=~z+1;



    }
}).play(quant: TempoClock.default.beats + 1.0);)