【发布时间】:2015-09-12 13:48:50
【问题描述】:
我使用 Adobe Flash Builder 4.6 / AIR 从我的麦克风录制了语音样本,语音录制成功。我首先在 actionscript 中将语音数据(字节数组)转换为 base64 格式,然后使用我的 PHP 代码将该 base64 数据转换为 WAV 文件。但是那个 WAV 文件会在 RiffPad 中引发文件损坏问题。
RIFFPad 是 RIFF 格式文件(如 WAV、AVI)的查看器。
预期的 wav 文件规范:
采样率: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 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文件写入功能:
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 方面我得到了 '$this->request->voiceSample' 参数并将 base64 解码为 .wav 文件
file_put_contents('name.wav', base64_decode($this->request->voiceSample));
在 Riffpad 中加载“name.wav”文件后 我有问题
文件末尾有多余的垃圾。
任何人请给我解决这个问题的建议......
【问题讨论】:
-
通过与公开启用的编码器进行比较来检查您的 base64 字符串编码器是否正确。还要检查您的 PHP 解码器是否正确编写(除非它是内置函数)。还要检查你的 Flash 端是否依赖字节端(至少在
c = data[int(i++)] << 16 | data[int(i++)] << 8 | data[int(i++)];部分有一个隐藏的依赖项)。 -
嗨 vesper,我正在使用编码 base64 lib 进行 byteArray 到 base64 的转换。在 PHP 方面,我使用的是内置函数。
-
当您通过十六进制编辑器查看解码后的文件时,它的前 4 个字节是否有
RIFF签名?如果没有,您将不得不调试您的转换例程。 -
那么,收到的文件有完整的 RIFF 签名吗?好吧,那就不是base64转换了。抱歉,我无法帮助调试签名编写程序,也许其他人可以。 (嗯,也许写 UTFBytes 会在某处丢一个零?我建议改为写一个 int。)另外,一旦你发现错误在哪里,请在这个问题中发布答案。
-
好问题! :) 我认为最好的选择是将 wav 文件加载到闪存中,对其进行解码,然后使用您的函数对其进行编码和转换并将其发送到服务器。将结果与您在十六进制编辑器中看到的结果进行比较 - 也许您错过了签名中的某些内容..
标签: php actionscript-3 apache-flex flex3 wav