KORG microX utility.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

553 lines
11 KiB

/*
* asysex - A SysEx Utility
* Copyright (C) 2012,2013 Damien Goutte-Gattat
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
#ifdef HAVE_CONFIG_H
#include <config.h>
#endif
#include <midi.h>
#include <stdio.h>
#include <stdlib.h>
#include <assert.h>
#include <xmem.h>
#ifdef HAVE_ALSA
#include <alsa/asoundlib.h>
#endif
#ifdef HAVE_OSS
#include <fcntl.h>
#include <string.h>
#include <dirent.h>
#endif
typedef int (*m_close) (midi_io_t *);
typedef ssize_t (*m_write) (midi_io_t *, unsigned char *, size_t);
typedef ssize_t (*m_read) (midi_io_t *, unsigned char *, size_t);
typedef const char * (*m_error) (midi_io_t *);
#define MIDI_IO_BUFFER_SIZE 256
struct midi_io
{
/* Backend-dependent functions. */
struct {
m_close close;
m_write write;
m_read read;
m_error error;
} backend;
/* Common data. */
int error;
unsigned char buffer[MIDI_IO_BUFFER_SIZE];
size_t pos;
size_t len;
unsigned char status;
/* Backend-dependent data. */
union {
#ifdef HAVE_ALSA
struct {
snd_rawmidi_t *in;
snd_rawmidi_t *out;
} alsa;
#endif
#ifdef HAVE_OSS
struct {
int fd;
} oss;
#endif
};
};
/* Backend-specific functions. */
#ifdef HAVE_ALSA
static int
alsa_midi_close(midi_io_t *midi)
{
snd_rawmidi_close(midi->alsa.in);
snd_rawmidi_close(midi->alsa.out);
free(midi);
return 0;
}
static ssize_t
alsa_midi_write(midi_io_t *midi, unsigned char *buffer, size_t len)
{
/* Flush reading buffer. */
midi->pos = midi->len = 0;
midi->error = snd_rawmidi_write(midi->alsa.out, buffer, len);
return midi->error;
}
static ssize_t
alsa_midi_read(midi_io_t *midi, unsigned char *buffer, size_t len)
{
midi->error = snd_rawmidi_read(midi->alsa.in, buffer, len);
return midi->error;
}
static const char *
alsa_midi_error(midi_io_t *midi)
{
if ( midi->error > 0 )
return NULL;
else
return snd_strerror(midi->error);
}
static midi_io_t *
alsa_midi_open(const char *name)
{
midi_io_t *midi;
midi = xmalloc(sizeof(*midi));
midi->alsa.in = midi->alsa.out = NULL;
midi->error = midi->pos = midi->len = midi->status = 0;
midi->backend.close = alsa_midi_close;
midi->backend.write = alsa_midi_write;
midi->backend.read = alsa_midi_read;
midi->backend.error = alsa_midi_error;
if ( snd_rawmidi_open(&(midi->alsa.in), &(midi->alsa.out), name,
SND_RAWMIDI_NONBLOCK) < 0 ) {
free(midi);
midi = NULL;
}
else if ( snd_rawmidi_nonblock(midi->alsa.out, 0) < 0 ) {
midi_close(midi);
midi = NULL;
}
return midi;
}
static void
alsa_midi_get_ports(char ***ports, size_t *n, size_t *max)
{
char name[32];
int card;
card = -1;
while ( snd_card_next(&card) >= 0 && card != -1 ) {
snd_ctl_t *ctl;
snprintf(name, sizeof(name), "hw:%d", card);
if ( snd_ctl_open(&ctl, name, 0) >= 0 ) {
int device;
device = -1;
while ( snd_ctl_rawmidi_next_device(ctl, &device) >= 0 && device >= 0 ) {
snprintf(name, sizeof(name), "hw:%d,%d", card, device);
if ( *n >= *max ) {
*max += 10;
*ports = xrealloc(*ports, *max);
}
(*ports)[(*n)++] = xstrdup(name);
}
snd_ctl_close(ctl);
}
}
}
#endif /* HAVE_ALSA */
#ifdef HAVE_OSS
static int
oss_midi_close(midi_io_t *midi)
{
close(midi->oss.fd);
free(midi);
return 0;
}
static ssize_t
oss_midi_write(midi_io_t *midi, unsigned char *buffer, size_t len)
{
ssize_t n;
/* Flush reading buffer. */
midi->pos = midi->len = 0;
if ( (n = write(midi->oss.fd, buffer, len)) == -1 )
midi->error = errno;
else
midi->error = 0;
return n;
}
static ssize_t
oss_midi_read(midi_io_t *midi, unsigned char *buffer, size_t len)
{
ssize_t n;
if ( (n = read(midi->oss.fd, buffer, len)) == -1 )
midi->error = errno;
else
midi->error = 0;
return n;
}
static const char *
oss_midi_error(midi_io_t *midi)
{
if ( midi->error == 0 )
return NULL;
else
return strerror(midi->error);
}
static midi_io_t *
oss_midi_open(const char *name)
{
midi_io_t *midi;
assert(name != NULL);
midi = xmalloc(sizeof(*midi));
midi->oss.fd = -1;
midi->error = midi->pos = midi->len = midi->status = 0;
midi->backend.close = oss_midi_close;
midi->backend.write = oss_midi_write;
midi->backend.read = oss_midi_read;
midi->backend.error = oss_midi_error;
if ( (midi->oss.fd = open(name, O_RDWR, 0)) == -1 ) {
free(midi);
midi = NULL;
}
return midi;
}
static int
oss_midi_device(const struct dirent *entry)
{
return strncmp(entry->d_name, "midi", 4) == 0;
}
static void
oss_midi_get_ports(char ***ports, size_t *n, size_t *max)
{
char name[32];
size_t k;
struct dirent **namelist;
if ( (k = scandir("/dev", &namelist, oss_midi_device, alphasort)) != -1 ) {
while ( k-- ) {
snprintf(name, sizeof(name), "/dev/%s", namelist[k]->d_name);
free(namelist[k]);
if ( *n >= *max ) {
*max += 10;
*ports = xrealloc(*ports, *max);
}
(*ports)[(*n)++] = xstrdup(name);
}
}
}
#endif /* HAVE_OSS */
/* Backend-independent functions. */
midi_io_t *
midi_open(const char *name)
{
midi_io_t *midi;
midi = NULL;
#ifdef HAVE_ALSA
if ( midi == NULL )
midi = alsa_midi_open(name);
#endif
#ifdef HAVE_OSS
if ( midi == NULL )
midi = oss_midi_open(name);
#endif
return midi;
}
int
midi_close(midi_io_t *midi)
{
assert(midi != NULL);
return midi->backend.close(midi);
}
ssize_t
midi_write(midi_io_t *midi, unsigned char *buffer, size_t len)
{
assert(midi != NULL);
assert(buffer != NULL);
return midi->backend.write(midi, buffer, len);
}
ssize_t
midi_read(midi_io_t *midi, unsigned char *buffer, size_t len)
{
assert(midi != NULL);
assert(buffer != NULL);
return midi->backend.read(midi, buffer, len);
}
const char *
midi_error(midi_io_t *midi)
{
assert(midi != NULL);
return midi->backend.error(midi);
}
char **
midi_get_ports(void)
{
char **ports, name[32];
size_t n, max;
ports = NULL;
n = max = 0;
#ifdef HAVE_ALSA
alsa_midi_get_ports(&ports, &n, &max);
#endif
#ifdef HAVE_OSS
oss_midi_get_ports(&ports, &n, &max);
#endif
if ( n >= max ) {
max += 1;
ports = xrealloc(ports, max);
}
ports[n++] = NULL;
return ports;
}
int
midi_next(midi_io_t *midi)
{
unsigned char b;
assert(midi != NULL);
if ( midi->pos >= midi->len ) {
ssize_t n;
n = midi_read(midi, midi->buffer, MIDI_IO_BUFFER_SIZE);
if ( n < 0 )
return n;
midi->len = n;
midi->pos = 0;
}
b = (unsigned char) midi->buffer[midi->pos++];
if ( b >= 0x80 && b < 0xF8 ) /* Status byte? */
midi->status = b;
return b;
}
inline unsigned char
midi_status(midi_io_t *midi)
{
return midi->status;
}
int
midi_change_program(midi_io_t *midi,
unsigned char channel,
unsigned short bank,
unsigned char program)
{
unsigned char msg[] = { 0xB0, /* Control Change */
0x00, /* CC Bank Select MSB */
0x00, /* Bank number MSB */
0xB0, /* Control Change */
0x20, /* CC Bank Select LSB */
0x00, /* Bank number LSB */
0xC0, /* Program Change */
0x00 /* Program number */ };
assert(midi != NULL);
msg[0] |= (channel & 0x0F);
msg[3] |= (channel & 0x0F);
msg[6] |= (channel & 0x0F);
msg[2] |= ((bank >> 7) & 0x7F);
msg[5] |= (bank & 0x7F);
msg[7] |= (program & 0x7F);
return midi_write(midi, msg, sizeof(msg));
}
const char *midi_gm_patches[] = {
"Acoustic Grand Piano",
"Bright Acoustic Piano",
"Electric Grand Piano",
"Honky-tonk Piano",
"Electric Piano 1",
"Electric Piano 2",
"Harpsichord",
"Clavi",
"Celesta",
"Glockenspiel",
"Music Box",
"Vibraphone",
"Marimba",
"Xylophone",
"Tubular Bells",
"Dulcimer",
"Drawbar Organ",
"Percussive Organ",
"Rock Organ",
"Church Organ",
"Reed Organ",
"Accordion",
"Harmonica",
"Tango Accordion",
"Guitar (nylon)",
"Acoustic Guitar (steel)",
"Electric Guitar (jazz)",
"Electric Guitar (clean)",
"Electric Guitar (muted)",
"Overdriven Guitar",
"Distortion Guitar",
"Guitar harmonics",
"Acoustic Bass",
"Electric Bass (finger)",
"Electric Bass (pick)",
"Fretless Bass",
"Slap Bass 1",
"Slap Bass 2",
"Synth Bass 1",
"Synth Bass 2",
"Violin",
"Viola",
"Cello",
"Contrabass",
"Tremolo Strings",
"Pizzicato Strings",
"Orchestral Harp",
"Timpani",
"String Ensemble 1",
"String Ensemble 2",
"SynthStrings 1",
"SynthStrings 2",
"Choir Aahs",
"Voice Oohs",
"Synth Voice",
"Orchestra Hit",
"Trumpet",
"Trombone",
"Tuba",
"Muted Trumpet",
"French Horn",
"Brass Section",
"SynthBrass 1",
"SynthBrass 2",
"Soprano Sax",
"Alto Sax",
"Tenor Sax",
"Baritone Sax",
"Oboe",
"English Horn",
"Bassoon",
"Clarinet",
"Piccolo",
"Flute",
"Recorder",
"Pan Flute",
"Blown Bottle",
"Shakuhachi",
"Whistle",
"Ocarina",
"Lead 1 (square)",
"Lead 2 (sawtooth)",
"Lead 3 (calliope)",
"Lead 4 (chiff)",
"Lead 5 (charang)",
"Lead 6 (voice)",
"Lead 7 (fifths)",
"Lead 8 (bass+lead)",
"Pad 1 (new age)",
"Pad 2 (warm)",
"Pad 3 (polysynth)",
"Pad 4 (choir)",
"Pad 5 (bowed)",
"Pad 6 (metallic)",
"Pad 7 (halo)",
"Pad 8 (sweep)",
"FX 1 (rain)",
"FX 2 (soundtrack)",
"FX 3 (crystal)",
"FX 4 (atmosphere)",
"FX 5 (brightness)",
"FX 6 (goblins)",
"FX 7 (echoes)",
"FX 8 (sci-fi)",
"Sitar",
"Banjo",
"Shamisen",
"Koto",
"Kalimba",
"Bag pipe",
"Fiddle",
"Shanai",
"Tinkle Bell",
"Agogo",
"Steel Drums",
"Woodblock",
"Taiko drum",
"Melodic Tom",
"Synth Drum",
"Reverse Cymbal",
"Guitar Fret Noise",
"Breath Noise",
"Seashore",
"Bird Tweet",
"Telephone Ring",
"Helicopter",
"Applause",
"Gunshot"
};