Git Product home page Git Product logo

Comments (11)

mgeier avatar mgeier commented on July 28, 2024

PortAudio is not claiming to be thread-safe: https://app.assembla.com/wiki/show/portaudio/Tips_Threading

Since opening and closing a stream doesn't work for you, you can try writing your own callback function and implement the "stop" functionality without actually stopping the stream (but just writing zeros afterwards).

Or you can try to somehow have play() and stop() on the same thread.

Often creating a separate thread isn't even necessary, since the callback function is called in a dedicated thread (created by PortAudio) anyway.

from python-sounddevice.

riggsd avatar riggsd commented on July 28, 2024

I am never creating multiple simultaneous threads, my application has a GUI thread and either one (if currently playing a sound) or zero (if not playing) audio thread. Both play() and stop() always happen in the same thread. This doesn't seem to be a multithreading issue as described in the above PortAudio documentation, but I most certainly don't understand the internals of PortAudio as well as you do.

I'd be fine with calling play() asynchronously if I could ask for the current status as to whether it is still playing audio or not. Is there a way to do this in the API? If I could query whether playback was still in progress, I wouldn't need the extra thread. My example code doesn't show it, but by asking whether Thread.is_alive() I can tell whether the blocking play() is still playing or not.

from python-sounddevice.

mgeier avatar mgeier commented on July 28, 2024

But in your code example, play() and stop() are called from different threads ... is your real use case different than that?

Currently, there is no API function to check if playing or not. The idea is that you use sd.wait(), which returns once playback has stopped. Is this not applicable in your case?

If not, I guess this could be implemented based on the active property of the underlying stream.
If you need that, please make a new issue giving reasons why you would like to have that, how the API should look like and why sd.wait() doesn't suffice. You can of course also make a PR!

from python-sounddevice.

riggsd avatar riggsd commented on July 28, 2024

My use case is that users are playing audio which may be as long as 150 seconds, and at the press of a button it should stop the audio if currently playing, or replay the audio from the start if not currently playing. I cannot have the GUI thread block while playing, so neither the non-blocking play() nor wait() would work.

I locally modified sd.play() to return a handle on its underlying OutputStream. If I replace the above example code's run() method to query the stream's active property, as you suggest, I am able to stop the non-blocking play():

    def run(self):
        samplerate, signal = wavfile.read(self.fname)
        stream = sounddevice.play(signal, samplerate, blocking=False)
        while stream.active:
            time.sleep(0.001)

Given that sd.play() and sd.stop() exist at the module level in an imperative style, it seems intuitive to me that there would also be a module-level sd.is_active() (or similar) to correspond with the use of those functions.

Alternatively, returning a handle on the OutputStream from sd.play() seems like a reasonable enough compromise.

I'm happy to write an issue (and PR) for either of these two options if you feel that I'm going down the right path. Again, I apologize that I don't have intimate knowledge of PortAudio... but that's exactly why I'm using your sounddevice module!

from python-sounddevice.

mgeier avatar mgeier commented on July 28, 2024

Thanks for providing your use case, there is indeed something missing in the sounddevice module ...

I don't really like the idea of returning something from play(), since this would look awkward when using it interactively (which is still its most important use case IMHO).

I also don't really like is_active(), although I can't really tell why ...

But here's another idea: what about returning the stream itself?
Something like sd.get_stream()?
With this, you could get your "active" information with sd.get_stream().active, but you could also get more details about the stream, like latency and cpu_load.

What do you think about this idea?

from python-sounddevice.

riggsd avatar riggsd commented on July 28, 2024

I have no problem with a top-level get_stream() function as opposed to having play(), rec(), and recplay() return the stream.

It looks like its implementation should be trivial, it need just hand back the global _last_context.stream reference.

I like that your Stream acts as context manager, so returning it directly you could do things like:

with sd.play(signal, samplerate) as stream:
    while stream.active:
        try:
            sleep(0.001)  # or whatever...
        except KeyboardInterrupt:
            stream.stop()

(Note: blocking play() or a call to wait() prevents KeyboardInterrupt on my Mac so that it can never be used to stop audio playback; I have to do tricks like the above to kill non-blocking playback if using the interpreter interactively. This is counter to what the docs say for wait().)

I'm not sure how useful a context manager is for the imperative API anyway, so it probably doesn't really matter... just as long as there's some way to get a handle on the stream. Thanks for considering this long-winded discussion!

from python-sounddevice.

mgeier avatar mgeier commented on July 28, 2024

It looks like its implementation should be trivial

Yes, most of sounddevice's implementation is trivial! It's just a wrapper after all. And CFFI does a great job simplifying the access to the C world.
The hard thing is getting the API right.

I have to do tricks like the above

I know a much better trick: Use Python 3!

I know that interrupting doesn't work with Python 2. That's because threading.Event.wait() doesn't support it. I was just too lazy to document this ...

Anyway ... your call to stream.stop() shouldn't be necessary because it's done anyway by the context manager.

it probably doesn't really matter...

I guess it doesn't.

So yes, please make a PR!

from python-sounddevice.

mgeier avatar mgeier commented on July 28, 2024

I don't know if it has anything to do with this issue, but I just saw that there was a thread-related issue on macOS with PyAudio: https://people.csail.mit.edu/hubert/git/?p=pyaudio.git;a=commit;h=884cedae7f2dca14b30ad3606cfa98653d57ae5e

AFAIK, CFFI releases the GIL for all C function calls anyway, so this is probably irrelevant here ...

from python-sounddevice.

mgeier avatar mgeier commented on July 28, 2024

@riggsd Shall we keep this open?
Is there something else (other than #70) to do?

from python-sounddevice.

riggsd avatar riggsd commented on July 28, 2024

from python-sounddevice.

riggsd avatar riggsd commented on July 28, 2024

Fixed by #70

from python-sounddevice.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.