📄 wmwavegen.c
字号:
};
#endif
/*
* 10th partial
*
* y = sin(x) + sin(3x)/3 + sin(5x)/5 + sin(7x)/7 + sin(9x)/9 + sin(11x)/11
* + sin(13x)/13 + sin(15x)/15 + sin(17x)/17 + sin(19x)/19
*
* Nyquist divisor 38 => max frequency 1160Hz at 44.1kHz
*/
#if WM_SQUAREWAVE_PARTIAL10
static const short SquareWave10[] =
{
0, 16514, 28196, 32767, 31449, 27802, 25342, 25571,
27612, 29443, 29640, 28335, 26836, 26406, 27263, 28538,
29115, 28574, 27475, 26799, 27094, 28040, 28785, 28695,
27889, 27098, 27010, 27680, 28501, 28748, 28214, 27379,
26984, 27379, 28214, 28748, 28501, 27680, 27010, 27098,
27889, 28695, 28785, 28040, 27094, 26799, 27475, 28574,
29115, 28538, 27263, 26406, 26836, 28335, 29640, 29443,
27612, 25571, 25342, 27802, 31449, 32767, 28196, 16514,
0, -16514, -28196, -32767, -31449, -27802, -25342, -25571,
-27612, -29443, -29640, -28335, -26836, -26406, -27263, -28538,
-29115, -28574, -27475, -26799, -27094, -28040, -28785, -28695,
-27889, -27098, -27010, -27680, -28501, -28748, -28214, -27379,
-26984, -27379, -28214, -28748, -28501, -27680, -27010, -27098,
-27889, -28695, -28785, -28040, -27094, -26799, -27475, -28574,
-29115, -28538, -27263, -26406, -26836, -28335, -29640, -29443,
-27612, -25571, -25342, -27802, -31449, -32767, -28196, -16514,
0
};
#endif
/*
* Now an array to work from.
* Note the entries in the array should be in decreasing order of
* quality - i.e. should decrease in nyquistDivisor, and hence increase
* in maximum frequency. The last entry in the table should be the sine
* wave - this is the table used by default if no other table is
* appropriate.
*/
static const WaveTable SquareWavePartials[] =
{
#if WM_SQUAREWAVE_PARTIAL10
{ 38, SquareWave10 },
#endif
#if WM_SQUAREWAVE_PARTIAL9
{ 34, SquareWave9 },
#endif
#if WM_SQUAREWAVE_PARTIAL8
{ 30, SquareWave8 },
#endif
#if WM_SQUAREWAVE_PARTIAL7
{ 26, SquareWave7 },
#endif
#if WM_SQUAREWAVE_PARTIAL6
{ 22, SquareWave6 },
#endif
#if WM_SQUAREWAVE_PARTIAL5
{ 18, SquareWave5 },
#endif
#if WM_SQUAREWAVE_PARTIAL4
{ 14, SquareWave4 },
#endif
#if WM_SQUAREWAVE_PARTIAL3
{ 10, SquareWave3 },
#endif
#if WM_SQUAREWAVE_PARTIAL2
{ 6, SquareWave2 },
#endif
/* We always have a sine wave */
{ 2, SquareWave1 }
};
static const WaveTable SineWave = { 2, SquareWave1 };
#define TABLE_SIZE WM_ARRAY_COUNT( SquareWave1 )
#define NUM_TABLE_ENTRIES (TABLE_SIZE - 1)
/*-----------------------------------------------------------------------------
* Function: WMGenerateSineWave
*
* Generates a sine wave of the given frequency.
*
* Parameters:
* buffer buffer to receive samples
* nSamples number of samples to generate (mono or stereo,
* according to 'format')
* sampleRate output sample rate (e.g. 44100)
* frequency frequency to generate
* amplitude peak amplitude to generate (0 to 32768)
* format what the resulting data should look like. One of the
* WM_WAVEGEN_FORMAT constants.
* pRestart pointer to a restart context - position after previous call.
* Used to restart the wave where the previous call left off.
* Updated to the start position for the next call.
* Can be NULL - in this case will always start at the start of
* the wave.
*
* Returns: void
*
*---------------------------------------------------------------------------*/
void WMGenerateSineWave(WM_AUDIO_STEREO_SAMPLE *buffer,
int nSamples,
int sampleRate,
unsigned short frequency,
unsigned short amplitude,
WM_WAVEGEN_FORMAT format,
WM_WAVEGEN_CTX *pRestart
)
{
/*
* Call the common function to do the interpolation.
*/
private_GenerateWave( buffer,
nSamples,
sampleRate,
frequency,
amplitude,
format,
pRestart,
&SineWave
);
}
/*-----------------------------------------------------------------------------
* Function: WMGenerateSquareWave
*
* Generates as close an approximation as possible to a square wave of the
* given frequency.
*
* Parameters:
* buffer buffer to receive samples
* nSamples number of samples to generate (mono or stereo,
* according to 'format')
* sampleRate output sample rate (e.g. 44100)
* frequency frequency to generate
* amplitude peak amplitude to generate (0 to 32768)
* format what the resulting data should look like. One of the
* WM_WAVEGEN_FORMAT constants.
* pRestart pointer to a restart context - position after previous call.
* Used to restart the wave where the previous call left off.
* Updated to the start position for the next call.
* Can be NULL - in this case will always start at the start of
* the wave.
*
* Returns: void
*
*---------------------------------------------------------------------------*/
void WMGenerateSquareWave(WM_AUDIO_STEREO_SAMPLE *buffer,
int nSamples,
int sampleRate,
unsigned short frequency,
unsigned short amplitude,
WM_WAVEGEN_FORMAT format,
WM_WAVEGEN_CTX *pRestart
)
{
const WaveTable *pTable;
unsigned int table;
int maxFrequency;
/*
* Pick the best table possible.
*/
for ( table = 0; table < WM_ARRAY_COUNT( SquareWavePartials ); table++ )
{
pTable = &SquareWavePartials[table];
maxFrequency = sampleRate/pTable->nyquistDivisor;
if ( maxFrequency > frequency )
{
break;
}
}
/*
* If we couldn't find a table, we will have been left with the
* last table in the array - this should be the sine wave.
*/
/*
* Now call the common function to do the interpolation.
*/
private_GenerateWave( buffer,
nSamples,
sampleRate,
frequency,
amplitude,
format,
pRestart,
pTable
);
}
/*-----------------------------------------------------------------------------
* Function: private_GenerateWave (static)
*
* Generates the wave data, given the table to work from.
* Assumes 44.1kHz.
*
* Parameters:
* buffer buffer to receive samples
* nSamples number of samples to generate (mono or stereo,
* according to 'format')
* sampleRate output sample rate (e.g. 44100)
* frequency frequency to generate
* amplitude peak amplitude to generate (0 to 32768)
* format what the resulting data should look like. One of the
* WM_WAVEGEN_FORMAT constants.
* pRestart pointer to a restart context - position after previous call.
* Used to restart the wave where the previous call left off.
* Updated to the start position for the next call.
* Can be NULL - in this case will always start at the start of
* the wave.
* waveTable wave table containing samples to use
*
* Returns: void
*---------------------------------------------------------------------------*/
void private_GenerateWave(WM_AUDIO_STEREO_SAMPLE *buffer,
int nSamples,
int sampleRate,
unsigned short frequency,
unsigned short amplitude,
WM_WAVEGEN_FORMAT format,
WM_WAVEGEN_CTX *pRestart,
const WaveTable *waveTable
)
{
div_t delta;
div_t pos;
int tableLength;
int sample;
const short *waveData = waveTable->waveData;
WM_AUDIO_SAMPLE *sampleBuf = (WM_AUDIO_SAMPLE *) buffer;
/*WM_TRACE( hDevice, ("WMWaveGen: Generating %dHz at %dHz", frequency, sampleRate) );*/
/*
* Calculate the step size for the interpolation.
* (frequency/sampleRate) * samplesPerCycle
*/
tableLength = NUM_TABLE_ENTRIES;
delta = div( tableLength * frequency, sampleRate );
/* Set up pos */
if ( pRestart )
{
pos.quot = pRestart->quot;
pos.rem = pRestart->rem;
}
else
{
pos.quot = pos.rem = 0;
}
/*
* Now do the generation
*/
for ( sample = 0; sample < nSamples; sample++ )
{
int sampleVal;
short scaledVal;
/*
* If it's an integer (no remainder), we've landed directly on
* a sample, so we can just use it without interpolation.
*/
if ( 0 == pos.rem )
{
sampleVal = waveData[pos.quot];
}
else
{
/*
* Otherwise do a linear interpolation between the nearest
* two samples - the value we want is the lower of the two
* values plus the appropriate portion of the difference
* between the upper and lower values. Or, if v1 is the
* lower value, v2 is the upper value, and r is the fraction
* of the difference between the v1 and v2:
*
* v = v1 + r*(v2-v1)
*
* Note r is the remainder portion of the current position,
* divided by the sample rate.
*/
int difference = waveData[pos.quot+1] - waveData[pos.quot];
int sampleDelta = difference * pos.rem / sampleRate;
sampleVal = waveData[pos.quot] + sampleDelta;
}
/* Now scale according to the amplitude */
scaledVal = (short)((sampleVal*amplitude)/AMPLITUDE_DIVISOR);
/* Save the sample to the buffer */
switch ( format )
{
case WM_WAVEGEN_CONTIGUOUS:
sampleBuf[sample] = scaledVal;
break;
case WM_WAVEGEN_CONTIGUOUS_WIDE:
case WM_WAVEGEN_MONO:
buffer[sample].sampleVal = 0;
buffer[sample].mono = scaledVal;
break;
case WM_WAVEGEN_LEFT:
buffer[sample].stereo.left = scaledVal;
break;
case WM_WAVEGEN_RIGHT:
buffer[sample].stereo.right = scaledVal;
break;
case WM_WAVEGEN_STEREO:
buffer[sample].stereo.left = scaledVal;
buffer[sample].stereo.right = scaledVal;
break;
}
/*
* Increment our position in the wave table, wrapping around
* if necessary.
*/
pos.rem += delta.rem;
if ( pos.rem >= sampleRate )
{
pos.quot++;
pos.rem -= sampleRate;
}
pos.quot += delta.quot;
if ( pos.quot >= tableLength)
{
pos.quot %= tableLength;
}
}
/* Update the restart context for the next call. */
if ( pRestart )
{
pRestart->quot = pos.quot;
pRestart->rem = pos.rem;
}
}
#endif /* WM_TESTING || WM_AUDIO_WAVEGEN */
/*------------------------------ END OF FILE ---------------------------------*/
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -