DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/libs/decoders/flac.c
00001 /*
00002  *  SPDX-License-Identifier: GPL-2.0-or-later
00003  *
00004  *  Copyright (C) 2001-2017  Ryan C. Gordon <icculus@icculus.org>
00005  *  Copyright (C) 2018-2019  Kevin R. Croft <krcroft@gmail.com>
00006  *  Copyright (C) 2020-2020  The DOSBox-X project
00007  *
00008  *  This program is free software; you can redistribute it and/or modify
00009  *  it under the terms of the GNU General Public License as published by
00010  *  the Free Software Foundation; either version 2 of the License, or
00011  *  (at your option) any later version.
00012  *
00013  *  This program is distributed in the hope that it will be useful,
00014  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00015  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00016  *  GNU General Public License for more details.
00017  *
00018  *  You should have received a copy of the GNU General Public License along
00019  *  with this program; if not, write to the Free Software Foundation, Inc.,
00020  *  51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00021  */
00022 
00023 /*
00024  *  DOSBox-X FLAC decoder API implementation
00025  *  ----------------------------------------
00026  *  This decoder makes use of the dr_flac library by David Reid (mackron@gmail.com)
00027  *    - dr_libs: https://github.com/mackron/dr_libs (source)
00028  *    - dr_flac: http://mackron.github.io/dr_flac.html (website)
00029  */
00030 
00031 #include <math.h> /* for llroundf */
00032 
00033 #include "SDL_sound.h"
00034 #define __SDL_SOUND_INTERNAL__
00035 #include "SDL_sound_internal.h"
00036 
00037 #define DR_FLAC_IMPLEMENTATION
00038 #define DR_FLAC_NO_STDIO 1
00039 #define DR_FLAC_NO_WIN32_IO 1
00040 #define DR_FLAC_NO_OGG 1
00041 #define DR_FLAC_BUFFER_SIZE 8192
00042 #include "dr_flac.h"
00043 
00044 static size_t flac_read(void* pUserData, void* pBufferOut, size_t bytesToRead)
00045 {
00046     Uint8 *ptr = (Uint8 *) pBufferOut;
00047     Sound_Sample *sample = (Sound_Sample *) pUserData;
00048     Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
00049     SDL_RWops *rwops = internal->rw;
00050     size_t bytes_read = 0;
00051 
00052     while (bytes_read < bytesToRead)
00053     {
00054         const size_t rc = SDL_RWread(rwops, ptr, 1, bytesToRead - bytes_read);
00055         if (rc == 0) {
00056             sample->flags |= SOUND_SAMPLEFLAG_EOF;
00057             break;
00058         } /* if */
00059         bytes_read += rc;
00060         ptr += rc;
00061     } /* while */
00062 
00063     return bytes_read;
00064 } /* flac_read */
00065 
00066 static drflac_bool32 flac_seek(void* pUserData, int offset, drflac_seek_origin origin)
00067 {
00068     const int whence = (origin == drflac_seek_origin_start) ? RW_SEEK_SET : RW_SEEK_CUR;
00069     Sound_Sample *sample = (Sound_Sample *) pUserData;
00070     Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
00071     return (SDL_RWseek(internal->rw, offset, whence) != -1) ? DRFLAC_TRUE : DRFLAC_FALSE;
00072 } /* flac_seek */
00073 
00074 
00075 static int FLAC_init(void)
00076 {
00077     return 1;  /* always succeeds. */
00078 } /* FLAC_init */
00079 
00080 
00081 static void FLAC_quit(void)
00082 {
00083     /* it's a no-op. */
00084 } /* FLAC_quit */
00085 
00086 static int FLAC_open(Sound_Sample *sample, const char *ext)
00087 {
00088     (void) ext; // deliberately unused, but present for API compliance
00089     Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
00090     drflac *dr = drflac_open(flac_read, flac_seek, sample, NULL);
00091 
00092     if (!dr) {
00093         BAIL_IF_MACRO(sample->flags & SOUND_SAMPLEFLAG_ERROR, ERR_IO_ERROR, 0);
00094         BAIL_MACRO("FLAC: Not a FLAC stream.", 0);
00095     } /* if */
00096 
00097     SNDDBG(("FLAC: Accepting data stream.\n"));
00098     sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
00099 
00100     sample->actual.channels = dr->channels;
00101     sample->actual.rate = dr->sampleRate;
00102     sample->actual.format = AUDIO_S16SYS; /* returns native byte-order based on architecture */
00103 
00104     const Uint64 frames = (Uint64) dr->totalPCMFrameCount;
00105     if (frames == 0) {
00106         internal->total_time = -1;
00107     }
00108     else {
00109         const Uint32 rate = (Uint32) dr->sampleRate;
00110         internal->total_time = ( (Sint32)frames / rate) * 1000;
00111         internal->total_time += ((frames % rate) * 1000) / rate;
00112     } /* else */
00113 
00114     internal->decoder_private = dr;
00115 
00116     return 1;
00117 } /* FLAC_open */
00118 
00119 
00120 
00121 static void FLAC_close(Sound_Sample *sample)
00122 {
00123     Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
00124     drflac *dr = (drflac *) internal->decoder_private;
00125     drflac_close(dr);
00126 } /* FLAC_close */
00127 
00128 
00129 static Uint32 FLAC_read(Sound_Sample *sample, void* buffer, Uint32 desired_frames)
00130 {
00131     Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
00132     drflac *dr = (drflac *) internal->decoder_private;
00133     const drflac_uint64 decoded_frames = drflac_read_pcm_frames_s16(dr,
00134                                                                     desired_frames,
00135                                                                     (drflac_int16 *) buffer);
00136     return (Uint32) decoded_frames;
00137 } /* FLAC_read */
00138 
00139 
00140 static int FLAC_rewind(Sound_Sample *sample)
00141 {
00142     Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
00143     drflac *dr = (drflac *) internal->decoder_private;
00144     return (drflac_seek_to_pcm_frame(dr, 0) == DRFLAC_TRUE);
00145 } /* FLAC_rewind */
00146 
00147 static int FLAC_seek(Sound_Sample *sample, Uint32 ms)
00148 {
00149     Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
00150     drflac *dr = (drflac *) internal->decoder_private;
00151     const float frames_per_ms = ((float) sample->actual.rate) / 1000.0f;
00152     const drflac_uint64 frame_offset = llroundf(frames_per_ms * ms);
00153     return (drflac_seek_to_pcm_frame(dr, frame_offset) == DRFLAC_TRUE);
00154 } /* FLAC_seek */
00155 
00156 
00157 static const char *extensions_flac[] = { "FLAC", "FLA", NULL };
00158 const Sound_DecoderFunctions __Sound_DecoderFunctions_FLAC =
00159 {
00160     {
00161         extensions_flac,
00162         "Free Lossless Audio Codec (FLAC)",
00163         "The DOSBox-X project"
00164     },
00165 
00166     FLAC_init,       /*   init() method */
00167     FLAC_quit,       /*   quit() method */
00168     FLAC_open,       /*   open() method */
00169     FLAC_close,      /*  close() method */
00170     FLAC_read,       /*   read() method */
00171     FLAC_rewind,     /* rewind() method */
00172     FLAC_seek        /*   seek() method */
00173 };
00174 
00175 /* end of flac.c ... */