sndfile 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,43 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
5
+ spec/outputs/*
6
+
7
+ # rcov generated
8
+ coverage
9
+
10
+ # rdoc generated
11
+ rdoc
12
+
13
+ # yard generated
14
+ doc
15
+ .yardoc
16
+
17
+ # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
18
+ #
19
+ # * Create a file at ~/.gitignore
20
+ # * Include files you want ignored
21
+ # * Run: git config --global core.excludesfile ~/.gitignore
22
+ #
23
+ # After doing this, these files will be ignored in all your git projects,
24
+ # saving you from having to 'pollute' every project you touch with them
25
+ #
26
+ # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
27
+ #
28
+ # For MacOS:
29
+ #
30
+ #.DS_Store
31
+ #
32
+ # For TextMate
33
+ #*.tmproj
34
+ #tmtags
35
+ #
36
+ # For emacs:
37
+ #*~
38
+ #\#*
39
+ #.\#*
40
+ #
41
+ # For vim:
42
+ #*.swp
43
+
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'http://rubygems.org'
2
+
3
+ gemspec
data/README.md ADDED
@@ -0,0 +1,45 @@
1
+ Libsndfile for Ruby
2
+ -------------
3
+
4
+ *sndfile* provides a fast and easy way to read, process, and write audio
5
+ file data. It wraps the [libsndfile](http://www.mega-nerd.com/libsndfile/)
6
+ C library via [FFI](http://github.com/ffi/ffi), reading & writing the
7
+ sample data as a [GSLng](https://github.com/v01d/ruby-gsl-ng) matrix.
8
+
9
+ The author has (so far) fleshed out only the parts needed for his own projects. Please do fork this project and contribute to it.
10
+
11
+ Libsndfile?
12
+ ===========
13
+ "Libsndfile is a C library for reading and writing files containing sampled sound (such as MS Windows WAV and the Apple/SGI AIFF format) through one standard library interface. It is released in source code format under the Gnu Lesser General Public License."
14
+
15
+
16
+ Installation
17
+ ============
18
+
19
+ To use this Ruby library, you must have [libsndfile](http://www.mega-nerd.com/libsndfile/) and [GSL](http://www.gnu.org/software/gsl/) on your machine. You can install
20
+ them via apt-get (linux) or homebrew (OS X) or download them directly from their websites.
21
+
22
+ That being said:
23
+
24
+ gem install sndfile
25
+
26
+ or, in a Gemfile
27
+
28
+ gem "sndfile-ruby", :require => "sndfile"
29
+
30
+ Usage
31
+ =====
32
+ Here's a simple example, that reads an arbitrary source file and produces a WAV file at half the volume:
33
+
34
+ fin = Sndfile::File.new(inpath)
35
+ Sndfile::File.open(outpath, :mode => :WRITE, :format => :WAV, :encoding => :PCM_16, :channels => fin.channels, :samplerate => fin.samplerate) do |fout|
36
+ while data = fin.read(10000)
37
+ fout.write(data * 0.5)
38
+ end
39
+ end
40
+
41
+ See the [RDOC](http://rubydoc.info/gems/sndfile) for more info.
42
+
43
+
44
+ History
45
+ =======
data/Rakefile ADDED
@@ -0,0 +1,17 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rdoc/task'
4
+ Rake::RDocTask.new do |rdoc|
5
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
6
+
7
+ rdoc.main = 'README.rdoc'
8
+ rdoc.rdoc_dir = 'rdoc'
9
+ rdoc.title = "sndfile #{version}"
10
+ rdoc.rdoc_files.include('README*')
11
+ rdoc.rdoc_files.include('lib/**/*.rb')
12
+ end
13
+
14
+ require 'rspec/core/rake_task'
15
+ RSpec::Core::RakeTask.new(:spec) do |spec|
16
+ spec.rspec_opts = '-Ispec'
17
+ end
@@ -0,0 +1,38 @@
1
+ # GSLng::Matrix is extended with two aliases:
2
+ #
3
+ # GSLng::Matrix#frames, which is an alias for GSLng::Matrix#m and GSLng::Matrix#height
4
+ #
5
+ # GSLng::Matrix#channels, which is an alias for GSLng::Matrix#n and GSLng::Matrix#width
6
+ #
7
+ class GSLng::Matrix
8
+
9
+
10
+ # @method frames
11
+ # frames is an alias for GSLng::Matrix#m and GSLng::Matrix#height
12
+ #
13
+ alias :frames :m
14
+
15
+
16
+ # @method channels
17
+ # channels is an alias for GSLng::Matrix#n and GSLng::Matrix#width
18
+ #
19
+ alias :channels :n
20
+
21
+ unless instance_methods.include? :data_ptr
22
+
23
+ class GSL_matrix < FFI::Struct # :nodoc:
24
+ layout :size1, :size_t,
25
+ :size2, :size_t,
26
+ :tda, :size_t,
27
+ :data, :pointer,
28
+ :block, :pointer,
29
+ :owner, :int
30
+ end
31
+
32
+ def data_ptr # :nodoc:
33
+ GSL_matrix.new(ptr)[:data]
34
+ end
35
+
36
+ end
37
+
38
+ end
data/lib/sndfile.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'ffi'
2
+ require 'gslng'
3
+ require 'hash_keyword_args'
4
+
5
+ require 'gsl-ng/matrix'
6
+ require 'sndfile/enums'
7
+ require 'sndfile/error'
8
+ require 'sndfile/sndfile_api'
9
+ require 'sndfile/file'
@@ -0,0 +1,181 @@
1
+ module Sndfile # :nodoc: all
2
+ module Enums
3
+ extend FFI::Library
4
+
5
+ # Error Codes
6
+ ErrorCode = enum(
7
+ :SF_ERR_NO_ERROR, 0,
8
+ :SF_ERR_UNRECOGNISED_FORMAT, 1,
9
+ :SF_ERR_SYSTEM, 2,
10
+ :SF_ERR_MALFORMED_FILE, 3,
11
+ :SF_ERR_UNSUPPORTED_ENCODING, 4,
12
+ )
13
+
14
+
15
+ # Modes for opening files.
16
+ FileMode = enum(
17
+ :READ, 0x10,
18
+ :WRITE, 0x20,
19
+ :RDWR, 0x30,
20
+ )
21
+
22
+ # Major formats.
23
+ FORMAT_MASK = 0x0FFF0000
24
+ Format = enum(
25
+ :WAV, 0x010000, # Microsoft WAV format (little endian).
26
+ :AIFF, 0x020000, # Apple/SGI AIFF format (big endian).
27
+ :AU, 0x030000, # Sun/NeXT AU format (big endian).
28
+ :RAW, 0x040000, # RAW PCM data.
29
+ :PAF, 0x050000, # Ensoniq PARIS file format.
30
+ :SVX, 0x060000, # Amiga IFF / SVX8 / SV16 format.
31
+ :NIST, 0x070000, # Sphere NIST format.
32
+ :VOC, 0x080000, # VOC files.
33
+ :IRCAM, 0x0A0000, # Berkeley/IRCAM/CARL
34
+ :W64, 0x0B0000, # Sonic Foundry's 64 bit RIFF/WAV
35
+ :MAT4, 0x0C0000, # Matlab (tm) V4.2 / GNU Octave 2.0
36
+ :MAT5, 0x0D0000, # Matlab (tm) V5.0 / GNU Octave 2.1
37
+ :PVF, 0x0E0000, # Portable Voice Format
38
+ :XI, 0x0F0000, # Fasttracker 2 Extended Instrument
39
+ :HTK, 0x100000, # HMM Tool Kit format
40
+ :SDS, 0x110000, # Midi Sample Dump Standard
41
+ :AVR, 0x120000, # Audio Visual Research
42
+ :WAVEX, 0x130000, # MS WAVE with WAVEFORMATEX
43
+ :SD2, 0x160000, # Sound Designer 2
44
+ :FLAC, 0x170000, # FLAC lossless file format
45
+ :CAF, 0x180000, # Core Audio File format
46
+ :WVE, 0x190000, # Psion WVE format
47
+ :OGG, 0x200000, # Xiph OGG container
48
+ :MPC2K, 0x210000, # Akai MPC 2000 sampler
49
+ :RF64, 0x220000, # RF64 WAV file
50
+ )
51
+
52
+ # Subtypes from here on.
53
+ ENCODING_MASK = 0x0000FFFF
54
+ Encoding = enum(
55
+ :PCM_S8, 0x0001, # Signed 8 bit data
56
+ :PCM_16, 0x0002, # Signed 16 bit data
57
+ :PCM_24, 0x0003, # Signed 24 bit data
58
+ :PCM_32, 0x0004, # Signed 32 bit data
59
+
60
+ :PCM_U8, 0x0005, # Unsigned 8 bit data (WAV and RAW only)
61
+
62
+ :FLOAT, 0x0006, # 32 bit float data
63
+ :DOUBLE, 0x0007, # 64 bit float data
64
+
65
+ :ULAW, 0x0010, # U-Law encoded.
66
+ :ALAW, 0x0011, # A-Law encoded.
67
+ :IMA_ADPCM, 0x0012, # IMA ADPCM.
68
+ :MS_ADPCM, 0x0013, # Microsoft ADPCM.
69
+
70
+ :GSM610, 0x0020, # GSM 6.10 encoding.
71
+ :VOX_ADPCM, 0x0021, # Oki Dialogic ADPCM encoding.
72
+
73
+ :G721_32, 0x0030, # 32kbs G721 ADPCM encoding.
74
+ :G723_24, 0x0031, # 24kbs G723 ADPCM encoding.
75
+ :G723_40, 0x0032, # 40kbs G723 ADPCM encoding.
76
+
77
+ :DWVW_12, 0x0040, # 12 bit Delta Width Variable Word encoding.
78
+ :DWVW_16, 0x0041, # 16 bit Delta Width Variable Word encoding.
79
+ :DWVW_24, 0x0042, # 24 bit Delta Width Variable Word encoding.
80
+ :DWVW_N, 0x0043, # N bit Delta Width Variable Word encoding.
81
+
82
+ :DPCM_8, 0x0050, # 8 bit differential PCM (XI only)
83
+ :DPCM_16, 0x0051, # 16 bit differential PCM (XI only)
84
+
85
+ :VORBIS, 0x0060, # Xiph Vorbis encoding.
86
+ )
87
+
88
+ # Endian-ness options.
89
+ ENDIAN_MASK = 0x30000000
90
+ Endian = enum(
91
+ :FILE, 0x00000000, # Default file endian-ness.
92
+ :LITTLE, 0x10000000, # Force little endian-ness.
93
+ :BIG, 0x20000000, # Force big endian-ness.
94
+ :CPU, 0x30000000, # Force CPU endian-ness.
95
+ )
96
+
97
+ # Command numbers for sf_command
98
+ Command = enum(
99
+ :SFC_GET_LIB_VERSION, 0x1000,
100
+ :SFC_GET_LOG_INFO, 0x1001,
101
+ :SFC_GET_CURRENT_SF_INFO, 0x1002,
102
+
103
+
104
+ :SFC_GET_NORM_DOUBLE, 0x1010,
105
+ :SFC_GET_NORM_FLOAT, 0x1011,
106
+ :SFC_SET_NORM_DOUBLE, 0x1012,
107
+ :SFC_SET_NORM_FLOAT, 0x1013,
108
+ :SFC_SET_SCALE_FLOAT_INT_READ, 0x1014,
109
+ :SFC_SET_SCALE_INT_FLOAT_WRITE, 0x1015,
110
+
111
+ :SFC_GET_SIMPLE_FORMAT_COUNT, 0x1020,
112
+ :SFC_GET_SIMPLE_FORMAT, 0x1021,
113
+
114
+ :SFC_GET_FORMAT_INFO, 0x1028,
115
+
116
+ :SFC_GET_FORMAT_MAJOR_COUNT, 0x1030,
117
+ :SFC_GET_FORMAT_MAJOR, 0x1031,
118
+ :SFC_GET_FORMAT_SUBTYPE_COUNT, 0x1032,
119
+ :SFC_GET_FORMAT_SUBTYPE, 0x1033,
120
+
121
+ :SFC_CALC_SIGNAL_MAX, 0x1040,
122
+ :SFC_CALC_NORM_SIGNAL_MAX, 0x1041,
123
+ :SFC_CALC_MAX_ALL_CHANNELS, 0x1042,
124
+ :SFC_CALC_NORM_MAX_ALL_CHANNELS, 0x1043,
125
+ :SFC_GET_SIGNAL_MAX, 0x1044,
126
+ :SFC_GET_MAX_ALL_CHANNELS, 0x1045,
127
+
128
+ :SFC_SET_ADD_PEAK_CHUNK, 0x1050,
129
+ :SFC_SET_ADD_HEADER_PAD_CHUNK, 0x1051,
130
+
131
+ :SFC_UPDATE_HEADER_NOW, 0x1060,
132
+ :SFC_SET_UPDATE_HEADER_AUTO, 0x1061,
133
+
134
+ :SFC_FILE_TRUNCATE, 0x1080,
135
+
136
+ :SFC_SET_RAW_START_OFFSET, 0x1090,
137
+
138
+ :SFC_SET_DITHER_ON_WRITE, 0x10A0,
139
+ :SFC_SET_DITHER_ON_READ, 0x10A1,
140
+
141
+ :SFC_GET_DITHER_INFO_COUNT, 0x10A2,
142
+ :SFC_GET_DITHER_INFO, 0x10A3,
143
+
144
+ :SFC_GET_EMBED_FILE_INFO, 0x10B0,
145
+
146
+ :SFC_SET_CLIPPING, 0x10C0,
147
+ :SFC_GET_CLIPPING, 0x10C1,
148
+
149
+ :SFC_GET_INSTRUMENT, 0x10D0,
150
+ :SFC_SET_INSTRUMENT, 0x10D1,
151
+
152
+ :SFC_GET_LOOP_INFO, 0x10E0,
153
+
154
+ :SFC_GET_BROADCAST_INFO, 0x10F0,
155
+ :SFC_SET_BROADCAST_INFO, 0x10F1,
156
+
157
+ :SFC_GET_CHANNEL_MAP_INFO, 0x1100,
158
+ :SFC_SET_CHANNEL_MAP_INFO, 0x1101,
159
+
160
+ :SFC_RAW_DATA_NEEDS_ENDSWAP, 0x1110,
161
+
162
+ # Support for Wavex Ambisonics Format
163
+ :SFC_WAVEX_SET_AMBISONIC, 0x1200,
164
+ :SFC_WAVEX_GET_AMBISONIC, 0x1201,
165
+
166
+ :SFC_SET_VBR_ENCODING_QUALITY, 0x1300,
167
+
168
+ # Following commands for testing only.
169
+ :SFC_TEST_IEEE_FLOAT_REPLACE, 0x6001,
170
+
171
+ ##
172
+ ## :SFC_SET_ADD_* values are deprecated and will disappear at some
173
+ ## time in the future. They are guaranteed to be here up to and
174
+ ## including version 1.0.8 to avoid breakage of existng software.
175
+ ## They currently do nothing and will continue to do nothing.
176
+ ##
177
+ :SFC_SET_ADD_DITHER_ON_WRITE, 0x1070,
178
+ :SFC_SET_ADD_DITHER_ON_READ, 0x1071
179
+ )
180
+ end
181
+ end
@@ -0,0 +1,8 @@
1
+ module Sndfile
2
+ #
3
+ # Exception raised in case of errors from libsndfile. The message
4
+ # contains the libsndfile error message.
5
+ #
6
+ class Error < RuntimeError
7
+ end
8
+ end
@@ -0,0 +1,202 @@
1
+ module Sndfile
2
+
3
+ class File
4
+ include SndfileApi
5
+
6
+ #
7
+ # Without a block, this is the same as File.new(path, opts).
8
+ # With a block, yields the File object to the block and ensures that
9
+ # File#close is then called .
10
+ #
11
+ def self.open(path, opts={})
12
+ file = new(path, opts)
13
+ if block_given?
14
+ begin
15
+ yield file
16
+ ensure
17
+ file.close
18
+ end
19
+ else
20
+ file
21
+ end
22
+ end
23
+
24
+ #
25
+ # Create a File instance. Options are:
26
+ #
27
+ # :mode => :READ (default), :WRITE, or :RDWR
28
+ # :format => see list at File#format (default is :WAV)
29
+ # :encoding => see list at File#encoding (default is :PCM_16)
30
+ # :endian => see list at File#endian (default is :FILE)
31
+ # :samplerate => default is 44100
32
+ # :channles => default is 2
33
+ #
34
+ # When mode is :READ, then :format, :channels, and :samplerate are
35
+ # ignored -- unless the format is :RAW
36
+ #
37
+ # May raise Sndfile::Error on various error conditions
38
+ #
39
+ def initialize(path,opts={})
40
+ opts = opts.keyword_args(:mode => { :valid => Enums::FileMode.symbols, :default => :READ },
41
+ :format => { :valid => Enums::Format.symbols, :default => :WAV },
42
+ :encoding => { :valid => Enums::Encoding.symbols, :default => :PCM_16 },
43
+ :endian => { :valid => Enums::Endian.symbols, :default => :FILE },
44
+ :samplerate => 44100,
45
+ :channels => 2,
46
+ )
47
+
48
+ @path = path
49
+ @sfinfo = SfInfo.new
50
+ if opts.mode == :WRITE or opts.format == :RAW
51
+ @sfinfo[:format] = Enums::Format[opts.format]|Enums::Encoding[opts.encoding]|Enums::Endian[opts.endian]
52
+ @sfinfo[:channels] = opts.channels
53
+ @sfinfo[:samplerate] = opts.samplerate
54
+ end
55
+ @sfpointer = sf_open(path.to_s, opts.mode, @sfinfo)
56
+ check_error
57
+ sf_command @sfpointer, :SFC_SET_CLIPPING, nil, 1
58
+ check_error
59
+ end
60
+
61
+ # Closes the File instance.
62
+ #
63
+ # May raise Sndfile::Error for various error conditions
64
+ def close
65
+ check_error sf_close(@sfpointer)
66
+ end
67
+
68
+ # Returns the format of the file, which is one of
69
+ #
70
+ # :WAV - Microsoft WAV format (little endian).
71
+ # :AIFF - Apple/SGI AIFF format (big endian).
72
+ # :AU - Sun/NeXT AU format (big endian).
73
+ # :RAW - RAW PCM data.
74
+ # :PAF - Ensoniq PARIS file format.
75
+ # :SVX - Amiga IFF / SVX8 / SV16 format.
76
+ # :NIST - Sphere NIST format.
77
+ # :VOC - VOC files.
78
+ # :IRCAM - Berkeley/IRCAM/CARL
79
+ # :W64 - Sonic Foundry's 64 bit RIFF/WAV
80
+ # :MAT4 - Matlab (tm) V4.2 / GNU Octave 2.0
81
+ # :MAT5 - Matlab (tm) V5.0 / GNU Octave 2.1
82
+ # :PVF - Portable Voice Format
83
+ # :XI - Fasttracker 2 Extended Instrument
84
+ # :HTK - HMM Tool Kit format
85
+ # :SDS - Midi Sample Dump Standard
86
+ # :AVR - Audio Visual Research
87
+ # :WAVEX - MS WAVE with WAVEFORMATEX
88
+ # :SD2 - Sound Designer 2
89
+ # :FLAC - FLAC lossless file format
90
+ # :CAF - Core Audio File format
91
+ # :WVE - Psion WVE format
92
+ # :OGG - Xiph OGG container
93
+ # :MPC2K - Akai MPC 2000 sampler
94
+ # :RF64 - RF64 WAV file
95
+ def format
96
+ Format[@sfinfo[:format] & FORMAT_MASK]
97
+ end
98
+
99
+ # Returns the encoding of the file data, which is one of:
100
+ #
101
+ # :PCM_S8 - Signed 8 bit data
102
+ # :PCM_16 - Signed 16 bit data
103
+ # :PCM_24 - Signed 24 bit data
104
+ # :PCM_32 - Signed 32 bit data
105
+ # :PCM_U8 - Unsigned 8 bit data (WAV and RAW only)
106
+ # :FLOAT - 32 bit float data
107
+ # :DOUBLE - 64 bit float data
108
+ # :ULAW - U-Law encoded.
109
+ # :ALAW - A-Law encoded.
110
+ # :IMA_ADPCM - IMA ADPCM.
111
+ # :MS_ADPCM - Microsoft ADPCM.
112
+ # :GSM610 - GSM 6.10 encoding.
113
+ # :VOX_ADPCM - Oki Dialogic ADPCM encoding.
114
+ # :G721_32 - 32kbs G721 ADPCM encoding.
115
+ # :G723_24 - 24kbs G723 ADPCM encoding.
116
+ # :G723_40 - 40kbs G723 ADPCM encoding.
117
+ # :DWVW_12 - 12 bit Delta Width Variable Word encoding.
118
+ # :DWVW_16 - 16 bit Delta Width Variable Word encoding.
119
+ # :DWVW_24 - 24 bit Delta Width Variable Word encoding.
120
+ # :DWVW_N - N bit Delta Width Variable Word encoding.
121
+ # :DPCM_8 - 8 bit differential PCM (XI only)
122
+ # :DPCM_16 - 16 bit differential PCM (XI only)
123
+ # :VORBIS - Xiph Vorbis encoding.
124
+ def encoding
125
+ Encoding[@sfinfo[:format] & ENCODING_MASK]
126
+ end
127
+
128
+ # Returns the endian-ness of the data, one of:
129
+ #
130
+ # :FILE - Default file endian-ness.
131
+ # :LITTLE - Force little endian-ness.
132
+ # :BIG - Force big endian-ness.
133
+ # :CPU - Force CPU endian-ness.
134
+ def endian
135
+ Endian[@sfinfo[:format] & ENDIAN_MASK]
136
+ end
137
+
138
+ # Returns the number of frames (samples) in the file
139
+ def frames
140
+ @sfinfo[:frames]
141
+ end
142
+
143
+ # Returns the sample rate, frames per second
144
+ def samplerate
145
+ @sfinfo[:samplerate]
146
+ end
147
+
148
+ # Returns the number of channels of data
149
+ def channels
150
+ @sfinfo[:channels]
151
+ end
152
+
153
+ # Reads frames from the file, returning the data in a GSLng::Matrix of
154
+ # dimensions frames x channels. (For convenience, the height and width
155
+ # methods of GSLng::Matrix are aliased as GSLng::Matrix#frames and
156
+ # GSLng::Matrix#channels)
157
+ #
158
+ # Normally the requested number of frames is read, unless end of file is
159
+ # reached. If no frames are read (i.e. already at end of file),
160
+ # returns nil instead of a GSLng::Matrix.
161
+ #
162
+ # May raise Sndfile::Error in case of error.
163
+ def read(nframes)
164
+
165
+ buf = GSLng::Matrix.new(nframes, channels)
166
+
167
+ count = sf_readf_double @sfpointer, buf.data_ptr, nframes
168
+ check_error
169
+ case count
170
+ when 0 then nil
171
+ when nframes then buf
172
+ else buf.view(0, 0, count, channels)
173
+ end
174
+ end
175
+
176
+ # Writes frames to the file. The data must be (quack like) a GSLng::Matrix
177
+ # with dimensions frames x channels. The matrix can contain any number of
178
+ # frames.
179
+ #
180
+ # When writing to a file with integer encoding, values are clipped to
181
+ # [-1, 1].
182
+ #
183
+ # May raise Sndfile::Error in case of error.
184
+ def write(buf)
185
+ sf_writef_double @sfpointer, buf.data_ptr, buf.frames
186
+ check_error
187
+ end
188
+
189
+ private
190
+
191
+ def check_error(code = nil)
192
+ code ||= sf_error(@sfpointer.null? ? nil : @sfpointer)
193
+ if code != 0
194
+ msg = sf_strerror(@sfpointer.null? ? nil : @sfpointer)
195
+ msg += " (#{ErrorCode[code]})" if ErrorCode[code]
196
+ msg += " [#{@path}]"
197
+ raise Sndfile::Error, msg
198
+ end
199
+ end
200
+
201
+ end
202
+ end
@@ -0,0 +1,98 @@
1
+ module Sndfile # :nodoc: all
2
+ module SndfileApi
3
+ extend FFI::Library
4
+ include Enums
5
+
6
+ ffi_lib 'libsndfile'
7
+
8
+ typedef :int64, :sf_count_t
9
+
10
+ class SfInfo < FFI::Struct
11
+ layout :frames, :sf_count_t,
12
+ :samplerate, :int,
13
+ :channels, :int,
14
+ :format, :int,
15
+ :sections, :int,
16
+ :seekable, :int
17
+ end
18
+
19
+ # SNDFILE* sf_open (const char *path, int mode, SF_INFO *sfinfo) ;
20
+ # SNDFILE* sf_open_fd (int fd, int mode, SF_INFO *sfinfo, int close_desc) ;
21
+ # SNDFILE* sf_open_virtual (SF_VIRTUAL_IO *sfvirtual, int mode, SF_INFO *sfinfo, void *user_data) ;
22
+ # int sf_format_check (const SF_INFO *info) ;
23
+ attach_function :sf_open, [:string, FileMode, :pointer], :pointer
24
+ attach_function :sf_open_fd, [:int, :int, :pointer, :int], :pointer
25
+ attach_function :sf_open_virtual, [:pointer, :int, :pointer, :pointer], :pointer
26
+ attach_function :sf_format_check, [:pointer], :int
27
+
28
+ # sf_count_t sf_seek (SNDFILE *sndfile, sf_count_t frames, int whence) ;
29
+ attach_function :sf_seek, [:pointer, :sf_count_t, :int], :sf_count_t
30
+
31
+ # int sf_command (SNDFILE *sndfile, int cmd, void *data, int datasize) ;
32
+ attach_function :sf_command, [:pointer, Command, :pointer, :int], :int
33
+
34
+ # int sf_error (SNDFILE *sndfile) ;
35
+ # const char* sf_strerror (SNDFILE *sndfile) ;
36
+ # const char* sf_error_number (int errnum) ;
37
+ attach_function :sf_error, [:pointer], :int
38
+ attach_function :sf_strerror, [:pointer], :string
39
+ attach_function :sf_error_number, [:int], :string
40
+
41
+ # int sf_perror (SNDFILE *sndfile) ;
42
+ # int sf_error_str (SNDFILE *sndfile, char* str, size_t len) ;
43
+ attach_function :sf_perror, [:pointer], :int
44
+ attach_function :sf_error_str, [:pointer, :pointer, :int], :int
45
+
46
+ # int sf_close (SNDFILE *sndfile) ;
47
+ # void sf_write_sync (SNDFILE *sndfile) ;
48
+ attach_function :sf_close, [:pointer], :int
49
+ attach_function :sf_write_sync, [:pointer], :void
50
+
51
+
52
+ # sf_count_t sf_read_short (SNDFILE *sndfile, short *ptr, sf_count_t items) ;
53
+ # sf_count_t sf_read_int (SNDFILE *sndfile, int *ptr, sf_count_t items) ;
54
+ # sf_count_t sf_read_float (SNDFILE *sndfile, float *ptr, sf_count_t items) ;
55
+ # sf_count_t sf_read_double (SNDFILE *sndfile, double *ptr, sf_count_t items) ;
56
+ attach_function :sf_read_short, [:pointer, :pointer, :sf_count_t], :sf_count_t
57
+ attach_function :sf_read_int, [:pointer, :pointer, :sf_count_t], :sf_count_t
58
+ attach_function :sf_read_float, [:pointer, :pointer, :sf_count_t], :sf_count_t
59
+ attach_function :sf_read_double, [:pointer, :pointer, :sf_count_t], :sf_count_t
60
+
61
+ # sf_count_t sf_readf_short (SNDFILE *sndfile, short *ptr, sf_count_t frames) ;
62
+ # sf_count_t sf_readf_int (SNDFILE *sndfile, int *ptr, sf_count_t frames) ;
63
+ # sf_count_t sf_readf_float (SNDFILE *sndfile, float *ptr, sf_count_t frames) ;
64
+ # sf_count_t sf_readf_double (SNDFILE *sndfile, double *ptr, sf_count_t frames) ;
65
+ attach_function :sf_readf_short, [:pointer, :pointer, :sf_count_t], :sf_count_t
66
+ attach_function :sf_readf_int, [:pointer, :pointer, :sf_count_t], :sf_count_t
67
+ attach_function :sf_readf_float, [:pointer, :pointer, :sf_count_t], :sf_count_t
68
+ attach_function :sf_readf_double, [:pointer, :pointer, :sf_count_t], :sf_count_t
69
+
70
+ # sf_count_t sf_write_short (SNDFILE *sndfile, short *ptr, sf_count_t items) ;
71
+ # sf_count_t sf_write_int (SNDFILE *sndfile, int *ptr, sf_count_t items) ;
72
+ # sf_count_t sf_write_float (SNDFILE *sndfile, float *ptr, sf_count_t items) ;
73
+ # sf_count_t sf_write_double (SNDFILE *sndfile, double *ptr, sf_count_t items) ;
74
+ attach_function :sf_write_short, [:pointer, :pointer, :sf_count_t], :sf_count_t
75
+ attach_function :sf_write_int, [:pointer, :pointer, :sf_count_t], :sf_count_t
76
+ attach_function :sf_write_float, [:pointer, :pointer, :sf_count_t], :sf_count_t
77
+ attach_function :sf_write_double, [:pointer, :pointer, :sf_count_t], :sf_count_t
78
+
79
+ # sf_count_t sf_writef_short (SNDFILE *sndfile, short *ptr, sf_count_t frames) ;
80
+ # sf_count_t sf_writef_int (SNDFILE *sndfile, int *ptr, sf_count_t frames) ;
81
+ # sf_count_t sf_writef_float (SNDFILE *sndfile, float *ptr, sf_count_t frames) ;
82
+ # sf_count_t sf_writef_double (SNDFILE *sndfile, double *ptr, sf_count_t frames) ;
83
+ attach_function :sf_writef_short, [:pointer, :pointer, :sf_count_t], :sf_count_t
84
+ attach_function :sf_writef_int, [:pointer, :pointer, :sf_count_t], :sf_count_t
85
+ attach_function :sf_writef_float, [:pointer, :pointer, :sf_count_t], :sf_count_t
86
+ attach_function :sf_writef_double, [:pointer, :pointer, :sf_count_t], :sf_count_t
87
+
88
+ # sf_count_t sf_read_raw (SNDFILE *sndfile, void *ptr, sf_count_t bytes) ;
89
+ # sf_count_t sf_write_raw (SNDFILE *sndfile, void *ptr, sf_count_t bytes) ;
90
+ attach_function :sf_read_raw, [:pointer, :pointer, :sf_count_t], :sf_count_t
91
+ attach_function :sf_write_raw, [:pointer, :pointer, :sf_count_t], :sf_count_t
92
+
93
+ # const char* sf_get_string (SNDFILE *sndfile, int str_type) ;
94
+ # int sf_set_string (SNDFILE *sndfile, int str_type, const char* str) ;
95
+ attach_function :sf_get_string, [:pointer, :int], :string
96
+ attach_function :sf_set_string, [:pointer, :int, :string], :int
97
+ end
98
+ end
@@ -0,0 +1,3 @@
1
+ module Sndfile
2
+ VERSION = "0.1.0"
3
+ end
data/sndfile.gemspec ADDED
@@ -0,0 +1,26 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/sndfile/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.authors = ["ronen barzel"]
6
+ gem.email = ["ronen@barzel.org"]
7
+ gem.description = %q{Ruby wrapper for libsndfile. Reads/writes data as GSL matrices, to allow fast processing.}
8
+ gem.summary = %q{Ruby wrapper for libsndfile. Reads/writes data as GSL matrices, to allow fast processing.}
9
+ gem.homepage = "https://github.com/ronen/sndfile"
10
+
11
+ gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
12
+ gem.files = `git ls-files`.split("\n")
13
+ gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
14
+ gem.name = "sndfile"
15
+ gem.require_paths = ["lib"]
16
+ gem.version = Sndfile::VERSION
17
+
18
+ gem.add_dependency("ffi")
19
+ gem.add_dependency("ruby-gsl-ng")
20
+ gem.add_dependency("hash_keyword_args")
21
+
22
+ gem.add_development_dependency 'rake'
23
+ gem.add_development_dependency 'rspec'
24
+ gem.add_development_dependency 'simplecov'
25
+ gem.add_development_dependency 'simplecov-gem-adapter'
26
+ end
data/spec/file_spec.rb ADDED
@@ -0,0 +1,20 @@
1
+ require 'spec_helper'
2
+
3
+ describe Sndfile do
4
+
5
+ it "should return correct file info" do
6
+ Sndfile::File.open(INPUTS_DIR + "ComputerMagic.wav") do |f|
7
+ f.format.should == :WAV
8
+ f.encoding.should == :PCM_16
9
+ f.endian.should == :FILE
10
+ f.frames.should == 223451
11
+ f.samplerate.should == 44100
12
+ f.channels.should == 2
13
+ end
14
+ end
15
+
16
+ it "should raise an error when it can't open the file" do
17
+ expect { Sndfile::File.open("/no/such/file") }.should raise_error Sndfile::Error
18
+ end
19
+
20
+ end
Binary file
Binary file
@@ -0,0 +1,73 @@
1
+ require 'spec_helper'
2
+ require 'tmpdir'
3
+ require 'narray'
4
+
5
+ describe "Integration" do
6
+
7
+ it "should copy a file" do
8
+ inpath = in_path("ComputerMagic.wav")
9
+ outpath, refpath = out_ref_paths("copy.wav")
10
+
11
+ fin = Sndfile::File.open(inpath)
12
+ Sndfile::File.open(outpath, :mode => :WRITE, :format => :WAV, :encoding => :PCM_16, :channels => 2, :samplerate => fin.samplerate) do |fout|
13
+ while data = fin.read(12345)
14
+ fout.write(data)
15
+ end
16
+ system("cmp #{outpath} #{refpath}").should be_true
17
+ end
18
+ end
19
+
20
+ it "should mix two files to and write the result" do
21
+ inpath1 = in_path("ComputerMagic.wav")
22
+ inpath2 = in_path("Flute3.wav")
23
+ outpath, refpath = out_ref_paths("mix.wav")
24
+
25
+ fin1 = Sndfile::File.open(inpath1)
26
+ fin1.channels.should == 2
27
+
28
+ fin2 = Sndfile::File.open(inpath2)
29
+ fin2.channels.should == 1
30
+ fin1.samplerate.should == fin2.samplerate
31
+
32
+ Sndfile::File.open(outpath, :mode => :WRITE, :format => :WAV, :encoding => :PCM_16, :channels => 2, :samplerate => fin1.samplerate) do |fout|
33
+ while true
34
+ a = fin1.read(10000)
35
+ b = fin2.read(10000)
36
+ b = b * GSLng::Matrix.from_array([[1,0]]) if b
37
+ case
38
+ when a.nil? && b.nil?
39
+ break
40
+ when a.nil?
41
+ r = b
42
+ when b.nil?
43
+ r = a
44
+ when a.frames > b.frames
45
+ r = a
46
+ r.view(0, 0, b.frames, 2).add! b
47
+ when b.frames > a.frames
48
+ r = b
49
+ r.view(0, 0, a.frames, 2).add! a
50
+ else
51
+ r = a
52
+ r.add! b
53
+ end
54
+ fout.write(r)
55
+ end
56
+ system("cmp #{outpath} #{refpath}").should be_true
57
+ end
58
+ end
59
+
60
+ it "should multiply data by a scalar" do
61
+ inpath = in_path("ComputerMagic.wav")
62
+ outpath, refpath = out_ref_paths("half.wav")
63
+
64
+ fin = Sndfile::File.open(inpath)
65
+ Sndfile::File.open(outpath, :mode => :WRITE, :format => :WAV, :encoding => :PCM_16, :channels => 2, :samplerate => fin.samplerate) do |fout|
66
+ while data = fin.read(10000)
67
+ fout.write(data * 0.5)
68
+ end
69
+ system("cmp #{outpath} #{refpath}").should be_true
70
+ end
71
+ end
72
+
73
+ end
Binary file
Binary file
Binary file
@@ -0,0 +1,33 @@
1
+ if RUBY_VERSION > "1.9"
2
+ require 'simplecov'
3
+ require 'simplecov-gem-adapter'
4
+ SimpleCov.start 'gem'
5
+ end
6
+
7
+ require 'rspec'
8
+ require 'sndfile'
9
+
10
+ # Requires supporting files with custom matchers and macros, etc,
11
+ # in ./support/ and its subdirectories.
12
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
13
+
14
+ RSpec.configure do |config|
15
+
16
+ end
17
+
18
+ INPUTS_DIR = Pathname.new(__FILE__) + "../inputs"
19
+ REFS_DIR = Pathname.new(__FILE__) + "../refs"
20
+ OUTPUTS_DIR = Pathname.new(__FILE__) + "../outputs"
21
+ Dir.mkdir(OUTPUTS_DIR) unless Dir.exist? OUTPUTS_DIR
22
+
23
+ def in_path(name)
24
+ INPUTS_DIR + name
25
+ end
26
+
27
+ def out_ref_paths(name)
28
+ [
29
+ OUTPUTS_DIR + "out-#{name}",
30
+ REFS_DIR + "ref-#{name}",
31
+ ]
32
+ end
33
+
metadata ADDED
@@ -0,0 +1,153 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: sndfile
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - ronen barzel
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-02-06 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: ffi
16
+ requirement: &70255236835440 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: *70255236835440
25
+ - !ruby/object:Gem::Dependency
26
+ name: ruby-gsl-ng
27
+ requirement: &70255236834820 !ruby/object:Gem::Requirement
28
+ none: false
29
+ requirements:
30
+ - - ! '>='
31
+ - !ruby/object:Gem::Version
32
+ version: '0'
33
+ type: :runtime
34
+ prerelease: false
35
+ version_requirements: *70255236834820
36
+ - !ruby/object:Gem::Dependency
37
+ name: hash_keyword_args
38
+ requirement: &70255236850680 !ruby/object:Gem::Requirement
39
+ none: false
40
+ requirements:
41
+ - - ! '>='
42
+ - !ruby/object:Gem::Version
43
+ version: '0'
44
+ type: :runtime
45
+ prerelease: false
46
+ version_requirements: *70255236850680
47
+ - !ruby/object:Gem::Dependency
48
+ name: rake
49
+ requirement: &70255236850240 !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :development
56
+ prerelease: false
57
+ version_requirements: *70255236850240
58
+ - !ruby/object:Gem::Dependency
59
+ name: rspec
60
+ requirement: &70255236849800 !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ type: :development
67
+ prerelease: false
68
+ version_requirements: *70255236849800
69
+ - !ruby/object:Gem::Dependency
70
+ name: simplecov
71
+ requirement: &70255236849360 !ruby/object:Gem::Requirement
72
+ none: false
73
+ requirements:
74
+ - - ! '>='
75
+ - !ruby/object:Gem::Version
76
+ version: '0'
77
+ type: :development
78
+ prerelease: false
79
+ version_requirements: *70255236849360
80
+ - !ruby/object:Gem::Dependency
81
+ name: simplecov-gem-adapter
82
+ requirement: &70255236848880 !ruby/object:Gem::Requirement
83
+ none: false
84
+ requirements:
85
+ - - ! '>='
86
+ - !ruby/object:Gem::Version
87
+ version: '0'
88
+ type: :development
89
+ prerelease: false
90
+ version_requirements: *70255236848880
91
+ description: Ruby wrapper for libsndfile. Reads/writes data as GSL matrices, to allow
92
+ fast processing.
93
+ email:
94
+ - ronen@barzel.org
95
+ executables: []
96
+ extensions: []
97
+ extra_rdoc_files: []
98
+ files:
99
+ - .gitignore
100
+ - Gemfile
101
+ - README.md
102
+ - Rakefile
103
+ - lib/gsl-ng/matrix.rb
104
+ - lib/sndfile.rb
105
+ - lib/sndfile/enums.rb
106
+ - lib/sndfile/error.rb
107
+ - lib/sndfile/file.rb
108
+ - lib/sndfile/sndfile_api.rb
109
+ - lib/sndfile/version.rb
110
+ - sndfile.gemspec
111
+ - spec/file_spec.rb
112
+ - spec/inputs/ComputerMagic.wav
113
+ - spec/inputs/Flute3.wav
114
+ - spec/integration_spec.rb
115
+ - spec/refs/ref-copy.wav
116
+ - spec/refs/ref-half.wav
117
+ - spec/refs/ref-mix.wav
118
+ - spec/spec_helper.rb
119
+ homepage: https://github.com/ronen/sndfile
120
+ licenses: []
121
+ post_install_message:
122
+ rdoc_options: []
123
+ require_paths:
124
+ - lib
125
+ required_ruby_version: !ruby/object:Gem::Requirement
126
+ none: false
127
+ requirements:
128
+ - - ! '>='
129
+ - !ruby/object:Gem::Version
130
+ version: '0'
131
+ required_rubygems_version: !ruby/object:Gem::Requirement
132
+ none: false
133
+ requirements:
134
+ - - ! '>='
135
+ - !ruby/object:Gem::Version
136
+ version: '0'
137
+ requirements: []
138
+ rubyforge_project:
139
+ rubygems_version: 1.8.12
140
+ signing_key:
141
+ specification_version: 3
142
+ summary: Ruby wrapper for libsndfile. Reads/writes data as GSL matrices, to allow
143
+ fast processing.
144
+ test_files:
145
+ - spec/file_spec.rb
146
+ - spec/inputs/ComputerMagic.wav
147
+ - spec/inputs/Flute3.wav
148
+ - spec/integration_spec.rb
149
+ - spec/refs/ref-copy.wav
150
+ - spec/refs/ref-half.wav
151
+ - spec/refs/ref-mix.wav
152
+ - spec/spec_helper.rb
153
+ has_rdoc: