title: How do I generate a 1kHz pure tone using asoundlib ALSA API in C++?
body: <p>I am following the sample code here to generate a pure tone sinewave using ALSA API in C++ in Linux: <a href="http://equalarea.com/paul/alsa-audio.html" rel="nofollow noreferrer">http://equalarea.com/paul/alsa-audio.html</a></p>
  <p>The code I am using is under &quot;A Minimal Interrupt-Driven Program&quot;. The code as is does not compile and my modified version of the code that actually compiles makes some sound is given below:</p>
  <pre><code>#include &lt;stdio.h&gt;
  #include &lt;stdlib.h&gt;
  #include &lt;errno.h&gt;
  #include &lt;poll.h&gt;
  #include &lt;math.h&gt;
  #include &lt;alsa/asoundlib.h&gt;
        
  snd_pcm_t *playback_handle;
  // short buf[4096];
  short buf[48*2];        // try 1000 Hz @48000, 48 samples @48000 = 1 ms
  
  void sine_gen(){
      // call once to generate the sine values
      // if this works do this inside callback function later on
      double tempSin = 0.0;
      #if 1
      // check if buf[] contains 2 channels interleaved
      for(int i = 0; i&lt;48*2; i+=2){
          tempSin = sin(2.0*M_PI*1000*i/48000);
          buf[i] = (short)(tempSin * pow(2.0, 15) );
          buf[i+1] = buf[i];
      }
      #else
      // check if buf[] contains single channel data
      for(int i = 0; i&lt;48*2; i++){
          tempSin = sin(2*M_PI*1000*i/48000);
          buf[i] = (short)(tempSin * pow(2.0, 15) );
      }   
      #endif
      
  }
  
  void SetAlsaMasterVolume(long volume)
  {
      long min, max;
      snd_mixer_t *handle;
      snd_mixer_selem_id_t *sid;
      const char *card = &quot;default&quot;;
      const char *selem_name = &quot;Master&quot;;
  
      snd_mixer_open(&amp;handle, 0);
      snd_mixer_attach(handle, card);
      snd_mixer_selem_register(handle, NULL, NULL);
      snd_mixer_load(handle);
  
      snd_mixer_selem_id_alloca(&amp;sid);
      snd_mixer_selem_id_set_index(sid, 0);
      snd_mixer_selem_id_set_name(sid, selem_name);
      snd_mixer_elem_t* elem = snd_mixer_find_selem(handle, sid);
  
      snd_mixer_selem_get_playback_volume_range(elem, &amp;min, &amp;max);
      snd_mixer_selem_set_playback_volume_all(elem, volume * max / 100);
  
      snd_mixer_close(handle);
  }
  
  int
  playback_callback (snd_pcm_sframes_t nframes)
  {
      int err;
  
      // printf (&quot;playback callback called with %u frames\n&quot;, nframes);
  
      /* ... fill buf with data ... */
      // if this part is empty, buf[] should have been filled correctly inside sine_gen()
      
      if ((err = snd_pcm_writei (playback_handle, buf, nframes)) &lt; 0) {
          fprintf (stderr, &quot;write failed (%s)\n&quot;, snd_strerror (err));
      }
      
      return err;
  }
        
  int main (int argc, char *argv[])
  {
  
      snd_pcm_hw_params_t *hw_params;
      snd_pcm_sw_params_t *sw_params;
      snd_pcm_sframes_t frames_to_deliver;
      int nfds;
      int err;
      struct pollfd *pfds;
      
      sine_gen();     // call this once to generate sinewave
  
      if ((err = snd_pcm_open (&amp;playback_handle, argv[1], SND_PCM_STREAM_PLAYBACK, 0)) &lt; 0) {
          fprintf (stderr, &quot;cannot open audio device %s (%s)\n&quot;, 
               argv[1],
               snd_strerror (err));
          exit (1);
      }
      
      if ((err = snd_pcm_hw_params_malloc (&amp;hw_params)) &lt; 0) {
          fprintf (stderr, &quot;cannot allocate hardware parameter structure (%s)\n&quot;,
               snd_strerror (err));
          exit (1);
      }
      
      if ((err = snd_pcm_hw_params_any (playback_handle, hw_params)) &lt; 0) {
          fprintf (stderr, &quot;cannot initialize hardware parameter structure (%s)\n&quot;,
               snd_strerror (err));
          exit (1);
      }
      
      if ((err = snd_pcm_hw_params_set_access (playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) &lt; 0) {
          fprintf (stderr, &quot;cannot set access type (%s)\n&quot;,
               snd_strerror (err));
          exit (1);
      }
      
      if ((err = snd_pcm_hw_params_set_format (playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) &lt; 0) {
          fprintf (stderr, &quot;cannot set sample format (%s)\n&quot;,
               snd_strerror (err));
          exit (1);
      }
      
      unsigned int f_s = 48000;
      // if ((err = snd_pcm_hw_params_set_rate_near (playback_handle, hw_params, 44100, 0)) &lt; 0) {        // causes segmentation fault; see // https://www.alsa-project.org/alsa-doc/alsa-lib/group___p_c_m___h_w___params.html#ga6014e0e1ec7934f8c745290e83e59199
      if ((err = snd_pcm_hw_params_set_rate_near (playback_handle, hw_params, &amp;f_s, 0)) &lt; 0) {
          fprintf (stderr, &quot;cannot set sample rate (%s)\n&quot;,
               snd_strerror (err));
          exit (1);
      }
      
      if ((err = snd_pcm_hw_params_set_channels (playback_handle, hw_params, 2)) &lt; 0) {
          fprintf (stderr, &quot;cannot set channel count (%s)\n&quot;,
               snd_strerror (err));
          exit (1);
      }
      
      if ((err = snd_pcm_hw_params (playback_handle, hw_params)) &lt; 0) {
          fprintf (stderr, &quot;cannot set parameters (%s)\n&quot;,
               snd_strerror (err));
          exit (1);
      }
      
      snd_pcm_hw_params_free (hw_params);
      
      /* tell ALSA to wake us up whenever 4096 or more frames
         of playback data can be delivered. Also, tell
         ALSA that we'll start the device ourselves.
      */
  
      if ((err = snd_pcm_sw_params_malloc (&amp;sw_params)) &lt; 0) {
          fprintf (stderr, &quot;cannot allocate software parameters structure (%s)\n&quot;,
               snd_strerror (err));
          exit (1);
      }
      if ((err = snd_pcm_sw_params_current (playback_handle, sw_params)) &lt; 0) {
          fprintf (stderr, &quot;cannot initialize software parameters structure (%s)\n&quot;,
               snd_strerror (err));
          exit (1);
      }
      // if ((err = snd_pcm_sw_params_set_avail_min (playback_handle, sw_params, 4096)) &lt; 0) {        // change this as per the size of the buffer used
      if ((err = snd_pcm_sw_params_set_avail_min (playback_handle, sw_params, 48*2)) &lt; 0) {       
          fprintf (stderr, &quot;cannot set minimum available count (%s)\n&quot;,
               snd_strerror (err));
          exit (1);
      }
      if ((err = snd_pcm_sw_params_set_start_threshold (playback_handle, sw_params, 0U)) &lt; 0) {
          fprintf (stderr, &quot;cannot set start mode (%s)\n&quot;,
               snd_strerror (err));
          exit (1);
      }
      if ((err = snd_pcm_sw_params (playback_handle, sw_params)) &lt; 0) {
          fprintf (stderr, &quot;cannot set software parameters (%s)\n&quot;,
               snd_strerror (err));
          exit (1);
      }
  
      /* the interface will interrupt the kernel every 4096 frames, and ALSA
         will wake up this program very soon after that.
      */
  
      if ((err = snd_pcm_prepare (playback_handle)) &lt; 0) {
          fprintf (stderr, &quot;cannot prepare audio interface for use (%s)\n&quot;,
               snd_strerror (err));
          exit (1);
      }
      
      // SetAlsaMasterVolume(0);      // attempt changing volume      // didn't make any difference
  
      while (1) {
  
          /* wait till the interface is ready for data, or 1 second
             has elapsed.
          */
  
          if ((err = snd_pcm_wait (playback_handle, 1000)) &lt; 0) {
                  fprintf (stderr, &quot;poll failed (%s)\n&quot;, strerror (errno));
                  break;
          }              
  
          /* find out how much space is available for playback data */
  
          if ((frames_to_deliver = snd_pcm_avail_update (playback_handle)) &lt; 0) {
              if (frames_to_deliver == -EPIPE) {
                  fprintf (stderr, &quot;an xrun occured\n&quot;);
                  break;
              } else {
                  fprintf (stderr, &quot;unknown ALSA avail update return value (%d)\n&quot;, 
                       frames_to_deliver);
                  break;
              }
          }
  
          // frames_to_deliver = frames_to_deliver &gt; 4096 ? 4096 : frames_to_deliver;
          frames_to_deliver = frames_to_deliver &gt; (48*2) ? (48*2) : frames_to_deliver;
  
          /* deliver the data */
  
          if (playback_callback (frames_to_deliver) != frames_to_deliver) {
                  fprintf (stderr, &quot;playback callback failed\n&quot;);
              break;
          }
      }
  
      snd_pcm_close (playback_handle);
      exit (0);
      
      return 0;
  }  
  </code></pre>
  <p>The program is making a continuous tone but it is not 1 kHz. I am not sure if it is because the output is saturated (it is very loud and I haven't figured out how to reduce the volume yet), or if I am generating/sending the sinewave samples to hardware incorrectly.</p>
  <p>My questions are the following:</p>
  <ol>
  <li><p>Is the audio buffer (buf[]) argument to snd_pcm_writei() supposed to contains interleaved 2 channel data? Is it supposed to be contain audio samples in the following format: buf[0] = Ch_A_0, buf[1] = Ch_B_0, buf[2] = Ch_A_1, buf[3] = Ch_B_1, buf[4] = Ch_A_2, buf[5] = Ch_B_2, ..., or is it only supposed to contain data for channel A and the hardware copied the same data to channel B?</p>
  </li>
  <li><p>Is snd_pcm_writei() a blocking function? Will this program reach <code>return err;</code> only after everything in buf[] has been sent out to the playback hardware?</p>
  </li>
  <li><p>What is the correct way to reduce volume in the ALSA api? I tried reducing the amplitude of the sinewave by calculating it as <code>tempSin = sin(2.0*M_PI*1000*i/48000)*0.1;</code> but the output is still the distorted tone sound, just a lot smaller. What is the ALSA function for reducing volume?</p>
  </li>
  <li><p>Is there clear sample code available that shows how to read/write audio data frame by frame from/to the record/playback hardware? I also need it to have some kind of interrupt mechanism, where a callback function would be called when a frame of data is ready to be received/sent.</p>
  </li>
  </ol>
  <p>This is specifically for the raspberry pi zero W running raspberry pi OS, if that is relevant.</p>