lame 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -136,26 +136,51 @@ end
136
136
 
137
137
  ### Encoding
138
138
 
139
- TODO
139
+ See `spec/integration/encoding_spec.rb` for an example how to encode a WAV file
140
+ to an MP3 file.
140
141
 
141
- ### Decoding
142
+ The available encoding functions are:
142
143
 
143
- Check this link for a 'simple' example. Note that we need to deal with the
144
- ID3 tags ourselves before decoding MP3 frames.
144
+ ```ruby
145
+ encoder = LAME::Encoder.new
145
146
 
146
- http://sourceforge.net/mailarchive/message.php?msg_id=26907120
147
+ # encode shorts (−32,768 to +32,767), for 16-bit audio
148
+ encoder.encode_short(left, right) do |mp3_frame|
149
+ # ...
150
+ end
147
151
 
148
- Analysis of `lame_decode_initfile` in `get_audio.c`:
152
+ # encode floats (−1.0 to +1.0), for 32-bit floating point audio
153
+ encoder.encode_float(left, right) do |mp3_frame|
154
+ # ...
155
+ end
149
156
 
150
- 1. `hip_decode_init`
151
- 2. Read ID3 tags, starting with "ID3"
152
- 3. The length of the ID3 tag are at the start (right after "ID3")
153
- 4. Optionally read the contents of the ID3 tag, or just skip it
154
- 5. Check if there is a "AID" header, and skip it
155
- 6. Read up until the first "mp123 syncword"
157
+ # encode longs (system dependent, +/- 2^(bits_per_long-1)
158
+ encoder.encode_long(left, right) do |mp3_frame|
159
+ # ...
160
+ end
161
+
162
+ # encode interleaved shorts
163
+ encoder.encode_interleaved_short(samples) do |mp3_frame|
164
+ # ...
165
+ end
166
+
167
+ # encode interleaved floats
168
+ encoder.encode_interleaved_float(samples) do |mp3_frame|
169
+ # ...
170
+ end
171
+ ```
172
+
173
+ The `left` and `right` are arrays of sample values for the given data type.
174
+
175
+ ### Decoding
176
+
177
+ See `spec/integration/decoding_spec.rb` for an example how to decode an MP3 file
178
+ to an WAV file.
156
179
 
157
180
  ### Development
158
181
 
182
+ This section contains some references used during development of this gem.
183
+
159
184
  #### ID3v2 tags
160
185
 
161
186
  To use ID3v2 tags in files, see this post on the `lame-dev` mailing list:
@@ -165,13 +190,43 @@ http://sourceforge.net/mailarchive/message.php?msg_id=18557283
165
190
  So:
166
191
 
167
192
  1. Disable automatic id3tag writing
168
- 2. Write id3v2 tag at start of file (keep track of position)
193
+ 2. Write id3v2 tag at start of file (keep track of size of this tag)
169
194
  3. Write audio to file
170
195
  4. Write id3v1 tag at end of file
171
- 5. Write vbr 'lametag' at start of audio (using position)
196
+ 5. Write vbr 'lametag' at start of audio (using the size of the id3v2 tag)
172
197
 
173
198
  See the example code in `spec/integration/encoding_spec.rb` for an example.
174
199
 
200
+ #### Decoding
201
+
202
+ Check this link for a 'simple' example. Note that we need to deal with the
203
+ ID3 tags ourselves before decoding MP3 frames.
204
+
205
+ http://sourceforge.net/mailarchive/message.php?msg_id=26907120
206
+
207
+ Analysis of `lame_decode_initfile` in `get_audio.c`:
208
+
209
+ 1. `hip_decode_init`
210
+ 2. Read ID3 tags, starting with "ID3"
211
+ 3. The length of the ID3 tag are at the start (right after "ID3")
212
+ 4. Optionally read the contents of the ID3 tag, or just skip it
213
+ 5. Check if there is a "AID" header, and skip it
214
+ 6. Read up until the first "mp123 syncword"
215
+
216
+ After this, we are ready to decode MP3 data.
217
+
218
+ 1. Check if there are some samples in the internal decoder by passing an empty
219
+ input-buffer into the decoder.
220
+ 2. If some decoded audio samples were indeed left in the internal decoder, do
221
+ something useful with them.
222
+ 3. Repeat this until no audio is left in the internal buffer.
223
+ 4. Read some data from the file (starting at the "mp123 syncword") and feed it
224
+ to the decoder.
225
+ 5. Handle the decoded audio.
226
+ 6. Now repeat this process (`GOTO 1`) until the end of the MP3 file.
227
+
228
+ See the example code in `spec/integration/decoding_spec.rb` for an example.
229
+
175
230
  ## Contributing
176
231
 
177
232
  1. Fork it
@@ -6,7 +6,13 @@ require "lame/delegation"
6
6
  require "lame/configuration"
7
7
  require "lame/buffer"
8
8
 
9
- require "lame/encoding/encode_short_buffer"
9
+ require "lame/encoding/stereo_buffer_encoder"
10
+ require "lame/encoding/short_buffer_encoder"
11
+ require "lame/encoding/float_buffer_encoder"
12
+ require "lame/encoding/long_buffer_encoder"
13
+ require "lame/encoding/interleaved_buffer_encoder"
14
+ require "lame/encoding/interleaved_short_buffer_encoder"
15
+ require "lame/encoding/interleaved_float_buffer_encoder"
10
16
  require "lame/encoding/flusher"
11
17
  require "lame/encoding/id3"
12
18
  require "lame/encoding/vbr_info"
@@ -1,6 +1,17 @@
1
1
  module LAME
2
2
  class Encoder
3
3
 
4
+ STEREO_ENCODERS = {
5
+ :short => Encoding::ShortBufferEncoder,
6
+ :float => Encoding::FloatBufferEncoder,
7
+ :long => Encoding::LongBufferEncoder
8
+ }
9
+
10
+ INTERLEAVED_ENCODERS = {
11
+ :short => Encoding::InterleavedShortBufferEncoder,
12
+ :float => Encoding::InterleavedFloatBufferEncoder
13
+ }
14
+
4
15
  attr_reader :global_flags
5
16
 
6
17
  def initialize
@@ -12,13 +23,24 @@ module LAME
12
23
  apply_configuration
13
24
  end
14
25
 
15
- def encode_short(left, right)
16
- apply_configuration
26
+ def encode_short(left, right, &block)
27
+ encode_stereo(left, right, :short, &block)
28
+ end
17
29
 
18
- each_frame(left, right) do |left_frame, right_frame|
19
- mp3_data = Encoding::EncodeShortBuffer.new(configuration).encode_frame(left_frame, right_frame)
20
- yield mp3_data
21
- end
30
+ def encode_float(left, right, &block)
31
+ encode_stereo(left, right, :float, &block)
32
+ end
33
+
34
+ def encode_long(left, right, &block)
35
+ encode_stereo(left, right, :long, &block)
36
+ end
37
+
38
+ def encode_interleaved_short(samples, &block)
39
+ encode_interleaved(samples, :short, &block)
40
+ end
41
+
42
+ def encode_interleaved_float(samples, &block)
43
+ encode_interleaved(samples, :float, &block)
22
44
  end
23
45
 
24
46
  def flush(&block)
@@ -55,12 +77,36 @@ module LAME
55
77
  configuration.apply! unless configuration.applied?
56
78
  end
57
79
 
80
+ def encode_stereo(left, right, data_type)
81
+ apply_configuration
82
+
83
+ each_frame(left, right) do |left_frame, right_frame|
84
+ mp3_data = STEREO_ENCODERS[data_type].new(configuration).encode_frame(left_frame, right_frame)
85
+ yield mp3_data
86
+ end
87
+ end
88
+
89
+ def encode_interleaved(interleaved_samples, data_type)
90
+ apply_configuration
91
+
92
+ each_interleaved_frame(interleaved_samples) do |interleaved_frame|
93
+ mp3_data = INTERLEAVED_ENCODERS[data_type].new(configuration).encode_frame(interleaved_frame)
94
+ yield mp3_data
95
+ end
96
+ end
97
+
58
98
  def each_frame(left, right)
59
99
  left.zip(right).each_slice(framesize) do |slice|
60
100
  yield slice.transpose
61
101
  end
62
102
  end
63
103
 
104
+ def each_interleaved_frame(interleaved_samples)
105
+ interleaved_samples.each_slice(framesize * 2) do |slice|
106
+ yield slice
107
+ end
108
+ end
109
+
64
110
  def yield_or_return(value)
65
111
  if block_given?
66
112
  yield value
@@ -0,0 +1,15 @@
1
+ module LAME
2
+ module Encoding
3
+ class FloatBufferEncoder < StereoBufferEncoder
4
+
5
+ def data_type
6
+ :float
7
+ end
8
+
9
+ def lame_function
10
+ :lame_encode_buffer_ieee_float
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -3,7 +3,9 @@ module LAME
3
3
  class Flusher
4
4
  extend Forwardable
5
5
 
6
- def_delegators :@configuration, :global_flags, :framesize, :output_buffer_size
6
+ attr_reader :configuration
7
+
8
+ def_delegators :configuration, :global_flags, :framesize, :output_buffer_size
7
9
 
8
10
  def initialize(configuration)
9
11
  @configuration = configuration
@@ -3,7 +3,9 @@ module LAME
3
3
  class Id3
4
4
  extend Forwardable
5
5
 
6
- def_delegators :@configuration, :global_flags, :framesize, :output_buffer_size
6
+ attr_reader :configuration
7
+
8
+ def_delegators :configuration, :global_flags, :framesize, :output_buffer_size
7
9
 
8
10
  def initialize(configuration)
9
11
  @configuration = configuration
@@ -0,0 +1,27 @@
1
+ module LAME
2
+ module Encoding
3
+ class InterleavedBufferEncoder
4
+ extend Forwardable
5
+
6
+ attr_reader :configuration
7
+
8
+ def_delegators :configuration, :global_flags, :framesize, :output_buffer_size
9
+
10
+ def initialize(configuration)
11
+ @configuration = configuration
12
+ end
13
+
14
+ def encode_frame(interleaved_samples)
15
+ interleaved_buffer = Buffer.create(data_type, interleaved_samples)
16
+ output = Buffer.create_empty(:uchar, output_buffer_size)
17
+
18
+ mp3_size = LAME.send(lame_function, global_flags,
19
+ interleaved_buffer, interleaved_samples.size/2,
20
+ output, output_buffer_size)
21
+
22
+ output.get_bytes(0, mp3_size)
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,15 @@
1
+ module LAME
2
+ module Encoding
3
+ class InterleavedFloatBufferEncoder < InterleavedBufferEncoder
4
+
5
+ def data_type
6
+ :float
7
+ end
8
+
9
+ def lame_function
10
+ :lame_encode_buffer_interleaved_ieee_float
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module LAME
2
+ module Encoding
3
+ class InterleavedShortBufferEncoder < InterleavedBufferEncoder
4
+
5
+ def data_type
6
+ :short
7
+ end
8
+
9
+ def lame_function
10
+ :lame_encode_buffer_interleaved
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module LAME
2
+ module Encoding
3
+ class LongBufferEncoder < StereoBufferEncoder
4
+
5
+ def data_type
6
+ :long
7
+ end
8
+
9
+ def lame_function
10
+ :lame_encode_buffer_long2
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,15 @@
1
+ module LAME
2
+ module Encoding
3
+ class ShortBufferEncoder < StereoBufferEncoder
4
+
5
+ def data_type
6
+ :short
7
+ end
8
+
9
+ def lame_function
10
+ :lame_encode_buffer
11
+ end
12
+
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,28 @@
1
+ module LAME
2
+ module Encoding
3
+ class StereoBufferEncoder
4
+ extend Forwardable
5
+
6
+ attr_reader :configuration
7
+
8
+ def_delegators :configuration, :global_flags, :framesize, :output_buffer_size
9
+
10
+ def initialize(configuration)
11
+ @configuration = configuration
12
+ end
13
+
14
+ def encode_frame(left, right)
15
+ left_buffer = Buffer.create(data_type, left)
16
+ right_buffer = Buffer.create(data_type, right)
17
+ output = Buffer.create_empty(:uchar, output_buffer_size)
18
+
19
+ mp3_size = LAME.send(lame_function, global_flags,
20
+ left_buffer, right_buffer, left.size,
21
+ output, output_buffer_size)
22
+
23
+ output.get_bytes(0, mp3_size)
24
+ end
25
+
26
+ end
27
+ end
28
+ end
@@ -3,7 +3,9 @@ module LAME
3
3
  class VBRInfo
4
4
  extend Forwardable
5
5
 
6
- def_delegators :@configuration, :global_flags, :framesize, :output_buffer_size
6
+ attr_reader :configuration
7
+
8
+ def_delegators :configuration, :global_flags, :framesize, :output_buffer_size
7
9
 
8
10
  def initialize(configuration)
9
11
  @configuration = configuration
@@ -1,3 +1,3 @@
1
1
  module LAME
2
- VERSION = "0.0.1"
2
+ VERSION = "0.0.2"
3
3
  end
@@ -54,86 +54,197 @@ module LAME
54
54
 
55
55
  let(:left) { [0] }
56
56
  let(:right) { [0] }
57
+ let(:samples) { [0,0] }
58
+
57
59
  let(:global_flags) { subject.global_flags }
58
60
  let(:configuration) { stub(Configuration, :framesize => 100) }
59
61
 
60
62
  before do
61
63
  Configuration.stub(:new).and_return(configuration)
62
64
  configuration.stub(:applied?).and_return(true)
63
- Encoding::EncodeShortBuffer.stub(:new).and_return(stub.as_null_object)
64
65
  end
65
66
 
66
- it "applies the configuration if not done already" do
67
- configuration.stub(:applied?).and_return(false)
68
- configuration.should_receive(:apply!)
69
- encoder.encode_short(left, right) { }
70
- end
67
+ describe "#encode_short" do
71
68
 
72
- it "does not apply the configuration if already applied" do
73
- configuration.stub(:applied?).and_return(true)
74
- configuration.should_not_receive(:apply!)
75
- encoder.encode_short(left, right) { }
69
+ before do
70
+ Encoding::ShortBufferEncoder.stub(:new).and_return(stub.as_null_object)
71
+ end
72
+
73
+ it "applies the configuration if not done already" do
74
+ configuration.stub(:applied?).and_return(false)
75
+ configuration.should_receive(:apply!)
76
+ encoder.encode_short(left, right) { }
77
+ end
78
+
79
+ it "does not apply the configuration if already applied" do
80
+ configuration.stub(:applied?).and_return(true)
81
+ configuration.should_not_receive(:apply!)
82
+ encoder.encode_short(left, right) { }
83
+ end
84
+
85
+ context "delegation" do
86
+ let(:short_encoder) { stub.as_null_object }
87
+
88
+ before do
89
+ Encoding::ShortBufferEncoder.stub(:new).and_return(short_encoder)
90
+ end
91
+
92
+ it "create a short encoder with configuration" do
93
+ Encoding::ShortBufferEncoder.should_receive(:new).with(configuration)
94
+
95
+ encoder.encode_short(left, right) { }
96
+ end
97
+
98
+ it "delegates encoding to the short encoder" do
99
+ left.stub(:length => 100) # exactly framesize
100
+ short_encoder.should_receive(:encode_frame).with(left, right)
101
+
102
+ encoder.encode_short(left, right) { }
103
+ end
104
+
105
+ it "yields the encoder results" do
106
+ mp3_data = stub
107
+ short_encoder.stub(:encode_frame).and_return(mp3_data)
108
+
109
+ expect { |block|
110
+ encoder.encode_short(left, right, &block)
111
+ }.to yield_with_args(mp3_data)
112
+ end
113
+
114
+ it "delegates multiple times for large input" do
115
+ right = left = [0]*150 # larger than framesize
116
+
117
+ short_encoder.should_receive(:encode_frame) do |left_frame, right_frame|
118
+ left_frame.length.should eql 100
119
+ right_frame.length.should eql 100
120
+ end
121
+
122
+ short_encoder.should_receive(:encode_frame) do |left_frame, right_frame|
123
+ left_frame.length.should eql 50
124
+ right_frame.length.should eql 50
125
+ end
126
+
127
+ encoder.encode_short(left, right) { }
128
+ end
129
+
130
+ it "yields multiple times for large input" do
131
+ mp3_data1 = stub("frame1")
132
+ mp3_data2 = stub("frame2")
133
+
134
+ left = [0]*150 # larger than framesize
135
+
136
+ short_encoder.stub(:encode_frame).and_return(mp3_data1, mp3_data2)
137
+
138
+ expect { |block|
139
+ encoder.encode_short(left, right, &block)
140
+ }.to yield_successive_args(mp3_data1, mp3_data2)
141
+ end
142
+ end
76
143
  end
77
144
 
78
- context "delegation" do
79
- let(:short_encoder) { stub.as_null_object }
145
+ describe "#encode_float" do
146
+ it "create a float encoder" do
147
+ Encoding::FloatBufferEncoder.stub(:new).and_return(stub.as_null_object)
148
+ Encoding::FloatBufferEncoder.should_receive(:new).with(configuration)
80
149
 
81
- before do
82
- Encoding::EncodeShortBuffer.stub(:new).and_return(short_encoder)
150
+ encoder.encode_float(left, right) { }
83
151
  end
152
+ end
84
153
 
85
- it "create a short encoder with configuration" do
86
- Encoding::EncodeShortBuffer.should_receive(:new).with(configuration)
154
+ describe "#encode_long" do
155
+ it "create a long encoder" do
156
+ Encoding::LongBufferEncoder.stub(:new).and_return(stub.as_null_object)
157
+ Encoding::LongBufferEncoder.should_receive(:new).with(configuration)
87
158
 
88
- encoder.encode_short(left, right) { }
159
+ encoder.encode_long(left, right) { }
89
160
  end
161
+ end
90
162
 
91
- it "delegates encoding to the short encoder" do
92
- left.stub(:length => 100) # exactly framesize
93
- short_encoder.should_receive(:encode_frame).with(left, right)
163
+ describe "#encode_interleaved_short" do
94
164
 
95
- encoder.encode_short(left, right) { }
165
+ before do
166
+ Encoding::InterleavedShortBufferEncoder.stub(:new).and_return(stub.as_null_object)
96
167
  end
97
168
 
98
- it "yields the encoder results" do
99
- mp3_data = stub
100
- short_encoder.stub(:encode_frame).and_return(mp3_data)
169
+ it "applies the configuration if not done already" do
170
+ configuration.stub(:applied?).and_return(false)
171
+ configuration.should_receive(:apply!)
172
+ encoder.encode_interleaved_short(samples) { }
173
+ end
101
174
 
102
- expect { |block|
103
- encoder.encode_short(left, right, &block)
104
- }.to yield_with_args(mp3_data)
175
+ it "does not apply the configuration if already applied" do
176
+ configuration.stub(:applied?).and_return(true)
177
+ configuration.should_not_receive(:apply!)
178
+ encoder.encode_interleaved_short(samples) { }
105
179
  end
106
180
 
107
- it "delegates multiple times for large input" do
108
- right = left = [0]*150 # larger than framesize
181
+ context "delegation" do
182
+ let(:interleaved_short_encoder) { stub.as_null_object }
109
183
 
110
- short_encoder.should_receive(:encode_frame) do |left_frame, right_frame|
111
- left_frame.length.should eql 100
112
- right_frame.length.should eql 100
184
+ before do
185
+ Encoding::InterleavedShortBufferEncoder.stub(:new).and_return(interleaved_short_encoder)
113
186
  end
114
187
 
115
- short_encoder.should_receive(:encode_frame) do |left_frame, right_frame|
116
- left_frame.length.should eql 50
117
- right_frame.length.should eql 50
188
+ it "create a short encoder with configuration" do
189
+ Encoding::InterleavedShortBufferEncoder.should_receive(:new).with(configuration)
190
+
191
+ encoder.encode_interleaved_short(samples) { }
118
192
  end
119
193
 
120
- encoder.encode_short(left, right) { }
121
- end
194
+ it "delegates encoding to the short encoder" do
195
+ samples.stub(:length => 200) # exactly framesize
196
+ interleaved_short_encoder.should_receive(:encode_frame).with(samples)
122
197
 
123
- it "yields multiple times for large input" do
124
- mp3_data1 = stub("frame1")
125
- mp3_data2 = stub("frame2")
198
+ encoder.encode_interleaved_short(samples) { }
199
+ end
200
+
201
+ it "yields the encoder results" do
202
+ mp3_data = stub
203
+ interleaved_short_encoder.stub(:encode_frame).and_return(mp3_data)
126
204
 
127
- left = [0]*150 # larger than framesize
205
+ expect { |block|
206
+ encoder.encode_interleaved_short(samples, &block)
207
+ }.to yield_with_args(mp3_data)
208
+ end
128
209
 
129
- short_encoder.stub(:encode_frame).and_return(mp3_data1, mp3_data2)
210
+ it "delegates multiple times for large input" do
211
+ samples = [0]*300 # larger than framesize
130
212
 
131
- expect { |block|
132
- encoder.encode_short(left, right, &block)
133
- }.to yield_successive_args(mp3_data1, mp3_data2)
213
+ interleaved_short_encoder.should_receive(:encode_frame) do |samples|
214
+ samples.length.should eql 200
215
+ end
216
+
217
+ interleaved_short_encoder.should_receive(:encode_frame) do |samples|
218
+ samples.length.should eql 100
219
+ end
220
+
221
+ encoder.encode_interleaved_short(samples) { }
222
+ end
223
+
224
+ it "yields multiple times for large input" do
225
+ mp3_data1 = stub("frame1")
226
+ mp3_data2 = stub("frame2")
227
+
228
+ samples = [0]*300 # larger than framesize
229
+
230
+ interleaved_short_encoder.stub(:encode_frame).and_return(mp3_data1, mp3_data2)
231
+
232
+ expect { |block|
233
+ encoder.encode_interleaved_short(samples, &block)
234
+ }.to yield_successive_args(mp3_data1, mp3_data2)
235
+ end
134
236
  end
237
+ end
238
+
239
+ describe "#encode_interleaved_float" do
240
+ it "create a float encoder" do
241
+ Encoding::InterleavedFloatBufferEncoder.stub(:new).and_return(stub.as_null_object)
242
+ Encoding::InterleavedFloatBufferEncoder.should_receive(:new).with(configuration)
135
243
 
244
+ encoder.encode_interleaved_float(samples) { }
245
+ end
136
246
  end
247
+
137
248
  end
138
249
 
139
250
  context "flushing" do