[Waveaudio] waveInStart() und waveOutWrite() gleichzeitig



  • Hallo!

    Ich will wissen, ob folgendes möglich ist: ich lasse zwei Puffer per waveInAddBuffer() von meinem Mikrofon auffüllen. In einem anderen Thread werde ich benachrichtigt, wenn einer dieser Puffer voll ist und dann lasse ich diesen Puffer asynchron per waveOutWrite() ausgeben, sodass ich ihn gleich per waveInAddBuffer() wieder zur Soundkarte schicken kann und nicht warten muss, bis waveOutWrite() fertig ist. Aber der Puffer, der gerade ausgegeben wird, sollte ja während der Ausgabe nicht wieder überschrieben werden (der ja durch den anderen Thread schon wieder per waveInAddBuffer() in der Eingabe steckt), da ja noch der andere Puffer da ist und erst mal in den gelesen wird, solange der andere Puffer ausgegeben wird.

    Diese Theorie sollte doch funktionieren, oder?
    Das Ziel soll sein, dass ich einfach zeitnah das hören kann, was ich zuvor gesagt habe.



  • Nach meiner Erfahrung bekommt man mit waveOutWrite kaum niedrige Latenz hin. Guck mal bei Technologien aus dem aktuellen Jahrtausend. Also DirectSound oder gleich WASAPI. Oder ggf. auch ASIO (ist zwar auch alt, aber für Low-Latency immer noch gut).

    Davon abgesehen: mach getrennte "IN" und "OUT" Puffer, und memcpy() die Daten vom "IN" Puffer in den "OUT" Puffer rüber.

    Also quasi

    freeOutputBuffers: BufferList
    pendingBuffers: BufferList
    
    OnInputBufferComplete(in):
        out = freeOutputBuffers.RemoveFirst()
        if out == Nil
            -- wir müssen warten bis ein Output Buffer frei wird
            pendingBuffers.Append(in)
        else
            PumpData(in, out)
    
    OnOutputBufferComplete(out):
        pending = pendingBuffers.RemoveFirst()
        if pending == Nil
            -- wir müssen warten bis es wieder 'was zu spielen gibt
            freeOutputBuffers.Append(out)
        else
            PumpData(pending, out)
    
    PumpData(in, out):
        memcpy(out.Data, in.Data, out.Length)
        out.QueueForPlayback()
        in.QueueForRecording()
    


  • Hm... Das ganze ist so schwierig, da man im Callback, wo man die Nachrichten wie WIM_DATA behandelt, keine großartigen System-Funktionen aufrufen kann. Mein Code ist leider schon viel zu komplex, als dass ich ihn großartig posten könnte. Die Knackpunkte sind die hier:

    // Diese Routine läuft in einem eigenen Thread
    // jeder Inputbuffer hat eine eigene von diesen Routinen am Laufen
    // der Callback-Thread signalisiert per Event, dass der Puffer voll ist
    // mit index_ sind die Puffer indiziert
    void input::callback_worker::operator ()() {
    	while(true) {
    		in_->information_.events[index_].wait(); // im Callback-Thread ist WIM_DATA eingetreten
    		auto header = in_->information_.buffers[index_]; // Audio-Header aus dem vector holen
    		std::vector<char> buf(header.dwBytesRecorded);
    		for(unsigned n = 0; n < buf.size(); ++n) // Input-Daten kopieren
    			buf[n] = header.lpData[n];
    
    		state_type state = func_(buffer(buf)); // jetzt eine nutzerdefinierte Funktion aufrufen und die Daten übergeben			
    		in_->information_.events[index_].reset(); // das Event zurücksetzen, damit es im Callback-Thread wieder gesetzt wird, wenn WIM_DATA auf dem Puffer passiert
    
    		if(state == exit) { // func_() hat entschieden, wie es weitergeht, entweder exit
    			waveInStop(in_->information_.handle);
    			waveInReset(in_->information_.handle);
    			waveInUnprepareHeader(in_->information_.handle, &header, sizeof header);
    			return;
    		} // oder wir fügen den Puffer wieder hinzu
    		else waveInAddBuffer(in_->information_.handle, &header, sizeof(header));	
    	}
    }
    

    Jetzt sieht man: func_() darf auf keinen Fall blockieren, da sonst waveInAddBuffer() zu spät kommt und man viel Knacksen hört.
    Mein func_() sieht so aus:

    // die Variable out wurde woanders instantiiert, es ist eine Abstraktion für waveOut-Operationen
    audio::state_type test(buffer& b) { // wird aufgerufen, wenn Daten vorliegen
    	out.play(b, [](){return audio::ok;}); // spiele Daten asynchron, d.h. mache waveOutWrite() und rufe die Lambdafunktion in einem anderen Thread, wenn das Abspielen zu ende ist
    	return audio::ok;
    }
    

    test() blockiert nicht, da out::play() nur den Puffer kopiert, einen schlafenden Thread startet, waveOutPrepare() und waveOutWrite() durchführt.
    Trotzdem knackt es, wenn ich die Puffer klein mache. Und das Knacken wird mit wachsendem Puffer länger, also vergeht viel Zeit, in der nicht aufgenommen wird.

    Ich weiß leider nicht, woran das liegen kann, da die Soundkarte nach dieser Logik doch immer Betrieb haben müsste.



  • Ich sehe gerade, dass das sowieso alles nichtig ist, da man eh nur noch DirectSound verwendet.

    Schade, aber nicht zu ändern.


Anmelden zum Antworten