/*
Реализация спецификаций 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.microedition.system.player;
import java.io.*;
import javax.microedition.media.*;
import javax.microedition.media.control.*;
import malik.emulator.fileformats.sound.synthetic.*;
import malik.emulator.media.sound.*;
import malik.emulator.microedition.media.*;
public final class SyntheticPlayer extends CustomPlayer
{
private final class Playback extends Volume implements SoundPlayerListener, MIDIControl
{
boolean loaded;
int position;
int loopCountSetted;
int loopCountRemaining;
long[] synthesizerMessages;
SyntheticSoundPlayer systemPlayer;
public Playback(Object monitor) {
super(monitor);
this.loopCountSetted = 1;
this.loopCountRemaining = 1;
}
public void endOfBlock(SoundPlayer player, int blockIndexForLoad) {
if(blockIndexForLoad >= 0) return;
synchronized(monitor)
{
label0:
{
SyntheticPlayer parent = SyntheticPlayer.this;
if(--loopCountRemaining == 0)
{
parent.playerEndOfMedia();
break label0;
}
parent.playerRestart();
try
{
player.start();
}
catch(SoundPlayerException e)
{
e.printRealStackTrace();
}
}
}
}
public void shortMidiEvent(int statusByte, int dataByte1, int dataByte2) {
SyntheticSoundPlayer player;
if(statusByte < 0x80 || statusByte >= 0xf0 && statusByte <= 0xf7 || statusByte > 0xff)
{
throw new IllegalArgumentException("MIDIControl.shortMidiEvent: аргумент statusByte имеет недопустимое значение.");
}
if(dataByte1 < 0x00 || dataByte1 > 0x7f)
{
throw new IllegalArgumentException("MIDIControl.shortMidiEvent: аргумент dataByte1 имеет недопустимое значение.");
}
if(dataByte2 < 0x00 || dataByte2 > 0x7f)
{
throw new IllegalArgumentException("MIDIControl.shortMidiEvent: аргумент dataByte2 имеет недопустимое значение.");
}
if((player = systemPlayer) == null)
{
throw new IllegalStateException("MIDIControl.shortMidiEvent: проигрыватель не получил доступ к ресурсам.");
}
try
{
player.sendMessage(statusByte << 16 | dataByte1 << 8 | dataByte2);
}
catch(SoundPlayerException e)
{
throw new IllegalStateException("MIDIControl.shortMidiEvent: проигрыватель закрыт.");
}
}
public void setChannelVolume(int channel, int volume) {
SyntheticSoundPlayer player;
if(channel < 0x00 || channel > 0x0f)
{
throw new IllegalArgumentException("MIDIControl.setChannelVolume: аргумент channel имеет недопустимое значение.");
}
if(volume < 0x00 || volume > 0x7f)
{
throw new IllegalArgumentException("MIDIControl.setChannelVolume: аргумент volume имеет недопустимое значение.");
}
if((player = systemPlayer) == null)
{
throw new IllegalStateException("MIDIControl.setChannelVolume: проигрыватель не получил доступ к ресурсам.");
}
try
{
player.sendMessage((CONTROL_CHANGE | channel) << 16 | CONTROL_MAIN_VOLUME << 8 | volume);
}
catch(SoundPlayerException e)
{
throw new IllegalStateException("MIDIControl.setChannelVolume: проигрыватель закрыт.");
}
}
public void setProgram(int channel, int bank, int programme) {
SyntheticSoundPlayer player;
if(channel < 0x00 || channel > 0x0f)
{
throw new IllegalArgumentException("MIDIControl.setProgram: аргумент channel имеет недопустимое значение.");
}
if(bank < -1 || bank > 0x3fff)
{
throw new IllegalArgumentException("MIDIControl.setProgram: аргумент bank имеет недопустимое значение.");
}
if(programme < 0x00 || programme > 0x7f)
{
throw new IllegalArgumentException("MIDIControl.setProgram: аргумент programme имеет недопустимое значение.");
}
if((player = systemPlayer) == null)
{
throw new IllegalStateException("MIDIControl.setProgram: проигрыватель не получил доступ к ресурсам.");
}
try
{
if(bank >= 0)
{
player.sendMessage((CONTROL_CHANGE | channel) << 16 | CONTROL_BANK_CHANGE_MSB << 8 | bank >> 7);
player.sendMessage((CONTROL_CHANGE | channel) << 16 | CONTROL_BANK_CHANGE_LSB << 8 | bank & 0x7f);
}
player.sendMessage((PROGRAM_CHANGE | channel) << 16 | programme << 8);
}
catch(SoundPlayerException e)
{
throw new IllegalStateException("MIDIControl.setProgram: проигрыватель закрыт.");
}
}
public boolean isBankQuerySupported() {
return false;
}
public int longMidiEvent(byte[] src, int offset, int length) {
return length; /* Малик Эмулятор не поддерживает отправку длинных событий MIDI */
}
public int getChannelVolume(int channel) {
return -1; /* Малик Эмулятор не поддерживает чтение громкости канала */
}
public int[] getProgram(int channel) throws MediaException {
throw new BankQueryNotSupportedException("MIDIControl.getProgram: получение банков не поддерживается.");
}
public int[] getProgramList(int bank) throws MediaException {
throw new BankQueryNotSupportedException("MIDIControl.getProgramList: получение банков не поддерживается.");
}
public int[] getBankList(boolean custom) throws MediaException {
throw new BankQueryNotSupportedException("MIDIControl.getBankList: получение банков не поддерживается.");
}
public String getProgramName(int bank, int programme) throws MediaException {
throw new BankQueryNotSupportedException("MIDIControl.getProgramName: получение банков не поддерживается.");
}
public String getKeyName(int bank, int programme, int key) throws MediaException {
throw new BankQueryNotSupportedException("MIDIControl.getKeyName: получение банков не поддерживается.");
}
public void setSystemPlayer(SyntheticSoundPlayer player) throws SoundPlayerException {
setupSystemPlayer(player);
loaded = false;
systemPlayer = player;
}
public void closeSystemPlayer() {
SoundPlayer player;
if((player = systemPlayer) != null)
{
systemPlayer = null;
player.close();
}
}
protected void volumeChanged(boolean muted, int level) {
(SyntheticPlayer.this).postEvent(PlayerListener.VOLUME_CHANGED, this);
}
protected void setupSystemPlayer(SoundPlayer player) throws SoundPlayerException {
player.setSoundPlayerListener(this);
super.setupSystemPlayer(player);
}
protected SoundPlayer getSystemPlayer() {
return systemPlayer;
}
}
private final boolean closeNeed;
private Playback playback;
private InputStream stream;
private final SyntheticSoundDecoder decoder;
public SyntheticPlayer(SyntheticPlayerDecoder decoder, InputStream stream, boolean closeNeed) {
int len;
Control[] controls;
Playback playback;
if(decoder == null)
{
throw new NullPointerException("SyntheticPlayer: аргумент decoder равен нулевой ссылке.");
}
if(stream == null)
{
throw new NullPointerException("SyntheticPlayer: аргумент stream равен нулевой ссылке.");
}
playback = this.new Playback(monitor);
if((controls = decoder.getControls()) == null || (len = controls.length) <= 0)
{
controls = new Control[] { playback };
} else
{
Array.copy(controls, 0, controls = new Control[len + 1], 0, len);
controls[len] = playback;
}
this.controls = controls;
this.contentType = decoder.getContentType();
this.closeNeed = closeNeed;
this.playback = playback;
this.stream = stream;
this.decoder = decoder;
}
public SyntheticSoundDecoder getDecoder() {
return decoder;
}
protected void postEvent(String event, Object data) {
super.postEvent(event, data);
}
protected void playerClose() {
Playback playback;
if((playback = this.playback) != null)
{
playback.closeSystemPlayer();
playback.synthesizerMessages = null;
}
this.playback = null;
this.stream = null;
this.decoder.stopDecoding();
}
protected void playerRestart() {
super.playerRestart();
}
protected void playerEndOfMedia() {
super.playerEndOfMedia();
}
protected void playerUnrealize() {
decoder.stopDecoding();
}
protected void playerUnprefetch() {
Playback playback;
if((playback = this.playback) != null) playback.closeSystemPlayer();
}
protected void playerRealize() throws MediaException, InterruptedException {
Playback playback;
InputStream stream;
if((stream = this.stream) != null && (playback = this.playback) != null)
{
this.stream = null;
try
{
int lim;
long[] messages;
SyntheticSoundDecoder decoder;
try
{
(decoder = this.decoder).loadFromInputStream(stream);
}
finally
{
if(closeNeed) stream.close();
}
if((playback.synthesizerMessages = messages = decoder.getMessages()) != null && (lim = messages.length - 1) >= 0) setDuration(1000L * (messages[lim] >> 24));
}
catch(InterruptedIOException e)
{
throw new InterruptedException();
}
catch(IOException e)
{
throw new MediaException(e.getMessage());
}
}
}
protected void playerPrefetch() throws MediaException {
Playback playback;
if((playback = this.playback) != null && playback.systemPlayer == null)
{
try
{
playback.setSystemPlayer(SyntheticSoundPlayer.open());
}
catch(SoundPlayerException e)
{
throw new MediaException(e.getMessage());
}
}
}
protected void playerStart() throws MediaException {
long[] messages;
Playback playback;
SoundPlayer player;
if((playback = this.playback) != null && (player = playback.systemPlayer) != null && (messages = playback.synthesizerMessages) != null)
{
if(playback.loopCountRemaining == 0) playback.loopCountRemaining = playback.loopCountSetted;
try
{
if(playback.loaded)
{
player.start();
} else
{
int pos;
player.loadBlock(messages, 0, messages.length);
playback.loaded = true;
if((pos = playback.position) > 0) player.setCurrentBlockPosition(pos);
}
}
catch(SoundPlayerException e)
{
throw new MediaException(e.getMessage());
}
}
}
protected void playerStop() throws MediaException {
Playback playback;
SoundPlayer player;
if((playback = this.playback) != null && (player = playback.systemPlayer) != null)
{
try
{
player.stop();
}
catch(SoundPlayerException e)
{
throw new MediaException(e.getMessage());
}
}
}
protected void playerSetLoopCount(int loopCount) {
Playback playback;
if((playback = this.playback) != null)
{
playback.loopCountSetted = loopCount;
playback.loopCountRemaining = loopCount;
}
}
protected long playerSetMediaTime(long mediaTime) throws MediaException {
long[] messages;
Playback playback;
SoundPlayer player;
if((playback = this.playback) != null && (player = playback.systemPlayer) != null && (messages = playback.synthesizerMessages) != null)
{
int posGuess;
long guessTime;
for(int pos1 = 0, pos2 = messages.length - 1; ; )
{
posGuess = (pos1 + pos2) >> 1;
guessTime = 1000L * (messages[posGuess] >> 24);
if(pos1 == pos2) break;
if(mediaTime < guessTime)
{
if((pos2 = posGuess - 1) < pos1) pos2 = pos1;
continue;
}
if(mediaTime > guessTime)
{
if((pos1 = posGuess + 1) > pos2) pos1 = pos2;
continue;
}
break;
}
mediaTime = guessTime;
if(playback.loaded)
{
try
{
player.setCurrentBlockPosition(posGuess);
}
catch(SoundPlayerException e)
{
throw new MediaException(e.getMessage());
}
} else
{
playback.position = posGuess;
}
}
return mediaTime;
}
protected long playerGetMediaTime() {
long[] messages;
Playback playback;
SoundPlayer player;
if((playback = this.playback) != null && (player = playback.systemPlayer) != null && (messages = playback.synthesizerMessages) != null)
{
int pos;
if(playback.loaded)
{
try
{
pos = player.getCurrentBlockPosition();
}
catch(SoundPlayerException e)
{
pos = playback.position;
}
} else
{
pos = playback.position;
}
return 1000L * (messages[pos] >> 24);
}
return TIME_UNKNOWN;
}
}