-
-
Save Simon-L/4d368841189a0507dd519693c4786d29 to your computer and use it in GitHub Desktop.
Thread for MIDI on ARM.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <stdio.h> | |
#include <unistd.h> | |
#include <signal.h> | |
#include "wrapper/heavy.h" | |
#include "wrapper/osc.h" | |
#include "wrapper/midi.h" | |
#include <alsa/asoundlib.h> | |
// #define PCM_DEVICE "default" | |
#define PCM_DEVICE "hw:PCH,0" | |
#define BLOCKSIZE 128 | |
void printHook(double timestampMs, const char *printLabel, | |
const char *msgString, void *userData) { | |
printf("[@ %.3fms] %s: %s\n", timestampMs, printLabel, msgString); | |
} | |
void intHandler(int dummy) { | |
printf("Exiting.\n"); | |
stop_midi(); | |
usleep(100000); | |
stop_osc(); | |
exit(0); | |
} | |
int main(int argc, const char *argv[]) { | |
signal(SIGINT, intHandler); | |
// #### Heavy begin | |
double sampleRate = 44100.0; | |
Hv_mfx_ctx *context = hv_mfx_new_with_options(sampleRate, 40, 20); | |
hv_setPrintHook(context, &printHook); | |
int numOutputChannels = hv_getNumOutputChannels(context); | |
int numIterations = 100; | |
int blockSize = BLOCKSIZE; // ideally a multiple of 8 | |
short *outBuffers = (short *) hv_malloc(numOutputChannels * blockSize * sizeof(short)); | |
// #### Heavy end | |
init_osc(context); | |
init_midi(context); | |
// #### Alsa begin | |
unsigned int pcm, tmp, dir; | |
int rate, channels, seconds; | |
snd_pcm_t *pcm_handle; | |
snd_pcm_hw_params_t *params; | |
snd_pcm_uframes_t frames; | |
char *buff; | |
int buff_size, loops; | |
if (argc < 4) { | |
printf("Usage: %s <sample_rate> <channels> <seconds>\n", | |
argv[0]); | |
return -1; | |
} | |
rate = atoi(argv[1]); | |
channels = atoi(argv[2]); | |
seconds = atoi(argv[3]); | |
/* Open the PCM device in playback mode */ | |
if (pcm = snd_pcm_open(&pcm_handle, PCM_DEVICE, | |
SND_PCM_STREAM_PLAYBACK, 0) < 0) | |
printf("ERROR: Can't open \"%s\" PCM device. %s\n", | |
PCM_DEVICE, snd_strerror(pcm)); | |
/* Allocate parameters object and fill it with default values*/ | |
snd_pcm_hw_params_alloca(¶ms); | |
snd_pcm_hw_params_any(pcm_handle, params); | |
/* Set parameters */ | |
if (pcm = snd_pcm_hw_params_set_access(pcm_handle, params, | |
SND_PCM_ACCESS_RW_INTERLEAVED) < 0) | |
printf("ERROR: Can't set interleaved mode. %s\n", snd_strerror(pcm)); | |
if (pcm = snd_pcm_hw_params_set_period_size(pcm_handle, params, BLOCKSIZE, 0) < 0) | |
printf("ERROR: Can't set period size. %s\n", snd_strerror(pcm)); | |
// caca prout | |
if (pcm = snd_pcm_hw_params_set_format(pcm_handle, params, | |
SND_PCM_FORMAT_S16_LE) < 0) | |
printf("ERROR: Can't set format. %s\n", snd_strerror(pcm)); | |
if (pcm = snd_pcm_hw_params_set_channels(pcm_handle, params, channels) < 0) | |
printf("ERROR: Can't set channels number. %s\n", snd_strerror(pcm)); | |
if (pcm = snd_pcm_hw_params_set_rate_near(pcm_handle, params, &rate, 0) < 0) | |
printf("ERROR: Can't set rate. %s\n", snd_strerror(pcm)); | |
/* Write parameters */ | |
if (pcm = snd_pcm_hw_params(pcm_handle, params) < 0) | |
printf("ERROR: Can't set harware parameters. %s\n", snd_strerror(pcm)); | |
/* Resume information */ | |
printf("PCM name: '%s'\n", snd_pcm_name(pcm_handle)); | |
printf("PCM state: %s\n", snd_pcm_state_name(snd_pcm_state(pcm_handle))); | |
snd_pcm_hw_params_get_channels(params, &tmp); | |
printf("channels: %i ", tmp); | |
if (tmp == 1) | |
printf("(mono)\n"); | |
else if (tmp == 2) | |
printf("(stereo)\n"); | |
snd_pcm_hw_params_get_rate(params, &tmp, 0); | |
printf("rate: %d bps\n", tmp); | |
printf("seconds: %d\n", seconds); | |
snd_pcm_hw_params_get_period_size(params, &frames, 0); | |
printf("Period size: %u\n", frames); | |
/* Allocate buffer to hold single period */ | |
buff_size = frames * channels * 2 /* 2 -> sample size */; | |
buff = (char *) malloc(buff_size); | |
snd_pcm_hw_params_get_period_time(params, &tmp, NULL); | |
printf("Period time: %uµs\n", tmp); | |
for (loops = (seconds * 1000000) / tmp; loops > 0; loops--) { | |
// printf("%u\n", loops); | |
// if (loops == 2500) hv_sendFloatToReceiver(context, HV_ALSA_HEAVY1_PARAM_FREQ, 880.0f); | |
hv_mfx_process_interleaved_short(context, NULL, outBuffers, blockSize); | |
// printf("%d\n", outBuffers[12]); | |
// printf("%d\n", loops); | |
if (pcm = snd_pcm_writei(pcm_handle, outBuffers, frames) == -EPIPE) { | |
printf("XRUN.\n"); | |
snd_pcm_prepare(pcm_handle); | |
} else if (pcm < 0) { | |
printf("ERROR. Can't write to PCM device. %s\n", snd_strerror(pcm)); | |
} | |
} | |
// stop_midi(); | |
usleep(100000); | |
stop_osc(); | |
hv_mfx_free(context); | |
snd_pcm_drain(pcm_handle); | |
snd_pcm_close(pcm_handle); | |
free(buff); | |
return 0; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// Copyright Simon Larcher 2016 | |
#include "midi.h" | |
#include "heavy.h" | |
#include <alsa/asoundlib.h> | |
#include <pthread.h> | |
// Largely taken from http://alsamodular.sourceforge.net/seqdemo.c for the MIDI part | |
// Copyright Matthias Nagorni ???? | |
// and from https://computing.llnl.gov/tutorials/pthreads/#PassingArguments for the threading part | |
// Copyright blaiseb@llnl.gov 2016 | |
// Thread data struct | |
struct midi_thread_data { | |
snd_seq_t *a_seq_handle; | |
int a_npfd; | |
struct pollfd *a_pfd; | |
Hv_mfx_ctx *hv_ctx; | |
}; | |
struct midi_thread_data data; | |
pthread_t midi_thread; | |
// Create a client and open default ALSA MIDI sequencer | |
snd_seq_t *open_seq() { | |
snd_seq_t *seq_handle; | |
int portid; | |
if (snd_seq_open(&seq_handle, "default", SND_SEQ_OPEN_INPUT, 0) < 0) { | |
fprintf(stderr, "Error opening ALSA sequencer.\n"); | |
exit(1); | |
} | |
// Cool name heh?! | |
snd_seq_set_client_name(seq_handle, "Marteau FX"); | |
if ((portid = snd_seq_create_simple_port(seq_handle, "Marteau FX", | |
SND_SEQ_PORT_CAP_WRITE|SND_SEQ_PORT_CAP_SUBS_WRITE, | |
SND_SEQ_PORT_TYPE_APPLICATION)) < 0) { | |
fprintf(stderr, "Error creating sequencer port.\n"); | |
exit(1); | |
} | |
return(seq_handle); | |
} | |
// Midi event handler | |
void midi_action(snd_seq_t *seq_handle, Hv_mfx_ctx *context) { | |
snd_seq_event_t *ev; | |
do { | |
snd_seq_event_input(seq_handle, &ev); | |
// The MFX_foo used in the if()'s are defined in the heavy.h file | |
// One for each of [notein], [ctlin], [bendin] and [polytouchin] when used, false when unused | |
// The messages are dispatched to the matching [receive] object inside the corresponding mfx_abstractions patch. | |
switch (ev->type) { | |
case SND_SEQ_EVENT_CONTROLLER: | |
if (MFX_CTLIN) { | |
printf("Control event on Channel %2d: %5d on CC %5d\n", ev->data.control.channel, ev->data.control.value, ev->data.control.param); | |
hv_vscheduleMessageForReceiver(context, HV_PARAM_MFX_CTLIN, 0.0, "fff", (float)ev->data.control.value, (float)ev->data.control.param, (float)ev->data.control.channel); | |
break; | |
} | |
break; | |
case SND_SEQ_EVENT_PITCHBEND: | |
printf("Pitchbender event on Channel %2d: %5d\n", ev->data.control.channel, ev->data.control.value); | |
break; | |
case SND_SEQ_EVENT_NOTEON: | |
if (MFX_NOTEIN){ | |
printf("Note On event on Channel %2d: %5d with velocity %5d\n", ev->data.control.channel, ev->data.note.note, ev->data.note.velocity); | |
hv_vscheduleMessageForReceiver(context, HV_PARAM_MFX_NOTEIN, 0.0, "fff", (float)ev->data.note.note, (float)ev->data.note.velocity, (float)ev->data.control.channel); | |
break; | |
} | |
break; | |
case SND_SEQ_EVENT_NOTEOFF: | |
if (MFX_NOTEIN){ | |
printf("Note Off event on Channel %2d: %5d with velocity %5d\n", ev->data.control.channel, ev->data.note.note, ev->data.note.velocity); | |
hv_vscheduleMessageForReceiver(context, HV_PARAM_MFX_NOTEIN, 0.0, "fff", (float)ev->data.note.note, (float)ev->data.note.velocity, (float)ev->data.control.channel); | |
break; | |
} | |
break; | |
case SND_SEQ_EVENT_KEYPRESS: | |
printf("Key Pressure event on Channel %2d: Note %5d with pressure %5d\n", ev->data.control.channel, ev->data.note.note, ev->data.note.velocity); | |
break; | |
} | |
} while (snd_seq_event_input_pending(seq_handle, 0) > 0); | |
} | |
// Thread loop | |
void *midiLoop(void *data){ | |
// Cast from data to thread struct | |
struct midi_thread_data *_data; | |
_data = (struct midi_thread_data *)data; | |
// While (1) loop to keep polling and handle messages in the above function | |
while (1) { | |
if (poll(_data->a_pfd, _data->a_npfd, 1000) > 0) { | |
midi_action(_data->a_seq_handle, _data->hv_ctx); | |
} | |
} | |
pthread_exit(NULL); | |
} | |
snd_seq_t *seq_handle; | |
void init_midi(Hv_mfx_ctx *context) { | |
int npfd; | |
struct pollfd *pfd; | |
int rc; | |
// Create a client and file descriptors for polling | |
seq_handle = open_seq(); | |
npfd = snd_seq_poll_descriptors_count(seq_handle, POLLIN); | |
pfd = (struct pollfd *)alloca(npfd * sizeof(struct pollfd)); | |
snd_seq_poll_descriptors(seq_handle, pfd, npfd, POLLIN); | |
// Drop all previous events, we don't want to process old data. | |
uint8_t tmp; | |
tmp = snd_seq_drop_input(seq_handle); | |
printf("Dropped input: %d\n", tmp); | |
tmp = snd_seq_drop_input_buffer(seq_handle); | |
printf("Dropped input buffer: %d\n", tmp); | |
// Prepare thread data | |
data.a_seq_handle = seq_handle; | |
data.a_npfd = npfd; | |
data.a_pfd = pfd; | |
data.hv_ctx = context; // Must include pointer to Heavy context | |
// Create thread with thread data | |
rc = pthread_create(&midi_thread, NULL, midiLoop, (void *)&data); | |
printf("Rc is: %d\n", rc); | |
if (rc){ | |
printf("ERROR; return code from pthread_create() is %d\n", rc); | |
exit(-1); | |
} | |
} | |
// Called to close MIDI thread | |
void stop_midi(){ | |
printf("Exiting midi thread.\n"); | |
pthread_cancel(midi_thread); | |
// Drop messages and buffer, then close Alsa sequencer | |
uint8_t tmp; | |
tmp = snd_seq_drop_input(seq_handle); | |
printf("Dropped input: %d\n", tmp); | |
tmp = snd_seq_drop_input_buffer(seq_handle); | |
printf("Dropped input buffer: %d\n", tmp); | |
tmp = snd_seq_close(seq_handle); | |
printf("Closed: %d\n", tmp); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment