123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412 |
- MODULE WAVCodec; (** AUTHOR "MVT, PL"; PURPOSE "WAV audio format Codec"; *)
- IMPORT
- Codecs, SoundDevices, Streams, KernelLog,SYSTEM;
- CONST
- MAXBUF = 4096;
- TYPE
- Chunk = ARRAY 5 OF CHAR; (* type of wave header part *)
- (* Header of a wave file *)
- WaveHeader* = RECORD
- chunkRIFF: Chunk; (* must be "RIFF" *)
- chunkWAVE: Chunk; (* must be "WAVE" *)
- chunkfmt: Chunk; (* must be "fmt " *)
- waveFormatSize: LONGINT; (* must be 16 for PCM wave *)
- formatTag: INTEGER; (* must be 1 for PCM wave *)
- nofCh: INTEGER; (* number of channels *)
- sRate: LONGINT; (* sampling rate *)
- bRate: LONGINT; (* byte rate *)
- blockAlign: INTEGER; (* bytes per sample *)
- bitsPerSample: INTEGER; (* sampling resolution = bits per sample for 1 channel *)
- chunkdata: Chunk; (* must be "data" *)
- fileSize: LONGINT; (* size of the whole file minus 8 byte *)
- dataSize: LONGINT; (* size of PCM data in byte = file size minus header size *)
- END;
- (* Audio Wave PCM Encoder *)
- WAVEncoder* = OBJECT(Codecs.AudioEncoder)
- VAR
- out: Streams.Writer;
- h: WaveHeader;
- PROCEDURE Open*(out: Streams.Writer; sRate, sRes, nofCh: LONGINT; VAR res : WORD);
- BEGIN
- res := -1;
- IF out = NIL THEN
- KernelLog.String("WAVEncoder - Writer is NIL"); KernelLog.Ln;
- RETURN;
- END;
- SELF.out := out;
- (* Write wave header *)
- h.chunkRIFF[0] := "R"; h.chunkRIFF[1] := "I"; h.chunkRIFF[2] := "F"; h.chunkRIFF[3] := "F";
- out.Bytes(h.chunkRIFF, 0, 4);
- h.fileSize := SIZEOF(WaveHeader)-8; (* for wave file with zero-length sound - will be updated later *)
- WriteRawBELongInt(out, h.fileSize);
- h.chunkWAVE[0] := "W"; h.chunkWAVE[1] := "A"; h.chunkWAVE[2] := "V"; h.chunkWAVE[3] := "E";
- out.Bytes(h.chunkWAVE, 0, 4);
- h.chunkfmt[0] := "f"; h.chunkfmt[1] := "m"; h.chunkfmt[2] := "t"; h.chunkfmt[3] := " ";
- out.Bytes(h.chunkfmt, 0, 4);
- h.waveFormatSize := 16;
- WriteRawBELongInt(out, h.waveFormatSize);
- h.formatTag := 1;
- WriteRawBEInteger(out, h.formatTag);
- h.nofCh := SHORT(nofCh);
- WriteRawBEInteger(out, h.nofCh);
- h.sRate := sRate;
- WriteRawBELongInt(out, h.sRate);
- h.blockAlign := SHORT(nofCh * (sRes DIV 8));
- h.bRate := sRate * h.blockAlign;
- WriteRawBELongInt(out, h.bRate);
- WriteRawBEInteger(out, h.blockAlign);
- h.bitsPerSample := SHORT(sRes);
- WriteRawBEInteger(out, h.bitsPerSample);
- h.chunkdata[0] := "d"; h.chunkdata[1] := "a"; h.chunkdata[2] := "t"; h.chunkdata[3] := "a";
- out.Bytes(h.chunkdata, 0, 4);
- h.dataSize := 0; (* for wave file with zero-length sound - will be updated later *)
- WriteRawBELongInt(out, h.dataSize);
- out.Update;
- res := 0
- END Open;
- PROCEDURE Write*(buffer : SoundDevices.Buffer; VAR res : WORD);
- BEGIN
- out.Bytes(buffer.data^, 0, buffer.len);
- out.Update
- END Write;
- END WAVEncoder;
- (* Audio Wave PCM Decoder *)
- WAVDecoder* = OBJECT(Codecs.AudioDecoder)
- VAR in: Streams.Reader;
- h: WaveHeader;
- hasMoreBytes : BOOLEAN;
- PROCEDURE Open*(in : Streams.Reader; VAR res : WORD);
- VAR len, inLen: LONGINT; c: CHAR;
- BEGIN
- res := -1; inLen := 0;
- IF in = NIL THEN
- KernelLog.String("WAVDecoder - InputStream is NIL"); KernelLog.Ln;
- RETURN;
- END;
- SELF.in := in;
- (* Read header and check for correctness *)
- in.Bytes(h.chunkRIFF, 0, 4, inLen);
- IF (inLen # 4) OR (h.chunkRIFF # "RIFF") THEN
- KernelLog.String("WAVDecoder - RIFF header ID not found"); KernelLog.Ln;
- RETURN;
- END;
- ReadRawBELongInt(in, h.fileSize);
- in.Bytes(h.chunkWAVE, 0, 4, inLen);
- IF (inLen # 4) OR (h.chunkWAVE # "WAVE") THEN
- KernelLog.String("WAVDecoder - WAVE header ID not found"); KernelLog.Ln;
- RETURN;
- END;
- in.Bytes(h.chunkfmt, 0, 4, inLen);
- IF (inLen # 4) OR (h.chunkfmt # "fmt ") THEN
- KernelLog.String("WAVDecoder - fmt header ID not found"); KernelLog.Ln;
- RETURN;
- END;
- ReadRawBELongInt(in, h.waveFormatSize);
- IF (h.waveFormatSize < 16) THEN
- KernelLog.String("WAVDecoder - Wrong header size"); KernelLog.Ln;
- RETURN;
- END;
- in.RawInt(h.formatTag);
- IF (h.formatTag # 1) THEN
- KernelLog.String("WAVDecoder - Wrong wave format (must be PCM)"); KernelLog.Ln;
- RETURN;
- END;
- ReadRawBEInteger(in, h.nofCh);
- ReadRawBELongInt(in, h.sRate);
- ReadRawBELongInt(in, h.bRate);
- ReadRawBEInteger(in, h.blockAlign);
- ReadRawBEInteger(in, h.bitsPerSample);
- IF (h.blockAlign*h.sRate # h.bRate) OR (h.nofCh*(h.bitsPerSample DIV 8) # h.blockAlign) THEN
- KernelLog.String("WAVDecoder - Inconsistent header info"); KernelLog.Ln;
- RETURN;
- END;
- len := h.waveFormatSize - 16;
- WHILE len > 0 DO c := in.Get(); DEC(len) END;
- REPEAT
- in.Bytes(h.chunkdata, 0, 4, inLen);
- UNTIL (inLen = 4) & (h.chunkdata = "data") OR (in.Pos() >= (h.fileSize + 8));
- IF (inLen # 4) OR (h.chunkdata # "data") THEN
- KernelLog.String("WAVDecoder - data header ID not found"); KernelLog.Ln;
- KernelLog.String("res= "); KernelLog.Int(inLen, 0);
- KernelLog.String("h.chunkdata= "); KernelLog.String(h.chunkdata);
- RETURN;
- END;
- ReadRawBELongInt(in, h.dataSize);
- hasMoreBytes := TRUE;
- res := 0
- END Open;
- PROCEDURE HasMoreData*():BOOLEAN;
- BEGIN
- RETURN hasMoreBytes
- END HasMoreData;
- PROCEDURE GetAudioInfo*(VAR nofChannels, samplesPerSecond, bitsPerSample : LONGINT);
- BEGIN
- nofChannels := h.nofCh;
- bitsPerSample := h.bitsPerSample;
- samplesPerSecond := h.sRate
- END GetAudioInfo;
- (* Dumps part of the header *)
- PROCEDURE DumpHeader;
- BEGIN
- KernelLog.String("-- WAV Header Data --"); KernelLog.Ln;
- KernelLog.String("h.nofCh= "); KernelLog.Int(h.nofCh, 0); KernelLog.Ln;
- KernelLog.String("h.sRate= "); KernelLog.Int(h.sRate, 0); KernelLog.Ln;
- KernelLog.String("h.bitsPerSample= "); KernelLog.Int(h.bitsPerSample, 0); KernelLog.Ln;
- KernelLog.String("h.bRate= "); KernelLog.Int(h.bRate, 0); KernelLog.Ln;
- KernelLog.String("h.blockAlign= "); KernelLog.Int(h.blockAlign, 0); KernelLog.Ln;
- KernelLog.String("h.fileSize= "); KernelLog.Int(h.fileSize, 0);
- KernelLog.String("h.dataSize= "); KernelLog.Int(h.dataSize, 0)
- END DumpHeader;
- PROCEDURE CanSeek*() : BOOLEAN;
- BEGIN
- KernelLog.String("Not Implemented");
- RETURN FALSE;
- END CanSeek;
- PROCEDURE GetCurrentSample*() : LONGINT;
- BEGIN
- RETURN ENTIER((in.Pos() - (h.fileSize - h.dataSize)) / h.bRate * h.sRate)
- END GetCurrentSample;
- PROCEDURE GetTotalSamples*() : LONGINT;
- BEGIN
- RETURN ENTIER(h.dataSize / h.bRate * h.sRate)
- END GetTotalSamples;
- (* Returns the current time in 1/10 sec *)
- PROCEDURE GetCurrentTime*() : LONGINT;
- BEGIN
- RETURN ENTIER((in.Pos() - (h.fileSize - h.dataSize)) / h.bRate * 10)
- END GetCurrentTime;
- PROCEDURE SetStreamLength*(length : LONGINT);
- BEGIN
- h.fileSize := length-8;
- h.dataSize := length-SIZEOF(WaveHeader);
- END SetStreamLength;
- PROCEDURE SeekSample*(sample: LONGINT; goKeySample : BOOLEAN; VAR res : WORD);
- VAR seekType: LONGINT;
- BEGIN
- seekType := Codecs.SeekByte;
- (* in.Seek(seekType, h.fileSize - h.dataSize + ENTIER(sample / h.sRate * h.bRate), itemSize, res); *)
- in.SetPos(h.fileSize - h.dataSize + ENTIER(sample / h.sRate * h.bRate))
- END SeekSample;
- PROCEDURE SeekMillisecond*(millisecond : LONGINT; goKeySample : BOOLEAN; VAR res : WORD);
- BEGIN
- SeekSample(ENTIER(millisecond / 1000 * h.sRate), goKeySample, res)
- END SeekMillisecond;
- (** Prepare the next audio bytes not yet filled into a buffer *)
- PROCEDURE Next*;
- END Next;
- PROCEDURE FillBuffer*(buffer : SoundDevices.Buffer);
- BEGIN
- in.Bytes(buffer.data^, 0, LEN(buffer.data^), buffer.len);
- IF (in.res = Streams.EOF) OR (buffer.len < LEN(buffer.data)) THEN
- hasMoreBytes := FALSE;
- RETURN;
- END;
- END FillBuffer;
- END WAVDecoder;
- (* Audio PCM Decoder (WAV without header) *)
- PCMDecoder* = OBJECT(Codecs.AudioDecoder)
- VAR
- in: Streams.Reader;
- h : WaveHeader;
- hasMoreBytes : BOOLEAN;
- PROCEDURE Open*(in : Streams.Reader; VAR res : WORD);
- BEGIN
- res := -1;
- IF in = NIL THEN
- KernelLog.String("PCMDecoder - InputStream is NIL"); KernelLog.Ln;
- RETURN;
- END;
- SELF.in := in;
- hasMoreBytes := TRUE;
- res := 0
- END Open;
- PROCEDURE HasMoreData*():BOOLEAN;
- BEGIN
- RETURN hasMoreBytes
- END HasMoreData;
- PROCEDURE GetAudioInfo*(VAR nofChannels, samplesPerSecond, bitsPerSample : LONGINT);
- BEGIN
- nofChannels := h.nofCh;
- bitsPerSample := h.bitsPerSample;
- samplesPerSecond := h.sRate
- END GetAudioInfo;
- PROCEDURE SetAudioInfo*(nofChannels, samplesPerSecond, bitsPerSample : LONGINT);
- BEGIN
- h.nofCh := SHORT(nofChannels);
- h.bitsPerSample := SHORT(bitsPerSample);
- h.sRate := samplesPerSecond;
- (* calc the others *)
- h.bRate := h.nofCh * h.sRate * h.bitsPerSample DIV 8;
- h.blockAlign := h.nofCh * h.bitsPerSample DIV 8
- END SetAudioInfo;
- PROCEDURE CanSeek*() : BOOLEAN;
- BEGIN
- KernelLog.String("Not Implemented");
- RETURN FALSE;
- END CanSeek;
- PROCEDURE GetCurrentSample*() : LONGINT;
- BEGIN
- KernelLog.String("pi= ");
- RETURN ENTIER(8 * in.Pos() / h.bitsPerSample / h.nofCh)
- END GetCurrentSample;
- PROCEDURE GetTotalSamples*() : LONGINT;
- BEGIN
- KernelLog.String("pa= ");
- RETURN ENTIER(8 * h.dataSize / h.bitsPerSample / h.nofCh)
- END GetTotalSamples;
- (* Returns the current time in 1/10 sec *)
- PROCEDURE GetCurrentTime*() : LONGINT;
- BEGIN
- KernelLog.String("po= ");
- RETURN ENTIER(8 * in.Pos() / h.bitsPerSample / h.nofCh / h.sRate * 10)
- END GetCurrentTime;
- PROCEDURE SetStreamLength*(length : LONGINT);
- BEGIN
- h.fileSize := length+SIZEOF(WaveHeader)-8;
- h.dataSize := length
- END SetStreamLength;
- PROCEDURE SeekSample*(sample: LONGINT; goKeySample : BOOLEAN; VAR res : WORD);
- VAR seekType: LONGINT;
- BEGIN
- KernelLog.String("pu= "); KernelLog.Int(sample, 0);
- KernelLog.String("bi= "); KernelLog.Int(h.bitsPerSample, 0);
- seekType := Codecs.SeekByte;
- (* in.Seek(seekType, ENTIER(sample * h.bitsPerSample / 8 * h.nofCh), itemSize, res); *)
- in.SetPos(ENTIER(sample * h.bitsPerSample / 8 * h.nofCh))
- END SeekSample;
- PROCEDURE SeekMillisecond*(millisecond : LONGINT; goKeySample : BOOLEAN; VAR res : WORD);
- BEGIN
- SeekSample(ENTIER(millisecond / 1000 * h.sRate), goKeySample, res)
- END SeekMillisecond;
- (** Prepare the next audio bytes not yet filled into a buffer *)
- PROCEDURE Next*;
- END Next;
- PROCEDURE FillBuffer*(buffer : SoundDevices.Buffer);
- BEGIN
- in.Bytes(buffer.data^, 0, LEN(buffer.data^), buffer.len);
- IF (in.res = Streams.EOF) OR (buffer.len < LEN(buffer.data)) THEN
- hasMoreBytes := FALSE; KernelLog.String("BOOOOM!!");
- RETURN;
- END;
- END FillBuffer;
- END PCMDecoder;
- (* Routines for reading and writing numbers in Intel's big endian format *)
- PROCEDURE ReadRawBEInteger(VAR r: Streams.Reader; VAR value: INTEGER);
- BEGIN
- value := ORD(r.Get()) + 100H *ORD(r.Get());
- END ReadRawBEInteger;
- PROCEDURE ReadRawBELongInt(VAR r: Streams.Reader; VAR value: LONGINT);
- BEGIN
- value := LONG(ORD(r.Get())) + 100H * LONG(ORD(r.Get()))
- + 10000H * LONG(ORD(r.Get())) + 1000000H * LONG(ORD(r.Get()));
- END ReadRawBELongInt;
- PROCEDURE WriteRawBEInteger(VAR w: Streams.Writer; value: INTEGER);
- BEGIN
- w.Char(CHR(value MOD 100H));
- w.Char(CHR(value DIV 100H));
- END WriteRawBEInteger;
- PROCEDURE WriteRawBELongInt(VAR w: Streams.Writer; value: LONGINT);
- BEGIN
- w.Char(CHR(value MOD 100H));
- value := value DIV 100H;
- w.Char(CHR(value MOD 100H));
- value := value DIV 100H;
- w.Char(CHR(value MOD 100H));
- w.Char(CHR(value DIV 100H));
- END WriteRawBELongInt;
- (* -- Factories -- *)
- PROCEDURE EncoderFactory*() : Codecs.AudioEncoder;
- VAR p : WAVEncoder;
- BEGIN
- NEW(p);
- RETURN p
- END EncoderFactory;
- PROCEDURE DecoderFactory*() : Codecs.AudioDecoder;
- VAR p : WAVDecoder;
- BEGIN
- NEW(p);
- RETURN p
- END DecoderFactory;
- PROCEDURE PCMDecoderFactory*() : Codecs.AudioDecoder;
- VAR p : PCMDecoder;
- BEGIN
- NEW(p);
- RETURN p
- END PCMDecoderFactory
- END WAVCodec.
- ------------------------------------------------------------------------------
- System.Free WAVCodec;
|