music_ogg.c
author Ryan C. Gordon <icculus@icculus.org>
Fri, 20 Jan 2017 02:23:51 -0500
changeset 726 298d69a81cc9
parent 725 bdf7b8d20566
child 777 92882ef2ab81
permissions -rw-r--r--
Fixed music fading for Ogg Vorbis, libmad, and FLAC.
     1 /*
     2   SDL_mixer:  An audio mixer library based on the SDL library
     3   Copyright (C) 1997-2017 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, 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 *src, int freesrc)
    71 {
    72     OGG_music *music;
    73     ov_callbacks callbacks;
    74 
    75     if ( !Mix_Init(MIX_INIT_OGG) ) {
    76         return(NULL);
    77     }
    78 
    79     SDL_memset(&callbacks, 0, sizeof(callbacks));
    80     callbacks.read_func = sdl_read_func;
    81     callbacks.seek_func = sdl_seek_func;
    82     callbacks.tell_func = sdl_tell_func;
    83 
    84     music = (OGG_music *)SDL_malloc(sizeof *music);
    85     if ( music ) {
    86         /* Initialize the music structure */
    87         SDL_memset(music, 0, (sizeof *music));
    88         music->src = src;
    89         music->freesrc = freesrc;
    90         OGG_stop(music);
    91         OGG_setvolume(music, MIX_MAX_VOLUME);
    92         music->section = -1;
    93 
    94         if ( vorbis.ov_open_callbacks(src, &music->vf, NULL, 0, callbacks) < 0 ) {
    95             SDL_SetError("Not an Ogg Vorbis audio stream");
    96             SDL_free(music);
    97             return(NULL);
    98         }
    99     } else {
   100         SDL_OutOfMemory();
   101         return(NULL);
   102     }
   103     return(music);
   104 }
   105 
   106 /* Start playback of a given OGG stream */
   107 void OGG_play(OGG_music *music)
   108 {
   109     music->playing = 1;
   110 }
   111 
   112 /* Return non-zero if a stream is currently playing */
   113 int OGG_playing(OGG_music *music)
   114 {
   115     return(music->playing);
   116 }
   117 
   118 /* Read some Ogg stream data and convert it for output */
   119 static void OGG_getsome(OGG_music *music)
   120 {
   121     int section;
   122     int len;
   123     char data[4096];
   124     SDL_AudioCVT *cvt;
   125 
   126 #ifdef OGG_USE_TREMOR
   127     len = vorbis.ov_read(&music->vf, data, sizeof(data), &section);
   128 #else
   129     len = vorbis.ov_read(&music->vf, data, sizeof(data), 0, 2, 1, &section);
   130 #endif
   131     if ( len <= 0 ) {
   132         if ( len == 0 ) {
   133             music->playing = 0;
   134         }
   135         return;
   136     }
   137     cvt = &music->cvt;
   138     if ( section != music->section ) {
   139         vorbis_info *vi;
   140 
   141         vi = vorbis.ov_info(&music->vf, -1);
   142         SDL_BuildAudioCVT(cvt, AUDIO_S16, vi->channels, vi->rate,
   143                                mixer.format,mixer.channels,mixer.freq);
   144         if ( cvt->buf ) {
   145             SDL_free(cvt->buf);
   146         }
   147         cvt->buf = (Uint8 *)SDL_malloc(sizeof(data)*cvt->len_mult);
   148         music->section = section;
   149     }
   150     if ( cvt->buf ) {
   151         SDL_memcpy(cvt->buf, data, len);
   152         if ( cvt->needed ) {
   153             cvt->len = len;
   154             SDL_ConvertAudio(cvt);
   155         } else {
   156             cvt->len_cvt = len;
   157         }
   158         music->len_available = music->cvt.len_cvt;
   159         music->snd_available = music->cvt.buf;
   160     } else {
   161         SDL_SetError("Out of memory");
   162         music->playing = 0;
   163     }
   164 }
   165 
   166 /* Play some of a stream previously started with OGG_play() */
   167 int OGG_playAudio(OGG_music *music, Uint8 *snd, int len)
   168 {
   169     int mixable;
   170 
   171     while ( (len > 0) && music->playing ) {
   172         if ( ! music->len_available ) {
   173             OGG_getsome(music);
   174         }
   175         mixable = len;
   176         if ( mixable > music->len_available ) {
   177             mixable = music->len_available;
   178         }
   179         if ( music->volume == MIX_MAX_VOLUME ) {
   180             SDL_memcpy(snd, music->snd_available, mixable);
   181         } else {
   182             SDL_MixAudioFormat(snd, music->snd_available, mixer.format, mixable, music->volume);
   183         }
   184         music->len_available -= mixable;
   185         music->snd_available += mixable;
   186         len -= mixable;
   187         snd += mixable;
   188     }
   189 
   190     return len;
   191 }
   192 
   193 /* Stop playback of a stream previously started with OGG_play() */
   194 void OGG_stop(OGG_music *music)
   195 {
   196     music->playing = 0;
   197 }
   198 
   199 /* Close the given OGG stream */
   200 void OGG_delete(OGG_music *music)
   201 {
   202     if ( music ) {
   203         if ( music->cvt.buf ) {
   204             SDL_free(music->cvt.buf);
   205         }
   206         if ( music->freesrc ) {
   207             SDL_RWclose(music->src);
   208         }
   209         vorbis.ov_clear(&music->vf);
   210         SDL_free(music);
   211     }
   212 }
   213 
   214 /* Jump (seek) to a given position (time is in seconds) */
   215 void OGG_jump_to_time(OGG_music *music, double time)
   216 {
   217 #ifdef OGG_USE_TREMOR
   218        vorbis.ov_time_seek( &music->vf, (ogg_int64_t)(time * 1000.0) );
   219 #else
   220        vorbis.ov_time_seek( &music->vf, time );
   221 #endif
   222 }
   223 
   224 #endif /* OGG_MUSIC */