/* MIDI-controlled tone generator * by Paul Coccoli * compile with: gcc -Wall midi-tg.c -o midi-tg -lm */ #include #include #include #include #include #include #include #include #define DEVICE "/dev/dsp" #define TRUE 1 #define FALSE 0 #define BUF_SIZE 16 #define AMP_MAX 0x7fff #define AMP_MIN 0x8000 /* midi stuff */ enum MidiMessageType { NOTE_ON = 0x90 }; extern float notes[]; /* Compute the nth sample of a sine wave of frequency fHz sampled at rHz */ float sine(float f, int n, int r); /* Open the OSS device */ int open_audio_dev(const char* dev, int format, int channels, int rate); int main(int argc,char *argv[]) { /* OSS stuff */ int stereo = FALSE; int rate = 44100; float freq = 440.0; int devfd; /* DSP stuff */ int i, j; short int sample; char buffer[BUF_SIZE]; /* midi stuff */ int midifd; unsigned char midibuf[128]; ssize_t len, n; int channel, notenum, velocity; if ((midifd = open("/dev/midi00", O_RDONLY|O_NONBLOCK|O_SYNC)) < 0) { perror("open: /dev/midi00"); } if ((devfd = open_audio_dev(DEVICE, AFMT_S16_LE, stereo, 44100)) == -1) { fprintf(stderr, "open_audio_dev failed\n"); exit(1); } i = 0; /* number of bytes in buffer */ j = 0; /* number of samples in current cycle */ channel = 0; notenum = 0; velocity = 0; while (TRUE) { /* First check for a note event */ if ((len = read(midifd, midibuf, 128)) > 0) { midibuf[len] = '\0'; switch (midibuf[0] & 0xf0) { case NOTE_ON: { channel = (short)(buffer[0] & 0x0f); notenum = (short)(midibuf[1]); velocity = (short)(midibuf[2]); freq = notes[notenum]; if (velocity > 0) { printf("NOTE ON: %f\n", freq); } else printf("NOTE OFF: %f\n", freq); break; } default: } } /* Compute next sample */ sample = sine(freq, j, rate)*velocity/128*AMP_MAX; /* Put sample into buffer */ buffer[i] = sample & 0xff; buffer[i+1] = (sample >> 8) & 0xff; i += 2; if (i >= BUF_SIZE) { /* Buffer is full */ if ((n = write(devfd, buffer, i)) == -1) { perror("write"); exit(1); } i = 0; } if (++j == rate) { j = 0; } } return 0; } /* Compute the nth sample of a sine wave of frequency fHz sampled at rHz */ float sine(float f, int n, int r) { float sample_num = ((int)f*n)%r; float x = sample_num/r; return sin(x*M_2_PI); } int open_audio_dev(const char* dev, int format, int channels, int rate) { int fd = -1; int orig_format = format; int orig_rate = rate; if ((fd = open(dev, O_WRONLY)) < 0) { perror("open"); return fd; } if (ioctl(fd, SNDCTL_DSP_SETFMT, &format) == -1) { perror("ioctl"); return -1; } if (format != orig_format) { fprintf(stderr, "SNDCTL_DSP_SETFMT failed\n"); return -1; } if (ioctl(fd, SNDCTL_DSP_CHANNELS, &channels) == -1) { perror("ioctl"); return -1; } if (channels != 1) { fprintf(stderr, "SNDCTL_DSP_CHANNELS failed\n"); return -1; } if (ioctl(fd, SNDCTL_DSP_SPEED, &rate) == -1) { perror("ioctl"); return -1; } if (rate != orig_rate) { fprintf(stderr, "SNDCTL_DSP_SPEED failed\n"); return -1; } return fd; } /* MIDI note frequencies in Hz */ float notes[] = { 8.176, 8.662, 9.177, 9.723, 10.301, 10.913, 11.562, 12.250, 12.978, 13.750, 14.568, 15.434, 16.352, 17.324, 18.354, 19.445, 20.602, 21.827, 23.125, 24.500, 25.957, 27.500, 29.135, 30.868, 32.703, 34.648, 36.708, 38.891, 41.203, 43.654, 46.249, 48.999, 51.913, 55.000, 58.270, 61.735, 65.406, 69.296, 73.416, 77.782, 82.407, 87.307, 92.499, 97.999, 103.826, 110.000, 116.541, 123.471, 130.813, 138.591, 146.832, 155.563, 164.814, 174.614, 184.997, 195.998, 207.652, 220.000, 233.082, 246.942, 261.626, 277.183, 293.665, 311.127, 329.628, 349.228, 369.994, 391.995, 415.305, 440.000, 466.164, 493.883, 523.251, 554.365, 587.330, 622.254, 659.255, 698.456, 739.989, 783.991, 830.609, 880.000, 932.328, 987.767, 1046.502, 1108.731, 1174.659, 1244.508, 1318.510, 1396.913, 1479.978, 1567.982, 1661.219, 1760.000, 1864.655, 1975.533, 2093.005, 2217.461, 2349.318, 2489.016, 2637.020, 2793.826, 2959.955, 3135.963, 3322.438, 3520.000, 3729.310, 3951.066, 4186.009, 4434.922, 4698.636, 4978.032, 5274.041, 5587.652, 5919.911, 6271.927, 6644.875, 7040.000, 7458.620, 7902.133, 8372.018, 8869.844, 9397.273, 9956.063, 10548.08, 11175.30, 11839.82, 12543.85 };