amr_plugin 0.1.0
amr_plugin: ^0.1.0 copied to clipboard
A Flutter plugin that provides AMR (Adaptive Multi-Rate) audio format conversion capabilities for both iOS and Android platforms. Supports bidirectional conversion between AMR-NB/AMR-WB and WAV audio [...]
import 'dart:io';
import 'package:flutter/material.dart';
import 'dart:async';
import 'package:amr_plugin/amr_plugin.dart';
import 'package:audioplayers/audioplayers.dart';
import 'package:path_provider/path_provider.dart';
import 'package:http/http.dart' as http;
void main() {
runApp(const MyApp());
}
class MyApp extends StatefulWidget {
const MyApp({super.key});
@override
State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
final _urlController = TextEditingController();
String? _downloadedFilePath;
String? _convertedFilePath;
int? _downloadedFileSize;
int? _convertedFileSize;
String _status = '';
bool _isConverting = false;
bool _isWavToAmr = true;
final AudioPlayer _audioPlayer = AudioPlayer();
Map<String, dynamic>? _codecSupport;
Future<void> _downloadFile(String url) async {
setState(() {
_status = '正在下载...';
_downloadedFilePath = null;
_convertedFilePath = null;
_downloadedFileSize = null;
_convertedFileSize = null;
});
try {
final response = await http.get(Uri.parse(url));
if (response.statusCode == 200) {
final ext = url.toLowerCase().endsWith('.amr') ? 'amr' : 'wav';
final dir = await getApplicationDocumentsDirectory();
final file = File('${dir.path}/input.$ext');
await file.writeAsBytes(response.bodyBytes);
final fileSize = await file.length();
setState(() {
_downloadedFilePath = file.path;
_downloadedFileSize = fileSize;
_status = '下载完成: ${file.path} ($fileSize bytes)';
});
} else {
setState(() {
_status = '下载失败: ${response.statusCode}';
});
}
} catch (e) {
setState(() {
_status = '下载异常: $e';
});
}
}
Future<void> _convert() async {
if (_downloadedFilePath == null) return;
setState(() {
_isConverting = true;
_status = '正在转换...';
_convertedFilePath = null;
_convertedFileSize = null;
});
try {
String? resultPath;
if (_isWavToAmr) {
resultPath = await AmrPlugin.convertWavToAmr(_downloadedFilePath!, sampleRate: 8000, bitRate: 12200);
} else {
resultPath = await AmrPlugin.convertAmrToWav(_downloadedFilePath!, sampleRate: 8000);
}
if (resultPath != null) {
final fileSize = await File(resultPath).length();
setState(() {
_convertedFilePath = resultPath;
_convertedFileSize = fileSize;
_status = '转换完成: $resultPath ($fileSize bytes)';
});
} else {
setState(() {
_status = '转换失败';
});
}
} catch (e) {
setState(() {
_status = '转换异常: $e';
});
} finally {
setState(() {
_isConverting = false;
});
}
}
Future<void> _playConverted() async {
if (_convertedFilePath == null) return;
await _audioPlayer.stop();
await _audioPlayer.play(DeviceFileSource(_convertedFilePath!));
}
Future<void> _uploadConvertedFile() async {
if (_convertedFilePath == null) return;
setState(() {
_status = '正在上传...';
});
try {
var uri = Uri.parse('http://192.168.1.58:8080/upload');
var request = http.MultipartRequest('POST', uri);
request.files.add(
await http.MultipartFile.fromPath('file', _convertedFilePath!),
);
var response = await request.send();
if (response.statusCode == 200) {
final respStr = await response.stream.bytesToString();
setState(() {
_status = '上传成功: $respStr';
});
} else {
setState(() {
_status = '上传失败: \\${response.statusCode}';
});
}
} catch (e) {
setState(() {
_status = '上传异常: $e';
});
}
}
Future<void> _testCodecSupport() async {
setState(() {
_status = '正在测试MediaCodec支持...';
});
try {
final support = await AmrPlugin.testMediaCodecSupport();
setState(() {
_codecSupport = support;
if (support != null) {
final supportList = support.entries
.map((e) => '${e.key}: ${e.value ? '✅' : '❌'}')
.join('\n');
_status = 'MediaCodec支持测试完成:\n$supportList';
} else {
_status = 'MediaCodec支持测试失败';
}
});
} catch (e) {
setState(() {
_status = 'MediaCodec测试异常: $e';
});
}
}
@override
void dispose() {
_audioPlayer.dispose();
_urlController.dispose();
super.dispose();
}
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Scaffold(
appBar: AppBar(
title: const Text('AMR/WAV 转换与播放 Demo'),
),
body: SingleChildScrollView(child: Padding(
padding: const EdgeInsets.all(16.0),
child: Column(
crossAxisAlignment: CrossAxisAlignment.stretch,
children: [
TextField(
controller: _urlController,
decoration: const InputDecoration(
labelText: '输入 wav/amr 文件的 URL',
border: OutlineInputBorder(),
),
),
const SizedBox(height: 12),
Row(
children: [
Expanded(
child: ElevatedButton(
onPressed: () {
if (_urlController.text.isNotEmpty) {
_downloadFile(_urlController.text);
}
},
child: const Text('下载'),
),
),
const SizedBox(width: 12),
Expanded(
child: DropdownButton<bool>(
value: _isWavToAmr,
items: const [
DropdownMenuItem(value: true, child: Text('WAV → AMR')),
DropdownMenuItem(value: false, child: Text('AMR → WAV')),
],
onChanged: (v) {
setState(() {
_isWavToAmr = v ?? true;
});
},
),
),
],
),
const SizedBox(height: 12),
Visibility(child: ElevatedButton(
onPressed: _testCodecSupport,
child: const Text('测试MediaCodec支持'),
), visible: false),
const SizedBox(height: 12),
ElevatedButton(
onPressed: (_downloadedFilePath != null && !_isConverting)
? _convert
: null,
child: _isConverting ? const CircularProgressIndicator() : const Text('转换'),
),
const SizedBox(height: 12),
if (_convertedFilePath != null)
ElevatedButton(
onPressed: _playConverted,
child: const Text('播放转换后文件'),
),
if (_convertedFilePath != null)
ElevatedButton(
onPressed: _uploadConvertedFile,
child: const Text('上传转换后文件'),
),
const SizedBox(height: 12),
Container(
padding: const EdgeInsets.all(8),
decoration: BoxDecoration(
border: Border.all(color: Colors.grey),
borderRadius: BorderRadius.circular(4),
),
child: Column(
crossAxisAlignment: CrossAxisAlignment.start,
children: [
const Text('状态信息:', style: TextStyle(fontWeight: FontWeight.bold)),
const SizedBox(height: 4),
Text(_status),
if (_downloadedFilePath != null) ...[
const SizedBox(height: 8),
Text('下载文件: $_downloadedFilePath'),
Text('下载文件大小: ${_downloadedFileSize ?? '-'} bytes'),
],
if (_convertedFilePath != null) ...[
const SizedBox(height: 8),
Text('转换后文件: $_convertedFilePath'),
Text('转换后文件大小: ${_convertedFileSize ?? '-'} bytes'),
],
if (_codecSupport != null) ...[
const SizedBox(height: 8),
const Text('MediaCodec支持情况:', style: TextStyle(fontWeight: FontWeight.bold)),
for (final entry in _codecSupport!.entries)
Text('${entry.key}: ${entry.value ? '✅ 支持' : '❌ 不支持'}'),
],
],
),
),
],
),
),
),
)
);
}
}