DOSBox-X
|
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 ... */