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 MP3 decoder API implementation 00025 * --------------------------------------- 00026 * This decoder makes use of the dr_mp3 library by David Reid (mackron@gmail.com) 00027 * - dr_libs: https://github.com/mackron/dr_libs (source) 00028 * - dr_mp3: http://mackron.github.io/dr_mp3.html (website) 00029 */ 00030 #include <support.h> 00031 00032 //#include "mp3_seek_table.h" 00033 #define DR_MP3_IMPLEMENTATION 00034 #include "dr_mp3.h" 00035 00036 #include "SDL_sound.h" 00037 #define __SDL_SOUND_INTERNAL__ 00038 #include "SDL_sound_internal.h" 00039 00040 static constexpr char fast_seek_filename[] = "fastseek.lut"; 00041 00042 static size_t mp3_read(void* const pUserData, void* const pBufferOut, const size_t bytesToRead) 00043 { 00044 Uint8* ptr = static_cast<Uint8*>(pBufferOut); 00045 Sound_Sample* const sample = static_cast<Sound_Sample*>(pUserData); 00046 const Sound_SampleInternal* const internal = static_cast<const Sound_SampleInternal*>(sample->opaque); 00047 SDL_RWops* rwops = internal->rw; 00048 size_t bytes_read = 0; 00049 00050 while (bytes_read < bytesToRead) 00051 { 00052 const size_t rc = SDL_RWread(rwops, ptr, 1, bytesToRead - bytes_read); 00053 if (rc == 0) { 00054 sample->flags |= SOUND_SAMPLEFLAG_EOF; 00055 break; 00056 } /* if */ 00057 bytes_read += rc; 00058 ptr += rc; 00059 } /* while */ 00060 00061 return bytes_read; 00062 } /* mp3_read */ 00063 00064 static drmp3_bool32 mp3_seek(void* const pUserData, const Sint32 offset, const drmp3_seek_origin origin) 00065 { 00066 const Sint32 whence = (origin == drmp3_seek_origin_start) ? RW_SEEK_SET : RW_SEEK_CUR; 00067 Sound_Sample* const sample = static_cast<Sound_Sample*>(pUserData); 00068 Sound_SampleInternal* const internal = static_cast<Sound_SampleInternal*>(sample->opaque); 00069 return (SDL_RWseek(internal->rw, offset, whence) != -1) ? DRMP3_TRUE : DRMP3_FALSE; 00070 } /* mp3_seek */ 00071 00072 00073 static Sint32 MP3_init(void) 00074 { 00075 return 1; /* always succeeds. */ 00076 } /* MP3_init */ 00077 00078 00079 static void MP3_quit(void) 00080 { 00081 /* it's a no-op. */ 00082 } /* MP3_quit */ 00083 00084 static void MP3_close(Sound_Sample* const sample) 00085 { 00086 Sound_SampleInternal* const internal = static_cast<Sound_SampleInternal*>(sample->opaque); 00087 mp3_t* p_mp3 = static_cast<mp3_t*>(internal->decoder_private); 00088 if (p_mp3) { 00089 delete p_mp3->p_dr; 00090 p_mp3->p_dr = nullptr; 00091 delete p_mp3; 00092 internal->decoder_private = nullptr; 00093 } 00094 } /* MP3_close */ 00095 00096 static Uint32 MP3_read(Sound_Sample* const sample, void* buffer, Uint32 desired_frames) 00097 { 00098 Sound_SampleInternal* const internal = static_cast<Sound_SampleInternal*>(sample->opaque); 00099 mp3_t* p_mp3 = static_cast<mp3_t*>(internal->decoder_private); 00100 00101 // LOG_MSG("read-while: num_frames: %u", num_frames); 00102 return static_cast<Uint32>(drmp3_read_pcm_frames_s16(p_mp3->p_dr, 00103 static_cast<drmp3_uint64>(desired_frames), 00104 static_cast<drmp3_int16*>(buffer))); 00105 } /* MP3_read */ 00106 00107 static int32_t MP3_open(Sound_Sample* const sample, const char* const ext) 00108 { 00109 (void) ext; // deliberately unused 00110 Sound_SampleInternal* const internal = static_cast<Sound_SampleInternal*>(sample->opaque); 00111 00112 // Allocate our structures to be held by sample 00113 mp3_t* p_mp3 = new mp3_t; 00114 p_mp3->p_dr = new drmp3; 00115 00116 // Open the MP3 00117 if (drmp3_init(p_mp3->p_dr, mp3_read, mp3_seek, sample, nullptr) != DRMP3_TRUE) { 00118 SNDDBG(("MP3: Failed to open the data stream.\n")); 00119 delete p_mp3->p_dr; 00120 delete p_mp3; 00121 return 0; // failure 00122 } 00123 00124 // Assign our internal decoder to the mp3 object we've just opened 00125 internal->decoder_private = p_mp3; 00126 00127 bool result; 00128 // Count the MP3's frames 00129 const uint64_t num_frames = populate_seek_points(internal->rw, p_mp3, fast_seek_filename, result); 00130 if (!result) { 00131 SNDDBG(("MP3: Unable to count the number of PCM frames.\n")); 00132 MP3_close(sample); 00133 return 0; // failure 00134 } 00135 00136 // Populate the sample's properties from the MP3 00137 SNDDBG(("MP3: Accepting data stream.\n")); 00138 sample->flags = SOUND_SAMPLEFLAG_CANSEEK; 00139 sample->actual.channels = static_cast<uint8_t>(p_mp3->p_dr->channels); 00140 sample->actual.rate = p_mp3->p_dr->sampleRate; 00141 sample->actual.format = AUDIO_S16SYS; // native byte-order based on architecture 00142 // total_time needs milliseconds 00143 internal->total_time = static_cast<int32_t>(ceil_udivide(num_frames * 1000u, sample->actual.rate)); 00144 return 1; // success 00145 } /* MP3_open */ 00146 00147 static Sint32 MP3_rewind(Sound_Sample* const sample) 00148 { 00149 Sound_SampleInternal* const internal = static_cast<Sound_SampleInternal*>(sample->opaque); 00150 mp3_t* p_mp3 = static_cast<mp3_t*>(internal->decoder_private); 00151 return (drmp3_seek_to_start_of_stream(p_mp3->p_dr) == DRMP3_TRUE); 00152 } /* MP3_rewind */ 00153 00154 static Sint32 MP3_seek(Sound_Sample* const sample, const Uint32 ms) 00155 { 00156 Sound_SampleInternal* const internal = static_cast<Sound_SampleInternal*>(sample->opaque); 00157 mp3_t* p_mp3 = static_cast<mp3_t*>(internal->decoder_private); 00158 const uint64_t sample_rate = sample->actual.rate; 00159 const drmp3_uint64 pcm_frame = ceil_udivide(sample_rate * ms, 1000u); 00160 const drmp3_bool32 result = drmp3_seek_to_pcm_frame(p_mp3->p_dr, pcm_frame); 00161 return (result == DRMP3_TRUE); 00162 } /* MP3_seek */ 00163 00164 /* dr_mp3 will play layer 1 and 2 files, too */ 00165 static const char* extensions_mp3[] = { "MP3", "MP2", "MP1", nullptr }; 00166 00167 extern const Sound_DecoderFunctions __Sound_DecoderFunctions_MP3 = { 00168 { 00169 extensions_mp3, 00170 "MPEG-1 Audio Layer I-III", 00171 "The DOSBox-X project" 00172 }, 00173 00174 MP3_init, /* init() method */ 00175 MP3_quit, /* quit() method */ 00176 MP3_open, /* open() method */ 00177 MP3_close, /* close() method */ 00178 MP3_read, /* read() method */ 00179 MP3_rewind, /* rewind() method */ 00180 MP3_seek /* seek() method */ 00181 }; 00182 /* end of SDL_sound_mp3.c ... */