lame 0.0.1

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.
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