music_ogg.c
author Ozkan Sezer <sezeroz@gmail.com>
Sun, 28 Oct 2018 09:28:02 +0300
branchSDL-1.2
changeset 920 6d8ba24cbbd6
parent 869 318285c30cff
permissions -rw-r--r--
ogg music: #define OV_EXCLUDE_STATIC_CALLBACKS
     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 #ifdef OGG_MUSIC
    23 
    24 /* This file supports Ogg Vorbis music streams */
    25 
    26 #include <stdio.h>
    27 #include <stdlib.h>
    28 #include <string.h>
    29 
    30 #include "SDL_mixer.h"
    31 #include "dynamic_ogg.h"
    32 #include "music_ogg.h"
    33 
    34 /* This is the format of the audio mixer data */
    35 static SDL_AudioSpec mixer;
    36 
    37 /* Initialize the Ogg Vorbis player, with the given mixer settings
    38    This function returns 0, or -1 if there was an error.
    39  */
    40 int OGG_init(SDL_AudioSpec *mixerfmt)
    41 {
    42 	mixer = *mixerfmt;
    43 	return(0);
    44 }
    45 
    46 /* Set the volume for an OGG stream */
    47 void OGG_setvolume(OGG_music *music, int volume)
    48 {
    49 	music->volume = volume;
    50 }
    51 
    52 static size_t sdl_read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
    53 {
    54     return SDL_RWread((SDL_RWops*)datasource, ptr, size, nmemb);
    55 }
    56 
    57 static int sdl_seek_func(void *datasource, ogg_int64_t offset, int whence)
    58 {
    59     return SDL_RWseek((SDL_RWops*)datasource, (int)offset, whence);
    60 }
    61 
    62 static long sdl_tell_func(void *datasource)
    63 {
    64     return SDL_RWtell((SDL_RWops*)datasource);
    65 }
    66 
    67 /* Load an OGG stream from an SDL_RWops object */
    68 OGG_music *OGG_new_RW(SDL_RWops *rw, int freerw)
    69 {
    70 	OGG_music *music;
    71 	ov_callbacks callbacks;
    72 
    73 	if ( !Mix_Init(MIX_INIT_OGG) ) {
    74 		if ( freerw ) {
    75 			SDL_RWclose(rw);
    76 		}
    77 		return(NULL);
    78 	}
    79 
    80 	SDL_memset(&callbacks, 0, sizeof(callbacks));
    81 	callbacks.read_func = sdl_read_func;
    82 	callbacks.seek_func = sdl_seek_func;
    83 	callbacks.tell_func = sdl_tell_func;
    84 
    85 	music = (OGG_music *)SDL_malloc(sizeof *music);
    86 	if ( music ) {
    87 		/* Initialize the music structure */
    88 		memset(music, 0, (sizeof *music));
    89 		music->rw = rw;
    90 		music->freerw = freerw;
    91 		OGG_stop(music);
    92 		OGG_setvolume(music, MIX_MAX_VOLUME);
    93 		music->section = -1;
    94 
    95 		if ( vorbis.ov_open_callbacks(rw, &music->vf, NULL, 0, callbacks) < 0 ) {
    96 			SDL_free(music);
    97 			if ( freerw ) {
    98 				SDL_RWclose(rw);
    99 			}
   100 			SDL_SetError("Not an Ogg Vorbis audio stream");
   101 			return(NULL);
   102 		}
   103 	} else {
   104 		if ( freerw ) {
   105 			SDL_RWclose(rw);
   106 		}
   107 		SDL_OutOfMemory();
   108 		return(NULL);
   109 	}
   110 	return(music);
   111 }
   112 
   113 /* Start playback of a given OGG stream */
   114 void OGG_play(OGG_music *music)
   115 {
   116 	music->playing = 1;
   117 }
   118 
   119 /* Return non-zero if a stream is currently playing */
   120 int OGG_playing(OGG_music *music)
   121 {
   122 	return(music->playing);
   123 }
   124 
   125 /* Read some Ogg stream data and convert it for output */
   126 static void OGG_getsome(OGG_music *music)
   127 {
   128 	int section;
   129 	int len;
   130 	char data[4096];
   131 	SDL_AudioCVT *cvt;
   132 
   133 #ifdef OGG_USE_TREMOR
   134 	len = vorbis.ov_read(&music->vf, data, sizeof(data), &section);
   135 #else
   136 	len = vorbis.ov_read(&music->vf, data, sizeof(data), 0, 2, 1, &section);
   137 #endif
   138 	if ( len <= 0 ) {
   139 		if ( len == 0 ) {
   140 			music->playing = 0;
   141 		}
   142 		return;
   143 	}
   144 	cvt = &music->cvt;
   145 	if ( section != music->section ) {
   146 		vorbis_info *vi;
   147 
   148 		vi = vorbis.ov_info(&music->vf, -1);
   149 		SDL_BuildAudioCVT(cvt, AUDIO_S16, vi->channels, vi->rate,
   150 		                       mixer.format,mixer.channels,mixer.freq);
   151 		if ( cvt->buf ) {
   152 			SDL_free(cvt->buf);
   153 		}
   154 		cvt->buf = (Uint8 *)SDL_malloc(sizeof(data)*cvt->len_mult);
   155 		music->section = section;
   156 	}
   157 	if ( cvt->buf ) {
   158 		memcpy(cvt->buf, data, len);
   159 		if ( cvt->needed ) {
   160 			cvt->len = len;
   161 			SDL_ConvertAudio(cvt);
   162 		} else {
   163 			cvt->len_cvt = len;
   164 		}
   165 		music->len_available = music->cvt.len_cvt;
   166 		music->snd_available = music->cvt.buf;
   167 	} else {
   168 		SDL_SetError("Out of memory");
   169 		music->playing = 0;
   170 	}
   171 }
   172 
   173 /* Play some of a stream previously started with OGG_play() */
   174 int OGG_playAudio(OGG_music *music, Uint8 *snd, int len)
   175 {
   176 	int mixable;
   177 
   178 	while ( (len > 0) && music->playing ) {
   179 		if ( ! music->len_available ) {
   180 			OGG_getsome(music);
   181 		}
   182 		mixable = len;
   183 		if ( mixable > music->len_available ) {
   184 			mixable = music->len_available;
   185 		}
   186 		if ( music->volume == MIX_MAX_VOLUME ) {
   187 			memcpy(snd, music->snd_available, mixable);
   188 		} else {
   189 			SDL_MixAudio(snd, music->snd_available, mixable,
   190 			                              music->volume);
   191 		}
   192 		music->len_available -= mixable;
   193 		music->snd_available += mixable;
   194 		len -= mixable;
   195 		snd += mixable;
   196 	}
   197 	
   198 	return len;
   199 }
   200 
   201 /* Stop playback of a stream previously started with OGG_play() */
   202 void OGG_stop(OGG_music *music)
   203 {
   204 	music->playing = 0;
   205 }
   206 
   207 /* Close the given OGG stream */
   208 void OGG_delete(OGG_music *music)
   209 {
   210 	if ( music ) {
   211 		if ( music->cvt.buf ) {
   212 			SDL_free(music->cvt.buf);
   213 		}
   214 		if ( music->freerw ) {
   215 			SDL_RWclose(music->rw);
   216 		}
   217 		vorbis.ov_clear(&music->vf);
   218 		SDL_free(music);
   219 	}
   220 }
   221 
   222 /* Jump (seek) to a given position (time is in seconds) */
   223 void OGG_jump_to_time(OGG_music *music, double time)
   224 {
   225 #ifdef OGG_USE_TREMOR
   226        vorbis.ov_time_seek( &music->vf, (ogg_int64_t)(time * 1000.0) );
   227 #else
   228        vorbis.ov_time_seek( &music->vf, time );
   229 #endif
   230 }
   231 
   232 #endif /* OGG_MUSIC */