├── .travis.yml ├── launchpad.jpg ├── lp4j-api ├── README.md ├── src │ ├── test │ │ └── java │ │ │ └── net │ │ │ └── thecodersbreakfast │ │ │ └── lp4j │ │ │ └── api │ │ │ ├── BrightnessTest.java │ │ │ ├── BufferTest.java │ │ │ ├── LaunchpadExceptionTest.java │ │ │ ├── ScrollSpeedTest.java │ │ │ ├── LaunchpadListenerAdapterTest.java │ │ │ ├── PadTest.java │ │ │ ├── ButtonTest.java │ │ │ └── ColorTest.java │ └── main │ │ └── java │ │ └── net │ │ └── thecodersbreakfast │ │ └── lp4j │ │ └── api │ │ ├── LightIntensity.java │ │ ├── BackBufferOperation.java │ │ ├── LaunchpadListenerAdapter.java │ │ ├── LaunchpadException.java │ │ ├── Buffer.java │ │ ├── Launchpad.java │ │ ├── LaunchpadListener.java │ │ ├── ScrollSpeed.java │ │ ├── Pad.java │ │ ├── Brightness.java │ │ ├── LaunchpadClient.java │ │ ├── Color.java │ │ └── Button.java └── pom.xml ├── lp4j-midi ├── src │ ├── test │ │ └── java │ │ │ └── net.thecodersbreakfast.lp4j.midi │ │ │ ├── MidiLaunchpadTest.java │ │ │ ├── MidiDeviceConfigurationTest.java │ │ │ ├── DefaultMidiProtocolReceiverTest.java │ │ │ ├── DefaultMidiProtocolListenerTest.java │ │ │ ├── DefaultMidiProtocolClientTest.java │ │ │ └── MidiLaunchpadClientTest.java │ └── main │ │ └── java │ │ └── net │ │ └── thecodersbreakfast │ │ └── lp4j │ │ └── midi │ │ ├── protocol │ │ ├── MidiProtocolListener.java │ │ ├── DefaultMidiProtocolListener.java │ │ ├── DefaultMidiProtocolReceiver.java │ │ ├── MidiProtocolClient.java │ │ └── DefaultMidiProtocolClient.java │ │ ├── MidiDeviceConfiguration.java │ │ ├── MidiLaunchpad.java │ │ └── MidiLaunchpadClient.java └── pom.xml ├── lp4j-emu-web ├── src │ └── main │ │ ├── resources │ │ └── web │ │ │ ├── index.html │ │ │ ├── vertxbus.min.js │ │ │ ├── Launchpad.js │ │ │ ├── emulator.js │ │ │ └── sockjs-0.3.min.js │ │ └── java │ │ └── net │ │ └── thecodersbreakfast │ │ └── lp4j │ │ └── emulator │ │ ├── EmulatorLaunchpadClient.java │ │ └── EmulatorLaunchpad.java └── pom.xml ├── README.md ├── pom.xml └── LICENCE.TXT /.travis.yml: -------------------------------------------------------------------------------- 1 | language: java 2 | jdk: 3 | - openjdk11 4 | 5 | -------------------------------------------------------------------------------- /launchpad.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/OlivierCroisier/LP4J/HEAD/launchpad.jpg -------------------------------------------------------------------------------- /lp4j-api/README.md: -------------------------------------------------------------------------------- 1 | lp4j 2 | ==== 3 | 4 | LP4J is a client API for the Novation Launchpad S DJ pad. 5 | It provides high- and low-level APIs to send commands to the device, and react when its keys and buttons are pressed. 6 | 7 | The high-level API is MIDI-agnostic, so it can be used with a software emulator. 8 | The low-level API is MIDI-specific, and can be used to send and decode raw MIDI messages. 9 | 10 | 11 | -------------------------------------------------------------------------------- /lp4j-midi/src/test/java/net.thecodersbreakfast.lp4j.midi/MidiLaunchpadTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.thecodersbreakfast.lp4j.midi; 18 | 19 | public class MidiLaunchpadTest { 20 | 21 | 22 | } 23 | -------------------------------------------------------------------------------- /lp4j-api/src/test/java/net/thecodersbreakfast/lp4j/api/BrightnessTest.java: -------------------------------------------------------------------------------- 1 | package net.thecodersbreakfast.lp4j.api; 2 | 3 | import org.junit.Test; 4 | 5 | import static org.junit.Assert.assertEquals; 6 | import static org.junit.Assert.assertNotNull; 7 | 8 | public class BrightnessTest { 9 | 10 | @Test 11 | public void valueOf() { 12 | Brightness brightness = Brightness.of(Brightness.MIN_VALUE); 13 | assertNotNull(brightness); 14 | assertEquals(Brightness.MIN_VALUE, brightness.getBrightness()); 15 | } 16 | 17 | @Test(expected = IllegalArgumentException.class) 18 | public void valueOf_tooLow() { 19 | Brightness.of(Brightness.MIN_VALUE - 1); 20 | } 21 | 22 | @Test(expected = IllegalArgumentException.class) 23 | public void valueOf_tooHigh() { 24 | Brightness.of(Brightness.MAX_VALUE + 1); 25 | } 26 | 27 | 28 | } 29 | -------------------------------------------------------------------------------- /lp4j-api/src/main/java/net/thecodersbreakfast/lp4j/api/LightIntensity.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.thecodersbreakfast.lp4j.api; 18 | 19 | /** 20 | * Represents a light intensity used during led testing (see {@link net.thecodersbreakfast.lp4j.api.LaunchpadClient#testLights(LightIntensity)}. 21 | * 22 | * @author Olivier Croisier (olivier.croisier@gmail.com) 23 | */ 24 | public enum LightIntensity { 25 | /** Low light intensity */ 26 | LOW, 27 | /** Medium light intensity */ 28 | MEDIUM, 29 | /** High light intensity */ 30 | HIGH 31 | } 32 | -------------------------------------------------------------------------------- /lp4j-api/src/test/java/net/thecodersbreakfast/lp4j/api/BufferTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.thecodersbreakfast.lp4j.api; 18 | 19 | 20 | import org.junit.Assert; 21 | import org.junit.Test; 22 | 23 | public class BufferTest { 24 | 25 | @Test 26 | public void other_0() { 27 | Buffer other = Buffer.BUFFER_0.other(); 28 | Assert.assertEquals(Buffer.BUFFER_1, other); 29 | } 30 | 31 | @Test 32 | public void other_1() { 33 | Buffer other = Buffer.BUFFER_1.other(); 34 | Assert.assertEquals(Buffer.BUFFER_0, other); 35 | } 36 | 37 | } 38 | -------------------------------------------------------------------------------- /lp4j-api/src/test/java/net/thecodersbreakfast/lp4j/api/LaunchpadExceptionTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.thecodersbreakfast.lp4j.api; 18 | 19 | import org.junit.Test; 20 | 21 | public class LaunchpadExceptionTest { 22 | 23 | @Test 24 | public void testLaunchpadException_noargs() { 25 | new LaunchpadException(); 26 | } 27 | 28 | @Test 29 | public void testLaunchpadException_message() { 30 | new LaunchpadException("message"); 31 | } 32 | 33 | @Test 34 | public void testLaunchpadException_cause() { 35 | new LaunchpadException(new RuntimeException()); 36 | } 37 | 38 | @Test 39 | public void testLaunchpadException_both() { 40 | new LaunchpadException("message", new RuntimeException()); 41 | } 42 | 43 | } 44 | -------------------------------------------------------------------------------- /lp4j-api/src/main/java/net/thecodersbreakfast/lp4j/api/BackBufferOperation.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.thecodersbreakfast.lp4j.api; 18 | 19 | /** 20 | * Describes how the backbuffer is impacted by the operation currently applied to the write buffer. 21 | * 22 | *
The backbuffer is defined as "the buffer that is not the current write buffer". It has nothing to do with which 23 | * buffer is currently visible. 24 | * 25 | * @author Olivier Croisier (olivier.croisier@gmail.com) 26 | */ 27 | public enum BackBufferOperation { 28 | /** Ths operation is applied to the write buffer only, the back buffer is not modified. */ 29 | NONE, 30 | /** The operation is applied to the write buffer and to backbuffer. */ 31 | COPY, 32 | /** The operation is applied to the write buffer, and the corresponding location on the backbuffer is cleared */ 33 | CLEAR 34 | } 35 | -------------------------------------------------------------------------------- /lp4j-api/src/main/java/net/thecodersbreakfast/lp4j/api/LaunchpadListenerAdapter.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.thecodersbreakfast.lp4j.api; 18 | 19 | /** 20 | * Convenient empty implementation of a {@link net.thecodersbreakfast.lp4j.api.LaunchpadListener}. 21 | * 22 | * @author Olivier Croisier (olivier.croisier@gmail.com) 23 | */ 24 | public abstract class LaunchpadListenerAdapter implements LaunchpadListener { 25 | 26 | @Override 27 | public void onPadPressed(Pad pad, long timestamp) { 28 | } 29 | 30 | @Override 31 | public void onPadReleased(Pad pad, long timestamp) { 32 | } 33 | 34 | @Override 35 | public void onButtonPressed(Button button, long timestamp) { 36 | } 37 | 38 | @Override 39 | public void onButtonReleased(Button button, long timestamp) { 40 | } 41 | 42 | @Override 43 | public void onTextScrolled(long timestamp) { 44 | } 45 | 46 | } 47 | -------------------------------------------------------------------------------- /lp4j-api/src/test/java/net/thecodersbreakfast/lp4j/api/ScrollSpeedTest.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.thecodersbreakfast.lp4j.api; 18 | 19 | import org.junit.Test; 20 | 21 | import static org.junit.Assert.assertEquals; 22 | import static org.junit.Assert.assertNotNull; 23 | 24 | public class ScrollSpeedTest { 25 | 26 | @Test 27 | public void valueOf() { 28 | ScrollSpeed scrollSpeed = ScrollSpeed.of(ScrollSpeed.MIN_VALUE); 29 | assertNotNull(scrollSpeed); 30 | assertEquals(ScrollSpeed.MIN_VALUE, scrollSpeed.getScrollSpeed()); 31 | } 32 | 33 | @Test(expected = IllegalArgumentException.class) 34 | public void valueOf_tooLow() { 35 | ScrollSpeed.of(ScrollSpeed.MIN_VALUE - 1); 36 | } 37 | 38 | @Test(expected = IllegalArgumentException.class) 39 | public void valueOf_tooHigh() { 40 | ScrollSpeed.of(ScrollSpeed.MAX_VALUE + 1); 41 | } 42 | 43 | 44 | } 45 | -------------------------------------------------------------------------------- /lp4j-emu-web/src/main/resources/web/index.html: -------------------------------------------------------------------------------- 1 | 16 | 17 | 18 |
19 |In addition of the current write buffer, most operations can (depending on a {@link 24 | * net.thecodersbreakfast.lp4j.api.BackBufferOperation} parameter) impact a "backbuffer", defined as "the buffer that is 25 | * not the current write buffer". 26 | * 27 | * @author Olivier Croisier (olivier.croisier@gmail.com) 28 | */ 29 | public enum Buffer { 30 | /** The first buffer */ 31 | BUFFER_0() { 32 | @Override 33 | public Buffer other() { 34 | return BUFFER_1; 35 | } 36 | }, 37 | /** The second buffer */ 38 | BUFFER_1() { 39 | @Override 40 | public Buffer other() { 41 | return BUFFER_0; 42 | } 43 | }; 44 | 45 | /** 46 | * Returns the other buffer ({@link Buffer#BUFFER_1} if the current buffer is {@link Buffer#BUFFER_0}, and 47 | * vice-versa). 48 | * 49 | * @return The other buffer 50 | */ 51 | public abstract Buffer other(); 52 | 53 | } 54 | -------------------------------------------------------------------------------- /lp4j-api/src/main/java/net/thecodersbreakfast/lp4j/api/Launchpad.java: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net) 3 | * 4 | * Licensed under the Apache License, Version 2.0 (the "License"); 5 | * you may not use this file except in compliance with the License. 6 | * You may obtain a copy of the License at 7 | * 8 | * http://www.apache.org/licenses/LICENSE-2.0 9 | * 10 | * Unless required by applicable law or agreed to in writing, software 11 | * distributed under the License is distributed on an "AS IS" BASIS, 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | * See the License for the specific language governing permissions and 14 | * limitations under the License. 15 | */ 16 | 17 | package net.thecodersbreakfast.lp4j.api; 18 | 19 | import java.io.Closeable; 20 | 21 | /** 22 | * Describes a generic Launchpad interface. 23 | * 24 | *
Implementations are responsible for providing a {@link net.thecodersbreakfast.lp4j.api.LaunchpadClient} which allows
25 | * to send commands to the Launchpad, and to accept a {@link net.thecodersbreakfast.lp4j.api.LaunchpadListener} through
26 | * which the API user will be notified of events such as pad or button presses.
27 | *
28 | * @author Olivier Croisier (olivier.croisier@gmail.com)
29 | */
30 | public interface Launchpad extends Closeable {
31 |
32 | /**
33 | * Returns an implementation of {@link net.thecodersbreakfast.lp4j.api.LaunchpadClient} suitable to send commands to
34 | * a Launchpad.
35 | *
36 | * @return a client
37 | */
38 | public LaunchpadClient getClient();
39 |
40 | /**
41 | * Accepts a {@link net.thecodersbreakfast.lp4j.api.LaunchpadListener}, which will be notified of any
42 | * Launchpad-related event such as pad or button presses.
43 | *
44 | * @param listener The listener to be notified
45 | */
46 | public void setListener(LaunchpadListener listener);
47 |
48 | }
49 |
--------------------------------------------------------------------------------
/lp4j-api/src/test/java/net/thecodersbreakfast/lp4j/api/PadTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.thecodersbreakfast.lp4j.api;
18 |
19 | import org.junit.Test;
20 |
21 | import static org.junit.Assert.assertEquals;
22 | import static org.junit.Assert.assertNotNull;
23 |
24 |
25 | public class PadTest {
26 |
27 | @Test
28 | public void at() {
29 | Pad pad = Pad.at(Pad.X_MIN, Pad.Y_MIN);
30 | assertNotNull(pad);
31 | assertEquals(Pad.X_MIN, pad.getX());
32 | assertEquals(Pad.Y_MIN, pad.getY());
33 | }
34 |
35 | @Test(expected = IllegalArgumentException.class)
36 | public void at_xTooLow() {
37 | Pad pad = Pad.at(Pad.X_MIN - 1, Pad.Y_MIN);
38 | }
39 |
40 | @Test(expected = IllegalArgumentException.class)
41 | public void at_xTooHigh() {
42 | Pad pad = Pad.at(Pad.X_MAX + 1, Pad.Y_MIN);
43 | }
44 |
45 | @Test(expected = IllegalArgumentException.class)
46 | public void at_yTooLow() {
47 | Pad pad = Pad.at(Pad.X_MIN, Pad.Y_MIN - 1);
48 | }
49 |
50 | @Test(expected = IllegalArgumentException.class)
51 | public void at_yTooHigh() {
52 | Pad pad = Pad.at(Pad.X_MIN, Pad.Y_MAX + 1);
53 | }
54 |
55 | @Test
56 | public void tostring() {
57 | Pad pad = Pad.at(Pad.X_MIN, Pad.Y_MIN);
58 | assertEquals("Pad[0,0]", pad.toString());
59 | }
60 |
61 | }
62 |
--------------------------------------------------------------------------------
/lp4j-api/src/test/java/net/thecodersbreakfast/lp4j/api/ButtonTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.thecodersbreakfast.lp4j.api;
18 |
19 | import org.junit.Assert;
20 | import org.junit.Test;
21 |
22 | import static org.junit.Assert.*;
23 |
24 | public class ButtonTest {
25 |
26 | @Test
27 | public void at_topButton() {
28 | Button button = Button.atTop(0);
29 | assertEquals(Button.UP, button);
30 | Assert.assertTrue(button.isTopButton());
31 | }
32 |
33 | @Test
34 | public void at_rightButton() {
35 | Button button = Button.atRight(0);
36 | assertEquals(Button.VOL, button);
37 | Assert.assertTrue(button.isRightButton());
38 | }
39 |
40 | @Test(expected = IllegalArgumentException.class)
41 | public void at_coordinateTooLow() {
42 | Button button = Button.atTop(Button.MIN_COORD - 1);
43 | }
44 |
45 | @Test(expected = IllegalArgumentException.class)
46 | public void at_coordinateTooHigh() {
47 | Button button = Button.atTop(Button.MAX_COORD + 1);
48 | }
49 |
50 | @Test
51 | public void isTopOrRight() {
52 | assertTrue(Button.UP.isTopButton());
53 | assertFalse(Button.UP.isRightButton());
54 | assertTrue(Button.VOL.isRightButton());
55 | assertFalse(Button.VOL.isTopButton());
56 | }
57 |
58 | @Test
59 | public void tostring() {
60 | assertEquals("Button[UP(top,0)]", Button.UP.toString());
61 | }
62 |
63 | }
64 |
--------------------------------------------------------------------------------
/lp4j-emu-web/src/main/resources/web/vertxbus.min.js:
--------------------------------------------------------------------------------
1 | var vertx=vertx||{};
2 | !function(j){"function"===typeof define&&define.amd?define("vertxbus",["sockjs"],j):j(SockJS)}(function(j){vertx.EventBus=function(p,q){function o(){e.send(JSON.stringify({type:"ping"}))}function l(b,a,c,f){g("address","string",a);g("replyHandler","function",f,!0);k();b={type:b,address:a,body:c};d.sessionID&&(b.sessionID=d.sessionID);f&&(a=r(),b.replyAddress=a,m[a]=f);f=JSON.stringify(b);e.send(f)}function k(){if(h!=vertx.EventBus.OPEN)throw Error("INVALID_STATE_ERR");}function g(b,a,c,f){if(!f&&
3 | !c)throw Error("Parameter "+b+" must be specified");if(c&&typeof c!=a)throw Error("Parameter "+b+" must be of type "+a);}function r(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(b,a){return a=16*Math.random(),("y"==b?a&3|8:a|0).toString(16)})}var d=this,e=new j(p,void 0,q),i={},m={},h=vertx.EventBus.CONNECTING,n=null;d.onopen=null;d.onclose=null;d.login=function(b,a,c){l("send","vertx.basicauthmanager.login",{username:b,password:a},function(a){"ok"===a.status&&(d.sessionID=
4 | a.sessionID);c&&(delete a.sessionID,c(a))})};d.send=function(b,a,c){l("send",b,a,c)};d.publish=function(b,a,c){l("publish",b,a,c)};d.registerHandler=function(b,a){g("address","string",b);g("handler","function",a);k();var c=i[b];c?c[c.length]=a:(c=[a],i[b]=c,e.send(JSON.stringify({type:"register",address:b})))};d.unregisterHandler=function(b,a){g("address","string",b);g("handler","function",a);k();var c=i[b];if(c){var d=c.indexOf(a);-1!=d&&c.splice(d,1);0==c.length&&(e.send(JSON.stringify({type:"unregister",
5 | address:b})),delete i[b])}};d.close=function(){k();n&&clearInterval(n);h=vertx.EventBus.CLOSING;e.close()};d.readyState=function(){return h};e.onopen=function(){o();n=setInterval(o,5E3);h=vertx.EventBus.OPEN;if(d.onopen)d.onopen()};e.onclose=function(){h=vertx.EventBus.CLOSED;if(d.onclose)d.onclose()};e.onmessage=function(b){var a=JSON.parse(b.data),b=a.body,c=a.replyAddress,a=a.address,f;c&&(f=function(a,b){d.send(c,a,b)});var e=i[a];if(e){a=e.slice(0);for(e=0;e {@code ScrollSpeed} instances are immutable and cached.
24 | *
25 | * @author Olivier Croisier (olivier.croisier@gmail.com)
26 | */
27 | public class ScrollSpeed {
28 |
29 | /** Minimum value for a scrolling speed */
30 | public static final int MIN_VALUE = 1;
31 | /** Maximum value for a scrolling speed */
32 | public static final int MAX_VALUE = 7;
33 |
34 | /** Cache of all possible scrolling speeds */
35 | private static final ScrollSpeed[] CACHE;
36 |
37 | static {
38 | CACHE = new ScrollSpeed[MAX_VALUE - MIN_VALUE + 1];
39 | for (int i = MIN_VALUE; i <= MAX_VALUE; i++) {
40 | CACHE[i - MIN_VALUE] = new ScrollSpeed(i);
41 | }
42 | }
43 |
44 | /** Slowest scrolling speed */
45 | public static final ScrollSpeed SPEED_MIN = of(MIN_VALUE);
46 | /** Fastest scrolling speed */
47 | public static final ScrollSpeed SPEED_MAX = of(MAX_VALUE);
48 |
49 | /**
50 | * Factory method.
51 | *
52 | * @param speed The desired scrolling speed. Must be in range [{@link net.thecodersbreakfast.lp4j.api.ScrollSpeed#MIN_VALUE},{@link
53 | * net.thecodersbreakfast.lp4j.api.ScrollSpeed#MAX_VALUE}].
54 | * @return The ScrollSpeed instance
55 | * @throws java.lang.IllegalArgumentException If the requested speed is out of acceptable range.
56 | */
57 | public static ScrollSpeed of(int speed) {
58 | if (speed < MIN_VALUE || speed > MAX_VALUE) {
59 | throw new IllegalArgumentException("Invalid speed value : " + speed + ". Acceptable values are in range [1..7].");
60 | }
61 | return CACHE[speed - MIN_VALUE];
62 | }
63 |
64 | /** The speed value. */
65 | private final int scrollSpeed;
66 |
67 | /**
68 | * Constructor
69 | *
70 | * @param scrollSpeed The scrolling speed
71 | */
72 | private ScrollSpeed(int scrollSpeed) {
73 | this.scrollSpeed = scrollSpeed;
74 | }
75 |
76 | /**
77 | * Returns the speed value.
78 | *
79 | * @return the speed value
80 | */
81 | public int getScrollSpeed() {
82 | return scrollSpeed;
83 | }
84 |
85 | @Override
86 | public boolean equals(Object o) {
87 | if (this == o) {
88 | return true;
89 | }
90 | if (o == null || getClass() != o.getClass()) {
91 | return false;
92 | }
93 | ScrollSpeed that = (ScrollSpeed) o;
94 | return scrollSpeed == that.scrollSpeed;
95 | }
96 |
97 | @Override
98 | public int hashCode() {
99 | return scrollSpeed;
100 | }
101 | }
102 |
--------------------------------------------------------------------------------
/lp4j-api/src/main/java/net/thecodersbreakfast/lp4j/api/Pad.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.thecodersbreakfast.lp4j.api;
18 |
19 | /**
20 | * Represents a square pad on the Launchpad.
21 | *
22 | * {@code Pad} instances are immutable and cached.
23 | *
24 | * @author Olivier Croisier (olivier.croisier@gmail.com)
25 | */
26 | public class Pad {
27 |
28 | /** Minimal X coordinate for a pad */
29 | public static final int X_MIN = 0;
30 | /** Maximal X coordinate for a pad */
31 | public static final int X_MAX = 7;
32 | /** Minimal Y coordinate for a pad */
33 | public static final int Y_MIN = 0;
34 | /** Maximal Y coordinate for a pad */
35 | public static final int Y_MAX = 7;
36 |
37 | /** Cache of all pads */
38 | private static final Pad[][] PADS = new Pad[8][8];
39 |
40 | static {
41 | for (int i = X_MIN; i <= X_MAX; i++) {
42 | for (int j = Y_MIN; j <= Y_MAX; j++) {
43 | PADS[i][j] = new Pad(i, j);
44 | }
45 | }
46 | }
47 |
48 | /**
49 | * Factory method.
50 | *
51 | * @param x The X coordinate of the pad. Must be in range [{@link Pad#X_MIN},{@link Pad#X_MAX}].
52 | * @param y The Y coordinate of the pad. Must be in range [{@link Pad#Y_MIN},{@link Pad#Y_MAX}].
53 | * @return The pad.
54 | * @throws java.lang.IllegalArgumentException If the coordinates are invalid.
55 | */
56 | public static Pad at(int x, int y) {
57 | if (x < X_MIN || x > X_MAX || y < Y_MIN || y > Y_MAX) {
58 | throw new IllegalArgumentException("Illegal pad coordinates : (" + x + "," + y + "). Acceptable values are in [0..7] on both axis.");
59 | }
60 | return PADS[x][y];
61 | }
62 |
63 | /** The X coordinate */
64 | private final int x;
65 | /** The Y coordinate */
66 | private final int y;
67 |
68 | /**
69 | * Constructor.
70 | *
71 | * @param x The X coordinate of the pad.
72 | * @param y The Y coordinate of the pad.
73 | */
74 | private Pad(int x, int y) {
75 | this.x = x;
76 | this.y = y;
77 | }
78 |
79 | /**
80 | * Returns the X coordinate.
81 | *
82 | * @return The X coordinate.
83 | */
84 | public int getX() {
85 | return x;
86 | }
87 |
88 | /**
89 | * Returns the Y coordinate.
90 | *
91 | * @return The Y coordinate
92 | */
93 | public int getY() {
94 | return y;
95 | }
96 |
97 | @Override
98 | public boolean equals(Object o) {
99 | if (this == o) {
100 | return true;
101 | }
102 | if (o == null || getClass() != o.getClass()) {
103 | return false;
104 | }
105 | Pad pad = (Pad) o;
106 | return x == pad.x && y == pad.y;
107 | }
108 |
109 | @Override
110 | public int hashCode() {
111 | int result = x;
112 | result = 31 * result + y;
113 | return result;
114 | }
115 |
116 | @Override
117 | public String toString() {
118 | return "Pad[" + x + ',' + y + ']';
119 | }
120 | }
121 |
--------------------------------------------------------------------------------
/lp4j-midi/src/main/java/net/thecodersbreakfast/lp4j/midi/protocol/DefaultMidiProtocolListener.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.thecodersbreakfast.lp4j.midi.protocol;
18 |
19 | import net.thecodersbreakfast.lp4j.api.Button;
20 | import net.thecodersbreakfast.lp4j.api.LaunchpadListener;
21 | import net.thecodersbreakfast.lp4j.api.Pad;
22 |
23 | /**
24 | * Parses low-level messages and notifies a high-level {@link net.thecodersbreakfast.lp4j.api.LaunchpadListener}.
25 | *
26 | * @author Olivier Croisier (olivier.croisier@gmail.com)
27 | */
28 | public class DefaultMidiProtocolListener implements MidiProtocolListener {
29 |
30 | /** The high-level LaunchpadListener to notify. */
31 | private final LaunchpadListener listener;
32 |
33 | /**
34 | * Constructor.
35 | *
36 | * @param listener The high-level LaunchpadListener to notify.
37 | */
38 | public DefaultMidiProtocolListener(LaunchpadListener listener) {
39 | this.listener = listener;
40 | }
41 |
42 | /** {@inheritDoc} */
43 | @Override
44 | public void onNoteOn(int note, long timestamp) {
45 | if (listener == null) {
46 | return;
47 | }
48 | int x = note % 16;
49 | int y = note / 16;
50 | if (x >= 8) {
51 | Button button = Button.atRight(y);
52 | listener.onButtonPressed(button, timestamp);
53 | } else {
54 | if (x < 0 || y < 0 || y > 7) {
55 | throw new IllegalArgumentException("Invalid pad coordinates : (" + x + "," + y + "). Acceptable values on either axis are in range [0..7].");
56 | }
57 | listener.onPadPressed(Pad.at(x, y), timestamp);
58 | }
59 | }
60 |
61 | /** {@inheritDoc} */
62 | @Override
63 | public void onNoteOff(int note, long timestamp) {
64 | if (listener == null) {
65 | return;
66 | }
67 | int x = note % 16;
68 | int y = note / 16;
69 | if (x >= 8) {
70 | Button button = Button.atRight(y);
71 | listener.onButtonReleased(button, timestamp);
72 | } else {
73 | if (x < 0 || y < 0 || y > 7) {
74 | throw new IllegalArgumentException("Invalid pad coordinates : (" + x + "," + y + "). Acceptable values on either axis are in range [0..7].");
75 | }
76 | listener.onPadReleased(Pad.at(x, y), timestamp);
77 | }
78 | }
79 |
80 | /** {@inheritDoc} */
81 | @Override
82 | public void onButtonOn(int note, long timestamp) {
83 | if (listener == null) {
84 | return;
85 | }
86 | int value = note - 104;
87 | Button button = Button.atTop(value);
88 | listener.onButtonPressed(button, timestamp);
89 | }
90 |
91 | /** {@inheritDoc} */
92 | @Override
93 | public void onButtonOff(int note, long timestamp) {
94 | if (listener == null) {
95 | return;
96 | }
97 | int value = note - 104;
98 | Button button = Button.atTop(value);
99 | listener.onButtonReleased(button, timestamp);
100 | }
101 |
102 | /** {@inheritDoc} */
103 | @Override
104 | public void onTextScrolled(long timestamp) {
105 | listener.onTextScrolled(timestamp);
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/lp4j-api/src/main/java/net/thecodersbreakfast/lp4j/api/Brightness.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.thecodersbreakfast.lp4j.api;
18 |
19 | /**
20 | * Describes the level of brightness of the pads and buttons lights.
21 | *
22 | * {@code Brightness} instances are immutable and cached.
23 | *
24 | * @author Olivier Croisier (olivier.croisier@gmail.com)
25 | */
26 | public final class Brightness {
27 |
28 | /** Minimum level of brightness */
29 | public static final int MIN_VALUE = 0;
30 | /** Maximum level of brightness */
31 | public static final int MAX_VALUE = 15;
32 |
33 | /** Cache for all levels of brightness */
34 | private static final Brightness[] CACHE = new Brightness[16];
35 |
36 | static {
37 | for (int i = 0; i < 16; i++) {
38 | CACHE[i] = new Brightness(i);
39 | }
40 | }
41 |
42 | /** Minimum brightness */
43 | public static final Brightness BRIGHTNESS_MIN = of(MIN_VALUE);
44 | /** Maximum brightness */
45 | public static final Brightness BRIGHTNESS_MAX = of(MAX_VALUE);
46 |
47 | /**
48 | * Factory method.
49 | *
50 | * @param brightness The desired level of brightess. Must be in range [{@link net.thecodersbreakfast.lp4j.api.Brightness#MIN_VALUE},{@link
51 | * net.thecodersbreakfast.lp4j.api.Brightness#MAX_VALUE}]
52 | * @return The Brightness instance.
53 | * @throws java.lang.IllegalArgumentException If the requested brightness level is out of acceptable range.
54 | */
55 | public static Brightness of(int brightness) {
56 | if (brightness < MIN_VALUE || brightness > MAX_VALUE) {
57 | throw new IllegalArgumentException("Invalid brightness level : " + brightness + ". Acceptable values are in range [0..15].");
58 | }
59 | return CACHE[brightness - MIN_VALUE];
60 | }
61 |
62 | /** Level of brightness */
63 | private final int brightness;
64 |
65 | /**
66 | * Constructor.
67 | *
68 | * @param brightness The desired level of brightess.
69 | */
70 | private Brightness(int brightness) {
71 | this.brightness = brightness;
72 | }
73 |
74 | /**
75 | * Returns the level of brightness
76 | *
77 | * @return the level of brightness
78 | */
79 | public int getBrightness() {
80 | return brightness;
81 | }
82 |
83 | /**
84 | * Returns a brighter Brightness instance, if the maximum level of brightness is not already reached.
85 | *
86 | * @return a brighter Brightness instance, or the same instance if it was already the brightest.
87 | */
88 | public Brightness more() {
89 | return brightness < MAX_VALUE ? of(brightness + 1) : this;
90 | }
91 |
92 | /**
93 | * Returns a less bright Brightness instance, if the minimum level of brightness is not already reached.
94 | *
95 | * @return a less bright Brightness instance, or the same instance if it was already the least bright.
96 | */
97 | public Brightness less() {
98 | return brightness > MIN_VALUE ? of(brightness - 1) : this;
99 | }
100 |
101 | @Override
102 | public boolean equals(Object o) {
103 | if (this == o) {
104 | return true;
105 | }
106 | if (o == null || getClass() != o.getClass()) {
107 | return false;
108 | }
109 | Brightness that = (Brightness) o;
110 | return brightness == that.brightness;
111 | }
112 |
113 | @Override
114 | public int hashCode() {
115 | return brightness;
116 | }
117 | }
118 |
--------------------------------------------------------------------------------
/lp4j-midi/src/test/java/net.thecodersbreakfast.lp4j.midi/DefaultMidiProtocolReceiverTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.thecodersbreakfast.lp4j.midi;
18 |
19 | import net.thecodersbreakfast.lp4j.api.LaunchpadException;
20 | import net.thecodersbreakfast.lp4j.midi.protocol.DefaultMidiProtocolReceiver;
21 | import net.thecodersbreakfast.lp4j.midi.protocol.MidiProtocolListener;
22 | import org.junit.Before;
23 | import org.junit.Test;
24 | import org.mockito.Mockito;
25 |
26 | import javax.sound.midi.*;
27 |
28 | public class DefaultMidiProtocolReceiverTest {
29 |
30 | private static final int VELOCITY_PRESSED = 127;
31 | private static final int VELOCITY_RELEASED = 0;
32 | private static final int VELOCITY_TEXT_SCROLLED = 3;
33 | private static final long TIMESTAMP = -1;
34 |
35 | private Receiver receiver;
36 | private MidiProtocolListener listener;
37 |
38 | @Before
39 | public void init() {
40 | listener = Mockito.mock(MidiProtocolListener.class);
41 | receiver = new DefaultMidiProtocolReceiver(listener);
42 | }
43 |
44 | @Test(expected = LaunchpadException.class)
45 | public void send_sysexMessage() {
46 | MidiMessage message = new SysexMessage();
47 | receiver.send(message, TIMESTAMP);
48 | }
49 |
50 | @Test(expected = LaunchpadException.class)
51 | public void send_metaMessage() {
52 | MidiMessage message = new MetaMessage();
53 | receiver.send(message, TIMESTAMP);
54 | }
55 |
56 | @Test(expected = LaunchpadException.class)
57 | public void send_shortMessage_unknown() throws Exception {
58 | ShortMessage message = new ShortMessage();
59 | message.setMessage(ShortMessage.STOP, 0, 0);
60 | receiver.send(message, TIMESTAMP);
61 | }
62 |
63 | @Test
64 | public void send_noteOn_pressed() throws Exception {
65 | ShortMessage message = new ShortMessage();
66 | message.setMessage(ShortMessage.NOTE_ON, 42, VELOCITY_PRESSED);
67 | receiver.send(message, TIMESTAMP);
68 | Mockito.verify(listener).onNoteOn(42, TIMESTAMP);
69 | }
70 |
71 | @Test
72 | public void send_noteOn_released() throws Exception {
73 | ShortMessage message = new ShortMessage();
74 | message.setMessage(ShortMessage.NOTE_ON, 42, VELOCITY_RELEASED);
75 | receiver.send(message, TIMESTAMP);
76 | Mockito.verify(listener).onNoteOff(42, TIMESTAMP);
77 | }
78 |
79 | @Test
80 | public void send_controlChange_pressed() throws Exception {
81 | ShortMessage message = new ShortMessage();
82 | message.setMessage(ShortMessage.CONTROL_CHANGE, 42, VELOCITY_PRESSED);
83 | receiver.send(message, TIMESTAMP);
84 | Mockito.verify(listener).onButtonOn(42, TIMESTAMP);
85 | }
86 |
87 | @Test
88 | public void send_controlChange_released() throws Exception {
89 | ShortMessage message = new ShortMessage();
90 | message.setMessage(ShortMessage.CONTROL_CHANGE, 42, VELOCITY_RELEASED);
91 | receiver.send(message, TIMESTAMP);
92 | Mockito.verify(listener).onButtonOff(42, TIMESTAMP);
93 | }
94 |
95 | @Test
96 | public void send_controlChange_textScrolled() throws Exception {
97 | ShortMessage message = new ShortMessage();
98 | message.setMessage(ShortMessage.CONTROL_CHANGE, 0, VELOCITY_TEXT_SCROLLED);
99 | receiver.send(message, TIMESTAMP);
100 | Mockito.verify(listener).onTextScrolled(TIMESTAMP);
101 | }
102 |
103 | @Test
104 | public void close() throws Exception {
105 | receiver.close();
106 | }
107 |
108 |
109 | }
110 |
--------------------------------------------------------------------------------
/lp4j-midi/src/test/java/net.thecodersbreakfast.lp4j.midi/DefaultMidiProtocolListenerTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.thecodersbreakfast.lp4j.midi;
18 |
19 | import net.thecodersbreakfast.lp4j.api.Button;
20 | import net.thecodersbreakfast.lp4j.api.LaunchpadListener;
21 | import net.thecodersbreakfast.lp4j.api.Pad;
22 | import net.thecodersbreakfast.lp4j.midi.protocol.DefaultMidiProtocolListener;
23 | import net.thecodersbreakfast.lp4j.midi.protocol.MidiProtocolListener;
24 | import org.junit.Before;
25 | import org.junit.Test;
26 | import org.mockito.Mockito;
27 |
28 | public class DefaultMidiProtocolListenerTest {
29 |
30 | private static final long TIMESTAMP = -1;
31 |
32 | private static final int BUTTON_UP = 104;
33 | private static final int BUTTON_UNKNOWN = 100;
34 | private static final int NOTE_VOL = 8;
35 | private static final int NOTE_PAD00 = 0;
36 | private static final int NOTE_UNKNOWN = -1;
37 |
38 | private MidiProtocolListener midiProtocolListener;
39 | private LaunchpadListener listener;
40 |
41 | @Before
42 | public void init() {
43 | this.listener = Mockito.mock(LaunchpadListener.class);
44 | this.midiProtocolListener = new DefaultMidiProtocolListener(listener);
45 | }
46 |
47 | @Test
48 | public void onButtonOn_up() {
49 | midiProtocolListener.onButtonOn(BUTTON_UP, TIMESTAMP);
50 | Mockito.verify(listener).onButtonPressed(Button.UP, TIMESTAMP);
51 | }
52 |
53 | @Test(expected = IllegalArgumentException.class)
54 | public void onButtonOn_unknown() {
55 | midiProtocolListener.onButtonOn(BUTTON_UNKNOWN, TIMESTAMP);
56 | Mockito.verify(listener).onButtonPressed(Button.UP, TIMESTAMP);
57 | }
58 |
59 | @Test
60 | public void onButtonOff_up() {
61 | midiProtocolListener.onButtonOff(BUTTON_UP, TIMESTAMP);
62 | Mockito.verify(listener).onButtonReleased(Button.UP, TIMESTAMP);
63 | }
64 |
65 | @Test(expected = IllegalArgumentException.class)
66 | public void onButtonOff_unknown() {
67 | midiProtocolListener.onButtonOff(BUTTON_UNKNOWN, TIMESTAMP);
68 | Mockito.verify(listener).onButtonReleased(Button.UP, TIMESTAMP);
69 | }
70 |
71 | @Test
72 | public void onNoteOn_vol() {
73 | midiProtocolListener.onNoteOn(NOTE_VOL, TIMESTAMP);
74 | Mockito.verify(listener).onButtonPressed(Button.VOL, TIMESTAMP);
75 | }
76 |
77 | @Test
78 | public void onNoteOff_vol() {
79 | midiProtocolListener.onNoteOff(NOTE_VOL, TIMESTAMP);
80 | Mockito.verify(listener).onButtonReleased(Button.VOL, TIMESTAMP);
81 | }
82 |
83 | @Test
84 | public void onNoteOn_pad00() {
85 | midiProtocolListener.onNoteOn(NOTE_PAD00, TIMESTAMP);
86 | Mockito.verify(listener).onPadPressed(Pad.at(0, 0), TIMESTAMP);
87 | }
88 |
89 | @Test
90 | public void onNoteOff_pad00() {
91 | midiProtocolListener.onNoteOff(NOTE_PAD00, TIMESTAMP);
92 | Mockito.verify(listener).onPadReleased(Pad.at(0, 0), TIMESTAMP);
93 | }
94 |
95 | @Test(expected = IllegalArgumentException.class)
96 | public void onNoteOn_unknown() {
97 | midiProtocolListener.onNoteOn(NOTE_UNKNOWN, TIMESTAMP);
98 | Mockito.verify(listener).onPadPressed(Pad.at(0, 0), TIMESTAMP);
99 | }
100 |
101 | @Test(expected = IllegalArgumentException.class)
102 | public void onNoteOff_unknown() {
103 | midiProtocolListener.onNoteOff(NOTE_UNKNOWN, TIMESTAMP);
104 | Mockito.verify(listener).onPadReleased(Pad.at(0, 0), TIMESTAMP);
105 | }
106 |
107 | @Test
108 | public void onTextScrolled() {
109 | midiProtocolListener.onTextScrolled(TIMESTAMP);
110 | Mockito.verify(listener).onTextScrolled(TIMESTAMP);
111 | }
112 |
113 |
114 | }
115 |
--------------------------------------------------------------------------------
/lp4j-emu-web/src/main/resources/web/Launchpad.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // Abstract Launchpad definition.
18 | function Launchpad() {
19 | this.colors = [
20 | ["#CCC", "#060", "#0C2", "#0F4"],
21 | ["#600", "#660", "#6C2", "#6F4"],
22 | ["#C00", "#C60", "#CC2", "#CF4"],
23 | ["#F00", "#F60", "#FC2", "#FF4"]
24 | ];
25 | this.buffers = new Array(2);
26 | this.visibleBuffer = 0;
27 | this.writeBuffer = 0;
28 | this.backBuffer = 1;
29 | this.brightness = 1;
30 | this.listener = null;
31 | this.reset();
32 | }
33 |
34 | Launchpad.prototype.setListener = function (launchpadListener) {
35 | this.listener = launchpadListener;
36 | };
37 |
38 | // ----------------------------------------
39 | // Client methods
40 | // ----------------------------------------
41 |
42 | Launchpad.prototype.reset = function () {
43 | this.buffers[0] = new Array(9);
44 | this.buffers[1] = new Array(9);
45 | for (var i = 0; i < 9; i++) {
46 | this.buffers[0][i] = new Array(9);
47 | this.buffers[1][i] = new Array(9);
48 | }
49 | };
50 |
51 | Launchpad.prototype.testLights = function (intensity) {
52 | this.setBrightness(intensity);
53 | var self = this;
54 | this.buffers[this.writeBuffer].map(function (a) {
55 | a.fill(self.colors[3][3]);
56 | });
57 | };
58 |
59 | Launchpad.prototype.setLights = function (colors, operation) {
60 | // Not implemented yet
61 | };
62 |
63 | Launchpad.prototype.setPadLight = function (x, y, color, operation) {
64 | this.buffers[this.writeBuffer][x][y + 1] = this.colors[color.r][color.g];
65 | switch (operation) {
66 | case 'NONE' :
67 | break;
68 | case 'COPY' :
69 | this.buffers[this.backBuffer][x][y + 1] = this.colors[color.r][color.g];
70 | break;
71 | case 'CLEAR' :
72 | this.buffers[this.backBuffer][x][y + 1] = this.colors[0][0];
73 | break;
74 | }
75 | };
76 |
77 | Launchpad.prototype.setButtonLight = function (t, i, color, operation) {
78 | var x = 0;
79 | var y = 0;
80 | if (t === true) {
81 | x = i;
82 | y = -1;
83 | } else {
84 | x = 8;
85 | y = i;
86 | }
87 | this.setPadLight(x, y, color, operation);
88 | };
89 |
90 | Launchpad.prototype.setBrightness = function (level) {
91 | this.brightness = 0.1 + 0.06 * level;
92 | };
93 |
94 | Launchpad.prototype.setBuffers = function (visibleBuffer, writeBuffer, copyVisibleBufferToWriteBuffer, autoSwap) {
95 | this.visibleBuffer = this.bufIdx(visibleBuffer);
96 | this.writeBuffer = this.bufIdx(writeBuffer);
97 | this.backBuffer = 1 - this.writeBuffer;
98 | if (copyVisibleBufferToWriteBuffer === true) {
99 | this.buffers[this.writeBuffer] = this.buffers[this.visibleBuffer].clone;
100 | }
101 | };
102 |
103 | Launchpad.prototype.bufIdx = function (buffer) {
104 | return (buffer === 'BUFFER_0' ? 0 : 1);
105 | };
106 |
107 | Launchpad.prototype.scrollText = function (text, color, speed, loop, operation) {
108 | // Not implemented yet
109 | };
110 |
111 | // ----------------------------------------
112 | // Listener Methods
113 | // ----------------------------------------
114 |
115 | // Definition of a listener allowing to be notified when the user interacts with the Launchpad interface
116 | function LaunchpadListener() {
117 | }
118 |
119 | LaunchpadListener.prototype.onPadPressed = function (x, y) {
120 | };
121 |
122 | LaunchpadListener.prototype.onPadReleased = function (x, y) {
123 | };
124 |
125 | LaunchpadListener.prototype.onButtonPressed = function (x, y) {
126 | };
127 |
128 | LaunchpadListener.prototype.onButtonReleased = function (x, y) {
129 | };
130 |
131 | LaunchpadListener.prototype.onTextScrolled = function () {
132 | };
133 |
134 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | LP4J - A Launchpad API for Java !
2 | ====
3 |
4 | [](https://travis-ci.org/OlivierCroisier/LP4J)
5 |
6 | Launchpad is a midi device built by Novation, used to drive computer-assisted music creation applications such as Ableton Live.
7 |
8 | The device is a square board with a 8x8 pads grid, surrounded by round command buttons. Pads and buttons can be lit in various tints of yellow, red and green.
9 | This makes the Launchpad an interesting general-purpose I/O device, where a user can press pads and buttons to send commands to an application, and provide feedback with its 8x8 matrix of colored leds.
10 |
11 | 
12 |
13 | LP4J is a lightweight Java API allowing any Java application to interact with a Launchpad S (second generation).
14 | It also provides a embedded, web-based emulator to allow developers to design and test applications should they not have access to a physical device.
15 |
16 | The API is split in 3 submodules :
17 | - lp4j-api : an abstract, high-level API defining how to interact with a Launchpad (LaunchpadClient / LaunchpadListener)
18 | - lp4j-midi : a MIDI implementation of the API, suitable to interact with a physical device.
19 | - lp4j-emulator : a web-based emulator, using SVG and websockets.
20 |
21 | How to use LP4J
22 | ====
23 |
24 | From a developper's perspective, all interaction with LP4J are done through the high-level API.
25 |
26 | First, you need to get a reference to a Launchpad :
27 |
28 | ```java
29 | // Physical device (with auto-detected ports configuration)
30 | Launchpad launchpad = new MidiLaunchpad(MidiDeviceConfiguration.autodetect());
31 |
32 | // Or start the emulator on http://localhost:9000
33 | Launchpad launchpad = new EmulatorLaunchpad(9000);
34 | ```
35 |
36 | From this Launchpad instance, you can :
37 | - retrieve a LaunchpadClient, used to send commands TO the device or emulator (mostly to turn on/off the pads or buttons lights),
38 | - set up a LaunchpadListener to react to events.
39 |
40 | ```java
41 | LaunchpadClient client = launchpad.getClient();
42 | launchpad.setListener(new MyListener());
43 | ```
44 |
45 | A very simple debugging listener can look like this :
46 |
47 | ```java
48 | public static class MyListener extends LaunchpadListenerAdapter {
49 |
50 | @Override
51 | public void onPadPressed(Pad pad, long timestamp) {
52 | System.out.println("Pad pressed : "+pad);
53 | }
54 |
55 | }
56 | ```
57 |
58 | A more complex example
59 | ====
60 |
61 | Listeners can be more complex.
62 | For example, the following Listener sends commands back to the Launchpad to put a yellow light under pads when they are pressed, lighting them off when they are released.
63 |
64 | Please also note the use of a CountDownLatch to wait for the user to press the "STOP" button before exiting the application.
65 |
66 | ```java
67 | public class Example {
68 |
69 | private static CountDownLatch stop = new CountDownLatch(1);
70 |
71 | public static void main(String[] args) throws Exception {
72 |
73 | Launchpad launchpad = new EmulatorLaunchpad(9000);
74 | LaunchpadClient client = launchpad.getClient();
75 |
76 | MyListener myListener = new MyListener(client);
77 | launchpad.setListener(myListener);
78 |
79 | // Set a red light under the STOP button
80 | client.reset();
81 | client.setButtonLight(Button.STOP, Color.RED, BackBufferOperation.NONE);
82 |
83 | stop.await();
84 | client.reset();
85 | launchpad.close();
86 | }
87 |
88 | public static class MyListener extends LaunchpadListenerAdapter {
89 |
90 | private final LaunchpadClient client;
91 |
92 | public MyListener(LaunchpadClient client) {
93 | this.client = client;
94 | }
95 |
96 | @Override
97 | public void onPadPressed(Pad pad, long timestamp) {
98 | client.setPadLight(pad, Color.YELLOW, BackBufferOperation.NONE);
99 | }
100 |
101 | @Override
102 | public void onPadReleased(Pad pad, long timestamp) {
103 | client.setPadLight(pad, Color.BLACK, BackBufferOperation.NONE);
104 | }
105 |
106 | @Override
107 | public void onButtonReleased(Button button, long timestamp) {
108 | client.setButtonLight(button, Color.BLACK, BackBufferOperation.NONE);
109 | switch (button) {
110 | case STOP:
111 | stop.countDown();
112 | break;
113 | }
114 | }
115 | }
116 |
117 | }
118 | ```
119 |
--------------------------------------------------------------------------------
/lp4j-api/src/main/java/net/thecodersbreakfast/lp4j/api/LaunchpadClient.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.thecodersbreakfast.lp4j.api;
18 |
19 | /**
20 | * Represents a client through which the API user can send commands to a particular Launchpad implementation. Usually
21 | * obtained by calling {@link Launchpad#getClient()}.
22 | *
23 | * @author Olivier Croisier (olivier.croisier@gmail.com)
24 | */
25 | public interface LaunchpadClient {
26 |
27 | /**
28 | * Reset the state of the Launchpad.
29 | */
30 | public void reset();
31 |
32 | /**
33 | * Lights up all the pads and buttons with the given light intensity.
34 | *
35 | * @param intensity The desired light intensity.
36 | */
37 | public void testLights(LightIntensity intensity);
38 |
39 | /**
40 | * Bulk-set the colors of all pads and buttons.
41 | *
42 | * The length of colors array passed as a parameter may vary. Lights will be lit line by line, starting from the
43 | * upper-left pad down to the bottom-right pad, then under the upper-row buttons, and finally under the right-side
44 | * buttons.
45 | *
46 | * @param colors The colors to be set
47 | * @param operation What to do on the backbuffer
48 | */
49 | public void setLights(Color[] colors, BackBufferOperation operation);
50 |
51 | /**
52 | * Lights up the given pad with the given color.
53 | *
54 | * @param pad The pad to light up.
55 | * @param color The color to use. Use {@link net.thecodersbreakfast.lp4j.api.Color#BLACK} to switch the light off.
56 | * @param operation What to do on the backbuffer
57 | */
58 | public void setPadLight(Pad pad, Color color, BackBufferOperation operation);
59 |
60 | /**
61 | * Lights up the given button with the given color.
62 | *
63 | * @param button The button to light up.
64 | * @param color The color to use. Use {@link net.thecodersbreakfast.lp4j.api.Color#BLACK} to switch the light off.
65 | * @param operation What to do on the backbuffer
66 | */
67 | public void setButtonLight(Button button, Color color, BackBufferOperation operation);
68 |
69 | /**
70 | * Set the overall brightness of the pad and button lights.
71 | *
72 | * @param brightness The desired brightness.
73 | */
74 | public void setBrightness(Brightness brightness);
75 |
76 | /**
77 | * Sets the current write and visible buffers.
78 | *
79 | * @param visibleBuffer The buffer to display
80 | * @param writeBuffer The buffer to which the commands are applied.
81 | * @param copyVisibleBufferToWriteBuffer Tells if the visible buffer state should be copied to the write buffer.
82 | * This operation occurs after the new write and visible buffers have been set.
83 | * @param autoSwap Set to {@code true} to make the visible buffer quickly swap between the two buffers. This can be
84 | * used to create a blinking effect. Set back to {@code false} to stop the blinking.
85 | */
86 | public void setBuffers(Buffer visibleBuffer, Buffer writeBuffer, boolean copyVisibleBufferToWriteBuffer, boolean autoSwap);
87 |
88 | /**
89 | * Starts scrolling a text across the Launchpad, using the 8x8 pad grid as a font grid. Beware, this operation may
90 | * be blocking !
91 | *
92 | * After the text has been displayed (or after each loop if {@code loop} is set to {@code true}), the listener's
93 | * {@link net.thecodersbreakfast.lp4j.api.LaunchpadListener#onTextScrolled(long)} is notified.
94 | *
95 | * @param text The text to display.
96 | * @param color The color to use
97 | * @param speed The speed to use
98 | * @param loop Tells if the text should loop endlessly. In that case, set to {@code false} to stop the scrolling.
99 | * @param operation What to do on the backbuffer
100 | */
101 | public void scrollText(String text, Color color, ScrollSpeed speed, boolean loop, BackBufferOperation operation);
102 | }
103 |
--------------------------------------------------------------------------------
/lp4j-midi/src/main/java/net/thecodersbreakfast/lp4j/midi/protocol/DefaultMidiProtocolReceiver.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.thecodersbreakfast.lp4j.midi.protocol;
18 |
19 | import net.thecodersbreakfast.lp4j.api.LaunchpadException;
20 |
21 | import javax.sound.midi.MidiMessage;
22 | import javax.sound.midi.Receiver;
23 | import javax.sound.midi.ShortMessage;
24 |
25 | /**
26 | * A MIDI Receiver, to which the Launchpad sends low-level commands.
27 | * Those commands are parsed and transmitted (still in a close-to-the-metal format) to a
28 | * {@link net.thecodersbreakfast.lp4j.midi.protocol.MidiProtocolListener} for further processing.
29 | *
30 | * @author Olivier Croisier (olivier.croisier@gmail.com)
31 | */
32 | public class DefaultMidiProtocolReceiver implements Receiver {
33 |
34 | /** The MidiProtocolListener to to notify when commands are received. */
35 | private MidiProtocolListener midiProtocolListener;
36 |
37 | /**
38 | * Constructor.
39 | *
40 | * @param listener The MidiProtocolListener to to notify when commands are received. Must not be null.
41 | */
42 | public DefaultMidiProtocolReceiver(MidiProtocolListener listener) {
43 | if (listener == null) {
44 | throw new IllegalArgumentException("Listener must not be null.");
45 | }
46 | this.midiProtocolListener = listener;
47 | }
48 |
49 | /**
50 | * {@inheritDoc}
51 | *
52 | * THIS METHOD SHOULD ONLY BE CALLED BY THE LAUNCHPAD DEVICE.
53 | */
54 | @Override
55 | public void send(MidiMessage message, long timestamp) {
56 | if (message instanceof ShortMessage) {
57 | handleShortMessage((ShortMessage) message, timestamp);
58 | } else {
59 | throw new LaunchpadException("Unknown event : " + message);
60 | }
61 | }
62 |
63 | /**
64 | * Parses and routes MIDI short messages to adequate sub-handlers.
65 | *
66 | * @param message The inconming message.
67 | * @param timestamp When the message arrived.
68 | */
69 | protected void handleShortMessage(ShortMessage message, long timestamp) {
70 | int status = message.getStatus();
71 | int note = message.getData1();
72 | int velocity = message.getData2();
73 |
74 | if (status == ShortMessage.NOTE_ON) {
75 | handleNoteOnMessage(note, velocity, timestamp);
76 | } else if (status == ShortMessage.CONTROL_CHANGE) {
77 | handleControlChangeMessage(note, velocity, timestamp);
78 | } else {
79 | throw new LaunchpadException("Unknown event : " + message);
80 | }
81 | }
82 |
83 | /**
84 | * Parses "note on" messages and notifies the to the higher-level {@code midiProtocolListener}
85 | *
86 | * @param note The activated note.
87 | * @param velocity The note velocity.
88 | * @param timestamp When the note was activated.
89 | */
90 | protected void handleNoteOnMessage(int note, int velocity, long timestamp) {
91 | if (velocity == 0) {
92 | midiProtocolListener.onNoteOff(note, timestamp);
93 | } else {
94 | midiProtocolListener.onNoteOn(note, timestamp);
95 | }
96 | }
97 |
98 | /**
99 | * Parses "control" messages and notifies the to the higher-level {@code midiProtocolListener}
100 | *
101 | * @param note The activated note.
102 | * @param velocity The note velocity.
103 | * @param timestamp When the note was activated.
104 | */
105 | protected void handleControlChangeMessage(int note, int velocity, long timestamp) {
106 | if (note == 0 && velocity == 3) {
107 | midiProtocolListener.onTextScrolled(timestamp);
108 | } else if (velocity == 0) {
109 | midiProtocolListener.onButtonOff(note, timestamp);
110 | } else {
111 | midiProtocolListener.onButtonOn(note, timestamp);
112 | }
113 | }
114 |
115 | @Override
116 | public void close() {
117 | }
118 | }
119 |
--------------------------------------------------------------------------------
/lp4j-api/src/main/java/net/thecodersbreakfast/lp4j/api/Color.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.thecodersbreakfast.lp4j.api;
18 |
19 | /**
20 | * Represents the color of a pad or button on the Launchpad. The Launchpad has a red light and a green light under each
21 | * pad or button. The actual color is obtained by adjusting their respective intensities.
22 | *
23 | * {@code Color} instances are immutable and cached.
24 | *
25 | * @author Olivier Croisier (olivier.croisier@gmail.com)
26 | */
27 | public final class Color {
28 |
29 | /** Minimal red or green component intensity */
30 | public static final int MIN_INTENSITY = 0;
31 | /** Maximal red or green component intensity */
32 | public static final int MAX_INTENSITY = 3;
33 |
34 | // Color cache
35 | private static final Color[][] CACHE = new Color[4][4];
36 |
37 | static {
38 | for (int r = 0; r < 4; r++) {
39 | for (int g = 0; g < 4; g++) {
40 | CACHE[r][g] = new Color(r, g);
41 | }
42 | }
43 | }
44 |
45 | // Most used colors
46 | /** Black (red 0, green 0) */
47 | public static final Color BLACK = CACHE[0][0];
48 | /** Red (red 3, green 0) */
49 | public static final Color RED = CACHE[3][0];
50 | /** Green (red 0, green 3) */
51 | public static final Color GREEN = CACHE[0][3];
52 | /** Orange (red 3, green 2) */
53 | public static final Color ORANGE = CACHE[3][2];
54 | /** Amber (red 3, green 3) */
55 | public static final Color AMBER = CACHE[3][3];
56 | /** Yellow (red 2, green 3) */
57 | public static final Color YELLOW = CACHE[2][3];
58 |
59 | /**
60 | * Factory method
61 | *
62 | * @param red The red component. Acceptable values are in [{@link net.thecodersbreakfast.lp4j.api.Color#MIN_INTENSITY},{@link
63 | * net.thecodersbreakfast.lp4j.api.Color#MAX_INTENSITY}].
64 | * @param green The green component. Acceptable values are in [{@link net.thecodersbreakfast.lp4j.api.Color#MIN_INTENSITY},{@link
65 | * net.thecodersbreakfast.lp4j.api.Color#MAX_INTENSITY}].
66 | * @return The Color obtained by mixing the given red and green values.
67 | * @throws java.lang.IllegalArgumentException If the red or green parameters are out of acceptable range.
68 | */
69 | public static Color of(int red, int green) {
70 | if (red < MIN_INTENSITY || red > MAX_INTENSITY) {
71 | throw new IllegalArgumentException("Invalid red value : " + red + ". Acceptable values are in range [0..3].");
72 | }
73 | if (green < MIN_INTENSITY || green > MAX_INTENSITY) {
74 | throw new IllegalArgumentException("Invalid green value : " + green + ". Acceptable values are in range [0..3].");
75 | }
76 | return CACHE[red][green];
77 | }
78 |
79 | /** The red component intensity */
80 | private final int red;
81 | /** The green component intensity */
82 | private final int green;
83 |
84 | /**
85 | * Constructor
86 | *
87 | * @param red The red component
88 | * @param green The green component
89 | */
90 | private Color(int red, int green) {
91 | this.red = red;
92 | this.green = green;
93 | }
94 |
95 | /**
96 | * Returns the red intensity
97 | *
98 | * @return the red intensity
99 | */
100 | public int getRed() {
101 | return red;
102 | }
103 |
104 | /**
105 | * Returns the green intensity
106 | *
107 | * @return the green intensity
108 | */
109 | public int getGreen() {
110 | return green;
111 | }
112 |
113 | @Override
114 | public boolean equals(Object o) {
115 | if (this == o) {
116 | return true;
117 | }
118 | if (o == null || getClass() != o.getClass()) {
119 | return false;
120 | }
121 | Color color = (Color) o;
122 | return green == color.green && red == color.red;
123 | }
124 |
125 | @Override
126 | public int hashCode() {
127 | int result = red;
128 | result = 31 * result + green;
129 | return result;
130 | }
131 | }
132 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
18 |
19 | Requires both an inbound and outbound MidiDevices, because data is sent from (pad events) and to (lights, etc.)
27 | * the Launchpad.
28 | *
29 | * In most cases, it should suffice to call the {@link MidiDeviceConfiguration#autodetect()} factory method to get
30 | * a working configuration.
31 | *
32 | * @author Olivier Croisier (olivier.croisier@gmail.com)
33 | */
34 | public class MidiDeviceConfiguration {
35 |
36 | /** Device signature of a Launchpad S, used for autodetection. */
37 | public static final String DEVICE_SIGNATURE = "Launchpad S";
38 |
39 | /** Inbound communication channel. */
40 | private final MidiDevice inputDevice;
41 | /** Outbound communication channel. */
42 | private final MidiDevice outputDevice;
43 |
44 | /**
45 | * Tries to auto-detect a MIDI Launchpad based on its device signature.
46 | *
47 | * @return The auto-detected configuration.
48 | * @throws MidiUnavailableException If an error occurs during device probing.
49 | */
50 | public static MidiDeviceConfiguration autodetect() throws MidiUnavailableException {
51 | MidiDevice inputDevice = autodetectInputDevice();
52 | MidiDevice outputDevice = autodetectOutputDevice();
53 | return new MidiDeviceConfiguration(inputDevice, outputDevice);
54 | }
55 |
56 | /**
57 | * Constructor.
58 | *
59 | * @param inputDevice The inbound Midi channel.
60 | * @param outputDevice The outbound midi channel.
61 | */
62 | public MidiDeviceConfiguration(MidiDevice inputDevice, MidiDevice outputDevice) {
63 | this.inputDevice = inputDevice;
64 | this.outputDevice = outputDevice;
65 | }
66 |
67 | /**
68 | * Returns the inbound communication channel.
69 | *
70 | * @return the inbound communication channel.
71 | */
72 | public MidiDevice getInputDevice() {
73 | return inputDevice;
74 | }
75 |
76 | /**
77 | * Returns the outbound communication channel.
78 | *
79 | * @return the outbound communication channel.
80 | */
81 | public MidiDevice getOutputDevice() {
82 | return outputDevice;
83 | }
84 |
85 | /**
86 | * Tries to detect a valid outbound communication channel, based on a known device signature
87 | * (see {@link net.thecodersbreakfast.lp4j.midi.MidiDeviceConfiguration#DEVICE_SIGNATURE}).
88 | *
89 | * @return A valid outbound communication channel, or {@code null} if non was found.
90 | * @throws MidiUnavailableException if the requested device is not available due to resource restrictions
91 | */
92 | public static MidiDevice autodetectOutputDevice() throws MidiUnavailableException {
93 | MidiDevice.Info[] midiDeviceInfo = MidiSystem.getMidiDeviceInfo();
94 | for (MidiDevice.Info info : midiDeviceInfo) {
95 | if (info.getDescription().contains(DEVICE_SIGNATURE) || info.getName().contains(DEVICE_SIGNATURE)) {
96 | MidiDevice device = MidiSystem.getMidiDevice(info);
97 | if (device.getMaxReceivers() == -1) {
98 | return device;
99 | }
100 | }
101 | }
102 | return null;
103 | }
104 |
105 | /**
106 | * Tries to detect a valid inbound communication channel, based on a known device signature
107 | * (see {@link net.thecodersbreakfast.lp4j.midi.MidiDeviceConfiguration#DEVICE_SIGNATURE}).
108 | *
109 | * @return A valid outbound communication channel, or {@code null} if non was found.
110 | * @throws MidiUnavailableException if the requested device is not available due to resource restrictions
111 | */
112 | public static MidiDevice autodetectInputDevice() throws MidiUnavailableException {
113 | MidiDevice.Info[] midiDeviceInfo = MidiSystem.getMidiDeviceInfo();
114 | for (MidiDevice.Info info : midiDeviceInfo) {
115 | if (info.getDescription().contains(DEVICE_SIGNATURE) || info.getName().contains(DEVICE_SIGNATURE)) {
116 | MidiDevice device = MidiSystem.getMidiDevice(info);
117 | if (device.getMaxTransmitters() == -1) {
118 | return device;
119 | }
120 | device.close();
121 | }
122 | }
123 | return null;
124 | }
125 |
126 | }
127 |
--------------------------------------------------------------------------------
/lp4j-midi/src/main/java/net/thecodersbreakfast/lp4j/midi/MidiLaunchpad.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.thecodersbreakfast.lp4j.midi;
18 |
19 | import net.thecodersbreakfast.lp4j.api.Launchpad;
20 | import net.thecodersbreakfast.lp4j.api.LaunchpadClient;
21 | import net.thecodersbreakfast.lp4j.api.LaunchpadException;
22 | import net.thecodersbreakfast.lp4j.api.LaunchpadListener;
23 | import net.thecodersbreakfast.lp4j.midi.protocol.DefaultMidiProtocolClient;
24 | import net.thecodersbreakfast.lp4j.midi.protocol.DefaultMidiProtocolListener;
25 | import net.thecodersbreakfast.lp4j.midi.protocol.DefaultMidiProtocolReceiver;
26 | import net.thecodersbreakfast.lp4j.midi.protocol.MidiProtocolListener;
27 |
28 | import javax.sound.midi.MidiDevice;
29 | import javax.sound.midi.MidiUnavailableException;
30 | import javax.sound.midi.Receiver;
31 | import javax.sound.midi.Transmitter;
32 | import java.io.IOException;
33 |
34 | /**
35 | * Represents a physical MIDI Launchpad device.
36 | *
37 | * @author Olivier Croisier (olivier.croisier@gmail.com)
38 | */
39 | public class MidiLaunchpad implements Launchpad {
40 |
41 | /** The Launchpad's input channel (Device -> LP4J). */
42 | private final Receiver receiver;
43 | /** The Launchpad's output channel (LP4J -> Device). */
44 | private final Transmitter transmitter;
45 | /** The MIDI configuration holder. */
46 | private MidiDeviceConfiguration configuration;
47 |
48 | /** Indicates that the output channel has been successfully opened. */
49 | private boolean openedOutputDevice = false;
50 | /** Indicates that the input channel has been successfully opened. */
51 | private boolean openedInputDevice = false;
52 |
53 | /**
54 | * Constructor.
55 | *
56 | * @param configuration The MIDI configuration to use. Must not be null.
57 | * @throws MidiUnavailableException If the input or output channels cannot be opened.
58 | */
59 | public MidiLaunchpad(MidiDeviceConfiguration configuration) throws MidiUnavailableException {
60 | if (configuration == null) {
61 | throw new IllegalArgumentException("Configuration must not be null");
62 | }
63 | this.configuration = configuration;
64 |
65 | MidiDevice outputDevice = configuration.getOutputDevice();
66 | if (outputDevice != null) {
67 | if (!outputDevice.isOpen()) {
68 | outputDevice.open();
69 | }
70 | openedOutputDevice = true;
71 | this.receiver = outputDevice.getReceiver();
72 | } else {
73 | this.receiver = null;
74 | }
75 |
76 | MidiDevice inputDevice = configuration.getInputDevice();
77 | if (inputDevice != null) {
78 | if (!inputDevice.isOpen()) {
79 | inputDevice.open();
80 | }
81 | openedInputDevice = true;
82 | this.transmitter = inputDevice.getTransmitter();
83 | } else {
84 | this.transmitter = null;
85 |
86 | }
87 | }
88 |
89 | /** {@inheritDoc} */
90 | @Override
91 | public LaunchpadClient getClient() {
92 | if (this.receiver == null) {
93 | throw new LaunchpadException("Unable to provide a client, because no Receiver or Output Device have been configured.");
94 | }
95 | return new MidiLaunchpadClient(new DefaultMidiProtocolClient(this.receiver));
96 | }
97 |
98 | /** {@inheritDoc} */
99 | @Override
100 | public void setListener(LaunchpadListener listener) {
101 | if (transmitter == null) {
102 | throw new LaunchpadException("Unable to set the listener, because no Transmitter or Input Device have been configured.");
103 | }
104 | MidiProtocolListener midiProtocolListener = new DefaultMidiProtocolListener(listener);
105 | Receiver midiReceiver = new DefaultMidiProtocolReceiver(midiProtocolListener);
106 | transmitter.setReceiver(midiReceiver);
107 | }
108 |
109 | /** {@inheritDoc} */
110 | @Override
111 | public void close() throws IOException {
112 | if (configuration == null) {
113 | return;
114 | }
115 | if (openedOutputDevice) {
116 | MidiDevice outputDevice = configuration.getOutputDevice();
117 | if (outputDevice != null && outputDevice.isOpen()) {
118 | outputDevice.close();
119 | }
120 | }
121 | if (openedInputDevice) {
122 | MidiDevice inputDevice = configuration.getInputDevice();
123 | if (inputDevice != null && inputDevice.isOpen()) {
124 | inputDevice.close();
125 | }
126 | }
127 | }
128 |
129 | }
130 |
--------------------------------------------------------------------------------
/lp4j-midi/src/main/java/net/thecodersbreakfast/lp4j/midi/protocol/MidiProtocolClient.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.thecodersbreakfast.lp4j.midi.protocol;
18 |
19 | import javax.sound.midi.InvalidMidiDataException;
20 |
21 | /**
22 | * A low-level client used to send raw MIDI commands to a physical Launchpad device.
23 | *
24 | * Please note that some that the Launchpad considers some of the (round) buttons as (square) pads, whereas some
25 | * other buttons are treated as, well, buttons. This explains some oddities in the method signatures.
26 | *
27 | * For the full specification and details about the meaning of each command and their acceptable parameter values,
28 | * please refer to Novation's online documentation.
29 | *
30 | * @author Olivier Croisier (olivier.croisier@gmail.com)
31 | */
32 | public interface MidiProtocolClient {
33 |
34 | /**
35 | * Sends a "reset" command.
36 | *
37 | * @throws InvalidMidiDataException If a MIDI communication error occurs.
38 | */
39 | void reset() throws InvalidMidiDataException;
40 |
41 | /**
42 | * Sends a "lights on" command, lightning all the lights at once. Useful to test the LEDs.
43 | *
44 | * @param intensity The desired light intensity.
45 | * @throws InvalidMidiDataException If a MIDI communication error occurs.
46 | */
47 | void lightsOn(int intensity) throws InvalidMidiDataException;
48 |
49 | /**
50 | * Sends a "note on" command, to light up the LED under a pad or button.
51 | *
52 | * @param note The "note" identifying the target pad or button.
53 | * @param color The color to display.
54 | * @throws InvalidMidiDataException If a MIDI communication error occurs.
55 | */
56 | void noteOn(int note, int color) throws InvalidMidiDataException;
57 |
58 | /**
59 | * Sends a "note off" command, to turn off the LED under a pad or button.
60 | *
61 | * @param note The "note" identifying the target pad or button.
62 | * @throws InvalidMidiDataException If a MIDI communication error occurs.
63 | */
64 | void noteOff(int note) throws InvalidMidiDataException;
65 |
66 | /**
67 | * A batch version of the "light on" command. Multiple lights can be set at once, starting from the upper-left one.
68 | *
69 | * @param colors The colors to display.
70 | * @throws InvalidMidiDataException If a MIDI communication error occurs.
71 | */
72 | void notesOn(int... colors) throws InvalidMidiDataException;
73 |
74 | /**
75 | * Toggles between a X-Y layout and "note-oriented" one.
76 | *
77 | * @param mode The layout to switch to.
78 | * @throws InvalidMidiDataException If a MIDI communication error occurs.
79 | */
80 | void layout(int mode) throws InvalidMidiDataException;
81 |
82 | /**
83 | * Sends a "button on" command, to light up the LED under a button.
84 | *
85 | * @param button The button to light up.
86 | * @param color The desired color.
87 | * @throws InvalidMidiDataException If a MIDI communication error occurs.
88 | */
89 | void buttonOn(int button, int color) throws InvalidMidiDataException;
90 |
91 | /**
92 | * Sets the overall brightness of the LEDs, expressed as a ratio between a numerator and a denominator.
93 | *
94 | * @param numerator The numerator.
95 | * @param denominator The denominator.
96 | * @throws InvalidMidiDataException If a MIDI communication error occurs.
97 | */
98 | void brightness(int numerator, int denominator) throws InvalidMidiDataException;
99 |
100 | /**
101 | * Scrolls the given text across the board, using an embedded 8x8 dot matrix font.
102 | *
103 | * @param text The text to display.
104 | * @param color The text color.
105 | * @param speed The scrolling speed.
106 | * @param loop Whether to display the text once, or to loop until a new "text" command is emitted.
107 | * @throws InvalidMidiDataException If a MIDI communication error occurs.
108 | */
109 | void text(String text, int color, int speed, boolean loop) throws InvalidMidiDataException;
110 |
111 | /**
112 | * Sets which buffer is written to, and which one is currently displayed (can be the same).
113 | * The "autoswap" parameter allows a "blinking" effect, where the Launchpad keeps swapping the visible and
114 | * non-visible buffers until autoswapping is turned off again.
115 | *
116 | * @param visibleBuffer The buffer to set as the visible buffer.
117 | * @param writeBuffer The buffer to set as the write buffer.
118 | * @param copyVisibleBufferToWriteBuffer Whether the visible buffer should be copied to the back buffer during the
119 | * process.
120 | * @param autoSwap Whether to trigger the "blinking" effect. Set to {@code false} to disable blinking.
121 | * @throws InvalidMidiDataException If a MIDI communication error occurs.
122 | */
123 | void doubleBufferMode(int visibleBuffer, int writeBuffer, boolean copyVisibleBufferToWriteBuffer, boolean autoSwap) throws InvalidMidiDataException;
124 | }
125 |
--------------------------------------------------------------------------------
/lp4j-api/src/main/java/net/thecodersbreakfast/lp4j/api/Button.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.thecodersbreakfast.lp4j.api;
18 |
19 | /**
20 | * Represents the Launchpad's 16 round buttons (8 at the top, 8 on the right side)
21 | *
22 | * @author Olivier Croisier (olivier.croisier@gmail.com)
23 | */
24 | public enum Button {
25 |
26 | /** The UP button (1st position from the left, on the top row) */
27 | UP(0, true),
28 | /** The DOWN button (2nd position from the left, on the top row) */
29 | DOWN(1, true),
30 | /** The LEFT button (3rd position from the left, on the top row) */
31 | LEFT(2, true),
32 | /** The RIGHT button (4th position from the left, on the top row) */
33 | RIGHT(3, true),
34 | /** The SESSION button (5th position from the left, on the top row) */
35 | SESSION(4, true),
36 | /** The USER_1 button (6th position from the left, on the top row) */
37 | USER_1(5, true),
38 | /** The USER_2 button (7th position from the left, on the top row) */
39 | USER_2(6, true),
40 | /** The MIXER button (8th position from the left, on the top row) */
41 | MIXER(7, true),
42 | /** The VOL button (1st position from the top, on the right side) */
43 | VOL(0, false),
44 | /** The PAN button (2nd position from the top, on the right side) */
45 | PAN(1, false),
46 | /** The SND_A button (3rd position from the top, on the right side) */
47 | SND_A(2, false),
48 | /** The SND_B button (4th position from the top, on the right side) */
49 | SND_B(3, false),
50 | /** The STOP button (5th position from the top, on the right side) */
51 | STOP(4, false),
52 | /** The TRACK_ON button (6th position from the top, on the right side) */
53 | TRACK_ON(5, false),
54 | /** The SOLO button (7th position from the top, on the right side) */
55 | SOLO(6, false),
56 | /** The ARM button (8th position from the top, on the right side) */
57 | ARM(7, false);
58 |
59 | /** Minimal coordinate for a button */
60 | public static final int MIN_COORD = 0;
61 | /** Maximal coordinate for a button */
62 | public static final int MAX_COORD = 7;
63 |
64 | /** Top-row buttons, in left-to-right order */
65 | public static final Button[] BUTTONS_TOP = {UP, DOWN, LEFT, RIGHT, SESSION, USER_1, USER_2, MIXER};
66 | /** Right-side buttons, in top-to-bottom order */
67 | public static final Button[] BUTTONS_RIGHT = {VOL, PAN, SND_A, SND_B, STOP, TRACK_ON, SOLO, ARM};
68 |
69 | /**
70 | * Factory method for a top-row button.
71 | *
72 | * @param isTopButton {@code true} if the button is on the top row, {@code false} if it is on the right side
73 | * @param c The coordinate of the button. Must be in range [{@link Button#MIN_COORD},{@link Button#MAX_COORD}].
74 | * @return The button
75 | * @throws IllegalArgumentException if the coordinates are invalid.
76 | */
77 | private static Button at(boolean isTopButton, int c) {
78 | if (c < MIN_COORD || c > MAX_COORD) {
79 | throw new IllegalArgumentException(String.format("Invalid button coordinates : %d. Value must be between %d and %d inclusive.", c, MIN_COORD, MAX_COORD));
80 | }
81 | return isTopButton ? BUTTONS_TOP[c] : BUTTONS_RIGHT[c];
82 | }
83 |
84 | /**
85 | * Factory method for a top-row button.
86 | *
87 | * @param c The coordinate of the button. Must be in range [{@link Button#MIN_COORD},{@link Button#MAX_COORD}].
88 | * @return The button
89 | * @throws IllegalArgumentException if the coordinates are invalid.
90 | */
91 | public static Button atTop(int c) {
92 | return at(true, c);
93 | }
94 |
95 | /**
96 | * Factory method for a top-row button.
97 | *
98 | * @param c The coordinate of the button. Must be in range [{@link Button#MIN_COORD},{@link Button#MAX_COORD}].
99 | * @return The button
100 | * @throws IllegalArgumentException if the coordinates are invalid.
101 | */
102 | public static Button atRight(int c) {
103 | return at(false, c);
104 | }
105 |
106 | /** The button coordinates */
107 | private final int coordinate;
108 | /** Tells if it is a top-row button ({@code true}), as opposed to a righ-side button ({@code false}) */
109 | private final boolean topButton;
110 |
111 | /**
112 | * Constructor.
113 | *
114 | * @param coordinate The coordinate of the button.
115 | * @param topButton {@code true} for a top-row button, {@code false} otherwise.
116 | */
117 | private Button(int coordinate, boolean topButton) {
118 | this.coordinate = coordinate;
119 | this.topButton = topButton;
120 | }
121 |
122 | /**
123 | * Returns the button coordinate.
124 | *
125 | * @return The coordinate.
126 | */
127 | public int getCoordinate() {
128 | return coordinate;
129 | }
130 |
131 | /**
132 | * Tells if this button is located on the top row.
133 | *
134 | * @return {@code true} if this button belongs to the top row, {@code false} otherwise.
135 | */
136 | public boolean isTopButton() {
137 | return topButton;
138 | }
139 |
140 | /**
141 | * Tells if this button is located on the right side.
142 | *
143 | * @return {@code true} if this button belongs to the right side, {@code false} otherwise.
144 | */
145 | public boolean isRightButton() {
146 | return !topButton;
147 | }
148 |
149 | @Override
150 | public String toString() {
151 | return String.format("Button[%s(%s,%d)]", name(), topButton ? "top" : "right", coordinate);
152 | }
153 | }
154 |
--------------------------------------------------------------------------------
/lp4j-midi/src/main/java/net/thecodersbreakfast/lp4j/midi/protocol/DefaultMidiProtocolClient.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.thecodersbreakfast.lp4j.midi.protocol;
18 |
19 | import net.thecodersbreakfast.lp4j.api.LaunchpadException;
20 |
21 | import javax.sound.midi.*;
22 | import java.nio.charset.Charset;
23 |
24 | /**
25 | * Default implementation of a {@link net.thecodersbreakfast.lp4j.midi.protocol.MidiProtocolClient}.
26 | *
27 | * @author Olivier Croisier (olivier.croisier@gmail.com)
28 | */
29 | public class DefaultMidiProtocolClient implements MidiProtocolClient {
30 |
31 | /** The Launchpad's Receiver, to which commands are sent. */
32 | private final Receiver receiver;
33 |
34 | /**
35 | * Constructor.
36 | *
37 | * @param receiver The Launchpad's MIDI Receiver. Must not be null.
38 | */
39 | public DefaultMidiProtocolClient(Receiver receiver) {
40 | if (receiver == null) {
41 | throw new IllegalArgumentException("Receiver must not be null.");
42 | }
43 | this.receiver = receiver;
44 | }
45 |
46 | // ================================================================================
47 | // Low-level MIDI output API
48 | // ================================================================================
49 |
50 | /** {@inheritDoc} */
51 | @Override
52 | public void reset() throws InvalidMidiDataException {
53 | sendShortMessage(ShortMessage.CONTROL_CHANGE, 0, 0);
54 | }
55 |
56 | /** {@inheritDoc} */
57 | @Override
58 | public void lightsOn(int intensity) throws InvalidMidiDataException {
59 | sendShortMessage(ShortMessage.CONTROL_CHANGE, 0, intensity);
60 | }
61 |
62 | /** {@inheritDoc} */
63 | @Override
64 | public void noteOn(int note, int color) throws InvalidMidiDataException {
65 | sendShortMessage(ShortMessage.NOTE_ON, note, color);
66 | }
67 |
68 | /** {@inheritDoc} */
69 | @Override
70 | public void noteOff(int note) throws InvalidMidiDataException {
71 | sendShortMessage(ShortMessage.NOTE_OFF, note, 0);
72 | }
73 |
74 | /**
75 | * {@inheritDoc}
76 | *
77 | * @param colors {@inheritDoc} Must not be null.
78 | */
79 | @Override
80 | public void notesOn(int... colors) throws InvalidMidiDataException {
81 | if (colors == null) {
82 | throw new IllegalArgumentException("Colors should not be null.");
83 | }
84 | int nbMessages = colors.length / 2;
85 | for (int i = 0; i < nbMessages; i++) {
86 | sendShortMessage(ShortMessage.NOTE_ON, 3, colors[i * 2], colors[i * 2 + 1]);
87 | }
88 | }
89 |
90 | /** {@inheritDoc} */
91 | @Override
92 | public void layout(int mode) throws InvalidMidiDataException {
93 | sendShortMessage(ShortMessage.CONTROL_CHANGE, 0, mode);
94 | }
95 |
96 | /** {@inheritDoc} */
97 | @Override
98 | public void buttonOn(int button, int color) throws InvalidMidiDataException {
99 | sendShortMessage(ShortMessage.CONTROL_CHANGE, button, color);
100 | }
101 |
102 | /** {@inheritDoc} */
103 | @Override
104 | public void brightness(int numerator, int denominator) throws InvalidMidiDataException {
105 | if (numerator < 9) {
106 | sendShortMessage(ShortMessage.CONTROL_CHANGE, 30, 16 * (numerator - 1) + (denominator - 3));
107 | } else {
108 | sendShortMessage(ShortMessage.CONTROL_CHANGE, 31, 16 * (numerator - 9) + (denominator - 3));
109 | }
110 | }
111 |
112 | /** {@inheritDoc} */
113 | @Override
114 | public void text(String text, int color, int speed, boolean loop) throws InvalidMidiDataException {
115 | if (loop) {
116 | color += 64;
117 | }
118 | byte[] header = {(byte) 240, 0, 32, 41, 9, (byte) color};
119 | byte[] chars = text == null ? new byte[]{} : text.getBytes(Charset.forName("ASCII"));
120 | byte[] message = new byte[chars.length + 8];
121 | System.arraycopy(header, 0, message, 0, header.length);
122 | message[header.length] = (byte) speed;
123 | System.arraycopy(chars, 0, message, header.length + 1, chars.length);
124 | message[message.length - 1] = (byte) 247;
125 | sendSysExMessage(message);
126 | }
127 |
128 | /** {@inheritDoc} */
129 | @Override
130 | public void doubleBufferMode(int displayBuffer, int writeBuffer, boolean copyVisibleBufferToWriteBuffer, boolean autoSwap) throws InvalidMidiDataException {
131 | int mode = 32 + 4 * writeBuffer + displayBuffer;
132 | if (copyVisibleBufferToWriteBuffer) {
133 | mode |= 16;
134 | }
135 | if (autoSwap) {
136 | mode |= 8;
137 | }
138 | sendShortMessage(ShortMessage.CONTROL_CHANGE, 0, mode);
139 | }
140 |
141 |
142 | // ================================================================================
143 | // Utils
144 | // ================================================================================
145 |
146 | private void sendShortMessage(int command, int controller, int data) throws LaunchpadException, InvalidMidiDataException {
147 | ShortMessage message = new ShortMessage();
148 | message.setMessage(command, controller, data);
149 | send(message);
150 | }
151 |
152 | private void sendShortMessage(int command, int channel, int controller, int data) throws LaunchpadException, InvalidMidiDataException {
153 | ShortMessage message = new ShortMessage();
154 | message.setMessage(command, channel, controller, data);
155 | send(message);
156 | }
157 |
158 | private void sendSysExMessage(byte[] data) throws InvalidMidiDataException {
159 | SysexMessage message = new SysexMessage();
160 | message.setMessage(data, data.length);
161 | send(message);
162 | }
163 |
164 | private void send(MidiMessage message) {
165 | this.receiver.send(message, -1);
166 | }
167 |
168 | }
169 |
--------------------------------------------------------------------------------
/lp4j-emu-web/src/main/resources/web/emulator.js:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | // The standart SVG namespace
18 | var SVGNS = "http://www.w3.org/2000/svg";
19 |
20 | // The Launchpad emulator
21 | var launchpad;
22 |
23 | // The Vertx event bus
24 | var eventbus;
25 |
26 | // Eventbus IDs of the client (browser-side) and server (application-side) emulator parts
27 | var CLIENT_EVENTBUS_ID = 'lp4j:client';
28 | var SERVER_EVENTBUS_ID = 'lp4j:server';
29 |
30 |
31 | // Sends the given message on the event bus, to the server-side part of the emulator
32 | function sendToServer(params) {
33 | if (eventbus) {
34 | eventbus.send(SERVER_EVENTBUS_ID, params);
35 | }
36 | }
37 |
38 |
39 | // Initializes the launchpad instance and its listener
40 | function configureLaunchpad() {
41 |
42 | var listener = new LaunchpadListener();
43 | listener.onPadPressed = function (x, y) {
44 | sendToServer({"evt": "PP", "x": x, "y": y});
45 | };
46 | listener.onPadReleased = function (x, y) {
47 | sendToServer({"evt": "PR", "x": x, "y": y});
48 | };
49 | listener.onButtonPressed = function (x, y) {
50 | sendToServer({"evt": "BP", "x": x, "y": y});
51 | };
52 | listener.onButtonReleased = function (x, y) {
53 | sendToServer({"evt": "BR", "x": x, "y": y});
54 | };
55 |
56 | launchpad = new Launchpad();
57 | launchpad.setListener(listener);
58 | }
59 |
60 |
61 | // Handles commands send to the launchpad
62 | function handleClientCommand(event) {
63 | var eventType = event.evt;
64 | // Event types defined in EmulatorClient$OutputEventType enum
65 | switch (eventType) {
66 | case "RST" :
67 | launchpad.reset();
68 | break;
69 | case "PADLGT" :
70 | launchpad.setPadLight(event.x, event.y, event.c, event.o);
71 | break;
72 | case "BTNLGT" :
73 | launchpad.setButtonLight(event.t, event.i, event.c, event.o);
74 | break;
75 | case "BRGHT" :
76 | launchpad.setBrightness(event.b);
77 | break;
78 | case "BUF" :
79 | launchpad.setBuffers(event.v, event.w, event.c, event.a);
80 | break;
81 | case "TST" :
82 | launchpad.testLights(event.i);
83 | break;
84 | }
85 | }
86 |
87 |
88 | $(document).ready(function () {
89 |
90 | configureLaunchpad();
91 | initDisplay(document.getElementById("launchpad"));
92 |
93 | eventbus = new vertx.EventBus('/eventbus');
94 | eventbus.onopen = function () {
95 | eventbus.registerHandler(CLIENT_EVENTBUS_ID, function (event) {
96 | handleClientCommand(event);
97 | updateDisplay(launchpad);
98 | });
99 | }
100 |
101 | });
102 |
103 |
104 | // Creates the emulator SVG display
105 | function initDisplay(container) {
106 |
107 | // Root SVG node
108 | var svg = document.createElementNS(SVGNS, "svg");
109 | svg.setAttribute("xmlns", SVGNS);
110 | svg.setAttribute("viewBox", "0 0 110 110"); // 10px per pad/button, 2px gutter
111 | svg.setAttribute("preserveAspectRatio", "xMinYMin meet");
112 | container.appendChild(svg);
113 |
114 | // Launchpad background
115 | var bg = document.createElementNS(SVGNS, "rect");
116 | bg.setAttribute("width", "110");
117 | bg.setAttribute("height", "110");
118 | bg.setAttribute("fill", "#444");
119 | bg.setAttribute("stroke", "#000");
120 | bg.setAttribute("stroke-width", "2");
121 | svg.appendChild(bg);
122 |
123 | var listener = launchpad.listener || new LaunchpadListener();
124 |
125 | var btnNames = ["^", "v", "<", ">", "SES", "USR1", "USR2", "MIX", "VOL", "PAN", "SNDA", "SNDB", "STOP","TRCK", "SOLO", "ARM"];
126 |
127 | for (var x = 0; x < 9; x++) {
128 | for (var y = 0; y < 9; y++) {
129 | if (x == 8 && y == 0) continue;
130 | var pad;
131 |
132 | // Round buttons on top and right sides
133 | if (x == 8 || y == 0) {
134 | var centerX = (2 + 12 * x + 5).toString();
135 | var centerY = (2 + 12 * y + 5).toString();
136 | pad = document.createElementNS(SVGNS, "circle");
137 | pad.setAttribute("cx", centerX);
138 | pad.setAttribute("cy", centerY);
139 | pad.setAttribute("r", "4");
140 | pad.onmousedown = (function (pX, pY) {
141 | return function (e) {
142 | e.preventDefault();
143 | if (pX == 8) pX = -1;
144 | listener.onButtonPressed(pX, pY - 1);
145 | }
146 | })(x, y);
147 | pad.onmouseup = (function (pX, pY) {
148 | return function (e) {
149 | e.preventDefault();
150 | // Ctrl-clicking skips the "button released" event
151 | if (e.ctrlKey) return;
152 | if (pX == 8) pX = -1;
153 | listener.onButtonReleased(pX, pY - 1);
154 | }
155 | })(x, y);
156 | pad.setAttribute("id", "key" + x + y);
157 | pad.style.fill = launchpad.colors[0][0];
158 | svg.appendChild(pad);
159 |
160 | // Button text
161 | var btnName;
162 | if (y==0) {
163 | btnName = btnNames[x]
164 | } else {
165 | btnName = btnNames[7+y];
166 | }
167 | var txtEl = document.createElementNS(SVGNS, "text");
168 | txtEl.setAttribute("text-anchor","middle");
169 | txtEl.setAttribute("x",centerX);
170 | txtEl.setAttribute("y",centerY);
171 | txtEl.setAttribute("font-size","12%");
172 | txtEl.setAttribute("font-family","sans-serif");
173 | txtEl.setAttribute("font-weight","bold");
174 | txtEl.setAttribute("pointer-events","none");
175 | var txt = document.createTextNode(btnName);
176 | txtEl.appendChild(txt);
177 | svg.appendChild(txtEl);
178 | }
179 |
180 | // Square pads
181 | else {
182 | pad = document.createElementNS(SVGNS, "rect");
183 | pad.setAttribute("x", (2 + 12 * x).toString());
184 | pad.setAttribute("y", (2 + 12 * y).toString());
185 | pad.setAttribute("width", "10");
186 | pad.setAttribute("height", "10");
187 | pad.onmousedown = (function (pX, pY) {
188 | return function (e) {
189 | e.preventDefault();
190 | listener.onPadPressed(pX, pY - 1);
191 | }
192 | })(x, y);
193 | pad.onmouseup = (function (pX, pY) {
194 | return function (e) {
195 | e.preventDefault();
196 | // Ctrl-clicking skips the "button released" event
197 | if (e.ctrlKey) return;
198 | listener.onPadReleased(pX, pY - 1);
199 | }
200 | })(x, y);
201 | pad.setAttribute("id", "key" + x + y);
202 | pad.style.fill = launchpad.colors[0][0];
203 | svg.appendChild(pad);
204 | }
205 | }
206 | }
207 | }
208 |
209 |
210 | // Updates the SVG display with the latest Launchpad state
211 | function updateDisplay(launchpad) {
212 | for (var x = 0; x < 9; x++) {
213 | for (var y = 0; y < 9; y++) {
214 | if (x == 8 && y == 0) continue;
215 | var color = launchpad.buffers[launchpad.visibleBuffer][x][y] || launchpad.colors[0][0];
216 | var shape = document.getElementById("key" + x + y);
217 | // Do not apply brightness correction to non-lit pads and buttons
218 | shape.style.opacity = (color == launchpad.colors[0][0]) ? 1 : launchpad.brightness;
219 | shape.style.fill = color;
220 | }
221 | }
222 | }
223 |
--------------------------------------------------------------------------------
/lp4j-emu-web/src/main/java/net/thecodersbreakfast/lp4j/emulator/EmulatorLaunchpadClient.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.thecodersbreakfast.lp4j.emulator;
18 |
19 | import net.thecodersbreakfast.lp4j.api.*;
20 | import org.vertx.java.core.Vertx;
21 | import org.vertx.java.core.json.JsonObject;
22 |
23 | /**
24 | * A client to communicate with the Launchpad emulator
25 | *
26 | * @author Olivier Croisier (olivier.croisier@gmail.com)
27 | */
28 | public class EmulatorLaunchpadClient implements LaunchpadClient {
29 |
30 | /** Eventbus ID of the emulator, on the browser side */
31 | private static final String EVENTBUS_CLIENT_HANDLER_ID = "lp4j:client";
32 |
33 | /** Types of events sent to the emulator, on the browser side */
34 | private static enum OutputEventType {
35 | /** Reset */
36 | RST,
37 | /** Padlight */
38 | PADLGT,
39 | /** Button light */
40 | BTNLGT,
41 | /** Test */
42 | TST,
43 | /** Brightness */
44 | BRGHT,
45 | /** SetBuffers */
46 | BUF
47 | }
48 |
49 | /** The Vertx engine that powers the emulator on the server side */
50 | private final Vertx vertx;
51 |
52 | /**
53 | * Constructor
54 | *
55 | * @param vertx The Vertx engine to use
56 | */
57 | public EmulatorLaunchpadClient(Vertx vertx) {
58 | this.vertx = vertx;
59 | }
60 |
61 | @Override
62 | public void reset() {
63 | publishEvent(OutputEventType.RST);
64 | }
65 |
66 | /**
67 | * {@inheritDoc}
68 | *
69 | * @param intensity The desired light intensity. Must not be null.
70 | */
71 | @Override
72 | public void testLights(LightIntensity intensity) {
73 | if (intensity == null) {
74 | throw new IllegalArgumentException("Light intensity must not be null.");
75 | }
76 | int brightness = 0;
77 | if (intensity == LightIntensity.LOW) {
78 | brightness = 5;
79 | }
80 | if (intensity == LightIntensity.MEDIUM) {
81 | brightness = 10;
82 | }
83 | if (intensity == LightIntensity.HIGH) {
84 | brightness = 15;
85 | }
86 | JsonObject params = new JsonObject()
87 | .putNumber("i", brightness);
88 | publishEvent(OutputEventType.TST, params);
89 | }
90 |
91 | /**
92 | * {@inheritDoc}
93 | *
94 | * NOT IMPLEMENTED YET ON THE EMULATOR
95 | */
96 | @Override
97 | public void setLights(Color[] colors, BackBufferOperation operation) {
98 | if (colors == null) {
99 | throw new IllegalArgumentException("Colors must not be null");
100 | }
101 | if (operation == null) {
102 | throw new IllegalArgumentException("BackBuffer operation must not be null.");
103 | }
104 | }
105 |
106 | /**
107 | * {@inheritDoc}
108 | *
109 | * @param pad The pad to light up. Must not be null.
110 | * @param color The color to use. Use {@link net.thecodersbreakfast.lp4j.api.Color#BLACK} to switch the light off. Must not be null.
111 | * @param operation What to do on the backbuffer. Must not be null.
112 | */
113 | @Override
114 | public void setPadLight(Pad pad, Color color, BackBufferOperation operation) {
115 | if (pad == null) {
116 | throw new IllegalArgumentException("Pad must not be null");
117 | }
118 | if (color == null) {
119 | throw new IllegalArgumentException("Color must not be null.");
120 | }
121 | if (operation == null) {
122 | throw new IllegalArgumentException("BackBuffer operation must not be null.");
123 | }
124 | JsonObject params = new JsonObject()
125 | .putNumber("x", pad.getX())
126 | .putNumber("y", pad.getY())
127 | .putObject("c", new JsonObject().putNumber("r", color.getRed()).putNumber("g", color.getGreen()))
128 | .putString("o", operation.name());
129 | publishEvent(OutputEventType.PADLGT, params);
130 | }
131 |
132 | /**
133 | * {@inheritDoc}
134 | *
135 | * @param button The button to light up. Must not be null.
136 | * @param color The color to use. Use {@link net.thecodersbreakfast.lp4j.api.Color#BLACK} to switch the light off. Must not be null.
137 | * @param operation What to do on the backbuffer. Must not be null.
138 | */
139 | @Override
140 | public void setButtonLight(Button button, Color color, BackBufferOperation operation) {
141 | if (button == null) {
142 | throw new IllegalArgumentException("Button must not be null.");
143 | }
144 | if (color == null) {
145 | throw new IllegalArgumentException("Color must not be null.");
146 | }
147 | if (operation == null) {
148 | throw new IllegalArgumentException("BackBuffer operation must not be null.");
149 | }
150 | JsonObject params = new JsonObject()
151 | .putBoolean("t", button.isTopButton())
152 | .putNumber("i", button.getCoordinate())
153 | .putObject("c", new JsonObject().putNumber("r", color.getRed()).putNumber("g", color.getGreen()))
154 | .putString("o", operation.name());
155 | publishEvent(OutputEventType.BTNLGT, params);
156 | }
157 |
158 | /**
159 | * {@inheritDoc}
160 | *
161 | * @param brightness The desired brightness. Must not be null.
162 | */
163 | @Override
164 | public void setBrightness(Brightness brightness) {
165 | if (brightness == null) {
166 | throw new IllegalArgumentException("Brightness must not be null");
167 | }
168 | JsonObject params = new JsonObject()
169 | .putNumber("b", brightness.getBrightness());
170 | publishEvent(OutputEventType.BRGHT, params);
171 | }
172 |
173 | /**
174 | * {@inheritDoc}
175 | *
176 | * @param visibleBuffer The buffer to display. Must not be null.
177 | * @param writeBuffer The buffer to which the commands are applied. Must not be null.
178 | */
179 | @Override
180 | public void setBuffers(Buffer visibleBuffer, Buffer writeBuffer, boolean copyVisibleBufferToWriteBuffer, boolean autoSwap) {
181 | if (visibleBuffer == null) {
182 | throw new IllegalArgumentException("Visible buffer must not be null.");
183 | }
184 | if (writeBuffer == null) {
185 | throw new IllegalArgumentException("Write buffer must not be null.");
186 | }
187 | JsonObject params = new JsonObject()
188 | .putString("v", visibleBuffer.name())
189 | .putString("w", writeBuffer.name())
190 | .putBoolean("c", copyVisibleBufferToWriteBuffer)
191 | .putBoolean("a", autoSwap);
192 | publishEvent(OutputEventType.BUF, params);
193 | }
194 |
195 | /**
196 | * {@inheritDoc}
197 | *
198 | * NOT IMPLEMENTED YET ON THE EMULATOR
199 | */
200 | @Override
201 | public void scrollText(String text, Color color, ScrollSpeed speed, boolean loop, BackBufferOperation operation) {
202 | if (color == null) {
203 | throw new IllegalArgumentException("Color must not be null.");
204 | }
205 | }
206 |
207 | /**
208 | * Sends the given event to the emulator, with no additional parameters
209 | *
210 | * @param outputEventType The event to send
211 | */
212 | private void publishEvent(OutputEventType outputEventType) {
213 | publishEvent(outputEventType, null);
214 | }
215 |
216 | /**
217 | * Sends the given event to the emulator, with the given additional parameters
218 | *
219 | * @param outputEventType The event to send
220 | * @param params The additional parameters
221 | */
222 | private void publishEvent(OutputEventType outputEventType, JsonObject params) {
223 | JsonObject payload = new JsonObject();
224 | payload.putString("evt", outputEventType.name());
225 | if (params != null) {
226 | payload.mergeIn(params);
227 | }
228 | vertx.eventBus().publish(EVENTBUS_CLIENT_HANDLER_ID, payload);
229 | }
230 |
231 | }
232 |
--------------------------------------------------------------------------------
/lp4j-emu-web/src/main/java/net/thecodersbreakfast/lp4j/emulator/EmulatorLaunchpad.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.thecodersbreakfast.lp4j.emulator;
18 |
19 | import net.thecodersbreakfast.lp4j.api.*;
20 | import org.vertx.java.core.Handler;
21 | import org.vertx.java.core.Vertx;
22 | import org.vertx.java.core.VertxFactory;
23 | import org.vertx.java.core.buffer.Buffer;
24 | import org.vertx.java.core.eventbus.Message;
25 | import org.vertx.java.core.http.HttpServer;
26 | import org.vertx.java.core.http.HttpServerRequest;
27 | import org.vertx.java.core.http.HttpServerResponse;
28 | import org.vertx.java.core.json.JsonArray;
29 | import org.vertx.java.core.json.JsonObject;
30 |
31 | import java.io.ByteArrayOutputStream;
32 | import java.io.IOException;
33 | import java.io.InputStream;
34 |
35 | /**
36 | * A web-based (HTML/SVG/Sebsockets) Launchpad emulator.
37 | *
38 | * @author Olivier Croisier (olivier.croisier@gmail.com)
39 | */
40 | public class EmulatorLaunchpad implements Launchpad {
41 |
42 | /** Directory to serve static files from. */
43 | public static final String WEB_RESOURCES_PREFIX = "/web";
44 | /** URL of the Vertx eventbus bridge */
45 | public static final String EVENTBUS_ADDRESS = "/eventbus";
46 | /** Eventbus ID of the emulator, on the server side */
47 | public static final String EVENTBUS_SERVER_HANDLER_ID = "lp4j:server";
48 | /** Handler for Vertx eventbus messages. */
49 | private final EventBusHandler eventBusHandler = new EventBusHandler();
50 | /** Vertx engine instance. */
51 | private final Vertx vertx;
52 |
53 | /**
54 | * Constructor.
55 | *
56 | * @param httpPort The HTTP port on which the emulator should run.
57 | */
58 | public EmulatorLaunchpad(int httpPort) {
59 |
60 | vertx = VertxFactory.newVertx();
61 |
62 | // Static files
63 | HttpServer httpServer = vertx.createHttpServer();
64 | httpServer.requestHandler(new WebResourceHandler());
65 |
66 | // Eventbus bridge
67 | JsonObject bridgeConfig = new JsonObject().putString("prefix", EVENTBUS_ADDRESS);
68 | JsonArray credentialsPermitAll = new JsonArray().add(new JsonObject());
69 | vertx.createSockJSServer(httpServer).bridge(bridgeConfig, credentialsPermitAll, credentialsPermitAll);
70 | vertx.eventBus().registerLocalHandler(EVENTBUS_SERVER_HANDLER_ID, eventBusHandler);
71 |
72 | System.out.println("Launchpad emulator is ready on http://localhost:" + httpPort + "/");
73 | httpServer.listen(httpPort);
74 | }
75 |
76 | /** {@inheritDoc} */
77 | @Override
78 | public LaunchpadClient getClient() {
79 | return new EmulatorLaunchpadClient(vertx);
80 | }
81 |
82 | /** {@inheritDoc} */
83 | @Override
84 | public void setListener(LaunchpadListener listener) {
85 | this.eventBusHandler.setListener(listener);
86 | }
87 |
88 | /** {@inheritDoc} */
89 | @Override
90 | public void close() throws IOException {
91 | vertx.stop();
92 | }
93 |
94 | /**
95 | * Handler for standart HTTP requests, used to serve static files.
96 | */
97 | private static class WebResourceHandler implements Handler This class serves as an adapter between the high-level LP4J API and the low-level MIDI communication layer
28 | *
29 | * @author Olivier Croisier (olivier.croisier@gmail.com)
30 | */
31 | public class MidiLaunchpadClient implements LaunchpadClient {
32 |
33 | /** Low-level MIDI client to communicate with the Launchpad. */
34 | private final MidiProtocolClient midiProtocolClient;
35 |
36 | /**
37 | * Constructor.
38 | *
39 | * @param midiProtocolClient The low-level client to adapt. Must not be null.
40 | */
41 | public MidiLaunchpadClient(MidiProtocolClient midiProtocolClient) {
42 | if (midiProtocolClient == null) {
43 | throw new IllegalArgumentException("MidiClient must not be null.");
44 | }
45 | this.midiProtocolClient = midiProtocolClient;
46 | }
47 |
48 | /*
49 | ================================================================================
50 | Launchpad API
51 | ================================================================================
52 | */
53 |
54 | /** {@inheritDoc} */
55 | @Override
56 | public void reset() {
57 | try {
58 | midiProtocolClient.reset();
59 | } catch (InvalidMidiDataException e) {
60 | throw new LaunchpadException(e);
61 | }
62 | }
63 |
64 | /**
65 | * {@inheritDoc}
66 | *
67 | * @param intensity {@inheritDoc} Must not be null.
68 | */
69 | @Override
70 | public void testLights(LightIntensity intensity) {
71 | if (intensity == null) {
72 | throw new IllegalArgumentException("Light intensity must not be null.");
73 | }
74 |
75 | int value;
76 | switch (intensity) {
77 | case LOW:
78 | value = 125;
79 | break;
80 | case MEDIUM:
81 | value = 126;
82 | break;
83 | case HIGH:
84 | value = 127;
85 | break;
86 | default:
87 | throw new IllegalArgumentException("Unknown intensity value : " + intensity.name());
88 | }
89 |
90 | try {
91 | midiProtocolClient.lightsOn(value);
92 | } catch (InvalidMidiDataException e) {
93 | throw new LaunchpadException(e);
94 | }
95 | }
96 |
97 | /**
98 | * {@inheritDoc}
99 | *
100 | * @param colors {@inheritDoc} Must be of even size.
101 | * @param operation {@inheritDoc} Must not be null.
102 | */
103 | @Override
104 | public void setLights(Color[] colors, BackBufferOperation operation) {
105 | if (colors == null) {
106 | throw new IllegalArgumentException("Colors must not be null");
107 | }
108 | int nbColors = colors.length;
109 | if ((nbColors & 1) != 0) {
110 | throw new IllegalArgumentException("The number of colors for a batch update must be even.");
111 | }
112 | if (operation == null) {
113 | throw new IllegalArgumentException("BackBuffer operation must not be null.");
114 | }
115 | int[] rawColors = new int[nbColors];
116 | for (int i = 0; i < nbColors; i++) {
117 | rawColors[i] = toRawColor(colors[i], operation);
118 | }
119 |
120 | try {
121 | midiProtocolClient.notesOn(rawColors);
122 | } catch (InvalidMidiDataException e) {
123 | throw new LaunchpadException(e);
124 | }
125 | }
126 |
127 | /**
128 | * {@inheritDoc}
129 | *
130 | * @param pad {@inheritDoc} Must not be null.
131 | * @param color {@inheritDoc} Must not be null.
132 | * @param operation {@inheritDoc} Must not be null.
133 | */
134 | @Override
135 | public void setPadLight(Pad pad, Color color, BackBufferOperation operation) {
136 | if (pad == null) {
137 | throw new IllegalArgumentException("Pad must not be null.");
138 | }
139 | if (color == null) {
140 | throw new IllegalArgumentException("Color must not be null.");
141 | }
142 | if (operation == null) {
143 | throw new IllegalArgumentException("BackBuffer operation must not be null.");
144 | }
145 |
146 | int rawCoords = toRawCoords(pad.getX(), pad.getY());
147 | int rawColor = toRawColor(color, operation);
148 |
149 | try {
150 | midiProtocolClient.noteOn(rawCoords, rawColor);
151 | } catch (InvalidMidiDataException e) {
152 | throw new LaunchpadException(e);
153 | }
154 | }
155 |
156 | /**
157 | * {@inheritDoc}
158 | *
159 | * @param button {@inheritDoc} Must not be null.
160 | * @param color {@inheritDoc} Must not be null.
161 | * @param operation {@inheritDoc} Must not be null.
162 | */
163 | @Override
164 | public void setButtonLight(Button button, Color color, BackBufferOperation operation) {
165 | if (button == null) {
166 | throw new IllegalArgumentException("Button must not be null.");
167 | }
168 | if (color == null) {
169 | throw new IllegalArgumentException("Color must not be null.");
170 | }
171 | if (operation == null) {
172 | throw new IllegalArgumentException("BackBuffer operation must not be null.");
173 | }
174 |
175 | try {
176 | int rawColor = toRawColor(color, operation);
177 | if (button.isTopButton()) {
178 | int rawCoords = 104 + button.getCoordinate();
179 | midiProtocolClient.buttonOn(rawCoords, rawColor);
180 | } else {
181 | int rawCoords = toRawCoords(8, button.getCoordinate());
182 | midiProtocolClient.noteOn(rawCoords, rawColor);
183 | }
184 | } catch (InvalidMidiDataException e) {
185 | throw new LaunchpadException(e);
186 | }
187 | }
188 |
189 | /**
190 | * {@inheritDoc}
191 | *
192 | * @param brightness {@inheritDoc} Must not be null.
193 | */
194 | @Override
195 | public void setBrightness(Brightness brightness) {
196 | if (brightness == null) {
197 | throw new IllegalArgumentException("Brightness must not be null");
198 | }
199 |
200 | int level = brightness.getBrightness();
201 | try {
202 | midiProtocolClient.brightness(1, 18 - level);
203 | } catch (InvalidMidiDataException e) {
204 | throw new LaunchpadException(e);
205 | }
206 | }
207 |
208 | /**
209 | * {@inheritDoc}
210 | *
211 | * @param visibleBuffer {@inheritDoc} Must not be null.
212 | * @param writeBuffer {@inheritDoc} Must not be null.
213 | */
214 | @Override
215 | public void setBuffers(Buffer visibleBuffer, Buffer writeBuffer, boolean copyVisibleBufferToWriteBuffer, boolean autoSwap) {
216 | if (visibleBuffer == null) {
217 | throw new IllegalArgumentException("Visible buffer must not be null.");
218 | }
219 | if (writeBuffer == null) {
220 | throw new IllegalArgumentException("Write buffer must not be null.");
221 | }
222 |
223 | try {
224 | midiProtocolClient.doubleBufferMode(getBufferValue(visibleBuffer), getBufferValue(writeBuffer), copyVisibleBufferToWriteBuffer, autoSwap);
225 | } catch (InvalidMidiDataException e) {
226 | throw new LaunchpadException(e);
227 | }
228 | }
229 |
230 | /**
231 | * Converts an abstract Buffer into its Launchpad-specific low-level representation.
232 | *
233 | * @param buffer The buffer to convert. Must not be null.
234 | * @return The buffer's low-level representation.
235 | */
236 | private int getBufferValue(Buffer buffer) {
237 | return buffer == Buffer.BUFFER_0 ? 0 : 1;
238 | }
239 |
240 | /**
241 | * {@inheritDoc}
242 | *
243 | * @param color {@inheritDoc} Must not be null.
244 | * @param speed {@inheritDoc} Must not be null.
245 | * @param operation {@inheritDoc} Must not be null.
246 | */
247 | @Override
248 | public void scrollText(String text, Color color, ScrollSpeed speed, boolean loop, BackBufferOperation operation) {
249 | if (color == null) {
250 | throw new IllegalArgumentException("Color must not be null.");
251 | }
252 | if (speed == null) {
253 | throw new IllegalArgumentException("Speed must not be null.");
254 | }
255 | if (operation == null) {
256 | throw new IllegalArgumentException("Operation must not be null.");
257 | }
258 |
259 | int rawColor = toRawColor(color, operation);
260 |
261 | try {
262 | midiProtocolClient.text(text, rawColor, speed.getScrollSpeed(), loop);
263 | } catch (InvalidMidiDataException e) {
264 | throw new LaunchpadException(e);
265 | }
266 | }
267 |
268 | /*
269 | ================================================================================
270 | Utils
271 | ================================================================================
272 | */
273 |
274 | /**
275 | * Converts a Color into its Launchpad-specific low-level representation
276 | *
277 | * @param color The Color to convert. Must not be null.
278 | * @param operation What to do on the backbuffer. Must not be null.
279 | * @return A binary representation of the color and how it should be applied to the Launchpad's buffers.
280 | */
281 | private byte toRawColor(Color color, BackBufferOperation operation) {
282 | int flags = 0;
283 | switch (operation) {
284 | case NONE:
285 | flags = 0;
286 | break;
287 | case CLEAR:
288 | flags = 8;
289 | break;
290 | case COPY:
291 | flags = 12;
292 | break;
293 | }
294 | return (byte) (flags + color.getRed() + (16 * color.getGreen()));
295 | }
296 |
297 | /**
298 | * Converts an X-Y coordinates into its Launchpad-specific low-level representation.
299 | *
300 | * @param x The X coordinate.
301 | * @param y The Y corrdinate.
302 | * @return The low-level representation of those coordinates.
303 | */
304 | private int toRawCoords(int x, int y) {
305 | return x + 16 * y;
306 | }
307 |
308 | }
309 |
--------------------------------------------------------------------------------
/LICENCE.TXT:
--------------------------------------------------------------------------------
1 |
2 | Apache License
3 | Version 2.0, January 2004
4 | http://www.apache.org/licenses/
5 |
6 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
7 |
8 | 1. Definitions.
9 |
10 | "License" shall mean the terms and conditions for use, reproduction,
11 | and distribution as defined by Sections 1 through 9 of this document.
12 |
13 | "Licensor" shall mean the copyright owner or entity authorized by
14 | the copyright owner that is granting the License.
15 |
16 | "Legal Entity" shall mean the union of the acting entity and all
17 | other entities that control, are controlled by, or are under common
18 | control with that entity. For the purposes of this definition,
19 | "control" means (i) the power, direct or indirect, to cause the
20 | direction or management of such entity, whether by contract or
21 | otherwise, or (ii) ownership of fifty percent (50%) or more of the
22 | outstanding shares, or (iii) beneficial ownership of such entity.
23 |
24 | "You" (or "Your") shall mean an individual or Legal Entity
25 | exercising permissions granted by this License.
26 |
27 | "Source" form shall mean the preferred form for making modifications,
28 | including but not limited to software source code, documentation
29 | source, and configuration files.
30 |
31 | "Object" form shall mean any form resulting from mechanical
32 | transformation or translation of a Source form, including but
33 | not limited to compiled object code, generated documentation,
34 | and conversions to other media types.
35 |
36 | "Work" shall mean the work of authorship, whether in Source or
37 | Object form, made available under the License, as indicated by a
38 | copyright notice that is included in or attached to the work
39 | (an example is provided in the Appendix below).
40 |
41 | "Derivative Works" shall mean any work, whether in Source or Object
42 | form, that is based on (or derived from) the Work and for which the
43 | editorial revisions, annotations, elaborations, or other modifications
44 | represent, as a whole, an original work of authorship. For the purposes
45 | of this License, Derivative Works shall not include works that remain
46 | separable from, or merely link (or bind by name) to the interfaces of,
47 | the Work and Derivative Works thereof.
48 |
49 | "Contribution" shall mean any work of authorship, including
50 | the original version of the Work and any modifications or additions
51 | to that Work or Derivative Works thereof, that is intentionally
52 | submitted to Licensor for inclusion in the Work by the copyright owner
53 | or by an individual or Legal Entity authorized to submit on behalf of
54 | the copyright owner. For the purposes of this definition, "submitted"
55 | means any form of electronic, verbal, or written communication sent
56 | to the Licensor or its representatives, including but not limited to
57 | communication on electronic mailing lists, source code control systems,
58 | and issue tracking systems that are managed by, or on behalf of, the
59 | Licensor for the purpose of discussing and improving the Work, but
60 | excluding communication that is conspicuously marked or otherwise
61 | designated in writing by the copyright owner as "Not a Contribution."
62 |
63 | "Contributor" shall mean Licensor and any individual or Legal Entity
64 | on behalf of whom a Contribution has been received by Licensor and
65 | subsequently incorporated within the Work.
66 |
67 | 2. Grant of Copyright License. Subject to the terms and conditions of
68 | this License, each Contributor hereby grants to You a perpetual,
69 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
70 | copyright license to reproduce, prepare Derivative Works of,
71 | publicly display, publicly perform, sublicense, and distribute the
72 | Work and such Derivative Works in Source or Object form.
73 |
74 | 3. Grant of Patent License. Subject to the terms and conditions of
75 | this License, each Contributor hereby grants to You a perpetual,
76 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable
77 | (except as stated in this section) patent license to make, have made,
78 | use, offer to sell, sell, import, and otherwise transfer the Work,
79 | where such license applies only to those patent claims licensable
80 | by such Contributor that are necessarily infringed by their
81 | Contribution(s) alone or by combination of their Contribution(s)
82 | with the Work to which such Contribution(s) was submitted. If You
83 | institute patent litigation against any entity (including a
84 | cross-claim or counterclaim in a lawsuit) alleging that the Work
85 | or a Contribution incorporated within the Work constitutes direct
86 | or contributory patent infringement, then any patent licenses
87 | granted to You under this License for that Work shall terminate
88 | as of the date such litigation is filed.
89 |
90 | 4. Redistribution. You may reproduce and distribute copies of the
91 | Work or Derivative Works thereof in any medium, with or without
92 | modifications, and in Source or Object form, provided that You
93 | meet the following conditions:
94 |
95 | (a) You must give any other recipients of the Work or
96 | Derivative Works a copy of this License; and
97 |
98 | (b) You must cause any modified files to carry prominent notices
99 | stating that You changed the files; and
100 |
101 | (c) You must retain, in the Source form of any Derivative Works
102 | that You distribute, all copyright, patent, trademark, and
103 | attribution notices from the Source form of the Work,
104 | excluding those notices that do not pertain to any part of
105 | the Derivative Works; and
106 |
107 | (d) If the Work includes a "NOTICE" text file as part of its
108 | distribution, then any Derivative Works that You distribute must
109 | include a readable copy of the attribution notices contained
110 | within such NOTICE file, excluding those notices that do not
111 | pertain to any part of the Derivative Works, in at least one
112 | of the following places: within a NOTICE text file distributed
113 | as part of the Derivative Works; within the Source form or
114 | documentation, if provided along with the Derivative Works; or,
115 | within a display generated by the Derivative Works, if and
116 | wherever such third-party notices normally appear. The contents
117 | of the NOTICE file are for informational purposes only and
118 | do not modify the License. You may add Your own attribution
119 | notices within Derivative Works that You distribute, alongside
120 | or as an addendum to the NOTICE text from the Work, provided
121 | that such additional attribution notices cannot be construed
122 | as modifying the License.
123 |
124 | You may add Your own copyright statement to Your modifications and
125 | may provide additional or different license terms and conditions
126 | for use, reproduction, or distribution of Your modifications, or
127 | for any such Derivative Works as a whole, provided Your use,
128 | reproduction, and distribution of the Work otherwise complies with
129 | the conditions stated in this License.
130 |
131 | 5. Submission of Contributions. Unless You explicitly state otherwise,
132 | any Contribution intentionally submitted for inclusion in the Work
133 | by You to the Licensor shall be under the terms and conditions of
134 | this License, without any additional terms or conditions.
135 | Notwithstanding the above, nothing herein shall supersede or modify
136 | the terms of any separate license agreement you may have executed
137 | with Licensor regarding such Contributions.
138 |
139 | 6. Trademarks. This License does not grant permission to use the trade
140 | names, trademarks, service marks, or product names of the Licensor,
141 | except as required for reasonable and customary use in describing the
142 | origin of the Work and reproducing the content of the NOTICE file.
143 |
144 | 7. Disclaimer of Warranty. Unless required by applicable law or
145 | agreed to in writing, Licensor provides the Work (and each
146 | Contributor provides its Contributions) on an "AS IS" BASIS,
147 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
148 | implied, including, without limitation, any warranties or conditions
149 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
150 | PARTICULAR PURPOSE. You are solely responsible for determining the
151 | appropriateness of using or redistributing the Work and assume any
152 | risks associated with Your exercise of permissions under this License.
153 |
154 | 8. Limitation of Liability. In no event and under no legal theory,
155 | whether in tort (including negligence), contract, or otherwise,
156 | unless required by applicable law (such as deliberate and grossly
157 | negligent acts) or agreed to in writing, shall any Contributor be
158 | liable to You for damages, including any direct, indirect, special,
159 | incidental, or consequential damages of any character arising as a
160 | result of this License or out of the use or inability to use the
161 | Work (including but not limited to damages for loss of goodwill,
162 | work stoppage, computer failure or malfunction, or any and all
163 | other commercial damages or losses), even if such Contributor
164 | has been advised of the possibility of such damages.
165 |
166 | 9. Accepting Warranty or Additional Liability. While redistributing
167 | the Work or Derivative Works thereof, You may choose to offer,
168 | and charge a fee for, acceptance of support, warranty, indemnity,
169 | or other liability obligations and/or rights consistent with this
170 | License. However, in accepting such obligations, You may act only
171 | on Your own behalf and on Your sole responsibility, not on behalf
172 | of any other Contributor, and only if You agree to indemnify,
173 | defend, and hold each Contributor harmless for any liability
174 | incurred by, or claims asserted against, such Contributor by reason
175 | of your accepting any such warranty or additional liability.
176 |
177 | END OF TERMS AND CONDITIONS
178 |
179 | APPENDIX: How to apply the Apache License to your work.
180 |
181 | To apply the Apache License to your work, attach the following
182 | boilerplate notice, with the fields enclosed by brackets "[]"
183 | replaced with your own identifying information. (Don't include
184 | the brackets!) The text should be enclosed in the appropriate
185 | comment syntax for the file format. We also recommend that a
186 | file or class name and description of purpose be included on the
187 | same "printed page" as the copyright notice for easier
188 | identification within third-party archives.
189 |
190 | Copyright 2015 Olivier Croisier
191 |
192 | Licensed under the Apache License, Version 2.0 (the "License");
193 | you may not use this file except in compliance with the License.
194 | You may obtain a copy of the License at
195 |
196 | http://www.apache.org/licenses/LICENSE-2.0
197 |
198 | Unless required by applicable law or agreed to in writing, software
199 | distributed under the License is distributed on an "AS IS" BASIS,
200 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
201 | See the License for the specific language governing permissions and
202 | limitations under the License.
203 |
--------------------------------------------------------------------------------
/lp4j-midi/src/test/java/net.thecodersbreakfast.lp4j.midi/MidiLaunchpadClientTest.java:
--------------------------------------------------------------------------------
1 | /*
2 | * Copyright 2015 Olivier Croisier (thecodersbreakfast.net)
3 | *
4 | * Licensed under the Apache License, Version 2.0 (the "License");
5 | * you may not use this file except in compliance with the License.
6 | * You may obtain a copy of the License at
7 | *
8 | * http://www.apache.org/licenses/LICENSE-2.0
9 | *
10 | * Unless required by applicable law or agreed to in writing, software
11 | * distributed under the License is distributed on an "AS IS" BASIS,
12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 | * See the License for the specific language governing permissions and
14 | * limitations under the License.
15 | */
16 |
17 | package net.thecodersbreakfast.lp4j.midi;
18 |
19 | import net.thecodersbreakfast.lp4j.api.*;
20 | import net.thecodersbreakfast.lp4j.midi.protocol.MidiProtocolClient;
21 | import org.junit.Before;
22 | import org.junit.Test;
23 | import org.junit.runner.RunWith;
24 | import org.mockito.Mock;
25 | import org.mockito.runners.MockitoJUnitRunner;
26 |
27 | import javax.sound.midi.InvalidMidiDataException;
28 |
29 | import static org.mockito.Matchers.anyBoolean;
30 | import static org.mockito.Matchers.anyInt;
31 | import static org.mockito.Matchers.anyString;
32 | import static org.mockito.Mockito.anyVararg;
33 | import static org.mockito.Mockito.*;
34 |
35 | @RunWith(MockitoJUnitRunner.class)
36 | public class MidiLaunchpadClientTest {
37 |
38 | private LaunchpadClient launchpadClient;
39 |
40 | @Mock
41 | private MidiProtocolClient midiProtocolClient;
42 |
43 | @Before
44 | public void init() {
45 | midiProtocolClient = mock(MidiProtocolClient.class);
46 | launchpadClient = new MidiLaunchpadClient(midiProtocolClient);
47 | }
48 |
49 | /*
50 | ================================================================================
51 | setBrightness
52 | ================================================================================
53 | */
54 |
55 | @Test
56 | public void setBrightness() {
57 | launchpadClient.setBrightness(Brightness.BRIGHTNESS_MIN);
58 | }
59 |
60 | @Test(expected = IllegalArgumentException.class)
61 | public void setBrightness_tooLow() {
62 | launchpadClient.setBrightness(Brightness.of(Brightness.MIN_VALUE - 1));
63 | }
64 |
65 | @Test(expected = IllegalArgumentException.class)
66 | public void setBrightness_tooHigh() {
67 | launchpadClient.setBrightness(Brightness.of(Brightness.MAX_VALUE + 1));
68 | }
69 |
70 | @Test(expected = LaunchpadException.class)
71 | public void setBrightness_exception() throws InvalidMidiDataException {
72 | doThrow(new InvalidMidiDataException()).when(midiProtocolClient).brightness(anyInt(), anyInt());
73 | launchpadClient.setBrightness(Brightness.BRIGHTNESS_MIN);
74 | }
75 |
76 | /*
77 | ================================================================================
78 | setBuffers
79 | ================================================================================
80 | */
81 |
82 | @Test
83 | public void setBuffers() throws InvalidMidiDataException {
84 | launchpadClient.setBuffers(Buffer.BUFFER_0, Buffer.BUFFER_1, false, false);
85 | verify(midiProtocolClient).doubleBufferMode(0, 1, false, false);
86 | }
87 |
88 | @Test(expected = LaunchpadException.class)
89 | public void setBuffers_exception() throws InvalidMidiDataException {
90 | doThrow(new InvalidMidiDataException()).when(midiProtocolClient).doubleBufferMode(anyInt(), anyInt(), anyBoolean(), anyBoolean());
91 | launchpadClient.setBuffers(Buffer.BUFFER_0, Buffer.BUFFER_1, false, false);
92 | }
93 |
94 | /*
95 | ================================================================================
96 | setButtonLight
97 | ================================================================================
98 | */
99 |
100 | @Test(expected = LaunchpadException.class)
101 | public void setButtonLight_exception() throws InvalidMidiDataException {
102 | doThrow(new InvalidMidiDataException()).when(midiProtocolClient).buttonOn(anyInt(), anyInt());
103 | launchpadClient.setButtonLight(Button.UP, Color.BLACK, BackBufferOperation.COPY);
104 | }
105 |
106 | @Test
107 | public void setButtonLight_topButton_NONE() throws InvalidMidiDataException {
108 | launchpadClient.setButtonLight(Button.UP, Color.BLACK, BackBufferOperation.NONE);
109 | verify(midiProtocolClient).buttonOn(104, 0);
110 | }
111 |
112 | @Test
113 | public void setButtonLight_topButton_CLEAR() throws InvalidMidiDataException {
114 | launchpadClient.setButtonLight(Button.UP, Color.BLACK, BackBufferOperation.CLEAR);
115 | verify(midiProtocolClient).buttonOn(104, 8);
116 | }
117 |
118 | @Test
119 | public void setButtonLight_topButton_COPY() throws InvalidMidiDataException {
120 | launchpadClient.setButtonLight(Button.UP, Color.BLACK, BackBufferOperation.COPY);
121 | verify(midiProtocolClient).buttonOn(104, 12);
122 | }
123 |
124 | @Test
125 | public void setButtonLight_rightButton() throws InvalidMidiDataException {
126 | launchpadClient.setButtonLight(Button.VOL, Color.BLACK, BackBufferOperation.COPY);
127 | verify(midiProtocolClient).noteOn(8, 12);
128 | }
129 |
130 | @Test
131 | public void setButtonLight_rightButton_COPY() throws InvalidMidiDataException {
132 | launchpadClient.setButtonLight(Button.VOL, Color.BLACK, BackBufferOperation.COPY);
133 | verify(midiProtocolClient).noteOn(8, 12);
134 | }
135 |
136 | @Test(expected = IllegalArgumentException.class)
137 | public void setButtonLight_XY_illegal_COPY() {
138 | launchpadClient.setButtonLight(Button.atTop(-1), Color.BLACK, BackBufferOperation.COPY);
139 | }
140 |
141 | @Test
142 | public void setButtonLight_topButton_XY_COPY() throws InvalidMidiDataException {
143 | launchpadClient.setButtonLight(Button.atTop(0), Color.BLACK, BackBufferOperation.COPY); // UP
144 | verify(midiProtocolClient).buttonOn(104, 12);
145 | }
146 |
147 | @Test
148 | public void setButtonLight_rightButton_XY_COPY() throws InvalidMidiDataException {
149 | launchpadClient.setButtonLight(Button.atRight(0), Color.BLACK, BackBufferOperation.COPY); // VOL
150 | verify(midiProtocolClient).noteOn(8, 12);
151 | }
152 |
153 | /*
154 | ================================================================================
155 | setPadLight
156 | ================================================================================
157 | */
158 |
159 | @Test
160 | public void setPadLight_COPY() throws InvalidMidiDataException {
161 | launchpadClient.setPadLight(Pad.at(0, 0), Color.BLACK, BackBufferOperation.COPY);
162 | verify(midiProtocolClient).noteOn(0, 12);
163 | }
164 |
165 | @Test(expected = IllegalArgumentException.class)
166 | public void setPadLight_illegal() {
167 | launchpadClient.setPadLight(Pad.at(-1, -1), Color.BLACK, BackBufferOperation.COPY);
168 | }
169 |
170 | @Test(expected = LaunchpadException.class)
171 | public void setPadLight_exception() throws InvalidMidiDataException {
172 | doThrow(new InvalidMidiDataException()).when(midiProtocolClient).noteOn(anyInt(), anyInt());
173 | launchpadClient.setPadLight(Pad.at(1, 1), Color.BLACK, BackBufferOperation.COPY);
174 | }
175 |
176 | /*
177 | ================================================================================
178 | reset
179 | ================================================================================
180 | */
181 |
182 | @Test
183 | public void reset() throws InvalidMidiDataException {
184 | launchpadClient.reset();
185 | verify(midiProtocolClient).reset();
186 | }
187 |
188 | @Test(expected = LaunchpadException.class)
189 | public void reset_exception() throws InvalidMidiDataException {
190 | doThrow(new InvalidMidiDataException()).when(midiProtocolClient).reset();
191 | launchpadClient.reset();
192 | verify(midiProtocolClient).reset();
193 | }
194 |
195 | /*
196 | ================================================================================
197 | testLights
198 | ================================================================================
199 | */
200 |
201 | @Test
202 | public void testLights() throws InvalidMidiDataException {
203 | launchpadClient.testLights(LightIntensity.LOW);
204 | verify(midiProtocolClient).lightsOn(125);
205 | launchpadClient.testLights(LightIntensity.MEDIUM);
206 | verify(midiProtocolClient).lightsOn(126);
207 | launchpadClient.testLights(LightIntensity.HIGH);
208 | verify(midiProtocolClient).lightsOn(127);
209 | }
210 |
211 | @Test(expected = LaunchpadException.class)
212 | public void testLights_exception() throws InvalidMidiDataException {
213 | doThrow(new InvalidMidiDataException()).when(midiProtocolClient).lightsOn(anyInt());
214 | launchpadClient.testLights(LightIntensity.LOW);
215 | }
216 |
217 | /*
218 | ================================================================================
219 | setLights
220 | ================================================================================
221 | */
222 |
223 | @Test(expected = IllegalArgumentException.class)
224 | public void setLights_null() {
225 | launchpadClient.setLights(null, BackBufferOperation.NONE);
226 | }
227 |
228 | @Test(expected = IllegalArgumentException.class)
229 | public void setLights_odd() {
230 | launchpadClient.setLights(new Color[1], BackBufferOperation.NONE);
231 | }
232 |
233 | @Test(expected = LaunchpadException.class)
234 | public void setLights_exception() throws InvalidMidiDataException {
235 | doThrow(new InvalidMidiDataException()).when(midiProtocolClient).notesOn((int[]) anyVararg());
236 | launchpadClient.setLights(new Color[]{Color.BLACK, Color.BLACK}, BackBufferOperation.COPY);
237 | }
238 |
239 | @Test
240 | public void setLights_COPY() throws InvalidMidiDataException {
241 | launchpadClient.setLights(new Color[]{Color.BLACK, Color.BLACK}, BackBufferOperation.COPY);
242 | verify(midiProtocolClient).notesOn(12, 12);
243 | }
244 |
245 | /*
246 | ================================================================================
247 | scrollText
248 | ================================================================================
249 | */
250 |
251 | @Test(expected = IllegalArgumentException.class)
252 | public void scrollText_speedTooLow() {
253 | launchpadClient.scrollText("Hello", Color.BLACK, ScrollSpeed.of(ScrollSpeed.MIN_VALUE - 1), false, BackBufferOperation.COPY);
254 | }
255 |
256 | @Test(expected = IllegalArgumentException.class)
257 | public void scrollText_speedTooHIgh() {
258 | launchpadClient.scrollText("Hello", Color.BLACK, ScrollSpeed.of(ScrollSpeed.MAX_VALUE + 1), false, BackBufferOperation.COPY);
259 | }
260 |
261 | @Test(expected = IllegalArgumentException.class)
262 | public void scrollText_nullColor() {
263 | launchpadClient.scrollText("Hello", null, ScrollSpeed.SPEED_MIN, false, BackBufferOperation.COPY);
264 | }
265 |
266 | @Test
267 | public void scrollText_COPY() throws InvalidMidiDataException {
268 | launchpadClient.scrollText("Hello", Color.BLACK, ScrollSpeed.SPEED_MIN, false, BackBufferOperation.COPY);
269 | verify(midiProtocolClient).text("Hello", 12, 1, false);
270 | }
271 |
272 | @Test(expected = LaunchpadException.class)
273 | public void scrollText_exception() throws InvalidMidiDataException {
274 | doThrow(new InvalidMidiDataException()).when(midiProtocolClient).text(anyString(), anyInt(), anyInt(), anyBoolean());
275 | launchpadClient.scrollText("Hello", Color.BLACK, ScrollSpeed.SPEED_MIN, false, BackBufferOperation.COPY);
276 | }
277 |
278 |
279 | }
280 |
--------------------------------------------------------------------------------
/lp4j-emu-web/src/main/resources/web/sockjs-0.3.min.js:
--------------------------------------------------------------------------------
1 | /* SockJS client, version 0.3.4, http://sockjs.org, MIT License
2 |
3 | Copyright (c) 2011-2012 VMware, Inc.
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in
13 | all copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21 | THE SOFTWARE.
22 | */
23 |
24 | // JSON2 by Douglas Crockford (minified).
25 | var JSON;JSON||(JSON={}),function(){function str(a,b){var c,d,e,f,g=gap,h,i=b[a];i&&typeof i=="object"&&typeof i.toJSON=="function"&&(i=i.toJSON(a)),typeof rep=="function"&&(i=rep.call(b,a,i));switch(typeof i){case"string":return quote(i);case"number":return isFinite(i)?String(i):"null";case"boolean":case"null":return String(i);case"object":if(!i)return"null";gap+=indent,h=[];if(Object.prototype.toString.apply(i)==="[object Array]"){f=i.length;for(c=0;c