Browse Source

Initial commit

master
Peter Nelson 12 years ago
commit
b971b9b4c1
  1. 63
      CMakeLists.txt
  2. 48
      client.cpp
  3. 40
      client.h
  4. 80
      cvin.cpp
  5. 36
      cvin.h
  6. 84
      cvout.cpp
  7. 32
      cvout.h
  8. 16
      example.cfg
  9. 85
      jm2cv.cpp
  10. 99
      mapping.h

63
CMakeLists.txt

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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

@ -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
Loading…
Cancel
Save