This is a command-line win32 program will lets you sample from an audio
card, starting at a preset time and ending at another time. Useful for
recording radio shows.
Any comments, pls reply to publicgis_d AT rogers DOT com
Enjoy
Kevin
============================================================================
==========
// Copyright 2004: Kevin John Macdonald
// You are free to use this code for personal use.
#define STRICT
#include <limits.h>
#include <signal.h>
#include <math.h>
#include <time.h>
#include <stdio.h>
#include <string.h>
#include <windows.h>
#include <basetsd.h>
#include <mmreg.h>
#include <dxerr8.h>
#include <dsound.h>
#include "DSUtil.h"
typedef char HH24MISS[6];
#define SAFE_DELETE(p) { if(p) { delete (p); (p)=NULL; } }
#define SAFE_RELEASE(p) { if(p) { (p)->Release(); (p)=NULL; } }
// DEFAULT QUALITY PARAMETERS
#define DEFAULT_SAMPLE_FREQ_KHZ (44)
#define DEFAULT_CHANNELS (2)
#define DEFAULT_BITS_PER_SAMPLE (16)
// AMPLITUDE THRESHOLDS
#define INVALID_AMP (INT_MAX)
#define MAX_AMP_8BIT (127)
#define MAX_AMP_16BIT (32767)
// CLIPPING THRESHOLDS
//
// Note: two or more consecutive samples at or above the clip level
// are considered to be clipped samples (ie. input signal is too high).
#define CLIP_LEVEL_8BIT (MAX_AMP_8BIT - 2)
#define CLIP_LEVEL_16BIT (MAX_AMP_16BIT - 2)
// OTHER MANIFEST CONSTANTS
#define VERSION "1.0"
#define SECONDS_IN_HOUR (60 * 60)
#define SECONDS_IN_DAY (24 * SECONDS_IN_HOUR)
#define BUF_SIZE_IN_SECONDS (5)
#define NUM_POS_NOTIFICATIONS (BUF_SIZE_IN_SECONDS * 2)
#define MIN_NOTIFY_SIZE (2048)
#define MAX_AUDIO_DRIVER_GUIDS (20)
#define MAX_AUDIO_DRIVER_DESC_LEN (100)
#define POLL_INTERVAL_MSEC (250)
GUID audioDriverGUIDs[MAX_AUDIO_DRIVER_GUIDS];
char
audioDriverDesc[MAX_AUDIO_DRIVER_GUIDS][MAX_AUDIO_DRIVER_DESC_LEN+1];
char
audioDriverName[MAX_AUDIO_DRIVER_GUIDS][MAX_AUDIO_DRIVER_DESC_LEN+1];
int numAudioDrivers = 0;
const GUID * captureDeviceGUID = 0;
DSBPOSITIONNOTIFY posNotify[NUM_POS_NOTIFICATIONS + 1];
int fmt = 0;
int notifySize = 0;
int captureBufferSize = 0;
int nextCaptureOffset = 0;
WAVEFORMATEX wfxInput;
HANDLE notificationEvent = 0;
CWaveFile * waveFile = 0;
IDirectSoundCapture * dsc = 0;
IDirectSoundCaptureBuffer * dscb = 0;
IDirectSoundNotify * dsn = 0;
// Set to true if interrupt signal is caught to indicate to shutdown.
bool shutdownSignalled = false;
void cvtTimetToComponents (
const time_t delay,
int & hr,
int & min,
int & sec)
{
hr = delay / SECONDS_IN_HOUR;
min = (delay - hr * SECONDS_IN_HOUR) / 60;
sec = delay % 60;
}
const char * getTimeHH24MISS ()
{
const time_t long_now = time (0);
const struct tm * const now = localtime (&long_now);
static char hh24miss[7];
if (! now) {
printf ("localtime failed.\n");
hh24miss[0] = 0;
} else {
sprintf (hh24miss, "%02d%02d%02d", now->tm_hour, now->tm_min,
now->tm_sec);
}
return hh24miss;
}
int cvt99ToInt (
const char dig[2])
{
if ( ! isdigit (dig[0])
|| ! isdigit (dig[1])) {
return -1;
}
return (dig[0] - '0') * 10 + dig[1] - '0';
}
int magnitude (
const unsigned char sample)
{
if (wfxInput.wBitsPerSample != 8) {
printf ("Internal error on line %d\n", __LINE__);
exit (1);
}
if (sample == 0) {
return 127;
} else {
return abs (sample - 128);
}
}
int magnitude (
const short sample)
{
if (wfxInput.wBitsPerSample != 16) {
printf ("Internal error on line %d\n", __LINE__);
exit (1);
}
if (sample == -32768) {
return 32767;
} else {
return abs (sample);
}
}
// Returned string is a null terminated string 38 characters long.
const char * guidToString (
const GUID * const guid)
{
static char str[40];
sprintf (str, "{%08X-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X}",
*((unsigned int *)guid), *(((unsigned short *)guid)+2),
*(((unsigned short *)guid)+3), *(((unsigned char *)guid)+8),
*(((unsigned char *)guid)+9), *(((unsigned char *)guid)+10),
*(((unsigned char *)guid)+11), *(((unsigned char *)guid)+12),
*(((unsigned char *)guid)+13), *(((unsigned char *)guid)+14),
*(((unsigned char *)guid)+15));
return str;
}
// Convert time in string 'hh24miss' to time_t value. if error, return -1.
time_t cvtHH24MISSToTime (
const HH24MISS hh24miss)
{
// EXTRACT AND CHECK HOUR, MINUTE AND SECONDS COMPONENTS
const int hour = cvt99ToInt (hh24miss);
const int min = cvt99ToInt (hh24miss+2);
const int sec = cvt99ToInt (hh24miss+4);
if (hour < 0 || hour > 23) {
printf ("Bad hour.\n");
return -1;
}
if (min < 0 || min > 59) {
printf ("Bad minute.\n");
return -1;
}
if (sec < 0 || sec > 59) {
printf ("Bad second.\n");
return -1;
}
// CONVERT
const time_t now_t = time (0);
const struct tm * const now_tm = localtime (&now_t);
struct tm t;
t = *now_tm;
t.tm_hour = hour;
t.tm_min = min;
t.tm_sec = sec;
time_t then_t = mktime (&t);
if (then_t == -1) {
printf ("mktime failed.\n");
return -1;
}
// ADJUST IF TIME IS FOR TOMORROW
HH24MISS now;
memcpy (now, getTimeHH24MISS (), 6);
if (! now[0]) {
return -1;
}
if (strncmp (hh24miss, now, 6) < 0) {
printf ("Note: the time '%.6s' occurs tomorrow.\n", hh24miss);
then_t += SECONDS_IN_DAY;
}
return then_t;
}
INT_PTR CALLBACK soundEnumCallback (
GUID * guid,
LPSTR strDesc,
LPSTR strDrvName,
VOID * /*pContext*/)
{
// Skip the default device.
if (guid && numAudioDrivers < MAX_AUDIO_DRIVER_GUIDS) {
memcpy (audioDriverGUIDs + numAudioDrivers, guid, sizeof(GUID));
strncpy ((char *) (audioDriverDesc + numAudioDrivers), strDesc,
MAX_AUDIO_DRIVER_DESC_LEN);
strncpy ((char *) (audioDriverName + numAudioDrivers), strDrvName,
MAX_AUDIO_DRIVER_DESC_LEN);
++numAudioDrivers;
}
return TRUE;
}
IDirectSoundCapture * initDirectSound (
const GUID * const guid)
{
IDirectSoundCapture * p;
if (CoInitialize (NULL) != S_OK) {
printf ("Error: Failed to initialize COM library.\n");
return 0;
}
if (DirectSoundCaptureCreate (guid, &p, NULL ) != DS_OK) {
printf ("Error: Failed to initialize IDirectSoundCapture interface.\n");
return 0;
}
return p;
}
void initWfxInput (
const int sampleFreq,
const int channels,
const int bitsPerSample)
{
ZeroMemory (&wfxInput, sizeof(wfxInput));
wfxInput.wFormatTag = WAVE_FORMAT_PCM;
wfxInput.nSamplesPerSec = sampleFreq;
wfxInput.wBitsPerSample = (unsigned short) bitsPerSample;
wfxInput.nChannels = (unsigned short) channels;
wfxInput.nBlockAlign = (unsigned short) (wfxInput.nChannels *
(wfxInput.wBitsPerSample / 8 ));
wfxInput.nAvgBytesPerSec = wfxInput.nBlockAlign *
wfxInput.nSamplesPerSec;
}
bool initNotifications ()
{
if(! dscb) {
return false;
}
notificationEvent = CreateEvent (NULL, FALSE, FALSE, NULL );
if (notificationEvent == NULL) {
return false;
}
// Create a notification event, for when the sound stops playing
if (dscb->QueryInterface (IID_IDirectSoundNotify, (VOID**)&dsn) != S_OK)
{
return false;
}
// Setup the notification positions
for (int i = 0; i < NUM_POS_NOTIFICATIONS; ++i) {
posNotify[i].dwOffset = notifySize * (i+1) - 1;
posNotify[i].hEventNotify = notificationEvent;
}
// Tell DirectSound when to notify us. the notification will come in the
from
// of signaled events that are handled in WinMain()
if (dsn->SetNotificationPositions (NUM_POS_NOTIFICATIONS, posNotify) !=
DS_OK) {
return false;
}
return true;
}
bool createCaptureBuffer (
WAVEFORMATEX & wfx)
{
SAFE_RELEASE (dsn);
SAFE_RELEASE (dscb);
// Set the buffer sizes
captureBufferSize = wfx.nAvgBytesPerSec * BUF_SIZE_IN_SECONDS;
// Set the notification size
notifySize = max (MIN_NOTIFY_SIZE, captureBufferSize /
NUM_POS_NOTIFICATIONS);
notifySize -= notifySize % wfx.nBlockAlign;
// Create the capture buffer
DSCBUFFERDESC d;
ZeroMemory (&d, sizeof(d));
d.dwSize = sizeof(d);
d.dwBufferBytes = captureBufferSize;
d.lpwfxFormat = &wfx;
HRESULT h;
if((h=dsc->CreateCaptureBuffer (&d, &dscb, 0)) != DS_OK) {
return false;
}
nextCaptureOffset = 0;
if(! initNotifications ()) {
return false;
}
return true;
}
// Convert volume (expressed in percentage 0-100) into a magnitude value
within the range
// defined by the current dynamic range being sampled.
// domain/range have a somewhat exponential relationahip to mimic
amplitude/decibels
// relationship.
int cvtVolumePctToMagnitude (
const int volumePct)
{
const double MAX_PCT = 100;
if (wfxInput.wBitsPerSample == 8) {
return (int) (MAX_AMP_8BIT * volumePct * volumePct / (MAX_PCT *
MAX_PCT));
} else if (wfxInput.wBitsPerSample == 16) {
return (int) (MAX_AMP_16BIT * volumePct * volumePct / (MAX_PCT *
MAX_PCT));
} else {
return 0;
}
}
int cvtMagnitudeToVolumePct (
const int magnitude)
{
const double MAX_PCT = 100;
if (wfxInput.wBitsPerSample == 8) {
return (int) sqrt (MAX_PCT * MAX_PCT * magnitude / MAX_AMP_8BIT);
} else if (wfxInput.wBitsPerSample == 16) {
return (int) ceil (sqrt (MAX_PCT * MAX_PCT * magnitude /
MAX_AMP_16BIT));
} else {
return 0;
}
}
// the amplitude of each sample (all channels) in 'buf' (of length
'lenInBytes') are observed.
// output:
// 'cnt' measures the number of samples whose amplitude reach
10%,20%,30%..100% of the amplitude range.
// 'numClipped' measures the number of consecutive samples that exceed
amplitude limits.
void analyzeSamples (
const void * buf,
const int lenInBytes,
int cnt[10],
int & numClipped)
{
for (int i = 0; i < 10; ++i) {
cnt[i] = 0;
}
numClipped = 0;
int numSamples = 0;
int prevClipped = 0;
// Performance concerns dictate separate implementations for 8 and 16
bits.
switch (wfxInput.wBitsPerSample) {
case 8:
{for (const unsigned char * p = (const unsigned char *) buf; p < (const
unsigned char *) buf + lenInBytes; ++p) {
const int mag = magnitude (*p);
int pct = cvtMagnitudeToVolumePct (mag)/10;
if (pct == 10) {
pct = 9;
}
++cnt[pct];
if (mag >= CLIP_LEVEL_8BIT) {
++prevClipped;
} else {
if (prevClipped > 1) {
// Only two or more consecutive samples are considered to have been
clipped.
numClipped += prevClipped;
}
prevClipped = 0;
}
}}
numSamples = lenInBytes;
break;
case 16:
{for (const short * p = (const short *) buf; p < (const short *) buf +
lenInBytes / 2; ++p) {
const int mag = magnitude (*p);
int pct = cvtMagnitudeToVolumePct (mag)/10;
if (pct == 10) {
pct = 9;
}
++cnt[pct];
if (mag >= CLIP_LEVEL_16BIT) {
++prevClipped;
} else {
if (prevClipped > 1) {
// Only two or more consecutive samples are considered to have been
clipped.
numClipped += prevClipped;
}
prevClipped = 0;
}
}}
numSamples = lenInBytes / 2;
break;
default:
printf ("Undefined bit size.\n");
numSamples = 1;
break;
}
// NORMALIZE COUNT AND EXPRESS IN PERCENT
for (i = 0; i < 10; ++i) {
double dval = (double) cnt[i] * 100 / numSamples;
if (dval > 0 && dval < 1) {
dval = 1;
}
cnt[i] = (int) floor (dval + 0.5);
}
}
// The buffer 'buf' (size='bufLen') should be filtered (ie. discarded) if
its average
// volume is lower than 'volumeCutoff'. Return true if buffer should be
filtered.
// Filtering occurs if
// 1. average volume of all samples in buffer is at or below cutoff.
// 2. number of samples that exceed volume cutoff is less than 0.1% of all
samples.
bool filter (
const void * buf,
const unsigned long bufLen,
const int volumeCutoff)
{
const int MAG_THRESHOLD = cvtVolumePctToMagnitude (volumeCutoff);
if (wfxInput.wBitsPerSample == 8) {
unsigned char * const bufStart = (unsigned char *) buf;
unsigned char * const bufEnd = bufStart + bufLen / sizeof (unsigned
char);
const int bufSamples = bufEnd - bufStart;
int sampleSum = 0;
int highSamples = 0;
for (unsigned char * p = bufStart; p < bufEnd; ++p) {
const int mag = magnitude (*p);
sampleSum += mag;
if (mag > MAG_THRESHOLD) {
++highSamples;
}
}
return sampleSum / bufSamples <= MAG_THRESHOLD
&& highSamples < bufSamples / 1000;
} else if (wfxInput.wBitsPerSample == 16) {
short * const bufStart = (short *) buf;
short * const bufEnd = bufStart + bufLen / sizeof (short);
const int bufSamples = bufEnd - bufStart;
int sampleSum = 0;
int highSamples = 0;
for (short * p = bufStart; p < bufEnd; ++p) {
const int mag = magnitude (*p);
sampleSum += mag;
if (mag > MAG_THRESHOLD) {
++highSamples;
}
}
return sampleSum / bufSamples <= MAG_THRESHOLD
&& highSamples < bufSamples / 1000;
} else {
// Unsupported sample size.
return 0;
}
}
bool recordCapturedData (
const int volumeCutoff,
const bool verbose)
{
// CONSISTENCY CHECK
if(! dscb) {
printf ("Internal error on line %d\n", __LINE__);
return false;
}
if(! waveFile) {
printf ("Internal error on line %d\n", __LINE__);
return false;
}
// IDENTIFY WHICH BUFFER EXTENTS (ONE OR TWO) THAT ARE TO BE WRITTEN
DWORD readPos;
if(dscb->GetCurrentPosition (0, &readPos) != DS_OK) {
printf ("Cannot get current buf pos.\n");
return false;
}
int lockSize = readPos - nextCaptureOffset;
if (lockSize < 0) {
lockSize += captureBufferSize;
}
// Block align lock size so that we are always write on a boundary
lockSize -= (lockSize % notifySize);
if (lockSize == 0) {
return false;
}
int cnt[10];
int numClipped;
void * ext1 = 0;
DWORD extLen1 = 0;
void * ext2 = 0;
DWORD extLen2 = 0;
UINT ioWrite = 0;
DWORD extLen = 0;
// LOCK THE CAPTURE BUFFER
if (dscb->Lock (nextCaptureOffset, lockSize, &ext1, &extLen1, &ext2,
&extLen2, 0) != DS_OK) {
printf ("Cannot lock buf.\n");
return false;
}
// WRITE THE CAPTURE BUFFER
if (verbose) {
analyzeSamples (ext1, extLen1, cnt, numClipped);
printf ("%.6s: %3d %3d ", getTimeHH24MISS (), numClipped, cnt[0]);
for (int i = 1; i < 10; ++i) {
printf ("%2d ", cnt[i]);
}
}
extLen = extLen1;
if (volumeCutoff != -1) {
if (filter (ext1, extLen1, volumeCutoff)) {
if (verbose) {
printf (" (silent)");
}
extLen = 0;
}
}
if (verbose) {
printf ("\n");
}
if (extLen > 0 && waveFile->Write (extLen, (BYTE *) ext1, &ioWrite) !=
S_OK) {
printf ("waveFile->Write 1 failed.\n");
return false;
}
// Move the capture offset along
nextCaptureOffset += extLen1;
nextCaptureOffset %= captureBufferSize; // Circular buffer
// OPTIONALLY WRITE THE SECOND CAPTURE BUFFER
if (ext2) {
if (verbose) {
analyzeSamples (ext2, extLen2, cnt, numClipped);
printf ("%.6s: %3d %3d ", getTimeHH24MISS (), numClipped, cnt[0]);
for (int i = 1; i < 10; ++i) {
printf ("%2d ", cnt[i]);
}
}
extLen = extLen2;
if (volumeCutoff != -1) {
if (filter (ext2, extLen2, volumeCutoff)) {
if (verbose) {
printf (" (silent)");
}
extLen = 0;
}
}
if (verbose) {
printf ("\n");
}
if (extLen > 0 && waveFile->Write (extLen, (BYTE *) ext2, &ioWrite) !=
S_OK) {
printf ("waveFile->Write 2 failed.\n");
return false;
}
// Move the capture offset along
nextCaptureOffset += extLen2;
nextCaptureOffset %= captureBufferSize; // Circular buffer
}
// Unlock the capture buffer
if (dscb->Unlock (ext1, extLen1, ext2, extLen2) != DS_OK) {
printf ("Cannot unlock buffer.\n");
return false;
}
return true;
}
// Start recording immediately and end when time = 'finish'.
bool startRecording (
const time_t finish,
const int volumeCutoff,
const bool verbose)
{
if(dscb->Start (DSCBSTART_LOOPING) != DS_OK) {
printf ("Failed to start recording.\n");
return false;
}
while ( time (0) < finish
&& ! shutdownSignalled) {
const int r = WaitForMultipleObjects (1, ¬ificationEvent, FALSE,
POLL_INTERVAL_MSEC);
switch (r) {
case WAIT_FAILED:
printf ("Failed to start recording.\n");
goto CLEANUP;
case WAIT_OBJECT_0 + 0:
if (! recordCapturedData (volumeCutoff, verbose)) {
printf ("Failed to record captured data.\n");
goto CLEANUP;
}
break;
case WAIT_TIMEOUT:
break;
default:
printf ("Internal inconsistency: WaitForMultipleObjects returned %d\n",
r);
}
}
CLEANUP:
if (dscb->Stop () != DS_OK) {
printf ("Failed to stop recording.\n");
return false;
}
return true;
}
void cleanup ()
{
SAFE_DELETE(waveFile);
// Release DirectSound interfaces
SAFE_RELEASE (dsn);
SAFE_RELEASE (dscb);
SAFE_RELEASE (dsc);
// Release COM
CoUninitialize();
}
bool setShutdownSigHandler ();
void sigHandler (
int sig)
{
setShutdownSigHandler ();
if (sig != SIGINT) {
printf ("Warning: caught unexpected signal '%d'\n", sig);
}
printf ("Received shutdown signal. Shutting down soon...\n");
shutdownSignalled = true;
}
bool setShutdownSigHandler ()
{
return signal (SIGINT, sigHandler) != SIG_ERR;
}
void showUsage (
const char * const prgName)
{
printf
("\n----------------------------------------------------------------------\n
");
printf ("Capture a signal from an audio device starting and ending at\n"
"preset times and save it to a wave file.\n"
"\n"
"Usage\n"
"-----\n"
"%s { flags } -e end_time wave_filename\n"
"\n"
" Where flags are any of the following:\n"
" -f 8 | 11 | 22 | -- sample frequency (KHz)\n"
" 44 | 48 | 88 | 96\n"
" -c 1 | 2 -- # channels (1=mono, 2=stereo)\n"
" -b 8 | 16 -- bits per sample\n"
" -s start_time -- time when recording begins\n"
" -e end_time -- time when recording ends\n"
" -v -- verbose (report on signal amplitude)\n"
" -a volume_cutoff -- only keep samples above volume cutoff
(0-99%)\n"
" -? -- show usage info\n"
"\n"
"Defaults are -f %d -c %d -b %d -s {now}\n"
"Both start_time end_time are entered as HH24MISS, where HH24 is
hour\n"
"(00..23), MI is minute (00..59) and SS is seconds (00..59)\n",
prgName, DEFAULT_SAMPLE_FREQ_KHZ, DEFAULT_CHANNELS,
DEFAULT_BITS_PER_SAMPLE);
printf ("\n"
"Signal Amplitude Report\n"
"-----------------------\n"
"The 'v' flag will report on the amplitude of the samples stored in
the\n"
"audio buffer before they are written to the wave file. A report
occupies\n"
"one line and is structured as follows:\n"
"999999: 999999 999 99 99 99 99 99 99 99 99 99 99\n"
"^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^ ^\n"
"| | | \\------------------------/\n"
"| | | percent of samples whose amplitude are within
the\n"
"| | | range 0..10%%, 10..20%%, .. 80..90%% and
90..100%%\n"
"| | |\n"
"| | number of samples that have been clipped (ie.
samples\n"
"| | whose amplitude are at 100%%)\n"
"| buffer size (in bytes)\n"
"current time (hh24miss)\n");
printf ("\n"
"Product Info\n"
"------------\n"
"Version : %s\n"
"Build date: %s\n", VERSION, __DATE__);
}
int main (
int argc,
const char ** argv)
{
// ASSIGN DEFAULT VALUES TO COMMAND LINE PARAMETERS
/*
// for debugging only.
if (argc == 1) {
printf ("Using defaults.\n");
argc=12;
argv[0] = "RecordIt";
argv[1] = "-f";
argv[2] = "44";
argv[3] = "-c";
argv[4] = "2";
argv[5] = "-b";
argv[6] = "16";
argv[7] = "-a";
argv[8] = "10";
argv[9] = "-e";
argv[10] = "095000";
argv[11] = "a.wav";
}
*/
int sampleFreq = DEFAULT_SAMPLE_FREQ_KHZ; // -f
int channels = DEFAULT_CHANNELS; // -c
int bitsPerSample = DEFAULT_BITS_PER_SAMPLE; // -b
HH24MISS startHH24MISS = { 0 }; // -s
HH24MISS endHH24MISS = { 0 }; // -e
int volumeCutoff = -1; // -a
bool verbose = false; // -v
const char * waveFilename = 0;
if (argc < 4) {
printf ("Missing -e flag and wave filename.\n");
showUsage (argv[0]);
return 1;
}
// EXTRACT AND VERIFY ALL COMMAND LINE PARAMETERS EXCEPT LAST ONE
for (int i = 1; i < argc - 1; ++i) {
if (argv[i][0] != '-') {
printf ("All arguments except the last must be flags that begin with
'-'.\n");
showUsage (argv[0]);
return 1;
}
const char flag = argv[i][1];
switch (flag) {
case '?':
showUsage (argv[0]);
return 0;
case 'f':
sampleFreq = atoi (argv[++i]);
break;
case 'c':
channels = atoi (argv[++i]);
break;
case 'b':
bitsPerSample = atoi (argv[++i]);
break;
case 's':
strncpy (startHH24MISS, argv[++i], 6);
break;
case 'e':
strncpy (endHH24MISS, argv[++i], 6);
break;
case 'a':
volumeCutoff = atoi (argv[++i]);
break;
case 'v':
verbose = true;
break;
default:
printf ("Invalid flag: %c\n", flag);
showUsage (argv[0]);
return 1;
}
}
// EXTRACT AND VERIFY LAST COMMAND LINE PARAMETER
waveFilename = argv[argc-1];
if (! isalpha (waveFilename[0]) && waveFilename[0] != '_') {
printf ("Last parameter must be wave filename.\n");
showUsage (argv[0]);
return 1;
}
// ENSURE FILE DOES NOT EXIST
if (::GetFileAttributes (waveFilename) != -1) {
printf ("Cannot create new wave file, because a file of that name
already
exists.\n");
return 1;
}
// CHECK EXTRACTED PARAMETER VALUES
switch (sampleFreq) {
case 8:
sampleFreq = 8000;
break;
case 11:
sampleFreq = 11025;
break;
case 22:
sampleFreq = 22050;
break;
case 44:
sampleFreq = 44100;
break;
case 48:
sampleFreq = 48000;
break;
case 88:
sampleFreq = 88200;
break;
case 96:
sampleFreq = 96000;
break;
default:
printf ("Bad argument to flag '-f'.\n");
showUsage (argv[0]);
return 1;
}
if (channels < 1 || channels > 2) {
printf ("Bad argument to flag '-c'.\n");
showUsage (argv[0]);
return 1;
}
if (volumeCutoff < -1 || volumeCutoff > 99) {
printf ("Bad argument to flag '-a'.\n");
showUsage (argv[0]);
return 1;
}
if (bitsPerSample != 8 && bitsPerSample != 16) {
printf ("Bad argument to flag '-b'.\n");
showUsage (argv[0]);
return 1;
}
time_t start_t;
time_t end_t;
if (startHH24MISS[0] == 0) {
// start time defaults to NOW.
start_t = time (0);
} else {
start_t = cvtHH24MISSToTime (startHH24MISS);
}
if (start_t == -1) {
printf ("Cannot initialize start time.\n");
showUsage (argv[0]);
return 1;
}
if (endHH24MISS[0] == 0) {
printf ("Missing parameter: -e end_time.\n");
showUsage (argv[0]);
return 1;
}
end_t = cvtHH24MISSToTime (endHH24MISS);
if (end_t == -1) {
printf ("Cannot initialize end time.\n");
showUsage (argv[0]);
return 1;
}
if (start_t >= end_t) {
printf ("Start time must precede end time.\n");
showUsage (argv[0]);
return 1;
}
// ENUMERATE THEN SELECT AUDIO CAPTURE DEVICE
{
int drv = 0;
numAudioDrivers = 0;
DirectSoundCaptureEnumerate ((LPDSENUMCALLBACK)soundEnumCallback, 0);
if (numAudioDrivers == 0) {
printf ("The computer has no audio capture devices.\n");
return 1;
} else if (numAudioDrivers == 1) {
drv = 0;
} else {
printf ("The computer has the following audio capture devices:\n");
for (int i = 0; i < numAudioDrivers; ++i) {
printf ("%2d: %s (%s)\n", i, audioDriverDesc[i], audioDriverName[i]);
}
printf ("Please select one from list above: ");
scanf ("%d", &drv);
if (drv < 0 || drv >= numAudioDrivers) {
printf ("Error: invalid selection.\n");
return 1;
}
}
captureDeviceGUID = audioDriverGUIDs + drv;
printf ("Recording capture device: %s (%s)\n", audioDriverDesc[drv],
audioDriverName[drv]);
}
dsc = initDirectSound (captureDeviceGUID);
if (! dsc) {
return 1;
}
// INITIALIZE AUDIO CAPTURE DEVICE
initWfxInput (sampleFreq, channels, bitsPerSample);
if (! createCaptureBuffer (wfxInput)) {
printf ("Error: cannot create capture buffer. Possible causes:\n");
printf ("- another program has is using the audio capture device.\n");
printf ("- the sampling parameters are unsupported (eg. sampling rate is
too high).\n");
return 1;
}
// OPEN WAVE FILE
waveFile = new CWaveFile;
// Get the format of the capture buffer in g_wfxCaptureWaveFormat
WAVEFORMATEX wfx;
ZeroMemory( &wfx, sizeof(WAVEFORMATEX) );
dscb->GetFormat (&wfx, sizeof(WAVEFORMATEX), NULL);
if (waveFile->Open (const_cast<char*> (waveFilename), &wfx,
WAVEFILE_WRITE)) {
printf ("Cannot open wave file.\n");
return 1;
}
// REPORT ON PENDING ACTIVITIES
printf ("\nRecording schedule:\n");
printf (" Begin : %.19s\n", ctime (&start_t));
printf (" End : %.19s\n", ctime (&end_t));
{
const int duration = end_t - start_t;
int hr;
int min;
int sec;
cvtTimetToComponents (duration, hr, min, sec);
printf (" Duration: %02d:%02d:%02d\n", hr, min, sec);
}
printf (" Quality : %dHz %d-bit %s\n", sampleFreq, bitsPerSample,
channels == 1 ? "Mono" : "Stereo");
if (volumeCutoff != -1) {
printf ("Volume Cutoff: %d\n\n", volumeCutoff);
}
// SETUP SIGNAL HANDLER
if (! setShutdownSigHandler ()) {
printf ("Cannot register signal handler.\n");
return 1;
}
// WAIT TO BEGIN RECORDING
int delay;
delay = start_t - time (0);
if (delay > 0) {
printf ("\n");
do {
int hr;
int min;
int sec;
Sleep (POLL_INTERVAL_MSEC);
delay = start_t - time (0);
cvtTimetToComponents (delay, hr, min, sec);
printf ("Waiting %02d:%02d:%02d before recording starts...\r", hr, min,
sec);
} while ( delay > 0
&& ! shutdownSignalled);
printf ("\n");
}
// BEGIN RECORDING
if (! shutdownSignalled) {
printf ("\nRecording has started.\n");
startRecording (end_t, volumeCutoff, verbose);
printf ("\nRecording has finished.\n");
}
// SHUTDOWN
cleanup ();
return 0;
}
/*
Live
Frequency Response: 10Hz - 44KHz
Signal to Noise Ratio: >96 db
Noise Floor: -120dB
Sampling Rate for Playback/Recording (Stereo): 8 KHz - 48 KHz
Supply Voltage Requirement (Loading): +5, +12, -12 Volt
Current Consumption (Typical): 300, 500, 30 mA respectively
Microphone Impedance: 600 Ohms
Line-In Impedance: 47 KOhms
CD Audio-In Impedance: 50 KOhms
Microphone Sensitivity: 10 - 200 mVpp
Line-In Sensitivity: 0 - 2 Vpp
CD Audio-In Sensitivity: 0 - 2 Vpp
AD/DA Resolution: 16 bits
Output Power (Max.): N/A - available only on specific models
input jacs (facing card)
1 2 3 4 5
1=joystick
2=
3=headphones
4=microphone in
5=line in (for sony portable radio)
*/


|