main: typo fixed; timing: regression from 0.07 fixed
[kkapture:kkapture.git] / kkapturedll / sound.cpp
1 /* kkapture: intrusive demo video capturing.
2  * Copyright (c) 2005-2009 Fabian "ryg/farbrausch" Giesen.
3  *
4  * This program is free software; you can redistribute and/or modify it under
5  * the terms of the Artistic License, Version 2.0beta5, or (at your opinion)
6  * any later version; all distributions of this program should contain this
7  * license in a file named "LICENSE.txt".
8  *
9  * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
10  * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
11  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
12  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT UNLESS REQUIRED BY
13  * LAW OR AGREED TO IN WRITING WILL ANY COPYRIGHT HOLDER OR CONTRIBUTOR
14  * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY,
15  * OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT
16  * OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, PROFITS; OR
17  * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
18  * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
19  * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
20  * EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
21  */
22
23 #include "stdafx.h"
24 #include <malloc.h>
25 #include "videoencoder.h"
26 #include "util.h"
27 #include "video.h"
28 #include <psapi.h>
29 #define DIRECTSOUND_VERSION 0x0800
30 #include <dsound.h>
31
32 #pragma comment(lib,"dsound.lib")
33 #pragma comment(lib,"dxguid.lib")
34 #pragma comment(lib,"winmm.lib")
35 #pragma comment(lib,"psapi.lib")
36
37 // my own directsound fake!
38 class MyDirectSound8;
39 class MyDirectSoundBuffer8;
40
41 static MyDirectSoundBuffer8 *playBuffer = 0;
42
43 class LockOwner
44 {
45   CRITICAL_SECTION &section;
46
47 public:
48   LockOwner(CRITICAL_SECTION &cs)
49     : section(cs)
50   {
51     EnterCriticalSection(&section);
52   }
53
54   ~LockOwner()
55   {
56     LeaveCriticalSection(&section);
57   }
58 };
59
60 class MyDirectSound3DListener8 : public IDirectSound3DListener8
61 {
62   int RefCount;
63
64 public:
65   MyDirectSound3DListener8()
66     : RefCount(1)
67   {
68   }
69
70   // IUnknown methods
71   virtual HRESULT __stdcall QueryInterface(REFIID iid,LPVOID *ptr)
72   {
73     if(iid == IID_IDirectSound3DListener || iid == IID_IDirectSound3DListener8)
74     {
75       *ptr = this;
76       AddRef();
77       return S_OK;
78     }
79     else
80       return E_NOINTERFACE;
81   }
82
83   virtual ULONG __stdcall AddRef()
84   {
85     return ++RefCount;
86   }
87
88   virtual ULONG __stdcall Release()
89   {
90     ULONG ret = --RefCount;
91     if(!RefCount)
92       delete this;
93
94     return ret;
95   }
96
97   // IDirectSound3DListener methods
98   virtual HRESULT __stdcall GetAllParameters(LPDS3DLISTENER pListener)
99   {
100     return E_NOTIMPL;
101   }
102
103   virtual HRESULT __stdcall GetDistanceFactor(D3DVALUE* pflDistanceFactor)
104   {
105     return E_NOTIMPL;
106   }
107
108   virtual HRESULT __stdcall GetDopplerFactor(D3DVALUE* pflDistanceFactor)
109   {
110     return E_NOTIMPL;
111   }
112
113   virtual HRESULT __stdcall GetOrientation(D3DVECTOR* pvOrientFront, D3DVECTOR* pvOrientTop)
114   {
115     return E_NOTIMPL;
116   }
117
118   virtual HRESULT __stdcall GetPosition(D3DVECTOR* pvPosition)
119   {
120     return E_NOTIMPL;
121   }
122
123   virtual HRESULT __stdcall GetRolloffFactor(D3DVALUE* pflRolloffFactor)
124   {
125     return E_NOTIMPL;
126   }
127
128   virtual HRESULT __stdcall GetVelocity(D3DVECTOR* pvVelocity)
129   {
130     return E_NOTIMPL;
131   }
132
133   virtual HRESULT __stdcall SetAllParameters(LPCDS3DLISTENER pcListener,DWORD dwApply)
134   {
135     return E_NOTIMPL;
136   }
137
138   virtual HRESULT __stdcall SetDistanceFactor(D3DVALUE flDistanceFactor,DWORD dwApply)
139   {
140     return E_NOTIMPL;
141   }
142
143   virtual HRESULT __stdcall SetDopplerFactor(D3DVALUE flDopplerFactor,DWORD dwApply)
144   {
145     return E_NOTIMPL;
146   }
147
148   virtual HRESULT __stdcall SetOrientation(D3DVALUE xFront,D3DVALUE yFront,D3DVALUE zFront,D3DVALUE xTop,D3DVALUE yTop,D3DVALUE zTop,DWORD dwApply)
149   {
150     return E_NOTIMPL;
151   }
152
153   virtual HRESULT __stdcall SetPosition(D3DVALUE x,D3DVALUE y,D3DVALUE z,DWORD dwApply)
154   {
155     return E_NOTIMPL;
156   }
157
158   virtual HRESULT __stdcall SetRolloffFactor(D3DVALUE flRolloffFactor,DWORD dwApply)
159   {
160     return E_NOTIMPL;
161   }
162
163   virtual HRESULT __stdcall SetVelocity(D3DVALUE x,D3DVALUE y,D3DVALUE z,DWORD dwApply)
164   {
165     return E_NOTIMPL;
166   }
167
168   virtual HRESULT __stdcall CommitDeferredSettings()
169   {
170     return S_OK;
171   }
172 };
173
174 class MyDirectSound3DBuffer8 : public IDirectSound3DBuffer8
175 {
176   int RefCount;
177
178 public:
179   MyDirectSound3DBuffer8()
180     : RefCount(1)
181   {
182   }
183
184   // IUnknown methods
185   virtual HRESULT __stdcall QueryInterface(REFIID iid,LPVOID *ptr)
186   {
187     if(iid == IID_IDirectSound3DBuffer || iid == IID_IDirectSound3DListener8)
188     {
189       *ptr = this;
190       AddRef();
191       return S_OK;
192     }
193     else
194       return E_NOINTERFACE;
195   }
196
197   virtual ULONG __stdcall AddRef()
198   {
199     return ++RefCount;
200   }
201
202   virtual ULONG __stdcall Release()
203   {
204     ULONG ret = --RefCount;
205     if(!RefCount)
206       delete this;
207
208     return ret;
209   }
210
211   // IDirectSound3DBuffer methods
212   virtual HRESULT __stdcall GetAllParameters(LPDS3DBUFFER pDs3dBuffer)
213   {
214     return E_NOTIMPL;
215   }
216
217   virtual HRESULT __stdcall GetConeAngles(LPDWORD pdwInsideConeAngle, LPDWORD pdwOutsideConeAngle)
218   {
219     return E_NOTIMPL;
220   }
221
222   virtual HRESULT __stdcall GetConeOrientation(D3DVECTOR *pvOrientation)
223   {
224     return E_NOTIMPL;
225   }
226
227   virtual HRESULT __stdcall GetConeOutsideVolume(LPLONG plConeOutsideVolume)
228   {
229     return E_NOTIMPL;
230   }
231
232   virtual HRESULT __stdcall GetMaxDistance(D3DVALUE *pflMaxDistance)
233   {
234     return E_NOTIMPL;
235   }
236
237   virtual HRESULT __stdcall GetMinDistance(D3DVALUE *pflMaxDistance)
238   {
239     return E_NOTIMPL;
240   }
241
242   virtual HRESULT __stdcall GetMode(LPDWORD pdwMode)
243   {
244     return E_NOTIMPL;
245   }
246
247   virtual HRESULT __stdcall GetPosition(D3DVECTOR *pvPosition)
248   {
249     return E_NOTIMPL;
250   }
251
252   virtual HRESULT __stdcall GetVelocity(D3DVECTOR *pvVelocity)
253   {
254     return E_NOTIMPL;
255   }
256
257   virtual HRESULT __stdcall SetAllParameters(LPCDS3DBUFFER pDs3dBuffer, DWORD dwApply)
258   {
259     return E_NOTIMPL;
260   }
261
262   virtual HRESULT __stdcall SetConeAngles(DWORD dwInsideConeAngle, DWORD dwOutsideConeAngle, DWORD dwApply)
263   {
264     return E_NOTIMPL;
265   }
266
267   virtual HRESULT __stdcall SetConeOrientation(D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD dwApply)
268   {
269     return E_NOTIMPL;
270   }
271
272   virtual HRESULT __stdcall SetConeOutsideVolume(LONG lConeOutsideVolume, DWORD dwApply)
273   {
274     return E_NOTIMPL;
275   }
276
277   virtual HRESULT __stdcall SetMaxDistance(D3DVALUE flMaxDistance, DWORD dwApply)
278   {
279     return E_NOTIMPL;
280   }
281
282   virtual HRESULT __stdcall SetMinDistance(D3DVALUE flMaxDistance, DWORD dwApply)
283   {
284     return E_NOTIMPL;
285   }
286
287   virtual HRESULT __stdcall SetMode(DWORD dwMode, DWORD dwApply)
288   {
289     return E_NOTIMPL;
290   }
291   
292   virtual HRESULT __stdcall SetPosition(D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD dwApply)
293   {
294     return E_NOTIMPL;
295   }
296
297   virtual HRESULT __stdcall SetVelocity(D3DVALUE x, D3DVALUE y, D3DVALUE z, DWORD dwApply)
298   {
299     return E_NOTIMPL;
300   }
301 };
302
303 class MyDirectSoundBuffer8 : public IDirectSoundBuffer8
304 {
305   int RefCount;
306   PBYTE Buffer;
307   DWORD Flags;
308   DWORD Bytes;
309   WAVEFORMATEX *Format;
310   DWORD Frequency;
311   BOOL Playing,Looping;
312   LONG Volume;
313   LONG Panning;
314   CRITICAL_SECTION BufferLock;
315   DWORD PlayCursor;
316   BOOL SkipAllowed;
317   DWORD SamplesPlayed;
318   DWORD GetPosThisFrame;
319   int FirstFrame;
320
321   DWORD NextFrameSize()
322   {
323     DWORD frame = getFrameTiming() - FirstFrame;
324     DWORD samplePos = UMulDiv(frame,Frequency * frameRateDenom,frameRateScaled);
325     DWORD bufferPos = samplePos * Format->nBlockAlign;
326     DWORD nextSize = bufferPos - SamplesPlayed;
327
328     return nextSize;
329   }
330
331   DWORD WriteCursor()
332   {
333     if(!Playing)
334       return 0;
335
336     return (PlayCursor + 128 * Format->nBlockAlign) % Bytes;
337   }
338
339 public:
340   MyDirectSoundBuffer8(DWORD flags,DWORD bufBytes,LPWAVEFORMATEX fmt)
341     : RefCount(1)
342   {
343     Flags = flags;
344     Buffer = new BYTE[bufBytes];
345     Bytes = bufBytes;
346     memset(Buffer,0,bufBytes);
347
348     if(fmt)
349       Format = CopyFormat(fmt);
350     else
351     {
352       Format = new WAVEFORMATEX;
353       Format->wFormatTag = WAVE_FORMAT_PCM;
354       Format->nChannels = 2;
355       Format->nSamplesPerSec = 44100;
356       Format->nAvgBytesPerSec = 176400;
357       Format->nBlockAlign = 4;
358       Format->wBitsPerSample = 16;
359       Format->cbSize = 0;
360     }
361
362     Frequency = Format->nSamplesPerSec;
363
364     Playing = FALSE;
365     Looping = FALSE;
366     Volume = 0;
367     Panning = 0;
368     PlayCursor = 0;
369     SkipAllowed = FALSE;
370     SamplesPlayed = 0;
371     GetPosThisFrame = 0;
372
373     InitializeCriticalSection(&BufferLock);
374   }
375
376   ~MyDirectSoundBuffer8()
377   {
378     // synchronize access to bufferlock before deleting the section
379     {
380       LockOwner sync(BufferLock);
381     }
382
383     DeleteCriticalSection(&BufferLock);
384     delete Format;
385     delete[] Buffer;
386   }
387
388   // IUnknown methods
389   virtual HRESULT __stdcall QueryInterface(REFIID iid,LPVOID *ptr)
390   {
391     if(iid == IID_IDirectSoundBuffer || iid == IID_IDirectSoundBuffer8)
392     {
393       *ptr = this;
394       AddRef();
395       return S_OK;
396     }
397     else if(iid == IID_IDirectSound3DListener || iid == IID_IDirectSound3DListener8)
398     {
399       *ptr = new MyDirectSound3DListener8;
400       return S_OK;
401     }
402     else if(iid == IID_IDirectSound3DBuffer || iid == IID_IDirectSound3DBuffer8)
403     {
404       *ptr = new MyDirectSound3DBuffer8;
405       return S_OK;
406     }
407     else
408       return E_NOINTERFACE;
409   }
410
411   virtual ULONG __stdcall AddRef()
412   {
413     return ++RefCount;
414   }
415
416   virtual ULONG __stdcall Release()
417   {
418     ULONG ret = --RefCount;
419     if(!RefCount)
420       delete this;
421
422     return ret;
423   }
424
425   // IDirectSoundBuffer methods
426   virtual HRESULT __stdcall GetCaps(LPDSBCAPS caps)
427   {
428     if(caps && caps->dwSize == sizeof(DSBCAPS))
429     {
430       caps->dwFlags = Flags;
431       caps->dwBufferBytes = Bytes;
432       caps->dwUnlockTransferRate = 400 * 1024;
433       caps->dwPlayCpuOverhead = 0;
434
435       return S_OK;
436     }
437     else
438     {
439       printLog("dsound: DirectSoundBuffer::GetCaps - invalid size\n");
440       return DSERR_INVALIDPARAM;
441     }
442   }
443
444   virtual HRESULT __stdcall GetCurrentPosition(LPDWORD pdwCurrentPlayCursor,LPDWORD pdwCurrentWriteCursor)
445   {
446     LockOwner lock(BufferLock);
447
448     // skip some milliseconds of silence at start
449     if(SkipAllowed)
450     {
451       DWORD maxskip = UMulDiv(Format->nSamplesPerSec,Format->nBlockAlign*params.SoundMaxSkip,1000);
452       DWORD pp = PlayCursor;
453       DWORD i;
454
455       // find out whether the next maxskip bytes are zero
456       for(i=0;i<maxskip;i++)
457       {
458         if(Buffer[pp])
459           break;
460
461         if(++pp == Bytes)
462           pp = 0;
463       }
464
465       // yes they are, skip them
466       if(i && i == maxskip)
467         PlayCursor = pp;
468       else
469         SkipAllowed = FALSE;
470     }
471
472     if(!Playing) // not playing, report zeroes
473     {
474       if(pdwCurrentPlayCursor)
475         *pdwCurrentPlayCursor = 0;
476
477       if(pdwCurrentWriteCursor)
478         *pdwCurrentWriteCursor = 0;
479     }
480     else // playing, so report current positions
481     {
482       //// the "track one" hack!
483       //if(params.FairlightHack && !Looping && ++GetPosThisFrame > 128)
484       //{
485       //  Stop();
486       //  PlayCursor = Bytes;
487       //}
488
489       if(pdwCurrentPlayCursor)
490         *pdwCurrentPlayCursor = PlayCursor;
491
492       if(pdwCurrentWriteCursor)
493         *pdwCurrentWriteCursor = WriteCursor();
494     }
495
496     return S_OK;
497   }
498
499   virtual HRESULT __stdcall GetFormat(LPWAVEFORMATEX pwfxFormat, DWORD dwSizeAllocated, LPDWORD pdwSizeWritten)
500   {
501     int size = min(dwSizeAllocated,Format ? sizeof(WAVEFORMATEX) + Format->cbSize : 0);
502
503     if(pdwSizeWritten)
504       *pdwSizeWritten = size;
505
506     if(pwfxFormat)
507       memcpy(pwfxFormat,Format,size);
508
509     return S_OK;
510   }
511
512   virtual HRESULT __stdcall GetVolume(LPLONG plVolume)
513   {
514     if(plVolume)
515       *plVolume = Volume;
516
517     return S_OK;
518   }
519
520   virtual HRESULT __stdcall GetPan(LPLONG plPan)
521   {
522     if(plPan)
523       *plPan = Panning;
524
525     return S_OK;
526   }
527
528   virtual HRESULT __stdcall GetFrequency(LPDWORD pdwFrequency)
529   {
530     if(pdwFrequency)
531       *pdwFrequency = Frequency;
532
533     return S_OK;
534   }
535
536   virtual HRESULT __stdcall GetStatus(LPDWORD pdwStatus)
537   {
538     if(pdwStatus)
539     {
540       *pdwStatus = 0;
541       *pdwStatus |= Playing ? DSBSTATUS_PLAYING : 0;
542       *pdwStatus |= Looping ? DSBSTATUS_LOOPING : 0;
543     }
544
545     return DS_OK;
546   }
547
548   virtual HRESULT __stdcall Initialize(LPDIRECTSOUND pDirectSound, LPCDSBUFFERDESC pcDSBufferDesc)
549   {
550     return S_OK;
551   }
552
553   virtual HRESULT __stdcall Lock(DWORD dwOffset,DWORD dwBytes,LPVOID *ppvAudioPtr1,LPDWORD pdwAudioBytes1,LPVOID *ppvAudioPtr2,LPDWORD pdwAudioBytes2,DWORD dwFlags)
554   {
555     LockOwner lock(BufferLock);
556
557     if(dwFlags & DSBLOCK_FROMWRITECURSOR)
558       dwOffset = WriteCursor();
559
560     if(dwFlags & DSBLOCK_ENTIREBUFFER)
561       dwBytes = Bytes;
562
563     if(dwOffset >= Bytes || dwBytes > Bytes)
564     {
565       LeaveCriticalSection(&BufferLock);
566       return DSERR_INVALIDPARAM;
567     }
568
569     if(dwOffset + dwBytes <= Bytes) // no wrap
570     {
571       *ppvAudioPtr1 = (LPVOID) (Buffer + dwOffset);
572       *pdwAudioBytes1 = dwBytes;
573       if(ppvAudioPtr2)
574       {
575         *ppvAudioPtr2 = 0;
576         *pdwAudioBytes2 = 0;
577       }
578     }
579     else // wrap
580     {
581       *ppvAudioPtr1 = (LPVOID) (Buffer + dwOffset);
582       *pdwAudioBytes1 = Bytes - dwOffset;
583       if(ppvAudioPtr2)
584       {
585         *ppvAudioPtr2 = (LPVOID) Buffer;
586         *pdwAudioBytes2 = dwBytes - *pdwAudioBytes1;
587       }
588     }
589
590     return S_OK;
591   }
592
593   virtual HRESULT __stdcall Play(DWORD dwReserved1,DWORD dwPriority,DWORD dwFlags)
594   {
595     if(!Playing)
596       SkipAllowed = TRUE;
597
598     printLog("sound: play\n");
599
600     Playing = TRUE;
601     Looping = (dwFlags & DSBPLAY_LOOPING) ? TRUE : FALSE;
602
603     if(!(Flags & DSBCAPS_PRIMARYBUFFER)/* && (!params.FairlightHack || Looping)*/)
604     {
605       encoder->SetAudioFormat(Format);
606       playBuffer = this;
607       FirstFrame = getFrameTiming();
608     }
609
610     return S_OK;
611   }
612
613   virtual HRESULT __stdcall SetCurrentPosition(DWORD dwNewPosition)
614   {
615     PlayCursor = dwNewPosition;
616     return S_OK;
617   }
618
619   virtual HRESULT __stdcall SetFormat(LPCWAVEFORMATEX pcfxFormat)
620   {
621     delete Format;
622     Format = CopyFormat(pcfxFormat);
623     if(playBuffer==this)
624       encoder->SetAudioFormat(Format);
625
626     return S_OK;
627   }
628
629   virtual HRESULT __stdcall SetVolume(LONG lVolume)
630   {
631     Volume = lVolume;
632     return S_OK;
633   }
634
635   virtual HRESULT __stdcall SetPan(LONG lPan)
636   {
637     Panning = lPan;
638     return S_OK;
639   }
640
641   virtual HRESULT __stdcall SetFrequency(DWORD dwFrequency)
642   {
643     Frequency = dwFrequency;
644     Format->nSamplesPerSec = dwFrequency;
645     Format->nAvgBytesPerSec = Format->nBlockAlign * dwFrequency;
646     if(playBuffer==this)
647       encoder->SetAudioFormat(Format);
648
649     return S_OK;
650   }
651
652   virtual HRESULT __stdcall Stop()
653   {
654     Playing = FALSE;
655     if(playBuffer == this)
656       playBuffer = 0;
657
658     return S_OK;
659   }
660
661   virtual HRESULT __stdcall Unlock(LPVOID pvAudioPtr1,DWORD dwAudioBytes1,LPVOID pvAudioPtr2,DWORD dwAudioBytes2)
662   {
663     return S_OK;
664   }
665
666   virtual HRESULT __stdcall Restore()
667   {
668     return S_OK;
669   }
670
671   // IDirectSoundBuffer8 methods
672   virtual HRESULT __stdcall SetFX(DWORD dwEffectsCount,LPDSEFFECTDESC pDSFXDesc,LPDWORD pdwResultCodes)
673   {
674     return E_NOTIMPL;
675   }
676
677   virtual HRESULT __stdcall AcquireResources(DWORD dwFlags,DWORD dwEffectsCount,LPDWORD pdwResultCodes)
678   {
679     return E_NOTIMPL;
680   }
681
682   virtual HRESULT __stdcall GetObjectInPath(REFGUID rguidObject,DWORD dwIndex,REFGUID rguidInterface,LPVOID *ppObject)
683   {
684     return E_NOTIMPL;
685   }
686
687   // ----
688   void encodeLastFrameAudio()
689   {
690     LockOwner lock(BufferLock);
691
692     // calculate number of samples processed since last frame, then encode
693     DWORD frameSize = NextFrameSize();
694     DWORD end = PlayCursor + frameSize;
695     DWORD align = Format->nBlockAlign;
696
697     if(end - PlayCursor > Bytes)
698     {
699       printLog("sound: more samples required per frame than buffer allows, increase frame rate\n");
700       end = PlayCursor + Bytes;
701     }
702
703     if(end > Bytes) // wrap
704     {
705       encoder->WriteAudioFrame(Buffer + PlayCursor,(Bytes - PlayCursor) / align);
706       encoder->WriteAudioFrame(Buffer,(end - Bytes) / align);
707     }
708     else // no wrap
709       encoder->WriteAudioFrame(Buffer + PlayCursor,(end - PlayCursor) / align);
710
711     PlayCursor = end % Bytes;
712     SamplesPlayed += frameSize;
713     GetPosThisFrame = 0;
714   }
715 };
716
717 class MyDirectSound8 : public IDirectSound8
718 {
719   int RefCount;
720
721 public:
722   MyDirectSound8()
723     : RefCount(1)
724   {
725     videoNeedEncoder();
726   }
727
728   // IUnknown methods
729   virtual HRESULT __stdcall QueryInterface(REFIID iid,LPVOID *ptr)
730   {
731     printLog("sound: dsound queryinterface\n");
732     return E_NOINTERFACE;
733   }
734
735   virtual ULONG __stdcall AddRef()
736   {
737     return ++RefCount;
738   }
739
740   virtual ULONG __stdcall Release()
741   {
742     ULONG ret = --RefCount;
743     if(!RefCount)
744       delete this;
745
746     return ret;
747   }
748
749   // IDirectSound methods
750   virtual HRESULT __stdcall CreateSoundBuffer(LPCDSBUFFERDESC desc,LPDIRECTSOUNDBUFFER *ppDSBuffer,LPUNKNOWN pUnkOuter)
751   {
752     if(desc && (desc->dwSize == sizeof(DSBUFFERDESC) || desc->dwSize == sizeof(DSBUFFERDESC1)))
753     {
754       if(desc->dwFlags & DSBCAPS_LOCHARDWARE) // we certainly don't do hw mixing.
755         return DSERR_CONTROLUNAVAIL;
756
757       *ppDSBuffer = new MyDirectSoundBuffer8(desc->dwFlags,desc->dwBufferBytes,desc->lpwfxFormat);
758       printLog("sound: buffer created\n");
759       return S_OK;
760     }
761     else
762       return DSERR_INVALIDPARAM;
763   }
764
765   virtual HRESULT __stdcall GetCaps(LPDSCAPS pDSCaps)
766   {
767     if(pDSCaps && pDSCaps->dwSize == sizeof(DSCAPS))
768     {
769       ZeroMemory(pDSCaps,sizeof(DSCAPS));
770
771       pDSCaps->dwSize = sizeof(DSCAPS);
772       pDSCaps->dwFlags = DSCAPS_CONTINUOUSRATE
773         | DSCAPS_PRIMARY16BIT | DSCAPS_PRIMARY8BIT
774         | DSCAPS_PRIMARYMONO | DSCAPS_PRIMARYSTEREO
775         | DSCAPS_SECONDARY16BIT | DSCAPS_SECONDARY8BIT
776         | DSCAPS_SECONDARYMONO | DSCAPS_SECONDARYSTEREO;
777       pDSCaps->dwMinSecondarySampleRate = 4000;
778       pDSCaps->dwMaxSecondarySampleRate = 96000;
779       pDSCaps->dwPrimaryBuffers = 1;
780
781       return S_OK;
782     }
783     else
784     {
785       printLog("sound: DirectSound::GetCaps - invalid size\n");
786       return DSERR_INVALIDPARAM;
787     }
788   }
789
790   virtual HRESULT __stdcall DuplicateSoundBuffer(LPDIRECTSOUNDBUFFER pDSBufferOriginal,LPDIRECTSOUNDBUFFER *ppDSBufferDuplicate)
791   {
792     printLog("sound: attempting DuplicateSoundBuffer hack...\n");
793
794     if(!ppDSBufferDuplicate)
795       return E_INVALIDARG;
796
797     // we don't mix, so simply return a handle to the same buffer and hope it works...
798     pDSBufferOriginal->AddRef();
799     *ppDSBufferDuplicate = pDSBufferOriginal;
800     return S_OK;
801
802     //printLog("sound: DuplicateSoundBuffer attempted (not implemented yet)\n");
803     //return E_NOTIMPL;
804   }
805
806   virtual HRESULT __stdcall SetCooperativeLevel(HWND hwnd,DWORD dwLevel)
807   {
808     return S_OK;
809   }
810
811   virtual HRESULT __stdcall Compact()
812   {
813     return S_OK;
814   }
815
816   virtual HRESULT __stdcall GetSpeakerConfig(LPDWORD pdwSpeakerConfig)
817   {
818     *pdwSpeakerConfig = DSSPEAKER_STEREO;
819     return S_OK;
820   }
821
822   virtual HRESULT __stdcall SetSpeakerConfig(DWORD dwSpeakerConfig)
823   {
824     printLog("sound: dsound setspeakerconfig\n");
825     return E_NOTIMPL;
826   }
827
828   virtual HRESULT __stdcall Initialize(LPCGUID pcGuidDevice)
829   {
830     return S_OK;
831   }
832
833   // IDirectSound8 methods
834   virtual HRESULT __stdcall VerifyCertification(LPDWORD pdwCertified)
835   {
836     printLog("sound: dsound verifycertification\n");
837     return E_NOTIMPL;
838   }
839 };
840
841 // trampolines
842 DETOUR_TRAMPOLINE(HRESULT __stdcall Real_DirectSoundCreate(LPCGUID lpcGuidDevice,LPDIRECTSOUND *ppDS,LPUNKNOWN pUnkOuter), DirectSoundCreate);
843 DETOUR_TRAMPOLINE(HRESULT __stdcall Real_DirectSoundCreate8(LPCGUID lpcGuidDevice,LPDIRECTSOUND8 *ppDS8,LPUNKNOWN pUnkOuter), DirectSoundCreate8);
844
845 DETOUR_TRAMPOLINE(HRESULT __stdcall Real_CoCreateInstance(REFCLSID rclsid,LPUNKNOWN pUnkOuter,DWORD dwClsContext,REFIID riid,LPVOID *ppv), CoCreateInstance);
846
847 HRESULT __stdcall Mine_DirectSoundCreate(LPCGUID lpcGuidDevice,LPDIRECTSOUND8 *ppDS8,LPUNKNOWN pUnkOuter)
848 {
849   printLog("sound: emulating DirectSound\n");
850   *ppDS8 = new MyDirectSound8;
851   return S_OK;
852 }
853
854 HRESULT __stdcall Mine_DirectSoundCreate8(LPCGUID lpcGuidDevice,LPDIRECTSOUND8 *ppDS8,LPUNKNOWN pUnkOuter)
855 {
856   printLog("sound: emulating DirectSound 8\n");
857   *ppDS8 = new MyDirectSound8;
858   return S_OK;
859 }
860
861 HRESULT __stdcall Mine_CoCreateInstance(REFCLSID rclsid,LPUNKNOWN pUnkOuter,DWORD dwClsContext,REFIID riid,LPVOID *ppv)
862 {
863   IUnknown **ptr = (IUnknown **) ppv;
864
865   if(rclsid == CLSID_DirectSound && riid == IID_IDirectSound)
866   {
867     printLog("sound: emulating DirectSound (created via CoCreateInstance)\n");
868     *ptr = new MyDirectSound8;
869     return S_OK;
870   }
871   else if(rclsid == CLSID_DirectSound8 && riid == IID_IDirectSound8)
872   {
873     printLog("sound: emulating DirectSound 8 (created via CoCreateInstance)\n");
874     *ptr = new MyDirectSound8;
875     return S_OK;
876   }
877   else
878     return Real_CoCreateInstance(rclsid,pUnkOuter,dwClsContext,riid,ppv);
879 }
880
881 // --- now, waveout
882
883 class WaveOutImpl;
884 static WaveOutImpl *currentWaveOut = 0;
885
886 class WaveOutImpl
887 {
888   char MagicCookie[16];
889   WAVEFORMATEX *Format;
890   DWORD_PTR Callback;
891   DWORD_PTR CallbackInstance;
892   DWORD OpenFlags;
893   WAVEHDR *Head,*Current,*Tail;
894   bool Paused,InLoop;
895   int FirstFrame;
896   int FirstWriteFrame;
897   DWORD CurrentBufferPos;
898   DWORD CurrentSamplePos;
899
900   void callbackMessage(UINT uMsg,DWORD dwParam1,DWORD dwParam2)
901   {
902     switch(OpenFlags & CALLBACK_TYPEMASK)
903     {
904     case CALLBACK_EVENT:
905       SetEvent((HANDLE) Callback);
906       break;
907
908     case CALLBACK_FUNCTION:
909       ((PDRVCALLBACK) Callback)((HDRVR) this,uMsg,CallbackInstance,dwParam1,dwParam2);
910       break;
911
912     case CALLBACK_THREAD:
913       PostThreadMessage((DWORD) Callback,uMsg,(WPARAM) this,(LPARAM) dwParam1);
914       break;
915
916     case CALLBACK_WINDOW:
917       PostMessage((HWND) Callback,uMsg,(WPARAM) this,(LPARAM) dwParam1);
918       break;
919     }
920   }
921
922   void doneBuffer()
923   {
924     if(Head && Head == Current)
925     {
926       // mark current buffer as done and advance
927       Current->dwFlags = (Current->dwFlags & ~WHDR_INQUEUE) | WHDR_DONE;
928       callbackMessage(WOM_DONE,(DWORD) Current,0);
929
930       Current = Current->lpNext;
931       Head = Current;
932       if(!Head)
933         Tail = 0;
934     }
935     else
936       printLog("sound: inconsistent state in waveOut (this is a kkapture bug)\n");
937   }
938
939   void advanceBuffer()
940   {
941     // loops need seperate processing
942     if(InLoop && (Current->dwFlags & WHDR_ENDLOOP))
943     {
944       Current = Head;
945       if(!--Current->dwLoops)
946         InLoop = false;
947       return;
948     }
949
950     // current buffer is done, mark it as done and advance
951     if(!InLoop)
952       doneBuffer();
953     else
954       Current = Current->lpNext;
955
956     processCurrentBuffer();
957   }
958
959   void processCurrentBuffer()
960   {
961     // process beginloop flag because it may cause a state change
962     if(Current && (Current->dwFlags & WHDR_BEGINLOOP) && Current->dwLoops)
963       InLoop = true;
964   }
965
966 public:
967   WaveOutImpl(const WAVEFORMATEX *fmt,DWORD_PTR cb,DWORD_PTR cbInstance,DWORD fdwOpen)
968   {
969     videoNeedEncoder();
970
971     Format = CopyFormat(fmt);
972     Callback = cb;
973     CallbackInstance = cbInstance;
974     OpenFlags = fdwOpen;
975
976     Head = Current = Tail = 0;
977     CurrentBufferPos = 0;
978
979     Paused = false;
980     InLoop = false;
981     FirstFrame = -1;
982     FirstWriteFrame = -1;
983
984     CurrentSamplePos = 0;
985     memcpy(MagicCookie,"kkapture.waveout",16);
986
987     callbackMessage(WOM_OPEN,0,0);
988   }
989
990   ~WaveOutImpl()
991   {
992     delete Format;
993     callbackMessage(WOM_CLOSE,0,0);
994   }
995
996   bool amIReal() const
997   {
998     bool result = false;
999
1000     __try
1001     {
1002       result = memcmp(MagicCookie,"kkapture.waveout",16) == 0;
1003     }
1004     __except(EXCEPTION_EXECUTE_HANDLER)
1005     {
1006     }
1007
1008     return result;
1009   }
1010
1011   MMRESULT prepareHeader(WAVEHDR *hdr,UINT size)
1012   {
1013     if(!hdr || size != sizeof(WAVEHDR))
1014       return MMSYSERR_INVALPARAM;
1015
1016     hdr->dwFlags |= WHDR_PREPARED;
1017     return MMSYSERR_NOERROR;
1018   }
1019
1020   MMRESULT unprepareHeader(WAVEHDR *hdr,UINT size)
1021   {
1022     if(!hdr || size != sizeof(WAVEHDR))
1023       return MMSYSERR_INVALPARAM;
1024
1025     if(hdr->dwFlags & WHDR_INQUEUE)
1026       return WAVERR_STILLPLAYING;
1027
1028     hdr->dwFlags &= ~WHDR_PREPARED;
1029     return MMSYSERR_NOERROR;
1030   }
1031
1032   MMRESULT write(WAVEHDR *hdr,UINT size)
1033   {
1034     if(!hdr || size != sizeof(WAVEHDR))
1035       return MMSYSERR_INVALPARAM;
1036
1037     if(!(hdr->dwFlags & WHDR_PREPARED))
1038       return WAVERR_UNPREPARED;
1039
1040     if(hdr->dwFlags & WHDR_INQUEUE)
1041       return MMSYSERR_NOERROR;
1042
1043     // enqueue
1044     if(FirstFrame == -1) // officially start playback!
1045     {
1046       FirstFrame = getFrameTiming();
1047       FirstWriteFrame = FirstFrame;
1048       encoder->SetAudioFormat(Format);
1049       currentWaveOut = this;
1050     }
1051
1052     hdr->lpNext = 0;
1053     hdr->dwFlags = (hdr->dwFlags | WHDR_INQUEUE) & ~WHDR_DONE;
1054
1055     if(Tail)
1056     {
1057       Tail->lpNext = hdr;
1058       Tail = hdr;
1059     }
1060     else
1061     {
1062       Head = Current = Tail = hdr;
1063       processCurrentBuffer();
1064     }
1065
1066     return MMSYSERR_NOERROR;
1067   }
1068
1069   MMRESULT pause()
1070   {
1071     Paused = true;
1072     return MMSYSERR_NOERROR;
1073   }
1074
1075   MMRESULT restart()
1076   {
1077     Paused = FALSE;
1078     return MMSYSERR_NOERROR;
1079   }
1080
1081   MMRESULT reset()
1082   {
1083     while(Head)
1084       doneBuffer();
1085
1086     CurrentBufferPos = 0;
1087     CurrentSamplePos = 0;
1088
1089     Paused = false;
1090     InLoop = false;
1091     FirstFrame = -1;
1092     FirstWriteFrame = -1;
1093
1094     return MMSYSERR_NOERROR;
1095   }
1096
1097   MMRESULT message(UINT uMsg,DWORD dwParam1,DWORD dwParam2)
1098   {
1099     return 0;
1100   }
1101
1102   MMRESULT getPosition(MMTIME *mmt,UINT size)
1103   {
1104     if(!mmt || size < sizeof(MMTIME))
1105     {
1106       printLog("sound: invalid param to getPosition");
1107       return MMSYSERR_INVALPARAM;
1108     }
1109
1110     if(size > sizeof(MMTIME))
1111     {
1112       static bool warnedAboutMMTime = false;
1113       if(!warnedAboutMMTime)
1114       {
1115         printLog("sound: MMTIME structure passed to waveOutGetPosition is too large, ignoring extra fields (will only report this once).\n");
1116         warnedAboutMMTime = true;
1117       }
1118     }
1119
1120     if(mmt->wType != TIME_BYTES && mmt->wType != TIME_SAMPLES && mmt->wType != TIME_MS)
1121     {
1122       printLog("sound: unsupported timecode format, defaulting to bytes.\n");
1123       mmt->wType = TIME_BYTES;
1124       return MMSYSERR_INVALPARAM;
1125     }
1126
1127     // current frame (offset corrected)
1128     DWORD now;
1129     int relFrame = getFrameTiming() - FirstFrame;
1130
1131     // calc time in requested format
1132     switch(mmt->wType)
1133     {
1134     case TIME_BYTES:
1135     case TIME_SAMPLES:
1136     case TIME_MS:
1137       now = UMulDiv(relFrame,Format->nSamplesPerSec * frameRateDenom,frameRateScaled);
1138       if(mmt->wType == TIME_BYTES || mmt->wType == TIME_MS) // yes, TIME_MS seems to return *bytes*. WHATEVER.
1139         mmt->u.cb = now * Format->nBlockAlign;
1140       else if(mmt->wType == TIME_SAMPLES)
1141         mmt->u.sample = now;
1142       break;
1143     }
1144
1145     return MMSYSERR_NOERROR;
1146   }
1147
1148   // ----
1149   void encodeNoAudio(DWORD sampleCount)
1150   {
1151     // no new/delete, we do not know from where this might be called
1152     void *buffer = _alloca(256 * Format->nBlockAlign);
1153     memset(buffer,0,256 * Format->nBlockAlign);
1154
1155     while(sampleCount)
1156     {
1157       int sampleBlock = min(sampleCount,256);
1158       encoder->WriteAudioFrame(buffer,sampleBlock);
1159       sampleCount -= sampleBlock;
1160     }
1161   }
1162
1163   void processFrame()
1164   {
1165     // calculate number of samples to write
1166     int frame = getFrameTiming() - FirstWriteFrame;
1167     int align = Format->nBlockAlign;
1168
1169     DWORD sampleNew = UMulDiv(frame,Format->nSamplesPerSec * frameRateDenom,frameRateScaled);
1170     DWORD sampleCount = sampleNew - CurrentSamplePos;
1171
1172     if(!Current || Paused) // write one frame of no audio
1173     {
1174       encodeNoAudio(sampleCount);
1175
1176       // also fix write frame timing
1177       FirstFrame++;
1178     }
1179     else // we have audio playing, so consume buffers as long as possible
1180     {
1181       while(sampleCount && Current)
1182       {
1183         int smps = min(sampleCount,(Current->dwBufferLength - CurrentBufferPos) / align);
1184         if(smps)
1185           encoder->WriteAudioFrame((PBYTE) Current->lpData + CurrentBufferPos,smps);
1186
1187         sampleCount -= smps;
1188         CurrentBufferPos += smps * align;
1189         if(CurrentBufferPos >= Current->dwBufferLength) // buffer done
1190         {
1191           advanceBuffer();
1192           CurrentBufferPos = 0;
1193         }
1194       }
1195
1196       if(sampleCount && !Current) // ran out of audio data
1197         encodeNoAudio(sampleCount);
1198     }
1199
1200     CurrentSamplePos = sampleNew;
1201   }
1202 };
1203
1204 DETOUR_TRAMPOLINE(MMRESULT __stdcall Real_waveOutOpen(LPHWAVEOUT phwo,UINT uDeviceID,LPCWAVEFORMATEX pwfx,DWORD_PTR dwCallback,DWORD_PTR dwInstance,DWORD fdwOpen), waveOutOpen);
1205 DETOUR_TRAMPOLINE(MMRESULT __stdcall Real_waveOutClose(HWAVEOUT hwo), waveOutClose);
1206 DETOUR_TRAMPOLINE(MMRESULT __stdcall Real_waveOutPrepareHeader(HWAVEOUT hwo,LPWAVEHDR pwh,UINT cbwh), waveOutPrepareHeader);
1207 DETOUR_TRAMPOLINE(MMRESULT __stdcall Real_waveOutUnprepareHeader(HWAVEOUT hwo,LPWAVEHDR pwh,UINT cbwh), waveOutUnprepareHeader);
1208 DETOUR_TRAMPOLINE(MMRESULT __stdcall Real_waveOutWrite(HWAVEOUT hwo,LPWAVEHDR pwh,UINT cbwh), waveOutWrite);
1209 DETOUR_TRAMPOLINE(MMRESULT __stdcall Real_waveOutPause(HWAVEOUT hwo), waveOutPause);
1210 DETOUR_TRAMPOLINE(MMRESULT __stdcall Real_waveOutRestart(HWAVEOUT hwo), waveOutRestart);
1211 DETOUR_TRAMPOLINE(MMRESULT __stdcall Real_waveOutReset(HWAVEOUT hwo), waveOutReset);
1212 DETOUR_TRAMPOLINE(MMRESULT __stdcall Real_waveOutMessage(HWAVEOUT hwo,UINT uMsg,DWORD_PTR dw1,DWORD_PTR dw2), waveOutMessage);
1213 DETOUR_TRAMPOLINE(MMRESULT __stdcall Real_waveOutGetPosition(HWAVEOUT hwo,LPMMTIME pmmt,UINT cbmmt), waveOutGetPosition);
1214 DETOUR_TRAMPOLINE(MMRESULT __stdcall Real_waveOutGetDevCaps(UINT_PTR uDeviceId,LPWAVEOUTCAPS pwo,UINT cbwoc), waveOutGetDevCaps);
1215 DETOUR_TRAMPOLINE(MMRESULT __stdcall Real_waveOutGetNumDevs(), waveOutGetNumDevs);
1216
1217 static WaveOutImpl *waveOutLast = 0;
1218
1219 static WaveOutImpl *GetWaveOutImpl(HWAVEOUT hwo)
1220 {
1221   if(hwo)
1222     return (WaveOutImpl *) hwo;
1223
1224   return waveOutLast;
1225 }
1226
1227 MMRESULT __stdcall Mine_waveOutOpen(LPHWAVEOUT phwo,UINT uDeviceID,LPCWAVEFORMATEX pwfx,DWORD_PTR dwCallback,DWORD_PTR dwInstance,DWORD fdwOpen)
1228 {
1229   TRACE(("waveOutOpen(%p,%u,%p,%p,%p,%u)\n",phwo,uDeviceID,pwfx,dwCallback,dwInstance,fdwOpen));
1230
1231   if(phwo)
1232   {
1233     printLog("sound: waveOutOpen %08x (%d hz, %d bits, %d channels)\n",
1234       uDeviceID,pwfx->nSamplesPerSec,pwfx->wBitsPerSample,pwfx->nChannels);
1235
1236     WaveOutImpl *impl = new WaveOutImpl(pwfx,dwCallback,dwInstance,fdwOpen);
1237     waveOutLast = impl;
1238     *phwo = (HWAVEOUT) impl;
1239
1240     return MMSYSERR_NOERROR;
1241   }
1242   else
1243   {
1244     if(fdwOpen & WAVE_FORMAT_QUERY)
1245       return MMSYSERR_NOERROR;
1246     else
1247       return MMSYSERR_INVALPARAM;
1248   }
1249 }
1250
1251 MMRESULT __stdcall Mine_waveOutClose(HWAVEOUT hwo)
1252 {
1253   TRACE(("waveOutClose(%p)\n",hwo));
1254
1255   printLog("sound: waveOutClose %08x\n",hwo);
1256
1257   WaveOutImpl *impl = GetWaveOutImpl(hwo);
1258   if(impl)
1259   {
1260     delete impl;
1261     if(impl == waveOutLast)
1262       waveOutLast = 0;
1263
1264     return MMSYSERR_NOERROR;
1265   }
1266   else
1267     return MMSYSERR_INVALHANDLE;
1268 }
1269
1270 MMRESULT __stdcall Mine_waveOutPrepareHeader(HWAVEOUT hwo,LPWAVEHDR pwh,UINT cbwh)
1271 {
1272   TRACE(("waveOutPrepareHeader(%p,%p,%u)\n",hwo,pwh,cbwh));
1273   WaveOutImpl *impl = GetWaveOutImpl(hwo);
1274   return impl ? impl->prepareHeader(pwh,cbwh) : MMSYSERR_INVALHANDLE;
1275 }
1276
1277 MMRESULT __stdcall Mine_waveOutUnprepareHeader(HWAVEOUT hwo,LPWAVEHDR pwh,UINT cbwh)
1278 {
1279   TRACE(("waveOutUnprepareHeader(%p,%p,%u)\n",hwo,pwh,cbwh));
1280   WaveOutImpl *impl = GetWaveOutImpl(hwo);
1281   return impl ? impl->unprepareHeader(pwh,cbwh) : MMSYSERR_INVALHANDLE;
1282 }
1283
1284 MMRESULT __stdcall Mine_waveOutWrite(HWAVEOUT hwo,LPWAVEHDR pwh,UINT cbwh)
1285 {
1286   TRACE(("waveOutWrite(%p,%p,%u)\n",hwo,pwh,cbwh));
1287   WaveOutImpl *impl = GetWaveOutImpl(hwo);
1288   return impl ? impl->write(pwh,cbwh) : MMSYSERR_INVALHANDLE;
1289 }
1290
1291 MMRESULT __stdcall Mine_waveOutPause(HWAVEOUT hwo)
1292 {
1293   TRACE(("waveOutPause(%p)\n",hwo));
1294   WaveOutImpl *impl = GetWaveOutImpl(hwo);
1295   return impl ? impl->pause() : MMSYSERR_INVALHANDLE;
1296 }
1297
1298 MMRESULT __stdcall Mine_waveOutRestart(HWAVEOUT hwo)
1299 {
1300   TRACE(("waveOutRestart(%p)\n",hwo));
1301   WaveOutImpl *impl = GetWaveOutImpl(hwo);
1302   return impl ? impl->restart() : MMSYSERR_INVALHANDLE;
1303 }
1304
1305 MMRESULT __stdcall Mine_waveOutReset(HWAVEOUT hwo)
1306 {
1307   TRACE(("waveOutReset(%p)\n",hwo));
1308   WaveOutImpl *impl = GetWaveOutImpl(hwo);
1309   return impl ? impl->reset() : MMSYSERR_INVALHANDLE;
1310 }
1311
1312 MMRESULT __stdcall Mine_waveOutMessage(HWAVEOUT hwo,UINT uMsg,DWORD_PTR dw1,DWORD_PTR dw2)
1313 {
1314   TRACE(("waveOutMessage(%p,%u,%p,%p)\n",hwo,uMsg,dw1,dw2));
1315   WaveOutImpl *impl = GetWaveOutImpl(hwo);
1316   return impl ? impl->message(uMsg,(DWORD) dw1,(DWORD) dw2) : MMSYSERR_INVALHANDLE;
1317 }
1318
1319 MMRESULT __stdcall Mine_waveOutGetPosition(HWAVEOUT hwo,LPMMTIME pmmt,UINT cbmmt)
1320 {
1321   TRACE(("waveOutGetPosition(%p,%p,%u)\n",hwo,pmmt,cbmmt));
1322   WaveOutImpl *impl = GetWaveOutImpl(hwo);
1323   return impl ? impl->getPosition(pmmt,cbmmt) : MMSYSERR_INVALHANDLE;
1324 }
1325
1326 MMRESULT __stdcall Mine_waveOutGetDevCaps(UINT_PTR uDeviceID,LPWAVEOUTCAPS pwoc,UINT cbwoc)
1327 {
1328   TRACE(("waveOutGetDevCaps(%p,%p,%u)\n",uDeviceID,pwoc,cbwoc));
1329
1330   WaveOutImpl *impl;
1331
1332   if(uDeviceID == WAVE_MAPPER || uDeviceID == 0)
1333     impl = waveOutLast;
1334   else if(uDeviceID < 0x10000)
1335     return MMSYSERR_BADDEVICEID;
1336   else
1337   {
1338     impl = (WaveOutImpl *) uDeviceID;
1339     if(!impl->amIReal())
1340       return MMSYSERR_NODRIVER;
1341   }
1342
1343   if(cbwoc < sizeof(WAVEOUTCAPS))
1344     return MMSYSERR_INVALPARAM;
1345
1346   pwoc->wMid = MM_MICROSOFT;
1347   pwoc->wPid = (uDeviceID == WAVE_MAPPER) ? MM_WAVE_MAPPER : MM_MSFT_GENERIC_WAVEOUT;
1348   pwoc->vDriverVersion = 0x100;
1349   strcpy(pwoc->szPname,".kkapture Audio");
1350   pwoc->dwFormats = WAVE_FORMAT_1M08 | WAVE_FORMAT_1M16 | WAVE_FORMAT_1S08 | WAVE_FORMAT_1S16
1351     | WAVE_FORMAT_2M08 | WAVE_FORMAT_2M16 | WAVE_FORMAT_2S08 | WAVE_FORMAT_2S16
1352     | WAVE_FORMAT_4M08 | WAVE_FORMAT_4M16 | WAVE_FORMAT_4S08 | WAVE_FORMAT_4S16;
1353   pwoc->wChannels = 2;
1354   pwoc->wReserved1 = 0;
1355   pwoc->dwSupport = WAVECAPS_PLAYBACKRATE | WAVECAPS_SAMPLEACCURATE;
1356
1357   return MMSYSERR_NOERROR;
1358 }
1359
1360 UINT __stdcall Mine_waveOutGetNumDevs()
1361 {
1362   TRACE(("waveOutGetNumDevs()\n"));
1363   return 1;
1364 }
1365
1366 // ---- BASS
1367
1368 typedef BOOL (__stdcall *PBASS_INIT)(int device,DWORD freq,DWORD flags,HWND win,GUID *clsid);
1369
1370 static PBASS_INIT Real_BASS_Init = 0;
1371
1372 static BOOL __stdcall Mine_BASS_Init(int device,DWORD freq,DWORD flags,HWND win,GUID *clsid)
1373 {
1374   // for BASS, all we need to do is make sure that the BASS_DEVICE_LATENCY flag is cleared.
1375   return Real_BASS_Init(device,freq,flags & ~256,win,clsid);
1376 }
1377
1378 static void initSoundsysBASS()
1379 {
1380   HMODULE bassDll = LoadLibraryA("bass.dll");
1381   if(bassDll)
1382   {
1383     PBASS_INIT init = (PBASS_INIT) GetProcAddress(bassDll,"BASS_Init");
1384
1385     if(init)
1386     {
1387       printLog("sound/bass: bass.dll found, BASS support enabled.\n");
1388       Real_BASS_Init = (PBASS_INIT) DetourFunction((PBYTE) init,(PBYTE) Mine_BASS_Init);
1389     }
1390   }
1391 }
1392
1393 // ---- FMOD 3.xx
1394
1395 typedef int (__stdcall *PFSOUND_STREAM_PLAY)(int channel,void *stream);
1396 typedef int (__stdcall *PFSOUND_STREAM_PLAYEX)(int channel,void *stream,void *dspunit,signed char paused);
1397 typedef signed char (__stdcall *PFSOUND_STREAM_STOP)(void *stream);
1398 typedef int (__stdcall *PFSOUND_STREAM_GETTIME)(void *stream);
1399 typedef int (__stdcall *PFSOUND_PLAYSOUND)(int channel,void *sample);
1400 typedef int (__stdcall *PFSOUND_GETFREQUENCY)(int channel);
1401 typedef unsigned int (__stdcall *PFSOUND_GETCURRENTPOSITION)(int channel);
1402
1403 static PFSOUND_STREAM_PLAY Real_FSOUND_Stream_Play = 0;
1404 static PFSOUND_STREAM_PLAYEX Real_FSOUND_Stream_PlayEx = 0;
1405 static PFSOUND_STREAM_STOP Real_FSOUND_Stream_Stop = 0;
1406 static PFSOUND_STREAM_GETTIME Real_FSOUND_Stream_GetTime = 0;
1407 static PFSOUND_PLAYSOUND Real_FSOUND_PlaySound = 0;
1408 static PFSOUND_GETFREQUENCY Real_FSOUND_GetFrequency = 0;
1409 static PFSOUND_GETCURRENTPOSITION Real_FSOUND_GetCurrentPosition = 0;
1410
1411 static void *FMODStart = 0, *FMODEnd = 0;
1412
1413 #define CalledFromFMOD() (_ReturnAddress() >= FMODStart && _ReturnAddress() < FMODEnd) // needs to be a macro
1414
1415 struct FMODStreamDesc
1416 {
1417   void *stream;
1418   int channel;
1419   int firstFrame;
1420   int frequency;
1421 };
1422
1423 static const int FMODNumStreams = 16; // max # of active (playing) streams supported
1424 static FMODStreamDesc FMODStreams[FMODNumStreams];
1425
1426 static void RegisterFMODStream(void *stream,int channel)
1427 {
1428   if(channel == -1) // error from fmod
1429     return;
1430
1431   // find a free stream desc
1432   int index = 0;
1433   while(index<FMODNumStreams && FMODStreams[index].stream)
1434     index++;
1435
1436   if(index==FMODNumStreams)
1437   {
1438     printLog("sound/fmod: more than %d streams playing, ran out of handles.\n",FMODNumStreams);
1439     return;
1440   }
1441
1442   FMODStreams[index].stream = stream;
1443   FMODStreams[index].channel = channel;
1444   FMODStreams[index].firstFrame = getFrameTiming();
1445   FMODStreams[index].frequency = Real_FSOUND_GetFrequency(channel);
1446   printLog("sound/fmod: stream playing on channel %08x with frequency %d Hz.\n",channel,FMODStreams[index].frequency);
1447 }
1448
1449 static FMODStreamDesc *FMODStreamFromChannel(int chan)
1450 {
1451   for(int i=0;i<FMODNumStreams;i++)
1452     if(FMODStreams[i].stream && FMODStreams[i].channel == chan)
1453       return &FMODStreams[i];
1454
1455   return 0;
1456 }
1457
1458 static FMODStreamDesc *FMODStreamFromPtr(void *ptr)
1459 {
1460   for(int i=0;i<FMODNumStreams;i++)
1461     if(FMODStreams[i].stream == ptr)
1462       return &FMODStreams[i];
1463
1464   return 0;
1465 }
1466
1467 static int __stdcall Mine_FSOUND_Stream_Play(int channel,void *stream)
1468 {
1469   printLog("sound/fmod: StreamPlay\n");
1470   int realChan = Real_FSOUND_Stream_Play(channel,stream);
1471   RegisterFMODStream(stream,realChan);
1472
1473   return realChan;
1474 }
1475
1476 static int __stdcall Mine_FSOUND_Stream_PlayEx(int channel,void *stream,void *dspunit,signed char paused)
1477 {
1478   printLog("sound/fmod: StreamPlayEx(%08x,%p,%p,%d)\n",channel,stream,dspunit,paused);
1479   int realChan = Real_FSOUND_Stream_PlayEx(channel,stream,dspunit,paused);
1480   RegisterFMODStream(stream,realChan);
1481
1482   return realChan;
1483 }
1484
1485 static int __stdcall Mine_FSOUND_PlaySound(int channel,void *sample)
1486 {
1487   printLog("sound/fmod: PlaySound(%08x,%p)\n",channel,sample);
1488   int realChan = Real_FSOUND_PlaySound(channel,sample);
1489   RegisterFMODStream(sample,realChan);
1490
1491   return realChan;
1492 }
1493
1494 static signed char __stdcall Mine_FSOUND_Stream_Stop(void *stream)
1495 {
1496   printLog("sound/fmod: StreamStop(%p)\n",stream);
1497   FMODStreamDesc *desc = FMODStreamFromPtr(stream);
1498   if(desc)
1499     desc->stream = 0;
1500
1501   return Real_FSOUND_Stream_Stop(stream);
1502 }
1503
1504 static int __stdcall Mine_FSOUND_Stream_GetTime(void *stream)
1505 {
1506   FMODStreamDesc *desc = FMODStreamFromPtr(stream);
1507   if(desc)
1508   {
1509     unsigned int time = UMulDiv(getFrameTiming() - desc->firstFrame,1000*frameRateDenom,frameRateScaled);
1510     return time;
1511   }
1512
1513   return Real_FSOUND_Stream_GetTime(stream);
1514 }
1515
1516 static unsigned int __stdcall Mine_FSOUND_GetCurrentPosition(int channel)
1517 {
1518   FMODStreamDesc *desc = FMODStreamFromChannel(channel);
1519   if(!CalledFromFMOD() && desc)
1520   {
1521     // this is a known stream playing; return time based on frame timing.
1522     // (to work around FMODs stupidly coarse timer resolution)
1523     unsigned int time = UMulDiv(getFrameTiming() - desc->firstFrame,desc->frequency*frameRateDenom,frameRateScaled);
1524     return time;
1525   }
1526
1527   return Real_FSOUND_GetCurrentPosition(channel);
1528 }
1529
1530 static void initSoundsysFMOD3()
1531 {
1532   HMODULE fmodDll = LoadLibraryA("fmod.dll");
1533   if(fmodDll)
1534   {
1535     if(GetProcAddress(fmodDll,"_FSOUND_Stream_Play@8"))
1536     {
1537       MODULEINFO moduleInfo;
1538       GetModuleInformation(GetCurrentProcess(),fmodDll,&moduleInfo,sizeof(moduleInfo));
1539
1540       FMODStart = moduleInfo.lpBaseOfDll;
1541       FMODEnd = (void*) ((BYTE*) moduleInfo.lpBaseOfDll + moduleInfo.SizeOfImage);
1542
1543       printLog("sound/FMOD3: fmod.dll found, FMOD support enabled.\n");
1544       Real_FSOUND_Stream_Play = (PFSOUND_STREAM_PLAY) DetourFunction((PBYTE) GetProcAddress(fmodDll,"_FSOUND_Stream_Play@8"),(PBYTE) Mine_FSOUND_Stream_Play);
1545       Real_FSOUND_Stream_PlayEx = (PFSOUND_STREAM_PLAYEX) DetourFunction((PBYTE) GetProcAddress(fmodDll,"_FSOUND_Stream_PlayEx@16"),(PBYTE) Mine_FSOUND_Stream_PlayEx);
1546       Real_FSOUND_Stream_Stop = (PFSOUND_STREAM_STOP) DetourFunction((PBYTE) GetProcAddress(fmodDll,"_FSOUND_Stream_Stop@4"),(PBYTE) Mine_FSOUND_Stream_Stop);
1547       Real_FSOUND_Stream_GetTime = (PFSOUND_STREAM_GETTIME) DetourFunction((PBYTE) GetProcAddress(fmodDll,"_FSOUND_Stream_GetTime@4"),(PBYTE) Mine_FSOUND_Stream_GetTime);
1548       Real_FSOUND_PlaySound = (PFSOUND_PLAYSOUND) DetourFunction((PBYTE) GetProcAddress(fmodDll,"_FSOUND_PlaySound@8"),(PBYTE) Mine_FSOUND_PlaySound);
1549       Real_FSOUND_GetFrequency = (PFSOUND_GETFREQUENCY) GetProcAddress(fmodDll,"_FSOUND_GetFrequency@4");
1550       Real_FSOUND_GetCurrentPosition = (PFSOUND_GETCURRENTPOSITION) DetourFunction((PBYTE) GetProcAddress(fmodDll,"_FSOUND_GetCurrentPosition@4"),(PBYTE) Mine_FSOUND_GetCurrentPosition);
1551     }
1552   }
1553 }
1554
1555 // ---- FMODEx 4.xx
1556
1557 typedef int (__stdcall *PSYSTEM_PLAYSOUND)(void *sys,int index,void *sound,bool paused,void **channel);
1558 typedef int (__stdcall *PCHANNEL_GETFREQUENCY)(void *chan,float *freq);
1559 typedef int (__stdcall *PCHANNEL_GETPOSITION)(void *chan,unsigned *position,int posType);
1560
1561 static PSYSTEM_PLAYSOUND Real_System_playSound;
1562 static PCHANNEL_GETFREQUENCY Real_Channel_getFrequency;
1563 static PCHANNEL_GETPOSITION Real_Channel_getPosition;
1564
1565 static void *FMODExStart = 0, *FMODExEnd = 0;
1566
1567 #define CalledFromFMODEx() (_ReturnAddress() >= FMODExStart && _ReturnAddress() < FMODExEnd) // needs to be a macro
1568
1569 struct FMODExSoundDesc
1570 {
1571   void *sound;
1572   void *channel;
1573   int firstFrame;
1574   float frequency;
1575 };
1576
1577 static const int FMODExNumSounds = 16; // max # of active (playing) sounds supported
1578 static FMODExSoundDesc FMODExSounds[FMODExNumSounds];
1579
1580 static int __stdcall Mine_System_playSound(void *sys,int index,void *sound,bool paused,void **channel)
1581 {
1582   void *chan;
1583   printLog("sound/fmodex: playSound\n");
1584   int result = Real_System_playSound(sys,index,sound,paused,&chan);
1585   if(result == 0) // FMOD_OK
1586   {
1587     // find a free sound desc
1588     int index = 0;
1589     while(index<FMODExNumSounds && FMODExSounds[index].sound)
1590       index++;
1591
1592     if(index==FMODExNumSounds)
1593       printLog("sound/fmodex: more than %d sounds playing, ran out of handles.\n",FMODExNumSounds);
1594     else
1595     {
1596       FMODExSounds[index].sound = sound;
1597       FMODExSounds[index].channel = chan;
1598       FMODExSounds[index].firstFrame = getFrameTiming();
1599       Real_Channel_getFrequency(chan,&FMODExSounds[index].frequency);
1600     }
1601   }
1602
1603   if(channel)
1604     *channel = chan;
1605
1606   return result;
1607 }
1608
1609 static FMODExSoundDesc *FMODExSoundFromChannel(void *chan)
1610 {
1611   for(int i=0;i<FMODExNumSounds;i++)
1612     if(FMODExSounds[i].sound && FMODExSounds[i].channel == chan)
1613       return &FMODExSounds[i];
1614
1615   return 0;
1616 }
1617
1618 static int __stdcall Mine_Channel_getPosition(void *channel,unsigned *position,int posType)
1619 {
1620   FMODExSoundDesc *desc = FMODExSoundFromChannel(channel);
1621   if(desc && !CalledFromFMODEx())
1622   {
1623     int timeBase;
1624
1625     switch(posType)
1626     {
1627     case 1:   timeBase = 1000; break;                   // FMOD_TIMEUNIT_MS
1628     case 2:   timeBase = (int) desc->frequency; break;  // FMOD_TIMEUNIT_PCM
1629     default:  timeBase = -1; break;
1630     }
1631
1632     if(timeBase != -1)
1633     {
1634       *position = UMulDiv(getFrameTiming() - desc->firstFrame,timeBase*frameRateDenom,frameRateScaled);
1635       return 0; // FMOD_OK
1636     }
1637     else
1638       printLog("sound/fmodex: unsupported timebase used in Channel::getPosition, forwarding to fmod timer.\n");
1639   }
1640
1641   return Real_Channel_getPosition(channel,position,posType);
1642 }
1643
1644 static void initSoundsysFMODEx()
1645 {
1646   HMODULE fmodDll = LoadLibraryA("fmodex.dll");
1647   if(fmodDll)
1648   {
1649     if(GetProcAddress(fmodDll,"FMOD_System_Init"))
1650     {
1651       MODULEINFO moduleInfo;
1652       GetModuleInformation(GetCurrentProcess(),fmodDll,&moduleInfo,sizeof(moduleInfo));
1653
1654       FMODExStart = moduleInfo.lpBaseOfDll;
1655       FMODExEnd = (void*) ((BYTE*) moduleInfo.lpBaseOfDll + moduleInfo.SizeOfImage);
1656
1657       printLog("sound/fmodex: fmodex.dll found, FMODEx support enabled.\n");
1658
1659       Real_System_playSound = (PSYSTEM_PLAYSOUND) DetourFunction((PBYTE) GetProcAddress(fmodDll,"?playSound@System@FMOD@@QAG?AW4FMOD_RESULT@@W4FMOD_CHANNELINDEX@@PAVSound@2@_NPAPAVChannel@2@@Z"),(PBYTE) Mine_System_playSound);
1660       Real_Channel_getFrequency = (PCHANNEL_GETFREQUENCY) GetProcAddress(fmodDll,"?getFrequency@Channel@FMOD@@QAG?AW4FMOD_RESULT@@PAM@Z");
1661       Real_Channel_getPosition = (PCHANNEL_GETPOSITION) DetourFunction((PBYTE) GetProcAddress(fmodDll,"?getPosition@Channel@FMOD@@QAG?AW4FMOD_RESULT@@PAII@Z"),(PBYTE) Mine_Channel_getPosition);
1662     }
1663   }
1664 }
1665
1666 // ----
1667
1668 void initSound()
1669 {
1670   DetourFunctionWithTrampoline((PBYTE) Real_DirectSoundCreate,(PBYTE) Mine_DirectSoundCreate);
1671   DetourFunctionWithTrampoline((PBYTE) Real_DirectSoundCreate8,(PBYTE) Mine_DirectSoundCreate8);
1672
1673   DetourFunctionWithTrampoline((PBYTE) Real_CoCreateInstance,(PBYTE) Mine_CoCreateInstance);
1674
1675   DetourFunctionWithTrampoline((PBYTE) Real_waveOutOpen,(PBYTE) Mine_waveOutOpen);
1676   DetourFunctionWithTrampoline((PBYTE) Real_waveOutClose,(PBYTE) Mine_waveOutClose);
1677   DetourFunctionWithTrampoline((PBYTE) Real_waveOutPrepareHeader,(PBYTE) Mine_waveOutPrepareHeader);
1678   DetourFunctionWithTrampoline((PBYTE) Real_waveOutUnprepareHeader,(PBYTE) Mine_waveOutUnprepareHeader);
1679   DetourFunctionWithTrampoline((PBYTE) Real_waveOutWrite,(PBYTE) Mine_waveOutWrite);
1680   DetourFunctionWithTrampoline((PBYTE) Real_waveOutPause,(PBYTE) Mine_waveOutPause);
1681   DetourFunctionWithTrampoline((PBYTE) Real_waveOutReset,(PBYTE) Mine_waveOutReset);
1682   DetourFunctionWithTrampoline((PBYTE) Real_waveOutRestart,(PBYTE) Mine_waveOutRestart);
1683   DetourFunctionWithTrampoline((PBYTE) Real_waveOutMessage,(PBYTE) Mine_waveOutMessage);
1684   DetourFunctionWithTrampoline((PBYTE) Real_waveOutGetPosition,(PBYTE) Mine_waveOutGetPosition);
1685   DetourFunctionWithTrampoline((PBYTE) Real_waveOutGetDevCaps, (PBYTE) Mine_waveOutGetDevCaps);
1686   DetourFunctionWithTrampoline((PBYTE) Real_waveOutGetNumDevs, (PBYTE) Mine_waveOutGetNumDevs);
1687
1688   if(params.SoundsysInterception)
1689   {
1690     initSoundsysBASS();
1691     initSoundsysFMOD3();
1692     initSoundsysFMODEx();
1693   }
1694 }
1695
1696 void doneSound()
1697 {
1698 }
1699
1700 void nextFrameSound()
1701 {
1702   if(playBuffer)
1703     playBuffer->encodeLastFrameAudio();
1704   else if(currentWaveOut)
1705     currentWaveOut->processFrame();
1706 }