wahwah 0.1.0.pre.test → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +8 -0
  3. data/.travis.yml +5 -0
  4. data/Gemfile +6 -0
  5. data/Gemfile.lock +22 -0
  6. data/README.md +35 -0
  7. data/Rakefile +10 -0
  8. data/bin/console +14 -0
  9. data/bin/setup +8 -0
  10. data/lib/wahwah.rb +3 -74
  11. data/lib/wahwah/version.rb +2 -4
  12. data/wahwah.gemspec +27 -0
  13. metadata +25 -96
  14. data/LICENSE +0 -21
  15. data/lib/wahwah/asf/object.rb +0 -39
  16. data/lib/wahwah/asf_tag.rb +0 -220
  17. data/lib/wahwah/errors.rb +0 -6
  18. data/lib/wahwah/flac/block.rb +0 -57
  19. data/lib/wahwah/flac/streaminfo_block.rb +0 -51
  20. data/lib/wahwah/flac_tag.rb +0 -84
  21. data/lib/wahwah/helper.rb +0 -37
  22. data/lib/wahwah/id3/comment_frame_body.rb +0 -21
  23. data/lib/wahwah/id3/frame.rb +0 -180
  24. data/lib/wahwah/id3/frame_body.rb +0 -36
  25. data/lib/wahwah/id3/genre_frame_body.rb +0 -15
  26. data/lib/wahwah/id3/image_frame_body.rb +0 -60
  27. data/lib/wahwah/id3/text_frame_body.rb +0 -16
  28. data/lib/wahwah/id3/v1.rb +0 -96
  29. data/lib/wahwah/id3/v2.rb +0 -60
  30. data/lib/wahwah/id3/v2_header.rb +0 -53
  31. data/lib/wahwah/mp3/mpeg_frame_header.rb +0 -141
  32. data/lib/wahwah/mp3/vbri_header.rb +0 -47
  33. data/lib/wahwah/mp3/xing_header.rb +0 -45
  34. data/lib/wahwah/mp3_tag.rb +0 -110
  35. data/lib/wahwah/mp4/atom.rb +0 -105
  36. data/lib/wahwah/mp4_tag.rb +0 -126
  37. data/lib/wahwah/ogg/flac_tag.rb +0 -37
  38. data/lib/wahwah/ogg/opus_tag.rb +0 -33
  39. data/lib/wahwah/ogg/packets.rb +0 -41
  40. data/lib/wahwah/ogg/page.rb +0 -121
  41. data/lib/wahwah/ogg/pages.rb +0 -24
  42. data/lib/wahwah/ogg/vorbis_comment.rb +0 -51
  43. data/lib/wahwah/ogg/vorbis_tag.rb +0 -35
  44. data/lib/wahwah/ogg_tag.rb +0 -66
  45. data/lib/wahwah/riff/chunk.rb +0 -54
  46. data/lib/wahwah/riff_tag.rb +0 -140
  47. data/lib/wahwah/tag.rb +0 -59
  48. data/lib/wahwah/tag_delegate.rb +0 -16
data/lib/wahwah/id3/v2.rb DELETED
@@ -1,60 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'forwardable'
4
-
5
- module WahWah
6
- module ID3
7
- class V2 < Tag
8
- extend Forwardable
9
-
10
- def_delegators :@header, :major_version, :size, :has_extended_header?, :valid?
11
-
12
- def version
13
- "v2.#{major_version}"
14
- end
15
-
16
- private
17
- def parse
18
- @file_io.rewind
19
- @header = V2Header.new(@file_io)
20
-
21
- return unless valid?
22
-
23
- until end_of_tag? do
24
- frame = ID3::Frame.new(@file_io, major_version)
25
- next unless frame.valid?
26
-
27
- update_attribute(frame)
28
- end
29
- end
30
-
31
- def update_attribute(frame)
32
- name = frame.name
33
- value = frame.value
34
-
35
- case name
36
- when :comment
37
- # Because there may be more than one comment frame in each tag,
38
- # so push it into a array.
39
- @comments.push(value)
40
- when :image
41
- # Because there may be more than one image frame in each tag,
42
- # so push it into a array.
43
- @images.push(value)
44
- when :track, :disc
45
- # Track and disc value may be extended with a "/" character
46
- # and a numeric string containing the total numer.
47
- count, total_count = value.split('/', 2)
48
- instance_variable_set("@#{name}", count)
49
- instance_variable_set("@#{name}_total", total_count) unless total_count.nil?
50
- else
51
- instance_variable_set("@#{name}", value)
52
- end
53
- end
54
-
55
- def end_of_tag?
56
- size <= @file_io.pos || file_size <= @file_io.pos
57
- end
58
- end
59
- end
60
- end
@@ -1,53 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module WahWah
4
- module ID3
5
- # The ID3v2 tag header, which should be the first information in the file,
6
- # is 10 bytes as follows:
7
-
8
- # ID3v2/file identifier "ID3"
9
- # ID3v2 version $03 00
10
- # ID3v2 flags %abc00000
11
- # ID3v2 size 4 * %0xxxxxxx
12
- class V2Header
13
- TAG_ID = 'ID3'
14
- HEADER_SIZE = 10
15
- HEADER_FORMAT = 'A3CxB8B*'
16
-
17
- attr_reader :major_version, :size
18
-
19
- def initialize(file_io)
20
- header_content = file_io.read(HEADER_SIZE)
21
- @id, @major_version, @flags, size_bits = header_content.unpack(HEADER_FORMAT) if header_content.size >= HEADER_SIZE
22
-
23
- return unless valid?
24
-
25
- # Tag size is the size excluding the header size,
26
- # so add header size back to get total size.
27
- @size = Helper.id3_size_caculate(size_bits) + HEADER_SIZE
28
-
29
- if has_extended_header?
30
- # Extended header structure:
31
- #
32
- # Extended header size $xx xx xx xx
33
- # Extended Flags $xx xx
34
- # Size of padding $xx xx xx xx
35
-
36
- # Skip extended_header
37
- extended_header_size = Helper.id3_size_caculate(file_io.read(4).unpack('B32').first)
38
- file_io.seek(extended_header_size - 4, IO::SEEK_CUR)
39
- end
40
- end
41
-
42
- def valid?
43
- @id == TAG_ID
44
- end
45
-
46
- # The second bit in flags byte indicates whether or not the header
47
- # is followed by an extended header.
48
- def has_extended_header?
49
- @flags[1] == '1'
50
- end
51
- end
52
- end
53
- end
@@ -1,141 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module WahWah
4
- module Mp3
5
- # mpeg frame header structure:
6
- #
7
- # Position Length Meaning
8
- # 0 11 Frame sync to find the header (all bits are always set)
9
- #
10
- # 11 2 Audio version ID
11
- # 00 - MPEG Version 2.5 (unofficial extension of MPEG 2)
12
- # 01 - reserved
13
- # 10 - MPEG Version 2 (ISO/IEC 13818-3)
14
- # 11 - MPEG Version 1 (ISO/IEC 11172-3)
15
- #
16
- # 13 2 Layer index
17
- # 00 - reserved
18
- # 01 - Layer III
19
- # 10 - Layer II
20
- # 11 - Layer I
21
- #
22
- # 15 1 Protection bit
23
- #
24
- # 16 4 Bitrate index, see FRAME_BITRATE_INDEX constant
25
- #
26
- # 20 2 Sampling rate index, see SAMPLE_RATE_INDEX constant
27
- #
28
- # 22 1 Padding bit
29
- #
30
- # 23 1 Private bit
31
- #
32
- # 24 2 Channel mode
33
- # 00 - Stereo
34
- # 01 - Joint Stereo (Stereo)
35
- # 10 - Dual channel (Two mono channels)
36
- # 11 - Single channel (Mono)
37
- #
38
- # 26 2 Mode extension (Only used in Joint Stereo)
39
- #
40
- # 28 1 Copyright bit (only informative)
41
- #
42
- # 29 1 Original bit (only informative)
43
- #
44
- # 30 2 Emphasis
45
- class MpegFrameHeader
46
- HEADER_SIZE = 4
47
-
48
- FRAME_BITRATE_INDEX = {
49
- 'MPEG1 layer1' => [0, 32, 64, 96, 128, 160, 192, 224, 256, 288, 320, 352, 384, 416, 448, 0],
50
- 'MPEG1 layer2' => [0, 32, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 384, 0],
51
- 'MPEG1 layer3' => [0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160, 192, 224, 256, 320, 0],
52
-
53
- 'MPEG2 layer1' => [0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0],
54
- 'MPEG2 layer2' => [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],
55
- 'MPEG2 layer3' => [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],
56
-
57
- 'MPEG2.5 layer1' => [0, 32, 48, 56, 64, 80, 96, 112, 128, 144, 160, 176, 192, 224, 256, 0],
58
- 'MPEG2.5 layer2' => [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0],
59
- 'MPEG2.5 layer3' => [0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96, 112, 128, 144, 160, 0]
60
- }
61
-
62
- VERSIONS_INDEX = ['MPEG2.5', nil, 'MPEG2', 'MPEG1']
63
- LAYER_INDEX = [nil, 'layer3', 'layer2', 'layer1']
64
- CHANNEL_MODE_INDEX = ['Stereo', 'Joint Stereo', 'Dual Channel', 'Single Channel']
65
-
66
- SAMPLE_RATE_INDEX = {
67
- 'MPEG1' => [44100, 48000, 32000],
68
- 'MPEG2' => [22050, 24000, 16000],
69
- 'MPEG2.5' => [11025, 12000, 8000]
70
- }
71
-
72
- SAMPLES_PER_FRAME_INDEX = {
73
- 'MPEG1 layer1' => 384,
74
- 'MPEG1 layer2' => 1152,
75
- 'MPEG1 layer3' => 1152,
76
-
77
- 'MPEG2 layer1' => 384,
78
- 'MPEG2 layer2' => 1152,
79
- 'MPEG2 layer3' => 576,
80
-
81
- 'MPEG2.5 layer1' => 384,
82
- 'MPEG2.5 layer2' => 1152,
83
- 'MPEG2.5 layer3' => 576
84
- }
85
-
86
- attr_reader :version, :layer, :frame_bitrate, :channel_mode, :sample_rate
87
-
88
- def initialize(file_io, offset = 0)
89
- # mpeg frame header start with '11111111111' sync bits,
90
- # So look through file until find it.
91
- loop do
92
- file_io.rewind
93
- file_io.seek(offset)
94
-
95
- break if file_io.eof?
96
-
97
- header = file_io.read(HEADER_SIZE)
98
- sync_bits = header.unpack('B11').first
99
-
100
- if sync_bits == "#{'1' * 11}".b
101
- @header = header.unpack('B*').first
102
- @position = offset
103
-
104
- parse; break
105
- end
106
-
107
- offset += 1
108
- end
109
- end
110
-
111
- def valid?
112
- !@header.nil?
113
- end
114
-
115
- def position
116
- return 0 unless valid?
117
- @position
118
- end
119
-
120
- def kind
121
- return if @version.nil? && @layer.nil?
122
- "#{@version} #{@layer}"
123
- end
124
-
125
- def samples_per_frame
126
- SAMPLES_PER_FRAME_INDEX[kind]
127
- end
128
-
129
- private
130
- def parse
131
- return unless valid?
132
-
133
- @version = VERSIONS_INDEX[@header[11..12].to_i(2)]
134
- @layer = LAYER_INDEX[@header[13..14].to_i(2)]
135
- @frame_bitrate = FRAME_BITRATE_INDEX[kind]&.fetch(@header[16..19].to_i(2))
136
- @channel_mode = CHANNEL_MODE_INDEX[@header[24..25].to_i(2)]
137
- @sample_rate = SAMPLE_RATE_INDEX[@version]&.fetch(@header[20..21].to_i(2))
138
- end
139
- end
140
- end
141
- end
@@ -1,47 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module WahWah
4
- module Mp3
5
- # VBRI header structure:
6
- #
7
- # Position Length Meaning
8
- # 0 4 VBR header ID in 4 ASCII chars, always 'VBRI', not NULL-terminated
9
- #
10
- # 4 2 Version ID as Big-Endian 16-bit unsigned
11
- #
12
- # 6 2 Delay as Big-Endian float
13
- #
14
- # 8 2 Quality indicator
15
- #
16
- # 10 4 Number of Bytes as Big-Endian 32-bit unsigned
17
- #
18
- # 14 4 Number of Frames as Big-Endian 32-bit unsigned
19
- #
20
- # 18 2 Number of entries within TOC table as Big-Endian 16-bit unsigned
21
- #
22
- # 20 2 Scale factor of TOC table entries as Big-Endian 32-bit unsigned
23
- #
24
- # 22 2 Size per table entry in bytes (max 4) as Big-Endian 16-bit unsigned
25
- #
26
- # 24 2 Frames per table entry as Big-Endian 16-bit unsigned
27
- #
28
- # 26 TOC entries for seeking as Big-Endian integral.
29
- # From size per table entry and number of entries,
30
- # you can calculate the length of this field.
31
- class VbriHeader
32
- HEADER_SIZE = 32
33
- HEADER_FORMAT = 'A4x6NN'
34
-
35
- attr_reader :frames_count, :bytes_count
36
-
37
- def initialize(file_io, offset = 0)
38
- file_io.seek(offset)
39
- @id, @bytes_count, @frames_count = file_io.read(HEADER_SIZE)&.unpack(HEADER_FORMAT)
40
- end
41
-
42
- def valid?
43
- @id == 'VBRI'
44
- end
45
- end
46
- end
47
- end
@@ -1,45 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- module WahWah
4
- module Mp3
5
- # Xing header structure:
6
- #
7
- # Position Length Meaning
8
- # 0 4 VBR header ID in 4 ASCII chars, either 'Xing' or 'Info',
9
- # not NULL-terminated
10
- #
11
- # 4 4 Flags which indicate what fields are present,
12
- # flags are combined with a logical OR. Field is mandatory.
13
- #
14
- # 0x0001 - Frames field is present
15
- # 0x0002 - Bytes field is present
16
- # 0x0004 - TOC field is present
17
- # 0x0008 - Quality indicator field is present
18
- #
19
- # 8 4 Number of Frames as Big-Endian 32-bit unsigned (optional)
20
- #
21
- # 8 or 12 4 Number of Bytes in file as Big-Endian 32-bit unsigned (optional)
22
- #
23
- # 8,12 or 16 100 100 TOC entries for seeking as integral BYTE (optional)
24
- #
25
- # 8,12,16,108,112 or 116 4 Quality indicator as Big-Endian 32-bit unsigned
26
- # from 0 - best quality to 100 - worst quality (optional)
27
- class XingHeader
28
- attr_reader :frames_count, :bytes_count
29
-
30
- def initialize(file_io, offset = 0)
31
- file_io.seek(offset)
32
-
33
- @id, @flags = file_io.read(8)&.unpack('A4N')
34
- return unless valid?
35
-
36
- @frames_count = @flags & 1 == 1 ? file_io.read(4).unpack('N').first : 0
37
- @bytes_count = @flags & 2 == 2 ? file_io.read(4).unpack('N').first : 0
38
- end
39
-
40
- def valid?
41
- %w(Xing Info).include? @id
42
- end
43
- end
44
- end
45
- end
@@ -1,110 +0,0 @@
1
- # frozen_string_literal: true
2
-
3
- require 'forwardable'
4
-
5
- module WahWah
6
- class Mp3Tag < Tag
7
- extend TagDelegate
8
- extend Forwardable
9
-
10
- def_delegator :@mpeg_frame_header, :version, :mpeg_version
11
- def_delegator :@mpeg_frame_header, :layer, :mpeg_layer
12
- def_delegator :@mpeg_frame_header, :kind, :mpeg_kind
13
- def_delegators :@mpeg_frame_header, :channel_mode, :sample_rate
14
-
15
- tag_delegate :@id3_tag,
16
- :title,
17
- :artist,
18
- :album,
19
- :albumartist,
20
- :composer,
21
- :comments,
22
- :track,
23
- :track_total,
24
- :genre,
25
- :year,
26
- :disc,
27
- :disc_total,
28
- :images
29
-
30
- def id3v2?
31
- @id3_tag.instance_of? ID3::V2
32
- end
33
-
34
- def invalid_id3?
35
- @id3_tag.nil?
36
- end
37
-
38
- def id3_version
39
- @id3_tag&.version
40
- end
41
-
42
- def is_vbr?
43
- xing_header.valid? || vbri_header.valid?
44
- end
45
-
46
- private
47
- def parse
48
- @id3_tag = parse_id3_tag
49
- parse_duration if mpeg_frame_header.valid?
50
- end
51
-
52
- def parse_id3_tag
53
- id3_v1_tag = ID3::V1.new(@file_io)
54
- id3_v2_tag = ID3::V2.new(@file_io)
55
-
56
- return id3_v2_tag if id3_v2_tag.valid?
57
- id3_v1_tag if id3_v1_tag.valid?
58
- end
59
-
60
- def parse_duration
61
- if is_vbr?
62
- @duration = (frames_count * (mpeg_frame_header.samples_per_frame / sample_rate.to_f)).round
63
- @bitrate = bytes_count * 8 / @duration / 1000 unless @duration.zero?
64
- else
65
- @bitrate = mpeg_frame_header.frame_bitrate
66
- @duration = (file_size - (@id3_tag&.size || 0)) * 8 / (@bitrate * 1000) unless @bitrate.zero?
67
- end
68
- end
69
-
70
- def mpeg_frame_header
71
- # Because id3v2 tag on the file header so skip id3v2 tag
72
- @mpeg_frame_header ||= Mp3::MpegFrameHeader.new(@file_io, id3v2? ? @id3_tag&.size : 0)
73
- end
74
-
75
- def xing_header
76
- @xing_header ||= Mp3::XingHeader.new(@file_io, xing_header_offset)
77
- end
78
-
79
- def vbri_header
80
- @vbri_header ||= Mp3::VbriHeader.new(@file_io, vbri_header_offset)
81
- end
82
-
83
- def xing_header_offset
84
- mpeg_frame_header_position = mpeg_frame_header.position
85
- mpeg_frame_header_size = Mp3::MpegFrameHeader::HEADER_SIZE
86
- mpeg_frame_side_info_size = mpeg_version == 'MPEG1' ?
87
- (channel_mode == 'Single Channel' ? 17 : 32) :
88
- (channel_mode == 'Single Channel' ? 9 : 17)
89
-
90
- mpeg_frame_header_position + mpeg_frame_header_size + mpeg_frame_side_info_size
91
- end
92
-
93
- def vbri_header_offset
94
- mpeg_frame_header_position = mpeg_frame_header.position
95
- mpeg_frame_header_size = Mp3::MpegFrameHeader::HEADER_SIZE
96
-
97
- mpeg_frame_header_position + mpeg_frame_header_size + 32
98
- end
99
-
100
- def frames_count
101
- return xing_header.frames_count if xing_header.valid?
102
- vbri_header.frames_count if vbri_header.valid?
103
- end
104
-
105
- def bytes_count
106
- return xing_header.bytes_count if xing_header.valid?
107
- vbri_header.bytes_count if vbri_header.valid?
108
- end
109
- end
110
- end