/* * Copyright (c) CompanyNameMagicTag 2021-2021. All rights reserved. * Description: sample audio utils * Author: audio * Create: 2021-07-27 */ #include #include #include #include #include "securec.h" #include "sample_audio_api.h" #include "sample_audio_utils.h" #ifndef ftell #define ftell #endif #ifndef ftell #define ftell #endif td_char *strcasestr(const td_char *, const td_char *); static const td_char *g_samplerate_signs[] = { "hz", "khz", "k_", "k.", }; static const td_char *g_channel_signs[] = { "channel", "ch_", "ch.", }; static const td_char *g_bit_signs[] = { "bit", }; static const td_char *find_sign_pos_in_line(const td_char *line, const td_char *sign) { td_char *sign_pos = strcasestr(line, sign); td_char *num_pos = TD_NULL; if (sign_pos == TD_NULL || sign_pos == line) { return TD_NULL; } num_pos = sign_pos - 1; while (num_pos >= line && (isdigit((td_s32)(*num_pos)) != 0 || *num_pos == '.')) { num_pos--; } num_pos++; return num_pos; } static double parse_sign_float_value(const td_char *line, const td_char *sign) { const td_char *num_pos = find_sign_pos_in_line(line, sign); if (num_pos == TD_NULL) { return 0.0; } return strtod(num_pos, TD_NULL); } static td_s32 parse_sign_int_value(const td_char *line, const td_char *sign) { const td_char *num_pos = find_sign_pos_in_line(line, sign); if (num_pos == TD_NULL) { return -1; } return (td_s32)strtol(num_pos, TD_NULL, 0); } static td_s32 parse_samplerate(const td_char *line) { const size_t sign_num = sizeof(g_samplerate_signs) / sizeof(g_samplerate_signs[0]); td_u32 i; td_s32 samplerate; double vf; for (i = 0; i < sign_num; i++) { vf = parse_sign_float_value(line, g_samplerate_signs[i]); if (strcasestr(g_samplerate_signs[i], "k") != TD_NULL) { vf = vf * 1000; /* 1000 kHz to Hz */ } samplerate = (td_s32)vf; if (samplerate > 0) { return samplerate; } } return -1; } static td_s32 parse_sign(const td_char *line, const td_char **signs, td_u32 sign_num) { td_u32 i; td_s32 v; for (i = 0; i < sign_num; i++) { v = parse_sign_int_value(line, signs[i]); if (v > 0) { return v; } } return -1; } td_void parse_audio_params_from_file(const td_char *file, uapi_audio_pcm_format *pcm_format) { td_s32 channels; td_s32 samplerate; td_s32 bit_depth; if (file == TD_NULL || pcm_format == TD_NULL) { return; } channels = parse_sign(file, g_channel_signs, sizeof(g_channel_signs) / sizeof(g_channel_signs[0])); samplerate = parse_samplerate(file); bit_depth = parse_sign(file, g_bit_signs, sizeof(g_bit_signs) / sizeof(g_bit_signs[0])); sap_printf("parsed from file = '%s': channels = %d, samplerate = %d, bit_depth = %d\n", file, channels, samplerate, bit_depth); if (channels <= 0 || samplerate <= 0 || bit_depth <= 0) { return; } pcm_format->sample_rate = (td_u32)samplerate; pcm_format->bit_depth = (td_u32)bit_depth; pcm_format->channels = (td_u32)channels; } #define MAX_PATH_LEN 128 FILE *sample_audio_open_input_stream(const td_char *stream) { td_u32 i; td_s32 ret; FILE *file = TD_NULL; const td_char *path_prefix[] = { "/user/", "/user/test/", "/user/stream/", "/music/", "/music/test/", "/music/stream/", }; td_char path[MAX_PATH_LEN]; file = fopen(stream, "rb"); if (file != TD_NULL) { return file; } for (i = 0; i < sizeof(path_prefix) / sizeof(path_prefix[0]); i++) { ret = snprintf_s(path, sizeof(path) - 1, sizeof(path) - 1, "%s%s", path_prefix[i], stream); if (ret < 0) { sap_err_log_fun(snprintf_s, ret); return TD_NULL; } file = fopen(path, "rb"); if (file != TD_NULL) { return file; } } return TD_NULL; } td_u32 calc_pcm_frame_size(const uapi_audio_pcm_format *pcm_fmt) { size_t frame_size; if (pcm_fmt == TD_NULL) { return 0; } if (pcm_fmt->bit_depth == UAPI_AUDIO_BIT_DEPTH_16) { frame_size = sizeof(td_s16); } else { frame_size = sizeof(td_s32); } return frame_size * pcm_fmt->channels * pcm_fmt->sample_per_frame; } static td_u32 pcm_sample_size(td_u32 channels, td_u32 bit_depth) { size_t depth_size; switch (bit_depth) { case UAPI_AUDIO_BIT_DEPTH_8: depth_size = sizeof(td_s8); break; case UAPI_AUDIO_BIT_DEPTH_16: depth_size = sizeof(td_s16); break; case UAPI_AUDIO_BIT_DEPTH_24: case UAPI_AUDIO_BIT_DEPTH_32: depth_size = sizeof(td_s32); break; default: return 0; } return depth_size * channels; } /* how many bytes per frame */ td_u32 calc_audio_frame_size(const uapi_audio_frame *frame) { if (frame == TD_NULL) { return 0; } if (frame->channels < UAPI_AUDIO_CHANNEL_1 || frame->channels > UAPI_AUDIO_CHANNEL_16) { return 0; } return pcm_sample_size(frame->channels, frame->bit_depth) * frame->pcm_samples; } typedef struct { const td_char *name; uapi_acodec_id acodec_id; } acodec_type_map; static acodec_type_map g_acodec_type_map[] = { {"pcm", UAPI_ACODEC_ID_PCM}, {"mp3", UAPI_ACODEC_ID_MP3}, {"aac", UAPI_ACODEC_ID_AAC}, {"sbc", UAPI_ACODEC_ID_SBC}, {"msbc", UAPI_ACODEC_ID_MSBC}, {"opus", UAPI_ACODEC_ID_OPUS}, {"flac", UAPI_ACODEC_ID_FLAC}, {"l2hc", UAPI_ACODEC_ID_L2HC}, {"lc3", UAPI_ACODEC_ID_LC3}, {"silk", UAPI_ACODEC_ID_SILK}, {"vorbis", UAPI_ACODEC_ID_VORBIS}, {"amrwb", UAPI_ACODEC_ID_AMRWB}, }; td_s32 sample_audio_get_acodec_id(const td_char *name, uapi_acodec_id *acodec_id) { td_u32 i; const size_t num = sizeof(g_acodec_type_map) / sizeof(acodec_type_map); for (i = 0; i < num; i++) { if (strcasecmp(name, g_acodec_type_map[i].name) == 0) { *acodec_id = g_acodec_type_map[i].acodec_id; return EXT_SUCCESS; } } *acodec_id = UAPI_ACODEC_ID_MAX; return EXT_FAILURE; } #define I2S_MCLK_DEF 12288000 /* i2s mclk default 12.288M */ static uapi_audio_i2s_mclk_sel get_i2s_mclk_div(uapi_audio_sample_rate sample_rate) { const td_u32 clk_div = I2S_MCLK_DEF / (td_u32)sample_rate; const struct { uapi_audio_i2s_mclk_sel mclk_div_e; td_u32 mclk_div; } div_tab[] = { {UAPI_AUDIO_I2S_MCLK_128_FS, 128}, /* 128 = mclk / fs */ {UAPI_AUDIO_I2S_MCLK_256_FS, 256}, /* 256 = mclk / fs */ {UAPI_AUDIO_I2S_MCLK_384_FS, 384}, /* 384 = mclk / fs */ {UAPI_AUDIO_I2S_MCLK_512_FS, 512}, /* 512 = mclk / fs */ {UAPI_AUDIO_I2S_MCLK_768_FS, 768}, /* 768 = mclk / fs */ {UAPI_AUDIO_I2S_MCLK_1024_FS, 1024}, /* 1024 = mclk / fs */ {UAPI_AUDIO_I2S_MCLK_1536_FS, 1536}, /* 1536 = mclk / fs */ }; sap_trace_log_u32(sample_rate); sap_trace_log_u32(clk_div); for (td_u32 i = 0; i < sizeof(div_tab) / sizeof(div_tab[0]); i++) { if (div_tab[i].mclk_div == clk_div) { return div_tab[i].mclk_div_e; } } return UAPI_AUDIO_I2S_MCLK_MAX; } static uapi_audio_i2s_bclk_sel get_i2s_bclk_div(uapi_audio_channel channels, uapi_audio_bit_depth bit_depth, uapi_audio_sample_rate sample_rate) { td_u32 bclk_hz = (td_u32)sample_rate * (td_u32)bit_depth * (td_u32)channels; sap_trace_log_u32(bclk_hz); return (uapi_audio_i2s_bclk_sel)(I2S_MCLK_DEF / bclk_hz); } static uapi_audio_i2s_mode get_i2s_mode(uapi_audio_channel channels) { uapi_audio_i2s_mode i2s_mode; /* Set I2S mode according to input channels */ if (channels == UAPI_AUDIO_CHANNEL_1) { sap_alert_log_info("i2s_pcm mode"); i2s_mode = UAPI_AUDIO_I2S_PCM_MODE; } else if (channels == UAPI_AUDIO_CHANNEL_2) { sap_alert_log_info("i2s_std mode"); i2s_mode = UAPI_AUDIO_I2S_STD_MODE; } else { sap_alert_log_info("i2s_tdm mode"); i2s_mode = UAPI_AUDIO_I2S_TDM_MODE; } sap_trace_log_u32(i2s_mode); return i2s_mode; } td_void get_i2s_attr(uapi_audio_i2s_attr *i2s_attr, const uapi_audio_pcm_format *pcm_fmt, td_bool local_out_port) { i2s_attr->bit_depth = pcm_fmt->bit_depth; i2s_attr->channels = pcm_fmt->channels; /* Set I2S mode according to output channels */ i2s_attr->i2s_mode = get_i2s_mode(pcm_fmt->channels); i2s_attr->mclk = get_i2s_mclk_div(pcm_fmt->sample_rate); /* The fs division from mclk */ if (local_out_port == TD_TRUE) { /* Local output port : SmartPA Speaker */ i2s_attr->master = SND_OUT_I2S_MST_DEF; i2s_attr->pcm_sample_rise_edge = SND_OUT_I2S_DATA_EDGE_DEF; i2s_attr->bclk = get_i2s_bclk_div(pcm_fmt->channels, SND_OUT_FRAME_MODE_WORD_LEN, pcm_fmt->sample_rate); } else { /* Cat1 port */ i2s_attr->master = SIO_CAT1_I2S_MST_DEF; i2s_attr->pcm_sample_rise_edge = SIO_CAT1_I2S_DATA_EDGE_DEF; i2s_attr->pcm_delay_cycle = SIO_CAT1_I2S_PCM_DELAY_DEF; i2s_attr->bclk = get_i2s_bclk_div(pcm_fmt->channels, SIO_CAT1_FRAME_MODE_WORD_LEN, pcm_fmt->sample_rate); } sap_trace_log_u32(i2s_attr->mclk); sap_trace_log_u32(i2s_attr->bclk); } #define FLAC_HEADER_SIZE 4 #define ID3V2_HEADER_SIZE 10 /* Default magic bytes for ID3v2 header: "ID3" */ #define ID3V2_DEFAULT_MAGIC "ID3" #define av_rb32(x) \ (((uint32_t)((const uint8_t*)(x))[0] << 24) | \ (((const uint8_t*)(x))[1] << 16) | \ (((const uint8_t*)(x))[2] << 8) | \ ((const uint8_t*)(x))[3]) #define flac_tag(a, b, c, d) ((d) | ((c) << 8) | ((b) << 16) | ((unsigned)(a) << 24)) typedef union { /* Define the struct bits */ struct { unsigned int block_size : 24; /* 24 bits is metadata size */ unsigned int block_type : 7; /* 7 bits is metadata type */ unsigned int last : 1; /* 1 bits represent last metadata block */ } bits; /* Define an unsigned member */ unsigned int u32; } flac_metadata_header; static td_bool ff_id3v2_match(const uint8_t *buf, const char *magic) { return buf[0] == magic[0] && /* 0 magic num */ buf[1] == magic[1] && /* 1 magic num */ buf[2] == magic[2] && /* 2 magic num */ buf[3] != 0xff && /* 3 magic num */ buf[4] != 0xff && /* 4 magic num */ (buf[6] & 0x80) == 0 && /* 6 magic num */ (buf[7] & 0x80) == 0 && /* 7 magic num */ (buf[8] & 0x80) == 0 && /* 8 magic num */ (buf[9] & 0x80) == 0; /* 9 magic num */ } static td_u32 ff_id3v2_tag_len(const uint8_t *buf, td_u32 size) { td_u32 len; len = ((buf[6] & 0x7f) << 21) + /* 6 represent sixth byte, 21 is shift num */ ((buf[7] & 0x7f) << 14) + /* 7 represent seventh byte, 14 is shift num */ ((buf[8] & 0x7f) << 7) + /* 8 represent eighth byte, 7 is shift num */ (buf[9] & 0x7f) + /* 9 represent ninth byte */ ID3V2_HEADER_SIZE; if ((buf[5] & 0x10) != 0x0) { /* 5 represent fifth byte */ len += ID3V2_HEADER_SIZE; } sap_unused(size); return len; } static td_void skip_id3v2_tag(FILE *in) { td_u32 tag_len; td_u32 read_len; td_u32 ret; uint8_t header[ID3V2_HEADER_SIZE]; read_len = fread(header, 1, sizeof(header), in); if (read_len != sizeof(header)) { sap_printf("read failed!"); return; } if (!(ff_id3v2_match(header, ID3V2_DEFAULT_MAGIC))) { rewind(in); return; } tag_len = ff_id3v2_tag_len(header, sizeof(header)); tag_len -= ID3V2_HEADER_SIZE; ret = (td_u32)fseek(in, tag_len, SEEK_CUR); if (ret != 0) { sap_printf("offset failed!"); } } static td_void skip_metadata(FILE *in) { td_u32 read_len; td_u32 ret; uint8_t header[FLAC_HEADER_SIZE]; flac_metadata_header metadata; read_len = fread(header, sizeof(header), 1, in); if (read_len != 1) { sap_printf("read failed!"); return; } if (av_rb32(header) != flac_tag('f', 'L', 'a', 'C')) { sap_printf("check fLaC header error"); return; } while (1) { read_len = fread(header, sizeof(header), 1, in); if (read_len != 1) { sap_printf("read failed!"); return; } metadata.u32 = av_rb32(header); ret = (td_u32)fseek(in, metadata.bits.block_size, SEEK_CUR); if (ret != 0) { sap_printf("offset failed!"); } if (metadata.bits.last != 0x0) { break; } } } td_void flac_skip_header(FILE *in) { skip_id3v2_tag(in); skip_metadata(in); } #define MIN_ARG_NUM 2 #define INDEX_1 1 #define INDEX_2 2 static td_bool is_argv_end(const parg_state *ps, int argc, char *const argv[]) { return (ps->optind >= argc || argv[ps->optind] == NULL) ? TD_TRUE : TD_FALSE; } static td_s32 match_short(parg_state *ps, int argc, char *const argv[], const char *optstring) { const char *p = strchr(optstring, *ps->nextchar); if (p == NULL) { ps->optopt = *ps->nextchar++; return '?'; } /* if no option argument, return option */ if (p[INDEX_1] != ':') { return *ps->nextchar++; } /* if more characters, return as option argument */ if (ps->nextchar[INDEX_1] != '\0') { ps->optarg = &ps->nextchar[1]; ps->nextchar = NULL; return *p; } /* if option argument is optional, return option */ if (p[INDEX_2] == ':') { return *ps->nextchar++; } /* option argument required, so return next argv element */ if (is_argv_end(ps, argc, argv)) { ps->optopt = *ps->nextchar++; return (optstring[0] == ':') ? ':' : '?'; } ps->optarg = argv[ps->optind++]; ps->nextchar = NULL; return *p; } td_void parg_init(parg_state *ps) { ps->optarg = NULL; ps->optind = 1; ps->optopt = '?'; ps->nextchar = NULL; } td_s32 parg_getopt(parg_state *ps, int argc, td_char *const argv[], const td_char *optstring) { if (ps == NULL || argv == NULL || optstring == NULL) { return -1; } if (argc < MIN_ARG_NUM) { return -1; } ps->optarg = NULL; /* advance to next element if needed */ if (ps->nextchar == NULL || *ps->nextchar == '\0') { if (is_argv_end(ps, argc, argv)) { return -1; } ps->nextchar = argv[ps->optind++]; /* check for nonoption element (including '-') */ if (ps->nextchar[0] != '-' || ps->nextchar[1] == '\0') { ps->optarg = ps->nextchar; ps->nextchar = NULL; return 1; } /* check for '--' */ if (ps->nextchar[INDEX_1] == '-') { if (ps->nextchar[INDEX_2] == '\0') { ps->nextchar = NULL; return -1; } } ps->nextchar++; } /* match nextchar */ return match_short(ps, argc, argv, optstring); }