Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with
or
.
Download ZIP
Browse files

Fixed AlsaInput, bugfix for AlsaOutput

  • Loading branch information...
commit 08a0d5e632d663b4e9506eba7398fa5d11779958 1 parent acbd4f1
@wedesoft authored
View
2  Rakefile
@@ -7,7 +7,7 @@ require 'rake/loaders/makefile'
require 'rbconfig'
PKG_NAME = 'hornetseye-alsa'
-PKG_VERSION = '1.1.2'
+PKG_VERSION = '1.2.0'
CFG = RbConfig::CONFIG
CXX = ENV[ 'CXX' ] || 'g++'
RB_FILES = FileList[ 'lib/**/*.rb' ]
View
193 ext/alsainput.cc
@@ -1,5 +1,5 @@
/* HornetsEye - Computer Vision with Ruby
- Copyright (C) 2006, 2007, 2008, 2009, 2010 Jan Wedekind
+ Copyright (C) 2012 Jan Wedekind
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -19,16 +19,17 @@ using namespace std;
VALUE AlsaInput::cRubyClass = Qnil;
-AlsaInput::AlsaInput( const string &pcmName, unsigned int rate,
- unsigned int channels, int periods,
- snd_pcm_uframes_t frames ) throw (Error):
- m_pcmHandle(NULL), m_pcmName( pcmName ), m_rate( rate ), m_channels( channels )
+AlsaInput::AlsaInput(const string &pcmName, unsigned int rate,
+ unsigned int channels) throw (Error):
+ m_pcmHandle(NULL), m_pcmName( pcmName ), m_rate( rate ), m_channels( channels ),
+ m_periodSize(1024), m_threadInitialised(false), m_start(0), m_count(0),
+ m_size(rate)
{
try {
snd_pcm_hw_params_t *hwParams;
- snd_pcm_hw_params_alloca( &hwParams );
- int err = snd_pcm_open( &m_pcmHandle, m_pcmName.c_str(), SND_PCM_STREAM_CAPTURE,
- 0 );
+ snd_pcm_hw_params_alloca(&hwParams);
+ int err = snd_pcm_open(&m_pcmHandle, m_pcmName.c_str(), SND_PCM_STREAM_CAPTURE,
+ 0);
ERRORMACRO( err >= 0, Error, , "Error opening PCM device \"" << m_pcmName
<< "\": " << snd_strerror( err ) );
err = snd_pcm_hw_params_any( m_pcmHandle, hwParams );
@@ -48,16 +49,22 @@ AlsaInput::AlsaInput( const string &pcmName, unsigned int rate,
err = snd_pcm_hw_params_set_channels( m_pcmHandle, hwParams, channels );
ERRORMACRO( err >= 0, Error, , "Error setting number of channels of PCM device \""
<< m_pcmName << "\" to " << channels << ": " << snd_strerror( err ) );
- err = snd_pcm_hw_params_set_periods( m_pcmHandle, hwParams, periods, 0 );
- ERRORMACRO( err >= 0, Error, , "Error setting number of periods of PCM device \""
- << m_pcmName << "\" to " << periods << ": " << snd_strerror( err ) );
- err = snd_pcm_hw_params_set_buffer_size_near( m_pcmHandle, hwParams, &frames );
- ERRORMACRO( err >= 0, Error, , "Error setting buffer size of PCM device \""
- << m_pcmName << "\" to " << frames << " frames: "
- << snd_strerror( err ) );
+ unsigned int bufferTime = 500000;
+ err = snd_pcm_hw_params_set_buffer_time_near(m_pcmHandle, hwParams, &bufferTime, NULL);
+ ERRORMACRO(err >= 0, Error, , "Error setting buffer time of PCM device \""
+ << m_pcmName << "\" to " << bufferTime << " us: " << snd_strerror(err));
+ unsigned int periods = 16;
+ err = snd_pcm_hw_params_set_periods_near(m_pcmHandle, hwParams, &periods, NULL);
+ ERRORMACRO(err >= 0, Error, , "Error setting periods of PCM device \""
+ << m_pcmName << "\" to " << periods << ": " << snd_strerror(err));
err = snd_pcm_hw_params( m_pcmHandle, hwParams );
ERRORMACRO( err >= 0, Error, , "Error setting parameters of PCM device \""
<< m_pcmName << "\": " << snd_strerror( err ) );
+ err = snd_pcm_hw_params_get_period_size(hwParams, &m_periodSize, NULL);
+ ERRORMACRO( err >= 0, Error, , "Error getting period size of PCM device \""
+ << m_pcmName << "\": " << snd_strerror( err ) );
+ err = pthread_mutex_init(&m_mutex, NULL);
+ ERRORMACRO(err == 0, Error, , "Error initialising mutex: " << strerror(err));
} catch ( Error &e ) {
close();
throw e;
@@ -72,29 +79,59 @@ AlsaInput::~AlsaInput(void)
void AlsaInput::close(void)
{
if ( m_pcmHandle != NULL ) {
- // drop();
+ drop();
+ pthread_mutex_destroy(&m_mutex);
snd_pcm_close( m_pcmHandle );
m_pcmHandle = NULL;
};
}
-SequencePtr AlsaInput::read( int samples ) throw (Error)
+SequencePtr AlsaInput::read(int samples) throw (Error)
{
- ERRORMACRO( m_pcmHandle != NULL, Error, , "PCM device \"" << m_pcmName
- << "\" is not open. Did you call \"close\" before?" );
- SequencePtr frame( new Sequence( (int)( samples * 2 * m_channels ) ) );
- int err;
- while ( ( err = snd_pcm_readi( m_pcmHandle, (short int *)frame->data(),
- samples ) ) < 0 ) {
- err = snd_pcm_recover( m_pcmHandle, err, 1 );
- ERRORMACRO( err >= 0, Error, , "Error reading audio frames from PCM device \""
- << m_pcmName << "\": " << snd_strerror( err ) );
+ ERRORMACRO(m_pcmHandle != NULL, Error, , "PCM device \"" << m_pcmName
+ << "\" is not open. Did you call \"close\" before?");
+ SequencePtr frame(new Sequence((int)(samples * 2 * m_channels)));
+ lock();
+ if (!m_data.get()) {
+ if (m_threadInitialised) pthread_join(m_thread, NULL);
+ m_data = boost::shared_array<short int>(new short int[m_size * m_channels]);
+ m_start = 0;
+ m_count = 0;
+ pthread_create(&m_thread, NULL, staticThreadFunc, this);
};
- ERRORMACRO( samples == err, Error, , "Only managed to read " << err << " of "
- << samples << " frames from PCM device \"" << m_pcmName << "\"" );
+ int n = samples;
+ if (n > m_count) n = m_count;
+ if (m_start + n > m_size) {
+ memcpy(frame->data(), m_data.get() + m_start * m_channels, (m_size - m_start) * 2 * m_channels);
+ memcpy(frame->data() + (m_size - m_start) * 2 * m_channels, m_data.get(), (n + m_start - m_size) * 2 * m_channels);
+ } else
+ memcpy(frame->data(), m_data.get() + m_start * m_channels, n * 2 * m_channels);
+ m_start += n;
+ if (m_start >= m_size) m_start -= m_size;
+ m_count -= n;
+ if (n < samples) {
+ try {
+ readi((short int *)frame->data() + n * m_channels * 2, samples - n);
+ } catch (Error &e) {
+ unlock();
+ throw e;
+ }
+ }
+ unlock();
return frame;
}
+void AlsaInput::drop(void) throw (Error)
+{
+ ERRORMACRO( m_pcmHandle != NULL, Error, , "PCM device \"" << m_pcmName
+ << "\" is not open. Did you call \"close\" before?" );
+ lock();
+ m_data.reset();
+ m_count = 0;
+ snd_pcm_drop(m_pcmHandle);
+ unlock();
+}
+
unsigned int AlsaInput::rate(void)
{
return m_rate;
@@ -109,6 +146,7 @@ int AlsaInput::avail(void) throw (Error)
{
ERRORMACRO( m_pcmHandle != NULL, Error, , "PCM device \"" << m_pcmName
<< "\" is not open. Did you call \"close\" before?" );
+ lock();
snd_pcm_sframes_t frames;
int err = 0;
while ( ( frames = snd_pcm_avail( m_pcmHandle ) ) < 0 ) {
@@ -117,22 +155,34 @@ int AlsaInput::avail(void) throw (Error)
"retrieval from PCM device \"" << m_pcmName << "\": "
<< snd_strerror( err ) );
};
+ frames += m_count;
+ unlock();
return frames;
}
-int AlsaInput::delay(void) throw (Error)
+void AlsaInput::readi(short int *data, int count)
{
- ERRORMACRO( m_pcmHandle != NULL, Error, , "PCM device \"" << m_pcmName
- << "\" is not open. Did you call \"close\" before?" );
- snd_pcm_sframes_t frames;
int err;
- while ( ( err = snd_pcm_delay( m_pcmHandle, &frames ) ) < 0 ) {
- err = snd_pcm_recover( m_pcmHandle, err, 1 );
- ERRORMACRO( err >= 0, Error, , "Error querying number of available frames for "
- "capture on PCM device \"" << m_pcmName << "\": "
- << snd_strerror( err ) );
+ while ((err = snd_pcm_readi(m_pcmHandle, data, count)) < 0) {
+ if (err == -EBADFD)
+ err = snd_pcm_prepare(m_pcmHandle);
+ else
+ err = snd_pcm_recover(m_pcmHandle, err, 1);
+ ERRORMACRO(err >= 0, Error, , "Error reading audio frames from PCM device \""
+ << m_pcmName << "\": " << snd_strerror(err));
};
- return frames;
+ ERRORMACRO(count == err, Error, , "Only managed to read " << err << " of "
+ << count << " frames from PCM device \"" << m_pcmName << "\"" );
+}
+
+void AlsaInput::lock(void)
+{
+ pthread_mutex_lock( &m_mutex );
+}
+
+void AlsaInput::unlock(void)
+{
+ pthread_mutex_unlock( &m_mutex );
}
void AlsaInput::prepare(void) throw (Error)
@@ -144,17 +194,59 @@ void AlsaInput::prepare(void) throw (Error)
<< "\": " << snd_strerror( err ) );
}
+void AlsaInput::threadFunc(void)
+{
+ bool quit = false;
+ while (!quit) {
+ snd_pcm_wait(m_pcmHandle, 1000);
+ try {
+ lock();
+ if (m_data.get()) {
+ int n = m_periodSize;
+ if (m_count + n > m_size) {
+ int m_size_new = m_size;
+ while(m_size_new < m_count + n) m_size_new = 2 * m_size_new;
+ boost::shared_array<short int> data(new short int[m_size_new * m_channels]);
+ if (m_start + m_count > m_size) {
+ memcpy(data.get(), m_data.get() + m_start * m_channels, (m_size - m_start) * 2 * m_channels);
+ memcpy(data.get() + (m_size - m_start) * m_channels, m_data.get(), (m_start + m_count - m_size) * 2 * m_channels);
+ } else
+ memcpy(data.get(), m_data.get() + m_start * m_channels, m_count * 2 * m_channels);
+ m_data = data;
+ m_start = 0;
+ m_size = m_size_new;
+ };
+ int offset = m_start + m_count;
+ if (offset > m_size) offset -= m_size;
+ if (offset + n > m_size) n = m_size - offset;
+ readi(m_data.get() + offset * m_channels, n);
+ m_count += n;
+ } else
+ quit = true;
+ unlock();
+ } catch (Error &e) {
+ quit = true;
+ m_data.reset();
+ unlock();
+ }
+ };
+}
+
+void *AlsaInput::staticThreadFunc( void *self )
+{
+ ((AlsaInput *)self)->threadFunc();
+ return self;
+}
VALUE AlsaInput::registerRubyClass( VALUE rbModule )
{
cRubyClass = rb_define_class_under( rbModule, "AlsaInput", rb_cObject );
- rb_define_singleton_method( cRubyClass, "new",
- RUBY_METHOD_FUNC( wrapNew ), 5 );
+ rb_define_singleton_method(cRubyClass, "new",
+ RUBY_METHOD_FUNC(wrapNew), 3);
rb_define_method( cRubyClass, "close", RUBY_METHOD_FUNC( wrapClose ), 0 );
rb_define_method( cRubyClass, "read", RUBY_METHOD_FUNC( wrapRead ), 1 );
rb_define_method( cRubyClass, "rate", RUBY_METHOD_FUNC( wrapRate ), 0 );
rb_define_method( cRubyClass, "channels", RUBY_METHOD_FUNC( wrapChannels ), 0 );
rb_define_method( cRubyClass, "avail", RUBY_METHOD_FUNC( wrapAvail ), 0 );
- rb_define_method( cRubyClass, "delay", RUBY_METHOD_FUNC( wrapDelay ), 0 );
rb_define_method( cRubyClass, "prepare", RUBY_METHOD_FUNC( wrapPrepare ), 0 );
}
@@ -164,14 +256,13 @@ void AlsaInput::deleteRubyObject( void *ptr )
}
VALUE AlsaInput::wrapNew( VALUE rbClass, VALUE rbPCMName, VALUE rbRate,
- VALUE rbChannels, VALUE rbPeriods, VALUE rbFrames )
+ VALUE rbChannels)
{
VALUE retVal = Qnil;
try {
rb_check_type( rbPCMName, T_STRING );
- AlsaInputPtr ptr( new AlsaInput( StringValuePtr( rbPCMName ),
- NUM2UINT( rbRate ), NUM2UINT( rbChannels ),
- NUM2INT( rbPeriods ), NUM2INT( rbFrames ) ) );
+ AlsaInputPtr ptr(new AlsaInput(StringValuePtr(rbPCMName),
+ NUM2UINT(rbRate), NUM2UINT(rbChannels)));
retVal = Data_Wrap_Struct( rbClass, 0, deleteRubyObject,
new AlsaInputPtr( ptr ) );
} catch ( exception &e ) {
@@ -224,18 +315,6 @@ VALUE AlsaInput::wrapAvail( VALUE rbSelf )
return rbRetVal;
}
-VALUE AlsaInput::wrapDelay( VALUE rbSelf )
-{
- VALUE rbRetVal = Qnil;
- try {
- AlsaInputPtr *self; Data_Get_Struct( rbSelf, AlsaInputPtr, self );
- rbRetVal = INT2NUM( (*self)->delay() );
- } catch ( exception &e ) {
- rb_raise( rb_eRuntimeError, "%s", e.what() );
- };
- return rbRetVal;
-}
-
VALUE AlsaInput::wrapPrepare( VALUE rbSelf )
{
try {
View
25 ext/alsainput.hh
@@ -1,5 +1,5 @@
/* HornetsEye - Computer Vision with Ruby
- Copyright (C) 2006, 2007, 2008, 2009, 2010 Jan Wedekind
+ Copyright (C) 2012 Jan Wedekind
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@@ -26,33 +26,44 @@ class AlsaInput
{
public:
AlsaInput( const std::string &pcmName = "default:0",
- unsigned int rate = 48000, unsigned int channels = 2,
- int periods = 16, snd_pcm_uframes_t frames = 1024 ) throw (Error);
+ unsigned int rate = 48000, unsigned int channels = 2) throw (Error);
virtual ~AlsaInput(void);
void close(void);
SequencePtr read( int samples ) throw (Error);
+ void drop(void) throw (Error);
unsigned int rate(void);
unsigned int channels(void);
int avail(void) throw (Error);
- int delay(void) throw (Error);
+ void lock(void);
+ void unlock(void);
void prepare(void) throw (Error);
static VALUE cRubyClass;
static VALUE registerRubyClass( VALUE rbModule );
static void deleteRubyObject( void *ptr );
- static VALUE wrapNew( VALUE rbClass, VALUE rbPCMName, VALUE rbRate,
- VALUE rbChannels, VALUE rbPeriods, VALUE rbFrames );
+ static VALUE wrapNew(VALUE rbClass, VALUE rbPCMName, VALUE rbRate,
+ VALUE rbChannels);
static VALUE wrapClose( VALUE rbSelf );
static VALUE wrapRead( VALUE rbSelf, VALUE rbSamples );
static VALUE wrapRate( VALUE rbSelf );
static VALUE wrapChannels( VALUE rbSelf );
static VALUE wrapAvail( VALUE rbSelf );
- static VALUE wrapDelay( VALUE rbSelf );
static VALUE wrapPrepare( VALUE rbSelf );
protected:
+ void readi(short int *data, int count);
+ void threadFunc(void);
+ static void *staticThreadFunc( void *self );
snd_pcm_t *m_pcmHandle;
std::string m_pcmName;
unsigned int m_rate;
unsigned int m_channels;
+ snd_pcm_uframes_t m_periodSize;
+ bool m_threadInitialised;
+ boost::shared_array<short int> m_data;
+ int m_start;
+ int m_count;
+ int m_size;
+ pthread_t m_thread;
+ pthread_mutex_t m_mutex;
};
typedef boost::shared_ptr< AlsaInput > AlsaInputPtr;
View
27 ext/alsaoutput.cc
@@ -77,7 +77,7 @@ AlsaOutput::~AlsaOutput(void)
void AlsaOutput::close(void)
{
- if ( m_pcmHandle != NULL ) {
+ if (m_pcmHandle != NULL) {
drain();
pthread_mutex_destroy(&m_mutex);
snd_pcm_close( m_pcmHandle );
@@ -113,10 +113,10 @@ void AlsaOutput::write(SequencePtr frame) throw (Error)
m_size = m_size_new;
};
int offset = m_start + m_count;
- if (offset > m_size) offset -= m_size;
+ if (offset >= m_size) offset -= m_size;
if (offset + n > m_size) {
memcpy(m_data.get() + offset * m_channels, frame->data(), (m_size - offset) * 2 * m_channels);
- memcpy(m_data.get(), frame->data() + (m_size - offset) * m_channels, (n + m_size - offset) * 2 * m_channels);
+ memcpy(m_data.get(), frame->data() + (m_size - offset) * 2 * m_channels, (n + offset - m_size) * 2 * m_channels);
} else
memcpy(m_data.get() + offset * m_channels, frame->data(), n * 2 * m_channels);
m_count += n;
@@ -155,19 +155,6 @@ unsigned int AlsaOutput::channels(void)
return m_channels;
}
-int AlsaOutput::avail(void) throw (Error)
-{
- snd_pcm_sframes_t frames;
- int err = 0;
- while ( ( frames = snd_pcm_avail( m_pcmHandle ) ) < 0 ) {
- err = snd_pcm_recover( m_pcmHandle, frames, 1 );
- ERRORMACRO( err >= 0, Error, , "Error querying number of available frames for "
- "update of PCM device \"" << m_pcmName << "\": "
- << snd_strerror( err ) );
- };
- return frames;
-}
-
int AlsaOutput::delay(void) throw (Error)
{
ERRORMACRO(m_pcmHandle != NULL, Error, , "PCM device \"" << m_pcmName
@@ -226,11 +213,8 @@ void AlsaOutput::threadFunc(void)
if (m_count <= 0) m_data.reset();
if (m_start >= m_size) m_start -= m_size;
if (m_data.get()) {
- if (m_start + n > m_size) {
- writei(m_data.get() + m_start * m_channels, m_size - m_start);
- writei(m_data.get(), m_start + n - m_size);
- } else
- writei(m_data.get() + m_start * m_channels, n);
+ if (m_start + n > m_size) n = m_size - m_start;
+ writei(m_data.get() + m_start * m_channels, n);
m_start += n;
m_count -= n;
} else
@@ -238,6 +222,7 @@ void AlsaOutput::threadFunc(void)
unlock();
} catch (Error &e) {
quit = true;
+ m_data.reset();
unlock();
}
};
View
1  ext/alsaoutput.hh
@@ -50,7 +50,6 @@ public:
static VALUE wrapChannels( VALUE rbSelf );
static VALUE wrapDelay( VALUE rbSelf );
protected:
- int avail(void) throw (Error);
void writei(short int *data, int count) throw (Error);
void threadFunc(void);
static void *staticThreadFunc( void *self );
View
7 lib/hornetseye-alsa/alsainput.rb
@@ -45,14 +45,11 @@ class << self
# @param [String] pcm_name Name of the PCM device
# @param [Integer] rate Desired sampling rate.
# @param [Integer] channels Number of channels (1=mono, 2=stereo).
- # @param [Integer] periods Number of audio frames of the input buffer.
- # @param [Integer] frames Size of the audio frames of the input buffer.
# @return [AlsaInput] An object for accessing the microphone.
#
# @see #rate
- def new( pcm_name = 'default', rate = 48000, channels = 2, periods = 8,
- frames = 1024 )
- orig_new pcm_name, rate, channels, periods, frames
+ def new(pcm_name = 'default', rate = 48000, channels = 2)
+ orig_new pcm_name, rate, channels
end
end
Please sign in to comment.
Something went wrong with that request. Please try again.