home *** CD-ROM | disk | FTP | other *** search
/ Programming Tool Box / SIMS_2.iso / tool / sound / vb_mid / vb_seq.bas < prev    next >
BASIC Source File  |  1995-02-05  |  29KB  |  955 lines

  1. Option Explicit
  2.  
  3. 'Type of recorded Midi Message
  4. Type udtMidiMsg
  5.     TimeStamp As Long   'Associated time in milliseconds
  6.     MidiData As Long    'Usually: (Status + Channel) + (&H100& * Data1) + (&H10000 * Data2)
  7. End Type
  8.  
  9. 'RecBuffer parameters
  10. Global aRecBuffer() As udtMidiMsg        'dynamic array of recorded messages
  11. Global nRecCounter As Long               'N. of recorded messages
  12. Global nRecErrors As Long                'N. of lost Midi In Messages
  13.  
  14. 'Timing variables
  15. Global lInitTime As Long        'timeGetTime() when Play or Rec starts (in Internal Sync)
  16. Global lOffsetTime As Long      'Display Time when Play or Rec starts (in Internal Sync)
  17.  
  18. 'Flags to track Play and Rec activity
  19. Global bStop As Integer     'if True indicates Stop Mode
  20. Global bPlay As Integer     'if True indicates Play Mode
  21. Global bRec As Integer      'if True indicates Rec Mode
  22.  
  23. 'For Clock displaying purposes  (incremented by one frame every frame)
  24. Global nDisplayHours As Integer
  25. Global nDisplayMinutes As Integer
  26. Global nDisplaySeconds As Integer
  27. Global nDisplayFrames As Integer
  28.  
  29. 'For MTC Out purposes   (incremented by two frames every two frames)
  30. Global nHoursCounter   As Integer
  31. Global nMinutesCounter As Integer
  32. Global nSecondsCounter As Integer
  33. Global nFramesCounter  As Integer
  34.  
  35. 'Name of the last saved or opened file
  36. Global sFilename As String
  37.  
  38. 'Visualize flags
  39. Global bVisualClock As Integer 'Visualize clock display
  40. Global bVisualData As Integer  'Visualize Midi Data Flow
  41. Global bVisualMtc As Integer   'Visualize MTC flow
  42.  
  43. 'To track Midi flow visualisation
  44. Global lMtcInTime As Long       'Time when MtcIn led was switched on
  45. Global lMtcOutTime As Long      'Time when MtcOut led was switched on
  46. Global lDataInTime As Long      'Time when DataIn led was switched on
  47. Global lDataOutTime As Long     'Time when DataOut led was switched on
  48.  
  49. 'Sequencer parameters
  50. Global nSeqChannel As Integer
  51. Global aSeqProgram(15) As Integer
  52.  
  53. 'Indicates Mouse state in Rewind and Forward MouseDown events
  54. Global bMouseDown As Integer
  55.  
  56. 'Led colors
  57. Global Const LED_OFF = &H80&
  58. Global Const LED_ON = &H80FF&
  59.  
  60. 'GENERAL CONSTANTS
  61.  
  62. 'MousePointer
  63. Global Const DEFAULT = 0
  64. Global Const HOURGLASS = 11
  65.  
  66. 'Keycodes
  67. Global Const KEY_ESCAPE = &H1B
  68. Global Const KEY_NUMPAD0 = &H60
  69. Global Const KEY_RETURN = &HD
  70. Global Const KEY_MULTIPLY = &H6A
  71. Global Const KEY_SPACE = &H20
  72. Global Const KEY_F12 = &H7B
  73.  
  74. 'Special keys
  75. Global Const SHIFT_MASK = 1
  76. Global Const CTRL_MASK = 2
  77. Global Const ALT_MASK = 4
  78.  
  79. ' MsgBox parameters
  80. Global Const MB_OK = 0                 ' OK button only
  81. Global Const MB_YESNO = 4              ' Yes and No buttons
  82. Global Const MB_ICONQUESTION = 32      ' Warning query
  83. Global Const MB_ICONEXCLAMATION = 48   ' Warning message
  84.  
  85. ' MsgBox return values
  86. Global Const IDOK = 1                  ' OK button pressed
  87. Global Const IDYES = 6                 ' Yes button pressed
  88. Global Const IDNO = 7                  ' No button pressed
  89.  
  90. 'Colors
  91. Global Const WHITE = &HFFFFFF
  92. Global Const DARKBLUE = &H800000
  93.  
  94. ' DragOver
  95. Global Const ENTER = 0
  96. Global Const LEAVE = 1
  97.  
  98. Sub Display_Erase ()
  99.     If frmVBSeq.lblHours <> "--" Then frmVBSeq.lblHours = "--"
  100.     If frmVBSeq.lblMinutes <> "--" Then frmVBSeq.lblMinutes = "--"
  101.     If frmVBSeq.lblSeconds <> "--" Then frmVBSeq.lblSeconds = "--"
  102.     If frmVBSeq.lblFrames <> "--" Then frmVBSeq.lblFrames = "--"
  103. End Sub
  104.  
  105. Sub Display_Show ()
  106.     Dim sDisplay As String
  107.  
  108.     sDisplay = Format$(nDisplayHours, "00")
  109.     If frmVBSeq.lblHours <> sDisplay Then frmVBSeq.lblHours = sDisplay
  110.  
  111.     sDisplay = Format$(nDisplayMinutes, "00")
  112.     If frmVBSeq.lblMinutes <> sDisplay Then frmVBSeq.lblMinutes = sDisplay
  113.  
  114.     sDisplay = Format$(nDisplaySeconds, "00")
  115.     If frmVBSeq.lblSeconds <> sDisplay Then frmVBSeq.lblSeconds = sDisplay
  116.  
  117.     sDisplay = Format$(nDisplayFrames, "00")
  118.     If frmVBSeq.lblFrames <> sDisplay Then frmVBSeq.lblFrames = sDisplay
  119. End Sub
  120.  
  121. Sub Dlg_Alert (sMsg As String)
  122.      Beep
  123.      MsgBox sMsg, MB_OK + MB_ICONEXCLAMATION, "ALERT"
  124. End Sub
  125.  
  126. Function Dlg_YesNo (sMsg1 As String) As Integer
  127.     Dim sMsg2 As String
  128.  
  129.     sMsg2 = "Make your decission"
  130.     Beep
  131.     If MsgBox(sMsg1, MB_YESNO + MB_ICONQUESTION, sMsg2) = IDYES Then
  132.         Dlg_YesNo = True
  133.     Else
  134.        Dlg_YesNo = False
  135.     End If
  136. End Function
  137.  
  138. 'Returns True if File must be deleted / False if File must not
  139. Function File_Delete% (sPath As String)
  140.     Dim i As Integer
  141.     Dim sName As String
  142.     Dim FNum As Integer
  143.  
  144.     If Len(sPath) <= 1 Or Mid$(sPath, Len(sPath), 1) = "\" Then
  145.     Call Dlg_Alert(sFilename & Chr(10) & "Bad file name!")
  146.     frmVBSeq.dlgFileDialog.Filename = "*.SNG"
  147.     sFilename = "?"
  148.     File_Delete = False
  149.     Exit Function
  150.     End If
  151.  
  152.     For i = Len(sPath) To 1 Step -1
  153.     If Mid$(sPath, i, 1) = "\" Then
  154.         sName = Mid$(sPath, i + 1, Len(sPath) - i)
  155.         Exit For
  156.     End If
  157.     Next i
  158.     
  159.     FNum = FreeFile
  160.  
  161.     On Error Resume Next
  162.  
  163.     Open sPath For Input As FNum
  164.  
  165.     'No error -> File already exists
  166.     If Err = 0 Then
  167.     If Dlg_YesNo(sName & " already exists!" & Chr(10) & "Replace it...?") = True Then
  168.         'overwrite it
  169.         File_Delete = True
  170.     Else
  171.         'abort save
  172.         File_Delete = False
  173.     End If
  174.  
  175.     'File not found
  176.     ElseIf Err = 53 Then
  177.     'doesn't need to be deleted
  178.     'keep on saving
  179.     File_Delete = True
  180.  
  181.     'Bad File Name
  182.     ElseIf Err = 64 Or Err = 52 Then
  183.     Call Dlg_Alert(sName & Chr(10) & "Bad file name!")
  184.     frmVBSeq.dlgFileDialog.Filename = "*.SNG"
  185.     sFilename = "?"
  186.     'abort save
  187.     File_Delete = False
  188.     
  189.     'Unexpected error
  190.     Else
  191.     Call Dlg_Alert("Error #" & Err & Chr(10) & Error$)
  192.     frmVBSeq.dlgFileDialog.Filename = "*.SNG"
  193.     sFilename = "?"
  194.     'abort save
  195.     File_Delete = False
  196.     End If
  197.  
  198.     Close FNum
  199. End Function
  200.  
  201. Sub File_Open ()
  202.     Dim FNum   As Integer
  203.     Dim nLen    As Integer
  204.     Dim i       As Integer
  205.     
  206.     'If buffer not empty confirm loss of data
  207.     If nRecCounter > 0 Then
  208.     If Dlg_YesNo("Erase recorded MIDI messages?") = False Then Exit Sub
  209.     End If
  210.  
  211.     On Error GoTo Open_Error_Handler
  212.  
  213.     'Activate cancel error
  214.     frmVBSeq.dlgFileDialog.CancelError = True
  215.  
  216.     'Set File Dialog parameters
  217.     frmVBSeq.dlgFileDialog.Filter = "Custom MIDI song (*.SNG)|*.SNG|Standard MIDI file (*.MID)|*.MID|All (*.*)|*.*"
  218.     frmVBSeq.dlgFileDialog.FilterIndex = 1
  219.     frmVBSeq.dlgFileDialog.DialogTitle = "Open File"
  220.     frmVBSeq.dlgFileDialog.Action = 1    '1 = Open file dialog
  221.  
  222.     frmVBSeq.Refresh
  223.  
  224.     'Get path and file name to be opened
  225.     sFilename = frmVBSeq.dlgFileDialog.Filename
  226.  
  227.     nLen = Len(sFilename)
  228.     For i = nLen To 1 Step -1
  229.     If Mid$(sFilename, i, 1) = "\" Then Exit For
  230.     Next i
  231.  
  232.     sFilename = Right$(sFilename, nLen - i)
  233.  
  234.     Screen.MousePointer = HOURGLASS
  235.  
  236.     If Right$(sFilename, 4) = ".SNG" Then
  237.     FNum = FreeFile
  238.     Open frmVBSeq.dlgFileDialog.Filename For Input As FNum
  239.     Input #FNum, nRecCounter
  240.     
  241.     If nRecCounter > 0 Then
  242.         ReDim aRecBuffer(nRecCounter + 1024 - (nRecCounter Mod 1024))
  243.         For i = 0 To nRecCounter - 1
  244.         Input #FNum, aRecBuffer(i).TimeStamp
  245.         Input #FNum, aRecBuffer(i).MidiData
  246.         Next i
  247.     End If
  248.     'Display recorded messages counter
  249.     frmVBSeq.lblRecMesNum = CStr(nRecCounter)
  250.     
  251.     Close #FNum
  252.     ElseIf Right$(sFilename, 4) = ".MID" Then
  253.     Call Dlg_Alert("Not implemented!")
  254.     Else
  255.     Call Dlg_Alert("Wrong file format!")
  256.     End If
  257.     
  258. Open_Exit:
  259.     Screen.MousePointer = DEFAULT
  260.     Exit Sub
  261.  
  262. Open_Error_Handler:
  263.     If Err = 32755 Then   'Cancel
  264.     Resume Open_Exit
  265.     Else
  266.     Call Dlg_Alert("Error #" & Err & Chr(10) & Error$)
  267.     Close #FNum
  268.     Resume Open_Exit
  269.     End If
  270.  
  271. End Sub
  272.  
  273. Sub File_Save ()
  274.     Dim sFname As String
  275.     Dim FNum As Integer
  276.     Dim i As Integer
  277.     Dim nStartName As Integer
  278.     Dim nLen As Integer
  279.  
  280.     'Exit if buffer empty
  281.     If nRecCounter = 0 Then
  282.     Call Dlg_Alert("Nothing to save!")
  283.     Exit Sub
  284.     End If
  285.  
  286.     On Error GoTo Save_Error_Handler
  287.  
  288.     'Activate cancel error
  289.     frmVBSeq.dlgFileDialog.CancelError = True
  290.  
  291.     'Set File Dialog parameters
  292.     frmVBSeq.dlgFileDialog.Filter = "Custom MIDI song (*.SNG)|*.SNG|Standard MIDI file (*.MID)|*.MID|All (*.*)|*.*"
  293.     frmVBSeq.dlgFileDialog.FilterIndex = 1
  294.     frmVBSeq.dlgFileDialog.DialogTitle = "Save File"
  295.     frmVBSeq.dlgFileDialog.Action = 2   '2 = Savefile Dialog
  296.  
  297.     frmVBSeq.Refresh
  298.  
  299.     'Get path and file name to be saved
  300.     sFname = frmVBSeq.dlgFileDialog.Filename
  301.   
  302.     'Check Filename suffix (must be .SNG)
  303.     If Right$(sFname, 4) <> ".SNG" Then   'And Right$(sFname, 4) <> ".MID"
  304.     nLen = Len(sFname)
  305.     For i = nLen To 1 Step -1
  306.         If Mid$(sFname, i, 1) = "\" Then Exit For
  307.     Next i
  308.     nStartName = i
  309.     If nStartName = 0 Then nStartName = 1   'for safety
  310.  
  311.     For i = nLen To nStartName Step -1
  312.         If Mid$(sFname, i, 1) = "." Then   'Is there a wrong suffix?
  313.         sFname = Left$(sFname, i - 1)  'Remove suffix
  314.         Exit For
  315.         End If
  316.     Next i
  317.     
  318.     'add suffix
  319.     sFname = sFname & ".SNG"
  320.     End If
  321.  
  322.     'Check if file exists and user wants to replace it
  323.     If File_Delete(sFname) = False Then Exit Sub
  324.  
  325.     nLen = Len(sFname)
  326.     For i = nLen To 1 Step -1
  327.     If Mid$(sFname, i, 1) = "\" Then Exit For
  328.     Next i
  329.  
  330.     sFilename = Right$(sFname, nLen - i)
  331.  
  332.     FNum = FreeFile
  333.     Open sFname For Output As FNum
  334.  
  335.     Screen.MousePointer = HOURGLASS
  336.     
  337.     Write #FNum, nRecCounter
  338.     If nRecCounter > 0 Then
  339.     For i = 0 To nRecCounter - 1
  340.         Write #FNum, aRecBuffer(i).TimeStamp, aRecBuffer(i).MidiData
  341.     Next i
  342.     End If
  343.  
  344. Save_Exit1:
  345.     Close #FNum
  346.  
  347. Save_Exit2:
  348.     Screen.MousePointer = DEFAULT
  349.     Exit Sub
  350.  
  351. Save_Error_Handler:
  352.     If Err = 64 Or Err = 20477 Then
  353.     Call Dlg_Alert(sFname & Chr(10) & "Bad file name!")
  354.     frmVBSeq.dlgFileDialog.Filename = "*.SNG"
  355.     sFilename = "?"
  356.     Resume Save_Exit2
  357.     ElseIf Err = 32755 Then   'Cancel
  358.     Resume Save_Exit2
  359.     Else
  360.     Call Dlg_Alert("Error #" & Err & Chr(10) & Error$)
  361.     Resume Save_Exit1
  362.     End If
  363.  
  364. End Sub
  365.  
  366. Function Get_Next& (lTime As Long)
  367.     Dim lCount As Long
  368.     
  369.     Get_Next = -1&
  370.  
  371.     If nRecCounter = 0& Then Exit Function
  372.  
  373.     For lCount = 0& To nRecCounter - 1&
  374.     If aRecBuffer(lCount).TimeStamp >= lTime Then
  375.         Get_Next = lCount
  376.         Exit For
  377.     End If
  378.     vntRet = DoEvents()
  379.     Next lCount
  380. End Function
  381.  
  382. Function KeytoNum (KeyCode As Integer) As Integer
  383.     Select Case KeyCode
  384.     Case Asc("0") To Asc("9")
  385.         KeytoNum = KeyCode - Asc("0")
  386.     Case KEY_NUMPAD0 To KEY_NUMPAD0 + 9
  387.         KeytoNum = KeyCode - KEY_NUMPAD0
  388.     Case Else
  389.         KeytoNum = -1
  390.     End Select
  391. End Function
  392.  
  393. Function Label_Decrement% (lblLabel As Label, nMin As Integer, nOffset As Integer)
  394.     Dim nValue As Integer, bFirst As Integer
  395.  
  396.     bMouseDown = True
  397.     bFirst = True
  398.     Do While bMouseDown = True
  399.     nValue = Val(lblLabel.Caption)
  400.  
  401.     If nValue > nMin Then
  402.         nValue = nValue - nOffset
  403.         If nValue < nMin Then nValue = nMin
  404.         lblLabel.Caption = CStr(nValue)
  405.         lblLabel.Refresh
  406.     End If
  407.  
  408.     If bFirst = True Then       'For key repeat purposes
  409.         Wait_DoEvents (200)
  410.         bFirst = False
  411.     Else
  412.         Wait_DoEvents (10)
  413.     End If
  414.     
  415.     Loop
  416.     Label_Decrement = nValue
  417. End Function
  418.  
  419. Function Label_Increment% (lblLabel As Label, nMax As Integer, nOffset As Integer)
  420.     Dim nValue As Integer, bFirst As Integer
  421.  
  422.     bMouseDown = True
  423.     bFirst = True
  424.     Do While bMouseDown = True
  425.     nValue = Val(lblLabel)
  426.  
  427.     If nValue < nMax Then
  428.         nValue = nValue + nOffset
  429.         If nValue > nMax Then nValue = nMax
  430.         lblLabel.Caption = CStr(nValue)
  431.         lblLabel.Refresh
  432.     End If
  433.  
  434.     If bFirst = True Then       'For key repeat purposes
  435.         Wait_DoEvents (200)
  436.         bFirst = False
  437.     Else
  438.         Wait_DoEvents (10)
  439.     End If
  440.     
  441.     Loop
  442.     Label_Increment = nValue
  443. End Function
  444.  
  445. Sub Play_External ()
  446.     Dim lPlayPointer    As Long
  447.     Dim lNextTime       As Long
  448.     Dim lNextData       As Long
  449.     Dim nOldMtcFrames   As Long
  450.     Dim bPlayError      As Integer
  451.  
  452.     'If already playing or recording -> do nothing
  453.     If bStop = False Then Exit Sub
  454.     
  455.     'Change tracking buttons appearance to Play position.
  456.     frmVBSeq.cmdPlay.Picture = frmVBSeq.cmdPlayDn.Picture
  457.     frmVBSeq.cmdStop.Picture = frmVBSeq.cmdStopUp.Picture
  458.     frmVBSeq.cmdRec.Picture = frmVBSeq.cmdRecUp.Picture
  459.  
  460.     'Set corresponding flags
  461.     bRec = False
  462.     bStop = False
  463.     bPlay = True
  464.  
  465.     'reset play error flag
  466.     bPlayError = False
  467.  
  468.     'Reset play variables to ready to start play values
  469.     nNewMtc = 0            'new MTC not yet received
  470.     nQfIdExpected = &H0    'first quarter frame message to be taked in account
  471.     lPlayPointer = -1      'nothing to play yet
  472.  
  473.     'Set Mtc variables to out of sync values
  474.     bInSync = False
  475.     nMtcTotalframes = -1
  476.     lMtcTime = -1
  477.     nOldMtcFrames = -1
  478.  
  479.     Do While bStop = False
  480.     If bInSync = False Then
  481.         'Erase clock to indicate that we're out of sync
  482.         Display_Erase
  483.         nMtcTotalframes = -1
  484.         lMtcTime = -1
  485.         nOldMtcFrames = -1
  486.     Else
  487.         'Check if MTC has changed
  488.         If nNewMtc > 0 Then
  489.         'a new MTC could arrive while we're trying to resync
  490.         'so make sure that we're in sync before exiting loop
  491.         Do
  492.             'resync as many times as necessary
  493.             lPlayPointer = Get_Next(lMtcTime + 250)    '500 ms. preroll
  494.             nNewMtc = nNewMtc - 1
  495.             vntRet = DoEvents()    'to allow new MTC messages to be hooked
  496.         Loop While nNewMtc > 0     'exit loop when we're in sync
  497.  
  498.         'Get_Next() function returns -1 if there's nothing to play, thus...
  499.         If lPlayPointer >= 0 Then
  500.             'parameters of next message to be played
  501.             lNextTime = aRecBuffer(lPlayPointer).TimeStamp
  502.             lNextData = aRecBuffer(lPlayPointer).MidiData
  503.         End If
  504.         End If
  505.  
  506.         'Play everything that should be played
  507.         Do While lPlayPointer >= 0 And lMtcTime >= lNextTime
  508.         If MidiOut_Msg(lNextData) = False Then
  509.             'if a MIDI OUT error occurred -> stop playing
  510.             bPlayError = True
  511.             Exit Do
  512.         End If
  513.  
  514.         'to allow new MTC messages to be hooked
  515.         vntRet = DoEvents()
  516.         
  517.         'Increase array pointer
  518.         lPlayPointer = lPlayPointer + 1
  519.         If lPlayPointer >= nRecCounter Then
  520.             'nothing else to be played
  521.             lPlayPointer = -1
  522.         Else
  523.             'parameters of next message to be played
  524.             lNextTime = aRecBuffer(lPlayPointer).TimeStamp
  525.             lNextData = aRecBuffer(lPlayPointer).MidiData
  526.         End If
  527.         Loop
  528.  
  529.         'Can't continue
  530.         If bPlayError = True Then Exit Do
  531.  
  532.         'Set new clock values if necessary
  533.         If bVisualClock = True Then
  534.         If nMtcTotalframes <> nOldMtcFrames Then
  535.             'convert frame counter to clock values
  536.             Call Mtc_Frames_to_HMSF(nMtcTotalframes, nDisplayHours, nDisplayMinutes, nDisplaySeconds, nDisplayFrames)
  537.             'show new values
  538.             Call Display_Show
  539.             nOldMtcFrames = nMtcTotalframes
  540.         End If
  541.         End If
  542.     End If
  543.  
  544.     'to allow Stop button to be pressed and new MTC messages to be hooked
  545.     vntRet = DoEvents()
  546.     Loop
  547.  
  548.     'In case visualize Clock was disabled set it to last received MTC time
  549.     If nMtcTotalframes = -1 Then nMtcTotalframes = 0
  550.     Call Mtc_Frames_to_HMSF(nMtcTotalframes, nDisplayHours, nDisplayMinutes, nDisplaySeconds, nDisplayFrames)
  551.     Call Display_Show
  552.  
  553.     'Change tracking buttons appearance back to Stop position
  554.     frmVBSeq.cmdStop.Picture = frmVBSeq.cmdStopDn.Picture
  555.     frmVBSeq.cmdPlay.Picture = frmVBSeq.cmdPlayUp.Picture
  556.     frmVBSeq.cmdRec.Picture = frmVBSeq.cmdRecUp.Picture
  557.  
  558.     'Set flags accordingly
  559.     bStop = True
  560.     bPlay = False
  561.     bRec = False
  562.  
  563. End Sub
  564.  
  565. Sub Play_Internal ()
  566.     Dim lSystemTime     As Long
  567.     Dim lPlayingTime    As Long
  568.     Dim fEllapsedTime   As Single
  569.     Dim fLastQfTime     As Single
  570.     Dim lPlayPointer    As Long
  571.     Dim lNextTime       As Long
  572.     Dim lNextData       As Long
  573.     Dim nQfCounter      As Integer
  574.     Dim lQfTotalCounter As Long
  575.     Dim bPlayError      As Integer
  576.  
  577.     'If already playing or recording -> do nothing
  578.     If bStop = False Then Exit Sub
  579.     
  580.     'Change tracking buttons appearance to Play position.
  581.     frmVBSeq.cmdPlay.Picture = frmVBSeq.cmdPlayDn.Picture
  582.     frmVBSeq.cmdStop.Picture = frmVBSeq.cmdStopUp.Picture
  583.     frmVBSeq.cmdRec.Picture = frmVBSeq.cmdRecUp.Picture
  584.  
  585.     'Set corresponding flags
  586.     bRec = False
  587.     bStop = False
  588.     bPlay = True
  589.  
  590.     'Check if Display Clock values are correct and show them.
  591.     nDisplayHours = Val(frmVBSeq.lblHours)
  592.     nDisplayMinutes = Val(frmVBSeq.lblMinutes)
  593.     nDisplaySeconds = Val(frmVBSeq.lblSeconds)
  594.     nDisplayFrames = Val(frmVBSeq.lblFrames)
  595.     Call Mtc_Adjust(nDisplayHours, nDisplayMinutes, nDisplaySeconds, nDisplayFrames)
  596.     Call Display_Show
  597.  
  598.     'Assign Smpte internal counters to match Clock values.
  599.     nHoursCounter = nDisplayHours
  600.     nMinutesCounter = nDisplayMinutes
  601.     nSecondsCounter = nDisplaySeconds
  602.     nFramesCounter = nDisplayFrames
  603.  
  604.     'Initial Offset = Clock values at Start playing (in milliseconds)
  605.     lOffsetTime = Mtc_HMSF_To_Ms(nDisplayHours, nDisplayMinutes, nDisplaySeconds, nDisplayFrames)
  606.  
  607.     'Get next array index to be played
  608.     lPlayPointer = Get_Next(lOffsetTime)   'if there's nothing to play returns -1
  609.     If lPlayPointer >= 0 Then
  610.     lNextTime = aRecBuffer(lPlayPointer).TimeStamp
  611.     lNextData = aRecBuffer(lPlayPointer).MidiData
  612.     End If
  613.  
  614.     'Reset Quarter frame counters
  615.     nQfCounter = 0
  616.     lQfTotalCounter = 0
  617.  
  618.     'Reset Play error flag
  619.     bPlayError = False
  620.  
  621.     'Set timing variables
  622.     lInitTime = timeGetTime()    'Actual high resolution system time in ms.
  623.     fLastQfTime = lInitTime      'To calculate time ellapsed since last quarter frame
  624.  
  625.     Do While bStop = False
  626.     'actual system time
  627.     lSystemTime = timeGetTime()
  628.     'actual playing time
  629.     lPlayingTime = lOffsetTime + (lSystemTime - lInitTime)
  630.  
  631.     'Play everything that should be played
  632.     Do While lPlayPointer >= 0 And lPlayingTime >= lNextTime
  633.         If MidiOut_Msg(lNextData) = False Then
  634.         'if a MIDI OUT error occurred -> stop playing
  635.         bPlayError = True
  636.         Exit Do
  637.         End If
  638.  
  639.         'Increase array pointer
  640.         lPlayPointer = lPlayPointer + 1
  641.         If lPlayPointer >= nRecCounter Then
  642.         'nothing else to be played
  643.         lPlayPointer = -1
  644.         Else
  645.         'parameters of next message to be played
  646.         lNextTime = aRecBuffer(lPlayPointer).TimeStamp
  647.         lNextData = aRecBuffer(lPlayPointer).MidiData
  648.         End If
  649.     Loop
  650.  
  651.     'Can't continue
  652.     If bPlayError = True Then Exit Do
  653.  
  654.     'A new quarter frame interval ellapsed?
  655.     fEllapsedTime = CSng(lSystemTime) - fLastQfTime
  656.  
  657.     If fEllapsedTime >= fMsPerQF Then
  658.         'Yes, send next MTC quarter frame message out (if requested)
  659.         If bMtcOut = True Then
  660.         If MidiOut_Mtc(nQfCounter, nHoursCounter, nMinutesCounter, nSecondsCounter, nFramesCounter) = False Then
  661.             'if a MIDI OUT error occurred -> stop playing
  662.             Exit Do
  663.         End If
  664.         End If
  665.  
  666.         'To start counting next quarter frame interval
  667.         lQfTotalCounter = lQfTotalCounter + 1
  668.  
  669.         'Operation must be float to avoid rounding errors
  670.         fLastQfTime = CSng(lInitTime) + fMsPerQF * CSng(lQfTotalCounter)
  671.  
  672.         'increase MTC out quarter frame counter
  673.         nQfCounter = nQfCounter + 1
  674.         If nQfCounter = 4 Then
  675.         'One whole frame interval has ellapsed (4 quarter frames)
  676.         'Thus increase Display Frame Counter (Clock) if necessary
  677.         If bVisualClock = True Then
  678.             nDisplayFrames = nDisplayFrames + 1
  679.             'Check if parameters are correct and display new clock values
  680.             Call Mtc_Adjust(nDisplayHours, nDisplayMinutes, nDisplaySeconds, nDisplayFrames)
  681.             Call Display_Show
  682.         End If
  683.         ElseIf nQfCounter = 8 Then
  684.         If bVisualClock = True Then
  685.             'Another whole frame interval has elapsed (4 quarter frames more)
  686.             'Actualize clock values as before
  687.             nDisplayFrames = nDisplayFrames + 1
  688.             Call Mtc_Adjust(nDisplayHours, nDisplayMinutes, nDisplaySeconds, nDisplayFrames)
  689.             Call Display_Show
  690.         End If
  691.  
  692.         'One complete MTC message takes 2 frames to be sent.
  693.         'As MTC hours, minutes, seconds or frames can not be changed in the middle
  694.         'of sending the MTC message, we must increase SMPTE Frame Counter
  695.         'only every 2 Frames (after a whole MTC message is completed)
  696.         nFramesCounter = nFramesCounter + 2
  697.         'Check if parameters are correct
  698.         Call Mtc_Adjust(nHoursCounter, nMinutesCounter, nSecondsCounter, nFramesCounter)
  699.         'wrap around MTC out quarter frame counter
  700.         nQfCounter = 0
  701.         End If
  702.     End If
  703.     
  704.     DoEvents   'allows bStop to be changed by pressing Stop button or Space key
  705.     Loop
  706.  
  707.     'If visualize Clock was disabled set it to last MTC time
  708.     If bVisualClock = False Then
  709.     'Assign Clock values to match MTC internal counters.
  710.     nDisplayHours = nHoursCounter
  711.     nDisplayMinutes = nMinutesCounter
  712.     nDisplaySeconds = nSecondsCounter
  713.     nDisplayFrames = nFramesCounter
  714.     
  715.     'Check if Display Clock values are correct and show them.
  716.     Call Mtc_Adjust(nDisplayHours, nDisplayMinutes, nDisplaySeconds, nDisplayFrames)
  717.     Call Display_Show
  718.     End If
  719.  
  720.  
  721.     'Change tracking buttons appearance back to Stop position
  722.     frmVBSeq.cmdStop.Picture = frmVBSeq.cmdStopDn.Picture
  723.     frmVBSeq.cmdPlay.Picture = frmVBSeq.cmdPlayUp.Picture
  724.     frmVBSeq.cmdRec.Picture = frmVBSeq.cmdRecUp.Picture
  725.  
  726.     'Set flags accordingly
  727.     bStop = True
  728.     bPlay = False
  729.     bRec = False
  730.  
  731. End Sub
  732.  
  733. Sub Rec_External ()
  734.     Dim nOldMtcFrames   As Long
  735.  
  736.     
  737.     'Change tracking buttons appearance to Play position.
  738.     frmVBSeq.cmdRec.Picture = frmVBSeq.cmdRecDn.Picture
  739.     frmVBSeq.cmdPlay.Picture = frmVBSeq.cmdPlayDn.Picture
  740.     frmVBSeq.cmdStop.Picture = frmVBSeq.cmdStopUp.Picture
  741.  
  742.     'Set corresponding flags
  743.     bRec = True
  744.     bStop = False
  745.     bPlay = False
  746.  
  747.     'Reset Recorded messages caption
  748.     frmVBSeq.lblRecMesNum = "0"
  749.  
  750.     'Prepare Rec buffer array
  751.     nRecCounter = 0
  752.     Erase aRecBuffer
  753.  
  754.     'Reset rec variables to ready to start rec values
  755.     nNewMtc = 0          'new MTC not yet received
  756.     nQfIdExpected = &H0  'first quarter frame message identifier expected
  757.  
  758.     'Set Mtc variables to out of sync values
  759.     bInSync = False
  760.     nMtcTotalframes = -1
  761.     lMtcTime = -1
  762.     nOldMtcFrames = -1
  763.  
  764.     Do While bStop = False
  765.     If bInSync = False Then
  766.         'Erase clock to indicate that we're out of sync
  767.         Display_Erase
  768.         nMtcTotalframes = -1
  769.         lMtcTime = -1
  770.         nOldMtcFrames = -1
  771.     Else
  772.         If bVisualClock = True Then
  773.         'Set new clock values if necessary
  774.         If nMtcTotalframes <> nOldMtcFrames Then
  775.             'convert frame counter to clock values
  776.             Call Mtc_Frames_to_HMSF(nMtcTotalframes, nDisplayHours, nDisplayMinutes, nDisplaySeconds, nDisplayFrames)
  777.             'show new values
  778.             Call Display_Show
  779.             nOldMtcFrames = nMtcTotalframes
  780.         End If
  781.         End If
  782.     End If
  783.  
  784.     'to allow Stop button to be pressed and new MTC messages to be hooked
  785.     vntRet = DoEvents()
  786.     Loop
  787.  
  788.     'If Visualize Clock was disabled set it to last received MTC time
  789.     If nMtcTotalframes = -1 Then nMtcTotalframes = 0
  790.     Call Mtc_Frames_to_HMSF(nMtcTotalframes, nDisplayHours, nDisplayMinutes, nDisplaySeconds, nDisplayFrames)
  791.     Call Display_Show
  792.  
  793.     'Change tracking buttons appearance back to Stop position
  794.     frmVBSeq.cmdStop.Picture = frmVBSeq.cmdStopDn.Picture
  795.     frmVBSeq.cmdPlay.Picture = frmVBSeq.cmdPlayUp.Picture
  796.     frmVBSeq.cmdRec.Picture = frmVBSeq.cmdRecUp.Picture
  797.  
  798.     'Set flags accordingly
  799.     bStop = True
  800.     bPlay = False
  801.     bRec = False
  802.  
  803.     'Display recorded messages counter
  804.     frmVBSeq.lblRecMesNum = CStr(nRecCounter)
  805.  
  806. End Sub
  807.  
  808. Sub Rec_Internal ()
  809.     Dim fEllapsedTime   As Long
  810.     Dim fLastQfTime     As Long
  811.     Dim nQfCounter      As Integer
  812.     Dim lQfTotalCounter As Long
  813.     Dim nHoursCounter   As Integer
  814.     Dim nMinutesCounter As Integer
  815.     Dim nSecondsCounter As Integer
  816.     Dim nFramesCounter  As Integer
  817.     
  818.     
  819.     'Change tracking buttons appearance to Rec position.
  820.     frmVBSeq.cmdRec.Picture = frmVBSeq.cmdRecDn.Picture
  821.     frmVBSeq.cmdPlay.Picture = frmVBSeq.cmdPlayDn.Picture
  822.     frmVBSeq.cmdStop.Picture = frmVBSeq.cmdStopUp.Picture
  823.  
  824.     'Set corresponding flags
  825.     bRec = True
  826.     bStop = False
  827.     bPlay = False
  828.  
  829.     'Check if Display Clock values are correct and show them.
  830.     nDisplayHours = Val(frmVBSeq.lblHours)
  831.     nDisplayMinutes = Val(frmVBSeq.lblMinutes)
  832.     nDisplaySeconds = Val(frmVBSeq.lblSeconds)
  833.     Call Mtc_Adjust(nDisplayHours, nDisplayMinutes, nDisplaySeconds, nDisplayFrames)
  834.     Call Display_Show
  835.  
  836.     'Assign Smpte internal counters to match Clock values
  837.     nHoursCounter = nDisplayHours
  838.     nMinutesCounter = nDisplayMinutes
  839.     nSecondsCounter = nDisplaySeconds
  840.     nFramesCounter = nDisplayFrames
  841.  
  842.     'Reset Recorded messages caption
  843.     frmVBSeq.lblRecMesNum = "0"
  844.  
  845.     'Prepare Rec buffer array
  846.     nRecCounter = 0
  847.     Erase aRecBuffer
  848.  
  849.     'Reset Quarter frame counters
  850.     lQfTotalCounter = 0
  851.     nQfCounter = 0
  852.  
  853.     'Set timing variables used by MidiHook to timestamp incoming Midi Data
  854.     'Initial Offset = Display Clock values at Start playing (in milliseconds)
  855.     lOffsetTime = Mtc_HMSF_To_Ms(nDisplayHours, nDisplayMinutes, nDisplaySeconds, nDisplayFrames)
  856.     'Actual system time in milliseconds
  857.     lInitTime = timeGetTime()
  858.  
  859.     'system time at last quarter frame message
  860.     fLastQfTime = lInitTime
  861.     
  862.     Do While bStop = False
  863.     'time ellapsed since last quarter frame message
  864.     fEllapsedTime = CSng(timeGetTime()) - fLastQfTime
  865.  
  866.     If fEllapsedTime >= fMsPerQF Then
  867.         'A quarter frame time has elapsed
  868.         'Send next MTC quarter frame message out
  869.         If bMtcOut = True Then
  870.         If MidiOut_Mtc(nQfCounter, nHoursCounter, nMinutesCounter, nSecondsCounter, nFramesCounter) = False Then
  871.             'if a MIDI OUT error occurred -> stop playing
  872.             Exit Do
  873.         End If
  874.         End If
  875.  
  876.         'To start counting next quarter frame interval
  877.         lQfTotalCounter = lQfTotalCounter + 1
  878.  
  879.         'Operation must be float to avoid rounding errors
  880.         fLastQfTime = CSng(lInitTime) + fMsPerQF * CSng(lQfTotalCounter)
  881.     
  882.         'increase MTC out quarter frame counter
  883.         nQfCounter = nQfCounter + 1        'increase local quarter frame counter
  884.         If nQfCounter = 4 Then
  885.         If bVisualClock = True Then
  886.             'One frame has elapsed (4 quarter frames)
  887.             'Thus increase Display Frame Counter (Clock)
  888.             nDisplayFrames = nDisplayFrames + 1
  889.             Call Mtc_Adjust(nDisplayHours, nDisplayMinutes, nDisplaySeconds, nDisplayFrames)
  890.             Call Display_Show
  891.             vntRet = DoEvents()   'to allow Midi In Data to be hooked
  892.         End If
  893.  
  894.         ElseIf nQfCounter = 8 Then
  895.         If bVisualClock = True Then
  896.             'Another frame has elapsed
  897.             'Increase Display Frame Counter
  898.             nDisplayFrames = nDisplayFrames + 1
  899.             Call Mtc_Adjust(nDisplayHours, nDisplayMinutes, nDisplaySeconds, nDisplayFrames)
  900.             Call Display_Show
  901.             vntRet = DoEvents()   'to allow Midi In Data to be hooked
  902.         End If
  903.  
  904.         'One complete MTC message takes 2 frames to be sent.
  905.         'As MTC hours, minutes, seconds or frames can not be changed in the middle
  906.         'of sending the MTC message, we must increase SMPTE Frame Counter
  907.         'only every 2 Frames (after a whole MTC message is completed)
  908.         nFramesCounter = nFramesCounter + 2
  909.         Call Mtc_Adjust(nHoursCounter, nMinutesCounter, nSecondsCounter, nFramesCounter)
  910.         'wrap around MTC out quarter frame counter
  911.         nQfCounter = 0
  912.         End If
  913.     End If
  914.     
  915.     vntRet = DoEvents()   'to allow Midi In Data to be hooked and trap Stop button click
  916.     Loop
  917.  
  918.     'If Visualize Clock was disabled set it to last SMPTE time
  919.     If bVisualClock = False Then
  920.     'Assign Clock values to match Smpte internal counters.
  921.     nDisplayHours = nHoursCounter
  922.     nDisplayMinutes = nMinutesCounter
  923.     nDisplaySeconds = nSecondsCounter
  924.     nDisplayFrames = nFramesCounter + 1
  925.     
  926.     'Check if Display Clock values are correct and show them.
  927.     Call Mtc_Adjust(nDisplayHours, nDisplayMinutes, nDisplaySeconds, nDisplayFrames)
  928.     Call Display_Show
  929.     End If
  930.  
  931.     'Change tracking buttons appearance back to Stop position
  932.     frmVBSeq.cmdStop.Picture = frmVBSeq.cmdStopDn.Picture
  933.     frmVBSeq.cmdPlay.Picture = frmVBSeq.cmdPlayUp.Picture
  934.     frmVBSeq.cmdRec.Picture = frmVBSeq.cmdRecUp.Picture
  935.  
  936.     'Set flags accordingly
  937.     bStop = True
  938.     bPlay = False
  939.     bRec = False
  940.  
  941.     'Display recorded messages counter
  942.     frmVBSeq.lblRecMesNum = CStr(nRecCounter)
  943.  
  944. End Sub
  945.  
  946. Sub Wait_DoEvents (lDelay As Long)
  947.     Dim lSystemTime As Long
  948.  
  949.     lSystemTime = timeGetTime()
  950.     Do
  951.     DoEvents
  952.     Loop Until timeGetTime() - lSystemTime >= lDelay
  953. End Sub
  954.  
  955.