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