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