portmidi weirdness

117 views
Skip to first unread message

Jason Levine

unread,
Nov 7, 2015, 6:36:35 AM11/7/15
to extemp...@googlegroups.com
Hi all,

Some bizarre behaviours from portmidi.xtm.

First, as I mentioned in Roberto's thread, the scheme helper functions in libs/external/portmidi.xtm are NOT evaluated when (sys:load "libs/external/portmidi.xtm") is evaluated.  A simpler workaround is to evaluate the following code manually after the (sys:load) call

;; scheme helper functions
(define *midi-note-off*     #x80) ;; args: key, velocity
(define *midi-note-on*      #x90) ;; args: key, velocity
(define *midi-poly-touch*   #xA0) ;; args: key, touch
(define *midi-ctrl*         #xB0) ;; args: controller, value
(define *midi-ch-program*   #xC0) ;; args: value, ignored
(define *midi-touch*        #xD0) ;; args: value, ignored
(define *midi-bend*         #xE0) ;; args: lsb (7 bits), msb (7 bits)

Second, while (pm_print_devices) successfully recognizes my AU loaded in AULab:
Portmidi: found 1 devices
---- device 0  ----
<PmDeviceInfo: interface=CoreMIDI name=Alchemy I/O?:O>


Once I connect to device 0 and play-midi-note,  there is no midi message received by AULab. (http://mac.softpedia.com/get/Audio/AU-Lab.shtml)

However! To test if the problem was Extempore or AULab, I downloaded Virtual Midi Piano Keyboard (http://sourceforge.net/projects/vmpk/?source=typ_redirect)

And here is the weird part.  If I connect VMPK to device 0 I can play my AU loaded into AULab.  But if VMPK is device 0 and I connect Extempore to device 0, I can play the synth that comes with VMPK.  So as a workaround I have been connecting Extempore to VMPK and then VMPK to AULab.  However, this means I can only have one instrument. Ideally I can load multiple instances of the same AU and then make closures for *midi-out1* *midi-out2* *midi-out3* etc etc.

So it seems to me that there is more than one way to connect to a MIDI device?  Or more than one way to send messages? And Extempore portmidi is using a different method than VMPK?

Let me know your thoughts! I would love to get my AUs rocking again for my upcoming HK show!

Thanks!
--  
Jason Levine
new media performer + creative coder

Roberto Arletti

unread,
Nov 7, 2015, 10:02:04 AM11/7/15
to Extempore
Hi Jason ,I ask for a little help ,   I do not understand exactly where to place the appropriate device index arguments for connect Extempore to the device ,
in the portmidi example there is this :

;; we'll just create one input and one output device (midi_in and
;; midi_out), but by passing the appropriate device index arguments to
;; pm_create_input_stream and pm_create_output_stream you can create
;; as many as you like

(bind-val midi_in PmStream* (pm_create_input_stream (Pm_GetDefaultInputDeviceID)))  
(bind-val midi_out PmStream* (pm_create_output_stream (Pm_GetDefaultOutputDeviceID)))

where exactly in (bind-val midi_out PmStream* (pm_create_output_stream (Pm_GetDefaultOutputDeviceID))) i passing the device index  ????

thanks

Roberto

Jason Levine

unread,
Nov 7, 2015, 10:27:48 AM11/7/15
to extemp...@googlegroups.com
Hey Roberto,

(bind-val midi_out PmStream* (pm_create_output_stream (Pm_GetDefaultOutputDeviceID))

As I understand it, Pm_GetDefaultOutputDeviceID will give you a pointer to your default device id.  If you haven't set a default device, then device 0 will be your default. According to the comments in libs/external/portmidi.xtm, to set a different default you need to either use a utility called pmdefaults which comes with portmidi, OR you can manually edit a this file: /Users/$NAME/Library/Preferences/com.apple.java.util.prefs.plist

I know, this is pretty crazy and low-level!  All I want is a macro to do something like (open-midi-device 2). But this is not possible yet.  So what I'm doing is only connecting the MIDI device that I want to use so that its default.

Hope that helps! I'm just learning all this right now!

Jason


--
You received this message because you are subscribed to the Google Groups "Extempore" group.
To unsubscribe from this group and stop receiving emails from it, send an email to extemporelan...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Jason Levine

unread,
Nov 7, 2015, 10:59:40 AM11/7/15
to extemp...@googlegroups.com
Ok,  so it looks like you CAN choose the device, but it has to be an i32.  integers are by default i64 in extempore so 
(bind-val midi_out PmStream* (pm_create_output_stream 1) 

will give the error:
Type Error calling pm_create_output_stream, got i64, was expecting i32 

Which is weird... because in the docs it states:
the default integer size is i64, so int literals in the code will be interpreted as i64, unless the function signature suggests otherwise e.g. if the signature is [double,i32]*, then a (single) int literal argument will be read as an i32.

And the signature of  pm_create_output_stream is
(lambda (dev)

It has no signature!

So I changed it to:
(bind-func pm_create_output_stream
  (lambda (dev:i32) 

Which got rid of the type error, but now my statement
 (bind-val midi_out PmStream* (pm_create_output_stream 1) 

doesn't evaluate.  No message in the console or terminal, but when I try to get a handle like this:
(bind-func get_midi_out
  (lambda ()
    midi_out))

I get:
Compiler Error cannot find symbol  midi_out  

So I'm stuck! But it should be possible. 

Roberto Arletti

unread,
Nov 7, 2015, 3:48:52 PM11/7/15
to Extempore
Hi Jason , it works !!! 

I 'put this setting :  (bind-val midi_out PmStream* (pm_create_output_stream 3))

i manually evaluate : 
(define *midi-note-off*     #x80) ;; args: key, velocity
(define *midi-note-on*      #x90) ;; args: key, velocity
(define *midi-poly-touch*   #xA0) ;; args: key, touch
(define *midi-ctrl*         #xB0) ;; args: controller, value
(define *midi-ch-program*   #xC0) ;; args: value, ignored
(define *midi-touch*        #xD0) ;; args: value, ignored
(define *midi-bend*         #xE0) ;; args: lsb (7 bits), msb (7 bits)

and with (play-midi-note (now) *midi-out* (random 60 72) (random 60 80) 88100)  extempore play Reaktor.

But attention i am on windows 7.



Ben Swift

unread,
Nov 7, 2015, 7:07:47 PM11/7/15
to extemp...@googlegroups.com
Hi guys

Sorry to hear you're having trouble with the midi stuff. The MIDI stuff
is a bit in flux at the moment (I've pushed some fixes to master in the
last week which will solve some of the problems you're having, but I'm
sure there are still kinks to work out). I know Andy has been doing
some work on the MIDI side of things as well.

Jason, re: your specific issues:

>> (bind-val midi_out PmStream* (pm_create_output_stream 1)
>
>
> will give the error:
>
>> Type Error calling pm_create_output_stream, got i64, was expecting i32
>
>
> Which is weird... because in the docs it states:
>
>> the default integer size is i64, so int literals in the code will be
>> interpreted as i64, unless the function signature suggests otherwise e.g.
>> if the signature is [double,i32]*, then a (single) int literal argument
>> will be read as an i32.

This is still true, but the generics support has gotten a bit more
sophisticated since those docs were written, and so sometimes it's
harder for the type inferencer to figure out what's what. In these
cases, you can append an ":i32" type annotation to an int literal, e.g.

(pm_create_output_stream 1:i32)

>
> And the signature of pm_create_output_stream is
>
>> (lambda (dev)
>
>
> It has no signature!

Well, that's not true - every closure has a signature (as you can see
from the log "Compiled: pm_create_output_stream >>> [i8*,i32]*") - it
just doesn't have an explicit type annotation on the dev argument (the
type inferencer must be able to figure it out from somewhere else).

> So I changed it to:
>
>> (bind-func pm_create_output_stream
>> (lambda (dev:i32)
>
>
> Which got rid of the type error, but now my statement
>
>> (bind-val midi_out PmStream* (pm_create_output_stream 1)
>
>
> doesn't evaluate. No message in the console or terminal, but when I try to
> get a handle like this:
>
>> (bind-func get_midi_out
>> (lambda ()
>> midi_out))
>
>
> I get:
>
>> Compiler Error cannot find symbol midi_out
>
>
> So I'm stuck! But it should be possible.

A few of these issues are to do with scheme/xtlang interop. Design-wise,
the MIDI api needs to be fully accessible from Scheme, and that's the
effect of some of the changes which have landed in master. Maybe we
should do a small point release this coming week.

If you need it for a gig soon, maybe just using the RTmidi stuff (which
presumably was working) is the way to go. The RTmidi stuff had some
issues as well, which is why moving to portmidi seemed like an appealing
choice - but there are clearly some teething problems. Sorry about
that, guys.

Love,
Ben :)

> On Sat, Nov 7, 2015 at 11:27 PM, Jason Levine <jasonbe...@gmail.com>
> wrote:
>
>> Hey Roberto,
>>
>> (bind-val midi_out PmStream* (pm_create_output_stream (
>> *Pm_GetDefaultOutputDeviceID*)))
>>
>> As I understand it,* Pm_GetDefaultOutputDeviceID* will give you a pointer
>> to your default device id. If you haven't set a default device, then
>> device 0 will be your default. According to the comments in
>> *libs/external/portmidi.xtm*, to set a different default you need to
>> *https://www.behance.net/jasonlevine <https://www.behance.net/jasonlevine>*
>>
>>
>
>
> --
> Jason Levine
> new media performer + creative coder
> *https://www.behance.net/jasonlevine <https://www.behance.net/jasonlevine>*

Andrew Sorensen

unread,
Nov 7, 2015, 8:16:58 PM11/7/15
to extemp...@googlegroups.com
Hi Jason,

Your logic is fine, I suspect that you've tripped yourself up with typo's along the way

(bind-val midi_out PmStream* (pm_create_output_stream 1))

should work fine - no need for 1:i32 (although it doesn't hurt).  Notice that I have a parenthesis on the end that you are missing in your pasted example??

Ok,  so it looks like you CAN choose the device, but it has to be an i32.  integers are by default i64 in extempore so 
(bind-val midi_out PmStream* (pm_create_output_stream 1) 

 Writing a "getter" like you have done for interaction between xtlang and scheme is a good approach.


(bind-func get_midi_out
  (lambda ()
    midi_out))


This will fail if you don't have midi_out bound correctly (which if you were missing a closing parenthesis would be the case) - but once bound your code will compile just fine.

Andrew Brown has been hitting portmidi pretty hard this week (successfully) and I'm sure he would be happy to post some infrastructure code for you to take a look at.  However, as far as I can see, once the typos are under control you will already be well on your way ;)

As Ben mentioned there have been a few changes to libs/external/portmidi.xtm since 0.6.0.  As portmidi is a reasonably small library, my suggestion is to simply grab the latest copy of libs/external/portmidi.xtm from github and manually copy it into libs/external/portmidi.xtm on your machine.  Then simply remove the file libs/aot-cache/portmidi.xtm - which will force the use of libs/external/portmidi.xtm instead of the precompiled version.

Cheers,
Andrew.
 

algomusic

unread,
Nov 7, 2015, 9:19:42 PM11/7/15
to Extempore
Hi Jason,

I have also been us AU Lab as a host with Extempore. I have found that with a combination of Xtm 0.6 (port midi) and OS X 10.11 that AU Lab has been a bit flakey. However, I find that Reaper seems much more solid as a host. You may like to try it.

Andrew B.

Jason Levine

unread,
Nov 8, 2015, 11:40:46 AM11/8/15
to extemp...@googlegroups.com
However, as far as I can see, once the typos are under control you will already be well on your way ;)

<Hangs head in shame> 

Ok, so now I've switched to Jux for AU hosting ( http://ju-x.com/Hosting%20AU.html )

However to be able to play different AUs on the same device I need to be able to send midi messages on different channels.  How do I achieve this?

I tried the homo-play-midi-note function  like this:
(homo-play-midi-note (now) *midi-out* (random 50 62) (random 60 90) 44100 1)

and like this 
(homo-play-midi-note (now) *midi-out* (random 50 62) (random 60 90) 44100 (PmChannel 1))

and both times got this

Error in scheme->xtlang wrapper: check the arg arity and types

So I saw this comment in portmidi.xtm:
;;     i.e. to set receive only input on channel 1, call with
;;     Pm_SetChannelMask(Pm_Channel(1));
 
And thought "maybe it will work for output as well?"  Unfortunately:
(Pm_SetChannelMask (Pm_Channel 1 ))

gave me:
eval: unbound variable: Pm_SetChannelMask

And I checked my parens twice! Thoughts?

Thanks!

--
You received this message because you are subscribed to the Google Groups "Extempore" group.
To unsubscribe from this group and stop receiving emails from it, send an email to extemporelan...@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Ben Swift

unread,
Nov 8, 2015, 5:30:17 PM11/8/15
to extemp...@googlegroups.com
Hi Jason

If you want to try and build homebrew from head

brew install --HEAD extempore

then you'll be using the very freshest portmidi lib, with all my recent
updates. Are you able to give that a go? See if these issues are fixed -
they may not all be, but we'll at least be singing from the same
songsheet.

Cheers mate,
Ben

Jason Levine <jasonbe...@gmail.com> writes:

>>
>> However, as far as I can see, once the typos are under control you will
>> already be well on your way ;)
>
>
> *<Hangs head in shame> *
>>> received by *AULab*. (http://mac.softpedia.com/get/Audio/AU-Lab.shtml)
>>>
>>> However! To test if the problem was Extempore or AULab, I downloaded*
>>> Virtual Midi Piano Keyboard* (
>>> http://sourceforge.net/projects/vmpk/?source=typ_redirect)
>>>
>>> And here is the weird part. If I connect VMPK to device 0 I can play my
>>> AU loaded into AULab. But if VMPK is device 0 and I connect Extempore to
>>> device 0, I can play the synth that comes with VMPK. So as a workaround I
>>> have been connecting Extempore to VMPK and then VMPK to AULab. However,
>>> this means I can only have one instrument. Ideally I can load multiple
>>> instances of the same AU and then make closures for *midi-out1* *midi-out2*
>>> *midi-out3* etc etc.
>>>
>>> So it seems to me that there is more than one way to connect to a MIDI
>>> device? Or more than one way to send messages? And Extempore portmidi is
>>> using a different method than VMPK?
>>>
>>> Let me know your thoughts! I would love to get my AUs rocking again for
>>> my upcoming HK show!
>>>
>>> Thanks!
>>> --
>>> Jason Levine
>>> new media performer + creative coder
>>> --
>> You received this message because you are subscribed to the Google Groups
>> "Extempore" group.
>> To unsubscribe from this group and stop receiving emails from it, send an
>> email to extemporelan...@googlegroups.com.
>> For more options, visit https://groups.google.com/d/optout.
>>
>
>
>
> --
> Jason Levine
> new media performer + creative coder

Jason Levine

unread,
Nov 8, 2015, 7:55:14 PM11/8/15
to extemp...@googlegroups.com
Jasons-MacBook-Pro:bin jasonlevine$ brew install --HEAD extempore
Warning: benswift/extempore/extempore-0.6.0 already installed

Do I need to uninstall my current installation first? Shouldn't Brew see that you've made changes since  I last downloaded?

algomusic

unread,
Nov 10, 2015, 2:23:10 AM11/10/15
to Extempore
Hi Jason,

I find it easiest to uninstall then reinstall extempore when changing branches. Ben's suggestion of installing --HEAD works fine for me. However, this alone does not install portmidi or the other externals but if you do a "brew install extempore --with-extended" first these get added and are not deleted by "brew uninstall extempore" so you can then install --HEAD. It would be great to be able to do "brew install --HEAD extempore --with-extended" but this seems to hang at the stage of "make aot" (Ben?).

Having installed MIDI should be sweet. As Andrew S. mentioned we hit Port MIDI pretty hard last week and below is code for managing incoming MIDI data in xtlang - an elaboration on the port midi example in 0.6.0. Adjust the input and output stream numbers to suit your setup.


(sys:load "libs/external/portmidi.xtm")
(sys:load "libs/core/xthread.xtm")

(pm_initialize)
(pm_print_devices)

(bind-val midi_in PmStream* (pm_create_input_stream 2))
(bind-func get_midi_in (lambda () midi_in))

(bind-val midi_out PmStream* (pm_create_output_stream 3))
(bind-func get_midi_out (lambda () midi_out))

(bind-func midi_input_handler
  (let ((input_buffer:PmEvent* (zalloc PM_MIDI_BUFFER_SIZE))
        (read_count 0:i32)
        (i:i32 0))
    (lambda (input_stream:PmStream*)
      (set! read_count (Pm_Read input_stream input_buffer PM_MIDI_BUFFER_SIZE))
      (if (> read_count 0)
        (dotimes (i read_count)
                    (let ((mess (Pm_Event_Message (pref-ptr input_buffer i)))
                (type (Pm_Message_Type mess))
                (chan (Pm_Message_Channel mess))
                      (a (Pm_Message_Data1 mess))
                      (b (Pm_Message_Data2 mess)))
              (println "Chan:" chan "Type:" type "Data1:" a "Data2:" b)
              (pm_send (get_midi_out) type chan a b)))))))

(bind-func midi_read_loop
  (let ((run 1))
    (lambda (input_s)
      (spawn (lambda ()
        (while (> run 0)
          (midi_input_handler input_s)
          (thread_sleep 0 (dtoi64 (/ 1.0e9 100.0))))
        (println "Exiting midi-read-loop")))
      void)))

(midi_read_loop (get_midi_in))
;(midi_read_loop.run 0) ;; to stop MIDI polling

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

Following this pattern, then recursive callbacks in Scheme can look pretty normal - use (get_midi_out) to access the output stream.

(define music
  (lambda (beat)
    (homo-play-midi-note (*metro* beat) (get_midi_out) (random 48 85) (random 60 120) (* 20000 (random)) 0)
    (callback (*metro* (+ beat 1/4)) 'music (+ beat 1/2))))

(music (*metro* 'get-beat 1))

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

One gotcha is that this approach breaks the "mplay" macro but this rectified thus:

; override previous mplay and add an additional argument for channel
; args: (optional-beat-offset) device pitch velocity duration-in-beats channel
(define-macro (mplay . args)
  (if (= (length args) 5)
      `(play-midi-note (*metro* beat) ,(car args)
                       (real->integer ,(cadr args))
                       (real->integer ,(caddr args))
                       (*metro* 'dur ,(car (cdddr args))) ,(cadr (cdddr args)))
      `(play-midi-note (*metro* (+ beat ,(car args))) ,(cadr args)
                       (real->integer ,(caddr args))
                       (real->integer ,(car (cdddr args)))
                       (*metro* 'dur ,(cadr (cdddr args))) ,(caddr (cdddr args)))))

Jason Levine

unread,
Nov 10, 2015, 2:29:59 AM11/10/15
to extemp...@googlegroups.com
This time I got:

Jasons-MacBook-Pro:bin jasonlevine$ brew install --HEAD extempore --with-extended

==> Installing extempore from benswift/homebrew-extempore

==> Cloning https://github.com/digego/extempore.git

Updating /Library/Caches/Homebrew/extempore--git

==> Checking out branch master

==> cmake -DIN_TREE=OFF . -DCMAKE_C_FLAGS_RELEASE= -DCMAKE_CXX_FLAGS_RELEASE= -DCM

==> make install

==> AOT-compiling the Extempore standard library.  This may take several minutes..

==> make aot

Last 15 lines from /Users/jasonlevine/Library/Logs/Homebrew/extempore/03.make:

Compiled:  notch_mc_c >>> [[float,i64,float,float,float]*,i64]* zone size: 8192 (default)

Compiled:  peak_c >>> [[float,float,float,float]*]* zone size: 8192 (default)

Compiled:  peak_mc_c >>> [[float,i64,float,float,float]*,i64]* zone size: 8192 (default)

Compiled:  lshelf_c >>> [[float,float,float,float,float]*]* zone size: 8192 (default)

Compiled:  lshelf_mc_c >>> [[float,i64,float,float,float,float]*,i64]* zone size: 8192 (default)

Compiled:  hshelf_c >>> [[float,float,float,float,float]*]* zone size: 8192 (default)

Compiled:  hshelf_mc_c >>> [[float,i64,float,float,float,float]*,i64]* zone size: 8192 (default)

Compiled:  skf_c >>> [[float,float,float,float]*]* zone size: 8192 (default)

Compiled:  skf_mc_c >>> [[float,i64,float,float,float]*,i64]* zone size: 8192 (default)

Compiled:  granulator_c >>> [[float,i64,i64,float]*,i64]* zone size: 8192 (default)

Compiler Error cannot find symbol  hermite_interp 

make[3]: *** [CMakeFiles/aot_audio_dsp] Error 2

make[2]: *** [CMakeFiles/aot_audio_dsp.dir/all] Error 2

make[1]: *** [CMakeFiles/aot.dir/rule] Error 2

make: *** [aot] Error 2


READ THIS: https://git.io/brew-troubleshooting

If reporting this issue please do so at (not Homebrew/homebrew):

  https://github.com/benswift/homebrew-extempore/issues

Andrew Sorensen

unread,
Nov 10, 2015, 2:42:38 AM11/10/15
to extemp...@googlegroups.com
Jason,  sorry, that's my fault :(

Try again now.  Worth it as you'll get a new granulator to play with ;)

Jason Levine

unread,
Nov 10, 2015, 2:47:04 AM11/10/15
to extemp...@googlegroups.com
Works!

Jason Levine

unread,
Nov 10, 2015, 2:59:16 AM11/10/15
to extemp...@googlegroups.com
lol been listening to this loop for 10 minutes now

(define midi-loop
  (lambda (beat dur)
    (mplay *midi-out* (random (list 60 60 60 67 70 74 75)) (random 60 80) dur)
    (callback (*metro* (+ beat (* .5 dur))) 'midi-loop (+ beat dur) dur)))

Ben Swift

unread,
Nov 10, 2015, 3:45:27 AM11/10/15
to extemp...@googlegroups.com
Cool, glad you like it. I must confess it was more of a test loop than a
considered work of art, but sometimes it works better that way anyway :)

Cheers,

Roberto Arletti

unread,
Nov 27, 2015, 6:45:29 AM11/27/15
to Extempore
Hi all , last week I really enjoyed playing with extempore , and piano sound from kontakt library , but I have a question :
in the portimidi.xtm library there is a function "play-midi-note" where the last argument is the midi channel , this works well ,
and there is a macro "mplay" (that recalls the function "play-midi-note") , in this macro the midi channel is set to 0 ;
it's possible to choose the midi channel ?

thank you

Jason Levine

unread,
Nov 27, 2015, 9:08:38 AM11/27/15
to extemp...@googlegroups.com
Hi Roberto,

The mplay macro wasn't updated to support channels. Here's a mplay macro with channels:

;; A midi-specific play macro
(impc:aot:do-or-emit
 (define-macro (mplay . args)
   (if (symbol? (car args))

       `(play-midi-note (*metro* beat) ,(car args)
                        (real->integer ,(cadr args))
                        (real->integer ,(caddr args))
                        (*metro* 'dur ,(car (cdddr args))) 
                        (real->integer , (car (cddddr args))))

       `(play-midi-note (*metro* (+ beat ,(car args))) ,(cadr args)
                        (real->integer ,(caddr args))
                        (real->integer ,(car (cdddr args)))
                        (*metro* 'dur ,(cadr (cdddr args))) 
                        (real->integer , (cadr (cddddr args)))))))


This should replace the current one at the bottom of libs/portmidi.xtm


@Ben @Andrew : I guess I should make a pull request for things like this?  Does that mean I have to abandon the safety and comfort of homebrew and switch to building from source?

--
You received this message because you are subscribed to the Google Groups "Extempore" group.
To unsubscribe from this group and stop receiving emails from it, send an email to extemporelan...@googlegroups.com.

For more options, visit https://groups.google.com/d/optout.

Ben Swift

unread,
Nov 27, 2015, 5:21:28 PM11/27/15
to extemp...@googlegroups.com
Hi Jason

Thanks for helping out.

Re: pull requests, I'm happy to just keep merging the changes myself,
but if you want to win all the internet points (and have github show you
as a contributor - how exciting) then I can help you through that
process.

Building from source isn't really that tricky these days, especially
because you can still use homebrew to manage all the external libs
(portmidi, etc.)

Cheers,
Ben

Jason Levine <jasonbe...@gmail.com> writes:

Jason Levine

unread,
Nov 28, 2015, 8:49:27 AM11/28/15
to extemp...@googlegroups.com
Ok, lets go down that road next time I make a useful change :)

Ben Swift

unread,
Nov 28, 2015, 9:26:16 PM11/28/15
to extemp...@googlegroups.com
Sure mate :)
Reply all
Reply to author
Forward
0 new messages