Allow inserting mp3 without tags.
[amarok_sonynw.git] / walkgirl / mp3.rb
blob63606ffe1e90fba655dc3c630289a821b6c5bd4b
1 ############################################################################
2 #    Copyright (C) 2008 by RafaƂ Rzepecki   #
3 #    divided.mind@gmail.com   #
4 #                                                                          #
5 #    This program is free software; you can redistribute it and#or modify  #
6 #    it under the terms of the GNU General Public License as published by  #
7 #    the Free Software Foundation; either version 2 of the License, or     #
8 #    (at your option) any later version.                                   #
9 #                                                                          #
10 #    This program is distributed in the hope that it will be useful,       #
11 #    but WITHOUT ANY WARRANTY; without even the implied warranty of        #
12 #    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         #
13 #    GNU General Public License for more details.                          #
14 #                                                                          #
15 #    You should have received a copy of the GNU General Public License     #
16 #    along with this program; if not, write to the                         #
17 #    Free Software Foundation, Inc.,                                       #
18 #    59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.             #
19 ############################################################################
21 require 'id3'
22 require 'audiofile'
23 require 'nwscrambler'
25 module NetworkWalkman
26   def self.get_dvid(mount_point)
27     File.open(File.join([mount_point, "mp3fm", "dvid.dat"])) {|f|
28       f.seek(10)
29       f.read(4).unpack("N")[0]
30     }
31   end
33   class AudioFile
34     class Mp3 < AudioFile
35       MPEG_VERSION = [:v2_5, :reserved, :v2, :v1]
36       LAYER = [:reserved, :layer3, :layer2, :layer1]
37       BITRATE = [
38           # v1l1 v1l2 v1l3 v2l1 v2l*
39   [        0, 0, 0, 0, 0],
40   [        32, 32, 32, 32, 8],
41   [        64, 48, 40, 48, 16],
42   [        96, 56, 48, 56, 24],
43   [        128, 64, 56, 64, 32],
44   [        160, 80, 64, 80, 40],
45   [        192, 96, 80, 96, 48],
46   [        224, 112, 96, 112, 56],
47   [        256, 128, 112, 128, 64],
48   [        288, 160, 128, 144, 80],
49   [        320, 192, 160, 160, 96],
50   [        352, 224, 192, 176, 112],
51   [        384, 256, 224, 192, 128],
52   [        416, 320, 256, 224, 144],
53   [        448, 384, 320, 256, 160],
54   [        -1, -1, -1, -1, -1]].transpose
56       SAMPLING_RATE = { :v1 => [44100, 48000, 32000, -1],
57       :v2 => [22050, 24000, 16000, -1],
58       :v2_5 => [11025, 12000, 8000, -1] }
59       SAMPLE_PER_FRAME = { :v1 => [0, 1152, 1152, 384],
60       :v2 => [0, 576, 1152, 384],
61       :v2_5 => [0, 576, 1152, 384]}
62       def bitrates
63         if version == :v1
64           case layer
65           when :layer1:
66             BITRATE[0]
67           when :layer2
68             BITRATE[1]
69           when :layer3
70             BITRATE[2]
71           end
72         elsif layer == :layer1
73           BITRATE[3]
74         else
75           BITRATE[4]
76         end
77       end
78       attr_reader :version, :layer, :tags, :bitrate, :length, :frames, :encoding
80       def initialize(filename)
81         @tags = ID3::AudioFile.new(filename)
82         @filename = File.basename(filename)
83         @file = File.new(filename)
84         @file.seek(@tags.audioStartX)
86         # find first frame
87         loop do
88           loop do
89             if @file.readchar == 0xFF
90               @tags.audioStartX = @file.pos
91               if (byte = @file.readchar) & 0xE0 == 0xE0
92                 @version = MPEG_VERSION[(byte & 0x18) >> 3]
93                 @layer = LAYER[(byte & 0x06) >> 1]
94                 @has_crc = (byte & 1) == 0
95                 @encoding = (byte & 0x1e) << 3
96                 break
97               end
98             end
99           end
101           byte = @file.readchar
102           @bitrate = bitrates[(byte & 0xF0) >> 4]
103           if @bitrate == 0
104             next
105           end
106           @sampling_rate = SAMPLING_RATE[version][(byte & 0x0C) >> 2]
107           @is_padded = (byte & 2) == 2
108           @length = (tags.audioEndX - tags.audioStartX) * 8 / bitrate
109           @sample_per_frame = SAMPLE_PER_FRAME[version][LAYER.index(layer)]
110           @frames = @length * @sampling_rate / 1000 / @sample_per_frame
111           @framelen = bitrate * 1000 * @sample_per_frame / 8 / @sampling_rate 
112           @encoding |= (byte & 0xF0) >> 4
113           if @has_crc
114             @crc = @file.read(2).unpack("n")
115           end
116           if @sampling_rate > 0
117             break
118           end
119         end
120         @audioStartX, @audioEndX = tags.audioStartX, tags.audioEndX
121         mtags = tags.tagID3v2 || tags.tagID3v1
122         @tags = ID3::Tag2.new
123         mtags ||= ID3::Tag2.new
124         mtags.each{|t,v|
125           begin
126             if v.class == ID3::Frame
127               @tags[t] = v
128             else
129               @tags[t] = "\3#{v}"
130             end
131           rescue
132           end
133         }
134         @tags["ORIGFILENAME"] = "\3#{@filename}"
135         self
136       end
138       def putOnDevice(mount_point)
139         @dvid = NetworkWalkman.get_dvid(mount_point)
140         @slot = AudioFile.freeSlot(mount_point)
141         @filename = AudioFile.filename_for_slot(@slot, mount_point)
142         save
143       end
145 #       def scramble(outfile)
146 #         key = 0xFFFFFFFF & (( 0x2465 + @slot * 0x5296E435 ) ^ @dvid);
147 #         @file.seek(@audioStartX)
148 #         left = @audioEndX - @audioStartX
149 #         while left > 0 and block = @file.read([8, left].min)
150 #           if block.length != 8
151 #             outfile << block
152 #           else
153 #             outfile << block.unpack("N2").map{|x|x^key}.pack("N2")
154 #           end
155 #           left -= block.length
156 #         end
157 #       end
160     end # class Mp3
161   end