文档章节

MyMinimad ── Linux下用libmad写的mp3解码播放程序(四)

 代码强国
发布于 11/21 00:15
字数 1118
阅读 60
收藏 0

【推荐】2019 Java 开发者跳槽指南.pdf(吐血整理) >>>

优化了内存使用,不再使用mmap映射整个文件到内存

/*
 * 本程序是从 minimad 改进而来,如要更详细的说明请参看 minimad.c
 *
 * Gu Zhou, 2009/12/25, SiChuan University, China
 *
 * 系统: ubuntu 19.10
 * 依赖: sudo apt install libmad0-dev libasound2-dev
 * 编译: gcc -Wall -o madplayer madplayer.c -lmad -lasound
 * 运行: ./madplayer filename.mp3
 *
 * 20191027, bugfix & improve
 * 20191120, optimized for memory used
 */

#include <stdio.h>
#include <mad.h>
#include <alsa/asoundlib.h>

/* pcm device, eg. "plughw:0,0", use `aplay -l` */
const char *PCM_DEVICE = "default";

const size_t BUFFER_SIZE = 65536;

static snd_pcm_hw_params_t *hwparams = NULL;
static snd_pcm_t *pcm_handle = NULL;

static int init_alsa();

typedef struct {
    FILE *file;
    unsigned char *buffer;
} decoder_data;

static int decode_and_play(decoder_data *data);

int main(int argc, const char * const argv[])
{
    decoder_data data;
    data.file = fopen(argv[1], "r");
    if (NULL == data.file)
    {
        fprintf(stderr, "open %s failed\n", argv[1]);
        return -1;
    }
    data.buffer = malloc(BUFFER_SIZE);
    if (init_alsa() == -1)
    {
        fprintf(stderr, "init_alsa() error\n");
        return -1;
    }
    decode_and_play(&data);
    fclose(data.file);
    free(data.buffer);
    return 0;
}

static int init_alsa()
{
    snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
    char *pcm_name;
    const unsigned int rate = 44100;
    unsigned int exact_rate;
    /*const int periods = 2;
    const snd_pcm_uframes_t periodsize = 8192;*/
    pcm_name = strdup(PCM_DEVICE);
    snd_pcm_hw_params_alloca(&hwparams);
    if (snd_pcm_open(&pcm_handle, pcm_name, stream, 0) < 0)
    {
        fprintf (stderr, "Error opening PCM device %s\n", pcm_name);
        return -1;
    }
    if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0)
    {
        fprintf (stderr, "Can not configure this PCM device.\n");
        return -1;
    }
    if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
    {
        fprintf (stderr, "Error setting access.\n");
        return -1;
    }
    if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0)
    {
        fprintf (stderr, "Error setting format.\n");
        return -1;
    }
    exact_rate = rate;
    if (snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_rate, 0) < 0)
    {
        fprintf (stderr, "Error setting rate.\n");
        return -1;
    }
    if (rate != exact_rate)
    {
        fprintf (stderr, "The rate %d Hz is not supported by your hardware.\n==> Using %d Hz instead.\n", rate, exact_rate);
    }
    if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 2) < 0)
    {
        fprintf (stderr, "Error setting channels.\n");
        return -1;
    }
    /* 设置periods报错,好像不用也行 :) */
    /*if (snd_pcm_hw_params_set_periods(pcm_handle, hwparams, periods, 0) < 0)
    {
        fprintf(stderr, "Error setting periods.\n");
        return -1;
    }
    if (snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, (periodsize * periods)>>2) < 0)
    {
        fprintf(stderr, "Error setting buffersize.\n");
        return -1;
    }*/
    if (snd_pcm_hw_params(pcm_handle, hwparams) < 0)
    {
        fprintf (stderr, "Error setting HW params.\n");
        return -1;
    }
    return 0;
}

static enum mad_flow input(void *data, struct mad_stream *stream)
{
    size_t length, bytes = 0;
    decoder_data *dd = data;
    if (feof(dd->file))
        return MAD_FLOW_STOP;
    if (NULL != stream->next_frame)
    {
        bytes = stream->bufend - stream->next_frame;
        memcpy(dd->buffer, stream->next_frame, bytes);
    }
    length = fread(dd->buffer + bytes, 1, BUFFER_SIZE - bytes, dd->file);
    mad_stream_buffer(stream, dd->buffer, length + bytes);
    return MAD_FLOW_CONTINUE;
}

/* 缩放成16位的pcm音频 */
static short scale(mad_fixed_t sample)
{
    sample += (1L << (MAD_F_FRACBITS - 16));
    if (sample >= MAD_F_ONE)
        sample = MAD_F_ONE - 1;
    else if (sample < -MAD_F_ONE)
        sample = -MAD_F_ONE;
    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, i;
    const mad_fixed_t *channel[2];
    short output[8192], *outputPtr;
    short sample;
    nchannels = pcm->channels;
    nsamples = pcm->length;
    for (i = 0; i < nchannels; i++)
        channel[i] = pcm->samples[i];
    outputPtr = output;
    while (nsamples--)
    {
        for (i = 0; i < nchannels; i++)
        {
            sample = scale(*channel[i]++);
            *(outputPtr++) = sample;
        }
    }
    while (snd_pcm_writei(pcm_handle, output, pcm->length) < 0)
        snd_pcm_prepare(pcm_handle);
    return MAD_FLOW_CONTINUE;
}

static enum mad_flow error(void *data, struct mad_stream *stream, struct mad_frame *frame)
{
    fprintf(stderr, "decoding error 0x%04x (%s)\n", stream->error, mad_stream_errorstr(stream));
    return MAD_FLOW_CONTINUE;
}

static int decode_and_play(decoder_data *data)
{
    struct mad_decoder decoder;
    int result;
    mad_decoder_init(&decoder, data, input, 0, 0, output, error, 0);
    mad_decoder_options(&decoder, 0);
    result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);
    mad_decoder_finish(&decoder);
    snd_pcm_drain(pcm_handle);
    return result;
}

再来个简化版,使用底层api

#include <stdio.h>
#include <mad.h>
#include <alsa/asoundlib.h>

#define BUFFER_SIZE 65536

static snd_pcm_hw_params_t *hwparams = NULL;
static snd_pcm_t *pcm_handle = NULL;

static int init_alsa();
static void play(struct mad_pcm *pcm);

int main(int argc, const char *argv[])
{
    int ret;
    struct mad_stream stream;
    struct mad_synth synth;
    struct mad_frame frame;
    size_t length, bytes = 0;
    unsigned char *buf = malloc(BUFFER_SIZE);
    FILE *file = fopen(argv[1], "r");
    init_alsa();
    mad_stream_init(&stream);
    mad_synth_init(&synth);
    mad_frame_init(&frame);
    stream.error = MAD_ERROR_NONE;
    while (!feof(file))
    {
        if (NULL != stream.next_frame)
        {
            bytes = stream.bufend - stream.next_frame;
            memcpy(buf, stream.next_frame, bytes);
        }
        length = fread(&buf[bytes], 1, BUFFER_SIZE - bytes, file);
        mad_stream_buffer(&stream, buf, length + bytes);
        bytes = 0;
        for (;;)
        {
            ret = mad_frame_decode(&frame, &stream);
            if (0 == ret)
            {
                mad_synth_frame(&synth, &frame);
                play(&synth.pcm);
                continue;
            }
            fprintf(stderr, "decoding error 0x%04x (%s)\n", stream.error, mad_stream_errorstr(&stream));
            if (!MAD_RECOVERABLE(stream.error))
                break;
        }
    }
    free(buf);
    fclose(file);
    mad_synth_finish(&synth);
    mad_frame_finish(&frame);
    mad_stream_finish(&stream);
    snd_pcm_drain(pcm_handle);
    return 0;
}

static short scale(mad_fixed_t sample)
{
    sample += (1L << (MAD_F_FRACBITS - 16));
    if (sample >= MAD_F_ONE)
        sample = MAD_F_ONE - 1;
    else if (sample < -MAD_F_ONE)
        sample = -MAD_F_ONE;
    return sample >> (MAD_F_FRACBITS + 1 - 16);
}

static void play(struct mad_pcm *pcm)
{
    unsigned int nchannels, nsamples, i;
    const mad_fixed_t *channel[2];
    short output[8192], *outputPtr;
    short sample;
    nchannels = pcm->channels;
    nsamples = pcm->length;
    for (i = 0; i < nchannels; i++)
        channel[i] = pcm->samples[i];
    outputPtr = output;
    while (nsamples--)
    {
        for (i = 0; i < nchannels; i++)
        {
            sample = scale(*channel[i]++);
            *(outputPtr++) = sample;
        }
    }
    while (snd_pcm_writei(pcm_handle, output, pcm->length) < 0)
        snd_pcm_prepare(pcm_handle);
}

static int init_alsa()
{
    snd_pcm_stream_t stream = SND_PCM_STREAM_PLAYBACK;
    const char *pcm_name = "default";
    const unsigned int rate = 44100;
    unsigned int exact_rate;
    /*const int periods = 2;
    const snd_pcm_uframes_t periodsize = 8192;*/
    snd_pcm_hw_params_alloca(&hwparams);
    if (snd_pcm_open(&pcm_handle, pcm_name, stream, 0) < 0)
    {
        fprintf (stderr, "Error opening PCM device %s\n", pcm_name);
        return -1;
    }
    if (snd_pcm_hw_params_any(pcm_handle, hwparams) < 0)
    {
        fprintf (stderr, "Can not configure this PCM device.\n");
        return -1;
    }
    if (snd_pcm_hw_params_set_access(pcm_handle, hwparams, SND_PCM_ACCESS_RW_INTERLEAVED) < 0)
    {
        fprintf (stderr, "Error setting access.\n");
        return -1;
    }
    if (snd_pcm_hw_params_set_format(pcm_handle, hwparams, SND_PCM_FORMAT_S16_LE) < 0)
    {
        fprintf (stderr, "Error setting format.\n");
        return -1;
    }
    exact_rate = rate;
    if (snd_pcm_hw_params_set_rate_near(pcm_handle, hwparams, &exact_rate, 0) < 0)
    {
        fprintf (stderr, "Error setting rate.\n");
        return -1;
    }
    if (rate != exact_rate)
    {
        fprintf (stderr, "The rate %d Hz is not supported by your hardware.\n==> Using %d Hz instead.\n", rate, exact_rate);
    }
    if (snd_pcm_hw_params_set_channels(pcm_handle, hwparams, 2) < 0)
    {
        fprintf (stderr, "Error setting channels.\n");
        return -1;
    }
    /* 设置periods报错,好像不用也行 :) */
    /*if (snd_pcm_hw_params_set_periods(pcm_handle, hwparams, periods, 0) < 0)
    {
        fprintf(stderr, "Error setting periods.\n");
        return -1;
    }
    if (snd_pcm_hw_params_set_buffer_size(pcm_handle, hwparams, (periodsize * periods)>>2) < 0)
    {
        fprintf(stderr, "Error setting buffersize.\n");
        return -1;
    }*/
    if (snd_pcm_hw_params(pcm_handle, hwparams) < 0)
    {
        fprintf (stderr, "Error setting HW params.\n");
        return -1;
    }
    return 0;
}

 

© 著作权归作者所有

粉丝 1
博文 69
码字总数 23723
作品 0
深圳
私信 提问
windows mobile 使用 libmad 解码 mp3

libmad本来是个linux下的解码库,但是现在已经有人将之移植到windows mobile上,参见:http://www.oschina.net/question/54100_30177。 利用libmad来播放mp3的思路很简单:开启两个线程,其中...

鉴客
2011/10/23
975
0
win mobile 5播放mp3音乐的方法(1)--libmad库的移植篇

win mobile 5上面播放mp3似乎已经有了可以依赖于win mobile提供的 multi-media播放器的调用方法。这种显而易见的方法,我认为缺少一些 主动性,只能微软怎么做,咱就怎么做。如果想做出个性化...

鉴客
2011/10/23
1K
0
一个简单的 libmad 包装类

近来要把WinCE.Net平台的的程序移植到Pocket PC上,出现了一点小麻烦。原有工程中有个DirectShow的封装类,用来播放MP3格式的音频,而Pocket PC 2003不支持DirectShow,为了和老系统兼容看来...

鉴客
2011/10/23
1K
0
开源mp3解码库--libmad

libmad是一个开源mp3解码库,其对mp3解码算法做了很多优化,性能较好,很多播放器如mplayer、xmms等都是使用这个开源库进行解码的;如果要设计mp3播放器而又不想研究mp3解码算法的话,libma...

匿名
2011/10/23
9K
0
android SDL系列讲解(十三) 播放音乐库 SDL_mixer教程

项目外包项目信息更新: qq抢红包,因为评估时间问题,没有对接下来。 一个网页开发项目,已经内部消耗掉了。 机会总是稍纵即逝,有兴趣探讨技术,以及项目事宜,可以联系代码GG微信: code...

代码GG陆晓明
2017/10/28
0
0

没有更多内容

加载失败,请刷新页面

加载更多

ForkJoinPool线程池

1. 拆分线程池的使用场景是什么? 答: 是对一组连续的数据进行耗时操作,例如 一个 大小为 10000 的集合 进行操作。 例子: 对1000万个数据进行排序,那么会将这个任务分割成两个500万的排序...

杨凯123
9分钟前
2
0
在多列上使用group by

我理解GROUP BY x的观点 但GROUP BY x, y如何运作的,它是什么意思? #1楼 Group By X表示将所有具有相同X值的组合放入一组中 。 Group By X, Y表示将所有具有相同值的值放在一个组中的X和Y...

技术盛宴
25分钟前
2
0
线程池ThreadPoolExecutor的内部类Worker的感想和思考

Worker依然是一个Runnable,封装了一个创建自己的原因对象,就是firstTask变量,和自己将要执行的所在线程thread变量。 thread成员变量可以直接被外部类ThreadPoolExecutor所获得,当调用add...

萧默
今天
2
0
Git推送错误“ [[远程拒绝]主机->主机(分支当前已签出)”)

昨天,我发布了一个有关如何将Git存储库从我的一台计算机克隆到另一台计算机的问题 , 如何从另一台计算机“ git clone”? 。 现在,我可以成功地将Git存储库从源(192.168.1.2)克隆到目标...

javail
今天
4
0
Selenium 4.0 Alpha更新日志

早在2018年8月,整个测试自动化社区就发生了一件重大新闻:Selenium的创始成员Simon Stewart在班加罗尔Selenium会议上正式确认了Selenium 4的发布日期和一些重要更新。 Selenium 4.0 Alpha版...

八音弦
今天
7
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部