microphone_wave.dart 4.3 KB

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