sndfile 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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: