Flutter音频波形展示插件audio_waveforms的使用

发布于 1周前 作者 htzhanglong 来自 Flutter

Flutter音频波形展示插件audio_waveforms的使用

简介

audio_waveforms 是一个用于在Flutter中生成和显示音频文件波形的插件。它支持从文件或实时录音中生成波形,并提供了丰富的自定义选项,包括样式调整、手势控制等。

Audio Waveforms

Demo GIF

前提条件

  1. 添加依赖:在 pubspec.yaml 文件中添加 audio_waveforms 依赖。
    dependencies:
      audio_waveforms: <latest-version>
    
  2. 清理项目:确保删除设备上的应用并执行 flutter cleanflutter pub get

使用方法

录音功能

平台特定设置

  • Android

    • 修改 android/app/build.gradle 中的最小SDK版本为21(或更高)。
      minSdkVersion 21
      
    • AndroidManifest.xml 中添加录音权限。
      <uses-permission android:name="android.permission.RECORD_AUDIO" />
      
  • iOS

    • ios/Runner/Info.plist 中添加麦克风使用描述。
      <key>NSMicrophoneUsageDescription</key>
      <string>请允许访问麦克风以录制音频。</string>
      
    • 确保 Podfile 中指定iOS版本为12.0或更高。
      platform :ios, '12.0'
      

快速示例

以下是一个简单的例子,展示了如何在录音时显示波形。

import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:flutter/material.dart';

class RecorderExample extends StatefulWidget {
  @override
  _RecorderExampleState createState() => _RecorderExampleState();
}

class _RecorderExampleState extends State<RecorderExample> {
  String? recordedFilePath;
  final RecorderController recorderController = RecorderController();

  @override
  void initState() {
    super.initState();
    recorderController.checkPermission();
  }

  @override
  void dispose() {
    recorderController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ElevatedButton(
          onPressed: () {
            if (recorderController.hasPermission) {
              recorderController.record(); // 默认保存文件名为当前时间戳
            }
          },
          child: Text('Record'),
        ),
        ElevatedButton(
          onPressed: () {
            recorderController.pause();
          },
          child: Text('Pause'),
        ),
        ElevatedButton(
          onPressed: () async {
            if (recorderController.isRecording) {
              recordedFilePath = await recorderController.stop();
            }
          },
          child: Text('Stop'),
        ),
        AudioWaveforms(
          controller: recorderController,
          size: Size(300, 50),
        ),
      ],
    );
  }
}

播放功能

快速示例

以下是一个简单的例子,展示了如何在播放音频文件时显示波形。

import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:flutter/material.dart';

class PlayerExample extends StatefulWidget {
  @override
  _PlayerExampleState createState() => _PlayerExampleState();
}

class _PlayerExampleState extends State<PlayerExample> {
  final PlayerController playerController = PlayerController();

  @override
  void initState() {
    super.initState();
    playerController.preparePlayer(path: '../myFile.mp3');
  }

  @override
  void dispose() {
    playerController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        ElevatedButton(
          onPressed: () {
            playerController.startPlayer();
          },
          child: Text('Play'),
        ),
        ElevatedButton(
          onPressed: () {
            playerController.pausePlayer();
          },
          child: Text('Pause'),
        ),
        ElevatedButton(
          onPressed: () {
            playerController.stopPlayer();
          },
          child: Text('Stop'),
        ),
        AudioFileWaveforms(
          controller: playerController,
          size: Size(300, 50),
        ),
      ],
    );
  }
}

自定义波形样式

你可以通过 WaveStyle 类来自定义波形的外观,例如颜色、渐变、间距等。

AudioWaveforms(
  size: Size(MediaQuery.of(context).size.width, 200.0),
  shouldCalculateScrolledPosition: true,
  enableGesture: true,
  waveStyle: WaveStyle(
    waveColor: Colors.blue,
    extendWaveform: true,
    showMiddleLine: false,
    waveGradient: LinearGradient(colors: [Colors.red, Colors.orange]),
  ),
)

高级特性

  • 自定义录音参数:可以设置文件路径、编码器、输出格式等。
  • 监听事件:可以通过流监听录音状态变化、进度更新等。
  • 预计算波形数据:通过 extractWaveformData 方法提前计算波形数据,节省资源。
  • 平滑动画:设置 updateFrequency 来控制波形动画的流畅度。

完整示例代码

以下是结合了录音和播放功能的完整示例代码:

import 'dart:io';
import 'package:audio_waveforms/audio_waveforms.dart';
import 'package:flutter/material.dart';
import 'package:path_provider/path_provider.dart';

void main() => runApp(const MyApp());

class MyApp extends StatelessWidget {
  const MyApp({super.key});

  @override
  Widget build(BuildContext context) {
    return const MaterialApp(
      title: 'Audio Waveforms',
      debugShowCheckedModeBanner: false,
      home: Home(),
    );
  }
}

class Home extends StatefulWidget {
  const Home({super.key});

  @override
  State<Home> createState() => _HomeState();
}

class _HomeState extends State<Home> {
  late final RecorderController recorderController;
  late final PlayerController playerController;

  String? path;
  bool isRecording = false;
  bool isRecordingCompleted = false;
  bool isLoading = true;
  late Directory appDirectory;

  @override
  void initState() {
    super.initState();
    _getDir();
    _initialiseControllers();
  }

  void _getDir() async {
    appDirectory = await getApplicationDocumentsDirectory();
    path = "${appDirectory.path}/recording.m4a";
    isLoading = false;
    setState(() {});
  }

  void _initialiseControllers() {
    recorderController = RecorderController()
      ..androidEncoder = AndroidEncoder.aac
      ..androidOutputFormat = AndroidOutputFormat.mpeg4
      ..iosEncoder = IosEncoder.kAudioFormatMPEG4AAC
      ..sampleRate = 44100;

    playerController = PlayerController();
  }

  @override
  void dispose() {
    recorderController.dispose();
    playerController.dispose();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(title: Text('Audio Waveforms')),
      body: isLoading
          ? Center(child: CircularProgressIndicator())
          : Column(
              children: [
                Expanded(
                  child: ListView.builder(
                    itemCount: 1,
                    itemBuilder: (_, index) {
                      return Container(
                        height: 200,
                        color: Colors.grey[200],
                        child: Center(child: Text('Waveform Display Area')),
                      );
                    },
                  ),
                ),
                Row(
                  children: [
                    IconButton(
                      onPressed: () async {
                        if (isRecording) {
                          await recorderController.stop(false);
                          isRecordingCompleted = true;
                        } else {
                          await recorderController.record(path: path);
                        }
                        setState(() {
                          isRecording = !isRecording;
                        });
                      },
                      icon: Icon(isRecording ? Icons.stop : Icons.mic),
                    ),
                    IconButton(
                      onPressed: () {
                        if (isRecording) recorderController.refresh();
                      },
                      icon: Icon(Icons.refresh),
                    ),
                    Spacer(),
                    IconButton(
                      onPressed: () {
                        if (!isRecording && recordedFilePath != null) {
                          playerController.preparePlayer(path: recordedFilePath);
                          playerController.startPlayer();
                        }
                      },
                      icon: Icon(Icons.play_arrow),
                    ),
                    IconButton(
                      onPressed: () {
                        playerController.pausePlayer();
                      },
                      icon: Icon(Icons.pause),
                    ),
                    IconButton(
                      onPressed: () {
                        playerController.stopPlayer();
                      },
                      icon: Icon(Icons.stop),
                    ),
                  ],
                ),
                if (isRecordingCompleted)
                  AudioWaveforms(
                    controller: recorderController,
                    size: Size(double.infinity, 50),
                  ),
                if (recordedFilePath != null)
                  AudioFileWaveforms(
                    controller: playerController,
                    size: Size(double.infinity, 50),
                  ),
              ],
            ),
    );
  }
}

这个完整的示例展示了如何集成录音和播放功能,并动态显示波形。希望这能帮助你更好地理解和使用 audio_waveforms 插件。


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

1 回复

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


当然,以下是如何在Flutter项目中使用audio_waveforms插件来展示音频波形的示例代码。这个插件允许你从音频文件中生成并展示波形图。

首先,确保你已经在pubspec.yaml文件中添加了audio_waveforms依赖:

dependencies:
  flutter:
    sdk: flutter
  audio_waveforms: ^最新版本号  # 请替换为实际的最新版本号

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

接下来,你可以按照以下步骤在你的Flutter应用中展示音频波形:

  1. 导入必要的包
import 'package:flutter/material.dart';
import 'package:audio_waveforms/audio_waveforms.dart';
  1. 创建一个Widget来展示波形
class AudioWaveformWidget extends StatefulWidget {
  final String audioFilePath;

  AudioWaveformWidget({required this.audioFilePath});

  @override
  _AudioWaveformWidgetState createState() => _AudioWaveformWidgetState();
}

class _AudioWaveformWidgetState extends State<AudioWaveformWidget> {
  late AudioWaveformsController _controller;
  late List<double> _waveformData;

  @override
  void initState() {
    super.initState();
    _controller = AudioWaveformsController();
    _loadWaveformData();
  }

  @override
  void dispose() {
    _controller.dispose();
    super.dispose();
  }

  Future<void> _loadWaveformData() async {
    try {
      // 从音频文件路径加载波形数据
      _waveformData = await _controller.loadFromFile(widget.audioFilePath);
      setState(() {});
    } catch (e) {
      print("Error loading waveform data: $e");
    }
  }

  @override
  Widget build(BuildContext context) {
    return CustomPaint(
      size: Size(double.infinity, 200),  // 根据需要调整大小
      painter: WaveformPainter(_waveformData),
    );
  }
}

class WaveformPainter extends CustomPainter {
  final List<double> waveformData;
  final Paint paint = Paint()
    ..color = Colors.blue
    ..strokeWidth = 2.0
    ..style = PaintingStyle.stroke;

  WaveformPainter(this.waveformData);

  @override
  void paint(Canvas canvas, Size size) {
    final double canvasWidth = size.width;
    final double canvasHeight = size.height;
    final double scaleFactor = canvasWidth / waveformData.length;

    for (int i = 0; i < waveformData.length - 1; i++) {
      final double x1 = i * scaleFactor;
      final double y1 = canvasHeight / 2 - waveformData[i] * (canvasHeight / 4);
      final double x2 = (i + 1) * scaleFactor;
      final double y2 = canvasHeight / 2 - waveformData[i + 1] * (canvasHeight / 4);
      canvas.drawLine(Offset(x1, y1), Offset(x2, y2), paint);
    }
  }

  @override
  bool shouldRepaint(covariant CustomPainter oldDelegate) {
    return oldDelegate != this;
  }
}
  1. 在你的主应用中使用这个Widget
void main() {
  runApp(MyApp());
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: Text('Audio Waveform Demo'),
        ),
        body: Center(
          child: AudioWaveformWidget(
            audioFilePath: 'path/to/your/audio/file.mp3',  // 替换为你的音频文件路径
          ),
        ),
      ),
    );
  }
}

注意事项

  • 确保你提供了正确的音频文件路径。
  • AudioWaveformsControllerloadFromFile方法是一个异步操作,因此你需要处理异步逻辑(例如使用Futureasync/await)。
  • WaveformPainter类中的paint方法负责根据波形数据绘制波形图。你可以根据需要调整Paint对象的属性来改变波形的样式。

这段代码展示了如何使用audio_waveforms插件从音频文件中加载波形数据,并使用CustomPaintCustomPainter在Flutter应用中绘制波形图。如果你有任何进一步的问题或需要更详细的信息,请随时告诉我!

回到顶部