#include #include #include //#include #include #include #include #include #include #define MAXPADS 4 #define MAP_READNAME 0 #define MAP_READKEY 1 #define MAP_READINDEX 2 #define MAP_PRINT 3 typedef struct { unsigned char inst; unsigned char chan; } padmap_t; struct termios term_orig; struct termios term_canon; struct termios term_noncanon; config_t config; char quit_thread=0; sem_t init_sem; padmap_t padmap[MAXPADS]; pthread_mutex_t global_mutex; // protects config and padmap. char read_sound(const char *kitstr, const char *soundstr, unsigned char *midinum) { // reads sounds definition from config file. config_setting_t *kits, *kit, *kit_name_setting, *sounds, *sound; const char *kit_name, *sound_name; //printf("loading sound: %s\n",soundstr); int num_kits, num_sounds; long sound_midinum; int i; kits=config_lookup(&config,"kits"); if (kits==NULL) { printf("No 'kits' section in config file.\n"); return 0; } num_kits=config_setting_length(kits); if (num_kits==0) { printf("No drum kits defined in 'kits' section\n"); return 0; } //printf("Looking for kit: %s\n",kitstr); for (i=0; i127) { printf("Invalid midi note number for sound: %s in kit: %s\n",soundstr,kitstr); return 0; } *midinum=(unsigned char)sound_midinum; //printf("Found sound with name: %s, midi number: %d in kit %s\n",soundstr,sound_midinum,kitstr); return -1; } char read_channel(const char *chanstr, const char **kit_name, unsigned char *midichannel) { // read information about a given channel from config data config_setting_t *channels,*channel, *channel_name_setting, *channel_kit_setting, *channel_midi_setting; const char *channel_name, *channel_kit; int num_channels; unsigned char channel_midi; int i; //printf("loading channel: %s\n",chanstr); channels=config_lookup(&config,"channels"); if (channels==NULL) { printf("No channel section defined.\n"); return 0; } num_channels=config_setting_length(channels); if (num_channels==0) { printf("No channels defined.\n"); return 0; } for (i=0; i16 || channel_midi<=0) { printf("'midichannel' setting invalid or empty in channel: %s\n",chanstr); return 0; } //printf("Found channel: %s with kit: %s, midi channel: %d\n",chanstr,channel_kit,channel_midi); *kit_name=channel_kit; *midichannel=(unsigned char)channel_midi; return -1; } char read_mapping(const char *mapstr,int mapno,char action) { // load a pad mapping with the name mapstr. config_setting_t *mappings; config_setting_t *mapping; config_setting_t *map_name_setting; config_setting_t *pads; config_setting_t *pad_group; config_setting_t *pad_num_setting; config_setting_t *pad_channel_setting; config_setting_t *pad_sound_setting; config_setting_t *map_hotkey_setting; const char *pad_channel, *pad_sound, *kit_name, *map_hotkey; int pad_num; unsigned char midinum,midichan; int i; const char *map_name; int num_mappings; int num_pads; mappings=config_lookup(&config,"mappings"); if (mappings==NULL) { printf("No 'mappings' section in config file.\n"); return 0; } num_mappings=config_setting_length(mappings); if (num_mappings==0) { printf("\nNo valid mappings found in config file.\n"); return 0; } for (i=0;i0) { printf("(%s)",map_hotkey); } printf("\n"); } } if (action==MAP_PRINT) return 0; if (i==num_mappings) { printf("\nCan't find mapping: %s\n",mapstr); return 0; } printf("loading mapping: %s ...\n",map_name); // found the right mapping, so assign the keys. //printf("found mapping.\n"); pads=config_setting_get_member(mapping,"pads"); if (pads==NULL) { printf("Pads section not defined.\n"); return 0; } num_pads=config_setting_length(pads); if (num_pads==0) { printf("No pads defined.\n"); return 0; } for (i=0; i MAXPADS) { printf("Invalid pad number in pad group number: %d in mapping: %s\n",i,mapstr); return 0; } pad_channel_setting=config_setting_get_member(pad_group,"channel"); if (pad_channel_setting==NULL) { printf("No 'channel' setting in pad group number %d\n",i); return 0; } pad_channel=config_setting_get_string(pad_channel_setting); if (pad_channel==NULL) { printf("Channel name empty in pad group number %d\n",i); return 0; } pad_sound_setting=config_setting_get_member(pad_group,"sound"); if (pad_sound_setting==NULL) { printf("No 'sound' setting in pad group number %d\n",i); return 0; } pad_sound=config_setting_get_string(pad_sound_setting); if (pad_sound==NULL) { printf("Pad sound name empty in pad group number %d\n",i); return 0; } //printf("Read pad group: pad=%d, channel=%s, sound=%s\n",pad_num,pad_channel,pad_sound); if (!read_channel(pad_channel,&kit_name,&midichan)) { printf("Failed to load channel.\n"); return 0; } else { //printf("Loaded channel.\n"); } if (!read_sound(kit_name,pad_sound,&midinum)) { printf("Failed to load sound.\n"); return 0; } else { //printf("Loaded sound.\n"); } printf("Assigning pad:%d to sound:%s with note number:%d on midi channel:%d\n",pad_num,pad_sound,midinum,midichan); padmap[pad_num].inst=midinum; padmap[pad_num].chan=midichan; } return -1; // success. } void parse_config() { // parses the configuration file using libconfig. const char *conf_err; int conf_errline; config_init(&config); printf("Reading config file... "); int result=config_read_file(&config,"mididrum.conf"); if (result==CONFIG_FALSE) { // failed to parse file. conf_err=config_error_text(&config); conf_errline=config_error_line(&config); printf("\nError reading config file: %s at line: %d\n",conf_err,conf_errline); config_destroy(&config); exit(1); } else { printf("OK\n"); } } char open_midi(snd_rawmidi_t **phandle_out) { //snd_seq_client_info_t* out_client_info=NULL; //const char* out_client="FLUID Synth"; snd_seq_t *seq_handle; int midi_err; snd_rawmidi_info_t *rawmidi_info=NULL; printf("Opening midi port...\n"); midi_err=snd_rawmidi_open(NULL, phandle_out, "virtual",0); if (midi_err<0) { printf("error opening midi port: %d\n", midi_err); return 0; } //printf("rawmidi_name:%s\n",snd_rawmidi_name(*phandle_out)); //snd_rawmidi_info_alloca(&rawmidi_info); //midi_err=snd_rawmidi_info(*phandle_out,rawmidi_info); //printf("rawmidi_id:%s\n",snd_rawmidi_info_get_id(rawmidi_info)); //err = snd_seq_open(&handle, "default", SND_SEQ_OPEN_INPUT, 0); //if (err < 0) // return NULL; //snd_seq_set_client_name(handle, "My Client"); return -1; //printf("Subscribing to midi client: %s\n",output_to); //while ( } void *usbmidi(void *arg) { // thread to read usb port and send midi events. snd_rawmidi_t *handle_out=0; int pad,vel,rawvel,samples,val,startval; int ret; FILE * usbf; const char *usbdevstr; const char *usbdevstr_conf; const char *usbdevstr_default="/dev/ttyUSB0"; unsigned char inst=0, chan=0; unsigned char midibuf[3]; pthread_mutex_lock(&global_mutex); // lock config. usbdevstr_conf=config_lookup_string(&config,"device"); pthread_mutex_unlock(&global_mutex); if (usbdevstr_conf==NULL) { usbdevstr=usbdevstr_default; } else { usbdevstr=usbdevstr_conf; } printf("opening usb device (%s)... ",usbdevstr); usbf=fopen(usbdevstr,"r"); if (usbf == NULL) { printf("\nError opening file.\n"); exit(1); } else { printf("OK\n"); } if (open_midi(&handle_out)) { printf("waiting for first hit\n"); sem_post(&init_sem); do { ret=fscanf(usbf,"%d, %d, %d, %d, %d, %d",&pad,&vel,&rawvel,&samples,&val,&startval); if (ret == EOF || ret ==0) break; //printf("pad: %d, vel: %d, rawvel: %d, samples: %d, value: %d, start value: %d\n",pad,vel,rawvel,samples,val,startval); pthread_mutex_lock(&global_mutex); // lock padmap inst=padmap[pad].inst; chan=padmap[pad].chan; pthread_mutex_unlock(&global_mutex); midibuf[0]=0x90 | (chan-1); midibuf[1]=inst; midibuf[2]=vel; snd_rawmidi_write(handle_out,&midibuf,3); snd_rawmidi_drain(handle_out); printf("pad: %d hit - playing note %d on channel: %d at velocity %d\n",pad,inst,chan,vel); } while (quit_thread!=-1); printf("Closing midi port\n"); snd_rawmidi_drain(handle_out); snd_rawmidi_close(handle_out); } printf("Closing usb port\n"); fclose(usbf); pthread_exit(0); } int main() { int res; pthread_t usbmidi_thread; void *thread_result; const char *mapstr; char *listval,*retstr; int listindex,ret; char in_ch=' '; char in_ch2[2]={32,0}; char in_buf[20]; FILE *input,*output; if (!isatty(fileno(stdout))) { fprintf(stderr,"Not running from a terminal.\n"); exit(1); } input=fopen("/dev/tty","r"); output=fopen("/dev/tty","w"); if (!input || !output) { fprintf(stderr,"Unable to open /dev/tty.\n"); exit(1); } // save termios details. tcgetattr(fileno(stdin),&term_orig); tcgetattr(fileno(stdin),&term_canon); tcgetattr(fileno(stdin),&term_noncanon); term_canon.c_lflag!=ICANON; term_noncanon.c_lflag&=~ICANON; term_noncanon.c_cc[VMIN]=1; term_noncanon.c_cc[VTIME]=0; //padmap={35,38,42,44}; // safe default values. parse_config(); //parse configuration file. // read in default pad mapping. mapstr=config_lookup_string(&config,"mapping"); if (mapstr==NULL) { printf("No pad mapping given in config file.\n"); exit(5); } else { if (!read_mapping(mapstr,0,MAP_READNAME)) exit(3); } // start threading res=pthread_mutex_init(&global_mutex,NULL); if (res!=0) { perror("main: Can't create mutex.\n"); exit(1); } res=sem_init(&init_sem,0,0); if (res!=0) { perror("main: Can't create semaphore.\n"); exit(1); } res=pthread_create(&usbmidi_thread,NULL,usbmidi,NULL); if (res!=0) { perror("main: Can't start usb/midi thread.\n"); exit(1); } sem_wait(&init_sem); // wait until thread has initialised. // do some key processing here. do { fprintf(output,"\nChoose an option:\nm - load mapping by number\nh - load mapping by hotkey\nq - quit\n"); //retstr=fgets(in_buf,20,input); //if (retstr==NULL) break; //in_ch=in_buf[0]; tcsetattr(fileno(stdin),TCSAFLUSH,&term_noncanon); in_ch=fgetc(input); printf("\n"); switch (in_ch) { case 'm': printf("Choose a pad mapping from the list:\n"); read_mapping(NULL,0,MAP_PRINT); tcsetattr(fileno(stdin),TCSANOW,&term_canon); retstr=fgets(in_buf,20,input); if (retstr==NULL || in_buf[0]=='\0') break; // TODO - not quite right. listindex=(int)strtol(in_buf,(char **)NULL,10); //printf("Loading mapping #%d\n",listindex); pthread_mutex_lock(&global_mutex); read_mapping(NULL,listindex,MAP_READINDEX); pthread_mutex_unlock(&global_mutex); break; case 'h': tcsetattr(fileno(stdin),TCSAFLUSH,&term_noncanon); printf("Choose a pad mapping from the list:\n"); read_mapping(NULL,0,MAP_PRINT); printf("here."); in_ch2[0]=(char)fgetc(input); //in_ch2[1]='\0'; printf("...\n"); if (in_ch2[0]>='a' && in_ch2[0]<='z') { //printf("Loading mapping with hotkey:\n");//,listval[0]); pthread_mutex_lock(&global_mutex); read_mapping(in_ch2,0,MAP_READKEY); pthread_mutex_unlock(&global_mutex); } break; } } while (in_ch != 'q'); tcsetattr(fileno(stdin),TCSAFLUSH,&term_orig); // return terminal settings to orginal values. quit_thread=-1; fprintf(output,"Waiting for usb/midi thread\n"); res=pthread_join(usbmidi_thread,&thread_result); if (res!=0) { perror("main: Thread join failure."); } sem_destroy(&init_sem); pthread_mutex_destroy(&global_mutex); config_destroy(&config); fprintf(output,"Exiting"); exit (0); }