Sysdriver: How to capture Input data and play user audio application data

Hi,

For my project I need to create virtual mic and speaker. I am Sysvad is doing it. I am very new to audio driver development. while I am using sysvad driver, I cant able to record my voice,instead generated sine wave(tonegenerate.cpp) is showing in recording device. 

Can you people please guide me, where the changes need to be taken care to capture my mic data instead of sine wave. I am going through minwavertstreams.cpp file, but I don’t have any idea where to tap the mic data. Hope I get solution on this or any suggestions for any document to read. Advance Thanks.

What application did you use to test this? My guess is that you pulled your input from the sysvad fake microphone and played it to the sysvad fake speaker. In that case, naturally, what you’re going to get is a recording of the fake microphone, which produces a sine wave. That should not have been hard to figure out. If you want to record live mic data, then your test app needs to read from a live microphone and play it to the sysvad fake speaker.

1 Like

Thank you for reply sir,
I tested sysvad on audacity. I enabled “Sysavd mic” in audacity mic options as well as speakers. I want take live mic data from sysvad fake microphone and pass it to user application and I want take take user app data and post-process it before giving to sysvad fake speakers.

My Actual Target is:
. If i get any open source of virtual driver, I will integrate my pre-process code in virtual audio driver code at both mic side and speaker side. I got to know about sysvad which can be suitable for my task. But, I cannot able to figure out, which code block I need to change to get my task. Can you give your guidelines.It will be very useful for my project. If you want me read any document about it,please suggest. Advance thanks.

Can you tell me, which buffer I have to tap to feed the user application for live mic data.

I want to take live mic data from sysvad fake microphone…

But don’t you see how ridiculous that statement is? SYSVAD has NOTHING to do with your live microphones. It is not involved with your real hardware in any way, neither on the mic side nor on the speaker side. It is a completely separate path. The topology is complicated, and if you don’t understand it, you’ll never be able to implement it.

Let me describe a typical scenario that people commonly want. Let’s say you think you have the World’s Greatest echo cancellation process. You want to do that processing in a user-mode app, even though there are better ways to do it. Let’s say Skype is your target.

In that case, you would have your processing application attach to the real microphone and the real speakers, just like a normal audio application. Then, you would have Skype attach to your fake microphone and your fake speakers. You would then modify SYSVAD so that the speaker accepts audio data and stores it in a circular buffer. You would modify SYSVAD so that the microphone feeds data from another circular buffer. You would modify SYSVAD so that the processing application can supply data to the SYSVAD mic buffer, and read data from the SYSVAD speaker buffer.

So, once things get rolling, Skype sends data to the SYSVAD fake speaker. The processing app reads that data using a back-door ioctl. The processing app also reads the live microphone data from the system’s real microphone. It mixes the mic data from Skype’s speaker data using your World’s Greatest process. The processed speaker data is then written to the real speakers for the user to hear. The processed microphone data is written back to SYSVAD’s fake microphone using a back-door ioctl. Skype then reads the microphone data from the SYSVAD buffer.

I ought to be charging you for this, because I just did your system design for you, and it had apparently not occurred to you yet. Notice that what I described is not necessarily easy. SYSVAD is big and complicated, and it can be difficult to figure out how much you can strip out. I’ve done a couple of projects like this, and after starting with SYSVAD, I threw it out and went back to the MSVAD sample from Windows 7. It has everything one needs and is much easier to understand.

Thank you for your explanation. By some forum discussions, the way previously I understood was,

  1. Generatesine() function’s(Tone generate.cpp) buffer is sending to user application and
    2.whatever we speak in live mic is storing(Savedata.cpp) in local directory files.

So, if I can replace the buffer of generate sine() function with microphone audio buffer(which is used for storing audio in directory - savedata.cpp), we can able to pass mic data to user application. So, I called this process as feeding live mic data to fake mic.

I assumed like, While copying audio buffer data to generatesine() buffer,I wanted pre-process data and store in generatesine() buffer. Here, I will open fake mic in skype app.
That is what I thought to do with sysvad.

In that case, you would have your processing application attach to the real microphone and the real speakers, just like a normal audio application. Then, you would have Skype attach to your fake microphone and your fake speakers. You would then modify SYSVAD so that the speaker accepts audio data and stores it in a circular buffer. You would modify SYSVAD so that the microphone feeds data from another circular buffer. You would modify SYSVAD so that the processing application can supply data to the SYSVAD mic buffer, and read data from the SYSVAD speaker buffer.

I wanted to implement the same thing you explained. May be I have conveyed my doubt in a wrong way. I am in learning stage of drivers, does the buffer “AudioBufferMdl” in CMiniportWaveRTStream::AllocateBuffer called as cyclic buffer? Is that buffer need to replace with generatesine() function’s buffer so that we can pass the processed data to skype. Sorry If I understood in a wrong way

  1. Generatesine() function’s(Tone generate.cpp) buffer is sending to user application, and

The generated sine wave is seen by the fake microphone consumer, like Audacity. That app does not know that the stream is not coming from a real mic.

  1. whatever we speak in live mic is storing(Savedata.cpp) in local directory files.

This is absolutely not true. The sysvad fake speaker records whatever is being sent to it from an audio application. There is no connection at all, at the driver level, between the microphone and the speaker part of an audio driver, and there is certainly no connection between the microphone from your real hardware and the speaker part of sysvad. They are all totally independent streams.

Now, if you happen to have an audio application, like Audacity, that is reading from a live microphone and copying that data stream out to the sysvad fake speaker, then the recording will be the live mic data. That’s thanks to Audacity, not to the driver. If you had your MP3 player playing into the sysvad speakers, then sysvad would record the music.

So, I called this process as feeding live mic data to fake mic.

But you are glossing over the details, which are considerable. You are also using the phrase “user application” rather loosely. There are two very different applications involved here. You have one innocent application using the standard audio APIs, which has been told to use the sysvad microphone and speakers. It doesn’t know there is anything funny going on. Then, you have your custom service application, which is using the live microphone and speakers, and as part of its processing is using custom APIs that you have to add to sysvad, in order to extract the speaker data from the innocent application and push filtered microphone data back in.

does the buffer “AudioBufferMdl” in CMiniportWaveRTStream::AllocateBuffer called as cyclic buffer?

Yes. In a typical WaveRT driver, that buffer is part of the audio hardware. The AudioEngine reads and writes directly from/to that buffer, with no driver involvement. Sysvad has to be involved in this case, because of course there is no hardware. WaveRT was designed for hardware with circular buffers, which is partly why I think it is a bad choice for a virtual device.

Is that buffer need to replace with generatesine() function’s buffer so that we can pass the processed data to skype.

Well, you can’t “replace” that buffer. That buffer is the place where the Audio Engine reads the fake microphone data and writes the fake speaker data. AudioEngine reads and writes that buffer on its own. Sysvad just updates pointers to tell the AudioEngine how much data is in the buffer (or how much room). In the sample, CMiniportWaveRTStream::WriteBytes is the spot where the GenerateSine application fills in more fake microphone data to send to Audio Engine. You need to replace that with something that copies your processed data to the buffer instead. That means, of course, that you have to have a way to get your processed data into sysvad so the driver can copy it to that buffer.

You can (and should) draw out the design on a whiteboard. There are 7 components in this design. Skype, your processing app, and the Audio Engine live in user mode. The real speaker driver, the real mic driver, the sysvad fake speaker, and the sysvad fake mic live in kernel mode.

The data path is lengthy. Data come in from the real microphone, goes through the Audio Engine, into your processing app It does its processing and writes via a back door into the sysvad microphone buffer. Audio Engine pulls sysvad microphone data and sends it to Skype. Skype writes its data through Audio Engine into the sysvad fake speaker. Your processing app reads from the fake speaker using a back door. It does some processing on that data, and writes it through the Audio Engine to the real speakers.

1 Like

Thank you sir, I am really thanful for your detailed explanation of everything which is useful particularly in my case. I can shape my work now.

I also want to try with MSVAD. Is MSVAD can be compiled with windows 10 x64 machine or it can be used on only win7 x32 machine.

Please share ,If you have any MSVAD source(URL link), sir. I am not able to build on my system with the MSVAD source I got. My machine is windows7 X64.

  1. Sysvad - Is Testing on mode is compulsory to deploy driver on target computer?
  2. If not, How to test the driver without keeping test on
  3. I tried with driver signatures off while system loading time.

Can you give me suggestion on it, sir!

Please Ignore MSVAD related doubt.I could able to conclude on that :slight_smile: Please ignore it

Now, With your suggestions i can able to understand and I could able to clean the code to get only single Mic and Speaker. Previously it was showing 5 to 6 Mic and speaker pairs.
1.I want to remove the dependency “bcdedit /set TESTSIGNING ON” while installing. So that, I can install on other pc without enabling signing off

There is another alternative by changing some things while booting. I want to avoid those dependencies. Please share any alternatives with me.

MSVAD was part of the WDK until version 8.1. You should be able to findvit in Microsoft’s archive:
https://github.com/microsoftarchive/msdn-code-gallery-microsoft/tree/master/Official%20Windows%20Driver%20Kit%20Sample/Windows%20Driver%20Kit%20(WDK)%208.1%20Samples

For systems prior to Windows 10, you have to sign the driver with a Class 3 Code-Signing Certificate, from a certificate authority that has an approved “cross certificate” For Windows 10, the driver has to be signed by Microsoft, either through WHQL or by submitting through the attestation signing service. For now, just use test mode.

Sir, I am stuck at two doubts As per my understanding, IOCTL function can read or write data from/to driver. So that, I can able to fill sysvad fake mic buffer from my process application through IOCTL. So, 1. Will IOCTL directly know the sysvad fake mic buffer to which it has to write or read. Or else we need specify the buffer name. I am thinking like we need to integrate some blocks which are related to IOCTL calls(which is specified by macros) . Then only it can able to call the sysvad driver. Which blocks I need to integrate.Is there any examples are available. 2.Without IOCTL, shall I directly use AudioBufferMdl buffer to fill the processed data. While I am going through wavert port driver, it is handling write/play buffer pointers. Will it automatically take care even if we fill our data at write pointer. 3. Does audio engine calls that buffer every 10msec? Or how much time. So that I can fill that buffer with that duration. Can you please me to go ahead by explaining this doubts. Which one is better.

  1. Your application will need to use CreateFile to open a handle to your driver. That means you need to get a file name. One of the simpler ways is to have your driver use IoRegisterDeviceInterface to register a custom interface GUID. Then you can use CM_Get_Device_Interface_List to get the file name associated with that interface. Audio devices already registry several interfaces, so you might be able to piggy-back on one of those, but it doesn’t cost very much to register your own.

I don’t know what you mean by “integrate some blocks”. Do you mean “copy existing code”? Ioctl handling is very, very common, so there are lots of examples. You’ll need to find WDM examples, because you can’t let KMDF do your dispatching. Port Class is already doing that.

  1. Without IOCTL, shall i directly use AudioBufferMdl buffer to fill the processed data.

Without an IOCTL, how do you think you’ll be doing your processing?

Audio Engine accesses the data through the buffer that gets returned as AudioBufferMdl. ReadBytes and WriteBytes get called during the driver’s simulated timer tick and when Audio Engine checks the pointer positions. Those calls update the pointers into that buffer, as if there had been real hardware.

  1. Yes, Audio Engine reads/write the buffer 10ms at a time. Your process will not be synchronized with Audio Engine, so you may have to think about how to handle it when a buffer gets full or empty.

Which one is better.

Which what is better?

Thank you sir

Thank you for the explanation sir. It has given me some knowledge to proceed further.

Hi sir, I tried to implement ioctl interface. But, whenever I added the IOCTL interface, fake mic and fake speaker are not visible in the devices list(Recording & Playback). My IOCTL implementation is: #define MTCS_IOCTL_SUPPORT 1 #if MTCS_IOCTL_SUPPORT Dispatch_type(IRP_MJ_CREATE) DRIVER_DISPATCH CSMTIOCtlCreate; //function Body has implemented Dispatch_type(IRP_MJ_CLOSE) DRIVER_DISPATCH CSMTIOCtlClose; //function Body has implemented Dispatch_type(IRP_MJ_DEVICE_CONTROL) DRIVER_DISPATCH CSMTIOCtlDeviceControl; //function Body has implemented UNICODE_STRING referenceIOCtlString; UNICODE_STRING linkIOCtlString; PDEVICE_OBJECT m_pPhysicalIOCtldeviceObject; #define CSMT_IOCTL_NT_DEVICE_NAME L"\Device\Mtctldevice" // IOCTL GUID DEFINE_GUIDSTRUCT(“05513b4d-6462-45e1-8cbd-0a43ac176b91”, MTCS_IOCTL_AUDIO); #define MTCS_IOCTL_AUDIO DEFINE_GUIDNAMED(MTCS_IOCTL_AUDIO) #define IOCTL_CSMT_READ_METHOD_BUFFERED CTL_CODE(FILE_DEVICE_UNKNOWN, 0x900, METHOD_BUFFERED, FILE_ANY_ACCESS) #endif #pragma code_seg(“PAGE”) NTSTATUS CSMTIOCtlDeviceControl ( In DEVICE_OBJECT* _DeviceObject, Inout IRP* _Irp ) { NTSTATUS ntStatus = STATUS_SUCCESS; PIO_STACK_LOCATION irpStack; ULONG ControlCode; //ULONG inBufLength; // Input buffer length ULONG outBufLength; // Output buffer length PCHAR /*inBuf,*/ outBuf; // pointer to Input and output buffer PCHAR data = “This String is from Device Driver !!!”; size_t datalen = strlen(data) + 1;//Length of data including null PAGED_CODE(); UNREFERENCED_PARAMETER(_DeviceObject); ASSERT(_DeviceObject); ASSERT(_Irp); irpStack = IoGetCurrentIrpStackLocation(_Irp); ControlCode = irpStack->Parameters.DeviceIoControl.IoControlCode; switch (ControlCode) { case IOCTL_CSMT_READ_METHOD_BUFFERED: outBufLength = irpStack->Parameters.DeviceIoControl.OutputBufferLength; if (outBufLength != 0) { outBuf = (PCHAR)_Irp->AssociatedIrp.SystemBuffer; // // Write to the buffer over-writes the input buffer content // RtlCopyBytes(outBuf, data, outBufLength); // // Assign the length of the data copied to IoStatus.Information // of the Irp and complete the Irp. // _Irp->IoStatus.Information = (outBufLength < datalen ? outBufLength : datalen); // // When the Irp is completed the content of the SystemBuffer // is copied to the User output buffer and the SystemBuffer is // is freed. // } else { _Irp->IoStatus.Information = 0; } ntStatus = STATUS_SUCCESS; break; default: ntStatus = STATUS_INVALID_PARAMETER; _Irp->IoStatus.Information = 0; break; } if (ntStatus != STATUS_INVALID_PARAMETER) { ntStatus = STATUS_SUCCESS; } _Irp->IoStatus.Status = ntStatus; IoCompleteRequest(_Irp, IO_NO_INCREMENT); return ntStatus; } IOCTL Interface in Adddevice() function: RtlInitUnicodeString(&referenceIOCtlString, CSMT_IOCTL_NT_DEVICE_NAME); ioctlStatus = IoRegisterDeviceInterface( PhysicalDeviceObject, &MTCS_IOCTL_AUDIO, NULL, &referenceIOCtlString ); if (NT_SUCCESS(ioctlStatus)) { DbgPrint(" Registered Device Status %d Name Length %d \n", ioctlStatus, referenceIOCtlString.Length); ioctlStatus = IoSetDeviceInterfaceState(&referenceIOCtlString, TRUE); if (NT_SUCCESS(ioctlStatus)) { DbgPrint(" Ready for Communication Status %d \n", ioctlStatus); } } Code is building properly and Driver package is also created properly. But, fake mic and fake speaker are not visible. Is there any line creating that problem?, sir. Or, can you suggest me to modify something so that mic & speaker will be in sound list.

Sir, I am bit confusing with name conventions. I want to understand in a practical manner In sysvad: 1. Adapter card - sound card 2. Device - Virtual mic and virtual speaker 3.Adapter driver - code which can communicate with sound card 4.Device object - Virtual mic and virtual speaker’s object Adddevice() function creates the function device objects for Virtual mic and virtual speaker’s object so that IRP can be given to devices Here, Physical device and device are diiferent? Is my understanding correct, sir?

Did you change DriverEntry to install your dispatch routines in the DriverObject after PcInitializeAdapterDriver does its thing?

You need to remember that ALL communication with streaming drivers happens through ioctls. The Audio Engine is sending your driver boatloads of ioctl requests to query your properties, negotiate your format, set the framing, and even stream the data. If you installed your own dispatcher, then you are returning an error for all of the KS ioctls.

If you get an ioctl that you don’t recognize, you need to call PcDispatchIrp so the Port Class driver can do what it would have done if you weren’t there.