Index: cinelerra/midicontrol.inc
===================================================================
--- cinelerra/midicontrol.inc	(revision 0)
+++ cinelerra/midicontrol.inc	(revision 0)
@@ -0,0 +1,6 @@
+#ifndef MIDICONTROL_INC
+#define MIDICONTROL_INC
+
+class MidiControl;
+
+#endif
Index: cinelerra/Makefile.am
===================================================================
--- cinelerra/Makefile.am	(revision 1055)
+++ cinelerra/Makefile.am	(working copy)
@@ -208,6 +208,7 @@
 		    menueffects.C \
 		    menuveffects.C \
 		    meterpanel.C \
+		    midicontrol.C \
 		    module.C \
 		    mtimebar.C \
 		    mwindow.C \
Index: cinelerra/midicontrol.C
===================================================================
--- cinelerra/midicontrol.C	(revision 0)
+++ cinelerra/midicontrol.C	(revision 0)
@@ -0,0 +1,330 @@
+#include "edl.h"
+#include "edlsession.h"
+#include "localsession.h"
+#include "tracks.h"
+#include "mbuttons.h"
+#include "mwindow.h"
+#include "mwindowgui.h"
+#include "vwindow.h"
+#include "vplayback.h"
+#include "vwindowgui.h"
+#include "cwindow.h"
+#include "cplayback.h"
+#include "transportque.h"
+#include "cwindowgui.h"
+#include "awindow.h"
+#include "awindowgui.h"
+#include "mainsession.h"
+#include "mainclock.h"
+
+#include "midicontrol.h"
+
+#define DEBUG(w) printf("%s %d - %s: %s\n", __FILE__, __LINE__,  __PRETTY_FUNCTION__, (w))
+
+/*
+ * Storing the configuration: Enumerate functions to be automated. Each function can
+ * be used by keypress, keyrelease, CCabs or CCrel events. (sliders require CCabs or CCrel,
+ * triggers require keypress or keyrelease.
+ * func_num, func_type (key or cc), subtype (press/release or abs/rel), channel, key/cc 
+ */
+
+
+MidiControl::MidiControl(MWindow *mwindow)
+ : Thread()
+{
+	int err;
+
+	DEBUG("entering..");
+	this->mwindow = mwindow;
+	mwindow_needs_update = 0;
+
+	// Open the MIDI device here
+
+	err = snd_seq_open(&seq_handle,  "default", SND_SEQ_OPEN_DUPLEX, SND_SEQ_NONBLOCK);
+	if (err != 0) {
+		// Block MIDI feature silently.
+	}
+
+	snd_seq_set_client_name(seq_handle, "Cinelerra");
+
+	snd_seq_port_info_alloca(&port_info);
+	snd_seq_port_info_set_capability(port_info, SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE |
+					 SND_SEQ_PORT_CAP_READ | SND_SEQ_PORT_CAP_SUBS_READ );
+	snd_seq_port_info_set_type(port_info, SND_SEQ_PORT_TYPE_MIDI_GENERIC | SND_SEQ_PORT_TYPE_APPLICATION );
+	snd_seq_port_info_set_midi_channels(port_info, 16);
+	snd_seq_port_info_set_name(port_info, "Cinelerra");
+	port = snd_seq_create_port(seq_handle, port_info);	
+	channel = 0;
+}
+
+MidiControl::~MidiControl()
+{
+	DEBUG("entering...");
+	// Cleanup code here
+	snd_seq_close(seq_handle);
+}
+
+void MidiControl::handle_event(snd_seq_event_t *ev)
+{
+	int diff;
+	double abs;
+	char channel;
+	char midicontrol;
+	char midivalue;
+
+	EDL *edl;
+	double total_length, current, frame_rate, delta;
+
+	double new_position;
+
+	switch(ev->type) {
+	case SND_SEQ_EVENT_CONTROLLER:
+		// handle_cc(ev->data.control)
+		channel = ev->data.control.channel;
+		midicontrol = ev->data.control.param;
+		midivalue = ev->data.control.value;
+		printf("MidiControl: CC chan %d, ctrl %d, val %d\n", channel, midicontrol, midivalue);
+		diff = midivalue - 64;
+		abs = (midivalue < 64) ? midivalue / 128. : (midivalue-1) / 126.;
+		/*
+		 * search for param in list of params
+		 * for each match: interpret as rel/abs?
+		 *   choose correct function (switch)
+		 *   run the function.
+		 */
+		switch (ev->data.control.param) {
+		// Control 1: Left wheel for view window.
+		case 1:
+			edl = mwindow->vwindow->get_edl();
+			if (!diff) break;
+			if (!edl) break;
+			total_length = edl->tracks->total_length();
+			current = mwindow->vwindow->gui->slider->get_value();
+			frame_rate = edl->session->frame_rate;
+			// Calculate difference in half-frames
+			delta = (double)diff / frame_rate / 4.;
+			current += delta;
+			if (current > total_length) current = total_length;
+			if (current < 0) current = 0;
+			mwindow->vwindow->gui->transport->handle_transport(STOP, 0, 0);
+			edl->local_session->set_selectionstart(current);
+			edl->local_session->set_selectionend(current);
+			mwindow->vwindow->gui->slider->set_position();
+			mwindow->vwindow->playback_engine->que->send_command(CURRENT_FRAME,
+									     CHANGE_NONE,
+									     edl,
+									     1);
+			break;
+	
+		// Control 3: Right wheel for compositor window.
+		case 3:
+			edl = mwindow->edl;
+			if (!diff) break;
+			if (!edl) break;
+			total_length = edl->tracks->total_playable_length();
+			current = edl->local_session->get_selectionstart(1);
+			frame_rate = edl->session->frame_rate;
+			// Calculate difference in half-frames
+			delta = (double)diff / frame_rate / 4.;
+			current += delta;
+			if (current > total_length) current = total_length;
+			if (current < 0) current = 0;
+			mwindow->gui->mbuttons->transport->handle_transport(STOP, 1, 0, 0);
+			// current = edl->align_to_frame(current, 1);
+			edl->local_session->set_selectionstart(current);
+			edl->local_session->set_selectionend(current);
+			mwindow_needs_update = 1;
+			mwindow->cwindow->gui->slider->set_position();
+			mwindow->cwindow->playback_engine->que->send_command(CURRENT_FRAME,
+									     CHANGE_NONE,
+									     edl,
+									     1);
+			break;
+#if 0
+		case 17:
+			// Dragging an edit handle
+			TrackCanvas *track_canvas = mwindow->gui->canvas;
+			edl = mwindow->edl;
+			current = mwindow->session->drag_position;
+			frame_rate = edl->session->frame_rate;
+			// Calculate difference in half-frames
+			delta = (double)diff / frame_rate / 4.;
+
+			new_position = current + delta;
+			new_position = 
+				edl->align_to_frame(new_position, 0);
+			if(new_position != mwindow->session->drag_position) {
+				mwindow->session->drag_position = new_position;
+				mwindow->gui->mainclock->update(new_position);
+			}
+			break;
+#endif
+		}
+		break;
+	case SND_SEQ_EVENT_NOTEON:
+		channel = ev->data.note.channel;
+		midicontrol = ev->data.note.note;
+		midivalue = ev->data.note.velocity;
+		printf("MidiControl: KeyOn chan %d, note %d, vel %d\n", channel, midicontrol, midivalue);
+		if (!(ev->data.note.velocity)) break;
+		switch (ev->data.note.note) {
+		case 17: // VIEWER_SET_INPOINT
+			mwindow->vwindow->set_inpoint(); break;
+		case 21: // VIEWER_SET_OUTPOINT
+			mwindow->vwindow->set_outpoint(); break;
+		case 16: // VIEWER_SPLICE
+			mwindow->vwindow->gui->edit_panel->splice_selection(); break;
+		case 22: // VIEWER_OVERWRITE
+			mwindow->vwindow->gui->edit_panel->overwrite_selection(); break;
+		case 18: // VIEWER_GOTO_START
+			mwindow->vwindow->goto_start(); break;
+		case 20: // VIEWER_GOTO_END
+			mwindow->vwindow->goto_end(); break;
+		case 55: // VIEWER_PLAY
+			mwindow->vwindow->gui->transport->handle_transport(NORMAL_FWD, 0, 0); break;
+		case 23: // TOGGLE_EDIT_MODE
+			mwindow->toggle_editing_mode(); break;
+		case 65: // ASSETS_NEXT
+			AWindowAssets *asset_list;
+			asset_list = mwindow->awindow->gui->asset_list;
+			asset_list->select_next(0);
+			asset_list->center_selection();
+			break;
+		case 67: // ASSETS_PREV
+			asset_list = mwindow->awindow->gui->asset_list;
+			asset_list->select_previous(0);
+			asset_list->center_selection();
+			break;
+		case 69: // ASSETS_VIEW
+			asset_list = mwindow->awindow->gui->asset_list;
+			if (!(asset_list->get_selection(0, 0))) break;
+                        if (!(((AssetPicon*)(asset_list->get_selection(0, 0)))->asset)) break;
+			mwindow->vwindow->change_source(((AssetPicon*)(asset_list->get_selection(0, 0)))->asset);
+			break;
+		case 68: // PREV_EDIT_HANDLE
+			mwindow->gui->mbuttons->transport->handle_transport(STOP, 1, 0, 0);
+			mwindow->gui->lock_window("MidiControl::handle_event");
+			mwindow->prev_edit_handle(0); // 1 to hold shift (select range)
+			mwindow->gui->unlock_window();
+			break;
+		case 66: // NEXT_EDIT_HANDLE
+			mwindow->gui->mbuttons->transport->handle_transport(STOP, 1, 0, 0);
+			mwindow->gui->lock_window("MidiControl::handle_event");
+			mwindow->next_edit_handle(0); // 1 to hold shift (select range)
+			mwindow->gui->unlock_window();
+			break;
+		case 34: // GOTO_START
+			mwindow->gui->mbuttons->transport->handle_transport(REWIND, 1);
+			mwindow->gui->lock_window("MidiControl::handle_event");
+			mwindow->goto_start();
+			mwindow->gui->unlock_window();
+			break;
+		case 36: // GOTO_END
+			mwindow->gui->mbuttons->transport->handle_transport(GOTO_END, 1);
+			mwindow->gui->lock_window("MidiControl::handle_event");
+			mwindow->goto_end();
+			mwindow->gui->unlock_window();
+			break;
+		case 33: // SET_INPOINT
+			mwindow->set_inpoint(1);
+			break;
+		case 37: // SET_OUTPOINT
+			mwindow->set_outpoint(1);
+			break;
+		case 54: // PLAY
+			mwindow->gui->mbuttons->transport->handle_transport(NORMAL_FWD, 1); break;
+		}
+		break;
+	case SND_SEQ_EVENT_NOTEOFF:
+		channel = ev->data.note.channel;
+		midicontrol = ev->data.note.note;
+		midivalue = ev->data.note.velocity;
+		printf("MidiControl: KeyOff chan %d, note %d, vel %d\n", channel, midicontrol, midivalue);
+		break;
+	case SND_SEQ_EVENT_PITCHBEND:
+		channel = ev->data.control.channel;
+		int value = ev->data.control.value;
+		printf("MidiControl: Pitch chan %d, val %d\n", channel, value);
+		break;
+	}
+	
+}
+
+void MidiControl::run()
+{
+	int num_polls;
+	struct pollfd *polls;
+	int rt, idle;
+
+	DEBUG("entering...");
+	// MIDI listener here
+	num_polls = snd_seq_poll_descriptors_count(seq_handle, POLLIN);
+	polls = (pollfd *)alloca(sizeof(*polls) * num_polls);
+	snd_seq_poll_descriptors(seq_handle, polls, num_polls, POLLIN);
+
+	while (true) {
+		DEBUG("poll loop...");
+		rt = poll(polls, num_polls, 1000);
+		if (rt < 0) continue;
+		idle = true;
+		do {
+			snd_seq_event_t * ev;
+			if (snd_seq_event_input(seq_handle, &ev) >= 0 && ev) {
+				handle_event(ev);
+				idle = false;
+			}
+		} while (snd_seq_event_input_pending(seq_handle, 0) > 0);
+		// Deferred slow main window update happens here:
+		if (mwindow_needs_update && idle) {
+			mwindow_needs_update = 0;
+			mwindow->gui->lock_window();
+			mwindow->find_cursor();
+			mwindow->gui->update(1, 1, 1, 1, 1, 1, 0);
+			mwindow->gui->unlock_window();
+		}
+	}
+}
+
+
+
+
+void MidiControl::send_key(int key, int velocity)
+{
+	snd_seq_event_t ev;
+	DEBUG("entering...");
+
+	// Initialize the event structure
+	snd_seq_ev_set_direct(&ev);
+	snd_seq_ev_set_source(&ev, port);
+
+	// Send to all subscribers
+	snd_seq_ev_set_dest(&ev, SND_SEQ_ADDRESS_SUBSCRIBERS, 0);
+
+	// Send keypress event
+        snd_seq_ev_set_noteon(&ev, channel, key, velocity);
+	snd_seq_event_output_direct(seq_handle, &ev);
+}
+
+void MidiControl::send_cc(int control, int value)
+{
+	snd_seq_event_t ev;
+	DEBUG("entering...");
+
+	// Initialize the event structure
+	snd_seq_ev_set_direct(&ev);
+	snd_seq_ev_set_source(&ev, port);
+
+	// Send to all subscribers
+	snd_seq_ev_set_dest(&ev, SND_SEQ_ADDRESS_SUBSCRIBERS, 0);
+
+	// Send control change
+	snd_seq_ev_set_controller(&ev, channel, control, value);
+	snd_seq_event_output_direct(seq_handle, &ev);
+}
+
+void MidiControl::set_channel(int channel)
+{
+	DEBUG("entering...");
+	this->channel = channel;
+}
+
Index: cinelerra/midicontrol.h
===================================================================
--- cinelerra/midicontrol.h	(revision 0)
+++ cinelerra/midicontrol.h	(revision 0)
@@ -0,0 +1,38 @@
+#ifndef MIDICONTROL_H
+#define MIDICONTROL_H
+
+
+#include "mwindow.inc"
+#include "thread.h"
+#include <alsa/asoundlib.h>
+
+// ================================= Listen to MIDI input in a thread
+// Listens to a MIDI input and performs callbacks accordingly.
+
+class MidiControl : public Thread
+{
+public:
+	MidiControl(MWindow *mwindow);
+	~MidiControl();
+
+	void run();
+
+	void set_channel(int ch);
+
+	void send_key(int key, int velocity);
+	void send_cc(int param, int value);
+
+private:
+	void handle_event(snd_seq_event_t *ev);
+
+	MWindow *mwindow;
+	int mwindow_needs_update;
+
+	int channel;
+
+	snd_seq_t *seq_handle;
+	snd_seq_port_info_t *port_info;
+	int port;
+};
+
+#endif
Index: cinelerra/mwindow.C
===================================================================
--- cinelerra/mwindow.C	(revision 1055)
+++ cinelerra/mwindow.C	(working copy)
@@ -82,6 +82,7 @@
 #include "wavecache.h"
 #include "zoombar.h"
 #include "exportedl.h"
+#include "midicontrol.h"
 
 #include <string.h>
 
@@ -647,6 +648,11 @@
 	channeldb_v4l2jpeg->load("channeldb_v4l2jpeg");
 }
 
+void MWindow::init_midicontrol()
+{
+	midi_control = new MidiControl(this);
+}
+
 void MWindow::init_menus()
 {
 	char string[BCTEXTLEN];
@@ -1311,6 +1317,8 @@
 SET_TRACE
 	init_channeldb();
 SET_TRACE
+	init_midicontrol();
+SET_TRACE
 
 	init_gui();
 	init_gwindow();
@@ -1353,6 +1361,8 @@
 	hide_splash();
 SET_TRACE
 	init_shm();
+
+	midi_control->start(); 
 }
 
 
Index: cinelerra/mwindow.h
===================================================================
--- cinelerra/mwindow.h	(revision 1055)
+++ cinelerra/mwindow.h	(working copy)
@@ -62,6 +62,7 @@
 #include "videowindow.inc"
 #include "vwindow.inc"
 #include "wavecache.inc"
+#include "midicontrol.inc"
 
 #include <stdint.h>
 
@@ -410,6 +411,8 @@
 	MainUndo *undo;
 	BC_Hash *defaults;
 	Assets *assets;
+// MIDI Control
+	MidiControl *midi_control;
 // CICaches for drawing timeline only
 	CICache *audio_cache, *video_cache;
 // Frame cache for drawing timeline only.
@@ -527,6 +530,7 @@
 	void init_levelwindow();
 	void init_viewer();
 	void init_cache();
+	void init_midicontrol();
 	void init_menus();
 	void init_indexes();
 	void init_gui();
