electro-music.com   Dedicated to experimental electro-acoustic
and electronic music
 
    Front Page  |  Radio
 |  Media  |  Forum  |  Wiki  |  Links
Forum with support of Syndicator RSS
 FAQFAQ   CalendarCalendar   SearchSearch   MemberlistMemberlist   UsergroupsUsergroups   LinksLinks
 RegisterRegister   ProfileProfile   Log in to check your private messagesLog in to check your private messages   Log inLog in  Chat RoomChat Room 
go to the radio page Live at electro-music.com radio 1 Please visit the chat
  host / artist show at your time
today> Twyndyllyngs tonight Chez Mosc
 Forum index » DIY Hardware and Software » ChucK programming language
Modedular: a class for musical mode and operations
Post new topic   Reply to topic Moderators: Kassen
Page 1 of 1 [21 Posts]
View unread posts
View new posts in the last week
Mark the topic unread :: View previous topic :: View next topic
Author Message
kijjaz



Joined: Sep 20, 2004
Posts: 765
Location: bangkok, thailand
Audio files: 4

PostPosted: Sun Feb 24, 2008 1:39 pm    Post subject: Modedular: a class for musical mode and operations
Subject description: please help me test this new tool.
Reply with quote  Mark this post and the followings unread

I wanted to have some kind of utility to work with musical modes (traditional, and comtemporary).
Today I'm done testing the first prototype: Modedular version 0.1 testing

these are the features:
* Mode data is kept in an array of integers as Intervals
for example, ionian (major mode) is [2, 2, 1, 2, 2, 2, 1] (1 = half-tone, 2 = whole-tone).

* Octave size (semitones) can be anything, from traditional (12 semitones) to anything,
and is autmatically calculated from the sum of intervals when the mode intervals are set
* Can be set to lydian, ionian, mixulydian, dorian, aeolian, phrygian, locrian
harmonic minor, melodic minor ... as presets (melodic minor = ascending melodic minor)
* Can return note by supplying pitch (1 = root.. and so on) or with pitch and octave
* Can rotate left / right by number of times to rotate (can be integer also)
* Can create chords by supplying root, chord degree as array of int

Okay.. enough with the features for now.
I'm trying to make it simple and easy to use.
But the version's code is still not optimized for best performance
(although the calculations are only looping and transfering integers)
Please give it a try and some feedback would be very very helpful to the next versions!

Code:
// Modedular version 0.1 testing
// by Kijjasak Triyanond (kijjaz) kijjaz@yahoo.com

// [note] this is still quite experimental, so some functions might have problems
// or too cpu-consuming. This is somewhat too simple and not optimized.
// some more features will be added soon!

// licence: Attribution-Share Alike 3.0
// You are free:
//    * to Share — to copy, distribute and transmit the work
//    * to Remix — to adapt the work
// Under the following conditions:
//    * Attribution. You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work).
//    * Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under the same, similar or a compatible license.

class Modedular
{
    [0] @=> int intervals[]; // array of intervals in the mode
    0 => int octaveSize; // octave size in semitones
       
    fun int update()
    {
        // use this to octave Octave Size
        0 => octaveSize;
        for(int i; i < intervals.cap(); i++) intervals[i] +=> octaveSize;
        return octaveSize;
    }
    fun void set(int input[])
    {
        // use this to copy intervals from the input array
        new int[input.cap()] @=> intervals;
        for(int i; i < input.cap(); i++) input[i] => intervals[i];
        update();
    }
    fun void set(string input)
    {
        // use this to set the mode to a preset value by a string
        if (input == "lydian") set([2,2,2,1,2,2,1]);
        if (input == "ionian") set([2,2,1,2,2,2,1]);
        if (input == "mixolydian") set([2,2,1,2,2,1,2]);
        if (input == "dorian") set([2,1,2,2,2,1,2]);
        if (input == "aeolian") set([2,1,2,2,1,2,2]);
        if (input == "phrygian") set([1,2,2,2,1,2,2]);
        if (input == "locrian") set([1,2,2,1,2,2,2]);
       
        if (input == "harmonic minor") set([2,1,2,2,1,3,1]);
        if (input == "melodic minor") set([2,1,2,2,2,2,1]);
        update();
    }
    fun void get(int input[])
    {
        // use this to copy to an outside array
        new int[input.cap()] @=> input;
        for(int i; i < input.cap(); i++) intervals[i] => input[i];
    }
   
    fun int note(int pitch)
    {
        // use this to acquire note (calculated in semitones) from the mode
        // without octave input
        pitch--; // so user can start the first pitch from 1 instead of 0
        0 => int octave; // but we still have to use octave if pitch is negative
        if (pitch >= intervals.cap()) // if pitch is higher than mode's cap(),
        {
            pitch / intervals.cap() +=> octave; // increase octave
            intervals.cap() %=> pitch; // and reset pitch to the mode's range
        }
        else while(pitch < 0)
        {
            // decrease octave if pitch is still negative, transpose up 1 octave
            intervals.cap() +=> pitch;
            octave--;
        }
       
        0 => int sum;
        for(int i; i < pitch; i++) intervals[i] +=> sum; // calculate semitones for the pitch
        octave * octaveSize +=> sum; // select desired octave
        return sum; // and we'll have the result in semitone
    }
    fun int note(int pitch, int octave)
    {
        // note, with octave number also
        return note(pitch) + octave * octaveSize;
    }   
   
    fun void rotate(int x)
    {
        // rotate the mode x times
       
        int dummy[intervals.cap()];
        // if x is not in the range 0..intervals.cap()-1, fix it
        // this will make both positive and negative rotation possible
        if (x >= intervals.cap()) intervals.cap() %=> x;
        else while(x < 0) intervals.cap() +=> x;
        for(int i; i < intervals.cap(); i++) intervals[i] => dummy[(i + x) % intervals.cap()];
        // restore new values
        for(int i; i < intervals.cap(); i++) dummy[i] => intervals[i];
    }
   
    fun void chord(int root, int positions[], int result[])
    {
        // make a chord from position list (chord degrees)
        for(int i; i < positions.cap() && i < result.cap(); i++)
        {
            note(root + positions[i]) => result[i];
        }
    }
    fun void chord(int root, int octave, int positions[], int result[])
    {
        // make a chord from position list, with octave
        for(int i; i < positions.cap() && i < result.cap(); i++)
        {
            note(root + positions[i]) + octave * octaveSize => result[i];
        }
    }   
}


You can test out with this set of test code:

Code:
// - - - - - test code - - - - -

Mandolin s1 => NRev rev => dac;
s1.gain(.5);
rev.mix(.2);
TriOsc s2[7];

Modedular A;
A.set("ionian");
<<< "mode A is set to ionian" >>>;
for(int i; i < A.intervals.cap(); i++) <<< "interval ", i, ": ", A.intervals[i] >>>;

// let's play a melody
<<< "let's play a melody" >>>;

48 => int baseNote; // start at C below middle C

[1, 1, 5, 5, 6, 6, 5, 4, 4, 3, 3, 2, 2 + 7, 1 + 7, 1 + 7*2] @=> int melody1[];
for(int i; i < melody1.cap(); i++)
{
    s1.noteOn(1);
    A.note(melody1[i]) + baseNote => Std.mtof => s1.freq;
    400::ms => now;   
}

2::second => now; // applause -_-"

// let's play something with the same melody but with random octave values
<<< "let's play something with the same melody but with random octave values" >>>;
[1, 1, 5, 5, 6, 6, 5, 4, 4, 3, 3, 2, 2, 1, 1] @=> int melody2[];
for(int i; i < melody2.cap(); i++)
{
    Std.rand2(-1, 2) => int octave;
    s1.noteOn(1);
    A.note(melody2[i], octave) + baseNote => Std.mtof => s1.freq;
    400::ms => now;   
}

2::second => now; // applause -_-"

// let's hear out this melody while rotating into other modes
<<< "let's hear out this melody while rotating into other modes" >>>;
[1, 3, 5, 7, 8, 7, 5, 3, 2, 4, 6, 8, 7, 5, 4, 0, 1] @=> int melody3[];
for(int mode; mode < 7; mode++)
{
    <<< "mode: ", mode >>>;
    for(int i; i < melody3.cap(); i++)
    {
        s1.noteOn(1);
        A.note(melody3[i]) + baseNote => Std.mtof => s1.freq;
        200::ms => now;   
    }
   
    second => now;
   
    A.rotate(1);
}

// let's hear some more exotic modes
<<< "let's hear some more exotic modes !!!" >>>;
A.set([1, 3, 1, 3, 1, 2, 1]); // C Db E F G# A B C .. whatever -_- ..
for(int mode; mode < 7; mode++)
{
    <<< "mode: ", mode >>>;
    for(int i; i < melody3.cap(); i++)
    {
        s1.noteOn(1);
        A.note(melody3[i]) + baseNote => Std.mtof => s1.freq;
        200::ms => now;   
    }
   
    second => now;
   
    A.rotate(1);
}
Back to top
View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger MSN Messenger
Frostburn



Joined: Dec 12, 2007
Posts: 255
Location: Finland
Audio files: 9

PostPosted: Sun Feb 24, 2008 1:56 pm    Post subject: Reply with quote  Mark this post and the followings unread

Coolness!
I just finished working with my KeyboardPlay class that works with my Instrument class and I was thinking that the way I did the scales wasn't the most flexible one. I'll dig in to your code later and see if it can be implemented directly.
How about fractional intervals to be able make Pythagorean scales? They may need to be defined for the whole frequency scale used though... Maybe they need their own treatment but I've already tweaked my Instrument code to support playing notes at arbitrary frequencies.

_________________
To boldly go where no man has bothered to go before.
Back to top
View user's profile Send private message
kijjaz



Joined: Sep 20, 2004
Posts: 765
Location: bangkok, thailand
Audio files: 4

PostPosted: Sun Feb 24, 2008 2:02 pm    Post subject: Reply with quote  Mark this post and the followings unread

Frostburn: Yes! I had already decided about microtonal.
A straightforward way to do that is to change the interval datas to float..
but actually I think, more musically, it's easier to count notes in integer.
so I decided to let it stay integer.

Instead, the midi note produced can later be converted into microtonal floating-point midi note number (for use with Std.mtof function).
I think that is not hard to implement. ^_^
we'll be testing on that soon!
Back to top
View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger MSN Messenger
kijjaz



Joined: Sep 20, 2004
Posts: 765
Location: bangkok, thailand
Audio files: 4

PostPosted: Sun Feb 24, 2008 4:37 pm    Post subject: Reply with quote  Mark this post and the followings unread

Frostburn: good news! I've just finished doing a class for managing microtonal scales
(still not very flexible, but nice for all classical and folk scales)

I'm quite sleepy now so i decide to let the code introduce itself.
I tried to make it as easy to use as possible,
it can be used with any midi number,
and surely, with the Modedular version 0.1 testing hahah

If you'd like to use Modedular with MicrotonalUnit,
try feeding the output from Modedular's note to MicrotonalUnit's ratio.
that'd return result in float that you can use to multiply with base frequency for the note to play.

You can try loading presets first (i've got Pythagorean and one 'just' in the presets).

Code:
// MicrotonalUnit version 0.1 testing
// by Kijjasak Triyanond (kijjaz) kijjaz@yahoo.com

// licence: Attribution-Share Alike 3.0
// You are free:
//    * to Share — to copy, distribute and transmit the work
//    * to Remix — to adapt the work
// Under the following conditions:
//    * Attribution. You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work).
//    * Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under the same, similar or a compatible license.

class MicrotonalUnit
{
    [1.0] @=> float intervals[]; // list of interval in terms of frequency ratio of the root note
    1 => int octaveSize; // octave size in semitones (according midi note value)
    2.0 => float octaveRatio; // frequency ratio of "up an octave"
   
    fun void setOctaveSize(float input)
    {
        input => octaveRatio;
    }
   
    fun void set(float input[], float octaveRatioIn)
    {
        input.cap() => octaveSize;
        new float[input.cap()] @=> intervals;
        for(int i; i < input.cap(); i++) input[i] => intervals[i];
        octaveRatioIn => octaveRatio;
    }
    fun void set(string input)
    {
        // these are some presets to try out       
        // Pythagorean tuning preset: from http://en.wikipedia.org/wiki/Pythagorean_tuning
        if (input == "Pythagorean")
        [1.0, 256.0/243, 9.0/8, 32.0/27,
        81.0/64, 4.0/3, 729.0/512, 3.0/2,
        128.0/81, 27.0/16, 16.0/9, 243.0/128] @=> intervals,
        2.0 => float octaveRatio;
       
        // just tuning preset: from http://en.wikipedia.org/wiki/Just_intonation
        // Only factors 2, 3 and 5 are used in the construction
        if (input == "just")
        [1.0, 16.0/15, 9.0/8, 6.0/5,
        5.0/4, 4.0/3, 45.0/32, 3.0/2,
        8.0/5, 5.0/3, 16.0/9, 15.0/8] @=> intervals,
        2.0 => float octaveRatio;
       
        // 12-tone equal temperament
        if (input == "12-TET")
        [1.000000, 1.059463, 1.122462, 1.189207,
        1.259921, 1.334840, 1.414214, 1.498307,
        1.587401, 1.681793, 1.781797, 1.887749] @=> intervals,
        2.0 => float octaveRatio;
       
        intervals.cap() => octaveSize;
    }
    fun void setBySemitone(float input[])
    {
        // set the intervals by supplying semitone values
        new float[input.cap()] @=> intervals;
        for(int i; i < input.cap(); i++) Math.pow(2.0, input[i] / 12) => intervals[i];
        intervals.cap() => octaveSize;
    }
    fun void setByOctave(float input[])
    {
        // set the intervals by supplying octave values
        new float[input.cap()] @=> intervals;
        for(int i; i < input.cap(); i++) Math.pow(2.0, input[i]) => intervals[i];
        intervals.cap() => octaveSize;
    }
    fun float ratio(int pitch)
    {
        // use this function to return the ratio of the input pitch number.
        // use the ratio to multiply with a base frequency
        // to get the desired frequency of the note
       
        pitch--; // so user can start the first pitch from 1 instead of 0
        0 => int octave; // but we still have to use octave if pitch is negative
        if (pitch >= intervals.cap() ) // if pitch is higher than mode's cap(),
        {
            pitch / intervals.cap() +=> octave; // increase octave
            intervals.cap() %=> pitch; // and reset pitch to the mode's range
        }
        else while(pitch < 0)
        {
            // decrease octave if pitch is still negative, transpose up 1 octave
            intervals.cap() +=> pitch;
            octave--;
        }
        return intervals[pitch] * Math.pow(octaveRatio, octave);
    }
}

and the some goes gain: the test code!

Code:
// - - - - - test code - - - - -

Flute s1 => NRev rev => dac;
s1.gain(.5);
rev.mix(.2);

60.0 => float baseNote;

MicrotonalUnit A;

A.set("12-TET");
<<< "MicrotonalUnit A is set to 12-TET" >>>;
for(int i; i < 25; i++)
{
    A.ratio(i + 1) * Std.mtof(baseNote) => s1.freq;
    s1.noteOn(.1);
    400::ms => now;
    s1.noteOff(1.0);
    100::ms => now;
}

2::second => now; // applause ..

A.set("Pythagorean");
<<< "MicrotonalUnit A is set to Pythagorean" >>>;
for(int i; i < 25; i++)
{
    A.ratio(i + 1) * Std.mtof(baseNote) => s1.freq;
    s1.noteOn(.1);
    400::ms => now;
    s1.noteOff(1.0);
    100::ms => now;
}

2::second => now; // applause ..

float scale1[19]; // make a 19-TET by using octave value
for(int i; i < 19; i++) i $ float / 19 => scale1[i];
A.setByOctave(scale1);

<<< "MicrotonalUnit A is set to 19-TET by hand" >>>;
for(int i; i < 20; i++)
{
    A.ratio(i + 1) * Std.mtof(baseNote) => s1.freq;
    s1.noteOn(.1);
    400::ms => now;
    s1.noteOff(1.0);
    100::ms => now;
}

2::second => now; // applause ..
Back to top
View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger MSN Messenger
Inventor
Stream Operator


Joined: Oct 13, 2007
Posts: 6221
Location: near Austin, Tx, USA
Audio files: 267

PostPosted: Sun Feb 24, 2008 11:31 pm    Post subject: Reply with quote  Mark this post and the followings unread

I tried the microtonal example and it sounded OK, but being totally untrained in music I'm not sure just what I'm listening to. I take it the three little ditty's played are sequences of notes, from low to high, in a particular scale system.

If that's true, then I'm wondering if we could collaborate a bit here. I could use your tonal classes in Guitar Lab. The frequencies are generated as an algebraic function (y=m(x+b) actually) of the weighted sum of bits that are active when the note is played. If this could map into your tonal system then instead of outputting funky frequencies that don't fit into a scale, the Guitar Lab would output scale notes.

I'm not sure if i can use your code directly or if there is some modification required. Let me know what you think.
Back to top
View user's profile Send private message Send e-mail
kijjaz



Joined: Sep 20, 2004
Posts: 765
Location: bangkok, thailand
Audio files: 4

PostPosted: Mon Feb 25, 2008 12:20 am    Post subject: Reply with quote  Mark this post and the followings unread

Inventor: At first, you still don't have to make any changes in the code to use it with your engine.
Instead, the note number generated from your engine.. from the logic calculation part.. (i think i saw them integers) can be used directly with Modedular as an argument in note function.

Hmm.. let's try this:
use the set function to start off with maybe a preset mode.
(let's say, you've got a Modedular object named A)
I'd try A.note(YourNumberGeneratedFromTheEngine) + BaseNote => Std.mtof => .. (this would be where the frequency goes.. for example, to a .freq of an Oscillator or an STK instrument)

With only this, you'll be able to hear out some riffs depending on notes from a scale.
Let me know if it goes well or how it'd go further ^_^,
thank you! Shocked
Back to top
View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger MSN Messenger
Frostburn



Joined: Dec 12, 2007
Posts: 255
Location: Finland
Audio files: 9

PostPosted: Mon Feb 25, 2008 12:34 am    Post subject: Reply with quote  Mark this post and the followings unread

On a second though I would scratch microtonal scales and extend Std.mtof instead.
This way you don't have to do double work if you want to use a lydian scale with pythagorean tuning. EDIT: Oh wait... your MicrotonalUnit just does that...

So a class that has a lot of functions for tuning:
Code:
class Tuning{ //Tuning functions like Std.mtof
//The difference this time is that only input of type int is accepted.
//int note_number => Tuning.fun => float freq;
/*I'll think about this a bit, ok. I'll edit later.*/
}

_________________
To boldly go where no man has bothered to go before.
Back to top
View user's profile Send private message
Inventor
Stream Operator


Joined: Oct 13, 2007
Posts: 6221
Location: near Austin, Tx, USA
Audio files: 267

PostPosted: Mon Feb 25, 2008 4:16 am    Post subject: Reply with quote  Mark this post and the followings unread

kijjaz wrote:
Inventor: At first, you still don't have to make any changes in the code to use it with your engine.
Instead, the note number generated from your engine.. from the logic calculation part.. (i think i saw them integers) can be used directly with Modedular as an argument in note function.


Ah, but I'm not using note numbers, I'm using frequencies. First I have an array of 12 bit weights held by an array of buttons. Then, if a note is played, then for each bit that is a 1 in the count you add its bit weight. Then you add in an offset, then multiply by a frequency multiplier.

I could cast that to an int and put it into your note() function as in the above examples, but that would be an awfully high note number. (100-1000 or more). But not so bad if I allow really small values of frequency multiplier...

I'm planning to put buttons for each of those pythagorean, whatever, types of scales.

Yes, I think all I have to do is sharply reduce the frequency multiplier slider's upper value and cast the frequency to an int when calling your method. Right?

(p.s. more of Kijjaz's code for my programs, hehe, it's like christmas again!)
Back to top
View user's profile Send private message Send e-mail
kijjaz



Joined: Sep 20, 2004
Posts: 765
Location: bangkok, thailand
Audio files: 4

PostPosted: Mon Feb 25, 2008 4:27 am    Post subject: Reply with quote  Mark this post and the followings unread

Inventor: "... First I have an array of 12 bit weights held by an array of buttons. Then, if a note is played, then for each bit that is a 1 in the count you add its bit weight. Then you add in an offset ..."
kijjaz: before adding an offset .. that's actually the result i'd like you to try feeding into the note function heheheh.
the number that would make the function produce the 'base note number' is 1, so if you usually have 1 as the first, you can feed it into the function rightaway.

The output from the note function should be int, suitable to 'add in an offset', then do Std.mtof to convert to frequency. and it's done ^_^

the offset is what I call 'baseNote'. It's gonna be the first note of the scale.
for example, if you add 60 to the result produced by note function, you'll start the first note at Middle C.
- - -

I'm gonna revise and produce a more effiecient (and possibly, shorter) code very soon.
Back to top
View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger MSN Messenger
Inventor
Stream Operator


Joined: Oct 13, 2007
Posts: 6221
Location: near Austin, Tx, USA
Audio files: 267

PostPosted: Mon Feb 25, 2008 4:40 am    Post subject: Reply with quote  Mark this post and the followings unread

kijjaz wrote:
I'm gonna revise and produce a more effiecient (and possibly, shorter) code very soon.


OK, kijjaz, I'll wait to use that one, I'm not coding for a few days because I have appointments the early part of this week. The thing is, I'm allowing those weights to be negative, so I need an offset to go with the bit weighting. Then after the multiplier I'll cast it to int and put it into your function, and then add the function output to baseNote which could be say 0 to 100 on the slider. It's a bit over-complicated but allows full flexibility.

Or maybe I should disallow negative bit weights and remove the frequency offset?

Not sure, gotta think on it more. Cool, thanks for the input.
Back to top
View user's profile Send private message Send e-mail
Frostburn



Joined: Dec 12, 2007
Posts: 255
Location: Finland
Audio files: 9

PostPosted: Mon Feb 25, 2008 5:00 am    Post subject: Reply with quote  Mark this post and the followings unread

Alright, time for some perfect ratios:
Code:
// licence: Attribution-Share Alike 3.0
// You are free:
//    * to Share — to copy, distribute and transmit the work
//    * to Remix — to adapt the work
// Under the following conditions:
//    * Attribution. You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work).
//    * Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under the same, similar or a compatible license.

class Tuning{ //Tuning functions like Std.mtof
//The difference this time is that only input of type int is generally accepted.
//int note_number => Tuning.tune => float freq;

    //baseNote => Tuning.tune == baseFreq; //With all scales
    69 => int baseNote;
    440.0 => float baseFreq;
    "12-TET" => string type;
       
    int hammingExponents[3]; //hamming_juster stores the a,b and c exponents of 2,3 and 5 that it finds here.
    //Just tuning related constants and variables:
    Math.log(2.0) => float ln2;
    Math.log(3.0) => float ln3;
    Math.log(5.0) => float ln5;
    float just_table[];
    0 => int just_max;
    0 => int just_base;
   

    fun float tune(float note_number){
        if(type == "12-TET") return TET(note_number,12); //The most popular tuning
        else if(type == "5-TET") return TET(note_number,5);
        else if(type == "7-TET") return TET(note_number,7);
        else if(type == "15-TET") return TET(note_number,15);
        else if(type == "19-TET") return TET(note_number,19);
        else if(type == "21-TET") return TET(note_number,21);
        else if(type == "22-TET") return TET(note_number,22);
        else if(type == "31-TET") return TET(note_number,31);
        else if(type == "34-TET") return TET(note_number,34);
        else if(type == "53-TET") return TET(note_number,53);
        else if(type == "72-TET") return TET(note_number,72);
       
        else if(type == "Bohlen-Pierce") return note_number => BP;
        else if(type == "BP") return note_number => BP;
       
        else if(type == "alpha") return note_number => alpha;
        else if(type == "beta") return note_number => beta;
        else if(type == "gamma") return note_number => gamma;
       
        else if(type == "just 12-TET") return hamming_juster( TET(note_number,12) , 4);
        else if(type == "just 19-TET") return hamming_juster( TET(note_number,19) , 4);
       
        else if(Math.floor(note_number) - note_number < 0.0){
            <<<"Sorry, the scale doesn't support fractional note_numbers","">>>;
            return 0.0;
        }
        else{
            if(type == "just") return note_number$int => just;
            else if(type == "just1") return just_free( note_number$int , 1);
            else if(type == "just2") return just_free( note_number$int , 2);
            else if(type == "just3") return just_free( note_number$int , 3);
            else if(type == "just4") return just_free( note_number$int , 4);
            else if(type == "just5") return just_free( note_number$int , 5);
            else if(type == "Pythagorean") return note_number$int => Pythagorean;
           
            else{<<<"Invalid scale!">>>; return 0.0; }
        }
    }

    //Base function for all equal temperaments that divide the octave in 'index' parts.
    fun float TET(float note_number, float index){
        return baseFreq*Math.pow( 2.0 , (note_number - baseNote$float)/index );
    }
   
    //The Bohlen-Pierce scale (BP scale)
    fun float BP(float note_number){
        return baseFreq*Math.pow( 3.0 , (note_number - baseNote$float)/13.0 );
    }
   
    //Equal temperament scales by Wendy Carlos:
    fun float alpha(float note_number){
        return baseFreq*Math.pow( 1.5 , (note_number - baseNote$float)/9.0 );
    }
    fun float beta(float note_number){
        return baseFreq*Math.pow( 1.5 , (note_number - baseNote$float)/11.0 );
    }
    fun float gamma(float note_number){
        return baseFreq*Math.pow( 1.5 , (note_number - baseNote$float)/20.0 );
    }
   
    //You still want more equal temperament?
    fun float EDO(float note_number, float index, float octave_size){
        return baseFreq*Math.pow( octave_size , (note_number - baseNote$float)/index );
    }
   
    //Search for the closest hamming frequency approximation [of the form baseFreq*2^a*3^b*5^c, a,b,c integers]
    // to the 'float freq' with |a|,|b|,|c| <= 'int max'.
    fun float hamming_juster(float freq,int max){
        Math.log(baseFreq) => float lnbase;
        Math.log(freq) => float lnfreq;
       
        float candidate[(2*max+1)*(2*max+1)];
        int exponents[candidate.cap()][3];
        0 => int index;
       
        int c;
        for(-max => int a; a <= max; a++){
            //Math.pow(2.0,a) => two;
            for(-max => int b; b <= max; b++){
                //baseFreq*Math.pow(2.0,a)*Math.pow(3.0,b)*Math.pow(5.0,c) == freq;
                Math.round(   -(lnbase-lnfreq+a*ln2+b*ln3)/ln5   )$int => c;
                if(Std.abs(c) <= max){
                    lnbase+a*ln2+b*ln3+c*ln5 => candidate[index];
                    a => exponents[index][0];
                    b => exponents[index][1];
                    c => exponents[index][2];
                    index++;
                }
            }
        }
        //Compare the logarithms:
        candidate[0] - lnfreq => Std.fabs => float min;
        0 => int minindex;
        for(1 => int i; i < index; i++){
            if( Std.fabs(candidate[i] - lnfreq) < min ){
                candidate[i] - lnfreq => Std.fabs => min;
                i => minindex;
            }
        }
        for(0 => int i; i < 3; i++) exponents[minindex][i] => hammingExponents[i]; //Store the exponents for later analysis
        return Math.exp(candidate[minindex]);
    }
   
    fun float Pythagorean(int note_number){
            Math.floor( (note_number - baseNote)/12.0 )$int => int octave;
            note_number - baseNote - 12*octave => int note;
            float ratio;
            if(note == 0) 1.0 => ratio;
            else if(note == 1) 256.0/243.0 => ratio;
            else if(note == 2) 9.0/8.0 => ratio;
            else if(note == 3) 32.0/27.0 => ratio;
            else if(note == 4) 81.0/64.0 => ratio;
            else if(note == 5) 4.0/3.0 => ratio;
            else if(note == 6) 729.0/512.0 => ratio;
            else if(note == 7) 3.0/2.0 => ratio;
            else if(note == 8) 128.0/81.0 => ratio;
            else if(note == 9) 27.0/16.0 => ratio;
            else if(note == 10) 16.0/9.0 => ratio;
            else if(note == 11) 243.0/128.0 => ratio;
            else {
                <<<"Invalid note!","Returning the pythagorean comma.">>>;
                531441.0/524288.0 => ratio;
            }
                       
            return baseFreq*ratio*Math.pow(2.0,octave);
    }
    fun float just(int note_number){
            Math.floor( (note_number - baseNote)/12.0 )$int => int octave;
            note_number - baseNote - 12*octave => int note;
            float ratio;
            if(note == 0) 1.0 => ratio;
            else if(note == 1) 16.0/15.0 => ratio;
            else if(note == 2) 9.0/8.0 => ratio;
            else if(note == 3) 6.0/5.0 => ratio;
            else if(note == 4) 5.0/4.0 => ratio;
            else if(note == 5) 4.0/3.0 => ratio;
            else if(note == 6) 45.0/32.0 => ratio; //64.0/45.0 => ratio;
            else if(note == 7) 3.0/2.0 => ratio;
            else if(note == 8) 8.0/5.0 => ratio;
            else if(note == 9) 5.0/3.0 => ratio;
            else if(note == 10) 16.0/9.0 => ratio;
            else if(note == 11) 15.0/8.0 => ratio;
            else {
                <<<"Invalid note!">>>;
                0.0 => ratio;
            }
                       
            return baseFreq*ratio*Math.pow(2.0,octave);
    }
    fun float just_free(int note_number,int max){
        if (just_max != max) generate_just_table(max);
        note_number - baseNote + just_base => int just_index;
        if (just_index < 0) return 0.0;
        else if (just_index >= just_table.cap() ) return 0.0;
        else return baseFreq*Math.exp(just_table[just_index]);
    }
    //Generate all the ratios of the form 2^a*3^b*5^c, a,b,c integers
    // with |a|,|b|,|c| <= 'int max' and arrange their logarithms by magnitude in 'just_table'
    fun void generate_just_table(int max){
        new float[(2*max+1)*(2*max+1)*(2*max+1) + 1 ] @=> just_table;
        float lnratio;
        0 => int index;
        for( -max => int c; c <= max; c++){
            for( - max => int b; b <= max; b++){
                for( - max => int a; a <= max; a++){
                    a*ln2 + b*ln3 + c*ln5 => lnratio;
                    if (index == 0) lnratio => just_table[index];
                    else{
                        index => int i;
                        while(true){
                            if(just_table[i] <= lnratio){
                                lnratio => just_table[i+1];
                                break;
                            }
                            else just_table[i] => just_table[i+1];
                           
                            if(i <= 0){ <<<"error while generating 'just_table'">>>; break; }
                            i--;
                        }
                    }
                    index++;
                }
            }
        }
        for(0 => int i; i < just_table.cap(); i++){
            if(just_table[i] == 0.0){
                i => just_base;
                break;
            }
        }
        max => just_max;
    }
}

Try it out:
Code:
//Try some just tuning and print the errors:
Tuning tuning;
"just 12-TET" => tuning.type;
Impulse imp => BiQuad ping => dac;
0.9999 => ping.prad;
float freq;

for(30 => int i; i <= 110; i++){
    i => tuning.tune => freq => ping.pfreq;
    0.0001*freq => imp.next;
    <<<"The midi note number",i,"is approximating the just-tuned frequency of:">>>;
    <<<tuning.baseFreq,"* ( 2 ^",tuning.hammingExponents[0],") * ( 3 ^",tuning.hammingExponents[1],") * (5 ^",tuning.hammingExponents[1],")">>>;
    <<<"Error:",(1.0-Std.mtof(i)/freq)*100.0,"%">>>;
    300::ms => now;
}
3::second => now;

Code:
//This time just play the just-tuned frequencies and don't try to simulate equal temperament
Tuning tuning;
"just2" => tuning.type;
Mandolin s1 => NRev rev => dac;
s1.gain(.5);
rev.mix(.2);
for(40 => int i; i < 100; i++){
    i => tuning.tune => s1.freq;
    s1.noteOn(1);
    200::ms => now;
}
3::second => now;

Code:
//Hear that Pythagorean tuning works
Tuning tuning;
"Pythagorean" => tuning.type;
Mandolin s1 => NRev rev => dac;
s1.gain(.5);
rev.mix(.2);

for(48 => int i; i <= 72; i++){
    i => tuning.tune => s1.freq;
    s1.noteOn(1);
    200::ms => now;
}
3::second => now;


EDIT: Changed "just" to refer to the standard just tuning (that I also added while editing) and just_free for the limiting cases.

_________________
To boldly go where no man has bothered to go before.
Back to top
View user's profile Send private message
Frostburn



Joined: Dec 12, 2007
Posts: 255
Location: Finland
Audio files: 9

PostPosted: Mon Feb 25, 2008 7:47 am    Post subject: Reply with quote  Mark this post and the followings unread

kijjaz, I was able to use your class without modification with my KeyboardPlay. Worked like a charm, good work!
I did however change the part where you do:
Code:
pitch--; // so user can start the first pitch from 1 instead of 0

ChucK uses C syntax and I prefer that everything starts at zero if arrays start at zero.

_________________
To boldly go where no man has bothered to go before.
Back to top
View user's profile Send private message
kijjaz



Joined: Sep 20, 2004
Posts: 765
Location: bangkok, thailand
Audio files: 4

PostPosted: Mon Feb 25, 2008 3:51 pm    Post subject: Reply with quote  Mark this post and the followings unread

FrostBurn: Arrrggh that was such a great music architectural class!!
I still haven't read and understand all the systems in there.
I'll check out later.

I agree with you, I really wanted to make everything starts from 0
But considering 'ease of use', it sounds more conventional starting from 1 as what most musicians are more accustomed to..
for example:
a triad starting on the root position: 1 3 5 (not 0 2 4)
(alrhough my brain says 0 2 4) hahaahaha

so my version is still kept starting at pitch = 1.
- - -

Good news: Version 0.2 is releasing! .. a little more new features on the rotation..
and more optimized (cleaned up) code.
and.. test code for the new rotation feature and the chord feature.

Code:
// Modedular version 0.2 testing
// by Kijjasak Triyanond (kijjaz) kijjaz@yahoo.com

// [note] this is still quite experimental, so some functions might have problems
// some more features will be added soon.
// [fixes]
// * now the note function's code had been cleaned up
//   and is greatly reduced (in size) and optimized (generalized); the output is the same.
// * i've decided that the normal rotation should not rotate the whole mode data,
//   but instead set the offset of the readhead while using note function
//   the rotation can by 'Applied' so that it really rotate the interval array.
// * now chord is fully functional, check the test code to see chord in action

// licence: Attribution-Share Alike 3.0
// You are free:
//    * to Share — to copy, distribute and transmit the work
//    * to Remix — to adapt the work
// Under the following conditions:
//    * Attribution. You must attribute the work in the manner specified by the author or licensor (but not in any way that suggests that they endorse you or your use of the work).
//    * Share Alike. If you alter, transform, or build upon this work, you may distribute the resulting work only under the same, similar or a compatible license.

class Modedular
{
    [0] @=> int intervals[]; // array of intervals in the mode
    0 => int octaveSize; // octave size in semitones
    0 => int rotationOffset; // for easy mode rotation
       
    fun int update()
    {
        // use this to octave Octave Size
        0 => octaveSize;
        for(int i; i < intervals.cap(); i++) intervals[i] +=> octaveSize;
        return octaveSize;
    }
    fun void set(int input[])
    {
        // use this to copy intervals from the input array
        new int[input.cap()] @=> intervals;
        for(int i; i < input.cap(); i++) input[i] => intervals[i];
        update();
    }
    fun void set(string input)
    {
        // use this to set the mode to a preset value by a string
        if (input == "lydian") set([2,2,2,1,2,2,1]);
        if (input == "ionian") set([2,2,1,2,2,2,1]);
        if (input == "mixolydian") set([2,2,1,2,2,1,2]);
        if (input == "dorian") set([2,1,2,2,2,1,2]);
        if (input == "aeolian") set([2,1,2,2,1,2,2]);
        if (input == "phrygian") set([1,2,2,2,1,2,2]);
        if (input == "locrian") set([1,2,2,1,2,2,2]);
       
        if (input == "harmonic minor") set([2,1,2,2,1,3,1]);
        if (input == "melodic minor") set([2,1,2,2,2,2,1]);
        update();
    }
    fun void get(int input[])
    {
        // use this to copy to an outside array
        new int[input.cap()] @=> input;
        for(int i; i < input.cap(); i++) intervals[i] => input[i];
    }
   
    fun int note(int pitch)
    {
        // use this to acquire note (calculated in semitones) from the mode
        // without octave input
        pitch--; // so user can start the first pitch from 1 instead of 0
        0 => int octave; // but we still have to use octave if pitch is negative
       
        // calculate pitch and octave for use the intervals array
        // by limiting pitch in rang 0..intervals.cap()-1 and adjust octave number
        if (pitch < 0) octave--;
        pitch / intervals.cap() +=> octave;
        (pitch - (pitch / intervals.cap() - 1) * intervals.cap()) % intervals.cap() => pitch;
       
        0 => int sum;
        // calculate semitones for the pitch
        // with rootPosition for easy mode rotation
        for(int i; i < pitch; i++) intervals[(i + rotationOffset) % intervals.cap()] +=> sum;
        octave * octaveSize +=> sum; // select desired octave
        return sum; // and we'll have the result in semitone
    }
    fun int note(int pitch, int octave)
    {
        // note, with octave number also
        return note(pitch) + octave * octaveSize;
    }   
   
    fun void rotate(int x)
    {
        // rotate the mode x times       
        x +=> rotationOffset;
        (rotationOffset - (rotationOffset / intervals.cap() - 1) * intervals.cap()) % intervals.cap() => rotationOffset;
    }
    fun void setRotate(int x)
    {
        // reset rotation point to x
        x => rotationOffset;
        (rotationOffset - (rotationOffset / intervals.cap() - 1) * intervals.cap()) % intervals.cap() => rotationOffset;
    }
    fun void rotateApply()
    {
        // use current rotation offset to really rotate the interval array.
        // then reset the rotation offset.
        int dummy[intervals.cap()];
        for(int i; i < intervals.cap(); i++) intervals[(i + rotationOffset) % intervals.cap()] => dummy[i];
        for(int i; i < intervals.cap(); i++) dummy[i] => intervals[i];
        0 => rotationOffset;
    }
    fun void rotateApply(int x)
    {
        // use in supplied number as the rotation offset, then really rotate the interval array.
        // then reset the rotation offset, also.       
        setRotate(x);
        rotateApply();
    }
   
    fun void chord(int root, int positions[], int result[])
    {
        // make a chord from position list (chord degrees)
        for(int i; i < positions.cap() && i < result.cap(); i++)
        {
            note(root + positions[i] - 1) => result[i];
        }
    }
    fun void chord(int root, int octave, int positions[], int result[])
    {
        // make a chord from position list, with octave
        for(int i; i < positions.cap() && i < result.cap(); i++)
        {
            note(root + positions[i] - 1) + octave * octaveSize => result[i];
        }
    }   
}


Test code: The old test code can be used and should produce the same result (with a little bit improved CPU consumption)
The new test code mainly features the features that are fixed/added:
- Rotation
- Chord

Code:
// - - - - - test code

60 => int baseNote; // use middle C as base note

Flute s1 => NRev rev => dac; // prepare flute and reverb for performance

Clarinet s2[4]; // prepare clarinet orchestra
for(int i; i < 4; i++)
{
    s2[i] => rev;
    s2[i].gain(.1);
}


s1.gain(.5);
rev.mix(.15);

Modedular A;

fun void PlayScale()
{
    for(1 => int i; i <= 15; i++)
    {
        A.note(i) + baseNote => Std.mtof => s1.freq;
        s1.noteOn(.2);
        200::ms => now;
        s1.noteOff(.2);
        50::ms => now;
    }
}

<<< "play notes from C ionain mode.", "" >>>;
A.set("ionian");
PlayScale();
second => now;

<<< "rotate mode by +1.", "" >>>;
A.rotate(1);
PlayScale();
<<< "oh, correct. That was dorian, so the rotation works fine", "" >>>;
second => now;

<<< "now rotate it by -3 and let's see.", "" >>>;
A.rotate(-3);
PlayScale();
<<< "that is aeolian. Okay the rotation works correctly", "" >>>;
second => now;

<<< "take a look at the interval array: ", "" >>>;
for(int i; i < A.intervals.cap(); i++) <<< "interval ", i, " : ", A.intervals[i] >>>;
<<< "ah! after rotating it around, it still stays as an ionian the same,", "" >>>;
<<< "let's try applying the rotation and see that the change is update in the interval array.", "" >>>;
A.rotateApply();
for(int i; i < A.intervals.cap(); i++) <<< "interval ", i, " : ", A.intervals[i] >>>;
PlayScale();
second => now;

// - - - chord testing
// although using Note is simple enough to use as chord creator, but I'm introducing a chord idea:

fun void PlayChord(int semitones[])
{
    for(int i; i < 4; i++)
    {
        semitones[i] + baseNote => Std.mtof => s2[i].freq;
        s2[i].noteOn(.8);
    }
    1200::ms => now;
    for(int i; i < 4; i++) s2[i].noteOff(.8);
    200::ms => now;
   
}

<<< "make 7th chord form: 1 3 5 7", "" >>>;
int ChordResult[4]; // will be used to keep notes produced from Modedular's chord function
int ChordDegree[]; // will be used to specify chord form

[1, 3, 5, 7] @=> ChordDegree;

for(1 => int i; i <= 8; i++)
{
    A.chord(i, ChordDegree, ChordResult);
    PlayChord(ChordResult);
}
second => now;

<<< "try changing into another mode, and play with a different set of chord degrees", "" >>>;
<<< "change mode to super locriant", "" >>>;
<<< "and change chord degrees to 1 4 5 7 but 7 up an octave (7+7)", "" >>>;
A.set([1, 2, 1, 2, 2, 2, 2]); // set A to "Super Locrian" mode to play some jazzy tension chords
[1, 4, 5, 7 + 7] @=> ChordDegree; // change voicing form, notice the +7, this simply means up an octave
// (because this time, we have 7 tones per octave)

for(1 => int i; i <= 8; i++)
{
    A.chord(i, ChordDegree, ChordResult);
    PlayChord(ChordResult);
}
second => now;
Back to top
View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger MSN Messenger
kijjaz



Joined: Sep 20, 2004
Posts: 765
Location: bangkok, thailand
Audio files: 4

PostPosted: Tue Feb 26, 2008 1:08 am    Post subject: Reply with quote  Mark this post and the followings unread

now Modedular is on ChucK's wiki: http://wiki.cs.princeton.edu/forums.html/Modedular.ck.
It includes a brief (and easier to understand) user manual.

Keep rockin' people. ^_^
Back to top
View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger MSN Messenger
Inventor
Stream Operator


Joined: Oct 13, 2007
Posts: 6221
Location: near Austin, Tx, USA
Audio files: 267

PostPosted: Tue Feb 26, 2008 10:55 pm    Post subject: Reply with quote  Mark this post and the followings unread

OK, I plugged in the class without too much effort. I did have to make a bunch of buttons and write functions for all the buttons, but that went alright. Some questions.

in the guitar when you do a for loop over the three strings and set each higher indexed string as a multiple of the first frequency, creating harmonics, how is that implemented with these notes?

Also, what about rotate and chord, could I put buttons for those up there, and what would be reasonable ranges for those buttons?

Nice job, i like it.
Back to top
View user's profile Send private message Send e-mail
kijjaz



Joined: Sep 20, 2004
Posts: 765
Location: bangkok, thailand
Audio files: 4

PostPosted: Wed Feb 27, 2008 2:49 am    Post subject: Reply with quote  Mark this post and the followings unread

Inventor: "In the guitar when you do a for loop over the three strings and set each higher indexed string as a multiple of the first frequency, creating harmonics, how is that implemented with these notes?"

kijjaz: "I think the easiest way to implement this is to do that with frequency. so you can just multiply frequency with the harmonic number before setting the string's .freq."
- - -

Thanks Inventor! Our collaboration would produce one cool playable tool.
- - -

Good news... version 0.3 is released. hahahah
with many many new features! .. those are mainly what I'd like to experiment with modes.
now it includes a manual as comments.
this one has a long.. long... test code.

The test code is integrated on the file.
So after testing, just copy the class code out and use with your new file.


kijjaz-Modedulator-0.3-testing.ck
 Description:
ChucK class and code for testing the class (more like a tutorial/introduction) for playing with music modes and mode operations.

Download (listen)
 Filename:  kijjaz-Modedulator-0.3-testing.ck
 Filesize:  15.67 KB
 Downloaded:  180 Time(s)

Back to top
View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger MSN Messenger
Frostburn



Joined: Dec 12, 2007
Posts: 255
Location: Finland
Audio files: 9

PostPosted: Wed Feb 27, 2008 4:22 am    Post subject: Reply with quote  Mark this post and the followings unread

very nice

EDIT: I added the following modifications to the version I'm using with my classes.

//Contructor code:
1 => int firstKey; //One is the first key. -added by Frostburn
//...
// fun int note(int pitch)
// {
// use this to acquire note (calculated in semitones) from the mode
// without octave input

firstKey -=> pitch; // so user can start the first pitch from 1 instead of 0
//...


Now I'll just have to 0 => my_modedular.firstKey; whenever I use it.

_________________
To boldly go where no man has bothered to go before.
Back to top
View user's profile Send private message
Inventor
Stream Operator


Joined: Oct 13, 2007
Posts: 6221
Location: near Austin, Tx, USA
Audio files: 267

PostPosted: Thu Feb 28, 2008 4:20 am    Post subject: Reply with quote  Mark this post and the followings unread

Excellent, kijjaz, I put in that change as you described and now my guitar sounds like a guitar again! haha! Only this time it is scaled and that's a new feature of the program.

I'd like to add some of the features of your class as well, such as rotate or chorus, I'll go look and see if you documented those well enough for me to understand. Really, I want to add any buttons or sliders that change the sound.

BTW, I made baseNote be 0 to 100, is that a good range for it?

Keep up the good work, kijjaz!
Back to top
View user's profile Send private message Send e-mail
kijjaz



Joined: Sep 20, 2004
Posts: 765
Location: bangkok, thailand
Audio files: 4

PostPosted: Thu Feb 28, 2008 5:24 am    Post subject: Reply with quote  Mark this post and the followings unread

Frostburn: " I added the following modifications to the version I'm using with my classes. "

kijjaz: "Aaahh! Thanks. That is more convenient. I'll add to the next version with your credit, ok?"

(the update is in version 0.4 release candidate 1. It also applies to the chord generation part)
- - -

Inventor: "BTW, I made baseNote be 0 to 100, is that a good range for it?"

kijjaz: "Hmm.. the range is okay comparing to the midi note range
(0 - 127.. comparing to the range of the piano - extended down and up a little bit).
But it very much depends on the sound/instrument chosen to play.
For example, if it's some pure synth sound, some basic oscillators, etc. it's ok because they're fine producing a wide range of pitches.
But with some instruments we've got (especially in StkInstruments), pitch range does effect how the outcome will be.
For example, Mandolin starts to go out of tune when played too high.
(But sounds okay at the bass)
The flute and be made very high, but at bass-tenor range, it easily resonance in harmonics.
(but it also depends on the parameters of each instruments)..

So.. hmm it depends ^_^

for me, a Clarinet is a good example of quite a wide range."


kijjaz-Modedulator-0.4-RC1.ck
 Description:
Modedular version 0.4 RC1

Download (listen)
 Filename:  kijjaz-Modedulator-0.4-RC1.ck
 Filesize:  15.87 KB
 Downloaded:  211 Time(s)

Back to top
View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger MSN Messenger
Inventor
Stream Operator


Joined: Oct 13, 2007
Posts: 6221
Location: near Austin, Tx, USA
Audio files: 267

PostPosted: Thu Feb 28, 2008 5:53 am    Post subject: Reply with quote  Mark this post and the followings unread

OK, i set it for 0-127. It is kind of fun fine-tuning my application with your goodies in it. Right now I have a problem with Flute in that it is always on, it never accepts a noteOff () for some reason. I've got startBlowing () and stopBlowing() in there too, so I'm not sure if I've mixed something up.

BTW your latest example wouldn't run on miniAudicle. It just said

[kijjaz_modedulator_03_testing_170.ck]:line(1).char(1): parse error

huh? line(1).char(1) is the / character. dunno what that's all about.
Back to top
View user's profile Send private message Send e-mail
kijjaz



Joined: Sep 20, 2004
Posts: 765
Location: bangkok, thailand
Audio files: 4

PostPosted: Thu Feb 28, 2008 7:00 am    Post subject: Reply with quote  Mark this post and the followings unread

Hmm.. i also tried by opening the attached .ck file directy on the browser, copied to miniAudicle editor (but on Linux) and it works well..
i think it worths trying copy its text from the web browser and see if it runs okay..

Hmm ^_^ Thank you for collaborating also. this will keep me going & develope more from it.
I'm gonna write a song incoporating the tuning function and Modedular very soon.
Back to top
View user's profile Send private message Send e-mail Visit poster's website Yahoo Messenger MSN Messenger
Display posts from previous:   
Post new topic   Reply to topic Moderators: Kassen
Page 1 of 1 [21 Posts]
View unread posts
View new posts in the last week
Mark the topic unread :: View previous topic :: View next topic
 Forum index » DIY Hardware and Software » ChucK programming language
Jump to:  

You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot vote in polls in this forum
You cannot attach files in this forum
You can download files in this forum


Forum with support of Syndicator RSS
Powered by phpBB © 2001, 2005 phpBB Group
Copyright © 2003 through 2009 by electro-music.com - Conditions Of Use