Loading README.md +3 −3 Original line number Diff line number Diff line Loading @@ -28,9 +28,9 @@ Example: play a note when pressing button #1. hat position changes frequency, y func main() { jsevents := Capture( Channel{10, State.OnLong}, // events[0] button #10 long pressed Channel{1, State.OnClose}, // events[1] button #1 closes Channel{1, State.OnMove}, // events[2] hat #1 moves Channel{10, Joystick.OnLong}, // events[0] button #10 long pressed Channel{1, Joystick.OnClose}, // events[1] button #1 closes Channel{1, Joystick.OnMove}, // events[2] hat #1 moves ) var x float32 = .5 var f time.Duration = time.Second / 440 Loading examples/play.go +3 −3 Original line number Diff line number Diff line Loading @@ -13,9 +13,9 @@ import . "github.com/splace/sounds" func main() { jsevents := Capture( Channel{10, State.OnLong}, // events[0] button #10 long pressed Channel{1, State.OnClose}, // events[1] button #1 closes Channel{1, State.OnMove}, // events[2] hat #1 moves Channel{10, Joystick.OnLong}, // events[0] button #10 long pressed Channel{1, Joystick.OnClose}, // events[1] button #1 closes Channel{1, Joystick.OnMove}, // events[2] hat #1 moves ) var x float32 = .5 var f time.Duration = time.Second / 440 Loading joysticks.go +92 −34 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ package joysticks import ( "time" "math" ) type hatAxis struct { Loading @@ -25,68 +26,104 @@ type Joystick struct { buttonCloseEvents map[uint8]chan event buttonOpenEvents map[uint8]chan event buttonLongPressEvents map[uint8]chan event hatChangeEvents map[uint8]chan event hatPanXEvents map[uint8]chan event hatPanYEvents map[uint8]chan event hatPositionEvents map[uint8]chan event hatAngleEvents map[uint8]chan event } type event interface { Moment() time.Duration } type HatChangeEvent struct { type when struct { time time.Duration X, Y float32 } func (b HatChangeEvent) Moment() time.Duration { func (b when) Moment() time.Duration { return b.time } type HatPositionEvent struct { when X, Y float32 } type ButtonChangeEvent struct { time time.Duration when } func (b ButtonChangeEvent) Moment() time.Duration { return b.time type HatPanXEvent struct { when V float32 } type HatPanYEvent struct { when V float32 } type HatAngleEvent struct { when Angle float32 } // ParcelOutEvents interprets whats on the State.OSEvent channel, then puts the required event(s), on any registered channel(s). func (js Joystick) ParcelOutEvents() { for { evt, ok := <-js.OSEvent if !ok { break } if evt, ok := <-js.OSEvent; ok { switch evt.Type { case 1: if evt.Value == 0 { if c, ok := js.buttonOpenEvents[js.buttons[evt.Index].number]; ok { c <- ButtonChangeEvent{toDuration(evt.Time)} c <- ButtonChangeEvent{when{toDuration(evt.Time)}} } if c, ok := js.buttonLongPressEvents[js.buttons[evt.Index].number]; ok { if toDuration(evt.Time) > js.buttons[evt.Index].time+time.Second { c <- ButtonChangeEvent{toDuration(evt.Time)} c <- ButtonChangeEvent{when{toDuration(evt.Time)}} } } } if evt.Value == 1 { if c, ok := js.buttonCloseEvents[js.buttons[evt.Index].number]; ok { c <- ButtonChangeEvent{toDuration(evt.Time)} c <- ButtonChangeEvent{when{toDuration(evt.Time)}} } } js.buttons[evt.Index] = button{js.buttons[evt.Index].number, toDuration(evt.Time), evt.Value != 0} case 2: if c, ok := js.hatChangeEvents[js.hatAxes[evt.Index].number]; ok { switch js.hatAxes[evt.Index].axis { case 1: c <- HatChangeEvent{toDuration(evt.Time), float32(evt.Value) / maxValue, js.hatAxes[evt.Index+1].value} if c, ok := js.hatPanXEvents[js.hatAxes[evt.Index].number]; ok { c <- HatPanXEvent{when{toDuration(evt.Time)}, float32(evt.Value) / maxValue} } case 2: c <- HatChangeEvent{toDuration(evt.Time), js.hatAxes[evt.Index-1].value, float32(evt.Value) / maxValue} if c, ok := js.hatPanYEvents[js.hatAxes[evt.Index].number]; ok { c <- HatPanYEvent{when{toDuration(evt.Time)}, float32(evt.Value) / maxValue} } } if c, ok := js.hatPositionEvents[js.hatAxes[evt.Index].number]; ok { switch js.hatAxes[evt.Index].axis { case 1: c <- HatPositionEvent{when{toDuration(evt.Time)}, float32(evt.Value) / maxValue, js.hatAxes[evt.Index+1].value} case 2: c <- HatPositionEvent{when{toDuration(evt.Time)}, js.hatAxes[evt.Index-1].value, float32(evt.Value) / maxValue} } } if c, ok := js.hatAngleEvents[js.hatAxes[evt.Index].number]; ok { switch js.hatAxes[evt.Index].axis { case 1: c <- HatAngleEvent{when{toDuration(evt.Time)}, float32(math.Atan2(float64(evt.Value),float64(js.hatAxes[evt.Index+1].value)))} case 2: c <- HatAngleEvent{when{toDuration(evt.Time)}, float32(math.Atan2(float64(js.hatAxes[evt.Index-1].value), float64(evt.Value) / maxValue))} } } js.hatAxes[evt.Index] = hatAxis{js.hatAxes[evt.Index].number, js.hatAxes[evt.Index].axis, toDuration(evt.Time), float32(evt.Value) / maxValue} default: // log.Println("unknown input type. ",evt.Type & 0x7f) } } else { break } } } Loading Loading @@ -120,7 +157,28 @@ func (js Joystick) OnLong(button uint8) chan event { // hat moved func (js Joystick) OnMove(hat uint8) chan event { c := make(chan event) js.hatChangeEvents[hat] = c js.hatPositionEvents[hat] = c return c } // hat axis-X moved func (js Joystick) OnPanX(hat uint8) chan event { c := make(chan event) js.hatPanXEvents[hat] = c return c } // hat axis-Y moved func (js Joystick) OnPanY(hat uint8) chan event { c := make(chan event) js.hatPanYEvents[hat] = c return c } // hat axis-Y moved func (js Joystick) OnRotate(hat uint8) chan event { c := make(chan event) js.hatAngleEvents[hat] = c return c } Loading joysticks_linux.go +2 −2 Original line number Diff line number Diff line Loading @@ -44,13 +44,13 @@ var inputPathSlice = []byte("/dev/input/js ")[0:13] // Connect sets up a go routine that puts a joysticks events onto registered channels. // register channels by using the returned state object's On<xxx>(index) methods. // Note: only one event, of each type '<xxx>', for each 'index', re-registering stops events going on the old channel. // then activate using state objects ParcelOutEvents() method.(usually in a go routine.) // then activate using state objects ParcelOutEvents() method.(blocking.) func Connect(index int) (js *Joystick) { r, e := os.OpenFile(string(strconv.AppendUint(inputPathSlice, uint64(index-1), 10)), os.O_RDWR, 0) if e != nil { return nil } js = &Joystick{make(chan osEventRecord), make(map[uint8]button), make(map[uint8]hatAxis), make(map[uint8]chan event), make(map[uint8]chan event), make(map[uint8]chan event), make(map[uint8]chan event)} js = &Joystick{make(chan osEventRecord), make(map[uint8]button), make(map[uint8]hatAxis), make(map[uint8]chan event), make(map[uint8]chan event), make(map[uint8]chan event), make(map[uint8]chan event), make(map[uint8]chan event), make(map[uint8]chan event), make(map[uint8]chan event)} // start thread to read joystick events to the joystick.state osEvent channel go eventPipe(r, js.OSEvent) js.populate() Loading joysticks_test.go +16 −10 Original line number Diff line number Diff line Loading @@ -17,7 +17,8 @@ func TestJoysticksCapture(t *testing.T) { events := Capture( Channel{10, Joystick.OnLong}, // event[0] button #10 long pressed Channel{1, Joystick.OnClose}, // event[1] button #1 closes Channel{1, Joystick.OnMove}, // event[2] hat #1 moves Channel{1, Joystick.OnRotate}, // event[2] hat #1 rotates Channel{2, Joystick.OnRotate}, // event[2] hat #1 rotates ) var x float32 = .5 var f time.Duration = time.Second / 440 Loading @@ -28,8 +29,10 @@ func TestJoysticksCapture(t *testing.T) { case <-events[1]: play(NewSound(NewTone(f, float64(x)), time.Second/3)) case h := <-events[2]: x = h.(HatChangeEvent).X/2 + .5 f = time.Duration(100*math.Pow(2, float64(h.(HatChangeEvent).Y))) * time.Second / 44000 fmt.Println(h.(HatAngleEvent).Angle) x = h.(HatAngleEvent).Angle/6.28 + .5 case h := <-events[3]: f = time.Duration(100*math.Pow(2, float64(h.(HatAngleEvent).Angle)/6.28)) * time.Second / 44000 } } } Loading Loading @@ -58,11 +61,11 @@ func TestJoysticksMutipleCapture(t *testing.T) { case <-events2[1]: play(NewSound(NewTone(f, float64(x)), time.Second/3)) case h := <-events1[2]: x = h.(HatChangeEvent).X/2 + .5 f = time.Duration(100*math.Pow(2, float64(h.(HatChangeEvent).Y))) * time.Second / 44000 x = h.(HatPositionEvent).X/2 + .5 f = time.Duration(100*math.Pow(2, float64(h.(HatPositionEvent).Y))) * time.Second / 44000 case h := <-events2[2]: x = h.(HatChangeEvent).X/2 + .5 f = time.Duration(100*math.Pow(2, float64(h.(HatChangeEvent).Y))) * time.Second / 44000 x = h.(HatPositionEvent).X/2 + .5 f = time.Duration(100*math.Pow(2, float64(h.(HatPositionEvent).Y))) * time.Second / 44000 } } } Loading @@ -83,7 +86,8 @@ func TestJoysticksAdvanced(t *testing.T) { b4 := js1.OnClose(4) quit := js1.OnOpen(10) h1 := js1.OnMove(1) h2 := js1.OnMove(2) h4 := js1.OnPanX(2) h5 := js1.OnPanY(2) h3 := js1.OnMove(3) go js1.ParcelOutEvents() time.AfterFunc(time.Second*10, func() { js1.InsertSyntheticEvent(1, 1, 1) }) // value=1 (close),type=1 (button), index=1, so fires b1 after 10 seconds Loading @@ -102,10 +106,12 @@ func TestJoysticksAdvanced(t *testing.T) { play(NewSound(NewTone(time.Second/150, 1), time.Second/3)) case h := <-h1: fmt.Println("hat 1 moved", h) case h := <-h2: fmt.Println("hat 2 moved", h) case h := <-h3: fmt.Println("hat 3 moved", h) case h := <-h4: fmt.Println("hat 2 X moved", h.(HatPanXEvent).V) case h := <-h5: fmt.Println("hat 2 Y moved", h) } } } Loading Loading
README.md +3 −3 Original line number Diff line number Diff line Loading @@ -28,9 +28,9 @@ Example: play a note when pressing button #1. hat position changes frequency, y func main() { jsevents := Capture( Channel{10, State.OnLong}, // events[0] button #10 long pressed Channel{1, State.OnClose}, // events[1] button #1 closes Channel{1, State.OnMove}, // events[2] hat #1 moves Channel{10, Joystick.OnLong}, // events[0] button #10 long pressed Channel{1, Joystick.OnClose}, // events[1] button #1 closes Channel{1, Joystick.OnMove}, // events[2] hat #1 moves ) var x float32 = .5 var f time.Duration = time.Second / 440 Loading
examples/play.go +3 −3 Original line number Diff line number Diff line Loading @@ -13,9 +13,9 @@ import . "github.com/splace/sounds" func main() { jsevents := Capture( Channel{10, State.OnLong}, // events[0] button #10 long pressed Channel{1, State.OnClose}, // events[1] button #1 closes Channel{1, State.OnMove}, // events[2] hat #1 moves Channel{10, Joystick.OnLong}, // events[0] button #10 long pressed Channel{1, Joystick.OnClose}, // events[1] button #1 closes Channel{1, Joystick.OnMove}, // events[2] hat #1 moves ) var x float32 = .5 var f time.Duration = time.Second / 440 Loading
joysticks.go +92 −34 Original line number Diff line number Diff line Loading @@ -2,6 +2,7 @@ package joysticks import ( "time" "math" ) type hatAxis struct { Loading @@ -25,68 +26,104 @@ type Joystick struct { buttonCloseEvents map[uint8]chan event buttonOpenEvents map[uint8]chan event buttonLongPressEvents map[uint8]chan event hatChangeEvents map[uint8]chan event hatPanXEvents map[uint8]chan event hatPanYEvents map[uint8]chan event hatPositionEvents map[uint8]chan event hatAngleEvents map[uint8]chan event } type event interface { Moment() time.Duration } type HatChangeEvent struct { type when struct { time time.Duration X, Y float32 } func (b HatChangeEvent) Moment() time.Duration { func (b when) Moment() time.Duration { return b.time } type HatPositionEvent struct { when X, Y float32 } type ButtonChangeEvent struct { time time.Duration when } func (b ButtonChangeEvent) Moment() time.Duration { return b.time type HatPanXEvent struct { when V float32 } type HatPanYEvent struct { when V float32 } type HatAngleEvent struct { when Angle float32 } // ParcelOutEvents interprets whats on the State.OSEvent channel, then puts the required event(s), on any registered channel(s). func (js Joystick) ParcelOutEvents() { for { evt, ok := <-js.OSEvent if !ok { break } if evt, ok := <-js.OSEvent; ok { switch evt.Type { case 1: if evt.Value == 0 { if c, ok := js.buttonOpenEvents[js.buttons[evt.Index].number]; ok { c <- ButtonChangeEvent{toDuration(evt.Time)} c <- ButtonChangeEvent{when{toDuration(evt.Time)}} } if c, ok := js.buttonLongPressEvents[js.buttons[evt.Index].number]; ok { if toDuration(evt.Time) > js.buttons[evt.Index].time+time.Second { c <- ButtonChangeEvent{toDuration(evt.Time)} c <- ButtonChangeEvent{when{toDuration(evt.Time)}} } } } if evt.Value == 1 { if c, ok := js.buttonCloseEvents[js.buttons[evt.Index].number]; ok { c <- ButtonChangeEvent{toDuration(evt.Time)} c <- ButtonChangeEvent{when{toDuration(evt.Time)}} } } js.buttons[evt.Index] = button{js.buttons[evt.Index].number, toDuration(evt.Time), evt.Value != 0} case 2: if c, ok := js.hatChangeEvents[js.hatAxes[evt.Index].number]; ok { switch js.hatAxes[evt.Index].axis { case 1: c <- HatChangeEvent{toDuration(evt.Time), float32(evt.Value) / maxValue, js.hatAxes[evt.Index+1].value} if c, ok := js.hatPanXEvents[js.hatAxes[evt.Index].number]; ok { c <- HatPanXEvent{when{toDuration(evt.Time)}, float32(evt.Value) / maxValue} } case 2: c <- HatChangeEvent{toDuration(evt.Time), js.hatAxes[evt.Index-1].value, float32(evt.Value) / maxValue} if c, ok := js.hatPanYEvents[js.hatAxes[evt.Index].number]; ok { c <- HatPanYEvent{when{toDuration(evt.Time)}, float32(evt.Value) / maxValue} } } if c, ok := js.hatPositionEvents[js.hatAxes[evt.Index].number]; ok { switch js.hatAxes[evt.Index].axis { case 1: c <- HatPositionEvent{when{toDuration(evt.Time)}, float32(evt.Value) / maxValue, js.hatAxes[evt.Index+1].value} case 2: c <- HatPositionEvent{when{toDuration(evt.Time)}, js.hatAxes[evt.Index-1].value, float32(evt.Value) / maxValue} } } if c, ok := js.hatAngleEvents[js.hatAxes[evt.Index].number]; ok { switch js.hatAxes[evt.Index].axis { case 1: c <- HatAngleEvent{when{toDuration(evt.Time)}, float32(math.Atan2(float64(evt.Value),float64(js.hatAxes[evt.Index+1].value)))} case 2: c <- HatAngleEvent{when{toDuration(evt.Time)}, float32(math.Atan2(float64(js.hatAxes[evt.Index-1].value), float64(evt.Value) / maxValue))} } } js.hatAxes[evt.Index] = hatAxis{js.hatAxes[evt.Index].number, js.hatAxes[evt.Index].axis, toDuration(evt.Time), float32(evt.Value) / maxValue} default: // log.Println("unknown input type. ",evt.Type & 0x7f) } } else { break } } } Loading Loading @@ -120,7 +157,28 @@ func (js Joystick) OnLong(button uint8) chan event { // hat moved func (js Joystick) OnMove(hat uint8) chan event { c := make(chan event) js.hatChangeEvents[hat] = c js.hatPositionEvents[hat] = c return c } // hat axis-X moved func (js Joystick) OnPanX(hat uint8) chan event { c := make(chan event) js.hatPanXEvents[hat] = c return c } // hat axis-Y moved func (js Joystick) OnPanY(hat uint8) chan event { c := make(chan event) js.hatPanYEvents[hat] = c return c } // hat axis-Y moved func (js Joystick) OnRotate(hat uint8) chan event { c := make(chan event) js.hatAngleEvents[hat] = c return c } Loading
joysticks_linux.go +2 −2 Original line number Diff line number Diff line Loading @@ -44,13 +44,13 @@ var inputPathSlice = []byte("/dev/input/js ")[0:13] // Connect sets up a go routine that puts a joysticks events onto registered channels. // register channels by using the returned state object's On<xxx>(index) methods. // Note: only one event, of each type '<xxx>', for each 'index', re-registering stops events going on the old channel. // then activate using state objects ParcelOutEvents() method.(usually in a go routine.) // then activate using state objects ParcelOutEvents() method.(blocking.) func Connect(index int) (js *Joystick) { r, e := os.OpenFile(string(strconv.AppendUint(inputPathSlice, uint64(index-1), 10)), os.O_RDWR, 0) if e != nil { return nil } js = &Joystick{make(chan osEventRecord), make(map[uint8]button), make(map[uint8]hatAxis), make(map[uint8]chan event), make(map[uint8]chan event), make(map[uint8]chan event), make(map[uint8]chan event)} js = &Joystick{make(chan osEventRecord), make(map[uint8]button), make(map[uint8]hatAxis), make(map[uint8]chan event), make(map[uint8]chan event), make(map[uint8]chan event), make(map[uint8]chan event), make(map[uint8]chan event), make(map[uint8]chan event), make(map[uint8]chan event)} // start thread to read joystick events to the joystick.state osEvent channel go eventPipe(r, js.OSEvent) js.populate() Loading
joysticks_test.go +16 −10 Original line number Diff line number Diff line Loading @@ -17,7 +17,8 @@ func TestJoysticksCapture(t *testing.T) { events := Capture( Channel{10, Joystick.OnLong}, // event[0] button #10 long pressed Channel{1, Joystick.OnClose}, // event[1] button #1 closes Channel{1, Joystick.OnMove}, // event[2] hat #1 moves Channel{1, Joystick.OnRotate}, // event[2] hat #1 rotates Channel{2, Joystick.OnRotate}, // event[2] hat #1 rotates ) var x float32 = .5 var f time.Duration = time.Second / 440 Loading @@ -28,8 +29,10 @@ func TestJoysticksCapture(t *testing.T) { case <-events[1]: play(NewSound(NewTone(f, float64(x)), time.Second/3)) case h := <-events[2]: x = h.(HatChangeEvent).X/2 + .5 f = time.Duration(100*math.Pow(2, float64(h.(HatChangeEvent).Y))) * time.Second / 44000 fmt.Println(h.(HatAngleEvent).Angle) x = h.(HatAngleEvent).Angle/6.28 + .5 case h := <-events[3]: f = time.Duration(100*math.Pow(2, float64(h.(HatAngleEvent).Angle)/6.28)) * time.Second / 44000 } } } Loading Loading @@ -58,11 +61,11 @@ func TestJoysticksMutipleCapture(t *testing.T) { case <-events2[1]: play(NewSound(NewTone(f, float64(x)), time.Second/3)) case h := <-events1[2]: x = h.(HatChangeEvent).X/2 + .5 f = time.Duration(100*math.Pow(2, float64(h.(HatChangeEvent).Y))) * time.Second / 44000 x = h.(HatPositionEvent).X/2 + .5 f = time.Duration(100*math.Pow(2, float64(h.(HatPositionEvent).Y))) * time.Second / 44000 case h := <-events2[2]: x = h.(HatChangeEvent).X/2 + .5 f = time.Duration(100*math.Pow(2, float64(h.(HatChangeEvent).Y))) * time.Second / 44000 x = h.(HatPositionEvent).X/2 + .5 f = time.Duration(100*math.Pow(2, float64(h.(HatPositionEvent).Y))) * time.Second / 44000 } } } Loading @@ -83,7 +86,8 @@ func TestJoysticksAdvanced(t *testing.T) { b4 := js1.OnClose(4) quit := js1.OnOpen(10) h1 := js1.OnMove(1) h2 := js1.OnMove(2) h4 := js1.OnPanX(2) h5 := js1.OnPanY(2) h3 := js1.OnMove(3) go js1.ParcelOutEvents() time.AfterFunc(time.Second*10, func() { js1.InsertSyntheticEvent(1, 1, 1) }) // value=1 (close),type=1 (button), index=1, so fires b1 after 10 seconds Loading @@ -102,10 +106,12 @@ func TestJoysticksAdvanced(t *testing.T) { play(NewSound(NewTone(time.Second/150, 1), time.Second/3)) case h := <-h1: fmt.Println("hat 1 moved", h) case h := <-h2: fmt.Println("hat 2 moved", h) case h := <-h3: fmt.Println("hat 3 moved", h) case h := <-h4: fmt.Println("hat 2 X moved", h.(HatPanXEvent).V) case h := <-h5: fmt.Println("hat 2 Y moved", h) } } } Loading