Filter Device Audio - Capture the main audio stream

Hi, i?m developing a filter driver to capture the main audio stream, i have attached successfuly to the main device(a realtek device), i?m capture some irps in deviceiocontrol routine, but i can?t read the main stream(all the buffer?s are empty), and the read/write routine are never called by the application(like windows media player) while it?s sending the audio to sound card.

I will need to access the dma to get the stream?
To get access to dma i will need to use the StartIo routine? and a inf file to PnP Manager start?s this routine?

I tried to use the flags FILE_DEVICE_SOUND, FILE_DEVICE_KS and FILE_DEVICE_WAVE_OUT, but none worked.

My driver doesn?t have a inf file, i?m loading and attaching the device to device stack in DriveEntry function.

That?s my DriveEntry routine:

NTSTATUS DriverEntry(IN PDRIVER_OBJECT DriverObject, IN PUNICODE_STRING RegistryPath)
{
UNICODE_STRING DeviceName, Win32Device;
PDEVICE_OBJECT DeviceObject = NULL;
NTSTATUS status;

UNICODE_STRING DeviceNameDest;
PDEVICE_OBJECT pTargetDeviceObj;
PFILE_OBJECT pFileObj;

PDEVICE_EXTENSION pDeviceExt = NULL;

RtlInitUnicodeString(&DeviceName,L"\Device\FilterAudioTest");
RtlInitUnicodeString(&Win32Device,L"\DosDevices\FilterAudioTest");

for(unsigned int fcount = 0; fcount < IRP_MJ_MAXIMUM_FUNCTION; ++fcount){
DriverObject->MajorFunction[fcount] = OnDispatch;
}

DriverObject->MajorFunction[IRP_MJ_CREATE] = OnCreate;
DriverObject->MajorFunction[IRP_MJ_READ] = OnRead;
DriverObject->MajorFunction[IRP_MJ_WRITE] = OnWrite;
DriverObject->MajorFunction[IRP_MJ_DEVICE_CONTROL] = DeviceIOControl;
DriverObject->DriverUnload = OnUnload;
DriverObject->DriverStartIo = OnStartIo;

PWSTR devicelist = NULL;
CHAR convertida[1024];

status = IoGetDeviceInterfaces(&GUID_DEVINTERFACE_SOUND, NULL, 0, &devicelist);

if(!NT_SUCCESS(status)){
DbgPrint(“Audio - Erro ao buscar interfaces\n”);
}

if(!devicelist){
DbgPrint(“Audio - Erro lista nula\n”);
}
else{
DbgPrint(“Audio - Lista\n”);

RtlZeroMemory(convertida, sizeof(convertida));
wcstombs(convertida, devicelist, sizeof(convertida));
DbgPrint(“%s\n”, convertida);

RtlInitUnicodeString(&DeviceNameDest, devicelist);

status = IoGetDeviceObjectPointer(&DeviceNameDest,
FILE_CHARACTERISTICS_PROPAGATED,
&pFileObj,
&pTargetDeviceObj);
if (!NT_SUCCESS(status)){
DbgPrint(“Audio - Erro ao buscar o device de destino\n”);
}
else{
DbgPrint(“ptr\n”);
}

if(pFileObj){
DbgPrint(“FO %p\n”, pFileObj);
}
else{
DbgPrint(“FO - Erro\n”);
}

if(pTargetDeviceObj){
DbgPrint(“DO %p\n”, pTargetDeviceObj);
}
else{
DbgPrint(“DO - Erro\n”);
}

}

DbgPrint(“Criando device\n”);

//Cria o nosso device
status = IoCreateDevice(DriverObject,
sizeof(DEVICE_EXTENSION),
&DeviceName,
//pTargetDeviceObj->DeviceType,
FILE_DEVICE_SOUND,
//FILE_DEVICE_KS,
//FILE_DEVICE_WAVE_OUT,
//pTargetDeviceObj->Characteristics,
FILE_DEVICE_SECURE_OPEN,
FALSE,
&DeviceObject);

if (!NT_SUCCESS(status))
{
DbgPrint(“Erro ao criar device %d\n”, status);
ObDereferenceObject(pFileObj);
return status;
}

DbgPrint(“Criou device\n”);

//-f–> Cria o symbolic link para que aplica??es possam
// obter um handle para este device.
status = IoCreateSymbolicLink(&Win32Device,
&DeviceName);
if (!NT_SUCCESS(status))
{
//-f–> Ops!
DbgPrint(“Audio - Erro criar link\n”);
//IoDeleteDevice(DeviceObject);
}
else{
DbgPrint(“Audio - link criado\n”);
}

//IoGetRelatedDeviceObject
//pTargetDeviceObj = IoGetRelatedDeviceObject(DriverObject->

//-f–> Obtem nosso DEVICE_EXTENSION
pDeviceExt = (PDEVICE_EXTENSION)DeviceObject->DeviceExtension;
pDeviceExt->LowerDeviceObject = NULL;
pDeviceExt->next = NULL;
pDeviceExt->last = NULL;
pDeviceExt->count = 0;
KeInitializeMutex(&pDeviceExt->kListMutex, 0);

//NtStatus = KeWaitForMutexObject(&pExampleDeviceContext->kListMutex, Executive, KernelMode, FALSE, NULL);
//KeReleaseMutex(&pExampleDeviceContext->kListMutex, FALSE);

//-f–> Utiliza o mesmo m?todo de IO do driver original
//DeviceObject->Flags |= pTargetDeviceObj->Flags & (DO_BUFFERED_IO | DO_DIRECT_IO);
DeviceObject->Flags |= pTargetDeviceObj->Flags | DRVO_LEGACY_RESOURCES | DO_MAP_IO_BUFFER | DO_DIRECT_IO;
DeviceObject->Flags &= ~DO_DEVICE_INITIALIZING;

DbgPrint(“Atachando\n”);

//Aqui nosso driver entra na pilha de dispositivos
status = IoAttachDeviceToDeviceStackSafe(DeviceObject,
pTargetDeviceObj,
&pDeviceExt->LowerDeviceObject);
if (!NT_SUCCESS(status))
{
DbgPrint(“Erro ao atachar %d\n”, status);
IoDeleteDevice(DeviceObject);
}
else{
DbgPrint(“Atachou com sucesso\n”);
}

if(pDeviceExt->LowerDeviceObject){
DbgPrint(“Audio - Lower Device %p\n”, pDeviceExt->LowerDeviceObject);
}
else{
DbgPrint(“Audio - Lower Device NULL\n”);
}

DbgPrint(“IoCallDriver\n”);

ObDereferenceObject(pFileObj);

return status;
}

Thanks.

On 6/17/2010 10:15 AM, xxxxx@gmail.com wrote:

Hi, i´m developing a filter driver to capture the main audio stream, i have attached successfuly to the main device(a realtek device), i´m capture some irps in deviceiocontrol routine, but i can´t read the main stream(all the buffer´s are empty), and the read/write routine are never called by the application(like windows media player) while it´s sending the audio to sound card.

Kernel streaming does not use IRP_MJ_READ and IRP_MJ_WRITE. Everything
is done with ioctls. Streaming data is transmitted via
IOCTL_KS_READ_STREAM and IOCTL_KS_WRITE_STREAM.

However, if this is a WaveRT driver, even that doesn’t have to happen.
WaveRT in Vista is designed so that the kernel driver never has to be
involved in streaming at all. The audio engine updates the circular
buffer pointers in user mode, and the hardware pulls the data out on its
own.

The Vista audio APIs include a concept called “loopback recording” that
lets you gain access to the audio output stream from a user-mode
application. That would be the “supported” way of doing what you ask.

I tried to use the flags FILE_DEVICE_SOUND, FILE_DEVICE_KS and FILE_DEVICE_WAVE_OUT, but none worked.

With very few exceptions, none of those mean anything at all, especially
for a filter driver.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Thanks for the reply Tim.

So, with this method can i capture the main audio output(like speakers), apply some effects on audio stream(like equalization), and send the changed stream to the main output? without duplicate the main output stream?

Can I do this using WASAIP? IMMDevice and IAudioClient interfaces?

Thanks.

On 6/17/2010 11:40 AM, xxxxx@gmail.com wrote:

So, with this method can i capture the main audio output(like speakers), apply some effects on audio stream(like equalization), and send the changed stream to the main output? without duplicate the main output stream?

Which method do you mean? The GFX APO was designed for this. The name
implies that: “global effects audio processing object”. It is a way to
apply an effect to the audio stream on the way through. However, they
are tricky to install, and if you are not the hardware vendor, you can’t
release it as a general solution.

Can I do this using WASAIP? IMMDevice and IAudioClient interfaces?

You can certainly do this inside of your own application. If you’re
talking about affecting the output from OTHER applications, then no.
You can capture the stream with IMMDevice, but you cannot change it.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

With “loopback recording” i will only be capable of record the sound from other application but i wont be capable of change the stream. I understand.

About the effects i?m using an lib to do this task, no problem. I dont?s want to use GFX APO or sAPO to do this task.

My problem is, i want to divert the flow of main output audio stream to my application, change the stream and send the changed stream to the sound card. So, to do this i will need to develop a filter device driver for audio or a Virtual Audio device, right? I can?t do this using the Device Topoly API from windows vista/7, right?

Thanks.

On 6/17/2010 12:19 PM, xxxxx@gmail.com wrote:

About the effects i´m using an lib to do this task, no problem. I dont´s want to use GFX APO or sAPO to do this task.

Why not? Just curious. I assume you saw the other thread on this list
today, from someone who started out with a virtual audio device
solution, and is now converting to a GFX APO.

My problem is, i want to divert the flow of main output audio stream to my application, change the stream and send the changed stream to the sound card.

Yes, I understood that. And YOU need to understand that Microsoft does
not want people to do this any more. It is possible do to it, but the
mechanisms are not supported and not well documented. It is always
dangerous to embark on an adventure that is counter to the Microsoft
philosophy.

So, to do this i will need to develop a filter device driver for audio or a Virtual Audio device, right? I can´t do this using the Device Topoly API from windows vista/7, right?

You can’t do it using the device topology API, that’s true.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Well in fact i want to change and “control” the main output audio stream from windows 7.

To do this in windows vista/7 i will need to develop a filter driver or a virtual audio device, right?

Thanks.

Tim, please, Ignore my last post.

Well, I understand.

About the effects and equalization, i?m using the bass library in my application. That?s why i want to capture the main windows audio.

Please, can you help me with other solution? Can i use another approach?
I will have to do a virtual audio device like in ddk samples (msvad) to achieve this goal?

Thanks a lot.

Hi,

Please, can anyone help me with other solution? Can i use another approach?
I will have to do a virtual audio device like in ddk samples (msvad) to achieve
this goal?

Thanks.

Hi, Tim Roberts,

You said :

You can certainly do this inside of your own application. If you’re
talking about affecting the output from OTHER applications, then no.
You can capture the stream with IMMDevice, but you cannot change it.

It is possible to capture the all render streams from different applications(such as: Media Player, PowerDVD, flash player and the others)?
If it is possible, How to do it? I know capture a eCapture streams(such as : capture the voice from MIC), Thank you.
By the way, How to do it if I want to change the default audio endpoint by program.

Allen

On 6/18/2010 8:50 AM, xxxxx@sina.com wrote:

It is possible to capture the all render streams from different applications(such as: Media Player, PowerDVD, flash player and the others)?
If it is possible, How to do it?

Have you been reading ANY of the messages on the list this week? We
have two different threads already talking about this. This is exactly
what the “loopback” facility in IMMDevice is designed to do.

By the way, How to do it if I want to change the default audio endpoint by program.

It is not your program’s responsibility to do this. This is yet another
manifestation of the “who’s in charge” focus of the Vista+ audio stack.
It is up to the USER to decide which audio endpoint is the default one,
not you or your application.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Hi,

Please, can anyone help me with other solution? Can i use another approach?
I will have to do a virtual audio device like in ddk samples (msvad) to achieve
this goal?

Thanks.

Nico wrote:

Please, can anyone help me with other solution? Can i use
another approach? I will have to do a virtual audio device
like in ddk samples (msvad) to achieve this goal?

Are you seeing what anyone is saying, or just posting the same appeal over and over? Maybe you’re like the “do not send the same message for me” guy.

On 6/18/2010 12:56 PM, xxxxx@gmail.com wrote:

Please, can anyone help me with other solution? Can i use another approach?
I will have to do a virtual audio device like in ddk samples (msvad) to achieve
this goal?

There is no easy answer. For XP, the only practical solution is a
filter driver. For Vista and beyond, the supported method is to write a
GFX APO, but it’s only supported if you are the hardware vendor. It
will WORK even if you are not the vendor, but it’s not approved.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Hi Tim Roberts
Thank you for your answer,

Have you been reading ANY of the messages on the list this week? We
have two different threads already talking about this. This is exactly
what the “loopback” facility in IMMDevice is designed to do.
I have read the document about “loopback” in IMMDevice(http://msdn.microsoft.com/en-us/library/dd316551(VS.85).aspx), The follow lines said:
“If your audio adapter contains a hardware loopback device, you can use the Windows multimedia control panel, Mmsys.cpl, to designate the device as the default capture device.”

In other words, If your audio adapter doesn’t contains a hardware loopback device, How can i get the audio stream data from the eRender endpoint.

It is not your program’s responsibility to do this. This is yet another
manifestation of the “who’s in charge” focus of the Vista+ audio stack.
It is up to the USER to decide which audio endpoint is the default one,
not you or your application.
Yes, I know that is correct in normal.
If I want to switch the endpoint in my program for the reason:
I want to use the VAC for capture the stream from the eRender, I must set VAC to default ednpoint in my program, Otherwize I can’t capture the stream, My client must be switch it by himself if I do implement it in my program, I want to set VAC to default ednpoint only when My program is working, I’ll restore it before my program be close, So I must switch it dynamic in my program.

xxxxx@sina.com wrote:

I have read the document about “loopback” in IMMDevice(http://msdn.microsoft.com/en-us/library/dd316551(VS.85).aspx), The follow lines said:
“If your audio adapter contains a hardware loopback device, you can use the Windows multimedia control panel, Mmsys.cpl, to designate the device as the default capture device.”

In other words, If your audio adapter doesn’t contains a hardware loopback device, How can i get the audio stream data from the eRender endpoint.

You’re only reading part of it. You missed this paragraph:

The WASAPI system module implements loopback mode in software. In
loopback mode, WASAPI copies the output stream from the audio engine
into an application’s capture buffer. In contrast, some hardware
vendors implement loopback devices in their audio adapters. Although
hardware loopback devices are similar in operation to the WASAPI
loopback mode, they can be more difficult to use.

So, if your audio adapter doesn’t contain a hardware loopback, you can’t
use hardware loopback, but you probably didn’t want to use it anyway.
You want the SOFTWARE loopback, which does not depend on hardware support.

Yes, I know that is correct in normal.
If I want to switch the endpoint in my program for the reason:
I want to use the VAC for capture the stream from the eRender, I must set VAC to default ednpoint in my program, Otherwize I can’t capture the stream, My client must be switch it by himself if I do implement it in my program, I want to set VAC to default ednpoint only when My program is working, I’ll restore it before my program be close, So I must switch it dynamic in my program.

This is only a requirement if you choose to disregard the supported
mechanism for capturing the main audio stream. In that case, you’re
pretty much on your own.


Tim Roberts, xxxxx@probo.com
Providenza & Boekelheide, Inc.

Hi Tim Roberts,

Thank you for your help,

There is the other reason why I want to the other mechanism,
As far as I know It is possible to capture “shared-mode” only by loopback, It doesn’t work on “exclusive-mode”.
Could you tell me how can I implement “SetDefaultAudioEndpoint” function in my program, Is it possible in application or driver?

Thank you very much

Allen