123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182 |
- //
- // WAV/AU Flash player with resampler
- //
- // Copyright (c) 2009, Anton Fedorov <datacompboy@call2ru.com>
- //
- /* This code is free software; you can redistribute it and/or modify it
- * under the terms of the GNU General Public License version 2 only, as
- * published by the Free Software Foundation.
- *
- * This code is distributed in the hope that it will be useful, but WITHOUT
- * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
- * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
- * version 2 for more details (a copy is included in the LICENSE file that
- * accompanied this code).
- */
- package fmt;
- // Generic file stream scanner interface
- interface IFile {
- var last: Bool;
- // Push data from audio stream to decoder
- function push(bytes: flash.utils.IDataInput, last:Bool): Void;
- // Require decoder to populate at least <samples> samples from audio stream
- function populate(samples: Int): Void;
- // Rewind on input stream to Pos, and return real pos we have seeked, or seeking to
- function seek(Pos: Float): Float;
- // Returns is stream ready to operate: header readed (1), not ready (0), error(-1)
- function ready(): Int;
- // Get sound samplerate is Hz
- function getRate(): Int;
- // Get sound channels
- function getChannels(): Int;
- // Set known full file length
- function setSize(size: Int): Void;
- // Get estimated sound length
- function getEtaLength(): Null<Float>;
- // Get loaded sound length
- function getLoadedLength(): Float;
- // Get count of complete samples available
- function samplesAvailable(): Int;
- // Get complete samples as array of channel samples
- function getSamples(): Array<Array<Float>>;
- }
- // File: generic stream file reader. Subclass it to define used sound decoder and headers
- class File implements IFile {
- public var last : Bool;
- var Buffer: flash.utils.ByteArray;
- var bufsize: Int;
- var rate : Int;
- var channels : Int;
- var sndDecoder : Null<Decoder>;
- var chunkSize : Int;
- var align : Int;
- var SoundBuffer: Array<Array<Float>>;
- var dataOff: Int;
- var dataSize : Null<Int>;
- var dataLen : Null<Float>;
- var Readed : Int;
- public function new() {
- Buffer = new flash.utils.ByteArray();
- bufsize = 0;
- rate = 0;
- channels = 0;
- chunkSize = 0;
- align = 0;
- Readed = 0;
- dataOff = 0;
- dataSize = 0;
- last = false;
- }
- // Initialize SoundBuffer after header readed
- private function init() {
- SoundBuffer = new Array<Array<Float>>();
- for(c in 0...channels)
- SoundBuffer.push( new Array<Float>() );
- }
- // Set known full file length
- public function setSize(size: Int): Void {
- dataSize = size;
- }
-
- // Get estimated sound length
- public function getEtaLength(): Null<Float> {
- if (rate==0 || chunkSize==0 || dataSize==0) return null;
- if (dataLen == null && dataSize > 0)
- dataLen = sndDecoder.decodeLength(Math.floor(dataSize/chunkSize))/rate;
- return dataLen;
- }
-
- // Get loaded sound length
- public function getLoadedLength(): Float {
- if (rate == 0 || chunkSize == 0 || sndDecoder==null)
- return 0.0;
- else
- return (bufsize-dataOff > dataLen)
- ? getEtaLength()
- : sndDecoder.decodeLength(Math.floor((bufsize-dataOff)/chunkSize)) / rate;
- }
-
- // Read file header
- public function readHeader() {
- return; // on raw file no header
- }
- // Push data from audio stream to decoder
- public function push(bytes: flash.utils.IDataInput, last:Bool): Void {
- if (ready() < 0) return; // Do not operate on error
- var avail = bytes.bytesAvailable;
- if (avail > 65536) avail = 65536;
- trace("Pushing "+avail+" bytes...");
- if (avail == 0) return;
- bytes.readBytes(Buffer, bufsize, avail);
- bufsize += avail;
- if (ready() == 0) readHeader();
- }
- // Require decoder to populate at least <samples> samples from audio stream
- public function populate(samples: Int): Void {
- if (ready() != 1) return;
- var i = Readed;
- var chk = 0;
- while(SoundBuffer[0].length < samples && bufsize - i >= chunkSize) {
- for(j in 0...channels) {
- sndDecoder.decode(Buffer, i, j, SoundBuffer[j], SoundBuffer[j].length);
- i += sndDecoder.sampleSize;
- }
- i += align;
- chk++;
- }
- Readed = i;
- last = (Readed-dataOff+chunkSize >= dataSize);
- if(chk>0) trace("Read "+chk+" chunks, last="+last+"; Readed="+Readed+"; dataSize="+dataSize+"; dataOff="+dataOff+"; chunkSize="+chunkSize);
- }
- // Rewind on input stream to Pos, and return real pos we have seeked, or seeking to
- public function seek(Pos: Float): Float {
- if (ready() != 1) return 0;
- var sample = Math.ceil( Pos * rate ); // Wanted sample number
- var chunk = sndDecoder.seek(Math.ceil( sample / sndDecoder.sampleLength )); // Wanted sample chunk
- var offset = chunk * chunkSize + dataOff; // Offset, where to seek
- if (offset > bufsize) // Round to maximal ready
- offset = Math.floor( (bufsize - dataOff) / chunkSize ) * chunkSize + dataOff;
- Readed = offset; // Seek to required offset
- sample = Math.ceil( (offset - dataOff) / chunkSize ) * sndDecoder.sampleLength; // Get final selected sample
- trace("Sync to Pos "+Pos+"; sample = "+sample+"; offset="+offset+"; res="+(sample/rate));
- return sample / rate;
- }
-
- // Returns is stream ready to operate: header readed (1), not ready (0), error(-1)
- public function ready(): Int {
- return -1;
- }
- // Get sound samplerate is Hz
- public function getRate(): Int {
- return rate;
- }
- // Get sound channels
- public function getChannels(): Int {
- return channels;
- }
- // Get count of complete samples available
- public function samplesAvailable(): Int {
- return SoundBuffer[0].length;
- }
- // Get complete samples as array of channel samples
- public function getSamples(): Array<Array<Float>> {
- var Ret = SoundBuffer;
- SoundBuffer = new Array<Array<Float>>();
- for(j in 0...channels)
- SoundBuffer.push(new Array<Float>());
- return Ret;
- }
- }
|