Skip to content

Commit 3e814ec

Browse files
committed
update.
1 parent 5ddda8c commit 3e814ec

File tree

4 files changed

+145
-38
lines changed

4 files changed

+145
-38
lines changed

example/lib/widgets/participant.dart

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -177,13 +177,14 @@ abstract class _ParticipantWidgetState<T extends ParticipantWidget>
177177
child: ParticipantStatsWidget(
178178
participant: widget.participant,
179179
)),
180-
Positioned(
180+
if(activeAudioTrack != null && !activeAudioTrack!.muted) Positioned(
181181
top: 10,
182182
right: 10,
183183
left: 10,
184184
bottom: 10,
185185
child: SoundWaveformWidget(
186-
participant: widget.participant,
186+
key: ValueKey(activeAudioTrack!.hashCode),
187+
audioTrack: activeAudioTrack!,
187188
width: 8,
188189
),
189190
),
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
import 'package:collection/collection.dart';
2+
import 'package:flutter/material.dart';
3+
import 'package:flutter_webrtc/flutter_webrtc.dart';
4+
import 'package:livekit_client/livekit_client.dart';
5+
import 'package:livekit_example/theme.dart';
6+
7+
class SoundWaveformWidget extends StatefulWidget {
8+
final int count;
9+
final double width;
10+
final double minHeight;
11+
final double maxHeight;
12+
final int durationInMilliseconds;
13+
const SoundWaveformWidget({
14+
super.key,
15+
required this.audioTrack,
16+
this.count = 7,
17+
this.width = 5,
18+
this.minHeight = 8,
19+
this.maxHeight = 100,
20+
this.durationInMilliseconds = 500,
21+
});
22+
final AudioTrack audioTrack;
23+
@override
24+
State<SoundWaveformWidget> createState() => _SoundWaveformWidgetState();
25+
}
26+
27+
class _SoundWaveformWidgetState extends State<SoundWaveformWidget>
28+
with TickerProviderStateMixin {
29+
late AnimationController controller;
30+
List<double> samples = [0,0,0,0,0,0,0];
31+
EventsListener<TrackEvent>? _listener;
32+
33+
void _startVisualizer(AudioTrack track) async {
34+
await widget.audioTrack.startVisualizer();
35+
_listener?.dispose();
36+
_listener = track.createListener();
37+
_listener?.on<AudioVisualizerEvent>((e) {
38+
if(mounted) {
39+
setState(() {
40+
samples = e.event.map((e) => ((e as num) * 100).toDouble()).toList();
41+
});
42+
}
43+
});
44+
}
45+
46+
void _stopVisualizer(AudioTrack track) async {
47+
await widget.audioTrack.stopVisualizer();
48+
_listener?.dispose();
49+
}
50+
51+
@override
52+
void initState() {
53+
super.initState();
54+
55+
_startVisualizer(widget.audioTrack);
56+
57+
controller = AnimationController(
58+
vsync: this,
59+
duration: Duration(
60+
milliseconds: widget.durationInMilliseconds,
61+
))
62+
..repeat();
63+
}
64+
65+
@override
66+
void dispose() {
67+
controller.dispose();
68+
_stopVisualizer(widget.audioTrack);
69+
super.dispose();
70+
}
71+
72+
@override
73+
Widget build(BuildContext context) {
74+
final count = widget.count;
75+
final minHeight = widget.minHeight;
76+
final maxHeight = widget.maxHeight;
77+
return AnimatedBuilder(
78+
animation: controller,
79+
builder: (c, child) {
80+
//double t = controller.value;
81+
//int current = (samples.length * t).floor();
82+
return Row(
83+
mainAxisSize: MainAxisSize.min,
84+
children: List.generate(
85+
count,
86+
(i) => AnimatedContainer(
87+
duration: Duration(
88+
milliseconds: widget.durationInMilliseconds ~/ count),
89+
margin: i == (samples.length - 1)
90+
? EdgeInsets.zero
91+
: const EdgeInsets.only(right: 5),
92+
height: samples[i] < minHeight
93+
? minHeight
94+
: samples[i] > maxHeight
95+
? maxHeight
96+
: samples[i],
97+
width: widget.width,
98+
decoration: BoxDecoration(
99+
color: Colors.white,
100+
borderRadius: BorderRadius.circular(9999),
101+
),
102+
),
103+
),
104+
);
105+
},
106+
);
107+
}
108+
}

lib/src/track/local/local.dart

Lines changed: 33 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import '../../extensions.dart';
2727
import '../../internal/events.dart';
2828
import '../../logger.dart';
2929
import '../../participant/remote.dart';
30+
import '../../support/native.dart';
3031
import '../../support/platform.dart';
3132
import '../../types/other.dart';
3233
import '../options.dart';
@@ -58,7 +59,38 @@ mixin VideoTrack on Track {
5859
}
5960

6061
/// Used to group [LocalAudioTrack] and [RemoteAudioTrack].
61-
mixin AudioTrack on Track {}
62+
mixin AudioTrack on Track {
63+
64+
EventChannel? _eventChannel ;
65+
StreamSubscription? _streamSubscription;
66+
67+
Future<void> startVisualizer() async {
68+
if(_eventChannel != null) {
69+
return;
70+
}
71+
72+
await Native.startVisualizer(mediaStreamTrack.id!);
73+
74+
_eventChannel = EventChannel('io.livekit.audio.visualizer/eventChannel-${mediaStreamTrack.id}');
75+
_streamSubscription = _eventChannel?.receiveBroadcastStream().listen((event) {
76+
//logger.fine('[$objectId] visualizer event(${event})');
77+
events.emit(AudioVisualizerEvent(
78+
track: this,
79+
event: event,
80+
));
81+
});
82+
}
83+
84+
Future<void> stopVisualizer() async {
85+
if(_eventChannel == null) {
86+
return;
87+
}
88+
await Native.stopVisualizer(mediaStreamTrack.id!);
89+
await _streamSubscription?.cancel();
90+
_streamSubscription = null;
91+
_eventChannel = null;
92+
}
93+
}
6294

6395
/// Base class for [LocalAudioTrack] and [LocalVideoTrack].
6496
abstract class LocalTrack extends Track {

lib/src/track/track.dart

Lines changed: 1 addition & 35 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,6 @@
1414

1515
import 'dart:async';
1616

17-
import 'package:flutter/services.dart';
1817
import 'package:flutter_webrtc/flutter_webrtc.dart' as rtc;
1918
import 'package:meta/meta.dart';
2019
import 'package:uuid/uuid.dart';
@@ -26,7 +25,6 @@ import '../logger.dart';
2625
import '../managers/event.dart';
2726
import '../stats/stats.dart';
2827
import '../support/disposable.dart';
29-
import '../support/native.dart';
3028
import '../types/other.dart';
3129

3230
/// Wrapper around a MediaStreamTrack with additional metadata.
@@ -109,11 +107,6 @@ abstract class Track extends DisposableChangeNotifier
109107
}
110108

111109
logger.fine('$objectId.start()');
112-
113-
if(source == TrackSource.microphone) {
114-
await Native.startVisualizer(mediaStreamTrack.id!);
115-
listenVisualizerEvent();
116-
}
117110

118111
startMonitor();
119112

@@ -130,11 +123,6 @@ abstract class Track extends DisposableChangeNotifier
130123
return false;
131124
}
132125

133-
if(source == TrackSource.microphone) {
134-
await Native.stopVisualizer(mediaStreamTrack.id!);
135-
stopVisualizerEventListen();
136-
}
137-
138126
stopMonitor();
139127

140128
logger.fine('$objectId.stop()');
@@ -143,29 +131,6 @@ abstract class Track extends DisposableChangeNotifier
143131
return true;
144132
}
145133

146-
147-
EventChannel? _eventChannel ;
148-
StreamSubscription? _streamSubscription;
149-
150-
@internal
151-
void listenVisualizerEvent() {
152-
_eventChannel = EventChannel('io.livekit.audio.visualizer/eventChannel-${mediaStreamTrack.id}');
153-
154-
_eventChannel?.receiveBroadcastStream().listen((event) {
155-
//logger.fine('[$objectId] visualizer event(${event})');
156-
events.emit(AudioVisualizerEvent(
157-
track: this,
158-
event: event,
159-
));
160-
});
161-
}
162-
163-
@internal
164-
void stopVisualizerEventListen() {
165-
_streamSubscription?.cancel();
166-
_eventChannel = null;
167-
}
168-
169134
Future<void> enable() async {
170135
logger.fine('$objectId.enable() enabling ${mediaStreamTrack.objectId}...');
171136
try {
@@ -193,6 +158,7 @@ abstract class Track extends DisposableChangeNotifier
193158

194159
Timer? _monitorTimer;
195160

161+
@internal
196162
Future<bool> monitorStats();
197163

198164
@internal

0 commit comments

Comments
 (0)