/*
Реализация спецификаций 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 malik.emulator.fileformats.sound.sampled.*;
import malik.emulator.media.sound.*;
import malik.emulator.microedition.media.*;
public final class SampledPlayer extends CustomPlayer
{
private static long[] packSamples(short[] samples) {
int len;
int pos = 0;
int sample0;
int sample1;
int sample2;
int sample3;
int slen = samples == null ? 0 : samples.length;
long[] result = new long[(len = slen >> 2) + 1];
for(int i = 0; i < len; i++)
{
sample0 = samples[pos++] & 0xffff;
sample1 = samples[pos++] & 0xffff;
sample2 = samples[pos++] & 0xffff;
sample3 = samples[pos++] & 0xffff;
result[i] = (long) sample0 | (long) sample1 << 0x10 | (long) sample2 << 0x20 | (long) sample3 << 0x30;
}
sample0 = (pos < slen ? samples[pos++] : pos > 0 ? samples[pos - 1] : 0) & 0xffff;
sample1 = (pos < slen ? samples[pos++] : pos > 0 ? samples[pos - 1] : 0) & 0xffff;
sample2 = (pos < slen ? samples[pos++] : pos > 0 ? samples[pos - 1] : 0) & 0xffff;
sample3 = (pos < slen ? samples[pos++] : pos > 0 ? samples[pos - 1] : 0) & 0xffff;
result[len] = (long) sample0 | (long) sample1 << 0x10 | (long) sample2 << 0x20 | (long) sample3 << 0x30;
return result;
}
private final class Playback extends Volume implements SoundPlayerListener
{
boolean loaded;
int position;
int loopCountSetted;
int loopCountRemaining;
long[] packedSamples;
SampledSoundPlayer systemPlayer;
public Playback(Object monitor) {
super(monitor);
this.loopCountSetted = 1;
this.loopCountRemaining = 1;
}
public void endOfBlock(SoundPlayer player, int blockIndexForLoad) {
synchronized(monitor)
{
label0:
{
int pos;
int len;
long[] samples;
SampledPlayer parent = SampledPlayer.this;
len = (samples = packedSamples) == null ? 0 : samples.length;
if(blockIndexForLoad < 0)
{
if(--loopCountRemaining == 0)
{
loaded = false;
parent.playerEndOfMedia();
break label0;
}
parent.playerRestart();
try
{
player.loadBlock(samples, 0, Math.min(len, SampledSoundPlayer.MAX_BLOCK_LENGTH));
}
catch(SoundPlayerException e)
{
e.printRealStackTrace();
}
break label0;
}
if((pos = position + blockIndexForLoad * SampledSoundPlayer.MAX_BLOCK_LENGTH) >= 0 && pos < len)
{
try
{
player.loadBlock(samples, pos, Math.min(len - pos, SampledSoundPlayer.MAX_BLOCK_LENGTH));
}
catch(SoundPlayerException e)
{
e.printRealStackTrace();
}
}
}
}
}
public void setSystemPlayer(SampledSoundPlayer 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) {
(SampledPlayer.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 double microsPerPackedSample;
private int channels;
private int samplesPerSecond;
private Playback playback;
private InputStream stream;
private final SampledSoundDecoder decoder;
public SampledPlayer(SampledPlayerDecoder decoder, InputStream stream, boolean closeNeed) {
int len;
Control[] controls;
Playback playback;
if(decoder == null)
{
throw new NullPointerException("SampledPlayer: аргумент decoder равен нулевой ссылке.");
}
if(stream == null)
{
throw new NullPointerException("SampledPlayer: аргумент 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 SampledSoundDecoder 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.packedSamples = 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 c;
int ss;
int len;
long samples;
SampledSoundDecoder decoder;
try
{
(decoder = this.decoder).loadFromInputStream(stream);
}
finally
{
if(closeNeed) stream.close();
}
channels = c = decoder.getChannels();
samplesPerSecond = ss = decoder.getSamplesPerSecond();
microsPerPackedSample = 4.e+006d / (double) (samples = (long) c * (long) ss);
len = (playback.packedSamples = packSamples(decoder.getSamples())).length;
setDuration(4000000L * (long) len / samples);
}
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(SampledSoundPlayer.open(channels, samplesPerSecond, SampledSoundPlayer.MAX_BLOCK_LENGTH));
}
catch(SoundPlayerException e)
{
throw new MediaException(e.getMessage());
}
}
}
protected void playerStart() throws MediaException {
long[] samples;
Playback playback;
SoundPlayer player;
if((playback = this.playback) != null && (player = playback.systemPlayer) != null && (samples = playback.packedSamples) != null)
{
if(playback.loopCountRemaining == 0) playback.loopCountRemaining = playback.loopCountSetted;
try
{
if(playback.loaded)
{
player.start();
} else
{
int pos;
player.loadBlock(samples, pos = playback.position, Math.min(samples.length - pos, SampledSoundPlayer.MAX_BLOCK_LENGTH));
playback.loaded = true;
}
}
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[] samples;
Playback playback;
SoundPlayer player;
if((playback = this.playback) != null && (player = playback.systemPlayer) != null && (samples = playback.packedSamples) != null)
{
double microsPS = microsPerPackedSample;
int pos;
int lim = samples.length - 1;
long maxTime = (long) (microsPS * (double) lim);
if(mediaTime < 0L) mediaTime = 0L;
if(mediaTime > maxTime) mediaTime = maxTime;
if((pos = (int) ((double) mediaTime / microsPS)) < 0) pos = 0;
if(pos > lim) pos = lim;
playback.position = pos;
mediaTime = (long) (microsPS * (double) pos);
try
{
switch(getState())
{
case PREFETCHED:
if(playback.loaded)
{
player.reset();
playback.loaded = false;
}
break;
case STARTED:
player.reset();
player.loadBlock(samples, pos, Math.min(lim - pos + 1, SampledSoundPlayer.MAX_BLOCK_LENGTH));
playback.loaded = true;
break;
}
}
catch(SoundPlayerException e)
{
throw new MediaException(e.getMessage());
}
}
return mediaTime;
}
protected long playerGetMediaTime() {
Playback playback;
SoundPlayer player;
if((playback = this.playback) != null && (player = playback.systemPlayer) != null && playback.packedSamples != null)
{
int pos;
if(playback.loaded)
{
try
{
long blockpos = player.getCurrentBlockIndexAndPosition();
pos = playback.position + (int) (blockpos >> 32) * SampledSoundPlayer.MAX_BLOCK_LENGTH + (int) blockpos;
}
catch(SoundPlayerException e)
{
pos = playback.position;
}
} else
{
pos = playback.position;
}
return (long) (microsPerPackedSample * (double) pos);
}
return TIME_UNKNOWN;
}
}