Initial commit

master
Peter Nelson 2010-01-24 20:46:33 +00:00
commit b971b9b4c1
10 changed files with 583 additions and 0 deletions

63
CMakeLists.txt 100644
View File

@ -0,0 +1,63 @@
cmake_minimum_required(VERSION 2.0)
PROJECT(jm2cv)
INCLUDE( ${CMAKE_ROOT}/Modules/FindPkgConfig.cmake )
INCLUDE( ${CMAKE_ROOT}/Modules/CheckIncludeFile.cmake )
#pkg_check_modules(LV2CORE lv2core)
pkg_check_modules(JACK jack>=0.118)
#pkg_check_modules(GTKMM gtkmm-2.4>=2.4)
#pkg_check_modules(CAIROMM cairomm-1.0>=1.0)
#pkg_check_modules(DBUS dbus-glib-1)
#pkg_check_modules(PCRE libpcrecpp)
#ADD_CUSTOM_COMMAND(
# OUTPUT ${CMAKE_CURRENT_BINARY_DIR}/jsweeper.ui
# COMMAND gtk-builder-convert ${CMAKE_CURRENT_SOURCE_DIR}/src/jsweeper.glade ${CMAKE_CURRENT_BINARY_DIR}/jsweeper.ui
# DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/src/jsweeper.glade
#)
SET(SOURCES
client.cpp
client.h
cvin.cpp
cvin.h
cvout.cpp
cvout.h
jm2cv.cpp
)
LINK_DIRECTORIES(
# ${LV2CORE_LIBRARY_DIRS}
${JACK_LIBRARY_DIRS}
# ${GTKMM_LIBRARY_DIRS}
# ${CAIROMM_LIBRARY_DIRS}
# ${DBUS_LIBRARY_DIRS}
# ${PCRE_LIBRARY_DIRS}
)
INCLUDE_DIRECTORIES(
# ${LV2CORE_INCLUDE_DIRS}
${JACK_INCLUDE_DIRS}
# ${GTKMM_INCLUDE_DIRS}
# ${CAIROMM_INCLUDE_DIRS}
# ${DBUS_INCLUDE_DIRS}
# ${PCRE_INCLUDE_DIRS}
)
ADD_DEFINITIONS(-g -Wall -Wextra -pedantic -D_GNU_SOURCE)
ADD_EXECUTABLE(jm2cv ${SOURCES})
TARGET_LINK_LIBRARIES(jm2cv
-lm
# ${LV2CORE_LIBRARIES}
${JACK_LIBRARIES}
# ${GTKMM_LIBRARIES}
# ${CAIROMM_LIBRARIES}
# ${DBUS_LIBRARIES}
# ${PCRE_LIBRARIES}
)
INSTALL(PROGRAMS jm2cv DESTINATION bin)
#INSTALL(FILES jsweeper.ui DESTINATION share/jsweeper)

48
client.cpp 100644
View File

@ -0,0 +1,48 @@
#include <iostream>
#include "client.h"
bool Client::open(const char *name)
{
jack_status_t status;
m_client = jack_client_open(name, JackNoStartServer, &status);
if (m_client == NULL) {
if (status & JackServerFailed) {
std::cerr << "JACK server not running" << std::endl;
} else {
std::cerr << "jack_client_open() failed, status = " << status << std::endl;
}
return false;
}
m_name = jack_get_client_name(m_client);
m_sample_rate = jack_get_sample_rate(m_client);
jack_on_shutdown(m_client, &shutdown, this);
jack_set_process_callback(m_client, &process, this);
jack_activate(m_client);
return true;
}
void Client::close()
{
jack_deactivate(m_client);
jack_client_close(m_client);
}
void Client::shutdown(void *arg)
{
((Client *)arg)->shutdown();
}
int Client::process(jack_nframes_t nframes, void *arg)
{
return ((Client *)arg)->process(nframes);
}
jack_port_t *Client::port_register(const char *port_name, const char *port_type, unsigned long flags, unsigned long buffer_size)
{
return jack_port_register(m_client, port_name, port_type, flags, buffer_size);
}

40
client.h 100644
View File

@ -0,0 +1,40 @@
#ifndef CLIENT_H
#define CLIENT_H
#include <jack/jack.h>
typedef jack_default_audio_sample_t sample_t;
typedef jack_nframes_t tick_t;
class Client
{
private:
jack_client_t *m_client;
protected:
jack_port_t **m_ports;
const char *m_name;
jack_nframes_t m_sample_rate;
private:
static void shutdown(void *arg);
static int process(jack_nframes_t nframes, void *arg);
virtual void shutdown() = 0;
virtual int process(jack_nframes_t) = 0;
public:
Client() : m_client(NULL), m_ports(NULL), m_name(NULL), m_sample_rate(0)
{
}
~Client()
{
}
bool open(const char *name);
void close();
jack_port_t *port_register(const char *port_name, const char *port_type, unsigned long flags, unsigned long buffer_size);
};
#endif // CLIENT_H

80
cvin.cpp 100644
View File

@ -0,0 +1,80 @@
#include <jack/jack.h>
#include <jack/midiport.h>
#include "cvin.h"
void CVIn::shutdown()
{
}
int CVIn::process(jack_nframes_t nframes)
{
if (!m_ready) return 0;
void *midi_out = jack_port_get_buffer(m_midi_out, nframes);
jack_midi_clear_buffer(midi_out);
for (unsigned i = 0; i < m_mapping_list.size(); i++) {
m_buffers[i] = static_cast<sample_t *>(jack_port_get_buffer(m_ports[i], nframes));
}
for (jack_nframes_t f = 0; f < nframes; f++) {
int port = 0;
MappingList::iterator it;
for (it = m_it_begin; it != m_it_end; ++it, port++) {
Mapping *m = &(*it);
m->cur_mv = m->to_mv(*m_buffers[port]++);
if (m->tick_cvin()) {
if (m->cclsb == -1) {
jack_midi_data_t buf[3];
buf[0] = 0xB0 | m->channel;
buf[1] = m->ccmsb;
buf[2] = m->last_mv;
jack_midi_event_write(midi_out, f, buf, sizeof buf);
} else {
jack_midi_data_t buf[3];
buf[0] = 0xB0 | m->channel;
buf[1] = m->ccmsb;
buf[2] = m->last_mv >> 7;
jack_midi_event_write(midi_out, f, buf, sizeof buf);
buf[1] = m->cclsb;
buf[2] = m->last_mv & 0x7F;
jack_midi_event_write(midi_out, f, buf, sizeof buf);
}
}
}
}
return 0;
}
void CVIn::start()
{
m_ready = false;
m_tick = 0;
if (m_mapping_list.size() == 0) return;
m_ports = new jack_port_t *[m_mapping_list.size()];
m_buffers = new sample_t *[m_mapping_list.size()];
open("m2cv_in");
m_midi_out = port_register("midi_out", JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0);
MappingList::iterator it_begin = m_mapping_list.begin();
MappingList::iterator it_end = m_mapping_list.end();
int port = 0;
MappingList::iterator it;
for (it = it_begin; it != it_end; ++it, port++) {
Mapping *m = &(*it);
m_ports[port] = port_register(m->name.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0);
m->latency_ticks = m_sample_rate * m->latency / 1000.0f;
}
m_it_end = it_end;
m_it_begin = it_begin;
m_ready = true;
}

36
cvin.h 100644
View File

@ -0,0 +1,36 @@
#ifndef CVIN_H
#define CVIN_H
#include "client.h"
#include "mapping.h"
class CVIn : Client
{
private:
MappingList m_mapping_list;
jack_port_t *m_midi_out;
bool m_ready;
tick_t m_tick;
sample_t **m_buffers;
MappingList::iterator m_it_begin;
MappingList::iterator m_it_end;
void shutdown();
int process(jack_nframes_t nframes);
public:
void add_mapping(Mapping m)
{
m_mapping_list.push_back(m);
}
void start();
void stop()
{
close();
}
};
#endif // CVIN_H

84
cvout.cpp 100644
View File

@ -0,0 +1,84 @@
#include <jack/jack.h>
#include <jack/midiport.h>
#include "cvout.h"
void CVOut::shutdown()
{
}
int CVOut::process(jack_nframes_t nframes)
{
if (!m_ready) return 0;
void *midi_in = jack_port_get_buffer(m_midi_in, nframes);
for (unsigned i = 0; i < m_mapping_list.size(); i++) {
m_buffers[i] = static_cast<sample_t *>(jack_port_get_buffer(m_ports[i], nframes));
}
jack_nframes_t event_count = jack_midi_get_event_count(midi_in);
jack_nframes_t event_index = 0;
jack_midi_event_t ev;
if (0 < event_count) jack_midi_event_get(&ev, midi_in, event_index);
for (jack_nframes_t f = 0; f < nframes; f++) {
while (ev.time == f && event_index < event_count) {
if ((ev.buffer[0] & 0xF0) == 0xB0) {
// Do what
int channel = ev.buffer[0] & 0x0F;
MappingList::iterator it;
for (it = m_it_begin; it != m_it_end; ++it) {
Mapping *m = &(*it);
m->handle_cvout(channel, ev.buffer[1], ev.buffer[2], m_tick);
}
}
event_index++;
if (event_index < event_count) jack_midi_event_get(&ev, midi_in, event_index);
}
int port = 0;
MappingList::iterator it;
for (it = m_it_begin; it != m_it_end; ++it, port++) {
const Mapping *m = &(*it);
*m_buffers[port]++ = m->last_cv;
}
m_tick++;
}
return 0;
}
void CVOut::start()
{
m_ready = false;
m_tick = 0;
if (m_mapping_list.size() == 0) return;
m_ports = new jack_port_t *[m_mapping_list.size()];
m_buffers = new sample_t *[m_mapping_list.size()];
open("m2cv_out");
m_midi_in = port_register("midi_in", JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0);
MappingList::iterator it_begin = m_mapping_list.begin();
MappingList::iterator it_end = m_mapping_list.end();
int port = 0;
MappingList::iterator it;
for (it = it_begin; it != it_end; ++it, port++) {
Mapping *m = &(*it);
m_ports[port] = port_register(m->name.c_str(), JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0);
m->latency_ticks = m_sample_rate * m->latency / 1000.0f;
}
m_it_end = it_end;
m_it_begin = it_begin;
m_ready = true;
}

32
cvout.h 100644
View File

@ -0,0 +1,32 @@
#include "client.h"
#include "mapping.h"
class CVOut : Client
{
private:
MappingList m_mapping_list;
jack_port_t *m_midi_in;
bool m_ready;
tick_t m_tick;
sample_t **m_buffers;
MappingList::iterator m_it_begin;
MappingList::iterator m_it_end;
void shutdown();
int process(jack_nframes_t nframes);
public:
void add_mapping(Mapping m)
{
m_mapping_list.push_back(m);
}
void start();
void stop()
{
close();
}
};

16
example.cfg 100644
View File

@ -0,0 +1,16 @@
#type name chan, cc MSB/LSB, midi range, cv range, latency
cvout pan -1 10 -1 0 127 -1.0 1.0 10
cvout reverb -1 91 -1 0 127 -1.0 1.0 10
cvout exp -1 11 -1 0 127 -1.0 1.0 10
cvout vol1 0 7 -1 0 127 -1.0 1.0 10
cvout vol2 1 7 -1 0 127 -1.0 1.0 10
cvout vol3 2 7 -1 0 127 -1.0 1.0 10
cvout vol4 3 7 -1 0 127 -1.0 1.0 10
cvout vol5 4 7 -1 0 127 -1.0 1.0 10
cvout vol6 5 7 -1 0 127 -1.0 1.0 10
cvout vol7 6 7 -1 0 127 -1.0 1.0 10
cvout vol8 7 7 -1 0 127 -1.0 1.0 10
cvout mod -1 1 33 0 16383 -1.0 1.0 10
cvin mod 0 1 33 0 16383 -1.0 1.0 10
cvin vol1 0 7 -1 0 127 -1.0 1.0 10

85
jm2cv.cpp 100644
View File

@ -0,0 +1,85 @@
#include <iostream>
#include <cstring>
#include <csignal>
#include "cvout.h"
#include "cvin.h"
static CVOut cvout;
static CVIn cvin;
bool read_config(const char *filename)
{
FILE *f = fopen(filename, "r");
if (f == NULL) {
std::cerr << "Unable to open '" << filename << "' for reading" << std::endl;
return false;
}
while (!feof(f)) {
char buf[80];
fgets(buf, sizeof buf, f);
/* Ignore comments */
if (buf[0] == '#') continue;
char type[80], name[80];
int channel, ccmsb, cclsb, mrl, mru;
float crl, cru;
float latency;
if (sscanf(buf, "%s %s %d %d %d %d %d %f %f %f", type, name, &channel, &ccmsb, &cclsb, &mrl, &mru, &crl, &cru, &latency) == 10) {
if (ccmsb < 0 || ccmsb > 127) continue;
if (cclsb < -1 || cclsb > 127) continue;
if (mrl < 0 || mrl > (cclsb == -1 ? 127 : 16383)) continue;
if (mru < 0 || mru > (cclsb == -1 ? 127 : 16383)) continue;
if (mrl > mru) continue;
// if (crl < -1.0f || crl > 1.0f) continue;
// if (cru < -1.0f || cru > 1.0f) continue;
if (crl > cru) continue;
if (latency < 0.0f) continue;
if (!strcmp(type, "cvout")) {
if (channel < -1 || channel > 15) continue;
cvout.add_mapping(Mapping(name, channel, ccmsb, cclsb, mrl, mru, crl, cru, latency));
} else if (!strcmp(type, "cvin")) {
if (channel < 0 || channel > 15) continue;
cvin.add_mapping(Mapping(name, channel, ccmsb, cclsb, mrl, mru, crl, cru, latency));
}
}
}
return true;
}
static int _running = false;
static void sigint_handler(int)
{
_running = false;
}
int main(int argc, char **argv)
{
if (argc != 2) {
std::cerr << "Usage: " << argv[0] << " <config>" << std::endl;
return 0;
}
if (!read_config(argv[1])) return 0;
cvout.start();
cvin.start();
_running = true;
signal(SIGINT, &sigint_handler);
signal(SIGTERM, &sigint_handler);
while (_running) {
sleep(1);
}
cvin.stop();
cvout.stop();
}

99
mapping.h 100644
View File

@ -0,0 +1,99 @@
#ifndef MAPPING_H
#define MAPPING_H
#include <string>
#include <list>
class Mapping
{
public:
std::string name;
int channel;
int ccmsb, cclsb;
int mrl, mru;
float crl, cru;
float latency;
float adj1;
float adj2;
tick_t latency_ticks;
int cur_mv;
int last_mv;
sample_t cur_cv;
sample_t last_cv;
tick_t last_tick;
tick_t since_last_tick;
Mapping(const char *name,
int channel, int ccmsb, int cclsb,
int mrl, int mru, float crl, float cru, float latency) :
name(name), channel(channel), ccmsb(ccmsb), cclsb(cclsb),
mrl(mrl), mru(mru), crl(crl), cru(cru), latency(latency),
cur_mv(0), last_mv(0),
last_cv(0.0),
last_tick(0), since_last_tick(0)
{
/* Set up adjustment values for converting between
* MIDI values and CV values for this mapping. This
* results in scaling equations that are two ops. */
adj1 = (cru - crl) / (mru - mrl);
adj2 = (-crl / adj1) + mrl;
}
void interp_cvout(tick_t tick)
{
cur_cv = to_cv(cur_mv);
last_tick = tick;
last_cv = cur_cv;
}
void tick_cvout()
{
if (last_cv == cur_cv) return;
since_last_tick++;
}
bool tick_cvin()
{
since_last_tick++;
if (last_mv == cur_mv) return false;
if (since_last_tick < latency_ticks) return false;
since_last_tick = 0;
last_mv = cur_mv;
return true;
}
void handle_cvout(int c, int cc, int val, tick_t tick)
{
if (c != channel && channel != -1) return;
if (cc == ccmsb) {
if (cclsb == -1) {
cur_mv = val;
interp_cvout(tick);
} else {
cur_mv = val << 7;
}
} else if (cc == cclsb) {
cur_mv |= val;
interp_cvout(tick);
}
}
inline float to_cv(int mv)
{
return (mv - adj2) * adj1;
}
inline int to_mv(float cv)
{
return cv / adj1 + adj2;
}
};
typedef std::list<Mapping> MappingList;
#endif // MAPPING_H