├── .gitignore ├── LICENSE ├── README.md ├── demos ├── encoder-raw-mt │ ├── Makefile │ ├── README.md │ └── encoder-raw-mt.c ├── encoder-raw-serial │ ├── Makefile │ ├── README.md │ └── encoder-raw-serial.c ├── encoder-raw │ ├── Makefile │ ├── README.md │ └── encoder-raw.c ├── encoder │ ├── Makefile │ ├── README.md │ ├── encoder.c │ ├── wavdecoder.c │ └── wavdecoder.h └── wasm │ ├── Makefile │ ├── README.md │ ├── demo.mjs │ ├── tflac.c │ └── tflac.mjs ├── misc ├── gen-crc16-tables │ └── gen-crc16-tables.c └── weird-wav-maker │ ├── Makefile │ ├── README.md │ └── weird-wav-maker.c ├── tests └── residuals │ ├── Makefile │ ├── test.c │ └── time.c └── tflac.h /.gitignore: -------------------------------------------------------------------------------- 1 | *.o 2 | *.a 3 | *.so 4 | *.dll 5 | *.exe 6 | *.wasm 7 | *.ll 8 | /demos/encoder/encoder 9 | /demos/encoder-raw/encoder-raw 10 | /misc/weird-wav-maker/weird-wav-maker 11 | /tests/residuals/test-32bit 12 | /tests/residuals/test-64bit 13 | /tests/residuals/time-32bit 14 | /tests/residuals/time-64bit 15 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2024 John Regan 2 | 3 | Permission to use, copy, modify, and/or distribute this software for any 4 | purpose with or without fee is hereby granted. 5 | 6 | THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH 7 | REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY 8 | AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, 9 | INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM 10 | LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 11 | OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 12 | PERFORMANCE OF THIS SOFTWARE. 13 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # tflac 2 | 3 | A single-file [FLAC](https://xiph.org/flac/) encoding library. Does not 4 | use any C library functions, does not allocate memory directly (it leaves 5 | that up to you to figure out). Compatible with C89. 6 | 7 | Use case: you want to generate FLAC audio without requiring an external 8 | library, and simplicity is your main concern, not compression or speed. 9 | 10 | ## Status 11 | 12 | tflac currently supports the following subframe encodings: 13 | 14 | * `CONSTANT` 15 | * `FIXED` 16 | * `VERBATIM` 17 | 18 | It's roughly equivalent to compressing with FLAC's "-0" switch. 19 | 20 | ## Building 21 | 22 | In one C file define `TFLAC_IMPLEMENTATION` before including `tflac.h`. 23 | 24 | ```c 25 | #define TFLAC_IMPLEMENTATION 26 | #include "tflac.h" 27 | ``` 28 | 29 | ### Options 30 | 31 | There's a few compile-time options you can set: 32 | 33 | * Define `TFLAC_32BIT_ONLY` and tflac will not use 64-bit types at all, 34 | and instead emulate 64-bit types with a pair of 32-bit ints. 35 | * Define `TFLAC_DISABLE_SSE2` to disable SSE2 detection. 36 | * Define `TFLAC_DISABLE_SSSE3` to disable SSSE3 detection. 37 | * Define `TFLAC_DISABLE_SSE4_1` to disable SSE4.1 detection. 38 | * Define `TFLAC_PUBLIC` if you need to customize function decorators 39 | for public API functions. 40 | * Define `TFLAC_PRIVATE` if you need to customize function decorators 41 | for private API functions. 42 | 43 | For example, if you're building a DLL on Windows and want to export 44 | the public API functions you could define `TFLAC_PUBLIC` as 45 | `__declspec(dllexport)` like so: 46 | 47 | ```c 48 | #define TFLAC_IMPLEMENTATION 49 | #define TFLAC_PUBLIC __declspec(dllexport) 50 | #include "tflac.h" 51 | ``` 52 | 53 | ## Usage 54 | 55 | ### Detect your CPU features 56 | 57 | Call `tflac_detect_cpu()` before doing anything, like as early in your 58 | program as possible, before you create any threads etc. 59 | 60 | This will attempt to check your CPU type, and set some internal 61 | global variables. For example, if your CPU supports SSE2, the library 62 | will swap the default fixed-order calculators for a set that use 63 | SSE2. 64 | 65 | ### Get memory and initialize things. 66 | 67 | You'll have to create a tflac struct. The whole struct definition is 68 | provided so you can allocate it on the stack, or call `tflac_size()` 69 | to get the structure size at runtime. Initialize it with `tflac_init()`. 70 | 71 | You then need to set 4 parameters. You can either set the tflac struct fields 72 | yourself, or use setter functions (`tflac_set_blocksize()`, 73 | `tflac_set_samplerate()`, etc). 74 | 75 | 1. audio block size 76 | 2. channels 77 | 3. bit depth 78 | 4. sample rate 79 | 80 | You then need to acquire a memory chunk, to be used by tflac internally 81 | for storing residuals. You can get this needed memory size with a C 82 | macro, or with a function. The only required parameter is your audio block 83 | size. 84 | 85 | Once you've set your parameters and acquired memory, call `tflac_validate()` 86 | with the memory you've allocated for tflac's internals. It will make sure 87 | your parameters are sane, that the memory chunk is large enough, and 88 | proceed to set up all of its internal pointers to the memory chunk. 89 | 90 | Note: the library will not free this memory for you since the library 91 | didn't allocate it. 92 | 93 | ### Generate some FLAC! 94 | 95 | You'll need to write out the `fLaC` stream marker and a `STREAMINFO` block. 96 | The library has a function for generating a `STREAMINFO` block, since 97 | that's a required metadata block. Other blocks are pretty straightforward 98 | to create, I don't plan to include that functionality. 99 | 100 | You'll need a buffer for storing encoded frames. You can find the maximum 101 | required buffer size with a C macro, or with a function. The parameters 102 | are your audio block size, channels, and bit depth. 103 | 104 | You can have your audio in interleaved or planar format, and as either 105 | `tflac_s16` or `tflac_s32`. The encode functions are: 106 | 107 | * `tflac_encode_s16i`: `tflac_s16` samples in a 1-dimensional array (`tflac_s16*`), interleaved. 108 | * `tflac_encode_s16p`: `tflac_s16` samples in a multi-dimensional array (`tflac_s16**`). 109 | * `tflac_encode_s32i`: `tflac_int32` samples in a 1-dimensional array (`tflac_int32*`), interleaved. 110 | * `tflac_encode_s32p`: `tflac_int32` samples in a multi-dimensional array (`tflac_int32**`). 111 | 112 | `tflac_s16` is a typedef for `int16_t`, and `tflac_s32` is a typedef for `int32_t` 113 | in most cases. 114 | 115 | ```C 116 | 117 | #define TFLAC_IMPLEMENTATION 118 | #include "tflac.h" 119 | 120 | #define BLOCKSIZE 1152 121 | #define CHANNELS 2 122 | #define BITDEPTH 16 123 | #define SAMPLERATE 44100 124 | 125 | int main(int argc, const char *argv[]) { 126 | /* get the size we need to alloc for tflac internal memory */ 127 | tflac_u32 memory_size = tflac_size_memory(BLOCKSIZE); 128 | 129 | /* get a chunk for tflac internal memory */ 130 | void* memory = malloc(memory_size); 131 | 132 | /* get the size we need to alloc for the output buffer */ 133 | tflac_u32 buffer_size = tflac_size_frame(BLOCKSIZE, CHANNELS, BITDEPTH); 134 | 135 | /* get a hunk for our buffer */ 136 | void* buffer = malloc(buffer_size); 137 | 138 | /* this will be used to know how much of the buffer to write 139 | after calls to encode */ 140 | tflac_u32 buffer_used = 0; 141 | 142 | tflac t; 143 | 144 | tflac_init(&t); 145 | 146 | /* set some parameters */ 147 | t.blocksize = BLOCKSIZE; 148 | t.samplerate = SAMPLERATE; 149 | t.bitdepth = BITDEPTH; 150 | t.channels = CHANNELS; 151 | 152 | /* validate our parameters and tell tflac what memory to use */ 153 | tflac_validate(&t, memory, memory_size); 154 | 155 | /* open a file and write out the stream marker */ 156 | FILE* output = fopen("some-file.flac","wb"); 157 | fwrite("fLaC", 1, 4, output); 158 | 159 | /* encode the STREAMINFO block and write it out */ 160 | tflac_encode_streaminfo(&t, 1, buffer, buffer_size, &buffer_used); 161 | fwrite(buffer, 1, buffer_used, output); 162 | 163 | /* loop until you run out of audio */ 164 | while(have_audio()) { 165 | 166 | /* hand-waving over this part, here we're using a 2d array of audio samples 167 | and assuming it's always BLOCKSIZE samples */ 168 | tflac_s32** samples = get_some_audio_somehow(); 169 | 170 | /* encode your audio samples into a FLAC frame */ 171 | tflac_encode_s32p(&t, BLOCKSIZE, samples, buffer, buffer_size, &buffer_used); 172 | 173 | /* and write it out */ 174 | fwrite(buffer, 1, buffer_used, output); 175 | } 176 | 177 | free(buffer); 178 | free(ptr); 179 | } 180 | ``` 181 | 182 | 183 | ## LICENSE 184 | 185 | BSD Zero Clause (see the `LICENSE` file). 186 | -------------------------------------------------------------------------------- /demos/encoder-raw-mt/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean 2 | 3 | CFLAGS = -pthread -Wall -Wextra -Wconversion -Wdouble-promotion -g -O2 -I../.. 4 | LDFLAGS = -pthread 5 | 6 | all: encoder-raw-mt 7 | 8 | encoder-raw-mt: encoder-raw-mt.o 9 | $(CC) -o $@ $^ $(LDFLAGS) 10 | 11 | encoder-raw-mt.o: encoder-raw-mt.c ../../tflac.h 12 | $(CC) $(CFLAGS) -c -o $@ $< 13 | 14 | clean: 15 | rm -f encoder-raw-mt encoder-raw-mt.o 16 | -------------------------------------------------------------------------------- /demos/encoder-raw-mt/README.md: -------------------------------------------------------------------------------- 1 | # Simple Encoder with stereo decorrelation, multithreaded 2 | 3 | This creates 4 | 4 instances of a tflac encoder, each with a different stereo decorrelation 5 | mode set. It runs audio through all 4 encoders simultaneously and picks 6 | the smallest audio frame produced by each one. 7 | 8 | This is using Linux-specific syscalls (FUTEX) to handle thread sync, 9 | so this demo is Linux-only at the moment. 10 | 11 | Like the other raw-file encodes, this assumes you're reading a file of 12 | signed, 16-bit, little-endian, 2-channel, interleaved audio. 13 | 14 | You can produce raw audio samples with ffmpeg, then use this encoder to produce a basic 15 | FLAC file: 16 | 17 | ```bash 18 | ffmpeg -i source.flac -ar 44100 -ac 2 -f s16le pipe:1 | ./encoder-raw-mt - destination.flac 19 | ``` 20 | 21 | -------------------------------------------------------------------------------- /demos/encoder-raw-mt/encoder-raw-mt.c: -------------------------------------------------------------------------------- 1 | #define TFLAC_IMPLEMENTATION 2 | #include "tflac.h" 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | #include 9 | #include 10 | #include 11 | #include 12 | 13 | 14 | /* headerless wav can be created via ffmpeg like: 15 | * ffmpeg -i your-audio.mp3 -ar 44100 -ac 2 -f s16le your-audio.raw 16 | */ 17 | 18 | #define FRAME_SIZE 1152 19 | #define SAMPLERATE 44100 20 | #define BITDEPTH 16 21 | #define CHANNELS 2 22 | 23 | /* example that reads in a headerless WAV file and writes 24 | * out a FLAC file. assumes WAV has the defined parameters above */ 25 | 26 | #if (BITDEPTH == 16) 27 | 28 | static tflac_u16 unpack_u16le(const tflac_u8* d) { 29 | return (((tflac_u16)d[0]) ) | 30 | (((tflac_u16)d[1])<< 8); 31 | } 32 | 33 | static tflac_s16 unpack_s16le(const tflac_u8* d) { 34 | return (tflac_s16)unpack_u16le(d); 35 | } 36 | 37 | void 38 | repack_samples(tflac_s16 *s, tflac_u32 channels, tflac_u32 num) { 39 | tflac_u32 i = 0; 40 | while(i < (channels*num)) { 41 | s[i] = unpack_s16le( (tflac_u8*) (&s[i]) ); 42 | i++; 43 | } 44 | } 45 | 46 | typedef tflac_s16 sample; 47 | #define ENCODE_FUNC tflac_encode_s16i 48 | 49 | #elif (BITDEPTH == 32) 50 | static tflac_u32 unpack_u32le(const tflac_u8* d) { 51 | return (((tflac_u32)d[0]) ) | 52 | (((tflac_u32)d[1])<< 8 ) | 53 | (((tflac_u32)d[2])<< 16) | 54 | (((tflac_u32)d[3])<< 24); 55 | } 56 | 57 | static tflac_s32 unpack_s32le(const tflac_u8* d) { 58 | return (tflac_s32)unpack_u32le(d); 59 | } 60 | 61 | void 62 | repack_samples(tflac_s32 *s, tflac_u32 channels, tflac_u32 num) { 63 | tflac_u32 i = 0; 64 | while(i < (channels*num)) { 65 | s[i] = unpack_s32le( (tflac_u8*) (&s[i]) ); 66 | i++; 67 | } 68 | } 69 | 70 | typedef tflac_s32 sample; 71 | #define ENCODE_FUNC tflac_encode_s32i 72 | #else 73 | #error "unsupported bit depth" 74 | #endif 75 | 76 | typedef union encoder_atomic_int { 77 | void *align; 78 | int val; 79 | } encoder_atomic_int; 80 | 81 | typedef union encoder_atomic_u32 { 82 | void *align; 83 | tflac_u32 val; 84 | } encoder_atomic_u32; 85 | 86 | static inline void encoder_atomic_int_store(encoder_atomic_int *i, int val) { 87 | __atomic_store_n(&i->val, val, __ATOMIC_SEQ_CST); 88 | } 89 | 90 | static inline int encoder_atomic_int_load(encoder_atomic_int *i) { 91 | return (int)__atomic_load_n(&i->val, __ATOMIC_SEQ_CST); 92 | } 93 | 94 | static inline int encoder_atomic_int_inc(encoder_atomic_int *i) { 95 | return (int)__atomic_fetch_add(&i->val, 1, __ATOMIC_SEQ_CST); 96 | } 97 | 98 | static inline int encoder_atomic_int_dec(encoder_atomic_int *i) { 99 | return (int)__atomic_fetch_sub(&i->val, 1, __ATOMIC_SEQ_CST); 100 | } 101 | 102 | static inline int encoder_atomic_int_cas(encoder_atomic_int* i, int expected, int val) { 103 | return __atomic_compare_exchange_n(&i->val, &expected, val, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? expected : !expected; 104 | } 105 | 106 | static inline void encoder_atomic_u32_store(encoder_atomic_u32 *i, tflac_u32 val) { 107 | __atomic_store_n(&i->val, val, __ATOMIC_SEQ_CST); 108 | } 109 | 110 | static inline tflac_u32 encoder_atomic_u32_load(encoder_atomic_u32 *i) { 111 | return (tflac_u32)__atomic_load_n(&i->val, __ATOMIC_SEQ_CST); 112 | } 113 | 114 | static inline tflac_u32 encoder_atomic_u32_inc(encoder_atomic_u32 *i) { 115 | return (tflac_u32)__atomic_fetch_add(&i->val, 1, __ATOMIC_SEQ_CST); 116 | } 117 | 118 | static inline tflac_u32 encoder_atomic_u32_dec(encoder_atomic_u32 *i) { 119 | return (tflac_u32)__atomic_fetch_sub(&i->val, 1, __ATOMIC_SEQ_CST); 120 | } 121 | 122 | static inline tflac_u32 encoder_atomic_u32_cas(encoder_atomic_u32* i, tflac_u32 expected, tflac_u32 val) { 123 | return __atomic_compare_exchange_n(&i->val, &expected, val, 0, __ATOMIC_SEQ_CST, __ATOMIC_SEQ_CST) ? expected : !expected; 124 | } 125 | 126 | /* a struct to wrap an encoder so it can reference a shared sample buffer */ 127 | struct encoder { 128 | tflac t; 129 | tflac_u8 *buffer; 130 | tflac_u32 bufferlen; 131 | tflac_u32 bufferused; 132 | void *tflac_mem; 133 | pthread_t thread; 134 | sample* samples; 135 | encoder_atomic_u32* ready; 136 | encoder_atomic_int* running; 137 | tflac_u32* frames; 138 | }; 139 | 140 | typedef struct encoder encoder; 141 | 142 | void * encoder_run(void * ud) { 143 | tflac_u32 counter = 0; 144 | tflac_u32 ready = 0; 145 | encoder* e = (encoder *) ud; 146 | 147 | for(;;) { 148 | 149 | while( (ready = encoder_atomic_u32_load(e->ready)) == counter) { 150 | syscall(SYS_futex, &(e->ready->val), FUTEX_WAIT, counter, 0, 0, 0); 151 | } 152 | counter = ready; 153 | 154 | if(encoder_atomic_int_load(e->running) == -1) break; 155 | 156 | if(ENCODE_FUNC(&e->t, *(e->frames), e->samples, e->buffer, e->bufferlen, &e->bufferused) != 0) abort(); 157 | 158 | /* mark ourselves as complete and wake up the main thread to check status */ 159 | encoder_atomic_int_dec(e->running); 160 | syscall(SYS_futex, &(e->running->val), FUTEX_WAKE, 1, 0, 0, 0); 161 | } 162 | 163 | tflac_finalize(&e->t); 164 | 165 | return NULL; 166 | } 167 | 168 | 169 | void encoder_init(encoder* e, enum TFLAC_CHANNEL_MODE mode, sample* samples, encoder_atomic_u32* ready, encoder_atomic_int* running, tflac_u32* frames) { 170 | tflac_init(&e->t); 171 | 172 | e->t.samplerate = SAMPLERATE; 173 | e->t.channels = CHANNELS; 174 | e->t.bitdepth = BITDEPTH; 175 | e->t.blocksize = FRAME_SIZE; 176 | e->t.max_partition_order = 3; 177 | e->t.enable_md5 = 0; 178 | e->t.channel_mode = mode; 179 | 180 | e->tflac_mem = malloc(tflac_size_memory(e->t.blocksize)); 181 | if(e->tflac_mem == NULL) abort(); 182 | 183 | if(tflac_validate(&e->t, e->tflac_mem, tflac_size_memory(e->t.blocksize)) != 0) abort(); 184 | 185 | e->bufferlen = tflac_size_frame(FRAME_SIZE,CHANNELS,BITDEPTH); 186 | e->buffer = malloc(e->bufferlen); 187 | if(e->buffer == NULL) abort(); 188 | 189 | e->ready = ready; 190 | e->running = running; 191 | e->frames = frames; 192 | e->samples = samples; 193 | 194 | if(pthread_create(&e->thread, NULL, encoder_run, e) != 0) abort(); 195 | } 196 | 197 | void encoder_free(encoder* e) { 198 | free(e->buffer); 199 | free(e->tflac_mem); 200 | } 201 | 202 | int main(int argc, const char *argv[]) { 203 | FILE *input = NULL; 204 | FILE *output = NULL; 205 | tflac_u32 frames = 0; 206 | sample *samples = NULL; 207 | unsigned int i = 0; 208 | unsigned int smallest_frame = 0; 209 | encoder e[TFLAC_CHANNEL_MODE_COUNT]; 210 | encoder_atomic_u32 ready; 211 | encoder_atomic_int running; 212 | 213 | int r; 214 | 215 | if(argc < 3) { 216 | printf("Usage: %s /path/to/raw /path/to/flac\n",argv[0]); 217 | return 1; 218 | } 219 | 220 | tflac_detect_cpu(); 221 | 222 | if(strcmp(argv[1],"-") == 0) { 223 | input = stdin; 224 | } else { 225 | input = fopen(argv[1],"rb"); 226 | } 227 | 228 | if(input == NULL) return 1; 229 | 230 | output = fopen(argv[2],"wb"); 231 | if(output == NULL) { 232 | fclose(input); 233 | return 1; 234 | } 235 | 236 | samples = (sample *)malloc(sizeof(sample) * CHANNELS * FRAME_SIZE); 237 | if(!samples) abort(); 238 | 239 | encoder_atomic_u32_store(&ready,0); 240 | encoder_atomic_int_store(&running,0); 241 | 242 | for(i=0;i 0) { 257 | repack_samples(samples, CHANNELS, frames); 258 | 259 | encoder_atomic_int_store(&running,TFLAC_CHANNEL_MODE_COUNT); 260 | encoder_atomic_u32_inc(&ready); 261 | syscall(SYS_futex, &(e->ready->val), FUTEX_WAKE, INT_MAX, 0, 0, 0); 262 | while( (r = encoder_atomic_int_load(&running)) != 0) { 263 | syscall(SYS_futex, &(e->ready->val), FUTEX_WAKE, INT_MAX, 0, 0, 0); 264 | syscall(SYS_futex, &(running.val), FUTEX_WAIT, r, 0, 0, 0); 265 | } 266 | 267 | smallest_frame = 0; 268 | for(i=1;i e[smallest_frame].bufferused) { 278 | e[0].t.min_frame_size = e[smallest_frame].bufferused; 279 | } 280 | if(e[0].t.max_frame_size < e[smallest_frame].bufferused) { 281 | e[0].t.max_frame_size = e[smallest_frame].bufferused; 282 | } 283 | } 284 | 285 | encoder_atomic_int_store(&running,-1); 286 | encoder_atomic_u32_inc(&ready); 287 | syscall(SYS_futex, &(e->ready->val), FUTEX_WAKE, INT_MAX, 0, 0, 0); 288 | 289 | for(i=0;i 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | /* headerless wav can be created via ffmpeg like: 11 | * ffmpeg -i your-audio.mp3 -ar 44100 -ac 2 -f s16le your-audio.raw 12 | */ 13 | 14 | #define FRAME_SIZE 1152 15 | #define SAMPLERATE 44100 16 | #define BITDEPTH 16 17 | #define CHANNELS 2 18 | 19 | /* example that reads in a headerless WAV file and writes 20 | * out a FLAC file. assumes WAV has the defined parameters above */ 21 | 22 | #if (BITDEPTH == 16) 23 | 24 | static tflac_u16 unpack_u16le(const tflac_u8* d) { 25 | return (((tflac_u16)d[0]) ) | 26 | (((tflac_u16)d[1])<< 8); 27 | } 28 | 29 | static tflac_s16 unpack_s16le(const tflac_u8* d) { 30 | return (tflac_s16)unpack_u16le(d); 31 | } 32 | 33 | void 34 | repack_samples(tflac_s16 *s, tflac_u32 channels, tflac_u32 num) { 35 | tflac_u32 i = 0; 36 | while(i < (channels*num)) { 37 | s[i] = unpack_s16le( (tflac_u8*) (&s[i]) ); 38 | i++; 39 | } 40 | } 41 | 42 | typedef tflac_s16 sample; 43 | #define ENCODE_FUNC tflac_encode_s16i 44 | 45 | #elif (BITDEPTH == 32) 46 | static tflac_u32 unpack_u32le(const tflac_u8* d) { 47 | return (((tflac_u32)d[0]) ) | 48 | (((tflac_u32)d[1])<< 8 ) | 49 | (((tflac_u32)d[2])<< 16) | 50 | (((tflac_u32)d[3])<< 24); 51 | } 52 | 53 | static tflac_s32 unpack_s32le(const tflac_u8* d) { 54 | return (tflac_s32)unpack_u32le(d); 55 | } 56 | 57 | void 58 | repack_samples(tflac_s32 *s, tflac_u32 channels, tflac_u32 num) { 59 | tflac_u32 i = 0; 60 | while(i < (channels*num)) { 61 | s[i] = unpack_s32le( (tflac_u8*) (&s[i]) ); 62 | i++; 63 | } 64 | } 65 | 66 | typedef tflac_s32 sample; 67 | #define ENCODE_FUNC tflac_encode_s32i 68 | #else 69 | #error "unsupported bit depth" 70 | #endif 71 | 72 | /* a struct to wrap an encoder so it can reference a shared sample buffer */ 73 | struct encoder { 74 | tflac t; 75 | tflac_u8 *buffer; 76 | tflac_u32 bufferlen; 77 | tflac_u32 bufferused; 78 | void *tflac_mem; 79 | }; 80 | 81 | typedef struct encoder encoder; 82 | 83 | void encoder_init(encoder* e, enum TFLAC_CHANNEL_MODE mode) { 84 | tflac_init(&e->t); 85 | 86 | e->t.samplerate = SAMPLERATE; 87 | e->t.channels = CHANNELS; 88 | e->t.bitdepth = BITDEPTH; 89 | e->t.blocksize = FRAME_SIZE; 90 | e->t.max_partition_order = 3; 91 | e->t.enable_md5 = 0; 92 | e->t.channel_mode = mode; 93 | 94 | e->tflac_mem = malloc(tflac_size_memory(e->t.blocksize)); 95 | if(e->tflac_mem == NULL) abort(); 96 | 97 | if(tflac_validate(&e->t, e->tflac_mem, tflac_size_memory(e->t.blocksize)) != 0) abort(); 98 | 99 | e->bufferlen = tflac_size_frame(FRAME_SIZE,CHANNELS,BITDEPTH); 100 | e->buffer = malloc(e->bufferlen); 101 | if(e->buffer == NULL) abort(); 102 | } 103 | 104 | void encoder_free(encoder* e) { 105 | free(e->buffer); 106 | free(e->tflac_mem); 107 | } 108 | 109 | void encoder_run(encoder* e, tflac_u32 frames, sample* samples) { 110 | if(ENCODE_FUNC(&e->t, frames, samples, e->buffer, e->bufferlen, &e->bufferused) != 0) abort(); 111 | } 112 | 113 | 114 | int main(int argc, const char *argv[]) { 115 | FILE *input = NULL; 116 | FILE *output = NULL; 117 | tflac_u32 frames = 0; 118 | sample *samples = NULL; 119 | unsigned int i = 0; 120 | unsigned int smallest_frame = 0; 121 | encoder e[TFLAC_CHANNEL_MODE_COUNT]; 122 | 123 | if(argc < 3) { 124 | printf("Usage: %s /path/to/raw /path/to/flac\n",argv[0]); 125 | return 1; 126 | } 127 | 128 | tflac_detect_cpu(); 129 | 130 | if(strcmp(argv[1],"-") == 0) { 131 | input = stdin; 132 | } else { 133 | input = fopen(argv[1],"rb"); 134 | } 135 | 136 | if(input == NULL) return 1; 137 | 138 | output = fopen(argv[2],"wb"); 139 | if(output == NULL) { 140 | fclose(input); 141 | return 1; 142 | } 143 | 144 | for(i=0;i 0) { 162 | repack_samples(samples, CHANNELS, frames); 163 | 164 | for(i=0;i e[smallest_frame].bufferused) { 179 | e[0].t.min_frame_size = e[smallest_frame].bufferused; 180 | } 181 | if(e[0].t.max_frame_size < e[smallest_frame].bufferused) { 182 | e[0].t.max_frame_size = e[smallest_frame].bufferused; 183 | } 184 | } 185 | 186 | /* this will calculate the final MD5 */ 187 | for(i=0;i 5 | #include 6 | #include 7 | #include 8 | 9 | 10 | /* headerless wav can be created via ffmpeg like: 11 | * ffmpeg -i your-audio.mp3 -ar 44100 -ac 2 -f s16le your-audio.raw 12 | */ 13 | 14 | #define FRAME_SIZE 1152 15 | #define SAMPLERATE 44100 16 | #define BITDEPTH 16 17 | #define CHANNELS 2 18 | 19 | /* example that reads in a headerless WAV file and writes 20 | * out a FLAC file. assumes WAV has the defined parameters above */ 21 | 22 | #if (BITDEPTH == 16) 23 | 24 | static tflac_u16 unpack_u16le(const tflac_u8* d) { 25 | return (((tflac_u16)d[0]) ) | 26 | (((tflac_u16)d[1])<< 8); 27 | } 28 | 29 | static tflac_s16 unpack_s16le(const tflac_u8* d) { 30 | return (tflac_s16)unpack_u16le(d); 31 | } 32 | 33 | void 34 | repack_samples(tflac_s16 *s, tflac_u32 channels, tflac_u32 num) { 35 | tflac_u32 i = 0; 36 | while(i < (channels*num)) { 37 | s[i] = unpack_s16le( (tflac_u8*) (&s[i]) ); 38 | i++; 39 | } 40 | } 41 | 42 | typedef tflac_s16 sample; 43 | #define ENCODE_FUNC tflac_encode_s16i 44 | 45 | #elif (BITDEPTH == 32) 46 | static tflac_u32 unpack_u32le(const tflac_u8* d) { 47 | return (((tflac_u32)d[0]) ) | 48 | (((tflac_u32)d[1])<< 8 ) | 49 | (((tflac_u32)d[2])<< 16) | 50 | (((tflac_u32)d[3])<< 24); 51 | } 52 | 53 | static tflac_s32 unpack_s32le(const tflac_u8* d) { 54 | return (tflac_s32)unpack_u32le(d); 55 | } 56 | 57 | void 58 | repack_samples(tflac_s32 *s, tflac_u32 channels, tflac_u32 num) { 59 | tflac_u32 i = 0; 60 | while(i < (channels*num)) { 61 | s[i] = unpack_s32le( (tflac_u8*) (&s[i]) ); 62 | i++; 63 | } 64 | } 65 | 66 | typedef tflac_s32 sample; 67 | #define ENCODE_FUNC tflac_encode_s32i 68 | #else 69 | #error "unsupported bit depth" 70 | #endif 71 | 72 | #define DUMP_SIZES 1 73 | #define DUMP_COUNTS 1 74 | 75 | int main(int argc, const char *argv[]) { 76 | tflac_u8 *buffer = NULL; 77 | tflac_u32 bufferlen = 0; 78 | tflac_u32 bufferused = 0; 79 | FILE *input = NULL; 80 | FILE *output = NULL; 81 | tflac_u32 frames = 0; 82 | sample *samples = NULL; 83 | void *tflac_mem = NULL; 84 | tflac t; 85 | 86 | #if DUMP_SIZES 87 | printf("tflac_size(): %u\n", tflac_size()); 88 | printf("tflac_size_memory(%u): %u\n", FRAME_SIZE, tflac_size_memory(FRAME_SIZE)); 89 | printf("TFLAC_SIZE_MEMORY(%u): %lu\n", FRAME_SIZE, TFLAC_SIZE_MEMORY(FRAME_SIZE)); 90 | printf("tflac_size_frame(%u,%u,%u): %u\n", FRAME_SIZE, CHANNELS, BITDEPTH, tflac_size_frame(FRAME_SIZE, CHANNELS, BITDEPTH)); 91 | printf("TFLAC_SIZE_FRAME(%u,%u,%u): %lu\n", FRAME_SIZE, CHANNELS, BITDEPTH, TFLAC_SIZE_FRAME(FRAME_SIZE, CHANNELS, BITDEPTH)); 92 | #endif 93 | 94 | if(argc < 3) { 95 | printf("Usage: %s /path/to/raw /path/to/flac\n",argv[0]); 96 | return 1; 97 | } 98 | 99 | if(tflac_size_memory(FRAME_SIZE) != TFLAC_SIZE_MEMORY(FRAME_SIZE)) { 100 | printf("Error with needed memory size: %u != %lu\n", 101 | tflac_size_memory(FRAME_SIZE),TFLAC_SIZE_MEMORY(FRAME_SIZE)); 102 | return 1; 103 | } 104 | 105 | if(tflac_size_frame(FRAME_SIZE,CHANNELS,BITDEPTH) != TFLAC_SIZE_FRAME(FRAME_SIZE,CHANNELS,BITDEPTH)) { 106 | printf("Error with needed frame size: %u != %u\n", 107 | tflac_size_frame(FRAME_SIZE,CHANNELS,BITDEPTH),TFLAC_SIZE_FRAME(FRAME_SIZE,CHANNELS,BITDEPTH)); 108 | return 1; 109 | } 110 | 111 | tflac_detect_cpu(); 112 | tflac_init(&t); 113 | 114 | printf("CPU feature enabled: "); 115 | if(tflac_get_enable_sse2(&t)) { 116 | printf("SSE2"); 117 | } else if(tflac_get_enable_ssse3(&t)) { 118 | printf("SSSE3"); 119 | } else if(tflac_get_enable_sse4_1(&t)) { 120 | printf("SSE4.1"); 121 | } else { 122 | printf("none"); 123 | } 124 | printf("\n"); 125 | 126 | t.samplerate = SAMPLERATE; 127 | t.channels = CHANNELS; 128 | t.bitdepth = BITDEPTH; 129 | t.blocksize = FRAME_SIZE; 130 | t.max_partition_order = 3; 131 | t.enable_md5 = 1; 132 | t.channel_mode = TFLAC_CHANNEL_INDEPENDENT; 133 | 134 | if(strcmp(argv[1],"-") == 0) { 135 | input = stdin; 136 | } else { 137 | input = fopen(argv[1],"rb"); 138 | } 139 | 140 | if(input == NULL) return 1; 141 | 142 | output = fopen(argv[2],"wb"); 143 | if(output == NULL) { 144 | fclose(input); 145 | return 1; 146 | } 147 | 148 | 149 | tflac_mem = malloc(tflac_size_memory(t.blocksize)); 150 | if(tflac_mem == NULL) abort(); 151 | 152 | tflac_set_constant_subframe(&t, 1); 153 | tflac_set_fixed_subframe(&t, 1); 154 | 155 | if(tflac_validate(&t, tflac_mem, tflac_size_memory(t.blocksize)) != 0) abort(); 156 | 157 | bufferlen = tflac_size_frame(FRAME_SIZE,CHANNELS,BITDEPTH); 158 | buffer = malloc(bufferlen); 159 | if(buffer == NULL) abort(); 160 | 161 | samples = (sample *)malloc(sizeof(sample) * CHANNELS * FRAME_SIZE); 162 | if(!samples) abort(); 163 | 164 | fwrite("fLaC",1,4,output); 165 | 166 | /* we'll write out an empty STREAMINFO block and overwrite later to get MD5 checksum 167 | * and sample count */ 168 | tflac_encode_streaminfo(&t, 1, buffer, bufferlen, &bufferused); 169 | fwrite(buffer,1,bufferused,output); 170 | 171 | while((frames = fread(samples,sizeof(sample) * CHANNELS, FRAME_SIZE, input)) > 0) { 172 | repack_samples(samples, CHANNELS, frames); 173 | 174 | if(ENCODE_FUNC(&t, frames, samples, buffer, bufferlen, &bufferused) != 0) abort(); 175 | fwrite(buffer,1,bufferused,output); 176 | } 177 | 178 | /* this will calculate the final MD5 */ 179 | tflac_finalize(&t); 180 | 181 | /* now we overwrite our original STREAMINFO with an updated one */ 182 | fseek(output,4,SEEK_SET); 183 | tflac_encode_streaminfo(&t, 1, buffer, bufferlen, &bufferused); 184 | fwrite(buffer,1,bufferused,output); 185 | 186 | #if DUMP_COUNTS 187 | do { 188 | unsigned int i,j; 189 | for(i=0;i<2;i++) { 190 | printf("channel %u:\n",i+1); 191 | for(j=0;j 7 | #include 8 | #include 9 | #include 10 | 11 | int main(int argc, const char *argv[]) { 12 | tflac_u8 *buffer = NULL; 13 | tflac_u32 bufferlen = 0; 14 | tflac_u32 bufferused = 0; 15 | FILE *input = NULL; 16 | FILE *output = NULL; 17 | tflac_u32 frames = 0; 18 | tflac_s32 *samples = NULL; 19 | void *tflac_mem = NULL; 20 | tflac_u32 frame_size = 1152; 21 | wav_decoder w = WAV_DECODER_ZERO; 22 | tflac t; 23 | 24 | tflac_init(&t); 25 | 26 | if(argc < 3) { 27 | printf("Usage: %s /path/to/raw /path/to/flac\n",argv[0]); 28 | return 1; 29 | } 30 | 31 | if(strcmp(argv[1],"-") == 0) { 32 | input = stdin; 33 | } else { 34 | input = fopen(argv[1],"rb"); 35 | } 36 | 37 | if(input == NULL) return 1; 38 | 39 | if(wav_decoder_open(&w,input) != 0) return 1; 40 | 41 | t.samplerate = w.samplerate; 42 | t.channels = w.channels; 43 | t.bitdepth = w.bitdepth; 44 | t.blocksize = frame_size; 45 | t.max_partition_order = 3; 46 | 47 | tflac_mem = malloc(tflac_size_memory(t.blocksize)); 48 | if(tflac_mem == NULL) abort(); 49 | 50 | tflac_set_constant_subframe(&t, 1); 51 | tflac_set_fixed_subframe(&t, 1); 52 | 53 | if(tflac_validate(&t, tflac_mem, tflac_size_memory(t.blocksize)) != 0) abort(); 54 | 55 | bufferlen = tflac_size_frame(t.blocksize,t.channels,t.bitdepth); 56 | buffer = malloc(bufferlen); 57 | if(buffer == NULL) abort(); 58 | 59 | samples = (tflac_s32 *)malloc(sizeof(tflac_s32) * t.channels * t.blocksize); 60 | if(!samples) abort(); 61 | 62 | output = fopen(argv[2],"wb"); 63 | if(output == NULL) { 64 | fclose(input); 65 | return 1; 66 | } 67 | 68 | fwrite("fLaC",1,4,output); 69 | 70 | /* we'll write out an empty STREAMINFO block and overwrite later to get MD5 checksum 71 | * and sample count */ 72 | tflac_encode_streaminfo(&t, 1, buffer, bufferlen, &bufferused); 73 | fwrite(buffer,1,bufferused,output); 74 | 75 | while( wav_decoder_decode(&w, samples, t.blocksize, &frames) == 0) { 76 | if(tflac_encode_s32i(&t, frames, samples, buffer, bufferlen, &bufferused) != 0) abort(); 77 | fwrite(buffer,1,bufferused,output); 78 | } 79 | 80 | /* this will calculate the final MD5 */ 81 | tflac_finalize(&t); 82 | 83 | /* now we overwrite our original STREAMINFO with an updated one */ 84 | fseek(output,4,SEEK_SET); 85 | tflac_encode_streaminfo(&t, 1, buffer, bufferlen, &bufferused); 86 | fwrite(buffer,1,bufferused,output); 87 | 88 | fclose(input); 89 | fclose(output); 90 | free(tflac_mem); 91 | free(samples); 92 | free(buffer); 93 | 94 | return 0; 95 | } 96 | 97 | -------------------------------------------------------------------------------- /demos/encoder/wavdecoder.c: -------------------------------------------------------------------------------- 1 | #include "wavdecoder.h" 2 | 3 | #define CHUNK_ID_RIFF 0x52494646 4 | #define CHUNK_ID_WAVE 0x57415645 5 | #define CHUNK_ID_FMT 0x666d7420 6 | #define CHUNK_ID_DATA 0x64617461 7 | 8 | #define FORMAT_TAG_PCM 0x0001 9 | #define FORMAT_TAG_EXTENSIBLE 0xfffe 10 | 11 | static tflac_u16 unpack_u16le(const uint8_t* d) { 12 | return (((tflac_u16)d[0]) ) | 13 | (((tflac_u16)d[1])<< 8); 14 | } 15 | 16 | static tflac_u32 unpack_u32be(const uint8_t* d) { 17 | return (((tflac_u32)d[0])<<24) | 18 | (((tflac_u32)d[1])<<16) | 19 | (((tflac_u32)d[2])<<8 ) | 20 | (((tflac_u32)d[3]) ); 21 | } 22 | 23 | static tflac_u32 unpack_u24le(const uint8_t* d) { 24 | return (((tflac_u32)d[0]) ) | 25 | (((tflac_u32)d[1])<< 8) | 26 | (((tflac_u32)d[2])<<16); 27 | } 28 | 29 | static tflac_s32 unpack_s24le(const uint8_t* d) { 30 | tflac_u32 val = unpack_u24le(d); 31 | tflac_s32 r; 32 | struct { 33 | signed int x:24; 34 | } s; 35 | r = s.x = val; 36 | return r; 37 | 38 | } 39 | 40 | static tflac_u32 unpack_u32le(const uint8_t* d) { 41 | return (((tflac_u32)d[0]) ) | 42 | (((tflac_u32)d[1])<< 8) | 43 | (((tflac_u32)d[2])<<16) | 44 | (((tflac_u32)d[3])<<24); 45 | } 46 | 47 | static tflac_s32 unpack_s32le(const uint8_t* d) { 48 | return (tflac_s32) unpack_u32le(d); 49 | } 50 | 51 | static int16_t unpack_s16le(const uint8_t* d) { 52 | return (int16_t) unpack_u16le(d); 53 | } 54 | 55 | static int read_s16le(FILE* f, int16_t* val) { 56 | uint8_t buffer[2]; 57 | if(fread(buffer,1,2,f) != 2) return 1; 58 | *val = unpack_s16le(buffer); 59 | return 0; 60 | } 61 | 62 | static int read_u16le(FILE* f, tflac_u16* val) { 63 | uint8_t buffer[2]; 64 | if(fread(buffer,1,2,f) != 2) return 1; 65 | *val = unpack_u16le(buffer); 66 | return 0; 67 | } 68 | 69 | static int read_s24le(FILE* f, tflac_s32* val) { 70 | uint8_t buffer[3]; 71 | if(fread(buffer,1,3,f) != 3) return 1; 72 | *val = unpack_s24le(buffer); 73 | return 0; 74 | } 75 | 76 | static int read_s32le(FILE* f, tflac_s32* val) { 77 | uint8_t buffer[4]; 78 | if(fread(buffer,1,4,f) != 4) return 1; 79 | *val = unpack_s32le(buffer); 80 | return 0; 81 | } 82 | 83 | static int read_u32le(FILE* f, tflac_u32* val) { 84 | uint8_t buffer[4]; 85 | if(fread(buffer,1,4,f) != 4) return 1; 86 | *val = unpack_u32le(buffer); 87 | return 0; 88 | } 89 | 90 | static int read_u32be(FILE* f, tflac_u32* val) { 91 | uint8_t buffer[4]; 92 | if(fread(buffer,1,4,f) != 4) return 1; 93 | *val = unpack_u32be(buffer); 94 | return 0; 95 | } 96 | 97 | static int readsample_8(wav_decoder* w, tflac_s32* val) { 98 | uint8_t v; 99 | tflac_s32 t; 100 | if( fread(&v,1,1,w->input) != 1) return 1; 101 | t = (tflac_s32)v; 102 | t -= 128; 103 | *val = t; 104 | return 0; 105 | } 106 | 107 | static int readsample_16(wav_decoder* w, tflac_s32* val) { 108 | int16_t tmp; 109 | int r; 110 | 111 | if( (r = read_s16le(w->input, &tmp)) != 0) return r; 112 | *val = (tflac_s32)tmp; 113 | return 0; 114 | } 115 | 116 | static int readsample_24(wav_decoder* w, tflac_s32* val) { 117 | return read_s24le(w->input, val); 118 | } 119 | 120 | static int readsample_32(wav_decoder* w, tflac_s32* val) { 121 | return read_s32le(w->input, val); 122 | } 123 | 124 | #define TRY(x) if( (r = (x)) != 0) return r 125 | #define CHECK(x,msg) if(!(x)) { fprintf(stderr,msg"\n"); return 1; } 126 | 127 | int wav_decoder_open(wav_decoder *w, FILE* f) { 128 | int r = 1; 129 | 130 | tflac_u16 tmp_u16; 131 | tflac_u32 tmp_u32; 132 | tflac_u32 chunk_id; 133 | tflac_u32 chunk_len; 134 | tflac_u16 formattag; 135 | tflac_u32 guid0 = 0; 136 | tflac_u32 guid1 = 0; 137 | tflac_u32 guid2 = 0; 138 | tflac_u32 guid3 = 0; 139 | tflac_u32 samplesize = 0; 140 | 141 | w->input = f; 142 | w->readsample = NULL; 143 | w->length = 0; 144 | w->channels = 0; 145 | w->wasted_bits = 0; 146 | 147 | TRY( read_u32be(w->input, &chunk_id) ); 148 | CHECK(chunk_id == CHUNK_ID_RIFF,"Input file is not RIFF"); 149 | 150 | TRY( read_u32be(w->input, &tmp_u32) ); 151 | CHECK(tmp_u32 != 0xFFFFFFFF, "RIFF chunk size set to -1"); 152 | 153 | TRY( read_u32be(w->input, &chunk_id) ); 154 | CHECK(chunk_id == CHUNK_ID_WAVE,"Input file is not WAVE"); 155 | 156 | TRY( read_u32be(w->input, &chunk_id) ); 157 | while(chunk_id != CHUNK_ID_FMT) { 158 | TRY( read_u32le(w->input, &chunk_len) ); 159 | TRY( fseek(w->input,chunk_len,SEEK_CUR) ); 160 | TRY( read_u32be(w->input, &chunk_id) ); 161 | } 162 | TRY( read_u32le(w->input, &chunk_len) ); 163 | 164 | TRY( read_u16le(w->input, &formattag) ); 165 | CHECK(formattag == FORMAT_TAG_PCM || formattag == FORMAT_TAG_EXTENSIBLE,"WAVE not in compatible format"); 166 | 167 | TRY( read_u16le(w->input, &w->channels) ); 168 | TRY( read_u32le(w->input, &w->samplerate) ); 169 | TRY( read_u32le(w->input, &tmp_u32) ); /* average bytes per second, ignore */ 170 | TRY( read_u16le(w->input, &tmp_u16) ); 171 | TRY( read_u16le(w->input, &w->bitdepth) ); 172 | CHECK(w->bitdepth % 8 == 0, "WAVE file has bitdepth that isn't divisible by 8"); 173 | CHECK(w->bitdepth * w->channels / 8 == tmp_u16, "WAVE file has unexpected block alignment"); 174 | samplesize = w->bitdepth / 8; 175 | 176 | if(formattag == FORMAT_TAG_EXTENSIBLE) { 177 | TRY( read_u16le(w->input, &tmp_u16) ); 178 | CHECK(tmp_u16 == 22, "WAVE file has FORMAT_TAG_EXTENSIBLE but extensible data length is not 22"); 179 | TRY( read_u16le(w->input, &tmp_u16) ); 180 | w->wasted_bits = w->bitdepth - tmp_u16; 181 | w->bitdepth -= w->wasted_bits; 182 | 183 | TRY( read_u32le(w->input, &w->channelmask) ); 184 | 185 | TRY( read_u32le(w->input, &guid0) ); 186 | TRY( read_u32le(w->input, &guid1) ); 187 | TRY( read_u32le(w->input, &guid2) ); 188 | TRY( read_u32le(w->input, &guid3) ); 189 | 190 | CHECK(guid0 == 0x00000001 && 191 | guid1 == 0x00100000 && 192 | guid2 == 0xaa000080 && 193 | guid3 == 0x719b3800, "Unknown subformat GUID found"); 194 | } else { 195 | switch(w->channels) { 196 | case 1: w->channelmask = 0x04; break; 197 | case 2: w->channelmask = 0x03; break; 198 | default: { 199 | fprintf(stderr,"For non-extensible waves this tool only handles 1 or 2 channels\n"); 200 | return 1; 201 | } 202 | } 203 | } 204 | 205 | TRY( read_u32be(w->input, &chunk_id) ); 206 | while(chunk_id != CHUNK_ID_DATA) { 207 | TRY( read_u32le(w->input, &chunk_len) ); 208 | TRY( fseek(w->input,chunk_len,SEEK_CUR) ); 209 | 210 | TRY( read_u32be(w->input, &chunk_id) ); 211 | } 212 | TRY( read_u32le(w->input, &chunk_len) ); 213 | 214 | /* we're ready to read samples */ 215 | w->length = chunk_len / samplesize / w->channels; 216 | switch(samplesize) { 217 | case 1: w->readsample = readsample_8; break; 218 | case 2: w->readsample = readsample_16; break; 219 | case 3: w->readsample = readsample_24; break; 220 | case 4: w->readsample = readsample_32; break; 221 | default: { 222 | fprintf(stderr,"Unknown sample size\n"); 223 | return 1; 224 | } 225 | } 226 | 227 | return r; 228 | } 229 | 230 | 231 | int wav_decoder_decode(wav_decoder* w, tflac_s32* buffer, tflac_u32 len, tflac_u32* r) { 232 | tflac_u32 i = 0; 233 | if(w->length == 0) return 1; 234 | 235 | len = len > w->length ? w->length : len; 236 | 237 | for(i=0;ichannels);i++) { 238 | if(w->readsample(w,&buffer[i]) != 0) return -1; 239 | buffer[i] /= ( 1 << w->wasted_bits) ; 240 | } 241 | 242 | w->length -= len; 243 | *r = len; 244 | return 0; 245 | } 246 | -------------------------------------------------------------------------------- /demos/encoder/wavdecoder.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | /* including tflac.h for types */ 4 | #include "tflac.h" 5 | 6 | /* a basic WAV decoder, just works with FILE*, enough for our demo purposes */ 7 | 8 | struct wav_decoder { 9 | FILE* input; 10 | int (*readsample)(struct wav_decoder*, tflac_s32* sample); 11 | tflac_u32 length; /* the length of the data chunk, in samples */ 12 | tflac_u16 channels; 13 | tflac_u32 samplerate; 14 | tflac_u16 bitdepth; /* already has wasted_bits subtracted */ 15 | tflac_u32 channelmask; 16 | 17 | tflac_u16 wasted_bits; 18 | }; 19 | 20 | typedef struct wav_decoder wav_decoder; 21 | 22 | #define WAV_DECODER_ZERO { \ 23 | .input = NULL, \ 24 | .readsample = NULL, \ 25 | .length = 0, \ 26 | .channels = 0, \ 27 | .samplerate = 0, \ 28 | .bitdepth = 0, \ 29 | .wasted_bits = 0, \ 30 | } 31 | 32 | int wav_decoder_open(wav_decoder*, FILE*); 33 | int wav_decoder_decode(wav_decoder*, tflac_s32* buffer, tflac_u32 len, tflac_u32* read); 34 | -------------------------------------------------------------------------------- /demos/wasm/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean 2 | 3 | all: tflac.wasm 4 | 5 | tflac.wasm: tflac.o 6 | wasm-ld --no-entry --export-all -o $@ $^ 7 | 8 | tflac.o: tflac.ll 9 | llc -march=wasm32 -filetype obj -o $@ $^ 10 | 11 | tflac.ll: tflac.c ../../tflac.h 12 | clang -DNDEBUG --target=wasm32 -mbulk-memory -emit-llvm -flto -Os -c -S -o $@ $< 13 | 14 | clean: 15 | rm -f tflac.wasm tflac.o tflac.ll 16 | -------------------------------------------------------------------------------- /demos/wasm/README.md: -------------------------------------------------------------------------------- 1 | # wasm demo 2 | 3 | A small demo of using tflac as a WASM module. 4 | 5 | It just creates a 10-second sine wave. Requires NodeJS, 6 | though I think this could be adapted to run in a browser 7 | pretty easily. 8 | 9 | I couldn't find a great tutorial covering using memory 10 | the way I wanted to use it - by pre-allocating a chunk 11 | of memory on the JavaScript side and providing pointers 12 | in to WASM. 13 | 14 | The point of this demo is to demonstrate how to do exactly that. 15 | 16 | Requires NodeJS, clang, lld (for wasm-ld), llvm (for llc). 17 | -------------------------------------------------------------------------------- /demos/wasm/demo.mjs: -------------------------------------------------------------------------------- 1 | /* our FLAC file audio properties */ 2 | const BLOCK_SIZE = 1152; 3 | const CHANNELS = 2; 4 | const BITDEPTH = 16; 5 | const SAMPLE_RATE = 44100; 6 | 7 | /* We'll generate a sine wave */ 8 | const LENGTH = 10; // in seconds 9 | const FREQ = 440; // tone frequency 10 | const AMP = Math.ceil(0x7FFF * .5); // limit to about half of max volume 11 | const STEP = FREQ * 2 * Math.PI/SAMPLE_RATE; 12 | 13 | import { writeFile, readFile } from 'node:fs/promises'; 14 | import { TFlac } from './tflac.mjs'; 15 | 16 | /* load up the WASM module */ 17 | const wasmBuffer = await readFile('tflac.wasm'); 18 | await TFlac.load(wasmBuffer); 19 | 20 | /* create our encoder */ 21 | const tflac = new TFlac(BLOCK_SIZE, CHANNELS, BITDEPTH, SAMPLE_RATE); 22 | 23 | /* generate a sine wave, chunk it into blocks, and encode */ 24 | const samples = new Int32Array(BLOCK_SIZE * CHANNELS); 25 | let o = 0; 26 | let p = 0; 27 | let i = 0; 28 | while(i < SAMPLE_RATE * LENGTH) { 29 | o = i % BLOCK_SIZE; 30 | 31 | if(i > 0 && o === 0) { 32 | // we're starting a new block, encode previous block */ 33 | tflac.encode_s32i(BLOCK_SIZE,samples); 34 | } 35 | 36 | for(let j = 0; j < CHANNELS; j++) { 37 | samples[ (o * CHANNELS) + j] = AMP * Math.sin(p); 38 | } 39 | 40 | p += STEP; 41 | i++; 42 | } 43 | 44 | /* check for final block */ 45 | o = i % BLOCK_SIZE; 46 | if(o) { 47 | tflac.encode_s32i(o,samples); 48 | } 49 | 50 | /* finalize MD5 and update internal STREAMINFO block */ 51 | tflac.finalize(); 52 | 53 | /* we'll collect all the encoded chunks, turn it into a blob, write it out. 54 | * I'm sure there's better ways to do this, I went with Blob because 55 | * I think that's what I'd want to do in a browser - create a Blob and 56 | * download it. */ 57 | const b = new Blob(tflac.chunks, { 58 | type: 'audio/flac', 59 | }); 60 | const buf = await b.arrayBuffer(); 61 | 62 | await writeFile('demo.flac',Buffer.from(buf)); 63 | console.log('successfully wrote demo.flac'); 64 | -------------------------------------------------------------------------------- /demos/wasm/tflac.c: -------------------------------------------------------------------------------- 1 | #define TFLAC_IMPLEMENTATION 2 | #include "../../tflac.h" 3 | 4 | -------------------------------------------------------------------------------- /demos/wasm/tflac.mjs: -------------------------------------------------------------------------------- 1 | /* stores a global Module and Instance of tflac, 2 | * we use this first instance to calculate the needed 3 | * memory pages when creating a new instance. 4 | * 5 | * An alternative would be to just load the wasm module, 6 | * then grow the memory of the loaded instance. */ 7 | let mod = null; 8 | 9 | function calc_needed_pages(block_size,channels,bitdepth) { 10 | 11 | /* WASM uses flat memory - just one big buffer of data. So 12 | * we need to calculate the total amount of memory, then request 13 | * the needed pages. */ 14 | 15 | const struct_size = mod.instance.exports.tflac_size(); 16 | const memory_size = mod.instance.exports.tflac_size_memory(block_size); 17 | const frame_size = mod.instance.exports.tflac_size_frame(block_size, channels, bitdepth); 18 | const samples_size = Int32Array.BYTES_PER_ELEMENT * block_size * channels; 19 | const total_size = 20 | struct_size + // storage for the tflac struct 21 | memory_size + // storage for tflac internal memory (residuals) 22 | frame_size + // storage for the output frame 23 | Int32Array.BYTES_PER_ELEMENT + // to ensure we align Int32Array on 4-byte boundary 24 | samples_size + // storage for our incoming audio samples 25 | (1 * Uint32Array.BYTES_PER_ELEMENT) + // single-element Uint32Array for used ptr 26 | mod.instance.exports.__heap_base.value; // base offset of where heap memory starts:w 27 | 28 | const pages = Math.ceil(total_size / 65536); // convert to pages, each page is 64k 29 | return pages; 30 | } 31 | 32 | class TFlac { 33 | 34 | /* static method to load the global module from a buffer, this needs to be called 35 | * before creating any instances of the class. */ 36 | static async load(buf) { 37 | mod = await WebAssembly.instantiate(buf); 38 | } 39 | 40 | constructor(block_size, channels, bitdepth, samplerate) { 41 | if(mod === null) throw new Error("WASM module not loaded"); 42 | 43 | this.chunks = []; // we'll store things like the fLaC stream marker, STREAMINFO block and audio frames 44 | // as an array of Uint8Array 45 | 46 | /* Note: In a "real" version of this I wouldn't store chunks, but instead 47 | * emit them as needed, or return them from calls to encode, etc. 48 | * The main point of this demo was to figure out how memory management works with WASM 49 | * so I'm just going to collect them all and let the caller get them at the end. */ 50 | 51 | /* calculate how much memory we'll need, request it, create a new instance */ 52 | const needed_pages = calc_needed_pages(block_size, channels, bitdepth); 53 | const memory = new WebAssembly.Memory({ initial: needed_pages }); 54 | this.wasm = new WebAssembly.Instance(mod.module, { env: { memory: this.memory }}); 55 | 56 | /* grab a bunch of needed functions and values */ 57 | const { 58 | __heap_base, 59 | tflac_size, 60 | tflac_size_memory, 61 | tflac_size_frame, 62 | tflac_init, 63 | tflac_validate, 64 | tflac_set_blocksize, 65 | tflac_set_channels, 66 | tflac_set_bitdepth, 67 | tflac_set_samplerate, 68 | tflac_encode_streaminfo, 69 | } = this.wasm.exports; 70 | 71 | /* Start sectioning off our memory buffer. 72 | * For each thing we allocate (tflac struct, memory, buffers, etc) 73 | * we'll also create a "pointer" - which is just a byte offset we provide 74 | * to our WASM calls. */ 75 | let offset = __heap_base.value; 76 | const buffer = this.wasm.exports.memory.buffer; 77 | 78 | /* our tflac struct and pointer */ 79 | this.tflac = new Uint8Array(buffer, offset, tflac_size()); 80 | this.tflac_ptr = this.tflac.byteOffset; 81 | 82 | offset += this.tflac.byteLength; 83 | 84 | /* our tflac's residual memory and pointer */ 85 | this.tflac_memory = new Uint8Array(buffer, offset, tflac_size_memory(block_size)); 86 | this.tflac_memory_ptr = this.tflac_memory.byteOffset; 87 | 88 | offset += this.tflac_memory.byteLength; 89 | 90 | /* our output buffer and pointer */ 91 | this.tflac_frame = new Uint8Array(buffer, offset, tflac_size_frame(block_size,channels,bitdepth)); 92 | this.tflac_frame_ptr = this.tflac_frame.byteOffset; 93 | 94 | offset += this.tflac_frame.byteLength; 95 | 96 | /* make sure offset is aligned on an element boundary */ 97 | if(offset % Int32Array.BYTES_PER_ELEMENT != 0) { 98 | offset += Int32Array.BYTES_PER_ELEMENT - (offset % Int32Array.BYTES_PER_ELEMENT); 99 | } 100 | 101 | /* our incoming samples and pointer */ 102 | this.samples = new Int32Array(buffer, offset, block_size * channels); 103 | this.samples_ptr = this.samples.byteOffset; 104 | 105 | offset += this.samples.byteLength; 106 | 107 | /* a single Uint32Array and pointer for getting out bytes-used outvar */ 108 | this.used = new Uint32Array(buffer, offset, 1); 109 | this.used_ptr = this.used.byteOffset; 110 | 111 | /* Let's get it all set up! */ 112 | tflac_init(this.tflac_ptr); 113 | 114 | tflac_set_blocksize(this.tflac_ptr,block_size); 115 | tflac_set_channels(this.tflac_ptr,channels); 116 | tflac_set_bitdepth(this.tflac_ptr,bitdepth); 117 | tflac_set_samplerate(this.tflac_ptr,samplerate); 118 | 119 | /* tflac_validate returns non-zero on error so just throw an error if that happens. */ 120 | if(tflac_validate(this.tflac_ptr,this.tflac_memory_ptr,this.tflac_memory.byteLength)) { 121 | throw new Error("Error validating tflac"); 122 | } 123 | 124 | /* we'll add our "fLaC" stream marker to our first chunk */ 125 | this.chunks.push(new Uint8Array([0x66, 0x4c, 0x61, 0x43])); 126 | 127 | /* we'll also add a placeholder STREAMINFO block */ 128 | tflac_encode_streaminfo(this.tflac_ptr, 1, this.tflac_frame_ptr, this.tflac_frame.byteLength, this.used_ptr); 129 | this.chunks.push(this.tflac_frame.slice(0,this.used[0])); 130 | } 131 | 132 | /* A more prodution-y version of this would have a nicer method for 133 | * accepting audio samples - like allow the caller to provide any 134 | * number of samples, and split them up into blocks for the 135 | * caller. 136 | * 137 | * For this demo we just require the caller to always provide a whole block of 138 | * samples, every block should be the same size except the final block. */ 139 | 140 | encode_s32i(block_size, samples) { 141 | this.samples.set(samples); // copy caller-provided samples to our internal buffer 142 | 143 | // tflac_encode_s32i returns non-zero on error so just throw an error if that happens. 144 | if(this.wasm.exports.tflac_encode_s32i( 145 | this.tflac_ptr, 146 | block_size, 147 | this.samples_ptr, 148 | this.tflac_frame_ptr, 149 | this.tflac_frame.byteLength, 150 | this.used_ptr)) { 151 | throw new Error("Error encoding frame"); 152 | } 153 | this.chunks.push(this.tflac_frame.slice(0,this.used[0])); 154 | } 155 | 156 | /* tflac_finalize that also updates the STREAMINFO block in memory */ 157 | finalize() { 158 | this.wasm.exports.tflac_finalize(this.tflac_ptr); 159 | this.wasm.exports.tflac_encode_streaminfo(this.tflac_ptr, 1, this.tflac_frame_ptr, this.tflac_frame.byteLength, this.used_ptr); 160 | this.chunks[1] = this.tflac_frame.slice(0,this.used[0]); 161 | } 162 | 163 | } 164 | 165 | export { TFlac }; 166 | -------------------------------------------------------------------------------- /misc/gen-crc16-tables/gen-crc16-tables.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | uint16_t crc16_table[8][256]; 5 | 6 | int main(void) { 7 | unsigned int i; 8 | unsigned int j; 9 | uint16_t polynomial = 0x8005; 10 | uint16_t crc = 0; 11 | 12 | for(i=0;i<256;i++) { 13 | crc = i << 8; 14 | for(j=0;j<8;j++) { 15 | crc = (crc << 1) ^ (crc & (1 << 15) ? polynomial : 0); 16 | } 17 | crc16_table[0][i] = crc; 18 | } 19 | 20 | for(i=0;i<256;i++) { 21 | for(j=1;j<8;j++) { 22 | crc16_table[j][i] = crc16_table[0][crc16_table[j-1][i] >> 8] ^ (crc16_table[j-1][i] << 8); 23 | } 24 | } 25 | 26 | printf("uint16_t const crc16_table[8][256] = {\n"); 27 | for(j=0;j<8;j++) { 28 | if(j > 0) printf("\n"); 29 | printf(" {\n "); 30 | for(i=0;i<256;i++) { 31 | if(i > 0) { 32 | if(i % 8 == 0) { 33 | printf("\n "); 34 | } else { 35 | printf(" "); 36 | } 37 | } 38 | printf("0x%04x,",crc16_table[j][i]); 39 | } 40 | printf("\n },"); 41 | } 42 | printf("\n};\n"); 43 | 44 | return 0; 45 | } 46 | -------------------------------------------------------------------------------- /misc/weird-wav-maker/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean 2 | 3 | CFLAGS = -Wall -Wextra -g 4 | 5 | all: weird-wav-maker 6 | 7 | weird-wav-maker: weird-wav-maker.o 8 | $(CC) -o $@ $^ $(LDFLAGS) 9 | 10 | weird-wav-maker.o: weird-wav-maker.c 11 | $(CC) $(CFLAGS) -o $@ -c $< 12 | 13 | clean: 14 | rm -f weird-wav-maker.o weird-wav-maker 15 | -------------------------------------------------------------------------------- /misc/weird-wav-maker/README.md: -------------------------------------------------------------------------------- 1 | # weird-wav-maker 2 | 3 | This is a tool to create WAVE files with weird bitdepths. 4 | 5 | It supports reading from WAVE files with non-floating-point audio, 6 | and will downscale to any bitdepth between 4 and 32 (any bitdepth 7 | supported by FLAC). 8 | 9 | I tried looking into dithering and honestly have no idea if and 10 | how that should be applied. Since this tool is mostly just for 11 | creating WAVE files for testing, that probably doesn't really 12 | matter anyway. The point is to test that sources with strange 13 | bit depths are handled correctly, not to produce good-sounding 14 | 4-bit audio files. 15 | 16 | The code is terrible and pretty slapped together but it works. 17 | 18 | ## Usage 19 | 20 | ``` 21 | ./weird-wav-maker -(bitdepth) source.wav dest.wav 22 | ``` 23 | 24 | Example, to convert a 16-bit WAVE to a 15-bit WAVE: 25 | 26 | ``` 27 | ./weird-wav-maker -15 source.wav dest.wav 28 | ``` 29 | 30 | The default output is a 5-bit WAVE. 31 | 32 | The source file needs to be a higher bitdepth than your destination. 33 | For example, you can't use this tool to convert a 16-bit WAVE to a 17-bit WAVE. 34 | 35 | I recommend creating a source 32-bit WAVE file since that can 36 | be converted to any supported bitdepth with this tool. 37 | -------------------------------------------------------------------------------- /misc/weird-wav-maker/weird-wav-maker.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | static void pack_u16le(uint8_t* d, uint16_t n) { 9 | d[0] = ( n ) & 0xFF; 10 | d[1] = ( n >> 8 ) & 0xFF; 11 | } 12 | 13 | static void pack_u24le(uint8_t* d, uint32_t n) { 14 | d[0] = ( n ) & 0xFF; 15 | d[1] = ( n >> 8 ) & 0xFF; 16 | d[2] = ( n >> 16 ) & 0xFF; 17 | } 18 | 19 | static void pack_u32be(uint8_t* d, uint32_t n) { 20 | d[0] = ( n >> 24 ) & 0xFF; 21 | d[1] = ( n >> 16 ) & 0xFF; 22 | d[2] = ( n >> 8 ) & 0xFF; 23 | d[3] = ( n ) & 0xFF; 24 | } 25 | 26 | static void pack_u32le(uint8_t* d, uint32_t n) { 27 | d[0] = ( n ) & 0xFF; 28 | d[1] = ( n >> 8 ) & 0xFF; 29 | d[2] = ( n >> 16 ) & 0xFF; 30 | d[3] = ( n >> 24 ) & 0xFF; 31 | } 32 | 33 | static void pack_s16le(uint8_t* d, int16_t n) { 34 | pack_u16le(d, (uint16_t) n); 35 | } 36 | 37 | static void pack_s32be(uint8_t* d, int32_t n) { 38 | pack_u32be(d, (uint32_t) n); 39 | } 40 | 41 | static void pack_s24le(uint8_t* d, int32_t n) { 42 | pack_u24le(d, (uint32_t) n); 43 | } 44 | 45 | static void pack_s32le(uint8_t* d, int32_t n) { 46 | pack_u32le(d, (uint32_t) n); 47 | } 48 | 49 | static uint16_t unpack_u16le(const uint8_t* d) { 50 | return (((uint16_t)d[0]) ) | 51 | (((uint16_t)d[1])<< 8); 52 | } 53 | 54 | static uint32_t unpack_u32be(const uint8_t* d) { 55 | return (((uint32_t)d[0])<<24) | 56 | (((uint32_t)d[1])<<16) | 57 | (((uint32_t)d[2])<<8 ) | 58 | (((uint32_t)d[3]) ); 59 | } 60 | 61 | static uint32_t unpack_u24le(const uint8_t* d) { 62 | return (((uint32_t)d[0]) ) | 63 | (((uint32_t)d[1])<< 8) | 64 | (((uint32_t)d[2])<<16); 65 | } 66 | 67 | static int32_t unpack_s24le(const uint8_t* d) { 68 | uint32_t val = unpack_u24le(d); 69 | int32_t r; 70 | struct { 71 | signed int x:24; 72 | } s; 73 | r = s.x = val; 74 | return r; 75 | 76 | } 77 | 78 | static uint32_t unpack_u32le(const uint8_t* d) { 79 | return (((uint32_t)d[0]) ) | 80 | (((uint32_t)d[1])<< 8) | 81 | (((uint32_t)d[2])<<16) | 82 | (((uint32_t)d[3])<<24); 83 | } 84 | 85 | static int32_t unpack_s32be(const uint8_t* d) { 86 | return (int32_t) unpack_u32be(d); 87 | } 88 | 89 | static int32_t unpack_s32le(const uint8_t* d) { 90 | return (int32_t) unpack_u32le(d); 91 | } 92 | 93 | static int16_t unpack_s16le(const uint8_t* d) { 94 | return (int16_t) unpack_u16le(d); 95 | } 96 | 97 | static int read_s16le(FILE* f, int16_t* val) { 98 | uint8_t buffer[2]; 99 | if(fread(buffer,1,2,f) != 2) return 1; 100 | *val = unpack_s16le(buffer); 101 | return 0; 102 | } 103 | 104 | static int read_u16le(FILE* f, uint16_t* val) { 105 | uint8_t buffer[2]; 106 | if(fread(buffer,1,2,f) != 2) return 1; 107 | *val = unpack_u16le(buffer); 108 | return 0; 109 | } 110 | 111 | static int read_s24le(FILE* f, int32_t* val) { 112 | uint8_t buffer[3]; 113 | if(fread(buffer,1,3,f) != 3) return 1; 114 | *val = unpack_s24le(buffer); 115 | return 0; 116 | } 117 | 118 | static int read_s32le(FILE* f, int32_t* val) { 119 | uint8_t buffer[4]; 120 | if(fread(buffer,1,4,f) != 4) return 1; 121 | *val = unpack_s32le(buffer); 122 | return 0; 123 | } 124 | 125 | static int read_u32le(FILE* f, uint32_t* val) { 126 | uint8_t buffer[4]; 127 | if(fread(buffer,1,4,f) != 4) return 1; 128 | *val = unpack_u32le(buffer); 129 | return 0; 130 | } 131 | 132 | static int read_s32be(FILE* f, int32_t* val) { 133 | uint8_t buffer[4]; 134 | if(fread(buffer,1,4,f) != 4) return 1; 135 | *val = unpack_s32be(buffer); 136 | return 0; 137 | } 138 | 139 | static int read_u32be(FILE* f, uint32_t* val) { 140 | uint8_t buffer[4]; 141 | if(fread(buffer,1,4,f) != 4) return 1; 142 | *val = unpack_u32be(buffer); 143 | return 0; 144 | } 145 | 146 | static int write_u16le(FILE* f, uint16_t val) { 147 | uint8_t buffer[2]; 148 | pack_u16le(buffer, val); 149 | if(fwrite(buffer,1,2,f) != 2) return 1; 150 | return 0; 151 | } 152 | 153 | static int write_s16le(FILE* f, int16_t val) { 154 | return write_u16le(f, (uint16_t) val); 155 | } 156 | 157 | static int write_u32be(FILE* f, uint32_t val) { 158 | uint8_t buffer[4]; 159 | pack_u32be(buffer, val); 160 | if(fwrite(buffer,1,4,f) != 4) return 1; 161 | return 0; 162 | } 163 | 164 | static int write_u24le(FILE* f, uint32_t val) { 165 | uint8_t buffer[3]; 166 | pack_u24le(buffer, val); 167 | if(fwrite(buffer,1,3,f) != 3) return 1; 168 | return 0; 169 | } 170 | 171 | static int write_u32le(FILE* f, uint32_t val) { 172 | uint8_t buffer[4]; 173 | pack_u32le(buffer, val); 174 | if(fwrite(buffer,1,4,f) != 4) return 1; 175 | return 0; 176 | } 177 | 178 | static int write_s32be(FILE* f, int32_t val) { 179 | return write_u32be(f, (uint32_t) val); 180 | } 181 | 182 | static int write_s24le(FILE* f, int32_t val) { 183 | return write_u24le(f, (uint32_t) val); 184 | } 185 | 186 | static int write_s32le(FILE* f, int32_t val) { 187 | return write_u32le(f, (uint32_t) val); 188 | } 189 | 190 | static int readsample_32(FILE* f, int32_t* val) { 191 | return read_s32le(f, val); 192 | } 193 | 194 | static int readsample_24(FILE* f, int32_t* val) { 195 | return read_s24le(f, val); 196 | } 197 | 198 | static int readsample_16(FILE* f, int32_t* val) { 199 | int16_t tmp; 200 | int r; 201 | 202 | if( (r = read_s16le(f, &tmp)) != 0) return r; 203 | *val = (int32_t)tmp; 204 | return 0; 205 | } 206 | 207 | static int readsample_8(FILE* f, int32_t* val) { 208 | uint8_t v; 209 | int32_t t; 210 | if( fread(&v,1,1,f) != 1) return 1; 211 | t = v; 212 | t -= 128; 213 | *val = t; 214 | return 0; 215 | } 216 | 217 | static int writesample_8(FILE* f, int32_t val) { 218 | uint8_t b; 219 | val += 128; 220 | b = (uint8_t)val; 221 | if( fwrite(&b,1,1,f) != 1) return 1; 222 | return 0; 223 | } 224 | 225 | static int writesample_16(FILE* f, int32_t val) { 226 | return write_s16le(f, (int16_t)val); 227 | } 228 | 229 | static int writesample_24(FILE* f, int32_t val) { 230 | return write_s24le(f, val); 231 | } 232 | 233 | static int writesample_32(FILE* f, int32_t val) { 234 | return write_s32le(f, val); 235 | } 236 | 237 | #define CHUNK_ID_RIFF 0x52494646 238 | #define CHUNK_ID_WAVE 0x57415645 239 | #define CHUNK_ID_FMT 0x666d7420 240 | #define CHUNK_ID_DATA 0x64617461 241 | 242 | #define FORMAT_TAG_PCM 0x0001 243 | #define FORMAT_TAG_EXTENSIBLE 0xfffe 244 | 245 | #define TRY(x) if( (x) != 0 ) goto complete 246 | 247 | int main(int argc, const char *argv[]) { 248 | int r = 1; 249 | int i = 0; 250 | uint8_t buffer[4] = { 0, 0, 0, 0 }; 251 | int16_t temp_s16; 252 | uint16_t temp_u16; 253 | int32_t temp_s32; 254 | int32_t isample; 255 | int32_t osample; 256 | uint32_t temp_u32; 257 | uint16_t formattag = 0; 258 | uint16_t channels = 0; 259 | uint32_t samplerate = 0; 260 | uint16_t bitdepth = 0; 261 | uint16_t wastedbits = 0; 262 | uint16_t downshift = 0; 263 | uint16_t upshift = 0; 264 | uint32_t channelmask = 0; 265 | uint16_t blockalign = 0; 266 | uint32_t chunk_id = 0; 267 | uint32_t chunk_len = 0; 268 | uint32_t guid0 = 0; 269 | uint32_t guid1 = 0; 270 | uint32_t guid2 = 0; 271 | uint32_t guid3 = 0; 272 | uint16_t outdepth = 5; 273 | uint32_t bytespersample = 0; 274 | FILE* input = NULL; 275 | FILE* output = NULL; 276 | int (*readsample)(FILE *, int32_t* val) = NULL; 277 | int (*writesample)(FILE *, int32_t val) = NULL; 278 | const char *progname = NULL; 279 | const char *inname = NULL; 280 | const char *outname = NULL; 281 | 282 | progname = *argv++; 283 | argc--; 284 | 285 | while(argc--) { 286 | /* this is a very stupid way to do this */ 287 | if(strcmp(*argv,"-4") == 0) { 288 | outdepth = 4; 289 | } else if(strcmp(*argv,"-5") == 0) { 290 | outdepth = 5; 291 | } else if(strcmp(*argv,"-6") == 0) { 292 | outdepth = 6; 293 | } else if(strcmp(*argv,"-7") == 0) { 294 | outdepth = 7; 295 | } else if(strcmp(*argv,"-8") == 0) { 296 | outdepth = 8; 297 | } else if(strcmp(*argv,"-9") == 0) { 298 | outdepth = 9; 299 | } else if(strcmp(*argv,"-10") == 0) { 300 | outdepth = 10; 301 | } else if(strcmp(*argv,"-11") == 0) { 302 | outdepth = 11; 303 | } else if(strcmp(*argv,"-12") == 0) { 304 | outdepth = 12; 305 | } else if(strcmp(*argv,"-13") == 0) { 306 | outdepth = 13; 307 | } else if(strcmp(*argv,"-14") == 0) { 308 | outdepth = 14; 309 | } else if(strcmp(*argv,"-15") == 0) { 310 | outdepth = 15; 311 | } else if(strcmp(*argv,"-16") == 0) { 312 | outdepth = 16; 313 | } else if(strcmp(*argv,"-17") == 0) { 314 | outdepth = 17; 315 | } else if(strcmp(*argv,"-18") == 0) { 316 | outdepth = 18; 317 | } else if(strcmp(*argv,"-19") == 0) { 318 | outdepth = 19; 319 | } else if(strcmp(*argv,"-20") == 0) { 320 | outdepth = 20; 321 | } else if(strcmp(*argv,"-21") == 0) { 322 | outdepth = 21; 323 | } else if(strcmp(*argv,"-22") == 0) { 324 | outdepth = 22; 325 | } else if(strcmp(*argv,"-23") == 0) { 326 | outdepth = 23; 327 | } else if(strcmp(*argv,"-24") == 0) { 328 | outdepth = 24; 329 | } else if(strcmp(*argv,"-25") == 0) { 330 | outdepth = 25; 331 | } else if(strcmp(*argv,"-26") == 0) { 332 | outdepth = 26; 333 | } else if(strcmp(*argv,"-27") == 0) { 334 | outdepth = 27; 335 | } else if(strcmp(*argv,"-28") == 0) { 336 | outdepth = 28; 337 | } else if(strcmp(*argv,"-29") == 0) { 338 | outdepth = 29; 339 | } else if(strcmp(*argv,"-30") == 0) { 340 | outdepth = 30; 341 | } else if(strcmp(*argv,"-31") == 0) { 342 | outdepth = 31; 343 | } else if(strcmp(*argv,"-32") == 0) { 344 | outdepth = 32; 345 | } else if(argv[0][0] == '-' && isdigit(argv[0][1]) ) { 346 | printf("Unsupported bit depth option %s, valid options are -4 through -15\n", 347 | *argv); 348 | goto complete; 349 | } 350 | else if(inname == NULL) { 351 | inname = *argv; 352 | } else if(outname == NULL) { 353 | outname = *argv; 354 | } 355 | argv++; 356 | } 357 | 358 | if(inname == NULL || outname == NULL) { 359 | printf("Usage: %s /path/to/input /path/to/output\n", progname); 360 | goto complete; 361 | } 362 | 363 | input = fopen(inname,"rb"); 364 | if(input == NULL) { 365 | printf("Unable to open %s for reading\n", inname); 366 | goto complete; 367 | } 368 | output = fopen(outname,"wb"); 369 | if(output == NULL) { 370 | printf("Unable to open %s for writing\n", outname); 371 | goto complete; 372 | } 373 | 374 | TRY( read_u32be(input, &chunk_id) ); 375 | if(chunk_id != CHUNK_ID_RIFF) { 376 | printf("This tool only works with RIFF files\n"); 377 | goto complete; 378 | } 379 | 380 | /* skip file chunk size */ 381 | TRY( read_u32be(input, &temp_u32) ); 382 | 383 | /* check the format is WAVE */ 384 | TRY( read_u32be(input, &chunk_id) ); 385 | if(chunk_id != CHUNK_ID_WAVE) { 386 | printf("This tool only works with WAVE files\n"); 387 | goto complete; 388 | } 389 | 390 | /* find the fmt chunk */ 391 | TRY( read_u32be(input, &chunk_id) ); 392 | while(chunk_id != CHUNK_ID_FMT) { 393 | TRY( read_u32le(input, &chunk_len) ); 394 | TRY( fseek(input,chunk_len,SEEK_CUR) ); 395 | 396 | TRY( read_u32be(input, &chunk_id) ); 397 | } 398 | TRY( read_u32le(input, &chunk_len) ); 399 | 400 | TRY( read_u16le(input, &formattag) ); 401 | if( ! (formattag == FORMAT_TAG_PCM || formattag == FORMAT_TAG_EXTENSIBLE) ) { 402 | printf("This tool only works with PCM and EXTENSIBLE WAVE files, formattag=0x%04x\n",formattag); 403 | goto complete; 404 | } 405 | 406 | TRY( read_u16le(input, &channels) ); 407 | TRY( read_u32le(input, &samplerate) ); 408 | TRY( read_u32le(input, &temp_u32) ); /* average bytes per second, ignore */ 409 | TRY( read_u16le(input, &blockalign) ); 410 | TRY( read_u16le(input, &bitdepth) ); 411 | if(bitdepth % 8 != 0) { 412 | printf("weird bitdepth encountered!\n"); 413 | goto complete; 414 | } 415 | if(formattag == FORMAT_TAG_EXTENSIBLE) { 416 | TRY( read_u16le(input, &temp_u16) ); 417 | if(temp_u16 != 22) { 418 | printf("Unknown cbSize found - %u, expected 22\n", temp_u16); 419 | goto complete; 420 | } 421 | TRY( read_u16le(input, &temp_u16) ); 422 | wastedbits = bitdepth - temp_u16; 423 | 424 | TRY( read_u32le(input, &channelmask) ); 425 | 426 | TRY( read_u32le(input, &guid0) ); 427 | TRY( read_u32le(input, &guid1) ); 428 | TRY( read_u32le(input, &guid2) ); 429 | TRY( read_u32le(input, &guid3) ); 430 | 431 | if(guid0 != 0x00000001 || 432 | guid1 != 0x00100000 || 433 | guid2 != 0xaa000080 || 434 | guid3 != 0x719b3800) { 435 | printf("Unknown subformat GUID found\n"); 436 | goto complete; 437 | } 438 | } else { 439 | switch(channels) { 440 | case 1: channelmask = 0x04; break; 441 | case 2: channelmask = 0x03; break; 442 | default: { 443 | printf("For non-extensible waves this tool only handles 1 or 2 channels\n"); 444 | goto complete; 445 | } 446 | } 447 | } 448 | 449 | if(bitdepth - wastedbits < outdepth) { 450 | printf("Error converting from %u-bit to %u-bit," 451 | " source bit-depth must be greater than dest bit-depth!", bitdepth - wastedbits, outdepth); 452 | goto complete; 453 | } 454 | 455 | /* find the data chunk */ 456 | TRY( read_u32be(input, &chunk_id) ); 457 | while(chunk_id != CHUNK_ID_DATA) { 458 | TRY( read_u32le(input, &chunk_len) ); 459 | TRY( fseek(input,chunk_len,SEEK_CUR) ); 460 | 461 | TRY( read_u32be(input, &chunk_id) ); 462 | } 463 | TRY( read_u32le(input, &chunk_len) ); 464 | 465 | switch(bitdepth) { 466 | case 8: readsample = readsample_8; downshift = 8 - wastedbits - outdepth; break; 467 | case 16: readsample = readsample_16; downshift = 16 - wastedbits - outdepth; break; 468 | case 24: readsample = readsample_24; downshift = 24 - wastedbits - outdepth; break; 469 | case 32: readsample = readsample_32; downshift = 32 - wastedbits - outdepth; break; 470 | default: { 471 | printf("unsupported bitdepth: %u\n",bitdepth); 472 | goto complete; 473 | } 474 | } 475 | 476 | bytespersample = (uint32_t)((outdepth + 7) & 0xFFF8) / 8; 477 | 478 | switch(bytespersample) { 479 | case 1: writesample = writesample_8; upshift = 8 - outdepth; break; 480 | case 2: writesample = writesample_16; upshift = 16 - outdepth; break; 481 | case 3: writesample = writesample_24; upshift = 24 - outdepth; break; 482 | case 4: writesample = writesample_32; upshift = 32 - outdepth; break; 483 | default: { 484 | printf("this shouldn't happen\n"); 485 | goto complete; 486 | } 487 | } 488 | 489 | printf("Source info:\n"); 490 | printf(" bit depth: %u\n", bitdepth); 491 | printf("Destination info:\n"); 492 | printf(" bit depth: %u\n", outdepth); 493 | printf(" bytes per sample: %u\n", bytespersample); 494 | printf("Downshift: %u\n", downshift); 495 | printf("Upshift: %u\n", upshift); 496 | 497 | /* write out a WAVE header */ 498 | TRY( write_u32be(output, CHUNK_ID_RIFF) ); 499 | TRY( write_u32le(output, 0 ) ); /* file length to fill in later */ 500 | TRY( write_u32be(output, CHUNK_ID_WAVE) ); 501 | TRY( write_u32be(output, CHUNK_ID_FMT) ); 502 | TRY( write_u32le(output, 40) ); 503 | TRY( write_u16le(output, FORMAT_TAG_EXTENSIBLE) ); 504 | TRY( write_u16le(output, channels) ); 505 | TRY( write_u32le(output, samplerate) ); 506 | TRY( write_u32le(output, samplerate * ((uint32_t)channels) * bytespersample ) ); /* bytes per second */ 507 | TRY( write_u16le(output, channels * bytespersample ) ); /* block alignment */ 508 | TRY( write_u16le(output, bytespersample * 8) ); 509 | TRY( write_u16le(output, 22) ); 510 | TRY( write_u16le(output, outdepth) ); 511 | TRY( write_u32le(output, channelmask) ); 512 | TRY( write_u32le(output, 0x00000001) ); 513 | TRY( write_u32le(output, 0x00100000) ); 514 | TRY( write_u32le(output, 0xaa000080) ); 515 | TRY( write_u32le(output, 0x719b3800) ); 516 | 517 | TRY( write_u32be(output, CHUNK_ID_DATA) ); 518 | TRY( write_u32le(output, 0 ) ); /* chunk length to fill in later */ 519 | 520 | chunk_len /= (bitdepth/8); /* convert to number of samples */ 521 | while(chunk_len--) { 522 | TRY( readsample(input, &isample) ); 523 | osample = isample; 524 | osample /= (1 << wastedbits); 525 | osample /= (1 << downshift); 526 | osample *= (1 << upshift); 527 | if(wastedbits == 0 && 528 | downshift == 0 && 529 | upshift == 0) { 530 | assert(isample == osample); 531 | } 532 | 533 | TRY( writesample(output, osample) ); 534 | } 535 | 536 | temp_u32 = ftell(output); 537 | fseek(output, 4, SEEK_SET); 538 | TRY( write_u32le(output, temp_u32 - 8 ) ); /* file length */ 539 | fseek(output, 64, SEEK_SET); 540 | TRY( write_u32le(output, temp_u32 - 68 ) ); /* file length */ 541 | 542 | r = 0; 543 | 544 | complete: 545 | if(input != NULL) fclose(input); 546 | if(output != NULL) fclose(output); 547 | 548 | return r; 549 | } 550 | -------------------------------------------------------------------------------- /tests/residuals/Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all clean test time 2 | 3 | CFLAGS = -I../.. -Wall -Wextra -g -O2 4 | 5 | all: test-64bit test-32bit time-64bit time-32bit 6 | 7 | test: test-64bit test-32bit 8 | echo "Native 64 bit integers" 9 | ./test-64bit 10 | echo "Emulated 64 bit integers" 11 | ./test-32bit 12 | 13 | time: time-64bit time-32bit 14 | echo "Native 64 bit integers" 15 | ./time-64bit 16 | echo "Emulated 64 bit integers" 17 | ./time-32bit 18 | 19 | test-64bit: test.c ../../tflac.h 20 | $(CC) $(CFLAGS) -o $@ $^ 21 | 22 | test-32bit: test.c ../../tflac.h 23 | $(CC) $(CFLAGS) -DTFLAC_32BIT_ONLY -o $@ $^ 24 | 25 | time-64bit: time.c ../../tflac.h 26 | $(CC) $(CFLAGS) -o $@ $^ 27 | 28 | time-32bit: time.c ../../tflac.h 29 | $(CC) $(CFLAGS) -DTFLAC_32BIT_ONLY -o $@ $^ 30 | 31 | clean: 32 | rm -f test-64bit test-32bit 33 | rm -f test-64bit.exe test-32bit.exe 34 | rm -f time-64bit time-32bit 35 | rm -f time-64bit.exe time-32bit.exe 36 | -------------------------------------------------------------------------------- /tests/residuals/test.c: -------------------------------------------------------------------------------- 1 | #define TFLAC_IMPLEMENTATION 2 | #include "tflac.h" 3 | 4 | #include 5 | #include 6 | 7 | /* makes sure the residual-calculating functions get expected results */ 8 | 9 | #define BLOCKSIZE 16 10 | 11 | 12 | static tflac_s32* samples_unaligned = NULL; 13 | static tflac_s32* residuals_unaligned = NULL; 14 | 15 | static tflac_s32* samples = NULL; 16 | static tflac_s32* residuals = NULL; 17 | static tflac_u64 result; 18 | 19 | #ifdef TFLAC_32BIT_ONLY 20 | static void print_result(void) { 21 | printf("%u",result.lo); 22 | } 23 | #else 24 | static void print_result(void) { 25 | printf("%lu",result); 26 | } 27 | #endif 28 | 29 | static const tflac_s32 residuals_order1[] = { 30 | 11056, 31 | 8986, 32 | -12937, 33 | -16518, 34 | -17099, 35 | 9990, 36 | 5727, 37 | 14423, 38 | -30911, 39 | 37530, 40 | -28880, 41 | 20186, 42 | 10334, 43 | -26912, 44 | -368, 45 | 24809, 46 | }; 47 | 48 | static const tflac_s32 residuals_order2[] = { 49 | 11056, 50 | 20042, 51 | -21923, 52 | -3581, 53 | -581, 54 | 27089, 55 | -4263, 56 | 8696, 57 | -45334, 58 | 68441, 59 | -66410, 60 | 49066, 61 | -9852, 62 | -37246, 63 | 26544, 64 | 25177, 65 | }; 66 | 67 | static const tflac_s32 residuals_order3[] = { 68 | 11056, 69 | 20042, 70 | 7105, 71 | 18342, 72 | 3000, 73 | 27670, 74 | -31352, 75 | 12959, 76 | -54030, 77 | 113775, 78 | -134851, 79 | 115476, 80 | -58918, 81 | -27394, 82 | 63790, 83 | -1367, 84 | }; 85 | 86 | static const tflac_s32 residuals_order4[] = { 87 | 11056, 88 | 20042, 89 | 7105, 90 | -9413, 91 | -15342, 92 | 24670, 93 | -59022, 94 | 44311, 95 | -66989, 96 | 167805, 97 | -248626, 98 | 250327, 99 | -174394, 100 | 31524, 101 | 91184, 102 | -65157, 103 | }; 104 | 105 | static const char * const passfail[] = { 106 | "pass", 107 | "fail", 108 | }; 109 | 110 | static void* align_ptr(void* ptr) { 111 | tflac_u8 *d; 112 | tflac_uptr p1, p2; 113 | 114 | p1 = (tflac_uptr)ptr; 115 | p2 = (p1 + 15) & ~(tflac_uptr)0x0F; 116 | d = (tflac_u8*)ptr; 117 | 118 | return &d[p2 - p1]; 119 | } 120 | 121 | static void test_reset(void) { 122 | tflac_u32 i = 0; 123 | for(i=0;i 5 | #include 6 | #include 7 | #include 8 | 9 | /* times various residual-calculating methods */ 10 | 11 | /* max blocksize */ 12 | #define BLOCKSIZE 65535 13 | #define TESTRUNS 1000 14 | 15 | #if defined(_WIN32) || defined(_WIN64) 16 | #define USE_QPC 17 | #include 18 | #else 19 | #define CLOCK_ID CLOCK_REALTIME 20 | #endif 21 | 22 | 23 | static tflac_s32* samples_unaligned = NULL; 24 | static tflac_s32* residuals_unaligned = NULL; 25 | 26 | static tflac_s32* samples = NULL; 27 | static tflac_s32* residuals = NULL; 28 | #ifdef USE_QPC 29 | static LARGE_INTEGER frequency; 30 | #endif 31 | static tflac_u64 result; 32 | 33 | static void test_reset(void) { 34 | tflac_u32 i = 0; 35 | for(i=0;i 50 ? -1 : 1)); 37 | } 38 | } 39 | 40 | static void* align_ptr(void* ptr) { 41 | tflac_u8 *d; 42 | tflac_uptr p1, p2; 43 | 44 | p1 = (tflac_uptr)ptr; 45 | p2 = (p1 + 15) & ~(tflac_uptr)0x0F; 46 | d = (tflac_u8*)ptr; 47 | 48 | return &d[p2 - p1]; 49 | } 50 | 51 | #ifndef USE_QPC 52 | static struct timespec timespec_diff(struct timespec start, struct timespec end) { 53 | struct timespec temp; 54 | if ((end.tv_nsec-start.tv_nsec)<0) { 55 | temp.tv_sec = end.tv_sec-start.tv_sec-1; 56 | temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; 57 | } else { 58 | temp.tv_sec = end.tv_sec-start.tv_sec; 59 | temp.tv_nsec = end.tv_nsec-start.tv_nsec; 60 | } 61 | return temp; 62 | } 63 | #endif 64 | 65 | static tflac_u32 time_cfr(void(*cfr)(tflac_u32, const tflac_s32* TFLAC_RESTRICT, tflac_s32* TFLAC_RESTRICT, tflac_u64* TFLAC_RESTRICT)) { 66 | tflac_u32 i = 0; 67 | #ifdef USE_QPC 68 | LARGE_INTEGER time_start; 69 | LARGE_INTEGER time_end; 70 | LARGE_INTEGER time_total; 71 | #else 72 | struct timespec time_start; 73 | struct timespec time_end; 74 | struct timespec time_total; 75 | struct timespec time_diff; 76 | #endif 77 | tflac_u32 time_avg; 78 | 79 | #ifdef USE_QPC 80 | time_total.QuadPart = 0; 81 | #else 82 | time_total.tv_sec = 0; 83 | time_total.tv_nsec = 0; 84 | #endif 85 | 86 | for(i=0;i 1000000000) { 103 | time_total.tv_nsec -= 1000000000; 104 | time_total.tv_sec++; 105 | } 106 | #endif 107 | } 108 | 109 | /* in both cases we convert the time to microseconds */ 110 | #ifdef USE_QPC 111 | time_total.QuadPart *= 1000000; 112 | time_total.QuadPart /= TESTRUNS; 113 | time_total.QuadPart /= frequency.QuadPart; 114 | time_avg = (tflac_u32)time_total.QuadPart; 115 | #else 116 | time_avg = (tflac_u32)(time_total.tv_sec * 1000000); /* scale s up to us */ 117 | time_avg += (tflac_u32)time_total.tv_nsec / 1000; /* scale ns down to us */ 118 | time_avg /= TESTRUNS; 119 | #endif 120 | 121 | return time_avg; 122 | 123 | } 124 | 125 | 126 | int main(void) { 127 | int r = 0; 128 | tflac_u32 std_times[5]; 129 | tflac_u32 wide_times[5]; 130 | #ifdef TFLAC_ENABLE_SSE2 131 | tflac_u32 sse2_times[5]; 132 | #endif 133 | #ifdef TFLAC_ENABLE_SSSE3 134 | tflac_u32 ssse3_times[5]; 135 | #endif 136 | #ifdef TFLAC_ENABLE_SSE4_1 137 | tflac_u32 sse4_1_times[5]; 138 | #endif 139 | samples_unaligned = (tflac_s32*)malloc(15 + (sizeof(tflac_s32) * BLOCKSIZE)); 140 | if(samples_unaligned == NULL) abort(); 141 | residuals_unaligned = (tflac_s32*)malloc(15 + (sizeof(tflac_s32) * BLOCKSIZE)); 142 | if(residuals_unaligned == NULL) abort(); 143 | 144 | samples = align_ptr(samples_unaligned); 145 | residuals = align_ptr(residuals_unaligned); 146 | 147 | #ifdef USE_QPC 148 | QueryPerformanceFrequency(&frequency); 149 | #endif 150 | 151 | printf(".______________________________________________________________________________.\n"); 152 | printf("|%6s|%13s|%13s|%13s|%14s|%14s|\n","","order0","order1","order2","order3","order4"); 153 | printf("|------|-------------|-------------|-------------|--------------|--------------|\n"); 154 | 155 | std_times[0] = time_cfr(tflac_cfr_order0_std); 156 | std_times[1] = time_cfr(tflac_cfr_order1_std); 157 | std_times[2] = time_cfr(tflac_cfr_order2_std); 158 | std_times[3] = time_cfr(tflac_cfr_order3_std); 159 | std_times[4] = time_cfr(tflac_cfr_order4_std); 160 | 161 | printf("|%6s|%13u|%13u|%13u|%14u|%14u|\n", 162 | "std", std_times[0], std_times[1], std_times[2], std_times[3], std_times[4]); 163 | 164 | wide_times[0] = std_times[0]; 165 | wide_times[1] = time_cfr(tflac_cfr_order1_wide_std); 166 | wide_times[2] = time_cfr(tflac_cfr_order2_wide_std); 167 | wide_times[3] = time_cfr(tflac_cfr_order3_wide_std); 168 | wide_times[4] = time_cfr(tflac_cfr_order4_wide_std); 169 | 170 | printf("|%6s|%13u|%13u|%13u|%14u|%14u|\n", 171 | "wstd", wide_times[0], wide_times[1], wide_times[2], wide_times[3], wide_times[4]); 172 | 173 | #ifdef TFLAC_ENABLE_SSE2 174 | 175 | sse2_times[0] = time_cfr(tflac_cfr_order0_sse2); 176 | sse2_times[1] = time_cfr(tflac_cfr_order1_sse2); 177 | sse2_times[2] = time_cfr(tflac_cfr_order2_sse2); 178 | sse2_times[3] = time_cfr(tflac_cfr_order3_sse2); 179 | sse2_times[4] = time_cfr(tflac_cfr_order4_sse2); 180 | 181 | printf("|%6s|%13u|%13u|%13u|%14u|%14u|\n", 182 | "sse2", sse2_times[0], sse2_times[1], sse2_times[2], sse2_times[3], sse2_times[4]); 183 | 184 | #endif 185 | 186 | #ifdef TFLAC_ENABLE_SSSE3 187 | 188 | ssse3_times[0] = time_cfr(tflac_cfr_order0_ssse3); 189 | ssse3_times[1] = time_cfr(tflac_cfr_order1_ssse3); 190 | ssse3_times[2] = time_cfr(tflac_cfr_order2_ssse3); 191 | ssse3_times[3] = time_cfr(tflac_cfr_order3_ssse3); 192 | ssse3_times[4] = time_cfr(tflac_cfr_order4_ssse3); 193 | printf("|%6s|%13u|%13u|%13u|%14u|%14u|\n", 194 | "ssse3", ssse3_times[0], ssse3_times[1], ssse3_times[2], ssse3_times[3], ssse3_times[4]); 195 | 196 | #endif 197 | 198 | #ifdef TFLAC_ENABLE_SSE4_1 199 | 200 | sse4_1_times[0] = time_cfr(tflac_cfr_order0_sse4_1); 201 | sse4_1_times[1] = time_cfr(tflac_cfr_order1_sse4_1); 202 | sse4_1_times[2] = time_cfr(tflac_cfr_order2_sse4_1); 203 | sse4_1_times[3] = time_cfr(tflac_cfr_order3_sse4_1); 204 | sse4_1_times[4] = time_cfr(tflac_cfr_order4_sse4_1); 205 | printf("|%6s|%13u|%13u|%13u|%14u|%14u|\n", 206 | "sse4_1", sse4_1_times[0], sse4_1_times[1], sse4_1_times[2], sse4_1_times[3], sse4_1_times[4]); 207 | 208 | #endif 209 | 210 | printf("|______________________________________________________________________________|\n"); 211 | 212 | 213 | free(samples_unaligned); 214 | free(residuals_unaligned); 215 | return r; 216 | } 217 | 218 | --------------------------------------------------------------------------------