So, zunächst zu :
Zitat:
Wenn hingegen die Audiokomponente asynchron läuft, dann hast Du mit einem Thread ein Problem. Aber dann benötigst Du auch keinen.
Da bin ich anderer Meinung. Also die Audiokomponente läuft asynchron (soweit ich das beurteilen kann). Sie löst immer dann, wenn der Puffer voll ist ein Event aus (reagiert intern auf MM_WIM_DATA) und dann wieder beim nächsten vollen Puffer usw. (ohne den Prozessablauf anzuhalten). Dennoch möchte ich die Verarbeitung in einem Thread durchführen, damit 1. der Hauptthread während der Verarbeitung des Puffers nicht hängt, 2. bei hängendem Hauptthread die Verarbeitung der Audiodaten nicht unterbrochen wird. Der Thread hat zusätzlich eine höhere Priorität. Ich hoffe Du stimmst / Ihr stimmt mir da zu.
Jetzt zum eigentlichen Problem:
Als ich versuchte, ein einfaches Programm nachzubauen und anstatt der Audiokomponente einen einfachen Timer einzusetzen, kam es - egal was ich machte - nicht mehr zu dem Fehler. (Sowas ärgerliches
). Also habe ich mich auf die Suche gemacht und bin (hoffentlich) fündig geworden:
Die Audiokomponente erstellt ein CallBackWin (TWinControl), welches für die Verarbeitung der MM Messages verantwortlich ist.
Delphi-Quellcode:
TCallBackWin = Class(TWinControl)
private
{ Private declarations }
AudioComponent : PAudioComp;
procedure BufferFinished(var Msg: TMessage); message MM_WIM_DATA;
procedure WaveOpenIn(var Msg: TMessage); message MM_WIM_OPEN;
procedure WaveCloseIn(var Msg: TMessage); message MM_WIM_CLOSE;
end;
Dieses wurde ursprünglich erzeugt mit:
Delphi-Quellcode:
CallBackWin := TCallBackWinIn.CreateParented(TWinControl(Owner).Handle);
CallBackWin.Visible := false;
CallBackWin.AudioComponent := @TS;
iErr := WaveInOpen(@WaveHandle, FWaveDevice, @FWaveFmtEx, Integer(CallBackWin.Handle),
0, CALLBACK_WINDOW or WAVE_ALLOWSYNC );
Da ein Thread keinen TWinControl-Owner hat, habe ich das ganze abgewandelt in:
Delphi-Quellcode:
FWindowHandle := Classes.AllocateHWnd(WndProc); // <- Neu
CallBackWin := TCallBackWinIn.CreateParented(FWindowHandle); // <- Geändert
CallBackWin.Visible := false;
CallBackWin.AudioComponent := @TS;
iErr := WaveInOpen(@WaveHandle, FWaveDevice, @FWaveFmtEx, Integer(CallBackWin.Handle),
0, CALLBACK_WINDOW or WAVE_ALLOWSYNC );
Die WndProc war einfach leer.
Das Problem ist (war) jetzt wohl, dass überhaupt dieses CallBackWin, also ein TWinControl im TThread erzeugt ist und Messages verarbeitet (oder sonst was Problematisches vornimmt). Mir ist wohl das Problem bekannt, dass beim Erzeugen eines Formulars in einem Thread der gleiche Fehler auftritt: "Leinwand/Bild erlaubt kein Zeichnen". Deshalb dachte ich mir: Wozu überhaupt dieses CallBackWin, wenn ich ja jetzt eine WndProc-Prozedur habe. Gesagt, getan: CallBackWin entfernt und Ereignisbehandlung in WndProc gepackt:
Delphi-Quellcode:
FWindowHandle := Classes.AllocateHWnd(WndProc); // <- Neu
iErr := WaveInOpen(@WaveHandle, FWaveDevice, @FWaveFmtEx, Integer(FWindowHandle), // <- Geändert
0, CALLBACK_WINDOW or WAVE_ALLOWSYNC );
Jetzt geht's und der Fehler tritt nicht mehr auf. Allerdings verwende ich immer noch das Application.ProcessMessages in der Schleife des Threads. Scheint also kein Problem darzustellen. Meine Tests haben bisher auch ergeben, dass das im Thread aufgerufene Application.ProcessMessages auf den Hauptthread keine Auswirkungen hat. Vielleicht bedeutet das aber auch nur, dass die Messages für den Hauptthread verloren gehen? Mir fällt jedenfalls nichts auf.
An dieser Stelle wäre nur noch das Problem zu klären:
1. Ob Application.ProcessMessages (im Thread aufgerufen) sich irgendwie schadhaft/problematisch auswirkt.
2. if 1 then
Wie können wir das Problem umgehen? Sprich: "Eigene" MessageBehandlung innerhalb des Threads ermöglichen.
Das warten auf die Events in dem Thread läuft nämlich immer noch so:
Delphi-Quellcode:
while not Terminated do
begin
sleep(100);
Application.ProcessMessages;
end;
Vielleicht habt ihr ja noch eine Idee.
Grüsse
...Doc