Carl Karsten wrote:
Jan Jockusch wrote:
I published the patch to the list, but it's still less than
half-finished. I will be glad to team up with anyone brave enough to
test the new functions with different hardware, so just drop me a
line, anyone.
I now have a ShuttlePro2. building c-cv cvs. where is this patch you
speak of?
Your dmesg looks fine. I've attached the patch. But I guess it won't
work out of the box.
To find out about your controller, please run 'aconnect -l -i' and see
if you can find your device.
You should also be able to find an according '/dev/snd/midiCxDx' device
node.
After that, you should use 'amidi -d -p <your port>' to dump raw midi
data from the device and see if it meets your expectations.
If you send me that dump along with comments when you did what (turn
left, turn right, push button), then I can configure the patch
accordingly. The MIDI control/key switch statement can be easily found
in the patch, so you might even change the patch yourself.
Good luck and don't hesitate to ask if anything's unclear.
- Ján
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();