jazz-soft/JZZ

Jzz not working when packaged in Electron

Closed this issue · 12 comments

Hi. I've used the Jzz lib in my Electron app.
Electron version is 26.2.1. I'm using Windows 10
It works very well when I run it under development via Visual Studio Code.
But when I package the Electron app the Jzz lb doesn't work.

Jzz.Info doesn't return a list of available midi devices and outputting midi to a known midi device doesn't work either.

The front end of my Electron app is React and it communicates to the node backend via ipc channels.
It's all written in Typescript

The node backend uses the Jzz lib. Any idea why the packaging of my app stops Jzz from working ? There are no error messages that I could try/catch or log. It simply will not work when packaged.

No, I was not aware of it. TBH, your web site as far as I can see doesn't mention the Electron version. Maybe it does and I simply didn't notice it.
I will install this Electron version and re-test

I removed the "jzz": "^1.7.7" from my package.json.
I then installed: npm install jazz-midi-electron --save
I ran my code in development in VS Code and it works.
I packaged the Electron app and I still have the same problems

In my node file I read the available midi devices as below

export interface IStartUp {
project : IProject;
jzzInfo : any;
}

import JZZ from 'jzz';
const jzzInfo = JZZ().info()
const startUp : IStartUp = { project : midiModel, jzzInfo : jzzInfo }

if(mainWindow){
//send the 'state' to react
mainWindow.webContents.send(FileChannelName, FileChannelNameEvent, startUp);
}

But, const jzzInfo = JZZ().info(); fails the detect anything.
As I said it works under development but not when packaged


So then I did this

import JZZ from 'jazz-midi-electron';

and I created a declaration file named

jzz.d.ts in my node folder where main.ts is located
and put this line in the file
declare module 'jazz-midi-electron';

In main.ts I have this

mainWindow = new BrowserWindow({
show: false,
width: 1920,
height: 1880,
icon: getAssetPath('icon.png'),
webPreferences: {
preload: app.isPackaged
? path.join(__dirname, 'preload.js')
: path.join(__dirname, '../../.erb/dll/preload.js'),
},
});

JZZ.init(mainWindow);

When i execute this line , in VS Code
const jzzInfo = JZZ().info();
It gives this error TypeError: (0, jazz_midi_electron_1.default) is not a function.

If I do not provided the file jzz.d.ts
I get this error of the import
import JZZ from 'jazz-midi-electron';

Could not find a declaration file for module 'jazz-midi-electron'. 'c:/dev/midi/node_modules/jazz-midi-electron/jazz-midi-electron.js' implicitly has an 'any' type.
Try npm i --save-dev @types/jazz-midi-electron if it exists or add a new declaration (.d.ts) file containing declare module 'jazz-midi-electron';ts(7016)


If I use a require

const JZZ = require('jzz');

I get this error
(node:9940) UnhandledPromiseRejectionWarning: TypeError: JZZ.init is not a function

That's right. JZZ.init is not a function. Why are you trying to call it?
Please check if the examples from https://github.com/jazz-soft/jazz-midi-electron work for you.

Yes that was an attempt to try a require statement hence the error with .init()

I know you said to use jazz-midi-electron but that will not run via Typescript
import JZZ from 'jazz-midi-electron';
JZZ().info();
TypeError: (0, jazz_midi_electron_1.default) is not a function.

So I did this in the node process, main.ts, created async/await/promise
import JZZ from 'jzz';

function getInfoAsync() {
return new Promise((resolve) => {
const jzzInfo = JZZ().info();
resolve(jzzInfo);
});
}

async function getMidiInfo() {
const jzzInfo = await getInfoAsync();
console.log(jzzInfo);
return jzzInfo;
}

Then i call the above using .....

const jzzInfo = await getMidiInfo() 

fs.writeFile('test.txt', JSON.stringify(jzzInfo ), err => {
if (err) {
console.error(err);
} else {
// file written successfully
}
});

In development running under VS Code the file test.tx is as follows
{
"engine":"node",
"inputs":[
{"id":"LoopBe Internal MIDI","name":"LoopBe Internal MIDI","manufacturer":"Microsoft Corporation","version":"10.0","engine":"node"},
{"id":"i9","name":"i9","manufacturer":"unknown","version":"1.0","engine":"node"}
],
"name":"JZZ.js",
"outputs":[
{"id":"Microsoft GS Wavetable Synth","name":"Microsoft GS Wavetable Synth", "manufacturer":"Microsoft Corporation","version":"1.0","engine":"node"},
{"id":"LoopBe Internal MIDI","name":"LoopBe Internal MIDI","manufacturer":"Microsoft Corporation","version":"10.0","engine":"node"},
{"id":"i9","name":"i9","manufacturer":"unknown","version":"1.0","engine":"node"}
],
"sysex":true,"ver":"1.7.9","version":"1.5.2"
}
And my app runs perfectly ok there after
When I package the the electron file and run the .exe, test.txt is as follows
{"engine":"none","inputs":[],"name":"JZZ.js","outputs":[],"sysex":true,"ver":"1.7.9","version":"1.7.9"}
There are no midi inputs or outputs in the file

Note there is a difference with the sysex versions

So using your sample-midi-player I put this code into index.js
// optional: adding virtual MIDI-Out port (output to the console)
var vmOut = JZZ.Widget();
vmOut.connect(function(msg) {
console.log(msg.toString());
});
JZZ.addMidiOut('Virtual MIDI-Out', vmOut);

Here’s the code i wrote into index.js, using .then rather than async/await

function getInfoAsync() {
return new Promise((resolve) => {
const jzzInfo = JZZ().info();
resolve(jzzInfo);
});
}

function getMidiInfo() {
return getInfoAsync().then(jzzInfo => {
console.log(jzzInfo);
return jzzInfo;
});
}

getMidiInfo().then(jzzInfo => {
fs.writeFile('test.txt', JSON.stringify(jzzInfo), err => {
if (err) {
console.error(err);
} else {
// file written successfully
}
});
});

let win;
const isMac = process.platform === 'darwin';

It wrote out the correct midi info data to the file test.txt when run in development AND packaged

So next I wrote this javascript file in my typescript project called it midiInfo.js
const JZZ = require('jzz');
const fs = require('fs');

function getInfoAsync() {
return new Promise((resolve) => {
const jzzInfo = JZZ.info();
resolve(jzzInfo);
});
}

function getMidiInfo() {
return getInfoAsync().then(jzzInfo => {
console.log(jzzInfo);
return jzzInfo;
});
}

export function testMidiInfo(){
getMidiInfo().then(jzzInfo => {
fs.writeFile('test.txt', JSON.stringify(jzzInfo), err => {
if (err) {
console.error(err);
} else {
// file written successfully
}
});
});
}
Exported the function as export function testMidiInfo() to typescript and called it.

In development it writes all the info data to the file, but when packaged the file contains
{"name":"JZZ.js","ver":"1.7.9","version":"1.7.9","inputs":[],"outputs":[],"engine":"none","sysex":true}

If I use
const JME = require('jazz-midi-electron');
JME.info() is an error, not a function

So what is the problem with packaged Typescript and your JavaScript lib.
Possible WebPack issue ?

I'll try to investigate what is going on...
Unfortunately. I'm not fluent with TypeScript and WebPack, so I would really appreciate help from someone more knowledgeable...

Yeah, WebPack... it's a nightmare

I've created a minimal Jzz Electron ,React, Typescript app.
It works under VS code, but when packaged the .info is not read

*** EDIT 29/02/24
I had the repo as public it's now private while I look for a fix

Good news, I have fixed the packaging issue.... well I hope so.

https://github.com/RoyTynan/JzzMidiTest.git

I've tested on Windows 10 and MacBook Catalina.
I've include a small read.me file in the repo that hopefully explains how to package.
Please feel free to clone the repo and add to you web site or whatever

Excellent! Thanks a lot!
Just a question: why does one need to add JZZ to the dependencies mmanually?

I've closed this issue as packaging now works