Discussion:
blocking / non-blocking capturing
Kraus Philipp
2012-07-27 14:51:40 UTC
Permalink
Hello,

I'm new with OpenAL and I would like to record data to analyse the frequency of the data samples. I'm using also C++.
So my first question is: Exists a blocking capture function?

So at the moment I would like to read the record samples like this:
boost::shared_ptr<ALint> l_buffer( new ALint[p_buffersize] );
while (m_capturing) {

std::size_t i=0;
while (i < p_puffersize)
{
AlUInt l_samplesread = 0;
alcGetIntegerv(mydevice, ALC_CAPTURE_SAMPLES, &l_samplesread);
if (l_samplesread > 0) {
alcCaptureSamples(mydevice, (ALCvoid*)l_buffer+i, l_samplesread);
i += l_samplesread;
}

// wait for other threads
boost::this_thread::yield();
}

... do something with the buffer....
}

So can I substitute the inner while loop with an alc-call like this alcReadFull(mydevice, buffer, buffersize),
so that I can get the full-filled buffer after the call (blocked call)?

Is my example correct to read the data? This loops are within a thread, so after the data is received I normalized
the data in [-1,1], so I think audio that will get by the device are not cached.

Can anybody help me to create a correct working example

Thanks

Phil
Chris Robinson
2012-07-27 22:03:54 UTC
Permalink
Post by Kraus Philipp
Hello,
I'm new with OpenAL and I would like to record data to analyse the frequency of the data samples. I'm using also C++.
So my first question is: Exists a blocking capture function?
There isn't. It's easy enough to make your own blocking capture
function, though.
Post by Kraus Philipp
Is my example correct to read the data? This loops are within a thread, so after the data is received I normalized
the data in [-1,1], so I think audio that will get by the device are not cached.
boost::shared_ptr<ALint> l_buffer( new ALint[p_buffersize] );
A shared_ptr isn't for arrays. It'll call delete when it's finished with
instead of delete[], and you should avoid mixing new[] with delete. I
think boost may have another type designed for arrays, though, or you
can put an std::vector in there instead.

Second, you should use a type matching the sample format. This is
typically 16-bit, which should be ALshort.

Also, it may be a good idea to check for the ALC_EXT_disconnect[1]
extension so you can detect if the device becomes lost, to avoid an
infinite loop while waiting for more data that never comes.
Post by Kraus Philipp
AlUInt l_samplesread = 0;
alcGetIntegerv(mydevice, ALC_CAPTURE_SAMPLES,
&l_samplesread);
Post by Kraus Philipp
if (l_samplesread > 0) {
alcCaptureSamples(mydevice,
(ALCvoid*)l_buffer+i, l_samplesread);
Post by Kraus Philipp
i += l_samplesread;
}
You should also clamp l_samplesread so you don't write past the end of
the buffer.

Something like this (untested!):

boost::shared_ptr< std::vector<ALshort> > l_buffer( new
std::vector<ALshort>(p_buffersize) );
while(m_capturing)
{
std::size_t i=0;
while(i < p_puffersize)
{
ALint l_samplesread = 0;
alcGetIntegerv(mydevice, ALC_CAPTURE_SAMPLES, &l_samplesread);
if(l_samplesread > 0)
{
l_samplesread = std::min(l_samplesread, p_buffersize-i);
alcCaptureSamples(mydevice, (ALCvoid*)(l_buffer->data()+i),
l_samplesread);
i += l_samplesread;
}
else if(alcIsExtensionPresent(mydevice, "ALC_EXT_disconnect")
{
ALint connected = 0;
alcGetIntegerv(mydevice, ALC_CONNECTED, &connected);
if(!connected)
{
std::fill(l_buffer->begin()+i, l_buffer->end(), 0);
m_capturing = false;
break;
}
}

// wait for other threads
boost::this_thread::yield();
}

... do something with the buffer....
}

Hopes that helps. :)

[1] http://icculus.org/alextreg/wiki/ALC_EXT_disconnect
Philipp Kraus
2012-07-28 20:41:46 UTC
Permalink
Post by Chris Robinson
Post by Kraus Philipp
Hello,
I'm new with OpenAL and I would like to record data to analyse the
frequency of the data samples. I'm using also C++.
So my first question is: Exists a blocking capture function?
There isn't. It's easy enough to make your own blocking capture
function, though.
Post by Kraus Philipp
Is my example correct to read the data? This loops are within a thread,
so after the data is received I normalized
the data in [-1,1], so I think audio that will get by the device are not cached.
boost::shared_ptr<ALint> l_buffer( new ALint[p_buffersize] );
A shared_ptr isn't for arrays. It'll call delete when it's finished
with instead of delete[], and you should avoid mixing new[] with
delete. I think boost may have another type designed for arrays,
though, or you can put an std::vector in there instead.
Second, you should use a type matching the sample format. This is
typically 16-bit, which should be ALshort.
Also, it may be a good idea to check for the ALC_EXT_disconnect[1]
extension so you can detect if the device becomes lost, to avoid an
infinite loop while waiting for more data that never comes.
Post by Kraus Philipp
AlUInt l_samplesread = 0;
alcGetIntegerv(mydevice, ALC_CAPTURE_SAMPLES,
&l_samplesread);
Post by Kraus Philipp
if (l_samplesread > 0) {
alcCaptureSamples(mydevice,
(ALCvoid*)l_buffer+i, l_samplesread);
Post by Kraus Philipp
i += l_samplesread;
}
You should also clamp l_samplesread so you don't write past the end of
the buffer.
boost::shared_ptr< std::vector<ALshort> > l_buffer( new
std::vector<ALshort>(p_buffersize) );
while(m_capturing)
{
std::size_t i=0;
while(i < p_puffersize)
{
ALint l_samplesread = 0;
alcGetIntegerv(mydevice, ALC_CAPTURE_SAMPLES, &l_samplesread);
if(l_samplesread > 0)
{
l_samplesread = std::min(l_samplesread, p_buffersize-i);
alcCaptureSamples(mydevice, (ALCvoid*)(l_buffer->data()+i),
l_samplesread);
i += l_samplesread;
}
else if(alcIsExtensionPresent(mydevice, "ALC_EXT_disconnect")
{
ALint connected = 0;
alcGetIntegerv(mydevice, ALC_CONNECTED, &connected);
if(!connected)
{
std::fill(l_buffer->begin()+i, l_buffer->end(), 0);
m_capturing = false;
break;
}
}
// wait for other threads
boost::this_thread::yield();
}
... do something with the buffer....
}
Hopes that helps. :)
I have created this code:

std::vector<ALshort> l_buffer(p_buffersize);
for(std::size_t i=0; i < l_buffer.size(); )
{
ALint l_samplesread = 0;
alcGetIntegerv(l_capturedevice, ALC_CAPTURE_SAMPLES,
static_cast<ALCsizei>(sizeof(ALint)), &l_samplesread);

/*
if ( (!l_samplesread) && (alcIsExtensionPresent(l_capturedevice,
"ALC_EXT_disconnect")) )
{
ALint l_connected = 0;
alcGetIntegerv(l_capturedevice, ALC_CONNECTED,
static_cast<ALCsizei>(sizeof(ALint)), &l_connected);
if(!l_connected)
throw std::runtime_error("audio device is disconnected");
}
*/

if (l_samplesread > 0)
{
l_samplesread = std::min( static_cast<std::size_t>(l_samplesread),
l_buffer.size()-i);
alcCaptureSamples(l_capturedevice, (ALCvoid*)(&l_buffer[i]),
l_samplesread);
i += l_samplesread;
}

boost::this_thread::yield();
}

If I show my l_buffer values, it generates always the same data, so it
seems, I have created an error dyring the alcCaptureSamples
or the interface results wrong data.
I have initializate my interface with the sampling frequency 44.1k and
mono 8 bit. The interface is named on OSX with "CoreAudio Default".

Can you help my to find out which mistake I have made?

Thanks

Phil
Chris Robinson
2012-07-28 22:56:21 UTC
Permalink
Post by Philipp Kraus
If I show my l_buffer values, it generates always the same data, so it
seems, I have created an error dyring the alcCaptureSamples
or the interface results wrong data. I have initializate my interface
with the sampling frequency 44.1k and mono 8 bit. The interface is named
on OSX with "CoreAudio Default".
If you're capturing 8-bit, then you need to store it using ALubyte
instead of ALshort.

If the data it captures is always silence (values are all near 128),
then check to make sure other OSX apps can properly capture audio. Make
sure also that you've called alcCaptureStart(l_capturedevice), and that
no errors are being generated (check alcGetError(l_capturedevice)).
Philipp Kraus
2012-08-07 12:58:20 UTC
Permalink
Post by Chris Robinson
Post by Philipp Kraus
If I show my l_buffer values, it generates always the same data, so it
seems, I have created an error dyring the alcCaptureSamples
or the interface results wrong data. I have initializate my interface
with the sampling frequency 44.1k and mono 8 bit. The interface is named
on OSX with "CoreAudio Default".
If you're capturing 8-bit, then you need to store it using ALubyte
instead of ALshort.
If the data it captures is always silence (values are all near 128),
then check to make sure other OSX apps can properly capture audio. Make
sure also that you've called alcCaptureStart(l_capturedevice), and that
no errors are being generated (check alcGetError(l_capturedevice)).
Sorry, I have not enough time to test it, but I will try it in the next days.
So imho I have create a C++ class and do this code for creating a
capture audio device:

in the ctor I create the device

m_device is a pointer to ALCdevice and p_device the capture device name.

m_device( alcOpenDevice( p_device.c_str() ) )

in my capture methode I call this
ALCdevice* l_capturedevice = alcCaptureOpenDevice(
alcGetString(m_device, ALC_CAPTURE_DEVICE_SPECIFIER),
(ALCuint)p_samplefrequency, AL_FORMAT_MONO8,
(ALCsizei)p_buffersize)
alcCaptureStart(l_capturedevice);
// here I will read the samples with a blocking structure

Is this correct?

Thanks

Phil
Chris Robinson
2012-08-08 07:08:32 UTC
Permalink
Post by Philipp Kraus
in the ctor I create the device
m_device is a pointer to ALCdevice and p_device the capture device name.
m_device( alcOpenDevice( p_device.c_str() ) )
in my capture methode I call this
ALCdevice* l_capturedevice = alcCaptureOpenDevice(
alcGetString(m_device, ALC_CAPTURE_DEVICE_SPECIFIER),
(ALCuint)p_samplefrequency, AL_FORMAT_MONO8, (ALCsizei)p_buffersize)
alcCaptureStart(l_capturedevice);
// here I will read the samples with a blocking structure
Is this correct?
Unfortunately that's not correct.

You can't rely on capture devices having the same name as playback
devices, or a capture device even having a corresponding playback
device. You would need to call alcCaptureOpenDevice with the name passed
in, while making sure the name came from the list of enumerated capture
device names.

It's also not technically valid to get the ALC_CAPTURE_DEVICE_SPECIFIER
string on a playback device, as far as I'm aware.

It would likely be best to remove the m_device stuff for playback, and
open the capture device when the class initializes instead.
Eric Wing
2012-08-08 08:05:26 UTC
Permalink
Post by Chris Robinson
Post by Philipp Kraus
in the ctor I create the device
m_device is a pointer to ALCdevice and p_device the capture device name.
m_device( alcOpenDevice( p_device.c_str() ) )
in my capture methode I call this
ALCdevice* l_capturedevice = alcCaptureOpenDevice(
alcGetString(m_device, ALC_CAPTURE_DEVICE_SPECIFIER),
(ALCuint)p_samplefrequency, AL_FORMAT_MONO8, (ALCsizei)p_buffersize)
alcCaptureStart(l_capturedevice);
// here I will read the samples with a blocking structure
Is this correct?
Unfortunately that's not correct.
You can't rely on capture devices having the same name as playback
devices, or a capture device even having a corresponding playback
device. You would need to call alcCaptureOpenDevice with the name passed
in, while making sure the name came from the list of enumerated capture
device names.
It's also not technically valid to get the ALC_CAPTURE_DEVICE_SPECIFIER
string on a playback device, as far as I'm aware.
If I recall, it is even worse than that. On Mac, it used to be the
case that you could not programmatically select a capture device via
OpenAL and only the user's default device could be opened (via passing
NULL as the parameter). The user would have to change this in System
Preferences for their account. I had an open bug and a preliminary
patch to Apple's implementation to fix this, but it went nowhere. (My
patch didn't handle USB devices coming and going on the fly which is
why it was not accepted and to my knowledge, Apple has not added this
functionality over the past years since I first reported that bug.)


You mentioned Core Audio which means Mac or iOS, so I will plug my
book :) I have an OpenAL capture example in my book in Chapter 12 for
both Mac and iOS. You can find a link to the source code here:
http://playcontrol.net/iphonegamebook/links.html

-Eric
--
Beginning iPhone Games Development
http://playcontrol.net/iphonegamebook/
Continue reading on narkive:
Loading...