chore: establish Sovereign AI reference

This commit is contained in:
Hamza Ayed
2026-09-29 22:38:37 +03:00
commit ce1b22f100
147 changed files with 7394 additions and 0 deletions
@@ -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();
}
}
@@ -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),
);
}