wahwah 0.1.0.pre.test → 0.1.0

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