// MUSIC-251 Final Project - Jingjie Zhang // experiment.ck - Impulse, Record, Survey & Archive //============================================================================== // Read the subject counter file FileIO subject_counter; subject_counter.open(me.dir() + "0_subject_counter.txt", FileIO.READ); if(!subject_counter.good()) { <<< "Cannot get current subject number..." >>>; me.exit(); } // Get the current subject number subject_counter => int subject_num; ++subject_num; subject_counter.close(); //============================================================================== // Create the data file FileIO fout; fout.open(me.dir() + "subject" + subject_num + ".csv", FileIO.WRITE); if(!fout.good()) { <<< "Cannot create data file..." >>>; me.exit(); } //============================================================================== // BPM Array [60, 90, 120, 150] @=> int BPM[]; // Choose the order [[0, 1, 2, 3, 0], [0, 1, 3, 2, 0], [0, 2, 1, 3, 0], [0, 2, 3, 1, 0], [0, 3, 1, 2, 0], [0, 3, 2, 1, 0], [1, 0, 2, 3, 1], [1, 0, 3, 2, 1], [1, 2, 0, 3, 1], [1, 2, 3, 0, 1], [1, 3, 0, 2, 1], [1, 3, 2, 0, 1], [2, 0, 1, 3, 2], [2, 0, 3, 1, 2], [2, 1, 0, 3, 2], [2, 1, 3, 0, 2], [2, 3, 0, 1, 2], [2, 3, 1, 0, 2], [3, 0, 1, 2, 3], [3, 0, 2, 1, 3], [3, 1, 0, 2, 3], [3, 1, 2, 0, 3], [3, 2, 0, 1, 3], [3, 2, 1, 0, 3]] @=> int order[][]; Math.random2(0, 23) => int row; // Metronome Pulse SinOsc sine => ADSR env => dac; 1100 => sine.freq; env.set(.01::ms, 8::ms, .707, 30::ms); 4::ms => dur pulse_width; // BPM Setting dur period; dur interval; fun void setBPM(int BPM) { (60 * 1000.0 / BPM)::ms => period; period - pulse_width => interval; } // Test Duration Setting dur test_duration; fun void switchTestDuration(int BPM) { if (BPM == 60) 70::second => test_duration; if (BPM == 90) 50::second => test_duration; if (BPM == 120) 40::second => test_duration; if (BPM == 150) 30::second => test_duration; } // Pulse Train Generators fun void generatePulseTrain(int count) { for(0 => int i; i < count; ++i) { env.keyOn(); pulse_width => now; env.keyOff(); interval => now; } } fun void generateInitialPulseTrain() { generatePulseTrain(16); } fun void generateHighPitchPulse() { 2200 => sine.freq; generatePulseTrain(1); 1100 => sine.freq; } fun void generateLaterPulseTrain() { generateHighPitchPulse(); generatePulseTrain(15); } //============================================================================== // Create MIDI event MidiIn min; MidiMsg midi_msg; // Select MIDI device 0 => int midi_device; if(!min.open(midi_device)) { <<<"Cannot open MIDI device!">>>; me.exit(); } else { // Print out the MIDI device that was opened <<< "MIDI device:", min.num(), " -> ", min.name() >>>; } // Time Stamps time start_time; float current_time; //float last_time; // MIDI Pad Listener fun int detectMIDIPad() { // Wait on the event min => now; // Get all the message(s) in the MIDI buffer while (min.recv(midi_msg)) { // MIDI Status Byte == 144 (Decimal) == 90 (Hex) // 90 (Hex): 9 -> MIDI Note On, 0 -> Channel 0 if (midi_msg.data1 == 144) { // Pad's MIDI Note Number == 47 if (midi_msg.data2 == 47) { // Pad is pressed (now - start_time) / 1::second => current_time; <<<"Beat detected!">>>; //current_time - last_time => float delta_time; //current_time => last_time; //<<<"Current Time: ", current_time, ", Time Diffence: ", delta_time>>>; fout <= current_time <= ", "; return 1; } } return 0; } } // MIDI Message Buffer Cleaner fun void clearMIDIBuffer() { // Wait on the event min => now; // Get all the message(s) in the MIDI buffer while (min.recv(midi_msg)) { // Do nothing } } // MIDI Pad Recorder fun void recordMIDIPad() { clearMIDIBuffer(); now => start_time; //0 => last_time; while (1) { detectMIDIPad(); if (now - start_time >= test_duration) break; } } //============================================================================== // Create keyboard event Hid hi; HidMsg kb_msg; // Select keyboard 0 => int keyboard; if (!hi.openKeyboard(keyboard)) { <<<"Cannot open keyboard!">>>; me.exit(); } // Keyboard ASCII Listener fun int readASCIIFromKeyboard() { hi => now; while(hi.recv(kb_msg)) { if (kb_msg.isButtonDown()) return kb_msg.ascii; } } //============================================================================== // Initial State 1 => int state; 0 => int state_completed; // Multiple Choice Question 0 => int temp_answer; // Temporary Answer Variable fun void ABCDQuestion(int target_state) { readASCIIFromKeyboard() => int ASCII; if (ASCII == 49) { 1 => temp_answer; target_state => state; 1 => state_completed; } else if (ASCII == 50) { 2 => temp_answer; target_state => state; 1 => state_completed; } else if (ASCII == 51) { 3 => temp_answer; target_state => state; 1 => state_completed; } else if (ASCII == 52) { 4 => temp_answer; target_state => state; 1 => state_completed; } } // Yes/No Question fun void YesOrNo(int state1, int state2) { readASCIIFromKeyboard() => int ASCII; if (ASCII == 89 || ASCII == 10) { state1 => state; 1 => state_completed; } else if (ASCII == 78) { state2 => state; 1 => state_completed; } } //============================================================================== // State Machine fun void stateMachine() { if (state == 1) { <<<"==========================================================">>>; <<<"==================== Experiment Begin ====================">>>; <<<"==========================================================">>>; <<<"Welcome to my experiment! You are Subject No." + subject_num>>>; <<<"In this experiment, you will hear five different tempos. Each tempo will be played for a few seconds. Try to track the tempo through tapping and persist after it stops.">>>; <<<"The recording will start (and stop) after a higher-pitched pulse. You are allowed to create any context out of each tempo to help you persist them, otherwise it will be pretty boring! :)">>>; 21 => state; } else if (state == 21) { <<<"==========================================================">>>; <<<"============== Press ENTER to Start Tempo 1 ==============">>>; <<<"==========================================================">>>; 0 => state_completed; while (!state_completed) { readASCIIFromKeyboard() => int ASCII; if (ASCII == 10) { <<<"Tempo 1">>>; BPM[order[row][0]] => int bpm; fout <= 1 <= ", " <= bpm <= ", "; setBPM(bpm); switchTestDuration(bpm); fout <= test_duration / 1::second <= ", "; generateInitialPulseTrain(); <<<"Start Recording...">>>; spork ~generateLaterPulseTrain(); recordMIDIPad(); fout <= "\n"; generateHighPitchPulse(); 22 => state; 1 => state_completed; } } } else if (state == 22) { <<<"==========================================================">>>; <<<"============== Press ENTER to Start Tempo 2 ==============">>>; <<<"==========================================================">>>; 0 => state_completed; while (!state_completed) { readASCIIFromKeyboard() => int ASCII; if (ASCII == 10) { <<<"Tempo 2">>>; BPM[order[row][1]] => int bpm; fout <= 2 <= ", " <= bpm <= ", "; setBPM(bpm); switchTestDuration(bpm); fout <= test_duration / 1::second <= ", "; generateInitialPulseTrain(); <<<"Start Recording...">>>; spork ~generateLaterPulseTrain(); recordMIDIPad(); fout <= "\n"; generateHighPitchPulse(); 23 => state; 1 => state_completed; } } } else if (state == 23) { <<<"==========================================================">>>; <<<"============== Press ENTER to Start Tempo 3 ==============">>>; <<<"==========================================================">>>; 0 => state_completed; while (!state_completed) { readASCIIFromKeyboard() => int ASCII; if (ASCII == 10) { <<<"Tempo 3">>>; BPM[order[row][2]] => int bpm; fout <= 3 <= ", " <= bpm <= ", "; setBPM(bpm); switchTestDuration(bpm); fout <= test_duration / 1::second <= ", "; generateInitialPulseTrain(); <<<"Start Recording...">>>; spork ~generateLaterPulseTrain(); recordMIDIPad(); fout <= "\n"; generateHighPitchPulse(); 24 => state; 1 => state_completed; } } } else if (state == 24) { <<<"==========================================================">>>; <<<"============== Press ENTER to Start Tempo 4 ==============">>>; <<<"==========================================================">>>; 0 => state_completed; while (!state_completed) { readASCIIFromKeyboard() => int ASCII; if (ASCII == 10) { <<<"Tempo 4">>>; BPM[order[row][3]] => int bpm; fout <= 4 <= ", " <= bpm <= ", "; setBPM(bpm); switchTestDuration(bpm); fout <= test_duration / 1::second <= ", "; generateInitialPulseTrain(); <<<"Start Recording...">>>; spork ~generateLaterPulseTrain(); recordMIDIPad(); fout <= "\n"; generateHighPitchPulse(); 25 => state; 1 => state_completed; } } } else if (state == 25) { <<<"==========================================================">>>; <<<"============== Press ENTER to Start Tempo 5 ==============">>>; <<<"==========================================================">>>; 0 => state_completed; while (!state_completed) { readASCIIFromKeyboard() => int ASCII; if (ASCII == 10) { <<<"Tempo 5">>>; BPM[order[row][4]] => int bpm; fout <= 5 <= ", " <= bpm <= ", "; setBPM(bpm); switchTestDuration(bpm); fout <= test_duration / 1::second <= ", "; generateInitialPulseTrain(); <<<"Start Recording...">>>; spork ~generateLaterPulseTrain(); recordMIDIPad(); fout <= "\n"; generateHighPitchPulse(); <<<"==========================================================">>>; <<<"====================== Result Saved ======================">>>; <<<"==========================================================">>>; 3 => state; 1 => state_completed; } } } else if (state == 3) { <<<"Now please answer a few survey questions:">>>; <<<"A. Which of the following best describes your musical training background?">>>; <<<"(1) Never (2) <1 year (3) 1-3 years (4) >3 years">>>; 0 => state_completed; while (!state_completed) ABCDQuestion(31); } else if (state == 31) { <<<"You answer is \"(" + temp_answer + ")\". Please confirm... (Y/N)">>>; 0 => state_completed; while (!state_completed) YesOrNo(4, 3); } else if (state == 4) { fout <= temp_answer <= "\n"; <<<"Confirmed!">>>; <<<"B. How much time do you spend practicing (or listening to music) everyday?">>>; <<<"(1) <0.5 hour (2) 0.5-1 hour (3) 1-3 hours (4) >3 hours">>>; 0 => state_completed; while (!state_completed) ABCDQuestion(41); } else if (state == 41) { <<<"You answer is \"(" + temp_answer + ")\". Please confirm... (Y/N)">>>; 0 => state_completed; while (!state_completed) YesOrNo(5, 4); } else if (state == 5) { fout <= temp_answer <= "\n"; <<<"Confirmed!">>>; <<<"C. Which of the following best describes your experience in bands (or musical groups)?">>>; <<<"(1) Never (2) <6 months">>>; <<<"(3) >6 months (You are the one who follows the tempo)">>>; <<<"(4) >6 months (You are the one who controls the tempo)">>>; 0 => state_completed; while (!state_completed) ABCDQuestion(51); } else if (state == 51) { <<<"You answer is \"(" + temp_answer + ")\". Please confirm... (Y/N)">>>; 0 => state_completed; while (!state_completed) YesOrNo(6, 5); } else if (state == 6) { fout <= temp_answer <= "\n"; <<<"Confirmed!">>>; <<<"D. When you practice alone, how often do you use a metronome?">>>; <<<"(1) Never (2) Seldom (3) Often (4) Always">>>; 0 => state_completed; while (!state_completed) ABCDQuestion(61); } else if (state == 61) { <<<"You answer is \"(" + temp_answer + ")\". Please confirm... (Y/N)">>>; 0 => state_completed; while (!state_completed) YesOrNo(7, 6); } else if (state == 7) { fout <= temp_answer <= "\n"; <<<"Confirmed!">>>; <<<"==========================================================">>>; <<<"================== Experiment Completed ==================">>>; <<<"====================== Thank you! :) =====================">>>; <<<"==========================================================">>>; 8 => state; } } //============================================================================== // The Main Loop while (state != 8) { stateMachine(); } //============================================================================== // Close data file fout.close(); //============================================================================== // Update the subject number subject_counter.open(me.dir() + "0_subject_counter.txt", FileIO.WRITE); if(!subject_counter.good()) { <<< "Cannot update current subject number..." >>>; me.exit(); } subject_counter <= subject_num; subject_counter.close();