home *** CD-ROM | disk | FTP | other *** search
/ Large Pack of OldSkool DOS MOD Trackers / buzzmachines_massive.exe / Dev / Waveout / WaveOut.cpp < prev    next >
Encoding:
C/C++ Source or Header  |  2001-08-26  |  9.2 KB  |  488 lines

  1. // waveout stuff based on old midas code
  2.  
  3. #define VC_EXTRALEAN
  4.  
  5. #include <afxwin.h>
  6. #include <afxcmn.h>
  7. #include <mmsystem.h>
  8. #include <stdio.h>
  9. #include <process.h>
  10. #include <assert.h>
  11. #include "../AudioDriver.h"
  12. #include "resource.h"
  13. #include "ConfigDlg.h"
  14.  
  15. #define MAX_WAVEOUT_BLOCKS        8
  16. #define BYTES_PER_SAMPLE        4    // 2 * 16bits
  17.  
  18. class CBlock
  19. {
  20. public:
  21.     HANDLE Handle;
  22.     byte *pData;
  23.     WAVEHDR *pHeader;
  24.     HANDLE HeaderHandle;
  25.     bool Prepared;
  26. };
  27.  
  28. class ad : public CAudioDriver
  29. {
  30. public:
  31.     ad();
  32.     virtual ~ad();
  33.     virtual void Initialize(dword hwnd, float *(*pcallback)(int &numsamples));
  34.     virtual void Reset();
  35.     virtual bool Enable(bool e);    
  36.     virtual int GetWritePos();
  37.     virtual int GetPlayPos();
  38.     virtual void Configure();
  39.  
  40.     void DoBlocks();
  41.  
  42. private:
  43.     bool Start();    
  44.     bool Stop();
  45.     void ReadConfig();
  46.     void WriteConfig();
  47.     void Error(char const *msg);
  48.  
  49. public:
  50.     HWAVEOUT Handle;
  51.     int DeviceID;
  52.     int numBlocks;
  53.     int BlockSize;
  54.     int CurrentBlock;
  55.     int WritePos;
  56.     int PollSleep;
  57.     int Dither;
  58.     CBlock Blocks[MAX_WAVEOUT_BLOCKS];
  59.     float *(*pCallback)(int &numsamples);
  60.     bool Initialized;
  61.     bool Running;
  62.     bool StopPolling;
  63.  
  64. };
  65.  
  66. static void __cdecl PollerThread(void *poo)
  67. {
  68.     ad *pad = (ad *)poo;
  69.  
  70.     SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_ABOVE_NORMAL);
  71.  
  72.     pad->StopPolling = false;
  73.     
  74.     while(!pad->StopPolling)
  75.     {
  76.         pad->DoBlocks();
  77.         Sleep(pad->PollSleep);
  78.     }
  79.  
  80.     pad->StopPolling = false;
  81.  
  82.     _endthread();
  83. }
  84.  
  85. ad::ad()
  86. {
  87.     Initialized = false;
  88. }
  89.  
  90.  
  91. ad::~ad()
  92. {
  93.     Reset();
  94. }
  95.  
  96. void ad::Error(char const *msg)
  97. {
  98.     MessageBox(NULL, msg, "WaveOut driver", MB_OK);
  99. }
  100.  
  101. bool ad::Start()
  102. {
  103.     if (Running)
  104.         return true;
  105.  
  106.     WAVEFORMATEX format;
  107.     format.wFormatTag = WAVE_FORMAT_PCM;
  108.     format.nChannels = (Flags & ADF_STEREO) ? 2 : 1;
  109.     format.wBitsPerSample = 16;
  110.     format.nSamplesPerSec = SamplesPerSec;
  111.     format.nBlockAlign = format.nChannels * format.wBitsPerSample / 8;
  112.     format.nAvgBytesPerSec = format.nSamplesPerSec * format.nBlockAlign;
  113.     format.cbSize = 0;
  114.  
  115.     if (waveOutOpen(&Handle, DeviceID, &format, NULL, 0, CALLBACK_NULL) != MMSYSERR_NOERROR)
  116.     {
  117.         Error("waveOutOpen() failed");
  118.         return false;
  119.     }
  120.  
  121.     CurrentBlock = 0;
  122.     WritePos = 0;
  123.  
  124.     // allocate blocks
  125.     for (CBlock *pBlock = Blocks; pBlock < Blocks + numBlocks; pBlock++)
  126.     {
  127.         pBlock->Handle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, BlockSize);
  128.         pBlock->pData = (byte *)GlobalLock(pBlock->Handle);
  129.     }
  130.  
  131.     // allocate block headers
  132.     for (pBlock = Blocks; pBlock < Blocks + numBlocks; pBlock++)
  133.     {
  134.         pBlock->HeaderHandle = GlobalAlloc(GMEM_MOVEABLE | GMEM_SHARE, sizeof(WAVEHDR));
  135.         pBlock->pHeader = (WAVEHDR *)GlobalLock(pBlock->HeaderHandle);
  136.  
  137.         WAVEHDR *ph = pBlock->pHeader;
  138.         ph->lpData = (char *)pBlock->pData;
  139.         ph->dwBufferLength = BlockSize;
  140.         ph->dwFlags = WHDR_DONE;
  141.         ph->dwLoops = 0;
  142.  
  143.         pBlock->Prepared = false;
  144.     }
  145.     
  146.     HANDLE h = (HANDLE)_beginthread(PollerThread, 0, this);
  147.  
  148.     /*
  149.     BOOL ret = SetThreadPriority(h, THREAD_PRIORITY_ABOVE_NORMAL);
  150.     assert(ret);
  151. */    
  152.  
  153.     Running = true;
  154.     return true;
  155. }
  156.  
  157. bool ad::Stop()
  158. {
  159.     if (!Running)
  160.         return true;
  161.  
  162.     StopPolling = true;
  163.     while(StopPolling)
  164.         Sleep(PollSleep);
  165.  
  166.     if (waveOutReset(Handle) != MMSYSERR_NOERROR)
  167.     {
  168.         Error("waveOutReset() failed");
  169.         return false;
  170.     }
  171.  
  172.     while(1)
  173.     {
  174.         bool alldone = true;
  175.  
  176.         for (CBlock *pBlock = Blocks; pBlock < Blocks + numBlocks; pBlock++)
  177.         {
  178.             if ((pBlock->pHeader->dwFlags & WHDR_DONE) == 0)
  179.                 alldone = false;
  180.         }
  181.     
  182.         if (alldone)
  183.             break;
  184.  
  185.         Sleep(20);
  186.     }
  187.  
  188.     for (CBlock *pBlock = Blocks; pBlock < Blocks + numBlocks; pBlock++)
  189.     {
  190.         if (pBlock->Prepared)
  191.         {
  192.             if (waveOutUnprepareHeader(Handle, pBlock->pHeader, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
  193.             {
  194.                 Error("waveOutUnprepareHeader() failed");
  195.             }
  196.         }
  197.     }
  198.  
  199.     if (waveOutClose(Handle) != MMSYSERR_NOERROR)
  200.     {
  201.         Error("waveOutClose() failed");
  202.         return false;
  203.     }
  204.  
  205.     for (pBlock = Blocks; pBlock < Blocks + numBlocks; pBlock++)
  206.     {
  207.         GlobalUnlock(pBlock->Handle);
  208.         GlobalFree(pBlock->Handle);
  209.         GlobalUnlock(pBlock->HeaderHandle);
  210.         GlobalFree(pBlock->HeaderHandle);
  211.     }
  212.     
  213.     Running = false;
  214.     return true;
  215. }
  216.  
  217. #define SHORT_MIN    -32768
  218. #define SHORT_MAX    32767
  219.  
  220. // returns random value between 0 and 1
  221. // i got the magic numbers from csound so they should be ok but 
  222. // I haven't checked them myself
  223. inline double frand()
  224. {
  225.     static long stat = 0x16BA2118;
  226.     stat = (stat * 1103515245 + 12345) & 0x7fffffff;
  227.     return (double)stat * (1.0 / 0x7fffffff);
  228. }
  229.  
  230. static void QuantizeWithDither(float *pin, int *piout, int c)
  231. {
  232.     double const d2i = (1.5 * (1 << 26) * (1 << 26));
  233.     
  234.     do
  235.     {
  236.         double res = ((double)pin[1] + frand()) + d2i;
  237.  
  238.         int r = *(int *)&res;
  239.  
  240.         if (r < SHORT_MIN)
  241.             r = SHORT_MIN;
  242.         else if (r > SHORT_MAX)
  243.             r = SHORT_MAX;
  244.  
  245.         res = ((double)pin[0] + frand()) + d2i;
  246.         int l = *(int *)&res;
  247.  
  248.         if (l < SHORT_MIN)
  249.             l = SHORT_MIN;
  250.         else if (l > SHORT_MAX)
  251.             l = SHORT_MAX;
  252.  
  253.         *piout++ = (r << 16) | (word)l;
  254.         pin += 2;
  255.     } while(--c);
  256. }
  257.  
  258. static void Quantize(float *pin, int *piout, int c)
  259. {
  260.     double const d2i = (1.5 * (1 << 26) * (1 << 26));
  261.     
  262.     do
  263.     {
  264.         double res = ((double)pin[1]) + d2i;
  265.  
  266.         int r = *(int *)&res;
  267.  
  268.         if (r < SHORT_MIN)
  269.             r = SHORT_MIN;
  270.         else if (r > SHORT_MAX)
  271.             r = SHORT_MAX;
  272.  
  273.         res = ((double)pin[0]) + d2i;
  274.         int l = *(int *)&res;
  275.  
  276.         if (l < SHORT_MIN)
  277.             l = SHORT_MIN;
  278.         else if (l > SHORT_MAX)
  279.             l = SHORT_MAX;
  280.  
  281.         *piout++ = (r << 16) | (word)l;
  282.         pin += 2;
  283.     } while(--c);
  284. }
  285.  
  286. void ad::DoBlocks()
  287. {
  288.     CBlock *pb = Blocks + CurrentBlock;
  289.  
  290.     while(pb->pHeader->dwFlags & WHDR_DONE)
  291.     {
  292.         if (pb->Prepared)
  293.         {
  294.             if (waveOutUnprepareHeader(Handle, pb->pHeader, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
  295.                 Error("waveOutUnprepareHeader() failed");
  296.  
  297.             pb->Prepared = false;
  298.         }
  299.  
  300.         int *pout = (int *)pb->pData;
  301.         int bs = BlockSize / BYTES_PER_SAMPLE;
  302.         
  303.         do
  304.         {
  305.             int n = bs;
  306.             float *pbuf = pCallback(n);
  307.             if (Dither)
  308.                 QuantizeWithDither(pbuf, pout, n);
  309.             else
  310.                 Quantize(pbuf, pout, n);
  311.             pout += n;
  312.             bs -= n;
  313.         } while(bs > 0);
  314.         
  315.  
  316.         WritePos += BlockSize/BYTES_PER_SAMPLE;
  317.  
  318.         pb->pHeader->dwFlags = 0;
  319.         pb->pHeader->lpData = (char *)pb->pData;
  320.         pb->pHeader->dwBufferLength = BlockSize;
  321.         pb->pHeader->dwFlags = 0;
  322.         pb->pHeader->dwLoops = 0;
  323.  
  324.         if (waveOutPrepareHeader(Handle, pb->pHeader, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
  325.             Error("waveOutPrepareHeader() failed");
  326.  
  327.         pb->Prepared = true;
  328.  
  329.         if (waveOutWrite(Handle, pb->pHeader, sizeof(WAVEHDR)) != MMSYSERR_NOERROR)
  330.             Error("waveOutWrite() failed");
  331.  
  332.         pb++;
  333.         if (pb== Blocks + numBlocks)
  334.             pb = Blocks;
  335.  
  336.     }
  337.  
  338.     CurrentBlock = pb - Blocks;
  339. }
  340.  
  341. void ad::ReadConfig()
  342. {
  343.     SamplesPerSec = ReadProfileInt("SamplesPerSec", 44100);
  344.     DeviceID = ReadProfileInt("DeviceID", 0);
  345.     numBlocks = ReadProfileInt("numBlocks", 4);
  346.     BlockSize = ReadProfileInt("BlockSize", 8192);
  347.     PollSleep = ReadProfileInt("PollSleep", 20);
  348.     Dither = ReadProfileInt("Dither", 0);
  349.  
  350.     Flags = ADF_STEREO;
  351. }
  352.  
  353. void ad::WriteConfig()
  354. {
  355.     WriteProfileInt("SamplesPerSec", SamplesPerSec);
  356.     WriteProfileInt("DeviceID", DeviceID);
  357.     WriteProfileInt("numBlocks", numBlocks);
  358.     WriteProfileInt("BlockSize", BlockSize);
  359.     WriteProfileInt("PollSleep", PollSleep);
  360.     WriteProfileInt("Dither", Dither);
  361. }
  362.  
  363.  
  364. void ad::Initialize(dword hwnd, float *(*pcallback)(int &numsamples))
  365. {
  366.     pCallback = pcallback;
  367.     Running = false;
  368.     ReadConfig();
  369.  
  370.     Initialized = true;
  371.     Start();
  372. }
  373.  
  374. void ad::Reset()
  375. {
  376.     Stop();
  377. }
  378.  
  379. void ad::Configure()
  380. {
  381.     CConfigDlg dlg;
  382.     dlg.m_BufNum = numBlocks;
  383.     dlg.m_BufSize = BlockSize;
  384.     dlg.m_Device = DeviceID;
  385.     dlg.m_Dither = Dither;
  386.     dlg.m_SampleRate = SamplesPerSec;
  387.  
  388.     {
  389.         AFX_MANAGE_STATE(AfxGetStaticModuleState());
  390.  
  391.         if (dlg.DoModal() != IDOK)
  392.             return;
  393.     }
  394.  
  395.     int oldnb = numBlocks;
  396.     int oldbs = BlockSize;
  397.     int olddid = DeviceID;
  398.     int olddither = Dither;
  399.     int oldsps = SamplesPerSec;
  400.  
  401.     if (Initialized)
  402.         Stop();
  403.  
  404.     numBlocks = dlg.m_BufNum;
  405.     BlockSize = dlg.m_BufSize;
  406.     DeviceID = dlg.m_Device;
  407.     Dither = dlg.m_Dither;
  408.     SamplesPerSec = dlg.m_SampleRate;
  409.  
  410.     if (Initialized)
  411.     {
  412.         if (Start())
  413.         {
  414.             WriteConfig();
  415.         }
  416.         else
  417.         {
  418.             numBlocks = oldnb;
  419.             BlockSize = oldbs;
  420.             DeviceID = olddid;
  421.             Dither = olddither;
  422.             SamplesPerSec = oldsps;
  423.  
  424.             Start();
  425.         }
  426.     }
  427.     else
  428.     {
  429.         WriteConfig();
  430.     }
  431. }
  432.  
  433. int ad::GetPlayPos()
  434. {
  435.     if (!Running)
  436.         return 0;
  437.  
  438.     MMTIME time;
  439.     time.wType = TIME_SAMPLES;
  440.  
  441.     if (waveOutGetPosition(Handle, &time, sizeof(MMTIME)) != MMSYSERR_NOERROR)
  442.         Error("waveOutGetPosition() failed");
  443.  
  444.     if (time.wType != TIME_SAMPLES)
  445.         Error("waveOutGetPosition() doesn't support TIME_SAMPLES");
  446.  
  447.     return time.u.sample & ((1 << 23) - 1);
  448.  
  449. }
  450.  
  451. int ad::GetWritePos()
  452. {
  453.     if (!Running)
  454.         return 0;    
  455.  
  456.     return WritePos & ((1 << 23) - 1);
  457. }
  458.  
  459. bool ad::Enable(bool e)
  460. {
  461.     if (e)
  462.         return Start();
  463.     else
  464.         return Stop();
  465. }
  466.  
  467. CAudioDriverInfo info = { 
  468. #ifdef _DEBUG
  469.     "Windows Waveform Audio (debug build)"
  470. #else
  471.     "Windows Waveform Audio"
  472. #endif
  473. };
  474.  
  475. extern "C"
  476. {
  477. __declspec(dllexport) CAudioDriver * __cdecl NewAD()
  478. {
  479.     return new ad;
  480. }
  481.  
  482. __declspec(dllexport) CAudioDriverInfo const * __cdecl GetADInfo()
  483. {
  484.     return &info;
  485. }
  486. }
  487.  
  488. CWinApp App;