#include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "eq_log.h" #include "Rk_wake_lock.h" #include "Rk_socket_app.h" #define EQ_DRC_PROCESS_VERSION "1.29 20210720" #define SOC_IS_RK3308 (0x1) #define SOC_IS_RK3326 (0x2) #define ROCKCHIP_SOC SOC_IS_RK3326 #define SAMPLE_RATE 48000 #define CHANNEL 2 #define REC_DEVICE_NAME "fake_record" #define WRITE_DEVICE_NAME "fake_play" #define JACK_DEVICE_NAME "fake_jack" #define JACK2_DEVICE_NAME "fake_jack2" #define READ_FRAME_DEFAULT 1920 #define PERIOD_SIZE_DEFAULT (READ_FRAME_DEFAULT) #define PERIOD_COUNTS_DEFAULT (8) #define BUFFER_SIZE_DEFAULT (PERIOD_SIZE_DEFAULT * PERIOD_COUNTS_DEFAULT) /* Keeping a large buffer_size ASAP */ #define MUTE_TIME_DEFAULT (3) /* 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 /* * 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_ANALOG_HP 0x02 #define DEVICE_FLAG_DIGITAL_HP 0x03 #define DEVICE_FLAG_BLUETOOTH 0x04 #define DEVICE_FLAG_BLUETOOTH_BSA 0x05 enum BT_CONNECT_STATE{ BT_DISCONNECT = 0, BT_CONNECT_BLUEZ, BT_CONNECT_BSA }; #define POWER_STATE_PATH "/sys/power/state" #define USER_PLAY_STATUS "/dev/snd/pcmC7D0p" #define USER_CAPT_STATUS "/dev/snd/pcmC0D0c" /** * 0: By default and universal (Recommend) * 1: More fast but only used for RK817 or RK809 Codec */ #define KEEPING_HW_CARD 0 #if KEEPING_HW_CARD #define HW_CARD_PATH_DEFAULT "SPK" #endif struct user_play_inotify { int fd; int watch_desc; bool stop; }; struct user_capt_inotify { int fd; int watch_desc; bool stop; }; enum { USER_PLAY_CLOSED = 0, USER_PLAY_CLOSING, USER_PLAY_OPENED, }; enum { USER_CAPT_CLOSED = 0, USER_CAPT_CLOSING, USER_CAPT_OPENED, }; enum { POWER_STATE_RESUME = 0, POWER_STATE_SUSPENDING, POWER_STATE_SUSPEND, }; static int g_read_frame = READ_FRAME_DEFAULT; static int g_period_size = PERIOD_SIZE_DEFAULT; static int g_period_counts = PERIOD_COUNTS_DEFAULT; static int g_buffer_size = BUFFER_SIZE_DEFAULT; #if KEEPING_HW_CARD static char g_path_name[32]; volatile static bool g_fast_codec = false; #endif static struct user_play_inotify g_upi; static struct user_capt_inotify g_uci; static char g_bt_mac_addr[17]; static enum BT_CONNECT_STATE g_bt_is_connect = BT_DISCONNECT; static bool g_system_sleep = false; static char sock_path[] = "/data/bsa/config/bsa_socket"; #if KEEPING_HW_CARD static snd_pcm_t *write_handle_bak = NULL; #endif volatile static int power_state = POWER_STATE_RESUME; volatile static int user_play_state = USER_PLAY_CLOSED; volatile static int user_capt_state = USER_CAPT_CLOSED; 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); /* epoll for inotify */ #define EPOLL_SIZE 512 #define ARRAY_LENGTH 128 #define NAME_LENGTH 128 #define EPOLL_MAX_EVENTS 32 struct file_name_fd_desc { int fd; char name[32]; char base_name[NAME_LENGTH]; }; static struct epoll_event g_PendingEventItems[EPOLL_MAX_EVENTS]; static struct file_name_fd_desc g_file_name_fd_desc[ARRAY_LENGTH]; static int array_index = 0; static const char *base_dir = "/sys/power"; /* hanlding fade-in or fade-out */ #define FADE_IN 0 #define FADE_OUT 1 #ifndef av_clipd # define av_clipd av_clipd_c #endif enum CurveType { NONE = -1, TRI, QSIN, ESIN, HSIN, LOG, IPAR, QUA, CUB, SQU, CBR, PAR, EXP, IQSIN, IHSIN, DESE, DESI, LOSI, SINC, ISINC, NB_CURVES }; /** * Clip a double value into the amin-amax range. * @param a value to clip * @param amin minimum value of the clip range * @param amax maximum value of the clip range * @return clipped value */ static inline const double av_clipd_c(double a, double amin, double amax) { // eq_info("[EQ] %s - %d enter\n", __func__, __LINE__); if (a < amin) return amin; else if (a > amax) return amax; else return a; } static double fade_gain(int curve, int64_t index, int64_t range) { #define CUBE(a) ((a)*(a)*(a)) double gain; gain = av_clipd(1.0 * index / range, 0, 1.0); // eq_info("[EQ] %s - %d index=%ld gain=%lf\n", __func__, __LINE__, index, gain); switch (curve) { case QSIN: gain = sin(gain * M_PI / 2.0); break; case IQSIN: /* 0.6... = 2 / M_PI */ gain = 0.6366197723675814 * asin(gain); break; case ESIN: gain = 1.0 - cos(M_PI / 4.0 * (CUBE(2.0*gain - 1) + 1)); break; case HSIN: gain = (1.0 - cos(gain * M_PI)) / 2.0; break; case IHSIN: /* 0.3... = 1 / M_PI */ gain = 0.3183098861837907 * acos(1 - 2 * gain); break; case EXP: /* -11.5... = 5*ln(0.1) */ gain = exp(-11.512925464970227 * (1 - gain)); break; case LOG: gain = av_clipd(1 + 0.2 * log10(gain), 0, 1.0); break; case PAR: gain = 1 - sqrt(1 - gain); break; case IPAR: gain = (1 - (1 - gain) * (1 - gain)); break; case QUA: gain *= gain; break; case CUB: gain = CUBE(gain); break; case SQU: gain = sqrt(gain); break; case CBR: gain = cbrt(gain); break; case DESE: gain = gain <= 0.5 ? cbrt(2 * gain) / 2: 1 - cbrt(2 * (1 - gain)) / 2; break; case DESI: gain = gain <= 0.5 ? CUBE(2 * gain) / 2: 1 - CUBE(2 * (1 - gain)) / 2; break; case LOSI: { const double a = 1. / (1. - 0.787) - 1; double A = 1. / (1.0 + exp(0 -((gain-0.5) * a * 2.0))); double B = 1. / (1.0 + exp(a)); double C = 1. / (1.0 + exp(0-a)); gain = (A - B) / (C - B); } break; case SINC: gain = gain >= 1.0 ? 1.0 : sin(M_PI * (1.0 - gain)) / (M_PI * (1.0 - gain)); break; case ISINC: gain = gain <= 0.0 ? 0.0 : 1.0 - sin(M_PI * gain) / (M_PI * gain); break; case NONE: gain = 1.0; break; } return gain; } #define FADE(name, type) \ static void fade_samples_## name (uint8_t **dst, uint8_t * const *src, \ int nb_samples, int channels, int dir, \ int64_t start, int64_t range, int curve) \ { \ type *d = (type *)dst[0]; \ const type *s = (type *)src[0]; \ int i, c, k = 0; \ \ for (i = 0; i < nb_samples; i++) { \ double gain = fade_gain(curve, start + i * dir, range); \ for (c = 0; c < channels; c++, k++) \ d[k] = s[k] * gain; \ } \ } FADE(dbl, double) FADE(flt, float) FADE(s16, int16_t) FADE(s32, int32_t) static int add_to_epoll(int epoll_fd, int fd) { int result; struct epoll_event eventItem; memset(&eventItem, 0, sizeof(eventItem)); eventItem.events = EPOLLIN; eventItem.data.fd = fd; result = epoll_ctl(epoll_fd, EPOLL_CTL_ADD, fd, &eventItem); return result; } static void remove_from_epoll(int epoll_fd, int fd) { epoll_ctl(epoll_fd, EPOLL_CTL_DEL, fd, NULL); } static int get_name_from_fd(int fd, char **name) { int i; for(i = 0; i < ARRAY_LENGTH; i++) { if(fd == g_file_name_fd_desc[i].fd) { *name = g_file_name_fd_desc[i].name; return 0; } } return -1; } static int inotify_ctl_info(int inotify_fd, int epoll_fd) { char event_buf[EPOLL_SIZE]; int event_pos = 0; int event_size; struct inotify_event *event; int result; int tmp_fd; int i; memset(event_buf, 0, EPOLL_SIZE); result = read(inotify_fd, event_buf, sizeof(event_buf)); if(result < (int)sizeof(*event)) { printf("could not get event!\n"); return -1; } while (result >= (int)sizeof(*event)) { event = (struct inotify_event *)(event_buf + event_pos); if (event->len) { if (event->mask & IN_CREATE) { sprintf(g_file_name_fd_desc[array_index].name, "%s", event->name); sprintf(g_file_name_fd_desc[array_index].base_name, "%s/%s", base_dir, event->name); tmp_fd = open(g_file_name_fd_desc[array_index].base_name, O_RDWR); if(-1 == tmp_fd) { printf("inotify_ctl_info open error!\n"); return -1; } add_to_epoll(epoll_fd, tmp_fd); g_file_name_fd_desc[array_index].fd = tmp_fd; if(ARRAY_LENGTH == array_index) { array_index = 0; } array_index += 1; printf("add file to epoll: %s\n", event->name); } else if (event->mask & IN_DELETE) { for(i = 0; i < ARRAY_LENGTH; i++) { if(!strcmp(g_file_name_fd_desc[i].name, event->name)) { remove_from_epoll(epoll_fd, g_file_name_fd_desc[i].fd); g_file_name_fd_desc[i].fd = 0; memset(g_file_name_fd_desc[i].name, 0, sizeof(g_file_name_fd_desc[i].name)); memset(g_file_name_fd_desc[i].base_name, 0, sizeof(g_file_name_fd_desc[i].base_name)); printf("remove file from epoll: %s\n", event->name); break; } } } else if (event->mask & IN_MODIFY) { printf("modify file to epoll: %s and will suspend, power_state: %d\n", event->name, power_state); if (power_state == POWER_STATE_RESUME) power_state = POWER_STATE_SUSPENDING; else power_state = POWER_STATE_RESUME; } } event_size = sizeof(*event) + event->len; result -= event_size; event_pos += event_size; } return 0; } static void *power_status_listen(void *arg) { int inotify_fd; int epoll_fd; int result; int i; char readbuf[EPOLL_SIZE]; int readlen; char *tmp_name; return NULL; eq_info("[EQ] %s enter\n", __func__); epoll_fd = epoll_create(1); if(-1 == epoll_fd) { printf("epoll_create error!\n"); goto err; } inotify_fd = inotify_init(); result = inotify_add_watch(inotify_fd, base_dir, IN_MODIFY); if(-1 == result) { printf("inotify_add_watch error!\n"); goto err; } add_to_epoll(epoll_fd, inotify_fd); eq_info("[EQ] %s, %d add_to_epoll\n", __func__, __LINE__); while (1) { result = epoll_wait(epoll_fd, g_PendingEventItems, EPOLL_MAX_EVENTS, -1); if (-1 == result) { printf("epoll wait error!\n"); goto err; } else { for (i = 0; i < result; i++) { if (g_PendingEventItems[i].data.fd == inotify_fd) { if (-1 == inotify_ctl_info(inotify_fd, epoll_fd)) { printf("inotify_ctl_info error!\n"); goto err; } } else { if (!get_name_from_fd(g_PendingEventItems[i].data.fd, &tmp_name)) { readlen = read(g_PendingEventItems[i].data.fd, readbuf, EPOLL_SIZE); readbuf[readlen] = '\0'; printf("read data from %s : %s\n", tmp_name, readbuf); } } } } } err: eq_info("[EQ] %s exit\n", __func__); return NULL; } 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 = g_period_size; snd_pcm_uframes_t bufferSize = g_buffer_size; int dir = 0; int err; err = snd_pcm_open(capture_handle, REC_DEVICE_NAME, SND_PCM_STREAM_CAPTURE, 0); if (err) { eq_err("[EQ_RECORD_OPEN] Unable to open capture PCM device\n"); exit(1); } eq_debug("[EQ_RECORD_OPEN] snd_pcm_open\n"); //err = snd_pcm_hw_params_alloca(&hw_params); err = snd_pcm_hw_params_malloc(&hw_params); if(err) { eq_err("[EQ_RECORD_OPEN] cannot allocate hardware parameter structure (%s)\n", snd_strerror(err)); exit(1); } eq_debug("[EQ_RECORD_OPEN] snd_pcm_hw_params_malloc\n"); err = snd_pcm_hw_params_any(*capture_handle, hw_params); if(err) { eq_err("[EQ_RECORD_OPEN] cannot initialize hardware parameter structure (%s)\n", snd_strerror(err)); exit(1); } eq_debug("[EQ_RECORD_OPEN] 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) { eq_err("[EQ_RECORD_OPEN] Error setting interleaved mode\n"); exit(1); } eq_debug("[EQ_RECORD_OPEN] snd_pcm_hw_params_set_access!\n"); err = snd_pcm_hw_params_set_format(*capture_handle, hw_params, ALSA_READ_FORMAT); if (err) { eq_err("[EQ_RECORD_OPEN] Error setting format: %s\n", snd_strerror(err)); exit(1); } eq_debug("[EQ_RECORD_OPEN] snd_pcm_hw_params_set_format\n"); err = snd_pcm_hw_params_set_channels(*capture_handle, hw_params, channels); if (err) { eq_debug("[EQ_RECORD_OPEN] channels = %d\n",channels); eq_err("[EQ_RECORD_OPEN] Error setting channels: %s\n", snd_strerror(err)); exit(1); } eq_debug("[EQ_RECORD_OPEN] channels = %d\n",channels); err = snd_pcm_hw_params_set_buffer_size_near(*capture_handle, hw_params, &bufferSize); if (err) { eq_err("[EQ_RECORD_OPEN] Error setting buffer size (%d): %s\n", bufferSize, snd_strerror(err)); exit(1); } eq_debug("[EQ_RECORD_OPEN] bufferSize = %d\n",bufferSize); err = snd_pcm_hw_params_set_period_size_near(*capture_handle, hw_params, &periodSize, 0); if (err) { eq_err("[EQ_RECORD_OPEN] Error setting period time (%d): %s\n", periodSize, snd_strerror(err)); exit(1); } eq_debug("[EQ_RECORD_OPEN] periodSize = %d\n",periodSize); err = snd_pcm_hw_params_set_rate_near(*capture_handle, hw_params, &rate, 0/*&dir*/); if (err) { eq_err("[EQ_RECORD_OPEN] Error setting sampling rate (%d): %s\n", rate, snd_strerror(err)); exit(1); } eq_debug("[EQ_RECORD_OPEN] Rate = %d\n", rate); /* Write the parameters to the driver */ err = snd_pcm_hw_params(*capture_handle, hw_params); if (err < 0) { eq_err("[EQ_RECORD_OPEN] Unable to set HW parameters: %s\n", snd_strerror(err)); exit(1); } eq_debug("[EQ_RECORD_OPEN] Open record device done \n"); //if(set_sw_params(*capture_handle,bufferSize,periodSize,NULL) < 0) // exit(1); if(hw_params) snd_pcm_hw_params_free(hw_params); } int alsa_fake_device_write_open(snd_pcm_t** write_handle, int channels, uint32_t write_sampleRate, int device_flag, int *socket_fd) { snd_pcm_hw_params_t *write_params; snd_pcm_uframes_t write_periodSize = g_period_size; snd_pcm_uframes_t write_bufferSize = g_buffer_size; int write_err; int write_dir; char bluealsa_device[256] = {0}; if (device_flag == DEVICE_FLAG_ANALOG_HP) { eq_debug("[EQ_WRITE_OPEN] Open PCM: %s\n", JACK_DEVICE_NAME); write_err = snd_pcm_open(write_handle, JACK_DEVICE_NAME, SND_PCM_STREAM_PLAYBACK, 0); } else if (device_flag == DEVICE_FLAG_DIGITAL_HP) { eq_debug("[EQ_WRITE_OPEN] Open PCM: %s\n", JACK2_DEVICE_NAME); write_err = snd_pcm_open(write_handle, JACK2_DEVICE_NAME, SND_PCM_STREAM_PLAYBACK, 0); } else if (device_flag == DEVICE_FLAG_BLUETOOTH) { sprintf(bluealsa_device, "%s%s", "bluealsa:HCI=hci0,PROFILE=a2dp,DEV=", g_bt_mac_addr); eq_debug("[EQ_WRITE_OPEN] Open PCM: %s\n", bluealsa_device); write_err = snd_pcm_open(write_handle, bluealsa_device, SND_PCM_STREAM_PLAYBACK, 0); } else if (device_flag == DEVICE_FLAG_BLUETOOTH_BSA) { *socket_fd = RK_socket_client_setup(sock_path); if (*socket_fd < 0) { eq_err("[EQ_WRITE_OPEN] Fail to connect server socket\n"); return -1; } else { eq_debug("[EQ_WRITE_OPEN] Socket client connected\n"); return 0; } } else { eq_debug("[EQ_WRITE_OPEN] Open PCM: %s\n", WRITE_DEVICE_NAME); write_err = snd_pcm_open(write_handle, WRITE_DEVICE_NAME, SND_PCM_STREAM_PLAYBACK, 0); } if (write_err) { eq_err("[EQ_WRITE_OPEN] Unable to open playback PCM device: %d\n", write_err); return write_err; } eq_debug("[EQ_WRITE_OPEN] interleaved mode\n"); // snd_pcm_hw_params_alloca(&write_params); snd_pcm_hw_params_malloc(&write_params); eq_debug("[EQ_WRITE_OPEN] 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) { eq_err("[EQ_WRITE_OPEN] Error setting interleaved mode\n"); goto failed; } eq_debug( "[EQ_WRITE_OPEN] interleaved mode\n"); write_err = snd_pcm_hw_params_set_format(*write_handle, write_params, ALSA_WRITE_FORMAT); if (write_err) { eq_err("[EQ_WRITE_OPEN] Error setting format: %s\n", snd_strerror(write_err)); goto failed; } eq_debug("[EQ_WRITE_OPEN] format successed\n"); write_err = snd_pcm_hw_params_set_channels(*write_handle, write_params, channels); if (write_err) { eq_err( "[EQ_WRITE_OPEN] Error setting channels: %s\n", snd_strerror(write_err)); goto failed; } eq_debug("[EQ_WRITE_OPEN] 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) { eq_err("[EQ_WRITE_OPEN] Error setting sampling rate (%d): %s\n", write_sampleRate, snd_strerror(write_err)); goto failed; } eq_debug("[EQ_WRITE_OPEN] 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) { eq_err("[EQ_WRITE_OPEN] Error setting buffer size (%ld): %s\n", write_bufferSize, snd_strerror(write_err)); goto failed; } eq_debug("[EQ_WRITE_OPEN] write_bufferSize = %d\n", write_bufferSize); write_err = snd_pcm_hw_params_set_period_size_near(*write_handle, write_params, &write_periodSize, 0); if (write_err) { eq_err("[EQ_WRITE_OPEN] Error setting period time (%ld): %s\n", write_periodSize, snd_strerror(write_err)); goto failed; } eq_debug("[EQ_WRITE_OPEN] write_periodSize = %d\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); eq_debug(" 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); eq_debug(" final period size %ld \n" , write_final_period); #endif { int monotonic, can_pause; monotonic = snd_pcm_hw_params_is_monotonic(write_params); can_pause = snd_pcm_hw_params_can_pause(write_params); eq_info("[EQ_INFO] monotonic: %d, can_pause: %d\n", monotonic, can_pause); } /* Write the parameters to the driver */ write_err = snd_pcm_hw_params(*write_handle, write_params); if (write_err < 0) { eq_err("[EQ_WRITE_OPEN] Unable to set HW parameters: %s\n", snd_strerror(write_err)); goto failed; } eq_debug("[EQ_WRITE_OPEN] open write device is successful\n"); if(set_sw_params(*write_handle, write_bufferSize, write_periodSize, NULL) < 0) goto failed; if(write_params) snd_pcm_hw_params_free(write_params); #if KEEPING_HW_CARD if (device_flag == DEVICE_FLAG_LINE_OUT) g_fast_codec = true; #endif return 0; failed: if(write_params) snd_pcm_hw_params_free(write_params); snd_pcm_close(*write_handle); *write_handle = NULL; return -1; } 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; snd_pcm_uframes_t threshold; 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) { eq_err("[EQ_SET_SW_PARAMS] Get current params: %s\n", snd_strerror(err)); goto failed; } /* start the transfer when the buffer is full (or almost full) */ threshold = (buffer_size / period_size) * period_size; if ((err = snd_pcm_sw_params_set_start_threshold(pcm, params, threshold)) != 0) { eq_err("[EQ_SET_SW_PARAMS] Set start threshold: %s: %lu\n", snd_strerror(err), threshold); goto failed; } /* 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) { eq_err("[EQ_SET_SW_PARAMS] Set avail min: %s: %lu\n", snd_strerror(err), period_size); goto failed; } if ((err = snd_pcm_sw_params(pcm, params)) != 0) { eq_err("[EQ_SET_SW_PARAMS] %s\n", snd_strerror(err)); goto failed; } if(params) snd_pcm_sw_params_free(params); return 0; failed: if(params) snd_pcm_sw_params_free(params); return -1; } static int is_mute_frame(short *in,unsigned int size) { int i; int mute_count = 0; if (!size) { eq_err("frame size is zero!!!\n"); return 0; } for (i = 0; i < size;i ++) { if(in[i] != 0) return 0; } return 1; } /* Determine whether to enter the energy saving mode according to * the value of the environment variable "EQ_LOW_POWERMODE" */ bool low_power_mode_check() { char *value = NULL; /* env: "EQ_LOW_POWERMODE=TRUE" or "EQ_LOW_POWERMODE=true" ? */ value = getenv("EQ_LOW_POWERMODE"); if (value && (!strcmp("TRUE", value) || !strcmp("true", value))) return true; return false; } /* Check device changing. */ int get_device_flag() { int fd = 0, ret = 0; char buff[512] = {0}; int device_flag = DEVICE_FLAG_LINE_OUT; #if (ROCKCHIP_SOC == SOC_IS_RK3308) const char *path = "/sys/devices/platform/ff560000.acodec/rk3308-acodec-dev/dac_output"; #else /* else is RK3326 */ const char *path = "/sys/class/switch/h2w/state"; #endif FILE *pp = NULL; /* pipeline */ char *bt_mac_addr = NULL; if (g_bt_is_connect == BT_CONNECT_BLUEZ) return DEVICE_FLAG_BLUETOOTH; else if(g_bt_is_connect == BT_CONNECT_BSA) return DEVICE_FLAG_BLUETOOTH_BSA; fd = open(path, O_RDONLY); if (fd < 0) { eq_err("[EQ_DEVICE_FLAG] Open %s failed!\n", path); return device_flag; } ret = read(fd, buff, sizeof(buff)); if (ret <= 0) { eq_err("[EQ_DEVICE_FLAG] Read %s failed!\n", path); close(fd); return device_flag; } #if (ROCKCHIP_SOC == SOC_IS_RK3308) if (strstr(buff, "hp out")) device_flag = DEVICE_FLAG_ANALOG_HP; #else /* else is RK3326 */ if (strstr(buff, "1")) device_flag = DEVICE_FLAG_ANALOG_HP; else if (strstr(buff, "2")) device_flag = DEVICE_FLAG_DIGITAL_HP; #endif close(fd); return device_flag; } /* Get device name frome device_flag */ const char *get_device_name(int device_flag) { const char *device_name = NULL; switch (device_flag) { case DEVICE_FLAG_BLUETOOTH: case DEVICE_FLAG_BLUETOOTH_BSA: device_name = "BLUETOOTH"; break; case DEVICE_FLAG_ANALOG_HP: device_name = JACK_DEVICE_NAME; break; case DEVICE_FLAG_DIGITAL_HP: device_name = JACK2_DEVICE_NAME; break; case DEVICE_FLAG_LINE_OUT: device_name = WRITE_DEVICE_NAME; break; default: break; } return device_name; } static void user_play_inotify_handler(struct inotify_event *event) { // eq_info("[EQ] %s enter\n", __func__); // eq_info("[EQ] event->mask: 0x%08x\n", event->mask); // eq_info("[EQ] event->name: %s\n", event->name); switch (event->mask) { case IN_OPEN: user_play_state = USER_PLAY_OPENED; eq_info("[EQ] %s USER_PLAY_OPENED\n", __func__); break; case IN_CLOSE_WRITE: user_play_state = USER_PLAY_CLOSING; eq_info("[EQ] %s USER_PLAY_CLOSING\n", __func__); break; default: break; } } static void user_capt_inotify_handler(struct inotify_event *event) { // eq_info("[EQ] %s enter\n", __func__); // eq_info("[EQ] event->mask: 0x%08x\n", event->mask); // eq_info("[EQ] event->name: %s\n", event->name); switch (event->mask) { case IN_OPEN: user_capt_state = USER_CAPT_OPENED; eq_info("[EQ] %s USER_CAPT_OPENED\n", __func__); break; case IN_CLOSE_WRITE: user_capt_state = USER_CAPT_CLOSING; eq_info("[EQ] %s USER_CAPT_CLOSING\n", __func__); break; default: break; } } static void *user_play_status_listen(void *arg) { struct user_play_inotify *upi = &g_upi; struct inotify_event *event = NULL; FILE *fp; char *buf; eq_info("[EQ] %s enter\n", __func__); buf = (char *)calloc(1024, 1); if (!buf) { eq_err("[EQ] %s alloc buf failed!\n", __func__); return NULL; } upi->stop = 0; upi->fd = inotify_init(); upi->watch_desc = inotify_add_watch(upi->fd, USER_PLAY_STATUS, IN_OPEN | IN_CLOSE_WRITE); while (!upi->stop) { fd_set fds; FD_ZERO(&fds); FD_SET(upi->fd, &fds); if (select(upi->fd + 1, &fds, NULL, NULL, NULL) > 0) { int len, index = 0; while (((len = read(upi->fd, buf, 1024)) < 0) && (errno == EINTR)); while (index < len) { event = (struct inotify_event *)(buf + index); user_play_inotify_handler(event); index += sizeof(struct inotify_event) + event->len; } } } if (upi->fd >= 0) { inotify_rm_watch(upi->fd, upi->watch_desc); close(upi->fd); upi->fd = -1; } if (buf) free(buf); eq_info("[EQ] %s exit\n", __func__); return NULL; err_out: if (buf) free(buf); return NULL; } static void *user_capt_status_listen(void *arg) { struct user_capt_inotify *uci = &g_uci; struct inotify_event *event = NULL; FILE *fp; char *buf; eq_info("[EQ] %s enter\n", __func__); buf = (char *)calloc(1024, 1); if (!buf) { eq_err("[EQ] %s alloc buf failed!\n", __func__); return NULL; } uci->stop = 0; uci->fd = inotify_init(); uci->watch_desc = inotify_add_watch(uci->fd, USER_CAPT_STATUS, IN_OPEN | IN_CLOSE_WRITE); while (!uci->stop) { fd_set fds; FD_ZERO(&fds); FD_SET(uci->fd, &fds); if (select(uci->fd + 1, &fds, NULL, NULL, NULL) > 0) { int len, index = 0; while (((len = read(uci->fd, buf, 1024)) < 0) && (errno == EINTR)); while (index < len) { event = (struct inotify_event *)(buf + index); user_capt_inotify_handler(event); index += sizeof(struct inotify_event) + event->len; } } } if (uci->fd >= 0) { inotify_rm_watch(uci->fd, uci->watch_desc); close(uci->fd); uci->fd = -1; } if (buf) free(buf); eq_info("[EQ] %s exit\n", __func__); return NULL; err_out: if (buf) free(buf); return NULL; } void *a2dp_status_listen(void *arg) { int ret = 0; char buff[100] = {0}; struct sockaddr_un clientAddr; struct sockaddr_un serverAddr; int sockfd; socklen_t addr_len; char *start = NULL; snd_pcm_t* audio_bt_handle; char bluealsa_device[256] = {0}; int retry_cnt = 5; sockfd = socket(AF_UNIX, SOCK_DGRAM, 0); if (sockfd < 0) { eq_err("[EQ_A2DP_LISTEN] Create socket failed!\n"); return NULL; } serverAddr.sun_family = AF_UNIX; strcpy(serverAddr.sun_path, "/tmp/a2dp_master_status"); system("rm -rf /tmp/a2dp_master_status"); ret = bind(sockfd, (struct sockaddr *)&serverAddr, sizeof(serverAddr)); if (ret < 0) { eq_err("[EQ_A2DP_LISTEN] Bind Local addr failed!\n"); return NULL; } while(1) { addr_len = sizeof(clientAddr); memset(buff, 0, sizeof(buff)); ret = recvfrom(sockfd, buff, sizeof(buff), 0, (struct sockaddr *)&clientAddr, &addr_len); if (ret <= 0) { eq_err("[EQ_A2DP_LISTEN]: %s\n", strerror(errno)); break; } eq_debug("[EQ_A2DP_LISTEN] Received a message(%s)\n", buff); if (strstr(buff, "status:connect:bsa-source")) { if (g_bt_is_connect == BT_DISCONNECT) { eq_debug("[EQ_A2DP_LISTEN] bsa bluetooth source is connect\n"); g_bt_is_connect = BT_CONNECT_BSA; } } else if (strstr(buff, "status:connect")) { start = strstr(buff, "address:"); if (start == NULL) { eq_debug("[EQ_A2DP_LISTEN] Received a malformed connect message(%s)\n", buff); continue; } start += strlen("address:"); if (g_bt_is_connect == BT_DISCONNECT) { //sleep(2); memcpy(g_bt_mac_addr, start, sizeof(g_bt_mac_addr)); sprintf(bluealsa_device, "%s%s", "bluealsa:HCI=hci0,PROFILE=a2dp,DEV=", g_bt_mac_addr); retry_cnt = 5; while (retry_cnt--) { eq_debug("[EQ_A2DP_LISTEN] try open bluealsa device(%d)\n", retry_cnt + 1); ret = snd_pcm_open(&audio_bt_handle, bluealsa_device, SND_PCM_STREAM_PLAYBACK, 0); if (ret == 0) { snd_pcm_close(audio_bt_handle); g_bt_is_connect = BT_CONNECT_BLUEZ; break; } usleep(600000); //600ms * 5 = 3s. } } } else if (strstr(buff, "status:disconnect")) { g_bt_is_connect = BT_DISCONNECT; } else if (strstr(buff, "status:suspend")) { g_system_sleep = true; } else if (strstr(buff, "status:resume")) { g_system_sleep = false; } else { eq_debug("[EQ_A2DP_LISTEN] Received a malformed message(%s)\n", buff); } } close(sockfd); return NULL; } static void sigpipe_handler(int sig) { eq_info("[EQ] catch the signal number: %d\n", sig); } static int signal_handler() { struct sigaction sa; /* Install signal handler for SIGPIPE */ memset(&sa, 0, sizeof(sa)); sa.sa_handler = sigpipe_handler; sigemptyset(&sa.sa_mask); sa.sa_flags = 0; if (sigaction(SIGPIPE, &sa, NULL) < 0) { eq_err("sigaction() failed: %s", strerror(errno)); return -1; } return 0; } #if 1 /* I/O error handler */ static int eq_drc_xrun(snd_pcm_t *handle, snd_pcm_stream_t stream) { snd_pcm_status_t *status; snd_output_t *log; int fatal_errors = 0, monotonic = 1, verbose = 1; int res; eq_err("[EQ] %s %d enter\n", __func__, __LINE__); snd_output_stdio_attach(&log, stderr, 0); snd_pcm_status_alloca(&status); if ((res = snd_pcm_status(handle, status))<0) { eq_err("[EQ] status error: %s\n", snd_strerror(res)); // prg_exit(EXIT_FAILURE); } if (snd_pcm_status_get_state(status) == SND_PCM_STATE_XRUN) { if (fatal_errors) { eq_err("[EQ] fatal %s: %s\n", stream == SND_PCM_STREAM_PLAYBACK ? "underrun" : "overrun", snd_strerror(res)); // prg_exit(EXIT_FAILURE); } if (monotonic) { #ifdef HAVE_CLOCK_GETTIME struct timespec now, diff, tstamp; clock_gettime(CLOCK_MONOTONIC, &now); snd_pcm_status_get_trigger_htstamp(status, &tstamp); timermsub(&now, &tstamp, &diff); fprintf(stderr, "%s!!! (at least %.3f ms long)\n", stream == SND_PCM_STREAM_PLAYBACK ? "underrun" : "overrun", diff.tv_sec * 1000 + diff.tv_nsec / 1000000.0); #else fprintf(stderr, "%s !!!\n", "underrun"); #endif } else { struct timeval now, diff, tstamp; gettimeofday(&now, 0); snd_pcm_status_get_trigger_tstamp(status, &tstamp); timersub(&now, &tstamp, &diff); fprintf(stderr, "%s!!! (at least %.3f ms long)\n", stream == SND_PCM_STREAM_PLAYBACK ? "underrun" : "overrun", diff.tv_sec * 1000 + diff.tv_usec / 1000.0); } if (verbose) { fprintf(stderr, "Status:\n"); snd_pcm_status_dump(status, log); } if ((res = snd_pcm_prepare(handle))<0) { eq_err("[EQ] xrun: prepare error: %s\n", snd_strerror(res)); // prg_exit(EXIT_FAILURE); } goto out_xrun; /* ok, data should be accepted again */ } if (snd_pcm_status_get_state(status) == SND_PCM_STATE_DRAINING) { if (verbose) { fprintf(stderr, "Status(DRAINING):\n"); snd_pcm_status_dump(status, log); } if (stream == SND_PCM_STREAM_CAPTURE) { fprintf(stderr, "capture stream format change? attempting recover...\n"); if ((res = snd_pcm_prepare(handle))<0) { eq_err("[EQ] xrun(DRAINING): prepare error: %s\n", snd_strerror(res)); // prg_exit(EXIT_FAILURE); } goto out_xrun; } } if (verbose) { fprintf(stderr, "Status(R/W):\n"); snd_pcm_status_dump(status, log); } eq_err("[EQ] read/write error, state = %s\n", snd_pcm_state_name(snd_pcm_status_get_state(status))); // prg_exit(EXIT_FAILURE); out_xrun: snd_output_close(log); return 0; } #endif static void usage(char *command) { snd_pcm_format_t k; printf( "Usage: %s [OPTION]...\n" "\n" "-h, --help help\n" "-v --version print current version\n" "-s, --seconds close sound card after playback is stopped seconds (default: 3s)\n" "-p, --period-size specify the size of the frame period (default: 1920)\n" "-n, --period-counts specify the count of the frame periods (default: 8)\n" #if KEEPING_HW_CARD "-P, --path-name specify the name of playback path for RK817/RK809 codec (default: SPK)\n" #endif , command); } static long parse_long(const char *str, int *err) { long val; char *endptr; errno = 0; val = strtol(str, &endptr, 0); if (errno != 0 || *endptr != '\0') *err = -1; else *err = 0; return val; } static void get_version(char *command) { printf("%s: version " EQ_DRC_PROCESS_VERSION " by Rockchip\n", command); } int main(int argc, char *argv[]) { int err, c; snd_pcm_t *capture_handle, *write_handle; char *buffer; unsigned int sampleRate, channels; int mute_frame_thd, mute_frame, skip_frame = 0; /* LINE_OUT is the default output device */ int device_flag, new_flag, last_flag; pthread_t a2dp_status_listen_thread; pthread_t user_play_status_listen_thread; // pthread_t user_capt_status_listen_thread; // pthread_t power_status_listen_thread; // struct rk_wake_lock* wake_lock; bool low_power_mode = low_power_mode_check(); volatile bool need_close_card = false; char *silence_data; int socket_fd = -1; clock_t startProcTime, endProcTime; int mute_time = MUTE_TIME_DEFAULT; int option_index; static char *command = argv[0]; static const char short_options[] = "hvs:p:n:P:"; static const struct option long_options[] = { {"help", 0, 0, 'h'}, {"version", 0, 0, 'v'}, {"seconds", 1, 0, 's'}, {"period-size", 1, 0, 'p'}, {"period-counts", 1, 0, 'n'}, #if KEEPING_HW_CARD {"path-name", 1, 0, 'P'}, #endif {0, 0, 0, 0} }; #if KEEPING_HW_CARD memset(g_path_name, 0, sizeof(g_path_name)); strcpy(g_path_name, HW_CARD_PATH_DEFAULT); #endif while ((c = getopt_long(argc, argv, short_options, long_options, &option_index)) != -1) { switch (c) { case 'h': usage(command); return 0; case 'v': get_version(command); return 0; case 's': mute_time = parse_long(optarg, &err); if (err < 0) { eq_err("[EQ] invalid mute_time argument '%s'\n", optarg); return -1; } break; case 'p': g_period_size = parse_long(optarg, &err); if (err < 0) { eq_err("[EQ] invalid g_period_size argument '%s'\n", optarg); return -1; } g_read_frame = g_period_size; break; case 'n': g_period_counts = parse_long(optarg, &err); if (err < 0) { eq_err("[EQ] invalid g_period_counts argument '%s'\n", optarg); return -1; } break; #if KEEPING_HW_CARD case 'P': memset(g_path_name, 0, sizeof(g_path_name)); strcpy(g_path_name, optarg); break; #endif default: eq_err("[EQ] Try `%s --help' for more information.\n", command); return 1; } } if (g_period_size == 0 || g_period_counts == 0) { eq_err("[EQ] g_period_size:%d or g_period_size:%d is zero!\n", g_period_size, g_period_counts); return -1; } g_buffer_size = g_period_size * g_period_counts; buffer = (char *)malloc(g_read_frame * g_period_counts * CHANNEL * sizeof(int16_t)); if (!buffer) { eq_err("[EQ] Alloc buffer failed\n"); return -1; } silence_data = (char *)malloc(g_read_frame * sizeof(int16_t) * CHANNEL); /* 2ch 16bit */ if (!silence_data) { eq_err("[EQ] Alloc silence_data failed\n"); return -1; } // wake_lock = RK_wake_lock_new("eq_drc_process"); if(signal_handler() < 0) { eq_err("[EQ] Install signal_handler for SIGPIPE failed\n"); return -1; } /* Create a thread to listen for Bluetooth connection status. */ // pthread_create(&power_status_listen_thread, NULL, power_status_listen, NULL); pthread_create(&user_play_status_listen_thread, NULL, user_play_status_listen, NULL); // pthread_create(&user_capt_status_listen_thread, NULL, user_capt_status_listen, NULL); pthread_create(&a2dp_status_listen_thread, NULL, a2dp_status_listen, NULL); repeat: capture_handle = NULL; write_handle = NULL; err = 0; memset(buffer, 0, sizeof(buffer)); memset((char *)silence_data, 0, sizeof(silence_data)); sampleRate = SAMPLE_RATE; channels = CHANNEL; mute_frame_thd = (int)(SAMPLE_RATE * mute_time / g_read_frame); mute_frame = 0; /* LINE_OUT is the default output device */ device_flag = DEVICE_FLAG_LINE_OUT; new_flag = DEVICE_FLAG_LINE_OUT; last_flag = DEVICE_FLAG_LINE_OUT; eq_debug("\n==========EQ/DRC process release version %s==============\n", EQ_DRC_PROCESS_VERSION); eq_debug("==========KEEPING_HW_CARD: %d===============\n", KEEPING_HW_CARD); eq_debug("===== g_read_frame:%d g_period_size:%d g_period_counts:%d g_buffer_size:%d =====\n", g_read_frame, g_period_size, g_period_counts, g_buffer_size); #if KEEPING_HW_CARD eq_debug("===== g_path_name: %s =====\n", g_path_name); #endif alsa_fake_device_record_open(&capture_handle, channels, sampleRate); #if KEEPING_HW_CARD if (write_handle_bak != NULL) { snd_pcm_close(write_handle_bak); eq_info("[EQ] resume process and release last write_handle_bak: 0x%x\n", write_handle_bak); write_handle_bak = NULL; } else { eq_info("[EQ] run process first\n"); } #endif device_flag = get_device_flag(); err = alsa_fake_device_write_open(&write_handle, channels, sampleRate, device_flag, &socket_fd); if (err < 0) { eq_err("LINE: %d, first open playback device failed, and repeat\n", __LINE__); // return -1; goto repeat; } else { eq_info("LINE: %d, open write_handle: 0x%x\n", __LINE__, write_handle); } #if KEEPING_HW_CARD /* Avoid start with plugged phones and crashed during close sound card. */ if (device_flag == DEVICE_FLAG_LINE_OUT) write_handle_bak = write_handle; eq_info("[EQ] line: %d init write_handle: 0x%x | 0x%x, device_flag: %d | %d\n", __LINE__, write_handle, write_handle_bak, device_flag, last_flag); #endif // RK_acquire_wake_lock(wake_lock); while (1) { // startProcTime = clock(); err = snd_pcm_readi(capture_handle, buffer, g_read_frame); // endProcTime = clock(); // printf("snd_pcm_readi cost_time: %ld us\n", endProcTime - startProcTime); if (err != g_read_frame) { if (err == -ESTRPIPE) { eq_err("====[EQ] LINE: %d system suspend and resumed\n", __LINE__); } else { eq_err("====[EQ] LINE: %d read frame error = %d, not %d\n", __LINE__, err, g_read_frame); } } if (err < 0) { if (err == -EPIPE) eq_err("[EQ] Overrun occurred: %d\n", err); err = snd_pcm_recover(capture_handle, err, 0); // Still an error, need to exit. if (err < 0) { eq_err("[EQ] Error occured while recording: %s, goto repeat\n", snd_strerror(err)); // usleep(200 * 1000); if (capture_handle) snd_pcm_close(capture_handle); goto repeat; } } if (g_system_sleep) mute_frame = mute_frame_thd; else if(low_power_mode && is_mute_frame((short *)buffer, channels * g_read_frame)) mute_frame ++; else mute_frame = 0; if (device_flag == DEVICE_FLAG_BLUETOOTH_BSA) { if ((g_bt_is_connect == BT_DISCONNECT) && (socket_fd >= 0)) { eq_debug("[EQ] bsa bt source disconnect, teardown client socket\n"); RK_socket_client_teardown(socket_fd); socket_fd = -1; } } // eq_info("[EQ] user_play_state=%d\n", user_play_state); if(mute_frame >= mute_frame_thd) { // eq_info("[EQ] g_system_sleep=%d, power_state=%d\n", g_system_sleep, power_state); //usleep(30*1000); /* Reassign to avoid overflow */ // memset(buffer, 0, sizeof(buffer)); mute_frame = mute_frame_thd; if (write_handle) { #if 1 // fade-out int64_t start = 0; int fade_type = FADE_OUT; int nb_samples = g_read_frame * g_period_counts; int buf_bytes = g_read_frame * channels * sizeof(short) * g_period_counts; short *fade_buf, *src_buf; int curve_type = IQSIN; fade_buf = (short *)calloc(buf_bytes, 1); if (!fade_buf) { eq_err("[EQ] alloc fade_buf failed\n"); return -1; } src_buf = (short *)calloc(buf_bytes, 1); if (!src_buf) { eq_err("[EQ] alloc src_buf failed\n"); return -1; } memcpy((void *)(src_buf), (void *)(buffer), buf_bytes); eq_info("[EQ] USER_PLAY_CLOSED and fade out\n"); fade_samples_s16((uint8_t **)(&fade_buf), (uint8_t **)(&src_buf), nb_samples, channels, fade_type ? -1 : 1, start, nb_samples, curve_type); memcpy((void *)(buffer), (void *)(fade_buf), buf_bytes); if (src_buf) free(src_buf); if (fade_buf) free(fade_buf); #endif #if KEEPING_HW_CARD if (device_flag == DEVICE_FLAG_LINE_OUT) { system("amixer sset 'Playback Path' OFF"); eq_info("[EQ] disable Playback path and PA\n"); write_handle = NULL; } else { snd_pcm_close(write_handle); eq_info("[EQ]: %d Close sound card\n", __LINE__); write_handle = NULL; } #else snd_pcm_close(write_handle); write_handle = NULL; #endif // RK_release_wake_lock(wake_lock); if (power_state == POWER_STATE_SUSPENDING) { eq_err("[EQ] suspend and close write handle for you right now!\n"); power_state = POWER_STATE_SUSPEND; } else { eq_err("[EQ] %d second no playback, close write handle for you now!\n ", mute_time); } user_play_state = USER_PLAY_CLOSED; } #if KEEPING_HW_CARD // if (write_handle == NULL) { // snd_pcm_forward(write_handle_bak, g_read_frame); // eq_info("[EQ] forward %d frames\n", g_read_frame); // } #endif continue; } last_flag = device_flag; new_flag = get_device_flag(); if (new_flag != device_flag) { eq_debug("\n[EQ] Device route changed, from\"%s\" to \"%s\"\n\n", get_device_name(device_flag), get_device_name(new_flag)); device_flag = new_flag; #if KEEPING_HW_CARD if (device_flag == DEVICE_FLAG_LINE_OUT) { if (g_fast_codec == true && last_flag == DEVICE_FLAG_LINE_OUT && last_flag == device_flag) { eq_info("[EQ]: %d Do nothing, write_handle: 0x%x | 0x%x\n", __LINE__, device_flag, write_handle, write_handle_bak); } else { eq_info("[EQ]: %d Close card LINE_OUT from %d, write_handle: 0x%x | 0x%x, g_fast_codec:%d\n", __LINE__, last_flag, write_handle, write_handle_bak, g_fast_codec); if (write_handle) { snd_pcm_close(write_handle); eq_info("[EQ]: %d Close sound card\n", __LINE__); write_handle = NULL; write_handle_bak = NULL; g_fast_codec = false; } } } else { eq_info("[EQ]: %d Close card, %d, write_handle: 0x%x | 0x%x, g_fast_codec:%d\n", __LINE__, device_flag, write_handle, write_handle_bak, g_fast_codec); if (write_handle == write_handle_bak) { if (write_handle) { snd_pcm_close(write_handle); eq_info("[EQ]: %d Close sound card\n", __LINE__); write_handle = NULL; write_handle_bak = NULL; g_fast_codec = false; } } else { if (write_handle_bak) { snd_pcm_close(write_handle_bak); eq_info("[EQ]: %d Close write_handle_bak card\n", __LINE__); write_handle_bak = NULL; } if (write_handle) { snd_pcm_close(write_handle); eq_info("[EQ]: %d Close write_handle card\n", __LINE__); write_handle = NULL; } g_fast_codec = false; } } #else if (write_handle) { snd_pcm_close(write_handle); write_handle = NULL; } #endif } // eq_info("[EQ] device_flag: %d, %s, write_handle: 0x%x\n", // device_flag, get_device_name(device_flag), write_handle); while (write_handle == NULL && socket_fd < 0) { // RK_acquire_wake_lock(wake_lock); eq_info("[EQ] device_flag: %d, %s, write_handle: 0x%x, socket_fd: %d\n", device_flag, get_device_name(device_flag), write_handle, socket_fd); #if KEEPING_HW_CARD if (device_flag == DEVICE_FLAG_LINE_OUT) { if (g_fast_codec == true && last_flag == DEVICE_FLAG_LINE_OUT && write_handle_bak > 0) { char cmd_str[64] = { 0 }; write_handle = write_handle_bak; sprintf(cmd_str, "amixer sset 'Playback Path' %s", g_path_name); system(cmd_str); eq_info("[EQ] enable Playback path and PA, write_handle: 0x%x\n", write_handle); // snd_pcm_forward(write_handle, g_read_frame); // continue; } else { eq_info("EQ]: %d if switch device_flag: %d | %d and open start, write_handle: 0x%x | 0x%x g_fast_codec: %d\n", __LINE__, device_flag, last_flag, write_handle, write_handle_bak, g_fast_codec); err = alsa_fake_device_write_open(&write_handle, channels, sampleRate, device_flag, &socket_fd); if (err < 0 || (write_handle == NULL && socket_fd < 0)) { eq_err("[EQ] line:%d Route change failed! Using default audio path.\n", __LINE__); // last_flag = DEVICE_FLAG_LINE_OUT; g_bt_is_connect = BT_DISCONNECT; continue; } else { eq_info("LINE: %d, open write_handle: 0x%x\n", __LINE__, write_handle); } write_handle_bak = write_handle; } } else { static int overflow = 0; eq_info("EQ]: %d else switch device_flag: %d | %d and open start, write_handle: 0x%x | 0x%x g_fast_codec: %d\n", __LINE__, device_flag, last_flag, write_handle, write_handle_bak, g_fast_codec); // if (write_handle_bak) { // snd_pcm_close(write_handle_bak); // write_handle_bak = NULL; // g_fast_codec = false; // eq_info("EQ]: %d close g_fast_codec\n", __LINE__); // } err = alsa_fake_device_write_open(&write_handle, channels, sampleRate, device_flag, &socket_fd); if (err < 0 || (write_handle == NULL && socket_fd < 0)) { eq_err("[EQ] line:%d Route change failed!\n", __LINE__); // device_flag = DEVICE_FLAG_LINE_OUT; if (device_flag == DEVICE_FLAG_DIGITAL_HP) { /* Maybe need to more prepare some time for digital headphone */ usleep(200 * 1000); if (overflow++ >= 12) { /* about 3s */ eq_err("[EQ] line:%d Using default audio path: %d, overflow: %d\n", __LINE__, DEVICE_FLAG_LINE_OUT, overflow); last_flag = DEVICE_FLAG_DIGITAL_HP; device_flag = DEVICE_FLAG_LINE_OUT; overflow = 0; } // need_close_card = true; } else { eq_err("[EQ] line:%d device_flag will: %d to %d\n", __LINE__, last_flag, DEVICE_FLAG_LINE_OUT); if (device_flag == DEVICE_FLAG_BLUETOOTH || device_flag == DEVICE_FLAG_BLUETOOTH_BSA) { g_bt_is_connect = BT_DISCONNECT; } last_flag = device_flag; device_flag = DEVICE_FLAG_LINE_OUT; } // else if (device_flag == DEVICE_FLAG_ANALOG_HP) { // last_flag = DEVICE_FLAG_ANALOG_HP; // device_flag = DEVICE_FLAG_LINE_OUT; // need_close_card = true; // } // g_bt_is_connect = BT_DISCONNECT; continue; } else { eq_err("[EQ] line:%d Clean overflow:%d, write_handle: 0x%x\n", __LINE__, overflow, write_handle); overflow = 0; } if (write_handle_bak) { snd_pcm_close(write_handle_bak); write_handle_bak = NULL; g_fast_codec = false; eq_info("EQ]: %d close g_fast_codec\n", __LINE__); } } #else err = alsa_fake_device_write_open(&write_handle, channels, sampleRate, device_flag, &socket_fd); if (err < 0 || (write_handle == NULL && socket_fd < 0)) { eq_err("[EQ] Route change failed! Using default audio path.\n"); device_flag = DEVICE_FLAG_LINE_OUT; g_bt_is_connect = BT_DISCONNECT; } #endif skip_frame = 0; // memset(buffer, 0xff, sizeof(buffer)); // if (capture_handle) // snd_pcm_close(capture_handle); // alsa_fake_device_record_open(&capture_handle, channels, sampleRate); if (0 && low_power_mode) { int i, num = 4; eq_debug("[EQ] feed mute data %d frame\n", num); for (i = 0; i < num; i++) { if(write_handle != NULL) { err = snd_pcm_writei(write_handle, silence_data, g_read_frame); if(err != g_read_frame) eq_err("====[EQ] %d, write frame error = %d, not %d\n", __LINE__, err, g_read_frame); } else if (socket_fd >= 0) { err = RK_socket_send(socket_fd, silence_data, g_read_frame * 4); //2ch 16bit if(err != (g_read_frame * 4)) eq_err("====[EQ] %d, write frame error = %d, not %d\n", __LINE__, err, g_read_frame * 4); } } } } if(write_handle != NULL) { #if 0 if (skip_frame > 0) { int err; err = snd_pcm_writei(write_handle, silence_data, g_read_frame); if(err != g_read_frame) eq_err("====[EQ] %d, write frame error = %d, not %d\n", __LINE__, err, g_read_frame); eq_err("skip_frame = %d\n", skip_frame); skip_frame--; continue; } #endif //usleep(30*1000); err = snd_pcm_writei(write_handle, buffer, g_read_frame); if(err != g_read_frame) { eq_err("====[EQ] %d, write frame error = %d, not %d\n", __LINE__, err, g_read_frame); // if (err > 0) { // snd_pcm_sframes_t frames = g_read_frame - err; // startProcTime = clock(); // frames = snd_pcm_forward(write_handle, frames); // endProcTime = clock(); // printf("snd_pcm_forward cost_time: %ld us\n", endProcTime - startProcTime); // eq_err("[EQ] snd_pcm_forward frames: %d\n", frames); // } } if (err < 0) { if (err == -EPIPE) { eq_err("[EQ] Underrun occurred from write: %d\n", err); #if 1 err = snd_pcm_recover(write_handle, err, 0); if (err < 0) { eq_err( "[EQ] Error occured while writing: %s\n", snd_strerror(err)); // usleep(200 * 1000); #if KEEPING_HW_CARD /* Do nothing */ #else if (write_handle) { snd_pcm_close(write_handle); write_handle = NULL; } #endif if (device_flag == DEVICE_FLAG_BLUETOOTH) g_bt_is_connect = BT_DISCONNECT; } #else eq_drc_xrun(write_handle, SND_PCM_STREAM_PLAYBACK); #endif } #if KEEPING_HW_CARD else if (err == -EBADFD) { int err; eq_err("====[EQ] %d, EBADFD and re-open sound, device_flag: %d write_handle: 0x%x | 0x%x\n", __LINE__, device_flag, write_handle, write_handle_bak); if (write_handle) { snd_pcm_close(write_handle); write_handle = NULL; write_handle_bak = NULL; g_fast_codec = false; } err = alsa_fake_device_write_open(&write_handle, channels, sampleRate, device_flag, &socket_fd); if (err < 0) { // eq_err("LINE: %d, open playback device failed, exit eq\n", __LINE__); eq_err("LINE: %d, open playback device failed, continue\n", __LINE__); // write_handle_bak = write_handle; // return -1; continue; } else { eq_info("LINE: %d, open write_handle: 0x%x\n", __LINE__, write_handle); } write_handle_bak = write_handle; } #endif } }else if (socket_fd >= 0) { if (g_bt_is_connect == BT_CONNECT_BSA) { err = RK_socket_send(socket_fd, (char *)buffer, g_read_frame * 4); if (err != g_read_frame * 4 && -EAGAIN != err) eq_err("====[EQ] %d, write frame error = %d, not %d\n", __LINE__, err, g_read_frame * 4); if (err < 0 && -EAGAIN != err) { if (socket_fd >= 0) { eq_err("[EQ] socket send err: %d, teardown client socket\n", err); RK_socket_client_teardown(socket_fd); socket_fd = -1; } g_bt_is_connect = BT_DISCONNECT; } } else { if(socket_fd >= 0){ eq_debug("[EQ] bsa bt source disconnect, teardown client socket\n"); RK_socket_client_teardown(socket_fd); socket_fd = -1; } } } } error: eq_debug("=== [EQ] Exit eq ===\n"); if (silence_data) { free(silence_data); silence_data = NULL; } if (buffer) { free(buffer); buffer = NULL; } g_upi.stop = 1; if (capture_handle) snd_pcm_close(capture_handle); if (write_handle) snd_pcm_close(write_handle); if (socket_fd >= 0) RK_socket_client_teardown(socket_fd); pthread_cancel(a2dp_status_listen_thread); pthread_join(a2dp_status_listen_thread, NULL); return 0; }