Skip to content

Instantly share code, notes, and snippets.

@MCJack123
Last active Mar 20, 2021
Embed
What would you like to do?
Please go to https://github.com/MCJack123/craftos2-plugins for new versions of this plugin.
/*
* sound.cpp plugin for CraftOS-PC
* Adds a number of programmable sound channels (default 4) that play sound waves with the specified frequency, wave type, volume, and pan position.
* Windows: cl /EHsc /Fesound.dll /LD /Icraftos2\api /Icraftos2\craftos2-lua\include plugin.cpp /link craftos2\craftos2-lua\src\lua51.lib SDL2.lib SDL2_mixer.lib
* Linux: g++ -fPIC -shared -Icraftos2/api -Icraftos2/craftos2-lua/include -o sound.so sound.cpp craftos2/craftos2-lua/src/liblua.a -lSDL2 -lSDL2_mixer
* Licensed under the MIT license.
*
* MIT License
*
* Copyright (c) 2021 JackMacWindows
*
* Permission is hereby granted, free of charge, to any person obtaining a copy
* of this software and associated documentation files (the "Software"), to deal
* in the Software without restriction, including without limitation the rights
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
* copies of the Software, and to permit persons to whom the Software is
* furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all
* copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
* AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
* SOFTWARE.
*/
#include <CraftOS-PC.hpp>
#include <SDL2/SDL_mixer.h>
#include <cmath>
#include <chrono>
#include <random>
#include <mutex>
#define NUM_CHANNELS ((int)get_comp(L)->userdata[ChannelInfo::identifier+1])
#define channelGroup(id) ((id) | 0x74A800)
#ifndef M_PI
#define M_PI 3.14159265358979323846
#endif
enum class WaveType {
None,
Sine,
Triangle,
Sawtooth,
RSawtooth,
Square,
Noise
};
struct ChannelInfo {
static constexpr int identifier = 0x1d4c1cd0;
int channelNumber;
double position = 0.0;
WaveType wavetype = WaveType::None;
unsigned int frequency = 0;
float amplitude = 1.0;
float pan = 0.0;
unsigned int fadeSamples = 0;
unsigned int fadeSamplesMax = 0;
float fadeSamplesInit = 0.0;
bool halting = false;
std::mutex lock;
int channelCount = 4;
};
static Uint8 empty_audio[32];
static Mix_Chunk * empty_chunk;
static int targetFrequency = 0;
static Uint16 targetFormat = 0;
static int targetChannels = 0;
static std::default_random_engine rng;
static PluginFunctions * func;
#if SDL_BYTEORDER == SDL_LIL_ENDIAN
#define MAKELE(size, x) (x)
#define MAKEBE(size, x) (SDL_Swap##size##(x))
#else
#define MAKELE(size, x) (SDL_Swap##size##(x))
#define MAKEBE(size, x) (x)
#endif
static void writeSample(float sample, void* data) {
switch (targetFormat) {
case AUDIO_S8: *(int8_t*)data = sample * INT8_MAX; return;
case AUDIO_U8: *(uint8_t*)data = (sample + 1.0) * UINT8_MAX; return;
case AUDIO_S16LSB: *(int16_t*)data = MAKELE(16, sample * INT16_MAX); return;
case AUDIO_S16MSB: *(int16_t*)data = MAKEBE(16, sample * INT16_MAX); return;
case AUDIO_U16LSB: *(uint16_t*)data = MAKELE(16, (sample + 1.0) * UINT16_MAX); return;
case AUDIO_U16MSB: *(uint16_t*)data = MAKEBE(16, (sample + 1.0) * UINT16_MAX); return;
case AUDIO_S32LSB: *(int32_t*)data = MAKELE(32, sample * INT32_MAX); return;
case AUDIO_S32MSB: *(int32_t*)data = MAKEBE(32, sample * INT32_MAX); return;
case AUDIO_F32LSB: *(float*)data = MAKELE(Float, sample); return;
case AUDIO_F32MSB: *(float*)data = MAKEBE(Float, sample); return;
}
}
static float getSample(WaveType type, double amplitude, double pos) {
if (amplitude < 0.0001) return 0.0;
switch (type) {
case WaveType::Sine: return amplitude * sin(2.0 * pos * M_PI);
case WaveType::Triangle: return 2.0 * abs(amplitude * fmod(2.0 * pos + 1.5, 2.0) - amplitude) - amplitude;
case WaveType::Sawtooth: return amplitude * fmod(2.0 * pos + 1.0, 2.0) - amplitude;
case WaveType::RSawtooth: return amplitude * fmod(2.0 * (1.0 - pos) + 1.0, 2.0) - amplitude;
case WaveType::Square: return -2.0 * amplitude * floor(2 * fmod(pos, 1.0)) + amplitude;
case WaveType::Noise: return amplitude * (((float)rng() / (float)rng.max()) * 2.0f - 1.0f);
default: return 0.0;
}
}
template<typename T> static T min(T a, T b) {return a < b ? a : b;}
template<typename T> static T max(T a, T b) {return a > b ? a : b;}
static void generateWaveform(int channel, void* stream, int length, void* udata) {
ChannelInfo * info = (ChannelInfo*)udata;
std::lock_guard<std::mutex> lock(info->lock);
const int sampleSize = (SDL_AUDIO_BITSIZE(targetFormat) / 8) * targetChannels;
int numSamples = length / sampleSize;
for (int i = 0; i < numSamples; i++) {
//if (info->wavetype == WaveType::Triangle) printf("%f %f\n", info->position, getSample(info->wavetype, info->amplitude, info->position));
if (targetChannels == 1) {
writeSample(info->frequency == 0 ? 0.0 : getSample(info->wavetype, info->amplitude, info->position), (uint8_t*)stream + i * sampleSize);
} else {
writeSample(info->frequency == 0 ? 0.0 : getSample(info->wavetype, info->amplitude * min(1.0 + info->pan, 1.0), info->position), (uint8_t*)stream + i * sampleSize);
writeSample(info->frequency == 0 ? 0.0 : getSample(info->wavetype, info->amplitude * min(1.0 - info->pan, 1.0), info->position), (uint8_t*)stream + i * sampleSize + (SDL_AUDIO_BITSIZE(targetFormat) / 8));
for (int j = 2; j < targetChannels; j++) writeSample(info->frequency == 0 ? 0.0 : getSample(info->wavetype, info->amplitude, info->position), (uint8_t*)stream + i * sampleSize + j * (SDL_AUDIO_BITSIZE(targetFormat) / 8));
}
info->position = fmod(info->position + (double)info->frequency / (double)targetFrequency, 1.0);
if (info->fadeSamplesMax > 0) {
info->amplitude -= info->fadeSamplesInit / info->fadeSamplesMax;
if (--info->fadeSamples <= 0) {
info->fadeSamples = info->fadeSamplesMax = 0;
info->fadeSamplesInit = info->amplitude = 0.0f;
}
}
}
}
static void channelFinished(int channel, void* udata) {
if (!((ChannelInfo*)udata)->halting) Mix_PlayChannel(((ChannelInfo*)udata)->channelNumber, empty_chunk, -1);
}
static void ChannelInfo_destructor(Computer * comp, int id, void* data) {
ChannelInfo * channels = (ChannelInfo*)data;
for (int i = 0; i < channels[0].channelCount; i++) {
channels[i].halting = true;
Mix_HaltChannel(channels[i].channelNumber);
Mix_UnregisterEffect(channels[i].channelNumber, generateWaveform);
Mix_GroupChannel(channels[i].channelNumber, -1);
}
delete[] channels;
}
/*
* Returns the type of wave assigned to the channel.
* 1: The channel to check (1 - NUM_CHANNELS)
* Returns: The current wave type
*/
static int sound_getWaveType(lua_State *L) {
const int channel = luaL_checkinteger(L, 1);
if (channel < 1 || channel > NUM_CHANNELS) luaL_error(L, "bad argument #1 (channel out of range)");
ChannelInfo * info = (ChannelInfo*)get_comp(L)->userdata[ChannelInfo::identifier] + (channel - 1);
switch (info->wavetype) {
case WaveType::None: lua_pushstring(L, "none"); break;
case WaveType::Sine: lua_pushstring(L, "sine"); break;
case WaveType::Triangle: lua_pushstring(L, "triangle"); break;
case WaveType::Sawtooth: lua_pushstring(L, "sawtooth"); break;
case WaveType::RSawtooth: lua_pushstring(L, "rsawtooth"); break;
case WaveType::Square: lua_pushstring(L, "square"); break;
case WaveType::Noise: lua_pushstring(L, "noise"); break;
default: lua_pushstring(L, "unknown"); break;
}
return 1;
}
/*
* Sets the wave type for a channel.
* 1: The channel to set (1 - NUM_CHANNELS)
* 2: The type of wave as a string (from {"none", "sine", "triangle", "sawtooth", "square", and "noise"})
*/
static int sound_setWaveType(lua_State *L) {
const int channel = luaL_checkinteger(L, 1);
if (channel < 1 || channel > NUM_CHANNELS) luaL_error(L, "bad argument #1 (channel out of range)");
ChannelInfo * info = (ChannelInfo*)get_comp(L)->userdata[ChannelInfo::identifier] + (channel - 1);
std::string type = luaL_checkstring(L, 2);
std::transform(type.begin(), type.end(), type.begin(), tolower);
std::lock_guard<std::mutex> lock(info->lock);
if (type == "none") info->wavetype = WaveType::None;
else if (type == "sine") info->wavetype = WaveType::Sine;
else if (type == "triangle") info->wavetype = WaveType::Triangle;
else if (type == "sawtooth") info->wavetype = WaveType::Sawtooth;
else if (type == "rsawtooth") info->wavetype = WaveType::RSawtooth;
else if (type == "square") info->wavetype = WaveType::Square;
else if (type == "noise") info->wavetype = WaveType::Noise;
else luaL_error(L, "bad argument #2 (invalid option '%s')", type.c_str());
return 0;
}
/*
* Returns the frequency assigned to the channel.
* 1: The channel to check (1 - NUM_CHANNELS)
* Returns: The current frequency
*/
static int sound_getFrequency(lua_State *L) {
const int channel = luaL_checkinteger(L, 1);
if (channel < 1 || channel > NUM_CHANNELS) luaL_error(L, "bad argument #1 (channel out of range)");
ChannelInfo * info = (ChannelInfo*)get_comp(L)->userdata[ChannelInfo::identifier] + (channel - 1);
lua_pushinteger(L, info->frequency);
return 1;
}
/*
* Sets the frequency of the wave on a channel.
* 1: The channel to set (1 - NUM_CHANNELS)
* 2: The frequency in Hz
*/
static int sound_setFrequency(lua_State *L) {
const int channel = luaL_checkinteger(L, 1);
if (channel < 1 || channel > NUM_CHANNELS) luaL_error(L, "bad argument #1 (channel out of range)");
ChannelInfo * info = (ChannelInfo*)get_comp(L)->userdata[ChannelInfo::identifier] + (channel - 1);
lua_Integer frequency = luaL_checkinteger(L, 2);
if (frequency < 0 || frequency > targetFrequency / 2) luaL_error(L, "bad argument #2 (frequency out of range)");
std::lock_guard<std::mutex> lock(info->lock);
info->frequency = frequency;
return 0;
}
/*
* Returns the volume of the channel.
* 1: The channel to check (1 - NUM_CHANNELS)
* Returns: The current volume
*/
static int sound_getVolume(lua_State *L) {
const int channel = luaL_checkinteger(L, 1);
if (channel < 1 || channel > NUM_CHANNELS) luaL_error(L, "bad argument #1 (channel out of range)");
ChannelInfo * info = (ChannelInfo*)get_comp(L)->userdata[ChannelInfo::identifier] + (channel - 1);
lua_pushnumber(L, info->amplitude);
return 1;
}
/*
* Sets the volume of a channel.
* 1: The channel to set (1 - NUM_CHANNELS)
* 2: The volume, from 0.0 to 1.0
*/
static int sound_setVolume(lua_State *L) {
const int channel = luaL_checkinteger(L, 1);
if (channel < 1 || channel > NUM_CHANNELS) luaL_error(L, "bad argument #1 (channel out of range)");
ChannelInfo * info = (ChannelInfo*)get_comp(L)->userdata[ChannelInfo::identifier] + (channel - 1);
float amplitude = luaL_checknumber(L, 2);
if (amplitude < 0.0 || amplitude > 1.0) luaL_error(L, "bad argument #2 (volume out of range)");
std::lock_guard<std::mutex> lock(info->lock);
info->amplitude = amplitude;
return 0;
}
/*
* Returns the panning of the channel.
* 1: The channel to check (1 - NUM_CHANNELS)
* Returns: The current panning
*/
static int sound_getPan(lua_State *L) {
const int channel = luaL_checkinteger(L, 1);
if (channel < 1 || channel > NUM_CHANNELS) luaL_error(L, "bad argument #1 (channel out of range)");
ChannelInfo * info = (ChannelInfo*)get_comp(L)->userdata[ChannelInfo::identifier] + (channel - 1);
lua_pushnumber(L, info->pan);
return 1;
}
/*
* Sets the panning for a channel.
* 1: The channel to set (1 - NUM_CHANNELS)
* 2: The panning, from -1.0 (right) to 1.0 (left)
*/
static int sound_setPan(lua_State *L) {
const int channel = luaL_checkinteger(L, 1);
if (channel < 1 || channel > NUM_CHANNELS) luaL_error(L, "bad argument #1 (channel out of range)");
ChannelInfo * info = (ChannelInfo*)get_comp(L)->userdata[ChannelInfo::identifier] + (channel - 1);
float pan = luaL_checknumber(L, 2);
if (pan < -1.0 || pan > 1.0) luaL_error(L, "bad argument #2 (pan out of range)");
std::lock_guard<std::mutex> lock(info->lock);
info->pan = pan;
return 0;
}
/*
* Starts or stops a fade out operation on a channel.
* 1: The channel to fade out (1 - NUM_CHANNELS)
* 2: The time for the fade out in seconds (0 to stop any fade out in progress)
*/
static int sound_fadeOut(lua_State *L) {
const int channel = luaL_checkinteger(L, 1);
if (channel < 1 || channel > NUM_CHANNELS) luaL_error(L, "bad argument #1 (channel out of range)");
ChannelInfo * info = (ChannelInfo*)get_comp(L)->userdata[ChannelInfo::identifier] + (channel - 1);
double time = luaL_checknumber(L, 2);
if (time < 0.0) luaL_error(L, "bad argument #2 (time out of range)");
std::lock_guard<std::mutex> lock(info->lock);
if (time < 0.0001) {
info->fadeSamplesInit = 0.0;
info->fadeSamples = info->fadeSamplesMax = 0;
} else {
info->fadeSamplesInit = info->amplitude;
info->fadeSamples = info->fadeSamplesMax = time * targetFrequency;
}
return 0;
}
static PluginInfo info("sound");
static luaL_Reg sound_lib[] = {
{"getWaveType", sound_getWaveType},
{"setWaveType", sound_setWaveType},
{"getFrequency", sound_getFrequency},
{"setFrequency", sound_setFrequency},
{"getVolume", sound_getVolume},
{"setVolume", sound_setVolume},
{"getPan", sound_getPan},
{"setPan", sound_setPan},
{"fadeOut", sound_fadeOut},
{NULL, NULL}
};
extern "C" {
#ifdef _WIN32
_declspec(dllexport)
#endif
PluginInfo * plugin_init(PluginFunctions * func, const path_t& path) {
if (func->abi_version != PLUGIN_VERSION) return &info;
memset(empty_audio, 0, 32);
empty_chunk = Mix_QuickLoad_RAW(empty_audio, 32);
rng.seed(std::chrono::system_clock::now().time_since_epoch().count());
::func = func;
if (func->structure_version >= 2) func->registerConfigSetting("sound.numChannels", CONFIG_TYPE_INTEGER, [](const std::string&, void*)->int{return CONFIG_EFFECT_REOPEN;}, NULL);
return &info;
}
#ifdef _WIN32
_declspec(dllexport)
#endif
int luaopen_sound(lua_State *L) {
Computer * comp = get_comp(L);
int num_channels = 4;
if (func->structure_version >= 2) { // Plugin config is broken on v2.5-v2.5.2
try {num_channels = func->getConfigSettingInt("sound.numChannels");}
catch (...) {func->setConfigSettingInt("sound.numChannels", num_channels);}
}
if (comp->userdata.find(ChannelInfo::identifier) == comp->userdata.end()) {
ChannelInfo * channels = new ChannelInfo[num_channels];
Mix_QuerySpec(&targetFrequency, &targetFormat, &targetChannels);
Mix_AllocateChannels(Mix_AllocateChannels(-1) + num_channels);
for (int i = 0; i < num_channels; i++) {
channels[i].channelCount = num_channels;
channels[i].channelNumber = Mix_GroupAvailable(-1);
while (channels[i].channelNumber == -1) {
Mix_AllocateChannels(Mix_AllocateChannels(-1) + 1);
channels[i].channelNumber = Mix_GroupAvailable(-1);
}
Mix_GroupChannel(channels[i].channelNumber, channelGroup(comp->id));
Mix_RegisterEffect(channels[i].channelNumber, generateWaveform, channelFinished, &channels[i]);
Mix_PlayChannel(channels[i].channelNumber, empty_chunk, -1);
}
comp->userdata[ChannelInfo::identifier] = channels;
comp->userdata[ChannelInfo::identifier+1] = (void*)num_channels;
comp->userdata_destructors[ChannelInfo::identifier] = ChannelInfo_destructor;
}
luaL_register(L, "sound", sound_lib);
return 1;
}
#ifdef _WIN32
_declspec(dllexport)
#endif
void plugin_deinit(PluginInfo * info) {
Mix_FreeChunk(empty_chunk);
}
}
local octave = 440
local keymap = {
[keys.a] = 3,
[keys.w] = 4,
[keys.s] = 5,
[keys.e] = 6,
[keys.d] = 7,
[keys.f] = 8,
[keys.t] = 9,
[keys.g] = 10,
[keys.y] = 11,
[keys.h] = 12,
[keys.u] = 13,
[keys.j] = 14,
[keys.k] = 15,
}
local topRow = {[keys.w] = 2, [keys.e] = 4, [keys.t] = 8, [keys.y] = 10, [keys.u] = 12}
local bottomRow = {[keys.a] = 1, [keys.s] = 3, [keys.d] = 5, [keys.f] = 7, [keys.g] = 9, [keys.h] = 11, [keys.j] = 13, [keys.k] = 15}
local channels = {
nil,
nil,
nil
}
sound.setVolume(1, 0)
sound.setVolume(2, 0)
sound.setVolume(3, 0)
sound.setVolume(4, 0)
sound.setWaveType(1, "sine")
sound.setWaveType(2, "sine")
sound.setWaveType(3, "sine")
sound.setWaveType(4, "noise")
sound.setFrequency(4, 1)
local x, y = term.getCursorPos()
local w, h = term.getSize()
if y == h then
y = y - 1
term.scroll(1)
end
term.setCursorPos(1, y)
term.blit(" W E T Y U", "777777777777", "ffffffffffff")
term.setCursorPos(1, y + 1)
term.blit("A S D F G H J K", "777777777777777", "fffffffffffffff")
term.setCursorBlink(false)
while true do
local ev, key, held = os.pullEvent()
if ev == "key" and not held then
if keymap[key] and #channels < 3 then
local c = #channels+1
channels[c] = key
sound.setFrequency(c, octave * 2^(keymap[key]/12))
sound.setVolume(c, 0.25)
if topRow[key] then term.setCursorPos(topRow[key], y)
else term.setCursorPos(bottomRow[key], y + 1) end
term.blit(keys.getName(key):upper(), "0", "f")
elseif key == keys.n then
sound.setVolume(4, 0.1)
elseif key == keys.m then
sound.setVolume(4, 0.1)
sound.fadeOut(4, 0.1)
elseif key == keys.comma then
sound.setVolume(4, 0.1)
sound.fadeOut(4, 0.2)
elseif key == keys.q then
break
end
elseif ev == "key_up" then
if keymap[key] then
for i = 1, 3 do
if key == channels[i] then
channels[i] = nil
sound.setVolume(i, 0)
if topRow[key] then term.setCursorPos(topRow[key], y)
else term.setCursorPos(bottomRow[key], y + 1) end
term.blit(keys.getName(key):upper(), "7", "f")
break
end
end
elseif key == keys.n then
sound.setVolume(4, 0)
end
end
end
local tetris_cesnd = {
0x93, 0x82, 0x00, 0x52, 0xa0, 0x00, 0x00, 0xa0, 0x04, 0xa5, 0xb0, 0x00,
0xee, 0x81, 0x04, 0x9f, 0x91, 0x00, 0x52, 0xa0, 0x00, 0x00, 0xb0, 0x00,
0x0b, 0x82, 0x04, 0xb8, 0x91, 0x00, 0x00, 0xa0, 0x00, 0xa5, 0xb0, 0x00,
0x4b, 0x82, 0x03, 0xee, 0x91, 0x00, 0x52, 0xa0, 0x00, 0x00, 0xb0, 0x00,
0x00, 0x90, 0x03, 0xa5, 0x80, 0x01, 0x93, 0x92, 0x00, 0x00, 0xa0, 0x00,
0x4b, 0x92, 0x02, 0x00, 0x80, 0x02, 0xb8, 0x91, 0x00, 0x0b, 0xa2, 0x00,
0x52, 0xb0, 0x00, 0x00, 0x90, 0x03, 0xee, 0x81, 0x01, 0x9f, 0x91, 0x00,
0xa5, 0xa0, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x90, 0x03, 0xb8, 0x81, 0x01,
0x4a, 0x91, 0x00, 0x00, 0xa0, 0x00, 0x6e, 0xb0, 0x00, 0xdc, 0xa0, 0x04,
0x00, 0xb0, 0x00, 0xb8, 0x81, 0x04, 0x4a, 0x91, 0x00, 0x00, 0xa0, 0x00,
0x6e, 0xb0, 0x00, 0x0b, 0x82, 0x04, 0xb8, 0x91, 0x00, 0xdc, 0xa0, 0x00,
0x00, 0xb0, 0x00, 0x93, 0x82, 0x04, 0x0b, 0x92, 0x00, 0x00, 0xa0, 0x00,
0x6e, 0xb0, 0x00, 0xdc, 0xa0, 0x04, 0x00, 0xb0, 0x00, 0x4b, 0x82, 0x04,
0xee, 0x91, 0x00, 0x00, 0xa0, 0x00, 0x6e, 0xb0, 0x00, 0x00, 0x90, 0x03,
0x0b, 0x82, 0x01, 0xb8, 0x91, 0x00, 0xdc, 0xa0, 0x00, 0x00, 0xb0, 0x00,
0x00, 0x90, 0x03, 0x9f, 0x81, 0x01, 0xee, 0x91, 0x00, 0x00, 0xa0, 0x00,
0x68, 0xb0, 0x00, 0x4a, 0x81, 0x04, 0xd0, 0xa0, 0x00, 0x00, 0xb0, 0x00,
0x9f, 0x81, 0x04, 0x00, 0xa0, 0x00, 0x68, 0xb0, 0x00, 0xb8, 0x81, 0x04,
0x0b, 0x92, 0x00, 0xd0, 0xa0, 0x00, 0x00, 0xb0, 0x00, 0x4b, 0x82, 0x04,
0xee, 0x91, 0x00, 0x00, 0xa0, 0x00, 0x52, 0xb0, 0x00, 0xa5, 0xa0, 0x04,
0x00, 0xb0, 0x00, 0x93, 0x82, 0x04, 0x0b, 0x92, 0x00, 0x00, 0xa0, 0x00,
0x52, 0xb0, 0x00, 0xa5, 0xa0, 0x04, 0x00, 0xb0, 0x00, 0x0b, 0x82, 0x04,
0xb8, 0x91, 0x00, 0x00, 0xa0, 0x00, 0x6e, 0xb0, 0x00, 0xdc, 0xa0, 0x04,
0x00, 0xb0, 0x00, 0xb8, 0x81, 0x04, 0x4a, 0x91, 0x00, 0x00, 0xa0, 0x00,
0x6e, 0xb0, 0x00, 0xdc, 0xa0, 0x04, 0x00, 0xb0, 0x00, 0xb8, 0x81, 0x04,
0x4a, 0x91, 0x00, 0x00, 0xa0, 0x00, 0x6e, 0xb0, 0x00, 0xdc, 0xa0, 0x04,
0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04, 0x7b, 0xb0, 0x00, 0x83, 0xa0, 0x04,
0x00, 0xb0, 0x00, 0x00, 0x80, 0x04, 0x93, 0x90, 0x00, 0x00, 0xa0, 0x00,
0x5d, 0x81, 0x04, 0x00, 0x90, 0x00, 0x4b, 0xa2, 0x00, 0x49, 0xb0, 0x00,
0x00, 0xb0, 0x04, 0xba, 0x82, 0x04, 0xb8, 0x91, 0x00, 0x49, 0xa0, 0x00,
0x0b, 0x82, 0x04, 0x70, 0x93, 0x00, 0x00, 0xa0, 0x00, 0x0b, 0x82, 0x04,
0x49, 0xa0, 0x00, 0x0b, 0x82, 0x02, 0x00, 0x80, 0x02, 0xee, 0x91, 0x00,
0x00, 0xa0, 0x00, 0x10, 0xb3, 0x00, 0x00, 0x90, 0x03, 0xba, 0x82, 0x01,
0xb8, 0x91, 0x00, 0x57, 0xa0, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x90, 0x03,
0x93, 0x82, 0x01, 0x88, 0x91, 0x00, 0x00, 0xa0, 0x00, 0x41, 0xb0, 0x00,
0x83, 0xa0, 0x04, 0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04, 0x0b, 0x82, 0x04,
0x4a, 0x91, 0x00, 0x83, 0xa0, 0x00, 0x88, 0x81, 0x04, 0x93, 0x92, 0x00,
0x00, 0xa0, 0x00, 0x41, 0xb0, 0x00, 0x00, 0x80, 0x03, 0xb8, 0x81, 0x01,
0x62, 0xa0, 0x00, 0x00, 0xb0, 0x00, 0x88, 0x81, 0x02, 0x4b, 0x82, 0x02,
0x5d, 0x91, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x90, 0x03, 0x0b, 0x82, 0x01,
0x4a, 0x91, 0x00, 0x62, 0xa0, 0x00, 0x00, 0x90, 0x03, 0x9f, 0x81, 0x01,
0xee, 0x91, 0x00, 0x00, 0xa0, 0x00, 0x7b, 0xb0, 0x00, 0x4a, 0x81, 0x04,
0xf7, 0xa0, 0x00, 0x00, 0xb0, 0x00, 0xee, 0x81, 0x04, 0x9f, 0x91, 0x00,
0x00, 0xa0, 0x00, 0x0b, 0x82, 0x04, 0xb8, 0x91, 0x00, 0xf7, 0xa0, 0x00,
0xee, 0x81, 0x04, 0x4b, 0x92, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x80, 0x03,
0x9f, 0x81, 0x01, 0xa5, 0xa0, 0x00, 0x00, 0x80, 0x03, 0x0b, 0x82, 0x01,
0x93, 0x92, 0x00, 0x00, 0xa0, 0x00, 0x00, 0x80, 0x03, 0x9f, 0x81, 0x01,
0xd0, 0xa0, 0x00, 0x00, 0x80, 0x03, 0xb8, 0x81, 0x01, 0x0b, 0x92, 0x00,
0x00, 0xa0, 0x00, 0x6e, 0xb0, 0x00, 0x4a, 0x81, 0x04, 0xa5, 0xa0, 0x00,
0x00, 0xb0, 0x00, 0xb8, 0x81, 0x04, 0x4a, 0x91, 0x00, 0x00, 0xa0, 0x00,
0x6e, 0xb0, 0x00, 0xa5, 0xa0, 0x04, 0x00, 0xb0, 0x00, 0xb8, 0x81, 0x04,
0x4a, 0x91, 0x00, 0x00, 0xa0, 0x00, 0x6e, 0xb0, 0x00, 0x00, 0x80, 0x08,
0x00, 0x90, 0x00, 0x00, 0xb0, 0x00, 0x93, 0x82, 0x08, 0x52, 0xa0, 0x00,
0x00, 0xa0, 0x04, 0xa5, 0xb0, 0x00, 0xee, 0x81, 0x04, 0x9f, 0x91, 0x00,
0x52, 0xa0, 0x00, 0x00, 0xb0, 0x00, 0x0b, 0x82, 0x04, 0xb8, 0x91, 0x00,
0x00, 0xa0, 0x00, 0xa5, 0xb0, 0x00, 0x4b, 0x82, 0x03, 0xee, 0x91, 0x00,
0x52, 0xa0, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x90, 0x03, 0xa5, 0x80, 0x01,
0x93, 0x92, 0x00, 0x00, 0xa0, 0x00, 0x4b, 0x92, 0x02, 0x00, 0x80, 0x02,
0xb8, 0x91, 0x00, 0x0b, 0xa2, 0x00, 0x52, 0xb0, 0x00, 0x00, 0x90, 0x03,
0xee, 0x81, 0x01, 0x9f, 0x91, 0x00, 0xa5, 0xa0, 0x00, 0x00, 0xb0, 0x00,
0x00, 0x90, 0x03, 0xb8, 0x81, 0x01, 0x4a, 0x91, 0x00, 0x00, 0xa0, 0x00,
0x6e, 0xb0, 0x00, 0xdc, 0xa0, 0x04, 0x00, 0xb0, 0x00, 0xb8, 0x81, 0x04,
0x4a, 0x91, 0x00, 0x00, 0xa0, 0x00, 0x6e, 0xb0, 0x00, 0x0b, 0x82, 0x04,
0xb8, 0x91, 0x00, 0xdc, 0xa0, 0x00, 0x00, 0xb0, 0x00, 0x93, 0x82, 0x04,
0x0b, 0x92, 0x00, 0x00, 0xa0, 0x00, 0x6e, 0xb0, 0x00, 0xdc, 0xa0, 0x04,
0x00, 0xb0, 0x00, 0x4b, 0x82, 0x04, 0xee, 0x91, 0x00, 0x00, 0xa0, 0x00,
0x6e, 0xb0, 0x00, 0x00, 0x90, 0x03, 0x0b, 0x82, 0x01, 0xb8, 0x91, 0x00,
0xdc, 0xa0, 0x00, 0x00, 0xb0, 0x00, 0x00, 0x90, 0x03, 0x9f, 0x81, 0x01,
0xee, 0x91, 0x00, 0x00, 0xa0, 0x00, 0x68, 0xb0, 0x00, 0x4a, 0x81, 0x04,
0xd0, 0xa0, 0x00, 0x00, 0xb0, 0x00, 0x9f, 0x81, 0x04, 0x00, 0xa0, 0x00,
0x68, 0xb0, 0x00, 0xb8, 0x81, 0x04, 0x0b, 0x92, 0x00, 0xd0, 0xa0, 0x00,
0x00, 0xb0, 0x00, 0x4b, 0x82, 0x04, 0xee, 0x91, 0x00, 0x00, 0xa0, 0x00,
0x52, 0xb0, 0x00, 0xa5, 0xa0, 0x04, 0x00, 0xb0, 0x00, 0x93, 0x82, 0x04,
0x0b, 0x92, 0x00, 0x00, 0xa0, 0x00, 0x52, 0xb0, 0x00, 0xa5, 0xa0, 0x04,
0x00, 0xb0, 0x00, 0x0b, 0x82, 0x04, 0xb8, 0x91, 0x00, 0x00, 0xa0, 0x00,
0x6e, 0xb0, 0x00, 0xdc, 0xa0, 0x04, 0x00, 0xb0, 0x00, 0xb8, 0x81, 0x04,
0x4a, 0x91, 0x00, 0x00, 0xa0, 0x00, 0x6e, 0xb0, 0x00, 0xdc, 0xa0, 0x04,
0x00, 0xb0, 0x00, 0xb8, 0x81, 0x04, 0x4a, 0x91, 0x00, 0x00, 0xa0, 0x00,
0x6e, 0xb0, 0x00, 0xdc, 0xa0, 0x04, 0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04,
0x7b, 0xb0, 0x00, 0x83, 0xa0, 0x04, 0x00, 0xb0, 0x00, 0x00, 0x80, 0x04,
0x93, 0x90, 0x00, 0x00, 0xa0, 0x00, 0x5d, 0x81, 0x04, 0x00, 0x90, 0x00,
0x4b, 0xa2, 0x00, 0x49, 0xb0, 0x00, 0x00, 0xb0, 0x04, 0xba, 0x82, 0x04,
0xb8, 0x91, 0x00, 0x49, 0xa0, 0x00, 0x0b, 0x82, 0x04, 0x70, 0x93, 0x00,
0x00, 0xa0, 0x00, 0x0b, 0x82, 0x04, 0x49, 0xa0, 0x00, 0x0b, 0x82, 0x02,
0x00, 0x80, 0x02, 0xee, 0x91, 0x00, 0x00, 0xa0, 0x00, 0x10, 0xb3, 0x00,
0x00, 0x90, 0x03, 0xba, 0x82, 0x01, 0xb8, 0x91, 0x00, 0x57, 0xa0, 0x00,
0x00, 0xb0, 0x00, 0x00, 0x90, 0x03, 0x93, 0x82, 0x01, 0x88, 0x91, 0x00,
0x00, 0xa0, 0x00, 0x41, 0xb0, 0x00, 0x83, 0xa0, 0x04, 0x00, 0xb0, 0x00,
0x00, 0xa0, 0x04, 0x0b, 0x82, 0x04, 0x4a, 0x91, 0x00, 0x83, 0xa0, 0x00,
0x88, 0x81, 0x04, 0x93, 0x92, 0x00, 0x00, 0xa0, 0x00, 0x41, 0xb0, 0x00,
0x00, 0x80, 0x03, 0xb8, 0x81, 0x01, 0x62, 0xa0, 0x00, 0x00, 0xb0, 0x00,
0x88, 0x81, 0x02, 0x4b, 0x82, 0x02, 0x5d, 0x91, 0x00, 0x00, 0xa0, 0x00,
0x00, 0x90, 0x03, 0x0b, 0x82, 0x01, 0x4a, 0x91, 0x00, 0x62, 0xa0, 0x00,
0x00, 0x90, 0x03, 0x9f, 0x81, 0x01, 0xee, 0x91, 0x00, 0x00, 0xa0, 0x00,
0x7b, 0xb0, 0x00, 0x4a, 0x81, 0x04, 0xf7, 0xa0, 0x00, 0x00, 0xb0, 0x00,
0xee, 0x81, 0x04, 0x9f, 0x91, 0x00, 0x00, 0xa0, 0x00, 0x0b, 0x82, 0x04,
0xb8, 0x91, 0x00, 0xf7, 0xa0, 0x00, 0xee, 0x81, 0x04, 0x4b, 0x92, 0x00,
0x00, 0xa0, 0x00, 0x00, 0x80, 0x03, 0x9f, 0x81, 0x01, 0xa5, 0xa0, 0x00,
0x00, 0x80, 0x03, 0x0b, 0x82, 0x01, 0x93, 0x92, 0x00, 0x00, 0xa0, 0x00,
0x00, 0x80, 0x03, 0x9f, 0x81, 0x01, 0xd0, 0xa0, 0x00, 0x00, 0x80, 0x03,
0xb8, 0x81, 0x01, 0x0b, 0x92, 0x00, 0x00, 0xa0, 0x00, 0x6e, 0xb0, 0x00,
0x4a, 0x81, 0x04, 0xa5, 0xa0, 0x00, 0x00, 0xb0, 0x00, 0xb8, 0x81, 0x04,
0x4a, 0x91, 0x00, 0x00, 0xa0, 0x00, 0x6e, 0xb0, 0x00, 0xa5, 0xa0, 0x04,
0x00, 0xb0, 0x00, 0xb8, 0x81, 0x04, 0x4a, 0x91, 0x00, 0x00, 0xa0, 0x00,
0x6e, 0xb0, 0x00, 0x00, 0x80, 0x08, 0x00, 0x90, 0x00, 0x00, 0xb0, 0x00,
0x4a, 0x81, 0x08, 0x06, 0x91, 0x00, 0x6e, 0xa0, 0x00, 0x00, 0xa0, 0x04,
0xa5, 0xb0, 0x00, 0x6e, 0xa0, 0x04, 0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04,
0xa5, 0xb0, 0x00, 0x06, 0x81, 0x04, 0xdc, 0x90, 0x00, 0x6e, 0xa0, 0x00,
0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04, 0xa5, 0xb0, 0x00, 0x6e, 0xa0, 0x04,
0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04, 0xa5, 0xb0, 0x00, 0x26, 0x81, 0x04,
0xf7, 0x90, 0x00, 0x68, 0xa0, 0x00, 0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04,
0xa5, 0xb0, 0x00, 0x68, 0xa0, 0x04, 0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04,
0xa5, 0xb0, 0x00, 0xf7, 0x80, 0x04, 0xd0, 0x90, 0x00, 0x68, 0xa0, 0x00,
0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04, 0xa5, 0xb0, 0x00, 0x68, 0xa0, 0x04,
0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04, 0xa5, 0xb0, 0x00, 0x06, 0x81, 0x04,
0xdc, 0x90, 0x00, 0x6e, 0xa0, 0x00, 0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04,
0xa5, 0xb0, 0x00, 0x6e, 0xa0, 0x04, 0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04,
0xa5, 0xb0, 0x00, 0x6e, 0x80, 0x04, 0xdc, 0x90, 0x00, 0xa5, 0xb0, 0x00,
0x00, 0x80, 0x04, 0xa5, 0xa0, 0x00, 0x6e, 0x80, 0x04, 0x00, 0xa0, 0x00,
0x00, 0x80, 0x04, 0xa5, 0xa0, 0x00, 0xd0, 0x80, 0x04, 0xa5, 0x90, 0x00,
0x68, 0xa0, 0x00, 0xa5, 0x90, 0x04, 0x00, 0xa0, 0x00, 0x00, 0x90, 0x04,
0x68, 0xa0, 0x00, 0xa5, 0x90, 0x04, 0x00, 0xa0, 0x00, 0xf7, 0x80, 0x04,
0xd0, 0x90, 0x00, 0x68, 0xa0, 0x00, 0x00, 0xa0, 0x04, 0xa5, 0xb0, 0x00,
0x68, 0xa0, 0x04, 0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04, 0xa5, 0xb0, 0x00,
0x4a, 0x81, 0x04, 0x06, 0x91, 0x00, 0x6e, 0xa0, 0x00, 0x00, 0xa0, 0x04,
0xa5, 0xb0, 0x00, 0x6e, 0xa0, 0x04, 0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04,
0xa5, 0xb0, 0x00, 0x06, 0x81, 0x04, 0xdc, 0x90, 0x00, 0x6e, 0xa0, 0x00,
0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04, 0xa5, 0xb0, 0x00, 0x6e, 0xa0, 0x04,
0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04, 0xa5, 0xb0, 0x00, 0x26, 0x81, 0x04,
0xf7, 0x90, 0x00, 0x68, 0xa0, 0x00, 0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04,
0xa5, 0xb0, 0x00, 0x68, 0xa0, 0x04, 0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04,
0xa5, 0xb0, 0x00, 0xf7, 0x80, 0x04, 0xd0, 0x90, 0x00, 0x68, 0xa0, 0x00,
0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04, 0xa5, 0xb0, 0x00, 0x68, 0xa0, 0x04,
0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04, 0xa5, 0xb0, 0x00, 0x06, 0x81, 0x04,
0xdc, 0x90, 0x00, 0x6e, 0xa0, 0x00, 0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04,
0xa5, 0xb0, 0x00, 0x4a, 0x81, 0x04, 0x06, 0x91, 0x00, 0x6e, 0xa0, 0x00,
0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04, 0xa5, 0xb0, 0x00, 0xb8, 0x81, 0x04,
0x4a, 0x91, 0x00, 0x6e, 0xa0, 0x00, 0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04,
0xa5, 0xb0, 0x00, 0x6e, 0xa0, 0x04, 0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04,
0xa5, 0xb0, 0x00, 0x9f, 0x81, 0x04, 0x4a, 0x91, 0x00, 0x68, 0xa0, 0x00,
0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04, 0xa5, 0xb0, 0x00, 0x68, 0xa0, 0x04,
0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04, 0xa5, 0xb0, 0x00, 0x68, 0xa0, 0x04,
0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04, 0xa5, 0xb0, 0x00, 0x68, 0xa0, 0x04,
0x00, 0xb0, 0x00, 0x00, 0xa0, 0x04, 0xa5, 0xb0, 0x00, 0x00, 0x80, 0x03,
0x00, 0x90, 0x00, 0x00, 0xb0, 0x00
}
for i = 1, 4 do
sound.setVolume(i, 0)
sound.setWaveType(i, "square")
end
local offset = 1
while offset < #tetris_cesnd do
local frequency = tetris_cesnd[offset] + bit32.band(tetris_cesnd[offset+1], 0x0F) * 0x100
local channel = math.floor(tetris_cesnd[offset+1] / 16) % 4
local type = math.floor(tetris_cesnd[offset+1] / 64)
local ticks = tetris_cesnd[offset+2]
if ticks > 0 then sleep(0.05 * ticks) end
if channel < 4 then
if type == 0 or type == 1 then sound.setWaveType(channel + 1, "sine")
elseif math.floor(tetris_cesnd[offset+1] / 16) == 0 then sound.setWaveType(channel + 1, "noise")
else sound.setWaveType(channel + 1, "square") end
sound.setFrequency(channel + 1, frequency)
sound.setVolume(channel + 1, 0.1)
end
offset = offset + 3
end
for i = 1, 4 do sound.fadeOut(i, 0.5) end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment