Home
JAQForum Ver 24.01
Log In or Join  
Active Topics
Local Time 03:44 30 Jul 2025 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 : Background Music  (a Kind of ASCII ART)

Author Message
Martin H.

Guru

Joined: 04/06/2022
Location: Germany
Posts: 1231
Posted: 10:59am 17 Oct 2022
Copy link to clipboard 
Print this post

After a discussion with Volhout how to Tokenise Music,here is my proposal.
I tried out MidiNote and store as Ascii
as the Note C2 starts at 36 and B7 is 107 this range should cover almost all possibilities
We are able to store a complete Voice of a Melody in one String where Every Note  uses just one Byte..
As 36 is the First Character that we use, we dont get in trouble with the " Char (34)

the Player is "Tick" driven so it runs independend in Background and you can insert your Main Program in Frontground.
This excample is just 2 Voice polyphone but 4 Voce will be possible


'Test ASCII Coded Background Music (with reference to MIDI note numbers)
'limitation to 255 Notes song length,since the max Stringlenght is 255
' Exeptions: Space pauses the Note, # ends the song
Dim FRQ%(128)
Dim notes%(4,255)
mt%=1:CNT%=1
Dim ADSR%(10)=(25,25,25,24,22,20,18,13,7,3,0)
Dim ADSR2%(10)=(2,8,12,17,22,20,15,10,5,2,0)
For f%=36 To 107:Read FRQ%(f%):Next :FRQ%(32)=1
'Sample Music just for this Test
AA$="<>@ C C EC @ < > @ @ > < >    <>@ C C EC @ < > @ @ > > <   "
AA$=AA$+"A  A  E E  E C C @ >  <>@ C C EC @ < >@ @ > > <   #"
AB$="  $0$0$0$0$0$0$0$0$0$0$0$0+7+7+7$0$0$0$0$0$0$0$0$0$0$0$0$"
AB$=AB$+"                           $0$0$0$0$0$0$0$0$0+7+7$ #"
cnt%=Len(AA$):Spos%=1
SetTick 20, music,4
MODE 2
Do
Colour RGB(255,mt%*25,255)
Print @(1,1)"'your Program here"
Loop

Sub music
Local Fcent%,F1%,F2%,FL%,FR%
 If mt%=1 Then
  nt%=Asc(Mid$(AA$,CNT%,1))
  If nt%=35 Then CNT%=1:nt%=Asc(Mid$(AA$,CNT%,1))
  Freq1%=FRQ%(nt%)
  Freq2%=FRQ%(Asc(Mid$(AB$,CNT%,1)))
Inc cnt%
EndIf
'Pseudo Chorus
Fcent%=Int(Freq1%/100)
FL%=Freq1% - Fcent%
FR%=Freq1% + Fcent%
EndIf
If Freq1%>1 Then Play sound 1,"L","q",FL%,adsr%(mt%):Play sound 3,"R","q",FR%,a
dsr%(mt%)
If Freq2%>1  Then Play sound 2,"B","w",Freq2%,adsr2%(mt%)
Inc mt%:If mt%>10 Then mt%=1

End Sub

'Note to Frequency, starting with C2 (Midi 36) up to B7 (Midi 107)
Data 65,69,73,78,82,87,93,98,104,110,117,123
Data 131,139,147,156,165,175,185,196,208,220,233,247
Data 262,277,294,311,330,349,370,392,415,440,466,494
Data 523,554,587,622,659,698,740,784,831,880,932,988,1047
Data 1109,1175,1245,1319,1397,1480,1568,1661,1760,1865,1976
Data 2093,2217,2349,2489,2637,2794,2960,3136,3322,3520,3729,3951


Cheers
  Martin
Edited 2022-10-17 21:01 by Martin H.
'no comment
 
Amnesie
Guru

Joined: 30/06/2020
Location: Germany
Posts: 662
Posted: 12:07pm 17 Oct 2022
Copy link to clipboard 
Print this post

This is really impressive and a great idea for small games! Never thought it would be possible  

-Daniel
 
stanleyella

Guru

Joined: 25/06/2022
Location: United Kingdom
Posts: 2562
Posted: 01:18pm 17 Oct 2022
Copy link to clipboard 
Print this post

I will try with NON vga mmbasic... It will take a while to understand the code,
(a few comments helps   )
I can not be bothered looking for a piezo speaker so I will use a powered one.
is Inc cnt% same as cnt%=cnt%+1 or cnt%++ ?
SetTick 20, music,4 ? Set up interrupt in NON vga mmb?
Thanks for sharing.
 
Martin H.

Guru

Joined: 04/06/2022
Location: Germany
Posts: 1231
Posted: 01:35pm 17 Oct 2022
Copy link to clipboard 
Print this post

Hi Stanley,
a few comments:


Dim FRQ%(128)

there the Frequency Values from the Data are stored,

Dim notes%(4,255)
can be deleted, was from a previous attempt.

Dim ADSR%(10)=(25,25,25,24,22,20,18,13,7,3,0)
Dim ADSR2%(10)=(2,8,12,17,22,20,15,10,5,2,0)

The ADSR volume curve for the "Instruments"

the Sub is called 10 times per Note to process the ADSR volume curve,
Therefor mt% is counting, if it reaches 11 it is set to 1 and a new tone is read.

the Pseudo Chorus part detunes the lead voyce 1% up and down and uses 2 Oszilators to create a kind of chorus / flanging effect

Sub music
Local Fcent%,F1%,F2%,FL%,FR%
If mt%=1 Then
 nt%=Asc(Mid$(AA$,CNT%,1))
 If nt%=35 Then CNT%=1:nt%=Asc(Mid$(AA$,CNT%,1))
 Freq1%=FRQ%(nt%)
 Freq2%=FRQ%(Asc(Mid$(AB$,CNT%,1)))
Inc cnt%
EndIf
'Pseudo Chorus
Fcent%=Int(Freq1%/100)
FL%=Freq1% - Fcent%
FR%=Freq1% + Fcent%
EndIf
If Freq1%>1 Then Play sound 1,"L","q",FL%,adsr%(mt%):Play sound 3,"R","q",FR%,a
dsr%(mt%)
If Freq2%>1  Then Play sound 2,"B","w",Freq2%,adsr2%(mt%)
Inc mt%:If mt%>10 Then mt%=1
End Sub

Edited 2022-10-18 01:17 by Martin H.
'no comment
 
stanleyella

Guru

Joined: 25/06/2022
Location: United Kingdom
Posts: 2562
Posted: 03:08pm 17 Oct 2022
Copy link to clipboard 
Print this post

  Martin H. said  Hi Stanley,
this will also run on Non VGA Picomite, I use headphones on my , with four resistors as Voltage-deviders and 2 small Elcos 10 uF for DC Suppression.

Inc c%
is the same cnt%=cnt%+1 but a little faster.

SetTick 20, music,4  

Sets Interrupt chanal 4 to jump every 20 mS to the Suproutine "music"


Thanks. I got the vga mmb guide but not used vga mmb yet.
ok my loss but the non vga is more my interest BUT the 2 could overlap ie vga 320x240 to lcd 320x240. vga version will be faster but I am liking lcd support so there will be speed differences but still my preference. vga uses second processor, so will be faster for graphics but lcd can be fast.
it is the touch features I am interested in because they are well implemented, for a resistive display very good.
It will never be as good as vga for games but some doable.
 
Martin H.

Guru

Joined: 04/06/2022
Location: Germany
Posts: 1231
Posted: 03:19pm 17 Oct 2022
Copy link to clipboard 
Print this post

edited the Comment while you answert, plz re-read the last Comment  
Edited 2022-10-18 01:29 by Martin H.
'no comment
 
TassyJim

Guru

Joined: 07/08/2011
Location: Australia
Posts: 6280
Posted: 07:52pm 17 Oct 2022
Copy link to clipboard 
Print this post

A few years ago, I wrote programs for the CMM2 to play QuickBasic sounds and Nocia phone sounds.
Both programs should run on the pico.

Edit: The pico doesn't seem to trigger an interrupt at the end of a TONE, only at the end of playing a WAV file. This means the program can be used to convert to another format but not play in the background on a pico.


You can either use them to play the source as is or convert sound tracks to the format use in other program.
The QB sound program
 ' playQB by TassyJim August 2020
 ' To PLAY Quick Basic style sound strings.
 ' you can either use the function in your program directly or,
 ' more likely, use it to convert QBasic sound strings to DATA listings
 ' and use PLAY TONE in your progams
 '
 OPTION EXPLICIT
 
 DIM playdone, p
 DIM dl$(150) ' array to store the converted data lines
 dl$(0) = "ToneData:" ' label for DATA
 p = 1
 dl$(p) = "  DATA "
 ' samples taken from QBasic games
 PLAYQB "MBT120L64O4cP32dP32eP32fP32gP32fP32eP32dP32c"'"T180 o2 P2 P8 L8 GGG L2 E-"+"P24 P8 L8 FFF L2 D"
 melodyEnd
 PAUSE 500
 PLAYQB "MBMST220L64O1B"'"T120MLO2L1Eee"
 melodyEnd
 PAUSE 500
 PLAYQB "MBMST220L64O6B"'"t120o1l16b9n0baan0bn0bn0baaan0b9n0baan0b"
 melodyEnd
 PAUSE 500
 PLAYQB  "o2l16e-9n0e-d-d-n0e-n0e-n0e-d-d-d-n0e-9n0e-d-d-n0e-"
 melodyEnd
 PAUSE 500
 PLAYQB  "o2l16g-9n0g-een0g-n0g-n0g-eeen0g-9n0g-een0g-"
 melodyEnd
 PAUSE 500
 PLAYQB "o2l16b9n0baan0g-n0g-n0g-eeen0o1b9n0baan0b"
 melodyEnd
 '"MBT120L64O4cP32dP32eP32fP32gP32fP32eP32dP32c"
 "MBMST220L64O1B"
 "MBMST220L64O1B"
 "MBMST220L64O6B"
 
 printOutput
 
END
 
SUB playQB notation$
 ' play a quickbasic string
 LOCAL ch$, item$, shift$= " "
 LOCAL AS INTEGER nNum, tempo, quarter, value, n
 LOCAL AS FLOAT pl, pauseTime, dur, ext, nTone
 LOCAL octave = 2
 notation$ = notation$ + " "
 tempo = 120
 quarter = 500  ' length of quarter note in mS
 dur = quarter
 pl = 7/8 ' normal note length
 playdone = 1
 FOR n = 1 TO LEN(notation$)
   'print item$;
   ch$ = MID$(notation$,n,1)
   SELECT CASE ch$
     CASE "0","1","2","3","4","5","6","7","8","9"
       value = value * 10 + VAL(ch$)
     CASE ">"
       IF octave < 6 THEN octave = octave + 1
     CASE "<"
       IF octave > 0 THEN octave = octave - 1
     CASE "#","+"
       shift$ = "#"
     CASE "-"
       shift$ = "-"
     CASE "."
       IF ext = 0 THEN
         ext = dur/2
       ELSE
         ext = dur*3/4
       ENDIF
     CASE "X"
       ' ignore
     CASE ELSE
       '      print "Value =  ";value 'DEBUG
       SELECT CASE item$
           
         CASE "A","B","C","D","E","F","G"
           nNum = Note2nNum(item$+shift$, octave+2)
           nTone = nNum2Tone(nNum)
           DO : LOOP UNTIL playdone <> 0 : playdone=0
           PLAY TONE nTone, nTone, (dur + ext) * pl, playint
           dl$(p)=dl$(p)+STR$(nTone,4,1)+","+STR$((dur + ext) * pl,5,1)
           IF LEN(dl$(p)) < 65 THEN ' check for new line required
             dl$(p)=dl$(p)+","
           ELSE
             p = p+1
             dl$(p) = "  DATA "
           ENDIF
           IF pl < 1 THEN
             DO : LOOP UNTIL playdone <> 0 : playdone=0
             PLAY TONE 0, 0, (dur + ext) * (1 - pl), playint
             dl$(p)=dl$(p)+STR$(0,4,1)+","+STR$((dur + ext) * (1 - pl),5,1)
             IF LEN(dl$(p)) < 65 THEN ' check for new line required
               dl$(p)=dl$(p)+","
             ELSE
               p = p+1
               dl$(p) = "  DATA "
             ENDIF
           ENDIF
           ext = 0
           item$ = UCASE$(ch$)
           value = 0
         CASE "T"
           tempo = value
           quarter = 60000 / tempo
           item$ = UCASE$(ch$)
           value = 0
           '            print "quarter = ";quarter 'DEBUG
         CASE "L"
           dur = quarter * 4 / value
           item$ = UCASE$(ch$)
           value = 0
           '            print "duration = ";duration 'DEBUG
         CASE "O"
           octave = value
           item$ = UCASE$(ch$)
           value = 0
         CASE "P"
           pauseTime = quarter * 4 / value
           DO : LOOP UNTIL playdone <> 0 : playdone=0
           PLAY TONE 0, 0, pausetime, playint
           dl$(p)=dl$(p)+STR$(0,4,1)+","+STR$(pausetime,5,1)
           IF LEN(dl$(p)) < 65 THEN ' check for new line required
             dl$(p)=dl$(p)+","
           ELSE
             p = p+1
             dl$(p) = "  DATA "
           ENDIF
           item$ = UCASE$(ch$)
           value = 0
         CASE "T"
           tempo = value
           quarter = 60000/tempo
           item$ = UCASE$(ch$)
           value = 0
         CASE "M"
           IF UCASE$(ch$) = "N" THEN pl = 7/8
           IF UCASE$(ch$) = "L" THEN pl = 1
           IF UCASE$(ch$) = "S" THEN pl = 3/4
           item$ = " "
           value = 0
           ' if ucase$(ch$) = "F" then ' foreground
           ' if ucase$(ch$) = "B" then ' background
         CASE "N"
           IF value = 0 THEN
             DO : LOOP UNTIL playdone <> 0 : playdone=0
             PLAY TONE 0, 0, (dur + ext) , playint
             dl$(p)=dl$(p)+STR$(0,4,1)+","+STR$((dur + ext),5,1)
             IF LEN(dl$(p)) < 65 THEN ' check for new line required
               dl$(p)=dl$(p)+","
             ELSE
               p = p+1
               dl$(p) = "  DATA "
             ENDIF
           ELSE
             nTone = nNum2Tone(value+24)
             DO : LOOP UNTIL playdone <> 0 : playdone=0
             PLAY TONE nTone, nTone, (dur + ext) * pl, playint
             dl$(p)=dl$(p)+STR$(nTone,4,1)+","+STR$((dur + ext) * pl,5,1)
             IF LEN(dl$(p)) < 65 THEN ' check for new line required
               dl$(p)=dl$(p)+","
             ELSE
               p = p+1
               dl$(p) = "  DATA "
             ENDIF
             IF pl < 1 THEN
               DO : LOOP UNTIL playdone <> 0 : playdone=0
               PLAY TONE 0, 0, (dur + ext) * (1 - pl), playint
               dl$(p)=dl$(p)+STR$(0,4,1)+","+STR$((dur + ext) * (1 - pl),5,1)
               IF LEN(dl$(p)) < 65 THEN ' check for new line required
                 dl$(p)=dl$(p)+","
               ELSE
                 p = p+1
                 dl$(p) = "  DATA "
               ENDIF
             ENDIF
             ext = 0
           ENDIF
           item$ = UCASE$(ch$)
           value = 0
         CASE ELSE
           item$ = UCASE$(ch$)
           value = 0
       END SELECT
       shift$ = " "
   END SELECT
 NEXT n
 DO : LOOP UNTIL playdone <> 0 : playdone=0
END SUB
 
SUB playint 'end of tone interrupt
 playdone=1
END SUB
 
SUB melodyEnd
 dl$(p) = dl$(p)+" 0, 0"
 p = p+1
 dl$(p) = "  "
 p = p+1
 dl$(p) = "  DATA "
END SUB
 
 
FUNCTION Note2nNum(Note$, octave AS INTEGER) AS INTEGER
 ' given music note and octave, return piano key number
 LOCAL allNotes$ = "C C#D D#E F F#G G#A A#B "
 LOCAL altNotes$ = "C D-D E-E F G-G A-A B-B "
 LOCAL flat
 IF INSTR(Note$,"-") > 0 THEN
   flat = 1
   Note$ = MID$(Note$,1,1)
 ENDIF
 IF LEN(Note$) < 2 THEN Note$ = Note$+" "
 IF Note$ = "E#" THEN Note$ = "F " ' correct for 2 undefined sharps
 IF Note$ = "B#" THEN Note$ = "C " : octave = octave + 1
 IF Note$ = "F-" THEN Note$ = "E " ' correct for 2 undefined flats
 IF Note$ = "C-" THEN Note$ = "B " : octave = octave - 1
 IF INSTR(Note$,"-")>0 THEN
   Note2nNum = INSTR(altNotes$,Note$)/2 +octave*12 - 9 - flat
 ELSE
   Note2nNum = INSTR(allNotes$,Note$)/2 +octave*12 - 9 - flat
 ENDIF
 IF Note2nNum < 1 THEN Note2nNum = Note2nNum + 1 ' correct for negative numbers
END FUNCTION
 
FUNCTION nNum2Tone(noteN AS INTEGER) AS FLOAT
 ' given piano key number return note frequency
 nNum2Tone = 440 * 2^((noteN-49)/12)
END FUNCTION
 
FUNCTION nNum2Octave(noteN AS INTEGER) AS INTEGER
 ' given piano key number, return octave number
 ' if noteN <98 and noteN > 88 then noteN = noteN - 97
   IF     noteN > 87 THEN : nNum2Octave = 8
   ELSEIF noteN > 75 THEN : nNum2Octave = 7
   ELSEIF noteN > 63 THEN : nNum2Octave = 6
   ELSEIF noteN > 51 THEN : nNum2Octave = 5
   ELSEIF noteN > 39 THEN : nNum2Octave = 4
   ELSEIF noteN > 27 THEN : nNum2Octave = 3
   ELSEIF noteN > 15 THEN : nNum2Octave = 2
   ELSEIF noteN > 3 THEN : nNum2Octave = 1
   ELSE                   : nNum2Octave = 0
 ENDIF
END FUNCTION
 
FUNCTION nNum2Note(noteN AS INTEGER) AS STRING
 ' given piano key number, return music note
 LOCAL allnotes$ = "G#A A#B C C#D D#E F F#G G#A "
 noteN = noteN + 12
 IF noteN > 0 THEN
   nNum2Note = MID$(allnotes$,(noteN MOD 12) * 2 +1,2)
 ENDIF
END FUNCTION
 
SUB printOutput
 LOCAL p
 PRINT
 DO
   PRINT dl$(p)
   p = p + 1
 LOOP UNTIL dl$(p) = ""
END SUB


The Nocia phone program.
 ' playNokia by TassyJim August 2020
 ' play Nokia ringtones and convert to tone - duration pairs of DATA
 '
 '
 OPTION EXPLICIT
 
 DIM playdone, n
 DIM source$(11) ' array for the source tune
 DIM dl$(150) ' array to store the converted data lines
 DIM newName$
 DIM d=4, o=5, b=120, pl = 7/8 ' normal note length
 'input "Default length, octave, beats (4,5,120): ";d,o,b
 
 RESTORE testtune 'jinglebells
 PRINT
 ' read the source data into a string array
 ' output is DATA statements in array dl$()
 DO
   n = n + 1
   READ source$(n)
   IF source$(n) = "" THEN EXIT DO
 LOOP
 playNokia
 printOutput
END
 
SUB playNokia
 ' play a nokia string
 ' and convert it to tone, duration DATA pairs
 LOCAL ch$, item$, shift$= " "
 LOCAL AS INTEGER nNum, tempo, quarter, value, n, k, p, lastone
 LOCAL AS FLOAT dur, ext, nTone
 LOCAL octave
 k = INSTR(source$(1),":") ' look for file header
 IF k>0 THEN ' get name and settings
   newName$ = LEFT$(source$(1),k-1)
   k = INSTR(source$(1),"d=") ' default note duration
   IF k THEN
     n = INSTR(k,source$(1),",")-k-2
     d = VAL(MID$(source$(1), k+2,n))
   ENDIF
   k = INSTR(source$(1),"o=") ' default octave
   IF k THEN
     n = INSTR(k,source$(1),",")-k-2
     o = VAL(MID$(source$(1), k+2,n))
   ENDIF
   k = INSTR(source$(1),"b=") ' default tempo
   IF k THEN
     n = INSTR(k,source$(1),":")-k-2
     b = VAL(MID$(source$(1), k+2,n))
   ENDIF
   source$(1) = MID$(source$(1),k+n+3) ' strip off header
   '    print newName$, d, o, b 'DEBUG
   '    print source$(1) 'DEBUG
 ELSE
   newName$ = "Test"
 ENDIF
 dl$(0) = newName$+":" ' label for DATA
 p = 1
 dl$(p) = "  DATA "
 tempo = b
 octave = o
 quarter = 60000 / tempo  ' length of quarter note in mS
 dur = quarter * 4 / d
 'pl = 7/8 ' normal note length
 playdone = 1
 FOR k = 1 TO 11
   IF LEN(source$(k)) = 0 THEN
     source$(k) = "," 'add final comma
     lastone = 1 'and set flag
   ENDIF
   FOR n = 1 TO LEN(source$(k))
     ch$ = UCASE$(MID$(source$(k),n,1))
     SELECT CASE ch$
       CASE "0","1","2","3","4","5","6","7","8","9"
         value = value * 10 + VAL(ch$)
       CASE "A","B","C","D","E","F","G","P"
         item$ = ch$
         IF value = 0 THEN
           dur = quarter * 4 / d
         ELSE
           dur = quarter * 4 / value
         ENDIF
         value = 0
         
       CASE "#","+"
         shift$ = "#"
       CASE "-"
         shift$ = "-"
       CASE "."
         IF ext = 0 THEN
           ext = dur/2
         ELSE
           ext = dur*3/4
         ENDIF
       CASE " "
         ' skip
       CASE ","
         IF value = 0 THEN
           octave = o
         ELSE
           octave = value
         ENDIF
         'playdone=0
         IF item$ = "P" THEN ' rest
         DO : LOOP UNTIL playdone<> 0 : playdone=0
           PLAY TONE 0, 0, (dur + ext), playint
           dl$(p)=dl$(p)+STR$(0,4,1)+","+STR$((dur + ext),5,1)
           IF LEN(dl$(p)) < 65 THEN ' check for new line required
             dl$(p)=dl$(p)+","
           ELSE
             p = p+1
             dl$(p) = "  DATA "
           ENDIF
         ELSE
           nNum = Note2nNum(item$+shift$, octave-1)
           nTone = nNum2Tone(nNum)
           DO : LOOP UNTIL playdone<> 0 : playdone=0
           PLAY TONE nTone, nTone, (dur + ext) * pl, playint
           dl$(p)=dl$(p)+STR$(nTone,4,1)+","+STR$((dur + ext)* pl,5,1)
           IF LEN(dl$(p)) < 65 THEN
             dl$(p)=dl$(p)+","
           ELSE
             p = p+1
             dl$(p) = "  DATA "
           ENDIF
           IF pl < 1 THEN
             DO : LOOP UNTIL playdone<> 0 : playdone=0' wait for current tone to finish
             PLAY TONE 0, 0, (dur + ext) * (1 - pl), playint
             dl$(p)=dl$(p)+STR$(0,4,1)+","+STR$((dur + ext)* (1 - pl),5,1)
             IF LEN(dl$(p)) < 65 THEN
               dl$(p)=dl$(p)+","
             ELSE
               p = p+1
               dl$(p) = "  DATA "
             ENDIF
           ENDIF
         ENDIF
         value = 0
         shift$ = " "
         ext = 0
         
     END SELECT
   NEXT n
   IF lastone = 1 THEN EXIT FOR ' all done!
 NEXT k
 dl$(p) = LEFT$(dl$(p),LEN(dl$(p))-1)+", 0,0"
 dl$(p+1) = "' End of "+newName$
 dl$(p+2) = ""
 DO : LOOP UNTIL playdone<> 0 : playdone=0' wait for current tone to finish
END SUB
 
SUB printOutput
 LOCAL p
 PRINT
 DO
   PRINT dl$(p)
   p = p + 1
 LOOP UNTIL dl$(p) = ""
END SUB
 
SUB playint 'end of tone interrupt
 playdone=1
END SUB
 
SUB melodyEnd
 PRINT "DATA 0, 00"
 PRINT
END SUB
 
 
FUNCTION Note2nNum(Note$, octave AS INTEGER) AS INTEGER
 ' given music note and octave, return piano key number
 LOCAL allNotes$ = "C C#D D#E F F#G G#A A#B "
 LOCAL altNotes$ = "C D-D E-E F G-G A-A B-B "
 LOCAL flat
 IF INSTR(Note$,"-") > 0 THEN
   flat = 1
   Note$ = MID$(Note$,1,1)
 ENDIF
 IF LEN(Note$) < 2 THEN Note$ = Note$+" "
 IF Note$ = "E#" THEN Note$ = "F " ' correct for 2 undefined sharps
 IF Note$ = "B#" THEN Note$ = "C " : octave = octave + 1
 IF Note$ = "F-" THEN Note$ = "E " ' correct for 2 undefined flats
 IF Note$ = "C-" THEN Note$ = "B " : octave = octave - 1
 IF INSTR(Note$,"-")>0 THEN
   Note2nNum = INSTR(altNotes$,Note$)/2 +octave*12 - 9 - flat
 ELSE
   Note2nNum = INSTR(allNotes$,Note$)/2 +octave*12 - 9 - flat
 ENDIF
 IF Note2nNum < 1 THEN Note2nNum = Note2nNum + 1 ' correct for negative numbers
END FUNCTION
 
FUNCTION nNum2Tone(noteN AS INTEGER) AS FLOAT
 ' given piano key number return note frequency
 nNum2Tone = 440 * 2^((noteN-49)/12)
END FUNCTION
 
FUNCTION nNum2Octave(noteN AS INTEGER) AS INTEGER
 ' given piano key number, return octave number
 ' if noteN <98 and noteN > 88 then noteN = noteN - 97
   IF     noteN > 87 THEN : nNum2Octave = 8
   ELSEIF noteN > 75 THEN : nNum2Octave = 7
   ELSEIF noteN > 63 THEN : nNum2Octave = 6
   ELSEIF noteN > 51 THEN : nNum2Octave = 5
   ELSEIF noteN > 39 THEN : nNum2Octave = 4
   ELSEIF noteN > 27 THEN : nNum2Octave = 3
   ELSEIF noteN > 15 THEN : nNum2Octave = 2
   ELSEIF noteN > 3 THEN : nNum2Octave = 1
   ELSE                   : nNum2Octave = 0
 ENDIF
END FUNCTION
 
FUNCTION nNum2Note(noteN AS INTEGER) AS STRING
 ' given piano key number, return music note
 LOCAL allnotes$ = "G#A A#B C C#D D#E F F#G G#A "
 noteN = noteN + 12
 IF noteN > 0 THEN
   nNum2Note = MID$(allnotes$,(noteN MOD 12) * 2 +1,2)
 ENDIF
 
END FUNCTION
 
JingleBells:
 'd=4,o=5,b=125
 DATA "JingleBells:d=4,o=5,b=125:8g,8e6,8d6,8c6,2g,8g,8e6,8d6,8c6,2a,8a,8f6,8e6"
 DATA "8d6,8b,8g,8b,8d6,8g.6,16g6,8f6,8d6,2e6,8g,8e6,8d6,8c6,2g,16f#,8g,8e6"
 DATA "8d6,8c6,2a,8a,8f6,8e6,8d6,8g6,16g6,16f#6,16g6,16f#6,16g6,16g#6,8a.6"
 DATA "16g6,8e6,8d6,c6,g6,8e6,8e6,8e.6,16d#6,8e6,8e6,8e.6,16d#6,8e6,8g6,8c.6"
 DATA "16d6,2e6,8f6,8f6,8f.6,16f6,8f6,8e6,8e6,16e6,16e6,8e6,8d6,8d6,8e6,2d6"
 DATA ""
 

 DATA "g,c7,g,e,g,c.7,8c7,c7,e7,d7,c7,b,c7,2d.7,g,c7,g,e,c,g.,8g,g,e7,d7,c7,b"
 DATA "a,2g,p,g,a.,8b,c7,a,2g,8e,e,g,a,c7,f7,e7,2d7"
 DATA ""
 testtune:
 DATA "g,g,a,f#.,8g,a,b,b,c6,b.,8a,g,a,g,f#,g.,8a,8b,8c6,d6,d6,d6,d.6,8c6,b,c6"
 DATA "c6,c6,c.6,8b,a,b,8c6,8b,8a,8g,b.,8c6,d6,8e6,8c6,b,a,g."
 DATA ""
 
 DATA "8c#,8d,e,c#,d,b4,c#,a4,b4,p,16c#6,16p,16d6,16p,8e6,8p,8c#6,8p,8d6,8p"
 DATA "8b,8p,8c#6,8p,8a,8p,b,p,a4,a4,b4,c#,a4,c#,b4,p,8a,8p,8a,8p,8b,8p,8c#6"
 DATA "8p,8a,8p,8c#6,8p,8b"
 DATA ""


There are plenty of Nocia tunes out there on the 'net and plenty of QuickBasic programs have sound effects embedded in them.

Jim
Edited 2022-10-18 06:39 by TassyJim
VK7JH
MMedit
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 5085
Posted: 08:27am 18 Oct 2022
Copy link to clipboard 
Print this post

In Europe they where sold under the name Nokia. With K.
PicomiteVGA PETSCII ROBOTS
 
Martin H.

Guru

Joined: 04/06/2022
Location: Germany
Posts: 1231
Posted: 08:55am 18 Oct 2022
Copy link to clipboard 
Print this post

  Volhout said  In Europe they where sold under the name Nokia. With K.


'no comment
 
Martin H.

Guru

Joined: 04/06/2022
Location: Germany
Posts: 1231
Posted: 05:53pm 18 Oct 2022
Copy link to clipboard 
Print this post

  TassyJim said  
Edit: The pico doesn't seem to trigger an interrupt at the end of a TONE, only at the end of playing a WAV file. This means the program can be used to convert to another format but not play in the background on a pico.

Jim

if you look at my sub , the Tone ends when mt%>10
That cause the sub to read the next Tone(s).
so, with a little tinkering, it should be possible to use the QB program on the Pico as well. Next 2 Days not Time for Programming, but I will have a look
'no comment
 
matherp
Guru

Joined: 11/12/2012
Location: United Kingdom
Posts: 10303
Posted: 10:30am 19 Oct 2022
Copy link to clipboard 
Print this post

  Quote  Edit: The pico doesn't seem to trigger an interrupt at the end of a TONE, only at the end of playing a WAV file. This means the program can be used to convert to another format but not play in the background on a pico.

This is a bug, it should - will fix

UPDATE: fixed in RC8
Edited 2022-10-19 23:10 by matherp
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 5085
Posted: 05:52pm 19 Oct 2022
Copy link to clipboard 
Print this post

Hi Martin,

I have profiled your code in the first post, and it is impressive sound with all the ADSR and chorus. But it consumes 51% CPU power at 252MHz.

For reference, playing CD music (44.1kHz stereo) from SD card only consumes 27% CPU power at 252MHz. Of coarse that does not support in game sounds...

Volhout
Edited 2022-10-20 04:20 by Volhout
PicomiteVGA PETSCII ROBOTS
 
Martin H.

Guru

Joined: 04/06/2022
Location: Germany
Posts: 1231
Posted: 06:27pm 19 Oct 2022
Copy link to clipboard 
Print this post

  Volhout said  Hi Martin,

I have profiled your code in the first post, and it is impressive sound with all the ADSR and chorus. But it consumes 51% CPU power at 252MHz.

Volhout

well ...  Time for optimisation  
The cent calculation for the Pseudo Chorus is done every 20 mS (with every call of the Sub).
This is not necessary
If we move the Chorus calculation within the IF statement,
then the Chorus will be calculated only every 10th call (every 200 mS) without loosing the ADSR and the "Chorus".
This should reduse the CPU Consumption. but we lose our local variables


Sub music
'Local Fcent%,F1%,F2%,FL%,FR%
If mt%=1 Then
   nt%=Asc(Mid$(AA$,CNT%,1))
   If nt%=35 Then CNT%=1:nt%=Asc(Mid$(AA$,CNT%,1))
   Freq1%=FRQ%(nt%)
   Freq2%=FRQ%(Asc(Mid$(AB$,CNT%,1)))
   'Pseudo Chorus
   Fcent%=Int(Freq1%/100)
   FL%=Freq1% - Fcent%
   FR%=Freq1% + Fcent%
   Inc cnt%
 EndIf
 If Freq1%>1 Then Play sound 1,"L","q",FL%,adsr%(mt%):Play sound 3,"R","q",FR%,a
dsr%(mt%)
If Freq2%>1  Then Play sound 2,"B","w",Freq2%,adsr2%(mt%)
Inc mt%:If mt%>10 Then mt%=1
End Sub

Edited 2022-10-20 04:30 by Martin H.
'no comment
 
phil99

Guru

Joined: 11/02/2018
Location: Australia
Posts: 2636
Posted: 09:51pm 19 Oct 2022
Copy link to clipboard 
Print this post

By trial and error I have noticed local variables, especially large arrays, slow the Pico down a little more than global ones.
Putting as much of the program as possible in the main loop gains a little more speed.
 
Volhout
Guru

Joined: 05/03/2018
Location: Netherlands
Posts: 5085
Posted: 07:26pm 24 Oct 2022
Copy link to clipboard 
Print this post

CPU load is 48% now (from 51%).
Apparently there is a lot of CPU in the play commands themselves.
You can still shave 2% CPU power off, when you change the ADSR from 10 steps to 6 steps, without much loss of sound quality. SETTICK to 36ms.
Manual mentions that some 25% is used simply by playing. Not sure if we can improve any further.

Volhout


  Martin H. said  
  Volhout said  Hi Martin,

I have profiled your code in the first post, and it is impressive sound with all the ADSR and chorus. But it consumes 51% CPU power at 252MHz.

Volhout

well ...  Time for optimisation  
The cent calculation for the Pseudo Chorus is done every 20 mS (with every call of the Sub).
This is not necessary
If we move the Chorus calculation within the IF statement,
then the Chorus will be calculated only every 10th call (every 200 mS) without loosing the ADSR and the "Chorus".
This should reduse the CPU Consumption. but we lose our local variables


Sub music
'Local Fcent%,F1%,F2%,FL%,FR%
If mt%=1 Then
   nt%=Asc(Mid$(AA$,CNT%,1))
   If nt%=35 Then CNT%=1:nt%=Asc(Mid$(AA$,CNT%,1))
   Freq1%=FRQ%(nt%)
   Freq2%=FRQ%(Asc(Mid$(AB$,CNT%,1)))
   'Pseudo Chorus
   Fcent%=Int(Freq1%/100)
   FL%=Freq1% - Fcent%
   FR%=Freq1% + Fcent%
   Inc cnt%
 EndIf
 If Freq1%>1 Then Play sound 1,"L","q",FL%,adsr%(mt%):Play sound 3,"R","q",FR%,a
dsr%(mt%)
If Freq2%>1  Then Play sound 2,"B","w",Freq2%,adsr2%(mt%)
Inc mt%:If mt%>10 Then mt%=1
End Sub

Edited 2022-10-25 06:22 by Volhout
PicomiteVGA PETSCII ROBOTS
 
Print this page


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

The Back Shed's forum code is written, and hosted, in Australia.
© JAQ Software 2025