Android .Media.MediaRecorder - задержка при записи звука, с разделами тишины - PullRequest
0 голосов
/ 01 февраля 2020

Пожалуйста, помогите решить проблему. Для записи аудио файла я использую Android .Media.MediaRecorder. В результате я получаю раздел с молчанием 650 миллисекунд в начале и в конце файла. И получается, что часть фразы в начале и в конце не записана, только тишина.

public static async Task Record(CancellationToken token)
    {
        try
        {
            var filename = System.Environment.GetFolderPath(System.Environment.SpecialFolder.Personal) + "/temp.amr";
            var tmp = new Java.IO.File(filename);
            if (tmp.Exists()) tmp.Delete();
            var recorder = new MediaRecorder();
            recorder.SetAudioSource(AudioSource.Mic);
            recorder.SetOutputFormat(OutputFormat.AmrNb);
            recorder.SetAudioEncoder(AudioEncoder.AmrNb);
            recorder.SetOutputFile(filename);
            recorder.Prepare();
            recorder.Start();
            try
            {
                var div = MAX_RECORD_TIME / 100;
                for(int i=0; i<div; i++)
                {
                    IncrementProgressBar(100 / div);
                    await Task.Delay(div, token);
                }
                fSave = true;
            }
            catch (OperationCanceledException) { }
            recorder.Stop();
        }
        catch (Exception e)
        {
            string s = context.GetText(Resource.String.err_unexpected) + "\r" + e.Message;
            handler.ToastMessage(s, ToastLength.Short);
        }
    }

Ответы [ 2 ]

1 голос
/ 03 февраля 2020

Эта проблема не может быть решена с помощью кода, похоже, это аппаратная проблема. Вы можете увидеть ту же задержку в приложении камеры по умолчанию, которое также использует MediaRecorder. К сожалению, некоторые устройства будут вести себя по-разному.

Решение состоит в том, чтобы использовать класс AudioRecorder, который гораздо более гибкий, так как он ближе к аппаратному

0 голосов
/ 04 февраля 2020

Нашел решение для себя. Следующий код работает, это может быть кому-то полезно.

    public static async Task Record(CancellationToken token)
    {
        var filename = Environment.GetFolderPath(System.Environment.SpecialFolder.Personal) + "/wav.pcm";
        System.IO.Stream outputStream = System.IO.File.Open(filename, FileMode.Create);
        BinaryWriter bWriter = new BinaryWriter(outputStream);
        var audioBuffer = new byte[8000];
        var audRecorder = new AudioRecord(AudioSource.Mic, 8000, ChannelIn.Mono, Encoding.Pcm16bit, audioBuffer.Length);
        while(audRecorder.State != State.Initialized) { }
        audRecorder.StartRecording();
        while (!token.IsCancellationRequested)
        {
            try
            {
                var audioData = await audRecorder.ReadAsync(audioBuffer, 0, audioBuffer.Length);
                bWriter.Write(audioBuffer);
                IncrementProgressBar(1);
            }
            catch (Exception ex)
            {
                System.Console.Out.WriteLine("INFO: " + ex.Message);
                break;
            }
        }
        outputStream.Close();
        bWriter.Close();
        audRecorder.Stop();
        audRecorder.Release();
        fSave = true;
    }
    public static async Task Play(CancellationToken token)
    {
        var filename = Environment.GetFolderPath(Environment.SpecialFolder.Personal) + "/wav.pcm";
        var audBuffer = await System.IO.File.ReadAllBytesAsync(filename, token);
        var bufferSizeInBytes = audBuffer.Length / 2;
        AudioTrack audioTrack;
        if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Lollipop)
        {
            AudioAttributes attributes = new AudioAttributes.Builder().SetLegacyStreamType(Android.Media.Stream.Music)
                                                                    .Build();
            AudioFormat format = new AudioFormat.Builder().SetChannelMask(ChannelOut.Mono)
                                                            .SetEncoding(Encoding.Pcm16bit)
                                                            .SetSampleRate(8000)
                                                            .Build();
            audioTrack = new AudioTrack(attributes, format, bufferSizeInBytes, AudioTrackMode.Stream, 1);
        }
        else
        {
            audioTrack = new AudioTrack(Android.Media.Stream.Music, 8000, ChannelOut.Mono,
                                        Encoding.Pcm16bit, bufferSizeInBytes, AudioTrackMode.Stream);
        }
        audioTrack.Play();
        await audioTrack.WriteAsync(audBuffer, 0, bufferSizeInBytes*2);
        try
        {
            await Task.Delay(((bufferSizeInBytes/8000)-1)*1000, token);
        }
        catch (OperationCanceledException) { }
        audioTrack.Stop();
        audioTrack.Release();
    }      
    public static async Task SaveAsAmr()
    {
        var encoder = MediaCodec.CreateEncoderByType(MediaFormat.MimetypeAudioAmrNb);
        MediaFormat format = new MediaFormat();
        format.SetString(MediaFormat.KeyMime, MediaFormat.MimetypeAudioAmrNb);
        format.SetInteger(MediaFormat.KeySampleRate, 8000);
        format.SetInteger(MediaFormat.KeyChannelCount, 1);
        format.SetInteger(MediaFormat.KeyBitRate, 7936);
        encoder.Configure(format, null, null, MediaCodecConfigFlags.Encode);

        try
        {
            Java.IO.File pcmFile = new Java.IO.File(Environment.GetFolderPath(Environment.SpecialFolder.Personal) + "/wav.pcm");
            FileInputStream fis = new FileInputStream(pcmFile);
            Java.IO.File armFIle = new Java.IO.File(Environment.GetFolderPath(Environment.SpecialFolder.Personal) + "/wav.amr");
            FileOutputStream fos = new FileOutputStream(armFIle);
            fos.Write(System.Text.Encoding.ASCII.GetBytes("#!AMR\n"));
            encoder.Start();

            var tempBuffer = new byte[8000];
            var hasMoreData = true;
            MediaCodec.BufferInfo outBuffInfo = new MediaCodec.BufferInfo();
            double presentationTimeUs = 0;
            int totalBytesRead = 0;

            do
            {
                int inputBufIndex = 0;
                while (inputBufIndex != -1 && hasMoreData)
                {
                    inputBufIndex = encoder.DequeueInputBuffer(0);
                    if (inputBufIndex >= 0)
                    {
                        Java.Nio.ByteBuffer dstBuf;
                        if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Lollipop)
                        {
                            dstBuf = encoder.GetInputBuffer(inputBufIndex);
                        }
                        else
                        {
                            dstBuf = encoder.GetInputBuffers()[inputBufIndex];
                        }
                        dstBuf.Clear();

                        int bytesRead = await fis.ReadAsync(tempBuffer, 0, dstBuf.Limit());
                        if (bytesRead == -1)
                        { // -1 implies EOS
                            hasMoreData = false;
                            encoder.QueueInputBuffer(inputBufIndex, 0, 0, (long)presentationTimeUs, MediaCodecBufferFlags.EndOfStream);
                        }
                        else
                        {
                            totalBytesRead += bytesRead;
                            dstBuf.Put(tempBuffer, 0, bytesRead);
                            encoder.QueueInputBuffer(inputBufIndex, 0, bytesRead, (long)presentationTimeUs, 0);
                            presentationTimeUs = 1000000L * (totalBytesRead / 2) / 8000;
                        }
                    }
                }
                // Drain audio
                int outputBufIndex = 0;
                while (outputBufIndex != (int)MediaCodecInfoState.TryAgainLater)
                {
                    outputBufIndex = encoder.DequeueOutputBuffer(outBuffInfo, 0);
                    if (outputBufIndex >= 0)
                    {
                        Java.Nio.ByteBuffer encodedData;
                        if (Android.OS.Build.VERSION.SdkInt >= Android.OS.BuildVersionCodes.Lollipop)
                        {
                            encodedData = encoder.GetOutputBuffer(outputBufIndex);
                        }
                        else
                        {
                            encodedData = encoder.GetOutputBuffers()[outputBufIndex];
                        }
                        encodedData.Position(outBuffInfo.Offset);
                        encodedData.Limit(outBuffInfo.Offset + outBuffInfo.Size);
                        byte[] outData = new byte[outBuffInfo.Size];
                        encodedData.Get(outData, 0, outBuffInfo.Size);
                        fos.Write(outData, 0, outBuffInfo.Size);
                        encoder.ReleaseOutputBuffer(outputBufIndex, false);
                    }
                }
            } while (outBuffInfo.Flags != MediaCodecBufferFlags.EndOfStream);
            fis?.Close();
            fos?.Flush();
            fos?.Close();
        }
        catch(Exception ex)
        {
            System.Console.Out.WriteLine("INFO: " + ex.Message);
        }
    }
...