1 /*
2 * SPDX-License-Identifier: GPL-2.0-or-later
3 *
4 * Copyright (C) 2020-2021 The DOSBox Staging Team
5 * Copyright (C) 2018-2021 kcgen <kcgen@users.noreply.github.com>
6 * Copyright (C) 2001-2017 Ryan C. Gordon <icculus@icculus.org>
7 *
8 * This program is free software; you can redistribute it and/or modify
9 * it under the terms of the GNU General Public License as published by
10 * the Free Software Foundation; either version 2 of the License, or
11 * (at your option) any later version.
12 *
13 * This program is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
17 *
18 * You should have received a copy of the GNU General Public License along
19 * with this program; if not, write to the Free Software Foundation, Inc.,
20 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
21 */
22
23 /*
24 * DOSBox FLAC decoder API implementation
25 * --------------------------------------
26 * This decoder makes use of the dr_flac library by David Reid (mackron@gmail.com)
27 * - dr_libs: https://github.com/mackron/dr_libs (source)
28 * - dr_flac: http://mackron.github.io/dr_flac.html (website)
29 */
30
31 #include <math.h> /* for llroundf */
32
33 #include "SDL_sound.h"
34 #define __SDL_SOUND_INTERNAL__
35 #include "SDL_sound_internal.h"
36
37 #define DR_FLAC_IMPLEMENTATION
38 #define DR_FLAC_NO_STDIO 1
39 #define DR_FLAC_NO_WIN32_IO 1
40 #define DR_FLAC_NO_OGG 1
41 #define DR_FLAC_BUFFER_SIZE 8192
42 #include "dr_flac.h"
43
flac_read(void * pUserData,void * pBufferOut,size_t bytesToRead)44 static size_t flac_read(void* pUserData, void* pBufferOut, size_t bytesToRead)
45 {
46 Uint8 *ptr = (Uint8 *) pBufferOut;
47 Sound_Sample *sample = (Sound_Sample *) pUserData;
48 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
49 SDL_RWops *rwops = internal->rw;
50 size_t bytes_read = 0;
51
52 while (bytes_read < bytesToRead)
53 {
54 const size_t rc = SDL_RWread(rwops, ptr, 1, bytesToRead - bytes_read);
55 if (rc == 0) {
56 sample->flags |= SOUND_SAMPLEFLAG_EOF;
57 break;
58 } /* if */
59 bytes_read += rc;
60 ptr += rc;
61 } /* while */
62
63 return bytes_read;
64 } /* flac_read */
65
flac_seek(void * pUserData,int offset,drflac_seek_origin origin)66 static drflac_bool32 flac_seek(void* pUserData, int offset, drflac_seek_origin origin)
67 {
68 const int whence = (origin == drflac_seek_origin_start) ? RW_SEEK_SET : RW_SEEK_CUR;
69 Sound_Sample *sample = (Sound_Sample *) pUserData;
70 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
71 return (SDL_RWseek(internal->rw, offset, whence) != -1) ? DRFLAC_TRUE : DRFLAC_FALSE;
72 } /* flac_seek */
73
74
FLAC_init(void)75 static int FLAC_init(void)
76 {
77 return 1; /* always succeeds. */
78 } /* FLAC_init */
79
80
FLAC_quit(void)81 static void FLAC_quit(void)
82 {
83 /* it's a no-op. */
84 } /* FLAC_quit */
85
FLAC_open(Sound_Sample * sample,const char * ext)86 static int FLAC_open(Sound_Sample *sample, const char *ext)
87 {
88 (void) ext; // deliberately unused, but present for API compliance
89 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
90 drflac *dr = drflac_open(flac_read, flac_seek, sample, NULL);
91
92 if (!dr) {
93 BAIL_IF_MACRO(sample->flags & SOUND_SAMPLEFLAG_ERROR, ERR_IO_ERROR, 0);
94 BAIL_MACRO("FLAC: Not a FLAC stream.", 0);
95 } /* if */
96
97 SNDDBG(("FLAC: Accepting data stream.\n"));
98 sample->flags = SOUND_SAMPLEFLAG_CANSEEK;
99
100 sample->actual.channels = dr->channels;
101 sample->actual.rate = dr->sampleRate;
102 sample->actual.format = AUDIO_S16SYS; /* returns native byte-order based on architecture */
103
104 const Uint64 frames = (Uint64) dr->totalPCMFrameCount;
105 if (frames == 0) {
106 internal->total_time = -1;
107 }
108 else {
109 const Uint32 rate = (Uint32) dr->sampleRate;
110 internal->total_time = ( (Sint32)frames / rate) * 1000;
111 internal->total_time += ((frames % rate) * 1000) / rate;
112 } /* else */
113
114 internal->decoder_private = dr;
115
116 return 1;
117 } /* FLAC_open */
118
119
120
FLAC_close(Sound_Sample * sample)121 static void FLAC_close(Sound_Sample *sample)
122 {
123 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
124 drflac *dr = (drflac *) internal->decoder_private;
125 drflac_close(dr);
126 } /* FLAC_close */
127
128
FLAC_read(Sound_Sample * sample,void * buffer,Uint32 desired_frames)129 static Uint32 FLAC_read(Sound_Sample *sample, void* buffer, Uint32 desired_frames)
130 {
131 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
132 drflac *dr = (drflac *) internal->decoder_private;
133 const drflac_uint64 decoded_frames = drflac_read_pcm_frames_s16(dr,
134 desired_frames,
135 (drflac_int16 *) buffer);
136 return (Uint32) decoded_frames;
137 } /* FLAC_read */
138
139
FLAC_rewind(Sound_Sample * sample)140 static int FLAC_rewind(Sound_Sample *sample)
141 {
142 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
143 drflac *dr = (drflac *) internal->decoder_private;
144 return (drflac_seek_to_pcm_frame(dr, 0) == DRFLAC_TRUE);
145 } /* FLAC_rewind */
146
FLAC_seek(Sound_Sample * sample,Uint32 ms)147 static int FLAC_seek(Sound_Sample *sample, Uint32 ms)
148 {
149 Sound_SampleInternal *internal = (Sound_SampleInternal *) sample->opaque;
150 drflac *dr = (drflac *) internal->decoder_private;
151 const float frames_per_ms = ((float) sample->actual.rate) / 1000.0f;
152 const drflac_uint64 frame_offset = llroundf(frames_per_ms * ms);
153 return (drflac_seek_to_pcm_frame(dr, frame_offset) == DRFLAC_TRUE);
154 } /* FLAC_seek */
155
156
157 static const char *extensions_flac[] = { "FLAC", "FLA", NULL };
158 const Sound_DecoderFunctions __Sound_DecoderFunctions_FLAC =
159 {
160 {
161 extensions_flac,
162 "Free Lossless Audio Codec (FLAC)",
163 "The DOSBox Staging Team"
164 },
165
166 FLAC_init, /* init() method */
167 FLAC_quit, /* quit() method */
168 FLAC_open, /* open() method */
169 FLAC_close, /* close() method */
170 FLAC_read, /* read() method */
171 FLAC_rewind, /* rewind() method */
172 FLAC_seek /* seek() method */
173 };
174
175 /* end of flac.c ... */
176