Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions example/pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,18 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: transitive
description: flutter
source: sdk
version: "0.0.0"
js:
dependency: transitive
description:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.3"
matcher:
dependency: transitive
description:
Expand Down
28 changes: 1 addition & 27 deletions lib/flutter_audio_capture.dart
Original file line number Diff line number Diff line change
@@ -1,27 +1 @@
import 'dart:async';

import 'package:flutter/services.dart';

const AUDIO_CAPTURE_EVENT_CHANNEL_NAME = "ymd.dev/audio_capture_event_channel";

class FlutterAudioCapture {
final EventChannel _audioCaptureEventChannel =
EventChannel(AUDIO_CAPTURE_EVENT_CHANNEL_NAME);
StreamSubscription? _audioCaptureEventChannelSubscription;

Future<void> start(Function listener, Function onError,
{int sampleRate = 44000, int bufferSize = 5000}) async {
if (_audioCaptureEventChannelSubscription != null) return;
_audioCaptureEventChannelSubscription = _audioCaptureEventChannel
.receiveBroadcastStream({
"sampleRate": sampleRate,
"bufferSize": bufferSize
}).listen(listener as void Function(dynamic)?, onError: onError);
}

Future<void> stop() async {
if (_audioCaptureEventChannelSubscription == null) return;
_audioCaptureEventChannelSubscription!.cancel();
_audioCaptureEventChannelSubscription = null;
}
}
export 'native.dart' if (dart.library.html) 'web.dart';
10 changes: 10 additions & 0 deletions lib/interface.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
class IAudioCapture {
Future<void> start(Function listener, Function onError,
{int sampleRate = 44000, int bufferSize = 5000}) async {
throw UnimplementedError();
}

Future<void> stop() async {
throw UnimplementedError();
}
}
30 changes: 30 additions & 0 deletions lib/native.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import 'dart:async';

import 'package:flutter/services.dart';
import 'package:flutter_audio_capture/interface.dart';

const AUDIO_CAPTURE_EVENT_CHANNEL_NAME = "ymd.dev/audio_capture_event_channel";

class FlutterAudioCapture implements IAudioCapture {
final EventChannel _audioCaptureEventChannel =
EventChannel(AUDIO_CAPTURE_EVENT_CHANNEL_NAME);
StreamSubscription? _audioCaptureEventChannelSubscription;

@override
Future<void> start(Function listener, Function onError,
{int sampleRate = 44000, int bufferSize = 5000}) async {
if (_audioCaptureEventChannelSubscription != null) return;
_audioCaptureEventChannelSubscription = _audioCaptureEventChannel
.receiveBroadcastStream({
"sampleRate": sampleRate,
"bufferSize": bufferSize
}).listen(listener as void Function(dynamic)?, onError: onError);
}

@override
Future<void> stop() async {
if (_audioCaptureEventChannelSubscription == null) return;
_audioCaptureEventChannelSubscription!.cancel();
_audioCaptureEventChannelSubscription = null;
}
}
67 changes: 67 additions & 0 deletions lib/web.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
import 'dart:async';
import 'dart:html';
import 'dart:web_audio';
import 'dart:typed_data';

import 'package:flutter_audio_capture/interface.dart';

class FlutterAudioCapture extends IAudioCapture {
StreamController<Float64List>? controller;

@override
Future<void> start(Function listener, Function onError,
{int sampleRate = 44000, int bufferSize = 5000}) async {
if (controller == null) {
controller = StreamController();
controller!.stream.listen((event) {
listener(event);
}, onError: (error) {
onError();
});
startRecording();
}
}

@override
Future<void> stop() async {
if (controller != null) {
await controller!.close();
controller = null;
}
}

Float64List _convertBytesToFloat64(dynamic buf) {
return Float64List.fromList(buf);
}

startRecording() async {
// build up stuff we need
final stream = await window.navigator.mediaDevices!
.getUserMedia({"audio": true, "video": false});
final context = AudioContext();
final source = context.createMediaStreamSource(stream);
final processor = context.createScriptProcessor(4096, 1, 1);
source.connectNode(processor);
processor.connectNode(context.destination!);
final audioStream = processor.onAudioProcess;
var closed = false;
audioStream.listen((e) async {
var sr = e.inputBuffer!.sampleRate;
var buf = e.inputBuffer!.getChannelData(0);
if (controller != null &&
!controller!.isClosed &&
!controller!.isPaused &&
!closed) {
try {
controller!.add(_convertBytesToFloat64(buf));
} catch (e) {}
} else {
// cancel recording stuff...
if (!closed) {
closed = true;
await context.close();
}
}
});
}
}
12 changes: 12 additions & 0 deletions pubspec.lock
Original file line number Diff line number Diff line change
Expand Up @@ -151,13 +151,25 @@ packages:
description: flutter
source: sdk
version: "0.0.0"
flutter_web_plugins:
dependency: "direct main"
description: flutter
source: sdk
version: "0.0.0"
glob:
dependency: transitive
description:
name: glob
url: "https://pub.dartlang.org"
source: hosted
version: "2.0.1"
js:
dependency: transitive
description:
name: js
url: "https://pub.dartlang.org"
source: hosted
version: "0.6.3"
logging:
dependency: transitive
description:
Expand Down
4 changes: 3 additions & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,8 @@ environment:
dependencies:
flutter:
sdk: flutter
flutter_web_plugins:
sdk: flutter

dev_dependencies:
mockito: ^5.0.5
Expand All @@ -20,8 +22,8 @@ flutter:
plugin:
platforms:
android:
package: com.ymd.flutter_audio_capture
pluginClass: FlutterAudioCapturePlugin
package: com.ymd.flutter_audio_capture
ios:
pluginClass: FlutterAudioCapturePlugin
linux:
Expand Down