From 0f09f24663c9c97196c2149de4dcd78afbf52f6e Mon Sep 17 00:00:00 2001 From: Jan Wedekind Date: Sun, 14 Nov 2010 22:58:23 +0000 Subject: [PATCH] Added empty AlsaInput implementation --- ext/alsainput.cc | 175 ++++++++++++++++++++++++++++++ ext/alsainput.hh | 55 ++++++++++ ext/alsaoutput.cc | 21 +--- ext/alsaoutput.hh | 5 +- ext/init.cc | 2 + lib/hornetseye-alsa/alsainput.rb | 42 +++++++ lib/hornetseye-alsa/alsaoutput.rb | 2 +- lib/hornetseye_alsa_ext.rb | 1 + 8 files changed, 281 insertions(+), 22 deletions(-) create mode 100644 ext/alsainput.cc create mode 100644 ext/alsainput.hh create mode 100644 lib/hornetseye-alsa/alsainput.rb diff --git a/ext/alsainput.cc b/ext/alsainput.cc new file mode 100644 index 0000000..0c8236e --- /dev/null +++ b/ext/alsainput.cc @@ -0,0 +1,175 @@ +/* HornetsEye - Computer Vision with Ruby + Copyright (C) 2006, 2007, 2008, 2009, 2010 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ +#include "alsainput.hh" + +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 ) +{ + 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, + SND_PCM_NONBLOCK ); + ERRORMACRO( err >= 0, Error, , "Error opening PCM device \"" << m_pcmName + << "\": " << snd_strerror( err ) ); + err = snd_pcm_hw_params_any( m_pcmHandle, hwParams ); + ERRORMACRO( err >= 0, Error, , "Unable to configure the PCM device \"" + << m_pcmName << "\": " << snd_strerror( err ) ); + err = snd_pcm_hw_params_set_access( m_pcmHandle, hwParams, + SND_PCM_ACCESS_RW_INTERLEAVED ); + ERRORMACRO( err >= 0, Error, , "Error setting PCM device \"" + << m_pcmName << "\" to interlaced access: " << snd_strerror( err ) ); + err = snd_pcm_hw_params_set_format( m_pcmHandle, hwParams, + SND_PCM_FORMAT_S16_LE ); + ERRORMACRO( err >= 0, Error, , "Error setting PCM device \"" << m_pcmName + << "\" to 16-bit signed integer format: " << snd_strerror( err ) ); + err = snd_pcm_hw_params_set_rate_near( m_pcmHandle, hwParams, &m_rate, 0 ); + ERRORMACRO( err >= 0, Error, , "Error setting sampling rate of PCM device \"" + << m_pcmName << "\" to " << rate << " Hz: " << snd_strerror( err ) ); + 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 ) ); + err = snd_pcm_hw_params( m_pcmHandle, hwParams ); + ERRORMACRO( err >= 0, Error, , "Error setting parameters of PCM device \"" + << m_pcmName << "\": " << snd_strerror( err ) ); + } catch ( Error &e ) { + close(); + throw e; + }; +} + +AlsaInput::~AlsaInput(void) +{ + close(); +} + +void AlsaInput::close(void) +{ + if ( m_pcmHandle != NULL ) { + // drop(); + snd_pcm_close( m_pcmHandle ); + m_pcmHandle = NULL; + }; +} + +SequencePtr AlsaInput::read(void) throw (Error) +{ + ERRORMACRO( m_pcmHandle != NULL, Error, , "PCM device \"" << m_pcmName + << "\" is not open. Did you call \"close\" before?" ); +#if 0 + int n = frame->size() / ( 2 * m_channels ); + int err; + while ( ( err = snd_pcm_writei( m_pcmHandle, (short int *)frame->data(), + n ) ) < 0 ) { + err = snd_pcm_recover( m_pcmHandle, err, 1 ); + ERRORMACRO( err >= 0, Error, , "Error writing audio frames to PCM device \"" + << m_pcmName << "\": " << snd_strerror( err ) ); + }; + ERRORMACRO( n == err, Error, , "Only managed to write " << err << " of " << n + << " frames to PCM device \"" << m_pcmName << "\"" ); +#endif + SequencePtr sequence( new Sequence( 1024 ) ); + return sequence; +} + +unsigned int AlsaInput::rate(void) +{ + return m_rate; +} + +unsigned int AlsaInput::channels(void) +{ + return m_channels; +} + +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_method( cRubyClass, "close", RUBY_METHOD_FUNC( wrapClose ), 0 ); + rb_define_method( cRubyClass, "read", RUBY_METHOD_FUNC( wrapRead ), 0 ); + rb_define_method( cRubyClass, "rate", RUBY_METHOD_FUNC( wrapRate ), 0 ); + rb_define_method( cRubyClass, "channels", RUBY_METHOD_FUNC( wrapChannels ), 0 ); +} + +void AlsaInput::deleteRubyObject( void *ptr ) +{ + delete (AlsaInputPtr *)ptr; +} + +VALUE AlsaInput::wrapNew( VALUE rbClass, VALUE rbPCMName, VALUE rbRate, + VALUE rbChannels, VALUE rbPeriods, VALUE rbFrames ) +{ + VALUE retVal = Qnil; + try { + rb_check_type( rbPCMName, T_STRING ); + AlsaInputPtr ptr( new AlsaInput( StringValuePtr( rbPCMName ), + NUM2UINT( rbRate ), NUM2UINT( rbChannels ), + NUM2INT( rbPeriods ), NUM2INT( rbFrames ) ) ); + retVal = Data_Wrap_Struct( rbClass, 0, deleteRubyObject, + new AlsaInputPtr( ptr ) ); + } catch ( exception &e ) { + rb_raise( rb_eRuntimeError, "%s", e.what() ); + }; + return retVal; +} + +VALUE AlsaInput::wrapClose( VALUE rbSelf ) +{ + AlsaInputPtr *self; Data_Get_Struct( rbSelf, AlsaInputPtr, self ); + (*self)->close(); + return rbSelf; +} + +VALUE AlsaInput::wrapRead( VALUE rbSelf ) +{ + VALUE rbRetVal = Qnil; + try { + AlsaInputPtr *self; Data_Get_Struct( rbSelf, AlsaInputPtr, self ); + SequencePtr sequence( (*self)->read() ); + rbRetVal = sequence->rubyObject(); + } catch ( exception &e ) { + rb_raise( rb_eRuntimeError, "%s", e.what() ); + }; + return rbRetVal; +} + +VALUE AlsaInput::wrapRate( VALUE rbSelf ) +{ + AlsaInputPtr *self; Data_Get_Struct( rbSelf, AlsaInputPtr, self ); + return UINT2NUM( (*self)->rate() ); +} + +VALUE AlsaInput::wrapChannels( VALUE rbSelf ) +{ + AlsaInputPtr *self; Data_Get_Struct( rbSelf, AlsaInputPtr, self ); + return UINT2NUM( (*self)->channels() ); +} + diff --git a/ext/alsainput.hh b/ext/alsainput.hh new file mode 100644 index 0000000..27ad996 --- /dev/null +++ b/ext/alsainput.hh @@ -0,0 +1,55 @@ +/* HornetsEye - Computer Vision with Ruby + Copyright (C) 2006, 2007, 2008, 2009, 2010 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 + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . */ +#ifndef ALSAINPUT_HH +#define ALSAINPUT_HH + +#include +#include +#include "rubyinc.hh" +#include "error.hh" +#include "sequence.hh" + +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); + virtual ~AlsaInput(void); + void close(void); + SequencePtr read(void) throw (Error); + unsigned int rate(void); + unsigned int channels(void); + 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 wrapClose( VALUE rbSelf ); + static VALUE wrapRead( VALUE rbSelf ); + static VALUE wrapRate( VALUE rbSelf ); + static VALUE wrapChannels( VALUE rbSelf ); +protected: + snd_pcm_t *m_pcmHandle; + std::string m_pcmName; + unsigned int m_rate; + unsigned int m_channels; +}; + +typedef boost::shared_ptr< AlsaInput > AlsaInputPtr; + +#endif + diff --git a/ext/alsaoutput.cc b/ext/alsaoutput.cc index 69f0d36..4259e7c 100644 --- a/ext/alsaoutput.cc +++ b/ext/alsaoutput.cc @@ -21,9 +21,8 @@ VALUE AlsaOutput::cRubyClass = Qnil; AlsaOutput::AlsaOutput( const string &pcmName, unsigned int rate, unsigned int channels, int periods, - int frames ) throw (Error): - m_pcmHandle(NULL), m_pcmName( pcmName ), m_rate( rate ), m_channels( channels ), - m_frames( frames ) + snd_pcm_uframes_t frames ) throw (Error): + m_pcmHandle(NULL), m_pcmName( pcmName ), m_rate( rate ), m_channels( channels ) { try { snd_pcm_hw_params_t *hwParams; @@ -52,9 +51,9 @@ AlsaOutput::AlsaOutput( const string &pcmName, unsigned int rate, 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, &m_frames ); + 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 " << m_frames << " frames: " + << m_pcmName << "\" to " << frames << " frames: " << snd_strerror( err ) ); err = snd_pcm_hw_params( m_pcmHandle, hwParams ); ERRORMACRO( err >= 0, Error, , "Error setting parameters of PCM device \"" @@ -119,11 +118,6 @@ unsigned int AlsaOutput::channels(void) return m_channels; } -unsigned int AlsaOutput::frames(void) -{ - return m_frames; -} - int AlsaOutput::avail(void) throw (Error) { ERRORMACRO( m_pcmHandle != NULL, Error, , "PCM device \"" << m_pcmName @@ -174,7 +168,6 @@ VALUE AlsaOutput::registerRubyClass( VALUE rbModule ) rb_define_method( cRubyClass, "drain", RUBY_METHOD_FUNC( wrapDrain ), 0 ); rb_define_method( cRubyClass, "rate", RUBY_METHOD_FUNC( wrapRate ), 0 ); rb_define_method( cRubyClass, "channels", RUBY_METHOD_FUNC( wrapChannels ), 0 ); - rb_define_method( cRubyClass, "frames", RUBY_METHOD_FUNC( wrapFrames ), 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 ); @@ -255,12 +248,6 @@ VALUE AlsaOutput::wrapChannels( VALUE rbSelf ) return UINT2NUM( (*self)->channels() ); } -VALUE AlsaOutput::wrapFrames( VALUE rbSelf ) -{ - AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self ); - return UINT2NUM( (*self)->frames() ); -} - VALUE AlsaOutput::wrapAvail( VALUE rbSelf ) { VALUE rbRetVal = Qnil; diff --git a/ext/alsaoutput.hh b/ext/alsaoutput.hh index ba9aa37..15c28b5 100644 --- a/ext/alsaoutput.hh +++ b/ext/alsaoutput.hh @@ -27,7 +27,7 @@ class AlsaOutput public: AlsaOutput( const std::string &pcmName = "default:0", unsigned int rate = 48000, unsigned int channels = 2, - int periods = 2, int frames = 1024 ) throw (Error); + int periods = 16, snd_pcm_uframes_t frames = 1024 ) throw (Error); virtual ~AlsaOutput(void); void close(void); void write( SequencePtr sequence ) throw (Error); @@ -35,7 +35,6 @@ public: void drain(void) throw (Error); unsigned int rate(void); unsigned int channels(void); - unsigned int frames(void); int avail(void) throw (Error); int delay(void) throw (Error); void prepare(void) throw (Error); @@ -50,7 +49,6 @@ public: static VALUE wrapDrain( VALUE rbSelf ); static VALUE wrapRate( VALUE rbSelf ); static VALUE wrapChannels( VALUE rbSelf ); - static VALUE wrapFrames( VALUE rbSelf ); static VALUE wrapAvail( VALUE rbSelf ); static VALUE wrapDelay( VALUE rbSelf ); static VALUE wrapPrepare( VALUE rbSelf ); @@ -59,7 +57,6 @@ protected: std::string m_pcmName; unsigned int m_rate; unsigned int m_channels; - snd_pcm_uframes_t m_frames; }; typedef boost::shared_ptr< AlsaOutput > AlsaOutputPtr; diff --git a/ext/init.cc b/ext/init.cc index 05d891e..a723f63 100644 --- a/ext/init.cc +++ b/ext/init.cc @@ -14,6 +14,7 @@ You should have received a copy of the GNU General Public License along with this program. If not, see . */ #include "alsaoutput.hh" +#include "alsainput.hh" #ifdef WIN32 #define DLLEXPORT __declspec(dllexport) @@ -32,6 +33,7 @@ extern "C" { rb_require( "multiarray" ); VALUE rbHornetseye = rb_define_module( "Hornetseye" ); AlsaOutput::registerRubyClass( rbHornetseye ); + AlsaInput::registerRubyClass( rbHornetseye ); rb_require( "hornetseye_alsa_ext.rb" ); } diff --git a/lib/hornetseye-alsa/alsainput.rb b/lib/hornetseye-alsa/alsainput.rb new file mode 100644 index 0000000..f7b4814 --- /dev/null +++ b/lib/hornetseye-alsa/alsainput.rb @@ -0,0 +1,42 @@ +# hornetseye-alsa - Play audio data using libalsa +# Copyright (C) 2010 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 +# the Free Software Foundation, either version 3 of the License, or +# (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program. If not, see . + +# Namespace of Hornetseye computer vision library +module Hornetseye + + class AlsaInput + + class << self + + alias_method :orig_new, :new + + def new( pcm_name = 'default:0', rate = 48000, channels = 2, periods = 16, + frames = 1024 ) + orig_new pcm_name, rate, channels, periods, frames + end + + end + + alias_method :orig_read, :read + + def read + orig_read + end + + end + +end + diff --git a/lib/hornetseye-alsa/alsaoutput.rb b/lib/hornetseye-alsa/alsaoutput.rb index 4c5770e..944cd46 100644 --- a/lib/hornetseye-alsa/alsaoutput.rb +++ b/lib/hornetseye-alsa/alsaoutput.rb @@ -43,7 +43,7 @@ def write( frame ) raise "Audio frame must have #{channels} channel(s) but had " + "#{frame.shape.first}" end - orig_write Sequence( UBYTE, 2 * frame.size ).new( frame.memory ) + orig_write Hornetseye::Sequence( UBYTE, 2 * frame.size ).new( frame.memory ) end end diff --git a/lib/hornetseye_alsa_ext.rb b/lib/hornetseye_alsa_ext.rb index 28fb4ab..dfd84ab 100644 --- a/lib/hornetseye_alsa_ext.rb +++ b/lib/hornetseye_alsa_ext.rb @@ -14,4 +14,5 @@ # You should have received a copy of the GNU General Public License # along with this program. If not, see . require 'hornetseye-alsa/alsaoutput' +require 'hornetseye-alsa/alsainput'