Tau: `startRecorder` doesn't seem to access Android native code

Created on 15 Jan 2020  路  12Comments  路  Source: Canardoux/tau

Version of flutter_sound

1.4.2 up to 2.0.0. Haven't tested in previous versions.

flutter doctor

[v] Flutter (Channel stable, v1.12.13+hotfix.5, on Microsoft Windows [Version 10.0.16299.15],
locale en-US)
[v] Android toolchain - develop for Android devices (Android SDK version 29.0.0)
[v] Android Studio (version 3.4)
[v] VS Code, 64-bit edition (version 1.35.0)
[v] Connected device (1 available)

Platforms you faced the error (IOS or Android or both?)

Android. Haven't thoroughly tested in iOS, however it doesn't record as well.

Expected behavior

Upon call of FlutterSound#startRecorder, future should resolve and FlutterSound.onRecorderStateChanged stream should be active.

Actual behavior

FlutterSound#startRecorder future doesnt resolve and will await forever because it gets stuck at _channel.invokeMethod('startRecorder'...); https://github.com/dooboolab/flutter_sound/blob/f0054692f83e5d3ab520ebe07ab4924b2bcaa1e5/lib/flutter_sound.dart#L170

Tested environment (Emulator? Real Device?)

  • Emulators with Android SDK 28 and 29
  • Samsung Galaxy S6 Edge (Android version 7.0)

Steps to reproduce the behavior

  1. Create a new flutter project
  2. Add flutter_sound: 2.0.0 in pubspec.yaml
  3. Add code in main.dart such that it calls startRecorder (see code below)
  4. Press button that calls startRecorder to start recording

main.dart:

import 'dart:async';

import 'package:flutter/material.dart';
import 'package:flutter_sound/android_encoder.dart';
import 'package:flutter_sound/flutter_sound.dart';
import 'package:intl/intl.dart';

void main() => runApp(MyApp());

class MyApp extends StatelessWidget {
  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Flutter Demo',
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: MyHomePage(title: 'Flutter Demo Home Page'),
    );
  }
}

class MyHomePage extends StatefulWidget {
  MyHomePage({Key key, this.title}) : super(key: key);

  final String title;

  @override
  _MyHomePageState createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  bool _isRecording = false;
  List<String> _path = [null, null, null, null, null, null, null];
  StreamSubscription _recorderSubscription;
  StreamSubscription _dbPeakSubscription;
  StreamSubscription _playerSubscription;
  FlutterSound flutterSound;

  String _recorderTxt = '00:00:00';
  double _dbLevel;

  @override
  void initState() {
    super.initState();
    flutterSound = FlutterSound();
    flutterSound.setSubscriptionDuration(0.01);
    flutterSound.setDbPeakLevelUpdate(0.8);
    flutterSound.setDbLevelEnabled(true);
  }

  static const List<String> paths = [
    'sound.aac', // DEFAULT
    'sound.aac', // CODEC_AAC
    'sound.opus', // CODEC_OPUS
    'sound.caf', // CODEC_CAF_OPUS
    'sound.mp3', // CODEC_MP3
    'sound.ogg', // CODEC_VORBIS
    'sound.wav', // CODEC_PCM
  ];
  t_CODEC _codec = t_CODEC.CODEC_AAC;

  void startRecorder() async {
    print('start recorder');
    try {
      String path = await flutterSound.startRecorder(
        paths[_codec.index],
        codec: _codec,
        sampleRate: 16000,
        bitRate: 16000,
        numChannels: 1,
        androidAudioSource: AndroidAudioSource.MIC,
      );
      print('startRecorder: $path');

      _recorderSubscription = flutterSound.onRecorderStateChanged.listen((e) {
        DateTime date = new DateTime.fromMillisecondsSinceEpoch(
            e.currentPosition.toInt(),
            isUtc: true);
        String txt = DateFormat('mm:ss:SS', 'en_GB').format(date);

        this.setState(() {
          this._recorderTxt = txt.substring(0, 8);
        });
      });
      _dbPeakSubscription =
          flutterSound.onRecorderDbPeakChanged.listen((value) {
        print("got update -> $value");
        setState(() {
          this._dbLevel = value;
        });
      });

      this.setState(() {
        this._isRecording = true;
        this._path[_codec.index] = path;
      });
    } catch (err) {
      print('startRecorder error: $err');
      setState(() {
        this._isRecording = false;
      });
    }
  }

  void stopRecorder() async {
    print('stop recorder');
    try {
      String result = await flutterSound.stopRecorder();
      print('stopRecorder: $result');

      if (_recorderSubscription != null) {
        _recorderSubscription.cancel();
        _recorderSubscription = null;
      }
      if (_dbPeakSubscription != null) {
        _dbPeakSubscription.cancel();
        _dbPeakSubscription = null;
      }
    } catch (err) {
      print('stopRecorder error: $err');
    }
    this.setState(() {
      this._isRecording = false;
    });
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: SafeArea(
        child: Column(
          children: <Widget>[
            FlatButton(
              child: Text(_isRecording.toString()),
              onPressed: _isRecording ? stopRecorder : startRecorder,
            ),
            Text(_recorderTxt)
          ],
        ),
      ),
    );
  }
}

Thank you!

bug

Most helpful comment

Just checked the example project and it works. But as soon as I replace

String path = await flutterSound.startRecorder
      (
        paths[_codec.index],
        codec: _codec,
        sampleRate: 16000,
        bitRate: 16000,
        numChannels: 1,
        androidAudioSource: AndroidAudioSource.MIC,
      );

with

String path = await flutterSound.startRecorder(null);

nothing happens when pressing the recording button.It looks like some of the assumed default values aren't valid on my device (Samsung S7 on Android 8.0.0).

All 12 comments

Somehow, changing some static values in the android code fixes it (for example, changing MethodChannel and Registrar from static to instance variable). Not sure why though. Probably classes resets some cache? Not sure. It worked before when Flutter version was 1.9.x. But now that we upgraded Flutter to 1.12.13, it doesn't work anymore.

Probably the same problem as mine https://github.com/dooboolab/flutter_sound/issues/192

@Boehrsi any workarounds for this? Can't record. I can't play audio either.

Note by the way that the example project works. I'm not sure why it doesn't work when implemented outside of the example project.

@SkycladObserver For the time being I just reverted to flutter_sound: 1.6.0 / 1.7.0 which were the last working versions for me.

The problem seems to be related to the checks if a codec is supported. My devices somehow report something back the plugin doesn't like, so everything is canceled.

Could you all try out for example project and see if it's working on your env? The example is working fine for me.

Just checked the example project and it works. But as soon as I replace

String path = await flutterSound.startRecorder
      (
        paths[_codec.index],
        codec: _codec,
        sampleRate: 16000,
        bitRate: 16000,
        numChannels: 1,
        androidAudioSource: AndroidAudioSource.MIC,
      );

with

String path = await flutterSound.startRecorder(null);

nothing happens when pressing the recording button.It looks like some of the assumed default values aren't valid on my device (Samsung S7 on Android 8.0.0).

I think this must be the issue i was having in #191

I was also using String savePath = await _flutterSound.startRecorder(null); and the recording was never starting - though I only got an exception when I ran String recordingFileLocation = await _flutterSound.stopRecorder(); as @hyochan indicated, because that happens when recording is already stopped.

Just checked the example project and it works. But as soon as I replace

String path = await flutterSound.startRecorder
      (
        paths[_codec.index],
        codec: _codec,
        sampleRate: 16000,
        bitRate: 16000,
        numChannels: 1,
        androidAudioSource: AndroidAudioSource.MIC,
      );

with

String path = await flutterSound.startRecorder(null);

nothing happens when pressing the recording button.It looks like some of the assumed default values aren't valid on my device (Samsung S7 on Android 8.0.0).

Thanks for trying out~! We've narrowed down the problem so I'll look up at the weekend.

@mdrideout looks like the same problem, yep.

@hyochan thank you. If I can help with more information, e.g. some more native code debugging or similar just mention me 馃憤 .

The default params weren't set properly so I've just fixed this and released to 2.0.1.
Also I've removed putting null since this is useless.
You can just simply run startRecorder() for the default case.

Can confirm it's working again 馃憤 . Thanks.

@hyochan hello, thank you for noticing this bug! And sorry for late response, was only able to attend to this now.

I still experience the problem. I tried the example, and it works, but in my project, it doesn't.

I've narrowed it down. I've made a new ticket though since it's slightly different from this issue. https://github.com/dooboolab/flutter_sound/issues/198

Thanks everyone!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ggirotto picture ggirotto  路  3Comments

Denmal1982 picture Denmal1982  路  4Comments

gtilson picture gtilson  路  5Comments

kiha-la picture kiha-la  路  5Comments

Larpoux picture Larpoux  路  4Comments