Skip to content
Snippets Groups Projects
Commit be914dbf authored by Lyubomir Marinov's avatar Lyubomir Marinov
Browse files

Works on improving the acoustic echo cancellation (AEC) of Windows Audio Session API (WASAPI).

parent 774c0c4c
No related branches found
No related tags found
No related merge requests found
...@@ -40,6 +40,24 @@ public class AudioCaptureClient ...@@ -40,6 +40,24 @@ public class AudioCaptureClient
private static final Logger logger private static final Logger logger
= Logger.getLogger(AudioCaptureClient.class); = Logger.getLogger(AudioCaptureClient.class);
private static int maybeIAudioCaptureClientGetNextPacketSize(
long iAudioCaptureClient)
{
int numFramesInNextPacket;
try
{
numFramesInNextPacket
= IAudioCaptureClient_GetNextPacketSize(iAudioCaptureClient);
}
catch (HResultException hre)
{
numFramesInNextPacket = 0; // Silence the compiler.
logger.error("IAudioCaptureClient_GetNextPacketSize", hre);
}
return numFramesInNextPacket;
}
/** /**
* The internal buffer of this instance in which audio data is read from the * The internal buffer of this instance in which audio data is read from the
* associated <tt>IAudioCaptureClient</tt> by the instance and awaits to be * associated <tt>IAudioCaptureClient</tt> by the instance and awaits to be
...@@ -142,6 +160,17 @@ public class AudioCaptureClient ...@@ -142,6 +160,17 @@ public class AudioCaptureClient
*/ */
final AudioFormat outFormat; final AudioFormat outFormat;
/**
* The indicator which reports whether audio samples have been read out of
* this instance since the indicator has been set to <tt>false</tt>. For
* example, determines whether audio samples have been read out of this
* instance since this instance has given control to its associated
* <tt>BufferTransferHandler</tt> (if any) and thus resolves a condition
* similar to a busy wait which arises if the associated
* <tt>BufferTransferHandler</tt> does not read any data.
*/
private boolean read;
/** /**
* The number of channels with which {@link #iAudioClient} has been * The number of channels with which {@link #iAudioClient} has been
* initialized. * initialized.
...@@ -505,6 +534,8 @@ else if (!started) ...@@ -505,6 +534,8 @@ else if (!started)
{ {
read = doRead(iMediaBuffer, buffer, offset, length); read = doRead(iMediaBuffer, buffer, offset, length);
cause = null; cause = null;
if (read > 0)
this.read = true;
} }
catch (Throwable t) catch (Throwable t)
{ {
...@@ -556,18 +587,9 @@ private BufferTransferHandler readInEventHandleCmd() ...@@ -556,18 +587,9 @@ private BufferTransferHandler readInEventHandleCmd()
* Determine the size in bytes of the next data packet in the capture * Determine the size in bytes of the next data packet in the capture
* endpoint buffer. * endpoint buffer.
*/ */
int numFramesInNextPacket; int numFramesInNextPacket
= maybeIAudioCaptureClientGetNextPacketSize(iAudioCaptureClient);
try
{
numFramesInNextPacket
= IAudioCaptureClient_GetNextPacketSize(iAudioCaptureClient);
}
catch (HResultException hre)
{
numFramesInNextPacket = 0; // Silence the compiler.
logger.error("IAudioCaptureClient_GetNextPacketSize", hre);
}
if (numFramesInNextPacket != 0) if (numFramesInNextPacket != 0)
{ {
int toRead = numFramesInNextPacket * dstFrameSize; int toRead = numFramesInNextPacket * dstFrameSize;
...@@ -619,6 +641,7 @@ private void runInEventHandleCmd(Runnable eventHandleCmd) ...@@ -619,6 +641,7 @@ private void runInEventHandleCmd(Runnable eventHandleCmd)
{ {
long eventHandle; long eventHandle;
BufferTransferHandler transferHandler; BufferTransferHandler transferHandler;
int numFramesInNextPacket;
synchronized (this) synchronized (this)
{ {
...@@ -648,6 +671,17 @@ private void runInEventHandleCmd(Runnable eventHandleCmd) ...@@ -648,6 +671,17 @@ private void runInEventHandleCmd(Runnable eventHandleCmd)
try try
{ {
transferHandler = readInEventHandleCmd(); transferHandler = readInEventHandleCmd();
if (transferHandler != null)
read = false;
/*
* If the audio engine has more samples to deliver to the
* application, deliver them as soon as the transferHandler,
* if any, has been given a chance to read (from) the
* available samples.
*/
numFramesInNextPacket
= maybeIAudioCaptureClientGetNextPacketSize(
iAudioCaptureClient);
} }
finally finally
{ {
...@@ -668,7 +702,8 @@ private void runInEventHandleCmd(Runnable eventHandleCmd) ...@@ -668,7 +702,8 @@ private void runInEventHandleCmd(Runnable eventHandleCmd)
* exception, we will WaitForSingleObject in order to * exception, we will WaitForSingleObject in order to
* give the application time to recover. * give the application time to recover.
*/ */
continue; if (read)
continue;
} }
catch (Throwable t) catch (Throwable t)
{ {
...@@ -682,6 +717,12 @@ private void runInEventHandleCmd(Runnable eventHandleCmd) ...@@ -682,6 +717,12 @@ private void runInEventHandleCmd(Runnable eventHandleCmd)
} }
} }
} }
/*
* The audio engine may already have more samples to deliver to
* the application.
*/
if (numFramesInNextPacket != 0)
continue;
int wfso; int wfso;
......
...@@ -1014,7 +1014,14 @@ private void initializeRender(final MediaLocator locator, AudioFormat format) ...@@ -1014,7 +1014,14 @@ private void initializeRender(final MediaLocator locator, AudioFormat format)
* at this time. If the transferHandler (which will normally invoke * at this time. If the transferHandler (which will normally invoke
* transferRenderData) was non-null, it would cause excessive CPU use. * transferRenderData) was non-null, it would cause excessive CPU use.
*/ */
BufferTransferHandler transferHandler = null; BufferTransferHandler transferHandler
= new BufferTransferHandler()
{
public void transferData(PushBufferStream stream)
{
transferRenderData();
}
};
render render
= new AudioCaptureClient( = new AudioCaptureClient(
...@@ -1022,7 +1029,7 @@ private void initializeRender(final MediaLocator locator, AudioFormat format) ...@@ -1022,7 +1029,7 @@ private void initializeRender(final MediaLocator locator, AudioFormat format)
locator, locator,
AudioSystem.DataFlow.PLAYBACK, AudioSystem.DataFlow.PLAYBACK,
WASAPI.AUDCLNT_STREAMFLAGS_LOOPBACK, WASAPI.AUDCLNT_STREAMFLAGS_LOOPBACK,
/* hnsBufferDuration */ Format.NOT_SPECIFIED, WASAPISystem.DEFAULT_BUFFER_DURATION,
format, format,
transferHandler); transferHandler);
replenishRender = true; replenishRender = true;
...@@ -1115,7 +1122,7 @@ private void processInput(int dwInputStreamIndex, int maxLength) ...@@ -1115,7 +1122,7 @@ private void processInput(int dwInputStreamIndex, int maxLength)
if ((dwInputStreamIndex == RENDER_INPUT_STREAM_INDEX) if ((dwInputStreamIndex == RENDER_INPUT_STREAM_INDEX)
&& replenishRender) && replenishRender)
{ {
int replenishThreshold = renderBufferMaxLength; int replenishThreshold = (3 * renderBufferMaxLength / 2);
if (audioCaptureClient.getAvailableLength() if (audioCaptureClient.getAvailableLength()
< replenishThreshold) < replenishThreshold)
...@@ -1698,7 +1705,6 @@ private void transferCaptureData() ...@@ -1698,7 +1705,6 @@ private void transferCaptureData()
* Notifies this instance that audio data has been made available in * Notifies this instance that audio data has been made available in
* {@link #render}. * {@link #render}.
*/ */
@SuppressWarnings("unused")
private void transferRenderData() private void transferRenderData()
{ {
/* /*
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment