flutter_voice_processor 1.1.2 copy "flutter_voice_processor: ^1.1.2" to clipboard
flutter_voice_processor: ^1.1.2 copied to clipboard

A Flutter audio recording plugin designed for real-time speech audio processing.

example/lib/main.dart

//
// Copyright 2020-2023 Picovoice Inc.
//
// You may not use this file except in compliance with the license. A copy of the license is located in the "LICENSE"
// file accompanying this source.
//
// Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on
// an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the
// specific language governing permissions and limitations under the License.
//

import 'dart:async';
import 'dart:math';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_voice_processor/flutter_voice_processor.dart';
import 'package:flutter_voice_processor_example/vu_meter_painter.dart';

void main() {
  runApp(MyApp());
}

class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  final int frameLength = 512;
  final int sampleRate = 16000;
  final int volumeHistoryCapacity = 5;
  final double dbOffset = 50.0;

  final List<double> _volumeHistory = [];
  double _smoothedVolumeValue = 0.0;
  bool _isButtonDisabled = false;
  bool _isProcessing = false;
  String? _errorMessage;
  VoiceProcessor? _voiceProcessor;

  @override
  void initState() {
    super.initState();
    _initVoiceProcessor();
  }

  void _initVoiceProcessor() async {
    _voiceProcessor = VoiceProcessor.instance;
  }

  Future<void> _startProcessing() async {
    setState(() {
      _isButtonDisabled = true;
    });

    _voiceProcessor?.addFrameListener(_onFrame);
    _voiceProcessor?.addErrorListener(_onError);
    try {
      if (await _voiceProcessor?.hasRecordAudioPermission() ?? false) {
        await _voiceProcessor?.start(frameLength, sampleRate);
        bool? isRecording = await _voiceProcessor?.isRecording();
        setState(() {
          _isProcessing = isRecording!;
        });
      } else {
        setState(() {
          _errorMessage = "Recording permission not granted";
        });
      }
    } on PlatformException catch (ex) {
      setState(() {
        _errorMessage = "Failed to start recorder: " + ex.toString();
      });
    } finally {
      setState(() {
        _isButtonDisabled = false;
      });
    }
  }

  Future<void> _stopProcessing() async {
    setState(() {
      _isButtonDisabled = true;
    });

    try {
      await _voiceProcessor?.stop();
    } on PlatformException catch (ex) {
      setState(() {
        _errorMessage = "Failed to stop recorder: " + ex.toString();
      });
    } finally {
      bool? isRecording = await _voiceProcessor?.isRecording();
      setState(() {
        _isButtonDisabled = false;
        _isProcessing = isRecording!;
      });
    }
  }

  void _toggleProcessing() async {
    if (_isProcessing) {
      await _stopProcessing();
    } else {
      await _startProcessing();
    }
  }

  double _calculateVolumeLevel(List<int> frame) {
    double rms = 0.0;
    for (int sample in frame) {
      rms += pow(sample, 2);
    }
    rms = sqrt(rms / frame.length);

    double dbfs = 20 * log(rms / 32767.0) / log(10);
    double normalizedValue = (dbfs + dbOffset) / dbOffset;
    return normalizedValue.clamp(0.0, 1.0);
  }

  void _onFrame(List<int> frame) {
    double volumeLevel = _calculateVolumeLevel(frame);
    if (_volumeHistory.length == volumeHistoryCapacity) {
      _volumeHistory.removeAt(0);
    }
    _volumeHistory.add(volumeLevel);

    setState(() {
      _smoothedVolumeValue =
          _volumeHistory.reduce((a, b) => a + b) / _volumeHistory.length;
    });
  }

  void _onError(VoiceProcessorException error) {
    setState(() {
      _errorMessage = error.message;
    });
  }

  Color picoBlue = Color.fromRGBO(55, 125, 255, 1);

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      home: Scaffold(
        appBar: AppBar(
          title: const Text('Voice Processor'),
        ),
        body: Column(children: [
          buildVuMeter(context),
          buildStartButton(context),
          buildErrorMessage(context)
        ]),
      ),
    );
  }

  buildVuMeter(BuildContext context) {
    return Expanded(
        flex: 2,
        child: LayoutBuilder(
          builder: (BuildContext context, BoxConstraints constraints) {
            return Container(
                alignment: Alignment.bottomCenter,
                child: CustomPaint(
                    painter: VuMeterPainter(_smoothedVolumeValue, picoBlue),
                    size: Size(constraints.maxWidth * 0.95, 50)));
          },
        ));
  }

  buildStartButton(BuildContext context) {
    final ButtonStyle buttonStyle = ElevatedButton.styleFrom(
        backgroundColor: picoBlue,
        shape: CircleBorder(),
        textStyle: TextStyle(color: Colors.white));

    return Expanded(
      flex: 2,
      child: Container(
          child: SizedBox(
              width: 150,
              height: 150,
              child: ElevatedButton(
                style: buttonStyle,
                onPressed: _isButtonDisabled || _errorMessage != null
                    ? null
                    : _toggleProcessing,
                child: Text(_isProcessing ? "Stop" : "Start",
                    style: TextStyle(fontSize: 30, color: Colors.white)),
              ))),
    );
  }

  buildErrorMessage(BuildContext context) {
    return Expanded(
        flex: 1,
        child: Container(
            alignment: Alignment.center,
            margin: EdgeInsets.all(30),
            decoration: _errorMessage == null
                ? null
                : BoxDecoration(
                    color: Colors.red, borderRadius: BorderRadius.circular(5)),
            child: _errorMessage == null
                ? null
                : Text(
                    _errorMessage!,
                    style: TextStyle(color: Colors.white, fontSize: 20),
                  )));
  }
}
24
likes
160
points
5.66k
downloads

Publisher

verified publisherpicovoice.ai

Weekly Downloads

A Flutter audio recording plugin designed for real-time speech audio processing.

Homepage
Repository (GitHub)
View/report issues

Documentation

Documentation
API reference

License

Apache-2.0 (license)

Dependencies

flutter

More

Packages that depend on flutter_voice_processor