]> git.alsa-project.org Git - alsa-utils.git/commitdiff
Implement Pink noise output.
authorJames Courtier-Dutton <James@superbug.co.uk>
Thu, 14 Apr 2005 15:53:53 +0000 (15:53 +0000)
committerJames Courtier-Dutton <James@superbug.co.uk>
Thu, 14 Apr 2005 15:53:53 +0000 (15:53 +0000)
Thank you Nathan Hurst and Phil Burk.

speaker-test/Makefile.am
speaker-test/pink.c [new file with mode: 0644]
speaker-test/pink.h [new file with mode: 0644]
speaker-test/speaker-test.1
speaker-test/speaker-test.c

index e4db4847e6a6eb1eaeb2c3e5718cc8207fc00f43..c80e3fb46831d41c09c95147edac6e623950705a 100644 (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)\"
 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
 
diff --git a/speaker-test/pink.c b/speaker-test/pink.c
new file mode 100644 (file)
index 0000000..ef30755
--- /dev/null
@@ -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;
+}
diff --git a/speaker-test/pink.h b/speaker-test/pink.h
new file mode 100644 (file)
index 0000000..0d17f7a
--- /dev/null
@@ -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 );
index e9f521bf2d1aef50f89a5531e4bff36302c4ca3e..07a6485d83132a2ca9b681879c1a2eda33f98b89 100644 (file)
@@ -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.
index d3e8691fda932b50907916a8dc5a79e99251814c..867735ce42752d57c04e4e83f2545ab31001c2bf 100644 (file)
@@ -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.
  *
  *
  * 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);