The QBasic Forum      Other Subforums, Links and Downloads
 
 Return to Index  

PLAY macro decoder

March 15 2009 at 3:28 PM
  (Login T3sl4)
R

' BASIC PLAY macro to Z80 one-shot timer-based song data converter.
' By Tim Williams, 3-14-2009 (happy Pi day!).
'
' Output is an assembly code fragment containing the number of
' counts (at 4MHz) per half cycle, followed by the number of
' half cycles to count for one note. 65535 is a special value
' which produces no sound while counting, thereby giving a rest.
'

DEFINT A-Z

DIM Duration AS SINGLE, Length AS SINGLE, OldLength AS SINGLE
DIM NoteTime AS SINGLE, RestTime AS SINGLE, NoteCyc AS LONG
DIM NoteCyc(83) AS LONG, NoteFreq(83) AS SINGLE, NoteLetter(6)
DIM k AS STRING * 1

GOSUB ConvTables

OPEN COMMAND$ + ".PLY" FOR BINARY AS #1
OPEN "CONS:" FOR OUTPUT AS #2

PRINT #2, ";"
PRINT #2, "; Music data"
PRINT #2, ";"
PRINT #2, "tones:"

Octave = 3
Tempo = 80
Duration = .875
Length = .25 '1/nth note (0.25 = quarter note, etc.)

DO
'Strip command byte
GET #1, , k: k = UCASE$(k)
Length = OldLength
SELECT CASE k
CASE "A" TO "G" 'Note
Note = NoteLetter(ASC(k) - 65) + Octave * 12
'Strip one byte, check for suffix
GET #1, , k: k = UCASE$(k)
SELECT CASE k
CASE "#", "+"
Note = Note + 1 'Sharp
CASE "-"
Note = Note - 1 'Flat
CASE "."
'Dotted note (single dotting only)
Length = Length * 1.5
CASE ELSE
SEEK #1, LOC(1) 'undo character grab
END SELECT
GOSUB WriteNote
CASE "L" 'Length
'Strip one to two digits
Number$ = ""
FOR i = 4 TO 0 STEP -1
GET #1, , k: k = UCASE$(k)
IF ASC(k) < 48 OR ASC(k) >= 58 THEN
SEEK #1, LOC(1) 'undo character grab
EXIT FOR
END IF
Number$ = Number$ + k
NEXT
IF i = 0 THEN PRINT "Too many digits in length!": ERROR 6
OldLength = 1 / VAL(Number$)
CASE "M" 'Duration/control
'Strip one byte
GET #1, , k: k = UCASE$(k)
SELECT CASE k
CASE "B", "F" 'Background or Foreground: N/A
CASE "N" 'Normal
Duration = .875
CASE "L" 'Legato
Duration = 1
CASE "S" 'Staccato
Duration = .75
END SELECT
CASE "N" 'Note (direct)
'Strip one to two digits
Number$ = ""
FOR i = 3 TO 0 STEP -1
GET #1, , k: k = UCASE$(k)
IF ASC(k) < 48 OR ASC(k) >= 58 THEN
SEEK #1, LOC(1) 'undo character grab
EXIT FOR
END IF
Number$ = Number$ + k
NEXT
Note = VAL(Number$)
IF i = 0 OR Note > 84 THEN PRINT "Too many digits in note!": ERROR 6
GOSUB WriteNote
CASE "O" 'Octave
'Strip one digit
GET #1, , k
Octave = VAL(k)
CASE "P" 'Rest (pause)
'Strip one to two digits
Number$ = ""
FOR i = 3 TO 0 STEP -1
GET #1, , k: k = UCASE$(k)
IF ASC(k) < 48 OR ASC(k) >= 58 THEN
SEEK #1, LOC(1) 'undo character grab
EXIT FOR
END IF
Number$ = Number$ + k
NEXT
IF i = 0 THEN PRINT "Too many digits in rest!": ERROR 6
Length = VAL(Number$)
IF Length < 1 OR Length > 63 THEN PRINT "Rest out of range!": ERROR 6
Length = 1 / Length
Note = 0
GOSUB WriteNote
CASE "T" 'Tempo
'Strip two to three digits
Number$ = ""
FOR i = 4 TO 0 STEP -1
GET #1, , k: k = UCASE$(k)
IF ASC(k) < 48 OR ASC(k) >= 58 THEN
SEEK #1, LOC(1) 'undo character grab
EXIT FOR
END IF
Number$ = Number$ + k
NEXT
IF i <= 0 THEN PRINT "Too many digits in tempo!": ERROR 6
Tempo = VAL(Number$)
IF Tempo < 32 OR Tempo > 255 THEN PRINT "Tempo out of range!": ERROR 6
CASE ">" 'Increase octave
IF Octave < 6 THEN Octave = Octave + 1
CASE "<" 'Decrease octave
IF Octave > 0 THEN Octave = Octave - 1
END SELECT
LOOP UNTIL EOF(1)
PRINT #2, "endtones:"
END

'
' Writes the specified note to #2.
'
WriteNote:
IF Note > 0 THEN
NoteCycles = NoteFreq(Note) * Duration * Length * 480 / Tempo
ELSE
NoteCycles = NoteFreq(Note) * Length * 240 / Tempo
END IF
IF NoteCycles > 65535 THEN NoteCyc = 65535

'SOUND NoteFreq(Note), NoteTime * 18.2

PRINT #2, CHR$(9) + CHR$(9) + ".word ";
IF Note = 0 THEN
' Rest
WRITE #2, NoteCyc(0), NoteCycles
ELSEIF Duration < .99 AND Note > 0 THEN
' Regular note plus brief rest
RestCycles = NoteFreq(0) * (1 - Duration) * Length * 240 / Tempo
WRITE #2, NoteCyc(Note), NoteCycles, NoteCyc(0), RestCycles
ELSE
'Duration = 1 (legato), note only
WRITE #2, NoteCyc(Note), NoteCycles
END IF
RETURN

'
' Conversion Tables
'
ConvTables:
NoteCyc(0) = 65535 'Rest
NoteCyc(1) = 34234 'A#0
NoteCyc(2) = 32308 'B0
NoteCyc(3) = 30490 'C0
NoteCyc(4) = 28773 'C#0
NoteCyc(5) = 27153 'D0
NoteCyc(6) = 25624 'D#0
NoteCyc(7) = 24181 'E0
NoteCyc(8) = 22819 'F0
NoteCyc(9) = 21533 'F#0
NoteCyc(10) = 20320 'G0
NoteCyc(11) = 19174 'G#0
NoteCyc(12) = 18093 'A1
NoteCyc(13) = 17073 'A#1
NoteCyc(14) = 16110 'B1
NoteCyc(15) = 15201 'C1
NoteCyc(16) = 14342 'C#1
NoteCyc(17) = 13532 'D1
NoteCyc(18) = 12768 'D#1
NoteCyc(19) = 12046 'E1
NoteCyc(20) = 11365 'F1
NoteCyc(21) = 10722 'F#1
NoteCyc(22) = 10116 'G1
NoteCyc(23) = 9543 'G#1
NoteCyc(24) = 9002 'A2
NoteCyc(25) = 8492 'A#2
NoteCyc(26) = 8011 'B2
NoteCyc(27) = 7556 'C2
NoteCyc(28) = 7127 'C#2
NoteCyc(29) = 6722 'D2
NoteCyc(30) = 6340 'D#2
NoteCyc(31) = 5979 'E2
NoteCyc(32) = 5638 'F2
NoteCyc(33) = 5317 'F#2
NoteCyc(34) = 5014 'G2
NoteCyc(35) = 4727 'G#2
NoteCyc(36) = 4457 'A3
NoteCyc(37) = 4202 'A#3
NoteCyc(38) = 3961 'B3
NoteCyc(39) = 3734 'C3
NoteCyc(40) = 3519 'C#3
NoteCyc(41) = 3317 'D3
NoteCyc(42) = 3126 'D#3
NoteCyc(43) = 2945 'E3
NoteCyc(44) = 2775 'F3
NoteCyc(45) = 2614 'F#3
NoteCyc(46) = 2463 'G3
NoteCyc(47) = 2319 'G#3
NoteCyc(48) = 2184 'A4
NoteCyc(49) = 2057 'A#4
NoteCyc(50) = 1936 'B4
NoteCyc(51) = 1823 'C4
NoteCyc(52) = 1715 'C#4
NoteCyc(53) = 1614 'D4
NoteCyc(54) = 1519 'D#4
NoteCyc(55) = 1428 'E4
NoteCyc(56) = 1343 'F4
NoteCyc(57) = 1263 'F#4
NoteCyc(58) = 1187 'G4
NoteCyc(59) = 1115 'G#4
NoteCyc(60) = 1048 'A5
NoteCyc(61) = 984 'A#5
NoteCyc(62) = 924 'B5
NoteCyc(63) = 867 'C5
NoteCyc(64) = 813 'C#5
NoteCyc(65) = 763 'D5
NoteCyc(66) = 715 'D#5
NoteCyc(67) = 670 'E5
NoteCyc(68) = 627 'F5
NoteCyc(69) = 587 'F#5
NoteCyc(70) = 549 'G5
NoteCyc(71) = 513 'G#5
NoteCyc(72) = 480 'A6
NoteCyc(73) = 448 'A#6
NoteCyc(74) = 418 'B6
NoteCyc(75) = 389 'C6
NoteCyc(76) = 362 'C#6
NoteCyc(77) = 337 'D6
NoteCyc(78) = 313 'D#6
NoteCyc(79) = 291 'E6
NoteCyc(80) = 269 'F6
NoteCyc(81) = 249 'F#6
NoteCyc(82) = 230 'G6
NoteCyc(83) = 212 'G#6

' Frequencies in Hz
NoteFreq(0) = 60.66581 'Rest
NoteFreq(1) = 60.66673 'A#0
NoteFreq(2) = 61.73542 'B0
NoteFreq(3) = 65.4064 'C0
NoteFreq(4) = 69.29567 'C#0
NoteFreq(5) = 73.41621 'D0
NoteFreq(6) = 77.78176 'D#0
NoteFreq(7) = 82.40691 'E0
NoteFreq(8) = 87.30708 'F0
NoteFreq(9) = 92.49863 'F#0
NoteFreq(10) = 97.99889 'G0
NoteFreq(11) = 103.8262 'G#0
NoteFreq(12) = 110.0001 'A1
NoteFreq(13) = 116.541 'A#1
NoteFreq(14) = 123.4709 'B1
NoteFreq(15) = 130.8129 'C1
NoteFreq(16) = 138.5914 'C#1
NoteFreq(17) = 146.8325 'D1
NoteFreq(18) = 155.5636 'D#1
NoteFreq(19) = 164.8139 'E1
NoteFreq(20) = 174.6143 'F1
NoteFreq(21) = 184.9974 'F#1
NoteFreq(22) = 195.9979 'G1
NoteFreq(23) = 207.6526 'G#1
NoteFreq(24) = 220.0002 'A2
NoteFreq(25) = 233.0821 'A#2
NoteFreq(26) = 246.9419 'B2
NoteFreq(27) = 261.6259 'C2
NoteFreq(28) = 277.183 'C#2
NoteFreq(29) = 293.6652 'D2
NoteFreq(30) = 311.1274 'D#2
NoteFreq(31) = 329.628 'E2
NoteFreq(32) = 349.2287 'F2
NoteFreq(33) = 369.995 'F#2
NoteFreq(34) = 391.996 'G2
NoteFreq(35) = 415.3054 'G#2
NoteFreq(36) = 440.0007 'A3
NoteFreq(37) = 466.1646 'A#3
NoteFreq(38) = 493.8842 'B3
NoteFreq(39) = 523.2521 'C3
NoteFreq(40) = 554.3663 'C#3
NoteFreq(41) = 587.3306 'D3
NoteFreq(42) = 622.2551 'D#3
NoteFreq(43) = 659.2563 'E3
NoteFreq(44) = 698.4578 'F3
NoteFreq(45) = 739.9903 'F#3
NoteFreq(46) = 783.9924 'G3
NoteFreq(47) = 830.6111 'G#3
NoteFreq(48) = 880.0018 'A4
NoteFreq(49) = 932.3295 'A#4
NoteFreq(50) = 987.7688 'B4
NoteFreq(51) = 1046.505 'C4
NoteFreq(52) = 1108.733 'C#4
NoteFreq(53) = 1174.662 'D4
NoteFreq(54) = 1244.511 'D#4
NoteFreq(55) = 1318.514 'E4
NoteFreq(56) = 1396.917 'F4
NoteFreq(57) = 1479.982 'F#4
NoteFreq(58) = 1567.986 'G4
NoteFreq(59) = 1661.223 'G#4
NoteFreq(60) = 1760.005 'A5
NoteFreq(61) = 1864.66 'A#5
NoteFreq(62) = 1975.539 'B5
NoteFreq(63) = 2093.011 'C5
NoteFreq(64) = 2217.468 'C#5
NoteFreq(65) = 2349.325 'D5
NoteFreq(66) = 2489.024 'D#5
NoteFreq(67) = 2637.029 'E5
NoteFreq(68) = 2793.835 'F5
NoteFreq(69) = 2959.965 'F#5
NoteFreq(70) = 3135.974 'G5
NoteFreq(71) = 3322.448 'G#5
NoteFreq(72) = 3520.012 'A6
NoteFreq(73) = 3729.323 'A#6
NoteFreq(74) = 3951.08 'B6
NoteFreq(75) = 4186.024 'C6
NoteFreq(76) = 4434.938 'C#6
NoteFreq(77) = 4698.653 'D6
NoteFreq(78) = 4978.05 'D#6
NoteFreq(79) = 5274.06 'E6
NoteFreq(80) = 5587.672 'F6
NoteFreq(81) = 5919.933 'F#6
NoteFreq(82) = 6271.951 'G6
NoteFreq(83) = 6644.901 'G#6

NoteLetter(0) = 12 'A
NoteLetter(1) = 14 'B
NoteLetter(2) = 3 'C
NoteLetter(3) = 5 'D
NoteLetter(4) = 7 'E
NoteLetter(5) = 8 'F
NoteLetter(6) = 10 'G
RETURN


 
 Respond to this message   
Response TitleAuthor and Date
Try this PLAY with GOTO nightmare. (View Thread) on Mar 16
   Removing the extra line numbersqbguy on Mar 17
      Yeah, I recall sombody posted a program to remove them. on Mar 17
         Micro$uck wrote REMLINE.BASqbguy on Mar 18
            * BSD license? Gimme the crap from M$ if you are a thief. on Mar 18
               Oh well, Pete already rewrote it (* URL)qbguy on Mar 19

Newbies usually go to www.qbasic.com and click on The QBasic Forum
Forum regulars have their own ways, which include The QBasic Community Forums