Giter VIP home page Giter VIP logo

miditk-smf's Introduction

miditk-smf

A Python toolkit for working with Standard MIDI files

Quickstart

Install:

pip install miditk-smf

Usage:

from miditk.smf import MidiFileWriter

# Open file for writing in binary mode.
with open('minimal.mid', 'wb') as smf:
    # Create standard MIDI file writer.
    midi = MidiFileWriter(smf)

    # Write file and track header for Type 0 file, one track, 96 pulses per
    # quarter note (ppqn). These are also the default parameter values.
    midi.header(format=0, num_tracks=1, tick_division=96)
    midi.start_of_track()

    # Set tempo to 120 bpm in µsec per quarter note.
    # When no tempo is set in the MIDI file, sequencers will generally assume
    # it to be 120 bpm.
    midi.tempo(int(60_000_000 / 120))

    # Add MIDI events.
    midi.note_on(channel=0, note=0x40)
    # Advance 192 ticks (i.e. a half note).
    midi.update_ticks(192)
    midi.note_off(channel=0, note=0x40)

    # End track and midi file.
    midi.end_of_track()
    midi.eof()

For more examples, see the Usage examples section below.

Overview

miditk-smf is a general-purpose library for the parsing and generation of Standard MIDI Files (SMF). The package is part of several planned packages under the common top-level package namespace miditk. This package mainly provides the miditk.smf sub-package for handling standard MIDI files. Additional sub-packages with more specialised MIDI libraries and tools may be developed and distributed as separate package distributions in the future.

Compatibility

miditk-smf works with (C)Python >= 3.8 and PyPy 3.

Installation

miditk-smf is installable via pip from the Python Package Index:

pip install miditk-smf

It is provided as a source distribution and a universal Python wheel for all supported Python versions and operating systems. It only depends on the Python standard library.

Package contents

miditk.common:

A collection of constants from the MIDI specification used by sub-packages and general data types for working with MIDI events.

miditk.smf:

An event-based standard MIDI file (SMF) parsing and generation framework.

miditk.smf.api:

Base event handler classes, which can be subclassed for specialised event handling.

miditk.smf.converters:

A collection of functions that converts the special data types used in midi files to and from byte strings.

miditk.smf.parser:

The main binary MIDI file data parser.

miditk.smf.reader:

Combines the parser with an event handler class.

miditk.smf.sequence:

An event handler, which stores all MIDI events from a MIDI file in a MidiSequence container class.

miditk.smf.writer:

An event handler to write out MIDI events to a standard MIDI File.

Usage examples

The following section contains a few code examples, which demonstrate several usage scenarios for the different modules in the package. For more examples see also the scripts in the examples directory of the source distribution.

Parsing a standard MIDI file

The miditk.smf module provides the MidiSequence container class, which uses its own MIDI event handler class to collect all information and events from parsing a midi file. Use the MidiSequence.fromfile() class method to parse a standard MIDI file.

You can then use several convenience methods of the returned MidiSequence instance to access information about the midi file properties or events.

from miditk.smf import MidiSequence

# Do parsing
sequence = MidiSequence.fromfile(sys.argv[1])

# Print some info from the MIDI file header,
# e.g. number of tracks, events sequence name.
print(sequence)
# Print a list of events with event type, data and timestamp.
sequence.dump_events()

# Iterate over all sysex events in track 0.
# If track is not specified, sysex_events() yields all sysex events
# in all tracks.
for ev in sequence.sysex_events(track=0):
    print("Sysex event ({} bytes) @ {:.2f}".format(len(ev.data), ev.timestamp))

# Iterate over all events sorted by timestamp and then track.
for time, group in sequence.events_by_time():
    for ev in group:
        handle_event(ev)

Changing MIDI events in-stream

The event-based parsing allows to handle MIDI events as they are read (or received via MIDI in). You need to define a sub-class of miditk.smf.BaseMidiEventHandler or miditk.smf.NullMidiEventHandler and overwrite only the event handling methods for the events you are interested in.

The following example transposes all note on/off events by an octave (i.e. 12 semitones):

import sys
from miditk.smf import MidiFileReader, MidiFileWriter

# MidiFileWriter is a sub-class of NullMidiEventHandler.
class Transposer(MidiFileWriter):
    """Transpose note values of all note on/off events by 1 octave."""

    def note_on(self, channel, note, velocity):
        super().note_on(self, channel, min(127, note + 12), velocity)

    def note_off(self, channel, note, velocity):
        super().note_off(self, channel, min(127, note + 12), velocity)

infile = sys.argv.pop(1)
outfile = sys.argv.pop(1)

# Create the parser and event handler
with open(outfile, 'wb') as smf:
    midiout = Transposer(smf)
    midiin = MidiFileReader(infile, midiout)

    # Now do the processing.
    midiin.read()

Development

Clone the Git repository:

git clone https://github.com/SpotlightKid/miditk-smf.git
cd miditk-smf

Install tox:

pip install tox

Or via your Linux distribution package manager, e.g. on debian/Ubuntu:

sudo apt-get install python-tox

Or on Arch Linux:

sudo pacman -S python-tox

Run the tests via tox for all Python versions configured in tox.ini:

tox

If all is well, create a new git branch and start hacking and then contribute your changes by opening a pull request on GitHub.

Code QA

The included Makefile is set up to run several Python static code checking and reporting tools. To print a list of available Makefile targets and the tools they run, simple run:

make

Then run the Makefile target of your choice, e.g.:

make flake8

Unless noted otherwise, these targets run all tools directly, i.e. without tox, which means they need to be installed in your Python environment. You can use [hatch] to create a virtual environments for general development tasks or for specific tasks as, for example, building the documentation. Dependencies and tools needed for these tasks will be installed automatically into these environments on creation:

To show which special environments are defined:

hatch env show

To create and enter e.g. the "dev" environment:

hatch --env dev shell

Documentation

Package documentation is generated by Sphinx. The documentation can be build with:

make docs

After a successful build the documentation index is opened in your web browser.

Authors and License

The miditk package is written by Christopher Arndt and licensed under the MIT License.

The the structure of the miditk.smf sub-package owes inspiration to the Python Midi 1 package, written by [email protected].

Footnotes

  1. Original web site, now defunct: http://www.mxm.dk/products/public/pythonmidi/

miditk-smf's People

Contributors

dependabot[bot] avatar jboone avatar spotlightkid avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar

miditk-smf's Issues

ValueError: bytes must be in range

Hi

I receive the following error while using note_on with legal values (channel=0, note=37, velocity=109):

  File "/home/nico/miniconda2/envs/py36/lib/python3.6/site-packages/miditk/smf/writer.py", line 124, in note_on
    import pdb; pdb.set_trace()
  File "/home/nico/miniconda2/envs/py36/lib/python3.6/site-packages/miditk/smf/writer.py", line 47, in event_slice
    self._write_varlen(self.relative_time)
  File "/home/nico/miniconda2/envs/py36/lib/python3.6/site-packages/miditk/smf/writer.py", line 67, in _write_varlen
    self._write(write_varlen(value))
  File "/home/nico/miniconda2/envs/py36/lib/python3.6/site-packages/miditk/smf/converters.py", line 136, in write_varlen
    return _tobytes(value)
  File "/home/nico/miniconda2/envs/py36/lib/python3.6/site-packages/miditk/smf/converters.py", line 28, in _tobytes
    return bytes(values)
ValueError: bytes must be in range(0, 256)

What am I doing wrong?

Writing System-exclusive data crashes with TypeError

With Python 3.10.8 the following code unfortunately crashes on calling MidiFileWriter.system_exclusive():

import tempfile
from miditk.smf import MidiFileWriter

with tempfile.NamedTemporaryFile(mode='wb', suffix='.mid',
                                 prefix='score-', delete=False) as smf:
    midi = MidiFileWriter(smf)
    midi.header(format=0, num_tracks=1, tick_division=96)
    midi.start_of_track(track=0)
    microsec_per_quarter_note = 500000
    midi.tempo(microsec_per_quarter_note)

    midi.system_exclusive(b'Trigger the crash.') # ...BOOM

    midi.end_of_track()
    midi.eof()

The traceback on my Linux box is:

Traceback (most recent call last):
  File "/home/jakob/Dokumente/prg/Python3/synth_replicator/minimal.py", line 16, in <module>
    midi.system_exclusive(b'Trigger the crash.')
  File "/home/jakob/.local/lib/python3.10/site-packages/miditk/smf/writer.py", line 146, in system_exclusive
    self.event_slice(tobytestr(SYSTEM_EXCLUSIVE) + sysex_len + data +
TypeError: unsupported operand type(s) for +: 'int' and 'bytes'

I guess, a valid solution would be to use write_bew from miditk.smf.converters, like:

number = 42
valid = write_bew(number, length=2) + b'data'

But I am not sure about the endianess and the length.

question

could you please explain the way to install&use it more clearly?

thanks :)

processing midi-file fails

When reading the included midi-file, the library eventually fails with:

9884: Channel event - type: 90H, channel: 2, data: (61, 0) [582517.35]
9885: Meta event - type: 2FH, data: () [582517.35]
Traceback (most recent call last):
  File "/home/folkert/Projects2/midi/./midi-test.py", line 19, in <module>
    print("Sysex event ({} bytes) @ {:.2f}".format(len(ev.data), ev.timestamp))
                                                                 ^^^^^^^^^^^^
AttributeError: 'MidiEvent' object has no attribute 'timestamp'

The program used is the example of the readme:

from miditk.smf import MidiSequence
import sys

sequence = MidiSequence.fromfile(sys.argv[1])

for time, group in sequence.events_by_time():
    for ev in group:
        print(time, ev)

example.zip

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.