music_ogg.c
author v-novichkov
Wed, 18 Oct 2017 19:48:42 +0300
changeset 795 bd5fa0d4f34a
parent 779 a2b494c054d5
child 797 b4b6adff699a
permissions -rw-r--r--
Add support for Vorbis comments based loop tags in OGG files

LOOPSTART - loop start PCM sample position
LOOPEND - loop end PCM sample position
LOOPLENGTH - loop length PCM sample position (alternative to LOOPEND, added for compatibility with RPG Maker targeted OGG files)
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@777
    65
slouken@777
    66
static int OGG_Load(void)
slouken@63
    67
{
slouken@777
    68
    if (vorbis.loaded == 0) {
slouken@777
    69
        vorbis.handle = SDL_LoadObject(OGG_DYNAMIC);
slouken@777
    70
        if (vorbis.handle == NULL) {
slouken@777
    71
            return -1;
slouken@777
    72
        }
slouken@777
    73
        vorbis.ov_clear =
slouken@777
    74
            (int (*)(OggVorbis_File *))
slouken@777
    75
            SDL_LoadFunction(vorbis.handle, "ov_clear");
slouken@777
    76
        if (vorbis.ov_clear == NULL) {
slouken@777
    77
            SDL_UnloadObject(vorbis.handle);
slouken@777
    78
            return -1;
slouken@777
    79
        }
slouken@777
    80
        vorbis.ov_info =
slouken@777
    81
            (vorbis_info *(*)(OggVorbis_File *,int))
slouken@777
    82
            SDL_LoadFunction(vorbis.handle, "ov_info");
slouken@777
    83
        if (vorbis.ov_info == NULL) {
slouken@777
    84
            SDL_UnloadObject(vorbis.handle);
slouken@777
    85
            return -1;
slouken@777
    86
        }
v-novichkov@795
    87
        vorbis.ov_comment =
v-novichkov@795
    88
            (vorbis_comment *(*)(OggVorbis_File *,int))
v-novichkov@795
    89
            SDL_LoadFunction(vorbis.handle, "ov_comment");
v-novichkov@795
    90
        if (vorbis.ov_comment == NULL) {
v-novichkov@795
    91
            SDL_UnloadObject(vorbis.handle);
v-novichkov@795
    92
            return -1;
v-novichkov@795
    93
        }
slouken@777
    94
        vorbis.ov_open_callbacks =
slouken@777
    95
            (int (*)(void *, OggVorbis_File *, const char *, long, ov_callbacks))
slouken@777
    96
            SDL_LoadFunction(vorbis.handle, "ov_open_callbacks");
slouken@777
    97
        if (vorbis.ov_open_callbacks == NULL) {
slouken@777
    98
            SDL_UnloadObject(vorbis.handle);
slouken@777
    99
            return -1;
slouken@777
   100
        }
slouken@777
   101
        vorbis.ov_pcm_total =
slouken@777
   102
            (ogg_int64_t (*)(OggVorbis_File *,int))
slouken@777
   103
            SDL_LoadFunction(vorbis.handle, "ov_pcm_total");
slouken@777
   104
        if (vorbis.ov_pcm_total == NULL) {
slouken@777
   105
            SDL_UnloadObject(vorbis.handle);
slouken@777
   106
            return -1;
slouken@777
   107
        }
slouken@777
   108
        vorbis.ov_read =
slouken@777
   109
#ifdef OGG_USE_TREMOR
slouken@777
   110
            (long (*)(OggVorbis_File *,char *,int,int *))
slouken@777
   111
#else
slouken@777
   112
            (long (*)(OggVorbis_File *,char *,int,int,int,int,int *))
slouken@777
   113
#endif
slouken@777
   114
            SDL_LoadFunction(vorbis.handle, "ov_read");
slouken@777
   115
        if (vorbis.ov_read == NULL) {
slouken@777
   116
            SDL_UnloadObject(vorbis.handle);
slouken@777
   117
            return -1;
slouken@777
   118
        }
slouken@777
   119
        vorbis.ov_time_seek =
slouken@777
   120
#ifdef OGG_USE_TREMOR
slouken@777
   121
            (long (*)(OggVorbis_File *,ogg_int64_t))
slouken@777
   122
#else
slouken@777
   123
            (int (*)(OggVorbis_File *,double))
slouken@777
   124
#endif
slouken@777
   125
            SDL_LoadFunction(vorbis.handle, "ov_time_seek");
slouken@777
   126
        if (vorbis.ov_time_seek == NULL) {
slouken@777
   127
            SDL_UnloadObject(vorbis.handle);
slouken@777
   128
            return -1;
slouken@777
   129
        }
v-novichkov@795
   130
        vorbis.ov_pcm_seek =
v-novichkov@795
   131
            (int (*)(OggVorbis_File *,ogg_int64_t))
v-novichkov@795
   132
            SDL_LoadFunction(vorbis.handle, "ov_pcm_seek");
v-novichkov@795
   133
        if (vorbis.ov_pcm_seek == NULL) {
v-novichkov@795
   134
            SDL_UnloadObject(vorbis.handle);
v-novichkov@795
   135
            return -1;
v-novichkov@795
   136
        }
v-novichkov@795
   137
        vorbis.ov_pcm_tell =
v-novichkov@795
   138
            (ogg_int64_t (*)(OggVorbis_File *))
v-novichkov@795
   139
            SDL_LoadFunction(vorbis.handle, "ov_pcm_tell");
v-novichkov@795
   140
        if (vorbis.ov_pcm_tell == NULL) {
v-novichkov@795
   141
            SDL_UnloadObject(vorbis.handle);
v-novichkov@795
   142
            return -1;
v-novichkov@795
   143
        }
slouken@777
   144
    }
slouken@777
   145
    ++vorbis.loaded;
slouken@777
   146
slouken@777
   147
    return 0;
slouken@63
   148
}
slouken@63
   149
slouken@777
   150
static void OGG_Unload(void)
slouken@63
   151
{
slouken@777
   152
    if (vorbis.loaded == 0) {
slouken@777
   153
        return;
slouken@777
   154
    }
slouken@777
   155
    if (vorbis.loaded == 1) {
slouken@777
   156
        SDL_UnloadObject(vorbis.handle);
slouken@777
   157
    }
slouken@777
   158
    --vorbis.loaded;
slouken@63
   159
}
slouken@63
   160
slouken@777
   161
#else /* !OGG_DYNAMIC */
slouken@777
   162
slouken@777
   163
static int OGG_Load(void)
slouken@777
   164
{
slouken@777
   165
    if (vorbis.loaded == 0) {
slouken@777
   166
#ifdef __MACOSX__
slouken@777
   167
        extern int ov_open_callbacks(void*, OggVorbis_File*, const char*, long, ov_callbacks) __attribute__((weak_import));
slouken@777
   168
        if (ov_open_callbacks == NULL)
slouken@777
   169
        {
slouken@777
   170
            /* Missing weakly linked framework */
slouken@777
   171
            Mix_SetError("Missing Vorbis.framework");
slouken@777
   172
            return -1;
slouken@777
   173
        }
slouken@777
   174
#endif // __MACOSX__
slouken@777
   175
slouken@777
   176
        vorbis.ov_clear = ov_clear;
slouken@777
   177
        vorbis.ov_info = ov_info;
v-novichkov@795
   178
        vorbis.ov_comment = ov_comment;
slouken@777
   179
        vorbis.ov_open_callbacks = ov_open_callbacks;
slouken@777
   180
        vorbis.ov_pcm_total = ov_pcm_total;
slouken@777
   181
        vorbis.ov_read = ov_read;
slouken@777
   182
        vorbis.ov_time_seek = ov_time_seek;
v-novichkov@795
   183
        vorbis.ov_pcm_seek = ov_pcm_seek;
v-novichkov@795
   184
        vorbis.ov_pcm_tell = ov_pcm_tell;
slouken@777
   185
    }
slouken@777
   186
    ++vorbis.loaded;
slouken@777
   187
slouken@777
   188
    return 0;
slouken@777
   189
}
slouken@777
   190
slouken@777
   191
static void OGG_Unload(void)
slouken@777
   192
{
slouken@777
   193
    if (vorbis.loaded == 0) {
slouken@777
   194
        return;
slouken@777
   195
    }
slouken@777
   196
    if (vorbis.loaded == 1) {
slouken@777
   197
    }
slouken@777
   198
    --vorbis.loaded;
slouken@777
   199
}
slouken@777
   200
slouken@777
   201
#endif /* OGG_DYNAMIC */
slouken@777
   202
slouken@777
   203
slouken@777
   204
typedef struct {
slouken@777
   205
    SDL_RWops *src;
slouken@777
   206
    int freesrc;
slouken@777
   207
    SDL_bool playing;
slouken@777
   208
    int volume;
slouken@777
   209
    OggVorbis_File vf;
slouken@777
   210
    int section;
slouken@777
   211
    SDL_AudioCVT cvt;
slouken@777
   212
    int len_available;
slouken@777
   213
    Uint8 *snd_available;
v-novichkov@795
   214
    int loop;
v-novichkov@795
   215
    ogg_int64_t loop_start;
v-novichkov@795
   216
    ogg_int64_t loop_end;
v-novichkov@795
   217
    ogg_int64_t loop_len;
v-novichkov@795
   218
    ogg_int64_t channels;
slouken@777
   219
} OGG_music;
slouken@777
   220
slouken@246
   221
static size_t sdl_read_func(void *ptr, size_t size, size_t nmemb, void *datasource)
slouken@246
   222
{
slouken@246
   223
    return SDL_RWread((SDL_RWops*)datasource, ptr, size, nmemb);
slouken@246
   224
}
slouken@246
   225
slouken@690
   226
static int sdl_seek_func(void *datasource, ogg_int64_t offset, int whence)
slouken@246
   227
{
slouken@690
   228
    return (int)SDL_RWseek((SDL_RWops*)datasource, offset, whence);
slouken@246
   229
}
slouken@246
   230
slouken@690
   231
static long sdl_tell_func(void *datasource)
slouken@246
   232
{
slouken@690
   233
    return (long)SDL_RWtell((SDL_RWops*)datasource);
slouken@246
   234
}
slouken@246
   235
slouken@246
   236
/* Load an OGG stream from an SDL_RWops object */
slouken@777
   237
static void *OGG_CreateFromRW(SDL_RWops *src, int freesrc)
slouken@246
   238
{
slouken@617
   239
    OGG_music *music;
slouken@617
   240
    ov_callbacks callbacks;
slouken@246
   241
slouken@617
   242
    SDL_memset(&callbacks, 0, sizeof(callbacks));
slouken@617
   243
    callbacks.read_func = sdl_read_func;
slouken@617
   244
    callbacks.seek_func = sdl_seek_func;
slouken@617
   245
    callbacks.tell_func = sdl_tell_func;
slouken@246
   246
slouken@777
   247
    music = (OGG_music *)SDL_calloc(1, sizeof *music);
slouken@777
   248
    if (music) {
v-novichkov@795
   249
        vorbis_info *vi;
v-novichkov@795
   250
        vorbis_comment *vc;
v-novichkov@795
   251
        int isLoopLength = 0, i;
v-novichkov@795
   252
        ogg_int64_t fullLength;
v-novichkov@795
   253
slouken@617
   254
        /* Initialize the music structure */
slouken@625
   255
        music->src = src;
slouken@625
   256
        music->freesrc = freesrc;
slouken@777
   257
        music->volume = MIX_MAX_VOLUME;
slouken@617
   258
        music->section = -1;
slouken@246
   259
v-novichkov@795
   260
        music->loop         = -1;
v-novichkov@795
   261
        music->loop_start   = -1;
v-novichkov@795
   262
        music->loop_end     =  0;
v-novichkov@795
   263
        music->loop_len     =  0;
v-novichkov@795
   264
slouken@777
   265
        if (vorbis.ov_open_callbacks(src, &music->vf, NULL, 0, callbacks) < 0) {
slouken@625
   266
            SDL_SetError("Not an Ogg Vorbis audio stream");
slouken@617
   267
            SDL_free(music);
slouken@777
   268
            return NULL;
slouken@617
   269
        }
v-novichkov@795
   270
v-novichkov@795
   271
        vi = vorbis.ov_info(&music->vf, -1);
v-novichkov@795
   272
        music->channels = vi->channels;
v-novichkov@795
   273
v-novichkov@795
   274
        vc = vorbis.ov_comment(&music->vf, -1);
v-novichkov@795
   275
v-novichkov@795
   276
        for (i = 0; i < vc->comments; i++) {
v-novichkov@795
   277
            int   paramLen = vc->comment_lengths[i] + 1;
v-novichkov@795
   278
            char *param = (char *)SDL_malloc((size_t)paramLen);
v-novichkov@795
   279
            char *argument  = param;
v-novichkov@795
   280
            char *value     = param;
v-novichkov@795
   281
            SDL_memset(param, 0, (size_t)paramLen);
v-novichkov@795
   282
            SDL_memcpy(param, vc->user_comments[i], (size_t)vc->comment_lengths[i]);
v-novichkov@795
   283
            value = SDL_strchr(param, '=');
v-novichkov@795
   284
            if (value == NULL) {
v-novichkov@795
   285
                value = param + paramLen - 1; /* set null */
v-novichkov@795
   286
            } else {
v-novichkov@795
   287
                *(value++) = '\0';
v-novichkov@795
   288
            }
v-novichkov@795
   289
v-novichkov@795
   290
            #ifdef __USE_ISOC99
v-novichkov@795
   291
            #define A_TO_OGG64(x) (ogg_int64_t)atoll(x)
v-novichkov@795
   292
            #else
v-novichkov@795
   293
            #define A_TO_OGG64(x) (ogg_int64_t)atol(x)
v-novichkov@795
   294
            #endif
v-novichkov@795
   295
v-novichkov@795
   296
            if (SDL_strcasecmp(argument, "LOOPSTART") == 0)
v-novichkov@795
   297
                music->loop_start = A_TO_OGG64(value);
v-novichkov@795
   298
            else if (SDL_strcasecmp(argument, "LOOPLENGTH") == 0) {
v-novichkov@795
   299
                music->loop_len = A_TO_OGG64(value);
v-novichkov@795
   300
                isLoopLength = 1;
v-novichkov@795
   301
            }
v-novichkov@795
   302
            else if (SDL_strcasecmp(argument, "LOOPEND") == 0) {
v-novichkov@795
   303
                isLoopLength = 0;
v-novichkov@795
   304
                music->loop_end = A_TO_OGG64(value);
v-novichkov@795
   305
            }
v-novichkov@795
   306
v-novichkov@795
   307
            #undef A_TO_OGG64
v-novichkov@795
   308
            SDL_free(param);
v-novichkov@795
   309
        }
v-novichkov@795
   310
v-novichkov@795
   311
        if (isLoopLength == 1)
v-novichkov@795
   312
            music->loop_end = music->loop_start + music->loop_len;
v-novichkov@795
   313
        else
v-novichkov@795
   314
            music->loop_len = music->loop_end - music->loop_start;
v-novichkov@795
   315
v-novichkov@795
   316
        fullLength = vorbis.ov_pcm_total(&music->vf, -1);
v-novichkov@795
   317
        if (((music->loop_start >= 0) || (music->loop_end > 0)) &&
v-novichkov@795
   318
            ((music->loop_start < music->loop_end) || (music->loop_end == 0)) &&
v-novichkov@795
   319
             (music->loop_start < fullLength) &&
v-novichkov@795
   320
             (music->loop_end <= fullLength)) {
v-novichkov@795
   321
            if (music->loop_start < 0) music->loop_start = 0;
v-novichkov@795
   322
            if (music->loop_end == 0)  music->loop_end = fullLength;
v-novichkov@795
   323
            music->loop = 1;
v-novichkov@795
   324
        }
v-novichkov@795
   325
slouken@617
   326
    } else {
slouken@617
   327
        SDL_OutOfMemory();
slouken@617
   328
    }
slouken@777
   329
    return music;
slouken@777
   330
}
slouken@777
   331
slouken@777
   332
/* Set the volume for an OGG stream */
slouken@777
   333
static void OGG_SetVolume(void *context, int volume)
slouken@777
   334
{
slouken@777
   335
    OGG_music *music = (OGG_music *)context;
slouken@777
   336
    music->volume = volume;
slouken@246
   337
}
slouken@246
   338
slouken@63
   339
/* Start playback of a given OGG stream */
slouken@777
   340
static int OGG_Play(void *context)
slouken@63
   341
{
slouken@777
   342
    OGG_music *music = (OGG_music *)context;
slouken@777
   343
    music->playing = SDL_TRUE;
slouken@777
   344
    return 0;
slouken@63
   345
}
slouken@63
   346
slouken@63
   347
/* Return non-zero if a stream is currently playing */
slouken@777
   348
static SDL_bool OGG_IsPlaying(void *context)
slouken@63
   349
{
slouken@777
   350
    OGG_music *music = (OGG_music *)context;
slouken@777
   351
    return music->playing;
slouken@63
   352
}
slouken@63
   353
slouken@63
   354
/* Read some Ogg stream data and convert it for output */
slouken@63
   355
static void OGG_getsome(OGG_music *music)
slouken@63
   356
{
slouken@617
   357
    int section;
slouken@617
   358
    int len;
slouken@617
   359
    char data[4096];
v-novichkov@795
   360
    ogg_int64_t pcmPos;
slouken@617
   361
    SDL_AudioCVT *cvt;
slouken@63
   362
slouken@353
   363
#ifdef OGG_USE_TREMOR
slouken@617
   364
    len = vorbis.ov_read(&music->vf, data, sizeof(data), &section);
slouken@353
   365
#else
slouken@779
   366
    len = (int)vorbis.ov_read(&music->vf, data, sizeof(data), 0, 2, 1, &section);
slouken@353
   367
#endif
v-novichkov@795
   368
v-novichkov@795
   369
    pcmPos = vorbis.ov_pcm_tell(&music->vf);
v-novichkov@795
   370
    if ((music->loop == 1) && (pcmPos >= music->loop_end)) {
v-novichkov@795
   371
        len -= ((pcmPos - music->loop_end) * music->channels) * (long)sizeof(Uint16);
v-novichkov@795
   372
        vorbis.ov_pcm_seek(&music->vf, music->loop_start);
v-novichkov@795
   373
    }
v-novichkov@795
   374
slouken@777
   375
    if (len <= 0) {
slouken@777
   376
        if (len == 0) {
slouken@777
   377
            music->playing = SDL_FALSE;
slouken@617
   378
        }
slouken@617
   379
        return;
slouken@617
   380
    }
slouken@617
   381
    cvt = &music->cvt;
slouken@777
   382
    if (section != music->section) {
slouken@617
   383
        vorbis_info *vi;
slouken@63
   384
slouken@617
   385
        vi = vorbis.ov_info(&music->vf, -1);
slouken@779
   386
        SDL_BuildAudioCVT(cvt, AUDIO_S16, vi->channels, (int)vi->rate,
slouken@777
   387
                               music_spec.format, music_spec.channels, music_spec.freq);
slouken@777
   388
        if (cvt->buf) {
slouken@617
   389
            SDL_free(cvt->buf);
slouken@617
   390
        }
slouken@617
   391
        cvt->buf = (Uint8 *)SDL_malloc(sizeof(data)*cvt->len_mult);
slouken@617
   392
        music->section = section;
slouken@617
   393
    }
slouken@777
   394
    if (cvt->buf) {
slouken@621
   395
        SDL_memcpy(cvt->buf, data, len);
slouken@777
   396
        if (cvt->needed) {
slouken@617
   397
            cvt->len = len;
slouken@617
   398
            SDL_ConvertAudio(cvt);
slouken@617
   399
        } else {
slouken@617
   400
            cvt->len_cvt = len;
slouken@617
   401
        }
slouken@617
   402
        music->len_available = music->cvt.len_cvt;
slouken@617
   403
        music->snd_available = music->cvt.buf;
slouken@617
   404
    } else {
slouken@617
   405
        SDL_SetError("Out of memory");
slouken@777
   406
        music->playing = SDL_FALSE;
slouken@617
   407
    }
slouken@63
   408
}
slouken@63
   409
slouken@63
   410
/* Play some of a stream previously started with OGG_play() */
slouken@777
   411
static int OGG_GetAudio(void *context, void *data, int bytes)
slouken@63
   412
{
slouken@777
   413
    OGG_music *music = (OGG_music *)context;
slouken@777
   414
    Uint8 *snd = (Uint8 *)data;
slouken@777
   415
    int len = bytes;
slouken@617
   416
    int mixable;
slouken@63
   417
slouken@777
   418
    while ((len > 0) && music->playing) {
slouken@777
   419
        if (!music->len_available) {
slouken@617
   420
            OGG_getsome(music);
slouken@617
   421
        }
slouken@617
   422
        mixable = len;
slouken@777
   423
        if (mixable > music->len_available) {
slouken@617
   424
            mixable = music->len_available;
slouken@617
   425
        }
slouken@777
   426
        if (music->volume == MIX_MAX_VOLUME) {
slouken@621
   427
            SDL_memcpy(snd, music->snd_available, mixable);
slouken@617
   428
        } else {
slouken@777
   429
            SDL_MixAudioFormat(snd, music->snd_available, music_spec.format, mixable, music->volume);
slouken@617
   430
        }
slouken@617
   431
        music->len_available -= mixable;
slouken@617
   432
        music->snd_available += mixable;
slouken@617
   433
        len -= mixable;
slouken@617
   434
        snd += mixable;
slouken@617
   435
    }
slouken@617
   436
slouken@617
   437
    return len;
slouken@63
   438
}
slouken@63
   439
slouken@777
   440
/* Jump (seek) to a given position (time is in seconds) */
slouken@777
   441
static int OGG_Seek(void *context, double time)
slouken@777
   442
{
slouken@777
   443
    OGG_music *music = (OGG_music *)context;
slouken@777
   444
#ifdef OGG_USE_TREMOR
slouken@777
   445
    vorbis.ov_time_seek(&music->vf, (ogg_int64_t)(time * 1000.0));
slouken@777
   446
#else
slouken@777
   447
    vorbis.ov_time_seek(&music->vf, time);
slouken@777
   448
#endif
slouken@777
   449
    return 0;
slouken@777
   450
}
slouken@777
   451
slouken@63
   452
/* Stop playback of a stream previously started with OGG_play() */
slouken@777
   453
static void OGG_Stop(void *context)
slouken@63
   454
{
slouken@777
   455
    OGG_music *music = (OGG_music *)context;
slouken@777
   456
    music->playing = SDL_FALSE;
slouken@63
   457
}
slouken@63
   458
slouken@63
   459
/* Close the given OGG stream */
slouken@777
   460
static void OGG_Delete(void *context)
slouken@63
   461
{
slouken@777
   462
    OGG_music *music = (OGG_music *)context;
slouken@777
   463
    if (music) {
slouken@777
   464
        if (music->cvt.buf) {
slouken@617
   465
            SDL_free(music->cvt.buf);
slouken@617
   466
        }
slouken@777
   467
        if (music->freesrc) {
slouken@625
   468
            SDL_RWclose(music->src);
slouken@617
   469
        }
slouken@617
   470
        vorbis.ov_clear(&music->vf);
slouken@617
   471
        SDL_free(music);
slouken@617
   472
    }
slouken@63
   473
}
slouken@63
   474
slouken@777
   475
Mix_MusicInterface Mix_MusicInterface_OGG =
slouken@155
   476
{
slouken@777
   477
    "OGG",
slouken@777
   478
    MIX_MUSIC_OGG,
slouken@777
   479
    MUS_OGG,
slouken@777
   480
    SDL_FALSE,
slouken@777
   481
    SDL_FALSE,
slouken@155
   482
slouken@777
   483
    OGG_Load,
slouken@777
   484
    NULL,   /* Open */
slouken@777
   485
    OGG_CreateFromRW,
slouken@777
   486
    NULL,   /* CreateFromFile */
slouken@777
   487
    OGG_SetVolume,
slouken@777
   488
    OGG_Play,
slouken@777
   489
    OGG_IsPlaying,
slouken@777
   490
    OGG_GetAudio,
slouken@777
   491
    OGG_Seek,
slouken@777
   492
    NULL,   /* Pause */
slouken@777
   493
    NULL,   /* Resume */
slouken@779
   494
    OGG_Stop,
slouken@777
   495
    OGG_Delete,
slouken@777
   496
    NULL,   /* Close */
slouken@777
   497
    OGG_Unload,
slouken@777
   498
};
slouken@777
   499
slouken@777
   500
#endif /* MUSIC_OGG */
slouken@777
   501
slouken@777
   502
/* vi: set ts=4 sw=4 expandtab: */