/* The original version of this program can be found at http://damb.dk */
#include <windows.h>
#include <stdio.h>
#include <math.h>
#include <stdlib.h>
#define SAMPLE_RATE 22050
void CheckError(MMRESULT MMResult, const char *msg)
{
if(MMResult != MMSYSERR_NOERROR)
{
char Text[256];
waveOutGetErrorText(MMResult, Text, sizeof(Text));
fprintf(stderr, "%s: %sn", msg, Text);
exit(EXIT_FAILURE);
}
}
BOOL KeyHit();
WORD GetChar();
bool Play(char *buffer, int NumSamples)
{
HWAVEOUT WaveHandle;
WAVEHDR WaveHeader;
MMRESULT MMResult;
WAVEFORMATEX WaveFormat;
// Setup the WaveFormat structure and open the Output device
WaveFormat.wFormatTag = WAVE_FORMAT_PCM;
WaveFormat.nChannels = 1;
WaveFormat.nSamplesPerSec = SAMPLE_RATE;
WaveFormat.nAvgBytesPerSec = SAMPLE_RATE;
WaveFormat.nBlockAlign = 1;
WaveFormat.wBitsPerSample = 8;
WaveFormat.cbSize = 0;
MMResult = waveOutOpen(&WaveHandle, WAVE_MAPPER, &WaveFormat, 0, 0, 0);
CheckError(MMResult, "Failed to open wave out");
// Setup the WaveHeader, whcih contaions info about the data to play
memset(&WaveHeader, 0, sizeof(WaveHeader));
WaveHeader.lpData = buffer;
WaveHeader.dwBufferLength = NumSamples;
WaveHeader.dwBytesRecorded = NumSamples;
WaveHeader.dwLoops = 1;
WaveHeader.dwFlags = WHDR_BEGINLOOP | WHDR_ENDLOOP;
MMResult = waveOutPrepareHeader(WaveHandle, &WaveHeader, sizeof(WaveHeader));
CheckError(MMResult, "Failed to prepare header");
// "write" (ie play) the samples
MMResult = waveOutWrite(WaveHandle, &WaveHeader, sizeof(WaveHeader));
CheckError(MMResult, "Failed to write");
// Wait until done
printf("Playing");
fflush(stdout);
Sleep(1000*NumSamples/SAMPLE_RATE);
while(!(WaveHeader.dwFlags & WHDR_DONE))
Sleep(10);
printf("n");
// Gracefully shutdown
MMResult = waveOutUnprepareHeader(WaveHandle, &WaveHeader, sizeof(WaveHeader));
CheckError(MMResult, "Failed to unprepare header");
MMResult = waveOutClose(WaveHandle);
CheckError(MMResult, "Failed to close");
return true;
}
bool Record(char *buffer, int &NumSamples)
{
HWAVEIN WaveInHandle;
WAVEFORMATEX WaveFormat;
MMRESULT MMResult;
WAVEHDR WaveHeader;
MMTIME MMTime;
// Setup the WaveFormat structure and open the Input device
WaveFormat.wFormatTag = WAVE_FORMAT_PCM;
WaveFormat.nChannels = 1;
WaveFormat.nSamplesPerSec = SAMPLE_RATE;
WaveFormat.nAvgBytesPerSec = SAMPLE_RATE;
WaveFormat.nBlockAlign = 1;
WaveFormat.wBitsPerSample = 8;
WaveFormat.cbSize = 0;
MMResult = waveInOpen(&WaveInHandle, WAVE_MAPPER, &WaveFormat, 0, 0, 0);
CheckError(MMResult, "Failed to open input");
// Setup the WaveHeader, whcih contaions info about the data to record
memset(&WaveHeader, 0, sizeof(WaveHeader));
WaveHeader.lpData = buffer;
WaveHeader.dwBufferLength = NumSamples;
MMResult = waveInPrepareHeader(WaveInHandle, &WaveHeader, sizeof(WaveHeader));
CheckError(MMResult, "Failed to prepare header");
// Tell the system to use our buffer
MMResult = waveInAddBuffer(WaveInHandle, &WaveHeader, sizeof(WaveHeader));
CheckError(MMResult, "Failed to add buffer");
// Start recording
MMResult = waveInStart(WaveInHandle);
CheckError(MMResult, "Failed to Start");
// Wait for user key or end of input
printf("Recording, hit any key to stop: ");
fflush(stdout);
do
{
Sleep(100);
}
while(!KeyHit() && !(WaveHeader.dwFlags & WHDR_DONE));
while(KeyHit())
GetChar();
printf("n");
// Get the position at which the recording stopped
MMTime.wType = TIME_BYTES;
MMResult = waveInGetPosition(WaveInHandle, &MMTime, sizeof(MMTime));
CheckError(MMResult, "Failed to get positionn");
NumSamples = MMTime.u.cb;
// Gracefully stop
MMResult = waveInReset(WaveInHandle);
CheckError(MMResult, "Failed to resetn");
MMResult = waveInUnprepareHeader(WaveInHandle, &WaveHeader, sizeof(WaveHeader));
CheckError(MMResult, "Failed to unprepare header");
MMResult = waveInClose(WaveInHandle);
CheckError(MMResult, "Failed to close input");
return true;
}
void SaveFile(char *buffer, int NumSamples)
{ /* http://ccrma.stanford.edu/courses/422/projects/WaveFormat/ */
FILE *f = fopen("sample.wav", "wb");
if(!f)
{
fprintf(stderr, "Failed to open output filen");
return;
}
fwrite("RIFF", 4, 1, f);
int size = NumSamples + 36;
fwrite(&size, 4, 1, f);
fwrite("WAVE", 4, 1, f);
fwrite("fmt ", 4, 1, f);
size = 16;
fwrite(&size, 4, 1, f);
size = 1;
fwrite(&size, 2, 1, f);
size = 1;
fwrite(&size, 2, 1, f);
size = SAMPLE_RATE;
fwrite(&size, 4, 1, f);
fwrite(&size, 4, 1, f);
size = 1;
fwrite(&size, 2, 1, f);
size = 8;
fwrite(&size, 2, 1, f);
fwrite("data", 4, 1, f);
fwrite(&NumSamples, 4, 1, f);
fwrite(buffer, NumSamples, 1, f);
fclose(f);
printf("Recorded data written to sample.wavn");
}
int main(void)
{
/* Run sndvol32.exe -r to adjust the recording properties */
int NumSamples = 10*SAMPLE_RATE;
printf("Hit any key to Record");
fflush(stdout);
GetChar();
printf("n");
char *Buffer = (char *)malloc(10*SAMPLE_RATE);
if(Record(Buffer, NumSamples))
{
SaveFile(Buffer, NumSamples);
printf("Hit any key to play");
fflush(stdout);
GetChar();
printf("n");
Play(Buffer, NumSamples);
}
free(Buffer);
return 0;
}
// Some helper functions
BOOL KeyHit()
{
DWORD NumEvents, NumEventsRead;
INPUT_RECORD *InputRecord;
DWORD i;
GetNumberOfConsoleInputEvents(GetStdHandle(STD_INPUT_HANDLE), &NumEvents);
InputRecord = (INPUT_RECORD *)malloc(sizeof(INPUT_RECORD)*NumEvents);
PeekConsoleInput(GetStdHandle(STD_INPUT_HANDLE), InputRecord, NumEvents, &NumEventsRead);
for(i = 0; i < NumEventsRead; i++)
{
if(InputRecord[i].EventType & KEY_EVENT && InputRecord[i].Event.KeyEvent.bKeyDown)
{
if(InputRecord[i].Event.KeyEvent.wVirtualKeyCode != VK_CONTROL &&
InputRecord[i].Event.KeyEvent.wVirtualKeyCode != VK_MENU &&
InputRecord[i].Event.KeyEvent.wVirtualKeyCode != VK_SHIFT)
{
free(InputRecord);
return TRUE;
}
}
}
free(InputRecord);
return FALSE;
}
WORD GetChar()
{
DWORD NumEventsRead;
INPUT_RECORD InputRecord;
while(1)
{
if(!ReadConsoleInput(GetStdHandle(STD_INPUT_HANDLE), &InputRecord, 1, &NumEventsRead))
return 0;
if(InputRecord.EventType & KEY_EVENT && InputRecord.Event.KeyEvent.bKeyDown)
{
if(InputRecord.Event.KeyEvent.wVirtualKeyCode != VK_CONTROL &&
InputRecord.Event.KeyEvent.wVirtualKeyCode != VK_MENU &&
InputRecord.Event.KeyEvent.wVirtualKeyCode != VK_SHIFT)
{
return InputRecord.Event.KeyEvent.wVirtualKeyCode;
}
}
}
}