From 296dbbec3d6ad47281455b2585fe186475f77df0 Mon Sep 17 00:00:00 2001 From: Samuel Mimram Date: Tue, 30 Jan 2024 17:40:58 +0100 Subject: [PATCH 1/4] RIFF support. --- CHANGES.md | 1 + examples/dune | 4 +-- src/metadata.ml | 6 +++-- src/metadata.mli | 6 +++++ src/metadataAVI.ml | 62 ++------------------------------------------ src/metadataAVI.mli | 3 --- src/metadataRIFF.ml | 62 ++++++++++++++++++++++++++++++++++++++++++++ src/metadataRIFF.mli | 3 +++ src/metadataWAV.ml | 3 +++ 9 files changed, 82 insertions(+), 68 deletions(-) delete mode 100644 src/metadataAVI.mli create mode 100644 src/metadataRIFF.ml create mode 100644 src/metadataRIFF.mli create mode 100644 src/metadataWAV.ml diff --git a/CHANGES.md b/CHANGES.md index 6efa926..966b283 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -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. 0.2.0 (2023-07-01) ===== diff --git a/examples/dune b/examples/dune index 55e424b..6393f62 100644 --- a/examples/dune +++ b/examples/dune @@ -73,9 +73,7 @@ -i "sine=frequency=440:sample_rate=44100:duration=10" -f - mp3 - -write_id3v1 - true + wav -metadata "title=The title" -metadata diff --git a/src/metadata.ml b/src/metadata.ml index 14d14ab..134db8a 100644 --- a/src/metadata.ml +++ b/src/metadata.ml @@ -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 @@ -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 = @@ -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 diff --git a/src/metadata.mli b/src/metadata.mli index aeb79af..79e836a 100644 --- a/src/metadata.mli +++ b/src/metadata.mli @@ -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 ] -> diff --git a/src/metadataAVI.ml b/src/metadataAVI.ml index 47b7640..c4890aa 100644 --- a/src/metadataAVI.ml +++ b/src/metadataAVI.ml @@ -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 " diff --git a/src/metadataAVI.mli b/src/metadataAVI.mli deleted file mode 100644 index b0f6d0a..0000000 --- a/src/metadataAVI.mli +++ /dev/null @@ -1,3 +0,0 @@ -val parse : MetadataBase.Reader.t -> MetadataBase.metadata - -val parse_file : ?custom_parser:MetadataBase.custom_parser -> string -> MetadataBase.metadata diff --git a/src/metadataRIFF.ml b/src/metadataRIFF.ml new file mode 100644 index 0000000..69e16d3 --- /dev/null +++ b/src/metadataRIFF.ml @@ -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 diff --git a/src/metadataRIFF.mli b/src/metadataRIFF.mli new file mode 100644 index 0000000..94bbe3b --- /dev/null +++ b/src/metadataRIFF.mli @@ -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 diff --git a/src/metadataWAV.ml b/src/metadataWAV.ml new file mode 100644 index 0000000..a2d2a21 --- /dev/null +++ b/src/metadataWAV.ml @@ -0,0 +1,3 @@ +let parse = MetadataRIFF.parse ~format:"WAVE" + +let parse_file = MetadataRIFF.parse_file ~format:"WAVE" From 7275dfc0256a3a72512e0709a49490658db12e11 Mon Sep 17 00:00:00 2001 From: Samuel Mimram Date: Tue, 30 Jan 2024 17:41:29 +0100 Subject: [PATCH 2/4] Bug number. --- CHANGES.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/CHANGES.md b/CHANGES.md index 966b283..5cab4d4 100644 --- a/CHANGES.md +++ b/CHANGES.md @@ -5,7 +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. +- Generic RIFF format parser, adds support for wave files (#6). 0.2.0 (2023-07-01) ===== From cb846441f3693a67fe064585a4f537f0e821a2e3 Mon Sep 17 00:00:00 2001 From: Samuel Mimram Date: Tue, 30 Jan 2024 17:47:58 +0100 Subject: [PATCH 3/4] wav. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c57cd46..07f183f 100644 --- a/README.md +++ b/README.md @@ -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 From b3dc4a4ab3ba0621a5d5f80cf3061e552c1d0d17 Mon Sep 17 00:00:00 2001 From: Samuel Mimram Date: Tue, 30 Jan 2024 17:51:26 +0100 Subject: [PATCH 4/4] Fix test. --- examples/test.ml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/examples/test.ml b/examples/test.ml index f0900d3..0cb7a60 100644 --- a/examples/test.ml +++ b/examples/test.ml @@ -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");