Here's a new version, it can now can play a string of given notes and you can specify the note duration in a musical way (whole note, quarter note, etc). I went ahead and named the program because I think it has some potential to expand on. I put the note duration stuff inside the notes FOR/NEXT loop because I intend to later allow duration calls in the notes string itself later on, like "qn606162hn656362", and not have it a separate parameter.
This version plays notes up and down on every sound patch. One more baby step.
- Dav
This version plays notes up and down on every sound patch. One more baby step.
- Dav
Code: (Select All)
'=============
'MidiNotes.bas
'=============
'Makes MIDI notes and plays it from memory.
'Exploring QB64-PE default soundfont patches.
'By Dav, SEP/2024 (v1.01)
$Unstable:Midi
$MidiSoundFont: Default
'cycle through all sounds, press any key to quit.
For patch = 0 To 127
Print "Patch#"; patch
note$ = MidiNotes$(120, patch, "6062646567656462", "en")
midisound& = _SndOpen(note$, "memory")
_SndPlay midisound&
_Delay 2
_SndStop midisound&
_SndClose midisound&
If InKey$ <> "" Then End
Next
Function MidiNotes$ (tempo&, patch, notes$, duration$)
'--------------------------------------------
'Returns MIDI data suitable for MIDI playback
'--------------------------------------------
'tempo& = tempo of the midi
'patch = program number (sound) to use
'note$ = string of note to play
'duration$ = the duration the note will have
'The following duration string is allowed:
' wn = whole note
' hn = half note
' dq = dotted quarter
' qn = quarter note
' de = dotted eighth
' en = eighth note
' sn = sixteenth note
TicksPerQuarterNote = 96 ' Set ticks per quarter note
'make MIDI Header Chunk (MThd)
MThd$ = "MThd"
MThd$ = MThd$ + Chr$(0) + Chr$(0) + Chr$(0) + Chr$(6) 'header size
MThd$ = MThd$ + Chr$(0) + Chr$(1) 'format type (1)
MThd$ = MThd$ + Chr$(0) + Chr$(1) 'number of tracks (1)
MThd$ = MThd$ + Chr$(0) + Chr$(TicksPerQuarterNote) 'division (ticks per quarter note)
'calculate microseconds per beat from tempo& in BPM
MicroSecsPerBeat& = 60000000 \ tempo& 'Converts BPM to microseconds per beat
'get msb/mb/lsb from MicroSecsPerBeat& for saving tempo
'(Midi requires 3 bytes for this info)
msb = (MicroSecsPerBeat& \ 65536) And 255 'most significant byte
middle = (MicroSecsPerBeat& \ 256) And 255 'middle byte
lsb = MicroSecsPerBeat& And 255 'least significant byte
'make the tempo data + the 3 bytes
TrackData$ = TrackData$ + Chr$(0) + Chr$(255) + Chr$(81) + Chr$(3) + Chr$(msb) + Chr$(middle) + Chr$(lsb)
'set Program number (patch) to use
TrackData$ = TrackData$ + Chr$(0) + Chr$(192) + Chr$(patch)
'loop through all notes given
For n = 1 To Len(notes$) Step 2
note = Val(Mid$(notes$, n, 2)) 'get a note
'set Note On: play note value (with velocity 127)
TrackData$ = TrackData$ + Chr$(0) + Chr$(144) + Chr$(note) + Chr$(127)
'get notes duration in beats
Select Case LCase$(duration$)
Case "wn": ticks& = 4 * TicksPerQuarterNote 'whole note (4 beats)
Case "hn": ticks& = 2 * TicksPerQuarterNote 'half note (2 beats)
Case "qn": ticks& = 1 * TicksPerQuarterNote 'quarter note (1 beat)
Case "dq": ticks& = (3 / 2) * TicksPerQuarterNote 'dotted quarter (1.5 beats)
Case "en": ticks& = (1 / 2) * TicksPerQuarterNote 'eighth note (.5 beat)
Case "de": ticks& = (3 / 4) * TicksPerQuarterNote 'dotted eighth (.75 beat)
Case "sn": ticks& = (1 / 4) * TicksPerQuarterNote 'sixteenth note (.25 beat)
Case Else
ticks& = 0 'If invalid, set default to 0
End Select
'================================================
'convert ticks& to variable length quantity (VLQ)
TickData$ = ""
If ticks& = 0 Then
TickData$ = Chr$(0) 'safety, in case ticks& = 0
Else
Do
byte& = ticks& And &H7F
ticks& = ticks& \ 128
If TickData$ <> "" Then byte& = byte& Or &H80
TickData$ = TickData$ + Chr$(byte&)
Loop While ticks& <> 0
End If
'===============================================
'set note off: stop playing note after specified duration
TrackData$ = TrackData$ + TickData$ + Chr$(128) + Chr$(note) + Chr$(64)
Next
'make track end event
TrackData$ = TrackData$ + Chr$(0) + Chr$(255) + Chr$(47) + Chr$(0)
'make the MTrk header
MTrk$ = "MTrk"
'make the track data length (4 bytes)
TrackLen& = Len(TrackData$)
TrackLength$ = Chr$((TrackLen& \ 16777216) And 255) + Chr$((TrackLen& \ 65536) And 255) + Chr$((TrackLen& \ 256) And 255) + Chr$(TrackLen& And 255)
'put it all together
MidiNotes$ = MThd$ + MTrk$ + TrackLength$ + TrackData$
End Function