https://github.com/amiika/sonicmidilib

Midilib for Sonic Pi

https://github.com/amiika/sonicmidilib

Science Score: 13.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
  • DOI references
  • Academic publication links
  • Academic email domains
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (10.6%) to scientific vocabulary
Last synced: 9 months ago · JSON representation

Repository

Midilib for Sonic Pi

Basic Info
  • Host: GitHub
  • Owner: amiika
  • Language: Ruby
  • Default Branch: main
  • Size: 196 KB
Statistics
  • Stars: 5
  • Watchers: 3
  • Forks: 0
  • Open Issues: 0
  • Releases: 0
Created about 5 years ago · Last pushed about 5 years ago
Metadata Files
Readme Changelog

README.rdoc

= midilib for Sonic Pi

FORK of minilib for Sonic Pi and Ruby 3.x

There is couple of helper method defined in addition to normal midilib methods (See sonicpi.rb):

* midi_to_seq
* midi_to_hash
* play_midi

Example usage:

  load "~/midilib/sonicpi.rb"

  test_midi = ENV['HOME']+'/midilib/examples/pinnin_valssi.mid'

  use_bpm 120

  # Parse to hash
  midi_hash = midi_to_hash test_midi

  print midi_hash.keys
  print midi_hash

  2.times do
    with_fx :reverb, room: 0.7 do
      with_fx :wobble, phase: 1.0, smooth: 0.7, amp: 0.8 do
        # Play directly or from preparsed hash
        play_midi test_midi, [:chiplead, :chipbass]
      end
    end
    # Midi tracks are played in separate threads so sleep until tracks are finished
    sleep midi_hash[:track_lengths].max
  end

For other things see minilib documentation. Requiring midilib classes directly also works, for example: 

  require '~/midilib/io/seqreader'

= midilib

midilib is a pure Ruby MIDI library useful for reading and writing standard
MIDI files and manipulating MIDI event data.

== Overview

midilib can read and write MIDI file formats 0 (one single track) or 1
multiple tracks. By default, it writes format 1 which is the most common
format. MIDI file format 2 is not yet supported.

=== MIDI::Sequence

A sequence contains a collection of tracks and global information like the
sequence's pulses per quarter note (ppqn) and time signature.

The first track in a sequence is special; it holds meta-events like tempo and
sequence name. Don't put any notes in this track.

MIDI::Sequence also contains some convenience methods that let you set and
retrieve the sequence's name, the time signature, and to retrieve the first
tempo event's beats-per-minute value.

Normally instances of MIDI::IO::SeqReader and MIDI::IO::SeqWriter are used
when a sequence reads itself from or writes itself to a MIDI file. You can
change that by setting a sequence's reader_class or writer_class attributes.
Instances of the classes contained in those attributes are created and used
whenever the sequence reads or writes itself.

=== MIDI::Track

A track contains an array of events.

When you modify the +events+ array, make sure to call recalc_times so each
event gets its +time_from_start+ recalculated. You don't have to do that
after every event you add; just remember to do so before using the track in a
way that expects the list of events to be ordered correctly.

A Track also holds a bit mask that specifies the channels used by the track.
This bit mask is set when the track is read from the MIDI file by a SeqReader
but is _not_ kept up to date by any other methods. Specifically, if you add
events to a track at any other time, the bit mask will not be updated.

=== MIDI::Measure

This class contains information about a measure from the sequence. Measure
data is based on the time signature information from the sequence and is not
stored in the sequence itself.

=== MIDI::Measures

The class MIDI::Sequence method get_measures returns a MIDI::Measures object.
MIDI::Measures is a subclass of Array. It is a specialized container for
MIDI::Measure objects, which can be use to map event times to measure numbers.
Please note that this object has to be remade when events are deleted/added in
the sequence.

MIDI::Measure and MIDI::Measures are brought to us by Jari Williamsson
, who also contributed some improvements
to the MIDI::Event and MIDI::Track classes.

=== MIDI::Event

Each event holds not only its delta time but also its time from the start of
the track. The track is responsible for recalculating its events' start times.
You can call MIDI::Track#recalc_times to do so.

Subclasses of MIDI::Event implement the various MIDI messages such as note on
and off, controller values, system exclusive data, and realtime bytes.

MIDI::Realtime events have delta values and start times, just like all the
other midilib event types do. (MIDI real time status bytes don't have delta
times, but this way we can record when in a track the realtime byte was
received and should be sent. This is useful for start/continue/stop events
that control other devices, for example.) Note that when a MIDI::Realtime
event is written out to a MIDI file, the delta time is not written.

MIDI::MetaEvent events hold an array of bytes named 'data'. Many meta events
are string holders (text, lyric, marker, etc.) Though the 'data' value is
always an array of bytes, MIDI::MetaEvent helps with saving and accessing
string. The MIDI::MetaEvent#data_as_str method returns the data bytes as a
string. When assigning to a meta event's data, if you pass in a string it will
get converted to an array of bytes.


== How To Use

The following examples show you how to use midilib to read, write, and
manipulate MIDI files and modify track events. See also the files in the
examples directory, which are described below.


=== Reading a MIDI File

To read a MIDI file, create a MIDI::Sequence object and call its #read method,
passing in an IO object.

The #read method takes an optional block. If present, the block is called
once after each track has finished being read. Each time, it is passed the
track object, the total number of tracks and the number of the current track
that has just been read. This is useful for notifying the user of progress,
for example by updating a GUI progress bar.

 require 'midilib/io/seqreader'

 # Create a new, empty sequence.
 seq = MIDI::Sequence.new()

 # Read the contents of a MIDI file into the sequence.
 File.open('my_midi_file.mid', 'rb') { | file |
     seq.read(file) { | track, num_tracks, i |
         # Print something when each track is read.
         puts "read track #{i} of #{num_tracks}"
     }
 }


=== Writing a MIDI File

To write a MIDI file, call the write method, passing in an IO object.


 require 'midilib/io/seqwriter'

 # Start with a sequence that has something worth saving.
 seq = read_or_create_seq_we_care_not_how()

 # Write the sequence to a MIDI file.
 File.open('my_output_file.mid', 'wb') { | file | seq.write(file) }


=== Editing a MIDI File

Combining the last two examples, here is a script that reads a MIDI file,
transposes some events, and writes the sequence out to a different file. This
is a useful template for programatically manipulating MIDI data.


This code transposes all of the note events (note on, note off, and poly
pressure) on channel 5 down one octave.

==== Transposing One Channel

 require 'midilib/io/seqreader'
 require 'midilib/io/seqwriter'

 # Create a new, empty sequence.
 seq = MIDI::Sequence.new()

 # Read the contents of a MIDI file into the sequence.
 File.open('my_input_file.mid', 'rb') { | file |
     seq.read(file) { | track, num_tracks, i |
         # Print something when each track is read.
         puts "read track #{i} of #{num_tracks}"
     }
 }

 # Iterate over every event in every track.
 seq.each { | track |
     track.each { | event |
         # If the event is a note event (note on, note off, or poly
         # pressure) and it is on MIDI channel 5 (channels start at
         # 0, so we use 4), then transpose the event down one octave.
         if MIDI::NoteEvent === event && event.channel == 4
             event.note -= 12
         end
     }
 }

 # Write the sequence to a MIDI file.
 File.open('my_output_file.mid', 'wb') { | file | seq.write(file) }


=== Manipulating tracks

If you modify a track's list of events directly, don't forget to call
MIDI::Track#recalc_times when you are done.

 track.events[42, 1] = array_of_events
 track.events << an_event
 track.merge(array_of_events)
 track.recalc_times

=== Calculating delta times

A few methods in MIDI::Sequence make it easier to calculate the delta times
that represent note lengths. MIDI::Sequence#length_to_delta takes a note
length (a multiple of a quarter note) and returns the delta time given the
sequence's current ppqn (pulses per quarter note) setting. 1 is a quarter
note, 1.0/32.0 is a 32nd note (use floating-point numbers to avoid integer
rounding), 1.5 is a dotted quarter, etc. See the documentation for that method
for more information.

MIDI::Sequence#note_to_length takes a note name and returns a length value
(again, as a multiple of a quarter note). Legal note names are those found in
MIDI::Sequence::NOTE_TO_LENGTH, and may begin with "dotted" and/or end with
"triplet". For example, "whole", "sixteenth", "32nd", "quarter triplet",
"dotted 16th", and "dotted 8th triplet" are all legal note names.

Finally, MIDI::Sequence#note_to_delta takes a note name and returns a delta
time. It does this by calling note_to_length, then passing the result to
length_to_delta.


=== Example Scripts

Here are short descriptions of each of the examples found in the examples
directory.

* examples/from_scratch.rb shows you how to create a new sequence from scratch
  and save it to a MIDI file. It creates a file called 'from_scratch.mid'.

* examples/seq2text.rb dumps a MIDI file as text. It reads in a sequence and
  uses the to_s method of each event.

* examples/reader2text.rb dumps a MIDI file as text. It subclasses
  MIDI::SeqReader instead of creating a sequence containing tracks and events.

* examples/transpose.rb transposes all note events (note on, note off, poly
  pressure) on a specified channel by a specified amount.

* There is also one MIDI file, examples/NoFences.mid. It is a little pop ditty
  I wrote. The instruments in this file use General MIDI patch numbers and
  drum note assignments. Since I don't normally use GM patches, the sounds
  used here are at best approximations of the sounds I use.


== Resources

The Ruby Web site (http://www.ruby-lang.org/en/index.html) contains an
introduction to Ruby, the Ruby Application Archive (RAA) at
http://raa.ruby-lang.org, and pointers to more information.


Programming Ruby, The Pragmatic Programmer's Guide, by David
Thomas and Andrew Hunt, is a well-written and practical introduction to Ruby.
Its Web page at http://www.rubycentral.com/book also contains a wealth of Ruby
information. Though the first edition book is available online, I encourage
you to purchase a copy of the latest edition.

A description of the MIDI file format can be found in a few places such as
https://www.csie.ntu.edu.tw/~r92092/ref/midi/.

The MIDI message reference at http://www.jimmenard.com/midi_ref.html
describes the format of MIDI commands.


= To Do

:include: TODO.rdoc


= Support

* Visit the forums, bug list, and mailing list pages at
  http://rubyforge.org/projects/midilib

* Send email to Jim Menard at mailto:jim@jimmenard.com

* Ask on the ruby-talk mailing list


= Administrivia

Author:: Jim Menard (mailto:jim@jimmenard.com)
Copyright:: Copyright (c) 2003-2013 Jim Menard
License:: Distributed under the same license as Ruby.


== Copying

midilib is copyrighted free software by Jim Menard and is released under the
same license as Ruby. See the Ruby license at
http://www.ruby-lang.org/en/LICENSE.txt.

midilib may be freely copied in its entirety providing this notice, all
source code, all documentation, and all other files are included.

midilib is Copyright (c) 2003-2013 by Jim Menard.

The song "No Fences" contained in the MIDI file examples/NoFences.mid is
Copyright (c) 1992 by Jim Menard (jim@jimmenard.com). It may be freely used
for non-commercial purposes as long as the author is given credit.


== Recent Changes

=== Changes for 2.0.5:

Updated +install.rb+ to work with newer versions of Ruby by using
+fileutils+ instead of +ftools+.

=== Changes for 2.0.3:

New MIDI::Sequence.pulses_to_seconds method.

=== Changes for 2.0.2:

Stop monkeypatching Array in MIDI::Track.

=== Changes for 2.0.0:

MIDI::NoteOnEvent and MIDI::NoteOffEvent renamed to MIDI::NoteOn and
MIDI::NoteOff. The old names will still work for a while.

The MIDI::Event boolean methods like meta? and note? have been removed. Use
the event classes themselves (for example, MIDI::MetaEvent === my_event or
my_event.kind_of?(MIDI::MetaEvent)). Case statements that use classes work,
too:

  case my_event
  when MIDI::NoteEvent # superclass of note on, note off, poly press
    do_this()
  when MIDI::Controller
    do_that()
  end

Introduced Adam Murray's stable sorting code for
MIDI::Track#recalc_delta_from_times. See
http://wiki.github.com/adamjmurray/cosy/midilib-notes and
http://github.com/adamjmurray/cosy/blob/master/lib/cosy/helper/midi_file_renderer_helper.rb
for details.

Aliased MIDI::Track#sort to MIDI::Track#recalc_delta_from_times, since all
sort did was sort the events then call recalc_delta_from_times, and
recalc_delta_from_times sorts the events before doing anything else.

MIDI::Tempo#mpq_to_bpm now returns a float.

=== Changes for 1.2.0:

Use byte arrays instead of strings for passing around data. All tests now pass
for both Ruby 1.8.X and 1.9.X.

=== New code repository

The midilib code is now hosted at Github (http://github.com/jimm/midilib).

=== Changes for 1.1.4:

* Fixed a bug in KeySig.data_as_bytes. Thanks to Noah Thorp for finding this
  and the bug fixed in 1.1.3.

=== Changes for 1.1.3:

* Fixed the way midilib detects the behavior of IO.getc.

=== Changes for 1.1.2:

* Define MIDI::IO::MIDIFile.getc differently for different Ruby versions,
  instead of checking for String.bytes every time we read a byte.

=== Changes for 1.1.1:

* Make MIDI::IO::MIDIFile.getc do the right thing for both Ruby 1.8 and 1.9.

=== Changes for 1.1.0:

* Added test/test.mid to list of files to be included when packaging midifile
  for distribution.

=== Changes for 1.0.0:

* Fixed the bug in Track#recalc_delta_from_times found by Christopher Rose.

=== Changes for 0.8.7:

* Fixed the misspelled POLY_PRESSURE constant, thanks to Mario Pehle.

=== Changes for 0.8.6:

* Added missing test/test.mid.

=== Changes for 0.8.5:

* Fixed bugs in MIDI::PitchBend reading and writing, thanks to Emanuel
  Borsboom.

* Fixed a bug in MIDI::Track#quantize.

* The argument to MIDI::Track#quantize has changed: it is now either a note
  name ("sixteenth", "32nd", "8th triplet") or a length (1 = quarter, 0.25 =
  sixteenth). This is a drastic change that will break all previous calls to
  quantize. However, since that method was broken already, I don't feel it's
  a burden to anybody to change the arguments.

=== Changes for 0.8.4:

* Realtime status bytes now set @is_realtime to true and return true when
  realtime? is called.

* All system common events now set @is_system to true and return true when
  system? is called, not just system exclusive events.

* Added examples/from_scratch.rb, which shows how to create a sequence
  manually.

* New MIDI::Sequence methods that turn note length names like "32nd", "dotted
  quarter", and "16th triplet" into delta times. See the docs below and
  MIDI::Sequence::length_to_delta, MIDI::Sequence::note_to_length, and
  MIDI::Sequence::note_to_delta.


=== Changes for 0.8.3:

* Added MIDI::NoteEvent.note_to_s, which returns note name as a string like
  "C4" or "F#6".

* Added new boolean attributes to MIDI::Event: @print_decimal_numbers and
  @print_note_names. These are used by all Event to_s methods. See
  examples/seq2text.rb for an example.

=== Changes for 0.8.2:

* Changed MIDI::MetaEvent.type to MIDI::MetaEvent.event_type to avoid
  runtime complaints about Object#type calls.
* Added 'b' binary flag to file open modes for Windows.
* Fixed $LOAD_PATH in example files.
* Fixed read and write block arguments.
* Fixed other example script bugs.

=== Changes for 0.8.1:

* Fixed track sorting.
* Fixed track's recalc_delta_from_times method.
* Fixed event quantization.
* More tests and documentation.


== Warranty

This software is provided "as is" and without any express or implied
warranties, including, without limitation, the implied warranties of
merchantability and fitness for a particular purpose.

Owner

  • Name: Miika Alonen
  • Login: amiika
  • Kind: user
  • Location: Helsinki
  • Company: Gofore

GitHub Events

Total
Last Year

Issues and Pull Requests

Last synced: about 1 year ago

All Time
  • Total issues: 0
  • Total pull requests: 0
  • Average time to close issues: N/A
  • Average time to close pull requests: N/A
  • Total issue authors: 0
  • Total pull request authors: 0
  • Average comments per issue: 0
  • Average comments per pull request: 0
  • Merged pull requests: 0
  • Bot issues: 0
  • Bot pull requests: 0
Past Year
  • Issues: 0
  • Pull requests: 0
  • Average time to close issues: N/A
  • Average time to close pull requests: N/A
  • Issue authors: 0
  • Pull request authors: 0
  • Average comments per issue: 0
  • Average comments per pull request: 0
  • Merged pull requests: 0
  • Bot issues: 0
  • Bot pull requests: 0
Top Authors
Issue Authors
Pull Request Authors
Top Labels
Issue Labels
Pull Request Labels