All Projects → gomidi → Midi

gomidi / Midi

Licence: mit
Library for reading and writing of MIDI messages and MIDI files (SMF) with Go

Programming Languages

go
31211 projects - #10 most used programming language
golang
3204 projects

Labels

Projects that are alternatives of or similar to Midi

Sfz2bitwig
Convert .SFZ files into Bitwig Studio multisample instruments.
Stars: ✭ 37 (-71.54%)
Mutual labels:  music, midi
Audiokit
Swift audio synthesis, processing, & analysis platform for iOS, macOS and tvOS
Stars: ✭ 8,827 (+6690%)
Mutual labels:  music, midi
Musictheory
🎵 Music theory concepts in Go.
Stars: ✭ 53 (-59.23%)
Mutual labels:  music, midi
Giada
Your Hardcore Loop Machine.
Stars: ✭ 903 (+594.62%)
Mutual labels:  music, midi
Ble Midi For Android
MIDI over Bluetooth LE driver for Android 4.3 or later
Stars: ✭ 90 (-30.77%)
Mutual labels:  music, midi
Intellij Music
Play fancy music based on your keyboard activity in IDE
Stars: ✭ 28 (-78.46%)
Mutual labels:  music, midi
Abcjs
javascript for rendering abc music notation
Stars: ✭ 1,141 (+777.69%)
Mutual labels:  music, midi
Romplayer
AudioKit Sample Player (ROM Player) - EXS24, Sound Font, Wave Player
Stars: ✭ 445 (+242.31%)
Mutual labels:  music, midi
Audiokitsynthone
AudioKit Synth One: Open-Source iOS Synthesizer App
Stars: ✭ 1,258 (+867.69%)
Mutual labels:  music, midi
Minibae
The platform-neutral Beatnik Audio Engine, Mini Edition (miniBAE) is an exceptionally mature, well-rounded, and reliable computer music and sound system specially customized for small-footprint and embedded applications.
Stars: ✭ 82 (-36.92%)
Mutual labels:  music, midi
Zrythm
a highly automated and intuitive digital audio workstation - official mirror
Stars: ✭ 703 (+440.77%)
Mutual labels:  music, midi
Miti
miti is a musical instrument textual interface. Basically, its MIDI, but with human-readable text. 🎵
Stars: ✭ 103 (-20.77%)
Mutual labels:  music, midi
Webaudiofont
Use full GM set of musical instruments to play MIDI and single sounds or effects. Support for reverberation and equaliser. No plugins, no Flash. Pure HTML5 implementation compatible with desktop and mobile browser. See live examples.
Stars: ✭ 600 (+361.54%)
Mutual labels:  music, midi
Midiflip
🎹 MIDI music mayhem - flip, transpose, and arbitrarily remap pitches in MIDI files
Stars: ✭ 33 (-74.62%)
Mutual labels:  music, midi
Lmms
Cross-platform music production software
Stars: ✭ 5,450 (+4092.31%)
Mutual labels:  music, midi
Midichlorian
A Visual Studio extension that allows you to write code and automate the IDE using MIDI musical instruments.
Stars: ✭ 65 (-50%)
Mutual labels:  music, midi
Midiwriterjs
♬ A JavaScript library which provides an API for programmatically generating and creating expressive multi-track MIDI files and JSON objects.
Stars: ✭ 381 (+193.08%)
Mutual labels:  music, midi
Bitmidi.com
🎹 Listen to free MIDI songs, download the best MIDI files, and share the best MIDIs on the web
Stars: ✭ 422 (+224.62%)
Mutual labels:  music, midi
Atm Cli
Command line tool for generating and working with MIDI files.
Stars: ✭ 1,235 (+850%)
Mutual labels:  music, midi
Webmidikit
Simplest MIDI Swift library
Stars: ✭ 100 (-23.08%)
Mutual labels:  music, midi

midi

Modular library for reading and writing of MIDI messages and MIDI files with Go.

Note: If you are reading this on Github, please note that the repo has moved to Gitlab (gitlab.com/gomidi/midi) and this is only a mirror.

  • Go version: >= 1.14
  • OS/architectures: everywhere Go runs (tested on Linux and Windows).

Installation

go get gitlab.com/gomidi/[email protected]

Features

This package provides a unified way to read and write "over the wire" MIDI data and MIDI files (SMF).

  • [x] implementation of complete MIDI standard ("cable" and SMF MIDI)
  • [x] reading and optional writing with "running status"
  • [x] seamless integration with io.Reader and io.Writer
  • [x] allows the reuse of same libraries for live writing and writing to SMF files
  • [x] provide building blocks for other MIDI libraries and applications
  • [x] no dependencies outside the standard library
  • [x] small modular core packages
  • [x] typed Messages

Drivers

For "cable" communication you need a Driverto connect with the MIDI system of your OS. Currently there are two multi-platform drivers available:

  • package gitlab.com/gomidi/rtmididrv based on rtmidi
  • package gitlab.com/gomidi/portmididrv based on portmidi

Projects using this library

Porcelain package

For easy access, the following packages are recommended:

  • reading: gitlab.com/gomidi/midi/reader Go Reference
  • writing: gitlab.com/gomidi/midi/writer Go Reference

The other packages are more low level and allow you to write your own implementations of the midi.Reader, midi.Writerand midi.Driver interfaces to wrap the given SMF and live readers/writers/drivers for your own application.

Example with MIDI cables

package main

import (
	"fmt"
	"time"

	"gitlab.com/gomidi/midi"
	"gitlab.com/gomidi/midi/reader"

	// replace with e.g. "gitlab.com/gomidi/rtmididrv" for real midi connections
	driver "gitlab.com/gomidi/midi/testdrv"
	"gitlab.com/gomidi/midi/writer"
)

// This example reads from the first input and and writes to the first output port
func main() {
	// you would take a real driver here e.g. rtmididrv.New()
	drv := driver.New("fake cables: messages written to output port 0 are received on input port 0")

	// make sure to close all open ports at the end
	defer drv.Close()

	ins, err := drv.Ins()
	must(err)

	outs, err := drv.Outs()
	must(err)

	in, out := ins[0], outs[0]

	must(in.Open())
	must(out.Open())

	defer in.Close()
	defer out.Close()

	// the writer we are writing to
	wr := writer.New(out)

	// to disable logging, pass mid.NoLogger() as option
	rd := reader.New(
		reader.NoLogger(),
		// write every message to the out port
		reader.Each(func(pos *reader.Position, msg midi.Message) {
			fmt.Printf("got %s\n", msg)
		}),
	)

	// listen for MIDI
	err = rd.ListenTo(in)
	must(err)

	err = writer.NoteOn(wr, 60, 100)
	must(err)

	time.Sleep(1)
	err = writer.NoteOff(wr, 60)

	must(err)
	// Output: got channel.NoteOn channel 0 key 60 velocity 100
	// got channel.NoteOff channel 0 key 60
}

func must(err error) {
	if err != nil {
		panic(err.Error())
	}
}

Example with MIDI file (SMF)

package main

import (
	"fmt"
	"os"
	"path/filepath"

	"gitlab.com/gomidi/midi/reader"
	"gitlab.com/gomidi/midi/writer"
)

type printer struct{}

func (pr printer) noteOn(p *reader.Position, channel, key, vel uint8) {
	fmt.Printf("Track: %v Pos: %v NoteOn (ch %v: key %v vel: %v)\n", p.Track, p.AbsoluteTicks, channel, key, vel)
}

func (pr printer) noteOff(p *reader.Position, channel, key, vel uint8) {
	fmt.Printf("Track: %v Pos: %v NoteOff (ch %v: key %v)\n", p.Track, p.AbsoluteTicks, channel, key)
}

func main() {
	dir := os.TempDir()
	f := filepath.Join(dir, "smf-test.mid")

	defer os.Remove(f)

	var p printer

	err := writer.WriteSMF(f, 2, func(wr *writer.SMF) error {
		
		wr.SetChannel(11) // sets the channel for the next messages
		writer.NoteOn(wr, 120, 50)
		wr.SetDelta(120)
		writer.NoteOff(wr, 120)
		
		wr.SetDelta(240)
		writer.NoteOn(wr, 125, 50)
		wr.SetDelta(20)
		writer.NoteOff(wr, 125)
		writer.EndOfTrack(wr)
		
		wr.SetChannel(2)
		writer.NoteOn(wr, 120, 50)
		wr.SetDelta(60)
		writer.NoteOff(wr, 120)
		writer.EndOfTrack(wr)
		return nil
	})

	if err != nil {
		fmt.Printf("could not write SMF file %v\n", f)
		return
	}

	// to disable logging, pass mid.NoLogger() as option
	rd := reader.New(reader.NoLogger(),
		// set the functions for the messages you are interested in
		reader.NoteOn(p.noteOn),
		reader.NoteOff(p.noteOff),
	)

	err = reader.ReadSMFFile(rd, f)

	if err != nil {
		fmt.Printf("could not read SMF file %v\n", f)
	}

	// Output: Track: 0 Pos: 0 NoteOn (ch 11: key 120 vel: 50)
	// Track: 0 Pos: 120 NoteOff (ch 11: key 120)
	// Track: 0 Pos: 360 NoteOn (ch 11: key 125 vel: 50)
	// Track: 0 Pos: 380 NoteOff (ch 11: key 125)
	// Track: 1 Pos: 0 NoteOn (ch 2: key 120 vel: 50)
	// Track: 1 Pos: 60 NoteOff (ch 2: key 120)
}

Low level packages

Go Reference

Example with low level packages

package main

import (
    "bytes"
    "fmt"
    . "gitlab.com/gomidi/midi/midimessage/channel"
    "gitlab.com/gomidi/midi/midimessage/realtime"
    "gitlab.com/gomidi/midi/midireader"
    "gitlab.com/gomidi/midi/midiwriter"
    "io"
    "gitlab.com/gomidi/midi"
)

func main() {
    var bf bytes.Buffer

    wr := midiwriter.New(&bf)
    wr.Write(Channel2.Pitchbend(5000))
    wr.Write(Channel2.NoteOn(65, 90))
    wr.Write(realtime.Reset)
    wr.Write(Channel2.NoteOff(65))

    rthandler := func(m realtime.Message) {
        fmt.Printf("Realtime: %s\n", m)
    }

    rd := midireader.New(bytes.NewReader(bf.Bytes()), rthandler)

    var m midi.Message
    var err error

    for {
        m, err = rd.Read()

        // breaking at least with io.EOF
        if err != nil {
            break
        }

        // inspect
        fmt.Println(m)

        switch v := m.(type) {
        case NoteOn:
            fmt.Printf("NoteOn at channel %v: key: %v velocity: %v\n", v.Channel(), v.Key(), v.Velocity())
        case NoteOff:
            fmt.Printf("NoteOff at channel %v: key: %v\n", v.Channel(), v.Key())
        }

    }

    if err != io.EOF {
        panic("error: " + err.Error())
    }

    // Output:
    // channel.Pitchbend channel 2 value 5000 absValue 13192
    // channel.NoteOn channel 2 key 65 velocity 90
    // NoteOn at channel 2: key: 65 velocity: 90
    // Realtime: Reset
    // channel.NoteOff channel 2 key 65
    // NoteOff at channel 2: key: 65
}

Modularity

Apart from the porcelain packages there are small subpackages, so that you only need to import what you really need.

This keeps packages and dependencies small, better testable and should result in a smaller memory footprint which should help smaller devices.

For reading and writing of cable and SMF MIDI data io.Readers are accepted as input and io.Writers as output. Furthermore there are common interfaces for live and SMF MIDI data handling: midi.Reader and midi.Writer. The typed MIDI messages used in each case are the same.

To connect with MIDI libraries expecting and returning plain bytes, use the midiio subpackage.

Stability / API / Semantic Versioning

This excellent blog post by Peter Bourgon describes perfectly the problem, I have with Go modules' take on semantic versioning.

First of all, the API of this library is large (if you use all of it, which is unlikely) and generally stable. There may be small incompatibilities from time to time that will only affect a small amount of users and that will "blow up into your face" when compiling. These are easy to fix with the help of the compiler and the documentation.

The diamond dependency problem should be unlikely with this library, since that would mean, you are using a library that is abstracting over MIDI and uses this library in order to do so. Well, then you should reconsider the use of that library, since there really is no point in further abstractions. For other uses however (e.g. integration), the interfaces should be used and they won't change.

As a last resort, you could fork that other library, add a replace statement to your go.mod and fix the issue, followed by a pull request. If that libary requiring the old code is not maintained anymore, you would need to fork anyway in the future. If it is maintained, your pull request should get accepted.

We are not going to bump the major version with every tiny incompatible change of this code base. The costs are too high and the benefits too low. Instead, any minor version upgrade reflects incompatible changes and the patchversions contain compatible changes (i.e. also compatible additions). A large rewrite will however end up in a major version bump.

License

MIT (see LICENSE file)

Credits

Inspiration and low level code for MIDI reading (see internal midilib package) came from the http://github.com/afandian/go-midi package of Joe Wass which also helped as a starting point for the reading of SMF files.

Alternatives

Matt Aimonetti is also working on MIDI inside https://github.com/mattetti/audio but I didn't try it.

Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].