2015-05-25 101 views
5

Ho registrato campioni vocali dal mio microfono utilizzando Adobe Flash Builder 4.6/AIR, voce registrata correttamente. Ho prima convertito i dati vocali (array di byte) in formato base64 in actionscript, quindi ho convertito i dati di base64 in file WAV usando il mio codice PHP. ma quel file WAV lancia il problema corrotto del file in RiffPad.Problema danneggiato con file registrato audio in ActionScript

RIFFPad è un visualizzatore per file in formato RIFF come WAV, AVI.

specifica file wav atteso:

frequenza di campionamento: 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 funzione intestazione Writer:

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 funzione di file di Writer:

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); 
    } 
} 

io mando che i dati base64 alla mia richiesta di servizio lato php ho ottenuto il '$ this-> request-> voiceSample' parametro e decodificare Base64 per il wav file

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

Dopo caricare il file "name.wav" in Riffpad ho avuto problema

C'è ulteriore spazzatura alla fine del file.

Chiunque si prega di darmi il consiglio per risolvere questo problema ...

+0

Verificare se il codificatore di stringa base64 è corretto confrontando con encoder abilitati pubblicamente. Controlla anche se il tuo decodificatore PHP è stato scritto correttamente (a meno che non sia una funzione integrata). Controlla anche se il tuo lato Flash dipende da byte endian (esiste una dipendenza nascosta in 'c = data [int (i ++)] << 16 | data [int (i ++)] << 8 | data [int (i ++)]; 'parte almeno). – Vesper

+0

Ciao vesper, sto usando la codifica [base64] (http://www.sociodox.com/base64.html) lib per la conversione da byteArray a base64. In PHP, sto usando la funzione integrata. – VijayRagavan

+0

Quando si visualizza il file decodificato tramite l'editor esadecimale, ha una firma 'RIFF' nei primi 4 byte? In caso contrario, dovrai eseguire il debug delle tue routine di conversione. – Vesper

risposta

2

C'è un errore insito in questa linea:

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

I Microphone.rate manuale indica che la frequenza di campionamento effettiva differisce rispetto alle microphone.rate*1000 come previsto da questo codice. La tabella effettiva è la seguente:

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

Così, mentre il vostro stato commenti del codice che rate viene segnalato come 8, questo potrebbe non essere il caso sul lato client, in generale, in modo da eseguire la ricerca prima di passare alla dedotta frequenza di campionamento in wavWriter.processSamples().

Successivamente, si sta calcolando il calcolo in virgola mobile dataByteLength, questo potrebbe risultare inaccurato poiché si campionano i dati byte per byte, quindi è meglio ricampionare, quindi raccogliere la lunghezza dei dati e solo poi scrivere tutti i dati in dataOutput, in questo modo:

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); 

} 

ho riscritto la routine di campionamento a utilizzare sliding window (funziona meglio se la nuova frequenza di campionamento è superiore a quello vecchio, ma accetta qualsiasi rapporto). Questo algoritmo utilizza l'interpolazione lineare tra i campioni invece di riutilizzare semplicemente il vecchio campione sulla lunghezza della sequenza interpolata. Sentiti libero di sostituire con il tuo loop.Il principale che deve essere conservato è che si compila prima completotempData e solo dopo si scrive l'intestazione con la lunghezza dei dati ora definita correttamente.

Si prega di segnalare problemi se ce ne sono.

+0

Ho usato la tua routine riscritta genera il file audio senza alcuna spazzatura ma la dimensione del file audio è molto bassa e il file non è quello che abbiamo registrato nessun suono è stato ascoltato quando lo abbiamo riprodotto – VijayRagavan

+0

Ouch, c'era un refuso nel determinare il valore di 'step', ho diviso per una variabile sbagliata, avrei dovuto dividerlo per' samplingRate' not 'soundRate'. Fisso. – Vesper

+0

Ora il file audio è stato generato ma il file è troppo lungo che è 375kb e anche quando ho provato a riprodurre l'audio il suono non era esattamente quello che abbiamo registrato – VijayRagavan