/*
	Реализация спецификаций 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 WaveEncoder extends Object
		implements OutputAdapter, SoundEncoder, DataHolder, DataEncoder, SoundEncoderSampled
{
	public static final int RIFF_SIGNATURE = 0x52494646;
	private static final int DATA_SIGNATURE = 0x64617461;
	private static final long WAVEFMT_SIGNATURE = 0x57415645666d7420L;

	private static void writeShortLE(OutputStream stream, int src)
			throws IOException
	{
		stream.write(src);
		stream.write(src >> 8);
	}

	private static void writeIntLE(OutputStream stream, int src)
			throws IOException
	{
		stream.write(src);
		stream.write(src >> 8);
		stream.write(src >> 16);
		stream.write(src >> 24);
	}


	private int channels;
	private int samplesPerSecond;
	private short[] samples;

	public WaveEncoder()
	{
	}

	public void saveToOutputStream(OutputStream stream)
			throws IOException, EmptyAdapterException
	{
		saveToDataStream(new DataOutputStream(stream));
	}

	public void saveToDataStream(DataOutputStream stream)
			throws IOException, EmptyAdapterException
	{
		int i;
		int j;
		int len;
		int sample;
		int channels;
		int samplesPerSecond;
		byte[] data;
		short[] samples;
		if((samples = this.samples) == null)
		{
			throw new EmptyAdapterException("Кодирование Wave-файлов: " +
					"адаптер пуст.");
		}
		data = new byte[(len = samples.length) << 1];
		for(i = 0, j = 0; j < len; j++)
		{
			data[i++] = (byte) (sample = samples[j]);
			data[i++] = (byte) (sample >> 8);
		}
		channels = this.channels;
		samplesPerSecond = this.samplesPerSecond;
		stream.writeInt(RIFF_SIGNATURE);
		writeIntLE(stream, (len <<= 1) + 0x24);
		stream.writeLong(WAVEFMT_SIGNATURE);
		writeIntLE(stream, 0x10);
		writeShortLE(stream, 0x01); /* алгоритм сжатия */
		writeShortLE(stream, channels); /* количество каналов */
		writeIntLE(stream, samplesPerSecond); /* частота дискретизации */
		writeIntLE(stream, (samplesPerSecond * channels) << 1); /* количество байт в секунду */
		writeShortLE(stream, channels << 1); /* выравнивание */
		writeShortLE(stream, 0x10); /* разрядность отсчёта */
		stream.writeInt(DATA_SIGNATURE);
		writeIntLE(stream, len);
		stream.write(data);
	}

	public void clear()
	{
		channels = 0;
		samplesPerSecond = 0;
		samples = null;
	}

	public boolean isEmpty()
	{
		return samples == null;
	}

	public void setSamples(int channels, int samplesPerSecond, short[] samples)
			throws InvalidDataFormatException
	{
		if(channels != 1 && channels != 2)
		{
			throw new InvalidDataFormatException("Кодирование Wave-файлов: " +
					"недопустимое количество каналов " + channels + ".");
		}
		if(samplesPerSecond <= 0 || samplesPerSecond > 0xffff)
		{
			throw new InvalidDataFormatException("Кодирование Wave-файлов: " +
					"недопустимая частота дискретизации " + samplesPerSecond + ".");
		}
		if(samples == null)
		{
			throw new InvalidDataFormatException("Кодирование Wave-файлов: " +
					"параметр samples равен нулевой ссылке.");
		}
		if(samples.length % channels != 0)
		{
			throw new InvalidDataFormatException("Кодирование Wave-файлов: " +
					"параметр samples имеет недопустимую длину.");
		}
		this.channels = channels;
		this.samplesPerSecond = samplesPerSecond;
		this.samples = samples;
	}
}
