music_opus.c
author Ozkan Sezer <sezeroz@gmail.com>
Mon, 08 Oct 2018 10:05:02 +0300
changeset 899 f97f817b94bb
parent 855 a7dee77dd60f
child 917 a94168f3fc7f
permissions -rw-r--r--
music_opus.c: fix a typo. (found with -Wall in CFLAGS).

also update changelog to mention opus support.
sezeroz@855
     1
/*
sezeroz@855
     2
  SDL_mixer:  An audio mixer library based on the SDL library
sezeroz@855
     3
  Copyright (C) 1997-2018 Sam Lantinga <slouken@libsdl.org>
sezeroz@855
     4
sezeroz@855
     5
  This software is provided 'as-is', without any express or implied
sezeroz@855
     6
  warranty.  In no event will the authors be held liable for any damages
sezeroz@855
     7
  arising from the use of this software.
sezeroz@855
     8
sezeroz@855
     9
  Permission is granted to anyone to use this software for any purpose,
sezeroz@855
    10
  including commercial applications, and to alter it and redistribute it
sezeroz@855
    11
  freely, subject to the following restrictions:
sezeroz@855
    12
sezeroz@855
    13
  1. The origin of this software must not be misrepresented; you must not
sezeroz@855
    14
     claim that you wrote the original software. If you use this software
sezeroz@855
    15
     in a product, an acknowledgment in the product documentation would be
sezeroz@855
    16
     appreciated but is not required.
sezeroz@855
    17
  2. Altered source versions must be plainly marked as such, and must not be
sezeroz@855
    18
     misrepresented as being the original software.
sezeroz@855
    19
  3. This notice may not be removed or altered from any source distribution.
sezeroz@855
    20
*/
sezeroz@855
    21
sezeroz@855
    22
#ifdef MUSIC_OPUS
sezeroz@855
    23
sezeroz@855
    24
/* This file supports Ogg Opus music streams */
sezeroz@855
    25
sezeroz@855
    26
#include "SDL_loadso.h"
sezeroz@855
    27
sezeroz@855
    28
#include "music_opus.h"
sezeroz@855
    29
sezeroz@855
    30
#include <opusfile.h>
sezeroz@855
    31
sezeroz@855
    32
typedef struct {
sezeroz@855
    33
    int loaded;
sezeroz@855
    34
    void *handle;
sezeroz@855
    35
    OggOpusFile *(*op_open_callbacks)(void *,const OpusFileCallbacks *,const unsigned char *,size_t,int *);
sezeroz@855
    36
    void (*op_free)(OggOpusFile *);
sezeroz@855
    37
    const OpusHead *(*op_head)(const OggOpusFile *,int);
sezeroz@855
    38
    int (*op_seekable)(const OggOpusFile *);
sezeroz@855
    39
    int (*op_read)(OggOpusFile *, opus_int16 *,int,int *);
sezeroz@855
    40
    int (*op_pcm_seek)(OggOpusFile *,ogg_int64_t);
sezeroz@855
    41
} opus_loader;
sezeroz@855
    42
sezeroz@855
    43
static opus_loader opus = {
sezeroz@855
    44
    0, NULL
sezeroz@855
    45
};
sezeroz@855
    46
sezeroz@855
    47
#ifdef OPUS_DYNAMIC
sezeroz@855
    48
#define FUNCTION_LOADER(FUNC, SIG) \
sezeroz@855
    49
    opus.FUNC = (SIG) SDL_LoadFunction(opus.handle, #FUNC); \
sezeroz@855
    50
    if (opus.FUNC == NULL) { SDL_UnloadObject(opus.handle); return -1; }
sezeroz@855
    51
#else
sezeroz@855
    52
#define FUNCTION_LOADER(FUNC, SIG) \
sezeroz@855
    53
    opus.FUNC = FUNC;
sezeroz@855
    54
#endif
sezeroz@855
    55
sezeroz@855
    56
static int OPUS_Load(void)
sezeroz@855
    57
{
sezeroz@855
    58
    if (opus.loaded == 0) {
sezeroz@855
    59
#ifdef OPUS_DYNAMIC
sezeroz@855
    60
        opus.handle = SDL_LoadObject(OPUS_DYNAMIC);
sezeroz@855
    61
        if (opus.handle == NULL) {
sezeroz@855
    62
            return -1;
sezeroz@855
    63
        }
sezeroz@855
    64
#elif defined(__MACOSX__)
sezeroz@855
    65
        extern OggOpusFile *op_open_callbacks(void *,const OpusFileCallbacks *,const unsigned char *,size_t,int *) __attribute__((weak_import));
sezeroz@855
    66
        if (op_open_callbacks == NULL) {
sezeroz@855
    67
            /* Missing weakly linked framework */
sezeroz@855
    68
            Mix_SetError("Missing Opus.framework");
sezeroz@855
    69
            return -1;
sezeroz@855
    70
        }
sezeroz@855
    71
#endif
sezeroz@855
    72
        FUNCTION_LOADER(op_open_callbacks, OggOpusFile *(*)(void *,const OpusFileCallbacks *,const unsigned char *,size_t,int *))
sezeroz@855
    73
        FUNCTION_LOADER(op_free, void (*)(OggOpusFile *))
sezeroz@855
    74
        FUNCTION_LOADER(op_head, const OpusHead *(*)(const OggOpusFile *,int))
sezeroz@855
    75
        FUNCTION_LOADER(op_seekable, int (*)(const OggOpusFile *))
sezeroz@855
    76
        FUNCTION_LOADER(op_read, int (*)(OggOpusFile *, opus_int16 *,int,int *))
sezeroz@855
    77
        FUNCTION_LOADER(op_pcm_seek, int (*)(OggOpusFile *,ogg_int64_t))
sezeroz@855
    78
    }
sezeroz@855
    79
    ++opus.loaded;
sezeroz@855
    80
sezeroz@855
    81
    return 0;
sezeroz@855
    82
}
sezeroz@855
    83
sezeroz@855
    84
static void OPUS_Unload(void)
sezeroz@855
    85
{
sezeroz@855
    86
    if (opus.loaded == 0) {
sezeroz@855
    87
        return;
sezeroz@855
    88
    }
sezeroz@855
    89
    if (opus.loaded == 1) {
sezeroz@855
    90
#ifdef OPUS_DYNAMIC
sezeroz@855
    91
        SDL_UnloadObject(opus.handle);
sezeroz@855
    92
#endif
sezeroz@855
    93
    }
sezeroz@855
    94
    --opus.loaded;
sezeroz@855
    95
}
sezeroz@855
    96
sezeroz@855
    97
sezeroz@855
    98
typedef struct {
sezeroz@855
    99
    SDL_RWops *src;
sezeroz@855
   100
    int freesrc;
sezeroz@855
   101
    int play_count;
sezeroz@855
   102
    int volume;
sezeroz@855
   103
    OggOpusFile *of;
sezeroz@855
   104
    const OpusHead *op_info;
sezeroz@855
   105
    int section;
sezeroz@855
   106
    SDL_AudioStream *stream;
sezeroz@855
   107
    char *buffer;
sezeroz@855
   108
    int buffer_size;
sezeroz@855
   109
} OPUS_music;
sezeroz@855
   110
sezeroz@855
   111
sezeroz@855
   112
static int set_op_error(const char *function, int error)
sezeroz@855
   113
{
sezeroz@855
   114
#define HANDLE_ERROR_CASE(X)    case X: Mix_SetError("%s: %s", function, #X); break;
sezeroz@855
   115
    switch (error) {
sezeroz@855
   116
    HANDLE_ERROR_CASE(OP_FALSE);
sezeroz@855
   117
    HANDLE_ERROR_CASE(OP_EOF);
sezeroz@855
   118
    HANDLE_ERROR_CASE(OP_HOLE);
sezeroz@855
   119
    HANDLE_ERROR_CASE(OP_EREAD);
sezeroz@855
   120
    HANDLE_ERROR_CASE(OP_EFAULT);
sezeroz@855
   121
    HANDLE_ERROR_CASE(OP_EIMPL);
sezeroz@855
   122
    HANDLE_ERROR_CASE(OP_EINVAL);
sezeroz@855
   123
    HANDLE_ERROR_CASE(OP_ENOTFORMAT);
sezeroz@855
   124
    HANDLE_ERROR_CASE(OP_EBADHEADER);
sezeroz@855
   125
    HANDLE_ERROR_CASE(OP_EVERSION);
sezeroz@855
   126
    HANDLE_ERROR_CASE(OP_ENOTAUDIO);
sezeroz@855
   127
    HANDLE_ERROR_CASE(OP_EBADPACKET);
sezeroz@855
   128
    HANDLE_ERROR_CASE(OP_EBADLINK);
sezeroz@855
   129
    HANDLE_ERROR_CASE(OP_ENOSEEK);
sezeroz@855
   130
    HANDLE_ERROR_CASE(OP_EBADTIMESTAMP);
sezeroz@855
   131
    default:
sezeroz@855
   132
        Mix_SetError("%s: unknown error %d\n", function, error);
sezeroz@855
   133
        break;
sezeroz@855
   134
    }
sezeroz@855
   135
    return -1;
sezeroz@855
   136
}
sezeroz@855
   137
sezeroz@855
   138
static int sdl_read_func(void *datasource, unsigned char *ptr, int size)
sezeroz@855
   139
{
sezeroz@855
   140
    return (int)SDL_RWread((SDL_RWops*)datasource, ptr, 1, size);
sezeroz@855
   141
}
sezeroz@855
   142
sezeroz@899
   143
static int sdl_seek_func(void *datasource, opus_int64  offset, int whence)
sezeroz@855
   144
{
sezeroz@855
   145
    return (SDL_RWseek((SDL_RWops*)datasource, offset, whence) < 0)? -1 : 0;
sezeroz@855
   146
}
sezeroz@855
   147
sezeroz@855
   148
static opus_int64 sdl_tell_func(void *datasource)
sezeroz@855
   149
{
sezeroz@855
   150
    return SDL_RWtell((SDL_RWops*)datasource);
sezeroz@855
   151
}
sezeroz@855
   152
sezeroz@855
   153
static int OPUS_Seek(void*, double);
sezeroz@855
   154
static void OPUS_Delete(void*);
sezeroz@855
   155
sezeroz@855
   156
static int OPUS_UpdateSection(OPUS_music *music)
sezeroz@855
   157
{
sezeroz@855
   158
    const OpusHead *op_info;
sezeroz@855
   159
sezeroz@855
   160
    op_info = opus.op_head(music->of, -1);
sezeroz@855
   161
    if (!op_info) {
sezeroz@855
   162
        Mix_SetError("op_head returned NULL");
sezeroz@855
   163
        return -1;
sezeroz@855
   164
    }
sezeroz@855
   165
sezeroz@855
   166
    if (music->op_info && op_info->channel_count == music->op_info->channel_count) {
sezeroz@855
   167
        return 0;
sezeroz@855
   168
    }
sezeroz@855
   169
    music->op_info = op_info;
sezeroz@855
   170
sezeroz@855
   171
    if (music->buffer) {
sezeroz@855
   172
        SDL_free(music->buffer);
sezeroz@855
   173
        music->buffer = NULL;
sezeroz@855
   174
    }
sezeroz@855
   175
sezeroz@855
   176
    if (music->stream) {
sezeroz@855
   177
        SDL_FreeAudioStream(music->stream);
sezeroz@855
   178
        music->stream = NULL;
sezeroz@855
   179
    }
sezeroz@855
   180
sezeroz@855
   181
    music->stream = SDL_NewAudioStream(AUDIO_S16, op_info->channel_count, 48000,
sezeroz@855
   182
                                       music_spec.format, music_spec.channels, music_spec.freq);
sezeroz@855
   183
    if (!music->stream) {
sezeroz@855
   184
        return -1;
sezeroz@855
   185
    }
sezeroz@855
   186
sezeroz@855
   187
    music->buffer_size = music_spec.samples * sizeof(opus_int16) * op_info->channel_count;
sezeroz@855
   188
    music->buffer = (char *)SDL_malloc(music->buffer_size);
sezeroz@855
   189
    if (!music->buffer) {
sezeroz@855
   190
        return -1;
sezeroz@855
   191
    }
sezeroz@855
   192
    return 0;
sezeroz@855
   193
}
sezeroz@855
   194
sezeroz@855
   195
/* Load an Opus stream from an SDL_RWops object */
sezeroz@855
   196
static void *OPUS_CreateFromRW(SDL_RWops *src, int freesrc)
sezeroz@855
   197
{
sezeroz@855
   198
    OPUS_music *music;
sezeroz@855
   199
    OpusFileCallbacks callbacks;
sezeroz@855
   200
    int err = 0;
sezeroz@855
   201
sezeroz@855
   202
    music = (OPUS_music *)SDL_calloc(1, sizeof *music);
sezeroz@855
   203
    if (!music) {
sezeroz@855
   204
        SDL_OutOfMemory();
sezeroz@855
   205
        return NULL;
sezeroz@855
   206
    }
sezeroz@855
   207
    music->src = src;
sezeroz@855
   208
    music->volume = MIX_MAX_VOLUME;
sezeroz@855
   209
    music->section = -1;
sezeroz@855
   210
sezeroz@855
   211
    SDL_zero(callbacks);
sezeroz@855
   212
    callbacks.read = sdl_read_func;
sezeroz@855
   213
    callbacks.seek = sdl_seek_func;
sezeroz@855
   214
    callbacks.tell = sdl_tell_func;
sezeroz@855
   215
sezeroz@855
   216
    music->of = opus.op_open_callbacks(src, &callbacks, NULL, 0, &err);
sezeroz@855
   217
    if (music->of == NULL) {
sezeroz@855
   218
    /*  set_op_error("op_open_callbacks", err);*/
sezeroz@855
   219
        SDL_SetError("Not an Opus audio stream");
sezeroz@855
   220
        SDL_free(music);
sezeroz@855
   221
        return NULL;
sezeroz@855
   222
    }
sezeroz@855
   223
sezeroz@855
   224
    if (!opus.op_seekable(music->of)) {
sezeroz@855
   225
        OPUS_Delete(music);
sezeroz@855
   226
        Mix_SetError("Opus stream not seekable");
sezeroz@855
   227
        return NULL;
sezeroz@855
   228
    }
sezeroz@855
   229
sezeroz@855
   230
    if (OPUS_UpdateSection(music) < 0) {
sezeroz@855
   231
        OPUS_Delete(music);
sezeroz@855
   232
        return NULL;
sezeroz@855
   233
    }
sezeroz@855
   234
sezeroz@855
   235
    music->freesrc = freesrc;
sezeroz@855
   236
    return music;
sezeroz@855
   237
}
sezeroz@855
   238
sezeroz@855
   239
/* Set the volume for an Opus stream */
sezeroz@855
   240
static void OPUS_SetVolume(void *context, int volume)
sezeroz@855
   241
{
sezeroz@855
   242
    OPUS_music *music = (OPUS_music *)context;
sezeroz@855
   243
    music->volume = volume;
sezeroz@855
   244
}
sezeroz@855
   245
sezeroz@855
   246
/* Start playback of a given Opus stream */
sezeroz@855
   247
static int OPUS_Play(void *context, int play_count)
sezeroz@855
   248
{
sezeroz@855
   249
    OPUS_music *music = (OPUS_music *)context;
sezeroz@855
   250
    music->play_count = play_count;
sezeroz@855
   251
    return OPUS_Seek(music, 0.0);
sezeroz@855
   252
}
sezeroz@855
   253
sezeroz@855
   254
/* Play some of a stream previously started with OPUS_Play() */
sezeroz@855
   255
static int OPUS_GetSome(void *context, void *data, int bytes, SDL_bool *done)
sezeroz@855
   256
{
sezeroz@855
   257
    OPUS_music *music = (OPUS_music *)context;
sezeroz@855
   258
    int filled, samples, section;
sezeroz@855
   259
sezeroz@855
   260
    filled = SDL_AudioStreamGet(music->stream, data, bytes);
sezeroz@855
   261
    if (filled != 0) {
sezeroz@855
   262
        return filled;
sezeroz@855
   263
    }
sezeroz@855
   264
sezeroz@855
   265
    if (!music->play_count) {
sezeroz@855
   266
        /* All done */
sezeroz@855
   267
        *done = SDL_TRUE;
sezeroz@855
   268
        return 0;
sezeroz@855
   269
    }
sezeroz@855
   270
sezeroz@855
   271
    section = music->section;
sezeroz@855
   272
    samples = opus.op_read(music->of, (opus_int16 *)music->buffer, music->buffer_size / sizeof(opus_int16), &section);
sezeroz@855
   273
    if (samples < 0) {
sezeroz@855
   274
        set_op_error("op_read", samples);
sezeroz@855
   275
        return -1;
sezeroz@855
   276
    }
sezeroz@855
   277
sezeroz@855
   278
    if (section != music->section) {
sezeroz@855
   279
        music->section = section;
sezeroz@855
   280
        if (OPUS_UpdateSection(music) < 0) {
sezeroz@855
   281
            return -1;
sezeroz@855
   282
        }
sezeroz@855
   283
    }
sezeroz@855
   284
sezeroz@855
   285
    if (samples > 0) {
sezeroz@855
   286
        filled = samples * music->op_info->channel_count * 2;
sezeroz@855
   287
        if (SDL_AudioStreamPut(music->stream, music->buffer, filled) < 0) {
sezeroz@855
   288
            return -1;
sezeroz@855
   289
        }
sezeroz@855
   290
    } else {
sezeroz@855
   291
        if (music->play_count == 1) {
sezeroz@855
   292
            music->play_count = 0;
sezeroz@855
   293
            SDL_AudioStreamFlush(music->stream);
sezeroz@855
   294
        } else {
sezeroz@855
   295
            int play_count = -1;
sezeroz@855
   296
            if (music->play_count > 0) {
sezeroz@855
   297
                play_count = (music->play_count - 1);
sezeroz@855
   298
            }
sezeroz@855
   299
            if (OPUS_Play(music, play_count) < 0) {
sezeroz@855
   300
                return -1;
sezeroz@855
   301
            }
sezeroz@855
   302
        }
sezeroz@855
   303
    }
sezeroz@855
   304
    return 0;
sezeroz@855
   305
}
sezeroz@855
   306
sezeroz@855
   307
static int OPUS_GetAudio(void *context, void *data, int bytes)
sezeroz@855
   308
{
sezeroz@855
   309
    OPUS_music *music = (OPUS_music *)context;
sezeroz@855
   310
    return music_pcm_getaudio(context, data, bytes, music->volume, OPUS_GetSome);
sezeroz@855
   311
}
sezeroz@855
   312
sezeroz@855
   313
/* Jump (seek) to a given position (time is in seconds) */
sezeroz@855
   314
static int OPUS_Seek(void *context, double time)
sezeroz@855
   315
{
sezeroz@855
   316
    OPUS_music *music = (OPUS_music *)context;
sezeroz@855
   317
    int result;
sezeroz@855
   318
    result = opus.op_pcm_seek(music->of, (ogg_int64_t)(time * 48000));
sezeroz@855
   319
    if (result < 0) {
sezeroz@855
   320
        return set_op_error("op_pcm_seek", result);
sezeroz@855
   321
    }
sezeroz@855
   322
    return 0;
sezeroz@855
   323
}
sezeroz@855
   324
sezeroz@855
   325
/* Close the given Opus stream */
sezeroz@855
   326
static void OPUS_Delete(void *context)
sezeroz@855
   327
{
sezeroz@855
   328
    OPUS_music *music = (OPUS_music *)context;
sezeroz@855
   329
    opus.op_free(music->of);
sezeroz@855
   330
    if (music->stream) {
sezeroz@855
   331
        SDL_FreeAudioStream(music->stream);
sezeroz@855
   332
    }
sezeroz@855
   333
    if (music->buffer) {
sezeroz@855
   334
        SDL_free(music->buffer);
sezeroz@855
   335
    }
sezeroz@855
   336
    if (music->freesrc) {
sezeroz@855
   337
        SDL_RWclose(music->src);
sezeroz@855
   338
    }
sezeroz@855
   339
    SDL_free(music);
sezeroz@855
   340
}
sezeroz@855
   341
sezeroz@855
   342
Mix_MusicInterface Mix_MusicInterface_Opus =
sezeroz@855
   343
{
sezeroz@855
   344
    "OPUS",
sezeroz@855
   345
    MIX_MUSIC_OPUS,
sezeroz@855
   346
    MUS_OPUS,
sezeroz@855
   347
    SDL_FALSE,
sezeroz@855
   348
    SDL_FALSE,
sezeroz@855
   349
sezeroz@855
   350
    OPUS_Load,
sezeroz@855
   351
    NULL,   /* Open */
sezeroz@855
   352
    OPUS_CreateFromRW,
sezeroz@855
   353
    NULL,   /* CreateFromFile */
sezeroz@855
   354
    OPUS_SetVolume,
sezeroz@855
   355
    OPUS_Play,
sezeroz@855
   356
    NULL,   /* IsPlaying */
sezeroz@855
   357
    OPUS_GetAudio,
sezeroz@855
   358
    OPUS_Seek,
sezeroz@855
   359
    NULL,   /* Pause */
sezeroz@855
   360
    NULL,   /* Resume */
sezeroz@855
   361
    NULL,   /* Stop */
sezeroz@855
   362
    OPUS_Delete,
sezeroz@855
   363
    NULL,   /* Close */
sezeroz@855
   364
    OPUS_Unload,
sezeroz@855
   365
};
sezeroz@855
   366
sezeroz@855
   367
#endif /* MUSIC_OPUS */
sezeroz@855
   368
sezeroz@855
   369
/* vi: set ts=4 sw=4 expandtab: */