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,46 @@
1
+ module LAME
2
+ module Decoding
3
+ class MPEGAudioFrameFinder
4
+
5
+ MPEG_HEADER_SIZE = 4
6
+
7
+ def initialize(stream)
8
+ @stream = stream
9
+ end
10
+
11
+ def find!
12
+ begin
13
+ @data = @stream.read(MPEG_HEADER_SIZE)
14
+ if MPEGAudioFrameMatcher.new(@data).match?
15
+ seek_back!
16
+ return
17
+ end
18
+ seek!
19
+ end until end_of_stream?
20
+
21
+ raise MPEGAudioFrameNotFoundError
22
+ end
23
+
24
+ private
25
+
26
+ def offset
27
+ @stream.pos
28
+ end
29
+
30
+ # Move to next 4 bytes.
31
+ def seek!
32
+ @stream.seek(offset - (MPEG_HEADER_SIZE-1))
33
+ end
34
+
35
+ # Move back to frame position.
36
+ def seek_back!
37
+ @stream.seek(offset - (MPEG_HEADER_SIZE))
38
+ end
39
+
40
+ def end_of_stream?
41
+ !@data || (@data.size != MPEG_HEADER_SIZE)
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,98 @@
1
+ module LAME
2
+ module Decoding
3
+
4
+ # Specification is here:
5
+ # http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header
6
+ #
7
+ # Also see `is_syncword_mp123` in lame-3.99.5/frontend/get_audio.c
8
+ #
9
+ # Get ready for some bit-matching magic:
10
+ class MPEGAudioFrameMatcher
11
+
12
+ attr_reader :bytes
13
+
14
+ CHANNEL_MODES = [:stereo, :joint_stereo, :dual_channel, :mono]
15
+
16
+ def initialize(bytes)
17
+ @bytes = bytes.unpack("C*")
18
+ end
19
+
20
+ def match?
21
+ leading_bits? &&
22
+ mpeg? &&
23
+ layer? &&
24
+ bitrate? &&
25
+ sample_frequency? &&
26
+ valid_mpeg1_layer2? &&
27
+ emphasis?
28
+ end
29
+
30
+ private
31
+
32
+ # First eleven bits (all 1)
33
+ def leading_bits?
34
+ (bytes[0] & 0b11111111) == 0b11111111 &&
35
+ (bytes[1] & 0b11100000) == 0b11100000
36
+ end
37
+
38
+ # 4th and 5th bit in second byte (01 is reserved)
39
+ def mpeg?
40
+ (bytes[1] & 0b11000) != 0b01000
41
+ end
42
+
43
+ # 6th and 7th bit in second byte (00 is reserved)
44
+ def layer?
45
+ (bytes[1] & 0b110) != 0b000
46
+ end
47
+
48
+ # first 4 bits in third byte (all 1's is reserved)
49
+ def bitrate?
50
+ (bytes[2] & 0b11110000) != 0b11110000
51
+ end
52
+
53
+ # 5th and 6th bit in third byte (all 1's is reserved)
54
+ def sample_frequency?
55
+ (bytes[2] & 0b1100) != 0b1100
56
+ end
57
+
58
+ # http://www.codeproject.com/Articles/8295/MPEG-Audio-Frame-Header#Combinations
59
+ # Only certain combination are valid for MPEG1 Layer II:
60
+ def valid_mpeg1_layer2?
61
+ return true if !mpeg1?
62
+ return true if !layer2?
63
+
64
+ bitrate_index = (bytes[2] >> 4)
65
+
66
+ case bitrate_index
67
+ when 1,2,3,5
68
+ channel_mode == :mono
69
+ when 11,12,13,14
70
+ [:stereo, :joint_stereo, :dual_channel].include?(channel_mode)
71
+ else
72
+ true
73
+ end
74
+ end
75
+
76
+ # 7th and 8th bit in fourth byte (10 is reserved)
77
+ def emphasis?
78
+ (bytes[3] & 0b11) != 0b10
79
+ end
80
+
81
+ # MPEG1 = 11
82
+ def mpeg1?
83
+ (bytes[1] & 0b11000) == 0b11000
84
+ end
85
+
86
+ # Layer II = 10
87
+ def layer2?
88
+ (bytes[1] & 0b110) == 0b100
89
+ end
90
+
91
+ # 1st and 2nd bit of fourth byte
92
+ def channel_mode
93
+ @channel_mode ||= CHANNEL_MODES[bytes[3] >> 6]
94
+ end
95
+
96
+ end
97
+ end
98
+ end
@@ -0,0 +1,51 @@
1
+ module LAME
2
+ module Decoding
3
+ class SingleFrameDecoder
4
+
5
+ def initialize(decode_flags, mp3_data)
6
+ @decode_flags = decode_flags
7
+ @mp3_data = mp3_data
8
+ end
9
+
10
+ def decode(data)
11
+ flush_buffer do |decoded_frame|
12
+ yield decoded_frame
13
+ end
14
+ decode_data(data) do |decoded_frame|
15
+ yield decoded_frame
16
+ end
17
+ end
18
+
19
+ private
20
+
21
+ def flush_buffer(&block)
22
+ in_buffer = LAME::Buffer.create_empty(:uchar, 0)
23
+ decode_buffer(in_buffer, &block)
24
+ end
25
+
26
+ def decode_data(data, &block)
27
+ in_buffer = LAME::Buffer.create_uchar(data)
28
+ decode_buffer(in_buffer, &block)
29
+ end
30
+
31
+ def decode_buffer(in_buffer, &block)
32
+ out_left, out_right = output_buffers
33
+ result = LAME.hip_decode1_headers(@decode_flags, in_buffer, in_buffer.size, out_left, out_right, @mp3_data)
34
+
35
+ if result > 0
36
+ yield DecodedFrame.from_short_buffers(out_left, out_right)
37
+ flush_buffer(&block)
38
+ end
39
+
40
+ raise DecodingError if result < 0
41
+ end
42
+
43
+ def output_buffers
44
+ out_left = LAME::Buffer.create_empty(:short, 1152)
45
+ out_right = LAME::Buffer.create_empty(:short, 1152)
46
+ [out_left, out_right]
47
+ end
48
+
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,37 @@
1
+ module LAME
2
+ module Decoding
3
+ class StreamDecoder
4
+
5
+ # arbitrary, taken from get_audio.c#lame_decode_fromfile
6
+ DECODE_SIZE = 1024
7
+
8
+ def initialize(decode_flags, mp3_data, stream)
9
+ @stream = stream
10
+ @single_frame_decoder = SingleFrameDecoder.new(decode_flags, mp3_data)
11
+ end
12
+
13
+ def each_decoded_frame(&block)
14
+ begin
15
+ @data = @stream.read(DECODE_SIZE)
16
+
17
+ decode &block
18
+ end until end_of_stream?
19
+ end
20
+
21
+ private
22
+
23
+ def decode
24
+ return if end_of_stream?
25
+
26
+ @single_frame_decoder.decode(@data) do |decoded_frame|
27
+ yield decoded_frame
28
+ end
29
+ end
30
+
31
+ def end_of_stream?
32
+ !@data
33
+ end
34
+
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,68 @@
1
+ module LAME
2
+ module Delegation
3
+
4
+ def delegate_to_lame(*delegations)
5
+ delegations.each do |flag|
6
+ define_setter_delegator(flag, flag)
7
+ define_getter_delegator(flag, flag)
8
+ end
9
+ end
10
+
11
+ def delegate_alias_to_lame(delegations)
12
+ delegations.each_pair do |from, to|
13
+ define_setter_delegator(from, to)
14
+ define_getter_delegator(from, to)
15
+ end
16
+ end
17
+
18
+ def delegate_id3_to_lame(*delegations)
19
+ delegations.each do |flag|
20
+ define_setter_delegator(flag, flag, "id3tag")
21
+ end
22
+ end
23
+
24
+ private
25
+
26
+ def define_setter_delegator(from, to, preset = "lame")
27
+ define_method(:"#{from}=") do |value|
28
+ value = TypeConvertor.convert(value)
29
+ LAME.send(:"#{preset}_set_#{to}", global_flags, value)
30
+ end
31
+ end
32
+
33
+ def define_getter_delegator(from, to, preset = "lame")
34
+ define_method(:"#{from}") do
35
+ LAME.send(:"#{preset}_get_#{to}", global_flags)
36
+ end
37
+ define_method(:"#{from}?") do
38
+ return_value = send(:"#{from}")
39
+ TypeConvertor.convert_return(return_value)
40
+ end
41
+ end
42
+
43
+ class TypeConvertor
44
+ def self.convert(value)
45
+ case value
46
+ when true
47
+ 1
48
+ when false
49
+ 0
50
+ when String
51
+ ::FFI::MemoryPointer.from_string(value)
52
+ else
53
+ value
54
+ end
55
+ end
56
+
57
+ def self.convert_return(value)
58
+ case value
59
+ when 0
60
+ false
61
+ else
62
+ !!value
63
+ end
64
+ end
65
+ end
66
+
67
+ end
68
+ end
@@ -0,0 +1,73 @@
1
+ module LAME
2
+ class Encoder
3
+
4
+ attr_reader :global_flags
5
+
6
+ def initialize
7
+ @global_flags = FFI::GlobalFlags.new
8
+ end
9
+
10
+ def configure
11
+ yield configuration
12
+ apply_configuration
13
+ end
14
+
15
+ def encode_short(left, right)
16
+ apply_configuration
17
+
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
22
+ end
23
+
24
+ def flush(&block)
25
+ mp3_data = Encoding::Flusher.new(configuration).flush
26
+ yield_or_return(mp3_data, &block)
27
+ end
28
+
29
+ def vbr_frame(&block)
30
+ mp3_data = Encoding::VBRInfo.new(configuration).frame
31
+ yield_or_return(mp3_data, &block)
32
+ end
33
+
34
+ def id3v1(&block)
35
+ mp3_data = Encoding::Id3.new(configuration).v1
36
+ yield_or_return(mp3_data, &block)
37
+ end
38
+
39
+ def id3v2(&block)
40
+ mp3_data = Encoding::Id3.new(configuration).v2
41
+ yield_or_return(mp3_data, &block)
42
+ end
43
+
44
+ def configuration
45
+ @configuration ||= Configuration.new(global_flags)
46
+ end
47
+
48
+ def framesize
49
+ configuration.framesize
50
+ end
51
+
52
+ private
53
+
54
+ def apply_configuration
55
+ configuration.apply! unless configuration.applied?
56
+ end
57
+
58
+ def each_frame(left, right)
59
+ left.zip(right).each_slice(framesize) do |slice|
60
+ yield slice.transpose
61
+ end
62
+ end
63
+
64
+ def yield_or_return(value)
65
+ if block_given?
66
+ yield value
67
+ else
68
+ value
69
+ end
70
+ end
71
+
72
+ end
73
+ end
@@ -0,0 +1,26 @@
1
+ module LAME
2
+ module Encoding
3
+ class EncodeShortBuffer
4
+ extend Forwardable
5
+
6
+ def_delegators :@configuration, :global_flags, :framesize, :output_buffer_size
7
+
8
+ def initialize(configuration)
9
+ @configuration = configuration
10
+ end
11
+
12
+ def encode_frame(left, right)
13
+ left_buffer = Buffer.create(:short, left)
14
+ right_buffer = Buffer.create(:short, right)
15
+ output = Buffer.create_empty(:uchar, output_buffer_size)
16
+
17
+ mp3_size = LAME.lame_encode_buffer(global_flags,
18
+ left_buffer, right_buffer, left.size,
19
+ output, output_buffer_size)
20
+
21
+ output.get_bytes(0, mp3_size)
22
+ end
23
+
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,22 @@
1
+ module LAME
2
+ module Encoding
3
+ class Flusher
4
+ extend Forwardable
5
+
6
+ def_delegators :@configuration, :global_flags, :framesize, :output_buffer_size
7
+
8
+ def initialize(configuration)
9
+ @configuration = configuration
10
+ end
11
+
12
+ def flush
13
+ output = Buffer.create_empty(:uchar, output_buffer_size)
14
+
15
+ mp3_size = LAME.lame_encode_flush(global_flags, output, output_buffer_size)
16
+
17
+ output.get_bytes(0, mp3_size)
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,46 @@
1
+ module LAME
2
+ module Encoding
3
+ class Id3
4
+ extend Forwardable
5
+
6
+ def_delegators :@configuration, :global_flags, :framesize, :output_buffer_size
7
+
8
+ def initialize(configuration)
9
+ @configuration = configuration
10
+ end
11
+
12
+ def v1
13
+ tag_size = v1_tag_size
14
+ output = output_buffer(tag_size)
15
+ LAME.lame_get_id3v1_tag(global_flags, output, tag_size)
16
+ output.get_bytes(0, tag_size)
17
+ end
18
+
19
+ def v2
20
+ tag_size = v2_tag_size
21
+ output = output_buffer(tag_size)
22
+ LAME.lame_get_id3v2_tag(global_flags, output, tag_size)
23
+ output.get_bytes(0, tag_size)
24
+ end
25
+
26
+ private
27
+
28
+ def output_buffer(size)
29
+ Buffer.create_empty(:uchar, size)
30
+ end
31
+
32
+ # LAME returns the required buffer size if the input buffer is too small.
33
+ def v1_tag_size
34
+ small_buffer = output_buffer(0)
35
+ LAME.lame_get_id3v1_tag(global_flags, small_buffer, 0)
36
+ end
37
+
38
+ # LAME returns the required buffer size if the input buffer is too small.
39
+ def v2_tag_size
40
+ small_buffer = output_buffer(0)
41
+ LAME.lame_get_id3v2_tag(global_flags, small_buffer, 0)
42
+ end
43
+
44
+ end
45
+ end
46
+ end