Flutter音频播放插件flutter_ogg_piano的使用
Flutter音频播放插件flutter_ogg_piano的使用
获取开始
要在这个Flutter项目中使用该插件,你需要在pubspec.yaml
文件的依赖部分添加以下行:
dependencies:
flutter_ogg_piano: ^1.1.3
实现
在使用FlutterOggPiano
类之前,必须首先调用init
方法。默认情况下,可以同时播放的最大声音数量为128,但你可以更改此值。请注意,该插件不能处理超过5秒的声音。
从版本1.1.0开始,该插件在Android上使用了Oboe库,你可以设置性能模式为LOW_LATENCY
或POWER_SAVING
。
LOW_LATENCY
模式可以在渲染音频时实现最低延迟,但是同时播放太多声音会导致渲染过程无法正确完成,从而导致声音体验不佳。POWER_SAVING
模式需要比LOW_LATENCY
模式更高的性能,但它可以同时处理更多的声音。
该插件默认使用LOW_LATENCY
模式以获得更好的音频渲染时间,但你可以通过调用init()
方法来更改模式。
你还可以决定音频是否将以立体声模式渲染。如果在init()
方法中将isStereo
参数设置为false
,则所有音频将以单声道形式渲染。这也将影响设备的扬声器。如果你打开了单声道模式,即使扬声器有两通道,也会像只有一个通道一样播放任何声音。
如果你关心内存使用情况,你可以允许用户加载任何声音,而不仅仅是Android上的SoundPool
。
示例
以下示例展示了如何使用保存在资源文件夹中的声音文件来使用FlutterOggPiano
。
在pubspec.yaml
文件中添加资源文件路径:
assets:
- assets/123.ogg
- assets/456.ogg
当加载文件时,应用程序会在设备上生成一个临时的声音文件,并且不会被删除以便以后使用。如果你想要替换这个文件,需要将replace
参数设置为true
,否则会加载旧的数据。
你可以这样加载声音文件:
import 'dart:async';
import 'dart:typed_data';
import 'package:flutter/services.dart';
// 在代码的某个地方...
rootBundle.load("assets/123.ogg").then((ogg) {
// 如果你想覆盖已存在的声音...
fop.load(src: ogg, name: "123.ogg", index: 1, forceLoad: true);
// 如果你想正常加载...
fop.load(src: ogg, name: "123.ogg", index: 1);
// 如果你想替换生成的临时文件...
fop.load(src: ogg, name: "123.ogg", index: 1, forceLoad: true, replace: true);
});
rootBundle.load("assets/456.ogg").then((ogg) {
fop.load(src: ogg, index: 0);
});
当你想播放声音时,需要传递音符值。1个单位的差异等于1个半音的差异。负值表示降低音高,正值表示提高音高,零表示与源声音相同的音高。
// 在代码的某个地方...
fop.play(index: 1, note: -1); // 使用123.ogg声音降低1个半音
fop.play(index: 0, note: 3); // 使用456.ogg声音提高3个半音
从版本1.0.5开始,播放支持分离的左右音量。如果没有指定,左右音量的默认值为1.0。
// 在代码的某个地方...
fop.play(index: 1, note: -1, left: 0.5, right: 0.75);
从版本1.1.0开始,我们不再使用左右音量值。相反,我们只使用pan
值。pan
值可以是小数,但必须在-1.0到1.0之间。-1.0表示向左偏移,1.0表示向右偏移,0.0表示居中。如果没有指定pan
值,默认值为0.0。
// 在代码的某个地方...
fop.play(index: 1, note: 3, pan: 1.0);
从版本1.0.6开始,你可以发送多个声音数据进行播放,但有一些限制。你需要传递一个Map<int, double[]>
,每个键都是ID,双精度数组包含[pitch, left_volume, right_volume]。
Map<int, List<Float64List>> map = Map();
List<Float64List> sounds = [];
for(int i = 0; i < _number; i++) {
Float64List list = Float64List(3);
list[0] = _pitch;
list[1] = _left;
list[2] = _right;
sounds.add(list);
}
map[id] = sounds;
fop.playInGroup(map);
从版本1.1.0开始,playInGroup()
方法的参数有所改变。由于去除了左右音量值,双精度数组现在必须包含pan
值。由于Oboe可以执行较小的延迟来播放声音,开发者可以在一次会话中多次播放相同的声音。因此,双精度数组仍然需要3个数据,但现在它将是[pitch, pan, scale]。
注意,如果scale
值太大,用户可能会因为剪辑而听到糟糕的声音,所以要适当地调整这个值。pan
值仍然必须在-1.0到1.0之间。
Map<int, List<Float64List>> map = Map();
List<Float64List> sounds = [];
for(int i = 0; i < _number; i++) {
Float64List list = Float64List(3);
list[0] = _pitch;
list[1] = _pan;
list[2] = _scale;
sounds.add(list);
}
map[id] = sounds;
fop.playInGroup(map);
在使用完这个类之后,不要忘记释放它。
fop.release();
它会自动防止在已释放状态下播放或加载声音,但你可以通过调用以下方法简单地检查它是否已被释放。
fop.isReleased().then((r) {
// 做一些操作
})
完整示例代码
以下是完整的示例代码,展示了如何使用FlutterOggPiano
插件:
import 'dart:math';
import 'dart:typed_data';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:flutter/services.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_ogg_piano/flutter_ogg_piano.dart';
void main() {
runApp(MyApp());
}
class MyApp extends StatefulWidget {
[@override](/user/override)
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
String _platformVersion = 'Unknown';
FlutterOggPiano fop = FlutterOggPiano();
List<String> files = ["piano.ogg", "piano2.ogg"];
bool initialized = false;
int _count = 1;
int _pitch = 0;
[@override](/user/override)
void initState() {
super.initState();
initPlatformState();
if (!initialized) {
loadPianoSounds();
}
}
// 平台消息是异步的,所以我们初始化在一个异步方法中。
Future<void> initPlatformState() async {
String platformVersion;
// 平台消息可能失败,所以我们使用一个try/catch PlatformException。
try {
platformVersion = (await FlutterOggPiano.platformVersion) ?? _platformVersion;
} on PlatformException {
platformVersion = 'Failed to get platform version.';
}
// 如果在异步平台消息还在飞行时小部件被从树中移除,我们需要丢弃回复而不是调用setState来更新我们的非存在的外观。
if (!mounted) return;
setState(() {
_platformVersion = platformVersion;
});
}
[@override](/user/override)
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('插件示例应用'),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(onPressed: () {
if (initialized) {
fop.play(index: 0, note: 0);
}
}, child: Text("播放声音1")),
ElevatedButton(onPressed: () {
if (initialized) {
fop.play(index: 1, note: 0);
}
}, child: Text("播放声音2")),
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceAround,
children: [
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Center(child: Text("音高"),),
Row(
children: [
Ink(
decoration: ShapeDecoration(
color: Colors.lightBlue,
shape: CircleBorder(),
),
child: IconButton(
splashRadius: 24,
splashColor: Colors.lightBlueAccent,
onPressed: () {
setState(() {
_pitch++;
});
},
icon: Icon(Icons.arrow_upward, color: Colors.white70,),
),
),
Padding(padding: EdgeInsets.fromLTRB(6, 0, 6, 0)),
Container(
width: 72,
height: 36,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Colors.grey[100],
boxShadow: [
BoxShadow(
offset: Offset(4,4),
blurRadius: 4,
color: Colors.grey[600]!
)
]
),
child: Center(
child: Text(_pitch.toString()),
),
),
Padding(padding: EdgeInsets.fromLTRB(6, 0, 6, 0)),
Ink(
decoration: ShapeDecoration(
color: Colors.lightBlue,
shape: CircleBorder(),
),
child: IconButton(
splashRadius: 24,
splashColor: Colors.lightBlueAccent,
onPressed: () {
setState(() {
_pitch--;
});
},
icon: Icon(Icons.arrow_downward, color: Colors.white70,),
),
)
],
)
],
),
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(onPressed: () {
if (initialized) {
fop.play(index: 0, note: _pitch);
}
}, child: Text("播放声音1")),
ElevatedButton(onPressed: () {
fop.play(index: 1, note: _pitch);
}, child: Text("播放声音2")),
],
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
Ink(
decoration: ShapeDecoration(
color: Colors.lightBlue,
shape: CircleBorder(),
),
child: IconButton(
splashRadius: 24,
splashColor: Colors.lightBlueAccent,
onPressed: () {
setState(() {
_count++;
});
},
icon: Icon(Icons.arrow_upward, color: Colors.white70,),
),
),
Container(
width: 72,
height: 36,
decoration: BoxDecoration(
borderRadius: BorderRadius.circular(8),
color: Colors.grey[100],
boxShadow: [
BoxShadow(
offset: Offset(4,4),
blurRadius: 4,
color: Colors.grey[600]!
)
]
),
child: Center(
child: Text(_count.toString()),
),
),
Ink(
decoration: ShapeDecoration(
color: Colors.lightBlue,
shape: CircleBorder(),
),
child: IconButton(
splashRadius: 24,
splashColor: Colors.lightBlueAccent,
onPressed: () {
setState(() {
_count = max(--_count, 1);
});
},
icon: Icon(Icons.arrow_downward, color: Colors.white70,),
),
),
Column(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(onPressed: () {
if (initialized) {
Map<int, List<Float64List>> maps = Map();
List<Float64List> sounds = [];
Float64List data = Float64List(3);
data[0] = 0;
data[1] = 0;
data[2] = _count.toDouble();
sounds.add(data);
maps[0] = sounds;
fop.playInGroup(maps);
}
}, child: Text("播放声音1 ${_count}次")),
ElevatedButton(onPressed: () {
if (initialized) {
Map<int, List<Float64List>> maps = Map();
List<Float64List> sounds = [];
Float64List data = Float64List(3);
data[0] = 0;
data[1] = 0;
data[2] = _count.toDouble();
sounds.add(data);
maps[1] = sounds;
fop.playInGroup(maps);
}
}, child: Text("播放声音2 ${_count}次")),
ElevatedButton(onPressed: () {
if (initialized) {
Map<int, List<Float64List>> maps = Map();
List<Float64List> sounds = [];
Float64List data = Float64List(3);
data[0] = 0;
data[1] = 0;
data[2] = _count.toDouble();
sounds.add(data);
maps[1] = sounds;
maps[0] = sounds;
fop.playInGroup(maps);
}
}, child: Text("播放两个声音 ${_count}次")),
],
)
],
),
Row(
mainAxisAlignment: MainAxisAlignment.spaceEvenly,
children: [
ElevatedButton(onPressed: () {
if (initialized) {
fop.play(index: 0, note: 0, pan: -1.0);
}
}, child: Text("播放声音1在左边")),
ElevatedButton(onPressed: () {
if (initialized) {
fop.play(index: 0, note: 0, pan: 1.0);
}
}, child: Text("播放声音1在右边"))
],
)
],
),
),
),
);
}
Future<void> loadPianoSounds() async {
fop.init(mode: MODE.LOW_LATENCY);
for (int i = 0; i < files.length; i++) {
String name = "assets/" + files[i];
ByteData data = await rootBundle.load(name);
await fop.load(src: data, name: files[i], index: i, forceLoad: true);
}
initialized = true;
}
}
更多关于Flutter音频播放插件flutter_ogg_piano的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
更多关于Flutter音频播放插件flutter_ogg_piano的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html
当然,以下是如何在Flutter项目中使用flutter_ogg_piano
插件来播放音频的一个简单示例。请注意,flutter_ogg_piano
这个插件名称看起来像是一个特定的音频播放库,但实际上在Flutter社区中并没有一个广泛认可的名为flutter_ogg_piano
的官方或广泛使用的插件。不过,我可以给你一个使用类似功能的Flutter音频播放插件的示例,比如just_audio
,这是一个功能强大且广泛使用的音频播放插件。
假设你使用的是just_audio
插件,以下是如何实现音频播放的示例代码:
-
添加依赖: 首先,在你的
pubspec.yaml
文件中添加just_audio
依赖:dependencies: flutter: sdk: flutter just_audio: ^0.9.20 # 请检查最新版本号
-
运行
flutter pub get
: 保存pubspec.yaml
文件后,在终端中运行flutter pub get
来安装依赖。 -
使用
just_audio
播放音频:import 'package:flutter/material.dart'; import 'package:just_audio/just_audio.dart'; void main() { runApp(MyApp()); } class MyApp extends StatelessWidget { @override Widget build(BuildContext context) { return MaterialApp( home: AudioPlayerScreen(), ); } } class AudioPlayerScreen extends StatefulWidget { @override _AudioPlayerScreenState createState() => _AudioPlayerScreenState(); } class _AudioPlayerScreenState extends State<AudioPlayerScreen> { late AudioPlayer _audioPlayer; @override void initState() { super.initState(); _audioPlayer = AudioPlayer(); } @override void dispose() { _audioPlayer.dispose(); super.dispose(); } void _playAudio() async { // 替换为你的音频文件URL或本地路径 final url = 'https://example.com/audio/sample.ogg'; await _audioPlayer.setAudioSource(AudioSource.uri(Uri.parse(url))); _audioPlayer.play(); } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: Text('Audio Player'), ), body: Center( child: ElevatedButton( onPressed: _playAudio, child: Text('Play Audio'), ), ), ); } }
在这个示例中,我们使用了just_audio
插件来创建一个简单的音频播放器。当用户点击按钮时,音频将从指定的URL开始播放。
请注意,如果你确实在寻找一个专门用于播放OGG格式音频且名为flutter_ogg_piano
的插件,并且这个插件在Flutter社区中存在但不在pub.dev上广泛认知,你可能需要查阅该插件的官方文档或仓库以获取正确的使用方法。上面的代码是使用just_audio
作为替代方案的示例,因为just_audio
支持多种音频格式,包括OGG,并且是一个广泛使用的Flutter音频播放插件。