linuxOS_D21X/source/artinchip/test-libmad/mp3player.c
2024-11-29 16:33:21 +08:00

378 lines
9.1 KiB
C

/*
* Copyright (C) 2020-2022 Artinchip Technology Co. Ltd
*
* SPDX-License-Identifier: Apache-2.0
*
* author: <qi.xu@artinchip.com>
* Desc: mp3 audio player (mp3 decode use libmad)
*/
#include <stdio.h>
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
#include <malloc.h>
#include <string.h>
#include <pthread.h>
#include <alsa/asoundlib.h>
#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(&params);
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;
}