summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--TODO8
-rw-r--r--TODO.done2
-rw-r--r--synth/Makefile2
-rw-r--r--synth/cli.cpp6
-rw-r--r--synth/globals.cpp1
-rw-r--r--synth/globals.h1
-rw-r--r--synth/in_synth_cli.cpp37
-rw-r--r--synth/in_synth_cli.h8
-rw-r--r--synth/main.cpp32
-rw-r--r--synth/watch_files.cpp160
-rw-r--r--synth/watch_files.h9
11 files changed, 263 insertions, 3 deletions
diff --git a/TODO b/TODO
index 587f253..a454c84 100644
--- a/TODO
+++ b/TODO
@@ -1,7 +1,15 @@
+!!! interface der compilten note und korrespondierende funktion
+ im synth stimmen nicht mehr überein! ÄNDERN!
+
!!! SEGFAULT beim laden einer nicht-existenten datei per in-synth-cli
wenn man danach die noten spielen will. nicht reproduzierbar
TODO für den synth
+ o file-watcher ist unsauber: inotify_map_mutex und prog_load_mutex
+ werden eigentlich zu spät erstellt; bei EXTREM schnellen events
+ könnte ein noch nicht existenter mutex gelockt werden
+ o lock_and_load_program_no_watch_updates und auch die requests
+ passen nicht wirklich ins in-synth-cli. vlt woandershin schieben?
o im in-synth-cli: lfos- und snh-neusetzen ist falsch
es muss IMMER gelockt werden.
allerdings muss maybe_calc_lfos gelockt werden, die noten können
diff --git a/TODO.done b/TODO.done
index e354909..9a931b4 100644
--- a/TODO.done
+++ b/TODO.done
@@ -76,6 +76,7 @@ TODO für den synth
x mehr wellen für wave[]
x wenn aufgehängt, kann er mit ctrl+c nicht mehr abgebrochen werden!
x ctrl+d führt zu bug
+ x watcher implementieren (per inotify)
TODO fürs CLI
@@ -98,6 +99,7 @@ TODO fürs CLI
x .so unloaden!
* lfo-maxima getrennt regeln. [abgelehnt]
x on-the-fly panic, einzelne channels, einzelne instrumente, etc
+ x CLI-flag für watch-files/don't-watch
TODO für den compiler
diff --git a/synth/Makefile b/synth/Makefile
index a9ec0a1..39b6e80 100644
--- a/synth/Makefile
+++ b/synth/Makefile
@@ -3,7 +3,7 @@ CFLAGS=-Wall -O2 -g
CXXFLAGS=$(CFLAGS)
LDFLAGS=-lm `pkg-config --cflags --libs jack`
-OBJ=channel.o cli.o defines.o envelope.o filter.o globals.o jack.o load.o main.o note.o note_skel.o parser.o programs.o readwave.o util.o note_loader.o in_synth_cli.o communication.o shared_object_manager.o lfos.o
+OBJ=channel.o cli.o defines.o envelope.o filter.o globals.o jack.o load.o main.o note.o note_skel.o parser.o programs.o readwave.o util.o note_loader.o in_synth_cli.o communication.o shared_object_manager.o lfos.o watch_files.o
BIN=synth
DEPENDFILE = .depend
diff --git a/synth/cli.cpp b/synth/cli.cpp
index eb52c95..4d5cc2f 100644
--- a/synth/cli.cpp
+++ b/synth/cli.cpp
@@ -30,6 +30,8 @@ void parse_args(int argc, char** argv)
{"xruns", required_argument, 0, 'x'},
{"dir", required_argument, 0, 'd'},
{"directory", required_argument, 0, 'd'},
+ {"no-watch-files", no_argument, 0, 'w'},
+ {"dont-watch-files", no_argument, 0, 'w'},
{"program", required_argument, 0, 'p'},
{"cleanup-interval", required_argument, 0, 'i'},
{"lfo0-freq", required_argument, 0, 400}, //FINDLFO
@@ -59,7 +61,7 @@ void parse_args(int argc, char** argv)
while (optind<argc)
{
int index=-1;
- int result=getopt_long(argc,argv,"hVf:d:p:i:c:x:vqFam", long_options, &index);
+ int result=getopt_long(argc,argv,"hVf:d:p:i:c:x:vqFamw", long_options, &index);
if (result==-1) break;
switch (result)
@@ -131,6 +133,8 @@ void parse_args(int argc, char** argv)
else
output_warning("WARNING: not a number in --interval option. ignoring it...");
break;
+ case 'w': watchfiles=false;
+ break;
case 304: if (isfloat(optarg))
snh_freq_hz=atof(optarg);
else
diff --git a/synth/globals.cpp b/synth/globals.cpp
index 96a56d5..8cde306 100644
--- a/synth/globals.cpp
+++ b/synth/globals.cpp
@@ -18,6 +18,7 @@ bool quiet=false;
bool connect_audio=true, connect_midi=true;
+bool watchfiles=true;
float cleanup_interval_sec=0;
float snh_freq_hz=0;
diff --git a/synth/globals.h b/synth/globals.h
index b89813d..bccacdf 100644
--- a/synth/globals.h
+++ b/synth/globals.h
@@ -29,6 +29,7 @@ extern bool quiet;
extern bool connect_audio, connect_midi;
+extern bool watchfiles;
extern float cleanup_interval_sec;
extern float snh_freq_hz;
diff --git a/synth/in_synth_cli.cpp b/synth/in_synth_cli.cpp
index 7ca9c7d..fc24b03 100644
--- a/synth/in_synth_cli.cpp
+++ b/synth/in_synth_cli.cpp
@@ -10,11 +10,14 @@
#include "globals.h"
#include "load.h"
#include "lfos.h"
+#include "watch_files.h"
using namespace std;
#define PROMPT "> "
+pthread_mutex_t prog_load_mutex;
+
void signal_handler(int sig)
{
cout << endl << PROMPT << flush;
@@ -49,12 +52,18 @@ void do_request(int prg_no, bool susp)
void lock_and_load_program(int prg_no, string file)
{
+ pthread_mutex_lock(&prog_load_mutex);
+
+ remove_watch(prg_no);
+
do_request(prg_no, true);
if (load_program(file,program_settings[prg_no]))
{
cout << "success" << endl;
programfile[prg_no]=file;
+
+ add_watch(prg_no);
}
else
cout << "failed" << endl;
@@ -63,6 +72,32 @@ void lock_and_load_program(int prg_no, string file)
channel[i]->maybe_reload_program(prg_no);
do_request(prg_no, false);
+
+ pthread_mutex_unlock(&prog_load_mutex);
+}
+
+//only use this, if you don't want the file-watches to be updated
+//i.e., only when reloading a program!
+void lock_and_load_program_no_watch_updates(int prg_no, string file)
+{
+ pthread_mutex_lock(&prog_load_mutex);
+
+ do_request(prg_no, true);
+
+ if (load_program(file,program_settings[prg_no]))
+ {
+ cout << "success" << endl;
+ programfile[prg_no]=file;
+ }
+ else
+ cout << "failed" << endl;
+
+ for (int i=0;i<N_CHANNELS;++i)
+ channel[i]->maybe_reload_program(prg_no);
+
+ do_request(prg_no, false);
+
+ pthread_mutex_unlock(&prog_load_mutex);
}
void lock_and_change_lfo(int lfo_no, float freq)
@@ -83,6 +118,8 @@ void do_in_synth_cli()
string command;
string params;
int num;
+
+ pthread_mutex_init(&prog_load_mutex, NULL);
if (signal(2,signal_handler)==SIG_ERR)
output_warning("WARNING: failed to set signal handler in the in-synth-cli. pressing ctrl+c will\n"
diff --git a/synth/in_synth_cli.h b/synth/in_synth_cli.h
index 27ae216..18f7a62 100644
--- a/synth/in_synth_cli.h
+++ b/synth/in_synth_cli.h
@@ -1,6 +1,14 @@
#ifndef __IN_SYNTH_CLI_H__
#define __IN_SYNTH_CLI_H__
+#include <string>
+
+using namespace std;
+
void do_in_synth_cli();
+
+//only use this, if you don't want the file-watches to be updated
+//i.e., only when reloading a program!
+void lock_and_load_program_no_watch_updates(int prg_no, string file);
#endif
diff --git a/synth/main.cpp b/synth/main.cpp
index a9d7ef0..32b2b84 100644
--- a/synth/main.cpp
+++ b/synth/main.cpp
@@ -2,6 +2,7 @@
#include <iostream>
#include <cmath>
#include <cstdlib>
+#include <pthread.h>
#include "jack.h"
#include "load.h"
@@ -15,6 +16,7 @@
#include "communication.h"
#include "note_loader.h"
#include "lfos.h"
+#include "watch_files.h"
using namespace std;
@@ -22,9 +24,10 @@ using namespace std;
void cleanup();
void dump_options();
+pthread_t watcher_thread=-1;
int main(int argc, char** argv)
-{
+{
init_communication();
for (int i=0;i<N_LFOS;++i)
@@ -118,6 +121,21 @@ int main(int argc, char** argv)
start_jack(connect_audio, connect_midi);
+ if (watchfiles)
+ {
+ if (pthread_create(&watcher_thread, NULL, watch_files, NULL) != 0)
+ {
+ output_warning("WARNING: could not start file-watcher thread. you must inform me about\n"
+ " updated files manually.");
+ watcher_thread=-1;
+ }
+ }
+ else
+ {
+ output_note("NOTE: you disabled the watching of files. you must inform me about\n"
+ " updated files manually.");
+ }
+
do_in_synth_cli();
cleanup();
@@ -135,6 +153,18 @@ int main(int argc, char** argv)
void cleanup()
{
+ if (watcher_thread!=-1)
+ {
+ if (pthread_cancel(watcher_thread) != 0)
+ {
+ output_warning("WARNING: could not cancel watcher thread!");
+ }
+ else
+ {
+ pthread_join(watcher_thread,NULL);
+ }
+ }
+
exit_jack();
uninit_communication();
diff --git a/synth/watch_files.cpp b/synth/watch_files.cpp
new file mode 100644
index 0000000..ce80224
--- /dev/null
+++ b/synth/watch_files.cpp
@@ -0,0 +1,160 @@
+#include <iostream>
+#include <pthread.h>
+#include <unistd.h>
+#include <sys/inotify.h>
+
+#include "watch_files.h"
+#include "util.h"
+#include "globals.h"
+#include "in_synth_cli.h"
+
+using namespace std;
+
+int fd=-1;
+map<int, set<int> > inotify_map;
+pthread_mutex_t inotify_map_mutex;
+
+void watch_files_cleanup(void* unused)
+{
+ if (fd==-1)
+ {
+ output_verbose("NOTE: no cleaning necessary for watch-files-thread");
+ }
+ else
+ {
+ output_verbose("NOTE: cleaning up for watch-files-thread...");
+
+ }
+}
+
+void* watch_files(void* unused)
+{
+ pthread_cleanup_push(watch_files_cleanup, NULL);
+
+ pthread_mutex_init(&inotify_map_mutex, NULL);
+
+ fd=inotify_init();
+ if (fd==-1)
+ {
+ output_warning("WARNING: could not initalize inotify. you must inform me about\n"
+ " updated files manually.");
+ while (true) sleep(10);
+ }
+ else
+ {
+ for (int i=0;i<128;i++) // add watches for all loaded programs
+ if (programfile[i]!="")
+ add_watch(i);
+
+ inotify_event ev;
+ size_t s;
+ while (true)
+ {
+ s=read (fd, &ev, sizeof(inotify_event));
+ while (s<sizeof(inotify_event))
+ s+=read (fd,(char*)&ev + s, sizeof(inotify_event)-s);
+
+ pthread_mutex_lock(&inotify_map_mutex);
+
+ if (ev.mask & IN_MODIFY)
+ {
+ if (verbose)
+ {
+ string str="";
+ set<int>& tmp=inotify_map[ev.wd];
+ for (set<int>::iterator it=tmp.begin(); it!=tmp.end(); it++)
+ str+="#"+IntToStr(*it)+" ";
+
+ output_verbose("NOTE: reloading programs "+str+"...");
+ }
+
+ set<int>& tmp=inotify_map[ev.wd];
+ for (set<int>::iterator it=tmp.begin(); it!=tmp.end(); it++)
+ lock_and_load_program_no_watch_updates(*it, programfile[*it]);
+ }
+ else if (ev.mask & (IN_MOVE_SELF | IN_DELETE_SELF))
+ {
+ if (verbose)
+ {
+ string str="";
+ set<int>& tmp=inotify_map[ev.wd];
+ for (set<int>::iterator it=tmp.begin(); it!=tmp.end(); it++)
+ str+="#"+IntToStr(*it)+" ";
+
+ output_verbose("NOTE: removed watch for programs "+str);
+ }
+
+ inotify_map.erase(ev.wd);
+ inotify_rm_watch(fd,ev.wd);
+ }
+ else if (ev.mask != IN_IGNORED)
+ {
+ output_note("NOTE: in watch_files-thread: unknown event received ("+IntToStrHex(ev.mask)+")");
+ }
+
+ pthread_mutex_unlock(&inotify_map_mutex);
+ }
+ }
+
+ pthread_cleanup_pop(0);
+}
+
+void remove_watch(int prog)
+{
+ if (watchfiles)
+ {
+ map<int, set<int> >::iterator mit;
+ set<int>* tmp;
+ set<int>::iterator sit;
+
+ pthread_mutex_lock(&inotify_map_mutex);
+
+ //search in all known watch descriptors
+ for (mit=inotify_map.begin(); mit!=inotify_map.end(); mit++)
+ {
+ tmp=&(mit->second);
+ sit=tmp->find(prog);
+
+ //search for some wd which affects $prog
+ if (sit!=tmp->end()) //found?
+ {
+ //erase $prog from the affect-set
+ tmp->erase(sit);
+ if (tmp->empty())
+ {
+ //if the affect-set is now empty, we can garbage-collect
+ //the wd (i.e., remove it)
+ cout << "garbage collecting wd #"<<mit->first<<endl;
+ inotify_rm_watch(fd, mit->first);
+ inotify_map.erase(mit);
+ }
+
+ //we're done now
+ break;
+ }
+ }
+
+ pthread_mutex_unlock(&inotify_map_mutex);
+ }
+}
+
+void add_watch(int prog)
+{
+ if (watchfiles)
+ {
+ int wd=inotify_add_watch(fd, programfile[prog].c_str(), IN_MODIFY | IN_MOVE_SELF | IN_DELETE_SELF);
+
+ pthread_mutex_lock(&inotify_map_mutex);
+
+ if (wd!=-1)
+ {
+ inotify_map[wd].insert(prog);
+ }
+ else
+ {
+ //TODO: warning
+ }
+
+ pthread_mutex_unlock(&inotify_map_mutex);
+ }
+}
diff --git a/synth/watch_files.h b/synth/watch_files.h
new file mode 100644
index 0000000..b9c7d2d
--- /dev/null
+++ b/synth/watch_files.h
@@ -0,0 +1,9 @@
+#ifndef __WATCH_FILES_H__
+#define __WATCH_FILES_H__
+
+void* watch_files(void* unused);
+
+void add_watch(int prog);
+void remove_watch(int prog);
+
+#endif