Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

ProgramChange does not work #37

Open
Grumpy-Mike opened this issue Jul 14, 2021 · 20 comments
Open

ProgramChange does not work #37

Grumpy-Mike opened this issue Jul 14, 2021 · 20 comments

Comments

@Grumpy-Mike
Copy link

Grumpy-Mike commented Jul 14, 2021

When using the Program Change call it sends 0x0 followed by the patch number.
Example code:-

import usb_midi
import adafruit_midi
from adafruit_midi.note_off import NoteOff
from adafruit_midi.note_on import NoteOn
from adafruit_midi.program_change import ProgramChange
import time

midi = adafruit_midi.MIDI(
        midi_in=usb_midi.ports[0], in_channel=0, midi_out=usb_midi.ports[1], out_channel=0 )

midi.send(NoteOn(65,100))
time.sleep(0.6)
midi.send(NoteOff(65,0))
time.sleep(0.6)
midi.send(ProgramChange(0x7))

I notice in the unit test test_MIDI_unittests.py this call is not tested despite the import list described as the "full monty".
Is there a temporary fix to send this message? I have been tying to specify the data bytes and sent those, but I keep getting error messages like "AttributeError: 'NoneType' object has no attribute 'channel'".

@tannewt
Copy link
Member

tannewt commented Jul 14, 2021

@todbot @jedgarpark Have you used ProgramChange at all?

@tannewt
Copy link
Member

tannewt commented Jul 14, 2021

Corresponding forum post: https://forums.adafruit.com/viewtopic.php?f=60&t=181219

@todbot
Copy link

todbot commented Jul 14, 2021

ProgramChange MIDI messages work for me via USB MIDI using adafruit_midi on one of the latest CP7 builds Adafruit CircuitPython 7.0.0-alpha.4-51-gc18c33050 on 2021-07-13; Adafruit Rotary Trinkey M0 with samd21e18.

This script I'm using is:

import time, usb_midi, adafruit_midi
from adafruit_midi.note_on import NoteOn
from adafruit_midi.note_off import NoteOff
from adafruit_midi.program_change import ProgramChange
midi = adafruit_midi.MIDI( midi_in=usb_midi.ports[0], midi_out=usb_midi.ports[1] )
i=0
while True:
    midi.send(NoteOn("A4",10))
    time.sleep(1)
    midi.send(NoteOff("A4"))
    time.sleep(1)
    midi.send(ProgramChange(i))
    i = (i+1) % 127
    time.sleep(1)

The output I see is this:
Screen Shot 2021-07-14 at 11 30 53a

And VSTs in Ableton Live respond appropriately to the Program Change messages.

@todbot
Copy link

todbot commented Jul 14, 2021

I just verified the problem @Grumpy-Mike is seeing is related to the recently-closed adafruit/circuitpython#4895
This problem exists in 7.0.0-alpha4 but not in the latest builds.

@Grumpy-Mike, I suggest going to https://circuitpython.org/downloads, finding your board, then scrolling down to the "Absolute Newest / Browse S3" section. From there you can download a latest build that fixes this issue.

@Grumpy-Mike
Copy link
Author

Thanks. I did try what you said but was a little lost as to what I found there. It was a list of language options all zero sized. I followed it up to https://adafruit-circuit-python.s3.amazonaws.com / where some of them stopped being zero size and actually had content. Sorry but I have no idea what to do next with these files, non of which seemed to have relevant names. I can confirm I was using 6.3.0 as it was the latest stable version and my board is a Pico.

@todbot
Copy link

todbot commented Jul 14, 2021

@Grumpy-Mike apologies, I forget not everyone is familar with old-timey web directory displays.
For the Pico, you'll end up with a page that looks like this:
screenshot1

Those are all directories. Pick the one for your language & region. I pick "en_US" because I speak English in the USA. And then you'll see a page that looks like this:
Screen Shot 2021-07-14 at 3 11 31p

Those are all CircuitPython UF2 install files, like you normally use to install CircuitPython. They are arranged newest-first, with the newest one in the above screenshot being made on 2021-07-14 @ 19:21 GMT. Unless I need a specific build, I always pick the top (most recent) one.

When you click on it, it downloads a UF2 file, and you can use it to install CircuitPython on your device just like normal.

@Grumpy-Mike
Copy link
Author

Thanks for that but sadly it did not work, I still get the invalid message even with todbot's code. I used the latest build adafruit-circuitpython-raspberry_pi_pico-en_GB-20210714-e3bc800.uf2 and got the error message about updating the .mpy so I think that proves that the update was working. I did that with the latest bundle for version 7. The to double check I used adafruit-circuitpython-raspberry_pi_pico-en_GB-7.0.0-alpha.4.uf2 with the same results, shown below.
MIDI moniter

@todbot
Copy link

todbot commented Jul 15, 2021

Just to verify what version CircuitPython you're running, when you go into the REPL and press Ctrl-C what CircuitPython version is printed? It will look something like:

Adafruit CircuitPython 7.0.0-alpha.4-65-ge2888cba6 on 2021-07-13; Adafruit MagTag with ESP32S2

@Grumpy-Mike
Copy link
Author

Nothing happens when I press Ctrl-C, but I am using Thonny. When it connects to the Pico I get this message:-
Adafruit CircuitPython 7.0.0-alpha.4-67-ge3bc800bb on 2021-07-14; Raspberry Pi Pico with rp2040

I also tried other things like:-
Adafruit CircuitPython 7.0.0-alpha.4-15-g22e8a5058 on 2021-07-11; Raspberry Pi Pico with rp2040
with the same effect.

I am normally just running this into the MIDI monitor, but I get the same behavior when I use Garage Band ( even though it dose nothing with a Program Change message) and I have just tried it with Ableton 10.

@Grumpy-Mike
Copy link
Author

Is it the case that you can no longer see this problem with your builds?
I tried build adafruit-circuitpython-raspberry_pi_pico-en_GB-20210718-40b0857.uf2 yesterday and was still getting the same problem. @todbot do you have the exact build you used to get the result you posted?
Thanks

@todbot
Copy link

todbot commented Oct 22, 2021

Hi @Grumpy-Mike,
Apologies for not responding to you specifically. The build I was using was quoted above.
I just did a new test using production CircuitPython 7.0. The code is basically yours quoted above but has a loop to increment the program change.

From what I can tell USB MIDI Program Change works fine on CircuitPython 7 and has worked since the fix tested back in July. Here's a video showing what I'm seeing. It uses MIDI Monitor on MacOS, Ableton Live, and the free MG-1 VST from Cherry Audio. Note that many VSTs don't do Program Change and some require Bank Select commands (which are Control Change). But the MG-1 is simple enough to just do standard PC.

IMG_9066.mp4

Here's the code in the video:

import usb_midi
import adafruit_midi
from adafruit_midi.note_off import NoteOff
from adafruit_midi.note_on import NoteOn
from adafruit_midi.program_change import ProgramChange
import time

midi = adafruit_midi.MIDI(midi_in=usb_midi.ports[0], in_channel=0,
                          midi_out=usb_midi.ports[1], out_channel=0 )
print("MIDI program change test!")
prog_chg_num = 0x07
while True:
    midi.send(NoteOn(65,100))
    time.sleep(0.6)
    midi.send(NoteOff(65,0))
    time.sleep(0.6)
    print("sending program change:", prog_chg_num)
    midi.send(ProgramChange( prog_chg_num ))
    time.sleep(1)
    prog_chg_num = (prog_chg_num + 1 ) % 127

In my opinion, this issue can be closed.

@Grumpy-Mike
Copy link
Author

Grumpy-Mike commented Oct 23, 2021 via email

@todbot
Copy link

todbot commented Oct 23, 2021

Hi @Grumpy-Mike,

A few comments:

  • Please describe how the code.py I included in the above comment is different than the code in my video. (Perhaps you are not seeing my edit? I inadvertently pasted in your original code when I first posted but that was corrected within minutes)
  • The demo I showed was using the QTPy RP2040, which is using the same chip as the Raspberry Pi Pico. In fact, I reflashed my QTPy RP2040 with the Pico build of CircuitPython 7 and the code works identically
  • What is the error you are seeing when you run this demo code? Can you make a quick video that shows what you're experiencing?
  • I recommend using circup to install the latest version of libraries on a CircuitPython device. (Copying files by hand on MacOS can cause problems because of the metadata files MacOS creates, unless you're doing it in Terminal with cp -X) And no need to download the Library Bundle!
  • The choice of editor (Thonny for your, Emacs + tio for me) does not matter. All we're doing is editing a text file on a USB drive

As for your OLED error, CircuitPython is a rapidly-changing platform. APIs are constantly in flux so the kind of error you experienced is pretty common. A quick scan of the docs for the library in question will usually sort out what the changes are. It's frustrating but common in fast-paced open-source projects.

@Grumpy-Mike
Copy link
Author

Grumpy-Mike commented Oct 23, 2021 via email

@Grumpy-Mike
Copy link
Author

Sorry but just realised that my screen dump I sent in the email reply did not seem to make it. Here it is:-
Circuit Pytho 7

@todbot
Copy link

todbot commented Oct 23, 2021

Hi, For completeness' sake, I tried both @Grumpy-Mike's original code and my looping version on a fresh Raspberry Pi Pico running Adafruit CircuitPython 7.0.0 on 2021-09-20; Raspberry Pi Pico with rp2040. Both versions work as expected. The screenshot below is MIDI Monitor showing a run of Mike's original code.

Trying a new Pico sounds like a good idea. It almost seems like you're board is running an old version of CircuitPython or libraries. I periodically do rm -rf /Volumes/CIRCUITPY/lib to remove any random libraries just in case (or in case I copied libraries by hand and didn't use cp -X). I've also had to entirely clean the Pico's flash using flash_nuke.uf2 as described here or here.

Also, for more completeness' sake: which version of MacOS are you running? I've tested on Mac OS 11.6 ("Big Sur") on 2015 Intel MacBook Pro and a 2020 M1 MacBook Pro. Similarly, what version of MIDI Monitor are you running? Current version is 1.4.1.

Screen Shot 2021-10-23 at 3 47 59p

@Grumpy-Mike
Copy link
Author

OK Got it to work with only a handful of hair to go.

Basically it will not work correctly on my Laptop which is running macOS Mojave V10.14.6 but it does work on my new iMac running macOS Big Sur V11.6, I had not transferred much over to that as i don't want to use it as a hardware development platform. So I had install all the software to it to test this.

In order to get it to work on my iMac I had to upgrade Thonny from 3.3.3 to 3.3.13 tried the same on my laptop with no effect.

Also upgraded MIDI monitor to latest but made no difference.
I nuked the flash and installed Circuit Python and the Library files on a Raspberry Pi. Again to no effect on the laptop.

macOS Mojave is the last Mac OS that supports 32 bit applications so I have to keep the laptop at that otherwise too much stuff will stop working.

It is odd that so much else in circuit python seems to work.
Dose this give any clue as to what is wrong, or do you just want to sack it of and add in the documentation that MIDI through USB dosn't work on older Mac OS?

@todbot
Copy link

todbot commented Oct 24, 2021

Thank you, @Grumpy-Mike. This is a good result! I was able to recreate your issue on an old Mac Mini running MacOS 10.13 ("High Sierra").
Screen Shot 2021-10-24 at 11 59 48a

I discovered the issue also exists in Arduino when using the Adafruit_TinyUSB_Arduino library on current MacOS. See the above linked issue and this gist.

CircuitPython, the Adafruit_TinyUSB_Arduino library, and many other projects use TinyUSB (it's really good). I think this issue is in TinyUSB, but I don't have a good test case yet for just TinyUSB, only via CP or Arduino. I'm looking into that now but that will take longer.

Suffice to say, I think this issue is not with this library ("Adafruit_CircuitPython_MIDI") nor with CircuitPython directly, but both are affected by it.

@todbot
Copy link

todbot commented Oct 25, 2021

After further investigation, I've been able to replicate this issue on recent MacOS in both CircuitPython and in a TinyUSB C-only program. In CircuitPython, first let's eliminate this library entirely and send raw MIDI messages like this:

import usb_midi
import time

midi_out = usb_midi.ports[1]
midi_ch = 0

while True:
    note_on = bytearray( (0x90 | midi_ch, 65, 100) )
    midi_out.write( note_on, len(note_on) )
    time.sleep(0.6)
    
    note_off = bytearray( (0x80 | midi_ch, 65, 23) )
    midi_out.write( note_off, len(note_off) )
    time.sleep(0.6)
    
    prog_chg = bytearray( (0xC0 | midi_ch, 7) )
    midi_out.write( prog_chg, len(prog_chg) )
    time.sleep(1.6)

This works fine and all three MIDI messages are sent correctly. But if you change it to single-byte writes like this:

import usb_midi
import time
midi_out = usb_midi.ports[1]
midi_ch = 0
while True:
    note_on = bytearray( (0x90 | midi_ch, 65, 100) )
    midi_out.write( note_on[0:1], 1)
    midi_out.write( note_on[1:2], 1)
    midi_out.write( note_on[2:3], 1)
    time.sleep(0.6)
    
    note_off = bytearray( (0x80 | midi_ch, 65, 23) )
    midi_out.write( note_off[0:1], 1)
    midi_out.write( note_off[1:2], 1)
    midi_out.write( note_off[2:3], 1)
    time.sleep(0.6)

    prog_chg = bytearray( (0xC0 | midi_ch, 7) )
    midi_out.write( prog_chg[0:1], 1 )
    midi_out.write( prog_chg[1:2], 1 )
    time.sleep(1.6)

Then the Note On & Note Off messages are sent correctly, but the Program Change message is corrupted (first by 0x00 instead of 0xC0). Sending a byte a time is what the MIDI Library in Arduino does, which is why it is having the same issue.

@Grumpy-Mike
Copy link
Author

Grumpy-Mike commented Oct 25, 2021

Thanks Tod, I did try that last example of sending the bytes one at a time when I was investigating the problem and got exactly the same as you.
By the way did you know that this problem applies to MIDI message with the first number 0xD. So that is the little used Channel Pressure (After-touch). Again the single byte at a time method produces 0x00 as the first byte.
I did try the method you say works
`prog_chg = bytearray( (0xC0 | midi_ch, 7) )

midi_out.write( prog_chg, len(prog_chg) )`

But that didn't work on my laptop. Nether did using 0xD0 but 0xE0 (with an extra byte of data) did produce the correct Pitch Wheel message.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants