Skip to content

Recorded audio playback no longer audible after few seconds #1207

@gbaranedavid

Description

@gbaranedavid

When I start recording, the audio record and playback starts smooth but after a few seconds, my voice or recoded sound playback starts lagging and eventually stops after a few seconds(about 4 seconds after), despite the recording indicator showing that recording is still in progress. How do I make the recording and playback continuous without audio decay after a short while?

so far, I have reduced the buffer size and sample rate to minimal as I suspected that it might be a resource problem but issue still persist.

Here is my code below:

note: I have used the taudio upgrade of flutter_sound. please pardon me for making this post her as I observed that issue raised on taudio repo are barely responded.

thanks in advance.

/*
 * Copyright 2018, 2019, 2020, 2021 Dooboolab.
 *
 * This file is part of Flutter-Sound.
 *
 * Flutter-Sound is free software: you can redistribute it and/or modify
 * it under the terms of the Mozilla Public License version 2 (MPL2.0),
 * as published by the Mozilla organization.
 *
 * Flutter-Sound is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * MPL General Public License for more details.
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

import 'dart:async';
import 'package:flutter/foundation.dart' show kIsWeb, Uint8List;
import 'package:flutter/material.dart';
import 'package:taudio/taudio.dart';
import 'package:permission_handler/permission_handler.dart';
import 'package:audio_session/audio_session.dart';

/*

This is a very simple example for Flutter Sound beginners,
hat shows how to record, and then playback a file.

This example is really basic.

 */
void main() {
  runApp(const MaterialApp(
    home: PlayFromMic(),
  ));
}
typedef _Fn = void Function();

const theSource = AudioSource.microphone;

// Example app.
class PlayFromMic extends StatefulWidget {
  const PlayFromMic({super.key});

  @override
  State<PlayFromMic> createState() => _PlayFromMic();
}

class _PlayFromMic extends State<PlayFromMic> {
  /// Our player
  final FlutterSoundPlayer _mPlayer = FlutterSoundPlayer();

  /// Our recorder
  final FlutterSoundRecorder _mRecorder = FlutterSoundRecorder();


  var recordingDataControllerUint8 = StreamController<Uint8List>();


  Future<void> init() async {
    final session = await AudioSession.instance;
    await session.configure(AudioSessionConfiguration(
      avAudioSessionCategory: AVAudioSessionCategory.playAndRecord,
      avAudioSessionCategoryOptions:
      AVAudioSessionCategoryOptions.allowBluetooth |
      AVAudioSessionCategoryOptions.defaultToSpeaker,
      avAudioSessionMode: AVAudioSessionMode.spokenAudio,
      avAudioSessionRouteSharingPolicy:
      AVAudioSessionRouteSharingPolicy.defaultPolicy,
      avAudioSessionSetActiveOptions: AVAudioSessionSetActiveOptions.none,
      androidAudioAttributes: const AndroidAudioAttributes(
        contentType: AndroidAudioContentType.speech,
        flags: AndroidAudioFlags.none,
        usage: AndroidAudioUsage.voiceCommunication,
      ),
      androidAudioFocusGainType: AndroidAudioFocusGainType.gain,
      androidWillPauseWhenDucked: true,
    ));

    _mPlayer.openPlayer().then((value) {
      setState(() {
        _mPlayerIsInited = true;
      });
    });

    openTheRecorder().then((value) {
      setState(() {
        _mRecorderIsInited = true;
      });
    });
  }

  @override
  void initState() {
    super.initState();
    init();
  }

  @override
  void dispose() {
    _mPlayer.closePlayer();

    _mRecorder.closeRecorder();
    super.dispose();
  }

  // ------------------------------ This is the recorder stuff -----------------------------

  static const Codec _codec = Codec.pcm16;
  static const int _sampleRate = 16000;

  bool _mRecorderIsInited = false;
  double _dbLevel = 0.0;
  StreamSubscription? _recorderSubscription;
  bool bNoiseSuppression = false;
  bool bEchoCancellation = false;

  /// Request permission to record something and open the recorder
  Future<void> openTheRecorder() async {
    if (!kIsWeb) {
      var status = await Permission.microphone.request();
      if (status != PermissionStatus.granted) {
        throw RecordingPermissionException('Microphone permission not granted');
      }
    }
    await _mRecorder.openRecorder();

    /*_recorderSubscription = */ _mRecorder.onProgress!.listen((e) {
      // pos = e.duration.inMilliseconds; // We do not need this information in this example.
      setState(() {
        _dbLevel = e.decibels as double;
      });
    });
    await _mRecorder.setSubscriptionDuration(
        const Duration(milliseconds: 100)); // DO NOT FORGET THIS CALL !!!

    _mRecorderIsInited = true;
  }

  /// Begin to record.
  /// This is our main function.
  /// We ask Flutter Sound to record to a File.
  int count = 0;
  void record() async {
    try {
      assert(_mPlayerIsInited && _mRecorder.isStopped && _mPlayer.isStopped);

      await _mPlayer.startPlayerFromStream(
        codec: _codec,
        sampleRate: _sampleRate,
        interleaved: true,
        bufferSize: 256,
        numChannels: 2,

      );

      await _mRecorder.startRecorder(
        codec: _codec,
        audioSource: theSource,
        //toStreamFloat32: _mPlayer.float32Sink,
        toStream: recordingDataControllerUint8,
        sampleRate: _sampleRate,
        numChannels: 2,
        enableNoiseSuppression: bNoiseSuppression,
        enableEchoCancellation: bEchoCancellation,

      );
      recordingDataControllerUint8.stream.listen((Uint8List buf) {

        //In actual production usage, Uint8List buf will be provided by websocket for _player.feedUint8FromStream(buf); on receiver end
        if(buf!=null){
          //_mPlayer.feedUint8FromStream(buf);
          _mPlayer.uint8ListSink!.add(buf);
        }else{
          print("====$count=======");
          count++;
        }

      },
        onError: (error) {
          print('Recorder error: $error');
        },
        onDone: () {
          print('Recorder stopped');
        },);
      setState(() {});


    } catch (e) {
      print('AAAAAAAAAAAAAAAAAAA----------Error starting recorder: $e -----------------BBBBBBBBBBBBBBBBBB------');
    }
  }

  /// Stop the recorder
  void stopRecorder() async {
    await _mPlayer.stopPlayer();
    await _mRecorder.stopRecorder().then((value) {
      setState(() {
        //var url = value;
      });
    });
  }

// ----------------------------- This is the player stuff ---------------------------------

  bool _mPlayerIsInited = false;

  /// Begin to play the recorded sound
  void play() {}

  /// Stop the player
  void stopPlayer() {
    _mPlayer.stopPlayer().then((value) {
      setState(() {});
    });
  }

// ----------------------------- UI --------------------------------------------

  // The user changed its selection. Reset the 3 buffers
  Future<void> reinit() async {
    await _mPlayer.stopPlayer();
    await _mRecorder.stopRecorder();
    setState(() {});
  }

  _Fn? getRecorderFn() {
    if (!_mRecorderIsInited || !_mPlayerIsInited) {
      return null;
    }
    return _mRecorder.isStopped ? record : stopRecorder;
  }

  @override
  Widget build(BuildContext context) {
    Widget makeBody() {
      return Column(
        children: [
          Container(
            margin: const EdgeInsets.all(3),
            padding: const EdgeInsets.all(3),
            height: 200,
            width: double.infinity,
            alignment: Alignment.center,
            decoration: BoxDecoration(
              color: const Color(0xFFFAF0E6),
              border: Border.all(
                color: Colors.indigo,
                width: 3,
              ),
            ),
            child: Column(children: [
              Row(children: [
                ElevatedButton(
                  onPressed: getRecorderFn(),
                  //color: Colors.white,
                  //disabledColor: Colors.grey,
                  child: Text(_mRecorder.isRecording ? 'Stop' : 'Record'),
                ),
                const SizedBox(
                  width: 20,
                ),
                Text(_mRecorder.isRecording
                    ? 'Recording in progress'
                    : 'Recorder is stopped'),
              ]),
              const SizedBox(
                height: 20,
              ),
              _mRecorder.isRecording
                  ? LinearProgressIndicator(
                  value: _dbLevel / 100,
                  valueColor:
                  const AlwaysStoppedAnimation<Color>(Colors.indigo),
                  backgroundColor: Colors.limeAccent)
                  : Container(),
              CheckboxListTile(
                tileColor: const Color(0xFFFAF0E6),
                title: const Text("Noise Suppression"),
                value: bNoiseSuppression,
                onChanged: (newValue) {
                  reinit();
                  setState(() {
                    bNoiseSuppression = newValue!;
                  });
                },
              ),
              CheckboxListTile(
                tileColor: const Color(0xFFFAF0E6),
                title: const Text("Echo Cancellation"),
                value: bEchoCancellation,
                onChanged: (newValue) {
                  reinit();
                  setState(() {
                    bEchoCancellation = newValue!;
                  });
                },
              ),
            ]),
          ),
        ],
      );
    }

    return Scaffold(
      backgroundColor: Colors.blue,
      appBar: AppBar(
        title: const Text('Play from Mic'),
      ),
      body: makeBody(),
    );
  }
}

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions