PicoMite: driving a passive buzzer


Author Message
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 4354
Posted: 10:44pm 03 Dec 2022      

Hi folks,

With apologies to any music lovers in the shed, but this project got temporarily diverted resulting in this:

https://www.youtube.com/watch?v=x3CWEG2whhk

Here's the code. If you don't have a 4 piece passive buzzer orchestra on hand (shame on you) then you can play it more conventionally by setting RAW_PWM% = 0 at line 15. I've tested with PicoMite and MMB4W, I suspect it will work on a PicoMiteVGA or CMM2 too.

' Code Copyright (c) 2022 Thomas Hugo Williams
' License MIT <https://opensource.org/licenses/MIT>
' For MMBasic 5.07.03

' Musical compositions in MMBasic played via PWM or PLAY SOUND
'   - also encodes music as DATA statements for playing outside of this program.

Option Base 0
Option Default None
Option Explicit On

Option Break 4
On Key 3, on_exit

Const RAW_PWM% = 1

If RAW_PWM% Then
 SetPin GP2,PWM1A
 SetPin GP4,PWM2A
 SetPin GP6,PWM3A
 SetPin GP8,PWM4A
EndIf

Const FILENAME$ = "spmusic"
Const SZ% = 256
'Const NUM_CHANNELS% = music.prompt_for_num_channels%()
Const NUM_CHANNELS% = 4
Const OCTAVE_SHIFT% = 0

Dim channel1%(SZ%), channel2%(SZ%), channel3%(SZ%), channel4%(SZ%), music%(SZ% * 4)
Dim err$
Dim int_time!
Dim FREQUENCY!(127)
Dim music_ptr%

If InStr(MM.Device$, "PicoMite") Then Save FILENAME$ + ".bas"

music.init_globals()

music.run("spring")
music.run("entertainer")
music.run("black_white_rag")

Print "Time in interrupt:" int_time!

on_exit()

' Interrupt routine to stop music and restore default Break Key.
Sub on_exit()
 If RAW_PWM% Then
   Local channel%
   For channel% = 1 To 4 : Pwm channel%, Off : Next
 Else
   Play Stop
 EndIf
 Option Break 3
 End
End Sub

' Prompts user for number of channels to encode / play.
Function music.prompt_for_num_channels%()
 Local s$
 Do
   Line Input "How many channels (1-4)? ", s$
   Select Case Val(s$)
     Case 1 To 4 : Exit Do
   End Select
 Loop
 music.prompt_for_num_channels% = Val(s$)
End Function

' Initialises global variables.
Sub music.init_globals()
 Local i%
 FREQUENCY!(0) = 10.0 ' rest
 ' FREQUENCY!(1) - C0   - 16.35 Hz
 For i% = 1 To 127
   FREQUENCY!(i%) = 440 * 2^((i% - 58) / 12.0)
 Next
End Sub

' Composes, processes, saves and plays the named tune.
Sub music.run(name$)
 music.clear()
 Call "music.compose_" + name$
 music.process()
 music.write_data(name$)
 music.play()
End Sub

' Clears the tune data.
Sub music.clear()
 Local i%
 For i% = 1 To 4
   Execute "LongString Clear channel" + Str$(i%) + "%()"
 Next
 LongString Clear music%()
End Sub

' Parses comma separated list of notes.
'
' @param  channel_idx%  parsed notes is appended to data for this channel.
' @param  s$            comma separated list.
Sub music.parse(channel_idx%, s$)
 Local s_idx% = 1
 Local ch$ = ","
 Do While ch$ = ","
   If music.parse_note%(channel_idx%, s$, s_idx%) <> 0 Then
     Error err$ + " : s_idx = " + Str$(s_idx%)
   EndIf
   Inc s_idx%
   ch$ = Mid$(s$, s_idx%, 1)
   Inc s_idx%
 Loop
End Sub

' Parses single note.
'
' @param  channel_idx%  parsed notes is appended to data for this channel.
' @param  s$            note is parsed from this string ...
' @param  s_idx%        ... starting at this index. On exit this will
'                       contain the index of the last character parsed.
' @return               0 on success, -1 on error. Error message will
'                       be in the global err$ variable.
Function music.parse_note%(channel_idx%, s$, s_idx%)
 music.parse_note% = -1
 Local i%
 Local ch$ = Mid$(s$, s_idx%, 1)

 ' Parse duration.
 Local duration%
 Select Case ch$
   Case "q": duration% = 1
   Case "1", "2", "3", "4": duration% = 2 * Val(ch$)
   Case Else
     err$ = "Syntax error: expected duration"
     Exit Function
 End Select

 Inc s_idx%
 ch$ = Mid$(s$, s_idx%, 1)

 ' Parse note: 0 = Rest, 1 = C0, ...
 Local n%
 Select Case ch$
   Case "A" : n% = 10
   Case "B" : n% = 12
   Case "C" : n% = 1
   Case "D" : n% = 3
   Case "E" : n% = 5
   Case "F" : n% = 6
   Case "G" : n% = 8
   Case "-" : n% = 0
   Case Else
     err$ = "Syntax error: expected note"
     Exit Function
 End Select

 If n% = 0 Then
   For i% = 1 To duration%
     Execute "LongString Append channel" + Str$(channel_idx%) + "%(), Chr$(0)"
   Next
   music.parse_note% = 0
   Exit Function
 EndIf

 Inc s_idx%
 ch$ = Mid$(s$, s_idx%, 1)

 ' Parse b or #.
 Local off% = 0
 Select Case ch$
   Case "b" : off% = -1
   Case "#" : off% = 1
 End Select
 If off% <> 0 Then
   Inc n%, off%
   Inc s_idx%
   ch$ = Mid$(s$, s_idx%, 1)
 EndIf

 ' Parse octave.
 If Not Instr("012345678", ch$) Then
   err$ = "Syntax error: expected octave"
   Exit Function
 EndIf
 Inc n%, 12 * (OCTAVE_SHIFT% + Val(ch$))

 ' Write note into buffer.
 For i% = 1 To duration%
     Execute "LongString Append channel" + Str$(channel_idx%) + "%(), Chr$(n%)"
 Next

 music.parse_note% = 0
End Function

' Combines the individual channels into a single global music%() array.
Sub music.process()
 Local i%, j%

 ' Determine the maximum channel length.
 Local max_len% = 0
 For i% = 1 To NUM_CHANNELS%
   max_len% = Max(max_len%, Eval("LLen(channel" + Str$(i%) + "%())"))
 Next

 ' Pad each channel with rests until they are all the maximum length.
 For i% = 1 To NUM_CHANNELS%
   Do While Eval("LLen(channel" + Str$(i%) + "%())") < max_len%
     Execute "LongString Append channel" + Str$(i%) + "%(), Chr$(0)"
   Loop
 Next

 ' Pad each channel with &hFF until reach multiple of 8,
 ' always include at least one &hFF.
 Do
   Inc max_len%
   For i% = 1 To NUM_CHANNELS%
     Execute "LongString Append channel" + Str$(i%) + "%(), Chr$(&hFF)"
   Next
 Loop Until max_len% Mod 8 = 0

 ' Combine the channels into a single music buffer.
 For i% = 0 To max_len% - 1
   For j% = 1 To NUM_CHANNELS%
     Execute "LongString Append music%(), Chr$(LGetByte(channel" + Str$(j%) + "%(), i%))"
   Next
 Next
End Sub

' Writes music%() array into a file as DATA statements.
Sub music.write_data(name$)
 Local count% = 0, i%, p% = Peek(VarAddr music%()) + 8
 Open name$ + ".inc" For Output As #1
 Print #1, name$ "_data:"
 Print #1, "Data " Format$(LLen(music%()), "%-6g") "' Number of bytes of music data."
 Print #1, "Data " Format$(NUM_CHANNELS%,"%-6g") "' Number of channels."
 For i% = 0 To LLen(music%()) - 1 Step 8
   Print #1, Choice(count% = 0, "Data ", ", ");
   Print #1, "&h" Hex$(Peek(Integer p%), 16);
   Inc p%, 8
   Inc count%
   If count% = 4 Then Print #1 : count% = 0
 Next
 Close #1
End Sub

' Plays the contents of the music%() array using interrupts.
Sub music.play()
 music_ptr% = Peek(VarAddr music%()) + 8
 SetTick 200, music.play_interrupt, 1
 Do While music_ptr% <> 0 : Loop
 SetTick 0, 0, 1
 Play Stop
End Sub

' Interrupt routine playing a single half-beat (per channel) from the music%() array.
Sub music.play_interrupt()
 Local i%, n%, t! = Timer
 For i% = 1 To NUM_CHANNELS%
   n% = Peek(Byte music_ptr%)
   If n% = 255 Then
     Print Str$(i%) ": Halted"
     music_ptr% = 0
     Exit For
   EndIf
   If RAW_PWM% Then
     Pwm i%, FREQUENCY!(n%), (n% > 0) * 5
   Else
     Play Sound i%, B, S, FREQUENCY!(n%), (n% > 0) * 15
   EndIf
   Print Str$(i%) ": " Choice(n% = 0, "Rest", Str$(FREQUENCY!(n%)) + " hz")
   Inc music_ptr%
 Next
 Inc int_time!, Timer - t!
End Sub

Sub music.compose_blart()
 music.parse(1, "qC5,qB4,qF#4,qF4")
End Sub

Sub music.compose_die()
 music.parse(1, "qF6,qE6,qEb6,qD6,qC#6,qC6,qB5,qA#5")
 music.parse(1, "qA5,qAb5,qG5,qF#5,qF5,qE5,qEb5,qD5")
 music.parse(1, "qC#5,qC5,qB4,qA#4,qA4,qAb4,qG4,q-")
End Sub

Sub music.compose_select()
 music.parse(1, "qB4,qG5,qB5,q-")
End Sub

Sub music.compose_wipe()
 music.parse(1, "qG4,qAb4,qA4,qA#4,qB4,qC5,qC#5,qD5")
 music.parse(1, "qEb5,qE5,qF5,qF#5,qG5,qAb5,qA5,qA#5")
 music.parse(1, "qB5,qC6,qC#6,qD6,qEb6,qE6,qF6,q-")
End Sub

' Spring - by Antonio Vivaldi - 4 channels.
Sub music.compose_spring()

 ' ---------- Line 1 ----------
 music.parse(1, "1-,1E5")
 music.parse(2, "1-,1B4")
 music.parse(3, "1E3,qE3,q-")
 music.parse(4, "1E4,qE4,q-")
 Local i%,j%
 For i% = 1 To 2
 music.parse(1, "qG#5,q-,qG#5,q-,1G#5,qF#5,qE5,2B5,qB5,q-,qB5,qA5")
 music.parse(1, "qG#5,q-,qG#5,q-,1G#5,qF#5,qE5,2B5,qB5,q-,qB5,qA5")
 music.parse(1, "1G#5,qA5,qB5,1A5,1G#5,1F#5,1D#5,1B4,1E5")

 music.parse(2, "qE5,q-,qE5,q-,1E5,1-,2G#5,qG#5,q-,qG#5,qF#5")
 music.parse(2, "qE5,q-,qE5,q-,1E5,1-,2G#5,qG#5,q-,qG#5,qF#5")
 music.parse(2, "1E5,qF#5,qG#5,1F#5,1E5,1D#5,3-")

 music.parse(3, "1E3,qE3,q-,1E3,qE3,q-,1E3,qE3,q-,1E3,qE3,q-")
 music.parse(3, "1E3,qE3,q-,1E3,qE3,q-,1E3,qE3,q-,1E3,qE3,q-")
 music.parse(3, "qE3,q-,qE3,q-,qA2,q-,qA#2,q-,2B2,1-,qE3,q-")

 music.parse(4, "1E4,qE4,q-,1E4,qE4,q-,1E4,qE4,q-,1E4,qE4,q-")
 music.parse(4, "1E4,qE4,q-,1E4,qE4,q-,1E4,qE4,q-,1E4,qE4,q-")
 music.parse(4, "qE4,q-,qE4,q-,qA3,q-,qA#3,q-,2B3,1-,qE4,q-")

 ' ---------- Line 2 ----------
 music.parse(1, "qG#5,q-,qG#5,q-,1G#5,qF#5,qE5,2B5,qB5,q-,qB5,qA5")
 music.parse(1, "qG#5,q-,qG#5,q-,1G#5,qF#5,qE5,2B5,qB5,q-,qB5,qA5")
 music.parse(1, "1G#5,qA5,qB5,1A5,1G#5,1F#5,1D#5,1B4,1E5")

 music.parse(2, "qE5,q-,qE5,q-,1E5,1-,2G#5,qG#5,q-,qG#5,qF#5")
 music.parse(2, "qE5,q-,qE5,q-,1E5,1-,2G#5,qG#5,q-,qG#5,qF#5")
 music.parse(2, "1E5,qF#5,qG#5,1F#5,1E5,1D#5,3-")

 music.parse(3, "1E3,qE3,q-,1E3,qE3,q-,1E3,qE3,q-,1E3,qE3,q-")
 music.parse(3, "1E3,qE3,q-,1E3,qE3,q-,1E3,qE3,q-,1E3,qE3,q-")
 music.parse(3, "1E3,qE3,q-,1E3,qE3,q-,1E3,1B3,2E3")

 music.parse(4, "1E4,qE4,q-,1E4,qE4,q-,1E4,qE4,q-,1E4,qE4,q-")
 music.parse(4, "1E4,qE4,q-,1E4,qE4,q-,1E4,qE4,q-,1E4,qE4,q-")
 music.parse(4, "1E4,qE4,q-,1E4,qE4,q-,1E4,1B4,2E4")

 For j% = 1 To 2
 music.parse(1, "1B5,qA5,qG#5,1A5,1B5,1C#6,2B5,1E5")
 music.parse(2, "1G#5,qF#5,qE5,1F#5,1G#5,1A5,2G#5,1-")
 music.parse(3, "1E3,qE3,q-,1E3,qE3,q-,1E3,1B3,1E3,qE3,q-")
 music.parse(4, "1E4,qE4,q-,1E4,qE4,q-,1E4,1B4,1E4,qE4,q-")

 ' ---------- Line 3 ----------
 music.parse(1, "1B5,qA5,qG#5,1A5,1B5,1C#6,2B5,1E5")
 music.parse(1, "1C#6,2B5,1A5,1G#5,qF#5,qE5,2F#5")
 If i% = 2 And j% = 2 Then
   music.parse(1, "2E5,2-")
 Else
   music.parse(1, "2E5,1-,1E5")
 EndIf  

 music.parse(2, "1G#5,qF#5,qE5,1F#5,1G#5,1A5,2G#5,1-")
 music.parse(2, "1A5,2G#5,1F#5,1E5,qD#5,qB4,2-")
 music.parse(2, "2B4,2-")

 music.parse(3, "1E3,qE3,q-,1E3,qE3,q-,1E3,1B3,1E3,qE3,q-")
 music.parse(3, "1E3,qE3,q-,1E3,1B2,1E3,1E2,1B3,1B2")
 music.parse(3, "3E3,qE3,q-")

 music.parse(4, "1E4,qE4,q-,1E4,qE4,q-,1E4,1B4,1E4,qE4,q-")
 music.parse(4, "1E4,qE4,q-,1E4,1B4,1E4,1E3,1B4,1B3")
 music.parse(4, "3E4,qE4,q-")
 Next j%
 Next i%
End Sub

' The Entertainer - by Scott Joplin - 3 channels.
Sub music.compose_entertainer()
 ' ---------- Line 0 ----------

 music.parse(1, "qD4,qD#4")

 music.parse(2, "1-")

 music.parse(3, "1-")

 ' ---------- Line 1 ----------

 music.parse(1, "qE4,1C5,qE4,1C5,qE4,qC5")
 music.parse(1, "2C5,q-,qC5,qD5,qD#5")
 music.parse(1, "qE5,qC5,qD5,qE5,qE5,qB4,1D5")
 music.parse(1, "3C5,qD4,qD#4")

 music.parse(2, "1C4,1C3,1D#3,1E3")
 music.parse(2, "1F3,1G3,1A3,1B3")
 music.parse(2, "1C4,1E3,1F3,1G3")
 music.parse(2, "1C3,1G3,1C4,qC4,q-")

 music.parse(3, "4-")
 music.parse(3, "4-")
 music.parse(3, "4-")
 music.parse(3, "4-")

 ' ---------- Line 2 ----------

 music.parse(1, "qE4,1C5,qE4,1C5,qE4,qC5")
 music.parse(1, "3C5,qA4,qG4")
 music.parse(1, "qF#4,qA4,qC5,qE5,qE5,qD5,qC5,qA4")
 music.parse(1, "3D5,qD4,qD#4")

 music.parse(2, "1C4,1C3,1D#3,1E3")
 music.parse(2, "1F3,1G3,1A3,1C#3")
 music.parse(2, "1D3,1F#3,1A3,1D3")
 music.parse(2, "1G3,1F3,1E3,1D3")

 music.parse(3, "4-")
 music.parse(3, "4-")
 music.parse(3, "4-")
 music.parse(3, "4-")

 ' ---------- Line 3 ----------

 music.parse(1, "qE4,1C5,qE4,1C5,qE4,qC5")
 music.parse(1, "2C5,q-,qC5,qD5,qD#5")
 music.parse(1, "qE5,qC5,qD5,qE5,qE5,qB4,1D5")
 music.parse(1, "2C5,qC5,q-,qC5,qD5")

 music.parse(2, "1C3,1C4,1D#3,1E3")
 music.parse(2, "1F3,1G3,1A3,1B3")
 music.parse(2, "1C4,1E3,1F3,1G3")
 music.parse(2, "1C4,1G3,2C3")

 music.parse(3, "4-")
 music.parse(3, "4-")
 music.parse(3, "4-")
 music.parse(3, "4-")

 ' ---------- Line 4 ----------

 music.parse(1, "qE5,qC5,qD5,qE5,qE5,qC5,qD5,qC5")
 music.parse(1, "qE5,qC5,qD5,qE5,qE5,qC5,qD5,qC5")
 music.parse(1, "qE5,qC5,qD5,qE5,qE5,qB4,1D5")
 music.parse(1, "2C5,qC5,qE4,qF4,qF#4")

 music.parse(2, "qC5,q-,qC5,q-,qBb4,q-,qBb4,q-")
 music.parse(2, "qA4,q-,qA4,q-,qAb4,q-,qAb4,q-")
 music.parse(2, "qG4,q-,qG4,q-,1G4,1-")
 music.parse(2, "1-,1G3,qC4,q-,1-")

 music.parse(3, "qC4,q-,qC4,q-,qBb3,q-,qBb3,q-")
 music.parse(3, "qA3,q-,qA3,q-,qAb3,q-,qAb3,q-")
 music.parse(3, "qG3,q-,qG3,q-,1G3,1-")
 music.parse(3, "1-,1G2,qC3,q-,1-")

 ' ---------- Line 5 ----------

 music.parse(1, "1G4,qA4,qG4,qG4,qE4,qF4,qF#4")
 music.parse(1, "1G4,qA4,qG4,qG4,qE5,qC5,qG4")
 music.parse(1, "qA4,qB4,qC5,qD5,qE5,qD5,qC5,qD5")
 music.parse(1, "2G4,qG4,qE4,qF4,qF#4")

 music.parse(2, "1E4,qF4,qE4,qE4,q-,1-")
 music.parse(2, "1E4,qF4,qE4,qE4,q-,1-")
 music.parse(2, "4-")
 music.parse(2, "4-")

 music.parse(3, "1C4,1-,1G3,1-")
 music.parse(3, "1C4,1-,1G3,1-")
 music.parse(3, "2F3,2G3")
 music.parse(3, "1C3,1G3,1C4,1-")

 ' ---------- Line 6 ----------

 music.parse(1, "1G4,qA4,qG4,qG4,qE4,qF4,qF#4")
 music.parse(1, "1G4,qA4,qG4,qG4,qG4,qA4,qA#4")
 music.parse(1, "1B4,q-,1B4,qA4,qF#4,qD4")
 music.parse(1, "2G4,qG4,qE4,qF4,qF#4")

 music.parse(2, "1E4,qF4,qE4,qE4,q-,1-")
 music.parse(2, "1E4,qF4,qE4,qE4,q-,1-")
 music.parse(2, "4-")
 music.parse(2, "4-")

 music.parse(3, "1C4,1-,1G3,1-")
 music.parse(3, "1C4,1-,1G3,1C#4")
 music.parse(3, "2D4,2D3")
 music.parse(3, "qG3,q-,qG3,q-,1A3,1B3")

 ' ---------- Line 7 ----------

 music.parse(1, "1G4,qA4,qG4,qG4,qE4,qF4,qF#4")
 music.parse(1, "1G4,qA4,qG4,qG4,qE5,qC5,qG4")
 music.parse(1, "qA4,qB4,qC5,qD5,qE5,qD5,qC5,qD5")
 music.parse(1, "2C5,qC5,qG4,qF#4,qG4")

 music.parse(2, "1E4,qF4,qE4,qE4,q-,1-")
 music.parse(2, "1E4,qF4,qE4,qE4,q-,1-")
 music.parse(2, "4-")
 music.parse(2, "4-")

 music.parse(3, "1C4,1-,1G3,1-")
 music.parse(3, "1C4,1-,1G3,1-")
 music.parse(3, "2F3,2G3")
 music.parse(3, "1C3,1G3,1C4,1-")

 ' ---------- Line 8 ----------

 music.parse(1, "1C5,qA4,qC5,qC5,qA4,qC5,qA4")
 music.parse(1, "qG4,qC5,qE5,qG5,qG5,qE5,qC5,qG4")
 music.parse(1, "1A4,1C5,qE5,1D5,qD5")
 music.parse(1, "3C5,1-")

 music.parse(2, "qF4,q-,qF4,q-,qF#4,q-,qF#4,q-")
 music.parse(2, "qG4,q-,qG4,q-,qE4,q-,qE4,q-")
 music.parse(2, "qF4,q-,qF4,q-,qG4,q-,qG4,q-")
 music.parse(2, "1C5,1G4,1C4,1-")

 music.parse(3, "qF3,q-,qF3,q-,qF#3,q-,qF#3,q-")
 music.parse(3, "qG3,q-,qG3,q-,qE3,q-,qE3,q-")
 music.parse(3, "qF3,q-,qF3,q-,qG3,q-,qG3,q-")
 music.parse(3, "1C4,1G3,1C3,1-")
End Sub

' The Black & White Rag - by Winifred Atwell - 3 channels.
Sub music.compose_black_white_rag()
 music.parse(1, "1E5,qD#5,1E5,qC#5,1D5")
 music.parse(1, "qA#4,1B4,qF4,qG4,qE4,1D4")
 music.parse(1, "qD5,q-,qD5,q-,qD5,q-,qD5,q-")
 music.parse(1, "1D5,qD5,q-,1D5,1C#5")

 music.parse(2, "1A4,qD#4,1E4,qC#3,1D4")
 music.parse(2, "qA#3,1B3,qF3,qG3,qE3,1D3")
 music.parse(2, "1D4,1D4,1D3,1D4")
 music.parse(2, "1D3,1D3,1-,1-")

 music.parse(3, "4-")
 music.parse(3, "4-")
 music.parse(3, "4-")
 music.parse(3, "4-")

 Local i%
 For i% = 1 To 2
 music.parse(1, "qC5,qD5,qF5,qC5,qD5,qF5,qC5,qD5")
 music.parse(2, "1D3,1C4,1A2,1C4")
 music.parse(3, "1A1,1D3,1E1,1D3")

 music.parse(1, "qF5,qC5,qD5,1F5,qE5,qD5,qC5")
 music.parse(1, "qA#4,qB4,qE5,qA#4,qB4,qE5,qA#4,qB4")
 music.parse(1, "qE5,qA#4,qB4,1E5,qD5,qB4,qG4")
 music.parse(1, "1C5,qB4,1C5,qB4,1C5")
 music.parse(1, "1A4,qG#4,1A4,qG#4,1A4")

 music.parse(2, "1D3,1D#3,1E3,1F3")
 music.parse(2, "1G3,1B3,1D3,1B3")
 music.parse(2, "1G3,1G#3,1A3,1B3")
 music.parse(2, "1A3,1C4,1D3,1C4")
 music.parse(2, "1A3,1C4,1D3,1C4")

 music.parse(3, "1D2,1D#2,1E2,1F2")
 music.parse(3, "1G2,1D3,1G2,1D3")
 music.parse(3, "1G2,1G#2,1A2,1B2")
 music.parse(3, "1A2,1D3,1D2,1D3")
 music.parse(3, "1A2,1D3,1D2,1D3")

 music.parse(1, "4-")
 music.parse(1, "q-,qG4,qB4,qD5,q-,qG5,qB5,qD6")
 music.parse(1, "qC5,qD5,qF5,qC5,qD5,qF5,qC5,qD5")
 music.parse(1, "qF5,qC5,qD5,1F5,qE5,qD5,qC5")
 music.parse(1, "qA#4,qB4,qE5,qA#4,qB4,qE5,qA#4,qB4")

 music.parse(2, "q-,qG2,qB2,qD3,q-,qG3,qB3,qD4")
 music.parse(2, "4-")
 music.parse(2, "1D3,1C4,1A2,1C4")
 music.parse(2, "1D3,1D#3,1E3,1F3")
 music.parse(2, "1G3,1B3,1D3,1B3")

 music.parse(3, "4-")
 music.parse(3, "4-")
 music.parse(3, "1D2,1D3,1A1,1D3")
 music.parse(3, "1D2,1D#2,1E2,1F2")
 music.parse(3, "1G2,1D3,1D2,1D3")

 music.parse(1, "qE5,qA#4,qB4,1E5,qD5,qC5,qB4")
 music.parse(1, "qE5,qE4,qG#4,1E5,qD5,qC5,qB4")
 music.parse(1, "qA4,qG#4,qA4,1E5,qD5,qC5,qA4")
 music.parse(1, "qB4,qD4,qG4,1B4,qG4,1A4")
 music.parse(1, "2G4,1G5,1-")

 music.parse(2, "1G3,1G#3,1A3,1B3")
 music.parse(2, "1G#3,1D4,1E3,1D4")
 music.parse(2, "1A3,1C4,1E3,1C4")
 music.parse(2, "1D3,1B3,1D3,1F3")
 music.parse(2, "2D4,1D5,1-")

 music.parse(3, "1G2,1G#2,1A2,1B2")
 music.parse(3, "1G#2,1E3,1E2,1E3")
 music.parse(3, "1A2,1E3,1E2,1E3")
 music.parse(3, "1D2,1D3,1D2,1F2")
 music.parse(3, "2B3,1B4,1-")
 Next
End Sub


Best wishes,

Tom
Edited 2022-12-04 09:09 by thwill