1 /*
2 Copyright (C) 2011 Tom Szilagyi
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 2 of the License, or
7 (at your option) any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program; if not, write to the Free Software
16 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 #include <glib.h>
20 #include <stdint.h>
21 #include <stdlib.h>
22 #include <string.h>
23 #include <inttypes.h>
24 #include <math.h>
25
26 #include "ir.h"
27 #include "ir_utils.h"
28
29 /* Hash function to obtain key to IR file path.
30 * This is the djb2 algorithm taken from:
31 * http://www.cse.yorku.ca/~oz/hash.html
32 */
fhash(char * str)33 uint64_t fhash(char *str) {
34 uint64_t hash = 5381;
35 int c;
36
37 while ((c = *str++)) {
38 hash = ((hash << 5) + hash) + c; /* hash * 33 + c */
39 }
40 return hash;
41 }
42
fhash_from_ports(float * port0,float * port1,float * port2)43 uint64_t fhash_from_ports(float *port0, float *port1, float *port2) {
44 uint64_t val0 = ((uint64_t)*port0) & 0xffff;
45 uint64_t val1 = ((uint64_t)*port1) & 0xffffff;
46 uint64_t val2 = ((uint64_t)*port2) & 0xffffff;
47 return (val0 << 48) + (val1 << 24) + val2;
48 }
49
ports_from_fhash(uint64_t fhash,float * port0,float * port1,float * port2)50 void ports_from_fhash(uint64_t fhash, float *port0, float *port1, float *port2) {
51 *port0 = (float)((fhash >> 48) & 0xffff);
52 *port1 = (float)((fhash >> 24) & 0xffffff);
53 *port2 = (float)(fhash & 0xffffff);
54 }
55
load_keyfile(void)56 GKeyFile * load_keyfile(void) {
57 GKeyFile * keyfile = g_key_file_new();
58 gchar * ir_save_path = g_build_filename(g_get_home_dir(), IR_SAVE_FILE, NULL);
59 if (g_file_test(ir_save_path, G_FILE_TEST_EXISTS) &&
60 g_file_test(ir_save_path, G_FILE_TEST_IS_REGULAR)) {
61 if (!g_key_file_load_from_file(keyfile, ir_save_path,
62 G_KEY_FILE_NONE, NULL)) {
63 fprintf(stderr, "IR: could not load configuration data from %s\n",
64 ir_save_path);
65 }
66 }
67 g_free(ir_save_path);
68 return keyfile;
69 }
70
save_keyfile(GKeyFile * keyfile)71 void save_keyfile(GKeyFile * keyfile) {
72 gchar * ir_save_path = g_build_filename(g_get_home_dir(), IR_SAVE_FILE, NULL);
73 gchar * file_contents = g_key_file_to_data(keyfile, NULL, NULL);
74 if (!g_file_set_contents(ir_save_path, file_contents, -1, NULL)) {
75 fprintf(stderr, "IR: error saving configuration data to %s\n", ir_save_path);
76 }
77 g_free(ir_save_path);
78 g_free(file_contents);
79 }
80
get_path_from_key(GKeyFile * keyfile,uint64_t fhash)81 char * get_path_from_key(GKeyFile * keyfile, uint64_t fhash) {
82 char key[20];
83 snprintf(key, 20, "%016" PRIx64, fhash);
84 char * path = g_key_file_get_string(keyfile, GROUP_FHASH, key, NULL);
85 return path;
86 }
87
save_path(GKeyFile * keyfile,char * path)88 void save_path(GKeyFile * keyfile, char * path) {
89 uint64_t hash = fhash(path);
90 char key[20];
91 snprintf(key, 20, "%016" PRIx64, hash);
92 g_key_file_set_string(keyfile, GROUP_FHASH, key, path);
93 }
94
load_bookmarks(GKeyFile * keyfile,GtkListStore * store)95 void load_bookmarks(GKeyFile * keyfile, GtkListStore * store) {
96 gchar ** keys = g_key_file_get_keys(keyfile, GROUP_BOOKMARKS, NULL, NULL);
97 gchar ** k = keys;
98 while (k && *k) {
99 gchar * str = g_key_file_get_string(keyfile, GROUP_BOOKMARKS, *k, NULL);
100 GtkTreeIter iter;
101 gtk_list_store_append(store, &iter);
102 gtk_list_store_set(store, &iter, 0, *k, 1, str, -1);
103 free(str);
104 ++k;
105 }
106 g_strfreev(keys);
107 }
108
109 /* returns path if found, NULL if not found */
lookup_bookmark_in_store(GtkTreeModel * model,const char * bookmark)110 char * lookup_bookmark_in_store(GtkTreeModel * model, const char * bookmark) {
111 GtkTreeIter iter;
112 if (!gtk_tree_model_get_iter_first(model, &iter)) {
113 return NULL;
114 }
115 do {
116 char * key;
117 char * path;
118 gtk_tree_model_get(model, &iter, 0, &key, 1, &path, -1);
119 if (strcmp(key, bookmark) == 0) {
120 g_free(key);
121 return path;
122 } else {
123 g_free(key);
124 g_free(path);
125 }
126 } while (gtk_tree_model_iter_next(model, &iter));
127 return NULL;
128 }
129
store_bookmark(GKeyFile * keyfile,char * bookmark,char * fullpath)130 void store_bookmark(GKeyFile * keyfile, char * bookmark, char * fullpath) {
131 g_key_file_set_string(keyfile, GROUP_BOOKMARKS, bookmark, fullpath);
132 }
133
delete_bookmark(GKeyFile * keyfile,char * bookmark)134 void delete_bookmark(GKeyFile * keyfile, char * bookmark) {
135 g_key_file_remove_key(keyfile, GROUP_BOOKMARKS, bookmark, NULL);
136 }
137
filename_filter(const char * file)138 int filename_filter(const char * file) {
139 if (!file) { return 0; }
140 if (strlen(file) < 5) { return 0; }
141 const char * ext = file + strlen(file)-4;
142 if (strcmp(ext, ".wav") == 0) { return 1; }
143 if (strcmp(ext, ".WAV") == 0) { return 1; }
144 if (strcmp(ext, ".aiff") == 0) { return 1; }
145 if (strcmp(ext, ".AIFF") == 0) { return 1; }
146 if (strcmp(ext, ".au") == 0) { return 1; }
147 if (strcmp(ext, ".AU") == 0) { return 1; }
148 if (strcmp(ext, ".flac") == 0) { return 1; }
149 if (strcmp(ext, ".FLAC") == 0) { return 1; }
150 if (strcmp(ext, ".ogg") == 0) { return 1; }
151 if (strcmp(ext, ".OGG") == 0) { return 1; }
152 return 0;
153 }
154
dirname_filter(const char * file)155 int dirname_filter(const char * file) {
156 if (!file) { return 0; }
157 if (strlen(file) < 1) { return 0; }
158 if (file[0] == '.') { return 0; }
159 return 1;
160 }
161
load_files(GtkListStore * store,char * dirpath)162 void load_files(GtkListStore * store, char * dirpath) {
163 gtk_list_store_clear(store);
164 GDir * dir = g_dir_open(dirpath, 0, NULL);
165 if (!dir) {
166 return;
167 }
168 const char * file;
169 while ((file = g_dir_read_name(dir))) {
170 char * filepath = g_build_filename(dirpath, file, NULL);
171 if ((g_file_test(filepath, G_FILE_TEST_IS_DIR) &&
172 dirname_filter(file)) ||
173 filename_filter(file)) {
174 GtkTreeIter iter;
175 gtk_list_store_append(store, &iter);
176 gtk_list_store_set(store, &iter, 0, file, 1, filepath, -1);
177 }
178 g_free(filepath);
179 }
180 g_dir_close(dir);
181 }
182
select_entry(GtkTreeModel * model,GtkTreeSelection * select,char * filename)183 void select_entry(GtkTreeModel * model, GtkTreeSelection * select, char * filename) {
184 GtkTreeIter iter;
185 if (!gtk_tree_model_get_iter_first(model, &iter)) {
186 return;
187 }
188 do {
189 char * path;
190 gtk_tree_model_get(model, &iter, 1, &path, -1);
191 if (strcmp(filename, path) == 0) {
192 gtk_tree_selection_select_iter(select, &iter);
193 g_free(path);
194 return;
195 }
196 } while (gtk_tree_model_iter_next(model, &iter));
197 gtk_tree_selection_unselect_all(select);
198 }
199
200 #define TAU 0.25
201 /* attack & envelope curves where t = 0..1 */
202 #define ATTACK_FN(t) (exp(((t) - 1.0) / TAU))
203 #define ENVELOPE_FN(t) (exp(-1.0 * (t) / TAU))
204
compute_envelope(float ** samples,int nchan,int nfram,int attack_time_s,float attack_pc,float env_pc,float length_pc)205 void compute_envelope(float ** samples, int nchan, int nfram,
206 int attack_time_s, float attack_pc,
207 float env_pc, float length_pc) {
208 float gain;
209
210 if (attack_time_s > nfram) {
211 attack_time_s = nfram;
212 }
213
214 for (int j = 0; j < attack_time_s; j++) {
215 gain = attack_pc / 100.0 +
216 (100.0 - attack_pc) / 100.0 *
217 ATTACK_FN((double)j / attack_time_s);
218 for (int i = 0; i < nchan; i++) {
219 samples[i][j] *= gain;
220 }
221 }
222
223 int length_s = length_pc / 100.0 * (nfram - attack_time_s);
224 for (int j = attack_time_s, k = 0; j < attack_time_s + length_s; j++, k++) {
225 gain = env_pc / 100.0 +
226 (100.0 - env_pc) / 100.0 *
227 ENVELOPE_FN((double)k / length_s);
228 for (int i = 0; i < nchan; i++) {
229 samples[i][j] *= gain;
230 }
231 }
232
233 for (int j = attack_time_s + length_s; j < nfram; j++) {
234 for (int i = 0; i < nchan; i++) {
235 samples[i][j] = 0.0f;
236 }
237 }
238 }
239
draw_centered_text(cairo_t * cr,const char * text,int x,int y)240 void draw_centered_text(cairo_t * cr, const char * text, int x , int y) {
241 cairo_text_extents_t extents;
242 cairo_text_extents(cr, text, &extents);
243 int text_x = x - (extents.width/2 + extents.x_bearing);
244 int text_y = y -(extents.height/2 + extents.y_bearing);
245 cairo_move_to(cr, text_x, text_y);
246 cairo_show_text(cr, text);
247 }
248