Re: [LAD] [OT] DIY Trigger - Piezo to MIDI to PC

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Ray Rashif wrote:
Oh wow, every single one of you starting from Nedko have just saved me hours of research! Thanks so much guys, extra points to Andy for his walkthrough with bonus code, Folderol for citing the exact reference material and Josh for landing the correct device (eroktronix) =P
Glad what I posted what helpful.

I actually posted the wrong C code - the right files are attached to this email.

Would be good to know how you get on with this as I'm thinking of making a midi'ed bass drum beater at some point.

andy
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
//#include <errno.h>
#include <alsa/asoundlib.h>
#include <libconfig.h>
#include <pthread.h>
#include <semaphore.h>
#include <termios.h>

#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; i<num_kits; i++) {
		kit=config_setting_get_elem(kits,i);
		kit_name_setting=config_setting_get_member(kit,"name");
		if (kit_name_setting==NULL) {
			printf("No 'name' setting in kit #%d\n",i);
			return 0;
		}
		kit_name=config_setting_get_string(kit_name_setting);
		if (kit_name==NULL) {
			printf("'name' setting in kit #%d is invalid or empty\n");
			return 0;
		}
		if (strcmp(kit_name,kitstr)==0) {
			break;
		}
	}
	if (i==num_kits) {
		printf("Can't find the drum kit: %s\n",kitstr);
		return 0;
	}
	//printf("Found kit.\n");
	// have found the right drum kit; now find the sound.
	sounds=config_setting_get_member(kit,"sounds");
	if (sounds==NULL) {
		printf("No 'sounds' setting in kit: %s\n",kitstr);
		return 0;
	}
	num_sounds=config_setting_length(sounds);
	if (num_sounds==0) {
		printf("No sounds defined in kit: %s\n",kitstr);
		return 0;
	}
	//printf("Looking for sound: %s\n",soundstr);
	for (i=0; i<num_sounds; i++) {
		sound=config_setting_get_elem(sounds,i);
		if (config_setting_length(sound)!=2) {
			printf("Invalid sound definition in sounds section of kit: %s\n",kitstr);
			return 0;
		}
		sound_name=config_setting_get_string_elem(sound,1);
		if (sound_name==NULL) {
			printf("Invalid sound name in sounds section of kit: %s\n",kitstr);
			return 0;
		}
		if (strcmp(sound_name,soundstr)==0) {
			break;
		}
	}
	if (i==num_sounds) {
		printf("Can't find sound: %s in kit: %s\n",soundstr,kitstr);
		return 0;
	}
	//printf("Found sound.\n");
	sound_midinum=config_setting_get_int_elem(sound,0);
	if (sound_midinum<0 || sound_midinum>127) {
		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; i<num_channels; i++) {
		channel=config_setting_get_elem(channels,i);
		channel_name_setting=config_setting_get_member(channel,"name");
		if (channel_name_setting==NULL) {
			printf("No 'name' setting for channel number %d\n",i);
			return 0;
		}
		channel_name=config_setting_get_string(channel_name_setting);
		if (channel_name==NULL) {
			printf("Channel name empty or invalid in channel number %d\n",i);
			return 0;
		}
		if (strcmp(channel_name,chanstr)==0) {
			break;
		}
	}
	if (i==num_channels) {
		printf("Can't find channel definition: %s\n",chanstr);
		return 0;
	}
	// have found the right channel.
	channel_kit_setting=config_setting_get_member(channel,"kit");
	if (channel_kit_setting==NULL) {
		printf("No 'kit' setting for channel: %s\n",chanstr);
		return 0;
	}
	channel_kit=config_setting_get_string(channel_kit_setting);
	if (channel_kit_setting==NULL) {
		printf("Kit setting empty or invalid in channel: %s\n",chanstr);
		return 0;
	}
	channel_midi_setting=config_setting_get_member(channel,"midichannel");
	if (channel_midi_setting==NULL) {
		printf("No 'midichannel' setting for channel: %s\n",chanstr);
		return 0;
	}
	channel_midi=config_setting_get_int(channel_midi_setting);
	if (channel_midi>16 || 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;i<num_mappings;i++) {
		mapping=config_setting_get_elem(mappings,i);
		map_name_setting=config_setting_get_member(mapping,"name");
		if (map_name_setting==NULL) {
			printf("\nNo map name for mapping number: %d\n",i);
			return 0;
		}
		map_name=config_setting_get_string(map_name_setting);
		map_hotkey_setting=config_setting_get_member(mapping,"hotkey");
		if (map_hotkey_setting==NULL) {
			map_hotkey="\01";
		} else {
			map_hotkey=config_setting_get_string(map_hotkey_setting);
		}
		if (action==MAP_READNAME && strcmp(map_name,mapstr)==0) {
			//printf("Found mapping: %s\n",map_name);
			break;
		} else if (action==MAP_READKEY && strncmp(map_hotkey,mapstr,1)==0) {
			break;
		} else if (action==MAP_READINDEX && i==mapno-1) {
			break;
		} else if (action==MAP_PRINT) {
			printf("%d - %s",i+1,map_name);
			if (strlen(map_hotkey)>0) {
				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<num_pads; i++) {
		pad_group=config_setting_get_elem(pads,i);
		pad_num_setting=config_setting_get_member(pad_group,"pad");
		if (pad_num_setting==NULL) {
			printf("No 'pad' setting in pad group number %d\n",i);
			return 0;
		}
		pad_num=config_setting_get_int(pad_num_setting);
		if (pad_num<0 || pad_num > 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);
}

device="/dev/ttyUSB0";
mapping="Tom Toms";
pads=4;

kits: (
	{	name="GM Kit";
		sounds=(
			(35,"Acoustic Bass Drum"), 
			(36,"Bass Drum 1"), 
			(37,"Side Stick"), 
			(38,"Acoustic Snare"), 
			(39,"Hand Clap"), 
			(40,"Electric Snare"),
			(41,"Low Floor Tom"), 
			(42,"Closed Hi-Hat"), 
			(43,"High Floor Tom"), 
			(44,"Pedal Hi-Hat"),
			(45,"Low Tom"), 
			(46,"Open Hi-Hat"), 
			(47,"Low-Mid Tom"), 
			(48,"Hi-Mid Tom"), 
			(49,"Crash Cymbal 1"), 
			(50,"High Tom"), 
			(51,"Ride Cymbal 1"), 
			(52,"Chinese Cymbal"), 
			(53,"Ride Bell"), 
			(54,"Tambourine"), 
			(55,"Splash Cymbal"), 
			(56,"Cowbell"), 
			(57,"Crash Cymbal 2"), 
			(58,"Vibraslap"),
			(59,"Ride Cymbal 2"),
			(60,"Hi Bongo"),
			(61,"Low Bongo"),
			(62,"Mute Hi Conga"),
			(63,"Open Hi Conga"),
			(64,"Low Conga"),
			(65,"High Timbale"),
			(66,"Low Timbale"),
			(67,"High Agogo"),
			(68,"Low Agogo"),
			(69,"Cabasa"),
			(70,"Maracas"),
			(71,"Short Whistle"),
			(72,"Long Whistle"),
			(73,"Short Guiro"),
			(74,"Long Guiro"),
			(75,"Claves"),
			(76,"Hi Wood Block"),
			(77,"Low Wood Block"),
			(78,"Mute Cuica"),
			(79,"Open Cuica"),
			(80,"Mute Triangle"),
			(81,"Open Triangle")
		);
	},
	{
		name="A Minor Pentatonic";
		sounds=(
			(45,"A2"),
			(48,"C3"),
			(50,"D3"),
			(52,"E3"),
			(55,"G3"),
			(57,"A3")
		);
	}
);

channels: (
	{ name="GM Kit 1"; kit="GM Kit"; midichannel=10; },
	{ name="A Minor Pent 1"; kit="A Minor Pentatonic"; midichannel=1;}
	);
	
mappings: (
	{ 	name="Tom Toms";
		hotkey="t";
		pads: (
			{pad=0; channel="GM Kit 1"; sound="Low Floor Tom"; },
			{pad=1; channel="GM Kit 1"; sound="Low Tom";},
			{pad=2; channel="GM Kit 1"; sound="Low-Mid Tom";},
			{pad=3; channel="GM Kit 1"; sound="High Tom";}
		); },
	{	name="Congas and claves";
		hotkey="c";
		pads: (
			{ pad=0; channel="GM Kit 1"; sound="Low Conga"; },
			{ pad=1; channel="GM Kit 1"; sound="Open Hi Conga"; },
			{ pad=2; channel="GM Kit 1"; sound="Mute Hi Conga"; },
			{ pad=3; channel="GM Kit 1"; sound="Claves"; }
		); },
	{	name="Acoustic Kit";
		hotkey="a";
		pads: (
			{pad=0; channel="GM Kit 1"; sound="Acoustic Bass Drum"; },
			{pad=1; channel="GM Kit 1"; sound="Acoustic Snare"; },
			{pad=2; channel="GM Kit 1"; sound="Closed Hi-Hat"; },
			{pad=3; channel="GM Kit 1"; sound="Pedal Hi-Hat"; }
		); },
	{	name="Bass, Bongo and Maracas";
		hotkey="b";
		pads: (
			{pad=0; channel="GM Kit 1"; sound="Bass Drum 1"; },
			{pad=1; channel="GM Kit 1"; sound="Low Bongo"; },
			{pad=2; channel="GM Kit 1"; sound="Hi Bongo"; },
			{pad=3; channel="GM Kit 1"; sound="Maracas"; }
		); },
	{	name="Tuned";
		hotkey="n";
		pads: (
			{pad=0; channel="A Minor Pent 1"; sound="A2"; },
			{pad=1; channel="A Minor Pent 1"; sound="C3"; },
			{pad=2; channel="A Minor Pent 1"; sound="D3"; },
			{pad=3; channel="A Minor Pent 1"; sound="E3"; }
			);
		}

);



_______________________________________________
Linux-audio-user mailing list
Linux-audio-user@xxxxxxxxxxxxxxxxxxxx
http://lists.linuxaudio.org/mailman/listinfo/linux-audio-user

[Index of Archives]     [Linux Sound]     [ALSA Users]     [Pulse Audio]     [ALSA Devel]     [Sox Users]     [Linux Media]     [Kernel]     [Photo Sharing]     [Gimp]     [Yosemite News]     [Linux Media]

  Powered by Linux