Implement Pink noise output.

Thank you Nathan Hurst and Phil Burk.
This commit is contained in:
James Courtier-Dutton 2005-04-14 15:53:53 +00:00
parent be0e80e87c
commit e7e0f401f1
5 changed files with 178 additions and 13 deletions

View file

@ -1,7 +1,7 @@
SPEAKER_TEST_VERSION = 0.0.7 SPEAKER_TEST_VERSION = 0.0.8
INCLUDES = -I$(top_srcdir)/include -DVERSION=\"$(SPEAKER_TEST_VERSION)\" INCLUDES = -I$(top_srcdir)/include -DVERSION=\"$(SPEAKER_TEST_VERSION)\"
bin_PROGRAMS = speaker-test bin_PROGRAMS = speaker-test
speaker_test_SOURCES = speaker-test.c speaker_test_SOURCES = speaker-test.c pink.c
man_MANS = speaker-test.1 man_MANS = speaker-test.1
EXTRA_DIST = readme.txt speaker-test.1 EXTRA_DIST = readme.txt speaker-test.1 pink.h

96
speaker-test/pink.c Normal file
View file

@ -0,0 +1,96 @@
/*
patest_pink.c
generate Pink Noise using Gardner method.
Optimization suggested by James McCartney uses a tree
to select which random value to replace.
x x x x x x x x x x x x x x x x
x x x x x x x x
x x x x
x x
x
Tree is generated by counting trailing zeros in an increasing index.
When the index is zero, no random number is selected.
This program uses the Portable Audio library which is under development.
For more information see: http://www.audiomulch.com/portaudio/
Author: Phil Burk, http://www.softsynth.com
Revision History:
Copyleft 1999 Phil Burk - No rights reserved.
*/
#include <stdio.h>
#include <math.h>
#include "pink.h"
/************************************************************/
/* Calculate pseudo-random 32 bit number based on linear congruential method. */
static unsigned long generate_random_number( void )
{
static unsigned long rand_seed = 22222; /* Change this for different random sequences. */
rand_seed = (rand_seed * 196314165) + 907633515;
return rand_seed;
}
/* Setup PinkNoise structure for N rows of generators. */
void initialize_pink_noise( pink_noise_t *pink, int num_rows )
{
int i;
long pmax;
pink->pink_index = 0;
pink->pink_index_mask = (1<<num_rows) - 1;
/* Calculate maximum possible signed random value. Extra 1 for white noise always added. */
pmax = (num_rows + 1) * (1<<(PINK_RANDOM_BITS-1));
pink->pink_scalar = 1.0f / pmax;
/* Initialize rows. */
for( i=0; i<num_rows; i++ ) pink->pink_rows[i] = 0;
pink->pink_running_sum = 0;
}
/* generate Pink noise values between -1.0 and +1.0 */
float generate_pink_noise_sample( pink_noise_t *pink )
{
long new_random;
long sum;
float output;
/* Increment and mask index. */
pink->pink_index = (pink->pink_index + 1) & pink->pink_index_mask;
/* If index is zero, don't update any random values. */
if( pink->pink_index != 0 )
{
/* Determine how many trailing zeros in PinkIndex. */
/* This algorithm will hang if n==0 so test first. */
int num_zeros = 0;
int n = pink->pink_index;
while( (n & 1) == 0 )
{
n = n >> 1;
num_zeros++;
}
/* Replace the indexed ROWS random value.
* Subtract and add back to Running_sum instead of adding all the random
* values together. Only one changes each time.
*/
pink->pink_running_sum -= pink->pink_rows[num_zeros];
new_random = ((long)generate_random_number()) >> PINK_RANDOM_SHIFT;
pink->pink_running_sum += new_random;
pink->pink_rows[num_zeros] = new_random;
}
/* Add extra white noise value. */
new_random = ((long)generate_random_number()) >> PINK_RANDOM_SHIFT;
sum = pink->pink_running_sum + new_random;
/* Scale to range of -1.0 to 0.9999. */
output = pink->pink_scalar * sum;
return output;
}

15
speaker-test/pink.h Normal file
View file

@ -0,0 +1,15 @@
#define PINK_MAX_RANDOM_ROWS (30)
#define PINK_RANDOM_BITS (24)
#define PINK_RANDOM_SHIFT ((sizeof(long)*8)-PINK_RANDOM_BITS)
typedef struct
{
long pink_rows[PINK_MAX_RANDOM_ROWS];
long pink_running_sum; /* Used to optimize summing of generators. */
int pink_index; /* Incremented each sample. */
int pink_index_mask; /* Index wrapped by ANDing with this mask. */
float pink_scalar; /* Used to scale within range of -1.0 to +1.0 */
} pink_noise_t;
void initialize_pink_noise( pink_noise_t *pink, int num_rows );
float generate_pink_noise_sample( pink_noise_t *pink );

View file

@ -26,6 +26,8 @@ speaker\-test \- command-line speaker test tone generator for ALSA
.IR FREQ ] .IR FREQ ]
.BR "" [ \-p | \-\-period .BR "" [ \-p | \-\-period
.IR TIME ] .IR TIME ]
.BR "" [ \-n | \-\-noise
.IR TIME ]
.BR "" [ \-s | \-\-speaker " " "1|2" ] .BR "" [ \-s | \-\-speaker " " "1|2" ]
.P .P
.B speaker\-test .B speaker\-test
@ -60,6 +62,14 @@ period size of \fITIME\fP microseconds
\fB\-r\fP | \fB\-\-rate\fP \fIRATE\fP \fB\-r\fP | \fB\-\-rate\fP \fIRATE\fP
stream of \fIRATE\fP Hz stream of \fIRATE\fP Hz
.TP
\fB\-t\fP | \fB\-\-test\fP \fB1\fP|\fB2\fP
-t1 means use pink noise (default).
Pink noise is perceptually uniform noise - that is, it sounds like every frequency at once. If you can hear any tone it may indicate resonances in your speaker system or room.
-t2 means use sine wave.
.TP .TP
\fB\-s\fP | \fB\-\-speaker\fP \fB1\fP|\fB2\fP \fB\-s\fP | \fB\-\-speaker\fP \fB1\fP|\fB2\fP
Test speaker 1 or speaker 2 only, rather than both Test speaker 1 or speaker 2 only, rather than both
@ -88,3 +98,4 @@ To send a nice low 75Hz tone to the Woofer and then exit without touching any ot
.SH AUTHOR .SH AUTHOR
The speaker-test program was written by James Courtier-Dutton. The speaker-test program was written by James Courtier-Dutton.
Pink noise support was added by Nathan Hurst.

View file

@ -1,5 +1,6 @@
/* /*
* Copyright (C) 2000-2004 James Courtier-Dutton * Copyright (C) 2000-2004 James Courtier-Dutton
* Copyright (C) 2005 Nathan Hurst
* *
* This file is part of the speaker-test tool. * This file is part of the speaker-test tool.
* *
@ -22,8 +23,14 @@
* *
* Main program by James Courtier-Dutton (including some source code fragments from the alsa project.) * Main program by James Courtier-Dutton (including some source code fragments from the alsa project.)
* Some cleanup from Daniel Caujolle-Bert <segfault@club-internet.fr> * Some cleanup from Daniel Caujolle-Bert <segfault@club-internet.fr>
* Pink noise option added Nathan Hurst,
* based on generator by Phil Burk (pink.c)
* *
* Changelog: * Changelog:
* 0.0.8 Added support for pink noise output.
* Changelog:
* 0.0.7 Added support for more than 6 channels.
* Changelog:
* 0.0.6 Added support for different sample formats. * 0.0.6 Added support for different sample formats.
* *
* $Id: speaker_test.c,v 1.00 2003/11/26 19:43:38 jcdutton Exp $ * $Id: speaker_test.c,v 1.00 2003/11/26 19:43:38 jcdutton Exp $
@ -42,6 +49,7 @@
#include <alsa/asoundlib.h> #include <alsa/asoundlib.h>
#include <sys/time.h> #include <sys/time.h>
#include <math.h> #include <math.h>
#include "pink.h"
static char *device = "plughw:0,0"; /* playback device */ static char *device = "plughw:0,0"; /* playback device */
static snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */ static snd_pcm_format_t format = SND_PCM_FORMAT_S16; /* sample format */
@ -52,6 +60,8 @@ static unsigned int buffer_time = 500000; /* ring buffer lengt
static unsigned int period_time = 100000; /* period time in us */ static unsigned int period_time = 100000; /* period time in us */
#define PERIODS 4 #define PERIODS 4
static double freq = 440; /* sinusoidal wave frequency in Hz */ static double freq = 440; /* sinusoidal wave frequency in Hz */
static int test_type = 1; /* Test type. 1 = noise, 2 = sine wave */
static pink_noise_t pink;
static snd_output_t *output = NULL; static snd_output_t *output = NULL;
static snd_pcm_uframes_t buffer_size; static snd_pcm_uframes_t buffer_size;
static snd_pcm_uframes_t period_size; static snd_pcm_uframes_t period_size;
@ -151,13 +161,30 @@ static void generate_sine(signed short *samples, int channel, int count, double
*_phase = phase; *_phase = phase;
} }
/* FIXME: Implement, because it is a better test than sine wave /* Pink noise is a better test than sine wave because we can tell
* because we can tell where pink noise is coming from more easily that a sine wave * where pink noise is coming from more easily that a sine wave.
*/ */
#if 0
static void generate_pink_noise( snd_pcm_uframes_t offset, int count, double *_phase) {
static void generate_pink_noise( signed short *samples, int channel, int count) {
double res;
int chn, ires;
while (count-- > 0) {
for(chn=0;chn<channels;chn++) {
if (chn==channel) {
// I've chosen to write different noise to each channel as it
// is more pleasant. -- njh
res = generate_pink_noise_sample(&pink) * 32767;
ires = res;
*samples++ = ires;
} else
*samples++ = 0;
}
}
} }
#endif
static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, snd_pcm_access_t access) { static int set_hwparams(snd_pcm_t *handle, snd_pcm_hw_params_t *params, snd_pcm_access_t access) {
unsigned int rrate; unsigned int rrate;
@ -350,8 +377,11 @@ static int write_loop(snd_pcm_t *handle, int channel, int periods, signed short
int err, cptr, n; int err, cptr, n;
for(n = 0; n < periods; n++) { for(n = 0; n < periods; n++) {
if(test_type==1)
generate_pink_noise(samples, channel, period_size);
else
generate_sine(samples, channel, period_size, &phase);
generate_sine(samples, channel, period_size, &phase);
ptr = samples; ptr = samples;
cptr = period_size; cptr = period_size;
@ -393,6 +423,7 @@ static void help(void)
"-F,--format sample format\n" "-F,--format sample format\n"
"-b,--buffer ring buffer size in us\n" "-b,--buffer ring buffer size in us\n"
"-p,--period period size in us\n" "-p,--period period size in us\n"
"-t,--test 1=use pink noise, 2=use sine wave\n"
"-s,--speaker single speaker test. Values 1=Left or 2=right\n" "-s,--speaker single speaker test. Values 1=Left or 2=right\n"
"\n"); "\n");
#if 1 #if 1
@ -427,7 +458,8 @@ int main(int argc, char *argv[]) {
{"format", 1, NULL, 'F'}, {"format", 1, NULL, 'F'},
{"buffer", 1, NULL, 'b'}, {"buffer", 1, NULL, 'b'},
{"period", 1, NULL, 'p'}, {"period", 1, NULL, 'p'},
{"speaker", 1, NULL, 's'}, {"test", 1, NULL, 't'},
{"speaker", 1, NULL, 's'},
{NULL, 0, NULL, 0 }, {NULL, 0, NULL, 0 },
}; };
@ -440,7 +472,7 @@ int main(int argc, char *argv[]) {
while (1) { while (1) {
int c; int c;
if ((c = getopt_long(argc, argv, "hD:r:c:f:F:b:p:s:", long_option, NULL)) < 0) if ((c = getopt_long(argc, argv, "hD:r:c:f:F:b:p:t:s:", long_option, NULL)) < 0)
break; break;
switch (c) { switch (c) {
@ -478,6 +510,11 @@ int main(int argc, char *argv[]) {
period_time = period_time < 1000 ? 1000 : period_time; period_time = period_time < 1000 ? 1000 : period_time;
period_time = period_time > 1000000 ? 1000000 : period_time; period_time = period_time > 1000000 ? 1000000 : period_time;
break; break;
case 't':
test_type = atoi(optarg);
test_type = test_type < 1 ? 1 : test_type;
test_type = test_type > 2 ? 2 : test_type;
break;
case 's': case 's':
speaker = atoi(optarg); speaker = atoi(optarg);
speaker = speaker < 1 ? 0 : speaker; speaker = speaker < 1 ? 0 : speaker;
@ -507,7 +544,11 @@ int main(int argc, char *argv[]) {
printf("Playback device is %s\n", device); printf("Playback device is %s\n", device);
printf("Stream parameters are %iHz, %s, %i channels\n", rate, snd_pcm_format_name(format), channels); printf("Stream parameters are %iHz, %s, %i channels\n", rate, snd_pcm_format_name(format), channels);
printf("Sine wave rate is %.4fHz\n", freq); if(test_type==1)
printf("Using 16 octaves of pink noise\n");
else
printf("Sine wave rate is %.4fHz\n", freq);
loop: loop:
while ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) { while ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
printf("Playback open error: %d,%s\n", err,snd_strerror(err)); printf("Playback open error: %d,%s\n", err,snd_strerror(err));
@ -529,6 +570,8 @@ loop:
} }
samples = malloc((period_size * channels * snd_pcm_format_width(format)) / 8); samples = malloc((period_size * channels * snd_pcm_format_width(format)) / 8);
initialize_pink_noise( &pink, 16);
if (samples == NULL) { if (samples == NULL) {
printf("No enough memory\n"); printf("No enough memory\n");
exit(EXIT_FAILURE); exit(EXIT_FAILURE);