[BACK]Return to midi.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / aucat

File: [local] / src / usr.bin / aucat / Attic / midi.c (download)

Revision 1.2, Wed Aug 19 05:54:15 2009 UTC (14 years, 9 months ago) by ratchov
Branch: MAIN
Changes since 1.1: +1 -3 lines

organize midi code like audio code, no functional change

/*	$OpenBSD: midi.c,v 1.2 2009/08/19 05:54:15 ratchov Exp $	*/
/*
 * Copyright (c) 2008 Alexandre Ratchov <alex@caoua.org>
 *
 * Permission to use, copy, modify, and distribute this software for any
 * purpose with or without fee is hereby granted, provided that the above
 * copyright notice and this permission notice appear in all copies.
 *
 * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
 * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
 * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
 * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
 * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
 * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
 * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 */
/*
 * TODO
 *
 * use abuf->duplex to implement bidirectionnal sockets
 * that don't receive what they send
 *
 * use shadow variables in the midi merger
 *
 * make output and input identical when only one
 * input is used (fix running status)
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "conf.h"
#include "abuf.h"
#include "aproc.h"
#include "midi.h"

/*
 * input data rate is XFER / TIMO (in bytes per microsecond),
 * it must be slightly larger than the MIDI standard 3125 bytes/s
 */ 
#define MIDITHRU_XFER 340
#define MIDITHRU_TIMO 100000

unsigned voice_len[] = { 3, 3, 3, 3, 2, 2, 3 };
unsigned common_len[] = { 0, 2, 3, 2, 0, 0, 1, 1 };

void
thru_flush(struct aproc *p, struct abuf *ibuf, struct abuf *obuf)
{
	unsigned ocount, itodo;
	unsigned char *odata, *idata;

	itodo = ibuf->mused;
	idata = ibuf->mdata;
	DPRINTFN(4, "thru_flush: mused = %u\n", itodo);
	while (itodo > 0) {
		if (!ABUF_WOK(obuf)) {
			abuf_rdiscard(obuf, obuf->used);
			DPRINTFN(2, "thru_flush: discarded %u\n", obuf->used);
			if (p->u.thru.owner == ibuf)
				p->u.thru.owner = NULL;
			return;
		}
		odata = abuf_wgetblk(obuf, &ocount, 0);
		if (ocount > itodo)
			ocount = itodo;
		memcpy(odata, idata, ocount);
		abuf_wcommit(obuf, ocount);
		itodo -= ocount;
		idata += ocount;
	}
	ibuf->mused = 0;
	p->u.thru.owner = ibuf;
}

void
thru_rt(struct aproc *p, struct abuf *ibuf, struct abuf *obuf, unsigned c)
{
	unsigned ocount;
	unsigned char *odata;

	DPRINTFN(4, "thru_rt:\n");
	if (!ABUF_WOK(obuf)) {
		DPRINTFN(2, "thru_rt: discarded %u\n", obuf->used);
		abuf_rdiscard(obuf, obuf->used);
		if (p->u.thru.owner == ibuf)
			p->u.thru.owner = NULL;
	}
	odata = abuf_wgetblk(obuf, &ocount, 0);
	odata[0] = c;
	abuf_wcommit(obuf, 1);
}


void
thru_bcopy(struct aproc *p, struct abuf *ibuf, struct abuf *obuf, unsigned todo)
{
	unsigned char *idata;
	unsigned c, icount, ioffs;

	idata = NULL;
	icount = ioffs = 0;
	for (;;) {
		if (icount == 0) {
			if (todo == 0)
				break;
			idata = abuf_rgetblk(ibuf, &icount, ioffs);
			if (icount > todo)
				icount = todo;
			if (icount == 0)
				break;
			todo -= icount;
			ioffs += icount;
		}
		c = *idata++;
		icount--;
		if (c < 0x80) {
			if (ibuf->mindex == 0 && ibuf->mstatus) {
				ibuf->mdata[ibuf->mused++] = ibuf->mstatus;
				ibuf->mindex++;
			}
			ibuf->mdata[ibuf->mused++] = c;
			ibuf->mindex++;
			if (ibuf->mindex == ibuf->mlen) {
				thru_flush(p, ibuf, obuf);
				if (ibuf->mstatus >= 0xf0)
					ibuf->mstatus = 0;
				ibuf->mindex = 0;
			}
			if (ibuf->mused == MDATA_NMAX) {
				if (ibuf->mused == ibuf->mindex ||
				    p->u.thru.owner == ibuf)
					thru_flush(p, ibuf, obuf);
				else
					ibuf->mused = 0;
			}
		} else if (c < 0xf8) {
			if (ibuf->mused == ibuf->mindex ||
			    p->u.thru.owner == ibuf) {
				thru_flush(p, ibuf, obuf);
			} else
				ibuf->mused = 0;
			ibuf->mdata[0] = c;
			ibuf->mused = 1;
			ibuf->mlen = (c >= 0xf0) ? 
			    common_len[c & 7] :
			    voice_len[(c >> 4) & 7];
			if (ibuf->mlen == 1) {
				thru_flush(p, ibuf, obuf);
				ibuf->mindex = 0;
				ibuf->mstatus = 0;
				ibuf->mlen = 0;
			} else { 
				ibuf->mstatus = c;
				ibuf->mindex = 1;
			}
		} else {
			thru_rt(p, ibuf, obuf, c);
		}
	}
}

int
thru_in(struct aproc *p, struct abuf *ibuf)
{
	struct abuf *i, *inext;
	unsigned todo;

	DPRINTFN(3, "thru_in: %s\n", p->name);

	if (!ABUF_ROK(ibuf))
		return 0;
	if (ibuf->mtickets == 0) {
		DPRINTFN(2, "thru_in: out of tickets\n");
		return 0;
	}
	todo = ibuf->used;
	if (todo > ibuf->mtickets)
		todo = ibuf->mtickets;
	ibuf->mtickets -= todo;
	for (i = LIST_FIRST(&p->obuflist); i != NULL; i = inext) {
		inext = LIST_NEXT(i, oent);
		if (ibuf->duplex == i)
			continue;
		thru_bcopy(p, ibuf, i, todo);
		(void)abuf_flush(i);
	}
	abuf_rdiscard(ibuf, todo);
	return 1;
}

int
thru_out(struct aproc *p, struct abuf *obuf)
{
	return 0;
}

void
thru_eof(struct aproc *p, struct abuf *ibuf)
{
	DPRINTF("thru_eof: %s: eof\n", p->name);
}

void
thru_hup(struct aproc *p, struct abuf *obuf)
{
	DPRINTF("thru_hup: %s: detached\n", p->name);
}

void
thru_newin(struct aproc *p, struct abuf *ibuf)
{
	ibuf->mused = 0;
	ibuf->mlen = 0;
	ibuf->mindex = 0;
	ibuf->mstatus = 0;
	ibuf->mtickets = MIDITHRU_XFER;
}

void
thru_done(struct aproc *p)
{
	timo_del(&p->u.thru.timo);
}

struct aproc_ops thru_ops = {
	"thru",
	thru_in,
	thru_out,
	thru_eof,
	thru_hup,
	thru_newin,
	NULL, /* newout */
	NULL, /* ipos */
	NULL, /* opos */
	thru_done
};

void
thru_cb(void *addr)
{
	struct aproc *p = (struct aproc *)addr;
	struct abuf *i, *inext;
	unsigned tickets;

	timo_add(&p->u.thru.timo, MIDITHRU_TIMO);
	
	for (i = LIST_FIRST(&p->ibuflist); i != NULL; i = inext) {
		inext = LIST_NEXT(i, ient);
		tickets = i->mtickets;
		i->mtickets = MIDITHRU_XFER;
		if (tickets == 0)
			abuf_run(i);
	}
}

struct aproc *
thru_new(char *name)
{
	struct aproc *p;

	p = aproc_new(&thru_ops, name);
	p->u.thru.owner = NULL;
	timo_set(&p->u.thru.timo, thru_cb, p);
	timo_add(&p->u.thru.timo, MIDITHRU_TIMO);
	return p;
}