mirror of
https://github.com/alsa-project/alsa-utils
synced 2024-11-10 03:35:42 +01:00
Implement Pink noise output.
Thank you Nathan Hurst and Phil Burk.
This commit is contained in:
parent
be0e80e87c
commit
e7e0f401f1
5 changed files with 178 additions and 13 deletions
|
@ -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
96
speaker-test/pink.c
Normal 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
15
speaker-test/pink.h
Normal 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 );
|
|
@ -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.
|
||||||
|
|
|
@ -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);
|
||||||
|
|
Loading…
Reference in a new issue