rakyll / portmidi Goto Github PK
View Code? Open in Web Editor NEWGo bindings for libportmidi
License: Apache License 2.0
Go bindings for libportmidi
License: Apache License 2.0
Running the following sample code:
`
package main
import(
"log"
"time"
"github.com/rakyll/portmidi"
)
func main() {
portmidi.Initialize()
out, err := portmidi.NewOutputStream(1, 1024, 0)
if err != nil {
log.Fatal(err)
}
// note on events to play C major chord
out.WriteShort(0x90, 60, 100)
out.WriteShort(0x90, 64, 100)
out.WriteShort(0x90, 67, 100)
// notes will be sustained for 2 seconds
time.Sleep(2 * time.Second)
// note off events
out.WriteShort(0x80, 60, 100)
out.WriteShort(0x80, 64, 100)
out.WriteShort(0x80, 67, 100)
out.Close()
}
`
I see a flash on my USB midi when the device opens, and another when it closes, but no note events are sent when it writes them. Also not seeing any kind of error.
If I use the Mac OS midi tester, then I can see note events been sent to my usb midi fine so I don't think it's a device issue.
Is there any debugging I can turn on or way of investigating further?
Golang 1.9
macOS 10.12.6 Sierra
Hello, I ran brew install portmidi
and that seemed to go OK. I then ran go get github.com/rakyll/portmidi
- again, no cause for alarm.
I then cd'd to the rakyll/portmidi
folder and ran go test
. I received the following messages:
carlca ~/code/go/src/github.com/rakyll/portmidi master go test ✓ 10:26:17 - 05.09.2017
# testmain
/usr/local/go/pkg/tool/darwin_amd64/link: running clang failed: exit status 1
duplicate symbol __cgo_d8e63e9abae4_Cfunc_Pm_Abort in:
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000000.o
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000002.o
duplicate symbol __cgo_d8e63e9abae4_Cfunc_Pm_Close in:
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000000.o
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000002.o
duplicate symbol __cgo_d8e63e9abae4_Cfunc_Pm_CountDevices in:
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000000.o
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000002.o
duplicate symbol __cgo_d8e63e9abae4_Cfunc_Pm_GetDefaultInputDeviceID in:
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000000.o
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000002.o
duplicate symbol __cgo_d8e63e9abae4_Cfunc_Pm_GetDefaultOutputDeviceID in:
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000000.o
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000002.o
duplicate symbol __cgo_d8e63e9abae4_Cfunc_Pm_GetDeviceInfo in:
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000000.o
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000002.o
duplicate symbol __cgo_d8e63e9abae4_Cfunc_Pm_GetErrorText in:
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000000.o
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000002.o
duplicate symbol __cgo_d8e63e9abae4_Cfunc_Pm_Initialize in:
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000000.o
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000002.o
duplicate symbol __cgo_d8e63e9abae4_Cfunc_Pm_OpenInput in:
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000000.o
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000002.o
duplicate symbol __cgo_d8e63e9abae4_Cfunc_Pm_OpenOutput in:
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000000.o
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000002.o
duplicate symbol __cgo_d8e63e9abae4_Cfunc_Pm_Poll in:
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000000.o
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000002.o
duplicate symbol __cgo_d8e63e9abae4_Cfunc_Pm_Read in:
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000000.o
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000002.o
duplicate symbol __cgo_d8e63e9abae4_Cfunc_Pm_SetChannelMask in:
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000000.o
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000002.o
duplicate symbol __cgo_d8e63e9abae4_Cfunc_Pm_Terminate in:
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000000.o
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000002.o
duplicate symbol __cgo_d8e63e9abae4_Cfunc_Pm_Write in:
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000000.o
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000002.o
duplicate symbol __cgo_d8e63e9abae4_Cfunc_Pm_WriteSysEx in:
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000000.o
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000002.o
duplicate symbol __cgo_d8e63e9abae4_Cfunc_Pt_Start in:
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000000.o
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000002.o
duplicate symbol __cgo_d8e63e9abae4_Cfunc_Pt_Stop in:
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000000.o
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000002.o
duplicate symbol __cgo_d8e63e9abae4_Cfunc_Pt_Time in:
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000000.o
/var/folders/1d/1rqcnlq51zd4j_vqhp1kv3540000gp/T/go-link-946891097/000002.o
ld: 19 duplicate symbols for architecture x86_64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
FAIL _/Users/carlca/code/go/src/github.com/rakyll/portmidi [build failed]
Any ideas what the problem could be? Cheers, Carl.
I have included the
#include <portmidi.h>
#include <porttime.h>
libraries and everything compiles fine, but I get the following errors on runtime.
# github.com/rakyll/portmidi
could not determine kind of name for C.PmDeviceID
could not determine kind of name for C.PmError
could not determine kind of name for C.Pm_CountDevices
could not determine kind of name for C.Pm_GetDefaultInputDeviceID
could not determine kind of name for C.Pm_GetDefaultOutputDeviceID
could not determine kind of name for C.Pm_GetDeviceInfo
could not determine kind of name for C.Pm_Initialize
could not determine kind of name for C.Pm_Terminate
could not determine kind of name for C.Pt_Start
could not determine kind of name for C.Pt_Stop
could not determine kind of name for C.Pt_Time
Does anyone have any idea what this can come from?
Sorry if its a noob question, I'm pretty new to Go and thanks for help!
I haven't tried it yet but I've just been reading the code
You define:
type Channel int
So using a filter that's Channel(1) | Channel(3)
will just get me channel 3...
The C version has a macro called Pm_Channel
which is 1 << channel
so I think you probably want to change Channel
to be a function that returns the same in order to generate a bitmask.
I ran into a problem where calling Stream.Close()
causes a crash in my app.
The call was fine when the app was closing down. i.e. when other go routines being killed.
The reason, Stream.Listen()
spawns a go routine with no exit feature, so it will keep polling the Stream even if it has been Closed. This of course results in a memory access violation.
I assume the reason testing hasn't picked this up before is because Steam.Close()
is usually only called on exit.
I am using the following in my project to allow a stream to stop listening.
It's a slight API change adding a stop signal to Stream.Listen()
.
Though it might be a good idea to use the context
package...
func (s *portmidi.Stream) Listen(stop <-chan int) <-chan portmidi.Event {
const pollingInterval = 10 * time.Millisecond
timer := time.NewTimer(pollingInterval)
ch := make(chan portmidi.Event)
go func(s *portmidi.Stream, ch chan portmidi.Event) {
for {
select {
case <-timer.C:
events, err := s.Read(1024)
if err != nil {
timer.Reset(pollingInterval)
continue
}
for i := range events {
ch <- events[i]
}
timer.Reset(pollingInterval)
case <-stop:
timer.Stop()
return
}
}
}(s, ch)
return ch
}
Hi, I hacked on a fork of this library to get Go talking to an older synth over Sysex. The changes I made are breaking, but I was interested in contributing if I can. Perhaps there is a non-breaking way of doing this, or a version of this can be included in a future release?
The diff is here: master...murdinc:master
As far as I can tell, only 3 bytes of the Message are available currently, but I needed the entire message.
// Event represents a MIDI event.
type Event struct {
Timestamp Timestamp
Status int64
Data1 int64
Data2 int64
}
PmEvent Struct
in the portmidi docs:// Event represents a MIDI event.
type Event struct {
Timestamp Timestamp
Message Message
}
// Message represents a 4 byte message
type Message []byte
func (m Message) Status() byte {
status := m[0]
return status
}
func (m Message) Data1() byte {
data1 := m[1]
return data1
}
func (m Message) Data2() byte {
data2 := m[2]
return data2
}
Line 233 in d436cea
s.Read()
can return success but zero messages (ie. nil, nil
). If this happens, the line below causes an array overflow:
return evt[0].SysEx, nil
Example backtrace:
panic: runtime error: index out of range [0] with length 0
goroutine 1 [running]:
github.com/rakyll/portmidi.(*Stream).ReadSysExBytes(...)
/home/fraggle/go/src/github.com/rakyll/portmidi/stream.go:238
main.(*getRegisterCommand).queryRegister(0x8205de8, 0x8ce8008, 0x8ce8488, 0x820e378)
/home/fraggle/sc55ctl/sc55ctl.go:133 +0x2e5
main.(*getRegisterCommand).Execute(0x8205de8, {0x814cbb4, 0x8205c20}, 0x8c962c0, {0x0, 0x0, 0x0})
/home/fraggle/sc55ctl/sc55ctl.go:182 +0x3e2
github.com/google/subcommands.(*Commander).Execute(0x8c96140, {0x814cbb4, 0x8205c20}, {0x0, 0x0, 0x0})
/home/fraggle/go/src/github.com/google/subcommands/subcommands.go:209 +0x294
github.com/google/subcommands.Execute(...)
/home/fraggle/go/src/github.com/google/subcommands/subcommands.go:492
main.main()
/home/fraggle/sc55ctl/sc55ctl.go:310 +0x71b
i see this fairly regularly on linux (via the launchpad package) but haven't yet figured out how to reproduce
fatal error: unexpected signal during runtime execution
[signal SIGSEGV: segmentation violation code=0x2 addr=0x7ff50c021000 pc=0x7ff51ce5ff46]
runtime stack:
runtime.throw(0x631c1f, 0x2a)
/usr/lib/go/src/runtime/panic.go:566 +0x95
runtime.sigpanic()
/usr/lib/go/src/runtime/sigpanic_unix.go:12 +0x2cc
goroutine 35 [syscall, locked to thread]:
runtime.cgocall(0x5bd000, 0xc420216c78, 0x0)
/usr/lib/go/src/runtime/cgocall.go:131 +0x110 fp=0xc420216c30 sp=0xc420216bf0
github.com/scgolang/beats/vendor/github.com/rakyll/portmidi._Cfunc_Pm_Write(0x7ff50c000bf0, 0xc42016e4a0, 0x1, 0x0)
??:0 +0x4d fp=0xc420216c78 sp=0xc420216c30
github.com/scgolang/beats/vendor/github.com/rakyll/portmidi.(*Stream).Write(0xc4201fc4d0, 0xc420216cf8, 0x1, 0x1, 0x0, 0x0)
/home/brian/go/src/github.com/scgolang/beats/vendor/github.com/rakyll/portmidi/stream.go:121 +0x11d fp=0xc420216cc8 sp=0xc420216c78
github.com/scgolang/beats/vendor/github.com/rakyll/portmidi.(*Stream).WriteShort(0xc4201fc4d0, 0x90, 0x54, 0xc, 0x0, 0x0)
/home/brian/go/src/github.com/scgolang/beats/vendor/github.com/rakyll/portmidi/stream.go:132 +0x9b fp=0xc420216d28 sp=0xc420216cc8
package main
import (
"fmt"
"log"
"github.com/rakyll/portmidi"
)
func main() {
in, err := portmidi.NewInputStream(portmidi.DefaultInputDeviceID(), 1024)
if err != nil {
log.Fatal(err)
}
msg, err := in.Read(1024)
if err != nil {
log.Fatal(err)
}
for i, b := range msg {
fmt.Printf("SysEx message byte %d = %02x\n", i, b)
}
}
Hi.
Thank you for writing this. I am playing around and have a question. I can send output using WriteShort, and then sleep until sending the next event.
But, what I really have is a []portmidi.Event, which already include timestamp information. It seems like I should be able to just call:
out, err := portmidi.NewOutputStream(output, 1024, 0)
out.Write(events) # events is []portmidi.Event recorded just before this call.
and have it play. But the timing isn't respected, so everything goes very quickly.
I tried setting the latency in the NewOutputStream call to various values (based on the C code, that's the only time that timestamps are actually used), but that doesn't seem to work. (I thought that setting it to portmidi.Time() would offset it correctly from the reads I did just before...)
Do you know how I can pass in a list of Events and just have them played with the timing information honored? It seems silly that I would have to do that in my code :)
Thank you
Dan
Hi
I seem to have an issue using in.Listen(). It loses key presses. Often I can press just one key, but only see the key down event, not the key up.
Have you seen this behavior?
Thanks
Dan
Hi Jaana,
I am writing to you, since you worked on MIDI and I wrote a rather complete library in the last weeks that should serve as a toolkit for creating MIDI based applications.
I would like to invite to help out, making it a common standard for MIDI with Go, by providing you insight and expertise.
I spend some effort to have a common API for live and SMF messages/events.
A second major priority was to make lots of mostly independent small packages to allow
small devices to just use what they need.
The other top priority is to make the API stable as in: no breakage.
Anyway I created the github group "gomidi" and already invited you -
would be glad to have you on board.
The core repo is:
https://github.com/gomidi/midi
and there are two simple applications
https://github.com/gomidi/midispy
and
https://github.com/gomidi/smfimage
making use of the core.
Where you could help out:
I did not announce the library yet, since I first wanted to get some feedback from developers that delved into the MIDI standard.... ;-)
What do you think?
Looking forward to hear from you.
Cheers,
Benny
(github.com/metakeule)
This is my golang code:
package main
import fmt "fmt" // 本包实现了格式化输入输出
import portmidi "./portmidi"
func main() {
fmt.Printf("Hello, world;\n")
var errorid = portmidi.Initialize()
fmt.Printf("Initialize:%d \n", errorid)
var deviceIdnum = portmidi.CountDevices() // returns the number of MIDI devices
fmt.Printf("CountDevices:%d \n", deviceIdnum)
portmidi.Terminate()
}
"deviceIdnum" is always "0".
I have try c++ code:
#include "portmidi.h"
int _tmain(int argc, _TCHAR* argv[])
{
PmError error = Pm_Initialize();
if(error != pmNoError) {
const char * errorstr = Pm_GetErrorText(error);
return error;
}
int deviceCount = Pm_CountDevices();
return Pm_Terminate();
}
"deviceCount" is 24.
Where am I wrong?
Thanks for your time.
Can you recommend a guide? I can't seem to figure out how to install it on OSX. I would really like to use this launchpad library to make instruments.
On Macos 10.14.6 (Mojave)
go 1.14.1
gitlab.com/gomidi/portmididrv v0.6.0
portmidi installed via homebrew - version 217_2
This looks very similar to #36 . I tried setting GODEBUG as described there, but don't see anything in the log. I've been successfully using the library for some prototyping, but now get this seg fault whenever my app processes a quick stream of events (each inbound event triggers a corresponding outbound event and soon after the first in the storm, the app crashes). If I break the cycle (process inbound events, but don't immediately send an outbound event in response - or just send a bunch of outbound events), everything works fine. Could there be some problem with attempting to write at same time the read listener is still processing an inbound event?
Help?
[signal SIGSEGV: segmentation violation code=0x1 addr=0x8 pc=0x7fff4d85068b]
runtime stack:
runtime.throw(0x151c3e9, 0x2a)
/usr/local/go/src/runtime/panic.go:1114 +0x72
runtime.sigpanic()
/usr/local/go/src/runtime/signal_unix.go:679 +0x46a
goroutine 1789 [syscall]:
runtime.cgocall(0x10025e0, 0xc00070ec88, 0xc00031e300)
/usr/local/go/src/runtime/cgocall.go:133 +0x5b fp=0xc00070ec58 sp=0xc00070ec20 pc=0x100616b
github.com/rakyll/portmidi._Cfunc_Pm_Write(0x68016b0, 0xc00051ccc8, 0x1, 0x0)
_cgo_gotypes.go:302 +0x4d fp=0xc00070ec88 sp=0xc00070ec58 pc=0x11d8c4d
github.com/rakyll/portmidi.(*Stream).Write.func1(0xc000052140, 0xc00051ccc8, 0x1, 0x1, 0x1, 0xc00070ed00)
/Users/tynor/go/pkg/mod/github.com/rakyll/[email protected]/stream.go:120 +0x73 fp=0xc00070ecc0 sp=0xc00070ec88 pc=0x11da173
github.com/rakyll/portmidi.(*Stream).Write(0xc000052140, 0xc00070ed30, 0x1, 0x1, 0x100fae8, 0x3)
/Users/tynor/go/pkg/mod/github.com/rakyll/[email protected]/stream.go:120 +0xc5 fp=0xc00070ed00 sp=0xc00070ecc0 pc=0x11d95c5
github.com/rakyll/portmidi.(*Stream).WriteShort(0xc000052140, 0xbf, 0x73, 0x1c, 0xc00070eda3, 0xc00051ccc3)
/Users/tynor/go/pkg/mod/github.com/rakyll/[email protected]/stream.go:131 +0x84 fp=0xc00070ed60 sp=0xc00070ed00 pc=0x11d96a4
gitlab.com/gomidi/portmididrv.(*out).Write(0xc0001b49f0, 0xc00051ccc3, 0x3, 0x3, 0x5cb97d0, 0x0, 0xc00011e340)
/Users/tynor/go/pkg/mod/gitlab.com/gomidi/[email protected]/out.go:43 +0x79 fp=0xc00070ede8 sp=0xc00070ed60 pc=0x11db619
apt-get install libportmidi-dev
is not working since debian is still using 184
MIDI System Real Time Messages are captured by if event.Status&0xF0 == 0xF0
in stream.Read and return an error because they are not SysEx messages.
There's no data payload for system real time messages so I think there could just be a literal check for messages with a status between 0xF8 and 0xFF.
I'm happy to make that as a PR but I don't want to be presumptuous. I'm pretty new to this repo and to MIDI system messages in general.
I am running OS X 10.10.1. I have installed portmidi v217 via homebrew, and I have verified that the header files are linked from /usr/local/Cellar/portmidi/217/include/ to /usr/local/include.
When I run "go get github.com/rakyll/portmidi" or "go install" it errors:
./portmidi.go:20:11: fatal error: 'portmidi.h' file not found
#include <portmidi.h>
^
1 error generated.
I am brand new to Go -- really just want to use it for music. Any idea how to get this working? Thanks.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.