Recording audio works, playback way too fast

2019-02-25 09:23发布

问题:

I am recording audio input from microphone using the following function:

    private function handleSampleData(p_sampleEvent:SampleDataEvent) :void
    {
        var data :Number;
        while (p_sampleEvent.data.bytesAvailable)
        {
            data = p_sampleEvent.data.readFloat();
            _buffer.writeFloat(data);
            _buffer.writeFloat(data);
        }
    }

This seems to work. After I have finished recording, I copy the recorded data to another buffer like this:

            _buffer.position = 0;
            _lastRecord = new ByteArray();
            while (_buffer.bytesAvailable)
            {
                _lastRecord.writeFloat(_buffer.readFloat());
            }
            _lastRecord.position = 0;

And after that, I use play() on a newly created sound and feed the _lastRecord buffer to it using this function:

    public function handlePlaybackSampleData(p_sampleEvent:SampleDataEvent) :void 
    {
        // Read data until either MAX_SAMPLES or all available samples are reached.
        var i:int = 0;
        var data :Number = 0;
        while( i < 8192 )
        {
            if( _lastRecord.bytesAvailable )
            {
                data = _lastRecord.readFloat();
                p_sampleEvent.data.writeFloat(data);
                i++;
                continue;
            }
            else 
            {
                break;
            }
        }
    }

While it does basically work, the resulting sound that is played is waaaaay too fast. I have taken most of the code from an example in which this works perfectly fine. I see no more vital differences between my code and this one: http://labs.makemachine.net/2011/04/record-visualize-save-microphone-input/

Still, the audio comes out way too fast. If I put even more data into the _buffer by adding more "_buffer.writeFloat(data);", it becomes better, and when I have 12 lines of that, it is the desired speed. But even that only helps when I just bump against my mic. If I actually speak, that does not seem sufficient, either.

But how comes that? And how comes that in the example I took that code from, only 2 lines of that is sufficient? Is there a way to determine how much data I need to write into the buffer while recording?

回答1:

The default sample rate of the microphone in FlashPlayer is 8 kHz mono - or, if that's not available, the first available of 11.025 kHz, 22.05 kHz or 44.1 kHz. Audio output is always 44.1 kHz stereo. This means, that the samples you're getting are too few: 44.1 / 8 ~= 5.5.

You can fix this by setting the recording sample rate to 44.1 kHz:

microphone.rate = 44; // Yes, the API doesn't want the decimals

You could also keep the rate at e.g. 22 or 11 and then write additional duplicate samples to your buffer (simple "upsampling") when recording - i.e. write 4 floats, rather than 2, if recording at 22 kHz, 8 if recording at 11 kHz.

One other thing to be aware of is that an output sample isn't a single float, but two - one for each channel. So, the microphone will give you 8192 mono samples, which you - like you should - apply to both channels by writing each sample twice. You now have 8192 "stereo" samples.

When you're writing the output, however, you're writing at most 8192 floats (i) - which correspond to 4096 stereo samples. The other half - I'd assume from your code and your results - is thrown out.

In the end, what you get is the first half of 8192 samples recorded at a samplerate approximately 5.5 times lower than the playback samplerate. Meaning the sound will appear to play 2*5.5 = 11 times too fast.

Make sure to write all the samples to the output, and record at 44.1 kHz (or upsample as described above), and it should play back at the right speed.

On a sidenote, event.data.writeBytes is likely to be a lot faster for copying the buffer (assumption - haven't actually checked):

_lastRecord = new ByteArray();
_lastRecord.writeBytes(_buffer, 0, _buffer.bytesAvailable);

Same for writing the sample data:

public function handlePlaybackSampleData(p_sampleEvent:SampleDataEvent) :void 
{
   // Maximum amount of samples we're allowed to write: 8192
   // Copying bytes here, rather than floats, so:
   // 8192 samples * 2 channels * 4 bytes per float = 65536:
   var bytesToCopy:int = Math.min(_lastRecord.bytesAvailable, 65536);

   // Using _lastRecord.position since some of it may have been written on the
   // last call to handlePlaybackSampleData if it had more than 8192 samples:
   p_sampleEvent.data.writeBytes(
      _lastRecord, 
      _lastRecord.position, 
      bytesToCopy
   );
}