0

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 !

4

0 回答 0