Skip to content

Commit

Permalink
start RTP-MIDI support
Browse files Browse the repository at this point in the history
  • Loading branch information
deleolajide committed Nov 27, 2024
1 parent aa5ed5b commit bd768b7
Show file tree
Hide file tree
Showing 56 changed files with 4,744 additions and 17 deletions.
10 changes: 10 additions & 0 deletions classes/jsp/WEB-INF/web.xml
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd"
version="3.1">

<servlet>
<servlet-name>ChordPro2Midi</servlet-name>
<servlet-class>org.ifsoft.chordpro.ChordPro2Midi</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>ChordPro2Midi</servlet-name>
<url-pattern>/cp2midi</url-pattern>
</servlet-mapping>

<filter>
<filter-name>CorsFilter</filter-name>
<filter-class>org.ifsoft.broadcastbox.openfire.CORSFilter</filter-class>
Expand Down
Binary file modified classes/linux-64/broadcast-box
Binary file not shown.
Binary file modified classes/win-64/broadcast-box.exe
Binary file not shown.
4 changes: 2 additions & 2 deletions plugin.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
<description>${project.description}</description>
<version>${project.version}</version>
<licenseType>Apache 2.0</licenseType>
<date>2024-09-31</date>
<minServerVersion>4.8.0</minServerVersion>
<date>2024-10-31</date>
<minServerVersion>4.7.5</minServerVersion>

<adminconsole>
<tab id="sidebar-media-services">
Expand Down
10 changes: 8 additions & 2 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<parent>
<artifactId>plugins</artifactId>
<groupId>org.igniterealtime.openfire</groupId>
<version>4.9.0</version>
<version>4.7.5</version>
</parent>

<groupId>org.igniterealtime.openfire</groupId>
Expand Down Expand Up @@ -114,7 +114,13 @@
<groupId>commons-codec</groupId>
<artifactId>commons-codec</artifactId>
<version>1.15</version>
</dependency>
</dependency>

<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<version>1.18.36</version>
</dependency>

</dependencies>

Expand Down
29 changes: 29 additions & 0 deletions src/java/io/github/leovr/rtipmidi/AppleMidiCommandListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package io.github.leovr.rtipmidi;

import io.github.leovr.rtipmidi.messages.AppleMidiClockSynchronization;
import io.github.leovr.rtipmidi.messages.AppleMidiInvitationRequest;
import io.github.leovr.rtipmidi.model.AppleMidiServer;

import javax.annotation.Nonnull;

public interface AppleMidiCommandListener extends EndSessionListener {

/**
* This method is called for every invitation request.
*
* @param invitation The invitation request
* @param appleMidiServer The origin server of this message
*/
void onMidiInvitation(@Nonnull final AppleMidiInvitationRequest invitation,
@Nonnull final AppleMidiServer appleMidiServer);

/**
* This method is called for every clock synchronization request.
*
* @param clockSynchronization The clock synchronization request
* @param appleMidiServer The origin server of this message
*/
void onClockSynchronization(@Nonnull final AppleMidiClockSynchronization clockSynchronization,
@Nonnull final AppleMidiServer appleMidiServer);

}
16 changes: 16 additions & 0 deletions src/java/io/github/leovr/rtipmidi/AppleMidiMessageListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package io.github.leovr.rtipmidi;

import io.github.leovr.rtipmidi.messages.MidiCommandHeader;
import io.github.leovr.rtipmidi.model.MidiMessage;

public interface AppleMidiMessageListener {

/**
* This method is called when a new MIDI message from the origin server is received
*
* @param midiCommandHeader The MIDI command meta information
* @param message The MIDI message
* @param timestamp Timestamp of this MIDI message
*/
void onMidiMessage(MidiCommandHeader midiCommandHeader, MidiMessage message, final int timestamp);
}
89 changes: 89 additions & 0 deletions src/java/io/github/leovr/rtipmidi/AppleMidiServer.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,89 @@
package io.github.leovr.rtipmidi;

import io.github.leovr.rtipmidi.session.AppleMidiSession;
import io.github.leovr.rtipmidi.session.SessionChangeListener;
import io.github.leovr.rtipmidi.control.AppleMidiControlServer;
import io.github.leovr.rtipmidi.session.AppleMidiSessionServer;
import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

import javax.annotation.Nonnull;

/**
* Main class for the RTP MIDI communication. This class instantiates the {@link AppleMidiControlServer} and the {@link
* AppleMidiSessionServer}. In order to receive midi messages a {@link AppleMidiSession} should be registerd via {@link
* #addAppleMidiSession(AppleMidiSession)}.
*/
@Slf4j
public class AppleMidiServer implements SessionChangeListener {

private static final int DEFAULT_PORT = 50004;
private static final String DEFAULT_NAME = "rtpMIDIJava";
@Getter
private final int port;
private final AppleMidiControlServer controlServer;
private final AppleMidiSessionServer sessionServer;

/**
* Creates a {@link AppleMidiServer} with {@link #DEFAULT_NAME} and {@link #DEFAULT_PORT}
*/
public AppleMidiServer() {
this(DEFAULT_NAME, DEFAULT_PORT);
}

/**
* Creates a new {@link AppleMidiServer} with the given name and port
*
* @param name The name under which the other peers should see this server
* @param port The control port. A session server will be created on the {@code port + 1}
*/
public AppleMidiServer(@Nonnull final String name, final int port) {
this.port = port;
controlServer = new AppleMidiControlServer(name, port);
sessionServer = new AppleMidiSessionServer(name, port + 1);
sessionServer.registerSessionChangeListener(this);
controlServer.registerEndSessionListener(sessionServer);
}

/**
* Add a new {@link AppleMidiSession} to this server
*
* @param session The session to be added
*/
public void addAppleMidiSession(@Nonnull final AppleMidiSession session) {
sessionServer.addAppleMidiSession(session);
}

/**
* Remove the {@link AppleMidiSession} from this server
*
* @param session The session to be removed
*/
public void removeAppleMidiSession(@Nonnull final AppleMidiSession session) {
sessionServer.removeAppleMidiSession(session);
}

@Override
public void onMaxNumberOfSessionsChange(final int maxNumberOfSessions) {
controlServer.setMaxNumberOfSessions(maxNumberOfSessions);
}

/**
* Starts the control server and the session server
*/
public void start() {
sessionServer.start();
controlServer.start();
log.info("AppleMidiServer started");
}

/**
* Stops the session server and the control server
*/
public void stop() {
sessionServer.stopServer();
controlServer.stopServer();
log.info("AppleMidiServer stopped");
}

}
17 changes: 17 additions & 0 deletions src/java/io/github/leovr/rtipmidi/EndSessionListener.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package io.github.leovr.rtipmidi;

import io.github.leovr.rtipmidi.messages.AppleMidiEndSession;

import javax.annotation.Nonnull;

public interface EndSessionListener {

/**
* This method is called when the origin server ends this session
*
* @param appleMidiEndSession The end session request
* @param appleMidiServer The origin server of this message
*/
void onEndSession(@Nonnull AppleMidiEndSession appleMidiEndSession,
@Nonnull io.github.leovr.rtipmidi.model.AppleMidiServer appleMidiServer);
}
40 changes: 40 additions & 0 deletions src/java/io/github/leovr/rtipmidi/JavaxAppleMidiSession.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package io.github.leovr.rtipmidi;

import io.github.leovr.rtipmidi.model.MidiMessage;
import io.github.leovr.rtipmidi.session.AppleMidiSession;
import lombok.AccessLevel;
import lombok.Setter;

/**
* {@link AppleMidiSession} which converts {@link MidiMessage} to {@link javax.sound.midi.MidiMessage}
*/
public abstract class JavaxAppleMidiSession extends AppleMidiSession {

@Setter(AccessLevel.PACKAGE)
private JavaxMidiMessageConverter midiMessageConverter = new JavaxMidiMessageConverter();

@Override
protected void onMidiMessage(final MidiMessage message, final long timestamp) {
onMidiMessage(midiMessageConverter.convert(message), timestamp);
}

/**
* This method is called when a new {@link javax.sound.midi.MidiMessage} is received
*
* @param message The MIDI-message
* @param timestamp The timestamp of this message
*/
protected void onMidiMessage(final javax.sound.midi.MidiMessage message, final long timestamp) {
}

/**
* This method sends a {@link javax.sound.midi.MidiMessage}
*
* @param message The message to deliver
* @param timestamp The timestamp of this message
*/
protected void sendMidiMessage(final javax.sound.midi.MidiMessage message, final long timestamp) {
final MidiMessage midiMessage = midiMessageConverter.convert(message);
sendMidiMessage(midiMessage, timestamp);
}
}
45 changes: 45 additions & 0 deletions src/java/io/github/leovr/rtipmidi/JavaxMidiMessageConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package io.github.leovr.rtipmidi;


import io.github.leovr.rtipmidi.adapter.ShortMessageAdapter;
import io.github.leovr.rtipmidi.adapter.SysexMessageAdapter;
import io.github.leovr.rtipmidi.model.ShortMessage;
import io.github.leovr.rtipmidi.model.SysexMessage;

import javax.sound.midi.MidiMessage;

/**
* Converter class to convert from {@link io.github.leovr.rtipmidi.model.MidiMessage} to {@link MidiMessage}
*/
class JavaxMidiMessageConverter {

MidiMessage convert(final io.github.leovr.rtipmidi.model.MidiMessage message) {
if (message instanceof ShortMessage) {
return handleShortMessage((ShortMessage) message);
} else if (message instanceof SysexMessage) {
return handleSysexMessage((SysexMessage) message);
}
throw new IllegalArgumentException("Message could not be converted");
}

io.github.leovr.rtipmidi.model.MidiMessage convert(final MidiMessage message) {
if (message instanceof javax.sound.midi.ShortMessage) {
final javax.sound.midi.ShortMessage shortMessage = (javax.sound.midi.ShortMessage) message;
return new ShortMessage((byte) shortMessage.getCommand(), (byte) shortMessage.getData1(),
(byte) shortMessage.getData2());
} else if (message instanceof javax.sound.midi.SysexMessage) {
final javax.sound.midi.SysexMessage sysexMessage = (javax.sound.midi.SysexMessage) message;
return new SysexMessage(sysexMessage.getMessage(), sysexMessage.getLength());
}
throw new IllegalArgumentException("Message could not be converted");
}

private MidiMessage handleSysexMessage(final SysexMessage message) {
return new SysexMessageAdapter(message);
}

private MidiMessage handleShortMessage(final ShortMessage message) {
return new ShortMessageAdapter(message);
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package io.github.leovr.rtipmidi;

import io.github.leovr.rtipmidi.error.AppleMidiSessionInstantiationException;
import io.github.leovr.rtipmidi.session.AppleMidiSession;

import javax.annotation.Nonnull;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.Receiver;
import java.util.Collections;
import java.util.List;

/**
* {@link AppleMidiSession} with one or more {@link Receiver} as the receiver(s) of the MIDI messages
*/
public class MidiReceiverAppleMidiSession extends JavaxAppleMidiSession {

private final List<Receiver> receivers;

public MidiReceiverAppleMidiSession(@Nonnull final Receiver receiver) throws
AppleMidiSessionInstantiationException {
this(Collections.singletonList(receiver));
}

public MidiReceiverAppleMidiSession(@Nonnull final List<Receiver> receivers) {
this.receivers = receivers;
}

@Override
protected void onMidiMessage(final MidiMessage message, final long timestamp) {
for (final Receiver receiver : receivers) {
receiver.send(message, timestamp);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package io.github.leovr.rtipmidi;

import io.github.leovr.rtipmidi.error.AppleMidiSessionInstantiationException;
import io.github.leovr.rtipmidi.session.AppleMidiSession;

import javax.annotation.Nonnull;
import javax.sound.midi.MidiMessage;
import javax.sound.midi.Receiver;
import javax.sound.midi.Transmitter;
import java.util.Collections;
import java.util.List;

/**
* {@link AppleMidiSession} with one or more {@link Transmitter} as the sender(s) of the MIDI messages
*/
public class MidiTransmitterAppleMidiSession extends JavaxAppleMidiSession {

private final List<Transmitter> transmitters;

public MidiTransmitterAppleMidiSession(@Nonnull final Transmitter transmitter) throws
AppleMidiSessionInstantiationException {
this(Collections.singletonList(transmitter));
}

public MidiTransmitterAppleMidiSession(@Nonnull final List<Transmitter> transmitters) {
this.transmitters = transmitters;

for (final Transmitter transmitter : transmitters) {
transmitter.setReceiver(new SimpleReceiver());
}
}

private class SimpleReceiver implements Receiver {

@Override
public void send(final MidiMessage message, final long timeStamp) {
sendMidiMessage(message, timeStamp);
}

@Override
public void close() {
}
}
}
13 changes: 13 additions & 0 deletions src/java/io/github/leovr/rtipmidi/adapter/ShortMessageAdapter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package io.github.leovr.rtipmidi.adapter;

import io.github.leovr.rtipmidi.model.ShortMessage;

/**
* Adapter class to make the {@link ShortMessage} compatible with {@link javax.sound.midi.ShortMessage}
*/
public class ShortMessageAdapter extends javax.sound.midi.ShortMessage {

public ShortMessageAdapter(final ShortMessage message) {
super(message.getData());
}
}
Loading

0 comments on commit bd768b7

Please sign in to comment.