music_ogg.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 01 Jun 2013 10:11:20 -0700
changeset 621 944412baab72
parent 617 87116a42526e
child 625 1d489d8ec2e0
permissions -rw-r--r--
Updated SDL_mixer with new versions of audio libraries, including SMPEG 2.0, and fixed Visual C++ build.
     1 /*
     2   SDL_mixer:  An audio mixer library based on the SDL library
     3   Copyright (C) 1997-2013 Sam Lantinga <slouken@libsdl.org>
     4 
     5   This software is provided 'as-is', without any express or implied
     6   warranty.  In no event will the authors be held liable for any damages
     7   arising from the use of this software.
     8 
     9   Permission is granted to anyone to use this software for any purpose,
    10   including commercial applications, and to alter it and redistribute it
    11   freely, subject to the following restrictions:
    12 
    13   1. The origin of this software must not be misrepresented; you must not
    14      claim that you wrote the original software. If you use this software
    15      in a product, an acknowledgment in the product documentation would be
    16      appreciated but is not required.
    17   2. Altered source versions must be plainly marked as such, and must not be
    18      misrepresented as being the original software.
    19   3. This notice may not be removed or altered from any source distribution.
    20 */
    21 
    22 /* $Id$ */
    23 
    24 #ifdef OGG_MUSIC
    25 
    26 /* This file supports Ogg Vorbis music streams */
    27 
    28 #include <stdio.h>
    29 #include <stdlib.h>
    30 #include <string.h>
    31 
    32 #include "SDL_mixer.h"
    33 #include "dynamic_ogg.h"
    34 #include "music_ogg.h"
    35 
    36 /* This is the format of the audio mixer data */
    37 static SDL_AudioSpec mixer;
    38 
    39 /* Initialize the Ogg Vorbis player, with the given mixer settings
    40    This function returns 0, or -1 if there was an error.
    41  */
    42 int OGG_init(SDL_AudioSpec *mixerfmt)
    43 {
    44     mixer = *mixerfmt;
    45     return(0);
    46 }
    47 
    48 /* Set the volume for an OGG stream */
    49 void OGG_setvolume(OGG_music *music, int volume)
    50 {
    51     music->volume = volume;
    52 }
    53 
    54 static size_t sdl_read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
    55 {
    56     return SDL_RWread((SDL_RWops*)datasource, ptr, size, nmemb);
    57 }
    58 
    59 static int sdl_seek_func(void *datasource, ogg_int64_t offset, int whence)
    60 {
    61     return (int)SDL_RWseek((SDL_RWops*)datasource, (int)offset, whence);
    62 }
    63 
    64 static long sdl_tell_func(void *datasource)
    65 {
    66     return (long)SDL_RWtell((SDL_RWops*)datasource);
    67 }
    68 
    69 /* Load an OGG stream from an SDL_RWops object */
    70 OGG_music *OGG_new_RW(SDL_RWops *rw, int freerw)
    71 {
    72     OGG_music *music;
    73     ov_callbacks callbacks;
    74 
    75     if ( !Mix_Init(MIX_INIT_OGG) ) {
    76         if ( freerw ) {
    77             SDL_RWclose(rw);
    78         }
    79         return(NULL);
    80     }
    81 
    82     SDL_memset(&callbacks, 0, sizeof(callbacks));
    83     callbacks.read_func = sdl_read_func;
    84     callbacks.seek_func = sdl_seek_func;
    85     callbacks.tell_func = sdl_tell_func;
    86 
    87     music = (OGG_music *)SDL_malloc(sizeof *music);
    88     if ( music ) {
    89         /* Initialize the music structure */
    90         SDL_memset(music, 0, (sizeof *music));
    91         music->rw = rw;
    92         music->freerw = freerw;
    93         OGG_stop(music);
    94         OGG_setvolume(music, MIX_MAX_VOLUME);
    95         music->section = -1;
    96 
    97         if ( vorbis.ov_open_callbacks(rw, &music->vf, NULL, 0, callbacks) < 0 ) {
    98             SDL_free(music);
    99             if ( freerw ) {
   100                 SDL_RWclose(rw);
   101             }
   102             SDL_SetError("Not an Ogg Vorbis audio stream");
   103             return(NULL);
   104         }
   105     } else {
   106         if ( freerw ) {
   107             SDL_RWclose(rw);
   108         }
   109         SDL_OutOfMemory();
   110         return(NULL);
   111     }
   112     return(music);
   113 }
   114 
   115 /* Start playback of a given OGG stream */
   116 void OGG_play(OGG_music *music)
   117 {
   118     music->playing = 1;
   119 }
   120 
   121 /* Return non-zero if a stream is currently playing */
   122 int OGG_playing(OGG_music *music)
   123 {
   124     return(music->playing);
   125 }
   126 
   127 /* Read some Ogg stream data and convert it for output */
   128 static void OGG_getsome(OGG_music *music)
   129 {
   130     int section;
   131     int len;
   132     char data[4096];
   133     SDL_AudioCVT *cvt;
   134 
   135 #ifdef OGG_USE_TREMOR
   136     len = vorbis.ov_read(&music->vf, data, sizeof(data), &section);
   137 #else
   138     len = vorbis.ov_read(&music->vf, data, sizeof(data), 0, 2, 1, &section);
   139 #endif
   140     if ( len <= 0 ) {
   141         if ( len == 0 ) {
   142             music->playing = 0;
   143         }
   144         return;
   145     }
   146     cvt = &music->cvt;
   147     if ( section != music->section ) {
   148         vorbis_info *vi;
   149 
   150         vi = vorbis.ov_info(&music->vf, -1);
   151         SDL_BuildAudioCVT(cvt, AUDIO_S16, vi->channels, vi->rate,
   152                                mixer.format,mixer.channels,mixer.freq);
   153         if ( cvt->buf ) {
   154             SDL_free(cvt->buf);
   155         }
   156         cvt->buf = (Uint8 *)SDL_malloc(sizeof(data)*cvt->len_mult);
   157         music->section = section;
   158     }
   159     if ( cvt->buf ) {
   160         SDL_memcpy(cvt->buf, data, len);
   161         if ( cvt->needed ) {
   162             cvt->len = len;
   163             SDL_ConvertAudio(cvt);
   164         } else {
   165             cvt->len_cvt = len;
   166         }
   167         music->len_available = music->cvt.len_cvt;
   168         music->snd_available = music->cvt.buf;
   169     } else {
   170         SDL_SetError("Out of memory");
   171         music->playing = 0;
   172     }
   173 }
   174 
   175 /* Play some of a stream previously started with OGG_play() */
   176 int OGG_playAudio(OGG_music *music, Uint8 *snd, int len)
   177 {
   178     int mixable;
   179 
   180     while ( (len > 0) && music->playing ) {
   181         if ( ! music->len_available ) {
   182             OGG_getsome(music);
   183         }
   184         mixable = len;
   185         if ( mixable > music->len_available ) {
   186             mixable = music->len_available;
   187         }
   188         if ( music->volume == MIX_MAX_VOLUME ) {
   189             SDL_memcpy(snd, music->snd_available, mixable);
   190         } else {
   191             SDL_MixAudio(snd, music->snd_available, mixable,
   192                                           music->volume);
   193         }
   194         music->len_available -= mixable;
   195         music->snd_available += mixable;
   196         len -= mixable;
   197         snd += mixable;
   198     }
   199 
   200     return len;
   201 }
   202 
   203 /* Stop playback of a stream previously started with OGG_play() */
   204 void OGG_stop(OGG_music *music)
   205 {
   206     music->playing = 0;
   207 }
   208 
   209 /* Close the given OGG stream */
   210 void OGG_delete(OGG_music *music)
   211 {
   212     if ( music ) {
   213         if ( music->cvt.buf ) {
   214             SDL_free(music->cvt.buf);
   215         }
   216         if ( music->freerw ) {
   217             SDL_RWclose(music->rw);
   218         }
   219         vorbis.ov_clear(&music->vf);
   220         SDL_free(music);
   221     }
   222 }
   223 
   224 /* Jump (seek) to a given position (time is in seconds) */
   225 void OGG_jump_to_time(OGG_music *music, double time)
   226 {
   227 #ifdef OGG_USE_TREMOR
   228        vorbis.ov_time_seek( &music->vf, (ogg_int64_t)(time * 1000.0) );
   229 #else
   230        vorbis.ov_time_seek( &music->vf, time );
   231 #endif
   232 }
   233 
   234 #endif /* OGG_MUSIC */