commit af7cb4f44700ae9862bc1b55a8ec475a627f6aea Author: Michael Schimmel Date: Fri Jan 30 10:11:05 2026 +0100 Init diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..5da0af1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,7 @@ +Win64 +__recovery +__history +/WhisperTest.delphilsp.json +/WhisperTest.dproj.local +/WhisperTest.dsk +/WhisperTest.~dsk diff --git a/WhisperMain.fmx b/WhisperMain.fmx new file mode 100644 index 0000000..27ceccd --- /dev/null +++ b/WhisperMain.fmx @@ -0,0 +1,59 @@ +object Form1: TForm1 + Left = 0 + Top = 0 + Caption = 'Form1' + ClientHeight = 480 + ClientWidth = 640 + FormFactor.Width = 320 + FormFactor.Height = 480 + FormFactor.Devices = [Desktop] + DesignerMasterStyle = 0 + object RecordButton: TButton + Position.X = 56.000000000000000000 + Position.Y = 24.000000000000000000 + TabOrder = 0 + Text = 'Record' + OnClick = RecordButtonClick + end + object StopButton: TButton + Position.X = 56.000000000000000000 + Position.Y = 54.000000000000000000 + TabOrder = 1 + Text = 'Stop' + OnClick = StopButtonClick + end + object LogMemo: TMemo + Touch.InteractiveGestures = [Pan, LongTap, DoubleTap] + DataDetectorTypes = [] + Anchors = [akLeft, akTop, akRight] + Position.X = 8.000000000000000000 + Position.Y = 84.000000000000000000 + Size.Width = 624.000000000000000000 + Size.Height = 77.000000000000000000 + Size.PlatformDefault = False + TabOrder = 2 + Viewport.Width = 620.000000000000000000 + Viewport.Height = 73.000000000000000000 + end + object OutputMemo: TMemo + Touch.InteractiveGestures = [Pan, LongTap, DoubleTap] + DataDetectorTypes = [] + TextSettings.WordWrap = True + Anchors = [akLeft, akTop, akRight, akBottom] + Position.X = 8.000000000000000000 + Position.Y = 170.000000000000000000 + Size.Width = 624.000000000000000000 + Size.Height = 302.000000000000000000 + Size.PlatformDefault = False + TabOrder = 3 + Viewport.Width = 620.000000000000000000 + Viewport.Height = 298.000000000000000000 + end + object DebugCheckBox: TCheckBox + IsChecked = True + Position.X = 168.000000000000000000 + Position.Y = 57.000000000000000000 + TabOrder = 4 + Text = 'Debug-Ausgaben' + end +end diff --git a/WhisperMain.pas b/WhisperMain.pas new file mode 100644 index 0000000..42b034e --- /dev/null +++ b/WhisperMain.pas @@ -0,0 +1,506 @@ +unit WhisperMain; + +interface + +uses + Winapi.Windows, Winapi.MMSystem, System.SysUtils, System.Types, System.UITypes, + System.Classes, System.Variants, System.IOUtils, FMX.Types, FMX.Controls, + FMX.Forms, FMX.Graphics, FMX.Dialogs, FMX.Memo.Types, FMX.ScrollBox, + FMX.Memo, FMX.Controls.Presentation, FMX.StdCtrls, System.JSON, + System.Net.HttpClient, System.Net.URLClient, System.Net.HttpClientComponent, + IdTCPClient, IdException, IdGlobal, IdComponent; + +type + TWaveHeader = packed record + RIFF: array[0..3] of AnsiChar; + FileSize: Cardinal; + WAVE: array[0..3] of AnsiChar; + fmt: array[0..3] of AnsiChar; + FormatLength: Cardinal; + FormatTag: Word; + Channels: Word; + SampleRate: Cardinal; + BytesPerSecond: Cardinal; + BlockAlign: Word; + BitsPerSample: Word; + Data: array[0..3] of AnsiChar; + DataLength: Cardinal; + end; + + TForm1 = class(TForm) + RecordButton: TButton; + StopButton: TButton; + LogMemo: TMemo; + OutputMemo: TMemo; + DebugCheckBox: TCheckBox; + procedure RecordButtonClick(Sender: TObject); + procedure StopButtonClick(Sender: TObject); + procedure FormDestroy(Sender: TObject); + private + waveHandle: HWAVEIN; + fileStream: TFileStream; + waveHeader: TWaveHeader; + isRecording: Boolean; + tempAudioPath: string; + + waveCaps: array[0..1] of TWaveHdr; + waveBuffers: array[0..1] of array[0..4095] of Byte; + + procedure Log(const Msg: string); + procedure LogDebug(const Msg: string); + + // Wyoming / Whisper + procedure SendToWhisperAsync(const FilePath: string); + function CreateEvent(const EventType: string; Data: TJSONObject = nil; PayloadLen: Int64 = -1): string; + function GetAudioMetadata: TJSONObject; + + // LLM / OpenAI API + procedure QueryLLM(const UserInput: string); + + // Audio Capture + procedure StartCapture(const FileName: string); + procedure StopCapture; + procedure WriteWavHeader; + procedure UpdateWavHeader; + public + end; + +var + Form1: TForm1; + +implementation + +{$R *.fmx} + +procedure waveInProc(hwi: HWAVEIN; uMsg: UINT; dwInstance, dwParam1, dwParam2: DWORD_PTR); stdcall; +var + header: PWaveHdr; + form: TForm1; +begin + if (uMsg = WIM_DATA) then + begin + header := PWaveHdr(dwParam1); + form := TForm1(dwInstance); + + if (form.isRecording) and (header.dwBytesRecorded > 0) then + begin + form.fileStream.WriteBuffer(header.lpData^, header.dwBytesRecorded); + waveInPrepareHeader(hwi, header, sizeof(TWaveHdr)); + waveInAddBuffer(hwi, header, sizeof(TWaveHdr)); + end; + end; +end; + +procedure TForm1.Log(const Msg: string); +begin + TThread.Queue(nil, + procedure + begin + LogMemo.Lines.Add(FormatDateTime('hh:nn:ss.zzz', Now) + ': ' + Msg); + LogMemo.GoToTextEnd; + end); +end; + +procedure TForm1.LogDebug(const Msg: string); +begin + if not DebugCheckBox.IsChecked then exit; + + TThread.Queue(nil, + procedure + begin + LogMemo.Lines.Add(FormatDateTime('hh:nn:ss.zzz', Now) + ' [DEBUG]: ' + Msg); + LogMemo.GoToTextEnd; + end); +end; + +function TForm1.GetAudioMetadata: TJSONObject; +begin + Result := TJSONObject.Create; + Result.AddPair('rate', TJSONNumber.Create(16000)); + Result.AddPair('width', TJSONNumber.Create(2)); + Result.AddPair('channels', TJSONNumber.Create(1)); +end; + +procedure TForm1.WriteWavHeader; +begin + FillChar(waveHeader, sizeof(TWaveHeader), 0); + waveHeader.RIFF := 'RIFF'; + waveHeader.WAVE := 'WAVE'; + waveHeader.fmt := 'fmt '; + waveHeader.FormatLength := 16; + waveHeader.FormatTag := WAVE_FORMAT_PCM; + waveHeader.Channels := 1; + waveHeader.SampleRate := 16000; + waveHeader.BitsPerSample := 16; + waveHeader.BlockAlign := (waveHeader.Channels * waveHeader.BitsPerSample) div 8; + waveHeader.BytesPerSecond := waveHeader.SampleRate * waveHeader.BlockAlign; + waveHeader.Data := 'data'; + fileStream.WriteBuffer(waveHeader, sizeof(TWaveHeader)); +end; + +procedure TForm1.UpdateWavHeader; +var + pos: Int64; +begin + pos := fileStream.Position; + LogDebug(Format('Updating WAV Header. Total File Size: %d', [pos])); + waveHeader.FileSize := pos - 8; + waveHeader.DataLength := pos - sizeof(TWaveHeader); + fileStream.Position := 0; + fileStream.WriteBuffer(waveHeader, sizeof(TWaveHeader)); +end; + +procedure TForm1.StartCapture(const FileName: string); +var + wfx: TWaveFormatEx; + i: Integer; + mmRes: MMRESULT; +begin + if (isRecording) then exit; + + LogDebug('Starting capture to: ' + FileName); + fileStream := TFileStream.Create(FileName, fmCreate); + WriteWavHeader; + + wfx.wFormatTag := WAVE_FORMAT_PCM; + wfx.nChannels := waveHeader.Channels; + wfx.nSamplesPerSec := waveHeader.SampleRate; + wfx.nAvgBytesPerSec := waveHeader.BytesPerSecond; + wfx.nBlockAlign := waveHeader.BlockAlign; + wfx.wBitsPerSample := waveHeader.BitsPerSample; + wfx.cbSize := 0; + + LogDebug('Opening WaveIn device...'); + mmRes := waveInOpen(@waveHandle, WAVE_MAPPER, @wfx, DWORD_PTR(@waveInProc), DWORD_PTR(self), CALLBACK_FUNCTION); + + if (mmRes = MMSYSERR_NOERROR) then + begin + isRecording := true; + LogDebug('WaveIn device opened successfully. Preparing buffers...'); + for i := 0 to 1 do + begin + FillChar(waveCaps[i], sizeof(TWaveHdr), 0); + waveCaps[i].lpData := @waveBuffers[i]; + waveCaps[i].dwBufferLength := sizeof(waveBuffers[i]); + waveInPrepareHeader(waveHandle, @waveCaps[i], sizeof(TWaveHdr)); + waveInAddBuffer(waveHandle, @waveCaps[i], sizeof(TWaveHdr)); + end; + waveInStart(waveHandle); + LogDebug('WaveIn started.'); + end + else + begin + Log('Error opening WaveIn device. Error code: ' + IntToStr(mmRes)); + end; +end; + +procedure TForm1.StopCapture; +var + i: Integer; +begin + if (not isRecording) then exit; + + LogDebug('Stopping capture...'); + isRecording := false; + waveInStop(waveHandle); + waveInReset(waveHandle); + + for i := 0 to 1 do + waveInUnprepareHeader(waveHandle, @waveCaps[i], sizeof(TWaveHdr)); + + waveInClose(waveHandle); + LogDebug('WaveIn closed.'); + + UpdateWavHeader; + if (Assigned(fileStream)) then + FreeAndNil(fileStream); +end; + +procedure TForm1.RecordButtonClick(Sender: TObject); +begin + tempAudioPath := TPath.Combine(TPath.GetTempPath, 'whisper_capture.wav'); + StartCapture(tempAudioPath); + RecordButton.Enabled := false; + StopButton.Enabled := true; + Log('Recording started...'); +end; + +procedure TForm1.StopButtonClick(Sender: TObject); +begin + StopCapture; + RecordButton.Enabled := true; + StopButton.Enabled := false; + if (TFile.Exists(tempAudioPath)) then + SendToWhisperAsync(tempAudioPath); +end; + +procedure TForm1.FormDestroy(Sender: TObject); +begin + if (isRecording) then + StopCapture; +end; + +function TForm1.CreateEvent(const EventType: string; Data: TJSONObject; PayloadLen: Int64): string; +var + evt: TJSONObject; +begin + evt := TJSONObject.Create; + try + evt.AddPair('type', EventType); + if (Assigned(Data)) then + evt.AddPair('data', Data) + else + evt.AddPair('data', TJSONObject.Create); + + if (PayloadLen >= 0) then + evt.AddPair('payload_length', TJSONNumber.Create(PayloadLen)); + + Result := evt.ToJSON + #10; + finally + evt.Free; + end; +end; + +procedure TForm1.QueryLLM(const UserInput: string); +var + client: TNetHTTPClient; + reqJson, msgSystem, msgUser: TJSONObject; + msgs: TJSONArray; + resp: IHTTPResponse; + respJson: TJSONObject; + choices: TJSONArray; + firstChoice, messageObj: TJSONObject; + content: string; + + exePath, promptPath, systemPromptText: string; +begin + LogDebug('Querying LLM (RAKI)...'); + + // Resolve system prompt path + exePath := ExtractFilePath(ParamStr(0)); + promptPath := TPath.GetFullPath(TPath.Combine(exePath, '..\..\Prompt.md')); + + if TFile.Exists(promptPath) then + begin + LogDebug('Loading system prompt from: ' + promptPath); + systemPromptText := TFile.ReadAllText(promptPath, TEncoding.UTF8); + end + else + begin + LogDebug('Prompt file not found at: ' + promptPath + '. Using default.'); + systemPromptText := 'You are a helpful assistant.'; + end; + + client := TNetHTTPClient.Create(nil); + reqJson := TJSONObject.Create; + try + try + // Prepare Request JSON + reqJson.AddPair('model', 'openai/gpt-oss-20b'); + reqJson.AddPair('temperature', TJSONNumber.Create(0.7)); + reqJson.AddPair('stream', TJSONBool.Create(False)); + + msgs := TJSONArray.Create; + + msgSystem := TJSONObject.Create; + msgSystem.AddPair('role', 'system'); + msgSystem.AddPair('content', systemPromptText); + msgs.Add(msgSystem); + + msgUser := TJSONObject.Create; + msgUser.AddPair('role', 'user'); + msgUser.AddPair('content', UserInput); + msgs.Add(msgUser); + + reqJson.AddPair('messages', msgs); + + client.ContentType := 'application/json'; + client.Accept := 'application/json'; + + // Execute Request + resp := client.Post('http://raki:1234/v1/chat/completions', TStringStream.Create(reqJson.ToJSON, TEncoding.UTF8)); + + LogDebug('LLM Status: ' + IntToStr(resp.StatusCode)); + + if resp.StatusCode = 200 then + begin + respJson := TJSONObject.ParseJSONValue(resp.ContentAsString(TEncoding.UTF8)) as TJSONObject; + if Assigned(respJson) then + try + // Parse: choices[0].message.content + choices := respJson.GetValue('choices') as TJSONArray; + if (Assigned(choices)) and (choices.Count > 0) then + begin + firstChoice := choices.Items[0] as TJSONObject; + messageObj := firstChoice.GetValue('message') as TJSONObject; + if Assigned(messageObj) then + begin + content := messageObj.GetValue('content').Value; + + TThread.Queue(nil, + procedure + begin + OutputMemo.Lines.Add(''); + OutputMemo.Lines.Add('RAKI: ' + content); + end); + end; + end; + finally + respJson.Free; + end; + end + else + begin + Log('LLM Error (' + IntToStr(resp.StatusCode) + '): ' + resp.StatusText); + end; + + except + on E: Exception do + Log('LLM Exception: ' + E.Message); + end; + finally + reqJson.Free; + client.Free; + end; +end; + +procedure TForm1.SendToWhisperAsync(const FilePath: string); +begin + TThread.CreateAnonymousThread( + procedure + var + client: TIdTCPClient; + stream: TFileStream; + jsonResp, jsonData: TJSONObject; + response, msgType, textValue: string; + dataSize: Int64; + isFinished: Boolean; + payloadLen: Integer; + debugMsg: string; + begin + client := TIdTCPClient.Create(nil); + isFinished := false; + try + try + client.Host := '192.168.178.10'; + client.Port := 10300; + client.ReadTimeout := 30000; + + LogDebug('Connecting to ' + client.Host + ':' + IntToStr(client.Port)); + + if not TFile.Exists(FilePath) then + begin + LogDebug('File not found: ' + FilePath); + exit; + end; + + client.Connect; + client.IOHandler.DefStringEncoding := IndyTextEncoding_UTF8; + + stream := TFileStream.Create(FilePath, fmOpenRead or fmShareDenyNone); + try + dataSize := stream.Size - sizeof(TWaveHeader); + LogDebug(Format('Sending audio. PCM Data Size: %d', [dataSize])); + + // 1. Wyoming protocol sequence + debugMsg := CreateEvent('audio-start', self.GetAudioMetadata); + LogDebug('Sending: ' + debugMsg.Trim); + client.IOHandler.Write(debugMsg); + + debugMsg := CreateEvent('audio-chunk', self.GetAudioMetadata, dataSize); + LogDebug('Sending: ' + debugMsg.Trim); + client.IOHandler.Write(debugMsg); + + stream.Position := sizeof(TWaveHeader); + client.IOHandler.Write(stream, dataSize); + + LogDebug('Audio data sent.'); + + client.IOHandler.Write(CreateEvent('audio-stop')); + + // 2. Response event loop + while (client.Connected) and (not isFinished) do + begin + response := client.IOHandler.ReadLn(); + if (response = '') then + begin + client.IOHandler.CheckForDisconnect(true, true); + if not client.Connected then break; + TThread.Sleep(10); + continue; + end; + + LogDebug('Received: ' + response); + + jsonResp := TJSONObject.ParseJSONValue(response) as TJSONObject; + if Assigned(jsonResp) then + try + if jsonResp.TryGetValue('type', msgType) then + begin + if (msgType = 'transcript') then + begin + textValue := ''; + if (jsonResp.TryGetValue('data_length', payloadLen)) and (payloadLen > 0) then + begin + textValue := client.IOHandler.ReadString(payloadLen); + // Handle case where payload is a JSON object itself (e.g. {"text": "..."}) + if textValue.Trim.StartsWith('{') then + begin + var nestedJson := TJSONObject.ParseJSONValue(textValue) as TJSONObject; + if Assigned(nestedJson) then + try + nestedJson.TryGetValue('text', textValue); + finally + nestedJson.Free; + end; + end; + end + else if jsonResp.TryGetValue('data', jsonData) then + jsonData.TryGetValue('text', textValue); + + if (textValue <> '') then + begin + var finalTxt := textValue.Trim; + + LogDebug('Transcript payload: ' + finalTxt); + + TThread.Queue(nil, + procedure + begin + OutputMemo.Lines.Add('Me: ' + finalTxt); + end); + + // Trigger LLM request (still in background thread context) + QueryLLM(finalTxt); + end; + isFinished := true; + end + else if (msgType = 'error') then + begin + Log('[Thread] Server Error: ' + response); + isFinished := true; + end; + end; + finally + jsonResp.Free; + end; + end; + finally + stream.Free; + end; + except + on E: Exception do + begin + var errMsg := E.Message; + Log('[Thread] Exception: ' + errMsg); + end; + end; + finally + if client.Connected then + client.Disconnect; + client.Free; + end; + end).Start; +end; + +end. diff --git a/WhisperTest.dpr b/WhisperTest.dpr new file mode 100644 index 0000000..0207de5 --- /dev/null +++ b/WhisperTest.dpr @@ -0,0 +1,14 @@ +program WhisperTest; + +uses + System.StartUpCopy, + FMX.Forms, + WhisperMain in 'WhisperMain.pas' {Form1}; + +{$R *.res} + +begin + Application.Initialize; + Application.CreateForm(TForm1, Form1); + Application.Run; +end. diff --git a/WhisperTest.dproj b/WhisperTest.dproj new file mode 100644 index 0000000..e65d700 --- /dev/null +++ b/WhisperTest.dproj @@ -0,0 +1,1145 @@ + + + {F0C6BFED-F793-419D-866E-33AE8E3ECE4E} + 20.3 + FMX + True + Debug + Win64 + WhisperTest + 3 + Application + WhisperTest.dpr + + + true + + + true + Base + true + + + true + Base + true + + + true + Base + true + + + true + Cfg_1 + true + true + + + true + Cfg_1 + true + true + + + true + Base + true + + + true + Cfg_2 + true + true + + + true + Cfg_2 + true + true + + + .\$(Platform)\$(Config) + .\$(Platform)\$(Config) + false + false + false + false + false + System;Xml;Data;Datasnap;Web;Soap;$(DCC_Namespace) + $(BDS)\bin\delphi_PROJECTICON.ico + $(BDS)\bin\delphi_PROJECTICNS.icns + WhisperTest + + + vclwinx;fmx;vclie;DbxCommonDriver;bindengine;IndyIPCommon;VCLRESTComponents;FireDACCommonODBC;FireDACCommonDriver;IndyProtocols;vclx;Skia.Package.RTL;IndyIPClient;dbxcds;vcledge;bindcompvclwinx;FmxTeeUI;bindcompfmx;inetdb;FireDACSqliteDriver;DbxClientDriver;Tee;soapmidas;vclactnband;TeeUI;fmxFireDAC;dbexpress;DBXMySQLDriver;VclSmp;inet;fmxase;vcltouch;dbrtl;Skia.Package.FMX;fmxdae;TeeDB;FireDACMSAccDriver;CustomIPTransport;vcldsnap;DBXInterBaseDriver;IndySystem;Skia.Package.VCL;vcldb;vclFireDAC;bindcomp;FireDACCommon;inetstn;IndyCore;RESTBackendComponents;bindcompdbx;rtl;FireDACMySQLDriver;FireDACADSDriver;RESTComponents;DBXSqliteDriver;vcl;IndyIPServer;dsnapxml;dsnapcon;adortl;vclimg;FireDACPgDriver;FireDAC;inetdbxpress;xmlrtl;tethering;bindcompvcl;dsnap;CloudService;fmxobj;bindcompvclsmp;FMXTee;soaprtl;soapserver;FireDACIBDriver;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;Bde;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + $(BDS)\bin\default_app.manifest + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + vclwinx;fmx;vclie;DbxCommonDriver;bindengine;IndyIPCommon;VCLRESTComponents;FireDACCommonODBC;FireDACCommonDriver;IndyProtocols;vclx;IndyIPClient;dbxcds;vcledge;bindcompvclwinx;FmxTeeUI;bindcompfmx;inetdb;FireDACSqliteDriver;DbxClientDriver;Tee;soapmidas;vclactnband;TeeUI;fmxFireDAC;dbexpress;DBXMySQLDriver;VclSmp;inet;fmxase;vcltouch;dbrtl;fmxdae;TeeDB;FireDACMSAccDriver;CustomIPTransport;vcldsnap;DBXInterBaseDriver;IndySystem;Skia.Package.VCL;vcldb;vclFireDAC;bindcomp;FireDACCommon;inetstn;IndyCore;RESTBackendComponents;bindcompdbx;rtl;FireDACMySQLDriver;FireDACADSDriver;RESTComponents;DBXSqliteDriver;vcl;IndyIPServer;dsnapxml;dsnapcon;adortl;vclimg;FireDACPgDriver;FireDAC;inetdbxpress;xmlrtl;tethering;bindcompvcl;dsnap;CloudService;fmxobj;bindcompvclsmp;FMXTee;soaprtl;soapserver;FireDACIBDriver;$(DCC_UsePackage) + Winapi;System.Win;Data.Win;Datasnap.Win;Web.Win;Soap.Win;Xml.Win;$(DCC_Namespace) + Debug + true + CompanyName=;FileDescription=$(MSBuildProjectName);FileVersion=1.0.0.0;InternalName=;LegalCopyright=;LegalTrademarks=;OriginalFilename=;ProgramID=com.embarcadero.$(MSBuildProjectName);ProductName=$(MSBuildProjectName);ProductVersion=1.0.0.0;Comments= + 1033 + $(BDS)\bin\default_app.manifest + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_44.png + $(BDS)\bin\Artwork\Windows\UWP\delphi_UwpDefault_150.png + + + DEBUG;$(DCC_Define) + true + false + true + true + true + true + true + + + false + PerMonitorV2 + + + PerMonitorV2 + + + false + RELEASE;$(DCC_Define) + 0 + 0 + + + PerMonitorV2 + + + PerMonitorV2 + + + + MainSource + + +
Form1
+ fmx +
+ + Base + + + Cfg_1 + Base + + + Cfg_2 + Base + +
+ + Delphi.Personality.12 + Application + + + + WhisperTest.dpr + + + + + + true + + + + + true + + + + + true + + + + + WhisperTest.exe + true + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + res\xml + 1 + + + res\xml + 1 + + + + + library\lib\armeabi + 1 + + + library\lib\armeabi + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + library\lib\mips + 1 + + + library\lib\mips + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + + + library\lib\armeabi-v7a + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-anydpi-v21 + 1 + + + res\drawable-anydpi-v21 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-v21 + 1 + + + res\values-v21 + 1 + + + + + res\values-v31 + 1 + + + res\values-v31 + 1 + + + + + res\values-v35 + 1 + + + res\values-v35 + 1 + + + + + res\drawable-anydpi-v26 + 1 + + + res\drawable-anydpi-v26 + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-anydpi-v33 + 1 + + + res\drawable-anydpi-v33 + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\values-night-v21 + 1 + + + res\values-night-v21 + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-ldpi + 1 + + + res\drawable-ldpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-mdpi + 1 + + + res\drawable-mdpi + 1 + + + + + res\drawable-hdpi + 1 + + + res\drawable-hdpi + 1 + + + + + res\drawable-xhdpi + 1 + + + res\drawable-xhdpi + 1 + + + + + res\drawable-xxhdpi + 1 + + + res\drawable-xxhdpi + 1 + + + + + res\drawable-xxxhdpi + 1 + + + res\drawable-xxxhdpi + 1 + + + + + res\drawable-small + 1 + + + res\drawable-small + 1 + + + + + res\drawable-normal + 1 + + + res\drawable-normal + 1 + + + + + res\drawable-large + 1 + + + res\drawable-large + 1 + + + + + res\drawable-xlarge + 1 + + + res\drawable-xlarge + 1 + + + + + res\values + 1 + + + res\values + 1 + + + + + res\drawable-anydpi-v24 + 1 + + + res\drawable-anydpi-v24 + 1 + + + + + res\drawable + 1 + + + res\drawable + 1 + + + + + res\drawable-night-anydpi-v21 + 1 + + + res\drawable-night-anydpi-v21 + 1 + + + + + res\drawable-anydpi-v31 + 1 + + + res\drawable-anydpi-v31 + 1 + + + + + res\drawable-night-anydpi-v31 + 1 + + + res\drawable-night-anydpi-v31 + 1 + + + + + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + Contents\MacOS + 1 + .framework + + + 0 + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .dll;.bpl + + + + + 1 + .dylib + + + 1 + .dylib + + + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + Contents\MacOS + 1 + .dylib + + + 0 + .bpl + + + + + 0 + + + 0 + + + 0 + + + 0 + + + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + Contents\Resources\StartUp\ + 0 + + + 0 + + + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + ..\ + 1 + + + + + Contents + 1 + + + Contents + 1 + + + Contents + 1 + + + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + Contents\Resources + 1 + + + + + library\lib\armeabi-v7a + 1 + + + library\lib\arm64-v8a + 1 + + + 1 + + + 1 + + + 1 + + + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + Contents\MacOS + 1 + + + 0 + + + + + library\lib\armeabi-v7a + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + ..\$(PROJECTNAME).app.dSYM\Contents\Resources\DWARF + 1 + + + + + ..\ + 1 + + + ..\ + 1 + + + ..\ + 1 + + + + + 1 + + + 1 + + + 1 + + + + + ..\$(PROJECTNAME).launchscreen + 64 + + + ..\$(PROJECTNAME).launchscreen + 64 + + + + + 1 + + + 1 + + + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + Assets + 1 + + + Assets + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\LaunchScreenImage.imageset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + ..\$(PROJECTNAME).launchscreen\Assets\AppIcon.appiconset + 1 + + + + + + + + + + + + + + + + + False + False + False + False + False + False + False + False + True + True + + + 12 + + + + +
diff --git a/WhisperTest.res b/WhisperTest.res new file mode 100644 index 0000000..2eea91d Binary files /dev/null and b/WhisperTest.res differ diff --git a/prompt.md b/prompt.md new file mode 100644 index 0000000..5d59c08 --- /dev/null +++ b/prompt.md @@ -0,0 +1,21 @@ +### ROLE +You are an expert Medical Documentation Specialist. Your task is to transform fragmented Speech-to-Text (STT) input into a professional, structured German medical report (Patientenakte). + +### CORE DIRECTIVES +1. MEDICAL ORTHOGRAPHY: Automatically correct all phonetic and orthographic errors common in STT (e.g., d/t, k/c, or missing letters). Use your internal medical knowledge base to ensure every term is written correctly in German medical nomenclature. +2. PHARMACOLOGICAL LOGIC: Identify and consolidate medications. If active ingredients and brand names are mentioned together (e.g., Sacubitril, Valsartan, Entresto), merge them into a single, professional entry: "Sacubitril/Valsartan (Entresto)". +3. CLINICAL REASONING: Do not just copy values. Analyze the relationship between diagnosis, lab results, and therapy. Highlight critical constellations (e.g., renal failure combined with diuretics) under a specific "Klinische Analyse" section. +4. NOISE FILTERING: Completely ignore speaker-related metadata, greetings, and legal disclaimers ("Keine medizinische Beratung"). + +### OUTPUT STRUCTURE (IN GERMAN) +- Diagnose & ICD-10 Codes +- Pathophysiologie & Klinisches Bild +- Laborstatus (Structured list or table) +- Therapieplan (Consolidated medications with dosages) +- Klinische Analyse (Risks, interactions, and critical findings) + +### OUTPUT LANGUAGE +German + +### INPUT DATA +