#include #include #include #include #include #include //#include //#include //#include #include #include #include #define SAMPLE_RATE 48000 #define CHANNEL 2 #define REC_DEVICE_NAME "fake_record" #define WRITE_DEVICE_NAME "fake_play" //#define WRITE_DEVICE_NAME "default" #define JACK_DEVICE_NAME "fake_jack" #define READ_FRAME 1024 //(768) #define PERIOD_SIZE (1024) //(SAMPLE_RATE/8) #define PERIOD_counts (2) //double of delay 200ms #define BUFFER_SIZE (PERIOD_SIZE * PERIOD_counts) // #define MUTE_TIME_THRESHOD (4)//seconds // #define MUTE_FRAME_THRESHOD (SAMPLE_RATE * MUTE_TIME_THRESHOD / READ_FRAME)//30 seconds //#define ALSA_READ_FORMAT SND_PCM_FORMAT_S32_LE #define ALSA_READ_FORMAT SND_PCM_FORMAT_S16_LE #define ALSA_WRITE_FORMAT SND_PCM_FORMAT_S16_LE #define paging_channel "volume-event-channel" // #define volume_channel "volume-value-channel" // #define volume_temp_channel "volume-temp-channel" #define VOL_CONF "/oem/etc/volctrl.conf" #define unix_socket_path "/tmp/redis.sock" /* * Select different alsa pathways based on device type. * LINE_OUT: LR-Mix(fake_play)->EqDrcProcess(ladspa)->Speaker(real_playback) * HEAD_SET: fake_jack -> Headset(real_playback) * BLUETOOTH: device as bluetooth source. */ // #define DEVICE_FLAG_LINE_OUT 0x01 // #define DEVICE_FLAG_HEAD_SET 0x02 // #define DEVICE_FLAG_BLUETOOTH 0x04 #define AULEVEL_MIN -96.0 #define AULEVEL_MAX 0.0 //current session #define IDLE 0 #define SIP 1 #define BROADCAST 2 #define MULTICAST 3 #define ONVIF 4 typedef struct _vumeter_play { int16_t *ch1_sampv; int16_t *ch2_sampv; size_t au_sampc; double ch1_avg_play; double ch2_avg_play; } vumeter_play; typedef struct { redisContext *context; const char *socket_path; time_t last_activity; } redis_client_t; static bool set_volume = false; static int volume = 100; static int en_threshold = false; static int vol_threshold; static int current_sess = IDLE; static int ctrl_sess = IDLE; static int sip_active = false; static int mc_active = false; static int onvif_active = false; static int bc_active = false; static bool DEBUG = false; //struct timeval tv_begin, tv_end; //gettimeofday(&tv_begin, NULL); extern int set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t buffer_size, snd_pcm_uframes_t period_size, char **msg); redis_client_t* create_redis_client(const char *socket_path) { redis_client_t *client = (redis_client_t *) malloc(sizeof(redis_client_t)); client->socket_path = socket_path; client->context = redisConnectUnix(socket_path); client->last_activity = time(NULL); return client; } int ensure_connected(redis_client_t *client) { if (client->context == NULL || client->context->err) { if (client->context) { redisFree(client->context); } client->context = redisConnectUnix(client->socket_path); if (client->context == NULL || client->context->err) { return -1; // 连接失败 } } client->last_activity = time(NULL); return 0; } void redis_command_safe(redis_client_t *client, const char *command) { if (ensure_connected(client) == 0) { redisReply *reply = (redisReply *) redisCommand(client->context, command); if (reply) { // 处理回复 freeReplyObject(reply); } } } void alsa_fake_device_record_open(snd_pcm_t** capture_handle,int channels,uint32_t rate) { snd_pcm_hw_params_t *hw_params; snd_pcm_uframes_t periodSize = PERIOD_SIZE; snd_pcm_uframes_t bufferSize = BUFFER_SIZE; int dir = 0; int err; err = snd_pcm_open(capture_handle, REC_DEVICE_NAME, SND_PCM_STREAM_CAPTURE, 0); if (err) { printf( "Unable to open capture PCM device: \n"); exit(1); } printf("snd_pcm_open\n"); //err = snd_pcm_hw_params_alloca(&hw_params); err = snd_pcm_hw_params_malloc(&hw_params); if(err) { fprintf(stderr, "cannot allocate hardware parameter structure (%s)\n",snd_strerror(err)); exit(1); } printf("snd_pcm_hw_params_malloc\n"); err = snd_pcm_hw_params_any(*capture_handle, hw_params); if(err) { fprintf(stderr, "cannot initialize hardware parameter structure (%s)\n",snd_strerror(err)); exit(1); } printf("snd_pcm_hw_params_any!\n"); err = snd_pcm_hw_params_set_access(*capture_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED); // err = snd_pcm_hw_params_set_access(*capture_handle, hw_params, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); if (err) { printf("Error setting interleaved mode\n"); exit(1); } printf("snd_pcm_hw_params_set_access!\n"); err = snd_pcm_hw_params_set_format(*capture_handle, hw_params, ALSA_READ_FORMAT); if (err) { printf("Error setting format: %s\n", snd_strerror(err)); exit(1); } printf("snd_pcm_hw_params_set_format\n"); err = snd_pcm_hw_params_set_channels(*capture_handle, hw_params, channels); if (err) { printf("channels = %d\n",channels); printf( "Error setting channels: %s\n", snd_strerror(err)); exit(1); } printf("channels = %d\n",channels); err = snd_pcm_hw_params_set_buffer_size_near(*capture_handle, hw_params, &bufferSize); if (err) { printf("Error setting buffer size (%ld): %s\n", bufferSize, snd_strerror(err)); exit(1); } printf("bufferSize = %ld\n",bufferSize); err = snd_pcm_hw_params_set_period_size_near(*capture_handle, hw_params, &periodSize, 0); if (err) { printf("Error setting period time (%ld): %s\n", periodSize, snd_strerror(err)); exit(1); } printf("periodSize = %ld\n",periodSize); err = snd_pcm_hw_params_set_rate_near(*capture_handle, hw_params, &rate, 0/*&dir*/); if (err) { printf("Error setting sampling rate (%d): %s\n", rate, snd_strerror(err)); //goto error; exit(1); } printf("Rate = %d\n", rate); /* Write the parameters to the driver */ err = snd_pcm_hw_params(*capture_handle, hw_params); if (err < 0) { printf( "Unable to set HW parameters: %s\n", snd_strerror(err)); //goto error; exit(1); } err = snd_pcm_prepare(*capture_handle); printf("Open record device done \n"); //set_sw_params(*capture_handle,bufferSize,periodSize,NULL); if(hw_params) snd_pcm_hw_params_free(hw_params); } void alsa_fake_device_write_open(snd_pcm_t** write_handle, int channels, uint32_t write_sampleRate) { snd_pcm_hw_params_t *write_params; snd_pcm_uframes_t write_periodSize = PERIOD_SIZE; snd_pcm_uframes_t write_bufferSize = BUFFER_SIZE; int write_err; int write_dir; int i = 10; retry: write_err = snd_pcm_open(write_handle, WRITE_DEVICE_NAME, SND_PCM_STREAM_PLAYBACK, 0); // } if (write_err) { usleep(100*1000); i --; if(i > 0) goto retry; printf( "Unable to open playback PCM device: \n"); exit(1); } //printf( "interleaved mode\n"); // snd_pcm_hw_params_alloca(&write_params); snd_pcm_hw_params_malloc(&write_params); //printf("snd_pcm_hw_params_alloca\n"); snd_pcm_hw_params_any(*write_handle, write_params); write_err = snd_pcm_hw_params_set_access(*write_handle, write_params, SND_PCM_ACCESS_RW_INTERLEAVED); //write_err = snd_pcm_hw_params_set_access(*write_handle, write_params, SND_PCM_ACCESS_MMAP_NONINTERLEAVED); if (write_err) { printf("Error setting interleaved mode\n"); exit(1); } //printf( "interleaved mode\n"); write_err = snd_pcm_hw_params_set_format(*write_handle, write_params, ALSA_WRITE_FORMAT); if (write_err) { printf("Error setting format: %s\n", snd_strerror(write_err)); exit(1); } //printf( "format successed\n"); write_err = snd_pcm_hw_params_set_channels(*write_handle, write_params, channels); if (write_err) { printf( "Error setting channels: %s\n", snd_strerror(write_err)); exit(1); } //printf("channels = %d\n",channels); write_err = snd_pcm_hw_params_set_rate_near(*write_handle, write_params, &write_sampleRate, 0/*&write_dir*/); if (write_err) { printf("Error setting sampling rate (%d): %s\n", write_sampleRate, snd_strerror(write_err)); exit(1); } //printf("setting sampling rate (%d)\n", write_sampleRate); write_err = snd_pcm_hw_params_set_buffer_size_near(*write_handle, write_params, &write_bufferSize); if (write_err) { printf("Error setting buffer size (%ld): %s\n", write_bufferSize, snd_strerror(write_err)); exit(1); } //printf("write_bufferSize = %ld\n",write_bufferSize); write_err = snd_pcm_hw_params_set_period_size_near(*write_handle, write_params, &write_periodSize, 0); if (write_err) { printf("Error setting period time (%ld): %s\n", write_periodSize, snd_strerror(write_err)); exit(1); } //printf("write_periodSize = %ld\n",write_periodSize); #if 0 snd_pcm_uframes_t write_final_buffer; write_err = snd_pcm_hw_params_get_buffer_size(write_params, &write_final_buffer); printf(" final buffer size %ld \n" , write_final_buffer); snd_pcm_uframes_t write_final_period; write_err = snd_pcm_hw_params_get_period_size(write_params, &write_final_period, &write_dir); printf(" final period size %ld \n" , write_final_period); #endif /* Write the parameters to the driver */ write_err = snd_pcm_hw_params(*write_handle, write_params); if (write_err < 0) { printf( "Unable to set HW parameters: %s\n", snd_strerror(write_err)); exit(1); } write_err = snd_pcm_prepare(*write_handle); if(DEBUG) printf("open write device is successful\n"); set_sw_params(*write_handle, write_bufferSize, write_periodSize, NULL); if(write_params) snd_pcm_hw_params_free(write_params); } int set_sw_params(snd_pcm_t *pcm, snd_pcm_uframes_t buffer_size, snd_pcm_uframes_t period_size, char **msg) { snd_pcm_sw_params_t *params; char buf[256]; int err; //snd_pcm_sw_params_alloca(¶ms); snd_pcm_sw_params_malloc(¶ms); if ((err = snd_pcm_sw_params_current(pcm, params)) != 0) { snprintf(buf, sizeof(buf), "Get current params: %s", snd_strerror(err)); //goto fail; exit(1); } /* start the transfer when the buffer is full (or almost full) */ snd_pcm_uframes_t threshold = (buffer_size / period_size) * period_size; if ((err = snd_pcm_sw_params_set_start_threshold(pcm, params, threshold)) != 0) { snprintf(buf, sizeof(buf), "Set start threshold: %s: %lu", snd_strerror(err), threshold); exit(1); } /* allow the transfer when at least period_size samples can be processed */ if ((err = snd_pcm_sw_params_set_avail_min(pcm, params, period_size)) != 0) { snprintf(buf, sizeof(buf), "Set avail min: %s: %lu", snd_strerror(err), period_size); exit(1); } if ((err = snd_pcm_sw_params(pcm, params)) != 0) { snprintf(buf, sizeof(buf), "%s", snd_strerror(err)); exit(1); } if(params) snd_pcm_sw_params_free(params); return 0; } //修改当前音量 void *control_volume(void *) { char cmd[64]; set_volume = true; volume -= 10; usleep(1000*1000); set_volume = false; return NULL; } void get_volume_threshold() { dictionary * ini ; ini = iniparser_load(VOL_CONF); if (ini==NULL) { fprintf(stderr, "cannot parse file: %s\n", VOL_CONF); return; } en_threshold = iniparser_getboolean(ini, "volume:enable_threshold", false); vol_threshold = iniparser_getint(ini, "volume:volume_threshold", -15); iniparser_freedict(ini); } /** * Generic routine to calculate RMS (Root-Mean-Square) from * a set of signed 16-bit values * * \verbatim .--------------- | N-1 | ----. | \ | \ 2 | | s[n] | / | / _ | ----' \ | n=0 \ | ------------ \| N \endverbatim * * @param data Array of signed 16-bit values * @param len Number of values * * @return RMS value from 0 to 32768 */ static double calc_rms_s16(const int16_t *data, size_t len) { int64_t sum = 0; size_t i; if (!data || !len) return .0; for (i = 0; i < len; i++) { sum += data[i] * data[i]; } return sqrt(sum / (double)len); } double aout_Aulevel_dbov(const int16_t *sampv, size_t sampc) { static const double peak_s16 = 32767.0; double rms, dbov; if (!sampv || !sampc) return 0; rms = calc_rms_s16(sampv, sampc) / peak_s16; dbov = 20 * log10(rms); if (dbov < AULEVEL_MIN) dbov = AULEVEL_MIN; else if (dbov > AULEVEL_MAX) dbov = AULEVEL_MAX; return dbov; } static void *vumeter_decode(void *arg) { char redisCmd[128]; int set_idle = false; vumeter_play *vu_p = (vumeter_play *) arg; redis_client_t *redisClient = create_redis_client(unix_socket_path); usleep(100*1000); while(1) { usleep(500*1000); if(sip_active || mc_active || onvif_active || bc_active) { vu_p->ch1_avg_play = aout_Aulevel_dbov(vu_p->ch1_sampv, vu_p->au_sampc); sprintf(redisCmd, "HSet volume audio_out_ch1 %d", (int) vu_p->ch1_avg_play); redis_command_safe(redisClient, redisCmd); vu_p->ch2_avg_play = aout_Aulevel_dbov(vu_p->ch2_sampv, vu_p->au_sampc); sprintf(redisCmd, "HSet volume audio_out_ch2 %d", (int) vu_p->ch2_avg_play); redis_command_safe(redisClient, redisCmd); set_idle = false; if(DEBUG) printf("vol1: %f, vol2: %f\n", vu_p->ch1_avg_play, vu_p->ch2_avg_play); } else if(!set_idle) { sprintf(redisCmd, "HSet volume audio_out_ch1 %d", (int) AULEVEL_MIN); redis_command_safe(redisClient, redisCmd); sprintf(redisCmd, "HSet volume audio_out_ch2 %d", (int) AULEVEL_MIN); redis_command_safe(redisClient, redisCmd); set_idle = true; } } return NULL; } //播放状态监控 void ProcessReply( char * pReplyStr ) { cJSON *pJson = NULL; if(DEBUG) printf( "Msg [%s]\n", pReplyStr ); pJson = cJSON_Parse(pReplyStr); if ( pJson ) { if(strcmp(cJSON_GetObjectItem(pJson, "name")->valuestring, "SIP") == 0) { if(strcmp(cJSON_GetObjectItem(pJson, "action")->valuestring, "on") == 0) { get_volume_threshold(); current_sess = SIP; sip_active = true; } else if(strcmp(cJSON_GetObjectItem(pJson, "action")->valuestring, "off") == 0) { if(ctrl_sess == SIP) { volume = 100; ctrl_sess = IDLE; } sip_active = false; } } else if(strcmp(cJSON_GetObjectItem(pJson, "name")->valuestring, "BROADCAST") == 0) { if(strcmp(cJSON_GetObjectItem(pJson, "action")->valuestring, "on") == 0) { get_volume_threshold(); current_sess = BROADCAST; bc_active = true; } else if(strcmp(cJSON_GetObjectItem(pJson, "action")->valuestring, "off") == 0) { if(ctrl_sess == BROADCAST) { volume = 100; ctrl_sess = IDLE; } bc_active = false; } } else if(strcmp(cJSON_GetObjectItem(pJson, "name")->valuestring, "MULTICAST") == 0) { if(strcmp(cJSON_GetObjectItem(pJson, "action")->valuestring, "on") == 0) { get_volume_threshold(); current_sess = MULTICAST; mc_active = true; } else if(strcmp(cJSON_GetObjectItem(pJson, "action")->valuestring, "off") == 0) { if(ctrl_sess == MULTICAST) { volume = 100; ctrl_sess = IDLE; } mc_active = false; } } else if(strcmp(cJSON_GetObjectItem(pJson, "name")->valuestring, "ONVIF") == 0) { if(strcmp(cJSON_GetObjectItem(pJson, "action")->valuestring, "on") == 0) { get_volume_threshold(); current_sess = ONVIF; onvif_active = true; } else if(strcmp(cJSON_GetObjectItem(pJson, "action")->valuestring, "off") == 0) { if(ctrl_sess == ONVIF) { volume = 100; ctrl_sess = IDLE; } onvif_active = false; } } } if(pJson != NULL) cJSON_Delete(pJson); } void *service_status_listen(void *arg) { // redisContext * pContext = redisConnect( "127.0.0.1", 6379 ); redisContext * pContext = redisConnectUnix(unix_socket_path); if ( NULL == pContext || pContext->err == 1 ) { printf( "%s\n", pContext->errstr ); exit( -1 ); } void* replyPtr = redisCommand( pContext, "SUBSCRIBE %s", paging_channel ); redisReply* pReply = static_cast(replyPtr); freeReplyObject( pReply ); while ( redisGetReply( pContext, (void **)&pReply ) == REDIS_OK ) { if ( pReply->elements == 3 ) { redisReply * pSubReply = pReply->element[2]; ProcessReply( pSubReply->str ); } if(pReply != NULL) freeReplyObject( pReply ); } redisFree( pContext ); return NULL; } void check_volume(int16_t *buffer, int count, vumeter_play *vu) { int i; pthread_t thread; if(vu->ch1_avg_play > vol_threshold || vu->ch2_avg_play > vol_threshold) { for(i=0; i 1.f) v = 1.f; return v; } void vol_uint16(int16_t *data, size_t n) { size_t i; for (i = 0; i < n; i++) { data[i*2] = (int16_t) (data[i*2] * get_volume(volume)); data[i*2+1] = (int16_t) (data[i*2+1] * get_volume(volume)); } } int main(int argc, char *argv[]) { snd_pcm_t *capture_handle; snd_pcm_t *write_handle; vumeter_play *vu = ( vumeter_play *) malloc (sizeof (*vu)); int err, i; int16_t buffer[READ_FRAME * 2]; unsigned int sampleRate; unsigned int channels; pthread_t service_status_listen_thread, thread; // pthread_t volume_temp_value_listen_thread; if(argc == 2 && strcmp(argv[1], "debug") == 0) DEBUG = true; repeat: //capture_handle = NULL; write_handle = NULL; err = 0; memset(buffer, 0, sizeof(buffer)); sampleRate = SAMPLE_RATE; channels = CHANNEL; printf("\n==========EQ/DRC process release version 1.23===============\n"); alsa_fake_device_record_open(&capture_handle, channels, sampleRate); alsa_fake_device_write_open(&write_handle, channels, sampleRate); vu->ch1_sampv = (int16_t *) malloc(READ_FRAME*2*sizeof(int16_t)); vu->ch2_sampv = (int16_t *) malloc(READ_FRAME*2*sizeof(int16_t)); vu->au_sampc = READ_FRAME; //RK_acquire_wake_lock(wake_lock); pthread_create(&thread, NULL, vumeter_decode, vu); pthread_detach(thread); pthread_create(&service_status_listen_thread, NULL, service_status_listen, NULL); pthread_detach(service_status_listen_thread); while (1) { err = snd_pcm_readi(capture_handle, buffer , READ_FRAME); if (err != READ_FRAME) printf("====read frame error = %d===\n",err); if (err < 0) { if (err == -EPIPE) printf( "Overrun occurred: %d\n", err); err = snd_pcm_recover(capture_handle, err, 0); // Still an error, need to exit. if (err < 0) { printf( "Error occured while recording: %s\n", snd_strerror(err)); usleep(200 * 1000); if (capture_handle) snd_pcm_close(capture_handle); if (write_handle) snd_pcm_close(write_handle); goto repeat; } } if(en_threshold) { vol_uint16(buffer, READ_FRAME); check_volume(buffer, READ_FRAME, vu); } for(i=0; ich1_sampv[i] = buffer[i*2]; vu->ch2_sampv[i] = buffer[i*2+1]; } err = snd_pcm_writei(write_handle, buffer, READ_FRAME); if (-EPIPE == err) { snd_pcm_prepare(write_handle); err = snd_pcm_writei(write_handle, buffer, READ_FRAME); if (err < 0) { printf("alsa: write error: %s\n", snd_strerror((int) err)); } } else if (err < 0) { printf("alsa: write error: %s\n", snd_strerror((int) err)); } else if (err != READ_FRAME) { printf("alsa: write: wrote %d of %d samples\n", (int) err, READ_FRAME); } } error: if (capture_handle) snd_pcm_close(capture_handle); if (write_handle) snd_pcm_close(write_handle); return 0; }