文档章节

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

 代码强国
发布于 2019/10/27 00:18
字数 732
阅读 100
收藏 0

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

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

csdn的博客帐号当年那个事件中被盗了,相应的邮箱也被盗了,这次写在这里。

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

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/soundcard.h>
#include <sys/ioctl.h>
#include <sys/fcntl.h>
#include <sys/types.h>
#include <mad.h>
#include <alsa/asoundlib.h>

/* pcm device, eg. "plughw:0,0", use `aplay -l` */
#define PCM_DEVICE "default"

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

struct buffer
{
    unsigned char const *start;
    unsigned long length;
};

static int decode_and_play(unsigned char const *, unsigned long);

static int init_alsa();

int main(int argc, char *argv[])
{
    struct stat stat;
    void *fdm;
    char const *file;
    int fd;
    file = argv[1];
    fd = open(file, O_RDONLY);
    if (fstat(fd, &stat) == -1 || stat.st_size == 0)
        return -1;
    fdm = mmap(0, stat.st_size, PROT_READ, MAP_SHARED, fd, 0);
    if (fdm == MAP_FAILED)
        return -1;
    if (init_alsa() == -1)
    {
        fprintf(stderr, "init_alsa() error\n");
        return -1;
    }
    decode_and_play(fdm, stat.st_size);
    if (munmap(fdm, stat.st_size) == -1)
        return -1;
    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)
{
    struct buffer *buffer = data;
    if (!buffer->length)
        return MAD_FLOW_STOP;
    mad_stream_buffer(stream, buffer->start, buffer->length);
    buffer->length = 0;
    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)
{
    return MAD_FLOW_CONTINUE;
}

static int decode_and_play(unsigned char const *start, unsigned long length)
{
    struct buffer buffer;
    struct mad_decoder decoder;
    int result;
    buffer.start = start;
    buffer.length = length;
    mad_decoder_init(&decoder, &buffer, 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;
}

 

© 著作权归作者所有

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

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

鉴客
2011/10/23
981
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
1.9K
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

没有更多内容

加载失败,请刷新页面

加载更多

“>”(大于号)CSS选择器是什么意思?

例如: div > p.some_class { /* Some declarations */} >符号到底是什么意思? #1楼 html <div> <p class="some_class">lohrem text (it will be of red color )</p> <div> <p class="......

javail
20分钟前
47
0
mysql中int(11)的列大小是多少?

mysql中int(11)的列大小是多少? 以及可以在此列中存储的最大值? #1楼 mysql中int(11)的列大小是多少? (11) int数据类型的此属性与列的大小无关。 它只是整数数据类型的显示宽度。 从11....

技术盛宴
今天
40
0
聊聊artemis消息的推拉模式

序 本文主要研究一下artemis消息的推拉模式 拉模式 receive activemq-artemis-2.11.0/artemis-jms-client/src/main/java/org/apache/activemq/artemis/jms/client/ActiveMQMessageConsumer.......

go4it
今天
73
0
vue 全局前置守卫引起死循环的原因与解决方法

我们经常会用到全局前置守卫,如判断用户有没有登陆过,如果登陆过就直接跳到目的页面,如果没有登陆过,就跳转到登陆页。 先看官网对全局前置守卫的介绍 使用 router.beforeEach 注册一个全...

tianyawhl
今天
39
0
使用生成器模拟时间分片

对于cpu密集型任务, 时间分片可以有效减少页面卡顿, 不过对于纯计算型任务还是推荐使用worker在后台做计算 效果图, 计算密集型任务被分到每次只执行16ms, 每次执行完毕留给浏览器时间去响应事...

阿豪boy
今天
65
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部