Thread Rating:
  • 1 Vote(s) - 5 Average
  • 1
  • 2
  • 3
  • 4
  • 5
Exploring QB64-PE default soundfont patches
#4
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

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

Find my programs here in Dav's QB64 Corner
Reply


Messages In This Thread
RE: Exploring QB64-PE default soundfont patches - by Dav - 09-10-2024, 01:44 PM



Users browsing this thread: 12 Guest(s)