[Webinar] Learn how to a build a cloud-first strategyRegister Now

x
?
Solved

Do methods of multiple instances of a class share the same resources?

Posted on 2006-05-16
10
Medium Priority
?
174 Views
Last Modified: 2010-04-23
OK!  Weird question.

I'm working on a program that is like a little music player.  You press the keys, it makes instrument sounds.  It's my first windows application, and my first attempt at OOP.

I've integrated a little looping feature that allows you to record what you're playing, loop it, and play over it.  I have a looper class that captures which sounds are being played, and when, and then plays it back.  Right now there are four loopers registered in the form1 class, and they all work just like they're supposed to... that is until you try to play back more than one at the same time.  You turn one on, it works fine.  You turn a second one on, and the first one turns off, and the second one plays.  And, weirdly, if you turn the second one off, the first one comes back on.  It's like they're masking each other in the order they're being played.  

Anyway, I've been over the code over and over, and the only thing I can imagine is that one instance of the Looper.Play method is somehow stepping on the others, making them pause until it's done doing its thing.  It that possible?

Thanks.
0
Comment
Question by:bti_admin
  • 5
  • 5
10 Comments
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 16699091
It sounds like the method you are using to play the sounds only allows one "source" to play at a time.

You play the first...sounds come out.
(first sounds still playing)
You play the second...first sounds are "muted"...second sounds come out.
You turn off the seond...first sounds (which were still playing albeit muted) are unmuted.

How are you playing the sounds?...
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 16699100
It's probably has nothing to do with your class code and everything to do with the underlying code in whatever sound library or API you are using.
0
 

Author Comment

by:bti_admin
ID: 16699520
Hey, thanks for the replies.

So, in response to your question, I am using Managed DirectX & DirectSound.  But I'm not doing anything fancy with it.  

Here's a further breakdown of how it works:

There's a Sound object that looks like this:

Public Class Sound
    Private buf As SecondaryBuffer

    Public keyList() As Keys
    Public mutes As MuteGroup
    Public hasMutes As Boolean = False

    Sub New(ByVal path As String, ByRef dev As Device, ByVal triggerKeys() As Keys)
        buf = New SecondaryBuffer(path, dev)
        keyList = triggerKeys
    End Sub

    Public Sub AddMuteGroup(ByRef newMuteGroup As MuteGroup)
        mutes = newMuteGroup
        hasMutes = True
    End Sub

    Public Sub play()
        If hasMutes Then
            mutes.mute()
        End If
        stp()
        buf.Play(0, BufferPlayFlags.Default)
    End Sub

    Public Sub stp()
        If Not buf Is Nothing Then
            If buf.PlayPosition > 0 Then
                buf.Stop()
                buf.SetCurrentPosition(0)
            End If
        End If
    End Sub
End Class

Dev is just a DirectSound Device, which is declared and initialized in the form1 class.  

So you can see, each sound can play only once at a time, you're correct.  However, each sound can only interrupt itself, which might sound a little nasty, but wont's stop anything from playing, at least as far as the API goes.

NOW!  I did a little more testing, and found that, if we have two loopers, looper1 and looper2, both containing a recording, and we start playing looper1, and then start playing looper2, looper1 isn't being maksked or muted, as I originally thought, but paused.  It's being interrupted, and waiting for its turn, it seems.  I feel like this has to be a quirk of the runtime environment, since nothing in my code provides for it.

I think it must have something to do with the way the loopers play back.  When we're recording, we have two ArrayLists that get populated; timeSig gets a TimeSpan, which is the difference between Now and a startTime which is set when recording starts,  and soundSig gets a reference to the Sound object played at that time.  

When we play it back, we do this:

Sub Play()
        If IsLocked Then
            Dim numSigs As Integer = soundSig.Count
            Dim thisSig As Integer = 0
            IsPlaying = True
            startTime = Now
            Do While thisSig < numSigs
                Do While startTime.Add(timeSig(thisSig)) >= Now
                    Application.DoEvents()
                    System.Threading.Thread.Sleep(1)
                    If Not IsPlaying Then
                        Exit Do
                    End If
                Loop
                If Not IsPlaying Then
                    Exit Do
                End If
                soundSig(thisSig).Play()
                thisSig = thisSig + 1
            Loop
            If Not IsPlaying Then
                Exit Sub
            End If
            Play()
        End If
End Sub

I suspect the culprit is how we get it to wait to play the next sound:

Application.DoEvents()                            'Keeps the application responsive
System.Threading.Thread.Sleep(1)         'Waits one millisecond before trying again

But I'm not sure.  And I have no idea what to do about it, other than to "bounce" to loops into one.

Thanks!

-J

0
Industry Leaders: We Want Your Opinion!

We value your feedback.

Take our survey and automatically be enter to win anyone of the following:
Yeti Cooler, Amazon eGift Card, and Movie eGift Card!

 
LVL 86

Accepted Solution

by:
Mike Tomlinson earned 1000 total points
ID: 16699752
Where are you calling Play() from?  The click of a button?

Where is Play() located?  The form or in the class?

Because you have Application.DoEvents() in the loop, this would allow the clicking of the buttons on your UI to be processed while sounds are playing.  The main UI is SINGLE threaded though, so I think the second button click "launches" another Play() and the new Play() must complete before returning to the original one.

Sounds like we need to use a different Thread() to do the playback.
0
 

Author Comment

by:bti_admin
ID: 16700246
Looper.Play is called from a textBox KeyDown event, and also by itself (that's why it loops).  They Play() you see above (the one containing the System.Threading.Sleep comman) is a method of the Looper class.

AND... I agree with you.  I'm not sure how I got this idea, but I was thinking that new thread were somehow getting auto-generated, which, after some research, I see is not true AT ALL.  So there you have it.

Anyway, I do believe you answered the question, explicity:  YES, unless you specifically create a new thread in which to run a procedure, multiple  instances of a class and its methods share the same resources, at least in terms of getting scheduled for processor time.  Thanks.

You get the points.  Let me know if you want them right now, or if you're interested in continuing this conversation.

-J
0
 

Author Comment

by:bti_admin
ID: 16700931
IT'S ALIIIIIIVE!

Here's a follow up for those actually trying to gain some information by reading this question.

I had to add Imports System.Threading at the top of the document to be able to explicitly create and manipulate threads.  Then I added a private property looperThread as Thread, which means that each instance of the Looper class gets its very own thread to work with.  I changed the Play method to ThreadedPlay, and created a different play method, which initializes looperThread, sticks ThreadedPlay inside of it, and starts it.  I added a line in ThreadedPlay that aborts the thread if the Looper's IsPlaying property has been set to False, which happens if the user calls the Looper.StopPlay() method.  And, instead of still calling Play() to loop the Looper, we call ThreadedPlay; we want to run the ThreadedPlay again, not re-instantiate looperThread.

Whew!

The Looper class now looks like this:

Public Class Looper
    Private timeSig As ArrayList = New ArrayList
    Private soundSig As ArrayList = New ArrayList

    Public IsArmed As Boolean = False
    Public IsRecording As Boolean = False
    Public IsPlaying As Boolean = False
    Public IsLocked As Boolean = False

    Private startTime As DateTime

    Public LoopId As String

    Private looperThread As Thread

    Sub New(ByVal loopNumber As String)
        LoopId = loopNumber
    End Sub

    Sub Arm()
        IsArmed = True
    End Sub

    Sub Record(ByRef FirstSound As Sound)
        IsArmed = False
        IsRecording = True
        startTime = Now
        AddSignature(FirstSound)
    End Sub

    Sub AddSignature(ByRef NextSound As Sound)
        Dim timeDif As TimeSpan = Now.Subtract(startTime)
        timeSig.Add(timeDif)
        soundSig.Add(NextSound)
    End Sub

    Sub StopRecording(ByRef EndSound As Sound)
        IsRecording = False
        IsLocked = True
        AddSignature(EndSound)
        Play()
    End Sub

    Sub Play()
        looperThread = New Thread(AddressOf ThreadedPlay)
        looperThread.Start()
    End Sub

    Private Sub ThreadedPlay()
        If IsLocked Then
            Dim numSigs As Integer = soundSig.Count
            Dim thisSig As Integer = 0
            IsPlaying = True
            startTime = Now
            Do While thisSig < numSigs
                Do While startTime.Add(timeSig(thisSig)) >= Now
                    Application.DoEvents()
                    System.Threading.Thread.Sleep(1)
                    If Not IsPlaying Then
                        Exit Do
                    End If
                Loop
                If Not IsPlaying Then
                    Exit Do
                End If
                soundSig(thisSig).Play()
                thisSig = thisSig + 1
            Loop
            If Not IsPlaying Then
                looperThread.Abort()
                Exit Sub
            End If
            ThreadedPlay()
        End If
    End Sub

    Sub StopPlay()
        IsPlaying = False
    End Sub

    Sub Reset()
        timeSig.Clear()
        soundSig.Clear()
        IsArmed = False
        IsLocked = False
        IsPlaying = False
        IsRecording = False
    End Sub

End Class

Idle Mind:  Not only did you answer my question, but my issue is solved entirely, so THANKS, and here are your points.
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 16700988
Glad you figured it out!

Just a couple comments on your threaded version.

            If Not IsPlaying Then
                looperThread.Abort()
                Exit Sub
            End If

You don't need the Abort() call.  In fact, it's a bad practive as things don't get cleaned up properly in the thread when you use it.

How about:
 
            ... code ...
            If IsPlaying Then
                ThreadedPlay()
            End If
        End If
    End Sub

This way the end of the sub is hit if we are NOT to continue and the thread ends by itself properly.

Also, since this is in a different thread now...Application.DoEvents() should be unnecessay.  Leave the small call to Sleep() in there though, since without it your CPU usage will ping to 100%.
0
 

Author Comment

by:bti_admin
ID: 16701129
So you're saying that a thread will stop and clean itself up when the procedure assigned to it ends?  That's cool.

AND, you're right, it works just fine now without the Application.DoEvents()

Say.... you're good at this.

Thanks!
0
 
LVL 86

Expert Comment

by:Mike Tomlinson
ID: 16701274
Lol...   =)

The fact that you are using recursion for this is really bothering me though!

It would be really easy to convert it to an iterative version.  You're not really gaining anything by using recursion here...except using up resources in your call stack.  Eventually you will run out of stack space and it will error out...if you let it "loop" long enough.

I think this will do it:    (save, save, save your work before making changes)

    Private Sub ThreadedPlay()
        If IsLocked Then
            Dim numSigs As Integer = soundSig.Count
            Dim thisSig As Integer = 0
            IsPlaying = True
           
            While IsPlaying
                thisSig = 0
                startTime = Now
                Do While thisSig < numSigs
                    Do While startTime.Add(timeSig(thisSig)) >= Now
                        System.Threading.Thread.Sleep(1)
                        If Not IsPlaying Then
                            Exit Sub
                        End If
                    Loop
                    If IsPlaying Then
                        soundSig(thisSig).Play()
                        thisSig = thisSig + 1  
                    End If
                Loop
            Wend
        End If
    End Sub
0
 

Author Comment

by:bti_admin
ID: 16701588
Hey!  That's important!  Thanks for pointing that out.  I will try it out, and make sure I'm not making the same mistake anywhere else in the code.

Thanks again!
0

Featured Post

Hire Technology Freelancers with Gigs

Work with freelancers specializing in everything from database administration to programming, who have proven themselves as experts in their field. Hire the best, collaborate easily, pay securely, and get projects done right.

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

Well, all of us have seen the multiple EXCEL.EXE's in task manager that won't die even if you call the .close, .dispose methods. Try this method to kill any excels in memory. You can copy the kill function to create a check function and replace the …
Parsing a CSV file is a task that we are confronted with regularly, and although there are a vast number of means to do this, as a newbie, the field can be confusing and the tools can seem complex. A simple solution to parsing a customized CSV fi…
Integration Management Part 2
This lesson discusses how to use a Mainform + Subforms in Microsoft Access to find and enter data for payments on orders. The sample data comes from a custom shop that builds and sells movable storage structures that are delivered to your property. …
Suggested Courses
Course of the Month20 days, 12 hours left to enroll

864 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question