├── .gitignore ├── BTT.h ├── LICENSE ├── README.md ├── demos ├── New_DFT.png ├── analysis_latency │ ├── MKAiff.c │ ├── MKAiff.h │ └── main.c ├── check_uninitialized_variables │ └── main.c ├── extract_tempo │ ├── 5_seconds.aiff │ └── test.c ├── extract_tempo_test │ ├── 5_seconds.aiff │ ├── 5_seconds.aiff.reapeaks │ ├── main.c │ └── test.aiff ├── getting_started │ └── main.c ├── iphone_example │ ├── TempoTest.xcodeproj │ │ ├── project.pbxproj │ │ ├── project.xcworkspace │ │ │ ├── contents.xcworkspacedata │ │ │ ├── xcshareddata │ │ │ │ └── IDEWorkspaceChecks.plist │ │ │ └── xcuserdata │ │ │ │ └── michaelkrzyzaniak.xcuserdatad │ │ │ │ └── UserInterfaceState.xcuserstate │ │ └── xcuserdata │ │ │ └── michaelkrzyzaniak.xcuserdatad │ │ │ └── xcschemes │ │ │ └── xcschememanagement.plist │ ├── TempoTest │ │ ├── 5_seconds.aiff │ │ ├── AppDelegate.h │ │ ├── AppDelegate.m │ │ ├── Assets.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ └── Contents.json │ │ │ └── Contents.json │ │ ├── Base.lproj │ │ │ ├── LaunchScreen.storyboard │ │ │ └── Main.storyboard │ │ ├── Beat-and-Tempo-Tracking │ │ │ ├── LICENSE │ │ │ └── README.md │ │ ├── Info.plist │ │ ├── SceneDelegate.h │ │ ├── SceneDelegate.m │ │ ├── ViewController.h │ │ ├── ViewController.m │ │ └── main.m │ ├── TempoTestTests │ │ ├── Info.plist │ │ └── TempoTestTests.m │ └── TempoTestUITests │ │ ├── Info.plist │ │ └── TempoTestUITests.m ├── offline │ ├── 3_min_90_bpm.wav │ ├── MKAiff.c │ ├── MKAiff.h │ ├── Timestamp.c │ ├── Timestamp.h │ └── main.c ├── online │ ├── AudioSuperclass.c │ ├── AudioSuperclass.h │ ├── Click.c │ ├── Click.h │ ├── Microphone.c │ ├── Microphone.h │ └── main.c └── online_print_tempo │ ├── AudioSuperclass.c │ ├── AudioSuperclass.h │ ├── Click.c │ ├── Click.h │ ├── Microphone.c │ ├── Microphone.h │ └── main.c └── src ├── BTT.c ├── DFT.c ├── DFT.c.old.txt ├── DFT.h ├── DFT.h.old ├── Filter.c ├── Filter.h ├── STFT.c ├── STFT.h ├── Statistics.c ├── Statistics.h ├── fastsin.c └── fastsin.h /.gitignore: -------------------------------------------------------------------------------- 1 | demos/offline/audio 2 | build/ 3 | a.out 4 | .DS_Store -------------------------------------------------------------------------------- /BTT.h: -------------------------------------------------------------------------------- 1 | /*---------------------------------------------------------------------- 2 | ____ _ _ 3 | | __ ) ___ __ _| |_ __ _ _ __ __| | 4 | | _ \ / _ \/ _` | __| / _` | '_ \ / _` | 5 | | |_) | __/ (_| | |_ | (_| | | | | (_| | 6 | |____/ \___|\__,_|\__| \__,_|_| |_|\__,_| 7 | 8 | _____ 9 | |_ _|__ _ __ ___ _ __ ___ 10 | | |/ _ \ '_ ` _ \| '_ \ / _ \ 11 | | | __/ | | | | | |_) | (_) | 12 | |_|\___|_| |_| |_| .__/ \___/ 13 | |_| 14 | _____ _ _ 15 | |_ _| __ __ _ ___| | _(_)_ __ __ _ 16 | | || '__/ _` |/ __| |/ / | '_ \ / _` | 17 | | || | | (_| | (__| <| | | | | (_| | 18 | |_||_| \__,_|\___|_|\_\_|_| |_|\__, | 19 | |___/ 20 | ------------------------------------------------------------------------ 21 | 22 | Made by Michael Krzyzaniak 23 | 24 | Version: 25 | 1.0 26 | 27 | ----------------------------------------------------------------------*/ 28 | #ifndef __BTT__ 29 | #define __BTT__ 1 30 | 31 | #if defined(__cplusplus) 32 | extern "C"{ 33 | #endif //(__cplusplus) 34 | 35 | #include "src/DFT.h" 36 | 37 | /*--------------------------------------------------------------------*/ 38 | typedef enum 39 | { 40 | BTT_COUNT_IN_TRACKING, 41 | BTT_ONSET_TRACKING, 42 | BTT_ONSET_AND_TEMPO_TRACKING, 43 | BTT_ONSET_AND_TEMPO_AND_BEAT_TRACKING, 44 | BTT_TEMPO_LOCKED_BEAT_TRACKING, 45 | BTT_METRONOME_MODE, 46 | BTT_NUM_TRACKING_MODES, 47 | }btt_tracking_mode_t; 48 | 49 | /*--------------------------------------------------------------------*/ 50 | #define BTT_SUGGESTED_SPECTRAL_FLUX_STFT_LEN 1024 // fft size 51 | #define BTT_SUGGESTED_SPECTRAL_FLUX_STFT_OVERLAP 8 // hop size will be len / overlap 52 | #define BTT_SUGGESTED_OSS_FILTER_ORDER 15 // this will delay the signal by about 7 samples 53 | #define BTT_SUGGESTED_OSS_LENGTH 1024 // enough to hold 3 seconds at 44.1kHz and hop of 128 54 | #define BTT_SUGGESTED_ONSET_THRESHOLD_N 1024 // size of moving average of oss to see if onset occured 55 | #define BTT_SUGGESTED_SAMPLE_RATE 44100 // audio sample rate 56 | #define BTT_SUGGESTED_CBSS_LENGTH 1024 // should be at least a little bigger than the slowest tempo lag in oss samples 57 | 58 | #define BTT_DEFAULT_ANALYSIS_LATENCY_ONSET_ADJUSTMENT 857 59 | #define BTT_DEFAULT_ANALYSIS_LATENCY_BEAT_ADJUSTMENT 1270 60 | 61 | /*--------------------------------------------------------------------*/ 62 | #define BTT_DEFAULT_MIN_TEMPO 50 // BPM 63 | #define BTT_DEFAULT_MAX_TEMPO 200 // BPM 64 | #define BTT_DEFAULT_SPECTRAL_COMPRESSION_GAMMA 0 // COMPRESSED(spectrum) = log(1+gamma|spectrum|) / log(1+gamma) 65 | #define BTT_DEFAULT_AUTOCORRELATION_EXPONENT 0.5 // for generalized autocorrelation 66 | #define BTT_DEFAULT_NUM_TEMPO_CANDIDATES 10 // 67 | #define BTT_DEFAULT_TRACKING_MODE BTT_ONSET_AND_TEMPO_AND_BEAT_TRACKING 68 | #define BTT_DEFAULT_OSS_FILTER_CUTOFF 10 // Hz 69 | #define BTT_DEFAULT_USE_AMP_NORMALIZATION 0 // false 70 | #define BTT_DEFAULT_ONSET_TREHSHOLD 0.1 // std devs above mean OSS signal 71 | #define BTT_DEFAULT_ONSET_TREHSHOLD_MIN 5.0 // raw flux value 72 | #define BTT_DEFAULT_NOISE_CANCELLATION_THRESHOLD -74 // dB per freq bin 73 | #define BTT_DEFAULT_LOG_GAUSSIAN_TEMPO_WEIGHT_MEAN 120 // supress harmonics by favoring tempos closer to 120 74 | #define BTT_DEFAULT_LOG_GAUSSIAN_TEMPO_WEIGHT_WIDTH 75 // oss samples starndard deviation 75 | #define BTT_DEFAULT_GAUSSIAN_TEMPO_HISTOGRAM_DECAY 0.999 // 76 | #define BTT_DEFAULT_GAUSSIAN_TEMPO_HISTOGRAM_WIDTH 5 // oss samples starndard deviation 77 | #define BTT_DEFAULT_CBSS_ALPHA 0.9 // 90% old, 10 percent new 78 | #define BTT_DEFAULT_CBSS_ETA 300 // width of gaussian window. Larger number is narrower window 79 | #define BTT_DEFAULT_BEAT_PREDICTION_ADJUSTMENT 10 // oss samples earlier than detected 80 | #define BTT_DEFAULT_PREDICTED_BEAT_TRIGGER_INDEX 20 // 81 | #define BTT_DEFAULT_PREDICTED_BEAT_GAUSSIAN_WIDTH 10 // oss samples 82 | #define BTT_DEFAULT_IGNORE_SPURIOUS_BEATS_DURATION 40 // percent of beat at current tempo 83 | #define BTT_DEFAULT_COUNT_IN_N 2 84 | 85 | #define BTT_DEFAULT_XCORR_NUM_PULSES 8 // 86 | #define BTT_DEFAULT_XCORR_PULSE_LOCATIONS {0, 1, 1.5, 2, 3, 4, 4.5, 6} 87 | #define BTT_DEFAULT_XCORR_PULSE_VALUES {2.0, 1.0, 0.5, 1.5, 1.5, 0.5, 0.5, 0.5} 88 | 89 | /*--------------------------------------------------------------------*/ 90 | typedef struct Opaque_BTT_Struct BTT; 91 | 92 | typedef void (*btt_onset_callback_t) (void* SELF, unsigned long long sample_time); 93 | typedef void (*btt_beat_callback_t) (void* SELF, unsigned long long sample_time); 94 | 95 | /*--------------------------------------------------------------------*/ 96 | /* if you use btt_new you are going to have to empirically determine the analysis_latency_adjustments using the utility in demos/analysis_latency/ 97 | the analysis latency is caused by complex interactions between filters, buffering, adaptive thresholds, and other things, and I couldn't 98 | find a closed-form expression that caputures it. If you use btt_new_default you will be fine, if not, you have to manually calculate it. 99 | */ 100 | BTT* btt_new (int spectral_flux_stft_len, int spectral_flux_stft_overlap, 101 | int oss_filter_order , int oss_length, 102 | int cbss_length , int onset_threshold_len, double sample_rate, 103 | int analysis_latency_onset_adjustment, int analysis_latency_beat_adjustment); 104 | BTT* btt_new_default (); 105 | BTT* btt_destroy (BTT* self); 106 | void btt_process (BTT* self, dft_sample_t* input, int num_samples); 107 | double btt_get_sample_rate (BTT* self); 108 | void btt_init (BTT* self); 109 | void btt_clear (BTT* self); 110 | void btt_init_tempo (BTT* self, double bpm /*0 to clear tempo*/); 111 | 112 | int btt_get_beat_period_audio_samples (BTT* self); 113 | double btt_get_tempo_bpm (BTT* self); 114 | double btt_get_tempo_certainty (BTT* self); 115 | void btt_set_count_in_n (BTT* self, int n); 116 | int btt_get_count_in_n (BTT* self); 117 | 118 | /* only valid in metronome mode */ 119 | void btt_set_metronome_bpm (BTT* self, double bpm); 120 | 121 | /* onset detection adjustments */ 122 | void btt_set_use_amplitude_normalization (BTT* self, int use); 123 | int btt_get_use_amplitude_normalization (BTT* self); 124 | void btt_set_spectral_compression_gamma (BTT* self, double gamma); 125 | double btt_get_spectral_compression_gamma (BTT* self); 126 | void btt_set_oss_filter_cutoff (BTT* self, double Hz); 127 | double btt_get_oss_filter_cutoff (BTT* self); 128 | void btt_set_onset_threshold (BTT* self, double num_std_devs); 129 | double btt_get_onset_threshold (BTT* self); 130 | void btt_set_onset_threshold_min (BTT* self, double value); 131 | double btt_get_onset_threshold_min (BTT* self); 132 | void btt_set_noise_cancellation_threshold (BTT* self, double dB /*negative*/); 133 | double btt_get_noise_cancellation_threshold (BTT* self); 134 | 135 | /* tempo tracking adjustments */ 136 | void btt_set_autocorrelation_exponent (BTT* self, double exponent); 137 | double btt_get_autocorrelation_exponent (BTT* self); 138 | void btt_set_min_tempo (BTT* self, double min_tempo); 139 | double btt_get_min_tempo (BTT* self); 140 | void btt_set_max_tempo (BTT* self, double max_tempo); 141 | double btt_get_max_tempo (BTT* self); 142 | void btt_set_num_tempo_candidates (BTT* self, int num_candidates); 143 | int btt_get_num_tempo_candidates (BTT* self); 144 | void btt_set_gaussian_tempo_histogram_decay (BTT* self, double coefficient); 145 | double btt_get_gaussian_tempo_histogram_decay (BTT* self); 146 | void btt_set_gaussian_tempo_histogram_width (BTT* self, double width); 147 | double btt_get_gaussian_tempo_histogram_width (BTT* self); 148 | void btt_set_log_gaussian_tempo_weight_mean (BTT* self, double bpm); 149 | double btt_get_log_gaussian_tempo_weight_mean (BTT* self); 150 | void btt_set_log_gaussian_tempo_weight_width(BTT* self, double bpm); 151 | double btt_get_log_gaussian_tempo_weight_width(BTT* self); 152 | 153 | /* beat tracking adjustments */ 154 | void btt_set_cbss_alpha (BTT* self, double alpha); 155 | double btt_get_cbss_alpha (BTT* self); 156 | void btt_set_cbss_eta (BTT* self, double eta); 157 | double btt_get_cbss_eta (BTT* self); 158 | void btt_set_beat_prediction_adjustment (BTT* self, int oss_samples_earlier); 159 | int btt_get_beat_prediction_adjustment (BTT* self); 160 | int btt_get_beat_prediction_adjustment_audio_samples (BTT* self); 161 | void btt_set_predicted_beat_trigger_index (BTT* self, int index); 162 | int btt_get_predicted_beat_trigger_index (BTT* self); 163 | void btt_set_predicted_beat_gaussian_width (BTT* self, double width); 164 | double btt_get_predicted_beat_gaussian_width (BTT* self); 165 | void btt_set_ignore_spurious_beats_duration (BTT* self, double percent_of_tempo); 166 | double btt_get_ignore_spurious_beats_duration (BTT* self); 167 | void btt_set_analysis_latency_onset_adjustment(BTT* self, int adjustment); 168 | int btt_get_analysis_latency_onset_adjustment(BTT* self); 169 | void btt_set_analysis_latency_beat_adjustment(BTT* self, int adjustment); 170 | int btt_get_analysis_latency_beat_adjustment(BTT* self); 171 | 172 | void btt_set_tracking_mode (BTT* self, btt_tracking_mode_t mode); 173 | btt_tracking_mode_t btt_get_tracking_mode (BTT* self); 174 | const char* btt_get_tracking_mode_string (BTT* self); 175 | void btt_set_onset_tracking_callback (BTT* self, btt_onset_callback_t callback, void* callback_self); 176 | btt_onset_callback_t btt_get_onset_tracking_callback (BTT* self, void** returned_callback_self); 177 | void btt_set_beat_tracking_callback (BTT* self, btt_beat_callback_t callback, void* callback_self); 178 | btt_beat_callback_t btt_get_beat_tracking_callback (BTT* self, void** returned_callback_self); 179 | 180 | #if defined(__cplusplus) 181 | } 182 | #endif //(__cplusplus) 183 | 184 | #endif // __BTT__ 185 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 michaelkrzyzaniak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /demos/New_DFT.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelkrzyzaniak/Beat-and-Tempo-Tracking/c039090f1af771092d95c3ffc402e557940f7384/demos/New_DFT.png -------------------------------------------------------------------------------- /demos/analysis_latency/main.c: -------------------------------------------------------------------------------- 1 | //gcc *.c ../../src/*.c 2 | 3 | //************************************** 4 | //the suggested onset adjustment is 857 5 | //the suggested beat adjustment is 1270 6 | //************************************** 7 | //************************************** 8 | //the onset error is -0.913972 9 | //the beat error is 50.123595 10 | //************************************** 11 | 12 | //************************************** 13 | //the onset error mean is -0.913972 14 | //the beat error mean is 50.546271 15 | //the onset error std dev is 5.753604 16 | //the beat error std dev is 280.149971 (6.4 millisecs) 17 | //************************************** 18 | 19 | #include "../../BTT.h" 20 | #include "../../src/Statistics.h" 21 | 22 | #include "MKAiff.h" 23 | 24 | #include 25 | 26 | #define SAMPLE_RATE 44100 27 | #define MIN_TEMPO 90 //inclusive 28 | #define MAX_TEMPO 180 //exclusive 29 | //#define MIN_TEMPO 82.6875 //inclusive once every 250 hops 30 | //#define MAX_TEMPO 82.6875 //exclusive 31 | //#define MIN_TEMPO 82 32 | //#define MAX_TEMPO 82 33 | #define NUM_TEMPO_SAMPLES 100 34 | #define ANALYSIS_LENGTH_SECONDS 120 35 | 36 | #define ANALYSIS_LENGTH_SAMPLES ((ANALYSIS_LENGTH_SECONDS) * (SAMPLE_RATE)) 37 | 38 | void onset_detected_callback(void* SELF, unsigned long long sample_time); 39 | void beat_detected_callback (void* SELF, unsigned long long sample_time); 40 | 41 | void get_analysis_latency_mean(double tempo, int onset_adjustment, int beat_adjustment, double* returned_onset_error, double* returned_beat_error); 42 | 43 | OnlineAverage* compute_mean_error(MKAiff* reference, MKAiff* test, int* returned_max, int* returned_min); 44 | int get_unit_impulse_locations_in_aiff(MKAiff* aiff, int* returned_locations, int returned_buffer_length); 45 | int count_unit_impulses_in_aiff(MKAiff* aiff); 46 | 47 | /*--------------------------------------------------------------------*/ 48 | int main(void) 49 | { 50 | int i; 51 | 52 | OnlineAverage* onset_adjustment = online_average_new(); 53 | OnlineAverage* beat_adjustment = online_average_new(); 54 | 55 | double onset_error, beat_error; 56 | 57 | if((onset_adjustment == NULL) || (beat_adjustment == NULL)) 58 | {fprintf(stderr, "allocate OnlineAverage\r\n"); return -1;} 59 | 60 | for(i=0; i test_impulse_locations[i]) 246 | break; 247 | 248 | if(reference_impulse_locations[j] <= test_impulse_locations[i]) 249 | continue; 250 | 251 | below = ((j-1)<0) ? 0 : j-1; 252 | int distance_above = reference_impulse_locations[j] - test_impulse_locations[i]; 253 | int distance_below = test_impulse_locations[i] - reference_impulse_locations[below]; 254 | 255 | double distance = (distance_below < distance_above) ? distance_below : -distance_above; 256 | 257 | 258 | if(distance > max) max = distance; 259 | if(distance < min) min = distance; 260 | online_average_update(result, distance); 261 | } 262 | *returned_max = max; 263 | *returned_min = min; 264 | return result; 265 | } 266 | -------------------------------------------------------------------------------- /demos/check_uninitialized_variables/main.c: -------------------------------------------------------------------------------- 1 | //gcc *.c ../../src/*.c 2 | 3 | #include "../../BTT.h" 4 | #include 5 | 6 | void onset_detected_callback(void* SELF, unsigned long long sample_time); 7 | void beat_detected_callback (void* SELF, unsigned long long sample_time); 8 | 9 | /*--------------------------------------------------------------------*/ 10 | int main(int argc, char* argv[]) 11 | { 12 | BTT* btt = btt_new_default(); 13 | if(btt == NULL){perror("unable to create btt object"); return -2;} 14 | 15 | fprintf(stderr, "btt_get_beat_period_audio_samples: %i\r\n", btt_get_beat_period_audio_samples(btt)); 16 | fprintf(stderr, "btt_get_tempo_bpm: %f\r\n", btt_get_tempo_bpm(btt)); 17 | fprintf(stderr, "btt_get_count_in_n: %i\r\n", btt_get_count_in_n(btt)); 18 | fprintf(stderr, "btt_get_use_amplitude_normalization: %i\r\n", btt_get_use_amplitude_normalization(btt)); 19 | fprintf(stderr, "btt_get_spectral_compression_gamma: %f\r\n", btt_get_spectral_compression_gamma(btt)); 20 | fprintf(stderr, "btt_get_oss_filter_cutoff: %f\r\n", btt_get_oss_filter_cutoff(btt)); 21 | fprintf(stderr, "btt_get_onset_threshold: %f\r\n", btt_get_onset_threshold(btt)); 22 | fprintf(stderr, "btt_get_onset_threshold_min: %f\r\n", btt_get_onset_threshold_min(btt)); 23 | fprintf(stderr, "btt_get_noise_cancellation_threshold: %f\r\n", btt_get_noise_cancellation_threshold(btt)); 24 | 25 | fprintf(stderr, "btt_get_autocorrelation_exponent: %f\r\n", btt_get_autocorrelation_exponent(btt)); 26 | fprintf(stderr, "btt_get_min_tempo: %f\r\n", btt_get_min_tempo(btt)); 27 | fprintf(stderr, "btt_get_max_tempo: %f\r\n", btt_get_max_tempo(btt)); 28 | 29 | fprintf(stderr, "btt_get_num_tempo_candidates: %i\r\n", btt_get_num_tempo_candidates(btt)); 30 | fprintf(stderr, "btt_get_gaussian_tempo_histogram_decay: %f\r\n", btt_get_gaussian_tempo_histogram_decay(btt)); 31 | fprintf(stderr, "btt_get_gaussian_tempo_histogram_width: %f\r\n", btt_get_gaussian_tempo_histogram_width(btt)); 32 | fprintf(stderr, "btt_get_log_gaussian_tempo_weight_mean: %f\r\n", btt_get_log_gaussian_tempo_weight_mean(btt)); 33 | fprintf(stderr, "btt_get_log_gaussian_tempo_weight_width: %f\r\n", btt_get_log_gaussian_tempo_weight_width(btt)); 34 | 35 | fprintf(stderr, "btt_get_cbss_alpha: %f\r\n", btt_get_cbss_alpha(btt)); 36 | fprintf(stderr, "btt_get_cbss_eta: %f\r\n", btt_get_cbss_eta(btt)); 37 | 38 | fprintf(stderr, "btt_get_beat_prediction_adjustment: %i\r\n", btt_get_beat_prediction_adjustment(btt)); 39 | fprintf(stderr, "btt_get_beat_prediction_adjustment_audio_samples: %i\r\n", btt_get_beat_prediction_adjustment_audio_samples(btt)); 40 | fprintf(stderr, "btt_get_predicted_beat_trigger_index: %i\r\n", btt_get_predicted_beat_trigger_index(btt)); 41 | 42 | fprintf(stderr, "btt_get_predicted_beat_gaussian_width: %f\r\n", btt_get_predicted_beat_gaussian_width(btt)); 43 | fprintf(stderr, "btt_get_ignore_spurious_beats_duration: %f\r\n", btt_get_ignore_spurious_beats_duration(btt)); 44 | 45 | fprintf(stderr, "btt_get_analysis_latency_onset_adjustment: %i\r\n", btt_get_analysis_latency_onset_adjustment(btt)); 46 | fprintf(stderr, "btt_get_analysis_latency_beat_adjustment: %i\r\n", btt_get_analysis_latency_beat_adjustment(btt)); 47 | 48 | fprintf(stderr, "btt_get_tracking_mode: %i\r\n", btt_get_tracking_mode(btt)); 49 | fprintf(stderr, "btt_get_tracking_mode_string: %s\r\n", btt_get_tracking_mode_string(btt)); 50 | 51 | btt = btt_destroy(btt); 52 | } 53 | 54 | /*--------------------------------------------------------------------*/ 55 | void onset_detected_callback(void* SELF, unsigned long long sample_time) 56 | { 57 | 58 | } 59 | 60 | /*--------------------------------------------------------------------*/ 61 | void beat_detected_callback (void* SELF, unsigned long long sample_time) 62 | { 63 | 64 | } 65 | -------------------------------------------------------------------------------- /demos/extract_tempo/5_seconds.aiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelkrzyzaniak/Beat-and-Tempo-Tracking/c039090f1af771092d95c3ffc402e557940f7384/demos/extract_tempo/5_seconds.aiff -------------------------------------------------------------------------------- /demos/extract_tempo/test.c: -------------------------------------------------------------------------------- 1 | //compile with: 2 | //gcc *.c ../../src/*.c ../offline/MKAiff.c 3 | 4 | #include "../../BTT.h" 5 | #include "../offline/MKAiff.h" 6 | 7 | #define AUDIO_BUFFER_SIZE 64 8 | 9 | /*--------------------------------------------------------------------*/ 10 | int main(int argc, char* argv[]) 11 | { 12 | if(argc < 2) 13 | { 14 | fprintf(stderr, "Please specify an aiff or wav file you would like to process.\r\n"); exit(-1); 15 | } 16 | 17 | /*open the audio file, make it mono, and check sample rate*/ 18 | MKAiff* aiff = aiffWithContentsOfFile(argv[1]); 19 | aiffMakeMono(aiff); 20 | double sample_rate = aiffSampleRate(aiff); 21 | if(sample_rate != BTT_SUGGESTED_SAMPLE_RATE) 22 | {fprintf(stderr, "Audio file should be %lf Hz but is %lf. Aborting.\r\n", (double)BTT_SUGGESTED_SAMPLE_RATE, sample_rate); exit(-1);} 23 | 24 | /*get an audio buffer*/ 25 | dft_sample_t* buffer = calloc(AUDIO_BUFFER_SIZE, sizeof(*buffer)); 26 | if(buffer == NULL){perror("unable to allocate buffer"); exit(-3);} 27 | 28 | /*make beat tracking object*/ 29 | BTT* btt = btt_new_default(); 30 | if(btt == NULL){perror("unable to create btt object"); exit(-2);} 31 | 32 | /*This will save a little CPU since we jst want the tempo estimate and don't care about the exact location of each beat*/ 33 | 34 | btt_set_tracking_mode(btt, BTT_ONSET_AND_TEMPO_TRACKING); 35 | 36 | /* don't decay the histogram just assume constant tempo and accumulate estimates over the whole song */ 37 | //btt_set_gaussian_tempo_histogram_decay(btt, 1); 38 | 39 | for(;;) 40 | { 41 | int num_samples = aiffReadFloatingPointSamplesAtPlayhead(aiff, buffer, AUDIO_BUFFER_SIZE, aiffYes); 42 | btt_process(btt, buffer, num_samples); 43 | if(num_samples < AUDIO_BUFFER_SIZE) break; 44 | } 45 | 46 | double tempo = btt_get_tempo_bpm(btt); 47 | fprintf(stderr, "Tempo of %s is %02f\r\n", argv[1], tempo); 48 | 49 | /*cleanup*/ 50 | btt_destroy(btt); 51 | aiffDestroy(aiff); 52 | } 53 | 54 | -------------------------------------------------------------------------------- /demos/extract_tempo_test/5_seconds.aiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelkrzyzaniak/Beat-and-Tempo-Tracking/c039090f1af771092d95c3ffc402e557940f7384/demos/extract_tempo_test/5_seconds.aiff -------------------------------------------------------------------------------- /demos/extract_tempo_test/5_seconds.aiff.reapeaks: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelkrzyzaniak/Beat-and-Tempo-Tracking/c039090f1af771092d95c3ffc402e557940f7384/demos/extract_tempo_test/5_seconds.aiff.reapeaks -------------------------------------------------------------------------------- /demos/extract_tempo_test/main.c: -------------------------------------------------------------------------------- 1 | //compile with: 2 | //gcc *.c ../../src/*.c ../offline/MKAiff.c 3 | 4 | #include "../../BTT.h" 5 | #include "../offline/MKAiff.h" 6 | 7 | #define AUDIO_BUFFER_SIZE 64 8 | 9 | double calcTempo(char *fname); 10 | 11 | /*--------------------------------------------------------------------*/ 12 | int main(int argc, char* argv[]) 13 | { 14 | if(argc < 2) 15 | { 16 | fprintf(stderr, "Please specify an aiff or wav file you would like to process.\r\n"); exit(-1); 17 | } 18 | calcTempo(argv[1]); 19 | } 20 | 21 | double calcTempo(char *fname) 22 | { 23 | long fpos=0; 24 | int plen; 25 | double bpm = 0; 26 | 27 | FILE *fp = fopen(fname, "rb"); 28 | if(!fp) return -1; 29 | 30 | long packetsize = 1024; 31 | long outpos = 0; 32 | 33 | int bStereo = 0;//IsStereo(fp); 34 | long sampleRate = 44100;//getSampleRate(fp); 35 | 36 | int frame_size = bStereo ? 4 : 2; 37 | 38 | short *packet = (short *)malloc(frame_size * packetsize); 39 | if(!packet) 40 | { 41 | fclose(fp); 42 | return -1; 43 | } 44 | 45 | float *buffer = (float *)malloc(sizeof(float) * packetsize); 46 | 47 | BTT* btt = btt_new(BTT_SUGGESTED_SPECTRAL_FLUX_STFT_LEN, 48 | BTT_SUGGESTED_SPECTRAL_FLUX_STFT_OVERLAP, 49 | BTT_SUGGESTED_OSS_FILTER_ORDER, 50 | BTT_SUGGESTED_OSS_LENGTH, 51 | BTT_SUGGESTED_ONSET_THRESHOLD_N, 52 | BTT_SUGGESTED_CBSS_LENGTH, 53 | sampleRate, 54 | BTT_DEFAULT_ANALYSIS_LATENCY_ONSET_ADJUSTMENT, 55 | BTT_DEFAULT_ANALYSIS_LATENCY_BEAT_ADJUSTMENT 56 | ); 57 | 58 | btt_set_gaussian_tempo_histogram_decay(btt, 1); 59 | 60 | btt_set_tracking_mode(btt, BTT_ONSET_AND_TEMPO_TRACKING); 61 | 62 | float mult = 1.0f / 32768.0f; 63 | 64 | fseek(fp, 256, SEEK_SET); 65 | 66 | while(1) 67 | { 68 | plen = (int)fread(packet, frame_size, packetsize, fp); 69 | 70 | outpos = 0; 71 | 72 | if(bStereo) 73 | { 74 | for(unsigned int i=0; i 4 | #include 5 | #include "../../BTT.h" 6 | 7 | /*--------------------------------------------------------------------*/ 8 | void onset_detected_callback(void* SELF, unsigned long long sample_time) 9 | { 10 | fprintf(stderr, "Yay, an onset was detected %llu samples into the audio stream!\r\n", sample_time); 11 | } 12 | 13 | /*--------------------------------------------------------------------*/ 14 | void beat_detected_callback (void* SELF, unsigned long long sample_time) 15 | { 16 | fprintf(stderr, "Yay, a beat was detected %llu samples into the audio stream!\r\n", sample_time); 17 | } 18 | 19 | /*--------------------------------------------------------------------*/ 20 | int main(void) 21 | { 22 | BTT* btt = btt_new_default(); 23 | 24 | /* btt_new_default is the same as: 25 | BTT* btt = btt_new(BTT_SUGGESTED_SPECTRAL_FLUX_STFT_LEN, 26 | BTT_SUGGESTED_SPECTRAL_FLUX_STFT_OVERLAP, 27 | BTT_SUGGESTED_OSS_FILTER_ORDER, 28 | BTT_SUGGESTED_OSS_LENGTH, 29 | BTT_SUGGESTED_ONSET_THRESHOLD_N, 30 | BTT_SUGGESTED_CBSS_LENGTH, 31 | BTT_SUGGESTED_SAMPLE_RATE, 32 | BTT_DEFAULT_ANALYSIS_LATENCY_ONSET_ADJUSTMENT, 33 | BTT_DEFAULT_ANALYSIS_LATENCY_BEAT_ADJUSTMENT 34 | ); 35 | */ 36 | 37 | if(btt == NULL){perror("unable to create btt object"); exit(-2);} 38 | 39 | /* specify which functions should recieve notificaions */ 40 | btt_set_onset_tracking_callback (btt, onset_detected_callback, NULL); 41 | btt_set_beat_tracking_callback (btt, beat_detected_callback , NULL); 42 | 43 | /* 44 | * it dosen't matter how big your buffers are, 45 | * it isn't related to the underlying analysis. 46 | */ 47 | int buffer_size = 64; 48 | dft_sample_t buffer[buffer_size]; 49 | 50 | for(;;) 51 | { 52 | /* Fill the buffer with your audio samples here. 53 | * The audio should be mono and the sample rate 54 | * by default is expected to be 44100 Hz. 55 | * You chan change it with the last 56 | * argument of btt_new, but the performance 57 | * of the algorithm may suffer. 58 | */ 59 | btt_process(btt, buffer, buffer_size); 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /demos/iphone_example/TempoTest.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /demos/iphone_example/TempoTest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /demos/iphone_example/TempoTest.xcodeproj/project.xcworkspace/xcuserdata/michaelkrzyzaniak.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelkrzyzaniak/Beat-and-Tempo-Tracking/c039090f1af771092d95c3ffc402e557940f7384/demos/iphone_example/TempoTest.xcodeproj/project.xcworkspace/xcuserdata/michaelkrzyzaniak.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /demos/iphone_example/TempoTest.xcodeproj/xcuserdata/michaelkrzyzaniak.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | TempoTest.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /demos/iphone_example/TempoTest/5_seconds.aiff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelkrzyzaniak/Beat-and-Tempo-Tracking/c039090f1af771092d95c3ffc402e557940f7384/demos/iphone_example/TempoTest/5_seconds.aiff -------------------------------------------------------------------------------- /demos/iphone_example/TempoTest/AppDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.h 3 | // TempoTest 4 | // 5 | // Created by Michael Krzyzaniak on 9/18/21. 6 | // Copyright © 2021 Michael Krzyzaniak. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface AppDelegate : UIResponder 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /demos/iphone_example/TempoTest/AppDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // AppDelegate.m 3 | // TempoTest 4 | // 5 | // Created by Michael Krzyzaniak on 9/18/21. 6 | // Copyright © 2021 Michael Krzyzaniak. All rights reserved. 7 | // 8 | 9 | #import "AppDelegate.h" 10 | 11 | @interface AppDelegate () 12 | 13 | @end 14 | 15 | @implementation AppDelegate 16 | 17 | 18 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions { 19 | // Override point for customization after application launch. 20 | return YES; 21 | } 22 | 23 | 24 | #pragma mark - UISceneSession lifecycle 25 | 26 | 27 | - (UISceneConfiguration *)application:(UIApplication *)application configurationForConnectingSceneSession:(UISceneSession *)connectingSceneSession options:(UISceneConnectionOptions *)options { 28 | // Called when a new scene session is being created. 29 | // Use this method to select a configuration to create the new scene with. 30 | return [[UISceneConfiguration alloc] initWithName:@"Default Configuration" sessionRole:connectingSceneSession.role]; 31 | } 32 | 33 | 34 | - (void)application:(UIApplication *)application didDiscardSceneSessions:(NSSet *)sceneSessions { 35 | // Called when the user discards a scene session. 36 | // If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions. 37 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return. 38 | } 39 | 40 | 41 | @end 42 | -------------------------------------------------------------------------------- /demos/iphone_example/TempoTest/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /demos/iphone_example/TempoTest/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /demos/iphone_example/TempoTest/Base.lproj/LaunchScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | -------------------------------------------------------------------------------- /demos/iphone_example/TempoTest/Base.lproj/Main.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /demos/iphone_example/TempoTest/Beat-and-Tempo-Tracking/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 michaelkrzyzaniak 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /demos/iphone_example/TempoTest/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | UISceneConfigurations 28 | 29 | UIWindowSceneSessionRoleApplication 30 | 31 | 32 | UISceneConfigurationName 33 | Default Configuration 34 | UISceneDelegateClassName 35 | SceneDelegate 36 | UISceneStoryboardFile 37 | Main 38 | 39 | 40 | 41 | 42 | UILaunchStoryboardName 43 | LaunchScreen 44 | UIMainStoryboardFile 45 | Main 46 | UIRequiredDeviceCapabilities 47 | 48 | armv7 49 | 50 | UISupportedInterfaceOrientations 51 | 52 | UIInterfaceOrientationPortrait 53 | UIInterfaceOrientationLandscapeLeft 54 | UIInterfaceOrientationLandscapeRight 55 | 56 | UISupportedInterfaceOrientations~ipad 57 | 58 | UIInterfaceOrientationPortrait 59 | UIInterfaceOrientationPortraitUpsideDown 60 | UIInterfaceOrientationLandscapeLeft 61 | UIInterfaceOrientationLandscapeRight 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /demos/iphone_example/TempoTest/SceneDelegate.h: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.h 3 | // TempoTest 4 | // 5 | // Created by Michael Krzyzaniak on 9/18/21. 6 | // Copyright © 2021 Michael Krzyzaniak. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface SceneDelegate : UIResponder 12 | 13 | @property (strong, nonatomic) UIWindow * window; 14 | 15 | @end 16 | 17 | -------------------------------------------------------------------------------- /demos/iphone_example/TempoTest/SceneDelegate.m: -------------------------------------------------------------------------------- 1 | // 2 | // SceneDelegate.m 3 | // TempoTest 4 | // 5 | // Created by Michael Krzyzaniak on 9/18/21. 6 | // Copyright © 2021 Michael Krzyzaniak. All rights reserved. 7 | // 8 | 9 | #import "SceneDelegate.h" 10 | 11 | @interface SceneDelegate () 12 | 13 | @end 14 | 15 | @implementation SceneDelegate 16 | 17 | 18 | - (void)scene:(UIScene *)scene willConnectToSession:(UISceneSession *)session options:(UISceneConnectionOptions *)connectionOptions { 19 | // Use this method to optionally configure and attach the UIWindow `window` to the provided UIWindowScene `scene`. 20 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene. 21 | // This delegate does not imply the connecting scene or session are new (see `application:configurationForConnectingSceneSession` instead). 22 | } 23 | 24 | 25 | - (void)sceneDidDisconnect:(UIScene *)scene { 26 | // Called as the scene is being released by the system. 27 | // This occurs shortly after the scene enters the background, or when its session is discarded. 28 | // Release any resources associated with this scene that can be re-created the next time the scene connects. 29 | // The scene may re-connect later, as its session was not neccessarily discarded (see `application:didDiscardSceneSessions` instead). 30 | } 31 | 32 | 33 | - (void)sceneDidBecomeActive:(UIScene *)scene { 34 | // Called when the scene has moved from an inactive state to an active state. 35 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive. 36 | } 37 | 38 | 39 | - (void)sceneWillResignActive:(UIScene *)scene { 40 | // Called when the scene will move from an active state to an inactive state. 41 | // This may occur due to temporary interruptions (ex. an incoming phone call). 42 | } 43 | 44 | 45 | - (void)sceneWillEnterForeground:(UIScene *)scene { 46 | // Called as the scene transitions from the background to the foreground. 47 | // Use this method to undo the changes made on entering the background. 48 | } 49 | 50 | 51 | - (void)sceneDidEnterBackground:(UIScene *)scene { 52 | // Called as the scene transitions from the foreground to the background. 53 | // Use this method to save data, release shared resources, and store enough scene-specific state information 54 | // to restore the scene back to its current state. 55 | } 56 | 57 | 58 | @end 59 | -------------------------------------------------------------------------------- /demos/iphone_example/TempoTest/ViewController.h: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.h 3 | // TempoTest 4 | // 5 | // Created by Michael Krzyzaniak on 9/18/21. 6 | // Copyright © 2021 Michael Krzyzaniak. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface ViewController : UIViewController 12 | 13 | 14 | @end 15 | 16 | -------------------------------------------------------------------------------- /demos/iphone_example/TempoTest/ViewController.m: -------------------------------------------------------------------------------- 1 | // 2 | // ViewController.m 3 | // TempoTest 4 | // 5 | // Created by Michael Krzyzaniak on 9/18/21. 6 | // Copyright © 2021 Michael Krzyzaniak. All rights reserved. 7 | // 8 | 9 | #import "ViewController.h" 10 | 11 | #import "Beat-and-Tempo-Tracking/BTT.h" 12 | 13 | @interface ViewController () 14 | 15 | @end 16 | 17 | @implementation ViewController 18 | 19 | - (void)viewDidLoad { 20 | [super viewDidLoad]; 21 | // Do any additional setup after loading the view. 22 | calcTempo("/Users/michaelkrzyzaniak/Documents/TempoTest/TempoTest/5_seconds.aiff"); 23 | } 24 | 25 | double calcTempo(char *fname) 26 | { 27 | long fpos=0; 28 | int plen; 29 | double bpm = 0; 30 | 31 | NSLog(@"HERE1"); 32 | 33 | FILE *fp = fopen(fname, "rb"); 34 | if(!fp) return -1; 35 | 36 | NSLog(@"HERE2"); 37 | 38 | long packetsize = 1024; 39 | long outpos = 0; 40 | 41 | int bStereo = 0;//IsStereo(fp); 42 | long sampleRate = 44100;//getSampleRate(fp); 43 | 44 | int frame_size = bStereo ? 4 : 2; 45 | 46 | short *packet = (short *)malloc(frame_size * packetsize); 47 | if(!packet) 48 | { 49 | fclose(fp); 50 | return -1; 51 | } 52 | 53 | float *buffer = (float *)malloc(sizeof(float) * packetsize); 54 | 55 | BTT* btt = btt_new(BTT_SUGGESTED_SPECTRAL_FLUX_STFT_LEN, 56 | BTT_SUGGESTED_SPECTRAL_FLUX_STFT_OVERLAP, 57 | BTT_SUGGESTED_OSS_FILTER_ORDER, 58 | BTT_SUGGESTED_OSS_LENGTH, 59 | BTT_SUGGESTED_ONSET_THRESHOLD_N, 60 | BTT_SUGGESTED_CBSS_LENGTH, 61 | sampleRate, 62 | BTT_DEFAULT_ANALYSIS_LATENCY_ONSET_ADJUSTMENT, 63 | BTT_DEFAULT_ANALYSIS_LATENCY_BEAT_ADJUSTMENT 64 | ); 65 | 66 | btt_set_gaussian_tempo_histogram_decay(btt, 1); 67 | 68 | btt_set_tracking_mode(btt, BTT_ONSET_AND_TEMPO_TRACKING); 69 | 70 | float mult = 1.0f / 32768.0f; 71 | 72 | fseek(fp, 256, SEEK_SET); 73 | 74 | while(1) 75 | { 76 | plen = (int)fread(packet, frame_size, packetsize, fp); 77 | 78 | outpos = 0; 79 | 80 | if(bStereo) 81 | { 82 | for(unsigned int i=0; i 10 | #import "AppDelegate.h" 11 | 12 | int main(int argc, char * argv[]) { 13 | NSString * appDelegateClassName; 14 | @autoreleasepool { 15 | // Setup code that might create autoreleased objects goes here. 16 | appDelegateClassName = NSStringFromClass([AppDelegate class]); 17 | } 18 | return UIApplicationMain(argc, argv, nil, appDelegateClassName); 19 | } 20 | -------------------------------------------------------------------------------- /demos/iphone_example/TempoTestTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /demos/iphone_example/TempoTestTests/TempoTestTests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TempoTestTests.m 3 | // TempoTestTests 4 | // 5 | // Created by Michael Krzyzaniak on 9/18/21. 6 | // Copyright © 2021 Michael Krzyzaniak. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TempoTestTests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation TempoTestTests 16 | 17 | - (void)setUp { 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | } 20 | 21 | - (void)tearDown { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | - (void)testExample { 26 | // This is an example of a functional test case. 27 | // Use XCTAssert and related functions to verify your tests produce the correct results. 28 | } 29 | 30 | - (void)testPerformanceExample { 31 | // This is an example of a performance test case. 32 | [self measureBlock:^{ 33 | // Put the code you want to measure the time of here. 34 | }]; 35 | } 36 | 37 | @end 38 | -------------------------------------------------------------------------------- /demos/iphone_example/TempoTestUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /demos/iphone_example/TempoTestUITests/TempoTestUITests.m: -------------------------------------------------------------------------------- 1 | // 2 | // TempoTestUITests.m 3 | // TempoTestUITests 4 | // 5 | // Created by Michael Krzyzaniak on 9/18/21. 6 | // Copyright © 2021 Michael Krzyzaniak. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface TempoTestUITests : XCTestCase 12 | 13 | @end 14 | 15 | @implementation TempoTestUITests 16 | 17 | - (void)setUp { 18 | // Put setup code here. This method is called before the invocation of each test method in the class. 19 | 20 | // In UI tests it is usually best to stop immediately when a failure occurs. 21 | self.continueAfterFailure = NO; 22 | 23 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 24 | } 25 | 26 | - (void)tearDown { 27 | // Put teardown code here. This method is called after the invocation of each test method in the class. 28 | } 29 | 30 | - (void)testExample { 31 | // UI tests must launch the application that they test. 32 | XCUIApplication *app = [[XCUIApplication alloc] init]; 33 | [app launch]; 34 | 35 | // Use recording to get started writing UI tests. 36 | // Use XCTAssert and related functions to verify your tests produce the correct results. 37 | } 38 | 39 | - (void)testLaunchPerformance { 40 | if (@available(macOS 10.15, iOS 13.0, tvOS 13.0, *)) { 41 | // This measures how long it takes to launch your application. 42 | [self measureWithMetrics:@[XCTOSSignpostMetric.applicationLaunchMetric] block:^{ 43 | [[[XCUIApplication alloc] init] launch]; 44 | }]; 45 | } 46 | } 47 | 48 | @end 49 | -------------------------------------------------------------------------------- /demos/offline/3_min_90_bpm.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/michaelkrzyzaniak/Beat-and-Tempo-Tracking/c039090f1af771092d95c3ffc402e557940f7384/demos/offline/3_min_90_bpm.wav -------------------------------------------------------------------------------- /demos/offline/Timestamp.c: -------------------------------------------------------------------------------- 1 | #include "Timestamp.h" 2 | 3 | 4 | /*----------------------------------------------------------*/ 5 | #if defined __APPLE__ 6 | 7 | #include 8 | 9 | timestamp_microsecs_t timestamp_get_current_time() 10 | { 11 | static mach_timebase_info_data_t s_timebase_info; 12 | 13 | //could move this elsewhere? 14 | if (s_timebase_info.denom == 0) 15 | (void) mach_timebase_info(&s_timebase_info); 16 | 17 | // mach_absolute_time() returns nanoseonds, 18 | return (timestamp_microsecs_t)((mach_absolute_time() * s_timebase_info.numer) / (1000 * s_timebase_info.denom)); 19 | } 20 | 21 | 22 | /*----------------------------------------------------------*/ 23 | #elif defined __linux__ 24 | #include 25 | timestamp_microsecs_t timestamp_get_current_time() 26 | { 27 | timestamp_microsecs_t result; 28 | struct timeval t; 29 | gettimeofday(&t, NULL); 30 | 31 | result = (t.tv_sec - 1428738298) * 1000000; 32 | result += t.tv_usec; 33 | 34 | return result; 35 | } 36 | 37 | #endif -------------------------------------------------------------------------------- /demos/offline/Timestamp.h: -------------------------------------------------------------------------------- 1 | #ifndef __MK_TIMESTAMP__ 2 | #define __MK_TIMESTAMP__ 3 | 4 | 5 | #if defined(__cplusplus) 6 | extern "C"{ 7 | #endif //(__cplusplus) 8 | 9 | #include 10 | 11 | typedef uint64_t timestamp_microsecs_t; 12 | timestamp_microsecs_t timestamp_get_current_time(); 13 | 14 | #if defined(__cplusplus) 15 | } 16 | #endif //(__cplusplus) 17 | 18 | #endif //__MK_TIMESTAMP__ -------------------------------------------------------------------------------- /demos/offline/main.c: -------------------------------------------------------------------------------- 1 | //gcc *.c ../../src/*.c 2 | 3 | #include "../../BTT.h" 4 | #include "MKAiff.h" 5 | #include "Timestamp.h" 6 | 7 | #define AUDIO_BUFFER_SIZE 64 8 | 9 | void onset_detected_callback(void* SELF, unsigned long long sample_time); 10 | void beat_detected_callback (void* SELF, unsigned long long sample_time); 11 | 12 | /*--------------------------------------------------------------------*/ 13 | int main(int argc, char* argv[]) 14 | { 15 | /* generate a click track; 16 | 17 | MKAiff* click = aiffWithDurationInSeconds(1, 44100, 16, 60); 18 | aiffAppendSilenceInSeconds(click, 60); 19 | float sample = 0.5; 20 | int i; 21 | for(i=0; i<120; i++) 22 | { 23 | aiffSetPlayheadToFrames(click, 22050 * i); 24 | aiffAddFloatingPointSamplesAtPlayhead(click, &sample, 1, aiffFloatSampleType, aiffYes); 25 | } 26 | aiffSaveWithFilename(click, "click.aiff"); 27 | return 0; 28 | */ 29 | if(argc < 2) 30 | {fprintf(stderr, "Please specify an aiff or wav file you would like to process.\r\n"); exit(-1);} 31 | 32 | MKAiff* aiff = aiffWithContentsOfFile(argv[1]); 33 | if(aiff == NULL){perror("unable to open audio file"); exit(-1);} 34 | aiffMakeMono(aiff); 35 | float secs = aiffDurationInSeconds(aiff); 36 | double sample_rate = aiffSampleRate(aiff); 37 | if(sample_rate != BTT_SUGGESTED_SAMPLE_RATE) 38 | {fprintf(stderr, "Audio file should be %lf Hz but is %lf. Aborting.\r\n", (double)BTT_SUGGESTED_SAMPLE_RATE, sample_rate); exit(-1);} 39 | 40 | BTT* btt = btt_new_default(); 41 | if(btt == NULL){perror("unable to create btt object"); exit(-2);} 42 | 43 | MKAiff* onset_aiff = aiffWithDurationInSeconds(1, sample_rate, 16, secs+1); 44 | if(onset_aiff == NULL){perror("unable to create onset_aiff obejct"); exit(-1);} 45 | 46 | MKAiff* beat_aiff = aiffWithDurationInSeconds(1, sample_rate, 16, secs+1); 47 | if(beat_aiff == NULL){perror("unable to create beat_aiff obejct"); exit(-1);} 48 | 49 | aiffAppendSilenceInSeconds(onset_aiff, secs); 50 | aiffAppendSilenceInSeconds(beat_aiff , secs); 51 | 52 | btt_set_onset_tracking_callback (btt, onset_detected_callback, onset_aiff); 53 | btt_set_beat_tracking_callback (btt, beat_detected_callback , beat_aiff); 54 | 55 | dft_sample_t* buffer = calloc(AUDIO_BUFFER_SIZE, sizeof(*buffer)); 56 | if(buffer == NULL){perror("unable to allocate buffer"); exit(-3);} 57 | 58 | timestamp_microsecs_t start = timestamp_get_current_time(); 59 | for(;;) 60 | { 61 | int num_samples = aiffReadFloatingPointSamplesAtPlayhead(aiff, buffer, AUDIO_BUFFER_SIZE, aiffYes); 62 | btt_process(btt, buffer, num_samples); 63 | if(num_samples < AUDIO_BUFFER_SIZE) break; 64 | } 65 | timestamp_microsecs_t end = timestamp_get_current_time(); 66 | double process_time = (end-start)/(double)1000000; 67 | 68 | aiffSaveWithFilename(onset_aiff, "onsets.aiff"); 69 | aiffSaveWithFilename(beat_aiff , "beats.aiff"); 70 | 71 | fprintf(stderr, "Done. %f secs to process a %f sec audio file: %f%% realtime\r\n", process_time, secs, 100*(process_time/secs)); 72 | } 73 | 74 | /*--------------------------------------------------------------------*/ 75 | /* store each onset as a single click in an AIFF file so we can compare it to the original */ 76 | void onset_detected_callback(void* SELF, unsigned long long sample_time) 77 | { 78 | MKAiff* onset_aiff = (MKAiff*) SELF; 79 | aiffSetPlayheadToSamples (onset_aiff, sample_time); 80 | float click = 1; 81 | aiffAddFloatingPointSamplesAtPlayhead(onset_aiff, &click, 1, aiffFloatSampleType, aiffYes); 82 | } 83 | 84 | /*--------------------------------------------------------------------*/ 85 | /* store each beat as a single click in an AIFF file so we can compare it to the original */ 86 | void beat_detected_callback (void* SELF, unsigned long long sample_time) 87 | { 88 | MKAiff* beat_aiff = (MKAiff*) SELF; 89 | aiffSetPlayheadToSamples (beat_aiff, sample_time); 90 | float click = 1; 91 | aiffAddFloatingPointSamplesAtPlayhead(beat_aiff, &click, 1, aiffFloatSampleType, aiffYes); 92 | } 93 | -------------------------------------------------------------------------------- /demos/online/AudioSuperclass.c: -------------------------------------------------------------------------------- 1 | /* 2 | * AudioSuperclass.c 3 | * Root class for audio synthesizers, for OSX or Linux 4 | * 5 | * Made by Michael Krzyzaniak at Arizona State University's 6 | * School of Arts, Media + Engineering in Spring of 2013 7 | * mkrzyzan@asu.edu 8 | */ 9 | 10 | 11 | 12 | //#define RECORD_MODE 1 13 | 14 | 15 | #include 16 | #include 17 | #include "AudioSuperclass.h" 18 | 19 | const double AU_TWO_PI_OVER_SAMPLE_RATE = SIN_TWO_PI / AU_SAMPLE_RATE; 20 | 21 | #if defined __APPLE__ 22 | void auAudioOutputCallback(void*, AudioQueueRef, AudioQueueBufferRef); 23 | void auAudioInputCallback (void*, AudioQueueRef, AudioQueueBufferRef, const AudioTimeStamp*, UInt32, const AudioStreamPacketDescription*); 24 | 25 | #elif defined __linux__ 26 | void* auAudioCallback(void* SELF); 27 | #endif 28 | 29 | 30 | /*OpaqueAudioStruct----------------------------------------*/ 31 | struct OpaqueAudioStruct 32 | { 33 | AUDIO_GUTS; 34 | }; 35 | 36 | /*auNew---------------------------------------------------*/ 37 | Audio* auAlloc(int sizeofstarself, auAudioCallback_t callback, BOOL isOutput, unsigned numChannels) 38 | { 39 | Audio* self = (Audio*)calloc(1, sizeofstarself); 40 | 41 | if(self != NULL) 42 | { 43 | self->isOutput = isOutput; 44 | self->isPlaying = NO; 45 | self->audioCallback = callback; 46 | self->numChannels = numChannels; 47 | self->targetMasterVolume = 1; 48 | self->destroy = auDestroy; 49 | auSetMasterVolumeSmoothing(self, 0.9999); 50 | 51 | #if defined __APPLE__ 52 | int i; 53 | OSStatus error; 54 | 55 | self->dataFormat.mSampleRate = AU_SAMPLE_RATE; 56 | self->dataFormat.mBytesPerPacket = 4 * numChannels ; 57 | self->dataFormat.mFramesPerPacket = 1 ; 58 | self->dataFormat.mBytesPerFrame = 4 * numChannels ; 59 | self->dataFormat.mBitsPerChannel = 32 ; 60 | self->dataFormat.mChannelsPerFrame = numChannels ; 61 | self->dataFormat.mFormatID = kAudioFormatLinearPCM; 62 | //self->dataFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; 63 | self->dataFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat; 64 | 65 | if(isOutput) 66 | error = AudioQueueNewOutput(&(self->dataFormat), auAudioOutputCallback, self, NULL, NULL, 0, &(self->queue)); 67 | else 68 | error = AudioQueueNewInput (&(self->dataFormat), auAudioInputCallback, self, NULL, NULL, 0, &(self->queue)); 69 | if(error) 70 | { 71 | fprintf(stderr, "Audio.c: unable to allocate Audio queue\n"); 72 | return auDestroy(self); 73 | } 74 | else //(!error) 75 | { 76 | unsigned bufferNumBytes = AU_BUFFER_NUM_FRAMES * numChannels * sizeof(auSample_t); 77 | for(i=0; iqueue, bufferNumBytes, &((self->buffers)[i])); 80 | if(error) 81 | { 82 | self = auDestroy(self); 83 | fprintf(stderr, "Audio.c: allocate buffer error\n"); 84 | break; 85 | } 86 | } 87 | } 88 | 89 | #elif defined __linux__ 90 | int error = 0; 91 | 92 | snd_pcm_hw_params_t *hardwareParameters; 93 | snd_pcm_hw_params_alloca(&hardwareParameters); 94 | 95 | //untested for input stream... 96 | const char* name = (isOutput) ? AU_SPEAKER_DEVICE_NAME : AU_MIC_DEVICE_NAME; 97 | unsigned direction = (isOutput) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE; 98 | 99 | error = snd_pcm_open(&(self->device), name, direction, 0); 100 | if(error < 0) fprintf(stderr, "Audio.c: Unable to open speaker device %s: %s\n", AU_SPEAKER_DEVICE_NAME, snd_strerror(error)); 101 | 102 | if(error >= 0) 103 | { 104 | error = snd_pcm_hw_params_any(self->device, hardwareParameters); 105 | if(error < 0) fprintf(stderr, "Audio.c: Unable to get a generic hardware configuration: %s\n", snd_strerror(error)); 106 | } 107 | if(error >= 0) 108 | { 109 | error = snd_pcm_hw_params_set_access(self->device, hardwareParameters, SND_PCM_ACCESS_RW_INTERLEAVED); 110 | if(error < 0) fprintf(stderr, "Audio.c: Device does not support interleaved audio access: %s\n", snd_strerror(error)); 111 | } 112 | if(error >= 0) 113 | { 114 | error = snd_pcm_hw_params_set_format(self->device, hardwareParameters, /*SND_PCM_FORMAT_S16*/ SND_PCM_FORMAT_FLOAT) ; 115 | if(error < 0) fprintf(stderr, "Audio.c: Unable to set sample format: %s\n", snd_strerror(error)); 116 | } 117 | if(error >= 0) 118 | { 119 | //self->numChannels = AU_NUM_CHANNELS; 120 | error = snd_pcm_hw_params_set_channels_near(self->device, hardwareParameters, &self->numChannels); 121 | if(error < 0) fprintf(stderr, "Audio.c: unable to set the number of channels to %i: %s\n", self->numChannels, snd_strerror(error)); 122 | else if(self->numChannels != numChannels) 123 | fprintf(stderr, "Audio.c: device does not support %u numChannels, %i will be used instead\n", numChannels, self->numChannels); 124 | } 125 | if(error >= 0) 126 | { 127 | unsigned int sampleRate = AU_SAMPLE_RATE; 128 | error = snd_pcm_hw_params_set_rate_near(self->device, hardwareParameters, &sampleRate, 0); 129 | if(error < 0) fprintf(stderr, "Audio.c: Unable to set the sample rate to %u: %s\n", sampleRate, snd_strerror(error)); 130 | else if(sampleRate != AU_SAMPLE_RATE) 131 | fprintf(stderr, "Audio.c: device does not support %i sample rate, %u will be used instead\n", (int)AU_SAMPLE_RATE, sampleRate); 132 | } 133 | if(error >= 0) 134 | { 135 | int dir = 0; 136 | self->bufferNumFrames = AU_BUFFER_NUM_FRAMES; //the buffer I give to ALSA 137 | error = snd_pcm_hw_params_set_period_size_near(self->device, hardwareParameters, &(self->bufferNumFrames), &dir); 138 | if(error < 0) fprintf(stderr, "Audio.cpp: Unable to set the sample buffer size to %lu: %s\n", self->bufferNumFrames, snd_strerror(error)); 139 | else if(self->bufferNumFrames != AU_BUFFER_NUM_FRAMES) 140 | fprintf(stderr, "Audio.c: device does not support %i period size, %lu will be used instead\n", AU_BUFFER_NUM_FRAMES, self->bufferNumFrames); 141 | } 142 | if(error >= 0) 143 | { 144 | unsigned long int internalBufferNumFrames = self->bufferNumFrames * AU_NUM_AUDIO_BUFFERS; //the buffer ALSA uses internally 145 | error = snd_pcm_hw_params_set_buffer_size_near(self->device, hardwareParameters, &internalBufferNumFrames); 146 | if(error < 0) fprintf(stderr, "Audio.c: Unable to set the internal buffer size to %lu: %s\n", internalBufferNumFrames, snd_strerror(error)); 147 | else if(internalBufferNumFrames != AU_NUM_AUDIO_BUFFERS * self->bufferNumFrames) 148 | fprintf(stderr, "Audio.c: device does not support %lu internal buffer size, %lu will be used instead\n", AU_NUM_AUDIO_BUFFERS * self->bufferNumFrames, internalBufferNumFrames); 149 | } 150 | if(error >= 0) 151 | { 152 | error = snd_pcm_hw_params(self->device, hardwareParameters); 153 | if(error < 0) fprintf(stderr, "Audio.c: Unable to load the hardware parameters into the device: %s\n", snd_strerror(error)); 154 | } 155 | if(error >= 0) 156 | { 157 | unsigned int size = sizeof(auSample_t) * self->numChannels * self->bufferNumFrames; 158 | self->sampleBuffer = (auSample_t*)malloc(size); 159 | if(self->sampleBuffer == NULL) 160 | { 161 | error = -1; 162 | perror("Audio.c: Unable to allocate audio buffers \n"); 163 | } 164 | } 165 | if (error < 0) self = auDestroy(self); 166 | #endif 167 | } 168 | else perror("Audio.c: Unable to create new Audio object"); 169 | 170 | srandom((unsigned)time(NULL)); 171 | 172 | return self; 173 | } 174 | 175 | /*auDestroy-----------------------------------------------*/ 176 | Audio* auDestroy(Audio* self) 177 | { 178 | if(self != NULL) 179 | { 180 | if(self->isPlaying) auPause(self); 181 | 182 | #if defined __APPLE__ 183 | int i; 184 | for(i=0; ibuffers[i] != NULL) 186 | AudioQueueFreeBuffer(self->queue, self->buffers[i]); 187 | 188 | if(self->queue != NULL) 189 | AudioQueueDispose(self->queue, YES); 190 | 191 | #elif defined __linux__ 192 | if(self->device != NULL) 193 | { 194 | snd_pcm_close(self->device); 195 | self->device = NULL; 196 | } 197 | if(self->sampleBuffer) 198 | free(self->sampleBuffer); 199 | //if(self->thread != NULL) 200 | //pthread_detach(self->thread); 201 | #endif 202 | 203 | free(self); 204 | } 205 | return (Audio*)NULL; 206 | } 207 | 208 | /*auDestroy-----------------------------------------------*/ 209 | Audio* auSubclassDestroy (Audio* self) 210 | { 211 | Audio* result = NULL; 212 | 213 | if(self != NULL) 214 | result = self->destroy(self); 215 | 216 | return result; 217 | } 218 | 219 | /*auStart-------------------------------------------------*/ 220 | BOOL auPlay(Audio* self) 221 | { 222 | if(!self->isPlaying) 223 | { 224 | 225 | #ifdef __APPLE__ 226 | int i; 227 | for(i=0; iisOutput) 230 | auAudioOutputCallback(self, self->queue, self->buffers[i]); 231 | else 232 | AudioQueueEnqueueBuffer(self->queue, self->buffers[i],0, NULL); 233 | } 234 | OSStatus error = AudioQueueStart(self->queue, NULL); 235 | if(error) fprintf(stderr, "Audio.c: unable to start queue\n"); 236 | 237 | #elif defined __linux__ 238 | int error = pthread_create(&(self->thread), NULL, auAudioCallback, self); 239 | if(error != 0) perror("Audio.c: error creating Audio thread"); 240 | 241 | #endif 242 | else self->isPlaying = YES; 243 | } 244 | 245 | return self->isPlaying; 246 | } 247 | 248 | /*auPause--------------------------------------------------*/ 249 | BOOL auPause(Audio* self) 250 | { 251 | 252 | if(self->isPlaying) 253 | { 254 | 255 | #ifdef __APPLE__ 256 | OSStatus error = AudioQueueFlush(self->queue); 257 | if(!error) 258 | error = AudioQueueStop (self->queue, YES); 259 | self->isPlaying = (error != 0); 260 | 261 | #elif defined __linux__ 262 | self->threadShouldContinueRunning = NO; 263 | if(self->threadIsRunning) 264 | pthread_join(self->thread, NULL); 265 | self->isPlaying = NO; 266 | 267 | #endif 268 | } 269 | return self->isPlaying; 270 | } 271 | 272 | /*auTogglePlayPause---------------------------------------*/ 273 | BOOL auTogglePlayPause (Audio* self) 274 | { 275 | return (self->isPlaying) ? auPause(self) : auPlay(self); 276 | } 277 | 278 | /*auMasterVolume------------------------------------------*/ 279 | float auMasterVolume (Audio* self) 280 | { 281 | return sqrt(self->targetMasterVolume); 282 | } 283 | 284 | /*auSetMasterVolume---------------------------------------*/ 285 | void auSetMasterVolume(Audio* self, float volume) 286 | { 287 | if(volume > 1) volume = 1; 288 | if(volume < 0) volume = 0; 289 | volume *= volume; 290 | self->targetMasterVolume = volume; 291 | } 292 | 293 | /*auSetMasterVolumeSmoothing-------------------------------*/ 294 | void auSetMasterVolumeSmoothing(Audio* self, float smoothing) 295 | { 296 | //strange, I know. 297 | self->masterVolumeSmoothing = 1 - smoothing; 298 | self->oneMinusMasterVolumeSmoothing = smoothing; 299 | } 300 | 301 | /*auMasterVolumeSmoothing----------------------------------*/ 302 | float auMasterVolumeSmoothing(Audio* self) 303 | { 304 | return self->oneMinusMasterVolumeSmoothing; 305 | } 306 | 307 | /*auisPlaying----------------------------------------------*/ 308 | BOOL auIsPlaying (Audio* self) 309 | { 310 | return self->isPlaying; 311 | } 312 | 313 | /*auPlayFile------------------------------------------------*/ 314 | /* 315 | void auPlayFile (Audio* self, const char* path) 316 | { 317 | char* command; 318 | asprintf(&command, "afplay %s&", path); 319 | system(command); 320 | } 321 | */ 322 | 323 | /*auDeinterleave-------------------------------------------*/ 324 | //if buffer is big, use calloc instead for temp 325 | //there is probably a better way that uses less space... 326 | void auDeinterleave (auSample_t* buffer, int numFrames, int numChannels) 327 | { 328 | if(numChannels <= 1) return; 329 | 330 | int i, j; 331 | auSample_t temp[numFrames * numChannels]; 332 | auSample_t* t=temp; 333 | memcpy(temp, buffer, numFrames * numChannels * sizeof(auSample_t)); 334 | 335 | for(i=0; iaudioCallback(SELF, (auSample_t*)buffer->mAudioData, (buffer->mAudioDataBytesCapacity / self->dataFormat.mBytesPerFrame), self->dataFormat.mChannelsPerFrame); 347 | buffer->mAudioDataByteSize = resultFrames * self->dataFormat.mBytesPerFrame;//buffer->mAudioDataBytesCapacity; 348 | AudioQueueEnqueueBuffer(queue, buffer, 0, NULL); 349 | } 350 | 351 | void auAudioInputCallback( 352 | void *SELF, 353 | AudioQueueRef queue, 354 | AudioQueueBufferRef buffer, 355 | const AudioTimeStamp *startTime, 356 | UInt32 inNumberPacketDescriptions, 357 | const AudioStreamPacketDescription *inPacketDescs 358 | ) 359 | { 360 | Audio* self = (Audio*) SELF; 361 | 362 | if(startTime->mFlags & kAudioTimeStampHostTimeValid) 363 | { 364 | self->buffer_timestamp_is_supported = YES; 365 | self->current_buffer_timestamp = startTime->mHostTime / 1000; 366 | } 367 | else self->buffer_timestamp_is_supported = NO; 368 | 369 | auAudioOutputCallback(SELF, queue, buffer); 370 | } 371 | 372 | #elif defined __linux__ 373 | BOOL auTransferData(Audio* self, snd_pcm_sframes_t (*transfer)(snd_pcm_t*, void*, snd_pcm_uframes_t)); 374 | 375 | void* auAudioCallback(void* SELF) 376 | { 377 | Audio* self = (Audio*)SELF; 378 | self->threadIsRunning = self->threadShouldContinueRunning = YES; 379 | signal(SIGPIPE, SIG_IGN); 380 | int success = YES; 381 | 382 | while(self->threadShouldContinueRunning) 383 | { 384 | if(!self->isOutput) 385 | success = auTransferData(self, snd_pcm_readi); 386 | 387 | if(success) 388 | self->audioCallback(SELF, self->sampleBuffer, self->bufferNumFrames, self->numChannels); 389 | 390 | if(self->isOutput) 391 | success = auTransferData(self, (snd_pcm_sframes_t (*)(snd_pcm_t*, void*, snd_pcm_uframes_t)) snd_pcm_writei); 392 | 393 | if(!success) break; 394 | } 395 | self->threadIsRunning = NO; 396 | return NULL; 397 | } 398 | 399 | BOOL auTransferData(Audio* self, snd_pcm_sframes_t (*transfer)(snd_pcm_t*, void*, snd_pcm_uframes_t)) 400 | { 401 | int numFramesTransferred = 0, error = 0; 402 | int numFramesLeft = self->bufferNumFrames; 403 | auSample_t* p = self->sampleBuffer; 404 | 405 | while((numFramesLeft > 0) && self->threadShouldContinueRunning) 406 | { 407 | error = numFramesTransferred = transfer(self->device, p, numFramesLeft); 408 | 409 | if(numFramesTransferred < 0) 410 | { 411 | //fprintf(stderr, "Audio.c: audio device error while transferring samples: %s, attempting to recover... ", snd_strerror(error)); 412 | switch(error) 413 | { 414 | case -EPIPE: //overflow / underflow 415 | snd_pcm_wait(self->device, 100); 416 | if((error = snd_pcm_avail(self->device)) < 0) //broken pipe 417 | usleep(10000); //wait for more samples to come 418 | else numFramesLeft = 0; //overrun, skip remaining samples; 419 | 420 | error = snd_pcm_prepare(self->device); 421 | break; 422 | 423 | case -ESTRPIPE: 424 | while(((error = snd_pcm_resume(self->device)) == -EAGAIN) && self->threadShouldContinueRunning) 425 | sleep(1); 426 | if(error == -ENOSYS) error = snd_pcm_prepare(self->device); 427 | break; 428 | 429 | } 430 | if(error < 0) 431 | { 432 | //fprintf(stderr, "Aborting\n"); 433 | self->threadShouldContinueRunning = NO; 434 | break; 435 | } 436 | else 437 | { 438 | //fprintf(stderr, "Okay\n"); 439 | numFramesTransferred = 0; 440 | } 441 | } 442 | p += numFramesTransferred * self->numChannels; 443 | numFramesLeft -= numFramesTransferred; 444 | } 445 | return (numFramesLeft == 0) ? YES : NO; 446 | } 447 | 448 | #endif 449 | -------------------------------------------------------------------------------- /demos/online/AudioSuperclass.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AudioSuperclass.h 3 | * Root class for audio synthesizers, for OSX or Linux 4 | * 5 | * Made by Michael Krzyzaniak at Arizona State University's 6 | * School of Arts, Media + Engineering in Spring of 2013 7 | * mkrzyzan@asu.edu 8 | 9 | 03 Oct 2019, fixed linux audio input for raspi. 10 | */ 11 | 12 | 13 | #ifndef __AUDIO_SUPERCLASS__ 14 | #define __AUDIO_SUPERCLASS__ 1 15 | 16 | #if defined(__cplusplus) 17 | extern "C"{ 18 | #endif //(__cplusplus) 19 | 20 | #if defined __APPLE__ 21 | #include 22 | #elif defined __BELA__ 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include //usleep 29 | #include // NULL 30 | #elif defined __linux__ 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #endif 37 | 38 | //#include "MKAiff.h" 39 | //#include "fastsin.h" 40 | #define SIN_TWO_PI 0xFFFFFFFF 41 | 42 | #ifndef BOOL 43 | #define BOOL int 44 | #endif 45 | #ifndef NO 46 | #define NO 0 47 | #define YES (!NO) 48 | #endif 49 | 50 | #define AU_NUM_AUDIO_BUFFERS 4 51 | #define AU_BUFFER_NUM_FRAMES 128 52 | #define AU_SAMPLE_RATE 44100.0 53 | //#define AU_SAMPLE_RATE 192000.0 54 | 55 | typedef float auSample_t; 56 | typedef int (*auAudioCallback_t) (void* SELF, auSample_t* buffer, int numFrames, int numChannels); 57 | 58 | 59 | #if defined __APPLE__ 60 | #define AUDIO_PLATFORM_SPECIFIC_GUTS \ 61 | AudioQueueRef queue ; \ 62 | AudioQueueBufferRef buffers[AU_NUM_AUDIO_BUFFERS] ; \ 63 | AudioStreamBasicDescription dataFormat ; \ 64 | 65 | #elif defined __BELA__ 66 | #define AUDIO_PLATFORM_SPECIFIC_GUTS 67 | 68 | #elif defined __linux__ 69 | #define AUDIO_PLATFORM_SPECIFIC_GUTS \ 70 | snd_pcm_t *device ; \ 71 | auSample_t *sampleBuffer ; \ 72 | pthread_t thread ; \ 73 | int threadIsRunning ; \ 74 | int threadShouldContinueRunning ; \ 75 | unsigned long int bufferNumFrames ; 76 | 77 | #define AU_SPEAKER_DEVICE_NAME "default" 78 | #define AU_MIC_DEVICE_NAME "default" 79 | #endif 80 | 81 | #define AUDIO_GUTS \ 82 | AUDIO_PLATFORM_SPECIFIC_GUTS \ 83 | unsigned numChannels ; \ 84 | BOOL isOutput ; \ 85 | BOOL isPlaying ; \ 86 | float masterVolume ; \ 87 | float targetMasterVolume ; \ 88 | float masterVolumeSmoothing ; \ 89 | float oneMinusMasterVolumeSmoothing ; \ 90 | auAudioCallback_t audioCallback ; \ 91 | uint64_t current_buffer_timestamp ; \ 92 | Audio* (*destroy)(Audio*) ; \ 93 | BOOL buffer_timestamp_is_supported ; // system uptime in usec 94 | 95 | typedef struct OpaqueAudioStruct Audio; 96 | 97 | 98 | const extern double AU_TWO_PI_OVER_SAMPLE_RATE; 99 | 100 | #define AU_RANDOM(min, max) (((random() / (long double)RAND_MAX)) * ((max) - (min)) + (min)) 101 | #define AU_MIDI2CPS(x) (440 * pow(2, ((x)-69) / 12.0)) 102 | #define AU_MIDI2FREQ(x) (AU_MIDI2CPS(x) * SIN_TWO_PI / (float)AU_SAMPLE_RATE) 103 | #define AU_CPS2MIDI(x) (69 + 12.0 * log2((x) / 440.0)) 104 | #define AU_FREQ2MIDI(x) AU_CPS2MIDI(x) / (SIN_TWO_PI / (float)AU_SAMPLE_RATE); 105 | #define AU_CONSTRAIN(x, MIN, MAX) ((x) = ((x) < (MIN)) ? (MIN) : ((x) > (MAX)) ? (MAX) : (x)) 106 | 107 | 108 | 109 | /* Subcalsses call these in their own Alloc / Destroy */ 110 | /* routines. Users call the subclasses' ruotines, */ 111 | /* not these. */ 112 | Audio* auAlloc (int sizeofstarself, auAudioCallback_t callback, BOOL isOutput, unsigned numChannels); 113 | Audio* auDestroy (Audio* self); 114 | 115 | /* this is the destroy routine for all subclasses. It just follows a pointer to the correct */ 116 | /* destroy routine */ 117 | Audio* auSubclassDestroy (Audio* self); 118 | 119 | /* cast subcalsses to (Audio* and call these) */ 120 | BOOL auPlay (Audio* self); 121 | BOOL auPause (Audio* self); 122 | BOOL auTogglePlayPause (Audio* self); 123 | BOOL auIsPlaying (Audio* self); 124 | float auMasterVolume (Audio* self); 125 | void auSetMasterVolume (Audio* self, float volume); 126 | void auSetMasterVolumeSmoothing(Audio* self, float smoothing); 127 | float auMasterVolumeSmoothing(Audio* self); 128 | void auDeinterleave (auSample_t* buffer, int numFrames, int numChannels); 129 | 130 | #if defined(__cplusplus) 131 | } 132 | #endif //(__cplusplus) 133 | 134 | #endif// __AUDIO_SUPERCLASS__ 135 | -------------------------------------------------------------------------------- /demos/online/Click.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Click.h 3 | * Make weird noises 4 | * 5 | * Made by Michael Krzyzaniak at Arizona State University's 6 | * School of Arts, Media + Engineering in Spring of 2013 7 | * mkrzyzan@asu.edu 8 | */ 9 | 10 | #include "Click.h" 11 | 12 | /*--------------------------------------------------------------------*/ 13 | struct OpaqueClickStruct 14 | { 15 | AUDIO_GUTS ; 16 | BOOL needs_click; 17 | }; 18 | 19 | /*--------------------------------------------------------------------*/ 20 | int click_audio_callback (void* SELF, auSample_t* buffer, int num_frames, int num_channels); 21 | 22 | Click* click_destroy (Click* self); 23 | 24 | 25 | /*--------------------------------------------------------------------*/ 26 | Click* click_new() 27 | { 28 | Click* self = (Click*) auAlloc(sizeof(*self), click_audio_callback, YES, 1); 29 | 30 | if(self != NULL) 31 | { 32 | self->destroy = (Audio* (*)(Audio*))click_destroy; 33 | } 34 | return self; 35 | } 36 | 37 | 38 | /*--------------------------------------------------------------------*/ 39 | Click* click_destroy(Click* self) 40 | { 41 | if(self != NULL) 42 | { 43 | 44 | } 45 | 46 | return (Click*) NULL; 47 | } 48 | 49 | /*--------------------------------------------------------------------*/ 50 | void click_click (Click* self) 51 | { 52 | self->needs_click = YES; 53 | } 54 | 55 | /*--------------------------------------------------------------------*/ 56 | int click_audio_callback(void* SELF, auSample_t* buffer, int num_frames, int num_channels) 57 | { 58 | Click* self = (Click*)SELF; 59 | 60 | memset(buffer, 0, sizeof(*buffer) * num_frames * num_channels); 61 | 62 | if(self->needs_click) 63 | { 64 | buffer[0] = 1; 65 | self->needs_click = NO; 66 | } 67 | 68 | return num_frames; 69 | } 70 | 71 | -------------------------------------------------------------------------------- /demos/online/Click.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Click.h 3 | * Make weird noises 4 | * 5 | * Made by Michael Krzyzaniak at Arizona State University's 6 | * School of Arts, Media + Engineering in Spring of 2013 7 | * mkrzyzan@asu.edu 8 | */ 9 | 10 | #ifndef __CLICK__ 11 | #define __CLICK__ 1 12 | 13 | #if defined(__cplusplus) 14 | extern "C"{ 15 | #endif //(__cplusplus) 16 | 17 | #include "AudioSuperclass.h" 18 | 19 | typedef struct OpaqueClickStruct Click; 20 | 21 | Click* click_new (); 22 | void click_click (Click* self); 23 | 24 | //Click* click_destroy (Click* self ); 25 | //call with self->destroy(self); 26 | 27 | #if defined(__cplusplus) 28 | } 29 | #endif //(__cplusplus) 30 | 31 | #endif // __CLICK__ 32 | -------------------------------------------------------------------------------- /demos/online/Microphone.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Synth.h 3 | * Make weird noises 4 | * 5 | * Made by Michael Krzyzaniak at Arizona State University's 6 | * School of Arts, Media + Engineering in Spring of 2013 7 | * mkrzyzan@asu.edu 8 | */ 9 | 10 | #include "Microphone.h" 11 | #include "Click.h" 12 | 13 | void mic_onset_detected_callback(void* SELF, unsigned long long sample_time); 14 | void mic_beat_detected_callback (void* SELF, unsigned long long sample_time); 15 | 16 | /*--------------------------------------------------------------------*/ 17 | struct OpaqueMicrophoneStruct 18 | { 19 | AUDIO_GUTS ; 20 | BTT* btt ; 21 | Click* click ; 22 | }; 23 | 24 | /*--------------------------------------------------------------------*/ 25 | int mic_audio_callback (void* SELF, auSample_t* buffer, int num_frames, int num_channels); 26 | Microphone* mic_destroy (Microphone* self); 27 | 28 | /*--------------------------------------------------------------------*/ 29 | Microphone* mic_new() 30 | { 31 | Microphone* self = (Microphone*) auAlloc(sizeof(*self), mic_audio_callback, NO, 2); 32 | 33 | if(self != NULL) 34 | { 35 | self->destroy = (Audio* (*)(Audio*))mic_destroy; 36 | 37 | self->click = click_new(); 38 | if(self->click == NULL) 39 | return (Microphone*)auDestroy((Audio*)self); 40 | 41 | self->btt = btt_new_default(); 42 | if(self->btt == NULL) 43 | return (Microphone*)auDestroy((Audio*)self); 44 | 45 | btt_set_onset_tracking_callback (self->btt, mic_onset_detected_callback, self); 46 | btt_set_beat_tracking_callback (self->btt, mic_beat_detected_callback , self); 47 | } 48 | 49 | //there should be a play callback that I can intercept and do this there. 50 | auPlay((Audio*)self->click); 51 | return self; 52 | } 53 | 54 | /*--------------------------------------------------------------------*/ 55 | void mic_onset_detected_callback(void* SELF, unsigned long long sample_time) 56 | { 57 | Microphone* self = (Microphone*) SELF; 58 | if(btt_get_tracking_mode(self->btt) <= BTT_ONSET_TRACKING) 59 | { 60 | click_click(self->click); 61 | //fprintf(stderr, "onset\r\n"); 62 | } 63 | } 64 | 65 | /*--------------------------------------------------------------------*/ 66 | /* store each beat as a single click in an AIFF file so we can compare it to the original */ 67 | void mic_beat_detected_callback (void* SELF, unsigned long long sample_time) 68 | { 69 | Microphone* self = (Microphone*) SELF; 70 | click_click(self->click); 71 | //fprintf(stderr, "beat\r\n"); 72 | } 73 | 74 | /*--------------------------------------------------------------------*/ 75 | Microphone* mic_destroy(Microphone* self) 76 | { 77 | if(self != NULL) 78 | { 79 | btt_destroy(self->btt); 80 | auDestroy((Audio*)self->click); 81 | } 82 | 83 | return (Microphone*) NULL; 84 | } 85 | 86 | /*--------------------------------------------------------------------*/ 87 | BTT* mic_get_btt (Microphone* self) 88 | { 89 | return self->btt; 90 | } 91 | 92 | /*--------------------------------------------------------------------*/ 93 | int mic_audio_callback(void* SELF, auSample_t* buffer, int num_frames, int num_channels) 94 | { 95 | Microphone* self = (Microphone*)SELF; 96 | int frame, channel; 97 | 98 | //mix to mono without correcting amplitude 99 | for(frame=0; framebtt, buffer, num_frames); 104 | return num_frames; 105 | } 106 | 107 | -------------------------------------------------------------------------------- /demos/online/Microphone.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Synth.h 3 | * Make weird noises 4 | * 5 | * Made by Michael Krzyzaniak at Arizona State University's 6 | * School of Arts, Media + Engineering in Spring of 2013 7 | * mkrzyzan@asu.edu 8 | */ 9 | 10 | #ifndef __MICROPHONE__ 11 | #define __MICROPHONE__ 1 12 | 13 | #if defined(__cplusplus) 14 | extern "C"{ 15 | #endif //(__cplusplus) 16 | 17 | #include "AudioSuperclass.h" 18 | #include "../../BTT.h" 19 | 20 | typedef struct OpaqueMicrophoneStruct Microphone; 21 | 22 | Microphone* mic_new (); 23 | BTT* mic_get_btt (Microphone* self); 24 | 25 | //Microphone* mic_destroy (Microphone* self ); 26 | //call with self->destroy(self); 27 | 28 | #if defined(__cplusplus) 29 | } 30 | #endif //(__cplusplus) 31 | 32 | #endif // __MICROPHONE__ 33 | -------------------------------------------------------------------------------- /demos/online/main.c: -------------------------------------------------------------------------------- 1 | //OSX: gcc *.c ../../src/*.c -framework AudioToolbox 2 | //Linux: gcc *.c ../../src/*.c -lasound -lm -lpthread -lrt 3 | 4 | #include "Microphone.h" 5 | 6 | void i_hate_canonical_input_processing(void); 7 | void make_stdin_cannonical_again(); 8 | 9 | typedef void (*double_setter)(BTT* self, double val); 10 | typedef double (*double_getter)(BTT* self); 11 | typedef void (*int_setter) (BTT* self, int val); 12 | typedef int (*int_getter) (BTT* self); 13 | typedef void (*funct) (void); 14 | 15 | typedef struct parameter_struct 16 | { 17 | funct set; 18 | funct get; 19 | char type; 20 | double init; 21 | double increment; 22 | char name[128]; 23 | }param_t; 24 | 25 | param_t params[] = 26 | { 27 | { 28 | .set = (funct)btt_set_tracking_mode, 29 | .get = (funct)btt_get_tracking_mode, 30 | .type = 'i', 31 | .init = BTT_DEFAULT_TRACKING_MODE, 32 | .increment = 1, 33 | .name = "btt_set_tracking_mode", 34 | }, 35 | { 36 | .set = (funct)btt_set_noise_cancellation_threshold, 37 | .get = (funct)btt_get_noise_cancellation_threshold, 38 | .type = 'd', 39 | .init = BTT_DEFAULT_NOISE_CANCELLATION_THRESHOLD, 40 | .increment = 1, 41 | .name = "btt_set_noise_cancellation_threshold", 42 | }, 43 | { 44 | .set = (funct)btt_set_use_amplitude_normalization, 45 | .get = (funct)btt_get_use_amplitude_normalization, 46 | .type = 'i', 47 | .init = BTT_DEFAULT_USE_AMP_NORMALIZATION, 48 | .increment = 1, 49 | .name = "btt_set_use_amplitude_normalization", 50 | }, 51 | { 52 | .set = (funct)btt_set_spectral_compression_gamma, 53 | .get = (funct)btt_get_spectral_compression_gamma, 54 | .type = 'd', 55 | .init = BTT_DEFAULT_SPECTRAL_COMPRESSION_GAMMA, 56 | .increment = 0.05, 57 | .name = "btt_set_spectral_compression_gamma", 58 | }, 59 | { 60 | .set = (funct)btt_set_oss_filter_cutoff, 61 | .get = (funct)btt_get_oss_filter_cutoff, 62 | .type = 'd', 63 | .init = BTT_DEFAULT_OSS_FILTER_CUTOFF, 64 | .increment = 0.5, 65 | .name = "btt_set_oss_filter_cutoff", 66 | }, 67 | { 68 | .set = (funct)btt_set_onset_threshold, 69 | .get = (funct)btt_get_onset_threshold, 70 | .type = 'd', 71 | .init = BTT_DEFAULT_ONSET_TREHSHOLD, 72 | .increment = 0.1, 73 | .name = "btt_set_onset_threshold", 74 | }, 75 | { 76 | .set = (funct)btt_set_onset_threshold_min, 77 | .get = (funct)btt_get_onset_threshold_min, 78 | .type = 'd', 79 | .init = BTT_DEFAULT_ONSET_TREHSHOLD_MIN, 80 | .increment = 0.01, 81 | .name = "btt_set_onset_threshold_min", 82 | }, 83 | { 84 | .set = (funct)btt_set_autocorrelation_exponent, 85 | .get = (funct)btt_get_autocorrelation_exponent, 86 | .type = 'd', 87 | .init = BTT_DEFAULT_AUTOCORRELATION_EXPONENT, 88 | .increment = 0.1, 89 | .name = "btt_set_autocorrelation_exponent", 90 | }, 91 | { 92 | .set = (funct)btt_set_min_tempo, 93 | .get = (funct)btt_get_min_tempo, 94 | .type = 'd', 95 | .init = BTT_DEFAULT_MIN_TEMPO, 96 | .increment = 4, 97 | .name = "btt_set_min_tempo", 98 | }, 99 | { 100 | .set = (funct)btt_set_max_tempo, 101 | .get = (funct)btt_get_max_tempo, 102 | .type = 'd', 103 | .init = BTT_DEFAULT_MAX_TEMPO, 104 | .increment = 4, 105 | .name = "btt_set_max_tempo", 106 | }, 107 | { 108 | .set = (funct)btt_set_num_tempo_candidates, 109 | .get = (funct)btt_get_num_tempo_candidates, 110 | .type = 'i', 111 | .init = BTT_DEFAULT_NUM_TEMPO_CANDIDATES, 112 | .increment = 1, 113 | .name = "btt_set_num_tempo_candidates", 114 | }, 115 | { 116 | .set = (funct)btt_set_gaussian_tempo_histogram_decay, 117 | .get = (funct)btt_get_gaussian_tempo_histogram_decay, 118 | .type = 'd', 119 | .init = BTT_DEFAULT_GAUSSIAN_TEMPO_HISTOGRAM_DECAY, 120 | .increment = 0.00001, 121 | .name = "btt_set_gaussian_tempo_histogram_decay", 122 | }, 123 | { 124 | .set = (funct)btt_set_gaussian_tempo_histogram_width, 125 | .get = (funct)btt_get_gaussian_tempo_histogram_width, 126 | .type = 'd', 127 | .init = BTT_DEFAULT_GAUSSIAN_TEMPO_HISTOGRAM_WIDTH, 128 | .increment = 0.5, 129 | .name = "btt_set_gaussian_tempo_histogram_width", 130 | }, 131 | { 132 | .set = (funct)btt_set_log_gaussian_tempo_weight_mean, 133 | .get = (funct)btt_get_log_gaussian_tempo_weight_mean, 134 | .type = 'd', 135 | .init = BTT_DEFAULT_LOG_GAUSSIAN_TEMPO_WEIGHT_MEAN, 136 | .increment = 4, 137 | .name = "btt_set_log_gaussian_tempo_weight_mean", 138 | }, 139 | { 140 | .set = (funct)btt_set_log_gaussian_tempo_weight_width, 141 | .get = (funct)btt_get_log_gaussian_tempo_weight_width, 142 | .type = 'd', 143 | .init = BTT_DEFAULT_LOG_GAUSSIAN_TEMPO_WEIGHT_WIDTH, 144 | .increment = 4, 145 | .name = "btt_set_log_gaussian_tempo_weight_width", 146 | }, 147 | { 148 | .set = (funct)btt_set_cbss_alpha, 149 | .get = (funct)btt_get_cbss_alpha, 150 | .type = 'd', 151 | .init = BTT_DEFAULT_CBSS_ALPHA, 152 | .increment = 0.01, 153 | .name = "btt_set_cbss_alpha", 154 | }, 155 | { 156 | .set = (funct)btt_set_cbss_eta, 157 | .get = (funct)btt_get_cbss_eta, 158 | .type = 'd', 159 | .init = BTT_DEFAULT_CBSS_ETA, 160 | .increment = 5, 161 | .name = "btt_set_cbss_eta", 162 | }, 163 | { 164 | .set = (funct)btt_set_beat_prediction_adjustment, 165 | .get = (funct)btt_get_beat_prediction_adjustment, 166 | .type = 'i', 167 | .init = BTT_DEFAULT_BEAT_PREDICTION_ADJUSTMENT, 168 | .increment = 1, 169 | .name = "btt_set_beat_prediction_adjustment", 170 | }, 171 | { 172 | .set = (funct)btt_set_predicted_beat_trigger_index, 173 | .get = (funct)btt_get_predicted_beat_trigger_index, 174 | .type = 'i', 175 | .init = BTT_DEFAULT_PREDICTED_BEAT_TRIGGER_INDEX, 176 | .increment = 1, 177 | .name = "btt_set_predicted_beat_trigger_index", 178 | }, 179 | { 180 | .set = (funct)btt_set_predicted_beat_gaussian_width, 181 | .get = (funct)btt_get_predicted_beat_gaussian_width, 182 | .type = 'd', 183 | .init = BTT_DEFAULT_PREDICTED_BEAT_GAUSSIAN_WIDTH, 184 | .increment = 1, 185 | .name = "btt_set_predicted_beat_gaussian_width", 186 | }, 187 | { 188 | .set = (funct)btt_set_ignore_spurious_beats_duration, 189 | .get = (funct)btt_get_ignore_spurious_beats_duration, 190 | .type = 'd', 191 | .init = BTT_DEFAULT_IGNORE_SPURIOUS_BEATS_DURATION, 192 | .increment = 5, 193 | .name = "btt_set_ignore_spurious_beats_duration", 194 | }, 195 | { 196 | .set = (funct)btt_set_count_in_n, 197 | .get = (funct)btt_get_count_in_n, 198 | .type = 'i', 199 | .init = BTT_DEFAULT_COUNT_IN_N, 200 | .increment = 1, 201 | .name = "btt_set_count_in_n", 202 | }, 203 | }; 204 | 205 | int num_params = 22; //sizeof(params) / sizeof(params[0]); 206 | 207 | /*--------------------------------------------------------------------*/ 208 | int main(void) 209 | { 210 | i_hate_canonical_input_processing(); 211 | 212 | fprintf(stderr, "'q' to quit\r\n'<' or '>' to scroll through paramaters\r\n'+' or '-' to change the parameter values\r\n"); 213 | 214 | int param_index = 0; 215 | double increment; 216 | double val; 217 | char c; 218 | 219 | Microphone* mic = mic_new(); 220 | if(mic == NULL) {perror("Unable to create microphone object"); exit(-1);} 221 | 222 | BTT* btt = mic_get_btt(mic); 223 | btt_set_tracking_mode(btt, BTT_COUNT_IN_TRACKING); 224 | 225 | auPlay((Audio*)mic); 226 | 227 | for(;;) 228 | { 229 | c = getchar(); 230 | increment = 0; 231 | param_t p = params[param_index]; 232 | switch(c) 233 | { 234 | case '.': /* cascade */ 235 | case '>': 236 | ++param_index; 237 | if(param_index >= num_params) param_index = num_params - 1; 238 | p = params[param_index]; 239 | break; 240 | 241 | case ',': /* cascade */ 242 | case '<': 243 | --param_index; 244 | if(param_index < 0) param_index = 0; 245 | p = params[param_index]; 246 | break; 247 | 248 | case '-': /* cascade */ 249 | case '_': 250 | increment = -p.increment; 251 | break; 252 | 253 | case '=': /* cascade */ 254 | case '+': 255 | increment = p.increment; 256 | break; 257 | 258 | case 'd': /* cascade */ 259 | case 'D': 260 | if(p.type == 'i') 261 | ((int_setter) p.set)(btt, p.init); 262 | else if(p.type == 'd') 263 | ((double_setter) p.set)(btt, p.init); 264 | break; 265 | 266 | case 'c': /* cascade */ 267 | case 'C': 268 | btt_set_tracking_mode(btt, BTT_COUNT_IN_TRACKING); 269 | break; 270 | 271 | case 'q': /* cascade */ 272 | case 'Q': 273 | goto out; 274 | break; 275 | 276 | default: break; 277 | } 278 | 279 | if(p.type == 'i') 280 | { 281 | val = ((int_getter) p.get)(btt); 282 | ((int_setter) p.set)(btt, val + increment); 283 | val = ((int_getter) p.get)(btt); 284 | } 285 | else if(p.type == 'd') 286 | { 287 | val = ((double_getter) p.get)(btt); 288 | ((double_setter) p.set)(btt, val + increment); 289 | val = ((double_getter) p.get)(btt); 290 | } 291 | fprintf(stderr, " %s(btt, %lf); \r", p.name, val); 292 | } 293 | 294 | out: 295 | make_stdin_cannonical_again(); 296 | } 297 | 298 | 299 | /*--------------------------------------------------------------------*/ 300 | /*--------------------------------------------------------------------*/ 301 | /*--------------------------------------------------------------------*/ 302 | /*--------------------------------------------------------------------*/ 303 | #include 304 | #include 305 | #include 306 | #include 307 | struct termios old_terminal_attributes; 308 | 309 | void i_hate_canonical_input_processing(void) 310 | { 311 | int error; 312 | struct termios new_terminal_attributes; 313 | 314 | int fd = fcntl(STDIN_FILENO, F_DUPFD, 0); 315 | 316 | error = tcgetattr(fd, &(old_terminal_attributes)); 317 | if(error == -1) { fprintf(stderr, "Error getting serial terminal attributes\r\n"); return;} 318 | 319 | new_terminal_attributes = old_terminal_attributes; 320 | 321 | cfmakeraw(&new_terminal_attributes); 322 | 323 | error = tcsetattr(fd, TCSANOW, &new_terminal_attributes); 324 | if(error == -1) { fprintf(stderr, "Error setting serial attributes\r\n"); return; } 325 | } 326 | 327 | /*--------------------------------------------------------------------*/ 328 | void make_stdin_cannonical_again() 329 | { 330 | int fd = fcntl(STDIN_FILENO, F_DUPFD, 0); 331 | 332 | if (tcsetattr(fd, TCSANOW, &old_terminal_attributes) == -1) 333 | fprintf(stderr, "Error setting serial attributes\r\n"); 334 | } 335 | 336 | -------------------------------------------------------------------------------- /demos/online_print_tempo/AudioSuperclass.c: -------------------------------------------------------------------------------- 1 | /* 2 | * AudioSuperclass.c 3 | * Root class for audio synthesizers, for OSX or Linux 4 | * 5 | * Made by Michael Krzyzaniak at Arizona State University's 6 | * School of Arts, Media + Engineering in Spring of 2013 7 | * mkrzyzan@asu.edu 8 | */ 9 | 10 | 11 | 12 | //#define RECORD_MODE 1 13 | 14 | 15 | #include 16 | #include 17 | #include "AudioSuperclass.h" 18 | 19 | const double AU_TWO_PI_OVER_SAMPLE_RATE = SIN_TWO_PI / AU_SAMPLE_RATE; 20 | 21 | #if defined __APPLE__ 22 | void auAudioOutputCallback(void*, AudioQueueRef, AudioQueueBufferRef); 23 | void auAudioInputCallback (void*, AudioQueueRef, AudioQueueBufferRef, const AudioTimeStamp*, UInt32, const AudioStreamPacketDescription*); 24 | 25 | #elif defined __linux__ 26 | void* auAudioCallback(void* SELF); 27 | #endif 28 | 29 | 30 | /*OpaqueAudioStruct----------------------------------------*/ 31 | struct OpaqueAudioStruct 32 | { 33 | AUDIO_GUTS; 34 | }; 35 | 36 | /*auNew---------------------------------------------------*/ 37 | Audio* auAlloc(int sizeofstarself, auAudioCallback_t callback, BOOL isOutput, unsigned numChannels) 38 | { 39 | Audio* self = (Audio*)calloc(1, sizeofstarself); 40 | 41 | if(self != NULL) 42 | { 43 | self->isOutput = isOutput; 44 | self->isPlaying = NO; 45 | self->audioCallback = callback; 46 | self->numChannels = numChannels; 47 | self->targetMasterVolume = 1; 48 | self->destroy = auDestroy; 49 | auSetMasterVolumeSmoothing(self, 0.9999); 50 | 51 | #if defined __APPLE__ 52 | int i; 53 | OSStatus error; 54 | 55 | self->dataFormat.mSampleRate = AU_SAMPLE_RATE; 56 | self->dataFormat.mBytesPerPacket = 4 * numChannels ; 57 | self->dataFormat.mFramesPerPacket = 1 ; 58 | self->dataFormat.mBytesPerFrame = 4 * numChannels ; 59 | self->dataFormat.mBitsPerChannel = 32 ; 60 | self->dataFormat.mChannelsPerFrame = numChannels ; 61 | self->dataFormat.mFormatID = kAudioFormatLinearPCM; 62 | //self->dataFormat.mFormatFlags = kLinearPCMFormatFlagIsSignedInteger | kLinearPCMFormatFlagIsPacked; 63 | self->dataFormat.mFormatFlags = kLinearPCMFormatFlagIsFloat; 64 | 65 | if(isOutput) 66 | error = AudioQueueNewOutput(&(self->dataFormat), auAudioOutputCallback, self, NULL, NULL, 0, &(self->queue)); 67 | else 68 | error = AudioQueueNewInput (&(self->dataFormat), auAudioInputCallback, self, NULL, NULL, 0, &(self->queue)); 69 | if(error) 70 | { 71 | fprintf(stderr, "Audio.c: unable to allocate Audio queue\n"); 72 | return auDestroy(self); 73 | } 74 | else //(!error) 75 | { 76 | unsigned bufferNumBytes = AU_BUFFER_NUM_FRAMES * numChannels * sizeof(auSample_t); 77 | for(i=0; iqueue, bufferNumBytes, &((self->buffers)[i])); 80 | if(error) 81 | { 82 | self = auDestroy(self); 83 | fprintf(stderr, "Audio.c: allocate buffer error\n"); 84 | break; 85 | } 86 | } 87 | } 88 | 89 | #elif defined __linux__ 90 | int error = 0; 91 | 92 | snd_pcm_hw_params_t *hardwareParameters; 93 | snd_pcm_hw_params_alloca(&hardwareParameters); 94 | 95 | //untested for input stream... 96 | const char* name = (isOutput) ? AU_SPEAKER_DEVICE_NAME : AU_MIC_DEVICE_NAME; 97 | unsigned direction = (isOutput) ? SND_PCM_STREAM_PLAYBACK : SND_PCM_STREAM_CAPTURE; 98 | 99 | error = snd_pcm_open(&(self->device), name, direction, 0); 100 | if(error < 0) fprintf(stderr, "Audio.c: Unable to open speaker device %s: %s\n", AU_SPEAKER_DEVICE_NAME, snd_strerror(error)); 101 | 102 | if(error >= 0) 103 | { 104 | error = snd_pcm_hw_params_any(self->device, hardwareParameters); 105 | if(error < 0) fprintf(stderr, "Audio.c: Unable to get a generic hardware configuration: %s\n", snd_strerror(error)); 106 | } 107 | if(error >= 0) 108 | { 109 | error = snd_pcm_hw_params_set_access(self->device, hardwareParameters, SND_PCM_ACCESS_RW_INTERLEAVED); 110 | if(error < 0) fprintf(stderr, "Audio.c: Device does not support interleaved audio access: %s\n", snd_strerror(error)); 111 | } 112 | if(error >= 0) 113 | { 114 | error = snd_pcm_hw_params_set_format(self->device, hardwareParameters, /*SND_PCM_FORMAT_S16*/ SND_PCM_FORMAT_FLOAT) ; 115 | if(error < 0) fprintf(stderr, "Audio.c: Unable to set sample format: %s\n", snd_strerror(error)); 116 | } 117 | if(error >= 0) 118 | { 119 | //self->numChannels = AU_NUM_CHANNELS; 120 | error = snd_pcm_hw_params_set_channels_near(self->device, hardwareParameters, &self->numChannels); 121 | if(error < 0) fprintf(stderr, "Audio.c: unable to set the number of channels to %i: %s\n", self->numChannels, snd_strerror(error)); 122 | else if(self->numChannels != numChannels) 123 | fprintf(stderr, "Audio.c: device does not support %u numChannels, %i will be used instead\n", numChannels, self->numChannels); 124 | } 125 | if(error >= 0) 126 | { 127 | unsigned int sampleRate = AU_SAMPLE_RATE; 128 | error = snd_pcm_hw_params_set_rate_near(self->device, hardwareParameters, &sampleRate, 0); 129 | if(error < 0) fprintf(stderr, "Audio.c: Unable to set the sample rate to %u: %s\n", sampleRate, snd_strerror(error)); 130 | else if(sampleRate != AU_SAMPLE_RATE) 131 | fprintf(stderr, "Audio.c: device does not support %i sample rate, %u will be used instead\n", (int)AU_SAMPLE_RATE, sampleRate); 132 | } 133 | if(error >= 0) 134 | { 135 | int dir = 0; 136 | self->bufferNumFrames = AU_BUFFER_NUM_FRAMES; //the buffer I give to ALSA 137 | error = snd_pcm_hw_params_set_period_size_near(self->device, hardwareParameters, &(self->bufferNumFrames), &dir); 138 | if(error < 0) fprintf(stderr, "Audio.cpp: Unable to set the sample buffer size to %lu: %s\n", self->bufferNumFrames, snd_strerror(error)); 139 | else if(self->bufferNumFrames != AU_BUFFER_NUM_FRAMES) 140 | fprintf(stderr, "Audio.c: device does not support %i period size, %lu will be used instead\n", AU_BUFFER_NUM_FRAMES, self->bufferNumFrames); 141 | } 142 | if(error >= 0) 143 | { 144 | unsigned long int internalBufferNumFrames = self->bufferNumFrames * AU_NUM_AUDIO_BUFFERS; //the buffer ALSA uses internally 145 | error = snd_pcm_hw_params_set_buffer_size_near(self->device, hardwareParameters, &internalBufferNumFrames); 146 | if(error < 0) fprintf(stderr, "Audio.c: Unable to set the internal buffer size to %lu: %s\n", internalBufferNumFrames, snd_strerror(error)); 147 | else if(internalBufferNumFrames != AU_NUM_AUDIO_BUFFERS * self->bufferNumFrames) 148 | fprintf(stderr, "Audio.c: device does not support %lu internal buffer size, %lu will be used instead\n", AU_NUM_AUDIO_BUFFERS * self->bufferNumFrames, internalBufferNumFrames); 149 | } 150 | if(error >= 0) 151 | { 152 | error = snd_pcm_hw_params(self->device, hardwareParameters); 153 | if(error < 0) fprintf(stderr, "Audio.c: Unable to load the hardware parameters into the device: %s\n", snd_strerror(error)); 154 | } 155 | if(error >= 0) 156 | { 157 | unsigned int size = sizeof(auSample_t) * self->numChannels * self->bufferNumFrames; 158 | self->sampleBuffer = (auSample_t*)malloc(size); 159 | if(self->sampleBuffer == NULL) 160 | { 161 | error = -1; 162 | perror("Audio.c: Unable to allocate audio buffers \n"); 163 | } 164 | } 165 | if (error < 0) self = auDestroy(self); 166 | #endif 167 | } 168 | else perror("Audio.c: Unable to create new Audio object"); 169 | 170 | srandom((unsigned)time(NULL)); 171 | 172 | return self; 173 | } 174 | 175 | /*auDestroy-----------------------------------------------*/ 176 | Audio* auDestroy(Audio* self) 177 | { 178 | if(self != NULL) 179 | { 180 | if(self->isPlaying) auPause(self); 181 | 182 | #if defined __APPLE__ 183 | int i; 184 | for(i=0; ibuffers[i] != NULL) 186 | AudioQueueFreeBuffer(self->queue, self->buffers[i]); 187 | 188 | if(self->queue != NULL) 189 | AudioQueueDispose(self->queue, YES); 190 | 191 | #elif defined __linux__ 192 | if(self->device != NULL) 193 | { 194 | snd_pcm_close(self->device); 195 | self->device = NULL; 196 | } 197 | if(self->sampleBuffer) 198 | free(self->sampleBuffer); 199 | //if(self->thread != NULL) 200 | //pthread_detach(self->thread); 201 | #endif 202 | 203 | free(self); 204 | } 205 | return (Audio*)NULL; 206 | } 207 | 208 | /*auDestroy-----------------------------------------------*/ 209 | Audio* auSubclassDestroy (Audio* self) 210 | { 211 | Audio* result = NULL; 212 | 213 | if(self != NULL) 214 | result = self->destroy(self); 215 | 216 | return result; 217 | } 218 | 219 | /*auStart-------------------------------------------------*/ 220 | BOOL auPlay(Audio* self) 221 | { 222 | if(!self->isPlaying) 223 | { 224 | 225 | #ifdef __APPLE__ 226 | int i; 227 | for(i=0; iisOutput) 230 | auAudioOutputCallback(self, self->queue, self->buffers[i]); 231 | else 232 | AudioQueueEnqueueBuffer(self->queue, self->buffers[i],0, NULL); 233 | } 234 | OSStatus error = AudioQueueStart(self->queue, NULL); 235 | if(error) fprintf(stderr, "Audio.c: unable to start queue\n"); 236 | 237 | #elif defined __linux__ 238 | int error = pthread_create(&(self->thread), NULL, auAudioCallback, self); 239 | if(error != 0) perror("Audio.c: error creating Audio thread"); 240 | 241 | #endif 242 | else self->isPlaying = YES; 243 | } 244 | 245 | return self->isPlaying; 246 | } 247 | 248 | /*auPause--------------------------------------------------*/ 249 | BOOL auPause(Audio* self) 250 | { 251 | 252 | if(self->isPlaying) 253 | { 254 | 255 | #ifdef __APPLE__ 256 | OSStatus error = AudioQueueFlush(self->queue); 257 | if(!error) 258 | error = AudioQueueStop (self->queue, YES); 259 | self->isPlaying = (error != 0); 260 | 261 | #elif defined __linux__ 262 | self->threadShouldContinueRunning = NO; 263 | if(self->threadIsRunning) 264 | pthread_join(self->thread, NULL); 265 | self->isPlaying = NO; 266 | 267 | #endif 268 | } 269 | return self->isPlaying; 270 | } 271 | 272 | /*auTogglePlayPause---------------------------------------*/ 273 | BOOL auTogglePlayPause (Audio* self) 274 | { 275 | return (self->isPlaying) ? auPause(self) : auPlay(self); 276 | } 277 | 278 | /*auMasterVolume------------------------------------------*/ 279 | float auMasterVolume (Audio* self) 280 | { 281 | return sqrt(self->targetMasterVolume); 282 | } 283 | 284 | /*auSetMasterVolume---------------------------------------*/ 285 | void auSetMasterVolume(Audio* self, float volume) 286 | { 287 | if(volume > 1) volume = 1; 288 | if(volume < 0) volume = 0; 289 | volume *= volume; 290 | self->targetMasterVolume = volume; 291 | } 292 | 293 | /*auSetMasterVolumeSmoothing-------------------------------*/ 294 | void auSetMasterVolumeSmoothing(Audio* self, float smoothing) 295 | { 296 | //strange, I know. 297 | self->masterVolumeSmoothing = 1 - smoothing; 298 | self->oneMinusMasterVolumeSmoothing = smoothing; 299 | } 300 | 301 | /*auMasterVolumeSmoothing----------------------------------*/ 302 | float auMasterVolumeSmoothing(Audio* self) 303 | { 304 | return self->oneMinusMasterVolumeSmoothing; 305 | } 306 | 307 | /*auisPlaying----------------------------------------------*/ 308 | BOOL auIsPlaying (Audio* self) 309 | { 310 | return self->isPlaying; 311 | } 312 | 313 | /*auPlayFile------------------------------------------------*/ 314 | /* 315 | void auPlayFile (Audio* self, const char* path) 316 | { 317 | char* command; 318 | asprintf(&command, "afplay %s&", path); 319 | system(command); 320 | } 321 | */ 322 | 323 | /*auDeinterleave-------------------------------------------*/ 324 | //if buffer is big, use calloc instead for temp 325 | //there is probably a better way that uses less space... 326 | void auDeinterleave (auSample_t* buffer, int numFrames, int numChannels) 327 | { 328 | if(numChannels <= 1) return; 329 | 330 | int i, j; 331 | auSample_t temp[numFrames * numChannels]; 332 | auSample_t* t=temp; 333 | memcpy(temp, buffer, numFrames * numChannels * sizeof(auSample_t)); 334 | 335 | for(i=0; iaudioCallback(SELF, (auSample_t*)buffer->mAudioData, (buffer->mAudioDataBytesCapacity / self->dataFormat.mBytesPerFrame), self->dataFormat.mChannelsPerFrame); 347 | buffer->mAudioDataByteSize = resultFrames * self->dataFormat.mBytesPerFrame;//buffer->mAudioDataBytesCapacity; 348 | AudioQueueEnqueueBuffer(queue, buffer, 0, NULL); 349 | } 350 | 351 | void auAudioInputCallback( 352 | void *SELF, 353 | AudioQueueRef queue, 354 | AudioQueueBufferRef buffer, 355 | const AudioTimeStamp *startTime, 356 | UInt32 inNumberPacketDescriptions, 357 | const AudioStreamPacketDescription *inPacketDescs 358 | ) 359 | { 360 | Audio* self = (Audio*) SELF; 361 | 362 | if(startTime->mFlags & kAudioTimeStampHostTimeValid) 363 | { 364 | self->buffer_timestamp_is_supported = YES; 365 | self->current_buffer_timestamp = startTime->mHostTime / 1000; 366 | } 367 | else self->buffer_timestamp_is_supported = NO; 368 | 369 | auAudioOutputCallback(SELF, queue, buffer); 370 | } 371 | 372 | #elif defined __linux__ 373 | BOOL auTransferData(Audio* self, snd_pcm_sframes_t (*transfer)(snd_pcm_t*, void*, snd_pcm_uframes_t)); 374 | 375 | void* auAudioCallback(void* SELF) 376 | { 377 | Audio* self = (Audio*)SELF; 378 | self->threadIsRunning = self->threadShouldContinueRunning = YES; 379 | signal(SIGPIPE, SIG_IGN); 380 | int success = YES; 381 | 382 | while(self->threadShouldContinueRunning) 383 | { 384 | if(!self->isOutput) 385 | success = auTransferData(self, snd_pcm_readi); 386 | 387 | if(success) 388 | self->audioCallback(SELF, self->sampleBuffer, self->bufferNumFrames, self->numChannels); 389 | 390 | if(self->isOutput) 391 | success = auTransferData(self, (snd_pcm_sframes_t (*)(snd_pcm_t*, void*, snd_pcm_uframes_t)) snd_pcm_writei); 392 | 393 | if(!success) break; 394 | } 395 | self->threadIsRunning = NO; 396 | return NULL; 397 | } 398 | 399 | BOOL auTransferData(Audio* self, snd_pcm_sframes_t (*transfer)(snd_pcm_t*, void*, snd_pcm_uframes_t)) 400 | { 401 | int numFramesTransferred = 0, error = 0; 402 | int numFramesLeft = self->bufferNumFrames; 403 | auSample_t* p = self->sampleBuffer; 404 | 405 | while((numFramesLeft > 0) && self->threadShouldContinueRunning) 406 | { 407 | error = numFramesTransferred = transfer(self->device, p, numFramesLeft); 408 | 409 | if(numFramesTransferred < 0) 410 | { 411 | //fprintf(stderr, "Audio.c: audio device error while transferring samples: %s, attempting to recover... ", snd_strerror(error)); 412 | switch(error) 413 | { 414 | case -EPIPE: //overflow / underflow 415 | snd_pcm_wait(self->device, 100); 416 | if((error = snd_pcm_avail(self->device)) < 0) //broken pipe 417 | usleep(10000); //wait for more samples to come 418 | else numFramesLeft = 0; //overrun, skip remaining samples; 419 | 420 | error = snd_pcm_prepare(self->device); 421 | break; 422 | 423 | case -ESTRPIPE: 424 | while(((error = snd_pcm_resume(self->device)) == -EAGAIN) && self->threadShouldContinueRunning) 425 | sleep(1); 426 | if(error == -ENOSYS) error = snd_pcm_prepare(self->device); 427 | break; 428 | 429 | } 430 | if(error < 0) 431 | { 432 | //fprintf(stderr, "Aborting\n"); 433 | self->threadShouldContinueRunning = NO; 434 | break; 435 | } 436 | else 437 | { 438 | //fprintf(stderr, "Okay\n"); 439 | numFramesTransferred = 0; 440 | } 441 | } 442 | p += numFramesTransferred * self->numChannels; 443 | numFramesLeft -= numFramesTransferred; 444 | } 445 | return (numFramesLeft == 0) ? YES : NO; 446 | } 447 | 448 | #endif 449 | -------------------------------------------------------------------------------- /demos/online_print_tempo/AudioSuperclass.h: -------------------------------------------------------------------------------- 1 | /* 2 | * AudioSuperclass.h 3 | * Root class for audio synthesizers, for OSX or Linux 4 | * 5 | * Made by Michael Krzyzaniak at Arizona State University's 6 | * School of Arts, Media + Engineering in Spring of 2013 7 | * mkrzyzan@asu.edu 8 | 9 | 03 Oct 2019, fixed linux audio input for raspi. 10 | */ 11 | 12 | 13 | #ifndef __AUDIO_SUPERCLASS__ 14 | #define __AUDIO_SUPERCLASS__ 1 15 | 16 | #if defined(__cplusplus) 17 | extern "C"{ 18 | #endif //(__cplusplus) 19 | 20 | #if defined __APPLE__ 21 | #include 22 | #elif defined __BELA__ 23 | #include 24 | #include 25 | #include 26 | #include 27 | #include 28 | #include //usleep 29 | #include // NULL 30 | #elif defined __linux__ 31 | #include 32 | #include 33 | #include 34 | #include 35 | #include 36 | #endif 37 | 38 | //#include "MKAiff.h" 39 | //#include "fastsin.h" 40 | #define SIN_TWO_PI 0xFFFFFFFF 41 | 42 | #ifndef BOOL 43 | #define BOOL int 44 | #endif 45 | #ifndef NO 46 | #define NO 0 47 | #define YES (!NO) 48 | #endif 49 | 50 | #define AU_NUM_AUDIO_BUFFERS 4 51 | #define AU_BUFFER_NUM_FRAMES 128 52 | #define AU_SAMPLE_RATE 44100.0 53 | //#define AU_SAMPLE_RATE 192000.0 54 | 55 | typedef float auSample_t; 56 | typedef int (*auAudioCallback_t) (void* SELF, auSample_t* buffer, int numFrames, int numChannels); 57 | 58 | 59 | #if defined __APPLE__ 60 | #define AUDIO_PLATFORM_SPECIFIC_GUTS \ 61 | AudioQueueRef queue ; \ 62 | AudioQueueBufferRef buffers[AU_NUM_AUDIO_BUFFERS] ; \ 63 | AudioStreamBasicDescription dataFormat ; \ 64 | 65 | #elif defined __BELA__ 66 | #define AUDIO_PLATFORM_SPECIFIC_GUTS 67 | 68 | #elif defined __linux__ 69 | #define AUDIO_PLATFORM_SPECIFIC_GUTS \ 70 | snd_pcm_t *device ; \ 71 | auSample_t *sampleBuffer ; \ 72 | pthread_t thread ; \ 73 | int threadIsRunning ; \ 74 | int threadShouldContinueRunning ; \ 75 | unsigned long int bufferNumFrames ; 76 | 77 | #define AU_SPEAKER_DEVICE_NAME "default" 78 | #define AU_MIC_DEVICE_NAME "default" 79 | #endif 80 | 81 | #define AUDIO_GUTS \ 82 | AUDIO_PLATFORM_SPECIFIC_GUTS \ 83 | unsigned numChannels ; \ 84 | BOOL isOutput ; \ 85 | BOOL isPlaying ; \ 86 | float masterVolume ; \ 87 | float targetMasterVolume ; \ 88 | float masterVolumeSmoothing ; \ 89 | float oneMinusMasterVolumeSmoothing ; \ 90 | auAudioCallback_t audioCallback ; \ 91 | uint64_t current_buffer_timestamp ; \ 92 | Audio* (*destroy)(Audio*) ; \ 93 | BOOL buffer_timestamp_is_supported ; // system uptime in usec 94 | 95 | typedef struct OpaqueAudioStruct Audio; 96 | 97 | 98 | const extern double AU_TWO_PI_OVER_SAMPLE_RATE; 99 | 100 | #define AU_RANDOM(min, max) (((random() / (long double)RAND_MAX)) * ((max) - (min)) + (min)) 101 | #define AU_MIDI2CPS(x) (440 * pow(2, ((x)-69) / 12.0)) 102 | #define AU_MIDI2FREQ(x) (AU_MIDI2CPS(x) * SIN_TWO_PI / (float)AU_SAMPLE_RATE) 103 | #define AU_CPS2MIDI(x) (69 + 12.0 * log2((x) / 440.0)) 104 | #define AU_FREQ2MIDI(x) AU_CPS2MIDI(x) / (SIN_TWO_PI / (float)AU_SAMPLE_RATE); 105 | #define AU_CONSTRAIN(x, MIN, MAX) ((x) = ((x) < (MIN)) ? (MIN) : ((x) > (MAX)) ? (MAX) : (x)) 106 | 107 | 108 | 109 | /* Subcalsses call these in their own Alloc / Destroy */ 110 | /* routines. Users call the subclasses' ruotines, */ 111 | /* not these. */ 112 | Audio* auAlloc (int sizeofstarself, auAudioCallback_t callback, BOOL isOutput, unsigned numChannels); 113 | Audio* auDestroy (Audio* self); 114 | 115 | /* this is the destroy routine for all subclasses. It just follows a pointer to the correct */ 116 | /* destroy routine */ 117 | Audio* auSubclassDestroy (Audio* self); 118 | 119 | /* cast subcalsses to (Audio* and call these) */ 120 | BOOL auPlay (Audio* self); 121 | BOOL auPause (Audio* self); 122 | BOOL auTogglePlayPause (Audio* self); 123 | BOOL auIsPlaying (Audio* self); 124 | float auMasterVolume (Audio* self); 125 | void auSetMasterVolume (Audio* self, float volume); 126 | void auSetMasterVolumeSmoothing(Audio* self, float smoothing); 127 | float auMasterVolumeSmoothing(Audio* self); 128 | void auDeinterleave (auSample_t* buffer, int numFrames, int numChannels); 129 | 130 | #if defined(__cplusplus) 131 | } 132 | #endif //(__cplusplus) 133 | 134 | #endif// __AUDIO_SUPERCLASS__ 135 | -------------------------------------------------------------------------------- /demos/online_print_tempo/Click.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Click.h 3 | * Make weird noises 4 | * 5 | * Made by Michael Krzyzaniak at Arizona State University's 6 | * School of Arts, Media + Engineering in Spring of 2013 7 | * mkrzyzan@asu.edu 8 | */ 9 | 10 | #include "Click.h" 11 | 12 | /*--------------------------------------------------------------------*/ 13 | struct OpaqueClickStruct 14 | { 15 | AUDIO_GUTS ; 16 | BOOL needs_click; 17 | }; 18 | 19 | /*--------------------------------------------------------------------*/ 20 | int click_audio_callback (void* SELF, auSample_t* buffer, int num_frames, int num_channels); 21 | 22 | Click* click_destroy (Click* self); 23 | 24 | 25 | /*--------------------------------------------------------------------*/ 26 | Click* click_new() 27 | { 28 | Click* self = (Click*) auAlloc(sizeof(*self), click_audio_callback, YES, 1); 29 | 30 | if(self != NULL) 31 | { 32 | self->destroy = (Audio* (*)(Audio*))click_destroy; 33 | } 34 | return self; 35 | } 36 | 37 | 38 | /*--------------------------------------------------------------------*/ 39 | Click* click_destroy(Click* self) 40 | { 41 | if(self != NULL) 42 | { 43 | 44 | } 45 | 46 | return (Click*) NULL; 47 | } 48 | 49 | /*--------------------------------------------------------------------*/ 50 | void click_click (Click* self) 51 | { 52 | self->needs_click = YES; 53 | } 54 | 55 | /*--------------------------------------------------------------------*/ 56 | int click_audio_callback(void* SELF, auSample_t* buffer, int num_frames, int num_channels) 57 | { 58 | Click* self = (Click*)SELF; 59 | 60 | memset(buffer, 0, sizeof(*buffer) * num_frames * num_channels); 61 | 62 | if(self->needs_click) 63 | { 64 | buffer[0] = 1; 65 | self->needs_click = NO; 66 | } 67 | 68 | return num_frames; 69 | } 70 | 71 | -------------------------------------------------------------------------------- /demos/online_print_tempo/Click.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Click.h 3 | * Make weird noises 4 | * 5 | * Made by Michael Krzyzaniak at Arizona State University's 6 | * School of Arts, Media + Engineering in Spring of 2013 7 | * mkrzyzan@asu.edu 8 | */ 9 | 10 | #ifndef __CLICK__ 11 | #define __CLICK__ 1 12 | 13 | #if defined(__cplusplus) 14 | extern "C"{ 15 | #endif //(__cplusplus) 16 | 17 | #include "AudioSuperclass.h" 18 | 19 | typedef struct OpaqueClickStruct Click; 20 | 21 | Click* click_new (); 22 | void click_click (Click* self); 23 | 24 | //Click* click_destroy (Click* self ); 25 | //call with self->destroy(self); 26 | 27 | #if defined(__cplusplus) 28 | } 29 | #endif //(__cplusplus) 30 | 31 | #endif // __CLICK__ 32 | -------------------------------------------------------------------------------- /demos/online_print_tempo/Microphone.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Synth.h 3 | * Make weird noises 4 | * 5 | * Made by Michael Krzyzaniak at Arizona State University's 6 | * School of Arts, Media + Engineering in Spring of 2013 7 | * mkrzyzan@asu.edu 8 | */ 9 | 10 | #include "Microphone.h" 11 | #include "Click.h" 12 | 13 | void mic_onset_detected_callback(void* SELF, unsigned long long sample_time); 14 | void mic_beat_detected_callback (void* SELF, unsigned long long sample_time); 15 | 16 | /*--------------------------------------------------------------------*/ 17 | struct OpaqueMicrophoneStruct 18 | { 19 | AUDIO_GUTS ; 20 | BTT* btt ; 21 | Click* click ; 22 | }; 23 | 24 | /*--------------------------------------------------------------------*/ 25 | int mic_audio_callback (void* SELF, auSample_t* buffer, int num_frames, int num_channels); 26 | Microphone* mic_destroy (Microphone* self); 27 | 28 | /*--------------------------------------------------------------------*/ 29 | Microphone* mic_new() 30 | { 31 | Microphone* self = (Microphone*) auAlloc(sizeof(*self), mic_audio_callback, NO, 2); 32 | 33 | if(self != NULL) 34 | { 35 | self->destroy = (Audio* (*)(Audio*))mic_destroy; 36 | 37 | self->click = click_new(); 38 | if(self->click == NULL) 39 | return (Microphone*)auDestroy((Audio*)self); 40 | 41 | self->btt = btt_new_default(); 42 | if(self->btt == NULL) 43 | return (Microphone*)auDestroy((Audio*)self); 44 | 45 | btt_set_onset_tracking_callback (self->btt, mic_onset_detected_callback, self); 46 | btt_set_beat_tracking_callback (self->btt, mic_beat_detected_callback , self); 47 | } 48 | 49 | //there should be a play callback that I can intercept and do this there. 50 | auPlay((Audio*)self->click); 51 | return self; 52 | } 53 | 54 | /*--------------------------------------------------------------------*/ 55 | void mic_onset_detected_callback(void* SELF, unsigned long long sample_time) 56 | { 57 | Microphone* self = (Microphone*) SELF; 58 | if(btt_get_tracking_mode(self->btt) <= BTT_ONSET_TRACKING) 59 | { 60 | //click_click(self->click); 61 | //fprintf(stderr, "onset\r\n"); 62 | } 63 | } 64 | 65 | /*--------------------------------------------------------------------*/ 66 | /* store each beat as a single click in an AIFF file so we can compare it to the original */ 67 | void mic_beat_detected_callback (void* SELF, unsigned long long sample_time) 68 | { 69 | Microphone* self = (Microphone*) SELF; 70 | //click_click(self->click); 71 | //fprintf(stderr, "beat\r\n"); 72 | } 73 | 74 | /*--------------------------------------------------------------------*/ 75 | Microphone* mic_destroy(Microphone* self) 76 | { 77 | if(self != NULL) 78 | { 79 | btt_destroy(self->btt); 80 | auDestroy((Audio*)self->click); 81 | } 82 | 83 | return (Microphone*) NULL; 84 | } 85 | 86 | /*--------------------------------------------------------------------*/ 87 | BTT* mic_get_btt (Microphone* self) 88 | { 89 | return self->btt; 90 | } 91 | 92 | /*--------------------------------------------------------------------*/ 93 | int mic_audio_callback(void* SELF, auSample_t* buffer, int num_frames, int num_channels) 94 | { 95 | Microphone* self = (Microphone*)SELF; 96 | int frame, channel; 97 | 98 | //mix to mono without correcting amplitude 99 | for(frame=0; framebtt, buffer, num_frames); 104 | return num_frames; 105 | } 106 | 107 | -------------------------------------------------------------------------------- /demos/online_print_tempo/Microphone.h: -------------------------------------------------------------------------------- 1 | /* 2 | * Synth.h 3 | * Make weird noises 4 | * 5 | * Made by Michael Krzyzaniak at Arizona State University's 6 | * School of Arts, Media + Engineering in Spring of 2013 7 | * mkrzyzan@asu.edu 8 | */ 9 | 10 | #ifndef __MICROPHONE__ 11 | #define __MICROPHONE__ 1 12 | 13 | #if defined(__cplusplus) 14 | extern "C"{ 15 | #endif //(__cplusplus) 16 | 17 | #include "AudioSuperclass.h" 18 | #include "../../BTT.h" 19 | 20 | typedef struct OpaqueMicrophoneStruct Microphone; 21 | 22 | Microphone* mic_new (); 23 | BTT* mic_get_btt (Microphone* self); 24 | 25 | //Microphone* mic_destroy (Microphone* self ); 26 | //call with self->destroy(self); 27 | 28 | #if defined(__cplusplus) 29 | } 30 | #endif //(__cplusplus) 31 | 32 | #endif // __MICROPHONE__ 33 | -------------------------------------------------------------------------------- /demos/online_print_tempo/main.c: -------------------------------------------------------------------------------- 1 | //OSX: gcc *.c ../../src/*.c -framework AudioToolbox 2 | //Linux: gcc *.c ../../src/*.c -lasound -lm -lpthread -lrt 3 | 4 | #include "Microphone.h" 5 | 6 | /*--------------------------------------------------------------------*/ 7 | int main(void) 8 | { 9 | Microphone* mic = mic_new(); 10 | if(mic == NULL) {perror("Unable to create microphone object"); exit(-1);} 11 | 12 | BTT* btt = mic_get_btt(mic); 13 | btt_set_count_in_n(btt, 0); 14 | btt_set_tracking_mode(btt, BTT_ONSET_AND_TEMPO_TRACKING); 15 | 16 | auPlay((Audio*)mic); 17 | 18 | for(;;) 19 | { 20 | sleep(1); 21 | double tempo = btt_get_tempo_bpm(btt); 22 | fprintf(stderr, "%02f BPM\r\n", tempo); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /src/DFT.c.old.txt: -------------------------------------------------------------------------------- 1 | /*__--------------_-----------_---------------------__--------------------- 2 | / _|___ _ _ _ _(_)___ _ _ | |_ _ _ __ _ _ _ ___/ _|___ _ _ _ __ 3 | | _/ _ \ || | '_| / -_) '_| | _| '_/ _` | ' \(_-< _/ _ \ '_| ' \ 4 | |_| \___/\_,_|_| |_\___|_| \__|_| \__,_|_||_/__/_| \___/_| |_|_|_| 5 | -------------------------------------------------------------------------*/ 6 | //MODIFIED AUGUST 29 2018 TO FIX ERRORS IN REAL FORWARD AND INVERSE TRANSFORMS! 7 | 8 | #include "DFT.h" 9 | #include "fastsin.h" 10 | 11 | #include 12 | #include 13 | 14 | /*-----------------------------------------------------------------------*/ 15 | //produces output in bit-reversed order 16 | void dft_raw_forward_dft(dft_sample_t* real, dft_sample_t* imag, int N) 17 | { 18 | fastsin_t two_pi_over_N = (fastsin_t)(SIN_TWO_PI / N), omega_two_pi_over_n; 19 | int sub_transform, butterfly; 20 | int num_sub_transforms = 1, num_butterflies = N/2, omega; 21 | dft_sample_t wr, wi, *r=real, *i=imag, tempr, tempi; 22 | int top_index = 0, bottom_index; 23 | 24 | while(num_sub_transforms < N) 25 | { 26 | top_index = 0; 27 | for(sub_transform=0; sub_transform>= 1; 53 | } 54 | } 55 | 56 | /*-----------------------------------------------------------------------*/ 57 | //takes input in bit-reversed order 58 | void dft_raw_inverse_dft(dft_sample_t* real, dft_sample_t* imag, int N) 59 | { 60 | fastsin_t two_pi_over_N = (fastsin_t)(SIN_TWO_PI / N), omega_two_pi_over_n; 61 | int sub_transform, butterfly; 62 | int num_sub_transforms = N, num_butterflies = 1, omega; 63 | dft_sample_t wr, wi, *r=real, *i=imag, tempr, tempi; 64 | int top_index = 0, bottom_index; 65 | 66 | while((num_sub_transforms >>= 1) > 0) 67 | { 68 | top_index = 0; 69 | for(sub_transform=0; sub_transform> 1; 127 | int two_i; 128 | fastsin_t pi_over_N = SIN_PI / N_over_2; 129 | fastsin_t i_pi_over_N; 130 | float ar, ai, br, bi; 131 | dft_sample_t r1, i1; 132 | 133 | for(i=0; i> 1; 183 | int two_i; 184 | int i_plus_N_over_2; 185 | fastsin_t pi_over_N = SIN_PI / N_over_2; 186 | fastsin_t i_pi_over_N; 187 | float ar, ai, br, bi; 188 | dft_sample_t r1, i1; 189 | 190 | 191 | for(i=0; i> 1; 230 | 231 | dft_sample_t *real = real_1, *imag = real_2; 232 | dft_sample_t r1, i1, r2, i2; 233 | 234 | dft_raw_forward_dft(real, imag, N); 235 | dft_bit_reverse_indices(real, imag, N); 236 | 237 | *imag_1 = 0; 238 | *imag_2 = 0; 239 | real_1[N_over_2] = real[N_over_2]; 240 | imag_1[N_over_2] = 0; 241 | real_2[N_over_2] = imag[N_over_2]; 242 | imag_2[N_over_2] = 0; 243 | 244 | for(i=1; i> 1; 289 | dft_sample_t re, im; 290 | 291 | dft_2_real_forward_dfts(real_1, real_2, imag_1, imag_2, N); 292 | 293 | for(i=0; i> 1; 309 | dft_sample_t re, im; 310 | 311 | dft_2_real_forward_dfts(real_1, real_2, imag_1, imag_2, N); 312 | 313 | for(i=0; i> 1; 329 | dft_sample_t re, im; 330 | 331 | dft_real_forward_dft(real, imag, N); 332 | 333 | for(i=0; i> 1; 349 | 350 | dft_real_forward_dft(real, imag, N); 351 | dft_rect_to_polar(real, imag, n_over_2); 352 | 353 | for(i=0; i> 1; 367 | int n, next_bit, n_reversed=0; 368 | dft_sample_t temp; 369 | 370 | for(n=1; n N_minus_1) next_bit >>= 1; //find highest unpopulated bit 374 | n_reversed &= next_bit - 1; //clear all higher bits 375 | n_reversed |= next_bit; //set new bit 376 | 377 | if(n_reversed > n) 378 | { 379 | temp = real[n] ; 380 | real[n] = real[n_reversed]; 381 | real[n_reversed] = temp ; 382 | temp = imag[n] ; 383 | imag[n] = imag[n_reversed]; 384 | imag[n_reversed] = temp ; 385 | } 386 | } 387 | } 388 | 389 | /*-----------------------------------------------------------------------*/ 390 | void dft_rect_to_polar(dft_sample_t* real, dft_sample_t* imag, int N) 391 | { 392 | dft_sample_t temp; 393 | 394 | while(N-- > 0) 395 | { 396 | temp = *real; 397 | *real = hypot(*real, *imag); 398 | *imag = atan2(*imag, temp); 399 | ++real, ++imag; 400 | } 401 | } 402 | 403 | /*-----------------------------------------------------------------------*/ 404 | void dft_polar_to_rect(dft_sample_t* real, dft_sample_t* imag, int N) 405 | { 406 | dft_sample_t temp; 407 | 408 | while(N-- > 0) 409 | { 410 | temp = *real; 411 | *real = *real * cos(*imag); 412 | *imag = temp * sin(*imag); 413 | ++real, ++imag; 414 | } 415 | } 416 | 417 | /*-----------------------------------------------------------------------*/ 418 | void dft_magnitude_to_db (dft_sample_t* real, int N) 419 | { 420 | int i; 421 | 422 | for(i=0; i max) 442 | max = *r; 443 | ++r; 444 | } 445 | 446 | r = real; 447 | if(max > 0) 448 | { 449 | max = 1 / max; 450 | for(i=0; i 0) 536 | *real++ *= *window++; 537 | } 538 | -------------------------------------------------------------------------------- /src/DFT.h: -------------------------------------------------------------------------------- 1 | /*__--------------_-----------_---------------------__--------------------- 2 | / _|___ _ _ _ _(_)___ _ _ | |_ _ _ __ _ _ _ ___/ _|___ _ _ _ __ 3 | | _/ _ \ || | '_| / -_) '_| | _| '_/ _` | ' \(_-< _/ _ \ '_| ' \ 4 | |_| \___/\_,_|_| |_\___|_| \__|_| \__,_|_||_/__/_| \___/_| |_|_|_| 5 | -------------------------------------------------------------------------*/ 6 | 7 | #ifndef __DFT__ 8 | #define __DFT__ 1 9 | 10 | #if defined(__cplusplus) 11 | extern "C"{ 12 | #endif //(__cplusplus) 13 | 14 | typedef float dft_sample_t; 15 | 16 | void dft_init_blackman_window (dft_sample_t* window, int N); 17 | void dft_init_hann_window (dft_sample_t* window, int N); 18 | void dft_init_hamming_window (dft_sample_t* window, int N); 19 | void dft_init_half_sine_window(dft_sample_t* window, int N); 20 | void dft_apply_window (dft_sample_t* real, dft_sample_t* window, int N); 21 | 22 | void dft_raw_forward_dft (dft_sample_t* real, dft_sample_t* imag, int N); 23 | void dft_raw_inverse_dft (dft_sample_t* real, dft_sample_t* imag, int N); 24 | void dft_bit_reverse_indices (dft_sample_t* real, dft_sample_t* imag, int N); 25 | void dft_complex_forward_dft (dft_sample_t* real, dft_sample_t* imag, int N); 26 | void dft_complex_inverse_dft (dft_sample_t* real, dft_sample_t* imag, int N); 27 | 28 | void dft_real_forward_dft (dft_sample_t* real, dft_sample_t* imag, int N); 29 | void dft_real_inverse_dft (dft_sample_t* real, dft_sample_t* imag, int N); 30 | 31 | void dft_2_real_forward_dfts (dft_sample_t* real_1, dft_sample_t* real_2, dft_sample_t* imag_1, dft_sample_t* imag_2, int N); 32 | void dft_2_real_inverse_dfts (dft_sample_t* real_1, dft_sample_t* real_2, dft_sample_t* imag_1, dft_sample_t* imag_2, int N); 33 | 34 | 35 | /* 36 | Note: dft_real_forward_dft takes real input and ignores imag input, 37 | and uses a N/2-point complex DFT to produce 2-sided real and imaginary output 38 | where the negative frequencies are filled in with the complex conjugates 39 | of the positive ones. 40 | 41 | rdft_forward_dft takes real input and uses a native real DFT to produce 42 | single-sided real and imag output in-place, in the order 43 | [real[0], real[1] ... real[N/2], imag[N/2-1], imag[1]] 44 | imag[0] and imag[N/2] are understood to be 0. This is faster and uses 45 | half the space of dft_real_forward_dft. 46 | */ 47 | 48 | void rdft_bit_reverse_indices (dft_sample_t* real, int N); 49 | void rdft_real_forward_dft (dft_sample_t* real, int N); 50 | void rdft_real_inverse_dft (dft_sample_t* real, int N); 51 | void rdft_2_real_forward_dfts (dft_sample_t* real_1, dft_sample_t* real_2, int N); 52 | void rdft_2_real_inverse_dfts (dft_sample_t* real_1, dft_sample_t* real_2, int N); 53 | 54 | void rdft_real_generalized_autocorrelation (dft_sample_t* real, int N, double exponent); 55 | 56 | void rdft_rect_to_polar (dft_sample_t* real, int N); 57 | void rdft_polar_to_rect (dft_sample_t* real, int N); 58 | 59 | /* user should zero-pad data to twice its original length for correlation and convolution */ 60 | void dft_real_convolve (dft_sample_t* real_1, dft_sample_t* real_2, dft_sample_t* imag_1, dft_sample_t* imag_2, int N); 61 | void dft_real_correlate (dft_sample_t* real_1, dft_sample_t* real_2, dft_sample_t* imag_1, dft_sample_t* imag_2, int N); 62 | void dft_real_autocorrelate (dft_sample_t* real , dft_sample_t* imag , int N); 63 | /* generalized cross-correlation with phase transform */ 64 | void dft_real_generalized_autocorrelation (dft_sample_t* real , dft_sample_t* imag, int N, double exponent); 65 | 66 | void dft_rect_to_polar (dft_sample_t* real, dft_sample_t* imag, int N); 67 | void dft_polar_to_rect (dft_sample_t* real, dft_sample_t* imag, int N); 68 | void dft_magnitude_to_db (dft_sample_t* real, int N); 69 | void dft_normalize_magnitude (dft_sample_t* real, int N); 70 | 71 | double dft_bin_of_frequency (double hz, double sample_rate, int N); 72 | double dft_frequency_of_bin (double bin, double sample_rate, int N); 73 | int dft_smallest_power_of_2_at_least_as_great_as(int n); 74 | 75 | #if defined(__cplusplus) 76 | } 77 | #endif //(__cplusplus) 78 | 79 | #endif // __DFT__ 80 | -------------------------------------------------------------------------------- /src/DFT.h.old: -------------------------------------------------------------------------------- 1 | /*__--------------_-----------_---------------------__--------------------- 2 | / _|___ _ _ _ _(_)___ _ _ | |_ _ _ __ _ _ _ ___/ _|___ _ _ _ __ 3 | | _/ _ \ || | '_| / -_) '_| | _| '_/ _` | ' \(_-< _/ _ \ '_| ' \ 4 | |_| \___/\_,_|_| |_\___|_| \__|_| \__,_|_||_/__/_| \___/_| |_|_|_| 5 | -------------------------------------------------------------------------*/ 6 | 7 | #ifndef __DFT__ 8 | #define __DFT__ 1 9 | 10 | #if defined(__cplusplus) 11 | extern "C"{ 12 | #endif //(__cplusplus) 13 | 14 | typedef float dft_sample_t; 15 | 16 | void dft_init_blackman_window (dft_sample_t* window, int N); 17 | void dft_init_hann_window (dft_sample_t* window, int N); 18 | void dft_init_hamming_window (dft_sample_t* window, int N); 19 | void dft_init_half_sine_window(dft_sample_t* window, int N); 20 | void dft_apply_window (dft_sample_t* real, dft_sample_t* window, int N); 21 | 22 | void dft_raw_forward_dft (dft_sample_t* real, dft_sample_t* imag, int N); 23 | void dft_raw_inverse_dft (dft_sample_t* real, dft_sample_t* imag, int N); 24 | void dft_bit_reverse_indices (dft_sample_t* real, dft_sample_t* imag, int N); 25 | void dft_complex_forward_dft (dft_sample_t* real, dft_sample_t* imag, int N); 26 | void dft_complex_inverse_dft (dft_sample_t* real, dft_sample_t* imag, int N); 27 | void dft_real_forward_dft (dft_sample_t* real, dft_sample_t* imag, int N); 28 | void dft_real_inverse_dft (dft_sample_t* real, dft_sample_t* imag, int N); 29 | void dft_2_real_forward_dfts (dft_sample_t* real_1, dft_sample_t* real_2, dft_sample_t* imag_1, dft_sample_t* imag_2, int N); 30 | void dft_2_real_inverse_dfts (dft_sample_t* real_1, dft_sample_t* real_2, dft_sample_t* imag_1, dft_sample_t* imag_2, int N); 31 | 32 | /* user should zero-pad data to twice its original length for correlation and convolution */ 33 | void dft_real_convolve (dft_sample_t* real_1, dft_sample_t* real_2, dft_sample_t* imag_1, dft_sample_t* imag_2, int N); 34 | void dft_real_correlate (dft_sample_t* real_1, dft_sample_t* real_2, dft_sample_t* imag_1, dft_sample_t* imag_2, int N); 35 | void dft_real_autocorrelate (dft_sample_t* real , dft_sample_t* imag , int N); 36 | /* generalized cross-correlation with phase transform */ 37 | void dft_real_generalized_autocorrelation (dft_sample_t* real , dft_sample_t* imag, int N, double exponent); 38 | 39 | void dft_rect_to_polar (dft_sample_t* real, dft_sample_t* imag, int N); 40 | void dft_polar_to_rect (dft_sample_t* real, dft_sample_t* imag, int N); 41 | void dft_magnitude_to_db (dft_sample_t* real, int N); 42 | void dft_normalize_magnitude (dft_sample_t* real, int N); 43 | 44 | double dft_bin_of_frequency (double hz, double sample_rate, int N); 45 | double dft_frequency_of_bin (double bin, double sample_rate, int N); 46 | int dft_smallest_power_of_2_at_least_as_great_as(int n); 47 | 48 | #if defined(__cplusplus) 49 | } 50 | #endif //(__cplusplus) 51 | 52 | #endif // __DFT__ 53 | -------------------------------------------------------------------------------- /src/Filter.c: -------------------------------------------------------------------------------- 1 | #include "Filter.h" 2 | #include "math.h" 3 | #include 4 | 5 | #define TWO_PI (M_PI * 2.0) 6 | 7 | /*-------------------------------------------------*/ 8 | void filter_init_lowpass_coeffs (Filter* self); 9 | void filter_init_highpass_coeffs (Filter* self); 10 | void filter_init_bandpass_coeffs (Filter* self); 11 | void filter_init_bandstop_coeffs (Filter* self); 12 | void filter_init_rect_window (Filter* self); 13 | void filter_init_hann_window (Filter* self); 14 | void filter_init_hamming_window (Filter* self); 15 | void filter_init_blackmann_window(Filter* self); 16 | 17 | /*-------------------------------------------------*/ 18 | struct opaque_filter_struct 19 | { 20 | filter_type_t type; 21 | filter_window_t window_type; 22 | int order, max_order; 23 | float cutoff; 24 | //float cutoff2; 25 | float sample_rate; 26 | float* prev_samples; 27 | float* window; 28 | float* coeffs; 29 | }; 30 | 31 | /*-------------------------------------------------*/ 32 | Filter* filter_new(filter_type_t type, float cutoff, int order) 33 | { 34 | Filter* self = calloc(1, sizeof(*self)); 35 | if(self != NULL) 36 | { 37 | self->type = type; 38 | self->order = order; 39 | self->max_order = order; 40 | self->cutoff = cutoff; 41 | self->sample_rate = 44100; 42 | self->prev_samples = calloc(order+1, sizeof(*(self->prev_samples))); 43 | if(self->prev_samples == NULL) return filter_destroy(self); 44 | self->window = calloc(order+1, sizeof(*(self->window))); 45 | if(self->window == NULL) return filter_destroy(self); 46 | self->coeffs = calloc(order+1, sizeof(*(self->coeffs))); 47 | if(self->coeffs == NULL) return filter_destroy(self); 48 | filter_set_window_type(self, FILTER_WINDOW_BLACKMANN); //triggers calculation of window and coeffs 49 | } 50 | 51 | return self; 52 | } 53 | 54 | /*-------------------------------------------------*/ 55 | Filter* filter_destroy(Filter* self) 56 | { 57 | if(self != NULL) 58 | { 59 | if(self->window != NULL) 60 | free(self->window); 61 | if(self->coeffs != NULL) 62 | free(self->coeffs); 63 | if(self->prev_samples != NULL) 64 | free(self->prev_samples); 65 | 66 | free(self); 67 | } 68 | return (Filter*) NULL; 69 | } 70 | 71 | /*-------------------------------------------------*/ 72 | void filter_clear (Filter* self) 73 | { 74 | int i; 75 | for(i=0; iorder+1; i++) 76 | self->prev_samples[i] = 0; 77 | } 78 | 79 | /*-------------------------------------------------*/ 80 | void filter_set_filter_type(Filter* self, filter_type_t type) 81 | { 82 | if((self->type == type) /*|| (type < 0)*/ || (type >= FILTER_NUMBER_OF_TYPES)) 83 | return; 84 | 85 | self->type = type; 86 | filter_set_cutoff(self, self->cutoff); 87 | } 88 | 89 | /*-------------------------------------------------*/ 90 | filter_type_t filter_get_filter_type(Filter* self) 91 | { 92 | return self->type; 93 | } 94 | 95 | /*-------------------------------------------------*/ 96 | void filter_set_sample_rate(Filter* self, float sample_rate) 97 | { 98 | self->sample_rate = sample_rate; 99 | filter_set_cutoff(self, self->cutoff); 100 | } 101 | 102 | /*-------------------------------------------------*/ 103 | float filter_get_sample_rate(Filter* self) 104 | { 105 | return self->sample_rate; 106 | } 107 | 108 | /*-------------------------------------------------*/ 109 | void filter_set_cutoff(Filter* self, float cutoff) 110 | { 111 | self->cutoff = cutoff; 112 | void (*init_coeffs)(Filter*) = NULL; 113 | 114 | switch(self->type) 115 | { 116 | case FILTER_LOW_PASS : 117 | init_coeffs = filter_init_lowpass_coeffs; 118 | break; 119 | case FILTER_HIGH_PASS: 120 | init_coeffs = filter_init_highpass_coeffs; 121 | break; 122 | case FILTER_BAND_PASS: 123 | init_coeffs = filter_init_bandpass_coeffs; 124 | break; 125 | case FILTER_BAND_STOP: 126 | init_coeffs = filter_init_bandstop_coeffs; 127 | break; 128 | 129 | default:break; 130 | } 131 | if(init_coeffs != NULL) 132 | init_coeffs(self); 133 | } 134 | 135 | /*-------------------------------------------------*/ 136 | float filter_get_cutoff(Filter* self) 137 | { 138 | return self->cutoff; 139 | } 140 | 141 | /*-------------------------------------------------*/ 142 | void filter_set_order(Filter* self, int order) 143 | { 144 | if(order > self->max_order) 145 | order = self->max_order; 146 | if(order < 0) 147 | order = 0; 148 | 149 | self->order = order; 150 | filter_set_window_type(self, self->window_type); //recalculate window and coeffs 151 | } 152 | 153 | /*-------------------------------------------------*/ 154 | int filter_get_order(Filter* self) 155 | { 156 | return self->order; 157 | } 158 | 159 | /*-------------------------------------------------*/ 160 | void filter_set_window_type(Filter* self, filter_window_t window) 161 | { 162 | if(window == self->window_type) return; 163 | 164 | self->window_type = window; 165 | void (*init_window)(Filter*) = NULL; 166 | 167 | switch(self->window_type) 168 | { 169 | case FILTER_WINDOW_RECT : 170 | init_window = filter_init_rect_window; 171 | break; 172 | case FILTER_WINDOW_HANN: 173 | init_window = filter_init_hann_window; 174 | break; 175 | case FILTER_WINDOW_HAMMING: 176 | init_window = filter_init_hamming_window; 177 | break; 178 | case FILTER_WINDOW_BLACKMANN: 179 | init_window = filter_init_blackmann_window; 180 | break; 181 | 182 | default:break; 183 | } 184 | if(init_window != NULL) 185 | init_window(self); 186 | 187 | filter_set_cutoff(self, self->cutoff); 188 | } 189 | 190 | /*-------------------------------------------------*/ 191 | filter_window_t filter_get_window_type(Filter* self) 192 | { 193 | return self->window_type; 194 | } 195 | 196 | /*-------------------------------------------------*/ 197 | void filter_init_lowpass_coeffs(Filter* self) 198 | { 199 | int i, n=self->order+1; 200 | float m_over_2 = self->order / 2.0; 201 | float f_t = self->cutoff / self->sample_rate; 202 | float two_pi_f_t = TWO_PI * f_t; 203 | float i_minus_m_over_2; 204 | float temp; 205 | 206 | for(i=0; iwindow[i]; 218 | self->coeffs[i] = temp; 219 | } 220 | } 221 | 222 | /*-------------------------------------------------*/ 223 | void filter_init_highpass_coeffs(Filter* self) 224 | { 225 | /* to do: */ 226 | /* this is almost identical to filter_init_lowpass_coeffs*/ 227 | /* and could be serviced by the same function */ 228 | 229 | int i, n=self->order+1; 230 | float m_over_2 = self->order / 2.0; 231 | float f_t = self->cutoff / self->sample_rate; 232 | float two_pi_f_t = TWO_PI * f_t; 233 | float i_minus_m_over_2; 234 | float temp; 235 | 236 | for(i=0; iwindow[i]; 249 | self->coeffs[i] = temp; 250 | } 251 | } 252 | 253 | /*-------------------------------------------------*/ 254 | void filter_init_bandpass_coeffs(Filter* self) 255 | { 256 | int i, n=self->order+1; 257 | float m_over_2 = self->order / 2.0; 258 | float f_t = self->cutoff / self->sample_rate; 259 | float two_pi_f_t = TWO_PI * f_t; 260 | float i_minus_m_over_2; 261 | float temp; 262 | 263 | for(i=0; iwindow[i]; 276 | self->coeffs[i] = temp; 277 | } 278 | } 279 | 280 | /*-------------------------------------------------*/ 281 | void filter_init_bandstop_coeffs(Filter* self) 282 | { 283 | 284 | } 285 | 286 | /*-------------------------------------------------*/ 287 | void filter_init_rect_window(Filter* self) 288 | { 289 | int i, n = self->order + 1; 290 | for(i=0; iwindow[i] = 1; 292 | } 293 | 294 | /*-------------------------------------------------*/ 295 | void filter_init_hann_window(Filter* self) 296 | { 297 | int i, n = self->order + 1; 298 | double phase = 0; 299 | double phase_increment = TWO_PI / (double)self->order; 300 | 301 | for(i=0; iwindow[i] = 0.5 * (1-cos(phase)); 304 | phase += phase_increment; 305 | } 306 | } 307 | 308 | /*-------------------------------------------------*/ 309 | void filter_init_hamming_window(Filter* self) 310 | { 311 | int i, n = self->order + 1; 312 | double phase = 0; 313 | double phase_increment = TWO_PI / (double)self->order; 314 | for(i=0; iwindow[i] = 0.54 - 0.46 * cos(phase); 317 | phase += phase_increment; 318 | } 319 | } 320 | 321 | /*-------------------------------------------------*/ 322 | void filter_init_blackmann_window(Filter* self) 323 | { 324 | int i, n = self->order + 1; 325 | double phase = 0; 326 | double phase_increment = TWO_PI / (double)self->order; 327 | double a = 0; 328 | double a0 = (1-a)/2.0; 329 | double a1 = 1/2.0; 330 | double a2 = a/2.0; 331 | 332 | for(i=0; iwindow[i] = a0 - a1*cos(phase) + a2*cos(2*phase); 335 | phase += phase_increment; 336 | } 337 | } 338 | 339 | /*-------------------------------------------------*/ 340 | void filter_process_data(Filter* self, float* data, int num_samples) 341 | { 342 | int i, n = self->order + 1; 343 | float output; 344 | 345 | while(num_samples-- > 0) 346 | { 347 | //shift previous samples 348 | for(i=self->order; i>0; i--) 349 | self->prev_samples[i] = self->prev_samples[i-1]; 350 | self->prev_samples[0] = *data; 351 | 352 | //calculate filter output 353 | output = 0; 354 | for(i=0; iprev_samples[i] * self->coeffs[i]; 356 | 357 | *data++ = output; 358 | } 359 | } 360 | 361 | -------------------------------------------------------------------------------- /src/Filter.h: -------------------------------------------------------------------------------- 1 | #ifndef __FILTER__ 2 | 3 | #ifdef __cplusplus 4 | extern "C"{ 5 | #endif //__cplusplus 6 | 7 | typedef struct opaque_filter_struct Filter; 8 | 9 | typedef enum filter_type_enum 10 | { 11 | FILTER_LOW_PASS, 12 | FILTER_HIGH_PASS, 13 | FILTER_BAND_PASS, 14 | FILTER_BAND_STOP, 15 | FILTER_NUMBER_OF_TYPES, 16 | }filter_type_t; 17 | 18 | typedef enum filter_window_enum 19 | { 20 | FILTER_WINDOW_RESERVED = 0, 21 | FILTER_WINDOW_RECT, 22 | FILTER_WINDOW_BARTLETT, 23 | FILTER_WINDOW_HANN, 24 | FILTER_WINDOW_HAMMING, 25 | FILTER_WINDOW_BLACKMANN, 26 | }filter_window_t; 27 | 28 | Filter* filter_new (filter_type_t type, float cutoff, int order); 29 | Filter* filter_destroy (Filter* self); 30 | 31 | void filter_clear (Filter* self); 32 | void filter_set_filter_type(Filter* self, filter_type_t type); 33 | filter_type_t filter_get_filter_type(Filter* self); 34 | void filter_set_sample_rate(Filter* self, float sample_rate); 35 | float filter_get_sample_rate(Filter* self); 36 | void filter_set_cutoff (Filter* self, float cutoff); 37 | float filter_get_cutoff (Filter* self); 38 | void filter_set_order (Filter* self, int order); //cannot be greater than the initial order... 39 | int filter_get_order (Filter* self); 40 | void filter_set_window_type(Filter* self, filter_window_t window); 41 | filter_window_t filter_get_window_type(Filter* self); 42 | 43 | void filter_process_data (Filter* self, float* data, int num_samples); 44 | 45 | #ifdef __cplusplus 46 | } 47 | #endif 48 | #endif //__FILTER__ 49 | -------------------------------------------------------------------------------- /src/STFT.c: -------------------------------------------------------------------------------- 1 | #include "STFT.h" 2 | 3 | #include //calloc 4 | 5 | /*--------------------------------------------------------------------*/ 6 | struct Opaque_STFT_Struct 7 | { 8 | int should_resynthesize; 9 | int window_size; 10 | int fft_N; 11 | int overlap; 12 | int hop_size; 13 | int sample_counter; 14 | int input_index; 15 | int output_index;; 16 | dft_sample_t* window; 17 | dft_sample_t* running_input; 18 | dft_sample_t* running_output; 19 | dft_sample_t* real; 20 | }; 21 | 22 | /*--------------------------------------------------------------------*/ 23 | STFT* stft_new(int window_size /*power of 2 please*/, int overlap /* 1, 2, 4, 8 */, int should_resynthesize) 24 | { 25 | STFT* self = calloc(1, sizeof(*self)); 26 | if(self != NULL) 27 | { 28 | self->should_resynthesize = should_resynthesize; 29 | self->window_size = window_size; 30 | self->fft_N = (should_resynthesize) ? overlap * window_size : window_size; 31 | self->overlap = overlap; 32 | self->hop_size = window_size / overlap; 33 | self->sample_counter = 0; 34 | self->input_index = 0; 35 | self->output_index = 0; 36 | self->window = calloc(self->window_size, sizeof(*(self->window))); 37 | self->running_input = calloc(self->window_size, sizeof(*(self->running_input))); 38 | self->running_output = calloc(self->fft_N , sizeof(*(self->running_output))); 39 | self->real = calloc(self->fft_N , sizeof(*(self->real))); 40 | if(self->window == NULL) return stft_destroy(self); 41 | if(self->running_input == NULL) return stft_destroy(self); 42 | if(self->running_output == NULL) return stft_destroy(self); 43 | if(self->real == NULL) return stft_destroy(self); 44 | 45 | //no choice for now 46 | dft_init_blackman_window(self->window, self->window_size); 47 | } 48 | return self; 49 | } 50 | 51 | /*--------------------------------------------------------------------*/ 52 | STFT* stft_destroy(STFT* self) 53 | { 54 | if(self != NULL) 55 | { 56 | if(self->window != NULL) 57 | free(self->window); 58 | if(self->running_input != NULL) 59 | free(self->running_input); 60 | if(self->running_output != NULL) 61 | free(self->running_output); 62 | if(self->real != NULL) 63 | free(self->real); 64 | 65 | free(self); 66 | } 67 | return (STFT*) NULL; 68 | } 69 | 70 | /*--------------------------------------------------------------------*/ 71 | /* resynthesized samples, if any, returned in real_input */ 72 | void stft_process(STFT* self, dft_sample_t* real_input, int len, stft_onprocess_t onprocess, void* onprocess_self) 73 | { 74 | int i, j; 75 | 76 | for(i=0; irunning_input[self->input_index++] = real_input[i]; 79 | self->input_index %= self->window_size; 80 | 81 | if(self->should_resynthesize) 82 | { 83 | real_input[i] = self->running_output[self->output_index]; 84 | self->running_output[self->output_index++] = 0; 85 | self->output_index %= self->fft_N; 86 | } 87 | 88 | if(++self->sample_counter == self->hop_size) 89 | { 90 | self->sample_counter = 0; 91 | 92 | for(j=0; jwindow_size; j++) 93 | { 94 | self->real[j] = self->running_input[(self->input_index+j) % self->window_size]; 95 | } 96 | 97 | dft_apply_window(self->real, self->window, self->window_size); 98 | for(j=self->window_size; jfft_N; j++) 99 | { 100 | self->real[j] = 0; 101 | } 102 | rdft_real_forward_dft(self->real, self->fft_N); 103 | onprocess (onprocess_self, self->real, self->fft_N); 104 | if(self->should_resynthesize) 105 | { 106 | rdft_real_inverse_dft(self->real, self->fft_N); 107 | for(j=0; jfft_N; j++) 108 | self->running_output[(self->output_index+j) % self->fft_N] += self->real[j]; 109 | } 110 | } 111 | } 112 | } 113 | 114 | /*--------------------------------------------------------------------*/ 115 | int stft_get_N (STFT* self) 116 | { 117 | return self->fft_N; 118 | } 119 | 120 | /*--------------------------------------------------------------------*/ 121 | int stft_get_overlap (STFT* self) 122 | { 123 | return self->overlap; 124 | } 125 | 126 | /*--------------------------------------------------------------------*/ 127 | int stft_get_hop (STFT* self) 128 | { 129 | return self->hop_size; 130 | } 131 | 132 | /*--------------------------------------------------------------------*/ 133 | /*--------------------------------------------------------------------*/ 134 | /*--------------------------------------------------------------------*/ 135 | /*--------------------------------------------------------------------*/ 136 | /*--------------------------------------------------------------------*/ 137 | /*--------------------------------------------------------------------*/ 138 | struct Opaque_TWO_STFTS_Struct 139 | { 140 | int should_resynthesize; 141 | int window_size; 142 | int fft_N; 143 | int overlap; 144 | int hop_size; 145 | int sample_counter; 146 | int input_index; 147 | int output_index;; 148 | dft_sample_t* window; 149 | dft_sample_t* running_input; 150 | dft_sample_t* running_output; 151 | dft_sample_t* real; 152 | dft_sample_t* imag; 153 | dft_sample_t* running_input_2; 154 | dft_sample_t* running_output_2; 155 | dft_sample_t* real_2; 156 | dft_sample_t* imag_2; 157 | }; 158 | 159 | 160 | 161 | /*--------------------------------------------------------------------*/ 162 | TWO_STFTS* two_stfts_new(int window_size /*power of 2 please*/, int overlap /* 1, 2, 4, 8 */, int should_resynthesize) 163 | { 164 | TWO_STFTS* self = calloc(1, sizeof(*self)); 165 | if(self != NULL) 166 | { 167 | self->should_resynthesize = should_resynthesize; 168 | self->window_size = window_size; 169 | self->fft_N = (should_resynthesize) ? overlap * window_size : window_size; 170 | self->overlap = overlap; 171 | self->hop_size = window_size / overlap; 172 | self->sample_counter = 0; 173 | 174 | self->input_index = 0; 175 | self->output_index = 0; 176 | 177 | self->window = calloc(self->window_size, sizeof(*(self->window))); 178 | self->running_input = calloc(self->window_size, sizeof(*(self->running_input))); 179 | self->running_output = calloc(self->fft_N , sizeof(*(self->running_output))); 180 | self->real = calloc(self->fft_N , sizeof(*(self->real))); 181 | self->imag = calloc(self->fft_N , sizeof(*(self->imag))); 182 | self->running_input_2 = calloc(self->window_size, sizeof(*(self->running_input_2))); 183 | self->running_output_2 = calloc(self->fft_N , sizeof(*(self->running_output_2))); 184 | self->real_2 = calloc(self->fft_N , sizeof(*(self->real_2))); 185 | self->imag_2 = calloc(self->fft_N , sizeof(*(self->imag_2))); 186 | 187 | if(self->window == NULL) return two_stfts_destroy(self); 188 | if(self->running_input == NULL) return two_stfts_destroy(self); 189 | if(self->running_output == NULL) return two_stfts_destroy(self); 190 | if(self->real == NULL) return two_stfts_destroy(self); 191 | if(self->imag == NULL) return two_stfts_destroy(self); 192 | if(self->running_input_2 == NULL) return two_stfts_destroy(self); 193 | if(self->running_output_2 == NULL) return two_stfts_destroy(self); 194 | if(self->real_2 == NULL) return two_stfts_destroy(self); 195 | if(self->imag_2 == NULL) return two_stfts_destroy(self); 196 | 197 | //no choice for now 198 | dft_init_blackman_window(self->window, self->window_size); 199 | } 200 | 201 | return self; 202 | } 203 | 204 | /*--------------------------------------------------------------------*/ 205 | TWO_STFTS* two_stfts_destroy(TWO_STFTS* self) 206 | { 207 | if(self != NULL) 208 | { 209 | if(self->window != NULL) 210 | free(self->window); 211 | if(self->running_input != NULL) 212 | free(self->running_input); 213 | if(self->running_output != NULL) 214 | free(self->running_output); 215 | if(self->real != NULL) 216 | free(self->real); 217 | if(self->imag != NULL) 218 | free(self->imag); 219 | if(self->running_input_2 != NULL) 220 | free(self->running_input_2); 221 | if(self->running_output_2 != NULL) 222 | free(self->running_output_2); 223 | if(self->real_2 != NULL) 224 | free(self->real_2); 225 | if(self->imag_2 != NULL) 226 | free(self->imag_2); 227 | free(self); 228 | } 229 | return (TWO_STFTS*) NULL; 230 | } 231 | 232 | /*--------------------------------------------------------------------*/ 233 | void two_stfts_process(TWO_STFTS* self, dft_sample_t* real_input, dft_sample_t* real_input_2, int len, int two_inverses, two_stfts_onprocess_t onprocess, void* onprocess_self) 234 | { 235 | int i, j; 236 | 237 | for(i=0; irunning_input[self->input_index] = real_input[i]; 240 | self->running_input_2[self->input_index] = real_input_2[i]; 241 | ++self->input_index; self->input_index %= self->window_size; 242 | 243 | //? 244 | if(self->should_resynthesize) 245 | { 246 | real_input[i] = self->running_output[self->output_index]; 247 | self->running_output[self->output_index] = 0; 248 | if(two_inverses) 249 | { 250 | real_input_2[i] = self->running_output_2[self->output_index]; 251 | self->running_output_2[self->output_index] = 0; 252 | } 253 | ++self->output_index; self->output_index %= self->fft_N; 254 | } 255 | 256 | ++self->sample_counter; 257 | 258 | //new dft if necessary 259 | if(self->sample_counter == self->hop_size) 260 | { 261 | self->sample_counter = 0; 262 | 263 | for(j=0; jwindow_size; j++) 264 | { 265 | self->real[j] = self->running_input[(self->input_index+j) % self->window_size]; 266 | self->imag[j] = 0; 267 | self->real_2[j] = self->running_input_2[(self->input_index+j) % self->window_size]; 268 | self->imag_2[j] = 0; 269 | } 270 | 271 | dft_apply_window(self->real, self->window, self->window_size); 272 | dft_apply_window(self->real_2, self->window, self->window_size); 273 | for(j=self->window_size; jfft_N; j++) 274 | { 275 | self->real[j] = 0; 276 | self->imag[j] = 0; 277 | self->real_2[j] = 0; 278 | self->imag_2[j] = 0; 279 | } 280 | 281 | dft_2_real_forward_dfts (self->real, self->real_2, self->imag, self->imag_2, self->fft_N); 282 | onprocess (onprocess_self, self->real, self->real_2, self->imag, self->imag_2, self->fft_N); 283 | 284 | if(self->should_resynthesize) 285 | { 286 | if(two_inverses) 287 | dft_2_real_inverse_dfts(self->real, self->real_2, self->imag, self->imag_2, self->fft_N); 288 | else 289 | dft_real_inverse_dft(self->real, self->imag, self->fft_N); 290 | 291 | for(j=0; jfft_N; j++) 292 | { 293 | self->running_output[(self->output_index+j) % self->fft_N] += self->real[j]; 294 | if(two_inverses) 295 | self->running_output_2[(self->output_index+j) % self->fft_N] += self->real_2[j]; 296 | } 297 | } 298 | } 299 | } 300 | } 301 | -------------------------------------------------------------------------------- /src/STFT.h: -------------------------------------------------------------------------------- 1 | /*__--------------_-----------_---------------------__--------------------- 2 | 3 | -------------------------------------------------------------------------*/ 4 | 5 | #ifndef __STFT__ 6 | #define __STFT__ 1 7 | 8 | #if defined(__cplusplus) 9 | extern "C"{ 10 | #endif //(__cplusplus) 11 | 12 | #include "DFT.h" 13 | 14 | /*--------------------------------------------------------------------*/ 15 | typedef struct Opaque_STFT_Struct STFT; 16 | 17 | typedef void (*stft_onprocess_t)(void* onprocess_self, dft_sample_t* real, int N); 18 | 19 | STFT* stft_new (int window_size /*power of 2 please*/, int overlap /* 1, 2, 4, 8 */, int should_resynthesize); 20 | STFT* stft_destroy (STFT* self); 21 | void stft_process (STFT* self, dft_sample_t* real_input, int len, stft_onprocess_t onprocess, void* onprocess_self); 22 | int stft_get_N (STFT* self); 23 | int stft_get_overlap (STFT* self); 24 | int stft_get_hop (STFT* self); 25 | 26 | /*--------------------------------------------------------------------*/ 27 | typedef struct Opaque_TWO_STFTS_Struct TWO_STFTS; 28 | 29 | typedef void (*two_stfts_onprocess_t)(void* onprocess_self, dft_sample_t* real, dft_sample_t* imag, dft_sample_t* real_2, dft_sample_t* imag_2, int N); 30 | 31 | TWO_STFTS* two_stfts_new(int window_size /*power of 2 please*/, int overlap /* 1, 2, 4, 8 */, int should_resynthesize); 32 | TWO_STFTS* two_stfts_destroy(TWO_STFTS* self); 33 | void two_stfts_process(TWO_STFTS* self, dft_sample_t* real_input, dft_sample_t* real_input_2, int len, int two_inverses, two_stfts_onprocess_t onprocess, void* onprocess_self); 34 | 35 | #if defined(__cplusplus) 36 | } 37 | #endif //(__cplusplus) 38 | 39 | #endif // __DFT__ 40 | -------------------------------------------------------------------------------- /src/Statistics.c: -------------------------------------------------------------------------------- 1 | /*-____--_--------_---_-----_---_--------------------------------------- 2 | / ___|| |_ __ _| |_(_)___| |_(_) ___ ___ ___ 3 | \___ \| __/ _` | __| / __| __| |/ __/ __| / __| 4 | ___) | || (_| | |_| \__ \ |_| | (__\__ \| (__ 5 | |____/ \__\__,_|\__|_|___/\__|_|\___|___(_)___| 6 | ------------------------------------------------------------------------ 7 | 8 | ----------------------------------------------------------------------*/ 9 | #include "Statistics.h" 10 | 11 | #include 12 | #include 13 | #include //memset, memcpy 14 | 15 | /*--------------------------------------------------------------------*/ 16 | /*--___-------_-_------------------------------------------------------- 17 | / _ \ _ _ | (_)_ _ ___ /_\__ _____ _ _ __ _ __ _ ___ 18 | | (_) | ' \| | | ' \/ -_) / _ \ V / -_) '_/ _` / _` / -_) 19 | \___/|_||_|_|_|_||_\___| /_/ \_\_/\___|_| \__,_\__, \___| 20 | --------------------------------------------------|___/----------------- 21 | Calculate the mean and variance of a signal one sample at a time. 22 | ----------------------------------------------------------------------*/ 23 | struct opaque_online_average_struct 24 | { 25 | unsigned n; 26 | double mean; 27 | double variance; 28 | double old_mean; 29 | double s; 30 | double old_s; 31 | }; 32 | 33 | /*--------------------------------------------------------------------*/ 34 | OnlineAverage* online_average_new() 35 | { 36 | OnlineAverage* self = calloc(1, sizeof(*self)); 37 | if(self != NULL) 38 | { 39 | online_average_init(self); 40 | } 41 | return self; 42 | } 43 | 44 | /*--------------------------------------------------------------------*/ 45 | void online_average_init(OnlineAverage* self) 46 | { 47 | self->n = 0; 48 | self->mean = 0; 49 | self->variance = 0; 50 | self->old_mean = 0; 51 | self->s = 0; 52 | self->old_s = 0; 53 | } 54 | 55 | /*--------------------------------------------------------------------*/ 56 | OnlineAverage* online_average_destroy(OnlineAverage* self) 57 | { 58 | if(self != NULL) 59 | { 60 | free(self); 61 | } 62 | return (OnlineAverage*) NULL; 63 | } 64 | 65 | /*--------------------------------------------------------------------*/ 66 | int online_average_n(OnlineAverage* self) 67 | { 68 | return self->n; 69 | } 70 | 71 | /*--------------------------------------------------------------------*/ 72 | double online_average_mean(OnlineAverage* self) 73 | { 74 | return self->mean; 75 | } 76 | 77 | /*--------------------------------------------------------------------*/ 78 | double online_average_variance(OnlineAverage* self) 79 | { 80 | return self->variance; 81 | } 82 | 83 | /*--------------------------------------------------------------------*/ 84 | double online_average_std_dev(OnlineAverage* self) 85 | { 86 | return sqrt(self->variance); 87 | } 88 | 89 | /*--------------------------------------------------------------------*/ 90 | void online_average_update(OnlineAverage* self, double x) 91 | { 92 | ++self->n; 93 | 94 | if (self->n == 1) 95 | { 96 | self->old_mean = self->mean = x; 97 | self->old_s = 0.0; 98 | } 99 | else 100 | { 101 | self->mean = self->old_mean + (x - self->old_mean) / self->n; 102 | self->s = self->old_s + ((x - self->old_mean) * (x - self->mean)); 103 | self->old_mean = self->mean; 104 | self->old_s = self->s; 105 | self->variance = self->s / (self->n - 1); 106 | } 107 | } 108 | 109 | /*--------------------------------------------------------------------*/ 110 | /*-__--__---------_---------------_------------------------------------- 111 | | \/ |_____ _(_)_ _ __ _ /_\__ _____ _ _ __ _ __ _ ___ 112 | | |\/| / _ \ V / | ' \/ _` | / _ \ V / -_) '_/ _` / _` / -_) 113 | |_| |_\___/\_/|_|_||_\__, | /_/ \_\_/\___|_| \__,_\__, \___| 114 | ------------------------|___/------------------------|___/-------------- 115 | Calculate the moving or rolling mean and variance over the past N 116 | samples of a signal. This algorithm is online and updates one sample 117 | at a time. 118 | ----------------------------------------------------------------------*/ 119 | struct opaque_moving_average_struct 120 | { 121 | unsigned N; // the nominal window size 122 | double mean; 123 | double variance; 124 | double* past_samples; 125 | unsigned current_sample_index; 126 | double s; 127 | OnlineAverage* online; //used until there are at least self->N samples 128 | }; 129 | 130 | /*--------------------------------------------------------------------*/ 131 | MovingAverage* moving_average_new(unsigned N) 132 | { 133 | MovingAverage* self = calloc(1, sizeof(*self)); 134 | if(self != NULL) 135 | { 136 | self->N = N; 137 | self->past_samples = calloc(self->N, sizeof(*self->past_samples)); 138 | if(self->past_samples == NULL) 139 | return moving_average_destroy(self); 140 | 141 | self->online = online_average_new(); 142 | if(self->online == NULL) 143 | return moving_average_destroy(self); 144 | 145 | moving_average_init(self); 146 | } 147 | return self; 148 | } 149 | 150 | /*--------------------------------------------------------------------*/ 151 | void moving_average_init(MovingAverage* self) 152 | { 153 | self->mean = 0; 154 | self->variance = 0; 155 | self->current_sample_index = 0; 156 | self->s = 0; 157 | online_average_init(self->online); 158 | } 159 | 160 | /*--------------------------------------------------------------------*/ 161 | MovingAverage* moving_average_destroy(MovingAverage* self) 162 | { 163 | if(self != NULL) 164 | { 165 | if(self->past_samples != NULL) 166 | free(self->past_samples); 167 | 168 | self->online = online_average_destroy(self->online); 169 | 170 | free(self); 171 | } 172 | return (MovingAverage*) NULL; 173 | } 174 | 175 | /*--------------------------------------------------------------------*/ 176 | int moving_average_N(MovingAverage* self) 177 | { 178 | return self->N; 179 | } 180 | 181 | /*--------------------------------------------------------------------*/ 182 | int moving_average_n(MovingAverage* self) 183 | { 184 | return online_average_n(self->online); 185 | } 186 | 187 | /*--------------------------------------------------------------------*/ 188 | double moving_average_mean(MovingAverage* self) 189 | { 190 | return self->mean; 191 | } 192 | 193 | /*--------------------------------------------------------------------*/ 194 | double moving_average_variance(MovingAverage* self) 195 | { 196 | return self->variance; 197 | } 198 | 199 | /*--------------------------------------------------------------------*/ 200 | double moving_average_std_dev(MovingAverage* self) 201 | { 202 | return sqrt(self->variance); 203 | } 204 | /*--------------------------------------------------------------------*/ 205 | void moving_average_update(MovingAverage* self, double x) 206 | { 207 | double new_mean; 208 | double oldest_sample = self->past_samples[self->current_sample_index]; 209 | 210 | self->past_samples[self->current_sample_index] = x; 211 | ++self->current_sample_index; 212 | self->current_sample_index %= self->N; 213 | 214 | if(online_average_n(self->online) < self->N) 215 | { 216 | online_average_update(self->online, x); 217 | self->variance = online_average_variance(self->online); 218 | self->mean = online_average_mean(self->online); 219 | self->s = self->variance * self->N; 220 | } 221 | else 222 | { 223 | new_mean = self->mean + (x - oldest_sample) / self->N; 224 | self->s += (x + oldest_sample - self->mean - new_mean) * (x - oldest_sample); 225 | self->variance = self->s / self->N; 226 | self->mean = new_mean; 227 | } 228 | }; 229 | 230 | /*--------------------------------------------------------------------*/ 231 | /*--___-------_-_------------___------------------------_--------------- 232 | / _ \ _ _ | (_)_ _ ___ | _ \___ __ _ _ _ ___ _____(_)___ _ _ 233 | | (_) | ' \| | | ' \/ -_) | / -_) _` | '_/ -_|_-<_-< / _ \ ' \ 234 | \___/|_||_|_|_|_||_\___| |_|_\___\__, |_| \___/__/__/_\___/_||_| 235 | ------------------------------------|___/------------------------------- 236 | Calculate the means, variances, covariance, slope, y-intercept and 237 | correlation coeficient of two signals. This is an online algorithm 238 | that operates one sample at a time. 239 | ----------------------------------------------------------------------*/ 240 | struct opaque_online_regression_struct 241 | { 242 | unsigned n ; 243 | double a_mean ; 244 | double b_mean ; 245 | double a_variance; 246 | double b_variance; 247 | double covariance; 248 | 249 | //slope, y-intercept, correlation 250 | double m ; 251 | double b ; 252 | double r_2 ; 253 | }; 254 | 255 | /*--------------------------------------------------------------------*/ 256 | OnlineRegression* online_regression_new() 257 | { 258 | OnlineRegression* self = calloc(1, sizeof(*self)); 259 | if(self != NULL) 260 | { 261 | online_regression_init(self); 262 | } 263 | return self; 264 | } 265 | 266 | /*--------------------------------------------------------------------*/ 267 | void online_regression_init(OnlineRegression* self) 268 | { 269 | self->n = 0; 270 | self->a_mean = 0; 271 | self->b_mean = 0; 272 | self->a_variance = 0; 273 | self->b_variance = 0; 274 | self->covariance = 0; 275 | self->m = 1; 276 | self->b = 0; 277 | self->r_2 = 0; 278 | } 279 | 280 | /*--------------------------------------------------------------------*/ 281 | OnlineRegression* online_regression_destroy(OnlineRegression* self) 282 | { 283 | if(self != NULL) 284 | { 285 | free(self); 286 | } 287 | return (OnlineRegression*) NULL; 288 | } 289 | 290 | /*--------------------------------------------------------------------*/ 291 | int online_regression_n(OnlineRegression* self) 292 | { 293 | return self->n; 294 | } 295 | 296 | /*--------------------------------------------------------------------*/ 297 | double online_regression_covariance(OnlineRegression* self) 298 | { 299 | return self->covariance; 300 | } 301 | 302 | /*--------------------------------------------------------------------*/ 303 | double online_regression_slope(OnlineRegression* self) 304 | { 305 | return self->m; 306 | } 307 | 308 | /*--------------------------------------------------------------------*/ 309 | double online_regression_y_intercept(OnlineRegression* self) 310 | { 311 | return self->b; 312 | } 313 | 314 | /*--------------------------------------------------------------------*/ 315 | double online_regression_r_squared(OnlineRegression* self) 316 | { 317 | return self->r_2; 318 | } 319 | 320 | /*--------------------------------------------------------------------*/ 321 | void online_regression_update(OnlineRegression* self, double a_data /*x*/, double b_data /*y*/) 322 | { 323 | ++self->n; 324 | double one_over_n = 1.0 / (double)self->n; 325 | 326 | double n_minus_1_over_n = (self->n-1) * one_over_n; 327 | double d_a = a_data - self->a_mean; 328 | double d_b = b_data - self->b_mean; 329 | 330 | double new_m, new_b; 331 | self->a_variance += (n_minus_1_over_n * d_a * d_a - self->a_variance) * one_over_n; 332 | self->b_variance += (n_minus_1_over_n * d_b * d_b - self->b_variance) * one_over_n; 333 | self->covariance += (n_minus_1_over_n * d_a * d_b - self->covariance) * one_over_n; 334 | 335 | self->a_mean += d_a * one_over_n; 336 | self->b_mean += d_b * one_over_n; 337 | 338 | if((self->a_variance == 0) || (self->covariance == 0)) 339 | return; 340 | 341 | new_m = self->covariance / self->a_variance; 342 | new_b = self->b_mean - (new_m * self->a_mean); 343 | self->m = new_m; 344 | self->b = new_b; 345 | self->r_2 = (self->covariance * self->covariance) / (self->a_variance * self->b_variance); 346 | } 347 | 348 | /*--------------------------------------------------------------------*/ 349 | /*----------_-----------_---_-----------_____-_---------------_----_---- _ _ 350 | /_\ __| |__ _ _ __| |_(_)_ _____ |_ _| |_ _ _ ___ __| |_ | |_ ___| |__| | 351 | / _ \/ _` / _` | '_ \ _| \ V / -_) | | | ' \| '_/ -_|_-< ' \| ' \/ _ \ / _` | 352 | /_/ \_\__,_\__,_| .__/\__|_|\_/\___| |_| |_||_|_| \___/__/_||_|_||_\___/_\__,_| 353 | ------------------|_|--------------------------------------------------- 354 | Adaptive threshhold tells you when your signal goes a specified number 355 | of standard deviations above or below a moving average of a filtered 356 | version of your signal. update() returns +1 or -1 whenever your signal 357 | transitions to being above or below the threshold. 358 | ----------------------------------------------------------------------*/ 359 | struct opaque_adaptive_threshold_struct 360 | { 361 | double smoothing; //coefficient 0 ~ 1 362 | double threshold; //standard deviations from filtered signal 363 | double min; 364 | // step function (-1, 0, 1) that will hold its value while input is 365 | // above threshold. update() only returns +1 or -1 at the moment 366 | // onset_signal transitions to +1 or -1; 367 | double onset_signal; 368 | 369 | //(start with high variance to avoid spurious onsets on first iterations) 370 | MovingAverage* avg; 371 | double filtered_x; 372 | }; 373 | 374 | /*--------------------------------------------------------------------*/ 375 | AdaptiveThreshold* adaptive_threshold_new(unsigned N) 376 | { 377 | AdaptiveThreshold* self = calloc(1, sizeof(*self)); 378 | if(self != NULL) 379 | { 380 | self->avg = moving_average_new(N); 381 | if(self->avg == NULL) 382 | return adaptive_threshold_destroy(self); 383 | 384 | adaptive_threshold_init(self); 385 | } 386 | return self; 387 | } 388 | 389 | /*--------------------------------------------------------------------*/ 390 | void adaptive_threshold_init(AdaptiveThreshold* self) 391 | { 392 | self->smoothing = 0.5; //coefficient 0 ~ 1 393 | self->threshold = 3.5; //standard deviations from filtered signal 394 | self->min = 0; 395 | adaptive_threshold_clear(self); 396 | } 397 | 398 | /*--------------------------------------------------------------------*/ 399 | void adaptive_threshold_clear(AdaptiveThreshold* self) 400 | { 401 | self->onset_signal = 0; 402 | moving_average_init(self->avg); 403 | self->filtered_x = 0; 404 | } 405 | 406 | /*--------------------------------------------------------------------*/ 407 | AdaptiveThreshold* adaptive_threshold_destroy(AdaptiveThreshold* self) 408 | { 409 | if(self != NULL) 410 | { 411 | self->avg = moving_average_destroy(self->avg); 412 | free(self); 413 | } 414 | return (AdaptiveThreshold*) NULL; 415 | } 416 | 417 | /*--------------------------------------------------------------------*/ 418 | double adaptive_threshold_smoothing(AdaptiveThreshold* self) 419 | { 420 | return self->smoothing; 421 | } 422 | 423 | /*--------------------------------------------------------------------*/ 424 | void adaptive_threshold_set_smoothing(AdaptiveThreshold* self, double coefficient) 425 | { 426 | coefficient = (coefficient > 1) ? 1 : ((coefficient < 0) ? 0 : coefficient); 427 | self->smoothing = coefficient; 428 | } 429 | 430 | /*--------------------------------------------------------------------*/ 431 | double adaptive_threshold_threshold(AdaptiveThreshold* self) 432 | { 433 | return self->threshold; 434 | } 435 | 436 | /*--------------------------------------------------------------------*/ 437 | double adaptive_threshold_threshold_value(AdaptiveThreshold* self) 438 | { 439 | double result = moving_average_std_dev(self->avg) * self->threshold; 440 | if(result < self->min) 441 | result = self->min; 442 | return result + moving_average_mean(self->avg); 443 | } 444 | 445 | /*--------------------------------------------------------------------*/ 446 | void adaptive_threshold_set_threshold(AdaptiveThreshold* self, double std_devs) 447 | { 448 | //if(std_devs < 0) std_devs = 0; 449 | self->threshold = std_devs; 450 | } 451 | 452 | /*--------------------------------------------------------------------*/ 453 | double adaptive_threshold_threshold_min (AdaptiveThreshold* self) 454 | { 455 | return self->min; 456 | } 457 | /*--------------------------------------------------------------------*/ 458 | void adaptive_threshold_set_threshold_min(AdaptiveThreshold* self, double min) 459 | { 460 | //if(min < 0) min = 0; 461 | self->min = min; 462 | } 463 | 464 | /*--------------------------------------------------------------------*/ 465 | double adaptive_threshold_onset_signal(AdaptiveThreshold* self) 466 | { 467 | return self->onset_signal; 468 | } 469 | 470 | /*--------------------------------------------------------------------*/ 471 | double adaptive_threshold_mean (AdaptiveThreshold* self) 472 | { 473 | return moving_average_mean(self->avg); 474 | } 475 | 476 | /*--------------------------------------------------------------------*/ 477 | double adaptive_threshold_update(AdaptiveThreshold* self, double x) 478 | { 479 | double next = 0; 480 | double result = 0; 481 | 482 | self->filtered_x = (x * (1-self->smoothing)) + (self->filtered_x * self->smoothing); 483 | moving_average_update(self->avg, self->filtered_x); 484 | 485 | double thresh = moving_average_std_dev(self->avg) * self->threshold; 486 | if(thresh < self->min) thresh = self->min; 487 | 488 | if((fabs(x) - moving_average_mean(self->avg)) > thresh) 489 | next = (x > moving_average_mean(self->avg)) ? 1 : -1; 490 | 491 | //ignore the first few samples so we get a stable std dev 492 | if(moving_average_n(self->avg) > 10) 493 | if((next != 0) && (self->onset_signal != next)) 494 | result = next; 495 | 496 | self->onset_signal = next; 497 | 498 | return result; 499 | }; 500 | 501 | 502 | /*--------------------------------------------------------------------*/ 503 | double statistics_random_flat() 504 | { 505 | return random() / (double)RAND_MAX; 506 | } 507 | 508 | /*--------------------------------------------------------------------*/ 509 | double statistics_random_normal(double mean, double std_dev) 510 | { 511 | double u, v, r; 512 | 513 | do{ 514 | u = 2 * statistics_random_flat() - 1; 515 | v = 2 * statistics_random_flat() - 1; 516 | r = u*u + v*v; 517 | }while(r == 0 || r > 1); 518 | 519 | double c = sqrt(-2 * log(r) / r); 520 | return mean + u * c * std_dev; 521 | } 522 | 523 | /*--------------------------------------------------------------------*/ 524 | double statistics_random_cauchy(double peak_location, double half_width_at_half_maximum) 525 | { 526 | double result = statistics_random_flat(); 527 | result -= 0.5; 528 | result *= M_PI; 529 | //if(fabs(result) == M_PI) 530 | //result = 0; 531 | //else 532 | result = tan(result); 533 | 534 | result *= half_width_at_half_maximum; 535 | result += peak_location; 536 | return result; 537 | } 538 | 539 | -------------------------------------------------------------------------------- /src/Statistics.h: -------------------------------------------------------------------------------- 1 | /*-____--_--------_---_-----_---_------------_-------------------------- 2 | / ___|| |_ __ _| |_(_)___| |_(_) ___ ___ | |__ 3 | \___ \| __/ _` | __| / __| __| |/ __/ __| | '_ \ 4 | ___) | || (_| | |_| \__ \ |_| | (__\__ \_| | | | 5 | |____/ \__\__,_|\__|_|___/\__|_|\___|___(_)_| |_| 6 | ------------------------------------------------------------------------ 7 | 8 | ----------------------------------------------------------------------*/ 9 | #ifndef __IC_STATISTICS__ 10 | #define __IC_STATISTICS__ 11 | 12 | #if defined(__cplusplus) 13 | extern "C"{ 14 | #endif //(__cplusplus) 15 | 16 | /*--------------------------------------------------------------------*/ 17 | /*--___-------_-_--------------_---------------------------------------- 18 | / _ \ _ _ | (_)_ _ ___ /_\__ _____ _ _ __ _ __ _ ___ 19 | | (_) | ' \| | | ' \/ -_) / _ \ V / -_) '_/ _` / _` / -_); 20 | \___/|_||_|_|_|_||_\___| /_/ \_\_/\___|_| \__,_\__, \___| 21 | --------------------------------------------------|___/----------------- 22 | Calculate the mean and variance of a signal one sample at a time. 23 | ----------------------------------------------------------------------*/ 24 | typedef struct opaque_online_average_struct OnlineAverage; 25 | OnlineAverage* online_average_new (); 26 | void online_average_init (OnlineAverage* self); 27 | OnlineAverage* online_average_destroy (OnlineAverage* self); 28 | int online_average_n (OnlineAverage* self); 29 | double online_average_mean (OnlineAverage* self); 30 | double online_average_variance (OnlineAverage* self); 31 | double online_average_std_dev (OnlineAverage* self); 32 | void online_average_update (OnlineAverage* self, double x); 33 | 34 | /*--------------------------------------------------------------------*/ 35 | /*-__--__---------_---------------_------------------------------------- 36 | | \/ |_____ _(_)_ _ __ _ /_\__ _____ _ _ __ _ __ _ ___ 37 | | |\/| / _ \ V / | ' \/ _` | / _ \ V / -_) '_/ _` / _` / -_); 38 | |_| |_\___/\_/|_|_||_\__, | /_/ \_\_/\___|_| \__,_\__, \___| 39 | ------------------------|___/------------------------|___/-------------- 40 | Calculate the moving or rolling mean and variance over the past N 41 | samples of a signal. This algorithm is online and updates one sample 42 | at a time. 43 | ----------------------------------------------------------------------*/ 44 | typedef struct opaque_moving_average_struct MovingAverage; 45 | MovingAverage* moving_average_new (unsigned N); 46 | void moving_average_init (MovingAverage* self); 47 | MovingAverage* moving_average_destroy (MovingAverage* self); 48 | int moving_average_N (MovingAverage* self); 49 | int moving_average_n (MovingAverage* self); 50 | double moving_average_mean (MovingAverage* self); 51 | double moving_average_variance (MovingAverage* self); 52 | double moving_average_std_dev (MovingAverage* self); 53 | void moving_average_update (MovingAverage* self, double x); 54 | 55 | /*--------------------------------------------------------------------*/ 56 | /*--___-------_-_------------___------------------------_--------------- 57 | / _ \ _ _ | (_)_ _ ___ | _ \___ __ _ _ _ ___ _____(_)___ _ _ 58 | | (_) | ' \| | | ' \/ -_) | / -_) _` | '_/ -_|_-<_-< / _ \ ' \ 59 | \___/|_||_|_|_|_||_\___| |_|_\___\__, |_| \___/__/__/_\___/_||_| 60 | ------------------------------------|___/------------------------------- 61 | Calculate the means, variances, covariance, slope, y-intercept and 62 | correlation coeficient of two signals. This is an online algorithm 63 | that operates one sample at a time. 64 | ----------------------------------------------------------------------*/ 65 | typedef struct opaque_online_regression_struct OnlineRegression; 66 | OnlineRegression* online_regression_new (); 67 | void online_regression_init (OnlineRegression* self); 68 | OnlineRegression* online_regression_destroy (OnlineRegression* self); 69 | int online_regression_n (OnlineRegression* self); 70 | double online_regression_covariance (OnlineRegression* self); 71 | double online_regression_slope (OnlineRegression* self); 72 | double online_regression_y_intercept (OnlineRegression* self); 73 | double online_regression_r_squared (OnlineRegression* self); 74 | void online_regression_update (OnlineRegression* self, double a_data, double b_data); 75 | 76 | /*--------------------------------------------------------------------*/ 77 | /*----------_-----------_---_-----------_____-_---------------_----_---- _ _ 78 | /_\ __| |__ _ _ __| |_(_)_ _____ |_ _| |_ _ _ ___ __| |_ | |_ ___| |__| | 79 | / _ \/ _` / _` | '_ \ _| \ V / -_) | | | ' \| '_/ -_|_-< ' \| ' \/ _ \ / _` | 80 | /_/ \_\__,_\__,_| .__/\__|_|\_/\___| |_| |_||_|_| \___/__/_||_|_||_\___/_\__,_| 81 | ------------------|_|--------------------------------------------------- 82 | Adaptive threshhold tells you when your signal goes a specified number 83 | of standard deviations above or below a moving average of a filtered 84 | version of your signal. update() returns +1 or -1 whenever your signal 85 | transitions to being above or below the threshold. 86 | ----------------------------------------------------------------------*/ 87 | typedef struct opaque_adaptive_threshold_struct AdaptiveThreshold; 88 | AdaptiveThreshold* adaptive_threshold_new (unsigned N); 89 | void adaptive_threshold_init (AdaptiveThreshold* self); 90 | void adaptive_threshold_clear (AdaptiveThreshold* self); 91 | AdaptiveThreshold* adaptive_threshold_destroy (AdaptiveThreshold* self); 92 | double adaptive_threshold_smoothing (AdaptiveThreshold* self); 93 | void adaptive_threshold_set_smoothing (AdaptiveThreshold* self, double coefficient); 94 | double adaptive_threshold_threshold_value (AdaptiveThreshold* self); //mean + num std devs * std dev 95 | double adaptive_threshold_threshold (AdaptiveThreshold* self); //num std devs 96 | void adaptive_threshold_set_threshold (AdaptiveThreshold* self, double std_devs); 97 | double adaptive_threshold_threshold_min (AdaptiveThreshold* self); 98 | void adaptive_threshold_set_threshold_min(AdaptiveThreshold* self, double min); //raw value 99 | double adaptive_threshold_onset_signal (AdaptiveThreshold* self); 100 | double adaptive_threshold_mean (AdaptiveThreshold* self); 101 | double adaptive_threshold_update (AdaptiveThreshold* self, double x); 102 | 103 | double statistics_random_flat(); //(0, 1] 104 | double statistics_random_normal(double mean, double std_dev); 105 | double statistics_random_cauchy(double peak_location, double half_width_at_half_maximum); 106 | 107 | #if defined(__cplusplus) 108 | } 109 | #endif //(__cplusplus) 110 | 111 | #endif //__IC_STATISTICS__ 112 | 113 | -------------------------------------------------------------------------------- /src/fastsin.h: -------------------------------------------------------------------------------- 1 | #ifndef __MK_SINE_TABLE__ 2 | #define __MK_SINE_TABLE__ 1 3 | 4 | #ifdef __cplusplus 5 | extern "C" { 6 | #endif 7 | 8 | #include 9 | 10 | 11 | #define SIN_NUM_SAMPLES 4096 12 | #define SIN_TWO_PI 0x100000000 13 | #define SIN_PI 0x80000000 14 | #define SIN_HALF_PI 0x40000000 15 | #define SIN_QUARTER_PI 0x20000000 16 | #define fastsin_t uint32_t 17 | 18 | float fastsin (fastsin_t angle); 19 | float fastcos (fastsin_t angle); 20 | 21 | #define fastsin(angle) (*(sinTable + ((angle) >> 20))) 22 | #define fastcos(angle) (*(sinTable + (((angle) + SIN_HALF_PI) >> 20))) 23 | 24 | const extern float sinTable[SIN_NUM_SAMPLES]; 25 | 26 | 27 | //slowsin 28 | /* 29 | #define SIN_TWO_PI (2 * M_PI) 30 | #define SIN_PI (M_PI) 31 | #define SIN_HALF_PI (0.5 * M_PI) 32 | #define SIN_QUARTER_PI (0.25 * M_PI) 33 | #define fastsin_t double 34 | 35 | float fastsin (fastsin_t angle); 36 | float fastcos (fastsin_t angle); 37 | 38 | #define fastsin(angle) (sin(angle)) 39 | #define fastcos(angle) (cos(angle)) 40 | */ 41 | 42 | #ifdef __cplusplus 43 | } 44 | #endif 45 | 46 | #endif//__MK_SINE_TABLE__ 47 | --------------------------------------------------------------------------------