hornetseye-alsa 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.md ADDED
@@ -0,0 +1,4 @@
1
+ hornetseye-alsa
2
+ ======
3
+ This Ruby extension provides an interface for playing audio data using ALSA.
4
+
data/Rakefile ADDED
@@ -0,0 +1,184 @@
1
+ #!/usr/bin/env ruby
2
+ require 'date'
3
+ require 'rake/clean'
4
+ require 'rake/testtask'
5
+ require 'rake/packagetask'
6
+ require 'rake/loaders/makefile'
7
+ require 'rbconfig'
8
+
9
+ PKG_NAME = 'hornetseye-alsa'
10
+ PKG_VERSION = '0.1.0'
11
+ CXX = ENV[ 'CXX' ] || 'g++'
12
+ STRIP = ENV[ 'STRIP' ] || 'strip'
13
+ RB_FILES = FileList[ 'lib/**/*.rb' ]
14
+ CC_FILES = FileList[ 'ext/*.cc' ]
15
+ HH_FILES = FileList[ 'ext/*.hh' ] + FileList[ 'ext/*.tcc' ]
16
+ TC_FILES = FileList[ 'test/tc_*.rb' ]
17
+ TS_FILES = FileList[ 'test/ts_*.rb' ]
18
+ SO_FILE = "ext/#{PKG_NAME.tr '\-', '_'}.so"
19
+ PKG_FILES = [ 'Rakefile', 'README.md', 'COPYING', '.document' ] +
20
+ RB_FILES + CC_FILES + HH_FILES + TS_FILES + TC_FILES
21
+ BIN_FILES = [ 'README.md', 'COPYING', '.document', SO_FILE ] +
22
+ RB_FILES + TS_FILES + TC_FILES
23
+ SUMMARY = %q{Play audio data using libalsa}
24
+ DESCRIPTION = %q{This Ruby extension provides an interface for playing audio data using ALSA.}
25
+ AUTHOR = %q{Jan Wedekind}
26
+ EMAIL = %q{jan@wedesoft.de}
27
+ HOMEPAGE = %q{http://wedesoft.github.com/hornetseye-alsa/}
28
+
29
+ OBJ = CC_FILES.ext 'o'
30
+ $CXXFLAGS = ENV[ 'CXXFLAGS' ] || ''
31
+ $CXXFLAGS = "#{$CXXFLAGS} -fPIC -DNDEBUG"
32
+ if RbConfig::CONFIG[ 'rubyhdrdir' ]
33
+ $CXXFLAGS = "#{$CXXFLAGS} -I#{RbConfig::CONFIG[ 'rubyhdrdir' ]} " +
34
+ "-I#{RbConfig::CONFIG[ 'rubyhdrdir' ]}/#{RbConfig::CONFIG[ 'arch' ]}"
35
+ else
36
+ $CXXFLAGS += "#{$CXXFLAGS} -I#{RbConfig::CONFIG[ 'archdir' ]}"
37
+ end
38
+ $LIBRUBYARG = RbConfig::CONFIG[ 'LIBRUBYARG' ]
39
+ $SITELIBDIR = RbConfig::CONFIG[ 'sitelibdir' ]
40
+ $SITEARCHDIR = RbConfig::CONFIG[ 'sitearchdir' ]
41
+
42
+ task :default => :all
43
+
44
+ desc 'Compile Ruby extension (default)'
45
+ task :all => [ SO_FILE ]
46
+
47
+ file SO_FILE => OBJ do |t|
48
+ sh "#{CXX} -shared -o #{t.name} #{OBJ} -lasound #{$LIBRUBYARG}"
49
+ sh "#{STRIP} --strip-all #{t.name}"
50
+ end
51
+
52
+ task :test => [ SO_FILE ]
53
+
54
+ desc 'Install Ruby extension'
55
+ task :install => :all do
56
+ verbose true do
57
+ for f in RB_FILES do
58
+ FileUtils.mkdir_p "#{$SITELIBDIR}/#{File.dirname f.gsub( /^lib\//, '' )}"
59
+ FileUtils.cp_r f, "#{$SITELIBDIR}/#{f.gsub( /^lib\//, '' )}"
60
+ end
61
+ FileUtils.mkdir_p $SITEARCHDIR
62
+ FileUtils.cp SO_FILE, "#{$SITEARCHDIR}/#{File.basename SO_FILE}"
63
+ end
64
+ end
65
+
66
+ desc 'Uninstall Ruby extension'
67
+ task :uninstall do
68
+ verbose true do
69
+ for f in RB_FILES do
70
+ FileUtils.rm_f "#{$SITELIBDIR}/#{f.gsub /^lib\//, ''}"
71
+ end
72
+ FileUtils.rm_f "#{$SITEARCHDIR}/#{File.basename SO_FILE}"
73
+ end
74
+ end
75
+
76
+ Rake::TestTask.new do |t|
77
+ t.libs << 'ext'
78
+ t.test_files = TC_FILES
79
+ end
80
+
81
+ begin
82
+ require 'yard'
83
+ YARD::Rake::YardocTask.new :yard do |y|
84
+ y.options << '--no-private'
85
+ y.files << FileList[ 'lib/**/*.rb' ]
86
+ end
87
+ rescue LoadError
88
+ STDERR.puts 'Please install \'yard\' if you want to generate documentation'
89
+ end
90
+
91
+ Rake::PackageTask.new PKG_NAME, PKG_VERSION do |p|
92
+ p.need_tar = true
93
+ p.package_files = PKG_FILES
94
+ end
95
+
96
+ begin
97
+ require 'rubygems'
98
+ require 'rubygems/builder'
99
+ $SPEC = Gem::Specification.new do |s|
100
+ s.name = PKG_NAME
101
+ s.version = PKG_VERSION
102
+ s.platform = Gem::Platform::RUBY
103
+ s.date = Date.today.to_s
104
+ s.summary = SUMMARY
105
+ s.description = DESCRIPTION
106
+ s.author = AUTHOR
107
+ s.email = EMAIL
108
+ s.homepage = HOMEPAGE
109
+ s.files = PKG_FILES
110
+ s.test_files = TC_FILES
111
+ s.require_paths = [ 'lib', 'ext' ]
112
+ s.rubyforge_project = %q{hornetseye}
113
+ s.extensions = %w{Rakefile}
114
+ s.has_rdoc = 'yard'
115
+ s.extra_rdoc_files = []
116
+ s.rdoc_options = %w{--no-private}
117
+ s.add_dependency %<malloc>, [ '~> 1.2' ]
118
+ s.add_dependency %<multiarray>, [ '~> 0.11' ]
119
+ s.add_dependency %<hornetseye-frame>, [ '~> 0.7' ]
120
+ s.add_development_dependency %q{rake}
121
+ end
122
+ GEM_SOURCE = "#{PKG_NAME}-#{PKG_VERSION}.gem"
123
+ $BINSPEC = Gem::Specification.new do |s|
124
+ s.name = PKG_NAME
125
+ s.version = PKG_VERSION
126
+ s.platform = Gem::Platform::CURRENT
127
+ s.date = Date.today.to_s
128
+ s.summary = SUMMARY
129
+ s.description = DESCRIPTION
130
+ s.author = AUTHOR
131
+ s.email = EMAIL
132
+ s.homepage = HOMEPAGE
133
+ s.files = BIN_FILES
134
+ s.test_files = TC_FILES
135
+ s.require_paths = [ 'lib', 'ext' ]
136
+ s.rubyforge_project = %q{hornetseye}
137
+ s.has_rdoc = 'yard'
138
+ s.extra_rdoc_files = []
139
+ s.rdoc_options = %w{--no-private}
140
+ s.add_dependency %<malloc>, [ '~> 1.2' ]
141
+ s.add_dependency %<multiarray>, [ '~> 0.11' ]
142
+ end
143
+ GEM_BINARY = "#{PKG_NAME}-#{PKG_VERSION}-#{$BINSPEC.platform}.gem"
144
+ desc "Build the gem file #{GEM_SOURCE}"
145
+ task :gem => [ "pkg/#{GEM_SOURCE}" ]
146
+ file "pkg/#{GEM_SOURCE}" => [ 'pkg' ] + $SPEC.files do
147
+ when_writing 'Creating GEM' do
148
+ Gem::Builder.new( $SPEC ).build
149
+ verbose true do
150
+ FileUtils.mv GEM_SOURCE, "pkg/#{GEM_SOURCE}"
151
+ end
152
+ end
153
+ end
154
+ desc "Build the gem file #{GEM_BINARY}"
155
+ task :gem_binary => [ "pkg/#{GEM_BINARY}" ]
156
+ file "pkg/#{GEM_BINARY}" => [ 'pkg' ] + $BINSPEC.files do
157
+ when_writing 'Creating binary GEM' do
158
+ Gem::Builder.new( $BINSPEC ).build
159
+ verbose true do
160
+ FileUtils.mv GEM_BINARY, "pkg/#{GEM_BINARY}"
161
+ end
162
+ end
163
+ end
164
+ rescue LoadError
165
+ STDERR.puts 'Please install \'rubygems\' if you want to create Gem packages'
166
+ end
167
+
168
+ rule '.o' => '.cc' do |t|
169
+ sh "#{CXX} #{$CXXFLAGS} -c -o #{t.name} #{t.source}"
170
+ end
171
+
172
+ file ".depends.mf" do |t|
173
+ sh "g++ -MM #{$CXXFLAGS} #{CC_FILES.join ' '} | " +
174
+ "sed -e :a -e N -e 's/\\n/\\$/g' -e ta | " +
175
+ "sed -e 's/ *\\\\\\$ */ /g' -e 's/\\$/\\n/g' | sed -e 's/^/ext\\//' > #{t.name}"
176
+ end
177
+ CC_FILES.each do |t|
178
+ file t.ext(".o") => t
179
+ end
180
+ import ".depends.mf"
181
+
182
+ CLEAN.include 'ext/*.o'
183
+ CLOBBER.include SO_FILE, 'doc', '.yardoc', '.depends.mf'
184
+
data/ext/alsaoutput.cc ADDED
@@ -0,0 +1,285 @@
1
+ /* HornetsEye - Computer Vision with Ruby
2
+ Copyright (C) 2006, 2007, 2008, 2009, 2010 Jan Wedekind
3
+
4
+ This program is free software: you can redistribute it and/or modify
5
+ it under the terms of the GNU General Public License as published by
6
+ the Free Software Foundation, either version 3 of the License, or
7
+ (at your option) any later version.
8
+
9
+ This program is distributed in the hope that it will be useful,
10
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12
+ GNU General Public License for more details.
13
+
14
+ You should have received a copy of the GNU General Public License
15
+ along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
+ #include "alsaoutput.hh"
17
+
18
+ using namespace std;
19
+
20
+ VALUE AlsaOutput::cRubyClass = Qnil;
21
+
22
+ AlsaOutput::AlsaOutput( const string &pcmName, unsigned int rate,
23
+ unsigned int channels, int periods,
24
+ int frames ) throw (Error):
25
+ m_pcmHandle(NULL), m_pcmName( pcmName ), m_rate( rate ), m_channels( channels ),
26
+ m_frames( frames )
27
+ {
28
+ try {
29
+ snd_pcm_hw_params_t *hwParams;
30
+ snd_pcm_hw_params_alloca( &hwParams );
31
+ int err = snd_pcm_open( &m_pcmHandle, m_pcmName.c_str(), SND_PCM_STREAM_PLAYBACK,
32
+ SND_PCM_NONBLOCK );
33
+ ERRORMACRO( err >= 0, Error, , "Error opening PCM device \"" << m_pcmName
34
+ << "\": " << snd_strerror( err ) );
35
+ err = snd_pcm_hw_params_any( m_pcmHandle, hwParams );
36
+ ERRORMACRO( err >= 0, Error, , "Unable to configure the PCM device \""
37
+ << m_pcmName << "\": " << snd_strerror( err ) );
38
+ err = snd_pcm_hw_params_set_access( m_pcmHandle, hwParams,
39
+ SND_PCM_ACCESS_RW_INTERLEAVED );
40
+ ERRORMACRO( err >= 0, Error, , "Error setting PCM device \""
41
+ << m_pcmName << "\" to interlaced access: " << snd_strerror( err ) );
42
+ err = snd_pcm_hw_params_set_format( m_pcmHandle, hwParams,
43
+ SND_PCM_FORMAT_S16_LE );
44
+ ERRORMACRO( err >= 0, Error, , "Error setting PCM device \"" << m_pcmName
45
+ << "\" to 16-bit signed integer format: " << snd_strerror( err ) );
46
+ err = snd_pcm_hw_params_set_rate_near( m_pcmHandle, hwParams, &m_rate, 0 );
47
+ ERRORMACRO( err >= 0, Error, , "Error setting sampling rate of PCM device \""
48
+ << m_pcmName << "\" to " << rate << " Hz: " << snd_strerror( err ) );
49
+ err = snd_pcm_hw_params_set_channels( m_pcmHandle, hwParams, channels );
50
+ ERRORMACRO( err >= 0, Error, , "Error setting number of channels of PCM device \""
51
+ << m_pcmName << "\" to " << channels << ": " << snd_strerror( err ) );
52
+ err = snd_pcm_hw_params_set_periods( m_pcmHandle, hwParams, periods, 0 );
53
+ ERRORMACRO( err >= 0, Error, , "Error setting number of periods of PCM device \""
54
+ << m_pcmName << "\" to " << periods << ": " << snd_strerror( err ) );
55
+ err = snd_pcm_hw_params_set_buffer_size_near( m_pcmHandle, hwParams, &m_frames );
56
+ ERRORMACRO( err >= 0, Error, , "Error setting buffer size of PCM device \""
57
+ << m_pcmName << "\" to " << m_frames << " frames: "
58
+ << snd_strerror( err ) );
59
+ err = snd_pcm_hw_params( m_pcmHandle, hwParams );
60
+ ERRORMACRO( err >= 0, Error, , "Error setting parameters of PCM device \""
61
+ << m_pcmName << "\": " << snd_strerror( err ) );
62
+ } catch ( Error &e ) {
63
+ close();
64
+ throw e;
65
+ };
66
+ }
67
+
68
+ AlsaOutput::~AlsaOutput(void)
69
+ {
70
+ close();
71
+ }
72
+
73
+ void AlsaOutput::close(void)
74
+ {
75
+ if ( m_pcmHandle != NULL ) {
76
+ drop();
77
+ snd_pcm_close( m_pcmHandle );
78
+ m_pcmHandle = NULL;
79
+ };
80
+ }
81
+
82
+ void AlsaOutput::write( SequencePtr frame ) throw (Error)
83
+ {
84
+ ERRORMACRO( m_pcmHandle != NULL, Error, , "PCM device \"" << m_pcmName
85
+ << "\" is not open. Did you call \"close\" before?" );
86
+ int err = snd_pcm_writei( m_pcmHandle, (short int *)frame->data(),
87
+ frame->size() / ( 2 * m_channels ) );
88
+ ERRORMACRO( err >= 0, Error, , "Error writing audio frames to PCM device \""
89
+ << m_pcmName << "\": " << snd_strerror( err ) );
90
+ }
91
+
92
+ void AlsaOutput::drop(void) throw (Error)
93
+ {
94
+ ERRORMACRO( m_pcmHandle != NULL, Error, , "PCM device \"" << m_pcmName
95
+ << "\" is not open. Did you call \"close\" before?" );
96
+ snd_pcm_drop( m_pcmHandle );
97
+ }
98
+
99
+ void AlsaOutput::drain(void) throw (Error)
100
+ {
101
+ ERRORMACRO( m_pcmHandle != NULL, Error, , "PCM device \"" << m_pcmName
102
+ << "\" is not open. Did you call \"close\" before?" );
103
+ snd_pcm_drain( m_pcmHandle );
104
+ }
105
+
106
+ unsigned int AlsaOutput::rate(void)
107
+ {
108
+ return m_rate;
109
+ }
110
+
111
+ unsigned int AlsaOutput::channels(void)
112
+ {
113
+ return m_channels;
114
+ }
115
+
116
+ unsigned int AlsaOutput::frames(void)
117
+ {
118
+ return m_frames;
119
+ }
120
+
121
+ int AlsaOutput::avail(void) throw (Error)
122
+ {
123
+ ERRORMACRO( m_pcmHandle != NULL, Error, , "PCM device \"" << m_pcmName
124
+ << "\" is not open. Did you call \"close\" before?" );
125
+ snd_pcm_sframes_t frames = snd_pcm_avail( m_pcmHandle );
126
+ ERRORMACRO( frames >= 0, Error, , "Error querying number of available frames for "
127
+ "update of PCM device \"" << m_pcmName << "\": "
128
+ << snd_strerror( frames ) );
129
+ return frames;
130
+ }
131
+
132
+ int AlsaOutput::delay(void) throw (Error)
133
+ {
134
+ ERRORMACRO( m_pcmHandle != NULL, Error, , "PCM device \"" << m_pcmName
135
+ << "\" is not open. Did you call \"close\" before?" );
136
+ snd_pcm_sframes_t frames;
137
+ int err = snd_pcm_delay( m_pcmHandle, &frames );
138
+ ERRORMACRO( err >= 0, Error, , "Error querying number of available frames for "
139
+ "update of PCM device \"" << m_pcmName << "\": "
140
+ << snd_strerror( err ) );
141
+ return frames;
142
+ }
143
+
144
+ void AlsaOutput::prepare(void) throw (Error)
145
+ {
146
+ ERRORMACRO( m_pcmHandle != NULL, Error, , "PCM device \"" << m_pcmName
147
+ << "\" is not open. Did you call \"close\" before?" );
148
+ int err = snd_pcm_prepare( m_pcmHandle );
149
+ ERRORMACRO( err >= 0, Error, , "Error preparing PCM device \"" << m_pcmName
150
+ << "\": " << snd_strerror( err ) );
151
+ }
152
+
153
+ VALUE AlsaOutput::registerRubyClass( VALUE rbModule )
154
+ {
155
+ cRubyClass = rb_define_class_under( rbModule, "AlsaOutput", rb_cObject );
156
+ rb_define_singleton_method( cRubyClass, "new",
157
+ RUBY_METHOD_FUNC( wrapNew ), 5 );
158
+ rb_define_method( cRubyClass, "close", RUBY_METHOD_FUNC( wrapClose ), 0 );
159
+ rb_define_method( cRubyClass, "write", RUBY_METHOD_FUNC( wrapWrite ), 1 );
160
+ rb_define_method( cRubyClass, "drop", RUBY_METHOD_FUNC( wrapDrop ), 0 );
161
+ rb_define_method( cRubyClass, "drain", RUBY_METHOD_FUNC( wrapDrain ), 0 );
162
+ rb_define_method( cRubyClass, "rate", RUBY_METHOD_FUNC( wrapRate ), 0 );
163
+ rb_define_method( cRubyClass, "channels", RUBY_METHOD_FUNC( wrapChannels ), 0 );
164
+ rb_define_method( cRubyClass, "frames", RUBY_METHOD_FUNC( wrapFrames ), 0 );
165
+ rb_define_method( cRubyClass, "avail", RUBY_METHOD_FUNC( wrapAvail ), 0 );
166
+ rb_define_method( cRubyClass, "delay", RUBY_METHOD_FUNC( wrapDelay ), 0 );
167
+ rb_define_method( cRubyClass, "prepare", RUBY_METHOD_FUNC( wrapPrepare ), 0 );
168
+ }
169
+
170
+ void AlsaOutput::deleteRubyObject( void *ptr )
171
+ {
172
+ delete (AlsaOutputPtr *)ptr;
173
+ }
174
+
175
+ VALUE AlsaOutput::wrapNew( VALUE rbClass, VALUE rbPCMName, VALUE rbRate,
176
+ VALUE rbChannels, VALUE rbPeriods, VALUE rbFrames )
177
+ {
178
+ VALUE retVal = Qnil;
179
+ try {
180
+ rb_check_type( rbPCMName, T_STRING );
181
+ AlsaOutputPtr ptr( new AlsaOutput( StringValuePtr( rbPCMName ),
182
+ NUM2UINT( rbRate ), NUM2UINT( rbChannels ),
183
+ NUM2INT( rbPeriods ), NUM2INT( rbFrames ) ) );
184
+ retVal = Data_Wrap_Struct( rbClass, 0, deleteRubyObject,
185
+ new AlsaOutputPtr( ptr ) );
186
+ } catch ( exception &e ) {
187
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
188
+ };
189
+ return retVal;
190
+ }
191
+
192
+ VALUE AlsaOutput::wrapClose( VALUE rbSelf )
193
+ {
194
+ AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self );
195
+ (*self)->close();
196
+ return rbSelf;
197
+ }
198
+
199
+ VALUE AlsaOutput::wrapWrite( VALUE rbSelf, VALUE rbSequence )
200
+ {
201
+ try {
202
+ AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self );
203
+ SequencePtr sequence( new Sequence( rbSequence ) );
204
+ (*self)->write( sequence );
205
+ } catch ( exception &e ) {
206
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
207
+ };
208
+ return rbSequence;
209
+ }
210
+
211
+ VALUE AlsaOutput::wrapDrop( VALUE rbSelf )
212
+ {
213
+ try {
214
+ AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self );
215
+ (*self)->drop();
216
+ } catch ( exception &e ) {
217
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
218
+ };
219
+ return rbSelf;
220
+ }
221
+
222
+ VALUE AlsaOutput::wrapDrain( VALUE rbSelf )
223
+ {
224
+ try {
225
+ AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self );
226
+ (*self)->drain();
227
+ } catch ( exception &e ) {
228
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
229
+ };
230
+ return rbSelf;
231
+ }
232
+
233
+ VALUE AlsaOutput::wrapRate( VALUE rbSelf )
234
+ {
235
+ AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self );
236
+ return UINT2NUM( (*self)->rate() );
237
+ }
238
+
239
+ VALUE AlsaOutput::wrapChannels( VALUE rbSelf )
240
+ {
241
+ AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self );
242
+ return UINT2NUM( (*self)->channels() );
243
+ }
244
+
245
+ VALUE AlsaOutput::wrapFrames( VALUE rbSelf )
246
+ {
247
+ AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self );
248
+ return UINT2NUM( (*self)->frames() );
249
+ }
250
+
251
+ VALUE AlsaOutput::wrapAvail( VALUE rbSelf )
252
+ {
253
+ VALUE rbRetVal = Qnil;
254
+ try {
255
+ AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self );
256
+ rbRetVal = INT2NUM( (*self)->avail() );
257
+ } catch ( exception &e ) {
258
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
259
+ };
260
+ return rbRetVal;
261
+ }
262
+
263
+ VALUE AlsaOutput::wrapDelay( VALUE rbSelf )
264
+ {
265
+ VALUE rbRetVal = Qnil;
266
+ try {
267
+ AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self );
268
+ rbRetVal = INT2NUM( (*self)->delay() );
269
+ } catch ( exception &e ) {
270
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
271
+ };
272
+ return rbRetVal;
273
+ }
274
+
275
+ VALUE AlsaOutput::wrapPrepare( VALUE rbSelf )
276
+ {
277
+ try {
278
+ AlsaOutputPtr *self; Data_Get_Struct( rbSelf, AlsaOutputPtr, self );
279
+ (*self)->prepare();
280
+ } catch ( exception &e ) {
281
+ rb_raise( rb_eRuntimeError, "%s", e.what() );
282
+ };
283
+ return rbSelf;
284
+ }
285
+