orbit_voice 0.4.0
orbit_voice: ^0.4.0 copied to clipboard
Orbit Voice Flutter SDK — LiveKit connect, party-room audio (media stream, mic release, headset routing, VAD gate), no ovk_ in the client.
Orbit Voice (Flutter) #
Advanced Orbit Voice client for Flutter. Connect with a session from your Edge — never put ovk_ API keys in the app.
Noise cancellation #
On by default. Connect a room and the capture path runs DeepFilterNet 3 inside the WebRTC APM, after echo cancellation and noise suppression — so it removes the fans, traffic, keyboard and room hiss those stages leave behind, then an expander and soft limiter clean up the tail. No setup, no asset copying, no native toolchain in your app.
// Nothing required. This already denoises:
await engine.loginRoom(session);
Profiles #
engine.audio = OrbitAudioSettings.of(OrbitAudioProfile.balanced);
| Profile | Denoise | Opus | Use for |
|---|---|---|---|
studio (default) |
full | 64 kbps, DTX off | hosts, podcasts, anything recorded |
balanced |
full | 48 kbps, DTX on | large rooms, mobile data |
lowData |
WebRTC only | 24 kbps | weak networks, low-end devices |
Every field is individually settable when a preset is not quite right:
engine.audio = OrbitAudioSettings.of(OrbitAudioProfile.studio)
.copyWith(denoiseStrength: 0.6, maxBitrate: 56000);
Talk vs. music rooms #
A speech-enhancement model treats a backing track as noise, so karaoke and listening rooms must not run it:
await engine.setRoomAudioMode(OrbitRoomAudioMode.music); // expander only
await engine.setRoomAudioMode(OrbitRoomAudioMode.talk); // full chain
OrbitRoomAudioMode.music is for singing into the mic. Publishing a song
file as its own LiveKit track is a separate, opt-in API (below).
Publish a song (opt-in) #
Backward compatible: if the app never calls publishSong, mic take / mute /
seats / denoise behave exactly as before. Song publish is a second audio track
(name: music); it is never mixed into the mic capture pipeline as the mic
path.
await engine.publishSong(Uri.parse('https://example.com/clip.mp3'));
// or Uri.file('/path/to/song.m4a')
print(engine.isSongPublishing); // true while live
await engine.stopSong();
Listeners hear it as a normal remote LiveKit audio track. Do not also play the same file locally on listeners (double audio). Presence billing uses the same audio 1× rate as mic — one session per user even if mic + song are both open.
While the song is live the SDK switches to OrbitRoomAudioMode.music (and
restores the previous mode on stopSong if it was the one that flipped it).
Turning it off #
await engine.setNoiseCancellation(false); // live A/B, or a user-facing switch
Checking it on device #
print(await engine.denoise?.stats());
// blocks > 0 means the stage is in the path; speechModelActive means the
// model itself is running, not just the expander.
What ships #
The model (7.6 MB) and the native library are bundled — Android arm64-v8a
(11 MB) and iOS device plus arm64 simulator (31 MB). On other Android ABIs, on
Intel simulators, and on web, the model is skipped and the expander runs alone.
That path is a clean fallback, not an error.
Features (Rave-parity audio) #
- DeepFilterNet 3 capture denoise with talk/music modes and a runtime toggle
- Android media-stream ADM (
USAGE_MEDIA/ speech), HW APM off viaflutter_webrtc1.6+ options (no pub-cache patches) - 48 kHz sample rate for software NS
- iOS
playAndRecord+videoChat(not earpiecevoiceChat) - Mic release after mute so other apps can record + audio focus release
- Headset routing that does not jump to speaker during re-enumeration
- Remote volume + VAD noise gate + stuck-silence heal
- Speaking levels feed, op serialization, publish-permission wait
- Pre-connect prefetch (TTL / claim)
OrbitSessionFetcher— host app supplies tokens- Opt-in
publishSong/stopSong(second LiveKit audio track + billing)
Install #
dependencies:
orbit_voice: ^0.4.0
Usage #
import 'package:orbit_voice/orbit_voice.dart';
Future<void> main() async {
WidgetsFlutterBinding.ensureInitialized();
await OrbitAudioBootstrap.ensureInitialized();
final engine = OrbitEngine.instance;
engine.sessionFetcher = ({
required String roomId,
required bool openMic,
bool onSeat = false,
bool leaveSeat = false,
bool billingOnly = false,
}) async {
// Call YOUR Edge — never ovk_ here.
// leaveSeat/billingOnly → Edge /leave billing path.
return OrbitVoiceSession.fromJson(await mintFromEdge(...));
};
engine.prefetchSession(roomId); // on room tap
// ...
final session = await engine.fetchSessionCached(
roomId: roomId,
openMic: false,
);
if (session != null) await engine.loginRoom(session);
}
License #
Apache-2.0. LiveKit / WebRTC are Apache-2.0; DeepFilterNet and its model are
MIT (assets/models/DeepFilterNet-LICENSE-MIT). Keep all three notices in your
app’s open-source licenses screen.