music_ogg.c
author Sam Lantinga <slouken@libsdl.org>
Sun, 15 Jul 2007 03:38:33 +0000
changeset 353 21fe62f9c4c6
parent 312 8a536296ef3d
child 354 e97cfc344dc3
permissions -rw-r--r--
Fixed bug #297

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