update fork to current state of original repository

* also include the Timer1 library directly for less hassle
This commit is contained in:
Alexander Weinhold 2015-07-23 19:51:09 +02:00
parent 7b20c89240
commit e29a4203ed
96 changed files with 7772 additions and 1033 deletions

View File

@ -1,4 +1,4 @@
#include <TimerOne.h>
#include "TimerOne.h"
boolean firstRun = true; // Used for one-run-only stuffs;
@ -8,32 +8,32 @@ const byte PIN_MAX = 17;
#define RESOLUTION 40 //Microsecond resolution for notes
/*NOTE: Many of the arrays below contain unused indexes. This is
to prevent the Arduino from having to convert a pin input to an alternate
array index and save as many cycles as possible. In other words information
for pin 2 will be stored in index 2, and information for pin 4 will be
stored in index 4.*/
/*NOTE: Many of the arrays below contain unused indexes. This is
to prevent the Arduino from having to convert a pin input to an alternate
array index and save as many cycles as possible. In other words information
for pin 2 will be stored in index 2, and information for pin 4 will be
stored in index 4.*/
/*An array of maximum track positions for each step-control pin. Even pins
are used for control, so only even numbers need a value here. 3.5" Floppies have
80 tracks, 5.25" have 50. These should be doubled, because each tick is now
half a position (use 158 and 98).
*/
are used for control, so only even numbers need a value here. 3.5" Floppies have
80 tracks, 5.25" have 50. These should be doubled, because each tick is now
half a position (use 158 and 98).
*/
byte MAX_POSITION[] = {
0,0,158,0,158,0,158,0,158,0,158,0,158,0,158,0,158,0};
//Array to track the current position of each floppy head. (Only even indexes (i.e. 2,4,6...) are used)
byte currentPosition[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0};
/*Array to keep track of state of each pin. Even indexes track the control-pins for toggle purposes. Odd indexes
track direction-pins. LOW = forward, HIGH=reverse
*/
track direction-pins. LOW = forward, HIGH=reverse
*/
int currentState[] = {
0,0,LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW,LOW
};
//Current period assigned to each pin. 0 = off. Each period is of the length specified by the RESOLUTION
//variable above. i.e. A period of 10 is (RESOLUTION x 10) microseconds long.
unsigned int currentPeriod[] = {
@ -42,7 +42,7 @@ unsigned int currentPeriod[] = {
//Current tick
unsigned int currentTick[] = {
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0
};
@ -75,7 +75,7 @@ void setup(){
void loop(){
//The first loop, reset all the drives, and wait 2 seconds...
if (firstRun)
{
@ -84,7 +84,7 @@ void loop(){
delay(2000);
}
//Only read if we have
//Only read if we have
if (Serial.available() > 2){
//Watch for special 100-message to reset the drives
if (Serial.peek() == 100) {
@ -93,7 +93,7 @@ void loop(){
while(Serial.available() > 0){
Serial.read();
}
}
}
else{
currentPeriod[Serial.read()] = (Serial.read() << 8) | Serial.read();
}
@ -103,13 +103,13 @@ void loop(){
/*
Called by the timer inturrupt at the specified resolution.
*/
*/
void tick()
{
/*
If there is a period set for control pin 2, count the number of
ticks that pass, and toggle the pin if the current period is reached.
*/
/*
If there is a period set for control pin 2, count the number of
ticks that pass, and toggle the pin if the current period is reached.
*/
if (currentPeriod[2]>0){
currentTick[2]++;
if (currentTick[2] >= currentPeriod[2]){
@ -166,29 +166,29 @@ void tick()
currentTick[16]=0;
}
}
}
void togglePin(byte pin, byte direction_pin) {
//Switch directions if end has been reached
if (currentPosition[pin] >= MAX_POSITION[pin]) {
currentState[direction_pin] = HIGH;
digitalWrite(direction_pin,HIGH);
}
}
else if (currentPosition[pin] <= 0) {
currentState[direction_pin] = LOW;
digitalWrite(direction_pin,LOW);
}
//Update currentPosition
//Update currentPosition
if (currentState[direction_pin] == HIGH){
currentPosition[pin]--;
}
}
else {
currentPosition[pin]++;
}
//Pulse the control pin
digitalWrite(pin,currentState[pin]);
currentState[pin] = ~currentState[pin];
@ -203,7 +203,7 @@ void togglePin(byte pin, byte direction_pin) {
void blinkLED(){
digitalWrite(13, HIGH); // set the LED on
delay(250); // wait for a second
digitalWrite(13, LOW);
digitalWrite(13, LOW);
}
//For a given controller pin, runs the read-head all the way back to 0
@ -222,12 +222,17 @@ void reset(byte pin)
//Resets all the pins
void resetAll(){
// Old one-at-a-time reset
//for (byte p=FIRST_PIN;p<=PIN_MAX;p+=2){
// reset(p);
//}
//Stop all notes (don't want to be playing during/after reset)
for (byte p=FIRST_PIN;p<=PIN_MAX;p+=2){
currentPeriod[p] = 0; // Stop playing notes
}
// New all-at-once reset
for (byte s=0;s<80;s++){ // For max drive's position
for (byte p=FIRST_PIN;p<=PIN_MAX;p+=2){
@ -237,14 +242,11 @@ void resetAll(){
}
delay(5);
}
for (byte p=FIRST_PIN;p<=PIN_MAX;p+=2){
currentPosition[p] = 0; // We're reset.
digitalWrite(p+1,LOW);
currentState[p+1] = 0; // Ready to go forward.
}
}

209
Arduino/Moppy/TimerOne.cpp Normal file
View File

@ -0,0 +1,209 @@
/*
* Interrupt and PWM utilities for 16 bit Timer1 on ATmega168/328
* Original code by Jesse Tane for http://labs.ideo.com August 2008
* Modified March 2009 by Jérôme Despatis and Jesse Tane for ATmega328 support
* Modified June 2009 by Michael Polli and Jesse Tane to fix a bug in setPeriod() which caused the timer to stop
* Modified June 2011 by Lex Talionis to add a function to read the timer
* Modified Oct 2011 by Andrew Richards to avoid certain problems:
* - Add (long) assignments and casts to TimerOne::read() to ensure calculations involving tmp, ICR1 and TCNT1 aren't truncated
* - Ensure 16 bit registers accesses are atomic - run with interrupts disabled when accessing
* - Remove global enable of interrupts (sei())- could be running within an interrupt routine)
* - Disable interrupts whilst TCTN1 == 0. Datasheet vague on this, but experiment shows that overflow interrupt
* flag gets set whilst TCNT1 == 0, resulting in a phantom interrupt. Could just set to 1, but gets inaccurate
* at very short durations
* - startBottom() added to start counter at 0 and handle all interrupt enabling.
* - start() amended to enable interrupts
* - restart() amended to point at startBottom()
* Modiied 7:26 PM Sunday, October 09, 2011 by Lex Talionis
* - renamed start() to resume() to reflect it's actual role
* - renamed startBottom() to start(). This breaks some old code that expects start to continue counting where it left off
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* See Google Code project http://code.google.com/p/arduino-timerone/ for latest
*/
#ifndef TIMERONE_cpp
#define TIMERONE_cpp
#include "TimerOne.h"
TimerOne Timer1; // preinstatiate
ISR(TIMER1_OVF_vect) // interrupt service routine that wraps a user defined function supplied by attachInterrupt
{
Timer1.isrCallback();
}
void TimerOne::initialize(long microseconds)
{
TCCR1A = 0; // clear control register A
TCCR1B = _BV(WGM13); // set mode 8: phase and frequency correct pwm, stop the timer
setPeriod(microseconds);
}
void TimerOne::setPeriod(long microseconds) // AR modified for atomic access
{
long cycles = (F_CPU / 2000000) * microseconds; // the counter runs backwards after TOP, interrupt is at BOTTOM so divide microseconds by 2
if(cycles < RESOLUTION) clockSelectBits = _BV(CS10); // no prescale, full xtal
else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11); // prescale by /8
else if((cycles >>= 3) < RESOLUTION) clockSelectBits = _BV(CS11) | _BV(CS10); // prescale by /64
else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12); // prescale by /256
else if((cycles >>= 2) < RESOLUTION) clockSelectBits = _BV(CS12) | _BV(CS10); // prescale by /1024
else cycles = RESOLUTION - 1, clockSelectBits = _BV(CS12) | _BV(CS10); // request was out of bounds, set as maximum
oldSREG = SREG;
cli(); // Disable interrupts for 16 bit register access
ICR1 = pwmPeriod = cycles; // ICR1 is TOP in p & f correct pwm mode
SREG = oldSREG;
TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12));
TCCR1B |= clockSelectBits; // reset clock select register, and starts the clock
}
void TimerOne::setPwmDuty(char pin, int duty)
{
unsigned long dutyCycle = pwmPeriod;
dutyCycle *= duty;
dutyCycle >>= 10;
oldSREG = SREG;
cli();
if(pin == 1 || pin == 9) OCR1A = dutyCycle;
else if(pin == 2 || pin == 10) OCR1B = dutyCycle;
SREG = oldSREG;
}
void TimerOne::pwm(char pin, int duty, long microseconds) // expects duty cycle to be 10 bit (1024)
{
if(microseconds > 0) setPeriod(microseconds);
if(pin == 1 || pin == 9) {
DDRB |= _BV(PORTB1); // sets data direction register for pwm output pin
TCCR1A |= _BV(COM1A1); // activates the output pin
}
else if(pin == 2 || pin == 10) {
DDRB |= _BV(PORTB2);
TCCR1A |= _BV(COM1B1);
}
setPwmDuty(pin, duty);
resume(); // Lex - make sure the clock is running. We don't want to restart the count, in case we are starting the second WGM
// and the first one is in the middle of a cycle
}
void TimerOne::disablePwm(char pin)
{
if(pin == 1 || pin == 9) TCCR1A &= ~_BV(COM1A1); // clear the bit that enables pwm on PB1
else if(pin == 2 || pin == 10) TCCR1A &= ~_BV(COM1B1); // clear the bit that enables pwm on PB2
}
void TimerOne::attachInterrupt(void (*isr)(), long microseconds)
{
if(microseconds > 0) setPeriod(microseconds);
isrCallback = isr; // register the user's callback with the real ISR
TIMSK1 = _BV(TOIE1); // sets the timer overflow interrupt enable bit
// might be running with interrupts disabled (eg inside an ISR), so don't touch the global state
// sei();
resume();
}
void TimerOne::detachInterrupt()
{
TIMSK1 &= ~_BV(TOIE1); // clears the timer overflow interrupt enable bit
// timer continues to count without calling the isr
}
void TimerOne::resume() // AR suggested
{
TCCR1B |= clockSelectBits;
}
void TimerOne::restart() // Depricated - Public interface to start at zero - Lex 10/9/2011
{
start();
}
void TimerOne::start() // AR addition, renamed by Lex to reflect it's actual role
{
unsigned int tcnt1;
TIMSK1 &= ~_BV(TOIE1); // AR added
GTCCR |= _BV(PSRSYNC); // AR added - reset prescaler (NB: shared with all 16 bit timers);
oldSREG = SREG; // AR - save status register
cli(); // AR - Disable interrupts
TCNT1 = 0;
SREG = oldSREG; // AR - Restore status register
resume();
do { // Nothing -- wait until timer moved on from zero - otherwise get a phantom interrupt
oldSREG = SREG;
cli();
tcnt1 = TCNT1;
SREG = oldSREG;
} while (tcnt1==0);
// TIFR1 = 0xff; // AR - Clear interrupt flags
// TIMSK1 = _BV(TOIE1); // sets the timer overflow interrupt enable bit
}
void TimerOne::stop()
{
TCCR1B &= ~(_BV(CS10) | _BV(CS11) | _BV(CS12)); // clears all clock selects bits
}
unsigned long TimerOne::read() //returns the value of the timer in microseconds
{ //rember! phase and freq correct mode counts up to then down again
unsigned long tmp; // AR amended to hold more than 65536 (could be nearly double this)
unsigned int tcnt1; // AR added
oldSREG= SREG;
cli();
tmp=TCNT1;
SREG = oldSREG;
char scale=0;
switch (clockSelectBits)
{
case 1:// no prescalse
scale=0;
break;
case 2:// x8 prescale
scale=3;
break;
case 3:// x64
scale=6;
break;
case 4:// x256
scale=8;
break;
case 5:// x1024
scale=10;
break;
}
do { // Nothing -- max delay here is ~1023 cycles. AR modified
oldSREG = SREG;
cli();
tcnt1 = TCNT1;
SREG = oldSREG;
} while (tcnt1==tmp); //if the timer has not ticked yet
//if we are counting down add the top value to how far we have counted down
tmp = ( (tcnt1>tmp) ? (tmp) : (long)(ICR1-tcnt1)+(long)ICR1 ); // AR amended to add casts and reuse previous TCNT1
return ((tmp*1000L)/(F_CPU /1000L))<<scale;
}
#endif

70
Arduino/Moppy/TimerOne.h Normal file
View File

@ -0,0 +1,70 @@
/*
* Interrupt and PWM utilities for 16 bit Timer1 on ATmega168/328
* Original code by Jesse Tane for http://labs.ideo.com August 2008
* Modified March 2009 by Jérôme Despatis and Jesse Tane for ATmega328 support
* Modified June 2009 by Michael Polli and Jesse Tane to fix a bug in setPeriod() which caused the timer to stop
* Modified June 2011 by Lex Talionis to add a function to read the timer
* Modified Oct 2011 by Andrew Richards to avoid certain problems:
* - Add (long) assignments and casts to TimerOne::read() to ensure calculations involving tmp, ICR1 and TCNT1 aren't truncated
* - Ensure 16 bit registers accesses are atomic - run with interrupts disabled when accessing
* - Remove global enable of interrupts (sei())- could be running within an interrupt routine)
* - Disable interrupts whilst TCTN1 == 0. Datasheet vague on this, but experiment shows that overflow interrupt
* flag gets set whilst TCNT1 == 0, resulting in a phantom interrupt. Could just set to 1, but gets inaccurate
* at very short durations
* - startBottom() added to start counter at 0 and handle all interrupt enabling.
* - start() amended to enable interrupts
* - restart() amended to point at startBottom()
* Modiied 7:26 PM Sunday, October 09, 2011 by Lex Talionis
* - renamed start() to resume() to reflect it's actual role
* - renamed startBottom() to start(). This breaks some old code that expects start to continue counting where it left off
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*
* See Google Code project http://code.google.com/p/arduino-timerone/ for latest
*/
#ifndef TIMERONE_h
#define TIMERONE_h
#include <avr/io.h>
#include <avr/interrupt.h>
#define RESOLUTION 65536 // Timer1 is 16 bit
class TimerOne
{
public:
// properties
unsigned int pwmPeriod;
unsigned char clockSelectBits;
char oldSREG; // To hold Status Register while ints disabled
// methods
void initialize(long microseconds=1000000);
void start();
void stop();
void restart();
void resume();
unsigned long read();
void pwm(char pin, int duty, long microseconds=-1);
void disablePwm(char pin);
void attachInterrupt(void (*isr)(), long microseconds=-1);
void detachInterrupt();
void setPeriod(long microseconds);
void setPwmDuty(char pin, int duty);
void (*isrCallback)();
};
extern TimerOne Timer1;
#endif

View File

@ -51,8 +51,7 @@
-init-macrodef-junit: defines macro for junit execution
-init-macrodef-debug: defines macro for class debugging
-init-macrodef-java: defines macro for class execution
-do-jar-with-manifest: JAR building (if you are using a manifest)
-do-jar-without-manifest: JAR building (if you are not using a manifest)
-do-jar: JAR building
run: execution of project
-javadoc-build: Javadoc generation
test-report: JUnit report generation

Binary file not shown.

View File

@ -12,18 +12,18 @@ is divided into following sections:
- execution
- debugging
- javadoc
- junit compilation
- junit execution
- junit debugging
- test compilation
- test execution
- test debugging
- applet
- cleanup
-->
<project xmlns:j2seproject1="http://www.netbeans.org/ns/j2se-project/1" xmlns:j2seproject3="http://www.netbeans.org/ns/j2se-project/3" xmlns:jaxrpc="http://www.netbeans.org/ns/j2se-project/jax-rpc" basedir=".." default="default" name="MoppyDesk-impl">
<fail message="Please build using Ant 1.7.1 or higher.">
<fail message="Please build using Ant 1.8.0 or higher.">
<condition>
<not>
<antversion atleast="1.7.1"/>
<antversion atleast="1.8.0"/>
</not>
</condition>
</fail>
@ -54,6 +54,7 @@ is divided into following sections:
<property file="nbproject/project.properties"/>
</target>
<target depends="-pre-init,-init-private,-init-user,-init-project,-init-macrodef-property" name="-do-init">
<property name="platform.java" value="${java.home}/bin/java"/>
<available file="${manifest.file}" property="manifest.available"/>
<condition property="splashscreen.available">
<and>
@ -71,16 +72,20 @@ is divided into following sections:
</not>
</and>
</condition>
<condition property="manifest.available+main.class">
<condition property="profile.available">
<and>
<isset property="manifest.available"/>
<isset property="main.class.available"/>
<isset property="javac.profile"/>
<length length="0" string="${javac.profile}" when="greater"/>
<matches pattern="1\.[89](\..*)?" string="${javac.source}"/>
</and>
</condition>
<condition property="do.archive">
<not>
<istrue value="${jar.archive.disabled}"/>
</not>
<or>
<not>
<istrue value="${jar.archive.disabled}"/>
</not>
<istrue value="${not.archive.disabled}"/>
</or>
</condition>
<condition property="do.mkdist">
<and>
@ -91,12 +96,6 @@ is divided into following sections:
</not>
</and>
</condition>
<condition property="manifest.available+main.class+mkdist.available">
<and>
<istrue value="${manifest.available+main.class}"/>
<isset property="do.mkdist"/>
</and>
</condition>
<condition property="do.archive+manifest.available">
<and>
<isset property="manifest.available"/>
@ -115,24 +114,12 @@ is divided into following sections:
<istrue value="${do.archive}"/>
</and>
</condition>
<condition property="do.archive+manifest.available+main.class">
<condition property="do.archive+profile.available">
<and>
<istrue value="${manifest.available+main.class}"/>
<isset property="profile.available"/>
<istrue value="${do.archive}"/>
</and>
</condition>
<condition property="manifest.available-mkdist.available">
<or>
<istrue value="${manifest.available}"/>
<isset property="do.mkdist"/>
</or>
</condition>
<condition property="manifest.available+main.class-mkdist.available">
<or>
<istrue value="${manifest.available+main.class}"/>
<isset property="do.mkdist"/>
</or>
</condition>
<condition property="have.tests">
<or>
<available file="${test.src.dir}"/>
@ -156,6 +143,7 @@ is divided into following sections:
</and>
</condition>
<property name="run.jvmargs" value=""/>
<property name="run.jvmargs.ide" value=""/>
<property name="javac.compilerargs" value=""/>
<property name="work.dir" value="${basedir}"/>
<condition property="no.deps">
@ -185,7 +173,15 @@ is divided into following sections:
</condition>
<path id="endorsed.classpath.path" path="${endorsed.classpath}"/>
<condition else="" property="endorsed.classpath.cmd.line.arg" value="-Xbootclasspath/p:'${toString:endorsed.classpath.path}'">
<length length="0" string="${endorsed.classpath}" when="greater"/>
<and>
<isset property="endorsed.classpath"/>
<not>
<equals arg1="${endorsed.classpath}" arg2="" trim="true"/>
</not>
</and>
</condition>
<condition else="" property="javac.profile.cmd.line.arg" value="-profile ${javac.profile}">
<isset property="profile.available"/>
</condition>
<condition else="false" property="jdkBug6558476">
<and>
@ -198,7 +194,29 @@ is divided into following sections:
<property name="javac.fork" value="${jdkBug6558476}"/>
<property name="jar.index" value="false"/>
<property name="jar.index.metainf" value="${jar.index}"/>
<property name="copylibs.rebase" value="true"/>
<available file="${meta.inf.dir}/persistence.xml" property="has.persistence.xml"/>
<condition property="junit.available">
<or>
<available classname="org.junit.Test" classpath="${run.test.classpath}"/>
<available classname="junit.framework.Test" classpath="${run.test.classpath}"/>
</or>
</condition>
<condition property="testng.available">
<available classname="org.testng.annotations.Test" classpath="${run.test.classpath}"/>
</condition>
<condition property="junit+testng.available">
<and>
<istrue value="${junit.available}"/>
<istrue value="${testng.available}"/>
</and>
</condition>
<condition else="testng" property="testng.mode" value="mixed">
<istrue value="${junit+testng.available}"/>
</condition>
<condition else="" property="testng.debug.mode" value="-mixed">
<istrue value="${junit+testng.available}"/>
</condition>
</target>
<target name="-post-init">
<!-- Empty placeholder for easier customization. -->
@ -252,6 +270,7 @@ is divided into following sections:
<path path="@{classpath}"/>
</classpath>
<compilerarg line="${endorsed.classpath.cmd.line.arg}"/>
<compilerarg line="${javac.profile.cmd.line.arg}"/>
<compilerarg line="${javac.compilerargs}"/>
<compilerarg value="-processorpath"/>
<compilerarg path="@{processorpath}:${empty.dir}"/>
@ -291,6 +310,7 @@ is divided into following sections:
<path path="@{classpath}"/>
</classpath>
<compilerarg line="${endorsed.classpath.cmd.line.arg}"/>
<compilerarg line="${javac.profile.cmd.line.arg}"/>
<compilerarg line="${javac.compilerargs}"/>
<customize/>
</javac>
@ -331,11 +351,57 @@ is divided into following sections:
</sequential>
</macrodef>
</target>
<target name="-init-macrodef-junit">
<target if="${junit.available}" name="-init-macrodef-junit-init">
<condition else="false" property="nb.junit.batch" value="true">
<and>
<istrue value="${junit.available}"/>
<not>
<isset property="test.method"/>
</not>
</and>
</condition>
<condition else="false" property="nb.junit.single" value="true">
<and>
<istrue value="${junit.available}"/>
<isset property="test.method"/>
</and>
</condition>
</target>
<target name="-init-test-properties">
<property name="test.binaryincludes" value="&lt;nothing&gt;"/>
<property name="test.binarytestincludes" value=""/>
<property name="test.binaryexcludes" value=""/>
</target>
<target if="${nb.junit.single}" name="-init-macrodef-junit-single" unless="${nb.junit.batch}">
<macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
<attribute default="${includes}" name="includes"/>
<attribute default="${excludes}" name="excludes"/>
<attribute default="**" name="testincludes"/>
<attribute default="" name="testmethods"/>
<element name="customize" optional="true"/>
<sequential>
<property name="junit.forkmode" value="perTest"/>
<junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
<test methods="@{testmethods}" name="@{testincludes}" todir="${build.test.results.dir}"/>
<syspropertyset>
<propertyref prefix="test-sys-prop."/>
<mapper from="test-sys-prop.*" to="*" type="glob"/>
</syspropertyset>
<formatter type="brief" usefile="false"/>
<formatter type="xml"/>
<jvmarg value="-ea"/>
<customize/>
</junit>
</sequential>
</macrodef>
</target>
<target depends="-init-test-properties" if="${nb.junit.batch}" name="-init-macrodef-junit-batch" unless="${nb.junit.single}">
<macrodef name="junit" uri="http://www.netbeans.org/ns/j2se-project/3">
<attribute default="${includes}" name="includes"/>
<attribute default="${excludes}" name="excludes"/>
<attribute default="**" name="testincludes"/>
<attribute default="" name="testmethods"/>
<element name="customize" optional="true"/>
<sequential>
<property name="junit.forkmode" value="perTest"/>
<junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
@ -343,33 +409,277 @@ is divided into following sections:
<fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
<filename name="@{testincludes}"/>
</fileset>
<fileset dir="${build.test.classes.dir}" excludes="@{excludes},${excludes},${test.binaryexcludes}" includes="${test.binaryincludes}">
<filename name="${test.binarytestincludes}"/>
</fileset>
</batchtest>
<classpath>
<path path="${run.test.classpath}"/>
</classpath>
<syspropertyset>
<propertyref prefix="test-sys-prop."/>
<mapper from="test-sys-prop.*" to="*" type="glob"/>
</syspropertyset>
<formatter type="brief" usefile="false"/>
<formatter type="xml"/>
<jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
<jvmarg value="-ea"/>
<jvmarg line="${run.jvmargs}"/>
<customize/>
</junit>
</sequential>
</macrodef>
</target>
<target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile, -profile-init-check" name="profile-init"/>
<target name="-profile-pre-init">
<target depends="-init-macrodef-junit-init,-init-macrodef-junit-single, -init-macrodef-junit-batch" if="${junit.available}" name="-init-macrodef-junit"/>
<target if="${testng.available}" name="-init-macrodef-testng">
<macrodef name="testng" uri="http://www.netbeans.org/ns/j2se-project/3">
<attribute default="${includes}" name="includes"/>
<attribute default="${excludes}" name="excludes"/>
<attribute default="**" name="testincludes"/>
<attribute default="" name="testmethods"/>
<element name="customize" optional="true"/>
<sequential>
<condition else="" property="testng.methods.arg" value="@{testincludes}.@{testmethods}">
<isset property="test.method"/>
</condition>
<union id="test.set">
<fileset dir="${test.src.dir}" excludes="@{excludes},**/*.xml,${excludes}" includes="@{includes}">
<filename name="@{testincludes}"/>
</fileset>
</union>
<taskdef classname="org.testng.TestNGAntTask" classpath="${run.test.classpath}" name="testng"/>
<testng classfilesetref="test.set" failureProperty="tests.failed" listeners="org.testng.reporters.VerboseReporter" methods="${testng.methods.arg}" mode="${testng.mode}" outputdir="${build.test.results.dir}" suitename="MoppyDesk" testname="TestNG tests" workingDir="${work.dir}">
<xmlfileset dir="${build.test.classes.dir}" includes="@{testincludes}"/>
<propertyset>
<propertyref prefix="test-sys-prop."/>
<mapper from="test-sys-prop.*" to="*" type="glob"/>
</propertyset>
<customize/>
</testng>
</sequential>
</macrodef>
</target>
<target name="-init-macrodef-test-impl">
<macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
<attribute default="${includes}" name="includes"/>
<attribute default="${excludes}" name="excludes"/>
<attribute default="**" name="testincludes"/>
<attribute default="" name="testmethods"/>
<element implicit="true" name="customize" optional="true"/>
<sequential>
<echo>No tests executed.</echo>
</sequential>
</macrodef>
</target>
<target depends="-init-macrodef-junit" if="${junit.available}" name="-init-macrodef-junit-impl">
<macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
<attribute default="${includes}" name="includes"/>
<attribute default="${excludes}" name="excludes"/>
<attribute default="**" name="testincludes"/>
<attribute default="" name="testmethods"/>
<element implicit="true" name="customize" optional="true"/>
<sequential>
<j2seproject3:junit excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
<customize/>
</j2seproject3:junit>
</sequential>
</macrodef>
</target>
<target depends="-init-macrodef-testng" if="${testng.available}" name="-init-macrodef-testng-impl">
<macrodef name="test-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
<attribute default="${includes}" name="includes"/>
<attribute default="${excludes}" name="excludes"/>
<attribute default="**" name="testincludes"/>
<attribute default="" name="testmethods"/>
<element implicit="true" name="customize" optional="true"/>
<sequential>
<j2seproject3:testng excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
<customize/>
</j2seproject3:testng>
</sequential>
</macrodef>
</target>
<target depends="-init-macrodef-test-impl,-init-macrodef-junit-impl,-init-macrodef-testng-impl" name="-init-macrodef-test">
<macrodef name="test" uri="http://www.netbeans.org/ns/j2se-project/3">
<attribute default="${includes}" name="includes"/>
<attribute default="${excludes}" name="excludes"/>
<attribute default="**" name="testincludes"/>
<attribute default="" name="testmethods"/>
<sequential>
<j2seproject3:test-impl excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
<customize>
<classpath>
<path path="${run.test.classpath}"/>
</classpath>
<jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
<jvmarg line="${run.jvmargs}"/>
<jvmarg line="${run.jvmargs.ide}"/>
</customize>
</j2seproject3:test-impl>
</sequential>
</macrodef>
</target>
<target if="${junit.available}" name="-init-macrodef-junit-debug" unless="${nb.junit.batch}">
<macrodef name="junit-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
<attribute default="${includes}" name="includes"/>
<attribute default="${excludes}" name="excludes"/>
<attribute default="**" name="testincludes"/>
<attribute default="" name="testmethods"/>
<element name="customize" optional="true"/>
<sequential>
<property name="junit.forkmode" value="perTest"/>
<junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
<test methods="@{testmethods}" name="@{testincludes}" todir="${build.test.results.dir}"/>
<syspropertyset>
<propertyref prefix="test-sys-prop."/>
<mapper from="test-sys-prop.*" to="*" type="glob"/>
</syspropertyset>
<formatter type="brief" usefile="false"/>
<formatter type="xml"/>
<jvmarg value="-ea"/>
<jvmarg line="${debug-args-line}"/>
<jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
<customize/>
</junit>
</sequential>
</macrodef>
</target>
<target depends="-init-test-properties" if="${nb.junit.batch}" name="-init-macrodef-junit-debug-batch">
<macrodef name="junit-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
<attribute default="${includes}" name="includes"/>
<attribute default="${excludes}" name="excludes"/>
<attribute default="**" name="testincludes"/>
<attribute default="" name="testmethods"/>
<element name="customize" optional="true"/>
<sequential>
<property name="junit.forkmode" value="perTest"/>
<junit dir="${work.dir}" errorproperty="tests.failed" failureproperty="tests.failed" fork="true" forkmode="${junit.forkmode}" showoutput="true" tempdir="${build.dir}">
<batchtest todir="${build.test.results.dir}">
<fileset dir="${test.src.dir}" excludes="@{excludes},${excludes}" includes="@{includes}">
<filename name="@{testincludes}"/>
</fileset>
<fileset dir="${build.test.classes.dir}" excludes="@{excludes},${excludes},${test.binaryexcludes}" includes="${test.binaryincludes}">
<filename name="${test.binarytestincludes}"/>
</fileset>
</batchtest>
<syspropertyset>
<propertyref prefix="test-sys-prop."/>
<mapper from="test-sys-prop.*" to="*" type="glob"/>
</syspropertyset>
<formatter type="brief" usefile="false"/>
<formatter type="xml"/>
<jvmarg value="-ea"/>
<jvmarg line="${debug-args-line}"/>
<jvmarg value="-Xrunjdwp:transport=${debug-transport},address=${jpda.address}"/>
<customize/>
</junit>
</sequential>
</macrodef>
</target>
<target depends="-init-macrodef-junit-debug,-init-macrodef-junit-debug-batch" if="${junit.available}" name="-init-macrodef-junit-debug-impl">
<macrodef name="test-debug-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
<attribute default="${includes}" name="includes"/>
<attribute default="${excludes}" name="excludes"/>
<attribute default="**" name="testincludes"/>
<attribute default="" name="testmethods"/>
<element implicit="true" name="customize" optional="true"/>
<sequential>
<j2seproject3:junit-debug excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
<customize/>
</j2seproject3:junit-debug>
</sequential>
</macrodef>
</target>
<target if="${testng.available}" name="-init-macrodef-testng-debug">
<macrodef name="testng-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
<attribute default="${main.class}" name="testClass"/>
<attribute default="" name="testMethod"/>
<element name="customize2" optional="true"/>
<sequential>
<condition else="-testclass @{testClass}" property="test.class.or.method" value="-methods @{testClass}.@{testMethod}">
<isset property="test.method"/>
</condition>
<condition else="-suitename MoppyDesk -testname @{testClass} ${test.class.or.method}" property="testng.cmd.args" value="@{testClass}">
<matches pattern=".*\.xml" string="@{testClass}"/>
</condition>
<delete dir="${build.test.results.dir}" quiet="true"/>
<mkdir dir="${build.test.results.dir}"/>
<j2seproject3:debug classname="org.testng.TestNG" classpath="${debug.test.classpath}">
<customize>
<customize2/>
<jvmarg value="-ea"/>
<arg line="${testng.debug.mode}"/>
<arg line="-d ${build.test.results.dir}"/>
<arg line="-listener org.testng.reporters.VerboseReporter"/>
<arg line="${testng.cmd.args}"/>
</customize>
</j2seproject3:debug>
</sequential>
</macrodef>
</target>
<target depends="-init-macrodef-testng-debug" if="${testng.available}" name="-init-macrodef-testng-debug-impl">
<macrodef name="testng-debug-impl" uri="http://www.netbeans.org/ns/j2se-project/3">
<attribute default="${main.class}" name="testClass"/>
<attribute default="" name="testMethod"/>
<element implicit="true" name="customize2" optional="true"/>
<sequential>
<j2seproject3:testng-debug testClass="@{testClass}" testMethod="@{testMethod}">
<customize2/>
</j2seproject3:testng-debug>
</sequential>
</macrodef>
</target>
<target depends="-init-macrodef-junit-debug-impl" if="${junit.available}" name="-init-macrodef-test-debug-junit">
<macrodef name="test-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
<attribute default="${includes}" name="includes"/>
<attribute default="${excludes}" name="excludes"/>
<attribute default="**" name="testincludes"/>
<attribute default="" name="testmethods"/>
<attribute default="${main.class}" name="testClass"/>
<attribute default="" name="testMethod"/>
<sequential>
<j2seproject3:test-debug-impl excludes="@{excludes}" includes="@{includes}" testincludes="@{testincludes}" testmethods="@{testmethods}">
<customize>
<classpath>
<path path="${run.test.classpath}"/>
</classpath>
<jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
<jvmarg line="${run.jvmargs}"/>
<jvmarg line="${run.jvmargs.ide}"/>
</customize>
</j2seproject3:test-debug-impl>
</sequential>
</macrodef>
</target>
<target depends="-init-macrodef-testng-debug-impl" if="${testng.available}" name="-init-macrodef-test-debug-testng">
<macrodef name="test-debug" uri="http://www.netbeans.org/ns/j2se-project/3">
<attribute default="${includes}" name="includes"/>
<attribute default="${excludes}" name="excludes"/>
<attribute default="**" name="testincludes"/>
<attribute default="" name="testmethods"/>
<attribute default="${main.class}" name="testClass"/>
<attribute default="" name="testMethod"/>
<sequential>
<j2seproject3:testng-debug-impl testClass="@{testClass}" testMethod="@{testMethod}">
<customize2>
<syspropertyset>
<propertyref prefix="test-sys-prop."/>
<mapper from="test-sys-prop.*" to="*" type="glob"/>
</syspropertyset>
</customize2>
</j2seproject3:testng-debug-impl>
</sequential>
</macrodef>
</target>
<target depends="-init-macrodef-test-debug-junit,-init-macrodef-test-debug-testng" name="-init-macrodef-test-debug"/>
<!--
pre NB7.2 profiling section; consider it deprecated
-->
<target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile, -profile-init-check" if="profiler.info.jvmargs.agent" name="profile-init"/>
<target if="profiler.info.jvmargs.agent" name="-profile-pre-init">
<!-- Empty placeholder for easier customization. -->
<!-- You can override this target in the ../build.xml file. -->
</target>
<target name="-profile-post-init">
<target if="profiler.info.jvmargs.agent" name="-profile-post-init">
<!-- Empty placeholder for easier customization. -->
<!-- You can override this target in the ../build.xml file. -->
</target>
<target name="-profile-init-macrodef-profile">
<target if="profiler.info.jvmargs.agent" name="-profile-init-macrodef-profile">
<macrodef name="resolve">
<attribute name="name"/>
<attribute name="value"/>
@ -384,6 +694,7 @@ is divided into following sections:
<property environment="env"/>
<resolve name="profiler.current.path" value="${profiler.info.pathvar}"/>
<java classname="@{classname}" dir="${profiler.info.dir}" fork="true" jvm="${profiler.info.jvm}">
<jvmarg line="${endorsed.classpath.cmd.line.arg}"/>
<jvmarg value="${profiler.info.jvmargs.agent}"/>
<jvmarg line="${profiler.info.jvmargs}"/>
<env key="${profiler.info.pathvar}" path="${profiler.info.agentpath}:${profiler.current.path}"/>
@ -400,10 +711,13 @@ is divided into following sections:
</sequential>
</macrodef>
</target>
<target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile" name="-profile-init-check">
<target depends="-profile-pre-init, init, -profile-post-init, -profile-init-macrodef-profile" if="profiler.info.jvmargs.agent" name="-profile-init-check">
<fail unless="profiler.info.jvm">Must set JVM to use for profiling in profiler.info.jvm</fail>
<fail unless="profiler.info.jvmargs.agent">Must set profiler agent JVM arguments in profiler.info.jvmargs.agent</fail>
</target>
<!--
end of pre NB7.2 profiling section
-->
<target depends="-init-debug-args" name="-init-macrodef-nbjpda">
<macrodef name="nbjpdastart" uri="http://www.netbeans.org/ns/j2se-project/1">
<attribute default="${main.class}" name="name"/>
@ -461,6 +775,7 @@ is divided into following sections:
<jvmarg value="-Dfile.encoding=${runtime.encoding}"/>
<redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/>
<jvmarg line="${run.jvmargs}"/>
<jvmarg line="${run.jvmargs.ide}"/>
<classpath>
<path path="@{classpath}"/>
</classpath>
@ -477,6 +792,7 @@ is divided into following sections:
<macrodef name="java" uri="http://www.netbeans.org/ns/j2se-project/1">
<attribute default="${main.class}" name="classname"/>
<attribute default="${run.classpath}" name="classpath"/>
<attribute default="jvm" name="jvm"/>
<element name="customize" optional="true"/>
<sequential>
<java classname="@{classname}" dir="${work.dir}" fork="true">
@ -484,6 +800,7 @@ is divided into following sections:
<jvmarg value="-Dfile.encoding=${runtime.encoding}"/>
<redirector errorencoding="${runtime.encoding}" inputencoding="${runtime.encoding}" outputencoding="${runtime.encoding}"/>
<jvmarg line="${run.jvmargs}"/>
<jvmarg line="${run.jvmargs.ide}"/>
<classpath>
<path path="@{classpath}"/>
</classpath>
@ -510,12 +827,15 @@ is divided into following sections:
<path path="${run.classpath.without.build.classes.dir}"/>
<chainedmapper>
<flattenmapper/>
<filtermapper>
<replacestring from=" " to="%20"/>
</filtermapper>
<globmapper from="*" to="lib/*"/>
</chainedmapper>
</pathconvert>
<taskdef classname="org.netbeans.modules.java.j2seproject.copylibstask.CopyLibs" classpath="${libs.CopyLibs.classpath}" name="copylibs"/>
<copylibs compress="${jar.compress}" index="${jar.index}" indexMetaInf="${jar.index.metainf}" jarfile="${dist.jar}" manifest="@{manifest}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
<fileset dir="${build.classes.dir}"/>
<copylibs compress="${jar.compress}" excludeFromCopy="${copylibs.excludes}" index="${jar.index}" indexMetaInf="${jar.index.metainf}" jarfile="${dist.jar}" manifest="@{manifest}" rebase="${copylibs.rebase}" runtimeclasspath="${run.classpath.without.build.classes.dir}">
<fileset dir="${build.classes.dir}" excludes="${dist.archive.excludes}"/>
<manifest>
<attribute name="Class-Path" value="${jar.classpath}"/>
<customize/>
@ -527,7 +847,7 @@ is divided into following sections:
<target name="-init-presetdef-jar">
<presetdef name="jar" uri="http://www.netbeans.org/ns/j2se-project/1">
<jar compress="${jar.compress}" index="${jar.index}" jarfile="${dist.jar}">
<j2seproject1:fileset dir="${build.classes.dir}"/>
<j2seproject1:fileset dir="${build.classes.dir}" excludes="${dist.archive.excludes}"/>
</jar>
</presetdef>
</target>
@ -555,7 +875,7 @@ is divided into following sections:
<target depends="-init-ap-cmdline-properties,-init-ap-cmdline-supported" name="-init-ap-cmdline">
<property name="ap.cmd.line.internal" value=""/>
</target>
<target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-junit,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar,-init-ap-cmdline" name="init"/>
<target depends="-pre-init,-init-private,-init-user,-init-project,-do-init,-post-init,-init-check,-init-macrodef-property,-init-macrodef-javac,-init-macrodef-test,-init-macrodef-test-debug,-init-macrodef-nbjpda,-init-macrodef-debug,-init-macrodef-java,-init-presetdef-jar,-init-ap-cmdline" name="init"/>
<!--
===================
COMPILATION SECTION
@ -608,7 +928,7 @@ is divided into following sections:
<target if="has.persistence.xml" name="-copy-persistence-xml">
<mkdir dir="${build.classes.dir}/META-INF"/>
<copy todir="${build.classes.dir}/META-INF">
<fileset dir="${meta.inf.dir}" includes="persistence.xml"/>
<fileset dir="${meta.inf.dir}" includes="persistence.xml orm.xml"/>
</copy>
</target>
<target name="-post-compile">
@ -643,41 +963,25 @@ is divided into following sections:
<!-- Empty placeholder for easier customization. -->
<!-- You can override this target in the ../build.xml file. -->
</target>
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="do.archive" name="-do-jar-without-manifest" unless="manifest.available-mkdist.available">
<j2seproject1:jar/>
</target>
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="do.archive+manifest.available" name="-do-jar-with-manifest" unless="manifest.available+main.class-mkdist.available">
<j2seproject1:jar manifest="${manifest.file}"/>
</target>
<target depends="init,compile,-pre-pre-jar,-pre-jar" if="do.archive+manifest.available+main.class" name="-do-jar-with-mainclass" unless="manifest.available+main.class+mkdist.available">
<j2seproject1:jar manifest="${manifest.file}">
<j2seproject1:manifest>
<j2seproject1:attribute name="Main-Class" value="${main.class}"/>
</j2seproject1:manifest>
</j2seproject1:jar>
<echo level="info">To run this application from the command line without Ant, try:</echo>
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
<property location="${dist.jar}" name="dist.jar.resolved"/>
<pathconvert property="run.classpath.with.dist.jar">
<path path="${run.classpath}"/>
<map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
</pathconvert>
<echo level="info">java -cp "${run.classpath.with.dist.jar}" ${main.class}</echo>
</target>
<target depends="init" if="do.archive" name="-do-jar-with-libraries-create-manifest" unless="manifest.available">
<target depends="init" if="do.archive" name="-do-jar-create-manifest" unless="manifest.available">
<tempfile deleteonexit="true" destdir="${build.dir}" property="tmp.manifest.file"/>
<touch file="${tmp.manifest.file}" verbose="false"/>
</target>
<target depends="init" if="do.archive+manifest.available" name="-do-jar-with-libraries-copy-manifest">
<target depends="init" if="do.archive+manifest.available" name="-do-jar-copy-manifest">
<tempfile deleteonexit="true" destdir="${build.dir}" property="tmp.manifest.file"/>
<copy file="${manifest.file}" tofile="${tmp.manifest.file}"/>
</target>
<target depends="init,-do-jar-with-libraries-create-manifest,-do-jar-with-libraries-copy-manifest" if="do.archive+main.class.available" name="-do-jar-with-libraries-set-main">
<target depends="init,-do-jar-create-manifest,-do-jar-copy-manifest" if="do.archive+main.class.available" name="-do-jar-set-mainclass">
<manifest file="${tmp.manifest.file}" mode="update">
<attribute name="Main-Class" value="${main.class}"/>
</manifest>
</target>
<target depends="init,-do-jar-with-libraries-create-manifest,-do-jar-with-libraries-copy-manifest" if="do.archive+splashscreen.available" name="-do-jar-with-libraries-set-splashscreen">
<target depends="init,-do-jar-create-manifest,-do-jar-copy-manifest" if="do.archive+profile.available" name="-do-jar-set-profile">
<manifest file="${tmp.manifest.file}" mode="update">
<attribute name="Profile" value="${javac.profile}"/>
</manifest>
</target>
<target depends="init,-do-jar-create-manifest,-do-jar-copy-manifest" if="do.archive+splashscreen.available" name="-do-jar-set-splashscreen">
<basename file="${application.splash}" property="splashscreen.basename"/>
<mkdir dir="${build.classes.dir}/META-INF"/>
<copy failonerror="false" file="${application.splash}" todir="${build.classes.dir}/META-INF"/>
@ -685,23 +989,41 @@ is divided into following sections:
<attribute name="SplashScreen-Image" value="META-INF/${splashscreen.basename}"/>
</manifest>
</target>
<target depends="init,-init-macrodef-copylibs,compile,-pre-pre-jar,-pre-jar,-do-jar-with-libraries-create-manifest,-do-jar-with-libraries-copy-manifest,-do-jar-with-libraries-set-main,-do-jar-with-libraries-set-splashscreen" if="do.mkdist" name="-do-jar-with-libraries-pack">
<target depends="init,-init-macrodef-copylibs,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen" if="do.mkdist" name="-do-jar-copylibs">
<j2seproject3:copylibs manifest="${tmp.manifest.file}"/>
<echo level="info">To run this application from the command line without Ant, try:</echo>
<property location="${dist.jar}" name="dist.jar.resolved"/>
<echo level="info">java -jar "${dist.jar.resolved}"</echo>
</target>
<target depends="-do-jar-with-libraries-pack" if="do.archive" name="-do-jar-with-libraries-delete-manifest">
<target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen" if="do.archive" name="-do-jar-jar" unless="do.mkdist">
<j2seproject1:jar manifest="${tmp.manifest.file}"/>
<property location="${build.classes.dir}" name="build.classes.dir.resolved"/>
<property location="${dist.jar}" name="dist.jar.resolved"/>
<pathconvert property="run.classpath.with.dist.jar">
<path path="${run.classpath}"/>
<map from="${build.classes.dir.resolved}" to="${dist.jar.resolved}"/>
</pathconvert>
<condition else="" property="jar.usage.message" value="To run this application from the command line without Ant, try:${line.separator}${platform.java} -cp ${run.classpath.with.dist.jar} ${main.class}">
<isset property="main.class.available"/>
</condition>
<condition else="debug" property="jar.usage.level" value="info">
<isset property="main.class.available"/>
</condition>
<echo level="${jar.usage.level}" message="${jar.usage.message}"/>
</target>
<target depends="-do-jar-copylibs" if="do.archive" name="-do-jar-delete-manifest">
<delete>
<fileset file="${tmp.manifest.file}"/>
</delete>
</target>
<target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-with-libraries-create-manifest,-do-jar-with-libraries-copy-manifest,-do-jar-with-libraries-set-main,-do-jar-with-libraries-set-splashscreen,-do-jar-with-libraries-pack,-do-jar-with-libraries-delete-manifest" name="-do-jar-with-libraries"/>
<target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen,-do-jar-jar,-do-jar-delete-manifest" name="-do-jar-without-libraries"/>
<target depends="init,compile,-pre-pre-jar,-pre-jar,-do-jar-create-manifest,-do-jar-copy-manifest,-do-jar-set-mainclass,-do-jar-set-profile,-do-jar-set-splashscreen,-do-jar-copylibs,-do-jar-delete-manifest" name="-do-jar-with-libraries"/>
<target name="-post-jar">
<!-- Empty placeholder for easier customization. -->
<!-- You can override this target in the ../build.xml file. -->
</target>
<target depends="init,compile,-pre-jar,-do-jar-with-manifest,-do-jar-without-manifest,-do-jar-with-mainclass,-do-jar-with-libraries,-post-jar" description="Build JAR." name="jar"/>
<target depends="init,compile,-pre-jar,-do-jar-without-libraries,-do-jar-with-libraries,-post-jar" name="-do-jar"/>
<target depends="init,compile,-pre-jar,-do-jar,-post-jar" description="Build JAR." name="jar"/>
<!--
=================
EXECUTION SECTION
@ -771,7 +1093,11 @@ is divided into following sections:
PROFILING SECTION
=================
-->
<target depends="profile-init,compile" description="Profile a project in the IDE." if="netbeans.home" name="profile">
<!--
pre NB7.2 profiler integration
-->
<target depends="profile-init,compile" description="Profile a project in the IDE." if="profiler.info.jvmargs.agent" name="-profile-pre72">
<fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
<nbprofiledirect>
<classpath>
<path path="${run.classpath}"/>
@ -779,8 +1105,9 @@ is divided into following sections:
</nbprofiledirect>
<profile/>
</target>
<target depends="profile-init,compile-single" description="Profile a selected class in the IDE." if="netbeans.home" name="profile-single">
<target depends="profile-init,compile-single" description="Profile a selected class in the IDE." if="profiler.info.jvmargs.agent" name="-profile-single-pre72">
<fail unless="profile.class">Must select one file in the IDE or set profile.class</fail>
<fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
<nbprofiledirect>
<classpath>
<path path="${run.classpath}"/>
@ -788,12 +1115,8 @@ is divided into following sections:
</nbprofiledirect>
<profile classname="${profile.class}"/>
</target>
<!--
=========================
APPLET PROFILING SECTION
=========================
-->
<target depends="profile-init,compile-single" if="netbeans.home" name="profile-applet">
<target depends="profile-init,compile-single" if="profiler.info.jvmargs.agent" name="-profile-applet-pre72">
<fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
<nbprofiledirect>
<classpath>
<path path="${run.classpath}"/>
@ -805,12 +1128,8 @@ is divided into following sections:
</customize>
</profile>
</target>
<!--
=========================
TESTS PROFILING SECTION
=========================
-->
<target depends="profile-init,compile-test-single" if="netbeans.home" name="profile-test-single">
<target depends="profile-init,compile-test-single" if="profiler.info.jvmargs.agent" name="-profile-test-single-pre72">
<fail unless="netbeans.home">This target only works when run from inside the NetBeans IDE.</fail>
<nbprofiledirect>
<classpath>
<path path="${run.test.classpath}"/>
@ -832,6 +1151,42 @@ is divided into following sections:
<formatter type="xml"/>
</junit>
</target>
<!--
end of pre NB72 profiling section
-->
<target if="netbeans.home" name="-profile-check">
<condition property="profiler.configured">
<or>
<contains casesensitive="true" string="${run.jvmargs.ide}" substring="-agentpath:"/>
<contains casesensitive="true" string="${run.jvmargs.ide}" substring="-javaagent:"/>
</or>
</condition>
</target>
<target depends="-profile-check,-profile-pre72" description="Profile a project in the IDE." if="profiler.configured" name="profile" unless="profiler.info.jvmargs.agent">
<startprofiler/>
<antcall target="run"/>
</target>
<target depends="-profile-check,-profile-single-pre72" description="Profile a selected class in the IDE." if="profiler.configured" name="profile-single" unless="profiler.info.jvmargs.agent">
<fail unless="run.class">Must select one file in the IDE or set run.class</fail>
<startprofiler/>
<antcall target="run-single"/>
</target>
<target depends="-profile-test-single-pre72" description="Profile a selected test in the IDE." name="profile-test-single"/>
<target depends="-profile-check" description="Profile a selected test in the IDE." if="profiler.configured" name="profile-test" unless="profiler.info.jvmargs">
<fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
<startprofiler/>
<antcall target="test-single"/>
</target>
<target depends="-profile-check" description="Profile a selected class in the IDE." if="profiler.configured" name="profile-test-with-main">
<fail unless="run.class">Must select one file in the IDE or set run.class</fail>
<startprofiler/>
<antcal target="run-test-with-main"/>
</target>
<target depends="-profile-check,-profile-applet-pre72" if="profiler.configured" name="profile-applet" unless="profiler.info.jvmargs.agent">
<fail unless="applet.url">Must select one file in the IDE or set applet.url</fail>
<startprofiler/>
<antcall target="run-applet"/>
</target>
<!--
===============
JAVADOC SECTION
@ -839,17 +1194,29 @@ is divided into following sections:
-->
<target depends="init" if="have.sources" name="-javadoc-build">
<mkdir dir="${dist.javadoc.dir}"/>
<javadoc additionalparam="${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
<condition else="" property="javadoc.endorsed.classpath.cmd.line.arg" value="-J${endorsed.classpath.cmd.line.arg}">
<and>
<isset property="endorsed.classpath.cmd.line.arg"/>
<not>
<equals arg1="${endorsed.classpath.cmd.line.arg}" arg2=""/>
</not>
</and>
</condition>
<condition else="" property="bug5101868workaround" value="*.java">
<matches pattern="1\.[56](\..*)?" string="${java.version}"/>
</condition>
<javadoc additionalparam="-J-Dfile.encoding=${file.encoding} ${javadoc.additionalparam}" author="${javadoc.author}" charset="UTF-8" destdir="${dist.javadoc.dir}" docencoding="UTF-8" encoding="${javadoc.encoding.used}" failonerror="true" noindex="${javadoc.noindex}" nonavbar="${javadoc.nonavbar}" notree="${javadoc.notree}" private="${javadoc.private}" source="${javac.source}" splitindex="${javadoc.splitindex}" use="${javadoc.use}" useexternalfile="true" version="${javadoc.version}" windowtitle="${javadoc.windowtitle}">
<classpath>
<path path="${javac.classpath}"/>
</classpath>
<fileset dir="${src.dir}" excludes="*.java,${excludes}" includes="${includes}">
<fileset dir="${src.dir}" excludes="${bug5101868workaround},${excludes}" includes="${includes}">
<filename name="**/*.java"/>
</fileset>
<fileset dir="${build.generated.sources.dir}" erroronmissingdir="false">
<include name="**/*.java"/>
<exclude name="*.java"/>
</fileset>
<arg line="${javadoc.endorsed.classpath.cmd.line.arg}"/>
</javadoc>
<copy todir="${dist.javadoc.dir}">
<fileset dir="${src.dir}" excludes="${excludes}" includes="${includes}">
@ -866,7 +1233,7 @@ is divided into following sections:
<target depends="init,-javadoc-build,-javadoc-browse" description="Build Javadoc." name="javadoc"/>
<!--
=========================
JUNIT COMPILATION SECTION
TEST COMPILATION SECTION
=========================
-->
<target depends="init,compile" if="have.tests" name="-pre-pre-compile-test">
@ -909,14 +1276,14 @@ is divided into following sections:
<target depends="init,compile,-pre-pre-compile-test,-pre-compile-test-single,-do-compile-test-single,-post-compile-test-single" name="compile-test-single"/>
<!--
=======================
JUNIT EXECUTION SECTION
TEST EXECUTION SECTION
=======================
-->
<target depends="init" if="have.tests" name="-pre-test-run">
<mkdir dir="${build.test.results.dir}"/>
</target>
<target depends="init,compile-test,-pre-test-run" if="have.tests" name="-do-test-run">
<j2seproject3:junit testincludes="**/*Test.java"/>
<j2seproject3:test includes="${includes}" testincludes="**/*Test.java"/>
</target>
<target depends="init,compile-test,-pre-test-run,-do-test-run" if="have.tests" name="-post-test-run">
<fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
@ -929,39 +1296,40 @@ is divided into following sections:
</target>
<target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single">
<fail unless="test.includes">Must select some files in the IDE or set test.includes</fail>
<j2seproject3:junit excludes="" includes="${test.includes}"/>
<j2seproject3:test excludes="" includes="${test.includes}" testincludes="${test.includes}"/>
</target>
<target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single" if="have.tests" name="-post-test-run-single">
<fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
</target>
<target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single,-post-test-run-single" description="Run single unit test." name="test-single"/>
<target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-do-test-run-single-method">
<fail unless="test.class">Must select some files in the IDE or set test.class</fail>
<fail unless="test.method">Must select some method in the IDE or set test.method</fail>
<j2seproject3:test excludes="" includes="${javac.includes}" testincludes="${test.class}" testmethods="${test.method}"/>
</target>
<target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single-method" if="have.tests" name="-post-test-run-single-method">
<fail if="tests.failed" unless="ignore.failing.tests">Some tests failed; see details above.</fail>
</target>
<target depends="init,compile-test-single,-pre-test-run-single,-do-test-run-single-method,-post-test-run-single-method" description="Run single unit test." name="test-single-method"/>
<!--
=======================
JUNIT DEBUGGING SECTION
TEST DEBUGGING SECTION
=======================
-->
<target depends="init,compile-test" if="have.tests" name="-debug-start-debuggee-test">
<target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-debug-start-debuggee-test">
<fail unless="test.class">Must select one file in the IDE or set test.class</fail>
<property location="${build.test.results.dir}/TEST-${test.class}.xml" name="test.report.file"/>
<delete file="${test.report.file}"/>
<mkdir dir="${build.test.results.dir}"/>
<j2seproject3:debug classname="org.apache.tools.ant.taskdefs.optional.junit.JUnitTestRunner" classpath="${ant.home}/lib/ant.jar:${ant.home}/lib/ant-junit.jar:${debug.test.classpath}">
<customize>
<syspropertyset>
<propertyref prefix="test-sys-prop."/>
<mapper from="test-sys-prop.*" to="*" type="glob"/>
</syspropertyset>
<arg value="${test.class}"/>
<arg value="showoutput=true"/>
<arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.BriefJUnitResultFormatter"/>
<arg value="formatter=org.apache.tools.ant.taskdefs.optional.junit.XMLJUnitResultFormatter,${test.report.file}"/>
</customize>
</j2seproject3:debug>
<j2seproject3:test-debug excludes="" includes="${javac.includes}" testClass="${test.class}" testincludes="${javac.includes}"/>
</target>
<target depends="init,compile-test-single,-pre-test-run-single" if="have.tests" name="-debug-start-debuggee-test-method">
<fail unless="test.class">Must select one file in the IDE or set test.class</fail>
<fail unless="test.method">Must select some method in the IDE or set test.method</fail>
<j2seproject3:test-debug excludes="" includes="${javac.includes}" testClass="${test.class}" testMethod="${test.method}" testincludes="${test.class}" testmethods="${test.method}"/>
</target>
<target depends="init,compile-test" if="netbeans.home+have.tests" name="-debug-start-debugger-test">
<j2seproject1:nbjpdastart classpath="${debug.test.classpath}" name="${test.class}"/>
</target>
<target depends="init,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test" name="debug-test"/>
<target depends="init,compile-test-single,-debug-start-debugger-test,-debug-start-debuggee-test-method" name="debug-test-method"/>
<target depends="init,-pre-debug-fix,compile-test-single" if="netbeans.home" name="-do-debug-fix-test">
<j2seproject1:nbjpdareload dir="${build.test.classes.dir}"/>
</target>
@ -1026,9 +1394,12 @@ is divided into following sections:
<target name="-check-call-dep">
<property file="${call.built.properties}" prefix="already.built."/>
<condition property="should.call.dep">
<not>
<isset property="already.built.${call.subproject}"/>
</not>
<and>
<not>
<isset property="already.built.${call.subproject}"/>
</not>
<available file="${call.script}"/>
</and>
</condition>
</target>
<target depends="-check-call-dep" if="should.call.dep" name="-maybe-call-dep">

View File

@ -1,8 +1,8 @@
build.xml.data.CRC32=23b52058
build.xml.script.CRC32=b64bcd8b
build.xml.stylesheet.CRC32=28e38971@1.38.2.45
build.xml.data.CRC32=bfb77442
build.xml.script.CRC32=b3725edf
build.xml.stylesheet.CRC32=8064a381@1.75.2.48
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
nbproject/build-impl.xml.data.CRC32=23b52058
nbproject/build-impl.xml.script.CRC32=9ea63390
nbproject/build-impl.xml.stylesheet.CRC32=0ae3a408@1.44.1.45
nbproject/build-impl.xml.data.CRC32=bfb77442
nbproject/build-impl.xml.script.CRC32=7aa67ee7
nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.2.48

View File

@ -1,9 +1,12 @@
annotation.processing.enabled=true
annotation.processing.enabled.in.editor=false
annotation.processing.processors.list=
annotation.processing.run.all.processors=true
annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
application.homepage=https://github.com/SammyIAm/Moppy
application.title=MoppyDesk
application.vendor=Sam
application.vendor=Sammy1Am
auxiliary.org-netbeans-spi-editor-hints-projects.perProjectHintSettingsFile=nbproject/cfg_hints.xml
build.classes.dir=${build.dir}/classes
build.classes.excludes=**/*.java,**/*.form
# This directory is removed when the project is cleaned:
@ -26,19 +29,20 @@ dist.jar=${dist.dir}/MoppyDesk.jar
dist.javadoc.dir=${dist.dir}/javadoc
endorsed.classpath=
excludes=
file.reference.RXTXcomm.jar=lib\\RXTXcomm.jar
file.reference.nrjavaserial-3.9.3.jar=..\\SerialDrivers\\nrjavaserial-3.9.3.jar
includes=**
jar.archive.disabled=${jnlp.enabled}
jar.compress=false
jar.index=${jnlp.enabled}
javac.classpath=\
${file.reference.RXTXcomm.jar}:\
${libs.swing-app-framework.classpath}
${file.reference.nrjavaserial-3.9.3.jar}
# Space-separated list of extra javac options
javac.compilerargs=
javac.deprecation=false
javac.processorpath=\
${javac.classpath}
javac.source=1.5
javac.target=1.5
javac.source=1.7
javac.target=1.7
javac.test.classpath=\
${javac.classpath}:\
${build.classes.dir}
@ -55,7 +59,20 @@ javadoc.splitindex=true
javadoc.use=true
javadoc.version=false
javadoc.windowtitle=
jnlp.codebase.type=no.codebase
jnlp.descriptor=application
jnlp.enabled=false
jnlp.mixed.code=default
jnlp.offline-allowed=false
jnlp.signed=false
jnlp.signing=
jnlp.signing.alias=
jnlp.signing.keystore=
main.class=moppydesk.MoppyUI
# Optional override of default Codebase manifest attribute, use to prevent RIAs from being repurposed
manifest.custom.codebase=
# Optional override of default Permissions manifest attribute (supported values: sandbox, all-permissions)
manifest.custom.permissions=
manifest.file=manifest.mf
meta.inf.dir=${src.dir}/META-INF
mkdist.disabled=false
@ -63,10 +80,6 @@ platform.active=default_platform
run.classpath=\
${javac.classpath}:\
${build.classes.dir}
# Space-separated list of JVM arguments used when running the project
# (you may also define separate properties like run-sys-prop.name=value instead of -Dname=value
# or test-sys-prop.name=value to set system properties for unit tests):
run.jvmargs=-Djava.library.path="../SerialDrivers/rxtx-2.1-7-bins-r2/Windows/ch-rxtx-2.2-20081207-win-x64;${env_var:PATH}"
run.test.classpath=\
${javac.test.classpath}:\
${build.test.classes.dir}

View File

@ -1,15 +1,18 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.java.j2seproject</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
<name>MoppyDesk</name>
<source-roots>
<root id="src.dir"/>
</source-roots>
<test-roots>
<root id="test.src.dir"/>
</test-roots>
</data>
</configuration>
</project>
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.java.j2seproject</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
<name>MoppyDesk</name>
<source-roots>
<root id="src.dir"/>
</source-roots>
<test-roots>
<root id="test.src.dir"/>
</test-roots>
</data>
<spellchecker-wordlist xmlns="http://www.netbeans.org/ns/spellchecker-wordlist/1">
<word>Arduino</word>
</spellchecker-wordlist>
</configuration>
</project>

View File

@ -0,0 +1,33 @@
package moppydesk;
/**
*
* @author Sammy1Am
*/
public class Constants {
//Sequencer preferences
public static final String PREF_LOADED_SEQ = "loadedSequencePath";
public static final String PREF_DELAY_RESET = "delayResetValue";
public static final String PREF_RESET_DRIVES = "resetDrivesValue";
public static final String PREF_REPEAT_SEQ = "repeatSequenceValue";
public static final String PREF_OUTPUT_SETTINGS = "outputSettingsArray";
//Playlist preferences
public static final String PREF_LOADED_LIST = "loadedPlaylistPath";
public static final String PREF_LOADED_MPL = "lastLoadedPlaylistFile";
public static final String PREF_LOAD_MPL_ON_START = "loadMPLValue";
//Pooling preferences
public static final String PREF_POOL_ENABLE = "poolingEnabled";
public static final String PREF_POOL_FROM_START = "poolingFromStart";
public static final String PREF_POOL_FROM_END = "poolingFromEnd";
public static final String PREF_POOL_TO_START = "poolingToStart";
public static final String PREF_POOL_TO_END = "poolingToEnd";
public static final String PREF_POOL_STRATEGY = "poolingStrategy";
//Filter preferences
public static final String PREF_FILTER_CONSTRAIN = "filterAutoConstrain";
public static final String PREF_FILTER_IGNORETEN = "ignore10";
public static final int NUM_MIDI_CHANNELS = 16;
}

View File

@ -1,101 +0,0 @@
package moppydesk;
import gnu.io.CommPortIdentifier;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.SerialPort;
import gnu.io.UnsupportedCommOperationException;
import java.io.IOException;
import java.io.OutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author Sammy1Am
*/
public class MoppyBridge {
static int FIRST_PIN = 2;
static int MAX_PIN = 17;
int SERIAL_RATE = 9600;
OutputStream os;
SerialPort com;
public MoppyBridge(String portName) throws NoSuchPortException, PortInUseException, UnsupportedCommOperationException, IOException {
CommPortIdentifier cpi = CommPortIdentifier.getPortIdentifier(portName);
com = (SerialPort) cpi.open("MoppyDesk", 2000);
com.setSerialPortParams(SERIAL_RATE, SerialPort.DATABITS_8, SerialPort.STOPBITS_1, SerialPort.PARITY_NONE);
os = com.getOutputStream();
}
/**
* Convenience method that splits the periodData int
* into two bytes for sending over serial.
* @param pin Controller pin to handle ntoe
* @param periodData length of period in microSeconds
*/
public void sendEvent(byte pin, int periodData){
sendEvent(pin, (byte)((periodData >> 8) & 0xFF), (byte)(periodData & 0xFF));
}
/**
* Sends an event to the Arduino.
* @param pin Controller pin
* @param b1
* @param b2
*/
public void sendEvent(byte pin, byte b1, byte b2){
sendArray(new byte[] {pin, b1, b2});
}
private void sendArray(byte[] message){
try {
os.write(message);
os.flush();
} catch (IOException ex) {
Logger.getLogger(MoppyBridge.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* Sends a '0' period to all drives to silence them.
*/
private void silenceDrives(){
// Stop notes
for (int d=FIRST_PIN;d<=MAX_PIN;d+=2){
sendArray(new byte[] {(byte)d,(byte)0,(byte)0});
}
}
/**
* Sends a special code (first byte=100) to reset the drives
*/
public void resetDrives(){
silenceDrives();
//Send reset code
sendArray(new byte[] {(byte)100,(byte)0,(byte)0});
try {
Thread.sleep(1500); // Give the drives time to reset
} catch (InterruptedException ex) {
Logger.getLogger(MoppyBridge.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void close(){
if (os != null){
silenceDrives();
try {
os.close();
} catch (IOException ex) {
Logger.getLogger(MoppyBridge.class.getName()).log(Level.SEVERE, null, ex);
}
}
if (com != null){
com.close();
}
}
}

View File

@ -1,470 +0,0 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
/*
* MoppyMainWindow.java
*
* Created on Oct 8, 2011, 8:34:57 PM
*/
package moppydesk;
import gnu.io.CommPortIdentifier;
import java.awt.Component;
import java.io.File;
import java.util.Enumeration;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.Preferences;
import javax.swing.JComboBox;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JSlider;
/**
*
* @author Sam
*/
public class MoppyMainWindow extends javax.swing.JFrame implements MoppyStatusConsumer {
static String PREF_COM_PORT = "comPort";
static String PREF_LOADED_SEQ = "loadedSequencePath";
MoppyUI app;
Preferences prefs = Preferences.userNodeForPackage(MoppyUI.class);
final JFileChooser sequenceChooser = new JFileChooser();
boolean comboBoxReady = false;
/** Creates new form MoppyMainWindow */
public MoppyMainWindow(MoppyUI app) {
this.app = app;
initComponents();
updateComSelectionMenu();
statusLabel.setText("Ready");
// initializeSequencer((String) comSelectionMenu.getSelectedItem());
//
//
}
private void updateComSelectionMenu() {
comboBoxReady = false;
comSelectionMenu.removeAllItems();
Enumeration<CommPortIdentifier> e = CommPortIdentifier.getPortIdentifiers();
while (e.hasMoreElements()) {
comSelectionMenu.addItem(e.nextElement().getName());
}
comSelectionMenu.setSelectedItem(prefs.get(PREF_COM_PORT, ""));
comboBoxReady = true;
}
private void initializeSequencer(String comPort) {
statusLabel.setText("Initializing sequencer...");
if (app.ms != null) {
app.ms.closeSequencer();
}
try {
app.ms = new MoppySequencer(comPort);
app.ms.addListener(this);
} catch (Exception ex) {
Logger.getLogger(MoppyMainWindow.class.getName()).log(Level.SEVERE, null, ex);
statusLabel.setText("Sequencer initialization error!");
JOptionPane.showMessageDialog(rootPane, ex);
}
statusLabel.setText("Sequencer connected to " + comPort);
String previouslyLoadedFile = prefs.get(PREF_LOADED_SEQ, null);
if (previouslyLoadedFile != null) {
loadSequenceFile(new File(previouslyLoadedFile));
}
setSequenceControlsEnabled(true);
connectSeqButton.setText("Disconnect");
}
private void shutdownSequencer() {
statusLabel.setText("Closing sequencer...");
if (app.ms != null) {
app.ms.closeSequencer();
app.ms = null;
}
setSequenceControlsEnabled(false);
connectSeqButton.setText("Connect");
statusLabel.setText("Sequencer closed");
}
/** This method is called from within the constructor to
* initialize the form.
* WARNING: Do NOT modify this code. The content of this method is
* always regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
comSelectionMenu = new javax.swing.JComboBox();
statusLabel = new javax.swing.JLabel();
jSeparator1 = new javax.swing.JSeparator();
jTabbedPane1 = new javax.swing.JTabbedPane();
jPanel1 = new javax.swing.JPanel();
sequencerControlsPanel = new javax.swing.JPanel();
jLabel1 = new javax.swing.JLabel();
sequenceNameLabel = new javax.swing.JLabel();
startButton = new javax.swing.JButton();
stopButton = new javax.swing.JButton();
loadButton = new javax.swing.JButton();
jSlider1 = new javax.swing.JSlider();
bpmLabel = new javax.swing.JLabel();
jSeparator2 = new javax.swing.JSeparator();
connectSeqButton = new javax.swing.JButton();
jPanel2 = new javax.swing.JPanel();
jLabel2 = new javax.swing.JLabel();
jLabel3 = new javax.swing.JLabel();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("MoppyDesk");
setMinimumSize(new java.awt.Dimension(480, 410));
addWindowListener(new java.awt.event.WindowAdapter() {
public void windowClosing(java.awt.event.WindowEvent evt) {
MoppyMainWindow.this.windowClosing(evt);
}
});
comSelectionMenu.setName("comSelectionMenu"); // NOI18N
comSelectionMenu.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
comPortSelected(evt);
}
});
statusLabel.setText("Loading application...");
statusLabel.setName("statusLabel"); // NOI18N
jSeparator1.setName("jSeparator1"); // NOI18N
jTabbedPane1.setName("bridgePanel"); // NOI18N
jPanel1.setName("sequencerPanel"); // NOI18N
jPanel1.addComponentListener(new java.awt.event.ComponentAdapter() {
public void componentHidden(java.awt.event.ComponentEvent evt) {
seqTabHidden(evt);
}
});
sequencerControlsPanel.setName("sequencerControlsPanel"); // NOI18N
sequencerControlsPanel.setPreferredSize(new java.awt.Dimension(200, 200));
jLabel1.setText("Current Sequence:");
jLabel1.setEnabled(false);
jLabel1.setName("jLabel1"); // NOI18N
sequenceNameLabel.setText("<None loaded>");
sequenceNameLabel.setEnabled(false);
sequenceNameLabel.setName("sequenceNameLabel"); // NOI18N
startButton.setText("Start");
startButton.setEnabled(false);
startButton.setName("startButton"); // NOI18N
startButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
startButtonClicked(evt);
}
});
stopButton.setText("Stop/Reset");
stopButton.setEnabled(false);
stopButton.setName("stopButton"); // NOI18N
stopButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
stopResetClicked(evt);
}
});
loadButton.setText("Load Sequence");
loadButton.setEnabled(false);
loadButton.setName("loadButton"); // NOI18N
loadButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
loadSequence(evt);
}
});
jSlider1.setMajorTickSpacing(10);
jSlider1.setMaximum(210);
jSlider1.setMinimum(20);
jSlider1.setPaintTicks(true);
jSlider1.setValue(120);
jSlider1.setEnabled(false);
jSlider1.setName("jSlider1"); // NOI18N
jSlider1.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
public void mouseDragged(java.awt.event.MouseEvent evt) {
tempoSliderChanged(evt);
}
});
bpmLabel.setText(jSlider1.getValue() + " bpm");
bpmLabel.setEnabled(false);
bpmLabel.setName("bpmLabel"); // NOI18N
javax.swing.GroupLayout sequencerControlsPanelLayout = new javax.swing.GroupLayout(sequencerControlsPanel);
sequencerControlsPanel.setLayout(sequencerControlsPanelLayout);
sequencerControlsPanelLayout.setHorizontalGroup(
sequencerControlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(sequencerControlsPanelLayout.createSequentialGroup()
.addGroup(sequencerControlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(sequencerControlsPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(sequencerControlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(sequencerControlsPanelLayout.createSequentialGroup()
.addGroup(sequencerControlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(sequenceNameLabel)
.addComponent(jLabel1))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 220, Short.MAX_VALUE)
.addComponent(loadButton))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, sequencerControlsPanelLayout.createSequentialGroup()
.addComponent(jSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 66, Short.MAX_VALUE)
.addComponent(startButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(stopButton))))
.addGroup(sequencerControlsPanelLayout.createSequentialGroup()
.addGap(136, 136, 136)
.addComponent(bpmLabel)))
.addContainerGap())
);
sequencerControlsPanelLayout.setVerticalGroup(
sequencerControlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(sequencerControlsPanelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(sequencerControlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(sequencerControlsPanelLayout.createSequentialGroup()
.addGroup(sequencerControlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(sequencerControlsPanelLayout.createSequentialGroup()
.addComponent(jLabel1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(sequenceNameLabel))
.addComponent(loadButton))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 100, Short.MAX_VALUE)
.addGroup(sequencerControlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(sequencerControlsPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(stopButton)
.addComponent(startButton))
.addComponent(jSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap())
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, sequencerControlsPanelLayout.createSequentialGroup()
.addComponent(bpmLabel)
.addGap(54, 54, 54))))
);
bpmLabel.getAccessibleContext().setAccessibleName(jSlider1.getValue() + " bpm");
jSeparator2.setName("jSeparator2"); // NOI18N
connectSeqButton.setText("Connect");
connectSeqButton.setToolTipText("Attempts to open selected COM port.");
connectSeqButton.setName("connectSequencerButton"); // NOI18N
connectSeqButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
connectSequencerPressed(evt);
}
});
javax.swing.GroupLayout jPanel1Layout = new javax.swing.GroupLayout(jPanel1);
jPanel1.setLayout(jPanel1Layout);
jPanel1Layout.setHorizontalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addComponent(sequencerControlsPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 436, Short.MAX_VALUE)
.addContainerGap())
.addComponent(jSeparator2, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 456, Short.MAX_VALUE)
.addGroup(jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addComponent(connectSeqButton)
.addContainerGap(373, Short.MAX_VALUE))
);
jPanel1Layout.setVerticalGroup(
jPanel1Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, jPanel1Layout.createSequentialGroup()
.addContainerGap()
.addComponent(connectSeqButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(jSeparator2, javax.swing.GroupLayout.PREFERRED_SIZE, 10, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(sequencerControlsPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 187, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
jTabbedPane1.addTab("Sequencer", jPanel1);
jPanel2.setName("jPanel2"); // NOI18N
jLabel2.setText("Coming soon... (sorry!)");
jLabel2.setName("jLabel2"); // NOI18N
javax.swing.GroupLayout jPanel2Layout = new javax.swing.GroupLayout(jPanel2);
jPanel2.setLayout(jPanel2Layout);
jPanel2Layout.setHorizontalGroup(
jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel2Layout.createSequentialGroup()
.addContainerGap()
.addComponent(jLabel2)
.addContainerGap(333, Short.MAX_VALUE))
);
jPanel2Layout.setVerticalGroup(
jPanel2Layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(jPanel2Layout.createSequentialGroup()
.addContainerGap()
.addComponent(jLabel2)
.addContainerGap(229, Short.MAX_VALUE))
);
jTabbedPane1.addTab("Bridge", jPanel2);
jLabel3.setText("Arduino Port:");
jLabel3.setName("jLabel3"); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jTabbedPane1)
.addComponent(statusLabel)
.addComponent(jSeparator1, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 461, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(jLabel3)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(comSelectionMenu, javax.swing.GroupLayout.PREFERRED_SIZE, 79, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(comSelectionMenu, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabel3))
.addGap(18, 18, 18)
.addComponent(jTabbedPane1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, 8, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(statusLabel)
.addContainerGap())
);
jTabbedPane1.getAccessibleContext().setAccessibleName("Bridge"); // NOI18N
pack();
}// </editor-fold>//GEN-END:initComponents
private void stopResetClicked(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_stopResetClicked
statusLabel.setText("Stopping...");
app.ms.stopSequencer();
startButton.setEnabled(true);
statusLabel.setText("Stopped");
}//GEN-LAST:event_stopResetClicked
private void startButtonClicked(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_startButtonClicked
startButton.setEnabled(false);
app.ms.startSequencer();
}//GEN-LAST:event_startButtonClicked
private void loadSequence(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_loadSequence
String previouslyLoaded = prefs.get(PREF_LOADED_SEQ, null);
if (previouslyLoaded != null) {
sequenceChooser.setCurrentDirectory(new File(previouslyLoaded));
}
int returnVal = sequenceChooser.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
loadSequenceFile(sequenceChooser.getSelectedFile());
} else {
//Cancelled
}
}//GEN-LAST:event_loadSequence
private void loadSequenceFile(File sequenceFile) {
try {
statusLabel.setText("Loading file...");
app.ms.loadFile(sequenceFile.getPath());
sequenceNameLabel.setText(sequenceFile.getName());
prefs.put(PREF_LOADED_SEQ, sequenceFile.getPath());
statusLabel.setText("Loaded " + sequenceFile.getName());
startButton.setEnabled(true);
} catch (Exception ex) {
Logger.getLogger(MoppyMainWindow.class.getName()).log(Level.SEVERE, null, ex);
statusLabel.setText("File loading error!");
JOptionPane.showMessageDialog(rootPane, ex);
}
}
private void tempoSliderChanged(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_tempoSliderChanged
JSlider s = (JSlider) evt.getSource();
app.ms.setTempo(s.getValue());
bpmLabel.setText(s.getValue() + " bpm");
}//GEN-LAST:event_tempoSliderChanged
private void comPortSelected(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comPortSelected
JComboBox cb = (JComboBox) evt.getSource();
if (comboBoxReady) {
String comPort = (String) cb.getSelectedItem();
shutdownSequencer();
prefs.put(PREF_COM_PORT, comPort);
}
}//GEN-LAST:event_comPortSelected
private void windowClosing(java.awt.event.WindowEvent evt) {//GEN-FIRST:event_windowClosing
if (app.ms != null){
app.ms.closeSequencer();
}
}//GEN-LAST:event_windowClosing
private void connectSequencerPressed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_connectSequencerPressed
if (connectSeqButton.getText().equals("Connect")) {
initializeSequencer((String) comSelectionMenu.getSelectedItem());
} else {
shutdownSequencer();
}
}//GEN-LAST:event_connectSequencerPressed
private void seqTabHidden(java.awt.event.ComponentEvent evt) {//GEN-FIRST:event_seqTabHidden
shutdownSequencer();
}//GEN-LAST:event_seqTabHidden
private void setSequenceControlsEnabled(boolean enabled) {
for (Component c : sequencerControlsPanel.getComponents()) {
c.setEnabled(enabled);
}
}
public void tempoChanged(int newTempo) {
jSlider1.setValue(newTempo);
bpmLabel.setText(newTempo + " bpm");
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel bpmLabel;
private javax.swing.JComboBox comSelectionMenu;
private javax.swing.JButton connectSeqButton;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel2;
private javax.swing.JLabel jLabel3;
private javax.swing.JPanel jPanel1;
private javax.swing.JPanel jPanel2;
private javax.swing.JSeparator jSeparator1;
private javax.swing.JSeparator jSeparator2;
private javax.swing.JSlider jSlider1;
private javax.swing.JTabbedPane jTabbedPane1;
private javax.swing.JButton loadButton;
private javax.swing.JLabel sequenceNameLabel;
private javax.swing.JPanel sequencerControlsPanel;
private javax.swing.JButton startButton;
private javax.swing.JLabel statusLabel;
private javax.swing.JButton stopButton;
// End of variables declaration//GEN-END:variables
}

View File

@ -10,4 +10,5 @@ package moppydesk;
*/
public interface MoppyStatusConsumer {
public void tempoChanged(int newTempo);
public void sequenceEnded();
}

View File

@ -4,6 +4,22 @@
*/
package moppydesk;
import moppydesk.inputs.MoppySequencer;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.UnsupportedCommOperationException;
import java.awt.EventQueue;
import java.io.*;
import java.util.logging.Level;
import java.util.logging.Logger;
import java.util.prefs.BackingStoreException;
import java.util.prefs.Preferences;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.swing.JOptionPane;
import moppydesk.inputs.MoppyMIDIInput;
import moppydesk.outputs.ReceiverMarshaller;
import moppydesk.ui.MoppyControlWindow;
/**
*
@ -11,19 +27,109 @@ package moppydesk;
*/
public class MoppyUI {
MoppySequencer ms = null;
public MoppyUI() {
MoppyMainWindow mainWindow = new MoppyMainWindow(this);
mainWindow.setVisible(true);
//Input objects
public MoppySequencer ms;
public MoppyMIDIInput midiIn;
/**
* The {@link ReceiverMarshaller} will be added as a receiver to whatver
* input object is selected.
*/
public ReceiverMarshaller rm = new ReceiverMarshaller();
public Preferences prefs = Preferences.userNodeForPackage(MoppyUI.class);
protected void startup() {
//Initialize parts
try {
ms = new MoppySequencer();
midiIn = new MoppyMIDIInput();
} catch (Exception ex) {
JOptionPane.showMessageDialog(null, ex.toString());
}
MoppyControlWindow mainWindow = new MoppyControlWindow(this);
mainWindow.setStatus("Initializing...");
mainWindow.setVisible(true);
mainWindow.setStatus("Initialized.");
}
/**
/**
* Main method launching the application.
*/
public static void main(String[] args) {
new MoppyUI();
/* Set the Nimbus look and feel */
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Windows".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(MoppyUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(MoppyUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(MoppyUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(MoppyUI.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
MoppyUI ui = new MoppyUI();
ui.startup();
}
});
}
public void savePreferences() {
try {
prefs.flush();
} catch (BackingStoreException ex) {
Logger.getLogger(MoppyUI.class.getName()).log(Level.SEVERE, null, ex);
}
}
public void putPreferenceObject(String key, Object object) {
try {
prefs.putByteArray(key, serializePref(object));
} catch (IOException ex) {
Logger.getLogger(MoppyUI.class.getName()).log(Level.SEVERE, null, ex);
}
}
public Object getPreferenceObject(String key) {
try {
return deserializePref(prefs.getByteArray(key, null));
} catch (NullPointerException ex) {
Logger.getLogger(MoppyUI.class.getName()).log(Level.WARNING, "No preference set for " + key, ex);
} catch (IOException ex) {
Logger.getLogger(MoppyUI.class.getName()).log(Level.SEVERE, null, ex);
} catch (ClassNotFoundException ex) {
Logger.getLogger(MoppyUI.class.getName()).log(Level.SEVERE, null, ex);
}
return null;
}
//Preference to object
private static Object deserializePref(byte[] b) throws NullPointerException, IOException, ClassNotFoundException {
return new ObjectInputStream(new ByteArrayInputStream(b)).readObject();
}
//Object to preference
private static byte[] serializePref(Object p) throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream();
ObjectOutputStream oos = new ObjectOutputStream(baos);
oos.writeObject(p);
oos.close();
return baos.toByteArray();
}
}

View File

@ -0,0 +1,25 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package moppydesk;
import java.io.Serializable;
/**
*
* @author Sam
*/
public class OutputSetting implements Serializable{
public enum OutputType {MOPPY, MIDI};
public final int MIDIChannel;
public boolean enabled = false;
public OutputType type = OutputType.MOPPY;
public String comPort;
public String midiDeviceName;
public OutputSetting(int MIDIChannel){
this.MIDIChannel = MIDIChannel;
}
}

View File

@ -0,0 +1,74 @@
package moppydesk.inputs;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.midi.*;
import javax.sound.midi.MidiDevice.Info;
public class MoppyMIDIInput implements Transmitter, Receiver{
MidiDevice currentDevice = null;
Receiver downstreamReceiver = null;
public void setDevice(Info deviceInfo) throws MidiUnavailableException{
if (currentDevice != null){
currentDevice.close();
}
currentDevice = MidiSystem.getMidiDevice(deviceInfo);
currentDevice.open();
currentDevice.getTransmitter().setReceiver(this);
}
public void setReceiver(Receiver receiver) {
downstreamReceiver = receiver;
}
public Receiver getReceiver() {
return downstreamReceiver;
}
public void close() {
if (currentDevice != null){
currentDevice.close();
currentDevice = null;
}
if (downstreamReceiver != null){
downstreamReceiver.close();
downstreamReceiver = null;
}
}
//
/// Receiver logic for forwarding messages
//
public void send(MidiMessage message, long timeStamp) {
//TODO Edit message based on settings.
if (downstreamReceiver != null){
downstreamReceiver.send(message, timeStamp);
}
}
//
//// UTILITY METHODS
//
public static HashMap<String,MidiDevice.Info> getMIDIInInfos(){
MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo();
HashMap<String,MidiDevice.Info> outInfos = new HashMap<String,MidiDevice.Info>();
for (MidiDevice.Info i : infos){
try {
MidiDevice dev = MidiSystem.getMidiDevice(i);
if (dev.getMaxTransmitters() != 0){
outInfos.put(i.getName(), i);
}
} catch (MidiUnavailableException ex) {
Logger.getLogger(MoppyMIDIInput.class.getName()).log(Level.SEVERE, null, ex);
}
}
return outInfos;
}
}

View File

@ -1,70 +1,86 @@
package moppydesk;
package moppydesk.inputs;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.UnsupportedCommOperationException;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.ArrayList;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.midi.InvalidMidiDataException;
import javax.sound.midi.MetaEventListener;
import javax.sound.midi.MetaMessage;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.MidiSystem;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Receiver;
import javax.sound.midi.Sequence;
import javax.sound.midi.Sequencer;
import javax.sound.midi.Transmitter;
import moppydesk.MoppyStatusConsumer;
/**
*
* @author Sammy1Am
*/
public class MoppySequencer implements MetaEventListener{
public class MoppySequencer implements MetaEventListener, Transmitter{
MoppyBridge mb;
MoppyPlayer mp;
Sequencer sequencer;
Sequence currentSequence = null;
Transmitter sequenceTransmitter; // Because we'll be piping all the messages to a common receiver, we'll only need one of these
ArrayList<MoppyStatusConsumer> listeners = new ArrayList<MoppyStatusConsumer>(1);
public MoppySequencer(String comPort) throws NoSuchPortException, PortInUseException, UnsupportedCommOperationException, IOException, MidiUnavailableException {
mb = new MoppyBridge(comPort); //Create MoppyBridge on the COM port with the Arduino
mp = new MoppyPlayer(mb);
mb.resetDrives();
public MoppySequencer() throws MidiUnavailableException {
sequencer = MidiSystem.getSequencer(false);
sequencer.open();
sequencer.getTransmitter().setReceiver(mp); // Set MoppyPlayer as a receiver.
sequencer.addMetaEventListener(this);
sequenceTransmitter = sequencer.getTransmitter(); // This method creates a new transmitter each time it's called!
}
public void loadFile(String filePath) throws InvalidMidiDataException, IOException, MidiUnavailableException {
sequencer.stop();
Sequence sequence = MidiSystem.getSequence(new File(filePath));
sequencer.setSequence(sequence);
System.out.println("Loaded sequence with "+(sequence.getTracks().length-1)+" MIDI channels.");
System.out.println("Loaded sequence with "+(sequence.getTracks().length-1)+" MIDI tracks.");
currentSequence = sequence;
}
public void startSequencer(){
sequencer.start();
}
public boolean isRunning(){
return sequencer.isRunning();
}
public void stopSequencer(){
if (sequencer.isOpen()){
sequencer.stop();
}
mb.resetDrives();
}
public void resetSequencer(){
if (sequencer.isOpen()){
sequencer.stop();
sequencer.setTickPosition(0);
}
}
public void setTempo(float newTempo){
sequencer.setTempoInBPM(newTempo);
}
public long getSecondsLength(){
return sequencer.getMicrosecondLength()/1000000;
}
public long getSecondsPosition(){
return sequencer.getMicrosecondPosition()/1000000;
}
public void setSecondsPosition(long seconds){
sequencer.setMicrosecondPosition(seconds*1000000);
}
public void addListener(MoppyStatusConsumer newListener){
listeners.add(newListener);
}
@ -76,7 +92,6 @@ public class MoppySequencer implements MetaEventListener{
public void closeSequencer(){
stopSequencer();
sequencer.close();
mp.close();
}
public void meta(MetaMessage meta) {
@ -97,5 +112,24 @@ public class MoppySequencer implements MetaEventListener{
System.out.println("Tempo changed to: " + newTempo);
}
else if (meta.getType() == 47) {
//MrSolidSnake745: Exposing end of sequence event
for (MoppyStatusConsumer c : listeners) {
c.sequenceEnded();
}
System.out.println("End of current sequence");
}
}
public void setReceiver(Receiver receiver) {
sequenceTransmitter.setReceiver(receiver);
}
public Receiver getReceiver() {
return sequenceTransmitter.getReceiver();
}
public void close() {
closeSequencer();
}
}

View File

@ -0,0 +1,171 @@
package moppydesk.midputs;
import java.util.Arrays;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.midi.*;
import moppydesk.Constants;
/**
* This class will re-map notes from specified incoming channels to a pool of
* output channels based on one of several strategies.
*/
public class DrivePooler implements Receiver, Transmitter {
public enum PoolingStrategy {
STRAIGHT_THROUGH, ROUND_ROBIN, STACK
};
Receiver receiver = null;
private int startInput = 0;
private int endInput = 0;
private int startOutput = 0;
private int endOutput = 7;
private PoolingStrategy currentStrategy = PoolingStrategy.STRAIGHT_THROUGH;
private int[] currentNotes = new int[Constants.NUM_MIDI_CHANNELS];
private int rrNextNote = 0;
public void setInputRange(int start, int end) {
startInput = start - 1;
endInput = end - 1;
}
public void setOutputRange(int start, int end) {
startOutput = start - 1;
endOutput = end - 1;
}
public void setStrategy(PoolingStrategy newStrat) {
allOff();
currentStrategy = newStrat;
}
public void send(MidiMessage message, long timeStamp) {
if (receiver != null) {
if (message instanceof ShortMessage
&& (message.getMessage()[0] & 0xFF) > 127 && (message.getMessage()[1] & 0xFF) < 160) {
switch (currentStrategy) {
case STRAIGHT_THROUGH:
receiver.send(message, timeStamp); //No modifications
break;
case ROUND_ROBIN:
receiver.send(roundRobinMap(message), timeStamp);
break;
case STACK:
receiver.send(stackMap(message), timeStamp);
}
}
}
}
private MidiMessage roundRobinMap(MidiMessage message) {
if (rrNextNote < startOutput || rrNextNote > endOutput) {
rrNextNote = startOutput;
}
ShortMessage mappedMessage = (ShortMessage) message;
try {
if (mappedMessage.getChannel() >= startInput && mappedMessage.getChannel() <= endInput) {
if (mappedMessage.getCommand() == ShortMessage.NOTE_OFF || (mappedMessage.getCommand() == ShortMessage.NOTE_ON && mappedMessage.getData2() == 0)) {
int noteNumber = mappedMessage.getData1();
for (int n = startOutput; n <= endOutput; n++) {
if (currentNotes[n] == noteNumber) {
currentNotes[n] = -1;
mappedMessage.setMessage(mappedMessage.getCommand(), n, mappedMessage.getData1(), mappedMessage.getData2());
break; // Only turn off one of the notes
}
}
} else if (mappedMessage.getCommand() == ShortMessage.NOTE_ON) {
int targetNote = rrNextNote;
while (targetNote <= endOutput) {
if (currentNotes[targetNote] < 0) {
break; // If that one's free, we're good.
} else if (targetNote == endOutput) {
targetNote = startOutput;
} else {
targetNote++;
}
if (targetNote == rrNextNote) {
break; //We've gone around once, stop!
}
}
currentNotes[targetNote] = mappedMessage.getData1();
mappedMessage.setMessage(mappedMessage.getCommand(), targetNote, mappedMessage.getData1(), mappedMessage.getData2());
rrNextNote++;
}
} else {
return message; //It's not in the mapping range, let it pass through...
}
} catch (InvalidMidiDataException ex) {
Logger.getLogger(DrivePooler.class.getName()).log(Level.SEVERE, null, ex);
}
return mappedMessage;
}
private MidiMessage stackMap(MidiMessage message) {
ShortMessage mappedMessage = (ShortMessage) message;
try {
//If it's in the chosen range:
if (mappedMessage.getChannel() >= startInput && mappedMessage.getChannel() <= endInput) {
if (mappedMessage.getCommand() == ShortMessage.NOTE_OFF || (mappedMessage.getCommand() == ShortMessage.NOTE_ON && mappedMessage.getData2() == 0)) {
int noteNumber = mappedMessage.getData1();
for (int n = startOutput; n <= endOutput; n++) {
if (currentNotes[n] == noteNumber) {
currentNotes[n] = -1;
mappedMessage.setMessage(mappedMessage.getCommand(), n, mappedMessage.getData1(), mappedMessage.getData2());
}
}
} else if (mappedMessage.getCommand() == ShortMessage.NOTE_ON) {
int targetChannel = 0;
for (int n = startOutput; n <= endOutput; n++) {
if (currentNotes[n] < 0) {
targetChannel = n;
break;
}
}
currentNotes[targetChannel] = mappedMessage.getData1();
mappedMessage.setMessage(mappedMessage.getCommand(), targetChannel, mappedMessage.getData1(), mappedMessage.getData2());
}
} else {
return message; //It's not in the mapping range, let it pass through...
}
} catch (InvalidMidiDataException ex) {
Logger.getLogger(DrivePooler.class.getName()).log(Level.SEVERE, null, ex);
}
return mappedMessage;
}
private void allOff() {
Arrays.fill(currentNotes, -1);
}
//
//// Transmitter-methods
//
public void close() {
if (receiver != null) {
receiver.close();
receiver = null;
}
}
public void setReceiver(Receiver newReceiver) {
if (this.receiver != null) {
this.receiver.close();
}
this.receiver = newReceiver;
}
public Receiver getReceiver() {
return receiver;
}
}

View File

@ -0,0 +1,92 @@
package moppydesk.midputs;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.midi.*;
/**
* This class will perform various operations on notes as they pass through. Mostly this
* is for keeping notes within the specified range.
*/
public class NoteFilter implements Receiver, Transmitter {
Receiver receiver = null;
private boolean autoContstrain = false;
private boolean ignoreTen = false;
private static final int LOWEST_NOTE = 24; //C1
private static final int HIGHEST_NOTE = 71; //B4
public void setAutoConstrain(boolean newValue) {
autoContstrain = newValue;
}
public void setIgnoreTen(boolean newValue) {
ignoreTen = newValue;
}
public void send(MidiMessage message, long timeStamp) {
if (receiver != null) {
if (message instanceof ShortMessage
&& (message.getMessage()[0] & 0xFF) >= 128 && (message.getMessage()[1] & 0xFF) <= 159) {
ShortMessage filteredMessage = (ShortMessage) message;
if (autoContstrain){
constrainNote(filteredMessage);
}
if (ignoreTen && filteredMessage.getChannel() == 9){
return; // If we're ignoring 10 and that's the channel, just return immediately
}
receiver.send(filteredMessage, timeStamp);
} else {
receiver.send(message, timeStamp); // If it's not a note event, pass it on through.
}
}
}
/**
* Adjusts the note data within this message to fit between {@link #LOWEST_NOTE} and {@link #HIGHEST_NOTE}, inclusive.
* This will affect both on and off messages so that any altered notes turned on will also be turned off.
* @param message
*/
private void constrainNote(ShortMessage message){
int newNote = message.getData1();
while (newNote<LOWEST_NOTE){
newNote += 12; // Up an octave
}
while (newNote>HIGHEST_NOTE){
newNote -= 12; // Down an octave
}
try {
message.setMessage(message.getStatus(), newNote, message.getData2());
} catch (InvalidMidiDataException ex) {
Logger.getLogger(NoteFilter.class.getName()).log(Level.SEVERE, null, ex);
}
}
//
//// Transmitter-methods
//
public void close() {
if (receiver != null) {
receiver.close();
receiver = null;
}
}
public void setReceiver(Receiver newReceiver) {
if (this.receiver != null) {
this.receiver.close();
}
this.receiver = newReceiver;
}
public Receiver getReceiver() {
return receiver;
}
}

View File

@ -0,0 +1,110 @@
package moppydesk.outputs;
import gnu.io.NRSerialPort;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.UnsupportedCommOperationException;
import java.io.IOException;
import java.io.OutputStream;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
*
* @author Sammy1Am
*/
public class MoppyCOMBridge {
static int FIRST_PIN = 2;
static int MAX_PIN = 17;
int SERIAL_RATE = 9600;
OutputStream os;
NRSerialPort com;
private boolean isOutputOpen = false;
public MoppyCOMBridge(String portName) throws NoSuchPortException, PortInUseException, UnsupportedCommOperationException, IOException {
com = new NRSerialPort(portName, SERIAL_RATE);
com.connect();
os = com.getOutputStream();
isOutputOpen = true;
}
/**
* Convenience method that splits the periodData int into two bytes for
* sending over serial.
*
* @param pin Controller pin to handle ntoe
* @param periodData length of period in microSeconds
*/
public void sendEvent(byte pin, int periodData) {
sendEvent(pin, (byte) ((periodData >> 8) & 0xFF), (byte) (periodData & 0xFF));
}
/**
* Sends an event to the Arduino.
*
* @param pin Controller pin
* @param b1
* @param b2
*/
public void sendEvent(byte pin, byte b1, byte b2) {
sendArray(new byte[]{pin, b1, b2});
}
private void sendArray(byte[] message) {
try {
os.write(message);
os.flush();
} catch (IOException ex) {
Logger.getLogger(MoppyCOMBridge.class.getName()).log(Level.SEVERE, null, ex);
}
}
/**
* Sends a '0' period to all drives to silence them.
*/
public void silenceDrives() {
// Stop notes
for (int d = FIRST_PIN; d <= MAX_PIN; d += 2) {
sendArray(new byte[]{(byte) d, (byte) 0, (byte) 0});
}
}
/**
* Sends a special code (first byte=100) to reset the drives
*/
public void resetDrives() {
if (isOutputOpen) {
//Send reset code
sendArray(new byte[]{(byte) 100, (byte) 0, (byte) 0});
try {
Thread.sleep(500); // Give the drives time to reset
} catch (InterruptedException ex) {
Logger.getLogger(MoppyCOMBridge.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
public void close() {
if (os != null) {
if (isOutputOpen) { //Only attempt to silence if the output is still open.
silenceDrives();
}
try {
os.close();
} catch (IOException ex) {
Logger.getLogger(MoppyCOMBridge.class.getName()).log(Level.SEVERE, null, ex);
}
}
if (com != null && com.isConnected()) {
com.disconnect();
}
isOutputOpen = false;
}
public static String[] getAvailableCOMPorts() {
return NRSerialPort.getAvailableSerialPorts().toArray(new String[0]);
}
}

View File

@ -0,0 +1,97 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package moppydesk.outputs;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.midi.*;
import javax.sound.midi.MidiDevice.Info;
/**
*
* @author Sam
*/
public class MoppyMIDIOutput implements MoppyReceiver{
MidiDevice device;
Receiver deviceReceiver;
public MoppyMIDIOutput(String midiDeviceName) throws MidiUnavailableException{
MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo();
for (Info i : infos){
try {
if (i.getName().equalsIgnoreCase(midiDeviceName)){
this.device = MidiSystem.getMidiDevice(i);
}
} catch (MidiUnavailableException ex) {
Logger.getLogger(MoppyMIDIOutput.class.getName()).log(Level.SEVERE, null, ex);
}
}
device.open();
deviceReceiver = device.getReceiver();
}
public static HashMap<String,Info> getMIDIOutInfos(){
MidiDevice.Info[] infos = MidiSystem.getMidiDeviceInfo();
HashMap<String,Info> outInfos = new HashMap<String,Info>();
for (Info i : infos){
try {
MidiDevice dev = MidiSystem.getMidiDevice(i);
if (dev.getMaxReceivers() != 0){
outInfos.put(i.getName(), i);
}
} catch (MidiUnavailableException ex) {
Logger.getLogger(MoppyMIDIOutput.class.getName()).log(Level.SEVERE, null, ex);
}
}
return outInfos;
}
public void send(MidiMessage message, long timeStamp) {
deviceReceiver.send(message, timeStamp);
}
public void close() {
deviceReceiver.close();
device.close();
}
public void reset() {
//Nothing really to do here, I don't think.
if (deviceReceiver != null){
try {
ShortMessage resetMessage = new ShortMessage();
resetMessage.setMessage(ShortMessage.SYSTEM_RESET);
deviceReceiver.send(resetMessage,(long)-1);
} catch (InvalidMidiDataException ex) {
Logger.getLogger(MoppyMIDIOutput.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
/**
* Since the goal here is to silence any playing devices, we'll pass the "All Note Off" command to
* each channel. Hopefully the receiving devices understand this message.
*/
public void silence() {
if (deviceReceiver != null) {
try {
ShortMessage silenceMessage = new ShortMessage();
for (int channelCode=176;channelCode<=191;channelCode++){
silenceMessage.setMessage(channelCode, 123, 0);
deviceReceiver.send(silenceMessage, (long) -1);
}
} catch (InvalidMidiDataException ex) {
Logger.getLogger(MoppyMIDIOutput.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}

View File

@ -1,14 +1,13 @@
package moppydesk;
package moppydesk.outputs;
import gnu.io.SerialPort;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.Receiver;
/**
*
* @author Sammy1Am
*/
public class MoppyPlayer implements Receiver {
public class MoppyPlayerOutput implements MoppyReceiver {
/**
* The periods for each MIDI note in an array. The floppy drives
@ -16,6 +15,8 @@ public class MoppyPlayer implements Receiver {
* Periods are in microseconds because that's what the Arduino uses for its
* clock-cycles in the micro() function, and because milliseconds aren't
* precise enough for musical notes.
*
* Notes are named (e.g. C1-B4) based on scientific pitch notation (A4=440Hz)
*/
public static int[] microPeriods = {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
@ -29,6 +30,11 @@ public class MoppyPlayer implements Receiver {
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0
};
/**
* Maximum number of cents to bend +/-.
*/
private static int BEND_CENTS = 200;
/**
* Resolution of the Arduino code in microSeconds.
*/
@ -40,15 +46,16 @@ public class MoppyPlayer implements Receiver {
*/
private int[] currentPeriod = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
MoppyBridge mb;
MoppyCOMBridge mb;
SerialPort com;
public MoppyPlayer(MoppyBridge newMb) {
public MoppyPlayerOutput(MoppyCOMBridge newMb) {
mb = newMb;
}
public void close() {
mb.close();
mb.resetDrives();
mb.close();
}
//Is called by Java MIDI libraries for each MIDI message encountered.
@ -94,13 +101,22 @@ public class MoppyPlayer implements Receiver {
//Arduino by multipying by 2.
byte pin = (byte) (2 * (message.getStatus() - 223));
double pitchBend = ((message.getMessage()[2] & 0xff) << 8) + (message.getMessage()[1] & 0xff);
int period = (int) (currentPeriod[message.getStatus() - 224] / Math.pow(2.0, (pitchBend - 8192) / 8192));
//System.out.println(currentPeriod[message.getStatus() - 224] + "-" + period);
double pitchBend = ((message.getMessage()[2] & 0xff) << 7) + (message.getMessage()[1] & 0xff);
//System.out.println("Pitch bend " + pitchBend);
// Calculate the new period based on the desired maximum bend and the current pitchBend value
int period = (int) (currentPeriod[message.getStatus() - 224] / Math.pow(2.0, (BEND_CENTS/1200.0)*((pitchBend - 8192.0) / 8192.0)));
//System.out.println("Bent by " + Math.pow(2.0, (bendCents/1200.0)*((pitchBend - 8192.0) / 8192.0)));
mb.sendEvent(pin, period);
}
}
}
public void reset() {
mb.resetDrives();
}
public void silence() {
mb.silenceDrives();
}
}

View File

@ -0,0 +1,17 @@
package moppydesk.outputs;
import javax.sound.midi.Receiver;
/**
* Adds a reset function to the MIDI Receiver class.
* @author Sam
*/
public interface MoppyReceiver extends Receiver{
/**
* Returns the drives/xylophone/calliope/organ/drums to a reset-state.
* This should not disconnect or dispose of any connection though.
*/
public void reset();
public void silence();
}

View File

@ -0,0 +1,107 @@
package moppydesk.outputs;
import java.util.ArrayList;
import java.util.Arrays;
import javax.sound.midi.MidiDevice;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.Receiver;
import javax.sound.midi.ShortMessage;
/**
* Marshals data from the chosen input device to the (up to) 16 output receivers.
* Receivers are defined in a size-16 array, one slot for each MIDI channel. Each received
* message is routed to the appropriate receiver based on its channel. If a receiver
* is not defined for the given channel's index in the array, the message is dropped.
*/
public class ReceiverMarshaller implements MoppyReceiver{
/**
* Array of {@link MoppyReceiver} references, one for each channel. The same receiver
* object can be assigned to multiple indexes if it is going to be handling multiple
* channels of data.
*/
private final MoppyReceiver[] outputReceivers = new MoppyReceiver[16];
/**
* Creates a new ReceiverMarshaller with an empty array of {@link Receiver}s.
* @param receivers
*/
public ReceiverMarshaller(){
//Nothing for now.
}
/**
* Sets a channel's Receiver object. If a receiver is already assigned to this
* channel, {@link Receiver#close()} will be called before it is removed and replaced
* with the new {@link Receiver}.
* @param MIDIChannel
* @param channelReceiver
*/
public void setReceiver(int MIDIChannel, MoppyReceiver channelReceiver){
if (MIDIChannel < 1 || MIDIChannel > 16){
throw new IllegalArgumentException("Only channels 1-16 are supported by the ReceiverMarshaller!");
}
if (outputReceivers[MIDIChannel-1] != null){
outputReceivers[MIDIChannel-1].close();
}
outputReceivers[MIDIChannel-1] = channelReceiver;
}
/**
* Closes all receivers, and removes them from the array (fills array with nulls).
*/
public void clearReceivers()
{
close();
}
public void send(MidiMessage message, long timeStamp) {
int ch = ((ShortMessage)message).getChannel();
if (outputReceivers[ch]!= null){
outputReceivers[ch].send(message, timeStamp);
}
}
/**
* Explicity closes all output receivers, and nulls the array.
* This differs slightly from the use described in {@link MidiDevice} in that
* the ReceiverMarshaller itself is not necessarily closed when this is called.
* After being closed, new receivers can continue to be added to the ReceiverMarshaller.
*/
public void close() {
for (Receiver r: outputReceivers){
if (r!= null){
r.close();
}
}
Arrays.fill(outputReceivers, null);
}
/**
* Finds the unique set of receivers and calls the {@link MoppyReceiver#reset() } method.
* We go through the trouble of finding unique receivers incase the reset is time-consuming.
*/
public void reset() {
ArrayList<MoppyReceiver> uniqueReceivers = new ArrayList<MoppyReceiver>();
for (MoppyReceiver r: outputReceivers){
if (r!= null && !uniqueReceivers.contains(r)){
uniqueReceivers.add(r);
}
}
for (MoppyReceiver r : uniqueReceivers){
r.reset();
}
}
public void silence() {
ArrayList<MoppyReceiver> uniqueReceivers = new ArrayList<MoppyReceiver>();
for (MoppyReceiver r: outputReceivers){
if (r!= null && !uniqueReceivers.contains(r)){
uniqueReceivers.add(r);
}
}
for (MoppyReceiver r : uniqueReceivers){
r.silence();
}
}
}

View File

@ -0,0 +1,173 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package moppydesk.playlist;
import java.io.BufferedReader;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.table.AbstractTableModel;
/**
*
* @author AJ (MrSolidSnake745)
* Class that represents a playlist
*/
public class MoppyPlaylist extends AbstractTableModel {
private List<MoppySong> playlist = new ArrayList<>();
private int currentIndex = -1;
private static final int COL_COUNT = 3;
private static final int INDICATOR_INDEX = 0;
private static final int TITLE_INDEX = 2;
private static final int PLAYED_INDEX = 1;
public MoppyPlaylist() {
}
public void addSong(File file) {playlist.add(new MoppySong(file)); fireTableDataChanged();}
public void currentSongFinished() {if(currentIndex != -1) {playlist.get(currentIndex).setPlayed(true);} fireTableDataChanged();}
public void currentSongReset() {if(currentIndex != -1) {playlist.get(currentIndex).setPlayed(false);} fireTableDataChanged();}
public Boolean isFinished() {
if(isEmpty()) return true;
for (MoppySong s: playlist) {if(!(s.getPlayed())) return false;}
return true;
}
public Boolean isEmpty() {return playlist.isEmpty();}
public Boolean isFirstSong() {return currentIndex == 0;}
public Boolean isLastSong() {return currentIndex == playlist.size() - 1;}
public int getIndex() {return currentIndex;}
public String getCurrentSongName() {return playlist.get(currentIndex).getName();}
public Boolean getCurrentSongPlayed() {return playlist.get(currentIndex).getPlayed();}
public void randomize() {Collections.shuffle(playlist); reset();}
public File getNextSong() {
if(!isLastSong()) {
currentSongFinished();
return playlist.get(++currentIndex).getFile();
}
return null;
}
public File getPreviousSong() {
if(!isFirstSong()) {
--currentIndex; currentSongReset();
return playlist.get(currentIndex).getFile();
}
return null;
}
public void reset() {
currentIndex = -1;
for (MoppySong i: playlist) {i.setPlayed(false);}
fireTableDataChanged();
}
public void clear() {
currentIndex = -1;
playlist.clear();
fireTableDataChanged();
}
public boolean savePlaylistFile(File input) {
try {
FileWriter fr = new FileWriter(input);
BufferedWriter br = new BufferedWriter(fr);
for (MoppySong i: playlist) {
br.write(i.getName() + ";" + i.getFile().getPath());
br.newLine();
}
br.close();
return true;
} catch (IOException ex) {
Logger.getLogger(MoppyPlaylist.class.getName()).log(Level.SEVERE, null, ex);
return false;
}
}
public boolean loadPlaylistFile(File input) {
try {
FileReader fr = new FileReader(input);
BufferedReader br = new BufferedReader(fr);
List<MoppySong> newpl = new ArrayList<>();
String s = br.readLine();
while (s != null) {
String[] line = s.split(";");
if(line.length == 2) { newpl.add(new MoppySong(line[0], line[1])); }
s = br.readLine();
}
br.close();
playlist = newpl;
fireTableDataChanged();
return true;
} catch (IOException ex) {
Logger.getLogger(MoppyPlaylist.class.getName()).log(Level.SEVERE, null, ex);
return false;
}
}
// <editor-fold defaultstate="collapsed" desc="TableModel Implementation">
@Override
public int getRowCount() { return playlist.size(); }
@Override
public int getColumnCount() { return COL_COUNT; }
@Override
public boolean isCellEditable(int row, int column) { return false; } //Setting all cells to read only
@Override
public Object getValueAt(int rowIndex, int columnIndex) {
MoppySong song = playlist.get(rowIndex);
switch (columnIndex) {
case INDICATOR_INDEX:
if(rowIndex == currentIndex) return "";
return "";
case TITLE_INDEX:
return song.getName();
case PLAYED_INDEX:
return song.getPlayed();
default:
return new Object();
}
}
@Override
public Class getColumnClass(int column) {
switch (column) {
case INDICATOR_INDEX:
return String.class;
case TITLE_INDEX:
return String.class;
case PLAYED_INDEX:
return Boolean.class;
default:
return Object.class;
}
}
@Override
public String getColumnName(int column) {
switch (column) {
case INDICATOR_INDEX:
return "";
case TITLE_INDEX:
return "Name";
case PLAYED_INDEX:
return "Played";
default:
return "";
}
}
// </editor-fold>
}

View File

@ -0,0 +1,34 @@
package moppydesk.playlist;
import java.io.File;
/**
*
* @author AJ (MrSolidSnake745)
* Stupid simple class to represent a song...
*/
public class MoppySong {
private String title;
private Boolean played = false;
private String filePath = null;
public MoppySong(String titleIn, String pathIn) { title = titleIn; filePath = pathIn; }
public MoppySong(File fileIn) {
title = fileIn.getName().substring(0, fileIn.getName().lastIndexOf("."));
filePath = fileIn.getAbsolutePath();
}
public String getName() {return title;}
public Boolean getPlayed() {return played;}
public String getFilePath() {return filePath;}
public void setName(String titleIn) {title = titleIn;}
public void setPlayed(Boolean playedIn) {played = playedIn;}
public void setFilePath(String filePathIn) {filePath = filePathIn;}
public File getFile() {
if(filePath != null) return new File(filePath);
return null;
}
}

View File

@ -0,0 +1,9 @@
Application.name = MoppyUI
Application.id = MoppyUI
Application.title = Moppy UI
Application.version = 1.1.0
Application.vendor = Sammy1Am
Application.vendorId = sam
Application.homepage = https://github.com/SammyIAm/Moppy
Application.description = Control software for Musical fOPPY drives.
Application.lookAndFeel = system

View File

@ -0,0 +1,122 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="outputTypeRB">
</Component>
</NonVisualComponents>
<Properties>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[525, 23]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="enabledCB" min="-2" pref="46" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="moppyTypeRB" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="comComboBox" pref="161" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="MIDITypeRB" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="midiOutComboBox" min="-2" pref="130" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="moppyTypeRB" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="MIDITypeRB" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="comComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="midiOutComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="enabledCB" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JRadioButton" name="moppyTypeRB">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="outputTypeRB"/>
</Property>
<Property name="text" type="java.lang.String" value="Moppy Serial:"/>
<Property name="toolTipText" type="java.lang.String" value="Sends Moppy-protocol serial data to selected COM port"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="outputTypeChanged"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="MIDITypeRB">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="outputTypeRB"/>
</Property>
<Property name="text" type="java.lang.String" value="MIDI Out:"/>
<Property name="toolTipText" type="java.lang.String" value="Sends MIDI messages through to selected MIDI port"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="outputTypeChanged"/>
</Events>
</Component>
<Component class="javax.swing.JComboBox" name="comComboBox">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new DefaultComboBoxModel&lt;String&gt;(moppydesk.outputs.MoppyCOMBridge.getAvailableCOMPorts())" type="code"/>
</Property>
</Properties>
<Events>
<EventHandler event="popupMenuWillBecomeVisible" listener="javax.swing.event.PopupMenuListener" parameters="javax.swing.event.PopupMenuEvent" handler="comComboBoxPopupMenuWillBecomeVisible"/>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="comComboBoxActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
<Component class="javax.swing.JComboBox" name="midiOutComboBox">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new DefaultComboBoxModel&lt;String&gt;(controlWindow.availableMIDIOuts.keySet().toArray(new String[0]))" type="code"/>
</Property>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="midiOutComboBoxActionPerformed"/>
</Events>
<AuxValues>
<AuxValue name="JavaCodeGenerator_TypeParameters" type="java.lang.String" value="&lt;String&gt;"/>
</AuxValues>
</Component>
<Component class="javax.swing.JCheckBox" name="enabledCB">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="String.valueOf(settings.MIDIChannel)" type="code"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="enabledCBActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,243 @@
package moppydesk.ui;
import java.awt.event.ActionEvent;
import javax.swing.DefaultComboBoxModel;
import javax.swing.JCheckBox;
import javax.swing.JComboBox;
import moppydesk.OutputSetting;
import moppydesk.OutputSetting.OutputType;
/**
*
* @author Sammy1Am
*/
public class ChannelOutControl extends javax.swing.JPanel {
private final OutputSetting settings;
private final MoppyControlWindow controlWindow;
/**
* Creates new form ChannelOutControl
*/
public ChannelOutControl(MoppyControlWindow mcw, OutputSetting os) {
this.settings = os;
this.controlWindow = mcw;
initComponents();
loadSettings();
}
private void loadSettings() {
if (settings.type.equals(OutputType.MOPPY)) {
moppyTypeRB.setSelected(true);
outputTypeChanged(OutputType.MOPPY);
} else {
MIDITypeRB.setSelected(true);
outputTypeChanged(OutputType.MIDI);
}
if (settings.enabled) {
enabledCB.setSelected(true);
enableControls();
} else {
enabledCB.setSelected(false);
disableControls();
}
//MrSolidSnake745: Ensure the saved settings are still valid and load their values
//Else default to the first item in the collection
if(doesCOMExist(settings.comPort)) {comComboBox.setSelectedItem(settings.comPort);}
else {settings.comPort = (String) comComboBox.getModel().getElementAt(0);}
if(doesMIDIExist(settings.midiDeviceName)) {midiOutComboBox.setSelectedItem(settings.midiDeviceName);}
else {settings.midiDeviceName = (String) midiOutComboBox.getModel().getElementAt(0);}
}
public void lockControl(){
enabledCB.setEnabled(false);
disableControls();
}
public void unlockControl(){
enabledCB.setEnabled(true);
if (enabledCB.isSelected()) enableControls();
}
private void disableControls() {
moppyTypeRB.setEnabled(false);
MIDITypeRB.setEnabled(false);
comComboBox.setEnabled(false);
midiOutComboBox.setEnabled(false);
}
private void enableControls() {
moppyTypeRB.setEnabled(true);
MIDITypeRB.setEnabled(true);
if (moppyTypeRB.isSelected()){
outputTypeChanged(new ActionEvent(moppyTypeRB, ActionEvent.ACTION_PERFORMED, moppyTypeRB.getActionCommand()));
} else if (MIDITypeRB.isSelected()){
outputTypeChanged(new ActionEvent(MIDITypeRB, ActionEvent.ACTION_PERFORMED, MIDITypeRB.getActionCommand()));
}
}
private boolean doesCOMExist(Object obj) {
return (((DefaultComboBoxModel)(comComboBox.getModel())).getIndexOf(obj) > -1);
}
private boolean doesMIDIExist(Object obj) {
return (((DefaultComboBoxModel)(midiOutComboBox.getModel())).getIndexOf(obj) > -1);
}
//MrSolidSnake745: Method to refresh the list of available COM ports
private void refreshAvailablePorts() {
comComboBox.setModel(new DefaultComboBoxModel<String>(moppydesk.outputs.MoppyCOMBridge.getAvailableCOMPorts()));
if(doesCOMExist(settings.comPort)) {comComboBox.setSelectedItem(settings.comPort);} //To avoid losing last user selected value
else {settings.comPort = (String) comComboBox.getModel().getElementAt(0);} //ActionPerformed event isn't triggered so we need to manually set
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
outputTypeRB = new javax.swing.ButtonGroup();
moppyTypeRB = new javax.swing.JRadioButton();
MIDITypeRB = new javax.swing.JRadioButton();
comComboBox = new javax.swing.JComboBox<String>();
midiOutComboBox = new javax.swing.JComboBox<String>();
enabledCB = new javax.swing.JCheckBox();
setPreferredSize(new java.awt.Dimension(525, 23));
outputTypeRB.add(moppyTypeRB);
moppyTypeRB.setText("Moppy Serial:");
moppyTypeRB.setToolTipText("Sends Moppy-protocol serial data to selected COM port");
moppyTypeRB.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
outputTypeChanged(evt);
}
});
outputTypeRB.add(MIDITypeRB);
MIDITypeRB.setText("MIDI Out:");
MIDITypeRB.setToolTipText("Sends MIDI messages through to selected MIDI port");
MIDITypeRB.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
outputTypeChanged(evt);
}
});
comComboBox.setModel(new DefaultComboBoxModel<String>(moppydesk.outputs.MoppyCOMBridge.getAvailableCOMPorts()));
comComboBox.addPopupMenuListener(new javax.swing.event.PopupMenuListener() {
public void popupMenuCanceled(javax.swing.event.PopupMenuEvent evt) {
}
public void popupMenuWillBecomeInvisible(javax.swing.event.PopupMenuEvent evt) {
}
public void popupMenuWillBecomeVisible(javax.swing.event.PopupMenuEvent evt) {
comComboBoxPopupMenuWillBecomeVisible(evt);
}
});
comComboBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
comComboBoxActionPerformed(evt);
}
});
midiOutComboBox.setModel(new DefaultComboBoxModel<String>(controlWindow.availableMIDIOuts.keySet().toArray(new String[0])));
midiOutComboBox.setEnabled(false);
midiOutComboBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
midiOutComboBoxActionPerformed(evt);
}
});
enabledCB.setText(String.valueOf(settings.MIDIChannel));
enabledCB.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
enabledCBActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(enabledCB, javax.swing.GroupLayout.PREFERRED_SIZE, 46, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(moppyTypeRB)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(comComboBox, 0, 161, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(MIDITypeRB)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(midiOutComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 130, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGap(0, 0, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(moppyTypeRB)
.addComponent(MIDITypeRB)
.addComponent(comComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(midiOutComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(enabledCB)))
);
}// </editor-fold>//GEN-END:initComponents
private void comComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_comComboBoxActionPerformed
//MrSolidSnake745: FYI, this fires on initial click to generate dropdown and once more on clicking a value
settings.comPort = (String) ((JComboBox) evt.getSource()).getSelectedItem();
}//GEN-LAST:event_comComboBoxActionPerformed
private void midiOutComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_midiOutComboBoxActionPerformed
settings.midiDeviceName = (String) ((JComboBox) evt.getSource()).getSelectedItem();
}//GEN-LAST:event_midiOutComboBoxActionPerformed
private void enabledCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_enabledCBActionPerformed
settings.enabled = ((JCheckBox) evt.getSource()).isSelected();
if (settings.enabled) {
enableControls();
} else {
disableControls();
}
}//GEN-LAST:event_enabledCBActionPerformed
private void outputTypeChanged(OutputType newType){
settings.type = newType;
if (newType.equals(OutputType.MOPPY)){
midiOutComboBox.setEnabled(false);
comComboBox.setEnabled(true);
} else {
comComboBox.setEnabled(false);
midiOutComboBox.setEnabled(true);
}
}
private void outputTypeChanged(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_outputTypeChanged
if (evt.getSource().equals(moppyTypeRB)) {
outputTypeChanged(OutputType.MOPPY);
} else {
outputTypeChanged(OutputType.MIDI);
}
}//GEN-LAST:event_outputTypeChanged
private void comComboBoxPopupMenuWillBecomeVisible(javax.swing.event.PopupMenuEvent evt) {//GEN-FIRST:event_comComboBoxPopupMenuWillBecomeVisible
refreshAvailablePorts();
}//GEN-LAST:event_comComboBoxPopupMenuWillBecomeVisible
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JRadioButton MIDITypeRB;
private javax.swing.JComboBox<String> comComboBox;
private javax.swing.JCheckBox enabledCB;
private javax.swing.JComboBox<String> midiOutComboBox;
private javax.swing.JRadioButton moppyTypeRB;
private javax.swing.ButtonGroup outputTypeRB;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,71 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
<EtchetBorder/>
</Border>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[149, 149]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="constrainCB" alignment="0" min="-2" max="-2" attributes="0"/>
<Component id="ignoreTenCB" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace pref="49" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="constrainCB" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="ignoreTenCB" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="91" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JCheckBox" name="constrainCB">
<Properties>
<Property name="text" type="java.lang.String" value="Constrain Notes"/>
<Property name="toolTipText" type="java.lang.String" value="Automatically transposes notes outside of the floppy drive range into playable range."/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="constrainCBActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="ignoreTenCB">
<Properties>
<Property name="text" type="java.lang.String" value="Ignore Channel 10"/>
<Property name="toolTipText" type="java.lang.String" value="MIDI channel 10 is generally reserved for percussion instruments and is unlikely to render correctly on floppy drives."/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="ignoreTenCBActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,139 @@
package moppydesk.ui;
import moppydesk.Constants;
import moppydesk.MoppyUI;
import moppydesk.midputs.NoteFilter;
/**
*
* @author Sammy1Am
*/
public class FilterControls extends javax.swing.JPanel {
private NoteFilter filter;
private MoppyUI app;
/**
* This constructor is only for use in the NetBeans editor window. The constructor
* below that takes a {@link MoppyUI} as an argument should always be used.
*/
public FilterControls() {
initComponents();
}
public FilterControls(MoppyUI app) {
this.app = app;
initComponents();
filter = new NoteFilter();
loadSettings();
updateFilter();
}
public NoteFilter getNoteFilter() {
return filter;
}
public void connected() {
saveSettings();
setControlsEnabled(false);
}
public void disconnected() {
filter.close();
setControlsEnabled(true);
}
private void loadSettings() {
constrainCB.setSelected(app.prefs.getBoolean(Constants.PREF_FILTER_CONSTRAIN, false));
ignoreTenCB.setSelected(app.prefs.getBoolean(Constants.PREF_FILTER_IGNORETEN, false));
updateFilter();
}
private void saveSettings() {
app.prefs.putBoolean(Constants.PREF_FILTER_CONSTRAIN, constrainCB.isSelected());
app.prefs.putBoolean(Constants.PREF_FILTER_IGNORETEN, ignoreTenCB.isSelected());
app.savePreferences();
}
private void updateFilter() {
filter.setAutoConstrain(constrainCB.isSelected());
filter.setIgnoreTen(ignoreTenCB.isSelected());
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
constrainCB = new javax.swing.JCheckBox();
ignoreTenCB = new javax.swing.JCheckBox();
setBorder(javax.swing.BorderFactory.createEtchedBorder());
setPreferredSize(new java.awt.Dimension(149, 149));
constrainCB.setText("Constrain Notes");
constrainCB.setToolTipText("Automatically transposes notes outside of the floppy drive range into playable range.");
constrainCB.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
constrainCBActionPerformed(evt);
}
});
ignoreTenCB.setText("Ignore Channel 10");
ignoreTenCB.setToolTipText("MIDI channel 10 is generally reserved for percussion instruments and is unlikely to render correctly on floppy drives.");
ignoreTenCB.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
ignoreTenCBActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(constrainCB)
.addComponent(ignoreTenCB))
.addContainerGap(49, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(constrainCB)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(ignoreTenCB)
.addContainerGap(91, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
private void constrainCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_constrainCBActionPerformed
updateFilter();
}//GEN-LAST:event_constrainCBActionPerformed
private void ignoreTenCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ignoreTenCBActionPerformed
updateFilter();
}//GEN-LAST:event_ignoreTenCBActionPerformed
private void setControlsEnabled(boolean enabled){
constrainCB.setEnabled(enabled);
ignoreTenCB.setEnabled(enabled);
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JCheckBox constrainCB;
private javax.swing.JCheckBox ignoreTenCB;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,21 @@
package moppydesk.ui;
import javax.sound.midi.Transmitter;
import javax.swing.JPanel;
/**
*
* @author Sam
*/
public abstract class InputPanel extends JPanel{
/** Returns the {@link Trasmitter} being controlled by this panel*/
abstract Transmitter getTransmitter();
/** Called when the outputs are connected to the input device*/
abstract void connected();
/** Called when the outputs are disconnected from the input device*/
abstract void disconnected();
//MrSolidSnake745: Below two optional methods define how preferences are saved/loaded for a given input panel
public void savePreferences() {}; //Called when connecting or switching panels on main window dropdown (MoppyControlWindow: , connect())
public void loadPreferences() {}; //Called when main window class is instantiated (MoppyControlWindow: constructor)
}

View File

@ -0,0 +1,58 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.3" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="midiInComboBox" min="-2" pref="187" max="-2" attributes="0"/>
<EmptySpace pref="241" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="midiInComboBox" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace pref="184" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JComboBox" name="midiInComboBox">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new DefaultComboBoxModel(availableMIDIIns.keySet().toArray(new String[0]))" type="code"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="midiInComboBoxActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="jLabel1">
<Properties>
<Property name="text" type="java.lang.String" value="MIDI IN:"/>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,99 @@
package moppydesk.ui;
import moppydesk.inputs.MoppyMIDIInput;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.midi.MidiDevice.Info;
import javax.sound.midi.MidiUnavailableException;
import javax.sound.midi.Transmitter;
import javax.swing.JComboBox;
import javax.swing.JOptionPane;
import javax.swing.DefaultComboBoxModel;
/**
* Controls for the {@link MoppyMIDIInput} object.
* @author Sam
*/
public class MIDIInControls extends InputPanel {
MoppyMIDIInput midiInput;
HashMap<String,Info> availableMIDIIns = MoppyMIDIInput.getMIDIInInfos();
/**
* Creates new form midiInControls
*/
public MIDIInControls(MoppyMIDIInput midiInput) {
this.midiInput = midiInput;
initComponents();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
midiInComboBox = new javax.swing.JComboBox();
jLabel1 = new javax.swing.JLabel();
midiInComboBox.setModel(new DefaultComboBoxModel(availableMIDIIns.keySet().toArray(new String[0])));
midiInComboBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
midiInComboBoxActionPerformed(evt);
}
});
jLabel1.setText("MIDI IN:");
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jLabel1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(midiInComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, 187, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(241, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(jLabel1)
.addComponent(midiInComboBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addContainerGap(184, Short.MAX_VALUE))
);
}// </editor-fold>//GEN-END:initComponents
private void midiInComboBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_midiInComboBoxActionPerformed
try {
midiInput.setDevice(availableMIDIIns.get(((JComboBox)evt.getSource()).getSelectedItem()));
} catch (MidiUnavailableException ex) {
Logger.getLogger(MIDIInControls.class.getName()).log(Level.SEVERE, null, ex);
}
}//GEN-LAST:event_midiInComboBoxActionPerformed
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel jLabel1;
private javax.swing.JComboBox midiInComboBox;
// End of variables declaration//GEN-END:variables
public Transmitter getTransmitter() {
return midiInput;
}
public void connected() {
//Nothing to do;
}
public void disconnected() {
midiInput.setReceiver(null); // Clear out receiver so that MIDI messages aren't sent anywhere.
}
}

View File

@ -0,0 +1,186 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.3" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
<Properties>
<Property name="defaultCloseOperation" type="int" value="3"/>
<Property name="title" type="java.lang.String" value="Moppy Control Application"/>
<Property name="iconImage" type="java.awt.Image" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new javax.swing.ImageIcon(MoppyControlWindow.class.getResource(&quot;/moppydesk/ui/moppy_ico.png&quot;)).getImage()" type="code"/>
</Property>
<Property name="resizable" type="boolean" value="false"/>
</Properties>
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
</SyntheticProperties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jSeparator1" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="inputSelectBox" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="493" max="-2" attributes="0"/>
<Component id="connectButton" min="-2" pref="99" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="filterControls1" min="-2" pref="171" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="poolingControls1" pref="0" max="32767" attributes="0"/>
</Group>
<Component id="mainInputPanel" min="-2" pref="529" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="mainOutputPanel" min="-2" pref="550" max="-2" attributes="0"/>
</Group>
<Component id="mainStatusLabel" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="1" attributes="0">
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="inputSelectBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Component id="mainOutputPanel" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="1" attributes="0">
<Component id="mainInputPanel" pref="0" max="32767" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Component id="poolingControls1" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="6" max="-2" attributes="0"/>
<Component id="filterControls1" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
<EmptySpace max="32767" attributes="0"/>
<Component id="connectButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="jSeparator1" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="mainStatusLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JSeparator" name="jSeparator1">
</Component>
<Component class="javax.swing.JLabel" name="mainStatusLabel">
<Properties>
<Property name="text" type="java.lang.String" value="Loaded."/>
<Property name="toolTipText" type="java.lang.String" value="Current status"/>
</Properties>
</Component>
<Container class="javax.swing.JPanel" name="mainInputPanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
<EtchetBorder/>
</Border>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[350, 400]"/>
</Property>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
<Component class="javax.swing.JComboBox" name="inputSelectBox">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.editors2.ComboBoxModelEditor">
<StringArray count="3">
<StringItem index="0" value="MIDI File"/>
<StringItem index="1" value="MIDI IN Port"/>
<StringItem index="2" value="Playlist"/>
</StringArray>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="inputSelectBoxActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="jLabel1">
<Properties>
<Property name="text" type="java.lang.String" value="Input Mode"/>
</Properties>
</Component>
<Container class="javax.swing.JPanel" name="mainOutputPanel">
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
<EtchetBorder/>
</Border>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[350, 400]"/>
</Property>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignBoxLayout">
<Property name="axis" type="int" value="1"/>
</Layout>
</Container>
<Component class="javax.swing.JButton" name="connectButton">
<Properties>
<Property name="text" type="java.lang.String" value="Connect"/>
<Property name="toolTipText" type="java.lang.String" value="Saves current output settings and connects as specified."/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="connectButtonActionPerformed"/>
</Events>
</Component>
<Component class="moppydesk.ui.PoolingControls" name="poolingControls1">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new PoolingControls(app);"/>
</AuxValues>
</Component>
<Component class="moppydesk.ui.FilterControls" name="filterControls1">
<AuxValues>
<AuxValue name="JavaCodeGenerator_CreateCodeCustom" type="java.lang.String" value="new FilterControls(app);"/>
</AuxValues>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,343 @@
package moppydesk.ui;
import moppydesk.outputs.MoppyMIDIOutput;
import moppydesk.outputs.MoppyCOMBridge;
import moppydesk.outputs.MoppyPlayerOutput;
import gnu.io.NoSuchPortException;
import gnu.io.PortInUseException;
import gnu.io.UnsupportedCommOperationException;
import java.awt.Component;
import java.io.IOException;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.midi.MidiDevice.Info;
import javax.sound.midi.MidiUnavailableException;
import javax.swing.JOptionPane;
import moppydesk.*;
import moppydesk.outputs.MoppyReceiver;
/**
*
* @author Sammy1Am
*/
public class MoppyControlWindow extends javax.swing.JFrame {
MoppyUI app;
HashMap<String, Info> availableMIDIOuts;
OutputSetting[] outputSettings = new OutputSetting[Constants.NUM_MIDI_CHANNELS];
HashMap<String,MoppyReceiver> outputPlayers = new HashMap<String, MoppyReceiver>();
MIDIInControls midiInControls;
SequencerControls seqControls;
PlaylistControls playControls;
InputPanel currentInputPanel;
/**
* Creates new form MoppyControlWindow
*/
public MoppyControlWindow(MoppyUI app) {
this.app = app;
midiInControls = new MIDIInControls(app.midiIn);
seqControls = new SequencerControls(app, this, app.ms);
playControls = new PlaylistControls(app, this, app.ms);
availableMIDIOuts = MoppyMIDIOutput.getMIDIOutInfos();
loadOutputSettings();
initComponents();
updateInputPanel(); //Sammy1Am: Preferences will be loaded for the input panels in this call
setupOutputControls();
}
private void loadOutputSettings() {
OutputSetting[] os = (OutputSetting[]) app.getPreferenceObject(Constants.PREF_OUTPUT_SETTINGS);
if (os == null) {
for (int i = 1; i <= 16; i++) {
outputSettings[i - 1] = new OutputSetting(i);
}
app.putPreferenceObject(Constants.PREF_OUTPUT_SETTINGS, outputSettings);
} else {
outputSettings = os;
}
}
private void setupOutputControls() {
for (OutputSetting s : outputSettings) {
ChannelOutControl newControl = new ChannelOutControl(this, s);
//TODO Read in preferences here? Serialize all properties to preferences?
mainOutputPanel.add(newControl);
}
mainOutputPanel.revalidate();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jSeparator1 = new javax.swing.JSeparator();
mainStatusLabel = new javax.swing.JLabel();
mainInputPanel = new javax.swing.JPanel();
inputSelectBox = new javax.swing.JComboBox();
jLabel1 = new javax.swing.JLabel();
mainOutputPanel = new javax.swing.JPanel();
connectButton = new javax.swing.JButton();
poolingControls1 = new PoolingControls(app);
filterControls1 = new FilterControls(app);
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("Moppy Control Application");
setIconImage(new javax.swing.ImageIcon(MoppyControlWindow.class.getResource("/moppydesk/ui/moppy_ico.png")).getImage());
setResizable(false);
mainStatusLabel.setText("Loaded.");
mainStatusLabel.setToolTipText("Current status");
mainInputPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
mainInputPanel.setPreferredSize(new java.awt.Dimension(350, 400));
inputSelectBox.setModel(new javax.swing.DefaultComboBoxModel(new String[] { "MIDI File", "MIDI IN Port", "Playlist" }));
inputSelectBox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
inputSelectBoxActionPerformed(evt);
}
});
jLabel1.setText("Input Mode");
mainOutputPanel.setBorder(javax.swing.BorderFactory.createEtchedBorder());
mainOutputPanel.setPreferredSize(new java.awt.Dimension(350, 400));
mainOutputPanel.setLayout(new javax.swing.BoxLayout(mainOutputPanel, javax.swing.BoxLayout.Y_AXIS));
connectButton.setText("Connect");
connectButton.setToolTipText("Saves current output settings and connects as specified.");
connectButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
connectButtonActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jSeparator1)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addComponent(jLabel1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(inputSelectBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup()
.addGap(493, 493, 493)
.addComponent(connectButton, javax.swing.GroupLayout.PREFERRED_SIZE, 99, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addGap(0, 0, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(filterControls1, javax.swing.GroupLayout.PREFERRED_SIZE, 171, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(poolingControls1, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
.addComponent(mainInputPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 529, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(mainOutputPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 550, javax.swing.GroupLayout.PREFERRED_SIZE))
.addComponent(mainStatusLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGap(8, 8, 8)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(inputSelectBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabel1))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addComponent(mainOutputPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addComponent(mainInputPanel, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(poolingControls1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup()
.addGap(6, 6, 6)
.addComponent(filterControls1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(connectButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(jSeparator1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(mainStatusLabel)
.addContainerGap())
);
pack();
}// </editor-fold>//GEN-END:initComponents
private void connect() {
try {
//Let the control pannels know they're connected.
currentInputPanel.connected();
poolingControls1.connected();
filterControls1.connected();
//Disable and save output settings...
for (Component c : mainOutputPanel.getComponents()){
if (c instanceof ChannelOutControl){
((ChannelOutControl)c).lockControl();
}
}
app.putPreferenceObject(Constants.PREF_OUTPUT_SETTINGS, outputSettings);
app.savePreferences();
//MrSolidSnake745: Tell the current input panel to save it's preferences
currentInputPanel.savePreferences();
setStatus("Initializing Receivers...");
initializeReceivers();
//If pooling is enabled, send messages through pooler, otherwise bypass it
inputSelectBox.setEnabled(false);
// Always connect to the note filter
currentInputPanel.getTransmitter().setReceiver(filterControls1.getNoteFilter());
// Only connect to pooling if it's enabled.
if (poolingControls1.isPoolingEnabled()){
filterControls1.getNoteFilter().setReceiver(poolingControls1.getDrivePooler());
poolingControls1.getDrivePooler().setReceiver(app.rm);
} else {
filterControls1.getNoteFilter().setReceiver(app.rm);
}
connectButton.setText("Disconnect");
setStatus("Connected.");
} catch (Exception ex) {
Logger.getLogger(MoppyControlWindow.class.getName()).log(Level.SEVERE, null, ex);
JOptionPane.showMessageDialog(rootPane, ex.toString(),ex.getMessage(),JOptionPane.ERROR_MESSAGE);
disconnect();
}
}
private void disconnect() {
setStatus("Disconnecting...");
app.rm.close();
currentInputPanel.disconnected();
poolingControls1.disconnected();
filterControls1.disconnected();
//Reenable output settings
for (Component c : mainOutputPanel.getComponents()){
if (c instanceof ChannelOutControl){
((ChannelOutControl)c).unlockControl();
}
}
inputSelectBox.setEnabled(true);
connectButton.setText("Connect");
setStatus("Disconnected.");
}
private void initializeReceivers() throws NoSuchPortException, PortInUseException, UnsupportedCommOperationException, IOException, MidiUnavailableException {
app.rm.clearReceivers();
outputPlayers.clear();
for (int ch = 1; ch <= 16; ch++) {
OutputSetting os = outputSettings[ch-1]; //OutputSettings are 0-indexed
if (os.enabled) {
// MoppyPlayer/Receivers are grouped by COM port
if (os.type.equals(OutputSetting.OutputType.MOPPY)) {
if (!outputPlayers.containsKey(os.comPort)){
outputPlayers.put(os.comPort, new MoppyPlayerOutput(new MoppyCOMBridge(os.comPort)));
}
app.rm.setReceiver(ch, outputPlayers.get(os.comPort));
}
//MIDIPlayer/Receivers are grouped by MIDI output name
else if (os.type.equals(OutputSetting.OutputType.MIDI)) {
if (!outputPlayers.containsKey(os.midiDeviceName)){
outputPlayers.put(os.midiDeviceName, new MoppyMIDIOutput(os.midiDeviceName));
}
app.rm.setReceiver(ch, outputPlayers.get(os.midiDeviceName));
}
}
}
}
private void updateInputPanel(){
if (currentInputPanel != null) { //Panel is only null on initial load, also helps prevent unecessary preference saving
currentInputPanel.savePreferences(); //Tell the current panel to save preferences before switching
//MrSolidSnake745: Necessary after implementing sequenceEnded event and having multiple input panels
//Without removing listener, sequenceEnded can fire on all panels implementing MoppyStatusConsumer even if they are not the current input panel
//Applies to all events defined through MoppyStatusConsumer
if (currentInputPanel instanceof MoppyStatusConsumer) app.ms.removeListener((MoppyStatusConsumer) currentInputPanel);
}
mainInputPanel.removeAll();
switch(inputSelectBox.getSelectedIndex())
{
case 0: currentInputPanel = seqControls; break; //MIDI File
case 1: currentInputPanel = midiInControls; break; //MIDI In
case 2: currentInputPanel = playControls; break; //Playlist
}
//Adding listener back for the selected panel if it implements MoppyStatusConsumer
if (currentInputPanel instanceof MoppyStatusConsumer) app.ms.addListener((MoppyStatusConsumer) currentInputPanel);
//Before adding the panel to the mainUI, load its preferences (in case something has been changed (e.g. by another input panel)
currentInputPanel.loadPreferences();
mainInputPanel.add(currentInputPanel);
mainInputPanel.revalidate();
mainInputPanel.repaint();
}
private void connectButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_connectButtonActionPerformed
connectButton.setEnabled(false);
if (connectButton.getText().equals("Connect")) {
connect();
} else {
disconnect();
}
connectButton.setEnabled(true);
}//GEN-LAST:event_connectButtonActionPerformed
private void inputSelectBoxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_inputSelectBoxActionPerformed
updateInputPanel();
}//GEN-LAST:event_inputSelectBoxActionPerformed
public void setStatus(String newStatus) {
mainStatusLabel.setText(newStatus);
mainStatusLabel.repaint();
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JButton connectButton;
private moppydesk.ui.FilterControls filterControls1;
private javax.swing.JComboBox inputSelectBox;
private javax.swing.JLabel jLabel1;
private javax.swing.JSeparator jSeparator1;
private javax.swing.JPanel mainInputPanel;
private javax.swing.JPanel mainOutputPanel;
private javax.swing.JLabel mainStatusLabel;
private moppydesk.ui.PoolingControls poolingControls1;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,371 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.7" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<Properties>
<Property name="maximumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[529, 240]"/>
</Property>
<Property name="minimumSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[0, 0]"/>
</Property>
<Property name="name" type="java.lang.String" value="" noResource="true"/>
<Property name="opaque" type="boolean" value="false"/>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[524, 240]"/>
</Property>
<Property name="requestFocusEnabled" type="boolean" value="false"/>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="currentPositionLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="sequenceProgressSlider" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="totalPositionLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="loadListButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="saveListButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="randomizeButton" min="-2" pref="60" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="loadLastListCheckbox" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="jPanel1" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="sequenceNameLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="bpmLabel" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="playlistScrollPane" min="-2" pref="425" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="loadButton" alignment="1" max="32767" attributes="0"/>
<Component id="resetDrivesCheckbox" alignment="1" pref="87" max="32767" attributes="0"/>
<Component id="loadDirectoryButton" max="32767" attributes="0"/>
</Group>
</Group>
<Component id="clearButton" max="32767" attributes="0"/>
<Component id="resetListButton" max="32767" attributes="0"/>
</Group>
</Group>
</Group>
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="2" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" max="-2" attributes="0">
<Group type="102" attributes="0">
<Component id="loadButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="loadDirectoryButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="resetListButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="clearButton" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="resetDrivesCheckbox" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="playlistScrollPane" pref="0" max="32767" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="loadListButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="saveListButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="randomizeButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="loadLastListCheckbox" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<Component id="jPanel1" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace pref="20" max="32767" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="sequenceNameLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="sequenceProgressSlider" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<Component id="bpmLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="currentPositionLabel" min="-2" max="-2" attributes="0"/>
<Component id="totalPositionLabel" alignment="0" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="sequenceNameLabel">
<Properties>
<Property name="text" type="java.lang.String" value="&lt;None loaded&gt;"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="bpmLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="&quot;? bpm&quot;" type="code"/>
</Property>
<Property name="alignmentX" type="float" value="1.0"/>
</Properties>
<AuxValues>
<AuxValue name="JavaCodeGenerator_SerializeTo" type="java.lang.String" value="PlaylistControls_bpmLabel"/>
</AuxValues>
</Component>
<Component class="javax.swing.JButton" name="clearButton">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="10" style="0"/>
</Property>
<Property name="text" type="java.lang.String" value="Clear List"/>
<Property name="toolTipText" type="java.lang.String" value=""/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 2, 2, 2]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="clearButtonstopResetClicked"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="loadButton">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="10" style="0"/>
</Property>
<Property name="label" type="java.lang.String" value="Load MIDI"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 2, 2, 2]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="loadButtonloadSequence"/>
</Events>
</Component>
<Component class="javax.swing.JSlider" name="sequenceProgressSlider">
<Properties>
<Property name="toolTipText" type="java.lang.String" value=""/>
<Property name="value" type="int" value="0"/>
</Properties>
<Events>
<EventHandler event="mouseDragged" listener="java.awt.event.MouseMotionListener" parameters="java.awt.event.MouseEvent" handler="sequenceProgressDragged"/>
<EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="sequenceProgressDragged"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="currentPositionLabel">
<Properties>
<Property name="text" type="java.lang.String" value="00:00"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="totalPositionLabel">
<Properties>
<Property name="text" type="java.lang.String" value="00:00"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="resetListButton">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="10" style="0"/>
</Property>
<Property name="text" type="java.lang.String" value="Reset List"/>
<Property name="toolTipText" type="java.lang.String" value=""/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 2, 2, 2]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="resetListButtonstopResetClicked"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="resetDrivesCheckbox">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="10" style="0"/>
</Property>
<Property name="text" type="java.lang.String" value="Reset drives"/>
<Property name="toolTipText" type="java.lang.String" value="If selected, will reset the drives to the intial position between songs"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="resetDrivesCheckboxActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="loadListButton">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="10" style="0"/>
</Property>
<Property name="text" type="java.lang.String" value="Load List"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 2, 2, 2]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="loadListButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="saveListButton">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="10" style="0"/>
</Property>
<Property name="text" type="java.lang.String" value="Save List"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 2, 2, 2]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="saveListButtonActionPerformed"/>
</Events>
</Component>
<Container class="javax.swing.JScrollPane" name="playlistScrollPane">
<AuxValues>
<AuxValue name="autoScrollPane" type="java.lang.Boolean" value="true"/>
</AuxValues>
<Layout class="org.netbeans.modules.form.compat2.layouts.support.JScrollPaneSupportLayout"/>
<SubComponents>
<Component class="javax.swing.JTable" name="playlistTable">
<Properties>
<Property name="model" type="javax.swing.table.TableModel" editor="org.netbeans.modules.form.editors2.TableModelEditor">
<Table columnCount="0" rowCount="0"/>
</Property>
<Property name="columnModel" type="javax.swing.table.TableColumnModel" editor="org.netbeans.modules.form.editors2.TableColumnModelEditor">
<TableColumnModel selectionModel="0"/>
</Property>
<Property name="tableHeader" type="javax.swing.table.JTableHeader" editor="org.netbeans.modules.form.editors2.JTableHeaderEditor">
<TableHeader reorderingAllowed="false" resizingAllowed="true"/>
</Property>
</Properties>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="jPanel1">
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
<SubComponents>
<Component class="javax.swing.JButton" name="previousButton">
<Properties>
<Property name="text" type="java.lang.String" value="&#x25c4;&#x25c4;"/>
<Property name="toolTipText" type="java.lang.String" value="Previous"/>
<Property name="enabled" type="boolean" value="false"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 2, 2, 2]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="previousButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="startButton">
<Properties>
<Property name="text" type="java.lang.String" value=" &#x25ba; "/>
<Property name="toolTipText" type="java.lang.String" value="Start/Pause"/>
<Property name="enabled" type="boolean" value="false"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 3, 2, 3]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="startButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="stopButton">
<Properties>
<Property name="text" type="java.lang.String" value="&#x25a0;"/>
<Property name="toolTipText" type="java.lang.String" value="Stop/Reset - Click while stopped to reset drives"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 7, 2, 7]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="stopButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="nextButton">
<Properties>
<Property name="text" type="java.lang.String" value="&#x25ba;&#x25ba;"/>
<Property name="toolTipText" type="java.lang.String" value="Next"/>
<Property name="enabled" type="boolean" value="false"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 2, 2, 2]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="nextButtonActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Container>
<Component class="javax.swing.JButton" name="loadDirectoryButton">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="10" style="0"/>
</Property>
<Property name="text" type="java.lang.String" value="Load Directory"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 2, 2, 2]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="loadDirectoryButtonloadSequence"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="randomizeButton">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="10" style="0"/>
</Property>
<Property name="text" type="java.lang.String" value="Randomize"/>
<Property name="margin" type="java.awt.Insets" editor="org.netbeans.beaninfo.editors.InsetsEditor">
<Insets value="[2, 2, 2, 2]"/>
</Property>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="randomizeButtonActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JCheckBox" name="loadLastListCheckbox">
<Properties>
<Property name="font" type="java.awt.Font" editor="org.netbeans.beaninfo.editors.FontEditor">
<Font name="Tahoma" size="10" style="0"/>
</Property>
<Property name="text" type="java.lang.String" value="Load last MPL on startup"/>
<Property name="toolTipText" type="java.lang.String" value="If selected, will load the last successfully loaded MPL file"/>
<Property name="actionCommand" type="java.lang.String" value="Load last MPL on startup"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="loadLastListCheckboxActionPerformed"/>
</Events>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,702 @@
/*
* To change this template, choose Tools | Templates
* and open the template in the editor.
*/
package moppydesk.ui;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.midi.Transmitter;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JSlider;
import javax.swing.JTable;
import javax.swing.Timer;
import javax.swing.filechooser.FileNameExtensionFilter;
import javax.swing.table.TableColumnModel;
import moppydesk.Constants;
import moppydesk.MoppyStatusConsumer;
import moppydesk.MoppyUI;
import moppydesk.inputs.MoppySequencer;
import moppydesk.playlist.MoppyPlaylist;
/**
*
* @author Sam/AJ (MrSolidSnake745)
*
*/
public class PlaylistControls extends InputPanel implements MoppyStatusConsumer {
MoppySequencer seq;
MoppyControlWindow controlWindow;
MoppyUI app;
final JFileChooser sequenceChooser = new JFileChooser();
final FileNameExtensionFilter MIDIFilter = new FileNameExtensionFilter("MIDI Files (*.mid, *.midi)", "mid", "midi");
final FileNameExtensionFilter MPLFilter = new FileNameExtensionFilter("Moppy Playlist Files", "mpl");
Timer progressTimer;
private boolean isConnected = false;
private boolean fileLoaded = false;
private boolean interruptNextSong = false;
MoppyPlaylist playlist = new MoppyPlaylist();
/**
* Creates new form PlaylistControls
*/
public PlaylistControls(MoppyUI app, MoppyControlWindow mcw, MoppySequencer newSequencer) {
this.seq = newSequencer;
this.app = app;
this.controlWindow = mcw;
initComponents();
progressTimer = new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
updateProgressDisplay();
}
});
setupPlaylistTable();
sequenceChooser.addChoosableFileFilter(MIDIFilter);
sequenceChooser.addChoosableFileFilter(MPLFilter);
}
@Override
public void savePreferences() {
//Nothing to do here! All prefs are saved upon UI actions currently
}
@Override
public void loadPreferences() {
loadLastListCheckbox.setSelected(app.prefs.getBoolean(Constants.PREF_LOAD_MPL_ON_START, false));
String previouslyLoaded = app.prefs.get(Constants.PREF_LOADED_MPL, null);
if (previouslyLoaded != null && !previouslyLoaded.isEmpty() && loadLastListCheckbox.isSelected()) {
//Try to load the last MPL, if it fails, clear out the preference
if (!loadPlaylist(new File(previouslyLoaded))) { app.prefs.put(Constants.PREF_LOADED_MPL, ""); }
}
}
private void updateProgressDisplay() {
long currentSeconds = seq.getSecondsPosition();
sequenceProgressSlider.setValue((int) (currentSeconds));
String currentPosition = String.format("%d:%02d",
TimeUnit.SECONDS.toMinutes(currentSeconds),
currentSeconds % 60);
String totalPosition = String.format("%d:%02d",
TimeUnit.SECONDS.toMinutes(seq.getSecondsLength()),
seq.getSecondsLength() % 60);
currentPositionLabel.setText(currentPosition);
totalPositionLabel.setText(totalPosition);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
sequenceNameLabel = new javax.swing.JLabel();
bpmLabel = new javax.swing.JLabel();
clearButton = new javax.swing.JButton();
loadButton = new javax.swing.JButton();
sequenceProgressSlider = new javax.swing.JSlider();
currentPositionLabel = new javax.swing.JLabel();
totalPositionLabel = new javax.swing.JLabel();
resetListButton = new javax.swing.JButton();
resetDrivesCheckbox = new javax.swing.JCheckBox();
loadListButton = new javax.swing.JButton();
saveListButton = new javax.swing.JButton();
playlistScrollPane = new javax.swing.JScrollPane();
playlistTable = new javax.swing.JTable();
jPanel1 = new javax.swing.JPanel();
previousButton = new javax.swing.JButton();
startButton = new javax.swing.JButton();
stopButton = new javax.swing.JButton();
nextButton = new javax.swing.JButton();
loadDirectoryButton = new javax.swing.JButton();
randomizeButton = new javax.swing.JButton();
loadLastListCheckbox = new javax.swing.JCheckBox();
setMaximumSize(new java.awt.Dimension(529, 240));
setMinimumSize(new java.awt.Dimension(0, 0));
setName(""); // NOI18N
setOpaque(false);
setPreferredSize(new java.awt.Dimension(524, 240));
setRequestFocusEnabled(false);
sequenceNameLabel.setText("<None loaded>");
bpmLabel.setText("? bpm");
bpmLabel.setAlignmentX(1.0F);
clearButton.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N
clearButton.setText("Clear List");
clearButton.setToolTipText("");
clearButton.setMargin(new java.awt.Insets(2, 2, 2, 2));
clearButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
clearButtonstopResetClicked(evt);
}
});
loadButton.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N
loadButton.setLabel("Load MIDI");
loadButton.setMargin(new java.awt.Insets(2, 2, 2, 2));
loadButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
loadButtonloadSequence(evt);
}
});
sequenceProgressSlider.setToolTipText("");
sequenceProgressSlider.setValue(0);
sequenceProgressSlider.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
public void mouseDragged(java.awt.event.MouseEvent evt) {
sequenceProgressDragged(evt);
}
});
sequenceProgressSlider.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
sequenceProgressDragged(evt);
}
});
currentPositionLabel.setText("00:00");
totalPositionLabel.setText("00:00");
resetListButton.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N
resetListButton.setText("Reset List");
resetListButton.setToolTipText("");
resetListButton.setMargin(new java.awt.Insets(2, 2, 2, 2));
resetListButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
resetListButtonstopResetClicked(evt);
}
});
resetDrivesCheckbox.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N
resetDrivesCheckbox.setText("Reset drives");
resetDrivesCheckbox.setToolTipText("If selected, will reset the drives to the intial position between songs");
resetDrivesCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
resetDrivesCheckboxActionPerformed(evt);
}
});
loadListButton.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N
loadListButton.setText("Load List");
loadListButton.setMargin(new java.awt.Insets(2, 2, 2, 2));
loadListButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
loadListButtonActionPerformed(evt);
}
});
saveListButton.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N
saveListButton.setText("Save List");
saveListButton.setMargin(new java.awt.Insets(2, 2, 2, 2));
saveListButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
saveListButtonActionPerformed(evt);
}
});
playlistTable.setModel(new javax.swing.table.DefaultTableModel(
new Object [][] {
},
new String [] {
}
));
playlistTable.getTableHeader().setReorderingAllowed(false);
playlistScrollPane.setViewportView(playlistTable);
previousButton.setText("◄◄");
previousButton.setToolTipText("Previous");
previousButton.setEnabled(false);
previousButton.setMargin(new java.awt.Insets(2, 2, 2, 2));
previousButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
previousButtonActionPerformed(evt);
}
});
jPanel1.add(previousButton);
startButton.setText("");
startButton.setToolTipText("Start/Pause");
startButton.setEnabled(false);
startButton.setMargin(new java.awt.Insets(2, 3, 2, 3));
startButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
startButtonActionPerformed(evt);
}
});
jPanel1.add(startButton);
stopButton.setText("");
stopButton.setToolTipText("Stop/Reset - Click while stopped to reset drives");
stopButton.setMargin(new java.awt.Insets(2, 7, 2, 7));
stopButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
stopButtonActionPerformed(evt);
}
});
jPanel1.add(stopButton);
nextButton.setText("►►");
nextButton.setToolTipText("Next");
nextButton.setEnabled(false);
nextButton.setMargin(new java.awt.Insets(2, 2, 2, 2));
nextButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
nextButtonActionPerformed(evt);
}
});
jPanel1.add(nextButton);
loadDirectoryButton.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N
loadDirectoryButton.setText("Load Directory");
loadDirectoryButton.setMargin(new java.awt.Insets(2, 2, 2, 2));
loadDirectoryButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
loadDirectoryButtonloadSequence(evt);
}
});
randomizeButton.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N
randomizeButton.setText("Randomize");
randomizeButton.setMargin(new java.awt.Insets(2, 2, 2, 2));
randomizeButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
randomizeButtonActionPerformed(evt);
}
});
loadLastListCheckbox.setFont(new java.awt.Font("Tahoma", 0, 10)); // NOI18N
loadLastListCheckbox.setText("Load last MPL on startup");
loadLastListCheckbox.setToolTipText("If selected, will load the last successfully loaded MPL file");
loadLastListCheckbox.setActionCommand("Load last MPL on startup");
loadLastListCheckbox.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
loadLastListCheckboxActionPerformed(evt);
}
});
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(2, 2, 2)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(currentPositionLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(sequenceProgressSlider, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(totalPositionLabel))
.addGroup(layout.createSequentialGroup()
.addComponent(loadListButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(saveListButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(randomizeButton, javax.swing.GroupLayout.PREFERRED_SIZE, 60, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(loadLastListCheckbox)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup()
.addComponent(sequenceNameLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(bpmLabel))
.addGroup(layout.createSequentialGroup()
.addComponent(playlistScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 425, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(2, 2, 2)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(loadButton, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(resetDrivesCheckbox, javax.swing.GroupLayout.Alignment.TRAILING, javax.swing.GroupLayout.DEFAULT_SIZE, 87, Short.MAX_VALUE)
.addComponent(loadDirectoryButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)))
.addComponent(clearButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(resetListButton, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))))
.addGap(2, 2, 2))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(2, 2, 2)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING, false)
.addGroup(layout.createSequentialGroup()
.addComponent(loadButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(loadDirectoryButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(resetListButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(clearButton)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(resetDrivesCheckbox))
.addComponent(playlistScrollPane, javax.swing.GroupLayout.PREFERRED_SIZE, 0, Short.MAX_VALUE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(loadListButton)
.addComponent(saveListButton)
.addComponent(randomizeButton)
.addComponent(loadLastListCheckbox))
.addComponent(jPanel1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, 20, Short.MAX_VALUE)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(layout.createSequentialGroup()
.addComponent(sequenceNameLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(sequenceProgressSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))
.addGroup(layout.createSequentialGroup()
.addComponent(bpmLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(currentPositionLabel)
.addComponent(totalPositionLabel))))
.addContainerGap())
);
}// </editor-fold>//GEN-END:initComponents
private void playSequencer() {
if(!(fileLoaded) && !(playlist.isFinished())) {
loadSequenceFile(playlist.getNextSong());
}
seq.startSequencer();
controlWindow.setStatus("Playing!");
startButton.setText("▐ ▌");
}
private void pauseSequencer() {
seq.stopSequencer();
startButton.setText("");
controlWindow.setStatus("...Paused");
}
private void stopResetSequencer() {
if (seq.isRunning()) {
controlWindow.setStatus("Stopping...");
seq.stopSequencer();
seq.resetSequencer();
startButton.setText("");
controlWindow.setStatus("Stopped.");
} else {
app.rm.reset();
controlWindow.setStatus("Reset.");
}
}
private void clearButtonstopResetClicked(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_clearButtonstopResetClicked
clearPlaylist();
}//GEN-LAST:event_clearButtonstopResetClicked
private void loadButtonloadSequence(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_loadButtonloadSequence
String previouslyLoaded = app.prefs.get(Constants.PREF_LOADED_LIST, null);
if (previouslyLoaded != null) {
sequenceChooser.setCurrentDirectory(new File(previouslyLoaded));
}
sequenceChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
sequenceChooser.setFileFilter(MIDIFilter);
int returnVal = sequenceChooser.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
app.prefs.put(Constants.PREF_LOADED_LIST, sequenceChooser.getSelectedFile().getPath());
playlist.addSong(sequenceChooser.getSelectedFile());
if (isConnected && !(playlist.isFinished())) {
startButton.setEnabled(true);
}
} else {
//Cancelled
}
}//GEN-LAST:event_loadButtonloadSequence
private void sequenceProgressDragged(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_sequenceProgressDragged
int seconds = ((JSlider) evt.getSource()).getValue();
seq.setSecondsPosition(seconds);
currentPositionLabel.setText(String.format("%d:%02d",
TimeUnit.SECONDS.toMinutes(seconds),
seconds % 60));
}//GEN-LAST:event_sequenceProgressDragged
private void resetListButtonstopResetClicked(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_resetListButtonstopResetClicked
resetPlaylist();
}//GEN-LAST:event_resetListButtonstopResetClicked
private void loadListButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_loadListButtonActionPerformed
loadPlaylist(getPlaylistFile(true));
}//GEN-LAST:event_loadListButtonActionPerformed
private void saveListButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_saveListButtonActionPerformed
savePlaylist(getPlaylistFile(false));
}//GEN-LAST:event_saveListButtonActionPerformed
private void resetDrivesCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_resetDrivesCheckboxActionPerformed
// TODO add your handling code here:
}//GEN-LAST:event_resetDrivesCheckboxActionPerformed
private void startButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_startButtonActionPerformed
if (startButton.getText().equals("")) {
interruptNextSong = false;
playSequencer();
} else {
pauseSequencer();
}
}//GEN-LAST:event_startButtonActionPerformed
private void stopButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_stopButtonActionPerformed
stopResetSequencer();
}//GEN-LAST:event_stopButtonActionPerformed
private void nextButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_nextButtonActionPerformed
if(!playlist.isLastSong()) {
try {
loadSequenceFile(playlist.getNextSong());
if(isConnected) {Thread.sleep(1000); playSequencer();}
} catch (InterruptedException ex) {
Logger.getLogger(PlaylistControls.class.getName()).log(Level.SEVERE, null, ex);
controlWindow.setStatus("Error playing next song!");
JOptionPane.showMessageDialog(this.getRootPane(), ex);
}
}
}//GEN-LAST:event_nextButtonActionPerformed
private void previousButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_previousButtonActionPerformed
if(!playlist.isFirstSong()) {
try {
loadSequenceFile(playlist.getPreviousSong());
if(isConnected) {Thread.sleep(1000); playSequencer();}
} catch (InterruptedException ex) {
Logger.getLogger(PlaylistControls.class.getName()).log(Level.SEVERE, null, ex);
controlWindow.setStatus("Error playing previous song!");
JOptionPane.showMessageDialog(this.getRootPane(), ex);
}
}
}//GEN-LAST:event_previousButtonActionPerformed
private void loadDirectoryButtonloadSequence(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_loadDirectoryButtonloadSequence
String previouslyLoaded = app.prefs.get(Constants.PREF_LOADED_LIST, null);
if (previouslyLoaded != null) {
sequenceChooser.setCurrentDirectory(new File(previouslyLoaded));
}
sequenceChooser.setFileSelectionMode(JFileChooser.DIRECTORIES_ONLY);
sequenceChooser.setFileFilter(sequenceChooser.getAcceptAllFileFilter());
int returnVal = sequenceChooser.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
app.prefs.put(Constants.PREF_LOADED_LIST, sequenceChooser.getSelectedFile().getPath());
for (File file : sequenceChooser.getSelectedFile().listFiles()) {
String extension = file.getName().substring(file.getName().lastIndexOf(".") + 1, file.getName().length());
if("mid".equals(extension) || "midi".equals(extension)) {playlist.addSong(file);}
}
sequenceChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
if (isConnected && !(playlist.isFinished())) {startButton.setEnabled(true);}
} else {
//Cancelled
}
sequenceChooser.setAcceptAllFileFilterUsed(true);
}//GEN-LAST:event_loadDirectoryButtonloadSequence
private void randomizeButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_randomizeButtonActionPerformed
playlist.randomize();
}//GEN-LAST:event_randomizeButtonActionPerformed
private void loadLastListCheckboxActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_loadLastListCheckboxActionPerformed
app.prefs.putBoolean(Constants.PREF_LOAD_MPL_ON_START, loadLastListCheckbox.isSelected());
}//GEN-LAST:event_loadLastListCheckboxActionPerformed
@Override
public void tempoChanged(int newTempo) {
bpmLabel.setText(newTempo + " bpm");
}
@Override
public void sequenceEnded() {
try {
playlist.currentSongFinished();
if (!(playlist.isFinished())) {
loadSequenceFile(playlist.getNextSong());
if(resetDrivesCheckbox.isSelected()) {Thread.sleep(500); app.rm.reset(); Thread.sleep(500);}
Thread.sleep(3000);
if(interruptNextSong) {interruptNextSong = false;}
else {playSequencer();}
}
else {
playlistEnded();
}
}
catch (InterruptedException ex) {
Logger.getLogger(MoppyControlWindow.class.getName()).log(Level.SEVERE, null, ex);
controlWindow.setStatus("Error playing next song!");
JOptionPane.showMessageDialog(this.getRootPane(), ex);
}
}
private void loadSequenceFile(File sequenceFile) {
try {
controlWindow.setStatus("Loading file...");
seq.loadFile(sequenceFile.getPath());
sequenceNameLabel.setText(playlist.getCurrentSongName());
sequenceProgressSlider.setMaximum((int) (seq.getSecondsLength()));
controlWindow.setStatus("Loaded " + sequenceFile.getName());
updateProgressDisplay();
fileLoaded = true;
} catch (Exception ex) {
Logger.getLogger(MoppyControlWindow.class.getName()).log(Level.SEVERE, null, ex);
controlWindow.setStatus("File loading error!");
JOptionPane.showMessageDialog(this.getRootPane(), ex);
fileLoaded = false;
}
enableNextPrev();
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel bpmLabel;
private javax.swing.JButton clearButton;
private javax.swing.JLabel currentPositionLabel;
private javax.swing.JPanel jPanel1;
private javax.swing.JButton loadButton;
private javax.swing.JButton loadDirectoryButton;
private javax.swing.JCheckBox loadLastListCheckbox;
private javax.swing.JButton loadListButton;
private javax.swing.JButton nextButton;
private javax.swing.JScrollPane playlistScrollPane;
private javax.swing.JTable playlistTable;
private javax.swing.JButton previousButton;
private javax.swing.JButton randomizeButton;
private javax.swing.JCheckBox resetDrivesCheckbox;
private javax.swing.JButton resetListButton;
private javax.swing.JButton saveListButton;
private javax.swing.JLabel sequenceNameLabel;
private javax.swing.JSlider sequenceProgressSlider;
private javax.swing.JButton startButton;
private javax.swing.JButton stopButton;
private javax.swing.JLabel totalPositionLabel;
// End of variables declaration//GEN-END:variables
@Override
public Transmitter getTransmitter() {
return seq;
}
@Override
public void connected() {
progressTimer.start();
isConnected = true;
if (!(playlist.isFinished())) {startButton.setEnabled(true);}
}
@Override
public void disconnected() {
startButton.setEnabled(false);
pauseSequencer();
isConnected = false;
progressTimer.stop();
seq.setReceiver(null); //Clear receiver so there's no connection here.
}
// <editor-fold defaultstate="collapsed" desc="Playlist specific functions">
private void enableNextPrev() {
previousButton.setEnabled(!playlist.isFirstSong());
nextButton.setEnabled(!playlist.isLastSong());
}
private void playlistEnded() {
interruptNextSong = false;
controlWindow.setStatus("Stopping...");
seq.resetSequencer();
startButton.setText("");
startButton.setEnabled(false);
sequenceProgressSlider.setValue(0);
fileLoaded = false;
controlWindow.setStatus("Stopped.");
}
private void clearPlaylist() {
interruptNextSong = true;
if (seq.isRunning()) {stopResetSequencer();} //I know this seems redundant, but I did not want to rewrite code and only wanted that part of the function
playlist.clear();
playlistEnded();
if (isConnected) {startButton.setEnabled(true);}
}
private void resetPlaylist() {
interruptNextSong = true;
if (seq.isRunning()) {stopResetSequencer();} //Same as above comment
playlist.reset();
playlistEnded();
if (isConnected) {startButton.setEnabled(true);}
}
private File getPlaylistFile(boolean direction) {
String previouslyLoaded = app.prefs.get(Constants.PREF_LOADED_LIST, null);
if (previouslyLoaded != null) {
sequenceChooser.setCurrentDirectory(new File(previouslyLoaded));
}
sequenceChooser.setFileSelectionMode(JFileChooser.FILES_ONLY);
sequenceChooser.setFileFilter(MPLFilter);
int returnVal;
if(direction) {returnVal = sequenceChooser.showOpenDialog(this);}
else {returnVal = sequenceChooser.showSaveDialog(this);}
if (returnVal == JFileChooser.APPROVE_OPTION) {
File returnFile = sequenceChooser.getSelectedFile();
app.prefs.put(Constants.PREF_LOADED_LIST, sequenceChooser.getSelectedFile().getPath());
if(!direction) { //Make sure file ends in appropriate extension
if(!returnFile.getAbsolutePath().endsWith(".mpl")) {returnFile = new File(sequenceChooser.getSelectedFile() + ".mpl");}
}
return returnFile;
}
return null;
}
private boolean savePlaylist(File f) {
if(playlist.savePlaylistFile(f)) {}
else {return false;}
return true;
}
private boolean loadPlaylist(File f) {
if(playlist.loadPlaylistFile(f)) {
app.prefs.put(Constants.PREF_LOADED_MPL, f.getPath());
if (isConnected && !playlist.isEmpty()) {startButton.setEnabled(true);}
}
else {return false;}
return true;
}
private void setupPlaylistTable() {
playlistTable.setModel(playlist);
playlistTable.setAutoResizeMode(JTable.AUTO_RESIZE_LAST_COLUMN);
TableColumnModel ColumnModel = playlistTable.getColumnModel();
ColumnModel.getColumn(0).setResizable(false);
ColumnModel.getColumn(1).setResizable(false);
ColumnModel.getColumn(0).setPreferredWidth(25);
ColumnModel.getColumn(0).setMaxWidth(25);
ColumnModel.getColumn(1).setPreferredWidth(55);
ColumnModel.getColumn(1).setMaxWidth(55);
}// </editor-fold>
}

View File

@ -0,0 +1,250 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<NonVisualComponents>
<Component class="javax.swing.ButtonGroup" name="poolStratBG">
</Component>
</NonVisualComponents>
<Properties>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.EtchedBorderInfo">
<EtchetBorder/>
</Border>
</Property>
<Property name="preferredSize" type="java.awt.Dimension" editor="org.netbeans.beaninfo.editors.DimensionEditor">
<Dimension value="[525, 149]"/>
</Property>
</Properties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="enablePoolingCB" min="-2" max="-2" attributes="0"/>
<Component id="poolingChannelPannel" min="-2" pref="211" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="poolStratPanel" min="-2" max="-2" attributes="0"/>
<EmptySpace pref="187" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="enablePoolingCB" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="poolingChannelPannel" max="32767" attributes="0"/>
</Group>
<Component id="poolStratPanel" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JCheckBox" name="enablePoolingCB">
<Properties>
<Property name="text" type="java.lang.String" value="Enable Drive-Pooling"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="enablePoolingCBActionPerformed"/>
</Events>
</Component>
<Container class="javax.swing.JPanel" name="poolingChannelPannel">
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jLabel1" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="inputStartSpinner" min="-2" pref="41" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="inputEndSpinner" min="-2" pref="41" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jLabel2" alignment="0" min="-2" max="-2" attributes="0"/>
<Group type="102" alignment="0" attributes="0">
<Component id="outputStartSpinner" min="-2" pref="41" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="outputEndSpinner" min="-2" pref="41" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="1" attributes="0">
<Group type="102" alignment="1" attributes="0">
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="inputStartSpinner" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="inputEndSpinner" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
<Group type="102" alignment="1" attributes="0">
<Component id="jLabel2" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="outputStartSpinner" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="outputEndSpinner" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
<EmptySpace max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="jLabel1">
<Properties>
<Property name="text" type="java.lang.String" value="Pool channels:"/>
</Properties>
</Component>
<Component class="javax.swing.JSpinner" name="inputStartSpinner">
<Properties>
<Property name="model" type="javax.swing.SpinnerModel" editor="org.netbeans.modules.form.editors2.SpinnerModelEditor">
<SpinnerModel initial="1" maximum="16" minimum="1" numberType="java.lang.Integer" stepSize="1" type="number"/>
</Property>
</Properties>
<Events>
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="poolingControlChanged"/>
</Events>
</Component>
<Component class="javax.swing.JSpinner" name="inputEndSpinner">
<Properties>
<Property name="model" type="javax.swing.SpinnerModel" editor="org.netbeans.modules.form.editors2.SpinnerModelEditor">
<SpinnerModel initial="1" maximum="16" minimum="1" numberType="java.lang.Integer" stepSize="1" type="number"/>
</Property>
</Properties>
<Events>
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="poolingControlChanged"/>
</Events>
</Component>
<Component class="javax.swing.JSpinner" name="outputStartSpinner">
<Properties>
<Property name="model" type="javax.swing.SpinnerModel" editor="org.netbeans.modules.form.editors2.SpinnerModelEditor">
<SpinnerModel initial="1" maximum="16" minimum="1" numberType="java.lang.Integer" stepSize="1" type="number"/>
</Property>
</Properties>
<Events>
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="poolingControlChanged"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="jLabel2">
<Properties>
<Property name="text" type="java.lang.String" value="Into channels:"/>
</Properties>
</Component>
<Component class="javax.swing.JSpinner" name="outputEndSpinner">
<Properties>
<Property name="model" type="javax.swing.SpinnerModel" editor="org.netbeans.modules.form.editors2.SpinnerModelEditor">
<SpinnerModel initial="1" maximum="16" minimum="1" numberType="java.lang.Integer" stepSize="1" type="number"/>
</Property>
</Properties>
<Events>
<EventHandler event="stateChanged" listener="javax.swing.event.ChangeListener" parameters="javax.swing.event.ChangeEvent" handler="poolingControlChanged"/>
</Events>
</Component>
</SubComponents>
</Container>
<Container class="javax.swing.JPanel" name="poolStratPanel">
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jLabel3" min="-2" max="-2" attributes="0"/>
<Component id="straightThroughRB" min="-2" max="-2" attributes="0"/>
<Component id="rndRbnRB" min="-2" max="-2" attributes="0"/>
<Component id="stkRB" min="-2" max="-2" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Component id="jLabel3" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="straightThroughRB" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="rndRbnRB" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="stkRB" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="15" max="32767" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="jLabel3">
<Properties>
<Property name="text" type="java.lang.String" value="Using strategy:"/>
</Properties>
</Component>
<Component class="javax.swing.JRadioButton" name="straightThroughRB">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="poolStratBG"/>
</Property>
<Property name="text" type="java.lang.String" value="Straight-Through"/>
<Property name="toolTipText" type="java.lang.String" value="Essentially does nothing. Mostly here for testing."/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="poolingStratChanged"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="rndRbnRB">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="poolStratBG"/>
</Property>
<Property name="selected" type="boolean" value="true"/>
<Property name="text" type="java.lang.String" value="Round-Robin"/>
<Property name="toolTipText" type="java.lang.String" value="Outputs to each channel in a round-robin fashion, attempting to find empty channels first."/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="poolingStratChanged"/>
</Events>
</Component>
<Component class="javax.swing.JRadioButton" name="stkRB">
<Properties>
<Property name="buttonGroup" type="javax.swing.ButtonGroup" editor="org.netbeans.modules.form.RADComponent$ButtonGroupPropertyEditor">
<ComponentRef name="poolStratBG"/>
</Property>
<Property name="text" type="java.lang.String" value="Stacking"/>
<Property name="toolTipText" type="java.lang.String" value="Uses first available output channel (drops notes if all drives are busy)"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="poolingStratChanged"/>
</Events>
</Component>
</SubComponents>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,337 @@
package moppydesk.ui;
import java.awt.Component;
import java.util.Enumeration;
import javax.swing.AbstractButton;
import javax.swing.JRadioButton;
import moppydesk.Constants;
import moppydesk.MoppyUI;
import moppydesk.midputs.DrivePooler;
/**
*
* @author Sammy1Am
*/
public class PoolingControls extends javax.swing.JPanel {
private DrivePooler pooler;
private MoppyUI app;
/**
* This constructor is only for use in the NetBeans editor window. The constructor
* below that takes a {@link MoppyUI} as an argument should always be used.
*/
public PoolingControls() {
initComponents();
}
public PoolingControls(MoppyUI app) {
this.app = app;
initComponents();
pooler = new DrivePooler();
loadSettings();
updatePooler();
setControlsEnabled(enablePoolingCB.isSelected());
}
public boolean isPoolingEnabled() {
return enablePoolingCB.isSelected();
}
public DrivePooler getDrivePooler() {
return pooler;
}
public void connected() {
saveSettings();
for (Component c : this.getComponents()) {
c.setEnabled(false);
setControlsEnabled(false);
}
}
public void disconnected() {
pooler.close();
for (Component c : this.getComponents()) {
c.setEnabled(true);
setControlsEnabled(enablePoolingCB.isSelected());
}
}
private void loadSettings() {
enablePoolingCB.setSelected(app.prefs.getBoolean(Constants.PREF_POOL_ENABLE, false));
inputStartSpinner.setValue(app.prefs.getInt(Constants.PREF_POOL_FROM_START, 1));
inputEndSpinner.setValue(app.prefs.getInt(Constants.PREF_POOL_FROM_END, 1));
outputStartSpinner.setValue(app.prefs.getInt(Constants.PREF_POOL_TO_START, 1));
outputEndSpinner.setValue(app.prefs.getInt(Constants.PREF_POOL_TO_END, 1));
switch (app.prefs.getInt(Constants.PREF_POOL_STRATEGY, 0)){
case 0:
straightThroughRB.setSelected(true);
break;
case 1:
rndRbnRB.setSelected(true);
break;
case 2:
stkRB.setSelected(true);
break;
}
updatePooler();
}
private void saveSettings() {
app.prefs.putBoolean(Constants.PREF_POOL_ENABLE, enablePoolingCB.isSelected());
app.prefs.putInt(Constants.PREF_POOL_FROM_START, (Integer) inputStartSpinner.getValue());
app.prefs.putInt(Constants.PREF_POOL_FROM_END, (Integer) inputEndSpinner.getValue());
app.prefs.putInt(Constants.PREF_POOL_TO_START, (Integer) outputStartSpinner.getValue());
app.prefs.putInt(Constants.PREF_POOL_TO_END, (Integer) outputEndSpinner.getValue());
app.prefs.putInt(Constants.PREF_POOL_STRATEGY, getSelectedStrat().ordinal());
app.savePreferences();
}
private void updatePooler() {
pooler.setInputRange((Integer) inputStartSpinner.getValue(), (Integer) inputEndSpinner.getValue());
pooler.setOutputRange((Integer) outputStartSpinner.getValue(), (Integer) outputEndSpinner.getValue());
pooler.setStrategy(getSelectedStrat());
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
poolStratBG = new javax.swing.ButtonGroup();
enablePoolingCB = new javax.swing.JCheckBox();
poolingChannelPannel = new javax.swing.JPanel();
jLabel1 = new javax.swing.JLabel();
inputStartSpinner = new javax.swing.JSpinner();
inputEndSpinner = new javax.swing.JSpinner();
outputStartSpinner = new javax.swing.JSpinner();
jLabel2 = new javax.swing.JLabel();
outputEndSpinner = new javax.swing.JSpinner();
poolStratPanel = new javax.swing.JPanel();
jLabel3 = new javax.swing.JLabel();
straightThroughRB = new javax.swing.JRadioButton();
rndRbnRB = new javax.swing.JRadioButton();
stkRB = new javax.swing.JRadioButton();
setBorder(javax.swing.BorderFactory.createEtchedBorder());
setPreferredSize(new java.awt.Dimension(525, 149));
enablePoolingCB.setText("Enable Drive-Pooling");
enablePoolingCB.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
enablePoolingCBActionPerformed(evt);
}
});
jLabel1.setText("Pool channels:");
inputStartSpinner.setModel(new javax.swing.SpinnerNumberModel(1, 1, 16, 1));
inputStartSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
poolingControlChanged(evt);
}
});
inputEndSpinner.setModel(new javax.swing.SpinnerNumberModel(1, 1, 16, 1));
inputEndSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
poolingControlChanged(evt);
}
});
outputStartSpinner.setModel(new javax.swing.SpinnerNumberModel(1, 1, 16, 1));
outputStartSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
poolingControlChanged(evt);
}
});
jLabel2.setText("Into channels:");
outputEndSpinner.setModel(new javax.swing.SpinnerNumberModel(1, 1, 16, 1));
outputEndSpinner.addChangeListener(new javax.swing.event.ChangeListener() {
public void stateChanged(javax.swing.event.ChangeEvent evt) {
poolingControlChanged(evt);
}
});
javax.swing.GroupLayout poolingChannelPannelLayout = new javax.swing.GroupLayout(poolingChannelPannel);
poolingChannelPannel.setLayout(poolingChannelPannelLayout);
poolingChannelPannelLayout.setHorizontalGroup(
poolingChannelPannelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(poolingChannelPannelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(poolingChannelPannelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jLabel1)
.addGroup(poolingChannelPannelLayout.createSequentialGroup()
.addComponent(inputStartSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 41, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(inputEndSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 41, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addGap(18, 18, 18)
.addGroup(poolingChannelPannelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jLabel2)
.addGroup(poolingChannelPannelLayout.createSequentialGroup()
.addComponent(outputStartSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 41, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(outputEndSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, 41, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
poolingChannelPannelLayout.setVerticalGroup(
poolingChannelPannelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(poolingChannelPannelLayout.createSequentialGroup()
.addContainerGap()
.addGroup(poolingChannelPannelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(poolingChannelPannelLayout.createSequentialGroup()
.addComponent(jLabel1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(poolingChannelPannelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(inputStartSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(inputEndSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addGroup(poolingChannelPannelLayout.createSequentialGroup()
.addComponent(jLabel2)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(poolingChannelPannelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(outputStartSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(outputEndSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE))))
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
);
jLabel3.setText("Using strategy:");
poolStratBG.add(straightThroughRB);
straightThroughRB.setText("Straight-Through");
straightThroughRB.setToolTipText("Essentially does nothing. Mostly here for testing.");
straightThroughRB.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
poolingStratChanged(evt);
}
});
poolStratBG.add(rndRbnRB);
rndRbnRB.setSelected(true);
rndRbnRB.setText("Round-Robin");
rndRbnRB.setToolTipText("Outputs to each channel in a round-robin fashion, attempting to find empty channels first.");
rndRbnRB.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
poolingStratChanged(evt);
}
});
poolStratBG.add(stkRB);
stkRB.setText("Stacking");
stkRB.setToolTipText("Uses first available output channel (drops notes if all drives are busy)");
stkRB.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
poolingStratChanged(evt);
}
});
javax.swing.GroupLayout poolStratPanelLayout = new javax.swing.GroupLayout(poolStratPanel);
poolStratPanel.setLayout(poolStratPanelLayout);
poolStratPanelLayout.setHorizontalGroup(
poolStratPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jLabel3)
.addComponent(straightThroughRB)
.addComponent(rndRbnRB)
.addComponent(stkRB)
);
poolStratPanelLayout.setVerticalGroup(
poolStratPanelLayout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(poolStratPanelLayout.createSequentialGroup()
.addComponent(jLabel3)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(straightThroughRB)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(rndRbnRB)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(stkRB)
.addGap(0, 15, Short.MAX_VALUE))
);
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(enablePoolingCB)
.addComponent(poolingChannelPannel, javax.swing.GroupLayout.PREFERRED_SIZE, 211, javax.swing.GroupLayout.PREFERRED_SIZE))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(poolStratPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addContainerGap(187, Short.MAX_VALUE))
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(enablePoolingCB)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(poolingChannelPannel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addComponent(poolStratPanel, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)))
);
}// </editor-fold>//GEN-END:initComponents
private void poolingControlChanged(javax.swing.event.ChangeEvent evt) {//GEN-FIRST:event_poolingControlChanged
updatePooler();
}//GEN-LAST:event_poolingControlChanged
private void enablePoolingCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_enablePoolingCBActionPerformed
setControlsEnabled(enablePoolingCB.isSelected());
}//GEN-LAST:event_enablePoolingCBActionPerformed
private void setControlsEnabled(boolean enabled){
for (Component c : poolingChannelPannel.getComponents()){
c.setEnabled(enabled);
}
for (Component c : poolStratPanel.getComponents()){
c.setEnabled(enabled);
}
}
private void poolingStratChanged(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_poolingStratChanged
updatePooler();
}//GEN-LAST:event_poolingStratChanged
private DrivePooler.PoolingStrategy getSelectedStrat(){
if (stkRB.isSelected()){
return DrivePooler.PoolingStrategy.STACK;
} else if (rndRbnRB.isSelected()){
return DrivePooler.PoolingStrategy.ROUND_ROBIN;
} else {
return DrivePooler.PoolingStrategy.STRAIGHT_THROUGH;
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JCheckBox enablePoolingCB;
private javax.swing.JSpinner inputEndSpinner;
private javax.swing.JSpinner inputStartSpinner;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel2;
private javax.swing.JLabel jLabel3;
private javax.swing.JSpinner outputEndSpinner;
private javax.swing.JSpinner outputStartSpinner;
private javax.swing.ButtonGroup poolStratBG;
private javax.swing.JPanel poolStratPanel;
private javax.swing.JPanel poolingChannelPannel;
private javax.swing.JRadioButton rndRbnRB;
private javax.swing.JRadioButton stkRB;
private javax.swing.JRadioButton straightThroughRB;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,227 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.5" maxVersion="1.8" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace min="-2" pref="110" max="-2" attributes="0"/>
<Component id="bpmLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="10" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Component id="sequenceNameLabel" max="32767" attributes="0"/>
</Group>
<EmptySpace type="separate" max="-2" attributes="0"/>
<Component id="loadButton" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<EmptySpace min="10" pref="10" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<Group type="103" groupAlignment="1" attributes="0">
<Group type="102" attributes="0">
<Component id="jLabel2" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="DelayResetSpinner" min="-2" max="-2" attributes="0"/>
<EmptySpace type="unrelated" max="-2" attributes="0"/>
<Component id="ResetDrivesCB" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="RepeatCB" min="-2" max="-2" attributes="0"/>
</Group>
<Group type="102" attributes="0">
<Component id="currentPositionLabel" min="-2" pref="30" max="-2" attributes="0"/>
<EmptySpace min="4" pref="4" max="-2" attributes="0"/>
<Component id="sequenceProgressSlider" min="-2" pref="376" max="-2" attributes="0"/>
<EmptySpace min="4" pref="4" max="-2" attributes="0"/>
<Component id="totalPositionLabel" min="-2" pref="30" max="-2" attributes="0"/>
</Group>
</Group>
<EmptySpace min="0" pref="0" max="32767" attributes="0"/>
</Group>
<Group type="102" alignment="0" attributes="0">
<Component id="jSlider1" min="-2" pref="267" max="-2" attributes="0"/>
<EmptySpace min="18" pref="18" max="-2" attributes="0"/>
<Component id="startButton" min="-2" pref="76" max="-2" attributes="0"/>
<EmptySpace max="32767" attributes="0"/>
<Component id="stopButton" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<EmptySpace min="11" pref="11" max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" attributes="0">
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
<EmptySpace min="-2" pref="46" max="-2" attributes="0"/>
</Group>
<Group type="102" alignment="1" attributes="0">
<Group type="103" groupAlignment="3" attributes="0">
<Component id="loadButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="sequenceNameLabel" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="bpmLabel" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jSlider1" min="-2" max="-2" attributes="0"/>
<Group type="102" attributes="0">
<EmptySpace min="8" pref="8" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="startButton" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="stopButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
<EmptySpace min="-2" pref="8" max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="RepeatCB" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="ResetDrivesCB" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="DelayResetSpinner" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="jLabel2" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="currentPositionLabel" min="-2" pref="23" max="-2" attributes="0"/>
<Component id="sequenceProgressSlider" min="-2" max="-2" attributes="0"/>
<Component id="totalPositionLabel" min="-2" pref="23" max="-2" attributes="0"/>
</Group>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="jLabel1">
<Properties>
<Property name="text" type="java.lang.String" value="Current Sequence:"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="sequenceNameLabel">
<Properties>
<Property name="text" type="java.lang.String" value="&lt;None loaded&gt;"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="bpmLabel">
<Properties>
<Property name="text" type="java.lang.String" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="jSlider1.getValue() + &quot; bpm&quot;" type="code"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JSlider" name="jSlider1">
<Properties>
<Property name="majorTickSpacing" type="int" value="10"/>
<Property name="maximum" type="int" value="310"/>
<Property name="minimum" type="int" value="20"/>
<Property name="paintTicks" type="boolean" value="true"/>
<Property name="value" type="int" value="120"/>
</Properties>
<Events>
<EventHandler event="mouseDragged" listener="java.awt.event.MouseMotionListener" parameters="java.awt.event.MouseEvent" handler="jSlider1tempoSliderChanged"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="startButton">
<Properties>
<Property name="text" type="java.lang.String" value="Start"/>
<Property name="enabled" type="boolean" value="false"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="startButtonClicked"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="stopButton">
<Properties>
<Property name="text" type="java.lang.String" value="Stop/Reset"/>
<Property name="toolTipText" type="java.lang.String" value="Press once to stop sequencer and return to beginning of track. Press again to reset drives."/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="stopButtonstopResetClicked"/>
</Events>
</Component>
<Component class="javax.swing.JButton" name="loadButton">
<Properties>
<Property name="text" type="java.lang.String" value="Load Sequence"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="loadButtonloadSequence"/>
</Events>
</Component>
<Component class="javax.swing.JSlider" name="sequenceProgressSlider">
<Properties>
<Property name="toolTipText" type="java.lang.String" value=""/>
<Property name="value" type="int" value="0"/>
</Properties>
<Events>
<EventHandler event="mouseClicked" listener="java.awt.event.MouseListener" parameters="java.awt.event.MouseEvent" handler="sequenceProgressDragged"/>
<EventHandler event="mouseDragged" listener="java.awt.event.MouseMotionListener" parameters="java.awt.event.MouseEvent" handler="sequenceProgressDragged"/>
</Events>
</Component>
<Component class="javax.swing.JLabel" name="currentPositionLabel">
<Properties>
<Property name="text" type="java.lang.String" value="00:00"/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="totalPositionLabel">
<Properties>
<Property name="text" type="java.lang.String" value="00:00"/>
</Properties>
</Component>
<Component class="javax.swing.JCheckBox" name="RepeatCB">
<Properties>
<Property name="text" type="java.lang.String" value="Repeat"/>
<Property name="toolTipText" type="java.lang.String" value="Repeats the song when selected."/>
</Properties>
</Component>
<Component class="javax.swing.JCheckBox" name="ResetDrivesCB">
<Properties>
<Property name="text" type="java.lang.String" value="Reset Drives"/>
<Property name="toolTipText" type="java.lang.String" value="Resets the drives after the end of the current song."/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="ResetDrivesCBActionPerformed"/>
</Events>
</Component>
<Component class="javax.swing.JSpinner" name="DelayResetSpinner">
<Properties>
<Property name="model" type="javax.swing.SpinnerModel" editor="org.netbeans.modules.form.editors2.SpinnerModelEditor">
<SpinnerModel initial="0" maximum="600" minimum="0" numberType="java.lang.Integer" stepSize="1" type="number"/>
</Property>
<Property name="toolTipText" type="java.lang.String" value="Delays resetting the drives after the song ends by a number of seconds."/>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="jLabel2">
<Properties>
<Property name="text" type="java.lang.String" value="Delay Reset:"/>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,433 @@
package moppydesk.ui;
import java.awt.Component;
import moppydesk.inputs.MoppySequencer;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.io.File;
import java.util.concurrent.TimeUnit;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.sound.midi.Transmitter;
import javax.swing.JFileChooser;
import javax.swing.JOptionPane;
import javax.swing.JSlider;
import javax.swing.Timer;
import moppydesk.*;
/**
*
* @author Sammy1Am
*/
public final class SequencerControls extends InputPanel implements MoppyStatusConsumer {
MoppySequencer seq;
MoppyControlWindow controlWindow;
MoppyUI app;
final JFileChooser sequenceChooser = new JFileChooser();
Timer progressTimer;
private boolean isConnected = false;
private boolean fileLoaded = false;
/**
* Creates new form SequencerControls
*/
public SequencerControls(MoppyUI app, MoppyControlWindow mcw, MoppySequencer newSequencer) {
this.seq = newSequencer;
this.app = app;
this.controlWindow = mcw;
initComponents();
progressTimer = new Timer(1000, new ActionListener() {
public void actionPerformed(ActionEvent e) {
updateProgressDisplay();
}
});
}
@Override
public void loadPreferences() {
ResetDrivesCB.setSelected(app.prefs.getBoolean(Constants.PREF_RESET_DRIVES, false));
RepeatCB.setSelected(app.prefs.getBoolean(Constants.PREF_REPEAT_SEQ, false));
DelayResetSpinner.setValue(app.prefs.getInt(Constants.PREF_DELAY_RESET, 0));
DelayResetSpinner.setEnabled(ResetDrivesCB.isSelected());
}
@Override
public void savePreferences() {
app.prefs.putBoolean(Constants.PREF_RESET_DRIVES, ResetDrivesCB.isSelected());
app.prefs.putBoolean(Constants.PREF_REPEAT_SEQ, RepeatCB.isSelected());
app.prefs.putInt(Constants.PREF_DELAY_RESET, (Integer)DelayResetSpinner.getValue());
}
private void updateProgressDisplay() {
long currentSeconds = seq.getSecondsPosition();
sequenceProgressSlider.setValue((int) (currentSeconds));
String currentPosition = String.format("%d:%02d",
TimeUnit.SECONDS.toMinutes(currentSeconds),
currentSeconds % 60);
String totalPosition = String.format("%d:%02d",
TimeUnit.SECONDS.toMinutes(seq.getSecondsLength()),
seq.getSecondsLength() % 60);
currentPositionLabel.setText(currentPosition);
totalPositionLabel.setText(totalPosition);
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jLabel1 = new javax.swing.JLabel();
sequenceNameLabel = new javax.swing.JLabel();
bpmLabel = new javax.swing.JLabel();
jSlider1 = new javax.swing.JSlider();
startButton = new javax.swing.JButton();
stopButton = new javax.swing.JButton();
loadButton = new javax.swing.JButton();
sequenceProgressSlider = new javax.swing.JSlider();
currentPositionLabel = new javax.swing.JLabel();
totalPositionLabel = new javax.swing.JLabel();
RepeatCB = new javax.swing.JCheckBox();
ResetDrivesCB = new javax.swing.JCheckBox();
DelayResetSpinner = new javax.swing.JSpinner();
jLabel2 = new javax.swing.JLabel();
jLabel1.setText("Current Sequence:");
sequenceNameLabel.setText("<None loaded>");
bpmLabel.setText(jSlider1.getValue() + " bpm");
jSlider1.setMajorTickSpacing(10);
jSlider1.setMaximum(310);
jSlider1.setMinimum(20);
jSlider1.setPaintTicks(true);
jSlider1.setValue(120);
jSlider1.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
public void mouseDragged(java.awt.event.MouseEvent evt) {
jSlider1tempoSliderChanged(evt);
}
});
startButton.setText("Start");
startButton.setEnabled(false);
startButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
startButtonClicked(evt);
}
});
stopButton.setText("Stop/Reset");
stopButton.setToolTipText("Press once to stop sequencer and return to beginning of track. Press again to reset drives.");
stopButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
stopButtonstopResetClicked(evt);
}
});
loadButton.setText("Load Sequence");
loadButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
loadButtonloadSequence(evt);
}
});
sequenceProgressSlider.setToolTipText("");
sequenceProgressSlider.setValue(0);
sequenceProgressSlider.addMouseListener(new java.awt.event.MouseAdapter() {
public void mouseClicked(java.awt.event.MouseEvent evt) {
sequenceProgressDragged(evt);
}
});
sequenceProgressSlider.addMouseMotionListener(new java.awt.event.MouseMotionAdapter() {
public void mouseDragged(java.awt.event.MouseEvent evt) {
sequenceProgressDragged(evt);
}
});
currentPositionLabel.setText("00:00");
totalPositionLabel.setText("00:00");
RepeatCB.setText("Repeat");
RepeatCB.setToolTipText("Repeats the song when selected.");
ResetDrivesCB.setText("Reset Drives");
ResetDrivesCB.setToolTipText("Resets the drives after the end of the current song.");
ResetDrivesCB.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
ResetDrivesCBActionPerformed(evt);
}
});
DelayResetSpinner.setModel(new javax.swing.SpinnerNumberModel(0, 0, 600, 1));
DelayResetSpinner.setToolTipText("Delays resetting the drives after the song ends by a number of seconds.");
jLabel2.setText("Delay Reset:");
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(110, 110, 110)
.addComponent(bpmLabel)
.addContainerGap(javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(10, 10, 10)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(jLabel1)
.addGap(0, 0, Short.MAX_VALUE))
.addComponent(sequenceNameLabel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE))
.addGap(18, 18, 18)
.addComponent(loadButton))
.addGroup(layout.createSequentialGroup()
.addGap(10, 10, 10)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.TRAILING)
.addGroup(layout.createSequentialGroup()
.addComponent(jLabel2)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(DelayResetSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.UNRELATED)
.addComponent(ResetDrivesCB)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(RepeatCB))
.addGroup(layout.createSequentialGroup()
.addComponent(currentPositionLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(4, 4, 4)
.addComponent(sequenceProgressSlider, javax.swing.GroupLayout.PREFERRED_SIZE, 376, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(4, 4, 4)
.addComponent(totalPositionLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 30, javax.swing.GroupLayout.PREFERRED_SIZE)))
.addGap(0, 0, Short.MAX_VALUE))
.addGroup(layout.createSequentialGroup()
.addComponent(jSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, 267, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGap(18, 18, 18)
.addComponent(startButton, javax.swing.GroupLayout.PREFERRED_SIZE, 76, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addComponent(stopButton)))))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addGap(11, 11, 11)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addComponent(jLabel1)
.addGap(46, 46, 46))
.addGroup(javax.swing.GroupLayout.Alignment.TRAILING, layout.createSequentialGroup()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(loadButton)
.addComponent(sequenceNameLabel))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(bpmLabel)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)))
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jSlider1, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addGroup(layout.createSequentialGroup()
.addGap(8, 8, 8)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(startButton)
.addComponent(stopButton))))
.addGap(8, 8, 8)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(RepeatCB)
.addComponent(ResetDrivesCB)
.addComponent(DelayResetSpinner, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabel2))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(currentPositionLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(sequenceProgressSlider, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(totalPositionLabel, javax.swing.GroupLayout.PREFERRED_SIZE, 23, javax.swing.GroupLayout.PREFERRED_SIZE)))
);
}// </editor-fold>//GEN-END:initComponents
private void jSlider1tempoSliderChanged(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_jSlider1tempoSliderChanged
JSlider s = (JSlider) evt.getSource();
seq.setTempo(s.getValue());
bpmLabel.setText(s.getValue() + " bpm");
}//GEN-LAST:event_jSlider1tempoSliderChanged
private void startButtonClicked(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_startButtonClicked
if (startButton.getText().equals("Start")) {
playSequencer();
} else {
pauseSequencer();
}
}//GEN-LAST:event_startButtonClicked
private void playSequencer() {
seq.startSequencer();
seq.setTempo(jSlider1.getValue());
controlWindow.setStatus("Playing!");
startButton.setText("Pause");
}
private void pauseSequencer() {
seq.stopSequencer();
startButton.setText("Start");
controlWindow.setStatus("...Paused");
}
private void stopResetSequencer() {
if (seq.isRunning()) {
stopSeq();
} else {
resetSeq();
}
updateProgressDisplay(); // Always update the progress here in case we're not connected but want to reset the sequencer
}
private void stopSeq(){
controlWindow.setStatus("Stopping...");
seq.stopSequencer();
seq.resetSequencer();
startButton.setText("Start");
controlWindow.setStatus("Stopped.");
}
private void resetSeq(){
app.rm.reset();
seq.resetSequencer();
controlWindow.setStatus("Reset.");
}
private void stopButtonstopResetClicked(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_stopButtonstopResetClicked
stopResetSequencer();
}//GEN-LAST:event_stopButtonstopResetClicked
private void loadButtonloadSequence(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_loadButtonloadSequence
String previouslyLoaded = app.prefs.get(Constants.PREF_LOADED_SEQ, null);
if (previouslyLoaded != null) {
sequenceChooser.setCurrentDirectory(new File(previouslyLoaded));
}
int returnVal = sequenceChooser.showOpenDialog(this);
if (returnVal == JFileChooser.APPROVE_OPTION) {
loadSequenceFile(sequenceChooser.getSelectedFile());
} else {
//Cancelled
}
}//GEN-LAST:event_loadButtonloadSequence
private void sequenceProgressDragged(java.awt.event.MouseEvent evt) {//GEN-FIRST:event_sequenceProgressDragged
int seconds = ((JSlider) evt.getSource()).getValue();
seq.setSecondsPosition(seconds);
currentPositionLabel.setText(String.format("%d:%02d",
TimeUnit.SECONDS.toMinutes(seconds),
seconds % 60));
}//GEN-LAST:event_sequenceProgressDragged
private void ResetDrivesCBActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_ResetDrivesCBActionPerformed
DelayResetSpinner.setEnabled(ResetDrivesCB.isSelected());
}//GEN-LAST:event_ResetDrivesCBActionPerformed
@Override
public void tempoChanged(int newTempo) {
jSlider1.setValue(newTempo);
bpmLabel.setText(newTempo + " bpm");
}
private void loadSequenceFile(File sequenceFile) {
try {
stopSeq();
controlWindow.setStatus("Loading file...");
seq.loadFile(sequenceFile.getPath());
sequenceNameLabel.setText(sequenceFile.getName());
sequenceProgressSlider.setMaximum((int) (seq.getSecondsLength()));
app.prefs.put(Constants.PREF_LOADED_SEQ, sequenceFile.getPath());
fileLoaded = true;
controlWindow.setStatus("Loaded " + sequenceFile.getName());
updateProgressDisplay();
if (isConnected) {
startButton.setEnabled(true);
}
} catch (Exception ex) {
Logger.getLogger(MoppyControlWindow.class.getName()).log(Level.SEVERE, null, ex);
controlWindow.setStatus("File loading error!");
JOptionPane.showMessageDialog(this.getRootPane(), ex);
}
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JSpinner DelayResetSpinner;
private javax.swing.JCheckBox RepeatCB;
private javax.swing.JCheckBox ResetDrivesCB;
private javax.swing.JLabel bpmLabel;
private javax.swing.JLabel currentPositionLabel;
private javax.swing.JLabel jLabel1;
private javax.swing.JLabel jLabel2;
private javax.swing.JSlider jSlider1;
private javax.swing.JButton loadButton;
private javax.swing.JLabel sequenceNameLabel;
private javax.swing.JSlider sequenceProgressSlider;
private javax.swing.JButton startButton;
private javax.swing.JButton stopButton;
private javax.swing.JLabel totalPositionLabel;
// End of variables declaration//GEN-END:variables
@Override
public Transmitter getTransmitter() {
return seq;
}
@Override
public void connected() {
progressTimer.start();
isConnected = true;
if (fileLoaded) {
startButton.setEnabled(true);
}
}
@Override
public void disconnected() {
startButton.setEnabled(false);
pauseSequencer();
isConnected = false;
progressTimer.stop();
seq.setReceiver(null); //Clear receiver so there's no connection here.
}
//MrSolidSnake745: Simple use for the SequenceEnded event
//Resets the sequence and drives if ResetDrivesCB is selected once the song has finished
@Override
public void sequenceEnded() {
controlWindow.setStatus("Song has ended.");
app.rm.silence(); //In case there are any stuck notes, most likely from pooling, silence all receivers
seq.resetSequencer();
startButton.setText("Start");
if(ResetDrivesCB.isSelected()) {
int y = ((Integer)DelayResetSpinner.getValue());
if(y > 0) {
try {
setEnabledAllControls(false); //Don't want users messing with controls while we delay
controlWindow.setStatus("Waiting " + y + " seconds before reset...");
Thread.sleep(y * 1000); //Thread is sooooo sleepy...
}
catch(Exception x){}
finally{setEnabledAllControls(true);}
}
controlWindow.setStatus("Resetting!");
app.rm.reset();
controlWindow.setStatus("Reset.");
}
if(RepeatCB.isSelected()) { playSequencer(); }
}
public void setEnabledAllControls(boolean value) {
for (Component c : this.getComponents()) {c.setEnabled(value);}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 18 KiB

73
Java/MoppySim/build.xml Normal file
View File

@ -0,0 +1,73 @@
<?xml version="1.0" encoding="UTF-8"?>
<!-- You may freely edit this file. See commented blocks below for -->
<!-- some examples of how to customize the build. -->
<!-- (If you delete it and reopen the project it will be recreated.) -->
<!-- By default, only the Clean and Build commands use this build script. -->
<!-- Commands such as Run, Debug, and Test only use this build script if -->
<!-- the Compile on Save feature is turned off for the project. -->
<!-- You can turn off the Compile on Save (or Deploy on Save) setting -->
<!-- in the project's Project Properties dialog box.-->
<project name="MoppySim" default="default" basedir=".">
<description>Builds, tests, and runs the project MoppySim.</description>
<import file="nbproject/build-impl.xml"/>
<!--
There exist several targets which are by default empty and which can be
used for execution of your tasks. These targets are usually executed
before and after some main targets. They are:
-pre-init: called before initialization of project properties
-post-init: called after initialization of project properties
-pre-compile: called before javac compilation
-post-compile: called after javac compilation
-pre-compile-single: called before javac compilation of single file
-post-compile-single: called after javac compilation of single file
-pre-compile-test: called before javac compilation of JUnit tests
-post-compile-test: called after javac compilation of JUnit tests
-pre-compile-test-single: called before javac compilation of single JUnit test
-post-compile-test-single: called after javac compilation of single JUunit test
-pre-jar: called before JAR building
-post-jar: called after JAR building
-post-clean: called after cleaning build products
(Targets beginning with '-' are not intended to be called on their own.)
Example of inserting an obfuscator after compilation could look like this:
<target name="-post-compile">
<obfuscate>
<fileset dir="${build.classes.dir}"/>
</obfuscate>
</target>
For list of available properties check the imported
nbproject/build-impl.xml file.
Another way to customize the build is by overriding existing main targets.
The targets of interest are:
-init-macrodef-javac: defines macro for javac compilation
-init-macrodef-junit: defines macro for junit execution
-init-macrodef-debug: defines macro for class debugging
-init-macrodef-java: defines macro for class execution
-do-jar: JAR building
run: execution of project
-javadoc-build: Javadoc generation
test-report: JUnit report generation
An example of overriding the target for project execution could look like this:
<target name="run" depends="MoppySim-impl.jar">
<exec dir="bin" executable="launcher.exe">
<arg file="${dist.jar}"/>
</exec>
</target>
Notice that the overridden target depends on the jar target and not only on
the compile target as the regular run target does. Again, for a list of available
properties which you can use, check the target you are overriding in the
nbproject/build-impl.xml file.
-->
</project>

View File

@ -0,0 +1,4 @@
#Thu, 23 Jul 2015 19:46:18 +0200
/home/alexander/tmp/SammyIAm/Moppy/Java/MoppySim=

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 765 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

View File

@ -0,0 +1,3 @@
Manifest-Version: 1.0
X-COMMENT: Main-Class will be added automatically by build

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,5 @@
# This file is used by a NetBeans-based IDE to track changes in generated files such as build-impl.xml.
# Do not edit this file. You may delete it but then the IDE will never regenerate such files for you.
nbproject/build-impl.xml.data.CRC32=68eeca74
nbproject/build-impl.xml.script.CRC32=1dbd8490
nbproject/build-impl.xml.stylesheet.CRC32=876e7a8f@1.75.2.48

View File

@ -0,0 +1 @@
user.properties.file=/home/alexander/.netbeans/8.0.2/build.properties

View File

@ -0,0 +1,77 @@
annotation.processing.enabled=true
annotation.processing.enabled.in.editor=false
annotation.processing.processor.options=
annotation.processing.processors.list=
annotation.processing.run.all.processors=true
annotation.processing.source.output=${build.generated.sources.dir}/ap-source-output
build.classes.dir=${build.dir}/classes
build.classes.excludes=**/*.java,**/*.form
# This directory is removed when the project is cleaned:
build.dir=build
build.generated.dir=${build.dir}/generated
build.generated.sources.dir=${build.dir}/generated-sources
# Only compile against the classpath explicitly listed here:
build.sysclasspath=ignore
build.test.classes.dir=${build.dir}/test/classes
build.test.results.dir=${build.dir}/test/results
# Uncomment to specify the preferred debugger connection transport:
#debug.transport=dt_socket
debug.classpath=\
${run.classpath}
debug.test.classpath=\
${run.test.classpath}
# Files in build.classes.dir which should be excluded from distribution jar
dist.archive.excludes=
# This directory is removed when the project is cleaned:
dist.dir=dist
dist.jar=${dist.dir}/MoppySim.jar
dist.javadoc.dir=${dist.dir}/javadoc
excludes=
file.reference.jsyn_16_7_3.jar=lib\\jsyn_16_7_3.jar
file.reference.nrjavaserial-3.9.3.jar=..\\SerialDrivers\\nrjavaserial-3.9.3.jar
includes=**
jar.compress=false
javac.classpath=\
${file.reference.nrjavaserial-3.9.3.jar}:\
${file.reference.jsyn_16_7_3.jar}
# Space-separated list of extra javac options
javac.compilerargs=
javac.deprecation=false
javac.processorpath=\
${javac.classpath}
javac.source=1.7
javac.target=1.7
javac.test.classpath=\
${javac.classpath}:\
${build.classes.dir}
javac.test.processorpath=\
${javac.test.classpath}
javadoc.additionalparam=
javadoc.author=false
javadoc.encoding=${source.encoding}
javadoc.noindex=false
javadoc.nonavbar=false
javadoc.notree=false
javadoc.private=false
javadoc.splitindex=true
javadoc.use=true
javadoc.version=false
javadoc.windowtitle=
main.class=moppysim.ui.MainWindow
manifest.file=manifest.mf
meta.inf.dir=${src.dir}/META-INF
mkdist.disabled=false
platform.active=default_platform
run.classpath=\
${javac.classpath}:\
${build.classes.dir}
# Space-separated list of JVM arguments used when running the project.
# You may also define separate properties like run-sys-prop.name=value instead of -Dname=value.
# To set system properties for unit tests define test-sys-prop.name=value:
run.jvmargs=
run.test.classpath=\
${javac.test.classpath}:\
${build.test.classes.dir}
source.encoding=UTF-8
src.dir=src
test.src.dir=test

View File

@ -0,0 +1,15 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://www.netbeans.org/ns/project/1">
<type>org.netbeans.modules.java.j2seproject</type>
<configuration>
<data xmlns="http://www.netbeans.org/ns/j2se-project/3">
<name>MoppySim</name>
<source-roots>
<root id="src.dir"/>
</source-roots>
<test-roots>
<root id="test.src.dir"/>
</test-roots>
</data>
</configuration>
</project>

View File

@ -0,0 +1,127 @@
package moppysim.comm;
import com.jsyn.JSyn;
import com.jsyn.Synthesizer;
import com.jsyn.unitgen.LineOut;
import com.jsyn.unitgen.Pan;
import gnu.io.NRSerialPort;
import java.io.DataInputStream;
import java.io.IOException;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import moppysim.components.SimDrive;
/**
*
* @author Sammy1Am
*/
public class SimController implements Runnable{
NRSerialPort serial;
static int SERIAL_RATE = 9600;
private boolean running = false;
private SimDrive[] simDrives;
static int ARDUINO_RESOLUTION = 40; // Used to convert incoming data into actual microseconds
private int numberOfDrives;
Synthesizer synth = JSyn.createSynthesizer();
LineOut lout = new LineOut();
public SimController(int numOfDrives){
numberOfDrives = numOfDrives;
simDrives = new SimDrive[numberOfDrives];
// Set up the synthesizer
synth.add(lout);
Pan pan = new Pan();
pan.pan.set(0.0);
pan.output.connect(0,lout.input,0);
pan.output.connect(1,lout.input,1);
for (int d=0;d<numberOfDrives;d++){
SimDrive sd = new SimDrive();
simDrives[d] = sd;
synth.add(sd.so);
sd.so.output.connect(pan.input);
}
}
public SimDrive[] getDrives(){
return simDrives;
}
public static String[] getComPorts(){
Set<String> ports = NRSerialPort.getAvailableSerialPorts();
return ports.toArray(new String[ports.size()]);
}
public void connect(String comPort){
serial = new NRSerialPort(comPort, SERIAL_RATE);
serial.connect();
}
public void disconnect(){
stop();
resetAll();
if (serial != null){
serial.disconnect();
}
}
public void stop(){
this.running = false;
synth.stop();
}
@Override
public void run() {
running = true;
synth.start();
lout.start();
if (serial.isConnected()){
DataInputStream in = new DataInputStream(serial.getInputStream());
byte messageCommand;
int messageData;
while (running && !Thread.currentThread().isInterrupted()){
try {
if (in.available() > 2){
messageCommand = in.readByte();
messageData = in.readUnsignedShort();
if (messageCommand == 100){
resetAll();
} else {
setNote(messageCommand,messageData);
}
} else {
Thread.sleep(5);
}
} catch (IOException ex) {
Logger.getLogger(SimController.class.getName()).log(Level.SEVERE, null, ex);
} catch (InterruptedException ex) {
Logger.getLogger(SimController.class.getName()).log(Level.SEVERE, null, ex);
}
}
}
}
private void resetAll(){
for (SimDrive s : simDrives){
s.setNote(0);
s.resetDrive();
}
}
private void setNote(int driveNumber, int periodData){
if ((driveNumber/2)-1 < numberOfDrives){
simDrives[(driveNumber/2)-1].setNote(periodData * (ARDUINO_RESOLUTION*2));
}
//System.out.println("Set drive "+((driveNumber/2)-1)+" to "+periodData);
}
}

View File

@ -0,0 +1,43 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package moppysim.components;
import com.jsyn.ports.UnitOutputPort;
import com.jsyn.unitgen.SquareOscillator;
import moppysim.ui.DrivePanel;
/**
*
* @author Sam
*/
public class SimDrive {
public SquareOscillator so = new SquareOscillator();
public DrivePanel drivePanel = new DrivePanel();
public SimDrive(){
so.noteOff();
}
public void setNote(int periodData){
if (periodData>0){
so.noteOn(1000000/periodData, 0.05);
drivePanel.playingNote(1000000/periodData);
} else {
so.noteOff();
drivePanel.playingNote(0);
}
}
public void resetDrive(){
drivePanel.resetDrive();
}
public UnitOutputPort getOutput(){
return so.getOutput();
}
}

View File

@ -0,0 +1,42 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.3" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JPanelFormInfo">
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Component id="jLabel1" alignment="0" max="32767" attributes="0"/>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JLabel" name="jLabel1">
<Properties>
<Property name="icon" type="javax.swing.Icon" editor="org.netbeans.modules.form.editors2.IconEditor">
<Image iconType="3" name="/moppysim/ui/floppy_xs.png"/>
</Property>
<Property name="border" type="javax.swing.border.Border" editor="org.netbeans.modules.form.editors2.BorderEditor">
<Border info="org.netbeans.modules.form.compat2.border.LineBorderInfo">
<LineBorder thickness="3"/>
</Border>
</Property>
</Properties>
</Component>
</SubComponents>
</Form>

View File

@ -0,0 +1,71 @@
/*
* To change this license header, choose License Headers in Project Properties.
* To change this template file, choose Tools | Templates
* and open the template in the editor.
*/
package moppysim.ui;
import java.awt.Color;
/**
*
* @author Sam
*/
public class DrivePanel extends javax.swing.JPanel {
double lastFrequency;
/**
* Creates new form DrivePanel
*/
public DrivePanel() {
initComponents();
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
jLabel1 = new javax.swing.JLabel();
jLabel1.setIcon(new javax.swing.ImageIcon(getClass().getResource("/moppysim/ui/floppy_xs.png"))); // NOI18N
jLabel1.setBorder(javax.swing.BorderFactory.createLineBorder(new java.awt.Color(0, 0, 0), 3));
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(this);
this.setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jLabel1)
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(jLabel1, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
);
}// </editor-fold>//GEN-END:initComponents
public void playingNote(double frequency){
if (frequency > 0){
lastFrequency = frequency;
jLabel1.setBorder(javax.swing.BorderFactory.createLineBorder(Color.getHSBColor((float)(frequency/500), 1, 1), 3));
} else if(lastFrequency>0) {
jLabel1.setBorder(javax.swing.BorderFactory.createLineBorder(Color.getHSBColor((float)(lastFrequency/500), 1, (float)0.5), 3));
} else {
jLabel1.setBorder(javax.swing.BorderFactory.createLineBorder(Color.BLACK, 3));
}
}
public void resetDrive(){
lastFrequency = 0;
jLabel1.setBorder(javax.swing.BorderFactory.createLineBorder(Color.BLACK, 3));
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JLabel jLabel1;
// End of variables declaration//GEN-END:variables
}

View File

@ -0,0 +1,89 @@
<?xml version="1.0" encoding="UTF-8" ?>
<Form version="1.3" maxVersion="1.9" type="org.netbeans.modules.form.forminfo.JFrameFormInfo">
<Properties>
<Property name="defaultCloseOperation" type="int" value="3"/>
<Property name="title" type="java.lang.String" value="Moppy Sim"/>
</Properties>
<SyntheticProperties>
<SyntheticProperty name="formSizePolicy" type="int" value="1"/>
<SyntheticProperty name="generateCenter" type="boolean" value="false"/>
</SyntheticProperties>
<AuxValues>
<AuxValue name="FormSettings_autoResourcing" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_autoSetComponentName" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_generateFQN" type="java.lang.Boolean" value="true"/>
<AuxValue name="FormSettings_generateMnemonicsCode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_i18nAutoMode" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_layoutCodeTarget" type="java.lang.Integer" value="1"/>
<AuxValue name="FormSettings_listenerGenerationStyle" type="java.lang.Integer" value="0"/>
<AuxValue name="FormSettings_variablesLocal" type="java.lang.Boolean" value="false"/>
<AuxValue name="FormSettings_variablesModifier" type="java.lang.Integer" value="2"/>
</AuxValues>
<Layout>
<DimensionLayout dim="0">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="0" attributes="0">
<Component id="drivesPanel" max="32767" attributes="0"/>
<Group type="102" attributes="0">
<Component id="jLabel1" min="-2" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="comSelectBox" min="-2" pref="167" max="-2" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
<Component id="connectButton" min="-2" max="-2" attributes="0"/>
<EmptySpace min="0" pref="311" max="32767" attributes="0"/>
</Group>
</Group>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
<DimensionLayout dim="1">
<Group type="103" groupAlignment="0" attributes="0">
<Group type="102" alignment="0" attributes="0">
<EmptySpace max="-2" attributes="0"/>
<Group type="103" groupAlignment="3" attributes="0">
<Component id="comSelectBox" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="jLabel1" alignment="3" min="-2" max="-2" attributes="0"/>
<Component id="connectButton" alignment="3" min="-2" max="-2" attributes="0"/>
</Group>
<EmptySpace max="-2" attributes="0"/>
<Component id="drivesPanel" pref="259" max="32767" attributes="0"/>
<EmptySpace max="-2" attributes="0"/>
</Group>
</Group>
</DimensionLayout>
</Layout>
<SubComponents>
<Component class="javax.swing.JComboBox" name="comSelectBox">
<Properties>
<Property name="model" type="javax.swing.ComboBoxModel" editor="org.netbeans.modules.form.RADConnectionPropertyEditor">
<Connection code="new DefaultComboBoxModel&lt;String&gt;(sc.getComPorts())&#xd;&#xa;" type="code"/>
</Property>
</Properties>
</Component>
<Component class="javax.swing.JLabel" name="jLabel1">
<Properties>
<Property name="text" type="java.lang.String" value="Serial Port:"/>
</Properties>
</Component>
<Component class="javax.swing.JButton" name="connectButton">
<Properties>
<Property name="text" type="java.lang.String" value="Connect"/>
</Properties>
<Events>
<EventHandler event="actionPerformed" listener="java.awt.event.ActionListener" parameters="java.awt.event.ActionEvent" handler="connectButtonActionPerformed"/>
</Events>
</Component>
<Container class="javax.swing.JPanel" name="drivesPanel">
<Properties>
<Property name="name" type="java.lang.String" value="Moppy Sim" noResource="true"/>
</Properties>
<Layout class="org.netbeans.modules.form.compat2.layouts.DesignFlowLayout"/>
</Container>
</SubComponents>
</Form>

View File

@ -0,0 +1,146 @@
package moppysim.ui;
import javax.swing.DefaultComboBoxModel;
import moppysim.comm.SimController;
import moppysim.components.SimDrive;
/**
*
* @author Sammy1Am
*/
public class MainWindow extends javax.swing.JFrame {
SimController sc;
/**
* Creates new form MainWindow
*/
public MainWindow() {
initComponents();
sc = new SimController(8);
for (SimDrive d : sc.getDrives()){
drivesPanel.add(d.drivePanel);
}
}
/**
* This method is called from within the constructor to initialize the form.
* WARNING: Do NOT modify this code. The content of this method is always
* regenerated by the Form Editor.
*/
@SuppressWarnings("unchecked")
// <editor-fold defaultstate="collapsed" desc="Generated Code">//GEN-BEGIN:initComponents
private void initComponents() {
comSelectBox = new javax.swing.JComboBox();
jLabel1 = new javax.swing.JLabel();
connectButton = new javax.swing.JButton();
drivesPanel = new javax.swing.JPanel();
setDefaultCloseOperation(javax.swing.WindowConstants.EXIT_ON_CLOSE);
setTitle("Moppy Sim");
comSelectBox.setModel(new DefaultComboBoxModel<String>(sc.getComPorts())
);
jLabel1.setText("Serial Port:");
connectButton.setText("Connect");
connectButton.addActionListener(new java.awt.event.ActionListener() {
public void actionPerformed(java.awt.event.ActionEvent evt) {
connectButtonActionPerformed(evt);
}
});
drivesPanel.setName("Moppy Sim"); // NOI18N
javax.swing.GroupLayout layout = new javax.swing.GroupLayout(getContentPane());
getContentPane().setLayout(layout);
layout.setHorizontalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addComponent(drivesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, Short.MAX_VALUE)
.addGroup(layout.createSequentialGroup()
.addComponent(jLabel1)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(comSelectBox, javax.swing.GroupLayout.PREFERRED_SIZE, 167, javax.swing.GroupLayout.PREFERRED_SIZE)
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(connectButton)
.addGap(0, 311, Short.MAX_VALUE)))
.addContainerGap())
);
layout.setVerticalGroup(
layout.createParallelGroup(javax.swing.GroupLayout.Alignment.LEADING)
.addGroup(layout.createSequentialGroup()
.addContainerGap()
.addGroup(layout.createParallelGroup(javax.swing.GroupLayout.Alignment.BASELINE)
.addComponent(comSelectBox, javax.swing.GroupLayout.PREFERRED_SIZE, javax.swing.GroupLayout.DEFAULT_SIZE, javax.swing.GroupLayout.PREFERRED_SIZE)
.addComponent(jLabel1)
.addComponent(connectButton))
.addPreferredGap(javax.swing.LayoutStyle.ComponentPlacement.RELATED)
.addComponent(drivesPanel, javax.swing.GroupLayout.DEFAULT_SIZE, 259, Short.MAX_VALUE)
.addContainerGap())
);
pack();
}// </editor-fold>//GEN-END:initComponents
private void connectButtonActionPerformed(java.awt.event.ActionEvent evt) {//GEN-FIRST:event_connectButtonActionPerformed
if (connectButton.getText().equals("Connect")){
connectButton.setText("Disconnect");
comSelectBox.setEnabled(false);
sc.connect((String) comSelectBox.getSelectedItem());
new Thread(sc).start();
} else {
connectButton.setText("Connect");
comSelectBox.setEnabled(true);
sc.stop();
sc.disconnect();
}
}//GEN-LAST:event_connectButtonActionPerformed
/**
* @param args the command line arguments
*/
public static void main(String args[]) {
/* Set the Nimbus look and feel */
//<editor-fold defaultstate="collapsed" desc=" Look and feel setting code (optional) ">
/* If Nimbus (introduced in Java SE 6) is not available, stay with the default look and feel.
* For details see http://download.oracle.com/javase/tutorial/uiswing/lookandfeel/plaf.html
*/
try {
for (javax.swing.UIManager.LookAndFeelInfo info : javax.swing.UIManager.getInstalledLookAndFeels()) {
if ("Nimbus".equals(info.getName())) {
javax.swing.UIManager.setLookAndFeel(info.getClassName());
break;
}
}
} catch (ClassNotFoundException ex) {
java.util.logging.Logger.getLogger(MainWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (InstantiationException ex) {
java.util.logging.Logger.getLogger(MainWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (IllegalAccessException ex) {
java.util.logging.Logger.getLogger(MainWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
} catch (javax.swing.UnsupportedLookAndFeelException ex) {
java.util.logging.Logger.getLogger(MainWindow.class.getName()).log(java.util.logging.Level.SEVERE, null, ex);
}
//</editor-fold>
/* Create and display the form */
java.awt.EventQueue.invokeLater(new Runnable() {
@Override
public void run() {
new MainWindow().setVisible(true);
}
});
}
// Variables declaration - do not modify//GEN-BEGIN:variables
private javax.swing.JComboBox comSelectBox;
private javax.swing.JButton connectButton;
private javax.swing.JPanel drivesPanel;
private javax.swing.JLabel jLabel1;
// End of variables declaration//GEN-END:variables
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 14 KiB

Binary file not shown.

View File

@ -1,57 +0,0 @@
Trent Here. I do those ugly brown pages at rxtx.org. Documentation is not
what I do well :) So please help me when you see problems or something is
confusing.
For more information provided by end users please visit the rxtx wiki at
http://rxtx.qbang.org/wiki. This is also where you can help.
Short Install Instructions
Windows
RXTXcomm.jar goes in \jre\lib\ext (under java)
rxtxSerial.dll goes in \jre\bin
Mac OS X (x86 and ppc) (there is an Installer with the source)
RXTXcomm.jar goes in /Library/Java/Extensions
librxtxSerial.jnilib goes in /Library/Java/Extensions
Run fixperm.sh thats in the directory. Fix perms is in the Mac_OS_X
subdirectory.
Linux (only x86, x86_64, ia64 here but more in the ToyBox)
RXTXcomm.jar goes in /jre/lib/ext (under java)
librxtxSerial.so goes in /jre/lib/[machine type] (i386 for instance)
Make sure the user is in group lock or uucp so lockfiles work.
Solaris (sparc only so far)
RXTXcomm.jar goes in /jre/lib/ext (under java)
librxtxSerial.so goes in /jre/lib/[machine type]
Make sure the user is in group uucp so lockfiles work.
A person is added to group lock or uucp by editing /etc/groups. Distributions
have various tools but this works:
lock:x:54: becomes:
lock:x:53:jarvi,taj
Now jarvi and taj are in group lock.
Also make sure jarvi and taj have read and write permissions on the port.
If there are problems please help each other on the wiki and ask questions
on the mail-list. User contributed changes will be used here in the next
release. If you don't like the documentation, you can improve it.
--
Trent Jarvi
tjarvi@qbang.org

View File

@ -1,4 +0,0 @@
Wed Mar 1 12:05:10 MST 2006
We forgot to update the Mac OS X binary. Previously, it was an old version
(RXTX-2.1-7pre20). This has now been corrected.

View File

@ -1,9 +0,0 @@
Build Date: Sun 12/07/2008 10:45
Compiled on Processor Architecture: AMD64
Compiled on Processor Identifier: Intel64 Family 15 Model 6 Stepping 4, GenuineIntel
Compiled on Processor Level: 15
Compiled on Processor Revision: 0604
Java Virtual Machine:
java version "1.6.0_10"
Java(TM) SE Runtime Environment (build 1.6.0_10-b33)
Java HotSpot(TM) 64-Bit Server VM (build 11.0-b15, mixed mode)

View File

@ -1,33 +0,0 @@
Windows
----------------------------------------------------
Choose your binary build - x64 or x86 (based on which version of
the JVM you are installing to)
NOTE: You MUST match your architecture. You can't install the i386
version on a 64-bit version of the JDK and vice-versa.
For a JDK installation:
Copy RXTXcomm.jar ---> <JAVA_HOME>\jre\lib\ext
Copy rxtxSerial.dll ---> <JAVA_HOME>\jre\bin
Copy rxtxParallel.dll ---> <JAVA_HOME>\jre\bin
Linux
----------------------------------------------------
Choose your binary build - x86_64 or i386 (based on which version of
the JVM you are installing to)
NOTE: You MUST match your architecture. You can't install the i386
version on a 64-bit version of the JDK and vice-versa.
For a JDK installation on architecture=i386
Copy RXTXcomm.jar ---> <JAVA_HOME>/jre/lib/ext
Copy librxtxSerial.so ---> <JAVA_HOME>/jre/lib/i386/
Copy librxtxParallel.so ---> <JAVA_HOME>/jre/lib/i386/
NOTE: For a JDK installation on architecture=x86_64, just change the
i386 to x86_64 above.

View File

@ -1,63 +0,0 @@
Overview
-----------------------------------------------
This package contains a custom binary distribution of
the RXTX serial package for Java.
Courtesy of Cloudhopper, Inc.
http://rxtx.cloudhopper.net/
http://www.cloudhopper.net/opensource/rxtx/
NOTE: If you include my builds in any of your distributions,
please make sure to at least provide a note of thanks to
Cloudhopper in your own ReleaseNotes. For example,
"RXTX binary builds provided as a courtesy of Cloudhopper.
Please see http://rxtx.cloudhopper.net/ for more info."
RXTX is a great package, but they were lacking pre-built
binaries for x64 versions of Windows. I also wanted a
version built explicitly with Microsoft Visual Studio
rather than MinGW.
Please see ReleaseNotes.txt for information about this
specific release.
Customization
-----------------------------------------------
1. I've based my build on recent CVS snapshots. Please
see the ReleaseNotes.txt for information about which
snapshot I based this distribution on.
2. Removed UTS_NAME warning from .c files to match
kernel with the version you compiled against.
3. Changed version in RXTXVersion.jar and in SerialImp.c
to match my release so that I know its compiled via a CVS
snapshot.
win-x86, win-x64, ia64
-----------------------------------------------
Built using Microsoft Visual C++ 2008 - not MinGW. The
x86 and x64 versions are native and do not rely on
any other non-standard windows libraries. Just drop
in the compiled .dlls that are specific to the version
of Java you run. If you installed the 64-bit version
of the JDK, then install the x64 build.
I've tested the x86 and x64 version with Windows 2008,
2003, and Vista SP1.
linux-i386 & linux-x86_64
-----------------------------------------------
Built using CentOS 5.2 and gcc 4.1.2.
Just drop in the compiled .dlls that are specific to
the version of Java you run. If you installed the 64-bit
version of the JDK, then install the x64 build.
I've tested the x86 and x64 versions with x86 and x64
versions of CentOS 5.0 and 5.2.

View File

@ -1,23 +0,0 @@
Release Notes
--------------------------------------------------
Cloudhopper distibution of RXTX serial package for Java.
http://rxtx.cloudhopper.net/
http://www.cloudhopper.net/opensource/rxtx/
2.2-20081207
* Initial distribution
* linux-i386 and linux-x86_64 using CentOS 5.2 and GCC 4.1.2
* win-x86, win-x64, and win-ia64 builds using MSCV 2008
* CVS snapshot of RXTX-2.2 from 12/07/2008
* Please see BuildInfo.txt for more information about the
particular build you downloaded
* All windows builds crash the JVM if a COM port becomes
unavailable. For example, if using a USB-to-serial port
cable and you pull it out while your app is connected to the
port. The linux version throws an exception in this scenario.
Please note that even when compiled using mingw vs. MSVC, the
build will have the same behavior.

View File

@ -1,3 +0,0 @@
Wed Mar 1 12:01:05 MST 2006
rxtxSerial.dll had to be recomopiled to link in missing native methods.

67
README.md Normal file
View File

@ -0,0 +1,67 @@
<pre>
__ ___ ___ __ __
/ |/ /___ ____ ____ __ __/ | ____/ / ______ _____ ________ ____/ /
/ /|_/ / __ \/ __ \/ __ \/ / / / /| |/ __ / | / / __ `/ __ \/ ___/ _ \/ __ /
/ / / / /_/ / /_/ / /_/ / /_/ / ___ / /_/ /| |/ / /_/ / / / / /__/ __/ /_/ /
/_/ /_/\____/ .___/ .___/\__, /_/ |_\__,_/ |___/\__,_/_/ /_/\___/\___/\__,_/
/_/ /_/ /____/
</pre>
by Sammy1Am
Moppy is a **M**usical Fl **oppy** controller program built for the [Arduino Uno](http://arduino.cc/en/Main/ArduinoBoardUno).
This version attempts to improve upon the original Moppy by adding additional functionality including:
- MIDI-IN support
- Per-channel output control
- Support for multiple Arduinos/MIDI devices
- Drive pooling
This document is meant to be a sort of quick-start guide. You can find an FAQ and troubleshooting guide on the [Wiki](https://github.com/SammyIAm/Moppy/wiki).
Installation
------------
The Arduino code requires the TimeOne library available here: http://www.arduino.cc/playground/Code/Timer1
The latest build is using NRJavaSerial, which should include suitable native drives for most systems. If you've previously run an older version of Moppy, you'll need to make sure that the RXTX jar file(s) are **not** being loaded (i.e. not on the classpath, &c.), since the two libraries will conflict if both present.
Upload the included Arduino code to the Arduino of your choice (requires [Arduino IDE](http://arduino.cc/en/Main/Software)), and open up the included Java code in your favorite IDE. This code includes a NetBeans project for your convenience, so you should be able to open the project directly in NetBeans.
Hardware
--------
I built Moppy using an Arduino Uno, though it should work just fine on most Arduinos. The pins are connected in pairs to floppy drives as follows: Even pins (2,4,6...) are connected to each drive's STEP pin, the matching odd pins (3,5,7...) are connected to the each drive's DIRECTION control pin. So the first floppy would be connected to pin 2 & 3, the second floppy to 4 & 5, and so on.
Some pinout information can be found here: http://pinouts.ru/Storage/InternalDisk_pinout.shtml
Make sure you ground the correct drive-select pin, or the drive won't respond to any input (just connect the drive-select pin on the floppy to the pin directly below it). You can tell when you have the right drive selected, because the light on the front of the drive will come on.
Also, it's VERY IMPORTANT that your Arduino is grounded with the drives, or the drives will not register the pulses correctly. To do this, make sure that the GND pin on the Arduino is connected to the odd-numbered pin below the STEP pin on the floppy (i.e. if the STEP pin is 20, connect the Audnio's GND pin to Floppy-pin 19). You might need to do this for the DIRECTION pin as well (I did it for both, but I don't know if it's required).
Configuration and use
---------------------
- Open up the code in [NetBeans](http://netbeans.org) (or your favorite IDE) and run it. Alternatively, you can build the MoppyDesk.jar file and run that directly.
- On the right half of the screen, check the channel output boxes for the number of drives you have connected, select "Moppy", and choose the COM port that you have your Arduino connected to.
- Click the "Load Sequence" button and select a suitable MIDI file (see below).
- Click the "Connect" button to connect the program to the devices specified in the output area.
- Click "Start" to start playback (if all goes well).
- The Stop/Reset button will stop playback and reset the drives.
MIDI file information and guidelines
------------------------------------
- MIDI files should have one MIDI channel for each controller pin on the Arduino. Channel 1 will be sent to pin2, channel 2 to pin4, &c.
- Each drive can only play a single note at a time.
- The software will only attempt to play notes between C1 and B4. Floppy drives don't seem to respond well to notes outside of this range (especially higher).
- Generally shorter notes tend to sound better, as longer notes are marred by the read-heads changing directions repeatedly.
Cross your fingers, and enjoy!
Help / contributions
--------------------
https://github.com/SammyIAm/Moppy

View File

@ -1,42 +0,0 @@
Moppy is a musical floppy controller program built for the Arduino UNO.
--INSTALLATION--
The Arduino code requires the TimeOne library available here: http://www.arduino.cc/playground/Code/Timer1
You will need to follow the directions in the appropriate Java/SerialDrivers folder for your system to install the serial drivers prior to running Moppy.
Upload the included Arduino code to the Arduino of your choice, and open up the included Java code in your favorite IDE.
--HARDWARE--
Moppy was built for Arduino UNO, though it should work just fine on most
Arduinos. The pins are connected in pairs to floppy drives as follows: Even
pins (2,4,6,...,A0,A2) are connected to each drive's STEP pin (pin 20) , the matching
odd pins (3,5,7,...,A1,A3) are connected to the each drive's DIRECTION control pin
(pin 18). So the first floppy would be connected to pin 2 & 3, the second floppy to 4 & 5, and so on.
Make sure you ground the correct drive-select pin (pin 12), or the drive won't respond to any input (just connect the drive-select pin on the floppy to the pin directly below it). You can tell when you have the right drive selected, because the light on the front of the drive will come on.
Also, it's VERY IMPORTANT that your Arduino is grounded with the drives, or
the drives will not register the pulses correctly. To do this, make sure that
the GND pin on the Arduino is connected to any of the odd-numbered pins on the
floppy drive (usually all odd-numbered pins are directly connected anyway, as
can easily be checked with an ohmmeter).
--CONFIGURAITON / USE--
- Open up the code in your favorite IDE and run it. Alternatively, you can build the MoppyDesk.jar file and run that.
- Select the COM port that the Arduino is hooked up to from the "Arduino Port" drop-down. You will need to have this configured before you launch MoppyDesk.
- Click the "Connect" button to create a new Sequencer connected to the specified COM port.
- Click the "Load Sequence" button and select a suitable MIDI file (see below).
- Click "Start" to start playback (if all goes well).
- The Stop/Reset button will stop playback and reset the drives. Pressing "Start" again will resume from where the sequencer left off. You will need to reload the MIDI to start from the beginning.
--MIDI FILE INFORMATION / GUIDELINES--
- MIDI files should have one MIDI channel for each controller pin on the Arduino. Channel 1 will be sent to pin2, channel 2 to pin4, &c.
- Each drive can only play a single note at a time.
- The software will only attempt to play notes between C1 and B4. Floppy drives don't seem to respond well to notes outside of this range (especially higher).
- Generally shorter notes tend to sound better, as longer notes are marred by the read-heads changing directions repeatedly.