2015-05-25 45 views
5

Nagrałem próbki głosu z mojego mikrofonu przy użyciu Adobe Flash Builder 4.6/AIR, głos nagrany z powodzeniem. Najpierw przekonwertowałem dane głosowe (tablica bajtów) na format base64 w actionscript, a następnie przekonwertowałem dane base64 na plik WAV przy użyciu mojego kodu PHP. ale ten plik WAV powoduje uszkodzenie pliku w RiffPad.Problem z nagranym plikiem audio w Actionscript

RIFFPad to przeglądarka plików w formacie RIFF, takich jak WAV, AVI.

spodziewane specyfikacja pliku wav:

próbkowania: 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 funkcja header Scenariusz:

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 
} 

plik WAV funkcja Scenariusz:

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

wyślę że dane Base64 do mojego zgłoszenia serwisowego php strona mam '$ this-> request-> voiceSample' parametr i dekodowania base64 do pliku .wav

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

Po obciążeniu że "name.wav" plików w Riffpad mam problem

Na końcu pliku znajdują się dodatkowe śmieci.

Każdy proszę mi dać rady, aby rozwiązać ten problem ...

+0

Sprawdź, czy koder ciągów znaków base64 jest poprawny, porównując je z publicznie włączonymi enkoderami. Sprawdź również, czy Twój dekoder PHP jest poprawnie zapisany (chyba że jest to wbudowana funkcja). Sprawdź również, czy twoja strona Flash zależy od endianu bajtowego (istnieje ukryta zależność w 'c = data [int (i ++)] << 16 | data [int (i ++)] << 8 | data [int (i ++)]; "część przynajmniej). – Vesper

+0

Cześć vesper, używam kodowania [base64] (http://www.sociodox.com/base64.html) lib dla byteArray do konwersji base64. Po stronie PHP używam wbudowanej funkcji. – VijayRagavan

+0

Podczas przeglądania dekodowanego pliku za pomocą edytora szesnastkowego, czy ma on podpis "RIFF" w pierwszych 4 bajtach? Jeśli nie, będziesz musiał debugować swoje rutynowe konwersje. – Vesper

Odpowiedz

2

Jest nieodłącznym błąd w tej linii:

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

W Microphone.rate ręczne stanowi, że rzeczywista częstotliwość próbkowania różni się od microphone.rate*1000 zgodnie z oczekiwaniami dla tego kodu. Rzeczywista tabela wygląda następująco:

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

Tak więc, choć twój stan komentarzach Kod że rate jest zgłaszane jako 8, to nie może być przypadek po stronie klienta w ogóle, więc wykonać odnośnika przed przekazaniem wydedukowana częstotliwość próbkowania do wavWriter.processSamples().

Następnie, obliczamy zmiennoprzecinkowe dataByteLength za pomocą obliczeń zmiennoprzecinkowych, co może okazać się niedokładne, ponieważ wtedy bajt danych jest próbkowany bajtowo, więc lepiej najpierw dokonać ponownego porównania, a następnie zebrać długość danych i dopiero wtedy zapisać wszystkie dane w dataOutput, tak:

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

} 

mam przepisany rutynę resample do użycia algorytmu przesuwne okno (działa najlepiej, jeśli nowa częstotliwość próbkowania jest wyższa niż stare, ale nie ponosi żadnej proporcji). Algorytm ten wykorzystuje interpolację liniową między próbkami zamiast ponownego użycia starej próbki na długości interpolowanej sekwencji. Możesz zastąpić własną pętlą.Główną zasadą, którą należy zachować, jest to, że najpierw kompilujemy pełnątempData i dopiero wtedy zapisujemy nagłówek z teraz poprawnie zdefiniowaną długością danych.

Proszę zgłaszać problemy, jeśli takie istnieją.

+0

Użyłem przepisanej przez ciebie procedury, która generuje plik audio bez żadnych śmieci, ale rozmiar pliku audio jest bardzo niski i plik, który nie nagrywaliśmy żaden dźwięk nie był słyszalny, kiedy go graliśmy – VijayRagavan

+0

Ou, był błąd w określaniu wartości "kroku", podzieliłem przez niewłaściwą zmienną, powinienem był ją podzielić przez 'samplingRate', a nie' soundRate'. Naprawiony. – Vesper

+0

Teraz plik dźwiękowy został wygenerowany, ale plik jest zbyt długi i wynosi 375kb, a także gdy próbowałem odtworzyć dźwięk, który nie był dokładnie nagrany, – VijayRagavan