chore: establish Sovereign AI reference
This commit is contained in:
+103
@@ -0,0 +1,103 @@
|
||||
import 'dart:async';
|
||||
import 'dart:typed_data';
|
||||
|
||||
import 'package:flutter_bloc/flutter_bloc.dart';
|
||||
import 'package:record/record.dart';
|
||||
|
||||
import '../../../../core/network/api_repository.dart';
|
||||
import 'voice_state.dart';
|
||||
|
||||
class VoiceCubit extends Cubit<VoiceState> {
|
||||
VoiceCubit(this._api, this._recorder) : super(const VoiceState());
|
||||
|
||||
final ApiRepository _api;
|
||||
final AudioRecorder _recorder;
|
||||
StreamSubscription<Uint8List>? _recordingSubscription;
|
||||
final List<int> _pcm = [];
|
||||
|
||||
Future<void> startRecording() async {
|
||||
emit(state.copyWith(clearError: true, clearTranscript: true));
|
||||
try {
|
||||
if (!await _recorder.hasPermission()) {
|
||||
throw Exception('لم يتم السماح باستخدام الميكروفون.');
|
||||
}
|
||||
_pcm.clear();
|
||||
final stream = await _recorder.startStream(
|
||||
const RecordConfig(
|
||||
encoder: AudioEncoder.pcm16bits,
|
||||
sampleRate: 16000,
|
||||
numChannels: 1,
|
||||
),
|
||||
);
|
||||
_recordingSubscription = stream.listen(
|
||||
_pcm.addAll,
|
||||
onError: (Object error) {
|
||||
if (!isClosed) emit(state.copyWith(error: error.toString()));
|
||||
},
|
||||
);
|
||||
emit(state.copyWith(status: VoiceStatus.recording));
|
||||
} catch (error) {
|
||||
emit(state.copyWith(status: VoiceStatus.idle, error: error.toString()));
|
||||
}
|
||||
}
|
||||
|
||||
Future<void> stopAndTranscribe() async {
|
||||
try {
|
||||
await _recorder.stop();
|
||||
await _recordingSubscription?.cancel();
|
||||
_recordingSubscription = null;
|
||||
if (_pcm.isEmpty) throw Exception('لم يلتقط التسجيل أي صوت.');
|
||||
if (_pcm.length > 25 * 1024 * 1024 - 44) {
|
||||
throw Exception('التسجيل طويل جدًا؛ الحد الحالي 25 ميغابايت.');
|
||||
}
|
||||
emit(state.copyWith(status: VoiceStatus.transcribing, clearError: true));
|
||||
final transcript = await _api.transcribe(
|
||||
_toWav(Uint8List.fromList(_pcm)),
|
||||
);
|
||||
emit(
|
||||
state.copyWith(
|
||||
status: VoiceStatus.idle,
|
||||
transcript: transcript,
|
||||
clearError: true,
|
||||
),
|
||||
);
|
||||
} catch (error) {
|
||||
emit(state.copyWith(status: VoiceStatus.idle, error: error.toString()));
|
||||
} finally {
|
||||
_pcm.clear();
|
||||
}
|
||||
}
|
||||
|
||||
Uint8List _toWav(Uint8List pcm) {
|
||||
final wav = Uint8List(44 + pcm.length);
|
||||
final view = ByteData.sublistView(wav);
|
||||
void writeText(int offset, String value) {
|
||||
for (var i = 0; i < value.length; i++) {
|
||||
wav[offset + i] = value.codeUnitAt(i);
|
||||
}
|
||||
}
|
||||
|
||||
writeText(0, 'RIFF');
|
||||
view.setUint32(4, 36 + pcm.length, Endian.little);
|
||||
writeText(8, 'WAVE');
|
||||
writeText(12, 'fmt ');
|
||||
view.setUint32(16, 16, Endian.little);
|
||||
view.setUint16(20, 1, Endian.little);
|
||||
view.setUint16(22, 1, Endian.little);
|
||||
view.setUint32(24, 16000, Endian.little);
|
||||
view.setUint32(28, 32000, Endian.little);
|
||||
view.setUint16(32, 2, Endian.little);
|
||||
view.setUint16(34, 16, Endian.little);
|
||||
writeText(36, 'data');
|
||||
view.setUint32(40, pcm.length, Endian.little);
|
||||
wav.setRange(44, wav.length, pcm);
|
||||
return wav;
|
||||
}
|
||||
|
||||
@override
|
||||
Future<void> close() {
|
||||
_recordingSubscription?.cancel();
|
||||
_recorder.dispose();
|
||||
return super.close();
|
||||
}
|
||||
}
|
||||
+25
@@ -0,0 +1,25 @@
|
||||
enum VoiceStatus { idle, recording, transcribing }
|
||||
|
||||
class VoiceState {
|
||||
const VoiceState({
|
||||
this.status = VoiceStatus.idle,
|
||||
this.transcript,
|
||||
this.error,
|
||||
});
|
||||
|
||||
final VoiceStatus status;
|
||||
final String? transcript;
|
||||
final String? error;
|
||||
|
||||
VoiceState copyWith({
|
||||
VoiceStatus? status,
|
||||
String? transcript,
|
||||
String? error,
|
||||
bool clearTranscript = false,
|
||||
bool clearError = false,
|
||||
}) => VoiceState(
|
||||
status: status ?? this.status,
|
||||
transcript: clearTranscript ? null : (transcript ?? this.transcript),
|
||||
error: clearError ? null : (error ?? this.error),
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user