/* * Copyright (C) 2020-2022 Artinchip Technology Co. Ltd * * SPDX-License-Identifier: Apache-2.0 * * author: * Desc: mp3 audio player (mp3 decode use libmad) */ #include #include #include #include #include #include #include #include #include "mad.h" #define LOG_TAG "mp3player" #define TAG_ERROR "error " #define TAG_WARNING "warning" #define TAG_INFO "info " #define TAG_DEBUG "debug " #define TAG_VERBOSE "verbose" #define mpp_log(level, tag, fmt, arg...) ({ \ printf("%s: %s <%s:%d>: "fmt"\n", tag, LOG_TAG, __FUNCTION__, __LINE__, ##arg); \ }) #define loge(fmt, arg...) mpp_log(LOGL_ERROR, TAG_ERROR, "\033[40;31m"fmt"\033[0m", ##arg) #define logw(fmt, arg...) mpp_log(LOGL_WARNING, TAG_WARNING, "\033[40;33m"fmt"\033[0m", ##arg) #define logi(fmt, arg...) mpp_log(LOGL_INFO, TAG_INFO, "\033[40;32m"fmt"\033[0m", ##arg) #define logd(fmt, arg...) mpp_log(LOGL_DEBUG, TAG_DEBUG, fmt, ##arg) #define logv(fmt, arg...) mpp_log(LOGL_VERBOSE, TAG_VERBOSE, fmt, ##arg) #define SAMPLES 44100 #define CHANELS 2 struct audio_ctx { unsigned char* start; unsigned long length; // audio paramter int channels; int sample_rate; int decode_eos; // output data unsigned char* pcm_buf; // the buffer of decoded pcm data int read_pos; // read point in pcm_buf, byte unit int write_pos; // write point in pcm_buf, byte unit int buf_len; // max length of pcm_buf int valid_size; // valid data size in pcm_buf pthread_mutex_t out_mutex; // output buffer mutex FILE* fp_out; }; static void print_help(void) { printf("Usage: mp3_test [options]:\n" " -i input stream file name\n" " -h help\n\n" "Example: mpp_test -i test.mp3\n"); } static enum mad_flow input(void *data, struct mad_stream *stream) { struct audio_ctx *buffer = data; if (!buffer->length) return MAD_FLOW_STOP; mad_stream_buffer(stream, buffer->start, buffer->length); buffer->length = 0; return MAD_FLOW_CONTINUE; } static inline signed int scale(mad_fixed_t sample) { /* round */ sample += (1L << (MAD_F_FRACBITS - 16)); /* clip */ if (sample >= MAD_F_ONE) sample = MAD_F_ONE - 1; else if (sample < -MAD_F_ONE) sample = -MAD_F_ONE; /* quantize */ return sample >> (MAD_F_FRACBITS + 1 - 16); } static enum mad_flow output(void *data, struct mad_header const *header, struct mad_pcm *pcm) { unsigned int nchannels, nsamples; mad_fixed_t const *left_ch, *right_ch; struct audio_ctx *ctx = data; /* pcm->samplerate contains the sampling frequency */ nchannels = pcm->channels; nsamples = pcm->length; left_ch = pcm->samples[0]; right_ch = pcm->samples[1]; ctx->channels = nchannels; ctx->sample_rate = pcm->samplerate; int data_size = nchannels * nsamples * sizeof(short); while(ctx->buf_len - ctx->valid_size < data_size) { loge("empty data not enough"); usleep(1000); } #ifdef SAVE_PCM int tmp = ctx->write_pos; #endif while (nsamples--) { signed int sample; /* output sample(s) in 16-bit signed little-endian PCM */ sample = scale(*left_ch++); ctx->pcm_buf[ctx->write_pos++] = (sample>>0) & 0xff; ctx->pcm_buf[ctx->write_pos++] = (sample>>8) & 0xff; if(ctx->write_pos >= ctx->buf_len) { ctx->write_pos = 0; } if (nchannels == 2) { sample = scale(*right_ch++); ctx->pcm_buf[ctx->write_pos++] = (sample>>0) & 0xff; ctx->pcm_buf[ctx->write_pos++] = (sample>>8) & 0xff; if(ctx->write_pos >= ctx->buf_len) { ctx->write_pos = 0; } } } pthread_mutex_lock(&ctx->out_mutex); ctx->valid_size += data_size; pthread_mutex_unlock(&ctx->out_mutex); logd("valid_size: %d, read_pos: %d, write_pos: %d\n", ctx->valid_size, ctx->read_pos, ctx->write_pos); #ifdef SAVE_PCM fwrite(ctx->pcm_buf + tmp, 1, data_size, ctx->fp_out); #endif return MAD_FLOW_CONTINUE; } static enum mad_flow error(void *data, struct mad_stream *stream, struct mad_frame *frame) { struct audio_ctx *buffer = data; loge("decoding error 0x%04x (%s) at byte offset %u\n", stream->error, mad_stream_errorstr(stream), (unsigned int)(stream->this_frame - buffer->start)); /* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */ return MAD_FLOW_CONTINUE; } static int set_param(struct audio_ctx* ctx, snd_pcm_t* handle) { snd_pcm_hw_params_t *params; int err; snd_pcm_hw_params_alloca(¶ms); err = snd_pcm_hw_params_any(handle, params); if (err < 0) { loge("Broken configuration for this PCM: no configurations available"); return -1; } err = snd_pcm_hw_params_set_access(handle, params, SND_PCM_ACCESS_RW_INTERLEAVED); err = snd_pcm_hw_params_set_format(handle, params, SND_PCM_FORMAT_S16_LE); if (err < 0) { loge("Sample format non available"); return -1; } err = snd_pcm_hw_params_set_channels(handle, params, ctx->channels); if (err < 0) { loge("Channels count non available"); return -1; } unsigned int rate = ctx->sample_rate; err = snd_pcm_hw_params_set_rate_near(handle, params, &rate, 0); assert(err >= 0); if ((float)ctx->sample_rate * 1.05 < rate || (float)ctx->sample_rate * 0.95 > rate) { logw("rate is not accurate(request: %d, got: %u)", ctx->sample_rate, rate); } snd_pcm_uframes_t period_size = 1024; err = snd_pcm_hw_params_set_period_size_near(handle, params, &period_size, 0); err = snd_pcm_hw_params(handle, params); if (err < 0) { loge("Unable to install hw params:"); return -1; } return 0; } void* audio_render_thread(void *p) { struct audio_ctx* ctx = (struct audio_ctx*)p; snd_pcm_t* handle; int first_frame = 0; int byte_per_sample = 2; // 16bit signed int pcm_data_len = 0; // length of pcm data every time to write int send_data_len; int count; int ret; ret = snd_pcm_open(&handle, "default", SND_PCM_STREAM_PLAYBACK, 0); // get decode sample rate while(first_frame == 0 && ctx->sample_rate == 0) { usleep(1000); } first_frame = 1; pcm_data_len = 12800; //ctx->channels * ctx->sample_rate * byte_per_sample * 20 / 1000; // 5ms set_param(ctx, handle); logi("samplerate: %d, byte_per_sample: %d, channel: %d", ctx->sample_rate, byte_per_sample, ctx->channels); while(1) { if(ctx->decode_eos && ctx->valid_size < pcm_data_len) break; // if the decode data is not enough, wait if(!ctx->decode_eos && (ctx->valid_size == 0 || ctx->valid_size < pcm_data_len)) { usleep(1000); continue; } if(ctx->read_pos <= ctx->write_pos) { // send data send_data_len = pcm_data_len; if(ctx->decode_eos) send_data_len = ctx->write_pos - ctx->read_pos; } else { send_data_len = ctx->buf_len - ctx->read_pos; } count = send_data_len/(ctx->channels*byte_per_sample); logi("read_pos: %d, send_data_len: %d", ctx->read_pos, send_data_len); while(count > 0) { ret = snd_pcm_writei(handle, &ctx->pcm_buf[ctx->read_pos], count); if(ret == -EAGAIN || (ret>=0 && ret < count)) { snd_pcm_wait(handle, 100); } else if(ret == -EPIPE) { // underrun ret = snd_pcm_prepare(handle); if (ret < 0) printf("Can't recovery from underrun, prepare failed: %s\n", snd_strerror(ret)); ret = 0; } else if(ret == -ESTRPIPE){ while ((ret = snd_pcm_resume(handle)) == -EAGAIN) sleep(1); } else if(ret < 0) { loge("write error, ret: %d", ret); return NULL; } if(ret) { count -= ret; pthread_mutex_lock(&ctx->out_mutex); ctx->read_pos += ret * ctx->channels*byte_per_sample; ctx->valid_size -= ret * ctx->channels*byte_per_sample; pthread_mutex_unlock(&ctx->out_mutex); } } if(ctx->read_pos >= (ctx->buf_len-1)) ctx->read_pos = 0; } if (snd_pcm_drain(handle) < 0) { loge("snd_pcm_drain failed"); } snd_pcm_close(handle); return NULL; } int main(int argc, char *argv[]) { struct audio_ctx ctx; struct mad_decoder decoder; FILE* fp_input = NULL; int opt; int ret = 0; memset(&ctx, 0, sizeof(struct audio_ctx)); pthread_t render_thread_id; if(argc < 2) { print_help(); return -1; } while (1) { opt = getopt(argc, argv, "i:h"); if (opt == -1) { break; } switch (opt) { case 'i': fp_input = fopen(optarg, "rb"); if(fp_input == NULL) { loge("open file failed"); return -1; } fseek(fp_input, 0, SEEK_END); ctx.length = ftell(fp_input); fseek(fp_input, 0, SEEK_SET); ctx.start = malloc(ctx.length); fread(ctx.start, ctx.length, 1, fp_input); break; case 'h': print_help(); default: goto out; } } ctx.fp_out = fopen("save.pcm", "wb"); ctx.buf_len = CHANELS * SAMPLES * 2; ctx.pcm_buf = (unsigned char*)malloc(ctx.buf_len); ctx.read_pos = 0; ctx.write_pos = 0; ctx.valid_size = 0; pthread_mutex_init(&ctx.out_mutex, NULL); mad_decoder_init(&decoder, &ctx, input, 0/* header */, 0/* filter */, output, error, 0/* message */); pthread_create(&render_thread_id, NULL, audio_render_thread, &ctx); mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC); mad_decoder_finish(&decoder); ctx.decode_eos = 1; pthread_join(render_thread_id, NULL); out: if(fp_input) fclose(fp_input); if(ctx.fp_out) fclose(ctx.fp_out); if(ctx.start) free(ctx.start); if(ctx.pcm_buf) free(ctx.pcm_buf); pthread_mutex_destroy(&ctx.out_mutex); return ret; }