Linux ALSA音频PCM播放编程
使用ALSA播放两个频率的单音,并使用GNU Radio中的Audio Source和FFT来观测声音的频谱。
1 #include <alsa/asoundlib.h> 2 #include <math.h> 3 #include <inttypes.h> 4 5 int main(int argc, char **argv) 6 { 7 long loops; 8 snd_pcm_t *handle; 9 snd_pcm_hw_params_t *params; 10 snd_pcm_uframes_t frames; 11 unsigned int val; 12 int rc; 13 int size; 14 int dir; 15 char *buffer; 16 17 /* Open PCM device for playback. */ 18 rc = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0); 19 if (rc < 0) { 20 fprintf(stderr, "unable to open pcm device: %s\n", snd_strerror(rc)); 21 exit(1); 22 } 23 24 /* Allocate a hardware parameters object. */ 25 snd_pcm_hw_params_alloca(¶ms); 26 27 /* Fill it in with default values. */ 28 snd_pcm_hw_params_any(handle, params); 29 30 /* Interleaved mode */ 31 snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); 32 /* Signed 16-bit format */ 33 snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16); 34 /* Two channels (stereo) */ 35 snd_pcm_hw_params_set_channels(handle, params, 2); 36 /* 16000 samples/second sampling rate */ 37 val = 16000; 38 snd_pcm_hw_params_set_rate_near(handle, params, &val, &dir); 39 /* Set period size to 32 frames. */ 40 frames = 32; 41 snd_pcm_hw_params_set_period_size_near(handle, params, &frames, &dir); 42 43 /* Write the parameters to the driver */ 44 rc = snd_pcm_hw_params(handle, params); 45 if (rc < 0) { 46 fprintf(stderr, "unable to set hw parameters: %s\n", snd_strerror(rc)); 47 exit(1); 48 } 49 50 /* Use a buffer large enough to hold one period */ 51 snd_pcm_hw_params_get_period_size(params, &frames, &dir); 52 53 size = frames * 2 * 2; /* 2 bytes/sample, 2 channels */ 54 55 buffer = (char *) malloc(size); 56 if(buffer == NULL) { 57 fprintf(stderr, "Not enough Memory!\n"); 58 exit(1); 59 } 60 61 /* We want to loop for 15 seconds */ 62 snd_pcm_hw_params_get_period_time(params, &val, &dir); 63 64 /* 15 seconds in microseconds divided by period time */ 65 loops = 15000000 / val; 66 67 for (size_t i = 0; i < size; i += 4) { 68 // Generate a 500Hz tone 69 *((int16_t *)(buffer + i)) = (uint16_t)(16384.0*sin(i*2.0*M_PI/size)); 70 *((int16_t *)(buffer + i + 2)) = (uint16_t)(16384.0*sin(i*2.0*M_PI/size)); 71 // Generate a 2kHz tone 72 *((int16_t *)(buffer + i)) += (uint16_t)(16384.0*sin(i*8.0*M_PI/size)); 73 *((int16_t *)(buffer + i + 2)) += (uint16_t)(16384.0*sin(i*8.0*M_PI/size)); 74 } 75 76 while (loops > 0) { 77 loops--; 78 //rc = read(0, buffer, size); 79 //if (rc == 0) { 80 // fprintf(stderr, "end of file on input\n"); 81 // break; 82 //} else if (rc != size) { 83 // fprintf(stderr, "short read: read %d bytes\n", rc); 84 //} 85 86 rc = snd_pcm_writei(handle, buffer, frames); 87 if (rc == -EPIPE) { 88 /* EPIPE means underrun */ 89 fprintf(stderr, "underrun occurred\n"); 90 snd_pcm_prepare(handle); 91 } else if (rc < 0) { 92 fprintf(stderr,"error from writei: %s\n", snd_strerror(rc)); 93 } else if (rc != (int)frames) { 94 fprintf(stderr,"short write, write %d frames\n", rc); 95 } 96 } 97 snd_pcm_drop(handle); 98 snd_pcm_drain(handle); 99 snd_pcm_close(handle); 100 free(buffer); 101 return 0; 102 }