Skip to content

Commit

Permalink
Experimenting with C-API for patches.
Browse files Browse the repository at this point in the history
See #3
  • Loading branch information
Robin IT committed May 30, 2017
1 parent d3b1e3a commit f4f56d5
Show file tree
Hide file tree
Showing 4 changed files with 149 additions and 0 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
/build
5 changes: 5 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
fmt:
go fmt ./...

lib: fmt
go build -buildmode=c-shared -o build/libkatana.so ./capi
41 changes: 41 additions & 0 deletions capi/patch.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package main

import "C"

import (
"fmt"
"unsafe"

"github.com/katana-dev/lib-katana/patch"
"github.com/katana-dev/lib-katana/sysex"
)

//Creates an interger reference to a new Patch.
//export new_patch
func new_patch() C.int {
p, _ := patch.New(patch.EncSparse)
n := trackObj(p)
return C.int(n)
}

/**
* Applies a sysex message to a patch.
*
* @param int Reference number
* @param void* Byte array pointer
* @param int Array length
* @return char* CString message
*/
//export apply_message_to_patch
func apply_message_to_patch(n C.int, arr unsafe.Pointer, len C.int) *C.char {
b := C.GoBytes(arr, len)
m, err := sysex.Parse(b)
if err != nil {
return C.CString(err.Error())
}
p := getObj(int32(n)).(patch.Patch)
r := p.ApplyMessage(m)
return C.CString(fmt.Sprintf("Wrote to ref %v > %+v\n", n, r))
}

func main() {}
102 changes: 102 additions & 0 deletions capi/refs.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package main

import (
"C"
"fmt"
"sync"
)

//A single reference object.
type ref struct {
obj interface{}
cnt int32
}

//A singleton tracker for references.
var t struct {
sync.Mutex
next int32
nums map[interface{}]int32
objs map[int32]ref
}

//Init tracking features.
func initTracker() {
if t.next != 0 {
return
}

t.Lock()
defer t.Unlock()
t.next = 1 //Start at non-zero.
t.nums = make(map[interface{}]int32)
t.objs = make(map[int32]ref)
}

//Gets an object without manipulating the counter.
func getObj(n int32) interface{} {
initTracker()

t.Lock()
r, ok := t.objs[n]
t.Unlock()
if !ok {
panic(fmt.Sprintf("getObj unknown reference number: %d", n))
}
return r.obj
}

//Track a given Go object which is outbound.
func trackObj(o interface{}) int32 {
initTracker()

t.Lock()
defer t.Unlock()

//Try deduplicate the reference.
n := t.nums[o]
if n != 0 {
r := t.objs[n]
t.objs[n] = ref{r.obj, r.cnt + 1}
} else {
//Push a new reference.
t.next++
if t.next < 0 {
panic("trackObj reference number overflow")
}
n = t.next
t.nums[o] = n
t.objs[n] = ref{o, 1}
}

return int32(n)
}

//Decrements the reference counter for given reference number, eventually deleting on zero.
func releaseRef(n int32) {
initTracker()

t.Lock()
defer t.Unlock()

r, ok := t.objs[n]
if !ok {
panic(fmt.Sprintf("releaseRef unknown reference number: %d", n))
}

//When this is the last reference, remove the entry from the maps.
if r.cnt <= 1 {
delete(t.objs, n)
delete(t.nums, r.obj)
} else {
//Otherwise decrement.
r := t.objs[n]
t.objs[n] = ref{r.obj, r.cnt - 1}
}
}

//Releases a reference generated before so it may be garbage collected.
//export release_ref
func release_ref(n C.int) {
releaseRef(int32(n))
}

0 comments on commit f4f56d5

Please sign in to comment.