// dac_sound.cpp : Defines the entry point for the console application.
//

#include <windows.h>
#include <mmsystem.h>

#include <stdio.h>
#pragma comment(lib, "winmm.lib")
#pragma comment(lib, "user32.lib")

const int nRate = 22050;
const int freqDividor = 25; 
const int nBits = 16;
const int nBytesPerSample = nBits / 8;

const int nSeconds = 1;

const int nSamples = nRate * nSeconds;
const int nBytes   = nSamples * nBytesPerSample;

typedef signed short SWORD;

LPCSTR GetMMSysError(MMRESULT mmResult){
    const int nBufferSize = 512;
    static CHAR acError[nBufferSize];

  waveOutGetErrorText(mmResult, acError, nBufferSize);
  CharToOem(acError, acError);
  return acError;
}

// Так делать некрасиво и неправильно. Только для простоты! 
// В реальных приложениях - либо обработка сообщений, либо мутексы, 
// либо эта же процедура выводит очередной "кусок" через waveOutWrite
LONG bDone = FALSE;

void CALLBACK waveOutProc(
  HWAVEOUT hwo, UINT uMsg,         
  DWORD dwInstance, DWORD dwParam1, DWORD dwParam2)
{
  if(uMsg == WOM_DONE || uMsg == WOM_CLOSE) 
      InterlockedIncrement(&bDone);
}

int bufNo=0;
int vel=0; 

SWORD aswBuffer[2][nSamples]; 
WAVEHDR whdr[2]; 
HWAVEOUT hWO; // handle identifying the open waveform-audio output device

void genSound(SWORD *buf) {
for (UINT i = 0; i < nSamples; i++) 
  {
    if((i/freqDividor)%2)
      buf[i] = vel;
    else
      buf[i] = -vel;
  }
}

void putNewBuffer(int withBreak, int withPut) {
   if(withPut) {
	  bufNo=(bufNo+1)%2;
	  genSound(aswBuffer[bufNo]);

  whdr[bufNo].lpData = (LPSTR)(aswBuffer[bufNo]); 
  whdr[bufNo].dwBufferLength = nBytes; // размер буфера в байтах.
  whdr[bufNo].dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP; // Буфер подготовлен (зафиксирован в памяти)
  whdr[bufNo].dwLoops = 0xFFFFFFFF; 

  MMRESULT mmResult = waveOutPrepareHeader(hWO, &whdr[bufNo], sizeof(whdr[bufNo]));
  if(mmResult != MMSYSERR_NOERROR) printf("waveOutPrepareHeader() ne srabotala! %s\n", GetMMSysError(mmResult));

  waveOutSetVolume(hWO, 0xFFFF);

  mmResult = waveOutWrite(hWO, &whdr[bufNo], sizeof(whdr[bufNo]));
  if(mmResult != MMSYSERR_NOERROR) printf("waveOutWrite() ne srabotala! %s\n", GetMMSysError(mmResult));

//  printf("value waveOutGetNumDevs() equals %d", waveOutGetNumDevs());

   }
   if(withBreak) {
		MMRESULT mmResult = waveOutBreakLoop(hWO); 
		if(mmResult != MMSYSERR_NOERROR) printf("waveOutBreakLoopError()%s\n", GetMMSysError(mmResult));
  }
}

const int input_size=128;


int main(void){

  // генерация звука: прямоугольные импульсы частотой nRate / 200; 
  // при nRate = 44100 получаем 220,5 Гц 
  // (частотный анализ записи показал 220,46 Гц) 

  WAVEFORMATEX wfx; // для получения описателя звукового устройства:
  wfx.wFormatTag = WAVE_FORMAT_PCM;   // wav-формат
  wfx.nChannels = 1; // моно - звук
  wfx.nSamplesPerSec = nRate; // 44100 Гц
  wfx.nAvgBytesPerSec = nBytesPerSample * nRate; // байт в секунду
  wfx.nBlockAlign = nBytesPerSample; // 2 байта на сэмпл-столбик
  wfx.wBitsPerSample = nBits; // 16-битный сэмпл-столбик
  wfx.cbSize = 0; // For only WAVE_FORMAT_PCM formats, this member is ignored

  MMRESULT mmResult = waveOutOpen(&hWO, WAVE_MAPPER, &wfx, (DWORD)waveOutProc, 0, CALLBACK_FUNCTION);
 
  if(mmResult != MMSYSERR_NOERROR) printf("waveOutOpen() ne srabotala! %s\n", GetMMSysError(mmResult));

  putNewBuffer(0,1);

  char input[input_size];
  for(;;){
	  gets_s(input,input_size);
	  switch(input[0]) {
		  case 'q': 
			  putNewBuffer(1,0); 
			  for(;;) {
				if(bDone) break;
				Sleep(100);
			  };
	 	      waveOutClose(hWO);
			  return 0;
		  case 'n':
			  gets_s(input,input_size);
	          sscanf_s(input,"%d",&vel);
			  putNewBuffer(1,1); 
			  break; 
	  }
  }

}

