/* ALSA sequencer support for OAK */ #include #include #include #include "oak.h" static void panic (const char *s) { fprintf (stderr, "alsa: %s\n", s); exit (1); } static snd_seq_t *seq; static int port; /* PROC C.alsa.seq.init () */ void _alsa_seq_init (int *w) { snd_seq_port_info_t *pi; const snd_seq_addr_t *our_addr; snd_seq_addr_t midi_addr; snd_seq_port_subscribe_t *subs; int rc; rc = snd_seq_open (&seq, "default", SND_SEQ_OPEN_INPUT, 0); if (rc < 0) panic ("cannot open sequencer"); snd_seq_set_client_name (seq, "oak"); port = snd_seq_create_simple_port (seq, "MIDI in", SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE, SND_SEQ_PORT_TYPE_MIDI_GENERIC); if (snd_seq_port_info_malloc (&pi) < 0) panic ("cannot alloc port info"); if (snd_seq_get_port_info (seq, port, pi) < 0) panic ("cannot get port info"); our_addr = snd_seq_port_info_get_addr (pi); midi_addr.client = 64; midi_addr.port = 0; snd_seq_port_subscribe_alloca (&subs); snd_seq_port_subscribe_set_sender (subs, &midi_addr); snd_seq_port_subscribe_set_dest (subs, our_addr); snd_seq_subscribe_port (seq, subs); snd_seq_port_info_free (pi); } /* PROC B.alsa.seq.read.event (BOOL ok, INT type, [4]INT args) */ void _alsa_seq_read_event (int *w) { int *ok = (int *) w[0]; int *type = (int *) w[1]; int *args = (int *) w[2]; /* size of args is w[3] */ snd_seq_event_t *ev; int rc, i; *ok = 0; *type = -1; for (i = 0; i < 4; i++) args[i] = -1; rc = snd_seq_event_input (seq, &ev); if (rc < 0) panic ("event read failed"); if (rc > 0) { *ok = 1; /* I don't know why this is necessary -- with my setup I see * note off events as note on events with velocity 0. */ if (ev->type == SND_SEQ_EVENT_NOTEON && ev->data.note.velocity == 0) ev->type = SND_SEQ_EVENT_NOTEOFF; switch (ev->type) { case SND_SEQ_EVENT_NOTEON: *type = 1; args[0] = ev->data.note.channel; args[1] = ev->data.note.note; args[2] = ev->data.note.velocity; break; case SND_SEQ_EVENT_NOTEOFF: *type = 2; args[0] = ev->data.note.channel; args[1] = ev->data.note.note; break; case SND_SEQ_EVENT_PITCHBEND: *type = 3; args[0] = ev->data.control.channel; args[1] = ev->data.control.value; break; case SND_SEQ_EVENT_CONTROLLER: *type = 4; args[0] = ev->data.control.channel; args[1] = ev->data.control.param; args[2] = ev->data.control.value; break; default: break; } snd_seq_free_event (ev); } } static snd_pcm_t *pcm_handle; /* PROC C.alsa.pcm.init (VAL INT sample.rate) */ void _alsa_pcm_init (int *w) { unsigned int speed = w[0], rate; snd_pcm_hw_params_t *hwparams; if (snd_pcm_open (&pcm_handle, "plughw:0,0", SND_PCM_STREAM_PLAYBACK, 0) < 0) { panic ("cannot open pcm"); } snd_pcm_hw_params_alloca (&hwparams); if (snd_pcm_hw_params_any (pcm_handle, hwparams) < 0) { panic ("cannot get hw params"); } if (snd_pcm_hw_params_set_access (pcm_handle, hwparams, SND_PCM_ACCESS_RW_NONINTERLEAVED) < 0) { panic ("cannot set access"); } if (snd_pcm_hw_params_set_format (pcm_handle, hwparams, SND_PCM_FORMAT_FLOAT) < 0) { panic ("cannot set format"); } rate = speed; if (snd_pcm_hw_params_set_rate_near (pcm_handle, hwparams, &rate, 0) < 0 || rate != speed) { panic ("cannot set rate"); } if (snd_pcm_hw_params_set_channels (pcm_handle, hwparams, 2) < 0) { panic ("cannot set channels"); } if (snd_pcm_hw_params_set_buffer_size (pcm_handle, hwparams, OAK_BLOCK_SIZE * 16) < 0) { panic ("cannot set buffer size"); } if (snd_pcm_hw_params (pcm_handle, hwparams) < 0) { panic ("cannot set hw params"); } } /* PROC B.alsa.pcm.play (VAL []REAL32 left, right) */ void _alsa_pcm_play (int *w) { float *ldata = (float *) w[0]; float *rdata = (float *) w[2]; void *bufs[2]; bufs[0] = ldata; bufs[1] = rdata; if (snd_pcm_writen(pcm_handle, bufs, OAK_BLOCK_SIZE) < 0) { fprintf (stderr, "alsa pcm buffer underrun\n"); snd_pcm_prepare(pcm_handle); } }