├── .gitignore ├── fourier.h ├── io.h ├── custom_math.h ├── wav.h ├── io.c ├── custom_math.c ├── LICENSE.txt ├── Makefile ├── test.c ├── fourier.c ├── compressor.h ├── wav.c ├── README.md ├── compressor.c └── fmt.c /.gitignore: -------------------------------------------------------------------------------- 1 | /fmt 2 | /*.o 3 | /tests 4 | -------------------------------------------------------------------------------- /fourier.h: -------------------------------------------------------------------------------- 1 | #ifndef FOURIER_H_DEFINED 2 | #define FOURIER_H_DEFINED 3 | 4 | #include 5 | #include "custom_math.h" 6 | 7 | void generic_slow_discrete_fourier_transform(struct complex_number * in, struct complex_number * out, unsigned int series_length, double scale, double direction); 8 | void fast_discrete_fourier_transform(struct complex_number * in, struct complex_number * out, unsigned int series_length, double scale, double direction); 9 | 10 | #endif 11 | -------------------------------------------------------------------------------- /io.h: -------------------------------------------------------------------------------- 1 | #ifndef MEME_IO_H 2 | #define MEME_IO_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include 8 | 9 | struct print_buffer{ 10 | /* Bytes allocated in buffer. */ 11 | unsigned int buffer_size; 12 | /* Current position where data can be written to. */ 13 | unsigned int pos; 14 | char * characters; 15 | }; 16 | 17 | void init_print_buffer(struct print_buffer * b); 18 | void printfbuff(struct print_buffer * b, const char * fmt, ...); 19 | 20 | #endif 21 | -------------------------------------------------------------------------------- /custom_math.h: -------------------------------------------------------------------------------- 1 | #ifndef COMPLEX_H_DEFINED 2 | #define COMPLEX_H_DEFINED 3 | 4 | #include 5 | #include 6 | 7 | #define PI 3.141592653589 8 | #define NUMBER_E 2.71828 9 | 10 | struct complex_number{ 11 | double r; 12 | double i; 13 | }; 14 | 15 | struct complex_number e_i_x(double); 16 | struct complex_number make_complex_number(double, double); 17 | struct complex_number complex_add(struct complex_number, struct complex_number); 18 | struct complex_number complex_multiply(struct complex_number, struct complex_number); 19 | double magnitude(struct complex_number); 20 | int is_power_of_two(uint32_t); 21 | 22 | #endif 23 | -------------------------------------------------------------------------------- /wav.h: -------------------------------------------------------------------------------- 1 | #ifndef WAVE_H_DEFINED 2 | #define WAVE_H_DEFINED 3 | 4 | #include 5 | #include 6 | #include 7 | #include "custom_math.h" 8 | 9 | struct wav_file_header{ 10 | char RIFF[4]; 11 | uint32_t file_size_bytes; 12 | char WAVE[4]; 13 | char fmt[4]; 14 | uint32_t fmt_length; 15 | uint16_t fmt_type; 16 | uint16_t num_channels; 17 | uint32_t sample_rate; 18 | uint32_t sample_rate_bps_channels_div8; 19 | uint16_t bps_channels; 20 | uint16_t bits_per_sample; 21 | }; 22 | 23 | struct run_params; 24 | 25 | void print_wav_header(struct wav_file_header *); 26 | char * find_wav_data(char * p, char * end, uint32_t); 27 | int load_wav_period(struct complex_number *, char *, uint32_t, uint32_t, uint32_t, uint32_t, struct wav_file_header *); 28 | 29 | #endif 30 | -------------------------------------------------------------------------------- /io.c: -------------------------------------------------------------------------------- 1 | 2 | #include "io.h" 3 | 4 | void init_print_buffer(struct print_buffer * b){ 5 | b->buffer_size = 1; 6 | b->pos = 0; 7 | b->characters = (char *)malloc(1); 8 | b->characters[0] = '\0'; 9 | } 10 | 11 | unsigned int get_count_snprintf(const char * fmt, va_list v){ 12 | /* Figure out how big it is first. */ 13 | return vsnprintf((char *)0, 0, fmt, v); 14 | } 15 | 16 | void printfbuff(struct print_buffer * b, const char * fmt, ...){ 17 | va_list t1; 18 | va_list t2; 19 | va_start(t1, fmt); 20 | va_start(t2, fmt); 21 | 22 | /* Make sure we have room. */ 23 | unsigned int size = get_count_snprintf(fmt, t1); 24 | b->buffer_size += size; 25 | b->characters = realloc(b->characters, b->buffer_size); 26 | 27 | /* Write the actual data. */ 28 | vsnprintf(&b->characters[b->pos], size + 1, fmt, t2); 29 | b->pos += size; 30 | 31 | va_end(t1); 32 | va_end(t2); 33 | } 34 | -------------------------------------------------------------------------------- /custom_math.c: -------------------------------------------------------------------------------- 1 | #include "custom_math.h" 2 | 3 | struct complex_number e_i_x(double d){ 4 | struct complex_number c; 5 | c.r = cos(d); 6 | c.i = sin(d); 7 | return c; 8 | } 9 | 10 | struct complex_number make_complex_number(double r, double i){ 11 | struct complex_number c; 12 | c.r = r; 13 | c.i = i; 14 | return c; 15 | } 16 | 17 | struct complex_number complex_add(struct complex_number a, struct complex_number b){ 18 | struct complex_number c; 19 | c.r = a.r + b.r; 20 | c.i = a.i + b.i; 21 | return c; 22 | } 23 | 24 | struct complex_number complex_multiply(struct complex_number a, struct complex_number b){ 25 | struct complex_number c; 26 | c.r = (a.r * b.r) - (a.i * b.i); 27 | c.i = (a.r * b.i) + (a.i * b.r); 28 | return c; 29 | } 30 | 31 | double magnitude(struct complex_number c) { 32 | return sqrt(pow(c.r,2) + pow(c.i,2)); 33 | } 34 | 35 | int is_power_of_two(uint32_t i){ 36 | while(!(i & 0x1) && i != 0){ 37 | i >>= 1; 38 | } 39 | if(i == 1){ 40 | return 1; 41 | }else{ 42 | return 0; 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2018 Robert Elder Software Inc. 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | CUSTOM_FLAGS=-g -Wall -std=c99 -pedantic -Werror -Wno-overlength-strings 2 | CUSTOM_COMPILER=clang 3 | 4 | fmt: fmt.o custom_math.o fourier.o wav.o compressor.o io.o 5 | @$(CUSTOM_COMPILER) compressor.o io.o fmt.o custom_math.o fourier.o wav.o -o fmt -lm $(CUSTOM_FLAGS) 6 | 7 | io.o: io.c 8 | @$(CUSTOM_COMPILER) -c $< -o $@ $(CUSTOM_FLAGS) 9 | 10 | compressor.o: compressor.c 11 | @$(CUSTOM_COMPILER) -c $< -o $@ $(CUSTOM_FLAGS) 12 | 13 | test: tests 14 | @./tests 15 | 16 | tests: test.o custom_math.o fourier.o wav.o compressor.o io.o 17 | @$(CUSTOM_COMPILER) compressor.o io.o test.o custom_math.o fourier.o wav.o -o tests -lm $(CUSTOM_FLAGS) 18 | 19 | fmt.o: fmt.c 20 | @$(CUSTOM_COMPILER) -c $< -o $@ $(CUSTOM_FLAGS) 21 | 22 | fourier.o: fourier.c 23 | @$(CUSTOM_COMPILER) -c $< -o $@ $(CUSTOM_FLAGS) 24 | 25 | custom_math.o: custom_math.c 26 | @$(CUSTOM_COMPILER) -c $< -o $@ $(CUSTOM_FLAGS) 27 | 28 | test.o: test.c 29 | @$(CUSTOM_COMPILER) -c $< -o $@ $(CUSTOM_FLAGS) 30 | 31 | wav.o: wav.c 32 | @$(CUSTOM_COMPILER) -c $< -o $@ $(CUSTOM_FLAGS) 33 | 34 | clean: 35 | @rm -f fmt *.o compressor tests 36 | -------------------------------------------------------------------------------- /test.c: -------------------------------------------------------------------------------- 1 | 2 | #include "fourier.h" 3 | #include 4 | #include 5 | 6 | int do_compression_tests(void); 7 | 8 | int main(void){ 9 | srand(50); 10 | unsigned int i; 11 | unsigned int tests_remaining = 200; 12 | while (tests_remaining--){ 13 | //unsigned int num_input = (rand() % 100) + 1; 14 | unsigned int num_input = 8; 15 | printf("Running test with %u items.\n", num_input); 16 | struct complex_number * input = malloc(sizeof(struct complex_number) * num_input); 17 | struct complex_number * output = malloc(sizeof(struct complex_number) * num_input); 18 | for(i = 0; i < num_input; i++){ 19 | int rand1 = rand(); 20 | int rand2 = rand(); 21 | int rand3 = rand(); 22 | int rand4 = rand(); 23 | int rand5 = rand(); 24 | int rand6 = rand(); 25 | if(rand1 % 2 == 0 && rand3 != 0){ 26 | input[i].r = (double)rand2 / (double)rand3; 27 | }else{ 28 | input[i].r = (double)rand2; 29 | } 30 | if(rand4 % 2 == 0 && rand6 != 0){ 31 | input[i].i = (double)rand5 / (double)rand6; 32 | }else{ 33 | input[i].i = (double)rand5; 34 | } 35 | } 36 | struct complex_number * original_input = malloc(sizeof(struct complex_number) * num_input); 37 | for(i = 0; i < num_input; i++){ 38 | original_input[i] = input[i]; 39 | } 40 | 41 | generic_slow_discrete_fourier_transform(input, output, num_input, 1.0, -1); 42 | 43 | generic_slow_discrete_fourier_transform(output, input, num_input, (1.0 / (double)num_input), 1); 44 | 45 | for(i = 0; i < num_input; i++){ 46 | double diff_r = input[i].r - original_input[i].r; 47 | double diff_i = input[i].i - original_input[i].i; 48 | 49 | double diff_ratio = magnitude(input[i]) / magnitude(original_input[i]); 50 | printf("i=%u, diff ratio %.30f observed r: %f, orig r: %f\n", i, diff_ratio, input[i].r, original_input[i].r); 51 | (void)diff_r; 52 | (void)diff_i; 53 | } 54 | 55 | free(output); 56 | free(input); 57 | free(original_input); 58 | } 59 | 60 | do_compression_tests(); 61 | return 0; 62 | } 63 | -------------------------------------------------------------------------------- /fourier.c: -------------------------------------------------------------------------------- 1 | 2 | #include "fourier.h" 3 | #include 4 | 5 | void generic_slow_discrete_fourier_transform(struct complex_number * in, struct complex_number * out, unsigned int series_length, double scale, double direction){ 6 | /* 7 | scale is usually 1 for forward transform, and 1/n for reverse transform. 8 | direction is usually -1 for forward transform, and 1 for reverse transform. 9 | */ 10 | unsigned int i; 11 | for(i = 0; i < series_length; i++){ 12 | struct complex_number s = make_complex_number(0, 0); 13 | unsigned int j; 14 | for(j = 0; j < series_length; j++){ 15 | s = complex_add(s, complex_multiply(in[j], e_i_x((direction * 2.0 * PI * (double)i * (double)j) / (double)series_length))); 16 | } 17 | out[i] = complex_multiply(make_complex_number(scale, 0), s); 18 | } 19 | } 20 | 21 | void bit_reverse_copy(struct complex_number * in, struct complex_number * out, unsigned int series_length, unsigned int log_2_len){ 22 | unsigned int i; 23 | for(i = 0; i < series_length; i++){ 24 | unsigned int NO_OF_BITS = log_2_len; 25 | unsigned int reverse_i = 0, j, temp; 26 | 27 | for (j = 0; j < NO_OF_BITS; j++) { 28 | temp = (i & (1 << j)); 29 | if(temp){ 30 | reverse_i |= (1 << ((NO_OF_BITS - 1) - j)); 31 | } 32 | } 33 | 34 | out[reverse_i] = in[i]; 35 | } 36 | } 37 | 38 | void fast_discrete_fourier_transform(struct complex_number * in, struct complex_number * out, unsigned int series_length, double scale, double direction){ 39 | uint32_t n = series_length; 40 | uint32_t log_2_len = 0; 41 | uint32_t num_ones = 0; 42 | uint32_t s; 43 | 44 | /* Determine the log base 2 value, and verify that it is a power of two. */ 45 | do { 46 | n & 0x1 ? num_ones++ : 0; 47 | if((n >>= 1) > 0){ 48 | log_2_len++; 49 | } 50 | } while (n > 0); 51 | 52 | assert(num_ones == 1 && "Must be a power of two!"); 53 | bit_reverse_copy(in, out, series_length, log_2_len); 54 | for (s = 1; s <= log_2_len; s++){ 55 | uint32_t m = 0x1 << s; 56 | struct complex_number omega_m = e_i_x((direction * (double)2 * PI) / (double)m); 57 | uint32_t k; 58 | for (k = 0; k < series_length; k += m){ 59 | struct complex_number omega = make_complex_number(1, 0); 60 | uint32_t j; 61 | for (j = 0; j < (m / 2); j++){ 62 | struct complex_number t = complex_multiply(omega, out[k + j + m/2]); 63 | struct complex_number u = out[k + j]; 64 | out[k + j] = complex_add(u, t); 65 | out[k + j + m / 2] = complex_add(u, complex_multiply(make_complex_number(-1,0), t)); 66 | omega = complex_multiply(omega, omega_m); 67 | } 68 | } 69 | } 70 | 71 | for(s = 0; s < series_length; s++){ 72 | out[s] = complex_multiply(make_complex_number(scale, 0), out[s]); 73 | } 74 | } 75 | -------------------------------------------------------------------------------- /compressor.h: -------------------------------------------------------------------------------- 1 | #ifndef MEME_COMPRESSOR_H 2 | #define MEME_COMPRESSOR_H 3 | 4 | #include 5 | #include 6 | #include 7 | #include "io.h" 8 | 9 | #define MAX_TRIE_DEPTH 4 10 | 11 | struct trie_node{ 12 | char c; 13 | struct trie_node_ref * children; 14 | unsigned int num_children; 15 | unsigned int count; 16 | int last_index; 17 | }; 18 | 19 | struct trie_node_ref{ 20 | char c; 21 | struct trie_node * t; 22 | }; 23 | 24 | struct character_node{ 25 | unsigned int index; 26 | char c; 27 | struct character_node * next; 28 | struct character_node * prev; 29 | }; 30 | 31 | struct compression_map{ 32 | /* An array of pointers that describes what each character will decompress to. */ 33 | char * decompression_pointers[256]; /* Pointers to strings that describe what the character should map to. 34 | NULL pointer means that this character is free to represent something new. Illegal characters should be 35 | represented as mapping to empty string. 36 | */ 37 | signed int next_available_character; 38 | }; 39 | 40 | 41 | struct trie_node * make_trie_node(char c, unsigned int trie_depth); 42 | void add_trie_child(struct trie_node * t, char c, unsigned int trie_depth); 43 | void add_trie_string(struct trie_node *, struct character_node *, unsigned int, unsigned int, unsigned int); 44 | void traverse_trie_child(struct trie_node * t, struct character_node * str, unsigned int num_chars, unsigned int origin_index, unsigned int trie_depth); 45 | void add_trie_string(struct trie_node * t, struct character_node * str, unsigned int num_chars, unsigned int origin_index, unsigned int trie_depth); 46 | void print_trie_level(struct trie_node * t, unsigned int level); 47 | void trie_destroy(struct trie_node * t); 48 | void make_random_input(struct print_buffer *); 49 | void find_max_count_trie(struct trie_node * t, unsigned int * max); 50 | unsigned int fill_max_count_trie(struct trie_node * t, unsigned int max, char * c); 51 | void make_character_list(struct character_node ** n, char * c, unsigned int chars_left); 52 | void enumerate_character_list(struct character_node * n, unsigned int index); 53 | void destroy_character_list(struct character_node * n); 54 | void print_character_list(struct character_node *, struct print_buffer *); 55 | int check_sequence_match(struct character_node * chrs, char * find, unsigned int chrs_left); 56 | struct character_node * get_following_character(struct character_node * chr, unsigned int depth); 57 | void replace_sequences(struct character_node ** chrs, char * winning_trie, unsigned int trie_depth, char c); 58 | unsigned int count_characters(struct character_node * c); 59 | void set_next_available_character(struct compression_map * m); 60 | void initialize_decompression_map(struct compression_map * m); 61 | void print_compression_map(struct compression_map * m); 62 | void create_compressed_package(struct compression_map *, struct character_node *, struct print_buffer *); 63 | void destroy_compression_map(struct compression_map * m); 64 | void add_decompression_mapping(struct compression_map * m, char * winning_trie, unsigned int trie_depth, char c); 65 | void do_compression(struct print_buffer *, struct print_buffer *, unsigned int); 66 | 67 | #endif 68 | -------------------------------------------------------------------------------- /wav.c: -------------------------------------------------------------------------------- 1 | 2 | #include "wav.h" 3 | 4 | void print_wav_header(struct wav_file_header * h){ 5 | printf("# Read .wav file header:\n"); 6 | printf("# RIFF: %c%c%c%c\n", h->RIFF[0], h->RIFF[1], h->RIFF[2], h->RIFF[3]); 7 | printf("# file_size_bytes: %u\n", h->file_size_bytes); 8 | printf("# WAVE: %c%c%c%c\n", h->WAVE[0], h->WAVE[1], h->WAVE[2], h->WAVE[3]); 9 | printf("# fmt: %c%c%c%c\n", h->fmt[0], h->fmt[1], h->fmt[2], h->fmt[3]); 10 | printf("# fmt_length: %u\n", h->fmt_length); 11 | printf("# fmt_type: %u\n", h->fmt_type); 12 | printf("# num_channels: %u\n", h->num_channels); 13 | printf("# sample_rate: %u\n", h->sample_rate); 14 | printf("# sample_rate_bps_channels_div8: %u\n", h->sample_rate_bps_channels_div8); 15 | printf("# bps_channels: %u\n", h->bps_channels); 16 | printf("# bits_per_sample: %u\n", h->bits_per_sample); 17 | } 18 | 19 | char * find_wav_data(char * p, char * end, uint32_t verbose){ 20 | while( 21 | !( 22 | ((char *)p)[0] == 'd' && 23 | ((char *)p)[1] == 'a' && 24 | ((char *)p)[2] == 't' && 25 | ((char *)p)[3] == 'a' 26 | ) && 27 | p < end 28 | ){ 29 | if(verbose){ 30 | printf("# Found %c%c%c%c\n", ((char*)p)[0], ((char*)p)[1], ((char*)p)[2], ((char*)p)[3]); 31 | } 32 | p += 1; 33 | } 34 | if(p < end){ 35 | if(verbose){ 36 | printf("# Found data section at address %p\n", (void*)p); 37 | } 38 | return p; 39 | }else{ 40 | if(verbose){ 41 | printf("# Did not find data section.\n"); 42 | } 43 | return (char *)0; 44 | } 45 | } 46 | 47 | int load_wav_period(struct complex_number * time, char * wav_data, uint32_t sample_offset, uint32_t period_number, uint32_t samples_per_period, uint32_t total_samples_mono_channel, struct wav_file_header * header){ 48 | /* 49 | time: A pointer to the time domain data we want to populate. 50 | wav_data: A pointer to the very first sample of the audio data not considering the sample offset starting point. 51 | sample_offset: This is an integer representing the integer offset of the first sample we want to consider (important when start_time != 0). 52 | period_number: An integer representing the current period number. 53 | samples_per_period: An integer representing the number of samples in one period (considered as if there were only one channel). 54 | total_samples_mono_channel: An integer representing the total number of samples in entire wav file (considered as if there were only one channel). 55 | */ 56 | uint32_t begin = sample_offset + samples_per_period * period_number; 57 | uint32_t end = sample_offset + samples_per_period * (period_number + 1); 58 | uint32_t i = begin; 59 | if(begin < total_samples_mono_channel){ 60 | /* Now consider padding. */ 61 | for(i = begin; (i < end) && (i < total_samples_mono_channel); i++){ 62 | /* Only considers data from one channel. TODO: Consider the other channel if present. */ 63 | unsigned int data_index = i * header->num_channels; 64 | double v; 65 | switch(header->bits_per_sample){ 66 | case 8:{ 67 | /* It turns out that 8-bit .wav formats are usually unsigned centered at 0x80 while higher bits are signed. */ 68 | v = (double)((int32_t)(((uint8_t*)wav_data)[data_index]) - 0x80); 69 | break; 70 | }case 16:{ 71 | v = (double)((int16_t*)wav_data)[data_index]; 72 | break; 73 | }case 32:{ 74 | v = (double)((int32_t*)wav_data)[data_index]; 75 | break; 76 | }default:{ 77 | assert(0); 78 | } 79 | } 80 | time[i - begin].r = v; 81 | time[i - begin].i = 0; 82 | } 83 | /* Possibly more data to process. */ 84 | return 0; 85 | }else{ 86 | return 1; 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | ![Fire](http://recc.robertelder.org/fire.gif "Fire") DO NOT CREATE PULL REQUESTS FOR THIS PROJECT. ![Fire](http://recc.robertelder.org/fire.gif "Fire") 4 | 5 | ![Fire](http://recc.robertelder.org/fire.gif "Fire") ANY PULL REQUESTS YOU CREATE WILL NOT BE MERGED IN. ![Fire](http://recc.robertelder.org/fire.gif "Fire") 6 | 7 | Contributing to this project is not currently permitted. You are, however, encouraged to create forks. 8 | 9 | Having said this, don't let the warning above prevent you from filing issues if you find something broken or undesirable. 10 | 11 | # THE FAST MEME TRANSFORM 12 | 13 | This repository contains a tool that can be used to transform audio in .wav files into self-contained Linux commands that contain all the audio data, along with an audio codec. 14 | 15 | For example, you can use the following commands to take audio extracted from a YouTube video and create a Linux command that plays the audio: 16 | 17 | ``` 18 | # Clone this repo 19 | git clone https://github.com/RobertElderSoftware/fast-meme-transform.git && cd fast-meme-transform 20 | # These instructions were tested to work correctly with this commit hash (optional step): 21 | git checkout 05c48a3271697217f893a97d87eea29fb4ff6f90 22 | # Build Fast Meme Transform tool 23 | make fmt 24 | # Get audio in PCM .wav format 25 | youtube-dl M6PbdJiAK84 -x --audio-format wav -o "tripple.%(ext)s" 26 | # Build the self-contained Linux command 27 | ./fmt -f tripple.wav -s 4.9 -e 7.9 28 | ``` 29 | 30 | The resulting output will be a Unix command that plays the audio in the clip: 31 | 32 | ``` 33 | echo -n 'uuuuJC8/CvOoO2/OgO5EGvHLHU:i:a:]:_:8I:c<3/D1>D2(DqD4/D5YD6FD7>DaDc?l?{?W?5E9z9fDiDa?k?1=r?2=r?4Y?h?m?[n9z9Q?|?3I?4Br?5I?6>?7}190E9f?P?f?p?4/?5Br?6B5#?7/n8h8a8ZD2/DWDgD5>DtD8/Dc?|?o?2/?Ln7h7L7_7a8l8P8WCZ:o:d:p:g:s:b:]<0}<1>72(7Q75/7s7UCkCRCwCtC7E5_6f6p6R6L6Un51F5p54E4h4L4b49EJ77/78I8V8z83X8R39z39W39y397Fn80F8f8i8cKQKmK[j0Xn8h86I8U8b8ZKgKyT{5O%NUN]61V869^y8t8_8[8cKQK_TzT3/T4/N[Nc61l6G^6M7M]9VKmTPTpTgNc61|6G(6H^w8tSRSsSaS]5:*5<*59bN|NoNqNRNyN6^y8m<]DV33vSP4OYS3YS4/S9^R85EuuJ7R7w7s77Y79>8k8W8Qn71X74>75=r76>77/78MP:k:{:3/:5E7U79}80=r8{82MW8R8L:Q:5/:L:Z9{D3XDRDL26_26v27l272(27QnDiDa?l35R35w35t35[3N%j6Fn?z?f35[35c3N/36P363Xnu8V82Mg8s:6I:7Y:[:3>:g:w:tn7fH7/ClCPC4B5#C5BrCLCiCaC]:knGWGwG7IG9>HkHoH2IHgH5}H6>Hin9L9b10q10R11V11{1G(1H%1D*1?*11cG|GPGLGUHlHUH[CoCqC5/CLC_C9F:l:q:h:L:Z0)printf(f==110?"\n":a[i]);else{h=split(a[i],b,"");for(j=0;j0?(R>32767?32767:R):(R<-32768?32768:65536+R)));}else if(n<2*L){o[n-L+1]=R;}}}' | awk '{a[i++]=$0}END{while(jc = c; 7 | t->num_children = 0; 8 | t->count = 0; 9 | t->last_index = -trie_depth; 10 | t->children = (struct trie_node_ref*)0; 11 | return t; 12 | } 13 | 14 | void add_trie_child(struct trie_node * t, char c, unsigned int trie_depth){ 15 | t->num_children++; 16 | t->children = realloc(t->children, sizeof(struct trie_node_ref) * t->num_children); 17 | t->children[t->num_children-1].c = c; 18 | t->children[t->num_children-1].t = make_trie_node(c, trie_depth); 19 | } 20 | 21 | void add_trie_string(struct trie_node *, struct character_node *, unsigned int, unsigned int, unsigned int); 22 | 23 | void traverse_trie_child(struct trie_node * t, struct character_node * str, unsigned int num_chars, unsigned int origin_index, unsigned int trie_depth){ 24 | int i; 25 | assert(num_chars); 26 | for(i = 0; i < t->num_children; i++){ 27 | if(t->children[i].c == str->c){ 28 | add_trie_string(t->children[i].t, str->next, num_chars-1, origin_index, trie_depth); 29 | return; 30 | } 31 | } 32 | /* There were no matching children above, so create a new one. */ 33 | add_trie_child(t, str->c, trie_depth); 34 | /* Just re-try again. */ 35 | traverse_trie_child(t, str, num_chars, origin_index, trie_depth); 36 | } 37 | 38 | void add_trie_string(struct trie_node * t, struct character_node * str, unsigned int num_chars, unsigned int origin_index, unsigned int trie_depth){ 39 | if(num_chars > 0){ 40 | traverse_trie_child(t, str, num_chars, origin_index, trie_depth); 41 | }else{ 42 | /* Make sure that this match isn't going to overlap the previous one. */ 43 | if(t->last_index + trie_depth <= origin_index){ 44 | /* The index of the character where this substring started. */ 45 | t->last_index = origin_index; 46 | t->count++; 47 | } 48 | } 49 | } 50 | 51 | void print_trie_level(struct trie_node * t, unsigned int level){ 52 | int i; 53 | for(i = 0; i < level; i++) printf(">>"); 54 | printf("C is: '%c'\n", t->c); 55 | for(i = 0; i < level; i++) printf(">>"); 56 | printf("count is: %u\n", t->count); 57 | 58 | for(i = 0; i < t->num_children; i++){ 59 | print_trie_level(t->children[i].t, level+1); 60 | } 61 | } 62 | 63 | 64 | void trie_destroy(struct trie_node * t){ 65 | unsigned int i; 66 | for(i = 0; i < t->num_children; i++){ 67 | trie_destroy(t->children[i].t); 68 | } 69 | free(t->children); 70 | free(t); 71 | } 72 | 73 | void make_random_input(struct print_buffer * b){ 74 | char samples[17] = {'0','1','2','3','4','5','6','7','8','9','e','-','=', '.', '+','n','x'}; 75 | unsigned int size = rand() % 1000; 76 | int i; 77 | for(i = 0; i < size; i++){ 78 | printfbuff(b, "%c", samples[rand() % 17]); 79 | } 80 | } 81 | 82 | 83 | void find_max_count_trie(struct trie_node * t, unsigned int * max){ 84 | int i; 85 | if(t->count > *max){ 86 | *max = t->count; 87 | } 88 | 89 | for(i = 0; i < t->num_children; i++){ 90 | find_max_count_trie(t->children[i].t, max); 91 | } 92 | } 93 | 94 | unsigned int fill_max_count_trie(struct trie_node * t, unsigned int max, char * c){ 95 | /* Find the first one that had max count and use that one. */ 96 | int i; 97 | *c = t->c; 98 | if(t->count == max){ 99 | return 1; 100 | } 101 | 102 | for(i = 0; i < t->num_children; i++){ 103 | if(fill_max_count_trie(t->children[i].t, max, c+1)){ 104 | return 1; 105 | } 106 | } 107 | return 0; 108 | } 109 | 110 | void make_character_list(struct character_node ** n, char * c, unsigned int chars_left){ 111 | struct character_node * prev = (struct character_node *)0; 112 | *n = (struct character_node *)0; 113 | while(chars_left > 0){ 114 | *n = malloc(sizeof(struct character_node)); 115 | (*n)->c = c[0]; 116 | (*n)->prev = prev; 117 | 118 | /* Advance to next char. */ 119 | chars_left--; 120 | c += 1; 121 | n = &(*n)->next; 122 | if(chars_left == 0){ 123 | *n = (struct character_node *)0; 124 | } 125 | } 126 | } 127 | 128 | void enumerate_character_list(struct character_node * n, unsigned int index){ 129 | while(n){ 130 | n->index = index; 131 | n = n->next; 132 | index += 1; 133 | } 134 | } 135 | 136 | void destroy_character_list(struct character_node * n){ 137 | while(n){ 138 | struct character_node * t = n; 139 | n = n->next; 140 | free(t); 141 | } 142 | } 143 | 144 | void print_character_list(struct character_node * n, struct print_buffer * b){ 145 | while(n){ 146 | printfbuff(b, "%c", n->c); 147 | n = n->next; 148 | } 149 | } 150 | 151 | int check_sequence_match(struct character_node * chrs, char * find, unsigned int chrs_left){ 152 | if(chrs){ 153 | if(chrs_left){ 154 | if(chrs->c == *find){ 155 | return check_sequence_match(chrs->next, find + 1, chrs_left -1); 156 | }else{ 157 | return 0; 158 | } 159 | }else{ 160 | return 1; 161 | } 162 | }else{ 163 | return 0; 164 | } 165 | } 166 | 167 | struct character_node * get_following_character(struct character_node * chr, unsigned int depth){ 168 | if(depth){ 169 | return get_following_character(chr->next, depth -1); 170 | }else{ 171 | return chr; 172 | } 173 | } 174 | 175 | void replace_sequences(struct character_node ** chrs, char * winning_trie, unsigned int trie_depth, char c){ 176 | struct character_node * current = *chrs; 177 | struct character_node * previous = (struct character_node *)0; 178 | while(current){ 179 | if(check_sequence_match(current, winning_trie, trie_depth)){ 180 | struct character_node * n = malloc(sizeof(struct character_node)); 181 | struct character_node * following = get_following_character(current, trie_depth); 182 | /* Update new node links */ 183 | n->c = c; 184 | n->prev = previous; 185 | n->next = following; 186 | /* Update link from prev to new */ 187 | if(previous){ 188 | previous->next = n; 189 | }else{ 190 | *chrs = n; 191 | } 192 | 193 | /* Update link from following to n. */ 194 | if(following){ 195 | following->prev = n; 196 | } 197 | 198 | while(current != following){ 199 | struct character_node * next = current->next; 200 | free(current); 201 | current = next; 202 | } 203 | 204 | /* Update navigation pointers. */ 205 | previous = n; 206 | current = following; 207 | }else{ 208 | previous = current; 209 | current = current->next; 210 | } 211 | } 212 | } 213 | 214 | unsigned int count_characters(struct character_node * c){ 215 | unsigned int i = 0; 216 | while(c){ 217 | c = c->next; 218 | i++; 219 | } 220 | return i; 221 | } 222 | 223 | void set_next_available_character(struct compression_map * m){ 224 | signed int i; 225 | for(i = m->next_available_character + 1; i < 256; i++){ 226 | if(!m->decompression_pointers[i]){ 227 | m->next_available_character = i; 228 | return; 229 | } 230 | } 231 | m->next_available_character = -1; 232 | } 233 | 234 | void initialize_decompression_map(struct compression_map * m){ 235 | unsigned int i; 236 | m->next_available_character = 0; 237 | /* Control characters, map to empty string. */ 238 | for(i = 0; i < 32; i++){ 239 | m->decompression_pointers[i] = malloc(1 * sizeof(char)); 240 | m->decompression_pointers[i][0] = '\0'; 241 | } 242 | 243 | /* Most regular characters. Initialize to null. */ 244 | for(i = 32; i < 128; i++){ 245 | m->decompression_pointers[i] = (char *)0; 246 | } 247 | 248 | /* Extended ASCII characters. */ 249 | for(i = 128; i < 256; i++){ 250 | m->decompression_pointers[i] = malloc(1 * sizeof(char)); 251 | m->decompression_pointers[i][0] = '\0'; 252 | } 253 | 254 | /* Digits map to themselves. */ 255 | for(i = 48; i < 58; i++){ 256 | m->decompression_pointers[i] = malloc(2 * sizeof(char)); 257 | m->decompression_pointers[i][0] = i; 258 | m->decompression_pointers[i][1] = '\0'; 259 | } 260 | 261 | /* Other terminal characters that must map to themselves. */ 262 | { 263 | char terminals[7] = {'e', '=', 'x', '-', '.', '+', ';'}; 264 | for(i = 0; i < 7; i++){ 265 | m->decompression_pointers[(int)terminals[i]] = malloc(2 * sizeof(char)); 266 | m->decompression_pointers[(int)terminals[i]][0] = terminals[i]; 267 | m->decompression_pointers[(int)terminals[i]][1] = '\0'; 268 | } 269 | } 270 | 271 | /* n is a special case because we want to translate it directly into a newline in the meme transform tool. */ 272 | /* NOTE: Actually, this had to be changed because awk on a Mac will split a newline into two empty strings. */ 273 | m->decompression_pointers['n'] = malloc(1 * sizeof(char)); 274 | m->decompression_pointers['n'][0] = '\0'; 275 | 276 | /* Illegal characters that might cause problems in shells and should never show up in input. Map them to empty string. */ 277 | { 278 | char illegals[9] = {'!', '`', '"', '\'', '$', '~', ',', '\\', 0x7F}; 279 | for(i = 0; i < 9; i++){ 280 | m->decompression_pointers[(int)illegals[i]] = malloc(1 * sizeof(char)); 281 | m->decompression_pointers[(int)illegals[i]][0] = '\0'; 282 | } 283 | } 284 | 285 | set_next_available_character(m); 286 | } 287 | 288 | void print_compression_map(struct compression_map * m){ 289 | int i; 290 | for(i = 0; i < 256; i++){ 291 | printf("0x%X ", i); 292 | if(i > 31 && i < 127){ 293 | printf("'%c' ", i); 294 | }else{ 295 | printf("--- "); 296 | } 297 | printf("--- => "); 298 | if(m->decompression_pointers[i]){ 299 | printf("'%s'\n", m->decompression_pointers[i]); 300 | }else{ 301 | printf("null\n"); 302 | } 303 | } 304 | } 305 | 306 | void create_compressed_package(struct compression_map * m, struct character_node * chrs, struct print_buffer * b){ 307 | unsigned int i; 308 | printfbuff(b, "echo -n '"); 309 | print_character_list(chrs, b); 310 | printfbuff(b, "' | "); 311 | 312 | printfbuff(b, "hexdump -v -e '/1 \"%%u\\n\"' | awk '"); 313 | printfbuff(b, "function d(f,k,a,b,o,w,i,q,h,j){"); 314 | printfbuff(b, "for(k=32;k<127;k++)"); 315 | printfbuff(b, "o[sprintf(\"%%c\", k)]=k;"); 316 | printfbuff(b, "split(\""); 317 | for(i = 32; i < 127; i++){ 318 | if(m->decompression_pointers[i]){ 319 | printfbuff(b, "%s,", m->decompression_pointers[i]); 320 | }else{ 321 | printfbuff(b, ","); 322 | } 323 | } 324 | printfbuff(b, "\",a,\",\");"); 325 | /* The list of terminal characters that will stop the recursion. */ 326 | printfbuff(b, "split(\"0,1,2,3,4,5,6,7,8,9,=,e,x,+,-,.,n,;\",w,\",\");"); 327 | printfbuff(b, "q=0;"); 328 | printfbuff(b, "for(g in w)"); 329 | printfbuff(b, "if(o[w[g]]==f)"); 330 | printfbuff(b, "q++;"); 331 | printfbuff(b, "i=f-32+1;"); 332 | printfbuff(b, "if(q>0)"); 333 | printfbuff(b, "printf(f==110?\"\\n\":a[i]);"); 334 | printfbuff(b, "else{"); 335 | /* Split up the characters and recursively run the expansion rules on each character. */ 336 | printfbuff(b, "h=split(a[i],b,\"\");"); 337 | printfbuff(b, "for(j=0;jdecompression_pointers[i]); 349 | } 350 | } 351 | 352 | void add_decompression_mapping(struct compression_map * m, char * winning_trie, unsigned int trie_depth, char c){ 353 | char * new = malloc((trie_depth + 1) * sizeof(char)); 354 | unsigned int i; 355 | for(i = 0; i < trie_depth; i++){ 356 | new[i] = winning_trie[i]; 357 | } 358 | new[i] = '\0'; 359 | m->decompression_pointers[(int)c] = new; 360 | } 361 | 362 | void do_compression(struct print_buffer * in, struct print_buffer * out, unsigned int verbose){ 363 | struct compression_map m; 364 | unsigned int max_trie_depth = MAX_TRIE_DEPTH; /* The maximum length of substring that we may want to replace. */ 365 | unsigned int occurrences[MAX_TRIE_DEPTH]; /* For calculating the occurrences from the various sequence sizes. */ 366 | int i; 367 | struct character_node * chrs; 368 | unsigned int done = 0; 369 | unsigned int current_size; 370 | initialize_decompression_map(&m); 371 | make_character_list(&chrs, in->characters, in->buffer_size-1); 372 | /* While there are still free characters that can be used as a substitution rule. */ 373 | while(!done && m.next_available_character != -1){ 374 | unsigned int trie_depth; 375 | char winning_trie[MAX_TRIE_DEPTH][MAX_TRIE_DEPTH + 1]; 376 | unsigned int highest_trie = 0; 377 | unsigned int highest_trie_benefit = 0; 378 | enumerate_character_list(chrs, 0); 379 | /* Array to store the nth winning result, which is a string that takes up trie_depth + 1 characters. */ 380 | current_size = count_characters(chrs); 381 | for(trie_depth = 2; trie_depth < max_trie_depth; trie_depth++){ 382 | struct trie_node * root = make_trie_node('\0', trie_depth); 383 | struct character_node * current_character = chrs; 384 | for(i = 0; (current_size > trie_depth) && (i < (current_size-(trie_depth-1))); i++){ 385 | add_trie_string(root, current_character, trie_depth, i, trie_depth); 386 | current_character = current_character->next; 387 | } 388 | 389 | occurrences[trie_depth] = 0; 390 | find_max_count_trie(root, &occurrences[trie_depth]); 391 | fill_max_count_trie(root, occurrences[trie_depth], &winning_trie[trie_depth][0]); 392 | trie_destroy(root); 393 | } 394 | 395 | for(trie_depth = 2; (trie_depth < max_trie_depth) && current_size > trie_depth; trie_depth++){ 396 | /* The approximate benefit in terms of characters removed considering the decompressor rule and replcement character. */ 397 | unsigned int current_benefit = occurrences[trie_depth] > 0 ? (occurrences[trie_depth] -1) * (trie_depth-1) : 0; 398 | if(current_benefit > highest_trie_benefit){ 399 | highest_trie_benefit = current_benefit; 400 | highest_trie = trie_depth; 401 | } 402 | } 403 | if(highest_trie == 0){ 404 | done = 1; /* Exit loop. */ 405 | }else{ 406 | if(verbose){ 407 | unsigned int g; 408 | printf("# Creating replacement rule for trie length %u '%c' -> '", highest_trie, m.next_available_character); 409 | for(g = 1; g < highest_trie + 1; g++){ 410 | printf("%c", winning_trie[highest_trie][g]); 411 | } 412 | printf("' for a savings of %u characters.\n", highest_trie_benefit); 413 | } 414 | replace_sequences(&chrs, &winning_trie[highest_trie][1], highest_trie, m.next_available_character); 415 | add_decompression_mapping(&m, &winning_trie[highest_trie][1], highest_trie, m.next_available_character); 416 | set_next_available_character(&m); 417 | } 418 | } 419 | 420 | create_compressed_package(&m, chrs, out); 421 | 422 | destroy_compression_map(&m); 423 | destroy_character_list(chrs); 424 | } 425 | 426 | void do_compression_tests(void){ 427 | unsigned int i; 428 | srand(3); 429 | for(i = 0; i < 100; i++){ 430 | struct print_buffer in; 431 | struct print_buffer out; 432 | init_print_buffer(&in); 433 | init_print_buffer(&out); 434 | 435 | make_random_input(&in); 436 | do_compression(&in, &out, 1); 437 | free(in.characters); 438 | printf("Result is '%s'\n",out.characters); 439 | free(out.characters); 440 | } 441 | } 442 | -------------------------------------------------------------------------------- /fmt.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | #include 5 | 6 | #include "fourier.h" 7 | #include "wav.h" 8 | #include "compressor.h" 9 | 10 | enum command_types { 11 | ALL_SOX = 0, 12 | UBUNTU_APLAY = 1, 13 | COREAUDIO_SOX = 2, 14 | COREAUDIO_ALSA = 3 15 | }; 16 | 17 | struct biquad_filter_details{ 18 | char type; 19 | double Q; 20 | double F_c; 21 | }; 22 | 23 | struct run_params{ 24 | uint32_t display_help_and_exit; 25 | int32_t infile_argv_index; 26 | uint32_t command_type; 27 | uint32_t verbose; 28 | uint32_t include_imaginary; 29 | uint32_t enable_endpoint_discontinuities; 30 | uint32_t full_power_spectrum; 31 | uint32_t use_compression; 32 | uint32_t apply_hann_window; 33 | uint32_t float_precision; 34 | double sample_time_period; 35 | double volume; 36 | double threshold; 37 | double starttime; 38 | double endtime; 39 | double pitch; 40 | struct biquad_filter_details * bfds; 41 | uint32_t num_biquad_filters; 42 | }; 43 | 44 | int load_input_file(char * argv[], signed int infile_argv_index, char ** buffer, uint32_t * bytes_used){ 45 | FILE *f = NULL; 46 | int c = 0; 47 | uint32_t buffer_size = 1; 48 | *buffer = (char *)malloc(1); 49 | 50 | if(infile_argv_index > 0){ 51 | if(!(f = fopen(argv[infile_argv_index], "rb"))){ 52 | printf("Failed to open file %s for read.\n", argv[infile_argv_index]); 53 | return 1; 54 | } 55 | }else{ 56 | f = stdin; 57 | } 58 | 59 | while (c != EOF) { 60 | c = getc(f); 61 | if(c == EOF) 62 | break; 63 | if(!(*bytes_used < buffer_size)){ 64 | buffer_size *= 2; 65 | *buffer = realloc(*buffer, buffer_size * sizeof(char)); 66 | } 67 | (*buffer)[*bytes_used] = c; 68 | (*bytes_used)++; 69 | } 70 | fclose(f); 71 | return 0; 72 | } 73 | 74 | int is_bigger_than_threshold(double th, double r, double i, uint32_t include_imaginary){ 75 | if(fabs(r) > th){ 76 | return 1; 77 | }else if(include_imaginary && fabs(i) > th){ 78 | return 1; 79 | }else{ 80 | return 0; 81 | } 82 | } 83 | 84 | void apply_biquad_filter(struct complex_number * x, unsigned int len, struct biquad_filter_details * bfd, double f_s, unsigned int verbose){ 85 | struct complex_number * y = malloc(sizeof(struct complex_number) * (len + 2)); 86 | double K = tan(PI * (bfd->F_c / f_s)); 87 | double Q = bfd->Q; 88 | 89 | /* Coefficients from quadratic equations resulting from bi-linear transform. */ 90 | double a1 = (2 * (K*K - 1)) / (K*K + (K/Q) + 1); 91 | double a2 = (K*K - (K/Q) + 1) / (K*K + (K/Q) + 1); 92 | double b0_lowpass = (K*K) / (K*K + K/Q + 1); 93 | double b0_highpass = 1 / (K*K + K/Q + 1); 94 | double b0 = bfd->type == 'l' ? b0_lowpass : b0_highpass; 95 | double b1_lowpass = 2 * b0; 96 | double b1_highpass = -2 * b0; 97 | double b1 = bfd->type == 'l' ? b1_lowpass : b1_highpass; 98 | double b2 = b0; 99 | double wn0 = 0; 100 | double wn1 = 0; 101 | double wn2 = 0; 102 | unsigned int i; 103 | if(verbose){ 104 | printf("# Applying a biquad filter with type=%c Q=%f, F_c=%f, F_s=%f\n", bfd->type, Q, bfd->F_c, f_s); 105 | printf("# a1=%f\n", a1); 106 | printf("# a2=%f\n", a2); 107 | printf("# b0=%f\n", b0); 108 | printf("# b1=%f\n", b1); 109 | printf("# b2=%f\n", b2); 110 | } 111 | 112 | /* Digital biquad filter, direct form 2. */ 113 | for(i = 0; i < len + 2; i++){ 114 | wn0 = x[i].r -(a1 * wn1) - (a2 * wn2); 115 | y[i].r = b0 * wn0 + b1 * wn1 + b2 * wn2; 116 | /* Update registers. */ 117 | wn2 = wn1; 118 | wn1 = wn0; 119 | } 120 | 121 | for(i = 0; i < len; i++){ 122 | /* Move filtered time domain signal back, and discard first 2 values. */ 123 | x[i].r = y[i + 2].r; 124 | } 125 | free(y); 126 | } 127 | 128 | void apply_hann_window(struct complex_number * in, unsigned int len){ 129 | unsigned int i; 130 | for(i = 0; i < len; i++){ 131 | double hann = 0.5 * (1.0 - cos((2.0 * PI * (double)i) / (double)(len - 1.0))); 132 | in[i].r *= hann; 133 | in[i].i *= hann; 134 | } 135 | } 136 | 137 | void do_decoder_output(unsigned int samples_per_period_per_channel_pow_2, unsigned int samples_per_period_per_channel, unsigned int num_required_periods, unsigned int num_samples_last_period, unsigned int sample_rate, struct run_params * p){ 138 | struct print_buffer fmt_string; 139 | init_print_buffer(&fmt_string); 140 | 141 | printfbuff(&fmt_string, " | awk '"); 142 | printfbuff(&fmt_string, "BEGIN{"); 143 | printfbuff(&fmt_string, "L=%u;", samples_per_period_per_channel); 144 | printfbuff(&fmt_string, "N=%u;", samples_per_period_per_channel_pow_2); 145 | printfbuff(&fmt_string, "for(n=0;ninclude_imaginary){ 153 | printfbuff(&fmt_string, "split(p[2],z,\";\");"); 154 | printfbuff(&fmt_string, "x[j+1]=z[1];"); 155 | printfbuff(&fmt_string, "y[j+1]=z[2];"); 156 | }else{ 157 | printfbuff(&fmt_string, "x[j+1]=p[2];"); 158 | } 159 | printfbuff(&fmt_string, "f[j+1]=p[1];"); 160 | printfbuff(&fmt_string, "}"); 161 | printfbuff(&fmt_string, "M=(NR==%u?%u:2*L);", num_required_periods, (num_samples_last_period == 0 ? samples_per_period_per_channel : num_samples_last_period)); 162 | printfbuff(&fmt_string, "for(n=0;ninclude_imaginary){ 165 | printfbuff(&fmt_string, "I=0;"); 166 | } 167 | printfbuff(&fmt_string, "for(j=0;jpitch); 169 | if(p->include_imaginary){ 170 | printfbuff(&fmt_string, "b=sin(2*3.141592*f[j+1]*(n/N)*%f);", p->pitch); 171 | } 172 | if(p->include_imaginary){ 173 | printfbuff(&fmt_string, "R+=(x[j+1]*a)-(y[j+1]*b);"); 174 | printfbuff(&fmt_string, "I+=(x[j+1]*b)+(y[j+1]*a);"); 175 | }else{ 176 | printfbuff(&fmt_string, "R+=(x[j+1]*a);"); 177 | } 178 | printfbuff(&fmt_string, "}"); 179 | printfbuff(&fmt_string, "if(nvolume != 1){ 183 | /* Multiply by max 16 bit signed int to scale. */ 184 | printfbuff(&fmt_string, "R*=%f;", p->volume); 185 | } 186 | printfbuff(&fmt_string, "R=int(R*32767);"); 187 | /* The integer comparisons are used to print negative signed 16 bit integers as though they were unsigned 16/32/64 bit integers. */ 188 | printfbuff(&fmt_string, "printf(\"%%04X\\n\",R==0?0:(R>0?(R>32767?32767:R):(R<-32768?32768:65536+R)));"); 189 | 190 | printfbuff(&fmt_string, "}else if(n<2*L){"); 191 | if(!p->enable_endpoint_discontinuities){ 192 | printfbuff(&fmt_string, "o[n-L+1]=R;"); 193 | } 194 | printfbuff(&fmt_string, "}"); 195 | printfbuff(&fmt_string, "}"); 196 | printfbuff(&fmt_string, "}' | awk '{a[i++]=$0}END{while(jcommand_type == ALL_SOX){ 199 | printfbuff(&fmt_string, "sox -q -r %u -b 16 -B -e signed -c 1 -t raw - -t `sox -h|grep DRIVERS|sed 's/^.*: \\([^ ]\\+\\).*$/\\1/g'`", sample_rate); 200 | }else if(p->command_type == UBUNTU_APLAY){ 201 | printfbuff(&fmt_string, "aplay -q -c 1 -f S16_BE -r %u ", sample_rate); 202 | }else if(p->command_type == COREAUDIO_SOX){ 203 | printfbuff(&fmt_string, "sox -q -r %u -b 16 -B -e signed -c 1 -t raw - -t coreaudio", sample_rate); 204 | }else if(p->command_type == COREAUDIO_ALSA){ 205 | printfbuff(&fmt_string, "sox -q -r %u -b 16 -B -e signed -c 1 -t raw - -t alsa", sample_rate); 206 | }else{ 207 | assert("Unknown play value." && 0); 208 | } 209 | 210 | printfbuff(&fmt_string, "\n"); 211 | 212 | printf("%s", fmt_string.characters); 213 | free(fmt_string.characters); 214 | } 215 | 216 | 217 | int parse_an_argument(int argc, char * argv[], struct run_params * p, unsigned int * current_arg){ 218 | if(!strcmp(argv[*current_arg], "--help") || !strcmp(argv[*current_arg], "-h")){ 219 | p->display_help_and_exit = 1; 220 | (*current_arg) += 1; 221 | }else if(!strcmp(argv[*current_arg], "--verbose") || !strcmp(argv[*current_arg], "-v")){ 222 | p->verbose = 1; 223 | (*current_arg) += 1; 224 | }else if(!strcmp(argv[*current_arg], "--file") || !strcmp(argv[*current_arg], "-f")){ 225 | (*current_arg) += 1; 226 | assert(*current_arg < argc); 227 | p->infile_argv_index = *current_arg; 228 | (*current_arg) += 1; 229 | }else if(!strcmp(argv[*current_arg], "--disable-compression") || !strcmp(argv[*current_arg], "-u")){ 230 | p->use_compression = 0; 231 | (*current_arg) += 1; 232 | }else if(!strcmp(argv[*current_arg], "--enable-hann-window") || !strcmp(argv[*current_arg], "-w")){ 233 | p->apply_hann_window = 1; 234 | (*current_arg) += 1; 235 | }else if(!strcmp(argv[*current_arg], "--enable-endpoint-discontinuities") || !strcmp(argv[*current_arg], "-b")){ 236 | p->enable_endpoint_discontinuities = 1; 237 | (*current_arg) += 1; 238 | }else if(!strcmp(argv[*current_arg], "--include-imaginary") || !strcmp(argv[*current_arg], "-i")){ 239 | /* When the imaginary parts aren't included the amplitude of the output is only half. */ 240 | p->volume /= 2; 241 | p->include_imaginary = 1; 242 | (*current_arg) += 1; 243 | }else if(!strcmp(argv[*current_arg], "--command-type") || !strcmp(argv[*current_arg], "-k")){ 244 | (*current_arg) += 1; 245 | assert(*current_arg < argc); 246 | p->command_type = strtoul(argv[*current_arg], (char **)0, 10); 247 | (*current_arg) += 1; 248 | }else if(!strcmp(argv[*current_arg], "--full-power-spectrum") || !strcmp(argv[*current_arg], "-a")){ 249 | p->full_power_spectrum = 1; 250 | (*current_arg) += 1; 251 | }else if(!strcmp(argv[*current_arg], "--volume") || !strcmp(argv[*current_arg], "-m")){ 252 | (*current_arg) += 1; 253 | assert(*current_arg < argc); 254 | p->volume = strtod(argv[*current_arg], (char **)0); 255 | (*current_arg) += 1; 256 | }else if(!strcmp(argv[*current_arg], "--threshold") || !strcmp(argv[*current_arg], "-t")){ 257 | (*current_arg) += 1; 258 | assert(*current_arg < argc); 259 | p->threshold = strtod(argv[*current_arg], (char **)0); 260 | (*current_arg) += 1; 261 | }else if(!strcmp(argv[*current_arg], "--start-time") || !strcmp(argv[*current_arg], "-s")){ 262 | (*current_arg) += 1; 263 | assert(*current_arg < argc); 264 | p->starttime = strtod(argv[*current_arg], (char **)0); 265 | (*current_arg) += 1; 266 | }else if(!strcmp(argv[*current_arg], "--end-time") || !strcmp(argv[*current_arg], "-e")){ 267 | (*current_arg) += 1; 268 | assert(*current_arg < argc); 269 | p->endtime = strtod(argv[*current_arg], (char **)0); 270 | (*current_arg) += 1; 271 | }else if(!strcmp(argv[*current_arg], "--sample-time-period") || !strcmp(argv[*current_arg], "-p")){ 272 | (*current_arg) += 1; 273 | assert(*current_arg < argc); 274 | p->sample_time_period = strtod(argv[*current_arg], (char **)0); 275 | (*current_arg) += 1; 276 | }else if(!strcmp(argv[*current_arg], "--float-prevision") || !strcmp(argv[*current_arg], "-n")){ 277 | (*current_arg) += 1; 278 | assert(*current_arg < argc); 279 | p->float_precision = strtoul(argv[*current_arg], (char **)0, 10); 280 | (*current_arg) += 1; 281 | }else if(!strcmp(argv[*current_arg], "--pitch") || !strcmp(argv[*current_arg], "-c")){ 282 | (*current_arg) += 1; 283 | assert(*current_arg < argc); 284 | p->pitch = strtod(argv[*current_arg], (char **)0); 285 | (*current_arg) += 1; 286 | }else if(!strcmp(argv[*current_arg], "--filters") || !strcmp(argv[*current_arg], "-l")){ 287 | (*current_arg) += 1; 288 | while(*current_arg < argc && argv[*current_arg][0] != '-'){ 289 | char type = argv[*current_arg][0]; 290 | assert(type == 'l' || type == 'h'); 291 | (*current_arg) += 1; 292 | if(*current_arg < argc && argv[*current_arg][0] != '-'){ 293 | double Q = strtod(argv[*current_arg], (char **)0); 294 | (*current_arg) += 1; 295 | if(*current_arg < argc && argv[*current_arg][0] != '-'){ 296 | unsigned long F_c = strtoul(argv[*current_arg], (char **)0, 10); 297 | (*current_arg) += 1; 298 | p->num_biquad_filters++; 299 | p->bfds = realloc(p->bfds, sizeof(struct biquad_filter_details) * p->num_biquad_filters); 300 | p->bfds[p->num_biquad_filters-1].type = type; 301 | p->bfds[p->num_biquad_filters-1].Q = Q; 302 | p->bfds[p->num_biquad_filters-1].F_c = (double)F_c; 303 | }else{ 304 | printf("TODO: Argument Error\n"); 305 | return 1; 306 | } 307 | }else{ 308 | printf("TODO: Argument Error\n"); 309 | return 1; 310 | } 311 | } 312 | }else{ 313 | printf("Don't know what to do with '%s'... ignoring this part of the parameters. Specify '-f' for the file, or '-h' for help.\n", argv[*current_arg]); 314 | return 1; 315 | *current_arg = argc; 316 | } 317 | return 0; 318 | } 319 | 320 | int populate_run_params(int argc, char * argv[], struct run_params * p){ 321 | unsigned int current_arg = 1; 322 | /* Default params. */ 323 | p->command_type = UBUNTU_APLAY; 324 | p->pitch = 1.0; 325 | p->display_help_and_exit = 0; 326 | p->float_precision = 0; 327 | p->verbose = 0; 328 | p->infile_argv_index = -1; 329 | p->full_power_spectrum = 0; 330 | p->include_imaginary = 0; 331 | p->use_compression = 1; 332 | p->apply_hann_window = 0; 333 | p->volume = 2; 334 | p->threshold = 300; 335 | p->sample_time_period = 0.05; 336 | p->starttime = 0.0; 337 | p->endtime = 0.0; 338 | p->bfds = (struct biquad_filter_details *)0; 339 | p->num_biquad_filters = 0; 340 | p->enable_endpoint_discontinuities = 0; 341 | 342 | while(current_arg < argc){ 343 | if(parse_an_argument(argc, argv, p, ¤t_arg)){ 344 | return 1; 345 | } 346 | } 347 | 348 | if(p->verbose){ 349 | unsigned int i; 350 | printf("# Here are the run-time arguments that will be used:\n"); 351 | printf("# verbose: %s\n", p->verbose ? "Yes" : "No"); 352 | printf("# include_imaginary: %s\n", p->include_imaginary ? "Yes" : "No"); 353 | printf("# use_compression: %s\n", p->use_compression ? "Yes" : "No"); 354 | printf("# full_power_spectrum: %s\n", p->full_power_spectrum ? "Yes" : "No"); 355 | printf("# apply_hann_window: %s\n", p->apply_hann_window ? "Yes" : "No"); 356 | printf("# float_precision: %u\n", p->float_precision); 357 | printf("# volume: %f\n", p->volume); 358 | printf("# threshold: %f\n", p->threshold); 359 | printf("# sample_time_period: %f\n", p->sample_time_period); 360 | printf("# starttime: %f\n", p->starttime); 361 | printf("# endtime: %f (0 means end of clip)\n", p->endtime); 362 | printf("# pitch: %f\n", p->pitch); 363 | printf("# Found %d biquad filter details\n", p->num_biquad_filters); 364 | for(i = 0; i < p->num_biquad_filters; i++){ 365 | printf("# Biquad filter #%d) Q = %f, F_c = %f\n", i, p->bfds[i].Q, p->bfds[i].F_c); 366 | } 367 | } 368 | return 0; 369 | } 370 | 371 | 372 | int analyze_audio_and_build_command(struct run_params *, struct wav_file_header *, char *); 373 | 374 | int analyze_audio_and_build_command(struct run_params * p, struct wav_file_header * header, char * data){ 375 | uint32_t done = 0; 376 | uint32_t current_period = 0; 377 | struct complex_number * time; 378 | struct complex_number * freq; 379 | struct print_buffer uncompressed; 380 | struct print_buffer compressed; 381 | double samples_per_second_per_channel = header->sample_rate; 382 | /* Note that this will have a loss due to conversion to integer so 383 | sample_time_period * samples_per_second_per_channel >= samples_per_period_per_channel / samples_per_second_per_channel 384 | */ 385 | uint32_t samples_per_period_per_channel = p->sample_time_period * samples_per_second_per_channel; 386 | 387 | uint32_t * samples_size_loc = (uint32_t*)(data + 4); 388 | char * actual_data_loc = data + 8; 389 | uint32_t total_samples_all_channels = *samples_size_loc / (header->bits_per_sample/8); 390 | uint32_t total_samples_mono_channel = total_samples_all_channels / header->num_channels; 391 | double clip_end_time = (double)total_samples_mono_channel / (double)header->sample_rate; 392 | double duration = (p->endtime == 0.0 ? clip_end_time : p->endtime) - p->starttime; 393 | 394 | uint32_t clip_total_samples_mono_channel = duration * header->sample_rate; 395 | uint32_t sample_offset = p->starttime * header->sample_rate; 396 | 397 | uint32_t num_required_periods = (clip_total_samples_mono_channel / samples_per_period_per_channel) + ((clip_total_samples_mono_channel % samples_per_period_per_channel) > 0 ? 1 : 0); 398 | uint32_t samples_per_period_per_channel_pow_2 = samples_per_period_per_channel; 399 | uint32_t num_samples_last_period = (clip_total_samples_mono_channel - ((clip_total_samples_mono_channel / samples_per_period_per_channel) * samples_per_period_per_channel)); 400 | /* This assertion should never fire: */ 401 | assert(total_samples_all_channels % header->num_channels == 0); 402 | /* Adjust endtime. */ 403 | p->endtime = p->endtime == 0.0 ? clip_end_time : p->endtime; 404 | if(!(p->endtime <= clip_end_time)){ 405 | printf("The end time can't be after the end of the entire file.\n"); 406 | } 407 | if(!(p->starttime <= p->endtime)){ 408 | printf("The start time should be before the end time.\n"); 409 | } 410 | if(p->verbose){ 411 | printf("# Output clip will start at %f, end at %f. Duration is %f\n", p->starttime, p->endtime, duration); 412 | } 413 | 414 | while(!is_power_of_two(samples_per_period_per_channel_pow_2)){ 415 | samples_per_period_per_channel_pow_2++; 416 | } 417 | /* Prevent aliasing due to circular convolution in overlap add when the default number of samples is already (or is close to) a power of two. */ 418 | samples_per_period_per_channel_pow_2 *= 2; 419 | 420 | if(p->verbose){ 421 | printf("# num_required_periods: %u\n", num_required_periods); 422 | printf("# num_samples_last_period: %u\n", num_samples_last_period); 423 | printf("# All samples size in bytes: %u\n", *samples_size_loc); 424 | printf("# Samples per period per channel : %u\n", samples_per_period_per_channel); 425 | printf("# Samples per period per channel power 2 adjusted: %u\n", samples_per_period_per_channel_pow_2); 426 | printf("# Total input samples mono channel: %u\n", total_samples_mono_channel); 427 | printf("# Total input samples all channels: %u\n", total_samples_all_channels); 428 | printf("# Num Required sample periods: %u\n", num_required_periods); 429 | printf("# Samples Per Period: %u\n", samples_per_period_per_channel); 430 | } 431 | 432 | time = malloc(sizeof(struct complex_number) * samples_per_period_per_channel_pow_2); 433 | freq = malloc(sizeof(struct complex_number) * samples_per_period_per_channel_pow_2); 434 | 435 | init_print_buffer(&uncompressed); 436 | init_print_buffer(&compressed); 437 | 438 | while(!done){ 439 | unsigned int i; 440 | unsigned int num_matches = 0; 441 | int last_one = -1; 442 | /* Initialize everything to zero. */ 443 | for(i = 0; i < samples_per_period_per_channel_pow_2; i++){ 444 | time[i].r = 0.0; 445 | time[i].i = 0.0; 446 | freq[i].r = 0.0; 447 | freq[i].i = 0.0; 448 | } 449 | 450 | done = load_wav_period(&time[0], actual_data_loc, sample_offset, current_period, samples_per_period_per_channel, total_samples_mono_channel, header); 451 | 452 | /* Apply biquad filters to attenuate unwanted frequencies. */ 453 | for(i = 0; i < p->num_biquad_filters; i++){ 454 | apply_biquad_filter(&time[0], samples_per_period_per_channel, &p->bfds[i], (double)header->sample_rate, p->verbose); 455 | } 456 | /* Apply a window function to the data to address issues with endpoint discontinuity. */ 457 | if(p->apply_hann_window){ 458 | apply_hann_window(&time[0], samples_per_period_per_channel); 459 | } 460 | 461 | fast_discrete_fourier_transform( 462 | &time[0], 463 | &freq[0], 464 | samples_per_period_per_channel_pow_2, 465 | (1.0 / (double)samples_per_period_per_channel_pow_2), 466 | -1 467 | ); 468 | 469 | for(i = 0; i < samples_per_period_per_channel_pow_2; i++){ 470 | struct complex_number c = freq[i]; 471 | if(is_bigger_than_threshold(p->threshold, c.r, c.i, p->include_imaginary)){ 472 | num_matches++; 473 | last_one = i; 474 | } 475 | } 476 | 477 | if(num_matches == 0){ 478 | printfbuff(&uncompressed, "0=%s", (p->include_imaginary ? "0;0" : "0")); 479 | }else{ 480 | unsigned int upper_bound = p->full_power_spectrum ? samples_per_period_per_channel_pow_2 : (samples_per_period_per_channel_pow_2 / 2) + 1; 481 | for(i = 0; i < upper_bound; i++){ 482 | struct complex_number c = freq[i]; 483 | if(is_bigger_than_threshold(p->threshold, c.r, c.i, p->include_imaginary)){ 484 | double denominator = (double)(1 << (header->bits_per_sample-1)); 485 | if(p->include_imaginary){ 486 | printfbuff(&uncompressed, "%u=%.*e;%.*e", i, p->float_precision, c.r / denominator, p->float_precision, c.i / denominator); 487 | }else{ 488 | printfbuff(&uncompressed, "%u=%.*e", i, p->float_precision, c.r / denominator); 489 | } 490 | if(i != last_one){ 491 | printfbuff(&uncompressed, "x"); 492 | } 493 | } 494 | } 495 | } 496 | if(!done){ 497 | if(p->use_compression){ 498 | printfbuff(&uncompressed, "n"); 499 | }else{ 500 | printfbuff(&uncompressed, "\\n"); 501 | } 502 | } 503 | current_period++; 504 | if(current_period >= num_required_periods){ 505 | done = 1; 506 | } 507 | } 508 | current_period++; 509 | 510 | if(p->use_compression){ 511 | do_compression(&uncompressed, &compressed, p->verbose); 512 | printf("%s", compressed.characters); 513 | }else{ 514 | printf("echo -en \"%s\"", uncompressed.characters); 515 | } 516 | 517 | free(uncompressed.characters); 518 | free(compressed.characters); 519 | 520 | do_decoder_output(samples_per_period_per_channel_pow_2, samples_per_period_per_channel, num_required_periods, num_samples_last_period, header->sample_rate, p); 521 | 522 | free(time); 523 | free(freq); 524 | free(p->bfds); 525 | return 0; 526 | } 527 | 528 | int main(int argc, char * argv[]){ 529 | struct run_params p; 530 | char * infile_bytes = (char *)0; 531 | uint32_t infile_bytes_size = 0; 532 | struct wav_file_header * header = (struct wav_file_header *)0; 533 | char * end; 534 | char * data; 535 | int rtn = 0; 536 | if(populate_run_params(argc, argv, &p)){ 537 | return 1; 538 | } 539 | 540 | if(p.display_help_and_exit){ 541 | printf("The Fast Meme Transform Tool\n"); 542 | printf("\n"); 543 | printf("Note that if you run the tool without any -f parameter, it will wait for input to come from standard in.\n"); 544 | printf("\n"); 545 | printf("Displaying command flags:\n"); 546 | printf("\t-k, --command-type\n\t\tA single number that will determine which type of command will be generated to play the final audio. You may need to use this option to generate a command that will play in your operating system without installing additional tools. 0 = use sox to automatically detect audio driver (Tested to work on Ubuntu 16), 1 = use aplay (tested on Ubuntu 16) 2 = sox with coreaudio (Probably works on a Mac, but I don't own one so I can't test this), 3 = sox with alsa.\n"); 547 | printf("\t-h, --help\n\t\tDisplay this help menu.\n"); 548 | printf("\t-t, --threshold\n\t\tA floating point number that describes the cutoff power level, below which any frequency with less than this quantity of power will not be included in the output. If you make this number high, the output will be small, but the audio will sound terrible. If you make this value low, the output will be huge and slow to decode, but the result will sound better. If you make this value large, the output will be small but the sound quality will decrease.\n"); 549 | printf("\t-s, --start-time\n\t\tThe start time in seconds where the output audio clip will begin.\n"); 550 | printf("\t-e, --end-time\n\t\tThe end time in seconds where the output audio clip will end. An end time of zero indicates end of the clip.\n"); 551 | printf("\t-v, --verbose\n\t\tBe verbose.\n"); 552 | printf("\t-f, --file\n\t\tThe file name of the input PCM .wav file to be processed.\n"); 553 | printf("\t-u, --disable-compression\n\t\tDisable compression of the output Fourier coefficients.\n"); 554 | printf("\t-w, --enable-hann-window\n\t\tEnable the application of a Hann window to each audio sample period.\n"); 555 | printf("\t-i, --include-imaginary\n\t\tInclude the imaginary parts of the coefficients of the Fourier transform output.\n"); 556 | printf("\t-a, --full-power-spectrum\n\t\tInclude the entire range of frequencies from the Fourier transform output instead of just including up to N/2.\n"); 557 | printf("\t-m, --volume\n\t\tA floating point number describing the output volume.\n"); 558 | printf("\t-b, --enable-endpoint-discontinuities\n\t\tEnable endpoint discontinuities that occur at the ends of sampling intervals (see -p flag). Enable this option if you want the presence of annoying high-frequency popping noises in your audio.\n"); 559 | printf("\t-p, --sample-time-period\n\t\tA floating point number that describes how much long each sub-sample will be when the audio is broken up into small sections to have a Fourier transform applied to it.\n"); 560 | printf("\t-n, --float-precision\n\t\tAn integer describing how many digits should be printed after the decimal after for each Fourier coefficient that appears in the output.\n"); 561 | printf("\t-l, --filters\n\t\tA list of length 3*n that one or more sequence of a single character followed by two numbers that describe the parameters of either a low pass or high pass biquad filter. Each biquad filter will be applied one after another on the waveform before the Fourier transform is calculated. For example, using '-l l 5 500' will apply a lowpass filter with a Q value of 5, and a cutoff frequency of 500. The extra character 'l' or 'h' is for low pass and high pass respectively.\n"); 562 | printf("\t-c, --pitch\n\t\tA positive number that can be used to either shift the frequency up or down. Set this value greater than one to increase the pitch, and set it between 0 and 1 to decrease the pitch.\n"); 563 | printf("\n"); 564 | printf("Copyright 2018 Robert Elder Software Inc. available freely under MIT license.\n"); 565 | return 0; 566 | } 567 | 568 | 569 | if(load_input_file(argv, p.infile_argv_index, &infile_bytes, &infile_bytes_size)){ 570 | return 1; 571 | } 572 | 573 | header = (struct wav_file_header *)infile_bytes; 574 | 575 | if(p.verbose){ 576 | print_wav_header(header); 577 | } 578 | 579 | if(header->fmt_type != 1){ 580 | printf("Only PCM wav format is supported.\n"); 581 | return 1; 582 | } 583 | 584 | if(p.verbose){ 585 | printf("# file size is %u in buffer\n", infile_bytes_size); 586 | } 587 | 588 | end = infile_bytes + infile_bytes_size; 589 | data = find_wav_data(((char*)header) + 36, end, p.verbose); 590 | if(data == (char*)0){ 591 | printf("# Unable to find data section."); 592 | return 1; 593 | } 594 | rtn = analyze_audio_and_build_command(&p, header, data); 595 | free(infile_bytes); 596 | return rtn; 597 | } 598 | --------------------------------------------------------------------------------