Results 1 to 18 of 18

Thread: Fast Timer + Sequencer + Tempo Problem

  1. #1

    Thread Starter
    Frenzied Member some1uk03's Avatar
    Join Date
    Jun 2006
    Location
    London, UK
    Posts
    1,663

    Question Fast Timer + Sequencer + Tempo Problem

    I've got a small sequencer going on, but am facing a few issues with the Timer. I was happy to a certain degree for a while but now I want to tackle this again.

    All these other sequencer apps like Cubase, Logic etc have smooth running playback systems where you can play the song and still do things in the background without affecting the playback in any way.
    Whereas here with me, having 8 tracks or moving the form around, etc, pauses and hangs the timer.

    KillTimer seems to run smoothly, but doesn't go below 1ms intervals.
    QueryPerformanceCounter with a do/Loop is perfect in terms of accuracy & Tempo etc, but causes the hang.

    So, can anyone figure out how to have a smooth timer going on without being interrupted ?

    Attached is my small project below.

    Timer.zip

    _____________________________________________________________________

    ----If this post has helped you. Please take time to Rate it.
    ----If you've solved your problem, then please mark it as RESOLVED from Thread Tools.



  2. #2
    The Idiot
    Join Date
    Dec 2014
    Posts
    2,721

    Re: Fast Timer + Sequencer + Tempo Problem

    that is why I use borderstyle=0 and use my own method to move around the form.

  3. #3

  4. #4

    Thread Starter
    Frenzied Member some1uk03's Avatar
    Join Date
    Jun 2006
    Location
    London, UK
    Posts
    1,663

    Re: Fast Timer + Sequencer + Tempo Problem

    Quote Originally Posted by baka View Post
    that is why I use borderstyle=0 and use my own method to move around the form.
    I'm currently using (releaseCapture / sendmessage)
    What's your method?


    Quote Originally Posted by The trick View Post
    Never ever use timers in such scenarios, you should synchronize your UI using your sound events like when you fill a buffer.
    I'm not quite sure what you mean by that.

    I have a an array() of events with Ticks that need triggering at certain points.
    That's why I have a timer on a loop checking for matching Ticks to raise the PLAY event.
    _____________________________________________________________________

    ----If this post has helped you. Please take time to Rate it.
    ----If you've solved your problem, then please mark it as RESOLVED from Thread Tools.



  5. #5
    Hyperactive Member
    Join Date
    Dec 2008
    Location
    Argentina
    Posts
    439

    Re: Fast Timer + Sequencer + Tempo Problem

    the modTimer uses a loop with a Doevents, that makes it stop you can check it with this simple code

    Code:
    Option Explicit
    Private Declare Function ReleaseCapture Lib "user32" () As Long
    Private Declare Function SendMessage Lib "user32" Alias "SendMessageA" (ByVal hwnd As Long, ByVal wMsg As Long, ByVal wParam As Long, lParam As Any) As Long
       
    Dim bFlag As Boolean
    Dim Counter As Long
    
    Private Sub Form_Load()
        Me.Show
        bFlag = True
        Do While bFlag
            Counter = Counter + 1
            Cls
            Me.Print Counter
            DoEvents
        Loop
    
    End Sub
    
    Private Sub Form_MouseDown(Button As Integer, Shift As Integer, X As Single, Y As Single)
        Call ReleaseCapture
        Call SendMessage(Me.hwnd, &H112, &HF012&, 0)
    End Sub
    
    Private Sub Form_Unload(Cancel As Integer)
        bFlag = False
    End Sub
    leandroascierto.com Visual Basic 6 projects

  6. #6
    PowerPoster
    Join Date
    Feb 2015
    Posts
    2,673

    Re: Fast Timer + Sequencer + Tempo Problem

    Quote Originally Posted by some1uk03 View Post
    I have a an array() of events with Ticks that need triggering at certain points.
    For events you should use WaitForSingleObject or something like Waiter. But im sure you select a wrong way because you should have a single buffer which you should track. When you fill this buffer with the data you check your events. If the events has been triggered you just add the samples of your new audio so you get the resolution with your sample rate frequency.

  7. #7

    Thread Starter
    Frenzied Member some1uk03's Avatar
    Join Date
    Jun 2006
    Location
    London, UK
    Posts
    1,663

    Re: Fast Timer + Sequencer + Tempo Problem

    I think you're presuming that I'm playing a single .wav file in a buffer and I'm seeking a way to find play positioning. That's not the scenario.

    Think of musical notes in an array.
    sNotes(0).start = 50 (ticks)
    sNotes(1).start = 100 (ticks)
    sNotes(2).start = 122 (ticks)
    sNotes(4).start = 240 (ticks)

    So the Timer goes through the sNotes array on each increment.
    If timerTicks = sNotes(loop).start then PLAY
    _____________________________________________________________________

    ----If this post has helped you. Please take time to Rate it.
    ----If you've solved your problem, then please mark it as RESOLVED from Thread Tools.



  8. #8
    PowerPoster
    Join Date
    Feb 2015
    Posts
    2,673

    Re: Fast Timer + Sequencer + Tempo Problem

    Quote Originally Posted by some1uk03 View Post
    I think you're presuming that I'm playing a single .wav file in a buffer and I'm seeking a way to find play positioning. That's not the scenario.

    Think of musical notes in an array.
    sNotes(0).start = 50 (ticks)
    sNotes(1).start = 100 (ticks)
    sNotes(2).start = 122 (ticks)
    sNotes(4).start = 240 (ticks)

    So the Timer goes through the sNotes array on each increment.
    If timerTicks = sNotes(loop).start then PLAY
    I'm just talking about this scenario.

  9. #9

    Thread Starter
    Frenzied Member some1uk03's Avatar
    Join Date
    Jun 2006
    Location
    London, UK
    Posts
    1,663

    Re: Fast Timer + Sequencer + Tempo Problem

    I'm having difficulty understanding your model.
    Either way it needs some form of a Timer to know the elapsed time in order to trigger the next event.

    Even instead of a LOOP/TIMER we calculate the different between currentTick vs Next Tick Event, you need a form of a timer to calculate that difference.


    I'm currently exploring the WAITER class to see if it handles ticks (as opposed to ms), which may be another option.
    _____________________________________________________________________

    ----If this post has helped you. Please take time to Rate it.
    ----If you've solved your problem, then please mark it as RESOLVED from Thread Tools.



  10. #10
    The Idiot
    Join Date
    Dec 2014
    Posts
    2,721

    Re: Fast Timer + Sequencer + Tempo Problem

    theres many different methods to use instead of releasecapture. the same is if u hold mousedown on titlebar if using borderstyle>0
    my method is just "when u press mouse down" it will store x/y position of the mouse relative to form left/top.
    when mouse-move the form is "locked" so it know the -x/-y that I got from mouse down. and mouse-up, it will "release it".
    now the form moves together with mouse and everything that is happening inside the form is showing. no delay/freeze.

  11. #11
    PowerPoster
    Join Date
    Feb 2015
    Posts
    2,673

    Re: Fast Timer + Sequencer + Tempo Problem

    See this small sequencer example:
    Code:
    Option Explicit
    
    Private Declare Sub memcpy Lib "kernel32" _
                        Alias "RtlMoveMemory" ( _
                        ByRef Destination As Any, _
                        ByRef Source As Any, _
                        ByVal Length As Long)
                        
    Private Const NUM_OF_CHANNELS   As Long = 4
    Private Const SAMPLE_RATE       As Long = 44100
    
    Private Type TSound
        lSamples    As Long
        iValues()   As Integer
    End Type
    
    Private WithEvents m_cPlayer    As clsTrickSound2
    Private m_tSounds()             As TSound
    Private m_fTempo                As Single
    
    Private Sub Form_Load()
        Dim lChIndex    As Long
        Dim lQIndex     As Long
        Dim lIndex      As Long
        
        For lChIndex = 0 To NUM_OF_CHANNELS - 1
            For lQIndex = 0 To 15
            
                If lIndex Then
                    Load chkKey(lIndex)
                End If
                    
                With chkKey(lIndex)
                    .Move lQIndex * .Width + fraTempo.Left, lChIndex * .Height + fraTempo.Top * 2 + fraTempo.Height
                    .Visible = True
                End With
                
                lIndex = lIndex + 1
                
            Next
        Next
        
        Me.Width = (Me.Width - Me.ScaleWidth) + 16 * chkKey(0).Width + fraTempo.Left * 2
        Me.Height = (Me.Height - Me.ScaleHeight) + NUM_OF_CHANNELS * chkKey(0).Height + fraTempo.Top * 3 + fraTempo.Height
        
        fraTempo.Width = Me.ScaleWidth - fraTempo.Left * 2
        sldTempo.Move sldTempo.Left, sldTempo.Top, fraTempo.Width - sldTempo.Left * 2
        
        sldTempo_Change
        
        ReDim m_tSounds(NUM_OF_CHANNELS - 1)
        ReDim m_tChannel(NUM_OF_CHANNELS - 1)
        
        m_tSounds(0) = LoadSound(App.Path & "\kick.snd")
        m_tSounds(1) = LoadSound(App.Path & "\snare.snd")
        m_tSounds(2) = LoadSound(App.Path & "\hi-hat.snd")
        m_tSounds(3) = LoadSound(App.Path & "\crash.snd")
        
        Set m_cPlayer = New clsTrickSound2
        
        m_cPlayer.InitPlayback 2, SAMPLE_RATE, 16, (SAMPLE_RATE * 2 * 0.05) And -2
        m_cPlayer.StartProcess
        
    End Sub
    
    Private Sub m_cPlayer_NewData( _
                ByVal DataPtr As Long, _
                ByVal CountBytes As Long)
        Static s_lBlock                         As Long
        Static s_lChIdx(NUM_OF_CHANNELS - 1)    As Long
        Static s_bChState(NUM_OF_CHANNELS - 1)  As Boolean
        Static s_iData()                        As Integer
        Static s_bDataInitialized               As Boolean
        Static s_lPrevUIBlock                   As Long
        
        Dim lBIndex     As Long
        Dim lKIndex     As Long
        Dim lChIndex    As Long
        Dim lBlocks     As Long
        Dim lBlkPerQ    As Long
        Dim lSampleL    As Long
        Dim lSampleR    As Long
        Dim lPbOffset   As Long
        
        If Not s_bDataInitialized Then
            ReDim s_iData(CountBytes \ 2 - 1)
            s_bDataInitialized = True
        End If
        
        lBlocks = CountBytes \ 4
        lBlkPerQ = 240 / m_fTempo * SAMPLE_RATE / 16
        
        For lBIndex = 0 To lBlocks - 1
            
            lSampleL = 0:   lSampleR = 0
            
            If (s_lBlock Mod lBlkPerQ) = 0 Then
    
                lKIndex = s_lBlock \ lBlkPerQ
                
                If lKIndex >= 16 Then
                    lKIndex = 0
                    s_lBlock = 0
                End If
                
                For lChIndex = 0 To NUM_OF_CHANNELS - 1
                
                    If chkKey(lChIndex * 16 + lKIndex).value Then
                        s_bChState(lChIndex) = True
                        s_lChIdx(lChIndex) = 0
                    End If
                    
                Next
                
            End If
            
            For lChIndex = 0 To NUM_OF_CHANNELS - 1
                If s_bChState(lChIndex) Then
                    
                    lSampleL = lSampleL + m_tSounds(lChIndex).iValues(s_lChIdx(lChIndex))
                    lSampleR = lSampleR + m_tSounds(lChIndex).iValues(s_lChIdx(lChIndex) + 1)
                    
                    s_lChIdx(lChIndex) = s_lChIdx(lChIndex) + 2
                    
                    If s_lChIdx(lChIndex) >= m_tSounds(lChIndex).lSamples Then
                        s_bChState(lChIndex) = False
                    End If
                    
                End If
            Next
            
            If lSampleL > 32767 Then
                lSampleL = 32767
            ElseIf lSampleL < -32768 Then
                lSampleL = -32768
            End If
            
            If lSampleR > 32767 Then
                lSampleR = 32767
            ElseIf lSampleR < -32768 Then
                lSampleR = -32768
            End If
            
            s_iData(lBIndex * 2) = lSampleL
            s_iData(lBIndex * 2 + 1) = lSampleR
            
            s_lBlock = s_lBlock + 1
            
        Next
        
        ' // Update visual
        lPbOffset = (m_cPlayer.BufferLengthSamples * m_cPlayer.BuffersCount + CountBytes \ 2) \ 2
        
        lKIndex = ((s_lBlock - lPbOffset) \ lBlkPerQ) Mod 16
        
        If lKIndex < 0 Then
            lKIndex = 16 + lKIndex
        End If
        
        For lChIndex = 0 To NUM_OF_CHANNELS - 1
        
            chkKey(lChIndex * 16 + lKIndex).BackColor = vbRed
            
            If s_lPrevUIBlock <> lKIndex Then
                chkKey(lChIndex * 16 + s_lPrevUIBlock).BackColor = vbWhite
            End If
            
        Next
        
        s_lPrevUIBlock = lKIndex
    
        memcpy ByVal DataPtr, s_iData(0), (UBound(s_iData) + 1) * 2
        
    End Sub
    
    Private Function LoadSound( _
                     ByRef sPath As String) As TSound
        Dim tRet    As TSound
        Dim iFNum   As Integer
        Dim lSize   As Long
        
        iFNum = FreeFile
        
        Open sPath For Binary As iFNum
        
        lSize = LOF(iFNum)
        
        If lSize Then
            ReDim tRet.iValues(lSize \ 2)
            Get iFNum, , tRet.iValues
        End If
        
        tRet.lSamples = lSize \ 2
        
        Close iFNum
        
        LoadSound = tRet
        
    End Function
    
    Private Sub sldTempo_Change()
        m_fTempo = sldTempo.value
        fraTempo.Caption = "Tempo (" & CStr(m_fTempo) & " BPM)"
    End Sub
    
    Private Sub sldTempo_Scroll()
        sldTempo_Change
    End Sub
    It use this class for sound playback.
    Attached Files Attached Files

  12. #12

    Thread Starter
    Frenzied Member some1uk03's Avatar
    Join Date
    Jun 2006
    Location
    London, UK
    Posts
    1,663

    Re: Fast Timer + Sequencer + Tempo Problem

    @TheTrick: Thank you for the example. I will take some time to analyse it tomorrow, but having a quick glance seems to be a very very different approach to what I currently have. And I'm not sure, I can go ahead and change my whole project to a totally different approach at this point of time.

    Do you think there could be an alternative approach with the Timer methods I'm currently using ?
    _____________________________________________________________________

    ----If this post has helped you. Please take time to Rate it.
    ----If you've solved your problem, then please mark it as RESOLVED from Thread Tools.



  13. #13
    PowerPoster
    Join Date
    Feb 2015
    Posts
    2,673

    Re: Fast Timer + Sequencer + Tempo Problem

    Quote Originally Posted by some1uk03 View Post
    Do you think there could be an alternative approach with the Timer methods I'm currently using ?
    As i already wrote you shouldn't use timers for this purpose because they are not accurate. If you don't need accurate time periods you could use multimedia timers but it'll require either high CPU loading or threading issues. Most of the DAW i know uses approach which i showed you so i would hihgly recommend you to change your architecture.

  14. #14
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: Fast Timer + Sequencer + Tempo Problem

    Quote Originally Posted by some1uk03 View Post
    I'm currently using (releaseCapture / sendmessage)
    What's your method?
    I've recently posted a solution for that kind of blocking here:
    https://www.vbforums.com/showthread....nd-a-solution)
    (based on subclassing).

    Olaf

  15. #15
    Addicted Member Mikle's Avatar
    Join Date
    Oct 2009
    Location
    Tuapse, Russia
    Posts
    138

    Re: Fast Timer + Sequencer + Tempo Problem

    As an alternative to the timer, you can use a waiting cycle with synchronization by the exact QueryPerformancaCounter(). I attach an example of a simple metronome based on this principle, the Sleep() function in the example is not used for synchronization, but to reduce the load on the CPU while waiting for the next event.
    I use this .tlb: https://www.vbforums.com/showthread....und&highlight=
    Attached Files Attached Files
    Last edited by Mikle; Feb 8th, 2023 at 01:51 AM.

  16. #16
    The Idiot
    Join Date
    Dec 2014
    Posts
    2,721

    Re: Fast Timer + Sequencer + Tempo Problem

    my recommendation is to follow The trick suggestions.
    we can always do workarounds but in the end, when we implement experts solutions we notice the difference in quality.
    so, even if this will require a startover it will reward u later.

  17. #17
    PowerPoster
    Join Date
    Jun 2013
    Posts
    7,219

    Re: Fast Timer + Sequencer + Tempo Problem

    If it is for "Midi-Notes only", then a normal Timer triggering the "Notes"
    (with 15msec +-8msec interval) - seems entirely sufficient.

    And due to the "slight imprecisions in timing" when triggering Notes,
    it even sounds more like "natural playing"...
    (less "mechanical" when compared to "msec-exact-outputs").

    Olaf

  18. #18
    Addicted Member Mikle's Avatar
    Join Date
    Oct 2009
    Location
    Tuapse, Russia
    Posts
    138

    Re: Fast Timer + Sequencer + Tempo Problem

    With the assistance of WinAPI commands, the accuracy of the system timer can be increased to 1 ms, then this is really enough for midi notes, but 16 ms is clearly not enough even for this.
    But when mixing audio tracks, even 1 ms is very inaccurate, at a frequency of 500 Hz it will create an antiphase.

Tags for this Thread

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •  



Click Here to Expand Forum to Full Width