/////////////////////////////////////////////////////////////////////////////// // Aperture Science Handheld Portal Device (ASHPD) - DIY Sound Effects // Adapted from ebs' forum post to ladyada.net, here: // http://www.ladyada.net/forums/viewtopic.php?t=6120#p29805 // And ladyada's dap.pde example: // http://www.ladyada.net/media/wavshield/dap.pde // // Copyright(c) 2009 by Rachel Mark // // Rachel Mark has no affiliation or connection to Valve Corporation. // This is fan written code. // Portal(R) is a registered trademark of Valve Corporation. // // This file is free software; you can redistribute it and/or modify // it under the terms of either the GNU General Public License version 2 // or the GNU Lesser General Public License version 2.1, both as // published by the Free Software Foundation. // // Overview: // ------------------ // Power-Switch (SPST ON-OFF): // - Turn the board (lights & sound) ON-OFF // - On setup(): Plays the powerup_sound1.wav file and starts the ambient_loop.wav // - ambient_loop.wav will continue to play while gun is on; // and pauses when gun is fired or other effect is triggered // // Mode-Toggle-Switch (SPDT ON-ON): // - Changes between Orange/Blue lights // - Toggles Fire-Button sound effects // // Fire-Button (SPST): // - Plays the color appropriate sound effect when pressed // - [Optionally] Flashes 4 additional white LEDs // - Follows up with randomly selected open-portal sound (open1 - open3.wav) // // Invalid-Surface-Toggle (SPST): // - Small discrete ON-OFF slide switch // - When ON: causes the follow-up sound to play invalid_surface.wav // rather than random open-portal sound // // Superfluous-Music-Button (SPST): // - When pressed it plays "Still Alive" by GLaDOS (Jonathan Coulton) // - Bonus! If the Invalid-Surface-Toggle is ON it plays the "radio mix" edit of Still Alive // #include #include #include #include AF_Wave card; Wavefile wave; // only one! File f; #define DEBOUNCE 100 #define invalidPin 14 #define colorPin 15 #define triggerPin 16 #define musicPin 17 #define fireLEDs 19 /////////////////////////////////////////////////////////////////////////////// // Had a little trouble looping the ambient wave without an audible pause // or "click", so I optimized the order of the files in the SD card // (making the loop first) and defragged the card. // (I swear this helped... me feel better at least) // #define LOOP "0LOOP.WAV" #define POWERUP "7POWERUP.WAV" #define FIRERED "1FIRERED.WAV" #define FIREBLUE "2FIREBLU.WAV" #define OPEN1 "3OPEN1.WAV" #define OPEN2 "4OPEN2.WAV" #define OPEN3 "5OPEN3.WAV" #define INVALID "6INVALID.WAV" #define RADIOMIX "8RADIO.WAV" #define STILLALIVE "9STILLAL.WAV" /////////////////////////////////////////////////////////////////////////////// void setup() { Serial.begin(9600); pinMode(2, OUTPUT); pinMode(3, OUTPUT); pinMode(4, OUTPUT); pinMode(5, OUTPUT); pinMode(fireLEDs, OUTPUT); digitalWrite(invalidPin, HIGH); digitalWrite(colorPin, HIGH); digitalWrite(triggerPin, HIGH); digitalWrite(musicPin, HIGH); randomSeed(analogRead(0)); if (!card.init_card()) { putstring_nl("Card init. failed!"); return; } if (!card.open_partition()) { putstring_nl("No partition!"); return; } if (!card.open_filesys()) { putstring_nl("Couldn't open filesys"); return; } if (!card.open_rootdir()) { putstring_nl("Couldn't open dir"); return; } playNowComplete(POWERUP); } /////////////////////////////////////////////////////////////////////////////// void loop() { checkFiredGun(); checkPlayMusic(); playLoop( LOOP ); } /////////////////////////////////////////////////////////////////////////////// // void checkFiredGun() { static long triggerTime = 0; static byte triggerPrevious = LOW; byte triggerReading = HIGH; byte colorReading = HIGH; byte invalidReading = HIGH; int followUp = 0; triggerReading = digitalRead(triggerPin); if ( triggerReading == LOW && triggerPrevious == HIGH && millis() - triggerTime > DEBOUNCE) { // play the fire effect based on color // and flash the extra LEDs digitalWrite(fireLEDs, HIGH); colorReading = digitalRead(colorPin); if (colorReading == LOW) { playNowComplete(FIREBLUE); } else { playNowComplete(FIRERED); } digitalWrite(fireLEDs, LOW); // Select the follow-up effect invalidReading = digitalRead( invalidPin ); if ( invalidReading == LOW ) { followUp = -1; } else { followUp = random(123); followUp %= 3; } // wait a little (0.5 seconds) and play the follow-up sound playNow(LOOP); delay(500); switch(followUp) { case 0: playNow(OPEN1); break; case 1: playNow(OPEN2); break; case 2: playNow(OPEN3); break; default: playNow(INVALID); break; } triggerTime = millis(); } triggerPrevious = triggerReading; } /////////////////////////////////////////////////////////////////////////////// // Check if the music button was pressed void checkPlayMusic() { static long musicTime = 0; static byte musicPrevious = LOW; byte musicReading = HIGH; byte invalidReading = HIGH; musicReading = digitalRead(musicPin); if (musicReading == LOW && musicPrevious == HIGH && millis() - musicTime > DEBOUNCE) { invalidReading = digitalRead( invalidPin ); if (invalidReading == LOW) { playNow(RADIOMIX); } else { playNow(STILLALIVE); } musicTime = millis(); } musicPrevious = musicReading; } /////////////////////////////////////////////////////////////////////////////// // Stop the current wave and play the given file now // - if completely is > 0, play the whole file before returning void playNow(char *name, bool completely) { // stop and close the current file if (wave.isplaying) { wave.stop(); } if (f) { card.close_file(f); } // create specified file card.reset_dir(); f = card.open_file(name); if (!f) { putstring_nl(" Couldn't open file"); return; } // create/play the wave if (!wave.create(f)) { putstring_nl(" Not a valid WAV"); return; } wave.play(); // play file completely before returning if (completely) { while (wave.isplaying); //wait, and do nothing card.close_file(f); } } /////////////////////////////////////////////////////////////////////////////// // Stop the current wave and play the file now, completely void playNow(char *name) { playNow(name, false); } /////////////////////////////////////////////////////////////////////////////// // Stop the current wave and play the file now, completely void playNowComplete(char *name) { playNow(name, true); } /////////////////////////////////////////////////////////////////////////////// // If nothing else is playing, play the ambient loop void playLoop(char *name) { // return if a file is already playing if (wave.isplaying) return; playNow( name, false ); }