Sample playback

The Gameduino has a 64-voice synthesizer, and it can also play back samples.

To do this, the Arduino loads the sample byte into the SAMPLE_L and SAMPLE_R registers, and the Gameduino mixes the sample with the 64-voice synthesized audio.

The simplest way to play a sample is to take the sample data and blast it into the SAMPLE registers. Here is the code to play an 8KHz sample:

    PROGMEM prog_uchar *p;
    for (p = cowbell; p < cowbell + sizeof(cowbell); p++) {
      byte v = pgm_read_byte_near(p);
      GD.wr(SAMPLE_L + 1, v);
      GD.wr(SAMPLE_R + 1, v);
      delayMicroseconds(125);
    }

the resulting sound output: :download:`cowbell.wav`

System Message: ERROR/3 (<string>, line 19); backlink

Unknown interpreted text role "download".

To play back samples without halting gameplay requires a sample buffer. You can use Arduino timer interrupts, but coordinating SPI writes from inside an interrupt service routine is fiddly.

Or, use a 256-byte sample buffer in Gameduino memory, and a coprocessor microprogram to feed the samples into SAMPLE_L and SAMPLE_R:

  GD.microcode(soundbuffer_code, sizeof(soundbuffer_code));

  byte writepointer = 0;
  PROGMEM prog_uchar *p = cowbell;
  for (;;) {
    byte readpointer = GD.rd(COMM+0);
    byte fullness = writepointer - readpointer;
    while (fullness < 254) {
      char v = pgm_read_byte_near(p);
      GD.wr(SOUNDBUFFER + writepointer++, v);
      if (++p == (cowbell + sizeof(cowbell))) {
        p = cowbell;
      }
      fullness++;
    }
  }

The soundbuffer microprogram continuously plays back samples from the sample buffer, updating the read pointer at COMM+0 as it sweeps through the buffer.

start-microcode soundbuffer

\ Interface:
\ COMM+0    sound read pointer
\ 3F00-3FFF sound buffer

\ This microprogram provides a simple sound sample buffer.
\ It reads 8-bit samples from the buffer at 3F00-3FFF and
\ writes them to the audio sample registers SAMPLE_L and
\ SAMPLE_R.
\ The current buffer read pointer is COMM+0.

h# 3f00 constant BUFFER
[ 125 50 * ] constant CYCLE \ one cycle of 8KHz in clocks

: 1+    d# 1 + ;
: -     invert 1+ + ;

: main
    d# 0        ( when )
    begin
        CLOCK c@ over -     \ positive means CLOCK has passed `when`
        d# 0 < invert if
            COMM+0 c@ dup
            h# 3f00 + c@
            dup SAMPLE_Lhi c! SAMPLE_Rhi c!
            1+ COMM+0 c!
            CYCLE +
        then
    again
;

end-microcode

At the 8KHz rate, 254 samples is enough for 31 milliseconds of sound. So if a game refills the sample buffer at least once during every 36 Hz frame, the audio playback will be continuous.