leffelmania / android-midi-lib Goto Github PK
View Code? Open in Web Editor NEWLicense: MIT License
License: MIT License
resource code is:
public static int bpmToMpqn(float bpm) {
return (int)(bpm * 6.0E7F);
}
I think it should be
public static int bpmToMpqn(float bpm) {
return (int)( 6.0E7F / bpm);
}
I wonder how do I import all your library for my project.
Add seek(tick) method in MidiProcessor class. It will set play time to another tick and dispatch all events before current tick.Please to implement it,thank you!
Hi Alex,
I'm working on my own project to create a midi file with the help of your midi-lib.
And I'm facing up to writing midi file if note is a chord.
Could you suggest me any idea to solve my problem? Or I missed the right method?
Thanks for you effort.
Hi, i don't understand where is set the default instrument used for composing Track and file.
I noticed that VariableLengthInts do not set the 7th bit on any byte in the parseBytes() function. This can be fixed easily by droping the & operation in line 78 resulting in
ints[mSizeInBytes - 1] = b;
To account this change line 93 must be changed to ensure mValue is correctly calculated
mValue += (ints[i] & 0x7F) << shift;
And by the way why don't you set shift (line83-86) simply by
int shift = (mSizeInBytes-1)*7;
saves another for loop ;)
Just figured this out.
While the event listener listens for MidiEvents, most of them are also ChannelEvents, which is an detention of MidiEvent. The ChannelEvents have the info you need to load the channels of your synth.Here is an example in Kotlin, within the onEvent callback.
var channel = 0 if(event is ChannelEvent){ Log.i("ChannelEvent", "${event.getChannel()}") channel = event.channel } if (event is ProgramChange){ synth.channels[event.channel].programChange(event.programNumber) }
The ProgramChange event has the channel number and instrument voice to load your synth.
How to handle Pitch Bend in Event Listener?
Originally posted by @ghopretz in #25 (comment)
I think there's an off by one error in the channel for ChannelEvent objects. I know drums are always supposed to be on channel 10 but in the lib it comes back as 9. Cross referenced this with my DAW
Hello,
I need implement a loop button control in my aplication. Its possible start and stop a midi play from an specific time. ?
Thanks
Seeing as how insertNote() is a shortcut for calling NoteOn and NoteOff shouldn't line 168 of MidiTrack.java create a NoteOff object?
Hi there, is there a way to control by Processing patch an hardware linked by MIDI/USB OUT of the android device?
Thanks a lot
Gian
public class MidiTrack {
...
this.insertEvent(new NoteOn(tick, channel, pitch, velocity));
this.insertEvent(new NoteOn(tick + duration, channel, pitch, 0));
...
should be
public class MidiTrack {
...
this.insertEvent(new NoteOn(tick, channel, pitch, velocity));
this.insertEvent(new NoteOff(tick + duration, channel, pitch, 0));
...
It is perfectly valid for a MidiFile to contain two noteOns followed by two noteOffs:
tick : event
0 : NoteOn channel 0 key 66 vel 100
1000 : NoteOn channel 0 key 66 vel 100
2000 : NoteOff channel 0 key 66
2000 : NoteOff channel 0 key 66
It is up to the synthesizer to decide whether it wants to voice this as two notes or not. Here's what the spec has to say:
ASSIGNMENT OF NOTE ON/OFF COMMANDS
If an instrument receives two or more Note On messages with the same key number and MIDI channel, it must make a determination of how to handle the additional Note Ons. It is up to the receiver as to whether the same voice or another voice will be sounded, or if the messages will be ignored. The transmitter, however, must send a corresponding Note Off message for every Note On sent. If the transmitter were to send only one Note Off message, and if the receiver in fact assigned the two Note On messages to different voices, then one note would linger. Since there is no harm or negative side effect in sending redundant Note Off messages this is the recommended practice.
The important thing here is that we need the same number of NoteOffs as NoteOns, otherwise some synthesizers may have a lingering note.
Some MIDI files, when read by this library, will drop one of these NoteOffs. This is because each track stores its events in a TreeSet
, so if two events are "equal" (due to the implementation of Comparable
), only one can be stored. This is a bug because perfectly valid MIDI files can cause this to happen, resulting in more NoteOns than NoteOffs, resulting in a lingering note on some (valid) synthesizers.
As a pathological example, the following code (Kotlin) throws an exception (i.e. the error
code was run).
val noteTrack = MidiTrack()
noteTrack.insertEvent(NoteOff(0, 0, 66, 64))
noteTrack.insertEvent(NoteOff(0, 0, 66, 64))
if (noteTrack.eventCount != 2) error("One event was dropped")
A more realistic example is as follows. This produces a lingering note on my synthesizer.
private fun generateMidiFile(): MidiFile {
val tempoTrack = MidiTrack()
val noteTrack = MidiTrack()
val ts = TimeSignature()
ts.setTimeSignature(
4,
4,
TimeSignature.DEFAULT_METER,
TimeSignature.DEFAULT_DIVISION
)
val tempo = Tempo()
tempo.bpm = 228f
tempoTrack.insertEvent(ts)
tempoTrack.insertEvent(tempo)
noteTrack.insertEvent(ProgramChange(0, 0, 0))
// Two noteOns - some synthesizers choose to play 2 voices here
noteTrack.insertEvent(NoteOn(0, 0, 66, 80))
noteTrack.insertEvent(NoteOn(1000, 0, 66, 100))
// Two simultaneous noteOffs and an unrelated noteOn at the same time.
// The first noteOff will be replaced by the second, leaving one more noteOn
// than noteOff.
noteTrack.insertEvent(NoteOn(2000, 0, 73, 100))
noteTrack.insertEvent(NoteOff(2000, 0, 66, 64))
noteTrack.insertEvent(NoteOff(2000, 0, 66, 64))
// Kill the unrelated note
noteTrack.insertEvent(NoteOff(2500, 0, 73, 64))
// Long pause to hear the ringing, then another note to end the file
noteTrack.insertEvent(NoteOn(10000, 0, 73, 100))
noteTrack.insertEvent(NoteOff(11000, 0, 73, 64))
val tracks: MutableList<MidiTrack> = ArrayList()
tracks.add(tempoTrack)
tracks.add(noteTrack)
return MidiFile(MidiFile.DEFAULT_RESOLUTION, tracks)
}
And here is how it sounds on my synthesizer (using the violin preset so we can hear the lingering notes):
Here's how these MIDI notes should sound (this was achieved by tweaking the velocity of one of the NoteOffs so they weren't equal):
I think it's surprising that a set (in this case, a TreeSet
) is used to store the events for a track, since you run into exactly this problem with sets (they cannot hold several identical objects). Moreover, it is prudent to remember the order that events were added, even if they occur on the same tick, as you should really honour the order they appeared in the file (e.g. the difference between NoteOn then simultaneous NoteOff, vs NoteOff then simultaneous NoteOn - they may sounds different). So I really think that a simple array or list is a more sensible data structure.
Alternatively, consider having some way to distinguish (i.e. order) two otherwise identical events. Maybe every event gets given some extra integer property tracking the order it was added to the track. This way, we can order two otherwise identical events, so they don't overwrite each other.
Hello,
I am trying to implement a Midi player with << and >> buttons.
Is there a way you can rewind the process and make it go faster to seek a possition in the midi file?
// Register for the events you're interested in:
EventPrinter ep = new EventPrinter("Individual Listener");
processor.registerEventListener(ep, NoteOn.class);
// Start the processor:
processor.start();
Thanks in advance!
Is there an existing way to control the gain/volume of the track? As of now, the noteOn function only accepts pitch and velocity. Is it possible to adjust the volume of the note too from here?
line 31 - it should be new VariableLengthInt(1)
Hi Alex,
In class KeySignature, line 120, method compareTo(), it seems you are comparing a key against an scale by mistake. I think you want to compare a scale against another scale.
Regards
The same as title, thank you.
How can i change the pitch and change instruments, i tried , but couldn't succeed. Any examples would be of great help.
Hi Alex,
Your code is inspiring, so please forgive my stupid questions.
I've run the sample code on PC, and plan to integrate the midi library
with other Midi Sheet music APP which generate midi sound and the midi library will send midi event to external midi device thru bluetooth interface at the same time.
Question #1
The example"Event Printer" show the parsed midi event on screen.
Can I just add my code "SENT TO BLUETOOTH INTERFACE" as below or other
modifications are needed? such as tempo,speed...
public void onEvent(MidiEvent event, long ms)
//System.out.println(mLabel + " received event: " + event);
"SENT TO BLUETOOTH INTERFACE"
}
Question #2
Can I just import the android-midi-libv3-49.jar file and use it in Eclipse ADT?
Thank you in advance for your time
Regards,
LICENSE files states "MIT" but file headers states "Apache 2.0".
Hi Alex,
I'm working on a little project and your android-midi-lib is helping out a lot!
I think that there is an important feature missing though, which is the ability to specify a midi-instrument for a chanel. Or did I just miss the right method?
Anyways thanks a lot for your effort :)
Kind regards
I already use the MIDI driver and Android MIDI library to play MIDI with Soundfont on Android. It works, but I only hear the piano. Even though my soundfont & midi file consists of many instruments, not just piano.
private int channel = 0;
SF2Soundbank sf = new SF2Soundbank(getAssets().open("test.sf2"));
synth = new SoftSynthesizer();
synth.open();
synth.loadAllInstruments(sf);
synth.getChannels()[0].programChange(0);
synth.getChannels()[1].programChange(1);
recv = synth.getReceiver();
synth.getChannels()[1].programChange(1);
recv = synth.getReceiver();
//To Play the Midi notes from midi file
MidiFile midiFile = new MidiFile(getAssets().open("test.mid"));
// Create a new MidiProcessor:
MidiProcessor processor = new MidiProcessor(midiFile);
// listen for all midi events:
processor.registerEventListener(new MidiEventListener() {
@Override
public void onStart(boolean fromBeginning) {
}
@Override
public void onEvent(MidiEvent event, long ms) {
if (event.getClass() == NoteOn.class) {
NoteOn noteOn = ((NoteOn) event);
try {
ShortMessage msg = new ShortMessage();
msg.setMessage(ShortMessage.NOTE_ON, channel, noteOn.getNoteValue(), noteOn.getVelocity());
recv.send(msg, ms);
} catch (InvalidMidiDataException e) {
e.printStackTrace();
}
} else if (event.getClass() == NoteOff.class) {
NoteOff noteOff = ((NoteOff) event);
try {
ShortMessage msg = new ShortMessage();
msg.setMessage(ShortMessage.NOTE_ON, channel, noteOff.getNoteValue(), noteOff.getVelocity());
recv.send(msg, ms);
} catch (InvalidMidiDataException e) {
e.printStackTrace();
}
}
}
@Override
public void onStop(boolean finished) {
}
}, MidiEvent.class);
// Start the processor:
processor.start();
I wanted to adapt the event printer example to actually send a file to a midi device... This is going just fine, except accessing the actual raw midi data from the event...
The event printer says what the event is, but not which channel etc... Ideally accessing the original bytes read from the midi file to create the event object would do the job - but despite a lot of looking, I haven't been able to get my head around your variable length int stuff(!).
I need to get timestamps of NoteOn and NoteOff, I've found the formula 60000 / (BPM * PPQ)
, but I don't know how to get the PPQ
value using this library.
NoteOff off = new NoteOff(tick, channel, pitch, 0);
When receive onSend(byte[] data, int offset, int count, long timestamp) event, we only know the event's timestamp, how to use timestamp to get tick? Timestamp is system's nano time
will this be updated to allow format 2 MIDI?
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.