/* =========================================================================

DESCRIPTION
   Netease && Xunfei speech processing main program

Copyright (c) 2017 by Netease, Co,LTD.  All Rights Reserved.
============================================================================ */

/* =========================================================================

                             REVISION

when                       who why
--------                    ---------
-------------------------------------------
2017/06/28          wangzijiao                                  Created.
============================================================================ */

/* ------------------------------------------------------------------------
** Includes
** ------------------------------------------------------------------------ */
#define _GNU_SOURCE
#include <errno.h>
#include <fcntl.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>

#include "base.h"
#include "formats.h"
#include "record.h"

#include "error.h"
#include <cae/cae_intf.h>
#include <pthread.h>
#include <sched.h>
#include <stdint.h>
#include <sys/stat.h>

/* ------------------------------------------------------------------------
** Macros
** ------------------------------------------------------------------------ */
#define ALSA_PCM_NEW_HW_PARAMS_API
#define AUDIO_QUEUE_BUFF_LEN (1024 * 10)

#define QUEUE_BUFF_MULTIPLE 100

/* ------------------------------------------------------------------------
** Types
** ------------------------------------------------------------------------ *

/* ------------------------------------------------------------------------
** Global Variable Definitions
** ------------------------------------------------------------------------ */
static char *out_pcm_name;

static Proc_CAENew api_cae_new;
static Proc_CAEAudioWrite api_cae_audio_write;
static Proc_CAEResetEng api_cae_reset_eng;
static Proc_CAESetRealBeam api_cae_set_real_beam;
static Proc_CAESetWParam api_cae_set_wparam;
static Proc_CAEGetWParam api_cae_get_wparam;
static Proc_CAEGetVersion api_cae_get_version;
static Proc_CAEGetChannel api_cae_get_channel;
static Proc_CAESetShowLog api_cae_set_show_log;
static Proc_CAEDestroy api_cae_destroy;

static off64_t fdcount = 0;
static snd_output_t *sndlog;

/* ------------------------------------------------------------------------
** Function Definitions
** ------------------------------------------------------------------------ */
static void *QueueReadThread(void *param);
static void *RecordThread(void *param);
static ssize_t xwrite(int fd, const void *buf, size_t count);
static void record_audio_cb(const void *audio, unsigned int audio_len,
                            int err_code);
void _record_stop(void *record_hd);
static void end_wave(int fd);
static void begin_wave(RecordData *record, int fd, size_t cnt);
void _record_stop(void *record_hd);
static int init_params(struct pcm_config *pcm_cfg, int init_queue);

/*
 * make sure we write all bytes or return an error
 */
static ssize_t xwrite(int fd, const void *buf, size_t count) {
    ssize_t written;
    size_t offset = 0;

    while (offset < count) {
        written = write(fd, buf + offset, count - offset);
        if (written <= 0)
            return written;

        offset += written;
    };

    return offset;
}

static void *QueueReadThread(void *param) {
    RecordData *record = (RecordData *)param;
    int readLen = 0;
    printf("QueueReadThread record :%p\n", record);
    while (record->runing) {
        char *data_buff = NULL;
        readLen = queue_read(record->queue, &data_buff);
        if (0 == readLen) {
            // printf("queue_read readLen = 0\n");
            // usleep(16000);
            continue;
        }
        if (record->buff_size != readLen) {
            // printf("\nqueue_read readLen %d\n", readLen);
        }
        if (record->cb)
            record->cb(data_buff, readLen, NETEASE_SUCCESS);
        free(data_buff);
    }
    printf("QueueReadThread end, running:%d \n", record->runing);
    return NULL;
}

static void do_test_position(RecordData *record) {
    static long counter = 0;
    static time_t tmr = -1;
    time_t now;
    static float availsum, delaysum, samples;
    static snd_pcm_sframes_t maxavail, maxdelay;
    static snd_pcm_sframes_t minavail, mindelay;
    static snd_pcm_sframes_t badavail = 0, baddelay = 0;
    snd_pcm_sframes_t outofrange;
    snd_pcm_sframes_t avail, delay;
    int err;
    int test_coef = 8;

    err = snd_pcm_avail_delay(record->handle, &avail, &delay);
    if (err < 0)
        return;
    outofrange = (test_coef * (snd_pcm_sframes_t)record->frames) / 2;
    if (avail > outofrange || avail < -outofrange || delay > outofrange ||
        delay < -outofrange) {
        badavail = avail;
        baddelay = delay;
        availsum = delaysum = samples = 0;
        maxavail = maxdelay = 0;
        minavail = mindelay = record->frames * 16;
        fprintf(stderr,
                "Suspicious buffer position (%li total): "
                "avail = %li, delay = %li, buffer = %li\n",
                ++counter, (long)avail, (long)delay, (long)record->frames);
    } else {
        time(&now);
        if (tmr == (time_t)-1) {
            tmr = now;
            availsum = delaysum = samples = 0;
            maxavail = maxdelay = 0;
            minavail = mindelay = record->frames * 16;
        }
        if (avail > maxavail)
            maxavail = avail;
        if (delay > maxdelay)
            maxdelay = delay;
        if (avail < minavail)
            minavail = avail;
        if (delay < mindelay)
            mindelay = delay;
        availsum += avail;
        delaysum += delay;
        samples++;
        if (avail != 0 && now != tmr) {
            fprintf(stderr,
                    "BUFPOS: avg%li/%li "
                    "min%li/%li max%li/%li (%li) (%li:%li/%li)\n",
                    (long)(availsum / samples), (long)(delaysum / samples),
                    (long)minavail, (long)mindelay, (long)maxavail,
                    (long)maxdelay, (long)record->frames, counter, badavail,
                    baddelay);
            tmr = now;
        }
    }
}

static ssize_t pcm_read(RecordData *record) {
    ssize_t r;
    size_t result = 0;
    size_t rcount = record->chunk_size;
    size_t count = rcount;
    char *data = record->buffer;

    while (count > 0 && !!record->runing) {
        r = snd_pcm_readi(record->handle, data, count);
        if (r == -EAGAIN || (r >= 0 && (size_t)r < count)) {
            n_debug("read data == EAGAIN\n");
            snd_pcm_wait(record->handle, 50);
        } else if (r == -EPIPE) {
            /* EPIPE means overrun */
            fprintf(stderr, "overrun occurred/n");
            LOG_EX(LOG_Error, "ALSA Read Data = -EPIPE\n");
            snd_pcm_prepare(record->handle);
            continue;
        } else if (r < 0) {
            fprintf(stderr, "error from read: %s/n", snd_strerror(r));
            LOG_EX(LOG_Error, "ALSA Read Data Error: %s(%d)\n", snd_strerror(r),
                   r);
            return 0;
        }
        if (r > 0) {
            result += r;
            count -= r;
            data += r * record->bits_per_frame / 8;
        }
    }

    return result;
}
static void end_wave(int fd) { /* only close output */
    WaveChunkHeader cd;
    off64_t length_seek;
    off64_t filelen;
    u_int rifflen;

    length_seek =
        sizeof(WaveHeader) + sizeof(WaveChunkHeader) + sizeof(WaveFmtBody);
    cd.type = WAV_DATA;
    cd.length = fdcount > 0x7fffffff ? LE_INT(0x7fffffff) : LE_INT(fdcount);
    filelen = fdcount + 2 * sizeof(WaveChunkHeader) + sizeof(WaveFmtBody) + 4;
    rifflen = filelen > 0x7fffffff ? LE_INT(0x7fffffff) : LE_INT(filelen);
    if (lseek64(fd, 4, SEEK_SET) == 4)
        xwrite(fd, &rifflen, 4);
    if (lseek64(fd, length_seek, SEEK_SET) == length_seek)
        xwrite(fd, &cd, sizeof(WaveChunkHeader));
    if (fd != 1)
        close(fd);
}

/* write a WAVE-header */
static void begin_wave(RecordData *record, int fd, size_t cnt) {
    WaveHeader h;
    WaveFmtBody f;
    WaveChunkHeader cf, cd;
    int bits;
    u_int tmp;
    u_short tmp2;

    /* WAVE cannot handle greater than 32bit (signed?) int */
    if (cnt == (size_t)-2)
        cnt = 0x7fffff00;

    bits = 8;
    switch ((unsigned long)record->format) {
    case SND_PCM_FORMAT_U8:
        bits = 8;
        break;
    case SND_PCM_FORMAT_S16_LE:
        bits = 16;
        break;
    case SND_PCM_FORMAT_S32_LE:
    case SND_PCM_FORMAT_FLOAT_LE:
        bits = 32;
        break;
    case SND_PCM_FORMAT_S24_LE:
    case SND_PCM_FORMAT_S24_3LE:
        bits = 24;
        break;
    default:
        n_error("Wave doesn't support %s format..."),
            snd_pcm_format_name(record->format);
        return;
    }
    h.magic = WAV_RIFF;
    tmp = cnt + sizeof(WaveHeader) + sizeof(WaveChunkHeader) +
          sizeof(WaveFmtBody) + sizeof(WaveChunkHeader) - 8;
    h.length = LE_INT(tmp);
    h.type = WAV_WAVE;

    cf.type = WAV_FMT;
    cf.length = LE_INT(16);

    if (record->format == SND_PCM_FORMAT_FLOAT_LE)
        f.format = LE_SHORT(WAV_FMT_IEEE_FLOAT);
    else
        f.format = LE_SHORT(WAV_FMT_PCM);
    f.channels = LE_SHORT(record->pcm_cfg->channels);
    f.sample_fq = LE_INT(record->pcm_cfg->rate);
#if 0
	tmp2 = (samplesize == 8) ? 1 : 2;
	f.byte_p_spl = LE_SHORT(tmp2);
	tmp = dsp_speed * hwparams.channels * (u_int) tmp2;
#else
    tmp2 = record->pcm_cfg->channels *
           snd_pcm_format_physical_width(record->format) / 8;
    f.byte_p_spl = LE_SHORT(tmp2);
    tmp = (u_int)tmp2 * record->pcm_cfg->rate;
#endif
    f.byte_p_sec = LE_INT(tmp);
    f.bit_p_spl = LE_SHORT(bits);

    cd.type = WAV_DATA;
    cd.length = LE_INT(cnt);

    if (xwrite(fd, &h, sizeof(WaveHeader)) != sizeof(WaveHeader) ||
        xwrite(fd, &cf, sizeof(WaveChunkHeader)) != sizeof(WaveChunkHeader) ||
        xwrite(fd, &f, sizeof(WaveFmtBody)) != sizeof(WaveFmtBody) ||
        xwrite(fd, &cd, sizeof(WaveChunkHeader)) != sizeof(WaveChunkHeader)) {
        n_error("write error");
        return;
    }
}

static void *RecordThread(void *param) {
    RecordData *record = (RecordData *)param;
    int ret = 0;
    int i = 0;
    long long max_file_size = 2147483648LL;

    // cpu_set_t mask;
    n_toast("RecordThread record:%p, running:%d\n", record, record->runing);
    n_toast("record->pcm_cfg:%p\n", record->pcm_cfg);
    n_toast("record->pcm_cfg->adc:%p\n", record->pcm_cfg->adc);
    // CPU_ZERO(&mask);
    // CPU_SET(0,&mask);
    // ret = sched_setaffinity(0, sizeof(mask), &mask);
    // printf("sched_setaffinity return = %d\n", ret);
    if (record->backupfilefd > 0 &&
        record->pcm_cfg->backupfile_waveformat > 0) {
        begin_wave(record, record->backupfilefd, (size_t)max_file_size);
        fdcount = 0;
    }
    if (record->finalfilefd > 0 && record->pcm_cfg->backupfile_waveformat > 0) {
        begin_wave(record, record->backupfilefd, (size_t)max_file_size);
    }
    while (!!record->runing) {
        if (getWorkMode() == WorkMode_R311_PV1) {
            if (pcm_read(record->pcm_cfg->adc) ==
                record->pcm_cfg->adc->chunk_size) {
                if (pcm_read(record) == record->chunk_size) {
                    // Write adc data to dmic data, in first two channel
                    for (i = 0; i < record->chunk_size; i++) {
                        // printf("copy from %p to %p, size:%d, frame:%d\n",
                        // (record->pcm_cfg->adc->buffer +
                        //                 i *
                        //                 record->pcm_cfg->adc->bits_per_frame
                        //                 / 8), (record->buffer + i *
                        //                 record->bits_per_frame / 8), 2 *
                        //                 record->significant_bits_per_sample /
                        //                 8, i);
                        // ch:1-6 dmic
                        // ch:7-8 adc
                        memmove(
                            (void *)(record->buffer +
                                     i * record->bits_per_frame / 8),
                            (void *)(record->buffer +
                                     i * record->bits_per_frame / 8 +
                                     2 * record->significant_bits_per_sample /
                                         8),
                            6 * record->significant_bits_per_sample / 8);

                        // printf("copy to 0x%p, size:%d\n", (record->buffer +
                        //              i * record->bits_per_frame / 8 +
                        //              6 * record->significant_bits_per_sample /
                        //                  8), 2 * record->significant_bits_per_sample / 8);
                        memcpy(
                            (void *)(record->buffer +
                                     i * record->bits_per_frame / 8 +
                                     6 * record->significant_bits_per_sample /
                                         8),
                            (void *)(record->pcm_cfg->adc->buffer +
                                     i * record->pcm_cfg->adc->bits_per_frame /
                                         8),
                            record->pcm_cfg->adc->bits_per_frame / 8);
                        // memcpy((void *)(record->buffer +
                        //                 i * record->bits_per_frame / 8),
                        //        (void *)(record->pcm_cfg->adc->buffer +
                        //                 i *
                        //                 record->pcm_cfg->adc->bits_per_frame
                        //                 / 8),
                        //        2 * record->significant_bits_per_sample / 8);
                    }

                    if (record->backupfilefd > 0) {
                        xwrite(record->backupfilefd, record->buffer,
                               record->chunk_bytes);
                        fdcount += record->chunk_bytes;
                    }
                    queue_write(record->queue, record->buffer,
                                record->chunk_bytes);
                } else {
                    fprintf(stderr, "short read, read %d frames/n", ret);
                    LOG_EX(LOG_Error, "ALSA Short Read, Read %d Frames: %d\n",
                           ret);
                }
            } else {
                // read dmic data and throw
                pcm_read(record);
                printf("Read adc codec error!\n");
            }
        } else {
            printf("Unknow work mode!\n");
            exit(0);
        }
    }

    if (record->backupfilefd > 0 &&
        record->pcm_cfg->backupfile_waveformat > 0) {
        end_wave(record->backupfilefd);
        record->backupfilefd = 0;
        n_debug("Write backup file end finish!\n");
    }
    n_debug("RecordThread end, running:%d\n", record->runing);
    return NULL;
}

static void show_available_sample_formats(snd_pcm_t *handle,
                                          snd_pcm_hw_params_t *params) {
    snd_pcm_format_t format;

    fprintf(stderr, "Available formats:\n");
    for (format = 0; format <= SND_PCM_FORMAT_LAST; format++) {
        if (snd_pcm_hw_params_test_format(handle, params, format) == 0)
            fprintf(stderr, "- %s\n", snd_pcm_format_name(format));
    }
}

int Neteasae_record_start(struct pcm_config *pcm_cfg) {
    pthread_attr_t thread_attr;
    struct sched_param thread_param;
    struct pcm_config *tmp;

    printf("Begin to init netease record!\n");
    snd_output_stdio_attach(&sndlog, stderr, 0);
    switch (getWorkMode()) {
    case WorkMode_R311_PV1:
        // dmic codec
        pcm_cfg->device_name = "hw:1,0";
        pcm_cfg->channels = 8;
        pcm_cfg->rate = 16000;
        pcm_cfg->period_size = 1535;
        pcm_cfg->format = "S16_LE";

        printf("Begin to init dmic!\n");
        init_params(pcm_cfg, 1);
        tmp = (struct pcm_config *)calloc(1, sizeof(struct pcm_config));
        if (tmp == NULL) {
            printf("alloc fail!\n");
            exit(0);
        } else {
            printf("Begin to init adc codec!\n");
            // adc codec
            tmp->device_name = "hw:2,0";
            tmp->channels = 2;
            tmp->rate = 16000;
            tmp->period_size = 1535;
            tmp->format = "S16_LE";
            init_params(tmp, 0);
        }
        pcm_cfg->adc = tmp->recordata;
        pcm_cfg->recordata->adc = tmp;

        pthread_attr_init(&thread_attr);
        pthread_attr_setschedpolicy(&thread_attr, SCHED_RR);
        thread_param.sched_priority = sched_get_priority_max(SCHED_RR);
        pthread_attr_setschedparam(&thread_attr, &thread_param);

        pthread_create(&pcm_cfg->recordata->tid_pcm_read, &thread_attr,
                       RecordThread, (void *)pcm_cfg->recordata);
        pthread_create(&pcm_cfg->recordata->tid_queue_read, NULL,
                       QueueReadThread, (void *)pcm_cfg->recordata);

        break;

    default:
        printf("Unknow mode!\n");
        exit(0);
    }
}
static int init_params(struct pcm_config *pcm_cfg, int init_queue) {
    int rc;
    int size;
    int ret = NETEASE_SUCCESS;
    snd_pcm_hw_params_t *params;
    snd_pcm_sw_params_t *swparams;
    unsigned int val, val2;
    int dir;
    snd_pcm_uframes_t frames;
    char *buffer;
    struct stat statbuf;

    RecordData *record = (RecordData *)malloc(sizeof(RecordData));
    if (NULL == record) {
        printf("Malloc fail for record data!\n");
        return NETEASE_ERROR_OUT_OF_MEMORY;
    }
    memset(record, 0, sizeof(RecordData));

    pcm_cfg->recordata = record;
    record->pcm_cfg = pcm_cfg;
    record->cb = pcm_cfg->audio_cb;

    //设置录音参数参数
    rc = snd_pcm_open(&record->handle, pcm_cfg->device_name,
                      SND_PCM_STREAM_CAPTURE, 0);
    if (rc < 0) {
        n_error("unable to open pcm device: %s/n", snd_strerror(rc));
        ret = NETEASE_ERROR_OPEN_FILE;
        goto error;
    }

    record->format = snd_pcm_format_value(pcm_cfg->format);
    if (record->format == SND_PCM_FORMAT_UNKNOWN) {
        n_error("Wrong format:%s\n", record->format);
        ret = NETEASE_ERROR_FAIL;
        goto error;
    }

    n_debug("Format:%s\n", snd_pcm_format_description(record->format));
    n_debug("Rate %d Hz\n", pcm_cfg->rate);
    n_debug("Channel %d\n", pcm_cfg->channels);

    /* Allocate a hardware parameters object. */
    snd_pcm_hw_params_alloca(&params);
    snd_pcm_sw_params_alloca(&swparams);

    /* Fill it in with default values. */
    snd_pcm_hw_params_any(record->handle, params);

    /* Set the desired hardware parameters. */
    /* Interleaved mode */
    snd_pcm_hw_params_set_access(record->handle, params,
                                 SND_PCM_ACCESS_RW_INTERLEAVED);

    /* Signed 16-bit little-endian format */
    rc = snd_pcm_hw_params_set_format(record->handle, params, record->format);
    if (rc < 0) {
        n_error("Sample format non available!\n");
        show_available_sample_formats(record->handle, params);
        goto error;
    }
    /* Two channels (stereo) */
    rc = snd_pcm_hw_params_set_channels(record->handle, params,
                                        pcm_cfg->channels);
    if (rc < 0) {
        n_error("Channel count(%d) not available!\n", pcm_cfg->channels);
        goto error;
    }
    /* 44100 bits/second sampling rate (CD quality) */
    val = pcm_cfg->rate;
    snd_pcm_hw_params_set_rate_near(record->handle, params, &val, 0);

    /* Set period size to  frames. */
    // frames = pcm_cfg->period_size;
    // snd_pcm_hw_params_set_period_size_near(record->handle, params, &frames,
    //                                        0);
    snd_pcm_hw_params_get_buffer_time_max(params, &record->buffer_time, 0);
    if (record->buffer_time > 500000) {
        record->buffer_time = 500000;
    }
    record->period_time = record->buffer_time / 4;
    n_debug("Buffer time:%d, period time:%d\n", record->buffer_time,
            record->period_time);
    snd_pcm_hw_params_set_period_time_near(record->handle, params,
                                           &record->period_time, 0);
    snd_pcm_hw_params_set_buffer_time_near(record->handle, params,
                                           &record->buffer_time, 0);

    snd_pcm_hw_params_get_period_size(params, &record->chunk_size, 0);
    record->frames = record->chunk_size;
    snd_pcm_hw_params_get_buffer_size(params, &record->buff_size);

    n_debug("Buffer size:%d, period size:%d\n", record->buff_size,
            record->chunk_size);

    /* Write the parameters to the driver */
    rc = snd_pcm_hw_params(record->handle, params);
    if (rc < 0) {
        n_error("unable to set hw parameters: %s/n", snd_strerror(rc));
        // snd_pcm_hw_dump(params, sndlog);
        ret = NETEASE_ERROR_FAIL;
        goto error;
    }
    // snd_pcm_hw_params_dump(params, stdout);
    snd_pcm_dump(record->handle, sndlog);
    /* Use a buffer large enough to hold one period */
    record->bits_per_sample = snd_pcm_format_physical_width(record->format);
    record->significant_bits_per_sample = snd_pcm_format_width(record->format);
    record->bits_per_frame = record->bits_per_sample * pcm_cfg->channels;
    record->chunk_bytes = record->chunk_size * record->bits_per_frame / 8;

    n_debug("bits_per_frame=%d, channel=%d, chunk_bytes=%d\n",
            record->bits_per_frame, pcm_cfg->channels, record->chunk_bytes);
    n_debug(
        "frames=%ld,record->buff_size=%d, quene multiple:%d, quene size:%d\n",
        record->chunk_size, record->buff_size, QUEUE_BUFF_MULTIPLE,
        QUEUE_BUFF_MULTIPLE * record->buff_size);
    record->buffer = (char *)malloc(record->chunk_bytes);
    if (NULL == record->buffer) {
        ret = NETEASE_ERROR_OUT_OF_MEMORY;
        goto error;
    }

    if (init_queue > 0) {
        record->queue_buff = (char *)malloc(
            sizeof(audio_queue_t) + record->chunk_bytes * QUEUE_BUFF_MULTIPLE + 1);
        if (NULL == record->queue_buff) {
            ret = NETEASE_ERROR_OUT_OF_MEMORY;
            goto error;
        }
        record->queue = queue_init(record->queue_buff,
                                record->chunk_bytes * QUEUE_BUFF_MULTIPLE + 1);
    } else {
        record->queue_buff = NULL;
        record->queue = NULL;
    }

    snd_pcm_sw_params_current(record->handle, swparams);
    snd_pcm_sw_params_set_avail_min(record->handle, swparams,
                                    record->chunk_size);
    snd_pcm_sw_params_set_start_threshold(record->handle, swparams, 1);
    snd_pcm_sw_params_set_stop_threshold(record->handle, swparams, 8000);

    if (snd_pcm_sw_params(record->handle, swparams) < 0) {
        n_error("unable to install sw params:");
        snd_pcm_sw_params_dump(swparams, stdout);
        goto error;
    }

    record->runing = 1;

    if (pcm_cfg->backupfilename != NULL) {
        if (!lstat(pcm_cfg->backupfilename, &statbuf)) {
            if (S_ISREG(statbuf.st_mode))
                remove(pcm_cfg->backupfilename);
            n_debug("Remove file:%s\n", pcm_cfg->backupfilename);
        }
        record->backupfilefd =
            open(pcm_cfg->backupfilename, O_TRUNC | O_WRONLY | O_CREAT, 06444);
        n_debug("Create backup file:%s, fd:%d\n", pcm_cfg->backupfilename,
                record->backupfilefd);
    }

    if (pcm_cfg->finalfilename != NULL) {
        if (!lstat(pcm_cfg->finalfilename, &statbuf)) {
            if (S_ISREG(statbuf.st_mode))
                remove(pcm_cfg->finalfilename);
            n_debug("Remove file:%s\n", pcm_cfg->finalfilename);
        }
        record->finalfilefd =
            open(pcm_cfg->finalfilename, O_TRUNC | O_WRONLY | O_CREAT, 06444);
        n_debug("Create final file:%s, fd:%d\n", pcm_cfg->finalfilename,
                record->finalfilefd);
    }

    n_toast("Record_start record :%p\n", record);

    goto exit;

error:
    n_debug("record start error %d\n", ret);
    _record_stop(record);

exit:
    return ret;
}

void _record_stop(void *record_hd) {
    RecordData *record = (RecordData *)record_hd;
    printf("\nrecord_stop in record:%p\n\n", record);
    if (NULL != record) {
        record->runing = 0;
        if (NULL != record->tid_pcm_read)
            pthread_join(record->tid_pcm_read, NULL);
        if (NULL != record->tid_queue_read)
            pthread_join(record->tid_queue_read, NULL);

        if (NULL != record->handle) {
            snd_pcm_drain(record->handle);
            snd_pcm_close(record->handle);
        }

        if (NULL != record->queue) {
            queue_destroy(record->queue);
        }

        if (NULL != record->queue_buff) {
            free(record->queue_buff);
        }

        if (NULL != record->buffer) {
            free(record->buffer);
        }

        free(record);
    }
    printf("\nrecord_stop out\n");
}

void Netease_record_stop(struct pcm_config *config) {
    if (config != NULL && config->recordata != NULL) {
        config->recordata->runing = 0;
    }
}

void device_list(void) {
    snd_ctl_t *handle;
    int card, err, dev, idx;
    snd_ctl_card_info_t *info;
    snd_pcm_info_t *pcminfo;
    snd_pcm_stream_t stream = SND_PCM_STREAM_CAPTURE;

    snd_ctl_card_info_alloca(&info);
    snd_pcm_info_alloca(&pcminfo);

    card = -1;
    if (snd_card_next(&card) < 0 || card < 0) {
        n_error("no soundcards found...");
        return;
    }
    printf("**** List of %s Hardware Devices ****\n",
           snd_pcm_stream_name(stream));
    while (card >= 0) {
        char name[32];
        sprintf(name, "hw:%d", card);
        if ((err = snd_ctl_open(&handle, name, 0)) < 0) {
            n_error("control open (%i): %s", card, snd_strerror(err));
            goto next_card;
        }
        if ((err = snd_ctl_card_info(handle, info)) < 0) {
            n_error("control hardware info (%i): %s", card, snd_strerror(err));
            snd_ctl_close(handle);
            goto next_card;
        }
        dev = -1;
        while (1) {
            unsigned int count;
            if (snd_ctl_pcm_next_device(handle, &dev) < 0)
                n_error("snd_ctl_pcm_next_device");
            if (dev < 0)
                break;
            snd_pcm_info_set_device(pcminfo, dev);
            snd_pcm_info_set_subdevice(pcminfo, 0);
            snd_pcm_info_set_stream(pcminfo, stream);
            if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
                if (err != -ENOENT)
                    n_error("control digital audio info (%i): %s", card,
                            snd_strerror(err));
                continue;
            }
            printf(("card %i: %s [%s], device %i: %s [%s]\n"), card,
                   snd_ctl_card_info_get_id(info),
                   snd_ctl_card_info_get_name(info), dev,
                   snd_pcm_info_get_id(pcminfo),
                   snd_pcm_info_get_name(pcminfo));
            count = snd_pcm_info_get_subdevices_count(pcminfo);
            printf(("  Subdevices: %i/%i\n"),
                   snd_pcm_info_get_subdevices_avail(pcminfo), count);
            for (idx = 0; idx < (int)count; idx++) {
                snd_pcm_info_set_subdevice(pcminfo, idx);
                if ((err = snd_ctl_pcm_info(handle, pcminfo)) < 0) {
                    n_error("control digital audio playback info (%i): %s",
                            card, snd_strerror(err));
                } else {
                    printf(("  Subdevice #%i: %s\n"), idx,
                           snd_pcm_info_get_subdevice_name(pcminfo));
                }
            }
        }
        snd_ctl_close(handle);
    next_card:
        if (snd_card_next(&card) < 0) {
            n_error("snd_card_next");
            break;
        }
    }
}

void pcm_list(void) {
    void **hints, **n;
    char *name, *descr, *descr1, *io;
    const char *filter;

    if (snd_device_name_hint(-1, "pcm", &hints) < 0)
        return;
    n = hints;
    // filter = stream == SND_PCM_STREAM_CAPTURE ? "Input" : "Output";
    filter = "Input";
    while (*n != NULL) {
        name = snd_device_name_get_hint(*n, "NAME");
        descr = snd_device_name_get_hint(*n, "DESC");
        io = snd_device_name_get_hint(*n, "IOID");
        if (io != NULL && strcmp(io, filter) != 0)
            goto __end;
        printf("%s\n", name);
        if ((descr1 = descr) != NULL) {
            printf("    ");
            while (*descr1) {
                if (*descr1 == '\n')
                    printf("\n    ");
                else
                    putchar(*descr1);
                descr1++;
            }
            putchar('\n');
        }
    __end:
        if (name != NULL)
            free(name);
        if (descr != NULL)
            free(descr);
        if (io != NULL)
            free(io);
        n++;
    }
    snd_device_name_free_hint(hints);
}