player/audioplayer.c
author Markus Bröker <mbroeker@largo.dyndns.tv>
Sat, 13 Dec 2008 15:56:39 +0100
changeset 0 06dd3b8d90ad
permissions -rw-r--r--
Virtual Reader committer: Markus Bröker <mbroeker@largo.homelinux.org>

/**
 *  $Id: audioplayer.c 53 2008-01-10 00:19:41Z mbroeker $
 * $URL: http://localhost/svn/c/VirtualReader/trunk/player/audioplayer.c $
 */

#include <audioplayer.h>

typedef struct {
    SDL_AudioSpec spec;         // internal structure
    Uint8 *sound_buffer;        // malloced pointer from sdl
    Uint32 sound_pos;           // position
    Uint32 sound_len;           // length in bytes
    Uint32 length;              // Length in Milliseconds

    int opened;                 // bool opened
    int loaded;                 // bool loaded
    int verbose;                // verbose messages
    int timeout;                // timeout
    int timer;                  // bool timer
} AudioDevice;

AudioDevice AD;

int audio_init ()
{
    // initialize SDL for audio output
    if (SDL_Init (SDL_INIT_AUDIO) < 0) {
        fprintf (stderr, "audio_init: Cannot initialize SDL-Subsystem\n");
        return -1;
    }

    AD.sound_buffer = 0;
    AD.sound_pos = 0;
    AD.sound_len = 0;

    AD.opened = 0;
    AD.loaded = 0;
    AD.verbose = 0;

    AD.timeout = 0;
    AD.timer = 0;

    return 0;
}

void audio_shutdown ()
{
    // quit sdl
    SDL_AudioQuit ();
    SDL_Quit ();
}

static void Callback (void *userdata, Uint8 * stream, int len)
{
    Uint8 *waveptr = NULL;

    /*
     *     loaded = 0 Segmentation fault
     *        len = 0 nothing to play
     */
    if (!(AD.loaded && len))
        return;

    waveptr = AD.sound_buffer + AD.sound_pos;
    SDL_MixAudio (stream, waveptr, len, SDL_MIX_MAXVOLUME);
    AD.sound_pos += len;        // bereits gespielt

    if (audioplayer_gettime () > (AD.length - AD.timeout)) {
        AD.sound_pos = AD.length;
        AD.sound_len = 0;
        AD.timer = 0;
    }
}

Uint32 audioplayer_getsoundlen ()
{
    return AD.sound_len;
}

void audioplayer_media_info (char *fname)
{
    AD.length = audioplayer_getwavelength (fname);

    printf ("Playing %s: %d Hz, %d Bit Audio, %d Channel(s), %3.2d ms\n",
            fname, AD.spec.freq, audioplayer_getbitrate (), AD.spec.channels, AD.length);
}

int audioplayer_getbitrate ()
{
    int bitrate = 0;

    switch (AD.spec.format) {
    case AUDIO_U8:
    case AUDIO_S8:
        bitrate = 8;
        break;
    case AUDIO_S16LSB:
    case AUDIO_S16MSB:
    case AUDIO_U16LSB:
    case AUDIO_U16MSB:
        bitrate = 16;
        break;
    }

    return bitrate;
}

Uint32 audioplayer_getwavelength (char *fname)
{
    struct stat info;
    Uint32 value = 0;

    if (fname == NULL)
        return AD.length;

    if (stat (fname, &info) == 0)
        /*
         * the value is really big, so be carefull
         * changes in the order result in different values
         * the storage type isn`t well choosen...
         */
        value = ((info.st_size) / (AD.spec.freq * AD.spec.channels * (audioplayer_getbitrate () / 8.0)) * 1000.0);

    return (value);
}

Uint32 audioplayer_getposition ()
{
    return AD.sound_pos;
}

Uint32 audioplayer_gettime ()
{
    Uint32 ret;

    ret = AD.sound_pos / (AD.spec.freq * AD.spec.channels * (audioplayer_getbitrate () / 8.0)) * 1000.0;

    return ret;
}

void audioplayer_settime (long tm)
{
    AD.sound_pos = tm / 1000.0 * (AD.spec.freq * AD.spec.channels * (audioplayer_getbitrate () / 8.0));

    /*
     * Eine Adresse ...
     */
    while ((AD.sound_pos % 2) != 0)
        AD.sound_pos++;

    if (AD.verbose)
        printf ("\e[31m[ATTENTION]\e[37m [#%d/#%d] = %d ms\n", AD.sound_pos, AD.sound_len, audioplayer_gettime ());
}

void audioplayer_stop ()
{
    /*
     * stops the callback function
     */
    SDL_PauseAudio (1);
    AD.spec.callback = NULL;

    if (AD.loaded == 1)
        SDL_FreeWAV (AD.sound_buffer);

    AD.loaded = 0;
    AD.timer = 0;
}

void audioplayer_delay (Uint32 milliseconds)
{
    struct timespec tm;

    tm.tv_sec = 0;
    tm.tv_nsec = milliseconds * 1000;

    while (AD.timer)
        nanosleep (&tm, NULL);
}

void audioplayer_setverbose (int verbose)
{
    AD.verbose = verbose;
}

int audioplayer (char *fname, Uint32 timeout)
{
    if (SDL_LoadWAV (fname, &AD.spec, &AD.sound_buffer, &AD.sound_len) == NULL) {
        printf ("SDL: %s\n", SDL_GetError ());
        return -1;
    }

    AD.loaded = 1;

    if (!AD.opened) {
        AD.spec.callback = Callback;
        if (SDL_OpenAudio (&AD.spec, NULL) < 0) {
            fprintf (stderr, "Cannot open audio device: %s\n", SDL_GetError ());
            return -1;
        }
        AD.opened = 1;
    }

    /*
     * calls implicitly getwavelength
     */
    audioplayer_media_info (fname);

    AD.sound_pos = 0;

    /*
     * starts playback via Callback function
     */
    SDL_PauseAudio (0);

    /*
     * initializes the timeout mechanism
     */
    AD.timer = 1;
    AD.timeout = timeout;

    /*
     * controlled delay
     */
    audioplayer_delay (timeout);

    return 0;
}