microphone_wave.dart 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. import 'dart:async';
  2. import 'package:flutter/material.dart';
  3. import 'package:flutter_sound/flutter_sound.dart';
  4. /// 麦克风输入可视化
  5. class MicrophoneWave extends StatefulWidget {
  6. final FlutterSoundRecorder recorder;
  7. final double width;
  8. final double height;
  9. final bool isRecording;
  10. MicrophoneWave({
  11. required this.recorder,
  12. required this.isRecording,
  13. this.width = 250.0,
  14. this.height = 40.0,
  15. });
  16. @override
  17. _MicrophoneWaveState createState() => _MicrophoneWaveState();
  18. }
  19. class _MicrophoneWaveState extends State<MicrophoneWave>
  20. with SingleTickerProviderStateMixin {
  21. StreamSubscription? _recorderSubscription;
  22. late AnimationController _controller;
  23. late Animation<double> _animation;
  24. List<double> _loudnessValues = [];
  25. @override
  26. void initState() {
  27. super.initState();
  28. _controller = AnimationController(
  29. vsync: this,
  30. duration: Duration(milliseconds: 50),
  31. );
  32. _animation = Tween<double>(begin: 0.0, end: 1.0).animate(_controller)
  33. ..addListener(() {
  34. setState(() {});
  35. });
  36. /// 如果没有分贝数据,请参考 https://github.com/Canardoux/flutter_sound/issues/838
  37. /// 将 flutter_sound_web 的 flutter_sound_recorder.js 从 flutter_sound_web/js/flutter_sound 复制到 flutter_sound_web/src
  38. /// 监听 onProgress 回调
  39. _recorderSubscription =
  40. widget.recorder.onProgress!.listen((RecordingDisposition e) {
  41. /// 输出响度,响度范围是0-120
  42. /// 0表示没有声音,120表示最大声音
  43. final loudness = ((e.decibels ?? 0) + 5) / 120;
  44. setState(() {
  45. _loudnessValues.add(loudness);
  46. });
  47. if (_loudnessValues.length > widget.width) {
  48. _loudnessValues.removeAt(0);
  49. }
  50. _controller.forward(from: 0.0);
  51. }, onError: (err) {
  52. print('onError: $err');
  53. });
  54. }
  55. @override
  56. void didUpdateWidget(covariant MicrophoneWave oldWidget) {
  57. super.didUpdateWidget(oldWidget);
  58. if (oldWidget.isRecording != widget.isRecording) {
  59. if (widget.isRecording) {
  60. _loudnessValues.clear();
  61. }
  62. }
  63. }
  64. @override
  65. void dispose() {
  66. super.dispose();
  67. _controller.dispose();
  68. _recorderSubscription?.cancel();
  69. _recorderSubscription = null;
  70. }
  71. @override
  72. Widget build(BuildContext context) {
  73. return Container(
  74. width: widget.width,
  75. height: widget.height,
  76. child: Row(
  77. crossAxisAlignment: CrossAxisAlignment.center,
  78. children: [
  79. Text(
  80. "麦克风输入:",
  81. style: TextStyle(
  82. height: 1,
  83. color: Colors.black87,
  84. ),
  85. ),
  86. Expanded(
  87. child: ClipPath(
  88. child: CustomPaint(
  89. painter: _MicrophoneVisualizerPainter(
  90. loudnessList: _loudnessValues,
  91. animationValue: _animation.value,
  92. isRecording: widget.isRecording,
  93. ),
  94. size: Size(widget.width, widget.height),
  95. ),
  96. )),
  97. ],
  98. ),
  99. );
  100. }
  101. }
  102. class _MicrophoneVisualizerPainter extends CustomPainter {
  103. final List<double> loudnessList;
  104. final double animationValue;
  105. final bool isRecording;
  106. _MicrophoneVisualizerPainter({
  107. required this.loudnessList,
  108. required this.animationValue,
  109. this.isRecording = false,
  110. });
  111. @override
  112. void paint(Canvas canvas, Size size) {
  113. final double sampleStep = 5.0;
  114. final offsetX = sampleStep * animationValue;
  115. final double centerY = size.height / 2;
  116. Paint linePaint = Paint()
  117. ..color = Colors.grey
  118. ..strokeWidth = 1.0;
  119. /// 绘制中轴线
  120. canvas.drawLine(Offset(0, centerY), Offset(size.width, centerY), linePaint);
  121. if (!isRecording) return;
  122. linePaint = Paint()
  123. ..color = Colors.blue
  124. ..strokeWidth = 2.0;
  125. for (int i = 0; i < loudnessList.length; i++) {
  126. final loudness = loudnessList[loudnessList.length - i - 1];
  127. final x = i * sampleStep + offsetX;
  128. final y = centerY + loudness * centerY;
  129. final y2 = centerY - loudness * centerY;
  130. canvas.drawLine(Offset(x, y), Offset(x, y2), linePaint);
  131. }
  132. }
  133. @override
  134. bool shouldRepaint(_MicrophoneVisualizerPainter oldDelegate) {
  135. return oldDelegate.animationValue != animationValue;
  136. }
  137. }