| Author |
Message |
jimmysword

Joined: Jan 11, 2008 Posts: 12 Location: Leeds, W Yorks
|
Posted: Fri Jan 25, 2008 2:09 pm Post subject:
midi controller seq (basic) Subject description: 16 knob step seq |
 |
|
Firstly, thanks to kassen for putting the idea in my head to 'think of my midi controller as a sequencer'.
The idea is that each of the 16 knobs on my controller represents step 1 to 16 of the sequence. The value of each knob dictates the note value that should be played on that step.
I think I have made the part of the patch which will control the sequence itself but I'm not sure how to get it to play back (code follows):
| Code: | //define empty sequencer array
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] @=> int seq[];
//int seq[16]; guess I can do it this way too but it seemed to behave strangely
//define variables
.5::second => dur time;
int mstep;
int mnote;
int note;
MidiIn min;
MidiMsg msg;
MidiOut mout;
//open midi reciever, exit on fail
if ( !min.open(0) ) me.exit();
while (true)
{
//wait on midi event
min => now;
//recieve midimsg
while( min.recv( msg ) )
{
msg.data2 => mstep;
msg.data3 => mnote;
mnote => seq[mstep];
<<< mstep, mnote >>>;
<<< seq[mstep] >>>;
}
}
|
I'm thinking read through the array, send the relevant mnote from each seq[mstep] to say, midi channel 2 as a note out. |
|
|
Back to top
|
|
 |
jimmysword

Joined: Jan 11, 2008 Posts: 12 Location: Leeds, W Yorks
|
Posted: Sun Jan 27, 2008 1:06 pm Post subject:
|
 |
|
edit: double post - didn't have html turned off Last edited by jimmysword on Sun Jan 27, 2008 1:08 pm; edited 1 time in total |
|
|
Back to top
|
|
 |
jimmysword

Joined: Jan 11, 2008 Posts: 12 Location: Leeds, W Yorks
|
Posted: Sun Jan 27, 2008 1:07 pm Post subject:
|
 |
|
Revised code reads through the array once a midi message is recieved then reads through the array once more but then stops. I want the sequence to be read on a loop so I tried a while inside the for but this stopped any data from being written to the sequence... (I think that's what happened anyway).
| Code: |
//define empty sequencer array
[0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0] @=> int seq[];
//int seq[16];
//define variables
.5::second => dur time;
int mstep;
int mnote;
int note;
int bar;
//call midi classes to variable
MidiIn min;
MidiMsg msg;
MidiOut mout;
//open midi in/out, exit on fail
if ( !min.open(0) ) me.exit();
if ( !mout.open(0) ) me.exit();
while (true)
{
//wait on midi event
min => now;
//recieve midimsg
while( min.recv( msg ) )
{
msg.data2 => mstep;
msg.data3 => mnote;
mnote => seq[mstep];
}
for ( 0 => int step; step < seq.cap(); step++ )
{
<<< seq[step] >>>;
time => now;
}
}
|
Probably something obviously wrong here but I have no idea what it is, any ideas? |
|
|
Back to top
|
|
 |
Kassen
Janitor


Joined: Jul 06, 2004 Posts: 5953 Location: The Hague, NL
G2 patch files: 3
|
Posted: Sun Jan 27, 2008 1:24 pm Post subject:
|
 |
|
Well, it all depends on what you want but...
Personally I would never advance time within a loop that waits for a event (MIDI, HID, etc) as that might mean missing some events.
It seems to me that what you are after here could be done more reliably using a second shred. Maybe it would be a good idea to read a bit about sporking shreds from functions and concurrency in the manual and examples.
I think a good core for a sequencer is to have a array that holds the data, then use one shred that listens for events (MIDI in your case) and where needed writes to this array, then have a second shred that deals with playback and reads from the same array to determine what to play when and how. _________________ while(!machine.crash() ) <<<"all is well">>>; |
|
|
Back to top
|
|
 |
jimmysword

Joined: Jan 11, 2008 Posts: 12 Location: Leeds, W Yorks
|
Posted: Mon Jan 28, 2008 1:16 pm Post subject:
|
 |
|
Thanks for the reply Kassen, I have a lot to learn!
I've done some reading up on shreds/functions/sporks but I'm having a very hard time getting my head round things. What is the difference between a spork and a function for example?
Also how can I run two shreds at the same time? I set up the shreds:
| Code: |
fun void mwrite()
{
while (true)
{
//wait on midi event
min => now;
//recieve midimsg
while( min.recv( msg ) )
{
msg.data2 => mstep;
msg.data3 => mnote;
mnote => seq[mstep];
}
}
}
fun void mread()
{
for ( 0 => int step; step < seq.cap(); step++ )
{
while (true)
{
<<< seq[step] >>>;
time => now;
}
}
}
|
and tried some things like:
| Code: |
while (true)
{
spork ~ mwrite();
spork ~ mread();
}
| this really breaks things (I couldn't even get chuck to stop after running this).
and
| Code: |
while (true)
{
spork ~ mwrite();
while (true)
{
spork ~ mread();
}
}
|
this didn't do anything either... I tried lots of other things too (I probably need to understand flow control better for this) but haven't managed to have both shreds running at the same time (or at least if I did then they don't work together as planned). |
|
|
Back to top
|
|
 |
Kassen
Janitor


Joined: Jul 06, 2004 Posts: 5953 Location: The Hague, NL
G2 patch files: 3
|
Posted: Mon Jan 28, 2008 1:53 pm Post subject:
|
 |
|
| jimmysword wrote: |
I've done some reading up on shreds/functions/sporks but I'm having a very hard time getting my head round things. What is the difference between a spork and a function for example? |
Well, a function can be seen as a sort of shorthand for a certain amount of code. Basically a function is a amount of code that has a name (and sometimes parameters) so you call the function (by it's name :¬) ) and it runs.
In ChucK there are two main ways of calling functions; you can call them normally and if you do they will run as if the code they represent was written in their place and the program will go on as soon as it's done with running that function. The other way is "sporking" them which means the function will execute and at the same time the rest of the program will go on as well. In you case above, for example, you probably want the sequencer to run while also parsing MIDI data.
| Quote: |
Also how can I run two shreds at the same time? I set up the shreds:
| Code: |
fun void mwrite()
{
while (true)
{
//wait on midi event
min => now;
//recieve midimsg
while( min.recv( msg ) )
{
msg.data2 => mstep;
msg.data3 => mnote;
mnote => seq[mstep];
}
}
}
fun void mread()
{
for ( 0 => int step; step < seq.cap(); step++ )
{
while (true)
{
<<< seq[step] >>>;
time => now;
}
}
}
|
|
Looks good to me, at least after a quick glance.
| Quote: | and tried some things like:
| Code: |
while (true)
{
spork ~ mwrite();
spork ~ mread();
}
| this really breaks things (I couldn't even get chuck to stop after running this).
|
Yeah! I imagine! What you are doing here is starting two new "shreds" (paralel ChucK processes) and after you did so you start them again, and again and again.... So the program will start more processes as quickly as it can and at some point you run out of CPU power.
| Quote: |
this didn't do anything either... I tried lots of other things too (I probably need to understand flow control better for this) but haven't managed to have both shreds running at the same time (or at least if I did then they don't work together as planned). |
Try; | Code: |
//spork the first
spork ~ mwrite();
//spork the second
spork ~ mread();
//keep the main shred allive
day => now;
|
You see, if the "mother" shred, the one that sporks the others, leaves the VM then all her "children" will go with her.
Another thing to notice is that ChucK will never get out of a "while(true)" loop unless there is a "break" instruction in it. Because of this I think you intend your second function to go something like;
| Code: |
fun void mread()
{
while(true)
{
for ( 0 => int step; step < seq.cap(); step++ )
{
<<< seq[step] >>>;
time => now;
}
}
}
|
We could do this yet nicer and yet a bit cleaner but let's make sure you get this stuff first. "while(true)" is extremely useful and great but it's also something that should be used with a little care, as I'm sure your crash showed.... :¬)
Hope that helps. _________________ while(!machine.crash() ) <<<"all is well">>>; |
|
|
Back to top
|
|
 |
Frostburn

Joined: Dec 12, 2007 Posts: 166 Location: Finland
Audio files: 5
|
Posted: Mon Jan 28, 2008 2:03 pm Post subject:
|
 |
|
It seems that Kassen answered your question while I was writing my own post... :)
I'll leave it here just in case.
| Quote: | | What is the difference between a spork and a function for example? |
Sporks are written as functions and you can as far as I know spork any function wheter it makes sense or not, but to make a new shred you need a function that advances time on it's own. Otherwise the shred will simply run and exit.
And when you've sporked a shred you need to advance time in the main shred too or things will go haywire like in your example:
| Quote: | | Code: | while (true)
{
spork ~ mwrite();
spork ~ mread();
} |
|
This one will spork an infinite number of new shreds because it doesn't advance time.
What you need to do is:
| Code: |
spork ~ mwrite();
spork ~ mread();
while (true)
{
second => now;
}
|
Now you've created two new shreds that will do their thing on their own and you're also advancing time in the main shred so it doesn't die or get stuck in an infine loop. _________________ In Serendipity We Trust |
|
|
Back to top
|
|
 |
Kassen
Janitor


Joined: Jul 06, 2004 Posts: 5953 Location: The Hague, NL
G2 patch files: 3
|
Posted: Mon Jan 28, 2008 2:26 pm Post subject:
|
 |
|
| Frostburn wrote: |
Sporks are written as functions and you can as far as I know spork any function wheter it makes sense or not,
|
To add to this; I do think the function needs to be of type "void" as the calling process can't/won't wait for it to return anything so I don't think it *can* return anything.
But yes, I can't think of any situation where sporking a function that doesn't advance time would be useful either.
I think that with that we covered most of the basics, sporking is closely linked to managing time&timing indeed. _________________ while(!machine.crash() ) <<<"all is well">>>; |
|
|
Back to top
|
|
 |
jimmysword

Joined: Jan 11, 2008 Posts: 12 Location: Leeds, W Yorks
|
Posted: Tue Jan 29, 2008 2:17 am Post subject:
|
 |
|
Excellent work guys, I have a much better grasp now thank you very much! I can now see that sporking shreds is the way to go (I always wondered wth everyone was on about)!
This is all very exciting stuff... |
|
|
Back to top
|
|
 |
jimmysword

Joined: Jan 11, 2008 Posts: 12 Location: Leeds, W Yorks
|
Posted: Tue Jan 29, 2008 3:22 am Post subject:
|
 |
|
Is there any documentation on MidiOut ? The Midi section of the chuck language specifation http://chuck.cs.princeton.edu/doc/language/event.html#midi goes into MidiIn and MidiMsg in the example but although I know MidiOut exists I can't find any further reading on it. I can only assume that midi out would be something like
| Code: |
MidiOut mout;
MidiMsg msg;
if ( !mout.open(0) ) me.exit();
while( true )
{
while( mout.send( msg ) )
{
144 => msg.data1;
60 => msg.data2;
100 => msg.data3;
1::second => now;
}
}
|
Is mout.send real? And if it is how do I initiatiate a midi out signal? |
|
|
Back to top
|
|
 |
Kassen
Janitor


Joined: Jul 06, 2004 Posts: 5953 Location: The Hague, NL
G2 patch files: 3
|
Posted: Tue Jan 29, 2008 3:49 am Post subject:
|
 |
|
Yeah, .send() is real.... But of the official docs it's only mentioned in the back of the manual, as far as I can find. This is bad and should be fixed, I think.
It will send a message of type MidiMsg which can hold up to three bytes, labelled .data1 to .data3. What comes out is literally those numbers so keeping a MIDI reference at hand is quite useful.....
I think that covers all of MidiOut, it's quite a simple class :¬) _________________ while(!machine.crash() ) <<<"all is well">>>; |
|
|
Back to top
|
|
 |
jimmysword

Joined: Jan 11, 2008 Posts: 12 Location: Leeds, W Yorks
|
Posted: Tue Jan 29, 2008 4:00 am Post subject:
|
 |
|
| Great stuff, and yes the data1,2,3 thing is quite easy to understand as long as a midi reference is at hand (plenty of these on the web). If I write the data as hex will chuck understand? ie just writing $90 instead of 144 (midi channel 1 note on)? |
|
|
Back to top
|
|
 |
Kassen
Janitor


Joined: Jul 06, 2004 Posts: 5953 Location: The Hague, NL
G2 patch files: 3
|
Posted: Tue Jan 29, 2008 4:08 am Post subject:
|
 |
|
Yes, it will, but "$" is for casting, I think hex numbers start with "0x". I think that for integers hex and normal numbers can be used interchangeably. There are also bitwise operations in the standard library if you want them :¬). _________________ while(!machine.crash() ) <<<"all is well">>>; |
|
|
Back to top
|
|
 |
|