import 'dart:typed_data'; import 'package:flutter/material.dart'; import 'package:opus/opus.dart'; void main() { runApp(const OpusExampleApp()); } class OpusExampleApp extends StatelessWidget { const OpusExampleApp({Key? key}) : super(key: key); @override Widget build(BuildContext context) { return MaterialApp( title: 'Opus Plugin Demo', theme: ThemeData( primarySwatch: Colors.blue, ), home: const OpusExamplePage(), ); } } class OpusExamplePage extends StatefulWidget { const OpusExamplePage({Key? key}) : super(key: key); @override _OpusExamplePageState createState() => _OpusExamplePageState(); } class _OpusExamplePageState extends State { // 状态变量 bool _encoderInitialized = false; bool _decoderInitialized = false; String _statusMessage = '等待初始化...'; // 模拟PCM数据 Uint8List? _samplePcmData; Uint8List? _encodedData; Uint8List? _decodedData; @override void initState() { super.initState(); _initOpus(); } @override void dispose() { _disposeOpus(); super.dispose(); } // 初始化Opus编解码器 Future _initOpus() async { setState(() { _statusMessage = '正在初始化编解码器...'; }); // 初始化编码器 final encoderResult = await Opus.createEncoder( sampleRate: 48000, channels: 2, application: OpusApplicationType.audio, complexity: 10, frameSize: 960, ); // 初始化解码器 final decoderResult = await Opus.createDecoder( sampleRate: 48000, channels: 2, ); setState(() { _encoderInitialized = encoderResult; _decoderInitialized = decoderResult; _statusMessage = _encoderInitialized && _decoderInitialized ? '编解码器初始化成功' : '编解码器初始化失败'; }); // 创建模拟PCM数据用于测试 _createSamplePcmData(); } // 创建模拟PCM数据(正弦波) void _createSamplePcmData() { // 创建一个简单的正弦波 const int sampleRate = 48000; const int channels = 2; const int frequency = 440; // 440Hz - A4音符 const int durationMs = 20; // 20ms,按照常规frameSize=960 const int frameSize = 960; // 对应20ms@48kHz // 创建PCM数据缓冲区,16位有符号整数,每采样2字节,双声道 final int samplesToGenerate = (sampleRate * durationMs / 1000).round(); final int bufferSize = samplesToGenerate * channels * 2; // 2字节/样本 final byteBuffer = ByteData(bufferSize); // 生成正弦波 for (int i = 0; i < samplesToGenerate; i++) { // 计算当前时间点 final double t = i / sampleRate; // 计算振幅值 (-1.0 to 1.0) final double amplitude = sin(2 * 3.14159265359 * frequency * t); // 缩放到16位有符号整数范围 (-32768 to 32767) final int sample = (amplitude * 32767).round(); // 为所有声道写入相同的值 for (int channel = 0; channel < channels; channel++) { final int offset = (i * channels + channel) * 2; byteBuffer.setInt16(offset, sample, Endian.little); } } _samplePcmData = Uint8List.fromList( List.generate(bufferSize, (index) => byteBuffer.getUint8(index)) ); setState(() { _statusMessage = '已创建${_samplePcmData!.length}字节测试PCM数据'; }); } // 编码PCM数据 Future _encodeData() async { if (!_encoderInitialized || _samplePcmData == null) { setState(() { _statusMessage = '编码器未初始化或没有PCM数据'; }); return; } setState(() { _statusMessage = '正在编码...'; }); final encodedData = await Opus.encodeData(_samplePcmData!); setState(() { _encodedData = encodedData; _statusMessage = encodedData != null ? '编码成功:${_samplePcmData!.length}字节PCM -> ${encodedData.length}字节Opus' : '编码失败'; }); } // 解码Opus数据 Future _decodeData() async { if (!_decoderInitialized || _encodedData == null) { setState(() { _statusMessage = '解码器未初始化或没有编码数据'; }); return; } setState(() { _statusMessage = '正在解码...'; }); final decodedData = await Opus.decodeData(_encodedData!, frameSize: 960); setState(() { _decodedData = decodedData; _statusMessage = decodedData != null ? '解码成功:${_encodedData!.length}字节Opus -> ${decodedData.length}字节PCM' : '解码失败'; }); } // 释放资源 Future _disposeOpus() async { if (_encoderInitialized) { await Opus.disposeEncoder(); } if (_decoderInitialized) { await Opus.disposeDecoder(); } } // 正弦波函数 double sin(double x) { // 简单泰勒展开实现 double result = 0; double term = x; for (int i = 1; i <= 7; i += 2) { result += term; term = -term * x * x / ((i + 1) * (i + 2)); } return result; } @override Widget build(BuildContext context) { return Scaffold( appBar: AppBar( title: const Text('Opus编解码示例'), ), body: Padding( padding: const EdgeInsets.all(16.0), child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, children: [ Card( child: Padding( padding: const EdgeInsets.all(16.0), child: Column( children: [ Text( '状态:$_statusMessage', style: const TextStyle(fontWeight: FontWeight.bold), ), const SizedBox(height: 8), Text('编码器:${_encoderInitialized ? "已初始化" : "未初始化"}'), Text('解码器:${_decoderInitialized ? "已初始化" : "未初始化"}'), ], ), ), ), const SizedBox(height: 16), ElevatedButton( onPressed: _initOpus, child: const Text('初始化编解码器'), ), const SizedBox(height: 8), ElevatedButton( onPressed: _encodeData, child: const Text('编码测试数据'), ), const SizedBox(height: 8), ElevatedButton( onPressed: _decodeData, child: const Text('解码测试数据'), ), const SizedBox(height: 16), if (_samplePcmData != null) _buildDataInfoCard('源PCM数据', _samplePcmData!), const SizedBox(height: 8), if (_encodedData != null) _buildDataInfoCard('编码后的Opus数据', _encodedData!), const SizedBox(height: 8), if (_decodedData != null) _buildDataInfoCard('解码后的PCM数据', _decodedData!), ], ), ), ); } Widget _buildDataInfoCard(String title, Uint8List data) { return Card( child: Padding( padding: const EdgeInsets.all(12.0), child: Column( crossAxisAlignment: CrossAxisAlignment.start, children: [ Text( title, style: const TextStyle(fontWeight: FontWeight.bold), ), const SizedBox(height: 4), Text('大小: ${data.length} 字节'), const SizedBox(height: 4), Text('前16字节: ${_formatBytes(data)}'), ], ), ), ); } String _formatBytes(Uint8List data) { final length = data.length > 16 ? 16 : data.length; return data.sublist(0, length).map((e) => e.toRadixString(16).padLeft(2, '0')).join(' '); } }