在Actionscript中音频录制的文件损坏问题 [英] Audio recorded file corrupted issue in Actionscript

查看:251
本文介绍了在Actionscript中音频录制的文件损坏问题的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我录制的语音样本,使用Adobe Flash Builder的4.6 / AIR,成功地记录声音将我的麦克风。我先转换语音数据(字节数组)在动作Base64格式然后我用我的PHP code转换的Base64编码数据到WAV文件。但WAV文件扔文件中RiffPad损坏问题。

I recorded voice samples from my microphone using Adobe Flash Builder 4.6 / AIR, voice recorded successfully. I first converted voice data(byte array) to base64 format in actionscript then I converted that base64 data to WAV file using my PHP code. but that WAV file throw file corrupted issue in RiffPad.

RIFFPad是一个查看器,如WAV RIFF格式的文件,AVI。

RIFFPad is a viewer for RIFF formatted files like WAV, AVI.

预计WAV文件规范:

expected wav file specification:

采样速率:22KHZ

    // -- saves the current audio data as a .wav file
    protected function onSubmit( event:Event ):void {
        alertBox.show("Processing ... please wait.");

        stopPlayback();
        stopRecording();
        playBtn.enabled = recordBtn.enabled = submitBtn.enabled = false;
        var position:int = capture.buffer.position;
        var wavWriter:WAVWriter = new WAVWriter()
        var wavWriter1:WaveEncoder = new WaveEncoder()
        wavWriter.numOfChannels = 1;
        wavWriter.samplingRate = 22050;
        wavWriter.sampleBitRate = 16; 
        var wavBytes:ByteArray = new ByteArray;
        capture.buffer.position = 0;
        wavWriter.processSamples(wavBytes, capture.buffer, capture.microphone.rate * 1000, 1);
        Settings.alertBox3.show("RATE :"+capture.microphone.rate); //Here show RATE: 8
        //wavWriter.processSamples(wavBytes, capture.buffer, 22050, 1);
        //wavBytes = wavWriter1.encode( capture.buffer, 1, 16, 22050);
        capture.buffer.position = position;
        wavBytes.position=0;
        submitVoiceSample(Base64_new.encodeByteArray(wavBytes));
    }

WAV编剧头功能:

public var samplingRate = 22050;
public var sampleBitRate:int = 8;
public var numOfChannels:int = 2;
private var compressionCode:int = 1;

private function header(dataOutput:IDataOutput, fileSize:Number):void
{
    dataOutput.writeUTFBytes("RIFF");
    dataOutput.writeUnsignedInt(fileSize); // Size of whole file
    dataOutput.writeUTFBytes("WAVE");
    // WAVE Chunk
    dataOutput.writeUTFBytes("fmt ");   // Chunk ID
    dataOutput.writeUnsignedInt(16);    // Header Chunk Data Size
    dataOutput.writeShort(compressionCode); // Compression code - 1 = PCM
    dataOutput.writeShort(numOfChannels); // Number of channels
    dataOutput.writeUnsignedInt(samplingRate); // Sample rate
    dataOutput.writeUnsignedInt(samplingRate * numOfChannels * sampleBitRate / 8); // Byte Rate == SampleRate * NumChannels * BitsPerSample/8       
    dataOutput.writeShort(numOfChannels * sampleBitRate / 8); // Block align == NumChannels * BitsPerSample/8
    dataOutput.writeShort(sampleBitRate); // Bits Per Sample
}

WAV文件写入功能:

public function processSamples(dataOutput:IDataOutput, dataInput:ByteArray, inputSamplingRate:int, inputNumChannels:int = 1):void
{
    if (!dataInput || dataInput.bytesAvailable <= 0) // Return if null
        throw new Error("No audio data");

    // 16 bit values are between -32768 to 32767.
    var bitResolution:Number = (Math.pow(2, sampleBitRate)/2)-1;
    var soundRate:Number = samplingRate / inputSamplingRate;
    var dataByteLength:int = ((dataInput.length/4) * soundRate * sampleBitRate/8);
    // data.length is in 4 bytes per float, where we want samples * sampleBitRate/8 for bytes
    //var fileSize:int = 32 + 8 + dataByteLength;
    var fileSize:int = 32 + 4 + dataByteLength;
    // WAV format requires little-endian
    dataOutput.endian = Endian.LITTLE_ENDIAN;  
    // RIFF WAVE Header Information
    header(dataOutput, fileSize);
    // Data Chunk Header
    dataOutput.writeUTFBytes("data");
    dataOutput.writeUnsignedInt(dataByteLength); // Size of whole file

    // Write data to file
    dataInput.position = 0;
    var tempData:ByteArray = new ByteArray();
    tempData.endian = Endian.LITTLE_ENDIAN;

    // Write to file in chunks of converted data.
    while (dataInput.bytesAvailable > 0) 
    {
        tempData.clear();
        // Resampling logic variables
        var minSamples:int = Math.min(dataInput.bytesAvailable/4, 8192);
        var readSampleLength:int = minSamples;//Math.floor(minSamples/soundRate);
        var resampleFrequency:int = 100;  // Every X frames drop or add frames
        var resampleFrequencyCheck:int = (soundRate-Math.floor(soundRate))*resampleFrequency;
        var soundRateCeil:int = Math.ceil(soundRate);
        var soundRateFloor:int = Math.floor(soundRate);
        var jlen:int = 0;
        var channelCount:int = (numOfChannels-inputNumChannels);
        /*
        trace("resampleFrequency: " + resampleFrequency + " resampleFrequencyCheck: " + resampleFrequencyCheck
            + " soundRateCeil: " + soundRateCeil + " soundRateFloor: " + soundRateFloor);
        */
        var value:Number = 0;
        // Assumes data is in samples of float value
        for (var i:int = 0;i < readSampleLength;i+=4)
        {
            value = dataInput.readFloat();
            // Check for sanity of float value
            if (value > 1 || value < -1)
                throw new Error("Audio samples not in float format");

            // Special case with 8bit WAV files
            if (sampleBitRate == 8)
                value = (bitResolution * value) + bitResolution;
            else
                value = bitResolution * value;

            // Resampling Logic for non-integer sampling rate conversions
            jlen = (resampleFrequencyCheck > 0 && i % resampleFrequency < resampleFrequencyCheck) ? soundRateCeil : soundRateFloor; 
            for (var j:int = 0; j < jlen; j++)
            {
                writeCorrectBits(tempData, value, channelCount);
            }
        }
        dataOutput.writeBytes(tempData);
    }
}

我发送Base64编码数据到我的服务请求  PHP端我得到了'$这个 - >请求 - > voiceSample'参数和DE code的base64为.wav文件

I send that base64 data to my service request php side i got the '$this->request->voiceSample' parameter and decode base64 to .wav file

 file_put_contents('name.wav', base64_decode($this->request->voiceSample));

负载之后,在Riffpadname.wav文件 我的问题

有一个在该文件的末尾额外的垃圾。

There is extra junk at the end of the file.

中的任何一个,请给我建议解决这个问题...

Any one please give me the advice to solve this issue...

推荐答案

目前在这一行固有的错误:

There is an inherent mistake in this line:

 wavWriter.processSamples(wavBytes, capture.buffer, capture.microphone.rate * 1000, 1);

的<一个href="http://help.adobe.com/en_US/FlashPlatform/reference/actionscript/3/flash/media/Microphone.html#rate"相对=nofollow> Microphone.rate 手册规定,实际采样频率不同于 microphone.rate * 1000 如预期本code。实际的表如下:

The Microphone.rate manual states that actual sampling frequency differs from microphone.rate*1000 as expected by this code. The actual table is as follows:

rate   Actual frequency
44     44,100 Hz
22     22,050 Hz
11     11,025 Hz
8      8,000 Hz
5      5,512 Hz

所以,当你的code注释状态报告为8,这可能不会对一般的客户端的情况下,所以执行查找之前通过推导采样速率为 wavWriter.processSamples()

So, while your code comments state that rate is reported as 8, this might not be the case on the client side in general, so perform the lookup prior to passing the deduced sampling rate into wavWriter.processSamples().

接下来,你是precalculating dataByteLength 通过浮点运算,这可能最终会被不正确的,你再采样数据逐字节,所以最好是第一重采样,然后收集数据的长度,然后才写的所有数据到的DataOutput ,像这样的:

Next, you are precalculating dataByteLength via floating point calculation, this might end up being inaccurate as you then sample the data byte by byte, so it's better to first resample, then gather data length and only then write all the data into dataOutput, like this:

public function processSamples(dataOutput:IDataOutput, dataInput:ByteArray, inputSamplingRate:int, inputNumChannels:int = 1):void
{
    if (!dataInput || dataInput.bytesAvailable <= 0) // Return if null
        throw new Error("No audio data");

    // 16 bit values are between -32768 to 32767.
    var bitResolution:Number = (Math.pow(2, sampleBitRate)/2)-1;
    // var soundRate:Number = samplingRate / inputSamplingRate;
    // var fileSize:int = 32 + 4 + dataByteLength; kept for reference
    // fmt tag is 4+4+16, data header is 8 bytes in size, and 4 bytes for WAVE
    // but the data length is not yet determined
    // WAV format requires little-endian
    dataOutput.endian = Endian.LITTLE_ENDIAN;  
    // Prepare data for data to file
    dataInput.position = 0;
    var tempData:ByteArray = new ByteArray();
    tempData.endian = Endian.LITTLE_ENDIAN;
    // Writing in chunks is no longer possible, because we don't have the header ready

    // Let's precalculate the data needed in the loop
    var step:Number=inputSamplingRate / samplingRate; // how far we should step into the input data to get next sample
    var totalOffset:Number=1.0-1e-8; // accumulator for step
    var oldChannels:Array=[];
    var i:int;
    for (i=0;i<numOfChannels;i++) oldChannels.push(0.0);
    // previous channels' sample holder
    var newChannels:Array=oldChannels.slice(); // same for new channels that are to be read from byte array
    // reading first sample set from input byte array
    if (dataInput.bytesAvailable>=inputNumChannels*4) {
        for (i=0;i<inputNumChannels;i++) {
            var buf:Number=dataInput.readFloat();
            if (buf > 1) buf=1; if (buf < -1) buf=-1;
            newChannels[i]=buf;
        }
        // if there's one channel, copy data to other channels
        if ((inputNumChannels==1) && (numOfChannels>1)) {
            for (i=1;i<numOfChannels;i++) newChannels[i]=newChannels[0];                
        }
    }
    while ((dataInput.bytesAvailable>=inputNumChannels*4) || (totalOffset<1.0))
    {
        // sample next value for output wave file
        var value:Number;
        for (i=0;i<numOfChannels;i++) {
            value = (totalOffset*newChannels[i])+(1.0-totalOffset)*oldChannels[i];
            // linear interpolation between old sample and new sample
            // Special case with 8bit WAV files
            if (sampleBitRate == 8)
                value = (bitResolution * value) + bitResolution;
            else
                value = bitResolution * value; 
            // writing one channel into tempData
            writeCorrectBits(tempData, value, 0);
        }
        totalOffset+=step; // advance per output sample
        while ((totalOffset>1) && (dataInput.bytesAvailable>=inputNumChannels*4)) {
            // we need a new sample, and have a sample to process in input
            totalOffset-=1;
            for (i=0;i<numOfChannels;i++) oldChannels[i]=newChannels[i]; // store old sample
            // get another sample, copypasted from above
            for (i=0;i<inputNumChannels;i++) {
                value=dataInput.readFloat();
                if (value > 1) value=1; if (value < -1) value=-1; // sanity check
                // I made it clip instead of throwing exception, replace if necessary
                // if (value > 1 || value < -1) throw new Error("Audio samples not in float format");
                newChannels[i]=value;
            }
            if ((inputNumChannels==1) && (numOfChannels>1)) {
                for (i=1;i<numOfChannels;i++) newChannels[i]=newChannels[0];
            }
        } // end advance by totalOffset
    } // end main loop
    var dataBytesLength:uint=tempData.length; // now the length will be correct by definition
    header(dataOutput, 32+4+dataBytesLength);
    dataOutput.writeUTFBytes("data");
    dataOutput.writeUnsignedInt(dataBytesLength);
    dataOutput.writeBytes(tempData);

}

我已经重写了重采样日常使用的滑动窗口算法(效果最好,如果新的采样率比老较高,但接受任何比)。这个算法使用的样品而不是简单地之间的线性插值重新使用旧的样本在插序列的长度。随意更换自己的循环。应当保留的主要是你第一次编译完全 的TempData ,然后才写出来与现在正确定义的数据长度头。

I have rewritten the resample routine to use sliding window algorithm (works best if new sample rate is higher than old, but accepts any ratio). This algorithm uses linear interpolation between samples instead of plainly re-using old sample over the length of the interpolated sequence. Feel free to replace with your own loop. The principal that should be retained is that you first compile full tempData and only then write the header with now correctly defined data length.

请报告问题,如果有任何。

Please report issues if there are any.

这篇关于在Actionscript中音频录制的文件损坏问题的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆