summaryrefslogblamecommitdiffstats
path: root/src/midi.c
blob: 8597100695c32d88c0531ac880763880ae1dba9f (plain) (tree)
1
2
3
4
5
6
7
8
                 
                  
                    
 

                   

                   





































































































                                                                              
                                                 
                                        







                                                            
                                                                                    
                                                  

              
                                                                                   
                                                 




                                                                                      


                                                                              







                                                                            

                                                                                           






                                       
               

                                                     
                  


                                  
                      
 
                                    

              





                                      

                                
                                     
 





                                    
                   
 



                                                            
 
                                                 
                           
          

                                           


                       


                                                                                                                                                                            

   

                                                                                                                                                                                 

                               








                                                               
             

 
         




















                                           

                                                  
                                     





                                           



                              
 


             


                         
            
                      

                 
#include "midi.h"
#include "notes.h"
#include "control.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

void midi_pitch_bend(synth_t *synth, int channel, int value) {
  (void)channel;

  float pitch;
  float semitones = 2;
  if (value == 0) {
    pitch = 1;
  } else if (value > 0) {
    pitch = 1 + ((float)value / 8191) * semitones * pow(2.0, 1.0 / 12.0) / 12;
  } else {
    pitch = 1 + ((float)value / 8192) * semitones * pow(2.0, 1.0 / 12.0) / 12;
  }
  synth->cc_pitch.target = pitch;
  /* cc_step(synth->cc_pitch, 1); */
}

void midi_cc(synth_t *synth, int channel, int controller, int value) {
  (void)channel;

  int x = value < 64 ? 1 : -1;
  switch (controller) {
  case 0:
    cc_step(&synth->cc_adsr_a, x);
    //synth->adsr.a = synth->adsr.a + (x * 0.01);
    //synth->freq_offset = synth->freq_offset + (x*5);
    //cc_step(&synth->cc_cutoff, x);
    break;
  case 1:
    cc_step(&synth->cc_adsr_d, x);
    //synth->adsr.d = synth->adsr.d + (x * 0.01);
    break;
  case 2:
    cc_step(&synth->cc_adsr_s, x);
    //synth->adsr.s = synth->adsr.s + (x * 0.01);
    break;
  case 3:
    cc_step(&synth->cc_adsr_r, x);
    //synth->adsr.r = synth->adsr.r + (x * 0.01);
    break;
  case 4:
    cc_step(&synth->cc_cutoff, x);
    //synth->cutoff = synth->cutoff + (x * (10 * log10(synth->cutoff)));
    break;
  case 5:
    cc_step(&synth->cc_resonance, x);
    break;
  case 6:
    //cc_step(&synth->cc_pitch, x);
    cc_step(&synth->cc_lfo_freq, x);
    //synth->lfo.freq = synth->lfo.freq + (x * 0.1);
    break;
  case 7:
    //cc_reset(&synth->cc_pitch);
    cc_step(&synth->cc_lfo_amp, x);
    //synth->lfo.amp = synth->lfo.amp + (x * 0.002);
    break;
  case 8:
    //cc_reset(&synth->cc_pitch);
    synth->wvt_pos = value;
    //synth->lfo.amp = synth->lfo.amp + (x * 0.002);
    break;
  case 28:
    if (synth->geni > 0) synth->geni--;
    break;
  case 29:
    if (synth->geni < 6) synth->geni++;
    break;
  default:
    break;
  }
}

void midi_note_off(synth_t *synth, int channel, int note, int velocity) {
  (void)channel;
  (void)velocity;

  synth->midi_note[note].noteOff = Pa_GetStreamTime(synth->stream);
  synth->midi_note[note].noteOffSample = synth->midi_note[note].elapsed;
}

void midi_note_on(synth_t *synth, int channel, int note, int velocity) {
  int flag = 1;

  synth->midi_note[note].freq = notes[note % 12][(note / 12) % 8];
  synth->midi_note[note].channel = channel;
  synth->midi_note[note].noteOn = Pa_GetStreamTime(synth->stream);
  synth->midi_note[note].noteOff = 0;
  synth->midi_note[note].velocity = (float)velocity / 127.0;
  synth->midi_note[note].elapsed = 0;
  synth->midi_note[note].active = 1;
  for (int i = 0; i < synth->midi_active_n; i++) {
    if (synth->midi_active[i] == &synth->midi_note[note]) {
      flag = 0;
    }
  }
  if (flag) {
    synth->midi_active[synth->midi_active_n++] = &synth->midi_note[note];
  }
  synth->active = 1;

}

void midi_decode(uint32_t msg, synth_t * synth) {
  //printf("MIDI message: 0x%X\n", msg);
  uint8_t status = msg;
  uint8_t data1 = (msg >> 8) & 0xFF;
  uint8_t data2 = (msg >> 16) & 0xFF;
  uint8_t channel = (status & 0x0F) + 1; // convert to human
  uint8_t message = status >> 4;

  switch (message) {
    case 0x08:
      printf("Note Off: channel=%d, note=%d, velocity=%d\n", channel, data1, data2);
      midi_note_off(synth, channel, data1, data2);
      break;
    case 0x09:
      printf("Note On: channel=%d, note=%d, velocity=%d\n", channel, data1, data2);
      midi_note_on(synth, channel, data1, data2);
      break;
    case 0x0A:
      printf("Aftertouch: channel=%d, note=%d, pressure=%d\n", channel, data1, data2);
      break;
    case 0x0B:
      printf("Control Change: channel=%d, controller=%d, value=%d\n", channel,
             data1, data2);
      midi_cc(synth, channel, data1, data2);
      break;
    case 0x0C:
      printf("Program Change: channel=%d, program=%d\n", channel, data1);
      break;
    case 0x0D:
      printf("Channel Pressure: channel=%d, pressure=%d\n", channel, data1);
      break;
    case 0x0E:
      printf("Pitch Bend: channel=%d, value=%d\n", channel, ((data2 << 7) | data1) - 8192);
      midi_pitch_bend(synth, channel, ((data2 << 7) | data1) - 8192);
      break;
    default:
      printf("Unknown MIDI message\n");
      break;
  }
}

int enable = 0;
void
midiCallback(PtTimestamp timestamp, void *userData) {
  (void)timestamp;
  midi_t * m = (midi_t *)userData;

  if (!m->stream) return;
  if (!enable) return;

  //if (!Pm_Poll(m->stream)) return;

  PmEvent buf;
  int e = Pm_Read(m->stream, &buf, 1);
  if (e == 0) return; // nothing to do
  if (e < 0) {
    printf("Pm_Read error %d\n", e);
    return;
  }

  //printf("%d\n", buf.message);
  midi_decode(buf.message, m->synth);

}

void
init_midi(midi_t *m, synth_t *synth)
{
  m->stream = NULL;
  m->synth = synth;

  PmError err = Pm_Initialize();
  if (err != pmNoError) {
    fprintf(stdout, "Pm Error: %s\n", Pm_GetErrorText(err));
  }

  //printf("midi devs: %d\n", Pm_CountDevices());
  const PmDeviceInfo *info;
  int i=0;
  for (i = 0; i < Pm_CountDevices(); i++) {
    info = Pm_GetDeviceInfo(i);
    if (!info->input) {
      continue;
    }
    printf("%d: %s [input: %d output: %d opened: %d is_virt:%d] (interface: %s)\n", i, info->name, info->input, info->output, info->opened, info->is_virtual, info->interf);
    if (0 == strcmp(synth->midi_device.name, info->name))
      break;
  }

  printf("Selected device: %s [input: %d output: %d opened: %d is_virt:%d] (interf: %s)\n", info->name, info->input, info->output, info->opened, info->is_virtual, info->interf);

  Pt_Start(1, midiCallback, m);

  err = Pm_OpenInput(&(m->stream),
                             i, //Pm_GetDefaultInputDeviceID(),
                             NULL,
                             128,
                             NULL,
                             NULL);
  if (err != pmNoError) {
    fprintf(stdout, "Pm Error: %s\n", Pm_GetErrorText(err));
  }
  enable = 1;
}

// unused
int
get_midi_device_id(const char * name)
{
  int i, c=0;
  const PmDeviceInfo *info;
  for (i = 0; i < Pm_CountDevices(); i++) {
    info = Pm_GetDeviceInfo(i);
    if (!info->input) {
      continue;
    }
    if (!strcmp(name, info->name)) {
      return c;
    }
    c++;
  }
  return -1;
}

char *
get_midi_devices()
{
  int i;
  char *ret = (char *)malloc(sizeof(char) * 4096);
  strcpy(ret, "Select Midi Device;");
  const PmDeviceInfo *info;
  for (i = 0; i < Pm_CountDevices(); i++) {
    info = Pm_GetDeviceInfo(i);
    if (!info->input) {
      continue;
    }
    strcat(ret, info->name);
    strcat(ret, ";");
  }
  ret[strlen(ret) - 1] = '\0';

  return ret;
}

void
terminate_midi(midi_t *m)
{
  Pt_Stop();
  Pm_Close(m->stream);
  Pm_Terminate();
}