View in color | License | Download script | History | Other scripts by: oldes |
7-Sep 18:38 UTC
[0.067] 18.204k
[0.067] 18.204k
mp3-stream-parser.rREBOL [
Title: "MP3 stream parser"
Name: "mp3-stream-parser"
Date: 1-Apr-2009/21:10:30+2:00
File: %mp3-stream-parser.r
Version: 1.0.0
Home: http://box.lebeda.ws/~hmm/rebol/mp3.html
Author: "Oldes"
Owner: none
Rights: none
Needs: none
Tabs: none
Usage: [
;to get just a part of MP3 file:
mp3/open http://box.lebeda.ws/~hmm/mp3/Electric_Bazar_Cie-America.mp3
write/binary %test.mp3 mp3/get-sample-of-length 0:0:4.5
;you can continue to read other samples here...
;...and close the stream if you don't need it
mp3/close
;to go thru complete file and parse it:
mp3/parse/file %test.mp3
print [
"Frames total:" mp3/num_frames
"^/Duration: " to-time mp3/duration
]
]
Purpose: {MP3 parser with streaming input}
Comment: none
History: none
Language: none
Type: none
Content: none
Email: %oliva--david--seznam--cz
library: [
level: [intermediate advanced]
type: 'tool
domain: 'file-handling
platform: 'all
tested-under: none
support: none
license: 'public-domain
see-also: none
]
]
;### MP3
MP3: context [
inBuffer: outBuffer:
MP3-port: ID3v2: ID3v1: frame:
MP3FrameHeader:
continue?: none
num_frames: duration: 0
headers: make hash! [] ;used to cache parsed mp3 frame headers
;## Functions used to read data from input stream
seekToBuffer: func[bytes /local new][
either any [
none? inBuffer
tail? inBuffer
][
inBuffer: copy/part MP3-port bytes
;print "seekToBuffer <-complete"
][
inBuffer: remove/part head inBuffer (-1 + index? inBuffer)
if all [
0 < bytes: (bytes - length? inBuffer)
; bytes > 8192
][
if new: copy/part MP3-port bytes [
insert tail inBuffer new
;print ["seekToBuffer <-only" bytes]
]
]
]
inBuffer
]
checkBufferSize: func[bytes][
if bytes > length? inBuffer [
;print "Refilling inBuffer"
seekToBuffer bytes
]
bytes
]
skipBytes: func[nbytes][inBuffer: skip inBuffer nbytes]
readBytes: func[nbytes][
copy/part inBuffer
inBuffer: skip inBuffer nbytes
]
readBytesRev: func[nbytes][
head reverse
copy/part inBuffer
inBuffer: skip inBuffer nbytes
]
readUI8: has[i][i: first inBuffer inBuffer: next inBuffer i]
readUI16: does[to integer! readBytesRev 2]
readUI32: does[to integer! readBytesRev 4]
readSynchSafeInt: does [
((readUI8 and 127) * 2097152)+
((readUI8 and 127) * 16384)+
((readUI8 and 127) * 128)+
( readUI8 and 127)
]
readID3v1: does [
checkBufferSize 128
ID3v1: context [
Type: 1
Title: as-string trim/with readBytes 30 #"^(00)"
Artist: as-string trim/with readBytes 30 #"^(00)"
Album: as-string trim/with readBytes 30 #"^(00)"
Year: to-integer as-string readBytes 4
Comment: as-string trim/with readBytes 30 #"^(00)"
Gendre: readUI8
]
]
readID3v2: does [
;http://www.id3.org/id3v2.4.0-structure
ID3v2: context [
Type: 2
Version: readUI16
Flags: readUI8
Data: readBytes checkBufferSize readSynchSafeInt ;<-not parsed yet
]
]
readMP3FrameHeader: has[hdrb hdr ][
hdrb: next copy/part inBuffer 4
unless MP3FrameHeader: select headers hdrb [
hdr: to integer! hdrb
if 14680064 = (14680064 and hdr) [
MP3FrameHeader: context [
MpegVersion: shift (1572864 and hdr) 19
Layer: shift (393216 and hdr) 17
;Protected?: shift (65536 and hdr) 16
Bitrate: pick (switch layer either MpegVersion = 3 [[
3 [[32 64 96 128 160 192 224 256 288 320 352 384 416 448]]
2 [[32 48 56 64 80 96 112 128 160 192 224 256 320 384]]
1 [[32 40 48 56 64 80 96 112 128 160 192 224 256 320]]
]][[
3 [[32 48 56 64 80 96 112 128 144 160 176 192 224 256]]
2 [[ 8 16 24 32 40 48 56 64 80 96 112 128 144 160]]
1 [[ 8 16 24 32 40 48 56 64 80 96 112 128 144 160]]
]]) shift (61440 and hdr) 12
SamplingRate: pick switch MpegVersion [
3 [[44100 48000 32000 none]]
2 [[22050 24000 16000 none]]
0 [[11025 12000 8000 none]]
] (1 + (shift (3072 and hdr) 10))
PaddingBit: shift (512 and hdr) 9
sdsize: to integer! either MpegVersion = 3 [ ;version 1
((( either layer = 3 [48000][144000]) * Bitrate) / SamplingRate) + PaddingBit
][
((( either layer = 3 [24000][72000]) * Bitrate) / SamplingRate) + PaddingBit
]
]
repend headers [hdrb MP3FrameHeader]
]
]
MP3FrameHeader
]
;## main functions
open: func["Opens MP3 file for parsing" MP3-file [file! url!]][
close ;<- try to close still opened previous port
MP3Header: ID3v2: ID3v1: frame: none
num_frames: duration: 0
inBuffer: make binary! 100 * 1024
outBuffer: make binary! 100 * 1024
MP3-port: system/words/open/read/binary/direct MP3-file
seekToBuffer 50480
]
close: does [
clear headers
error? try [system/words/close MP3-port]
]
parse: func[
"Parses MP3 file and prints info about it's content"
/file MP3-file [file! url!]
][
continue?: true
if file [open MP3-file]
while [all [continue? inBuffer]][
either all [
255 = first inBuffer
224 < second inBuffer
][
on-read-MP3Frame readMP3FrameHeader
][
switch as-string readBytes 3 [
"ID3" [ on-read-ID3 readID3v2]
"TAG" [ on-read-ID3 readID3v1]
]
]
if all [continue? 5048 > length? inBuffer] [
seekToBuffer 50480
]
]
close
true
]
;## parse events
on-read-MP3Frame: func[hdr][
num_frames: num_frames + 1
duration: duration + (1152 / hdr/SamplingRate)
skipBytes hdr/sdsize
]
on-read-ID3: func[id3][
?? id3
]
;## get-sample-of-length
get-sample-of-length: func[timeLength [time!] /since startTime [time!] /local hdr endTime][
clear outBuffer
startTime: either since [to decimal! startTime][duration]
endTime: startTime + to-decimal timeLength
while [inBuffer][
either all [
255 = first inBuffer
224 < second inBuffer
][
hdr: readMP3FrameHeader
either startTime <= duration [
either endTime >= duration: duration + (1152 / hdr/SamplingRate) [
insert tail outBuffer readBytes hdr/sdsize
][
break
]
][
duration: duration + (1152 / hdr/SamplingRate)
skipBytes hdr/sdsize
]
][
switch/default as-string copy/part inBuffer 3 [
"ID3" [ skipBytes 3 readID3v2]
"TAG" [ skipBytes 3 readID3v1]
][ inBuffer: next inBuffer]
]
if 5048 > length? inBuffer [
seekToBuffer 50480
]
]
outBuffer
]
] Notes
|