Flutter音频播放插件raw_sound的使用

Flutter音频播放插件raw_sound的使用

pub package License

A flutter plugin for playing raw PCM audio data (16-bit integer and 32-bit float).

平台支持

Android (Kotlin) iOS (Swift)
minSdkVersion 24 platform :ios, ‘14.0’
✔️ ✔️

使用

创建一个RawSoundPlayer实例

final _player = RawSoundPlayer();

初始化播放器实例

await _player.initialize(
  // 缓冲区大小(仅适用于Android)
  bufferSize: 4096 << 3,
  // 声道数量,可以是1或2
  nChannels: 1,
  // 播放速率(Hz)
  sampleRate: 16000,
  // PCM格式类型,可以是PCMI16(16位整数)或PCMF32(32位浮点数)
  pcmType: RawSoundPCMType.PCMI16,
);

开始播放

await _player.play();

向播放器实例喂入原始PCM数据

// 展示如何持续向播放器喂入数据直到播放暂停/停止
while (_player.isPlaying) {
  await _player.feed(dataBlock);
}

暂停播放

// 立即暂停并保持队列中的缓冲区
await _player.pause();

停止播放

// 立即停止并丢弃队列中的缓冲区
await _player.stop();

释放播放器实例

// 记住要释放任何已初始化的播放器实例
await _player.release();

完整示例代码

以下是一个完整的示例代码,展示了如何使用raw_sound插件播放16位整数和32位浮点数的PCM音频数据。

import 'dart:typed_data'; // for Uint8List
import 'dart:math' as math;

import 'package:flutter/material.dart';
import 'package:raw_sound/raw_sound_player.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  [@override](/user/override)
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  // 缓冲区大小(仅适用于Android)
  static int bufferSize = 4096 << 4;
  // 声道数量,可以是1或2
  static int nChannels = 1;
  // 播放速率(Hz)
  static int sampleRate = 16000;

  // 频率(Hz)
  static double freq = 440.0;
  // 周期(秒)
  static double period = 1.0 / freq;
  // 音量(范围在0.0到1.0之间)
  static double volume = 0.5;

  // 用于播放16位整数PCM音频数据的播放器实例
  final _playerPCMI16 = RawSoundPlayer();
  // 用于播放32位浮点数PCM音频数据的播放器实例
  final _playerPCMF32 = RawSoundPlayer();

  [@override](/user/override)
  void initState() {
    super.initState();
    // 初始化播放器实例
    _playerPCMI16
        .initialize(
      bufferSize: bufferSize,
      nChannels: nChannels,
      sampleRate: sampleRate,
      pcmType: RawSoundPCMType.PCMI16,
    )
        .then((value) {
      setState(() {});
    });
    _playerPCMF32
        .initialize(
      bufferSize: bufferSize,
      nChannels: nChannels,
      sampleRate: sampleRate,
      pcmType: RawSoundPCMType.PCMF32,
    )
        .then((value) {
      setState(() {});
    });
  }

  [@override](/user/override)
  void dispose() {
    // 释放播放器实例
    _playerPCMI16.release();
    _playerPCMF32.release();
    super.dispose();
  }

  Future<void> _playPCMI16() async {
    if (_playerPCMI16.isPlaying) {
      return;
    }
    await _playerPCMI16.play();
    setState(() {});
    // 持续向播放器喂入数据直到播放暂停/停止
    final dataBlock = _genPCMI16DataBlock(nPeriods: 20);
    while (_playerPCMI16.isPlaying) {
      await _playerPCMI16.feed(dataBlock);
    }
  }

  Future<void> _pausePCMI16() async {
    await _playerPCMI16.pause();
    setState(() {});
  }

  Future<void> _playPCMF32() async {
    if (_playerPCMF32.isPlaying) {
      return;
    }
    await _playerPCMF32.play();
    setState(() {});
    // 持续向播放器喂入数据直到播放暂停/停止
    final dataBlock = _genPCMF32DataBlock(nPeriods: 20);
    while (_playerPCMF32.isPlaying) {
      await _playerPCMF32.feed(dataBlock);
    }
  }

  Future<void> _pausePCMF32() async {
    await _playerPCMF32.pause();
    setState(() {});
  }

  // 生成16位整数PCM音频数据
  Uint8List _genPCMI16DataBlock({int nPeriods = 1}) {
    final nFramesPerPeriod = (period * sampleRate).toInt();
    debugPrint('nFrames / period: $nFramesPerPeriod');
    final step = math.pi * 2 / nFramesPerPeriod;
    // 填充一个周期的数据块
    final dataBlockPerPeriod =
        ByteData(nFramesPerPeriod << 1 /* one int16 is made of 2 bytes */);
    for (int i = 0; i < nFramesPerPeriod; i++) {
      // 幅度在-32767到32767之间
      final value = (math.sin(step * i) * volume * 32767).toInt();
      dataBlockPerPeriod.setInt16(
          i << 1, value, Endian.host /* native endianness */);
    }
    // 重复数据块nPeriods次
    final dataBlock = [];
    for (int i = 0; i < nPeriods; i++) {
      dataBlock.addAll(dataBlockPerPeriod.buffer.asUint8List());
    }
    debugPrint('dataBlock nBytes: ${dataBlock.length}');
    return Uint8List.fromList(dataBlock);
  }

  // 生成32位浮点数PCM音频数据
  Uint8List _genPCMF32DataBlock({int nPeriods = 1}) {
    final nFramesPerPeriod = (period * sampleRate).toInt();
    debugPrint('nFrames / period: $nFramesPerPeriod');
    final step = math.pi * 2 / nFramesPerPeriod;
    // 填充一个周期的数据块
    final dataBlockPerPeriod =
        ByteData(nFramesPerPeriod << 2 /* one float32 is made of 4 bytes */);
    for (int i = 0; i < nFramesPerPeriod; i++) {
      // 幅度在-1.0到1.0之间
      final value = math.sin(step * i) * volume;
      dataBlockPerPeriod.setFloat32(
          i << 2, value, Endian.host /* native endianness */);
    }
    // 重复数据块nPeriods次
    final dataBlock = [];
    for (int i = 0; i < nPeriods; i++) {
      dataBlock.addAll(dataBlockPerPeriod.buffer.asUint8List());
    }
    debugPrint('dataBlock nBytes: ${dataBlock.length}');
    return Uint8List.fromList(dataBlock);
  }

  Widget build(BuildContext context) {
    debugPrint('PlayerPCMI16 is inited? ${_playerPCMI16.isInited}');
    debugPrint('PlayerPCMF32 is inited? ${_playerPCMF32.isInited}');

    if (!_playerPCMI16.isInited || !_playerPCMF32.isInited) {
      return Container();
    }

    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.grey,
      ),
      home: Scaffold(
        appBar: AppBar(
          centerTitle: true,
          title: Text('Raw Sound Plugin Example App'),
        ),
        body: Column(
          children: [
            Card(
              child: Row(
                children: [
                  IconButton(
                    icon: Icon(_playerPCMI16.isPlaying
                        ? Icons.stop
                        : Icons.play_arrow),
                    onPressed: () {
                      _playerPCMI16.isPlaying ? _pausePCMI16() : _playPCMI16();
                    },
                  ),
                  Text('测试16位整数(PCMI16)'),
                ],
              ),
            ),
            Card(
              child: Row(
                children: [
                  IconButton(
                    icon: Icon(_playerPCMF32.isPlaying
                        ? Icons.stop
                        : Icons.play_arrow),
                    onPressed: () {
                      _playerPCMF32.isPlaying ? _pausePCMF32() : _playPCMF32();
                    },
                  ),
                  Text('测试32位浮点数(PCMF32)'),
                ],
              ),
            ),
          ],
        ),
      ),
    );
  }
}

更多关于Flutter音频播放插件raw_sound的使用的实战教程也可以访问 https://www.itying.com/category-92-b0.html

1 回复

更多关于Flutter音频播放插件raw_sound的使用的实战系列教程也可以访问 https://www.itying.com/category-92-b0.html


在Flutter中,raw_sound 是一个用于播放音频的插件,特别适用于播放RAW格式的音频数据。它允许你直接从内存或文件中播放原始的PCM音频数据。以下是如何使用 raw_sound 插件在Flutter项目中播放音频的步骤。

1. 添加依赖

首先,你需要在 pubspec.yaml 文件中添加 raw_sound 插件的依赖。

dependencies:
  flutter:
    sdk: flutter
  raw_sound: ^0.2.0  # 请确保使用最新版本

然后运行 flutter pub get 来安装依赖。

2. 导入插件

在你的Dart文件中导入 raw_sound 插件。

import 'package:raw_sound/raw_sound.dart';

3. 初始化 RawPlayer

RawPlayerraw_sound 插件中的核心类,用于管理和控制音频播放。

RawPlayer _rawPlayer = RawPlayer();

4. 配置音频数据

raw_sound 插件允许你从文件中加载音频数据或直接提供PCM数据。

从文件中加载音频数据

void _loadAudioFromFile() async {
  ByteData audioData = await rootBundle.load('assets/audio/sample.raw');
  Uint8List audioBytes = audioData.buffer.asUint8List();
  _rawPlayer.load(audioBytes);
}

直接提供PCM数据

void _loadAudioFromPCM(Uint8List pcmData) {
  _rawPlayer.load(pcmData);
}

5. 控制播放

你可以使用 RawPlayer 的以下方法来控制音频的播放、暂停和停止。

void _playAudio() {
  _rawPlayer.play();
}

void _pauseAudio() {
  _rawPlayer.pause();
}

void _stopAudio() {
  _rawPlayer.stop();
}

6. 释放资源

在不再需要 RawPlayer 时,记得释放资源以避免内存泄漏。

void _disposePlayer() {
  _rawPlayer.dispose();
}

7. 完整示例

以下是一个完整的示例,展示了如何使用 raw_sound 插件播放音频。

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:raw_sound/raw_sound.dart';

class AudioPlayerScreen extends StatefulWidget {
  [@override](/user/override)
  _AudioPlayerScreenState createState() => _AudioPlayerScreenState();
}

class _AudioPlayerScreenState extends State<AudioPlayerScreen> {
  RawPlayer _rawPlayer = RawPlayer();

  [@override](/user/override)
  void initState() {
    super.initState();
    _loadAudioFromFile();
  }

  void _loadAudioFromFile() async {
    ByteData audioData = await rootBundle.load('assets/audio/sample.raw');
    Uint8List audioBytes = audioData.buffer.asUint8List();
    _rawPlayer.load(audioBytes);
  }

  void _playAudio() {
    _rawPlayer.play();
  }

  void _pauseAudio() {
    _rawPlayer.pause();
  }

  void _stopAudio() {
    _rawPlayer.stop();
  }

  [@override](/user/override)
  void dispose() {
    _rawPlayer.dispose();
    super.dispose();
  }

  [@override](/user/override)
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Raw Sound Player'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: [
            ElevatedButton(
              onPressed: _playAudio,
              child: Text('Play'),
            ),
            ElevatedButton(
              onPressed: _pauseAudio,
              child: Text('Pause'),
            ),
            ElevatedButton(
              onPressed: _stopAudio,
              child: Text('Stop'),
            ),
          ],
        ),
      ),
    );
  }
}

void main() => runApp(MaterialApp(
  home: AudioPlayerScreen(),
));
回到顶部