I'm trying to make a simple DAW using Electron and C++ addons, and I want to use PortMidi for Midi keyboards support.
I made a midi.h
script to see if PortMidi works and I manage to compile and run the script with mingw-g++ (and it works), but when I build the nodejs addon using node-gyp, and run the app it shows this error during the execution of Pm_OpenInput(&midi_in, id, NULL, 0, NULL, NULL);
(Which I believe means that the c++ script stopped for no reason...)
npm ERR! code ELIFECYCLE
npm ERR! errno 4294930435
npm ERR! fynewav@0.1.0 start: `electron ./server/main.js`
npm ERR! Exit status 4294930435
npm ERR!
npm ERR! Failed at the fynewav@0.1.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
I generated the .lib
and .dll
files for PortMidi myself from this github repository https://github.com/philandstuff/portmidi using this command in a /build/
subfolder :
cmake .. -DCMAKE_MSVC_RUNTIME_LIBRARY=MultiThreadedDLL -DBUILD_SHARED_LIBS:BOOL=ON -DPM_CHECK_ERRORS=ON; cmake --build .
Here's my midi.h
file:
#include <iostream>
#include <thread>
#include "portmidi.h"
#define MIDI_SYSEX 0xf0
#define MIDI_EOX 0xf7
int active = FALSE;
bool running = true;
std::thread midiThread;
PmStream *midi_in;
int thru_sysex_in_progress = FALSE;
int app_sysex_in_progress = FALSE;
void process_midi()
{
while (running)
{
PmError result;
PmEvent buffer;
if (!active) continue;
if (!app_sysex_in_progress) {
do {
result = Pm_Poll(midi_in);
if (result) {
int status;
int rslt = Pm_Read(midi_in, &buffer, 1);
if (rslt == pmBufferOverflow)
continue;
status = Pm_MessageStatus(buffer.message);
if (((status & 0x80) == 0) && !thru_sysex_in_progress) {
continue; /* sysex old data, ignore */
}
int i = Pm_MessageData1(buffer.message);
int i2 = Pm_MessageData2(buffer.message);
int s = Pm_MessageStatus(buffer.message);
printf("Note %d - Force %d - State %s\n", i, i2, (s==128)? "Off": "On");
/* sysex processing */
if (status == MIDI_SYSEX) thru_sysex_in_progress = TRUE;
else if ((status & 0xF8) != 0xF8) {
/* not MIDI_SYSEX and not real-time, so */
thru_sysex_in_progress = FALSE;
}
if (thru_sysex_in_progress && /* look for EOX */
(((buffer.message & 0xFF) == MIDI_EOX) ||
(((buffer.message >> 8) & 0xFF) == MIDI_EOX) ||
(((buffer.message >> 16) & 0xFF) == MIDI_EOX) ||
(((buffer.message >> 24) & 0xFF) == MIDI_EOX))) {
thru_sysex_in_progress = FALSE;
}
if (thru_sysex_in_progress)
std::cout << "sysex in progress" << std::endl;
}
} while (result);
}
}
}
void portOpen()
{
const PmDeviceInfo *info;
PmDeviceID id;
PmError err = Pm_Initialize();
midiThread = std::thread(process_midi);
id = Pm_GetDefaultInputDeviceID();
info = Pm_GetDeviceInfo(id);
if (info == nullptr)
{
std::cout << "No Midi device found" << std::endl;
return;
}
printf("Opening input device %s %s\n", info->interf, info->name);
Pm_OpenInput(&midi_in, id, NULL, 0, NULL, NULL);
Pm_SetFilter(midi_in, PM_FILT_ACTIVE | PM_FILT_CLOCK);
active = true;
return;
}
void portClose()
{
active = false;
running = false;
midiThread.join();
Pm_Close(midi_in);
Pm_Terminate();
}
And here's the main.cpp
file I compiled with mingw-g++ to try PortMidi (and it works with g++)
#include "midi.h"
int main(int argc, char const *argv[])
{
portOpen();
std::this_thread::sleep_for(std::chrono::seconds(5));
portClose();
}
Here's the binding.gyp
file I'm using for node-gyp: (I only made the right includes for Windows)
{
"targets": [
{
"target_name": "engine",
"sources": [ "./engine/wrapper.cpp" ],
"include_dirs": [
"<(module_root_dir)/engine/include/",
"<!@(node -p \"require('node-addon-api').include\")"
],
'conditions': [
['OS=="win"',
{
'defines': [
'__WINDOWS_MM__'
],
'link_settings': {
'libraries': [
"-lOpenAL32",
"-lsndfile",
"-lportmidi"
],
'library_dirs': [
"<(module_root_dir)/engine/lib/"
]
}
}
],
['OS=="linux"',
{
'cflags_cc!': [
'-fno-exceptions'
],
'defines': [
'__LINUX_ALSASEQ__'
],
'link_settings': {
'libraries': [
'-lal'
]
}
}
],
['OS=="mac"',
{
'defines': [
'__MACOSX_CORE__'
],
'xcode_settings': {
'GCC_ENABLE_CPP_EXCEPTIONS': 'YES'
},
'link_settings': {
'libraries': [
'OpenAL.framework',
],
}
}
]
]
}
]
}
I already tried to do node-gyp clean
, .\node_modules\.bin\electron-rebuild
, node-gyp configure
, node-gyp build
and rebuilding the necessary .lib
and .dll
files for OpenAL, sdnFile and PortMidi.
I also tried using Pt_Start()
and Pt_Stop()
instead of making my own thread for the process_midi
function, but it didn't changed anything.
I checked in the portmidi.c
source file and it seems like the line 733 is causing the problem, but I'm a little bit lost ...
/* open system dependent input device */
err = (*midi->dictionary->open)(midi, inputDriverInfo);
I'm on Windows 10 Pro with:
- node-gyp v8.1.0
- electron v13.1.2
- node v14.16.0
The c++ addon works fine with sndFile and OpenAL, but only PortMidi don't work :/
All the source code from the app can be found here: https://github.com/FurWaz/FyneWav
If anyone could give me hints or suggest other c++ libraries for MIDI, that would be really helpful ^^ thanks !