Browse Source

Made a sound API.

David Piuva 9 months ago
parent
commit
c505507fa3

+ 4 - 4
Source/DFPSR/api/imageAPI.cpp

@@ -95,7 +95,7 @@ OrderedImageRgbaU8 image_decode_RgbaU8(const Buffer& fileContent) {
 	return image_decode_RgbaU8(buffer_getSafeData<uint8_t>(fileContent, "image file buffer"), buffer_getSize(fileContent));
 }
 // Loading from file
-OrderedImageRgbaU8 image_load_RgbaU8(const String& filename, bool mustExist) {
+OrderedImageRgbaU8 image_load_RgbaU8(const ReadableString& filename, bool mustExist) {
 	OrderedImageRgbaU8 result;
 	Buffer fileContent = file_loadBuffer(filename, mustExist);
 	if (buffer_exists(fileContent)) {
@@ -129,11 +129,11 @@ Buffer image_encode(const ImageRgbaU8 &image, ImageFileFormat format, int qualit
 	}
 }
 
-static ImageFileFormat detectImageFileExtension(const String& filename) {
+static ImageFileFormat detectImageFileExtension(const ReadableString& filename) {
 	ImageFileFormat result = ImageFileFormat::Unknown;
 	int lastDotIndex = string_findLast(filename, U'.');
 	if (lastDotIndex != -1) {
-		ReadableString extension = string_upperCase(file_getExtension(filename));
+		String extension = string_upperCase(file_getExtension(filename));
 		if (string_match(extension, U"JPG") || string_match(extension, U"JPEG")) {
 			result = ImageFileFormat::JPG;
 		} else if (string_match(extension, U"PNG")) {
@@ -147,7 +147,7 @@ static ImageFileFormat detectImageFileExtension(const String& filename) {
 	return result;
 }
 
-bool image_save(const ImageRgbaU8 &image, const String& filename, bool mustWork, int quality) {
+bool image_save(const ImageRgbaU8 &image, const ReadableString& filename, bool mustWork, int quality) {
 	ImageFileFormat extension = detectImageFileExtension(filename);
 	Buffer buffer;
 	if (extension == ImageFileFormat::Unknown) {

+ 2 - 2
Source/DFPSR/api/imageAPI.h

@@ -341,7 +341,7 @@ namespace dsr {
 	// Load an image from a file by giving the filename including folder path and extension.
 	// If mustExist is true, an exception will be raised on failure.
 	// If mustExist is false, failure will return an empty handle.
-	OrderedImageRgbaU8 image_load_RgbaU8(const String& filename, bool mustExist = true);
+	OrderedImageRgbaU8 image_load_RgbaU8(const ReadableString& filename, bool mustExist = true);
 	// Load an image from a memory buffer, which can be loaded with file_loadBuffer to get the same result as loading directly from the file.
 	// A convenient way of loading compressed images from larger files.
 	// Failure will return an empty handle.
@@ -362,7 +362,7 @@ namespace dsr {
 	// If mustWork is true, an exception will be raised on failure.
 	// If mustWork is false, failure will return false.
 	// The optional quality setting goes from 1% to 100% and is at the maximum by default.
-	bool image_save(const ImageRgbaU8 &image, const String& filename, bool mustWork = true, int quality = 100);
+	bool image_save(const ImageRgbaU8 &image, const ReadableString& filename, bool mustWork = true, int quality = 100);
 	// Save the image to a memory buffer.
 	// Post-condition: Returns a buffer with the encoded image format as it would be saved to a file, or empty on failure.
 	//                 No exceptions will be raised on failure, because an error message without a filename would not explain much.

+ 5 - 0
Source/DFPSR/api/randomAPI.cpp

@@ -120,6 +120,11 @@ int32_t random_generate_range(RandomGenerator &generator, int32_t minimum, int32
 	return minimum + int32_t(random_generate_U64(generator) % (uint64_t(maximum) - uint64_t(minimum) + 1u));
 }
 
+float random_generate_range(RandomGenerator &generator, float minimum, float maximum) {
+	double normalized = double(random_generate_U64(generator)) * (1.0 / 18446744073709551615.0);
+	return (normalized * (maximum - minimum)) + minimum;
+}
+
 bool random_generate_probability(RandomGenerator &generator, int32_t perCentProbability) {
 	return int32_t(random_generate_U64(generator) % 100u) < perCentProbability;
 }

+ 1 - 0
Source/DFPSR/api/randomAPI.h

@@ -51,6 +51,7 @@ RandomGenerator random_createGenerator(uint64_t seed);
 
 // Pre-condition: minimum <= maximum
 int32_t random_generate_range(RandomGenerator &generator, int32_t minimum, int32_t maximum);
+float random_generate_range(RandomGenerator &generator, float minimum, float maximum);
 
 // Returns true for roughly perCentProbability times in a hundred.
 bool random_generate_probability(RandomGenerator &generator, int32_t perCentProbability);

+ 395 - 0
Source/DFPSR/api/soundAPI.cpp

@@ -0,0 +1,395 @@
+
+#include "soundAPI.h"
+#include "fileAPI.h"
+#include "../settings.h"
+#include "../base/format.h"
+#include "../base/noSimd.h"
+
+namespace dsr {
+
+// See the Source/soundManagers folder for implementations of sound_streamToSpeakers for different operating systems.
+
+SoundBuffer::SoundBuffer(uint32_t samplesPerChannel, uint32_t channelCount, uint32_t sampleRate) {
+	this->impl_samplesPerChannel = samplesPerChannel;
+	if (this->impl_samplesPerChannel < 1) this->impl_samplesPerChannel = 1;
+	this->impl_channelCount = channelCount;
+	if (this->impl_channelCount < 1) this->impl_channelCount = 1;
+	this->impl_sampleRate = sampleRate;
+	if (this->impl_sampleRate < 1) this->impl_sampleRate = 1;	
+	this->impl_samples = buffer_create(this->impl_samplesPerChannel * this->impl_channelCount * sizeof(float));
+}
+
+// scaleOffset 0.0 preserves the mantissa better using power of two multiplications.
+// scaleOffset 1.0 allows using the full -1.0 to +1.0 range to prevent hard clipping of high values.
+static double scaleOffset = 1.0f;
+
+static double toIntegerScaleU8 = 128.0 - scaleOffset;
+static double toIntegerScaleI16 = 32768.0 - scaleOffset;
+static double toIntegerScaleI24 = 8388608.0 - scaleOffset;
+static double toIntegerScaleI32 = 2147483648.0 - scaleOffset;
+static double fromIntegerScaleU8 = 1.0 / toIntegerScaleU8;
+static double fromIntegerScaleI16 = 1.0 / toIntegerScaleI16;
+static double fromIntegerScaleI24 = 1.0 / toIntegerScaleI24;
+static double fromIntegerScaleI32 = 1.0 / toIntegerScaleI32;
+
+// TODO: Create a folder for implementations of sound formats.
+
+static const int fmtOffset_audioFormat = 0;
+static const int fmtOffset_channelCount = 2;
+static const int fmtOffset_sampleRate = 4;
+static const int fmtOffset_bytesPerSecond = 8;
+static const int fmtOffset_blockAlign = 12;
+static const int fmtOffset_bitsPerSample = 14;
+
+static uint32_t getSampleBits(RiffWaveFormat format) {
+	if (format == RiffWaveFormat::RawU8) {
+		return 8;
+	} else if (format == RiffWaveFormat::RawI16) {
+		return 16;
+	} else if (format == RiffWaveFormat::RawI24) {
+		return 24;
+	} else {
+		return 32;
+	}
+}
+
+static inline int64_t roundTo(double value, RoundingMethod roundingMethod) {
+	if (roundingMethod == RoundingMethod::Nearest){
+		return int64_t(value + (value > 0.0 ? 0.5 : -0.5));
+	} else { // RoundingMethod::Truncate
+		return int64_t(value);
+	}
+}
+
+static inline uint8_t floatToNormalizedU8(float value, RoundingMethod roundingMethod) {
+	int64_t closest = roundTo((double(value) * toIntegerScaleU8) + 128.0, roundingMethod);
+	if (closest <   0) closest =   0;
+	if (closest > 255) closest = 255;
+	return (uint8_t)closest;
+}
+
+static inline int16_t floatToNormalizedI16(float value, RoundingMethod roundingMethod) {
+	int64_t closest = roundTo(double(value) * toIntegerScaleI16, roundingMethod);
+	if (closest < -32768) closest = -32768;
+	if (closest >  32767) closest =  32767;
+	return (int16_t)closest;
+}
+
+static inline int32_t floatToNormalizedI24(float value, RoundingMethod roundingMethod) {
+	int64_t closest = roundTo(double(value) * toIntegerScaleI24, roundingMethod);
+	if (closest < -8388608) closest = -8388608;
+	if (closest >  8388607) closest =  8388607;
+	return (int32_t)closest;
+}
+
+static inline int32_t floatToNormalizedI32(float value, RoundingMethod roundingMethod) {
+	int64_t closest = roundTo(double(value) * toIntegerScaleI32, roundingMethod);
+	if (closest < -2147483648) closest = -2147483648;
+	if (closest >  2147483647) closest =  2147483647;
+	return (int32_t)closest;
+}
+
+static inline float floatFromNormalizedU8(uint8_t value) {
+	return float((double(value) - 128.0) * fromIntegerScaleU8);
+}
+
+static inline float floatFromNormalizedI16(int16_t value) {
+	return float(double(value) * fromIntegerScaleI16);
+}
+
+static inline float floatFromNormalizedI24(int32_t value) {
+	return float(double(value) * fromIntegerScaleI24);
+}
+
+static inline float floatFromNormalizedI32(int32_t value) {
+	return float(double(value) * fromIntegerScaleI32);
+}
+
+struct Chunk {
+	String name;
+	SafePointer<const uint8_t> chunkStart;
+	intptr_t chunkSize = 0;
+	Chunk(const ReadableString &name, const Buffer &buffer)
+	: name(name), chunkStart(buffer_getSafeData<uint8_t>(buffer, "Chunk buffer")), chunkSize(buffer_getSize(buffer)) {}
+	Chunk(const ReadableString &name, SafePointer<const uint8_t> chunkStart, intptr_t chunkSize)
+	: name(name), chunkStart(chunkStart), chunkSize(chunkSize) {}
+	Chunk() {}
+};
+
+static Buffer combineRiffChunks(List<Chunk> subChunks) {
+	uintptr_t payloadSize = 4u; // "WAVE"
+	for (intptr_t s = 0; s < subChunks.length(); s++) {
+		payloadSize += 8 + subChunks[s].chunkSize;
+	}
+	uintptr_t totalSize = payloadSize + 8u; // RIFF size
+	Buffer result = buffer_create(totalSize);
+	SafePointer<uint8_t> targetBytes = buffer_getSafeData<uint8_t>(result, "RIFF encoding target buffer");
+	targetBytes[0] = 'R';
+	targetBytes[1] = 'I';
+	targetBytes[2] = 'F';
+	targetBytes[3] = 'F';
+	targetBytes += 4;
+	format_writeU32_LE(targetBytes, payloadSize);
+	targetBytes += 4;
+	targetBytes[0] = 'W';
+	targetBytes[1] = 'A';
+	targetBytes[2] = 'V';
+	targetBytes[3] = 'E';
+	targetBytes += 4;
+	for (intptr_t s = 0; s < subChunks.length(); s++) {
+		uintptr_t subChunkSize = subChunks[s].chunkSize;
+		targetBytes[0] = char(subChunks[s].name[0]);
+		targetBytes[1] = char(subChunks[s].name[1]);
+		targetBytes[2] = char(subChunks[s].name[2]);
+		targetBytes[3] = char(subChunks[s].name[3]);
+		targetBytes += 4;
+		format_writeU32_LE(targetBytes, subChunkSize);
+		targetBytes += 4;
+		safeMemoryCopy(targetBytes, subChunks[s].chunkStart, subChunkSize);
+		targetBytes += subChunkSize;
+	}
+	return result;
+}
+
+Buffer sound_encode_RiffWave(const SoundBuffer &sound, RiffWaveFormat format, RoundingMethod roundingMethod) {
+	uint32_t bitsPerSample = getSampleBits(format);
+	uint32_t bytesPerSample = bitsPerSample / 8;
+	uint32_t channelCount = sound_getChannelCount(sound);
+	uint32_t samplesPerChannel = sound_getSamplesPerChannel(sound);
+	uint32_t blockAlign = channelCount * bytesPerSample;
+	uint32_t dataBytes = blockAlign * samplesPerChannel;
+	uint32_t sampleRate = sound_getSampleRate(sound);
+	uint32_t bytesPerSecond = blockAlign * sampleRate;
+
+	Buffer fmt = buffer_create(16);
+	SafePointer<uint8_t> formatBytes = buffer_getSafeData<uint8_t>(fmt, "RIFF encoding format buffer");
+	format_writeU16_LE(formatBytes + fmtOffset_audioFormat, 1); // PCM
+	format_writeU16_LE(formatBytes + fmtOffset_channelCount, channelCount);
+	format_writeU32_LE(formatBytes + fmtOffset_sampleRate, sampleRate);
+	format_writeU32_LE(formatBytes + fmtOffset_bytesPerSecond, bytesPerSecond);
+	format_writeU16_LE(formatBytes + fmtOffset_blockAlign, blockAlign);
+	format_writeU16_LE(formatBytes + fmtOffset_bitsPerSample, bitsPerSample);
+
+	Buffer data = buffer_create(dataBytes);
+	SafePointer<uint8_t> target = buffer_getSafeData<uint8_t>(data, "RIFF encoding data buffer");
+	SafePointer<const float> source = sound_getSafePointer(sound);
+	uintptr_t totalSamples = channelCount * samplesPerChannel;
+	if (format == RiffWaveFormat::RawU8) {
+		for (uintptr_t s = 0; s < totalSamples; s++) {
+			target[s] = floatToNormalizedU8(source[s], roundingMethod);
+		}
+	} else if (format == RiffWaveFormat::RawI16) {
+		for (uintptr_t s = 0; s < totalSamples; s++) {
+			format_writeI16_LE(target + s * bytesPerSample, floatToNormalizedI16(source[s], roundingMethod));
+		}
+	} else if (format == RiffWaveFormat::RawI24) {
+		for (uintptr_t s = 0; s < totalSamples; s++) {
+			format_writeI24_LE(target + s * bytesPerSample, floatToNormalizedI24(source[s], roundingMethod));
+		}
+	} else if (format == RiffWaveFormat::RawI32) {
+		for (uintptr_t s = 0; s < totalSamples; s++) {
+			format_writeI32_LE(target + s * bytesPerSample, floatToNormalizedI32(source[s], roundingMethod));
+		}
+	}
+	return combineRiffChunks(List<Chunk>(Chunk(U"fmt ", fmt), Chunk(U"data", data)));
+}
+
+static String readChar4(SafePointer<const uint8_t> nameStart) {
+	String name;
+	for (uintptr_t b = 0; b < 4; b++) {
+		string_appendChar(name, DsrChar(nameStart[b]));
+	}
+	return name;
+}
+
+static void getRiffChunks(const Chunk &parentChunk, std::function<void(const ReadableString &name, const Chunk &chunk)> returnChunk) {
+	SafePointer<const uint8_t> chunkStart = parentChunk.chunkStart;
+	SafePointer<const uint8_t> chunkEnd = chunkStart + parentChunk.chunkSize;
+	while (chunkStart.getUnchecked() + 8 <= chunkEnd.getUnchecked()) {
+		String name = readChar4(chunkStart);
+		uint32_t chunkSize = format_readU32_LE(chunkStart + 4);
+		SafePointer<const uint8_t> chunkPayload = chunkStart + 8;
+		if (chunkPayload.getUnchecked() + chunkSize > chunkEnd.getUnchecked()) {
+			sendWarning(U"Not enough space remaining (", uint64_t((uintptr_t)chunkEnd.getUnchecked() - (uintptr_t)chunkPayload.getUnchecked()), U" bytes) in the RIFF wave file to read the ", name, U" chunk of ", chunkSize, U" bytes!\n");
+			return;
+		}
+		returnChunk(name, Chunk(name, chunkPayload, chunkSize));
+		chunkStart = chunkStart + 8 + chunkSize;
+	}
+}
+
+static void getRiffChunks(const Buffer &fileBuffer, std::function<void(const ReadableString &name, const Chunk &chunk)> returnChunk) {
+	Chunk rootChunk = Chunk(U"RIFF", fileBuffer);
+	getRiffChunks(rootChunk, [&returnChunk](const ReadableString &name, const Chunk &chunk) {
+		if (string_match(name, U"RIFF")) {
+			if (!string_match(readChar4(chunk.chunkStart), U"WAVE")) {
+				throwError(U"WAVE format expected in RIFF file!\n");
+			}
+			getRiffChunks(Chunk(name, chunk.chunkStart + 4, chunk.chunkSize - 4), returnChunk);
+		}
+	});
+}
+
+SoundBuffer sound_decode_RiffWave(const Buffer &fileBuffer) {
+	Chunk fmtChunk;
+	Chunk dataChunk;
+	bool hasFmt = false;
+	bool hasData = false;
+	SafePointer<uint8_t> bufferStart = buffer_getSafeData<uint8_t>(fileBuffer, "File buffer");
+	getRiffChunks(fileBuffer, [&bufferStart, &fmtChunk, &hasFmt, &dataChunk, &hasData](const ReadableString &name, const Chunk &chunk) {
+		intptr_t byteOffset = intptr_t(chunk.chunkStart.getUnchecked()) - intptr_t(bufferStart.getUnchecked());
+		if (string_match(name, U"fmt ")) {
+			fmtChunk = chunk;
+			hasFmt = true;
+		} else if (string_match(name, U"data")) {
+			dataChunk = chunk;
+			hasData = true;
+		}
+	});
+	if (!hasFmt || !hasData) {
+		if (!hasFmt) {
+			sendWarning(U"Failed to find any fmt chunk in the RIFF wave file!\n");
+		}
+		if (!hasData) {
+			sendWarning(U"Failed to find any data chunk in the RIFF wave file!\n");
+		}
+		return SoundBuffer();
+	}
+	if (fmtChunk.chunkSize < 16) {
+		sendWarning(U"The fmt chunk of ", fmtChunk.chunkSize, U" bytes is not large enough in the RIFF wave file!\n");
+		return SoundBuffer();
+	}
+	uintptr_t audioFormat      = format_readU16_LE(fmtChunk.chunkStart + fmtOffset_audioFormat);
+	uintptr_t channelCount     = format_readU16_LE(fmtChunk.chunkStart + fmtOffset_channelCount);
+	uintptr_t sampleRate       = format_readU32_LE(fmtChunk.chunkStart + fmtOffset_sampleRate);
+	uintptr_t bytesPerSecond = format_readU32_LE(fmtChunk.chunkStart + fmtOffset_bytesPerSecond);
+	uintptr_t blockAlign       = format_readU16_LE(fmtChunk.chunkStart + fmtOffset_blockAlign);
+	uintptr_t bitsPerSample    = format_readU16_LE(fmtChunk.chunkStart + fmtOffset_bitsPerSample);
+	uintptr_t bytesPerSample   = bitsPerSample / 8;
+	uintptr_t dataSize         = dataChunk.chunkSize;
+	uintptr_t blockCount = dataSize / blockAlign;
+	SoundBuffer result = SoundBuffer(blockCount, channelCount, sampleRate);
+	SafePointer<float> target = sound_getSafePointer(result);
+	SafePointer<const uint8_t> waveContent = dataChunk.chunkStart;
+	if (audioFormat == 1 && bitsPerSample == 8) {
+		for (uintptr_t b = 0; b < blockCount; b++) {
+			for (uintptr_t c = 0; c < channelCount; c++) {
+				*target = floatFromNormalizedU8(waveContent[c]);
+				target += 1;
+			}
+			waveContent += blockAlign;
+		}
+		return result;
+	} else if (audioFormat == 1 && bitsPerSample == 16) {
+		for (uintptr_t b = 0; b < blockCount; b++) {
+			for (uintptr_t c = 0; c < channelCount; c++) {
+				*target = floatFromNormalizedI16(format_readI16_LE(waveContent + c * bytesPerSample));
+				target += 1;
+			}
+			waveContent += blockAlign;
+		}
+		return result;
+	} else if (audioFormat == 1 && bitsPerSample == 24) {
+		for (uintptr_t b = 0; b < blockCount; b++) {
+			for (uintptr_t c = 0; c < channelCount; c++) {
+				*target = floatFromNormalizedI24(format_readI24_LE(waveContent + c * bytesPerSample));
+				target += 1;
+			}
+			waveContent += blockAlign;
+		}
+		return result;
+	} else if (audioFormat == 1 && bitsPerSample == 32) {
+		for (uintptr_t b = 0; b < blockCount; b++) {
+			for (uintptr_t c = 0; c < channelCount; c++) {
+				*target = floatFromNormalizedI32(format_readI32_LE(waveContent + c * bytesPerSample));
+				target += 1;
+			}
+			waveContent += blockAlign;
+		}
+		return result;
+	} else if (audioFormat == 3 && bitsPerSample == 32) {
+		for (uintptr_t b = 0; b < blockCount; b++) {
+			for (uintptr_t c = 0; c < channelCount; c++) {
+				*target = format_bitsToF32_IEEE754(format_readU32_LE(waveContent + c * bytesPerSample));
+				target += 1;
+			}
+			waveContent += blockAlign;
+		}
+		return result;
+	} else if (audioFormat == 3 && bitsPerSample == 64) {
+		for (uintptr_t b = 0; b < blockCount; b++) {
+			for (uintptr_t c = 0; c < channelCount; c++) {
+				*target = format_bitsToF64_IEEE754(format_readU64_LE(waveContent + c * bytesPerSample));
+				target += 1;
+			}
+			waveContent += blockAlign;
+		}
+		return result;
+	} else {
+		sendWarning(U"Unsupported sound format ", audioFormat, U" of ", bitsPerSample, U" bits in RIFF wave file.\n");
+		// Returning an empty buffer because of the failure.
+		return SoundBuffer();
+	}
+	return SoundBuffer();
+}
+
+enum class SoundFileFormat {
+	Unknown,
+	WAV
+};
+
+static SoundFileFormat detectSoundFileExtension(const ReadableString& filename) {
+	SoundFileFormat result = SoundFileFormat::Unknown;
+	int lastDotIndex = string_findLast(filename, U'.');
+	if (lastDotIndex != -1) {
+		String extension = string_upperCase(file_getExtension(filename));
+		if (string_match(extension, U"WAV")) {
+			result = SoundFileFormat::WAV;
+		}
+	}
+	return result;
+}
+
+SoundBuffer sound_load(const ReadableString& filename, bool mustExist) {
+	SoundFileFormat extension = detectSoundFileExtension(filename);
+	Buffer fileContent = file_loadBuffer(filename, mustExist);
+	SoundBuffer result;
+	if (buffer_exists(fileContent)) {
+		if (extension == SoundFileFormat::WAV) {
+			result = sound_decode_RiffWave(fileContent);
+			if (mustExist && !sound_exists(result)) {
+				throwError(U"sound_load: Failed to load the sound at ", filename, U".\n");
+			}
+		}
+	}
+	return result;
+}
+
+bool sound_save(const ReadableString& filename, const SoundBuffer &sound, bool mustWork) {
+	SoundFileFormat extension = detectSoundFileExtension(filename);
+	if (extension == SoundFileFormat::WAV) {
+		Buffer fileContent = sound_encode_RiffWave(sound, RiffWaveFormat::RawI16);
+		return file_saveBuffer(filename, fileContent, mustWork);
+	// TODO: Add more sound formats.
+	} else {
+		if (mustWork) {
+			throwError(U"The extension of \"", filename, U"\" did not match any supported sound format!\n");
+		}
+		return false;
+	}
+}
+
+bool sound_save_RiffWave(const ReadableString& filename, const SoundBuffer &sound, RiffWaveFormat format, RoundingMethod roundingMethod, bool mustWork) {
+	SoundFileFormat extension = detectSoundFileExtension(filename);
+	if (extension == SoundFileFormat::WAV) {
+		Buffer fileContent = sound_encode_RiffWave(sound, format, roundingMethod);
+		return file_saveBuffer(filename, fileContent, mustWork);
+	} else {
+		if (mustWork) {
+			throwError(U"The extension of \"", filename, U"\" did not match RIFF wave's extension of *.wav!\n");
+		}
+		return false;
+	}
+}
+
+}

+ 84 - 0
Source/DFPSR/api/soundAPI.h

@@ -0,0 +1,84 @@
+
+#ifndef DFPSR_SOUND_API
+#define DFPSR_SOUND_API
+
+#include "bufferAPI.h"
+#include "stringAPI.h"
+
+namespace dsr {
+	// Call this function from a separate thread in a sound engine to initialize the sound system, call back with sound output requests and terminate when the callback returns false.
+	// The sound_streamToSpeakers function returns false if the backend could not be created, and true iff the backend completed all work and terminated safely.
+	// Channels: The number of virtual speakers to send data to.
+	//   How this is mapped to physical speakers depends on the system, because surround speakers may choose to play mono and stereo sound using only the front speakers.
+	// sampleRate: The number of ticks per second for each channel.
+	// soundOutput: A callback that requests length number of ticks generated by the sound engine and written in a packed format into the data array.
+	//   The soundOutput function returns true iff the audio backend should keep fetching sound samples, and false iff the engine is done and ready for the call to sound_streamToSpeakers to return.
+	//   data: The data array should be filled with sound samples in the -1.0f to 1.0f range, in indices from 0 to (length * channels) - 1.
+	//     The audio backend is responsible for converting the 32-bit float samples into a bit-depth chosen by the backend.
+	//     The backend is supposed to padd the SafePointer's range to at least be divisible by DSR_FLOAT_ALIGNMENT, which allow using both X vectors and F vectors.
+	//       The F vector can be larger than the X vector if building for a SIMD extension that only supports the widest vector length for floating-point operations.
+	//     Padding elements will not add to the time passed in the sound engine, for only played elements increment time.
+	//   length: The number of ticks per channel. The total number of elements to write into data is channels * length.
+	// How to use:
+	//   Call sound_streamToSpeakers with desired channels and sampleRate from a separate thread.
+	//   Handle callbacks to soundOutput by feeding the next packed sound samples and letting it return false when done.
+	//   Close the thread and let the sound engine clean up resources.
+	bool sound_streamToSpeakers(int channels, int sampleRate, std::function<bool(dsr::SafePointer<float> data, int length)> soundOutput);
+
+	// A sound buffer with packed channels of 32-bit floats.
+	// The duration in seconds equals samplesPerChannel / sampleRate
+	struct SoundBuffer {
+		Buffer impl_samples; // The packed samples.
+		uint32_t impl_samplesPerChannel = 0u; // Number of samples per channel.
+		uint32_t impl_channelCount = 0u; // Number of channels packed into the sound format.
+		uint32_t impl_sampleRate = 0u; // How many samples each channel will play per second.
+		SoundBuffer(uint32_t samplesPerChannel, uint32_t channelCount, uint32_t sampleRate);
+		SoundBuffer() {}
+	};
+
+	inline SoundBuffer sound_create(uint32_t samplesPerChannel, uint32_t channelCount, uint32_t sampleRate) { return SoundBuffer(samplesPerChannel, channelCount, sampleRate); }
+
+	inline bool sound_exists(const SoundBuffer &sound) { return sound.impl_samples.isNotNull(); }
+
+	inline int32_t sound_getSamplesPerChannel(const SoundBuffer &sound) { return sound.impl_samplesPerChannel; }
+
+	inline int32_t sound_getChannelCount(const SoundBuffer &sound) { return sound.impl_channelCount; }
+
+	inline int32_t sound_getSampleRate(const SoundBuffer &sound) { return sound.impl_sampleRate; }
+
+	inline SafePointer<float> sound_getSafePointer(const SoundBuffer &sound) { return buffer_getSafeData<float>(sound.impl_samples, "Sound buffer"); }
+
+	enum class RiffWaveFormat {
+		RawU8,
+		RawI16,
+		RawI24,
+		RawI32
+		// Floating-point sounds can currently be loaded but not saved.
+	};
+
+	// TODO: Implement random dither patterns?
+	enum class RoundingMethod {
+		Truncate,
+		Nearest
+	};
+
+	// Encodes a Riff wave file into a file buffer.
+	Buffer sound_encode_RiffWave(const SoundBuffer &sound, RiffWaveFormat format, RoundingMethod roundingMethod = RoundingMethod::Nearest);
+
+	// Decodes a RIFF wave file from memory in file buffer and returns the sound as a packed 32-bit float sound in the -1 to +1 range.
+	SoundBuffer sound_decode_RiffWave(const Buffer &fileBuffer);
+
+	SoundBuffer sound_load(const ReadableString& filename, bool mustExist = true);
+
+	// Save the sound buffer to the path specified by filename and return true iff the operation was successful.
+	// The file extension is case insensitive after the last dot in filename.
+	//   Accepted file extensions:
+	//     *.wav
+	// If mustWork is true, an exception will be raised on failure.
+	// If mustWork is false, failure will return false.
+	bool sound_save(const ReadableString& filename, const SoundBuffer &sound, bool mustWork = true);
+	// When you want to select a spefific version of the RIFF wave format.
+	bool sound_save_RiffWave(const ReadableString& filename, const SoundBuffer &sound, RiffWaveFormat format, RoundingMethod roundingMethod = RoundingMethod::Nearest, bool mustWork = true);
+}
+
+#endif

+ 166 - 0
Source/DFPSR/base/format.cpp

@@ -0,0 +1,166 @@
+// zlib open source license
+//
+// Copyright (c) 2025 David Forsgren Piuva
+// 
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// 
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 
+//    1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would be
+//    appreciated but is not required.
+// 
+//    2. Altered source versions must be plainly marked as such, and must not be
+//    misrepresented as being the original software.
+// 
+//    3. This notice may not be removed or altered from any source
+//    distribution.
+
+#include "format.h"
+#include <limits>
+#include <cmath>
+
+namespace dsr {
+
+uint16_t format_readU16_LE(SafePointer<const uint8_t> source) {
+	return ((uint16_t)source[0]     )
+	     | ((uint16_t)source[1] << 8);
+}
+
+uint32_t format_readU24_LE(SafePointer<const uint8_t> source) {
+	return ((int32_t)source[0]      )
+	     | ((int32_t)source[1] << 8 )
+	     | ((int32_t)source[2] << 16);
+}
+
+uint32_t format_readU32_LE(SafePointer<const uint8_t> source) {
+	return ((uint32_t)source[0]      )
+	     | ((uint32_t)source[1] << 8 )
+	     | ((uint32_t)source[2] << 16)
+	     | ((uint32_t)source[3] << 24);
+}
+
+uint64_t format_readU64_LE(SafePointer<const uint8_t> source) {
+	return ((uint64_t)source[0]      )
+	     | ((uint64_t)source[1] << 8 )
+	     | ((uint64_t)source[2] << 16)
+	     | ((uint64_t)source[3] << 24)
+	     | ((uint64_t)source[4] << 32)
+	     | ((uint64_t)source[5] << 40)
+	     | ((uint64_t)source[6] << 48)
+	     | ((uint64_t)source[7] << 56);
+}
+
+int16_t format_readI16_LE(SafePointer<const uint8_t> source) {
+	return int16_t(format_readU16_LE(source));
+}
+
+int32_t format_readI24_LE(SafePointer<const uint8_t> source) {
+	uint32_t result = format_readU24_LE(source);
+	if (result & 0b00000000100000000000000000000000) result |= 0b11111111000000000000000000000000;
+	return int32_t(result);
+}
+
+int32_t format_readI32_LE(SafePointer<const uint8_t> source) {
+	return int32_t(format_readU32_LE(source));
+}
+
+int64_t format_readI64_LE(SafePointer<const uint8_t> source) {
+	return int64_t(format_readU64_LE(source));
+}
+
+void format_writeU16_LE(SafePointer<uint8_t> target, uint16_t value) {
+	target[0] = uint8_t((value & 0x00FF)     );
+	target[1] = uint8_t((value & 0xFF00) >> 8);
+}
+
+void format_writeU24_LE(SafePointer<uint8_t> target, uint32_t value) {
+	target[0] = uint8_t((value & 0x0000FF)      );
+	target[1] = uint8_t((value & 0x00FF00) >>  8);
+	target[2] = uint8_t((value & 0xFF0000) >> 16);
+}
+
+void format_writeU32_LE(SafePointer<uint8_t> target, uint32_t value) {
+	target[0] = uint8_t((value & 0x000000FF)      );
+	target[1] = uint8_t((value & 0x0000FF00) >>  8);
+	target[2] = uint8_t((value & 0x00FF0000) >> 16);
+	target[3] = uint8_t((value & 0xFF000000) >> 24);
+}
+
+void format_writeU64_LE(SafePointer<uint8_t> target, uint64_t value) {
+	target[0] = uint8_t((value & 0x00000000000000FF)      );
+	target[1] = uint8_t((value & 0x000000000000FF00) >>  8);
+	target[2] = uint8_t((value & 0x0000000000FF0000) >> 16);
+	target[3] = uint8_t((value & 0x00000000FF000000) >> 24);
+	target[4] = uint8_t((value & 0x000000FF00000000) >> 32);
+	target[5] = uint8_t((value & 0x0000FF0000000000) >> 40);
+	target[6] = uint8_t((value & 0x00FF000000000000) >> 48);
+	target[7] = uint8_t((value & 0xFF00000000000000) >> 56);
+}
+
+void format_writeI16_LE(SafePointer<uint8_t> target, int16_t value) {
+	format_writeU16_LE(target, uint16_t(value));
+}
+
+void format_writeI24_LE(SafePointer<uint8_t> target, int32_t value) {
+	format_writeU24_LE(target, uint32_t(value));
+}
+
+void format_writeI32_LE(SafePointer<uint8_t> target, int32_t value) {
+	format_writeU32_LE(target, uint32_t(value));
+}
+
+void format_writeI64_LE(SafePointer<uint8_t> target, int64_t value) {
+	format_writeU32_LE(target, uint64_t(value));
+}
+
+float format_bitsToF32_IEEE754(uint32_t bits) {
+	bool     sign     =  bits & 0b10000000000000000000000000000000;
+	uint32_t exponent = (bits & 0b01111111100000000000000000000000) >> 23;
+	uint32_t mantissa =  bits & 0b00000000011111111111111111111111;
+	if (exponent == 0b11111111) {
+		if (mantissa == 0u) {
+			return sign ? -std::numeric_limits<float>::infinity() : std::numeric_limits<float>::infinity();
+		} else {
+			return std::numeric_limits<float>::quiet_NaN();
+		}
+	} else if (exponent == 0 && mantissa == 0) {
+		return 0.0f;
+	}
+	float value;
+	if (exponent == 0) {
+		value = std::ldexp(mantissa, -126 - 23);
+	} else {
+		value = std::ldexp((mantissa | 0b100000000000000000000000), exponent - 127 - 23);
+	}
+	return sign ? -value : value;
+}
+
+double format_bitsToF64_IEEE754(uint64_t bits) {
+	bool     sign     =  bits & 0b1000000000000000000000000000000000000000000000000000000000000000;
+	uint64_t exponent = (bits & 0b0111111111110000000000000000000000000000000000000000000000000000) >> 52;
+	uint64_t mantissa =  bits & 0b0000000000001111111111111111111111111111111111111111111111111111;
+	if (exponent == 0b11111111111) {
+		if (mantissa == 0u) {
+			return sign ? -std::numeric_limits<double>::infinity() : std::numeric_limits<double>::infinity();
+		} else {
+			return std::numeric_limits<double>::quiet_NaN();
+		}
+	} else if (exponent == 0 && mantissa == 0) {
+		return 0.0;
+	}
+	double value;
+	if (exponent == 0) {
+		value = std::ldexp(mantissa, -1022 - 52);
+	} else {
+		value = std::ldexp((mantissa | 0b10000000000000000000000000000000000000000000000000000), exponent - 1023 - 52);
+	}
+	return sign ? -value : value;
+}
+
+}

+ 66 - 0
Source/DFPSR/base/format.h

@@ -0,0 +1,66 @@
+// zlib open source license
+//
+// Copyright (c) 2025 David Forsgren Piuva
+// 
+// This software is provided 'as-is', without any express or implied
+// warranty. In no event will the authors be held liable for any damages
+// arising from the use of this software.
+// 
+// Permission is granted to anyone to use this software for any purpose,
+// including commercial applications, and to alter it and redistribute it
+// freely, subject to the following restrictions:
+// 
+//    1. The origin of this software must not be misrepresented; you must not
+//    claim that you wrote the original software. If you use this software
+//    in a product, an acknowledgment in the product documentation would be
+//    appreciated but is not required.
+// 
+//    2. Altered source versions must be plainly marked as such, and must not be
+//    misrepresented as being the original software.
+// 
+//    3. This notice may not be removed or altered from any source
+//    distribution.
+
+#ifndef DFPSR_FORMAT
+#define DFPSR_FORMAT
+
+#include "SafePointer.h"
+#include <cstdint>
+
+namespace dsr {
+
+// Helper functions for encoding and decoding binary file formats in buffers.
+
+// Read an unaligned unsigned integer with the first byte at source using little-endian byte order.
+uint16_t format_readU16_LE(SafePointer<const uint8_t> source);
+uint32_t format_readU24_LE(SafePointer<const uint8_t> source);
+uint32_t format_readU32_LE(SafePointer<const uint8_t> source);
+uint64_t format_readU64_LE(SafePointer<const uint8_t> source);
+
+// Read an unaligned signed integer in two's complement with the first byte at source using little-endian byte order.
+int16_t format_readI16_LE(SafePointer<const uint8_t> source);
+int32_t format_readI24_LE(SafePointer<const uint8_t> source);
+int32_t format_readI32_LE(SafePointer<const uint8_t> source);
+int64_t format_readI64_LE(SafePointer<const uint8_t> source);
+
+// Write an unaligned unsigned integer with the first byte at target using little-endian byte order.
+void format_writeU16_LE(SafePointer<uint8_t> target, uint16_t value);
+void format_writeU24_LE(SafePointer<uint8_t> target, uint32_t value);
+void format_writeU32_LE(SafePointer<uint8_t> target, uint32_t value);
+void format_writeU64_LE(SafePointer<uint8_t> target, uint64_t value);
+
+// Write an unaligned signed integer in two's complement with the first byte at target using little-endian byte order.
+void format_writeI16_LE(SafePointer<uint8_t> target, int16_t value);
+void format_writeI24_LE(SafePointer<uint8_t> target, int32_t value);
+void format_writeI32_LE(SafePointer<uint8_t> target, int32_t value);
+void format_writeI64_LE(SafePointer<uint8_t> target, int64_t value);
+
+// Convert bits interpreted as a 32-bit IEEE754 floating-point value into the native float representation.
+float format_bitsToF32_IEEE754(uint32_t bits);
+
+// Convert bits interpreted as a 65-bit IEEE754 floating-point value into the native double representation.
+double format_bitsToF64_IEEE754(uint64_t bits);
+	
+}
+
+#endif

+ 2 - 0
Source/DFPSR/includeFramework.h

@@ -21,6 +21,8 @@
 	#include "api/guiAPI.h" // Handling windows, interfaces and components
 	#include "api/mediaMachineAPI.h" // A machine for running image functions
 	#include "api/fontAPI.h" // Printing text to images
+	// Sound API
+	#include "api/soundAPI.h" // Processing buffers and playing sounds
 	// Convenient API
 	#include "api/algorithmAPI.h" // Functions for performing operations on whole collections
 	#include "api/timeAPI.h" // Methods for time and delays

+ 17 - 0
Source/SDK/SoundEngine/SoundEngine.DsrProj

@@ -0,0 +1,17 @@
+CompilerFlag "-std=c++14"
+Graphics
+Sound
+Crawl "main.cpp"
+Import "../../DFPSR/DFPSR.DsrHead"
+
+# If compiling using CLANG instead of GCC in tools/builder/buildProject.sh, you need to include the C++ standard library explicitly.
+#Link "stdc++"
+
+# Linking statically to standard C/C++ libraries allow running the program without installing them.
+#   Recommended for making an installer for another application or programs that should not need an installer.
+# If you don't want static linking of standard C/C++ libraries, you have to comment out StaticRuntime
+#   and bundle the C/C++ runtime of the compiler with your program's installer.
+StaticRuntime
+
+# Uncomment to enable debug mode, which is slower
+#Debug

BIN
Source/SDK/SoundEngine/Water.wav


+ 5 - 0
Source/SDK/SoundEngine/build_linux.sh

@@ -0,0 +1,5 @@
+#!/bin/bash
+
+echo "Running build_linux.sh $@"
+chmod +x ../../tools/builder/buildProject.sh;
+../../tools/builder/buildProject.sh SoundEngine.DsrProj Linux $@;

+ 4 - 0
Source/SDK/SoundEngine/build_windows.bat

@@ -0,0 +1,4 @@
+@echo off
+
+echo "Running build_windows.bat %@%
+..\..\tools\builder\buildProject.bat SoundEngine.DsrProj Windows %@%

+ 160 - 0
Source/SDK/SoundEngine/main.cpp

@@ -0,0 +1,160 @@
+
+/*
+TODO:
+	* Create a visual graph with instruments, filters, speakers and file recorders to test a modular sound engine.
+	* Allow recording the output of a session into a hi-fi stereo sound buffer, which can later be exported as a file.
+	* Create a basic compressed music format for looping sounds in different speed and volume from compressed interpolated curves.
+	* Make a list of named instruments containing a list of voices.
+		Each voice refers to a sound buffer by index (using names in files) and an envelope for how to play the sound.
+		Each voice will be played as its own instrument but from the same input for a richer sound without having to duplicate notes.
+		The sounds can be either embedded into the project (editable for tiny instrument patterns) or refer to external files (for whole music tracks).
+*/
+
+#include "../../DFPSR/includeFramework.h"
+#include "sound.h"
+
+using namespace dsr;
+
+// Global
+bool running = true;
+Window window;
+Component mainPanel;
+Component toolPanel;
+
+String interfaceContent =
+UR"QUOTE(
+Begin : Panel
+	Name = "mainPanel"
+	Solid = 0
+	Begin : Panel
+		Name = "toolPanel"
+		Color = 180,180,180
+		Solid = 1
+		bottom = 50
+	End
+End
+)QUOTE";
+
+static const double pi = 3.1415926535897932384626433832795;
+static const double cyclesToRadians = pi * 2.0;
+static const int toneCount = 9;
+int basicTone, testSound;
+int playing[toneCount];
+void createTestProject() {
+	for (int t = 0; t < toneCount; t++) {
+		playing[t] = -1;
+	}
+	// Pure tone
+	basicTone = generateMonoSoundBuffer(U"sine", 441, 44100, [](double time) -> float {
+		return sin(time * (cyclesToRadians * 100));
+	});
+	// Loaded from file
+	testSound = loadSoundFromFile(U"Water.wav");
+}
+
+static EnvelopeSettings envelope = EnvelopeSettings(0.1, 0.2, 0.8, 0.4, 0.1, -0.02, 0.04, 0.5);
+static double previewPressTime = 1.0;
+static double previewViewTime = 4.0;
+
+static int selectedBuffer = 0;
+static void limitSelection() {
+	int maxIndex = getSoundBufferCount() - 1;
+	if (selectedBuffer < 0) selectedBuffer = 0;
+	if (selectedBuffer > maxIndex) selectedBuffer = maxIndex;
+}
+
+DSR_MAIN_CALLER(dsrMain)
+void dsrMain(List<String> args) {
+	// Start sound thread
+	printText("Initializing sound\n");
+	sound_initialize();
+
+	// Create something to test
+	printText("Creating test project\n");
+	createTestProject();
+
+	// Create a window
+	window = window_create(U"Sound generator", 800, 600);
+
+	// Load an interface to the window
+	window_loadInterfaceFromString(window, interfaceContent);
+
+	// Find components
+	mainPanel = window_findComponentByName(window, U"mainPanel");
+	toolPanel = window_findComponentByName(window, U"toolPanel");
+
+	// Bind methods to events
+	window_setKeyboardEvent(window, [](const KeyboardEvent& event) {
+		DsrKey key = event.dsrKey;
+		if (event.keyboardEventType == KeyboardEventType::KeyDown) {
+			if (key == DsrKey_Escape) {
+				running = false;
+			} else if (key >= DsrKey_1 && key <= DsrKey_9) {
+				int toneIndex = key - DsrKey_1;
+				playing[toneIndex] = playSound(basicTone, true, 0.25, 0.25, 3.0 + toneIndex * 0.25, envelope);
+			} else if (key == DsrKey_A) {
+				playSound(testSound, false, 1.0, 0.0, 1.0);
+			} else if (key == DsrKey_S) {
+				playSound(testSound, false, 1.0, 1.0, 1.0);
+			} else if (key == DsrKey_D) {
+				playSound(testSound, false, 0.0, 1.0, 1.0);
+			} else if (key == DsrKey_UpArrow) {
+				selectedBuffer--;
+				limitSelection();
+			} else if (key == DsrKey_DownArrow) {
+				selectedBuffer++;
+				limitSelection();
+			}
+		} else if (event.keyboardEventType == KeyboardEventType::KeyUp) {
+			if (key >= DsrKey_1 && key <= DsrKey_9) {
+				int toneIndex = key - DsrKey_1;
+				releaseSound(playing[toneIndex]); // Soft stop with following release
+			} else if (key == DsrKey_Space) {
+				stopAllSounds();
+			}
+		}
+	});
+	/*
+	component_setMouseDownEvent(mainPanel, [](const MouseEvent& event) {
+		
+	});
+	component_setMouseMoveEvent(mainPanel, [](const MouseEvent& event) {
+		
+	});
+	component_setMouseUpEvent(mainPanel, [](const MouseEvent& event) {
+		
+	});
+	*/
+	window_setCloseEvent(window, []() {
+		running = false;
+	});
+
+	// Execute
+	while(running) {
+		// Wait for actions so that we don't render until an action has been recieved
+		// This will save battery on laptops for applications that don't require animation
+		while (!window_executeEvents(window)) {
+			time_sleepSeconds(0.01);
+		}
+		// Fill the background
+		AlignedImageRgbaU8 canvas = window_getCanvas(window);
+		image_fill(canvas, ColorRgbaI32(64, 64, 64, 255));
+		int width = image_getWidth(canvas);
+		// Draw things
+		drawEnvelope(canvas, IRect(0, 50, width, 100), envelope, previewPressTime, previewViewTime);
+		// TODO: Group into a visual component for viewing sound buffers.
+		int top = 150;
+		for (int s = 0; s < getSoundBufferCount(); s++) {
+			int height = 100;
+			drawSound(canvas, IRect(0, top, width, height), s, s == selectedBuffer);
+			top += height;
+		}
+		// Draw interface
+		window_drawComponents(window);
+		// Show the final image
+		window_showCanvas(window);
+	}
+	// Close sound thread
+	printText("Terminating sound\n");
+	sound_terminate();
+}

+ 106 - 165
Source/tools/wizard/sound.cpp → Source/SDK/SoundEngine/sound.cpp

@@ -1,22 +1,11 @@
 
 #include "sound.h"
-#include "../../soundManagers/soundManagers.h"
-
-using namespace dsr;
+#include "../../DFPSR/api/soundAPI.h"
 
 #include <future>
 #include <atomic>
 
-inline float sound_convertI16ToF32(int64_t input) {
-	return input * (1.0f / 32767.0f);
-}
-
-inline int sound_convertF32ToI16(float input) {
-	int64_t result = input * 32767.0f;
-	if (result > 32767) { result = 32767; }
-	if (result < -32768) { result = -32768; }
-	return result;
-}
+namespace dsr {
 
 static const int outputChannels = 2;
 static const int outputSampleRate = 44100;
@@ -27,179 +16,70 @@ std::future<void> soundFuture;
 static std::atomic<bool> soundRunning{true};
 static std::mutex soundMutex;
 
-static int soundFormatSize(int soundFormat) {
-	if (soundFormat == soundFormat_I16) {
-		return 2;
-	} else if (soundFormat == soundFormat_F32) {
-		return 4;
-	} else {
-		throwError("Cannot get size of unknown sound format!\n");
-		return 0;
-	}
-}
-
 static void minMax(float &minimum, float &maximum, float value) {
 	if (value < minimum) { minimum = value; }
 	if (value > maximum) { maximum = value; }
 }
 
 struct Sound {
+	SoundBuffer buffer;
 	String name;
 	bool fromFile;
-	int sampleCount;
-	int sampleRate;
-	Buffer samples;
-	int channelCount;
-	int soundFormat;
-	Sound(const ReadableString &name, bool fromFile, int sampleCount, int sampleRate, int channelCount, int soundFormat)
-	: name(name), fromFile(fromFile), sampleCount(sampleCount), sampleRate(sampleRate), samples(buffer_create(sampleCount * channelCount * soundFormatSize(soundFormat))), channelCount(channelCount), soundFormat(soundFormat) {}
-	float sampleLinear(int64_t floor, int64_t ceiling, double ratio, int channel) {
-		int bufferIndexF = floor * this->channelCount + channel;
-		int bufferIndexC = ceiling * this->channelCount + channel;
+	Sound(const SoundBuffer &buffer, const ReadableString &name, bool fromFile)
+	: buffer(buffer), name(name), fromFile(fromFile) {}
+	Sound(const ReadableString &name, bool fromFile, int32_t samplesPerChannel, int32_t channelCount, int32_t sampleRate)
+	: buffer(samplesPerChannel, channelCount, sampleRate), name(name), fromFile(fromFile) {}
+	float sampleLinear(int32_t leftIndex, int32_t rightIndex, double ratio, int32_t channel) {
+		int64_t leftOffset = leftIndex * sound_getChannelCount(this->buffer) + channel;
+		int64_t rightOffset = rightIndex * sound_getChannelCount(this->buffer) + channel;
 		float a = 0.0, b = 0.0;
-		if (this->soundFormat == soundFormat_I16) {
-			SafePointer<int16_t> source = buffer_getSafeData<int16_t>(this->samples, "I16 source sound buffer in sampleLinear");
-			a = sound_convertI16ToF32(source[bufferIndexF]);
-			b = sound_convertI16ToF32(source[bufferIndexC]);
-		} else if (this->soundFormat == soundFormat_F32) {
-			SafePointer<float> source = buffer_getSafeData<float>(this->samples, "F32 source sound buffer in sampleLinear");
-			a = source[bufferIndexF];
-			b = source[bufferIndexC];
-		}
+		SafePointer<float> source = sound_getSafePointer(this->buffer);
+		a = source[leftOffset];
+		b = source[rightOffset];
 		return b * ratio + a * (1.0 - ratio);
 	}
-	float sampleLinear_cyclic(double location, int channel) {
-		int64_t truncated = (int64_t)location;
-		int64_t floor = truncated % this->sampleCount;
-		int64_t ceiling = floor + 1; if (ceiling == sampleCount) { ceiling = 0; }
+	float sampleLinear_cyclic(double location, int32_t channel) {
+		int32_t truncated = (int64_t)location;
+		int32_t floor = truncated % sound_getSamplesPerChannel(this->buffer);
+		int32_t ceiling = floor + 1; if (ceiling == sound_getSamplesPerChannel(this->buffer)) { ceiling = 0; }
 		double ratio = location - truncated;
 		return this->sampleLinear(floor, ceiling, ratio, channel);
 	}
-	float sampleLinear_clamped(double location, int channel) {
-		int64_t truncated = (int64_t)location;
-		int64_t floor = truncated; if (floor >= sampleCount) { floor = sampleCount - 1; }
-		int64_t ceiling = floor + 1; if (ceiling >= sampleCount) { ceiling = sampleCount - 1; }
+	float sampleLinear_clamped(double location, int32_t channel) {
+		int32_t truncated = (int64_t)location;
+		int32_t floor = truncated; if (floor >= sound_getSamplesPerChannel(this->buffer)) { floor = sound_getSamplesPerChannel(this->buffer) - 1; }
+		int32_t ceiling = floor + 1; if (ceiling >= sound_getSamplesPerChannel(this->buffer)) { ceiling = sound_getSamplesPerChannel(this->buffer) - 1; }
 		double ratio = location - truncated;
 		return this->sampleLinear(floor, ceiling, ratio, channel);
 	}
 	void sampleMinMax(float &minimum, float &maximum, int startSample, int endSample, int channel) {
 		if (startSample < 0) { startSample = 0; }
-		if (endSample >= this->sampleCount) { endSample = this->sampleCount - 1; }
+		if (endSample >= sound_getSamplesPerChannel(this->buffer)) { endSample = sound_getSamplesPerChannel(this->buffer) - 1; }
 		if (channel < 0) { channel = 0; }
-		if (channel >= this->channelCount) { channel = this->channelCount - 1; }
-		int bufferIndex = startSample * this->channelCount + channel;
-		if (this->soundFormat == soundFormat_I16) {
-			SafePointer<int16_t> source = buffer_getSafeData<int16_t>(this->samples, "I16 source sound buffer in sampleMinMax");
-			for (int s = startSample; s <= endSample; s++) {
-				minMax(minimum, maximum, sound_convertI16ToF32(source[bufferIndex]));
-				bufferIndex += this->channelCount;
-			}
-		} else if (this->soundFormat == soundFormat_F32) {
-			SafePointer<float> source = buffer_getSafeData<float>(this->samples, "F32 source sound buffer in sampleMinMax");
-			for (int s = startSample; s <= endSample; s++) {
-				minMax(minimum, maximum, source[bufferIndex]);
-				bufferIndex += this->channelCount;
-			}
+		if (channel >= sound_getChannelCount(this->buffer)) { channel = sound_getChannelCount(this->buffer) - 1; }
+		int bufferIndex = startSample * sound_getChannelCount(this->buffer) + channel;
+		SafePointer<float> source = sound_getSafePointer(this->buffer);
+		for (int s = startSample; s <= endSample; s++) {
+			minMax(minimum, maximum, source[bufferIndex]);
+			bufferIndex += sound_getChannelCount(this->buffer);
 		}
 	}
 };
 List<Sound> sounds;
-static int createEmptySoundBuffer(const ReadableString &name, bool fromFile, int sampleCount, int sampleRate, int channelCount, int soundFormat) {
-	if (sampleCount < 1) { throwError("Cannot create sound buffer without and length!\n");}
+static int createEmptySoundBuffer(const ReadableString &name, bool fromFile, int samplesPerChannel, int sampleRate, int channelCount) {
+	if (samplesPerChannel < 1) { throwError("Cannot create sound buffer without and length!\n");}
 	if (channelCount < 1) { throwError("Cannot create sound buffer without any channels!\n");}
 	if (sampleRate < 1) { throwError("Cannot create sound buffer without any sample rate!\n");}
-	return sounds.pushConstructGetIndex(name, fromFile, sampleCount, sampleRate, channelCount, soundFormat);
+	return sounds.pushConstructGetIndex(name, fromFile, samplesPerChannel, channelCount, sampleRate);
 }
-int generateMonoSoundBuffer(const ReadableString &name, int sampleCount, int sampleRate, int soundFormat, std::function<double(double time)> generator) {
-	int result = createEmptySoundBuffer(name, false, sampleCount, sampleRate, 1, soundFormat);
+int generateMonoSoundBuffer(const ReadableString &name, int samplesPerChannel, int sampleRate, std::function<float(double time)> generator) {
+	int result = createEmptySoundBuffer(name, false, samplesPerChannel, sampleRate, 1);
 	double time = 0.0;
 	double soundStep = 1.0 / (double)sampleRate;
-	if (soundFormat == soundFormat_I16) {
-		SafePointer<int16_t> target = buffer_getSafeData<int16_t>(sounds.last().samples, "I16 target sound buffer");
-		for (int s = 0; s < sampleCount; s++) {
-			target[s] = sound_convertF32ToI16(generator(time));
-			time += soundStep;
-		}
-	} else if (soundFormat == soundFormat_F32) {
-		SafePointer<float> target = buffer_getSafeData<float>(sounds.last().samples, "F32 target sound buffer");
-		for (int s = 0; s < sampleCount; s++) {
-			target[s] = generator(time);
-			time += soundStep;
-		}
-	}
-	return result;
-}
-
-uint16_t readU16LE(const SafePointer<uint8_t> source, int firstByteIndex) {
-	return ((uint16_t)source[firstByteIndex])
-	     | ((uint16_t)source[firstByteIndex + 1] << 8);
-}
-
-uint32_t readU32LE(const SafePointer<uint8_t> source, int firstByteIndex) {
-	return ((uint32_t)source[firstByteIndex])
-	     | ((uint32_t)source[firstByteIndex + 1] << 8)
-	     | ((uint32_t)source[firstByteIndex + 2] << 16)
-	     | ((uint32_t)source[firstByteIndex + 3] << 24);
-}
-
-/*struct WaveHeader {
-	char chunkId[4]; // @0 RIFF
-	uint32_t chunkSize; //@ 4
-	char format[4]; // @ 8 WAVE
-	char subChunkId[4]; // @ 12 fmt
-	uint32_t subChunkSize; // @ 16
-	uint16_t audioFormat; // @ 20
-	uint16_t numChannels; // @ 22
-	uint32_t sampleRate; // @ 24
-	uint32_t bytesPerSecond; // @ 28
-	uint16_t blockAlign; // @ 32
-	uint16_t bitsPerSample; // @ 34
-	char dataChunkId[4]; // @ 36
-	uint32_t dataSize; // @ 40
-};*/
-static const int waveFileHeaderOffset_chunkId = 0;
-static const int waveFileHeaderOffset_chunkSize = 4;
-static const int waveFileHeaderOffset_format = 8;
-static const int waveFileHeaderOffset_subChunkId = 12;
-static const int waveFileHeaderOffset_subChunkSize = 16;
-static const int waveFileHeaderOffset_audioFormat = 20;
-static const int waveFileHeaderOffset_numChannels = 22;
-static const int waveFileHeaderOffset_sampleRate = 24;
-static const int waveFileHeaderOffset_bytesPerSecond = 28;
-static const int waveFileHeaderOffset_blockAlign = 32;
-static const int waveFileHeaderOffset_bitsPerSample = 34;
-static const int waveFileHeaderOffset_dataChunkId = 36;
-static const int waveFileHeaderOffset_dataSize = 40;
-static const int waveFileDataOffset = 44;
-int loadWaveSoundFromBuffer(const ReadableString &name, Buffer buffer) {
-	SafePointer<uint8_t> fileContent = buffer_getSafeData<uint8_t>(buffer, "Wave file buffer");
-	//uint32_t chunkSize = readU32LE(fileContent, waveFileHeaderOffset_chunkSize);
-	uint32_t subChunkSize = readU32LE(fileContent, waveFileHeaderOffset_subChunkSize);
-	uint16_t audioFormat = readU16LE(fileContent, waveFileHeaderOffset_audioFormat);
-	uint16_t numChannels = readU16LE(fileContent, waveFileHeaderOffset_numChannels);
-	uint32_t sampleRate = readU32LE(fileContent, waveFileHeaderOffset_sampleRate);
-	//uint32_t bytesPerSecond = readU32LE(fileContent, waveFileHeaderOffset_bytesPerSecond);
-	//uint16_t blockAlign = readU16LE(fileContent, waveFileHeaderOffset_blockAlign);
-	//uint16_t bitsPerSample = readU16LE(fileContent, waveFileHeaderOffset_bitsPerSample);
-	uint32_t dataSize = readU32LE(fileContent, waveFileHeaderOffset_dataSize);
-	if (audioFormat != 1) { // Only PCM format supported
-		throwError(U"Unhandled audio format ", audioFormat, " in wave file.\n"); return -1;
-	}
-	int result = -1;
-	if (subChunkSize == 16) {
-		if (dataSize > (buffer_getSize(buffer) - waveFileDataOffset)) {
-			throwError(U"Data size out of bound in wave file.\n"); return -1;
-		}
-		int totalSamples = dataSize / 2; // Safer to calculate length from the file's size
-		result = createEmptySoundBuffer(name, true, totalSamples, sampleRate, numChannels, soundFormat_I16);
-		SafePointer<int16_t> target = buffer_getSafeData<int16_t>(sounds.last().samples, "I16 target sound buffer");
-		SafePointer<int16_t> waveContent = buffer_getSafeData<int16_t>(buffer, "Wave file buffer");
-		waveContent.increaseBytes(waveFileDataOffset);
-		for (int s = 0; s < totalSamples; s ++) {
-			target[s] = waveContent[s]; // This part has to assume little endian because the value is signed. :(
-		}
-	} else {
-		throwError(U"Unsupported bit depth ", audioFormat, " in wave file.\n"); return -1;
+	SafePointer<float> target = sound_getSafePointer(sounds.last().buffer);
+	for (int s = 0; s < samplesPerChannel; s++) {
+		target[s] = generator(time);
+		time += soundStep;
 	}
 	return result;
 }
@@ -211,8 +91,7 @@ int loadSoundFromFile(const ReadableString &filename, bool mustExist) {
 			return s;
 		}
 	}
-	// Assuming the wave format until more are supported.
-	return loadWaveSoundFromBuffer(filename, file_loadBuffer(filename, mustExist));
+	return sounds.pushConstructGetIndex(Sound(sound_decode_RiffWave(file_loadBuffer(filename, mustExist)), filename, true));
 }
 
 int getSoundBufferCount() {
@@ -417,10 +296,10 @@ void sound_initialize() {
 					Player *player = &(players[p]);
 					int soundIndex = player->soundIndex;
 					Sound *sound = &(sounds[soundIndex]);
-					int sourceSampleCount = sound->sampleCount;
-					double sampleStep = player->speed * sound->sampleRate * outputSoundStep;
+					int sourceSampleCount = sound_getSamplesPerChannel(sound->buffer);
+					double sampleStep = player->speed * sound_getSampleRate(sound->buffer) * outputSoundStep;
 					if (player->repeat) {
-						if (sound->channelCount == 1) { // Mono source
+						if (sound_getChannelCount(sound->buffer) == 1) { // Mono source
 							for (int t = 0; t < requestedSamples; t++) {
 								PREPARE_SAMPLE
 								float monoSource = sound->sampleLinear_cyclic(player->location, 0) * envelope;
@@ -428,7 +307,7 @@ void sound_initialize() {
 								target[t * outputChannels + 1] += monoSource * player->rightVolume;
 								NEXT_SAMPLE_CYCLIC
 							}
-						} else if (sound->channelCount == 2) { // Stereo source
+						} else if (sound_getChannelCount(sound->buffer) == 2) { // Stereo source
 							for (int t = 0; t < requestedSamples; t++) {
 								PREPARE_SAMPLE
 								target[t * outputChannels + 0] += sound->sampleLinear_cyclic(player->location, 0) * envelope * player->leftVolume;
@@ -437,7 +316,7 @@ void sound_initialize() {
 							}
 						}
 					} else {
-						if (sound->channelCount == 1) { // Mono source
+						if (sound_getChannelCount(sound->buffer) == 1) { // Mono source
 							for (int t = 0; t < requestedSamples; t++) {
 								PREPARE_SAMPLE
 								float monoSource = sound->sampleLinear_clamped(player->location, 0) * envelope;
@@ -445,7 +324,7 @@ void sound_initialize() {
 								target[t * outputChannels + 1] += monoSource * player->rightVolume;
 								NEXT_SAMPLE_ONCE
 							}
-						} else if (sound->channelCount == 2) { // Stereo source
+						} else if (sound_getChannelCount(sound->buffer) == 2) { // Stereo source
 							for (int t = 0; t < requestedSamples; t++) {
 								PREPARE_SAMPLE
 								target[t * outputChannels + 0] += sound->sampleLinear_clamped(player->location, 0) * envelope * player->leftVolume;
@@ -470,3 +349,65 @@ void sound_terminate() {
 		}
 	}
 }
+
+void drawEnvelope(ImageRgbaU8 target, const IRect &region, const EnvelopeSettings &envelopeSettings, double releaseTime, double viewTime) {
+	int top = region.top();
+	int bottom = region.bottom() - 1;
+	Envelope envelope = Envelope(envelopeSettings);
+	double secondsPerPixel = viewTime / region.width();
+	draw_rectangle(target, region, ColorRgbaI32(0, 0, 0, 255));
+	draw_rectangle(target, IRect(region.left(), region.top(), region.width() * (releaseTime / viewTime), region.height() / 8), ColorRgbaI32(0, 128, 128, 255));
+	int oldHardY = bottom;
+	for (int s = 0; s < region.width(); s++) {
+		int x = s + region.left();
+		double time = s * secondsPerPixel;
+		double smoothLevel = envelope.getVolume(time < releaseTime, secondsPerPixel);
+		double hardLevel = envelope.currentGoal;
+		if (envelope.done()) {
+			draw_line(target, x, top, x, (top * 7 + bottom) / 8, ColorRgbaI32(128, 0, 0, 255));
+		} else {
+			draw_line(target, x, (top * smoothLevel) + (bottom * (1.0 - smoothLevel)), x, bottom, ColorRgbaI32(64, 64, 0, 255));
+			int hardY = (top * hardLevel) + (bottom * (1.0 - hardLevel));
+			draw_line(target, x, oldHardY, x, hardY, ColorRgbaI32(255, 255, 255, 255));
+			oldHardY = hardY;
+		}
+	}
+}
+
+void drawSound(dsr::ImageRgbaU8 target, const dsr::IRect &region, int soundIndex, bool selected) {
+	draw_rectangle(target, region, selected ? ColorRgbaI32(128, 255, 128, 255) : ColorRgbaI32(40, 40, 40, 255));
+	Sound *sound = &(sounds[soundIndex]);
+	int innerHeight = region.height() / sound_getChannelCount(sound->buffer);
+	ColorRgbaI32 foreColor = selected ? ColorRgbaI32(200, 255, 200, 255) : ColorRgbaI32(200, 200, 200, 255);
+	for (int c = 0; c < sound_getChannelCount(sound->buffer); c++) {
+		IRect innerBound = IRect(region.left() + 1, region.top() + 1, region.width() - 2, innerHeight - 2);
+		draw_rectangle(target, innerBound, selected ? ColorRgbaI32(0, 0, 0, 255) : ColorRgbaI32(20, 20, 20, 255));
+		double strideX = ((double)sound_getSamplesPerChannel(sound->buffer) - 1.0) / (double)innerBound.width();
+		double scale = innerBound.height() * 0.5;
+		double center = innerBound.top() + scale;
+		draw_line(target, innerBound.left(), center, innerBound.right() - 1, center, ColorRgbaI32(0, 0, 255, 255));
+		if (strideX > 1.0) {
+			double startSample = 0.0;
+			double endSample = strideX;
+			for (int x = innerBound.left(); x < innerBound.right(); x++) {
+				float minimum = 1.0, maximum = -1.0;
+				// TODO: Switch between min-max sampling (denser) and linear interpolation (sparser)
+				sound->sampleMinMax(minimum, maximum, (int)startSample, (int)endSample, c);
+				draw_line(target, x, center - (minimum * scale), x, center - (maximum * scale), foreColor);
+				startSample = endSample;
+				endSample = endSample + strideX;
+			}
+		} else {
+			double sampleX = 0.0;
+			for (int x = innerBound.left(); x < innerBound.right(); x++) {
+				float valueLeft = sound->sampleLinear_clamped(sampleX, c);
+				sampleX += strideX;
+				float valueRight = sound->sampleLinear_clamped(sampleX, c);
+				draw_line(target, x, center - (valueLeft * scale), x, center - (valueRight * scale), foreColor);
+			}
+		}
+	}
+	font_printLine(target, font_getDefault(), sound->name, IVector2D(region.left() + 5, region.top() + 5), foreColor);
+}
+
+}

+ 9 - 3
Source/tools/wizard/sound.h → Source/SDK/SoundEngine/sound.h

@@ -3,16 +3,16 @@
 #define MODULE_SOUND
 
 #include "../../DFPSR/includeFramework.h"
+#include "../../DFPSR/api/soundAPI.h"
 
-static const int soundFormat_I16 = 0; // Hi-fi bit-depth
-static const int soundFormat_F32 = 1; // Studio bit-depth
+namespace dsr {
 
 void sound_initialize();
 void sound_terminate();
 
 // Creates a single-channel sound using the generator function
 // generator takes the time in seconds as input and returns a value from -1.0 to 1.0
-int generateMonoSoundBuffer(const dsr::ReadableString &name, int sampleCount, int sampleRate, int soundFormat, std::function<double(double time)> generator);
+int generateMonoSoundBuffer(const dsr::ReadableString &name, int sampleCount, int sampleRate, std::function<float(double time)> generator);
 int getSoundBufferCount();
 
 int loadWaveSoundFromBuffer(const dsr::ReadableString &name, dsr::Buffer bufferconst);
@@ -36,4 +36,10 @@ void stopSound(int64_t playerID);
 // Stop all sounds at once
 void stopAllSounds();
 
+// Visualization
+void drawEnvelope(dsr::ImageRgbaU8 target, const dsr::IRect &region, const EnvelopeSettings &envelopeSettings, double releaseTime, double viewTime);
+void drawSound(dsr::ImageRgbaU8 target, const dsr::IRect &region, int soundIndex, bool selected);
+
+}
+
 #endif

+ 8 - 9
Source/soundManagers/AlsaSound.cpp

@@ -3,24 +3,21 @@
 //   Install on Arch: sudo pacman -S libasound-dev
 //   Install on Debian: sudo apt-get install libasound-dev
 
-#include "soundManagers.h"
-#include <alsa/asoundlib.h>
+#include "../DFPSR/api/soundAPI.h"
 #include "../DFPSR/base/simd.h"
+#include <alsa/asoundlib.h>
 
-using namespace dsr;
+namespace dsr {
 
-snd_pcm_t *pcm = nullptr;
+static snd_pcm_t *pcm = nullptr;
 static int bufferElements = 0;
 static Buffer outputBuffer, floatBuffer;
 static SafePointer<int16_t> outputData;
 static SafePointer<float> floatData;
 
-// Aligning memory to allow using the widest available floating-point SIMD vector.
-static const int soundBufferAlignment = DSR_FLOAT_ALIGNMENT;
-
 static void allocateBuffers(int neededElements) {
-	outputBuffer = buffer_create(neededElements * sizeof(int16_t), soundBufferAlignment);
-	floatBuffer = buffer_create(neededElements * sizeof(float), soundBufferAlignment);
+	outputBuffer = buffer_create(neededElements * sizeof(int16_t));
+	floatBuffer = buffer_create(neededElements * sizeof(float));
 	outputData = buffer_getSafeData<int16_t>(outputBuffer, "Output data");
 	floatData = buffer_getSafeData<float>(floatBuffer, "Output data");
 	bufferElements = neededElements;
@@ -121,3 +118,5 @@ bool sound_streamToSpeakers(int channels, int sampleRate, std::function<bool(Saf
 	terminateSound();
 	return true;
 }
+
+}

+ 5 - 1
Source/soundManagers/NoSound.cpp

@@ -1,6 +1,10 @@
 
-#include "soundManagers.h"
+#include "../DFPSR/api/soundAPI.h"
+
+namespace dsr {
 
 bool sound_streamToSpeakers(int channels, int sampleRate, std::function<bool(float*, int)> soundOutput) {
 	return false;
 }
+
+}

+ 4 - 2
Source/soundManagers/WinMMSound.cpp

@@ -1,13 +1,13 @@
 
 // Use -lwinmm for linking to the winmm library in GCC/G++
 
-#include "soundManagers.h"
+#include "../DFPSR/api/soundAPI.h"
 #include <windows.h>
 #include <mmsystem.h>
 #include "../DFPSR/implementation/math/scalar.h"
 #include "../DFPSR/base/simd.h"
 
-using namespace dsr;
+namespace dsr {
 
 static const int samplesPerChannel = 2048;
 
@@ -136,3 +136,5 @@ bool sound_streamToSpeakers(int channels, int sampleRate, std::function<bool(Saf
 	terminateSound();
 	return true;
 }
+
+}

+ 0 - 26
Source/soundManagers/soundManagers.h

@@ -1,26 +0,0 @@
-
-#ifndef DFPSR_SOUND_API
-#define DFPSR_SOUND_API
-
-#include "../DFPSR/includeEssentials.h"
-
-// Call this function from a separate thread in a sound engine to initialize the sound system, call back with sound output requests and terminate when the callback returns false.
-// The sound_streamToSpeakers function returns false if the backend could not be created, and true iff the backend completed all work and terminated safely.
-// Channels: The number of virtual speakers to send data to.
-//   How this is mapped to physical speakers depends on the system, because surround speakers may choose to play mono and stereo sound using only the front speakers.
-// sampleRate: The number of ticks per second for each channel.
-// soundOutput: A callback that requests length number of ticks generated by the sound engine and written in a packed format into the data array.
-//   The soundOutput function returns true iff the audio backend should keep fetching sound samples, and false iff the engine is done and ready for the call to sound_streamToSpeakers to return.
-//   data: The data array should be filled with sound samples in the -1.0f to 1.0f range, in indices from 0 to (length * channels) - 1.
-//     The audio backend is responsible for converting the 32-bit float samples into a bit-depth chosen by the backend.
-//     The backend is supposed to padd the SafePointer's range to at least be divisible by DSR_FLOAT_ALIGNMENT, which allow using both X vectors and F vectors.
-//       The F vector can be larger than the X vector if building for a SIMD extension that only supports the widest vector length for floating-point operations.
-//     Padding elements will not add to the time passed in the sound engine, for only played elements increment time.
-//   length: The number of ticks per channel. The total number of elements to write into data is channels * length.
-// How to use:
-//   Call sound_streamToSpeakers with desired channels and sampleRate from a separate thread.
-//   Handle callbacks to soundOutput by feeding the next packed sound samples and letting it return false when done.
-//   Close the thread and let the sound engine clean up resources.
-bool sound_streamToSpeakers(int channels, int sampleRate, std::function<bool(dsr::SafePointer<float> data, int length)> soundOutput);
-
-#endif

+ 63 - 0
Source/test/tests/FormatTest.cpp

@@ -0,0 +1,63 @@
+
+#include "../testTools.h"
+#include "../../DFPSR/base/format.h"
+
+START_TEST(Format)
+	{
+		Buffer data = buffer_create(8);
+		SafePointer<uint8_t> bytes = buffer_getSafeData<uint8_t>(data, "Integer encoding buffer");
+		format_writeU16_LE(bytes, 0);
+		ASSERT_EQUAL(format_readU16_LE(bytes), 0);
+		format_writeU16_LE(bytes, 1);
+		ASSERT_EQUAL(format_readU16_LE(bytes), 1);
+		format_writeU16_LE(bytes, 65534);
+		ASSERT_EQUAL(format_readU16_LE(bytes), 65534);
+		format_writeU16_LE(bytes, 65535);
+		ASSERT_EQUAL(format_readU16_LE(bytes), 65535);
+		format_writeI16_LE(bytes, -1);
+		ASSERT_EQUAL(format_readU16_LE(bytes), 0b1111111111111111u);
+		ASSERT_EQUAL(format_readI16_LE(bytes), -1);
+		format_writeI16_LE(bytes, -2736);
+		ASSERT_EQUAL(format_readI16_LE(bytes), -2736);
+		format_writeI16_LE(bytes, -32767);
+		ASSERT_EQUAL(format_readU16_LE(bytes), 0b1000000000000001u);
+		ASSERT_EQUAL(format_readI16_LE(bytes), -32767);
+		format_writeI16_LE(bytes, -32768);
+		ASSERT_EQUAL(format_readU16_LE(bytes), 0b1000000000000000u);
+		ASSERT_EQUAL(format_readI16_LE(bytes), -32768);
+		format_writeI16_LE(bytes, 0);
+		ASSERT_EQUAL(format_readU16_LE(bytes), 0b0000000000000000u);
+		ASSERT_EQUAL(format_readI16_LE(bytes), 0);
+		format_writeI16_LE(bytes, 1);
+		ASSERT_EQUAL(format_readU16_LE(bytes), 0b0000000000000001u);
+		ASSERT_EQUAL(format_readI16_LE(bytes), 1);
+		format_writeI16_LE(bytes, 375);
+		ASSERT_EQUAL(format_readI16_LE(bytes), 375);
+		format_writeI16_LE(bytes, 7216);
+		ASSERT_EQUAL(format_readI16_LE(bytes), 7216);
+		format_writeI16_LE(bytes, 32766);
+		ASSERT_EQUAL(format_readI16_LE(bytes), 32766);
+		format_writeI16_LE(bytes, 32767);
+		ASSERT_EQUAL(format_readU16_LE(bytes), 0b0111111111111111u);
+		ASSERT_EQUAL(format_readI16_LE(bytes), 32767);
+	}
+	// TODO: Test 32 and 64 bit reading and writing.
+	{
+		ASSERT_NEAR(format_bitsToF32_IEEE754(0b00000000000000000000000000000000u), 0.0f);
+		ASSERT_NEAR(format_bitsToF32_IEEE754(0b01000000101000000000000000000000u), 5.0f);
+		ASSERT_NEAR(format_bitsToF32_IEEE754(0b01000001001000000000000000000000u), 10.0f);
+		ASSERT_NEAR(format_bitsToF32_IEEE754(0b01000001101000000000000000000000u), 20.0f);
+		ASSERT_NEAR(format_bitsToF32_IEEE754(0b11000000101000000000000000000000u), -5.0f);
+		ASSERT_NEAR(format_bitsToF32_IEEE754(0b11000001001000000000000000000000u), -10.0f);
+		ASSERT_NEAR(format_bitsToF32_IEEE754(0b11000001101000000000000000000000u), -20.0f);
+		ASSERT_NEAR(format_bitsToF64_IEEE754(0b0000000000000000000000000000000000000000000000000000000000000000u),   0.0);
+		ASSERT_NEAR(format_bitsToF64_IEEE754(0b0100000000010100000000000000000000000000000000000000000000000000u),   5.0);
+		ASSERT_NEAR(format_bitsToF64_IEEE754(0b0100000000100100000000000000000000000000000000000000000000000000u),  10.0);
+		ASSERT_NEAR(format_bitsToF64_IEEE754(0b0100000000110100000000000000000000000000000000000000000000000000u),  20.0);
+		ASSERT_NEAR(format_bitsToF64_IEEE754(0b0100000001000100000000000000000000000000000000000000000000000000u),  40.0);
+		ASSERT_NEAR(format_bitsToF64_IEEE754(0b1100000000010100000000000000000000000000000000000000000000000000u),  -5.0);
+		ASSERT_NEAR(format_bitsToF64_IEEE754(0b1100000000100100000000000000000000000000000000000000000000000000u), -10.0);
+		ASSERT_NEAR(format_bitsToF64_IEEE754(0b1100000000110100000000000000000000000000000000000000000000000000u), -20.0);
+		ASSERT_NEAR(format_bitsToF64_IEEE754(0b1100000001000100000000000000000000000000000000000000000000000000u), -40.0);
+	}
+END_TEST

+ 2 - 449
Source/test/tests/RandomTest.cpp

@@ -84,7 +84,7 @@ START_TEST(Random)
 		// Making sure that the random generator does not break backward compatibility by changing its behavior.
 		RandomGenerator generator = random_createGenerator(1223334444u);
 		List<int32_t> generatedValues;
-		for (int32_t h = 0; h < 10000; h++) {
+		for (int32_t h = 0; h < 100; h++) {
 			generatedValues.push(random_generate_range(generator, -200, 200));
 		}
 		ASSERT_EQUAL(generatedValues,
@@ -93,453 +93,6 @@ START_TEST(Random)
 			-18, 102, 137, 166, -188, 130, -41, -100, -29, 160, 68, -171, -84, -76, 27, -151, -168, -91, 171, 155,
 			-139, 46, 185, -140, -60, -173, 0, 81, -73, 36, -33, 145, -31, 73, 152, -107, -140, -63, 181, 176, -142,
 			-122, 97, 102, 151, -110, 19, 103, -78, 21, -82, -89, -77, -69, -14, 27, -24, 6, 94, 186, -185, -71,
-			-184, 127, -97, 173, -179, 70, -74, 13, 3, 11, 129, 116, -58, 35, -175, 116, -69, 51, 117, 87, -99, -105,
-			-52, 120, 54, 46, -57, -67, -19, 200, 83, 29, 17, 27, 158, -46, 161, 66, -17, 127, 51, -38, 27, 106, 21,
-			74, 143, 79, 116, 45, -156, -92, -191, -191, 129, 165, 78, -3, -197, -32, 7, 108, -116, -165, 141, -142,
-			83, 74, 7, 127, 191, -77, -194, 147, 124, -149, 89, -131, 186, 87, 24, -116, -6, 164, 185, 178, 77, -138,
-			40, 187, 110, -79, -200, 10, 93, 108, 34, 159, 34, -140, -102, -192, 119, 136, 89, 137, 21, 99, 122, -98,
-			177, -103, -197, 178, -149, 114, -5, 148, -94, -65, -92, 152, 78, -183, 90, -148, -130, -130, 120, -134,
-			113, 186, -99, -24, -65, 168, -55, 6, -175, -174, 11, -135, 6, -84, -185, -170, -71, -62, 99, 98, -163,
-			94, -97, -109, 194, -48, 173, -172, -83, 113, 190, -148, 42, 6, -61, -142, 106, 77, 44, -52, 167, 162,
-			143, -137, -43, 151, 45, -125, -62, 9, -139, 160, 118, 141, -86, 0, -110, 120, -42, -8, 196, 63, -37,
-			-24, -186, 168, 86, 93, -189, -107, 92, 32, -113, 187, -56, 181, -24, -8, 123, -59, -153, 64, -36, -164,
-			-23, -170, -195, 116, -180, -30, 194, 41, 197, 26, -58, -131, 88, 199, -110, -181, -87, -12, 43, -16, -90,
-			105, 144, 89, 105, -46, -71, -131, 105, -175, 33, -82, -135, 56, 197, -7, 104, -104, -161, 105, -37, 164,
-			-138, -15, 3, -4, -174, -122, -157, -141, -95, -71, 109, 159, -59, 136, 31, -12, -180, 44, -143, 159, 30,
-			-82, 184, 88, -150, 183, 62, 105, 36, 176, 108, -195, 193, 96, -80, -197, -188, 194, 44, -176, 185, -54,
-			-34, 188, 101, 92, -39, 83, 91, -145, 63, -183, 91, -79, -195, -100, -94, -129, 152, 89, 105, 161, -116,
-			-72, -76, -139, 22, 100, 44, 116, -163, 43, 31, 186, -51, 176, 149, -98, 147, 17, -155, 86, 163, 18, -9,
-			-60, -195, -190, -180, 61, 147, 169, -5, 91, -12, -122, 162, 176, -76, 148, 44, 9, 166, 131, -69, -101,
-			138, 157, -156, -151, -59, -47, -80, -93, 60, -7, -65, 101, -161, -82, 148, 107, 43, -144, 136, 179, -14,
-			-164, 164, 47, -186, 54, 54, -34, 41, -56, -111, 42, 11, -6, 135, 147, 139, 32, -32, 86, 189, -63, 185,
-			-175, -47, 136, 175, -6, -164, -98, 67, -25, 129, -192, 151, -136, 167, -27, 77, -96, -22, 115, -96, 41,
-			62, 185, -112, 48, -90, -137, -90, 32, -96, 103, -172, -21, -31, -195, 24, 176, -99, -67, -18, -44, -34,
-			-21, 137, -142, -81, -51, 82, -82, -106, 96, -38, -98, 6, 87, 109, 185, -105, 70, 186, -74, -109, -101,
-			123, 3, 132, -172, -133, -6, -16, -152, 83, 42, 24, 184, -10, 175, 23, 97, 178, -69, 194, -114, 80, 123,
-			-84, -75, -106, -115, 170, 76, 120, 38, 177, -76, 70, 195, 5, -118, 42, 171, 24, 118, -103, 159, -101,
-			100, -146, -104, 108, 56, 32, 82, 197, 79, 194, -25, -34, -63, 168, -60, -189, 185, -103, 109, 21, -187,
-			-14, 69, 137, -191, -194, 66, 116, -105, 149, -11, 23, -144, -5, -180, -161, -42, -67, -14, -31, 22, -96,
-			-81, -1, -170, 150, 45, -136, -184, 176, 105, 48, 53, 159, 113, 113, -145, -170, -191, -51, 8, -143, 164,
-			50, -33, 94, 128, 79, -51, 53, -126, 166, -121, -78, -10, 129, 142, 87, -46, -183, 24, 84, -77, 81, 13,
-			163, -123, -141, -4, -85, -29, 2, -198, 11, -92, -117, 89, 24, -114, 88, -2, -185, -46, 29, 182, 85, 155,
-			181, -94, 147, 120, -44, -196, 117, 151, -194, -157, -82, -42, 45, -165, -129, -152, 102, 84, 168, 42, 0,
-			-136, 80, 12, 32, 108, 91, 108, -114, -82, -22, -74, -199, -173, 152, 185, -79, 186, -75, 100, 183, -41,
-			177, -116, 126, -176, -130, -72, -184, 74, 68, 5, -106, 98, 170, -96, 39, -164, 80, 69, 197, -48, -9, -90,
-			-116, -192, -14, 160, 9, 176, 163, 137, 123, -197, 25, 164, -194, 145, -39, 55, 60, 154, -1, 112, -52,
-			-148, -107, 111, 125, 120, -159, -1, 92, 20, 175, 125, -190, -30, 19, -106, 11, -94, 137, 198, -106, -30,
-			-22, 33, -177, 120, -43, -102, -36, 54, 115, 144, -171, -188, -102, -165, -148, -1, -17, 35, 70, 79, 176,
-			-107, 130, 15, 9, 103, -192, 15, 73, -196, 47, -64, 179, -167, 33, 127, 37, -90, -67, -126, 7, 31, -114,
-			60, -36, 52, -67, -168, 166, 10, 106, 159, -41, -172, 86, -69, -124, -47, -39, -32, 38, 79, -141, 97, 174,
-			125, 111, -32, 159, 143, -14, 42, 25, 194, -160, 166, -8, -99, -1, 170, -154, -196, 86, -16, 25, 29, 9,
-			158, -153, -172, 1, 93, 69, -60, 181, -112, -132, 182, 94, 131, 129, -189, 181, -76, 58, -59, 74, 51, 165,
-			-37, 170, 115, 180, 98, -47, 193, 126, 191, -156, 85, 127, 54, 180, -4, 165, -54, 93, 114, -180, -182, -66,
-			180, 28, 155, 139, -35, 195, 118, 11, 4, -6, 154, 129, -112, -40, -93, -38, -77, 73, -186, -23, -25, 17,
-			-88, -194, 107, -83, 105, -119, 103, -48, 73, 80, -107, 29, -107, -52, 120, 10, 174, -160, -167, 121, 47,
-			-4, -182, -150, 15, -129, 191, -198, -18, 62, -55, -62, -87, -179, -149, 46, 24, 11, 12, -72, -85, -58, 72,
-			-65, -162, 90, -139, 181, 141, -151, 2, 97, -109, -161, 18, 32, -46, 12, -140, 148, -166, 141, -160, -124,
-			-66, 77, -98, 133, -141, 72, -47, -85, 50, 59, -130, 132, 194, 110, 111, -29, 127, -200, -157, 1, -178,
-			-167, 21, -90, -27, -44, 71, 68, 130, -88, -45, 94, -175, 69, 27, -155, 131, -6, 47, 184, 74, 30, 158,
-			-37, -30, 92, 66, -18, -62, -1, 191, -115, 149, 84, 173, 98, -21, 50, -128, -95, -117, -146, 191, 56,
-			156, -21, -185, 76, -139, -57, -150, 179, -98, 198, -190, -148, 48, 167, 29, -152, -114, -43, 0, 73, -46,
-			154, -200, -39, -78, -131, -77, 143, 27, -16, 171, -129, -198, -78, 184, 82, 163, 46, -142, 66, 96, 61,
-			28, 195, 64, -200, -198, -117, -100, -102, 182, -76, -153, -4, 92, -14, 36, 17, 92, -91, -56, -107, 43,
-			14, 156, 117, -32, -153, -16, 25, 124, -8, 40, 84, 93, 100, -126, 63, -8, 123, -81, -50, 90, 38, -166,
-			-150, 145, 73, 13, -12, 67, -134, -45, 20, -199, 51, -192, -145, 173, -187, 104, 42, 7, 162, -19, 160,
-			-158, 87, 50, -100, -108, -167, 58, -170, 61, 56, -78, 162, 171, 199, 169, -164, -53, 67, 110, -150, -46,
-			-47, 80, 73, 191, -94, 195, 126, 8, 32, -112, -64, 47, 173, -81, -140, 158, 41, 146, 67, 148, -160, 48,
-			-143, -183, 127, 1, 78, -10, 63, -178, -154, -65, 99, -63, 110, -50, -41, 106, -106, 84, -100, 109, 77,
-			-113, 186, -194, -30, -9, -32, -102, 173, -42, -78, -135, 167, 199, -142, -58, 107, -8, -199, -73, -54,
-			66, 70, -107, 90, -159, -90, 139, 147, -50, 44, 156, 98, 80, -104, -108, 92, -96, -142, 71, 78, -7, -181,
-			-42, 197, 66, 54, -164, 101, -32, -150, -87, -105, 78, 151, -40, 189, 103, -74, 1, 0, 180, 150, 108, -181,
-			-130, -136, 112, -124, 125, 31, 116, -175, 189, -162, -2, -20, 66, 48, 13, 51, 126, 115, -154, -158, 195,
-			143, -59, -29, 75, 117, 112, 195, -29, -101, -104, -137, -164, -156, 65, -107, -60, -106, -136, -171, 86,
-			-36, 156, -152, 57, 129, -138, -77, -74, 151, -190, -139, 131, 158, -100, 184, -7, 164, -96, 21, 25, 99,
-			-151, -131, -126, 108, 79, 1, -11, 10, -31, -197, 29, 131, -94, 105, -112, 175, 33, -94, 190, 194, -10,
-			-150, -148, -63, -179, -70, 43, 19, -23, -48, -168, 93, 177, 69, 129, 11, -159, 59, 167, 26, 181, 162, 106,
-			137, -154, 52, -54, -171, -93, -1, -15, 114, 129, 35, 117, 178, 131, 48, -42, 160, -162, -166, -150, -111,
-			-103, 103, 27, 14, -84, 24, -126, -154, 116, 77, 35, 167, 106, 74, 4, -95, -7, -37, -156, 154, -43, 33, -29,
-			-179, -113, -113, -44, 70, 91, 128, 130, -34, 168, 64, 125, 130, -155, -120, -192, -6, -126, -143, 177, 27,
-			23, -11, 88, 178, 44, 37, -77, 56, -72, 7, 129, -75, -138, -50, -142, 84, -80, -74, 114, 183, 118, 131, 2,
-			-195, 164, -87, -153, -30, -3, 179, 120, 98, 68, -36, -28, 72, 52, 2, -168, 116, 132, -71, -41, -50, 151,
-			-36, 3, -194, 30, 42, -56, -123, 41, 72, -141, -106, 54, 84, -155, -187, -181, -81, -83, -152, -42, -7, 90,
-			23, -200, 91, -166, 128, 27, 13, 121, 170, 53, -56, 184, 160, -54, 41, -184, 80, 164, 172, 144, 163, 144,
-			-39, -78, -142, -18, 80, 5, 195, 116, -131, -47, -32, -24, -60, 47, -90, -197, 59, -1, 156, -163, -181, 116,
-			128, -79, 105, 188, 71, 39, -148, 37, -26, -113, -19, 5, 77, 47, 177, 135, -38, 84, -124, -109, 121, -51,
-			-43, -66, -44, -119, 200, -97, 29, 152, 108, 4, 41, 110, 58, -84, -63, -93, -190, 40, 31, 18, 147, -79, 170,
-			195, -61, 10, 189, -142, 148, 51, -2, 35, -157, 149, 111, -115, 185, 41, -91, -3, 4, 122, 23, -165, -64,
-			-126, 159, -147, 190, 126, -38, -39, -13, -148, -35, -116, -93, -5, -97, -25, 7, 60, -3, -172, 38, 104, 105,
-			152, 105, -34, 160, 111, -132, -194, 117, 114, -197, 119, 187, -27, 105, -90, 10, 182, -127, 111, -45, 159,
-			-133, -46, -96, -170, 179, -135, 155, -71, 147, 143, 27, -106, 80, 155, 143, 56, -23, -141, 28, -182, -4,
-			-23, 86, 21, 95, 94, -196, 195, -24, -200, 14, -148, 153, 107, -82, -55, 109, -159, -174, -42, 88, -128, 111,
-			-151, 8, 71, -19, 145, -84, -186, -28, 92, -190, 32, 64, -26, -136, -57, -120, -70, -169, 163, -171, 112,
-			112, 137, -20, 144, -199, 101, -150, -153, 37, 8, -20, 165, 75, -75, -20, -18, -103, 136, -186, 41, 102, -101,
-			-192, 1, 83, -74, -57, -71, -54, 113, 195, 75, 160, -109, 105, 36, -51, -49, 147, -77, 183, 33, -31, 8, -180,
-			99, 94, 186, 16, -81, -15, 46, 4, -182, -53, -65, 13, 193, 101, 175, -175, 53, 26, -94, -198, 152, 174,
-			-182, 131, -121, -15, 141, 53, -86, 3, 81, -51, 61, 61, -119, 138, 189, -85, -126, -121, -161, -62, -78,
-			-182, 112, -51, -97, -171, 151, -182, -165, -171, -115, 53, 129, -183, -3, 169, 146, 173, 4, -156, 164, 12,
-			-69, 157, 104, 76, -46, 12, -163, 100, 91, 110, -179, -26, -84, -17, 26, 165, 166, 125, -79, 134, -132,
-			-117, -102, 104, 30, 191, -23, -74, 79, 117, -162, 10, 9, -40, -105, -5, -129, 129, -107, 25, -66, -194,
-			-43, 38, 62, -113, 143, -96, 33, -164, 64, 167, -158, 144, -79, 14, -88, 17, -54, 151, 50, -47, -36, 130,
-			35, 36, -112, 103, -101, -96, 173, 56, -66, -45, 81, 136, 16, -121, 104, 198, -68, -66, -165, -144, 104, 126,
-			187, -28, -106, 79, 1, -126, 137, 115, 136, -9, -126, -172, 65, -160, -188, -44, 192, -134, 40, 7, -44, -115,
-			-5, -63, 155, -108, -128, -137, -189, 73, -102, -61, -14, -165, 166, 186, -172, -119, 153, -155, -38, 134,
-			-181, 146, -152, 43, -20, -113, -177, -182, 66, -16, 30, 40, 132, 158, -107, 18, -52, -76, -17, -40, -139,
-			97, -15, -150, 12, 58, -153, 196, 50, -80, -117, 172, -77, 62, -94, -135, 39, -185, 88, -18, -130, 184, 199,
-			-86, 26, -116, 184, 189, -47, -177, 63, -34, -171, -57, -78, 29, -159, -137, -105, 2, 130, 142, -110, 119,
-			-1, 2, 79, -10, -25, -139, -164, 108, -139, -165, -95, -200, -59, 84, -138, -42, -127, 69, 137, 153, -28,
-			-35, -32, 179, -28, 115, -37, -82, 44, -158, 116, 25, -115, 93, 22, -104, 128, -107, 112, 200, -196, -133,
-			-33, -178, 159, 39, -198, 74, 158, 151, 13, -100, -92, -176, 169, 169, 146, -20, -199, 140, 26, -12, -91,
-			-122, -136, 112, 192, -2, 61, -85, 133, 1, 8, -80, -165, 64, 93, 181, -58, -184, -53, -167, 161, 38, 95, 86,
-			-118, 41, 155, 182, -187, -163, -68, -78, -155, -70, 193, 59, -68, 46, -123, 144, -171, -157, -71, 190, 162,
-			8, -40, -56, -8, 25, -177, -62, 149, -29, -74, -145, -195, 114, 8, -83, -47, -23, -124, 85, 151, -64, 8, -163,
-			-157, 111, 157, 72, -110, -163, 100, -6, 187, 59, 109, 178, -147, -168, -180, -39, -124, -62, -159, -88, -108,
-			106, 89, 128, -156, -200, -73, -179, 48, -51, -186, -170, -4, -67, 30, -140, 199, 19, 137, 135, 132, -185, 147,
-			176, -3, 57, -84, 70, 127, 152, 102, 163, -42, 146, 152, 77, 145, -1, 75, 155, -195, -79, 79, -183, -89, -191,
-			18, 66, 113, 160, -182, -162, 62, 103, -169, -147, 78, -141, -87, 129, 61, 120, -162, -163, -47, -72, 129, -42,
-			-168, -24, -106, 162, -107, 6, 94, 66, -3, 24, -7, 114, -65, 159, 73, -22, 14, -137, 37, -194, 196, 35, -46,
-			194, -101, 153, -151, 157, 1, -95, 18, 145, 69, -14, -62, 193, -78, -106, 48, 41, -101, 137, 68, -92, 18, -43,
-			166, 123, 18, -180, -130, -197, -93, -58, 72, -49, -189, 127, -30, 23, 148, 93, 51, 34, 75, 6, -4, -53, 156,
-			106, -30, -79, 6, 80, 25, 89, 9, -165, -153, 66, 120, 35, -185, -139, 128, -33, 31, 94, 170, 179, 96, 98, -28,
-			31, 33, -26, -140, -86, -34, 199, 158, -146, 8, -187, -95, 98, 52, -146, -58, 116, -121, 15, 168, 120, 146,
-			-165, -199, -152, -153, 108, 31, -162, 57, -12, -73, -105, 24, -36, 50, 161, 137, -117, 76, 138, 54, 49, -166,
-			-109, -3, 117, -66, -56, -143, -159, -112, 30, 109, 35, -119, 117, -17, -116, -107, -166, -67, 28, -167, -163,
-			92, -97, 175, 26, 8, -130, 57, 25, 42, 192, 29, -123, 182, 92, 71, -114, -149, -194, -9, 85, 170, 38, 80, 178,
-			-31, 166, 36, -93, -44, 47, 192, -5, 180, -126, -110, 70, -55, 146, 8, 22, 83, -110, -70, -4, -3, 36, -55, -186,
-			-83, -134, -83, 160, -62, -95, -153, 143, -96, -2, -66, -4, -189, -135, 9, 6, -152, -32, -124, 145, 3, -106,
-			-81, 140, 48, 171, 57, 120, -174, -180, 50, -150, -199, -97, -40, 130, -84, 71, 51, 174, 22, 34, -128, 137,
-			-197, 39, -160, -56, -55, -113, -138, -125, 38, -86, -50, -80, 10, 39, -28, 190, -122, -20, -81, -4, -73, -53,
-			-32, -19, -87, 183, 82, 185, 131, -24, 80, -163, -148, -184, 51, 17, 104, -8, 46, 151, -166, 100, 64, -166,
-			-199, 37, -75, 147, 186, -93, -156, -159, 71, 178, -106, 39, -156, 178, -104, 93, -125, -78, 77, -70, 81, -30,
-			-199, -95, 59, 66, 29, 58, 186, -175, 144, 10, -71, -112, 62, -166, 19, 188, 80, -160, -184, -177, -122, 159,
-			-53, 161, -163, 27, -191, -104, 87, 99, -65, 49, -142, 52, 127, 31, -9, 176, -151, 158, -50, 139, 156, 172,
-			-10, -16, 61, -142, -87, -95, -6, -170, 196, 177, 196, 173, 147, -147, 193, 83, 152, -120, 46, -176, -45, 103,
-			129, -154, -3, 124, -122, -117, 104, 195, 81, 68, 37, -53, -164, 26, -50, 32, -64, 43, -111, 29, 112, 54, -98,
-			13, 141, 105, -180, 17, 21, 131, -154, 118, -89, -191, -93, 50, -64, 86, -138, 3, -155, 193, -95, -151, -38,
-			-135, 190, 46, 23, -87, 131, 128, -62, 122, 24, -172, 111, -143, -126, 93, -75, 130, -78, -158, 153, 76, -114,
-			-107, 170, 19, 137, -142, 13, 27, 94, 132, -169, -106, -96, -69, 115, 97, -197, 9, 101, -110, 135, -148, -13,
-			-59, 84, -134, -197, 69, 167, 70, 168, 177, -90, 68, 140, 82, -115, 181, -29, -163, 70, 59, 197, -41, 54, -91,
-			14, -100, -174, 161, -77, -80, 133, 14, 60, 75, -41, 173, -21, 165, -133, 176, -96, -102, -4, 154, -117, -88,
-			178, 75, 25, 19, 76, -150, 199, 192, -5, 162, 36, -138, -2, 138, -86, -105, 131, -79, -179, -173, -122, -161,
-			-11, -181, -17, 126, -8, 112, 63, 198, 49, 54, -139, 31, -32, -164, 59, 158, 24, 133, -118, -19, 105, 54, -38,
-			-50, 179, -133, 118, 168, 144, -66, 7, -127, -176, 17, -17, -195, -56, 139, 100, -200, 170, -145, 13, 109, -18,
-			177, -197, -53, 158, 157, -93, -124, -103, 91, 182, -67, 73, -59, 188, 99, -86, -193, 1, 89, -130, -150, 18,
-			-124, 154, -134, 93, 172, 161, 139, -5, -158, 49, -152, 137, 97, 193, -21, 56, -81, -130, 27, -199, 92, 107,
-			-185, -150, 94, -114, 64, 99, 190, 39, 25, -69, -179, 79, 30, 128, 140, 68, -121, -81, 166, -185, -113, -93,
-			-142, 6, 160, 82, -11, 196, 135, -80, -71, 31, 43, -146, 96, 19, 53, -112, -71, -152, 61, 112, -49, -46, -30,
-			-187, -157, -127, -77, -20, -124, 157, -143, -41, 82, 102, 132, -91, 84, -117, 89, -55, 101, -99, -164, 13, 25,
-			100, 71, -33, -74, 125, -124, -118, -147, 34, -80, -6, 157, -167, -129, -124, -24, -158, 4, -186, -122, -144,
-			142, -161, -74, 99, 191, 27, -144, 86, 5, 193, -112, -10, -148, 183, 126, -132, -189, -78, -14, 84, 193, -63,
-			-18, 77, 74, -118, 70, 89, -71, 1, 175, 36, 1, 105, -50, -148, 42, -145, 143, -151, 132, 8, -146, -169, 11, 68,
-			62, -41, 152, -147, 127, -98, 61, 167, 119, 58, -152, 133, 97, -31, 125, 35, 89, -74, 115, -169, 28, 185, -141,
-			8, -145, 167, -195, -156, 171, 100, 54, -198, 148, -166, -112, 171, -187, -73, -86, 4, -78, 151, -37, -138, 132,
-			-177, 78, 55, 144, -177, -164, 23, -33, 161, -198, 103, -190, 90, -78, 50, -7, 126, 122, -165, 107, 124, -97,
-			12, -42, 1, -164, 25, -44, -42, -24, -14, 136, 15, 183, 7, -113, -121, 147, 111, -36, -53, -65, -85, 38, 79,
-			-103, 191, -15, -90, 53, 78, 160, 99, 138, -174, -13, 194, -11, 27, -69, -109, 173, 78, 150, -27, -67, -112,
-			-96, 135, 112, -33, 66, 126, 148, -37, 32, -144, 147, -11, 169, -177, -59, 79, 45, -103, 72, 80, -13, -89, -104,
-			32, -71, -161, 23, -40, 89, -44, 85, -21, -187, 66, 120, 172, 100, 182, -195, 114, 37, -113, -90, -115, -190,
-			-109, 184, 103, 18, 151, 169, 56, -150, -100, -102, 36, -116, 181, 8, 72, 131, 183, 77, -59, 117, 12, 50, -28,
-			-180, 42, 22, -137, 128, -182, 92, 112, -198, 138, 101, 149, -98, 160, -144, 17, 15, 139, -138, 68, -134, 171,
-			-49, -185, -113, -5, -128, 96, -173, -115, 156, 57, -36, -115, 121, 0, -149, 58, -159, 58, -6, -109, 121, 12,
-			-25, -56, -6, 147, 104, 161, -45, 53, -4, 117, -171, 173, 180, -10, 114, -75, -1, -143, -195, -29, -198, -53,
-			-105, 165, -84, -53, -109, 88, 5, -125, -63, 172, 181, -71, -114, 144, 194, 92, 97, -70, -167, -65, -84, 53,
-			-131, 138, 11, -82, -11, 165, 193, 189, -178, 66, 74, 172, -69, 189, 22, -55, 56, -39, 138, -197, 182, 103,
-			188, 98, 25, -24, -114, 113, -99, 173, 146, -77, 3, -70, 164, -80, -48, 110, -12, -122, -43, -33, 12, 143,
-			-52, 24, 128, 45, -39, -70, 86, -197, 161, -133, 194, 74, 176, -164, 37, 185, 37, -94, -103, -84, -117, 120,
-			130, 67, -59, -101, -50, -84, -143, -97, 20, 0, -189, 4, -177, -193, -176, 200, 41, 29, 124, -36, 85, 79,
-			-147, -194, -37, -147, -74, -174, -78, -166, -90, 24, -97, -140, -8, -64, 195, -13, -27, 21, -131, 194, -20,
-			-152, -57, 155, -103, 168, -23, 157, -87, -64, -181, 120, 196, -177, 175, 98, 151, -188, 59, 193, 168, -64,
-			-144, -99, -111, 65, -65, 195, 194, 46, -92, 137, 181, 121, 178, -145, 140, 194, -178, -26, 11, 163, 59, 122,
-			33, -141, 21, 173, 126, 184, -109, -64, -23, 175, 195, -142, 177, -93, -169, 60, -65, 124, 90, -108, -170,
-			104, 180, -37, -72, -186, -159, -12, 98, -125, 87, 67, 28, 27, -29, 42, 30, -42, 164, -47, 114, 180, -177,
-			86, -113, 164, 2, -148, -117, -104, -88, 180, -88, -87, -127, -17, -41, -17, -179, 144, 162, 110, -104, 100,
-			38, 98, -160, -192, -68, -67, 121, -34, 158, -170, -139, -74, -107, 96, -24, 178, -102, 165, 137, -32, -4,
-			147, -58, 8, 61, -48, 198, -74, 58, -55, -16, 109, -191, -163, -137, 136, -178, -102, -124, 39, -156, 98,
-			-183, 52, 129, 2, -121, -79, -170, 101, -171, 74, -86, -79, -58, 85, -157, 63, 2, -194, 62, -81, -116, 62,
-			170, 52, 64, -65, 159, -115, 194, 27, -163, -61, 10, 146, 130, -190, -92, 54, 32, 124, 107, 35, -145, -23,
-			109, 150, -109, -8, 1, -142, 91, 62, 134, -191, 12, 178, -192, -117, 176, -26, -165, 120, 24, -152, -174,
-			-170, -55, 97, -46, -125, -130, -23, -4, -152, 112, 3, -97, 146, 31, -106, 69, -60, 134, -175, -174, 170,
-			140, 180, 83, -199, 8, -34, -81, -99, 58, 154, 159, 96, 51, -184, -156, 152, -49, -181, 1, 170, -10, -170,
-			148, -108, -47, -123, 125, 152, 9, -64, 28, 166, 7, 35, 159, 199, 143, -95, -88, -107, -69, 35, -51, -120,
-			-114, 164, 132, 142, 183, -123, -159, 124, 1, 56, -102, -34, -32, -82, 18, 143, -66, -56, -63, -80, 123,
-			175, 49, 46, 188, 33, -151, -131, 168, 0, 174, 91, 192, -185, -53, -139, -11, 75, 83, 47, 146, -15, 130,
-			-190, -150, -60, 180, 24, -50, 113, 91, -8, -121, 172, -178, 166, -37, -140, -138, 166, 166, -134, -175,
-			-116, 188, -99, -144, -171, -130, -92, -164, -43, -141, -68, -5, 6, 159, -123, 155, 109, 63, 28, -159, 192,
-			-150, 123, -114, 85, -22, -133, 28, 99, 125, 87, -20, -120, 148, 95, -173, 65, 161, 157, 16, 188, -79, 172,
-			-90, -48, -162, 103, -3, -104, -92, -178, 76, 166, 193, 196, -109, -4, -161, 112, 166, -144, -70, 95, -73,
-			-176, -139, 194, -11, -131, 72, -16, 104, -54, -181, -180, 144, 102, -23, 107, -44, 28, 163, -146, -155, 130,
-			76, 113, 76, -125, -124, -101, -178, -176, 6, 126, 186, 61, -127, 119, 36, -167, 65, 131, -174, -24, 85, 12,
-			81, -193, -110, 130, -180, -176, -22, -188, -91, 157, 152, -9, 13, 84, 70, 133, -178, -19, -120, -26, -95,
-			89, 1, 137, 1, 81, 30, -37, -53, -67, 194, 32, 124, 185, 32, 165, -151, -118, -21, 39, -1, 115, -56, 29, -86,
-			161, 7, 141, -114, -16, -142, -96, 46, 52, -118, -198, -154, 173, -151, -143, -121, 122, 20, -168, -114, 122,
-			173, 127, 0, -22, -137, 99, -159, -125, -62, -70, -171, 76, 66, -103, 86, 60, -195, -21, 90, 38, 118, -195,
-			-184, -125, -197, -42, -54, -33, -61, -74, -112, -14, 27, -137, -116, -192, -36, 155, -168, 82, 10, 197, 167,
-			173, -23, -83, -186, 22, 189, -20, -120, 158, 168, 9, -90, 132, -15, 91, 112, 62, -115, -143, 176, 120, -122,
-			-122, 159, -141, 4, -186, -182, 97, 130, -110, -30, 68, 70, 149, -32, 83, -138, -79, 147, 92, 50, -91, -198,
-			-134, -182, -25, 64, 69, 47, -167, -102, 29, -91, -118, -194, -97, 141, 191, -56, 74, -103, 10, 61, 105, -14,
-			-164, -78, 169, -133, -132, 64, -186, -139, 86, 22, 184, 154, -125, -114, -162, 196, -62, 116, 117, -77, 58,
-			-35, 25, -164, -170, -30, 101, -10, -116, 117, -106, -141, 109, 124, 30, 118, -74, 128, 32, -184, 189, -35,
-			-194, 72, 159, 8, 116, -157, 120, -31, -90, 27, -89, 15, 158, -54, -63, -84, 46, -33, 8, -108, 51, 107, -162,
-			13, 26, 154, -156, 31, -108, -35, -34, -124, 109, 160, 164, 159, -176, 92, 171, 5, 24, -101, 144, -191, -183,
-			-71, 90, 135, -55, 89, -12, -20, -59, 71, -192, -14, -121, -30, -112, -19, -5, 177, 187, 129, 121, -199, 37,
-			-185, 65, -189, 191, -12, -144, -34, -109, -128, 33, -128, 132, -163, 178, 198, 28, 4, 111, 9, 136, -151, -161,
-			-60, 52, -141, -144, 20, 153, -76, 152, 143, -126, 17, 148, -148, 126, -43, 105, -114, -186, -129, 6, 173, 47,
-			37, -186, 158, 65, -79, -20, -6, -77, 173, -73, 23, 96, 170, 108, -86, 145, -44, -177, -28, -18, -78, -15,
-			-159, -107, 59, 119, -165, 51, -189, -57, 123, -26, 97, 139, -107, -45, 106, -160, 162, 93, -143, 148, -106,
-			-4, -30, 159, 12, 99, -107, 145, -126, 72, 114, -9, -163, -91, -185, 70, -173, 95, 153, -22, 196, 178, 114,
-			112, -97, -197, -86, 45, -29, 149, -106, 143, 142, -109, -42, 192, -164, -193, 102, -34, 86, 143, 28, -200,
-			-26, -110, -183, -73, -31, -41, 141, 179, -18, -135, -62, -43, 74, 18, 142, 152, 153, -75, -34, -154, 87,
-			125, 46, -9, -31, -173, -42, -70, 182, 70, 2, 27, -33, 77, -41, -2, -154, 191, 41, -194, 114, 173, -61, 192,
-			-4, -27, -73, 56, 116, -131, -132, 67, -146, -179, 186, 102, 108, 34, -182, 62, 150, -175, 118, -190, 146,
-			-141, 172, -105, 158, -18, -21, 39, -128, 14, 138, -16, -81, 115, 144, 62, -124, 10, -71, -164, -66, 63, -41,
-			-89, 63, 86, -189, 9, 34, -115, -143, 35, 20, -33, 200, 116, -174, -1, -56, -139, -134, -144, 173, 196, -75,
-			-156, 136, 96, -92, 84, 109, -129, 199, -143, 123, 50, 51, 60, 130, -75, 111, 111, -44, -75, -57, -174, 38,
-			164, 172, -40, 60, 170, 132, -33, 111, 162, 8, -116, -88, 94, 182, 157, -123, 142, 20, 40, 161, -106, 61,
-			-155, 165, -113, 25, -56, 67, -200, -74, 140, -41, 124, 47, -8, -199, -8, 198, -35, -153, 98, -41, -171, -111,
-			-190, 171, -48, -143, 200, 47, -110, -97, -67, 191, 73, 158, -22, 36, 126, -41, 77, 137, 37, 4, 131, -43, 144,
-			-49, 24, 25, 99, 75, -189, -132, 118, 79, 109, 97, 163, 106, -199, 167, 42, -131, 170, 168, -81, -67, -15, 156,
-			101, 194, -45, -105, 24, 55, -173, -26, 128, -128, -82, 125, -129, 78, 29, -109, 153, -141, -146, -79, -108,
-			67, 92, 115, 154, 196, -6, -75, 12, -125, -34, -135, -80, -30, 0, -23, 72, -196, 138, 179, 133, 53, -121, 90,
-			-138, 82, -44, -38, 188, -20, -116, 115, -142, 69, -200, -110, -95, 8, 100, 6, -36, -127, 67, -119, 46, -137,
-			160, -8, 60, -82, 92, 134, 40, -46, -172, -48, 119, 111, 179, -109, -192, -69, -7, 173, -55, -51, -16, 58, 147,
-			-154, 46, -13, -117, 49, 164, -160, -109, -69, -140, 2, 97, -51, -57, 44, -48, -114, 5, 146, 120, -128, 18,
-			-185, -116, 145, 30, 39, -147, -34, 91, 62, -117, -62, -37, -25, 51, -173, 86, -63, -59, 27, 69, -63, 24, -38,
-			27, -181, -186, 116, 82, 28, 129, -19, 1, 68, -1, -199, 132, 172, 195, 112, -70, 188, -92, -92, 16, 104, 75,
-			-151, -141, 121, 98, -183, 19, 10, -177, -156, -139, 131, 167, -111, -87, 96, -42, 137, -187, -109, -46, 27,
-			-37, 138, -175, 7, -130, -23, -143, 61, 44, -150, 120, -1, 23, 196, 124, 42, -147, 174, 17, 189, 121, -27, 7,
-			99, 94, -38, 6, -6, 171, -80, -93, 180, -106, 47, 102, -105, -15, 94, 154, -192, 141, -104, 117, 13, 96, 77,
-			89, -171, 172, 1, 176, 37, 129, 183, -153, -192, -130, -91, -91, -140, -33, -151, -63, -45, -15, -79, -77,
-			-48, 194, 6, 66, 180, -99, 48, 10, -150, 134, -169, 177, 54, -87, 8, -6, -75, 93, -33, -184, -92, 174, 87, 17,
-			-104, -103, 166, 82, -30, -32, 61, 140, -161, 49, -71, -5, 58, -34, -75, -112, -193, -88, 145, 195, -134, 175,
-			-179, -76, -154, 138, -138, 175, 137, -17, 51, 41, -163, 170, 21, -200, 111, -9, 152, -82, 28, 112, -63, 24,
-			-152, 80, 119, -93, -175, 135, 69, 1, 92, -193, 199, -98, -56, -67, 194, 50, 79, 142, 186, 106, -4, 162, -43,
-			129, 3, 22, 77, 150, -140, 74, -63, -56, 61, 36, -179, 85, 175, -58, 46, 162, -193, -107, -22, -173, 95, -54,
-			-30, -161, -118, 79, -15, 10, -59, 5, 127, 7, -174, 156, 49, 54, -76, -182, -72, 53, 49, 22, -155, 72, -78,
-			-28, -146, -90, 96, 177, -51, 88, 162, -42, -194, 73, -48, -199, -192, 157, 100, 137, -145, -108, -30, 31, 155,
-			65, 125, -41, 13, -150, -70, -169, 86, 100, 20, 130, 75, -114, 102, -123, 171, 109, 70, 18, 24, -15, -54, -148,
-			-58, 71, -70, 61, 16, -91, -42, -112, 70, 190, -4, 149, -116, -178, 125, -129, -79, -121, -162, 166, -83, -33,
-			193, 99, 119, 17, -108, -66, -24, -63, 13, 142, 183, -50, -191, 39, 173, -44, 107, -8, -130, -112, 67, -137,
-			155, 164, -99, 69, -161, 53, -143, -200, 92, 103, -173, 148, 62, -47, 87, -121, 17, -83, 121, -181, -70, 6,
-			-162, 14, 40, -187, 10, -95, 59, 129, -128, 128, 194, 95, -24, -155, -195, 114, -147, 47, 176, -191, 24, -90,
-			187, 81, -128, 105, -186, 183, 110, 193, -190, -124, 153, -40, -190, -147, 169, -64, 34, -23, -60, -65, -168,
-			-199, 173, -104, 174, 43, 177, 42, -13, 4, 5, 80, 113, 73, -28, -5, -190, -27, 12, 166, 1, 161, -80, 192, 38,
-			-125, -61, 22, -153, -171, -183, 41, -92, 69, 22, -154, -68, -187, -152, 198, -63, -52, -31, 58, 141, -162,
-			-138, 99, 188, -94, -121, -39, 22, 175, -151, -188, 78, -198, -109, 27, 193, -4, 48, 98, -82, -67, -172, -120,
-			8, 164, 172, 178, 104, -109, 176, 37, -131, 177, -194, 114, -110, 27, 27, -22, 18, -21, -110, 63, -154, 153,
-			156, -182, -145, 44, -105, 163, -115, -176, 49, 22, -99, -94, -175, 17, -107, 166, -7, -61, 36, -181, 65,
-			-164, -15, -128, 34, 44, 122, -77, -156, -74, -145, -199, 112, 85, 136, -40, 65, -115, 83, -175, 188, 180,
-			55, -93, -95, -149, 180, -21, -158, 153, 38, -100, -86, -69, 143, -160, -164, 121, 105, 192, 168, 138, -175,
-			-200, 112, 35, -146, 178, 168, 181, -111, -144, -85, -59, -163, -56, 3, 40, -139, -98, 0, 167, 65, 53, -124,
-			65, 174, 84, -191, -109, -191, -171, 120, 83, 86, 145, 182, -87, 193, 37, 58, -187, -79, -70, -166, -126,
-			-22, 50, 67, 80, 193, -184, -151, 139, -55, -69, -127, 12, -44, -95, -84, -72, -41, 92, -174, 183, -117, -143,
-			107, 159, 147, -5, 180, 21, 140, 126, -65, 80, -182, 0, -14, 93, -185, 118, 140, 76, -12, 141, -16, -74, 52,
-			177, 23, 163, -156, 76, -18, -14, 147, -100, 137, 161, 17, -175, -163, 14, 110, -87, 25, 108, -150, 12, -120,
-			15, 7, -64, -179, -175, 141, 132, 150, 63, 79, 41, 29, -168, -31, -162, 153, -107, 137, 64, -102, 146, -136,
-			-152, -105, -119, -118, 17, 101, 153, 11, 10, 119, 166, -7, -65, 71, 149, -47, 131, 23, 82, -200, -127, -142,
-			61, -162, 26, -11, 174, 75, -189, 162, 55, 156, -9, 184, 139, -96, 199, -77, -145, 117, -172, -190, 92, 16,
-			-35, -187, -198, 128, -51, 135, 190, -198, -160, -200, 159, -154, -179, -79, -20, -31, 91, 124, 188, 65, 36,
-			-115, -75, 144, 123, 183, 173, 104, 172, 146, 88, 45, 97, -82, 94, 15, -79, -115, 5, -54, -5, 171, 121, 51,
-			158, 64, -91, -38, -155, -156, -124, -130, -52, 25, -82, -104, -1, -127, 14, 61, 60, -113, -24, 110, 45, -172,
-			54, -3, 64, 102, 95, -23, 84, -120, -153, 34, -141, -27, 11, -164, -128, 169, 60, 99, 78, -145, 15, 38, -200,
-			110, 20, 47, 123, -165, -61, 112, 122, 38, 65, -52, -109, -191, 176, -108, 76, -185, 53, -113, 134, 1, -90,
-			-137, 133, 160, -5, 16, 97, 106, -144, -10, -99, -88, -153, -45, -138, -132, -128, -160, 68, -152, 128, 166,
-			192, 146, -183, 90, -110, 199, 66, 85, 44, -49, 174, -41, -143, -83, -135, -22, 145, 31, -159, 11, -77, 124,
-			141, -174, 75, -31, 164, -30, 198, -191, 71, 15, 48, 115, 151, 137, -197, -123, -75, -88, 191, 21, -46, -9,
-			164, -35, -101, -114, -42, -116, -5, 110, -26, -34, 176, 65, 153, -81, -171, -125, -45, -184, -6, 176, -148,
-			-25, -132, 6, 126, -35, 199, -23, -150, -38, -33, 172, -26, 1, 22, -19, -83, -144, 148, 88, -86, -137, 4, 81,
-			38, 125, -131, 45, -60, -140, -81, 7, -96, 177, -32, -60, -141, 93, 165, 0, 200, 98, 139, 184, 76, 109, -155,
-			165, 4, 150, 10, -1, 107, -184, 84, 153, -86, 74, 85, 184, 67, 79, -103, 92, -50, 66, -118, -5, 184, -163, 93,
-			159, 83, 122, 135, 188, 103, 175, 179, 152, 94, 197, -66, -157, 101, -113, -200, 115, -152, 196, 38, -120, 20,
-			-187, -69, 46, 128, -199, 163, 143, -9, -122, -25, 118, -178, -193, -196, 130, 178, -158, -200, -148, 6, 12,
-			-40, 30, 158, 17, 63, 71, 151, -155, 183, -188, -47, -187, -38, 55, 187, -8, -75, -119, -193, -52, 1, 177, 169,
-			40, -154, 99, 143, 161, 147, -124, 39, -128, 71, -74, 90, -101, 173, 115, -103, 92, -83, 119, -193, -108, -188,
-			-141, 74, 152, -157, -79, -20, -46, -81, -46, 37, -25, -156, 192, 32, -73, -30, 155, 121, -52, 12, 108, -176, 82,
-			-12, -62, -129, 14, -65, -9, 128, -150, -31, 141, -83, 133, -48, -103, -66, 139, -17, 186, -184, 171, 166, 118,
-			-189, -166, -99, -98, -134, -27, 132, -67, -89, -88, -49, 98, 129, 62, 87, 190, -67, -134, 78, -23, -113, 117,
-			-179, 117, -191, -98, -149, -184, -183, -64, 1, 134, -163, -69, 63, 197, 190, 5, 127, 72, 177, -49, -111, 190,
-			-46, -157, -185, 166, -86, 15, -49, -78, -111, 101, 41, -117, -86, -108, -40, -104, 160, -118, -58, 48, -30, 126,
-			191, 150, 158, -140, -65, -100, -80, -101, -200, 178, 193, 162, -138, 71, -177, -6, 60, 66, 169, 35, -10, -104,
-			-4, -157, -33, 153, 5, 97, 178, 85, -143, -135, 113, -174, 151, 178, -17, -33, 111, -45, -196, -200, -163, -102,
-			172, 131, 166, 190, 60, -83, 154, 35, 90, -24, 89, 146, -123, 199, 172, 71, -129, -65, -107, 89, -105, 162, 18,
-			101, -123, -36, -172, -144, 13, 136, -86, 49, -77, -1, 151, -68, -22, -130, -130, 165, 37, -16, -174, 13, -17, 115,
-			-165, -153, -194, 41, -132, -9, 31, -5, -23, 123, -193, 9, 56, -93, 6, 58, -137, 136, 146, 106, 187, 178, -38,
-			-100, -195, 103, 28, -164, 199, 168, 134, 86, -129, 86, 30, -85, 13, -3, -62, -45, -31, 105, -125, -158, -190,
-			-22, 158, -75, 158, 39, 82, 21, 53, 147, 121, -24, 189, 68, 170, -168, -200, -31, 18, 166, 7, 160, -157, 8, -122,
-			152, 114, -106, 68, -69, -87, -170, -169, -111, 85, 142, 129, -155, 52, 26, -195, -33, -72, -147, -99, 162, -11,
-			-79, 60, -167, -98, 176, -130, -172, 23, 107, -100, 83, -84, -7, -35, -104, 115, 5, 102, 61, 61, 135, -54, 167,
-			133, -7, -94, -162, 65, -150, -135, 199, -57, -123, -114, 20, -22, -36, -133, -192, -113, -113, 149, -144, -162,
-			-94, 5, 58, 153, 143, -119, 67, 166, -161, 169, -35, 108, 138, -86, -181, 26, 37, 171, 35, 57, 35, -97, -52,
-			-134, -135, -118, -174, -74, -92, -186, 48, -75, -18, -156, 92, 35, 6, 148, -93, 191, 71, 128, -196, -185, -65,
-			187, 28, -93, 86, 74, -100, -40, 39, 134, -181, -11, -139, 80, -131, 158, 113, -14, 177, 168, -176, -188, 98,
-			197, -4, -131, 7, -127, -141, -24, 138, 49, -16, 93, -147, -193, -189, 47, 83, 63, -65, -3, 150, 194, 55, -143,
-			-57, 178, 139, 139, -88, -195, -61, -108, 98, 111, -179, 184, -105, -165, -37, 157, -195, 122, 194, 56, -81, -40,
-			-35, -143, -87, -114, -61, 6, 19, 153, 193, 59, 98, -53, 47, -35, -19, 88, -9, -108, 21, 110, -172, 58, 24, -151,
-			-121, -160, 79, -136, 168, 136, -156, 190, -36, -198, 146, -181, 60, -40, 190, -50, -114, 62, -79, -127, -56,
-			-160, 111, 106, 25, 56, 187, 50, -82, 183, 17, 180, 92, 116, 23, 175, -70, -164, -170, 12, 44, -40, -167, -45,
-			15, 164, -153, -11, 127, -64, -1, 125, -192, 158, 65, 108, 45, -131, -148, -134, 76, -6, 171, -30, -152, -110,
-			-42, 178, -62, -21, 170, -100, -41, 32, -54, -80, 15, -173, -112, -30, -18, 97, 84, 108, -197, 114, 93, 192, 102,
-			-85, -164, -112, -136, 138, -77, 118, 120, 117, -13, 130, 147, -120, -105, -44, -21, -32, 181, -122, -35, -149,
-			184, -139, 50, -165, -32, -96, 108, -134, 69, 92, 69, -98, -154, -181, 50, -53, 142, 40, -141, 27, -44, -168,
-			109, 112, 184, 148, -110, 79, 169, -32, -11, -83, 27, -169, -107, 128, 27, -121, 180, 164, -102, 26, 147, 155,
-			123, -10, 41, 56, 136, -69, -129, -165, 189, 12, 0, -163, 128, -174, 115, -132, 64, -195, -1, 115, -105, 176,
-			181, 88, -67, -32, -88, -76, 186, -105, 15, 183, 73, 53, 27, -94, -92, -32, -108, -106, 191, -86, 26, 75, -58,
-			92, 90, 122, 35, -117, -17, 126, 136, 80, 87, -190, -152, -94, 114, -55, 95, -150, -88, 111, -168, 147, -16, 185,
-			-31, -62, 144, 124, -126, -8, -76, -94, 103, 63, -63, -18, 68, -107, 116, -127, -106, 171, -120, 151, -149, -172,
-			-64, -76, -90, -12, 18, -174, -70, -191, -174, 189, -145, -64, 145, 181, 161, -126, -66, 183, -136, 10, 102,
-			-146, -57, 9, -39, -131, 66, 86, -148, 145, 98, 144, 176, 149, -115, 53, -73, -18, 21, -172, 84, 16, -163, 19,
-			143, -146, 177, 184, -104, 165, 194, -145, 157, -63, -16, 15, -13, 80, 80, 27, 140, 40, -158, -54, 17, -34, -59,
-			121, 34, 197, 38, 149, 189, -28, -3, 167, 21, 191, 196, 115, -56, 167, -100, -140, 96, 59, 37, 90, -93, -191,
-			-63, -144, 85, 194, 50, 74, -33, 158, -3, -163, 85, -102, -126, 151, -157, -83, -147, -129, 13, 195, 124, -134,
-			114, -90, -76, 120, 147, -135, -97, -140, -157, 180, -171, 161, -162, 183, 35, -48, -153, 89, 5, 140, 195, -75,
-			-144, -145, 141, -105, 99, -43, 157, -22, -136, 142, -105, 138, 85, 98, -103, -137, 10, -62, 1, 149, 135, -186,
-			125, 179, -120, 94, -159, -16, 27, -30, 17, 145, 85, -198, 35, 172, -145, -12, -56, 157, 178, 81, 105, -99, -80,
-			-102, 10, -116, 49, -144, 119, -140, -93, -186, -53, -102, -23, 135, 137, 91, 133, 183, -35, -37, -153, -171,
-			104, 194, 71, 118, -144, 19, 161, 149, -32, 79, 128, -102, -121, -135, -180, 37, -54, 40, 78, 12, -10, 30, 156,
-			166, -18, 193, -141, -115, 185, -39, 19, -71, -41, 47, 182, -199, -110, -148, -148, 8, 1, -48, 174, 81, -50,
-			-149, -38, 110, -43, 141, -117, 153, 71, -125, -40, 194, -60, 23, 151, 95, 86, 91, -55, 100, -97, -71, -46, 120,
-			74, 174, 164, 51, 119, 71, 132, -182, 147, 54, -83, -105, -136, -120, 113, -109, -17, 21, -9, -19, -119, 85,
-			-134, 74, -81, -153, -18, -170, 73, 25, -133, 90, -120, -98, 14, -136, 18, 146, -77, -170, -23, 59, -92, 131,
-			-145, 176, -73, -147, 149, 104, -121, -31, 180, 14, -101, -121, 174, -131, 78, 126, 189, 83, -23, -29, 11, -130,
-			87, -182, -64, -143, 162, 171, -183, -30, 188, 34, -139, 77, -133, -110, 17, -121, -119, 140, -67, 55, -198, -59,
-			191, 133, 166, -1, 73, 166, -145, -102, 131, -41, 96, -116, -9, -184, 156, 108, 157, -98, 183, 0, 19, -155, -145,
-			43, -2, -63, -27, -109, 148, -62, 22, -32, -101, -190, -64, 72, -67, -12, 37, -122, 75, -37, -10, 69, 4, 140,
-			-132, -42, -179, -138, -25, 5, -87, 2, -87, -160, -175, -152, 150, -169, -36, -127, -137, 30, -200, -20, 114,
-			-26, -99, -79, -14, -72, 138, 108, 13, -190, -7, 185, -21, -33, 152, 131, -185, 42, 72, 137, 49, -19, 1, 56, 81,
-			91, 193, -128, 30, 119, 142, 171, -14, 44, 117, 5, 79, -158, 126, 26, -68, -86, -19, 115, -145, -53, 25, 100, 154,
-			2, -31, -71, 124, 18, 30, 42, -4, 119, 72, 55, -138, -157, 110, -188, -30, -89, 198, -105, -24, 28, 153, -83, 85,
-			-85, 39, 66, -29, 85, 121, 8, -97, 23, -163, -165, 101, 41, -87, -192, -145, -118, -123, 175, -178, -153, 30, -143,
-			-109, 73, 130, -38, 125, -105, 194, 141, 113, 46, 166, -114, 53, 33, 44, 42, 17, 18, -154, -45, -140, 137, -72,
-			106, -70, -39, -85, 119, 51, -70, 162, -29, -52, 131, -3, -84, 50, 92, -120, 95, -197, -43, 93, 99, 69, 135, -109,
-			44, -121, -2, 53, 113, -153, 187, -187, 45, 132, -130, -112, -117, 156, -44, -67, -149, 84, 82, 66, -85, 190, -163,
-			-113, 21, 125, -62, 42, -160, 27, 113, -117, 63, -104, -78, -159, 187, -11, -186, 188, -196, -157, 189, 125, 158,
-			147, -186, 52, 14, 42, -46, 99, 154, -12, -72, -12, 137, 87, -155, 162, 128, 121, 49, 170, -99, 155, 137, -141,
-			-66, -152, -108, -23, 158, -189, 64, -76, -107, 88, -13, -47, -119, -58, 17, 98, 175, -133, 157, -15, 30, 36, 184,
-			-73, 154, -100, -55, 19, 96, -183, -96, -19, 162, -98, 101, 190, 149, -39, 169, -181, -4, 25, -106, -53, 96, -130,
-			-189, -104, 148, -179, -34, 196, -198, -26, -140, 5, -111, 70, 68, -2, 139, 197, -175, -151, 115, -120, -24, 116,
-			-21, 147, -79, -80, -143, -48, -126, 58, -122, 4, -172, 27, -118, 74, -106, 101, -145, -37, -167, -23, -45, 121,
-			3, -141, -110, -169, -135, -193, 48, 184, 194, -178, -137, 162, 153, 8, -93, -60, 151, -199, 8, -69, 36, -109, 140,
-			-118, 44, -195, -193, -125, 13, 8, -169, -52, 36, -45, -147, -183, -98, 2, -160, -131, 195, -33, 29, 95, -145, -162,
-			-200, -38, 142, -164, -27, 26, 81, -169, 91, -106, -188, -160, 12, 96, 146, -111, 137, 62, 111, 29, 197, -198, -195,
-			-87, -188, -59, 33, 16, 86, -151, 126, -4, -44, 100, -192, -30, -56, -161, -113, 64, -161, -12, -174, -20, 168, 23,
-			-59, 165, 108, 96, 20, -40, 36, 104, -12, -145, 176, -12, -77, -155, -51, 29, -62, 134, 64, -40, -126, 19, 138, -24,
-			189, 75, -75, 12, -151, -65, 56, 124, -118, -190, 43, 164, -97, 113, -121, -19, 101, -43, 35, 144, -103, -184, -65,
-			-151, 95, -139, 160, -21, 194, -138, 2, 57, 3, -187, 122, -34, -130, -26, 130, 128, 46, -197, 69, -120, -96, -183,
-			-78, 175, 52, -32, -54, -143, -94, -138, 120, 9, 186, 182, -54, 141, 74, -82, -54, -200, -12, -187, 95, -9, -106,
-			130, -17, 118, 132, -80, -70, -188, 26, 107, -157, 137, -98, -171, 96, -7, 22, -116, -41, 89, -124, -15, 179, 198,
-			130, 52, -170, -25, -35, 57, 115, 165, -200, -130, 32, 41, -164, -16, -116, 56, 133, 69, -47, -25, -70, 121, -22,
-			-73, 114, 155, 35, 13, 30, 46, 101, -153, -87, -31, -127, 193, -180, 56, 148, 148, 187, -135, 172, 70, 175, 25, 3,
-			-73, 134, 173, 170, 21, 195, 34, -171, -153, -19, -84, 88, 85, 8, 118, 13, 70, -120, -11, 185, 125, 115, -70, -77,
-			-192, 64, 79, 156, -193, -18, -171, 188, 77, -109, 52, 22, -5, -150, -121, -130, 66, 168, -59, 22, 190, -103, -104,
-			-115, 80, -54, 109, -12, -177, 2, 171, -29, 156, -33, -19, 174, 39, 36, -41, 8, -160, 129, 80, -11, -135, -198, -187,
-			-80, -62, 171, -179, 93, -64, -78, -170, 168, -77, 31, -116, 5, -135, 81, -145, 80, -151, 100, 45, -66, 180, -142,
-			-47, 176, -52, 60, 2, 122, -40, 33, -169, -38, -133, 89, -157, 92, 13, -6, -128, 73, 30, -15, -197, 151, -45, -112,
-			58, 37, 174, -173, -134, -90, 138, 61, 76, -41, 165, 140, 97, -153, -123, -114, -9, -60, 108, 153, -181, 142, 47,
-			-132, -187, -113, -190, 166, -95, 99, -177, -102, -67, 44, 46, -199, -17, -33, 80, 142, 184, -193, 42, -188, 183,
-			-42, -76, -182, -32, -198, -29, 181, -12, 128, 166, 31, -70, -15, 121, -2, 38, 46, -50, -36, -123, 171, -109, -47,
-			146, -148, 135, -91, 116, -133, -169, 94, -4, -117, -137, 155, 148, -158, 192, -93, 113, -146, -151, 121, 131, -151,
-			167, -199, 138, -11, -174, -120, 136, -69, 141, 121, -34, 149, -141, 84, -95, 119, 63, -93, 38, -51, -11, -129,
-			-44, -59, 135, -97, 4, 149, -182, -141, -109, 162, 42, -91, -162, 64, 117, -102, 145, -102, 11, 93, 143, 28, 172,
-			175, -110, -164, -187, -200, 71, 83, 57, -15, -31, 41, -53, 36, 176, -21, -5, -75, -110, -146, -174, -183, 88,
-			-71, 169, 161, 19, 130, 22, 188, 19, -102, 116, 26, -88, -38, -33, -37, -110, -88, -1, -195, 45, 46, -76, -133,
-			200, -36, -63, -15, -102, -132, 145, -200, 124, -109, -152, 120, -184, -159, 4, -82, 38, -152, -146, 196, -32,
-			137, 81, 32, 181, -160, -154, 113, -12, -12, -83, -5, 55, 36, -112, -30, -43, 117, -133, 83, -111, 31, 33, -79,
-			63, -9, 90, 113, 147, -124, 84, 181, -53, 101, -127, -67, 66, 42, 129, 124, 56, -48, -128, 155, -5, 191, 39, -68,
-			183, -187, 6, 13, -113, 142, 102, 48, 196, -196, -123, 31, -142, 140, 111, 127, 72, 186, -179, -155, 41, 143,
-			-181, -85, -86, -184, -57, 109, -23, -132, 189, -135, 165, 23, -12, 83, 151, -105, -6, 100, -91, 79, 196, -153,
-			-18, 101, 177, 33, -144, -167, -183, 64, -126, 20, 56, 144, 22, -40, 142, -9, 159, 151, -95, -28, -169, -158,
-			106, -87, 30, -54, 167, 135, -127, -104, -112, -125, -56, 42, 98, 71, -1, -116, 192, -86, 79, 0, 79, 144, 124,
-			154, 45, -194, -116, 90, -77, 13, 63, -103, 36, -67, 148, 39, -25, 60, 105, 20, 139, -21, -152, 164, 55, 10, 194,
-			-179, 20, -65, -120, 149, 42, -155, 136, 196, -4, 2, -101, -22, -170, -151, 151, -121, 126, 66, -94, 169, 176,
-			-55, -140, 12, -200, -68, 154, 142, -149, -88, -13, 165, -199, -3, -9, -183, 153, 0, -153, 36, -159, 24, 156,
-			-23, -196, 58, -59, 34, -173, -191, 43, 197, 172, -131, -83, -180, -74, 170, 91, 162, -189, -145, -108, -131,
-			119, -121, -73, -145, -180, 64, 108, -43, -61, -52, 60, -12, 12, -185, -189, 54, 176, -164, 158, -51, -8, 107,
-			-138, -35, -143, 165, 106, -15, -194, 10, -52, 120, -104, -162, -179, -90, -149, -155, 170, -136, 169, -103, -55,
-			18, -21, -124, -32, 130, -129, -28, 113, 171, 30, 129, -70, -170, -26, 63, -33, -78, -60, -12, 69, -66, 22, 46,
-			27, 4, -49, 71, 146, -6, -84, 99, 49, -189, 63, 155, 66, 18, 80, 185, 196, -57, -114, -21, 121, -114, -142, 131,
-			-180, -24, -49, -198, 41, 69, 124, 146, -58, 33, 136, 100, 145, 81, -23, -60, -92, -96, 66, 65, -140, -40, 104,
-			168, 64, 133, 82, 62, -113, -168, -93, 53, -55, 55, -62, -26, 152, -192, -106, -154, 153, -23, -34, -43, 75, 67,
-			-2, 149, 46, 25, 108, -42, 159, -56, -197, -78, 19, 140, -135, -122, 125, -199, 164, 93, -161, -178, -84, 25,
-			-193, -62, -192, -174, -161, 152, -112, 112, -196, 48, -81, 158, 55, 41, -2, 131, -15, 167, -71, -169, -190, -197,
-			-133, 82, 53, 43, 8, -30, -72, 92, -118, 151, -136, 67, 139, -112, 136, 195, -51, -195, -92, 26, -73, -195, 132,
-			7, -78, 111, 165, -96, -137, 133, 50, -121, 11, -179, 198, 96, 19, 159, 112, 127, -71, -55, -91, 4, 38, -156, -114,
-			50, 102, -180, -80, 133, -134, 196, 181, -3, 105, 15, -68, 67, -199, -36, -92, -111, -198, -148, 183, -200, 127,
-			-161, 79, -154, 102, 51, 26, -92, -127, -3, -7, -9, -30, -17, -137, -40, -101, -41, -84, -183, 29, -68, 197, -31,
-			68, -195, -71, -133, 184, -141, -147, 116, 180, 189, 115, -168, 177, 47, -52, -42, 97, -43, -176, 67, -163, -111,
-			125, -171, 99, -119, 66, -36, 0, 182, -63, 70, -59, 39, 41, 8, 98, -150, 162, -12, 29, -83, 41, 75, 99, -62, -135,
-			176, -104, 89, 65, -10, 95, 133, -56, 8, 176, -63, 22, 180, 76, -173, 66, 151, 69, -54, -26, 128, 194, 174, 112,
-			-112, 149, -51, -39, -99, 84, -182, 180, 28, 42, -123, 0, -30, 27, 92, -54, 78, 199, 40, -117, 141, -55, -109,
-			-126, -45, -140, -66, -81, 146, -123, -145, -118, -168, -74, -179, 172, -132, -126, 110, -39, 28, 171, -184, 21,
-			9, -181, 132, -185, 134, 86, -109, 88, 121, -14, -198, 40, -149, 142, -34, -199, 3, -44, -79, 194, 159, -172, -97,
-			-18, -17, -163, 48, 89, -48, -154, -162, -26, 61, -168, -2, 186, -120, -66, 64, 26, 47, -199, -120, -98, 12, -58,
-			-151, -120, -199, 93, 96, -83, -40, -50, 49, 10, 83, -63, -109, 146, 42, 25, -75, -152, 125, 121, -185, 140, -116,
-			67, 5, 156, 43, -12, -27, 199, 31, -70, -5, 55, -120, 31, 118, -74, 43, -120, -76, -190, -146, -134, -104, 94,
-			105, 184, -171, 191, -102, -172, 19, 77, 63, 3, 88, 33, -72, -181, -185, 160, 155, 106, 200, 76, -42, 140, 47, 47,
-			180, 63, 13, 160, 43, -180, -192, -57, -114, -152, 171, 116, -90, 17, 144, -40, -59, 63, -166, 187, -30, -60, 83,
-			20, 147, 20, 108, 109, 153, -103, -151, 82, 89, 53, -29, -151, 26, 109, 110, -93, -140, -176, 90, -101, 130, -71,
-			-182, -4, -178, -137, 37, 129, 80, 71, -178, 160, -22, 49, 34, 181, -185, 92, 112, -10, -6, 3, 105, 11, -157, -38,
-			127, -10, -176, -82, -37, -188, 132, -28, 186, 78, -39, -152, 19, 145, 198, 167, -29, 15, -42, 76, -158, -100,
-			-51, -24, 74, 118, 95, 164, 118, 36, 191, -23, -106, -141, 154, 172, -31, 13, 134, -160, 109, 70, -68, 9, 197,
-			-181, -39, 66, -170, 162, -83, 155, -80, 165, -16, -28, -88, 197, -132, 79, -185, -165, -40, 5, 66, 115, 146, -21,
-			-120, -141, -75, -117, 149, -135, -162, -166, 99, -155, -103, 122, -110, -157, 119, 153, -102, 64, -24, 24, 146,
-			-125, -1, -24, -87, -130, -70, 122, 126, -169, 3, -33, 135, -191, -87, -83, 192, -165, 111, -8, -149, -28, -136,
-			-192, -4, -180, 120, -70, -103, -165, 60, -170, 69, 90, -138, -162, -104, 12, -32, -93, 117, -2, 11, -74, -16,
-			-133, 15, 45, -39, -25, 38, 125, -23, -56, 130, 112, 77, 170, 58, 110, 78, 22, -66, -64, -177, -106, 54, 62, 13,
-			-195, -19, -180, 116, -134, 2, 198, 173, -43, -12, -156, 77, 19, -169, -26, -125, 25, -70, -170, 23, 133, 127, 10,
-			71, 196, 37, 112, 34, -7, -88, -169, -22, 63, -197, -2, -140, 165, 8, 102, 151, -139, 108, -84, -140, 199, -191,
-			3, 109, -62, 123, -131, -74, -150, 89, -46, -34, -111, 35, -190, 23, 174, -141, 147, 31, -127, -37, -46, -156, 131,
-			7, 93, 173, -107, 194, -163, -20, 56, 171, 132, -35, 196, -75, -104, -54, 189, -111, 199, -63, 101, 191, 193, -60,
-			193, 133, -168, 133, 180, -109, -63, 128, -97, -103, -159, 37, -79, 13, 72, 76, 29, 185, -21, -130, 161, -2, -181,
-			-61, -129, 150, 144, -188, -174, -97, -195, 183, 21, -196, 112, -158, 200, -2, 171, -143, -153, -24, -160, 78,
-			128, -5, -63, -68, -38, 191, 84, 133, 92, -31, -102, -168, 62, 0, 82, 82, 25, 132, 165, -63, -79, 173, -47, 82,
-			-144, 125, -187, -96, -20, 54, -90, -27, -79, 147, -131, 13, 155, -34, -188, 37, -188, 97, 75, -190, -32, -123, 2,
-			-155, 8, -17, 108, -90, -57, 117, 114, 191, 63, 114, 177, 69, -70, -118, 82, -116, -139, -22, -169, 181, 38, 35,
-			-193, 131, -137, -80, -107, -166, 163, -69, -97, -159, 87, -124, 94, -91, -4, -98, 42, -1, 55, 146, -59, 173, -156,
-			-33, -24, 164, -160, -171, 67, 136, 161, 126, 124, -41, 196, -69, -61, 117, 82, 34, 191, -182, -36, -162, -111, 24,
-			-88, 102, -48, 110, 15, -154, -18, 33, 33, 195, 45, -8, -5, 106, 101, 164, 141, 103, -16, -160, 151, 158, 161, 35,
-			-115, -185, 143, 85, 78, -69, -19, 128, -119, -147, -185, 24, 54, -12, -5, 120, -72, -67, 200, -199, -198, 14, 101,
-			-3, -21, -38, 125, -162, 189, 200, 57, 8, -155, -37, 113, -179, 170, 9, -69, 192, 195, 41, -69, -118, -83, 144,
-			-114, 161, -163, -197, -1, -9, -109, -174, 44, 200, 23, 20, 160, 4, -71, 100, -99, 173, -182, 18, -94, -72, -9,
-			43, 121, 165, 25, 36, -62, -42, 99, -56, 141, 83, -95, 125, 168, 15, -27, -75, -47, 84, -122, -3, 98, -118, 58,
-			-60, 141, -70, 106, 188, 148, -69, 81, -83, 125, 182, 188, -76, 124, -85, -12, -27, 93, 167, -94, 30, 160, -42,
-			-30, -117, 83, -75, -6, 84, 83, -192, -103, -110, -63, -126, 52, -16, -120, -189, -68, -94, -49, 15, -112, -72,
-			-138, 48, 186, 48, 52, 17, -91, -31, 190, 80, 183, -189, 186, -47, 169, -90, -157, -184, -124, -4, -48, 195,
-			-121, 119, -104, -45, 65, -110, -177, 64, -42, 122, -127, -134, 102, 57, -186, 131, -15, -198, -153, -142, 159,
-			57, 109, -145, -11, 78, -135, 117, 164, 72, -118, -141, -142, 191, 2, 111, -137, -170, -153, -130, 7, 200, 75,
-			-141, 196, -102, -68, 83, -12, 44, 19, 53, 93, 101, 154, -151, 50, -108, -193, -52, 94, 115, 161, 77, 195, -131,
-			-74, -170, 90, -124, -89, 98, -60, 0, -107, 92, 186, 191, 15, -134, 50, -5, 11, -7, -117, -159, -64, -196, -146,
-			-180, -170, -2, -185, 145, -53, -168, 90, -68, 133, 101, -117, -157, 126, 165, -200, -120, 150, -41, -42, -170,
-			-126, 129, -65, -116, -58, -171, -69, -121, 110, 49, -3, -104, -96, 71, 28, 109, 168, 141, -161, 43, 10, 173, -7,
-			37, -114, 27, -104, -162, -171, -52, 91, -40, 103, -154, 152, 172, 27, 136, -9, 10, -151, -4, 196, -66, 120, -188,
-			85, -144, -154, 187, 22, -60, -33, 28, 135, -62, 178, 21, -50, -105, -189, 53, -89, -87, 196, 147, 152, 180, 149,
-			-168, 165, 124, -187, 83, 91, 93, 42, 11, 2, -170, 93, 169, -162, -141, 66, -110, -10, 186, 13, 103, -41, 121, -29,
-			-3, -12, 98, 119, 29, -40, 104, -59, 111, -32, -159, -14, 27, -164, -74, 93, 2, -150, -193, 189, 98, 38, -128, -98,
-			-151, -177, 28, 91, 1, -17, -190, -169, 179, -32, 17, -127, 98, -30, -27, -55, -194, -41, 146, 126, 76, -191, -157,
-			-88, 98, -36, 170, -70, 4, 120, 92, -179, -27, 194, -111, 141, -44, -10, -136, 62, 178, -38, 96, -134, 97, -111,
-			144, 118, -55, 120, -50, 56, -51, 31, -27, -13, -151, -11, 194, -152, 23, 35, -148, -156, 171, -84, -59, 65, -90,
-			-135, 126, 168, -188, 135, 130, -53, -112, 95, -61, 56, -6, 19, 91, -195, 160, 81, 47, 61, 86, -190, -100, -107,
-			38, 167, -63, 152, 41, -38, 138, -46, 67, 40, 132, -120, -154, -200, -96, 85, 174, 192, -183, 48, 16, -54, -63,
-			177, 188, -129, -11, -51, 37, 103, -124, -71, -57, -65, -167, 73, -56, -35, 156, 1, -163, -102, -38, -78, -110,
-			183, -98, -96, -10, 86, 53, 76, 58, -144, -193, -58, -194, 182, -185, -151, -15, -54, -59, 156, 74, -66, -132,
-			-145, -60, -143, 63, 36, -151, 166, 43, -83, 48, -15, 161, -122, -131, 148, -80, -80, 40, -110, 113, 14, -65, 62,
-			-25, -90, 27, -96, -67, 166, -147, -157, -93, -197, -179, -117, 146, -119, -29, 121, -186, 34, -101, 159, -41, 108,
-			119, 54, -187, -135, -45, 20, 46, 57, 156, 18, -60, 77, -140, 179, 133, -157, -193, 112, -35, 52, 130, -5, -191,
-			-84, 50, -198, 95, -22, -200, -100, -185, -59, 95, -63, 162, -42, 50, -64, 63, -73, -114, 7, -156, 134, -28, -77,
-			-54, 95, -76, -88, 32, 172, 160, 91, -144, 196, -40, -79, 193, 70, 82, 131, 152, -34, -188, 190, -71, -19, 39, -8,
-			58, 154, -61, 186, 13, 91, -140, -73, -123, -114, -123, 113, -98, 110, 169, 131, -133, 126, 79, -133, 30, -78, -99,
-			109, 157, 157, 146, 13, -176, 105, 47, -144, -193, 176, 143, 161, -139, 9, 19, 95, -79, 188, -196, 12, 160, 46, -7,
-			89, 93, 172, 59, -55, -112, 93, 146, -59, 175, 127, 32, 130, -77, -70, -188, -87, 186, -176, 142, -55, 70, -176,
-			-32, 172, -59, -71, 53, 140, -116, 100, 17, 141, 58, -75, 1, -170, -133, 100, -65, -199, 165, -127, 46, 118, 44,
-			119, -157, 123, -146, 45, -160, 110, 110, -75, 6, -176, -150, 127, 184, -164, -107, 45, -32, -163, 117, -131, 85,
-			-63, -189, -199, 116, 21, -119, -89, 76, 153, -111, 73, -199, -167, -37, -13, -60, -154, -55, 166, -172, 33, 33,
-			21, -122, -42, -36, 132, -137, -20, 106, 17, 114, 186, 109, 129, 144, -140, 137, 114, 122, 93, -8, -108, -61,
-			-186, 25, 189, -59, 98, -187, -27, 54, -199, 106, -110, 114, -174, 173, -183, -162, -37, -88, 198, 31, -141, -88,
-			-25, 24, 93, 84, -92, -13, -193, 91, -98, -174, -132, 106, -199, -143, -159, -11, -192, 106, 155, 17, -4, 22, -164,
-			57, 151, 128, -95, -20, -97, 65, 44, -176, -149, 62, 18, 163, 172, -170, 179, -17, 25, 116, -117, -39, -45, 137,
-			-74, -162, -7, 19, 69, 121, -91, 89, 170, -28, -108, 142, 69, -141, -39, 113, -121, 179, -137, -145, -98, -101,
-			60, 196, 128, -133, -4, -102, 65, 68, -170, -76, -197, -3, -196, 152, -42, 63, 72, 1, 3, 130, 97, -2, 21, 166,
-			118, 152, 177, -12, -97, -105, 49, 8, -46, 182, 48, 12, 171, 88, 92, 15, -156, 145, -137, -124, -148, 82, -157,
-			-188, -81, 136, -130, -61, 84, 4, -65, 6, -34, 1, -151, 172, 132, 38, 198, 154, 155, 36, 188, -128, 96, -67, 115,
-			-26, 156, 14, 162, 197, 189, -94, 78, -37, -112, -158, -182, -155, -139, 132, -71, -195, 151, -53, 142, -23, -20,
-			-36, 139, 8, 45, 50, 96, 62, -95, 46, 84, 50, 6, -102, -148, -82, 86
-		));
+			-184, 127, -97, 173, -179, 70, -74, 13, 3, 11, 129, 116, -58, 35, -175, 116, -69));
 	}
 END_TEST

+ 123 - 0
Source/test/tests/SoundTest.cpp

@@ -0,0 +1,123 @@
+
+#include "../testTools.h"
+#include "../../DFPSR/api/soundAPI.h"
+#include "../../DFPSR/api/fileAPI.h"
+#include "../../DFPSR/api/randomAPI.h"
+#include <math.h>
+
+void compareSoundBuffers(const SoundBuffer &given, const SoundBuffer &expected, float tolerance) {
+	ASSERT_EQUAL(sound_getSamplesPerChannel(given), sound_getSamplesPerChannel(expected));
+	ASSERT_EQUAL(sound_getChannelCount(given), sound_getChannelCount(expected));
+	ASSERT_EQUAL(sound_getSampleRate(given), sound_getSampleRate(expected));
+	SafePointer<float> givenSamples = sound_getSafePointer(given);
+	SafePointer<float> expectedSamples = sound_getSafePointer(expected);
+	float maxGiven = 0.0f;
+	float minGiven = 0.0f;
+	float maxExpected = 0.0f;
+	float minExpected = 0.0f;
+	float maxOffset = -1000000.0f;
+	float minOffset =  1000000.0f;
+	for (int t = 0; t < sound_getSamplesPerChannel(given); t++) {
+		float givenValue = givenSamples[t];
+		float expectedValue = expectedSamples[t];
+		float offset = givenSamples[t] - expectedSamples[t];
+		if (offset > maxOffset) maxOffset = offset;
+		if (offset < minOffset) minOffset = offset;
+		if (givenValue > maxGiven) maxGiven = givenValue;
+		if (givenValue < minGiven) minGiven = givenValue;
+		if (expectedValue > maxExpected) maxExpected = expectedValue;
+		if (expectedValue < minExpected) minExpected = expectedValue;
+	}
+	ASSERT_LESSER(maxOffset, tolerance);
+	ASSERT_GREATER(minOffset, -tolerance);
+}
+
+static const double pi = 3.1415926535897932384626433832795;
+static const double cyclesToRadians = pi * 2.0;
+
+// TODO: Implement basic sound generation functions and move them to the sound API.
+//       Both in-place functions and allocating new buffers as needed to expand.
+//       Generation functions, multiplying masks, fade masks, echo effects, frequency filters, equalization, resampling...
+
+void sound_setNoise(SoundBuffer &sound, float minimum = -1.0f, float maximum = 1.0f) {
+	RandomGenerator generator = random_createGenerator(917542);
+	SafePointer<float> target = sound_getSafePointer(sound);
+	intptr_t totalSamples = sound_getSamplesPerChannel(sound) * sound_getChannelCount(sound);
+	for (intptr_t s = 0; s < totalSamples; s++) {
+		target[s] = random_generate_range(generator, minimum, maximum);
+	}
+}
+
+START_TEST(Sound)
+	String folderPath = file_combinePaths(U".", U"resources", U"sounds");
+	// Check that we have a valid folder path to the resources.
+	ASSERT_EQUAL(file_getEntryType(folderPath), EntryType::Folder);
+	{ // Single channel wave files.
+		// Generate the reference sine wave.
+		static const int sampleRate = 44100;
+		static const int samplesPerChannel = 441;
+		static const int frequency = 100;
+		SoundBuffer referenceSine = sound_create(samplesPerChannel, 1, sampleRate);
+		SafePointer<float> target = sound_getSafePointer(referenceSine);
+		static const double radiansPerElement = cyclesToRadians * (double)frequency / (double)sampleRate;
+		for (int t = 0; t < samplesPerChannel; t++) {
+			target[t] = (float)sin(double(t) * radiansPerElement);
+		}
+		// Load wave files that were exported from a 10 millisecond sine wave in the Audacity sound editor for reference.
+		//   Because Audacity truncates towards zero instead of rounding to nearest, the worst case accuracy has twice the error.
+		SoundBuffer sineU8 = sound_load(file_combinePaths(folderPath, U"SineU8.wav"));
+		ASSERT(sound_exists(sineU8));
+		compareSoundBuffers(sineU8, referenceSine, 0.02f);
+
+		SoundBuffer sineI16 = sound_load(file_combinePaths(folderPath, U"SineI16.wav"));
+		ASSERT(sound_exists(sineI16));
+		compareSoundBuffers(sineI16, referenceSine, 0.0002f);
+
+		SoundBuffer sineI24 = sound_load(file_combinePaths(folderPath, U"SineI24.wav"));
+		ASSERT(sound_exists(sineI24));
+		compareSoundBuffers(sineI24, referenceSine, 0.000002f);
+
+		SoundBuffer sineI32 = sound_load(file_combinePaths(folderPath, U"SineI32.wav"));
+		ASSERT(sound_exists(sineI32));
+		compareSoundBuffers(sineI32, referenceSine, 0.00000001f);
+
+		SoundBuffer sineF32 = sound_load(file_combinePaths(folderPath, U"SineF32.wav"));
+		ASSERT(sound_exists(sineF32));
+		compareSoundBuffers(sineF32, referenceSine, 0.00000001f);
+
+		SoundBuffer sineF64 = sound_load(file_combinePaths(folderPath, U"SineF64.wav"));
+		ASSERT(sound_exists(sineF64));
+		compareSoundBuffers(sineF64, referenceSine, 0.00000001f);
+
+		//sound_save_RiffWave(file_combinePaths(folderPath, U"SineI16_2.wav"), sineI16, RiffWaveFormat::RawI16);
+		//sound_save_RiffWave(file_combinePaths(folderPath, U"SineI24_2.wav"), sineI24, RiffWaveFormat::RawI24);
+		//sound_save_RiffWave(file_combinePaths(folderPath, U"SineI32_2.wav"), sineI32, RiffWaveFormat::RawI24);
+	}
+	{ // Brute-force encode and decode test with random noise.
+		RandomGenerator generator = random_createGenerator(12345);
+		for (uint32_t channelCount = 1; channelCount <= 16; channelCount++) {
+			SoundBuffer original = sound_create(1024, channelCount, 44100);
+			sound_setNoise(original);
+			Buffer encodedU8_truncate  = sound_encode_RiffWave(original, RiffWaveFormat::RawU8 , RoundingMethod::Truncate);
+			Buffer encodedU8_nearest   = sound_encode_RiffWave(original, RiffWaveFormat::RawU8 , RoundingMethod::Nearest);
+			Buffer encodedI16_truncate = sound_encode_RiffWave(original, RiffWaveFormat::RawI16, RoundingMethod::Truncate);
+			Buffer encodedI16_nearest  = sound_encode_RiffWave(original, RiffWaveFormat::RawI16, RoundingMethod::Nearest);
+			Buffer encodedI24_nearest  = sound_encode_RiffWave(original, RiffWaveFormat::RawI24, RoundingMethod::Nearest);
+			Buffer encodedI32_nearest  = sound_encode_RiffWave(original, RiffWaveFormat::RawI32, RoundingMethod::Nearest);
+			SoundBuffer decodedU8_truncate  = sound_decode_RiffWave(encodedU8_truncate);
+			SoundBuffer decodedU8_nearest   = sound_decode_RiffWave(encodedU8_nearest );
+			SoundBuffer decodedI16_truncate = sound_decode_RiffWave(encodedI16_truncate);
+			SoundBuffer decodedI16_nearest  = sound_decode_RiffWave(encodedI16_nearest);
+			SoundBuffer decodedI24_nearest  = sound_decode_RiffWave(encodedI24_nearest);
+			SoundBuffer decodedI32_nearest  = sound_decode_RiffWave(encodedI32_nearest);
+			compareSoundBuffers(decodedU8_truncate , original, 2.02 / 256.0);
+			compareSoundBuffers(decodedU8_nearest  , original, 1.01 / 256.0);
+			compareSoundBuffers(decodedI16_truncate, original, 2.02 / 65536.0);
+			compareSoundBuffers(decodedI16_nearest , original, 1.01 / 65536.0);
+			compareSoundBuffers(decodedI24_nearest , original, 1.01 / 8388608.0);
+			compareSoundBuffers(decodedI32_nearest , original, 1.01 / 2147483648.0);
+			if (failed) break;
+		}
+	}
+	// TODO: Test saving sounds to files.
+END_TEST

BIN
Source/test/tests/resources/sounds/SineF32.wav


BIN
Source/test/tests/resources/sounds/SineF64.wav


BIN
Source/test/tests/resources/sounds/SineI16.wav


BIN
Source/test/tests/resources/sounds/SineI24.wav


BIN
Source/test/tests/resources/sounds/SineI32.wav


BIN
Source/test/tests/resources/sounds/SineU8.wav


+ 3 - 2
Source/tools/wizard/main.cpp

@@ -9,7 +9,7 @@
 //         Can let frames have a caption for when used within a container.
 
 #include "../../DFPSR/includeFramework.h"
-#include "sound.h"
+#include "../../SDK/SoundEngine/sound.h"
 
 using namespace dsr;
 
@@ -210,7 +210,8 @@ void dsrMain(List<String> args) {
 					arguments.push(U"three");
 					arguments.push(U"Four");
 				}
-				// Launch the application.
+				// Launch the application from the project's folder.
+				file_setCurrentPath(file_getAbsoluteParentFolder(projects[projectIndex].executableFilePath));
 				projects[projectIndex].programHandle = process_execute(projects[projectIndex].executableFilePath, arguments);
 				updateInterface(true);
 			}