Code Monkey home page Code Monkey logo

isobar's Introduction

ideoforms / Daniel John Jones

I am a UK-based artist-researcher, interested in algorithmic composition, sonification, systems music, sound installations, and spatial audio.

As part of my practice, I develop a number of open-source frameworks that are focused on generating and interacting with sound, primarily via Python.

isobar's People

Contributors

cprosser avatar dtenenba avatar ideoforms avatar loparcog avatar lvm avatar piotereks avatar platonbjs avatar

Stargazers

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

Watchers

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

isobar's Issues

iso.Key.nearest_note - upper bound issue

Existing iso.Key.nearest_note is not considering (root note + 12) in proximity check.
Example below shows that midi note 59 which is closest to note 60 iso.Key.nearest_note shows 57 instead
image

I can solve that already.

Timeline Actions: Count Beats and Return song position?

The timeline function works ok for what i'm trying to but i can't get it to iterate a variable. Is there someway i'm missing that i can use timeline to regular update the total bars and beats that have elapsed to one variable? Thanks.

Scale.minor - scale.py wrong semitones

Last semitone of minor scale is wrong.
Scale.minor = Scale([0, 2, 3, 5, 7, 8, 11], "minor")
should be
Scale.minor = Scale([0, 2, 3, 5, 7, 8, 10], "minor")
h - half step
w - whole step
A-w-B-h-C-w-D-w-E-h-F-w-G-w-A

In this case between step 6 and 7 (semitone[5] and semitone[6]

Error with Vecops library

in core.py:
import vecops

Tried to install on Python 2.7, but the vecops library isn't there - also tried looking for it online but no success.

Can you help please? I'd love try out isobar, I've been livecoding but never with python!

Euclidean Algorithm with reversed arguments

Hi,

let me first thank you for making and sharing this package, and comment on isobar/pattern/sequence.py#L609 where arguments seems reversed from initially suggested (i.e. http://cgm.cs.mcgill.ca/~godfried/publications/banff.pdf).
I thought to comment on this because also examples elsewhere refer to a function E(k,n), where k denotes the number of one’s and n is the length of the sequence.

As yet another comment, I tried your package on Windows, and made the midi work after replacing python-rtmidi package with rtmidi-python (which works fine on Windows too) and then making some updates to io/midi.py, of course.

Cheers

ModuleNotFoundError: No module named 'isobar.exceptions'

Basically the title says it all.

In [7]: from isobar import *
---------------------------------------------------------------------------
ModuleNotFoundError                       Traceback (most recent call last)
<ipython-input-7-53cf3a29f17a> in <module>()
----> 1 from isobar import *

/tmp/isobar/env/lib/python3.6/site-packages/isobar-0.0.1-py3.6.egg/isobar/__init__.py in <module>()
      9 from .util import *
     10 from .timeline import *
---> 11 from .exceptions import *
     12 
     13 import sys

ModuleNotFoundError: No module named 'isobar.exceptions'

Add universal `every()` method across Pattern classes

Extend PRandomImpulseSequence's every() method across all Pattern subclasses, so that any pattern is able to schedule its own periodic events.

This means a that hook will need to be injected at the start of every class's __next__ method. Nicest option is probably to alter the convention so that the generic processing method for Pattern subclasses becomes next(), and have the top-level Pattern class define a generic __next__ method that triggers hooks before calling and returning self.next().

Restrict total time of a timeline

Hey, thanks so much for making this library. I'm trying to incorporate it into a larger framework to make generative music, which is not using this library in real-time. For that reason, i'm trying to find a way to limit the amount of time a total timeline can take. For example, the Steve Reich example by default loops infinitely, but i would really like to limit it to, say, three minutes.

I know i can add the count argument to the schedule method, but that means i need to add it to every single method call. Is there another way to force the timeline to stop after a specific amount of time, and still make it write / render the composition to a MIDI file as fast as possible?

Request: Call for projects using isobar

I'm writing an academic paper on isobar, and would love to include some references to projects that people have made with it.

So if you've made something that uses isobar in some way and would be happy for me to include it, please throw in a comment here (or email me) with a description and link.

Included so far: @dandante's incredible Acousmatic Tirade LP, and @woudsma's Heron multitrack hardware sequencer for the Raspberry Pi.

Thanks!

thanks

thanks for this
I was trying to do something like this myself
I took days iterating on a good interface to specify a sequence so it can be transformed into midi events.
now I can focus on creatively generating music

problems outputting midi to a file

Hi, first of all isobar is fantastic. I stumbled on it today and am just starting to explore the possibilities.

I took one of the examples and tweaked it a bit, then modified it further so that it would write its output to a midi file. It fails when I do that, but it works fine when outputting to the default midi device.

I can convert the 00 hello world example to write to a file and it works just fine, but this more complex example has an issue when writing to a file. What am I doing wrong?

Here is the stack trace:

$ python 11.ex-lsystem-rhythm.py
[2022-02-10 16:09:08,249] Timeline: Scheduled new track (total tracks: 1)
[2022-02-10 16:09:08,249] Timeline: Starting
 *** Exception in Timeline thread: data byte must be int
Traceback (most recent call last):
  File "11.ex-lsystem-rhythm.py", line 63, in <module>
    timeline.run()
  File "/Users/dtenenba/envs/dev-hXAMQGPn/lib/python3.7/site-packages/isobar/timeline/timeline.py", line 252, in run
    raise e
  File "/Users/dtenenba/envs/dev-hXAMQGPn/lib/python3.7/site-packages/isobar/timeline/timeline.py", line 240, in run
    self.clock_source.run()
  File "/Users/dtenenba/envs/dev-hXAMQGPn/lib/python3.7/site-packages/isobar/timeline/clock.py", line 86, in run
    self.clock_target.tick()
  File "/Users/dtenenba/envs/dev-hXAMQGPn/lib/python3.7/site-packages/isobar/timeline/timeline.py", line 150, in tick
    track.tick()
  File "/Users/dtenenba/envs/dev-hXAMQGPn/lib/python3.7/site-packages/isobar/timeline/track.py", line 124, in tick
    self.perform_event(self.current_event)
  File "/Users/dtenenba/envs/dev-hXAMQGPn/lib/python3.7/site-packages/isobar/timeline/track.py", line 355, in perform_event
    self.output_device.note_on(note, amp, channel)
  File "/Users/dtenenba/envs/dev-hXAMQGPn/lib/python3.7/site-packages/isobar/io/midifile/output.py", line 34, in note_on
    self.miditrack.append(Message('note_on', note=note, velocity=velocity, channel=channel, time=dt_ticks))
  File "/Users/dtenenba/envs/dev-hXAMQGPn/lib/python3.7/site-packages/mido/messages/messages.py", line 114, in __init__
    check_msgdict(msgdict)
  File "/Users/dtenenba/envs/dev-hXAMQGPn/lib/python3.7/site-packages/mido/messages/checks.py", line 99, in check_msgdict
    check_value(name, value)
  File "/Users/dtenenba/envs/dev-hXAMQGPn/lib/python3.7/site-packages/mido/messages/checks.py", line 86, in check_value
    _CHECKS[name](value)
  File "/Users/dtenenba/envs/dev-hXAMQGPn/lib/python3.7/site-packages/mido/messages/checks.py", line 56, in check_data_byte
    raise TypeError('data byte must be int')
TypeError: data byte must be int

and here is the script - the line it fails on is the 2nd to last.

#!/usr/bin/env python3

#------------------------------------------------------------------------
# isobar: ex-lsystem-rhythm
#
# Generates a rhythm line based on an L-system sprouting structure,
# sending output over MIDI.
#------------------------------------------------------------------------

import isobar as iso
import logging

logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(message)s")

#------------------------------------------------------------------------
# Generate a set of pitches based on an l-system, with recursive depth 4.
# symbols:
#  N = generate note node
#  + = transpose up one semitone
#  - = transpose down one semitone
#  [ = enter recursive branch
#  ] = leave recursive branch
#------------------------------------------------------------------------
notes = iso.PLSystem("N+[+N+N--N+N]+N[++N]", depth=4)
notes = iso.PDegree(notes, iso.Scale([0, 4, 6, 7, 10, 11]).shuffle())
# notes = iso.PNearestNoteInKey(notes, iso.Key("C", notes.scale))
min = min(notes)
notes.reset()
max = max(notes)
notes.reset()
notes = notes + 60 # optional...bassy without.
# scale to range of audible midi notes
notes = iso.PScaleLinLin(notes, min, max, 20, 127)
# notes = iso.PInt(notes)
# notes = iso.PDegree(notes, iso.Scale([0, 4, 6, 7, 10, 11]).shuffle())

# TODO - figure out if notes are still in the scale
# notes = notes + 60

#------------------------------------------------------------------------
# use another l-system to generate time intervals.
# take absolute values so that intervals are always positive.
#------------------------------------------------------------------------
times = iso.PLSystem("[N+[NN]-N+N]+N-N+N", depth=3)
times = iso.PAbs(iso.PDiff(times)) * 0.25

#------------------------------------------------------------------------
# ...and another l-system for amplitudes.
#------------------------------------------------------------------------
velocity = (iso.PLSystem("N+N[++N+N--N]-N[--N+N]") + iso.PWhite(-4, 4)) * 8
velocity = iso.PAbs(velocity)

output = iso.MidiFileOutputDevice("output.mid")

# timeline = iso.Timeline(120)#, output_device=output)
timeline = iso.Timeline(iso.MAX_CLOCK_RATE, output_device=output)
timeline.schedule({
    "note": notes,
    "amplitude": velocity,
    "duration": times
})
timeline.stop_when_done = True
timeline.run() # THIS is line 63 that the stack trace fails on
output.write()

Updating a running sequence in the timeline

First of all, thanks for this project! I'm working on a Raspberry Pi based MIDI sequencer and I'm using isobar to sync the timeline to an external MIDI clock, and to send MIDI events to external gear (I'm testing with a MFB-503 drumcomputer).

When I press a button on the hardware sequencer, a Node.js script sends the updated sequence data into the Python process where isobar is running.

I just can't seem to figure out how to update a running sequence without it falling silent until it repeats.
I want to update the notes in place, basically swapping notes/CC in the existing sequence while it keeps playing/repeating.

In the picture I've got 3 tracks (16, 14, 10 steps) with some trigs as an example, each track is scheduled separately in isobar.

pi-sequencer

It kind of works when I set the quantize=4 parameter. The tracks will sync up correctly when the pattern loops after 4 beats (16 steps with a duration of 0.25). But if the sequence is running and I update it when e.g. 10 steps are remaining, the final 10 steps are silent until the pattern loops again.

This is my (simplified) current approach:

_, bpm, midi_clock_in_name, midi_mfb503_out_name = sys.argv

midi_in = iso.MidiInputDevice(device_name=midi_clock_in_name)
midi_out = iso.MidiOutputDevice(device_name=midi_mfb503_out_name, send_clock=True)
timeline = iso.Timeline(bpm, clock_source=midi_in, output_device=midi_out)

events = {}
tracks = {}

while True:
  try:
    line = input().strip() # receive data from parent process on sequence update
    id, trigs, velocities, notes = line.split()

    track_name = f"track-{id}"
    note_seq, velocity_seq = get_seq(trigs, velocities, notes)

    # updated sequence:
    # note_seq:     [48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48, 48]
    # velocity_seq: [127, 0, 0, 0, 127, 0, 0, 0, 127, 0, 127, 0, 127, 0, 0, 0]

    events[track_name] = {
      iso.EVENT_NOTE: iso.PSequence(note_seq),
      iso.EVENT_AMPLITUDE: iso.PSequence(velocity_seq),
      iso.EVENT_DURATION: 0.25,
    }

    if track_name in tracks:
      tracks[track_name].update(events[track_name], quantize=4)
    else:
      tracks[track_name] = timeline.schedule(params=events[track_name], name=track_name, quantize=4)

  except EOFError:
    break

timeline.run()

How can I update the sequence without it stopping for a few beats? I want the current pattern to keep playing, while updating one or more note/CC values in the pattern.

Any thoughts/tips are much appreciated!

FR: Add Linux documentation to virtual midi setup

The current docs only have instructions for Windows and Mac OSX on how to set up a virtual midi bus. This is a surprisingly easy process on Linux if your distro has ALSA preinstalled or if you have ALSA available to you. A pretty quick guide can be found here:

https://tldp.org/HOWTO/MIDI-HOWTO-10.html

Either linking to this or paraphrasing the guide in the docs to mark down how to route midi could be useful for any Linux users

Can't hear sounds

I runned the first example ( also several others) and it says "Opened MIDI output: Midi Through:Midi Through Port-0 14:0". I checked with a midi monitor and it sends a few signals but how can I connect the program to a midi synth on my Ubuntu to hear sounds?

Thanks.

Midi clock stop event does not stop a timeline.

I have something very simple like:

logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(message)s")

midi_in = iso.MidiInputDevice()
timeline = iso.Timeline(clock_source=midi_in)
timeline.schedule({
    "note": iso.PSequence([ 60, 67, 72, 77, 84 ],
    "duration": 0.5,
    "amplitude": iso.PWhite(0, 128)
}, delay=1)
timeline.run()

And I'm sending midi clock and start/stop events from a DAW.

What I'm trying to achieve is for the timeline/sequence to start/stop when the DAW sends the start/stop events and for it to be synced with the DAW's MIDI clock.

Trouble installing using pip3

I tried to install isobar on Ubuntu 20.04, but got this:

(iso) $ pip3 install isobar
Collecting isobar
  Using cached isobar-0.1.1.tar.gz (61 kB)
Requirement already satisfied: mido in /tmp/iso/lib/python3.8/site-packages (from isobar) (1.2.9)
Requirement already satisfied: python-osc in /tmp/iso/lib/python3.8/site-packages (from isobar) (1.7.4)
Collecting python-rtmidi
  Using cached python-rtmidi-1.4.6.tar.gz (247 kB)
Using legacy 'setup.py install' for isobar, since package 'wheel' is not installed.
Using legacy 'setup.py install' for python-rtmidi, since package 'wheel' is not installed.
Installing collected packages: python-rtmidi, isobar
    Running setup.py install for python-rtmidi ... error
    ERROR: Command errored out with exit status 1:
     command: /tmp/iso/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-38din52k/python-rtmidi/setup.py'"'"'; __file__='"'"'/tmp/pip-install-38din52k/python-rtmidi/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-x5klmiqg/install-record.txt --single-version-externally-managed --compile --install-headers /tmp/iso/include/site/python3.8/python-rtmidi
         cwd: /tmp/pip-install-38din52k/python-rtmidi/
    Complete output (39 lines):
    Package jack was not found in the pkg-config search path.
    Perhaps you should add the directory containing `jack.pc'
    to the PKG_CONFIG_PATH environment variable
    No package 'jack' found
    running install
    running build
    running build_py
    creating build
    creating build/lib.linux-x86_64-3.8
    creating build/lib.linux-x86_64-3.8/rtmidi
    copying rtmidi/version.py -> build/lib.linux-x86_64-3.8/rtmidi
    copying rtmidi/midiutil.py -> build/lib.linux-x86_64-3.8/rtmidi
    copying rtmidi/midiconstants.py -> build/lib.linux-x86_64-3.8/rtmidi
    copying rtmidi/__init__.py -> build/lib.linux-x86_64-3.8/rtmidi
    running egg_info
    writing python_rtmidi.egg-info/PKG-INFO
    writing dependency_links to python_rtmidi.egg-info/dependency_links.txt
    writing top-level names to python_rtmidi.egg-info/top_level.txt
    reading manifest file 'python_rtmidi.egg-info/SOURCES.txt'
    reading manifest template 'MANIFEST.in'
    warning: no previously-included files found matching '.appveyor.yml'
    warning: no previously-included files found matching '.travis.yml'
    warning: no previously-included files found matching '*.rst.in'
    no previously-included directories found matching 'ci'
    warning: no previously-included files matching '__pycache__' found under directory '*'
    warning: no previously-included files matching '*.py[co]' found under directory '*'
    writing manifest file 'python_rtmidi.egg-info/SOURCES.txt'
    running build_ext
    building 'rtmidi._rtmidi' extension
    creating build/temp.linux-x86_64-3.8
    creating build/temp.linux-x86_64-3.8/src
    creating build/temp.linux-x86_64-3.8/src/rtmidi
    x86_64-linux-gnu-gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -D__RTMIDI_SILENCE_WARNINGS__ -D__LINUX_ALSA__ -D__UNIX_JACK__ -Isrc/rtmidi -I/tmp/iso/include -I/usr/include/python3.8 -c src/_rtmidi.cpp -o build/temp.linux-x86_64-3.8/src/_rtmidi.o
    x86_64-linux-gnu-gcc -pthread -Wno-unused-result -Wsign-compare -DNDEBUG -g -fwrapv -O2 -Wall -g -fstack-protector-strong -Wformat -Werror=format-security -g -fwrapv -O2 -g -fstack-protector-strong -Wformat -Werror=format-security -Wdate-time -D_FORTIFY_SOURCE=2 -fPIC -D__RTMIDI_SILENCE_WARNINGS__ -D__LINUX_ALSA__ -D__UNIX_JACK__ -Isrc/rtmidi -I/tmp/iso/include -I/usr/include/python3.8 -c src/rtmidi/RtMidi.cpp -o build/temp.linux-x86_64-3.8/src/rtmidi/RtMidi.o
    src/rtmidi/RtMidi.cpp:2938:10: fatal error: jack/jack.h: Datei oder Verzeichnis nicht gefunden
     2938 | #include <jack/jack.h>
          |          ^~~~~~~~~~~~~
    compilation terminated.
    error: command 'x86_64-linux-gnu-gcc' failed with exit status 1
    ----------------------------------------
ERROR: Command errored out with exit status 1: /tmp/iso/bin/python3 -u -c 'import sys, setuptools, tokenize; sys.argv[0] = '"'"'/tmp/pip-install-38din52k/python-rtmidi/setup.py'"'"'; __file__='"'"'/tmp/pip-install-38din52k/python-rtmidi/setup.py'"'"';f=getattr(tokenize, '"'"'open'"'"', open)(__file__);code=f.read().replace('"'"'\r\n'"'"', '"'"'\n'"'"');f.close();exec(compile(code, __file__, '"'"'exec'"'"'))' install --record /tmp/pip-record-x5klmiqg/install-record.txt --single-version-externally-managed --compile --install-headers /tmp/iso/include/site/python3.8/python-rtmidi Check the logs for full command output.

If jack is the JACK Audio Connection Kit, is jack so necessary today for audio?

Installed with pip, example code doesn't work

Hi,
Bit of a noob, so please be kind..
I did "pip install isobar" in the VSCode Terminal on Windows and got the following:

Collecting isobar
  Downloading isobar-0.0.1.tar.gz (40 kB)
     |β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 40 kB 215 kB/s
Collecting python-osc
  Downloading python-osc-1.7.4.tar.gz (23 kB)
Requirement already satisfied: python-rtmidi in c:\users\matthew.petty\appdata\local\programs\python\python36\lib\site-packages (from isobar) (1.4.1)
Collecting midiutil
  Downloading MIDIUtil-1.2.1.tar.gz (1.0 MB)
     |β–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆβ–ˆ| 1.0 MB 198 kB/s
Building wheels for collected packages: isobar, python-osc, midiutil
  Building wheel for isobar (setup.py) ... done
  Created wheel for isobar: filename=isobar-0.0.1-py3-none-any.whl size=39993 sha256=47fc057819579547e9d11d7d58be1fe264e8baab94b650312000260855f9972e
  Stored in directory: c:\users\matthew.petty\appdata\local\pip\cache\wheels\c0\06\72\2e2c2742a309ccb3abe27f5d6b095b682aea77bca07d91d799
  Building wheel for python-osc (setup.py) ... done
  Created wheel for python-osc: filename=python_osc-1.7.4-py3-none-any.whl size=31716 sha256=6c26b461c2453813255321f63e17d5c0bf30745e6a4d4ff61c29f57f709270bc
  Stored in directory: c:\users\matthew.petty\appdata\local\pip\cache\wheels\74\a9\d4\e4888b54ef450d7df4a13f0a70c31295aa9a7656c9995ad071
  Building wheel for midiutil (setup.py) ... done
  Created wheel for midiutil: filename=MIDIUtil-1.2.1-py3-none-any.whl size=55493 sha256=4822101369da83d3552188cafe756b0511b39d37bb752ce1ad530c216b7ce29b
  Stored in directory: c:\users\matthew.petty\appdata\local\pip\cache\wheels\42\09\98\ae0844fcc6f2bbd644cfbd6c04269ce188a43b4bab7e3684d2
Successfully built isobar python-osc midiutil
Installing collected packages: python-osc, midiutil, isobar
Successfully installed isobar-0.0.1 midiutil-1.2.1 python-osc-1.7.4

Then I copied and pasted the example program from the README into a new file. It gives the following problems in the editor:

Module 'isobar' has no 'PSequence' member
Instance of 'Timeline' has no 'Schedule' member

Am I installing it in the correct way? What else could I be doing wrong?

Trouble installing using pip3

Hi,

I'm getting the following error message when using pip3 install isobar-master.zip:

Processing ./isobar-master.zip
Collecting pyOSC>=0.3b0 (from isobar==0.0.1)
Downloading pyOSC-0.3.5b-5294.tar.gz
Complete output from command python setup.py egg_info:
Traceback (most recent call last):
File "", line 1, in
File "/tmp/pip-build-ptkkgifk/pyOSC/setup.py", line 5, in
import OSC
File "/tmp/pip-build-ptkkgifk/pyOSC/OSC.py", line 735
binary = struct.pack('>ll', 0L, 1L)
^
SyntaxError: invalid syntax

----------------------------------------

Command "python setup.py egg_info" failed with error code 1 in /tmp/pip-build-ptkkgifk/pyOSC/

Thanks for taking a look. Hope it's not me.

Modifying events in an active timeline?

Hi! Thank you for this library! How might I modify events in an active timeline? For example, if I wanted to modify pitch, duration, and amplitude in realtime via MIDI CC? I've hastily combined some code from your examples to see if changing a global amplitude variable used when creating events might update those events after they were assigned to a timeline (shown below). That does not work--all the notes play at the same volume. What is the correct approach? Also, importing isobar produces the warning No module named 'link'. What might be causing that?

import isobar as iso

def process_message(message):
    global amplitude
    if message.control == 1:
        amplitude = message.value
    print(" - Received MIDI: %s" % message)

def main():
    global amplitude
    series = iso.PRange(60, 73, 1)
    timeline = iso.Timeline(tempo=120, output_device=midi_out)
    timeline.schedule({
        "note": series,
        "duration": 1,
        "amplitude": amplitude,
    })

    timeline.start()
    midi_in.callback = process_message

if __name__ == "__main__":
    outname = "loopMIDI Port 1 2"
    inname = "Midi Fighter Twister 0"
    midi_out = iso.MidiOutputDevice(device_name=outname,send_clock=True)
    midi_in = iso.MidiInputDevice(device_name=inname)
    amplitude = 64
    main()
    while True:
        pass

subtle timing issue with PScaleLinLin

Below is a script that is meant to do something simple:

  • Create a finite PSequence (pat) from 10 hardcoded notes with repeats=1
  • Create another pattern (dur) using PScaleLinLin to scale the notes in pat to the range 0.2 to 1.2
  • Create reversed copies of each (rpat and rdur)
  • Schedule pat notes to play for duration dur
  • Schedule rpat notes to play for duration rdur
  • Run timeline.

You would expect both tracks to finish at the same time, because the sums of dur and rdur are the same forwards or backwards. But the first track finishes well before the second, as you can hear and see from the logging messages:

[2022-03-17 14:37:24,183] Timeline: Scheduled new track (total tracks: 1)
[2022-03-17 14:37:24,183] Timeline: Scheduled new track (total tracks: 2)
[2022-03-17 14:37:24,183] Timeline: Starting
[2022-03-17 14:37:27,624] Timeline: Track finished, removing from scheduler (total tracks: 1)
[2022-03-17 14:37:35,014] Timeline: Track finished, removing from scheduler (total tracks: 0)
[2022-03-17 14:37:35,014] Timeline: Finished

I found a workaround which is to pre-iterate through dur:

dur = iso.PSequence(dur.all(), 1) # <-- workaround!

When I uncomment that line, the tracks finish in the same millisecond:

[2022-03-17 14:52:35,214] Timeline: Scheduled new track (total tracks: 1)
[2022-03-17 14:52:35,214] Timeline: Scheduled new track (total tracks: 2)
[2022-03-17 14:52:35,214] Timeline: Starting
[2022-03-17 14:52:46,046] Timeline: Track finished, removing from scheduler (total tracks: 1)
[2022-03-17 14:52:46,046] Timeline: Track finished, removing from scheduler (total tracks: 0)
[2022-03-17 14:52:46,046] Timeline: Finished

But I am wondering why this happens. I looked at the code for PScaleLinLin and PMap which it calls, but nothing jumps out at me. It seems that when the PScaleLinLin pattern is iterated "live" (while scheduled on the timeline) something happens and it finishes early.

Not a huge deal since I have a workaround but thought it was worth reporting, and I'm curious what this turns out to be.

I'm on an M1 Mac Mini running macOS 12.3/Monterey and Python 3.10.2 (native ARM build) and the latest version of isobar from PyPI (0.1.1) (but it also happens when I install the latest version from GitHub).

Here is the full script:

#!/usr/bin/env python3

import isobar as iso

import logging

logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(message)s")

patnotes = [74, 1, 76, 76, 52, 30, 83, 13, 97, 9]
pat = iso.PSequence(patnotes, 1)

dur = iso.PScaleLinLin(pat, min(patnotes), max(patnotes), 0.2, 1.2)

# dur = iso.PSequence(dur.all(), 1) # <-- workaround!

rpat = iso.PReverse(pat.copy())
rdur = iso.PReverse(dur.copy())

# all have the same length:
print(len(pat))
print(len(dur))
print(len(rpat))
print(len(rdur))


timeline = iso.Timeline(40)
timeline.stop_when_done = True

timeline.schedule(dict(
    note=pat,
    duration=dur,
    amplitude=100,
    gate=1,
    channel=0
))

timeline.schedule(dict(
    note=rpat,
    duration=rdur,
    amplitude=100,
    gate=1,
    channel=0
))


try:
    timeline.run()
except KeyboardInterrupt:
    timeline.output_device.all_notes_off()

Note C-1 is not managed by note_name_to_midi_note

Note name for midi note 0 is C-1 and is maintained by util.
Inverted function breaks - see below

>>> import isobar as iso
>>> iso.midi_note_to_note_name(0)
'C-1'
>>> iso.note_name_to_midi_note('C-1')
Traceback (most recent call last):
  File "C:\Users\piote\PycharmProjects\isobar\isobar\util.py", line 63, in note_name_to_midi_note
    index = note_names.index([nameset for nameset in note_names if name in nameset][0])
IndexError: list index out of range
During handling of the above exception, another exception occurred:
Traceback (most recent call last):
  File "C:\Program Files\JetBrains\PyCharm\plugins\python-ce\helpers\pydev\pydevconsole.py", line 364, in runcode
    coro = func()
  File "<input>", line 1, in <module>
  File "C:\Users\piote\PycharmProjects\isobar\isobar\util.py", line 65, in note_name_to_midi_note

Hearing audio output on a mac?

I'm unable to hear audio from my macbook pro and I'm wondering if I'm even supposed to hear it? I have the 'IAC' midi enabled and no external midi sources. I'm able to play midi files and hear them through the VLC player.

Here's what's happening:

Using the following code:

# create a repeating sequence with scalar transposition:
# [ 36, 38, 43, 39, ... ]
a = PSeq([ 0, 2, 7, 3 ]) + 36

# apply pattern-wise transposition
# [ 36, 50, 43, 51, ... ]
a = a + PSeq([ 0, 12 ])

# create a geometric chromatic series, repeated back and forth
b = PSeries(0, 1, 12) + 72
b = PPingPong(b)
b = PLoop(b)

# create an velocity series, with emphasis every 4th note,
# plus a random walk to create gradual dynamic changes
amp = PSeq([ 50, 35, 25, 35 ]) + PBrown(0, 1, -20, 20)

# a Timeline schedules events at a given BPM.
# by default, send these over the first MIDI output.
timeline = Timeline(120, debug = True)

# assign each of our Patterns to particular properties
timeline.sched({ 'note': a, 'dur': 1, 'gate': 2 })
timeline.sched({ 'note': b, 'dur': 0.25, 'amp': amp })

timeline.run()

I get the following streaming output:

[isobar] Adding default MIDI output
[isobar] note on  (channel 0, note 36, velocity 64)
[isobar] note on  (channel 0, note 72, velocity 50)
[isobar] note on  (channel 0, note 73, velocity 34)
[isobar] note off (channel 0, note 72)
[isobar] note off (channel 0, note 73)
[isobar] note on  (channel 0, note 74, velocity 23)
[isobar] note on  (channel 0, note 75, velocity 32)
[isobar] note off (channel 0, note 74)
[isobar] note on  (channel 0, note 50, velocity 64)
[isobar] note on  (channel 0, note 76, velocity 46)
[isobar] note off (channel 0, note 75)
[isobar] note off (channel 0, note 76)
[isobar] note on  (channel 0, note 77, velocity 30)
[isobar] note off (channel 0, note 77)
[isobar] note on  (channel 0, note 78, velocity 21)

And yet, I can't actually hear anything.

Am I supposed to be hearing anything? Any suggestions/thoughts?

Thanks!

Can't seem to write MIDI file that I just read..

I altered the 30.ex-midifile-read.py to test re-writing a MIDI file but when it gets to the point where it's supposed to write it, it only writes like 30 bytes, and it never completes so it just keeps running while apparently doing nothing. I obviously can't easily attach the MIDI file I'm using but so far I've used it with all of my MIDI files and it won't write. Am I doing something wrong here?

Note, I have put the "output.write()" before and after "timeline.run()" to no effect.

#!/usr/bin/env python3

#------------------------------------------------------------------------
# ex-midifile-read:
#
# Example of reading from a MIDI file in real time.
#------------------------------------------------------------------------

import isobar as iso
from isobar.io import MidiFileInputDevice
from isobar.io import MidiFileOutputDevice

import argparse
import logging
logging.basicConfig(level=logging.INFO, format="[%(asctime)s] %(message)s")
filename = "output.mid"
output = MidiFileOutputDevice(filename)


parser = argparse.ArgumentParser(description="Read and play a .mid file")
parser.add_argument("filename", type=str, help="File to load (.mid)")
args = parser.parse_args()

#--------------------------------------------------------------------------------
# Read a MIDI file into a pattern.
# The resulting pattern is a PDict, with keys containing patterns for each
# of the event properties (note, duration, amplitude)
#--------------------------------------------------------------------------------
pattern = MidiFileInputDevice(args.filename).read()
print("Read pattern containing %d note events" % len(pattern["note"]))

timeline = iso.Timeline()
timeline.schedule(pattern)
timeline.run()
output.write()
print("wrote midi")

scheduling a track on a background timeline - it gets unscheduled immediately

Hi, I was following along with the jupyter notebook in the examples directory, basically running this code:

from isobar import *
import logging
logging.basicConfig(level=logging.INFO)
timeline = Timeline(120)
timeline.background()
stream1 = timeline.schedule()

The output of this is:

INFO:isobar.timeline.timeline:Timeline: Starting
WARNING:isobar.timeline.clock:Clock: Timer overflowed (late by 0.039s)
WARNING:isobar.timeline.clock:Clock: Timer overflowed (late by 0.280s)
INFO:isobar.io.midi.output:Opened MIDI output: IAC Driver Bus 1
INFO:isobar.timeline.timeline:Timeline: Scheduled new track (total tracks: 1)

INFO:isobar.timeline.timeline:Timeline: Track finished, removing from scheduler (total tracks: 0)

So the track is removed before I can even update() anything on it.

I can confirm that with:

In [3]: timeline.tracks
Out[3]: []

I can call update() on stream1 but nothing happens. When I try and unschedule the track I am told that the track is not scheduled.

Am I doing something wrong? Or is there some issue?

Thanks....

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.