/*
Реализация спецификаций 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.sampled.wavelib;
import java.io.*;
import malik.emulator.fileformats.*;
import malik.emulator.fileformats.sound.*;
import malik.emulator.fileformats.sound.sampled.*;
public final class WaveDecoder extends Object
implements InputAdapter, SoundDecoder, DataHolder, DataDecoder, SoundDecoderSampled
{
public static final long RIFF_SIGNATURE = 0x52494646L;
private static final int WAVE_SIGNATURE = 0x57415645; /* WAVE */
private static final int FMT_SIGNATURE = 0x666d7420; /* fmt */
private static final int DATA_SIGNATURE = 0x64617461; /* data */
private static int readUnsignedShortLE(InputStream stream)
throws IOException
{
int b1 = stream.read();
int b2 = stream.read();
if((b1 | b2) < 0)
{
throw new EOFException("DataInputStream: " +
"достигнут конец данных.");
}
return (b2 << 8) | b1;
}
private static int readIntLE(InputStream stream)
throws IOException
{
int b1 = stream.read();
int b2 = stream.read();
int b3 = stream.read();
int b4 = stream.read();
if((b1 | b2 | b3 | b4) < 0)
{
throw new EOFException("DataInputStream: " +
"достигнут конец данных.");
}
return (b4 << 24) | (b3 << 16) | (b2 << 8) | b1;
}
private boolean stopped;
private int channels;
private int samplesPerSecond;
private int bitsPerSample;
private byte[] data;
private short[] samples;
public WaveDecoder()
{
}
public void loadFromInputStream(InputStream stream)
throws IOException, InvalidDataFormatException
{
loadFromDataStream(new DataInputStream(stream));
}
public void loadFromDataStream(DataInputStream stream)
throws IOException, InvalidDataFormatException
{
boolean headerHandled = false;
boolean dataHandled = false;
int align = 0;
int riffSize;
int chunkStart;
int chunkLength;
if((riffSize = readIntLE(stream) - 4) < 0 || stream.readInt() != WAVE_SIGNATURE)
{
throw new InvalidDataFormatException("Декодирование Wave-файлов: " +
"тип файла может быть только WAVE.");
}
clear();
while(riffSize > 0)
{
chunkStart = stream.readInt();
chunkLength = readIntLE(stream);
if((riffSize -= chunkLength + 8) < 0)
{
throw new InvalidDataFormatException("Декодирование Wave-файлов: " +
"неправильный формат файла.");
}
switch(chunkStart)
{
default:
{
stream.skip(chunkLength);
}
break;
case FMT_SIGNATURE:
{
int cm;
int nc;
int ss;
int bs;
int ba;
int bt;
if(chunkLength < 0x10)
{
throw new InvalidDataFormatException("Декодирование Wave-файлов: " +
"маленькая длина важного куска fmt.");
}
headerHandled = true;
cm = readUnsignedShortLE(stream); /* алгоритм сжатия */
nc = readUnsignedShortLE(stream); /* количество каналов */
ss = readIntLE(stream); /* частота дискретизации */
bs = readIntLE(stream); /* количество байт в секунду */
ba = readUnsignedShortLE(stream); /* выравнивание */
bt = readUnsignedShortLE(stream); /* разрядность отсчёта */
if(cm != 1 || (nc != 1 && nc != 2) || (ss <= 0 || ss > 0xffff) ||
(bt != 8 && bt != 16) || bs != (ss * nc * bt) >> 3 ||
ba != (nc * bt) >> 3)
{
throw new InvalidDataFormatException("Декодирование Wave-файлов: " +
"система не поддерживает такой формат данных.");
}
if((chunkLength -= 0x10) > 0)
{
stream.skip((long) chunkLength);
}
align = ba;
this.channels = nc;
this.samplesPerSecond = ss;
this.bitsPerSample = bt;
}
break;
case DATA_SIGNATURE:
{
byte[] data;
if(!headerHandled)
{
clear();
throw new InvalidDataFormatException("Декодирование Wave-файлов: " +
"кусок формата fmt может идти только перед куском данных data.");
}
if(chunkLength % align != 0)
{
clear();
throw new InvalidDataFormatException("Декодирование Wave-файлов: " +
"размер куска данных не кратен выравниванию.");
}
dataHandled = true;
stream.read(data = new byte[chunkLength]);
this.data = data;
this.samples = null;
}
break;
}
if((chunkLength & 0x01) != 0 && riffSize > 0)
{
riffSize--;
stream.skip(1L);
}
}
if(!dataHandled)
{
clear();
throw new InvalidDataFormatException("Декодирование Wave-файлов: " +
"отсутствует важный кусок данных data.");
}
}
public void stopLoading()
{
stopped = true;
}
public void clear()
{
stopped = false;
channels = 0;
samplesPerSecond = 0;
bitsPerSample = 0;
data = null;
samples = null;
}
public boolean isEmpty()
{
return data == null && samples == null;
}
public int getChannels()
{
return channels;
}
public int getSamplesPerSecond()
{
return samplesPerSecond;
}
public short[] getSamples()
{
boolean stop;
int i;
int j;
int bt;
int len;
byte[] data;
short[] result;
if((result = this.samples) == null)
{
if((data = this.data) == null)
{
return null;
}
result = new short[len = data.length / (bt = bitsPerSample >> 3)];
stop = false;
label0:
{
switch(bt)
{
case 1:
for(i = len; i-- > 0; )
{
if(stopped)
{
stop = true;
break label0;
}
result[i] = (short) ((data[i] ^ 0x80) << 8);
}
break;
case 2:
for(j = (len - 1) << 1, i = len; i-- > 0; j -= 2)
{
if(stopped)
{
stop = true;
break label0;
}
result[i] = (short) ((data[j] & 0xff) | (data[j + 1] << 8));
}
break;
}
}
if(stop)
{
return null;
}
this.samples = result;
this.data = null;
}
return result;
}
}