root/trunk/liquidsoap/src/formats/wavformat.ml @ 7122

Revision 7122, 4.9 KB (checked in by dbaelde, 7 months ago)

Introduce a variant module of Generator.From_audio_video with builtin
multi-threading support and overfull protection.
It is used when streaming (because decoding and replaying are concurrent).
This simplifies the code of input.http/harbor and the Generated source.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
Line 
1(*****************************************************************************
2
3  Liquidsoap, a programmable audio stream generator.
4  Copyright 2003-2009 Savonet team
5
6  This program is free software; you can redistribute it and/or modify
7  it under the terms of the GNU General Public License as published by
8  the Free Software Foundation; either version 2 of the License, or
9  (at your option) any later version.
10
11  This program is distributed in the hope that it will be useful,
12  but WITHOUT ANY WARRANTY; without even the implied warranty of
13  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14  GNU General Public License for more details, fully stated in the COPYING
15  file at the root of the liquidsoap distribution.
16
17  You should have received a copy of the GNU General Public License
18  along with this program; if not, write to the Free Software
19  Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20
21 *****************************************************************************)
22
23(** Decode WAV files. *)
24
25let log = Dtools.Log.make ["decoder";"wav"]
26
27(** {1 Generic decoder} *)
28
29exception End_of_stream
30
31let rec really_input input len =
32  let s,i = input len in
33    if i=len then s else
34      if i=0 then raise End_of_stream else
35        String.sub s 0 i ^ really_input input (len-i)
36
37let input_byte input =
38  let s,i = input 1 in
39    if i=0 then raise End_of_stream ;
40    int_of_char s.[0]
41
42let read_int_num_bytes ic =
43  let rec aux = function
44    | 0 -> 0
45    | n ->
46        let b = input_byte ic in
47          b + 256*(aux (n-1))
48  in
49    aux
50
51let read_int ic = read_int_num_bytes ic 4
52
53let read_short ic = read_int_num_bytes ic 2
54
55module Make (Generator:Generator.S_Asio) =
56struct
57
58(* TODO It might be more efficient to write our code for an input
59 * channel and use directly the one we have when decoding files
60 * or external processes, if we could wrap the input function used
61 * for decoding stream (in http and harbor) as an in_channel. *)
62let create input =
63  let decoder = ref (fun gen -> assert false) in
64
65  let main_decoder converter gen =
66    let bytes_to_get = 1024*64 in
67    let data,bytes = input bytes_to_get in
68      if bytes=0 then raise End_of_stream ;
69      log#f 4 "Read %d bytes of PCM" bytes ;
70      let content,length = converter (String.sub data 0 bytes) in
71        Generator.set_mode gen `Audio ;
72        Generator.put_audio gen content 0 length ;
73        log#f 4 "Done (%d)" length
74  in
75
76  let read_header () =
77
78    if really_input input 4 <> "RIFF" then
79      raise (Wav.Not_a_wav_file "Bad header: \"RIFF\" not found") ;
80    (* Ignore the file size *)
81    ignore (really_input input 4) ;
82    if really_input input 8 <> "WAVEfmt " then
83      raise (Wav.Not_a_wav_file "Bad header: \"WAVEfmt \" not found") ;
84    (* Now we always have the following uninteresting bytes:
85     * 0x10 0x00 0x00 0x00 0x01 0x00 *)
86    ignore (really_input input 6) ;
87
88    let channels = read_short input in
89    let samplerate (* in Hz *) = read_int input in
90    let _ (* byt_per_sec *) = read_int input in
91    let _ (* byt_per_samp *) = read_short input in
92    let samplesize (* in bits *) = read_short input in
93
94    let signed = samplesize <> 8 in
95    let big_endian = false in
96
97    let section = really_input input 4 in
98    if section <> "data" then begin
99      if section = "INFO" then
100        raise (Wav.Not_a_wav_file "Valid wav file but unread");
101      raise (Wav.Not_a_wav_file "Bad header : string \"data\" not found")
102    end ;
103
104    let _ (* len_dat *) = read_int input in
105
106    let converter =
107      Rutils.create_from_s16le
108        ~channels ~samplesize ~signed ~big_endian ()
109    in
110
111      log#f 4
112        "WAV header read (%dHz, %dbits), starting decoding..."
113        samplerate samplesize ;
114      decoder :=
115        main_decoder
116          (fun pcm -> converter ~audio_src_rate:(float samplerate) pcm)
117
118  in
119    decoder := (fun _ -> read_header ()) ;
120    Decoder.Decoder (fun gen -> !decoder gen)
121
122end
123
124module Generator = Generator.From_audio_video
125module Buffered = Decoder.Buffered(Generator)
126
127(* File decoding *)
128
129module D = Make(Generator)
130
131let get_type filename =
132  let chan = open_in filename in
133  let info = Wav.read_header chan filename in
134    close_in chan ;
135    { Frame. video = 0 ; midi = 0 ; audio = Wav.channels info }
136
137let create_file_decoder filename kind =
138  let generator = Generator.create `Audio in
139    Buffered.file_decoder filename kind D.create generator
140
141let () =
142  Decoder.file_decoders#register "WAV"
143    ~sdoc:"Decode as WAV any file with a correct header."
144    (fun filename kind ->
145       let file_type = get_type filename in
146         if Frame.type_has_kind file_type kind then
147           Some (fun () -> create_file_decoder filename kind)
148         else begin
149           log#f 3
150             "WAV file %S has content type %s but %s was expected."
151             filename
152             (Frame.string_of_content_type file_type)
153             (Frame.string_of_content_kind kind) ;
154           None
155         end)
Note: See TracBrowser for help on using the browser.