music_ogg.c
author Ryan C. Gordon <icculus@icculus.org>
Sat, 19 Nov 2005 18:43:48 +0000
changeset 281 33730d0864d8
parent 246 9fa5d0f9d042
child 312 8a536296ef3d
permissions -rw-r--r--
From: Gabriel <mystml@adinet.com.uy>
To: SDL Mailing List <sdl@libsdl.org>
Date: Thu, 13 Oct 2005 15:00:22 -0300
Subject: [SDL] [PATCH] Correct OGG music looping

Bug description : looping music when playing .ogg files left an audible
gap in the mixer buffer.

Bug fix : OGG_playAudio() now returns the actual read length.
music_mixer restarts the music and calls OGG_playAudio() again if it
didn't fill the buffer.

Notes : Tested in Linux and works fine. Since there's no platform
dependent code involved, it should work everywhere else.

A similar fix for the rest of the music formats should be easy, but
since I can't test it at the time, I opted not to do it.

--Gabriel

--
Gabriel Gambetta
Mystery Studio - http://www.mysterystudio.com
Gabriel on Graphics - http://gabrielongraphics.blogspot.com
     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 "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 /* Load an OGG stream from the given file */
    55 OGG_music *OGG_new(const char *file)
    56 {
    57 	OGG_music *music;
    58 	FILE *fp;
    59 
    60 	music = (OGG_music *)malloc(sizeof *music);
    61 	if ( music ) {
    62 		/* Initialize the music structure */
    63 		memset(music, 0, (sizeof *music));
    64 		OGG_stop(music);
    65 		OGG_setvolume(music, MIX_MAX_VOLUME);
    66 		music->section = -1;
    67 
    68 		fp = fopen(file, "rb");
    69 		if ( fp == NULL ) {
    70 			SDL_SetError("Couldn't open %s", file);
    71 			free(music);
    72 			return(NULL);
    73 		}
    74 		if ( ov_open(fp, &music->vf, NULL, 0) < 0 ) {
    75 			SDL_SetError("Not an Ogg Vorbis audio stream");
    76 			free(music);
    77 			fclose(fp);
    78 			return(NULL);
    79 		}
    80 	} else {
    81 		SDL_SetError("Out of memory");
    82 	}
    83 	return(music);
    84 }
    85 
    86 
    87 static size_t sdl_read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
    88 {
    89     return SDL_RWread((SDL_RWops*)datasource, ptr, size, nmemb);
    90 }
    91 
    92 static int sdl_seek_func(void *datasource, ogg_int64_t offset, int whence)
    93 {
    94     return SDL_RWseek((SDL_RWops*)datasource, (int)offset, whence);
    95 }
    96 
    97 static int sdl_close_func(void *datasource)
    98 {
    99     return SDL_RWclose((SDL_RWops*)datasource);
   100 }
   101 
   102 static long sdl_tell_func(void *datasource)
   103 {
   104     return SDL_RWtell((SDL_RWops*)datasource);
   105 }
   106 
   107 /* Load an OGG stream from an SDL_RWops object */
   108 OGG_music *OGG_new_RW(SDL_RWops *rw)
   109 {
   110 	OGG_music *music;
   111 	ov_callbacks callbacks;
   112 
   113 	callbacks.read_func = sdl_read_func;
   114 	callbacks.seek_func = sdl_seek_func;
   115 	callbacks.close_func = sdl_close_func;
   116 	callbacks.tell_func = sdl_tell_func;
   117 
   118 	music = (OGG_music *)malloc(sizeof *music);
   119 	if ( music ) {
   120 		/* Initialize the music structure */
   121 		memset(music, 0, (sizeof *music));
   122 		OGG_stop(music);
   123 		OGG_setvolume(music, MIX_MAX_VOLUME);
   124 		music->section = -1;
   125 
   126 		if ( ov_open_callbacks(rw, &music->vf, NULL, 0, callbacks) < 0 ) {
   127 			SDL_SetError("Not an Ogg Vorbis audio stream");
   128 			free(music);
   129 			SDL_RWclose(rw);
   130 			return(NULL);
   131 		}
   132 	} else {
   133 		SDL_SetError("Out of memory");
   134 	}
   135 	return(music);
   136 }
   137 
   138 /* Start playback of a given OGG stream */
   139 void OGG_play(OGG_music *music)
   140 {
   141 	music->playing = 1;
   142 }
   143 
   144 /* Return non-zero if a stream is currently playing */
   145 int OGG_playing(OGG_music *music)
   146 {
   147 	return(music->playing);
   148 }
   149 
   150 /* Read some Ogg stream data and convert it for output */
   151 static void OGG_getsome(OGG_music *music)
   152 {
   153 	int section;
   154 	int len;
   155 	char data[4096];
   156 	SDL_AudioCVT *cvt;
   157 
   158 	len = ov_read(&music->vf, data, sizeof(data), 0, 2, 1, &section);
   159 	if ( len <= 0 ) {
   160 		if ( len == 0 ) {
   161 			music->playing = 0;
   162 		}
   163 		return;
   164 	}
   165 	cvt = &music->cvt;
   166 	if ( section != music->section ) {
   167 		vorbis_info *vi;
   168 
   169 		vi = ov_info(&music->vf, -1);
   170 		SDL_BuildAudioCVT(cvt, AUDIO_S16, vi->channels, vi->rate,
   171 		                       mixer.format,mixer.channels,mixer.freq);
   172 		if ( cvt->buf ) {
   173 			free(cvt->buf);
   174 		}
   175 		cvt->buf = (Uint8 *)malloc(sizeof(data)*cvt->len_mult);
   176 		music->section = section;
   177 	}
   178 	if ( cvt->buf ) {
   179 		memcpy(cvt->buf, data, len);
   180 		if ( cvt->needed ) {
   181 			cvt->len = len;
   182 			SDL_ConvertAudio(cvt);
   183 		} else {
   184 			cvt->len_cvt = len;
   185 		}
   186 		music->len_available = music->cvt.len_cvt;
   187 		music->snd_available = music->cvt.buf;
   188 	} else {
   189 		SDL_SetError("Out of memory");
   190 		music->playing = 0;
   191 	}
   192 }
   193 
   194 /* Play some of a stream previously started with OGG_play() */
   195 int OGG_playAudio(OGG_music *music, Uint8 *snd, int len)
   196 {
   197 	int mixable;
   198 
   199 	while ( (len > 0) && music->playing ) {
   200 		if ( ! music->len_available ) {
   201 			OGG_getsome(music);
   202 		}
   203 		mixable = len;
   204 		if ( mixable > music->len_available ) {
   205 			mixable = music->len_available;
   206 		}
   207 		if ( music->volume == MIX_MAX_VOLUME ) {
   208 			memcpy(snd, music->snd_available, mixable);
   209 		} else {
   210 			SDL_MixAudio(snd, music->snd_available, mixable,
   211 			                              music->volume);
   212 		}
   213 		music->len_available -= mixable;
   214 		music->snd_available += mixable;
   215 		len -= mixable;
   216 		snd += mixable;
   217 	}
   218 	
   219 	return len;
   220 }
   221 
   222 /* Stop playback of a stream previously started with OGG_play() */
   223 void OGG_stop(OGG_music *music)
   224 {
   225 	music->playing = 0;
   226 }
   227 
   228 /* Close the given OGG stream */
   229 void OGG_delete(OGG_music *music)
   230 {
   231 	if ( music ) {
   232 		if ( music->cvt.buf ) {
   233 			free(music->cvt.buf);
   234 		}
   235 		ov_clear(&music->vf);
   236 		free(music);
   237 	}
   238 }
   239 
   240 /* Jump (seek) to a given position (time is in seconds) */
   241 void OGG_jump_to_time(OGG_music *music, double time)
   242 {
   243        ov_time_seek( &music->vf, time );
   244 }
   245 
   246 #endif /* OGG_MUSIC */