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