#define _DEFAULT_SOURCE #include #include #include #include #include #include #include #include #include #include "tune.h" #include "ccdl.h" #define max(a,b) ((a) > (b) ? (a) : (b)) #define min(a,b) ((a) < (b) ? (a) : (b)) static unsigned int rate = 48000; static unsigned int channels_out = 1; static unsigned int channels_in = 1; static unsigned int latency = 10000; // us static int resample = 1; // allow resampling static int16_t maxval = 0x7fff; static snd_pcm_format_t format = SND_PCM_FORMAT_S16; int main(int argc, char *argv[]) { struct ccdl ccdl; void *tune_obj = NULL; int err; snd_pcm_t *pcm_out = NULL, *pcm_in = NULL; snd_rawmidi_t *midi_in = NULL; const char *src_fname = "tune.c"; const char *device_out = "default"; const char *device_in = "default"; const char *midi_device_in = "default"; if (argc > 1) src_fname = argv[1]; if (argc > 2) device_out = argv[2]; if (argc > 3) device_in = argv[3]; if (argc > 4) midi_device_in = argv[4]; if (device_in[0] == '\0') device_in = NULL; if (midi_device_in[0] == '\0') midi_device_in = NULL; ccdl_init(&ccdl, src_fname, "play"); if (ccdl_watch(&ccdl)) { errx(1, "ccdl_watch"); } err = snd_pcm_open(&pcm_out, device_out, SND_PCM_STREAM_PLAYBACK, 0); if (err < 0) { warnx("Playback open error: %s", snd_strerror(err)); return 1; } if (device_in) { err = snd_pcm_open(&pcm_in, device_in, SND_PCM_STREAM_CAPTURE, SND_PCM_NONBLOCK); if (err < 0) { warnx("Capture open error: %s", snd_strerror(err)); return 1; } } if (midi_device_in) { err = snd_rawmidi_open(&midi_in, NULL, midi_device_in, SND_RAWMIDI_NONBLOCK); if (err < 0) { warnx("Midi open error: %s", snd_strerror(err)); } } if ((err = snd_pcm_set_params(pcm_out, format, SND_PCM_ACCESS_RW_INTERLEAVED, channels_out, rate, resample, latency)) < 0) { warnx("Playback params error: %s", snd_strerror(err)); return 1; } if (pcm_in) { if ((err = snd_pcm_set_params(pcm_in, format, SND_PCM_ACCESS_RW_INTERLEAVED, channels_in, rate, resample, latency)) < 0) { warnx("Capture params error: %s", snd_strerror(err)); return 1; } } snd_pcm_uframes_t buffer_size, buffer_size_in; snd_pcm_uframes_t period_size, period_size_in; err = snd_pcm_get_params(pcm_out, &buffer_size, &period_size); if (err < 0) { warnx("Playback get params error: %s", snd_strerror(err)); return 1; } if (pcm_in) { err = snd_pcm_get_params(pcm_in, &buffer_size_in, &period_size_in); if (err < 0) { warnx("Capture get params error: %s", snd_strerror(err)); return 1; } period_size = min(period_size_in, period_size); buffer_size = min(buffer_size_in, buffer_size); } (void)buffer_size; // printf("a %zu\n", (size_t)period_size); double time = 0; double step = 1/(double)rate; int midi_reopen_timer = 0; while (1) { snd_pcm_sframes_t frames, frames_in = 0, frames_out; // frames = snd_pcm_avail_update(pcm_out); frames = period_size; if (frames < 0) { err = snd_pcm_recover(pcm_out, frames, 0); if (err) err = snd_pcm_wait(pcm_out, 1000); if (err) warnx("snd_pcm_wait(%s)", snd_strerror(err)); continue; } int16_t buffer[frames]; if (pcm_in) { if (snd_pcm_state(pcm_in) == SND_PCM_STATE_PREPARED) { if ((err = snd_pcm_start(pcm_in)) < 0) { warnx("snd_pcm_start: %s", snd_strerror(err)); } } frames_in = snd_pcm_readi(pcm_in, buffer, frames); if (frames_in < 0) { err = snd_pcm_recover(pcm_in, frames_in, 0); if (err == -EAGAIN) { } else if (err < 0) { warnx("snd_pcm_readi: %s\n", snd_strerror(err)); } } } if (midi_reopen_timer > 0) { if (--midi_reopen_timer == 0) { err = snd_rawmidi_open(&midi_in, NULL, midi_device_in, SND_RAWMIDI_NONBLOCK); if (err < 0) { warnx("midi device re-open failed: %s", snd_strerror(err)); midi_reopen_timer = 5; } } } unsigned char midi_buf[frames], *midi_bufp = NULL; size_t midi_bytes = 0; if (midi_in) { ssize_t bytes = snd_rawmidi_read(midi_in, midi_buf, frames); if (bytes > 0) { midi_bufp = midi_buf; midi_bytes = bytes; } else if (bytes == -EAGAIN) { } else if (bytes == -ENODEV) { // lost device. queue reopening it midi_in = NULL; midi_reopen_timer = 5; } else if (bytes < 0) { warnx("snd_rawmidi_read: %s (%zd)", snd_strerror(bytes), bytes); } } play_fn *play; *(void **)(&play) = ccdl_get(&ccdl); if (play) { for (size_t i = 0; i < (size_t)frames; i++) { time += step; int16_t frame_in = (ssize_t)i < frames_in ? buffer[i] : 0; float in = (float)frame_in / maxval; float val = play(&tune_obj, time, in, midi_bufp); if (midi_bufp) { if (midi_bytes <= 3) { midi_bufp = NULL; } else { midi_bufp += 3; midi_bytes -= 3; } } buffer[i] = val * maxval; } } else { time += step * frames; } frames_out = snd_pcm_writei(pcm_out, buffer, frames); if (frames_out < 0) snd_pcm_recover(pcm_out, frames_out, 0); } snd_rawmidi_close(midi_in); snd_pcm_close(pcm_out); snd_pcm_close(pcm_in); return ccdl_deinit(&ccdl); }