#include <stdio.h>
|
#include <stdlib.h>
|
#include <iostream>
|
#include <vector>
|
#include <errno.h>
|
#include <alsa/asoundlib.h>
|
#include <math.h>
|
#include <signal.h>
|
#include <sys/epoll.h>
|
#include <sys/inotify.h>
|
#include <sys/time.h>
|
#include <sys/socket.h>
|
#include <sys/un.h>
|
#include <getopt.h>
|
|
#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;
|
}
|