blob: 01296bab00364cd27033b1a5293b809733b4a0c0 [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 <CoreServices/CoreServices.h>
#include <AudioUnit/AudioUnit.h>
#include <AudioToolbox/AudioToolbox.h>
#include "log.h"
#include "midi.h"
struct MidiDeviceStruct {
AUGraph graph;
AudioUnit synth;
/* Note that is currently playing. */
int note;
};
MidiDevice *
openMidiDevice (int errorLevel, const char *device) {
MidiDevice *midi;
int result;
AUNode synthNode, outNode;
ComponentDescription cd;
UInt32 propVal;
if (!(midi = malloc(sizeof(*midi)))) {
logMallocError();
return NULL;
}
/* Create a graph with a software synth and a default output unit. */
cd.componentManufacturer = kAudioUnitManufacturer_Apple;
cd.componentFlags = 0;
cd.componentFlagsMask = 0;
if ((result = NewAUGraph(&midi->graph)) != noErr) {
logMessage(errorLevel, "Can't create audio graph component: %d", result);
goto err;
}
cd.componentType = kAudioUnitType_MusicDevice;
cd.componentSubType = kAudioUnitSubType_DLSSynth;
if ((result = AUGraphNewNode(midi->graph, &cd, 0, NULL, &synthNode))
!= noErr) {
logMessage(errorLevel, "Can't create software synthersizer component: %d",
result);
goto err;
}
cd.componentType = kAudioUnitType_Output;
cd.componentSubType = kAudioUnitSubType_DefaultOutput;
if ((result = AUGraphNewNode(midi->graph, &cd, 0, NULL, &outNode))
!= noErr) {
logMessage(errorLevel, "Can't create default output audio component: %d",
result);
goto err;
}
if ((result = AUGraphOpen(midi->graph)) != noErr) {
logMessage(errorLevel, "Can't open audio graph component: %d", result);
goto err;
}
if ((result = AUGraphConnectNodeInput(midi->graph, synthNode, 0, outNode, 0))
!= noErr) {
logMessage(errorLevel, "Can't connect synth audio component to output: %d",
result);
goto err;
}
if ((result = AUGraphGetNodeInfo(midi->graph, synthNode, 0, 0, 0,
&midi->synth)) != noErr) {
logMessage(errorLevel, "Can't get audio component for software synth: %d",
result);
goto err;
}
if ((result = AUGraphInitialize(midi->graph)) != noErr) {
logMessage(errorLevel, "Can't initialize audio graph: %d", result);
goto err;
}
/* Turn off the reverb. The value range is -120 to 40 dB. */
propVal = false;
if ((result = AudioUnitSetProperty(midi->synth,
kMusicDeviceProperty_UsesInternalReverb,
kAudioUnitScope_Global, 0,
&propVal, sizeof(propVal)))
!= noErr) {
/* So, having reverb isn't that critical, is it? */
logMessage(LOG_DEBUG, "Can't turn of software synth reverb: %d",
result);
}
/* TODO: Maybe just start the graph when we are going to use it? */
if ((result = AUGraphStart(midi->graph)) != noErr) {
logMessage(errorLevel, "Can't start audio graph component: %d", result);
goto err;
}
return midi;
err:
if (midi->graph)
DisposeAUGraph(midi->graph);
free(midi);
return NULL;
}
void
closeMidiDevice (MidiDevice *midi) {
int result;
if (midi) {
if ((result = DisposeAUGraph(midi->graph)) != noErr)
logMessage(LOG_ERR, "Can't dispose audio graph component: %d", result);
free(midi);
}
}
int
flushMidiDevice (MidiDevice *midi) {
return 1;
}
int
setMidiInstrument (MidiDevice *midi, unsigned char channel, unsigned char instrument) {
int result;
if ((result = MusicDeviceMIDIEvent(midi->synth, 0xC0 | channel, instrument,
0, 0)) != noErr)
logMessage(LOG_ERR, "Can't set MIDI instrument: %d", result);
return result == noErr;
}
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) {
int result;
if ((result = MusicDeviceMIDIEvent(midi->synth, 0x90 | channel, note,
volume, 0)) != noErr) {
logMessage(LOG_ERR, "Can't start MIDI note: %d", result);
return 0;
}
midi->note = note;
return 1;
}
int
stopMidiNote (MidiDevice *midi, unsigned char channel) {
int result;
if ((result = MusicDeviceMIDIEvent(midi->synth, 0x90 | channel, midi->note,
0, 0)) != noErr) {
logMessage(LOG_ERR, "Can't stop MIDI note: %d", result);
return 0;
}
midi->note = 0;
return 1;
}
int
insertMidiWait (MidiDevice *midi, int duration) {
usleep(duration * 1000);
return 1;
}