lame 0.0.1 → 0.0.2
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/README.md +69 -14
- data/lib/lame.rb +7 -1
- data/lib/lame/encoder.rb +52 -6
- data/lib/lame/encoding/float_buffer_encoder.rb +15 -0
- data/lib/lame/encoding/flusher.rb +3 -1
- data/lib/lame/encoding/id3.rb +3 -1
- data/lib/lame/encoding/interleaved_buffer_encoder.rb +27 -0
- data/lib/lame/encoding/interleaved_float_buffer_encoder.rb +15 -0
- data/lib/lame/encoding/interleaved_short_buffer_encoder.rb +15 -0
- data/lib/lame/encoding/long_buffer_encoder.rb +15 -0
- data/lib/lame/encoding/short_buffer_encoder.rb +15 -0
- data/lib/lame/encoding/stereo_buffer_encoder.rb +28 -0
- data/lib/lame/encoding/vbr_info.rb +3 -1
- data/lib/lame/version.rb +1 -1
- data/spec/encoder_spec.rb +156 -45
- data/spec/encoding/interleaved_buffer_encoder_spec.rb +81 -0
- data/spec/encoding/{encode_short_buffer_spec.rb → stereo_buffer_encoder_spec.rb} +31 -10
- data/spec/integration/decoding_spec.rb +5 -2
- data/spec/integration/encoding_spec.rb +56 -55
- data/spec/integration/id3_tags_spec.rb +2 -1
- data/spec/lib/custom_matchers.rb +116 -0
- data/spec/lib/setter_getter.rb +32 -0
- data/spec/lib/wave_file_generator.rb +46 -0
- data/spec/spec_helper.rb +4 -147
- metadata +20 -8
- data/lib/lame/encoding/encode_short_buffer.rb +0 -26
- data/spec/files/dies-irae.wav +0 -0
@@ -0,0 +1,81 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
module LAME
|
4
|
+
module Encoding
|
5
|
+
|
6
|
+
shared_examples_for "a interleaved buffer encoder" do
|
7
|
+
# stubbing galore!
|
8
|
+
|
9
|
+
let(:framesize) { 1152 }
|
10
|
+
let(:samples) { stub(:size => framesize*2) }
|
11
|
+
let(:global_flags) { stub }
|
12
|
+
let(:configuration) { stub(Configuration, :global_flags => global_flags, :framesize => framesize, :output_buffer_size => 8640) }
|
13
|
+
|
14
|
+
subject(:encoder) { described_class.new(configuration) }
|
15
|
+
|
16
|
+
it "creates input buffers" do
|
17
|
+
LAME.stub(lame_function).and_return(0)
|
18
|
+
|
19
|
+
Buffer.should_receive(:create).with(data_type, samples)
|
20
|
+
|
21
|
+
encoder.encode_frame(samples)
|
22
|
+
end
|
23
|
+
|
24
|
+
it "creates output buffer" do
|
25
|
+
LAME.stub(lame_function).and_return(0)
|
26
|
+
|
27
|
+
Buffer.stub(:create)
|
28
|
+
Buffer.should_receive(:create_empty).with(:uchar, 8640).and_return(stub.as_null_object)
|
29
|
+
|
30
|
+
encoder.encode_frame(samples)
|
31
|
+
end
|
32
|
+
|
33
|
+
it "encodes the input" do
|
34
|
+
samples_stub = stub("samples")
|
35
|
+
output_stub = stub("output", :get_bytes => [])
|
36
|
+
|
37
|
+
Buffer.stub(:create).with(data_type, samples).and_return(samples_stub)
|
38
|
+
Buffer.stub(:create_empty).with(:uchar, 8640).and_return(output_stub)
|
39
|
+
|
40
|
+
LAME.should_receive(lame_function) do |flags, interleaved_buffer, framesize, output, output_size|
|
41
|
+
flags.should eql global_flags
|
42
|
+
interleaved_buffer.should eql samples_stub
|
43
|
+
framesize.should eql 1152
|
44
|
+
output.should eql output_stub
|
45
|
+
output_size.should eql 8640
|
46
|
+
|
47
|
+
0 # return value
|
48
|
+
end
|
49
|
+
|
50
|
+
encoder.encode_frame(samples)
|
51
|
+
end
|
52
|
+
|
53
|
+
it "returns the encoded data" do
|
54
|
+
LAME.stub(lame_function).and_return(512)
|
55
|
+
|
56
|
+
mp3_data = stub
|
57
|
+
output = stub
|
58
|
+
output.stub(:get_bytes).with(0, 512).and_return(mp3_data)
|
59
|
+
|
60
|
+
Buffer.stub(:create)
|
61
|
+
Buffer.stub(:create_empty).with(:uchar, 8640).and_return(output)
|
62
|
+
|
63
|
+
encoder.encode_frame(samples).should eql mp3_data
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
describe InterleavedShortBufferEncoder do
|
68
|
+
it_should_behave_like "a interleaved buffer encoder" do
|
69
|
+
let(:data_type) { :short }
|
70
|
+
let(:lame_function) { :lame_encode_buffer_interleaved }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
describe InterleavedFloatBufferEncoder do
|
75
|
+
it_should_behave_like "a interleaved buffer encoder" do
|
76
|
+
let(:data_type) { :float }
|
77
|
+
let(:lame_function) { :lame_encode_buffer_interleaved_ieee_float }
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
@@ -2,8 +2,8 @@ require 'spec_helper'
|
|
2
2
|
|
3
3
|
module LAME
|
4
4
|
module Encoding
|
5
|
-
describe EncodeShortBuffer do
|
6
5
|
|
6
|
+
shared_examples_for "a stereo buffer encoder" do
|
7
7
|
# stubbing galore!
|
8
8
|
|
9
9
|
let(:framesize) { 1152 }
|
@@ -12,19 +12,19 @@ module LAME
|
|
12
12
|
let(:global_flags) { stub }
|
13
13
|
let(:configuration) { stub(Configuration, :global_flags => global_flags, :framesize => framesize, :output_buffer_size => 8640) }
|
14
14
|
|
15
|
-
subject(:encoder) {
|
15
|
+
subject(:encoder) { described_class.new(configuration) }
|
16
16
|
|
17
17
|
it "creates input buffers" do
|
18
|
-
LAME.stub(
|
18
|
+
LAME.stub(lame_function).and_return(0)
|
19
19
|
|
20
|
-
Buffer.should_receive(:create).with(
|
21
|
-
Buffer.should_receive(:create).with(
|
20
|
+
Buffer.should_receive(:create).with(data_type, left)
|
21
|
+
Buffer.should_receive(:create).with(data_type, right)
|
22
22
|
|
23
23
|
encoder.encode_frame(left, right)
|
24
24
|
end
|
25
25
|
|
26
26
|
it "creates output buffer" do
|
27
|
-
LAME.stub(
|
27
|
+
LAME.stub(lame_function).and_return(0)
|
28
28
|
|
29
29
|
Buffer.stub(:create)
|
30
30
|
Buffer.should_receive(:create_empty).with(:uchar, 8640).and_return(stub.as_null_object)
|
@@ -37,11 +37,11 @@ module LAME
|
|
37
37
|
right_stub = stub("right")
|
38
38
|
output_stub = stub("output", :get_bytes => [])
|
39
39
|
|
40
|
-
Buffer.stub(:create).with(
|
41
|
-
Buffer.stub(:create).with(
|
40
|
+
Buffer.stub(:create).with(data_type, left).and_return(left_stub)
|
41
|
+
Buffer.stub(:create).with(data_type, right).and_return(right_stub)
|
42
42
|
Buffer.stub(:create_empty).with(:uchar, 8640).and_return(output_stub)
|
43
43
|
|
44
|
-
LAME.should_receive(
|
44
|
+
LAME.should_receive(lame_function) do |flags, left_buffer, right_buffer, framesize, output, output_size|
|
45
45
|
flags.should eql global_flags
|
46
46
|
left_buffer.should eql left_stub
|
47
47
|
right_buffer.should eql right_stub
|
@@ -56,7 +56,7 @@ module LAME
|
|
56
56
|
end
|
57
57
|
|
58
58
|
it "returns the encoded data" do
|
59
|
-
LAME.stub(
|
59
|
+
LAME.stub(lame_function).and_return(512)
|
60
60
|
|
61
61
|
mp3_data = stub
|
62
62
|
output = stub
|
@@ -68,5 +68,26 @@ module LAME
|
|
68
68
|
encoder.encode_frame(left, right).should eql mp3_data
|
69
69
|
end
|
70
70
|
end
|
71
|
+
|
72
|
+
describe ShortBufferEncoder do
|
73
|
+
it_should_behave_like "a stereo buffer encoder" do
|
74
|
+
let(:data_type) { :short }
|
75
|
+
let(:lame_function) { :lame_encode_buffer }
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
describe FloatBufferEncoder do
|
80
|
+
it_should_behave_like "a stereo buffer encoder" do
|
81
|
+
let(:data_type) { :float }
|
82
|
+
let(:lame_function) { :lame_encode_buffer_ieee_float }
|
83
|
+
end
|
84
|
+
end
|
85
|
+
|
86
|
+
describe LongBufferEncoder do
|
87
|
+
it_should_behave_like "a stereo buffer encoder" do
|
88
|
+
let(:data_type) { :long }
|
89
|
+
let(:lame_function) { :lame_encode_buffer_long2 }
|
90
|
+
end
|
91
|
+
end
|
71
92
|
end
|
72
93
|
end
|
@@ -10,8 +10,9 @@ describe "Decoding", :slow => true do
|
|
10
10
|
decoder = LAME::Decoder.new(mp3_file)
|
11
11
|
|
12
12
|
format = WaveFile::Format.new(decoder.channel_mode, :pcm_16, decoder.sample_rate)
|
13
|
+
output = Tempfile.new('output.wav')
|
13
14
|
|
14
|
-
WaveFile::Writer.new(
|
15
|
+
WaveFile::Writer.new(output.path, format) do |writer|
|
15
16
|
|
16
17
|
decoder.each_decoded_frame do |decoded_frame|
|
17
18
|
buffer = WaveFile::Buffer.new(decoded_frame.samples, format)
|
@@ -57,9 +58,11 @@ describe "Decoding", :slow => true do
|
|
57
58
|
puts "framenum: #{@mp3_data[:framenum]}"
|
58
59
|
end
|
59
60
|
|
61
|
+
output = Tempfile.new('output.wav')
|
62
|
+
|
60
63
|
# read mp3_data (number of frames etc)
|
61
64
|
format = WaveFile::Format.new(:stereo, :pcm_16, 44100)
|
62
|
-
WaveFile::Writer.new(
|
65
|
+
WaveFile::Writer.new(output.path, format) do |writer|
|
63
66
|
|
64
67
|
# See get_audio.c:2082 #lame_decode_fromfile
|
65
68
|
#
|
@@ -4,12 +4,65 @@ require 'wavefile'
|
|
4
4
|
|
5
5
|
describe "Encoding", :slow => true do
|
6
6
|
|
7
|
-
let(:
|
8
|
-
let(:
|
9
|
-
let(:
|
7
|
+
let(:wav_file) { WaveFileGenerator.new(:length => 2).generate }
|
8
|
+
let(:wav_path) { wav_file.path }
|
9
|
+
let(:mp3_path_raw) { Tempfile.new("output-raw.mp3") }
|
10
|
+
let(:mp3_path_api) { Tempfile.new("output-api.mp3") }
|
10
11
|
|
11
12
|
let(:wav_reader) { WaveFile::Reader.new(wav_path) }
|
12
13
|
|
14
|
+
it "encodes a file by api" do
|
15
|
+
|
16
|
+
encoder = LAME::Encoder.new
|
17
|
+
|
18
|
+
encoder.configure do |config|
|
19
|
+
config.id3.write_automatic = false
|
20
|
+
config.id3.v2 = true
|
21
|
+
config.id3.title = "Dies Irae"
|
22
|
+
config.id3.genre = "Classical"
|
23
|
+
|
24
|
+
#config.bitrate = 192
|
25
|
+
|
26
|
+
# config.preset = :V0 # doesn't work?
|
27
|
+
config.vbr.mode = :vbr_default
|
28
|
+
config.vbr.q = 0
|
29
|
+
end
|
30
|
+
|
31
|
+
File.open(mp3_path_api, "wb") do |file|
|
32
|
+
|
33
|
+
id3v2_size = 0
|
34
|
+
encoder.id3v2 do |tag|
|
35
|
+
file.write tag
|
36
|
+
id3v2_size = tag.size
|
37
|
+
end
|
38
|
+
|
39
|
+
wav_reader.each_buffer(encoder.framesize) do |read_buffer|
|
40
|
+
left = read_buffer.samples.map { |s| s[0] }
|
41
|
+
right = read_buffer.samples.map { |s| s[1] }
|
42
|
+
|
43
|
+
encoder.encode_short(left, right) do |mp3|
|
44
|
+
file.write mp3
|
45
|
+
end
|
46
|
+
end
|
47
|
+
encoder.flush do |flush_frame|
|
48
|
+
file.write(flush_frame)
|
49
|
+
end
|
50
|
+
|
51
|
+
encoder.id3v1 do |tag|
|
52
|
+
file.write tag
|
53
|
+
end
|
54
|
+
|
55
|
+
encoder.vbr_frame do |vbr_frame|
|
56
|
+
file.seek(id3v2_size)
|
57
|
+
file.write(vbr_frame)
|
58
|
+
end
|
59
|
+
|
60
|
+
end
|
61
|
+
|
62
|
+
# TODO: Need a better way to test output..
|
63
|
+
# Digest::MD5.hexdigest(File.read(mp3_path_api)).should eql "d1cd92c106e7aac4f5291fd141a19e10"
|
64
|
+
end
|
65
|
+
|
13
66
|
# This test serves as an example how to use the LAME API
|
14
67
|
it "encodes a wav file" do
|
15
68
|
|
@@ -71,56 +124,4 @@ describe "Encoding", :slow => true do
|
|
71
124
|
# Digest::MD5.hexdigest(File.read(mp3_path_raw)).should eql "84a1ce7994bb4a54fc13fb5381ebac40"
|
72
125
|
end
|
73
126
|
|
74
|
-
it "encodes a file by api" do
|
75
|
-
|
76
|
-
encoder = LAME::Encoder.new
|
77
|
-
|
78
|
-
encoder.configure do |config|
|
79
|
-
config.id3.write_automatic = false
|
80
|
-
config.id3.v2 = true
|
81
|
-
config.id3.title = "Dies Irae"
|
82
|
-
config.id3.genre = "Classical"
|
83
|
-
|
84
|
-
#config.bitrate = 192
|
85
|
-
|
86
|
-
# config.preset = :V0 # doesn't work?
|
87
|
-
config.vbr.mode = :vbr_default
|
88
|
-
config.vbr.q = 0
|
89
|
-
end
|
90
|
-
|
91
|
-
File.open(mp3_path_api, "wb") do |file|
|
92
|
-
|
93
|
-
id3v2_size = 0
|
94
|
-
encoder.id3v2 do |tag|
|
95
|
-
file.write tag
|
96
|
-
id3v2_size = tag.size
|
97
|
-
end
|
98
|
-
|
99
|
-
wav_reader.each_buffer(encoder.framesize) do |read_buffer|
|
100
|
-
left = read_buffer.samples.map { |s| s[0] }
|
101
|
-
right = read_buffer.samples.map { |s| s[1] }
|
102
|
-
|
103
|
-
encoder.encode_short(left, right) do |mp3|
|
104
|
-
file.write mp3
|
105
|
-
end
|
106
|
-
end
|
107
|
-
encoder.flush do |flush_frame|
|
108
|
-
file.write(flush_frame)
|
109
|
-
end
|
110
|
-
|
111
|
-
encoder.id3v1 do |tag|
|
112
|
-
file.write tag
|
113
|
-
end
|
114
|
-
|
115
|
-
encoder.vbr_frame do |vbr_frame|
|
116
|
-
file.seek(id3v2_size)
|
117
|
-
file.write(vbr_frame)
|
118
|
-
end
|
119
|
-
|
120
|
-
end
|
121
|
-
|
122
|
-
# TODO: Need a better way to test output..
|
123
|
-
# Digest::MD5.hexdigest(File.read(mp3_path_api)).should eql "d1cd92c106e7aac4f5291fd141a19e10"
|
124
|
-
end
|
125
|
-
|
126
127
|
end
|
@@ -4,7 +4,8 @@ require 'mp3info'
|
|
4
4
|
|
5
5
|
describe "ID3 tags", :slow => true do
|
6
6
|
|
7
|
-
let(:
|
7
|
+
let(:wav_file) { WaveFileGenerator.new(:length => 2).generate }
|
8
|
+
let(:wav_path) { wav_file.path }
|
8
9
|
let(:mp3_id3_path) { File.expand_path(File.join(File.dirname(__FILE__), '../files/dies-irae-id3-raw.mp3')) }
|
9
10
|
let(:wav_reader) { WaveFile::Reader.new(wav_path) }
|
10
11
|
|
@@ -0,0 +1,116 @@
|
|
1
|
+
# Validate existence of a getter, setter and the default value.
|
2
|
+
RSpec::Matchers.define :have_flag do |expected|
|
3
|
+
include SetterGetter
|
4
|
+
|
5
|
+
chain :for do |flags_pointer|
|
6
|
+
@flags_pointer = flags_pointer
|
7
|
+
end
|
8
|
+
|
9
|
+
chain :with_value do |value|
|
10
|
+
@value = value
|
11
|
+
end
|
12
|
+
|
13
|
+
match do |actual|
|
14
|
+
has_getter?(actual, expected) &&
|
15
|
+
has_setter?(actual, expected) &&
|
16
|
+
has_value?(actual, expected)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Validate setting a value.
|
21
|
+
RSpec::Matchers.define :be_able_to_set do |expected|
|
22
|
+
include SetterGetter
|
23
|
+
|
24
|
+
chain :for do |flags_pointer|
|
25
|
+
@flags_pointer = flags_pointer
|
26
|
+
end
|
27
|
+
|
28
|
+
chain :to do |value|
|
29
|
+
@value = value
|
30
|
+
end
|
31
|
+
|
32
|
+
chain :and_return do |value|
|
33
|
+
@return = value
|
34
|
+
end
|
35
|
+
|
36
|
+
match do |actual|
|
37
|
+
set_value(actual, expected) &&
|
38
|
+
has_value?(actual, expected)
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Validate getting a value.
|
43
|
+
RSpec::Matchers.define :have_getter do |expected|
|
44
|
+
include SetterGetter
|
45
|
+
|
46
|
+
chain :for do |flags_pointer|
|
47
|
+
@flags_pointer = flags_pointer
|
48
|
+
end
|
49
|
+
|
50
|
+
chain :with_value do |value|
|
51
|
+
@value = value
|
52
|
+
end
|
53
|
+
|
54
|
+
match do |actual|
|
55
|
+
has_getter?(actual, expected) &&
|
56
|
+
has_value?(actual, expected)
|
57
|
+
end
|
58
|
+
|
59
|
+
failure_message_for_should do |actual|
|
60
|
+
if !has_getter?(actual, expected)
|
61
|
+
"expected that #{actual} would have a getter for field :#{expected}"
|
62
|
+
elsif @value && !has_value?(actual, expected)
|
63
|
+
actual_value = actual_value(actual, expected)
|
64
|
+
"expected field :#{expected} to have a value of #{@value}, but got #{actual_value}"
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
# Validate delegation to global_flags.
|
70
|
+
RSpec::Matchers.define :delegate do |from|
|
71
|
+
include SetterGetter
|
72
|
+
|
73
|
+
chain :to do |target|
|
74
|
+
@target = target
|
75
|
+
end
|
76
|
+
|
77
|
+
match do |subject|
|
78
|
+
@from = from
|
79
|
+
has_setter?(LAME, target) &&
|
80
|
+
delegates_setter? &&
|
81
|
+
has_getter?(LAME, target) &&
|
82
|
+
delegates_getter?
|
83
|
+
end
|
84
|
+
|
85
|
+
def delegates_setter?
|
86
|
+
LAME.should_receive(:"lame_set_#{target}").with(subject.global_flags, anything)
|
87
|
+
subject.send(:"#{from}=", double)
|
88
|
+
true
|
89
|
+
rescue => e
|
90
|
+
# TODO: save raised exception for better failure message
|
91
|
+
false
|
92
|
+
end
|
93
|
+
|
94
|
+
def delegates_getter?
|
95
|
+
LAME.should_receive(:"lame_get_#{target}").with(subject.global_flags)
|
96
|
+
subject.send(:"#{from}")
|
97
|
+
true
|
98
|
+
rescue => e
|
99
|
+
# TODO: save raised exception for better failure message
|
100
|
+
false
|
101
|
+
end
|
102
|
+
|
103
|
+
failure_message_for_should do |actual|
|
104
|
+
"expected #{subject.class} to delegate :#{from} to LAME.lame_set_#{target}"
|
105
|
+
end
|
106
|
+
|
107
|
+
def target
|
108
|
+
@target || from
|
109
|
+
end
|
110
|
+
|
111
|
+
def from
|
112
|
+
@from
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
|
@@ -0,0 +1,32 @@
|
|
1
|
+
module SetterGetter
|
2
|
+
def has_getter?(lame, flag)
|
3
|
+
lame.respond_to?(:"lame_get_#{flag}")
|
4
|
+
end
|
5
|
+
|
6
|
+
def has_setter?(lame, flag)
|
7
|
+
lame.respond_to?(:"lame_set_#{flag}")
|
8
|
+
end
|
9
|
+
|
10
|
+
def set_value(lame, flag)
|
11
|
+
lame.send(:"lame_set_#{flag}", @flags_pointer, @value) == return_value
|
12
|
+
end
|
13
|
+
|
14
|
+
def return_value
|
15
|
+
defined?(@return) ? @return : 0
|
16
|
+
end
|
17
|
+
|
18
|
+
def has_value?(lame, flag)
|
19
|
+
if @value && @value.is_a?(Float)
|
20
|
+
actual = actual_value(lame, flag)
|
21
|
+
(actual - @value).abs < 0.0001
|
22
|
+
elsif @value
|
23
|
+
actual_value(lame, flag) == @value
|
24
|
+
else
|
25
|
+
true
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def actual_value(lame, flag)
|
30
|
+
lame.send(:"lame_get_#{flag}", @flags_pointer)
|
31
|
+
end
|
32
|
+
end
|