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