/* ******************************************************************************* * Copyright (c) 2010-2015 VATICS Inc. All rights reserved. * * +-----------------------------------------------------------------+ * | THIS SOFTWARE IS FURNISHED UNDER A LICENSE AND MAY ONLY BE USED | * | AND COPIED IN ACCORDANCE WITH THE TERMS AND CONDITIONS OF SUCH | * | A LICENSE AND WITH THE INCLUSION OF THE THIS COPY RIGHT NOTICE. | * | THIS SOFTWARE OR ANY OTHER COPIES OF THIS SOFTWARE MAY NOT BE | * | PROVIDED OR OTHERWISE MADE AVAILABLE TO ANY OTHER PERSON. THE | * | OWNERSHIP AND TITLE OF THIS SOFTWARE IS NOT TRANSFERRED. | * | | * | THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT | * | ANY PRIOR NOTICE AND SHOULD NOT BE CONSTRUED AS A COMMITMENT BY | * | VATICS INC. | * +-----------------------------------------------------------------+ * ******************************************************************************* */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #define SRB_HEADER_SIZE 4 #define ATK_AUDIO_MAX_CHANNELS 32 #define PERIOD_SIZE_IN_FRAMES 1024 #define APP_NAME "UAC Speaker" #define CAP_DIR "./" #define CMD_UAC_FIFO_PATH "/tmp/uac/c0/command.fifo" #define UNUSED(x) (void)(x) enum { FALSE = 0, TRUE, }; enum { RUNNING = 0, INIT, CHECKING, TERMINATED, }; static unsigned int g_dwCurrCaptureRate = 0; static unsigned int g_dwCurrMicMute = 0; static unsigned int g_dwCurrMicVol = 0; static unsigned int g_dwCurrPlaybackRate = 0; static unsigned int g_dwCurrSpkMute = 0; static unsigned int g_dwCurrSpkVol = 0; typedef struct { ATK_AUDIOPLAY_HANDLE_T **playback_handle; ATK_AUDIOCAP_HANDLE_T **capture_handle; srb_handle_t **reader_handle; srb_handle_t **writer_handle; snd_hctl_t **alsa_handle; ATK_AUDIOCAP_CONFIG_T *p_audiocap_config; ATK_AUDIOPLAY_CONFIG_T *p_audiopb_config; pthread_mutex_t pb_data_mutex; pthread_cond_t pb_data_cond; pthread_mutex_t cap_data_mutex; pthread_cond_t cap_data_cond; //pthread_mutex_t play_mutex; pthread_cond_t play_cond; STATUS pb_process_status; STATUS cap_process_status; } user_data_t; static int isRunning = 0; static int is_terminate_ = 0; static int reset = INIT; static int savefile = 0; char const *file_name = NULL; char filename[128]; static int dwCurrSampleRate = 0; FILE* fp; //srb_handle_t* reader_handle = NULL; //srb_handle_t* writer_handle = NULL; srb_buffer_t srb_writer_buf; srb_buffer_t srb_reader_buf; //====================================== //HELP static void print_usage(const char *ap_name) { fprintf(stderr, "Usage:\n" " %s [-h]\n" "Options:\n" " -h This help.\n", ap_name); } //====================================== //write to fp static void write_to_file(unsigned char *data_buf, size_t buf_size) { fwrite(data_buf, buf_size, 1, fp); fflush(fp); } //====================================== //capture thread / SRB writer static void audiocap_callback(const ATK_AUDIO_NOTIFY_DATA_INFO_T *audio_info, void* user_data) { user_data_t *temp_data = (user_data_t*) user_data; srb_handle_t* writer_handle = *(temp_data->writer_handle); pthread_mutex_lock(&(temp_data->cap_data_mutex)); if (temp_data->cap_process_status == STOP) { pthread_cond_signal(&(temp_data->cap_data_cond)); pthread_mutex_unlock(&(temp_data->cap_data_mutex)); return; } pthread_mutex_unlock(&(temp_data->cap_data_mutex)); if(!isRunning) return ; if(audio_info->dwDataBytes > 0) { if (savefile){ //append data to a file write_to_file(audio_info->ppbyAudioBufs[0], audio_info->dwDataBytes); } unsigned int* buf_ptr = (unsigned int*)srb_writer_buf.buffer; buf_ptr[0] = audio_info->dwDataBytes; buf_ptr[1] = audio_info->tDataTimestamp.tv_sec; buf_ptr[2] = audio_info->tDataTimestamp.tv_usec; buf_ptr[3] = audio_info->dwDataBytes; buf_ptr[4] = audio_info->bIsInterleaved; buf_ptr[5] = audio_info->dwChannels; //printf("callback size %d %d %d %d %d %d\n",buf_ptr[0],buf_ptr[1],buf_ptr[2],buf_ptr[3],buf_ptr[4],buf_ptr[5]); memcpy(srb_writer_buf.buffer + SRB_HEADER_SIZE, audio_info->ppbyAudioBufs[0], audio_info->dwDataBytes); SRB_SendGetWriterBuff(writer_handle, &srb_writer_buf); } else { printf("[%s,%d] sending data to client failed, send bytes: %d, terminating ...\n", __func__,__LINE__, audio_info->dwDataBytes); isRunning = false; } } //playback buff void* srb_reader(void* args) { user_data_t *temp_data = (user_data_t*) args; ATK_AUDIOPLAY_HANDLE_T* playback_handle = *(temp_data->playback_handle); srb_handle_t* reader_handle = *(temp_data->reader_handle); if (!playback_handle){ printf(" [%s,%d] playback handle init error \n",__func__,__LINE__); return NULL; } // Audio output buffer. unsigned char *pbyBufs[ATK_AUDIO_MAX_CHANNELS] = {NULL}; unsigned int dwDataBytes; SRB_ReturnReceiveReaderBuff(reader_handle, &srb_reader_buf); while(isRunning && reset != INIT){ if (temp_data->pb_process_status == STOP) { pthread_mutex_lock(&(temp_data->pb_data_mutex)); pthread_cond_signal(&(temp_data->pb_data_cond)); pthread_mutex_unlock(&(temp_data->pb_data_mutex)); pthread_mutex_lock(&(temp_data->pb_data_mutex)); pthread_cond_wait(&(temp_data->play_cond), &(temp_data->pb_data_mutex)); pthread_mutex_unlock(&(temp_data->pb_data_mutex)); } //printf("Play data\n"); if(ATK_AudioPlay_PlayPeriodFrames(playback_handle, pbyBufs) < 0){ fprintf(stderr, "[%s,%s] Can't get the audio output buffer..\n", __FILE__, __func__); isRunning = false; printf("Playback error!!!\n"); break; } //printf("...\n"); if(SRB_ReturnReceiveReaderBuff(reader_handle, &srb_reader_buf)==0 && srb_reader_buf.buffer){ unsigned int* buf_ptr = (unsigned int*)(srb_reader_buf.buffer); dwDataBytes = buf_ptr[0]; //printf("receive: data size %d %d %d %d %d %d\n",buf_ptr[0],buf_ptr[1],buf_ptr[2],buf_ptr[3],buf_ptr[4],buf_ptr[5]); memcpy(pbyBufs[0],srb_reader_buf.buffer + SRB_HEADER_SIZE,dwDataBytes); } } SRB_ReturnReaderBuff(reader_handle, &srb_reader_buf); printf(" [%s,%d] Leaving the UAC_SPK func thread. Please wait. \n",__func__,__LINE__); return NULL; } static void sig_kill(int signo) { printf("[%s,%d] Received SIGNAL %d!!!\n", __func__,__LINE__, signo); switch(signo){ case SIGTERM: case SIGINT: isRunning = false; reset = TERMINATED; is_terminate_ = 1; //SRB_WakeupReader(reader_handle); break; default: break; } } //====================================== unsigned int VolumeMapping(unsigned int value) { if (value == 58272) return 0; else if (value < 59964) return 10; else if (value < 61139) return 20; else if (value < 62040) return 30; else if (value < 62771) return 40; else if (value < 63386) return 50; else if (value < 63917) return 60; else if (value < 64384) return 70; else if (value < 64801) return 80; else if (value < 65178) return 90; else return 100; } static int alsa_GetSampleRate(void) { int err; snd_hctl_t *handle; snd_hctl_elem_t *elem; snd_ctl_elem_id_t *id; snd_ctl_elem_value_t *control; snd_ctl_elem_id_alloca(&id); int sample_rate; unsigned int dwNewVolume = 0, dwIsMute = 0; if ((err = snd_hctl_open(&handle, "hw:1", 0)) < 0) { printf("Control %s open error: %s", "hw:1", snd_strerror(err)); return 1; } if ((err = snd_hctl_load(handle)) < 0) { printf("Control %s local error: %s\n", "hw:1", snd_strerror(err)); return 1; } for (elem = snd_hctl_first_elem(handle); elem; elem = snd_hctl_elem_next(elem)) { unsigned int value = 0; snd_hctl_elem_get_id(elem, id); snd_ctl_elem_value_alloca(&control); if ((err = snd_hctl_elem_read(elem, control)) < 0) { printf("Control %s element read error: %s\n", "hw:1", snd_strerror(err)); return err; } //printf("[UAC_SPK] List item %s value :%ld\n",snd_ctl_elem_id_get_name(id),snd_ctl_elem_value_get_integer(control, 0)); value = (unsigned int)snd_ctl_elem_value_get_integer(control, 0); if(strcmp("Capture Rate",snd_ctl_elem_id_get_name(id)) == 0) { //printf("Found [Capture Rate]\n"); g_dwCurrCaptureRate = value; } else if(strcmp("Mic Mute",snd_ctl_elem_id_get_name(id)) == 0) { //printf("Found [Mic Mute]\n"); g_dwCurrMicMute = value; } else if(strcmp("Mic Volume",snd_ctl_elem_id_get_name(id)) == 0) { //printf("Found [Mic Volume]\n"); g_dwCurrMicVol = value; } else if(strcmp("Playback Rate",snd_ctl_elem_id_get_name(id)) == 0) { //printf("Found [Playback Rate]\n"); g_dwCurrPlaybackRate = value; } else if(strcmp("Speaker Mute",snd_ctl_elem_id_get_name(id)) == 0) { //printf("Found [Speaker Mute]\n"); dwIsMute = value; //g_dwCurrSpkMute = value; }else if(strcmp("Speaker Volume",snd_ctl_elem_id_get_name(id)) == 0) { //printf("Found [Speaker Volume]\n"); dwNewVolume = VolumeMapping(value);//g_dwCurrSpkVol = value; } } snd_hctl_close(handle); //printf("%s C_rate_%d M_mute_%d M_Vol_%d P_rate_%d S_mute_%d S_Vol_%d\n",__func__, g_dwCurrCaptureRate,g_dwCurrMicMute,g_dwCurrMicVol, //g_dwCurrPlaybackRate,g_dwCurrSpkMute,g_dwCurrSpkVol); if (g_dwCurrSpkMute != dwIsMute) { if (dwIsMute == 1) { ATK_Audio_SetPlaybackVolume(0); g_dwCurrSpkMute = dwIsMute; g_dwCurrSpkVol = dwNewVolume; } else{ ATK_Audio_SetPlaybackVolume(dwNewVolume); g_dwCurrSpkMute = dwIsMute; g_dwCurrSpkVol = dwNewVolume; } } else if (g_dwCurrSpkVol != dwNewVolume) { ATK_Audio_SetPlaybackVolume(dwNewVolume); g_dwCurrSpkVol = dwNewVolume; } sample_rate = g_dwCurrCaptureRate; return sample_rate; } static int element_callback(snd_hctl_elem_t *elem __attribute__((unused)), unsigned int mask) { if (mask & SND_CTL_EVENT_MASK_VALUE) { if (isRunning) reset = CHECKING; } return -EAGAIN; } static void events_add(snd_hctl_elem_t *helem) { snd_ctl_elem_id_t *id; snd_ctl_elem_id_alloca(&id); snd_hctl_elem_get_id(helem, id); snd_hctl_elem_set_callback(helem, element_callback); } static int ctl_callback(snd_hctl_t *ctl __attribute__((unused)), unsigned int mask, snd_hctl_elem_t *elem) { if (mask & SND_CTL_EVENT_MASK_ADD) events_add(elem); return 0; } int init_service(user_data_t * temp_data) { ATK_AUDIOPLAY_CONFIG_T *playback_config = temp_data->p_audiopb_config; ATK_AUDIOCAP_CONFIG_T *capture_config = temp_data->p_audiocap_config; int sample_rate = alsa_GetSampleRate(); playback_config->dwSampleRate = sample_rate; capture_config->dwSampleRate = sample_rate; //printf("New Samplerate is %d\n",sample_rate); *(temp_data->writer_handle) = SRB_InitWriter("uac_sp_srb_1", MAX_RING_BUF_SIZE, 4); if (!*(temp_data->writer_handle)){ printf(" [%s,%d] srb writer handle init error \n",__func__,__LINE__); isRunning = false; } memset(&srb_writer_buf, 0, sizeof(srb_buffer_t)); SRB_SendGetWriterBuff(*(temp_data->writer_handle), &srb_writer_buf); *(temp_data->capture_handle) = ATK_AudioCap_Init(capture_config); if(!*(temp_data->capture_handle)){ printf(" [%s,%d] ATK_AudioCap_Init error \n",__func__,__LINE__); isRunning = false; } //printf("init playback\n"); *(temp_data->playback_handle) = ATK_AudioPlay_Init(playback_config); if(!*(temp_data->playback_handle)){ printf(" [%s,%d] ATK_AudioPlay_Init error \n",__func__,__LINE__); isRunning = false; } *(temp_data->reader_handle) = SRB_InitReader("uac_sp_srb_1"); if (!*(temp_data->reader_handle)){ printf(" [%s,%d] srb reader handle init error \n",__func__,__LINE__); isRunning = false; } if (isRunning == TRUE) dwCurrSampleRate = sample_rate; return isRunning; } //playback buff void* main_thread(void* args) { user_data_t *temp_data = (user_data_t*) args; snd_hctl_elem_t *helem; int err, res; pthread_t tid = 0; while (isRunning) { while (reset == RUNNING) { if (*(temp_data->alsa_handle) == NULL && ((err = snd_hctl_open(temp_data->alsa_handle, "hw:1", 0)) < 0)) { printf("Control %s open error: %s\n", "hw:1", snd_strerror(err)); return NULL; } snd_hctl_set_callback(*(temp_data->alsa_handle), ctl_callback); if ((err = snd_hctl_load(*(temp_data->alsa_handle))) < 0) { printf("Control %s hbuild error: %s\n", "hw:1", snd_strerror(err)); return NULL; } for (helem = snd_hctl_first_elem(*(temp_data->alsa_handle)); helem; helem = snd_hctl_elem_next(helem)) { snd_hctl_elem_set_callback(helem, element_callback); } //helem = snd_hctl_first_elem(alsa_handle); //snd_hctl_elem_set_callback(helem, element_callback); while (isRunning && reset == RUNNING) { res = snd_hctl_wait(*(temp_data->alsa_handle), 100); if (res > 0) { snd_hctl_handle_events(*(temp_data->alsa_handle)); break; } else if (res == 0) { // timeout continue; } else { // error // program exit: snd_hctl_close(*(temp_data->alsa_handle)); *(temp_data->alsa_handle) = NULL; break; } } while(isRunning){ if (reset == INIT || reset == CHECKING) { snd_hctl_close(*(temp_data->alsa_handle)); *(temp_data->alsa_handle) = NULL; //printf("try get SampleRate %d\n",dwCurrSampleRate); if (dwCurrSampleRate == alsa_GetSampleRate()){ //printf("same rate\n"); reset = RUNNING; } else reset = INIT; break; } } if (reset == RUNNING) continue; if (tid != 0) { SRB_WakeupReader(*(temp_data->reader_handle)); pthread_join(tid, NULL); //printf("%s::%d joined thread\n", __func__, __LINE__); tid = 0; } //release playabck and capture change state to 0 if (*(temp_data->capture_handle)) { ATK_AudioCap_Release(*(temp_data->capture_handle)); *(temp_data->capture_handle) = NULL; //printf("release cap handle\n"); } if (*(temp_data->playback_handle)) { ATK_AudioPlay_Release(*(temp_data->playback_handle)); *(temp_data->playback_handle) = NULL; //printf("release play handle\n"); } //release srb handles if(*(temp_data->writer_handle)) { SRB_Release(*(temp_data->writer_handle)); *(temp_data->writer_handle) = NULL; //printf("release srb w\n"); } if(*(temp_data->reader_handle)) { SRB_Release(*(temp_data->reader_handle)); *(temp_data->reader_handle) = NULL; //printf("release srb r\n"); } } if(reset == INIT && init_service(temp_data)) { reset = RUNNING; pthread_create(&tid, NULL, srb_reader, temp_data); //printf(" [%s,%d] pthread_create tid = %lu \n",__func__,__LINE__,tid); } } isRunning = false; if (tid != 0) { pthread_join(tid, NULL); tid = 0; } return NULL; } static void msg_callback(MsgContext* msg, void* args) { user_data_t *temp_data = (user_data_t*) args; ATK_AUDIOPLAY_CONFIG_T *playback_config = temp_data->p_audiopb_config; ATK_AUDIOCAP_CONFIG_T *capture_config = temp_data->p_audiocap_config; if( !strcasecmp(msg->pszHost, SR_MODULE_NAME) ){ if( !strcasecmp(msg->pszCmd, SUSPEND_CMD) ) { struct timeval now; struct timespec outtime; printf("receive SUSPEND cmd\n"); //Playback printf("try to hold playback\n"); pthread_mutex_lock(&(temp_data->pb_data_mutex)); SRB_WakeupReader(*(temp_data->reader_handle)); temp_data->pb_process_status = STOP; pthread_cond_wait(&(temp_data->pb_data_cond), &(temp_data->pb_data_mutex)); pthread_mutex_unlock(&(temp_data->pb_data_mutex)); if(*(temp_data->playback_handle)) ATK_AudioPlay_Release(*(temp_data->playback_handle)); //Capture printf("try to hold capture\n"); pthread_mutex_lock(&(temp_data->cap_data_mutex)); gettimeofday(&now, NULL); outtime.tv_sec = now.tv_sec + 2; temp_data->cap_process_status = STOP; //pthread_cond_wait(&(temp_data->cap_data_cond), &(temp_data->cap_data_mutex)); pthread_cond_timedwait(&(temp_data->cap_data_cond), &(temp_data->cap_data_mutex),&outtime); pthread_mutex_unlock(&(temp_data->cap_data_mutex)); ATK_AudioCap_Release(*(temp_data->capture_handle)); //Send susspend response printf("Ready to suspend.\n"); MsgBroker_SuspendAckMsg(); }else if( !strcasecmp(msg->pszCmd, RESUME_CMD) ) { //Playback *(temp_data->playback_handle) = ATK_AudioPlay_Init(playback_config); pthread_mutex_lock(&(temp_data->pb_data_mutex)); temp_data->pb_process_status = START; pthread_cond_signal(&(temp_data->play_cond)); pthread_mutex_unlock(&(temp_data->pb_data_mutex)); //Capture *(temp_data->capture_handle) = ATK_AudioCap_Init(capture_config); pthread_mutex_lock(&(temp_data->cap_data_mutex)); temp_data->cap_process_status = START; pthread_mutex_unlock(&(temp_data->cap_data_mutex)); } } if (msg->bHasResponse) msg->dwDataSize = 0; } int main(int argc, char* argv[]) { //int ret; int opt = -1; //int input = 0; //Mic unsigned int interleaved = 1; // is playing interleaved audio unsigned int sample_rate = 48000; // audio sample rate unsigned int channels = 2 ; //number of channels int simple_conf = 0; //use_simple_config unsigned int periods = 8; //periods_per_buffer ATK_AUDIOPLAY_HANDLE_T *playback_handle; ATK_AUDIOCAP_HANDLE_T *capture_handle; srb_handle_t* reader_handle = NULL; srb_handle_t* writer_handle = NULL; snd_hctl_t *alsa_handle = NULL; ATK_AUDIOCAP_CONFIG_T capture_config; //Capture ATK_AUDIOPLAY_CONFIG_T playback_config;//Playback user_data_t user_data; //int err, res; pthread_t main_tid = 0; while ((opt = getopt(argc, argv, "s:f:t:o:p:d:")) != -1){ switch(opt){ case 's': savefile = atoi(optarg); break; case 'f': file_name = strdup(optarg); break; case 't': interleaved = atoi(optarg); break; case 'o': simple_conf = atoi(optarg); break; case 'p': periods = atoi(optarg); break; default: print_usage(APP_NAME); goto end; break; } } memset(&user_data, 0, sizeof(user_data_t)); pthread_mutex_init(&(user_data.pb_data_mutex), NULL); pthread_cond_init(&(user_data.pb_data_cond), NULL); pthread_cond_init(&(user_data.play_cond), NULL); pthread_mutex_init(&(user_data.cap_data_mutex), NULL); pthread_cond_init(&(user_data.cap_data_cond), NULL); user_data.playback_handle = &playback_handle; user_data.capture_handle = &capture_handle; user_data.reader_handle = &reader_handle; user_data.writer_handle = &writer_handle; user_data.alsa_handle = &alsa_handle; user_data.p_audiocap_config = &capture_config; user_data.p_audiopb_config = &playback_config; user_data.pb_process_status = START; user_data.cap_process_status = START; if (savefile && NULL == file_name){ file_name = "audio_lookback.wav"; } //initial configuration memset(&playback_config,0,sizeof(ATK_AUDIOPLAY_CONFIG_T)); memset(&capture_config,0,sizeof(ATK_AUDIOCAP_CONFIG_T)); //Playback playback_config.szPcmName = "hw:0,0"; playback_config.bIsInterleaved = interleaved; playback_config.eFormat = SND_PCM_FORMAT_S16_LE; playback_config.dwChannelsCount = channels; playback_config.dwSampleRate = sample_rate; playback_config.bUseSimpleConfig = simple_conf; playback_config.dwPeriodsPerBuffer = periods; playback_config.dwPeriodSizeInFrames = PERIOD_SIZE_IN_FRAMES; playback_config.bDropFramesBeforeStop = TRUE; //Capture capture_config.szPcmName = "hw:1,0"; capture_config.bIsInterleaved = interleaved; capture_config.eFormat = SND_PCM_FORMAT_S16_LE; capture_config.dwChannelsCount = channels; capture_config.dwSampleRate = sample_rate; capture_config.bUseSimpleConfig = simple_conf; capture_config.dwPeriodsPerBuffer = periods; capture_config.dwPeriodSizeInFrames = PERIOD_SIZE_IN_FRAMES; capture_config.pfnCallback = audiocap_callback; capture_config.pUserData = (void*) (&user_data); //save audio to file... if(savefile){ snprintf(filename, sizeof(filename), CAP_DIR "%s", file_name); fp = fopen(filename, "ab"); } signal(SIGPIPE, SIG_IGN); signal(SIGTERM, sig_kill); signal(SIGINT, sig_kill); isRunning = true; pthread_create(&main_tid, NULL, main_thread, &user_data); MsgBroker_RegisterMsg(CMD_UAC_FIFO_PATH); MsgBroker_Run(CMD_UAC_FIFO_PATH, msg_callback, &user_data, &is_terminate_); MsgBroker_UnRegisterMsg(); if (main_tid != 0) { pthread_join(main_tid, NULL); printf(" [%s,%d] Leaving the main thread \n",__func__,__LINE__); } end: if(savefile) fclose(fp); if(NULL!=writer_handle) SRB_Release(writer_handle); if(NULL!=reader_handle) SRB_Release(reader_handle); pthread_mutex_destroy(&(user_data.cap_data_mutex)); pthread_cond_destroy(&(user_data.cap_data_cond)); pthread_mutex_destroy(&(user_data.pb_data_mutex)); pthread_cond_destroy(&(user_data.pb_data_cond)); pthread_cond_destroy(&(user_data.play_cond)); if(NULL!=playback_handle) ATK_AudioPlay_Release(playback_handle); if(NULL!=capture_handle) ATK_AudioCap_Release(capture_handle); if(NULL!=alsa_handle) snd_hctl_close(alsa_handle); }