kobodl/sound/a_midi.c
Ville Lindholm dbc223eb84
Initial commit
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>
2026-05-28 16:35:31 +03:00

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*/
}