/*
Реализация спецификаций CLDC версии 1.1 (JSR-139), MIDP версии 2.1 (JSR-118)
и других спецификаций для функционирования компактных приложений на языке
Java (мидлетов) в среде программного обеспечения Малик Эмулятор.
Copyright © 2016, 2019 Малик Разработчик
Это свободная программа: вы можете перераспространять ее и/или изменять
ее на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
в каком она была опубликована Фондом свободного программного обеспечения;
либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.
Эта программа распространяется в надежде, что она будет полезной,
но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЕННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
общественной лицензии GNU.
Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
вместе с этой программой. Если это не так, см.
<https://www.gnu.org/licenses/>.
*/
package malik.emulator.fileformats.sound.synthetic.midilib;
import java.io.*;
import malik.emulator.fileformats.*;
import malik.emulator.fileformats.sound.*;
import malik.emulator.fileformats.sound.synthetic.*;
public final class MIDIEncoder extends Object
implements OutputAdapter, SoundEncoder, DataHolder, DataEncoder, SoundEncoderSynthetic
{
private static final class Track extends ByteArrayOutputStream
{
Track()
{
}
public void writeVolatileLengthValue(int value)
{
if(value >= 0x00000000 && value < 0x00000080)
{
write(value);
return;
}
if(value >= 0x00000080 && value < 0x00004000)
{
write((value >> 7) | 0x80);
write(value & 0x7f);
return;
}
if(value >= 0x00004000 && value < 0x00200000)
{
write((value >> 14) | 0x80);
write((value >> 7) | 0x80);
write(value & 0x7f);
return;
}
if(value >= 0x00200000 && value < 0x10000000)
{
write((value >> 21) | 0x80);
write((value >> 14) | 0x80);
write((value >> 7) | 0x80);
write(value & 0x7f);
return;
}
write(0);
}
}
public static final long MIDI_SIGNATURE = 0x4d54686400000006L;
private static final int MTRK_SIGNATURE = 0x4d54726b; /* MTrk */
private static final int PPQN = 100;
private static final int TEMPO = 1000 * PPQN;
private long[] messages;
public MIDIEncoder()
{
}
public void saveToOutputStream(OutputStream stream)
throws IOException, EmptyAdapterException
{
saveToDataStream(new DataOutputStream(stream));
}
public void saveToDataStream(DataOutputStream stream)
throws IOException, EmptyAdapterException
{
boolean completed;
int i;
int len;
int shortMsg;
int statusByte;
int dataByte1;
int dataByte2;
int deltaTime;
int prevStatusByte;
int maxNoteVolume;
long prevTime;
long currTime;
long message;
byte[] trackBytes;
long[] messages;
Track track;
if((messages = this.messages) == null)
{
throw new EmptyAdapterException("Кодирование MIDI-файлов: " +
"адаптер пуст.");
}
(track = new Track()).writeVolatileLengthValue(0);
track.write(0xff);
track.write(0x51);
track.write(0x03);
track.write((byte) (TEMPO >> 16));
track.write((byte) (TEMPO >> 8));
track.write((byte) TEMPO);
completed = false;
for(maxNoteVolume = 1, len = messages.length, i = 0; i < len; i++)
{
if((statusByte = (shortMsg = ((int) (message =
messages[i])) & 0x00ffffff) >> 16) >= 0x90 && statusByte < 0xa0)
{
if(maxNoteVolume < (dataByte2 = shortMsg & 0xff))
{
maxNoteVolume = dataByte2;
}
}
}
for(prevTime = 0L, prevStatusByte = 0, i = 0; i < len; i++)
{
currTime = (message = messages[i]) >> 24;
statusByte = (shortMsg = ((int) message) & 0x00ffffff) >> 16;
dataByte1 = (shortMsg >> 8) & 0xff;
dataByte2 = shortMsg & 0xff;
deltaTime = (int) (currTime - prevTime);
if(statusByte >= 0x80 && statusByte < 0xf0)
{
prevTime = currTime;
track.writeVolatileLengthValue(deltaTime);
if(statusByte != prevStatusByte || dataByte1 >= 0x80)
{
track.write(prevStatusByte = statusByte);
}
track.write(dataByte1);
if(statusByte >= 0x90 && statusByte < 0xa0)
{
track.write(dataByte2 * 0x7f / maxNoteVolume);
}
else if(statusByte < 0xc0 || statusByte >= 0xe0)
{
track.write(dataByte2);
}
continue;
}
if(shortMsg == 0x00ff2f00)
{
completed = true;
track.writeVolatileLengthValue(deltaTime);
track.write(0xff);
track.write(0x2f);
track.write(0x00);
break;
}
}
if(!completed)
{
track.writeVolatileLengthValue(0);
track.write(0xff);
track.write(0x2f);
track.write(0x00);
}
stream.writeLong(MIDI_SIGNATURE);
stream.writeShort(0); /* тип MIDI-файла */
stream.writeShort(1); /* количество треков */
stream.writeShort(PPQN); /* PPQN */
stream.writeInt(MTRK_SIGNATURE);
stream.writeInt((trackBytes = track.toByteArray()).length);
stream.write(trackBytes);
}
public void clear()
{
messages = null;
}
public boolean isEmpty()
{
return messages == null;
}
public void setMessages(long[] messages)
throws InvalidDataFormatException
{
if(messages == null)
{
throw new InvalidDataFormatException("Кодирование MIDI-файлов: " +
"параметр messages равен нулевой ссылке.");
}
this.messages = messages;
}
}