flet-audio-recorder 0.2.0.dev17__py3-none-any.whl → 0.2.0.dev50__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Potentially problematic release.
This version of flet-audio-recorder might be problematic. Click here for more details.
- flet_audio_recorder/__init__.py +8 -2
- flet_audio_recorder/audio_recorder.py +212 -277
- flet_audio_recorder/types.py +336 -0
- flet_audio_recorder-0.2.0.dev50.dist-info/METADATA +69 -0
- flet_audio_recorder-0.2.0.dev50.dist-info/RECORD +18 -0
- {flet_audio_recorder-0.2.0.dev17.dist-info → flet_audio_recorder-0.2.0.dev50.dist-info}/WHEEL +1 -1
- flet_audio_recorder-0.2.0.dev50.dist-info/licenses/LICENSE +201 -0
- flutter/flet_audio_recorder/lib/flet_audio_recorder.dart +1 -1
- flutter/flet_audio_recorder/lib/src/audio_recorder.dart +67 -141
- flutter/flet_audio_recorder/lib/src/extension.dart +15 -0
- flutter/flet_audio_recorder/lib/src/utils/audio_recorder.dart +70 -23
- flutter/flet_audio_recorder/pubspec.lock +141 -84
- flutter/flet_audio_recorder/pubspec.yaml +7 -2
- flet_audio_recorder-0.2.0.dev17.dist-info/METADATA +0 -93
- flet_audio_recorder-0.2.0.dev17.dist-info/RECORD +0 -16
- flutter/flet_audio_recorder/lib/src/create_control.dart +0 -17
- {flet_audio_recorder-0.2.0.dev17.dist-info → flet_audio_recorder-0.2.0.dev50.dist-info}/top_level.txt +0 -0
|
@@ -1,5 +1,4 @@
|
|
|
1
1
|
import 'dart:async';
|
|
2
|
-
import 'dart:convert';
|
|
3
2
|
|
|
4
3
|
import 'package:flet/flet.dart';
|
|
5
4
|
import 'package:flutter/widgets.dart';
|
|
@@ -7,159 +6,86 @@ import 'package:record/record.dart';
|
|
|
7
6
|
|
|
8
7
|
import 'utils/audio_recorder.dart';
|
|
9
8
|
|
|
10
|
-
class
|
|
11
|
-
|
|
12
|
-
final Control control;
|
|
13
|
-
final FletControlBackend backend;
|
|
9
|
+
class AudioRecorderService extends FletService {
|
|
10
|
+
AudioRecorderService({required super.control});
|
|
14
11
|
|
|
15
|
-
const AudioRecorderControl(
|
|
16
|
-
{super.key,
|
|
17
|
-
required this.parent,
|
|
18
|
-
required this.control,
|
|
19
|
-
required this.backend});
|
|
20
|
-
|
|
21
|
-
@override
|
|
22
|
-
State<AudioRecorderControl> createState() => _AudioRecorderControlState();
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
class _AudioRecorderControlState extends State<AudioRecorderControl>
|
|
26
|
-
with FletStoreMixin {
|
|
27
12
|
AudioRecorder? recorder;
|
|
28
|
-
void Function(RecordState)? _onStateChanged;
|
|
29
13
|
StreamSubscription? _onStateChangedSubscription;
|
|
30
14
|
|
|
31
15
|
@override
|
|
32
|
-
void
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
recorder = AudioRecorder();
|
|
37
|
-
recorder = widget.control.state["recorder"] = recorder;
|
|
38
|
-
}
|
|
16
|
+
void init() {
|
|
17
|
+
super.init();
|
|
18
|
+
debugPrint("AudioRecorder.init($hashCode)");
|
|
19
|
+
control.addInvokeMethodListener(_invokeMethod);
|
|
39
20
|
|
|
40
|
-
|
|
41
|
-
_onStateChanged?.call(state);
|
|
42
|
-
});
|
|
21
|
+
recorder = AudioRecorder();
|
|
43
22
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
void _onRemove() {
|
|
50
|
-
debugPrint("AudioRecorder.remove($hashCode)");
|
|
51
|
-
widget.control.state["recorder"]?.dispose();
|
|
52
|
-
widget.backend.unsubscribeMethods(widget.control.id);
|
|
23
|
+
_onStateChangedSubscription = recorder!.onStateChanged().listen((state) {
|
|
24
|
+
_onStateChanged.call(state);
|
|
25
|
+
});
|
|
53
26
|
}
|
|
54
27
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
28
|
+
void _onStateChanged(RecordState state) {
|
|
29
|
+
var stateMap = {
|
|
30
|
+
RecordState.record: "recording",
|
|
31
|
+
RecordState.pause: "paused",
|
|
32
|
+
RecordState.stop: "stopped",
|
|
33
|
+
};
|
|
34
|
+
control.triggerEvent("state_change", stateMap[state]);
|
|
60
35
|
}
|
|
61
36
|
|
|
62
|
-
Future<String
|
|
63
|
-
debugPrint("AudioRecorder
|
|
64
|
-
|
|
65
|
-
|
|
37
|
+
Future<dynamic> _invokeMethod(String name, dynamic args) async {
|
|
38
|
+
debugPrint("AudioRecorder.$name($args)");
|
|
39
|
+
switch (name) {
|
|
40
|
+
case "start_recording":
|
|
41
|
+
final config = parseRecordConfig(args["configuration"]);
|
|
42
|
+
if (config != null && await recorder!.hasPermission()) {
|
|
43
|
+
final out = control.backend.getAssetSource(args["output_path"] ?? "");
|
|
44
|
+
if (!isWebPlatform() && !out.isFile) {
|
|
45
|
+
// on non-web/IO platforms, the output path must be a valid file path
|
|
46
|
+
return false;
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
await recorder!.start(config, path: out.path);
|
|
50
|
+
return true;
|
|
51
|
+
}
|
|
52
|
+
return false;
|
|
53
|
+
case "stop_recording":
|
|
54
|
+
return await recorder!.stop();
|
|
55
|
+
case "cancel_recording":
|
|
56
|
+
await recorder!.cancel();
|
|
57
|
+
case "resume_recording":
|
|
58
|
+
await recorder!.resume();
|
|
59
|
+
case "pause_recording":
|
|
60
|
+
await recorder!.pause();
|
|
61
|
+
case "is_supported_encoder":
|
|
62
|
+
var encoder = parseAudioEncoder(args["encoder"]);
|
|
63
|
+
if (encoder != null) {
|
|
64
|
+
return await recorder!.isEncoderSupported(encoder);
|
|
65
|
+
}
|
|
66
|
+
break;
|
|
67
|
+
case "is_paused":
|
|
68
|
+
return await recorder!.isPaused();
|
|
69
|
+
case "is_recording":
|
|
70
|
+
return await recorder!.isRecording();
|
|
71
|
+
case "has_permission":
|
|
72
|
+
return await recorder!.hasPermission();
|
|
73
|
+
case "get_input_devices":
|
|
74
|
+
List<InputDevice> devices = await recorder!.listInputDevices();
|
|
75
|
+
return devices.asMap().map((k, v) {
|
|
76
|
+
return MapEntry(v.id, v.label);
|
|
77
|
+
});
|
|
78
|
+
default:
|
|
79
|
+
throw Exception("Unknown AudioRecorder method: $name");
|
|
80
|
+
}
|
|
66
81
|
}
|
|
67
82
|
|
|
68
83
|
@override
|
|
69
|
-
|
|
70
|
-
debugPrint(
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
int numChannels = widget.control.attrInt("channels", 2)!;
|
|
76
|
-
bool autoGain = widget.control.attrBool("autoGain", false)!;
|
|
77
|
-
bool cancelEcho = widget.control.attrBool("cancelEcho", false)!;
|
|
78
|
-
bool suppressNoise = widget.control.attrBool("suppressNoise", false)!;
|
|
79
|
-
AudioEncoder audioEncoder =
|
|
80
|
-
parseAudioEncoder(widget.control.attrString("audioEncoder", "wav"))!;
|
|
81
|
-
|
|
82
|
-
_onStateChanged = (state) {
|
|
83
|
-
debugPrint("AudioRecorder($hashCode) - state_changed: ${state.name}");
|
|
84
|
-
var s = state.name.toString();
|
|
85
|
-
if (s == "record") {
|
|
86
|
-
s = "recording";
|
|
87
|
-
} else if (s == "pause") {
|
|
88
|
-
s = "paused";
|
|
89
|
-
} else if (s == "stop") {
|
|
90
|
-
s = "stopped";
|
|
91
|
-
}
|
|
92
|
-
widget.backend.triggerControlEvent(widget.control.id, "state_changed", s);
|
|
93
|
-
};
|
|
94
|
-
|
|
95
|
-
() async {
|
|
96
|
-
widget.backend.subscribeMethods(widget.control.id,
|
|
97
|
-
(methodName, args) async {
|
|
98
|
-
switch (methodName) {
|
|
99
|
-
case "start_recording":
|
|
100
|
-
if (await recorder!.hasPermission()) {
|
|
101
|
-
if (args["outputPath"] != null) {
|
|
102
|
-
await recorder!.start(
|
|
103
|
-
RecordConfig(
|
|
104
|
-
encoder: audioEncoder,
|
|
105
|
-
bitRate: bitRate,
|
|
106
|
-
sampleRate: sampleRate,
|
|
107
|
-
numChannels: numChannels,
|
|
108
|
-
autoGain: autoGain,
|
|
109
|
-
echoCancel: cancelEcho,
|
|
110
|
-
noiseSuppress: suppressNoise,
|
|
111
|
-
),
|
|
112
|
-
path: args["outputPath"]!);
|
|
113
|
-
return "true";
|
|
114
|
-
}
|
|
115
|
-
break;
|
|
116
|
-
}
|
|
117
|
-
return null;
|
|
118
|
-
case "stop_recording":
|
|
119
|
-
debugPrint("AudioRecorder.stopRecording($hashCode)");
|
|
120
|
-
String? out = await recorder!.stop();
|
|
121
|
-
return out;
|
|
122
|
-
case "cancel_recording":
|
|
123
|
-
debugPrint("AudioRecorder.cancelRecording($hashCode)");
|
|
124
|
-
await recorder!.cancel();
|
|
125
|
-
case "resume_recording":
|
|
126
|
-
debugPrint("AudioRecorder.resumeRecording($hashCode)");
|
|
127
|
-
await recorder!.resume();
|
|
128
|
-
case "pause_recording":
|
|
129
|
-
debugPrint("AudioRecorder.pauseRecording($hashCode)");
|
|
130
|
-
await recorder!.pause();
|
|
131
|
-
case "is_supported_encoder":
|
|
132
|
-
debugPrint("AudioRecorder.isEncoderSupported($hashCode)");
|
|
133
|
-
if (parseAudioEncoder(args["encoder"]) != null) {
|
|
134
|
-
bool isSupported = await recorder!.isEncoderSupported(
|
|
135
|
-
parseAudioEncoder(args["encoder"]) ?? AudioEncoder.wav);
|
|
136
|
-
return isSupported.toString();
|
|
137
|
-
}
|
|
138
|
-
break;
|
|
139
|
-
case "is_paused":
|
|
140
|
-
debugPrint("AudioRecorder.isPaused($hashCode)");
|
|
141
|
-
bool isPaused = await recorder!.isPaused();
|
|
142
|
-
return isPaused.toString();
|
|
143
|
-
case "is_recording":
|
|
144
|
-
debugPrint("AudioRecorder.isRecording($hashCode)");
|
|
145
|
-
bool isRecording = await recorder!.isRecording();
|
|
146
|
-
return isRecording.toString();
|
|
147
|
-
case "has_permission":
|
|
148
|
-
debugPrint("AudioRecorder.hasPermission($hashCode)");
|
|
149
|
-
bool hasPermission = await recorder!.hasPermission();
|
|
150
|
-
return hasPermission.toString();
|
|
151
|
-
case "get_input_devices":
|
|
152
|
-
debugPrint("AudioRecorder.getInputDevices($hashCode)");
|
|
153
|
-
List<InputDevice> devices = await recorder!.listInputDevices();
|
|
154
|
-
String devicesJson = json.encode(devices.asMap().map((key, value) {
|
|
155
|
-
return MapEntry(key, (value.id, value.label));
|
|
156
|
-
}).toString());
|
|
157
|
-
return devicesJson;
|
|
158
|
-
}
|
|
159
|
-
return null;
|
|
160
|
-
});
|
|
161
|
-
}();
|
|
162
|
-
|
|
163
|
-
return const SizedBox.shrink();
|
|
84
|
+
void dispose() {
|
|
85
|
+
debugPrint("AudioRecorder(${control.id}).dispose()");
|
|
86
|
+
_onStateChangedSubscription?.cancel();
|
|
87
|
+
recorder?.dispose();
|
|
88
|
+
control.removeInvokeMethodListener(_invokeMethod);
|
|
89
|
+
super.dispose();
|
|
164
90
|
}
|
|
165
91
|
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import 'package:flet/flet.dart';
|
|
2
|
+
|
|
3
|
+
import 'audio_recorder.dart';
|
|
4
|
+
|
|
5
|
+
class Extension extends FletExtension {
|
|
6
|
+
@override
|
|
7
|
+
FletService? createService(Control control) {
|
|
8
|
+
switch (control.type) {
|
|
9
|
+
case "AudioRecorder":
|
|
10
|
+
return AudioRecorderService(control: control);
|
|
11
|
+
default:
|
|
12
|
+
return null;
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
}
|
|
@@ -1,26 +1,73 @@
|
|
|
1
|
+
import 'package:collection/collection.dart';
|
|
2
|
+
import 'package:flet/flet.dart';
|
|
1
3
|
import 'package:record/record.dart';
|
|
2
4
|
|
|
3
|
-
AudioEncoder? parseAudioEncoder(String?
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
5
|
+
AudioEncoder? parseAudioEncoder(String? value, [AudioEncoder? defaultValue]) {
|
|
6
|
+
if (value == null) return defaultValue;
|
|
7
|
+
return AudioEncoder.values.firstWhereOrNull(
|
|
8
|
+
(e) => e.name.toLowerCase() == value.toLowerCase()) ??
|
|
9
|
+
defaultValue;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
InputDevice? parseInputDevice(dynamic value, [InputDevice? defaultValue]) {
|
|
13
|
+
if (value == null) return defaultValue;
|
|
14
|
+
return InputDevice(id: value["id"], label: value["label"]);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
AndroidAudioSource? parseAndroidAudioSource(String? value,
|
|
18
|
+
[AndroidAudioSource? defaultValue]) {
|
|
19
|
+
if (value == null) return defaultValue;
|
|
20
|
+
return AndroidAudioSource.values.firstWhereOrNull(
|
|
21
|
+
(e) => e.name.toLowerCase() == value.toLowerCase()) ??
|
|
22
|
+
defaultValue;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
AndroidRecordConfig? parseAndroidRecordConfig(dynamic value,
|
|
26
|
+
[AndroidRecordConfig? defaultValue]) {
|
|
27
|
+
if (value == null) return defaultValue;
|
|
28
|
+
return AndroidRecordConfig(
|
|
29
|
+
audioSource: parseAndroidAudioSource(
|
|
30
|
+
value["audio_source"], AndroidAudioSource.defaultSource)!,
|
|
31
|
+
manageBluetooth: parseBool(value["manage_bluetooth"], true)!,
|
|
32
|
+
muteAudio: parseBool(value["mute_audio"], false)!,
|
|
33
|
+
useLegacy: parseBool(value["use_legacy"], false)!);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
IosAudioCategoryOption? parseIosAudioCategoryOption(String? value,
|
|
37
|
+
[IosAudioCategoryOption? defaultValue]) {
|
|
38
|
+
if (value == null) return defaultValue;
|
|
39
|
+
return IosAudioCategoryOption.values.firstWhereOrNull(
|
|
40
|
+
(e) => e.name.toLowerCase() == value.toLowerCase()) ??
|
|
41
|
+
defaultValue;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
IosRecordConfig? parseIosRecordConfig(dynamic value,
|
|
45
|
+
[IosRecordConfig? defaultValue]) {
|
|
46
|
+
if (value == null) return defaultValue;
|
|
47
|
+
var options = (value["options"] as List)
|
|
48
|
+
.map((o) => parseIosAudioCategoryOption(o))
|
|
49
|
+
.nonNulls
|
|
50
|
+
.toList();
|
|
51
|
+
return IosRecordConfig(
|
|
52
|
+
manageAudioSession: parseBool(value["manage_audio_session"], true)!,
|
|
53
|
+
categoryOptions: options,
|
|
54
|
+
);
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
RecordConfig? parseRecordConfig(dynamic value, [RecordConfig? defaultValue]) {
|
|
58
|
+
if (value == null) return defaultValue;
|
|
59
|
+
return RecordConfig(
|
|
60
|
+
autoGain: parseBool(value["auto_gain"], false)!,
|
|
61
|
+
bitRate: parseInt(value["bit_rate"], 128000)!,
|
|
62
|
+
encoder: parseAudioEncoder(value["encoder"], AudioEncoder.wav)!,
|
|
63
|
+
echoCancel: parseBool(value["cancel_echo"], false)!,
|
|
64
|
+
noiseSuppress: parseBool(value["suppress_noise"], false)!,
|
|
65
|
+
numChannels: parseInt(value["channels"], 2)!,
|
|
66
|
+
device: parseInputDevice(value["device"]),
|
|
67
|
+
sampleRate: parseInt(value["sample_rate"], 44100)!,
|
|
68
|
+
androidConfig: parseAndroidRecordConfig(
|
|
69
|
+
value["android_configuration"], const AndroidRecordConfig())!,
|
|
70
|
+
iosConfig: parseIosRecordConfig(
|
|
71
|
+
value["ios_configuration"], const IosRecordConfig())!,
|
|
72
|
+
);
|
|
26
73
|
}
|