mic_stream 0.2.0+1 copy "mic_stream: ^0.2.0+1" to clipboard
mic_stream: ^0.2.0+1 copied to clipboard

outdated

Provides a tool to get the microphone input as PCM Stream [Android only]

example/lib/main.dart

import 'dart:async';
import 'dart:math';

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';

import 'package:url_launcher/url_launcher.dart';

import 'package:mic_stream/mic_stream.dart';

enum Command {
  start,
  stop,
  change,
}

void main() => runApp(MicStreamExampleApp());

class MicStreamExampleApp extends StatefulWidget {
  @override
  _MicStreamExampleAppState createState() => _MicStreamExampleAppState();
}

class _MicStreamExampleAppState extends State<MicStreamExampleApp> with SingleTickerProviderStateMixin, WidgetsBindingObserver {
  Stream<List<int>> stream;
  StreamSubscription<List<int>> listener;
  List<int> currentSamples;

  // Refreshes the Widget for every possible tick to force a rebuild of the sound wave
  AnimationController controller;

  Color _iconColor = Colors.white;
  bool isRecording = false;
  bool memRecordingState = false;
  bool isActive;
  DateTime startTime;

  int page = 0;
  List state = ["SoundWavePage", "InformationPage"];

  @override
  void initState() {
    print("Init application");
    super.initState();
    WidgetsBinding.instance.addObserver(this);
    setState(() {
      initPlatformState();
    });
  }

  void _controlPage(int index) => setState(() => page = index);

  // Responsible for switching between recording / idle state
  void _controlMicStream({Command command: Command.change}) async {
    switch(command) {
      case Command.change:
        _changeListening();
        break;
      case Command.start:
        _startListening();
        break;
      case Command.stop:
        _stopListening();
        break;
    }
  }

  bool _changeListening() => !isRecording ? _startListening() : _stopListening();

  bool _startListening() {
    if (isRecording) return false;
    stream = microphone(audioSource: AudioSource.DEFAULT, sampleRate: 16000, channelConfig: ChannelConfig.CHANNEL_IN_MONO, audioFormat: AudioFormat.ENCODING_PCM_16BIT);

    setState(() {
      isRecording = true;
      startTime = DateTime.now();
    });

    print("Start Listening to the microphone");
    listener = stream.listen((samples) => currentSamples = samples);
    return true;
  }

  bool _stopListening() {
    if (!isRecording) return false;
    print("Stop Listening to the microphone");
    listener.cancel();

    setState(() {
      isRecording = false;
      currentSamples = null;
      startTime = null;
    });
    return true;
  }

  // Platform messages are asynchronous, so we initialize in an async method.
  Future<void> initPlatformState() async {
    if (!mounted) return;
    isActive = true;

    Statistics(false);

    controller = AnimationController(duration: Duration(seconds: 1), vsync: this)
      ..addListener(() {
        if (isRecording) setState(() {});
      })
      ..addStatusListener((status) {
        if (status == AnimationStatus.completed) controller.reverse();
        else if (status == AnimationStatus.dismissed) controller.forward();
      })
      ..forward();
  }

  Color _getBgColor() => (isRecording) ? Colors.red : Colors.cyan;
  Icon _getIcon() => (isRecording) ? Icon(Icons.stop) : Icon(Icons.keyboard_voice);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData.dark(),
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Plugin: mic_stream :: Debug'),
        ),
        floatingActionButton: FloatingActionButton(
          onPressed: _controlMicStream,
          child: _getIcon(),
          foregroundColor: _iconColor,
          backgroundColor: _getBgColor(),
          tooltip: (isRecording) ? "Stop recording" : "Start recording",
        ),
        bottomNavigationBar: BottomNavigationBar(
          items: [
            BottomNavigationBarItem(
              icon: Icon(Icons.broken_image),
              title: Text("Sound Wave"),
            ),
            BottomNavigationBarItem(
              icon: Icon(Icons.view_list),
              title: Text("Statistics"),
            )
          ],
          backgroundColor: Colors.black26,
          elevation: 20,
          currentIndex: page,
          onTap: _controlPage,
        ),
        body: (page == 0) ?
          CustomPaint(
            painter: WavePainter(currentSamples, _getBgColor(), context),
          ) :
          Statistics(isRecording, startTime: startTime,)
      ),
    );
  }

  @override
  void didChangeAppLifecycleState(AppLifecycleState state) {
    if (state == AppLifecycleState.resumed) {
      isActive = true;
      print("Resume app");

      _controlMicStream(command: memRecordingState ? Command.start : Command.stop);
    }
    else if (isActive){
      memRecordingState = isRecording;
      _controlMicStream(command: Command.stop);

      print("Pause app");
      isActive = false;
    }
  }

  @override
  void dispose() {
    listener.cancel();
    controller.dispose();
    WidgetsBinding.instance.removeObserver(this);
    super.dispose();
  }
}

class WavePainter extends CustomPainter {
  List<int> samples;
  List<Offset> points;
  Color color;
  BuildContext context;
  Size size;

  static int absMax = 0;

  WavePainter(this.samples, this.color, this.context);

  @override
  void paint(Canvas canvas, Size size) {

    this.size = context.size;
    size = this.size;

    Paint paint = new Paint()
        ..color = color
        ..strokeWidth = 1.0
        ..style = PaintingStyle.stroke;

    points = toPoints(samples);

    Path path = new Path();
    path.addPolygon(points, false);

    canvas.drawPath(path, paint);
  }

  @override
  bool shouldRepaint(CustomPainter oldPainting) => true;

  // Maps a list of ints and their indices to a list of points on a cartesian grid
  List<Offset> toPoints (List<int> samples) {
    List<Offset> points = [];
    if (samples == null) samples = List<int>.filled(size.width.toInt(), (0.5 * size.height).toInt());
    else samples.forEach((sample) => absMax = max(absMax, sample.abs()));
    for (int i = 0; i < min(size.width, samples.length).toInt(); i++) {
      points.add(new Offset(i.toDouble(), project(samples[i], absMax, size.height)));
    }
    return points;
  }

  double project(int val, int max, double height) {
    double waveHeight;
    if (max == 0) waveHeight = val.toDouble();
    else waveHeight = (val / max) * 0.5 * height;
    return waveHeight + 0.5 * height;
  }
}

class Statistics extends StatelessWidget {
  final bool isRecording;
  final DateTime startTime;

  final String url = "https://github.com/anarchuser/mic_stream";

  Statistics(this.isRecording, {this.startTime});

  @override
  Widget build(BuildContext context) {
    return ListView(
      children: <Widget> [
        ListTile(
          leading: Icon(Icons.title),
          title: Text("Microphone Streaming Example App")
        ),
        ListTile(
          leading: Icon(Icons.input),
          title: MaterialButton(
            child: Text("Github Repository"),
            onPressed: _launchURL,
          )
        ),
        ListTile(
          leading: Icon(Icons.keyboard_voice),
          title: Text((isRecording ? "Recording" : "Not recording")),
        ),
        ListTile(
          leading: Icon(Icons.access_time),
          title: Text((isRecording ? DateTime.now().difference(startTime).toString() : "Not recording"))
        ),
      ]
    );
  }

  // According to "url_launcher"'s example implementation on https://pub.dev/packages/url_launcher
  void _launchURL() => launch(url);
}
88
likes
0
pub points
91%
popularity

Publisher

unverified uploader

Provides a tool to get the microphone input as PCM Stream [Android only]

Homepage

License

unknown (LICENSE)

Dependencies

flutter, permission

More

Packages that depend on mic_stream