Xylophone

classic Classic list List threaded Threaded
6 messages Options
iv
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Xylophone

iv
Hi, this is the first time I use Supercollider and I'm trying to create a xylophone.
What I want is to have 8 buttons (each representing a xylophone button) that when pressed they play the corresponding note.

I tried to create a sound for each note that is fairly probable.
Anyway, I think the main problem is that the notes (note1 ... note8) are repeated indefinitely.
In reality when a bar is beaten, it plays once only.
How can I change the code so that the sound does not repeat itself?

This is my GUI:
myGUIxylophone

And this is the code:

        // CODE FOR SOUND
        (
        ~impulse = { Impulse.ar(
                freq: 2,
                phase: 0,
                mul: 0.5,
                add: 0
        )};

        ~note1 = { Klank.ar(
                specificationsArrayRef: Ref.new([[1000], [1], [1]]),
            input: ~impulse,
            freqscale: 1,
            freqoffset: 0,
            decayscale: 1
        )};

        ~note2 = { Klank.ar(
                specificationsArrayRef: Ref.new([[1500], [1], [1]]),
            input: ~impulse,
            freqscale: 1,
            freqoffset: 0,
            decayscale: 1
        )};

        ~note3 = { Klank.ar(
                specificationsArrayRef: Ref.new([[2000], [1], [1]]),
            input: ~impulse,
            freqscale: 1,
            freqoffset: 0,
            decayscale: 1
        )};

        ~note4 = { Klank.ar(
                specificationsArrayRef: Ref.new([[2500], [1], [1]]),
            input: ~impulse,
            freqscale: 1,
            freqoffset: 0,
            decayscale: 1
        )};

        ~note5 = { Klank.ar(
                specificationsArrayRef: Ref.new([[3000], [1], [1]]),
            input: ~impulse,
            freqscale: 1,
            freqoffset: 0,
            decayscale: 1
        )};

        ~note6 = { Klank.ar(
                specificationsArrayRef: Ref.new([[3500], [1], [1]]),
            input: ~impulse,
            freqscale: 1,
            freqoffset: 0,
            decayscale: 1
        )};

        ~note7 = { Klank.ar(
                specificationsArrayRef: Ref.new([[4000], [1], [1]]),
            input: ~impulse,
            freqscale: 1,
            freqoffset: 0,
            decayscale: 1
        )};

        ~note8 = { Klank.ar(
                specificationsArrayRef: Ref.new([[4500], [1], [1]]),
            input: ~impulse,
            freqscale: 1,
            freqoffset: 0,
            decayscale: 1
        )};

        ~note1.play;
        ~note2.play;
        ~note3.play;
        ~note4.play;
        ~note5.play;
        ~note6.play;
        ~note7.play;
        ~note8.play;
        )

        // CODE FOR GUI
        (
        ~impulse = { Impulse.ar(
                freq: 2,
                phase: 0,
                mul: 0.5,
                add: 0
        )};

        ~note1 = { Klank.ar(
                specificationsArrayRef: Ref.new([[1000], [1], [1]]),
            input: ~impulse,
            freqscale: 1,
            freqoffset: 0,
            decayscale: 1
        )};

        w = Window.new(
                name: "Xylophone",
                resizable: true,
                border: true,
                server: s,
                scroll: false);

        b = Button(w, Rect(20, 20, 40, 190))
            .states_([["n1", Color.black, Color.fromHexString("#FF0000")]])
            .action_({~note1.play;});

        b = Button(w, Rect(60, 30, 40, 170))
            .states_([["n2", Color.black, Color.fromHexString("#FFC000")]])
            .action_({~note1.play;});

        b = Button(w, Rect(100, 40, 40, 150))
            .states_([["n3", Color.black, Color.fromHexString("#FFFF00")]])
            .action_({~note1.play;});

        b = Button(w, Rect(140, 50, 40, 130))
            .states_([["n4", Color.black, Color.fromHexString("#92D050")]])
            .action_({~note1.play;});

        b = Button(w, Rect(180, 60, 40, 110))
            .states_([["n5", Color.black, Color.fromHexString("#00B050")]])
            .action_({~note1.play;});

        b = Button(w, Rect(220, 70, 40, 90))
            .states_([["n6", Color.black, Color.fromHexString("#00B0F0")]])
            .action_({~note1.play;});

        b = Button(w, Rect(260, 80, 40, 70))
            .states_([["n7", Color.black, Color.fromHexString("#0070C0")]])
            .action_({~note1.play;});

        b = Button(w, Rect(300, 90, 40, 50))
            .states_([["n8", Color.black, Color.fromHexString("#7030A0")]])
            .action_({~note1.play;});

        w.front; // visualizza la finestra
        //w.onClose_({r.stop}); // How to stop sound when I close windows?
        )

Now if I press any button, the program always sounds the same note. How do I associate a different sound with each button?

I'm not a musician but from what I've learned by reading on the internet, ''xylophone extension goes from Do4 (262 Hz) to Do8 (4186 Hz) so 4 octaves".
That is, what are the 8 notes of each bar? Are these?

        Do4 = C4 =  262 Hz
        Do5 = C5 =  523 Hz
        Do6 = C6 = 1046 Hz
        Do7 = C7 = 2093 Hz
        Do8 = C8 = 4186 Hz

If so, how do I reproduce them correctly? I was thinking of doing such that but it does not seem to me to works correctly:

        // Do4
        ~impulse1 = { Impulse.ar(
                freq: 262,
                phase: 0,
                mul: 0.5,
                add: 0
        )};

        ~note1 = { Klank.ar(
                specificationsArrayRef: Ref.new([[1000], [1], [1]]),
            input: ~impulse1,
            freqscale: 1,
            freqoffset: 0,
            decayscale: 1
        )};

        // Do5
        ~impulse2 = { Impulse.ar(
                freq: 523,
                phase: 0,
                mul: 0.5,
                add: 0
        )};

        ~note2 = { Klank.ar(
                specificationsArrayRef: Ref.new([[1000], [1], [1]]),
            input: ~impulse2,
            freqscale: 1,
            freqoffset: 0,
            decayscale: 1
        )};

        // etc...


Thank you very much!

PS: sorry for my bad english.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Xylophone

shiihs
Hello Stefania,

There's a short answer and a longer answer. I'm going to give only the short answer now because it's way past bedtime for me. Also I'm traveling in the coming days so I won't be able to give you the long answer in the very short term.

Short answer: since you put the freq argument in Impulse.ar to value 2, this will generate 2 impulses per second. If you change it to 0, Impulse.ar behaves special: it will generate only one pulse and then stop. This is what you wanted to happen.

The longer answer involves discussing things like coding style (spoiler: you've worked too hard ;) ) and addressing the resource leaks you have. (spoiler: if you watch the server statistics, you will notice that the numbers keep increasing for every note you play. This means that if you play enough notes, the server will eventually get overloaded and may crash.)

Sorry for the incomplete answer.

Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Xylophone

shiihs
In reply to this post by iv
* also you've used ~note1.play as an action in every button. I guess you wanted this to be ~note2.play and ~note3.play, ...
*
iv
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Xylophone

iv
This post was updated on .
In reply to this post by shiihs
Thanks for your answer, it was very useful.
I've complicated the code a bit by adding a SynthDef and a Routine.

My goal is this:
goal

At the top there are 8 buttons corresponding the xylophone bars. When the user presses the button, the corresponding note is played.

Below is a section about random notes. These notes are always those made of xylophone.
The user can choose how many notes to play.
I tried to implement this part through a Routine but didn't complete it.
If I start the routine by hand (~randomRou.play) then everything works.
I don't know how to associate the parameter entered by the user as the var rep of the routine.

Below there is a section that I don't know how to implement it.
I would like to create a "fixed" sound: the jingle bells song, where the notes are fixed while the execution speed changes according to the slider value.
I tried to do this with a routine but I didn't succeed, how can I do that?

My code is:

    /*************************************************************/
    /* SYNTHDEF definition                                       */
    /*************************************************************/
    (
    SynthDef("xyl", {
        |freqs = #[440], rings = #[1], out = 0, pan = 5|

        var imp = Impulse.ar(
                freq: 0,
                phase: 0,
                mul: 0.5,
                add: 0
        );

        var snd = Klank.ar(
                specificationsArrayRef: `[freqs, nil, rings],
                input: imp,
                freqscale: 3,
                freqoffset: 0,
                decayscale: 1
        );

        Out.ar(
                bus: out,
                channelsArray: snd; Pan2.ar(
                    in: snd,
                    pos: 0,
                    level: pan));
    }).add;



    /*************************************************************/
    /* ROUTINE                                                   */
    /*************************************************************/
    ~randomRou = Routine({

        var delta; // time between a note and the next
        var rep = 5; // number of notes

        // repeat rep times
        rep.do {
            delta = rrand(1, 4) * 0.2;

            // possible notes
            x = [{Synth("xyl", [\freqs, #[150]])},
                 {Synth("xyl", [\freqs, #[200]])},
                 {Synth("xyl", [\freqs, #[250]])},
                 {Synth("xyl", [\freqs, #[300]])},
                 {Synth("xyl", [\freqs, #[350]])},
                 {Synth("xyl", [\freqs, #[400]])},
                 {Synth("xyl", [\freqs, #[450]])},
                 {Synth("xyl", [\freqs, #[500]])}
            ];

            // play a random notes between the above notes
            x.choose.value;

            // wait delta sec
            delta.yield;
        }
    });

    //~randomRou.play;
    //~randomRou.stop;



    /*************************************************************/
    /* GUI                                                       */
    /*************************************************************/
    Window.closeAll;

    w = Window.new(
        name: "Xylophone",
        resizable: true,
        border: true,
        server: s,
        scroll: false);

    w.alwaysOnTop = true; // doesn't work

    // Create buttons (bars) of xylophone
    b = Button(w, Rect(20, 20, 40, 190))
        .states_([["DO", Color.black, Color.fromHexString("#FF0000")]])
        .action_({Synth("xyl", [\freqs, #[150]]);});

    b = Button(w, Rect(60, 30, 40, 170))
        .states_([["RE", Color.black, Color.fromHexString("#FFC000")]])
        .action_({Synth("xyl", [\freqs, #[200]]);});

    b = Button(w, Rect(100, 40, 40, 150))
        .states_([["MI", Color.black, Color.fromHexString("#FFFF00")]])
        .action_({Synth("xyl", [\freqs, #[250]])});

    b = Button(w, Rect(140, 50, 40, 130))
        .states_([["FA", Color.black, Color.fromHexString("#92D050")]])
        .action_({Synth("xyl", [\freqs, #[300]])});

    b = Button(w, Rect(180, 60, 40, 110))
        .states_([["SOL", Color.black, Color.fromHexString("#00B050")]])
        .action_({Synth("xyl", [\freqs, #[350]])});

    b = Button(w, Rect(220, 70, 40, 90))
        .states_([["LA", Color.black, Color.fromHexString("#00B0F0")]])
        .action_({Synth("xyl", [\freqs, #[400]])});

    b = Button(w, Rect(260, 80, 40, 70))
        .states_([["SI", Color.black, Color.fromHexString("#0070C0")]])
        .action_({Synth("xyl", [\freqs, #[450]])});

    b = Button(w, Rect(300, 90, 40, 50))
        .states_([["DO", Color.black, Color.fromHexString("#7030A0")]])
        .action_({Synth("xyl", [\freqs, #[500]])});

    // Static text
    a = StaticText(w, Rect(20, 220, 85, 30));
    a.string = "# random notes:";

    // Textfield to insert number of notes random to play (how can I associate this value to rep in routine?)
    t = TextField(w, Rect(115, 220, 50, 30));
    t.value = rrand(1, 50);
    t.align = \right;

    ~buttonRandom = Button(w, Rect(180, 220, 80, 30))
        .states_([
            ["PLAY", Color.black, Color.gray(0.8)],
            ["PAUSE", Color.black, Color.gray(0.8)]
        ])
        .action_({arg flag;
            // doesn't work (sound doens't play)
            if(
                flag.value == 1,
                {~randomRou.play;}, {~randomRou.stop;}
            );
            ~numNotes = t.value; // take value from textfield
            ~numNotes.postln; // print it
        });

    w.front; // show window
    )

Thanks, I'm learning a lot.

[How can I post code with the correct format?]
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Xylophone

shiihs
Hi,

Let's go over some of the remaining problems...

1. When you make a sound by instantiating a synth, that synth normally keeps running in the background, even if it doesn't produce sound anymore. After starting many synths, this causes the server to overload.
For this reason, you should include some code to free the synth when it no longer produces sound. In many cases you'd do this from an envelope generator (EnvGen.ar(...)) where you can specify a parameter doneAction. If you set doneAction to value 2, it will cause the synth to free itself when it's done producing sound.

Your synth, however, doesn't use an envelope. In that case you can use a special UGen called "DetectSilence". In your synth, on the line just before Out.ar, insert the code "DetectSilence.ar(snd, doneAction:2);" What it means is that as soon as the DetectSilence finds out that the synth is not producing audible audio anymore, it will remove itself from the system.

2. The frequencies of the notes do not correspond to the notes you display on the colored bars. The mapping from frequency to pitch is not linear (you seem to assume that increases by 50Hz will increase frequency to that of the next note?). Instead of trying to calculate the correct frequencies manually, you can use something simpler called "midi note numbers". The a4 note, corresponding to 440Hz, has midi note number 69. To increase half a tone, you add 1 to the midi note number. Remember that between E and F and between B and the next C there's only half a tone, whereas between all other successive notes there's a whole tone (midi note number then increases by 2). To convert from midi note number to frequency, you can use the built in operator midicps: e.g. 69.midicps gives 440. Midi note numbers increase linearly, but frequencies increase exponentially.

3. If you want to play a melody you can use a routine if you like, but patterns typically will yield much shorter code for the same output. There's an extensive guide to patterns in supercollider's help files, but they can be a bit daunting to get started. Still I highly recommend getting patterns under your belt.

4. The routine you have defined works only once. The documentation for routine.stop explains why: ".stop
Equivalent to the Routine Function reaching its end or returning: after this, the Routine will never run again (the -next method has no effect and returns nil), unless -reset is called."

5. If you want to further simplify the GUI part, you will probably want to learn a bit more about control structures. Using control structures you can avoid a lot of the repetitive work in defining all the buttons.

Good luck!
Reply | Threaded
Open this post in threaded view
|  
Report Content as Inappropriate

Re: Xylophone

Andrea Valle
BTW, Stefania you have an Italian name.
In cause you are (or simply read) Italian, you might find useful this:



Best

-a-


--------------------------------------------------
Andrea Valle
--------------------------------------------------
CIRMA - StudiUm
Università degli Studi di Torino
--> http://www.cirma.unito.it/andrea/
--> http://www.fonurgia.unito.it/andrea/
--> http://www.flickr.com/photos/vanderaalle/sets/
--> http://vimeo.com/vanderaalle
--> [hidden email]
--------------------------------------------------

"This is a very complicated case, Maude. You know, a lotta ins, a lotta outs, a lotta what-have-yous." 
(Jeffrey 'The Dude' Lebowski)

On 15 Aug 2017, at 16:52, [hidden email] wrote:

Hi,

Let's go over some of the remaining problems...

1. When you make a sound by instantiating a synth, that synth normally keeps
running in the background, even if it doesn't produce sound anymore. After
starting many synths, this causes the server to overload.
For this reason, you should include some code to free the synth when it no
longer produces sound. In many cases you'd do this from an envelope
generator (EnvGen.ar(...)) where you can specify a parameter doneAction. If
you set doneAction to value 2, it will cause the synth to free itself when
it's done producing sound.

Your synth, however, doesn't use an envelope. In that case you can use a
special UGen called "DetectSilence". In your synth, on the line just before
Out.ar, insert the code "DetectSilence.ar(snd, doneAction:2);" What it means
is that as soon as the DetectSilence finds out that the synth is not
producing audible audio anymore, it will remove itself from the system.

2. The frequencies of the notes do not correspond to the notes you display
on the colored bars. The mapping from frequency to pitch is not linear (you
seem to assume that increases by 50Hz will increase frequency to that of the
next note?). Instead of trying to calculate the correct frequencies
manually, you can use something simpler called "midi note numbers". The a4
note, corresponding to 440Hz, has midi note number 69. To increase half a
tone, you add 1 to the midi note number. Remember that between E and F and
between B and the next C there's only half a tone, whereas between all other
successive notes there's a whole tone (midi note number then increases by
2). To convert from midi note number to frequency, you can use the built in
operator midicps: e.g. 69.midicps gives 440. Midi note numbers increase
linearly, but frequencies increase exponentially.

3. If you want to play a melody you can use a routine if you like, but
patterns typically will yield much shorter code for the same output. There's
an extensive guide to patterns in supercollider's help files, but they can
be a bit daunting to get started. Still I highly recommend getting patterns
under your belt.

4. The routine you have defined works only once. The documentation for
routine.stop explains why: ".stop
Equivalent to the Routine Function reaching its end or returning: after
this, the Routine will never run again (the -next method has no effect and
returns nil), unless -reset is called."

5. If you want to further simplify the GUI part, you will probably want to
learn a bit more about control structures. Using control structures you can
avoid a lot of the repetitive work in defining all the buttons.

Good luck!




--
View this message in context: http://new-supercollider-mailing-lists-forums-use-these.2681727.n2.nabble.com/Xylophone-tp7634499p7634609.html
Sent from the SuperCollider Users New (Use this!!!!) mailing list archive at Nabble.com.

_______________________________________________
sc-users mailing list

info (subscription, etc.): http://www.birmingham.ac.uk/facilities/ea-studios/research/supercollider/mailinglist.aspx
archive: http://www.listarc.bham.ac.uk/marchives/sc-users/
search: http://www.listarc.bham.ac.uk/lists/sc-users/search/

Loading...