/* * 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-2021 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 . */ #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; idhandle, 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; }