miditime


Namemiditime JSON
Version 1.2.1 PyPI version JSON
download
home_pageNone
SummaryGenerate MIDI files from time series data. You can control can control what octaves and octave ranges you want.
upload_time2025-02-14 01:58:59
maintainerNone
docs_urlNone
authorNone
requires_pythonNone
licenseNone
keywords audio data midi python series time
VCS
bugtrack_url
requirements No requirements were recorded.
Travis-CI No Travis.
coveralls test coverage No coveralls.
            It's MIDITime!
=======================

**UPDATE NOTE Feb. 13, 2025: After a long hiatus I have been revamping this package to use more modern package management and just fix some weird choices I made in the past when I was a less experienced developer. The PyPI package has now also been updated.**

**1 BREAKING CHANGE: Note that import of module is now slightly different in capitalization.**

**I am still new to package creation and am re-learning stuff I forgot long ago, so if you see things that don't make sense please let me know.**

Do you have time time series data you want to play as music? Of course you do!

MIDITime converts any kind of time series data into pitch, velocity and duration values based on musical options that you set up, then outputs a .mid file.

MIDI files aren't technically audio -- they're instructions on how software instruments should be played. You can either play .mid files directly in some music applications, or import them into a wide variety of music editors (like ProTools, Ableton, MaxMSP) and add a ton of bells and whistles to get broadcast-ready audio.

We used MIDITime to produce the data sonification in [this episode of Reveal](https://www.revealnews.org/episodes/power-struggle-the-perilous-price-of-americas-energy-boom/#segment-oklahomas-man-made-earthquakes). The musical track -- without the talking -- [is here](https://www.revealnews.org/article/listen-to-the-music-of-seismic-activity-in-oklahoma/).

Installing
----------

```python
# Temporarily, from this repository
pipenv install https://github.com/mikejcorey/miditime/raw/refs/heads/main/dist/miditime-1.2.1-py2.py3-none-any.whl
```

```python
pip install miditime
```

Usage
----------

### Very basic:
```python
from miditime.MIDITime import MIDITime

# Instantiate the class with a tempo (120bpm is the default) and an output file destination.
mymidi = MIDITime(120, 'myfile.mid')

# Create a list of notes. Each note is a list: [time, pitch, velocity, duration]
midinotes = [
    [0, 60, 127, 3],  #At 0 beats (the start), Middle C with velocity 127, for 3 beats
    [10, 61, 127, 4]  #At 10 beats (12 seconds from start), C#5 with velocity 127, for 4 beats
]

# Add a track with those notes
mymidi.add_track(midinotes)

# Output the .mid file
mymidi.save_midi()

```

### Multiple instruments (programs):

MIDI files can play different instruments. MIDITime assumes that you will want one instrument per track.

A list of MIDI program numbers [can be found here](https://www.ccarh.org/courses/253/handout/gminstruments/).

```python

from miditime.MIDITime import MIDITime

# Instantiate the class with a tempo (120bpm is the default) and an output file destination.
m = MIDITime(120, 'trackdemo.mid')

# Choose instrument for track 1
track_1_program = 0  # 0 = Acoustic grand piano
track_1_notes = [
    [0, 60, 127, 3],  #At 0 beats (the start), Middle C with velocity 127, for 3 beats
    [10, 61, 127, 4]  #At 10 beats (12 seconds from start), C#5 with velocity 127, for 4 beats
]
m.add_track(track_1_notes, track_1_program)  # Program num as argument

# Choose instrument for track 2
track_2_program = 16  # Hammond organ
track_2_notes = [
    [5, 50, 127, 3],  #At 0 beats (the start), C4 with velocity 127, for 3 beats
    [12, 51, 127, 4]  #At 10 beats (12 seconds from start), C#4 with velocity 127, for 4 beats
]
m.add_track(track_2_notes, track_2_program)  # Program num as argument

out_midi_obj = m.save_midi()
```

### A little more fun, a lot more control:

Instantiate the class with a tempo (120bpm is the default), an output file destination,  the number of seconds you want to represent a year in the final song (default is 5 sec/year), the base octave (C5 is middle C, so the default is 5, and how many octaves you want your output to range over (default is 1).

```python
from miditime.MIDITime import MIDITime
mymidi = MIDITime(120, 'myfile.mid', 5, 5, 1)
```

Bring in some data (this is some earthquakes). I'm assuming your data is already in date order, from oldest to newest.
```python
my_data = [
    {'event_date': <datetime object>, 'magnitude': 3.4},
    {'event_date': <datetime object>, 'magnitude': 3.2},
    {'event_date': <datetime object>, 'magnitude': 3.6},
    {'event_date': <datetime object>, 'magnitude': 3.0},
    {'event_date': <datetime object>, 'magnitude': 5.6},
    {'event_date': <datetime object>, 'magnitude': 4.0}
]
```

Convert your date/time data into an integer, like days since the epoch (Jan. 1, 1970). You can use the days_since_epoch() helper method, or not:

```python
my_data_epoched = [{'days_since_epoch': mymidi.days_since_epoch(d['event_date']), 'magnitude': d['magnitude']} for d in my_data]
```

Convert your integer date/time to something reasonable for a song. For example, at 120 beats per minute, you'll need to scale the data down a lot to avoid a very long song if your data spans years. This uses the seconds_per_year attribute you set at the top, so if your date is converted to something other than days you may need to do your own conversion. But if your dataset spans years and your dates are in days (with fractions is fine), use the beat() helper method.

```python
my_data_timed = [{'beat': mymidi.beat(d['days_since_epoch']), 'magnitude': d['magnitude']} for d in my_data_epoched]
```

Get the earliest date in your series so you can set that to 0 in the MIDI:

```python
start_time = my_data_timed[0]['beat']
```

Set up some functions to scale your other variable (magnitude in our case) to match your desired mode/key and octave range. There are helper methods to assist this scaling, very similar to a charting library like D3. You can choose a linear or logarithmic scale.

```python
def mag_to_pitch_tuned(magnitude):
    # Where does this data point sit in the domain of your data? (I.E. the min magnitude is 3, the max in 5.6). In this case the optional 'True' means the scale is reversed, so the highest value will return the lowest percentage.
    scale_pct = mymidi.linear_scale_pct(3, 5.7, magnitude)

    # Another option: Linear scale, reverse order
    # scale_pct = mymidi.linear_scale_pct(3, 5.7, magnitude, True)

    # Another option: Logarithmic scale, reverse order
    # scale_pct = mymidi.log_scale_pct(3, 5.7, magnitude, True)

    # Pick a range of notes. This allows you to play in a key.
    c_major = ['C', 'D', 'E', 'F', 'G', 'A', 'B']

    #Find the note that matches your data point
    note = mymidi.scale_to_note(scale_pct, c_major)

    #Translate that note to a MIDI pitch
    midi_pitch = mymidi.note_to_midi_pitch(note)

    return midi_pitch
```

Now build your note list

```python
note_list = []

for d in my_data_timed:
    note_list.append([
        d['beat'] - start_time,
        mag_to_pitch_tuned(d['magnitude']),
        100,  # velocity
        1  # duration, in beats
    ])
```

And finish

```python
# Add a track with those notes
mymidi.add_track(note_list)

# Output the .mid file
mymidi.save_midi()

```

### Play your music:

There are many programs to work with MIDI. If you are working in jupyter or similar notebook environment, [music21](https://music21-mit.blogspot.com/) is very easy to implement and play right in the notebook.

```
from music21 import converter
m = converter.parse('myfile.mid')
m.show('midi')
```

Other options:
- [timidity](http://sourceforge.net/projects/timidity/) (installable with apt) is a simple command-line one if you just want to hear what you hath wrought.

```
timidity mymidifilename.mid
```

License
----------

This software is released under an MIT license. It would be awful nice if you credited Reveal and Michael Corey somehow if you use this to make something awesome.

Credits
----------

Many thanks to Julia Smith for helping me to understand musical keys/modes better.

MIDITime is a wrapper around the actual midi-making hotness of [midiutil](https://github.com/duggan/midiutil), produced by [Mark Conway Wirt](http://www.emergentmusics.org/site-information).

            

Raw data

            {
    "_id": null,
    "home_page": null,
    "name": "miditime",
    "maintainer": null,
    "docs_url": null,
    "requires_python": null,
    "maintainer_email": null,
    "keywords": "audio, data, midi, python, series, time",
    "author": null,
    "author_email": "Michael Corey <mikejcorey@gmail.com>",
    "download_url": "https://files.pythonhosted.org/packages/d0/6a/72598d2ceedf0c8b9880720414fb891c93e967fcdff56d038b7b568fe68f/miditime-1.2.1.tar.gz",
    "platform": null,
    "description": "It's MIDITime!\n=======================\n\n**UPDATE NOTE Feb. 13, 2025: After a long hiatus I have been revamping this package to use more modern package management and just fix some weird choices I made in the past when I was a less experienced developer. The PyPI package has now also been updated.**\n\n**1 BREAKING CHANGE: Note that import of module is now slightly different in capitalization.**\n\n**I am still new to package creation and am re-learning stuff I forgot long ago, so if you see things that don't make sense please let me know.**\n\nDo you have time time series data you want to play as music? Of course you do!\n\nMIDITime converts any kind of time series data into pitch, velocity and duration values based on musical options that you set up, then outputs a .mid file.\n\nMIDI files aren't technically audio -- they're instructions on how software instruments should be played. You can either play .mid files directly in some music applications, or import them into a wide variety of music editors (like ProTools, Ableton, MaxMSP) and add a ton of bells and whistles to get broadcast-ready audio.\n\nWe used MIDITime to produce the data sonification in [this episode of Reveal](https://www.revealnews.org/episodes/power-struggle-the-perilous-price-of-americas-energy-boom/#segment-oklahomas-man-made-earthquakes). The musical track -- without the talking -- [is here](https://www.revealnews.org/article/listen-to-the-music-of-seismic-activity-in-oklahoma/).\n\nInstalling\n----------\n\n```python\n# Temporarily, from this repository\npipenv install https://github.com/mikejcorey/miditime/raw/refs/heads/main/dist/miditime-1.2.1-py2.py3-none-any.whl\n```\n\n```python\npip install miditime\n```\n\nUsage\n----------\n\n### Very basic:\n```python\nfrom miditime.MIDITime import MIDITime\n\n# Instantiate the class with a tempo (120bpm is the default) and an output file destination.\nmymidi = MIDITime(120, 'myfile.mid')\n\n# Create a list of notes. Each note is a list: [time, pitch, velocity, duration]\nmidinotes = [\n    [0, 60, 127, 3],  #At 0 beats (the start), Middle C with velocity 127, for 3 beats\n    [10, 61, 127, 4]  #At 10 beats (12 seconds from start), C#5 with velocity 127, for 4 beats\n]\n\n# Add a track with those notes\nmymidi.add_track(midinotes)\n\n# Output the .mid file\nmymidi.save_midi()\n\n```\n\n### Multiple instruments (programs):\n\nMIDI files can play different instruments. MIDITime assumes that you will want one instrument per track.\n\nA list of MIDI program numbers [can be found here](https://www.ccarh.org/courses/253/handout/gminstruments/).\n\n```python\n\nfrom miditime.MIDITime import MIDITime\n\n# Instantiate the class with a tempo (120bpm is the default) and an output file destination.\nm = MIDITime(120, 'trackdemo.mid')\n\n# Choose instrument for track 1\ntrack_1_program = 0  # 0 = Acoustic grand piano\ntrack_1_notes = [\n    [0, 60, 127, 3],  #At 0 beats (the start), Middle C with velocity 127, for 3 beats\n    [10, 61, 127, 4]  #At 10 beats (12 seconds from start), C#5 with velocity 127, for 4 beats\n]\nm.add_track(track_1_notes, track_1_program)  # Program num as argument\n\n# Choose instrument for track 2\ntrack_2_program = 16  # Hammond organ\ntrack_2_notes = [\n    [5, 50, 127, 3],  #At 0 beats (the start), C4 with velocity 127, for 3 beats\n    [12, 51, 127, 4]  #At 10 beats (12 seconds from start), C#4 with velocity 127, for 4 beats\n]\nm.add_track(track_2_notes, track_2_program)  # Program num as argument\n\nout_midi_obj = m.save_midi()\n```\n\n### A little more fun, a lot more control:\n\nInstantiate the class with a tempo (120bpm is the default), an output file destination,  the number of seconds you want to represent a year in the final song (default is 5 sec/year), the base octave (C5 is middle C, so the default is 5, and how many octaves you want your output to range over (default is 1).\n\n```python\nfrom miditime.MIDITime import MIDITime\nmymidi = MIDITime(120, 'myfile.mid', 5, 5, 1)\n```\n\nBring in some data (this is some earthquakes). I'm assuming your data is already in date order, from oldest to newest.\n```python\nmy_data = [\n    {'event_date': <datetime object>, 'magnitude': 3.4},\n    {'event_date': <datetime object>, 'magnitude': 3.2},\n    {'event_date': <datetime object>, 'magnitude': 3.6},\n    {'event_date': <datetime object>, 'magnitude': 3.0},\n    {'event_date': <datetime object>, 'magnitude': 5.6},\n    {'event_date': <datetime object>, 'magnitude': 4.0}\n]\n```\n\nConvert your date/time data into an integer, like days since the epoch (Jan. 1, 1970). You can use the days_since_epoch() helper method, or not:\n\n```python\nmy_data_epoched = [{'days_since_epoch': mymidi.days_since_epoch(d['event_date']), 'magnitude': d['magnitude']} for d in my_data]\n```\n\nConvert your integer date/time to something reasonable for a song. For example, at 120 beats per minute, you'll need to scale the data down a lot to avoid a very long song if your data spans years. This uses the seconds_per_year attribute you set at the top, so if your date is converted to something other than days you may need to do your own conversion. But if your dataset spans years and your dates are in days (with fractions is fine), use the beat() helper method.\n\n```python\nmy_data_timed = [{'beat': mymidi.beat(d['days_since_epoch']), 'magnitude': d['magnitude']} for d in my_data_epoched]\n```\n\nGet the earliest date in your series so you can set that to 0 in the MIDI:\n\n```python\nstart_time = my_data_timed[0]['beat']\n```\n\nSet up some functions to scale your other variable (magnitude in our case) to match your desired mode/key and octave range. There are helper methods to assist this scaling, very similar to a charting library like D3. You can choose a linear or logarithmic scale.\n\n```python\ndef mag_to_pitch_tuned(magnitude):\n    # Where does this data point sit in the domain of your data? (I.E. the min magnitude is 3, the max in 5.6). In this case the optional 'True' means the scale is reversed, so the highest value will return the lowest percentage.\n    scale_pct = mymidi.linear_scale_pct(3, 5.7, magnitude)\n\n    # Another option: Linear scale, reverse order\n    # scale_pct = mymidi.linear_scale_pct(3, 5.7, magnitude, True)\n\n    # Another option: Logarithmic scale, reverse order\n    # scale_pct = mymidi.log_scale_pct(3, 5.7, magnitude, True)\n\n    # Pick a range of notes. This allows you to play in a key.\n    c_major = ['C', 'D', 'E', 'F', 'G', 'A', 'B']\n\n    #Find the note that matches your data point\n    note = mymidi.scale_to_note(scale_pct, c_major)\n\n    #Translate that note to a MIDI pitch\n    midi_pitch = mymidi.note_to_midi_pitch(note)\n\n    return midi_pitch\n```\n\nNow build your note list\n\n```python\nnote_list = []\n\nfor d in my_data_timed:\n    note_list.append([\n        d['beat'] - start_time,\n        mag_to_pitch_tuned(d['magnitude']),\n        100,  # velocity\n        1  # duration, in beats\n    ])\n```\n\nAnd finish\n\n```python\n# Add a track with those notes\nmymidi.add_track(note_list)\n\n# Output the .mid file\nmymidi.save_midi()\n\n```\n\n### Play your music:\n\nThere are many programs to work with MIDI. If you are working in jupyter or similar notebook environment, [music21](https://music21-mit.blogspot.com/) is very easy to implement and play right in the notebook.\n\n```\nfrom music21 import converter\nm = converter.parse('myfile.mid')\nm.show('midi')\n```\n\nOther options:\n- [timidity](http://sourceforge.net/projects/timidity/) (installable with apt) is a simple command-line one if you just want to hear what you hath wrought.\n\n```\ntimidity mymidifilename.mid\n```\n\nLicense\n----------\n\nThis software is released under an MIT license. It would be awful nice if you credited Reveal and Michael Corey somehow if you use this to make something awesome.\n\nCredits\n----------\n\nMany thanks to Julia Smith for helping me to understand musical keys/modes better.\n\nMIDITime is a wrapper around the actual midi-making hotness of [midiutil](https://github.com/duggan/midiutil), produced by [Mark Conway Wirt](http://www.emergentmusics.org/site-information).\n",
    "bugtrack_url": null,
    "license": null,
    "summary": "Generate MIDI files from time series data. You can control can control what octaves and octave ranges you want.",
    "version": "1.2.1",
    "project_urls": {
        "Homepage": "https://github.com/cirlabs/miditime"
    },
    "split_keywords": [
        "audio",
        " data",
        " midi",
        " python",
        " series",
        " time"
    ],
    "urls": [
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "8f0084a050c218c8a738f50adc142cd2e6fd9b20ecb028f2b0b82f3cce2c4d9d",
                "md5": "1a046f4449aa54f3591198d76703b0a8",
                "sha256": "5245fe4c91f4889bc41b0ef06860b793d67fa1fb9af38a09e451027ba82a276d"
            },
            "downloads": -1,
            "filename": "miditime-1.2.1-py2.py3-none-any.whl",
            "has_sig": false,
            "md5_digest": "1a046f4449aa54f3591198d76703b0a8",
            "packagetype": "bdist_wheel",
            "python_version": "py2.py3",
            "requires_python": null,
            "size": 8754,
            "upload_time": "2025-02-14T01:58:58",
            "upload_time_iso_8601": "2025-02-14T01:58:58.006393Z",
            "url": "https://files.pythonhosted.org/packages/8f/00/84a050c218c8a738f50adc142cd2e6fd9b20ecb028f2b0b82f3cce2c4d9d/miditime-1.2.1-py2.py3-none-any.whl",
            "yanked": false,
            "yanked_reason": null
        },
        {
            "comment_text": null,
            "digests": {
                "blake2b_256": "d06a72598d2ceedf0c8b9880720414fb891c93e967fcdff56d038b7b568fe68f",
                "md5": "2b18adbf322afa7da2a5eaa6e93e2bb2",
                "sha256": "2038007e80d284b4a56a3c45585a259661cac961f1de64dd42c93d25ebf136a6"
            },
            "downloads": -1,
            "filename": "miditime-1.2.1.tar.gz",
            "has_sig": false,
            "md5_digest": "2b18adbf322afa7da2a5eaa6e93e2bb2",
            "packagetype": "sdist",
            "python_version": "source",
            "requires_python": null,
            "size": 8045,
            "upload_time": "2025-02-14T01:58:59",
            "upload_time_iso_8601": "2025-02-14T01:58:59.093520Z",
            "url": "https://files.pythonhosted.org/packages/d0/6a/72598d2ceedf0c8b9880720414fb891c93e967fcdff56d038b7b568fe68f/miditime-1.2.1.tar.gz",
            "yanked": false,
            "yanked_reason": null
        }
    ],
    "upload_time": "2025-02-14 01:58:59",
    "github": true,
    "gitlab": false,
    "bitbucket": false,
    "codeberg": false,
    "github_user": "cirlabs",
    "github_project": "miditime",
    "travis_ci": false,
    "coveralls": false,
    "github_actions": false,
    "lcname": "miditime"
}
        
Elapsed time: 0.59673s