2020年2月15日 星期六

Alsa Audio Capture - RMS

ref : Introduction to Sound Programming with ALSA
Simple Sound Recording
/*

This example reads from the default PCM device
and writes to standard output for 5 seconds of data.

*/

/* Use the newer ALSA API */
#define ALSA_PCM_NEW_HW_PARAMS_API

#include <alsa/asoundlib.h>
#include <math.h>
#include <unistd.h>

//#define DEVICE "hw:1,0"
#define DEVICE "default"
/
#define CH_2 1
//#define CH_4 1

float rmsValue(signed char arr[], int n)
  {
      float square = 0.0;
      float mean = 0.0, root = 0.0, db= 0.0;
      int i;

      // Calculate square.
      for (i = 0; i < n; i++)
      {
          //printf("arr[%d]=%d, pow=%f\n", i, arr[i], pow(arr[i], 2));
          square += pow(arr[i], 2); 
      }
      //printf("n=%d, square=%f\n", n, square);
  
      // Calculate Mean.
      mean = (square / (float)(n));
  
      // Calculate Root.
      root = sqrt(mean);

      return root;
  }

//for ARMv7
signed char complement (signed char *a)
{
     if((*a>>7))
     {
         *a=*a&0xEF;
         *a = ~(*a) + 1;
         *a = *a * (-1); //for ARMv7
     }
     else
     {
         *a=*a & 0xEF;
     }
  return *a;
}


int main() 
{
  long loops;
  int rc;
  int size;
  snd_pcm_t *handle;
  snd_pcm_hw_params_t *params;
  unsigned int val;
  int dir;
  snd_pcm_uframes_t frames;
  signed char *buffer;
  signed char *channelbuf;
  int i=0, j=0, k=0, m=0, n=0;
#if CH_2
  int channels=2;
  float root[2]={0}, aroot[2]={0};
#endif
#if CH_4
  int channels=4;
  float root[4]={0}, aroot[4]={0};
#endif
  int rate = 16000;
  signed char tmp=0;

  /* Open PCM device for recording (capture). */
  rc = snd_pcm_open(&handle, DEVICE,
                    SND_PCM_STREAM_CAPTURE, 0);
  //can use "dsnoop_test", "pulghw", or others to replace "default"
  if (rc < 0) {
    fprintf(stderr,
            "unable to open pcm device: %s\n",
            snd_strerror(rc));
    exit(1);
  }

  /* Allocate a hardware parameters object. */
  snd_pcm_hw_params_alloca(&params);

  /* Fill it in with default values. */
  snd_pcm_hw_params_any(handle, params);

  /* Set the desired hardware parameters. */

  /* Interleaved mode */
  snd_pcm_hw_params_set_access(handle, params,
                      SND_PCM_ACCESS_RW_INTERLEAVED);
  /* Signed 16-bit little-endian format */
  snd_pcm_hw_params_set_format(handle, params,
                              SND_PCM_FORMAT_S16_LE);

  /* Two channels (stereo) */
  snd_pcm_hw_params_set_channels(handle, params, channels);

  val = rate;
  snd_pcm_hw_params_set_rate_near(handle, params,
                                  &val, &dir);

  /* Set period size to 32 frames. */
#if CH_2
  frames = 32;
#endif
#if CH_4
  frames = 32 * 2;
#endif
  snd_pcm_hw_params_set_period_size_near(handle,
                              params, &frames, &dir);
#if 1
  /* Write the parameters to the driver */
  rc = snd_pcm_hw_params(handle, params);
  if (rc < 0) {
    fprintf(stderr,
            "unable to set hw parameters: %s\n",
            snd_strerror(rc));
    exit(1);
  }

  /* Use a buffer large enough to hold one period */
  snd_pcm_hw_params_get_period_size(params,
                                      &frames, &dir);
#if CH_2
  size = frames * 4; /* 2 bytes/sample, 2 channels */
#endif
#if CH_4
  size = frames * 4 * 2; /* 2 bytes/sample, 2 channels */
#endif
  buffer = (signed char *) malloc(sizeof(signed char)*size);

  /* We want to loop for 5 seconds */
  snd_pcm_hw_params_get_period_time(params,
                                         &val, &dir);
    loops = 5000000 / val;
    printf("loops=%ld, val=%d, frames=%ld, size=%d\n", loops, val, frames, size);
    channelbuf = (signed char *) malloc(sizeof(signed char)*size);
  
    while (loops > 0) 
    {
      loops--;
      //memset(buffer, '0', sizeof(signed char)*size);
      for(m=0; m<size; m++)
          buffer[m]=0;
      rc = snd_pcm_readi(handle, buffer, frames);
      if (rc == -EPIPE) {
        /* EPIPE means overrun */
        fprintf(stderr, "overrun occurred\n");
        snd_pcm_prepare(handle);
        break;
      } else if (rc < 0) {
        fprintf(stderr,
                "error from read: %s\n",
                snd_strerror(rc));
      } else if (rc != (int)frames) {
        fprintf(stderr, "short read, read %d frames\n", rc);
      }
  #if 0  //for recording
      rc = write(1, buffer, size);
      if (rc != size)
        fprintf(stderr, "short write: wrote %d bytes\n", rc);
    } //while
  #else

      for(j=0; j<channels; ++j)
      {
          for (i=j, k=0; i <rc ; i += channels, ++k)
          {

             memset(channelbuf, '0', sizeof(signed char)*size);
             for(m=0; m<size; m++)
                  channelbuf[m]=0;
             tmp=complement(buffer+(i+j)); //for ARMv7
#if 0
             printf("k=%3d,tmp=%4d;  ",k , tmp);
             if ( k%channels==0)
                 printf("\n");
#else
             channelbuf[k]=tmp;
             //printf("ch=%d, chbuf[%d]=%3d;  ", j, k, channelbuf[k]);
             //if ( k%channels==0)
             //    printf("\n");
#endif
          }
          root[j]=rmsValue(channelbuf, k+1);
#if 0
          printf("root[%d]=%f  ",j, root[j]);
          if ( j%channels==1)
              printf("\n");
#endif
          aroot[j] += root[j];
          //printf("aroot[%i]=%f\n", j, aroot[j]);
      }
    } //while
#if 1
    for(j=0; j<channels; j++)
        fprintf(stderr, "aroot[%i]=%f\n", j, aroot[j]);
    for(j=0; j<channels; j++)
    {
        root[j] = 10 * log10(aroot[j]);
        fprintf(stderr, "root[%i]=%f\n", j, root[j]);
    }
#endif
  #endif
  
    snd_pcm_drain(handle);
    snd_pcm_close(handle);
    free(buffer);
    free(channelbuf);
  #endif
    return 0;
}


Compile: gcc rec.c -o rec -lasound -lm

ref:
1.通过pcm音频数据计算分贝
2.Some typical Sound Pressure and Sound Pressure Levels
3.db-rms
4.Alsa Audio Capture by c language

沒有留言:

張貼留言