music_ogg.c
author Sam Lantinga <slouken@libsdl.org>
Sat, 21 Oct 2017 14:40:31 -0700
changeset 823 bcd59adacdcc
parent 800 cc4aef7fef64
child 848 3907db698eb5
permissions -rw-r--r--
Fixed linker order so -lmingw32 comes before -lSDL2main
slouken@63
     1
/*
slouken@518
     2
  SDL_mixer:  An audio mixer library based on the SDL library
slouken@725
     3
  Copyright (C) 1997-2017 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@777
    22
#ifdef MUSIC_OGG
slouken@63
    23
slouken@63
    24
/* This file supports Ogg Vorbis music streams */
slouken@63
    25
slouken@777
    26
#include "SDL_loadso.h"
slouken@63
    27
slouken@63
    28
#include "music_ogg.h"
slouken@63
    29
slouken@777
    30
#if defined(OGG_HEADER)
slouken@777
    31
#include OGG_HEADER
slouken@777
    32
#elif defined(OGG_USE_TREMOR)
slouken@777
    33
#include <tremor/ivorbisfile.h>
slouken@777
    34
#else
slouken@777
    35
#include <vorbis/vorbisfile.h>
slouken@777
    36
#endif
slouken@63
    37
slouken@777
    38
typedef struct {
slouken@777
    39
    int loaded;
slouken@777
    40
    void *handle;
slouken@777
    41
    int (*ov_clear)(OggVorbis_File *vf);
slouken@777
    42
    vorbis_info *(*ov_info)(OggVorbis_File *vf,int link);
v-novichkov@795
    43
    vorbis_comment *(*ov_comment)(OggVorbis_File *vf,int link);
slouken@777
    44
    int (*ov_open_callbacks)(void *datasource, OggVorbis_File *vf, const char *initial, long ibytes, ov_callbacks callbacks);
slouken@777
    45
    ogg_int64_t (*ov_pcm_total)(OggVorbis_File *vf,int i);
slouken@777
    46
#ifdef OGG_USE_TREMOR
slouken@777
    47
    long (*ov_read)(OggVorbis_File *vf,char *buffer,int length, int *bitstream);
slouken@777
    48
#else
slouken@777
    49
    long (*ov_read)(OggVorbis_File *vf,char *buffer,int length, int bigendianp,int word,int sgned,int *bitstream);
slouken@777
    50
#endif
slouken@777
    51
#ifdef OGG_USE_TREMOR
slouken@777
    52
    int (*ov_time_seek)(OggVorbis_File *vf,ogg_int64_t pos);
slouken@777
    53
#else
slouken@777
    54
    int (*ov_time_seek)(OggVorbis_File *vf,double pos);
slouken@777
    55
#endif
v-novichkov@795
    56
    int (*ov_pcm_seek)(OggVorbis_File *vf, ogg_int64_t pos);
v-novichkov@795
    57
    ogg_int64_t (*ov_pcm_tell)(OggVorbis_File *vf);
slouken@777
    58
} vorbis_loader;
slouken@777
    59
slouken@777
    60
static vorbis_loader vorbis = {
slouken@777
    61
    0, NULL
slouken@777
    62
};
slouken@777
    63
slouken@777
    64
#ifdef OGG_DYNAMIC
slouken@798
    65
#define FUNCTION_LOADER(FUNC, SIG) \
slouken@798
    66
    vorbis.FUNC = (SIG) SDL_LoadFunction(vorbis.handle, #FUNC); \
slouken@798
    67
    if (vorbis.FUNC == NULL) { SDL_UnloadObject(vorbis.handle); return -1; }
slouken@798
    68
#else
slouken@798
    69
#define FUNCTION_LOADER(FUNC, SIG) \
slouken@798
    70
    vorbis.FUNC = FUNC;
slouken@798
    71
#endif
slouken@777
    72
slouken@777
    73
static int OGG_Load(void)
slouken@63
    74
{
slouken@777
    75
    if (vorbis.loaded == 0) {
slouken@798
    76
#ifdef OGG_DYNAMIC
slouken@777
    77
        vorbis.handle = SDL_LoadObject(OGG_DYNAMIC);
slouken@777
    78
        if (vorbis.handle == NULL) {
slouken@777
    79
            return -1;
slouken@777
    80
        }
slouken@798
    81
#elif defined(__MACOSX__)
slouken@798
    82
        extern int ov_open_callbacks(void*, OggVorbis_File*, const char*, long, ov_callbacks) __attribute__((weak_import));
slouken@798
    83
        if (ov_open_callbacks == NULL)
slouken@798
    84
        {
slouken@798
    85
            /* Missing weakly linked framework */
slouken@798
    86
            Mix_SetError("Missing Vorbis.framework");
slouken@777
    87
            return -1;
slouken@777
    88
        }
slouken@798
    89
#endif
slouken@798
    90
        FUNCTION_LOADER(ov_clear, int (*)(OggVorbis_File *))
slouken@798
    91
        FUNCTION_LOADER(ov_info, vorbis_info *(*)(OggVorbis_File *,int))
slouken@798
    92
        FUNCTION_LOADER(ov_comment, vorbis_comment *(*)(OggVorbis_File *,int))
slouken@798
    93
        FUNCTION_LOADER(ov_open_callbacks, int (*)(void *, OggVorbis_File *, const char *, long, ov_callbacks))
slouken@798
    94
        FUNCTION_LOADER(ov_pcm_total, ogg_int64_t (*)(OggVorbis_File *,int))
slouken@777
    95
#ifdef OGG_USE_TREMOR
slouken@800
    96
        FUNCTION_LOADER(ov_read, long (*)(OggVorbis_File *,char *,int,int *))
slouken@800
    97
        FUNCTION_LOADER(ov_time_seek, long (*)(OggVorbis_File *,ogg_int64_t))
slouken@777
    98
#else
slouken@800
    99
        FUNCTION_LOADER(ov_read, long (*)(OggVorbis_File *,char *,int,int,int,int,int *))
slouken@800
   100
        FUNCTION_LOADER(ov_time_seek, int (*)(OggVorbis_File *,double))
slouken@777
   101
#endif
slouken@798
   102
        FUNCTION_LOADER(ov_pcm_seek, int (*)(OggVorbis_File *,ogg_int64_t))
slouken@798
   103
        FUNCTION_LOADER(ov_pcm_tell, ogg_int64_t (*)(OggVorbis_File *))
slouken@777
   104
    }
slouken@777
   105
    ++vorbis.loaded;
slouken@777
   106
slouken@777
   107
    return 0;
slouken@63
   108
}
slouken@63
   109
slouken@777
   110
static void OGG_Unload(void)
slouken@63
   111
{
slouken@777
   112
    if (vorbis.loaded == 0) {
slouken@777
   113
        return;
slouken@777
   114
    }
slouken@777
   115
    if (vorbis.loaded == 1) {
slouken@798
   116
#ifdef OGG_DYNAMIC
slouken@777
   117
        SDL_UnloadObject(vorbis.handle);
slouken@798
   118
#endif
slouken@777
   119
    }
slouken@777
   120
    --vorbis.loaded;
slouken@63
   121
}
slouken@63
   122
slouken@777
   123
slouken@777
   124
typedef struct {
slouken@777
   125
    SDL_RWops *src;
slouken@777
   126
    int freesrc;
slouken@797
   127
    int play_count;
slouken@777
   128
    int volume;
slouken@777
   129
    OggVorbis_File vf;
slouken@797
   130
    vorbis_info vi;
slouken@777
   131
    int section;
slouken@797
   132
    SDL_AudioStream *stream;
slouken@797
   133
    char *buffer;
slouken@797
   134
    int buffer_size;
v-novichkov@795
   135
    int loop;
v-novichkov@795
   136
    ogg_int64_t loop_start;
v-novichkov@795
   137
    ogg_int64_t loop_end;
v-novichkov@795
   138
    ogg_int64_t loop_len;
v-novichkov@795
   139
    ogg_int64_t channels;
slouken@777
   140
} OGG_music;
slouken@777
   141
slouken@797
   142
slouken@797
   143
static int set_ov_error(const char *function, int error)
slouken@797
   144
{
slouken@797
   145
#define HANDLE_ERROR_CASE(X)    case X: Mix_SetError("%s: %s", function, #X); break;
slouken@797
   146
    switch (error) {
slouken@797
   147
    HANDLE_ERROR_CASE(OV_FALSE);
slouken@797
   148
    HANDLE_ERROR_CASE(OV_EOF);
slouken@797
   149
    HANDLE_ERROR_CASE(OV_HOLE);
slouken@797
   150
    HANDLE_ERROR_CASE(OV_EREAD);
slouken@797
   151
    HANDLE_ERROR_CASE(OV_EFAULT);
slouken@797
   152
    HANDLE_ERROR_CASE(OV_EIMPL);
slouken@797
   153
    HANDLE_ERROR_CASE(OV_EINVAL);
slouken@797
   154
    HANDLE_ERROR_CASE(OV_ENOTVORBIS);
slouken@797
   155
    HANDLE_ERROR_CASE(OV_EBADHEADER);
slouken@797
   156
    HANDLE_ERROR_CASE(OV_EVERSION);
slouken@797
   157
    HANDLE_ERROR_CASE(OV_ENOTAUDIO);
slouken@797
   158
    HANDLE_ERROR_CASE(OV_EBADPACKET);
slouken@797
   159
    HANDLE_ERROR_CASE(OV_EBADLINK);
slouken@797
   160
    HANDLE_ERROR_CASE(OV_ENOSEEK);
slouken@797
   161
    default:
slouken@797
   162
        Mix_SetError("%s: unknown error %d\n", function, error);
slouken@797
   163
        break;
slouken@797
   164
    }
slouken@797
   165
    return -1;
slouken@797
   166
}
slouken@797
   167
slouken@246
   168
static size_t sdl_read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
slouken@246
   169
{
slouken@246
   170
    return SDL_RWread((SDL_RWops*)datasource, ptr, size, nmemb);
slouken@246
   171
}
slouken@246
   172
slouken@690
   173
static int sdl_seek_func(void *datasource, ogg_int64_t offset, int whence)
slouken@246
   174
{
slouken@690
   175
    return (int)SDL_RWseek((SDL_RWops*)datasource, offset, whence);
slouken@246
   176
}
slouken@246
   177
slouken@690
   178
static long sdl_tell_func(void *datasource)
slouken@246
   179
{
slouken@690
   180
    return (long)SDL_RWtell((SDL_RWops*)datasource);
slouken@246
   181
}
slouken@246
   182
slouken@797
   183
static int OGG_Seek(void *context, double time);
slouken@797
   184
static void OGG_Delete(void *context);
slouken@797
   185
slouken@797
   186
static int OGG_UpdateSection(OGG_music *music)
slouken@797
   187
{
slouken@797
   188
    vorbis_info *vi;
slouken@797
   189
slouken@797
   190
    vi = vorbis.ov_info(&music->vf, -1);
slouken@797
   191
    if (!vi) {
slouken@797
   192
        Mix_SetError("ov_info returned NULL");
slouken@797
   193
        return -1;
slouken@797
   194
    }
slouken@797
   195
slouken@797
   196
    if (vi->channels == music->vi.channels && vi->rate == music->vi.rate) {
slouken@797
   197
        return 0;
slouken@797
   198
    }
slouken@797
   199
    SDL_memcpy(&music->vi, vi, sizeof(*vi));
slouken@797
   200
slouken@797
   201
    if (music->buffer) {
slouken@797
   202
        SDL_free(music->buffer);
slouken@797
   203
        music->buffer = NULL;
slouken@797
   204
    }
slouken@797
   205
slouken@797
   206
    if (music->stream) {
slouken@797
   207
        SDL_FreeAudioStream(music->stream);
slouken@797
   208
        music->stream = NULL;
slouken@797
   209
    }
slouken@797
   210
slouken@797
   211
    music->stream = SDL_NewAudioStream(AUDIO_S16, vi->channels, (int)vi->rate,
slouken@797
   212
                                       music_spec.format, music_spec.channels, music_spec.freq);
slouken@797
   213
    if (!music->stream) {
slouken@797
   214
        return -1;
slouken@797
   215
    }
slouken@797
   216
slouken@797
   217
    music->buffer_size = music_spec.samples * sizeof(Sint16) * vi->channels;
slouken@797
   218
    music->buffer = (char *)SDL_malloc(music->buffer_size);
slouken@797
   219
    if (!music->buffer) {
slouken@797
   220
        return -1;
slouken@797
   221
    }
slouken@797
   222
    return 0;
slouken@797
   223
}
slouken@797
   224
slouken@246
   225
/* Load an OGG stream from an SDL_RWops object */
slouken@777
   226
static void *OGG_CreateFromRW(SDL_RWops *src, int freesrc)
slouken@246
   227
{
slouken@617
   228
    OGG_music *music;
slouken@617
   229
    ov_callbacks callbacks;
slouken@797
   230
    vorbis_comment *vc;
slouken@797
   231
    int isLoopLength = 0, i;
slouken@797
   232
    ogg_int64_t fullLength;
slouken@246
   233
slouken@797
   234
    music = (OGG_music *)SDL_calloc(1, sizeof *music);
slouken@797
   235
    if (!music) {
slouken@797
   236
        SDL_OutOfMemory();
slouken@797
   237
        return NULL;
slouken@797
   238
    }
slouken@797
   239
    music->src = src;
slouken@797
   240
    music->volume = MIX_MAX_VOLUME;
slouken@797
   241
    music->section = -1;
slouken@797
   242
    music->loop = -1;
slouken@797
   243
    music->loop_start = -1;
slouken@797
   244
    music->loop_end = 0;
slouken@797
   245
    music->loop_len = 0;
slouken@797
   246
slouken@797
   247
    SDL_zero(callbacks);
slouken@617
   248
    callbacks.read_func = sdl_read_func;
slouken@617
   249
    callbacks.seek_func = sdl_seek_func;
slouken@617
   250
    callbacks.tell_func = sdl_tell_func;
slouken@246
   251
slouken@797
   252
    if (vorbis.ov_open_callbacks(src, &music->vf, NULL, 0, callbacks) < 0) {
slouken@797
   253
        SDL_SetError("Not an Ogg Vorbis audio stream");
slouken@797
   254
        SDL_free(music);
slouken@797
   255
        return NULL;
slouken@797
   256
    }
v-novichkov@795
   257
slouken@797
   258
    if (OGG_UpdateSection(music) < 0) {
slouken@797
   259
        OGG_Delete(music);
slouken@797
   260
        return NULL;
slouken@797
   261
    }
slouken@246
   262
slouken@797
   263
    vc = vorbis.ov_comment(&music->vf, -1);
slouken@797
   264
    for (i = 0; i < vc->comments; i++) {
slouken@797
   265
        char *param = SDL_strdup(vc->user_comments[i]);
slouken@797
   266
        char *argument = param;
slouken@797
   267
        char *value = SDL_strchr(param, '=');
slouken@797
   268
        if (value == NULL) {
slouken@797
   269
            value = param + SDL_strlen(param);
slouken@797
   270
        } else {
slouken@797
   271
            *(value++) = '\0';
slouken@617
   272
        }
v-novichkov@795
   273
slouken@797
   274
        if (SDL_strcasecmp(argument, "LOOPSTART") == 0)
slouken@797
   275
            music->loop_start = SDL_strtoull(value, NULL, 0);
slouken@797
   276
        else if (SDL_strcasecmp(argument, "LOOPLENGTH") == 0) {
slouken@797
   277
            music->loop_len = SDL_strtoull(value, NULL, 0);
slouken@797
   278
            isLoopLength = 1;
slouken@797
   279
        } else if (SDL_strcasecmp(argument, "LOOPEND") == 0) {
slouken@797
   280
            isLoopLength = 0;
slouken@797
   281
            music->loop_end = SDL_strtoull(value, NULL, 0);
slouken@797
   282
        }
slouken@797
   283
        SDL_free(param);
slouken@797
   284
    }
v-novichkov@795
   285
slouken@797
   286
    if (isLoopLength == 1) {
slouken@797
   287
        music->loop_end = music->loop_start + music->loop_len;
slouken@797
   288
    } else {
slouken@797
   289
        music->loop_len = music->loop_end - music->loop_start;
slouken@797
   290
    }
v-novichkov@795
   291
slouken@797
   292
    fullLength = vorbis.ov_pcm_total(&music->vf, -1);
slouken@797
   293
    if (((music->loop_start >= 0) || (music->loop_end > 0)) &&
slouken@797
   294
        ((music->loop_start < music->loop_end) || (music->loop_end == 0)) &&
slouken@797
   295
         (music->loop_start < fullLength) &&
slouken@797
   296
         (music->loop_end <= fullLength)) {
slouken@797
   297
        if (music->loop_start < 0) music->loop_start = 0;
slouken@797
   298
        if (music->loop_end == 0)  music->loop_end = fullLength;
slouken@797
   299
        music->loop = 1;
slouken@797
   300
    }
v-novichkov@795
   301
slouken@797
   302
    music->freesrc = freesrc;
slouken@777
   303
    return music;
slouken@777
   304
}
slouken@777
   305
slouken@777
   306
/* Set the volume for an OGG stream */
slouken@777
   307
static void OGG_SetVolume(void *context, int volume)
slouken@777
   308
{
slouken@777
   309
    OGG_music *music = (OGG_music *)context;
slouken@777
   310
    music->volume = volume;
slouken@246
   311
}
slouken@246
   312
slouken@63
   313
/* Start playback of a given OGG stream */
slouken@797
   314
static int OGG_Play(void *context, int play_count)
slouken@63
   315
{
slouken@777
   316
    OGG_music *music = (OGG_music *)context;
slouken@797
   317
    music->play_count = play_count;
slouken@797
   318
    return OGG_Seek(music, 0.0);
slouken@63
   319
}
slouken@63
   320
slouken@797
   321
/* Play some of a stream previously started with OGG_play() */
slouken@797
   322
static int OGG_GetSome(void *context, void *data, int bytes, SDL_bool *done)
slouken@63
   323
{
slouken@777
   324
    OGG_music *music = (OGG_music *)context;
slouken@797
   325
    SDL_bool looped = SDL_FALSE;
slouken@797
   326
    int filled, amount, result;
slouken@797
   327
    int section;
slouken@797
   328
    ogg_int64_t pcmPos;
slouken@63
   329
slouken@797
   330
    filled = SDL_AudioStreamGet(music->stream, data, bytes);
slouken@797
   331
    if (filled != 0) {
slouken@797
   332
        return filled;
slouken@797
   333
    }
slouken@63
   334
slouken@797
   335
    if (!music->play_count) {
slouken@797
   336
        /* All done */
slouken@797
   337
        *done = SDL_TRUE;
slouken@797
   338
        return 0;
slouken@797
   339
    }
slouken@797
   340
slouken@797
   341
    section = music->section;
slouken@353
   342
#ifdef OGG_USE_TREMOR
slouken@797
   343
    amount = vorbis.ov_read(&music->vf, music->buffer, music->buffer_size, &section);
slouken@353
   344
#else
slouken@797
   345
    amount = (int)vorbis.ov_read(&music->vf, music->buffer, music->buffer_size, 0, 2, 1, &section);
slouken@353
   346
#endif
slouken@797
   347
    if (amount < 0) {
slouken@797
   348
        set_ov_error("ov_read", amount);
slouken@797
   349
        return -1;
slouken@797
   350
    }
slouken@797
   351
slouken@797
   352
    if (section != music->section) {
slouken@797
   353
        music->section = section;
slouken@797
   354
        if (OGG_UpdateSection(music) < 0) {
slouken@797
   355
            return -1;
slouken@797
   356
        }
slouken@797
   357
    }
v-novichkov@795
   358
v-novichkov@795
   359
    pcmPos = vorbis.ov_pcm_tell(&music->vf);
v-novichkov@795
   360
    if ((music->loop == 1) && (pcmPos >= music->loop_end)) {
slouken@800
   361
        amount -= (int)((pcmPos - music->loop_end) * music->channels) * sizeof(Sint16);
slouken@797
   362
        result = vorbis.ov_pcm_seek(&music->vf, music->loop_start);
slouken@797
   363
        if (result < 0) {
slouken@797
   364
            set_ov_error("ov_pcm_seek", result);
slouken@797
   365
            return -1;
slouken@797
   366
        }
slouken@797
   367
        looped = SDL_TRUE;
v-novichkov@795
   368
    }
v-novichkov@795
   369
slouken@797
   370
    if (amount > 0) {
slouken@797
   371
        if (SDL_AudioStreamPut(music->stream, music->buffer, amount) < 0) {
slouken@797
   372
            return -1;
slouken@617
   373
        }
slouken@797
   374
    } else if (!looped) {
slouken@797
   375
        if (music->play_count == 1) {
slouken@797
   376
            music->play_count = 0;
slouken@797
   377
            SDL_AudioStreamFlush(music->stream);
slouken@797
   378
        } else {
slouken@797
   379
            int play_count = -1;
slouken@797
   380
            if (music->play_count > 0) {
slouken@797
   381
                play_count = (music->play_count - 1);
slouken@797
   382
            }
slouken@797
   383
            if (OGG_Play(music, play_count) < 0) {
slouken@797
   384
                return -1;
slouken@797
   385
            }
slouken@797
   386
        }
slouken@617
   387
    }
slouken@797
   388
    return 0;
slouken@63
   389
}
slouken@777
   390
static int OGG_GetAudio(void *context, void *data, int bytes)
slouken@63
   391
{
slouken@777
   392
    OGG_music *music = (OGG_music *)context;
slouken@797
   393
    return music_pcm_getaudio(context, data, bytes, music->volume, OGG_GetSome);
slouken@63
   394
}
slouken@63
   395
slouken@777
   396
/* Jump (seek) to a given position (time is in seconds) */
slouken@777
   397
static int OGG_Seek(void *context, double time)
slouken@777
   398
{
slouken@777
   399
    OGG_music *music = (OGG_music *)context;
slouken@797
   400
    int result;
slouken@777
   401
#ifdef OGG_USE_TREMOR
slouken@797
   402
    result = vorbis.ov_time_seek(&music->vf, (ogg_int64_t)(time * 1000.0));
slouken@777
   403
#else
slouken@797
   404
    result = vorbis.ov_time_seek(&music->vf, time);
slouken@777
   405
#endif
slouken@797
   406
    if (result < 0) {
slouken@797
   407
        return set_ov_error("ov_time_seek", result);
slouken@797
   408
    }
slouken@777
   409
    return 0;
slouken@777
   410
}
slouken@777
   411
slouken@63
   412
/* Close the given OGG stream */
slouken@777
   413
static void OGG_Delete(void *context)
slouken@63
   414
{
slouken@777
   415
    OGG_music *music = (OGG_music *)context;
slouken@797
   416
    vorbis.ov_clear(&music->vf);
slouken@797
   417
    if (music->stream) {
slouken@797
   418
        SDL_FreeAudioStream(music->stream);
slouken@617
   419
    }
slouken@797
   420
    if (music->buffer) {
slouken@797
   421
        SDL_free(music->buffer);
slouken@797
   422
    }
slouken@797
   423
    if (music->freesrc) {
slouken@797
   424
        SDL_RWclose(music->src);
slouken@797
   425
    }
slouken@797
   426
    SDL_free(music);
slouken@63
   427
}
slouken@63
   428
slouken@777
   429
Mix_MusicInterface Mix_MusicInterface_OGG =
slouken@155
   430
{
slouken@777
   431
    "OGG",
slouken@777
   432
    MIX_MUSIC_OGG,
slouken@777
   433
    MUS_OGG,
slouken@777
   434
    SDL_FALSE,
slouken@777
   435
    SDL_FALSE,
slouken@155
   436
slouken@777
   437
    OGG_Load,
slouken@777
   438
    NULL,   /* Open */
slouken@777
   439
    OGG_CreateFromRW,
slouken@777
   440
    NULL,   /* CreateFromFile */
slouken@777
   441
    OGG_SetVolume,
slouken@777
   442
    OGG_Play,
slouken@797
   443
    NULL,   /* IsPlaying */
slouken@777
   444
    OGG_GetAudio,
slouken@777
   445
    OGG_Seek,
slouken@777
   446
    NULL,   /* Pause */
slouken@777
   447
    NULL,   /* Resume */
slouken@797
   448
    NULL,   /* Stop */
slouken@777
   449
    OGG_Delete,
slouken@777
   450
    NULL,   /* Close */
slouken@777
   451
    OGG_Unload,
slouken@777
   452
};
slouken@777
   453
slouken@777
   454
#endif /* MUSIC_OGG */
slouken@777
   455
slouken@777
   456
/* vi: set ts=4 sw=4 expandtab: */