Android: How to retrieve MIDI events at the right time? - java

I am trying to retrieve all MIDI events at the right time from a MIDI file, within an Android app.
The following code works on a standard JVM (on my computer), using the javax.sound.midi API.
Sequencer sequencer = MidiSystem.getSequencer();
Sequence sequence = MidiSystem.getSequence(new File(FILENAME));
sequencer.setSequence(sequence);
sequencer.open();
sequencer.getTransmitter().setReceiver(new Receiver()
{
#Override
public void send(MidiMessage message, long timeStamp)
{
System.out.println(Arrays.toString(message.getMessage()));
}
#Override
public void close()
{
}
});
sequencer.start();
Unfortunatelly, javax.sound.* package is not available on Android. A porting for Android is available on Github (https://github.com/kshoji/javax.sound.midi-for-Android) but my code sample above doesn't work (sequencer.getTransmitter() returns null).
Does anyone know how to do that? I didn't found any interesting library (http://www.midi.org/aboutmidi/android.php) for what I want to do.
Thank you.

Related

How to save game data to the cloud using gdx-gamesvcs?

I want to implement Google Play Games Services in my game on the libgdx engine. I tried using gdx-gamesvcs for this. But I am having trouble saving data. I understood from the example that one value is being saved, not the entire state of the game. So I decided to check it out: save and load one value using gsClient.loadGameState and gsClient.saveGameState. I deliberately deleted the game data from the device. But as a result, not only the test value changed, but many others as well. I thought that the state of the entire game is being saved, but the values ​​obtained do not fit into the logic of the game and could not be obtained in it.
How should I use this tool and is it worth it at all, or is it better to use what libgdx itself offers?
Here is a piece of code:
if (gsClient.isSessionActive()) {
try {
gsClient.saveGameState("data", intToByteArray(testValue), 0, null);
} catch (UnsupportedOperationException unsupportedOperationException) {
}
if (gsClient.isSessionActive()) {
try {
gsClient.loadGameState("data", new ILoadGameStateResponseListener() {
#Override
public void gsGameStateLoaded(byte[] gameState) {
if (gameState != null) {
setTestValue(bytesToInt(gameState));
}
}
});
} catch (UnsupportedOperationException unsupportedOperationException) {
}
}
UPD
Yes, saving occurs both to the cloud and to the device, for saving to the device I use Preferences. I have a Google account login button in the game, it works, I have repeatedly seen this standard bar of my account level, which appears at the top when I log in. Everything is set up in the developer console too, I have an id for achievements and leaderboards. In code, I work with the client like this (In the create() method):
public IGameServiceClient gsClient;
if (gsClient == null) {
gsClient = new MockGameServiceClient(1) {
#Override
protected Array<ILeaderBoardEntry> getLeaderboardEntries() {
return null;
}
#Override
protected Array<String> getGameStates() {
return null;
}
#Override
protected byte[] getGameState() {
return new byte[0];
}
#Override
protected Array<IAchievement> getAchievements() {
return null;
}
#Override
protected String getPlayerName() {
return null;
}
};
}
gsClient.setListener(this);
gsClient.resumeSession();
Next is loading.
The exception is not caught, I removed it and everything works as before.
Well, libgdx offers no built-in cloud-save, it is hard to use it for that. :-)
You should in any case save to local AND to cloud, as the cloud is not very fast to load its state.
I can see no problem in your code besides the fact that you swallow an UnsupportedOperationException that is thrown if you did not activate cloud save feature. So the interesting question is: what happens if you don't swallow the exception, and did you intialize GpgsClient with cloud save enabled? Are you really logged in to Gpgs, and is the feature also activated in your developer console?
The main problem was that gameState was null, this arose due to the fact that you had to wait 24 hours after enabling the save function in the developer console, and the advice on clearing the memory of google play games on the test device did not help. After a while gameState began to pass the existing values, but I started having problems with the graphics flow, probably due to the asynchronous loading.

System.exit(0) in Android Wear Watchface?

I've read that using System.exit(0) is frowned upon when it comes to Java and Android, but so far I can find no alternative for what I'm trying to accomplish. To be clear, this is for a Watchface, which is simply a service extending CanvasWatchFaceService. I cannot call finish() in this case. I've also tried stopService and startService with no success.
The issue I'm trying to solve: It's well known that changing timezones on your device will not be reflected on a watchface unless it is restarted. In my testing, I found that System.currentTimeMillis() quite literally does not respond to timezone changes in Android Wear. The watchface must be restarted in order for it to show the correct time after a timezone change.
So I built a system with the following code:
private final BroadcastReceiver timeChangeReceiver = new BroadcastReceiver() {
#Override
public void onReceive(Context context, Intent intent) {
final String action = intent.getAction();
if (!restarting) {
if (action.equals(Intent.ACTION_TIMEZONE_CHANGED)) {
if (upSeconds >= 15) {
System.exit(0);
} else {
restarting = true;
int delay = ((15 - upSeconds) * 1000);
new CountDownTimer(delay, delay) {
#Override
public void onTick(long millisUntilFinished) { }
#Override
public void onFinish() {
System.exit(0);
}
}.start();
}
}
}
}
};
The delay is in case a user triggers a time zone change more frequently than 15 seconds at a time. Android Wear seems to detect system exits that are too frequent and replace the watchface with the "Simple" watchface.
It seems to work great, Android Wear automatically boots the watchface back up on exit. But I would eventually like to put this app on the Google Play Store, so I thought I should make sure I'm not playing with fire here.
I can't believe I went through all that work when the proper solution was so simple. Thanks ianhanniballake for the link!
After looking at the Analog Watchface Sample, I found that all I needed to do was use mCalendar.setTimeZone(TimeZone.getDefault());. In many places I was directly comparing the time in milliseconds fetched with long now = System.currentTimeMillis();, so I simply did a now = mCalendar.getTimeInMillis() to take care of that.
Now the watchface changes time properly when the timezone is changed. I guess the other watchfaces I downloaded did not properly handle this!

Java MIDI Sequencer SyncMode

Hi: I'm trying to get an instance of the MIDI Sequencer sync to an external clock. I did:
S_p = MidiSystem.getSequencer(false);
D2 = MidiSystem.getMidiDevice(MidiSystem.getMidiDeviceInfo()[1]);
S_p.open();
D2.open();
R2=S_p.getReceiver();
T2=D2.getTransmitter();
T2.setReceiver(R2);
but
for(int i=0;i<S_p.getMasterSyncModes().length;i++)
{System.out.println("Available modes are "+i+ " "+S_p.getMasterSyncModes()[i].toString());}
returns
Available modes are 0 Internal Clock
which means this will be useless.
S_p.setMasterSyncMode(Sequencer.SyncMode.MIDI_SYNC);
What am I doing wrong ?
Of course I have confirmation of messages going out of D2 and into another receiver custom written to notify to system.out, and sequencer plays normally, it just appears that it doesn't support the SyncModes docs say it should. Specifically this phrase confuses me (from MIDI_SYNC: "This mode only applies as the master sync mode for sequencers that are also MIDI receivers."
What's the meaning of the sequencer BEING a receiver. I thought it should be enough with my approach of getReceiver()
Regards and thanks !
For the future generations:
Homebrew, works-for-me, MIDI-clocked-sequencer. (Guess it's now clear what it says about the sequencer BEING a receiver): (ASSUMES that receivers listen on different MIDI channels, and tracks are created accordingly. Sends all notes off at closing. Track_Master is from my implementation, keeps track of the channel the receiver is listening)
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
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;
/**
* #author claudio
*
*/
public class MIDI_Clocked_Sequencer implements Receiver {
private Sequence S;
private int[] playheads;
private long counter=0;
private Receiver[] receivers;
private long counter_reset;
/**
* #throws MidiUnavailableException
*
*/
public MIDI_Clocked_Sequencer(Sequence S,long counter_reset,int[] where_to_get_receivers) throws MidiUnavailableException {
this.setSequence(S);
this.counter_reset=counter_reset;
playheads=new int[S.getTracks().length];
receivers=new Receiver[where_to_get_receivers.length];
for(int i=0;i<where_to_get_receivers.length;i++){
this.receivers[i]=MidiSystem.getMidiDevice(MidiSystem.getMidiDeviceInfo()[where_to_get_receivers[i]]).getReceiver();
MidiSystem.getMidiDevice(MidiSystem.getMidiDeviceInfo()[where_to_get_receivers[i]]).open();
}
}
#Override
public void close() {
for(int j=0;j<receivers.length;j++){
try {
receivers[j].send(new ShortMessage(ShortMessage.CONTROL_CHANGE+Track_Master.channel_map.get(Track_Master.gui_map.get(j)),123,0), 0);
} catch (InvalidMidiDataException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
receivers[j].close();}
}
#Override
public void send(MidiMessage msg, long arg1) {
Set<MidiMessage> message_buffer = new HashSet<MidiMessage>();
if(msg.getMessage()[0]==-8){
if(counter==counter_reset){counter=0;}
for(int j=0;j<S.getTracks().length;j++){
if(playheads[j]==S.getTracks()[j].size()){playheads[j]=0;};
while(playheads[j]<S.getTracks()[j].size() && S.getTracks()[j].get(playheads[j]).getTick()==counter){
message_buffer.add(S.getTracks()[j].get(playheads[j]).getMessage());
playheads[j]=playheads[j]+1;
}
}
for(Iterator<MidiMessage> it=message_buffer.iterator();it.hasNext();){
MidiMessage f=it.next();
for(int j=0;j<receivers.length;j++)
{
receivers[j].send(f, -1);}
}
counter=counter+1;
}
}
public Sequence getSequence() {
return S;
}
public void setSequence(Sequence s) {
this.S = s;
}
}
Many questions at once!
1) The most important information is that Sequencers in Java Sound are plugins (service providers, SPI). Java ships with at least one Sequencer, but there is no mention that it does support all features imaginable. In particular, the existence of the getMasterSyncModes() query is an indication for the fact that not every Sequencer implementation will support all sync modes. And as you have found out, the default Sequencer in Oracle's Java happens to not support any sync modes except Internal. To be clear about it: the Java specification does not say that a Sequencer must support any particular sync mode.
2) It is, however, possible to add an own implementation of Sequencer with external sync in form of a plugin.
What's the meaning of the sequencer BEING a receiver?
3) I assume what is meant is that in order to sync to another master sync provider, a Sequencer must be able to receive the MIDI Sync messages, i.e. also receive MIDI messages. I agree that the terminology of Receiver and Transmitter is indeed confusing in Java Sound.
What am I doing wrong?
4) You're not doing anything wrong. You can query the sync modes, and if the Sequencer supports the sync mode you're after, it'll work. But chances are, you won't find such a Sequencer implementation...
S_p = MidiSystem.getSequencer(false);
5) As said above, in Java Sound, Sequencers are plugins just like MIDI devices and Synthesizers. So your system might provide multiple Sequencers. You can query MidiSystem.getMidiDeviceInfo(), get the various devices, and see if they are instanceof Sequencer to find all available Sequencers.
But I think the main answer is: there is probably no implementation of Sequencer publicly available which supports external synchronization.
It is possible to get active and fix it in OpenJDK's RealTimeSequencer...

Set UsbDeviceListener javax.usb/usb4java

I tried to set the UsbDeviceListener but nothing happens when I disconnect the device nor when I create a new file on the device.
Can someone tell me where is the problem in my code?
public static void listPeripherique(UsbHub hub) {
List perepheriques = hub.getAttachedUsbDevices();
Iterator iterator = perepheriques.iterator();
while (iterator.hasNext()) {
UsbDevice perepherique = (UsbDevice) iterator.next();
perepherique.addUsbDeviceListener(new UsbDeviceListener() {
#Override
public void usbDeviceDetached(UsbDeviceEvent arg0) {
System.out.println("connect " + arg0);
}
#Override
public void errorEventOccurred(UsbDeviceErrorEvent arg0) {
System.out.println("disconect " + arg0);
}
#Override
public void dataEventOccurred(UsbDeviceDataEvent arg0) {
System.out.println("new data on " + arg0);
}
});
}
if (perepherique.isUsbHub()) {
listPeripherique((UsbHub) perepherique);
}
}
Edit: It's work only for usb Detached.
Do you mind giving use more details on your environment (Linux, Win, OSX) and what kind of USB device (Stick, Gamepad, DVD-Burner, Dongle, iP(a|o)d, iPhone, Smartphone)? However, if you use Windows, it seems the driver is out-of-date: JSR80: This is an ABANDONED Windows implementation. it does not work, it needs a kernel driver.
Keep us posted on your project and progress.
Edit
I tried it with my Logitech Wingman controller and I can confirm that only usbDeviceDetached events are raised. I think the driver is really outdated :(
Sounds like it is working correctly.
Your code iterates once over all currently connected devices and registers a device listener on it. When the device is disconnected then the usbDeviceDetached event is triggered. The other two events are triggered after data is transfered from or to the device by your own program or when an error occurred when your program tried to communicate with the device.
javax-usb can't monitor data transfers made by other programs. Maybe this is what you expected? For this you need a special USB monitor tool like usbmon on Linux.

How to extract audio from live video stream on Red5?

I need to extract audio from a live stream on red5 and stream it separately. On nginx with rtmp module I'd just retranslate this stream via ffmpeg without videodata, but I have no idea how to do anything like this (with or without ffmpeg) on Red5.
The first link on google gave me this:
just register IStreamListeners on your IClientStreams, and then separate AudioData from VideoData in the RTMPEvents
But this doesn't help much. To be honest, this doesn't help at all. What are these IStreamListeners and how do I register them on IClientStream?
And, what is more misterious, how do I separate AudioData from VideoData in some RTMPEvents?
This is how your extend RTMPClient and capture the Audio or Video events
private class TestClient extends RTMPClient {
private int audioCounter;
private int videoCounter;
public void connect() {
private IEventDispatcher streamEventDispatcher = new IEventDispatcher() {
public void dispatchEvent(IEvent event) {
System.out.println("ClientStream.dispachEvent()" + event.toString());
String evt = event.toString();
if (evt.indexOf("Audio") >= 0) {
audioCounter++;
} else if (evt.indexOf("Video") >= 0) {
videoCounter++;
}
}
};
}
}
This simply counts the a/v events, but it will get you part of the way there. I suggest looking though the unit tests in red5 and there you can learn a lot.

Categories

Resources