Skip to content

Instantly share code, notes, and snippets.

@Simon-L

Simon-L/main.c Secret

Created September 30, 2016 13:29
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Simon-L/4d368841189a0507dd519693c4786d29 to your computer and use it in GitHub Desktop.
Save Simon-L/4d368841189a0507dd519693c4786d29 to your computer and use it in GitHub Desktop.
Thread for MIDI on ARM.
#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(&params);
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;
}
// 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