Show Posts
|
|
Pages: [1] 2 3 ... 13
|
|
1
|
Howto / Synths / Re: controlling synth
|
on: April 02, 2009, 09:03:49 PM
|
The only way to get new values into a running Synth is using a Control input. Controls are automatically created from arguments of the SynthDef function. The code you posted looks of the value of d.at("name") once when building the SynthDef. After that, it's hard coded and can't be changed (without rebuilding the SynthDef). I'd suggest, keep the dictionary on the client side and send the new value as needed using .set -- d = Dictionary.new; d.put("cfreq", 90); (SynthDef("name", { |cfreq = 440| var instr; instr = SinOsc.ar(freq); Out.ar(0, instr); }).send(s) )
y = Synth("name", [\cfreq, d.at("cfreq")]);
d["cfreq"] = 900; y.set("cfreq", d["cfreq"]); hjh
|
|
|
|
|
2
|
General Discussion / General Discussion / Re: loading buffers automatically with Pbind
|
on: February 16, 2009, 05:33:53 PM
|
Very tricky one. Pproto creates a separate environment that exists only within the pattern while it's executing. The function that you give at the start of Pproto executes within this separate environment. But, your wavetable functions exist in the main environment. Environments don't share values - there's no kind of nested scope like you have with functions. So, when {arg a; ~sin2.(a) } executes, ~sin2 is nil because the function is running inside an environment that knows nothing about ~sin2. A workaround is to use topEnvironment[\sin2] in place of ~sin2. Or, put inEnvir in front of the function: Pdef(\toto, ~mkbuf.( inEnvir {arg a; ~sin2.(a); a.postln; }, Pbind(\instrument, \wavetable))); This connects the function to the outer environment. ~mkbuf = { arg fun, pat; Pproto({ var cc; cc = Signal.newClear(512); fun.(cc); ~buf = Buffer.loadCollection(s, cc.asWavetable); }, Pset(\bufnum, Pkey(\buf), pat)) };
Note also that using Buffer.loadCollection in Pproto means that the buffer will not get freed automatically at the end of the pattern. Pproto depends on the event types defined in EventTypesWithCleanup -- if resources are created by other means, Pproto doesn't know what to do with them afterward. The \table event type should suit this purpose. ~buf = (type: \table, amps: cc.asWavetable).yield; I recently added a cleanup func to Pfset, which might be more intuitive. p = Pfset({ ... ~buf = Buffer.loadCollection(...); }, pattern, { ~buf.free; }); Pdef(\toto, ~mkbuf.({arg a; ~sinx.(_,3) }, Pbind(\instrument, \wavetable))); The last one isn't right because _ is not a substitute for a. {arg a; ~sinx.(a, 3) } is the same as ~sinx.(_, 3) -- but if you call .value on {arg a; ~sinx.(_,3) }, the result will be a Function that is supposed to call ~sinx - but this function will not actually be called. James
|
|
|
|
|
3
|
Howto / Language questions / Re: Reading parts from a text file
|
on: February 16, 2009, 04:54:01 PM
|
Dear James, 1, I am ok, if I use a text file with just the lines of useful data "simple_lines16.txt". (copy and paste from the original). TabFileReader seems to make things easier for a dummy like me. I still would like to use the original file "leftandright16ch.txt", where both channels (Left CH1 and Right CH2) could be read in arrays. Well, I think I just have to find out how to get the line number of where "Left CH1" and "Right CH2" is positioned. Then I could add and minus the numbers, and get the array to be read in from these new parameters. Do you have an idea how to get this line position? http://tedor2.extra.hu/mix/ReadintoArray.zipThe easiest way to find the line numbers is to use a proper text editor (not the one built into sc.app). A lot of programmers swear by text mate, but there are a bunch of freeware choices. Many editors, especially programming-oriented ones, have the option of showing line numbers in the left margin. But, looking at the tab file reader class definition, I don't see an easy way to tell it to stop after a given number of lines. I don't have a quick answer to that problem (except that I probably would write my own reader and parser using the outline in my earlier post, especially if this is something I expect to do a lot). 2, I wanted to send you an email on your site concerning the CD with your music, there is an error message after clicking on send. I wonder whether you would send it to the UK as well? ($?) Sure, I'll send to the UK. What was the error message? I can't help you much with the error if I don't know what the error is. (Unfortunately this is all too common in the computer world -- people think it's enough to say "there was an error." So then it takes longer to solve the problem because it's necessary to do another back-and-forth to find out what the problem really was. Sorry to go on about this -- it is a major peeve of mine.) I can tell you that I've had horrible problems with spam from that page, so I block actually rather a lot of phrases. I also had to put in a timer because a lot of spammers submit the input form instantaneously after visiting the page. The side effect is that if you write the message to me in another editor, then go to my page, paste it in quickly and hit "send," as far as the website is concerned, you look like a spammer. Try waiting at least 15 seconds before sending. Or, if all else fails, send me a PM here. James
|
|
|
|
|
4
|
Howto / Language questions / Re: Reading parts from a text file
|
on: January 27, 2009, 03:00:53 PM
|
thank you for clearing the haze! Do you think I should try learning C? Or which would be the best way to learn the language? Are the help files enough? Nah, don't waste your time with C. (C is still useful for a lot of things, but most of the programming world has moved on to C++ or Java. C is not object-oriented, so it won't teach you much about SC.) I can't think of a "general programming concepts for SC users" guide... maybe look at the Ruby language. It is somewhat similar to SC in some ways. Oh, this is interesting. Yeah, you sometimes have to dig into the raw data to see what's really there before writing the final code. Sometimes I do this to look at the raw data: f = File(p, "r"); z = Int8Array.newClear(200); f.read(z); z.clump(8).do({ |row| row.do({ |item| Post << item.asHexString(2) << " "; }); "\t".post; row.do({ |item| item = item.asAscii; Post << if(item.isPrint) { item } { "." }; }); Post << $\n; });
f.close; // prints: 0D 54 69 74 6C 65 20 6F .Title o 66 20 74 68 65 20 45 78 f the Ex 70 6F 72 74 20 42 72 61 port Bra 69 6E 20 44 61 74 61 3A in Data: 09 09 09 45 78 70 6F 72 ...Expor 74 20 42 72 61 69 6E 20 t Brain 44 61 74 61 0D 0D 4D 61 Data..Ma 78 20 62 75 66 66 65 72 x buffer 20 73 69 7A 65 3A 09 09 size:.. 09 20 30 31 3A 30 30 3A . 01:00: 30 30 2E 30 30 30 0D 53 00.000.S 74 61 72 74 20 54 69 6D tart Tim 65 20 69 6E 20 62 75 66 e in buf 66 65 72 3A 09 09 09 20 fer:... 30 30 3A 30 30 3A 30 30 00:00:00 2E 30 30 30 0D 45 6E 64 .000.End 20 54 69 6D 65 20 69 6E Time in 20 62 75 66 66 65 72 3A buffer: 09 09 09 20 30 30 3A 30 ... 00:0 32 3A 34 37 2E 35 39 31 2:47.591 0D 0D 49 42 56 41 34 20 ..IBVA4 61 6E 64 20 63 75 72 72 and curr 65 6E 74 20 66 69 6C 65 ent file 20 69 6E 66 6F 72 6D 61 informa 74 69 6F 6E 3A 09 09 09 tion:... The first thing I see from this is that 0D represents a line break in this file, and getLine doesn't recognize it as a line break. (How do I know this is the line break? By comparing the raw output to the appearance in a text editor.) The bad characters show up as NUL in notepad++ on my windows machine, meaning ascii code 0. That is a fairly serious data problem that is harder to correct in SC. Since SC's underbelly is C, the C-style rule for strings apply where a 0 byte means the end of the string. Probably the string in memory has all the characters, but they are ignored past 0 because of the C rule. SC doesn't have a built-in method to strip 0 bytes, so you'll have to write your own getLine function. ~getLine = { |file, endChar(13.asAscii)| var char = file.getChar, str = String.new; if(char.isNil) { nil } { while { char.notNil and: { char != endChar } } { if(char.ascii > 0) { str = str.add(char) }; char = file.getChar; }; }; str }; Sorry this is such a pain... working with files you didn't create yourself is sometimes hard. James
|
|
|
|
|
5
|
Howto / Language questions / Re: Reading parts from a text file
|
on: January 26, 2009, 03:12:34 PM
|
Dear James,
I rely on your help. Step by step...
I think I understood that a text file is made up of lines. A text file is really just a stream of bytes. Some of those bytes are interpreted to mean the end of a line (but it's not required to read the file that way). (
var file = File("/Users/krisztianhofstadter/Desktop/EEG/SC\ read/exported_data", "r"), line;
line = file.getLine; while { line.notNil and: { line != "Left CH1" } } { line = file.getLine; }; With the first part of the code we read a whole text file into a variable (file) and set up a new variable (line). No -- file = File(path, mode) doesn't read anything. It just opens a file handle. You can read data from the file handle - or just close the handle without reading anything (which is a way to test for a file's existence on disk, e.g. -- see File.exists in the SC lib). The second part is trickier:
line = file.getLine;
"getLine = Reads and returns a String up to lesser of next newline or 1023 chars." I got confused here:)
The while function has two parts:
testFunc :{ line.notNil and: { line != "Left CH1" } } bodyFunc : {line = file.getLine;}
"While" starts reading the text from the beginning (testFunc). It loops the "testFunc" until it's false. In scope > This mean that the "bodyFunc" evaluates "line = file.getLine", where this evaluation (line = file.getLine) reads strings from the text lines in a loop till it is false? getLine reads strings from the file >
are these strings the "text lines" them self? or are these strings characters next to each other like a word or numbers (Left ~ 000) or just one character in the text (1 ~ L) or is "Left CH1" a string? Try it for yourself -- running some exploratory code will teach you more about reading files. f = File(path, "r"); f.getLine; // what do you see in the post window? f.getLine.dump; // proves the result is a String, and shows some of the contents In the source file, there is a line "Left CH1" that occurs at the beginning of the first data section. When that line is read, line != "Left CH1" becomes false and the while loop stops. The file's position should be after the newline character terminating "Left CH1" (i.e., the next character to be read will be the first character on the line following "Left CH1"). If "Left CH1" is a string, while was looking for to stop, I think I am alright so far. Yes. Double-quote syntax for string literals is explained in the Literals help file. James
|
|
|
|
|
6
|
Howto / Language questions / Re: Reading parts from a text file
|
on: January 13, 2009, 01:41:43 PM
|
Reading a file like this takes a few basic capabilities that most computer languages have. - reading strings from a file - searching for characters or substrings in a string - extracting substrings - converting strings to numbers Supercollider can do all of these things, so yes, of course it's possible. What you need to do is think step-by-step about the parts of the file and how each should be processed. First is a header, which you just want to ignore up until the line that says "Left CH1." That's pretty easy -- read a line and check to see if it == "Left CH1" -- if so, stop and go onto the next section. The standard way to do this is with a while loop. (Checking for notNil is also important because you want the loop to stop if the file runs out of lines.) var file = File(path, "r"), line;
line = file.getLine; while { line.notNil and: { line != "Left CH1" } } { line = file.getLine; }; // the above line = ...; while ...; could be written more concisely as: while { (line = file.getLine).notNil and: { line != "Left CH1" } }; Then follow the data. What steps do we need for each line? - there are some bad characters; strip them out - split the string wherever there is white space - remove any empty substrings in the partitioned string (just in case) - throw out the first three columns - convert the rest to floating-point numbers Oh, and stop when a line doesn't contain any numbers. while { (line = file.getLine).notNil and: { line.any(_.isDigit) } } { // remove bad characters line = line.reject({ |ch| ch.ascii > 127 }) // split wherever there is a space (including tabs) .delimit({ |ch| ch.isSpace }) // remove empty strings .reject({ |substr| substr.size == 0 }); // starting with the 4th column, convert each string to a float // the result is an array of the FFT magnitudes from the file matrix = matrix.add(line[3..].asFloat); }; Then skip a few lines until you get to "Right CH2" and do the same thing to read the next table. I'm not sure of the format you need for the final matrix, but array manipulations can certainly get you there. Solving problems like this is a matter of thinking it through, one step at a time. Looking at the file as a whole, it seems difficult indeed but there is a very clear action to take for each separate line. So look at one line, and break down the process into simple steps. Also it's a good idea to use protect when processing file data. If there is an error in the middle of file processing, you could leave a file handle open and then have no way to close it, not so good. General template: file = File(path, "r"); if(file.isOpen) { protect { ... do file stuff in here ... } { file.close; }; } { "Couldn't open file".warn; }; With this, even if an error occurs inside the protect block, the file is always closed. James
|
|
|
|
|
7
|
General Discussion / General Discussion / Google map of SC users
|
on: December 24, 2008, 05:00:51 PM
|
An enterprising SC user has put up a global google map to locate other users. maps.google.nl/maps...Kind of fun to see where people are... feel free to add yourself if you like. All it takes is a google account, thus entailing the surrender of your online privacy in perpetuity. (Bit too late for me; I gave that up years ago.) James
|
|
|
|
|
9
|
Howto / Synths / fun: frequency-stretching evil
|
on: December 18, 2008, 01:10:49 PM
|
b = Buffer.read(s, "sounds/a11wlk01.wav");
( SynthDef('freq-stretch-evil', { |stretch = 1, center = 440, out| var sig = PlayBuf.ar(1, b, loop: 1); // stretched sig = PitchShift.ar(sig, 0.2, stretch, timeDispersion: 0.05); // re-centered sig = FreqShift.ar(sig, center * (1-stretch)); Out.ar(out, sig ! 2); }, metadata: (specs: (stretch: [0.25, 4, \exp], center: \freq))).memStore;
SynthDescLib.at('freq-stretch-evil').makeWindow; ) (Metadata is post-3.2 I believe... if you're on 3.2, leave it out and play the synth manually.) James
|
|
|
|
|
10
|
Howto / GUI and interaction / Re: Nerdy color sorting fun
|
on: December 13, 2008, 09:39:24 AM
|
Actually the userview/pen thing has some performance problems using Cocoa (OSX) gui objects. Here's another version using an empty statictext, setting the background color. It runs a lot faster in OSX. James ( var blockSize = 10, blocks = 60, pixels = blockSize * blocks, blockRect = Rect(0, 0, blockSize, blockSize),
brightness = { |color| color.red * color.green * color.blue }, colors = Array.fill(16, { Color.rand }).sort({ |a, b| brightness.(a) < brightness.(b) }), matrix = Array.fill(blocks, { Array.fill(blocks, { colors.size.rand }) }), sbounds = GUI.window.screenBounds, w = GUI.window.new("HexaDecaColorDoubleBubbleSort", Rect((sbounds.width - pixels) * 0.5, (sbounds.height - pixels * 0.5), pixels, pixels)), views = matrix.collect({ |row, i| row.collect({ |cell, j| GUI.staticText.new(w, Rect(i * blockSize, j * blockSize, blockSize, blockSize)) .background_(colors[matrix[i][j]]); }); }), updateColor = { |i, j| views[i][j].background_(colors[matrix[i][j]]); }, routine;
w.front;
routine = Routine({ var numSwaps = 1, temp, // a bit tricky: I want to randomize whether it swaps // horizontally first, or vertically // so I put h-swap and v-swap functions in an array // then, on each iteration, choose randomly which one to do first swaps = [{ |i, j, k| // horizontal swap if((matrix[i][j] > matrix[i][k])) { matrix[i].swap(j, k); updateColor.(i, j); updateColor.(i, k); numSwaps = numSwaps + 1; }; }, { |i, j, k| // vertical swap if(matrix[j][i] > matrix[k][i]) { temp = matrix[j][i]; matrix[j][i] = matrix[k][i]; matrix[k][i] = temp; updateColor.(j, i); updateColor.(k, i); numSwaps = numSwaps + 1; }; }], // another trick: going forward always causes a bias toward the bottom right // so, we will scan forward first, then backward fwdBackwd = Pseq(#[do, reverseDo], inf).asStream, scanDirection; while { numSwaps.debug("number of swaps last pass") > 0 } { numSwaps = 0; scanDirection = fwdBackwd.next; blocks.perform(scanDirection, { |i| (blocks-1).perform(scanDirection, { |j| swaps[#[[0, 1], [1, 0]].choose].do({ |func| func.value(i, j, j + 1); }); 0.01.wait; }); }); }; "done".postln; w.front; // no matter what I'm doing, I want this to pop up }).play(AppClock);
w.onClose = { routine.stop }; )
|
|
|
|
|
11
|
Howto / GUI and interaction / Nerdy color sorting fun
|
on: December 12, 2008, 05:25:20 PM
|
I'd been meaning to do this for awhile... when I was a kid, I was obsessed with a small program on the Apple II that had been printed in the magazine of the Puget Sound apple users group (I am really dating myself here). It did a two-dimensional bubble sort of colored blocks, ending up with these curved and tapered bands of color. So I hacked it up in SuperCollider... basically just like I remember it  With 3600 blocks it will take quite some time to complete, but it's fascinating to watch as the random matrix slowly organizes itself from the outside in. A smaller matrix runs faster of course, but the bands are chunkier and not as pretty. If you get bored with it, just close the window and the routine will stop. James ( var blockSize = 10, blocks = 60, pixels = blockSize * blocks, blockRect = Rect(0, 0, blockSize, blockSize),
brightness = { |color| color.red * color.green * color.blue }, colors = Array.fill(16, { Color.rand }).sort({ |a, b| brightness.(a) < brightness.(b) }), matrix = Array.fill(blocks, { Array.fill(blocks, { colors.size.rand }) }), sbounds = GUI.window.screenBounds, w = GUI.window.new("HexaDecaColorDoubleBubbleSort", Rect((sbounds.width - pixels) * 0.5, (sbounds.height - pixels * 0.5), pixels, pixels)), views = matrix.collect({ |row, i| row.collect({ |cell, j| GUI.userView.new(w, Rect(i * blockSize, j * blockSize, blockSize, blockSize)) .relativeOrigin_(true) .canFocus_(false) .drawFunc_({ |view| GUI.pen.color_(colors[matrix[i][j]]) .fillRect(blockRect) }); }); }), routine;
w.front;
routine = Routine({ var numSwaps = 1, temp, // a bit tricky: I want to randomize whether it swaps // horizontally first, or vertically // so I put h-swap and v-swap functions in an array // then, on each iteration, choose randomly which one to do first swaps = [{ |i, j, k| // horizontal swap if((matrix[i][j] > matrix[i][k])) { matrix[i].swap(j, k); views[i][j].refresh; views[i][k].refresh; numSwaps = numSwaps + 1; }; }, { |i, j, k| // vertical swap if(matrix[j][i] > matrix[k][i]) { temp = matrix[j][i]; matrix[j][i] = matrix[k][i]; matrix[k][i] = temp; views[j][i].refresh; views[k][i].refresh; numSwaps = numSwaps + 1; }; }], // another trick: going forward always causes a bias toward the bottom right // so, we will scan forward first, then backward fwdBackwd = Pseq(#[do, reverseDo], inf).asStream, scanDirection; while { numSwaps.debug("number of swaps last pass") > 0 } { numSwaps = 0; scanDirection = fwdBackwd.next; blocks.perform(scanDirection, { |i| (blocks-1).perform(scanDirection, { |j| swaps[#[[0, 1], [1, 0]].choose].do({ |func| func.value(i, j, j + 1); }); 0.01.wait; }); }); }; "done".postln; w.front; // no matter what I'm doing, I want this to pop up }).play(AppClock);
w.onClose = { routine.stop }; )
|
|
|
|
|
12
|
Howto / Language questions / Re: MouseButton.kr as recorder and player
|
on: December 01, 2008, 04:10:18 PM
|
Typically I like to keep the buffer with other data about it in one object. Identity dictionaries are really good for this because there is no preset structure and you can add stuff to it anytime -- and its subclass Event is even better because it has a really simple syntax. b = (buf: Buffer.alloc(...)); // (name: value) makes an event Then in the OSCresponderNode: o = OSCresponderNode(s.addr, '/tr', { |time, resp, msg| if(msg[1] = x.nodeID) { // msg[1] is node id b.put(\dur, msg[3]); // msg[3] is trigger value }; }).add; Now when you play back, you can use bufnum: b.buf and also have a duration argument to the synth, populated by b.dur (in seconds) or b.dur * b.buf.sampleRate (in frames). i havent quite figured it out, but should there be a problem if i use my mouse NORMALLY (i.e. choosing another piece of code by double clicking on it) after running this piece of code? is there a way to stop SC from reading the MouseButton so i can use the mouse without worry? As long as mouse button.kr is running, it's going to respond to every click. One solution might be to designate a block of screen coordinates to start and stop recording, and check using MouseX and MouseY (again, adapt this to your synth): withinCoordinates = InRange.kr(MouseX.kr(0, GUI.window.screenBounds.right), leftBound, rightBound) * InRange.kr(MouseY.kr(0, GUI.window.screenBounds.bottom), topBound, bottomBound); buttonPressed = MouseButton.kr; recordTrig = buttonPressed * withinCoordinates; Another way would be to have some sort of GUI thing to click on, which would trigger an action function in the client. The action function could set an argument in the record synth to replace MouseButton. Many GUI objects respond to mouse down as well as mouse up. James
|
|
|
|
|
14
|
Howto / Language questions / Re: Osc message from Max to trigger a Routine
|
on: November 30, 2008, 11:19:33 PM
|
|
How about:
r.next.set(\synthArgName, maxrate12);
(Since r.next returns the new Synth, you can use it as the receiver of .set. Then, the comment "can yield anything" is no longer valid -- the synth routine will have to yield the synth object.)
It isn't clear to me what you want to do with maxrate12, actually. If you were trying to set a value on a Synth, the above would work. If not, then I don't think you would need .set in there.
James
|
|
|
|
|
15
|
Howto / Language questions / Re: Osc message from Max to trigger a Routine
|
on: November 30, 2008, 09:47:51 AM
|
The Routine making the Synths needs to .yield something after playing the synth. Right now I think the loop is ending, causing the Routine to exit - at that point you can't do any more with it. But if that Routine loops infinitely and yields after each synth, you can call .next on the synth-routine to get the next synth. This is how I would do it. For fun, I'll convert your pitch stream to a pattern network  z = Pswitch([ Pseq(#[24, 31, 36, 43, 48, 55], 1), // run of fifths // varying arpeggio Pseq([60, Prand(#[63, 65], 1), 67, Prand(#[70, 72, 74], 1)], { rrand(2, 5) }), Prand(#[74,75,77,79,81], { rrand(3, 9) }) ], Plazy({ // if 0.5.coin is true, start with item 0; otherwise start with 1 // this mimics the 'if' in the original routine var start = 1 - 0.5.coin.binaryValue; Pseries(start, 1, 3 - start) })).asStream;
r = Routine { var freq; while { (freq = z.next).notNil } { Synth("Algorithmic", [freq: freq.midicps]).yield; // can yield anything } };
a = r.next; // do just one synth Then your osc responder can call .next on the synth routine. James
|
|
|
|
|