jazz-soft / JZZ-midi-SMF

Standard MIDI Files: read / write / play
MIT License
40 stars 5 forks source link

Typescript no exported member 'SMF' #21

Open fornof opened 1 year ago

fornof commented 1 year ago

I'm getting an error when I copy paste the example : Module '"jzz-midi-smf"' has no exported member 'SMF'

image

I did 'import JZZ from "jzz" and that gets live audio working. I could not get the jzz-midi-smf to work though.

I'm using Typescript/ Nodejs, compiling down in react and phaser and rendering to the browser. I did notice this one: https://github.com/jazz-soft/JZZ/issues/62 I might be able to help out if there is no quick solution to this.

fornof commented 1 year ago

update: Fixed it. the ES6/Typescript import is broken somehow. using require works fine. This is my working example for typescript.

import {promises as fs} from 'fs'
var JZZ = require('jzz')
var SMF =  require('jzz-midi-smf')

 let testFile = await fs.readFile("test/data/testScale1.mid", "binary")
  var midiout = JZZ().openMidiOut(); //I think this is just for player.play()

var smf = new JZZ.MIDI.SMF(testFile); 
var player = smf.player();
player.connect(midiout);
//player.play();
fs.writeFile('test/data/out.mid', smf.dump(), 'binary');
DJthefirst commented 1 year ago

I am using TypeScript in my project and have encountered the same problem. The require() solution however unfortunately does not work for me.

var JZZ = require('jzz') works fine but var SMF = require('jzz-midi-smf') throws Could not find a declaration file for module 'jzz-midi-smf'. I assume because index.d.ts is missing. I know it is a lot of work to convert the js to TS but is there some online generator to do it?

fornof commented 1 year ago

Use this for smf , rest of the code is above, ignore the smf import var smf = new JZZ.MIDI.SMF(testFile);

DJthefirst commented 1 year ago

I now get Property 'SMF' does not exist on type 'Constructor'. Did you mean 'smf'? I then tried var smf = JZZ.MIDI.smf(); but the var player = smf.player(); says Property 'player' does not exist on type 'MIDI'

The new keyword also did not work with/without.

jazz-soft commented 1 year ago

Use SMF (in capitals) and ignore the warning.

DJthefirst commented 1 year ago

I am using Svelte Kit and require does not work.

image

the code runs if I comment out var smf = new JZZ.MIDI.SMF();

so it can import jzz just fine. The jzz-midi-smf gives me

image

and SMF(); says

image

I assume because it is not imported correctly.\

The JZZ examples run just fine the problem is in JZZ.MIDI.SMF

jazz-soft commented 1 year ago

Call SMF(JZZ); as shown in the example. That will add the missing member.

DJthefirst commented 1 year ago

I still get the same error no change. I assume the error is in the import and TS can't find the package to load.

DJthefirst commented 1 year ago

I was able to get it the typescript errors are just cosmetic.

On a different note is there a way to access the array of available midi in and out ports?

And/Or recreate this function that updates on port change to get a dropdown menu of available ports?

    async initMidiPorts(){
        await navigator.requestMIDIAccess()
        .then((midiAccess) => {
            this.midiAccess = midiAccess;
            this.updateMidiOptions();
            midiAccess.onstatechange = (event) => this.updateMidiOptions();
        });
    }

If not it is no big deal. I appreciate the work you have done on this library. I didn't see it in the docs but could revealing midiAccess.inputs.values() allow this?

fornof commented 1 year ago

You can use other libraries to get the names of midi inputs and midi outputs

fornof commented 3 weeks ago

for getting a list of available outputs you will need to request access to midi, then the browser gives you the inputs and outputs to you:

const midis = await navigator.requestMIDIAccess()
for (let i of midis.outputs.values()){ console.log(i)}

you will want i.name I believe.

I found a solve for in browser typescript react. It is very close to the instructions , except it needs a type.d.ts file

  1. create a new file jzz-midi-smf.d.ts in src/ or similar , in this file put in:
    declare module 'jzz-midi-smf'

This step will give a complete running copy with a save file in the browser. Using react and vite. I wish something was on the main page for quick-start. It took me a few hours to find the docs and put everything together.

  1. 
    import { useEffect, useState } from 'react'
    import JZZ from 'jzz'
    import SMF from 'jzz-midi-smf'
    import './App.css'
    SMF(JZZ)
    function midiPlay(smfIn: any){
    const midiout = JZZ().openMidiOut([0,1,2,3,4]);
    const player = smfIn.player();
    console.log("dump", smfIn.dump())
    player.connect(midiout);
    player.play()
    }
    function midiToBase64Save(data: any){
    //https://jazz-soft.net/demo/WriteMidiFile.html
    // MIDI file dumped as a string
    var b64 = JZZ.lib.toBase64(data); // convert to base-64 string
    var uri = 'data:audio/midi;base64,' + b64; // data URI
    //const a = document.createElement('a');
    //a.href = uri;
    //a.download = name||'itWorks.mid';
    //a.click();
    return uri
    }
    function midiOut(){
    var smf =JZZ.MIDI.SMF(2,96); // type 0, 96 ticks per quarter note
    var trk = new JZZ.MIDI.SMF.MTrk();

smf.push(trk); // add contents: trk.add(0, JZZ.MIDI.smfSeqName('This is a sequence name')) .add(0, JZZ.MIDI.smfBPM(90)) // tempo 90 bpm .add(96, JZZ.MIDI.noteOn(0, 'C6', 127)) .add(96, JZZ.MIDI.noteOn(0, 'Eb6', 127)) .add(96, JZZ.MIDI.noteOn(0, 'G6', 127)) .add(192, JZZ.MIDI.noteOff(0, 'C6')) .add(192, JZZ.MIDI.noteOff(0, 'Eb6')) .add(192, JZZ.MIDI.noteOff(0, 'G6')) .add(988, JZZ.MIDI.smfEndOfTrack());

for (var i = 0; i < smf.length; i++) { for (var j = 0; j < smf[i].length; j++) { console.log('track:', i, 'tick:', smf[i][j].tt, smf[i][j].toString()); // or do whatever else with the message } } return smf } function App() { const [fileOut, SetFileOut] = useState("")

useEffect(()=>{ const smf = midiOut() midiPlay(smf) SetFileOut(midiToBase64Save(smf.dump())) console.log("alldone!") },[]) return ( <>

Click the logo to download the midi file Vite logo
</>

) }

export default App function onEffect(arg0: () => void) { throw new Error('Function not implemented.') }



You will need a midi out to hear `midiPlay()`.  The output just asks for the first output it can find in 0,1,2,3,4 - I think that is what https://jazz-soft.net/doc/JZZ/midiout.html#open OpenMidiOut does anyways 🤷  
fornof commented 3 weeks ago

another issue is that Typescript goes berserk when type at start is not type at end. I resolved with creating a type specifically for SMF which allows testing to continue with jest and not error out:

type Constructor = (typeof JZZ.MIDI)
export interface MidiConstructor extends Constructor  {
    SMF: Function| { MTrk: Function}| any
}
 export function midiOut(){
    var smf =(JZZ.MIDI as MidiConstructor).SMF(2,96); // type 0, 96 ticks per quarter note
  var trk = new (JZZ.MIDI as MidiConstructor).SMF.MTrk();
  ...
  }