mirror of
https://github.com/alsa-project/alsa-utils
synced 2025-01-03 00:49:47 +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)\"
|
||||
bin_PROGRAMS = speaker-test
|
||||
speaker_test_SOURCES = speaker-test.c
|
||||
speaker_test_SOURCES = speaker-test.c pink.c
|
||||
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 ]
|
||||
.BR "" [ \-p | \-\-period
|
||||
.IR TIME ]
|
||||
.BR "" [ \-n | \-\-noise
|
||||
.IR TIME ]
|
||||
.BR "" [ \-s | \-\-speaker " " "1|2" ]
|
||||
.P
|
||||
.B speaker\-test
|
||||
|
@ -60,6 +62,14 @@ period size of \fITIME\fP microseconds
|
|||
\fB\-r\fP | \fB\-\-rate\fP \fIRATE\fP
|
||||
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
|
||||
\fB\-s\fP | \fB\-\-speaker\fP \fB1\fP|\fB2\fP
|
||||
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
|
||||
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) 2005 Nathan Hurst
|
||||
*
|
||||
* 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.)
|
||||
* 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:
|
||||
* 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.
|
||||
*
|
||||
* $Id: speaker_test.c,v 1.00 2003/11/26 19:43:38 jcdutton Exp $
|
||||
|
@ -42,6 +49,7 @@
|
|||
#include <alsa/asoundlib.h>
|
||||
#include <sys/time.h>
|
||||
#include <math.h>
|
||||
#include "pink.h"
|
||||
|
||||
static char *device = "plughw:0,0"; /* playback device */
|
||||
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 */
|
||||
#define PERIODS 4
|
||||
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_pcm_uframes_t buffer_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;
|
||||
}
|
||||
|
||||
/* FIXME: Implement, because it is a better test than sine wave
|
||||
* because we can tell where pink noise is coming from more easily that a sine wave
|
||||
/* Pink noise is a better test than sine wave because we can tell
|
||||
* 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) {
|
||||
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;
|
||||
|
||||
for(n = 0; n < periods; n++) {
|
||||
|
||||
generate_sine(samples, channel, period_size, &phase);
|
||||
if(test_type==1)
|
||||
generate_pink_noise(samples, channel, period_size);
|
||||
else
|
||||
generate_sine(samples, channel, period_size, &phase);
|
||||
|
||||
ptr = samples;
|
||||
cptr = period_size;
|
||||
|
||||
|
@ -393,6 +423,7 @@ static void help(void)
|
|||
"-F,--format sample format\n"
|
||||
"-b,--buffer ring buffer 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"
|
||||
"\n");
|
||||
#if 1
|
||||
|
@ -427,7 +458,8 @@ int main(int argc, char *argv[]) {
|
|||
{"format", 1, NULL, 'F'},
|
||||
{"buffer", 1, NULL, 'b'},
|
||||
{"period", 1, NULL, 'p'},
|
||||
{"speaker", 1, NULL, 's'},
|
||||
{"test", 1, NULL, 't'},
|
||||
{"speaker", 1, NULL, 's'},
|
||||
{NULL, 0, NULL, 0 },
|
||||
};
|
||||
|
||||
|
@ -440,7 +472,7 @@ int main(int argc, char *argv[]) {
|
|||
while (1) {
|
||||
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;
|
||||
|
||||
switch (c) {
|
||||
|
@ -478,6 +510,11 @@ int main(int argc, char *argv[]) {
|
|||
period_time = period_time < 1000 ? 1000 : period_time;
|
||||
period_time = period_time > 1000000 ? 1000000 : period_time;
|
||||
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':
|
||||
speaker = atoi(optarg);
|
||||
speaker = speaker < 1 ? 0 : speaker;
|
||||
|
@ -507,7 +544,11 @@ int main(int argc, char *argv[]) {
|
|||
|
||||
printf("Playback device is %s\n", device);
|
||||
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:
|
||||
while ((err = snd_pcm_open(&handle, device, SND_PCM_STREAM_PLAYBACK, 0)) < 0) {
|
||||
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);
|
||||
initialize_pink_noise( &pink, 16);
|
||||
|
||||
if (samples == NULL) {
|
||||
printf("No enough memory\n");
|
||||
exit(EXIT_FAILURE);
|
||||
|
|
Loading…
Reference in a new issue