// Guitar Hero Game played with a real guitar! // Copyright 2008 Les Hall // This software is protected by the GNU General Public License // parameters 1200 => int screen_width; // for positioning windows 900 => int screen_height; // for positioning windows 1.0 => float size; // for sizing things 2.0 => float max_size; // max scaling to perform 100 => int button_width; // default width of buttons 60 => int button_height; // default height of buttons 200 => int slider_width; // default width of sliders 60 => int slider_height; // default height of sliders 80 => int fret_button_width; // default width of fret buttons 66 => int fret_button_height; // default height of fret buttons 60 => int fret_button_x; // for positioning buttons 30 => int fret_button_y; // for positioning buttons 36 => int led_width; // default width of LEDs 36 => int led_height; // default height of LEDs 20 => int window_frame_width; // width for window frame 50 => int window_frame_height; // height for window frame 6 => int num_strings; // number of strings on guitar 6 => int max_num_strings; // maximum number of strings on guitar 12 => int num_frets; // number of frets to work with 12 => int min_num_frets; // minimum number of frets on fretboard 25 => int max_num_frets; // maximum number of frets on fretboard 0 => int min_level; // minimum level number 2 => int max_level; // maximum level number 1 => int song_pace; // playback pace of the song // variables int player_score; // current score acheived by the player int player_level; // current level acheived by the player Event level_change; // fires when level has changed int exit; // one to exit program // instantiate the classes introduction Intro; scoreboard ScoreBoard; fretboard FretBoard; bullseye BullsEye; matchmeter MatchMeter; gameover GameOver; playgame PlayGame; // start first level PlayGame.play (); // if game ended due to poor performance, show end screen if (exit == 0) { GameOver.endscreen (); } // cleanup MatchMeter.close (); BullsEye.close (); FretBoard.close (); ScoreBoard.close (); // class for open strings level class playgame { // parameters 0.33 => float chord_probability; // probability of generating a chord int string_number; // 0 thru 5, 5 is high E string, 0 is low E string 0.05 => float noise_threshold; // average signal must be this large or larger 8.0 => float signal_threshold; // notes must have this signal or higher [82.407, 87.31, 92.50, 98.00, 103.83, 110.00, 116.54, 123.47, 130.81, 138.59, 146.83, 155.56, 164.81, 174.61, 185.00, 196.00, 207.65, 220.00, 233.08, 246.94, 261.63, 277.18, 293.67, 311.13, 329.63, 349.23, 369.99, 392.00, 415.30, 440.00, 466.16, 493.88, 523.25, 554.37, 587.33, 622.25, 659.26, 698.46, 783.99, 830.61, 880.00, 932.33, 987.77, 1046.50, 1108.73, 1174.66, 1244.51, 1318.51, 1396.91, 1479.98] @=> float string_frequencies[]; [0, 5, 10, 15, 19, 24] @=> int string_indices[]; // starting index of each string float string_table[num_strings][num_frets]; // the look-up table int time_delay; // time delay for this try // chords // note: all chords must have at least three tested strings, there are no two-string chords 5 => int max_num_chords; // number of available chords [ [0, 2, 2, 1, 0, 0], [0, 0, 2, 2, 2, 0], [-1, 0, 0, 2, 3, 2], [3, 2, 0, 0, 0, 3], [3, 3, 2, 0, 1, 0] ] @=> int chords[][]; // the chord definitions ["E Major", "A Major", "D Major", "G Major", "C Major"] @=> string chord_names[]; // the names of the chords // songs // Back in Black by AC/DC 67 => int num_chords_back_in_black; // number of chords in below song [[0, 2, 2, -1, -1, -1], [-1, -1, 0, 2, 3, -1], [-1, -1, 0, 2, 3, -1], [-1, -1, 0, 2, 3, -1], [-1, 0, 2, 2, -1, -1], [-1, 0, 2, 2, -1, -1], [-1, 0, 2, 2, -1, -1], [-1, -1, -1, -1, -1, 3], [-1, -1, -1, -1, -1, 0], [-1, -1, -1, -1, 3, -1], [-1, -1, -1, -1, 0, -1], [-1, -1, -1, 2, -1, -1], [-1, -1, -1, 4, -1, -1], [-1, -1, -1, 2, -1, -1], [-1, -1, -1, 0, -1, -1], [-1, 0, 2, 2, -1, -1], [0, 2, 2, -1, -1, -1], [-1, 2, 4, 4, -1, -1], [-1, 0, 2, 2, -1, -1], [-1, 2, 4, 4, -1, -1], [-1, 0, 2, 2, -1, -1], [0, 2, 2, -1, -1, -1], [-1, 2, 4, 4, -1, -1], [-1, 0, 2, 2, -1, -1], [-1, 2, 4, 4, -1, -1], [3, 2, 0, 0, 3, 3], [-1, -1, 0, 2, 3, 2], [-1, 0, 2, 2, -1, -1], [3, 2, 0, 0, -1, -1], [-1, 0, 2, 2, -1, -1], [3, 2, 0, 0, 3, 3], [-1, -1, 0, 2, 3, 2], [-1, 0, 2, 2, -1, -1], [3, 2, 0, 0, -1, -1], [-1, 0, 2, 2, -1, -1], [0, -1, -1, -1, -1, -1], [-1, 5, -1, -1, -1, -1], [-1, 4, -1, -1, -1, -1], [-1, 2, -1, -1, -1, -1], [-1, 4, -1, -1, -1, -1], [3, -1, -1, -1, -1, -1], [-1, 2, -1, -1, -1, -1], [5, -1, -1, -1, -1, -1], [-1, 2, -1, -1, -1, -1], [6, -1, -1, -1, -1, -1], [-1, 2, -1, -1, -1, -1], [7, -1, -1, -1, -1, -1], [0, -1, -1, -1, -1, -1], [-1, 5, -1, -1, -1, -1], [-1, 4, -1, -1, -1, -1], [-1, 2, -1, -1, -1, -1], [-1, 4, -1, -1, -1, -1], [3, -1, -1, -1, -1, -1], [-1, 2, -1, -1, -1, -1], [5, -1, -1, -1, -1, -1], [-1, 2, -1, -1, -1, -1], [6, -1, -1, -1, -1, -1], [-1, 2, -1, -1, -1, -1], [7, -1, -1, -1, -1, -1], [-1, 0, -1, -1, -1, -1], [-1, -1, 5, -1, -1, -1], [-1, -1, 4, -1, -1, -1], [-1, -1, 2, -1, -1, -1], [-1, -1, 4, -1, -1, -1], [-1, 3, -1, -1, -1, -1], [-1, -1, 2, -1, -1, -1], [-1, 5, -1, -1, -1, -1], [-1, -1, 2, -1, -1, -1], [-1, 6, -1, -1, -1, -1], [-1, -1, 2, -1, -1, -1], [-1, 7, -1, -1, -1, -1]] @=> int song_back_in_black[][]; // variables float frequency; // the observed frequency float percent_error; // the error of the frequency int result; // 1 if correct note was struck int str; // indexes strings int fret; // indexes frets int frets[max_num_strings]; // indexes the frets int sorted_strs[max_num_strings]; // sorted by testing order int num_tries; // number of tries so far int num_successes; // number of successes so far int chord_selection; // indexes which chord was selected int num_frets_in_chord; // for setting detection threshold int num_chord_of_song; // indexes the song chords int max_num_chords_in_song; // maximum number of chords in song int led_color; // color of LED (0 = green, 1 = red) float led_radius; // radius of LED (0 to 1) float led_theta; // theta of LED (0 to 1) float match_ratio; // >= 1 if a match is made int song_plays; // number of times song has been played // the patch // comb filters and magnitude checkers Gain input[max_num_strings]; Gain sum[max_num_strings]; DelayA K[max_num_strings]; Gain alpha[max_num_strings]; FullRect fwr[max_num_strings]; LPF lpf[max_num_strings]; Gain adder; for (0 => int str; str < max_num_strings; str++) { // feedback comb filter - picks off desired harmonics adc => input[str] => sum[str] => K[str] => alpha[str] => sum[str]; // magnitude checker sum[str] => fwr[str] => lpf[str] => adder; } adder => Gain divider => LPF output => blackhole; // reference signal magnitude adc => FullRect fwrx => LPF lpfx => LPF lpfy => divider; // success or failure buzzers SinOsc sinosc => dac; SqrOsc sqrosc => dac; // the patch parameters for (0 => int str; str < max_num_strings; str++) { // comb filters second / 20.0 => K[str].max; second / string_frequencies[0] - samp => K[str].delay; 0.9 => alpha[str].gain; // magnitude checker 33 => lpf[str].freq; } 33 => lpfx.freq; 10 => lpfy.freq; 4 => divider.op; 20 => output.freq; // success or failure buzzers 880 => sinosc.freq; 110 => sqrosc.freq; 0 => sinosc.gain => sqrosc.gain; 5 => adc.gain; // gain for line-in jack // build the string table for (0 => int str; str < num_strings; str++) { for (0 => int fret; fret < num_frets; fret++) { string_frequencies[string_indices[str]+fret] => string_table[str][fret]; } } // the play function fun void play () { while (true) { // decide if this is a chord or a note or a song if (player_level <= 1) { if ( (Std.rand2f (0, 1) < chord_probability) & (player_level > 0) ) { // choose chord Std.rand2 (0, max_num_chords - 1) => chord_selection; // fill frets with chord for (0 => int str; str < num_strings; str++) { chords[chord_selection][str] => frets[str]; } } else { // clear chord selection variable -1 => chord_selection; // clear fret indices for (0 => int str; str < num_strings; str++) { -1 => frets[str]; } // choose note Std.rand2 (0, num_strings - 1) => str; Std.rand2 (0, num_frets - 1) => fret; if (player_level == 0) { 0 => fret; } // fill frets with note fret => frets[str]; } } else { // fill frets with chord for (0 => int str; str < num_strings; str++) { // if song is back in black if (player_level == 2) { song_back_in_black[num_chord_of_song][str] => frets[str]; } num_chords_back_in_black => max_num_chords_in_song; } } // enable comb filters that have notes to detect 0 => num_frets_in_chord; for (0 => int str; str < num_strings; str++) { if (frets[str] >= 0) { 1 => input[str].gain; 1 +=> num_frets_in_chord; } else { 0 => input[str].gain; } } // set the comb filter delay times for (0 => int str; str < num_strings; str++) { if (frets[str] >= 0) { second / string_table[str][frets[str]] => K[str].delay; } else { second / string_table[0][0] => K[str].delay; } } // light up the button(s) for (0 => int str; str < num_strings; str++) { if (frets[str] >= 0) { FretBoard.push_button_fret (str, frets[str], 1); } } if (chord_selection >= 0) { FretBoard.chord_name (chord_names[chord_selection] + " Chord"); } if (player_level == 2) { FretBoard.chord_name ("Back in Black by AC/DC, chord " + num_chord_of_song); } // calculate time delay for this try if (player_level <= 1) { (1000 * Math.exp (-num_tries / 50) ) $ int => time_delay; } else { 1000 => time_delay; } // allow filters to settle for (0 => int t; t < 20; t++) { // determine the match ratio output.last () / (signal_threshold * num_frets_in_chord) => match_ratio; // light up match meter MatchMeter.draw (match_ratio); // break on exit if (exit == 1) { break; } 10::ms => now; } // shut off beeping 0.00 => sinosc.gain; 0.00 => sqrosc.gain; // time loop 0 => result; for (0 => int t; t < time_delay; t++) { // determine the match ratio output.last () / (signal_threshold * num_frets_in_chord) => match_ratio; // light up match meter MatchMeter.draw (match_ratio); // check to see if the correct signal is detected and signal is above noise level if ( (match_ratio >= 1.0) & (lpfx.last () > noise_threshold) & (lpfx.last () > lpfy.last () ) ) { 1 +=> result; } // remember LED theta value if (match_ratio > 0) { 1.0 / match_ratio => led_theta; } else { 0 => led_theta; } // look for a few correct detections if (result >= 3) { (t $ float) / (time_delay $ float) => led_radius; break; } else { 1 => led_radius; } // break on exit if (exit == 1) { break; } // delay a bit 10::ms => now; } // remember how many tries have been made 1 +=> num_tries; // unlight the button for (0 => int str; str < num_strings; str++) { if (frets[str] >= 0) { FretBoard.push_button_fret (str, frets[str], 0); } } FretBoard.chord_name (""); // adjust score if (result >= 1) { num_frets_in_chord +=> player_score; 0.5 => sinosc.gain; 1 +=> num_successes; 0 => led_color; } else { 0.5 => sqrosc.gain; 1 => led_color; } ScoreBoard.update_player_score (); // draw bullseye BullsEye.new_led (led_radius, led_theta, led_color); BullsEye.draw (); // adjust player level according to score if ( (player_score > 10) & (player_level == 0) ) { 1 => player_level; } // increment song chord index if (player_level > 1) { 1 +=> num_chord_of_song; if (num_chord_of_song == max_num_chords_in_song) { 1 +=> song_plays; } max_num_chords_in_song %=> num_chord_of_song; } // break when game is over if ( (BullsEye.get_success_percentage () <= 33) | (song_plays == 1) ) { // shut off beeping 0.00 => sinosc.gain; 0.00 => sqrosc.gain; break; } // break on exit if (exit == 1) { // shut off beeping 0.00 => sinosc.gain; 0.00 => sqrosc.gain; break; } } } } // class for displaying exit message class gameover { // display end screen fun void endscreen () { // the endscreen window MAUI_View endscreen_view; endscreen_view.size (2 * button_width * size, 2 * button_height * size); endscreen_view.position (screen_width / 2, screen_height / 2); endscreen_view.name ("Game Over"); endscreen_view.display (); // The score button MAUI_Button button_score; button_score.pushType (); button_score.size (2 * button_width * size, button_height * size); button_score.position (0, 0); button_score.name ("Score: " + player_score); endscreen_view.addElement (button_score); // The exit button MAUI_Button button_exit; button_exit.pushType (); button_exit.size (2 * button_width * size, button_height * size); button_exit.position (0, button_height * size); button_exit.name ("Exit"); endscreen_view.addElement (button_exit); // wait for button press, then exit button_exit.onChange () => now; // destroy the intro window endscreen_view.destroy (); } } // draw an LED meter showing the match ratio class matchmeter { // parameters 10 => int num_leds; // number of LED's to draw (must be an even number) (led_width * size) $ int => int view_width; // width of window (num_leds * led_height * size) $ int => int view_height; // height of window // the bullseye window MAUI_View matchmeter_view; matchmeter_view.size (view_width, view_height); matchmeter_view.position ( (3 * button_width + 2 * led_width + num_strings * fret_button_x + fret_button_width) * size + 2 * window_frame_width, screen_height - view_height); matchmeter_view.name (""); matchmeter_view.display (); // draw the LEDs unlit MAUI_LED led[num_leds]; for (0 => int i; i < num_leds; i++) { led[i].size (led_width, led_height); led[i].position (0, (num_leds - 1 - i) * led_height * size); if (i < num_leds / 2) { led[i].color (led[i].red); } else { led[i].color (led[i].green); } led[i].unlight (); matchmeter_view.addElement (led[i]); } // light the meter fun void draw (float match_ratio) { for (0 => int i; i < num_leds; i++) { if ( (i $ float) / ( (num_leds $ float) / 2.0) < match_ratio) { led[i].light (); } else { led[i].unlight (); } } } // function to destroy the bullseye window fun void close () { matchmeter_view.destroy (); } } // class for displaying the bullseye class bullseye { // parameters 10 => int num_leds; // number of LED's to draw (3 * button_width * size) $ int => int view_width; // width of window view_width => int view_height; // height of window (view_width - led_width * size) $ int => int diameter; // diameter of target diameter / 2 => int center_x; // x position of center diameter / 2 => int center_y; // y position of center // variables float radius[num_leds]; // radius of LED's float theta[num_leds]; // angle of LED's int color[num_leds]; // LED colors, 0 = green, 1 = red int index; // indexes the LEDs // the bullseye window MAUI_View bullseye_view; bullseye_view.size (view_width, view_height); bullseye_view.position (0, screen_height - 4 * button_height * size - window_frame_height - view_height); bullseye_view.name ("Guitar Zero Bullseye"); bullseye_view.display (); // draw the LEDs in their initial positions MAUI_LED led[num_leds]; for (0 => int i; i < num_leds; i++) { led[i].size (led_width, led_height); led[i].position (center_x, center_y); led[i].color (led[i].green); led[i].light (); bullseye_view.addElement (led[i]); } // overwrite the last LED with new position and color // r and t should be normalized from zero to one // c should be 0 for green and 1 for red fun void new_led (float r, float t, int c) { (diameter / 2.0) * r => radius[index]; (2.0 * pi) * t => theta[index]; c => color[index]; // increment LED index with modulus (index + 1) % num_leds => index; } // redraw the LEDs on the bullseye fun void draw () { // loop thru all the LEDs for (0 => int i; i < num_leds; i++) { // update LED position led[i].position (center_x + radius[i] * Math.cos(theta[i]), center_y + radius[i] * Math.sin(theta[i]) ); // update LED color if (color[i] == 0) { led[i].color (led[i].green); } else { led[i].color (led[i].red); } // light up LED led[i].light (); // redraw the LED bullseye_view.addElement (led[i]); } } // get the percentage of LEDs that are green fun float get_success_percentage () { float temp; // clear temp 0 => temp; // loop thru all the LEDs for (0 => int i; i < num_leds; i++) { // add up the green ones if (color[i] == 0) { 1 +=> temp; } } // calculate success percentage 100.0 * (temp / (num_leds $ float) ) => temp; return temp; } // function to destroy the bullseye window fun void close () { bullseye_view.destroy (); } } // class for handling fretboard class fretboard { // variables int note; // indexes the note of this string int led_counter; // counts the LEDs int window_width; // width of window int window_height; // height of window // the fretboard window MAUI_View fret_view; (2 * led_width + (num_strings + 1) * fret_button_x * size) $ int => window_width; ( (num_frets + 1) * fret_button_y * size + button_height * size) $ int => window_height; fret_view.size (window_width, window_height); fret_view.position (3 * button_width * size + window_frame_width, screen_height - window_height); fret_view.name ("Guitar Zero FretBoard"); fret_view.display (); // draw the fret buttons MAUI_Button button_fret[num_strings][num_frets]; for (0 => int str; str < num_strings; str++) { for (0 => int fret; fret < num_frets; fret++) { button_fret[str][fret].toggleType (); button_fret[str][fret].size (fret_button_width * size, fret_button_height * size); button_fret[str][fret].position (2 * led_width * size + str * fret_button_x * size, fret * fret_button_y * size); button_fret[str][fret].name (note_name (str, fret) ); fret_view.addElement (button_fret[str][fret]); } } // draw the fret name button MAUI_Button button_fret_name; button_fret_name.toggleType (); button_fret_name.size (window_width, button_height * size); button_fret_name.position (0, (num_frets + 1) * fret_button_y * size); button_fret_name.name (""); fret_view.addElement (button_fret_name); // draw the fret markers MAUI_LED led_marker[12]; 0 => led_counter; for (0 => int fret; fret < num_frets; fret++) { if ( ( (fret % 12) == 3) | ( (fret % 12) == 5) | ( (fret % 12) == 7) | ( (fret % 12) == 9) ) { led_marker[led_counter].size (led_width, led_height); led_marker[led_counter].position (led_width / 2, led_height / 2 + fret * fret_button_y * size); led_marker[led_counter].color (led_marker[led_counter].blue); led_marker[led_counter].light (); fret_view.addElement (led_marker[led_counter]); led_counter++; } if ( ( (fret % 12) == 0) & (fret > 0) ) { led_marker[led_counter].size (led_width, led_height); led_marker[led_counter].position (0, led_height / 2 + fret * fret_button_y * size); led_marker[led_counter].color (led_marker[led_counter].blue); led_marker[led_counter].light (); fret_view.addElement (led_marker[led_counter]); led_counter++; led_marker[led_counter].size (led_width, led_height); led_marker[led_counter].position (led_width, led_height / 2 + fret * fret_button_y * size); led_marker[led_counter].color (led_marker[led_counter].blue); led_marker[led_counter].light (); fret_view.addElement (led_marker[led_counter]); led_counter++; } } // function to determine name of each fret position fun string note_name (int str, int fret) { // A A#Bb B C C#Db D D#Eb E F F#Gb G G#Ab A... // 0 1 2 3 4 5 6 7 8 9 10 11 0 // first find the note of the open string 7 => note; // default is E string if ( (str % 6) == 0) { 7 => note; } if ( (str % 6) == 1) { 0 => note; } if ( (str % 6) == 2) { 5 => note; } if ( (str % 6) == 3) { 10 => note; } if ( (str % 6) == 4) { 2 => note; } if ( (str % 6) == 5) { 7 => note; } // then add in the fret number fret +=> note; // and return the appropriate string if ( (note % 12) == 0) { return "A"; } if ( (note % 12) == 1) { return "A#Bb"; } if ( (note % 12) == 2) { return "B"; } if ( (note % 12) == 3) { return "C"; } if ( (note % 12) == 4) { return "C#Db"; } if ( (note % 12) == 5) { return "D"; } if ( (note % 12) == 6) { return "D#Eb"; } if ( (note % 12) == 7) { return "E"; } if ( (note % 12) == 8) { return "F"; } if ( (note % 12) == 9) { return "F#Gb"; } if ( (note % 12) == 10) { return "G"; } if ( (note % 12) == 11) { return "G#Ab"; } } // push a toggle button fun void push_button_fret (int str, int fret, int button_state) { button_fret[str][fret].state (button_state); } // name the chord fun void chord_name (string name) { button_fret_name.name (name); } // function to destroy the fretboard window fun void close () { fret_view.destroy (); } } // class for handling scoreboard class scoreboard { // the scoreboard window MAUI_View score_view; score_view.size (3 * button_width * size, 4 * button_height * size); score_view.position (0, screen_height - 4 * button_height * size); score_view.name ("Guitar Zero ScoreBoard"); score_view.display (); // The exit button MAUI_Button button_exit; button_exit.pushType (); button_exit.size (button_width * size, button_height * size); button_exit.position (0, 0); button_exit.name ("Exit"); score_view.addElement (button_exit); // The score output button MAUI_Button button_score; button_score.pushType (); button_score.size (2 * button_width * size, button_height * size); button_score.position (button_width * size, 0); button_score.name ("" + player_score); score_view.addElement (button_score); // The credits button MAUI_Button button_credits; button_credits.pushType (); button_credits.size (3 * button_width * size, button_height * size); button_credits.position (0, button_height * size); button_credits.name ("Copyright 2008 Les Hall"); score_view.addElement (button_credits); // The email button MAUI_Button button_email; button_email.pushType (); button_email.size (3 * button_width * size, button_height * size); button_email.position (0, 2 * button_height * size); button_email.name ("inventor-66@comcast.net"); score_view.addElement (button_email); // The url button MAUI_Button button_url; button_url.pushType (); button_url.size (3 * button_width * size, button_height * size); button_url.position (0, 3 * button_height * size); button_url.name ("http://www.freedomodds.com/music/"); score_view.addElement (button_url); // watch the exit button spork ~ exit_button_watcher (); fun void exit_button_watcher () { while (true) { button_exit.onChange () => now; if (!button_exit.state ()) { 1 => exit; } } } // update the score fun void update_player_score () { button_score.name ("" + player_score); } // function to destroy the scoreboard window fun void close () { score_view.destroy (); } } // class for handling introductory window class introduction { // variables int game_type_shred_id; // id of button watcher shred // the intro window MAUI_View intro_view; intro_view.size (slider_width * size, (2 * slider_height + 2 * button_height) * size); intro_view.position (screen_width / 2, screen_height / 2); intro_view.name ("Settings"); // slider to set the size MAUI_Slider slider_size; slider_size.range (1.0, max_size); slider_size.value (size); slider_size.size (slider_width * size, slider_height * size); slider_size.position (0, 0); slider_size.name ("Screen Size Multiplier"); intro_view.addElement (slider_size); // slider to set the number of frets MAUI_Slider slider_num_frets; slider_num_frets.range (min_num_frets, max_num_frets); slider_num_frets.value (num_frets); slider_num_frets.size (slider_width * size, slider_height * size); slider_num_frets.position (0, slider_height * size); slider_num_frets.displayFormat (slider_num_frets.integerFormat); slider_num_frets.name ("Number of Frets"); intro_view.addElement (slider_num_frets); // button to select game type MAUI_Button button_game_type; button_game_type.pushType (); button_game_type.size (2 * button_width * size, button_height * size); button_game_type.position (0, 2 * slider_height * size); button_game_type.name ("Random"); intro_view.addElement (button_game_type); // button to start the program MAUI_Button button_initialize; button_initialize.pushType (); button_initialize.size (2 * button_width * size, button_height * size); button_initialize.position (0, (2 * slider_height + button_height) * size); button_initialize.name ("Play Now"); intro_view.addElement (button_initialize); // display the intro window intro_view.display (); // shred to watch game type button spork ~ game_type_button_watcher (); fun void game_type_button_watcher () { int state_of_button; // 0 for random, > 0 for songs me.id () => game_type_shred_id; while (true) { button_game_type.onChange () => now; if (!button_game_type.state ()) { (state_of_button + 1) % 2 => state_of_button; if (state_of_button == 0) { 0 => player_level; button_game_type.name ("Random"); } if (state_of_button == 1) { 2 => player_level; button_game_type.name ("Back in Black"); } } } } // wait for button press, then save parameters and remove button shred button_initialize.onChange () => now; slider_size.value () => size; slider_num_frets.value () $ int + 1 => num_frets; Machine.remove (game_type_shred_id); // destroy the intro window intro_view.destroy (); }