npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

hydra-midi

v0.4.3

Published

A utility script for using midi with https://hydra.ojack.xyz.

Downloads

6

Readme

Hydra Midi

A utility script for using midi with https://hydra.ojack.xyz.

Usage

Start (and optionally show) midi and use midi inputs as parameters for hydra:

// You can either use `@latest` or load a specific version with, for example, `@0.4.2`.
await loadScript('https://cdn.jsdelivr.net/npm/hydra-midi@latest/dist/index.js')

// Use midi messages from all channels of all inputs.
await midi.start({ channel: '*', input: '*' })
// Show a small midi monitor (similar to hydra's `a.show()`).
midi.show()

// Use any note to control the red amount of hydra's `solid()` function.
solid(note('*'), 0, 1).out()

// Or, if you are using a midi controller and not a keyboard:
// Use a control change value to control the red amount.
// solid(cc(74), 0, 1).out()

Edit on hydra

CDN

All versions beginning from 0.4.0 can be loaded from npm:

https://cdn.jsdelivr.net/npm/hydra-midi@latest/dist/index.js

all previous versions are available directly from github:

https://cdn.jsdelivr.net/gh/arnoson/[email protected]/dist/index.js

Examples

Use an envelope and scale it

await loadScript('https://cdn.jsdelivr.net/npm/hydra-midi@latest/dist/index.js')

// Use midi messages from all channels of all inputs.
await midi.start({ input: '*', channel: '*' }).show()

// Trigger an ADSR envelope each time the note C3 is played and scale
// the value to a range between 20 and 50.
osc(note('C3').adsr(300, 200, 1, 300).range(20, 50), 0, 0).out()

Edit on hydra

Use multiple midi controllers

await loadScript('https://cdn.jsdelivr.net/npm/hydra-midi@latest/dist/index.js')

await midi.start().show()

// Save input 0 (which in this is example could be a seaboard block) in a
// variable. As the seaboard is an MPE-keyboard it makes sense to listen to all
// channels by default.
seaboard = midi.input(0).channel('*')

// Save input 1 (for example a faderfox) in a variable. We don't specify the
// channel so it defaults to channel 0. To use another channel we have to select
// it explicitly in the `note()` or `cc()` functions.
faderfox = midi.input(1)

// Modulate the noise speed with the seaboard's CC74 values (of any channel).
noise(10, seaboard.cc(74))
  // Use any note on the seaboard to modulate the threshold.
  .thresh(seaboard.note('*').adsr().scale(0.6), 0.1)
  // Use the first 3 faders (which happen to be CC40–CC42 on channel 12) of the
  // faderfox to mix a color.
  .color(faderfox.cc(40, 12), faderfox.cc(41, 12), faderfox.cc(42, 12))
  .out()

Edit on hydra

Documentation

midi.start()

Start midi and optionally set default values. You have to call this function before using any midi functions. Note: It is important to wait the function, otherwise midi access isn't ready and you may not be able to target a specific midi input.

await midi.start({
  // Which input to use in `note()` and `cc()` by default.
  // Can be an input index, an input name or '*' to always listen to all inputs
  // by default.
  input: '*',

  // Which channel to use in `note()` and `cc()` by default.
  // Can be a channel index (0 -> midi channel 1, 1 -> midi channel 2, ...) or
  // '*' to always listen to all channels by default.
  channel: '*',

  // Default attack, decay, sustain and release values for the `adsr()` function
  // that can be chained to `note()`.
  adsr: [100, 100, 1, 100],
})

midi.show() / midi.hide()

Show / hide the midi monitor.

midi.input()

Use the specified input as default. (Doesn't change to global default input)

myKeyboard = midi.input(3)
// Listen to note 60 on the global default channel (defined in `midi.start()`)
//on input #3.
myKeyboard.note(60)
myController = midi.input(3).channel(15)
// Listen to CC74 on channel 15 on input #3.
myController.cc(74)

midi.channel()

Use the specified channel as default. (Doesn't change to global default channel)

ch16 = midi.channel(15)
// Listen to CC42 on midi channel 16 (-> channel index 15!).
ch16.cc(42)

note()

Listen to a note. Returns 1 if the notes is currently playing, 0 otherwise.

note(
  // Can be midi note number, a note name (like 'A3' or 'D#2') or '*' to listen
  // to any note.
  note,
  // A channel index between 0 and 15.
  channel?,
  // An input index or input name. (Show the midi monitor to see the indexes).
  input?
)

adsr()

Trigger an envelope each time a note is played. adsr()can only be used together with note.

// Attack, decay and release are specified in milliseconds, sustain is a factor
// between 0 and 1.
note('C3').adsr(attack, decay, sustain, release)

velocity()

Retrieves the latest velocity played for the note in a range from 0 to 1.

note('C3').velocity()

cc()

Listen to (normalized) CC values.

cc(
  // The CC index.
  index,
  // A channel index between 0 and 15.
  channel?,
  // An input index or input name. (Show the midi monitor to see the indexes).
  input?
)

.onNote()

You can define an event to trigger when a particular note is played on a specific input or channel:

myController = midi.input(3).channel(15)

myController.onNote('c1', () => osc().out())

Or listen to any note:

myController = midi.input(3).channel('*')

scene1 = () => solid(1, 0, 0).out()
scene2 = () => solid(0, 1, 0).out()

myController.onNote('*', ({ note, velocity, channel }) => {
  switch (note) {
    case 36: { scene1(); break; }
    case 37: { scene2(); break; }
  }
})

.onCC()

You can define an event to trigger when a particular CC is changing on a specific input or channel:

myController = midi.input(3).channel(15)

myController.onCC('1', () => osc().out())

Or listen to any CC:

myController = midi.input(3).channel('*')

myController.onCC("*", ({ index, value, channel }) => { 
  console.log("onCCevent")
  console.log(index) 
  console.log(value)
  console.log(channel)
});

### Transforms

Values from `note()`, `cc()` and `adsr()` can be transformed using the following functions:

#### scale()

Scale the value by a factor.

```js
note(60).adsr().scale(20)
cc(40).scale(3)

range()

Map the value to a different range.

cc(41).velocity().range(-0.5, 0.5)

value()

Apply custom transformations to a value.

note(60)
  .adsr()
  .value(v => Math.sin(v))

Accessing midi values directly

note() and cc() do not directly return the corresponding values. Instead they return a function. This is useful for usage as a parameter as the values are updated automatically:

// The color will get updated as soon as the value of CC40 changes.
solid(cc(40), 1, 1).out()

Together with the transforms this should be flexible enough. In same rare cases you might want to use the values directly. You can do so by prefixing the function with an underscore:

osc(() => _cc(40) * 2, 0, 0).out() // This could also be achieved with `scale()` or `value()`.
// This will only set the value when executing the script and won't update.
noteIsPlaying = _note(60)
solid(1, 0, 0).invert(noteIsPlaying).out()

Contribute

Contributions to the script and it's documentation are welcome :~) Please make sure you:

  • use prettier to format your code (should happen automatically if you work on this project in VSCode)
  • use conventional commits (these are used to automatically generate release messages, including credits for your contributions)

To get started have a look at the package.json.