Home
JAQForum Ver 20.06
Log In or Join  
Active Topics
Local Time 19:43 20 Apr 2024 Privacy Policy
Jump to

Notice. New forum software under development. It's going to miss a few functions and look a bit ugly for a while, but I'm working on it full time now as the old forum was too unstable. Couple days, all good. If you notice any issues, please contact me.

Forum Index : Microcontroller and PC projects : PicoMite: driving a passive buzzer

     Page 4 of 4    
Author Message
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3833
Posted: 10:44pm 03 Dec 2022
Copy link to clipboard 
Print this post

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
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3496
Posted: 12:35am 04 Dec 2022
Copy link to clipboard 
Print this post

Nice Tom!

Have seen people use floppy drive head motors playing similar music

But the 4 buzzers work also...

Volhout
PicomiteVGA PETSCII ROBOTS
 
Turbo46

Guru

Joined: 24/12/2017
Location: Australia
Posts: 1592
Posted: 01:03am 04 Dec 2022
Copy link to clipboard 
Print this post

I thought that calling those things 'buzzers' was a bit of a misnomer but having heard them, the description is quite accurate

Thankfully it plays on the CMM2 and sounds very good!  

Bill
Keep safe. Live long and prosper.
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3496
Posted: 07:51am 04 Dec 2022
Copy link to clipboard 
Print this post

https://youtu.be/Oi6MKmL9a0U

Just connect your 4 pwm outputs to 4 step inputs of stepper drivers.
Edited 2022-12-04 17:53 by Volhout
PicomiteVGA PETSCII ROBOTS
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3833
Posted: 11:11pm 15 Dec 2022
Copy link to clipboard 
Print this post

  Volhout said  No inductors, try this ....


My 4.7nF capacitors and an ~1.5cm 8 ohm speaker finally turned up so I was able to build @Volhout's inductor free circuit from a couple of pages back (using a 47 ohm resistor I had in stock rather than the 33 ohm specified). It sounds good (not that I can necessarily tell as I appear to be on the path to deafness), but it is very quiet, and will presumably get worse when I am running it from a 3.7V LiPo rather than 4.8ish V USB? Is this just "life" with a small speaker and no enclosure/cone or is there something I can do? Note that I only have room for a single speaker.

Thanks in advance,

Tom
Edited 2022-12-16 09:12 by thwill
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
Turbo46

Guru

Joined: 24/12/2017
Location: Australia
Posts: 1592
Posted: 11:26pm 15 Dec 2022
Copy link to clipboard 
Print this post

You could:

Add and extra cell just for the audio and use Phil's amplifier or:

Add another circuit the same as you have and drive it 180 degrees out of phase with the other then connect the speaker between the two outputs as in a 'bridge' amplifier.

Edit: Or use a boost regulator to produce a higher voltage for Phil's amplifier.

Bill
Edited 2022-12-16 12:11 by Turbo46
Keep safe. Live long and prosper.
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3496
Posted: 07:50am 16 Dec 2022
Copy link to clipboard 
Print this post

Hi Tom,

You are in a difficult situation. And I am not sure what I can do to help you, except experiment.
In essence the loudspeaker needs to pressurize air, and the air pressure wavefront travels to your ear. To build a wavefront it needs to displace air, and is doing so by moving the front surface. A speaker can only displace a volume that is equal to it's surface area multiplied by the movement of that surface.

If you look at a speaker with a diameter of 1.5cm, it needs 10x the surface movement of a speaker with diameter 4.5 cm. Or.. with similar movement the 5cm speaker is 10x as loud. So your small speaker has a disadvantage. YTou need to drive it really hard to get some audio out of it.

For computer games sounds (at least the vintage games) distortion is not really an issue (it even gives a charme to the game) so you can drive the speakers really hard.

The circuit you build (without the inductor) is designed to give low distortion, you can use it to play WAV's directly ripped from a CD, and it sounds good. But it does not drive the speaker to the max to avoid distortion.

I have no datasheet on the speaker you use, but if it is the same 16 ohm BUZZER discussed earlier, you would need to change the 47 to 33 or even 22 ohm (which will impact battery life seriously) to drive it to the optimum. But still, you would not get into distortion.

Can you share the spec of the speaker, or confirm it is still the same BUZZER.
Then we can look and see if we can work on a better solution, driven from 3.7V...4.V

Acoustics... Yeah....
When a wavefront is traveling to you , and the inverse wavefront (reflected by whatever PCB/walls/hands) also, you do not get optimal sound. That is "black art" (not really) that companies tie their name to. That is why you see "BOSE" on laptops, that have the same problem. Small speakers, tight space, how to get the best sound quality.

Volhout

P.S. if you know you are going to have a small speaker, low frequencies are typically not output well, you may also gain a bit by making the game sounds higher frequency. Just tune the whole melody 1 octave up.
Edited 2022-12-16 17:54 by Volhout
PicomiteVGA PETSCII ROBOTS
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3833
Posted: 10:28am 16 Dec 2022
Copy link to clipboard 
Print this post

Hi @Volhout,

Thank you (and Bill and others) for continuing to take an interest.

  Volhout said  For computer games sounds (at least the vintage games) distortion is not really an issue (it even gives a charme to the game) so you can drive the speakers really hard.


You have correctly identified the use-case.

  Volhout said  I have no datasheet on the speaker you use, but if it is the same 16 ohm BUZZER discussed earlier, you would need to change the 47 to 33 or even 22 ohm (which will impact battery life seriously) to drive it to the optimum. But still, you would not get into distortion.


I have upgraded and have a choice of two small 8R speakers:

  - https://bitsboxuk.com/data/sensors/st059.pdf
  - https://bitsboxuk.com/data/sensors/st060.pdf

At the moment I am driving from the PicoMite VSYS (which when on battery is being fed by a 2500mAh, 3.7V LiPo through a 1N5818 schottky diode).

  Volhou said  P.S. if you know you are going to have a small speaker, low frequencies are typically not output well, you may also gain a bit by making the game sounds higher frequency. Just tune the whole melody 1 octave up.


Thanks, I will play with that, and also with the 33/47R resistor (I can try a couple of 68R in parallel) and also see what volume I have having the PicoMite PLAY SOUND work at, it may not be the maximum.

Best wishes,

Tom
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3496
Posted: 12:43pm 16 Dec 2022
Copy link to clipboard 
Print this post

These are 8 ohm. They will not be suitable for my circuit. The circuit you tried is designed for 32ohm and higher impedance.

You will need a decent amplifier to drive 8 ohm. The st060 seems to be the better speaker. The 059 is measured short range and lower power.

Volhout
PicomiteVGA PETSCII ROBOTS
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3833
Posted: 01:11pm 16 Dec 2022
Copy link to clipboard 
Print this post

  Volhout said  These are 8 ohm. They will not be suitable for my circuit. The circuit you tried is designed for 32ohm and higher impedance.


I am easily confused and having to do "monkey see, monkey do", this is not my familiar world of zeroes and ones ;-)

I thought you suggested that circuit when we were discussing the 16R speaker/passive buzzer, not 32R ? - it does also work, if quietly.

  Volhout said  You will need a decent amplifier to drive 8 ohm.


Such as ?

Am I looking at something like this ?

https://www.adafruit.com/product/987

And then do I need a filter as well ?

  Volhout said  The st060 seems to be the better speaker. The 059 is measured short range and lower power.


Thanks, it's the bigger one, though not too big.

Best wishes,

Tom
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3496
Posted: 01:17pm 16 Dec 2022
Copy link to clipboard 
Print this post

Tom,

Can't look into your parts storage, but driving a low impedance speaker requires current. The simplest way to do that from a single cell is use a single cell class D amplifier. I thinks these are available on Aliexpress and others.

A quick alternative could be:

Take a 74HC244 (octal buffer), parallel all 8 channels (inputs tied to eachother, outputs tied to eachother). Run it from 3.7V. Pull enables low (otherwise it doesn't do much). Connect the 8 inputs to the PWM out pin on the PICO. GND=GND. Vdd = 3.7V.
In this configuration it will be a high current (200mA+) PWM output. From the common output an e-cap (100uF 16V) to the speaker. (other side speaker to GND). No filtering, done.

When that audio level is okay, we may add a filter between the speaker and the 74HC244. When not, you definitely need a class D amplifier.

- Use 74HC244, not 74HCT244, not 74LV244, not 74LS244.

Volhout

P.S. I know the pico runs 3.3V and the 74HC244 is connected to 3.7V/4.2V but logic levels will be okay enough for the demo.
Other multiple buffer chips in C-mos may also work (74HC04 (6 inverting buffers), 74HC14 (6 smitt trigger buffers), 74HC240 (8 inverting buffers)).
Edited 2022-12-16 23:20 by Volhout
PicomiteVGA PETSCII ROBOTS
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3833
Posted: 01:24pm 16 Dec 2022
Copy link to clipboard 
Print this post

  Volhout said  Tom,

Can't look into your parts storage,


You would find it wanting, it's stocked for a beginning hobbyist, not for someone who is reaching beyond their grasp .

  Quote  ... but driving a low impedance speaker requires current. The simplest way to do that from a single cell is use a single cell class D amplifier. I thinks these are available on Aliexpress and others.


"single cell" ?

Is that like the one I linked above, and also this Chinese "knock-off":

  https://www.aliexpress.com/item/4001137950571.html

I think my final build is just going to look like a bunch of off the shelf Aliexpress SMD modules ... I suppose you then work out what individual components they have on them and design a PCB for JLPCB (other services are available) to populate.

Best wishes,

Tom
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 3496
Posted: 01:52pm 16 Dec 2022
Copy link to clipboard 
Print this post

Yes, that one....

- do not place any of the jumpers 18/15/12/9, then it is 6dB gain.
- connect loudspeaker to LOUT+/LOUT-
- connect GND to GND
- connect L- to GND
- connect L+ to the output of the 1 transistor filter amplifier.
- connect VDD to 3.7V.

When you connect SHTDN to GND the amplifier will power down. Looks like there is a pullup on the board.

Check if that works. It should be LOUD!! This is on the edge of what the speaker can handle (the -060)

You may need a volume control potmeter between the output of the transistor and the input of the amplifier (10k or so). You need the transistor amplifier as an analog filter. You cannot drive a digital amplifier with a digital input signal. It needs analog.
Edited 2022-12-16 23:56 by Volhout
PicomiteVGA PETSCII ROBOTS
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3833
Posted: 02:17pm 16 Dec 2022
Copy link to clipboard 
Print this post

  Volhout said  Yes, that one.....


Thanks Volhout,

The rate I'm going I could have bought several knock-off gameboys for the money I'm investing in this project on Aliexpress .

  Volhout said  You need the transistor amplifier as an analog filter. You cannot drive a digital amplifier with a digital input signal. It needs analog.


Do you mean the 2k2 and PN2222 amplifier from the very first page of the thread ?

What about the filter, or is that for later ?

Best wishes,

Tom
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 5707
Posted: 03:59pm 16 Dec 2022
Copy link to clipboard 
Print this post

PAM8403 stereo amplifier modules on ebay. Will run from 2V5 to 6V. 4R or more speakers. No good for headphones as they have bridged outputs. 1.90UKP. These are class D and bridged so will probably give plenty of output (about 0.5W per channel at full volume, about 85% efficiency) for what you want. Quite capable of driving proper speakers if you wanted to.

You should put either the simple RC filter from the manual or, much better, Volhout's filter before this. The version with the volume control might be a good idea as 0.5W can be surprisingly loud!
Edited 2022-12-17 02:08 by Mixtel90
Mick

Zilog Inside! nascom.info for Nascom & Gemini
Preliminary MMBasic docs & my PCB designs
 
thwill

Guru

Joined: 16/09/2019
Location: United Kingdom
Posts: 3833
Posted: 04:24pm 16 Dec 2022
Copy link to clipboard 
Print this post

  Mixtel90 said  PAM8403 stereo amplifier modules on ebay. Will run from 2V5 to 6V. 4R or more speakers. No good for headphones as they have bridged outputs. 1.90UKP. These are class D and bridged so will probably give plenty of output (about 0.5W per channel at full volume, about 85% efficiency) for what you want. Quite capable of driving proper speakers if you wanted to.

You should put either the simple RC filter from the manual or, much better, Volhout's filter before this. The version with the volume control might be a good idea as 0.5W can be surprisingly loud!


Ah, through looking at eBay and Aliexpress listings I had persuaded myself that PAM8403 required 5V and thus I couldn't use it without also adding a buck-boost converter hence investing in the more expensive module.

I ordered some nice little pot's to use for volume control (https://www.aliexpress.com/item/32990973515.html), but alas none of this arrives until after Christmas, so I will have to find some other way to amuse myself in the meantime.

Best wishes,

Tom
Edited 2022-12-17 02:30 by thwill
Game*Mite, CMM2 Welcome Tape, Creaky old text adventures
 
Mixtel90

Guru

Joined: 05/10/2019
Location: United Kingdom
Posts: 5707
Posted: 06:05pm 16 Dec 2022
Copy link to clipboard 
Print this post

PAM8403 data sheet
Mick

Zilog Inside! nascom.info for Nascom & Gemini
Preliminary MMBasic docs & my PCB designs
 
     Page 4 of 4    
Print this page


To reply to this topic, you need to log in.

© JAQ Software 2024