lame 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (64) hide show
  1. data/.gitignore +20 -0
  2. data/.rspec +3 -0
  3. data/.rvmrc +1 -0
  4. data/.travis.yml +13 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +22 -0
  7. data/README.md +181 -0
  8. data/Rakefile +6 -0
  9. data/docs/id3v2.4.0-structure.txt +731 -0
  10. data/docs/lame-3.99.5.h +1323 -0
  11. data/lame.gemspec +28 -0
  12. data/lib/lame.rb +31 -0
  13. data/lib/lame/buffer.rb +21 -0
  14. data/lib/lame/configuration.rb +228 -0
  15. data/lib/lame/decoder.rb +38 -0
  16. data/lib/lame/decoding/decoded_frame.rb +25 -0
  17. data/lib/lame/decoding/id3_tag_parser.rb +53 -0
  18. data/lib/lame/decoding/mp3_data_header_parser.rb +64 -0
  19. data/lib/lame/decoding/mpeg_audio_frame_finder.rb +46 -0
  20. data/lib/lame/decoding/mpeg_audio_frame_matcher.rb +98 -0
  21. data/lib/lame/decoding/single_frame_decoder.rb +51 -0
  22. data/lib/lame/decoding/stream_decoder.rb +37 -0
  23. data/lib/lame/delegation.rb +68 -0
  24. data/lib/lame/encoder.rb +73 -0
  25. data/lib/lame/encoding/encode_short_buffer.rb +26 -0
  26. data/lib/lame/encoding/flusher.rb +22 -0
  27. data/lib/lame/encoding/id3.rb +46 -0
  28. data/lib/lame/encoding/vbr_info.rb +22 -0
  29. data/lib/lame/error.rb +13 -0
  30. data/lib/lame/ffi.rb +20 -0
  31. data/lib/lame/ffi/decode_flags.rb +19 -0
  32. data/lib/lame/ffi/enums.rb +75 -0
  33. data/lib/lame/ffi/functions.rb +272 -0
  34. data/lib/lame/ffi/global_flags.rb +20 -0
  35. data/lib/lame/ffi/mp3_data.rb +37 -0
  36. data/lib/lame/ffi/version.rb +17 -0
  37. data/lib/lame/version.rb +3 -0
  38. data/spec/buffer_spec.rb +26 -0
  39. data/spec/configuration_spec.rb +391 -0
  40. data/spec/decoder_spec.rb +120 -0
  41. data/spec/decoding/decoded_frame_spec.rb +44 -0
  42. data/spec/decoding/id3_tag_parser_spec.rb +54 -0
  43. data/spec/decoding/mp3_data_header_parser_spec.rb +95 -0
  44. data/spec/decoding/mpeg_audio_frame_finder_spec.rb +50 -0
  45. data/spec/decoding/mpeg_audio_frame_matcher_spec.rb +179 -0
  46. data/spec/decoding/single_frame_decoder_spec.rb +104 -0
  47. data/spec/decoding/stream_decoder_spec.rb +43 -0
  48. data/spec/delegation_spec.rb +146 -0
  49. data/spec/encoder_spec.rb +279 -0
  50. data/spec/encoding/encode_short_buffer_spec.rb +72 -0
  51. data/spec/encoding/flusher_spec.rb +47 -0
  52. data/spec/encoding/id3_spec.rb +106 -0
  53. data/spec/encoding/vbr_info_spec.rb +47 -0
  54. data/spec/ffi/decode_flags_spec.rb +16 -0
  55. data/spec/ffi/encoding_spec.rb +223 -0
  56. data/spec/ffi/global_flags_spec.rb +542 -0
  57. data/spec/ffi/id3tag_spec.rb +135 -0
  58. data/spec/ffi/mp3_data_spec.rb +26 -0
  59. data/spec/files/dies-irae.wav +0 -0
  60. data/spec/integration/decoding_spec.rb +179 -0
  61. data/spec/integration/encoding_spec.rb +126 -0
  62. data/spec/integration/id3_tags_spec.rb +96 -0
  63. data/spec/spec_helper.rb +175 -0
  64. metadata +254 -0
@@ -0,0 +1,37 @@
1
+ module LAME
2
+ module FFI
3
+ class MP3Data < ::FFI::Struct
4
+
5
+ layout :header_parsed, :int,
6
+ :stereo, :int,
7
+ :samplerate, :int,
8
+ :bitrate, :int,
9
+ :mode, :int,
10
+ :mod_ext, :int,
11
+ :framesize, :int,
12
+ :nsamp, :ulong,
13
+ :totalframes, :int,
14
+ :framenum, :int
15
+
16
+ def header_parsed?
17
+ self[:header_parsed] == 1
18
+ end
19
+
20
+ def channel_mode
21
+ case self[:stereo]
22
+ when 1
23
+ :mono
24
+ when 2
25
+ :stereo
26
+ end
27
+ end
28
+
29
+ def sample_rate
30
+ self[:samplerate]
31
+ end
32
+
33
+ end
34
+ end
35
+ end
36
+
37
+
@@ -0,0 +1,17 @@
1
+ module LAME
2
+ module FFI
3
+
4
+ class Version < ::FFI::Struct
5
+ layout :major, :int,
6
+ :minor, :int,
7
+ :alpha, :int,
8
+ :beta, :int,
9
+ :psy_major, :int,
10
+ :psy_minor, :int,
11
+ :psy_alpha, :int,
12
+ :psy_beta, :int,
13
+ :features, :string
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module LAME
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ module LAME
4
+ describe Buffer do
5
+
6
+ it "creates a buffer of given size and type" do
7
+ input = [-42, 0, 42]
8
+ buffer = Buffer.create(:short, input)
9
+
10
+ buffer.should be_a(::FFI::MemoryPointer)
11
+ buffer.size.should eql 6 # shorts are two bytes
12
+ buffer.type_size.should eql 2
13
+ buffer.get_array_of_short(0, 3).should eql [-42, 0, 42]
14
+ end
15
+
16
+ it "creates an empty buffer" do
17
+ buffer = Buffer.create_empty(:uchar, 100)
18
+
19
+ buffer.should be_a(::FFI::MemoryPointer)
20
+ buffer.size.should eql 100
21
+ buffer.type_size.should eql 1
22
+ buffer.get_array_of_uchar(0, 100).should eql [0]*100
23
+ end
24
+
25
+ end
26
+ end
@@ -0,0 +1,391 @@
1
+ require 'spec_helper'
2
+
3
+ module LAME
4
+ describe Configuration do
5
+
6
+ context "intialization" do
7
+ let(:global_flags) { stub }
8
+
9
+ subject(:configuration) { Configuration.new(global_flags) }
10
+
11
+ it "initializes with global flags" do
12
+ configuration.global_flags.should eql global_flags
13
+ end
14
+
15
+ it "initializes a AsmOptimization configuration object" do
16
+ Configuration::AsmOptimization.should_receive(:new).with(global_flags)
17
+ configuration.asm_optimization
18
+ end
19
+
20
+ it "memoizes one AsmOptimization configuration object" do
21
+ Configuration::AsmOptimization.stub(:new).and_return(stub)
22
+ Configuration::AsmOptimization.should_receive(:new).exactly(:once)
23
+ 2.times { configuration.asm_optimization }
24
+ end
25
+
26
+ it "initializes a Id3 configuration object" do
27
+ Configuration::Id3.should_receive(:new).with(global_flags)
28
+ configuration.id3
29
+ end
30
+
31
+ it "memoizes one Id3 configuration object" do
32
+ Configuration::Id3.stub(:new).and_return(stub)
33
+ Configuration::Id3.should_receive(:new).exactly(:once)
34
+ 2.times { configuration.id3 }
35
+ end
36
+
37
+ it "initializes a Quantization configuration object" do
38
+ Configuration::Quantization.should_receive(:new).with(global_flags)
39
+ configuration.quantization
40
+ end
41
+
42
+ it "memoizes one Quantization configuration object" do
43
+ Configuration::Quantization.stub(:new).and_return(stub)
44
+ Configuration::Quantization.should_receive(:new).exactly(:once)
45
+ 2.times { configuration.quantization }
46
+ end
47
+
48
+ it "initializes a VBR configuration object" do
49
+ Configuration::VBR.should_receive(:new).with(global_flags)
50
+ configuration.vbr
51
+ end
52
+
53
+ it "memoizes one VBR configuration object" do
54
+ Configuration::VBR.stub(:new).and_return(stub)
55
+ Configuration::VBR.should_receive(:new).exactly(:once)
56
+ 2.times { configuration.vbr }
57
+ end
58
+
59
+ it "initializes a Filtering configuration object" do
60
+ Configuration::Filtering.should_receive(:new).with(global_flags)
61
+ configuration.filtering
62
+ end
63
+
64
+ it "memoizes one Filtering configuration object" do
65
+ Configuration::Filtering.stub(:new).and_return(stub)
66
+ Configuration::Filtering.should_receive(:new).exactly(:once)
67
+ 2.times { configuration.filtering }
68
+ end
69
+
70
+ it "initializes a PsychoAcoustics configuration object" do
71
+ Configuration::PsychoAcoustics.should_receive(:new).with(global_flags)
72
+ configuration.psycho_acoustics
73
+ end
74
+
75
+ it "memoizes one PsychoAcoustics configuration object" do
76
+ Configuration::PsychoAcoustics.stub(:new).and_return(stub)
77
+ Configuration::PsychoAcoustics.should_receive(:new).exactly(:once)
78
+ 2.times { configuration.psycho_acoustics }
79
+ end
80
+
81
+ context "Id3" do
82
+
83
+ it "initializes the Id3 tag" do
84
+ LAME.should_receive(:id3tag_init).with(global_flags)
85
+ Configuration::Id3.new(global_flags)
86
+ end
87
+
88
+ end
89
+
90
+ describe "#apply! / #applied?" do
91
+ it "is not applied by default" do
92
+ configuration.should_not be_applied
93
+ end
94
+
95
+ it "applies the configuration" do
96
+ LAME.should_receive(:lame_init_params).with(global_flags)
97
+ configuration.apply!
98
+ configuration.should be_applied
99
+ end
100
+
101
+ it "raises an error if configuration could not be applied" do
102
+ LAME.stub(:lame_init_params).and_return(-1)
103
+ expect {
104
+ configuration.apply!
105
+ }.to raise_error(ConfigurationError)
106
+ end
107
+ end
108
+
109
+ describe "#framesize" do
110
+
111
+ it "gets framesize from LAME if configuration was applied" do
112
+ LAME.stub(:lame_init_params)
113
+ configuration.apply!
114
+
115
+ LAME.stub(:lame_get_framesize).and_return(42)
116
+ LAME.should_receive(:lame_get_framesize).with(global_flags)
117
+ configuration.framesize.should eql 42
118
+ end
119
+
120
+ it "raises an error if configuration was not applied" do
121
+ expect {
122
+ configuration.framesize
123
+ }.to raise_error(ConfigurationError)
124
+ end
125
+
126
+ it "calculates the output buffer size based on the framesize" do
127
+ LAME.stub(:lame_init_params)
128
+ configuration.apply!
129
+
130
+ LAME.stub(:lame_get_framesize).and_return(1152)
131
+ LAME.should_receive(:lame_get_framesize).with(global_flags)
132
+ configuration.output_buffer_size.should eql 8640
133
+ end
134
+
135
+ end
136
+ end
137
+
138
+ # More ruby-ish accessors:
139
+ #
140
+ # encoder = LAME::Encoder.new
141
+ # encoder.number_of_samples = 100
142
+ #
143
+ # TODO: question-mark readers for boolean values
144
+ #
145
+ # encoder.replay_gain?
146
+ #
147
+ # Delegates to `LAME.lame_set_num_samples(global_flags, 100)`
148
+ #
149
+ context "set/get delegation" do
150
+
151
+ let(:global_flags) { stub(GlobalFlags) }
152
+ subject(:configuration) { Configuration.new(global_flags) }
153
+
154
+ context "basic fields" do
155
+
156
+ it { should delegate(:number_of_samples).to(:num_samples) }
157
+ it { should delegate(:number_of_channels).to(:num_channels) }
158
+ it { should delegate(:input_samplerate).to(:in_samplerate) }
159
+ it { should delegate(:output_samplerate).to(:out_samplerate) }
160
+ it { should delegate(:force_mid_side).to(:force_ms) }
161
+ it { should delegate(:replay_gain).to(:findReplayGain) }
162
+ it { should delegate(:bitrate).to(:brate) }
163
+ it { should delegate(:strict_iso).to(:strict_ISO) }
164
+ it { should delegate(:allow_different_block_types).to(:allow_diff_short) }
165
+ it { should delegate(:temporal_masking).to(:useTemporal) }
166
+ it { should delegate(:inter_channel_ratio).to(:interChRatio) }
167
+ it { should delegate(:disable_short_blocks).to(:no_short_blocks) }
168
+
169
+ it { should delegate(:scale) }
170
+ it { should delegate(:scale_left) }
171
+ it { should delegate(:scale_right) }
172
+ it { should delegate(:analysis) }
173
+ it { should delegate(:decode_only) }
174
+ it { should delegate(:quality) }
175
+ it { should delegate(:mode) }
176
+ it { should delegate(:free_format) }
177
+ it { should delegate(:decode_on_the_fly) }
178
+ it { should delegate(:copyright) }
179
+ it { should delegate(:original) }
180
+ it { should delegate(:error_protection) }
181
+ it { should delegate(:extension) }
182
+ it { should delegate(:force_short_blocks) }
183
+ it { should delegate(:emphasis) }
184
+
185
+ it "delegates #preset= to LAME.lame_set_preset" do
186
+ LAME.should_receive(:lame_set_preset).with(global_flags, anything)
187
+ configuration.preset = stub
188
+ end
189
+
190
+ end
191
+
192
+ context "asm_optimization fields" do
193
+ subject(:asm_optimization) { configuration.asm_optimization }
194
+
195
+ it "delegates #mmx to LAME.lame_set_asm_optimizations" do
196
+ LAME.should_receive(:lame_set_asm_optimizations).with(global_flags, :MMX, 1)
197
+ asm_optimization.mmx = true
198
+ end
199
+
200
+ it "delegates #amd_3dnow to LAME.lame_set_asm_optimizations" do
201
+ LAME.should_receive(:lame_set_asm_optimizations).with(global_flags, :AMD_3DNOW, 1)
202
+ asm_optimization.amd_3dnow = true
203
+ end
204
+
205
+ it "delegates #sse to LAME.lame_set_asm_optimizations" do
206
+ LAME.should_receive(:lame_set_asm_optimizations).with(global_flags, :SSE, 1)
207
+ asm_optimization.sse = true
208
+ end
209
+ end
210
+
211
+ # There are only setters for these settings:
212
+ context "id3 fields" do
213
+ subject(:id3) { configuration.id3 }
214
+
215
+ it { should delegate(:write_automatic).to(:write_id3tag_automatic) }
216
+
217
+ before do
218
+ LAME.stub(:id3tag_init)
219
+ end
220
+
221
+ it "delegates #v2= to LAME.id3tag_add_v2" do
222
+ LAME.should_receive(:id3tag_add_v2).with(global_flags)
223
+ id3.v2 = true
224
+ end
225
+
226
+ it "delegates #v1_only= to LAME.id3tag_v1_only" do
227
+ LAME.should_receive(:id3tag_v1_only).with(global_flags)
228
+ id3.v1_only = true
229
+ end
230
+
231
+ it "delegates #v2_only= to LAME.id3tag_v2_only" do
232
+ LAME.should_receive(:id3tag_v2_only).with(global_flags)
233
+ id3.v2_only = true
234
+ end
235
+
236
+ it "delegates #v1_space= to LAME.id3tag_v1_space" do
237
+ LAME.should_receive(:id3tag_space_v1).with(global_flags)
238
+ id3.v1_space = true
239
+ end
240
+
241
+ it "delegates #v2_padding= to LAME.id3tag_pad_v2" do
242
+ LAME.should_receive(:id3tag_pad_v2).with(global_flags)
243
+ id3.v2_padding = true
244
+ end
245
+
246
+ it "delegates #v2_padding_size= to LAME.id3tag_set_pad" do
247
+ value = stub
248
+ LAME.should_receive(:id3tag_set_pad).with(global_flags, value)
249
+ id3.v2_padding_size = value
250
+ end
251
+
252
+ # Meta programming in a test. Oh well ;)
253
+ [ :title, :artist, :album, :year, :comment ].each do |field|
254
+
255
+ it "sets the #{field} with string buffer" do
256
+ LAME.should_receive(:"id3tag_set_#{field}") do |flags, string_buffer|
257
+ flags.should eql global_flags
258
+ string_buffer.should be_a(::FFI::MemoryPointer)
259
+ string_buffer.get_string(0).should eql "Some #{field}"
260
+ end
261
+ id3.send(:"#{field}=", "Some #{field}")
262
+ end
263
+
264
+ end
265
+
266
+ it "sets the track" do
267
+ LAME.should_receive(:"id3tag_set_track") do |flags, value|
268
+ flags.should eql global_flags
269
+ value.should eql 42
270
+ end
271
+ id3.track = 42
272
+ end
273
+
274
+ it "sets the genre by name" do
275
+ LAME.should_receive(:id3tag_set_genre) do |flags, value|
276
+ flags.should eql global_flags
277
+ value.should be_a(::FFI::MemoryPointer)
278
+ value.get_string(0).should eql "147"
279
+ end
280
+ id3.genre = "SynthPop"
281
+ end
282
+
283
+ it "sets the genre by id" do
284
+ LAME.should_receive(:id3tag_set_genre) do |flags, value|
285
+ flags.should eql global_flags
286
+ value.should be_a(::FFI::MemoryPointer)
287
+ value.get_string(0).should eql "81"
288
+ end
289
+
290
+ id3.genre = 81
291
+ end
292
+
293
+ it "sets non-standard genre name" do
294
+ LAME.should_receive(:id3tag_set_genre) do |flags, value|
295
+ flags.should eql global_flags
296
+ value.should be_a(::FFI::MemoryPointer)
297
+ value.get_string(0).should eql "Ruby FFI Rock"
298
+ end
299
+ id3.genre = "Ruby FFI Rock"
300
+ end
301
+ end
302
+
303
+ context "quantization fields" do
304
+ subject(:quantization) { configuration.quantization }
305
+
306
+ it { should delegate(:comp).to(:quant_comp) }
307
+ it { should delegate(:comp_short).to(:quant_comp_short) }
308
+ it { should delegate(:experimental_x).to(:experimentalX) }
309
+ it { should delegate(:experimental_y).to(:experimentalY) }
310
+ it { should delegate(:experimental_z).to(:experimentalZ) }
311
+ it { should delegate(:naoki).to(:exp_nspsytune) }
312
+ it { should delegate(:msfix) }
313
+
314
+ it "delegates #reservoir= to LAME.lame_set_disable_reservoir" do
315
+ LAME.should_receive(:lame_set_disable_reservoir).with(global_flags, 1)
316
+ quantization.reservoir = false
317
+ end
318
+
319
+ it "delegates #reservoir to LAME.lame_get_disable_reservoir" do
320
+ LAME.should_receive(:lame_get_disable_reservoir).with(global_flags)
321
+ quantization.reservoir
322
+ end
323
+
324
+ it "delegates #reservoir? to LAME.lame_get_disable_reservoir" do
325
+ LAME.should_receive(:lame_get_disable_reservoir).with(global_flags)
326
+ quantization.reservoir?
327
+ end
328
+
329
+ it "converts the return value 0 for #reservoir? to false" do
330
+ LAME.stub(:lame_get_disable_reservoir).and_return(0)
331
+ quantization.reservoir?.should be_false
332
+ end
333
+ end
334
+
335
+ context "vbr" do
336
+ subject(:vbr) { configuration.vbr }
337
+
338
+ it { should delegate(:write_tag).to(:bWriteVbrTag) }
339
+ it { should delegate(:mode).to(:VBR) }
340
+ it { should delegate(:q).to(:VBR_q) }
341
+ it { should delegate(:quality).to(:VBR_quality) }
342
+ it { should delegate(:mean_bitrate).to(:VBR_mean_bitrate_kbps) }
343
+ it { should delegate(:min_bitrate).to(:VBR_min_bitrate_kbps) }
344
+ it { should delegate(:max_bitrate).to(:VBR_max_bitrate_kbps) }
345
+ it { should delegate(:enforce_min_bitrate).to(:VBR_hard_min) }
346
+ end
347
+
348
+ context "filtering" do
349
+ subject(:filtering) { configuration.filtering }
350
+
351
+ it { should delegate(:low_pass_frequency).to(:lowpassfreq) }
352
+ it { should delegate(:low_pass_width).to(:lowpasswidth) }
353
+ it { should delegate(:high_pass_frequency).to(:highpassfreq) }
354
+ it { should delegate(:high_pass_width).to(:highpasswidth) }
355
+ end
356
+
357
+ context "psycho acoustics" do
358
+ subject(:psycho_acoustics) { configuration.psycho_acoustics }
359
+
360
+ it { should delegate(:ath_only).to(:ATHonly) }
361
+ it { should delegate(:ath_short).to(:ATHshort) }
362
+ it { should delegate(:ath_type).to(:ATHtype) }
363
+ it { should delegate(:ath_lower).to(:ATHlower) }
364
+ it { should delegate(:athaa_type) }
365
+ it { should delegate(:athaa_sensitivity) }
366
+
367
+ it "delegates #ath= to LAME.lame_set_noATH" do
368
+ LAME.should_receive(:lame_set_noATH).with(global_flags, 1)
369
+ psycho_acoustics.ath = false
370
+ end
371
+
372
+ it "delegates #ath to LAME.lame_get_noATH" do
373
+ LAME.should_receive(:lame_get_noATH).with(global_flags)
374
+ psycho_acoustics.ath
375
+ end
376
+
377
+ it "delegates #ath? to LAME.lame_get_noATH" do
378
+ LAME.should_receive(:lame_get_noATH).with(global_flags)
379
+ psycho_acoustics.ath?
380
+ end
381
+
382
+ it "converts the return value 0 for #ath? to true" do
383
+ LAME.stub(:lame_get_noATH).and_return(0)
384
+ psycho_acoustics.ath?.should be_true
385
+ end
386
+ end
387
+
388
+ end
389
+
390
+ end
391
+ end