├── .gitignore ├── README.md ├── examples └── hello_world.cpp ├── extras ├── dr_flac.h ├── dr_wav.h ├── stb_image.h └── stb_vorbis.c ├── mintaro.h ├── resources └── branding │ └── mintaro_logo1.png └── tools ├── mintaro_gen_font.c ├── mintaro_gen_law.c └── mintaro_gen_palette.c /.gitignore: -------------------------------------------------------------------------------- 1 | data/ 2 | docs/ 3 | tools/out/ 4 | website/ -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Mintaro](http://dred.io/img/mintaro_wide.png) 2 | 3 | Mintaro is a tiny framework for making simple, retro style games. It's not intended to be 4 | a full-featured game engine, but is instead focused on simplicity and just making it fun 5 | to make simple games. 6 | 7 | C/C++, single file, public domain. 8 | 9 | 10 | Features 11 | ======== 12 | - A single file with optional dependencies to extend functionality. 13 | - No external dependencies except for the standard library and necessary platform libraries 14 | like XLib and Win32. 15 | - Software rendering, with up to 256 colors and a customizable palette. 16 | - Uncapped framerate. 17 | - Custom resolutions of any dimensions. 18 | - 8 buttons of input 19 | - Up, down, left, right 20 | - A, B 21 | - Start, Select 22 | - Sound groups with independant volume controls. 23 | - A simple API. 24 | - Supports Windows and Linux. 25 | 26 | 27 | Features Coming Soon 28 | ==================== 29 | - Fullscreen mode 30 | - Line rasterization 31 | - Triangle rasterization (solid and textured) 32 | - Rotated sprites 33 | - More optimizations, especially for graphics 34 | - More platforms 35 | - More flexibility for input: 36 | - Support for binding different keys to the same button. 37 | - Support for general keyboard controls (in addition to buttons) 38 | - Support for mouse controls 39 | - Support for 360 controllers 40 | 41 | 42 | Usage 43 | ===== 44 | Mintaro is a single-file library. To use it, just #include "mintaro.h" like you would any other 45 | header file and then in one source file do the following: 46 | 47 | #define MINTARO_IMPLEMENTATION 48 | #include "mintaro.h" 49 | 50 | Make sure you don't define the implementation in more than one translation unit. 51 | 52 | Mintaro includes a built-in loader for TGA images, but you can enable loading of additional 53 | formats via stb_image by simply including it before the implementation of Mintaro, like this: 54 | 55 | #define STB_IMAGE_IMPLEMENTATION 56 | #include "stb_image.h" 57 | 58 | #define MINTARO_IMPLEMENTATION 59 | #include "mintaro.h" 60 | 61 | A copy of stb_image.h is included in the "extras" directory. 62 | 63 | Mintaro includes a built-in loader for WAV sounds, but you can enable loading of Vorbis and FLAC 64 | sounds by #including stb_vorbis.c and/or dr_flac.h before the implementation of Mintaro, in the 65 | same was as mentioned above for stb_image.h. 66 | 67 | 68 | Examples 69 | ======== 70 | Mintaro is focused on simplicity. Here's a quick example. 71 | 72 | ```c 73 | void on_step(mo_context* pContext, double dt) 74 | { 75 | // Input. 76 | if (mo_was_button_pressed(MO_BUTTON_A)) { 77 | if (CanShoot()) { 78 | mo_play_sound_source(pContext, pGunshotSoundSource); 79 | } 80 | } 81 | 82 | // Simulation. 83 | UpdateCharacter(dt); 84 | 85 | // Drawing. 86 | mo_clear(pContext, clearColorIndex); 87 | mo_draw_image(pContext, characterPosX, characterPosY, pCharacterSpriteSheet, 0, 0, 64, 64); 88 | mo_draw_textf(pContext, 0, 0, textColorIndex, "Health: %d", characterHealth); 89 | } 90 | 91 | int main() 92 | { 93 | mo_context* pContext; 94 | if (mo_init(NULL, "My Game's Name", on_step, pUserData, &pContext) { 95 | return -1; 96 | } 97 | 98 | // Load some resources. 99 | mo_sound_source* pMusicSource; 100 | mo_sound_source_load(pContext, "data/my_music.wav", &pMusic); 101 | 102 | mo_image* pCharacterSpriteSheet; 103 | mo_image_load(pContext, "data/character.tga", &pCharacterSpriteSheet); 104 | 105 | 106 | // Play some music in a loop. 107 | mo_sound* pMusic; 108 | mo_sound_create(pContext, pMusicSource, NULL, &pMusic); 109 | mo_sound_play(pMusic, MO_TRUE); 110 | 111 | int result = mo_run(pContext); 112 | 113 | mo_uninit(pContext); 114 | return result; 115 | } 116 | ``` 117 | 118 | You can also find a "Hello, World!" example in the "examples" folder. -------------------------------------------------------------------------------- /examples/hello_world.cpp: -------------------------------------------------------------------------------- 1 | // To build this example: 2 | // 3 | // VC++: Just create an empty project and add this file. No need to link to anything. 4 | // GCC/Clang (Windows) g++ hello_world.cpp -lgdi32 5 | // GCC/Clang (Linux) g++ hello_world.cpp -lX11 -lXext -lasound -lpthread -lm 6 | // 7 | // You shouldn't need to install anything other than the development packages for X11, pthreads, etc., but they 8 | // should all be ubiquitous and quite easy to install. 9 | 10 | #define MINTARO_IMPLEMENTATION 11 | #include "../mintaro.h" 12 | 13 | #define SCREEN_MAIN_MENU 0 14 | #define SCREEN_IN_GAME 1 15 | #define SCREEN_OPTIONS 2 16 | int g_CurrentScreen = SCREEN_MAIN_MENU; 17 | 18 | int g_FocusedMenuItem = 0; 19 | 20 | float g_PlayerPosX = 0; 21 | float g_PlayerPosY = 0; 22 | 23 | void example1_on_step(mo_context* pContext, double dt) 24 | { 25 | int black = mo_find_closest_color(pContext, mo_make_rgb(0, 0, 0)); 26 | int white = mo_find_closest_color(pContext, mo_make_rgb(255, 255, 255)); 27 | int blue = mo_find_closest_color(pContext, mo_make_rgb(128, 192, 255)); 28 | 29 | mo_clear(pContext, black); 30 | 31 | switch (g_CurrentScreen) 32 | { 33 | case SCREEN_MAIN_MENU: 34 | { 35 | // Input. 36 | if (mo_was_button_pressed(pContext, MO_BUTTON_DOWN)) { 37 | g_FocusedMenuItem = (g_FocusedMenuItem + 1) % 3; 38 | } 39 | if (mo_was_button_pressed(pContext, MO_BUTTON_UP)) { 40 | if (g_FocusedMenuItem == 0) { 41 | g_FocusedMenuItem = 2; 42 | } else { 43 | g_FocusedMenuItem -= 1; 44 | } 45 | } 46 | 47 | if (mo_was_button_pressed(pContext, MO_BUTTON_A) || mo_was_button_pressed(pContext, MO_BUTTON_START)) { 48 | if (g_FocusedMenuItem == 0) { 49 | g_PlayerPosX = (float)(pContext->profile.resolutionX/2 - 16); 50 | g_PlayerPosY = (float)(pContext->profile.resolutionY/2 - 16); 51 | g_CurrentScreen = SCREEN_IN_GAME; 52 | } else if (g_FocusedMenuItem == 1) { 53 | g_CurrentScreen = SCREEN_OPTIONS; 54 | } else if (g_FocusedMenuItem == 2) { 55 | mo_close(pContext); 56 | } 57 | } 58 | 59 | // Graphics. 60 | int caretPosX = 4; 61 | int caretPosY = 8 + (12*g_FocusedMenuItem); 62 | mo_draw_text(pContext, caretPosX, caretPosY, white, ">"); 63 | mo_draw_text(pContext, 15, 8+12*0, white, "Start Game"); 64 | mo_draw_text(pContext, 15, 8+12*1, white, "Options"); 65 | mo_draw_text(pContext, 15, 8+12*2, white, "Quit"); 66 | } break; 67 | 68 | case SCREEN_IN_GAME: 69 | { 70 | // Input. 71 | if (mo_is_button_down(pContext, MO_BUTTON_LEFT)) { 72 | g_PlayerPosX -= (float)(100 * dt); 73 | } 74 | if (mo_is_button_down(pContext, MO_BUTTON_RIGHT)) { 75 | g_PlayerPosX += (float)(100 * dt); 76 | } 77 | if (mo_is_button_down(pContext, MO_BUTTON_UP)) { 78 | g_PlayerPosY -= (float)(100 * dt); 79 | } 80 | if (mo_is_button_down(pContext, MO_BUTTON_DOWN)) { 81 | g_PlayerPosY += (float)(100 * dt); 82 | } 83 | 84 | if (mo_was_button_pressed(pContext, MO_BUTTON_START)) { 85 | g_CurrentScreen = SCREEN_MAIN_MENU; 86 | } 87 | 88 | // Graphics. 89 | mo_draw_quad(pContext, (int)g_PlayerPosX, (int)g_PlayerPosY, 32, 32, blue); 90 | mo_draw_textf(pContext, 4, 4, white, "FPS: %u", (unsigned int)(1/dt)); 91 | } break; 92 | 93 | case SCREEN_OPTIONS: 94 | { 95 | // Input. 96 | if (mo_was_button_pressed(pContext, MO_BUTTON_B)) { 97 | g_CurrentScreen = SCREEN_MAIN_MENU; 98 | } 99 | 100 | // Graphics. 101 | mo_draw_text(pContext, 8, 8, white, "OPTIONS"); 102 | mo_draw_text(pContext, 8, 30, white, "Press 'X' to go"); 103 | mo_draw_text(pContext, 8, 39, white, "back"); 104 | } break; 105 | 106 | default: 107 | { 108 | // Unknown state. 109 | } break; 110 | } 111 | } 112 | 113 | int main(int argc, char** argv) 114 | { 115 | (void)argc; 116 | (void)argv; 117 | 118 | mo_context* pContext; 119 | if (mo_init(NULL, 160*2, 144*2, "Hello, World!", example1_on_step, NULL, &pContext) != MO_SUCCESS) { 120 | return -1; 121 | } 122 | 123 | int result = mo_run(pContext); 124 | 125 | mo_uninit(pContext); 126 | return result; 127 | } 128 | -------------------------------------------------------------------------------- /extras/dr_wav.h: -------------------------------------------------------------------------------- 1 | // WAV audio loader. Public domain. See "unlicense" statement at the end of this file. 2 | // dr_wav - v0.5b - 2016-10-23 3 | // 4 | // David Reid - mackron@gmail.com 5 | 6 | // USAGE 7 | // 8 | // This is a single-file library. To use it, do something like the following in one .c file. 9 | // #define DR_WAV_IMPLEMENTATION 10 | // #include "dr_wav.h" 11 | // 12 | // You can then #include this file in other parts of the program as you would with any other header file. Do something 13 | // like the following to read audio data: 14 | // 15 | // drwav wav; 16 | // if (!drwav_init_file(&wav, "my_song.wav")) { 17 | // // Error opening WAV file. 18 | // } 19 | // 20 | // int32_t* pDecodedInterleavedSamples = malloc(wav.totalSampleCount * sizeof(int32_t)); 21 | // size_t numberOfSamplesActuallyDecoded = drwav_read_s32(&wav, wav.totalSampleCount, pDecodedInterleavedSamples); 22 | // 23 | // ... 24 | // 25 | // drwav_uninit(&wav); 26 | // 27 | // You can also use drwav_open() to allocate and initialize the loader for you: 28 | // 29 | // drwav* pWav = drwav_open_file("my_song.wav"); 30 | // if (pWav == NULL) { 31 | // // Error opening WAV file. 32 | // } 33 | // 34 | // ... 35 | // 36 | // drwav_close(pWav); 37 | // 38 | // If you just want to quickly open and read the audio data in a single operation you can do something like this: 39 | // 40 | // unsigned int channels; 41 | // unsigned int sampleRate; 42 | // uint64_t totalSampleCount; 43 | // float* pSampleData = drwav_open_and_read_file_s32("my_song.wav", &channels, &sampleRate, &totalSampleCount); 44 | // if (pSampleData == NULL) { 45 | // // Error opening and reading WAV file. 46 | // } 47 | // 48 | // ... 49 | // 50 | // drwav_free(pSampleData); 51 | // 52 | // The examples above use versions of the API that convert the audio data to a consistent format (32-bit signed PCM, in 53 | // this case), but you can still output the audio data in it's internal format (see notes below for supported formats): 54 | // 55 | // size_t samplesRead = drwav_read(&wav, wav.totalSampleCount, pDecodedInterleavedSamples); 56 | // 57 | // You can also read the raw bytes of audio data, which could be useful if dr_wav does not have native support for 58 | // a particular data format: 59 | // 60 | // size_t bytesRead = drwav_read_raw(&wav, bytesToRead, pRawDataBuffer); 61 | // 62 | // 63 | // dr_wav has seamless support the Sony Wave64 format. The decoder will automatically detect it and it should Just Work 64 | // without any manual intervention. 65 | // 66 | // 67 | // 68 | // OPTIONS 69 | // #define these options before including this file. 70 | // 71 | // #define DR_WAV_NO_CONVERSION_API 72 | // Disables conversion APIs such as drwav_read_f32() and drwav_s16_to_f32(). 73 | // 74 | // #define DR_WAV_NO_STDIO 75 | // Disables drwav_open_file(). 76 | // 77 | // 78 | // 79 | // QUICK NOTES 80 | // - Samples are always interleaved. 81 | // - The default read function does not do any data conversion. Use drwav_read_f32() to read and convert audio data 82 | // to IEEE 32-bit floating point samples. Likewise, use drwav_read_s32() to read and convert auto data to signed 83 | // 32-bit PCM. Tested and supported internal formats include the following: 84 | // - Unsigned 8-bit PCM 85 | // - Signed 12-bit PCM 86 | // - Signed 16-bit PCM 87 | // - Signed 24-bit PCM 88 | // - Signed 32-bit PCM 89 | // - IEEE 32-bit floating point. 90 | // - IEEE 64-bit floating point. 91 | // - A-law and u-law 92 | // - Microsoft ADPCM is not currently supported. 93 | // - dr_wav will try to read the WAV file as best it can, even if it's not strictly conformant to the WAV format. 94 | 95 | 96 | #ifndef dr_wav_h 97 | #define dr_wav_h 98 | 99 | #include 100 | #include 101 | 102 | #ifndef DR_SIZED_TYPES_DEFINED 103 | #define DR_SIZED_TYPES_DEFINED 104 | #if defined(_MSC_VER) && _MSC_VER < 1600 105 | typedef signed char dr_int8; 106 | typedef unsigned char dr_uint8; 107 | typedef signed short dr_int16; 108 | typedef unsigned short dr_uint16; 109 | typedef signed int dr_int32; 110 | typedef unsigned int dr_uint32; 111 | typedef signed __int64 dr_int64; 112 | typedef unsigned __int64 dr_uint64; 113 | #else 114 | #include 115 | typedef int8_t dr_int8; 116 | typedef uint8_t dr_uint8; 117 | typedef int16_t dr_int16; 118 | typedef uint16_t dr_uint16; 119 | typedef int32_t dr_int32; 120 | typedef uint32_t dr_uint32; 121 | typedef int64_t dr_int64; 122 | typedef uint64_t dr_uint64; 123 | #endif 124 | typedef dr_int8 dr_bool8; 125 | typedef dr_int32 dr_bool32; 126 | #define DR_TRUE 1 127 | #define DR_FALSE 0 128 | #endif 129 | 130 | #ifdef __cplusplus 131 | extern "C" { 132 | #endif 133 | 134 | // Common data formats. 135 | #define DR_WAVE_FORMAT_PCM 0x1 136 | #define DR_WAVE_FORMAT_ADPCM 0x2 // Not currently supported. 137 | #define DR_WAVE_FORMAT_IEEE_FLOAT 0x3 138 | #define DR_WAVE_FORMAT_ALAW 0x6 139 | #define DR_WAVE_FORMAT_MULAW 0x7 140 | #define DR_WAVE_FORMAT_EXTENSIBLE 0xFFFE 141 | 142 | typedef enum 143 | { 144 | drwav_seek_origin_start, 145 | drwav_seek_origin_current 146 | } drwav_seek_origin; 147 | 148 | typedef enum 149 | { 150 | drwav_container_riff, 151 | drwav_container_w64 152 | } drwav_container; 153 | 154 | // Callback for when data is read. Return value is the number of bytes actually read. 155 | typedef size_t (* drwav_read_proc)(void* pUserData, void* pBufferOut, size_t bytesToRead); 156 | 157 | // Callback for when data needs to be seeked. Offset is always relative to the current position. Return value 158 | // is DR_TRUE on success; fale on failure. 159 | typedef dr_bool32 (* drwav_seek_proc)(void* pUserData, int offset, drwav_seek_origin origin); 160 | 161 | // Structure for internal use. Only used for loaders opened with drwav_open_memory. 162 | typedef struct 163 | { 164 | const unsigned char* data; 165 | size_t dataSize; 166 | size_t currentReadPos; 167 | } drwav__memory_stream; 168 | 169 | typedef struct 170 | { 171 | // The format tag exactly as specified in the wave file's "fmt" chunk. This can be used by applications 172 | // that require support for data formats not natively supported by dr_wav. 173 | unsigned short formatTag; 174 | 175 | // The number of channels making up the audio data. When this is set to 1 it is mono, 2 is stereo, etc. 176 | unsigned short channels; 177 | 178 | // The sample rate. Usually set to something like 44100. 179 | unsigned int sampleRate; 180 | 181 | // Average bytes per second. You probably don't need this, but it's left here for informational purposes. 182 | unsigned int avgBytesPerSec; 183 | 184 | // Block align. This is equal to the number of channels * bytes per sample. 185 | unsigned short blockAlign; 186 | 187 | // Bit's per sample. 188 | unsigned short bitsPerSample; 189 | 190 | // The size of the extended data. Only used internally for validation, but left here for informational purposes. 191 | unsigned short extendedSize; 192 | 193 | // The number of valid bits per sample. When is equal to WAVE_FORMAT_EXTENSIBLE, 194 | // is always rounded up to the nearest multiple of 8. This variable contains information about exactly how 195 | // many bits a valid per sample. Mainly used for informational purposes. 196 | unsigned short validBitsPerSample; 197 | 198 | // The channel mask. Not used at the moment. 199 | unsigned int channelMask; 200 | 201 | // The sub-format, exactly as specified by the wave file. 202 | unsigned char subFormat[16]; 203 | 204 | } drwav_fmt; 205 | 206 | typedef struct 207 | { 208 | // A pointer to the function to call when more data is needed. 209 | drwav_read_proc onRead; 210 | 211 | // A pointer to the function to call when the wav file needs to be seeked. 212 | drwav_seek_proc onSeek; 213 | 214 | // The user data to pass to callbacks. 215 | void* pUserData; 216 | 217 | 218 | // Whether or not the WAV file is formatted as a standard RIFF file or W64. 219 | drwav_container container; 220 | 221 | 222 | // Structure containing format information exactly as specified by the wav file. 223 | drwav_fmt fmt; 224 | 225 | // The sample rate. Will be set to something like 44100. 226 | unsigned int sampleRate; 227 | 228 | // The number of channels. This will be set to 1 for monaural streams, 2 for stereo, etc. 229 | unsigned short channels; 230 | 231 | // The bits per sample. Will be set to somthing like 16, 24, etc. 232 | unsigned short bitsPerSample; 233 | 234 | // The number of bytes per sample. 235 | unsigned short bytesPerSample; 236 | 237 | // Equal to fmt.formatTag, or the value specified by fmt.subFormat if fmt.formatTag is equal to 65534 (WAVE_FORMAT_EXTENSIBLE). 238 | unsigned short translatedFormatTag; 239 | 240 | // The total number of samples making up the audio data. Use * to calculate 241 | // the required size of a buffer to hold the entire audio data. 242 | uint64_t totalSampleCount; 243 | 244 | 245 | // The number of bytes remaining in the data chunk. 246 | uint64_t bytesRemaining; 247 | 248 | 249 | // A hack to avoid a malloc() when opening a decoder with drwav_open_memory(). 250 | drwav__memory_stream memoryStream; 251 | 252 | } drwav; 253 | 254 | 255 | // Initializes a pre-allocated drwav object. 256 | // 257 | // 258 | dr_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData); 259 | 260 | // Uninitializes the given drwav object. Use this only for objects initialized with drwav_init(). 261 | void drwav_uninit(drwav* pWav); 262 | 263 | 264 | // Opens a .wav file using the given callbacks. 265 | // 266 | // Returns null on error. Close the loader with drwav_close(). 267 | drwav* drwav_open(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData); 268 | 269 | // Closes the given drwav object. Use this only for objects created with drwav_open(). 270 | void drwav_close(drwav* pWav); 271 | 272 | 273 | // Reads raw audio data. 274 | // 275 | // This is the lowest level function for reading audio data. It simply reads the given number of 276 | // bytes of the raw internal sample data. 277 | // 278 | // Returns the number of bytes actually read. 279 | size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut); 280 | 281 | // Reads a chunk of audio data in the native internal format. 282 | // 283 | // This is typically the most efficient way to retrieve audio data, but it does not do any format 284 | // conversions which means you'll need to convert the data manually if required. 285 | // 286 | // If the return value is less than it means the end of the file has been reached or 287 | // you have requested more samples than can possibly fit in the output buffer. 288 | // 289 | // This function will only work when sample data is of a fixed size. If you are using an unusual 290 | // format which uses variable sized samples, consider using drwav_read_raw(), but don't combine them. 291 | uint64_t drwav_read(drwav* pWav, uint64_t samplesToRead, void* pBufferOut); 292 | 293 | // Seeks to the given sample. 294 | // 295 | // The return value is DR_FALSE if an error occurs, DR_TRUE if successful. 296 | dr_bool32 drwav_seek_to_sample(drwav* pWav, uint64_t sample); 297 | 298 | 299 | 300 | //// Convertion Utilities //// 301 | #ifndef DR_WAV_NO_CONVERSION_API 302 | 303 | // Reads a chunk of audio data and converts it to IEEE 32-bit floating point samples. 304 | // 305 | // Returns the number of samples actually read. 306 | // 307 | // If the return value is less than it means the end of the file has been reached. 308 | uint64_t drwav_read_f32(drwav* pWav, uint64_t samplesToRead, float* pBufferOut); 309 | 310 | // Low-level function for converting unsigned 8-bit PCM samples to IEEE 32-bit floating point samples. 311 | void drwav_u8_to_f32(float* pOut, const uint8_t* pIn, size_t sampleCount); 312 | 313 | // Low-level function for converting signed 16-bit PCM samples to IEEE 32-bit floating point samples. 314 | void drwav_s16_to_f32(float* pOut, const int16_t* pIn, size_t sampleCount); 315 | 316 | // Low-level function for converting signed 24-bit PCM samples to IEEE 32-bit floating point samples. 317 | void drwav_s24_to_f32(float* pOut, const uint8_t* pIn, size_t sampleCount); 318 | 319 | // Low-level function for converting signed 32-bit PCM samples to IEEE 32-bit floating point samples. 320 | void drwav_s32_to_f32(float* pOut, const int32_t* pIn, size_t sampleCount); 321 | 322 | // Low-level function for converting IEEE 64-bit floating point samples to IEEE 32-bit floating point samples. 323 | void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount); 324 | 325 | // Low-level function for converting A-law samples to IEEE 32-bit floating point samples. 326 | void drwav_alaw_to_f32(float* pOut, const uint8_t* pIn, size_t sampleCount); 327 | 328 | // Low-level function for converting u-law samples to IEEE 32-bit floating point samples. 329 | void drwav_ulaw_to_f32(float* pOut, const uint8_t* pIn, size_t sampleCount); 330 | 331 | 332 | // Reads a chunk of audio data and converts it to signed 32-bit PCM samples. 333 | // 334 | // Returns the number of samples actually read. 335 | // 336 | // If the return value is less than it means the end of the file has been reached. 337 | uint64_t drwav_read_s32(drwav* pWav, uint64_t samplesToRead, int32_t* pBufferOut); 338 | 339 | // Low-level function for converting unsigned 8-bit PCM samples to signed 32-bit PCM samples. 340 | void drwav_u8_to_s32(int32_t* pOut, const uint8_t* pIn, size_t sampleCount); 341 | 342 | // Low-level function for converting signed 16-bit PCM samples to signed 32-bit PCM samples. 343 | void drwav_s16_to_s32(int32_t* pOut, const int16_t* pIn, size_t sampleCount); 344 | 345 | // Low-level function for converting signed 24-bit PCM samples to signed 32-bit PCM samples. 346 | void drwav_s24_to_s32(int32_t* pOut, const uint8_t* pIn, size_t sampleCount); 347 | 348 | // Low-level function for converting IEEE 32-bit floating point samples to signed 32-bit PCM samples. 349 | void drwav_f32_to_s32(int32_t* pOut, const float* pIn, size_t sampleCount); 350 | 351 | // Low-level function for converting IEEE 64-bit floating point samples to signed 32-bit PCM samples. 352 | void drwav_f64_to_s32(int32_t* pOut, const double* pIn, size_t sampleCount); 353 | 354 | // Low-level function for converting A-law samples to signed 32-bit PCM samples. 355 | void drwav_alaw_to_s32(int32_t* pOut, const uint8_t* pIn, size_t sampleCount); 356 | 357 | // Low-level function for converting u-law samples to signed 32-bit PCM samples. 358 | void drwav_ulaw_to_s32(int32_t* pOut, const uint8_t* pIn, size_t sampleCount); 359 | 360 | #endif //DR_WAV_NO_CONVERSION_API 361 | 362 | 363 | //// High-Level Convenience Helpers //// 364 | 365 | #ifndef DR_WAV_NO_STDIO 366 | 367 | // Helper for initializing a wave file using stdio. 368 | // 369 | // This holds the internal FILE object until drwav_uninit() is called. Keep this in mind if you're caching drwav 370 | // objects because the operating system may restrict the number of file handles an application can have open at 371 | // any given time. 372 | dr_bool32 drwav_init_file(drwav* pWav, const char* filename); 373 | 374 | // Helper for opening a wave file using stdio. 375 | // 376 | // This holds the internal FILE object until drwav_close() is called. Keep this in mind if you're caching drwav 377 | // objects because the operating system may restrict the number of file handles an application can have open at 378 | // any given time. 379 | drwav* drwav_open_file(const char* filename); 380 | 381 | #endif //DR_WAV_NO_STDIO 382 | 383 | // Helper for initializing a file from a pre-allocated memory buffer. 384 | // 385 | // This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for 386 | // the lifetime of the drwav object. 387 | // 388 | // The buffer should contain the contents of the entire wave file, not just the sample data. 389 | dr_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize); 390 | 391 | // Helper for opening a file from a pre-allocated memory buffer. 392 | // 393 | // This does not create a copy of the data. It is up to the application to ensure the buffer remains valid for 394 | // the lifetime of the drwav object. 395 | // 396 | // The buffer should contain the contents of the entire wave file, not just the sample data. 397 | drwav* drwav_open_memory(const void* data, size_t dataSize); 398 | 399 | 400 | 401 | #ifndef DR_WAV_NO_CONVERSION_API 402 | // Opens and reads a wav file in a single operation. 403 | float* drwav_open_and_read_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount); 404 | int32_t* drwav_open_and_read_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount); 405 | #ifndef DR_WAV_NO_STDIO 406 | // Opens an decodes a wav file in a single operation. 407 | float* drwav_open_and_read_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount); 408 | int32_t* drwav_open_and_read_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount); 409 | #endif 410 | 411 | // Opens an decodes a wav file from a block of memory in a single operation. 412 | float* drwav_open_and_read_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount); 413 | int32_t* drwav_open_and_read_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount); 414 | #endif 415 | 416 | // Frees data that was allocated internally by dr_wav. 417 | void drwav_free(void* pDataReturnedByOpenAndRead); 418 | 419 | #ifdef __cplusplus 420 | } 421 | #endif 422 | #endif // dr_wav_h 423 | 424 | 425 | ///////////////////////////////////////////////////// 426 | // 427 | // IMPLEMENTATION 428 | // 429 | ///////////////////////////////////////////////////// 430 | 431 | #ifdef DR_WAV_IMPLEMENTATION 432 | #include 433 | #include // For memcpy() 434 | #include 435 | #include 436 | 437 | #ifndef DR_WAV_NO_STDIO 438 | #include 439 | #endif 440 | 441 | static const uint8_t drwavGUID_W64_RIFF[16] = {0x72,0x69,0x66,0x66, 0x2E,0x91, 0xCF,0x11, 0xA5,0xD6, 0x28,0xDB,0x04,0xC1,0x00,0x00}; // 66666972-912E-11CF-A5D6-28DB04C10000 442 | static const uint8_t drwavGUID_W64_WAVE[16] = {0x77,0x61,0x76,0x65, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 65766177-ACF3-11D3-8CD1-00C04F8EDB8A 443 | static const uint8_t drwavGUID_W64_FMT [16] = {0x66,0x6D,0x74,0x20, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 20746D66-ACF3-11D3-8CD1-00C04F8EDB8A 444 | static const uint8_t drwavGUID_W64_DATA[16] = {0x64,0x61,0x74,0x61, 0xF3,0xAC, 0xD3,0x11, 0x8C,0xD1, 0x00,0xC0,0x4F,0x8E,0xDB,0x8A}; // 61746164-ACF3-11D3-8CD1-00C04F8EDB8A 445 | 446 | static dr_bool32 drwav__guid_equal(const uint8_t a[16], const uint8_t b[16]) 447 | { 448 | const uint32_t* a32 = (const uint32_t*)a; 449 | const uint32_t* b32 = (const uint32_t*)b; 450 | 451 | return 452 | a32[0] == b32[0] && 453 | a32[1] == b32[1] && 454 | a32[2] == b32[2] && 455 | a32[3] == b32[3]; 456 | } 457 | 458 | static dr_bool32 drwav__fourcc_equal(const unsigned char* a, const char* b) 459 | { 460 | return 461 | a[0] == b[0] && 462 | a[1] == b[1] && 463 | a[2] == b[2] && 464 | a[3] == b[3]; 465 | } 466 | 467 | 468 | 469 | 470 | static int drwav__is_little_endian() 471 | { 472 | int n = 1; 473 | return (*(char*)&n) == 1; 474 | } 475 | 476 | static unsigned short drwav__bytes_to_u16(const unsigned char* data) 477 | { 478 | if (drwav__is_little_endian()) { 479 | return (data[0] << 0) | (data[1] << 8); 480 | } else { 481 | return (data[1] << 0) | (data[0] << 8); 482 | } 483 | } 484 | 485 | static unsigned int drwav__bytes_to_u32(const unsigned char* data) 486 | { 487 | if (drwav__is_little_endian()) { 488 | return (data[0] << 0) | (data[1] << 8) | (data[2] << 16) | (data[3] << 24); 489 | } else { 490 | return (data[3] << 0) | (data[2] << 8) | (data[1] << 16) | (data[0] << 24); 491 | } 492 | } 493 | 494 | static uint64_t drwav__bytes_to_u64(const unsigned char* data) 495 | { 496 | if (drwav__is_little_endian()) { 497 | return 498 | ((uint64_t)data[0] << 0ULL) | ((uint64_t)data[1] << 8ULL) | ((uint64_t)data[2] << 16ULL) | ((uint64_t)data[3] << 24ULL) | 499 | ((uint64_t)data[4] << 32ULL) | ((uint64_t)data[5] << 40ULL) | ((uint64_t)data[6] << 48ULL) | ((uint64_t)data[7] << 56ULL); 500 | } else { 501 | return 502 | ((uint64_t)data[7] << 0ULL) | ((uint64_t)data[6] << 8ULL) | ((uint64_t)data[5] << 16ULL) | ((uint64_t)data[4] << 24ULL) | 503 | ((uint64_t)data[3] << 32ULL) | ((uint64_t)data[2] << 40ULL) | ((uint64_t)data[1] << 48ULL) | ((uint64_t)data[0] << 56ULL); 504 | } 505 | } 506 | 507 | static void drwav__bytes_to_guid(const unsigned char* data, uint8_t* guid) 508 | { 509 | for (int i = 0; i < 16; ++i) { 510 | guid[i] = data[i]; 511 | } 512 | } 513 | 514 | 515 | typedef struct 516 | { 517 | union 518 | { 519 | uint8_t fourcc[4]; 520 | uint8_t guid[16]; 521 | } id; 522 | 523 | // The size in bytes of the chunk. 524 | uint64_t sizeInBytes; 525 | 526 | // RIFF = 2 byte alignment. 527 | // W64 = 8 byte alignment. 528 | unsigned int paddingSize; 529 | 530 | } drwav__chunk_header; 531 | 532 | static dr_bool32 drwav__read_chunk_header(drwav_read_proc onRead, void* pUserData, drwav_container container, drwav__chunk_header* pHeaderOut) 533 | { 534 | if (container == drwav_container_riff) { 535 | if (onRead(pUserData, pHeaderOut->id.fourcc, 4) != 4) { 536 | return DR_FALSE; 537 | } 538 | 539 | unsigned char sizeInBytes[4]; 540 | if (onRead(pUserData, sizeInBytes, 4) != 4) { 541 | return DR_FALSE; 542 | } 543 | 544 | pHeaderOut->sizeInBytes = drwav__bytes_to_u32(sizeInBytes); 545 | pHeaderOut->paddingSize = pHeaderOut->sizeInBytes % 2; 546 | } else { 547 | if (onRead(pUserData, pHeaderOut->id.guid, 16) != 16) { 548 | return DR_FALSE; 549 | } 550 | 551 | unsigned char sizeInBytes[8]; 552 | if (onRead(pUserData, sizeInBytes, 8) != 8) { 553 | return DR_FALSE; 554 | } 555 | 556 | pHeaderOut->sizeInBytes = drwav__bytes_to_u64(sizeInBytes) - 24; // <-- Subtract 24 because w64 includes the size of the header. 557 | pHeaderOut->paddingSize = pHeaderOut->sizeInBytes % 8; 558 | } 559 | 560 | return DR_TRUE; 561 | } 562 | 563 | 564 | static dr_bool32 drwav__read_fmt(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, drwav_container container, drwav_fmt* fmtOut) 565 | { 566 | drwav__chunk_header header; 567 | if (!drwav__read_chunk_header(onRead, pUserData, container, &header)) { 568 | return DR_FALSE; 569 | } 570 | 571 | // Validation. 572 | if (container == drwav_container_riff) { 573 | if (!drwav__fourcc_equal(header.id.fourcc, "fmt ")) { 574 | return DR_FALSE; 575 | } 576 | } else { 577 | if (!drwav__guid_equal(header.id.guid, drwavGUID_W64_FMT)) { 578 | return DR_FALSE; 579 | } 580 | } 581 | 582 | 583 | unsigned char fmt[16]; 584 | if (onRead(pUserData, fmt, sizeof(fmt)) != sizeof(fmt)) { 585 | return DR_FALSE; 586 | } 587 | 588 | fmtOut->formatTag = drwav__bytes_to_u16(fmt + 0); 589 | fmtOut->channels = drwav__bytes_to_u16(fmt + 2); 590 | fmtOut->sampleRate = drwav__bytes_to_u32(fmt + 4); 591 | fmtOut->avgBytesPerSec = drwav__bytes_to_u32(fmt + 8); 592 | fmtOut->blockAlign = drwav__bytes_to_u16(fmt + 12); 593 | fmtOut->bitsPerSample = drwav__bytes_to_u16(fmt + 14); 594 | 595 | fmtOut->extendedSize = 0; 596 | fmtOut->validBitsPerSample = 0; 597 | fmtOut->channelMask = 0; 598 | memset(fmtOut->subFormat, 0, sizeof(fmtOut->subFormat)); 599 | 600 | if (header.sizeInBytes > 16) { 601 | unsigned char fmt_cbSize[2]; 602 | if (onRead(pUserData, fmt_cbSize, sizeof(fmt_cbSize)) != sizeof(fmt_cbSize)) { 603 | return DR_FALSE; // Expecting more data. 604 | } 605 | 606 | int bytesReadSoFar = 18; 607 | 608 | fmtOut->extendedSize = drwav__bytes_to_u16(fmt_cbSize); 609 | if (fmtOut->extendedSize > 0) { 610 | if (fmtOut->extendedSize != 22) { 611 | return DR_FALSE; // The extended size should be equal to 22. 612 | } 613 | 614 | unsigned char fmtext[22]; 615 | if (onRead(pUserData, fmtext, sizeof(fmtext)) != sizeof(fmtext)) { 616 | return DR_FALSE; // Expecting more data. 617 | } 618 | 619 | fmtOut->validBitsPerSample = drwav__bytes_to_u16(fmtext + 0); 620 | fmtOut->channelMask = drwav__bytes_to_u32(fmtext + 2); 621 | drwav__bytes_to_guid(fmtext + 6, fmtOut->subFormat); 622 | 623 | bytesReadSoFar += 22; 624 | } 625 | 626 | // Seek past any leftover bytes. For w64 the leftover will be defined based on the chunk size. 627 | if (!onSeek(pUserData, (int)(header.sizeInBytes - bytesReadSoFar), drwav_seek_origin_current)) { 628 | return DR_FALSE; 629 | } 630 | } 631 | 632 | if (header.paddingSize > 0) { 633 | if (!onSeek(pUserData, header.paddingSize, drwav_seek_origin_current)) { 634 | return DR_FALSE; 635 | } 636 | } 637 | 638 | return DR_TRUE; 639 | } 640 | 641 | 642 | #ifndef DR_WAV_NO_STDIO 643 | static size_t drwav__on_read_stdio(void* pUserData, void* pBufferOut, size_t bytesToRead) 644 | { 645 | return fread(pBufferOut, 1, bytesToRead, (FILE*)pUserData); 646 | } 647 | 648 | static dr_bool32 drwav__on_seek_stdio(void* pUserData, int offset, drwav_seek_origin origin) 649 | { 650 | return fseek((FILE*)pUserData, offset, (origin == drwav_seek_origin_current) ? SEEK_CUR : SEEK_SET) == 0; 651 | } 652 | 653 | dr_bool32 drwav_init_file(drwav* pWav, const char* filename) 654 | { 655 | FILE* pFile; 656 | #ifdef _MSC_VER 657 | if (fopen_s(&pFile, filename, "rb") != 0) { 658 | return DR_FALSE; 659 | } 660 | #else 661 | pFile = fopen(filename, "rb"); 662 | if (pFile == NULL) { 663 | return DR_FALSE; 664 | } 665 | #endif 666 | 667 | return drwav_init(pWav, drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile); 668 | } 669 | 670 | drwav* drwav_open_file(const char* filename) 671 | { 672 | FILE* pFile; 673 | #ifdef _MSC_VER 674 | if (fopen_s(&pFile, filename, "rb") != 0) { 675 | return NULL; 676 | } 677 | #else 678 | pFile = fopen(filename, "rb"); 679 | if (pFile == NULL) { 680 | return NULL; 681 | } 682 | #endif 683 | 684 | drwav* pWav = drwav_open(drwav__on_read_stdio, drwav__on_seek_stdio, (void*)pFile); 685 | if (pWav == NULL) { 686 | fclose(pFile); 687 | return NULL; 688 | } 689 | 690 | return pWav; 691 | } 692 | #endif //DR_WAV_NO_STDIO 693 | 694 | 695 | static size_t drwav__on_read_memory(void* pUserData, void* pBufferOut, size_t bytesToRead) 696 | { 697 | drwav__memory_stream* memory = (drwav__memory_stream*)pUserData; 698 | assert(memory != NULL); 699 | assert(memory->dataSize >= memory->currentReadPos); 700 | 701 | size_t bytesRemaining = memory->dataSize - memory->currentReadPos; 702 | if (bytesToRead > bytesRemaining) { 703 | bytesToRead = bytesRemaining; 704 | } 705 | 706 | if (bytesToRead > 0) { 707 | memcpy(pBufferOut, memory->data + memory->currentReadPos, bytesToRead); 708 | memory->currentReadPos += bytesToRead; 709 | } 710 | 711 | return bytesToRead; 712 | } 713 | 714 | static dr_bool32 drwav__on_seek_memory(void* pUserData, int offset, drwav_seek_origin origin) 715 | { 716 | drwav__memory_stream* memory = (drwav__memory_stream*)pUserData; 717 | assert(memory != NULL); 718 | 719 | if (origin == drwav_seek_origin_current) { 720 | if (offset > 0) { 721 | if (memory->currentReadPos + offset > memory->dataSize) { 722 | offset = (int)(memory->dataSize - memory->currentReadPos); // Trying to seek too far forward. 723 | } 724 | } else { 725 | if (memory->currentReadPos < (size_t)-offset) { 726 | offset = -(int)memory->currentReadPos; // Trying to seek too far backwards. 727 | } 728 | } 729 | } else { 730 | if ((uint32_t)offset <= memory->dataSize) { 731 | memory->currentReadPos = offset; 732 | } else { 733 | memory->currentReadPos = memory->dataSize; // Trying to seek too far forward. 734 | } 735 | } 736 | 737 | // This will never underflow thanks to the clamps above. 738 | memory->currentReadPos += offset; 739 | return DR_TRUE; 740 | } 741 | 742 | dr_bool32 drwav_init_memory(drwav* pWav, const void* data, size_t dataSize) 743 | { 744 | drwav__memory_stream memoryStream; 745 | memoryStream.data = (const unsigned char*)data; 746 | memoryStream.dataSize = dataSize; 747 | memoryStream.currentReadPos = 0; 748 | 749 | if (!drwav_init(pWav, drwav__on_read_memory, drwav__on_seek_memory, (void*)&memoryStream)) { 750 | return DR_FALSE; 751 | } 752 | 753 | pWav->memoryStream = memoryStream; 754 | pWav->pUserData = &pWav->memoryStream; 755 | return DR_TRUE; 756 | } 757 | 758 | drwav* drwav_open_memory(const void* data, size_t dataSize) 759 | { 760 | drwav__memory_stream memoryStream; 761 | memoryStream.data = (const unsigned char*)data; 762 | memoryStream.dataSize = dataSize; 763 | memoryStream.currentReadPos = 0; 764 | 765 | drwav* pWav = drwav_open(drwav__on_read_memory, drwav__on_seek_memory, (void*)&memoryStream); 766 | if (pWav == NULL) { 767 | return NULL; 768 | } 769 | 770 | pWav->memoryStream = memoryStream; 771 | pWav->pUserData = &pWav->memoryStream; 772 | return pWav; 773 | } 774 | 775 | 776 | dr_bool32 drwav_init(drwav* pWav, drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData) 777 | { 778 | if (onRead == NULL || onSeek == NULL) { 779 | return DR_FALSE; 780 | } 781 | 782 | 783 | // The first 4 bytes should be the RIFF identifier. 784 | unsigned char riff[4]; 785 | if (onRead(pUserData, riff, sizeof(riff)) != sizeof(riff)) { 786 | return DR_FALSE; // Failed to read data. 787 | } 788 | 789 | // The first 4 bytes can be used to identify the container. For RIFF files it will start with "RIFF" and for 790 | // w64 it will start with "riff". 791 | if (drwav__fourcc_equal(riff, "RIFF")) { 792 | pWav->container = drwav_container_riff; 793 | } else if (drwav__fourcc_equal(riff, "riff")) { 794 | pWav->container = drwav_container_w64; 795 | 796 | // Check the rest of the GUID for validity. 797 | uint8_t riff2[12]; 798 | if (onRead(pUserData, riff2, sizeof(riff2)) != sizeof(riff2)) { 799 | return DR_FALSE; 800 | } 801 | 802 | for (int i = 0; i < 12; ++i) { 803 | if (riff2[i] != drwavGUID_W64_RIFF[i+4]) { 804 | return DR_FALSE; 805 | } 806 | } 807 | } else { 808 | return DR_FALSE; // Unknown or unsupported container. 809 | } 810 | 811 | 812 | if (pWav->container == drwav_container_riff) { 813 | // RIFF/WAVE 814 | unsigned char chunkSizeBytes[4]; 815 | if (onRead(pUserData, chunkSizeBytes, sizeof(chunkSizeBytes)) != sizeof(chunkSizeBytes)) { 816 | return DR_FALSE; 817 | } 818 | 819 | unsigned int chunkSize = drwav__bytes_to_u32(chunkSizeBytes); 820 | if (chunkSize < 36) { 821 | return DR_FALSE; // Chunk size should always be at least 36 bytes. 822 | } 823 | 824 | unsigned char wave[4]; 825 | if (onRead(pUserData, wave, sizeof(wave)) != sizeof(wave)) { 826 | return DR_FALSE; 827 | } 828 | 829 | if (!drwav__fourcc_equal(wave, "WAVE")) { 830 | return DR_FALSE; // Expecting "WAVE". 831 | } 832 | } else { 833 | // W64 834 | unsigned char chunkSize[8]; 835 | if (onRead(pUserData, chunkSize, sizeof(chunkSize)) != sizeof(chunkSize)) { 836 | return DR_FALSE; 837 | } 838 | 839 | if (drwav__bytes_to_u64(chunkSize) < 84) { 840 | return DR_FALSE; 841 | } 842 | 843 | uint8_t wave[16]; 844 | if (onRead(pUserData, wave, sizeof(wave)) != sizeof(wave)) { 845 | return DR_FALSE; 846 | } 847 | 848 | if (!drwav__guid_equal(wave, drwavGUID_W64_WAVE)) { 849 | return DR_FALSE; 850 | } 851 | } 852 | 853 | 854 | // The next 24 bytes should be the "fmt " chunk. 855 | drwav_fmt fmt; 856 | if (!drwav__read_fmt(onRead, onSeek, pUserData, pWav->container, &fmt)) { 857 | return DR_FALSE; // Failed to read the "fmt " chunk. 858 | } 859 | 860 | 861 | // Translate the internal format. 862 | unsigned short translatedFormatTag = fmt.formatTag; 863 | if (translatedFormatTag == DR_WAVE_FORMAT_EXTENSIBLE) { 864 | translatedFormatTag = drwav__bytes_to_u16(fmt.subFormat + 0); 865 | } 866 | 867 | 868 | // The next chunk we care about is the "data" chunk. This is not necessarily the next chunk so we'll need to loop. 869 | uint64_t dataSize; 870 | for (;;) 871 | { 872 | drwav__chunk_header header; 873 | if (!drwav__read_chunk_header(onRead, pUserData, pWav->container, &header)) { 874 | return DR_FALSE; 875 | } 876 | 877 | dataSize = header.sizeInBytes; 878 | if (pWav->container == drwav_container_riff) { 879 | if (drwav__fourcc_equal(header.id.fourcc, "data")) { 880 | break; 881 | } 882 | } else { 883 | if (drwav__guid_equal(header.id.guid, drwavGUID_W64_DATA)) { 884 | break; 885 | } 886 | } 887 | 888 | // If we get here it means we didn't find the "data" chunk. Seek past it. 889 | 890 | // Make sure we seek past the padding. 891 | dataSize += header.paddingSize; 892 | 893 | uint64_t bytesRemainingToSeek = dataSize; 894 | while (bytesRemainingToSeek > 0) { 895 | if (bytesRemainingToSeek > 0x7FFFFFFF) { 896 | if (!onSeek(pUserData, 0x7FFFFFFF, drwav_seek_origin_current)) { 897 | return DR_FALSE; 898 | } 899 | bytesRemainingToSeek -= 0x7FFFFFFF; 900 | } else { 901 | if (!onSeek(pUserData, (int)bytesRemainingToSeek, drwav_seek_origin_current)) { 902 | return DR_FALSE; 903 | } 904 | bytesRemainingToSeek = 0; 905 | } 906 | } 907 | } 908 | 909 | // At this point we should be sitting on the first byte of the raw audio data. 910 | 911 | pWav->onRead = onRead; 912 | pWav->onSeek = onSeek; 913 | pWav->pUserData = pUserData; 914 | pWav->fmt = fmt; 915 | pWav->sampleRate = fmt.sampleRate; 916 | pWav->channels = fmt.channels; 917 | pWav->bitsPerSample = fmt.bitsPerSample; 918 | pWav->bytesPerSample = (unsigned int)(fmt.blockAlign / fmt.channels); 919 | pWav->translatedFormatTag = translatedFormatTag; 920 | pWav->totalSampleCount = dataSize / pWav->bytesPerSample; 921 | pWav->bytesRemaining = dataSize; 922 | 923 | return DR_TRUE; 924 | } 925 | 926 | void drwav_uninit(drwav* pWav) 927 | { 928 | if (pWav == NULL) { 929 | return; 930 | } 931 | 932 | #ifndef DR_WAV_NO_STDIO 933 | // If we opened the file with drwav_open_file() we will want to close the file handle. We can know whether or not drwav_open_file() 934 | // was used by looking at the onRead and onSeek callbacks. 935 | if (pWav->onRead == drwav__on_read_stdio) { 936 | fclose((FILE*)pWav->pUserData); 937 | } 938 | #endif 939 | } 940 | 941 | 942 | drwav* drwav_open(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData) 943 | { 944 | drwav* pWav = (drwav*)malloc(sizeof(*pWav)); 945 | if (pWav == NULL) { 946 | return NULL; 947 | } 948 | 949 | if (!drwav_init(pWav, onRead, onSeek, pUserData)) { 950 | free(pWav); 951 | return NULL; 952 | } 953 | 954 | return pWav; 955 | } 956 | 957 | void drwav_close(drwav* pWav) 958 | { 959 | drwav_uninit(pWav); 960 | free(pWav); 961 | } 962 | 963 | 964 | size_t drwav_read_raw(drwav* pWav, size_t bytesToRead, void* pBufferOut) 965 | { 966 | if (pWav == NULL || bytesToRead == 0 || pBufferOut == NULL) { 967 | return 0; 968 | } 969 | 970 | if (bytesToRead > pWav->bytesRemaining) { 971 | bytesToRead = (size_t)pWav->bytesRemaining; 972 | } 973 | 974 | size_t bytesRead = pWav->onRead(pWav->pUserData, pBufferOut, bytesToRead); 975 | 976 | pWav->bytesRemaining -= bytesRead; 977 | return bytesRead; 978 | } 979 | 980 | uint64_t drwav_read(drwav* pWav, uint64_t samplesToRead, void* pBufferOut) 981 | { 982 | if (pWav == NULL || samplesToRead == 0 || pBufferOut == NULL) { 983 | return 0; 984 | } 985 | 986 | // Don't try to read more samples than can potentially fit in the output buffer. 987 | if (samplesToRead * pWav->bytesPerSample > SIZE_MAX) { 988 | samplesToRead = SIZE_MAX / pWav->bytesPerSample; 989 | } 990 | 991 | size_t bytesRead = drwav_read_raw(pWav, (size_t)(samplesToRead * pWav->bytesPerSample), pBufferOut); 992 | return bytesRead / pWav->bytesPerSample; 993 | } 994 | 995 | dr_bool32 drwav_seek_to_sample(drwav* pWav, uint64_t sample) 996 | { 997 | // Seeking should be compatible with wave files > 2GB. 998 | 999 | if (pWav == NULL || pWav->onSeek == NULL) { 1000 | return 0; 1001 | } 1002 | 1003 | // If there are no samples, just return DR_TRUE without doing anything. 1004 | if (pWav->totalSampleCount == 0) { 1005 | return 1; 1006 | } 1007 | 1008 | // Make sure the sample is clamped. 1009 | if (sample >= pWav->totalSampleCount) { 1010 | sample = pWav->totalSampleCount - 1; 1011 | } 1012 | 1013 | 1014 | uint64_t totalSizeInBytes = pWav->totalSampleCount * pWav->bytesPerSample; 1015 | assert(totalSizeInBytes >= pWav->bytesRemaining); 1016 | 1017 | uint64_t currentBytePos = totalSizeInBytes - pWav->bytesRemaining; 1018 | uint64_t targetBytePos = sample * pWav->bytesPerSample; 1019 | 1020 | uint64_t offset; 1021 | int direction; 1022 | if (currentBytePos < targetBytePos) { 1023 | // Offset forward. 1024 | offset = targetBytePos - currentBytePos; 1025 | direction = 1; 1026 | } else { 1027 | // Offset backwards. 1028 | offset = currentBytePos - targetBytePos; 1029 | direction = -1; 1030 | } 1031 | 1032 | while (offset > 0) 1033 | { 1034 | int offset32 = ((offset > INT_MAX) ? INT_MAX : (int)offset); 1035 | pWav->onSeek(pWav->pUserData, offset32 * direction, drwav_seek_origin_current); 1036 | 1037 | pWav->bytesRemaining -= (offset32 * direction); 1038 | offset -= offset32; 1039 | } 1040 | 1041 | return 1; 1042 | } 1043 | 1044 | 1045 | #ifndef DR_WAV_NO_CONVERSION_API 1046 | #define drwav_min(a, b) (((a) < (b)) ? (a) : (b)) 1047 | 1048 | static unsigned short g_drwavAlawTable[256] = { 1049 | 0xEA80, 0xEB80, 0xE880, 0xE980, 0xEE80, 0xEF80, 0xEC80, 0xED80, 0xE280, 0xE380, 0xE080, 0xE180, 0xE680, 0xE780, 0xE480, 0xE580, 1050 | 0xF540, 0xF5C0, 0xF440, 0xF4C0, 0xF740, 0xF7C0, 0xF640, 0xF6C0, 0xF140, 0xF1C0, 0xF040, 0xF0C0, 0xF340, 0xF3C0, 0xF240, 0xF2C0, 1051 | 0xAA00, 0xAE00, 0xA200, 0xA600, 0xBA00, 0xBE00, 0xB200, 0xB600, 0x8A00, 0x8E00, 0x8200, 0x8600, 0x9A00, 0x9E00, 0x9200, 0x9600, 1052 | 0xD500, 0xD700, 0xD100, 0xD300, 0xDD00, 0xDF00, 0xD900, 0xDB00, 0xC500, 0xC700, 0xC100, 0xC300, 0xCD00, 0xCF00, 0xC900, 0xCB00, 1053 | 0xFEA8, 0xFEB8, 0xFE88, 0xFE98, 0xFEE8, 0xFEF8, 0xFEC8, 0xFED8, 0xFE28, 0xFE38, 0xFE08, 0xFE18, 0xFE68, 0xFE78, 0xFE48, 0xFE58, 1054 | 0xFFA8, 0xFFB8, 0xFF88, 0xFF98, 0xFFE8, 0xFFF8, 0xFFC8, 0xFFD8, 0xFF28, 0xFF38, 0xFF08, 0xFF18, 0xFF68, 0xFF78, 0xFF48, 0xFF58, 1055 | 0xFAA0, 0xFAE0, 0xFA20, 0xFA60, 0xFBA0, 0xFBE0, 0xFB20, 0xFB60, 0xF8A0, 0xF8E0, 0xF820, 0xF860, 0xF9A0, 0xF9E0, 0xF920, 0xF960, 1056 | 0xFD50, 0xFD70, 0xFD10, 0xFD30, 0xFDD0, 0xFDF0, 0xFD90, 0xFDB0, 0xFC50, 0xFC70, 0xFC10, 0xFC30, 0xFCD0, 0xFCF0, 0xFC90, 0xFCB0, 1057 | 0x1580, 0x1480, 0x1780, 0x1680, 0x1180, 0x1080, 0x1380, 0x1280, 0x1D80, 0x1C80, 0x1F80, 0x1E80, 0x1980, 0x1880, 0x1B80, 0x1A80, 1058 | 0x0AC0, 0x0A40, 0x0BC0, 0x0B40, 0x08C0, 0x0840, 0x09C0, 0x0940, 0x0EC0, 0x0E40, 0x0FC0, 0x0F40, 0x0CC0, 0x0C40, 0x0DC0, 0x0D40, 1059 | 0x5600, 0x5200, 0x5E00, 0x5A00, 0x4600, 0x4200, 0x4E00, 0x4A00, 0x7600, 0x7200, 0x7E00, 0x7A00, 0x6600, 0x6200, 0x6E00, 0x6A00, 1060 | 0x2B00, 0x2900, 0x2F00, 0x2D00, 0x2300, 0x2100, 0x2700, 0x2500, 0x3B00, 0x3900, 0x3F00, 0x3D00, 0x3300, 0x3100, 0x3700, 0x3500, 1061 | 0x0158, 0x0148, 0x0178, 0x0168, 0x0118, 0x0108, 0x0138, 0x0128, 0x01D8, 0x01C8, 0x01F8, 0x01E8, 0x0198, 0x0188, 0x01B8, 0x01A8, 1062 | 0x0058, 0x0048, 0x0078, 0x0068, 0x0018, 0x0008, 0x0038, 0x0028, 0x00D8, 0x00C8, 0x00F8, 0x00E8, 0x0098, 0x0088, 0x00B8, 0x00A8, 1063 | 0x0560, 0x0520, 0x05E0, 0x05A0, 0x0460, 0x0420, 0x04E0, 0x04A0, 0x0760, 0x0720, 0x07E0, 0x07A0, 0x0660, 0x0620, 0x06E0, 0x06A0, 1064 | 0x02B0, 0x0290, 0x02F0, 0x02D0, 0x0230, 0x0210, 0x0270, 0x0250, 0x03B0, 0x0390, 0x03F0, 0x03D0, 0x0330, 0x0310, 0x0370, 0x0350 1065 | }; 1066 | 1067 | static unsigned short g_drwavMulawTable[256] = { 1068 | 0x8284, 0x8684, 0x8A84, 0x8E84, 0x9284, 0x9684, 0x9A84, 0x9E84, 0xA284, 0xA684, 0xAA84, 0xAE84, 0xB284, 0xB684, 0xBA84, 0xBE84, 1069 | 0xC184, 0xC384, 0xC584, 0xC784, 0xC984, 0xCB84, 0xCD84, 0xCF84, 0xD184, 0xD384, 0xD584, 0xD784, 0xD984, 0xDB84, 0xDD84, 0xDF84, 1070 | 0xE104, 0xE204, 0xE304, 0xE404, 0xE504, 0xE604, 0xE704, 0xE804, 0xE904, 0xEA04, 0xEB04, 0xEC04, 0xED04, 0xEE04, 0xEF04, 0xF004, 1071 | 0xF0C4, 0xF144, 0xF1C4, 0xF244, 0xF2C4, 0xF344, 0xF3C4, 0xF444, 0xF4C4, 0xF544, 0xF5C4, 0xF644, 0xF6C4, 0xF744, 0xF7C4, 0xF844, 1072 | 0xF8A4, 0xF8E4, 0xF924, 0xF964, 0xF9A4, 0xF9E4, 0xFA24, 0xFA64, 0xFAA4, 0xFAE4, 0xFB24, 0xFB64, 0xFBA4, 0xFBE4, 0xFC24, 0xFC64, 1073 | 0xFC94, 0xFCB4, 0xFCD4, 0xFCF4, 0xFD14, 0xFD34, 0xFD54, 0xFD74, 0xFD94, 0xFDB4, 0xFDD4, 0xFDF4, 0xFE14, 0xFE34, 0xFE54, 0xFE74, 1074 | 0xFE8C, 0xFE9C, 0xFEAC, 0xFEBC, 0xFECC, 0xFEDC, 0xFEEC, 0xFEFC, 0xFF0C, 0xFF1C, 0xFF2C, 0xFF3C, 0xFF4C, 0xFF5C, 0xFF6C, 0xFF7C, 1075 | 0xFF88, 0xFF90, 0xFF98, 0xFFA0, 0xFFA8, 0xFFB0, 0xFFB8, 0xFFC0, 0xFFC8, 0xFFD0, 0xFFD8, 0xFFE0, 0xFFE8, 0xFFF0, 0xFFF8, 0x0000, 1076 | 0x7D7C, 0x797C, 0x757C, 0x717C, 0x6D7C, 0x697C, 0x657C, 0x617C, 0x5D7C, 0x597C, 0x557C, 0x517C, 0x4D7C, 0x497C, 0x457C, 0x417C, 1077 | 0x3E7C, 0x3C7C, 0x3A7C, 0x387C, 0x367C, 0x347C, 0x327C, 0x307C, 0x2E7C, 0x2C7C, 0x2A7C, 0x287C, 0x267C, 0x247C, 0x227C, 0x207C, 1078 | 0x1EFC, 0x1DFC, 0x1CFC, 0x1BFC, 0x1AFC, 0x19FC, 0x18FC, 0x17FC, 0x16FC, 0x15FC, 0x14FC, 0x13FC, 0x12FC, 0x11FC, 0x10FC, 0x0FFC, 1079 | 0x0F3C, 0x0EBC, 0x0E3C, 0x0DBC, 0x0D3C, 0x0CBC, 0x0C3C, 0x0BBC, 0x0B3C, 0x0ABC, 0x0A3C, 0x09BC, 0x093C, 0x08BC, 0x083C, 0x07BC, 1080 | 0x075C, 0x071C, 0x06DC, 0x069C, 0x065C, 0x061C, 0x05DC, 0x059C, 0x055C, 0x051C, 0x04DC, 0x049C, 0x045C, 0x041C, 0x03DC, 0x039C, 1081 | 0x036C, 0x034C, 0x032C, 0x030C, 0x02EC, 0x02CC, 0x02AC, 0x028C, 0x026C, 0x024C, 0x022C, 0x020C, 0x01EC, 0x01CC, 0x01AC, 0x018C, 1082 | 0x0174, 0x0164, 0x0154, 0x0144, 0x0134, 0x0124, 0x0114, 0x0104, 0x00F4, 0x00E4, 0x00D4, 0x00C4, 0x00B4, 0x00A4, 0x0094, 0x0084, 1083 | 0x0078, 0x0070, 0x0068, 0x0060, 0x0058, 0x0050, 0x0048, 0x0040, 0x0038, 0x0030, 0x0028, 0x0020, 0x0018, 0x0010, 0x0008, 0x0000 1084 | }; 1085 | 1086 | static int drwav__pcm_to_f32(float* pOut, const unsigned char* pIn, size_t sampleCount, unsigned short bytesPerSample) 1087 | { 1088 | if (pOut == NULL || pIn == NULL) { 1089 | return 0; 1090 | } 1091 | 1092 | // Special case for 8-bit sample data because it's treated as unsigned. 1093 | if (bytesPerSample == 1) { 1094 | drwav_u8_to_f32(pOut, pIn, sampleCount); 1095 | return 1; 1096 | } 1097 | 1098 | 1099 | // Slightly more optimal implementation for common formats. 1100 | if (bytesPerSample == 2) { 1101 | drwav_s16_to_f32(pOut, (const int16_t*)pIn, sampleCount); 1102 | return 1; 1103 | } 1104 | if (bytesPerSample == 3) { 1105 | drwav_s24_to_f32(pOut, pIn, sampleCount); 1106 | return 1; 1107 | } 1108 | if (bytesPerSample == 4) { 1109 | drwav_s32_to_f32(pOut, (const int32_t*)pIn, sampleCount); 1110 | return 1; 1111 | } 1112 | 1113 | 1114 | // Generic, slow converter. 1115 | for (unsigned int i = 0; i < sampleCount; ++i) 1116 | { 1117 | unsigned int sample = 0; 1118 | unsigned int shift = (8 - bytesPerSample) * 8; 1119 | for (unsigned short j = 0; j < bytesPerSample && j < 4; ++j) { 1120 | sample |= (unsigned int)(pIn[j]) << shift; 1121 | shift += 8; 1122 | } 1123 | 1124 | pIn += bytesPerSample; 1125 | *pOut++ = (float)((int)sample / 2147483648.0); 1126 | } 1127 | 1128 | return 1; 1129 | } 1130 | 1131 | static int drwav__ieee_to_f32(float* pOut, const unsigned char* pIn, size_t sampleCount, unsigned short bytesPerSample) 1132 | { 1133 | if (pOut == NULL || pIn == NULL) { 1134 | return 0; 1135 | } 1136 | 1137 | if (bytesPerSample == 4) { 1138 | for (unsigned int i = 0; i < sampleCount; ++i) { 1139 | *pOut++ = ((float*)pIn)[i]; 1140 | } 1141 | return 1; 1142 | } else { 1143 | drwav_f64_to_f32(pOut, (double*)pIn, sampleCount); 1144 | return 1; 1145 | } 1146 | } 1147 | 1148 | 1149 | uint64_t drwav_read_f32(drwav* pWav, uint64_t samplesToRead, float* pBufferOut) 1150 | { 1151 | if (pWav == NULL || samplesToRead == 0 || pBufferOut == NULL) { 1152 | return 0; 1153 | } 1154 | 1155 | // Fast path. 1156 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT && pWav->bytesPerSample == 4) { 1157 | return drwav_read(pWav, samplesToRead, pBufferOut); 1158 | } 1159 | 1160 | 1161 | // Don't try to read more samples than can potentially fit in the output buffer. 1162 | if (samplesToRead * sizeof(float) > SIZE_MAX) { 1163 | samplesToRead = SIZE_MAX / sizeof(float); 1164 | } 1165 | 1166 | 1167 | // Slow path. Need to read and convert. 1168 | uint64_t totalSamplesRead = 0; 1169 | 1170 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) 1171 | { 1172 | unsigned char sampleData[4096]; 1173 | while (samplesToRead > 0) 1174 | { 1175 | uint64_t samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); 1176 | if (samplesRead == 0) { 1177 | break; 1178 | } 1179 | 1180 | drwav__pcm_to_f32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample); 1181 | pBufferOut += samplesRead; 1182 | 1183 | samplesToRead -= samplesRead; 1184 | totalSamplesRead += samplesRead; 1185 | } 1186 | 1187 | return totalSamplesRead; 1188 | } 1189 | 1190 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) 1191 | { 1192 | unsigned char sampleData[4096]; 1193 | while (samplesToRead > 0) 1194 | { 1195 | uint64_t samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); 1196 | if (samplesRead == 0) { 1197 | break; 1198 | } 1199 | 1200 | drwav__ieee_to_f32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample); 1201 | pBufferOut += samplesRead; 1202 | 1203 | samplesToRead -= samplesRead; 1204 | totalSamplesRead += samplesRead; 1205 | } 1206 | 1207 | return totalSamplesRead; 1208 | } 1209 | 1210 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) 1211 | { 1212 | unsigned char sampleData[4096]; 1213 | while (samplesToRead > 0) 1214 | { 1215 | uint64_t samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); 1216 | if (samplesRead == 0) { 1217 | break; 1218 | } 1219 | 1220 | drwav_alaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); 1221 | pBufferOut += samplesRead; 1222 | 1223 | samplesToRead -= samplesRead; 1224 | totalSamplesRead += samplesRead; 1225 | } 1226 | 1227 | return totalSamplesRead; 1228 | } 1229 | 1230 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) 1231 | { 1232 | unsigned char sampleData[4096]; 1233 | while (samplesToRead > 0) 1234 | { 1235 | uint64_t samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); 1236 | if (samplesRead == 0) { 1237 | break; 1238 | } 1239 | 1240 | drwav_ulaw_to_f32(pBufferOut, sampleData, (size_t)samplesRead); 1241 | pBufferOut += samplesRead; 1242 | 1243 | samplesToRead -= samplesRead; 1244 | totalSamplesRead += samplesRead; 1245 | } 1246 | 1247 | return totalSamplesRead; 1248 | } 1249 | 1250 | return totalSamplesRead; 1251 | } 1252 | 1253 | void drwav_u8_to_f32(float* pOut, const uint8_t* pIn, size_t sampleCount) 1254 | { 1255 | if (pOut == NULL || pIn == NULL) { 1256 | return; 1257 | } 1258 | 1259 | for (size_t i = 0; i < sampleCount; ++i) { 1260 | *pOut++ = (pIn[i] / 255.0f) * 2 - 1; 1261 | } 1262 | } 1263 | 1264 | void drwav_s16_to_f32(float* pOut, const int16_t* pIn, size_t sampleCount) 1265 | { 1266 | if (pOut == NULL || pIn == NULL) { 1267 | return; 1268 | } 1269 | 1270 | for (size_t i = 0; i < sampleCount; ++i) { 1271 | *pOut++ = pIn[i] / 32768.0f; 1272 | } 1273 | } 1274 | 1275 | void drwav_s24_to_f32(float* pOut, const uint8_t* pIn, size_t sampleCount) 1276 | { 1277 | if (pOut == NULL || pIn == NULL) { 1278 | return; 1279 | } 1280 | 1281 | for (size_t i = 0; i < sampleCount; ++i) { 1282 | unsigned int s0 = pIn[i*3 + 0]; 1283 | unsigned int s1 = pIn[i*3 + 1]; 1284 | unsigned int s2 = pIn[i*3 + 2]; 1285 | 1286 | int sample32 = (int)((s0 << 8) | (s1 << 16) | (s2 << 24)); 1287 | *pOut++ = (float)(sample32 / 2147483648.0); 1288 | } 1289 | } 1290 | 1291 | void drwav_s32_to_f32(float* pOut, const int32_t* pIn, size_t sampleCount) 1292 | { 1293 | if (pOut == NULL || pIn == NULL) { 1294 | return; 1295 | } 1296 | 1297 | for (size_t i = 0; i < sampleCount; ++i) { 1298 | *pOut++ = (float)(pIn[i] / 2147483648.0); 1299 | } 1300 | } 1301 | 1302 | void drwav_f64_to_f32(float* pOut, const double* pIn, size_t sampleCount) 1303 | { 1304 | if (pOut == NULL || pIn == NULL) { 1305 | return; 1306 | } 1307 | 1308 | for (size_t i = 0; i < sampleCount; ++i) { 1309 | *pOut++ = (float)pIn[i]; 1310 | } 1311 | } 1312 | 1313 | void drwav_alaw_to_f32(float* pOut, const uint8_t* pIn, size_t sampleCount) 1314 | { 1315 | if (pOut == NULL || pIn == NULL) { 1316 | return; 1317 | } 1318 | 1319 | for (size_t i = 0; i < sampleCount; ++i) { 1320 | *pOut++ = g_drwavAlawTable[pIn[i]] / 32768.0f; 1321 | } 1322 | } 1323 | 1324 | void drwav_ulaw_to_f32(float* pOut, const uint8_t* pIn, size_t sampleCount) 1325 | { 1326 | if (pOut == NULL || pIn == NULL) { 1327 | return; 1328 | } 1329 | 1330 | for (size_t i = 0; i < sampleCount; ++i) { 1331 | *pOut++ = g_drwavMulawTable[pIn[i]] / 32768.0f; 1332 | } 1333 | } 1334 | 1335 | 1336 | 1337 | static int drwav__pcm_to_s32(int32_t* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample) 1338 | { 1339 | if (pOut == NULL || pIn == NULL) { 1340 | return 0; 1341 | } 1342 | 1343 | // Special case for 8-bit sample data because it's treated as unsigned. 1344 | if (bytesPerSample == 1) { 1345 | drwav_u8_to_s32(pOut, pIn, totalSampleCount); 1346 | return 1; 1347 | } 1348 | 1349 | 1350 | // Slightly more optimal implementation for common formats. 1351 | if (bytesPerSample == 2) { 1352 | drwav_s16_to_s32(pOut, (const int16_t*)pIn, totalSampleCount); 1353 | return 1; 1354 | } 1355 | if (bytesPerSample == 3) { 1356 | drwav_s24_to_s32(pOut, pIn, totalSampleCount); 1357 | return 1; 1358 | } 1359 | if (bytesPerSample == 4) { 1360 | for (unsigned int i = 0; i < totalSampleCount; ++i) { 1361 | *pOut++ = ((int32_t*)pIn)[i]; 1362 | } 1363 | return 1; 1364 | } 1365 | 1366 | 1367 | // Generic, slow converter. 1368 | for (unsigned int i = 0; i < totalSampleCount; ++i) 1369 | { 1370 | unsigned int sample = 0; 1371 | unsigned int shift = (8 - bytesPerSample) * 8; 1372 | for (unsigned short j = 0; j < bytesPerSample && j < 4; ++j) { 1373 | sample |= (unsigned int)(pIn[j]) << shift; 1374 | shift += 8; 1375 | } 1376 | 1377 | pIn += bytesPerSample; 1378 | *pOut++ = sample; 1379 | } 1380 | 1381 | return 1; 1382 | } 1383 | 1384 | static int drwav__ieee_to_s32(int32_t* pOut, const unsigned char* pIn, size_t totalSampleCount, unsigned short bytesPerSample) 1385 | { 1386 | if (pOut == NULL || pIn == NULL) { 1387 | return 0; 1388 | } 1389 | 1390 | if (bytesPerSample == 4) { 1391 | drwav_f32_to_s32(pOut, (float*)pIn, totalSampleCount); 1392 | return 1; 1393 | } else { 1394 | drwav_f64_to_s32(pOut, (double*)pIn, totalSampleCount); 1395 | return 1; 1396 | } 1397 | } 1398 | 1399 | uint64_t drwav_read_s32(drwav* pWav, uint64_t samplesToRead, int32_t* pBufferOut) 1400 | { 1401 | if (pWav == NULL || samplesToRead == 0 || pBufferOut == NULL) { 1402 | return 0; 1403 | } 1404 | 1405 | // Fast path. 1406 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM && pWav->bytesPerSample == 4) { 1407 | return drwav_read(pWav, samplesToRead, pBufferOut); 1408 | } 1409 | 1410 | 1411 | // Don't try to read more samples than can potentially fit in the output buffer. 1412 | if (samplesToRead * sizeof(int32_t) > SIZE_MAX) { 1413 | samplesToRead = SIZE_MAX / sizeof(int32_t); 1414 | } 1415 | 1416 | 1417 | // Slow path. Need to read and convert. 1418 | uint64_t totalSamplesRead = 0; 1419 | 1420 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_PCM) 1421 | { 1422 | unsigned char sampleData[4096]; 1423 | while (samplesToRead > 0) 1424 | { 1425 | uint64_t samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); 1426 | if (samplesRead == 0) { 1427 | break; 1428 | } 1429 | 1430 | drwav__pcm_to_s32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample); 1431 | pBufferOut += samplesRead; 1432 | 1433 | samplesToRead -= samplesRead; 1434 | totalSamplesRead += samplesRead; 1435 | } 1436 | 1437 | return totalSamplesRead; 1438 | } 1439 | 1440 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_IEEE_FLOAT) 1441 | { 1442 | unsigned char sampleData[4096]; 1443 | while (samplesToRead > 0) 1444 | { 1445 | uint64_t samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); 1446 | if (samplesRead == 0) { 1447 | break; 1448 | } 1449 | 1450 | drwav__ieee_to_s32(pBufferOut, sampleData, (size_t)samplesRead, pWav->bytesPerSample); 1451 | pBufferOut += samplesRead; 1452 | 1453 | samplesToRead -= samplesRead; 1454 | totalSamplesRead += samplesRead; 1455 | } 1456 | 1457 | return totalSamplesRead; 1458 | } 1459 | 1460 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_ALAW) 1461 | { 1462 | unsigned char sampleData[4096]; 1463 | while (samplesToRead > 0) 1464 | { 1465 | uint64_t samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); 1466 | if (samplesRead == 0) { 1467 | break; 1468 | } 1469 | 1470 | drwav_alaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); 1471 | pBufferOut += samplesRead; 1472 | 1473 | samplesToRead -= samplesRead; 1474 | totalSamplesRead += samplesRead; 1475 | } 1476 | 1477 | return totalSamplesRead; 1478 | } 1479 | 1480 | if (pWav->translatedFormatTag == DR_WAVE_FORMAT_MULAW) 1481 | { 1482 | unsigned char sampleData[4096]; 1483 | while (samplesToRead > 0) 1484 | { 1485 | uint64_t samplesRead = drwav_read(pWav, drwav_min(samplesToRead, sizeof(sampleData)/pWav->bytesPerSample), sampleData); 1486 | if (samplesRead == 0) { 1487 | break; 1488 | } 1489 | 1490 | drwav_ulaw_to_s32(pBufferOut, sampleData, (size_t)samplesRead); 1491 | pBufferOut += samplesRead; 1492 | 1493 | samplesToRead -= samplesRead; 1494 | totalSamplesRead += samplesRead; 1495 | } 1496 | 1497 | return totalSamplesRead; 1498 | } 1499 | 1500 | return totalSamplesRead; 1501 | } 1502 | 1503 | void drwav_u8_to_s32(int32_t* pOut, const uint8_t* pIn, size_t sampleCount) 1504 | { 1505 | if (pOut == NULL || pIn == NULL) { 1506 | return; 1507 | } 1508 | 1509 | for (size_t i = 0; i < sampleCount; ++i) { 1510 | *pOut++ = ((int)pIn[i] - 128) << 24; 1511 | } 1512 | } 1513 | 1514 | void drwav_s16_to_s32(int32_t* pOut, const int16_t* pIn, size_t sampleCount) 1515 | { 1516 | if (pOut == NULL || pIn == NULL) { 1517 | return; 1518 | } 1519 | 1520 | for (size_t i = 0; i < sampleCount; ++i) { 1521 | *pOut++ = pIn[i] << 16; 1522 | } 1523 | } 1524 | 1525 | void drwav_s24_to_s32(int32_t* pOut, const uint8_t* pIn, size_t sampleCount) 1526 | { 1527 | if (pOut == NULL || pIn == NULL) { 1528 | return; 1529 | } 1530 | 1531 | for (size_t i = 0; i < sampleCount; ++i) { 1532 | unsigned int s0 = pIn[i*3 + 0]; 1533 | unsigned int s1 = pIn[i*3 + 1]; 1534 | unsigned int s2 = pIn[i*3 + 2]; 1535 | 1536 | int32_t sample32 = (int32_t)((s0 << 8) | (s1 << 16) | (s2 << 24)); 1537 | *pOut++ = sample32; 1538 | } 1539 | } 1540 | 1541 | void drwav_f32_to_s32(int32_t* pOut, const float* pIn, size_t sampleCount) 1542 | { 1543 | if (pOut == NULL || pIn == NULL) { 1544 | return; 1545 | } 1546 | 1547 | for (size_t i = 0; i < sampleCount; ++i) { 1548 | *pOut++ = (int32_t)(2147483648.0 * pIn[i]); 1549 | } 1550 | } 1551 | 1552 | void drwav_f64_to_s32(int32_t* pOut, const double* pIn, size_t sampleCount) 1553 | { 1554 | if (pOut == NULL || pIn == NULL) { 1555 | return; 1556 | } 1557 | 1558 | for (size_t i = 0; i < sampleCount; ++i) { 1559 | *pOut++ = (int32_t)(2147483648.0 * pIn[i]); 1560 | } 1561 | } 1562 | 1563 | void drwav_alaw_to_s32(int32_t* pOut, const uint8_t* pIn, size_t sampleCount) 1564 | { 1565 | if (pOut == NULL || pIn == NULL) { 1566 | return; 1567 | } 1568 | 1569 | for (size_t i = 0; i < sampleCount; ++i) { 1570 | *pOut++ = ((int32_t)g_drwavAlawTable[pIn[i]]) << 16; 1571 | } 1572 | } 1573 | 1574 | void drwav_ulaw_to_s32(int32_t* pOut, const uint8_t* pIn, size_t sampleCount) 1575 | { 1576 | if (pOut == NULL || pIn == NULL) { 1577 | return; 1578 | } 1579 | 1580 | for (size_t i= 0; i < sampleCount; ++i) { 1581 | *pOut++ = ((int32_t)g_drwavMulawTable[pIn[i]]) << 16; 1582 | } 1583 | } 1584 | 1585 | 1586 | 1587 | 1588 | float* drwav__read_and_close_f32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount) 1589 | { 1590 | assert(pWav != NULL); 1591 | 1592 | uint64_t sampleDataSize = pWav->totalSampleCount * sizeof(float); 1593 | if (sampleDataSize > SIZE_MAX) { 1594 | drwav_uninit(pWav); 1595 | return NULL; // File's too big. 1596 | } 1597 | 1598 | float* pSampleData = (float*)malloc((size_t)(pWav->totalSampleCount * sizeof(float))); // <-- Safe cast due to the check above. 1599 | if (pSampleData == NULL) { 1600 | drwav_uninit(pWav); 1601 | return NULL; // Failed to allocate memory. 1602 | } 1603 | 1604 | uint64_t samplesRead = drwav_read_f32(pWav, (size_t)pWav->totalSampleCount, pSampleData); 1605 | if (samplesRead != pWav->totalSampleCount) { 1606 | free(pSampleData); 1607 | drwav_uninit(pWav); 1608 | return NULL; // There was an error reading the samples. 1609 | } 1610 | 1611 | drwav_uninit(pWav); 1612 | 1613 | if (sampleRate) *sampleRate = pWav->sampleRate; 1614 | if (channels) *channels = pWav->channels; 1615 | if (totalSampleCount) *totalSampleCount = pWav->totalSampleCount; 1616 | return pSampleData; 1617 | } 1618 | 1619 | int32_t* drwav__read_and_close_s32(drwav* pWav, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount) 1620 | { 1621 | assert(pWav != NULL); 1622 | 1623 | uint64_t sampleDataSize = pWav->totalSampleCount * sizeof(int32_t); 1624 | if (sampleDataSize > SIZE_MAX) { 1625 | drwav_uninit(pWav); 1626 | return NULL; // File's too big. 1627 | } 1628 | 1629 | int32_t* pSampleData = (int32_t*)malloc((size_t)(pWav->totalSampleCount * sizeof(int32_t))); // <-- Safe cast due to the check above. 1630 | if (pSampleData == NULL) { 1631 | drwav_uninit(pWav); 1632 | return NULL; // Failed to allocate memory. 1633 | } 1634 | 1635 | uint64_t samplesRead = drwav_read_s32(pWav, (size_t)pWav->totalSampleCount, pSampleData); 1636 | if (samplesRead != pWav->totalSampleCount) { 1637 | free(pSampleData); 1638 | drwav_uninit(pWav); 1639 | return NULL; // There was an error reading the samples. 1640 | } 1641 | 1642 | drwav_uninit(pWav); 1643 | 1644 | if (sampleRate) *sampleRate = pWav->sampleRate; 1645 | if (channels) *channels = pWav->channels; 1646 | if (totalSampleCount) *totalSampleCount = pWav->totalSampleCount; 1647 | return pSampleData; 1648 | } 1649 | 1650 | 1651 | 1652 | float* drwav_open_and_read_f32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount) 1653 | { 1654 | if (sampleRate) *sampleRate = 0; 1655 | if (channels) *channels = 0; 1656 | if (totalSampleCount) *totalSampleCount = 0; 1657 | 1658 | drwav wav; 1659 | if (!drwav_init(&wav, onRead, onSeek, pUserData)) { 1660 | return NULL; 1661 | } 1662 | 1663 | return drwav__read_and_close_f32(&wav, channels, sampleRate, totalSampleCount); 1664 | } 1665 | 1666 | int32_t* drwav_open_and_read_s32(drwav_read_proc onRead, drwav_seek_proc onSeek, void* pUserData, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount) 1667 | { 1668 | if (sampleRate) *sampleRate = 0; 1669 | if (channels) *channels = 0; 1670 | if (totalSampleCount) *totalSampleCount = 0; 1671 | 1672 | drwav wav; 1673 | if (!drwav_init(&wav, onRead, onSeek, pUserData)) { 1674 | return NULL; 1675 | } 1676 | 1677 | return drwav__read_and_close_s32(&wav, channels, sampleRate, totalSampleCount); 1678 | } 1679 | 1680 | #ifndef DR_WAV_NO_STDIO 1681 | float* drwav_open_and_read_file_f32(const char* filename, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount) 1682 | { 1683 | if (sampleRate) *sampleRate = 0; 1684 | if (channels) *channels = 0; 1685 | if (totalSampleCount) *totalSampleCount = 0; 1686 | 1687 | drwav wav; 1688 | if (!drwav_init_file(&wav, filename)) { 1689 | return NULL; 1690 | } 1691 | 1692 | return drwav__read_and_close_f32(&wav, channels, sampleRate, totalSampleCount); 1693 | } 1694 | 1695 | int32_t* drwav_open_and_read_file_s32(const char* filename, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount) 1696 | { 1697 | if (sampleRate) *sampleRate = 0; 1698 | if (channels) *channels = 0; 1699 | if (totalSampleCount) *totalSampleCount = 0; 1700 | 1701 | drwav wav; 1702 | if (!drwav_init_file(&wav, filename)) { 1703 | return NULL; 1704 | } 1705 | 1706 | return drwav__read_and_close_s32(&wav, channels, sampleRate, totalSampleCount); 1707 | } 1708 | #endif 1709 | 1710 | float* drwav_open_and_read_memory_f32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount) 1711 | { 1712 | if (sampleRate) *sampleRate = 0; 1713 | if (channels) *channels = 0; 1714 | if (totalSampleCount) *totalSampleCount = 0; 1715 | 1716 | drwav wav; 1717 | if (!drwav_init_memory(&wav, data, dataSize)) { 1718 | return NULL; 1719 | } 1720 | 1721 | return drwav__read_and_close_f32(&wav, channels, sampleRate, totalSampleCount); 1722 | } 1723 | 1724 | int32_t* drwav_open_and_read_memory_s32(const void* data, size_t dataSize, unsigned int* channels, unsigned int* sampleRate, uint64_t* totalSampleCount) 1725 | { 1726 | if (sampleRate) *sampleRate = 0; 1727 | if (channels) *channels = 0; 1728 | if (totalSampleCount) *totalSampleCount = 0; 1729 | 1730 | drwav wav; 1731 | if (!drwav_init_memory(&wav, data, dataSize)) { 1732 | return NULL; 1733 | } 1734 | 1735 | return drwav__read_and_close_s32(&wav, channels, sampleRate, totalSampleCount); 1736 | } 1737 | #endif //DR_WAV_NO_CONVERSION_API 1738 | 1739 | 1740 | void drwav_free(void* pDataReturnedByOpenAndRead) 1741 | { 1742 | free(pDataReturnedByOpenAndRead); 1743 | } 1744 | 1745 | #endif //DR_WAV_IMPLEMENTATION 1746 | 1747 | 1748 | // REVISION HISTORY 1749 | // 1750 | // v0.5b - 2016-10-23 1751 | // - A minor change to dr_bool8 and dr_bool32 types. 1752 | // 1753 | // v0.5a - 2016-10-11 1754 | // - Fixed a bug with drwav_open_and_read() and family due to incorrect argument ordering. 1755 | // - Improve A-law and mu-law efficiency. 1756 | // 1757 | // v0.5 - 2016-09-29 1758 | // - API CHANGE. Swap the order of "channels" and "sampleRate" parameters in drwav_open_and_read*(). Rationale for this is to 1759 | // keep it consistent with dr_audio and dr_flac. 1760 | // 1761 | // v0.4b - 2016-09-18 1762 | // - Fixed a typo in documentation. 1763 | // 1764 | // v0.4a - 2016-09-18 1765 | // - Fixed a typo. 1766 | // - Change date format to ISO 8601 (YYYY-MM-DD) 1767 | // 1768 | // v0.4 - 2016-07-13 1769 | // - API CHANGE. Make onSeek consistent with dr_flac. 1770 | // - API CHANGE. Rename drwav_seek() to drwav_seek_to_sample() for clarity and consistency with dr_flac. 1771 | // - Added support for Sony Wave64. 1772 | // 1773 | // v0.3a - 2016-05-28 1774 | // - API CHANGE. Return dr_bool32 instead of int in onSeek callback. 1775 | // - Fixed a memory leak. 1776 | // 1777 | // v0.3 - 2016-05-22 1778 | // - Lots of API changes for consistency. 1779 | // 1780 | // v0.2a - 2016-05-16 1781 | // - Fixed Linux/GCC build. 1782 | // 1783 | // v0.2 - 2016-05-11 1784 | // - Added support for reading data as signed 32-bit PCM for consistency with dr_flac. 1785 | // 1786 | // v0.1a - 2016-05-07 1787 | // - Fixed a bug in drwav_open_file() where the file handle would not be closed if the loader failed to initialize. 1788 | // 1789 | // v0.1 - 2016-05-04 1790 | // - Initial versioned release. 1791 | 1792 | 1793 | /* 1794 | This is free and unencumbered software released into the public domain. 1795 | 1796 | Anyone is free to copy, modify, publish, use, compile, sell, or 1797 | distribute this software, either in source code form or as a compiled 1798 | binary, for any purpose, commercial or non-commercial, and by any 1799 | means. 1800 | 1801 | In jurisdictions that recognize copyright laws, the author or authors 1802 | of this software dedicate any and all copyright interest in the 1803 | software to the public domain. We make this dedication for the benefit 1804 | of the public at large and to the detriment of our heirs and 1805 | successors. We intend this dedication to be an overt act of 1806 | relinquishment in perpetuity of all present and future rights to this 1807 | software under copyright law. 1808 | 1809 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 1810 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 1811 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. 1812 | IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR 1813 | OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, 1814 | ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR 1815 | OTHER DEALINGS IN THE SOFTWARE. 1816 | 1817 | For more information, please refer to 1818 | */ 1819 | -------------------------------------------------------------------------------- /resources/branding/mintaro_logo1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mackron/mintaro/4410490e55b894240c8be7d494e236f9ab02059f/resources/branding/mintaro_logo1.png -------------------------------------------------------------------------------- /tools/mintaro_gen_font.c: -------------------------------------------------------------------------------- 1 | // This is just a basic tool for converting the source font image to the native internal format. This 2 | // is probably temporary until it's converted to a bitmap. 3 | 4 | #define STB_IMAGE_IMPLEMENTATION 5 | #include "../stb_image.h" 6 | 7 | int main() 8 | { 9 | int srcSizeX; 10 | int srcSizeY; 11 | stbi_uc* pSrcData = stbi_load("../data/atari_8bit_font_revised.png", &srcSizeX, &srcSizeY, NULL, 1); 12 | if (pSrcData == NULL) { 13 | printf("Failed to load font file."); 14 | return -1; 15 | } 16 | 17 | int newLineCounter = 0; 18 | 19 | #if 1 20 | int glyphCount = srcSizeX / srcSizeY; 21 | for (int i = 0; i < glyphCount; ++i) { 22 | int offset = i*9; 23 | for (int y = 0; y < 9; ++y) { 24 | for (int x = 0; x < 9; ++x) { 25 | printf("0x%02x, ", (pSrcData + offset)[y*srcSizeX + x]); 26 | if (++newLineCounter == 36) { 27 | printf("\n"); 28 | newLineCounter = 0; 29 | } 30 | } 31 | } 32 | } 33 | #endif 34 | 35 | #if 0 36 | stbi_uc* pRunningPixel = pSrcData; 37 | for (int y = 0; y < srcSizeY; ++y) { 38 | for (int x = 0; x < srcSizeX; ++x) { 39 | printf("0x%02x, ", pRunningPixel[0]); 40 | if (++newLineCounter == 36) { 41 | printf("\n"); 42 | newLineCounter = 0; 43 | } 44 | 45 | pRunningPixel += 4; 46 | } 47 | } 48 | #endif 49 | 50 | return 0; 51 | } -------------------------------------------------------------------------------- /tools/mintaro_gen_law.c: -------------------------------------------------------------------------------- 1 | // This is just a basic tool for print the a-law and mu-law tables. 2 | #include 3 | 4 | int main() 5 | { 6 | // a-law. 7 | printf("A-LAW\n"); 8 | { 9 | int newLineCounter = 0; 10 | for (unsigned int i = 0; i < 256; ++i) 11 | { 12 | const unsigned char a = (unsigned char)i ^ 0x55; 13 | 14 | int t = (a & 0x0F) << 4; 15 | 16 | int s = ((unsigned int)a & 0x70) >> 4; 17 | switch (s) 18 | { 19 | case 0: 20 | { 21 | t += 8; 22 | } break; 23 | 24 | default: 25 | { 26 | t += 0x108; 27 | t <<= (s - 1); 28 | } break; 29 | } 30 | 31 | if ((a & 0x80) == 0) { 32 | t = -t; 33 | } 34 | 35 | printf("0x%04X, ", (unsigned short)(t)); 36 | if (++newLineCounter == 16) { 37 | printf("\n"); 38 | newLineCounter = 0; 39 | } 40 | } 41 | } 42 | printf("\n"); 43 | 44 | printf("U-LAW\n"); 45 | { 46 | int newLineCounter = 0; 47 | for (unsigned int i = 0; i < 256; ++i) 48 | { 49 | const unsigned char u = ~i; 50 | 51 | int t = (((u & 0x0F) << 3) + 0x84) << (((unsigned int)u & 0x70) >> 4); 52 | if (u & 0x80) { 53 | t = 0x84 - t; 54 | } else { 55 | t = t - 0x84; 56 | } 57 | 58 | printf("0x%04X, ", (unsigned short)(t)); 59 | if (++newLineCounter == 16) { 60 | printf("\n"); 61 | newLineCounter = 0; 62 | } 63 | } 64 | } 65 | printf("\n"); 66 | 67 | return 0; 68 | } -------------------------------------------------------------------------------- /tools/mintaro_gen_palette.c: -------------------------------------------------------------------------------- 1 | #define STB_IMAGE_IMPLEMENTATION 2 | #include "../extras/stb_image.h" 3 | 4 | #include 5 | 6 | typedef struct 7 | { 8 | union 9 | { 10 | struct 11 | { 12 | unsigned char b; 13 | unsigned char g; 14 | unsigned char r; 15 | unsigned char a; 16 | }; 17 | 18 | unsigned int rgba; 19 | }; 20 | } color_rgba; 21 | 22 | int main() 23 | { 24 | // To generate the palette we just load an image and add each unique color. Color index 255 is overwritten with a transparent color. 25 | 26 | int srcSizeX; 27 | int srcSizeY; 28 | stbi_uc* pSrcData = stbi_load("../data/palette.png", &srcSizeX, &srcSizeY, NULL, 4); 29 | if (pSrcData == NULL) { 30 | printf("Failed to load font file."); 31 | return -1; 32 | } 33 | 34 | int colorCount = 0; 35 | color_rgba palette[256]; 36 | for (int i = 0; i < 256; ++i) { 37 | palette[i].r = 0; 38 | palette[i].g = 0; 39 | palette[i].b = 0; 40 | palette[i].a = 255; 41 | } 42 | 43 | 44 | for (int i = 0; i < srcSizeX*srcSizeY; ++i) { 45 | stbi_uc* pixel = pSrcData + (i*4); 46 | color_rgba color; 47 | color.r = pixel[0]; 48 | color.g = pixel[1]; 49 | color.b = pixel[2]; 50 | color.a = 255; 51 | 52 | // If the pixel already exists in the palette just skip it. 53 | int exists = 0; 54 | for (int j = 0; j < colorCount; ++j) { 55 | if (palette[j].rgba == color.rgba) { 56 | exists = 1; 57 | break; 58 | } 59 | } 60 | 61 | if (!exists) { 62 | palette[colorCount++] = color; 63 | } 64 | } 65 | 66 | // Fill any remaining slots with true grays. This assumes true black and true white are already in the palette. 67 | int grayCount = 256 - colorCount; 68 | if (grayCount > 0) { 69 | unsigned char shade = 256 / grayCount; 70 | color_rgba gray; 71 | gray.a = 255; 72 | for (int i = 0; i < grayCount; ++i) { 73 | gray.r = gray.g = gray.b = (unsigned char)((i+1)*shade); 74 | palette[colorCount++] = gray; 75 | } 76 | } 77 | 78 | 79 | 80 | 81 | // Transparent color. 82 | palette[255].r = 0; 83 | palette[255].g = 0; 84 | palette[255].b = 0; 85 | palette[255].a = 0; 86 | 87 | 88 | int newLineCounter = 0; 89 | for (unsigned int i = 0; i < 256; ++i) { 90 | printf("0x%08X, ", palette[i].rgba); 91 | //printf("{0x%02X, 0x%02X, 0x%02X, 0x%02X}, ", palette[i].b, palette[i].g, palette[i].r, palette[i].a); 92 | if (++newLineCounter == 16) { 93 | printf("\n"); 94 | newLineCounter = 0; 95 | } 96 | } 97 | 98 | return 0; 99 | } --------------------------------------------------------------------------------