[Flutter] just_audioで音を再生する
Flutter製アプリで音を再生する方法をご紹介します。
今回は、just_audio
と呼ばれるライブラリを活用します。
このライブラリは
- 機能が豊富(クラスが多すぎて特定の機能を探すのに苦労するほどw)
- 評価が高い(2021/11/17時点 1391 LIKES / 130 PUBPOINTS)
- Exampleを通して、アプリで使う必要最小限の実装が説明されている
以上の理由で筆者は魅力を感じています。
↓はデモ動画です。
準備
$ flutter pub add just_audio
flutter:
assets:
- assets/audio/cute.mp3
説明
初期化
import 'package:just_audio/just_audio.dart';
import 'package:audio_session/audio_session.dart';
late AudioPlayer _player;
bool _changeAudioSource = false;
@override
void initState() {
super.initState();
_setupSession();
}
Future<void> _setupSession() async {
_player = AudioPlayer();
final session = await AudioSession.instance;
await session.configure(AudioSessionConfiguration.speech());
await _loadAudioFile();
}
Future<void> _loadAudioFile() async {
try {
if(_changeAudioSource) {
await _player.setUrl('https://s3.amazonaws.com/scifri-episodes/scifri20181123-episode.mp3'); // ストリーミング
} else {
await _player.setAsset('assets/audio/cute.mp3'); // アセット(ローカル)のファイル
}
} catch(e) {
print(e);
}
}
再生する音声ファイルの種類に合わせ、プレーヤーを最適化させる
final session = await AudioSession.instance;
await session.configure(AudioSessionConfiguration.speech());
OS側にオーディオの詳細設定を通知するaudio_sessionパッケージを活用します。
オーディオは
-
AudioSessionConfiguration.music
:音楽プレーヤー -
AudioSessionConfiguration.speech
:連続した音声(ポッドキャストやオーディオブックなど)
上記2種類用意されており、アプリ応じて適時宣言するとクオリティが向上すると思われる。
ローカルの音声ファイルをロードする
await _player.setAsset('assets/audio/cute.mp3');
pubspec.yaml
に宣言した音声ファイルのパスを指定します。またsetAsset method
はFuture
のため、awaitを付与すると安心な設計になります。
WEB上の音声ファイルをロードする
await _player.setUrl('https://s3.amazonaws.com/scifri-episodes/scifri20181123-episode.mp3');
URLを指定してあげれば実装できます。またsetUrl method
はFuture
のため、awaitを付与すると安心な設計になります。
再生状態と現在地を検出する
_player.playbackEventStream.listen((event) {
switch(event.processingState) {
case ProcessingState.idle:
print('オーディオファイルをロードしていないよ');
break;
case ProcessingState.loading:
print('オーディオファイルをロード中だよ');
break;
case ProcessingState.buffering:
print('バッファリング(読み込み)中だよ');
break;
case ProcessingState.ready:
print('再生できるよ');
break;
case ProcessingState.completed:
print('再生終了したよ');
break;
default:
print(event.processingState);
break;
}
});
playbackEventStream property でPlaybackEvent classのprocessingState propertyを取得します。これはプレーヤーの処理状態を列挙し、再生状態に合わせて処理を適時宣言することができます。
ProcessingState enumのConstants
-
buffering
: バッファリング中 -
completed
: 再生完了 -
idle
: ロードしていない -
loading
: ロード中 -
ready
: バッファリングが完了し、再生できる -
values
: 宣言順の定数リスト
別の方法で再生状態を検知する
playerStateStream propertyで再生状態を検知可能です。こちらはPlayerState classで再生状態と処理状態を検知可能だが、stop method
と相性が悪い。
処理状態も取得するため、stop method
でリソースを解放すると状態が検知できずエラーを吐く原因になるため、筆者は基本的にplaybackEventStream property
を使う。だが、ケースバイケースで使い分けしなければいけない。
再生
Future<void> _playSoundFile() async {
// 再生終了状態の場合、新たなオーディオファイルを定義し再生できる状態にする
if(_player.processingState == ProcessingState.completed) {
await _loadAudioFile();
}
await _player.setSpeed(_currentSliderValue); // 再生速度を指定
await _player.play();
}
オーディオファイルを再定義する
if(_player.processingState == ProcessingState.completed) {
await _loadAudioFile();
}
再生終了した場合、オーディオソースを再読み込みせずProcessingState
はcompleted
のままです。そのため、再生処理する前に一度if文で状態を確認しソースを再定義してください。
再生速度を設定する
await _player.setSpeed(_currentSliderValue);
double
を指定することで再生速度を調整可能。(デフォルトは1.0)
1.1以上の値を設定した場合、再生速度が(音声ファイルの)ダウンロード速度より速いとフリーズする可能性があるため、注意が必要である。
再生する
await _player.play();
再生する。またplay method
はFuture
のため、awaitを付与すると安心な設計になります。
全体
import 'package:flutter/material.dart';
import 'package:just_audio/just_audio.dart';
import 'package:audio_session/audio_session.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Demo just_audio Package'),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
late AudioPlayer _player;
double _currentSliderValue = 1.0;
bool _changeAudioSource = false;
String _stateSource = 'アセットを再生';
@override
void initState() {
super.initState();
_setupSession();
// AudioPlayerの状態を取得
_player.playbackEventStream.listen((event) {
switch(event.processingState) {
case ProcessingState.idle:
print('オーディオファイルをロードしていないよ');
break;
case ProcessingState.loading:
print('オーディオファイルをロード中だよ');
break;
case ProcessingState.buffering:
print('バッファリング(読み込み)中だよ');
break;
case ProcessingState.ready:
print('再生できるよ');
break;
case ProcessingState.completed:
print('再生終了したよ');
break;
default:
print(event.processingState);
break;
}
});
}
Future<void> _setupSession() async {
_player = AudioPlayer();
final session = await AudioSession.instance;
await session.configure(AudioSessionConfiguration.speech());
await _loadAudioFile();
}
@override
void dispose() {
_player.dispose();
super.dispose();
}
void _takeTurns() {
late String _changeStateText;
_changeAudioSource = _changeAudioSource ? false : true; // 真偽値を反転
_player.stop();
_loadAudioFile().then((_) {
if(_changeAudioSource) {
_changeStateText = 'ストリーミング再生';
} else {
_changeStateText = 'アセットを再生';
}
setState((){
_stateSource = _changeStateText;
});
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Text(_stateSource),
Slider(
value: _currentSliderValue,
min: 0,
max: 10.0,
divisions: 10,
label: _currentSliderValue.toString(),
onChanged: (double value) {
setState(() {
_currentSliderValue = value;
});
},
),
Text(_currentSliderValue.toString()),
IconButton(
icon: const Icon(Icons.play_arrow),
onPressed: () async => await _playSoundFile(),
),
IconButton(
icon: const Icon(Icons.pause),
onPressed: () async => await _player.pause(),
)
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _takeTurns,
tooltip: 'Increment',
child: const Icon(Icons.autorenew),
),
);
}
Future<void> _playSoundFile() async {
// 再生終了状態の場合、新たなオーディオファイルを定義し再生できる状態にする
if(_player.processingState == ProcessingState.completed) {
await _loadAudioFile();
}
await _player.setSpeed(_currentSliderValue); // 再生速度を指定
await _player.play();
}
Future<void> _loadAudioFile() async {
try {
if(_changeAudioSource) {
await _player.setUrl('https://s3.amazonaws.com/scifri-episodes/scifri20181123-episode.mp3'); // ストリーミング
} else {
await _player.setAsset('assets/audio/cute.mp3'); // アセット(ローカル)のファイル
}
} catch(e) {
print(e);
}
}
}
リポジトリ
スクラップ
Discussion