blob: f0e35bbc581eafaf27e4ab16c8b9ab57312434d9 [file] [log] [blame]
/*
* BRLTTY - A background process providing access to the console screen (when in
* text mode) for a blind person using a refreshable braille display.
*
* Copyright (C) 1995-2023 by The BRLTTY Developers.
*
* BRLTTY comes with ABSOLUTELY NO WARRANTY.
*
* This is free software, placed under the terms of the
* GNU Lesser General Public License, as published by the Free Software
* Foundation; either version 2.1 of the License, or (at your option) any
* later version. Please see the file LICENSE-LGPL for details.
*
* Web Page: http://brltty.app/
*
* This software is maintained by Dave Mielke <dave@mielke.cc>.
*/
#include "prologue.h"
#include "log.h"
#include "parse.h"
#include "timing.h"
#include "midi.h"
struct MidiDeviceStruct {
HMIDIOUT handle;
unsigned char note;
int count;
char buffer[0X80];
};
typedef enum {
MIDI_NoteOff = 0X80,
MIDI_NoteOn = 0X90,
MIDI_KeyPressure = 0XA0,
MIDI_ControlChange = 0XB0,
MIDI_ProgramChange = 0XC0,
MIDI_ChannelPresure = 0XD0,
MIDI_PitchBend = 0XE0,
MIDI_SystemPrefix = 0XF0
} MidiEvent;
static void
logMidiOutError (MMRESULT error, int errorLevel, const char *action) {
char text[MAXERRORLENGTH];
midiOutGetErrorText(error, text, sizeof(text));
logMessage(errorLevel, "%s error %d: %s", action, error, text);
}
static int
addMidiMessage (MidiDevice *midi, const unsigned char *message, int length) {
if ((midi->count + length) > sizeof(midi->buffer))
if (!flushMidiDevice(midi))
return 0;
memcpy(&midi->buffer[midi->count], message, length);
midi->count += length;
return 1;
}
static int
writeMidiMessage (MidiDevice *midi, const unsigned char *message, int length) {
if (!addMidiMessage(midi, message, length)) return 0;
if (!flushMidiDevice(midi)) return 0;
return 1;
}
MidiDevice *
openMidiDevice (int errorLevel, const char *device) {
MidiDevice *midi;
MMRESULT error;
int id = 0;
static const char *const defaultDevice = "default";
if (!*device) device = defaultDevice;
if (strcmp(device, defaultDevice) == 0) {
id = -1;
} else if (!isInteger(&id, device) || (id < 0) || (id >= midiOutGetNumDevs())) {
int count = midiOutGetNumDevs();
for (id=0; id<count; ++id) {
MIDIOUTCAPS cap;
if (midiOutGetDevCaps(id, &cap, sizeof(cap)) == MMSYSERR_NOERROR)
if (strncasecmp(device, cap.szPname, strlen(device)) == 0)
break;
}
if (id == count) {
logMessage(errorLevel, "invalid MIDI device number: %s", device);
return NULL;
}
}
if ((midi = malloc(sizeof(*midi)))) {
if ((error = midiOutOpen(&midi->handle, id, 0, 0, CALLBACK_NULL)) == MMSYSERR_NOERROR) {
midi->note = 0;
midi->count = 0;
return midi;
} else {
logMidiOutError(error, errorLevel, "MIDI device open");
}
free(midi);
} else {
logSystemError("MIDI device allocation");
}
return NULL;
}
void
closeMidiDevice (MidiDevice *midi) {
flushMidiDevice(midi);
midiOutClose(midi->handle);
free(midi);
}
int
flushMidiDevice (MidiDevice *midi) {
int ok = 1;
if (midi->count > 0) {
MMRESULT error;
MIDIHDR header;
header.lpData = midi->buffer;
header.dwBufferLength = midi->count;
header.dwFlags = 0;
if ((error = midiOutPrepareHeader(midi->handle, &header, sizeof(header))) == MMSYSERR_NOERROR) {
if ((error = midiOutLongMsg(midi->handle, &header, sizeof(header))) == MMSYSERR_NOERROR) {
midi->count = 0;
} else {
logMidiOutError(error, LOG_ERR, "midiOutLongMsg");
ok = 0;
}
while ((error = midiOutUnprepareHeader(midi->handle, &header, sizeof(header))) == MIDIERR_STILLPLAYING) {
approximateDelay(1);
}
if (error != MMSYSERR_NOERROR) {
logMidiOutError(error, LOG_ERR, "midiOutUnprepareHeader");
}
} else {
logMidiOutError(error, LOG_ERR, "midiOutPrepareHeader");
ok = 0;
}
}
return ok;
}
int
setMidiInstrument (MidiDevice *midi, unsigned char channel, unsigned char instrument) {
const unsigned char message[] = {
MIDI_ProgramChange|channel, instrument
};
return writeMidiMessage(midi, message, sizeof(message));
}
int
beginMidiBlock (MidiDevice *midi) {
return 1;
}
int
endMidiBlock (MidiDevice *midi) {
return 1;
}
int
startMidiNote (MidiDevice *midi, unsigned char channel, unsigned char note, unsigned char volume) {
const unsigned char message[] = {
MIDI_NoteOn|channel, note, (0X7F * volume / 100)
};
int ok = writeMidiMessage(midi, message, sizeof(message));
if (ok) midi->note = note;
return ok;
}
int
stopMidiNote (MidiDevice *midi, unsigned char channel) {
const unsigned char message[] = {
MIDI_NoteOff|channel, midi->note, 0
};
int ok = writeMidiMessage(midi, message, sizeof(message));
if (ok) midi->note = 0;
return ok;
}
int
insertMidiWait (MidiDevice *midi, int duration) {
approximateDelay(duration);
return 1;
}