flutter_sound.dart 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308
  1. import 'dart:async';
  2. import 'dart:core';
  3. import 'dart:convert';
  4. import 'package:flutter/services.dart';
  5. import 'package:flutter_sound/android_encoder.dart';
  6. import 'package:flutter_sound/ios_quality.dart';
  7. class FlutterSound {
  8. static const MethodChannel _channel = const MethodChannel('flutter_sound');
  9. static StreamController<RecordStatus> _recorderController;
  10. static StreamController<double> _dbPeakController;
  11. static StreamController<PlayStatus> _playerController;
  12. /// Value ranges from 0 to 120
  13. Stream<double> get onRecorderDbPeakChanged => _dbPeakController.stream;
  14. Stream<RecordStatus> get onRecorderStateChanged => _recorderController.stream;
  15. Stream<PlayStatus> get onPlayerStateChanged => _playerController.stream;
  16. bool get isPlaying => _isPlaying;
  17. bool get isRecording => _isRecording;
  18. bool _isRecording = false;
  19. bool _isPlaying = false;
  20. Future<String> setSubscriptionDuration(double sec) async {
  21. String result = await _channel
  22. .invokeMethod('setSubscriptionDuration', <String, dynamic>{
  23. 'sec': sec,
  24. });
  25. return result;
  26. }
  27. Future<void> _setRecorderCallback() async {
  28. if (_recorderController == null) {
  29. _recorderController = new StreamController.broadcast();
  30. }
  31. if (_dbPeakController == null) {
  32. _dbPeakController = new StreamController.broadcast();
  33. }
  34. _channel.setMethodCallHandler((MethodCall call) {
  35. switch (call.method) {
  36. case "updateRecorderProgress":
  37. Map<String, dynamic> result = json.decode(call.arguments);
  38. if (_recorderController != null)
  39. _recorderController.add(new RecordStatus.fromJSON(result));
  40. break;
  41. case "updateDbPeakProgress":
  42. if (_dbPeakController!= null)
  43. _dbPeakController.add(call.arguments);
  44. break;
  45. default:
  46. throw new ArgumentError('Unknown method ${call.method} ');
  47. }
  48. return null;
  49. });
  50. }
  51. Future<void> _setPlayerCallback() async {
  52. if (_playerController == null) {
  53. _playerController = new StreamController.broadcast();
  54. }
  55. _channel.setMethodCallHandler((MethodCall call) {
  56. switch (call.method) {
  57. case "updateProgress":
  58. Map<String, dynamic> result = jsonDecode(call.arguments);
  59. if (_playerController!=null)
  60. _playerController.add(new PlayStatus.fromJSON(result));
  61. break;
  62. case "audioPlayerDidFinishPlaying":
  63. Map<String, dynamic> result = jsonDecode(call.arguments);
  64. PlayStatus status = new PlayStatus.fromJSON(result);
  65. if (status.currentPosition != status.duration) {
  66. status.currentPosition = status.duration;
  67. }
  68. if (_playerController != null)
  69. _playerController.add(status);
  70. this._isPlaying = false;
  71. _removePlayerCallback();
  72. break;
  73. default:
  74. throw new ArgumentError('Unknown method ${call.method}');
  75. }
  76. return null;
  77. });
  78. }
  79. Future<void> _removeRecorderCallback() async {
  80. if (_recorderController != null) {
  81. _recorderController
  82. ..add(null)
  83. ..close();
  84. _recorderController = null;
  85. }
  86. }
  87. Future<void> _removeDbPeakCallback() async {
  88. if (_dbPeakController != null) {
  89. _dbPeakController
  90. ..add(null)
  91. ..close();
  92. _dbPeakController = null;
  93. }
  94. }
  95. Future<void> _removePlayerCallback() async {
  96. if (_playerController != null) {
  97. _playerController
  98. ..add(null)
  99. ..close();
  100. _playerController = null;
  101. }
  102. }
  103. Future<String> startRecorder(String uri,
  104. {int sampleRate, int numChannels, int bitRate,
  105. AndroidEncoder androidEncoder = AndroidEncoder.AAC,
  106. AndroidAudioSource androidAudioSource = AndroidAudioSource.MIC,
  107. AndroidOutputFormat androidOutputFormat = AndroidOutputFormat.MPEG_4,
  108. IosQuality iosQuality = IosQuality.LOW,
  109. }) async {
  110. if (this._isRecording) {
  111. throw new RecorderRunningException('Recorder is already recording.');
  112. }
  113. try {
  114. String result =
  115. await _channel.invokeMethod('startRecorder', <String, dynamic>{
  116. 'path': uri,
  117. 'sampleRate': sampleRate,
  118. 'numChannels': numChannels,
  119. 'bitRate': bitRate,
  120. 'androidEncoder': androidEncoder?.value,
  121. 'androidAudioSource': androidAudioSource?.value,
  122. 'androidOutputFormat': androidOutputFormat?.value,
  123. 'iosQuality': iosQuality?.value
  124. });
  125. _setRecorderCallback();
  126. this._isRecording = true;
  127. return result;
  128. } catch (err) {
  129. throw new Exception(err);
  130. }
  131. }
  132. Future<String> stopRecorder() async {
  133. if (!this._isRecording) {
  134. throw new RecorderStoppedException('Recorder is already stopped.');
  135. }
  136. String result = await _channel.invokeMethod('stopRecorder');
  137. this._isRecording = false;
  138. _removeRecorderCallback();
  139. _removeDbPeakCallback();
  140. return result;
  141. }
  142. Future<String> startPlayer(String uri) async {
  143. if (this._isPlaying) {
  144. this.resumePlayer();
  145. return 'Player resumed';
  146. // throw PlayerRunningException('Player is already playing.');
  147. }
  148. try {
  149. String result =
  150. await _channel.invokeMethod('startPlayer', <String, dynamic>{
  151. 'path': uri,
  152. });
  153. print('startPlayer result: $result');
  154. _setPlayerCallback();
  155. this._isPlaying = true;
  156. return result;
  157. } catch (err) {
  158. throw Exception(err);
  159. }
  160. }
  161. Future<String> stopPlayer() async {
  162. if (!this._isPlaying) {
  163. throw PlayerStoppedException('Player already stopped.');
  164. }
  165. this._isPlaying = false;
  166. String result = await _channel.invokeMethod('stopPlayer');
  167. _removePlayerCallback();
  168. return result;
  169. }
  170. Future<String> pausePlayer() async {
  171. try {
  172. String result = await _channel.invokeMethod('pausePlayer');
  173. return result;
  174. } catch (err) {
  175. print('err: $err');
  176. return err;
  177. }
  178. }
  179. Future<String> resumePlayer() async {
  180. try {
  181. String result = await _channel.invokeMethod('resumePlayer');
  182. return result;
  183. } catch (err) {
  184. print('err: $err');
  185. return err;
  186. }
  187. }
  188. Future<String> seekToPlayer(int milliSecs) async {
  189. try {
  190. String result =
  191. await _channel.invokeMethod('seekToPlayer', <String, dynamic>{
  192. 'sec': milliSecs,
  193. });
  194. return result;
  195. } catch (err) {
  196. print('err: $err');
  197. return err;
  198. }
  199. }
  200. Future<String> setVolume(double volume) async {
  201. String result = '';
  202. if (volume < 0.0 || volume > 1.0) {
  203. result = 'Value of volume should be between 0.0 and 1.0.';
  204. return result;
  205. }
  206. result = await _channel
  207. .invokeMethod('setVolume', <String, dynamic>{
  208. 'volume': volume,
  209. });
  210. return result;
  211. }
  212. /// Defines the interval at which the peak level should be updated.
  213. /// Default is 0.8 seconds
  214. Future<String> setDbPeakLevelUpdate(double intervalInSecs) async {
  215. String result = await _channel
  216. .invokeMethod('setDbPeakLevelUpdate', <String, dynamic>{
  217. 'intervalInSecs': intervalInSecs,
  218. });
  219. return result;
  220. }
  221. /// Enables or disables processing the Peak level in db's. Default is disabled
  222. Future<String> setDbLevelEnabled(bool enabled) async {
  223. String result = await _channel
  224. .invokeMethod('setDbLevelEnabled', <String, dynamic>{
  225. 'enabled': enabled,
  226. });
  227. return result;
  228. }
  229. }
  230. class RecordStatus {
  231. final double currentPosition;
  232. RecordStatus.fromJSON(Map<String, dynamic> json)
  233. : currentPosition = double.parse(json['current_position']);
  234. @override
  235. String toString() {
  236. return 'currentPosition: $currentPosition';
  237. }
  238. }
  239. class PlayStatus {
  240. final double duration;
  241. double currentPosition;
  242. PlayStatus.fromJSON(Map<String, dynamic> json)
  243. : duration = double.parse(json['duration']),
  244. currentPosition = double.parse(json['current_position']);
  245. @override
  246. String toString() {
  247. return 'duration: $duration, '
  248. 'currentPosition: $currentPosition';
  249. }
  250. }
  251. class PlayerRunningException implements Exception {
  252. final String message;
  253. PlayerRunningException(this.message);
  254. }
  255. class PlayerStoppedException implements Exception {
  256. final String message;
  257. PlayerStoppedException(this.message);
  258. }
  259. class RecorderRunningException implements Exception {
  260. final String message;
  261. RecorderRunningException(this.message);
  262. }
  263. class RecorderStoppedException implements Exception {
  264. final String message;
  265. RecorderStoppedException(this.message);
  266. }