music_ogg.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 12 May 2006 06:14:15 +0000
changeset 312 8a536296ef3d
parent 281 33730d0864d8
child 353 21fe62f9c4c6
permissions -rw-r--r--
Added support for dynamically loading Ogg Vorbis library
     1 /*
     2     SDL_mixer:  An audio mixer library based on the SDL library
     3     Copyright (C) 1997-2004 Sam Lantinga
     4 
     5     This library is free software; you can redistribute it and/or
     6     modify it under the terms of the GNU Library General Public
     7     License as published by the Free Software Foundation; either
     8     version 2 of the License, or (at your option) any later version.
     9 
    10     This library is distributed in the hope that it will be useful,
    11     but WITHOUT ANY WARRANTY; without even the implied warranty of
    12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
    13     Library General Public License for more details.
    14 
    15     You should have received a copy of the GNU Library General Public
    16     License along with this library; if not, write to the Free
    17     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
    18 
    19     Sam Lantinga
    20     slouken@libsdl.org
    21 */
    22 
    23 /* $Id$ */
    24 
    25 #ifdef OGG_MUSIC
    26 
    27 /* This file supports Ogg Vorbis music streams */
    28 
    29 #include <stdio.h>
    30 #include <stdlib.h>
    31 #include <string.h>
    32 
    33 #include "SDL_mixer.h"
    34 #include "dynamic_ogg.h"
    35 #include "music_ogg.h"
    36 
    37 /* This is the format of the audio mixer data */
    38 static SDL_AudioSpec mixer;
    39 
    40 /* Initialize the Ogg Vorbis player, with the given mixer settings
    41    This function returns 0, or -1 if there was an error.
    42  */
    43 int OGG_init(SDL_AudioSpec *mixerfmt)
    44 {
    45 	mixer = *mixerfmt;
    46 	return(0);
    47 }
    48 
    49 /* Set the volume for an OGG stream */
    50 void OGG_setvolume(OGG_music *music, int volume)
    51 {
    52 	music->volume = volume;
    53 }
    54 
    55 /* Load an OGG stream from the given file */
    56 OGG_music *OGG_new(const char *file)
    57 {
    58 	OGG_music *music;
    59 	FILE *fp;
    60 
    61 	music = (OGG_music *)malloc(sizeof *music);
    62 	if ( music ) {
    63 		/* Initialize the music structure */
    64 		memset(music, 0, (sizeof *music));
    65 		OGG_stop(music);
    66 		OGG_setvolume(music, MIX_MAX_VOLUME);
    67 		music->section = -1;
    68 
    69 		if ( Mix_InitOgg() < 0 ) {
    70 			return(NULL);
    71 		}
    72 		fp = fopen(file, "rb");
    73 		if ( fp == NULL ) {
    74 			free(music);
    75 			Mix_QuitOgg();
    76 			SDL_SetError("Couldn't open %s", file);
    77 			return(NULL);
    78 		}
    79 		if ( vorbis.ov_open(fp, &music->vf, NULL, 0) < 0 ) {
    80 			fclose(fp);
    81 			free(music);
    82 			Mix_QuitOgg();
    83 			SDL_SetError("Not an Ogg Vorbis audio stream");
    84 			return(NULL);
    85 		}
    86 	} else {
    87 		SDL_OutOfMemory();
    88 	}
    89 	return(music);
    90 }
    91 
    92 
    93 static size_t sdl_read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
    94 {
    95     return SDL_RWread((SDL_RWops*)datasource, ptr, size, nmemb);
    96 }
    97 
    98 static int sdl_seek_func(void *datasource, ogg_int64_t offset, int whence)
    99 {
   100     return SDL_RWseek((SDL_RWops*)datasource, (int)offset, whence);
   101 }
   102 
   103 static int sdl_close_func(void *datasource)
   104 {
   105     return SDL_RWclose((SDL_RWops*)datasource);
   106 }
   107 
   108 static long sdl_tell_func(void *datasource)
   109 {
   110     return SDL_RWtell((SDL_RWops*)datasource);
   111 }
   112 
   113 /* Load an OGG stream from an SDL_RWops object */
   114 OGG_music *OGG_new_RW(SDL_RWops *rw)
   115 {
   116 	OGG_music *music;
   117 	ov_callbacks callbacks;
   118 
   119 	callbacks.read_func = sdl_read_func;
   120 	callbacks.seek_func = sdl_seek_func;
   121 	callbacks.close_func = sdl_close_func;
   122 	callbacks.tell_func = sdl_tell_func;
   123 
   124 	music = (OGG_music *)malloc(sizeof *music);
   125 	if ( music ) {
   126 		/* Initialize the music structure */
   127 		memset(music, 0, (sizeof *music));
   128 		OGG_stop(music);
   129 		OGG_setvolume(music, MIX_MAX_VOLUME);
   130 		music->section = -1;
   131 
   132 		if ( Mix_InitOgg() < 0 ) {
   133 			return(NULL);
   134 		}
   135 		if ( vorbis.ov_open_callbacks(rw, &music->vf, NULL, 0, callbacks) < 0 ) {
   136 			free(music);
   137 			SDL_RWclose(rw);
   138 			Mix_QuitOgg();
   139 			SDL_SetError("Not an Ogg Vorbis audio stream");
   140 			return(NULL);
   141 		}
   142 	} else {
   143 		SDL_OutOfMemory();
   144 	}
   145 	return(music);
   146 }
   147 
   148 /* Start playback of a given OGG stream */
   149 void OGG_play(OGG_music *music)
   150 {
   151 	music->playing = 1;
   152 }
   153 
   154 /* Return non-zero if a stream is currently playing */
   155 int OGG_playing(OGG_music *music)
   156 {
   157 	return(music->playing);
   158 }
   159 
   160 /* Read some Ogg stream data and convert it for output */
   161 static void OGG_getsome(OGG_music *music)
   162 {
   163 	int section;
   164 	int len;
   165 	char data[4096];
   166 	SDL_AudioCVT *cvt;
   167 
   168 	len = vorbis.ov_read(&music->vf, data, sizeof(data), 0, 2, 1, &section);
   169 	if ( len <= 0 ) {
   170 		if ( len == 0 ) {
   171 			music->playing = 0;
   172 		}
   173 		return;
   174 	}
   175 	cvt = &music->cvt;
   176 	if ( section != music->section ) {
   177 		vorbis_info *vi;
   178 
   179 		vi = vorbis.ov_info(&music->vf, -1);
   180 		SDL_BuildAudioCVT(cvt, AUDIO_S16, vi->channels, vi->rate,
   181 		                       mixer.format,mixer.channels,mixer.freq);
   182 		if ( cvt->buf ) {
   183 			free(cvt->buf);
   184 		}
   185 		cvt->buf = (Uint8 *)malloc(sizeof(data)*cvt->len_mult);
   186 		music->section = section;
   187 	}
   188 	if ( cvt->buf ) {
   189 		memcpy(cvt->buf, data, len);
   190 		if ( cvt->needed ) {
   191 			cvt->len = len;
   192 			SDL_ConvertAudio(cvt);
   193 		} else {
   194 			cvt->len_cvt = len;
   195 		}
   196 		music->len_available = music->cvt.len_cvt;
   197 		music->snd_available = music->cvt.buf;
   198 	} else {
   199 		SDL_SetError("Out of memory");
   200 		music->playing = 0;
   201 	}
   202 }
   203 
   204 /* Play some of a stream previously started with OGG_play() */
   205 int OGG_playAudio(OGG_music *music, Uint8 *snd, int len)
   206 {
   207 	int mixable;
   208 
   209 	while ( (len > 0) && music->playing ) {
   210 		if ( ! music->len_available ) {
   211 			OGG_getsome(music);
   212 		}
   213 		mixable = len;
   214 		if ( mixable > music->len_available ) {
   215 			mixable = music->len_available;
   216 		}
   217 		if ( music->volume == MIX_MAX_VOLUME ) {
   218 			memcpy(snd, music->snd_available, mixable);
   219 		} else {
   220 			SDL_MixAudio(snd, music->snd_available, mixable,
   221 			                              music->volume);
   222 		}
   223 		music->len_available -= mixable;
   224 		music->snd_available += mixable;
   225 		len -= mixable;
   226 		snd += mixable;
   227 	}
   228 	
   229 	return len;
   230 }
   231 
   232 /* Stop playback of a stream previously started with OGG_play() */
   233 void OGG_stop(OGG_music *music)
   234 {
   235 	music->playing = 0;
   236 }
   237 
   238 /* Close the given OGG stream */
   239 void OGG_delete(OGG_music *music)
   240 {
   241 	if ( music ) {
   242 		if ( music->cvt.buf ) {
   243 			free(music->cvt.buf);
   244 		}
   245 		vorbis.ov_clear(&music->vf);
   246 		free(music);
   247 		Mix_QuitOgg();
   248 	}
   249 }
   250 
   251 /* Jump (seek) to a given position (time is in seconds) */
   252 void OGG_jump_to_time(OGG_music *music, double time)
   253 {
   254        vorbis.ov_time_seek( &music->vf, time );
   255 }
   256 
   257 #endif /* OGG_MUSIC */