/*================================================================ Copyright (c) 2020 Quectel Wireless Solution, Co., Ltd. All Rights Reserved. Quectel Wireless Solution Proprietary and Confidential. =================================================================*/ #include #include #include #include "ql_api_osi.h" #include "ql_log.h" #include "audio_demo.h" #include "ql_osi_def.h" #include "ql_audio.h" #include "ql_fs.h" #include "ql_i2c.h" #include "quec_pin_index.h" #include "ql_gpio.h" #define QL_AUDIO_LOG_LEVEL QL_LOG_LEVEL_INFO #define QL_AUDIO_LOG(msg, ...) QL_LOG(QL_AUDIO_LOG_LEVEL, "ql_audio", msg, ##__VA_ARGS__) #define QL_AUDIO_LOG_PUSH(msg, ...) QL_LOG_PUSH("ql_AUDIO", msg, ##__VA_ARGS__) #if !defined(audio_demo_no_err) #define audio_demo_no_err(x, action, str) \ do \ { \ if(x != 0) \ { \ QL_AUDIO_LOG(str); \ {action;} \ } \ } while( 1==0 ) #endif /* for headset_det */ #ifdef QL_APP_FEATURE_HEADSET_DET #define QL_HEADSET_DETECT_PIN QUEC_PIN_DNAME_GPIO_8 #define QL_HEADSET_DETECT_GPIO QUEC_GPIO_DNAME_GPIO_8 #define QL_HEADSET_DETECT_FUNC_GPIO 0 #define QL_HEADSET_DETECT_DEBOUNCE_TIME 300 //unit: ms #define DEMO_HEADSET_DETECT_PLUG_IN 1 #define DEMO_HEADSET_DETECT_PLUG_OUT 0 ql_task_t headset_det_task = NULL; ql_timer_t headset_det_debounce_timer = NULL; #endif // only for EC600U ql_task_t headset_EC600U_task = NULL; #define ID_RIFF 0x46464952 #define ID_WAVE 0x45564157 #define ID_FMT 0x20746d66 #define FORMAT_PCM 1 #define CHECK_AUDIO_CORRECT 0 #define CHECK_AUDIO_INVALID_PARAMETER 1 #define CHECK_AUDIO_WAV_ERR 2 #define CHECK_AUDIO_MP3_ERR 3 #define CHECK_AUDIO_AMR_ERR 4 struct wav_header{ unsigned int riff_id; unsigned int riff_sz; unsigned int riff_fmt; unsigned int fmt_id; unsigned int fmt_sz; unsigned short audio_format; unsigned short num_channels; unsigned int sample_rate; unsigned int byte_rate; unsigned short block_align; unsigned short bits_per_sample; unsigned int data_id; unsigned int data_sz; }; typedef struct { PCM_HANDLE_T recorder; PCM_HANDLE_T player; }ql_demo_poc_t; #ifdef QL_APP_FEATURE_AUDIO_RECORD static uint8 *pcm_buffer = NULL; static uint pcm_data_size = 0; #endif static bool ring_tone_start = 0; ql_task_t ql_play_task = NULL; static int play_callback(char *p_data, int len, enum_aud_player_state state) { if(state == AUD_PLAYER_START) { QL_AUDIO_LOG("player start run"); } else if(state == AUD_PLAYER_FINISHED) { QL_AUDIO_LOG("player stop run"); } else { QL_AUDIO_LOG("type is %d", state); } return QL_AUDIO_SUCCESS; } #ifdef QL_APP_FEATURE_AUDIO_RECORD static int record_callback(char *p_data, int len, enum_aud_record_state state) { if(state == AUD_RECORD_START) { QL_AUDIO_LOG("recorder start run"); } else if(state == AUD_RECORD_CLOSE) { QL_AUDIO_LOG("recorder stop run"); } else if(state == AUD_RECORD_CALL_INT) { QL_AUDIO_LOG("recorder int from ring/call"); } else if(state == AUD_RECORD_DATA) { if(len <= 0) return -1; if(pcm_data_size > RECORD_BUFFER_MAX){ return -1; } else{ memcpy(pcm_buffer+pcm_data_size, p_data, len); pcm_data_size += len; } } return QL_AUDIO_SUCCESS; } #endif static int ringtone_callback(bool start, void *ctx) { if(start) { QL_AUDIO_LOG("ringtone start play"); if(ql_aud_get_play_state() != QL_AUDIO_STATUS_RUNNING) { ql_event_t event = {0}; event.id = QL_AUDIO_RINGTONE_PLAY; ring_tone_start = (ql_rtos_event_send(ql_play_task, &event) ? FALSE:TRUE); } } else { QL_AUDIO_LOG("ringtone stop play"); if(ring_tone_start){ ql_aud_player_stop(); ring_tone_start = FALSE; } } return 0; } static void ql_audio_demo_thread(void *param) { QL_AUDIO_LOG("enter audio demo"); #ifdef QL_APP_FEATURE_EXT_CODEC // test_codec(); #endif // config_internal_codec_gain(); //配置内置codec的音频增益,此处不建议调整,保持默认值; 用户若要调整可参考此函数 // test_pcm(); // test_mp3(); // test_wav(); // test_amr(); // test_amr_stream(); #ifdef QL_APP_FEATURE_AUDIO_RECORD // test_record_file(); // test_record_stream(); // test_poc_full_duplex(); // test_poc_half_duplex(); #endif // test_ring_tone(); //the demo "ql_voice_call_app_init" must be opened // test_tone_dtmf(); QL_AUDIO_LOG("test done, exit audio demo"); ql_rtos_task_delete(NULL); } static int check_wav_file(QFILE fd) { int ret = 0; struct wav_header hdr; ret = ql_fseek(fd, 0, SEEK_SET); if(ret < 0) { return -1; } ret = ql_fread(&hdr, sizeof(hdr), 1, fd); if(ret < sizeof(hdr)) { ql_fseek(fd, 0, SEEK_SET); return -1; } if( (hdr.riff_id != ID_RIFF) || (hdr.riff_fmt != ID_WAVE) || (hdr.fmt_id != ID_FMT) ) { return -1; } if(hdr.audio_format != FORMAT_PCM) { return -1; } return 0; } static int check_mp3_file(QFILE fd) { int64 ret = 0; char head[10] = {0}; ret = ql_fseek(fd, 0, SEEK_SET); if(ret < 0) { return -1; } ret = ql_fread(head, sizeof(head), 1, fd); if(ret < sizeof(head)) { ql_fseek(fd, 0, SEEK_SET); return -1; } if(strncmp(head, "ID3", 3) == 0) { return 0; } else if( (head[0] == 0xFF) && (head[1] & 0xF0) == 0xF0 ) { return 0; } return 1; } static int check_amr_file(QFILE fd) { return 0; } //Whether the audio format is correct? int check_audio_format(char *fname) { int err = CHECK_AUDIO_CORRECT; if (fname == NULL) return CHECK_AUDIO_INVALID_PARAMETER; char *dot = strrchr(fname, '.'); if (dot == NULL) return CHECK_AUDIO_INVALID_PARAMETER; QFILE fd = ql_fopen(fname, "r"); if(fd<0) { return CHECK_AUDIO_INVALID_PARAMETER; } if(!check_wav_file(fd)) { if (strcasecmp(dot, ".wav") != 0) { err = CHECK_AUDIO_WAV_ERR; } } else if(!check_mp3_file(fd)) { if (strcasecmp(dot, ".mp3") != 0) { err = CHECK_AUDIO_MP3_ERR; } } else if(!check_amr_file(fd)) { if (strcasecmp(dot, ".amr") != 0) { err = CHECK_AUDIO_AMR_ERR; } } ql_fclose(fd); return err; } static void ql_audio_play_thread(void *ctx) { int err = 0; ql_event_t event = {0}; while(1) { err = ql_event_try_wait(&event); if(err) { QL_AUDIO_LOG("wait event failed"); continue; } switch(event.id) { case QL_AUDIO_RINGTONE_PLAY: do { err = ql_aud_play_file_start("ring_tone.mp3", QL_AUDIO_PLAY_TYPE_LOCAL, NULL); if(err) { ring_tone_start = FALSE; break; } ql_aud_wait_play_finish(QL_WAIT_FOREVER); }while(ring_tone_start); break; } } } void config_internal_codec_gain(void) { int err = 0; //配置本地播放模式下, 喇叭输出的1级音量对应的dac增益为 -13.5db, 算法增益为0db, 且实时生效 err = ql_aud_set_icvolume_level_gain(QL_AUDIO_PLAY_TYPE_LOCAL, QL_OUTPUT_SPEAKER, AUDIOHAL_SPK_VOL_1, 25, 15); audio_demo_no_err(err, return, "config volume failed"); //配置非volte通话模式下, 耳机的侧音增益为MUTE(关闭侧音),且实时生效 ql_aud_set_icsidet_gain(QL_AUD_VOICECALL_NB, QL_OUTPUT_HEADPHONE, QL_ICMIC_SIDET_GAIN_MUTE); audio_demo_no_err(err, return, "config side tone gain failed"); } void test_ring_tone(void) { ql_aud_set_ringtone_type(QL_AUD_RING_CUSTOMER_DEF); ql_bind_ring_tone_cb(ringtone_callback); } void test_tone_dtmf(void) { int err=0; ql_set_audio_path_earphone(); ql_aud_set_volume(QL_AUDIO_PLAY_TYPE_LOCAL, AUDIOHAL_SPK_VOL_11); QL_AUDIO_LOG("test dtmf"); err = ql_aud_dtmf_tone_init(); audio_demo_no_err(err, goto exit, "init fail"); err = ql_aud_dtmf_play(AUDIOHAL_DTMF_4,1000,1000);// dtmf "4", play 1s, mute 1s audio_demo_no_err(err, goto exit, "play fail"); ql_rtos_task_sleep_ms(200); err = ql_aud_dtmf_play(AUDIOHAL_DTMF_5,500,500);// dtmf "5",play 0.5s, mute 0.5s audio_demo_no_err(err, goto exit, "play fail"); ql_rtos_task_sleep_ms(200); err = ql_aud_tone_play(300,1000,300,300); //tone freq1:300Hz freq2:1000Hz,play 0.3s, mute 0.3s audio_demo_no_err(err, goto exit, "play fail"); exit: ql_aud_tone_stop(); } #ifdef QL_APP_FEATURE_AUDIO_RECORD void test_poc_full_duplex(void) { QL_PCM_CONFIG_T config = {0}; int err = 0, cnt_read=0, cnt_write; char *buffer = NULL; static ql_demo_poc_t *demo_poc = NULL; config.channels = 1; //单声道 config.samplerate = 16000; ql_set_audio_path_earphone(); ql_aud_set_volume(QL_AUDIO_PLAY_TYPE_VOICE, AUDIOHAL_SPK_VOL_6); //POC mode use the param of voice call buffer = calloc(1, 1024); audio_demo_no_err(!buffer, return, "no memory"); demo_poc = calloc(1, sizeof(ql_demo_poc_t)); audio_demo_no_err(!demo_poc, return, "no memory"); demo_poc->recorder = ql_aud_pcm_open(&config, QL_AUDIO_FORMAT_PCM, QL_PCM_BLOCK_FLAG|QL_PCM_READ_FLAG, QL_PCM_POC); audio_demo_no_err(!demo_poc->recorder, goto exit, "player created failed"); demo_poc->player = ql_aud_pcm_open(&config, QL_AUDIO_FORMAT_PCM, QL_PCM_BLOCK_FLAG|QL_PCM_WRITE_FLAG, QL_PCM_POC); audio_demo_no_err(!demo_poc->player, goto exit, "player created failed"); err = ql_aud_start_poc_mode(QL_POC_TYPE_FULL_DUPLEX); audio_demo_no_err(err, goto exit, "player created failed"); while(1) { memset(buffer, 0, 1024); cnt_read = ql_pcm_read(demo_poc->recorder, buffer, 640); audio_demo_no_err((cnt_read<=0), goto exit, "read data failed"); cnt_write = ql_pcm_write(demo_poc->player, buffer, cnt_read); audio_demo_no_err((cnt_write!=cnt_read), goto exit, "read data failed"); } exit: if(buffer) { free(buffer); } ql_aud_stop_poc_mode(); //users must call "ql_aud_stop_poc_mode" then call "ql_pcm_close", otherwise may leed to some errors if(demo_poc) { if(demo_poc->player) { ql_pcm_close(demo_poc->player); } if(demo_poc->recorder) { ql_pcm_close(demo_poc->recorder); } free(demo_poc); } } void test_poc_half_duplex(void) { PCM_HANDLE_T recorder = NULL; PCM_HANDLE_T player = NULL; QL_PCM_CONFIG_T config; void *data = NULL; int size, total_size = 0, cnt=0, write_cnt=0; int err=0; config.channels = 1; //单声道 config.samplerate = 16000; config.amrwb_param.amrwb_mode = AMRWB_MODE_2385; data = calloc(1, 200*1024); if(data == NULL) { goto exit; } ql_set_audio_path_earphone(); ql_aud_set_volume(QL_AUDIO_PLAY_TYPE_VOICE, AUDIOHAL_SPK_VOL_6); //POC mode use the param of voice call recorder = ql_aud_pcm_open(&config, QL_AUDIO_FORMAT_PCM, QL_PCM_BLOCK_FLAG|QL_PCM_READ_FLAG, QL_PCM_POC); audio_demo_no_err(!recorder, goto exit, "recorder created failed"); player = ql_aud_pcm_open(&config, QL_AUDIO_FORMAT_PCM, QL_PCM_BLOCK_FLAG|QL_PCM_WRITE_FLAG, QL_PCM_POC); audio_demo_no_err(!player, goto exit, "player created failed"); err = ql_aud_start_poc_mode(QL_POC_TYPE_HALF_DUPLEX); audio_demo_no_err(err, goto exit, "poc mode start failed"); err = ql_aud_poc_switch(QL_POC_MODE_REC); audio_demo_no_err(err, goto exit, "poc mode switch failed"); //start record while(total_size < 200*1024) { size = ql_pcm_read(recorder, data+total_size, 1024); if(size <= 0) { break; } total_size += size; } err = ql_aud_poc_switch(QL_POC_MODE_PLAY); audio_demo_no_err(err, goto exit, "poc mode switch failed"); while(write_cnt < total_size) { if(total_size - write_cnt > PACKET_WRITE_MAX_SIZE) //单次最多可写入 PACKET_WRITE_MAX_SIZE 字节 { cnt = ql_pcm_write(player, data+write_cnt, PACKET_WRITE_MAX_SIZE); } else { cnt = ql_pcm_write(player, data+write_cnt, total_size - write_cnt); } if(cnt <= 0) { QL_AUDIO_LOG("write failed"); goto exit; } write_cnt += cnt; } while(ql_pcm_buffer_used(player)) //in poc mode, player will not stop if not ql_aud_stop_poc_mode called { ql_rtos_task_sleep_ms(20); //wait the write buffer empty } ql_rtos_task_sleep_ms(200); exit: ql_aud_stop_poc_mode(); //users must call "ql_aud_stop_poc_mode" then call "ql_pcm_close", otherwise may leed to some errors ql_pcm_close(recorder); ql_pcm_close(player); if(data) { free(data); } QL_AUDIO_LOG("test done"); } #endif void ql_audio_app_init(void) { QlOSStatus err = QL_OSI_SUCCESS; ql_task_t ql_audio_task = NULL; QL_AUDIO_LOG("audio demo enter"); err = ql_rtos_task_create(&ql_audio_task, 4096*2, APP_PRIORITY_NORMAL, "ql_audio", ql_audio_demo_thread, NULL, 5); if(err != QL_OSI_SUCCESS) { QL_AUDIO_LOG("audio task create failed"); } err = ql_rtos_task_create(&ql_play_task, 4096, APP_PRIORITY_NORMAL, "ql_audio", ql_audio_play_thread, NULL, 2); if(err != QL_OSI_SUCCESS) { QL_AUDIO_LOG("audio task create failed"); } } void test_amr_stream(void) { PCM_HANDLE_T PCM = NULL; QL_PCM_CONFIG_T config; void *data = NULL; int total_size = 0, cnt=0, write_cnt=0; config.channels = 1; //单声道 config.samplerate = 16000; config.amrwb_param.amrwb_mode = AMRWB_MODE_2385; #ifdef QL_APP_FEATURE_AUDIO_RECORD int size=0; PCM = ql_aud_pcm_open(&config, QL_AUDIO_FORMAT_AMRWB, QL_PCM_BLOCK_FLAG|QL_PCM_READ_FLAG, QL_PCM_LOCAL); if(PCM == NULL) { QL_AUDIO_LOG("open pcm failed"); goto exit; } data = malloc(100*1024); if(data == NULL) { goto exit; } QL_AUDIO_LOG("start read"); //start record while(total_size < 10*1024) { size = ql_pcm_read(PCM, data+total_size, 41); if(size <= 0) { break; } total_size += size; } QL_AUDIO_LOG("exit record"); if(total_size <= 0) { QL_AUDIO_LOG("read pcm failed"); goto exit; } QL_AUDIO_LOG("size is %d", total_size); if(ql_pcm_close(PCM) != 0) { QL_AUDIO_LOG("close pcm failed"); goto exit; } PCM = NULL; #endif PCM = ql_aud_pcm_open(&config, QL_AUDIO_FORMAT_AMRWB, QL_PCM_BLOCK_FLAG|QL_PCM_WRITE_FLAG, QL_PCM_LOCAL); if(PCM == NULL) { QL_AUDIO_LOG("open pcm failed"); goto exit; } while(write_cnt < total_size) { if(total_size - write_cnt > PACKET_WRITE_MAX_SIZE) //单次最多可写 PACKET_WRITE_MAX_SIZE 字节 { cnt = ql_pcm_write(PCM, data+write_cnt, PACKET_WRITE_MAX_SIZE); } else { cnt = ql_pcm_write(PCM, data+write_cnt, total_size - write_cnt); } if(cnt <= 0) { QL_AUDIO_LOG("write failed"); goto exit; } write_cnt += cnt; } ql_aud_data_done(); ql_aud_wait_play_finish(QL_WAIT_FOREVER); QL_AUDIO_LOG("play finish"); exit: if(PCM != NULL) { ql_pcm_close(PCM); } if(data != NULL) { free(data); data = NULL; } } #ifdef QL_APP_FEATURE_AUDIO_RECORD void test_record_stream(void) { ql_aud_config config = {0}; int cnt = 0, total_cnt=0, err; config.amrwb_param.amrwb_mode = AMRWB_MODE_2385; pcm_buffer = malloc(RECORD_BUFFER_MAX); if(!pcm_buffer){ return; } /* 录音 */ if(ql_aud_record_stream_start_ex(&config, QL_REC_TYPE_MIC, QL_AUDIO_FORMAT_AMRWB, record_callback)) { QL_AUDIO_LOG("record fail"); goto exit; } ql_rtos_task_sleep_s(5); //record 5s ql_aud_record_stop(); if(pcm_data_size <= 0){ QL_AUDIO_LOG("data invalid"); goto exit; } /* 读取录音文件用于播放,此处也可调用 ql_aud_play_file_start,或者ql_pcm_open+ql_pcm_write去播放 */ ql_set_audio_path_speaker(); ql_set_volume(TEST_PLAY_VOLUME); while(total_cnt < pcm_data_size) { if(pcm_data_size - total_cnt > PACKET_WRITE_MAX_SIZE) //单次最多可播放 PACKET_WRITE_MAX_SIZE 字节 { cnt = PACKET_WRITE_MAX_SIZE; err = ql_aud_play_stream_start(QL_AUDIO_FORMAT_AMRWB, pcm_buffer+total_cnt, cnt, QL_AUDIO_PLAY_TYPE_LOCAL, play_callback); } else { cnt = pcm_data_size - total_cnt; err = ql_aud_play_stream_start(QL_AUDIO_FORMAT_AMRWB, pcm_buffer+total_cnt, cnt, QL_AUDIO_PLAY_TYPE_LOCAL, play_callback); } if(err < 0) { QL_AUDIO_LOG("start failed"); goto exit; } else { QL_AUDIO_LOG("play %d bytes, total %d", cnt, total_cnt); total_cnt += cnt; } } ql_aud_data_done(); ql_aud_wait_play_finish(QL_WAIT_FOREVER); ql_aud_player_stop(); QL_AUDIO_LOG("test successful"); exit: if(pcm_buffer){ free(pcm_buffer); pcm_buffer = NULL; pcm_data_size = 0; } } void test_record_file(void) { ql_aud_config config = {0}; config.samplerate = 8000; if(ql_aud_record_file_start(TEST_RECORD_WAV_NAME, &config, QL_REC_TYPE_MIC, NULL) != QL_AUDIO_SUCCESS) { QL_AUDIO_LOG("record failed"); return; } QL_AUDIO_LOG("record start"); ql_rtos_task_sleep_s(5); //record 5s ql_aud_record_stop(); QL_AUDIO_LOG("record finish, start play"); ql_set_audio_path_speaker(); if(ql_aud_play_file_start(TEST_RECORD_WAV_NAME, QL_AUDIO_PLAY_TYPE_LOCAL, play_callback)) { QL_AUDIO_LOG("play failed"); return; } ql_aud_wait_play_finish(QL_WAIT_FOREVER); ql_aud_player_stop(); QL_AUDIO_LOG("test successful"); } #endif void test_mp3(void) { //检测MP3文件格式是否正确,防止误将其他格式的文件命名为MP3格式 //int err = check_audio_format(TEST_MP3_FILE_NAME);//路径为客户预置音频文件放置的路径 //if(err) //{ //QL_AUDIO_LOG("err = %d",err); //return; //} if(ql_aud_play_file_start(TEST_MP3_FILE_NAME, QL_AUDIO_PLAY_TYPE_LOCAL, play_callback)) { QL_AUDIO_LOG("play failed"); return; } ql_aud_wait_play_finish(QL_WAIT_FOREVER); if(ql_aud_play_file_start(TEST_MP3_FILE_NAME, QL_AUDIO_PLAY_TYPE_LOCAL, play_callback)) { QL_AUDIO_LOG("play failed"); return; } ql_aud_wait_play_finish(QL_WAIT_FOREVER); ql_aud_player_stop(); //播放结束,释放播放资源 QL_AUDIO_LOG("test mp3 successful"); } void test_wav(void) { int cnt = 0; //检测WAV文件格式是否正确,防止误将其他格式的文件命名为WAV格式 //int err = check_audio_format(TEST_WAV_FILE_NAME);//路径为客户预置音频文件放置的路径 //if(err) //{ //QL_AUDIO_LOG("err = %d",err); //return; //} if(ql_aud_play_file_start(TEST_WAV_FILE_NAME, QL_AUDIO_PLAY_TYPE_LOCAL, play_callback)) { QL_AUDIO_LOG("play failed"); return; } ql_aud_wait_play_finish(QL_WAIT_FOREVER); QL_AUDIO_LOG("play %d times ok", ++cnt); if(ql_aud_play_file_start(TEST_WAV_FILE_NAME, QL_AUDIO_PLAY_TYPE_LOCAL, play_callback)) { QL_AUDIO_LOG("play failed"); return; } ql_aud_wait_play_finish(QL_WAIT_FOREVER); QL_AUDIO_LOG("play %d times ok", ++cnt); ql_aud_player_stop(); //播放结束,释放播放资源 QL_AUDIO_LOG("test wav successful"); } void test_amr(void) { int cnt = 0; //检测AMR文件格式是否正确,防止误将其他格式的文件命名为AMR格式 //int err = check_audio_format(TEST_AMR_FILE_NAME);//路径为客户预置音频文件放置的路径 //if(err) //{ //QL_AUDIO_LOG("err = %d",err); //return; //} if(ql_aud_play_file_start(TEST_AMR_FILE_NAME, QL_AUDIO_PLAY_TYPE_LOCAL, play_callback)) { QL_AUDIO_LOG("play failed"); } ql_aud_wait_play_finish(QL_WAIT_FOREVER); QL_AUDIO_LOG("play %d times ok", ++cnt); if(ql_aud_play_file_start(TEST_AMR_FILE_NAME, QL_AUDIO_PLAY_TYPE_LOCAL, play_callback)) { QL_AUDIO_LOG("play failed"); } ql_aud_wait_play_finish(QL_WAIT_FOREVER); QL_AUDIO_LOG("play %d times ok", ++cnt); ql_aud_player_stop(); //播放结束,释放播放资源 QL_AUDIO_LOG("test wav successful"); } void test_pcm(void) { PCM_HANDLE_T PCM = NULL; QL_PCM_CONFIG_T config; void *data = NULL; int size=0, write_cnt=0, cnt=0; config.channels = 1; //单声道 config.samplerate = 8000; #ifdef QL_APP_FEATURE_AUDIO_RECORD PCM = ql_pcm_open(&config, QL_PCM_BLOCK_FLAG|QL_PCM_READ_FLAG); if(PCM == NULL) { QL_AUDIO_LOG("open pcm failed"); goto exit; } data = malloc(50*1024); if(data == NULL) { goto exit; } QL_AUDIO_LOG("start read"); size = ql_pcm_read(PCM, data, 50*1024); if(size <= 0) { QL_AUDIO_LOG("read pcm failed"); goto exit; } QL_AUDIO_LOG("size is %d", size); if(ql_pcm_close(PCM) != 0) { QL_AUDIO_LOG("close pcm failed"); goto exit; } PCM = NULL; #endif ql_set_audio_path_speaker(); PCM = ql_pcm_open(&config, QL_PCM_BLOCK_FLAG|QL_PCM_WRITE_FLAG); if(PCM == NULL) { QL_AUDIO_LOG("open pcm failed"); goto exit; } QL_AUDIO_LOG("start write"); while(write_cnt < size) { if(size - write_cnt > PACKET_WRITE_MAX_SIZE) //单次最多可播放 PACKET_WRITE_MAX_SIZE 字节 { cnt = ql_pcm_write(PCM, data+write_cnt, PACKET_WRITE_MAX_SIZE); } else { cnt = ql_pcm_write(PCM, data+write_cnt, size - write_cnt); } if(cnt <= 0) { QL_AUDIO_LOG("write failed"); goto exit; } write_cnt += cnt; } ql_aud_data_done(); ql_aud_wait_play_finish(QL_WAIT_FOREVER); QL_AUDIO_LOG("play done"); if(ql_pcm_close(PCM) != 0) { QL_AUDIO_LOG("close pcm failed"); goto exit; } PCM = NULL; QL_AUDIO_LOG("play finish"); exit: if(PCM != NULL) { ql_pcm_close(PCM); } if(data != NULL) { free(data); data = NULL; } } #ifdef QL_APP_FEATURE_EXT_CODEC void test_codec(void) { ql_aud_codec_clk_enable(); ql_I2cInit(QL_AUDIO_IIC_CHANNEL, STANDARD_MODE); aud_codec_init(); ql_switch_ext_codec(TRUE); //need reset, as dsp only read audio nv when start } void ALC5616_reg_nv_load(QL_HAL_CODEC_CFG_T *nv_cfg) { AUD_CODEC_REG_T init_reg[] = RT5616_NV_INIT_REG, \ close_reg[] = RT5616_NV_CLOSE_REG, \ sample_8k_reg[] = RT5616_NV_SAMPLE_8K_REG, \ sample_16k_reg[] = RT5616_NV_SAMPLE_16K_REG, \ sample_32k_reg[] = RT5616_NV_SAMPLE_32K_REG, \ sample_44_1k_reg[] = RT5616_NV_SAMPLE_44_1K_REG,\ sample_48k_reg[] = RT5616_NV_SAMPLE_48K_REG; memset(&nv_cfg->initRegCfg[0], 0, 100*sizeof(AUD_CODEC_REG_T)); //clear codec init register nv memset(&nv_cfg->closeRegCfg[0], 0, 50*sizeof(AUD_CODEC_REG_T)); //clear codec close register nv memset(&nv_cfg->sampleRegCfg[0][0], 0, 120*sizeof(AUD_CODEC_REG_T)); //clear codec samprate register nv memset(&nv_cfg->inpathRegCfg[0][0], 0, 120*sizeof(AUD_CODEC_REG_T)); //clear codec in path register nv memset(&nv_cfg->outpathRegCfg[0][0], 0, 80*sizeof(AUD_CODEC_REG_T)); //clear codec output path register nv memcpy(nv_cfg->initRegCfg, init_reg, sizeof(init_reg)); memcpy(nv_cfg->closeRegCfg, close_reg, sizeof(close_reg)); memcpy(&nv_cfg->sampleRegCfg[0], sample_8k_reg, sizeof(sample_8k_reg)); memcpy(&nv_cfg->sampleRegCfg[3], sample_16k_reg, sizeof(sample_16k_reg)); memcpy(&nv_cfg->sampleRegCfg[6], sample_32k_reg, sizeof(sample_32k_reg)); memcpy(&nv_cfg->sampleRegCfg[7], sample_44_1k_reg, sizeof(sample_44_1k_reg)); memcpy(&nv_cfg->sampleRegCfg[8], sample_48k_reg, sizeof(sample_48k_reg)); } void NAU8810_reg_nv_load(QL_HAL_CODEC_CFG_T *nv_cfg) { AUD_CODEC_REG_T init_reg[] = NAU8810_NV_INIT_REG, \ close_reg[] = NAU8810_NV_CLOSE_REG, \ sample_8k_reg[] = NAU8810_NV_SAMPLE_8K_REG, \ sample_16k_reg[] = NAU8810_NV_SAMPLE_16K_REG, \ sample_32k_reg[] = NAU8810_NV_SAMPLE_32K_REG, \ sample_44_1k_reg[] = NAU8810_NV_SAMPLE_44_1K_REG,\ sample_48k_reg[] = NAU8810_NV_SAMPLE_48K_REG; memset(&nv_cfg->initRegCfg[0], 0, 100*sizeof(AUD_CODEC_REG_T)); //clear codec init register nv memset(&nv_cfg->closeRegCfg[0], 0, 50*sizeof(AUD_CODEC_REG_T)); //clear codec close register nv memset(&nv_cfg->sampleRegCfg[0][0], 0, 120*sizeof(AUD_CODEC_REG_T)); //clear codec samprate register nv memset(&nv_cfg->inpathRegCfg[0][0], 0, 120*sizeof(AUD_CODEC_REG_T)); //clear codec in path register nv memset(&nv_cfg->outpathRegCfg[0][0], 0, 80*sizeof(AUD_CODEC_REG_T)); //clear codec output path register nv memcpy(nv_cfg->initRegCfg, init_reg, sizeof(init_reg)); memcpy(nv_cfg->closeRegCfg, close_reg, sizeof(close_reg)); memcpy(&nv_cfg->sampleRegCfg[0], sample_8k_reg, sizeof(sample_8k_reg)); memcpy(&nv_cfg->sampleRegCfg[3], sample_16k_reg, sizeof(sample_16k_reg)); memcpy(&nv_cfg->sampleRegCfg[6], sample_32k_reg, sizeof(sample_32k_reg)); memcpy(&nv_cfg->sampleRegCfg[7], sample_44_1k_reg, sizeof(sample_44_1k_reg)); memcpy(&nv_cfg->sampleRegCfg[8], sample_48k_reg, sizeof(sample_48k_reg)); } void ALC5616e_reg_nv_load(QL_HAL_CODEC_CFG_T *nv_cfg) { AUD_CODEC_REG_T init_reg[] = RT5616E_NV_INIT_REG, \ close_reg[] = RT5616E_NV_CLOSE_REG, \ sample_8k_reg[] = RT5616E_NV_SAMPLE_8K_REG, \ sample_16k_reg[] = RT5616E_NV_SAMPLE_16K_REG, \ sample_32k_reg[] = RT5616E_NV_SAMPLE_32K_REG; memset(&nv_cfg->initRegCfg[0], 0, 100*sizeof(AUD_CODEC_REG_T)); //clear codec init register nv memset(&nv_cfg->closeRegCfg[0], 0, 50*sizeof(AUD_CODEC_REG_T)); //clear codec close register nv memset(&nv_cfg->sampleRegCfg[0][0], 0, 120*sizeof(AUD_CODEC_REG_T)); //clear codec samprate register nv memset(&nv_cfg->inpathRegCfg[0][0], 0, 120*sizeof(AUD_CODEC_REG_T)); //clear codec in path register nv memset(&nv_cfg->outpathRegCfg[0][0], 0, 80*sizeof(AUD_CODEC_REG_T)); //clear codec output path register nv memcpy(nv_cfg->initRegCfg, init_reg, sizeof(init_reg)); memcpy(nv_cfg->closeRegCfg, close_reg, sizeof(close_reg)); memcpy(&nv_cfg->sampleRegCfg[0], sample_8k_reg, sizeof(sample_8k_reg)); memcpy(&nv_cfg->sampleRegCfg[3], sample_16k_reg, sizeof(sample_16k_reg)); memcpy(&nv_cfg->sampleRegCfg[6], sample_32k_reg, sizeof(sample_32k_reg)); } //switch to ext codec ,need reset ql_audio_errcode_e ql_switch_ext_codec(bool enable) { ql_audio_errcode_e err = QL_AUDIO_SUCCESS; uint16 codec_id; QL_HAL_CODEC_CFG_T *halCodecCfg = NULL; halCodecCfg = calloc(1, sizeof(QL_HAL_CODEC_CFG_T)); if(!halCodecCfg){ return QL_AUDIO_UNKNOWN_ERROR; } err = ql_aud_codec_read_nv(halCodecCfg); audio_demo_no_err(err, goto exit, "read failed"); switch(enable) { case 1: //ext codec err = ql_audio_iic_read(RT5616E_I2C_SLAVE_ADDR, RT5616E_VENDOR_ID_REG, 2, &codec_id); //read 5616e firstly, if is not 5616e, then read other codec id //at_audio_no_err(err, goto exit, "read codec failed"); if(codec_id == RT5616E_VENDOR_ID) //init codec 5616e nv { QL_AUDIO_LOG("ext codec is ALC5616E, start config"); halCodecCfg->i2cCfg.id = (QL_AUDIO_IIC_CHANNEL == i2c_1 ? 0:1); //0 for use iic1, 1 for use iic 2, and iic 2 is default halCodecCfg->basicCfg.iic_data_width = 3; //iic register 16bit, hsb first halCodecCfg->basicCfg.codecAddr = RT5616E_I2C_SLAVE_ADDR; //iic addr, dsp will use it to connect codec ALC5616e_reg_nv_load(halCodecCfg); //load 5616e register to struct } else { err = ql_audio_iic_read(RT5616_I2C_SLAVE_ADDR, RT5616_VENDOR_ID_REG, 1, &codec_id); //read 5616 firstly, if is not 5616, then read other codec id //at_audio_no_err(err, goto exit, "read codec failed"); if(codec_id == RT5616_VENDOR_ID) //init codec 5616 nv { QL_AUDIO_LOG("ext codec is ALC5616, start config"); halCodecCfg->i2cCfg.id = (QL_AUDIO_IIC_CHANNEL == i2c_1 ? 0:1); //0 for use iic1, 1 for use iic 2, and iic 2 is default halCodecCfg->basicCfg.iic_data_width = 3; //iic register 16bit, hsb first halCodecCfg->basicCfg.codecAddr = RT5616_I2C_SLAVE_ADDR; //iic addr, dsp will use it to connect codec ALC5616_reg_nv_load(halCodecCfg); //load 5616 register to struct } else { err = ql_audio_iic_read(NAU8810_I2C_SLAVE_ADDR, NAU8810_VENDOR_ID_REG, 1, &codec_id); //read 8810 firstly, if is not 8810, then read other codec id audio_demo_no_err(err, goto exit, "read codec failed"); if(codec_id == NAU8810_VENDOR_ID) { QL_AUDIO_LOG("ext codec is NAU8810, start config"); halCodecCfg->i2cCfg.id = (QL_AUDIO_IIC_CHANNEL == i2c_1 ? 0:1); //0 for use iic1, 1 for use iic 2, and iic 2 is default halCodecCfg->basicCfg.iic_data_width = 1; //iic register 9bit, hsb first halCodecCfg->basicCfg.codecAddr = NAU8810_I2C_SLAVE_ADDR; //iic addr, dsp will use it to connect codec NAU8810_reg_nv_load(halCodecCfg); //load 8810 register to struct } else { err = QL_AUDIO_OPER_NOT_SUPPORTED; audio_demo_no_err(err, goto exit, "not support current codec"); } } } halCodecCfg->basicCfg.externalCodec = 1; //switch to ext codec err = ql_aud_codec_write_nv(halCodecCfg); audio_demo_no_err(err, goto exit, "write nv failed"); break; case 0: //internal codec halCodecCfg->basicCfg.externalCodec = 0; err = ql_aud_codec_write_nv(halCodecCfg); audio_demo_no_err(err, goto exit, "write nv failed"); break; } exit: if(halCodecCfg){ free(halCodecCfg); } return err; } ql_audio_errcode_e aud_codec_init(void) { int err = QL_AUDIO_SUCCESS, i=0; uint16 codec_id; QL_HAL_CODEC_CFG_T *halCodecCfg = NULL; halCodecCfg = calloc(1, sizeof(QL_HAL_CODEC_CFG_T)); audio_demo_no_err(!halCodecCfg, return QL_AUDIO_UNKNOWN_ERROR, "malloc memory failed"); err = ql_aud_codec_read_nv(halCodecCfg); audio_demo_no_err(err, goto exit, "read failed"); if(halCodecCfg->basicCfg.externalCodec) //1 for use external codec { QL_AUDIO_LOG("codec iic addr is %p, clk mode is %d", halCodecCfg->basicCfg.codecAddr, halCodecCfg->i2cCfg.clockMode); ql_aud_codec_clk_enable(); err = ql_audio_iic_read(RT5616_I2C_SLAVE_ADDR, RT5616_VENDOR_ID_REG, 2, &codec_id); // require_action(err, goto exit, "read codec failed"); if(codec_id == RT5616_VENDOR_ID) //init codec 5616 { AUD_CODEC_REG_T reg_list[] = RT5616_INIT_REG; for (i = 0; i < sizeof(reg_list)/sizeof(reg_list[0]); i++) { err = ql_audio_iic_write(RT5616_I2C_SLAVE_ADDR, reg_list[i].regAddr, 2, reg_list[i].val); audio_demo_no_err(err, goto exit, "write codec register fail"); } } else //NAU8810 support now { err = ql_audio_iic_read(NAU8810_I2C_SLAVE_ADDR, NAU8810_VENDOR_ID_REG, 2, &codec_id); audio_demo_no_err(err, goto exit, "read codec failed"); if(codec_id == NAU8810_VENDOR_ID) //init codec 8810 { AUD_CODEC_REG_T reg_list[] = NAU8810_INIT_REG; for (i = 0; i < sizeof(reg_list)/sizeof(reg_list[0]); i++) { err = ql_audio_iic_write(NAU8810_I2C_SLAVE_ADDR, reg_list[i].regAddr, 1, reg_list[i].val); audio_demo_no_err(err, goto exit, "write codec register fail"); } } else { err = QL_AUDIO_UNKNOWN_ERROR; audio_demo_no_err(err, goto exit, "codec not valid"); } } } exit: if(halCodecCfg){ free(halCodecCfg); } return err; } static ql_audio_errcode_e ql_rt5616_write_reg(uint8 RegAddr, uint16 RegData) { ql_audio_errcode_e status = QL_AUDIO_SUCCESS; uint8 param_data[3] = {0x00}; uint8 retry_count = 5; param_data[0] = (uint8)((RegData >> 8) & 0xFF); param_data[1] = (uint8)(RegData & 0xff); do { status = (ql_audio_errcode_e)ql_I2cWrite(QL_AUDIO_IIC_CHANNEL, RT5616_I2C_SLAVE_ADDR, RegAddr, param_data, 2); if (status != QL_AUDIO_SUCCESS) { QL_AUDIO_LOG("Error:[%dth] device[0x%x] addr[0x%x] data[0x%x] failed", retry_count, RT5616_I2C_SLAVE_ADDR, RegAddr, RegData); } else { break; } } while (--retry_count); return (status == 0 ? QL_AUDIO_SUCCESS : QL_AUDIO_CODEC_WR_FAIL); } static ql_audio_errcode_e ql_rt5616_read_reg(uint8 RegAddr, uint16 *p_value) { ql_audio_errcode_e status = QL_AUDIO_SUCCESS; uint8 temp_buf[2]; uint8 retry_count = 5; do { status = (ql_audio_errcode_e)ql_I2cRead(QL_AUDIO_IIC_CHANNEL, RT5616_I2C_SLAVE_ADDR, RegAddr, temp_buf, 2); if (status != QL_AUDIO_SUCCESS) { QL_AUDIO_LOG("Error:[%dth] device[0x%x] addr[0x%x] failed", retry_count, RT5616_I2C_SLAVE_ADDR, RegAddr); } else { *p_value = (((uint16)temp_buf[0]) << 8) | temp_buf[1]; break; } } while (--retry_count); return (status == 0 ? QL_AUDIO_SUCCESS : QL_AUDIO_CODEC_RD_FAIL); } static ql_audio_errcode_e ql_nau8810_write_reg(uint8 RegAddr, uint16 RegData) { ql_audio_errcode_e status = QL_AUDIO_SUCCESS; uint8 param_data[3] = {0x00}; uint8 retry_count = 5; param_data[0] = (uint8)((RegData >> 8) & 0xFF); param_data[1] = (uint8)(RegData & 0xff); RegAddr = (RegAddr << 1) & 0xFE; RegAddr = RegAddr|((RegData>>8)&0x01); param_data[0] = (uint8)(RegData & 0xFF); do { status = (ql_audio_errcode_e)ql_I2cWrite(QL_AUDIO_IIC_CHANNEL, NAU8810_I2C_SLAVE_ADDR, RegAddr, param_data, 1); if (status != QL_AUDIO_SUCCESS) { QL_AUDIO_LOG("Error:[%dth] device[0x%x] addr[0x%x] data[0x%x] failed", retry_count, NAU8810_I2C_SLAVE_ADDR, RegAddr, RegData); } } while (--retry_count); return (status == 0 ? QL_AUDIO_SUCCESS : QL_AUDIO_CODEC_WR_FAIL); } static ql_audio_errcode_e ql_nau8810_read_reg(uint8 RegAddr, uint16 *p_value) { ql_audio_errcode_e status = QL_AUDIO_SUCCESS; uint8 temp_buf[2]; uint8 retry_count = 5; RegAddr = (RegAddr << 1) & 0xFE; do { status = (ql_audio_errcode_e)ql_I2cRead(QL_AUDIO_IIC_CHANNEL, NAU8810_I2C_SLAVE_ADDR, RegAddr, temp_buf, 2); if (status != QL_AUDIO_SUCCESS) { QL_AUDIO_LOG("Error:[%dth] device[0x%x] addr[0x%x] failed", retry_count, NAU8810_I2C_SLAVE_ADDR, RegAddr); } else { *p_value = (((uint16)temp_buf[0]) << 8) | temp_buf[1]; break; } } while (--retry_count); return (status == 0 ? QL_AUDIO_SUCCESS : QL_AUDIO_CODEC_RD_FAIL); } #ifdef QL_CODEC_5616E_SUPPORT static ql_audio_errcode_e ql_rt5616e_write_reg(uint16 RegAddr, uint16 RegData) { ql_audio_errcode_e status = QL_AUDIO_SUCCESS; uint8 param_data[3] = {0x00}; uint8 retry_count = 5; param_data[0] = (uint8)((RegData >> 8) & 0xFF); param_data[1] = (uint8)(RegData & 0xff); do { status = (ql_audio_errcode_e)ql_I2cWrite_16bit_addr(QL_AUDIO_IIC_CHANNEL, RT5616E_I2C_SLAVE_ADDR, RegAddr, param_data, 2); if (status != QL_AUDIO_SUCCESS) { QL_AUDIO_LOG("Error:[%dth] device[0x%x] addr[0x%x] data[0x%x] failed", retry_count, RT5616E_I2C_SLAVE_ADDR, RegAddr, RegData); } } while (--retry_count); return (status == 0 ? QL_AUDIO_SUCCESS : QL_AUDIO_CODEC_WR_FAIL); } static ql_audio_errcode_e ql_rt5616e_read_reg(uint16 RegAddr, uint16 *p_value) { ql_audio_errcode_e status = QL_AUDIO_SUCCESS; uint8 temp_buf[2]; uint8 retry_count = 5; do { status = (ql_audio_errcode_e)ql_I2cRead_16bit_addr(QL_AUDIO_IIC_CHANNEL, RT5616E_I2C_SLAVE_ADDR, RegAddr, temp_buf, 2); if (status != QL_AUDIO_SUCCESS) { QL_AUDIO_LOG("Error:[%dth] device[0x%x] addr[0x%x] failed", retry_count, RT5616E_I2C_SLAVE_ADDR, RegAddr); } else { *p_value = (((uint16)temp_buf[0]) << 8) | temp_buf[1]; break; } } while (--retry_count); return (status == 0 ? QL_AUDIO_SUCCESS : QL_AUDIO_CODEC_RD_FAIL); } #endif ql_audio_errcode_e ql_audio_iic_read(uint8 dev_addr, uint16 reg_addr, uint8 size, uint16 *p_val) { ql_audio_errcode_e err = 0; /* 5616 */ if(0x1B == dev_addr) { err = ql_rt5616_read_reg((uint8)reg_addr, p_val); } /* 8810 */ else if (0x1A == dev_addr) { // support now err = ql_nau8810_read_reg((uint8)reg_addr, p_val); } else { return QL_AUDIO_INVALID_PARAM; } return err; } ql_audio_errcode_e ql_audio_iic_write(uint8 dev_addr, uint16 reg_addr, uint8 size, uint16 val) { ql_audio_errcode_e err = QL_AUDIO_SUCCESS; /* 5616 */ if(0x1B == dev_addr) { err = ql_rt5616_write_reg((uint8)reg_addr, val); } /* 8810 */ else if (0x1A == dev_addr) { // support now err = ql_nau8810_write_reg((uint8)reg_addr, val); } else { return QL_AUDIO_INVALID_PARAM; } return err; } #endif #ifdef QL_APP_FEATURE_HEADSET_DET void _ql_headset_det_debounce_callback(void *ctx) { if( headset_det_debounce_timer == NULL || ql_rtos_timer_is_running(headset_det_debounce_timer) ) { return; } ql_rtos_timer_start(headset_det_debounce_timer, QL_HEADSET_DETECT_DEBOUNCE_TIME, 1); QL_AUDIO_LOG("headset timer start"); } static void _ql_headset_detect_callback(void *ctx) { ql_LvlMode headsetdet_value; ql_event_t event; ql_rtos_timer_stop(headset_det_debounce_timer); event.id = QUEC_HEADSET_DET_EVENT_IND; ql_gpio_get_level(QL_HEADSET_DETECT_GPIO, &headsetdet_value); if( headsetdet_value == LVL_LOW ) //detect plug_in { event.param1 = DEMO_HEADSET_DETECT_PLUG_IN; } else/* headsetdet_value == LVL_HIGH */ //detect plug_out { event.param1 = DEMO_HEADSET_DETECT_PLUG_OUT; } ql_rtos_event_send(headset_det_task, &event); } int ql_headset_det_pin_init(void) { int err; err = ql_pin_set_func(QL_HEADSET_DETECT_PIN, QL_HEADSET_DETECT_FUNC_GPIO); if( err != QL_GPIO_SUCCESS ) { QL_AUDIO_LOG("headset_det err = %d", err); return err; } return 0; } int ql_headset_det_interrupt_init(void) { int err; err = ql_int_register(QL_HEADSET_DETECT_GPIO, EDGE_TRIGGER, DEBOUNCE_EN, EDGE_BOTH, PULL_UP, _ql_headset_det_debounce_callback, NULL); if( err != QL_GPIO_SUCCESS ) { QL_AUDIO_LOG("headset_det err = %d", err); return err; } ql_int_enable(QL_HEADSET_DETECT_GPIO); return 0; } static void ql_headset_det_demo_thread(void *param) { QL_AUDIO_LOG("headset_det demo thread enter, param 0x%x", param); ql_event_t event; if( ql_headset_det_pin_init() != 0 ) { QL_AUDIO_LOG("headset_det pin init failed"); ql_rtos_task_delete(NULL); } if( ql_headset_det_interrupt_init() != 0 ) { QL_AUDIO_LOG("headset_det interrupt init failed"); ql_rtos_task_delete(NULL); } ql_LvlMode headsetdet_value; ql_gpio_get_level(QL_HEADSET_DETECT_GPIO, &headsetdet_value); if( headsetdet_value == LVL_LOW ) //detect plug_in { QL_AUDIO_LOG("start up headset plug_in"); ql_set_audio_path_earphone(); } else/* headsetdet_value == LVL_HIGH */ //detect plug_out { QL_AUDIO_LOG("start up headset plug_out"); ql_set_audio_path_speaker(); } while(1) { if( ql_event_try_wait(&event) != 0 ) { continue; } if( event.id == QUEC_HEADSET_DET_EVENT_IND ) { if( event.param1 == DEMO_HEADSET_DETECT_PLUG_IN ) { QL_AUDIO_LOG("headset_det detect plug_in"); ql_aud_player_pause(); ql_set_audio_path_earphone(); ql_aud_player_resume(); } else if( event.param1 == DEMO_HEADSET_DETECT_PLUG_OUT ) { QL_AUDIO_LOG("headset_det detect plug_out"); ql_aud_player_pause(); ql_set_audio_path_speaker(); ql_aud_player_resume(); } } } ql_rtos_task_delete(NULL); } void ql_headset_det_app_init(void) { QlOSStatus err = QL_OSI_SUCCESS; err = ql_rtos_task_create(&headset_det_task, 1024, APP_PRIORITY_NORMAL, "ql_headset_demo", ql_headset_det_demo_thread, NULL, 1); if( err != QL_OSI_SUCCESS ) { QL_AUDIO_LOG("headset_det demo task created failed"); } err = ql_rtos_timer_create(&headset_det_debounce_timer, headset_det_task, _ql_headset_detect_callback, NULL); if( err != QL_OSI_SUCCESS ) { QL_AUDIO_LOG("headset_det demo timer created failed"); } } #endif // only for EC600U static void _headset_EC600U_callback(QL_HEADSET_CBTYPE_E cb_type, uint32_t param) { ql_event_t event; switch(cb_type) { case QL_HEADSET_CBTYPE_PLUG_IN: event.id = QUEC_HEADSET_PLUG_IN_IND; break; case QL_HEADSET_CBTYPE_PLUG_OUT: event.id = QUEC_HEADSET_PLUG_OUT_IND; break; case QL_HEADSET_CBTYPE_BTN_DOWN: event.id = QUEC_HEADSET_BTN_DOWN_IND; break; case QL_HEADSET_CBTYPE_BTN_UP: event.id = QUEC_HEADSET_BTN_UP_IND; break; default: break; } event.param1 = param; ql_rtos_event_send(headset_EC600U_task, &event); } static void ql_headset_EC600U_demo_thread(void *param) { QL_AUDIO_LOG("headset_btn demo thread enter, param 0x%x", param); ql_event_t event; // needed callback type with [or] operation ql_headset_info_t headset_info = { _headset_EC600U_callback, QL_HEADSET_CBTYPE_PLUG_IN|QL_HEADSET_CBTYPE_PLUG_OUT|QL_HEADSET_CBTYPE_BTN_DOWN|QL_HEADSET_CBTYPE_BTN_UP }; ql_headset_cb_register(headset_info); /* audio path init */ QL_HEADSET_PLUGSTATUS_E plug_status = QL_HEADSET_PLUGSTATUS_OUT; ql_headset_plug_status_get(&plug_status); if( plug_status == QL_HEADSET_PLUGSTATUS_OUT ) { ql_set_audio_path_speaker(); // change to speaker } else { ql_set_audio_path_earphone(); // change to headset } /* get current audio path */ int in_path = -1; int out_path = -1; in_path = ql_aud_get_input_type(); out_path = ql_aud_get_output_type(); QL_AUDIO_LOG("headset_btn demo start in_path[%d] out_path[%d]", in_path, out_path); while(1) { if( ql_event_try_wait(&event) != 0 ) { continue; } switch(event.id) { case QUEC_HEADSET_PLUG_IN_IND: QL_AUDIO_LOG("headset plug in type [%d]", event.param1); /* do something */ in_path = ql_aud_get_input_type(); out_path = ql_aud_get_output_type(); QL_AUDIO_LOG("headset_btn before plug in in_path[%d] out_path[%d]", in_path, out_path); ql_set_audio_path_earphone(); // change to headset in_path = ql_aud_get_input_type(); out_path = ql_aud_get_output_type(); QL_AUDIO_LOG("headset_btn after plug in in_path[%d] out_path[%d]", in_path, out_path); break; case QUEC_HEADSET_PLUG_OUT_IND: QL_AUDIO_LOG("headset plug out type [%d]", event.param1); /* do something */ in_path = ql_aud_get_input_type(); out_path = ql_aud_get_output_type(); QL_AUDIO_LOG("headset_btn before plug out in_path[%d] out_path[%d]", in_path, out_path); ql_set_audio_path_speaker(); // change to speaker in_path = ql_aud_get_input_type(); out_path = ql_aud_get_output_type(); QL_AUDIO_LOG("headset_btn after plug in in_path[%d] out_path[%d]", in_path, out_path); break; case QUEC_HEADSET_BTN_DOWN_IND: QL_AUDIO_LOG("headset button down adc_value [%d]", event.param1); /* do something */ out_path = ql_aud_get_output_type(); QL_AUDIO_LOG("headset_btn demo button down out_path[%d]", out_path); break; case QUEC_HEADSET_BTN_UP_IND: QL_AUDIO_LOG("headset button up adc_value [%d]", event.param1); /* do something */ out_path = ql_aud_get_output_type(); QL_AUDIO_LOG("headset_btn demo button up out_path[%d]", out_path); break; default: break; } } ql_rtos_task_delete(NULL); } void ql_headset_EC600U_app_init(void) { QlOSStatus err = QL_OSI_SUCCESS; err = ql_rtos_task_create(&headset_EC600U_task, 1024, APP_PRIORITY_NORMAL, "600hs_demo", ql_headset_EC600U_demo_thread, NULL, 1); if( err != QL_OSI_SUCCESS ) { QL_AUDIO_LOG("headset_btn demo task created failed"); } } // only for EC600U