`startRecorder` doesn't seem to access Android native code
See original GitHub issueVersion 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
- Create a new flutter project
- Add
flutter_sound: 2.0.0
inpubspec.yaml
- Add code in
main.dart
such that it callsstartRecorder
(see code below) - 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!
Issue Analytics
- State:
- Created 4 years ago
- Reactions:1
- Comments:12
Top GitHub Comments
Just checked the example project and it works. But as soon as I replace
with
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.