bastibe / PySoundCard

PySoundCard is an audio library based on PortAudio, CFFI and NumPy
BSD 3-Clause "New" or "Revised" License
87 stars 9 forks source link

ENH: "keep-awake" class to keep stream responsive #32

Closed jeremygray closed 7 years ago

jeremygray commented 10 years ago

Idea: Play continuous silence in a concurrent Stream in a new thread to avoid latency if only use sound intermittently. Otherwise there can be ~0.5s latency to wake-up or restart something (soundcard hardware?), esp on Mac and maybe other laptops.

bastibe commented 10 years ago

This is a very interesting idea! I am a little concerned that using the callback interface might use more performance than necessary. If I understand this correctly, it is sufficient to play tiny fragments of audio every few seconds, right? It might be easier/more efficient to start a Python thread that does just that.

That said, we are planning to focus our attention on PySoundCard after the next release of PySoundFile. We are thinking about some high-level classes like the one you just built. If you have more ideas like this, we would be very interested in them. Here are some ideas: https://github.com/bastibe/PySoundCard/wiki/High-Level-API

jeremygray commented 10 years ago

In theory, I think playing a very short snippet of silence every ~20 sec should work. I'll try it out!

I'll check out the high-level class proposal, and will let you guys know if I have anything to add.

mgeier commented 10 years ago

Feel free to directly add to the Wiki page. If you think the page becomes too long, just create a new one.

mgeier commented 10 years ago

Your KeepAwake class looks much more like a work-around than a real solution. I don't really feel comfortable adding such a thing to PySoundCard.

I think a real solution would be to create a possibility to open a long-running stream and then, during its runtime, call functions to immediately play NumPy arrays. If no array is active, only zeros would be generated, just like in your proposal. This would probably also allow to play several sounds at the same time. It might also allow to queue sounds to be played back in succession to other sounds.

There should also be an option to trade off latency against jitter.

Anyway, a proposal for such functionality should be made in the above-mentioned Wiki page.

mgeier commented 10 years ago

Wouldn't this basically do what you want?

import pysoundcard as pa

def callback(in_data, out_data, time_info, status):
    out_data.fill(0)
    return pa.continue_flag

keep_awake = pa.Stream(input_device=False, callback=callback)

keep_awake.start()
# do something completely different here
keep_awake.stop()
jeremygray commented 10 years ago

Great discussion. I'll leave the pull request open or close as you prefer -- the code is not ready to be pulled in, obviously.

I like the idea of a long-running stream that is playing something actively or is playing zeroes (perhaps zeros only intermittently, every 20s or so), and you can add to that stream, schedule something to start playing at a certain time, etc. That seems useful for other reasons, and would take care of this issue as well. It is also more work than the work-around solution -- and I just saw the very latest code in the comments here, looks nice (have not tried it). It would be easier for users if they only needed one line of code instead of 6 lines to defeat the nuisance (power-saving) behavior of Mac soundcards. Or perhaps ideally it would be enabled by default, and people could instead opt to turn it off if they needed the additional performance.... ok, I'll add things to the wiki!

mgeier commented 9 years ago

There is a related question on the PortAudio mailing list: http://music.columbia.edu/pipermail/portaudio/2014-November/016360.html

mgeier commented 9 years ago

This may also be related: http://music.columbia.edu/pipermail/portaudio/2014-September/016323.html