Flutter/Package

[ Flutter ] [ Package ] flutter_sound

Design-loving front-end engineer 2022. 2. 4. 09:33

Pub.dev

https://pub.dev/packages/flutter_sound

 

flutter_sound | Flutter Package

A complete api for audio playback and recording. Audio player, audio recorder, media player, media recorder, sound player, sound recorder.

pub.dev

 

pubspec.yaml

더보기
더보기
environment:
  sdk: '>=2.12.0 <3.0.0'
  flutter: ">=1.12.0"

dependencies:
  flutter:
    sdk: flutter

  flutter_sound: ^9.1.2

  cupertino_icons: ^1.0.2
  path_provider: ^2.0.2
  permission_handler: ^6.0.1
  path: ^1.6.4
  #flutter_ffmpeg: ^0.3.1
  intl: ^0.17.0
  just_audio: ^0.9.11
  audio_session: ^0.1.6

dev_dependencies:
 # flutter_test:
#    sdk: flutter
  pedantic: ^1.11.0
  #effective_dart: ^1.3.0

  #display.save( displayObject, options )effective_dart: ^1.0.0
  #lint: ^1.1.1

flutter:

  # The following line ensures that the Material Icons font is
  # included with your application, so that you can use the icons in
  # the material Icons class.
  uses-material-design: true

  # To add assets to your application, add an assets section, like this:
  assets:
  - res/icons/ic_play.png
  - res/icons/ic_play_disabled.png
  - res/icons/2.0x/ic_play.png
  - res/icons/3.0x/ic_play.png
  - res/icons/ic_stop.png
  - res/icons/ic_stop_disabled.png
  - res/icons/2.0x/ic_stop.png
  - res/icons/3.0x/ic_stop.png
  - res/icons/ic_pause.png
  - res/icons/ic_pause_disabled.png
  - res/icons/2.0x/ic_pause.png
  - res/icons/3.0x/ic_pause.png
  - res/icons/ic_mic.png
  - res/icons/ic_mic_disabled.png
  - res/icons/2.0x/ic_mic.png
  - res/icons/3.0x/ic_mic.png
  - res/icons/ic_volume_down.png
  - res/icons/2.0x/ic_volume_down.png
  - res/icons/3.0x/ic_volume_down.png
  - res/icons/ic_volume_up.png
  - res/icons/2.0x/ic_volume_up.png
  - res/icons/3.0x/ic_volume_up.png

  - assets/
  - assets/samples/
  - assets/noises/

 

Simple Playback

💬  간단한 사운드 재생 앱

UI

 

코드

더보기
더보기
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_sound/flutter_sound.dart';

final _exampleAudioFilePathMP3 =
    'https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3';

typedef Fn = void Function();

class SimplePlayback extends StatefulWidget {
  @override
  _SimplePlaybackState createState() => _SimplePlaybackState();
}

class _SimplePlaybackState extends State<SimplePlayback> {
  FlutterSoundPlayer? _mPlayer = FlutterSoundPlayer();
  bool _mPlayerIsInited = false;

  @override
  void initState() {
    super.initState();
    _mPlayer!.openPlayer().then((value) {
      setState(() {
        _mPlayerIsInited = true;
      });
    });
  }

  @override
  void dispose() {
    stopPlayer();
    // Be careful : you must `close` the audio session when you have finished with it.
    _mPlayer!.closePlayer();
    _mPlayer = null;

    super.dispose();
  }

  // -------  Here is the code to playback a remote file -----------------------

  void play() async {
    await _mPlayer!.startPlayer(
        fromURI: _exampleAudioFilePathMP3,
        codec: Codec.mp3,
        whenFinished: () {
          setState(() {});
        });
    setState(() {});
  }

  Future<void> stopPlayer() async {
    if (_mPlayer != null) {
      await _mPlayer!.stopPlayer();
    }
  }

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

  Fn? getPlaybackFn() {
    if (!_mPlayerIsInited) {
      return null;
    }
    return _mPlayer!.isStopped
        ? play
        : () {
            stopPlayer().then((value) => setState(() {}));
          };
  }

  @override
  Widget build(BuildContext context) {
    Widget makeBody() {
      return Column(
        children: [
          Container(
            margin: const EdgeInsets.all(3),
            padding: const EdgeInsets.all(3),
            height: 80,
            width: double.infinity,
            alignment: Alignment.center,
            decoration: BoxDecoration(
              color: Color(0xFFFAF0E6),
              border: Border.all(
                color: Colors.indigo,
                width: 3,
              ),
            ),
            child: Row(children: [
              ElevatedButton(
                onPressed: getPlaybackFn(),
                //color: Colors.white,
                //disabledColor: Colors.grey,
                child: Text(_mPlayer!.isPlaying ? 'Stop' : 'Play'),
              ),
              SizedBox(
                width: 20,
              ),
              Text(_mPlayer!.isPlaying
                  ? 'Playback in progress'
                  : 'Player is stopped'),
            ]),
          ),
        ],
      );
    }

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

 

Multi Playback

💬  여러 사운드 동시 재생 앱

UI

 

코드

더보기
더보기
import 'dart:async';
import 'dart:typed_data';
import 'package:intl/date_symbol_data_local.dart';
import 'package:intl/intl.dart' show DateFormat;
import 'package:flutter/material.dart';
import 'package:flutter_sound/flutter_sound.dart';
import 'package:flutter/services.dart' show rootBundle;

final _exampleAudioFilePathMP3 =
    'https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3';

typedef Fn = void Function();

class MultiPlayback extends StatefulWidget {
  @override
  _MultiPlaybackState createState() => _MultiPlaybackState();
}

class _MultiPlaybackState extends State<MultiPlayback> {
  FlutterSoundPlayer? _mPlayer1 = FlutterSoundPlayer();
  FlutterSoundPlayer? _mPlayer2 = FlutterSoundPlayer();
  FlutterSoundPlayer? _mPlayer3 = FlutterSoundPlayer();
  bool _mPlayer1IsInited = false;  // 1번 음성 파일 실행할 준비가 되었는지?
  bool _mPlayer2IsInited = false;
  bool _mPlayer3IsInited = false;
  Uint8List? buffer2;
  Uint8List? buffer3;
  String _playerTxt1 = '';
  String _playerTxt2 = '';
  String _playerTxt3 = '';
  StreamSubscription? _playerSubscription1;  // 스트림과 이벤트의 연결고리, 이벤트에 변경이 생기면 처리
  StreamSubscription? _playerSubscription2;
  StreamSubscription? _playerSubscription3;

  Future<Uint8List> _getAssetData(String path) async {  // Question
    var asset = await rootBundle.load(path);
    return asset.buffer.asUint8List();
  }

  @override
  void initState() {
    super.initState();
    initializeDateFormatting();  // Question
    _getAssetData(
      'assets/samples/sample.aac',
    ).then((value) => setState(() {
          buffer2 = value;
        }));
    _getAssetData(
      'assets/samples/sample.mp4',
    ).then((value) => setState(() {
          buffer3 = value;
        }));
    _mPlayer1!.openPlayer().then((value) {
      setState(() {
        _mPlayer1IsInited = true;
      });
    });
    _mPlayer2!.openPlayer().then((value) {
      setState(() {
        _mPlayer2IsInited = true;
      });
    });
    _mPlayer3!.openPlayer().then((value) {
      setState(() {
        _mPlayer3IsInited = true;
      });
    });
  }

  @override
  void dispose() {
    // Be careful : you must `close` the audio session when you have finished with it.
    cancelPlayerSubscriptions1();  // Question
    _mPlayer1!.closePlayer();
    _mPlayer1 = null;
    cancelPlayerSubscriptions2();
    _mPlayer2!.closePlayer();
    _mPlayer2 = null;
    cancelPlayerSubscriptions3();
    _mPlayer3!.closePlayer();
    _mPlayer3 = null;

    super.dispose();
  }

  // -------  Player1 play a remote file -----------------------

  void play1() async {  // 1번 음성 파일 재생
    await _mPlayer1!.setSubscriptionDuration(Duration(milliseconds: 10));
    _addListener1();
    await _mPlayer1!.startPlayer(
        fromURI: _exampleAudioFilePathMP3,
        codec: Codec.mp3,
        whenFinished: () {
          setState(() {});
        });
    setState(() {});
  }

  void cancelPlayerSubscriptions1() {  // 스트림 해제
    if (_playerSubscription1 != null) {
      _playerSubscription1!.cancel();
      _playerSubscription1 = null;
    }
  }

  Future<void> stopPlayer1() async {
    cancelPlayerSubscriptions1();
    if (_mPlayer1 != null) {
      await _mPlayer1!.stopPlayer();
    }
    setState(() {});
  }

  Future<void> pause1() async {
    if (_mPlayer1 != null) {
      await _mPlayer1!.pausePlayer();
    }
    setState(() {});
  }

  Future<void> resume1() async {
    if (_mPlayer1 != null) {
      await _mPlayer1!.resumePlayer();
    }
    setState(() {});
  }

  // -------  Player2 play a OPUS file -----------------------

  void play2() async {
    await _mPlayer2!.setSubscriptionDuration(Duration(milliseconds: 10));
    _addListener2();
    await _mPlayer2!.startPlayer(
        fromDataBuffer: buffer2,
        codec: Codec.aacADTS,
        whenFinished: () {
          setState(() {});
        });
    setState(() {});
  }

  void cancelPlayerSubscriptions2() {
    if (_playerSubscription2 != null) {
      _playerSubscription2!.cancel();
      _playerSubscription2 = null;
    }
  }

  Future<void> stopPlayer2() async {
    cancelPlayerSubscriptions2();
    if (_mPlayer2 != null) {
      await _mPlayer2!.stopPlayer();
    }
    setState(() {});
  }

  Future<void> pause2() async {
    if (_mPlayer2 != null) {
      await _mPlayer2!.pausePlayer();
    }
    setState(() {});
  }

  Future<void> resume2() async {
    if (_mPlayer2 != null) {
      await _mPlayer2!.resumePlayer();
    }
    setState(() {});
  }

  // -------  Player3 play a MP4 file -----------------------

  void play3() async {
    await _mPlayer3!.setSubscriptionDuration(Duration(milliseconds: 10));
    _addListener3();
    await _mPlayer3!.startPlayer(
        fromDataBuffer: buffer3,
        codec: Codec.aacMP4,
        whenFinished: () {
          setState(() {});
        });
    setState(() {});
  }

  void cancelPlayerSubscriptions3() {
    if (_playerSubscription3 != null) {
      _playerSubscription3!.cancel();
      _playerSubscription3 = null;
    }
  }

  Future<void> stopPlayer3() async {
    cancelPlayerSubscriptions3();
    if (_mPlayer3 != null) {
      await _mPlayer3!.stopPlayer();
    }
    setState(() {});
  }

  Future<void> pause3() async {
    if (_mPlayer3 != null) {
      await _mPlayer3!.pausePlayer();
    }
    setState(() {});
  }

  Future<void> resume3() async {
    if (_mPlayer3 != null) {
      await _mPlayer3!.resumePlayer();
    }
    setState(() {});
  }

  // ---------------------------------------------------------------------------

  void _addListener1() {  // 스트림 연결하여 실행되고 있는 음성 파일의 시간을 출력
    cancelPlayerSubscriptions1();
    _playerSubscription1 = _mPlayer1!.onProgress!.listen((e) {
      var date = DateTime.fromMillisecondsSinceEpoch(e.position.inMilliseconds,
          isUtc: true);
      var txt = DateFormat('mm:ss:SS', 'en_GB').format(date);
      setState(() {
        _playerTxt1 = txt.substring(0, 8);
      });
    });
  }

  Fn? getPlaybackFn1() {
    if (!_mPlayer1IsInited) {
      return null;
    }
    return _mPlayer1!.isStopped
        ? play1
        : () {
            stopPlayer1().then((value) => setState(() {}));
          };
  }

  Fn? getPauseResumeFn1() {
    if (!_mPlayer1IsInited || _mPlayer1!.isStopped) {
      return null;
    }
    return _mPlayer1!.isPaused ? resume1 : pause1;
  }

  void _addListener2() {
    cancelPlayerSubscriptions2();
    _playerSubscription2 = _mPlayer2!.onProgress!.listen((e) {
      var date = DateTime.fromMillisecondsSinceEpoch(e.position.inMilliseconds,
          isUtc: true);
      var txt = DateFormat('mm:ss:SS', 'en_GB').format(date);
      setState(() {
        _playerTxt2 = txt.substring(0, 8);
      });
    });
  }

  Fn? getPlaybackFn2() {
    if (!_mPlayer2IsInited || buffer2 == null) {
      return null;
    }
    return _mPlayer2!.isStopped
        ? play2
        : () {
            stopPlayer2().then((value) => setState(() {}));
          };
  }

  Fn? getPauseResumeFn2() {
    if (!_mPlayer2IsInited || _mPlayer2!.isStopped || buffer2 == null) {
      return null;
    }
    return _mPlayer2!.isPaused ? resume2 : pause2;
  }

  void _addListener3() {
    cancelPlayerSubscriptions3();
    _playerSubscription3 = _mPlayer3!.onProgress!.listen((e) {
      var date = DateTime.fromMillisecondsSinceEpoch(e.position.inMilliseconds,
          isUtc: true);
      var txt = DateFormat('mm:ss:SS', 'en_GB').format(date);
      setState(() {
        _playerTxt3 = txt.substring(0, 8);
      });
    });
  }

  Fn? getPlaybackFn3() {
    if (!_mPlayer3IsInited || buffer3 == null) {
      return null;
    }
    return _mPlayer3!.isStopped
        ? play3
        : () {
            stopPlayer3().then((value) => setState(() {}));
          };
  }

  Fn? getPauseResumeFn3() {
    if (!_mPlayer3IsInited || _mPlayer3!.isStopped || buffer3 == null) {
      return null;
    }
    return _mPlayer3!.isPaused ? resume3 : pause3;
  }

  @override
  Widget build(BuildContext context) {
    Widget makeBody() {
      return Column(
        children: [
          Container(
            margin: const EdgeInsets.all(3),
            padding: const EdgeInsets.all(3),
            height: 80,
            width: double.infinity,
            alignment: Alignment.center,
            decoration: BoxDecoration(
              color: Color(0xFFFAF0E6),
              border: Border.all(
                color: Colors.indigo,
                width: 3,
              ),
            ),
            child: Row(children: [
              ElevatedButton(
                onPressed: getPlaybackFn1(),
                //color: Colors.white,
                //disabledColor: Colors.grey,
                child: Text(_mPlayer1!.isStopped ? 'Play' : 'Stop'),
              ),
              SizedBox(
                width: 20,
              ),
              ElevatedButton(
                onPressed: getPauseResumeFn1(),
                //color: Colors.white,
                //disabledColor: Colors.grey,
                child: Text(_mPlayer1!.isPaused ? 'Resume' : 'Pause'),
              ),
              SizedBox(
                width: 20,
              ),
              Text(
                _playerTxt1,
                style: TextStyle(
                  color: Colors.black,
                ),
              ),
            ]),
          ),
          Container(
            margin: const EdgeInsets.all(3),
            padding: const EdgeInsets.all(3),
            height: 80,
            width: double.infinity,
            alignment: Alignment.center,
            decoration: BoxDecoration(
              color: Color(0xFFFAF0E6),
              border: Border.all(
                color: Colors.indigo,
                width: 3,
              ),
            ),
            child: Row(children: [
              ElevatedButton(
                onPressed: getPlaybackFn2(),
                //color: Colors.white,
                //disabledColor: Colors.grey,
                child: Text(_mPlayer2!.isStopped ? 'Play' : 'Stop'),
              ),
              SizedBox(
                width: 20,
              ),
              ElevatedButton(
                onPressed: getPauseResumeFn2(),
                //color: Colors.white,
                //disabledColor: Colors.grey,
                child: Text(_mPlayer2!.isPaused ? 'Resume' : 'Pause'),
              ),
              SizedBox(
                width: 20,
              ),
              Text(
                _playerTxt2,
                style: TextStyle(
                  color: Colors.black,
                ),
              ),
            ]),
          ),
          Container(
            margin: const EdgeInsets.all(3),
            padding: const EdgeInsets.all(3),
            height: 80,
            width: double.infinity,
            alignment: Alignment.center,
            decoration: BoxDecoration(
              color: Color(0xFFFAF0E6),
              border: Border.all(
                color: Colors.indigo,
                width: 3,
              ),
            ),
            child: Row(children: [
              ElevatedButton(
                onPressed: getPlaybackFn3(),
                //color: Colors.white,
                //disabledColor: Colors.grey,
                child: Text(_mPlayer3!.isStopped ? 'Play' : 'Stop'),
              ),
              SizedBox(
                width: 20,
              ),
              ElevatedButton(
                onPressed: getPauseResumeFn3(),
                //color: Colors.white,
                //disabledColor: Colors.grey,
                child: Text(_mPlayer3!.isPaused ? 'Resume' : 'Pause'),
              ),
              SizedBox(
                width: 20,
              ),
              Text(
                _playerTxt3,
                style: TextStyle(
                  color: Colors.black,
                ),
              ),
            ]),
          ),
        ],
      );
    }

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

 

Simple Recorder

💬  하이브리드, 웹 모두 구동되는 간단한 녹음 기능 앱

UI

 

코드

더보기
더보기
import 'dart:async';
import 'package:audio_session/audio_session.dart';
import 'package:flutter/foundation.dart' show kIsWeb;
import 'package:flutter/material.dart';
import 'package:flutter_sound/flutter_sound.dart';
import 'package:flutter_sound_platform_interface/flutter_sound_recorder_platform_interface.dart';
import 'package:permission_handler/permission_handler.dart';

typedef _Fn = void Function();
const theSource = AudioSource.microphone;

class SimpleRecorder extends StatefulWidget {
  @override
  _SimpleRecorderState createState() => _SimpleRecorderState();
}

class _SimpleRecorderState extends State<SimpleRecorder> {
  Codec _codec = Codec.aacMP4;
  String _mPath = 'tau_file.mp4';
  FlutterSoundPlayer? _mPlayer = FlutterSoundPlayer();
  FlutterSoundRecorder? _mRecorder = FlutterSoundRecorder();
  bool _mPlayerIsInited = false;
  bool _mRecorderIsInited = false;
  bool _mplaybackReady = false;

  @override
  void initState() {
    _mPlayer!.openPlayer().then((value) {
      setState(() {
        _mPlayerIsInited = true;
      });
    });

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

  @override
  void dispose() {
    _mPlayer!.closePlayer();
    _mPlayer = null;

    _mRecorder!.closeRecorder();
    _mRecorder = null;
    super.dispose();
  }

  Future<void> openTheRecorder() async {
    if (!kIsWeb) {  // 웹이 아니면 (안드로이드 | IOS)
      var status = await Permission.microphone.request();  // 권한 요청
      if (status != PermissionStatus.granted) {
        throw RecordingPermissionException('Microphone permission not granted');
      }
    }
    await _mRecorder!.openRecorder();
    if (!await _mRecorder!.isEncoderSupported(_codec) && kIsWeb) {
      _codec = Codec.opusWebM;
      _mPath = 'tau_file.webm';
      if (!await _mRecorder!.isEncoderSupported(_codec) && kIsWeb) {
        _mRecorderIsInited = true;
        return;
      }
    }
    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,
    ));

    _mRecorderIsInited = true;
  }

  // ----------------------  Here is the code for recording and playback -------

  void record() {
    _mRecorder!
        .startRecorder(
      toFile: _mPath,
      codec: _codec,
      audioSource: theSource,
    )
        .then((value) {
      setState(() {});
    });
  }

  void stopRecorder() async {
    await _mRecorder!.stopRecorder().then((value) {
      setState(() {
        //var url = value;
        _mplaybackReady = true;
      });
    });
  }

  void play() {
    assert(_mPlayerIsInited &&
        _mplaybackReady &&
        _mRecorder!.isStopped &&
        _mPlayer!.isStopped);
    // 위의 조건이면 프로그램이 중단된다.
    _mPlayer!
        .startPlayer(
            fromURI: _mPath,
            //codec: kIsWeb ? Codec.opusWebM : Codec.aacADTS,
            whenFinished: () {
              setState(() {});
            })
        .then((value) {
      setState(() {});
    });
  }

  void stopPlayer() {
    _mPlayer!.stopPlayer().then((value) {
      setState(() {});
    });
  }

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

  _Fn? getRecorderFn() {
    // 녹음 준비가 되어 있지 않거나 녹음 파일이 실행 중이라면,
    if (!_mRecorderIsInited || !_mPlayer!.isStopped) {
      return null;
    }
    return _mRecorder!.isStopped ? record : stopRecorder;
  }

  _Fn? getPlaybackFn() {
    // 실행 준비가 되어 있지 않거나 녹음 중이라면,
    if (!_mPlayerIsInited || !_mplaybackReady || !_mRecorder!.isStopped) {
      return null;
    }
    return _mPlayer!.isStopped ? play : stopPlayer;
  }

  @override
  Widget build(BuildContext context) {
    Widget makeBody() {
      return Column(
        children: [
          Container(
            margin: const EdgeInsets.all(3),
            padding: const EdgeInsets.all(3),
            height: 80,
            width: double.infinity,  // 중요
            alignment: Alignment.center,
            decoration: BoxDecoration(
              color: Color(0xFFFAF0E6),
              border: Border.all(
                color: Colors.indigo,
                width: 3,
              ),
            ),
            child: Row(children: [
              ElevatedButton(
                onPressed: getRecorderFn(),
                //color: Colors.white,
                //disabledColor: Colors.grey,
                child: Text(_mRecorder!.isRecording ? 'Stop' : 'Record'),
              ),
              SizedBox(
                width: 20,
              ),
              Text(_mRecorder!.isRecording
                  ? 'Recording in progress'
                  : 'Recorder is stopped'),
            ]),
          ),
          Container(
            margin: const EdgeInsets.all(3),
            padding: const EdgeInsets.all(3),
            height: 80,
            width: double.infinity,
            alignment: Alignment.center,
            decoration: BoxDecoration(
              color: Color(0xFFFAF0E6),
              border: Border.all(
                color: Colors.indigo,
                width: 3,
              ),
            ),
            child: Row(children: [
              ElevatedButton(
                onPressed: getPlaybackFn(),
                //color: Colors.white,
                //disabledColor: Colors.grey,
                child: Text(_mPlayer!.isPlaying ? 'Stop' : 'Play'),
              ),
              SizedBox(
                width: 20,
              ),
              Text(_mPlayer!.isPlaying
                  ? 'Playback in progress'
                  : 'Player is stopped'),
            ]),
          ),
        ],
      );
    }

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

 

Volume Control

💬  음원 재생 볼륨을 조절할 수 있는 앱

UI

 

코드

더보기
더보기
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_sound/flutter_sound.dart';

final _exampleAudioFilePathMP3_1 =
    'https://file-examples-com.github.io/uploads/2017/11/file_example_MP3_700KB.mp3';

typedef Fn = void Function();

class VolumeControl extends StatefulWidget {
  @override
  _VolumeControlState createState() => _VolumeControlState();
}

class _VolumeControlState extends State<VolumeControl> {
  final FlutterSoundPlayer _mPlayer1 = FlutterSoundPlayer();
  bool _mPlayerIsInited1 = false;
  double _mVolume1 = 100.0;

  @override
  void initState() {
    super.initState();
    _mPlayer1.openPlayer().then((value) {
      setState(() {
        _mPlayerIsInited1 = true;
      });
    });
  }

  @override
  void dispose() {
    stopPlayer(_mPlayer1);

    // Be careful : you must `close` the audio session when you have finished with it.
    _mPlayer1.closePlayer();

    super.dispose();
  }

  // -------  Here is the code to playback a remote file -----------------------

  void play(FlutterSoundPlayer? player, String uri) async {
    await player!.startPlayer(
        fromURI: uri,
        codec: Codec.mp3,
        whenFinished: () {
          setState(() {});
        });
    setState(() {});
  }

  Future<void> stopPlayer(FlutterSoundPlayer player) async {
    await player.stopPlayer();
  }

  Future<void> setVolume1(double v) async // v is between 0.0 and 100.0
  {
    v = v > 100.0 ? 100.0 : v;
    _mVolume1 = v;
    setState(() {});
    //await _mPlayer!.setVolume(v / 100, fadeDuration: Duration(milliseconds: 5000));
    await _mPlayer1.setVolume(
      v / 100,
    );
  }

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

  Fn? getPlaybackFn(FlutterSoundPlayer? player, String uri) {
    if (!(_mPlayerIsInited1)) {
      return null;
    }
    return player!.isStopped
        ? () {
            play(player, uri);
          }
        : () {
            stopPlayer(player).then((value) => setState(() {}));
          };
  }

  @override
  Widget build(BuildContext context) {
    Widget makeBody() {
      //return Column(
      //children: [
      return Container(
        margin: const EdgeInsets.all(3),
        padding: const EdgeInsets.all(3),
        height: 150,
        width: double.infinity,
        alignment: Alignment.center,
        decoration: BoxDecoration(
          color: Color(0xFFFAF0E6),
          border: Border.all(
            color: Colors.indigo,
            width: 3,
          ),
        ),
        child: Column(children: [
          Row(children: [
            ElevatedButton(
              onPressed: getPlaybackFn(_mPlayer1, _exampleAudioFilePathMP3_1),
              //color: Colors.white,
              //disabledColor: Colors.grey,
              child: Text(_mPlayer1.isPlaying ? 'Stop' : 'Play'),
            ),
            SizedBox(
              width: 20,
            ),
            Text(_mPlayer1.isPlaying
                ? 'Playback #1 in progress'
                : 'Player #1 is stopped'),
          ]),
          Text('Volume:'),
          Slider(
              value: _mVolume1,
              min: 0.0,
              max: 100.0,
              onChanged: setVolume1,
              divisions: 100),
        ]),
        //),
        //],
      );
    }

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

 

Speed Control

💬  음원 재생 속도를 조절할 수 있는 앱

UI

 

코드

더보기
더보기
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_sound/flutter_sound.dart';
import 'dart:typed_data';
import 'package:flutter/services.dart' show rootBundle;

const _boum = 'assets/samples/sample2.aac';

typedef Fn = void Function();

class SpeedControl extends StatefulWidget {
  @override
  _SpeedControlState createState() => _SpeedControlState();
}

class _SpeedControlState extends State<SpeedControl> {
  final FlutterSoundPlayer _mPlayer = FlutterSoundPlayer();
  bool _mPlayerIsInited = false;
  double _mSpeed = 100.0;
  Uint8List? _boumData;

  @override
  void initState() {
    super.initState();
    init().then((value) {
      setState(() {
        _mPlayerIsInited = true;
      });
    });
  }

  @override
  void dispose() {
    stopPlayer(_mPlayer);

    // Be careful : you must `close` the audio session when you have finished with it.
    _mPlayer.closePlayer();

    super.dispose();
  }

  Future<void> init() async {
    await _mPlayer.openPlayer();
    await _mPlayer.setSpeed(
        1.0); // This dummy instruction is MANDATORY on iOS, before the first `startRecorder()`.
    _boumData = await getAssetData(_boum);
  }

  Future<Uint8List> getAssetData(String path) async {
    var asset = await rootBundle.load(path);
    return asset.buffer.asUint8List();
  }

  // -------  Here is the code to playback  -----------------------

  void play(FlutterSoundPlayer? player) async {
    await player!.startPlayer(
        fromDataBuffer: _boumData,
        codec: Codec.aacADTS,
        whenFinished: () {
          setState(() {});
        });
    setState(() {});
  }

  Future<void> stopPlayer(FlutterSoundPlayer player) async {
    await player.stopPlayer();
  }

  Future<void> setSpeed(double v) async // v is between 0.0 and 100.0
  {
    v = v > 200.0 ? 200.0 : v;
    _mSpeed = v;
    setState(() {});
    await _mPlayer.setSpeed(
      v / 100,
    );
  }

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

  Fn? getPlaybackFn(FlutterSoundPlayer? player) {
    if (!_mPlayerIsInited) {
      return null;
    }
    return player!.isStopped
        ? () {
            play(player);
          }
        : () {
            stopPlayer(player).then((value) => setState(() {}));
          };
  }

  @override
  Widget build(BuildContext context) {
    Widget makeBody() {
      //return Column(
      //children: [
      return Container(
        margin: const EdgeInsets.all(3),
        padding: const EdgeInsets.all(3),
        height: 140,
        width: double.infinity,
        alignment: Alignment.center,
        decoration: BoxDecoration(
          color: Color(0xFFFAF0E6),
          border: Border.all(
            color: Colors.indigo,
            width: 3,
          ),
        ),
        child: Column(children: [
          Row(children: [
            ElevatedButton(
              onPressed: getPlaybackFn(_mPlayer),
              //color: Colors.white,
              //disabledColor: Colors.grey,
              child: Text(_mPlayer.isPlaying ? 'Stop' : 'Play'),
            ),
            SizedBox(
              width: 20,
            ),
            Text(_mPlayer.isPlaying
                ? 'Playback #1 in progress'
                : 'Player #1 is stopped'),
          ]),
          Text('Speed:'),
          Slider(
            value: _mSpeed,
            min: 0.0,
            max: 200.0,
            onChanged: setSpeed,
            //divisions: 100
          ),
        ]),
        //),
        //],
      );
    }

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

 

Seek

💬  음원 재생의 상대적 위치를 실시간으로 표시하며, 드래그를 통하여 음원 재생 위치를 변경할 수 있다.

UI

 

코드

더보기
더보기
import 'dart:async';
import 'package:flutter/material.dart';
import 'package:flutter_sound/flutter_sound.dart';
import 'dart:typed_data';
import 'package:flutter/services.dart' show rootBundle;

const _boum = 'assets/samples/sample2.aac';
const int duration = 42673;

typedef Fn = void Function();

class Seek extends StatefulWidget {
  @override
  _SeekState createState() => _SeekState();
}

class _SeekState extends State<Seek> {
  final FlutterSoundPlayer _mPlayer = FlutterSoundPlayer();
  bool _mPlayerIsInited = false;
  Uint8List? _boumData;
  StreamSubscription? _mPlayerSubscription;
  int pos = 0;

  @override
  void initState() {
    super.initState();
    init().then((value) {
      setState(() {
        _mPlayerIsInited = true;
      });
    });
  }

  @override
  void dispose() {
    stopPlayer(_mPlayer);
    cancelPlayerSubscriptions();

    // Be careful : you must `close` the audio session when you have finished with it.
    _mPlayer.closePlayer();

    super.dispose();
  }

  void cancelPlayerSubscriptions() {
    if (_mPlayerSubscription != null) {
      _mPlayerSubscription!.cancel();
      _mPlayerSubscription = null;
    }
  }

  Future<void> init() async {
    await _mPlayer.openPlayer();
    await _mPlayer.setSubscriptionDuration(Duration(milliseconds: 50));
    _boumData = await getAssetData(_boum);
    _mPlayerSubscription = _mPlayer.onProgress!.listen((e) {
      setPos(e.position.inMilliseconds);
      setState(() {});
    });
  }

  Future<Uint8List> getAssetData(String path) async {
    var asset = await rootBundle.load(path);
    return asset.buffer.asUint8List();
  }

  void play(FlutterSoundPlayer? player) async {
    await player!.startPlayer(
        fromDataBuffer: _boumData,
        codec: Codec.aacADTS,
        whenFinished: () {
          setState(() {});
        });
    setState(() {});
  }

  Future<void> stopPlayer(FlutterSoundPlayer player) async {
    await player.stopPlayer();
  }

  Future<void> setPos(int d) async {
    if (d > duration) {
      d = duration;
    }
    setState(() {
      pos = d;
    });
  }

  Future<void> seek(double d) async {
    await _mPlayer.seekToPlayer(Duration(milliseconds: d.floor()));
    await setPos(d.floor());
  }

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

  Fn? getPlaybackFn(FlutterSoundPlayer? player) {
    if (!_mPlayerIsInited) {
      return null;
    }
    return player!.isStopped
        ? () {
            play(player);
          }
        : () {
            stopPlayer(player).then((value) => setState(() {}));
          };
  }

  @override
  Widget build(BuildContext context) {
    Widget makeBody() {
      //return Column(
      //children: [
      return Container(
        margin: const EdgeInsets.all(3),
        padding: const EdgeInsets.all(3),
        height: 140,
        width: double.infinity,
        alignment: Alignment.center,
        decoration: BoxDecoration(
          color: Color(0xFFFAF0E6),
          border: Border.all(
            color: Colors.indigo,
            width: 3,
          ),
        ),
        child: Column(children: [
          Row(children: [
            ElevatedButton(
              onPressed: getPlaybackFn(_mPlayer),
              child: Text(_mPlayer.isPlaying ? 'Stop' : 'Play'),
            ),
            SizedBox(
              width: 20,
            ),
            Text(_mPlayer.isPlaying
                ? 'Playback in progress'
                : 'Player is stopped'),
            SizedBox(
              width: 20,
            ),
            Text('Pos: $pos'),
          ]),
          Text('Position:'),
          Slider(
            value: pos + 0.0,
            min: 0.0,
            max: duration + 0.0,
            onChanged: seek,
            //divisions: 100
          ),
        ]),
        //),
        //],
      );
    }

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