Repository: rdp/virtual-audio-capture-grabber-device Branch: master Commit: d5a7f6f3e64f Files: 53 Total size: 252.3 KB Directory structure: gitextract_thvsja40/ ├── .gitignore ├── .gitmodules ├── ChangeLog.txt ├── LICENSE ├── README.TXT ├── TODO ├── downloads_have_moved_here.txt ├── how_to_setup_code.txt ├── innosetup_installer_options.iss ├── notes ├── propaganda └── source_code/ ├── acam/ │ ├── Dll.cpp │ ├── ReadMe.txt │ ├── acam.def │ ├── acam.h │ ├── acam.vcxproj │ ├── acam.vcxproj.filters │ ├── acam.vcxproj.user │ ├── common.h │ ├── directshow_fillbuffer.cpp │ ├── directshow_stuff.cpp │ ├── dll_main.cpp │ ├── loopback-capture-helpers.cpp │ ├── loopback-capture.cpp │ ├── silence.h │ ├── silence_background_thread.cpp │ ├── silence_control.cpp │ ├── stdafx.cpp │ ├── stdafx.h │ ├── targetver.h │ └── utilities.cpp ├── acam_is_where_all_the_code_is ├── startup_debug_options ├── synth_deprecated/ │ ├── Synth.vcxproj │ ├── Synth.vcxproj.filters │ ├── Synth.vcxproj.user │ ├── dynsrc.cpp │ ├── dynsrc.h │ ├── isynth.h │ ├── loopback-capture-helpers.cpp │ ├── loopback-capture.cpp │ ├── resource.h │ ├── synth.cpp │ ├── synth.def │ ├── synth.h │ ├── synth.rc │ ├── synth.sln │ ├── synth.vcproj │ ├── synthprp.cpp │ ├── synthprp.h │ └── useless_synth.cpp ├── virtual audio output sniffer.sln └── yo.GRF ================================================ FILE CONTENTS ================================================ ================================================ FILE: .gitignore ================================================ source_code/.vs/** source_code/Debug/* !source_code/Debug/audio_sniffer.dll source_code/Release/* !source_code/Release/audio_sniffer.dll source_code/x64/Debug/* !source_code/x64/Debug/audio_sniffer.dll source_code/x64/Release/* !source_code/x64/Release/audio_sniffer-x64.dll source_code/acam/Debug/* source_code/acam/Release/* source_code/acam/x64/Release/* source_code/acam/x64/Debug/* ================================================ FILE: .gitmodules ================================================ [submodule "screen-capture-recorder-to-video-windows-free"] path = screen-capture-recorder-to-video-windows-free url = https://rdp@github.com/rdp/screen-capture-recorder-to-video-windows-free.git ================================================ FILE: ChangeLog.txt ================================================ 0.4.6: possible async fix LOL 0.4.5: possible async fix? UI user friendly fixes 0.4.4: UI fix for selecting devices 0.4.3: cleanup UI 0.4.2: updated to latest shortcuts minor ui improvements 0.4.1: fix it to not use as much cpu win. 8 [untested] 0.4.0 hopefully windows 8 friendlier... 0.3.14 icons hopefully work after poor previous distro 0.3.13 work with FMLE again 0.3.12 ? 0.3.11 actually work with ffmpeg (scripts) 0.3.10 actually end ffmpeg's now 0.3.9 Accomodate for cmd.exe's redirect failures better (I hope). I.e. let the program start. Try to write debug outputs to a consistent place now. Allow for spaces in dshow devices. 0.3.8 5.1 support now. If you have an ASUS Xonar card you may need to "disable gx" (the little gx green-light-button in the xonar control dock). 0.3.7 make compatible with FMLE now [hopefully], minor cleanups, but FMLE still "freezes" a lot I'm not sure why. 0.3.6 hopefully fix ffmpeg complaining like: [libmp3lame @ 00670aa0] Que input is backward in time Audio timestamp 329016 < 329026 invalid, cliping00:05:29.05 bitrate= 738.6kbits/s [libmp3lame @ 00670aa0] Que input is backward in time 0.3.5 bug fixes in recording 0.3.4 can also record to ac3/video now includes the 64 bit device now, too. 0.3.3 cleaner recorder, add broadcast links, allow xp users to install it, device didn't change at all 0.3.2 fix for 64 bit windows, fix button recording 0.3.1 new program icons, new record option 0.3.0 Fix output for 48000 Hz etc. Sweet! 0.2.10 Fix installer. 0.2.9 5/8/12 move an assertion, also we use a more "directshowy" fillbuffer now, which...is probably wrong but hey :P more debug logs 0.2.8 2-21-12 64 bit release kind of 0.2.7 1-5-12 some minor directshow stuff has been updated, I believe 0.2.6 some stuff from earlier, like adding bigger buffers and setting it to the graph's timestamp. 0.2.3: better timestamps barely 0.2.2: rename program files icon, new README 0.2.1: slightly better gui 0.2.0: make the capture thread realtime priority (better performance), add some record scripts, make timestamps match up now [I think this is right] ================================================ FILE: LICENSE ================================================ Contact me about a (free) open source license (MIT ?). Basically I can release the code under "some license", but it cannot be the GPL, or so I'm told: http://social.msdn.microsoft.com/Forums/en-US/windowsdirectshowdevelopment/thread/a3e8f60b-39e4-4621-837b-50a9a9a469b7 In essence, since its its own DLL (and I hereby grant license to distribute it free of charge, no warranty given of course) you can use it "verbatim" in your project, so you don't need to worry too much about its license per se, since it's "its own dll" project. Cheers! ================================================ FILE: README.TXT ================================================ This is an audio capture device allowing you to capture all the "wave out sound" that is playing on your speakers (i.e. record what you hear) for Windows 7/Vista. Windows XP users please read the "history" section for something else you can try in its place. NB THAT IF YOU WANT TO USE WITH SCREEN RECORDING THEN PLEASE INSTALL THE MORE FULL FEATURED https://github.com/rdp/screen-capture-recorder-to-video-windows-free == Installation == To use, download+install from here: http://sourceforge.net/projects/screencapturer/files (it says "on screen capture recorder" then use it via its included start menu utilities "start menu/windows orb -> screen capturer -> ...", or use it as input to any other program that can take directshow devices as an input. == Usage == Example: VLC media player: media [menu] -> open capture device -> select capture audio device "virtual-audio-capturer" Example: ffmpeg (to save audio to file yo.mp3 from what is currently playing): $ ffmpeg -f dshow -i audio="virtual-audio-capturer" yo.mp3 (also see https://github.com/rdp/screen-capture-recorder-program if you want to stream your desktop with audio as well or capture it or the like) NB that you'll need java JRE previously installed to use the included helper apps. == Troubleshooting/Contact/feature requests == Basically, if you have a problem/feature request (ex: recording multiple audio at the same time, adding ability to strip silence, etc.), ask! rogerdpack@gmail.com or mailing list http://groups.google.com/group/roger-projects Or submit an idea/feature request to our uservoice: http://rdp.uservoice.com Also note that VLC directshow input might need buffers of at least 40 or 50 ms or it fails for some reason. But trying even higher might help. Notes from users: "I was able to reduce the VLC delay messages by changing the output frame rate from 30 to 24 and increasing the dshow buffer from 200 to 20000" (you shouldn't need to though). If I make the "PlayOn Virtual Audio Device" or the "Virtual Audio Cable" the Playback Default device instead of the Realtek speakers all the problems with your filter go away. So it would seem the problem is really with the Realtek Driver. If it doesn't work well for you (of course please report this fact), then you may be able to turn on "record what you hear"/"wave out mix" for your system, see "alternatives". If that also doesn't work then you could use a small audio cable to connect your input jack with your output jack, and then record using that. See end section of "alternatives". If you use it in VLC you'll need a directshow cache of at least 40ms, for whatever reason. Also note that it's tuned set to work best for recording "what you hear" if this doesn't work for you then ping me I maybe could add a more "realtime" option or something. Basically any feedback welcome, if it doesn't work. Also note that if you turn down your system volume within windows, it will still continue recording or playing, as apparently it captures it at "normal volume" regardless of how high your speaker output is. Any feedback welcome, including feature requests like "support more audio channels than 2". If you want a smaller download you can just download the file source_code\Release\audio_sniffer.ax then run regsvr32 audio_sniffer.ax on it as an admin user, or use the "just device" distro. = History/XP users/Alternatives = Basically, with windows XP, you can typically already "record what you hear" Run sndvol32 (Start menu -> Accessories -> Volume/Audio), choose options [menu] -> properties -> recording radio button, click ok, now click the "select" checkbox underneath "Wave Out Mix" Now with for example VLC choose "Open Capture Device" -> Audio Device Name -> select "your soundcard's name" (other programs just select your soundcard's name). With Windows Vista/7, for some reason many sound card drivers do not include this as an option. Some do though. You can check if yours already does by following instructions here: http://downloadsquad.switched.com/2007/01/15/how-to-enable-wave-out-recording-in-vista You may want to download and install new audio drivers first before looking for it, sometimes that helps. If you're in Vista/Windows 7 and see a "Wave Out Mix" following the above instructions then you're good to go: enable it, and you can use it (you thus don't need virtual-audio-capture-grabber-device at all). If you don't see it then you're in luck this, device is for you. Windows Vista/7 offer a new interface called a "loopback" adapter that captures the outgoing audio just as it is sent to your speakers [1]. This little utility captures the loopback audio and offers it to you as an input device that you can then record (it's captured as a directshow audio capture device, if that means anything to you). VLC can use this to record, for example. There are some example apps included with this package. Basically I programmed this as an open source (free) competitor to virtual audio cable. Ping me if you want me to convert it into a "real" kernel level audio device so that any program can use it, not just directshow compatible ones. Ping me if you want an easier "start/stop" recorder, too. Another option is "If your sound card doesn't have the option to record what you hear, use a cable (with 3.5mm headphone jacks on both ends) to connect the line out of your sound card to the line in (using a splitter if you need to be able to hear what you're doing, and disabling mic boost if you use a microphone input)." But that's hardware and this is a software answer :) == related == There are a handful of (mostly closed source) similar projects, some with more functionality see http://superuser.com/questions/98720/is-there-a-free-or-open-source-equivalent-to-vac == building == If you want to hack on the source code see the file how_to_setup_code.txt == Attribution == Some source code originally from the windows SDK samples, some based on [1] So you'll probably need to install the Windows SDK before playing around with the source code, legally. Enjoy! [1] http://blogs.msdn.com/b/matthew_van_eerde/archive/2008/12/16/sample-wasapi-loopback-capture-record-what-you-hear.aspx ================================================ FILE: TODO ================================================ == actually do== a "real" kernel level receive from driver esp. if I ever need buffering in dshow, intead... it does not record wav right?! if no audio device, return -1 or what not... == maybe == simpler/easier "just audio straight" GUI recorder... == meh/never == it can be set to 24 bit audio if their input "is" 24 bit audio? maybe? it doesn't complain whn I choose an unwritable dir and writing fails? It also displays as "recording to /\" if I choose the base? bad icons? background console? this gui setup is awkward... new name? new web page/real web page provide a "straight -> 2" pin always, for those 5.1'ers provide a "32 bit" pin VAC: use real mutex no sleep(1) 2 pins, one with 32 bit audio :) some thing that shows the current "bars" of how much it could/would be capturing... figure out/debug pauses in FME when clicking "stop"...huh? faster script startup speeds? One feature I had thought of would be a "strip trailing silence" option or the like. I know if I were recording things I might like it :) Another option that might be useful/interesting might be to record/merge several audio input streams at a time. This might be useful for recording skype calls so you can get the microphone as well as the "audio out". copy broadcaster here I guess? fix "everybody's bug" sleep 0's? reproduce? timestamps offset with a maximum end time? max(should, real) etc.! maybe it's fixed/ok? http://msdn.microsoft.com/en-us/library/windows/desktop/dd407208(v=vs.85).aspx maybe something like VAC's usefulness for gamers or something? try "mixing" of several? http://msdn.microsoft.com/en-us/library/windows/hardware/ff537517%28v=vs.85%29.aspx system tray "start" "stop" figure out a way to stream to say ps3: http://superuser.com/questions/44629/application-to-stream-any-audio-to-upnp-device-xbox-360 have an "easy streamer" option or just have screen capture so it can stream, too :P have a "real" webpage for it so I can track analytics/adsense do the propaganda file also propaganda something like "record what you hear program" or whatever to compete with freecorder and the like can "convert" any audio file to an media player friendlier mp3 :) higher quality, like more than 2 tracks/44Khz [possible?] work when paused (esp. VLC)...hmm...do we need a realtime option? single-threaded m/b? actually...in this instance shouldn't I be calculating my own timestamps, starting from when I first got non discontinuous data? hmm... pass in the current ref time...hmm...or maybe if the buffer is too large I arbitrarily dump some of it? I don't accomodate VLC pause but maybe I'm ok realtime still, as is currently? real ddk thing [if anybody asks for it] == notes == note: the silence thing I think works...based on not hitting that line... note: output setting volume "doesn't" matter... ================================================ FILE: downloads_have_moved_here.txt ================================================ please download releases from here (it is packaged within it): https://github.com/rdp/screen-capture-recorder-to-video-windows-free ================================================ FILE: how_to_setup_code.txt ================================================ Install Windows 10 SDK and at least Visual Studio Express 2019 The directshow baseclasses example is no longer part of the Windows SDK. Get the samples by cloning https://github.com/microsoft/Windows-classic-samples to D:\build mkdir D:\build cd /d D:\build git clone https://github.com/microsoft/Windows-classic-samples Build directshow baseclasses from the SDK: Open D:\Build\Windows-classic-samples\Samples\Win7Samples\multimedia\directshow\baseclasses\baseclasses.sln in Visual Studio, let Visual Studio upgrade the version and build it (all combinations Debug x86, Release x86, x64... especialy for the platform you want to build audio_sniffer.dll for). Clone the virtual-audio-capture-grabber-device repository cd /d D:\build git clone https://github.com/rdp/virtual-audio-capture-grabber-device cd virtual-audio-capture-grabber-device git submodule update --init --recursive then open the visual studio project file \source_code\acam\acam.vcxproj let Visual Studio upgrade the version and build it. It outputs a .dll file that you "register" via (in administrator console) $ regsvr32 XXX.dll (and unregister when you're done with regsvr32 /u XXX.dll) In essence this is the "WASAPI loopback" sample https://blogs.msdn.microsoft.com/matthew_van_eerde/2008/12/16/sample-wasapi-loopback-capture-record-what-you-hear/ combined with the "capture source filter" http://tmhare.mvps.org/downloads.htm but with many improvements :) So go to those two to learn the basics. This is meant to provide a "directshow filter" you can use in your graph, not a demo of how to do graphs. ================================================ FILE: innosetup_installer_options.iss ================================================ ; use the innosetup from screen-capturer instead now instead of this one...even though "it's" the submodule, that's how its paths are setup as of today... ================================================ FILE: notes ================================================ notes: = 5.1 = with "gx" enabled, I could still capture 5.1 when playing a file through WMP (and it could play in 5.1). with "gx" disabled, SDL_Audio couldn't create a 5.1 output with "right click and test" from control panel, with or without gx enabled, could capture. Maybe it's an openSDL bug? :) should still report it to ASUS though...in 5.1 with GX enabled, it does not capture wave's played by ffplay (even stereo). = the need for "true" = "C:\Programs\VLC TEST 1.1.10\vlc.exe" -vvv dshow:// --sout=#transcode{venc=ffmpeg,vcodec=mp2v,vb=10000,fps=30,scale=0.5,width=1280,acodec=mp3,ab=192,channels=2,samplerate=44100,soverlay}:std{access=http,mux=ts,dst=:8081} --no-sout-rtp-sap --no-sout-standard-sap --ttl=1 --sout-keep :dshow-vdev=screen-capture-recorder :dshow-adev=virtual-audio-capturer :dshow-caching=0 I think play it with VLC, or with mplayer, and/or substitute VAC with it, see what happens. check for "tearing" and out of sync audio... = random = increase dshow-caching=200 to dshow-caching=900 ? ng VLC 1.0.0 native, w disable "cool'n'quiet" Changing back to High Performance Power Plan is a workaround to fix it, but consumes a lot more energy and produces more heat. subtracting at beginning didn't help it... adding "sleeps" resulted in this message very fast: ... main debug: Decoder buffering done in 0 ms main warning: received buffer in the future main warning: received buffer in the future main warning: PTS is out of range (-10000), dropping buffer main warning: PTS is out of range (-21000), dropping buffer main warning: PTS is out of range (-32000), dropping buffer main warning: output date isn't PTS date, requesting resampling (68000) *repeat* with similar numbers. "normal" doesn't have the output date isn't PTS date message, just main warning: buffer is 40569 late, triggering upsampling main warning: resampling stopped after 5324000 usec (drift: -1875) after awhile MIN() didn't help VLC with "choose the MAX I know what you want!:" creeps up, tons of main warning: received buffer in the future main warning: PTS is out of range (-10000), dropping buffer main warning: PTS is out of range (-33219), dropping buffer main warning: buffer is 68850 late, triggering upsampling main warning: output date isn't PTS date, requesting resampling (58561) main warning: audio drift is too big (120953), dropping buffer main warning: timing screwed, stopping resampling main warning: buffer is 110795 late, triggering upsampling main warning: audio drift is too big (120316), dropping buffer main warning: output date isn't PTS date, requesting resampling (45000) main warning: audio drift is too big (155214), dropping buffer with "subtract": main debug: Buffering 80% main debug: Buffering 90% main debug: Stream buffering done (100 ms in 110 ms) main debug: Decoder buffering done in 0 ms main warning: received buffer in the future main warning: PTS is out of range (-10000), dropping buffer main warning: PTS is out of range (-33219), dropping buffer main warning: PTS is out of range (-9841), dropping buffer main warning: PTS is out of range (-18841), dropping buffer main warning: output date isn't PTS date, requesting resampling (40159) main warning: buffer is 68108 late, triggering upsampling main warning: output date isn't PTS date, requesting resampling (45000) main warning: resampling stopped after 7688000 usec (drift: -40807) main warning: buffer is 40966 late, triggering upsampling main warning: output date isn't PTS date, requesting resampling (45000) main warning: resampling stopped after 5839000 usec (drift: -43702) main warning: buffer is 43860 late, triggering upsampling weird weird stuff there...gah! normal "set it like they tell me to:" main debug: Buffering 71% main debug: Buffering 81% main debug: Buffering 91% main debug: Stream buffering done (101 ms in 95 ms) main debug: Decoder buffering done in 0 ms main warning: PTS is out of range (-10000), dropping buffer main warning: PTS is out of range (-20158), dropping buffer main warning: PTS is out of range (-30317), dropping buffer after a long while: main warning: buffer is 40176 late, triggering upsampling main warning: output date isn't PTS date, requesting resampling (44524) main warning: resampling stopped after 5862000 usec (drift: -42776) main warning: buffer is 42777 late, triggering upsampling main warning: resampling stopped after 6079000 usec (drift: 765) main warning: output date isn't PTS date, requesting resampling (45000) main warning: buffer is 44792 late, triggering upsampling main warning: resampling stopped after 6225000 usec (drift: 1394) threaded: with 5 other threads, dual core, they both seem to start giving bad feedback, possibly single threaded with smaller sizes [?] with high prioritied single thread: 4480, some big though...seems as good as single thread maybe... seemed that it avoided discontinuity messages even if "lots" of them were running at once [!] unless one had to pause to delete a file, then it could get it. virtual "they can play into me:" http://social.msdn.microsoft.com/Forums/en-AU/wdk/thread/5dce267b-d452-4d1c-ad18-0c5b6edcff5f ================================================ FILE: propaganda ================================================ waveout mix 1600 audio sniffer 390 record what you hear 12,000 virtual audio interface 320 audio loopback 1900 record speakers 12,100 audio capture 246_000 virtual audio 135_000 audio grabber 165,000 virtual audio capture grabber device alternative.to virtual audio cable propaganda from the propaganda fella http://superuser.com/questions/190093/how-to-play-mp3-files-into-the-microphone-input http://www.google.com/search?rlz=1C1SKPL_enUS426US426&sourceid=chrome&ie=UTF-8&q=record+what+you+hear+windows+7&safe=active ================================================ FILE: source_code/acam/Dll.cpp ================================================ ================================================ FILE: source_code/acam/ReadMe.txt ================================================ TODO: msvad: yeah do it some day LOL notes: big buffer: blips 2, 3 a little "fast forward snip" at the beginning when it's catching up (and has tons of blips)... with threads 1, full size: a few fast forwards... with threads 2: lots of fast forward blips at the beginning, blips through, "stumbles forward" with threads 2, actually using it, still just 2 blips I think it's ok to use. ================================================ FILE: source_code/acam/acam.def ================================================ EXPORTS DllGetClassObject PRIVATE DllCanUnloadNow PRIVATE DllRegisterServer PRIVATE DllUnregisterServer PRIVATE ================================================ FILE: source_code/acam/acam.h ================================================ // The following ifdef block is the standard way of creating macros which make exporting // from a DLL simpler. All files within this DLL are compiled with the ACAM_EXPORTS // symbol defined on the command line. This symbol should not be defined on any project // that uses this DLL. This way any other project whose source files include this file see // ACAM_API functions as being imported from a DLL, whereas this DLL sees symbols // defined with this macro as being exported. #ifdef ACAM_EXPORTS #define ACAM_API __declspec(dllexport) #else #define ACAM_API __declspec(dllimport) #endif #include #include #include #include #include "common.h" #include //extern CCritSec m_cSharedState; // unused // This class is exported from the acam.dll class ACAM_API Cacam { public: Cacam(void); // TODO: add your methods here. }; extern ACAM_API int nacam; ACAM_API int fnacam(void); EXTERN_C const GUID CLSID_VirtualCam; // reuse it... // the "parent" class class CVCam : public CSource // not needed "public IMediaFilter" since CSource is already that { public: ////////////////////////////////////////////////////////////////////////// // IUnknown ////////////////////////////////////////////////////////////////////////// static CUnknown * WINAPI CreateInstance(LPUNKNOWN lpunk, HRESULT *phr); STDMETHODIMP QueryInterface(REFIID riid, void **ppv); IFilterGraph *GetGraph() {return m_pGraph;}; ////////////////////////////////////////////////////////////////////////// // IMediaFilter overrides [added] ////////////////////////////////////////////////////////////////////////// STDMETHODIMP Run(REFERENCE_TIME tStart); // it does call this... STDMETHODIMP GetState(DWORD dw, FILTER_STATE *pState); STDMETHODIMP Pause(); //protected: // IReferenceClock *m_pClock; // wrong place I think // CBaseFilter STDMETHODIMP Stop(); private: CVCam(LPUNKNOWN lpunk, HRESULT *phr); }; // child // I think stream means "a pin" as it were... class CVCamStream : public CSourceStream, public IAMStreamConfig, public IKsPropertySet, public IAMBufferNegotiation, public IAMFilterMiscFlags //IAMPushSource { public: ////////////////////////////////////////////////////////////////////////// // IUnknown ////////////////////////////////////////////////////////////////////////// STDMETHODIMP QueryInterface(REFIID riid, void **ppv); STDMETHODIMP_(ULONG) AddRef() { return GetOwner()->AddRef(); } \ STDMETHODIMP_(ULONG) Release() { return GetOwner()->Release(); } ////////////////////////////////////////////////////////////////////////// // IQualityControl ////////////////////////////////////////////////////////////////////////// STDMETHODIMP Notify(IBaseFilter * pSender, Quality q); ////////////////////////////////////////////////////////////////////////// // IAMStreamConfig ////////////////////////////////////////////////////////////////////////// HRESULT STDMETHODCALLTYPE SetFormat(AM_MEDIA_TYPE *pmt); HRESULT STDMETHODCALLTYPE GetFormat(AM_MEDIA_TYPE **ppmt); HRESULT STDMETHODCALLTYPE GetNumberOfCapabilities(int *piCount, int *piSize); HRESULT STDMETHODCALLTYPE GetStreamCaps(int iIndex, AM_MEDIA_TYPE **pmt, BYTE *pSCC); // IAMPushSource [not implemented yet] /*HRESULT STDMETHODCALLTYPE GetLatency(REFERENCE_TIME *in) {return S_OK;}; HRESULT STDMETHODCALLTYPE GetPushSourceFlags(ULONG *in){return S_OK;}; HRESULT STDMETHODCALLTYPE SetPushSourceFlags(ULONG) {return S_OK;}; HRESULT STDMETHODCALLTYPE SetStreamOffset(REFERENCE_TIME) { return E_FAIL; } HRESULT STDMETHODCALLTYPE GetStreamOffset(REFERENCE_TIME *) {return S_OK;}; HRESULT STDMETHODCALLTYPE GetMaxStreamOffset(REFERENCE_TIME *) {return S_OK;}; HRESULT STDMETHODCALLTYPE SetMaxStreamOffset(REFERENCE_TIME) {return S_OK;};*/ ////////////////////////////////////////////////////////////////////////// // IKsPropertySet ////////////////////////////////////////////////////////////////////////// HRESULT STDMETHODCALLTYPE Set(REFGUID guidPropSet, DWORD dwID, void *pInstanceData, DWORD cbInstanceData, void *pPropData, DWORD cbPropData); HRESULT STDMETHODCALLTYPE Get(REFGUID guidPropSet, DWORD dwPropID, void *pInstanceData,DWORD cbInstanceData, void *pPropData, DWORD cbPropData, DWORD *pcbReturned); HRESULT STDMETHODCALLTYPE QuerySupported(REFGUID guidPropSet, DWORD dwPropID, DWORD *pTypeSupport); ////////////////////////////////////////////////////////////////////////// // CSourceStream ////////////////////////////////////////////////////////////////////////// HRESULT FillBuffer(IMediaSample *pms); HRESULT DecideBufferSize(IMemAllocator *pIMemAlloc, ALLOCATOR_PROPERTIES *pProperties); HRESULT CheckMediaType(const CMediaType *pMediaType); HRESULT GetMediaType(int iPosition, CMediaType *pmt); HRESULT SetMediaType(const CMediaType *pmt); HRESULT OnThreadCreate(void); HRESULT OnThreadDestroy(void); /* don't seem to get called... */ HRESULT Stop(void); HRESULT Exit(void); HRESULT Inactive(void); // ondisconnect :) CVCamStream(HRESULT *phr, CVCam *pParent, LPCWSTR pPinName); ~CVCamStream(); CRefTime m_rtPreviousSampleEndTime; // IAMBufferNegotiation -- never gets called... HRESULT STDMETHODCALLTYPE SuggestAllocatorProperties( /* [in] */ const ALLOCATOR_PROPERTIES *pprop); HRESULT STDMETHODCALLTYPE GetAllocatorProperties( ALLOCATOR_PROPERTIES *pprop); // IAMFilterMiscFlags -- never gets called... ULONG STDMETHODCALLTYPE GetMiscFlags() { return AM_FILTER_MISC_FLAGS_IS_SOURCE; } private: CVCam *m_pParent; // unused now? REFERENCE_TIME m_rtLastTime; HBITMAP m_hLogoBmp; // IReferenceClock *m_pClock; // did we ever used to set this up? HRESULT setAsNormal(CMediaType *pmt); }; ================================================ FILE: source_code/acam/acam.vcxproj ================================================  Debug Win32 Debug x64 Release Win32 Release x64 {B6830EAD-C98E-490D-8999-CAA246788BF7} Win32Proj acam audio_sniffer 10.0.22000.0 DynamicLibrary true Unicode v142 DynamicLibrary true Unicode v142 DynamicLibrary false true Unicode v142 DynamicLibrary false true Unicode v142 true D:\Build\Windows-classic-samples\Samples\Win7Samples\multimedia\directshow\baseclasses\Debug;$(WindowsSDK_LibraryPath_x86);$(LibraryPath);$(UniversalCRT_LibraryPath_x86) D:\Build\Windows-classic-samples\Samples\Win7Samples\multimedia\directshow\baseclasses;$(IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);$(VC_IncludePath) .dll true D:\Build\Windows-classic-samples\Samples\Win7Samples\multimedia\directshow\baseclasses\x64\Debug;$(WindowsSDK_LibraryPath_x64);$(LibraryPath);$(UniversalCRT_LibraryPath_x64) D:\Build\Windows-classic-samples\Samples\Win7Samples\multimedia\directshow\baseclasses;$(WindowsSDK_IncludePath);$(IncludePath);$(UniversalCRT_IncludePath);$(VC_IncludePath) .dll false D:\Build\Windows-classic-samples\Samples\Win7Samples\multimedia\directshow\baseclasses;$(IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);$(VC_IncludePath) D:\Build\Windows-classic-samples\Samples\Win7Samples\multimedia\directshow\baseclasses\Release;$(WindowsSDK_LibraryPath_x86);$(LibraryPath);$(UniversalCRT_LibraryPath_x86) .dll false D:\Build\Windows-classic-samples\Samples\Win7Samples\multimedia\directshow\baseclasses;$(IncludePath);$(WindowsSDK_IncludePath);$(UniversalCRT_IncludePath);$(VC_IncludePath) D:\Build\Windows-classic-samples\Samples\Win7Samples\multimedia\directshow\baseclasses\x64\Release;$(WindowsSDK_LibraryPath_x64);$(LibraryPath);$(UniversalCRT_LibraryPath_x64) .dll audio_sniffer-x64 Use Level3 Disabled WIN32;_DEBUG;_WINDOWS;_USRDLL;ACAM_EXPORTS;%(PreprocessorDefinitions) Windows true strmbasd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Avrt.lib;%(AdditionalDependencies) acam.def Use Level3 Disabled WIN32;_DEBUG;_WINDOWS;_USRDLL;ACAM_EXPORTS;%(PreprocessorDefinitions) Windows true strmbasd.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Avrt.lib;%(AdditionalDependencies) acam.def Level3 Use MaxSpeed true true WIN32;NDEBUG;_WINDOWS;_USRDLL;ACAM_EXPORTS;%(PreprocessorDefinitions) Windows true true true strmbase.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Avrt.lib;%(AdditionalDependencies); acam.def Level3 Use MaxSpeed true true WIN32;NDEBUG;_WINDOWS;_USRDLL;ACAM_EXPORTS;%(PreprocessorDefinitions) Windows true true true strmbase.lib;kernel32.lib;user32.lib;gdi32.lib;winspool.lib;comdlg32.lib;advapi32.lib;shell32.lib;ole32.lib;oleaut32.lib;uuid.lib;odbc32.lib;odbccp32.lib;Avrt.lib;%(AdditionalDependencies); acam.def false false false false Create Create Create Create ================================================ FILE: source_code/acam/acam.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms Header Files Header Files Header Files Header Files Header Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files Source Files ================================================ FILE: source_code/acam/acam.vcxproj.user ================================================  C:\installs\graphstudionext.exe WindowsLocalDebugger c:\dev\ffmpeg\audio\audio_only.grf c:\dev\ffmpeg\audio C:\Program Files (x86)\VideoLAN\VLC\vlc.exe WindowsLocalDebugger c:\vids\ffmpeg.exe -f dshow -r 4 -i video=screen-capture-recorder:audio=virtual-audio-capturer -y yo.mp4 c:\vids WindowsLocalDebugger C:\installs\ffmpeg-20160415-git-21acc4d-win64-static\bin\ffmpeg.exe -f dshow -i audio=virtual-audio-capturer -y out.mp3 c:\dev WindowsLocalDebugger ================================================ FILE: source_code/acam/common.h ================================================ #include #include #define SECOND_FRACTIONS_TO_GRAB 16 // dangerous macros! #define MIN(a, b) (((a) < (b)) ? (a) : (b)) #define MAX(a, b) ((a) > (b) ? (a) : (b)) extern bool bDiscontinuityDetected; extern bool bVeryFirstPacket; void ShowOutput(const char *str, ...); HRESULT set_config_string_setting(LPCTSTR szValueName, wchar_t *szToThis ); int getHtzRate(HRESULT *hr); int getBitsPerSample(); int getChannels(); HRESULT LoopbackCaptureTakeFromBuffer(BYTE pBuf[], int iSize, WAVEFORMATEX* ifNotNullThenJustSetTypeOnly, LONG* sizeWrote); #define BITS_PER_BYTE 8 #define VIRTUAL_AUDIO_VERSION L"0.6.0" void LoopbackCaptureClear(); HRESULT get_default_device(IMMDevice **ppMMDevice); ================================================ FILE: source_code/acam/directshow_fillbuffer.cpp ================================================ #include "stdafx.h" #include "acam.h" CCritSec gSharedState; extern int totalBlips; // // FillBuffer // // Stuffs the buffer with data // "they" call this, every so often... HRESULT CVCamStream::FillBuffer(IMediaSample *pms) { // I don't expect these...the parent controls this/us and doesn't call us when it is stopped I guess, so we should always be active... ShowOutput("downstream requested an audio frame (FillBuffer cal led)"); //assert(m_pParent->IsActive()); // one of these can cause freezing on "stop button" in FME //assert(!m_pParent->IsStopped()); CheckPointer(pms, E_POINTER); BYTE *pData; HRESULT hr = pms->GetPointer(&pData); if (FAILED(hr)) { assert(false); return hr; } // allow it to warmup until Run is called...so StreamTime can work right (ai ai) see http://stackoverflow.com/questions/2469855/how-to-get-imediacontrol-run-to-start-a-file-playing-with-no-delay/2470548#2470548 FILTER_STATE myState; CSourceStream::m_pFilter->GetState(INFINITE, &myState); // get parent filter state which is only set to Run "after pause" etc. while(myState != State_Running) { ShowOutput("sleeping till graph running for audio..."); ShowOutput("clearing loop back capture buffer"); // why have extra from "during" the paused state? just trash it! :) LoopbackCaptureClear(); // could stop and restart it I guess here, too...in theory...but that seems a bit violent and this more the "dshow way of doing things" // also sets bVeryFirstPacket Sleep(1); Command com; if(CheckRequest(&com)) { // from http://microsoft.public.multimedia.directx.dshow.programming.narkive.com/h8ZxbM9E/csourcestream-fillbuffer-timing if(com == CMD_STOP) { ShowOutput("exiting early from CMD_STOP thinger"); return S_FALSE; } } m_pParent->GetState(INFINITE, &myState); } if (bVeryFirstPacket) LoopbackCaptureClear(); // this is to recover from pause resumes :| // the real meat -- get all the incoming audio data LONG totalWrote = -1; hr = LoopbackCaptureTakeFromBuffer(pData, pms->GetSize(), NULL, &totalWrote); if(FAILED(hr)) { // this one can return false during shutdown, so it's actually ok to just return from here... // assert(false); ShowOutput("capture failed 1"); return hr; // don't return false here or people may get a "the graph was unable to change state unspecified error return code: 0x80004005) when hitting the stop button in graphedit :| // which actually occurs during shutdown...not sure what to really do here... //pms->SetActualDataLength(0); //return S_OK; // except we *want* the graph to abort if they've unplugged something hrm... } CAutoLock cAutoLockShared(&gSharedState); // for the bFirstPacket boolean control, except there's probably still some odd race conditions er other... hr = pms->SetActualDataLength(totalWrote); if(FAILED(hr)) { assert(false); //return hr; } // Now set the sample's start and end time stamps... WAVEFORMATEX* pwfexCurrent = (WAVEFORMATEX*)m_mt.Format(); CRefTime sampleTimeUsed = (REFERENCE_TIME)(UNITS * totalWrote) / (REFERENCE_TIME)pwfexCurrent->nAvgBytesPerSec; CRefTime rtStart; if(bDiscontinuityDetected || bVeryFirstPacket) { // could either use bFirstPacket or true here...true seemed to help that one guy... CSourceStream::m_pFilter->StreamTime(rtStart); // this is (zero based clock_time if Run already called) but we don't have access to start_offset, and want (just clock time) so don't use it... :| if(bDiscontinuityDetected && !bVeryFirstPacket) { ShowOutput("audio discontinuity detected"); } if(bVeryFirstPacket) { // my theory is that sometimes the very first packet is "big" and there's tons there [slow ffmpeg startup for instance, or something like that] and it would mess up our timing to say that it "starts" now and goes "until" its end rtStart -= sampleTimeUsed; // so instruct it to think this frame started slightly in the past... // in an effort to try and avoid some async issues ShowOutput("initial very first packet size %I64d", sampleTimeUsed); } else if (bDiscontinuityDetected) { // same deal [as if I knew what I were doing...LOL] rtStart = MAX(m_rtPreviousSampleEndTime, rtStart - sampleTimeUsed); } } else { // since there hasn't been discontinuity, I think we should be safe to tell it // that this packet starts where the previous packet ended off // since that's theoretically accurate... // exept that it ends up being bad [?] // I don't "think" this will hurt graphs that have no reference clock...hopefully... rtStart = m_rtPreviousSampleEndTime; // CRefTime cur_time; // m_pParent->StreamTime(cur_time); // rtStart = max(rtStart, cur_time); // hopefully this being commented out avoids this message/error: // [libmp3lame @ 00670aa0] Que input is backward in time // Audio timestamp 329016 < 329026 invalid, cliping 00:05:29.05 bitrate= 738.6kbits/s // [libmp3lame @ 00670aa0] Que input is backward in time } // I once tried to change it to always have monotonicity of timestamps at this point, but it didn't fix any problems, and seems to do all right without it so maybe ok [?] m_rtPreviousSampleEndTime = rtStart + sampleTimeUsed; // attempt to disallow drift/keep these in sync :| CRefTime now; CSourceStream::m_pFilter->StreamTime(now); CRefTime diff = now - m_rtPreviousSampleEndTime; if (diff > 0) m_rtPreviousSampleEndTime += CRefTime((long)1); else if (diff < -100000) m_rtPreviousSampleEndTime -= CRefTime((long)1); // NB that this *can* set it to a negative start time...hmm...which apparently is "ok" when a graph is just starting up it's expected... ShowOutput("timestamping audio packet as %lld -> %lld", rtStart, m_rtPreviousSampleEndTime); hr = pms->SetTime((REFERENCE_TIME*) &rtStart, (REFERENCE_TIME*) &m_rtPreviousSampleEndTime); if (FAILED(hr)) { assert(false); //return hr; } // if we do SetTime(NULL, NULL) here then VLC can "play" it with directshows buffers of size 0ms. // however, then VLC cannot then stream it at all. So we leave it set to some time, and just require you to have VLC buffers of at least 40 or 50 ms // [a possible VLC bug?] http://forum.videolan.org/viewtopic.php?f=14&t=92659&hilit=+50ms // whatever SetMediaTime even means... // hr = pms->SetMediaTime((REFERENCE_TIME*)&rtStart, (REFERENCE_TIME*)&m_rtPreviousSampleEndTime); //m_llSampleMediaTimeStart = m_rtSampleEndTime; if (FAILED(hr)) { assert(false); //return hr; } // Set the sample's properties. hr = pms->SetPreroll(FALSE); // tell it that this isn't preroll, so to actually use it...I think. if (FAILED(hr)) { assert(false); //return hr; } hr = pms->SetMediaType(NULL); if (FAILED(hr)) { assert(false); //return hr; } hr = pms->SetDiscontinuity(bDiscontinuityDetected || bVeryFirstPacket); if (FAILED(hr)) { assert(false); //return hr; } // Set TRUE on every sample for PCM audio http://msdn.microsoft.com/en-us/library/windows/desktop/dd407021%28v=vs.85%29.aspx hr = pms->SetSyncPoint(TRUE); if (FAILED(hr)) { assert(false); return hr; } FILTER_STATE State; m_pParent->GetState(0, &State); bDiscontinuityDetected = false; // reset late since I use it for the SetDiscontinuity method bVeryFirstPacket = false; ShowOutput("sent audio frame, %d blips, filter state %d", totalBlips, State); return S_OK; } // end FillBuffer ================================================ FILE: source_code/acam/directshow_stuff.cpp ================================================ #include "stdafx.h" ////////////////////////////////////////////////////////////////////////// // This file contains routines to register / Unregister the // Directshow filter 'Virtual Cam' // We do not use the inbuilt BaseClasses routines as we need to register as // a capture source ////////////////////////////////////////////////////////////////////////// #include #include #include #include "acam.h" #pragma once #define DECLARE_PTR(type, ptr, expr) type* ptr = (type*)(expr); extern long expectedMaxBufferSize; extern long pBufOriginalSize; ////////////////////////////////////////////////////////////////////////// // CVCam is the source filter which masquerades as a capture device ////////////////////////////////////////////////////////////////////////// CUnknown * WINAPI CVCam::CreateInstance(LPUNKNOWN lpunk, HRESULT *phr) { ASSERT(phr); CUnknown *punk = new CVCam(lpunk, phr); return punk; } IReferenceClock *globalClock; CVCam::CVCam(LPUNKNOWN lpunk, HRESULT *phr) : CSource(NAME("Virtual cam5"), lpunk, CLSID_VirtualCam) { ASSERT(phr); //m_pClock is null at this point... m_paStreams = (CSourceStream **) new CVCamStream*[1]; m_paStreams[0] = new CVCamStream(phr, this, L"Capture Virtual Audio Pin"); } HRESULT CVCam::QueryInterface(REFIID riid, void **ppv) { //Forward request for IAMStreamConfig & IKsPropertySet to the pin if(riid == _uuidof(IAMStreamConfig) || riid == _uuidof(IKsPropertySet)) return m_paStreams[0]->QueryInterface(riid, ppv); else return CSource::QueryInterface(riid, ppv); } // this does seem to get called frequently... http://msdn.microsoft.com/en-us/library/dd377472%28v=vs.85%29.aspx // I believe theoretically return VFW_S_CANT_CUE means "don't call me for data" so we should be safe here [?] but somehow still FillBuffer is called :| STDMETHODIMP CVCam::GetState(DWORD dw, FILTER_STATE *pState) { //ShowOutput("GetState call ed"); CheckPointer(pState, E_POINTER); *pState = m_State; if (m_State == State_Paused) { return VFW_S_CANT_CUE; } else { return S_OK; } } ////////////////////////////////////////////////////////////////////////// // CVCamStream is the one and only output pin of CVCam which handles // all the stuff. ////////////////////////////////////////////////////////////////////////// CVCamStream::CVCamStream(HRESULT *phr, CVCam *pParent, LPCWSTR pPinName) : CSourceStream(NAME("Virtual cam5"),phr, pParent, pPinName), m_pParent(pParent) { // Set the default media type... GetMediaType(0, &m_mt); } void loopBackRelease(); CVCamStream::~CVCamStream() { // don't get here with any consistency... ShowOutput("destructor"); } // these latency/pushsource stuffs never seem to get called...ever...at least by VLC... /* therefore unimplemented yet... HRESULT STDMETHODCALLTYPE CVCamStream::GetLatency(REFERENCE_TIME *storeItHere) { *storeItHere = 10000/SECOND_FRACTIONS_TO_GRAB; // 1_000_000ns per s, this is in 100 ns or 10_000/s return S_OK; } HRESULT STDMETHODCALLTYPE CVCamStream::GetPushSourceFlags(ULONG *storeHere) { storeHere = 0; // the default (0) is ok... return S_OK; } HRESULT STDMETHODCALLTYPE CVCamStream::SetPushSourceFlags(ULONG storeHere) { return E_UNEXPECTED; // shouldn't ever call this... } HRESULT STDMETHODCALLTYPE CVCamStream::GetStreamOffset(REFERENCE_TIME *toHere) { return E_UNEXPECTED; // shouldn't ever call this... // *toHere = m_tStart; // guess this is right... huh? offset? to what? } HRESULT STDMETHODCALLTYPE CVCamStream::GetMaxStreamOffset(REFERENCE_TIME *toHere) { *toHere = 0; // TODO set this to a reasonable value... return S_OK; } HRESULT STDMETHODCALLTYPE CVCamStream::SetMaxStreamOffset(REFERENCE_TIME) { return E_UNEXPECTED; // I don't think they'd ever call this either... }*/ HRESULT CVCamStream::QueryInterface(REFIID riid, void **ppv) { // Standard OLE stuff...allow it to query for our interfaces that we implement... if(riid == _uuidof(IAMStreamConfig)) *ppv = (IAMStreamConfig*)this; else if(riid == _uuidof(IKsPropertySet)) *ppv = (IKsPropertySet*)this; else if(riid == _uuidof(IAMBufferNegotiation)) *ppv = (IAMBufferNegotiation*)this; else return CSourceStream::QueryInterface(riid, ppv); AddRef(); return S_OK; } // // Notify // Ignore quality management messages sent from the downstream filter STDMETHODIMP CVCamStream::Notify(IBaseFilter * pSender, Quality q) { return E_NOTIMPL; } // Notify ////////////////////////////////////////////////////////////////////////// // This is called when the output format has been negotiated ////////////////////////////////////////////////////////////////////////// HRESULT CVCamStream::SetMediaType(const CMediaType *pmt) { // call the base class' SetMediaType... HRESULT hr = CSourceStream::SetMediaType(pmt); return hr; } HRESULT setupPwfex(WAVEFORMATEX *pwfex, AM_MEDIA_TYPE *pmt) { // called super early during negotiation... // we prefer PCM since WAVE_FORMAT_EXTENSIBLE seems to cause IFilterGraph::DirectConnect to fail, and many programs use it [VLC maybe does?] // also we set it up to auto convert to PCM for us // see also the bInt16 setting pwfex->wFormatTag = WAVE_FORMAT_PCM; pwfex->cbSize = 0; // apparently should be zero if using WAVE_FORMAT_PCM http://msdn.microsoft.com/en-us/library/ff538799(VS.85).aspx pwfex->nChannels = getChannels(); // 1 for mono, 2 for stereo.. HRESULT hr; pwfex->nSamplesPerSec = getHtzRate(&hr); if (hr != S_OK) { return hr; } pwfex->wBitsPerSample = 16; // 16 bit sound pwfex->nBlockAlign = (WORD)((pwfex->wBitsPerSample * pwfex->nChannels) / BITS_PER_BYTE); pwfex->nAvgBytesPerSec = pwfex->nSamplesPerSec * pwfex->nBlockAlign; // it can't calculate this itself? huh? // copy this info into the pmt hr = ::CreateAudioMediaType(pwfex, pmt, FALSE /* dont allocate more memory */); return hr; } HRESULT CVCamStream::setAsNormal(CMediaType *pmt) { WAVEFORMATEX *pwfex; pwfex = (WAVEFORMATEX *) pmt->AllocFormatBuffer(sizeof(WAVEFORMATEX)); ZeroMemory(pwfex, sizeof(WAVEFORMATEX)); if(NULL == pwfex) { return E_OUTOFMEMORY; } return setupPwfex(pwfex, pmt); } // GetMediaType // I believe "they" call this... // we only have one type at a time... // so we just return our one type... // which we already told them what it was. HRESULT CVCamStream::GetMediaType(int iPosition, CMediaType *pmt) { if (iPosition < 0) { return E_INVALIDARG; } if (iPosition > 0) { return VFW_S_NO_MORE_ITEMS; } WAVEFORMATEX *pwfex = (WAVEFORMATEX *) pmt->AllocFormatBuffer(sizeof(WAVEFORMATEX)); setupPwfex(pwfex, pmt); return S_OK; } // This method is called to see if a given output format is supported HRESULT CVCamStream::CheckMediaType(const CMediaType *pMediaType) { int cbFormat = pMediaType->cbFormat; if(*pMediaType != m_mt) { return E_INVALIDARG; } return S_OK; } // CheckMediaType // Size of each allocated buffer // seems arbitrary // maybe downstream needs a certain size? const int WaveBufferChunkSize = 16*1024; // DecideBufferSize // // This will always be called after the format has been sucessfully // negotiated. So we have a look at m_mt to see what format we agreed to. // Then we can ask for buffers of the correct size to contain them. HRESULT CVCamStream::DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProperties) { CheckPointer(pAlloc,E_POINTER); CheckPointer(pProperties,E_POINTER); WAVEFORMATEX *pwfexCurrent = (WAVEFORMATEX*)m_mt.Format(); // just use our max size for the buffer size (or whatever they specified for us, if they did) pProperties->cBuffers = 1; pProperties->cbBuffer = expectedMaxBufferSize; // Ask the allocator to reserve us this much memory... ALLOCATOR_PROPERTIES Actual; HRESULT hr = pAlloc->SetProperties(pProperties, &Actual); if(FAILED(hr)) { return hr; } // Is this allocator unsuitable if(Actual.cbBuffer < pProperties->cbBuffer) { return E_FAIL; } return NOERROR; } // DecideBufferSize HRESULT LoopbackCaptureSetup(); HRESULT CVCamStream::OnThreadDestroy() { ShowOutput("OnThreadDestroy"); return S_OK; } //less useful, for VLC anyway.. HRESULT CVCamStream::Stop() { // never get here ShowOutput("pin stop called"); // call Inactive to initiate shutdown anyway just in case :) Inactive(); return S_OK; } HRESULT CVCamStream::Exit() { // never seem to get here ShowOutput("exit called"); return S_OK; } int currentlyRunning = 0; HRESULT CVCamStream::Inactive() { // we get here at least with vlc ShowOutput("stream inactive called"); return CSourceStream::Inactive(); //parent method } STDMETHODIMP CVCam::Stop() { ShowOutput("parent stop called"); if (currentlyRunning) { ShowOutput("about to release loopback"); loopBackRelease(); ShowOutput("loopback released"); currentlyRunning = 0; // allow it to restart later...untested... } return CSource::Stop(); } STDMETHODIMP CVCam::Pause() { // pause also gets called at "graph setup" time somehow before an initial Run, and always, weirdly... // but not at "teardown" or "stop" time apparently... // but yes at "pause" time as it were (though some requests for packets still occur, even after that, yikes!) // pause is also called immediately before Stop...yikes! // so the order is pause, run, pause, stop // or, with a pause in there // pause, run, pause, run, pause, stop // or have I seen run pause before, initially [?] ai ai // unfortunately after each pause it still "requires" a few samples out or the graph will die (well, as we're doing it ???) // see also http://microsoft.public.multimedia.directx.dshow.programming.narkive.com/h8ZxbM9E/csourcestream-fillbuffer-timing this thing shucks..huh? // so current plan: just start it once, leave it going...but when Run is called it clears a buffer somewhere... ShowOutput("parent pause called"); return CBaseFilter::Pause(); } // Called when graph is first started HRESULT CVCamStream::OnThreadCreate() { ShowOutput("OnThreadCreate called"); // seems like this gets called pretty early... GetMediaType(0, &m_mt); // give it a default type...do we even need this? LOL assert(currentlyRunning == 0); // sanity check no double starts... currentlyRunning = TRUE; HRESULT hr = LoopbackCaptureSetup(); if (FAILED(hr)) { printf("IAudioCaptureClient::setup failed"); return hr; } return NOERROR; } void LoopbackCaptureClear(); STDMETHODIMP CVCam::Run(REFERENCE_TIME tStart) { ShowOutput("Parent Run called"); LoopbackCaptureClear(); // post pause, want to clear this to alert it to use the right time stamps...I think hope! // LODO why is this called seemingly *way* later than packets are already collected? I guess it calls Pause first or something, to let filters "warm up" ... so in essence, this means "unPause! go!" // ((CVCamStream*) m_paStreams[0])->m_rtPreviousSampleEndTime = 0; // looks like we accomodate for "resetting" within our own discontinuity stuff... // LODO should we...umm...not give any samples before second one or not here? return CSource::Run(tStart); // this just calls CBaseFilter::Run (which calls Run on all the pins *then* sets its state to running) } ////////////////////////////////////////////////////////////////////////// // IAMStreamConfig ////////////////////////////////////////////////////////////////////////// HRESULT STDMETHODCALLTYPE CVCamStream::SetFormat(AM_MEDIA_TYPE *pmt) { // this is them saying you "must" use this type from now on...unless pmt is NULL that "means" reset...LODO handle it someday... if(!pmt) { return S_OK; // *sure* we reset..yeah...sure we did... } if(CheckMediaType((CMediaType *) pmt) != S_OK) { return E_FAIL; // just in case :P [FME...] } m_mt = *pmt; IPin* pin; ConnectedTo(&pin); if(pin) { IFilterGraph *pGraph = m_pParent->GetGraph(); pGraph->Reconnect(this); } return S_OK; } // set format or "preferred" format if none set. It's all always the same for us, and was already set to it. HRESULT STDMETHODCALLTYPE CVCamStream::GetFormat(AM_MEDIA_TYPE **ppmt) { *ppmt = CreateMediaType(&m_mt); return S_OK; } HRESULT STDMETHODCALLTYPE CVCamStream::GetNumberOfCapabilities(int *piCount, int *piSize) { *piCount = 1; // only allow one type currently... *piSize = sizeof(AUDIO_STREAM_CONFIG_CAPS); return S_OK; } HRESULT STDMETHODCALLTYPE CVCamStream::GetStreamCaps(int iIndex, AM_MEDIA_TYPE **ppMediaType, BYTE *pSCC) { if(iIndex < 0) return E_INVALIDARG; if(iIndex > 0) return S_FALSE; if(pSCC == NULL) return E_POINTER; *ppMediaType = CreateMediaType(&m_mt); if (*ppMediaType == NULL) return E_OUTOFMEMORY; DECLARE_PTR(WAVEFORMATEX, pAudioFormat, (*ppMediaType)->pbFormat); AM_MEDIA_TYPE * pm = *ppMediaType; setupPwfex(pAudioFormat, pm); AUDIO_STREAM_CONFIG_CAPS* pASCC = (AUDIO_STREAM_CONFIG_CAPS*) pSCC; ZeroMemory(pSCC, sizeof(AUDIO_STREAM_CONFIG_CAPS)); // Set up audio capabilities [one type only, for now] pASCC->guid = MEDIATYPE_Audio; pASCC->MaximumChannels = pAudioFormat->nChannels; pASCC->MinimumChannels = pAudioFormat->nChannels; pASCC->ChannelsGranularity = 1; // doesn't matter pASCC->MaximumSampleFrequency = pAudioFormat->nSamplesPerSec; pASCC->MinimumSampleFrequency = pAudioFormat->nSamplesPerSec; pASCC->SampleFrequencyGranularity = 11025; // doesn't matter pASCC->MaximumBitsPerSample = pAudioFormat->wBitsPerSample; pASCC->MinimumBitsPerSample = pAudioFormat->wBitsPerSample; pASCC->BitsPerSampleGranularity = 16; // doesn't matter return S_OK; } ////////////////////////////////////////////////////////////////////////// // IKsPropertySet ////////////////////////////////////////////////////////////////////////// HRESULT CVCamStream::Set(REFGUID guidPropSet, DWORD dwID, void *pInstanceData, DWORD cbInstanceData, void *pPropData, DWORD cbPropData) {// Set: Cannot set any properties. return E_NOTIMPL; } // Get: Return the pin category (our only property). HRESULT CVCamStream::Get( REFGUID guidPropSet, // Which property set. DWORD dwPropID, // Which property in that set. void *pInstanceData, // Instance data (ignore). DWORD cbInstanceData, // Size of the instance data (ignore). void *pPropData, // Buffer to receive the property data. DWORD cbPropData, // Size of the buffer. DWORD *pcbReturned // Return the size of the property. ) { if (guidPropSet != AMPROPSETID_Pin) return E_PROP_SET_UNSUPPORTED; if (dwPropID != AMPROPERTY_PIN_CATEGORY) return E_PROP_ID_UNSUPPORTED; if (pPropData == NULL && pcbReturned == NULL) return E_POINTER; if (pcbReturned) *pcbReturned = sizeof(GUID); if (pPropData == NULL) return S_OK; // Caller just wants to know the size. if (cbPropData < sizeof(GUID)) return E_UNEXPECTED;// The buffer is too small. *(GUID *)pPropData = PIN_CATEGORY_CAPTURE; return S_OK; } // QuerySupported: Query whether the pin supports the specified property. HRESULT CVCamStream::QuerySupported(REFGUID guidPropSet, DWORD dwPropID, DWORD *pTypeSupport) { if (guidPropSet != AMPROPSETID_Pin) return E_PROP_SET_UNSUPPORTED; if (dwPropID != AMPROPERTY_PIN_CATEGORY) return E_PROP_ID_UNSUPPORTED; // We support getting this property, but not setting it. if (pTypeSupport) *pTypeSupport = KSPROPERTY_SUPPORT_GET; return S_OK; } HRESULT STDMETHODCALLTYPE CVCamStream::SuggestAllocatorProperties( /* [in] */ const ALLOCATOR_PROPERTIES *pprop) { // maybe we shouldn't even care though...I mean like seriously...why let them make it smaller // LODO test it both ways with FME, fast computer/slow computer does it make a difference? int requested = pprop->cbBuffer; if(pprop->cBuffers > 0) requested *= pprop->cBuffers; if(pprop->cbPrefix > 0) requested += pprop->cbPrefix; if(requested <= pBufOriginalSize) { expectedMaxBufferSize = requested; return S_OK; // they requested it? ok you got it! You're requesting possible problems! oh well! you requested it! } else { return E_FAIL; } } HRESULT STDMETHODCALLTYPE CVCamStream::GetAllocatorProperties( ALLOCATOR_PROPERTIES *pprop) {return NULL;} // they never call this... ================================================ FILE: source_code/acam/dll_main.cpp ================================================ // dllmain.cpp : Defines the entry point for the DLL application. #include "stdafx.h" #include ////////////////////////////////////////////////////////////////////////// // This file contains routines to register / Unregister the // Directshow filter 'Virtual Cam' // We do not use the inbuilt BaseClasses routines as we need to register as // a capture source ////////////////////////////////////////////////////////////////////////// #include #include #include #include #pragma comment(lib, "kernel32") #pragma comment(lib, "user32") #pragma comment(lib, "gdi32") #pragma comment(lib, "advapi32") #pragma comment(lib, "winmm") #pragma comment(lib, "ole32") #pragma comment(lib, "oleaut32") #ifdef _DEBUG #pragma comment(lib, "strmbasd") #else #pragma comment(lib, "strmbase") #endif #include #include // can only include this once/project #include #include "acam.h" #define CreateComObject(clsid, iid, var) CoCreateInstance( clsid, NULL, CLSCTX_INPROC_SERVER, iid, (void **)&var); STDAPI AMovieSetupRegisterServer( CLSID clsServer, LPCWSTR szDescription, LPCWSTR szFileName, LPCWSTR szThreadingModel = L"Both", LPCWSTR szServerType = L"InprocServer32" ); STDAPI AMovieSetupUnregisterServer( CLSID clsServer ); #ifdef _WIN64 // was {8E14549A-DB61-4309-AFA1-3578E927E933} // {8E14549B-DB61-4309-AFA1-3578E927E935} now... DEFINE_GUID(CLSID_VirtualCam, 0x8e146464, 0xdb61, 0x4309, 0xaf, 0xa1, 0x35, 0x78, 0xe9, 0x27, 0xe9, 0x35); #else // was {8E14549A-DB61-4309-AFA1-3578E927E933} // {8E14549B-DB61-4309-AFA1-3578E927E935} now... DEFINE_GUID(CLSID_VirtualCam, 0x8e14549b, 0xdb61, 0x4309, 0xaf, 0xa1, 0x35, 0x78, 0xe9, 0x27, 0xe9, 0x35); #endif const AMOVIESETUP_MEDIATYPE AMSMediaTypesVCam = { &MEDIATYPE_Audio // clsMajorType , &MEDIASUBTYPE_NULL }; // clsMinorType const AMOVIESETUP_PIN AMSPinVCam= { L"Output", // Pin string name FALSE, // Is it rendered TRUE, // Is it an output FALSE, // Can we have none FALSE, // Can we have many &CLSID_NULL, // Connects to filter NULL, // Connects to pin 1, // Number of types &AMSMediaTypesVCam // Pin Media types }; const AMOVIESETUP_FILTER AMSFilterVCam = { &CLSID_VirtualCam, // Filter CLSID L"virtual-audio-capturer", // String name MERIT_DO_NOT_USE, // Filter merit 1, // Number pins &AMSPinVCam // Pin details }; CFactoryTemplate g_Templates[] = { { L"virtual-audio-capturer", &CLSID_VirtualCam, CVCam::CreateInstance, NULL, &AMSFilterVCam }, }; int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]); // straight call to here on init, instead of to // AMovieDllRegisterServer2 which is what the other fella does... // which I assume is similar to this...maybe? STDAPI RegisterFilters( BOOL bRegister ) { HRESULT hr = NOERROR; WCHAR achFileName[MAX_PATH]; char achTemp[MAX_PATH]; ASSERT(g_hInst != 0); if( 0 == GetModuleFileNameA(g_hInst, achTemp, sizeof(achTemp))) return AmHresultFromWin32(GetLastError()); MultiByteToWideChar(CP_ACP, 0L, achTemp, lstrlenA(achTemp) + 1, achFileName, NUMELMS(achFileName)); hr = CoInitialize(0); if(bRegister) { hr = AMovieSetupRegisterServer(CLSID_VirtualCam, L"virtual-audio-capturer", achFileName, L"Both", L"InprocServer32"); } if( SUCCEEDED(hr) ) { IFilterMapper2 *fm = 0; hr = CreateComObject( CLSID_FilterMapper2, IID_IFilterMapper2, fm ); if( SUCCEEDED(hr) ) { if(bRegister) { IMoniker *pMoniker = 0; REGFILTER2 rf2; rf2.dwVersion = 1; rf2.dwMerit = MERIT_DO_NOT_USE; rf2.cPins = 1; rf2.rgPins = &AMSPinVCam; hr = fm->RegisterFilter(CLSID_VirtualCam, L"virtual-audio-capturer", &pMoniker, &CLSID_AudioInputDeviceCategory, NULL, &rf2); } else { hr = fm->UnregisterFilter(&CLSID_AudioInputDeviceCategory, 0, CLSID_VirtualCam); } } // release interface if(fm) fm->Release(); } if( SUCCEEDED(hr) && !bRegister ) hr = AMovieSetupUnregisterServer( CLSID_VirtualCam ); CoFreeUnusedLibraries(); CoUninitialize(); return hr; } // DLL.cpp ////////////////////////////////////////////////////////////////////////// // This file contains routines to register / Unregister the // Directshow filter 'Virtual Cam' // We do not use the inbuilt BaseClasses routines as we need to register as // a capture source ////////////////////////////////////////////////////////////////////////// #include STDAPI RegisterFilters( BOOL bRegister ); STDAPI DllRegisterServer() { printf("hello there"); // we actually never see this... return RegisterFilters(TRUE); } STDAPI DllUnregisterServer() { return RegisterFilters(FALSE); } STDAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID); extern "C" BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) { return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved); } ================================================ FILE: source_code/acam/loopback-capture-helpers.cpp ================================================ #include "stdafx.h" #include #include #include #include #include #include // I guess this is the default...audio output device... HRESULT get_default_device(IMMDevice **ppMMDevice) { HRESULT hr; IMMDeviceEnumerator *pMMDeviceEnumerator; // activate a device enumerator hr = CoCreateInstance( __uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator ); if (FAILED(hr)) { printf("CoCreateInstance(IMMDeviceEnumerator) failed: hr = 0x%08x\n", hr); return hr; } // get the default render endpoint hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, ppMMDevice); pMMDeviceEnumerator->Release(); if (FAILED(hr)) { printf("IMMDeviceEnumerator::GetDefaultAudioEndpoint failed: hr = 0x%08x\n", hr); // we get here if no headphones plugged in return hr; } return S_OK; } ================================================ FILE: source_code/acam/loopback-capture.cpp ================================================ #include "stdafx.h" #include #include #include #include #include #include "common.h" #include "assert.h" #include //HRESULT open_file(LPCWSTR szFileName, HMMIO *phFile); void outputStats(); HRESULT start_silence_thread(); HRESULT join_silence_thread(); IAudioCaptureClient *pAudioCaptureClient; IAudioClient *pAudioClient; HANDLE hTask; bool bDiscontinuityDetected; // init'd eleswhere bool bVeryFirstPacket; // init'd elsewhere IMMDevice *m_pMMDevice; UINT32 nBlockAlign; UINT32 pnFrames; bool propagatingNormally; CCritSec csMyLock; // shared critical section. Starts not locked... int shouldStop = true; BYTE pBufLocal[1024*1024]; // 1MB is quite awhile I think... long pBufOriginalSize = 1024*1024; //long pBufLocalSize = 1024*1024; // used for buffer size negotiation method, use expectedmaxbuffersize instead long pBufLocalCurrentEndLocation = 0; long expectedMaxBufferSize = 1024*1024; // TODO make non-global HANDLE m_hThread; static DWORD WINAPI propagateBufferForever(LPVOID pv); #define EXIT_ON_ERROR(hres) \ if (FAILED(hres)) { return hres; } #define REFTIMES_PER_SEC 10000000 const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); const IID IID_IAudioClient = __uuidof(IAudioClient); const IID IID_IAudioRenderClient = __uuidof(IAudioRenderClient); void propagateWithRawCurrentFormat(WAVEFORMATEX *toThis, HRESULT *hrOut) { WAVEFORMATEX *pwfx; IMMDevice *pMMDevice; IAudioClient *pAudioClient; HANDLE hTask; DWORD nTaskIndex = 0; hTask = AvSetMmThreadCharacteristics(L"Capture", &nTaskIndex); HRESULT hr = get_default_device(&pMMDevice); if (FAILED(hr)) { *hrOut = hr; return; } // activate an (the default, for us, since we want loopback) IAudioClient hr = pMMDevice->Activate( __uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient ); if (FAILED(hr)) { ShowOutput("IMMDevice::Activate(IAudioClient) failed: hr = 0x%08x", hr); *hrOut = hr; return; } hr = pAudioClient->GetMixFormat(&pwfx); if (FAILED(hr)) { ShowOutput("IAudioClient::GetMixFormat failed: hr = 0x%08x\n", hr); CoTaskMemFree(pwfx); pAudioClient->Release(); *hrOut = hr; return; } pAudioClient->Stop(); AvRevertMmThreadCharacteristics(hTask); pAudioClient->Release(); pMMDevice->Release(); memcpy(toThis, pwfx, sizeof(WAVEFORMATEX)); CoTaskMemFree(pwfx); *hrOut = S_OK; } int getHtzRate(HRESULT *hr) { // called earliest so return "is it alive?" WAVEFORMATEX format; propagateWithRawCurrentFormat(&format, hr); return format.nSamplesPerSec; } /* int getBitsPerSample() { WAVEFORMATEX format; propagateWithRawCurrentFormat(&format); return format.wBitsPerSample; } */ int getChannels() { WAVEFORMATEX format; HRESULT hr; propagateWithRawCurrentFormat(&format, &hr); return format.nChannels; } // we only call this once...per hit of the play button :) HRESULT LoopbackCaptureSetup() { assert(shouldStop); // double start would be odd... shouldStop = false; // allow graphs to restart, if they so desire... pnFrames = 0; bool bInt16 = true; // makes it actually work, for some reason...my guess is it's a more common format HRESULT hr; hr = get_default_device(&m_pMMDevice); // so it can re-place our pointer... if (FAILED(hr)) { return hr; } // tell it to not overflow one buffer's worth not sure if this is right or not, and thus we don't "cache" or "buffer" more than that much currently... // but a buffer size is a buffer size...hmm...as long as we keep it small though... assert(expectedMaxBufferSize <= pBufOriginalSize); // activate an (the default, for us, since we want loopback) IAudioClient hr = m_pMMDevice->Activate( __uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient ); if (FAILED(hr)) { ShowOutput("IMMDevice::Activate(IAudioClient) failed: hr = 0x%08x", hr); return hr; } // get the default device periodicity, why? I don't know... REFERENCE_TIME hnsDefaultDevicePeriod; hr = pAudioClient->GetDevicePeriod(&hnsDefaultDevicePeriod, NULL); if (FAILED(hr)) { ShowOutput("IAudioClient::GetDevicePeriod failed: hr = 0x%08x\n", hr); pAudioClient->Release(); return hr; } // get the default device format (incoming...) WAVEFORMATEX *pwfx; // incoming wave... // apparently propogated by GetMixFormat... hr = pAudioClient->GetMixFormat(&pwfx); // we free pwfx if (FAILED(hr)) { ShowOutput("IAudioClient::GetMixFormat failed: hr = 0x%08x\n", hr); CoTaskMemFree(pwfx); pAudioClient->Release(); return hr; } if (true /*bInt16*/) { // coerce int-XX wave format (like int-16 or int-32) // can do this in-place since we're not changing the size of the format // also, the engine will auto-convert from float to int for us switch (pwfx->wFormatTag) { case WAVE_FORMAT_IEEE_FLOAT: assert(false);// we never get here...I never have anyway...my guess is windows vista+ by default just uses WAVE_FORMAT_EXTENSIBLE pwfx->wFormatTag = WAVE_FORMAT_PCM; pwfx->wBitsPerSample = 16; pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8; pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; break; case WAVE_FORMAT_EXTENSIBLE: // 65534 { // naked scope for case-local variable PWAVEFORMATEXTENSIBLE pEx = reinterpret_cast(pwfx); if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat)) { // WE GET HERE! pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; // convert it to PCM, but let it keep as many bits of precision as it has initially...though it always seems to be 32 // comment this out and set wBitsPerSample to pwfex->wBitsPerSample = getBitsPerSample(); to get an arguably "better" quality 32 bit pcm // unfortunately flash media live encoder basically rejects 32 bit pcm, and it's not a huge gain sound quality-wise, so disabled for now. pwfx->wBitsPerSample = 16; pEx->Samples.wValidBitsPerSample = pwfx->wBitsPerSample; pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8; pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; // see also setupPwfex method } else { ShowOutput("Don't know how to coerce mix format to int-16\n"); CoTaskMemFree(pwfx); pAudioClient->Release(); return E_UNEXPECTED; } } break; default: ShowOutput("Don't know how to coerce WAVEFORMATEX with wFormatTag = 0x%08x to int-16\n", pwfx->wFormatTag); CoTaskMemFree(pwfx); pAudioClient->Release(); return E_UNEXPECTED; } } MMCKINFO ckRIFF = {0}; MMCKINFO ckData = {0}; nBlockAlign = pwfx->nBlockAlign; // avoid stuttering on close when using loopback // http://social.msdn.microsoft.com/forums/en-US/windowspro-audiodevelopment/thread/c7ba0a04-46ce-43ff-ad15-ce8932c00171/ //IAudioClient *pAudioClient = NULL; //IAudioCaptureClient *pCaptureClient = NULL; IMMDeviceEnumerator *pEnumerator = NULL; IMMDevice *pDevice = NULL; IAudioRenderClient *pRenderClient = NULL; WAVEFORMATEXTENSIBLE *captureDataFormat = NULL; BYTE *captureData; REFERENCE_TIME hnsRequestedDuration = REFTIMES_PER_SEC; hr = CoCreateInstance( CLSID_MMDeviceEnumerator, NULL, CLSCTX_ALL, IID_IMMDeviceEnumerator, (void**)&pEnumerator); EXIT_ON_ERROR(hr) hr = get_default_device(&pDevice); EXIT_ON_ERROR(hr) hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); EXIT_ON_ERROR(hr) hr = pAudioClient->GetMixFormat((WAVEFORMATEX **)&captureDataFormat); EXIT_ON_ERROR(hr) // Silence: initialise in sharedmode [this is the "silence" bug overwriter, so buffer doesn't matter as much...] hr = pAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, 0, REFTIMES_PER_SEC, // buffer size a full 1.0s, though prolly doesn't matter here. 0, pwfx, NULL); EXIT_ON_ERROR(hr) // get the frame count UINT32 bufferFrameCount; hr = pAudioClient->GetBufferSize(&bufferFrameCount); EXIT_ON_ERROR(hr) // create a render client hr = pAudioClient->GetService(IID_IAudioRenderClient, (void**)&pRenderClient); EXIT_ON_ERROR(hr) // get the buffer hr = pRenderClient->GetBuffer(bufferFrameCount, &captureData); EXIT_ON_ERROR(hr) // release it hr = pRenderClient->ReleaseBuffer(bufferFrameCount, AUDCLNT_BUFFERFLAGS_SILENT); EXIT_ON_ERROR(hr) // release the audio client pAudioClient->Release(); EXIT_ON_ERROR(hr) // create a new IAudioClient hr = pDevice->Activate(IID_IAudioClient, CLSCTX_ALL, NULL, (void**)&pAudioClient); EXIT_ON_ERROR(hr) // -============================ now the sniffing code initialization stuff, direct from mauritius... =================================== // call IAudioClient::Initialize // note that AUDCLNT_STREAMFLAGS_LOOPBACK and AUDCLNT_STREAMFLAGS_EVENTCALLBACK // do not work together... // the "data ready" event never gets set // so we're going to have to do this in a timer-driven loop... hr = pAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, REFTIMES_PER_SEC, // buffer size a full 1.0s, seems ok VLC 0, pwfx, 0 ); if (FAILED(hr)) { ShowOutput("IAudioClient::Initialize failed: hr = 0x%08x\n", hr); pAudioClient->Release(); return hr; } CoTaskMemFree(pwfx); // activate an IAudioCaptureClient hr = pAudioClient->GetService( __uuidof(IAudioCaptureClient), (void**)&pAudioCaptureClient ); if (FAILED(hr)) { ShowOutput("IAudioClient::GetService(IAudioCaptureClient) failed: hr 0x%08x\n", hr); pAudioClient->Release(); return hr; } // register with MMCSS DWORD nTaskIndex = 0; hTask = AvSetMmThreadCharacteristics(L"Capture", &nTaskIndex); if (NULL == hTask) { DWORD dwErr = GetLastError(); ShowOutput("AvSetMmThreadCharacteristics failed: last error = %u\n", dwErr); pAudioCaptureClient->Release(); pAudioClient->Release(); return HRESULT_FROM_WIN32(dwErr); } // call IAudioClient::Start hr = pAudioClient->Start(); if (FAILED(hr)) { ShowOutput("IAudioClient::Start failed: hr = 0x%08x\n", hr); AvRevertMmThreadCharacteristics(hTask); pAudioCaptureClient->Release(); pAudioClient->Release(); return hr; } // init some var's [XXXX using global vars doesn't work if I had 2 of these at the same time in the same process [?]] bDiscontinuityDetected = true; bVeryFirstPacket = true; // start the forever grabbing thread... DWORD dwThreadID; m_hThread = CreateThread(NULL, 0, propagateBufferForever, 0, 0, &dwThreadID); if(!m_hThread) { DWORD dwErr = GetLastError(); return HRESULT_FROM_WIN32(dwErr); } else { // we...shouldn't need this...maybe? // seems to make no difference anyway, and probably won't hurt... hr = SetThreadPriority(m_hThread, THREAD_PRIORITY_TIME_CRITICAL); if (FAILED(hr)) { return hr; } } hr = start_silence_thread(); return hr; } // end LoopbackCaptureSetup HRESULT propagateBufferOnce(); extern CCritSec gSharedState; int totalSuccessFullyread = 0; int totalBlips = 0; int totalOverflows = 0; HRESULT propagateBufferOnce() { HRESULT hr = S_OK; // grab next audio chunk... int gotAnyAtAll = FALSE; DWORD start_time = timeGetTime(); while (!shouldStop) { UINT32 nNextPacketSize; hr = pAudioCaptureClient->GetNextPacketSize(&nNextPacketSize); // get next packet, if one is ready... if (FAILED(hr)) { ShowOutput("IAudioCaptureClient::GetNextPacketSize failed after %u frames: hr = 0x%08x\n", pnFrames, hr); /*pAudioClient->Stop(); // cleaned up in the teardown, don't do it twice -> segfault! [we get here if unplug headphones] AvRevertMmThreadCharacteristics(hTask); pAudioCaptureClient->Release(); pAudioClient->Release();*/ return hr; } if (0 == nNextPacketSize) { // (CA) - this condition appears on Win8 when there is "silence" in the audio stream // (CA) - we don't appear to hit this condition in Win7 .. so I have eliminated the logic in here for right now... /* // no data yet, we're waiting, between incoming chunks of audio. [occurs even with silence on the line--it just means no new data yet] DWORD millis_to_fill = (DWORD) (1.0/SECOND_FRACTIONS_TO_GRAB*1000); // truncate is ok :) -- 1s assert(millis_to_fill > 1); // sanity DWORD current_time = timeGetTime(); if((current_time - start_time > millis_to_fill)) { // I don't think we ever get to here anymore...thankfully, since it's mostly broken code probably, anyway if(!gotAnyAtAll) { // We get here under high load... // ignore for now, but sleep more ShowOutput("detected high amount of time without receiving a packet from the capturer!"); start_time = timeGetTime(); Sleep(0); } } else { Sleep(1); // doesn't seem to hurt cpu--"sleep x ms" continue; } */ Sleep(1); continue; } else { gotAnyAtAll = TRUE; totalSuccessFullyread++; } // get the captured data BYTE *pData; UINT32 nNumFramesToRead; DWORD dwFlags; // I guess it gives us...as much audio as possible to read...probably hr = pAudioCaptureClient->GetBuffer( &pData, &nNumFramesToRead, &dwFlags, NULL, NULL ); // ACTUALLY GET THE BUFFER which I assume it reads in the format of the fella we passed in if (FAILED(hr)) { ShowOutput("IAudioCaptureClient::GetBuffer failed after %u frames: hr = 0x%08x\n", pnFrames, hr); /*pAudioClient->Stop(); AvRevertMmThreadCharacteristics(hTask); pAudioCaptureClient->Release(); pAudioClient->Release();*/ return hr; } { CAutoLock cAutoLockShared(&gSharedState); // for the booleans, we lock csMyLock later :| XXXX weird? if( dwFlags == 0 ) { // the good case, got audio packet // we'll let fillbuffer set bDiscontinuityDetected = false; since it uses it to know if the next packet should restart, etc. } else if (bDiscontinuityDetected && AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY == dwFlags) { ShowOutput("Probably spurious glitch reported on first packet, or two discontinuity errors occurred before it read from the cached buffer\n"); bDiscontinuityDetected = true; // won't hurt, even if it is a real first packet :) // XXXX it should probably clear the buffers if it ever gets discontinuity // or "let" it clear the buffers then send the new data on // as we have any left-over data that will be assigned a wrong timestamp // but it won't be too far wrong, compared to what it would otherwise be with always // assigning it the current graph timestamp, like we used to... } else if (AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY == dwFlags) { ShowOutput("IAudioCaptureClient::discontinuity GetBuffer set flags to 0x%08x after %u frames\n", dwFlags, pnFrames); // expected your CPU gets behind or what not. I guess. /*pAudioClient->Stop(); AvRevertMmThreadCharacteristics(hTask); pAudioCaptureClient->Release(); pAudioClient->Release(); return E_UNEXPECTED;*/ bDiscontinuityDetected = true; } else if (AUDCLNT_BUFFERFLAGS_SILENT == dwFlags) { // ShowOutput("IAudioCaptureClient::silence (just) from GetBuffer after %u frames\n", pnFrames); // expected if there's silence (i.e. nothing playing), since we now include the "silence generator" work-around... // at least in windows 7, we get here... } else { // probably silence + discontinuity ShowOutput("IAudioCaptureClient::unknown discontinuity GetBuffer set flags to 0x%08x after %u frames\n", dwFlags, pnFrames); bDiscontinuityDetected = true; // probably is some type of discontinuity :P } if(bDiscontinuityDetected) totalBlips++; if (0 == nNumFramesToRead) { // we should probably never get here, right? // my guess is that we don't, even in win8? // I mean, this is probably really messed up it told us it had some data to grab, we try to grab it, it returns us nothing? /* ShowOutput("death failure: IAudioCaptureClient::GetBuffer said to read 0 frames after %u frames\n", pnFrames); pAudioClient->Stop(); AvRevertMmThreadCharacteristics(hTask); pAudioCaptureClient->Release(); pAudioClient->Release(); return E_UNEXPECTED; */ } pnFrames += nNumFramesToRead; // increment total count... // lBytesToWrite typically 1792 bytes... LONG lBytesToWrite = nNumFramesToRead * nBlockAlign; // nBlockAlign is "audio block size" or frame size, for one audio segment... { CAutoLock cObjectLock(&csMyLock); // Lock the critical section, releases scope after block is over... if(pBufLocalCurrentEndLocation > expectedMaxBufferSize) { // this happens during VLC pauses... // I have no idea what I'm doing here... this doesn't fix it, but helps a bit... TODO FINISH THIS // it seems like if you're just straight recording then you want this big...otherwise you want it like size 0 and non-threaded [pausing with graphedit, for example]... [?] // if you were recording skype, you'd want it non realtime...hmm... // it seems that if the cpu is loaded, we run into this if it's for the next packet...hmm... // so basically we don't accomodate realtime at all currently...hmmm... ShowOutput("overfilled buffer, cancelling/flushing."); //over flow overflow appears VLC just keeps reading though, when paused [?] but not graphedit...or does it? pBufLocalCurrentEndLocation = 0; totalOverflows++; bDiscontinuityDetected = true; } for(INT i = 0; i < lBytesToWrite && pBufLocalCurrentEndLocation < expectedMaxBufferSize; i++) { pBufLocal[pBufLocalCurrentEndLocation++] = pData[i]; } } } hr = pAudioCaptureClient->ReleaseBuffer(nNumFramesToRead); if (FAILED(hr)) { ShowOutput("IAudioCaptureClient::ReleaseBuffer failed after %u frames: hr = 0x%08x\n", pnFrames, hr); /*pAudioClient->Stop(); AvRevertMmThreadCharacteristics(hTask); pAudioCaptureClient->Release(); pAudioClient->Release(); */ return hr; } return hr; } // while !got anything && should continue loop return S_OK; // stop was called... } // iSize is max size of the BYTE buffer...so maybe...we should just drop it if we have past that size? hmm...we're probably HRESULT LoopbackCaptureTakeFromBuffer(BYTE pBuf[], int iSize, WAVEFORMATEX* ifNotNullThenJustSetTypeOnly, LONG* totalBytesWrote) { while(!shouldStop) { // allow this to exit, too, at shutdown, possibly a few times we kind of got stuck in here, waiting for more data, but it was shutdown so not receiving any more data :| { CAutoLock cObjectLock(&csMyLock); // Lock the critical section, releases scope after block is done... if(pBufLocalCurrentEndLocation > 0) { // fails lodo is that ok though? // assert(pBufLocalCurrentEndLocation <= expectedMaxBufferSize); int totalToWrite = MIN(pBufLocalCurrentEndLocation, expectedMaxBufferSize); ASSERT(totalToWrite <= iSize); // just in case...just in case almost... memcpy(pBuf, pBufLocal, totalToWrite); *totalBytesWrote = totalToWrite; pBufLocalCurrentEndLocation = 0; return S_OK; } // else fall through to sleep outside the lock... } // sleep outside the lock ... // using sleep doesn't seem to hurt the cpu // and it seems to not get many "discontinuity" messages currently... Sleep(1); if (!propagatingNormally) { return E_FAIL; // the capture has died, Jim! } } return E_FAIL; // we didn't fill anything...and are shutting down... } void LoopbackCaptureClear() { CAutoLock cObjectLock(&csMyLock); // Lock the critical section, releases scope after block is done... pBufLocalCurrentEndLocation = 0; bDiscontinuityDetected = 1; // it uses this for timestamping the next packet } // clean up void loopBackRelease() { // tell running collector thread to end... shouldStop = 1; WaitForSingleObject(m_hThread, INFINITE); CloseHandle(m_hThread); m_hThread = NULL; if (pAudioClient) { m_pMMDevice->Release(); pAudioClient->Stop(); //pAudioClient->GetService(); pAudioCaptureClient->Release(); pAudioClient->Release(); } AvRevertMmThreadCharacteristics(hTask); // thread is done, we are exiting... pBufLocalCurrentEndLocation = 0; outputStats(); join_silence_thread(); } void outputStats() { wchar_t output[250]; wsprintf(output, L"v. %s total reads %d total discontinuity blips %d, total overflows %d", VIRTUAL_AUDIO_VERSION, totalSuccessFullyread , totalBlips - 1, totalOverflows); set_config_string_setting(L"last_output", output); } // called via reflection :) static DWORD WINAPI propagateBufferForever(LPVOID pv) { propagatingNormally = true; // XXXX use events, the coolio way :| while(!shouldStop) { HRESULT hr = propagateBufferOnce(); if(FAILED(hr)) { propagatingNormally = false; return hr; // exit thread } } return S_OK; } ================================================ FILE: source_code/acam/silence.h ================================================ #include "common.h" struct PlaySilenceThreadFunctionArguments { IMMDevice *pMMDevice; HANDLE hStartedEvent; HANDLE hStopEvent; HRESULT hr; }; ================================================ FILE: source_code/acam/silence_background_thread.cpp ================================================ // silence.cpp from msdn blog #include "stdafx.h" #include #include #include #include #include #include "silence.h" // started in its own thread DWORD WINAPI PlaySilenceThreadFunction(LPVOID pContext) { PlaySilenceThreadFunctionArguments *pArgs = (PlaySilenceThreadFunctionArguments*)pContext; IMMDevice *pMMDevice = pArgs->pMMDevice; HRESULT &hr = pArgs->hr; // CoInitialize (probably don't even need this...) hr = CoInitialize(NULL); if (FAILED(hr)) { printf("CoInitialize failed: hr = 0x%08x\n", hr); return 0; } // activate an IAudioClient IAudioClient *pAudioClient; hr = pMMDevice->Activate( __uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient ); if (FAILED(hr)) { printf("IMMDevice::Activate(IAudioClient) failed: hr = 0x%08x", hr); //CoUninitialize(); return 0; } // get the mix format WAVEFORMATEX *pwfx; hr = pAudioClient->GetMixFormat(&pwfx); if (FAILED(hr)) { printf("IAudioClient::GetMixFormat failed: hr = 0x%08x\n", hr); pAudioClient->Release(); //CoUninitialize(); return 0; } // initialize the audio client hr = pAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_EVENTCALLBACK, 0, 0, pwfx, NULL ); CoTaskMemFree(pwfx); if (FAILED(hr)) { printf("IAudioClient::Initialize failed: hr 0x%08x\n", hr); pAudioClient->Release(); //CoUninitialize(); return 0; } // get the buffer size UINT32 nFramesInBuffer; hr = pAudioClient->GetBufferSize(&nFramesInBuffer); if (FAILED(hr)) { printf("IAudioClient::GetBufferSize failed: hr 0x%08x\n", hr); pAudioClient->Release(); //CoUninitialize(); return 0; } // get an IAudioRenderClient IAudioRenderClient *pAudioRenderClient; hr = pAudioClient->GetService( __uuidof(IAudioRenderClient), (void**)&pAudioRenderClient ); if (FAILED(hr)) { printf("IAudioClient::GetService(IAudioRenderClient) failed: hr 0x%08x\n", hr); pAudioClient->Release(); //CoUninitialize(); return 0; } // create a "feed me" event HANDLE hFeedMe; hFeedMe = CreateEvent(NULL, FALSE, FALSE, NULL); if (NULL == hFeedMe) { DWORD dwErr = GetLastError(); hr = HRESULT_FROM_WIN32(dwErr); printf("CreateEvent failed: last error is %u\n", dwErr); pAudioRenderClient->Release(); pAudioClient->Release(); //CoUninitialize(); return 0; } // set it as the event handle hr = pAudioClient->SetEventHandle(hFeedMe); if (FAILED(hr)) { printf("IAudioClient::SetEventHandle failed: hr = 0x%08x\n", hr); pAudioRenderClient->Release(); pAudioClient->Release(); //CoUninitialize(); return 0; } // pre-fill a single buffer of silence BYTE *pData; hr = pAudioRenderClient->GetBuffer(nFramesInBuffer, &pData); if (FAILED(hr)) { printf("IAudioRenderClient::GetBuffer failed on pre-fill: hr = 0x%08x\n", hr); pAudioClient->Stop(); printf("TODO: unregister with MMCSS\n"); CloseHandle(hFeedMe); pAudioRenderClient->Release(); pAudioClient->Release(); //CoUninitialize(); return 0; } // release the buffer with the silence flag hr = pAudioRenderClient->ReleaseBuffer(nFramesInBuffer, AUDCLNT_BUFFERFLAGS_SILENT); if (FAILED(hr)) { printf("IAudioRenderClient::ReleaseBuffer failed on pre-fill: hr = 0x%08x\n", hr); pAudioClient->Stop(); printf("TODO: unregister with MMCSS\n"); CloseHandle(hFeedMe); pAudioRenderClient->Release(); pAudioClient->Release(); //CoUninitialize(); return 0; } // register with MMCSS DWORD nTaskIndex = 0; HANDLE hTask = AvSetMmThreadCharacteristics(L"Playback", &nTaskIndex); if (NULL == hTask) { DWORD dwErr = GetLastError(); hr = HRESULT_FROM_WIN32(dwErr); printf("AvSetMmThreadCharacteristics failed: last error = %u\n", dwErr); pAudioRenderClient->Release(); pAudioClient->Release(); //CoUninitialize(); return 0; } // call Start hr = pAudioClient->Start(); if (FAILED(hr)) { printf("IAudioClient::Start failed: hr = 0x%08x\n", hr); printf("TODO: unregister with MMCSS\n"); AvRevertMmThreadCharacteristics(hTask); pAudioRenderClient->Release(); pAudioClient->Release(); //CoUninitialize(); return 0; } SetEvent(pArgs->hStartedEvent); HANDLE waitArray[2] = { pArgs->hStopEvent, hFeedMe }; DWORD dwWaitResult; bool bDone = false; for (UINT32 nPasses = 0; !bDone; nPasses++) { dwWaitResult = WaitForMultipleObjects( ARRAYSIZE(waitArray), waitArray, FALSE, INFINITE ); if (WAIT_OBJECT_0 == dwWaitResult) { printf("Received stop event after %u passes\n", nPasses); bDone = true; continue; // exits loop } if (WAIT_OBJECT_0 + 1 != dwWaitResult) { hr = E_UNEXPECTED; printf("Unexpected WaitForMultipleObjects return value %u on pass %u\n", dwWaitResult, nPasses); pAudioClient->Stop(); AvRevertMmThreadCharacteristics(hTask); CloseHandle(hFeedMe); pAudioRenderClient->Release(); pAudioClient->Release(); //CoUninitialize(); return 0; } // got "feed me" event - see how much padding there is // // padding is how much of the buffer is currently in use // // note in particular that event-driven (pull-mode) render should not // call GetCurrentPadding multiple times // in a single processing pass // this is in stark contrast to timer-driven (push-mode) render UINT32 nFramesOfPadding; hr = pAudioClient->GetCurrentPadding(&nFramesOfPadding); if (FAILED(hr)) { ShowOutput("IAudioClient::GetCurrentPadding failed on pass %u: hr = 0x%08x\n", nPasses, hr); pAudioClient->Stop(); AvRevertMmThreadCharacteristics(hTask); CloseHandle(hFeedMe); pAudioRenderClient->Release(); pAudioClient->Release(); //CoUninitialize(); return 0; } if (nFramesOfPadding == nFramesInBuffer) { hr = E_UNEXPECTED; ShowOutput("Got \"feed me\" event but IAudioClient::GetCurrentPadding reports buffer is full - glitch?\n"); // he said he ran into ths at least once? /*pAudioClient->Stop(); AvRevertMmThreadCharacteristics(hTask); CloseHandle(hFeedMe); pAudioRenderClient->Release(); pAudioClient->Release(); //CoUninitialize(); return 0; */ } hr = pAudioRenderClient->GetBuffer(nFramesInBuffer - nFramesOfPadding, &pData); if (FAILED(hr)) { printf("IAudioRenderClient::GetBuffer failed on pass %u: hr = 0x%08x - glitch?\n", nPasses, hr); pAudioClient->Stop(); AvRevertMmThreadCharacteristics(hTask); CloseHandle(hFeedMe); pAudioRenderClient->Release(); pAudioClient->Release(); //CoUninitialize(); return 0; } // *** AT THIS POINT *** // If you wanted to render something besides silence, // you would fill the buffer pData // with (nFramesInBuffer - nFramesOfPadding) worth of audio data // this should be in the same wave format // that the stream was initialized with // // In particular, if you didn't want to use the mix format, // you would need to either ask for a different format in IAudioClient::Initialize // or do a format conversion // // If you do, then change the AUDCLNT_BUFFERFLAGS_SILENT flags value below to 0 // release the buffer with the silence flag hr = pAudioRenderClient->ReleaseBuffer(nFramesInBuffer - nFramesOfPadding, AUDCLNT_BUFFERFLAGS_SILENT); if (FAILED(hr)) { printf("IAudioRenderClient::ReleaseBuffer failed on pass %u: hr = 0x%08x - glitch?\n", nPasses, hr); pAudioClient->Stop(); AvRevertMmThreadCharacteristics(hTask); CloseHandle(hFeedMe); pAudioRenderClient->Release(); pAudioClient->Release(); //CoUninitialize(); return 0; } } // for each pass pAudioClient->Stop(); AvRevertMmThreadCharacteristics(hTask); CloseHandle(hFeedMe); pAudioRenderClient->Release(); pAudioClient->Release(); //CoUninitialize(); return 0; } ================================================ FILE: source_code/acam/silence_control.cpp ================================================ #include "stdafx.h" #include #include "silence.h" IMMDevice *m_pMMSilenceDevice; HANDLE hStartedEvent; HANDLE hStopEvent; HANDLE hThread; DWORD WINAPI PlaySilenceThreadFunction(LPVOID pContext); PlaySilenceThreadFunctionArguments threadArgs; HRESULT start_silence_thread() { // create a "silence has started playing" event hStartedEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (NULL == hStartedEvent) { printf("CreateEvent failed: last error is %u\n", GetLastError()); return __LINE__; } // create a "stop playing silence now" event hStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL); if (NULL == hStopEvent) { printf("CreateEvent failed: last error is %u\n", GetLastError()); CloseHandle(hStartedEvent); return __LINE__; } HRESULT hr = get_default_device(&m_pMMSilenceDevice); if (FAILED(hr)) { printf("get_def_device failed: hr = 0x%08x\n", hr); return __LINE__; } // create arguments for silence-playing thread threadArgs.hr = E_UNEXPECTED; // thread will overwrite this threadArgs.hStartedEvent = hStartedEvent; threadArgs.hStopEvent = hStopEvent; threadArgs.pMMDevice = m_pMMSilenceDevice; hThread = CreateThread( NULL, 0, PlaySilenceThreadFunction, &threadArgs, 0, NULL ); if (NULL == hThread) { printf("CreateThread failed: last error is %u\n", GetLastError()); CloseHandle(hStopEvent); CloseHandle(hStartedEvent); return __LINE__; } // wait for either silence to start or the thread to end/abort unexpectedly :| HANDLE waitArray[2] = { hStartedEvent, hThread }; DWORD dwWaitResult; dwWaitResult = WaitForMultipleObjects( ARRAYSIZE(waitArray), waitArray, FALSE, INFINITE ); if (WAIT_OBJECT_0 + 1 == dwWaitResult) { printf("Thread aborted before starting to play silence: hr = 0x%08x\n", threadArgs.hr); CloseHandle(hStartedEvent); CloseHandle(hThread); CloseHandle(hStopEvent); return __LINE__; } if (WAIT_OBJECT_0 != dwWaitResult) { printf("Unexpected WaitForMultipleObjects return value %u: last error is %u\n", dwWaitResult, GetLastError()); CloseHandle(hStartedEvent); CloseHandle(hThread); CloseHandle(hStopEvent); return __LINE__; } CloseHandle(hStartedEvent); return S_OK; } HRESULT join_silence_thread() { SetEvent(hStopEvent); WaitForSingleObject(hThread, INFINITE); // wait for the thread to terminate HANDLE rhHandles[1] = { hThread }; DWORD exitCode; if (!GetExitCodeThread(hThread, &exitCode)) { printf("GetExitCodeThread failed: last error is %u\n", GetLastError()); CloseHandle(hThread); CloseHandle(hStopEvent); return __LINE__; } if (0 != exitCode) { printf("Silence thread exit code is %u; expected 0\n", exitCode); CloseHandle(hThread); CloseHandle(hStopEvent); return __LINE__; } /* if (S_OK != threadArgs.hr) { // didn't care enuf :| printf("Thread HRESULT is 0x%08x\n", threadArgs.hr); CloseHandle(hThread); CloseHandle(hStopEvent); return __LINE__; } */ CloseHandle(hThread); CloseHandle(hStopEvent); ShowOutput("silence thread done success!"); return S_OK; } ================================================ FILE: source_code/acam/stdafx.cpp ================================================ // stdafx.cpp : source file that includes just the standard includes // acam.pch will be the pre-compiled header // stdafx.obj will contain the pre-compiled type information #include "stdafx.h" // TODO: reference any additional headers you need in STDAFX.H // and not in this file ================================================ FILE: source_code/acam/stdafx.h ================================================ // stdafx.h : include file for standard system include files, // or project specific include files that are used frequently, but // are changed infrequently // #pragma once #include "targetver.h" #define WIN32_LEAN_AND_MEAN // Exclude rarely-used stuff from Windows headers // Windows Header Files: #include ================================================ FILE: source_code/acam/targetver.h ================================================ #pragma once // Including SDKDDKVer.h defines the highest available Windows platform. // If you wish to build your application for a previous Windows platform, include WinSDKVer.h and // set the _WIN32_WINNT macro to the platform you wish to support before including SDKDDKVer.h. #include ================================================ FILE: source_code/acam/utilities.cpp ================================================ #include "stdafx.h" #include /* unused ... void logToFile(char *log_this) { FILE *f; f = fopen("g:\\yo2", "a"); fprintf(f, log_this); fclose(f); } */ void ShowOutput(const char *str, ...) { #ifdef _DEBUG // avoid in release mode char buf[2048]; va_list ptr; va_start(ptr,str); vsprintf_s(buf,str,ptr); OutputDebugStringA(buf); OutputDebugStringA("\n"); //printf("%s\n", buf); //logToFile(buf); va_end(ptr); #endif } HRESULT set_config_string_setting(LPCTSTR szValueName, wchar_t *szToThis) { HKEY hKey = NULL; LONG i; DWORD dwDisp = 0; LPDWORD lpdwDisp = &dwDisp; i = RegCreateKeyEx(HKEY_CURRENT_USER, L"SOFTWARE\\virtual_audio_capture", 0L, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS | KEY_WOW64_32KEY, NULL, &hKey, lpdwDisp); // might fail in flash player...? if (i == ERROR_SUCCESS) { // bad for XP ... = RegSetKeyValueA(hKey, NULL, szValueName, REG_SZ, ... i = RegSetValueEx(hKey, szValueName, 0, REG_SZ, (LPBYTE) szToThis, wcslen(szToThis)*2+1); } else { // failed to create key... } if(hKey) RegCloseKey(hKey); return i; } ================================================ FILE: source_code/acam_is_where_all_the_code_is ================================================ ================================================ FILE: source_code/startup_debug_options ================================================ C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\graphedt c:\dev\ruby\virtual-audio-output-sniffer\yo.grf C:\installs\graphstudionext.exe c:\dev\virtual-audio-capture-grabber-device\source_code\yo2.grf C:\Program Files (x86)\VideoLAN\VLC\vlc.exe dshow:// :dshow-vdev=none :dshow-adev="virtual-audio-capturer" :dshow-caching=100 C:\Program Files (x86)\Adobe\Flash Media Live Encoder 3.2\FlashMediaLiveEncoder.exe C:\Program Files (x86)\VideoLAN\VLC\vlc.exe dshow:// :dshow-vdev=none :dshow-adev="virtual-audio-capturer" :dshow-caching=50 C:\Program Files\Microsoft SDKs\Windows\v7.1\Bin\graphedt.exe c:\dev\ruby\sensible-cinema\vendor\cache\ffmpeg\ffmpeg.exe -y -f dshow -i audio=virtual-audio-capturer -t 5 yo.wav c:\vids\ffmpeg.exe -f dshow -i audio=virtual-audio-capturer -y yo.mp4 ================================================ FILE: source_code/synth_deprecated/Synth.vcxproj ================================================  Debug Win32 Debug x64 Release Win32 Release x64 {BF20BC76-A330-4857-830B-80C85660478B} Synth Win32Proj DynamicLibrary Unicode DynamicLibrary Unicode DynamicLibrary Unicode DynamicLibrary Unicode <_ProjectFileVersion>10.0.30319.1 Debug\ Debug\ true Release\ Release\ false $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\ true $(Platform)\$(Configuration)\ $(Platform)\$(Configuration)\ false G:\Program Files\Microsoft SDKs\Windows\v7.1\Samples\multimedia\directshow\baseclasses;$(VCInstallDir)include;$(VCInstallDir)atlmfc\include;$(WindowsSdkDir)include;$(FrameworkSDKDir)\include; $(VCInstallDir)lib;$(VCInstallDir)atlmfc\lib;$(WindowsSdkDir)lib;$(FrameworkSDKDir)\lib;G:\Program Files\Microsoft SDKs\Windows\v7.1\Samples\multimedia\directshow\baseclasses\Debug Disabled ..\..\BaseClasses\;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;SYNTH_EXPORTS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL Level3 EditAndContinue StdCall strmbasd.lib;winmm.lib;msvcrtd.lib;Msacm32.lib;comctl32.lib;%(AdditionalDependencies);Avrt.lib true synth.def true Windows MachineX86 MaxSpeed ..\..\BaseClasses\;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;_USRDLL;SYNTH_EXPORTS;%(PreprocessorDefinitions) MultiThreadedDLL Level3 ProgramDatabase StdCall ..\..\BaseClasses\Release\strmbase.lib;winmm.lib;msvcrt.lib;Msacm32.lib;comctl32.lib;%(AdditionalDependencies) true synth.def true Windows true true MachineX86 X64 Disabled ..\..\BaseClasses\;%(AdditionalIncludeDirectories) WIN32;_DEBUG;_WINDOWS;_USRDLL;SYNTH_EXPORTS;%(PreprocessorDefinitions) true EnableFastChecks MultiThreadedDebugDLL Level3 ProgramDatabase StdCall strmbasd.lib;winmm.lib;msvcrtd.lib;Msacm32.lib;comctl32.lib;%(AdditionalDependencies) ..\..\BaseClasses\x64\Debug\;%(AdditionalLibraryDirectories) true synth.def true Windows MachineX64 X64 MaxSpeed ..\..\BaseClasses\;%(AdditionalIncludeDirectories) WIN32;NDEBUG;_WINDOWS;_USRDLL;SYNTH_EXPORTS;%(PreprocessorDefinitions) MultiThreadedDLL Level3 ProgramDatabase StdCall strmbase.lib;winmm.lib;msvcrt.lib;Msacm32.lib;comctl32.lib;%(AdditionalDependencies) ..\..\BaseClasses\x64\Release\;%(AdditionalLibraryDirectories) true synth.def true Windows true true MachineX64 ================================================ FILE: source_code/synth_deprecated/Synth.vcxproj.filters ================================================  {4FC737F1-C7A5-4376-A066-2A32D752A2FF} cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx {93995380-89BD-4b04-88EB-625FBE52EBFB} h;hpp;hxx;hm;inl;inc;xsd {67DA6AB6-F800-4c08-8B7A-83BB121AAD01} rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav Source Files Source Files Source Files Source Files Source Files Source Files Source Files Header Files Header Files Header Files Header Files Header Files Resource Files ================================================ FILE: source_code/synth_deprecated/Synth.vcxproj.user ================================================  c:\Program Files\VideoLAN\vlc\vlc.exe WindowsLocalDebugger ================================================ FILE: source_code/synth_deprecated/dynsrc.cpp ================================================ //------------------------------------------------------------------------------ // File: DynSrc.cpp // // Desc: DirectShow sample code - implements CDynamicSource, which is a // Quartz source filter. // // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ // I think this is not what I'm looking for, move along now... // Locking Strategy. // // Hold the filter critical section (m_pFilter->pStateLock()) to serialise // access to functions. Note that, in general, this lock may be held // by a function when the worker thread may want to hold it. Therefore // if you wish to access shared state from the worker thread you will // need to add another critical section object. The execption is during // the threads processing loop, when it is safe to get the filter critical // section from within FillBuffer(). // I think we don't have to worry about this one... #include #include #include "DynSrc.h" CDynamicSource::CDynamicSource(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr) : CBaseFilter(pName, lpunk, &m_cStateLock, clsid), m_iPins(0), m_paStreams(NULL), m_evFilterStoppingEvent(TRUE) { ASSERT(phr); // Make sure the event was successfully created. if( NULL == (HANDLE)m_evFilterStoppingEvent ) { if (phr) *phr = E_FAIL; } } #ifdef UNICODE CDynamicSource::CDynamicSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr) : CBaseFilter(pName, lpunk, &m_cStateLock, clsid), m_iPins(0), m_paStreams(NULL), m_evFilterStoppingEvent(TRUE) { ASSERT(phr); // Make sure the event was successfully created. if( NULL == (HANDLE)m_evFilterStoppingEvent ) { if (phr) *phr = E_FAIL; } } #endif // // CDynamicSource::Destructor // CDynamicSource::~CDynamicSource() { /* Free our pins and pin array */ while (m_iPins != 0) { // deleting the pins causes them to be removed from the array... delete m_paStreams[m_iPins - 1]; } ASSERT(m_paStreams == NULL); } // // Add a new pin // HRESULT CDynamicSource::AddPin(CDynamicSourceStream *pStream) { // This function holds the filter state lock because // it changes the number of pins. The number of pins // cannot be change while another thread is calling // IMediaFilter::Run(), IMediaFilter::Stop(), // IMediaFilter::Pause() or IBaseFilter::FindPin(). CAutoLock lock(&m_cStateLock); // This function holds the pin state lock because it // uses m_iPins and m_paStreams. CAutoLock alPinStateLock(&m_csPinStateLock); /* Allocate space for this pin and the old ones */ CDynamicSourceStream **paStreams = new CDynamicSourceStream *[m_iPins + 1]; if (paStreams == NULL) return E_OUTOFMEMORY; if (m_paStreams != NULL) { CopyMemory((PVOID)paStreams, (PVOID)m_paStreams, m_iPins * sizeof(m_paStreams[0])); paStreams[m_iPins] = pStream; delete [] m_paStreams; } m_paStreams = paStreams; m_paStreams[m_iPins] = pStream; m_iPins++; return S_OK; } // // Remove a pin - pStream is NOT deleted // HRESULT CDynamicSource::RemovePin(CDynamicSourceStream *pStream) { // This function holds the filter state lock because // it changes the number of pins. The number of pins // cannot be change while another thread is calling // IMediaFilter::Run(), IMediaFilter::Stop(), // IMediaFilter::Pause() or IBaseFilter::FindPin(). CAutoLock lock(&m_cStateLock); // This function holds the pin state lock because it // uses m_iPins and m_paStreams. CAutoLock alPinStateLock(&m_csPinStateLock); for(int i = 0; i < m_iPins; i++) { if(m_paStreams[i] == pStream) { if(m_iPins == 1) { delete [] m_paStreams; m_paStreams = NULL; } else { /* no need to reallocate */ while(++i < m_iPins) m_paStreams[i - 1] = m_paStreams[i]; } m_iPins--; return S_OK; } } return S_FALSE; } // // FindPin // // Set *ppPin to the IPin* that has the id Id. // or to NULL if the Id cannot be matched. STDMETHODIMP CDynamicSource::FindPin(LPCWSTR Id, IPin **ppPin) { CAutoLock alPinStateLock(&m_csPinStateLock); CheckPointer(ppPin,E_POINTER); ValidateReadWritePtr(ppPin,sizeof(IPin *)); // The -1 undoes the +1 in QueryId and ensures that totally invalid // strings (for which WstrToInt delivers 0) give a deliver a NULL pin. int i = WstrToInt(Id) -1; *ppPin = GetPin(i); if (*ppPin!=NULL) { (*ppPin)->AddRef(); return NOERROR; } return VFW_E_NOT_FOUND; } // // FindPinNumber // // return the number of the pin with this IPin* or -1 if none int CDynamicSource::FindPinNumber(IPin *iPin) { // This function holds the pin state lock because it // uses m_iPins and m_paStreams. CAutoLock alPinStateLock(&m_csPinStateLock); for (int i=0; in && n>=0 it follows that m_iPins>0 // which is what used to be checked (i.e. checking that we have a pin) if((n >= 0) && (n < m_iPins)) { ASSERT(m_paStreams[n]); return m_paStreams[n]; } return NULL; } STDMETHODIMP CDynamicSource::Stop(void) { m_evFilterStoppingEvent.Set(); HRESULT hr = CBaseFilter::Stop(); // The following code ensures that a pins thread will be destroyed even // if the pin is disconnected when CBaseFilter::Stop() is called. int nCurrentPin; CDynamicSourceStream* pOutputPin; { // This code holds the pin state lock because it // does not want the number of pins to change // while it executes. CAutoLock alPinStateLock(&m_csPinStateLock); for(nCurrentPin = 0; nCurrentPin < GetPinCount(); nCurrentPin++) { pOutputPin = (CDynamicSourceStream*)GetPin(nCurrentPin); if(pOutputPin->ThreadExists()) { pOutputPin->DestroySourceThread(); } } } if(FAILED(hr)) { return hr; } return NOERROR; } STDMETHODIMP CDynamicSource::Pause(void) { m_evFilterStoppingEvent.Reset(); return CBaseFilter::Pause(); } STDMETHODIMP CDynamicSource::JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR pName) { CAutoLock lock(&m_cStateLock); HRESULT hr; int nCurrentPin; CDynamicSourceStream* pOutputPin; // The filter is joining the filter graph. if(NULL != pGraph) { IGraphConfig* pGraphConfig = NULL; hr = pGraph->QueryInterface(IID_IGraphConfig, (void**)&pGraphConfig); if(FAILED(hr)) { return hr; } hr = CBaseFilter::JoinFilterGraph(pGraph, pName); if(FAILED(hr)) { pGraphConfig->Release(); return hr; } for(nCurrentPin = 0; nCurrentPin < GetPinCount(); nCurrentPin++) { pOutputPin = (CDynamicSourceStream*) GetPin(nCurrentPin); pOutputPin->SetConfigInfo(pGraphConfig, m_evFilterStoppingEvent); } pGraphConfig->Release(); } else { hr = CBaseFilter::JoinFilterGraph(pGraph, pName); if(FAILED(hr)) { return hr; } for(nCurrentPin = 0; nCurrentPin < GetPinCount(); nCurrentPin++) { pOutputPin = (CDynamicSourceStream*)GetPin(nCurrentPin); pOutputPin->SetConfigInfo(NULL, NULL); } } return S_OK; } // * // * --- CDynamicSourceStream ---- // * // // Set Id to point to a CoTaskMemAlloc'd STDMETHODIMP CDynamicSourceStream::QueryId(LPWSTR *pId) { CheckPointer(pId,E_POINTER); ValidateReadWritePtr(pId,sizeof(LPWSTR)); // We give the pins id's which are 1,2,... // FindPinNumber returns -1 for an invalid pin int i = 1+ m_pFilter->FindPinNumber(this); if(i<1) return VFW_E_NOT_FOUND; *pId = (LPWSTR)CoTaskMemAlloc(4*sizeof(WCHAR)); if(*pId==NULL) { return E_OUTOFMEMORY; } IntToWstr(i, *pId); return NOERROR; } // // CDynamicSourceStream::Constructor // // increments the number of pins present on the filter CDynamicSourceStream::CDynamicSourceStream( TCHAR *pObjectName, HRESULT *phr, CDynamicSource*ps, LPCWSTR pPinName) : CDynamicOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName), m_pFilter(ps), m_fReconnectOutputPin(false) { ASSERT(phr); *phr = m_pFilter->AddPin(this); } #ifdef UNICODE CDynamicSourceStream::CDynamicSourceStream( char *pObjectName, HRESULT *phr, CDynamicSource*ps, LPCWSTR pPinName) : CDynamicOutputPin(pObjectName, ps, ps->pStateLock(), phr, pPinName), m_pFilter(ps), m_fReconnectOutputPin(false) { ASSERT(phr); *phr = m_pFilter->AddPin(this); } #endif // // CDynamicSourceStream::Destructor // // Decrements the number of pins on this filter CDynamicSourceStream::~CDynamicSourceStream(void) { m_pFilter->RemovePin(this); } // // CheckMediaType // // Do we support this type? Provides the default support for 1 type. HRESULT CDynamicSourceStream::CheckMediaType(const CMediaType *pMediaType) { CheckPointer(pMediaType,E_POINTER); CAutoLock lock(m_pFilter->pStateLock()); CMediaType mt; GetMediaType(&mt); if(mt == *pMediaType) { return NOERROR; } return E_FAIL; } // // GetMediaType // // By default we support only one type // iPosition indexes are 0-n HRESULT CDynamicSourceStream::GetMediaType(int iPosition, CMediaType *pMediaType) { CAutoLock lock(m_pFilter->pStateLock()); if(iPosition<0) { return E_INVALIDARG; } if(iPosition>0) { return VFW_S_NO_MORE_ITEMS; } return GetMediaType(pMediaType); } // // Active // // The pin is active - start up the worker thread HRESULT CDynamicSourceStream::Active(void) { CAutoLock lock(m_pFilter->pStateLock()); HRESULT hr; if(m_pFilter->IsActive()) { return S_FALSE; // succeeded, but did not allocate resources (they already exist...) } // do nothing if not connected - its ok not to connect to // all pins of a source filter if(!IsConnected()) { return NOERROR; } hr = CDynamicOutputPin::Active(); if(FAILED(hr)) { return hr; } ASSERT(!ThreadExists()); // start the thread if(!Create()) { return E_FAIL; } // Tell thread to initialize. If OnThreadCreate Fails, so does this. hr = Init(); if(FAILED(hr)) return hr; return Pause(); } HRESULT CDynamicSourceStream::BreakConnect(void) { HRESULT hr = CDynamicOutputPin::BreakConnect(); if(FAILED(hr)) { return hr; } m_fReconnectOutputPin = false; return S_OK; } HRESULT CDynamicSourceStream::DestroySourceThread(void) { // The pin's thread cannot be destroyed if the thread does not exist. ASSERT(ThreadExists()); HRESULT hr = Stop(); if(FAILED(hr)) { return hr; } hr = Exit(); if(FAILED(hr)) { return hr; } Close(); // Wait for the thread to exit, then tidy up. return NOERROR; } // // ThreadProc // // When this returns the thread exits // Return codes > 0 indicate an error occured DWORD CDynamicSourceStream::ThreadProc(void) { HRESULT hr; // the return code from calls Command com; do { com = GetRequest(); if(com != CMD_INIT) { DbgLog((LOG_ERROR, 1, TEXT("Thread expected init command"))); Reply((DWORD) E_UNEXPECTED); } } while(com != CMD_INIT); DbgLog((LOG_TRACE, 1, TEXT("CDynamicSourceStream worker thread initializing"))); hr = OnThreadCreate(); // perform set up tasks if(FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("CDynamicSourceStream::OnThreadCreate failed. Aborting thread."))); OnThreadDestroy(); Reply(hr); // send failed return code from OnThreadCreate return 1; } // Initialisation suceeded Reply(NOERROR); Command cmd; do { cmd = GetRequest(); switch(cmd) { case CMD_EXIT: Reply(NOERROR); break; case CMD_RUN: DbgLog((LOG_ERROR, 1, TEXT("CMD_RUN received before a CMD_PAUSE???"))); // !!! fall through case CMD_PAUSE: Reply(NOERROR); DoBufferProcessingLoop(); break; case CMD_STOP: Reply(NOERROR); break; default: DbgLog((LOG_ERROR, 1, TEXT("Unknown command %d received!"), cmd)); Reply((DWORD) E_NOTIMPL); break; } } while(cmd != CMD_EXIT); hr = OnThreadDestroy(); // tidy up. if(FAILED(hr)) { DbgLog((LOG_ERROR, 1, TEXT("CDynamicSourceStream::OnThreadDestroy failed. Exiting thread."))); return 1; } DbgLog((LOG_TRACE, 1, TEXT("CDynamicSourceStream worker thread exiting"))); return 0; } // // DoBufferProcessingLoop // // Grabs a buffer and calls the users processing function. // Overridable, so that different delivery styles can be catered for. HRESULT CDynamicSourceStream::DoBufferProcessingLoop(void) { Command com; bool fOutputFormatChanged = false; OnThreadStartPlay(); do { while(!CheckRequest(&com)) { // CAutoUsingOutputPin::CAutoUsingOutputPin() only changes the value of hr // if an error occurs. HRESULT hr = S_OK; CAutoUsingOutputPin auopUsingOutputPin(this, &hr); if(FAILED(hr)) { FatalError(hr); return hr; } if(m_fReconnectOutputPin) { hr = DynamicReconnect(NULL); m_fReconnectOutputPin = false; if(FAILED(hr)) { FatalError(hr); return hr; } fOutputFormatChanged = true; } IMediaSample *pSample; hr = GetDeliveryBuffer(&pSample,NULL,NULL,0); if(FAILED(hr)) { Sleep(1); continue; // go round again. Perhaps the error will go away // or the allocator is decommited & we will be asked to // exit soon. } if(fOutputFormatChanged) { pSample->SetDiscontinuity(TRUE); fOutputFormatChanged = false; } // Virtual function user will override. hr = FillBuffer(pSample); if(hr == S_OK) { hr = Deliver(pSample); pSample->Release(); // downstream filter returns S_FALSE if it wants us to // stop or an error if it's reporting an error. if(hr != S_OK) { DbgLog((LOG_TRACE, 2, TEXT("Deliver() returned %08x; stopping"), hr)); return S_OK; } } else if(hr == S_FALSE) { // derived class wants us to stop pushing data pSample->Release(); DeliverEndOfStream(); return S_OK; } else { // derived class encountered an error pSample->Release(); DbgLog((LOG_ERROR, 1, TEXT("Error %08lX from FillBuffer!!!"), hr)); FatalError(hr); return hr; } // all paths release the sample } // For all commands sent to us there must be a Reply call! if(com == CMD_RUN || com == CMD_PAUSE) { Reply(NOERROR); } else if(com != CMD_STOP) { Reply((DWORD) E_UNEXPECTED); DbgLog((LOG_ERROR, 1, TEXT("Unexpected command!!!"))); } } while(com != CMD_STOP); return S_FALSE; } void CDynamicSourceStream::FatalError(HRESULT hr) { // Make sure the user is reporting a failure. ASSERT(FAILED(hr)); m_bRunTimeError = TRUE; DeliverEndOfStream(); m_pFilter->NotifyEvent(EC_ERRORABORT, hr, 0); } void CDynamicSourceStream::OutputPinNeedsToBeReconnected(void) { m_fReconnectOutputPin = true; } ================================================ FILE: source_code/synth_deprecated/dynsrc.h ================================================ //------------------------------------------------------------------------------ // File: DynSrc.h // // Desc: DirectShow sample code - defines classes to simplify creation of // ActiveX source filters that support continuous generation of data. // No support is provided for IMediaControl or IMediaPosition. // // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ // // Derive your source filter from CDynamicSource. // During construction either: // Create some CDynamicSourceStream objects to manage your pins // Provide the user with a means of doing so eg, an IPersistFile interface. // // CDynamicSource provides: // IBaseFilter interface management // IMediaFilter interface management, via CBaseFilter // Pin counting for CBaseFilter // // Derive a class from CDynamicSourceStream to manage your output pin types // Implement GetMediaType/1 to return the type you support. If you support multiple // types then overide GetMediaType/3, CheckMediaType and GetMediaTypeCount. // Implement Fillbuffer() to put data into one buffer. // // CDynamicSourceStream provides: // IPin management via CDynamicOutputPin // Worker thread management #ifndef __CDYNAMICSOURCE__ #define __CDYNAMICSOURCE__ class CDynamicSourceStream; // The class that will handle each pin // // CDynamicSource // // Override construction to provide a means of creating // CDynamicSourceStream derived objects - ie a way of creating pins. class CDynamicSource: public CBaseFilter { public: CDynamicSource(TCHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr); #ifdef UNICODE CDynamicSource(CHAR *pName, LPUNKNOWN lpunk, CLSID clsid, HRESULT *phr); #endif ~CDynamicSource(); int GetPinCount(void); CBasePin *GetPin(int n); // -- Utilities -- CCritSec* pStateLock(void) { return &m_cStateLock; } // provide our critical section HRESULT AddPin(CDynamicSourceStream *); HRESULT RemovePin(CDynamicSourceStream *); STDMETHODIMP FindPin( LPCWSTR Id, IPin ** ppPin ); int FindPinNumber(IPin *iPin); STDMETHODIMP JoinFilterGraph(IFilterGraph* pGraph, LPCWSTR pName); STDMETHODIMP Stop(void); STDMETHODIMP Pause(void); protected: CAMEvent m_evFilterStoppingEvent; int m_iPins; // The number of pins on this filter. Updated by // CDynamicSourceStream constructors & destructors. CDynamicSourceStream **m_paStreams; // the pins on this filter. // This lock must be held when m_paStreams or m_iPins // is being used. The state lock (m_cStateLock) must // also be held if the program wants to change the value // of m_paStreams or m_iPins. Functions cannot acquire // the state lock (m_cStateLock) after they acquire // m_csPinStateLock. The program will deadlock if it // violates this rule. Also the program may not acquire // m_csPinStateLock on the streaming thread. If it does, // the program will deadlock. The streaming thread calls // ThreadProc() and DoBufferProcessingLoop(). CCritSec m_csPinStateLock; // This lock serializes accesses to the filter's state. // It also must be held when the program changes // m_iPins's or m_paStreams's value. CCritSec m_cStateLock; }; // // CDynamicSourceStream // // Use this class to manage a stream of data that comes from a // pin. // Uses a worker thread to put data on the pin. class CDynamicSourceStream : public CAMThread, public CDynamicOutputPin { public: CDynamicSourceStream(TCHAR *pObjectName, HRESULT *phr, CDynamicSource*pms, LPCWSTR pName); #ifdef UNICODE CDynamicSourceStream(CHAR *pObjectName, HRESULT *phr, CDynamicSource*pms, LPCWSTR pName); #endif virtual ~CDynamicSourceStream(void); // virtual destructor ensures derived // class destructors are called too HRESULT DestroySourceThread(void); protected: CDynamicSource*m_pFilter; // The parent of this stream // * // * Data Source // * // * The following three functions: FillBuffer, OnThreadCreate/Destroy, are // * called from within the ThreadProc. They are used in the creation of // * the media samples this pin will provide // * // Override this to provide the worker thread a means // of processing a buffer virtual HRESULT FillBuffer(IMediaSample *pSamp) PURE; // Called as the thread is created/destroyed - use to perform // jobs such as start/stop streaming mode // If OnThreadCreate returns an error the thread will exit. virtual HRESULT OnThreadCreate(void) {return NOERROR;}; virtual HRESULT OnThreadDestroy(void) {return NOERROR;}; virtual HRESULT OnThreadStartPlay(void) {return NOERROR;}; // * // * Worker Thread // * HRESULT Active(void); // Starts up the worker thread HRESULT BreakConnect(void); public: // thread commands enum Command {CMD_INIT, CMD_PAUSE, CMD_RUN, CMD_STOP, CMD_EXIT}; HRESULT Init(void) { return CallWorker(CMD_INIT); } HRESULT Exit(void) { return CallWorker(CMD_EXIT); } HRESULT Run(void) { return CallWorker(CMD_RUN); } HRESULT Pause(void) { return CallWorker(CMD_PAUSE); } HRESULT Stop(void) { return CallWorker(CMD_STOP); } void OutputPinNeedsToBeReconnected(void); protected: Command GetRequest(void) { return (Command) CAMThread::GetRequest(); } BOOL CheckRequest(Command *pCom) { return CAMThread::CheckRequest( (DWORD *) pCom); } // override these if you want to add thread commands virtual DWORD ThreadProc(void); // the thread function virtual HRESULT DoBufferProcessingLoop(void); // the loop executed whilst running void FatalError(HRESULT hr); // * // * AM_MEDIA_TYPE support // * // If you support more than one media type then override these 2 functions virtual HRESULT CheckMediaType(const CMediaType *pMediaType); virtual HRESULT GetMediaType(int iPosition, CMediaType *pMediaType); // List pos. 0-n // If you support only one type then override this fn. // This will only be called by the default implementations // of CheckMediaType and GetMediaType(int, CMediaType*) // You must override this fn. or the above 2! virtual HRESULT GetMediaType(CMediaType *pMediaType) {return E_UNEXPECTED;} STDMETHODIMP QueryId( LPWSTR * Id ); bool m_fReconnectOutputPin; }; #endif // __CDYNAMICSOURCE__ ================================================ FILE: source_code/synth_deprecated/isynth.h ================================================ //------------------------------------------------------------------------------ // File: ISynth.h // // Desc: DirectShow sample code - custom interface to allow the user to // adjust the frequency. // // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ #ifndef __ISYNTH2__ #define __ISYNTH2__ #ifdef __cplusplus extern "C" { #endif // // ISynth2's GUID // // {00487A78-D875-44b0-ADBB-DECA9CDB51FC} DEFINE_GUID(IID_ISynth2, 0x487a78, 0xd875, 0x44b0, 0xad, 0xbb, 0xde, 0xca, 0x9c, 0xdb, 0x51, 0xfc); enum SYNTH_OUTPUT_FORMAT { SYNTH_OF_PCM, SYNTH_OF_MS_ADPCM }; // // ISynth2 // DECLARE_INTERFACE_(ISynth2, IUnknown) { STDMETHOD(get_Frequency) (THIS_ int *Frequency /* [out] */ // the current frequency ) PURE; STDMETHOD(put_Frequency) (THIS_ int Frequency /* [in] */ // Change to this frequency ) PURE; STDMETHOD(get_Waveform) (THIS_ int *Waveform /* [out] */ // the current Waveform ) PURE; STDMETHOD(put_Waveform) (THIS_ int Waveform /* [in] */ // Change to this Waveform ) PURE; STDMETHOD(get_Channels) (THIS_ int *Channels /* [out] */ // the current Channels ) PURE; STDMETHOD(put_Channels) (THIS_ int Channels /* [in] */ // Change to this Channels ) PURE; STDMETHOD(get_BitsPerSample) (THIS_ int *BitsPerSample /* [out] */ // the current BitsPerSample ) PURE; STDMETHOD(put_BitsPerSample) (THIS_ int BitsPerSample /* [in] */ // Change to this BitsPerSample ) PURE; STDMETHOD(get_SamplesPerSec) (THIS_ int *SamplesPerSec /* [out] */ // the current SamplesPerSec ) PURE; STDMETHOD(put_SamplesPerSec) (THIS_ int SamplesPerSec /* [in] */ // Change to this SamplesPerSec ) PURE; STDMETHOD(get_Amplitude) (THIS_ int *Amplitude /* [out] */ // the current Amplitude ) PURE; STDMETHOD(put_Amplitude) (THIS_ int Amplitude /* [in] */ // Change to this Amplitude ) PURE; STDMETHOD(get_SweepRange) (THIS_ int *SweepStart, /* [out] */ int *SweepEnd /* [out] */ ) PURE; STDMETHOD(put_SweepRange) (THIS_ int SweepStart, /* [in] */ int SweepEnd /* [in] */ ) PURE; STDMETHOD(get_OutputFormat) (THIS_ SYNTH_OUTPUT_FORMAT *pOutputFormat /* [out] */ ) PURE; STDMETHOD(put_OutputFormat) (THIS_ SYNTH_OUTPUT_FORMAT ofNewOutputFormat /* [out] */ ) PURE; }; #ifdef __cplusplus } #endif #endif // __ISYNTH2__ ================================================ FILE: source_code/synth_deprecated/loopback-capture-helpers.cpp ================================================ #include #include #include #include #include #include #include HRESULT open_file(LPCWSTR szFileName, HMMIO *phFile) { MMIOINFO mi = {0}; *phFile = mmioOpen( // some flags cause mmioOpen write to this buffer // but not any that we're using const_cast(szFileName), &mi, MMIO_WRITE | MMIO_CREATE ); if (NULL == *phFile) { printf("mmioOpen(\"%ls\", ...) failed. wErrorRet == %u\n", szFileName, mi.wErrorRet); return E_FAIL; } return S_OK; } HRESULT get_default_device(IMMDevice **ppMMDevice) { HRESULT hr = S_OK; IMMDeviceEnumerator *pMMDeviceEnumerator; // activate a device enumerator hr = CoCreateInstance( __uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL, __uuidof(IMMDeviceEnumerator), (void**)&pMMDeviceEnumerator ); if (FAILED(hr)) { printf("CoCreateInstance(IMMDeviceEnumerator) failed: hr = 0x%08x\n", hr); return hr; } // get the default render endpoint hr = pMMDeviceEnumerator->GetDefaultAudioEndpoint(eRender, eConsole, ppMMDevice); pMMDeviceEnumerator->Release(); if (FAILED(hr)) { printf("IMMDeviceEnumerator::GetDefaultAudioEndpoint failed: hr = 0x%08x\n", hr); return hr; } return S_OK; } ================================================ FILE: source_code/synth_deprecated/loopback-capture.cpp ================================================ #include #include #include #include #include #include HRESULT open_file(LPCWSTR szFileName, HMMIO *phFile); HRESULT get_default_device(IMMDevice **ppMMDevice); // size is size of the BYTE buffer...but...I guess...we just have to fill it all the way with data...I guess... HRESULT LoopbackCapture(const WAVEFORMATEX& wfex, BYTE pBuf[], int iSize, WAVEFORMATEX* ifNotNullThenJustSetTypeOnly) { bool bInt16 = true; // makes it actually work, for some reason... UINT32 pnFrames = 0; HRESULT hr; IMMDevice *m_pMMDevice; hr = get_default_device(&m_pMMDevice); // so it can re-place our pointer... if (FAILED(hr)) { return hr; } // activate an (the default, for us) IAudioClient IAudioClient *pAudioClient; hr = m_pMMDevice->Activate( __uuidof(IAudioClient), CLSCTX_ALL, NULL, (void**)&pAudioClient ); if (FAILED(hr)) { printf("IMMDevice::Activate(IAudioClient) failed: hr = 0x%08x", hr); return hr; } // get the default device periodicity REFERENCE_TIME hnsDefaultDevicePeriod; hr = pAudioClient->GetDevicePeriod(&hnsDefaultDevicePeriod, NULL); if (FAILED(hr)) { printf("IAudioClient::GetDevicePeriod failed: hr = 0x%08x\n", hr); pAudioClient->Release(); return hr; } // get the default device format (incoming...) WAVEFORMATEX *pwfx; // incoming wave... hr = pAudioClient->GetMixFormat(&pwfx); if (FAILED(hr)) { printf("IAudioClient::GetMixFormat failed: hr = 0x%08x\n", hr); CoTaskMemFree(pwfx); pAudioClient->Release(); return hr; } if (bInt16) { // coerce int-16 wave format // can do this in-place since we're not changing the size of the format // also, the engine will auto-convert from float to int for us switch (pwfx->wFormatTag) { case WAVE_FORMAT_IEEE_FLOAT: pwfx->wFormatTag = WAVE_FORMAT_PCM; pwfx->wBitsPerSample = 16; pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8; pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; break; case WAVE_FORMAT_EXTENSIBLE: { // naked scope for case-local variable PWAVEFORMATEXTENSIBLE pEx = reinterpret_cast(pwfx); if (IsEqualGUID(KSDATAFORMAT_SUBTYPE_IEEE_FLOAT, pEx->SubFormat)) { // WE GET HERE! pEx->SubFormat = KSDATAFORMAT_SUBTYPE_PCM; pEx->Samples.wValidBitsPerSample = 16; pwfx->wBitsPerSample = 16; pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8; pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; } else { printf("Don't know how to coerce mix format to int-16\n"); CoTaskMemFree(pwfx); pAudioClient->Release(); return E_UNEXPECTED; } } break; default: printf("Don't know how to coerce WAVEFORMATEX with wFormatTag = 0x%08x to int-16\n", pwfx->wFormatTag); CoTaskMemFree(pwfx); pAudioClient->Release(); return E_UNEXPECTED; } } if(ifNotNullThenJustSetTypeOnly) { // pwfx is set at this point... WAVEFORMATEX* pwfex = ifNotNullThenJustSetTypeOnly; // copy them all out as the possible format...hmm... pwfx->wFormatTag = WAVE_FORMAT_PCM; pwfx->wBitsPerSample = 16; pwfx->nBlockAlign = pwfx->nChannels * pwfx->wBitsPerSample / 8; pwfx->nAvgBytesPerSec = pwfx->nBlockAlign * pwfx->nSamplesPerSec; pwfex->wFormatTag = pwfx->wFormatTag; pwfex->nChannels = pwfx->nChannels; pwfex->nSamplesPerSec = pwfx->nSamplesPerSec; pwfex->wBitsPerSample = pwfx->wBitsPerSample; pwfex->nBlockAlign = pwfx->nBlockAlign; pwfex->nAvgBytesPerSec = pwfx->nAvgBytesPerSec; pwfex->cbSize = pwfx->cbSize; //FILE *fp = fopen("/normal2", "w"); // fails on me? maybe juts a VLC thing... //fprintf(fp, "hello world %d %d %d %d %d %d %d", pwfex->wFormatTag, pwfex->nChannels, // pwfex->nSamplesPerSec, pwfex->wBitsPerSample, pwfex->nBlockAlign, pwfex->nAvgBytesPerSec, pwfex->cbSize ); //fclose(fp); // cleanup // I might be leaking here... m_pMMDevice->Release(); return hr; } MMCKINFO ckRIFF = {0}; MMCKINFO ckData = {0}; // create a periodic waitable timer UINT32 nBlockAlign = pwfx->nBlockAlign; // call IAudioClient::Initialize // note that AUDCLNT_STREAMFLAGS_LOOPBACK and AUDCLNT_STREAMFLAGS_EVENTCALLBACK // do not work together... // the "data ready" event never gets set // so we're going to do a timer-driven loop... hr = pAudioClient->Initialize( AUDCLNT_SHAREMODE_SHARED, AUDCLNT_STREAMFLAGS_LOOPBACK, 0, 0, pwfx, 0 ); if (FAILED(hr)) { printf("IAudioClient::Initialize failed: hr = 0x%08x\n", hr); pAudioClient->Release(); return hr; } CoTaskMemFree(pwfx); // activate an IAudioCaptureClient IAudioCaptureClient *pAudioCaptureClient; hr = pAudioClient->GetService( __uuidof(IAudioCaptureClient), (void**)&pAudioCaptureClient ); if (FAILED(hr)) { printf("IAudioClient::GetService(IAudioCaptureClient) failed: hr 0x%08x\n", hr); //CloseHandle(hWakeUp); pAudioClient->Release(); return hr; } // register with MMCSS DWORD nTaskIndex = 0; HANDLE hTask = AvSetMmThreadCharacteristics(L"Capture", &nTaskIndex); if (NULL == hTask) { DWORD dwErr = GetLastError(); printf("AvSetMmThreadCharacteristics failed: last error = %u\n", dwErr); pAudioCaptureClient->Release(); //CloseHandle(hWakeUp); pAudioClient->Release(); return HRESULT_FROM_WIN32(dwErr); } // call IAudioClient::Start hr = pAudioClient->Start(); if (FAILED(hr)) { printf("IAudioClient::Start failed: hr = 0x%08x\n", hr); AvRevertMmThreadCharacteristics(hTask); pAudioCaptureClient->Release(); pAudioClient->Release(); return hr; } bool bDone = false; bool bFirstPacket = true; // loop forever until bDone is set by the keyboard for (UINT32 nBitsWrote = 0; nBitsWrote < iSize; ) { // TODO sleep until there is data available [?] or can it poll me... [lodo] UINT32 nNextPacketSize; hr = pAudioCaptureClient->GetNextPacketSize(&nNextPacketSize); if (FAILED(hr)) { printf("IAudioCaptureClient::GetNextPacketSize failed on pass %u after %u frames: hr = 0x%08x\n", nBitsWrote, pnFrames, hr); pAudioClient->Stop(); AvRevertMmThreadCharacteristics(hTask); pAudioCaptureClient->Release(); pAudioClient->Release(); return hr; } if (0 == nNextPacketSize) { // no data yet Sleep(0);// LODO (?) continue; } // get the captured data BYTE *pData; UINT32 nNumFramesToRead; DWORD dwFlags; // I guess it gives us...umm...as much as possible? hr = pAudioCaptureClient->GetBuffer( &pData, &nNumFramesToRead, &dwFlags, NULL, NULL ); // ACTUALLY GET THE BUFFER which I assume it reads in the format of the fella we passed in // so...it reads nNumFrames and calls it good or what? if (FAILED(hr)) { printf("IAudioCaptureClient::GetBuffer failed on pass %u after %u frames: hr = 0x%08x\n", nBitsWrote, pnFrames, hr); pAudioClient->Stop(); AvRevertMmThreadCharacteristics(hTask); pAudioCaptureClient->Release(); pAudioClient->Release(); return hr; } if (bFirstPacket && AUDCLNT_BUFFERFLAGS_DATA_DISCONTINUITY == dwFlags) { printf("Probably spurious glitch reported on first packet\n"); } else if (0 != dwFlags) { printf("IAudioCaptureClient::GetBuffer set flags to 0x%08x on pass %u after %u frames\n", dwFlags, nBitsWrote, pnFrames); pAudioClient->Stop(); AvRevertMmThreadCharacteristics(hTask); pAudioCaptureClient->Release(); pAudioClient->Release(); return E_UNEXPECTED; } if (0 == nNumFramesToRead) { printf("IAudioCaptureClient::GetBuffer said to read 0 frames on pass %u after %u frames\n", nBitsWrote, pnFrames); pAudioClient->Stop(); AvRevertMmThreadCharacteristics(hTask); pAudioCaptureClient->Release(); pAudioClient->Release(); return E_UNEXPECTED; } else { pnFrames += nNumFramesToRead; // increment total count... } LONG lBytesToWrite = nNumFramesToRead * nBlockAlign; #pragma prefast(suppress: __WARNING_INCORRECT_ANNOTATION, "IAudioCaptureClient::GetBuffer SAL annotation implies a 1-byte buffer") // TODO WRITE TO OUTGOING [?] for(int i = 0; i < lBytesToWrite && nBitsWrote < iSize;i++) { pBuf[nBitsWrote++] = pData[i]; // lodo use a straight call... } hr = pAudioCaptureClient->ReleaseBuffer(nNumFramesToRead); if (FAILED(hr)) { printf("IAudioCaptureClient::ReleaseBuffer failed on pass %u after %u frames: hr = 0x%08x\n", nBitsWrote, pnFrames, hr); pAudioClient->Stop(); AvRevertMmThreadCharacteristics(hTask); pAudioCaptureClient->Release(); pAudioClient->Release(); return hr; } bFirstPacket = false; } // capture loop... pAudioClient->Stop(); AvRevertMmThreadCharacteristics(hTask); pAudioCaptureClient->Release(); pAudioClient->Release(); m_pMMDevice->Release(); return hr; } ================================================ FILE: source_code/synth_deprecated/resource.h ================================================ // // Microsoft Visual C++ generated include file. // Used by synth.rc // #define IDD_BALLPROP 101 #define IDD_SYNTHPROP1 101 #define IDC_FREQUENCYTEXT 1002 #define IDC_FREQUENCY 1003 #define IDC_AMPLITUDETEXT 1003 #define IDC_FREQTRACKBAR 1004 #define IDC_FREQUENCYGROUP 1005 #define IDC_WAVEFORMGROUP 1006 #define IDC_WAVESINE 1007 #define IDC_WAVESQUARE 1008 #define IDC_WAVESAWTOOTH 1009 #define IDC_WAVESWEEP 1010 #define IDC_AMPLITUDETRACKBAR 1011 #define IDC_CHANNELS1 1012 #define IDC_CHANNELS2 1013 #define IDC_BITSPERSAMPLEGROUP 1014 #define IDC_BITSPERSAMPLE8 1015 #define IDC_BITSPERSAMPLE16 1016 #define IDC_SAMPLINGFREQUENCYGROUP 1017 #define IDC_SAMPLINGFREQ11 1018 #define IDC_SAMPLINGFREQ22 1019 #define IDC_SAMPLINGFREQ44 1020 #define IDC_AMPLITUDEGROUP 1021 #define IDC_SWEEP 1023 #define IDS_SYNTHPROPNAME 1024 #define IDC_OUTPUTFORMAT 1025 #define IDC_OF_PCM 1026 #define IDC_OF_MSADPCM 1027 #define IDS_STATIC -1 #define IDC_CHANNELSGROUP -1 // Next default values for new objects // #ifdef APSTUDIO_INVOKED #ifndef APSTUDIO_READONLY_SYMBOLS #define _APS_NO_MFC 1 #define _APS_3D_CONTROLS 1 #define _APS_NEXT_RESOURCE_VALUE 102 #define _APS_NEXT_COMMAND_VALUE 40001 #define _APS_NEXT_CONTROL_VALUE 1027 #define _APS_NEXT_SYMED_VALUE 101 #endif #endif ================================================ FILE: source_code/synth_deprecated/synth.cpp ================================================ //------------------------------------------------------------------------------ // File: Synth.cpp // // Desc: DirectShow sample code - implements an audio signal generator // source filter. // // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ // NB I don't know if directsource *can* actually work with VLC // I doub it //I.e. this particular sub-project fella prolly can't work as a VLC source #include #include #include #include #include #include #include #if (1100 > _MSC_VER) #include #else #include #endif #define _AUDIOSYNTH_IMPLEMENTATION_ #include "DynSrc.h" #include "isynth.h" #include "synth.h" #include "synthprp.h" #include // setup data const AMOVIESETUP_MEDIATYPE sudOpPinTypes = { &MEDIATYPE_Audio // clsMajorType , &MEDIASUBTYPE_NULL }; // clsMinorType const AMOVIESETUP_PIN sudOpPin = { L"Output" // strName , FALSE // bRendered , TRUE // bOutput , FALSE // bZero , FALSE // bMany , &CLSID_NULL // clsConnectsToFilter , L"Input" // strConnectsToPin , 1 // nTypes , &sudOpPinTypes }; // lpTypes const AMOVIESETUP_FILTER sudSynth = { &CLSID_SynthFilter // clsID , L"Audio Synthesizer" // strName , MERIT_UNLIKELY // dwMerit , 1 // nPins , &sudOpPin }; // lpPin // ------------------------------------------------------------------------- // g_Templates // ------------------------------------------------------------------------- // COM global table of objects in this dll CFactoryTemplate g_Templates[] = { { L"Audio Synthesizer" , &CLSID_SynthFilter , CSynthFilter::CreateInstance , NULL , &sudSynth } , { L"Audio Synthesizer Property Page" , &CLSID_SynthPropertyPage , CSynthProperties::CreateInstance } }; int g_cTemplates = sizeof(g_Templates) / sizeof(g_Templates[0]); // ------------------------------------------------------------------------- // CSynthFilter, the main filter object // ------------------------------------------------------------------------- // // CreateInstance // // The only allowed way to create Synthesizers CUnknown * WINAPI CSynthFilter::CreateInstance(LPUNKNOWN lpunk, HRESULT *phr) { ASSERT(phr); CUnknown *punk = new CSynthFilter(lpunk, phr); if (punk == NULL) { if (phr) *phr = E_OUTOFMEMORY; } return punk; } // // CSynthFilter::Constructor // // initialise a CSynthStream object so that we have a pin. CSynthFilter::CSynthFilter(LPUNKNOWN lpunk, HRESULT *phr) : CDynamicSource(NAME("Audio Synthesizer Filter"),lpunk, CLSID_SynthFilter, phr) , CPersistStream(lpunk, phr) { m_paStreams = (CDynamicSourceStream **) new CSynthStream*[1]; if (m_paStreams == NULL) { if (phr) *phr = E_OUTOFMEMORY; return; } m_paStreams[0] = new CSynthStream(phr, this, L"Audio Synth Stream"); if (m_paStreams[0] == NULL) { if (phr) *phr = E_OUTOFMEMORY; return; } if (SUCCEEDED(*phr)) { ASSERT(m_Synth); m_Synth->put_SynthFormat(1, // Channels 8, // Bits Per Sample 11025 // Samples Per Sececond ); } } STDMETHODIMP CSynthFilter::GetClassID(CLSID *pClsid) { return CBaseFilter::GetClassID(pClsid); } int CSynthFilter::SizeMax () { return sizeof (int) * 8; } DWORD CSynthFilter::GetSoftwareVersion(void) { return 1; } // ------------------------------------------------------------------------- // CSynthStream, the output pin // ------------------------------------------------------------------------- // // CSynthStream::Constructor // CSynthStream::CSynthStream(HRESULT *phr, CSynthFilter *pParent, LPCWSTR pName) : CDynamicSourceStream(NAME("Audio Synth output pin"),phr, pParent, pName) , m_hPCMToMSADPCMConversionStream(NULL) , m_dwTempPCMBufferSize(0) , m_fFirstSampleDelivered(FALSE) , m_llSampleMediaTimeStart(0) { ASSERT(phr); m_Synth = new CAudioSynth(pParent->pStateLock()); pParent->m_Synth = m_Synth; if (m_Synth == NULL) { *phr = E_OUTOFMEMORY; return; } m_pParent = pParent; } // // CSynthStream::Destructor // CSynthStream::~CSynthStream(void) { delete m_Synth; } HRESULT LoopbackCapture(const WAVEFORMATEX& wfex, BYTE pBuf[], int iSize, WAVEFORMATEX* ifNotNullThenJustSetTypeOnly); // // FillBuffer // // Stuffs the buffer with data // "they" call this // then "they" call Deliver...so I guess we just fill it with something? // they *must* call this only every so often... // you probably should fill the entire buffer...hmm... HRESULT CSynthStream::FillBuffer(IMediaSample *pms) { CheckPointer(pms,E_POINTER); BYTE *pData; HRESULT hr = pms->GetPointer(&pData); if (FAILED(hr)) { return hr; } // This function must hold the state lock because it calls // FillPCMAudioBuffer(). CAutoLock lStateLock(m_pParent->pStateLock()); // This lock must be held because this function uses // m_dwTempPCMBufferSize, m_hPCMToMSADPCMConversionStream, // m_rtSampleTime, m_fFirstSampleDelivered and // m_llSampleMediaTimeStart. CAutoLock lShared(&m_cSharedState); WAVEFORMATEX* pwfexCurrent = (WAVEFORMATEX*)m_mt.Format(); if (WAVE_FORMAT_PCM == pwfexCurrent->wFormatTag) { // old way // m_Synth->FillPCMAudioBuffer(); // new way LoopbackCapture(*pwfexCurrent, pData, pms->GetSize(), NULL); hr = pms->SetActualDataLength(pms->GetSize()); if (FAILED(hr)) return hr; } else { // who cares about ADPCM... return E_FAIL; } // Set the sample's start and end time stamps... CRefTime rtStart = m_rtSampleTime; m_rtSampleTime = rtStart + (REFERENCE_TIME)(UNITS * pms->GetActualDataLength()) / (REFERENCE_TIME)pwfexCurrent->nAvgBytesPerSec; hr = pms->SetTime((REFERENCE_TIME*)&rtStart, (REFERENCE_TIME*)&m_rtSampleTime); if (FAILED(hr)) { return hr; } // Set the sample's properties. hr = pms->SetPreroll(FALSE); if (FAILED(hr)) { return hr; } //...CDynamicSourceStream::SetMediaType( hr = pms->SetMediaType(NULL); if (FAILED(hr)) { return hr; } hr = pms->SetDiscontinuity(!m_fFirstSampleDelivered); if (FAILED(hr)) { return hr; } hr = pms->SetSyncPoint(!m_fFirstSampleDelivered); if (FAILED(hr)) { return hr; } LONGLONG llMediaTimeStart = m_llSampleMediaTimeStart; DWORD dwNumAudioSamplesInPacket = (pms->GetActualDataLength() * BITS_PER_BYTE) / (pwfexCurrent->nChannels * pwfexCurrent->wBitsPerSample); LONGLONG llMediaTimeStop = m_llSampleMediaTimeStart + dwNumAudioSamplesInPacket; hr = pms->SetMediaTime(&llMediaTimeStart, &llMediaTimeStop); if (FAILED(hr)) { return hr; } m_llSampleMediaTimeStart = llMediaTimeStop; m_fFirstSampleDelivered = TRUE; return NOERROR; } // // Format Support // // // GetMediaType // I believe "they" call this... // we only have one type at a time... // so we just return our one type... // which we already told them what it was. HRESULT CSynthStream::GetMediaType(CMediaType *pmt) { CheckPointer(pmt,E_POINTER); // The caller must hold the state lock because this function // calls get_OutputFormat() and GetPCMFormatStructure(). // The function assumes that the state of the m_Synth // object does not change between the two calls. The // m_Synth object's state will not change if the // state lock is held. ASSERT(CritCheckIn(m_pParent->pStateLock())); return setAsNormal(pmt); } #define DECLARE_PTR(type, ptr, expr) type* ptr = (type*)(expr); HRESULT CSynthStream::setAsNormal(CMediaType *pmt) { WAVEFORMATEX *pwfex; SYNTH_OUTPUT_FORMAT ofCurrent; HRESULT hr = m_Synth->get_OutputFormat( &ofCurrent ); // get PCM if(FAILED(hr)) { return hr; } if(SYNTH_OF_PCM == ofCurrent) { // we always get here... pwfex = (WAVEFORMATEX *) pmt->AllocFormatBuffer(sizeof(WAVEFORMATEX)); if(NULL == pwfex) { return E_OUTOFMEMORY; } // we'll set it as PCM or what not, within this call... // now tell them "this is what we will give you..." WAVEFORMATEX unused; LoopbackCapture(unused, NULL, -1, pwfex); // old sin wave way... //m_Synth->GetPCMFormatStructure(pwfex); } else if(SYNTH_OF_MS_ADPCM == ofCurrent) { // !no ADPCM! return E_FAIL; } else { return E_UNEXPECTED; } return CreateAudioMediaType(pwfex, pmt, FALSE); // not ours... } // other fella doesn't even have this one... // maybe it could if it wanted to... HRESULT CSynthStream::CompleteConnect(IPin *pReceivePin) { // This lock must be held because this function uses // m_hPCMToMSADPCMConversionStream, m_fFirstSampleDelivered // and m_llSampleMediaTimeStart. CAutoLock lShared(&m_cSharedState); HRESULT hr; WAVEFORMATEX *pwfexCurrent = (WAVEFORMATEX*)m_mt.Format(); if(WAVE_FORMAT_PCM == pwfexCurrent->wFormatTag) { // always create our pretty sin wave on connect... hr = m_Synth->AllocWaveCache(*pwfexCurrent); if(FAILED(hr)) { return hr; } } else if(WAVE_FORMAT_ADPCM == pwfexCurrent->wFormatTag) { // no ADPCM! return E_FAIL; } else { ASSERT(NULL == m_hPCMToMSADPCMConversionStream); } hr = CDynamicSourceStream::CompleteConnect(pReceivePin); if(FAILED(hr)) { if(WAVE_FORMAT_ADPCM == pwfexCurrent->wFormatTag) { // acmStreamClose() should never fail because m_hPCMToMSADPCMConversionStream // holds a valid ACM stream handle and all operations using the handle are // synchronous. EXECUTE_ASSERT(0 == acmStreamClose(m_hPCMToMSADPCMConversionStream, 0)); m_hPCMToMSADPCMConversionStream = NULL; } return hr; } m_fFirstSampleDelivered = FALSE; m_llSampleMediaTimeStart = 0; return S_OK; } // pin connect was broken HRESULT CSynthStream::BreakConnect(void) { // This lock must be held because this function uses // m_hPCMToMSADPCMConversionStream and m_dwTempPCMBufferSize. CAutoLock lShared(&m_cSharedState); HRESULT hr = CDynamicSourceStream::BreakConnect(); if(FAILED(hr)) { return hr; } if(NULL != m_hPCMToMSADPCMConversionStream) { // acmStreamClose() should never fail because m_hPCMToMSADPCMConversionStream // holds a valid ACM stream handle and all operations using the handle are // synchronous. EXECUTE_ASSERT(0 == acmStreamClose(m_hPCMToMSADPCMConversionStream, 0)); m_hPCMToMSADPCMConversionStream = NULL; m_dwTempPCMBufferSize = 0; } return S_OK; } // // DecideBufferSize // // This will always be called after the format has been sucessfully // negotiated. So we have a look at m_mt to see what format we agreed to. // Then we can ask for buffers of the correct size to contain them. HRESULT CSynthStream::DecideBufferSize(IMemAllocator *pAlloc, ALLOCATOR_PROPERTIES *pProperties) { // The caller should always hold the shared state lock // before calling this function. This function must hold // the shared state lock because it uses m_hPCMToMSADPCMConversionStream // m_dwTempPCMBufferSize. ASSERT(CritCheckIn(&m_cSharedState)); CheckPointer(pAlloc,E_POINTER); CheckPointer(pProperties,E_POINTER); WAVEFORMATEX *pwfexCurrent = (WAVEFORMATEX*)m_mt.Format(); if(WAVE_FORMAT_PCM == pwfexCurrent->wFormatTag) { pProperties->cbBuffer = WaveBufferSize; // guess 16K is standard for PCM? what? } else { // no ADMCP! return E_FAIL; } int nBitsPerSample = pwfexCurrent->wBitsPerSample; int nSamplesPerSec = pwfexCurrent->nSamplesPerSec; int nChannels = pwfexCurrent->nChannels; // Get 1 second worth of buffers pProperties->cBuffers = (nChannels * nSamplesPerSec * nBitsPerSample) / (pProperties->cbBuffer * BITS_PER_BYTE); // Get 1/2 second worth of buffers pProperties->cBuffers /= 2; if(pProperties->cBuffers < 1) pProperties->cBuffers = 1 ; // Ask the allocator to reserve us the memory... ALLOCATOR_PROPERTIES Actual; HRESULT hr = pAlloc->SetProperties(pProperties,&Actual); if(FAILED(hr)) { return hr; } // Is this allocator unsuitable if(Actual.cbBuffer < pProperties->cbBuffer) { return E_FAIL; } return NOERROR; } // switch the pin to active (paused or running) mode // not an error to call this if already active // // Active // HRESULT CSynthStream::Active(void) { // This lock must be held because the function // uses m_rtSampleTime, m_fFirstSampleDelivered // and m_llSampleMediaTimeStart. CAutoLock lShared(&m_cSharedState); HRESULT hr = CDynamicSourceStream::Active(); if(FAILED(hr)) { return hr; } m_rtSampleTime = 0; m_fFirstSampleDelivered = FALSE; m_llSampleMediaTimeStart = 0; return NOERROR; } // ------------------------------------------------------------------------- // CAudioSynth // ------------------------------------------------------------------------- // Object that knows nothing about DirectShow, but just synthesizes waveforms CAudioSynth::CAudioSynth( CCritSec* pStateLock, int Frequency, int Waveform, int iBitsPerSample, int iChannels, int iSamplesPerSec, int iAmplitude ) : m_bWaveCache(NULL) , m_wWaveCache(NULL) , m_pStateLock(pStateLock) { ASSERT(Waveform >= WAVE_SINE); ASSERT(Waveform < WAVE_LAST); m_iFrequency = Frequency; m_iWaveform = Waveform; m_iAmplitude = iAmplitude; m_iSweepStart = DefaultSweepStart; m_iSweepEnd = DefaultSweepEnd; m_wFormatTag = WAVE_FORMAT_PCM; m_wBitsPerSample = (WORD) iBitsPerSample; m_wChannels = (WORD) iChannels; m_dwSamplesPerSec = iSamplesPerSec; } CAudioSynth::~CAudioSynth() { if(m_bWaveCache) { delete[] m_bWaveCache; } if(m_wWaveCache) { delete[] m_wWaveCache; } } // // AllocWaveCache // // HRESULT CAudioSynth::AllocWaveCache(const WAVEFORMATEX& wfex) { // The caller should hold the state lock because this // function uses m_iWaveCacheCycles, m_iWaveCacheSize // m_iFrequency, m_bWaveCache and m_wWaveCache. The // function should also hold the state lock because // it calls CalcCache(). ASSERT(CritCheckIn(m_pStateLock)); m_iWaveCacheCycles = m_iFrequency; m_iWaveCacheSize = (int) wfex.nSamplesPerSec; if(m_bWaveCache) { delete[] m_bWaveCache; m_bWaveCache = NULL; } if(m_wWaveCache) { delete[] m_wWaveCache; m_wWaveCache = NULL; } // The wave cache always stores PCM audio data. if(wfex.wBitsPerSample == 8) { m_bWaveCache = new BYTE [m_iWaveCacheSize]; if(NULL == m_bWaveCache) { return E_OUTOFMEMORY; } } else { m_wWaveCache = new WORD [m_iWaveCacheSize]; if(NULL == m_wWaveCache) { return E_OUTOFMEMORY; } } CalcCache(wfex); // fill it with a sin wave... return S_OK; } // I don't think this is ever called anymore... void CAudioSynth::GetPCMFormatStructure(WAVEFORMATEX* pwfex) { ASSERT(pwfex); // The caller must hold the state lock because this function uses // m_wChannels, m_wBitsPerSample and m_dwSamplesPerSec. ASSERT(CritCheckIn(m_pStateLock)); // Check for valid input parametes. ASSERT((1 == m_wChannels) || (2 == m_wChannels)); ASSERT((8 == m_wBitsPerSample) || (16 == m_wBitsPerSample)); ASSERT((8000 == m_dwSamplesPerSec) || (11025 == m_dwSamplesPerSec) || (22050 == m_dwSamplesPerSec) || (44100 == m_dwSamplesPerSec)); pwfex->wFormatTag = WAVE_FORMAT_PCM; pwfex->nChannels = m_wChannels; pwfex->nSamplesPerSec = m_dwSamplesPerSec; pwfex->wBitsPerSample = m_wBitsPerSample; pwfex->nBlockAlign = (WORD)((pwfex->wBitsPerSample * pwfex->nChannels) / BITS_PER_BYTE); pwfex->nAvgBytesPerSec = pwfex->nBlockAlign * pwfex->nSamplesPerSec; pwfex->cbSize = 0; // NPE here, too? FILE *fp = fopen("/normal is", "w"); fprintf(fp, "hello world %d %d %d %d %d %d %d", pwfex->wFormatTag, pwfex->nChannels, pwfex->nSamplesPerSec, pwfex->wBitsPerSample, pwfex->nBlockAlign, pwfex->nAvgBytesPerSec, pwfex->cbSize ); fclose(fp); } void CAudioSynth::copyCacheToOutputBuffers(const WAVEFORMATEX& wfex, BYTE pBuf[], int iSize) { if(wfex.wBitsPerSample == 8 && wfex.nChannels == 1) { while(iSize--) { *pBuf++ = m_bWaveCache[m_iWaveCacheIndex++]; if(m_iWaveCacheIndex >= m_iWaveCacheSize) m_iWaveCacheIndex = 0; } } else if(wfex.wBitsPerSample == 8 && wfex.nChannels == 2) { iSize /= 2; while(iSize--) { *pBuf++ = m_bWaveCache[m_iWaveCacheIndex]; *pBuf++ = m_bWaveCache[m_iWaveCacheIndex++]; if(m_iWaveCacheIndex >= m_iWaveCacheSize) m_iWaveCacheIndex = 0; } } else if(wfex.wBitsPerSample == 16 && wfex.nChannels == 1) { WORD * pW = (WORD *) pBuf; iSize /= 2; while(iSize--) { *pW++ = m_wWaveCache[m_iWaveCacheIndex++]; if(m_iWaveCacheIndex >= m_iWaveCacheSize) m_iWaveCacheIndex = 0; } } else if(wfex.wBitsPerSample == 16 && wfex.nChannels == 2) { WORD * pW = (WORD *) pBuf; iSize /= 4; while(iSize--) { *pW++ = m_wWaveCache[m_iWaveCacheIndex]; *pW++ = m_wWaveCache[m_iWaveCacheIndex++]; if(m_iWaveCacheIndex >= m_iWaveCacheSize) m_iWaveCacheIndex = 0; } } } // copied from STDAPI AMovieSetupRegisterServer( CLSID clsServer, LPCWSTR szDescription, LPCWSTR szFileName, LPCWSTR szThreadingModel = L"Both", LPCWSTR szServerType = L"InprocServer32" ); STDAPI AMovieSetupUnregisterServer( CLSID clsServer ); #define CreateComObject(clsid, iid, var) CoCreateInstance( clsid, NULL, CLSCTX_INPROC_SERVER, iid, (void **)&var); // straight call to here on init, instead of to // AMovieDllRegisterServer2 which is what the other fella does... // which I assume is similar to this...maybe? STDAPI RegisterFilters( BOOL bRegister ) { HRESULT hr = NOERROR; WCHAR achFileName[MAX_PATH]; char achTemp[MAX_PATH]; ASSERT(g_hInst != 0); if( 0 == GetModuleFileNameA(g_hInst, achTemp, sizeof(achTemp))) return AmHresultFromWin32(GetLastError()); MultiByteToWideChar(CP_ACP, 0L, achTemp, lstrlenA(achTemp) + 1, achFileName, NUMELMS(achFileName)); hr = CoInitialize(0); if(bRegister) { hr = AMovieSetupRegisterServer(CLSID_SynthFilter, L"Audio Synthesizer", achFileName, L"Both", L"InprocServer32"); } if( SUCCEEDED(hr) ) { IFilterMapper2 *fm = 0; hr = CreateComObject( CLSID_FilterMapper2, IID_IFilterMapper2, fm ); if( SUCCEEDED(hr) ) { if(bRegister) { IMoniker *pMoniker = 0; REGFILTER2 rf2; rf2.dwVersion = 1; rf2.dwMerit = MERIT_DO_NOT_USE; rf2.cPins = 1; rf2.rgPins = &sudOpPin; hr = fm->RegisterFilter(CLSID_SynthFilter, L"Audio Synthesizer", &pMoniker, &CLSID_AudioInputDeviceCategory, NULL, &rf2); } else { hr = fm->UnregisterFilter(&CLSID_AudioInputDeviceCategory, 0, CLSID_SynthFilter); } } // release interface // if(fm) fm->Release(); } if( SUCCEEDED(hr) && !bRegister ) hr = AMovieSetupUnregisterServer( CLSID_SynthFilter ); CoFreeUnusedLibraries(); CoUninitialize(); return hr; } //////////////////////////////////////////////////////////////////////// // // Exported entry points for registration and unregistration // (in this case they only call through to default implementations). // //////////////////////////////////////////////////////////////////////// STDAPI DllRegisterServer() { //printf("hello there"); return RegisterFilters(TRUE); } STDAPI DllUnregisterServer() { return RegisterFilters(FALSE); } // // DllEntryPoint // extern "C" BOOL WINAPI DllEntryPoint(HINSTANCE, ULONG, LPVOID); BOOL APIENTRY DllMain(HANDLE hModule, DWORD dwReason, LPVOID lpReserved) { return DllEntryPoint((HINSTANCE)(hModule), dwReason, lpReserved); } ================================================ FILE: source_code/synth_deprecated/synth.def ================================================ ;=========================================================================== ; Copyright (c) 1992-2002 Microsoft Corporation. All Rights Reserved. ;=========================================================================== LIBRARY Synth.dll EXPORTS DllMain PRIVATE DllGetClassObject PRIVATE DllCanUnloadNow PRIVATE DllRegisterServer PRIVATE DllUnregisterServer PRIVATE ================================================ FILE: source_code/synth_deprecated/synth.h ================================================ //------------------------------------------------------------------------------ // File: Synth.h // // Desc: DirectShow sample code - header file for audio signal generator // source filter. // // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ #ifndef __AUDIOSYNTH__ #define __AUDIOSYNTH__ // There are 8 bits in a byte. const DWORD BITS_PER_BYTE = 8; //CLSID_SynthFilter //{79A98DE0-BC00-11ce-AC2E-444553540000} DEFINE_GUID(CLSID_SynthFilter, 0x79a98de0, 0xbc00, 0x11ce, 0xac, 0x2e, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0); //CLSID_SynthFilterPropertyPage //{79A98DE1-BC00-11ce-AC2E-444553540000} DEFINE_GUID(CLSID_SynthPropertyPage, 0x79a98de1, 0xbc00, 0x11ce, 0xac, 0x2e, 0x44, 0x45, 0x53, 0x54, 0x0, 0x0); const double TWOPI = 6.283185308; const int MaxFrequency = 20000; const int MinFrequency = 0; const int DefaultFrequency = 440; // A-440 const int MaxAmplitude = 100; const int MinAmplitude = 0; const int DefaultSweepStart = DefaultFrequency; const int DefaultSweepEnd = 5000; const int WaveBufferSize = 16*1024; // Size of each allocated buffer // Originally used to be 2K, but at // 44khz/16bit/stereo you would get // audio breaks with a transform in the // middle. // huh? enum Waveforms { WAVE_SINE = 0, WAVE_SQUARE, WAVE_SAWTOOTH, WAVE_SINESWEEP, WAVE_LAST // Always keep this entry last }; #define WM_PROPERTYPAGE_ENABLE (WM_USER + 100) // below stuff is implementation-only.... #ifdef _AUDIOSYNTH_IMPLEMENTATION_ class CSynthStream; // ------------------------------------------------------------------------- // CAudioSynth // ------------------------------------------------------------------------- class CAudioSynth { public: CAudioSynth( CCritSec* pStateLock, int Frequency = DefaultFrequency, int Waveform = WAVE_SINE, int iBitsPerSample = 8, int iChannels = 1, int iSamplesPerSec = 11025, int iAmplitude = 100 ); ~CAudioSynth(); // Load the buffer with the current waveform void FillPCMAudioBuffer(const WAVEFORMATEX& wfex, BYTE pBuf[], int iSize); // helper for same void copyCacheToOutputBuffers(const WAVEFORMATEX& wfex, BYTE pBuf[], int iSize); // Set the "current" format and allocate temporary memory HRESULT AllocWaveCache(const WAVEFORMATEX& wfex); void GetPCMFormatStructure(WAVEFORMATEX* pwfex); STDMETHODIMP get_Frequency(int *Frequency); STDMETHODIMP put_Frequency(int Frequency); STDMETHODIMP get_Waveform(int *Waveform); STDMETHODIMP put_Waveform(int Waveform); STDMETHODIMP get_Channels(int *Channels); STDMETHODIMP put_Channels(int Channels); STDMETHODIMP get_BitsPerSample(int *BitsPerSample); STDMETHODIMP put_BitsPerSample(int BitsPerSample); STDMETHODIMP get_SamplesPerSec(int *SamplesPerSec); STDMETHODIMP put_SamplesPerSec(int SamplesPerSec); STDMETHODIMP put_SynthFormat(int Channels, int BitsPerSample, int SamplesPerSec); STDMETHODIMP get_Amplitude(int *Amplitude); STDMETHODIMP put_Amplitude(int Amplitude); STDMETHODIMP get_SweepRange(int *SweepStart, int *SweepEnd); STDMETHODIMP put_SweepRange(int SweepStart, int SweepEnd); STDMETHODIMP get_OutputFormat(SYNTH_OUTPUT_FORMAT *pOutputFormat); STDMETHODIMP put_OutputFormat(SYNTH_OUTPUT_FORMAT ofOutputFormat); private: CCritSec* m_pStateLock; WORD m_wChannels; // The output format's current number of channels. WORD m_wFormatTag; // The output format. This can be PCM audio or MS ADPCM audio. DWORD m_dwSamplesPerSec; // The number of samples produced in one second by the synth filter. WORD m_wBitsPerSample; // The number of bits in each sample. This member is only valid if the // current format is PCM audio. int m_iWaveform; // WAVE_SINE ... int m_iFrequency; // if not using sweep, this is the frequency int m_iAmplitude; // 0 to 100 int m_iWaveformLast; // keep track of the last known format int m_iFrequencyLast; // so we can flush the cache if necessary int m_iAmplitudeLast; int m_iCurrentSample; // 0 to iSamplesPerSec-1 BYTE * m_bWaveCache; // Wave Cache as BYTEs. This cache ALWAYS holds PCM audio data. WORD * m_wWaveCache; // Wave Cache as WORDs. This cache ALWAYS holds PCM audio data. int m_iWaveCacheSize; // how big is the cache? int m_iWaveCacheCycles; // how many cycles are in the cache int m_iWaveCacheIndex; int m_iSweepStart; // start of sweep int m_iSweepEnd; // end of sweep void CalcCache (const WAVEFORMATEX& wfex); void CalcCacheSine (const WAVEFORMATEX& wfex); void CalcCacheSquare (const WAVEFORMATEX& wfex); void CalcCacheSawtooth (const WAVEFORMATEX& wfex); void CalcCacheSweep (const WAVEFORMATEX& wfex); }; // ------------------------------------------------------------------------- // CSynthFilter // ------------------------------------------------------------------------- // CSynthFilter manages filter level stuff class CSynthFilter : public ISynth2, public CPersistStream, public ISpecifyPropertyPages, public CDynamicSource { public: static CUnknown * WINAPI CreateInstance(LPUNKNOWN lpunk, HRESULT *phr); ~CSynthFilter(); DECLARE_IUNKNOWN; // override this to reveal our property interface STDMETHODIMP NonDelegatingQueryInterface(REFIID riid, void ** ppv); // --- ISpecifyPropertyPages --- // return our property pages STDMETHODIMP GetPages(CAUUID * pPages); // --- IPersistStream Interface STDMETHODIMP GetClassID(CLSID *pClsid); int SizeMax(); HRESULT WriteToStream(IStream *pStream); HRESULT ReadFromStream(IStream *pStream); DWORD GetSoftwareVersion(void); // // --- ISynth2 --- // STDMETHODIMP get_Frequency(int *Frequency); STDMETHODIMP put_Frequency(int Frequency); STDMETHODIMP get_Waveform(int *Waveform); STDMETHODIMP put_Waveform(int Waveform); STDMETHODIMP get_Channels(int *Channels); STDMETHODIMP get_BitsPerSample(int *BitsPerSample); STDMETHODIMP get_SamplesPerSec(int *SamplesPerSec); STDMETHODIMP put_Channels(int Channels); STDMETHODIMP put_BitsPerSample(int BitsPersample); STDMETHODIMP put_SamplesPerSec(int SamplesPerSec); STDMETHODIMP get_Amplitude(int *Amplitude); STDMETHODIMP put_Amplitude(int Amplitude); STDMETHODIMP get_SweepRange(int *SweepStart, int *SweepEnd); STDMETHODIMP put_SweepRange(int SweepStart, int SweepEnd); STDMETHODIMP get_OutputFormat(SYNTH_OUTPUT_FORMAT *pOutputFormat); STDMETHODIMP put_OutputFormat(SYNTH_OUTPUT_FORMAT ofOutputFormat); CAudioSynth *m_Synth; // the current synthesizer private: // it is only allowed to to create these objects with CreateInstance CSynthFilter(LPUNKNOWN lpunk, HRESULT *phr); // When the format changes, reconnect... void ReconnectWithNewFormat(void); }; // ------------------------------------------------------------------------- // CSynthStream // ------------------------------------------------------------------------- // CSynthStream manages the data flow from the output pin. class CSynthStream : public CDynamicSourceStream { public: CSynthStream(HRESULT *phr, CSynthFilter *pParent, LPCWSTR pPinName); ~CSynthStream(); BOOL ReadyToStop(void) {return FALSE;} // stuff an audio buffer with the current format HRESULT FillBuffer(IMediaSample *pms); // ask for buffers of the size appropriate to the agreed media type. HRESULT DecideBufferSize(IMemAllocator *pIMemAlloc, ALLOCATOR_PROPERTIES *pProperties); HRESULT GetMediaType(CMediaType *pmt); HRESULT setAsNormal(CMediaType *pmt); HRESULT CompleteConnect(IPin *pReceivePin); HRESULT BreakConnect(void); // resets the stream time to zero. HRESULT Active(void); private: void DerivePCMFormatFromADPCMFormatStructure(const WAVEFORMATEX& wfexADPCM, WAVEFORMATEX* pwfexPCM); // Access to this state information should be serialized with the filters // critical section (m_pFilter->pStateLock()) // This lock protects: m_dwTempPCMBufferSize, m_hPCMToMSADPCMConversionStream, // m_rtSampleTime, m_fFirstSampleDelivered and m_llSampleMediaTimeStart CCritSec m_cSharedState; CRefTime m_rtSampleTime; // The time to be stamped on each sample HACMSTREAM m_hPCMToMSADPCMConversionStream; DWORD m_dwTempPCMBufferSize; bool m_fFirstSampleDelivered; LONGLONG m_llSampleMediaTimeStart; CAudioSynth *m_Synth; // the current synthesizer CSynthFilter *m_pParent; }; #endif // _AUDIOSYNTH_IMPLEMENTATION_ implementation only.... #endif /* __AUDIOSYNTH__ */ ================================================ FILE: source_code/synth_deprecated/synth.rc ================================================ //==========================================================================; // // THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY // KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE // IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR // PURPOSE. // // Copyright (c) Microsoft Corporation. All Rights Reserved. // //==========================================================================; // //Microsoft Visual C++ generated resource script. // #include #include "resource.h" #define APSTUDIO_READONLY_SYMBOLS ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 2 resource. // #include #include ///////////////////////////////////////////////////////////////////////////// #undef APSTUDIO_READONLY_SYMBOLS #ifdef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // TEXTINCLUDE // 1 TEXTINCLUDE DISCARDABLE BEGIN "resource.h\0" END 2 TEXTINCLUDE DISCARDABLE BEGIN "#include \r\n" "\0" END 3 TEXTINCLUDE DISCARDABLE BEGIN "\r\n" "\0" END ///////////////////////////////////////////////////////////////////////////// #endif // APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Dialog // IDD_SYNTHPROP1 DIALOG DISCARDABLE 0, 0, 316, 98 STYLE WS_CHILD FONT 8, "MS Shell Dlg" BEGIN CONTROL "Generic1",IDC_FREQTRACKBAR,"msctls_trackbar32", WS_TABSTOP | WS_CHILD | TBS_AUTOTICKS | TBS_VERT | TBS_RIGHT | TBS_ENABLESELRANGE,65,15,15,55 GROUPBOX "Frequency",IDC_FREQUENCYGROUP,57,5,39,85,WS_GROUP GROUPBOX "Waveform",IDC_WAVEFORMGROUP,4,4,50,85,WS_GROUP CONTROL "&Sine",IDC_WAVESINE,"Button",BS_AUTORADIOBUTTON,8,20,40, 10 CONTROL "Squar&e",IDC_WAVESQUARE,"Button",BS_AUTORADIOBUTTON,8, 35,41,10 CONTROL "Sa&wtooth",IDC_WAVESAWTOOTH,"Button",BS_AUTORADIOBUTTON, 8,50,44,10 CONTROL "Swee&p",IDC_WAVESWEEP,"Button",BS_AUTORADIOBUTTON, 8,65,41,10 GROUPBOX "Channels",IDC_CHANNELSGROUP,145,5,75,25,WS_GROUP CONTROL "&1",IDC_CHANNELS1,"Button",BS_AUTORADIOBUTTON,147,15,20, 10 CONTROL "&2",IDC_CHANNELS2,"Button",BS_AUTORADIOBUTTON,171,15,20, 10 GROUPBOX "Bits per sample",IDC_BITSPERSAMPLEGROUP,145,36,75,24, WS_GROUP CONTROL "&8",IDC_BITSPERSAMPLE8,"Button",BS_AUTORADIOBUTTON,147, 45,20,10 CONTROL "1&6",IDC_BITSPERSAMPLE16,"Button",BS_AUTORADIOBUTTON, 171,45,20,10 GROUPBOX "Sampling frequency",IDC_SAMPLINGFREQUENCYGROUP,145,65, 75,25,WS_GROUP CONTROL "&11",IDC_SAMPLINGFREQ11,"Button",BS_AUTORADIOBUTTON,147, 75,20,10 CONTROL "&22",IDC_SAMPLINGFREQ22,"Button",BS_AUTORADIOBUTTON,172, 75,20,10 CONTROL "&44",IDC_SAMPLINGFREQ44,"Button",BS_AUTORADIOBUTTON,196, 75,20,10 CONTROL "Generic1",IDC_AMPLITUDETRACKBAR,"msctls_trackbar32", WS_TABSTOP | 0x3a,110,15,20,55 GROUPBOX "Amplitude",IDC_AMPLITUDEGROUP,100,5,40,85,WS_GROUP EDITTEXT IDC_FREQUENCYTEXT,60,70,30,15,ES_AUTOHSCROLL EDITTEXT IDC_AMPLITUDETEXT,105,70,30,15,ES_AUTOHSCROLL GROUPBOX "Output Format",IDC_OUTPUTFORMAT,226,5,81,85,WS_GROUP CONTROL "PCM",IDC_OF_PCM,"Button",BS_AUTORADIOBUTTON,231,15,66, 11 CONTROL "Microsoft ADPCM",IDC_OF_MSADPCM,"Button", BS_AUTORADIOBUTTON,231,28,71,11 END STRINGTABLE DISCARDABLE BEGIN IDS_SYNTHPROPNAME, "Synthesizer" END // // Version // #define VERSION_RES_BIN_NAME "Synth.ax\0" #define VERSION_RES_BIN_DESCRIPTION "Audio Generator Source (Sample)\0" #define AMOVIE_SELF_REGISTER #ifndef APSTUDIO_INVOKED ///////////////////////////////////////////////////////////////////////////// // // Generated from the TEXTINCLUDE 3 resource. // ///////////////////////////////////////////////////////////////////////////// #endif // not APSTUDIO_INVOKED ================================================ FILE: source_code/synth_deprecated/synth.sln ================================================  Microsoft Visual Studio Solution File, Format Version 11.00 # Visual C++ Express 2010 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Synth", "Synth.vcxproj", "{BF20BC76-A330-4857-830B-80C85660478B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {BF20BC76-A330-4857-830B-80C85660478B}.Debug|Win32.ActiveCfg = Debug|Win32 {BF20BC76-A330-4857-830B-80C85660478B}.Debug|Win32.Build.0 = Debug|Win32 {BF20BC76-A330-4857-830B-80C85660478B}.Debug|x64.ActiveCfg = Debug|x64 {BF20BC76-A330-4857-830B-80C85660478B}.Debug|x64.Build.0 = Debug|x64 {BF20BC76-A330-4857-830B-80C85660478B}.Release|Win32.ActiveCfg = Release|Win32 {BF20BC76-A330-4857-830B-80C85660478B}.Release|Win32.Build.0 = Release|Win32 {BF20BC76-A330-4857-830B-80C85660478B}.Release|x64.ActiveCfg = Release|x64 {BF20BC76-A330-4857-830B-80C85660478B}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal ================================================ FILE: source_code/synth_deprecated/synth.vcproj ================================================ ================================================ FILE: source_code/synth_deprecated/synthprp.cpp ================================================ //------------------------------------------------------------------------------ // File: SynthPrp.cpp // // Desc: DirectShow sample code - implements property page for synthesizer. // // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ #include #include #include #include #include #include "isynth.h" #include "synth.h" #include "synthprp.h" #include "resource.h" #pragma warning(disable:4127) // C4127: conditional expression is constant // ------------------------------------------------------------------------- // CSynthProperties // ------------------------------------------------------------------------- // // CreateInstance // CUnknown * WINAPI CSynthProperties::CreateInstance(LPUNKNOWN lpunk, HRESULT *phr) { CUnknown *punk = new CSynthProperties(lpunk, phr); if (punk == NULL) { *phr = E_OUTOFMEMORY; } return punk; } // // Constructor // // Creaete a Property page object for the synthesizer CSynthProperties::CSynthProperties(LPUNKNOWN lpunk, HRESULT *phr) : CBasePropertyPage(NAME("Synth Property Page"), lpunk, IDD_SYNTHPROP1,IDS_SYNTHPROPNAME) , m_pSynth(NULL) , m_iSweepStart(DefaultSweepStart) , m_iSweepEnd(DefaultSweepEnd) , m_fWindowInActive(TRUE) { ASSERT(phr); InitCommonControls(); } // // OnConnect // // Give us the filter to communicate with HRESULT CSynthProperties::OnConnect(IUnknown *pUnknown) { ASSERT(m_pSynth == NULL); // Ask the filter for it's control interface HRESULT hr = pUnknown->QueryInterface(IID_ISynth2,(void **)&m_pSynth); if (FAILED(hr)) { return hr; } ASSERT(m_pSynth); // Get current filter state m_pSynth->get_BitsPerSample(&m_iBitsPerSampleOriginal); m_pSynth->get_Waveform(&m_iWaveformOriginal); m_pSynth->get_Frequency(&m_iFrequencyOriginal); m_pSynth->get_Channels(&m_iChannelsOriginal); m_pSynth->get_SamplesPerSec(&m_iSamplesPerSecOriginal); m_pSynth->get_Amplitude(&m_iAmplitudeOriginal); m_pSynth->get_OutputFormat(&m_OutputFormat); return NOERROR; } // // OnDisconnect // // Release the interface HRESULT CSynthProperties::OnDisconnect() { // Release the interface if (m_pSynth == NULL) { return E_UNEXPECTED; } m_pSynth->put_Waveform(m_iWaveformOriginal); m_pSynth->put_Frequency(m_iFrequencyOriginal); m_pSynth->put_Amplitude(m_iAmplitudeOriginal); m_pSynth->put_Channels(m_iChannelsOriginal); m_pSynth->put_BitsPerSample(m_iBitsPerSampleOriginal); m_pSynth->put_SamplesPerSec(m_iSamplesPerSecOriginal); m_pSynth->put_OutputFormat(m_OutputFormat); m_pSynth->Release(); m_pSynth = NULL; return NOERROR; } // // OnActivate // // Called on dialog creation HRESULT CSynthProperties::OnActivate(void) { InitPropertiesDialog(m_hwnd); ASSERT(m_hwndFreqSlider); m_fWindowInActive = FALSE; return NOERROR; } // // OnDeactivate // // Called on dialog destruction HRESULT CSynthProperties::OnDeactivate(void) { m_fWindowInActive = TRUE; return NOERROR; } // // OnApplyChanges // // User pressed the Apply button, remember the current settings HRESULT CSynthProperties::OnApplyChanges(void) { m_pSynth->get_BitsPerSample(&m_iBitsPerSampleOriginal); m_pSynth->get_Waveform(&m_iWaveformOriginal); m_pSynth->get_Frequency(&m_iFrequencyOriginal); m_pSynth->get_Channels(&m_iChannelsOriginal); m_pSynth->get_SamplesPerSec(&m_iSamplesPerSecOriginal); m_pSynth->get_Amplitude(&m_iAmplitudeOriginal); m_pSynth->get_OutputFormat(&m_OutputFormat); return NOERROR; } // // OnReceiveMessages // // Handles the messages for our property window INT_PTR CSynthProperties::OnReceiveMessage(HWND hwnd , UINT uMsg , WPARAM wParam , LPARAM lParam) { HRESULT hr; if(m_fWindowInActive) return FALSE; switch (uMsg) { case WM_PROPERTYPAGE_ENABLE: // Our private message that our owning filter sends us when changing to a Run / Stop / Pause // state. if lParam, then enable the controls which affect the format; if not lParam, then // disable the controls that affect the format. EnableWindow (GetDlgItem (hwnd, IDC_SAMPLINGFREQ11), (BOOL) lParam); EnableWindow (GetDlgItem (hwnd, IDC_SAMPLINGFREQ22), (BOOL) lParam); EnableWindow (GetDlgItem (hwnd, IDC_SAMPLINGFREQ44), (BOOL) lParam); EnableWindow (GetDlgItem (hwnd, IDC_BITSPERSAMPLE8), (BOOL) lParam); EnableWindow (GetDlgItem (hwnd, IDC_BITSPERSAMPLE16), (BOOL) lParam); EnableWindow (GetDlgItem (hwnd, IDC_CHANNELS1), (BOOL) lParam); EnableWindow (GetDlgItem (hwnd, IDC_CHANNELS2), (BOOL) lParam); break; case WM_VSCROLL: if ((HWND) lParam == m_hwndFreqSlider) OnFreqSliderNotification(LOWORD (wParam), HIWORD (wParam)); else if ((HWND) lParam == m_hwndAmplitudeSlider) OnAmpSliderNotification(LOWORD (wParam), HIWORD (wParam)); SetDirty(); return TRUE; case WM_COMMAND: switch (LOWORD(wParam)) { case IDC_FREQUENCYTEXT: { int iNotify = HIWORD (wParam); if (iNotify == EN_KILLFOCUS) { BOOL fOK; int iPos = GetDlgItemInt (hwnd, IDC_FREQUENCYTEXT, &fOK, FALSE); int iMaxFreq; m_pSynth->get_SamplesPerSec(&iMaxFreq); iMaxFreq /= 2; if (!fOK || (iPos > iMaxFreq || iPos < 0)) { int iCurrentFreq; m_pSynth->get_Frequency(&iCurrentFreq); SetDlgItemInt (hwnd, IDC_FREQUENCYTEXT, iCurrentFreq, FALSE); break; } SendMessage(m_hwndFreqSlider, TBM_SETPOS, TRUE, (LPARAM) iMaxFreq - iPos); m_pSynth->put_Frequency(iPos); SetDirty(); } } break; case IDC_AMPLITUDETEXT: { int iNotify = HIWORD (wParam); if (iNotify == EN_KILLFOCUS) { BOOL fOK; int iPos = GetDlgItemInt (hwnd, IDC_AMPLITUDETEXT, &fOK, FALSE); if (!fOK || (iPos > MaxAmplitude || iPos < 0)) { int iCurrentAmplitude; m_pSynth->get_Amplitude(&iCurrentAmplitude); SetDlgItemInt (hwnd, IDC_AMPLITUDETEXT, iCurrentAmplitude, FALSE); break; } SendMessage(m_hwndAmplitudeSlider, TBM_SETPOS, TRUE, (LPARAM) MaxAmplitude - iPos); m_pSynth->put_Amplitude(iPos); SetDirty(); } } break; case IDC_SAMPLINGFREQ11: m_pSynth->put_SamplesPerSec(11025); RecalcFreqSlider(); SetDirty(); break; case IDC_SAMPLINGFREQ22: m_pSynth->put_SamplesPerSec(22050); RecalcFreqSlider(); SetDirty(); break; case IDC_SAMPLINGFREQ44: m_pSynth->put_SamplesPerSec(44100); RecalcFreqSlider(); SetDirty(); break; case IDC_BITSPERSAMPLE8: m_pSynth->put_BitsPerSample(8); SetDirty(); break; case IDC_BITSPERSAMPLE16: m_pSynth->put_BitsPerSample(16); SetDirty(); break; case IDC_CHANNELS1: m_pSynth->put_Channels(1); SetDirty(); break; case IDC_CHANNELS2: m_pSynth->put_Channels(2); SetDirty(); break; case IDC_WAVESINE: m_pSynth->put_Waveform(WAVE_SINE); SetDirty(); break; case IDC_WAVESQUARE: m_pSynth->put_Waveform(WAVE_SQUARE); SetDirty(); break; case IDC_WAVESAWTOOTH: m_pSynth->put_Waveform(WAVE_SAWTOOTH); SetDirty(); break; case IDC_WAVESWEEP: m_pSynth->put_Waveform(WAVE_SINESWEEP); SetDirty(); break; case IDC_OF_PCM: hr = m_pSynth->put_OutputFormat(SYNTH_OF_PCM); if (SUCCEEDED(hr)) { EnableBitsPerSampleRadioButtons(hwnd, TRUE); } else { MessageBeep(MB_ICONASTERISK); } SetDirty(); break; case IDC_OF_MSADPCM: hr = m_pSynth->put_OutputFormat(SYNTH_OF_MS_ADPCM); if (SUCCEEDED(hr)) { EnableBitsPerSampleRadioButtons(hwnd, FALSE); } else { MessageBeep(MB_ICONASTERISK); } SetDirty(); break; default: break; } return TRUE; case WM_DESTROY: return TRUE; default: return FALSE; } return TRUE; } // // InitPropertiesDialog // void CSynthProperties::InitPropertiesDialog(HWND hwndParent) { m_hwndFreqSlider = GetDlgItem (hwndParent, IDC_FREQTRACKBAR); m_hwndFreqText = GetDlgItem (hwndParent, IDC_FREQUENCYTEXT); m_hwndAmplitudeSlider = GetDlgItem (hwndParent, IDC_AMPLITUDETRACKBAR); m_hwndAmplitudeText = GetDlgItem (hwndParent, IDC_AMPLITUDETEXT); // Sampling Frequency int i=0; switch (m_iSamplesPerSecOriginal) { case 11025: i = IDC_SAMPLINGFREQ11; break; case 22050: i = IDC_SAMPLINGFREQ22; break; case 44100: i = IDC_SAMPLINGFREQ44; break; default: ASSERT(0); break; } CheckRadioButton(hwndParent, IDC_SAMPLINGFREQ11, IDC_SAMPLINGFREQ44, i); // BitsPerSample CheckRadioButton(hwndParent, IDC_BITSPERSAMPLE8, IDC_BITSPERSAMPLE16, IDC_BITSPERSAMPLE8 + m_iBitsPerSampleOriginal / 8 - 1); // Waveform 0 == sine, 1 == square, ... CheckRadioButton(hwndParent, IDC_WAVESINE, IDC_WAVESWEEP, IDC_WAVESINE + m_iWaveformOriginal); // Channels CheckRadioButton(hwndParent, IDC_CHANNELS1, IDC_CHANNELS2, IDC_CHANNELS1 + m_iChannelsOriginal - 1); CheckRadioButton(hwndParent, IDC_OF_PCM, IDC_OF_MSADPCM, IDC_OF_PCM + (int)m_OutputFormat); if(SYNTH_OF_MS_ADPCM == m_OutputFormat) { EnableBitsPerSampleRadioButtons(hwndParent, FALSE); } else { // The synth filter only supports two output formats: SYNTH_OF_PCM and SYNTH_OF_MS_ADPCM. ASSERT(SYNTH_OF_PCM == m_OutputFormat); EnableBitsPerSampleRadioButtons(hwndParent, TRUE); } // // Frequency trackbar // RecalcFreqSlider(); // // Amplitude trackbar // SendMessage(m_hwndAmplitudeSlider, TBM_SETRANGE, TRUE, MAKELONG(MinAmplitude, MaxAmplitude) ); SendMessage(m_hwndAmplitudeSlider, TBM_SETTIC, 0, ((MinAmplitude + MaxAmplitude) / 2)); SendMessage(m_hwndAmplitudeSlider, TBM_SETPOS, TRUE, (LPARAM) (MaxAmplitude - m_iAmplitudeOriginal)); SetDlgItemInt (hwndParent, IDC_AMPLITUDETEXT, m_iAmplitudeOriginal, TRUE); } // // RecalcFreqSlider // // Set the range, current settings for the Freq scrollbar void CSynthProperties::RecalcFreqSlider(void) { int iPos, iMaxFreq; // Limit the frequency to one half the sampling frequency m_pSynth->get_SamplesPerSec(&iMaxFreq); iMaxFreq /= 2; m_pSynth->get_Frequency(&iPos); if (iPos > iMaxFreq) iPos = iMaxFreq; SendMessage(m_hwndFreqSlider, TBM_SETRANGE, TRUE, MAKELONG(0, iMaxFreq)); SendMessage(m_hwndFreqSlider, TBM_SETTIC, 0, iMaxFreq / 2); SendMessage(m_hwndFreqSlider, TBM_SETPOS, TRUE, (LPARAM) (iMaxFreq - iPos)); SendMessage(m_hwndFreqSlider, TBM_SETPAGESIZE, 0, 10); SendMessage(m_hwndFreqSlider, TBM_SETSEL, TRUE, MAKELONG (iMaxFreq - m_iSweepEnd, iMaxFreq - m_iSweepStart)); SetDlgItemInt (m_hwnd, IDC_FREQUENCYTEXT, iPos, TRUE); } // // OnFreqSliderNotification // // Handle the notification meesages from the slider control void CSynthProperties::OnFreqSliderNotification(WPARAM wParam, WORD wPosition) { int MaxFreq; int Freq; int SliderPos; switch (wParam) { case TB_ENDTRACK: case TB_THUMBTRACK: case TB_LINEDOWN: case TB_LINEUP: { // max frequency of slider is half the sampling frequency m_pSynth->get_SamplesPerSec (&MaxFreq); MaxFreq /= 2; SliderPos = (int) SendMessage(m_hwndFreqSlider, TBM_GETPOS, 0, 0L); Freq = MaxFreq - SliderPos; m_pSynth->put_Frequency (Freq); // Set the end of sweep to the current slider pos if (!(GetKeyState (VK_SHIFT) & 0x8000)) { m_iSweepEnd = Freq; } // Set the start of the sweep range if SHIFT key is pressed if (GetKeyState (VK_SHIFT) & 0x8000) { m_iSweepStart = Freq; } m_pSynth->put_SweepRange (m_iSweepStart, m_iSweepEnd); if (m_iSweepEnd > m_iSweepStart) SendMessage(m_hwndFreqSlider, TBM_SETSEL, TRUE, MAKELONG (MaxFreq - m_iSweepEnd, MaxFreq - m_iSweepStart)); else SendMessage(m_hwndFreqSlider, TBM_SETSEL, TRUE, MAKELONG (MaxFreq - m_iSweepStart, MaxFreq - m_iSweepEnd)); SetDlgItemInt (m_hwnd, IDC_FREQUENCYTEXT, Freq, TRUE); } break; } } // // OnAmpSliderNotification // // Handle the notification meesages from the slider control void CSynthProperties::OnAmpSliderNotification(WPARAM wParam, WORD wPosition) { switch (wParam) { case TB_ENDTRACK: case TB_THUMBTRACK: case TB_LINEDOWN: case TB_LINEUP: { int Level = (int) SendMessage(m_hwndAmplitudeSlider, TBM_GETPOS, 0, 0L); m_pSynth->put_Amplitude (MaxAmplitude - Level); SetDlgItemInt (m_hwnd, IDC_AMPLITUDETEXT, MaxAmplitude - Level, TRUE); } break; } } // // SetDirty // // notifies the property page site of changes void CSynthProperties::SetDirty() { m_bDirty = TRUE; if (m_pPageSite) m_pPageSite->OnStatusChange(PROPPAGESTATUS_DIRTY); } void CSynthProperties::EnableBitsPerSampleRadioButtons(HWND hwndParent, BOOL fEnable) { EnableWindow (GetDlgItem (hwndParent, IDC_BITSPERSAMPLE8), fEnable); EnableWindow (GetDlgItem (hwndParent, IDC_BITSPERSAMPLE16), fEnable); } ================================================ FILE: source_code/synth_deprecated/synthprp.h ================================================ //------------------------------------------------------------------------------ // File: SynthPrp.h // // Desc: DirectShow sample code - definition of CSynthProperties class. // // Copyright (c) Microsoft Corporation. All rights reserved. //------------------------------------------------------------------------------ class CSynthProperties : public CBasePropertyPage { public: CSynthProperties(LPUNKNOWN lpUnk, HRESULT *phr); static CUnknown * WINAPI CreateInstance(LPUNKNOWN lpunk, HRESULT *phr); HRESULT OnConnect(IUnknown *pUnknown); HRESULT OnDisconnect(); HRESULT OnActivate(); HRESULT OnDeactivate(); HRESULT OnApplyChanges(); INT_PTR OnReceiveMessage(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam); private: static BOOL CALLBACK DialogProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam); void InitPropertiesDialog(HWND hwndParent); void OnFreqSliderNotification(WPARAM wParam, WORD wPosition); void OnAmpSliderNotification(WPARAM wParam, WORD wPosition); void RecalcFreqSlider(void); void SetDirty(void); void EnableBitsPerSampleRadioButtons(HWND hwndParent, BOOL fEnable); HWND m_hwndFreqSlider; // handle of slider HWND m_hwndFreqText; // Handle of frequency text window HWND m_hwndAmplitudeSlider; // handle of slider HWND m_hwndAmplitudeText; // Handle of amplitude text window int m_iWaveformOriginal; // WAVE_SINE ... int m_iFrequencyOriginal; // if not using sweep, this is the frequency int m_iBitsPerSampleOriginal; // 8 or 16 int m_iChannelsOriginal; // 1 or 2 int m_iSamplesPerSecOriginal; // 8000, 11025, ... int m_iAmplitudeOriginal; // 0 to 100 int m_iSweepStart; // Sweep range on freq slider int m_iSweepEnd; SYNTH_OUTPUT_FORMAT m_OutputFormat; BOOL m_fWindowInActive; // TRUE ==> dialog is being destroyed ISynth2 *m_pSynth; // Interface to the synthsizer }; ================================================ FILE: source_code/synth_deprecated/useless_synth.cpp ================================================ #include #include #include #include #include #include #if (1100 > _MSC_VER) #include #else #include #endif #define _AUDIOSYNTH_IMPLEMENTATION_ #include "DynSrc.h" #include "isynth.h" #include "synth.h" #include "synthprp.h" HRESULT LoopbackCapture(const WAVEFORMATEX& wfex, BYTE pBuf[], int iSize, WAVEFORMATEX* ifNotNullThenJustSetTypeOnly); // // FillAudioBuffer // // This actually fills it with the sin wave by copying it verbatim into the output... // void CAudioSynth::FillPCMAudioBuffer(const WAVEFORMATEX& wfex, BYTE pBuf[], int iSize) { BOOL fCalcCache = FALSE; // The caller should always hold the state lock because this // function uses m_iFrequency, m_iFrequencyLast, m_iWaveform // m_iWaveformLast, m_iAmplitude, m_iAmplitudeLast, m_iWaveCacheIndex // m_iWaveCacheSize, m_bWaveCache and m_wWaveCache. The caller should // also hold the state lock because this function calls CalcCache(). ASSERT(CritCheckIn(m_pStateLock)); // Only realloc the cache if the format has changed ! if(m_iFrequency != m_iFrequencyLast) { fCalcCache = TRUE; m_iFrequencyLast = m_iFrequency; } if(m_iWaveform != m_iWaveformLast) { fCalcCache = TRUE; m_iWaveformLast = m_iWaveform; } if(m_iAmplitude != m_iAmplitudeLast) { fCalcCache = TRUE; m_iAmplitudeLast = m_iAmplitude; } if(fCalcCache) { // recalculate the sin wave... CalcCache(wfex); } // sin wave (old way) // Copy cache to output buffers copyCacheToOutputBuffers(wfex, pBuf, iSize); } // // put_SamplesPerSec // STDMETHODIMP CSynthFilter::put_SamplesPerSec(int SamplesPerSec) { // This function holds the state lock because it does not want // the filter's format type state or its' connection state // to change between the call to put_SamplesPerSec() and the call to // ReconnectWithNewFormat(). CAutoLock lStateLock(pStateLock()); HRESULT hr = m_Synth->put_SamplesPerSec(SamplesPerSec); if( FAILED( hr ) ) { return hr; } ReconnectWithNewFormat (); DbgLog((LOG_TRACE, 3, TEXT("put_SamplesPerSec: %d"), SamplesPerSec)); return NOERROR; } // // put_Amplitude // STDMETHODIMP CSynthFilter::put_Amplitude(int Amplitude) { m_Synth->put_Amplitude (Amplitude); DbgLog((LOG_TRACE, 3, TEXT("put_Amplitude: %d"), Amplitude)); return NOERROR; } // // put_SweepRange // STDMETHODIMP CSynthFilter::put_SweepRange(int SweepStart, int SweepEnd) { m_Synth->put_SweepRange (SweepStart, SweepEnd); DbgLog((LOG_TRACE, 3, TEXT("put_SweepRange: %d %d"), SweepStart, SweepEnd)); return NOERROR; } STDMETHODIMP CSynthFilter::put_OutputFormat(SYNTH_OUTPUT_FORMAT ofOutputFormat) { // This function holds the state lock because it does not want // the filter's format type state or its' connection state // to change between the call to put_OutputFormat() and the call to // ReconnectWithNewFormat(). CAutoLock lStateLock(pStateLock()); HRESULT hr = m_Synth->put_OutputFormat(ofOutputFormat); if (FAILED(hr)) { return hr; } ReconnectWithNewFormat(); return S_OK; } // // put_BitsPerSample // STDMETHODIMP CSynthFilter::put_BitsPerSample(int BitsPerSample) { // This function holds the state lock because it does not want // the filter's format type state or its' connection state // to change between the call to put_BitsPerSample() and the call to // ReconnectWithNewFormat(). CAutoLock lStateLock(pStateLock()); HRESULT hr = m_Synth->put_BitsPerSample(BitsPerSample); if( FAILED( hr ) ) { return hr; } ReconnectWithNewFormat (); DbgLog((LOG_TRACE, 3, TEXT("put_BitsPerSample: %d"), BitsPerSample)); return NOERROR; } // // put_Channels // STDMETHODIMP CSynthFilter::put_Channels(int Channels) { // This function holds the state lock because it does not want // the filter's format type state or its' connection state // to change between the call to put_Channels() and the call to // ReconnectWithNewFormat(). CAutoLock lStateLock(pStateLock()); HRESULT hr = m_Synth->put_Channels(Channels); if( FAILED( hr ) ) { return hr; } ReconnectWithNewFormat (); DbgLog((LOG_TRACE, 3, TEXT("put_Channels: %d"), Channels)); return NOERROR; } // // CSynthFilter::Destructor // CSynthFilter::~CSynthFilter(void) { // // Base class will free our pins // } // // NonDelegatingQueryInterface // // Reveal our property page, persistance, and control interfaces STDMETHODIMP CSynthFilter::NonDelegatingQueryInterface(REFIID riid, void **ppv) { if (riid == IID_ISynth2) { return GetInterface((ISynth2 *) this, ppv); } else if (riid == IID_IPersistStream) { return GetInterface((IPersistStream *) this, ppv); } else if (riid == IID_ISpecifyPropertyPages) { return GetInterface((ISpecifyPropertyPages *) this, ppv); } else { return CDynamicSource::NonDelegatingQueryInterface(riid, ppv); } } // // GetPages // STDMETHODIMP CSynthFilter::GetPages(CAUUID * pPages) { CheckPointer(pPages,E_POINTER); pPages->cElems = 1; pPages->pElems = (GUID *) CoTaskMemAlloc(sizeof(GUID)); if (pPages->pElems == NULL) { return E_OUTOFMEMORY; } *(pPages->pElems) = CLSID_SynthPropertyPage; return NOERROR; } // ------------------------------------------------------------------------- // --- IPersistStream --- // ------------------------------------------------------------------------- #define WRITEOUT(var) hr = pStream->Write(&var, sizeof(var), NULL); \ if (FAILED(hr)) return hr; #define READIN(var) hr = pStream->Read(&var, sizeof(var), NULL); \ if (FAILED(hr)) return hr; // appears this one is called by directsound... HRESULT CSynthFilter::WriteToStream(IStream *pStream) { CheckPointer(pStream,E_POINTER); HRESULT hr; int i, k; get_Frequency (&i); WRITEOUT(i); get_Waveform (&i); WRITEOUT(i); get_Channels (&i); WRITEOUT(i); get_BitsPerSample (&i); WRITEOUT(i); get_SamplesPerSec (&i); WRITEOUT(i); get_Amplitude (&i); WRITEOUT(i); get_SweepRange (&i, &k); WRITEOUT(i); WRITEOUT(k); //#get_OutputFormat((SYNTH_OUTPUT_FORMAT*)&i); i = WAVE_FORMAT_PCM; WRITEOUT(i); return hr; } // I think this is just to read from the properties pages...I think... HRESULT CSynthFilter::ReadFromStream(IStream *pStream) { CheckPointer(pStream,E_POINTER); if (GetSoftwareVersion() != mPS_dwFileVersion) return E_FAIL; HRESULT hr; int i, k; READIN(i); // reads 4 bytes... put_Frequency(i); READIN(i); put_Waveform (i); READIN(i); put_Channels (i); READIN(i); put_BitsPerSample (i); READIN(i); put_SamplesPerSec (i); READIN(i); put_Amplitude (i); READIN(i); READIN(k); put_SweepRange (i, k); READIN(i); put_OutputFormat((SYNTH_OUTPUT_FORMAT)i); return hr; } // // get_Frequency // STDMETHODIMP CAudioSynth::get_Frequency(int *pFrequency) { CheckPointer(pFrequency,E_POINTER); *pFrequency = m_iFrequency; DbgLog((LOG_TRACE, 3, TEXT("get_Frequency: %d"), *pFrequency)); return NOERROR; } // // put_Frequency // STDMETHODIMP CAudioSynth::put_Frequency(int Frequency) { CAutoLock l(m_pStateLock); m_iFrequency = Frequency; DbgLog((LOG_TRACE, 3, TEXT("put_Frequency: %d"), Frequency)); return NOERROR; } // // get_Waveform // STDMETHODIMP CAudioSynth::get_Waveform(int *pWaveform) { CheckPointer(pWaveform,E_POINTER); *pWaveform = m_iWaveform; DbgLog((LOG_TRACE, 3, TEXT("get_Waveform: %d"), *pWaveform)); return NOERROR; } // // put_Waveform // STDMETHODIMP CAudioSynth::put_Waveform(int Waveform) { CAutoLock l(m_pStateLock); m_iWaveform = Waveform; DbgLog((LOG_TRACE, 3, TEXT("put_Waveform: %d"), Waveform)); return NOERROR; } // // get_Channels // STDMETHODIMP CAudioSynth::get_Channels(int *pChannels) { CheckPointer(pChannels,E_POINTER); *pChannels = m_wChannels; DbgLog((LOG_TRACE, 3, TEXT("get_Channels: %d"), *pChannels)); return NOERROR; } // // put_Channels // STDMETHODIMP CAudioSynth::put_Channels(int Channels) { CAutoLock l(m_pStateLock); m_wChannels = (WORD) Channels; return NOERROR; } // // get_BitsPerSample // STDMETHODIMP CAudioSynth::get_BitsPerSample(int *pBitsPerSample) { CheckPointer(pBitsPerSample,E_POINTER); *pBitsPerSample = m_wBitsPerSample; DbgLog((LOG_TRACE, 3, TEXT("get_BitsPerSample: %d"), *pBitsPerSample)); return NOERROR; } // // put_BitsPerSample // STDMETHODIMP CAudioSynth::put_BitsPerSample(int BitsPerSample) { CAutoLock l(m_pStateLock); m_wBitsPerSample = (WORD) BitsPerSample; return NOERROR; } // // get_SamplesPerSec // STDMETHODIMP CAudioSynth::get_SamplesPerSec(int *pSamplesPerSec) { CheckPointer(pSamplesPerSec,E_POINTER); *pSamplesPerSec = m_dwSamplesPerSec; DbgLog((LOG_TRACE, 3, TEXT("get_SamplesPerSec: %d"), *pSamplesPerSec)); return NOERROR; } // // put_SamplesPerSec // STDMETHODIMP CAudioSynth::put_SamplesPerSec(int SamplesPerSec) { CAutoLock l(m_pStateLock); m_dwSamplesPerSec = SamplesPerSec; return NOERROR; } // // put_SynthFormat // STDMETHODIMP CAudioSynth::put_SynthFormat(int Channels, int BitsPerSample, int SamplesPerSec) { CAutoLock l(m_pStateLock); m_wChannels = (WORD) Channels; m_wBitsPerSample = (WORD) BitsPerSample; m_dwSamplesPerSec = SamplesPerSec; DbgLog((LOG_TRACE, 1, TEXT("put_SynthFormat: %d-bit %d-channel %dHz"), BitsPerSample, Channels, SamplesPerSec)); return NOERROR; } // // get_Amplitude // STDMETHODIMP CAudioSynth::get_Amplitude(int *pAmplitude) { CheckPointer(pAmplitude,E_POINTER); *pAmplitude = m_iAmplitude; DbgLog((LOG_TRACE, 3, TEXT("get_Amplitude: %d"), *pAmplitude)); return NOERROR; } // // put_Amplitude // STDMETHODIMP CAudioSynth::put_Amplitude(int Amplitude) { CAutoLock l(m_pStateLock); if(Amplitude > MaxAmplitude || Amplitude < MinAmplitude) return E_INVALIDARG; m_iAmplitude = Amplitude; DbgLog((LOG_TRACE, 3, TEXT("put_Amplitude: %d"), Amplitude)); return NOERROR; } // // get_SweepRange // STDMETHODIMP CAudioSynth::get_SweepRange(int *pSweepStart, int *pSweepEnd) { CheckPointer(pSweepStart,E_POINTER); CheckPointer(pSweepEnd,E_POINTER); *pSweepStart = m_iSweepStart; *pSweepEnd = m_iSweepEnd; DbgLog((LOG_TRACE, 3, TEXT("get_SweepStart: %d %d"), *pSweepStart, *pSweepEnd)); return NOERROR; } // // put_SweepRange // STDMETHODIMP CAudioSynth::put_SweepRange(int SweepStart, int SweepEnd) { CAutoLock l(m_pStateLock); m_iSweepStart = SweepStart; m_iSweepEnd = SweepEnd; DbgLog((LOG_TRACE, 3, TEXT("put_SweepRange: %d %d"), SweepStart, SweepEnd)); return NOERROR; } // // get_OutputFormat // STDMETHODIMP CAudioSynth::get_OutputFormat(SYNTH_OUTPUT_FORMAT *pOutputFormat) { CheckPointer(pOutputFormat, E_POINTER); ValidateReadWritePtr(pOutputFormat, sizeof(SYNTH_OUTPUT_FORMAT)); switch(m_wFormatTag) { case WAVE_FORMAT_PCM: *pOutputFormat = SYNTH_OF_PCM; break; case WAVE_FORMAT_ADPCM: *pOutputFormat = SYNTH_OF_MS_ADPCM; break; default: return E_UNEXPECTED; } return S_OK; } // // put_OutputFormat // STDMETHODIMP CAudioSynth::put_OutputFormat(SYNTH_OUTPUT_FORMAT ofOutputFormat) { CAutoLock l(m_pStateLock); switch(ofOutputFormat) { case SYNTH_OF_PCM: m_wFormatTag = WAVE_FORMAT_PCM; break; case SYNTH_OF_MS_ADPCM: m_wFormatTag = WAVE_FORMAT_ADPCM; break; default: return E_INVALIDARG; } return S_OK; } // ------------------------------------------------------------------------- // ISynth2, the control interface for the synthesizer // ------------------------------------------------------------------------- // // get_Frequency // STDMETHODIMP CSynthFilter::get_Frequency(int *Frequency) { m_Synth->get_Frequency(Frequency); DbgLog((LOG_TRACE, 3, TEXT("get_Frequency: %d"), *Frequency)); return NOERROR; } // // put_Frequency // STDMETHODIMP CSynthFilter::put_Frequency(int Frequency) { m_Synth->put_Frequency (Frequency); DbgLog((LOG_TRACE, 3, TEXT("put_Frequency: %d"), Frequency)); return NOERROR; } // // get_Waveform // STDMETHODIMP CSynthFilter::get_Waveform(int *Waveform) { m_Synth->get_Waveform (Waveform); DbgLog((LOG_TRACE, 3, TEXT("get_Waveform: %d"), *Waveform)); return NOERROR; } // // put_Waveform // STDMETHODIMP CSynthFilter::put_Waveform(int Waveform) { m_Synth->put_Waveform (Waveform); DbgLog((LOG_TRACE, 3, TEXT("put_Waveform: %d"), Waveform)); return NOERROR; } // // get_Channels // STDMETHODIMP CSynthFilter::get_Channels(int *Channels) { HRESULT hr = m_Synth->get_Channels( Channels ); DbgLog((LOG_TRACE, 3, TEXT("get_Channels: %d"), *Channels)); return hr; } // // If the format changes, we need to reconnect // void CSynthFilter::ReconnectWithNewFormat(void) { // The caller must hold the state lock because this // function calls IsConnected(). ASSERT(CritCheckIn(pStateLock())); // The synth filter's only has one pin. The pin is an output pin. CDynamicSourceStream* pOutputPin = (CDynamicSourceStream*)GetPin(0); if( pOutputPin->IsConnected() ) { pOutputPin->OutputPinNeedsToBeReconnected(); } } // // get_BitsPerSample // STDMETHODIMP CSynthFilter::get_BitsPerSample(int *BitsPerSample) { HRESULT hr = m_Synth->get_BitsPerSample(BitsPerSample); DbgLog((LOG_TRACE, 3, TEXT("get_BitsPerSample: %d"), *BitsPerSample)); return hr; } // // get_SamplesPerSec // STDMETHODIMP CSynthFilter::get_SamplesPerSec(int *SamplesPerSec) { HRESULT hr = m_Synth->get_SamplesPerSec(SamplesPerSec); DbgLog((LOG_TRACE, 3, TEXT("get_SamplesPerSec: %d"), *SamplesPerSec)); return hr; } // // get_Amplitude // STDMETHODIMP CSynthFilter::get_Amplitude(int *Amplitude) { m_Synth->get_Amplitude (Amplitude); DbgLog((LOG_TRACE, 3, TEXT("get_Amplitude: %d"), *Amplitude)); return NOERROR; } // // get_SweepRange // STDMETHODIMP CSynthFilter::get_SweepRange(int *SweepStart, int *SweepEnd) { m_Synth->get_SweepRange (SweepStart, SweepEnd); DbgLog((LOG_TRACE, 3, TEXT("get_SweepStart: %d %d"), *SweepStart, *SweepEnd)); return NOERROR; } STDMETHODIMP CSynthFilter::get_OutputFormat(SYNTH_OUTPUT_FORMAT* pOutputFormat) { HRESULT hr = m_Synth->get_OutputFormat(pOutputFormat); if (FAILED(hr)) { return hr; } return S_OK; } void CSynthStream::DerivePCMFormatFromADPCMFormatStructure(const WAVEFORMATEX& wfexADPCM, WAVEFORMATEX* pwfexPCM) { ASSERT(pwfexPCM); if (!pwfexPCM) return; pwfexPCM->wFormatTag = WAVE_FORMAT_PCM; pwfexPCM->wBitsPerSample = 16; pwfexPCM->cbSize = 0; pwfexPCM->nChannels = wfexADPCM.nChannels; pwfexPCM->nSamplesPerSec = wfexADPCM.nSamplesPerSec; pwfexPCM->nBlockAlign = (WORD)((pwfexPCM->nChannels * pwfexPCM->wBitsPerSample) / BITS_PER_BYTE); pwfexPCM->nAvgBytesPerSec = pwfexPCM->nBlockAlign * pwfexPCM->nSamplesPerSec; } void CAudioSynth::CalcCache(const WAVEFORMATEX& wfex) { switch(m_iWaveform) { case WAVE_SINE: CalcCacheSine(wfex); break; case WAVE_SQUARE: CalcCacheSquare(wfex); break; case WAVE_SAWTOOTH: CalcCacheSawtooth(wfex); break; case WAVE_SINESWEEP: CalcCacheSweep(wfex); break; } } // // CalcCacheSine // // void CAudioSynth::CalcCacheSine(const WAVEFORMATEX& wfex) { int i; double d; double amplitude; double FTwoPIDivSpS; amplitude = ((wfex.wBitsPerSample == 8) ? 127 : 32767 ) * m_iAmplitude / 100; FTwoPIDivSpS = m_iFrequency * TWOPI / wfex.nSamplesPerSec; m_iWaveCacheIndex = 0; m_iCurrentSample = 0; if(wfex.wBitsPerSample == 8) { BYTE * pB = m_bWaveCache; for(i = 0; i < m_iWaveCacheSize; i++) { d = FTwoPIDivSpS * i; *pB++ = (BYTE) ((sin(d) * amplitude) + 128); } } else { PWORD pW = (PWORD) m_wWaveCache; for(i = 0; i < m_iWaveCacheSize; i++) { d = FTwoPIDivSpS * i; *pW++ = (WORD) (sin(d) * amplitude); } } } // // CalcCacheSquare // // void CAudioSynth::CalcCacheSquare(const WAVEFORMATEX& wfex) { int i; double d; double FTwoPIDivSpS; BYTE b0, b1; WORD w0, w1; b0 = (BYTE) (128 - (127 * m_iAmplitude / 100)); b1 = (BYTE) (128 + (127 * m_iAmplitude / 100)); w0 = (WORD) (32767. * m_iAmplitude / 100); w1 = (WORD) - (32767. * m_iAmplitude / 100); FTwoPIDivSpS = m_iFrequency * TWOPI / wfex.nSamplesPerSec; m_iWaveCacheIndex = 0; m_iCurrentSample = 0; if(wfex.wBitsPerSample == 8) { BYTE * pB = m_bWaveCache; for(i = 0; i < m_iWaveCacheSize; i++) { d = FTwoPIDivSpS * i; *pB++ = (BYTE) ((sin(d) >= 0) ? b1 : b0); } } else { PWORD pW = (PWORD) m_wWaveCache; for(i = 0; i < m_iWaveCacheSize; i++) { d = FTwoPIDivSpS * i; *pW++ = (WORD) ((sin(d) >= 0) ? w1 : w0); } } } // // CalcCacheSawtooth // void CAudioSynth::CalcCacheSawtooth(const WAVEFORMATEX& wfex) { int i; double d; double amplitude; double FTwoPIDivSpS; double step; double curstep=0; BOOL fLastWasNeg = TRUE; BOOL fPositive; amplitude = ((wfex.wBitsPerSample == 8) ? 255 : 65535 ) * m_iAmplitude / 100; FTwoPIDivSpS = m_iFrequency * TWOPI / wfex.nSamplesPerSec; step = amplitude * m_iFrequency / wfex.nSamplesPerSec; m_iWaveCacheIndex = 0; m_iCurrentSample = 0; BYTE * pB = m_bWaveCache; PWORD pW = (PWORD) m_wWaveCache; for(i = 0; i < m_iWaveCacheSize; i++) { d = FTwoPIDivSpS * i; // OneShot triggered on positive zero crossing fPositive = (sin(d) >= 0); if(fLastWasNeg && fPositive) { if(wfex.wBitsPerSample == 8) curstep = 128 - amplitude / 2; else curstep = 32768 - amplitude / 2; } fLastWasNeg = !fPositive; if(wfex.wBitsPerSample == 8) *pB++ = (BYTE) curstep; else *pW++ = (WORD) (-32767 + curstep); curstep += step; } } // // CalcCacheSweep // void CAudioSynth::CalcCacheSweep(const WAVEFORMATEX& wfex) { int i; double d; double amplitude; double FTwoPIDivSpS; double CurrentFreq; double DeltaFreq; amplitude = ((wfex.wBitsPerSample == 8) ? 127 : 32767 ) * m_iAmplitude / 100; DeltaFreq = ((double) m_iSweepEnd - m_iSweepStart) / m_iWaveCacheSize; CurrentFreq = m_iSweepStart; m_iWaveCacheIndex = 0; m_iCurrentSample = 0; if(wfex.wBitsPerSample == 8) { BYTE * pB = m_bWaveCache; d = 0.0; for(i = 0; i < m_iWaveCacheSize; i++) { FTwoPIDivSpS = (int) CurrentFreq * TWOPI / wfex.nSamplesPerSec; CurrentFreq += DeltaFreq; d += FTwoPIDivSpS; *pB++ = (BYTE) ((sin(d) * amplitude) + 128); } } else { PWORD pW = (PWORD) m_wWaveCache; d = 0.0; for(i = 0; i < m_iWaveCacheSize; i++) { FTwoPIDivSpS = (int) CurrentFreq * TWOPI / wfex.nSamplesPerSec; CurrentFreq += DeltaFreq; d += FTwoPIDivSpS; *pW++ = (WORD) (sin(d) * amplitude); } } } ================================================ FILE: source_code/virtual audio output sniffer.sln ================================================  Microsoft Visual Studio Solution File, Format Version 11.00 # Visual C++ Express 2010 Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "audio_sniffer", "acam\acam.vcxproj", "{B6830EAD-C98E-490D-8999-CAA246788BF7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Win32 = Debug|Win32 Debug|x64 = Debug|x64 Release|Win32 = Release|Win32 Release|x64 = Release|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {B6830EAD-C98E-490D-8999-CAA246788BF7}.Debug|Win32.ActiveCfg = Debug|Win32 {B6830EAD-C98E-490D-8999-CAA246788BF7}.Debug|Win32.Build.0 = Debug|Win32 {B6830EAD-C98E-490D-8999-CAA246788BF7}.Debug|x64.ActiveCfg = Debug|x64 {B6830EAD-C98E-490D-8999-CAA246788BF7}.Debug|x64.Build.0 = Debug|x64 {B6830EAD-C98E-490D-8999-CAA246788BF7}.Release|Win32.ActiveCfg = Release|Win32 {B6830EAD-C98E-490D-8999-CAA246788BF7}.Release|Win32.Build.0 = Release|Win32 {B6830EAD-C98E-490D-8999-CAA246788BF7}.Release|x64.ActiveCfg = Release|x64 {B6830EAD-C98E-490D-8999-CAA246788BF7}.Release|x64.Build.0 = Release|x64 EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection EndGlobal