Import existing source tree; original VCS history is no longer available. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
268 lines
6.2 KiB
C
268 lines
6.2 KiB
C
/*(LGPL)
|
|
---------------------------------------------------------------------------
|
|
a_midi.c - midi_socket_t MIDI Input Driver for OSS or ALSA
|
|
---------------------------------------------------------------------------
|
|
* Copyright (C) 2001, 2003, David Olofson
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it
|
|
* under the terms of the GNU Lesser General Public License as published by
|
|
* the Free Software Foundation; either version 2.1 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
|
|
* Lesser General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU Lesser General Public License
|
|
* along with this program; if not, write to the Free Software Foundation,
|
|
* Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
|
|
*/
|
|
|
|
/*
|
|
* NOTE: This code will build for ALSA *or* OSS, in that order of preference.
|
|
*/
|
|
|
|
#include "config.h"
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include "kobolog.h"
|
|
#include "a_midi.h"
|
|
|
|
#ifdef HAVE_ALSA
|
|
# include <sys/asoundlib.h>
|
|
# define USE_MIDI
|
|
#elif defined(HAVE_OSS)
|
|
# include <fcntl.h>
|
|
# include <unistd.h>
|
|
# define USE_MIDI
|
|
#else
|
|
# undef USE_MIDI
|
|
#endif
|
|
|
|
#define MDBG(x)
|
|
#define MDBG2(x)
|
|
|
|
|
|
#ifdef USE_MIDI
|
|
|
|
/* MIDI open/close */
|
|
#ifdef HAVE_ALSA
|
|
static snd_rawmidi_t *m_in_fd = NULL;
|
|
static snd_rawmidi_t *m_out_fd = NULL;
|
|
#elif defined(HAVE_OSS)
|
|
static int m_fd = 0;
|
|
#endif
|
|
|
|
|
|
/* MIDI I/O */
|
|
#ifdef HAVE_ALSA
|
|
# define midi_read(b,s) snd_rawmidi_read(m_in_fd, b, s)
|
|
# define midi_write(b,s) snd_rawmidi_write(m_out_fd, b, s)
|
|
#elif defined(HAVE_OSS)
|
|
# define midi_read(b,s) read(m_fd, b, s)
|
|
# define midi_write(b,s) write(m_fd, b, s)
|
|
#endif
|
|
|
|
|
|
/*
|
|
* Basic real time MIDI parsing
|
|
*/
|
|
|
|
static midisock_t *ms;
|
|
static midisock_t *mon;
|
|
|
|
/* Global state stuff - *not* per channel! */
|
|
static int m_channel = 0; /* current channel */
|
|
static int m_datacount = 0; /* current data byte */
|
|
static char m_status = 0; /* current (last seen) status */
|
|
static char m_buf[3]; /* space for data bytes */
|
|
static int m_expect = -1; /* expected message data length */
|
|
|
|
static inline void decode_midi_message(void)
|
|
{
|
|
/* Seems like we've got a complete message! */
|
|
int val;
|
|
switch(m_status & 0xf0)
|
|
{
|
|
case 0x80: /* Note off */
|
|
MDBG2(mon->note_off(m_channel, m_buf[0], m_buf[1]);)
|
|
ms->note_off(m_channel, m_buf[0], m_buf[1]);
|
|
break;
|
|
case 0x90: /* Note on */
|
|
MDBG2(mon->note_on(m_channel, m_buf[0], m_buf[1]);)
|
|
ms->note_on(m_channel, m_buf[0], m_buf[1]);
|
|
break;
|
|
case 0xa0: /* Poly pressure */
|
|
MDBG2(mon->poly_pressure(m_channel, m_buf[0], m_buf[1]);)
|
|
ms->poly_pressure(m_channel, m_buf[0], m_buf[1]);
|
|
break;
|
|
case 0xb0: /* Control change */
|
|
MDBG2(mon->control_change(m_channel, m_buf[0], m_buf[1]);)
|
|
ms->control_change(m_channel, m_buf[0], m_buf[1]);
|
|
break;
|
|
case 0xc0: /* Program change */
|
|
MDBG2(mon->program_change(m_channel, m_buf[0]);)
|
|
ms->program_change(m_channel, m_buf[0]);
|
|
break;
|
|
case 0xd0: /* Channel pressure */
|
|
MDBG2(mon->channel_pressure(m_channel, m_buf[0]);)
|
|
ms->channel_pressure(m_channel, m_buf[0]);
|
|
break;
|
|
case 0xe0: /* Pitch bend */
|
|
val = ((m_buf[1] << 7) | m_buf[0]) - 8192;
|
|
MDBG2(mon->pitch_bend(m_channel, val);)
|
|
ms->pitch_bend(m_channel, val);
|
|
break;
|
|
case 0xf0: /* System message */
|
|
break;
|
|
}
|
|
m_datacount = 0;
|
|
}
|
|
|
|
static const int full_xtab[8] = {
|
|
2, /* 0x80/0: Note off, */
|
|
2, /* 0x90/1: Note on */
|
|
2, /* 0xa0/2: Poly pressure */
|
|
2, /* 0xb0/3: Control change */
|
|
1, /* 0xc0/4: Program change */
|
|
1, /* 0xd0/5: Channel pressure */
|
|
2, /* 0xe0/6: Pitch bend */
|
|
-1 /* 0xf0/7: System message (unsupported) */
|
|
};
|
|
|
|
static int xtab[8];
|
|
|
|
static inline void decode_midi_byte(unsigned char b)
|
|
{
|
|
if(b & 0x80) /* New status! */
|
|
{
|
|
MDBG(log_printf(D3LOG, "\n%2.2X", b & 0xff));
|
|
m_status = b;
|
|
m_datacount = 0;
|
|
m_channel = b & 0x0f;
|
|
m_expect = xtab[(m_status >> 4) & 0x07];
|
|
return;
|
|
}
|
|
|
|
MDBG(log_printf(D3LOG, " %2.2X", b));
|
|
if(m_expect < 0)
|
|
return; /* Ignore --> */
|
|
|
|
m_buf[m_datacount++] = b;
|
|
|
|
if(m_datacount >= m_expect)
|
|
decode_midi_message();
|
|
}
|
|
|
|
#endif /*USE_MIDI*/
|
|
|
|
|
|
int midi_open(midisock_t *output)
|
|
{
|
|
#ifdef USE_MIDI
|
|
if(!output)
|
|
output = &dummy_midisock;
|
|
ms = output;
|
|
|
|
/*
|
|
* Set up the message length table to ignore messages
|
|
* that aren't supported by the output midisock.
|
|
*/
|
|
memset(xtab, -1, sizeof(xtab));
|
|
if(output->note_off)
|
|
xtab[0] = full_xtab[0];
|
|
if(output->note_on)
|
|
xtab[1] = full_xtab[1];
|
|
if(output->poly_pressure)
|
|
xtab[2] = full_xtab[2];
|
|
if(output->control_change)
|
|
xtab[3] = full_xtab[3];
|
|
if(output->program_change)
|
|
xtab[4] = full_xtab[4];
|
|
if(output->channel_pressure)
|
|
xtab[5] = full_xtab[5];
|
|
if(output->pitch_bend)
|
|
xtab[6] = full_xtab[6];
|
|
|
|
mon = &monitor_midisock;
|
|
#ifdef HAVE_ALSA
|
|
if(snd_rawmidi_open(&m_in_fd, 0, 0, SND_RAWMIDI_OPEN_INPUT |
|
|
SND_RAWMIDI_OPEN_NONBLOCK))
|
|
{
|
|
log_printf(ELOG, "Failed to open MIDI input device!\n");
|
|
return -1;
|
|
}
|
|
# ifdef MIDI_THRU
|
|
if(snd_rawmidi_open(&m_out_fd, 0, 0, SND_RAWMIDI_OPEN_OUTPUT))
|
|
{
|
|
log_printf(ELOG, "Failed to open MIDI output device!\n");
|
|
return -2;
|
|
}
|
|
# endif
|
|
return 0;
|
|
#elif defined(HAVE_OSS)
|
|
{
|
|
# ifdef MIDI_THRU
|
|
int flags = O_RDWR | O_NONBLOCK;
|
|
# else
|
|
int flags = O_RDONLY | O_NONBLOCK;
|
|
# endif
|
|
m_fd = open("/dev/midi00", flags);
|
|
if(m_fd < 0)
|
|
{
|
|
log_printf(ELOG, "Failed to open MIDI device!\n");
|
|
return -1;
|
|
}
|
|
return 0;
|
|
}
|
|
#endif /*HAVE_OSS*/
|
|
#else /*USE_MIDI*/
|
|
log_printf(ELOG, "MIDI support not compiled in!\n");
|
|
return -3;
|
|
#endif /*USE_MIDI*/
|
|
}
|
|
|
|
|
|
void midi_close(void)
|
|
{
|
|
#ifdef USE_MIDI
|
|
#ifdef HAVE_ALSA
|
|
if(m_in_fd)
|
|
{
|
|
snd_rawmidi_input_flush(m_in_fd);
|
|
snd_rawmidi_close(m_in_fd);
|
|
}
|
|
if(m_out_fd)
|
|
{
|
|
snd_rawmidi_output_flush(m_out_fd);
|
|
snd_rawmidi_close(m_out_fd);
|
|
}
|
|
m_in_fd = m_out_fd = NULL;
|
|
#elif defined(HAVE_OSS)
|
|
if(m_fd)
|
|
close(m_fd);
|
|
m_fd = 0;
|
|
#endif
|
|
#endif /*USE_MIDI*/
|
|
}
|
|
|
|
|
|
void midi_process(void)
|
|
{
|
|
#ifdef USE_MIDI
|
|
int bytes;
|
|
static char buf[MIDIMAXBYTES];
|
|
if( (bytes = midi_read(buf, MIDIMAXBYTES)) > 0 )
|
|
{
|
|
char *bufp = buf;
|
|
#ifdef MIDI_THRU
|
|
midi_write(buf, bytes); /* MIDI Thru */
|
|
#endif
|
|
while(bytes--)
|
|
decode_midi_byte(*bufp++);
|
|
}
|
|
#endif /*USE_MIDI*/
|
|
}
|