This is no longer simple but is near my goal. I converted to lists by suggestion. This is a concert of Mario 1-1.
Enjoy and feel free to make it great.
If you improve please share to the community. The shutdown isn't always perfect.
This is my last concert for a while, going back into lurk mode to emulate some ancient hardwares.
Code: Select all
; This program creates a 250ms cyclic audio buffer using the
; low-level Windows Waveform Audio API (winmm.dll).
; It uses a callback function To refill buffers As they finish playing.
;
; This version plays the Super Mario Bros. theme (3 voices)
; using selectable waveforms FOR EACH VOICE.
;
; *** This version uses COMPRESSED data sections (byte indices) ***
; *** to reduce total file size. ***
; --- Waveform Type Constants ---
Enumeration
#Wave_Square
#Wave_Sine
#Wave_Triangle
#Wave_WhiteNoise
#Wave_PinkNoise
EndEnumeration
; --- Constants And Structures ---
#NUM_BUFFERS = 2
#NUM_VOICES = 3 ; Treble, Bass, Percussion
#WAVE_MAPPER = -1
#WAVE_FORMAT_PCM = 1
#CALLBACK_FUNCTION = $30000
#WOM_OPEN = $3BB
#WOM_DONE = $3BD
#WOM_CLOSE = $3BC
#WHDR_PREPARED = $2
#WHDR_DONE = $1 ; Flag indicating buffer is finished playing
Structure Note
frequency.f
durationMs.i
pan.f ; -1.0 (Left) To 1.0 (Right)
EndStructure
; --- Note frequencies ---
#E2 = 82.41 : #G2 = 98.00
#A2 = 110.00 : #Bb2 = 116.54 : #B2 = 123.47
#C3 = 130.81 : #Db3 = 138.59 : #D3 = 146.83 : #Eb3 = 155.56 : #E3 = 164.81 : #F3 = 174.61 : #Gb3 = 185.00 : #G3 = 196.00 : #Ab3 = 207.65 : #A3 = 220.00 : #Bb3 = 233.08 : #B3 = 246.94
#C4 = 261.63 : #Db4 = 277.18 : #D4 = 293.66 : #Eb4 = 311.13 : #E4 = 329.63 : #F4 = 349.23 : #Gb4 = 369.99 : #G4 = 392.00 : #Ab4 = 415.30 : #A4 = 440.00 : #Bb4 = 466.16 : #B4 = 493.88
#C5 = 523.25 : #Db5 = 554.37 : #D5 = 587.33 : #Eb5 = 622.25 : #E5 = 659.26 : #F5 = 698.46 : #Gb5 = 739.99 : #G5 = 783.99 : #Ab5 = 830.61 : #A5 = 880.00 : #Bb5 = 932.33 : #B5 = 987.77
#C6 = 1046.50 : #Db6 = 1108.73 : #D6 = 1174.66 : #Eb6 = 1244.51 : #E6 = 1318.51 : #F6 = 1396.91 : #Gb6 = 1479.98 : #G6 = 1567.98 : #Ab6 = 1661.22 : #A6 = 1760.00 : #C7 = 2093.00 : #REST = 0.0
; Sharps
#FSharp3 = #Gb3
#CSharp4 = #Db4 : #DSharp4 = #Eb4 : #FSharp4 = #Gb4 : #GSharp4 = #Ab4 : #ASharp4 = #Bb4
#CSharp5 = #Db5 : #DSharp5 = #Eb5 : #FSharp5 = #Gb5 : #GSharp5 = #Ab5 : #ASharp5 = #Bb5
#CSharp6 = #Db6 : #DSharp6 = #Eb6
; --- Note durations ---
#BPM = 105 ; Tempo from Mario XML
#BeatMs = (60000 / #BPM) ; Milliseconds per beat (Quarter note)
#D_Whole = #BeatMs * 4
#D_Half = #BeatMs * 2
#D_Quarter = #BeatMs
#D_8th = #BeatMs / 2
#D_16th = #BeatMs / 4
#D_Triplet = #BeatMs / 3
#D_Dot_8th = #D_8th + #D_16th
; --- [NEW] Data Lookup Arrays ---
Global Dim g_Frequencies.f(40) ; Map for note frequencies
Global Dim g_Durations.i(6) ; Map for note durations
Global Dim g_Pans.f(2) ; Map for pan values
; Populate Frequency Map (Index -> Frequency)
g_Frequencies(0) = #REST
g_Frequencies(1) = #E2 : g_Frequencies(2) = #G2 : g_Frequencies(3) = #A2
g_Frequencies(4) = #C3 : g_Frequencies(5) = #D3 : g_Frequencies(6) = #E3 : g_Frequencies(7) = #F3 : g_Frequencies(8) = #FSharp3 : g_Frequencies(9) = #G3 : g_Frequencies(10) = #A3 : g_Frequencies(11) = #B3
g_Frequencies(12) = #C4
g_Frequencies(13) = #D4 ; [FIX] Added missing D4
g_Frequencies(14) = #E4 ; (was 13)
g_Frequencies(15) = #F4 ; (was 14)
g_Frequencies(16) = #Gb4 ; (was 15)
g_Frequencies(17) = #G4 ; (was 16)
g_Frequencies(18) = #A4 ; (was 17)
g_Frequencies(19) = #B4 ; (was 18)
g_Frequencies(20) = #C5 ; (was 19)
g_Frequencies(21) = #D5 ; (was 20)
g_Frequencies(22) = #Eb5 ; (was 21)
g_Frequencies(23) = #E5 ; (was 22)
g_Frequencies(24) = #F5 ; (was 23)
g_Frequencies(25) = #Gb5 ; (was 24)
g_Frequencies(26) = #G5 ; (was 25)
g_Frequencies(27) = #Ab5 ; (was 26)
g_Frequencies(28) = #A5 ; (was 27)
g_Frequencies(29) = #Bb5 ; (was 28)
g_Frequencies(30) = #B5 ; (was 29)
g_Frequencies(31) = #C6 ; (was 30)
g_Frequencies(32) = #D6 ; (was 31)
g_Frequencies(33) = #Eb6 ; (was 32)
g_Frequencies(34) = #E6 ; (was 33)
g_Frequencies(35) = #F6 ; (was 34)
g_Frequencies(36) = #Gb6 ; (was 35)
g_Frequencies(37) = #G6 ; (was 36)
g_Frequencies(38) = #A6 ; (was 37)
g_Frequencies(39) = #C7 ; (was 38)
g_Frequencies(40) = #A4 ; Percussion (was 39)
; Populate Duration Map (Index -> Duration in MS)
g_Durations(0) = #D_16th
g_Durations(1) = #D_8th
g_Durations(2) = #D_Dot_8th
g_Durations(3) = #D_Quarter
g_Durations(4) = #D_Half
g_Durations(5) = #D_Whole
g_Durations(6) = #D_Triplet
; Populate Pan Map (Index -> Pan value)
g_Pans(0) = 0.0 ; Center
g_Pans(1) = 0.2 ; Treble Pan
g_Pans(2) = -0.2 ; Bass Pan
; --- Globals ---
Global hWaveOut.i
Global wfx.WAVEFORMATEX
Global Dim waveHeaders.WAVEHDR(#NUM_BUFFERS - 1)
Global Dim *waveBuffers(#NUM_BUFFERS - 1)
Global bufferSize.l
Global waveRunning.i = #True ; Flag to control buffer submission in callback
; [CHANGED] Waveform selection is now per-voice
Global g_MelodyWaveformType = #Wave_Square ; <<< CHANGE THIS (Melody wave)
Global g_BassWaveformType = #Wave_Triangle ; <<< CHANGE THIS (Bass wave)
Global g_PercussionWaveformType = #Wave_PinkNoise ; <<< [CHANGED] Set to Pink Noise >>>
Global g_amplitude.f = 10000.0 / #NUM_VOICES ; Reduce amplitude per voice
#PINK_OCTAVES = 5
Global Dim pink_Values.f(#PINK_OCTAVES - 1)
Global pink_Index.i = 0
Global envelopeMs.i = 10 ; Envelope duration in milliseconds
Global envelopeSamples.i = 0
Global NewList melodyTreble.Note() ; Voice 0
Global NewList melodyBass.Note() ; Voice 1
Global NewList melodyPercussion.Note() ; Voice 2
Global Dim g_NoteCounter.i(#NUM_VOICES - 1) ; Note counters for debugging
Global Dim freq.f(#NUM_VOICES - 1)
Global Dim pan.f(#NUM_VOICES - 1)
Global Dim phase.d(#NUM_VOICES - 1)
Global Dim samplesLeft.i(#NUM_VOICES - 1)
Global Dim totalSamples.i(#NUM_VOICES - 1)
Global Dim samplesPlayed.i(#NUM_VOICES - 1)
Global Dim isFirstNote(#NUM_VOICES - 1)
Define initVoiceIndex.i
For initVoiceIndex = 0 To #NUM_VOICES - 1
isFirstNote(initVoiceIndex) = #True
Next
Global Dim cycleLength.d(#NUM_VOICES - 1)
Global Dim halfCycle.d(#NUM_VOICES - 1)
Global Dim radPhaseStep.d(#NUM_VOICES - 1)
; --- Audio Generation ---
Procedure FillBuffer(*buffer, length)
Define *b.Word = *buffer
Define samplesToFill.i = length / (wfx\nBlockAlign)
Define i, v
Define sampleValue.f, finalL.f, finalR.f
Define temp.f, sum.f, j
Define envelopeMultiplier.f
Define durationMs.i
Define piOverTwo.f = #PI / 2.0 ; Precalculate for envelope
Define currentWaveform.i
; Calculate envelope length in samples only once if needed
If envelopeSamples = 0
envelopeSamples = (wfx\nSamplesPerSec * envelopeMs) / 1000
If envelopeSamples = 0 : envelopeSamples = 1 : EndIf ; Ensure at least 1 sample
EndIf
For i = 0 To samplesToFill - 1
finalL = 0.0 : finalR = 0.0
For v = 0 To #NUM_VOICES - 1
; Check if the current note for this voice has finished playing
If samplesLeft(v) <= 0
; Get the next note from the appropriate melody list
Select v
Case 0 ; Treble
If isFirstNote(v) ; Handle the very first note
If Not FirstElement(melodyTreble()) : freq(v)=0.0:totalSamples(v)=1:samplesLeft(v)=1:Debug "Error: Treble empty!": EndIf
isFirstNote(v)=#False
Else ; Get the next note, loop back to start if end is reached
If NextElement(melodyTreble())=0 : FirstElement(melodyTreble()) : EndIf
EndIf
; Read note properties from the list element
If ListSize(melodyTreble())>0
freq(v)=melodyTreble()\frequency : pan(v)=melodyTreble()\pan : durationMs=melodyTreble()\durationMs
Else : freq(v)=0.0:pan(v)=0.0:durationMs=1000 : EndIf ; Default if list empty
Case 1 ; Bass
If isFirstNote(v) ; Handle the very first note
If Not FirstElement(melodyBass()) : freq(v)=0.0:totalSamples(v)=1:samplesLeft(v)=1:Debug "Error: Bass empty!": EndIf
isFirstNote(v)=#False
Else ; Get the next note, loop back to start if end is reached
If NextElement(melodyBass())=0 : FirstElement(melodyBass()) : EndIf
EndIf
; Read note properties from the list element
If ListSize(melodyBass())>0
freq(v)=melodyBass()\frequency : pan(v)=melodyBass()\pan : durationMs=melodyBass()\durationMs
Else : freq(v)=0.0:pan(v)=0.0:durationMs=1000 : EndIf ; Default if list empty
Case 2 ; Percussion
If isFirstNote(v) ; Handle the very first note
If Not FirstElement(melodyPercussion()) : freq(v)=0.0:totalSamples(v)=1:samplesLeft(v)=1:Debug "Error: Percussion empty!": EndIf
isFirstNote(v)=#False
Else ; Get the next note, loop back to start if end is reached
If NextElement(melodyPercussion())=0 : FirstElement(melodyPercussion()) : EndIf
EndIf
; Read note properties from the list element
If ListSize(melodyPercussion())>0
freq(v)=melodyPercussion()\frequency : pan(v)=melodyPercussion()\pan : durationMs=melodyPercussion()\durationMs
Else : freq(v)=0.0:pan(v)=0.0:durationMs=1000 : EndIf ; Default if list empty
EndSelect
; Increment and debug note counter for melody voice
g_NoteCounter(v) + 1
If v = 0 ; Only print for the melody voice (voice 0)
;Debug "Voice 0 (Melody) playing note: " + Str(g_NoteCounter(v))
EndIf
; Calculate total samples for the new note's duration
totalSamples(v)=(wfx\nSamplesPerSec * durationMs)/1000
If totalSamples(v)<=0 : totalSamples(v)=1 : EndIf ; Ensure at least 1 sample
samplesLeft(v)=totalSamples(v) : samplesPlayed(v)=0 : phase(v)=0.0 ; Reset counters for the new note
; Pre-calculate phase step for sine/triangle waves if frequency is valid
If freq(v)>0
cycleLength(v)=wfx\nSamplesPerSec / freq(v) : halfCycle(v)=cycleLength(v)/2.0 : radPhaseStep(v)=(2*#PI)/cycleLength(v)
Else ; Reset for rests
cycleLength(v)=0 : halfCycle(v)=0 : radPhaseStep(v)=0
EndIf
EndIf
; [CHANGED] Set waveform type based on voice
Select v
Case 0 ; Melody
currentWaveform = g_MelodyWaveformType
Case 1 ; Bass
currentWaveform = g_BassWaveformType
Case 2 ; Percussion
currentWaveform = g_PercussionWaveformType
EndSelect
sampleValue=0.0 ; Default to silence
If freq(v)>0 ; Only generate sound if frequency is not REST (#REST = 0.0)
Select currentWaveform ; Use the waveform specific to this voice
Case #Wave_Square
If halfCycle(v)>0 ; Avoid division by zero
If phase(v)<halfCycle(v) : sampleValue=g_amplitude : Else : sampleValue=-g_amplitude : EndIf
EndIf
Case #Wave_Sine
If cycleLength(v)>0 : sampleValue=Sin(phase(v)*radPhaseStep(v))*g_amplitude : EndIf
Case #Wave_Triangle
If cycleLength(v)>0
temp=phase(v)/cycleLength(v)
If temp<0.5 : sampleValue=(temp*4.0-1.0)*g_amplitude ; Rising slope
Else : sampleValue=((1.0-temp)*4.0-1.0)*g_amplitude : EndIf ; Falling slope
EndIf
Case #Wave_WhiteNoise : sampleValue=Random(g_amplitude*2)-g_amplitude ; Simple white noise
Case #Wave_PinkNoise ; Approximation using averaging random sources
pink_Index=(pink_Index+1)%#PINK_OCTAVES : pink_Values(pink_Index)=(Random(10000)/5000.0)-1.0
sum=0.0 : For j=0 To #PINK_OCTAVES-1 : sum+pink_Values(j) : Next
sampleValue=(sum/#PINK_OCTAVES)*g_amplitude*1.5 ; Scale factor is empirical
EndSelect
EndIf
; Apply smoother Attack/Decay envelope using Sine curve
envelopeMultiplier=1.0
If freq(v)>0 And totalSamples(v)>1 And envelopeSamples>0
Define fadeLength.f=envelopeSamples
; [MODIFIED] Percussion uses a slightly longer custom fade now, not the full note length
If v = 2
fadeLength = (wfx\nSamplesPerSec * 40) / 1000 ; Force 40ms fade for percussion
If fadeLength <= 0 : fadeLength = 1 : EndIf
Else
; Prevent envelope overlap if note is very short for non-percussion
If fadeLength*2.0>totalSamples(v) : fadeLength=totalSamples(v)/2.0 : EndIf
EndIf
If fadeLength > 0 ; Proceed only if fade length is valid
If samplesPlayed(v) < fadeLength ; Attack phase (0 to pi/2)
envelopeMultiplier = Sin( (samplesPlayed(v) / fadeLength) * piOverTwo )
ElseIf (totalSamples(v) - samplesPlayed(v)) <= fadeLength And v <> 2 ; Decay phase for non-percussion
; Calculate remaining samples in decay phase (fadeLength down to 1)
Define samplesRemainingInFade.f = (totalSamples(v) - samplesPlayed(v) - 1.0)
If samplesRemainingInFade < 0 : samplesRemainingInFade = 0 : EndIf
envelopeMultiplier = Sin( (samplesRemainingInFade / fadeLength) * piOverTwo )
ElseIf v = 2 And samplesPlayed(v) >= fadeLength ; [NEW] Cut off percussion after its fade-in
envelopeMultiplier = 0.0 ; Cut off sharply after attack
Else
envelopeMultiplier = 1.0 ; Sustain phase
EndIf
; Clamp envelope multiplier (Sin should naturally be 0 to 1, but for safety)
If envelopeMultiplier<0.0 : envelopeMultiplier=0.0 : EndIf
If envelopeMultiplier>1.0 : envelopeMultiplier=1.0 : EndIf
Else
envelopeMultiplier = 1.0 ; No fade if fadeLength is zero
EndIf
EndIf
sampleValue*envelopeMultiplier ; Apply envelope fade
; Calculate left/right channel volume based on pan value (-1=Left, 0=Center, 1=Right)
Define panL.f = (1.0 - pan(v)) / 2.0
Define panR.f = (1.0 + pan(v)) / 2.0
Define voiceL.f = sampleValue * panL
Define voiceR.f = sampleValue * panR
; Add this voice's contribution to the final mix for this sample frame
finalL+voiceL
finalR+voiceR
; Advance phase for the next sample calculation
If cycleLength(v)>0
phase(v)+1.0
If phase(v)>=cycleLength(v) : phase(v)-cycleLength(v) : EndIf ; Wrap phase back to 0 if cycle completes
EndIf
samplesLeft(v)-1 ; Decrement samples remaining for this note
samplesPlayed(v)+1 ; Increment samples played for envelope calculation
Next v ; End voice loop (v=0 to #NUM_VOICES-1)
; Clamp final mixed sample values to 16-bit integer range before writing
If finalL>32767.0:finalL=32767.0:EndIf:If finalL<-32767.0:finalL=-32767.0:EndIf
If finalR>32767.0:finalR=32767.0:EndIf:If finalR<-32767.0:finalR=-32767.0:EndIf
; Write interleaved stereo samples (Left, Right) to the buffer memory
*b\w=Int(finalL):*b+2 ; Write Left sample, advance pointer by 2 bytes (size of Word)
*b\w=Int(finalR):*b+2 ; Write Right sample, advance pointer by 2 bytes
Next i ; End sample loop (i=0 to samplesToFill-1)
EndProcedure
; --- Callback Procedure ---
; This runs in a separate thread managed by the audio driver.
ProcedureC WaveCallback(hWaveOut.i, uMsg.i, dwInstance.i, dwParam1.i, dwParam2.i)
Select uMsg
Case #WOM_OPEN : Debug "Callback: Device Opened."
Case #WOM_DONE ; This message is sent when a buffer finishes playing
If waveRunning ; Check if we should continue playing (not shutting down)
Define *header.WAVEHDR = dwParam1 ; dwParam1 points to the WAVEHDR of the finished buffer
; [Shutdown Safety Check] Ensure the header pointer is valid and the buffer
; is still marked as prepared before trying to refill and requeue it.
; This prevents trying to access a buffer that the main thread might be
; unpreparing during shutdown.
If *header And (*header\dwFlags & #WHDR_PREPARED)
FillBuffer(*header\lpData, *header\dwBufferLength) ; Refill the buffer with new audio data
waveOutWrite_(hWaveOut, *header, SizeOf(WAVEHDR)) ; Send the refilled buffer back to the queue
Else
; Don't log here during normal operation, only potentially during shutdown
If Not waveRunning
Debug "Callback: Received WOM_DONE for a header not marked as prepared (dwFlags=" + Str(*header\dwFlags) +"). Possibly shutting down."
EndIf
EndIf
Else
; If waveRunning is false, it means the main thread is shutting down.
; Do not refill or requeue the buffer.
Debug "Callback: Received WOM_DONE but waveRunning is false. Ignoring."
EndIf
Case #WOM_CLOSE : Debug "Callback: Device Closed." ; Sent when waveOutClose completes
EndSelect
EndProcedure
; --- Main Program Setup ---
;Data Compression Explanation:
;
; The large DataSections below are compressed to save space. Instead of writing
; "Data.f #C4, Data.i #D_Quarter, Data.f 0.2", we use byte-sized indices.
; Each note is represented by three bytes:
; Data.b <freq_index>, <duration_index>, <pan_index>
;
; 1. <freq_index>: A byte (0-255) that corresponds to an index in the
; 'g_Frequencies()' array defined above. g_Frequencies(12) holds #C4.
; 2. <duration_index>: A byte (0-255) for the 'g_Durations()' array.
; g_Durations(3) holds #D_Quarter.
; 3. <pan_index>: A byte (0-255) for the 'g_Pans()' array.
; g_Pans(1) holds 0.2 (treble pan).
;
; So, the note C4 for a quarter beat panned right becomes:
; Data.b 12, 3, 1
;
; This saves a significant amount of space compared to the original text data.
; The 'Sentinel' or end-of-data marker is now:
; Data.b -1, -1, -1
; [COMPRESSED] Super Mario Bros. Theme (Measures 1-37) - Treble (Voice 0 - P1)
; Data format: Data.b FrequencyIndex, DurationIndex, PanIndex
DataSection
melody_treble_data:
; M1
Data.b 34,0,1 : Data.b 34,1,1 : Data.b 34,0,1 : Data.b 0,0,0 : Data.b 31,0,1 : Data.b 34,1,1 : Data.b 37,3,1 : Data.b 0,3,0
; M2
Data.b 31,2,1 : Data.b 26,0,1 : Data.b 0,1,0 : Data.b 23,1,1 : Data.b 0,0,0 : Data.b 28,1,1 : Data.b 30,0,1 : Data.b 0,0,0 : Data.b 29,0,1 : Data.b 28,1,1
; M3
Data.b 26,0,1 : Data.b 34,1,1 : Data.b 37,0,1 : Data.b 38,1,1 : Data.b 35,0,1 : Data.b 37,0,1 : Data.b 0,0,0 : Data.b 34,1,1 : Data.b 31,0,1 : Data.b 32,0,1 : Data.b 30,2,1
; M4
Data.b 31,2,1 : Data.b 26,0,1 : Data.b 0,1,0 : Data.b 23,1,1 : Data.b 0,0,0 : Data.b 28,1,1 : Data.b 30,0,1 : Data.b 0,0,0 : Data.b 29,0,1 : Data.b 28,1,1
; M5
Data.b 26,0,1 : Data.b 34,1,1 : Data.b 37,0,1 : Data.b 38,1,1 : Data.b 35,0,1 : Data.b 37,0,1 : Data.b 0,0,0 : Data.b 34,1,1 : Data.b 31,0,1 : Data.b 32,0,1 : Data.b 30,2,1
; M6
Data.b 0,1,0 : Data.b 37,0,1 : Data.b 36,0,1 : Data.b 35,0,1 : Data.b 33,1,1 : Data.b 34,0,1 : Data.b 0,0,0 : Data.b 26,0,1 : Data.b 28,0,1 : Data.b 31,0,1 : Data.b 0,0,0 : Data.b 28,0,1 : Data.b 31,0,1 : Data.b 32,0,1
; M7
Data.b 0,1,0 : Data.b 37,0,1 : Data.b 36,0,1 : Data.b 35,0,1 : Data.b 33,1,1 : Data.b 34,0,1 : Data.b 0,0,0 : Data.b 39,1,1 : Data.b 39,0,1 : Data.b 39,3,1
; M8
Data.b 0,1,0 : Data.b 37,0,1 : Data.b 36,0,1 : Data.b 35,0,1 : Data.b 33,1,1 : Data.b 34,0,1 : Data.b 0,0,0 : Data.b 26,0,1 : Data.b 28,0,1 : Data.b 31,0,1 : Data.b 0,0,0 : Data.b 28,0,1 : Data.b 31,0,1 : Data.b 32,0,1
; M9
Data.b 0,1,0 : Data.b 33,1,1 : Data.b 0,0,0 : Data.b 32,2,1 : Data.b 31,3,1 : Data.b 0,3,0
; M10
Data.b 0,1,0 : Data.b 37,0,1 : Data.b 36,0,1 : Data.b 35,0,1 : Data.b 33,1,1 : Data.b 34,0,1 : Data.b 0,0,0 : Data.b 27,0,1 : Data.b 28,0,1 : Data.b 31,0,1 : Data.b 0,0,0 : Data.b 28,0,1 : Data.b 31,0,1 : Data.b 32,0,1
; M11
Data.b 0,1,0 : Data.b 37,0,1 : Data.b 36,0,1 : Data.b 35,0,1 : Data.b 33,1,1 : Data.b 34,0,1 : Data.b 0,0,0 : Data.b 39,1,1 : Data.b 39,0,1 : Data.b 39,3,1
; M12
Data.b 0,1,0 : Data.b 37,0,1 : Data.b 36,0,1 : Data.b 35,0,1 : Data.b 33,1,1 : Data.b 34,0,1 : Data.b 0,0,0 : Data.b 26,0,1 : Data.b 28,0,1 : Data.b 31,0,1 : Data.b 0,0,0 : Data.b 28,0,1 : Data.b 31,0,1 : Data.b 32,0,1
; M13
Data.b 0,1,0 : Data.b 33,1,1 : Data.b 0,0,0 : Data.b 32,2,1 : Data.b 31,3,1 : Data.b 0,3,0
; M14
Data.b 31,0,1 : Data.b 31,1,1 : Data.b 31,0,1 : Data.b 0,0,0 : Data.b 31,0,1 : Data.b 32,1,1 : Data.b 34,0,1 : Data.b 31,1,1 : Data.b 28,0,1 : Data.b 26,3,1
; M15
Data.b 31,0,1 : Data.b 31,1,1 : Data.b 31,0,1 : Data.b 0,0,0 : Data.b 31,0,1 : Data.b 32,1,1 : Data.b 34,0,1 : Data.b 0,4,0
; M16
Data.b 31,0,1 : Data.b 31,1,1 : Data.b 31,0,1 : Data.b 0,0,0 : Data.b 31,0,1 : Data.b 32,1,1 : Data.b 34,0,1 : Data.b 31,1,1 : Data.b 28,0,1 : Data.b 26,3,1
; M17
Data.b 34,0,1 : Data.b 34,1,1 : Data.b 34,0,1 : Data.b 0,0,0 : Data.b 31,0,1 : Data.b 34,1,1 : Data.b 37,3,1 : Data.b 0,3,0
; M18
Data.b 31,2,1 : Data.b 26,0,1 : Data.b 0,1,0 : Data.b 23,1,1 : Data.b 0,0,0 : Data.b 28,1,1 : Data.b 30,0,1 : Data.b 0,0,0 : Data.b 29,0,1 : Data.b 28,1,1
; M19
Data.b 26,0,1 : Data.b 34,1,1 : Data.b 37,0,1 : Data.b 38,1,1 : Data.b 35,0,1 : Data.b 37,0,1 : Data.b 0,0,0 : Data.b 34,1,1 : Data.b 31,0,1 : Data.b 32,0,1 : Data.b 30,2,1
; M20
Data.b 31,2,1 : Data.b 26,0,1 : Data.b 0,1,0 : Data.b 23,1,1 : Data.b 0,0,0 : Data.b 28,1,1 : Data.b 30,0,1 : Data.b 0,0,0 : Data.b 29,0,1 : Data.b 28,1,1
; M21
Data.b 26,0,1 : Data.b 34,1,1 : Data.b 37,0,1 : Data.b 38,1,1 : Data.b 35,0,1 : Data.b 37,0,1 : Data.b 0,0,0 : Data.b 34,1,1 : Data.b 31,0,1 : Data.b 32,0,1 : Data.b 30,2,1
; M22
Data.b 34,0,1 : Data.b 31,1,1 : Data.b 26,0,1 : Data.b 0,1,0 : Data.b 25,1,1 : Data.b 28,0,1 : Data.b 35,1,1 : Data.b 35,0,1 : Data.b 28,3,1
; M23
Data.b 30,0,1 : Data.b 38,1,1 : Data.b 38,0,1 : Data.b 38,0,1 : Data.b 37,1,1 : Data.b 35,0,1 : Data.b 34,0,1 : Data.b 31,1,1 : Data.b 28,0,1 : Data.b 26,3,1
; M24
Data.b 34,0,1 : Data.b 31,1,1 : Data.b 26,0,1 : Data.b 0,1,0 : Data.b 25,1,1 : Data.b 28,0,1 : Data.b 35,1,1 : Data.b 35,0,1 : Data.b 28,3,1
; M25
Data.b 30,0,1 : Data.b 35,1,1 : Data.b 35,0,1 : Data.b 35,0,1 : Data.b 34,1,1 : Data.b 32,0,1 : Data.b 31,3,1 : Data.b 0,3,0
; M26
Data.b 31,0,1 : Data.b 31,1,1 : Data.b 31,0,1 : Data.b 0,0,0 : Data.b 31,0,1 : Data.b 32,1,1 : Data.b 34,0,1 : Data.b 31,1,1 : Data.b 28,0,1 : Data.b 26,3,1
; M27
Data.b 31,0,1 : Data.b 31,1,1 : Data.b 31,0,1 : Data.b 0,0,0 : Data.b 31,0,1 : Data.b 32,1,1 : Data.b 34,0,1 : Data.b 0,4,0
; M28
Data.b 31,0,1 : Data.b 31,1,1 : Data.b 31,0,1 : Data.b 0,0,0 : Data.b 31,0,1 : Data.b 32,1,1 : Data.b 34,0,1 : Data.b 31,1,1 : Data.b 28,0,1 : Data.b 26,3,1
; M29
Data.b 34,0,1 : Data.b 34,1,1 : Data.b 34,0,1 : Data.b 0,0,0 : Data.b 31,0,1 : Data.b 34,1,1 : Data.b 37,3,1 : Data.b 0,3,0
; M30
Data.b 31,2,1 : Data.b 26,0,1 : Data.b 0,1,0 : Data.b 23,1,1 : Data.b 0,0,0 : Data.b 28,1,1 : Data.b 30,0,1 : Data.b 0,0,0 : Data.b 29,0,1 : Data.b 28,1,1
; M31
Data.b 26,0,1 : Data.b 34,1,1 : Data.b 37,0,1 : Data.b 38,1,1 : Data.b 35,0,1 : Data.b 37,0,1 : Data.b 0,0,0 : Data.b 34,1,1 : Data.b 31,0,1 : Data.b 32,0,1 : Data.b 30,2,1
; M32
Data.b 31,2,1 : Data.b 26,0,1 : Data.b 0,1,0 : Data.b 23,1,1 : Data.b 0,0,0 : Data.b 28,1,1 : Data.b 30,0,1 : Data.b 0,0,0 : Data.b 29,0,1 : Data.b 28,1,1
; M33
Data.b 26,0,1 : Data.b 34,1,1 : Data.b 37,0,1 : Data.b 38,1,1 : Data.b 35,0,1 : Data.b 37,0,1 : Data.b 0,0,0 : Data.b 34,1,1 : Data.b 31,0,1 : Data.b 32,0,1 : Data.b 30,2,1
; M34
Data.b 34,0,1 : Data.b 31,1,1 : Data.b 26,0,1 : Data.b 0,1,0 : Data.b 25,1,1 : Data.b 28,0,1 : Data.b 35,1,1 : Data.b 35,0,1 : Data.b 28,3,1
; M35
Data.b 30,0,1 : Data.b 38,1,1 : Data.b 38,0,1 : Data.b 38,0,1 : Data.b 37,1,1 : Data.b 35,0,1 : Data.b 34,0,1 : Data.b 31,1,1 : Data.b 28,0,1 : Data.b 26,3,1
; M36
Data.b 34,0,1 : Data.b 31,1,1 : Data.b 26,0,1 : Data.b 0,1,0 : Data.b 25,1,1 : Data.b 28,0,1 : Data.b 35,1,1 : Data.b 35,0,1 : Data.b 28,3,1
; M37
Data.b 30,0,1 : Data.b 35,1,1 : Data.b 35,0,1 : Data.b 35,0,1 : Data.b 34,1,1 : Data.b 32,0,1 : Data.b 31,3,1 : Data.b 0,3,0
; End
Data.b -1, -1, -1
EndDataSection
; [COMPRESSED] Super Mario Bros. Theme (Measures 1-37) - Bass (Voice 1 - P3)
DataSection
melody_bass_data:
; M1
Data.b 5,0,2 : Data.b 5,1,2 : Data.b 5,0,2 : Data.b 0,0,0 : Data.b 5,0,2 : Data.b 5,1,2 : Data.b 17,3,2 : Data.b 9,3,2
; M2
Data.b 9,2,2 : Data.b 6,0,2 : Data.b 0,1,0 : Data.b 4,1,2 : Data.b 0,0,0 : Data.b 7,1,2 : Data.b 9,0,2 : Data.b 0,0,0 : Data.b 8,0,2 : Data.b 7,1,2
; M3
Data.b 6,0,2 : Data.b 12,1,2 : Data.b 14,0,2 : Data.b 15,1,2 : Data.b 13,0,2 : Data.b 14,0,2 : Data.b 0,0,0 : Data.b 12,1,2 : Data.b 10,0,2 : Data.b 11,0,2 : Data.b 9,2,2
; M4
Data.b 9,2,2 : Data.b 6,0,2 : Data.b 0,1,0 : Data.b 4,1,2 : Data.b 0,0,0 : Data.b 7,1,2 : Data.b 9,0,2 : Data.b 0,0,0 : Data.b 8,0,2 : Data.b 7,1,2
; M5
Data.b 6,0,2 : Data.b 12,1,2 : Data.b 14,0,2 : Data.b 15,1,2 : Data.b 13,0,2 : Data.b 14,0,2 : Data.b 0,0,0 : Data.b 12,1,2 : Data.b 10,0,2 : Data.b 11,0,2 : Data.b 9,2,2
; M6
Data.b 0,1,0 : Data.b 17,0,2 : Data.b 16,0,2 : Data.b 15,0,2 : Data.b 22,1,2 : Data.b 14,0,2 : Data.b 0,0,0 : Data.b 9,0,2 : Data.b 10,0,2 : Data.b 12,0,2 : Data.b 0,0,0 : Data.b 10,0,2 : Data.b 12,0,2 : Data.b 13,0,2
; M7
Data.b 0,1,0 : Data.b 17,0,2 : Data.b 16,0,2 : Data.b 15,0,2 : Data.b 22,1,2 : Data.b 14,0,2 : Data.b 0,0,0 : Data.b 20,1,2 : Data.b 20,0,2 : Data.b 20,3,2
; M8
Data.b 0,1,0 : Data.b 17,0,2 : Data.b 16,0,2 : Data.b 15,0,2 : Data.b 22,1,2 : Data.b 14,0,2 : Data.b 0,0,0 : Data.b 9,0,2 : Data.b 10,0,2 : Data.b 12,0,2 : Data.b 0,0,0 : Data.b 10,0,2 : Data.b 12,0,2 : Data.b 13,0,2
; M9
Data.b 0,1,0 : Data.b 9,1,2 : Data.b 0,0,0 : Data.b 7,2,2 : Data.b 6,3,2 : Data.b 0,3,0
; M10
Data.b 0,1,0 : Data.b 17,0,2 : Data.b 16,0,2 : Data.b 15,0,2 : Data.b 22,1,2 : Data.b 14,0,2 : Data.b 0,0,0 : Data.b 9,0,2 : Data.b 10,0,2 : Data.b 12,0,2 : Data.b 0,0,0 : Data.b 10,0,2 : Data.b 12,0,2 : Data.b 13,0,2
; M11
Data.b 0,1,0 : Data.b 17,0,2 : Data.b 16,0,2 : Data.b 15,0,2 : Data.b 22,1,2 : Data.b 14,0,2 : Data.b 0,0,0 : Data.b 20,1,2 : Data.b 20,0,2 : Data.b 20,3,2
; M12
Data.b 0,1,0 : Data.b 17,0,2 : Data.b 16,0,2 : Data.b 15,0,2 : Data.b 22,1,2 : Data.b 14,0,2 : Data.b 0,0,0 : Data.b 9,0,2 : Data.b 10,0,2 : Data.b 12,0,2 : Data.b 0,0,0 : Data.b 10,0,2 : Data.b 12,0,2 : Data.b 13,0,2
; M13
Data.b 0,1,0 : Data.b 9,1,2 : Data.b 0,0,0 : Data.b 7,2,2 : Data.b 6,3,2 : Data.b 0,3,0
; M14
Data.b 4,0,2 : Data.b 4,1,2 : Data.b 4,0,2 : Data.b 0,0,0 : Data.b 4,0,2 : Data.b 5,1,2 : Data.b 6,0,2 : Data.b 4,1,2 : Data.b 3,0,2 : Data.b 2,3,2
; M15
Data.b 4,0,2 : Data.b 4,1,2 : Data.b 4,0,2 : Data.b 0,0,0 : Data.b 4,0,2 : Data.b 5,1,2 : Data.b 6,0,2 : Data.b 0,4,0
; M16
Data.b 4,0,2 : Data.b 4,1,2 : Data.b 4,0,2 : Data.b 0,0,0 : Data.b 4,0,2 : Data.b 5,1,2 : Data.b 6,0,2 : Data.b 4,1,2 : Data.b 3,0,2 : Data.b 2,3,2
; M17
Data.b 5,0,2 : Data.b 5,1,2 : Data.b 5,0,2 : Data.b 0,0,0 : Data.b 5,0,2 : Data.b 5,1,2 : Data.b 17,3,2 : Data.b 9,3,2
; M18
Data.b 9,2,2 : Data.b 6,0,2 : Data.b 0,1,0 : Data.b 4,1,2 : Data.b 0,0,0 : Data.b 7,1,2 : Data.b 9,0,2 : Data.b 0,0,0 : Data.b 8,0,2 : Data.b 7,1,2
; M19
Data.b 6,0,2 : Data.b 12,1,2 : Data.b 14,0,2 : Data.b 15,1,2 : Data.b 13,0,2 : Data.b 14,0,2 : Data.b 0,0,0 : Data.b 12,1,2 : Data.b 10,0,2 : Data.b 11,0,2 : Data.b 9,2,2
; M20
Data.b 9,2,2 : Data.b 6,0,2 : Data.b 0,1,0 : Data.b 4,1,2 : Data.b 0,0,0 : Data.b 7,1,2 : Data.b 9,0,2 : Data.b 0,0,0 : Data.b 8,0,2 : Data.b 7,1,2
; M21
Data.b 6,0,2 : Data.b 12,1,2 : Data.b 14,0,2 : Data.b 15,1,2 : Data.b 13,0,2 : Data.b 14,0,2 : Data.b 0,0,0 : Data.b 12,1,2 : Data.b 10,0,2 : Data.b 11,0,2 : Data.b 9,2,2
; M22
Data.b 4,0,2 : Data.b 3,1,2 : Data.b 1,0,2 : Data.b 0,1,0 : Data.b 1,1,2 : Data.b 7,0,2 : Data.b 12,1,2 : Data.b 12,0,2 : Data.b 7,3,2
; M23
Data.b 9,0,2 : Data.b 15,1,2 : Data.b 15,0,2 : Data.b 15,0,2 : Data.b 14,1,2 : Data.b 13,0,2 : Data.b 12,1,2 : Data.b 10,0,2 : Data.b 7,0,2 : Data.b 6,3,2
; M24
Data.b 4,0,2 : Data.b 3,1,2 : Data.b 1,0,2 : Data.b 0,1,0 : Data.b 1,1,2 : Data.b 7,0,2 : Data.b 12,1,2 : Data.b 12,0,2 : Data.b 7,3,2
; M25
Data.b 9,0,2 : Data.b 13,1,2 : Data.b 13,0,2 : Data.b 13,0,2 : Data.b 12,1,2 : Data.b 11,0,2 : Data.b 9,1,2 : Data.b 6,0,2 : Data.b 6,0,2 : Data.b 4,3,2
; M26
Data.b 4,0,2 : Data.b 4,1,2 : Data.b 4,0,2 : Data.b 0,0,0 : Data.b 4,0,2 : Data.b 5,1,2 : Data.b 6,0,2 : Data.b 4,1,2 : Data.b 3,0,2 : Data.b 2,3,2
; M27
Data.b 4,0,2 : Data.b 4,1,2 : Data.b 4,0,2 : Data.b 0,0,0 : Data.b 4,0,2 : Data.b 5,1,2 : Data.b 6,0,2 : Data.b 0,4,0
; M28
Data.b 4,0,2 : Data.b 4,1,2 : Data.b 4,0,2 : Data.b 0,0,0 : Data.b 4,0,2 : Data.b 5,1,2 : Data.b 6,0,2 : Data.b 4,1,2 : Data.b 3,0,2 : Data.b 2,3,2
; M29
Data.b 5,0,2 : Data.b 5,1,2 : Data.b 5,0,2 : Data.b 0,0,0 : Data.b 5,0,2 : Data.b 5,1,2 : Data.b 17,3,2 : Data.b 9,3,2
; M30
Data.b 9,2,2 : Data.b 6,0,2 : Data.b 0,1,0 : Data.b 4,1,2 : Data.b 0,0,0 : Data.b 7,1,2 : Data.b 9,0,2 : Data.b 0,0,0 : Data.b 8,0,2 : Data.b 7,1,2
; M31
Data.b 6,0,2 : Data.b 12,1,2 : Data.b 14,0,2 : Data.b 15,1,2 : Data.b 13,0,2 : Data.b 14,0,2 : Data.b 0,0,0 : Data.b 12,1,2 : Data.b 10,0,2 : Data.b 11,0,2 : Data.b 9,2,2
; M32
Data.b 9,2,2 : Data.b 6,0,2 : Data.b 0,1,0 : Data.b 4,1,2 : Data.b 0,0,0 : Data.b 7,1,2 : Data.b 9,0,2 : Data.b 0,0,0 : Data.b 8,0,2 : Data.b 7,1,2
; M33
Data.b 6,0,2 : Data.b 12,1,2 : Data.b 14,0,2 : Data.b 15,1,2 : Data.b 13,0,2 : Data.b 14,0,2 : Data.b 0,0,0 : Data.b 12,1,2 : Data.b 10,0,2 : Data.b 11,0,2 : Data.b 9,2,2
; M34
Data.b 4,0,2 : Data.b 3,1,2 : Data.b 1,0,2 : Data.b 0,1,0 : Data.b 1,1,2 : Data.b 7,0,2 : Data.b 12,1,2 : Data.b 12,0,2 : Data.b 7,3,2
; M35
Data.b 9,0,2 : Data.b 15,1,2 : Data.b 15,0,2 : Data.b 15,0,2 : Data.b 14,1,2 : Data.b 13,0,2 : Data.b 12,1,2 : Data.b 10,0,2 : Data.b 7,0,2 : Data.b 6,3,2
; M36
Data.b 4,0,2 : Data.b 3,1,2 : Data.b 1,0,2 : Data.b 0,1,0 : Data.b 1,1,2 : Data.b 7,0,2 : Data.b 12,1,2 : Data.b 12,0,2 : Data.b 7,3,2
; M37
Data.b 9,0,2 : Data.b 13,1,2 : Data.b 13,0,2 : Data.b 13,0,2 : Data.b 12,1,2 : Data.b 11,0,2 : Data.b 9,1,2 : Data.b 6,0,2 : Data.b 6,0,2 : Data.b 4,3,2
; End
Data.b -1, -1, -1
EndDataSection
; [COMPRESSED] Super Mario Bros. Theme (Measures 1-37) - Percussion (Voice 2 - P4)
DataSection
melody_percussion_data:
; M1
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M2
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M3
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M4
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M5
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M6
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M7
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M8
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M9
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M10
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M11
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M12
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M13
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M14
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M15
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M16
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M17
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M18
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M19
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M20
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M21
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M22
Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,0,0
; M23
Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,0,0
; M24
Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,0,0
; M25
Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,0,0
; M26
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M27
Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,0,0
; M28
Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,0,0
; M29
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M30
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M31
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M32
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M33
Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 40,0,0
; M34
Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,0,0
; M35
Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,0,0
; M36
Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,0,0
; M37
Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,1,0 : Data.b 40,0,0 : Data.b 40,0,0 : Data.b 0,0,0 : Data.b 40,0,0 : Data.b 0,0,0
; End
Data.b -1, -1, -1
EndDataSection
Define tempFreqIndex.b, tempDurIndex.b, tempPanIndex.b
; Load Treble Melody
Restore melody_treble_data
Repeat
Read.b tempFreqIndex
Read.b tempDurIndex
Read.b tempPanIndex
If tempFreqIndex = -1 : Break : EndIf
If tempFreqIndex < 0 Or tempFreqIndex > 40 : Debug "Treble Freq Index Out of Range: " + Str(tempFreqIndex) : Break : EndIf
If tempDurIndex < 0 Or tempDurIndex > 6 : Debug "Treble Dur Index Out of Range: " + Str(tempDurIndex) : Break : EndIf
If tempPanIndex < 0 Or tempPanIndex > 2 : Debug "Treble Pan Index Out of Range: " + Str(tempPanIndex) : Break : EndIf
AddElement(melodyTreble())
melodyTreble()\frequency = g_Frequencies(tempFreqIndex)
melodyTreble()\durationMs = g_Durations(tempDurIndex)
melodyTreble()\pan = g_Pans(tempPanIndex)
ForEver
; Load Bass Melody
Restore melody_bass_data
Repeat
Read.b tempFreqIndex
Read.b tempDurIndex
Read.b tempPanIndex
If tempFreqIndex = -1 : Break : EndIf
If tempFreqIndex < 0 Or tempFreqIndex > 40 : Debug "Bass Freq Index Out of Range: " + Str(tempFreqIndex) : Break : EndIf
If tempDurIndex < 0 Or tempDurIndex > 6 : Debug "Bass Dur Index Out of Range: " + Str(tempDurIndex) : Break : EndIf
If tempPanIndex < 0 Or tempPanIndex > 2 : Debug "Bass Pan Index Out of Range: " + Str(tempPanIndex) : Break : EndIf
AddElement(melodyBass())
melodyBass()\frequency = g_Frequencies(tempFreqIndex)
melodyBass()\durationMs = g_Durations(tempDurIndex)
melodyBass()\pan = g_Pans(tempPanIndex)
ForEver
; Load Percussion Melody
Restore melody_percussion_data
Repeat
Read.b tempFreqIndex
Read.b tempDurIndex
Read.b tempPanIndex
If tempFreqIndex = -1 : Break : EndIf
If tempFreqIndex < 0 Or tempFreqIndex > 40 : Debug "Perc Freq Index Out of Range: " + Str(tempFreqIndex) : Break : EndIf
If tempDurIndex < 0 Or tempDurIndex > 6 : Debug "Perc Dur Index Out of Range: " + Str(tempDurIndex) : Break : EndIf
If tempPanIndex < 0 Or tempPanIndex > 2 : Debug "Perc Pan Index Out of Range: " + Str(tempPanIndex) : Break : EndIf
AddElement(melodyPercussion())
melodyPercussion()\frequency = g_Frequencies(tempFreqIndex)
melodyPercussion()\durationMs = g_Durations(tempDurIndex)
melodyPercussion()\pan = g_Pans(tempPanIndex)
ForEver
; Set up audio format structure
wfx\wFormatTag = #WAVE_FORMAT_PCM : wfx\nChannels = 2 : wfx\nSamplesPerSec = 44100
wfx\wBitsPerSample = 16 : wfx\nBlockAlign = (wfx\nChannels * wfx\wBitsPerSample) / 8
wfx\nAvgBytesPerSec = wfx\nSamplesPerSec * wfx\nBlockAlign : wfx\cbSize = 0
; Calculate buffer size for ~250ms of audio
bufferSize = wfx\nAvgBytesPerSec / 4 ; (Bytes per second) / 4 = Bytes per 250ms
Debug "Audio Format: 44.1kHz, 16-bit, Stereo"
Debug "Buffer Size (250ms): " + Str(bufferSize) + " bytes"
Define melodyWaveName.s = ""
Select g_MelodyWaveformType
Case #Wave_Square: melodyWaveName = "Square" : Case #Wave_Sine: melodyWaveName = "Sine"
Case #Wave_Triangle: melodyWaveName = "Triangle" : Case #Wave_WhiteNoise: melodyWaveName = "White Noise"
Case #Wave_PinkNoise: melodyWaveName = "Pink Noise"
EndSelect
Debug "Melody Waveform: " + melodyWaveName
Define bassWaveName.s = ""
Select g_BassWaveformType
Case #Wave_Square: bassWaveName = "Square" : Case #Wave_Sine: bassWaveName = "Sine"
Case #Wave_Triangle: bassWaveName = "Triangle" : Case #Wave_WhiteNoise: bassWaveName = "White Noise"
Case #Wave_PinkNoise: bassWaveName = "Pink Noise"
EndSelect
Debug "Bass Waveform: " + bassWaveName
Define percWaveName.s = ""
Select g_PercussionWaveformType
Case #Wave_WhiteNoise: percWaveName = "White Noise"
Case #Wave_PinkNoise: percWaveName = "Pink Noise"
Default: percWaveName = "Other"
EndSelect
Debug "Percussion Waveform: " + percWaveName
; Open the default audio output device
Define result.i
result = waveOutOpen_(@hWaveOut, #WAVE_MAPPER, @wfx, @WaveCallback(), 0, #CALLBACK_FUNCTION)
If result <> 0 : MessageRequester("Error", "Could not open audio device. Error: " + Str(result)) : End : EndIf
Debug "Audio device opened successfully (Handle: " + Str(hWaveOut) + ")"
; Allocate, prepare, and fill initial buffers
Define i
For i = 0 To #NUM_BUFFERS - 1
*waveBuffers(i) = AllocateMemory(bufferSize) ; Allocate memory for audio samples
If *waveBuffers(i) = 0 : MessageRequester("Error", "Failed To allocate buffer " + Str(i)) : waveOutClose_(hWaveOut): End : EndIf ; Check allocation success
FillBuffer(*waveBuffers(i), bufferSize) ; Fill with initial audio data
waveHeaders(i)\lpData = *waveBuffers(i) ; Point header to the buffer
waveHeaders(i)\dwBufferLength = bufferSize ; Set buffer size in header
waveHeaders(i)\dwFlags = 0 ; Ensure flags are reset before preparing
waveHeaders(i)\dwUser = i ; Optional: Store buffer index For debugging in callback
; Prepare the header (locks buffer memory, makes it ready for driver)
result = waveOutPrepareHeader_(hWaveOut, @waveHeaders(i), SizeOf(WAVEHDR))
If result <> 0 : MessageRequester("Error", "Could not prepare buffer " + Str(i) + ". Error: " + Str(result)) : waveOutClose_(hWaveOut) : End : EndIf
Next
Debug "Prepared " + Str(#NUM_BUFFERS) + " audio buffers."
; Write the initial buffers to the output device to start playback
For i = 0 To #NUM_BUFFERS - 1 : waveOutWrite_(hWaveOut, @waveHeaders(i), SizeOf(WAVEHDR)) : Next
Debug "Playback started. Queued initial buffers."
; Simple window to keep program running and allow user to stop
OpenWindow(0, 0, 0, 300, 150, "Mario Test (3 Voice)", #PB_Window_SystemMenu | #PB_Window_ScreenCentered)
TextGadget(0, 10, 10, 280, 130, "Playing 'Super Mario Bros. Theme' (3 Voice)..." + #CRLF$ + "Melody: " + melodyWaveName + #CRLF$ + "Bass: " + bassWaveName + #CRLF$ + "Perc: " + percWaveName + #CRLF$ + #CRLF$ + "Close this window To stop.")
; Main event loop - waits until the window is closed
Repeat
Define event.i = WaitWindowEvent()
Until event = #PB_Event_CloseWindow
; --- [REVISED v3 - No waveOutReset_] Shutdown Sequence ---
Debug "Window closed. Shutting down audio..."
; 1. Signal the callback thread to stop processing WOM_DONE messages by queueing new buffers.
waveRunning = #False
Debug "Callback flag 'waveRunning' set to False."
; 2. Wait for all buffers to be returned by the callback.
; Check the WHDR_DONE flag. Buffers are marked DONE when they finish playing
; or when waveOutReset is called (which we are now avoiding).
; We need both buffers to be done before proceeding.
Define allBuffersDone.i = #False
Define waitTimeout.i = 2000 ; Max wait time in ms (e.g., 2 seconds)
Define startTime.i = ElapsedMilliseconds()
Debug "Waiting for buffers to complete (Max " + Str(waitTimeout) + "ms)..."
Repeat
allBuffersDone = #True ; Assume done until proven otherwise
For i = 0 To #NUM_BUFFERS - 1
; Check the DONE flag is set AND the PREPARED flag is still set (it shouldn't be cleared yet)
If Not (waveHeaders(i)\dwFlags & #WHDR_DONE) And (waveHeaders(i)\dwFlags & #WHDR_PREPARED)
allBuffersDone = #False
Break ; No need to check others if one isn't done
EndIf
Next
If allBuffersDone
Debug "All buffers marked as DONE."
Break
EndIf
If ElapsedMilliseconds() - startTime > waitTimeout
Debug "Timeout waiting for buffers to complete. Proceeding cautiously..."
Break ; Exit loop after timeout
EndIf
Delay(20) ; Wait a bit before checking again
Until allBuffersDone
; 3. Now that playback should be stopped and callbacks quiescent, unprepare headers.
Debug "Starting unprepare loop..."
For i = 0 To #NUM_BUFFERS - 1
; Safety check: Make sure the buffer pointer is actually valid (was allocated successfully)
If *waveBuffers(i)
Debug "Attempting to unprepare buffer " + Str(i) + "..."
; Check if the buffer is still marked as prepared before trying to unprepare it
; It *should* be prepared at this point if it was ever used.
If waveHeaders(i)\dwFlags & #WHDR_PREPARED
; 5. Retry loop for waveOutUnprepareHeader_. This function unlocks the buffer memory.
; Even without reset, retries might be needed if timing is tight.
Define retries.i = 0
Define maxRetries.i = 50 ; Wait up to 50 * 10ms = 500ms
Repeat
result = waveOutUnprepareHeader_(hWaveOut, @waveHeaders(i), SizeOf(WAVEHDR))
If result = 0 ; MMSYSERR_NOERROR = Success
; Successfully unprepared
Debug "Unprepared buffer " + Str(i) + " successfully."
Break ; Exit repeat loop
; 6. [Hang Prevention] If unprepare failed, check if the prepared flag got cleared anyway
ElseIf Not (waveHeaders(i)\dwFlags & #WHDR_PREPARED)
Debug "Buffer " + Str(i) + " flag cleared during unprepare attempt. Assuming success."
result = 0 ; Treat as success for the purpose of breaking the loop
Break ; Exit repeat loop
EndIf
; Wait briefly before retrying
Delay(10)
retries + 1
Until retries >= maxRetries ; Stop trying after a reasonable timeout
; 7. [Hang Prevention] If still unprepared after retries, log a warning but DO NOT HANG.
If result <> 0 ; Check if the loop exited due to failure after retries
Debug "Warning: Failed to unprepare header " + Str(i) + " after retries. Error: " + Str(result) + ". Flag: " + Str(waveHeaders(i)\dwFlags)
EndIf
Else
; Buffer wasn't marked as prepared initially (or flag cleared before loop), no need to unprepare
Debug "Buffer " + Str(i) + " was not marked as prepared before loop (dwFlags=" + Str(waveHeaders(i)\dwFlags) + ")."
EndIf
; 8. Free the memory allocated for the audio samples.
FreeMemory(*waveBuffers(i))
*waveBuffers(i) = 0 ; Nullify the pointer after freeing to prevent dangling pointers.
Debug "Freed buffer memory " + Str(i)
Else
Debug "Pointer for buffer " + Str(i) + " was null, skipping free."
EndIf
Next ; End buffer loop
Debug "Attempting to close audio device..."
; 9. Close the waveOut device handle, releasing associated system resources.
result = waveOutClose_(hWaveOut)
If result <> 0
Debug "Warning: waveOutClose_ failed. Error: " + Str(result)
Else
Debug "Audio device closed successfully." ; Callback should receive WOM_CLOSE now
EndIf
Debug "Exiting."
End