music_ogg.c
author Sam Lantinga <slouken@libsdl.org>
Fri, 13 Jan 2012 03:15:19 -0500
changeset 561 87bdb4c81c0b
parent 545 32e5ed415a34
child 601 05123263dab3
child 869 318285c30cff
permissions -rw-r--r--
Fixed memory crash loading Ogg Vorbis files on Windows
The pointer to the audio data could come from SDL_LoadWAV_RW() or from our other loaders, and we need to use the correct free() to release the memory. So we'll just use the SDL memory functions for consistency.
This pretty much only matters on Windows where we can't guarantee a certain C runtime so we provide our own malloc() and friends.
slouken@63
     1
/*
slouken@518
     2
  SDL_mixer:  An audio mixer library based on the SDL library
slouken@518
     3
  Copyright (C) 1997-2012 Sam Lantinga <slouken@libsdl.org>
slouken@63
     4
slouken@518
     5
  This software is provided 'as-is', without any express or implied
slouken@518
     6
  warranty.  In no event will the authors be held liable for any damages
slouken@518
     7
  arising from the use of this software.
slouken@63
     8
slouken@518
     9
  Permission is granted to anyone to use this software for any purpose,
slouken@518
    10
  including commercial applications, and to alter it and redistribute it
slouken@518
    11
  freely, subject to the following restrictions:
slouken@63
    12
slouken@518
    13
  1. The origin of this software must not be misrepresented; you must not
slouken@518
    14
     claim that you wrote the original software. If you use this software
slouken@518
    15
     in a product, an acknowledgment in the product documentation would be
slouken@518
    16
     appreciated but is not required.
slouken@518
    17
  2. Altered source versions must be plainly marked as such, and must not be
slouken@518
    18
     misrepresented as being the original software.
slouken@518
    19
  3. This notice may not be removed or altered from any source distribution.
slouken@63
    20
*/
slouken@63
    21
slouken@140
    22
/* $Id$ */
slouken@138
    23
slouken@63
    24
#ifdef OGG_MUSIC
slouken@63
    25
slouken@63
    26
/* This file supports Ogg Vorbis music streams */
slouken@63
    27
slouken@63
    28
#include <stdio.h>
slouken@152
    29
#include <stdlib.h>
slouken@63
    30
#include <string.h>
slouken@63
    31
slouken@63
    32
#include "SDL_mixer.h"
slouken@312
    33
#include "dynamic_ogg.h"
slouken@63
    34
#include "music_ogg.h"
slouken@63
    35
slouken@63
    36
/* This is the format of the audio mixer data */
slouken@63
    37
static SDL_AudioSpec mixer;
slouken@63
    38
slouken@63
    39
/* Initialize the Ogg Vorbis player, with the given mixer settings
slouken@63
    40
   This function returns 0, or -1 if there was an error.
slouken@63
    41
 */
slouken@63
    42
int OGG_init(SDL_AudioSpec *mixerfmt)
slouken@63
    43
{
slouken@63
    44
	mixer = *mixerfmt;
slouken@63
    45
	return(0);
slouken@63
    46
}
slouken@63
    47
slouken@63
    48
/* Set the volume for an OGG stream */
slouken@63
    49
void OGG_setvolume(OGG_music *music, int volume)
slouken@63
    50
{
slouken@63
    51
	music->volume = volume;
slouken@63
    52
}
slouken@63
    53
slouken@246
    54
static size_t sdl_read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
slouken@246
    55
{
slouken@246
    56
    return SDL_RWread((SDL_RWops*)datasource, ptr, size, nmemb);
slouken@246
    57
}
slouken@246
    58
slouken@246
    59
static int sdl_seek_func(void *datasource, ogg_int64_t offset, int whence)
slouken@246
    60
{
slouken@246
    61
    return SDL_RWseek((SDL_RWops*)datasource, (int)offset, whence);
slouken@246
    62
}
slouken@246
    63
slouken@246
    64
static long sdl_tell_func(void *datasource)
slouken@246
    65
{
slouken@246
    66
    return SDL_RWtell((SDL_RWops*)datasource);
slouken@246
    67
}
slouken@246
    68
slouken@246
    69
/* Load an OGG stream from an SDL_RWops object */
slouken@521
    70
OGG_music *OGG_new_RW(SDL_RWops *rw, int freerw)
slouken@246
    71
{
slouken@246
    72
	OGG_music *music;
slouken@246
    73
	ov_callbacks callbacks;
slouken@246
    74
slouken@545
    75
	if ( !Mix_Init(MIX_INIT_OGG) ) {
slouken@545
    76
		if ( freerw ) {
slouken@545
    77
			SDL_RWclose(rw);
slouken@545
    78
		}
slouken@545
    79
		return(NULL);
slouken@545
    80
	}
slouken@545
    81
slouken@521
    82
	SDL_memset(&callbacks, 0, sizeof(callbacks));
slouken@246
    83
	callbacks.read_func = sdl_read_func;
slouken@246
    84
	callbacks.seek_func = sdl_seek_func;
slouken@246
    85
	callbacks.tell_func = sdl_tell_func;
slouken@246
    86
slouken@561
    87
	music = (OGG_music *)SDL_malloc(sizeof *music);
slouken@246
    88
	if ( music ) {
slouken@246
    89
		/* Initialize the music structure */
slouken@246
    90
		memset(music, 0, (sizeof *music));
slouken@521
    91
		music->rw = rw;
slouken@521
    92
		music->freerw = freerw;
slouken@246
    93
		OGG_stop(music);
slouken@246
    94
		OGG_setvolume(music, MIX_MAX_VOLUME);
slouken@246
    95
		music->section = -1;
slouken@246
    96
slouken@312
    97
		if ( vorbis.ov_open_callbacks(rw, &music->vf, NULL, 0, callbacks) < 0 ) {
slouken@561
    98
			SDL_free(music);
slouken@521
    99
			if ( freerw ) {
slouken@521
   100
				SDL_RWclose(rw);
slouken@521
   101
			}
slouken@312
   102
			SDL_SetError("Not an Ogg Vorbis audio stream");
slouken@246
   103
			return(NULL);
slouken@246
   104
		}
slouken@246
   105
	} else {
slouken@521
   106
		if ( freerw ) {
slouken@521
   107
			SDL_RWclose(rw);
slouken@521
   108
		}
slouken@312
   109
		SDL_OutOfMemory();
slouken@521
   110
		return(NULL);
slouken@246
   111
	}
slouken@246
   112
	return(music);
slouken@246
   113
}
slouken@246
   114
slouken@63
   115
/* Start playback of a given OGG stream */
slouken@63
   116
void OGG_play(OGG_music *music)
slouken@63
   117
{
slouken@63
   118
	music->playing = 1;
slouken@63
   119
}
slouken@63
   120
slouken@63
   121
/* Return non-zero if a stream is currently playing */
slouken@63
   122
int OGG_playing(OGG_music *music)
slouken@63
   123
{
slouken@63
   124
	return(music->playing);
slouken@63
   125
}
slouken@63
   126
slouken@63
   127
/* Read some Ogg stream data and convert it for output */
slouken@63
   128
static void OGG_getsome(OGG_music *music)
slouken@63
   129
{
slouken@63
   130
	int section;
slouken@63
   131
	int len;
slouken@63
   132
	char data[4096];
slouken@63
   133
	SDL_AudioCVT *cvt;
slouken@63
   134
slouken@353
   135
#ifdef OGG_USE_TREMOR
slouken@353
   136
	len = vorbis.ov_read(&music->vf, data, sizeof(data), &section);
slouken@353
   137
#else
slouken@312
   138
	len = vorbis.ov_read(&music->vf, data, sizeof(data), 0, 2, 1, &section);
slouken@353
   139
#endif
slouken@63
   140
	if ( len <= 0 ) {
slouken@63
   141
		if ( len == 0 ) {
slouken@63
   142
			music->playing = 0;
slouken@63
   143
		}
slouken@63
   144
		return;
slouken@63
   145
	}
slouken@63
   146
	cvt = &music->cvt;
slouken@63
   147
	if ( section != music->section ) {
slouken@63
   148
		vorbis_info *vi;
slouken@63
   149
slouken@312
   150
		vi = vorbis.ov_info(&music->vf, -1);
slouken@63
   151
		SDL_BuildAudioCVT(cvt, AUDIO_S16, vi->channels, vi->rate,
slouken@63
   152
		                       mixer.format,mixer.channels,mixer.freq);
slouken@63
   153
		if ( cvt->buf ) {
slouken@561
   154
			SDL_free(cvt->buf);
slouken@63
   155
		}
slouken@561
   156
		cvt->buf = (Uint8 *)SDL_malloc(sizeof(data)*cvt->len_mult);
slouken@63
   157
		music->section = section;
slouken@63
   158
	}
slouken@63
   159
	if ( cvt->buf ) {
slouken@63
   160
		memcpy(cvt->buf, data, len);
slouken@63
   161
		if ( cvt->needed ) {
slouken@63
   162
			cvt->len = len;
slouken@63
   163
			SDL_ConvertAudio(cvt);
slouken@63
   164
		} else {
slouken@63
   165
			cvt->len_cvt = len;
slouken@63
   166
		}
slouken@63
   167
		music->len_available = music->cvt.len_cvt;
slouken@63
   168
		music->snd_available = music->cvt.buf;
slouken@63
   169
	} else {
slouken@93
   170
		SDL_SetError("Out of memory");
slouken@63
   171
		music->playing = 0;
slouken@63
   172
	}
slouken@63
   173
}
slouken@63
   174
slouken@63
   175
/* Play some of a stream previously started with OGG_play() */
icculus@281
   176
int OGG_playAudio(OGG_music *music, Uint8 *snd, int len)
slouken@63
   177
{
slouken@63
   178
	int mixable;
slouken@63
   179
slouken@63
   180
	while ( (len > 0) && music->playing ) {
slouken@63
   181
		if ( ! music->len_available ) {
slouken@63
   182
			OGG_getsome(music);
slouken@63
   183
		}
slouken@63
   184
		mixable = len;
slouken@63
   185
		if ( mixable > music->len_available ) {
slouken@63
   186
			mixable = music->len_available;
slouken@63
   187
		}
slouken@63
   188
		if ( music->volume == MIX_MAX_VOLUME ) {
slouken@63
   189
			memcpy(snd, music->snd_available, mixable);
slouken@63
   190
		} else {
slouken@63
   191
			SDL_MixAudio(snd, music->snd_available, mixable,
slouken@63
   192
			                              music->volume);
slouken@63
   193
		}
slouken@63
   194
		music->len_available -= mixable;
slouken@63
   195
		music->snd_available += mixable;
slouken@63
   196
		len -= mixable;
slouken@63
   197
		snd += mixable;
slouken@63
   198
	}
icculus@281
   199
	
icculus@281
   200
	return len;
slouken@63
   201
}
slouken@63
   202
slouken@63
   203
/* Stop playback of a stream previously started with OGG_play() */
slouken@63
   204
void OGG_stop(OGG_music *music)
slouken@63
   205
{
slouken@63
   206
	music->playing = 0;
slouken@63
   207
}
slouken@63
   208
slouken@63
   209
/* Close the given OGG stream */
slouken@63
   210
void OGG_delete(OGG_music *music)
slouken@63
   211
{
slouken@63
   212
	if ( music ) {
slouken@63
   213
		if ( music->cvt.buf ) {
slouken@561
   214
			SDL_free(music->cvt.buf);
slouken@63
   215
		}
slouken@521
   216
		if ( music->freerw ) {
slouken@521
   217
			SDL_RWclose(music->rw);
slouken@521
   218
		}
slouken@312
   219
		vorbis.ov_clear(&music->vf);
slouken@561
   220
		SDL_free(music);
slouken@63
   221
	}
slouken@63
   222
}
slouken@63
   223
slouken@155
   224
/* Jump (seek) to a given position (time is in seconds) */
slouken@155
   225
void OGG_jump_to_time(OGG_music *music, double time)
slouken@155
   226
{
slouken@505
   227
#ifdef OGG_USE_TREMOR
slouken@505
   228
       vorbis.ov_time_seek( &music->vf, (ogg_int64_t)time );
slouken@505
   229
#else
slouken@312
   230
       vorbis.ov_time_seek( &music->vf, time );
slouken@505
   231
#endif
slouken@155
   232
}
slouken@155
   233
slouken@63
   234
#endif /* OGG_MUSIC */