Chinaunix首页 | 论坛 | 博客
  • 博客访问: 218355
  • 博文数量: 37
  • 博客积分: 1627
  • 博客等级: 上尉
  • 技术积分: 396
  • 用 户 组: 普通用户
  • 注册时间: 2010-01-06 12:08
文章分类

全部博文(37)

文章存档

2018年(1)

2016年(2)

2015年(2)

2014年(1)

2012年(8)

2011年(9)

2010年(14)

我的朋友

分类: LINUX

2010-01-29 11:15:07

虽然想要加的网络文件功能还没有加,但是先记录,以免以后遗忘。
首先是主程序:
 

/*
 *miniplayer--a simple mp3 player based on libmad and alsa.
 *author: zheng.wenwei
 *date: 2009.4.17
 *version: 0.8(network file supported)
 */


/*
 * refix
 * 1.sigwait will action unlike the sigsuspend, when it return, it won't
 * trigger the signal handle, it just discards the signal. howerver, since t
 * the thread directed signal syscalls don't include mounting the signal
 * handle(p*kill, sigmask, sigwait), and the mount action in fack is about
 * the process, so it maybe understant here.
 *
 * 2.before creating the thread which will send a USR1 to the play thread,
 * you should make sure the USR1 is masked in it. this is the sequence
 * should be obeyed.
 *
 * 3.assume the MP3 is CBR, 128kps, 2 channels(in coming days, we shouldn't
 * use this assumption any longer.
 *
 * 4.BUG1:(refixed)
 * at the begining, it's silent, why?
 * silly!!! that's because I start playing at one part of the mem finished
 * filling, but not at the time all the mem(index == FREAMMEM);
 *
 * 5.BUG2:(refixed)
 * frequencily the music on lap, why?
 * NOTICE THIS:
 * the writei in fact count by frames!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
 * so I use bytes is wrong. maybe the alsa find the mem is not so long and
 * do something?
 *
 */


#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <pthread.h>
#include <error.h>
#include <signal.h>
#include <string.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include "miniplayer_decode.h"
#include "miniplayer_play.h"

//#define _DEBUG

typedef
enum{LOCAL_FILE, NETWORK_FILE} work_modeT;

//mem_to_fill is the mem to fill decoded data

char *mem1, *mem2, *mem_to_fill;
//mem include the MP3 data

char *mem_to_input;
//if the last_mem is not zero, that means it's the end of music

int last_mem_len = 0;
//the tid of play thread(also the main thread)

pthread_t play_tid;
//just for calling sigwait

int signo;
//working mode

work_modeT work_mode;
sigset_t usr2_set;
sigset_t usr1_set;
snd_pcm_t *playback_handle;

//the test and exit function

static void test_null(void *p, const char *errstr)
{
    if(p == NULL)
    {
        fputs(errstr, stderr);
        exit(-1);
    }
}

static void test_LT0(int no, int flag, const char *errstr)
{
    if(no < 0)
    {
        if(flag ==0)
        {
            perror(errstr);
        }
        else
        {
            fputs(errstr, stderr);
        }
        exit(-1);
    }
}

static void play(char *mem, unsigned int length);
void *decode_routin(void *arg);

int main()
{
    int mem_num = 0;
    pthread_t decode_tid;
#ifdef _DEBUG
    //rediret the stdin and stdout

    int stdin_fd;
    int stdout_fd;
    stdin_fd = open("travel.mp3", O_RDONLY, 0);
    test_LT0(stdin_fd, 0, "open");
    test_LT0(dup2(stdin_fd, STDIN_FILENO), 0, "dup2");
    stdout_fd = open("temp", O_WRONLY|O_CREAT, 0);
    test_LT0(stdout_fd, 0, "open");
    test_LT0(dup2(stdout_fd, STDOUT_FILENO), 0, "dup2");
#endif
    mem1 = (char *)malloc(sizeof(char) * MEMLEN);
    test_null(mem1, "malloc");
    mem2 = (char *)malloc(sizeof(char) * MEMLEN);
    test_null(mem2, "malloc");
    memset(mem1, 0, MEMLEN);
    memset(mem2, 0, MEMLEN);
    
    //the first mem to fill decoded data is mem1

    mem_to_fill = mem1;

    //do all prepare for playback handle

    all_prepare();

    //mask signal SIGUSR1 for waiting

    sigemptyset(&usr1_set);
    sigaddset(&usr1_set, SIGUSR1);
    pthread_sigmask(SIG_BLOCK, &usr1_set, NULL);

    //get play thread id

    play_tid = pthread_self();

    //create decode thread

    test_LT0(pthread_create(&decode_tid, NULL, decode_routin, NULL), 0, "pthread create");

    //wait for SIGUSR1 to start playing

    sigwait(&usr1_set, &signo);
    fputs("begin playing\n", stderr);

    //loop for playing music

    while(1)
    {
        //tell decodr to start filling mem1 or mem2

        mem_to_fill = mem_num?mem1:mem2;
        pthread_kill(decode_tid, SIGUSR2);
        if(mem_num = !mem_num)
        {
            if(last_mem_len)
            {
                play(mem1, last_mem_len);
                break;
            }
            else
            {
                play(mem1, MEMLEN);
            }
        }
        else
        {
            if(last_mem_len)
            {
                play(mem2, last_mem_len);
            }
            else
            {
                play(mem2, MEMLEN);
            }
        }
    }
    
    //finish playing

    fputs("finish playing\n", stderr);
    
    //wait for decode thread exit

    pthread_join(decode_tid, NULL);

    close_playback_handle(playback_handle);
    free(mem1);
    mem1 = NULL;
    free(mem2);
    mem2 = NULL;
#ifdef _DEBUG
    close(stdin_fd);
    close(stdout_fd);
#endif
    return(0);
}

static void play(char *mem, unsigned int length)
{
    write_to_audio(mem, length);
}

void *decode_routin(void *arg)
{
    int retval;
    size_t mem_size;
    struct stat stat;
    void *fdm;
    int stdin_mode;

    //mask the USR2 for waiting

    sigemptyset(&usr2_set);
    sigaddset(&usr2_set, SIGUSR2);
    pthread_sigmask(SIG_BLOCK, &usr2_set, NULL);

    //mmap the STDIN_FILE

    test_LT0(fstat(STDIN_FILENO, &stat), 0, "fstat");
    
    stdin_mode = stat.st_mode & S_IFMT;

    //if the stdin is not a regular file

    if(stdin_mode != S_IFREG)
    {
        work_mode = NETWORK_FILE;
    }
    else
    {
        work_mode = LOCAL_FILE;
        if(stat.st_size == 0)
        {
            fputs("file size error\n", stderr);
            exit(-1);
        }
        fdm = mmap(0, stat.st_size, PROT_READ, MAP_SHARED, STDIN_FILENO, 0);
        if(fdm == MAP_FAILED)
        {
            perror("map");
            exit(-1);
        }
        mem_to_input = (char *)fdm;
        mem_size = stat.st_size;
    }



    //decode the STD_FILE

    decode((const unsigned char *)mem_to_input, mem_size);
    
    //wait for the last USR2

    sigwait(&usr2_set, &signo);

    test_LT0(munmap(fdm ,stat.st_size), 0, "munmap");

    pthread_exit(&retval);
}

使用libmad解码的部分:

/*
 * miniplayer_decode.c
 * the implementation of miniplayer_decode.h
 */


#include <signal.h>
#include <pthread.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include "miniplayer_decode.h"

//#define _DEBUG


//memory to fill PCM data

extern char *mem_to_fill;
//the last memory length

extern int last_mem_len;
//the set include USR2

extern sigset_t usr2_set;
//the play thread id

extern pthread_t play_tid;
//jsut for calling sigwait

extern int signo;

//the current mem index

static int mem_index;

/*
 * This is the input callback. The purpose of this callback is to (re)fill
 * the stream buffer which is to be decoded. In this example, an entire file
 * has been mapped into memory, so we just call mad_stream_buffer() with the
 * address and length of the mapping. When this callback is called a second
 * time, we are finished decoding.
 */

enum mad_flow input(void *data,
         struct mad_stream *stream)
{
    struct buffer *buffer = (struct buffer *)data;

    //fprintf(stderr, "input\n");

    sleep(5);
    if (!buffer->length)
     return MAD_FLOW_STOP;

    mad_stream_buffer(stream, buffer->start, buffer->length);

    buffer->length = 0;

    return MAD_FLOW_CONTINUE;
}

/*
 * The following utility routine performs simple rounding, clipping, and
 * scaling of MAD's high-resolution samples down to 16 bits. It does not
 * perform any dithering or noise shaping, which would be recommended to
 * obtain any exceptional audio quality. It is therefore not recommended to
 * use this routine if high-quality output is desired.
 */

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);
}

/*
 * This is the output callback function. It is called after each frame of
 * MPEG audio data has been completely decoded. The purpose of this callback
 * is to output (or play) the decoded PCM audio.
 */

enum mad_flow output(void *data,
         struct mad_header const *header,
         struct mad_pcm *pcm)
{
    //the first time to decode?

    static int first = 1;
    //the pos to start fill the mem

    char *mem_pos;
    //the position

    int pos = 0;
    unsigned int nchannels, nsamples;
    mad_fixed_t const *left_ch, *right_ch;

    //if the memory full?

    if(mem_index == FRAME_MEM)
    {
        //start the playing

        if(first)
        {
            pthread_kill(play_tid, SIGUSR1);
            first = 0;
        }
        sigwait(&usr2_set, &signo);
        mem_index = 0;
    }

    mem_pos = mem_to_fill + mem_index * FRAMELEN;
    /* pcm->samplerate contains the sampling frequency */

    nchannels = pcm->channels;
    nsamples = pcm->length;
    left_ch = pcm->samples[0];
    right_ch = pcm->samples[1];

    while (nsamples--) {
     signed int sample;

     /* output sample(s) in 16-bit signed little-endian PCM */

     sample = scale(*left_ch++);
     mem_pos[pos++] = (sample >> 0) & 0xff;
     mem_pos[pos++] = (sample >> 8) & 0xff;

     if (nchannels == 2) {
     sample = scale(*right_ch++);
        mem_pos[pos++] = (sample >> 0) & 0xff;
        mem_pos[pos++] = (sample >> 8) & 0xff;
     }
    }
    //a new MP3 frame decoded

    mem_index ++;

    return MAD_FLOW_CONTINUE;
}

/*
 * This is the error callback function. It is called whenever a decoding
 * error occurs. The error is indicated by stream->error; the list of
 * possible MAD_ERROR_* errors can be found in the mad.h (or stream.h)
 * header file.
 */

enum mad_flow err(void *data,
         struct mad_stream *stream,
         struct mad_frame *frame)
{
    struct buffer *buffer = (struct buffer *)data;

    fprintf(stderr, "decoding error 0x%04x (%s) at byte offset %u\n",
     stream->error, mad_stream_errorstr(stream),
     stream->this_frame - buffer->start);

    /* return MAD_FLOW_BREAK here to stop decoding (and propagate an error) */

    return MAD_FLOW_CONTINUE;
}

/*
 * This is the function called to perform all the decoding.
 * It instantiates a decoder object and configures it with the input,
 * output, and error callback functions above. A single call to
 * mad_decoder_run() continues until a callback function returns
 * MAD_FLOW_STOP (to stop decoding) or MAD_FLOW_BREAK (to stop decoding and
 * signal an error).
 */

int decode(unsigned char const *start, unsigned long length)
{
    struct buffer buffer;
    struct mad_decoder decoder;
    int result;

    /* initialize our private message structure */

    buffer.start = start;
    buffer.length = length;

    /* configure input, output, and error functions */

    mad_decoder_init(&decoder, &buffer,
         input, 0 /* header */, 0 /* filter */, output,
         err, 0 /* message */);

    /* start decoding */

    result = mad_decoder_run(&decoder, MAD_DECODER_MODE_SYNC);

    //count the length of last mem

    last_mem_len = mem_index * FRAMELEN;

    /* release the decoder */

    mad_decoder_finish(&decoder);

    return result;
}

使用alsa播放的部分:

 

/*
 * miniplayer_play.c
 * implement for miniplayer_play.h
 */


#include <stdio.h>
#include <stdlib.h>
#include <alsa/asoundlib.h>
#include "miniplayer_play.h"

//the playback handle to process

extern snd_pcm_t *playback_handle;

//do all the preparation for playback handle

//ATTENTATIN:

//all the parameters here to set is the usual case,

//but not all the case. in coming versions, we should

//not depend on this assumption.

int all_prepare()
{
    char *pcm_name = "plughw:0,0";
    int err;
    int rate = 44100;
    snd_pcm_hw_params_t *hw_params;

    if ((err = snd_pcm_open (&playback_handle, pcm_name, SND_PCM_STREAM_PLAYBACK, 0)) < 0)
    {
        fprintf (stderr, "cannot open audio device %s (%s)\n",
             pcm_name,
             snd_strerror (err));
        exit(-1);
    }
    
    if ((err = snd_pcm_hw_params_malloc (&hw_params)) < 0)
    {
        fprintf (stderr, "cannot allocate hardware parameter structure (%s)\n",
             snd_strerror (err));
        exit(-1);
    }
            
    if ((err = snd_pcm_hw_params_any (playback_handle, hw_params)) < 0)
    {
        fprintf (stderr, "cannot initialize hardware parameter structure (%s)\n",
             snd_strerror (err));
        exit(-1);
    }

    if ((err = snd_pcm_hw_params_set_access (playback_handle, hw_params, SND_PCM_ACCESS_RW_INTERLEAVED)) < 0)
    {
        fprintf (stderr, "cannot set access type (%s)\n",
             snd_strerror (err));
        exit(-1);
    }

    if ((err = snd_pcm_hw_params_set_format (playback_handle, hw_params, SND_PCM_FORMAT_S16_LE)) < 0)
    {
        fprintf (stderr, "cannot set sample format (%s)\n",
             snd_strerror (err));
        exit(-1);
    }

    if ((err = snd_pcm_hw_params_set_rate_near (playback_handle, hw_params, (unsigned int *)(&(rate)), 0)) < 0)
    {
        fprintf (stderr, "cannot set sample rate (%s)\n",
             snd_strerror (err));
        exit(-1);
    }

    if ((err = snd_pcm_hw_params_set_channels (playback_handle, hw_params, 2)) < 0)
    {
        fprintf (stderr, "cannot set channel count (%s)\n",
             snd_strerror (err));
        exit(-1);
    }

    if ((err = snd_pcm_hw_params (playback_handle, hw_params)) < 0)
    {
        fprintf (stderr, "cannot set parameters (%s)\n",
             snd_strerror (err));
        exit(-1);
    }

    snd_pcm_hw_params_free (hw_params);

    if ((err = snd_pcm_prepare (playback_handle)) < 0)
    {
        fprintf (stderr, "cannot prepare audio interface for use (%s)\n",
             snd_strerror (err));
        exit(-1);
    }

    return(0);
}

//write the PCM data to the audio

//NOTICE:

//that the length is counted by bytes, but the writei count it by

//frames(usually one frame is 4 bytes(2 samples, 2 channel), here frame is

//a PCM frame, not a MP3 frame.

int write_to_audio(char *mem, int length)
{
    int err;
    snd_pcm_uframes_t frames = length >> 2 ;
    err = snd_pcm_writei(playback_handle, mem, frames);
    if(err == -EPIPE)
    {
        fprintf(stderr, "underrun occurred\n");
        snd_pcm_prepare(playback_handle);
    }
    else if(err == -ESTRPIPE)
    {
        fprintf(stderr, "suspend occurred\n");
        while((err = snd_pcm_resume(playback_handle)) == -EAGAIN)
        {
            sleep(1);
        }
        if(err < 0)
        {
            err = snd_pcm_prepare(playback_handle);
        }
    }
    else if(err < 0)
    {
        fprintf(stderr, "error from writei: %s\n", snd_strerror(err));
    }
    else if(err != (int)frames)
    {
        fprintf(stderr, "short write, write %d frames of total %d\n",
                err, frames);
    }
}

//close the playback_handle

int close_playback_handle(snd_pcm_t *playback_handle)
{
    snd_pcm_drain(playback_handle);
    snd_pcm_close(playback_handle);
}

现在要做的工作很简单,就是不用alsa,而直接操作/dev/dsp。

阅读(739) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~