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

demo not working #25

Closed jeremygray closed 10 years ago

jeremygray commented 10 years ago

I'm trying to play around with the pysoundcard demos in the readme.md. """Play an audio file.""" works fine, but I can't get """Loop back five seconds of audio data.""" to work. I tried several block lengths (16 to 1024). I get 5 seconds of:

/Library/Frameworks/Python.framework/Versions/7.3/lib/python2.7/site-packages/pysoundcard.py:592: RuntimeWarning: 689725.9183: Input overflowed
  self._handle_error(err)
/Library/Frameworks/Python.framework/Versions/7.3/lib/python2.7/site-packages/pysoundcard.py:592: RuntimeWarning: 689725.9190: Input overflowed
  self._handle_error(err)
...

Any ideas? I'm using python 2.7.3 32-bit, on Mac 10.9

mgeier commented 10 years ago

This seems like a low-level PortAudio problem, you probably have to change some settings (e.g. latency).

What kind of sound card do you have? Did you try any other software that uses PortAudio (e.g. Audacity)?

Could you please post the list of devices that PortAudio finds on your computer (use the devices() function)?

jeremygray commented 10 years ago

Thanks -- not sure how to set PortAudio settings through pysoundcard (I only see non-relevant google results).

I have the default soundcard that comes with MacBook Pro (2012). Audacity works well enough, as does pyo (digital sound processing package).

I have:

>>> d = devices()
>>> d.next()
{'struct_version': 2, 
'input_channels': 2, 
'default_high_output_latency': 0.1, 
'default_low_output_latency': 0.01, 
'default_sample_rate': 44100.0, 
'output_latency': 0.01, 
'name': u'Built-in Microph', 
'interleaved_data': True, 
'default_high_input_latency': 0.012154195011337868, 
'device_index': 0, 
'sample_format': <type 'numpy.float32'>, 
'default_low_input_latency': 0.00199546485260771, 
'host_api_index': 0, 
'input_latency': 0.00199546485260771, 
'output_channels': 0}
>>> d.next()
{'struct_version': 2, 
'input_channels': 0, 
'default_high_output_latency': 0.01671201814058957, 
'default_low_output_latency': 0.00655328798185941, 
'default_sample_rate': 44100.0, 
'output_latency': 0.00655328798185941, 
'name': u'Built-in Output', 
'interleaved_data': True, 
'default_high_input_latency': 0.1, 
'device_index': 1, 
'sample_format': <type 'numpy.float32'>, 
'default_low_input_latency': 0.01, 
'host_api_index': 0, 
'input_latency': 0.01, 
'output_channels': 2}
jeremygray commented 10 years ago

possibly of relevance -- looks like it might be using Core Audio?

>>> default_api()
{'struct_version': 1, 'name': u'Core Audio', 'api_idx': 0, 'type': 5L, 'default_output_device_index': 1, 'device_count': 2, 'default_input_device_index': 0}

and for completeness:

>>> pa_version()
(1899, u'PortAudio V19-devel (built Oct  4 2013 14:52:56)')
mgeier commented 10 years ago

I guess using Core Audio is normal on Mac, right?

Are the two devices you listed the only ones?

Are those the ones you're using with Audacity? Can you do playback and recording simultaneously in Audacity?

Can you do simultaneous playback and recording in pyo?

Did you build PortAudio yourself? They have an example program called paex_read_write_wire which you should also try. The source code is in examples/paex_read_write_wire.c.

jeremygray commented 10 years ago

Yes, core audio is typical / normal on mac. And yes, those are the only two devices; another .next() raises a StopIteration. I used those two with Audacity and pyo.

Recording in Audacity works, but the recording stops if I start playing a sound in another Audacity project window. (I don't tell recording to stop. It continues when I switch to the other project window, and only stops when I start playing something in that other window.)

I can play and record at the same time in pyo.

I did not build PortAudio myself (as far as I am aware!). I downloaded and tried building it now to find paex_read_write_wire, but the build did not go through.

Based on the error message ("RuntimeWarning: 689725.9190: Input overflowed"), I am wondering if the number of bytes per samples is off somehow on my machine, leading to the overflow.

Here's a minimal example for me to get that error:

from pysoundcard import Stream
block_length = 512
s = Stream(sample_rate=44100, block_length=block_length)
s.start()
s.read(block_length)

Interestingly a longer block length will succeed (at least for one read; it fails if I try multiple times in a loop like the example):

from pysoundcard import Stream
block_length = 1024
s = Stream(sample_rate=44100, block_length=block_length)
s.start()
s.read(block_length)
mgeier commented 10 years ago

I think I'm finally starting to understand what's the problem here!

Just to make sure: You are talking about the example that's mentioned in the section "Read/Write Mode", right?

Did you try the loop back example from the section "Callback Mode"?

Does it also produce overflow warnings?

jeremygray commented 10 years ago

Yes, I was trying the loop example from the section "Read/Write Mode".

The example from the section "Callback Mode" runs for me without errors of any kind. I am not sure what it is supposed to sound like, but I do hear a high-pitch, and it seems like if I tap some keys to make a sound, this triggers auditory feedback.

mgeier commented 10 years ago

OK, I think now I get it. I'm sorry that I asked for so many details which are in the end quite irrelevant.

Simply put, the read() and write() methods are not really meant for this use case. I think we should remove the example from the README file because it's misleading (see #26).

For any non-trivial use case, especially if it involves synchronous recording and playback, the callback API should be used.

I'm currently working on some high-level wrappers around the callback API that will hopefully make it much easier to use (#19).

The example from the section "Callback Mode" runs for me without errors of any kind. I am not sure what it is supposed to sound like, but I do hear a high-pitch, and it seems like if I tap some keys to make a sound, this triggers auditory feedback.

You are supposed to get on the output whatever reaches the input (but with a certain delay, a.k.a. latency). If, as in your case, you have a (i guess built-in) microphone and loudspeakers, you hear the sounds you're making (e.g. hitting the keyboard) plus an ugly acoustic feedback (like in every single movie where somebody grabs a microphone to make an announcement). The pitch of the feedback depends on the distance between the microphone and the loudspeakers and on the latency of the system. If you want to avoid the feedback, you should try the example using headphones.

jeremygray commented 10 years ago

Cool, all sounds good. The callback mode looks great -- I want to do things like voice-onset recording and voice-offset detection.

mgeier commented 10 years ago

If you only need recording you can try to use the blocking read() method, but I think on the long run you're better off using the callback mode.

If you have any ideas for high-level functions that would help for your use case, please comment on #19.

Please close this issue if it's resolved from your point of view.