gosu 1.4.1 → 1.4.4
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/dependencies/SDL_sound/SDL_sound.c +21 -63
- data/dependencies/SDL_sound/SDL_sound.h +2 -2
- data/dependencies/SDL_sound/SDL_sound_aiff.c +26 -23
- data/dependencies/SDL_sound/SDL_sound_au.c +8 -8
- data/dependencies/SDL_sound/SDL_sound_coreaudio.c +4 -5
- data/dependencies/SDL_sound/SDL_sound_flac.c +28 -30
- data/dependencies/SDL_sound/SDL_sound_internal.h +4 -4
- data/dependencies/SDL_sound/SDL_sound_modplug.c +1 -1
- data/dependencies/SDL_sound/SDL_sound_mp3.c +19 -23
- data/dependencies/SDL_sound/SDL_sound_raw.c +5 -6
- data/dependencies/SDL_sound/SDL_sound_shn.c +4 -4
- data/dependencies/SDL_sound/SDL_sound_voc.c +15 -15
- data/dependencies/SDL_sound/SDL_sound_vorbis.c +14 -7
- data/dependencies/SDL_sound/SDL_sound_wav.c +17 -17
- data/dependencies/SDL_sound/dr_flac.h +10840 -4779
- data/dependencies/SDL_sound/dr_mp3.h +2793 -1004
- data/dependencies/SDL_sound/libmodplug/fastmix.c +5 -0
- data/dependencies/SDL_sound/libmodplug/load_669.c +1 -1
- data/dependencies/SDL_sound/libmodplug/load_amf.c +1 -0
- data/dependencies/SDL_sound/libmodplug/load_ams.c +38 -22
- data/dependencies/SDL_sound/libmodplug/load_it.c +18 -14
- data/dependencies/SDL_sound/libmodplug/load_mdl.c +18 -9
- data/dependencies/SDL_sound/libmodplug/load_med.c +7 -6
- data/dependencies/SDL_sound/libmodplug/load_mt2.c +36 -17
- data/dependencies/SDL_sound/libmodplug/load_okt.c +51 -24
- data/dependencies/SDL_sound/libmodplug/load_psm.c +4 -2
- data/dependencies/SDL_sound/libmodplug/load_s3m.c +4 -4
- data/dependencies/SDL_sound/libmodplug/load_ult.c +4 -3
- data/dependencies/SDL_sound/libmodplug/load_xm.c +5 -5
- data/dependencies/SDL_sound/libmodplug/snd_fx.c +8 -1
- data/dependencies/SDL_sound/libmodplug/sndfile.c +21 -4
- data/dependencies/SDL_sound/stb_vorbis.h +10 -18
- data/dependencies/mojoAL/mojoal.c +260 -6
- data/dependencies/stb/stb_image.h +208 -73
- data/dependencies/stb/stb_image_write.h +57 -23
- data/dependencies/stb/stb_truetype.h +345 -279
- data/dependencies/utf8proc/utf8proc.c +37 -18
- data/dependencies/utf8proc/utf8proc.h +17 -5
- data/dependencies/utf8proc/utf8proc_data.h +12012 -10089
- data/ext/gosu/extconf.rb +6 -3
- data/include/Gosu/Buttons.hpp +103 -103
- data/include/Gosu/Directories.hpp +31 -24
- data/include/Gosu/Font.hpp +4 -2
- data/include/Gosu/Gosu.hpp +5 -8
- data/include/Gosu/IO.hpp +0 -3
- data/include/Gosu/Input.hpp +7 -1
- data/include/Gosu/Math.hpp +0 -3
- data/include/Gosu/TextInput.hpp +3 -3
- data/include/Gosu/Timing.hpp +3 -6
- data/include/Gosu/Version.hpp +1 -1
- data/include/Gosu/Window.hpp +3 -2
- data/rdoc/gosu.rb +16 -2
- data/src/Audio.cpp +2 -2
- data/src/AudioFileAudioToolbox.cpp +1 -1
- data/src/AudioFileSDLSound.cpp +1 -1
- data/src/AudioImpl.cpp +0 -7
- data/src/AudioImpl.hpp +1 -3
- data/src/BitmapIO.cpp +23 -2
- data/src/BlockAllocator.cpp +1 -1
- data/src/DirectoriesApple.cpp +25 -24
- data/src/DirectoriesUnix.cpp +14 -12
- data/src/DirectoriesWin.cpp +26 -30
- data/src/FileUnix.cpp +1 -1
- data/src/FileWin.cpp +1 -1
- data/src/Font.cpp +13 -3
- data/src/Graphics.cpp +1 -1
- data/src/Image.cpp +10 -15
- data/src/Input.cpp +16 -1
- data/src/InputUIKit.cpp +1 -1
- data/src/Macro.cpp +1 -1
- data/src/RubyGosu.cxx +76 -34
- data/src/TextInput.cpp +1 -1
- data/src/TimingApple.cpp +2 -2
- data/src/TimingUnix.cpp +3 -7
- data/src/TimingWin.cpp +1 -2
- data/src/TrueTypeFont.cpp +1 -1
- data/src/Window.cpp +5 -4
- data/src/WindowUIKit.cpp +1 -1
- metadata +3 -3
@@ -1,62 +1,87 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
//
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
1
|
+
/*
|
2
|
+
MP3 audio decoder. Choice of public domain or MIT-0. See license statements at the end of this file.
|
3
|
+
dr_mp3 - v0.6.32 - 2021-12-11
|
4
|
+
|
5
|
+
David Reid - mackron@gmail.com
|
6
|
+
|
7
|
+
GitHub: https://github.com/mackron/dr_libs
|
8
|
+
|
9
|
+
Based on minimp3 (https://github.com/lieff/minimp3) which is where the real work was done. See the bottom of this file for differences between minimp3 and dr_mp3.
|
10
|
+
*/
|
11
|
+
|
12
|
+
/*
|
13
|
+
RELEASE NOTES - VERSION 0.6
|
14
|
+
===========================
|
15
|
+
Version 0.6 includes breaking changes with the configuration of decoders. The ability to customize the number of output channels and the sample rate has been
|
16
|
+
removed. You must now use the channel count and sample rate reported by the MP3 stream itself, and all channel and sample rate conversion must be done
|
17
|
+
yourself.
|
18
|
+
|
19
|
+
|
20
|
+
Changes to Initialization
|
21
|
+
-------------------------
|
22
|
+
Previously, `drmp3_init()`, etc. took a pointer to a `drmp3_config` object that allowed you to customize the output channels and sample rate. This has been
|
23
|
+
removed. If you need the old behaviour you will need to convert the data yourself or just not upgrade. The following APIs have changed.
|
24
|
+
|
25
|
+
`drmp3_init()`
|
26
|
+
`drmp3_init_memory()`
|
27
|
+
`drmp3_init_file()`
|
28
|
+
|
29
|
+
|
30
|
+
Miscellaneous Changes
|
31
|
+
---------------------
|
32
|
+
Support for loading a file from a `wchar_t` string has been added via the `drmp3_init_file_w()` API.
|
33
|
+
*/
|
34
|
+
|
35
|
+
/*
|
36
|
+
Introducation
|
37
|
+
=============
|
38
|
+
dr_mp3 is a single file library. To use it, do something like the following in one .c file.
|
39
|
+
|
40
|
+
```c
|
41
|
+
#define DR_MP3_IMPLEMENTATION
|
42
|
+
#include "dr_mp3.h"
|
43
|
+
```
|
44
|
+
|
45
|
+
You can then #include this file in other parts of the program as you would with any other header file. To decode audio data, do something like the following:
|
46
|
+
|
47
|
+
```c
|
48
|
+
drmp3 mp3;
|
49
|
+
if (!drmp3_init_file(&mp3, "MySong.mp3", NULL)) {
|
50
|
+
// Failed to open file
|
51
|
+
}
|
52
|
+
|
53
|
+
...
|
54
|
+
|
55
|
+
drmp3_uint64 framesRead = drmp3_read_pcm_frames_f32(pMP3, framesToRead, pFrames);
|
56
|
+
```
|
57
|
+
|
58
|
+
The drmp3 object is transparent so you can get access to the channel count and sample rate like so:
|
59
|
+
|
60
|
+
```
|
61
|
+
drmp3_uint32 channels = mp3.channels;
|
62
|
+
drmp3_uint32 sampleRate = mp3.sampleRate;
|
63
|
+
```
|
64
|
+
|
65
|
+
The example above initializes a decoder from a file, but you can also initialize it from a block of memory and read and seek callbacks with
|
66
|
+
`drmp3_init_memory()` and `drmp3_init()` respectively.
|
67
|
+
|
68
|
+
You do not need to do any annoying memory management when reading PCM frames - this is all managed internally. You can request any number of PCM frames in each
|
69
|
+
call to `drmp3_read_pcm_frames_f32()` and it will return as many PCM frames as it can, up to the requested amount.
|
70
|
+
|
71
|
+
You can also decode an entire file in one go with `drmp3_open_and_read_pcm_frames_f32()`, `drmp3_open_memory_and_read_pcm_frames_f32()` and
|
72
|
+
`drmp3_open_file_and_read_pcm_frames_f32()`.
|
73
|
+
|
74
|
+
|
75
|
+
Build Options
|
76
|
+
=============
|
77
|
+
#define these options before including this file.
|
78
|
+
|
79
|
+
#define DR_MP3_NO_STDIO
|
80
|
+
Disable drmp3_init_file(), etc.
|
81
|
+
|
82
|
+
#define DR_MP3_NO_SIMD
|
83
|
+
Disable SIMD optimizations.
|
84
|
+
*/
|
60
85
|
|
61
86
|
#ifndef dr_mp3_h
|
62
87
|
#define dr_mp3_h
|
@@ -65,47 +90,170 @@
|
|
65
90
|
extern "C" {
|
66
91
|
#endif
|
67
92
|
|
68
|
-
#
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
typedef
|
81
|
-
typedef
|
82
|
-
typedef
|
83
|
-
typedef
|
84
|
-
typedef
|
85
|
-
|
86
|
-
typedef signed __int64
|
87
|
-
typedef unsigned __int64
|
93
|
+
#define DRMP3_STRINGIFY(x) #x
|
94
|
+
#define DRMP3_XSTRINGIFY(x) DRMP3_STRINGIFY(x)
|
95
|
+
|
96
|
+
#define DRMP3_VERSION_MAJOR 0
|
97
|
+
#define DRMP3_VERSION_MINOR 6
|
98
|
+
#define DRMP3_VERSION_REVISION 32
|
99
|
+
#define DRMP3_VERSION_STRING DRMP3_XSTRINGIFY(DRMP3_VERSION_MAJOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_MINOR) "." DRMP3_XSTRINGIFY(DRMP3_VERSION_REVISION)
|
100
|
+
|
101
|
+
#include <stddef.h> /* For size_t. */
|
102
|
+
|
103
|
+
/* Sized types. */
|
104
|
+
typedef signed char drmp3_int8;
|
105
|
+
typedef unsigned char drmp3_uint8;
|
106
|
+
typedef signed short drmp3_int16;
|
107
|
+
typedef unsigned short drmp3_uint16;
|
108
|
+
typedef signed int drmp3_int32;
|
109
|
+
typedef unsigned int drmp3_uint32;
|
110
|
+
#if defined(_MSC_VER) && !defined(__clang__)
|
111
|
+
typedef signed __int64 drmp3_int64;
|
112
|
+
typedef unsigned __int64 drmp3_uint64;
|
113
|
+
#else
|
114
|
+
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
|
115
|
+
#pragma GCC diagnostic push
|
116
|
+
#pragma GCC diagnostic ignored "-Wlong-long"
|
117
|
+
#if defined(__clang__)
|
118
|
+
#pragma GCC diagnostic ignored "-Wc++11-long-long"
|
119
|
+
#endif
|
120
|
+
#endif
|
121
|
+
typedef signed long long drmp3_int64;
|
122
|
+
typedef unsigned long long drmp3_uint64;
|
123
|
+
#if defined(__clang__) || (defined(__GNUC__) && (__GNUC__ > 4 || (__GNUC__ == 4 && __GNUC_MINOR__ >= 6)))
|
124
|
+
#pragma GCC diagnostic pop
|
125
|
+
#endif
|
126
|
+
#endif
|
127
|
+
#if defined(__LP64__) || defined(_WIN64) || (defined(__x86_64__) && !defined(__ILP32__)) || defined(_M_X64) || defined(__ia64) || defined (_M_IA64) || defined(__aarch64__) || defined(_M_ARM64) || defined(__powerpc64__)
|
128
|
+
typedef drmp3_uint64 drmp3_uintptr;
|
129
|
+
#else
|
130
|
+
typedef drmp3_uint32 drmp3_uintptr;
|
131
|
+
#endif
|
132
|
+
typedef drmp3_uint8 drmp3_bool8;
|
133
|
+
typedef drmp3_uint32 drmp3_bool32;
|
134
|
+
#define DRMP3_TRUE 1
|
135
|
+
#define DRMP3_FALSE 0
|
136
|
+
|
137
|
+
#if !defined(DRMP3_API)
|
138
|
+
#if defined(DRMP3_DLL)
|
139
|
+
#if defined(_WIN32)
|
140
|
+
#define DRMP3_DLL_IMPORT __declspec(dllimport)
|
141
|
+
#define DRMP3_DLL_EXPORT __declspec(dllexport)
|
142
|
+
#define DRMP3_DLL_PRIVATE static
|
143
|
+
#else
|
144
|
+
#if defined(__GNUC__) && __GNUC__ >= 4
|
145
|
+
#define DRMP3_DLL_IMPORT __attribute__((visibility("default")))
|
146
|
+
#define DRMP3_DLL_EXPORT __attribute__((visibility("default")))
|
147
|
+
#define DRMP3_DLL_PRIVATE __attribute__((visibility("hidden")))
|
148
|
+
#else
|
149
|
+
#define DRMP3_DLL_IMPORT
|
150
|
+
#define DRMP3_DLL_EXPORT
|
151
|
+
#define DRMP3_DLL_PRIVATE static
|
152
|
+
#endif
|
153
|
+
#endif
|
154
|
+
|
155
|
+
#if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION)
|
156
|
+
#define DRMP3_API DRMP3_DLL_EXPORT
|
157
|
+
#else
|
158
|
+
#define DRMP3_API DRMP3_DLL_IMPORT
|
159
|
+
#endif
|
160
|
+
#define DRMP3_PRIVATE DRMP3_DLL_PRIVATE
|
161
|
+
#else
|
162
|
+
#define DRMP3_API extern
|
163
|
+
#define DRMP3_PRIVATE static
|
164
|
+
#endif
|
165
|
+
#endif
|
166
|
+
|
167
|
+
typedef drmp3_int32 drmp3_result;
|
168
|
+
#define DRMP3_SUCCESS 0
|
169
|
+
#define DRMP3_ERROR -1 /* A generic error. */
|
170
|
+
#define DRMP3_INVALID_ARGS -2
|
171
|
+
#define DRMP3_INVALID_OPERATION -3
|
172
|
+
#define DRMP3_OUT_OF_MEMORY -4
|
173
|
+
#define DRMP3_OUT_OF_RANGE -5
|
174
|
+
#define DRMP3_ACCESS_DENIED -6
|
175
|
+
#define DRMP3_DOES_NOT_EXIST -7
|
176
|
+
#define DRMP3_ALREADY_EXISTS -8
|
177
|
+
#define DRMP3_TOO_MANY_OPEN_FILES -9
|
178
|
+
#define DRMP3_INVALID_FILE -10
|
179
|
+
#define DRMP3_TOO_BIG -11
|
180
|
+
#define DRMP3_PATH_TOO_LONG -12
|
181
|
+
#define DRMP3_NAME_TOO_LONG -13
|
182
|
+
#define DRMP3_NOT_DIRECTORY -14
|
183
|
+
#define DRMP3_IS_DIRECTORY -15
|
184
|
+
#define DRMP3_DIRECTORY_NOT_EMPTY -16
|
185
|
+
#define DRMP3_END_OF_FILE -17
|
186
|
+
#define DRMP3_NO_SPACE -18
|
187
|
+
#define DRMP3_BUSY -19
|
188
|
+
#define DRMP3_IO_ERROR -20
|
189
|
+
#define DRMP3_INTERRUPT -21
|
190
|
+
#define DRMP3_UNAVAILABLE -22
|
191
|
+
#define DRMP3_ALREADY_IN_USE -23
|
192
|
+
#define DRMP3_BAD_ADDRESS -24
|
193
|
+
#define DRMP3_BAD_SEEK -25
|
194
|
+
#define DRMP3_BAD_PIPE -26
|
195
|
+
#define DRMP3_DEADLOCK -27
|
196
|
+
#define DRMP3_TOO_MANY_LINKS -28
|
197
|
+
#define DRMP3_NOT_IMPLEMENTED -29
|
198
|
+
#define DRMP3_NO_MESSAGE -30
|
199
|
+
#define DRMP3_BAD_MESSAGE -31
|
200
|
+
#define DRMP3_NO_DATA_AVAILABLE -32
|
201
|
+
#define DRMP3_INVALID_DATA -33
|
202
|
+
#define DRMP3_TIMEOUT -34
|
203
|
+
#define DRMP3_NO_NETWORK -35
|
204
|
+
#define DRMP3_NOT_UNIQUE -36
|
205
|
+
#define DRMP3_NOT_SOCKET -37
|
206
|
+
#define DRMP3_NO_ADDRESS -38
|
207
|
+
#define DRMP3_BAD_PROTOCOL -39
|
208
|
+
#define DRMP3_PROTOCOL_UNAVAILABLE -40
|
209
|
+
#define DRMP3_PROTOCOL_NOT_SUPPORTED -41
|
210
|
+
#define DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED -42
|
211
|
+
#define DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED -43
|
212
|
+
#define DRMP3_SOCKET_NOT_SUPPORTED -44
|
213
|
+
#define DRMP3_CONNECTION_RESET -45
|
214
|
+
#define DRMP3_ALREADY_CONNECTED -46
|
215
|
+
#define DRMP3_NOT_CONNECTED -47
|
216
|
+
#define DRMP3_CONNECTION_REFUSED -48
|
217
|
+
#define DRMP3_NO_HOST -49
|
218
|
+
#define DRMP3_IN_PROGRESS -50
|
219
|
+
#define DRMP3_CANCELLED -51
|
220
|
+
#define DRMP3_MEMORY_ALREADY_MAPPED -52
|
221
|
+
#define DRMP3_AT_END -53
|
222
|
+
|
223
|
+
|
224
|
+
#define DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME 1152
|
225
|
+
#define DRMP3_MAX_SAMPLES_PER_FRAME (DRMP3_MAX_PCM_FRAMES_PER_MP3_FRAME*2)
|
226
|
+
|
227
|
+
#ifdef _MSC_VER
|
228
|
+
#define DRMP3_INLINE __forceinline
|
229
|
+
#elif defined(__GNUC__)
|
230
|
+
/*
|
231
|
+
I've had a bug report where GCC is emitting warnings about functions possibly not being inlineable. This warning happens when
|
232
|
+
the __attribute__((always_inline)) attribute is defined without an "inline" statement. I think therefore there must be some
|
233
|
+
case where "__inline__" is not always defined, thus the compiler emitting these warnings. When using -std=c89 or -ansi on the
|
234
|
+
command line, we cannot use the "inline" keyword and instead need to use "__inline__". In an attempt to work around this issue
|
235
|
+
I am using "__inline__" only when we're compiling in strict ANSI mode.
|
236
|
+
*/
|
237
|
+
#if defined(__STRICT_ANSI__)
|
238
|
+
#define DRMP3_INLINE __inline__ __attribute__((always_inline))
|
239
|
+
#else
|
240
|
+
#define DRMP3_INLINE inline __attribute__((always_inline))
|
241
|
+
#endif
|
242
|
+
#elif defined(__WATCOMC__)
|
243
|
+
#define DRMP3_INLINE __inline
|
88
244
|
#else
|
89
|
-
#
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
#define DRMP3_TRUE 1
|
102
|
-
#define DRMP3_FALSE 0
|
103
|
-
|
104
|
-
#define DRMP3_MAX_SAMPLES_PER_FRAME (1152*2)
|
105
|
-
|
106
|
-
|
107
|
-
// Low Level Push API
|
108
|
-
// ==================
|
245
|
+
#define DRMP3_INLINE
|
246
|
+
#endif
|
247
|
+
|
248
|
+
|
249
|
+
DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision);
|
250
|
+
DRMP3_API const char* drmp3_version_string(void);
|
251
|
+
|
252
|
+
|
253
|
+
/*
|
254
|
+
Low Level Push API
|
255
|
+
==================
|
256
|
+
*/
|
109
257
|
typedef struct
|
110
258
|
{
|
111
259
|
int frame_bytes, channels, hz, layer, bitrate_kbps;
|
@@ -115,102 +263,78 @@ typedef struct
|
|
115
263
|
{
|
116
264
|
float mdct_overlap[2][9*32], qmf_state[15*2*32];
|
117
265
|
int reserv, free_format_bytes;
|
118
|
-
|
266
|
+
drmp3_uint8 header[4], reserv_buf[511];
|
119
267
|
} drmp3dec;
|
120
268
|
|
121
|
-
|
122
|
-
void drmp3dec_init(drmp3dec *dec);
|
269
|
+
/* Initializes a low level decoder. */
|
270
|
+
DRMP3_API void drmp3dec_init(drmp3dec *dec);
|
123
271
|
|
124
|
-
|
125
|
-
int drmp3dec_decode_frame(drmp3dec *dec, const
|
272
|
+
/* Reads a frame from a low level decoder. */
|
273
|
+
DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info);
|
126
274
|
|
127
|
-
|
128
|
-
void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out,
|
275
|
+
/* Helper for converting between f32 and s16. */
|
276
|
+
DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples);
|
129
277
|
|
130
278
|
|
131
279
|
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
typedef drmp3_uint64 (* drmp3_src_read_proc)(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, void* pUserData); // Returns the number of frames that were read.
|
137
|
-
|
280
|
+
/*
|
281
|
+
Main API (Pull API)
|
282
|
+
===================
|
283
|
+
*/
|
138
284
|
typedef enum
|
139
285
|
{
|
140
|
-
|
141
|
-
|
142
|
-
}
|
286
|
+
drmp3_seek_origin_start,
|
287
|
+
drmp3_seek_origin_current
|
288
|
+
} drmp3_seek_origin;
|
143
289
|
|
144
|
-
#define DRMP3_SRC_CACHE_SIZE_IN_FRAMES 512
|
145
290
|
typedef struct
|
146
291
|
{
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
}
|
292
|
+
drmp3_uint64 seekPosInBytes; /* Points to the first byte of an MP3 frame. */
|
293
|
+
drmp3_uint64 pcmFrameIndex; /* The index of the PCM frame this seek point targets. */
|
294
|
+
drmp3_uint16 mp3FramesToDiscard; /* The number of whole MP3 frames to be discarded before pcmFramesToDiscard. */
|
295
|
+
drmp3_uint16 pcmFramesToDiscard; /* The number of leading samples to read and discard. These are discarded after mp3FramesToDiscard. */
|
296
|
+
} drmp3_seek_point;
|
152
297
|
|
153
|
-
|
154
|
-
|
155
|
-
drmp3_uint32 sampleRateIn;
|
156
|
-
drmp3_uint32 sampleRateOut;
|
157
|
-
drmp3_uint32 channels;
|
158
|
-
drmp3_src_algorithm algorithm;
|
159
|
-
drmp3_uint32 cacheSizeInFrames; // The number of frames to read from the client at a time.
|
160
|
-
} drmp3_src_config;
|
298
|
+
/*
|
299
|
+
Callback for when data is read. Return value is the number of bytes actually read.
|
161
300
|
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
drmp3_src_read_proc onRead;
|
166
|
-
void* pUserData;
|
167
|
-
float bin[256];
|
168
|
-
drmp3_src_cache cache; // <-- For simplifying and optimizing client -> memory reading.
|
169
|
-
union
|
170
|
-
{
|
171
|
-
struct
|
172
|
-
{
|
173
|
-
float alpha;
|
174
|
-
drmp3_bool32 isPrevFramesLoaded : 1;
|
175
|
-
drmp3_bool32 isNextFramesLoaded : 1;
|
176
|
-
} linear;
|
177
|
-
} algo;
|
178
|
-
};
|
301
|
+
pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family.
|
302
|
+
pBufferOut [out] The output buffer.
|
303
|
+
bytesToRead [in] The number of bytes to read.
|
179
304
|
|
180
|
-
|
181
|
-
{
|
182
|
-
drmp3_seek_origin_start,
|
183
|
-
drmp3_seek_origin_current
|
184
|
-
} drmp3_seek_origin;
|
305
|
+
Returns the number of bytes actually read.
|
185
306
|
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
// pBufferOut [out] The output buffer.
|
190
|
-
// bytesToRead [in] The number of bytes to read.
|
191
|
-
//
|
192
|
-
// Returns the number of bytes actually read.
|
193
|
-
//
|
194
|
-
// A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until
|
195
|
-
// either the entire bytesToRead is filled or you have reached the end of the stream.
|
307
|
+
A return value of less than bytesToRead indicates the end of the stream. Do _not_ return from this callback until
|
308
|
+
either the entire bytesToRead is filled or you have reached the end of the stream.
|
309
|
+
*/
|
196
310
|
typedef size_t (* drmp3_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead);
|
197
311
|
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
312
|
+
/*
|
313
|
+
Callback for when data needs to be seeked.
|
314
|
+
|
315
|
+
pUserData [in] The user data that was passed to drmp3_init(), drmp3_open() and family.
|
316
|
+
offset [in] The number of bytes to move, relative to the origin. Will never be negative.
|
317
|
+
origin [in] The origin of the seek - the current position or the start of the stream.
|
318
|
+
|
319
|
+
Returns whether or not the seek was successful.
|
320
|
+
|
321
|
+
Whether or not it is relative to the beginning or current position is determined by the "origin" parameter which
|
322
|
+
will be either drmp3_seek_origin_start or drmp3_seek_origin_current.
|
323
|
+
*/
|
208
324
|
typedef drmp3_bool32 (* drmp3_seek_proc)(void* pUserData, int offset, drmp3_seek_origin origin);
|
209
325
|
|
210
326
|
typedef struct
|
211
327
|
{
|
212
|
-
|
213
|
-
|
328
|
+
void* pUserData;
|
329
|
+
void* (* onMalloc)(size_t sz, void* pUserData);
|
330
|
+
void* (* onRealloc)(void* p, size_t sz, void* pUserData);
|
331
|
+
void (* onFree)(void* p, void* pUserData);
|
332
|
+
} drmp3_allocation_callbacks;
|
333
|
+
|
334
|
+
typedef struct
|
335
|
+
{
|
336
|
+
drmp3_uint32 channels;
|
337
|
+
drmp3_uint32 sampleRate;
|
214
338
|
} drmp3_config;
|
215
339
|
|
216
340
|
typedef struct
|
@@ -222,14 +346,19 @@ typedef struct
|
|
222
346
|
drmp3_read_proc onRead;
|
223
347
|
drmp3_seek_proc onSeek;
|
224
348
|
void* pUserData;
|
225
|
-
|
226
|
-
drmp3_uint32
|
227
|
-
drmp3_uint32
|
228
|
-
drmp3_uint32
|
229
|
-
|
230
|
-
|
349
|
+
drmp3_allocation_callbacks allocationCallbacks;
|
350
|
+
drmp3_uint32 mp3FrameChannels; /* The number of channels in the currently loaded MP3 frame. Internal use only. */
|
351
|
+
drmp3_uint32 mp3FrameSampleRate; /* The sample rate of the currently loaded MP3 frame. Internal use only. */
|
352
|
+
drmp3_uint32 pcmFramesConsumedInMP3Frame;
|
353
|
+
drmp3_uint32 pcmFramesRemainingInMP3Frame;
|
354
|
+
drmp3_uint8 pcmFrames[sizeof(float)*DRMP3_MAX_SAMPLES_PER_FRAME]; /* <-- Multipled by sizeof(float) to ensure there's enough room for DR_MP3_FLOAT_OUTPUT. */
|
355
|
+
drmp3_uint64 currentPCMFrame; /* The current PCM frame, globally, based on the output sample rate. Mainly used for seeking. */
|
356
|
+
drmp3_uint64 streamCursor; /* The current byte the decoder is sitting on in the raw stream. */
|
357
|
+
drmp3_seek_point* pSeekPoints; /* NULL by default. Set with drmp3_bind_seek_table(). Memory is owned by the client. dr_mp3 will never attempt to free this pointer. */
|
358
|
+
drmp3_uint32 seekPointCount; /* The number of items in pSeekPoints. When set to 0 assumes to no seek table. Defaults to zero. */
|
231
359
|
size_t dataSize;
|
232
360
|
size_t dataCapacity;
|
361
|
+
size_t dataConsumed;
|
233
362
|
drmp3_uint8* pData;
|
234
363
|
drmp3_bool32 atEnd : 1;
|
235
364
|
struct
|
@@ -237,85 +366,184 @@ typedef struct
|
|
237
366
|
const drmp3_uint8* pData;
|
238
367
|
size_t dataSize;
|
239
368
|
size_t currentReadPos;
|
240
|
-
} memory;
|
369
|
+
} memory; /* Only used for decoders that were opened against a block of memory. */
|
241
370
|
} drmp3;
|
242
371
|
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
372
|
+
/*
|
373
|
+
Initializes an MP3 decoder.
|
374
|
+
|
375
|
+
onRead [in] The function to call when data needs to be read from the client.
|
376
|
+
onSeek [in] The function to call when the read position of the client data needs to move.
|
377
|
+
pUserData [in, optional] A pointer to application defined data that will be passed to onRead and onSeek.
|
378
|
+
|
379
|
+
Returns true if successful; false otherwise.
|
380
|
+
|
381
|
+
Close the loader with drmp3_uninit().
|
382
|
+
|
383
|
+
See also: drmp3_init_file(), drmp3_init_memory(), drmp3_uninit()
|
384
|
+
*/
|
385
|
+
DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks);
|
386
|
+
|
387
|
+
/*
|
388
|
+
Initializes an MP3 decoder from a block of memory.
|
389
|
+
|
390
|
+
This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for
|
391
|
+
the lifetime of the drmp3 object.
|
392
|
+
|
393
|
+
The buffer should contain the contents of the entire MP3 file.
|
394
|
+
*/
|
395
|
+
DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks);
|
263
396
|
|
264
397
|
#ifndef DR_MP3_NO_STDIO
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
|
281
|
-
|
282
|
-
|
283
|
-
|
284
|
-
|
285
|
-
|
286
|
-
|
287
|
-
|
288
|
-
|
289
|
-
|
290
|
-
|
291
|
-
|
292
|
-
|
293
|
-
|
398
|
+
/*
|
399
|
+
Initializes an MP3 decoder from a file.
|
400
|
+
|
401
|
+
This holds the internal FILE object until drmp3_uninit() is called. Keep this in mind if you're caching drmp3
|
402
|
+
objects because the operating system may restrict the number of file handles an application can have open at
|
403
|
+
any given time.
|
404
|
+
*/
|
405
|
+
DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks);
|
406
|
+
DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks);
|
407
|
+
#endif
|
408
|
+
|
409
|
+
/*
|
410
|
+
Uninitializes an MP3 decoder.
|
411
|
+
*/
|
412
|
+
DRMP3_API void drmp3_uninit(drmp3* pMP3);
|
413
|
+
|
414
|
+
/*
|
415
|
+
Reads PCM frames as interleaved 32-bit IEEE floating point PCM.
|
416
|
+
|
417
|
+
Note that framesToRead specifies the number of PCM frames to read, _not_ the number of MP3 frames.
|
418
|
+
*/
|
419
|
+
DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut);
|
420
|
+
|
421
|
+
/*
|
422
|
+
Reads PCM frames as interleaved signed 16-bit integer PCM.
|
423
|
+
|
424
|
+
Note that framesToRead specifies the number of PCM frames to read, _not_ the number of MP3 frames.
|
425
|
+
*/
|
426
|
+
DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut);
|
427
|
+
|
428
|
+
/*
|
429
|
+
Seeks to a specific frame.
|
430
|
+
|
431
|
+
Note that this is _not_ an MP3 frame, but rather a PCM frame.
|
432
|
+
*/
|
433
|
+
DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex);
|
434
|
+
|
435
|
+
/*
|
436
|
+
Calculates the total number of PCM frames in the MP3 stream. Cannot be used for infinite streams such as internet
|
437
|
+
radio. Runs in linear time. Returns 0 on error.
|
438
|
+
*/
|
439
|
+
DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3);
|
440
|
+
|
441
|
+
/*
|
442
|
+
Calculates the total number of MP3 frames in the MP3 stream. Cannot be used for infinite streams such as internet
|
443
|
+
radio. Runs in linear time. Returns 0 on error.
|
444
|
+
*/
|
445
|
+
DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3);
|
446
|
+
|
447
|
+
/*
|
448
|
+
Calculates the total number of MP3 and PCM frames in the MP3 stream. Cannot be used for infinite streams such as internet
|
449
|
+
radio. Runs in linear time. Returns 0 on error.
|
450
|
+
|
451
|
+
This is equivalent to calling drmp3_get_mp3_frame_count() and drmp3_get_pcm_frame_count() except that it's more efficient.
|
452
|
+
*/
|
453
|
+
DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount);
|
454
|
+
|
455
|
+
/*
|
456
|
+
Calculates the seekpoints based on PCM frames. This is slow.
|
457
|
+
|
458
|
+
pSeekpoint count is a pointer to a uint32 containing the seekpoint count. On input it contains the desired count.
|
459
|
+
On output it contains the actual count. The reason for this design is that the client may request too many
|
460
|
+
seekpoints, in which case dr_mp3 will return a corrected count.
|
461
|
+
|
462
|
+
Note that seektable seeking is not quite sample exact when the MP3 stream contains inconsistent sample rates.
|
463
|
+
*/
|
464
|
+
DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints);
|
465
|
+
|
466
|
+
/*
|
467
|
+
Binds a seek table to the decoder.
|
468
|
+
|
469
|
+
This does _not_ make a copy of pSeekPoints - it only references it. It is up to the application to ensure this
|
470
|
+
remains valid while it is bound to the decoder.
|
471
|
+
|
472
|
+
Use drmp3_calculate_seek_points() to calculate the seek points.
|
473
|
+
*/
|
474
|
+
DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints);
|
475
|
+
|
476
|
+
|
477
|
+
/*
|
478
|
+
Opens an decodes an entire MP3 stream as a single operation.
|
479
|
+
|
480
|
+
On output pConfig will receive the channel count and sample rate of the stream.
|
481
|
+
|
482
|
+
Free the returned pointer with drmp3_free().
|
483
|
+
*/
|
484
|
+
DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
|
485
|
+
DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
|
486
|
+
|
487
|
+
DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
|
488
|
+
DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
|
489
|
+
|
294
490
|
#ifndef DR_MP3_NO_STDIO
|
295
|
-
float*
|
491
|
+
DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
|
492
|
+
DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks);
|
296
493
|
#endif
|
297
494
|
|
298
|
-
|
299
|
-
|
495
|
+
/*
|
496
|
+
Allocates a block of memory on the heap.
|
497
|
+
*/
|
498
|
+
DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks);
|
499
|
+
|
500
|
+
/*
|
501
|
+
Frees any memory that was allocated by a public drmp3 API.
|
502
|
+
*/
|
503
|
+
DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks);
|
300
504
|
|
301
505
|
#ifdef __cplusplus
|
302
506
|
}
|
303
507
|
#endif
|
304
|
-
#endif
|
508
|
+
#endif /* dr_mp3_h */
|
305
509
|
|
306
510
|
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
511
|
+
/************************************************************************************************************************************************************
|
512
|
+
************************************************************************************************************************************************************
|
513
|
+
|
514
|
+
IMPLEMENTATION
|
515
|
+
|
516
|
+
************************************************************************************************************************************************************
|
517
|
+
************************************************************************************************************************************************************/
|
518
|
+
#if defined(DR_MP3_IMPLEMENTATION) || defined(DRMP3_IMPLEMENTATION)
|
519
|
+
#ifndef dr_mp3_c
|
520
|
+
#define dr_mp3_c
|
521
|
+
|
313
522
|
#include <stdlib.h>
|
314
523
|
#include <string.h>
|
315
|
-
#include <
|
316
|
-
|
524
|
+
#include <limits.h> /* For INT_MAX */
|
525
|
+
|
526
|
+
DRMP3_API void drmp3_version(drmp3_uint32* pMajor, drmp3_uint32* pMinor, drmp3_uint32* pRevision)
|
527
|
+
{
|
528
|
+
if (pMajor) {
|
529
|
+
*pMajor = DRMP3_VERSION_MAJOR;
|
530
|
+
}
|
531
|
+
|
532
|
+
if (pMinor) {
|
533
|
+
*pMinor = DRMP3_VERSION_MINOR;
|
534
|
+
}
|
535
|
+
|
536
|
+
if (pRevision) {
|
537
|
+
*pRevision = DRMP3_VERSION_REVISION;
|
538
|
+
}
|
539
|
+
}
|
540
|
+
|
541
|
+
DRMP3_API const char* drmp3_version_string(void)
|
542
|
+
{
|
543
|
+
return DRMP3_VERSION_STRING;
|
544
|
+
}
|
317
545
|
|
318
|
-
|
546
|
+
/* Disable SIMD when compiling with TCC for now. */
|
319
547
|
#if defined(__TINYC__)
|
320
548
|
#define DR_MP3_NO_SIMD
|
321
549
|
#endif
|
@@ -362,12 +590,12 @@ void drmp3_free(void* p);
|
|
362
590
|
|
363
591
|
#if !defined(DR_MP3_NO_SIMD)
|
364
592
|
|
365
|
-
#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(
|
593
|
+
#if !defined(DR_MP3_ONLY_SIMD) && (defined(_M_X64) || defined(__x86_64__) || defined(__aarch64__) || defined(_M_ARM64))
|
366
594
|
/* x64 always have SSE2, arm64 always have neon, no need for generic code */
|
367
595
|
#define DR_MP3_ONLY_SIMD
|
368
596
|
#endif
|
369
597
|
|
370
|
-
#if (defined(_MSC_VER) && (defined(_M_IX86) || defined(_M_X64))) || ((defined(__i386__) || defined(__x86_64__)) && defined(__SSE2__))
|
598
|
+
#if ((defined(_MSC_VER) && _MSC_VER >= 1400) && (defined(_M_IX86) || defined(_M_X64))) || ((defined(__i386__) || defined(__x86_64__)) && defined(__SSE2__))
|
371
599
|
#if defined(_MSC_VER)
|
372
600
|
#include <intrin.h>
|
373
601
|
#endif
|
@@ -412,7 +640,7 @@ static __inline__ __attribute__((always_inline)) void drmp3_cpuid(int CPUInfo[],
|
|
412
640
|
#endif
|
413
641
|
}
|
414
642
|
#endif
|
415
|
-
static int drmp3_have_simd()
|
643
|
+
static int drmp3_have_simd(void)
|
416
644
|
{
|
417
645
|
#ifdef DR_MP3_ONLY_SIMD
|
418
646
|
return 1;
|
@@ -438,8 +666,9 @@ end:
|
|
438
666
|
return g_have_simd - 1;
|
439
667
|
#endif
|
440
668
|
}
|
441
|
-
#elif defined(__ARM_NEON) || defined(__aarch64__)
|
669
|
+
#elif defined(__ARM_NEON) || defined(__aarch64__) || defined(_M_ARM64)
|
442
670
|
#include <arm_neon.h>
|
671
|
+
#define DRMP3_HAVE_SSE 0
|
443
672
|
#define DRMP3_HAVE_SIMD 1
|
444
673
|
#define DRMP3_VSTORE vst1q_f32
|
445
674
|
#define DRMP3_VLD vld1q_f32
|
@@ -452,11 +681,12 @@ end:
|
|
452
681
|
#define DRMP3_VMUL_S(x, s) vmulq_f32(x, vmovq_n_f32(s))
|
453
682
|
#define DRMP3_VREV(x) vcombine_f32(vget_high_f32(vrev64q_f32(x)), vget_low_f32(vrev64q_f32(x)))
|
454
683
|
typedef float32x4_t drmp3_f4;
|
455
|
-
static int drmp3_have_simd()
|
684
|
+
static int drmp3_have_simd(void)
|
456
685
|
{ /* TODO: detect neon for !DR_MP3_ONLY_SIMD */
|
457
686
|
return 1;
|
458
687
|
}
|
459
688
|
#else
|
689
|
+
#define DRMP3_HAVE_SSE 0
|
460
690
|
#define DRMP3_HAVE_SIMD 0
|
461
691
|
#ifdef DR_MP3_ONLY_SIMD
|
462
692
|
#error DR_MP3_ONLY_SIMD used, but SSE/NEON not enabled
|
@@ -469,10 +699,23 @@ static int drmp3_have_simd()
|
|
469
699
|
|
470
700
|
#endif
|
471
701
|
|
472
|
-
|
702
|
+
#if defined(__ARM_ARCH) && (__ARM_ARCH >= 6) && !defined(__aarch64__) && !defined(_M_ARM64)
|
703
|
+
#define DRMP3_HAVE_ARMV6 1
|
704
|
+
static __inline__ __attribute__((always_inline)) drmp3_int32 drmp3_clip_int16_arm(drmp3_int32 a)
|
705
|
+
{
|
706
|
+
drmp3_int32 x = 0;
|
707
|
+
__asm__ ("ssat %0, #16, %1" : "=r"(x) : "r"(a));
|
708
|
+
return x;
|
709
|
+
}
|
710
|
+
#else
|
711
|
+
#define DRMP3_HAVE_ARMV6 0
|
712
|
+
#endif
|
713
|
+
|
714
|
+
|
715
|
+
/* Standard library stuff. */
|
473
716
|
#ifndef DRMP3_ASSERT
|
474
717
|
#include <assert.h>
|
475
|
-
#define DRMP3_ASSERT(expression)
|
718
|
+
#define DRMP3_ASSERT(expression) assert(expression)
|
476
719
|
#endif
|
477
720
|
#ifndef DRMP3_COPY_MEMORY
|
478
721
|
#define DRMP3_COPY_MEMORY(dst, src, sz) memcpy((dst), (src), (sz))
|
@@ -494,14 +737,6 @@ static int drmp3_have_simd()
|
|
494
737
|
#define DRMP3_FREE(p) free((p))
|
495
738
|
#endif
|
496
739
|
|
497
|
-
#define drmp3_assert DRMP3_ASSERT
|
498
|
-
#define drmp3_copy_memory DRMP3_COPY_MEMORY
|
499
|
-
#define drmp3_move_memory DRMP3_MOVE_MEMORY
|
500
|
-
#define drmp3_zero_memory DRMP3_ZERO_MEMORY
|
501
|
-
#define drmp3_zero_object DRMP3_ZERO_OBJECT
|
502
|
-
#define drmp3_malloc DRMP3_MALLOC
|
503
|
-
#define drmp3_realloc DRMP3_REALLOC
|
504
|
-
|
505
740
|
typedef struct
|
506
741
|
{
|
507
742
|
const drmp3_uint8 *buf;
|
@@ -675,7 +910,7 @@ static void drmp3_L12_read_scalefactors(drmp3_bs *bs, drmp3_uint8 *pba, drmp3_ui
|
|
675
910
|
if (mask & m)
|
676
911
|
{
|
677
912
|
int b = drmp3_bs_get_bits(bs, 6);
|
678
|
-
s = g_deq_L12[ba*3 - 6 + b % 3]*(1 << 21 >> b/3);
|
913
|
+
s = g_deq_L12[ba*3 - 6 + b % 3]*(int)(1 << 21 >> b/3);
|
679
914
|
}
|
680
915
|
*scf++ = s;
|
681
916
|
}
|
@@ -768,7 +1003,7 @@ static int drmp3_L12_dequantize_granule(float *grbuf, drmp3_bs *bs, drmp3_L12_sc
|
|
768
1003
|
static void drmp3_L12_apply_scf_384(drmp3_L12_scale_info *sci, const float *scf, float *dst)
|
769
1004
|
{
|
770
1005
|
int i, k;
|
771
|
-
|
1006
|
+
DRMP3_COPY_MEMORY(dst + 576 + sci->stereo_bands*18, dst + sci->stereo_bands*18, (sci->total_bands - sci->stereo_bands)*18*sizeof(float));
|
772
1007
|
for (i = 0; i < sci->total_bands; i++, dst += 18, scf += 6)
|
773
1008
|
{
|
774
1009
|
for (k = 0; k < 12; k++)
|
@@ -815,8 +1050,8 @@ static int drmp3_L3_read_side_info(drmp3_bs *bs, drmp3_L3_gr_info *gr, const drm
|
|
815
1050
|
|
816
1051
|
unsigned tables, scfsi = 0;
|
817
1052
|
int main_data_begin, part_23_sum = 0;
|
818
|
-
int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0);
|
819
1053
|
int gr_count = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2;
|
1054
|
+
int sr_idx = DRMP3_HDR_GET_MY_SAMPLE_RATE(hdr); sr_idx -= (sr_idx != 0);
|
820
1055
|
|
821
1056
|
if (DRMP3_HDR_TEST_MPEG1(hdr))
|
822
1057
|
{
|
@@ -913,14 +1148,14 @@ static void drmp3_L3_read_scalefactors(drmp3_uint8 *scf, drmp3_uint8 *ist_pos, c
|
|
913
1148
|
int cnt = scf_count[i];
|
914
1149
|
if (scfsi & 8)
|
915
1150
|
{
|
916
|
-
|
1151
|
+
DRMP3_COPY_MEMORY(scf, ist_pos, cnt);
|
917
1152
|
} else
|
918
1153
|
{
|
919
1154
|
int bits = scf_size[i];
|
920
1155
|
if (!bits)
|
921
1156
|
{
|
922
|
-
|
923
|
-
|
1157
|
+
DRMP3_ZERO_MEMORY(scf, cnt);
|
1158
|
+
DRMP3_ZERO_MEMORY(ist_pos, cnt);
|
924
1159
|
} else
|
925
1160
|
{
|
926
1161
|
int max_scf = (scfsi < 0) ? (1 << bits) - 1 : -1;
|
@@ -991,16 +1226,16 @@ static void drmp3_L3_decode_scalefactors(const drmp3_uint8 *hdr, drmp3_uint8 *is
|
|
991
1226
|
int sh = 3 - scf_shift;
|
992
1227
|
for (i = 0; i < gr->n_short_sfb; i += 3)
|
993
1228
|
{
|
994
|
-
iscf[gr->n_long_sfb + i + 0]
|
995
|
-
iscf[gr->n_long_sfb + i + 1]
|
996
|
-
iscf[gr->n_long_sfb + i + 2]
|
1229
|
+
iscf[gr->n_long_sfb + i + 0] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 0] + (gr->subblock_gain[0] << sh));
|
1230
|
+
iscf[gr->n_long_sfb + i + 1] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 1] + (gr->subblock_gain[1] << sh));
|
1231
|
+
iscf[gr->n_long_sfb + i + 2] = (drmp3_uint8)(iscf[gr->n_long_sfb + i + 2] + (gr->subblock_gain[2] << sh));
|
997
1232
|
}
|
998
1233
|
} else if (gr->preflag)
|
999
1234
|
{
|
1000
1235
|
static const drmp3_uint8 g_preamp[10] = { 1,1,1,1,2,2,3,3,3,2 };
|
1001
1236
|
for (i = 0; i < 10; i++)
|
1002
1237
|
{
|
1003
|
-
iscf[11 + i]
|
1238
|
+
iscf[11 + i] = (drmp3_uint8)(iscf[11 + i] + g_preamp[i]);
|
1004
1239
|
}
|
1005
1240
|
}
|
1006
1241
|
|
@@ -1080,41 +1315,72 @@ static void drmp3_L3_huffman(float *dst, drmp3_bs *bs, const drmp3_L3_gr_info *g
|
|
1080
1315
|
int sfb_cnt = gr_info->region_count[ireg++];
|
1081
1316
|
const drmp3_int16 *codebook = tabs + tabindex[tab_num];
|
1082
1317
|
int linbits = g_linbits[tab_num];
|
1083
|
-
|
1318
|
+
if (linbits)
|
1084
1319
|
{
|
1085
|
-
np = *sfb++ / 2;
|
1086
|
-
pairs_to_decode = DRMP3_MIN(big_val_cnt, np);
|
1087
|
-
one = *scf++;
|
1088
1320
|
do
|
1089
1321
|
{
|
1090
|
-
|
1091
|
-
|
1092
|
-
|
1322
|
+
np = *sfb++ / 2;
|
1323
|
+
pairs_to_decode = DRMP3_MIN(big_val_cnt, np);
|
1324
|
+
one = *scf++;
|
1325
|
+
do
|
1093
1326
|
{
|
1094
|
-
|
1095
|
-
|
1096
|
-
|
1097
|
-
|
1098
|
-
|
1327
|
+
int j, w = 5;
|
1328
|
+
int leaf = codebook[DRMP3_PEEK_BITS(w)];
|
1329
|
+
while (leaf < 0)
|
1330
|
+
{
|
1331
|
+
DRMP3_FLUSH_BITS(w);
|
1332
|
+
w = leaf & 7;
|
1333
|
+
leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)];
|
1334
|
+
}
|
1335
|
+
DRMP3_FLUSH_BITS(leaf >> 8);
|
1099
1336
|
|
1100
|
-
|
1337
|
+
for (j = 0; j < 2; j++, dst++, leaf >>= 4)
|
1338
|
+
{
|
1339
|
+
int lsb = leaf & 0x0F;
|
1340
|
+
if (lsb == 15)
|
1341
|
+
{
|
1342
|
+
lsb += DRMP3_PEEK_BITS(linbits);
|
1343
|
+
DRMP3_FLUSH_BITS(linbits);
|
1344
|
+
DRMP3_CHECK_BITS;
|
1345
|
+
*dst = one*drmp3_L3_pow_43(lsb)*((drmp3_int32)bs_cache < 0 ? -1: 1);
|
1346
|
+
} else
|
1347
|
+
{
|
1348
|
+
*dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
|
1349
|
+
}
|
1350
|
+
DRMP3_FLUSH_BITS(lsb ? 1 : 0);
|
1351
|
+
}
|
1352
|
+
DRMP3_CHECK_BITS;
|
1353
|
+
} while (--pairs_to_decode);
|
1354
|
+
} while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
|
1355
|
+
} else
|
1356
|
+
{
|
1357
|
+
do
|
1358
|
+
{
|
1359
|
+
np = *sfb++ / 2;
|
1360
|
+
pairs_to_decode = DRMP3_MIN(big_val_cnt, np);
|
1361
|
+
one = *scf++;
|
1362
|
+
do
|
1101
1363
|
{
|
1102
|
-
int
|
1103
|
-
|
1364
|
+
int j, w = 5;
|
1365
|
+
int leaf = codebook[DRMP3_PEEK_BITS(w)];
|
1366
|
+
while (leaf < 0)
|
1104
1367
|
{
|
1105
|
-
|
1106
|
-
|
1107
|
-
|
1108
|
-
|
1109
|
-
|
1368
|
+
DRMP3_FLUSH_BITS(w);
|
1369
|
+
w = leaf & 7;
|
1370
|
+
leaf = codebook[DRMP3_PEEK_BITS(w) - (leaf >> 3)];
|
1371
|
+
}
|
1372
|
+
DRMP3_FLUSH_BITS(leaf >> 8);
|
1373
|
+
|
1374
|
+
for (j = 0; j < 2; j++, dst++, leaf >>= 4)
|
1110
1375
|
{
|
1376
|
+
int lsb = leaf & 0x0F;
|
1111
1377
|
*dst = g_drmp3_pow43[16 + lsb - 16*(bs_cache >> 31)]*one;
|
1378
|
+
DRMP3_FLUSH_BITS(lsb ? 1 : 0);
|
1112
1379
|
}
|
1113
|
-
|
1114
|
-
}
|
1115
|
-
|
1116
|
-
|
1117
|
-
} while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
|
1380
|
+
DRMP3_CHECK_BITS;
|
1381
|
+
} while (--pairs_to_decode);
|
1382
|
+
} while ((big_val_cnt -= np) > 0 && --sfb_cnt >= 0);
|
1383
|
+
}
|
1118
1384
|
}
|
1119
1385
|
|
1120
1386
|
for (np = 1 - big_val_cnt;; dst += 4)
|
@@ -1149,12 +1415,22 @@ static void drmp3_L3_midside_stereo(float *left, int n)
|
|
1149
1415
|
int i = 0;
|
1150
1416
|
float *right = left + 576;
|
1151
1417
|
#if DRMP3_HAVE_SIMD
|
1152
|
-
if (drmp3_have_simd())
|
1418
|
+
if (drmp3_have_simd())
|
1153
1419
|
{
|
1154
|
-
|
1155
|
-
|
1156
|
-
|
1157
|
-
|
1420
|
+
for (; i < n - 3; i += 4)
|
1421
|
+
{
|
1422
|
+
drmp3_f4 vl = DRMP3_VLD(left + i);
|
1423
|
+
drmp3_f4 vr = DRMP3_VLD(right + i);
|
1424
|
+
DRMP3_VSTORE(left + i, DRMP3_VADD(vl, vr));
|
1425
|
+
DRMP3_VSTORE(right + i, DRMP3_VSUB(vl, vr));
|
1426
|
+
}
|
1427
|
+
#ifdef __GNUC__
|
1428
|
+
/* Workaround for spurious -Waggressive-loop-optimizations warning from gcc.
|
1429
|
+
* For more info see: https://github.com/lieff/minimp3/issues/88
|
1430
|
+
*/
|
1431
|
+
if (__builtin_constant_p(n % 4 == 0) && n % 4 == 0)
|
1432
|
+
return;
|
1433
|
+
#endif
|
1158
1434
|
}
|
1159
1435
|
#endif
|
1160
1436
|
for (; i < n; i++)
|
@@ -1264,7 +1540,7 @@ static void drmp3_L3_reorder(float *grbuf, float *scratch, const drmp3_uint8 *sf
|
|
1264
1540
|
*dst++ = src[2*len];
|
1265
1541
|
}
|
1266
1542
|
}
|
1267
|
-
|
1543
|
+
DRMP3_COPY_MEMORY(grbuf, scratch, (dst - scratch)*sizeof(float));
|
1268
1544
|
}
|
1269
1545
|
|
1270
1546
|
static void drmp3_L3_antialias(float *grbuf, int nbands)
|
@@ -1433,8 +1709,8 @@ static void drmp3_L3_imdct_short(float *grbuf, float *overlap, int nbands)
|
|
1433
1709
|
for (;nbands > 0; nbands--, overlap += 9, grbuf += 18)
|
1434
1710
|
{
|
1435
1711
|
float tmp[18];
|
1436
|
-
|
1437
|
-
|
1712
|
+
DRMP3_COPY_MEMORY(tmp, grbuf, sizeof(tmp));
|
1713
|
+
DRMP3_COPY_MEMORY(grbuf, overlap, 6*sizeof(float));
|
1438
1714
|
drmp3_L3_imdct12(tmp, grbuf + 6, overlap + 6);
|
1439
1715
|
drmp3_L3_imdct12(tmp + 1, grbuf + 12, overlap + 6);
|
1440
1716
|
drmp3_L3_imdct12(tmp + 2, overlap, overlap + 6);
|
@@ -1478,7 +1754,7 @@ static void drmp3_L3_save_reservoir(drmp3dec *h, drmp3dec_scratch *s)
|
|
1478
1754
|
}
|
1479
1755
|
if (remains > 0)
|
1480
1756
|
{
|
1481
|
-
|
1757
|
+
DRMP3_MOVE_MEMORY(h->reserv_buf, s->maindata + pos, remains);
|
1482
1758
|
}
|
1483
1759
|
h->reserv = remains;
|
1484
1760
|
}
|
@@ -1487,8 +1763,8 @@ static int drmp3_L3_restore_reservoir(drmp3dec *h, drmp3_bs *bs, drmp3dec_scratc
|
|
1487
1763
|
{
|
1488
1764
|
int frame_bytes = (bs->limit - bs->pos)/8;
|
1489
1765
|
int bytes_have = DRMP3_MIN(h->reserv, main_data_begin);
|
1490
|
-
|
1491
|
-
|
1766
|
+
DRMP3_COPY_MEMORY(s->maindata, h->reserv_buf + DRMP3_MAX(0, h->reserv - main_data_begin), DRMP3_MIN(h->reserv, main_data_begin));
|
1767
|
+
DRMP3_COPY_MEMORY(s->maindata + bytes_have, bs->buf + bs->pos/8, frame_bytes);
|
1492
1768
|
drmp3_bs_init(&s->bs, s->maindata, bytes_have + frame_bytes);
|
1493
1769
|
return h->reserv >= main_data_begin;
|
1494
1770
|
}
|
@@ -1621,7 +1897,7 @@ static void drmp3d_DCT_II(float *grbuf, int n)
|
|
1621
1897
|
} else
|
1622
1898
|
#endif
|
1623
1899
|
#ifdef DR_MP3_ONLY_SIMD
|
1624
|
-
{}
|
1900
|
+
{} /* for HAVE_SIMD=1, MINIMP3_ONLY_SIMD=1 case we do not need non-intrinsic "else" branch */
|
1625
1901
|
#else
|
1626
1902
|
for (; k < n; k++)
|
1627
1903
|
{
|
@@ -1689,11 +1965,18 @@ typedef drmp3_int16 drmp3d_sample_t;
|
|
1689
1965
|
|
1690
1966
|
static drmp3_int16 drmp3d_scale_pcm(float sample)
|
1691
1967
|
{
|
1968
|
+
drmp3_int16 s;
|
1969
|
+
#if DRMP3_HAVE_ARMV6
|
1970
|
+
drmp3_int32 s32 = (drmp3_int32)(sample + .5f);
|
1971
|
+
s32 -= (s32 < 0);
|
1972
|
+
s = (drmp3_int16)drmp3_clip_int16_arm(s32);
|
1973
|
+
#else
|
1692
1974
|
if (sample >= 32766.5) return (drmp3_int16) 32767;
|
1693
1975
|
if (sample <= -32767.5) return (drmp3_int16)-32768;
|
1694
|
-
|
1976
|
+
s = (drmp3_int16)(sample + .5f);
|
1695
1977
|
s -= (s < 0); /* away from zero, to be compliant */
|
1696
|
-
|
1978
|
+
#endif
|
1979
|
+
return s;
|
1697
1980
|
}
|
1698
1981
|
#else
|
1699
1982
|
typedef float drmp3d_sample_t;
|
@@ -1847,7 +2130,7 @@ static void drmp3d_synth(float *xl, drmp3d_sample_t *dstl, int nch, float *lins)
|
|
1847
2130
|
} else
|
1848
2131
|
#endif
|
1849
2132
|
#ifdef DR_MP3_ONLY_SIMD
|
1850
|
-
{}
|
2133
|
+
{} /* for HAVE_SIMD=1, MINIMP3_ONLY_SIMD=1 case we do not need non-intrinsic "else" branch */
|
1851
2134
|
#else
|
1852
2135
|
for (i = 14; i >= 0; i--)
|
1853
2136
|
{
|
@@ -1888,7 +2171,7 @@ static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int
|
|
1888
2171
|
drmp3d_DCT_II(grbuf + 576*i, nbands);
|
1889
2172
|
}
|
1890
2173
|
|
1891
|
-
|
2174
|
+
DRMP3_COPY_MEMORY(lins, qmf_state, sizeof(float)*15*64);
|
1892
2175
|
|
1893
2176
|
for (i = 0; i < nbands; i += 2)
|
1894
2177
|
{
|
@@ -1904,7 +2187,7 @@ static void drmp3d_synth_granule(float *qmf_state, float *grbuf, int nbands, int
|
|
1904
2187
|
} else
|
1905
2188
|
#endif
|
1906
2189
|
{
|
1907
|
-
|
2190
|
+
DRMP3_COPY_MEMORY(qmf_state, lins + nbands*64, sizeof(float)*15*64);
|
1908
2191
|
}
|
1909
2192
|
}
|
1910
2193
|
|
@@ -1957,15 +2240,15 @@ static int drmp3d_find_frame(const drmp3_uint8 *mp3, int mp3_bytes, int *free_fo
|
|
1957
2240
|
}
|
1958
2241
|
}
|
1959
2242
|
*ptr_frame_bytes = 0;
|
1960
|
-
return
|
2243
|
+
return mp3_bytes;
|
1961
2244
|
}
|
1962
2245
|
|
1963
|
-
void drmp3dec_init(drmp3dec *dec)
|
2246
|
+
DRMP3_API void drmp3dec_init(drmp3dec *dec)
|
1964
2247
|
{
|
1965
2248
|
dec->header[0] = 0;
|
1966
2249
|
}
|
1967
2250
|
|
1968
|
-
int drmp3dec_decode_frame(drmp3dec *dec, const
|
2251
|
+
DRMP3_API int drmp3dec_decode_frame(drmp3dec *dec, const drmp3_uint8 *mp3, int mp3_bytes, void *pcm, drmp3dec_frame_info *info)
|
1969
2252
|
{
|
1970
2253
|
int i = 0, igr, frame_size = 0, success = 1;
|
1971
2254
|
const drmp3_uint8 *hdr;
|
@@ -1982,7 +2265,7 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes
|
|
1982
2265
|
}
|
1983
2266
|
if (!frame_size)
|
1984
2267
|
{
|
1985
|
-
|
2268
|
+
DRMP3_ZERO_MEMORY(dec, sizeof(drmp3dec));
|
1986
2269
|
i = drmp3d_find_frame(mp3, mp3_bytes, &dec->free_format_bytes, &frame_size);
|
1987
2270
|
if (!frame_size || i + frame_size > mp3_bytes)
|
1988
2271
|
{
|
@@ -1992,18 +2275,13 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes
|
|
1992
2275
|
}
|
1993
2276
|
|
1994
2277
|
hdr = mp3 + i;
|
1995
|
-
|
2278
|
+
DRMP3_COPY_MEMORY(dec->header, hdr, DRMP3_HDR_SIZE);
|
1996
2279
|
info->frame_bytes = i + frame_size;
|
1997
2280
|
info->channels = DRMP3_HDR_IS_MONO(hdr) ? 1 : 2;
|
1998
2281
|
info->hz = drmp3_hdr_sample_rate_hz(hdr);
|
1999
2282
|
info->layer = 4 - DRMP3_HDR_GET_LAYER(hdr);
|
2000
2283
|
info->bitrate_kbps = drmp3_hdr_bitrate_kbps(hdr);
|
2001
2284
|
|
2002
|
-
if (!pcm)
|
2003
|
-
{
|
2004
|
-
return drmp3_hdr_frame_samples(hdr);
|
2005
|
-
}
|
2006
|
-
|
2007
2285
|
drmp3_bs_init(bs_frame, hdr + DRMP3_HDR_SIZE, frame_size - DRMP3_HDR_SIZE);
|
2008
2286
|
if (DRMP3_HDR_IS_CRC(hdr))
|
2009
2287
|
{
|
@@ -2019,11 +2297,11 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes
|
|
2019
2297
|
return 0;
|
2020
2298
|
}
|
2021
2299
|
success = drmp3_L3_restore_reservoir(dec, bs_frame, &scratch, main_data_begin);
|
2022
|
-
if (success)
|
2300
|
+
if (success && pcm != NULL)
|
2023
2301
|
{
|
2024
2302
|
for (igr = 0; igr < (DRMP3_HDR_TEST_MPEG1(hdr) ? 2 : 1); igr++, pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*576*info->channels))
|
2025
2303
|
{
|
2026
|
-
|
2304
|
+
DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));
|
2027
2305
|
drmp3_L3_decode(dec, &scratch, scratch.gr_info + igr*info->channels, info->channels);
|
2028
2306
|
drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 18, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]);
|
2029
2307
|
}
|
@@ -2035,9 +2313,14 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes
|
|
2035
2313
|
return 0;
|
2036
2314
|
#else
|
2037
2315
|
drmp3_L12_scale_info sci[1];
|
2316
|
+
|
2317
|
+
if (pcm == NULL) {
|
2318
|
+
return drmp3_hdr_frame_samples(hdr);
|
2319
|
+
}
|
2320
|
+
|
2038
2321
|
drmp3_L12_read_scale_info(hdr, bs_frame, sci);
|
2039
2322
|
|
2040
|
-
|
2323
|
+
DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));
|
2041
2324
|
for (i = 0, igr = 0; igr < 3; igr++)
|
2042
2325
|
{
|
2043
2326
|
if (12 == (i += drmp3_L12_dequantize_granule(scratch.grbuf[0] + i, bs_frame, sci, info->layer | 1)))
|
@@ -2045,7 +2328,7 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes
|
|
2045
2328
|
i = 0;
|
2046
2329
|
drmp3_L12_apply_scf_384(sci, sci->scf + igr, scratch.grbuf[0]);
|
2047
2330
|
drmp3d_synth_granule(dec->qmf_state, scratch.grbuf[0], 12, info->channels, (drmp3d_sample_t*)pcm, scratch.syn[0]);
|
2048
|
-
|
2331
|
+
DRMP3_ZERO_MEMORY(scratch.grbuf[0], 576*2*sizeof(float));
|
2049
2332
|
pcm = DRMP3_OFFSET_PTR(pcm, sizeof(drmp3d_sample_t)*384*info->channels);
|
2050
2333
|
}
|
2051
2334
|
if (bs_frame->pos > bs_frame->limit)
|
@@ -2056,74 +2339,74 @@ int drmp3dec_decode_frame(drmp3dec *dec, const unsigned char *mp3, int mp3_bytes
|
|
2056
2339
|
}
|
2057
2340
|
#endif
|
2058
2341
|
}
|
2342
|
+
|
2059
2343
|
return success*drmp3_hdr_frame_samples(dec->header);
|
2060
2344
|
}
|
2061
2345
|
|
2062
|
-
void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out,
|
2346
|
+
DRMP3_API void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, size_t num_samples)
|
2063
2347
|
{
|
2064
|
-
|
2065
|
-
{
|
2066
|
-
int i = 0;
|
2348
|
+
size_t i = 0;
|
2067
2349
|
#if DRMP3_HAVE_SIMD
|
2068
|
-
|
2069
|
-
|
2070
|
-
|
2071
|
-
|
2072
|
-
|
2073
|
-
|
2350
|
+
size_t aligned_count = num_samples & ~7;
|
2351
|
+
for(; i < aligned_count; i+=8)
|
2352
|
+
{
|
2353
|
+
drmp3_f4 scale = DRMP3_VSET(32768.0f);
|
2354
|
+
drmp3_f4 a = DRMP3_VMUL(DRMP3_VLD(&in[i ]), scale);
|
2355
|
+
drmp3_f4 b = DRMP3_VMUL(DRMP3_VLD(&in[i+4]), scale);
|
2074
2356
|
#if DRMP3_HAVE_SSE
|
2075
|
-
|
2076
|
-
|
2077
|
-
|
2078
|
-
|
2079
|
-
|
2080
|
-
|
2081
|
-
|
2082
|
-
|
2083
|
-
|
2084
|
-
|
2085
|
-
|
2086
|
-
|
2357
|
+
drmp3_f4 s16max = DRMP3_VSET( 32767.0f);
|
2358
|
+
drmp3_f4 s16min = DRMP3_VSET(-32768.0f);
|
2359
|
+
__m128i pcm8 = _mm_packs_epi32(_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(a, s16max), s16min)),
|
2360
|
+
_mm_cvtps_epi32(_mm_max_ps(_mm_min_ps(b, s16max), s16min)));
|
2361
|
+
out[i ] = (drmp3_int16)_mm_extract_epi16(pcm8, 0);
|
2362
|
+
out[i+1] = (drmp3_int16)_mm_extract_epi16(pcm8, 1);
|
2363
|
+
out[i+2] = (drmp3_int16)_mm_extract_epi16(pcm8, 2);
|
2364
|
+
out[i+3] = (drmp3_int16)_mm_extract_epi16(pcm8, 3);
|
2365
|
+
out[i+4] = (drmp3_int16)_mm_extract_epi16(pcm8, 4);
|
2366
|
+
out[i+5] = (drmp3_int16)_mm_extract_epi16(pcm8, 5);
|
2367
|
+
out[i+6] = (drmp3_int16)_mm_extract_epi16(pcm8, 6);
|
2368
|
+
out[i+7] = (drmp3_int16)_mm_extract_epi16(pcm8, 7);
|
2087
2369
|
#else
|
2088
|
-
|
2089
|
-
|
2090
|
-
|
2091
|
-
|
2092
|
-
|
2093
|
-
|
2094
|
-
|
2095
|
-
|
2096
|
-
|
2097
|
-
|
2098
|
-
|
2099
|
-
|
2100
|
-
|
2370
|
+
int16x4_t pcma, pcmb;
|
2371
|
+
a = DRMP3_VADD(a, DRMP3_VSET(0.5f));
|
2372
|
+
b = DRMP3_VADD(b, DRMP3_VSET(0.5f));
|
2373
|
+
pcma = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(a), vreinterpretq_s32_u32(vcltq_f32(a, DRMP3_VSET(0)))));
|
2374
|
+
pcmb = vqmovn_s32(vqaddq_s32(vcvtq_s32_f32(b), vreinterpretq_s32_u32(vcltq_f32(b, DRMP3_VSET(0)))));
|
2375
|
+
vst1_lane_s16(out+i , pcma, 0);
|
2376
|
+
vst1_lane_s16(out+i+1, pcma, 1);
|
2377
|
+
vst1_lane_s16(out+i+2, pcma, 2);
|
2378
|
+
vst1_lane_s16(out+i+3, pcma, 3);
|
2379
|
+
vst1_lane_s16(out+i+4, pcmb, 0);
|
2380
|
+
vst1_lane_s16(out+i+5, pcmb, 1);
|
2381
|
+
vst1_lane_s16(out+i+6, pcmb, 2);
|
2382
|
+
vst1_lane_s16(out+i+7, pcmb, 3);
|
2101
2383
|
#endif
|
2102
|
-
|
2384
|
+
}
|
2103
2385
|
#endif
|
2104
|
-
|
2386
|
+
for(; i < num_samples; i++)
|
2387
|
+
{
|
2388
|
+
float sample = in[i] * 32768.0f;
|
2389
|
+
if (sample >= 32766.5)
|
2390
|
+
out[i] = (drmp3_int16) 32767;
|
2391
|
+
else if (sample <= -32767.5)
|
2392
|
+
out[i] = (drmp3_int16)-32768;
|
2393
|
+
else
|
2105
2394
|
{
|
2106
|
-
|
2107
|
-
|
2108
|
-
|
2109
|
-
else if (sample <= -32767.5)
|
2110
|
-
out[i] = (drmp3_int16)-32768;
|
2111
|
-
else
|
2112
|
-
{
|
2113
|
-
short s = (drmp3_int16)(sample + .5f);
|
2114
|
-
s -= (s < 0); /* away from zero, to be compliant */
|
2115
|
-
out[i] = s;
|
2116
|
-
}
|
2395
|
+
short s = (drmp3_int16)(sample + .5f);
|
2396
|
+
s -= (s < 0); /* away from zero, to be compliant */
|
2397
|
+
out[i] = s;
|
2117
2398
|
}
|
2118
2399
|
}
|
2119
2400
|
}
|
2120
2401
|
|
2121
2402
|
|
2122
|
-
|
2123
|
-
|
2124
|
-
|
2125
|
-
|
2126
|
-
|
2403
|
+
|
2404
|
+
/************************************************************************************************************************************************************
|
2405
|
+
|
2406
|
+
Main Public API
|
2407
|
+
|
2408
|
+
************************************************************************************************************************************************************/
|
2409
|
+
#include <math.h> /* For sin() and exp(). */
|
2127
2410
|
|
2128
2411
|
#if defined(SIZE_MAX)
|
2129
2412
|
#define DRMP3_SIZE_MAX SIZE_MAX
|
@@ -2135,283 +2418,275 @@ void drmp3dec_f32_to_s16(const float *in, drmp3_int16 *out, int num_samples)
|
|
2135
2418
|
#endif
|
2136
2419
|
#endif
|
2137
2420
|
|
2138
|
-
|
2139
|
-
#ifndef
|
2140
|
-
#define
|
2421
|
+
/* Options. */
|
2422
|
+
#ifndef DRMP3_SEEK_LEADING_MP3_FRAMES
|
2423
|
+
#define DRMP3_SEEK_LEADING_MP3_FRAMES 2
|
2141
2424
|
#endif
|
2142
|
-
|
2143
|
-
#define
|
2425
|
+
|
2426
|
+
#define DRMP3_MIN_DATA_CHUNK_SIZE 16384
|
2427
|
+
|
2428
|
+
/* The size in bytes of each chunk of data to read from the MP3 stream. minimp3 recommends at least 16K, but in an attempt to reduce data movement I'm making this slightly larger. */
|
2429
|
+
#ifndef DRMP3_DATA_CHUNK_SIZE
|
2430
|
+
#define DRMP3_DATA_CHUNK_SIZE DRMP3_MIN_DATA_CHUNK_SIZE*4
|
2144
2431
|
#endif
|
2145
2432
|
|
2146
|
-
#define drmp3_countof(x) (sizeof(x) / sizeof(x[0]))
|
2147
|
-
#define drmp3_max(x, y) (((x) > (y)) ? (x) : (y))
|
2148
|
-
#define drmp3_min(x, y) (((x) < (y)) ? (x) : (y))
|
2149
2433
|
|
2150
|
-
#define
|
2434
|
+
#define DRMP3_COUNTOF(x) (sizeof(x) / sizeof(x[0]))
|
2435
|
+
#define DRMP3_CLAMP(x, lo, hi) (DRMP3_MAX(lo, DRMP3_MIN(x, hi)))
|
2436
|
+
|
2437
|
+
#ifndef DRMP3_PI_D
|
2438
|
+
#define DRMP3_PI_D 3.14159265358979323846264
|
2439
|
+
#endif
|
2440
|
+
|
2441
|
+
#define DRMP3_DEFAULT_RESAMPLER_LPF_ORDER 2
|
2151
2442
|
|
2152
|
-
static
|
2443
|
+
static DRMP3_INLINE float drmp3_mix_f32(float x, float y, float a)
|
2153
2444
|
{
|
2154
2445
|
return x*(1-a) + y*a;
|
2155
2446
|
}
|
2447
|
+
static DRMP3_INLINE float drmp3_mix_f32_fast(float x, float y, float a)
|
2448
|
+
{
|
2449
|
+
float r0 = (y - x);
|
2450
|
+
float r1 = r0*a;
|
2451
|
+
return x + r1;
|
2452
|
+
/*return x + (y - x)*a;*/
|
2453
|
+
}
|
2156
2454
|
|
2157
|
-
|
2455
|
+
|
2456
|
+
/*
|
2457
|
+
Greatest common factor using Euclid's algorithm iteratively.
|
2458
|
+
*/
|
2459
|
+
static DRMP3_INLINE drmp3_uint32 drmp3_gcf_u32(drmp3_uint32 a, drmp3_uint32 b)
|
2158
2460
|
{
|
2159
|
-
for (
|
2160
|
-
|
2461
|
+
for (;;) {
|
2462
|
+
if (b == 0) {
|
2463
|
+
break;
|
2464
|
+
} else {
|
2465
|
+
drmp3_uint32 t = a;
|
2466
|
+
a = b;
|
2467
|
+
b = t % a;
|
2468
|
+
}
|
2161
2469
|
}
|
2470
|
+
|
2471
|
+
return a;
|
2162
2472
|
}
|
2163
2473
|
|
2164
|
-
void drmp3_src_cache_init(drmp3_src* pSRC, drmp3_src_cache* pCache)
|
2165
|
-
{
|
2166
|
-
drmp3_assert(pSRC != NULL);
|
2167
|
-
drmp3_assert(pCache != NULL);
|
2168
2474
|
|
2169
|
-
|
2170
|
-
|
2171
|
-
|
2475
|
+
static DRMP3_INLINE double drmp3_sin(double x)
|
2476
|
+
{
|
2477
|
+
/* TODO: Implement custom sin(x). */
|
2478
|
+
return sin(x);
|
2172
2479
|
}
|
2173
2480
|
|
2174
|
-
|
2481
|
+
static DRMP3_INLINE double drmp3_exp(double x)
|
2175
2482
|
{
|
2176
|
-
|
2177
|
-
|
2178
|
-
|
2179
|
-
drmp3_assert(frameCount > 0);
|
2180
|
-
drmp3_assert(pFramesOut != NULL);
|
2483
|
+
/* TODO: Implement custom exp(x). */
|
2484
|
+
return exp(x);
|
2485
|
+
}
|
2181
2486
|
|
2182
|
-
|
2487
|
+
static DRMP3_INLINE double drmp3_cos(double x)
|
2488
|
+
{
|
2489
|
+
return drmp3_sin((DRMP3_PI_D*0.5) - x);
|
2490
|
+
}
|
2183
2491
|
|
2184
|
-
drmp3_uint64 totalFramesRead = 0;
|
2185
|
-
while (frameCount > 0) {
|
2186
|
-
// If there's anything in memory go ahead and copy that over first.
|
2187
|
-
drmp3_uint64 framesRemainingInMemory = pCache->cachedFrameCount - pCache->iNextFrame;
|
2188
|
-
drmp3_uint64 framesToReadFromMemory = frameCount;
|
2189
|
-
if (framesToReadFromMemory > framesRemainingInMemory) {
|
2190
|
-
framesToReadFromMemory = framesRemainingInMemory;
|
2191
|
-
}
|
2192
2492
|
|
2193
|
-
|
2194
|
-
|
2493
|
+
static void* drmp3__malloc_default(size_t sz, void* pUserData)
|
2494
|
+
{
|
2495
|
+
(void)pUserData;
|
2496
|
+
return DRMP3_MALLOC(sz);
|
2497
|
+
}
|
2195
2498
|
|
2196
|
-
|
2197
|
-
|
2198
|
-
|
2199
|
-
|
2200
|
-
|
2499
|
+
static void* drmp3__realloc_default(void* p, size_t sz, void* pUserData)
|
2500
|
+
{
|
2501
|
+
(void)pUserData;
|
2502
|
+
return DRMP3_REALLOC(p, sz);
|
2503
|
+
}
|
2201
2504
|
|
2202
|
-
|
2203
|
-
|
2204
|
-
|
2505
|
+
static void drmp3__free_default(void* p, void* pUserData)
|
2506
|
+
{
|
2507
|
+
(void)pUserData;
|
2508
|
+
DRMP3_FREE(p);
|
2509
|
+
}
|
2205
2510
|
|
2206
|
-
pCache->iNextFrame = 0;
|
2207
|
-
pCache->cachedFrameCount = 0;
|
2208
2511
|
|
2209
|
-
|
2210
|
-
|
2211
|
-
|
2212
|
-
|
2512
|
+
static void* drmp3__malloc_from_callbacks(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks)
|
2513
|
+
{
|
2514
|
+
if (pAllocationCallbacks == NULL) {
|
2515
|
+
return NULL;
|
2516
|
+
}
|
2213
2517
|
|
2214
|
-
|
2518
|
+
if (pAllocationCallbacks->onMalloc != NULL) {
|
2519
|
+
return pAllocationCallbacks->onMalloc(sz, pAllocationCallbacks->pUserData);
|
2520
|
+
}
|
2215
2521
|
|
2216
|
-
|
2217
|
-
|
2218
|
-
|
2219
|
-
}
|
2522
|
+
/* Try using realloc(). */
|
2523
|
+
if (pAllocationCallbacks->onRealloc != NULL) {
|
2524
|
+
return pAllocationCallbacks->onRealloc(NULL, sz, pAllocationCallbacks->pUserData);
|
2220
2525
|
}
|
2221
2526
|
|
2222
|
-
return
|
2527
|
+
return NULL;
|
2223
2528
|
}
|
2224
2529
|
|
2225
|
-
|
2226
|
-
drmp3_uint64 drmp3_src_read_frames_passthrough(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush);
|
2227
|
-
drmp3_uint64 drmp3_src_read_frames_linear(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush);
|
2228
|
-
|
2229
|
-
drmp3_bool32 drmp3_src_init(const drmp3_src_config* pConfig, drmp3_src_read_proc onRead, void* pUserData, drmp3_src* pSRC)
|
2530
|
+
static void* drmp3__realloc_from_callbacks(void* p, size_t szNew, size_t szOld, const drmp3_allocation_callbacks* pAllocationCallbacks)
|
2230
2531
|
{
|
2231
|
-
if (
|
2232
|
-
|
2233
|
-
|
2234
|
-
if (pConfig == NULL || onRead == NULL) return DRMP3_FALSE;
|
2235
|
-
if (pConfig->channels == 0 || pConfig->channels > 2) return DRMP3_FALSE;
|
2236
|
-
|
2237
|
-
drmp3_copy_memory(&pSRC->config, pConfig, sizeof (drmp3_src_config));
|
2238
|
-
pSRC->onRead = onRead;
|
2239
|
-
pSRC->pUserData = pUserData;
|
2240
|
-
|
2241
|
-
if (pSRC->config.cacheSizeInFrames > DRMP3_SRC_CACHE_SIZE_IN_FRAMES || pSRC->config.cacheSizeInFrames == 0) {
|
2242
|
-
pSRC->config.cacheSizeInFrames = DRMP3_SRC_CACHE_SIZE_IN_FRAMES;
|
2532
|
+
if (pAllocationCallbacks == NULL) {
|
2533
|
+
return NULL;
|
2243
2534
|
}
|
2244
2535
|
|
2245
|
-
|
2246
|
-
|
2247
|
-
}
|
2248
|
-
|
2249
|
-
drmp3_bool32 drmp3_src_set_input_sample_rate(drmp3_src* pSRC, drmp3_uint32 sampleRateIn)
|
2250
|
-
{
|
2251
|
-
if (pSRC == NULL) return DRMP3_FALSE;
|
2252
|
-
|
2253
|
-
// Must have a sample rate of > 0.
|
2254
|
-
if (sampleRateIn == 0) {
|
2255
|
-
return DRMP3_FALSE;
|
2536
|
+
if (pAllocationCallbacks->onRealloc != NULL) {
|
2537
|
+
return pAllocationCallbacks->onRealloc(p, szNew, pAllocationCallbacks->pUserData);
|
2256
2538
|
}
|
2257
2539
|
|
2258
|
-
|
2259
|
-
|
2260
|
-
|
2540
|
+
/* Try emulating realloc() in terms of malloc()/free(). */
|
2541
|
+
if (pAllocationCallbacks->onMalloc != NULL && pAllocationCallbacks->onFree != NULL) {
|
2542
|
+
void* p2;
|
2261
2543
|
|
2262
|
-
|
2263
|
-
{
|
2264
|
-
|
2544
|
+
p2 = pAllocationCallbacks->onMalloc(szNew, pAllocationCallbacks->pUserData);
|
2545
|
+
if (p2 == NULL) {
|
2546
|
+
return NULL;
|
2547
|
+
}
|
2265
2548
|
|
2266
|
-
|
2267
|
-
|
2268
|
-
|
2549
|
+
if (p != NULL) {
|
2550
|
+
DRMP3_COPY_MEMORY(p2, p, szOld);
|
2551
|
+
pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
|
2552
|
+
}
|
2553
|
+
|
2554
|
+
return p2;
|
2269
2555
|
}
|
2270
2556
|
|
2271
|
-
|
2272
|
-
return DRMP3_TRUE;
|
2557
|
+
return NULL;
|
2273
2558
|
}
|
2274
2559
|
|
2275
|
-
|
2560
|
+
static void drmp3__free_from_callbacks(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks)
|
2276
2561
|
{
|
2277
|
-
if (
|
2278
|
-
|
2279
|
-
drmp3_src_algorithm algorithm = pSRC->config.algorithm;
|
2280
|
-
|
2281
|
-
// Always use passthrough if the sample rates are the same.
|
2282
|
-
if (pSRC->config.sampleRateIn == pSRC->config.sampleRateOut) {
|
2283
|
-
algorithm = drmp3_src_algorithm_none;
|
2562
|
+
if (p == NULL || pAllocationCallbacks == NULL) {
|
2563
|
+
return;
|
2284
2564
|
}
|
2285
2565
|
|
2286
|
-
|
2287
|
-
|
2288
|
-
{
|
2289
|
-
case drmp3_src_algorithm_none: return drmp3_src_read_frames_passthrough(pSRC, frameCount, pFramesOut, flush);
|
2290
|
-
case drmp3_src_algorithm_linear: return drmp3_src_read_frames_linear(pSRC, frameCount, pFramesOut, flush);
|
2291
|
-
default: return 0;
|
2566
|
+
if (pAllocationCallbacks->onFree != NULL) {
|
2567
|
+
pAllocationCallbacks->onFree(p, pAllocationCallbacks->pUserData);
|
2292
2568
|
}
|
2293
2569
|
}
|
2294
2570
|
|
2295
|
-
|
2571
|
+
|
2572
|
+
static drmp3_allocation_callbacks drmp3_copy_allocation_callbacks_or_defaults(const drmp3_allocation_callbacks* pAllocationCallbacks)
|
2296
2573
|
{
|
2297
|
-
|
2574
|
+
if (pAllocationCallbacks != NULL) {
|
2575
|
+
/* Copy. */
|
2576
|
+
return *pAllocationCallbacks;
|
2577
|
+
} else {
|
2578
|
+
/* Defaults. */
|
2579
|
+
drmp3_allocation_callbacks allocationCallbacks;
|
2580
|
+
allocationCallbacks.pUserData = NULL;
|
2581
|
+
allocationCallbacks.onMalloc = drmp3__malloc_default;
|
2582
|
+
allocationCallbacks.onRealloc = drmp3__realloc_default;
|
2583
|
+
allocationCallbacks.onFree = drmp3__free_default;
|
2584
|
+
return allocationCallbacks;
|
2585
|
+
}
|
2298
2586
|
}
|
2299
2587
|
|
2300
|
-
drmp3_uint64 drmp3_src_read_frames_passthrough(drmp3_src* pSRC, drmp3_uint64 frameCount, void* pFramesOut, drmp3_bool32 flush)
|
2301
|
-
{
|
2302
|
-
drmp3_assert(pSRC != NULL);
|
2303
|
-
drmp3_assert(frameCount > 0);
|
2304
|
-
drmp3_assert(pFramesOut != NULL);
|
2305
2588
|
|
2306
|
-
(void)flush; // Passthrough need not care about flushing.
|
2307
|
-
return pSRC->onRead(pSRC, frameCount, pFramesOut, pSRC->pUserData);
|
2308
|
-
}
|
2309
2589
|
|
2310
|
-
|
2590
|
+
static size_t drmp3__on_read(drmp3* pMP3, void* pBufferOut, size_t bytesToRead)
|
2311
2591
|
{
|
2312
|
-
|
2313
|
-
|
2314
|
-
|
2592
|
+
size_t bytesRead = pMP3->onRead(pMP3->pUserData, pBufferOut, bytesToRead);
|
2593
|
+
pMP3->streamCursor += bytesRead;
|
2594
|
+
return bytesRead;
|
2595
|
+
}
|
2315
2596
|
|
2316
|
-
|
2597
|
+
static drmp3_bool32 drmp3__on_seek(drmp3* pMP3, int offset, drmp3_seek_origin origin)
|
2598
|
+
{
|
2599
|
+
DRMP3_ASSERT(offset >= 0);
|
2317
2600
|
|
2318
|
-
|
2319
|
-
|
2320
|
-
drmp3_uint64 framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pSRC->bin);
|
2321
|
-
if (framesRead == 0) {
|
2322
|
-
return 0;
|
2323
|
-
}
|
2324
|
-
pSRC->algo.linear.isPrevFramesLoaded = DRMP3_TRUE;
|
2325
|
-
}
|
2326
|
-
if (!pSRC->algo.linear.isNextFramesLoaded) {
|
2327
|
-
drmp3_uint64 framesRead = drmp3_src_cache_read_frames(&pSRC->cache, 1, pSRC->bin + pSRC->config.channels);
|
2328
|
-
if (framesRead == 0) {
|
2329
|
-
return 0;
|
2330
|
-
}
|
2331
|
-
pSRC->algo.linear.isNextFramesLoaded = DRMP3_TRUE;
|
2601
|
+
if (!pMP3->onSeek(pMP3->pUserData, offset, origin)) {
|
2602
|
+
return DRMP3_FALSE;
|
2332
2603
|
}
|
2333
2604
|
|
2334
|
-
|
2605
|
+
if (origin == drmp3_seek_origin_start) {
|
2606
|
+
pMP3->streamCursor = (drmp3_uint64)offset;
|
2607
|
+
} else {
|
2608
|
+
pMP3->streamCursor += offset;
|
2609
|
+
}
|
2335
2610
|
|
2336
|
-
|
2337
|
-
|
2338
|
-
// The bin is where the previous and next frames are located.
|
2339
|
-
float* pPrevFrame = pSRC->bin;
|
2340
|
-
float* pNextFrame = pSRC->bin + pSRC->config.channels;
|
2611
|
+
return DRMP3_TRUE;
|
2612
|
+
}
|
2341
2613
|
|
2342
|
-
|
2614
|
+
static drmp3_bool32 drmp3__on_seek_64(drmp3* pMP3, drmp3_uint64 offset, drmp3_seek_origin origin)
|
2615
|
+
{
|
2616
|
+
if (offset <= 0x7FFFFFFF) {
|
2617
|
+
return drmp3__on_seek(pMP3, (int)offset, origin);
|
2618
|
+
}
|
2343
2619
|
|
2344
|
-
pSRC->algo.linear.alpha += factor;
|
2345
2620
|
|
2346
|
-
|
2347
|
-
|
2348
|
-
|
2621
|
+
/* Getting here "offset" is too large for a 32-bit integer. We just keep seeking forward until we hit the offset. */
|
2622
|
+
if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_start)) {
|
2623
|
+
return DRMP3_FALSE;
|
2624
|
+
}
|
2349
2625
|
|
2350
|
-
|
2351
|
-
|
2352
|
-
|
2626
|
+
offset -= 0x7FFFFFFF;
|
2627
|
+
while (offset > 0) {
|
2628
|
+
if (offset <= 0x7FFFFFFF) {
|
2629
|
+
if (!drmp3__on_seek(pMP3, (int)offset, drmp3_seek_origin_current)) {
|
2630
|
+
return DRMP3_FALSE;
|
2353
2631
|
}
|
2354
|
-
|
2355
|
-
|
2356
|
-
if (
|
2357
|
-
|
2358
|
-
pNextFrame[j] = 0;
|
2359
|
-
}
|
2360
|
-
|
2361
|
-
if (pSRC->algo.linear.isNextFramesLoaded) {
|
2362
|
-
pSRC->algo.linear.isNextFramesLoaded = DRMP3_FALSE;
|
2363
|
-
} else {
|
2364
|
-
if (flush) {
|
2365
|
-
pSRC->algo.linear.isPrevFramesLoaded = DRMP3_FALSE;
|
2366
|
-
}
|
2367
|
-
}
|
2368
|
-
|
2369
|
-
break;
|
2632
|
+
offset = 0;
|
2633
|
+
} else {
|
2634
|
+
if (!drmp3__on_seek(pMP3, 0x7FFFFFFF, drmp3_seek_origin_current)) {
|
2635
|
+
return DRMP3_FALSE;
|
2370
2636
|
}
|
2371
|
-
|
2372
|
-
|
2373
|
-
pFramesOut = (drmp3_uint8*)pFramesOut + (1 * pSRC->config.channels * sizeof(float));
|
2374
|
-
frameCount -= 1;
|
2375
|
-
totalFramesRead += 1;
|
2376
|
-
|
2377
|
-
// If there's no frames available we need to get out of this loop.
|
2378
|
-
if (!pSRC->algo.linear.isNextFramesLoaded && (!flush || !pSRC->algo.linear.isPrevFramesLoaded)) {
|
2379
|
-
break;
|
2637
|
+
offset -= 0x7FFFFFFF;
|
2380
2638
|
}
|
2381
2639
|
}
|
2382
2640
|
|
2383
|
-
return
|
2641
|
+
return DRMP3_TRUE;
|
2384
2642
|
}
|
2385
2643
|
|
2386
2644
|
|
2387
|
-
static
|
2645
|
+
static drmp3_uint32 drmp3_decode_next_frame_ex__callbacks(drmp3* pMP3, drmp3d_sample_t* pPCMFrames)
|
2388
2646
|
{
|
2389
|
-
|
2390
|
-
|
2647
|
+
drmp3_uint32 pcmFramesRead = 0;
|
2648
|
+
|
2649
|
+
DRMP3_ASSERT(pMP3 != NULL);
|
2650
|
+
DRMP3_ASSERT(pMP3->onRead != NULL);
|
2391
2651
|
|
2392
2652
|
if (pMP3->atEnd) {
|
2393
|
-
return
|
2653
|
+
return 0;
|
2394
2654
|
}
|
2395
2655
|
|
2396
|
-
|
2397
|
-
|
2398
|
-
|
2399
|
-
|
2656
|
+
for (;;) {
|
2657
|
+
drmp3dec_frame_info info;
|
2658
|
+
|
2659
|
+
/* minimp3 recommends doing data submission in chunks of at least 16K. If we don't have at least 16K bytes available, get more. */
|
2660
|
+
if (pMP3->dataSize < DRMP3_MIN_DATA_CHUNK_SIZE) {
|
2661
|
+
size_t bytesRead;
|
2662
|
+
|
2663
|
+
/* First we need to move the data down. */
|
2664
|
+
if (pMP3->pData != NULL) {
|
2665
|
+
DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize);
|
2666
|
+
}
|
2667
|
+
|
2668
|
+
pMP3->dataConsumed = 0;
|
2669
|
+
|
2400
2670
|
if (pMP3->dataCapacity < DRMP3_DATA_CHUNK_SIZE) {
|
2401
|
-
|
2402
|
-
|
2671
|
+
drmp3_uint8* pNewData;
|
2672
|
+
size_t newDataCap;
|
2673
|
+
|
2674
|
+
newDataCap = DRMP3_DATA_CHUNK_SIZE;
|
2675
|
+
|
2676
|
+
pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks);
|
2403
2677
|
if (pNewData == NULL) {
|
2404
|
-
return
|
2678
|
+
return 0; /* Out of memory. */
|
2405
2679
|
}
|
2406
2680
|
|
2407
2681
|
pMP3->pData = pNewData;
|
2682
|
+
pMP3->dataCapacity = newDataCap;
|
2408
2683
|
}
|
2409
2684
|
|
2410
|
-
|
2685
|
+
bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
|
2411
2686
|
if (bytesRead == 0) {
|
2412
2687
|
if (pMP3->dataSize == 0) {
|
2413
2688
|
pMP3->atEnd = DRMP3_TRUE;
|
2414
|
-
return
|
2689
|
+
return 0; /* No data. */
|
2415
2690
|
}
|
2416
2691
|
}
|
2417
2692
|
|
@@ -2420,215 +2695,193 @@ static drmp3_bool32 drmp3_decode_next_frame(drmp3* pMP3)
|
|
2420
2695
|
|
2421
2696
|
if (pMP3->dataSize > INT_MAX) {
|
2422
2697
|
pMP3->atEnd = DRMP3_TRUE;
|
2423
|
-
return
|
2698
|
+
return 0; /* File too big. */
|
2424
2699
|
}
|
2425
2700
|
|
2426
|
-
|
2427
|
-
|
2428
|
-
if (samplesRead != 0) {
|
2429
|
-
size_t leftoverDataSize = (pMP3->dataSize - (size_t)info.frame_bytes);
|
2430
|
-
for (size_t i = 0; i < leftoverDataSize; ++i) {
|
2431
|
-
pMP3->pData[i] = pMP3->pData[i + (size_t)info.frame_bytes];
|
2432
|
-
}
|
2701
|
+
DRMP3_ASSERT(pMP3->pData != NULL);
|
2702
|
+
DRMP3_ASSERT(pMP3->dataCapacity > 0);
|
2433
2703
|
|
2434
|
-
|
2435
|
-
|
2436
|
-
|
2437
|
-
|
2438
|
-
pMP3->
|
2439
|
-
|
2704
|
+
pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->pData + pMP3->dataConsumed, (int)pMP3->dataSize, pPCMFrames, &info); /* <-- Safe size_t -> int conversion thanks to the check above. */
|
2705
|
+
|
2706
|
+
/* Consume the data. */
|
2707
|
+
if (info.frame_bytes > 0) {
|
2708
|
+
pMP3->dataConsumed += (size_t)info.frame_bytes;
|
2709
|
+
pMP3->dataSize -= (size_t)info.frame_bytes;
|
2710
|
+
}
|
2711
|
+
|
2712
|
+
/* pcmFramesRead will be equal to 0 if decoding failed. If it is zero and info.frame_bytes > 0 then we have successfully decoded the frame. */
|
2713
|
+
if (pcmFramesRead > 0) {
|
2714
|
+
pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header);
|
2715
|
+
pMP3->pcmFramesConsumedInMP3Frame = 0;
|
2716
|
+
pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
|
2717
|
+
pMP3->mp3FrameChannels = info.channels;
|
2718
|
+
pMP3->mp3FrameSampleRate = info.hz;
|
2440
2719
|
break;
|
2441
|
-
} else {
|
2442
|
-
|
2720
|
+
} else if (info.frame_bytes == 0) {
|
2721
|
+
/* Need more data. minimp3 recommends doing data submission in 16K chunks. */
|
2722
|
+
size_t bytesRead;
|
2723
|
+
|
2724
|
+
/* First we need to move the data down. */
|
2725
|
+
DRMP3_MOVE_MEMORY(pMP3->pData, pMP3->pData + pMP3->dataConsumed, pMP3->dataSize);
|
2726
|
+
pMP3->dataConsumed = 0;
|
2727
|
+
|
2443
2728
|
if (pMP3->dataCapacity == pMP3->dataSize) {
|
2444
|
-
|
2445
|
-
|
2446
|
-
|
2729
|
+
/* No room. Expand. */
|
2730
|
+
drmp3_uint8* pNewData;
|
2731
|
+
size_t newDataCap;
|
2732
|
+
|
2733
|
+
newDataCap = pMP3->dataCapacity + DRMP3_DATA_CHUNK_SIZE;
|
2734
|
+
|
2735
|
+
pNewData = (drmp3_uint8*)drmp3__realloc_from_callbacks(pMP3->pData, newDataCap, pMP3->dataCapacity, &pMP3->allocationCallbacks);
|
2447
2736
|
if (pNewData == NULL) {
|
2448
|
-
return
|
2737
|
+
return 0; /* Out of memory. */
|
2449
2738
|
}
|
2450
2739
|
|
2451
2740
|
pMP3->pData = pNewData;
|
2741
|
+
pMP3->dataCapacity = newDataCap;
|
2452
2742
|
}
|
2453
2743
|
|
2454
|
-
|
2455
|
-
|
2744
|
+
/* Fill in a chunk. */
|
2745
|
+
bytesRead = drmp3__on_read(pMP3, pMP3->pData + pMP3->dataSize, (pMP3->dataCapacity - pMP3->dataSize));
|
2456
2746
|
if (bytesRead == 0) {
|
2457
2747
|
pMP3->atEnd = DRMP3_TRUE;
|
2458
|
-
return
|
2748
|
+
return 0; /* Error reading more data. */
|
2459
2749
|
}
|
2460
2750
|
|
2461
2751
|
pMP3->dataSize += bytesRead;
|
2462
2752
|
}
|
2463
|
-
}
|
2753
|
+
};
|
2464
2754
|
|
2465
|
-
return
|
2755
|
+
return pcmFramesRead;
|
2466
2756
|
}
|
2467
2757
|
|
2468
|
-
static
|
2758
|
+
static drmp3_uint32 drmp3_decode_next_frame_ex__memory(drmp3* pMP3, drmp3d_sample_t* pPCMFrames)
|
2469
2759
|
{
|
2470
|
-
|
2471
|
-
|
2472
|
-
drmp3_assert(pMP3->onRead != NULL);
|
2473
|
-
|
2474
|
-
float* pFramesOutF = (float*)pFramesOut;
|
2475
|
-
drmp3_uint32 totalFramesRead = 0;
|
2760
|
+
drmp3_uint32 pcmFramesRead = 0;
|
2761
|
+
drmp3dec_frame_info info;
|
2476
2762
|
|
2477
|
-
|
2478
|
-
|
2479
|
-
while (pMP3->framesRemaining > 0 && frameCount > 0) {
|
2480
|
-
drmp3d_sample_t* frames = (drmp3d_sample_t*)pMP3->frames;
|
2481
|
-
#ifndef DR_MP3_FLOAT_OUTPUT
|
2482
|
-
if (pMP3->frameChannels == 1) {
|
2483
|
-
if (pMP3->channels == 1) {
|
2484
|
-
// Mono -> Mono.
|
2485
|
-
pFramesOutF[0] = frames[pMP3->framesConsumed] / 32768.0f;
|
2486
|
-
} else {
|
2487
|
-
// Mono -> Stereo.
|
2488
|
-
pFramesOutF[0] = frames[pMP3->framesConsumed] / 32768.0f;
|
2489
|
-
pFramesOutF[1] = frames[pMP3->framesConsumed] / 32768.0f;
|
2490
|
-
}
|
2491
|
-
} else {
|
2492
|
-
if (pMP3->channels == 1) {
|
2493
|
-
// Stereo -> Mono
|
2494
|
-
float sample = 0;
|
2495
|
-
sample += frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f;
|
2496
|
-
sample += frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f;
|
2497
|
-
pFramesOutF[0] = sample * 0.5f;
|
2498
|
-
} else {
|
2499
|
-
// Stereo -> Stereo
|
2500
|
-
pFramesOutF[0] = frames[(pMP3->framesConsumed*pMP3->frameChannels)+0] / 32768.0f;
|
2501
|
-
pFramesOutF[1] = frames[(pMP3->framesConsumed*pMP3->frameChannels)+1] / 32768.0f;
|
2502
|
-
}
|
2503
|
-
}
|
2504
|
-
#else
|
2505
|
-
if (pMP3->frameChannels == 1) {
|
2506
|
-
if (pMP3->channels == 1) {
|
2507
|
-
// Mono -> Mono.
|
2508
|
-
pFramesOutF[0] = frames[pMP3->framesConsumed];
|
2509
|
-
} else {
|
2510
|
-
// Mono -> Stereo.
|
2511
|
-
pFramesOutF[0] = frames[pMP3->framesConsumed];
|
2512
|
-
pFramesOutF[1] = frames[pMP3->framesConsumed];
|
2513
|
-
}
|
2514
|
-
} else {
|
2515
|
-
if (pMP3->channels == 1) {
|
2516
|
-
// Stereo -> Mono
|
2517
|
-
float sample = 0;
|
2518
|
-
sample += frames[(pMP3->framesConsumed*pMP3->frameChannels)+0];
|
2519
|
-
sample += frames[(pMP3->framesConsumed*pMP3->frameChannels)+1];
|
2520
|
-
pFramesOutF[0] = sample * 0.5f;
|
2521
|
-
} else {
|
2522
|
-
// Stereo -> Stereo
|
2523
|
-
pFramesOutF[0] = frames[(pMP3->framesConsumed*pMP3->frameChannels)+0];
|
2524
|
-
pFramesOutF[1] = frames[(pMP3->framesConsumed*pMP3->frameChannels)+1];
|
2525
|
-
}
|
2526
|
-
}
|
2527
|
-
#endif
|
2763
|
+
DRMP3_ASSERT(pMP3 != NULL);
|
2764
|
+
DRMP3_ASSERT(pMP3->memory.pData != NULL);
|
2528
2765
|
|
2529
|
-
|
2530
|
-
|
2531
|
-
|
2532
|
-
totalFramesRead += 1;
|
2533
|
-
pFramesOutF += pSRC->config.channels;
|
2534
|
-
}
|
2766
|
+
if (pMP3->atEnd) {
|
2767
|
+
return 0;
|
2768
|
+
}
|
2535
2769
|
|
2536
|
-
|
2770
|
+
for (;;) {
|
2771
|
+
pcmFramesRead = drmp3dec_decode_frame(&pMP3->decoder, pMP3->memory.pData + pMP3->memory.currentReadPos, (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos), pPCMFrames, &info);
|
2772
|
+
if (pcmFramesRead > 0) {
|
2773
|
+
pcmFramesRead = drmp3_hdr_frame_samples(pMP3->decoder.header);
|
2774
|
+
pMP3->pcmFramesConsumedInMP3Frame = 0;
|
2775
|
+
pMP3->pcmFramesRemainingInMP3Frame = pcmFramesRead;
|
2776
|
+
pMP3->mp3FrameChannels = info.channels;
|
2777
|
+
pMP3->mp3FrameSampleRate = info.hz;
|
2778
|
+
break;
|
2779
|
+
} else if (info.frame_bytes > 0) {
|
2780
|
+
/* No frames were read, but it looks like we skipped past one. Read the next MP3 frame. */
|
2781
|
+
pMP3->memory.currentReadPos += (size_t)info.frame_bytes;
|
2782
|
+
} else {
|
2783
|
+
/* Nothing at all was read. Abort. */
|
2537
2784
|
break;
|
2538
2785
|
}
|
2786
|
+
}
|
2539
2787
|
|
2540
|
-
|
2788
|
+
/* Consume the data. */
|
2789
|
+
pMP3->memory.currentReadPos += (size_t)info.frame_bytes;
|
2541
2790
|
|
2542
|
-
|
2543
|
-
|
2544
|
-
|
2545
|
-
|
2546
|
-
|
2791
|
+
return pcmFramesRead;
|
2792
|
+
}
|
2793
|
+
|
2794
|
+
static drmp3_uint32 drmp3_decode_next_frame_ex(drmp3* pMP3, drmp3d_sample_t* pPCMFrames)
|
2795
|
+
{
|
2796
|
+
if (pMP3->memory.pData != NULL && pMP3->memory.dataSize > 0) {
|
2797
|
+
return drmp3_decode_next_frame_ex__memory(pMP3, pPCMFrames);
|
2798
|
+
} else {
|
2799
|
+
return drmp3_decode_next_frame_ex__callbacks(pMP3, pPCMFrames);
|
2547
2800
|
}
|
2801
|
+
}
|
2548
2802
|
|
2549
|
-
|
2803
|
+
static drmp3_uint32 drmp3_decode_next_frame(drmp3* pMP3)
|
2804
|
+
{
|
2805
|
+
DRMP3_ASSERT(pMP3 != NULL);
|
2806
|
+
return drmp3_decode_next_frame_ex(pMP3, (drmp3d_sample_t*)pMP3->pcmFrames);
|
2550
2807
|
}
|
2551
2808
|
|
2552
|
-
|
2809
|
+
#if 0
|
2810
|
+
static drmp3_uint32 drmp3_seek_next_frame(drmp3* pMP3)
|
2553
2811
|
{
|
2554
|
-
|
2555
|
-
drmp3_assert(onRead != NULL);
|
2812
|
+
drmp3_uint32 pcmFrameCount;
|
2556
2813
|
|
2557
|
-
|
2558
|
-
drmp3dec_init(&pMP3->decoder);
|
2814
|
+
DRMP3_ASSERT(pMP3 != NULL);
|
2559
2815
|
|
2560
|
-
|
2561
|
-
|
2562
|
-
|
2563
|
-
drmp3_copy_memory(&config, pConfig, sizeof (drmp3_config));
|
2564
|
-
} else {
|
2565
|
-
drmp3_zero_object(&config);
|
2816
|
+
pcmFrameCount = drmp3_decode_next_frame_ex(pMP3, NULL);
|
2817
|
+
if (pcmFrameCount == 0) {
|
2818
|
+
return 0;
|
2566
2819
|
}
|
2567
2820
|
|
2568
|
-
|
2569
|
-
|
2570
|
-
|
2571
|
-
|
2821
|
+
/* We have essentially just skipped past the frame, so just set the remaining samples to 0. */
|
2822
|
+
pMP3->currentPCMFrame += pcmFrameCount;
|
2823
|
+
pMP3->pcmFramesConsumedInMP3Frame = pcmFrameCount;
|
2824
|
+
pMP3->pcmFramesRemainingInMP3Frame = 0;
|
2572
2825
|
|
2573
|
-
|
2574
|
-
|
2575
|
-
|
2576
|
-
}
|
2826
|
+
return pcmFrameCount;
|
2827
|
+
}
|
2828
|
+
#endif
|
2577
2829
|
|
2578
|
-
|
2579
|
-
|
2580
|
-
|
2581
|
-
|
2830
|
+
static drmp3_bool32 drmp3_init_internal(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks)
|
2831
|
+
{
|
2832
|
+
DRMP3_ASSERT(pMP3 != NULL);
|
2833
|
+
DRMP3_ASSERT(onRead != NULL);
|
2834
|
+
|
2835
|
+
/* This function assumes the output object has already been reset to 0. Do not do that here, otherwise things will break. */
|
2836
|
+
drmp3dec_init(&pMP3->decoder);
|
2582
2837
|
|
2583
2838
|
pMP3->onRead = onRead;
|
2584
2839
|
pMP3->onSeek = onSeek;
|
2585
2840
|
pMP3->pUserData = pUserData;
|
2841
|
+
pMP3->allocationCallbacks = drmp3_copy_allocation_callbacks_or_defaults(pAllocationCallbacks);
|
2586
2842
|
|
2587
|
-
|
2588
|
-
|
2589
|
-
drmp3_zero_object(&srcConfig);
|
2590
|
-
srcConfig.sampleRateIn = DR_MP3_DEFAULT_SAMPLE_RATE;
|
2591
|
-
srcConfig.sampleRateOut = pMP3->sampleRate;
|
2592
|
-
srcConfig.channels = pMP3->channels;
|
2593
|
-
srcConfig.algorithm = drmp3_src_algorithm_linear;
|
2594
|
-
if (!drmp3_src_init(&srcConfig, drmp3_read_src, pMP3, &pMP3->src)) {
|
2595
|
-
drmp3_uninit(pMP3);
|
2596
|
-
return DRMP3_FALSE;
|
2843
|
+
if (pMP3->allocationCallbacks.onFree == NULL || (pMP3->allocationCallbacks.onMalloc == NULL && pMP3->allocationCallbacks.onRealloc == NULL)) {
|
2844
|
+
return DRMP3_FALSE; /* Invalid allocation callbacks. */
|
2597
2845
|
}
|
2598
2846
|
|
2599
|
-
|
2600
|
-
if (
|
2601
|
-
|
2602
|
-
return DRMP3_FALSE;
|
2847
|
+
/* Decode the first frame to confirm that it is indeed a valid MP3 stream. */
|
2848
|
+
if (drmp3_decode_next_frame(pMP3) == 0) {
|
2849
|
+
drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks); /* The call above may have allocated memory. Need to make sure it's freed before aborting. */
|
2850
|
+
return DRMP3_FALSE; /* Not a valid MP3 stream. */
|
2603
2851
|
}
|
2604
2852
|
|
2853
|
+
pMP3->channels = pMP3->mp3FrameChannels;
|
2854
|
+
pMP3->sampleRate = pMP3->mp3FrameSampleRate;
|
2855
|
+
|
2605
2856
|
return DRMP3_TRUE;
|
2606
2857
|
}
|
2607
2858
|
|
2608
|
-
drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const
|
2859
|
+
DRMP3_API drmp3_bool32 drmp3_init(drmp3* pMP3, drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, const drmp3_allocation_callbacks* pAllocationCallbacks)
|
2609
2860
|
{
|
2610
2861
|
if (pMP3 == NULL || onRead == NULL) {
|
2611
2862
|
return DRMP3_FALSE;
|
2612
2863
|
}
|
2613
2864
|
|
2614
|
-
|
2615
|
-
return drmp3_init_internal(pMP3, onRead, onSeek, pUserData,
|
2865
|
+
DRMP3_ZERO_OBJECT(pMP3);
|
2866
|
+
return drmp3_init_internal(pMP3, onRead, onSeek, pUserData, pAllocationCallbacks);
|
2616
2867
|
}
|
2617
2868
|
|
2618
2869
|
|
2619
2870
|
static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead)
|
2620
2871
|
{
|
2621
2872
|
drmp3* pMP3 = (drmp3*)pUserData;
|
2622
|
-
|
2623
|
-
drmp3_assert(pMP3->memory.dataSize >= pMP3->memory.currentReadPos);
|
2873
|
+
size_t bytesRemaining;
|
2624
2874
|
|
2625
|
-
|
2875
|
+
DRMP3_ASSERT(pMP3 != NULL);
|
2876
|
+
DRMP3_ASSERT(pMP3->memory.dataSize >= pMP3->memory.currentReadPos);
|
2877
|
+
|
2878
|
+
bytesRemaining = pMP3->memory.dataSize - pMP3->memory.currentReadPos;
|
2626
2879
|
if (bytesToRead > bytesRemaining) {
|
2627
2880
|
bytesToRead = bytesRemaining;
|
2628
2881
|
}
|
2629
2882
|
|
2630
2883
|
if (bytesToRead > 0) {
|
2631
|
-
|
2884
|
+
DRMP3_COPY_MEMORY(pBufferOut, pMP3->memory.pData + pMP3->memory.currentReadPos, bytesToRead);
|
2632
2885
|
pMP3->memory.currentReadPos += bytesToRead;
|
2633
2886
|
}
|
2634
2887
|
|
@@ -2638,39 +2891,40 @@ static size_t drmp3__on_read_memory(void* pUserData, void* pBufferOut, size_t by
|
|
2638
2891
|
static drmp3_bool32 drmp3__on_seek_memory(void* pUserData, int byteOffset, drmp3_seek_origin origin)
|
2639
2892
|
{
|
2640
2893
|
drmp3* pMP3 = (drmp3*)pUserData;
|
2641
|
-
|
2894
|
+
|
2895
|
+
DRMP3_ASSERT(pMP3 != NULL);
|
2642
2896
|
|
2643
2897
|
if (origin == drmp3_seek_origin_current) {
|
2644
2898
|
if (byteOffset > 0) {
|
2645
2899
|
if (pMP3->memory.currentReadPos + byteOffset > pMP3->memory.dataSize) {
|
2646
|
-
byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos);
|
2900
|
+
byteOffset = (int)(pMP3->memory.dataSize - pMP3->memory.currentReadPos); /* Trying to seek too far forward. */
|
2647
2901
|
}
|
2648
2902
|
} else {
|
2649
2903
|
if (pMP3->memory.currentReadPos < (size_t)-byteOffset) {
|
2650
|
-
byteOffset = -(int)pMP3->memory.currentReadPos;
|
2904
|
+
byteOffset = -(int)pMP3->memory.currentReadPos; /* Trying to seek too far backwards. */
|
2651
2905
|
}
|
2652
2906
|
}
|
2653
2907
|
|
2654
|
-
|
2908
|
+
/* This will never underflow thanks to the clamps above. */
|
2655
2909
|
pMP3->memory.currentReadPos += byteOffset;
|
2656
2910
|
} else {
|
2657
2911
|
if ((drmp3_uint32)byteOffset <= pMP3->memory.dataSize) {
|
2658
2912
|
pMP3->memory.currentReadPos = byteOffset;
|
2659
2913
|
} else {
|
2660
|
-
pMP3->memory.currentReadPos = pMP3->memory.dataSize;
|
2914
|
+
pMP3->memory.currentReadPos = pMP3->memory.dataSize; /* Trying to seek too far forward. */
|
2661
2915
|
}
|
2662
2916
|
}
|
2663
2917
|
|
2664
2918
|
return DRMP3_TRUE;
|
2665
2919
|
}
|
2666
2920
|
|
2667
|
-
drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const
|
2921
|
+
DRMP3_API drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize, const drmp3_allocation_callbacks* pAllocationCallbacks)
|
2668
2922
|
{
|
2669
2923
|
if (pMP3 == NULL) {
|
2670
2924
|
return DRMP3_FALSE;
|
2671
2925
|
}
|
2672
2926
|
|
2673
|
-
|
2927
|
+
DRMP3_ZERO_OBJECT(pMP3);
|
2674
2928
|
|
2675
2929
|
if (pData == NULL || dataSize == 0) {
|
2676
2930
|
return DRMP3_FALSE;
|
@@ -2680,317 +2934,1852 @@ drmp3_bool32 drmp3_init_memory(drmp3* pMP3, const void* pData, size_t dataSize,
|
|
2680
2934
|
pMP3->memory.dataSize = dataSize;
|
2681
2935
|
pMP3->memory.currentReadPos = 0;
|
2682
2936
|
|
2683
|
-
return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3,
|
2937
|
+
return drmp3_init_internal(pMP3, drmp3__on_read_memory, drmp3__on_seek_memory, pMP3, pAllocationCallbacks);
|
2684
2938
|
}
|
2685
2939
|
|
2686
2940
|
|
2687
2941
|
#ifndef DR_MP3_NO_STDIO
|
2688
2942
|
#include <stdio.h>
|
2943
|
+
#include <wchar.h> /* For wcslen(), wcsrtombs() */
|
2689
2944
|
|
2690
|
-
|
2691
|
-
|
2692
|
-
|
2693
|
-
}
|
2694
|
-
|
2695
|
-
static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin)
|
2696
|
-
{
|
2697
|
-
return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
|
2698
|
-
}
|
2699
|
-
|
2700
|
-
drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* filePath, const drmp3_config* pConfig)
|
2701
|
-
{
|
2702
|
-
FILE* pFile;
|
2703
|
-
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
2704
|
-
if (fopen_s(&pFile, filePath, "rb") != 0) {
|
2705
|
-
return DRMP3_FALSE;
|
2706
|
-
}
|
2707
|
-
#else
|
2708
|
-
pFile = fopen(filePath, "rb");
|
2709
|
-
if (pFile == NULL) {
|
2710
|
-
return DRMP3_FALSE;
|
2711
|
-
}
|
2712
|
-
#endif
|
2713
|
-
|
2714
|
-
return drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pConfig);
|
2715
|
-
}
|
2716
|
-
#endif
|
2717
|
-
|
2718
|
-
void drmp3_uninit(drmp3* pMP3)
|
2719
|
-
{
|
2720
|
-
if (pMP3 == NULL) return;
|
2721
|
-
|
2722
|
-
#ifndef DR_MP3_NO_STDIO
|
2723
|
-
if (pMP3->onRead == drmp3__on_read_stdio) {
|
2724
|
-
fclose((FILE*)pMP3->pUserData);
|
2725
|
-
}
|
2726
|
-
#endif
|
2727
|
-
|
2728
|
-
drmp3_free(pMP3->pData);
|
2729
|
-
}
|
2730
|
-
|
2731
|
-
drmp3_uint64 drmp3_read_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut)
|
2732
|
-
{
|
2733
|
-
if (pMP3 == NULL || pMP3->onRead == NULL) return 0;
|
2734
|
-
|
2735
|
-
drmp3_uint64 totalFramesRead = 0;
|
2736
|
-
|
2737
|
-
if (pBufferOut == NULL) {
|
2738
|
-
float temp[4096];
|
2739
|
-
while (framesToRead > 0) {
|
2740
|
-
drmp3_uint64 framesToReadRightNow = sizeof(temp)/sizeof(temp[0]) / pMP3->channels;
|
2741
|
-
if (framesToReadRightNow > framesToRead) {
|
2742
|
-
framesToReadRightNow = framesToRead;
|
2743
|
-
}
|
2744
|
-
|
2745
|
-
drmp3_uint64 framesJustRead = drmp3_read_f32(pMP3, framesToReadRightNow, temp);
|
2746
|
-
if (framesJustRead == 0) {
|
2747
|
-
break;
|
2748
|
-
}
|
2749
|
-
|
2750
|
-
framesToRead -= framesJustRead;
|
2751
|
-
totalFramesRead += framesJustRead;
|
2752
|
-
}
|
2753
|
-
} else {
|
2754
|
-
totalFramesRead = drmp3_src_read_frames_ex(&pMP3->src, framesToRead, pBufferOut, DRMP3_TRUE);
|
2755
|
-
}
|
2756
|
-
|
2757
|
-
return totalFramesRead;
|
2758
|
-
}
|
2759
|
-
|
2760
|
-
drmp3_bool32 drmp3_seek_to_frame(drmp3* pMP3, drmp3_uint64 frameIndex)
|
2761
|
-
{
|
2762
|
-
if (pMP3 == NULL || pMP3->onSeek == NULL) return DRMP3_FALSE;
|
2763
|
-
|
2764
|
-
// Seek to the start of the stream to begin with.
|
2765
|
-
if (!pMP3->onSeek(pMP3->pUserData, 0, drmp3_seek_origin_start)) {
|
2766
|
-
return DRMP3_FALSE;
|
2767
|
-
}
|
2768
|
-
|
2769
|
-
// Clear any cached data.
|
2770
|
-
pMP3->framesConsumed = 0;
|
2771
|
-
pMP3->framesRemaining = 0;
|
2772
|
-
pMP3->dataSize = 0;
|
2773
|
-
pMP3->atEnd = DRMP3_FALSE;
|
2774
|
-
|
2775
|
-
// TODO: Optimize.
|
2776
|
-
//
|
2777
|
-
// This is inefficient. We simply read frames from the start of the stream.
|
2778
|
-
drmp3_uint64 framesRead = drmp3_read_f32(pMP3, frameIndex, NULL);
|
2779
|
-
if (framesRead != frameIndex) {
|
2780
|
-
return DRMP3_FALSE;
|
2781
|
-
}
|
2782
|
-
|
2783
|
-
return DRMP3_TRUE;
|
2784
|
-
}
|
2785
|
-
|
2786
|
-
|
2787
|
-
|
2788
|
-
float* drmp3__full_decode_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
|
2789
|
-
{
|
2790
|
-
drmp3_assert(pMP3 != NULL);
|
2791
|
-
|
2792
|
-
drmp3_uint64 totalFramesRead = 0;
|
2793
|
-
drmp3_uint64 framesCapacity = 0;
|
2794
|
-
float* pFrames = NULL;
|
2795
|
-
|
2796
|
-
float temp[4096];
|
2797
|
-
for (;;) {
|
2798
|
-
drmp3_uint64 framesToReadRightNow = drmp3_countof(temp) / pMP3->channels;
|
2799
|
-
drmp3_uint64 framesJustRead = drmp3_read_f32(pMP3, framesToReadRightNow, temp);
|
2800
|
-
if (framesJustRead == 0) {
|
2801
|
-
break;
|
2802
|
-
}
|
2803
|
-
|
2804
|
-
// Reallocate the output buffer if there's not enough room.
|
2805
|
-
if (framesCapacity < totalFramesRead + framesJustRead) {
|
2806
|
-
framesCapacity *= 2;
|
2807
|
-
if (framesCapacity < totalFramesRead + framesJustRead) {
|
2808
|
-
framesCapacity = totalFramesRead + framesJustRead;
|
2809
|
-
}
|
2810
|
-
|
2811
|
-
drmp3_uint64 newFramesBufferSize = framesCapacity*pMP3->channels*sizeof(float);
|
2812
|
-
if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) {
|
2813
|
-
break;
|
2814
|
-
}
|
2815
|
-
|
2816
|
-
float* pNewFrames = (float*)drmp3_realloc(pFrames, (size_t)newFramesBufferSize);
|
2817
|
-
if (pNewFrames == NULL) {
|
2818
|
-
drmp3_free(pFrames);
|
2819
|
-
break;
|
2820
|
-
}
|
2821
|
-
|
2822
|
-
pFrames = pNewFrames;
|
2823
|
-
}
|
2824
|
-
|
2825
|
-
drmp3_copy_memory(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float)));
|
2826
|
-
totalFramesRead += framesJustRead;
|
2827
|
-
|
2828
|
-
// If the number of frames we asked for is less that what we actually read it means we've reached the end.
|
2829
|
-
if (framesJustRead != framesToReadRightNow) {
|
2830
|
-
break;
|
2831
|
-
}
|
2832
|
-
}
|
2833
|
-
|
2834
|
-
if (pConfig != NULL) {
|
2835
|
-
pConfig->outputChannels = pMP3->channels;
|
2836
|
-
pConfig->outputSampleRate = pMP3->sampleRate;
|
2837
|
-
}
|
2838
|
-
|
2839
|
-
drmp3_uninit(pMP3);
|
2840
|
-
|
2841
|
-
if (pTotalFrameCount) *pTotalFrameCount = totalFramesRead;
|
2842
|
-
return pFrames;
|
2843
|
-
}
|
2844
|
-
|
2845
|
-
float* drmp3_open_and_decode_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
|
2945
|
+
/* drmp3_result_from_errno() is only used inside DR_MP3_NO_STDIO for now. Move this out if it's ever used elsewhere. */
|
2946
|
+
#include <errno.h>
|
2947
|
+
static drmp3_result drmp3_result_from_errno(int e)
|
2846
2948
|
{
|
2847
|
-
|
2848
|
-
|
2849
|
-
return
|
2850
|
-
|
2851
|
-
|
2852
|
-
|
2853
|
-
|
2854
|
-
|
2855
|
-
|
2856
|
-
|
2857
|
-
|
2858
|
-
|
2859
|
-
|
2860
|
-
|
2861
|
-
|
2862
|
-
|
2863
|
-
|
2864
|
-
|
2865
|
-
#
|
2866
|
-
|
2867
|
-
|
2868
|
-
|
2869
|
-
|
2949
|
+
switch (e)
|
2950
|
+
{
|
2951
|
+
case 0: return DRMP3_SUCCESS;
|
2952
|
+
#ifdef EPERM
|
2953
|
+
case EPERM: return DRMP3_INVALID_OPERATION;
|
2954
|
+
#endif
|
2955
|
+
#ifdef ENOENT
|
2956
|
+
case ENOENT: return DRMP3_DOES_NOT_EXIST;
|
2957
|
+
#endif
|
2958
|
+
#ifdef ESRCH
|
2959
|
+
case ESRCH: return DRMP3_DOES_NOT_EXIST;
|
2960
|
+
#endif
|
2961
|
+
#ifdef EINTR
|
2962
|
+
case EINTR: return DRMP3_INTERRUPT;
|
2963
|
+
#endif
|
2964
|
+
#ifdef EIO
|
2965
|
+
case EIO: return DRMP3_IO_ERROR;
|
2966
|
+
#endif
|
2967
|
+
#ifdef ENXIO
|
2968
|
+
case ENXIO: return DRMP3_DOES_NOT_EXIST;
|
2969
|
+
#endif
|
2970
|
+
#ifdef E2BIG
|
2971
|
+
case E2BIG: return DRMP3_INVALID_ARGS;
|
2972
|
+
#endif
|
2973
|
+
#ifdef ENOEXEC
|
2974
|
+
case ENOEXEC: return DRMP3_INVALID_FILE;
|
2975
|
+
#endif
|
2976
|
+
#ifdef EBADF
|
2977
|
+
case EBADF: return DRMP3_INVALID_FILE;
|
2978
|
+
#endif
|
2979
|
+
#ifdef ECHILD
|
2980
|
+
case ECHILD: return DRMP3_ERROR;
|
2981
|
+
#endif
|
2982
|
+
#ifdef EAGAIN
|
2983
|
+
case EAGAIN: return DRMP3_UNAVAILABLE;
|
2984
|
+
#endif
|
2985
|
+
#ifdef ENOMEM
|
2986
|
+
case ENOMEM: return DRMP3_OUT_OF_MEMORY;
|
2987
|
+
#endif
|
2988
|
+
#ifdef EACCES
|
2989
|
+
case EACCES: return DRMP3_ACCESS_DENIED;
|
2990
|
+
#endif
|
2991
|
+
#ifdef EFAULT
|
2992
|
+
case EFAULT: return DRMP3_BAD_ADDRESS;
|
2993
|
+
#endif
|
2994
|
+
#ifdef ENOTBLK
|
2995
|
+
case ENOTBLK: return DRMP3_ERROR;
|
2996
|
+
#endif
|
2997
|
+
#ifdef EBUSY
|
2998
|
+
case EBUSY: return DRMP3_BUSY;
|
2999
|
+
#endif
|
3000
|
+
#ifdef EEXIST
|
3001
|
+
case EEXIST: return DRMP3_ALREADY_EXISTS;
|
3002
|
+
#endif
|
3003
|
+
#ifdef EXDEV
|
3004
|
+
case EXDEV: return DRMP3_ERROR;
|
3005
|
+
#endif
|
3006
|
+
#ifdef ENODEV
|
3007
|
+
case ENODEV: return DRMP3_DOES_NOT_EXIST;
|
3008
|
+
#endif
|
3009
|
+
#ifdef ENOTDIR
|
3010
|
+
case ENOTDIR: return DRMP3_NOT_DIRECTORY;
|
3011
|
+
#endif
|
3012
|
+
#ifdef EISDIR
|
3013
|
+
case EISDIR: return DRMP3_IS_DIRECTORY;
|
3014
|
+
#endif
|
3015
|
+
#ifdef EINVAL
|
3016
|
+
case EINVAL: return DRMP3_INVALID_ARGS;
|
3017
|
+
#endif
|
3018
|
+
#ifdef ENFILE
|
3019
|
+
case ENFILE: return DRMP3_TOO_MANY_OPEN_FILES;
|
3020
|
+
#endif
|
3021
|
+
#ifdef EMFILE
|
3022
|
+
case EMFILE: return DRMP3_TOO_MANY_OPEN_FILES;
|
3023
|
+
#endif
|
3024
|
+
#ifdef ENOTTY
|
3025
|
+
case ENOTTY: return DRMP3_INVALID_OPERATION;
|
3026
|
+
#endif
|
3027
|
+
#ifdef ETXTBSY
|
3028
|
+
case ETXTBSY: return DRMP3_BUSY;
|
3029
|
+
#endif
|
3030
|
+
#ifdef EFBIG
|
3031
|
+
case EFBIG: return DRMP3_TOO_BIG;
|
3032
|
+
#endif
|
3033
|
+
#ifdef ENOSPC
|
3034
|
+
case ENOSPC: return DRMP3_NO_SPACE;
|
3035
|
+
#endif
|
3036
|
+
#ifdef ESPIPE
|
3037
|
+
case ESPIPE: return DRMP3_BAD_SEEK;
|
3038
|
+
#endif
|
3039
|
+
#ifdef EROFS
|
3040
|
+
case EROFS: return DRMP3_ACCESS_DENIED;
|
3041
|
+
#endif
|
3042
|
+
#ifdef EMLINK
|
3043
|
+
case EMLINK: return DRMP3_TOO_MANY_LINKS;
|
3044
|
+
#endif
|
3045
|
+
#ifdef EPIPE
|
3046
|
+
case EPIPE: return DRMP3_BAD_PIPE;
|
3047
|
+
#endif
|
3048
|
+
#ifdef EDOM
|
3049
|
+
case EDOM: return DRMP3_OUT_OF_RANGE;
|
3050
|
+
#endif
|
3051
|
+
#ifdef ERANGE
|
3052
|
+
case ERANGE: return DRMP3_OUT_OF_RANGE;
|
3053
|
+
#endif
|
3054
|
+
#ifdef EDEADLK
|
3055
|
+
case EDEADLK: return DRMP3_DEADLOCK;
|
3056
|
+
#endif
|
3057
|
+
#ifdef ENAMETOOLONG
|
3058
|
+
case ENAMETOOLONG: return DRMP3_PATH_TOO_LONG;
|
3059
|
+
#endif
|
3060
|
+
#ifdef ENOLCK
|
3061
|
+
case ENOLCK: return DRMP3_ERROR;
|
3062
|
+
#endif
|
3063
|
+
#ifdef ENOSYS
|
3064
|
+
case ENOSYS: return DRMP3_NOT_IMPLEMENTED;
|
3065
|
+
#endif
|
3066
|
+
#ifdef ENOTEMPTY
|
3067
|
+
case ENOTEMPTY: return DRMP3_DIRECTORY_NOT_EMPTY;
|
3068
|
+
#endif
|
3069
|
+
#ifdef ELOOP
|
3070
|
+
case ELOOP: return DRMP3_TOO_MANY_LINKS;
|
3071
|
+
#endif
|
3072
|
+
#ifdef ENOMSG
|
3073
|
+
case ENOMSG: return DRMP3_NO_MESSAGE;
|
3074
|
+
#endif
|
3075
|
+
#ifdef EIDRM
|
3076
|
+
case EIDRM: return DRMP3_ERROR;
|
3077
|
+
#endif
|
3078
|
+
#ifdef ECHRNG
|
3079
|
+
case ECHRNG: return DRMP3_ERROR;
|
3080
|
+
#endif
|
3081
|
+
#ifdef EL2NSYNC
|
3082
|
+
case EL2NSYNC: return DRMP3_ERROR;
|
3083
|
+
#endif
|
3084
|
+
#ifdef EL3HLT
|
3085
|
+
case EL3HLT: return DRMP3_ERROR;
|
3086
|
+
#endif
|
3087
|
+
#ifdef EL3RST
|
3088
|
+
case EL3RST: return DRMP3_ERROR;
|
3089
|
+
#endif
|
3090
|
+
#ifdef ELNRNG
|
3091
|
+
case ELNRNG: return DRMP3_OUT_OF_RANGE;
|
3092
|
+
#endif
|
3093
|
+
#ifdef EUNATCH
|
3094
|
+
case EUNATCH: return DRMP3_ERROR;
|
3095
|
+
#endif
|
3096
|
+
#ifdef ENOCSI
|
3097
|
+
case ENOCSI: return DRMP3_ERROR;
|
3098
|
+
#endif
|
3099
|
+
#ifdef EL2HLT
|
3100
|
+
case EL2HLT: return DRMP3_ERROR;
|
3101
|
+
#endif
|
3102
|
+
#ifdef EBADE
|
3103
|
+
case EBADE: return DRMP3_ERROR;
|
3104
|
+
#endif
|
3105
|
+
#ifdef EBADR
|
3106
|
+
case EBADR: return DRMP3_ERROR;
|
3107
|
+
#endif
|
3108
|
+
#ifdef EXFULL
|
3109
|
+
case EXFULL: return DRMP3_ERROR;
|
3110
|
+
#endif
|
3111
|
+
#ifdef ENOANO
|
3112
|
+
case ENOANO: return DRMP3_ERROR;
|
3113
|
+
#endif
|
3114
|
+
#ifdef EBADRQC
|
3115
|
+
case EBADRQC: return DRMP3_ERROR;
|
3116
|
+
#endif
|
3117
|
+
#ifdef EBADSLT
|
3118
|
+
case EBADSLT: return DRMP3_ERROR;
|
3119
|
+
#endif
|
3120
|
+
#ifdef EBFONT
|
3121
|
+
case EBFONT: return DRMP3_INVALID_FILE;
|
3122
|
+
#endif
|
3123
|
+
#ifdef ENOSTR
|
3124
|
+
case ENOSTR: return DRMP3_ERROR;
|
3125
|
+
#endif
|
3126
|
+
#ifdef ENODATA
|
3127
|
+
case ENODATA: return DRMP3_NO_DATA_AVAILABLE;
|
3128
|
+
#endif
|
3129
|
+
#ifdef ETIME
|
3130
|
+
case ETIME: return DRMP3_TIMEOUT;
|
3131
|
+
#endif
|
3132
|
+
#ifdef ENOSR
|
3133
|
+
case ENOSR: return DRMP3_NO_DATA_AVAILABLE;
|
3134
|
+
#endif
|
3135
|
+
#ifdef ENONET
|
3136
|
+
case ENONET: return DRMP3_NO_NETWORK;
|
3137
|
+
#endif
|
3138
|
+
#ifdef ENOPKG
|
3139
|
+
case ENOPKG: return DRMP3_ERROR;
|
3140
|
+
#endif
|
3141
|
+
#ifdef EREMOTE
|
3142
|
+
case EREMOTE: return DRMP3_ERROR;
|
3143
|
+
#endif
|
3144
|
+
#ifdef ENOLINK
|
3145
|
+
case ENOLINK: return DRMP3_ERROR;
|
3146
|
+
#endif
|
3147
|
+
#ifdef EADV
|
3148
|
+
case EADV: return DRMP3_ERROR;
|
3149
|
+
#endif
|
3150
|
+
#ifdef ESRMNT
|
3151
|
+
case ESRMNT: return DRMP3_ERROR;
|
3152
|
+
#endif
|
3153
|
+
#ifdef ECOMM
|
3154
|
+
case ECOMM: return DRMP3_ERROR;
|
3155
|
+
#endif
|
3156
|
+
#ifdef EPROTO
|
3157
|
+
case EPROTO: return DRMP3_ERROR;
|
3158
|
+
#endif
|
3159
|
+
#ifdef EMULTIHOP
|
3160
|
+
case EMULTIHOP: return DRMP3_ERROR;
|
3161
|
+
#endif
|
3162
|
+
#ifdef EDOTDOT
|
3163
|
+
case EDOTDOT: return DRMP3_ERROR;
|
3164
|
+
#endif
|
3165
|
+
#ifdef EBADMSG
|
3166
|
+
case EBADMSG: return DRMP3_BAD_MESSAGE;
|
3167
|
+
#endif
|
3168
|
+
#ifdef EOVERFLOW
|
3169
|
+
case EOVERFLOW: return DRMP3_TOO_BIG;
|
3170
|
+
#endif
|
3171
|
+
#ifdef ENOTUNIQ
|
3172
|
+
case ENOTUNIQ: return DRMP3_NOT_UNIQUE;
|
3173
|
+
#endif
|
3174
|
+
#ifdef EBADFD
|
3175
|
+
case EBADFD: return DRMP3_ERROR;
|
3176
|
+
#endif
|
3177
|
+
#ifdef EREMCHG
|
3178
|
+
case EREMCHG: return DRMP3_ERROR;
|
3179
|
+
#endif
|
3180
|
+
#ifdef ELIBACC
|
3181
|
+
case ELIBACC: return DRMP3_ACCESS_DENIED;
|
3182
|
+
#endif
|
3183
|
+
#ifdef ELIBBAD
|
3184
|
+
case ELIBBAD: return DRMP3_INVALID_FILE;
|
3185
|
+
#endif
|
3186
|
+
#ifdef ELIBSCN
|
3187
|
+
case ELIBSCN: return DRMP3_INVALID_FILE;
|
3188
|
+
#endif
|
3189
|
+
#ifdef ELIBMAX
|
3190
|
+
case ELIBMAX: return DRMP3_ERROR;
|
3191
|
+
#endif
|
3192
|
+
#ifdef ELIBEXEC
|
3193
|
+
case ELIBEXEC: return DRMP3_ERROR;
|
3194
|
+
#endif
|
3195
|
+
#ifdef EILSEQ
|
3196
|
+
case EILSEQ: return DRMP3_INVALID_DATA;
|
3197
|
+
#endif
|
3198
|
+
#ifdef ERESTART
|
3199
|
+
case ERESTART: return DRMP3_ERROR;
|
3200
|
+
#endif
|
3201
|
+
#ifdef ESTRPIPE
|
3202
|
+
case ESTRPIPE: return DRMP3_ERROR;
|
3203
|
+
#endif
|
3204
|
+
#ifdef EUSERS
|
3205
|
+
case EUSERS: return DRMP3_ERROR;
|
3206
|
+
#endif
|
3207
|
+
#ifdef ENOTSOCK
|
3208
|
+
case ENOTSOCK: return DRMP3_NOT_SOCKET;
|
3209
|
+
#endif
|
3210
|
+
#ifdef EDESTADDRREQ
|
3211
|
+
case EDESTADDRREQ: return DRMP3_NO_ADDRESS;
|
3212
|
+
#endif
|
3213
|
+
#ifdef EMSGSIZE
|
3214
|
+
case EMSGSIZE: return DRMP3_TOO_BIG;
|
3215
|
+
#endif
|
3216
|
+
#ifdef EPROTOTYPE
|
3217
|
+
case EPROTOTYPE: return DRMP3_BAD_PROTOCOL;
|
3218
|
+
#endif
|
3219
|
+
#ifdef ENOPROTOOPT
|
3220
|
+
case ENOPROTOOPT: return DRMP3_PROTOCOL_UNAVAILABLE;
|
3221
|
+
#endif
|
3222
|
+
#ifdef EPROTONOSUPPORT
|
3223
|
+
case EPROTONOSUPPORT: return DRMP3_PROTOCOL_NOT_SUPPORTED;
|
3224
|
+
#endif
|
3225
|
+
#ifdef ESOCKTNOSUPPORT
|
3226
|
+
case ESOCKTNOSUPPORT: return DRMP3_SOCKET_NOT_SUPPORTED;
|
3227
|
+
#endif
|
3228
|
+
#ifdef EOPNOTSUPP
|
3229
|
+
case EOPNOTSUPP: return DRMP3_INVALID_OPERATION;
|
3230
|
+
#endif
|
3231
|
+
#ifdef EPFNOSUPPORT
|
3232
|
+
case EPFNOSUPPORT: return DRMP3_PROTOCOL_FAMILY_NOT_SUPPORTED;
|
3233
|
+
#endif
|
3234
|
+
#ifdef EAFNOSUPPORT
|
3235
|
+
case EAFNOSUPPORT: return DRMP3_ADDRESS_FAMILY_NOT_SUPPORTED;
|
3236
|
+
#endif
|
3237
|
+
#ifdef EADDRINUSE
|
3238
|
+
case EADDRINUSE: return DRMP3_ALREADY_IN_USE;
|
3239
|
+
#endif
|
3240
|
+
#ifdef EADDRNOTAVAIL
|
3241
|
+
case EADDRNOTAVAIL: return DRMP3_ERROR;
|
3242
|
+
#endif
|
3243
|
+
#ifdef ENETDOWN
|
3244
|
+
case ENETDOWN: return DRMP3_NO_NETWORK;
|
3245
|
+
#endif
|
3246
|
+
#ifdef ENETUNREACH
|
3247
|
+
case ENETUNREACH: return DRMP3_NO_NETWORK;
|
3248
|
+
#endif
|
3249
|
+
#ifdef ENETRESET
|
3250
|
+
case ENETRESET: return DRMP3_NO_NETWORK;
|
3251
|
+
#endif
|
3252
|
+
#ifdef ECONNABORTED
|
3253
|
+
case ECONNABORTED: return DRMP3_NO_NETWORK;
|
3254
|
+
#endif
|
3255
|
+
#ifdef ECONNRESET
|
3256
|
+
case ECONNRESET: return DRMP3_CONNECTION_RESET;
|
3257
|
+
#endif
|
3258
|
+
#ifdef ENOBUFS
|
3259
|
+
case ENOBUFS: return DRMP3_NO_SPACE;
|
3260
|
+
#endif
|
3261
|
+
#ifdef EISCONN
|
3262
|
+
case EISCONN: return DRMP3_ALREADY_CONNECTED;
|
3263
|
+
#endif
|
3264
|
+
#ifdef ENOTCONN
|
3265
|
+
case ENOTCONN: return DRMP3_NOT_CONNECTED;
|
3266
|
+
#endif
|
3267
|
+
#ifdef ESHUTDOWN
|
3268
|
+
case ESHUTDOWN: return DRMP3_ERROR;
|
3269
|
+
#endif
|
3270
|
+
#ifdef ETOOMANYREFS
|
3271
|
+
case ETOOMANYREFS: return DRMP3_ERROR;
|
3272
|
+
#endif
|
3273
|
+
#ifdef ETIMEDOUT
|
3274
|
+
case ETIMEDOUT: return DRMP3_TIMEOUT;
|
3275
|
+
#endif
|
3276
|
+
#ifdef ECONNREFUSED
|
3277
|
+
case ECONNREFUSED: return DRMP3_CONNECTION_REFUSED;
|
3278
|
+
#endif
|
3279
|
+
#ifdef EHOSTDOWN
|
3280
|
+
case EHOSTDOWN: return DRMP3_NO_HOST;
|
3281
|
+
#endif
|
3282
|
+
#ifdef EHOSTUNREACH
|
3283
|
+
case EHOSTUNREACH: return DRMP3_NO_HOST;
|
3284
|
+
#endif
|
3285
|
+
#ifdef EALREADY
|
3286
|
+
case EALREADY: return DRMP3_IN_PROGRESS;
|
3287
|
+
#endif
|
3288
|
+
#ifdef EINPROGRESS
|
3289
|
+
case EINPROGRESS: return DRMP3_IN_PROGRESS;
|
3290
|
+
#endif
|
3291
|
+
#ifdef ESTALE
|
3292
|
+
case ESTALE: return DRMP3_INVALID_FILE;
|
3293
|
+
#endif
|
3294
|
+
#ifdef EUCLEAN
|
3295
|
+
case EUCLEAN: return DRMP3_ERROR;
|
3296
|
+
#endif
|
3297
|
+
#ifdef ENOTNAM
|
3298
|
+
case ENOTNAM: return DRMP3_ERROR;
|
3299
|
+
#endif
|
3300
|
+
#ifdef ENAVAIL
|
3301
|
+
case ENAVAIL: return DRMP3_ERROR;
|
3302
|
+
#endif
|
3303
|
+
#ifdef EISNAM
|
3304
|
+
case EISNAM: return DRMP3_ERROR;
|
3305
|
+
#endif
|
3306
|
+
#ifdef EREMOTEIO
|
3307
|
+
case EREMOTEIO: return DRMP3_IO_ERROR;
|
3308
|
+
#endif
|
3309
|
+
#ifdef EDQUOT
|
3310
|
+
case EDQUOT: return DRMP3_NO_SPACE;
|
3311
|
+
#endif
|
3312
|
+
#ifdef ENOMEDIUM
|
3313
|
+
case ENOMEDIUM: return DRMP3_DOES_NOT_EXIST;
|
3314
|
+
#endif
|
3315
|
+
#ifdef EMEDIUMTYPE
|
3316
|
+
case EMEDIUMTYPE: return DRMP3_ERROR;
|
3317
|
+
#endif
|
3318
|
+
#ifdef ECANCELED
|
3319
|
+
case ECANCELED: return DRMP3_CANCELLED;
|
3320
|
+
#endif
|
3321
|
+
#ifdef ENOKEY
|
3322
|
+
case ENOKEY: return DRMP3_ERROR;
|
3323
|
+
#endif
|
3324
|
+
#ifdef EKEYEXPIRED
|
3325
|
+
case EKEYEXPIRED: return DRMP3_ERROR;
|
3326
|
+
#endif
|
3327
|
+
#ifdef EKEYREVOKED
|
3328
|
+
case EKEYREVOKED: return DRMP3_ERROR;
|
3329
|
+
#endif
|
3330
|
+
#ifdef EKEYREJECTED
|
3331
|
+
case EKEYREJECTED: return DRMP3_ERROR;
|
3332
|
+
#endif
|
3333
|
+
#ifdef EOWNERDEAD
|
3334
|
+
case EOWNERDEAD: return DRMP3_ERROR;
|
3335
|
+
#endif
|
3336
|
+
#ifdef ENOTRECOVERABLE
|
3337
|
+
case ENOTRECOVERABLE: return DRMP3_ERROR;
|
3338
|
+
#endif
|
3339
|
+
#ifdef ERFKILL
|
3340
|
+
case ERFKILL: return DRMP3_ERROR;
|
3341
|
+
#endif
|
3342
|
+
#ifdef EHWPOISON
|
3343
|
+
case EHWPOISON: return DRMP3_ERROR;
|
3344
|
+
#endif
|
3345
|
+
default: return DRMP3_ERROR;
|
3346
|
+
}
|
3347
|
+
}
|
3348
|
+
|
3349
|
+
static drmp3_result drmp3_fopen(FILE** ppFile, const char* pFilePath, const char* pOpenMode)
|
3350
|
+
{
|
3351
|
+
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
3352
|
+
errno_t err;
|
3353
|
+
#endif
|
3354
|
+
|
3355
|
+
if (ppFile != NULL) {
|
3356
|
+
*ppFile = NULL; /* Safety. */
|
3357
|
+
}
|
3358
|
+
|
3359
|
+
if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
|
3360
|
+
return DRMP3_INVALID_ARGS;
|
3361
|
+
}
|
3362
|
+
|
3363
|
+
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
3364
|
+
err = fopen_s(ppFile, pFilePath, pOpenMode);
|
3365
|
+
if (err != 0) {
|
3366
|
+
return drmp3_result_from_errno(err);
|
3367
|
+
}
|
3368
|
+
#else
|
3369
|
+
#if defined(_WIN32) || defined(__APPLE__)
|
3370
|
+
*ppFile = fopen(pFilePath, pOpenMode);
|
3371
|
+
#else
|
3372
|
+
#if defined(_FILE_OFFSET_BITS) && _FILE_OFFSET_BITS == 64 && defined(_LARGEFILE64_SOURCE)
|
3373
|
+
*ppFile = fopen64(pFilePath, pOpenMode);
|
3374
|
+
#else
|
3375
|
+
*ppFile = fopen(pFilePath, pOpenMode);
|
3376
|
+
#endif
|
3377
|
+
#endif
|
3378
|
+
if (*ppFile == NULL) {
|
3379
|
+
drmp3_result result = drmp3_result_from_errno(errno);
|
3380
|
+
if (result == DRMP3_SUCCESS) {
|
3381
|
+
result = DRMP3_ERROR; /* Just a safety check to make sure we never ever return success when pFile == NULL. */
|
3382
|
+
}
|
3383
|
+
|
3384
|
+
return result;
|
3385
|
+
}
|
3386
|
+
#endif
|
3387
|
+
|
3388
|
+
return DRMP3_SUCCESS;
|
3389
|
+
}
|
3390
|
+
|
3391
|
+
/*
|
3392
|
+
_wfopen() isn't always available in all compilation environments.
|
3393
|
+
|
3394
|
+
* Windows only.
|
3395
|
+
* MSVC seems to support it universally as far back as VC6 from what I can tell (haven't checked further back).
|
3396
|
+
* MinGW-64 (both 32- and 64-bit) seems to support it.
|
3397
|
+
* MinGW wraps it in !defined(__STRICT_ANSI__).
|
3398
|
+
* OpenWatcom wraps it in !defined(_NO_EXT_KEYS).
|
3399
|
+
|
3400
|
+
This can be reviewed as compatibility issues arise. The preference is to use _wfopen_s() and _wfopen() as opposed to the wcsrtombs()
|
3401
|
+
fallback, so if you notice your compiler not detecting this properly I'm happy to look at adding support.
|
3402
|
+
*/
|
3403
|
+
#if defined(_WIN32)
|
3404
|
+
#if defined(_MSC_VER) || defined(__MINGW64__) || (!defined(__STRICT_ANSI__) && !defined(_NO_EXT_KEYS))
|
3405
|
+
#define DRMP3_HAS_WFOPEN
|
3406
|
+
#endif
|
3407
|
+
#endif
|
3408
|
+
|
3409
|
+
static drmp3_result drmp3_wfopen(FILE** ppFile, const wchar_t* pFilePath, const wchar_t* pOpenMode, const drmp3_allocation_callbacks* pAllocationCallbacks)
|
3410
|
+
{
|
3411
|
+
if (ppFile != NULL) {
|
3412
|
+
*ppFile = NULL; /* Safety. */
|
3413
|
+
}
|
3414
|
+
|
3415
|
+
if (pFilePath == NULL || pOpenMode == NULL || ppFile == NULL) {
|
3416
|
+
return DRMP3_INVALID_ARGS;
|
3417
|
+
}
|
3418
|
+
|
3419
|
+
#if defined(DRMP3_HAS_WFOPEN)
|
3420
|
+
{
|
3421
|
+
/* Use _wfopen() on Windows. */
|
3422
|
+
#if defined(_MSC_VER) && _MSC_VER >= 1400
|
3423
|
+
errno_t err = _wfopen_s(ppFile, pFilePath, pOpenMode);
|
3424
|
+
if (err != 0) {
|
3425
|
+
return drmp3_result_from_errno(err);
|
3426
|
+
}
|
3427
|
+
#else
|
3428
|
+
*ppFile = _wfopen(pFilePath, pOpenMode);
|
3429
|
+
if (*ppFile == NULL) {
|
3430
|
+
return drmp3_result_from_errno(errno);
|
3431
|
+
}
|
3432
|
+
#endif
|
3433
|
+
(void)pAllocationCallbacks;
|
3434
|
+
}
|
3435
|
+
#else
|
3436
|
+
/*
|
3437
|
+
Use fopen() on anything other than Windows. Requires a conversion. This is annoying because fopen() is locale specific. The only real way I can
|
3438
|
+
think of to do this is with wcsrtombs(). Note that wcstombs() is apparently not thread-safe because it uses a static global mbstate_t object for
|
3439
|
+
maintaining state. I've checked this with -std=c89 and it works, but if somebody get's a compiler error I'll look into improving compatibility.
|
3440
|
+
*/
|
3441
|
+
{
|
3442
|
+
mbstate_t mbs;
|
3443
|
+
size_t lenMB;
|
3444
|
+
const wchar_t* pFilePathTemp = pFilePath;
|
3445
|
+
char* pFilePathMB = NULL;
|
3446
|
+
char pOpenModeMB[32] = {0};
|
3447
|
+
|
3448
|
+
/* Get the length first. */
|
3449
|
+
DRMP3_ZERO_OBJECT(&mbs);
|
3450
|
+
lenMB = wcsrtombs(NULL, &pFilePathTemp, 0, &mbs);
|
3451
|
+
if (lenMB == (size_t)-1) {
|
3452
|
+
return drmp3_result_from_errno(errno);
|
3453
|
+
}
|
3454
|
+
|
3455
|
+
pFilePathMB = (char*)drmp3__malloc_from_callbacks(lenMB + 1, pAllocationCallbacks);
|
3456
|
+
if (pFilePathMB == NULL) {
|
3457
|
+
return DRMP3_OUT_OF_MEMORY;
|
3458
|
+
}
|
3459
|
+
|
3460
|
+
pFilePathTemp = pFilePath;
|
3461
|
+
DRMP3_ZERO_OBJECT(&mbs);
|
3462
|
+
wcsrtombs(pFilePathMB, &pFilePathTemp, lenMB + 1, &mbs);
|
3463
|
+
|
3464
|
+
/* The open mode should always consist of ASCII characters so we should be able to do a trivial conversion. */
|
3465
|
+
{
|
3466
|
+
size_t i = 0;
|
3467
|
+
for (;;) {
|
3468
|
+
if (pOpenMode[i] == 0) {
|
3469
|
+
pOpenModeMB[i] = '\0';
|
3470
|
+
break;
|
3471
|
+
}
|
3472
|
+
|
3473
|
+
pOpenModeMB[i] = (char)pOpenMode[i];
|
3474
|
+
i += 1;
|
3475
|
+
}
|
3476
|
+
}
|
3477
|
+
|
3478
|
+
*ppFile = fopen(pFilePathMB, pOpenModeMB);
|
3479
|
+
|
3480
|
+
drmp3__free_from_callbacks(pFilePathMB, pAllocationCallbacks);
|
3481
|
+
}
|
3482
|
+
|
3483
|
+
if (*ppFile == NULL) {
|
3484
|
+
return DRMP3_ERROR;
|
3485
|
+
}
|
3486
|
+
#endif
|
3487
|
+
|
3488
|
+
return DRMP3_SUCCESS;
|
3489
|
+
}
|
3490
|
+
|
3491
|
+
|
3492
|
+
|
3493
|
+
static size_t drmp3__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead)
|
3494
|
+
{
|
3495
|
+
return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData);
|
3496
|
+
}
|
3497
|
+
|
3498
|
+
static drmp3_bool32 drmp3__on_seek_stdio(void* pUserData, int offset, drmp3_seek_origin origin)
|
3499
|
+
{
|
3500
|
+
return fseek((FILE*)pUserData, offset, (origin == drmp3_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0;
|
3501
|
+
}
|
3502
|
+
|
3503
|
+
DRMP3_API drmp3_bool32 drmp3_init_file(drmp3* pMP3, const char* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks)
|
3504
|
+
{
|
3505
|
+
drmp3_bool32 result;
|
3506
|
+
FILE* pFile;
|
3507
|
+
|
3508
|
+
if (drmp3_fopen(&pFile, pFilePath, "rb") != DRMP3_SUCCESS) {
|
3509
|
+
return DRMP3_FALSE;
|
3510
|
+
}
|
3511
|
+
|
3512
|
+
result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
|
3513
|
+
if (result != DRMP3_TRUE) {
|
3514
|
+
fclose(pFile);
|
3515
|
+
return result;
|
3516
|
+
}
|
3517
|
+
|
3518
|
+
return DRMP3_TRUE;
|
3519
|
+
}
|
3520
|
+
|
3521
|
+
DRMP3_API drmp3_bool32 drmp3_init_file_w(drmp3* pMP3, const wchar_t* pFilePath, const drmp3_allocation_callbacks* pAllocationCallbacks)
|
3522
|
+
{
|
3523
|
+
drmp3_bool32 result;
|
3524
|
+
FILE* pFile;
|
3525
|
+
|
3526
|
+
if (drmp3_wfopen(&pFile, pFilePath, L"rb", pAllocationCallbacks) != DRMP3_SUCCESS) {
|
3527
|
+
return DRMP3_FALSE;
|
3528
|
+
}
|
3529
|
+
|
3530
|
+
result = drmp3_init(pMP3, drmp3__on_read_stdio, drmp3__on_seek_stdio, (void*)pFile, pAllocationCallbacks);
|
3531
|
+
if (result != DRMP3_TRUE) {
|
3532
|
+
fclose(pFile);
|
3533
|
+
return result;
|
3534
|
+
}
|
3535
|
+
|
3536
|
+
return DRMP3_TRUE;
|
3537
|
+
}
|
3538
|
+
#endif
|
3539
|
+
|
3540
|
+
DRMP3_API void drmp3_uninit(drmp3* pMP3)
|
3541
|
+
{
|
3542
|
+
if (pMP3 == NULL) {
|
3543
|
+
return;
|
3544
|
+
}
|
3545
|
+
|
3546
|
+
#ifndef DR_MP3_NO_STDIO
|
3547
|
+
if (pMP3->onRead == drmp3__on_read_stdio) {
|
3548
|
+
FILE* pFile = (FILE*)pMP3->pUserData;
|
3549
|
+
if (pFile != NULL) {
|
3550
|
+
fclose(pFile);
|
3551
|
+
pMP3->pUserData = NULL; /* Make sure the file handle is cleared to NULL to we don't attempt to close it a second time. */
|
3552
|
+
}
|
3553
|
+
}
|
3554
|
+
#endif
|
3555
|
+
|
3556
|
+
drmp3__free_from_callbacks(pMP3->pData, &pMP3->allocationCallbacks);
|
3557
|
+
}
|
3558
|
+
|
3559
|
+
#if defined(DR_MP3_FLOAT_OUTPUT)
|
3560
|
+
static void drmp3_f32_to_s16(drmp3_int16* dst, const float* src, drmp3_uint64 sampleCount)
|
3561
|
+
{
|
3562
|
+
drmp3_uint64 i;
|
3563
|
+
drmp3_uint64 i4;
|
3564
|
+
drmp3_uint64 sampleCount4;
|
3565
|
+
|
3566
|
+
/* Unrolled. */
|
3567
|
+
i = 0;
|
3568
|
+
sampleCount4 = sampleCount >> 2;
|
3569
|
+
for (i4 = 0; i4 < sampleCount4; i4 += 1) {
|
3570
|
+
float x0 = src[i+0];
|
3571
|
+
float x1 = src[i+1];
|
3572
|
+
float x2 = src[i+2];
|
3573
|
+
float x3 = src[i+3];
|
3574
|
+
|
3575
|
+
x0 = ((x0 < -1) ? -1 : ((x0 > 1) ? 1 : x0));
|
3576
|
+
x1 = ((x1 < -1) ? -1 : ((x1 > 1) ? 1 : x1));
|
3577
|
+
x2 = ((x2 < -1) ? -1 : ((x2 > 1) ? 1 : x2));
|
3578
|
+
x3 = ((x3 < -1) ? -1 : ((x3 > 1) ? 1 : x3));
|
3579
|
+
|
3580
|
+
x0 = x0 * 32767.0f;
|
3581
|
+
x1 = x1 * 32767.0f;
|
3582
|
+
x2 = x2 * 32767.0f;
|
3583
|
+
x3 = x3 * 32767.0f;
|
3584
|
+
|
3585
|
+
dst[i+0] = (drmp3_int16)x0;
|
3586
|
+
dst[i+1] = (drmp3_int16)x1;
|
3587
|
+
dst[i+2] = (drmp3_int16)x2;
|
3588
|
+
dst[i+3] = (drmp3_int16)x3;
|
3589
|
+
|
3590
|
+
i += 4;
|
3591
|
+
}
|
3592
|
+
|
3593
|
+
/* Leftover. */
|
3594
|
+
for (; i < sampleCount; i += 1) {
|
3595
|
+
float x = src[i];
|
3596
|
+
x = ((x < -1) ? -1 : ((x > 1) ? 1 : x)); /* clip */
|
3597
|
+
x = x * 32767.0f; /* -1..1 to -32767..32767 */
|
3598
|
+
|
3599
|
+
dst[i] = (drmp3_int16)x;
|
3600
|
+
}
|
3601
|
+
}
|
3602
|
+
#endif
|
3603
|
+
|
3604
|
+
#if !defined(DR_MP3_FLOAT_OUTPUT)
|
3605
|
+
static void drmp3_s16_to_f32(float* dst, const drmp3_int16* src, drmp3_uint64 sampleCount)
|
3606
|
+
{
|
3607
|
+
drmp3_uint64 i;
|
3608
|
+
for (i = 0; i < sampleCount; i += 1) {
|
3609
|
+
float x = (float)src[i];
|
3610
|
+
x = x * 0.000030517578125f; /* -32768..32767 to -1..0.999969482421875 */
|
3611
|
+
dst[i] = x;
|
3612
|
+
}
|
3613
|
+
}
|
3614
|
+
#endif
|
3615
|
+
|
3616
|
+
|
3617
|
+
static drmp3_uint64 drmp3_read_pcm_frames_raw(drmp3* pMP3, drmp3_uint64 framesToRead, void* pBufferOut)
|
3618
|
+
{
|
3619
|
+
drmp3_uint64 totalFramesRead = 0;
|
3620
|
+
|
3621
|
+
DRMP3_ASSERT(pMP3 != NULL);
|
3622
|
+
DRMP3_ASSERT(pMP3->onRead != NULL);
|
3623
|
+
|
3624
|
+
while (framesToRead > 0) {
|
3625
|
+
drmp3_uint32 framesToConsume = (drmp3_uint32)DRMP3_MIN(pMP3->pcmFramesRemainingInMP3Frame, framesToRead);
|
3626
|
+
if (pBufferOut != NULL) {
|
3627
|
+
#if defined(DR_MP3_FLOAT_OUTPUT)
|
3628
|
+
/* f32 */
|
3629
|
+
float* pFramesOutF32 = (float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalFramesRead * pMP3->channels);
|
3630
|
+
float* pFramesInF32 = (float*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(float) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
|
3631
|
+
DRMP3_COPY_MEMORY(pFramesOutF32, pFramesInF32, sizeof(float) * framesToConsume * pMP3->channels);
|
3632
|
+
#else
|
3633
|
+
/* s16 */
|
3634
|
+
drmp3_int16* pFramesOutS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalFramesRead * pMP3->channels);
|
3635
|
+
drmp3_int16* pFramesInS16 = (drmp3_int16*)DRMP3_OFFSET_PTR(&pMP3->pcmFrames[0], sizeof(drmp3_int16) * pMP3->pcmFramesConsumedInMP3Frame * pMP3->mp3FrameChannels);
|
3636
|
+
DRMP3_COPY_MEMORY(pFramesOutS16, pFramesInS16, sizeof(drmp3_int16) * framesToConsume * pMP3->channels);
|
3637
|
+
#endif
|
3638
|
+
}
|
3639
|
+
|
3640
|
+
pMP3->currentPCMFrame += framesToConsume;
|
3641
|
+
pMP3->pcmFramesConsumedInMP3Frame += framesToConsume;
|
3642
|
+
pMP3->pcmFramesRemainingInMP3Frame -= framesToConsume;
|
3643
|
+
totalFramesRead += framesToConsume;
|
3644
|
+
framesToRead -= framesToConsume;
|
3645
|
+
|
3646
|
+
if (framesToRead == 0) {
|
3647
|
+
break;
|
3648
|
+
}
|
3649
|
+
|
3650
|
+
DRMP3_ASSERT(pMP3->pcmFramesRemainingInMP3Frame == 0);
|
3651
|
+
|
3652
|
+
/*
|
3653
|
+
At this point we have exhausted our in-memory buffer so we need to re-fill. Note that the sample rate may have changed
|
3654
|
+
at this point which means we'll also need to update our sample rate conversion pipeline.
|
3655
|
+
*/
|
3656
|
+
if (drmp3_decode_next_frame(pMP3) == 0) {
|
3657
|
+
break;
|
3658
|
+
}
|
3659
|
+
}
|
3660
|
+
|
3661
|
+
return totalFramesRead;
|
3662
|
+
}
|
3663
|
+
|
3664
|
+
|
3665
|
+
DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_f32(drmp3* pMP3, drmp3_uint64 framesToRead, float* pBufferOut)
|
3666
|
+
{
|
3667
|
+
if (pMP3 == NULL || pMP3->onRead == NULL) {
|
3668
|
+
return 0;
|
3669
|
+
}
|
3670
|
+
|
3671
|
+
#if defined(DR_MP3_FLOAT_OUTPUT)
|
3672
|
+
/* Fast path. No conversion required. */
|
3673
|
+
return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
|
3674
|
+
#else
|
3675
|
+
/* Slow path. Convert from s16 to f32. */
|
3676
|
+
{
|
3677
|
+
drmp3_int16 pTempS16[8192];
|
3678
|
+
drmp3_uint64 totalPCMFramesRead = 0;
|
3679
|
+
|
3680
|
+
while (totalPCMFramesRead < framesToRead) {
|
3681
|
+
drmp3_uint64 framesJustRead;
|
3682
|
+
drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
|
3683
|
+
drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempS16) / pMP3->channels;
|
3684
|
+
if (framesToReadNow > framesRemaining) {
|
3685
|
+
framesToReadNow = framesRemaining;
|
3686
|
+
}
|
3687
|
+
|
3688
|
+
framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempS16);
|
3689
|
+
if (framesJustRead == 0) {
|
3690
|
+
break;
|
3691
|
+
}
|
3692
|
+
|
3693
|
+
drmp3_s16_to_f32((float*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(float) * totalPCMFramesRead * pMP3->channels), pTempS16, framesJustRead * pMP3->channels);
|
3694
|
+
totalPCMFramesRead += framesJustRead;
|
3695
|
+
}
|
3696
|
+
|
3697
|
+
return totalPCMFramesRead;
|
3698
|
+
}
|
3699
|
+
#endif
|
3700
|
+
}
|
3701
|
+
|
3702
|
+
DRMP3_API drmp3_uint64 drmp3_read_pcm_frames_s16(drmp3* pMP3, drmp3_uint64 framesToRead, drmp3_int16* pBufferOut)
|
3703
|
+
{
|
3704
|
+
if (pMP3 == NULL || pMP3->onRead == NULL) {
|
3705
|
+
return 0;
|
3706
|
+
}
|
3707
|
+
|
3708
|
+
#if !defined(DR_MP3_FLOAT_OUTPUT)
|
3709
|
+
/* Fast path. No conversion required. */
|
3710
|
+
return drmp3_read_pcm_frames_raw(pMP3, framesToRead, pBufferOut);
|
3711
|
+
#else
|
3712
|
+
/* Slow path. Convert from f32 to s16. */
|
3713
|
+
{
|
3714
|
+
float pTempF32[4096];
|
3715
|
+
drmp3_uint64 totalPCMFramesRead = 0;
|
3716
|
+
|
3717
|
+
while (totalPCMFramesRead < framesToRead) {
|
3718
|
+
drmp3_uint64 framesJustRead;
|
3719
|
+
drmp3_uint64 framesRemaining = framesToRead - totalPCMFramesRead;
|
3720
|
+
drmp3_uint64 framesToReadNow = DRMP3_COUNTOF(pTempF32) / pMP3->channels;
|
3721
|
+
if (framesToReadNow > framesRemaining) {
|
3722
|
+
framesToReadNow = framesRemaining;
|
3723
|
+
}
|
3724
|
+
|
3725
|
+
framesJustRead = drmp3_read_pcm_frames_raw(pMP3, framesToReadNow, pTempF32);
|
3726
|
+
if (framesJustRead == 0) {
|
3727
|
+
break;
|
3728
|
+
}
|
3729
|
+
|
3730
|
+
drmp3_f32_to_s16((drmp3_int16*)DRMP3_OFFSET_PTR(pBufferOut, sizeof(drmp3_int16) * totalPCMFramesRead * pMP3->channels), pTempF32, framesJustRead * pMP3->channels);
|
3731
|
+
totalPCMFramesRead += framesJustRead;
|
3732
|
+
}
|
3733
|
+
|
3734
|
+
return totalPCMFramesRead;
|
3735
|
+
}
|
3736
|
+
#endif
|
3737
|
+
}
|
3738
|
+
|
3739
|
+
static void drmp3_reset(drmp3* pMP3)
|
3740
|
+
{
|
3741
|
+
DRMP3_ASSERT(pMP3 != NULL);
|
3742
|
+
|
3743
|
+
pMP3->pcmFramesConsumedInMP3Frame = 0;
|
3744
|
+
pMP3->pcmFramesRemainingInMP3Frame = 0;
|
3745
|
+
pMP3->currentPCMFrame = 0;
|
3746
|
+
pMP3->dataSize = 0;
|
3747
|
+
pMP3->atEnd = DRMP3_FALSE;
|
3748
|
+
drmp3dec_init(&pMP3->decoder);
|
3749
|
+
}
|
3750
|
+
|
3751
|
+
static drmp3_bool32 drmp3_seek_to_start_of_stream(drmp3* pMP3)
|
3752
|
+
{
|
3753
|
+
DRMP3_ASSERT(pMP3 != NULL);
|
3754
|
+
DRMP3_ASSERT(pMP3->onSeek != NULL);
|
3755
|
+
|
3756
|
+
/* Seek to the start of the stream to begin with. */
|
3757
|
+
if (!drmp3__on_seek(pMP3, 0, drmp3_seek_origin_start)) {
|
3758
|
+
return DRMP3_FALSE;
|
3759
|
+
}
|
3760
|
+
|
3761
|
+
/* Clear any cached data. */
|
3762
|
+
drmp3_reset(pMP3);
|
3763
|
+
return DRMP3_TRUE;
|
3764
|
+
}
|
3765
|
+
|
3766
|
+
|
3767
|
+
static drmp3_bool32 drmp3_seek_forward_by_pcm_frames__brute_force(drmp3* pMP3, drmp3_uint64 frameOffset)
|
3768
|
+
{
|
3769
|
+
drmp3_uint64 framesRead;
|
3770
|
+
|
3771
|
+
/*
|
3772
|
+
Just using a dumb read-and-discard for now. What would be nice is to parse only the header of the MP3 frame, and then skip over leading
|
3773
|
+
frames without spending the time doing a full decode. I cannot see an easy way to do this in minimp3, however, so it may involve some
|
3774
|
+
kind of manual processing.
|
3775
|
+
*/
|
3776
|
+
#if defined(DR_MP3_FLOAT_OUTPUT)
|
3777
|
+
framesRead = drmp3_read_pcm_frames_f32(pMP3, frameOffset, NULL);
|
3778
|
+
#else
|
3779
|
+
framesRead = drmp3_read_pcm_frames_s16(pMP3, frameOffset, NULL);
|
3780
|
+
#endif
|
3781
|
+
if (framesRead != frameOffset) {
|
3782
|
+
return DRMP3_FALSE;
|
3783
|
+
}
|
3784
|
+
|
3785
|
+
return DRMP3_TRUE;
|
3786
|
+
}
|
3787
|
+
|
3788
|
+
static drmp3_bool32 drmp3_seek_to_pcm_frame__brute_force(drmp3* pMP3, drmp3_uint64 frameIndex)
|
3789
|
+
{
|
3790
|
+
DRMP3_ASSERT(pMP3 != NULL);
|
3791
|
+
|
3792
|
+
if (frameIndex == pMP3->currentPCMFrame) {
|
3793
|
+
return DRMP3_TRUE;
|
3794
|
+
}
|
3795
|
+
|
3796
|
+
/*
|
3797
|
+
If we're moving foward we just read from where we're at. Otherwise we need to move back to the start of
|
3798
|
+
the stream and read from the beginning.
|
3799
|
+
*/
|
3800
|
+
if (frameIndex < pMP3->currentPCMFrame) {
|
3801
|
+
/* Moving backward. Move to the start of the stream and then move forward. */
|
3802
|
+
if (!drmp3_seek_to_start_of_stream(pMP3)) {
|
3803
|
+
return DRMP3_FALSE;
|
3804
|
+
}
|
3805
|
+
}
|
3806
|
+
|
3807
|
+
DRMP3_ASSERT(frameIndex >= pMP3->currentPCMFrame);
|
3808
|
+
return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, (frameIndex - pMP3->currentPCMFrame));
|
3809
|
+
}
|
3810
|
+
|
3811
|
+
static drmp3_bool32 drmp3_find_closest_seek_point(drmp3* pMP3, drmp3_uint64 frameIndex, drmp3_uint32* pSeekPointIndex)
|
3812
|
+
{
|
3813
|
+
drmp3_uint32 iSeekPoint;
|
3814
|
+
|
3815
|
+
DRMP3_ASSERT(pSeekPointIndex != NULL);
|
3816
|
+
|
3817
|
+
*pSeekPointIndex = 0;
|
3818
|
+
|
3819
|
+
if (frameIndex < pMP3->pSeekPoints[0].pcmFrameIndex) {
|
3820
|
+
return DRMP3_FALSE;
|
3821
|
+
}
|
3822
|
+
|
3823
|
+
/* Linear search for simplicity to begin with while I'm getting this thing working. Once it's all working change this to a binary search. */
|
3824
|
+
for (iSeekPoint = 0; iSeekPoint < pMP3->seekPointCount; ++iSeekPoint) {
|
3825
|
+
if (pMP3->pSeekPoints[iSeekPoint].pcmFrameIndex > frameIndex) {
|
3826
|
+
break; /* Found it. */
|
3827
|
+
}
|
3828
|
+
|
3829
|
+
*pSeekPointIndex = iSeekPoint;
|
3830
|
+
}
|
3831
|
+
|
3832
|
+
return DRMP3_TRUE;
|
3833
|
+
}
|
3834
|
+
|
3835
|
+
static drmp3_bool32 drmp3_seek_to_pcm_frame__seek_table(drmp3* pMP3, drmp3_uint64 frameIndex)
|
3836
|
+
{
|
3837
|
+
drmp3_seek_point seekPoint;
|
3838
|
+
drmp3_uint32 priorSeekPointIndex;
|
3839
|
+
drmp3_uint16 iMP3Frame;
|
3840
|
+
drmp3_uint64 leftoverFrames;
|
3841
|
+
|
3842
|
+
DRMP3_ASSERT(pMP3 != NULL);
|
3843
|
+
DRMP3_ASSERT(pMP3->pSeekPoints != NULL);
|
3844
|
+
DRMP3_ASSERT(pMP3->seekPointCount > 0);
|
3845
|
+
|
3846
|
+
/* If there is no prior seekpoint it means the target PCM frame comes before the first seek point. Just assume a seekpoint at the start of the file in this case. */
|
3847
|
+
if (drmp3_find_closest_seek_point(pMP3, frameIndex, &priorSeekPointIndex)) {
|
3848
|
+
seekPoint = pMP3->pSeekPoints[priorSeekPointIndex];
|
3849
|
+
} else {
|
3850
|
+
seekPoint.seekPosInBytes = 0;
|
3851
|
+
seekPoint.pcmFrameIndex = 0;
|
3852
|
+
seekPoint.mp3FramesToDiscard = 0;
|
3853
|
+
seekPoint.pcmFramesToDiscard = 0;
|
3854
|
+
}
|
3855
|
+
|
3856
|
+
/* First thing to do is seek to the first byte of the relevant MP3 frame. */
|
3857
|
+
if (!drmp3__on_seek_64(pMP3, seekPoint.seekPosInBytes, drmp3_seek_origin_start)) {
|
3858
|
+
return DRMP3_FALSE; /* Failed to seek. */
|
3859
|
+
}
|
3860
|
+
|
3861
|
+
/* Clear any cached data. */
|
3862
|
+
drmp3_reset(pMP3);
|
3863
|
+
|
3864
|
+
/* Whole MP3 frames need to be discarded first. */
|
3865
|
+
for (iMP3Frame = 0; iMP3Frame < seekPoint.mp3FramesToDiscard; ++iMP3Frame) {
|
3866
|
+
drmp3_uint32 pcmFramesRead;
|
3867
|
+
drmp3d_sample_t* pPCMFrames;
|
3868
|
+
|
3869
|
+
/* Pass in non-null for the last frame because we want to ensure the sample rate converter is preloaded correctly. */
|
3870
|
+
pPCMFrames = NULL;
|
3871
|
+
if (iMP3Frame == seekPoint.mp3FramesToDiscard-1) {
|
3872
|
+
pPCMFrames = (drmp3d_sample_t*)pMP3->pcmFrames;
|
3873
|
+
}
|
3874
|
+
|
3875
|
+
/* We first need to decode the next frame. */
|
3876
|
+
pcmFramesRead = drmp3_decode_next_frame_ex(pMP3, pPCMFrames);
|
3877
|
+
if (pcmFramesRead == 0) {
|
3878
|
+
return DRMP3_FALSE;
|
3879
|
+
}
|
3880
|
+
}
|
3881
|
+
|
3882
|
+
/* We seeked to an MP3 frame in the raw stream so we need to make sure the current PCM frame is set correctly. */
|
3883
|
+
pMP3->currentPCMFrame = seekPoint.pcmFrameIndex - seekPoint.pcmFramesToDiscard;
|
3884
|
+
|
3885
|
+
/*
|
3886
|
+
Now at this point we can follow the same process as the brute force technique where we just skip over unnecessary MP3 frames and then
|
3887
|
+
read-and-discard at least 2 whole MP3 frames.
|
3888
|
+
*/
|
3889
|
+
leftoverFrames = frameIndex - pMP3->currentPCMFrame;
|
3890
|
+
return drmp3_seek_forward_by_pcm_frames__brute_force(pMP3, leftoverFrames);
|
3891
|
+
}
|
3892
|
+
|
3893
|
+
DRMP3_API drmp3_bool32 drmp3_seek_to_pcm_frame(drmp3* pMP3, drmp3_uint64 frameIndex)
|
3894
|
+
{
|
3895
|
+
if (pMP3 == NULL || pMP3->onSeek == NULL) {
|
3896
|
+
return DRMP3_FALSE;
|
3897
|
+
}
|
3898
|
+
|
3899
|
+
if (frameIndex == 0) {
|
3900
|
+
return drmp3_seek_to_start_of_stream(pMP3);
|
3901
|
+
}
|
3902
|
+
|
3903
|
+
/* Use the seek table if we have one. */
|
3904
|
+
if (pMP3->pSeekPoints != NULL && pMP3->seekPointCount > 0) {
|
3905
|
+
return drmp3_seek_to_pcm_frame__seek_table(pMP3, frameIndex);
|
3906
|
+
} else {
|
3907
|
+
return drmp3_seek_to_pcm_frame__brute_force(pMP3, frameIndex);
|
3908
|
+
}
|
3909
|
+
}
|
3910
|
+
|
3911
|
+
DRMP3_API drmp3_bool32 drmp3_get_mp3_and_pcm_frame_count(drmp3* pMP3, drmp3_uint64* pMP3FrameCount, drmp3_uint64* pPCMFrameCount)
|
3912
|
+
{
|
3913
|
+
drmp3_uint64 currentPCMFrame;
|
3914
|
+
drmp3_uint64 totalPCMFrameCount;
|
3915
|
+
drmp3_uint64 totalMP3FrameCount;
|
3916
|
+
|
3917
|
+
if (pMP3 == NULL) {
|
3918
|
+
return DRMP3_FALSE;
|
3919
|
+
}
|
3920
|
+
|
3921
|
+
/*
|
3922
|
+
The way this works is we move back to the start of the stream, iterate over each MP3 frame and calculate the frame count based
|
3923
|
+
on our output sample rate, the seek back to the PCM frame we were sitting on before calling this function.
|
3924
|
+
*/
|
3925
|
+
|
3926
|
+
/* The stream must support seeking for this to work. */
|
3927
|
+
if (pMP3->onSeek == NULL) {
|
3928
|
+
return DRMP3_FALSE;
|
3929
|
+
}
|
3930
|
+
|
3931
|
+
/* We'll need to seek back to where we were, so grab the PCM frame we're currently sitting on so we can restore later. */
|
3932
|
+
currentPCMFrame = pMP3->currentPCMFrame;
|
3933
|
+
|
3934
|
+
if (!drmp3_seek_to_start_of_stream(pMP3)) {
|
3935
|
+
return DRMP3_FALSE;
|
3936
|
+
}
|
3937
|
+
|
3938
|
+
totalPCMFrameCount = 0;
|
3939
|
+
totalMP3FrameCount = 0;
|
3940
|
+
|
3941
|
+
for (;;) {
|
3942
|
+
drmp3_uint32 pcmFramesInCurrentMP3Frame;
|
3943
|
+
|
3944
|
+
pcmFramesInCurrentMP3Frame = drmp3_decode_next_frame_ex(pMP3, NULL);
|
3945
|
+
if (pcmFramesInCurrentMP3Frame == 0) {
|
3946
|
+
break;
|
3947
|
+
}
|
3948
|
+
|
3949
|
+
totalPCMFrameCount += pcmFramesInCurrentMP3Frame;
|
3950
|
+
totalMP3FrameCount += 1;
|
3951
|
+
}
|
3952
|
+
|
3953
|
+
/* Finally, we need to seek back to where we were. */
|
3954
|
+
if (!drmp3_seek_to_start_of_stream(pMP3)) {
|
3955
|
+
return DRMP3_FALSE;
|
3956
|
+
}
|
3957
|
+
|
3958
|
+
if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
|
3959
|
+
return DRMP3_FALSE;
|
3960
|
+
}
|
3961
|
+
|
3962
|
+
if (pMP3FrameCount != NULL) {
|
3963
|
+
*pMP3FrameCount = totalMP3FrameCount;
|
3964
|
+
}
|
3965
|
+
if (pPCMFrameCount != NULL) {
|
3966
|
+
*pPCMFrameCount = totalPCMFrameCount;
|
3967
|
+
}
|
3968
|
+
|
3969
|
+
return DRMP3_TRUE;
|
3970
|
+
}
|
3971
|
+
|
3972
|
+
DRMP3_API drmp3_uint64 drmp3_get_pcm_frame_count(drmp3* pMP3)
|
3973
|
+
{
|
3974
|
+
drmp3_uint64 totalPCMFrameCount;
|
3975
|
+
if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, NULL, &totalPCMFrameCount)) {
|
3976
|
+
return 0;
|
3977
|
+
}
|
3978
|
+
|
3979
|
+
return totalPCMFrameCount;
|
3980
|
+
}
|
3981
|
+
|
3982
|
+
DRMP3_API drmp3_uint64 drmp3_get_mp3_frame_count(drmp3* pMP3)
|
3983
|
+
{
|
3984
|
+
drmp3_uint64 totalMP3FrameCount;
|
3985
|
+
if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, NULL)) {
|
3986
|
+
return 0;
|
3987
|
+
}
|
3988
|
+
|
3989
|
+
return totalMP3FrameCount;
|
3990
|
+
}
|
3991
|
+
|
3992
|
+
static void drmp3__accumulate_running_pcm_frame_count(drmp3* pMP3, drmp3_uint32 pcmFrameCountIn, drmp3_uint64* pRunningPCMFrameCount, float* pRunningPCMFrameCountFractionalPart)
|
3993
|
+
{
|
3994
|
+
float srcRatio;
|
3995
|
+
float pcmFrameCountOutF;
|
3996
|
+
drmp3_uint32 pcmFrameCountOut;
|
3997
|
+
|
3998
|
+
srcRatio = (float)pMP3->mp3FrameSampleRate / (float)pMP3->sampleRate;
|
3999
|
+
DRMP3_ASSERT(srcRatio > 0);
|
4000
|
+
|
4001
|
+
pcmFrameCountOutF = *pRunningPCMFrameCountFractionalPart + (pcmFrameCountIn / srcRatio);
|
4002
|
+
pcmFrameCountOut = (drmp3_uint32)pcmFrameCountOutF;
|
4003
|
+
*pRunningPCMFrameCountFractionalPart = pcmFrameCountOutF - pcmFrameCountOut;
|
4004
|
+
*pRunningPCMFrameCount += pcmFrameCountOut;
|
4005
|
+
}
|
4006
|
+
|
4007
|
+
typedef struct
|
4008
|
+
{
|
4009
|
+
drmp3_uint64 bytePos;
|
4010
|
+
drmp3_uint64 pcmFrameIndex; /* <-- After sample rate conversion. */
|
4011
|
+
} drmp3__seeking_mp3_frame_info;
|
4012
|
+
|
4013
|
+
DRMP3_API drmp3_bool32 drmp3_calculate_seek_points(drmp3* pMP3, drmp3_uint32* pSeekPointCount, drmp3_seek_point* pSeekPoints)
|
4014
|
+
{
|
4015
|
+
drmp3_uint32 seekPointCount;
|
4016
|
+
drmp3_uint64 currentPCMFrame;
|
4017
|
+
drmp3_uint64 totalMP3FrameCount;
|
4018
|
+
drmp3_uint64 totalPCMFrameCount;
|
4019
|
+
|
4020
|
+
if (pMP3 == NULL || pSeekPointCount == NULL || pSeekPoints == NULL) {
|
4021
|
+
return DRMP3_FALSE; /* Invalid args. */
|
4022
|
+
}
|
4023
|
+
|
4024
|
+
seekPointCount = *pSeekPointCount;
|
4025
|
+
if (seekPointCount == 0) {
|
4026
|
+
return DRMP3_FALSE; /* The client has requested no seek points. Consider this to be invalid arguments since the client has probably not intended this. */
|
4027
|
+
}
|
4028
|
+
|
4029
|
+
/* We'll need to seek back to the current sample after calculating the seekpoints so we need to go ahead and grab the current location at the top. */
|
4030
|
+
currentPCMFrame = pMP3->currentPCMFrame;
|
4031
|
+
|
4032
|
+
/* We never do more than the total number of MP3 frames and we limit it to 32-bits. */
|
4033
|
+
if (!drmp3_get_mp3_and_pcm_frame_count(pMP3, &totalMP3FrameCount, &totalPCMFrameCount)) {
|
4034
|
+
return DRMP3_FALSE;
|
4035
|
+
}
|
4036
|
+
|
4037
|
+
/* If there's less than DRMP3_SEEK_LEADING_MP3_FRAMES+1 frames we just report 1 seek point which will be the very start of the stream. */
|
4038
|
+
if (totalMP3FrameCount < DRMP3_SEEK_LEADING_MP3_FRAMES+1) {
|
4039
|
+
seekPointCount = 1;
|
4040
|
+
pSeekPoints[0].seekPosInBytes = 0;
|
4041
|
+
pSeekPoints[0].pcmFrameIndex = 0;
|
4042
|
+
pSeekPoints[0].mp3FramesToDiscard = 0;
|
4043
|
+
pSeekPoints[0].pcmFramesToDiscard = 0;
|
4044
|
+
} else {
|
4045
|
+
drmp3_uint64 pcmFramesBetweenSeekPoints;
|
4046
|
+
drmp3__seeking_mp3_frame_info mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES+1];
|
4047
|
+
drmp3_uint64 runningPCMFrameCount = 0;
|
4048
|
+
float runningPCMFrameCountFractionalPart = 0;
|
4049
|
+
drmp3_uint64 nextTargetPCMFrame;
|
4050
|
+
drmp3_uint32 iMP3Frame;
|
4051
|
+
drmp3_uint32 iSeekPoint;
|
4052
|
+
|
4053
|
+
if (seekPointCount > totalMP3FrameCount-1) {
|
4054
|
+
seekPointCount = (drmp3_uint32)totalMP3FrameCount-1;
|
4055
|
+
}
|
4056
|
+
|
4057
|
+
pcmFramesBetweenSeekPoints = totalPCMFrameCount / (seekPointCount+1);
|
4058
|
+
|
4059
|
+
/*
|
4060
|
+
Here is where we actually calculate the seek points. We need to start by moving the start of the stream. We then enumerate over each
|
4061
|
+
MP3 frame.
|
4062
|
+
*/
|
4063
|
+
if (!drmp3_seek_to_start_of_stream(pMP3)) {
|
4064
|
+
return DRMP3_FALSE;
|
4065
|
+
}
|
4066
|
+
|
4067
|
+
/*
|
4068
|
+
We need to cache the byte positions of the previous MP3 frames. As a new MP3 frame is iterated, we cycle the byte positions in this
|
4069
|
+
array. The value in the first item in this array is the byte position that will be reported in the next seek point.
|
4070
|
+
*/
|
4071
|
+
|
4072
|
+
/* We need to initialize the array of MP3 byte positions for the leading MP3 frames. */
|
4073
|
+
for (iMP3Frame = 0; iMP3Frame < DRMP3_SEEK_LEADING_MP3_FRAMES+1; ++iMP3Frame) {
|
4074
|
+
drmp3_uint32 pcmFramesInCurrentMP3FrameIn;
|
4075
|
+
|
4076
|
+
/* The byte position of the next frame will be the stream's cursor position, minus whatever is sitting in the buffer. */
|
4077
|
+
DRMP3_ASSERT(pMP3->streamCursor >= pMP3->dataSize);
|
4078
|
+
mp3FrameInfo[iMP3Frame].bytePos = pMP3->streamCursor - pMP3->dataSize;
|
4079
|
+
mp3FrameInfo[iMP3Frame].pcmFrameIndex = runningPCMFrameCount;
|
4080
|
+
|
4081
|
+
/* We need to get information about this frame so we can know how many samples it contained. */
|
4082
|
+
pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL);
|
4083
|
+
if (pcmFramesInCurrentMP3FrameIn == 0) {
|
4084
|
+
return DRMP3_FALSE; /* This should never happen. */
|
4085
|
+
}
|
4086
|
+
|
4087
|
+
drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
|
4088
|
+
}
|
4089
|
+
|
4090
|
+
/*
|
4091
|
+
At this point we will have extracted the byte positions of the leading MP3 frames. We can now start iterating over each seek point and
|
4092
|
+
calculate them.
|
4093
|
+
*/
|
4094
|
+
nextTargetPCMFrame = 0;
|
4095
|
+
for (iSeekPoint = 0; iSeekPoint < seekPointCount; ++iSeekPoint) {
|
4096
|
+
nextTargetPCMFrame += pcmFramesBetweenSeekPoints;
|
4097
|
+
|
4098
|
+
for (;;) {
|
4099
|
+
if (nextTargetPCMFrame < runningPCMFrameCount) {
|
4100
|
+
/* The next seek point is in the current MP3 frame. */
|
4101
|
+
pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos;
|
4102
|
+
pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame;
|
4103
|
+
pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES;
|
4104
|
+
pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
|
4105
|
+
break;
|
4106
|
+
} else {
|
4107
|
+
size_t i;
|
4108
|
+
drmp3_uint32 pcmFramesInCurrentMP3FrameIn;
|
4109
|
+
|
4110
|
+
/*
|
4111
|
+
The next seek point is not in the current MP3 frame, so continue on to the next one. The first thing to do is cycle the cached
|
4112
|
+
MP3 frame info.
|
4113
|
+
*/
|
4114
|
+
for (i = 0; i < DRMP3_COUNTOF(mp3FrameInfo)-1; ++i) {
|
4115
|
+
mp3FrameInfo[i] = mp3FrameInfo[i+1];
|
4116
|
+
}
|
4117
|
+
|
4118
|
+
/* Cache previous MP3 frame info. */
|
4119
|
+
mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].bytePos = pMP3->streamCursor - pMP3->dataSize;
|
4120
|
+
mp3FrameInfo[DRMP3_COUNTOF(mp3FrameInfo)-1].pcmFrameIndex = runningPCMFrameCount;
|
4121
|
+
|
4122
|
+
/*
|
4123
|
+
Go to the next MP3 frame. This shouldn't ever fail, but just in case it does we just set the seek point and break. If it happens, it
|
4124
|
+
should only ever do it for the last seek point.
|
4125
|
+
*/
|
4126
|
+
pcmFramesInCurrentMP3FrameIn = drmp3_decode_next_frame_ex(pMP3, NULL);
|
4127
|
+
if (pcmFramesInCurrentMP3FrameIn == 0) {
|
4128
|
+
pSeekPoints[iSeekPoint].seekPosInBytes = mp3FrameInfo[0].bytePos;
|
4129
|
+
pSeekPoints[iSeekPoint].pcmFrameIndex = nextTargetPCMFrame;
|
4130
|
+
pSeekPoints[iSeekPoint].mp3FramesToDiscard = DRMP3_SEEK_LEADING_MP3_FRAMES;
|
4131
|
+
pSeekPoints[iSeekPoint].pcmFramesToDiscard = (drmp3_uint16)(nextTargetPCMFrame - mp3FrameInfo[DRMP3_SEEK_LEADING_MP3_FRAMES-1].pcmFrameIndex);
|
4132
|
+
break;
|
4133
|
+
}
|
4134
|
+
|
4135
|
+
drmp3__accumulate_running_pcm_frame_count(pMP3, pcmFramesInCurrentMP3FrameIn, &runningPCMFrameCount, &runningPCMFrameCountFractionalPart);
|
4136
|
+
}
|
4137
|
+
}
|
4138
|
+
}
|
4139
|
+
|
4140
|
+
/* Finally, we need to seek back to where we were. */
|
4141
|
+
if (!drmp3_seek_to_start_of_stream(pMP3)) {
|
4142
|
+
return DRMP3_FALSE;
|
4143
|
+
}
|
4144
|
+
if (!drmp3_seek_to_pcm_frame(pMP3, currentPCMFrame)) {
|
4145
|
+
return DRMP3_FALSE;
|
4146
|
+
}
|
4147
|
+
}
|
4148
|
+
|
4149
|
+
*pSeekPointCount = seekPointCount;
|
4150
|
+
return DRMP3_TRUE;
|
4151
|
+
}
|
4152
|
+
|
4153
|
+
DRMP3_API drmp3_bool32 drmp3_bind_seek_table(drmp3* pMP3, drmp3_uint32 seekPointCount, drmp3_seek_point* pSeekPoints)
|
4154
|
+
{
|
4155
|
+
if (pMP3 == NULL) {
|
4156
|
+
return DRMP3_FALSE;
|
4157
|
+
}
|
4158
|
+
|
4159
|
+
if (seekPointCount == 0 || pSeekPoints == NULL) {
|
4160
|
+
/* Unbinding. */
|
4161
|
+
pMP3->seekPointCount = 0;
|
4162
|
+
pMP3->pSeekPoints = NULL;
|
4163
|
+
} else {
|
4164
|
+
/* Binding. */
|
4165
|
+
pMP3->seekPointCount = seekPointCount;
|
4166
|
+
pMP3->pSeekPoints = pSeekPoints;
|
4167
|
+
}
|
4168
|
+
|
4169
|
+
return DRMP3_TRUE;
|
4170
|
+
}
|
4171
|
+
|
4172
|
+
|
4173
|
+
static float* drmp3__full_read_and_close_f32(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
|
4174
|
+
{
|
4175
|
+
drmp3_uint64 totalFramesRead = 0;
|
4176
|
+
drmp3_uint64 framesCapacity = 0;
|
4177
|
+
float* pFrames = NULL;
|
4178
|
+
float temp[4096];
|
4179
|
+
|
4180
|
+
DRMP3_ASSERT(pMP3 != NULL);
|
4181
|
+
|
4182
|
+
for (;;) {
|
4183
|
+
drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels;
|
4184
|
+
drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_f32(pMP3, framesToReadRightNow, temp);
|
4185
|
+
if (framesJustRead == 0) {
|
4186
|
+
break;
|
4187
|
+
}
|
4188
|
+
|
4189
|
+
/* Reallocate the output buffer if there's not enough room. */
|
4190
|
+
if (framesCapacity < totalFramesRead + framesJustRead) {
|
4191
|
+
drmp3_uint64 oldFramesBufferSize;
|
4192
|
+
drmp3_uint64 newFramesBufferSize;
|
4193
|
+
drmp3_uint64 newFramesCap;
|
4194
|
+
float* pNewFrames;
|
4195
|
+
|
4196
|
+
newFramesCap = framesCapacity * 2;
|
4197
|
+
if (newFramesCap < totalFramesRead + framesJustRead) {
|
4198
|
+
newFramesCap = totalFramesRead + framesJustRead;
|
4199
|
+
}
|
4200
|
+
|
4201
|
+
oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(float);
|
4202
|
+
newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(float);
|
4203
|
+
if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) {
|
4204
|
+
break;
|
4205
|
+
}
|
4206
|
+
|
4207
|
+
pNewFrames = (float*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
|
4208
|
+
if (pNewFrames == NULL) {
|
4209
|
+
drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
|
4210
|
+
break;
|
4211
|
+
}
|
4212
|
+
|
4213
|
+
pFrames = pNewFrames;
|
4214
|
+
framesCapacity = newFramesCap;
|
4215
|
+
}
|
4216
|
+
|
4217
|
+
DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(float)));
|
4218
|
+
totalFramesRead += framesJustRead;
|
4219
|
+
|
4220
|
+
/* If the number of frames we asked for is less that what we actually read it means we've reached the end. */
|
4221
|
+
if (framesJustRead != framesToReadRightNow) {
|
4222
|
+
break;
|
4223
|
+
}
|
4224
|
+
}
|
4225
|
+
|
4226
|
+
if (pConfig != NULL) {
|
4227
|
+
pConfig->channels = pMP3->channels;
|
4228
|
+
pConfig->sampleRate = pMP3->sampleRate;
|
4229
|
+
}
|
4230
|
+
|
4231
|
+
drmp3_uninit(pMP3);
|
4232
|
+
|
4233
|
+
if (pTotalFrameCount) {
|
4234
|
+
*pTotalFrameCount = totalFramesRead;
|
4235
|
+
}
|
4236
|
+
|
4237
|
+
return pFrames;
|
4238
|
+
}
|
4239
|
+
|
4240
|
+
static drmp3_int16* drmp3__full_read_and_close_s16(drmp3* pMP3, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount)
|
4241
|
+
{
|
4242
|
+
drmp3_uint64 totalFramesRead = 0;
|
4243
|
+
drmp3_uint64 framesCapacity = 0;
|
4244
|
+
drmp3_int16* pFrames = NULL;
|
4245
|
+
drmp3_int16 temp[4096];
|
4246
|
+
|
4247
|
+
DRMP3_ASSERT(pMP3 != NULL);
|
4248
|
+
|
4249
|
+
for (;;) {
|
4250
|
+
drmp3_uint64 framesToReadRightNow = DRMP3_COUNTOF(temp) / pMP3->channels;
|
4251
|
+
drmp3_uint64 framesJustRead = drmp3_read_pcm_frames_s16(pMP3, framesToReadRightNow, temp);
|
4252
|
+
if (framesJustRead == 0) {
|
4253
|
+
break;
|
4254
|
+
}
|
4255
|
+
|
4256
|
+
/* Reallocate the output buffer if there's not enough room. */
|
4257
|
+
if (framesCapacity < totalFramesRead + framesJustRead) {
|
4258
|
+
drmp3_uint64 newFramesBufferSize;
|
4259
|
+
drmp3_uint64 oldFramesBufferSize;
|
4260
|
+
drmp3_uint64 newFramesCap;
|
4261
|
+
drmp3_int16* pNewFrames;
|
4262
|
+
|
4263
|
+
newFramesCap = framesCapacity * 2;
|
4264
|
+
if (newFramesCap < totalFramesRead + framesJustRead) {
|
4265
|
+
newFramesCap = totalFramesRead + framesJustRead;
|
4266
|
+
}
|
4267
|
+
|
4268
|
+
oldFramesBufferSize = framesCapacity * pMP3->channels * sizeof(drmp3_int16);
|
4269
|
+
newFramesBufferSize = newFramesCap * pMP3->channels * sizeof(drmp3_int16);
|
4270
|
+
if (newFramesBufferSize > (drmp3_uint64)DRMP3_SIZE_MAX) {
|
4271
|
+
break;
|
4272
|
+
}
|
4273
|
+
|
4274
|
+
pNewFrames = (drmp3_int16*)drmp3__realloc_from_callbacks(pFrames, (size_t)newFramesBufferSize, (size_t)oldFramesBufferSize, &pMP3->allocationCallbacks);
|
4275
|
+
if (pNewFrames == NULL) {
|
4276
|
+
drmp3__free_from_callbacks(pFrames, &pMP3->allocationCallbacks);
|
4277
|
+
break;
|
4278
|
+
}
|
4279
|
+
|
4280
|
+
pFrames = pNewFrames;
|
4281
|
+
framesCapacity = newFramesCap;
|
4282
|
+
}
|
4283
|
+
|
4284
|
+
DRMP3_COPY_MEMORY(pFrames + totalFramesRead*pMP3->channels, temp, (size_t)(framesJustRead*pMP3->channels*sizeof(drmp3_int16)));
|
4285
|
+
totalFramesRead += framesJustRead;
|
4286
|
+
|
4287
|
+
/* If the number of frames we asked for is less that what we actually read it means we've reached the end. */
|
4288
|
+
if (framesJustRead != framesToReadRightNow) {
|
4289
|
+
break;
|
4290
|
+
}
|
4291
|
+
}
|
4292
|
+
|
4293
|
+
if (pConfig != NULL) {
|
4294
|
+
pConfig->channels = pMP3->channels;
|
4295
|
+
pConfig->sampleRate = pMP3->sampleRate;
|
4296
|
+
}
|
4297
|
+
|
4298
|
+
drmp3_uninit(pMP3);
|
4299
|
+
|
4300
|
+
if (pTotalFrameCount) {
|
4301
|
+
*pTotalFrameCount = totalFramesRead;
|
4302
|
+
}
|
4303
|
+
|
4304
|
+
return pFrames;
|
4305
|
+
}
|
4306
|
+
|
4307
|
+
|
4308
|
+
DRMP3_API float* drmp3_open_and_read_pcm_frames_f32(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
|
4309
|
+
{
|
4310
|
+
drmp3 mp3;
|
4311
|
+
if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) {
|
4312
|
+
return NULL;
|
4313
|
+
}
|
4314
|
+
|
4315
|
+
return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
|
4316
|
+
}
|
4317
|
+
|
4318
|
+
DRMP3_API drmp3_int16* drmp3_open_and_read_pcm_frames_s16(drmp3_read_proc onRead, drmp3_seek_proc onSeek, void* pUserData, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
|
4319
|
+
{
|
4320
|
+
drmp3 mp3;
|
4321
|
+
if (!drmp3_init(&mp3, onRead, onSeek, pUserData, pAllocationCallbacks)) {
|
4322
|
+
return NULL;
|
4323
|
+
}
|
4324
|
+
|
4325
|
+
return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
|
4326
|
+
}
|
4327
|
+
|
4328
|
+
|
4329
|
+
DRMP3_API float* drmp3_open_memory_and_read_pcm_frames_f32(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
|
4330
|
+
{
|
4331
|
+
drmp3 mp3;
|
4332
|
+
if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) {
|
4333
|
+
return NULL;
|
4334
|
+
}
|
4335
|
+
|
4336
|
+
return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
|
4337
|
+
}
|
4338
|
+
|
4339
|
+
DRMP3_API drmp3_int16* drmp3_open_memory_and_read_pcm_frames_s16(const void* pData, size_t dataSize, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
|
4340
|
+
{
|
4341
|
+
drmp3 mp3;
|
4342
|
+
if (!drmp3_init_memory(&mp3, pData, dataSize, pAllocationCallbacks)) {
|
4343
|
+
return NULL;
|
4344
|
+
}
|
4345
|
+
|
4346
|
+
return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
|
4347
|
+
}
|
4348
|
+
|
4349
|
+
|
4350
|
+
#ifndef DR_MP3_NO_STDIO
|
4351
|
+
DRMP3_API float* drmp3_open_file_and_read_pcm_frames_f32(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
|
4352
|
+
{
|
4353
|
+
drmp3 mp3;
|
4354
|
+
if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) {
|
4355
|
+
return NULL;
|
4356
|
+
}
|
4357
|
+
|
4358
|
+
return drmp3__full_read_and_close_f32(&mp3, pConfig, pTotalFrameCount);
|
4359
|
+
}
|
4360
|
+
|
4361
|
+
DRMP3_API drmp3_int16* drmp3_open_file_and_read_pcm_frames_s16(const char* filePath, drmp3_config* pConfig, drmp3_uint64* pTotalFrameCount, const drmp3_allocation_callbacks* pAllocationCallbacks)
|
4362
|
+
{
|
4363
|
+
drmp3 mp3;
|
4364
|
+
if (!drmp3_init_file(&mp3, filePath, pAllocationCallbacks)) {
|
2870
4365
|
return NULL;
|
2871
4366
|
}
|
2872
4367
|
|
2873
|
-
return
|
4368
|
+
return drmp3__full_read_and_close_s16(&mp3, pConfig, pTotalFrameCount);
|
2874
4369
|
}
|
2875
4370
|
#endif
|
2876
4371
|
|
2877
|
-
void
|
4372
|
+
DRMP3_API void* drmp3_malloc(size_t sz, const drmp3_allocation_callbacks* pAllocationCallbacks)
|
2878
4373
|
{
|
2879
|
-
|
4374
|
+
if (pAllocationCallbacks != NULL) {
|
4375
|
+
return drmp3__malloc_from_callbacks(sz, pAllocationCallbacks);
|
4376
|
+
} else {
|
4377
|
+
return drmp3__malloc_default(sz, NULL);
|
4378
|
+
}
|
4379
|
+
}
|
4380
|
+
|
4381
|
+
DRMP3_API void drmp3_free(void* p, const drmp3_allocation_callbacks* pAllocationCallbacks)
|
4382
|
+
{
|
4383
|
+
if (pAllocationCallbacks != NULL) {
|
4384
|
+
drmp3__free_from_callbacks(p, pAllocationCallbacks);
|
4385
|
+
} else {
|
4386
|
+
drmp3__free_default(p, NULL);
|
4387
|
+
}
|
2880
4388
|
}
|
2881
4389
|
|
2882
|
-
#endif
|
2883
|
-
|
2884
|
-
|
2885
|
-
|
2886
|
-
|
2887
|
-
|
2888
|
-
|
2889
|
-
|
2890
|
-
|
2891
|
-
|
2892
|
-
|
2893
|
-
|
2894
|
-
|
2895
|
-
|
2896
|
-
|
2897
|
-
|
2898
|
-
|
2899
|
-
|
2900
|
-
|
2901
|
-
|
2902
|
-
|
2903
|
-
|
2904
|
-
|
2905
|
-
|
2906
|
-
|
2907
|
-
|
2908
|
-
|
2909
|
-
|
2910
|
-
|
2911
|
-
|
2912
|
-
|
2913
|
-
|
2914
|
-
|
2915
|
-
|
2916
|
-
|
2917
|
-
|
2918
|
-
|
2919
|
-
|
2920
|
-
|
2921
|
-
|
2922
|
-
|
2923
|
-
|
2924
|
-
|
2925
|
-
|
2926
|
-
|
2927
|
-
|
2928
|
-
|
2929
|
-
|
2930
|
-
|
2931
|
-
|
2932
|
-
|
2933
|
-
|
2934
|
-
|
2935
|
-
|
2936
|
-
|
2937
|
-
|
2938
|
-
|
2939
|
-
|
2940
|
-
|
2941
|
-
|
2942
|
-
|
2943
|
-
|
2944
|
-
|
2945
|
-
|
2946
|
-
|
2947
|
-
|
2948
|
-
|
2949
|
-
|
2950
|
-
|
2951
|
-
|
2952
|
-
|
2953
|
-
|
2954
|
-
|
2955
|
-
|
2956
|
-
|
2957
|
-
|
2958
|
-
|
2959
|
-
|
2960
|
-
|
2961
|
-
|
2962
|
-
|
2963
|
-
|
2964
|
-
|
2965
|
-
|
2966
|
-
|
4390
|
+
#endif /* dr_mp3_c */
|
4391
|
+
#endif /*DR_MP3_IMPLEMENTATION*/
|
4392
|
+
|
4393
|
+
/*
|
4394
|
+
DIFFERENCES BETWEEN minimp3 AND dr_mp3
|
4395
|
+
======================================
|
4396
|
+
- First, keep in mind that minimp3 (https://github.com/lieff/minimp3) is where all the real work was done. All of the
|
4397
|
+
code relating to the actual decoding remains mostly unmodified, apart from some namespacing changes.
|
4398
|
+
- dr_mp3 adds a pulling style API which allows you to deliver raw data via callbacks. So, rather than pushing data
|
4399
|
+
to the decoder, the decoder _pulls_ data from your callbacks.
|
4400
|
+
- In addition to callbacks, a decoder can be initialized from a block of memory and a file.
|
4401
|
+
- The dr_mp3 pull API reads PCM frames rather than whole MP3 frames.
|
4402
|
+
- dr_mp3 adds convenience APIs for opening and decoding entire files in one go.
|
4403
|
+
- dr_mp3 is fully namespaced, including the implementation section, which is more suitable when compiling projects
|
4404
|
+
as a single translation unit (aka unity builds). At the time of writing this, a unity build is not possible when
|
4405
|
+
using minimp3 in conjunction with stb_vorbis. dr_mp3 addresses this.
|
4406
|
+
*/
|
4407
|
+
|
4408
|
+
/*
|
4409
|
+
RELEASE NOTES - v0.5.0
|
4410
|
+
=======================
|
4411
|
+
Version 0.5.0 has breaking API changes.
|
4412
|
+
|
4413
|
+
Improved Client-Defined Memory Allocation
|
4414
|
+
-----------------------------------------
|
4415
|
+
The main change with this release is the addition of a more flexible way of implementing custom memory allocation routines. The
|
4416
|
+
existing system of DRMP3_MALLOC, DRMP3_REALLOC and DRMP3_FREE are still in place and will be used by default when no custom
|
4417
|
+
allocation callbacks are specified.
|
4418
|
+
|
4419
|
+
To use the new system, you pass in a pointer to a drmp3_allocation_callbacks object to drmp3_init() and family, like this:
|
4420
|
+
|
4421
|
+
void* my_malloc(size_t sz, void* pUserData)
|
4422
|
+
{
|
4423
|
+
return malloc(sz);
|
4424
|
+
}
|
4425
|
+
void* my_realloc(void* p, size_t sz, void* pUserData)
|
4426
|
+
{
|
4427
|
+
return realloc(p, sz);
|
4428
|
+
}
|
4429
|
+
void my_free(void* p, void* pUserData)
|
4430
|
+
{
|
4431
|
+
free(p);
|
4432
|
+
}
|
4433
|
+
|
4434
|
+
...
|
4435
|
+
|
4436
|
+
drmp3_allocation_callbacks allocationCallbacks;
|
4437
|
+
allocationCallbacks.pUserData = &myData;
|
4438
|
+
allocationCallbacks.onMalloc = my_malloc;
|
4439
|
+
allocationCallbacks.onRealloc = my_realloc;
|
4440
|
+
allocationCallbacks.onFree = my_free;
|
4441
|
+
drmp3_init_file(&mp3, "my_file.mp3", NULL, &allocationCallbacks);
|
4442
|
+
|
4443
|
+
The advantage of this new system is that it allows you to specify user data which will be passed in to the allocation routines.
|
4444
|
+
|
4445
|
+
Passing in null for the allocation callbacks object will cause dr_mp3 to use defaults which is the same as DRMP3_MALLOC,
|
4446
|
+
DRMP3_REALLOC and DRMP3_FREE and the equivalent of how it worked in previous versions.
|
4447
|
+
|
4448
|
+
Every API that opens a drmp3 object now takes this extra parameter. These include the following:
|
4449
|
+
|
4450
|
+
drmp3_init()
|
4451
|
+
drmp3_init_file()
|
4452
|
+
drmp3_init_memory()
|
4453
|
+
drmp3_open_and_read_pcm_frames_f32()
|
4454
|
+
drmp3_open_and_read_pcm_frames_s16()
|
4455
|
+
drmp3_open_memory_and_read_pcm_frames_f32()
|
4456
|
+
drmp3_open_memory_and_read_pcm_frames_s16()
|
4457
|
+
drmp3_open_file_and_read_pcm_frames_f32()
|
4458
|
+
drmp3_open_file_and_read_pcm_frames_s16()
|
4459
|
+
|
4460
|
+
Renamed APIs
|
4461
|
+
------------
|
4462
|
+
The following APIs have been renamed for consistency with other dr_* libraries and to make it clear that they return PCM frame
|
4463
|
+
counts rather than sample counts.
|
4464
|
+
|
4465
|
+
drmp3_open_and_read_f32() -> drmp3_open_and_read_pcm_frames_f32()
|
4466
|
+
drmp3_open_and_read_s16() -> drmp3_open_and_read_pcm_frames_s16()
|
4467
|
+
drmp3_open_memory_and_read_f32() -> drmp3_open_memory_and_read_pcm_frames_f32()
|
4468
|
+
drmp3_open_memory_and_read_s16() -> drmp3_open_memory_and_read_pcm_frames_s16()
|
4469
|
+
drmp3_open_file_and_read_f32() -> drmp3_open_file_and_read_pcm_frames_f32()
|
4470
|
+
drmp3_open_file_and_read_s16() -> drmp3_open_file_and_read_pcm_frames_s16()
|
4471
|
+
*/
|
4472
|
+
|
4473
|
+
/*
|
4474
|
+
REVISION HISTORY
|
4475
|
+
================
|
4476
|
+
v0.6.32 - 2021-12-11
|
4477
|
+
- Fix a warning with Clang.
|
4478
|
+
|
4479
|
+
v0.6.31 - 2021-08-22
|
4480
|
+
- Fix a bug when loading from memory.
|
4481
|
+
|
4482
|
+
v0.6.30 - 2021-08-16
|
4483
|
+
- Silence some warnings.
|
4484
|
+
- Replace memory operations with DRMP3_* macros.
|
4485
|
+
|
4486
|
+
v0.6.29 - 2021-08-08
|
4487
|
+
- Bring up to date with minimp3.
|
4488
|
+
|
4489
|
+
v0.6.28 - 2021-07-31
|
4490
|
+
- Fix platform detection for ARM64.
|
4491
|
+
- Fix a compilation error with C89.
|
4492
|
+
|
4493
|
+
v0.6.27 - 2021-02-21
|
4494
|
+
- Fix a warning due to referencing _MSC_VER when it is undefined.
|
4495
|
+
|
4496
|
+
v0.6.26 - 2021-01-31
|
4497
|
+
- Bring up to date with minimp3.
|
4498
|
+
|
4499
|
+
v0.6.25 - 2020-12-26
|
4500
|
+
- Remove DRMP3_DEFAULT_CHANNELS and DRMP3_DEFAULT_SAMPLE_RATE which are leftovers from some removed APIs.
|
4501
|
+
|
4502
|
+
v0.6.24 - 2020-12-07
|
4503
|
+
- Fix a typo in version date for 0.6.23.
|
4504
|
+
|
4505
|
+
v0.6.23 - 2020-12-03
|
4506
|
+
- Fix an error where a file can be closed twice when initialization of the decoder fails.
|
4507
|
+
|
4508
|
+
v0.6.22 - 2020-12-02
|
4509
|
+
- Fix an error where it's possible for a file handle to be left open when initialization of the decoder fails.
|
4510
|
+
|
4511
|
+
v0.6.21 - 2020-11-28
|
4512
|
+
- Bring up to date with minimp3.
|
4513
|
+
|
4514
|
+
v0.6.20 - 2020-11-21
|
4515
|
+
- Fix compilation with OpenWatcom.
|
4516
|
+
|
4517
|
+
v0.6.19 - 2020-11-13
|
4518
|
+
- Minor code clean up.
|
4519
|
+
|
4520
|
+
v0.6.18 - 2020-11-01
|
4521
|
+
- Improve compiler support for older versions of GCC.
|
4522
|
+
|
4523
|
+
v0.6.17 - 2020-09-28
|
4524
|
+
- Bring up to date with minimp3.
|
4525
|
+
|
4526
|
+
v0.6.16 - 2020-08-02
|
4527
|
+
- Simplify sized types.
|
4528
|
+
|
4529
|
+
v0.6.15 - 2020-07-25
|
4530
|
+
- Fix a compilation warning.
|
4531
|
+
|
4532
|
+
v0.6.14 - 2020-07-23
|
4533
|
+
- Fix undefined behaviour with memmove().
|
4534
|
+
|
4535
|
+
v0.6.13 - 2020-07-06
|
4536
|
+
- Fix a bug when converting from s16 to f32 in drmp3_read_pcm_frames_f32().
|
2967
4537
|
|
4538
|
+
v0.6.12 - 2020-06-23
|
4539
|
+
- Add include guard for the implementation section.
|
4540
|
+
|
4541
|
+
v0.6.11 - 2020-05-26
|
4542
|
+
- Fix use of uninitialized variable error.
|
4543
|
+
|
4544
|
+
v0.6.10 - 2020-05-16
|
4545
|
+
- Add compile-time and run-time version querying.
|
4546
|
+
- DRMP3_VERSION_MINOR
|
4547
|
+
- DRMP3_VERSION_MAJOR
|
4548
|
+
- DRMP3_VERSION_REVISION
|
4549
|
+
- DRMP3_VERSION_STRING
|
4550
|
+
- drmp3_version()
|
4551
|
+
- drmp3_version_string()
|
4552
|
+
|
4553
|
+
v0.6.9 - 2020-04-30
|
4554
|
+
- Change the `pcm` parameter of drmp3dec_decode_frame() to a `const drmp3_uint8*` for consistency with internal APIs.
|
4555
|
+
|
4556
|
+
v0.6.8 - 2020-04-26
|
4557
|
+
- Optimizations to decoding when initializing from memory.
|
4558
|
+
|
4559
|
+
v0.6.7 - 2020-04-25
|
4560
|
+
- Fix a compilation error with DR_MP3_NO_STDIO
|
4561
|
+
- Optimization to decoding by reducing some data movement.
|
4562
|
+
|
4563
|
+
v0.6.6 - 2020-04-23
|
4564
|
+
- Fix a minor bug with the running PCM frame counter.
|
4565
|
+
|
4566
|
+
v0.6.5 - 2020-04-19
|
4567
|
+
- Fix compilation error on ARM builds.
|
4568
|
+
|
4569
|
+
v0.6.4 - 2020-04-19
|
4570
|
+
- Bring up to date with changes to minimp3.
|
4571
|
+
|
4572
|
+
v0.6.3 - 2020-04-13
|
4573
|
+
- Fix some pedantic warnings.
|
4574
|
+
|
4575
|
+
v0.6.2 - 2020-04-10
|
4576
|
+
- Fix a crash in drmp3_open_*_and_read_pcm_frames_*() if the output config object is NULL.
|
4577
|
+
|
4578
|
+
v0.6.1 - 2020-04-05
|
4579
|
+
- Fix warnings.
|
4580
|
+
|
4581
|
+
v0.6.0 - 2020-04-04
|
4582
|
+
- API CHANGE: Remove the pConfig parameter from the following APIs:
|
4583
|
+
- drmp3_init()
|
4584
|
+
- drmp3_init_memory()
|
4585
|
+
- drmp3_init_file()
|
4586
|
+
- Add drmp3_init_file_w() for opening a file from a wchar_t encoded path.
|
4587
|
+
|
4588
|
+
v0.5.6 - 2020-02-12
|
4589
|
+
- Bring up to date with minimp3.
|
4590
|
+
|
4591
|
+
v0.5.5 - 2020-01-29
|
4592
|
+
- Fix a memory allocation bug in high level s16 decoding APIs.
|
4593
|
+
|
4594
|
+
v0.5.4 - 2019-12-02
|
4595
|
+
- Fix a possible null pointer dereference when using custom memory allocators for realloc().
|
4596
|
+
|
4597
|
+
v0.5.3 - 2019-11-14
|
4598
|
+
- Fix typos in documentation.
|
4599
|
+
|
4600
|
+
v0.5.2 - 2019-11-02
|
4601
|
+
- Bring up to date with minimp3.
|
4602
|
+
|
4603
|
+
v0.5.1 - 2019-10-08
|
4604
|
+
- Fix a warning with GCC.
|
4605
|
+
|
4606
|
+
v0.5.0 - 2019-10-07
|
4607
|
+
- API CHANGE: Add support for user defined memory allocation routines. This system allows the program to specify their own memory allocation
|
4608
|
+
routines with a user data pointer for client-specific contextual data. This adds an extra parameter to the end of the following APIs:
|
4609
|
+
- drmp3_init()
|
4610
|
+
- drmp3_init_file()
|
4611
|
+
- drmp3_init_memory()
|
4612
|
+
- drmp3_open_and_read_pcm_frames_f32()
|
4613
|
+
- drmp3_open_and_read_pcm_frames_s16()
|
4614
|
+
- drmp3_open_memory_and_read_pcm_frames_f32()
|
4615
|
+
- drmp3_open_memory_and_read_pcm_frames_s16()
|
4616
|
+
- drmp3_open_file_and_read_pcm_frames_f32()
|
4617
|
+
- drmp3_open_file_and_read_pcm_frames_s16()
|
4618
|
+
- API CHANGE: Renamed the following APIs:
|
4619
|
+
- drmp3_open_and_read_f32() -> drmp3_open_and_read_pcm_frames_f32()
|
4620
|
+
- drmp3_open_and_read_s16() -> drmp3_open_and_read_pcm_frames_s16()
|
4621
|
+
- drmp3_open_memory_and_read_f32() -> drmp3_open_memory_and_read_pcm_frames_f32()
|
4622
|
+
- drmp3_open_memory_and_read_s16() -> drmp3_open_memory_and_read_pcm_frames_s16()
|
4623
|
+
- drmp3_open_file_and_read_f32() -> drmp3_open_file_and_read_pcm_frames_f32()
|
4624
|
+
- drmp3_open_file_and_read_s16() -> drmp3_open_file_and_read_pcm_frames_s16()
|
4625
|
+
|
4626
|
+
v0.4.7 - 2019-07-28
|
4627
|
+
- Fix a compiler error.
|
4628
|
+
|
4629
|
+
v0.4.6 - 2019-06-14
|
4630
|
+
- Fix a compiler error.
|
4631
|
+
|
4632
|
+
v0.4.5 - 2019-06-06
|
4633
|
+
- Bring up to date with minimp3.
|
4634
|
+
|
4635
|
+
v0.4.4 - 2019-05-06
|
4636
|
+
- Fixes to the VC6 build.
|
4637
|
+
|
4638
|
+
v0.4.3 - 2019-05-05
|
4639
|
+
- Use the channel count and/or sample rate of the first MP3 frame instead of DRMP3_DEFAULT_CHANNELS and
|
4640
|
+
DRMP3_DEFAULT_SAMPLE_RATE when they are set to 0. To use the old behaviour, just set the relevant property to
|
4641
|
+
DRMP3_DEFAULT_CHANNELS or DRMP3_DEFAULT_SAMPLE_RATE.
|
4642
|
+
- Add s16 reading APIs
|
4643
|
+
- drmp3_read_pcm_frames_s16
|
4644
|
+
- drmp3_open_memory_and_read_pcm_frames_s16
|
4645
|
+
- drmp3_open_and_read_pcm_frames_s16
|
4646
|
+
- drmp3_open_file_and_read_pcm_frames_s16
|
4647
|
+
- Add drmp3_get_mp3_and_pcm_frame_count() to the public header section.
|
4648
|
+
- Add support for C89.
|
4649
|
+
- Change license to choice of public domain or MIT-0.
|
4650
|
+
|
4651
|
+
v0.4.2 - 2019-02-21
|
4652
|
+
- Fix a warning.
|
4653
|
+
|
4654
|
+
v0.4.1 - 2018-12-30
|
4655
|
+
- Fix a warning.
|
4656
|
+
|
4657
|
+
v0.4.0 - 2018-12-16
|
4658
|
+
- API CHANGE: Rename some APIs:
|
4659
|
+
- drmp3_read_f32 -> to drmp3_read_pcm_frames_f32
|
4660
|
+
- drmp3_seek_to_frame -> drmp3_seek_to_pcm_frame
|
4661
|
+
- drmp3_open_and_decode_f32 -> drmp3_open_and_read_pcm_frames_f32
|
4662
|
+
- drmp3_open_and_decode_memory_f32 -> drmp3_open_memory_and_read_pcm_frames_f32
|
4663
|
+
- drmp3_open_and_decode_file_f32 -> drmp3_open_file_and_read_pcm_frames_f32
|
4664
|
+
- Add drmp3_get_pcm_frame_count().
|
4665
|
+
- Add drmp3_get_mp3_frame_count().
|
4666
|
+
- Improve seeking performance.
|
4667
|
+
|
4668
|
+
v0.3.2 - 2018-09-11
|
4669
|
+
- Fix a couple of memory leaks.
|
4670
|
+
- Bring up to date with minimp3.
|
4671
|
+
|
4672
|
+
v0.3.1 - 2018-08-25
|
4673
|
+
- Fix C++ build.
|
4674
|
+
|
4675
|
+
v0.3.0 - 2018-08-25
|
4676
|
+
- Bring up to date with minimp3. This has a minor API change: the "pcm" parameter of drmp3dec_decode_frame() has
|
4677
|
+
been changed from short* to void* because it can now output both s16 and f32 samples, depending on whether or
|
4678
|
+
not the DR_MP3_FLOAT_OUTPUT option is set.
|
4679
|
+
|
4680
|
+
v0.2.11 - 2018-08-08
|
4681
|
+
- Fix a bug where the last part of a file is not read.
|
4682
|
+
|
4683
|
+
v0.2.10 - 2018-08-07
|
4684
|
+
- Improve 64-bit detection.
|
4685
|
+
|
4686
|
+
v0.2.9 - 2018-08-05
|
4687
|
+
- Fix C++ build on older versions of GCC.
|
4688
|
+
- Bring up to date with minimp3.
|
4689
|
+
|
4690
|
+
v0.2.8 - 2018-08-02
|
4691
|
+
- Fix compilation errors with older versions of GCC.
|
4692
|
+
|
4693
|
+
v0.2.7 - 2018-07-13
|
4694
|
+
- Bring up to date with minimp3.
|
4695
|
+
|
4696
|
+
v0.2.6 - 2018-07-12
|
4697
|
+
- Bring up to date with minimp3.
|
4698
|
+
|
4699
|
+
v0.2.5 - 2018-06-22
|
4700
|
+
- Bring up to date with minimp3.
|
4701
|
+
|
4702
|
+
v0.2.4 - 2018-05-12
|
4703
|
+
- Bring up to date with minimp3.
|
4704
|
+
|
4705
|
+
v0.2.3 - 2018-04-29
|
4706
|
+
- Fix TCC build.
|
4707
|
+
|
4708
|
+
v0.2.2 - 2018-04-28
|
4709
|
+
- Fix bug when opening a decoder from memory.
|
4710
|
+
|
4711
|
+
v0.2.1 - 2018-04-27
|
4712
|
+
- Efficiency improvements when the decoder reaches the end of the stream.
|
4713
|
+
|
4714
|
+
v0.2 - 2018-04-21
|
4715
|
+
- Bring up to date with minimp3.
|
4716
|
+
- Start using major.minor.revision versioning.
|
4717
|
+
|
4718
|
+
v0.1d - 2018-03-30
|
4719
|
+
- Bring up to date with minimp3.
|
4720
|
+
|
4721
|
+
v0.1c - 2018-03-11
|
4722
|
+
- Fix C++ build error.
|
4723
|
+
|
4724
|
+
v0.1b - 2018-03-07
|
4725
|
+
- Bring up to date with minimp3.
|
4726
|
+
|
4727
|
+
v0.1a - 2018-02-28
|
4728
|
+
- Fix compilation error on GCC/Clang.
|
4729
|
+
- Fix some warnings.
|
4730
|
+
|
4731
|
+
v0.1 - 2018-02-xx
|
4732
|
+
- Initial versioned release.
|
4733
|
+
*/
|
2968
4734
|
|
2969
4735
|
/*
|
4736
|
+
This software is available as a choice of the following licenses. Choose
|
4737
|
+
whichever you prefer.
|
4738
|
+
|
4739
|
+
===============================================================================
|
4740
|
+
ALTERNATIVE 1 - Public Domain (www.unlicense.org)
|
4741
|
+
===============================================================================
|
2970
4742
|
This is free and unencumbered software released into the public domain.
|
2971
4743
|
|
2972
|
-
Anyone is free to copy, modify, publish, use, compile, sell, or
|
2973
|
-
|
2974
|
-
|
2975
|
-
|
2976
|
-
|
2977
|
-
|
2978
|
-
|
2979
|
-
|
2980
|
-
|
2981
|
-
|
2982
|
-
|
2983
|
-
|
2984
|
-
|
2985
|
-
|
2986
|
-
|
2987
|
-
|
2988
|
-
|
2989
|
-
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
|
2990
|
-
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
2991
|
-
OTHER DEALINGS IN THE SOFTWARE.
|
4744
|
+
Anyone is free to copy, modify, publish, use, compile, sell, or distribute this
|
4745
|
+
software, either in source code form or as a compiled binary, for any purpose,
|
4746
|
+
commercial or non-commercial, and by any means.
|
4747
|
+
|
4748
|
+
In jurisdictions that recognize copyright laws, the author or authors of this
|
4749
|
+
software dedicate any and all copyright interest in the software to the public
|
4750
|
+
domain. We make this dedication for the benefit of the public at large and to
|
4751
|
+
the detriment of our heirs and successors. We intend this dedication to be an
|
4752
|
+
overt act of relinquishment in perpetuity of all present and future rights to
|
4753
|
+
this software under copyright law.
|
4754
|
+
|
4755
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
4756
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
4757
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
4758
|
+
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
4759
|
+
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
4760
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
2992
4761
|
|
2993
4762
|
For more information, please refer to <http://unlicense.org/>
|
4763
|
+
|
4764
|
+
===============================================================================
|
4765
|
+
ALTERNATIVE 2 - MIT No Attribution
|
4766
|
+
===============================================================================
|
4767
|
+
Copyright 2020 David Reid
|
4768
|
+
|
4769
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4770
|
+
this software and associated documentation files (the "Software"), to deal in
|
4771
|
+
the Software without restriction, including without limitation the rights to
|
4772
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
4773
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
4774
|
+
so.
|
4775
|
+
|
4776
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
4777
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
4778
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
4779
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
4780
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
4781
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
4782
|
+
SOFTWARE.
|
2994
4783
|
*/
|
2995
4784
|
|
2996
4785
|
/*
|