|
| 1 | +/*-------------------------------------------------- |
| 2 | +ALSA auto. record and play test |
| 3 | +Quote from: http://blog.csdn.net/ljclx1748/article/details/8606831 |
| 4 | +
|
| 5 | +Usage: ./autorecord |
| 6 | +It will monitor surrounding sound wave and trigger 10s recording if loud voice is sensed, |
| 7 | +then it will playback. The sound will also be saved to a raw file. |
| 8 | +
|
| 9 | +1. use alsamixer to adjust Capture and ADC PCM value |
| 10 | + ADC PCM 0-255 (240) |
| 11 | + Capture 0-63 (53) |
| 12 | +2. some explanation: |
| 13 | + sample: usually 8bits or 16bits, one sample data width. |
| 14 | + channel: 1-Mono. 2-Stereo |
| 15 | + frame: sizeof(one sample)*channels |
| 16 | + rate: frames per second |
| 17 | + period: Max. frame numbers hard ware can be handled each time. |
| 18 | + chunk: frames receive from/send to hard ware each time. |
| 19 | + buffer: N*periods |
| 20 | + interleaved mode:record period data frame by frame, such as frame1(Left sample,Right sample),frame2(), ...... |
| 21 | + uninterleaved mode: record period data channel by channel, such as period(Left sample,Left ,left...),period(right,right...),period()... |
| 22 | +3. lib: lasound |
| 23 | +--------------------------------------------------*/ |
| 24 | + |
| 25 | +#include <asoundlib.h> |
| 26 | +#include <stdbool.h> |
| 27 | +#define CHECK_AVERG 2500 //--threshold value of wave amplitude |
| 28 | + |
| 29 | +snd_pcm_t *pcm_handle; |
| 30 | +snd_pcm_hw_params_t *params; |
| 31 | +snd_pcm_format_t format_val; |
| 32 | +char *wave_buf; //---pointer to wave buffer |
| 33 | +int wave_buf_len; //---wave buffer length in bytes |
| 34 | +int bit_per_sample; |
| 35 | +snd_pcm_uframes_t frames; |
| 36 | +snd_pcm_uframes_t period_size; //length of period (in frames) |
| 37 | +snd_pcm_uframes_t chunk_size=32;//numbers of frames read/write to hard ware each time |
| 38 | +int chunk_byte; //length of chunk (period) (in bytes) |
| 39 | +unsigned int chanl_val,rate_val; |
| 40 | +int dir; |
| 41 | + |
| 42 | + |
| 43 | +//------------------- functions declaration ---------------------- |
| 44 | +bool device_open( int mode); |
| 45 | +bool device_setparams(); |
| 46 | +bool device_capture( int dtime ); |
| 47 | +bool device_play(); |
| 48 | +bool device_check_voice(); |
| 49 | + |
| 50 | +/*========================= MAIN ====================================*/ |
| 51 | +int main(int argc,char* argv[]){ |
| 52 | +int fd; |
| 53 | +int rc; |
| 54 | + |
| 55 | +while(1) |
| 56 | +{ |
| 57 | + |
| 58 | +//--------录音 beware of if...if...if...if...expressions |
| 59 | +system("amixer set Capture 53"); |
| 60 | +system("amixer set 'ADC PCM' 240"); |
| 61 | + |
| 62 | +if (!device_open(SND_PCM_STREAM_CAPTURE ))return 1; |
| 63 | + //printf("---device_open()\n"); |
| 64 | +if (!device_setparams(1,8000)) return 2; |
| 65 | + //printf("---device_setparams()\n"); |
| 66 | +if(!device_check_voice()) //--checking voice wave amplitude, and start to record if it exceeds preset threshold value,or it will loop checking ... |
| 67 | + continue; |
| 68 | + |
| 69 | +printf("start recording...\n"); |
| 70 | +if (!device_capture( 10 ))return 3; |
| 71 | + //printf("-----device_capture()\n"); |
| 72 | +snd_pcm_close( pcm_handle ); |
| 73 | + printf("record finish!\n"); |
| 74 | + |
| 75 | +fd=open("/tmp/record.raw",O_WRONLY|O_CREAT|O_TRUNC); |
| 76 | +rc=write(fd,wave_buf,wave_buf_len); |
| 77 | +printf("write to record.raw %d bytes\n",rc); |
| 78 | +close(fd); //though kernel will close it automatically |
| 79 | + |
| 80 | +//--------播放 |
| 81 | +printf("start playback...\n"); |
| 82 | +if (!device_open(SND_PCM_STREAM_PLAYBACK)) return 4; |
| 83 | +//printf("-----PLAY: device_open() finish\n"); |
| 84 | +if (!device_setparams(1,8000)) return 5; |
| 85 | +//printf("-----PLAY: device_setarams() finish\n"); |
| 86 | +if (!device_play()) return 6; |
| 87 | +printf("finish playback.\n\n\n"); |
| 88 | + |
| 89 | +snd_pcm_close( pcm_handle ); |
| 90 | +//printf("-----PLAY: snd_pcm_close() ----\n"); |
| 91 | + |
| 92 | +free(wave_buf); //--wave_buf mem. to be allocated in device_capture() and played in device_play(); |
| 93 | + |
| 94 | +}//while() |
| 95 | + |
| 96 | +return 0; |
| 97 | + |
| 98 | +} |
| 99 | + |
| 100 | + |
| 101 | +//首先让我们封装一个打开音频设备的函数: |
| 102 | +//snd_pcm_t *pcm_handle; |
| 103 | + |
| 104 | +bool device_open(int mode){ |
| 105 | +if(snd_pcm_open (&pcm_handle,"default",mode,0) < 0) |
| 106 | + { |
| 107 | + printf("snd_pcm_open() fail!\n"); |
| 108 | + return false; |
| 109 | + } |
| 110 | + printf("snd_pcm_open() succeed!\n"); |
| 111 | + return true; |
| 112 | +} |
| 113 | + |
| 114 | + |
| 115 | +bool device_setparams(int nchanl,int rate) |
| 116 | + { |
| 117 | +unsigned int val; |
| 118 | +int dir; |
| 119 | +int rc; |
| 120 | +snd_pcm_hw_params_t *hw_params; |
| 121 | + |
| 122 | +//------ beware of following if..if...if..if...if...expressions ---------- |
| 123 | +if(snd_pcm_hw_params_malloc (&hw_params) < 0)return false; //为参数变量分配空间 |
| 124 | +// printf("---- snd_pcm_hw_params_malloc(&hw_params) ----\n"); |
| 125 | +if(snd_pcm_hw_params_malloc (¶ms) < 0)return false; |
| 126 | +// printf("----snd_pcm_hw_params_malloc(¶ms) ----\n"); |
| 127 | +if(snd_pcm_hw_params_any (pcm_handle, hw_params) < 0)return false; //参数初始化 |
| 128 | +// printf("----snd_pcm_hw_params_any(pcm_handle,hw_params)----\n"); |
| 129 | +if(snd_pcm_hw_params_set_access (pcm_handle, hw_params,SND_PCM_ACCESS_RW_INTERLEAVED) < 0)return false; //设置为交错模式 |
| 130 | +// printf("----snd_pcm_hw_params_set_access()----\n"); |
| 131 | +if(snd_pcm_hw_params_set_format( pcm_handle, hw_params, SND_PCM_FORMAT_S16_LE) < 0)return false; //使用用16位样本 |
| 132 | +// printf("----snd_pmc_hw_params_set_format()-----\n"); |
| 133 | +val=rate;//8000; |
| 134 | +if(snd_pcm_hw_params_set_rate_near( pcm_handle, hw_params,&val,0) < 0)return false; //设置采样率 |
| 135 | +// printf("----snd_pcm_hw_params_set_rate_near() val=%d----\n",val); |
| 136 | +if(snd_pcm_hw_params_set_channels( pcm_handle, hw_params, nchanl) < 0)return false; //设置为立体声or Mono. |
| 137 | +// printf("----snd_pcm_hw_params_set_channels()-----\n"); |
| 138 | +frames=32; |
| 139 | +if(snd_pcm_hw_params_set_period_size_near(pcm_handle,hw_params,&chunk_size,&dir) < 0 )return false; |
| 140 | +// printf("----snd_pcm_hw_params_set_period_size_near() chunk_size=%d----\n",chunk_size); |
| 141 | +if(snd_pcm_hw_params_get_period_size( hw_params, &period_size,0) < 0)return false; //获取周期长度 |
| 142 | +// printf("----snd_pcm_hw_get_period_size(): %d----\n",(int)period_size); |
| 143 | +if(snd_pcm_hw_params_get_format(hw_params,&format_val) < 0)return false; |
| 144 | +// printf("----snd_pcm_hw_params_get_format()----\n"); |
| 145 | +bit_per_sample = snd_pcm_format_width((snd_pcm_format_t)format_val); |
| 146 | +//printf("---bit_per_sample=%d snd_pcm_format_width()----\n",bit_per_sample); |
| 147 | +//获取样本长度 |
| 148 | +snd_pcm_hw_params_get_channels(hw_params,&chanl_val); |
| 149 | +//printf("----snd_pcm_hw_params_get_channels %d---\n",chanl_val); |
| 150 | +chunk_byte = period_size*bit_per_sample*chanl_val/8; |
| 151 | +//chunk_size = frames;//period_size; //frames |
| 152 | +//计算周期长度(字节数(bytes) = 每周期的桢数 * 样本长度(bit) * 通道数 / 8 ) |
| 153 | + |
| 154 | +rc=snd_pcm_hw_params( pcm_handle, hw_params); //设置参数 |
| 155 | +if(rc<0){ |
| 156 | +printf("unable to set hw parameters:%s\n",snd_strerror(rc)); |
| 157 | +exit(1); |
| 158 | +} |
| 159 | +printf("finish setting sound hw parameters\n"); |
| 160 | +params = hw_params; //保存参数,方便以后使用 |
| 161 | +snd_pcm_hw_params_free( hw_params); //释放参数变量空间 |
| 162 | +//printf("----snd_pcm_hw_params_free()----\n"); |
| 163 | +return true; |
| 164 | + |
| 165 | +} |
| 166 | +//这里先使用了Alsa提供的一系列snd_pcm_hw_params_set_函数为参数变量赋值。 |
| 167 | +//最后才通过snd_pcm_hw_params将参数传递给设备。 |
| 168 | +//需要说明的是正式的开发中需要处理参数设置失败的情况,这里仅做为示例程序而未作考虑。 |
| 169 | +//设置好参数后便可以开始录音了。录音过程实际上就是从音频设备中读取数据信息并保存。 |
| 170 | + |
| 171 | + |
| 172 | + |
| 173 | + bool device_capture( int dtime ){ |
| 174 | + snd_pcm_hw_params_get_rate(params,&rate_val,&dir); //params=hw_params |
| 175 | + wave_buf_len=dtime*rate_val*bit_per_sample*chanl_val/8; |
| 176 | + //printf(" wave_buf_len=%d \n",wave_buf_len); |
| 177 | + //计算音频数据长度(秒数 * 采样率 * 桢长度) |
| 178 | + char *data=wave_buf=(char*)malloc(wave_buf_len); |
| 179 | + int r = 0; |
| 180 | + chunk_size=32; |
| 181 | + chunk_byte=chunk_size*bit_per_sample*chanl_val/8; |
| 182 | + //printf("chunk_byte=%d\n",chunk_byte); |
| 183 | + while ( (data-wave_buf) <= (wave_buf_len-chunk_byte) ){ //chunk_size*bit_per_sample*chanl_val)){ |
| 184 | + r = snd_pcm_readi( pcm_handle,data,chunk_size); //chunk_size*bit_per_sample*read interleaved rames from a PCM |
| 185 | +//--- snd_pcm_sframes_t snd_pcm_readi(snd_pcm_t *pcm,void* buffer, snd_pcm_uframes_t size) |
| 186 | +//--- pcm -PCM handle buffer-frames containing buffer size - frames to be read |
| 187 | + if ( r>0 ) { |
| 188 | +// printf(" r= %d\n",r); |
| 189 | +// printf("data=%d wave_buf=%d\n",data,wave_buf); |
| 190 | + data += chunk_byte;//--move current buffer position pointer |
| 191 | + } |
| 192 | + else |
| 193 | + return false; |
| 194 | +} |
| 195 | + |
| 196 | +return true; |
| 197 | + |
| 198 | +} |
| 199 | + |
| 200 | +//形参dtime用来确定录音时间,根据录音时间分配数据空间,再调用snd_pcm_readi从音频设备读取音频数据,存放到wave_buf中。 |
| 201 | +//同样的原理,我们再添加一个播放函数,向音频设备写入数据: |
| 202 | + |
| 203 | +bool device_play(){ |
| 204 | + char *data = wave_buf; |
| 205 | + int r = 0; |
| 206 | + chunk_size=32; |
| 207 | + chunk_byte=chunk_size*bit_per_sample*chanl_val/8; |
| 208 | + while ( (data-wave_buf) <= (wave_buf_len-chunk_byte)){ |
| 209 | + r = snd_pcm_writei( pcm_handle, data , chunk_size); //chunk_size = frames |
| 210 | + if(r == -EAGAIN)continue; |
| 211 | + if(r < 0){ |
| 212 | + printf("wirte error: %s\n",snd_strerror(r)); |
| 213 | + exit(EXIT_FAILURE); |
| 214 | + } |
| 215 | + //printf("----- writei() r=%d -----\n ",r); |
| 216 | + if ( r>0 ) data += chunk_byte; |
| 217 | + else |
| 218 | + return false; |
| 219 | + } |
| 220 | + return true; |
| 221 | + |
| 222 | +} |
| 223 | + |
| 224 | + |
| 225 | +bool device_check_voice(void ) |
| 226 | +{ |
| 227 | + int i; |
| 228 | + int r = 0; |
| 229 | + int count=0; |
| 230 | + int total=0; |
| 231 | + int averg=0;//average of sample values in one chunk. |
| 232 | + chunk_size=32; //--frames each time |
| 233 | + chunk_byte=chunk_size*bit_per_sample*chanl_val/8; //---bytes |
| 234 | + //printf("chunk_byte=%d\n",chunk_byte); |
| 235 | + int16_t *buf=(int16_t *)malloc(chunk_byte); //32bits |
| 236 | + //printf("size of int16_t = %d\n",sizeof(int16_t)); |
| 237 | + int16_t *data=buf; |
| 238 | + //char* tmp; |
| 239 | + |
| 240 | + printf("listening and checking any voice......\n"); |
| 241 | + while(1) |
| 242 | + { |
| 243 | + r = snd_pcm_readi( pcm_handle,(char *)buf,chunk_size); //chunk_size*bit_per_sample*read interleaved rames from a PCM |
| 244 | + if(r == -EAGAIN)continue; |
| 245 | + if ( r>=0 ) { |
| 246 | + //printf(" r= %d \n ",r); |
| 247 | + data=buf; |
| 248 | + averg=0;total=0; |
| 249 | + for(i=0;i<r;i++){ |
| 250 | + total+=abs((*data)); // !!!!!! |
| 251 | + data+=1; |
| 252 | + } |
| 253 | + //printf("total=%d\n",total); |
| 254 | + averg=(total>>5); |
| 255 | + //printf("averg=%d\n",averg); |
| 256 | + if(averg >= CHECK_AVERG){ |
| 257 | + printf("loud noise sensed!\n"); |
| 258 | + free(buf); |
| 259 | + return true; |
| 260 | + } |
| 261 | + |
| 262 | +// usleep(20000); // |
| 263 | +// snd_pcm_prepare(pcm_handle); |
| 264 | + //usleep(10000); //---you cann't sleep here, |
| 265 | + }//if |
| 266 | + else |
| 267 | + { |
| 268 | + printf(" r= %d \n ",r); |
| 269 | + free(buf); |
| 270 | + return false; |
| 271 | + } |
| 272 | + } //while(1) |
| 273 | + |
| 274 | +free(buf); |
| 275 | +return true; |
| 276 | + |
| 277 | +} |
0 commit comments