Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RIFF support. #6

Merged
merged 4 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CHANGES.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
- Add optional custom parser argument to override the default parsing mechanism.
- Update default metadata mappings to follow musicbrainz's picard mapping.
- Add `MIME` module to guess MIME type of files (#4).
- Generic RIFF format parser, adds support for wave files (#6).

0.2.0 (2023-07-01)
=====
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ The metadata library
A pure OCaml library to read metadata from various formats. For now, are
supported:

- audio formats: ID3v1 and ID3v2 (for mp3), ogg/vorbis, ogg/opus and flac
- audio formats: ID3v1 and ID3v2 (for mp3), ogg/vorbis, ogg/opus, flac and wav
- image formats: jpeg and png
- video formats: mp4 and avi

Expand Down
4 changes: 1 addition & 3 deletions examples/dune
Original file line number Diff line number Diff line change
Expand Up @@ -73,9 +73,7 @@
-i
"sine=frequency=440:sample_rate=44100:duration=10"
-f
mp3
-write_id3v1
true
wav
-metadata
"title=The title"
-metadata
Expand Down
2 changes: 1 addition & 1 deletion examples/test.ml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ let () =
p "mp3v2" (Metadata.ID3v2.parse_file "test.mp3");
p "mp3v1" (Metadata.ID3v1.parse_file "test.mp3");
p "mp3" (Metadata.ID3.parse_file "test.mp3");
p "wav" (Metadata.ID3.parse_file "test.wav");
p "wav" (Metadata.WAV.parse_file "test.wav");
p "png" (Metadata.PNG.parse_file "test.png");
p "jpg" (Metadata.JPEG.parse_file "test.jpg");
p "avi" (Metadata.AVI.parse_file "test.avi");
Expand Down
6 changes: 4 additions & 2 deletions src/metadata.ml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ module Make (E : CharEncoding.T) = struct
module PNG = MetadataPNG
module AVI = MetadataAVI
module MP4 = MetadataMP4
module WAV = MetadataWAV
module RIFF = MetadataRIFF

(** Charset conversion function. *)
let recode = E.convert
Expand Down Expand Up @@ -43,7 +45,7 @@ module Make (E : CharEncoding.T) = struct
| [] -> raise Invalid

module Audio = struct
let parsers = [ID3.parse; OGG.parse; FLAC.parse]
let parsers = [ID3.parse; OGG.parse; FLAC.parse; WAV.parse]
let parse = first_valid parsers

let parse_file ?custom_parser file =
Expand All @@ -67,7 +69,7 @@ module Make (E : CharEncoding.T) = struct
end

module Any = struct
let parsers = Audio.parsers @ Image.parsers @ Video.parsers
let parsers = Audio.parsers @ Image.parsers @ Video.parsers @ [RIFF.parse]

(** Genering parsing of metadata. *)
let parse = first_valid parsers
Expand Down
6 changes: 6 additions & 0 deletions src/metadata.mli
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,12 @@ module Make : functor (_ : CharEncoding.T) -> sig
(** MP4 metadata. *)
module MP4 = MetadataMP4

(** WAV metadata. *)
module WAV = MetadataWAV

(** RIFF metdata. *)
module RIFF = MetadataRIFF

(** Convert the charset encoding of a string. *)
val recode :
?source:[ `ISO_8859_1 | `UTF_16 | `UTF_16BE | `UTF_16LE | `UTF_8 ] ->
Expand Down
62 changes: 2 additions & 60 deletions src/metadataAVI.ml
Original file line number Diff line number Diff line change
@@ -1,61 +1,3 @@
open MetadataBase
module R = Reader
let parse = MetadataRIFF.parse ~format:"AVI "

(* Tag normalization. *)
let tagn =
[
("IART", "artist");
("ICMT", "comment");
("ICOP", "copyright");
("ICRD", "date");
("ICRD", "date");
("IGNR", "genre");
("INAM", "title");
("IPRD", "album");
("IPRT", "track");
("ISFT", "encoder");
]

let parse f : metadata =
if R.read f 4 <> "RIFF" then raise Invalid;
let _ (* file size *) = R.int32_le f in
if R.read f 4 <> "AVI " then raise Invalid;
let ans = ref [] in
let chunk () =
let tag = R.read f 4 in
let size = R.int32_le f in
if tag <> "LIST" then R.drop f size
else (
let subtag = R.read f 4 in
match subtag with
| "INFO" ->
let remaining = ref (size - 4) in
while !remaining > 0 do
let tag = R.read f 4 in
let size = R.int32_le f in
match R.read_tag ~length:(size - 1) ~label:tag f with
| None -> ()
| Some s ->
R.drop f 1;
(* null-terminated *)
let padding = size mod 2 in
R.drop f padding;
remaining := !remaining - (8 + size + padding);
let tag =
match List.assoc_opt tag tagn with
| Some tag -> tag
| None -> tag
in
ans := (tag, s) :: !ans
done
| "movi" -> raise Exit (* stop parsing there *)
| _ -> R.drop f (size - 4))
in
try
while true do
chunk ()
done;
assert false
with _ -> List.rev !ans

let parse_file ?custom_parser file = R.with_file ?custom_parser parse file
let parse_file = MetadataRIFF.parse_file ~format:"AVI "
3 changes: 0 additions & 3 deletions src/metadataAVI.mli

This file was deleted.

62 changes: 62 additions & 0 deletions src/metadataRIFF.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
open MetadataBase
module R = Reader

(* Tag normalization. *)
let tagn =
[
("IART", "artist");
("ICMT", "comment");
("ICOP", "copyright");
("ICRD", "date");
("ICRD", "date");
("IGNR", "genre");
("INAM", "title");
("IPRD", "album");
("IPRT", "track");
("ISFT", "encoder");
("ITRK", "track");
]

let parse ?format f : metadata =
if R.read f 4 <> "RIFF" then raise Invalid;
let _ (* file size *) = R.int32_le f in
if format <> None && Some (R.read f 4) <> format then raise Invalid;
let ans = ref [] in
let chunk () =
let tag = R.read f 4 in
let size = R.int32_le f in
if tag <> "LIST" then R.drop f size
else (
let subtag = R.read f 4 in
match subtag with
| "INFO" ->
let remaining = ref (size - 4) in
while !remaining > 0 do
let tag = R.read f 4 in
let size = R.int32_le f in
match R.read_tag ~length:(size - 1) ~label:tag f with
| None -> ()
| Some s ->
R.drop f 1;
(* null-terminated *)
let padding = size mod 2 in
R.drop f padding;
remaining := !remaining - (8 + size + padding);
let tag =
match List.assoc_opt tag tagn with
| Some tag -> tag
| None -> tag
in
ans := (tag, s) :: !ans
done
| "movi" -> raise Exit (* stop parsing there *)
| _ -> R.drop f (size - 4))
in
try
while true do
chunk ()
done;
assert false
with _ -> List.rev !ans

let parse_file ?format ?custom_parser file = R.with_file ?custom_parser (parse ?format) file
3 changes: 3 additions & 0 deletions src/metadataRIFF.mli
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
val parse : ?format:string -> MetadataBase.Reader.t -> MetadataBase.metadata

val parse_file : ?format:string -> ?custom_parser:MetadataBase.custom_parser -> string -> MetadataBase.metadata
3 changes: 3 additions & 0 deletions src/metadataWAV.ml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
let parse = MetadataRIFF.parse ~format:"WAVE"

let parse_file = MetadataRIFF.parse_file ~format:"WAVE"