voice_audio 0.3.1
voice_audio: ^0.3.1 copied to clipboard
Record voice memos straight to Opus and play them back. Audio never crosses into Dart as PCM - a recording is one compressed blob, about 120 KB a minute.
/// Demo and on-device test app for `voice_audio`.
///
/// Two halves, because the package needs both kinds of testing:
///
/// * **Memos** — a small but real voice memo app. Record with a live meter, a list with
/// durations and waveforms, play with pause and scrub, export, pick a device. This is how
/// you find out whether the audio *sounds* right, which no assertion can tell you.
/// * **Checks** — the same API driven by about twenty assertions, run on the device. The C++
/// and Dart suites run on a desktop against a null or PortAudio backend; a phone is AAudio,
/// a different codec build and a real microphone, and none of that is reachable from a CI
/// machine. So the checks travel with the app.
///
/// Built for Linux and Android. The package supports macOS, Windows and iOS too, but those
/// have not been compiled — see the README.
library;
import 'package:flutter/material.dart';
import 'package:voice_audio/voice_audio.dart';
import 'checks_page.dart';
import 'memo_page.dart';
void main() {
runApp(const VoiceAudioDemo());
}
class VoiceAudioDemo extends StatelessWidget {
const VoiceAudioDemo({super.key});
@override
Widget build(BuildContext context) => MaterialApp(
title: 'voice_audio',
theme: ThemeData(colorSchemeSeed: Colors.indigo, useMaterial3: true),
darkTheme: ThemeData(
colorSchemeSeed: Colors.indigo,
brightness: Brightness.dark,
useMaterial3: true,
),
home: const Home(),
);
}
class Home extends StatefulWidget {
const Home({super.key});
@override
State<Home> createState() => _HomeState();
}
class _HomeState extends State<Home> {
int _tab = 0;
@override
void dispose() {
// One engine for the whole app: one capture thread, one playback stream. It is brought up
// by whichever page needs it first and put down here, once.
VoiceAudio.instance.shutdown();
super.dispose();
}
@override
Widget build(BuildContext context) {
return Scaffold(
body: IndexedStack(
index: _tab,
children: const [MemoPage(), ChecksPage()],
),
bottomNavigationBar: NavigationBar(
selectedIndex: _tab,
onDestinationSelected: (index) => setState(() => _tab = index),
destinations: const [
NavigationDestination(
icon: Icon(Icons.mic_none),
selectedIcon: Icon(Icons.mic),
label: 'Memos',
),
NavigationDestination(
icon: Icon(Icons.fact_check_outlined),
selectedIcon: Icon(Icons.fact_check),
label: 'Checks',
),
],
),
);
}
}