music_ogg.c
author Ozkan Sezer <sezeroz@gmail.com>
Sun, 07 Oct 2018 00:08:00 +0300
branchSDL-1.2
changeset 871 284d10d87cc9
parent 869 318285c30cff
child 920 6d8ba24cbbd6
permissions -rw-r--r--
backport fix for bug #1833. (from 2.0 branch commit 7ead8213dfb0).
     1 /*
     2   SDL_mixer:  An audio mixer library based on the SDL library
     3   Copyright (C) 1997-2012 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 SDL_RWseek((SDL_RWops*)datasource, (int)offset, whence);
    62 }
    63 
    64 static long sdl_tell_func(void *datasource)
    65 {
    66     return 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 		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 		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 			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 */