v0.9.5 beta — full feature build ready for distribution
Core additions since initial commit: - Platform-split MIDI (midi_linux.c / midi_windows.c) - Patch system: save/load/scan, subdirectory navigation, favourites - Config system: last MIDI device and CC mappings persisted to disk - Custom embedded pixel font and sprite sheet (no loose asset files at runtime) - Window icon embedded at runtime (SetWindowIcon) and in Windows .exe (windres) - chdirToExeDir() in platform.c so double-click launch finds patches/ correctly - About screen accessible from Master panel UI polish: - TITLE_BAR_H constant (20 px) — sprite buttons now fit inside title bars - All title bar text, button overlay text, and close/active labels vertically centred - Full-screen dim overlay computed from camera transform (no more partial coverage) - Patch browser: folder navigation, breadcrumb title, 256-slot limit lifted Build: - Makefile auto-discovers sources; embeds sprites/font/icon via xxd rules - Windows cross-compile: make PLATFORM=windows (mingw-w64 + windres) - windows.h isolated in platform.c to avoid Rectangle/CloseWindow conflicts with raylib.h - WIN_RES uses lazy = assignment so OBJ_DIR expands correctly for windres output path Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
91
source/config.c
Normal file
91
source/config.c
Normal file
@@ -0,0 +1,91 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "config.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#ifdef _WIN32
|
||||
# include <direct.h>
|
||||
# define MAKE_DIR(p) _mkdir(p)
|
||||
#else
|
||||
# include <sys/stat.h>
|
||||
# define MAKE_DIR(p) mkdir(p, 0755)
|
||||
#endif
|
||||
|
||||
void configSanitizeName(const char *in, char *out, int maxLen)
|
||||
{
|
||||
int i = 0;
|
||||
for (; in[i] && i < maxLen - 1; i++) {
|
||||
char c = in[i];
|
||||
if ((c >= 'A' && c <= 'Z') || (c >= 'a' && c <= 'z') ||
|
||||
(c >= '0' && c <= '9') || c == '_' || c == '-')
|
||||
out[i] = c;
|
||||
else
|
||||
out[i] = '_';
|
||||
}
|
||||
out[i] = '\0';
|
||||
}
|
||||
|
||||
int configSaveLastDevice(const char *deviceName)
|
||||
{
|
||||
MAKE_DIR(CONFIG_DIR);
|
||||
FILE *f = fopen(CONFIG_DIR "/last_device.txt", "w");
|
||||
if (!f) return 0;
|
||||
fprintf(f, "%s\n", deviceName);
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int configLoadLastDevice(char *out, int maxLen)
|
||||
{
|
||||
FILE *f = fopen(CONFIG_DIR "/last_device.txt", "r");
|
||||
if (!f) return 0;
|
||||
int ok = (fgets(out, maxLen, f) != NULL);
|
||||
fclose(f);
|
||||
if (!ok) return 0;
|
||||
int len = (int)strlen(out);
|
||||
if (len > 0 && out[len - 1] == '\n') out[len - 1] = '\0';
|
||||
return out[0] != '\0';
|
||||
}
|
||||
|
||||
int configSaveCcMappings(const char *deviceName, ConfigCcEntry *entries, int count)
|
||||
{
|
||||
MAKE_DIR(CONFIG_DIR);
|
||||
char safe[CONFIG_NAME_LEN];
|
||||
configSanitizeName(deviceName, safe, sizeof(safe));
|
||||
char path[CONFIG_NAME_LEN + 32];
|
||||
snprintf(path, sizeof(path), CONFIG_DIR "/cc_%s.cfg", safe);
|
||||
|
||||
FILE *f = fopen(path, "w");
|
||||
if (!f) return 0;
|
||||
for (int i = 0; i < count; i++)
|
||||
fprintf(f, "%d %d %f %f\n",
|
||||
entries[i].cc, entries[i].controlId,
|
||||
entries[i].min, entries[i].max);
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int configLoadCcMappings(const char *deviceName, ConfigCcEntry *entries, int maxCount)
|
||||
{
|
||||
char safe[CONFIG_NAME_LEN];
|
||||
configSanitizeName(deviceName, safe, sizeof(safe));
|
||||
char path[CONFIG_NAME_LEN + 32];
|
||||
snprintf(path, sizeof(path), CONFIG_DIR "/cc_%s.cfg", safe);
|
||||
|
||||
FILE *f = fopen(path, "r");
|
||||
if (!f) return 0;
|
||||
int count = 0;
|
||||
while (count < maxCount) {
|
||||
int cc, id;
|
||||
float mn, mx;
|
||||
if (fscanf(f, "%d %d %f %f\n", &cc, &id, &mn, &mx) != 4) break;
|
||||
if (cc >= 0 && cc < 128) {
|
||||
entries[count].cc = cc;
|
||||
entries[count].controlId = id;
|
||||
entries[count].min = mn;
|
||||
entries[count].max = mx;
|
||||
count++;
|
||||
}
|
||||
}
|
||||
fclose(f);
|
||||
return count;
|
||||
}
|
||||
165
source/font_data.c
Normal file
165
source/font_data.c
Normal file
@@ -0,0 +1,165 @@
|
||||
unsigned char font_png[] = {
|
||||
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
|
||||
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x70,
|
||||
0x08, 0x06, 0x00, 0x00, 0x00, 0xd6, 0xac, 0x78, 0xd2, 0x00, 0x00, 0x00,
|
||||
0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00,
|
||||
0x07, 0x51, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0xed, 0x9c, 0xdd, 0x76,
|
||||
0xe3, 0x30, 0x08, 0x84, 0x93, 0x9e, 0xbe, 0xff, 0x2b, 0x7b, 0x6f, 0x56,
|
||||
0x3d, 0x94, 0x0c, 0x30, 0x48, 0x72, 0x12, 0x37, 0xf3, 0x5d, 0x35, 0x96,
|
||||
0xf8, 0x91, 0x04, 0x08, 0xa7, 0xdd, 0xbd, 0xdd, 0xc4, 0x47, 0x73, 0x67,
|
||||
0x27, 0x1e, 0xc7, 0x71, 0xdc, 0x6e, 0xb7, 0xdb, 0xfd, 0x7e, 0x87, 0x32,
|
||||
0x76, 0x1c, 0xcd, 0x3d, 0x8e, 0xe3, 0x18, 0x9f, 0xed, 0xcf, 0x8c, 0x8d,
|
||||
0x4a, 0x9f, 0x9d, 0xc3, 0xfa, 0x98, 0x8d, 0x7b, 0x3d, 0xd1, 0xf3, 0x2b,
|
||||
0x33, 0xd6, 0xf4, 0xf5, 0x4c, 0x63, 0xc7, 0x7f, 0xec, 0x33, 0x3f, 0xc7,
|
||||
0xcf, 0xcb, 0xf4, 0xd9, 0xcf, 0x77, 0x43, 0x26, 0xcb, 0xe8, 0x1c, 0x7a,
|
||||
0xa2, 0xcf, 0x91, 0xef, 0xd9, 0xfa, 0x98, 0x75, 0x3d, 0x0b, 0xeb, 0xc3,
|
||||
0xb6, 0x00, 0xf0, 0x1b, 0x86, 0xc6, 0xec, 0x46, 0x66, 0xd9, 0x9c, 0x1d,
|
||||
0x00, 0x93, 0xc5, 0xd1, 0x18, 0x93, 0xf9, 0x59, 0x76, 0x67, 0x15, 0xc7,
|
||||
0x07, 0xf0, 0xbb, 0x56, 0x09, 0xef, 0xf7, 0xf7, 0x4e, 0xe5, 0x59, 0x10,
|
||||
0x54, 0x72, 0xcc, 0x06, 0x46, 0x87, 0x1f, 0x5d, 0x3b, 0x95, 0x8f, 0xbb,
|
||||
0x88, 0xb2, 0x3d, 0xba, 0x42, 0x58, 0x5f, 0x33, 0xfd, 0x83, 0xce, 0x7a,
|
||||
0x50, 0xa0, 0xbf, 0xcd, 0x15, 0xe0, 0x83, 0xa7, 0xb3, 0x69, 0xf6, 0x0a,
|
||||
0xc8, 0x4a, 0xf0, 0x59, 0x20, 0x9f, 0xba, 0x81, 0xb6, 0x7a, 0x3d, 0x74,
|
||||
0xae, 0xcd, 0x97, 0x54, 0x27, 0x9f, 0xe1, 0x7e, 0xcc, 0x83, 0xc6, 0xb3,
|
||||
0xcf, 0x91, 0xad, 0x8e, 0x7f, 0xcc, 0x26, 0x46, 0xfe, 0x79, 0xbf, 0xa2,
|
||||
0xaa, 0x10, 0xe9, 0x3f, 0x33, 0x00, 0xb2, 0xb5, 0x6d, 0x7b, 0x0b, 0xd8,
|
||||
0x01, 0x6b, 0x03, 0xcd, 0xf3, 0x11, 0x3e, 0xaa, 0xc2, 0x8c, 0x7d, 0xab,
|
||||
0x27, 0x7b, 0xde, 0xa5, 0x5a, 0xdf, 0x8c, 0xcf, 0x3b, 0xec, 0x8a, 0x0f,
|
||||
0x26, 0x7c, 0xb5, 0xe9, 0x34, 0x61, 0x8c, 0xfc, 0xca, 0x78, 0x54, 0xda,
|
||||
0xb2, 0x39, 0x3b, 0xe5, 0x23, 0x1d, 0x67, 0x37, 0x94, 0x33, 0x4d, 0x22,
|
||||
0xf3, 0xa6, 0x63, 0xe7, 0x7d, 0xd9, 0x41, 0xf4, 0xba, 0x15, 0x29, 0x40,
|
||||
0x63, 0x91, 0x7c, 0x67, 0x3c, 0x5b, 0xe8, 0xdd, 0xc1, 0xea, 0xf7, 0xf2,
|
||||
0x3b, 0xfc, 0x47, 0x3a, 0x98, 0xfe, 0x21, 0x5b, 0x5f, 0x76, 0x90, 0x07,
|
||||
0x00, 0xcd, 0x45, 0x36, 0xb2, 0xf5, 0x7d, 0x7b, 0x25, 0x59, 0xb6, 0x64,
|
||||
0x0b, 0xc8, 0x0e, 0x6f, 0x57, 0xa6, 0xb0, 0xd9, 0x71, 0xc5, 0xbb, 0x6e,
|
||||
0x26, 0xf3, 0x2d, 0xf6, 0x7c, 0x7c, 0x2f, 0x91, 0xee, 0x95, 0x15, 0xb0,
|
||||
0x91, 0x82, 0x1a, 0x12, 0x26, 0x53, 0x99, 0x46, 0xc7, 0x8f, 0x33, 0x25,
|
||||
0x1a, 0xcd, 0xad, 0xb2, 0x3d, 0xb3, 0x5f, 0xf9, 0xf0, 0xcc, 0x2b, 0x60,
|
||||
0xf5, 0xf0, 0x59, 0x5d, 0x68, 0xec, 0xcb, 0x0f, 0xae, 0x38, 0xc0, 0xe8,
|
||||
0xa8, 0x2a, 0x4d, 0xa4, 0x97, 0xd5, 0x9d, 0xe9, 0xaf, 0x0e, 0x3f, 0x93,
|
||||
0x47, 0x57, 0x4f, 0xe4, 0x63, 0x77, 0x9c, 0x49, 0x86, 0xec, 0x0a, 0xa8,
|
||||
0xd6, 0x97, 0xd9, 0xfb, 0x42, 0x03, 0x9d, 0xc3, 0xf1, 0xc6, 0xa3, 0xac,
|
||||
0xad, 0xae, 0x0f, 0xbf, 0xb9, 0x4c, 0x06, 0xcf, 0xf8, 0x97, 0xe9, 0x7e,
|
||||
0x25, 0x4c, 0x10, 0x64, 0x30, 0xeb, 0x43, 0x01, 0xbc, 0xa5, 0x8b, 0x9e,
|
||||
0x89, 0xec, 0xce, 0x15, 0xc1, 0xc8, 0xb3, 0xe3, 0x6c, 0x60, 0xad, 0x5c,
|
||||
0x73, 0x99, 0x5c, 0x25, 0x33, 0x13, 0xa8, 0xac, 0xcc, 0x8e, 0x2a, 0x2f,
|
||||
0xfe, 0x18, 0x5b, 0x9a, 0x20, 0xa6, 0x41, 0x5a, 0x91, 0x3f, 0xa3, 0x02,
|
||||
0x21, 0xf9, 0xa8, 0x2f, 0x98, 0xb5, 0xef, 0xe5, 0xd1, 0x7c, 0x34, 0xee,
|
||||
0x61, 0xfd, 0x8b, 0xc6, 0x91, 0x8f, 0xe3, 0x39, 0xfd, 0x3d, 0x80, 0x17,
|
||||
0x8c, 0xc6, 0x90, 0xbc, 0x5f, 0x70, 0xe6, 0xd4, 0x4a, 0x1f, 0x12, 0xc1,
|
||||
0xda, 0x8f, 0x40, 0xfd, 0x09, 0xda, 0x07, 0x66, 0xce, 0x4a, 0x2f, 0x32,
|
||||
0x64, 0xb3, 0x46, 0x31, 0x7b, 0x8e, 0xd6, 0x4e, 0x7d, 0x0f, 0xe0, 0xa3,
|
||||
0x69, 0x44, 0x1d, 0x73, 0xa7, 0x59, 0xfc, 0x06, 0x65, 0xf3, 0x90, 0xfe,
|
||||
0xca, 0x56, 0x67, 0x7c, 0xe7, 0x3d, 0xc8, 0xea, 0xda, 0xd1, 0x88, 0x56,
|
||||
0x41, 0xdb, 0x3d, 0x93, 0xad, 0x7f, 0x0f, 0x70, 0x76, 0xa7, 0xdd, 0x69,
|
||||
0xe4, 0x32, 0xfb, 0xd1, 0xbc, 0x9d, 0x55, 0x27, 0xe3, 0x8c, 0xbd, 0x99,
|
||||
0xe5, 0x21, 0x00, 0xd8, 0x8d, 0x43, 0xd8, 0x2a, 0xd2, 0x8d, 0x44, 0x86,
|
||||
0x5d, 0xfa, 0xec, 0xbd, 0x19, 0xe9, 0xef, 0x04, 0x43, 0xb7, 0xbb, 0x9e,
|
||||
0xed, 0xc6, 0x87, 0xdf, 0x99, 0xff, 0x63, 0xef, 0x59, 0x9d, 0x3f, 0x01,
|
||||
0xc0, 0x1c, 0xd8, 0x6a, 0xb6, 0x58, 0x1b, 0xd9, 0x26, 0x44, 0x63, 0x51,
|
||||
0x23, 0x13, 0x7d, 0xce, 0xec, 0xbf, 0x82, 0xaa, 0xf7, 0xa8, 0xd6, 0x17,
|
||||
0x3d, 0xf3, 0xe3, 0xed, 0x00, 0x40, 0x91, 0x33, 0xb3, 0x51, 0x91, 0xbc,
|
||||
0x75, 0x2a, 0xb3, 0x71, 0xd6, 0x15, 0xc2, 0xda, 0x8f, 0xf0, 0x1b, 0x9a,
|
||||
0xf5, 0x44, 0x5d, 0xbd, 0xcf, 0x08, 0xc8, 0x5d, 0xe7, 0x2b, 0xfe, 0x20,
|
||||
0x5b, 0xdf, 0xa3, 0x77, 0xcb, 0x8f, 0x39, 0x28, 0xb3, 0xec, 0xb3, 0xdd,
|
||||
0xe3, 0xd5, 0xe7, 0x48, 0x3e, 0xdb, 0x03, 0x66, 0xfd, 0x4c, 0x73, 0xeb,
|
||||
0x65, 0xd1, 0xdc, 0x4e, 0x76, 0x3f, 0xfc, 0x2e, 0x00, 0x2d, 0x92, 0xed,
|
||||
0x9a, 0x77, 0xc8, 0x47, 0xba, 0xde, 0x01, 0xb4, 0x9e, 0x2e, 0x77, 0x43,
|
||||
0xa4, 0xbf, 0x23, 0xb3, 0xea, 0x1f, 0xfc, 0xab, 0xe0, 0xc8, 0x90, 0xbd,
|
||||
0xf3, 0x32, 0xa5, 0xcc, 0xb8, 0x5d, 0xd0, 0xab, 0x0f, 0xdb, 0x66, 0x22,
|
||||
0xca, 0xc6, 0x28, 0x68, 0xbd, 0xff, 0x7e, 0x0d, 0xec, 0x9a, 0xaa, 0x4a,
|
||||
0xc9, 0x10, 0xf5, 0x29, 0x15, 0x30, 0x00, 0x56, 0x22, 0xdc, 0xca, 0xa3,
|
||||
0x80, 0x39, 0x0c, 0x2b, 0x36, 0x32, 0xbb, 0x99, 0x6e, 0xe6, 0xba, 0x42,
|
||||
0x07, 0xc7, 0x1c, 0xee, 0x4a, 0x93, 0xb5, 0x12, 0x04, 0x6c, 0x85, 0x45,
|
||||
0xfc, 0x0a, 0x80, 0xaa, 0x53, 0xac, 0x36, 0x38, 0xeb, 0x84, 0x51, 0x96,
|
||||
0xec, 0x0a, 0x82, 0x2a, 0x43, 0x19, 0xac, 0x1c, 0xf2, 0xab, 0xd2, 0xbb,
|
||||
0x72, 0xf8, 0x48, 0x6e, 0x66, 0x6f, 0xd8, 0x0a, 0x6d, 0xf9, 0xf5, 0x45,
|
||||
0x50, 0x56, 0xfa, 0xab, 0xfb, 0x1c, 0xcd, 0xb3, 0xa5, 0xb5, 0xb2, 0xb1,
|
||||
0x8a, 0xb5, 0x1d, 0xd9, 0x89, 0x02, 0x18, 0x35, 0x7d, 0x1d, 0xff, 0xb3,
|
||||
0xf2, 0x3b, 0x53, 0x09, 0xd0, 0x1e, 0x56, 0x32, 0xd9, 0xb3, 0x2c, 0x98,
|
||||
0xe8, 0x7f, 0x19, 0xc4, 0x66, 0x30, 0x1a, 0x5f, 0xcd, 0x50, 0x7f, 0x30,
|
||||
0x59, 0x95, 0xc9, 0x9e, 0x67, 0x3f, 0x77, 0xfc, 0x58, 0x81, 0xb9, 0x02,
|
||||
0x51, 0xe5, 0x3d, 0xeb, 0xda, 0x14, 0x1f, 0x4e, 0x9a, 0xad, 0x2c, 0x2b,
|
||||
0xb2, 0x3b, 0xf5, 0xf9, 0x0c, 0x61, 0xe5, 0x59, 0x7b, 0xb3, 0xf3, 0x18,
|
||||
0xb9, 0x67, 0x5c, 0x93, 0x88, 0x87, 0x5f, 0x06, 0x45, 0xf7, 0xce, 0xaa,
|
||||
0x83, 0xb3, 0x87, 0xc3, 0xca, 0x57, 0x9b, 0xbc, 0x6a, 0xbf, 0x23, 0xb3,
|
||||
0x2b, 0x90, 0xb2, 0xb9, 0xe3, 0x9c, 0xfc, 0xb9, 0x64, 0xf3, 0x91, 0x8f,
|
||||
0xad, 0x4d, 0x8c, 0xe6, 0x75, 0x7b, 0x81, 0xaa, 0x37, 0xf0, 0x3a, 0x2a,
|
||||
0x79, 0xf6, 0xf0, 0x2b, 0xf9, 0xc8, 0xff, 0x48, 0x4f, 0x44, 0x66, 0x2f,
|
||||
0xd3, 0x51, 0x1d, 0x1e, 0xe3, 0x3f, 0xb2, 0x91, 0x7d, 0xa6, 0xde, 0x02,
|
||||
0xbc, 0x91, 0x08, 0xdf, 0xac, 0x45, 0xfa, 0xd8, 0x05, 0x66, 0x3e, 0x9c,
|
||||
0x51, 0xa5, 0x58, 0xff, 0x67, 0x59, 0xbd, 0x2a, 0xab, 0x33, 0x98, 0xd1,
|
||||
0x4b, 0xfd, 0x41, 0x08, 0x7b, 0x40, 0x67, 0x53, 0x2d, 0x30, 0x0b, 0xb8,
|
||||
0x33, 0xbb, 0xe8, 0xee, 0xc1, 0x9e, 0x11, 0x5c, 0xb3, 0x3c, 0xe5, 0x3f,
|
||||
0x88, 0xf0, 0xec, 0x38, 0x8c, 0xae, 0xfc, 0xdd, 0xb0, 0x62, 0x77, 0x85,
|
||||
0x55, 0xfb, 0x67, 0xf8, 0x4f, 0x55, 0x00, 0x9f, 0x41, 0xf6, 0x73, 0x74,
|
||||
0x8f, 0xdb, 0xe7, 0x28, 0x03, 0xab, 0xf1, 0xcc, 0xbe, 0x97, 0xaf, 0x60,
|
||||
0xef, 0xf8, 0x59, 0x66, 0xfc, 0x7b, 0x66, 0x15, 0x58, 0xdd, 0x3f, 0xf1,
|
||||
0x87, 0x79, 0xab, 0x28, 0x58, 0x6d, 0x92, 0xae, 0x40, 0x96, 0x85, 0xd5,
|
||||
0x5b, 0x00, 0x1a, 0x63, 0x6d, 0x46, 0x72, 0xdf, 0xde, 0x40, 0xe5, 0x1c,
|
||||
0x1a, 0x67, 0x0e, 0x2e, 0x93, 0xaf, 0x60, 0x5e, 0x6b, 0x90, 0xbf, 0x9d,
|
||||
0xf1, 0x4a, 0x3f, 0xf3, 0xd9, 0xc3, 0x5c, 0x8f, 0x0c, 0xfe, 0xed, 0x84,
|
||||
0xd1, 0xc1, 0xec, 0xd1, 0xed, 0x96, 0xfc, 0x41, 0x88, 0x75, 0xd8, 0x2a,
|
||||
0xcb, 0x16, 0x7d, 0x18, 0x22, 0x67, 0x22, 0xfd, 0xd9, 0x67, 0x06, 0xe4,
|
||||
0x6f, 0x67, 0x3c, 0x7b, 0xce, 0xc2, 0x34, 0x69, 0xab, 0x4d, 0x60, 0x34,
|
||||
0x16, 0xed, 0xbd, 0x9f, 0xe3, 0x9f, 0x51, 0x6f, 0x01, 0x6c, 0x04, 0x46,
|
||||
0x0e, 0xa2, 0xcc, 0xf1, 0x73, 0x7d, 0x53, 0xf8, 0xcc, 0x6b, 0xc0, 0x67,
|
||||
0x6a, 0x64, 0x3f, 0xcb, 0xf4, 0xa8, 0x3a, 0x78, 0x1b, 0x2b, 0x41, 0x56,
|
||||
0x5d, 0x11, 0x95, 0x0f, 0xc8, 0x7e, 0xf8, 0xf7, 0x00, 0x68, 0x01, 0x6c,
|
||||
0x84, 0x47, 0xc1, 0x71, 0xbf, 0xe3, 0x6e, 0x7f, 0xd7, 0x61, 0x57, 0x9b,
|
||||
0x9b, 0x65, 0x48, 0x54, 0xae, 0xab, 0x31, 0x64, 0x3f, 0xdb, 0xf8, 0x1d,
|
||||
0x15, 0x20, 0xf3, 0x81, 0x39, 0x27, 0x2b, 0x0f, 0xaf, 0x80, 0x48, 0x78,
|
||||
0x6c, 0x60, 0x37, 0x9a, 0x51, 0x60, 0xcd, 0x64, 0x42, 0x74, 0x80, 0xe8,
|
||||
0x80, 0x3a, 0x1b, 0xed, 0xd7, 0x85, 0xfc, 0x63, 0xf6, 0xc6, 0xda, 0x3d,
|
||||
0xe3, 0x9a, 0x61, 0x2a, 0x50, 0x35, 0xee, 0xe7, 0xd2, 0xff, 0x34, 0x0c,
|
||||
0xdd, 0xa1, 0xec, 0x26, 0xa3, 0xcc, 0xef, 0x1c, 0xd0, 0x90, 0x47, 0xcf,
|
||||
0xec, 0xc2, 0xb2, 0xea, 0xe5, 0x03, 0x37, 0xb2, 0x8f, 0x6c, 0xb1, 0x41,
|
||||
0x17, 0xcd, 0xb5, 0xf3, 0xab, 0x6b, 0x14, 0xb1, 0xab, 0x62, 0xc2, 0xca,
|
||||
0x3c, 0xa3, 0xe8, 0x2c, 0xd0, 0xc6, 0x54, 0x19, 0xc3, 0x66, 0xe4, 0xea,
|
||||
0x9c, 0xb3, 0xe8, 0x54, 0xaf, 0x33, 0xc6, 0x5f, 0xf2, 0x55, 0xf0, 0x3b,
|
||||
0xc0, 0x64, 0xed, 0x27, 0xb0, 0xf5, 0x5f, 0x07, 0xaf, 0xc0, 0x94, 0x4e,
|
||||
0xf4, 0xbc, 0xa3, 0xcb, 0x8f, 0xfb, 0xb2, 0x5c, 0xdd, 0xa3, 0x99, 0xce,
|
||||
0xca, 0x46, 0x57, 0xd6, 0xcb, 0xed, 0x0c, 0x52, 0xab, 0x1f, 0xfe, 0x41,
|
||||
0x48, 0x26, 0x7c, 0xa6, 0x63, 0x95, 0xdd, 0x99, 0x2b, 0xc2, 0x83, 0x74,
|
||||
0xd8, 0x1e, 0x65, 0xf6, 0x7e, 0xdd, 0xcd, 0xd9, 0xfb, 0x3a, 0xf4, 0x3f,
|
||||
0xbc, 0x06, 0xb2, 0x82, 0x57, 0x24, 0xcb, 0xca, 0x95, 0xb7, 0x93, 0x5d,
|
||||
0x30, 0xc1, 0xb7, 0x3b, 0x40, 0xc3, 0x2b, 0x80, 0xcd, 0xf4, 0x5d, 0x9d,
|
||||
0xec, 0xec, 0xc6, 0x23, 0xfb, 0x48, 0x17, 0x53, 0x92, 0xdf, 0xa1, 0x12,
|
||||
0x3c, 0xcb, 0xee, 0xb0, 0x43, 0xff, 0xd3, 0xb0, 0x55, 0xc7, 0x7c, 0xc9,
|
||||
0xdd, 0xad, 0x3f, 0xd3, 0xd5, 0xb9, 0x8f, 0xdf, 0xa1, 0x12, 0x9c, 0xcd,
|
||||
0x47, 0xbd, 0x05, 0xcc, 0x34, 0x63, 0x9f, 0x10, 0x04, 0x83, 0x32, 0x53,
|
||||
0x3a, 0x1b, 0xb8, 0x2a, 0x8f, 0x74, 0xfd, 0x38, 0xfa, 0x22, 0xf9, 0x77,
|
||||
0x69, 0x0a, 0xcf, 0xe2, 0xf4, 0x0a, 0x30, 0x93, 0x4d, 0x57, 0xdc, 0x7c,
|
||||
0xfb, 0x0d, 0x9f, 0xfd, 0x19, 0xcd, 0xab, 0xf4, 0x54, 0x36, 0x32, 0xdd,
|
||||
0xd5, 0x98, 0x9f, 0xf3, 0xf0, 0x9f, 0x45, 0x47, 0x4e, 0xac, 0x94, 0xc3,
|
||||
0x4e, 0x10, 0x5c, 0xf5, 0xf0, 0xab, 0x9f, 0xed, 0xe7, 0x2a, 0x38, 0x66,
|
||||
0xf6, 0x7a, 0x34, 0xae, 0xf6, 0x75, 0xd9, 0xeb, 0x1d, 0xe3, 0xf6, 0xd9,
|
||||
0x77, 0x27, 0x52, 0xd1, 0x33, 0xf6, 0xa0, 0x98, 0x0e, 0xfb, 0x8a, 0x87,
|
||||
0xcf, 0x82, 0x0e, 0x25, 0xfb, 0x3e, 0xc3, 0x8e, 0x67, 0x67, 0xd1, 0xd9,
|
||||
0x7f, 0xfb, 0xf3, 0x90, 0x7f, 0x6a, 0x13, 0xb8, 0x73, 0x41, 0x57, 0x62,
|
||||
0x36, 0xe3, 0xd9, 0x4a, 0xe0, 0x83, 0x09, 0x55, 0x81, 0x88, 0xef, 0x6a,
|
||||
0xc3, 0xad, 0xa2, 0x6c, 0xae, 0x2f, 0x7d, 0xd1, 0x5c, 0x54, 0x09, 0xae,
|
||||
0x7e, 0xf8, 0xd1, 0x86, 0x8f, 0xf5, 0xac, 0x8e, 0xef, 0x20, 0xaa, 0x28,
|
||||
0x53, 0x9d, 0x3d, 0x1a, 0x8f, 0x60, 0xe5, 0x66, 0xf5, 0xb3, 0xac, 0xbe,
|
||||
0x45, 0x30, 0x7a, 0x99, 0x64, 0x61, 0xae, 0xc0, 0xae, 0x7c, 0xe4, 0x6b,
|
||||
0x76, 0xc5, 0x8c, 0xb1, 0x97, 0xfd, 0x32, 0xc8, 0x46, 0xfd, 0x55, 0x33,
|
||||
0xdf, 0x33, 0xd6, 0x84, 0xd6, 0x83, 0x9a, 0x43, 0x3f, 0x6f, 0x56, 0x9e,
|
||||
0xd9, 0xbf, 0xb7, 0xd9, 0xe3, 0x2c, 0xb3, 0xc6, 0xd5, 0x30, 0x23, 0xcb,
|
||||
0xc8, 0x8b, 0x47, 0x7e, 0xfe, 0xbb, 0x78, 0xc4, 0x98, 0x94, 0x35, 0x6d,
|
||||
0x11, 0x2b, 0x07, 0x39, 0x23, 0x53, 0xf9, 0x2b, 0x30, 0x3f, 0x6f, 0x01,
|
||||
0x77, 0x87, 0x9d, 0xe4, 0x9b, 0x94, 0xec, 0x1e, 0x63, 0xc6, 0xc7, 0x1c,
|
||||
0xd6, 0xc9, 0x99, 0x60, 0x53, 0x10, 0x70, 0xd0, 0x3d, 0x40, 0x76, 0x67,
|
||||
0xb3, 0x8d, 0x0d, 0x1a, 0xeb, 0x06, 0x4a, 0x37, 0x70, 0xde, 0xe6, 0xee,
|
||||
0x7b, 0x53, 0xb6, 0x34, 0x81, 0xb6, 0x79, 0xe9, 0x6e, 0xfa, 0x6a, 0xf6,
|
||||
0xa2, 0x4e, 0x57, 0x87, 0xce, 0xd3, 0xfa, 0xab, 0x60, 0xfb, 0x1e, 0x39,
|
||||
0xf3, 0x0d, 0xd4, 0x90, 0xf5, 0x63, 0xd5, 0x6b, 0x0c, 0xf3, 0x7a, 0x24,
|
||||
0xe6, 0xf8, 0xf5, 0xbb, 0x00, 0xd4, 0x00, 0x8e, 0xb1, 0xea, 0x9b, 0xa6,
|
||||
0x6e, 0xf6, 0x7b, 0x7d, 0xdd, 0x71, 0x71, 0x51, 0xf4, 0x1a, 0x28, 0x84,
|
||||
0x10, 0x42, 0x08, 0x21, 0x84, 0x10, 0x42, 0x08, 0x21, 0x84, 0x10, 0x42,
|
||||
0x08, 0x21, 0x84, 0x10, 0x42, 0x08, 0x21, 0x84, 0x10, 0x42, 0x08, 0x21,
|
||||
0x84, 0x10, 0x42, 0x08, 0x21, 0x84, 0x10, 0x42, 0x08, 0x21, 0x84, 0x10,
|
||||
0x42, 0x08, 0x21, 0x84, 0x10, 0x42, 0x08, 0x21, 0x84, 0x10, 0x42, 0x08,
|
||||
0x81, 0xf9, 0x07, 0x4e, 0x1f, 0xd4, 0x6d, 0xe4, 0x18, 0xb7, 0xb8, 0x00,
|
||||
0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42, 0x60, 0x82
|
||||
};
|
||||
unsigned int font_png_len = 1943;
|
||||
312
source/icon_data.c
Normal file
312
source/icon_data.c
Normal file
@@ -0,0 +1,312 @@
|
||||
unsigned char icon_png[] = {
|
||||
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
|
||||
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x80,
|
||||
0x08, 0x06, 0x00, 0x00, 0x00, 0xc3, 0x3e, 0x61, 0xcb, 0x00, 0x00, 0x00,
|
||||
0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00,
|
||||
0x0e, 0x2d, 0x49, 0x44, 0x41, 0x54, 0x78, 0x9c, 0xed, 0x5d, 0x3b, 0x6f,
|
||||
0x1b, 0xc7, 0x16, 0xfe, 0x78, 0x13, 0x58, 0x8a, 0x40, 0x3d, 0x6c, 0xfd,
|
||||
0x98, 0x54, 0x41, 0x0a, 0x23, 0x4d, 0xe2, 0x2e, 0x69, 0x0c, 0xd8, 0x95,
|
||||
0x00, 0x03, 0x6e, 0x2c, 0x20, 0x80, 0x85, 0x14, 0x4a, 0x65, 0x15, 0x81,
|
||||
0x02, 0x04, 0xb0, 0x1a, 0x03, 0x04, 0x5c, 0x39, 0x40, 0x1a, 0xdd, 0x2e,
|
||||
0x57, 0xcd, 0x45, 0x8a, 0x8b, 0x5b, 0xdd, 0x1f, 0x43, 0x89, 0x22, 0x09,
|
||||
0xd9, 0x32, 0x10, 0xf0, 0x16, 0xf4, 0x8c, 0x67, 0x67, 0xce, 0xcc, 0x9c,
|
||||
0x79, 0xec, 0xce, 0x2c, 0xcd, 0x0f, 0xa0, 0x4d, 0x2d, 0x97, 0xcb, 0xd9,
|
||||
0x39, 0xdf, 0x9e, 0xe7, 0x3c, 0x06, 0x58, 0x03, 0x00, 0x16, 0xca, 0xfb,
|
||||
0x41, 0xb1, 0x56, 0x14, 0xc0, 0x2a, 0xde, 0xec, 0x42, 0xfb, 0xdb, 0x77,
|
||||
0x8f, 0xfa, 0xf9, 0x9c, 0xef, 0xac, 0x0c, 0xfe, 0x51, 0xba, 0x01, 0x99,
|
||||
0x41, 0x09, 0x93, 0x3a, 0xd6, 0xf8, 0xec, 0x74, 0xf4, 0x06, 0xa3, 0xf3,
|
||||
0x8b, 0x90, 0xdf, 0x50, 0x5f, 0xbd, 0xc6, 0xe7, 0xa5, 0x1b, 0xd0, 0x06,
|
||||
0x4e, 0x47, 0x6f, 0xe4, 0xfb, 0xa3, 0x27, 0x8f, 0x81, 0xa5, 0xa0, 0x9c,
|
||||
0x4f, 0xf5, 0xe4, 0x72, 0x8c, 0xd1, 0xf9, 0x05, 0x9e, 0xfc, 0xf0, 0xad,
|
||||
0xeb, 0x7c, 0x1b, 0xc1, 0x7a, 0xab, 0x31, 0xfa, 0x42, 0x80, 0x50, 0xb5,
|
||||
0xce, 0xbe, 0xa6, 0x4a, 0x96, 0xc9, 0xe5, 0x98, 0xd5, 0x86, 0xdf, 0xff,
|
||||
0xfc, 0x0b, 0x00, 0xf0, 0xe8, 0xc1, 0xfd, 0x0c, 0xcd, 0x28, 0x8b, 0x3e,
|
||||
0x98, 0x80, 0x50, 0xb5, 0xde, 0x80, 0x22, 0xe0, 0x2c, 0xea, 0x5a, 0x08,
|
||||
0x7f, 0x55, 0xd0, 0x07, 0x02, 0x00, 0x00, 0x8e, 0x4f, 0xcf, 0x1a, 0x4f,
|
||||
0x2b, 0xba, 0xb5, 0xbf, 0x0b, 0x00, 0x18, 0x9d, 0x5f, 0xe0, 0xe6, 0xdd,
|
||||
0x6d, 0xe8, 0xf7, 0xaa, 0xf6, 0x17, 0x7a, 0x43, 0x00, 0x00, 0x98, 0x4e,
|
||||
0xae, 0x74, 0x12, 0x84, 0x62, 0xa1, 0xfd, 0xcf, 0x39, 0xd7, 0xc0, 0xcd,
|
||||
0xbb, 0x5b, 0x0e, 0x11, 0x92, 0x34, 0x57, 0x57, 0xe8, 0x15, 0x01, 0x00,
|
||||
0x83, 0x04, 0xac, 0x0e, 0x4d, 0xd0, 0x1c, 0xb1, 0x4f, 0xee, 0x02, 0x00,
|
||||
0xfe, 0xf9, 0xef, 0xff, 0xca, 0x57, 0xad, 0x28, 0x4d, 0x80, 0x28, 0xf5,
|
||||
0x38, 0x9d, 0x5c, 0xd9, 0xae, 0x65, 0x05, 0xa5, 0x39, 0xb8, 0xc7, 0x5c,
|
||||
0xc7, 0x43, 0xdb, 0x51, 0x1b, 0x4a, 0x46, 0x01, 0x7a, 0x47, 0x05, 0x85,
|
||||
0x53, 0xc7, 0xa7, 0x67, 0x38, 0x39, 0x7a, 0x46, 0x5d, 0xc7, 0x0a, 0xae,
|
||||
0x10, 0x6d, 0xe7, 0x4d, 0x2e, 0xc7, 0xd8, 0xbb, 0xb7, 0x4f, 0x7d, 0x24,
|
||||
0xda, 0xde, 0x88, 0x14, 0xde, 0xde, 0xbe, 0x07, 0x00, 0x7c, 0xb1, 0x71,
|
||||
0x87, 0xdb, 0xc4, 0xce, 0x51, 0x4a, 0x03, 0xc8, 0x8e, 0x3a, 0x3e, 0x3d,
|
||||
0x23, 0x8f, 0xeb, 0xf8, 0x20, 0x6c, 0x2f, 0x12, 0x7d, 0x04, 0x2f, 0x26,
|
||||
0x97, 0x63, 0x5b, 0xb8, 0x28, 0xdb, 0xae, 0x27, 0x95, 0x04, 0x11, 0x6a,
|
||||
0x44, 0xa9, 0x04, 0xc6, 0x02, 0x68, 0x0a, 0x5f, 0x13, 0xf0, 0x40, 0x3f,
|
||||
0x57, 0x3f, 0x5f, 0xc5, 0xce, 0xde, 0xdd, 0xac, 0x8d, 0xe3, 0x62, 0xef,
|
||||
0xde, 0xbe, 0x48, 0x1c, 0x49, 0xa8, 0xc2, 0xdf, 0xda, 0xdc, 0x90, 0xef,
|
||||
0x95, 0x9c, 0x81, 0xde, 0xe7, 0x45, 0x53, 0xd1, 0xd5, 0x24, 0x82, 0x14,
|
||||
0x95, 0x0e, 0x04, 0x9a, 0x83, 0xe9, 0xe4, 0xaa, 0x18, 0x09, 0x5c, 0x29,
|
||||
0x64, 0x11, 0x29, 0x28, 0x24, 0xe1, 0x08, 0x5f, 0x1c, 0xef, 0x84, 0x04,
|
||||
0xa5, 0x4c, 0xc0, 0x00, 0x30, 0xd5, 0x3a, 0x61, 0x0e, 0xd8, 0xf6, 0xdd,
|
||||
0xe2, 0x18, 0xb6, 0x0a, 0x87, 0x39, 0xe0, 0x40, 0xde, 0x5b, 0xc9, 0x68,
|
||||
0xa1, 0x74, 0x14, 0x60, 0xc0, 0xa6, 0xe6, 0x6d, 0xc7, 0x55, 0x94, 0x20,
|
||||
0x01, 0xe0, 0x4d, 0x21, 0xab, 0x30, 0xf2, 0x10, 0xa5, 0x43, 0xc4, 0xd2,
|
||||
0x45, 0x0c, 0xc3, 0x17, 0x48, 0x85, 0xcd, 0x59, 0x0c, 0x71, 0x0e, 0x3f,
|
||||
0x14, 0x90, 0xa2, 0xae, 0x41, 0x45, 0x09, 0xba, 0x9f, 0xa0, 0x42, 0x27,
|
||||
0xc0, 0xf7, 0xdf, 0x7c, 0x25, 0xde, 0x76, 0x22, 0x9b, 0xb6, 0x7e, 0x84,
|
||||
0xeb, 0xd8, 0x24, 0x13, 0x80, 0x1b, 0x1d, 0x00, 0x3c, 0x01, 0xda, 0x84,
|
||||
0x1f, 0x72, 0x3d, 0x2e, 0x09, 0x7e, 0xff, 0xf3, 0x2f, 0x23, 0x44, 0xec,
|
||||
0x9a, 0x00, 0x6d, 0x38, 0x81, 0x9d, 0x38, 0x36, 0x0e, 0xc1, 0x73, 0xcb,
|
||||
0xb8, 0x3e, 0x78, 0xaf, 0x23, 0xc8, 0xa2, 0x13, 0x81, 0xca, 0x17, 0x50,
|
||||
0xce, 0xa2, 0x48, 0x29, 0xef, 0xef, 0x6e, 0x03, 0xe8, 0x5e, 0xf8, 0x40,
|
||||
0x8b, 0x51, 0xc0, 0xc1, 0xe1, 0x73, 0x00, 0xc0, 0x70, 0x7b, 0x07, 0x2f,
|
||||
0x4f, 0x7e, 0x16, 0x87, 0x45, 0x07, 0x36, 0x6e, 0x70, 0x36, 0xbd, 0xc6,
|
||||
0xf6, 0xce, 0xae, 0xf7, 0x9a, 0x81, 0x42, 0x6f, 0x03, 0xa4, 0x17, 0xaf,
|
||||
0x6a, 0x0d, 0x41, 0x06, 0x47, 0xd2, 0xc8, 0xc0, 0xf8, 0x7a, 0xd6, 0x08,
|
||||
0x19, 0xbb, 0x44, 0xeb, 0x61, 0xe0, 0x7c, 0x36, 0xc5, 0xe1, 0xf1, 0x0b,
|
||||
0x00, 0xa0, 0x88, 0x20, 0x31, 0x9b, 0x5e, 0x03, 0x00, 0x49, 0x04, 0x8b,
|
||||
0xe0, 0x4b, 0xfb, 0x2f, 0xc0, 0xc7, 0x36, 0xc8, 0xfb, 0x51, 0xb5, 0x82,
|
||||
0x70, 0x0e, 0x39, 0x44, 0xf0, 0x8c, 0x2d, 0x68, 0x2d, 0x57, 0xd0, 0x49,
|
||||
0x14, 0x30, 0x9f, 0x4d, 0x1b, 0x44, 0x50, 0xa1, 0x1f, 0x13, 0x44, 0x00,
|
||||
0x96, 0x82, 0x27, 0x84, 0x3f, 0x40, 0x1d, 0xc2, 0x57, 0x31, 0x80, 0xd6,
|
||||
0xae, 0xa3, 0x27, 0x8f, 0x25, 0x19, 0x02, 0xc3, 0xc5, 0x90, 0x5c, 0x41,
|
||||
0x32, 0xda, 0xe8, 0x48, 0xd9, 0x30, 0x61, 0x06, 0x54, 0x0c, 0xb7, 0x77,
|
||||
0x58, 0x17, 0x51, 0xb4, 0x85, 0x40, 0x4a, 0x5b, 0xa5, 0xb3, 0xe9, 0x4b,
|
||||
0x18, 0x29, 0xea, 0x3c, 0xb5, 0x6f, 0x64, 0x3f, 0xa8, 0x3e, 0x82, 0x4d,
|
||||
0x1b, 0x10, 0xc9, 0xa2, 0x86, 0x80, 0xd5, 0x68, 0x21, 0xa7, 0xaf, 0xd0,
|
||||
0x86, 0x09, 0x68, 0x14, 0x45, 0x74, 0xcc, 0x67, 0x53, 0x27, 0x09, 0x32,
|
||||
0x0b, 0xde, 0x40, 0x87, 0x59, 0x43, 0xd9, 0x0f, 0x81, 0x66, 0xc1, 0x2a,
|
||||
0xf8, 0x36, 0xd0, 0xaa, 0x09, 0x78, 0xfd, 0xf2, 0x57, 0xf2, 0xb8, 0x30,
|
||||
0x09, 0x3a, 0x34, 0xe1, 0xb7, 0xa6, 0xea, 0x3b, 0x4c, 0x18, 0x19, 0x66,
|
||||
0x41, 0x40, 0x37, 0x0b, 0xb6, 0x94, 0xf2, 0xf8, 0x7a, 0xd6, 0x5e, 0xeb,
|
||||
0xd0, 0xae, 0x2d, 0x75, 0x9a, 0x02, 0x01, 0xa1, 0x0d, 0x08, 0xe1, 0x67,
|
||||
0x6f, 0x8b, 0x9e, 0x6f, 0xa0, 0x34, 0x41, 0x46, 0x13, 0x40, 0xb6, 0x01,
|
||||
0x30, 0xc3, 0x46, 0x8e, 0x93, 0x48, 0x84, 0x8a, 0x40, 0x41, 0x13, 0xc0,
|
||||
0xf1, 0x4a, 0xa5, 0x0a, 0xbc, 0x99, 0xcf, 0xb1, 0x35, 0x1c, 0x92, 0x17,
|
||||
0x6a, 0x5b, 0xe5, 0xbb, 0x20, 0x34, 0x41, 0x87, 0x26, 0x01, 0x00, 0x16,
|
||||
0x7a, 0xfe, 0x80, 0x13, 0x32, 0x8e, 0xaf, 0x67, 0x7a, 0x32, 0xa9, 0x58,
|
||||
0x14, 0xe0, 0xf2, 0x4a, 0x17, 0xb6, 0xf3, 0x6e, 0xe6, 0x73, 0xdc, 0xcc,
|
||||
0xe7, 0x8d, 0x2f, 0x68, 0x26, 0xa2, 0x98, 0x77, 0x3f, 0x9d, 0x5c, 0x75,
|
||||
0x6d, 0x16, 0x00, 0x98, 0x26, 0xc1, 0x85, 0x36, 0x84, 0x0f, 0x24, 0x38,
|
||||
0x81, 0x0f, 0x0f, 0x9e, 0xca, 0xf7, 0x7f, 0xbc, 0x7e, 0xa5, 0x7e, 0xe4,
|
||||
0x0c, 0x4f, 0x04, 0x09, 0xb4, 0xef, 0x54, 0x11, 0xd6, 0x75, 0x4c, 0x02,
|
||||
0xe9, 0x20, 0x06, 0x0e, 0x62, 0xc9, 0xda, 0x57, 0x59, 0x9c, 0xc0, 0x87,
|
||||
0x07, 0x4f, 0xe5, 0x8b, 0xfa, 0x4c, 0x47, 0x8d, 0xc2, 0x2f, 0x00, 0x43,
|
||||
0x13, 0x24, 0x94, 0x97, 0xa3, 0x87, 0x9f, 0x47, 0x6b, 0x80, 0x3f, 0x5e,
|
||||
0xbf, 0x62, 0x0b, 0x5c, 0xff, 0x9e, 0x82, 0x4f, 0x55, 0xf8, 0x02, 0xa4,
|
||||
0x26, 0x60, 0xa6, 0x91, 0x7d, 0x73, 0x1e, 0x59, 0x7d, 0x1b, 0xa3, 0x01,
|
||||
0xa2, 0x85, 0xb6, 0x16, 0x3e, 0x89, 0x50, 0x9f, 0xc0, 0x78, 0xca, 0x47,
|
||||
0xe7, 0x17, 0x18, 0x9d, 0x5f, 0xe8, 0x39, 0x03, 0x96, 0x26, 0x88, 0x35,
|
||||
0x01, 0x03, 0xc0, 0x10, 0xa8, 0x13, 0x6b, 0xe1, 0x3b, 0xe1, 0x25, 0x01,
|
||||
0x95, 0x27, 0x10, 0x82, 0x17, 0x18, 0x5f, 0xcf, 0x82, 0xa7, 0xae, 0x25,
|
||||
0x67, 0x02, 0x6d, 0xa6, 0xc0, 0x81, 0xb5, 0xf0, 0x69, 0x90, 0x19, 0x54,
|
||||
0xd5, 0x1c, 0x70, 0xa6, 0xb0, 0x87, 0x4e, 0x58, 0x4d, 0x71, 0x02, 0x65,
|
||||
0xd8, 0xe6, 0xd3, 0x04, 0xca, 0xe7, 0x6b, 0xe1, 0xbb, 0x31, 0x00, 0xcc,
|
||||
0x41, 0x29, 0x5c, 0xc7, 0x30, 0x26, 0x54, 0x6c, 0xbd, 0x1a, 0x18, 0x62,
|
||||
0x26, 0xd6, 0x00, 0xe0, 0x20, 0x41, 0x42, 0x45, 0xd1, 0x0a, 0x1f, 0x01,
|
||||
0xf4, 0xf0, 0xc2, 0x99, 0xec, 0xd1, 0xb1, 0xb6, 0xfb, 0x69, 0xa0, 0x86,
|
||||
0xa7, 0x51, 0x24, 0x78, 0xf2, 0xc3, 0xb7, 0xae, 0xa1, 0xe7, 0x4e, 0xb8,
|
||||
0x08, 0xe0, 0x0b, 0x33, 0x42, 0xe2, 0xcd, 0x2a, 0x84, 0x3f, 0x9b, 0x5e,
|
||||
0x37, 0xc6, 0x1b, 0x54, 0x0c, 0xef, 0x6a, 0x26, 0x82, 0x08, 0xa9, 0x19,
|
||||
0x42, 0xaf, 0x13, 0xa8, 0x17, 0x72, 0x6e, 0xe6, 0x73, 0x43, 0xad, 0x7b,
|
||||
0x92, 0x3d, 0x55, 0x08, 0x5f, 0x85, 0x6b, 0xf4, 0x51, 0x45, 0x18, 0xe0,
|
||||
0x43, 0xdd, 0xc0, 0x35, 0x57, 0x51, 0x3b, 0x3f, 0x18, 0xc1, 0x51, 0xc0,
|
||||
0xd6, 0x70, 0x88, 0x83, 0xc3, 0xe7, 0x46, 0x5e, 0x5f, 0x45, 0x5f, 0xec,
|
||||
0x3e, 0x77, 0x2c, 0x62, 0x69, 0x44, 0xa4, 0x8b, 0xd9, 0x88, 0x0e, 0x03,
|
||||
0x45, 0x75, 0xcf, 0x45, 0x04, 0x54, 0xf8, 0xf4, 0xeb, 0xa8, 0x5c, 0x1b,
|
||||
0x38, 0x07, 0xd7, 0x58, 0xe0, 0x3a, 0xdf, 0x90, 0x87, 0xcb, 0x07, 0x18,
|
||||
0x00, 0xcb, 0x8a, 0x9d, 0x6d, 0x60, 0x07, 0xb0, 0x24, 0x82, 0x5a, 0xea,
|
||||
0xed, 0xcb, 0xd3, 0xaf, 0xa3, 0x62, 0xff, 0x80, 0x8c, 0x0a, 0x08, 0x70,
|
||||
0xfc, 0x32, 0xe3, 0x73, 0x9f, 0x06, 0x90, 0x0c, 0x14, 0x24, 0xb0, 0x0d,
|
||||
0xee, 0xd8, 0x1a, 0x0e, 0x75, 0x6d, 0x50, 0xfd, 0xd3, 0xbf, 0x0a, 0x38,
|
||||
0x1d, 0xbd, 0x31, 0xc8, 0x41, 0x25, 0x8c, 0x6c, 0xb3, 0x93, 0x38, 0x26,
|
||||
0xa0, 0xa1, 0x86, 0x5e, 0xbf, 0xfc, 0xd5, 0x4a, 0x82, 0xbe, 0x3e, 0xfd,
|
||||
0x3d, 0x80, 0xd3, 0x21, 0xe4, 0x4c, 0x4c, 0xb1, 0x81, 0x9b, 0x08, 0x0a,
|
||||
0x1d, 0xac, 0xb1, 0x7e, 0xfa, 0x0b, 0x43, 0x8d, 0x10, 0x5c, 0x73, 0x13,
|
||||
0xab, 0x9b, 0x1d, 0xbc, 0x46, 0x3e, 0x4c, 0x2e, 0xc7, 0xde, 0x3c, 0x41,
|
||||
0x48, 0x14, 0xe0, 0x74, 0x30, 0x14, 0x47, 0xb1, 0xda, 0xa7, 0x5f, 0x1d,
|
||||
0x89, 0xcc, 0x9d, 0x9f, 0x50, 0x11, 0xbc, 0x79, 0x01, 0x15, 0x84, 0xd3,
|
||||
0x48, 0xca, 0x45, 0x27, 0x40, 0xaf, 0x56, 0xb8, 0x4a, 0x81, 0x20, 0x43,
|
||||
0x0f, 0x89, 0xe0, 0x04, 0x57, 0xf0, 0x02, 0xaa, 0x09, 0x60, 0x09, 0xff,
|
||||
0xf0, 0xf8, 0x85, 0xd1, 0x69, 0xae, 0x30, 0xb1, 0x26, 0x50, 0x39, 0x0b,
|
||||
0x6a, 0x7e, 0x42, 0xc5, 0xe0, 0x86, 0x84, 0xe2, 0x5c, 0xaf, 0x36, 0x36,
|
||||
0x4c, 0x00, 0x35, 0x7f, 0x8f, 0x82, 0x20, 0x81, 0xd6, 0x81, 0xd5, 0xaa,
|
||||
0x7f, 0x01, 0x41, 0x02, 0x35, 0x77, 0xe1, 0x9b, 0xad, 0xb4, 0xca, 0x48,
|
||||
0x76, 0x02, 0xfb, 0xda, 0x71, 0xba, 0x36, 0xe8, 0x99, 0x26, 0xe0, 0x20,
|
||||
0x6e, 0x48, 0xd8, 0xcb, 0x93, 0x9f, 0x6b, 0x4d, 0x8b, 0x66, 0xc7, 0x27,
|
||||
0x42, 0x02, 0x67, 0x49, 0x5f, 0x25, 0x80, 0x54, 0xdf, 0x27, 0x47, 0xcf,
|
||||
0xd8, 0x24, 0x50, 0x66, 0xf6, 0x54, 0xaf, 0xfe, 0x29, 0x50, 0x13, 0x56,
|
||||
0x2a, 0x87, 0xd3, 0x0f, 0x08, 0x5d, 0xd2, 0x56, 0xf7, 0x01, 0x64, 0xd6,
|
||||
0x4f, 0x9d, 0x97, 0xcf, 0xf5, 0x0b, 0xfa, 0x0c, 0xd7, 0xf4, 0xb5, 0xbe,
|
||||
0xc1, 0x47, 0x02, 0x95, 0x3c, 0x94, 0x0f, 0x60, 0x78, 0x8f, 0xc4, 0xfc,
|
||||
0xbd, 0x95, 0x44, 0xcf, 0x34, 0x41, 0x16, 0xf8, 0xaa, 0x81, 0xbd, 0x54,
|
||||
0xeb, 0x6b, 0xd8, 0xa1, 0x9b, 0x0e, 0x76, 0x14, 0xf0, 0xa9, 0x38, 0x86,
|
||||
0xab, 0x0c, 0x4d, 0xf8, 0x03, 0xc0, 0x9f, 0x0a, 0x6e, 0x84, 0x12, 0xdb,
|
||||
0x3b, 0xbb, 0xae, 0x9a, 0xf9, 0xaa, 0x65, 0x11, 0xab, 0xbf, 0x1f, 0xce,
|
||||
0x6a, 0x27, 0x16, 0x67, 0x51, 0x6a, 0x76, 0x5d, 0x03, 0x78, 0x27, 0x19,
|
||||
0xea, 0x9a, 0xa0, 0x2f, 0x0e, 0x22, 0x77, 0xf2, 0x4a, 0xe0, 0x24, 0x97,
|
||||
0x62, 0x10, 0x8b, 0x5d, 0xb8, 0x66, 0x34, 0x5b, 0xd2, 0xc2, 0x03, 0xfd,
|
||||
0x00, 0xe0, 0x61, 0xbb, 0x6d, 0x25, 0xcf, 0xd8, 0x11, 0x34, 0xa9, 0xf1,
|
||||
0x76, 0xd7, 0xce, 0x9a, 0x2b, 0x3a, 0xc8, 0x99, 0x08, 0x4b, 0x31, 0xb3,
|
||||
0xba, 0x26, 0xa0, 0xd4, 0x3d, 0x85, 0xcf, 0xc1, 0x58, 0x8f, 0xdf, 0x06,
|
||||
0xd1, 0xe0, 0x50, 0x22, 0x0c, 0xb7, 0x77, 0x92, 0x48, 0xc0, 0x1c, 0x8f,
|
||||
0x98, 0x0d, 0xae, 0x10, 0x31, 0x67, 0x1a, 0x39, 0x65, 0x7c, 0xa2, 0x63,
|
||||
0xb5, 0x13, 0x5e, 0x31, 0x28, 0x65, 0xbd, 0xde, 0x98, 0x06, 0xe7, 0xe8,
|
||||
0xb4, 0x2e, 0xe3, 0x76, 0x17, 0xd9, 0x72, 0x67, 0x10, 0x53, 0xc6, 0x26,
|
||||
0x12, 0x26, 0xc1, 0xa9, 0xdd, 0xb3, 0x0d, 0x08, 0xd9, 0xde, 0xd9, 0x0d,
|
||||
0x26, 0x42, 0x2e, 0x12, 0x74, 0x45, 0x84, 0x9e, 0x93, 0x80, 0xf4, 0xeb,
|
||||
0x0c, 0x1f, 0x20, 0xd7, 0xd2, 0xed, 0xa1, 0x37, 0x90, 0xa3, 0x03, 0xbb,
|
||||
0xf4, 0x0d, 0x7c, 0xa4, 0xcb, 0x65, 0x16, 0x52, 0xfc, 0x02, 0xcf, 0x4a,
|
||||
0xea, 0x03, 0xf9, 0xcf, 0x07, 0x90, 0xaa, 0x22, 0x85, 0x10, 0x31, 0x2c,
|
||||
0xee, 0x93, 0x83, 0xd8, 0x07, 0x12, 0x00, 0x4d, 0xbf, 0x40, 0x77, 0x0e,
|
||||
0xd9, 0x4b, 0xab, 0xaf, 0x89, 0x40, 0xa3, 0x0f, 0x11, 0x02, 0x60, 0x27,
|
||||
0x81, 0xab, 0x16, 0x90, 0x2d, 0x0d, 0x5c, 0xca, 0x3f, 0xe8, 0x02, 0xae,
|
||||
0x6a, 0xa2, 0x58, 0x11, 0x35, 0x87, 0x79, 0x4b, 0x9d, 0xb4, 0xa2, 0xfa,
|
||||
0x05, 0x6a, 0xb1, 0xc8, 0xe7, 0x04, 0x66, 0xad, 0x05, 0xc4, 0x90, 0x40,
|
||||
0xbc, 0x42, 0xd1, 0x75, 0x65, 0xcf, 0xa7, 0x71, 0x72, 0x10, 0x21, 0x07,
|
||||
0x09, 0xa6, 0x93, 0xab, 0x86, 0x06, 0x08, 0x4a, 0x05, 0xdb, 0x10, 0xb2,
|
||||
0x6d, 0x4b, 0x0e, 0xb8, 0x96, 0x9e, 0x55, 0x41, 0xcc, 0x56, 0x02, 0xd0,
|
||||
0x9c, 0xc0, 0x92, 0x9a, 0xf9, 0x2b, 0x35, 0x19, 0x86, 0x6b, 0x92, 0x7d,
|
||||
0x8e, 0xa0, 0x4d, 0x03, 0xb0, 0xe7, 0xff, 0x77, 0x2d, 0x7c, 0x20, 0x6c,
|
||||
0x10, 0xaa, 0x6f, 0xee, 0x62, 0x8a, 0x00, 0x4b, 0xce, 0x84, 0xe2, 0xf4,
|
||||
0xbb, 0x67, 0x87, 0x95, 0x01, 0x60, 0xaf, 0x05, 0x34, 0xc0, 0x64, 0xdb,
|
||||
0xa0, 0x83, 0x57, 0x34, 0xb6, 0x86, 0x43, 0xeb, 0x8a, 0x25, 0x99, 0x04,
|
||||
0xd9, 0xc5, 0xfd, 0x37, 0xfa, 0xc1, 0x65, 0x12, 0x88, 0x9d, 0x58, 0xc9,
|
||||
0x7e, 0x54, 0x4d, 0x80, 0xb1, 0x9a, 0x75, 0xa9, 0x7d, 0xf8, 0x2c, 0x90,
|
||||
0xed, 0x3b, 0x3c, 0x7e, 0x11, 0x6c, 0x4f, 0x89, 0x75, 0x89, 0xc5, 0xff,
|
||||
0x0b, 0x20, 0x6a, 0xb5, 0x33, 0x3c, 0x3c, 0x78, 0xaa, 0x92, 0xa7, 0xb3,
|
||||
0xdd, 0x3e, 0x55, 0x30, 0xd6, 0x38, 0x60, 0xa5, 0x82, 0x17, 0xc0, 0x52,
|
||||
0xf0, 0xaa, 0x87, 0x58, 0x6a, 0x3b, 0x56, 0x02, 0x0d, 0xe1, 0x03, 0x08,
|
||||
0x72, 0x0e, 0x2d, 0xc2, 0x37, 0xfe, 0x8e, 0xd1, 0x04, 0x1a, 0x69, 0x8a,
|
||||
0x94, 0x90, 0x53, 0x9c, 0x43, 0x6f, 0x2a, 0x78, 0x67, 0xef, 0x6e, 0x69,
|
||||
0x22, 0x18, 0xc2, 0x57, 0xe1, 0x23, 0x82, 0x47, 0xf8, 0xc6, 0xf1, 0xbe,
|
||||
0x92, 0xc0, 0x01, 0xa7, 0x3f, 0xc7, 0xae, 0x05, 0x14, 0x22, 0x81, 0x53,
|
||||
0xf8, 0x2a, 0x28, 0x12, 0x30, 0x85, 0x6f, 0x7c, 0xde, 0x47, 0x12, 0xe8,
|
||||
0x5a, 0xe0, 0xf8, 0xf4, 0x8c, 0xb5, 0x17, 0x33, 0x6b, 0x3c, 0x80, 0xc0,
|
||||
0xe9, 0xe8, 0x0d, 0xe9, 0x17, 0x28, 0x0e, 0x47, 0x4e, 0x1b, 0x48, 0xd6,
|
||||
0x27, 0x5c, 0xea, 0x4e, 0xf5, 0x0b, 0x02, 0x85, 0x4f, 0xfe, 0x6e, 0x4c,
|
||||
0x88, 0xd8, 0xc1, 0xd2, 0x78, 0xce, 0x87, 0xc2, 0xe6, 0x0f, 0x10, 0x4e,
|
||||
0x21, 0x80, 0x8f, 0x1a, 0x80, 0xd5, 0xd0, 0xa3, 0x27, 0x8f, 0xbb, 0xd2,
|
||||
0x04, 0xd6, 0xe2, 0x94, 0x2b, 0xab, 0x28, 0xcc, 0x41, 0x82, 0xf0, 0x1b,
|
||||
0xe7, 0xd7, 0xac, 0x09, 0x6c, 0x1a, 0xd1, 0xf6, 0x80, 0x10, 0xda, 0x00,
|
||||
0x80, 0x39, 0x31, 0x84, 0x15, 0x7e, 0xb5, 0xec, 0x17, 0xb0, 0x2a, 0x93,
|
||||
0x36, 0x12, 0x64, 0xda, 0x7b, 0xa8, 0x17, 0x24, 0xb0, 0x81, 0x49, 0x02,
|
||||
0x00, 0x61, 0x2b, 0x84, 0x18, 0x68, 0x81, 0x08, 0x41, 0x65, 0x69, 0x5d,
|
||||
0x1b, 0x64, 0xde, 0x78, 0x6a, 0x25, 0x49, 0xa0, 0x23, 0xd8, 0x36, 0xda,
|
||||
0x66, 0x9d, 0x64, 0xd8, 0x6d, 0x2b, 0x69, 0x4c, 0x82, 0xcd, 0xc6, 0x65,
|
||||
0x40, 0xdb, 0x3e, 0x41, 0x48, 0xfe, 0x60, 0x01, 0xb8, 0x1d, 0xe2, 0x80,
|
||||
0x49, 0x3c, 0x64, 0x26, 0xd0, 0xf6, 0xa3, 0x6d, 0x33, 0xb8, 0x56, 0xe1,
|
||||
0x37, 0xae, 0xd7, 0x82, 0x26, 0x58, 0x58, 0x8e, 0x47, 0x21, 0x54, 0xf8,
|
||||
0x80, 0x7f, 0xad, 0xe0, 0x46, 0xc3, 0x5c, 0x73, 0xce, 0x94, 0xcf, 0xa2,
|
||||
0x17, 0x36, 0xac, 0x50, 0xf8, 0xc6, 0x75, 0x33, 0x92, 0x20, 0xe7, 0x02,
|
||||
0x90, 0x14, 0x58, 0x3e, 0x9d, 0xab, 0x18, 0x04, 0xe0, 0xe3, 0xae, 0x14,
|
||||
0x9c, 0xcd, 0x0a, 0x22, 0xd0, 0x07, 0xe1, 0x1b, 0xd7, 0x2f, 0xe9, 0x13,
|
||||
0x30, 0x2b, 0xa1, 0xec, 0xbe, 0xa0, 0x08, 0xd0, 0x10, 0xbe, 0x8a, 0xbd,
|
||||
0x7b, 0xfb, 0xec, 0xf5, 0xe7, 0x18, 0xe8, 0x93, 0xf0, 0x8d, 0xdf, 0x89,
|
||||
0x21, 0xc1, 0xd3, 0x1f, 0x7f, 0x72, 0xfe, 0xcd, 0x45, 0xce, 0x01, 0xa8,
|
||||
0xd6, 0x21, 0x61, 0xbe, 0x27, 0xde, 0xb6, 0x79, 0x01, 0xd3, 0x19, 0xec,
|
||||
0xa3, 0xf0, 0x55, 0x44, 0x3b, 0x86, 0x7b, 0xbb, 0xcd, 0xf0, 0xf5, 0xd5,
|
||||
0x6f, 0xbf, 0x88, 0xb7, 0xde, 0xfe, 0x52, 0x9f, 0x7e, 0x2a, 0xf3, 0x19,
|
||||
0xb3, 0x0b, 0x6b, 0xd0, 0x62, 0xd1, 0xfa, 0x82, 0x83, 0xa3, 0xf3, 0x8b,
|
||||
0x98, 0x7d, 0xee, 0xfa, 0x2e, 0x7c, 0xf1, 0xbb, 0x51, 0x55, 0xc4, 0xc9,
|
||||
0xf5, 0x32, 0x3c, 0xd3, 0x89, 0x80, 0xc0, 0x6a, 0x22, 0x35, 0x21, 0x45,
|
||||
0x44, 0x07, 0x0a, 0x11, 0xbc, 0xd7, 0x64, 0x0f, 0x0a, 0xb5, 0x81, 0x22,
|
||||
0x81, 0x43, 0x0b, 0xac, 0x82, 0xf0, 0x55, 0x24, 0x85, 0x88, 0x82, 0x04,
|
||||
0x1e, 0x2d, 0x60, 0x3c, 0xfd, 0x3a, 0x18, 0xda, 0xc0, 0xda, 0x57, 0xb6,
|
||||
0x41, 0xa1, 0x2e, 0x18, 0x9f, 0x3b, 0xfc, 0x02, 0x72, 0x7b, 0x99, 0xd3,
|
||||
0xd1, 0x9b, 0xe0, 0x24, 0x52, 0x85, 0xc2, 0x07, 0xf2, 0x0f, 0x2a, 0xd1,
|
||||
0x21, 0xfb, 0x2c, 0x74, 0x52, 0x8a, 0x96, 0x2b, 0x08, 0xae, 0x06, 0xfa,
|
||||
0x42, 0x08, 0x27, 0x09, 0x88, 0x70, 0xd1, 0x18, 0x6c, 0x22, 0xc0, 0x21,
|
||||
0x42, 0xa5, 0xc2, 0x17, 0x68, 0x9b, 0x04, 0x52, 0xbb, 0x24, 0x92, 0x80,
|
||||
0x44, 0x4a, 0x67, 0x5a, 0x9d, 0x45, 0x61, 0x12, 0xf4, 0xe9, 0xc9, 0x9c,
|
||||
0x05, 0x8c, 0xf4, 0x6a, 0x63, 0xe5, 0xc2, 0x57, 0x21, 0x49, 0xfe, 0xf4,
|
||||
0xc7, 0x9f, 0xa4, 0xad, 0xf7, 0xc1, 0x61, 0x06, 0xac, 0xe6, 0x45, 0x8c,
|
||||
0x71, 0xa4, 0xc6, 0x46, 0x1e, 0x1c, 0x3e, 0x6f, 0x98, 0x04, 0xdf, 0x22,
|
||||
0x5e, 0xb1, 0x3b, 0x86, 0x38, 0xfd, 0x04, 0xca, 0x24, 0x70, 0x57, 0xaf,
|
||||
0x12, 0xda, 0x60, 0x3a, 0xb9, 0xea, 0x93, 0xf0, 0x01, 0xc5, 0x31, 0x7c,
|
||||
0xf5, 0xdb, 0x2f, 0x41, 0x24, 0x20, 0xe0, 0xf4, 0x2d, 0xa8, 0x7d, 0x9b,
|
||||
0x04, 0x42, 0x57, 0x6d, 0x8d, 0x21, 0x80, 0x35, 0x4f, 0xa0, 0x23, 0x32,
|
||||
0x4a, 0x00, 0xd0, 0xab, 0x27, 0x5f, 0x85, 0x24, 0x41, 0x02, 0x62, 0x1c,
|
||||
0x4b, 0xdf, 0xb8, 0x8e, 0x20, 0x27, 0xd0, 0x05, 0xb6, 0xf0, 0x05, 0x62,
|
||||
0x92, 0x47, 0xdc, 0xc5, 0x0d, 0x2a, 0x84, 0xec, 0x9f, 0xf7, 0x7f, 0xff,
|
||||
0x8d, 0xbd, 0xdd, 0x5d, 0x2a, 0xdc, 0x6b, 0x40, 0x68, 0x09, 0x3d, 0x29,
|
||||
0x14, 0x21, 0x7c, 0xf1, 0xde, 0x5b, 0xce, 0xb7, 0x7d, 0xd9, 0x87, 0x60,
|
||||
0xe1, 0x53, 0xf0, 0x69, 0x84, 0x55, 0x10, 0xbe, 0x1a, 0xb2, 0xdd, 0xf9,
|
||||
0xec, 0xb3, 0xc6, 0x49, 0x09, 0x66, 0xa1, 0x81, 0x5c, 0xdb, 0xf2, 0x05,
|
||||
0x95, 0x21, 0x01, 0xc8, 0xdd, 0xa9, 0x6f, 0xde, 0xdd, 0x46, 0xff, 0x28,
|
||||
0x23, 0x8b, 0x08, 0xf4, 0x4b, 0xf8, 0x80, 0x43, 0xf5, 0xdb, 0x52, 0xbe,
|
||||
0xa9, 0x64, 0xc8, 0x31, 0xfc, 0x2c, 0x88, 0x00, 0xea, 0xfe, 0xf4, 0x6f,
|
||||
0x6f, 0xdf, 0x03, 0x58, 0x12, 0x81, 0xca, 0x10, 0xfa, 0xe0, 0x48, 0x1e,
|
||||
0x85, 0xb4, 0xab, 0x46, 0x38, 0x7d, 0x00, 0x9d, 0x0c, 0x29, 0x24, 0xe8,
|
||||
0x9c, 0x00, 0x00, 0xf0, 0xaf, 0xff, 0xfc, 0x0f, 0xef, 0xde, 0x2f, 0x85,
|
||||
0xff, 0xfd, 0x37, 0x5f, 0x59, 0xbf, 0x10, 0x52, 0x4b, 0x58, 0x21, 0xe1,
|
||||
0x53, 0x70, 0x6a, 0x86, 0x1c, 0x26, 0x21, 0xc5, 0x1c, 0x44, 0xf9, 0x00,
|
||||
0x9e, 0x6b, 0xb1, 0x8a, 0x49, 0xc0, 0x92, 0x04, 0x2b, 0x2e, 0x7c, 0x0a,
|
||||
0x8d, 0x7e, 0xcc, 0xb1, 0x2c, 0x5d, 0x8a, 0x26, 0x08, 0x09, 0x03, 0xa9,
|
||||
0x10, 0x27, 0x49, 0x60, 0x9f, 0xa0, 0xf0, 0x81, 0xc0, 0xa1, 0xf8, 0x80,
|
||||
0x7f, 0x36, 0xb3, 0x36, 0x45, 0x2d, 0x08, 0xa1, 0x79, 0x80, 0x4f, 0x45,
|
||||
0x48, 0x6d, 0x63, 0x01, 0xf0, 0x9e, 0x7e, 0xd7, 0x6c, 0xe6, 0x1c, 0xda,
|
||||
0x23, 0xb7, 0x40, 0x25, 0xab, 0x85, 0xc3, 0x38, 0xbe, 0x9e, 0x39, 0xbf,
|
||||
0x90, 0xba, 0xfd, 0x79, 0x0f, 0xc1, 0x4e, 0xf4, 0x10, 0xaa, 0x9d, 0xa3,
|
||||
0x35, 0x82, 0xfa, 0x30, 0xe7, 0xbe, 0x81, 0x86, 0xf0, 0x01, 0x60, 0x7f,
|
||||
0x77, 0x1b, 0x5b, 0x9b, 0x1b, 0xd6, 0x2f, 0x69, 0xc3, 0xcd, 0xba, 0x18,
|
||||
0x80, 0x5a, 0x0a, 0x8d, 0x7b, 0x3b, 0x38, 0x7c, 0x1e, 0xb2, 0x8a, 0x89,
|
||||
0x5a, 0x84, 0x73, 0x25, 0x77, 0x82, 0x1f, 0xa0, 0xe8, 0xdd, 0xc3, 0x35,
|
||||
0x90, 0xc2, 0x57, 0xb1, 0xb5, 0xb9, 0xe1, 0xcc, 0x1d, 0x8c, 0xce, 0x2f,
|
||||
0x54, 0x6d, 0x50, 0x64, 0xaa, 0x75, 0x8b, 0x20, 0x93, 0x44, 0x81, 0xdf,
|
||||
0xb7, 0xce, 0x6a, 0x4e, 0x41, 0xae, 0x4e, 0x5e, 0x00, 0xcb, 0x10, 0x11,
|
||||
0x80, 0x0c, 0x13, 0x05, 0xd4, 0x70, 0xd1, 0x17, 0x1d, 0x10, 0xdb, 0x9c,
|
||||
0xf6, 0x99, 0x08, 0x0d, 0x6d, 0xe6, 0x12, 0xbe, 0xad, 0xd4, 0x9b, 0x2b,
|
||||
0xe3, 0x67, 0x43, 0x2b, 0x04, 0x00, 0xe0, 0xcc, 0x15, 0x70, 0x42, 0xc4,
|
||||
0x15, 0xf0, 0x0d, 0xd8, 0xc2, 0x17, 0x28, 0x41, 0x82, 0xd6, 0x08, 0xf0,
|
||||
0xdd, 0xd7, 0x5f, 0x52, 0xbf, 0x15, 0x54, 0x4f, 0xe8, 0xa9, 0x36, 0x68,
|
||||
0x08, 0x5e, 0x0c, 0xca, 0x08, 0x19, 0xc9, 0xab, 0x13, 0xa1, 0xcd, 0x19,
|
||||
0xc7, 0x59, 0x09, 0xc0, 0xfc, 0x8d, 0x05, 0xb0, 0xac, 0x29, 0x70, 0xeb,
|
||||
0x09, 0x96, 0xdd, 0xaf, 0x6b, 0x22, 0x83, 0x71, 0xff, 0xd4, 0x68, 0x1c,
|
||||
0x8a, 0x04, 0x7a, 0xfd, 0x5e, 0x68, 0x0a, 0x07, 0x09, 0xaa, 0x24, 0x00,
|
||||
0x40, 0x93, 0xc0, 0x3a, 0x28, 0x34, 0xa6, 0xa8, 0x54, 0x21, 0x11, 0x9c,
|
||||
0x0b, 0x6a, 0xd9, 0x26, 0x68, 0x0a, 0x22, 0xd8, 0x06, 0x6f, 0x50, 0x24,
|
||||
0x68, 0x8b, 0x00, 0xb9, 0xa2, 0x00, 0x20, 0xa0, 0x61, 0x42, 0xf8, 0x80,
|
||||
0x3f, 0x3a, 0x50, 0x21, 0xcc, 0x86, 0x96, 0x42, 0xce, 0x9a, 0x9d, 0xf4,
|
||||
0xc0, 0xaa, 0xe9, 0x42, 0x46, 0x39, 0x13, 0xfb, 0x25, 0xa8, 0x6d, 0x5e,
|
||||
0x08, 0x62, 0x1c, 0x1c, 0x3e, 0x6f, 0x7d, 0xc9, 0xdb, 0xae, 0x9f, 0x1e,
|
||||
0xa9, 0xfe, 0x75, 0x84, 0x96, 0x97, 0x6d, 0xe3, 0x0e, 0x2d, 0x88, 0xb9,
|
||||
0x4f, 0x6f, 0x3e, 0x82, 0x2b, 0x74, 0x9b, 0x26, 0x70, 0x8c, 0xd7, 0x0b,
|
||||
0x31, 0xa9, 0x49, 0xc8, 0xa9, 0x01, 0x7c, 0x90, 0x37, 0xf5, 0xc5, 0xc6,
|
||||
0x1d, 0x59, 0x4e, 0x06, 0x80, 0x47, 0x0f, 0xee, 0x1b, 0x27, 0xfb, 0x9c,
|
||||
0xc4, 0xbd, 0x7b, 0xfb, 0x98, 0x5c, 0x8e, 0x8d, 0xb1, 0x86, 0x16, 0x42,
|
||||
0x64, 0x4b, 0x2e, 0xc5, 0xcc, 0x67, 0xf0, 0x6c, 0xb6, 0x05, 0x98, 0x71,
|
||||
0xbe, 0x2d, 0xeb, 0x57, 0x6d, 0x14, 0xc0, 0x81, 0x91, 0x2b, 0x78, 0x7b,
|
||||
0xfb, 0x9e, 0x14, 0xbe, 0x40, 0xcc, 0xb8, 0x02, 0x0a, 0x4c, 0x2d, 0x61,
|
||||
0x20, 0xd7, 0xde, 0x09, 0x2a, 0x74, 0x22, 0x94, 0xde, 0x7a, 0xb7, 0x4b,
|
||||
0x0d, 0xd0, 0xc0, 0xe6, 0x9d, 0x3b, 0xfa, 0xa1, 0xe0, 0x2a, 0x19, 0xf0,
|
||||
0x71, 0x04, 0xb2, 0x8b, 0x08, 0xdc, 0x11, 0xc9, 0xb9, 0x17, 0xc6, 0xd4,
|
||||
0x97, 0x6a, 0x3d, 0x3e, 0x3d, 0x8b, 0xde, 0x67, 0xa9, 0x2d, 0xe4, 0xac,
|
||||
0x05, 0x04, 0xe1, 0xbb, 0xaf, 0xbf, 0xb4, 0x0d, 0x28, 0x91, 0x4f, 0x82,
|
||||
0xab, 0x86, 0xa0, 0x23, 0xc7, 0xac, 0xe5, 0x9c, 0xcb, 0xdd, 0x50, 0xeb,
|
||||
0xf4, 0xaa, 0xc7, 0xc4, 0xf2, 0x36, 0x4a, 0xb8, 0x58, 0xa4, 0x06, 0xd2,
|
||||
0xb9, 0x06, 0xb0, 0x24, 0x88, 0xac, 0x10, 0x24, 0xe0, 0x38, 0x89, 0xc2,
|
||||
0x2f, 0x48, 0xc1, 0xce, 0xde, 0xdd, 0xdc, 0x9a, 0xc0, 0xa9, 0xd9, 0x4a,
|
||||
0xef, 0xc8, 0xda, 0x25, 0x01, 0xb8, 0x03, 0x4a, 0xc8, 0x8e, 0xda, 0xda,
|
||||
0xdc, 0x88, 0x72, 0x16, 0x63, 0xa0, 0x4e, 0x4e, 0xc9, 0x88, 0x01, 0x80,
|
||||
0x85, 0xd0, 0x02, 0x6d, 0xf8, 0x17, 0x31, 0xa8, 0x29, 0x9b, 0x26, 0x40,
|
||||
0x86, 0x8a, 0x21, 0xce, 0x62, 0xaa, 0x16, 0x50, 0xe1, 0x99, 0xaa, 0x06,
|
||||
0x80, 0x16, 0xa6, 0x63, 0xf1, 0xcc, 0xc6, 0x0c, 0xe9, 0xd2, 0x13, 0x60,
|
||||
0x8a, 0xf9, 0x00, 0x09, 0xf0, 0x4e, 0x76, 0xc8, 0xb9, 0x92, 0x89, 0xea,
|
||||
0x17, 0x64, 0xda, 0x1b, 0x41, 0xb6, 0xbd, 0xb4, 0xf0, 0x81, 0x82, 0x51,
|
||||
0x40, 0x08, 0x2c, 0x4f, 0xbf, 0x73, 0x84, 0x0c, 0x95, 0x36, 0x66, 0x2c,
|
||||
0x71, 0x47, 0x9e, 0x6b, 0x71, 0x0e, 0xa5, 0x6d, 0x3f, 0x39, 0x7a, 0x16,
|
||||
0xaa, 0xd2, 0xf5, 0xb6, 0x17, 0xd3, 0xc4, 0xd5, 0x6b, 0x00, 0x4d, 0xf8,
|
||||
0x64, 0x47, 0xe9, 0xd1, 0x82, 0xa5, 0x66, 0x40, 0x0a, 0xda, 0x96, 0x23,
|
||||
0xf0, 0xe4, 0x0e, 0x72, 0x08, 0x8c, 0x35, 0x75, 0xab, 0x6d, 0x54, 0x4b,
|
||||
0x80, 0x47, 0x0f, 0xee, 0xb3, 0x84, 0x2f, 0x60, 0x09, 0x19, 0x43, 0x3b,
|
||||
0x38, 0x46, 0x18, 0x03, 0xa0, 0xcc, 0xd6, 0x39, 0x39, 0x50, 0x23, 0x01,
|
||||
0x28, 0x21, 0xb0, 0x04, 0xb3, 0xb5, 0xb9, 0x91, 0x52, 0x31, 0xb4, 0x2e,
|
||||
0x7e, 0xc1, 0x45, 0x1f, 0x49, 0x50, 0xab, 0x0f, 0x90, 0x4b, 0xc5, 0x76,
|
||||
0x05, 0x69, 0xd3, 0xfb, 0x46, 0x82, 0x1a, 0x35, 0x40, 0x34, 0x5c, 0xa1,
|
||||
0x62, 0x20, 0x06, 0x40, 0x70, 0x0d, 0x21, 0x5a, 0x73, 0x95, 0x44, 0xad,
|
||||
0x1a, 0x20, 0x08, 0x84, 0xe0, 0x59, 0x1d, 0x9f, 0x61, 0x81, 0xeb, 0xa8,
|
||||
0xdf, 0xad, 0x09, 0xbd, 0x6b, 0x30, 0x81, 0x90, 0x01, 0x21, 0x8d, 0x15,
|
||||
0xcf, 0x19, 0x04, 0x08, 0x5e, 0x71, 0xa3, 0x6f, 0x58, 0x99, 0x1b, 0x61,
|
||||
0x22, 0xa6, 0xc6, 0xde, 0xe5, 0x88, 0xa3, 0xce, 0xb1, 0x52, 0x37, 0xc3,
|
||||
0x44, 0x15, 0x09, 0x98, 0x5a, 0xf0, 0x7f, 0x84, 0x1d, 0xed, 0xfe, 0xb1,
|
||||
0x2a, 0x47, 0x9d, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae,
|
||||
0x42, 0x60, 0x82
|
||||
};
|
||||
unsigned int icon_png_len = 3699;
|
||||
117
source/midi.c
117
source/midi.c
@@ -1,117 +0,0 @@
|
||||
#include "midi.h"
|
||||
#include <stdio.h>
|
||||
#include <poll.h>
|
||||
#include <stdatomic.h>
|
||||
#include <alloca.h>
|
||||
|
||||
extern atomic_int gRunning;
|
||||
|
||||
int midiInit(MidiState *m, Synth *synth)
|
||||
{
|
||||
if (snd_seq_open(&m->seq, "default", SND_SEQ_OPEN_INPUT | SND_SEQ_NONBLOCK, 0) < 0) {
|
||||
fprintf(stderr, "Failed to open ALSA sequencer\n");
|
||||
return 0;
|
||||
}
|
||||
snd_seq_nonblock(m->seq, 1);
|
||||
snd_seq_set_client_name(m->seq, "soundThing");
|
||||
m->client = snd_seq_client_id(m->seq);
|
||||
m->port = snd_seq_create_simple_port(m->seq, "MIDI In",
|
||||
SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE,
|
||||
SND_SEQ_PORT_TYPE_APPLICATION);
|
||||
m->synth = synth;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void midiListInputs(MidiState *m)
|
||||
{
|
||||
snd_seq_client_info_t *cinfo;
|
||||
snd_seq_port_info_t *pinfo;
|
||||
snd_seq_client_info_alloca(&cinfo);
|
||||
snd_seq_port_info_alloca(&pinfo);
|
||||
|
||||
printf("\nAvailable MIDI inputs:\n");
|
||||
snd_seq_client_info_set_client(cinfo, -1);
|
||||
while (snd_seq_query_next_client(m->seq, cinfo) >= 0) {
|
||||
int client = snd_seq_client_info_get_client(cinfo);
|
||||
if (client == m->client) continue;
|
||||
|
||||
snd_seq_port_info_set_client(pinfo, client);
|
||||
snd_seq_port_info_set_port(pinfo, -1);
|
||||
while (snd_seq_query_next_port(m->seq, pinfo) >= 0) {
|
||||
unsigned int caps = snd_seq_port_info_get_capability(pinfo);
|
||||
if (caps & SND_SEQ_PORT_CAP_READ && caps & SND_SEQ_PORT_CAP_SUBS_READ) {
|
||||
printf(" [%d:%d] %s — %s\n",
|
||||
client,
|
||||
snd_seq_port_info_get_port(pinfo),
|
||||
snd_seq_client_info_get_name(cinfo),
|
||||
snd_seq_port_info_get_name(pinfo));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void midiConnect(MidiState *m, int srcClient, int srcPort)
|
||||
{
|
||||
snd_seq_port_subscribe_t *sub;
|
||||
snd_seq_addr_t sender, dest;
|
||||
|
||||
sender.client = srcClient;
|
||||
sender.port = srcPort;
|
||||
dest.client = m->client;
|
||||
dest.port = m->port;
|
||||
|
||||
snd_seq_port_subscribe_alloca(&sub);
|
||||
snd_seq_port_subscribe_set_sender(sub, &sender);
|
||||
snd_seq_port_subscribe_set_dest(sub, &dest);
|
||||
snd_seq_subscribe_port(m->seq, sub);
|
||||
|
||||
printf("Connected [%d:%d] to Cricket\n", srcClient, srcPort);
|
||||
}
|
||||
|
||||
void midiClose(MidiState *m)
|
||||
{
|
||||
snd_seq_close(m->seq);
|
||||
}
|
||||
|
||||
void *midiThread(void *arg)
|
||||
{
|
||||
MidiState *m = (MidiState *)arg;
|
||||
struct pollfd pfd;
|
||||
snd_seq_poll_descriptors(m->seq, &pfd, 1, POLLIN);
|
||||
|
||||
while (gRunning) {
|
||||
int ready = poll(&pfd, 1, 50);
|
||||
if (ready <= 0) continue;
|
||||
|
||||
snd_seq_event_t *ev = NULL;
|
||||
while (snd_seq_event_input(m->seq, &ev) >= 0 && ev) {
|
||||
switch (ev->type) {
|
||||
case SND_SEQ_EVENT_NOTEON:
|
||||
if (ev->data.note.velocity == 0)
|
||||
synthNoteOff(m->synth, ev->data.note.note);
|
||||
else
|
||||
synthNoteOn(m->synth, ev->data.note.note);
|
||||
break;
|
||||
case SND_SEQ_EVENT_NOTEOFF:
|
||||
synthNoteOff(m->synth, ev->data.note.note);
|
||||
break;
|
||||
case SND_SEQ_EVENT_CONTROLLER:
|
||||
// This are controller parameter changes.
|
||||
// Map the Mod Wheel to PWM:
|
||||
if (ev->data.control.param == 1)
|
||||
m->synth->dutyCycle = 0.05f + (ev->data.control.value / 127.0f) * 0.90f;
|
||||
// This naively maps parameter 70 to the waveform in a way that does not play so nice with real pots.
|
||||
if (ev->data.control.param == 70)
|
||||
m->synth->waveform = ev->data.control.value % WAVE_COUNT;
|
||||
break;
|
||||
case SND_SEQ_EVENT_PITCHBEND:
|
||||
m->synth->pitchBend = ev->data.control.value / 8191.0f;
|
||||
break;
|
||||
|
||||
}
|
||||
snd_seq_free_event(ev);
|
||||
ev = NULL;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
157
source/midi_linux.c
Normal file
157
source/midi_linux.c
Normal file
@@ -0,0 +1,157 @@
|
||||
#include "midi.h"
|
||||
#include <stdio.h>
|
||||
#include <poll.h>
|
||||
#include <stdatomic.h>
|
||||
#include <alloca.h>
|
||||
|
||||
extern atomic_int gRunning;
|
||||
|
||||
int midiInit(MidiState *m, Synth *synth)
|
||||
{
|
||||
if (snd_seq_open(&m->seq, "default", SND_SEQ_OPEN_INPUT | SND_SEQ_NONBLOCK, 0) < 0) {
|
||||
fprintf(stderr, "Failed to open ALSA sequencer\n");
|
||||
return 0;
|
||||
}
|
||||
snd_seq_nonblock(m->seq, 1);
|
||||
snd_seq_set_client_name(m->seq, "soundThing");
|
||||
m->client = snd_seq_client_id(m->seq);
|
||||
m->port = snd_seq_create_simple_port(m->seq, "MIDI In",
|
||||
SND_SEQ_PORT_CAP_WRITE | SND_SEQ_PORT_CAP_SUBS_WRITE,
|
||||
SND_SEQ_PORT_TYPE_APPLICATION);
|
||||
m->synth = synth;
|
||||
m->connectedClient = -1;
|
||||
m->connectedPort = -1;
|
||||
m->inputCount = 0;
|
||||
atomic_store(&m->midiLearnMode, 0);
|
||||
atomic_store(&m->midiLearnCC, -1);
|
||||
for (int i = 0; i < 128; i++) m->ccMappings[i].active = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void midiScanInputs(MidiState *m)
|
||||
{
|
||||
m->inputCount = 0;
|
||||
snd_seq_client_info_t *cinfo;
|
||||
snd_seq_port_info_t *pinfo;
|
||||
snd_seq_client_info_alloca(&cinfo);
|
||||
snd_seq_port_info_alloca(&pinfo);
|
||||
|
||||
snd_seq_client_info_set_client(cinfo, -1);
|
||||
while (snd_seq_query_next_client(m->seq, cinfo) >= 0) {
|
||||
int client = snd_seq_client_info_get_client(cinfo);
|
||||
if (client == m->client) continue;
|
||||
|
||||
snd_seq_port_info_set_client(pinfo, client);
|
||||
snd_seq_port_info_set_port(pinfo, -1);
|
||||
while (snd_seq_query_next_port(m->seq, pinfo) >= 0) {
|
||||
if (m->inputCount >= MIDI_MAX_INPUTS) break;
|
||||
unsigned int caps = snd_seq_port_info_get_capability(pinfo);
|
||||
if ((caps & SND_SEQ_PORT_CAP_READ) && (caps & SND_SEQ_PORT_CAP_SUBS_READ)) {
|
||||
MidiInputInfo *info = &m->inputs[m->inputCount++];
|
||||
info->client = client;
|
||||
info->port = snd_seq_port_info_get_port(pinfo);
|
||||
snprintf(info->clientName, sizeof(info->clientName), "%s",
|
||||
snd_seq_client_info_get_name(cinfo));
|
||||
snprintf(info->portName, sizeof(info->portName), "%s",
|
||||
snd_seq_port_info_get_name(pinfo));
|
||||
}
|
||||
}
|
||||
if (m->inputCount >= MIDI_MAX_INPUTS) break;
|
||||
}
|
||||
}
|
||||
|
||||
void midiDevDisconnect(MidiState *m)
|
||||
{
|
||||
if (m->connectedClient < 0) return;
|
||||
|
||||
snd_seq_port_subscribe_t *sub;
|
||||
snd_seq_addr_t sender, dest;
|
||||
|
||||
sender.client = m->connectedClient;
|
||||
sender.port = m->connectedPort;
|
||||
dest.client = m->client;
|
||||
dest.port = m->port;
|
||||
|
||||
snd_seq_port_subscribe_alloca(&sub);
|
||||
snd_seq_port_subscribe_set_sender(sub, &sender);
|
||||
snd_seq_port_subscribe_set_dest(sub, &dest);
|
||||
snd_seq_unsubscribe_port(m->seq, sub);
|
||||
|
||||
m->connectedClient = -1;
|
||||
m->connectedPort = -1;
|
||||
}
|
||||
|
||||
void midiDevConnect(MidiState *m, int srcClient, int srcPort)
|
||||
{
|
||||
midiDevDisconnect(m);
|
||||
|
||||
snd_seq_port_subscribe_t *sub;
|
||||
snd_seq_addr_t sender, dest;
|
||||
|
||||
sender.client = srcClient;
|
||||
sender.port = srcPort;
|
||||
dest.client = m->client;
|
||||
dest.port = m->port;
|
||||
|
||||
snd_seq_port_subscribe_alloca(&sub);
|
||||
snd_seq_port_subscribe_set_sender(sub, &sender);
|
||||
snd_seq_port_subscribe_set_dest(sub, &dest);
|
||||
snd_seq_subscribe_port(m->seq, sub);
|
||||
|
||||
m->connectedClient = srcClient;
|
||||
m->connectedPort = srcPort;
|
||||
}
|
||||
|
||||
void midiClose(MidiState *m)
|
||||
{
|
||||
midiDevDisconnect(m);
|
||||
snd_seq_close(m->seq);
|
||||
}
|
||||
|
||||
void *midiThread(void *arg)
|
||||
{
|
||||
MidiState *m = (MidiState *)arg;
|
||||
struct pollfd pfd;
|
||||
snd_seq_poll_descriptors(m->seq, &pfd, 1, POLLIN);
|
||||
|
||||
while (gRunning) {
|
||||
int ready = poll(&pfd, 1, 50);
|
||||
if (ready <= 0) continue;
|
||||
|
||||
snd_seq_event_t *ev = NULL;
|
||||
while (snd_seq_event_input(m->seq, &ev) >= 0 && ev) {
|
||||
switch (ev->type) {
|
||||
case SND_SEQ_EVENT_NOTEON:
|
||||
if (ev->data.note.velocity == 0)
|
||||
synthNoteOff(m->synth, ev->data.note.note);
|
||||
else
|
||||
synthNoteOn(m->synth, ev->data.note.note);
|
||||
break;
|
||||
case SND_SEQ_EVENT_NOTEOFF:
|
||||
synthNoteOff(m->synth, ev->data.note.note);
|
||||
break;
|
||||
case SND_SEQ_EVENT_CONTROLLER: {
|
||||
int cc = ev->data.control.param;
|
||||
int val = ev->data.control.value;
|
||||
if (cc < 128) {
|
||||
if (atomic_load(&m->midiLearnMode))
|
||||
atomic_store(&m->midiLearnCC, cc);
|
||||
if (m->ccMappings[cc].active) {
|
||||
float t = (float)val / 127.0f;
|
||||
*m->ccMappings[cc].valuePtr =
|
||||
m->ccMappings[cc].min +
|
||||
t * (m->ccMappings[cc].max - m->ccMappings[cc].min);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
case SND_SEQ_EVENT_PITCHBEND:
|
||||
m->synth->pitchBend = ev->data.control.value / 8191.0f;
|
||||
break;
|
||||
}
|
||||
snd_seq_free_event(ev);
|
||||
ev = NULL;
|
||||
}
|
||||
}
|
||||
return NULL;
|
||||
}
|
||||
108
source/midi_windows.c
Normal file
108
source/midi_windows.c
Normal file
@@ -0,0 +1,108 @@
|
||||
#include "midi.h"
|
||||
#include <windows.h>
|
||||
#include <mmsystem.h>
|
||||
#include <stdatomic.h>
|
||||
#include <stdio.h>
|
||||
|
||||
extern atomic_int gRunning;
|
||||
|
||||
static void CALLBACK midiCallbackWin32(HMIDIIN hIn, UINT msg, DWORD_PTR instance,
|
||||
DWORD_PTR param1, DWORD_PTR param2)
|
||||
{
|
||||
(void)hIn; (void)param2;
|
||||
if (msg != MIM_DATA) return;
|
||||
|
||||
MidiState *m = (MidiState *)instance;
|
||||
BYTE status = (BYTE)( param1 & 0xFF);
|
||||
BYTE data1 = (BYTE)((param1 >> 8) & 0xFF);
|
||||
BYTE data2 = (BYTE)((param1 >> 16) & 0xFF);
|
||||
BYTE type = status & 0xF0;
|
||||
|
||||
if (type == 0x90 && data2 > 0) {
|
||||
synthNoteOn(m->synth, data1);
|
||||
} else if (type == 0x80 || (type == 0x90 && data2 == 0)) {
|
||||
synthNoteOff(m->synth, data1);
|
||||
} else if (type == 0xB0) {
|
||||
int cc = data1, val = data2;
|
||||
if (cc < 128) {
|
||||
if (atomic_load(&m->midiLearnMode))
|
||||
atomic_store(&m->midiLearnCC, cc);
|
||||
if (m->ccMappings[cc].active) {
|
||||
float t = (float)val / 127.0f;
|
||||
*m->ccMappings[cc].valuePtr =
|
||||
m->ccMappings[cc].min + t * (m->ccMappings[cc].max - m->ccMappings[cc].min);
|
||||
}
|
||||
}
|
||||
} else if (type == 0xE0) {
|
||||
int pb = (data2 << 7) | data1; // 0..16383, centre = 8192
|
||||
m->synth->pitchBend = (pb - 8192) / 8191.0f;
|
||||
}
|
||||
}
|
||||
|
||||
int midiInit(MidiState *m, Synth *synth)
|
||||
{
|
||||
m->synth = synth;
|
||||
m->hMidiIn = NULL;
|
||||
m->connectedClient = -1;
|
||||
m->connectedPort = -1;
|
||||
m->inputCount = 0;
|
||||
atomic_store(&m->midiLearnMode, 0);
|
||||
atomic_store(&m->midiLearnCC, -1);
|
||||
for (int i = 0; i < 128; i++) m->ccMappings[i].active = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
void midiScanInputs(MidiState *m)
|
||||
{
|
||||
m->inputCount = 0;
|
||||
UINT n = midiInGetNumDevs();
|
||||
for (UINT i = 0; i < n && m->inputCount < MIDI_MAX_INPUTS; i++) {
|
||||
MIDIINCAPSA caps;
|
||||
if (midiInGetDevCapsA(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR) {
|
||||
MidiInputInfo *info = &m->inputs[m->inputCount++];
|
||||
info->client = (int)i;
|
||||
info->port = 0;
|
||||
snprintf(info->clientName, sizeof(info->clientName), "%s", caps.szPname);
|
||||
snprintf(info->portName, sizeof(info->portName), "%s", caps.szPname);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void midiDevDisconnect(MidiState *m)
|
||||
{
|
||||
if (m->hMidiIn) {
|
||||
midiInStop((HMIDIIN)m->hMidiIn);
|
||||
midiInClose((HMIDIIN)m->hMidiIn);
|
||||
m->hMidiIn = NULL;
|
||||
}
|
||||
m->connectedClient = -1;
|
||||
m->connectedPort = -1;
|
||||
}
|
||||
|
||||
void midiDevConnect(MidiState *m, int srcClient, int srcPort)
|
||||
{
|
||||
(void)srcPort;
|
||||
midiDevDisconnect(m);
|
||||
HMIDIIN hIn = NULL;
|
||||
if (midiInOpen(&hIn, (UINT)srcClient, (DWORD_PTR)midiCallbackWin32,
|
||||
(DWORD_PTR)m, CALLBACK_FUNCTION) == MMSYSERR_NOERROR) {
|
||||
midiInStart(hIn);
|
||||
m->hMidiIn = hIn;
|
||||
m->connectedClient = srcClient;
|
||||
m->connectedPort = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void midiClose(MidiState *m)
|
||||
{
|
||||
midiDevDisconnect(m);
|
||||
}
|
||||
|
||||
void *midiThread(void *arg)
|
||||
{
|
||||
// WinMM delivers MIDI events via midiCallbackWin32 on its own MM thread.
|
||||
// This function exists only to satisfy the pthread_create call in soundThing.c.
|
||||
(void)arg;
|
||||
while (gRunning) Sleep(50);
|
||||
return NULL;
|
||||
}
|
||||
288
source/patch.c
Normal file
288
source/patch.c
Normal file
@@ -0,0 +1,288 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "patch.h"
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <dirent.h>
|
||||
#ifdef _WIN32
|
||||
# include <direct.h>
|
||||
# include <sys/stat.h>
|
||||
# define MAKE_DIR(p) _mkdir(p)
|
||||
#else
|
||||
# include <sys/stat.h>
|
||||
# define MAKE_DIR(p) mkdir(p, 0755)
|
||||
#endif
|
||||
|
||||
// ------------------------------------------------------------------ HELPERS
|
||||
|
||||
static const char *findValue(const char *json, const char *key)
|
||||
{
|
||||
char pattern[PATCH_NAME_LEN + 4];
|
||||
snprintf(pattern, sizeof(pattern), "\"%s\"", key);
|
||||
const char *p = strstr(json, pattern);
|
||||
if (!p) return NULL;
|
||||
p += strlen(pattern);
|
||||
while (*p && (*p == ' ' || *p == ':' || *p == '\t' || *p == '\n' || *p == '\r')) p++;
|
||||
return p;
|
||||
}
|
||||
|
||||
static int readInt(const char *json, const char *key, int *out)
|
||||
{
|
||||
const char *p = findValue(json, key);
|
||||
if (!p) return 0;
|
||||
*out = (int)strtol(p, NULL, 10);
|
||||
return 1;
|
||||
}
|
||||
|
||||
static int readFloat(const char *json, const char *key, float *out)
|
||||
{
|
||||
const char *p = findValue(json, key);
|
||||
if (!p) return 0;
|
||||
*out = strtof(p, NULL);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ SAVE
|
||||
|
||||
int patchSave(Synth *s, const char *path)
|
||||
{
|
||||
MAKE_DIR("patches");
|
||||
FILE *f = fopen(path, "w");
|
||||
if (!f) return 0;
|
||||
|
||||
fprintf(f, "{\n");
|
||||
|
||||
for (int o = 0; o < OSC_COUNT; o++) {
|
||||
Oscillator *osc = &s->voices[0].oscillators[o];
|
||||
fprintf(f, " \"osc%d_waveform\": %d,\n", o, (int)osc->waveform);
|
||||
fprintf(f, " \"osc%d_dutyCycle\": %f,\n", o, osc->dutyCycle);
|
||||
fprintf(f, " \"osc%d_detune\": %f,\n", o, osc->detune);
|
||||
fprintf(f, " \"osc%d_gain\": %f,\n", o, osc->gain);
|
||||
fprintf(f, " \"osc%d_active\": %d,\n", o, osc->active);
|
||||
fprintf(f, " \"osc%d_octave\": %d,\n", o, osc->octave);
|
||||
for (int m = 0; m < 3; m++)
|
||||
fprintf(f, " \"osc%d_modRouting%d\": %d,\n", o, m, osc->modRouting[m]);
|
||||
for (int m = 0; m < 3; m++)
|
||||
fprintf(f, " \"osc%d_modDepth%d\": %f,\n", o, m, osc->modDepth[m]);
|
||||
}
|
||||
|
||||
Envelope *ae = &s->voices[0].ampEnv;
|
||||
fprintf(f, " \"ampEnv_attack\": %f,\n", ae->attackSec);
|
||||
fprintf(f, " \"ampEnv_decay\": %f,\n", ae->decaySec);
|
||||
fprintf(f, " \"ampEnv_sustain\": %f,\n", ae->sustainLevel);
|
||||
fprintf(f, " \"ampEnv_release\": %f,\n", ae->releaseSec);
|
||||
|
||||
Envelope *me = &s->voices[0].modEnv;
|
||||
fprintf(f, " \"modEnv_attack\": %f,\n", me->attackSec);
|
||||
fprintf(f, " \"modEnv_decay\": %f,\n", me->decaySec);
|
||||
fprintf(f, " \"modEnv_sustain\": %f,\n", me->sustainLevel);
|
||||
fprintf(f, " \"modEnv_release\": %f,\n", me->releaseSec);
|
||||
|
||||
for (int l = 0; l < LFO_COUNT; l++) {
|
||||
LFO *lfo = &s->lfos[l];
|
||||
fprintf(f, " \"lfo%d_rate\": %f,\n", l, lfo->rate);
|
||||
fprintf(f, " \"lfo%d_waveform\": %d,\n", l, (int)lfo->waveform);
|
||||
fprintf(f, " \"lfo%d_active\": %d,\n", l, lfo->active);
|
||||
}
|
||||
|
||||
Filter *flt = &s->voices[0].filter;
|
||||
fprintf(f, " \"filter_cutoff\": %f,\n", flt->cutoff);
|
||||
fprintf(f, " \"filter_resonance\": %f,\n", flt->resonance);
|
||||
fprintf(f, " \"filter_type\": %d,\n", (int)flt->type);
|
||||
fprintf(f, " \"filter_active\": %d,\n", flt->active);
|
||||
fprintf(f, " \"filter_modRouting\": %d,\n", flt->modRouting);
|
||||
fprintf(f, " \"filter_modDepth\": %f,\n", flt->modDepth);
|
||||
fprintf(f, " \"filter_resModRouting\": %d,\n", flt->resModRouting);
|
||||
fprintf(f, " \"filter_resModDepth\": %f,\n", flt->resModDepth);
|
||||
|
||||
fprintf(f, " \"master_volume\": %f,\n", s->volume);
|
||||
fprintf(f, " \"master_pitchBendRange\": %f\n", s->pitchBendRange);
|
||||
|
||||
fprintf(f, "}\n");
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ LOAD
|
||||
|
||||
int patchLoad(Synth *s, const char *path)
|
||||
{
|
||||
FILE *f = fopen(path, "r");
|
||||
if (!f) return 0;
|
||||
|
||||
fseek(f, 0, SEEK_END);
|
||||
long size = ftell(f);
|
||||
fseek(f, 0, SEEK_SET);
|
||||
|
||||
char *buf = (char *)malloc((size_t)size + 1);
|
||||
if (!buf) { fclose(f); return 0; }
|
||||
size_t got = fread(buf, 1, (size_t)size, f);
|
||||
buf[got] = '\0';
|
||||
fclose(f);
|
||||
|
||||
int iv;
|
||||
float fv;
|
||||
char key[PATCH_NAME_LEN];
|
||||
|
||||
for (int o = 0; o < OSC_COUNT; o++) {
|
||||
Oscillator *osc = &s->voices[0].oscillators[o];
|
||||
snprintf(key, sizeof(key), "osc%d_waveform", o);
|
||||
if (readInt(buf, key, &iv) && iv >= 0 && iv < WAVE_COUNT)
|
||||
osc->waveform = (Waveform)iv;
|
||||
snprintf(key, sizeof(key), "osc%d_dutyCycle", o);
|
||||
if (readFloat(buf, key, &fv)) osc->dutyCycle = fv;
|
||||
snprintf(key, sizeof(key), "osc%d_detune", o);
|
||||
if (readFloat(buf, key, &fv)) osc->detune = fv;
|
||||
snprintf(key, sizeof(key), "osc%d_gain", o);
|
||||
if (readFloat(buf, key, &fv)) osc->gain = fv;
|
||||
snprintf(key, sizeof(key), "osc%d_active", o);
|
||||
if (readInt(buf, key, &iv)) osc->active = iv;
|
||||
snprintf(key, sizeof(key), "osc%d_octave", o);
|
||||
if (readInt(buf, key, &iv) && iv >= -2 && iv <= 2) osc->octave = iv;
|
||||
for (int m = 0; m < 3; m++) {
|
||||
snprintf(key, sizeof(key), "osc%d_modRouting%d", o, m);
|
||||
if (readInt(buf, key, &iv)) osc->modRouting[m] = iv;
|
||||
snprintf(key, sizeof(key), "osc%d_modDepth%d", o, m);
|
||||
if (readFloat(buf, key, &fv)) osc->modDepth[m] = fv;
|
||||
}
|
||||
}
|
||||
|
||||
Envelope *ae = &s->voices[0].ampEnv;
|
||||
if (readFloat(buf, "ampEnv_attack", &fv)) ae->attackSec = fv;
|
||||
if (readFloat(buf, "ampEnv_decay", &fv)) ae->decaySec = fv;
|
||||
if (readFloat(buf, "ampEnv_sustain", &fv)) ae->sustainLevel = fv;
|
||||
if (readFloat(buf, "ampEnv_release", &fv)) ae->releaseSec = fv;
|
||||
|
||||
Envelope *me = &s->voices[0].modEnv;
|
||||
if (readFloat(buf, "modEnv_attack", &fv)) me->attackSec = fv;
|
||||
if (readFloat(buf, "modEnv_decay", &fv)) me->decaySec = fv;
|
||||
if (readFloat(buf, "modEnv_sustain", &fv)) me->sustainLevel = fv;
|
||||
if (readFloat(buf, "modEnv_release", &fv)) me->releaseSec = fv;
|
||||
|
||||
for (int l = 0; l < LFO_COUNT; l++) {
|
||||
LFO *lfo = &s->lfos[l];
|
||||
snprintf(key, sizeof(key), "lfo%d_rate", l);
|
||||
if (readFloat(buf, key, &fv)) lfo->rate = fv;
|
||||
snprintf(key, sizeof(key), "lfo%d_waveform", l);
|
||||
if (readInt(buf, key, &iv) && iv >= 0 && iv < WAVE_COUNT)
|
||||
lfo->waveform = (Waveform)iv;
|
||||
snprintf(key, sizeof(key), "lfo%d_active", l);
|
||||
if (readInt(buf, key, &iv)) lfo->active = iv;
|
||||
lfo->phase = 0.0f;
|
||||
lfo->noisePhase = 0.0f;
|
||||
}
|
||||
|
||||
Filter *flt = &s->voices[0].filter;
|
||||
if (readFloat(buf, "filter_cutoff", &fv)) flt->cutoff = fv;
|
||||
if (readFloat(buf, "filter_resonance", &fv)) flt->resonance = fv;
|
||||
if (readInt (buf, "filter_type", &iv) && iv >= 0 && iv < FILTER_COUNT)
|
||||
flt->type = (FilterType)iv;
|
||||
if (readInt (buf, "filter_active", &iv)) flt->active = iv;
|
||||
if (readInt (buf, "filter_modRouting", &iv)) flt->modRouting = iv;
|
||||
if (readFloat(buf, "filter_modDepth", &fv)) flt->modDepth = fv;
|
||||
if (readInt (buf, "filter_resModRouting",&iv)) flt->resModRouting = iv;
|
||||
if (readFloat(buf, "filter_resModDepth", &fv)) flt->resModDepth = fv;
|
||||
flt->low = 0.0f;
|
||||
flt->band = 0.0f;
|
||||
|
||||
if (readFloat(buf, "master_volume", &fv)) s->volume = fv;
|
||||
if (readFloat(buf, "master_pitchBendRange", &fv)) s->pitchBendRange = fv;
|
||||
|
||||
free(buf);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ FAVORITES
|
||||
|
||||
int patchLoadFavs(char (*favs)[PATCH_NAME_LEN], int maxFavs)
|
||||
{
|
||||
FILE *f = fopen("patches/favorites.txt", "r");
|
||||
if (!f) return 0;
|
||||
int count = 0;
|
||||
while (count < maxFavs && fgets(favs[count], PATCH_NAME_LEN, f)) {
|
||||
size_t len = strlen(favs[count]);
|
||||
if (len > 0 && favs[count][len - 1] == '\n') favs[count][len - 1] = '\0';
|
||||
if (favs[count][0] != '\0') count++;
|
||||
}
|
||||
fclose(f);
|
||||
return count;
|
||||
}
|
||||
|
||||
int patchSaveFavs(char (*favs)[PATCH_NAME_LEN], int favCount)
|
||||
{
|
||||
MAKE_DIR("patches");
|
||||
FILE *f = fopen("patches/favorites.txt", "w");
|
||||
if (!f) return 0;
|
||||
for (int i = 0; i < favCount; i++)
|
||||
fprintf(f, "%s\n", favs[i]);
|
||||
fclose(f);
|
||||
return 1;
|
||||
}
|
||||
|
||||
// ------------------------------------------------------------------ SCAN
|
||||
|
||||
static int cmpStrings(const void *a, const void *b)
|
||||
{
|
||||
return strcmp((const char *)a, (const char *)b);
|
||||
}
|
||||
|
||||
int patchScanFiles(char (*files)[PATCH_NAME_LEN], int maxFiles)
|
||||
{
|
||||
int count = 0;
|
||||
DIR *d = opendir("patches");
|
||||
if (!d) return 0;
|
||||
|
||||
struct dirent *ent;
|
||||
while ((ent = readdir(d)) != NULL && count < maxFiles) {
|
||||
const char *name = ent->d_name;
|
||||
size_t len = strlen(name);
|
||||
if (len > 5 && strcmp(name + len - 5, ".json") == 0) {
|
||||
snprintf(files[count], PATCH_NAME_LEN, "%.*s", (int)(len - 5), name);
|
||||
count++;
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
|
||||
qsort(files, (size_t)count, PATCH_NAME_LEN, cmpStrings);
|
||||
return count;
|
||||
}
|
||||
|
||||
int patchScanDir(const char *relPath,
|
||||
char (*files)[PATCH_NAME_LEN], int maxFiles,
|
||||
char (*dirs)[PATCH_NAME_LEN], int maxDirs, int *outDirCount)
|
||||
{
|
||||
char scanPath[512];
|
||||
if (relPath && relPath[0])
|
||||
snprintf(scanPath, sizeof(scanPath), "patches/%s", relPath);
|
||||
else
|
||||
snprintf(scanPath, sizeof(scanPath), "patches");
|
||||
MAKE_DIR(scanPath);
|
||||
|
||||
int fc = 0, dc = 0;
|
||||
DIR *d = opendir(scanPath);
|
||||
if (!d) { *outDirCount = 0; return 0; }
|
||||
|
||||
struct dirent *ent;
|
||||
while ((ent = readdir(d)) != NULL) {
|
||||
const char *name = ent->d_name;
|
||||
if (name[0] == '.') continue;
|
||||
size_t len = strlen(name);
|
||||
char full[1024];
|
||||
snprintf(full, sizeof(full), "%s/%s", scanPath, name);
|
||||
struct stat st;
|
||||
if (stat(full, &st) != 0) continue;
|
||||
if (S_ISDIR(st.st_mode)) {
|
||||
if (dc < maxDirs)
|
||||
snprintf(dirs[dc++], PATCH_NAME_LEN, "%s", name);
|
||||
} else if (len > 5 && strcmp(name + len - 5, ".json") == 0) {
|
||||
if (fc < maxFiles)
|
||||
snprintf(files[fc++], PATCH_NAME_LEN, "%.*s", (int)(len - 5), name);
|
||||
}
|
||||
}
|
||||
closedir(d);
|
||||
|
||||
qsort(files, (size_t)fc, PATCH_NAME_LEN, cmpStrings);
|
||||
qsort(dirs, (size_t)dc, PATCH_NAME_LEN, cmpStrings);
|
||||
*outDirCount = dc;
|
||||
return fc;
|
||||
}
|
||||
29
source/platform.c
Normal file
29
source/platform.c
Normal file
@@ -0,0 +1,29 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
/* Isolated from raylib.h to avoid Rectangle/CloseWindow/ShowCursor conflicts
|
||||
with windows.h on Windows cross-compile targets. */
|
||||
#ifdef _WIN32
|
||||
# include <windows.h>
|
||||
# include <direct.h>
|
||||
#else
|
||||
# include <unistd.h>
|
||||
# include <libgen.h>
|
||||
#endif
|
||||
#include <string.h>
|
||||
#include "platform.h"
|
||||
|
||||
void chdirToExeDir(void)
|
||||
{
|
||||
char path[1024];
|
||||
#ifdef _WIN32
|
||||
DWORD len = GetModuleFileNameA(NULL, path, (DWORD)sizeof(path));
|
||||
if (!len) return;
|
||||
char *last = strrchr(path, '\\');
|
||||
if (!last) last = strrchr(path, '/');
|
||||
if (last) { *last = '\0'; _chdir(path); }
|
||||
#else
|
||||
ssize_t len = readlink("/proc/self/exe", path, sizeof(path) - 1);
|
||||
if (len <= 0) return;
|
||||
path[len] = '\0';
|
||||
if (chdir(dirname(path)) != 0) { }
|
||||
#endif
|
||||
}
|
||||
@@ -1,60 +1,60 @@
|
||||
#define _POSIX_C_SOURCE 200809L
|
||||
#include "raylib.h"
|
||||
#include <stdio.h>
|
||||
#include <string.h>
|
||||
#include <stdatomic.h>
|
||||
#include <pthread.h>
|
||||
#include <alloca.h>
|
||||
#include <alsa/asoundlib.h>
|
||||
#include "raylib.h"
|
||||
#include "synth.h"
|
||||
#include "midi.h"
|
||||
#include "ui.h"
|
||||
#include "config.h"
|
||||
#include "icon_data.h"
|
||||
#include "platform.h"
|
||||
|
||||
#define LOGICAL_WIDTH 1020
|
||||
#define LOGICAL_HEIGHT 710
|
||||
#define BORDER_WIDTH 8
|
||||
|
||||
atomic_int gRunning = 1;
|
||||
|
||||
static Synth gSynth = {0};
|
||||
static MidiState gMidi = {0};
|
||||
static UIState gUI = {0};
|
||||
|
||||
static void AudioOutputCallback(void *buffer, unsigned int frames)
|
||||
{
|
||||
synthFillBuffer(&gSynth, (int16_t *)buffer, (int)frames);
|
||||
}
|
||||
|
||||
static void drawWaveform(Waveform w, float dutyCycle, int x, int y, int width, int height)
|
||||
{
|
||||
int steps = width;
|
||||
for (int i = 0; i < steps - 1; i++) {
|
||||
float phase0 = (float)i / steps;
|
||||
float phase1 = (float)(i + 1) / steps;
|
||||
float s0 = waveformSample(w, phase0, dutyCycle);
|
||||
float s1 = waveformSample(w, phase1, dutyCycle);
|
||||
|
||||
int cx = x + i;
|
||||
int cy = y + (int)((1.0f - (s0 * 0.5f + 0.5f)) * height);
|
||||
int nx = x + i + 1;
|
||||
int ny = y + (int)((1.0f - (s1 * 0.5f + 0.5f)) * height);
|
||||
|
||||
DrawLine(cx, cy, nx, ny, GREEN);
|
||||
}
|
||||
DrawRectangleLines(x, y, width, height, DARKGRAY);
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
chdirToExeDir();
|
||||
const int sampleRate = 48000;
|
||||
|
||||
if (!midiInit(&gMidi, &gSynth)) return 1;
|
||||
midiListInputs(&gMidi);
|
||||
|
||||
int srcClient, srcPort;
|
||||
printf("\nEnter client and port to connect (e.g. '20 0'), or -1 -1 to skip: ");
|
||||
if (scanf("%d %d", &srcClient, &srcPort) == 2 && srcClient != -1)
|
||||
midiConnect(&gMidi, srcClient, srcPort);
|
||||
midiScanInputs(&gMidi);
|
||||
|
||||
pthread_t midiTid;
|
||||
pthread_create(&midiTid, NULL, midiThread, &gMidi);
|
||||
|
||||
synthInit(&gSynth, (float)sampleRate);
|
||||
|
||||
InitWindow(800, 450, "SoundThing");
|
||||
// Window setup
|
||||
SetConfigFlags(FLAG_WINDOW_RESIZABLE);
|
||||
InitWindow(LOGICAL_WIDTH + 2 * BORDER_WIDTH, LOGICAL_HEIGHT + 2 * BORDER_WIDTH, "SoundThing");
|
||||
{
|
||||
Image appIcon = LoadImageFromMemory(".png", icon_png, (int)icon_png_len);
|
||||
SetWindowIcon(appIcon);
|
||||
UnloadImage(appIcon);
|
||||
}
|
||||
|
||||
// Camera2D setup
|
||||
Camera2D ui_cam = { 0 };
|
||||
ui_cam.zoom = 1.0f;
|
||||
recomputeViewPort(&ui_cam, LOGICAL_WIDTH, LOGICAL_HEIGHT, BORDER_WIDTH);
|
||||
|
||||
// Audio device setup
|
||||
InitAudioDevice();
|
||||
SetAudioStreamBufferSizeDefault(1024);
|
||||
|
||||
@@ -63,34 +63,141 @@ int main(void)
|
||||
PlayAudioStream(stream);
|
||||
SetTargetFPS(120);
|
||||
|
||||
uiInit(&gUI, &gMidi, &ui_cam);
|
||||
|
||||
// Auto-connect to last used MIDI device if still available
|
||||
{
|
||||
char lastDev[CONFIG_NAME_LEN];
|
||||
if (configLoadLastDevice(lastDev, sizeof(lastDev))) {
|
||||
for (int i = 0; i < gMidi.inputCount; i++) {
|
||||
if (strcmp(gMidi.inputs[i].clientName, lastDev) == 0) {
|
||||
midiDevConnect(&gMidi, gMidi.inputs[i].client, gMidi.inputs[i].port);
|
||||
uiLoadCcMappingsForDevice(&gUI, lastDev);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Layout constants
|
||||
const float pad = 10.0f;
|
||||
const float oscW = 220.0f;
|
||||
const float oscH = 340.0f;
|
||||
const float envW = 180.0f;
|
||||
const float envH = 280.0f;
|
||||
const float masterW = 180.0f;
|
||||
const float masterH = 360.0f;
|
||||
const float lfoW = 180.0f;
|
||||
const float lfoH = 155.0f;
|
||||
const float filterW = 200.0f;
|
||||
const float filterH = 300.0f;
|
||||
|
||||
while (!WindowShouldClose()) {
|
||||
if (IsKeyPressed(KEY_UP))
|
||||
gSynth.waveform = (gSynth.waveform + 1) % WAVE_COUNT;
|
||||
if (IsKeyPressed(KEY_DOWN))
|
||||
gSynth.waveform = (gSynth.waveform + WAVE_COUNT - 1) % WAVE_COUNT;
|
||||
// Sync voice settings from voice 0
|
||||
synthSyncVoices(&gSynth);
|
||||
|
||||
BeginDrawing();
|
||||
ClearBackground(BLACK);
|
||||
BeginMode2D(ui_cam);
|
||||
if(IsWindowResized()) {
|
||||
recomputeViewPort(&ui_cam, LOGICAL_WIDTH, LOGICAL_HEIGHT, BORDER_WIDTH);
|
||||
}
|
||||
ClearBackground((Color){ 189, 168, 131, 255 }); // tan background
|
||||
|
||||
drawWaveform(gSynth.waveform, gSynth.dutyCycle, 20, 120, 400, 150);
|
||||
// // Oscillator panels — left side, stacked vertically
|
||||
// uiOscillatorPanel(&gUI, 0,
|
||||
// padding, padding,
|
||||
// oscW, oscH,
|
||||
// &gSynth.voices[0].oscillators[0], "Oscillator 0");
|
||||
//
|
||||
// uiOscillatorPanel(&gUI, 100,
|
||||
// padding, padding + oscH + padding,
|
||||
// oscW, oscH,
|
||||
// &gSynth.voices[0].oscillators[1], "Oscillator 1");
|
||||
//
|
||||
// // Envelope panels — to the right of oscillators
|
||||
// float envX = padding + oscW + padding;
|
||||
// uiEnvelopePanel(&gUI, 200,
|
||||
// envX, padding,
|
||||
// envW, envH,
|
||||
// &gSynth.voices[0].ampEnv, "Amp Envelope");
|
||||
//
|
||||
// uiEnvelopePanel(&gUI, 250,
|
||||
// envX, padding + envH + padding,
|
||||
// envW, envH,
|
||||
// &gSynth.voices[0].modEnv, "Mod Envelope");
|
||||
//
|
||||
// // Master panel — to the right of envelopes
|
||||
// float masterX = envX + envW + padding;
|
||||
// uiMasterPanel(&gUI,
|
||||
// masterX, padding,
|
||||
// masterW, masterH,
|
||||
// &gSynth);
|
||||
|
||||
DrawText("SoundThing", 20, 20, 24, RAYWHITE);
|
||||
DrawText(TextFormat("Waveform: %s (UP/DOWN to change)", waveformName(gSynth.waveform)),
|
||||
20, 55, 20, RAYWHITE);
|
||||
DrawText(TextFormat("MIDI: client %d port %d", gMidi.client, gMidi.port),
|
||||
20, 90, 20, GRAY);
|
||||
if (gSynth.waveform == WAVE_PULSE)
|
||||
DrawText(TextFormat("Duty Cycle: %.2f", gSynth.dutyCycle), 20, 280, 20, GREEN);
|
||||
// Left column — oscillator panels stacked vertically
|
||||
float oscX = pad;
|
||||
uiOscillatorPanel(&gUI, 0,
|
||||
oscX, pad,
|
||||
oscW, oscH,
|
||||
&gSynth.voices[0].oscillators[0], "Oscillator 0");
|
||||
|
||||
uiOscillatorPanel(&gUI, 100,
|
||||
oscX, pad + oscH + pad,
|
||||
oscW, oscH,
|
||||
&gSynth.voices[0].oscillators[1], "Oscillator 1");
|
||||
|
||||
// Middle column — envelope panels stacked vertically
|
||||
float envX = pad + oscW + pad;
|
||||
uiEnvelopePanel(&gUI, 200,
|
||||
envX, pad,
|
||||
envW, envH,
|
||||
&gSynth.voices[0].ampEnv, "Envelope 0 - Amp");
|
||||
|
||||
uiEnvelopePanel(&gUI, 250,
|
||||
envX, pad + envH + pad,
|
||||
envW, envH,
|
||||
&gSynth.voices[0].modEnv, "Envelope 1 - Mod");
|
||||
|
||||
// LFO column
|
||||
float lfoX = pad + oscW + pad + envW + pad;
|
||||
uiLfoPanel(&gUI, 500,
|
||||
lfoX, pad,
|
||||
lfoW, lfoH,
|
||||
&gSynth.lfos[0], "LFO 0");
|
||||
uiLfoPanel(&gUI, 600,
|
||||
lfoX, pad + lfoH + pad,
|
||||
lfoW, lfoH,
|
||||
&gSynth.lfos[1], "LFO 1");
|
||||
|
||||
// Filter panel
|
||||
float filterX = lfoX + lfoW + pad;
|
||||
uiFilterPanel(&gUI,
|
||||
filterX, pad,
|
||||
filterW, filterH,
|
||||
&gSynth.voices[0].filter, "Filter");
|
||||
|
||||
// Master panel — rightmost
|
||||
float masterX = filterX + filterW + pad;
|
||||
uiMasterPanel(&gUI,
|
||||
masterX, pad,
|
||||
masterW, masterH,
|
||||
&gSynth);
|
||||
|
||||
// Floating overlays — drawn last, each one on top of the previous
|
||||
uiMidiMenu(&gUI, (LOGICAL_WIDTH - 420) * 0.5f, 100.0f, 420.0f, &gMidi);
|
||||
uiPatchMenu(&gUI, (LOGICAL_WIDTH - 420) * 0.5f, 80.0f, 420.0f, &gSynth);
|
||||
uiAboutMenu(&gUI, (LOGICAL_WIDTH - 420) * 0.5f, 160.0f, 420.0f);
|
||||
|
||||
EndDrawing();
|
||||
}
|
||||
|
||||
|
||||
gRunning = 0;
|
||||
StopAudioStream(stream);
|
||||
UnloadAudioStream(stream);
|
||||
CloseAudioDevice();
|
||||
CloseWindow();
|
||||
pthread_join(midiTid, NULL);
|
||||
uiClose(&gUI);
|
||||
CloseWindow();
|
||||
midiClose(&gMidi);
|
||||
return 0;
|
||||
}
|
||||
|
||||
49
source/sprites_data.c
Normal file
49
source/sprites_data.c
Normal file
@@ -0,0 +1,49 @@
|
||||
unsigned char sprites_png[] = {
|
||||
0x89, 0x50, 0x4e, 0x47, 0x0d, 0x0a, 0x1a, 0x0a, 0x00, 0x00, 0x00, 0x0d,
|
||||
0x49, 0x48, 0x44, 0x52, 0x00, 0x00, 0x00, 0x58, 0x00, 0x00, 0x00, 0x10,
|
||||
0x08, 0x06, 0x00, 0x00, 0x00, 0x92, 0x9e, 0xb8, 0x35, 0x00, 0x00, 0x00,
|
||||
0x01, 0x73, 0x52, 0x47, 0x42, 0x00, 0xae, 0xce, 0x1c, 0xe9, 0x00, 0x00,
|
||||
0x01, 0xd8, 0x49, 0x44, 0x41, 0x54, 0x58, 0x85, 0xed, 0x58, 0xc1, 0x91,
|
||||
0xc2, 0x30, 0x0c, 0xdc, 0x30, 0x14, 0xa0, 0x12, 0x5c, 0x82, 0x4b, 0xa0,
|
||||
0x83, 0xb8, 0x84, 0x94, 0x40, 0x29, 0xbe, 0x0e, 0x28, 0x21, 0xa5, 0xa8,
|
||||
0x04, 0x4a, 0x50, 0x07, 0xb9, 0x07, 0xe3, 0xa0, 0x18, 0xc5, 0x36, 0x1c,
|
||||
0x30, 0xc7, 0x5d, 0x76, 0x26, 0x0f, 0xbc, 0x96, 0xac, 0xac, 0x14, 0x45,
|
||||
0xa4, 0xc3, 0x87, 0x83, 0x88, 0xa6, 0x57, 0xfa, 0x17, 0x91, 0xae, 0xf5,
|
||||
0x4c, 0x6b, 0xef, 0x1e, 0x00, 0x98, 0x79, 0x35, 0x48, 0xef, 0x7d, 0xa7,
|
||||
0xf9, 0x10, 0x02, 0xc6, 0x71, 0x5c, 0xf0, 0xce, 0xb9, 0x09, 0xc0, 0x62,
|
||||
0x7d, 0xcd, 0x5e, 0xfb, 0x01, 0x80, 0xf3, 0xf9, 0x6c, 0xf2, 0x35, 0xfb,
|
||||
0xe4, 0x43, 0x44, 0xe0, 0xbd, 0x37, 0x6d, 0x99, 0x39, 0xf9, 0x68, 0xe2,
|
||||
0x87, 0x61, 0x00, 0x00, 0x9c, 0x4e, 0xa7, 0x05, 0xaf, 0x41, 0x44, 0x53,
|
||||
0xc1, 0xdf, 0x94, 0x8b, 0xbc, 0xd7, 0xc1, 0x02, 0xb6, 0x48, 0x7a, 0xcf,
|
||||
0x38, 0x8e, 0x37, 0x22, 0x27, 0x3b, 0x6b, 0xbd, 0xe6, 0xc7, 0xe2, 0x6a,
|
||||
0x76, 0xfa, 0xcc, 0xc3, 0xe1, 0x00, 0xe0, 0x22, 0x0e, 0x11, 0x01, 0x00,
|
||||
0x44, 0x64, 0x16, 0x29, 0xa1, 0xc6, 0x13, 0xd1, 0xec, 0x9b, 0x88, 0x20,
|
||||
0x22, 0x37, 0xe7, 0x97, 0xc4, 0x05, 0x2e, 0x89, 0xca, 0x45, 0xde, 0xe9,
|
||||
0xc0, 0xf3, 0x9b, 0x2e, 0xdd, 0xac, 0x85, 0x35, 0xd1, 0x34, 0x5a, 0x92,
|
||||
0x90, 0x62, 0xb1, 0x92, 0x58, 0x8a, 0xab, 0x0f, 0x01, 0x7d, 0x29, 0x41,
|
||||
0x7d, 0x40, 0xe8, 0x6d, 0x5e, 0x44, 0x10, 0x42, 0x98, 0x9f, 0x8a, 0x1c,
|
||||
0xb9, 0xb8, 0x44, 0xb4, 0xb8, 0x12, 0xbc, 0xf7, 0x8b, 0x16, 0xb2, 0x47,
|
||||
0x23, 0xb4, 0x30, 0xa5, 0x6a, 0x2d, 0x71, 0xad, 0x15, 0x5e, 0xc3, 0x9a,
|
||||
0x8f, 0xaf, 0x18, 0x01, 0x00, 0x64, 0xb2, 0x40, 0xfc, 0xba, 0xf0, 0xd6,
|
||||
0x86, 0x52, 0x05, 0x5b, 0xe2, 0xe6, 0x49, 0xd0, 0x6b, 0xba, 0x92, 0x77,
|
||||
0x79, 0xc0, 0x49, 0xa0, 0x9f, 0x88, 0x61, 0x55, 0x72, 0xc9, 0x5f, 0x3a,
|
||||
0x6f, 0xad, 0x32, 0x5b, 0x9f, 0x2a, 0xe7, 0x1c, 0x9c, 0x73, 0x0f, 0xf1,
|
||||
0x6b, 0x15, 0xdc, 0x22, 0x6e, 0xb2, 0xb7, 0x2a, 0xd9, 0xac, 0xe0, 0x5c,
|
||||
0x88, 0x52, 0xcf, 0x5d, 0x83, 0xe6, 0x6b, 0xc9, 0x6a, 0x69, 0x19, 0xba,
|
||||
0x8d, 0x59, 0x3e, 0xf3, 0x9e, 0x9a, 0xa3, 0xc6, 0x37, 0xf6, 0x60, 0x73,
|
||||
0x3d, 0x21, 0x89, 0xac, 0xf7, 0xdc, 0x8c, 0x15, 0x9f, 0x86, 0x57, 0x8f,
|
||||
0x69, 0xc0, 0x75, 0xca, 0xa8, 0x09, 0x9c, 0xef, 0x61, 0xe6, 0xf6, 0x1e,
|
||||
0xfc, 0x5b, 0x61, 0xcd, 0x9e, 0xcf, 0x44, 0x29, 0x81, 0xc3, 0x30, 0x54,
|
||||
0x9f, 0x8c, 0xdd, 0xb3, 0x03, 0xfa, 0x2f, 0x68, 0x11, 0x17, 0xb8, 0x63,
|
||||
0x8a, 0xd8, 0x70, 0xed, 0xb1, 0x21, 0x04, 0x53, 0x5c, 0xab, 0x85, 0x6c,
|
||||
0x15, 0x5c, 0x81, 0x88, 0x74, 0xfa, 0x1f, 0xdd, 0xda, 0x0b, 0x3b, 0x17,
|
||||
0x97, 0x99, 0x21, 0x22, 0xdd, 0xc7, 0xbf, 0xe4, 0xde, 0x01, 0xe7, 0xdc,
|
||||
0x24, 0x22, 0x88, 0x31, 0xce, 0x95, 0xab, 0x47, 0x32, 0x00, 0xa6, 0xb8,
|
||||
0xc0, 0x56, 0xc1, 0xcd, 0x88, 0x31, 0xe2, 0x78, 0x3c, 0xce, 0xbf, 0x45,
|
||||
0x64, 0x71, 0x25, 0x68, 0x71, 0x81, 0x3f, 0x30, 0xa6, 0xbd, 0x03, 0xcc,
|
||||
0x3c, 0xa5, 0x99, 0xbe, 0xf6, 0x71, 0x29, 0x9f, 0x6a, 0x36, 0x81, 0x1f,
|
||||
0xc0, 0x3d, 0x9f, 0x2b, 0xbf, 0x01, 0xc8, 0x27, 0x72, 0x5f, 0xca, 0xe5,
|
||||
0xdb, 0x35, 0x00, 0x00, 0x00, 0x00, 0x49, 0x45, 0x4e, 0x44, 0xae, 0x42,
|
||||
0x60, 0x82
|
||||
};
|
||||
unsigned int sprites_png_len = 542;
|
||||
438
source/synth.c
438
source/synth.c
@@ -2,6 +2,7 @@
|
||||
#include <math.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
|
||||
#ifndef M_PI
|
||||
#define M_PI 3.14159265358979323846
|
||||
@@ -9,26 +10,231 @@
|
||||
|
||||
void synthInit(Synth *s, float sampleRate)
|
||||
{
|
||||
s->sampleRate = sampleRate;
|
||||
s->attackSec = 0.005f;
|
||||
s->releaseSec = 0.050f;
|
||||
s->dutyCycle = 0.5f;
|
||||
s->waveform = WAVE_SINE;
|
||||
s->lastStolenVoice = 0;
|
||||
s->sampleRate = sampleRate;
|
||||
s->pitchBend = 0.0f;
|
||||
s->pitchBendRange = 2.0f;
|
||||
s->lastStolenVoice = 0;
|
||||
s->volume = 0.8f;
|
||||
|
||||
for (int i = 0; i < VOICE_COUNT; i++) {
|
||||
s->voices[i].phase = 0.0f;
|
||||
s->voices[i].freqHz = 440.0f;
|
||||
s->voices[i].amp = 0.0f;
|
||||
s->voices[i].targetAmp = 0.0f;
|
||||
s->voices[i].active = 0;
|
||||
s->voices[i].midiNote = -1;
|
||||
s->pitchBend = 0.0f;
|
||||
s->pitchBendRange = 2.0f;
|
||||
s->voices[i].freqHz = 440.0f;
|
||||
s->voices[i].active = 0;
|
||||
s->voices[i].midiNote = -1;
|
||||
|
||||
oscillatorInit(&s->voices[i].oscillators[0], WAVE_TRIANGLE, 0.5f, 0.0f, OSC_MAX_GAIN);
|
||||
oscillatorInit(&s->voices[i].oscillators[1], WAVE_TRIANGLE, 0.5f, 0.0f, 0.0f);
|
||||
|
||||
envelopeInit(&s->voices[i].ampEnv,
|
||||
0.005f, // attack
|
||||
0.10f, // decay
|
||||
0.70f, // sustain
|
||||
0.50f); // release
|
||||
|
||||
envelopeInit(&s->voices[i].modEnv,
|
||||
0.005f, // attack
|
||||
0.50f, // decay
|
||||
0.0f, // sustain
|
||||
0.10f); // release
|
||||
|
||||
s->voices[i].filter.cutoff = 8000.0f;
|
||||
s->voices[i].filter.resonance = 0.0f;
|
||||
s->voices[i].filter.type = FILTER_LOWPASS;
|
||||
s->voices[i].filter.active = 0;
|
||||
s->voices[i].filter.low = 0.0f;
|
||||
s->voices[i].filter.band = 0.0f;
|
||||
s->voices[i].filter.modRouting = MOD_SOURCE_NONE;
|
||||
s->voices[i].filter.modDepth = 0.0f;
|
||||
s->voices[i].filter.resModRouting = MOD_SOURCE_NONE;
|
||||
s->voices[i].filter.resModDepth = 0.0f;
|
||||
}
|
||||
s->voices[0].oscillators[0].active = 1;
|
||||
|
||||
for (int l = 0; l < LFO_COUNT; l++) {
|
||||
s->lfos[l].phase = 0.0f;
|
||||
s->lfos[l].rate = 1.0f;
|
||||
s->lfos[l].waveform = WAVE_SINE;
|
||||
s->lfos[l].active = 0;
|
||||
s->lfos[l].noiseHeld = 0.0f;
|
||||
s->lfos[l].noisePhase = 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
float waveformSample(Waveform w, float phase, float dutyCycle)
|
||||
void synthResetPatch(Synth *s)
|
||||
{
|
||||
Voice *v = &s->voices[0];
|
||||
|
||||
oscillatorInit(&v->oscillators[0], WAVE_TRIANGLE, 0.5f, 0.0f, OSC_MAX_GAIN);
|
||||
v->oscillators[0].active = 1;
|
||||
for (int m = 0; m < 3; m++) {
|
||||
v->oscillators[0].modRouting[m] = MOD_SOURCE_NONE;
|
||||
v->oscillators[0].modDepth[m] = 0.0f;
|
||||
}
|
||||
|
||||
oscillatorInit(&v->oscillators[1], WAVE_TRIANGLE, 0.5f, 0.0f, 0.0f);
|
||||
v->oscillators[1].active = 0;
|
||||
for (int m = 0; m < 3; m++) {
|
||||
v->oscillators[1].modRouting[m] = MOD_SOURCE_NONE;
|
||||
v->oscillators[1].modDepth[m] = 0.0f;
|
||||
}
|
||||
|
||||
envelopeInit(&v->ampEnv, 0.005f, 0.10f, 0.70f, 0.50f);
|
||||
envelopeInit(&v->modEnv, 0.005f, 0.50f, 0.0f, 0.10f);
|
||||
|
||||
v->filter.cutoff = 8000.0f;
|
||||
v->filter.resonance = 0.0f;
|
||||
v->filter.type = FILTER_LOWPASS;
|
||||
v->filter.active = 0;
|
||||
v->filter.low = 0.0f;
|
||||
v->filter.band = 0.0f;
|
||||
v->filter.modRouting = MOD_SOURCE_NONE;
|
||||
v->filter.modDepth = 0.0f;
|
||||
v->filter.resModRouting = MOD_SOURCE_NONE;
|
||||
v->filter.resModDepth = 0.0f;
|
||||
|
||||
for (int l = 0; l < LFO_COUNT; l++) {
|
||||
s->lfos[l].phase = 0.0f;
|
||||
s->lfos[l].rate = 1.0f;
|
||||
s->lfos[l].waveform = WAVE_SINE;
|
||||
s->lfos[l].active = 0;
|
||||
s->lfos[l].noiseHeld = 0.0f;
|
||||
s->lfos[l].noisePhase = 0.0f;
|
||||
}
|
||||
|
||||
s->volume = 0.8f;
|
||||
s->pitchBendRange = 2.0f;
|
||||
}
|
||||
|
||||
|
||||
|
||||
void oscillatorInit(Oscillator *o, Waveform waveform, float dutyCycle, float detune, float gain)
|
||||
{
|
||||
o->phase = 0.0f;
|
||||
o->waveform = waveform;
|
||||
o->dutyCycle = dutyCycle;
|
||||
o->detune = detune;
|
||||
o->noiseHeld = 0.0f;
|
||||
o->noisePhase = 0.0f;
|
||||
o->gain = gain;
|
||||
o->octave = 0;
|
||||
}
|
||||
|
||||
|
||||
static float getModValue(float ampEnv, float modEnv, float lfo0, float lfo1, ModSource source)
|
||||
{
|
||||
switch (source) {
|
||||
case MOD_SOURCE_AMP_ENV: return ampEnv;
|
||||
case MOD_SOURCE_MOD_ENV: return modEnv;
|
||||
case MOD_SOURCE_LFO: return lfo0;
|
||||
case MOD_SOURCE_LFO2: return lfo1;
|
||||
default: return 0.0f;
|
||||
}
|
||||
}
|
||||
|
||||
float lfoTick(LFO *l, float sampleRate)
|
||||
{
|
||||
if (!l->active) return 0.0f;
|
||||
l->phase += l->rate / sampleRate;
|
||||
if (l->phase >= 1.0f) l->phase -= 1.0f;
|
||||
if (l->waveform == WAVE_NOISE) {
|
||||
l->noisePhase += l->rate / sampleRate;
|
||||
if (l->noisePhase >= 1.0f) {
|
||||
l->noisePhase -= 1.0f;
|
||||
l->noiseHeld = ((float)rand() / (float)RAND_MAX) * 2.0f - 1.0f;
|
||||
}
|
||||
}
|
||||
return waveformSample(l->waveform, l->phase, 0.5f, l->noiseHeld);
|
||||
}
|
||||
|
||||
float filterTick(Filter *f, float input, float cutoff, float resonance, float sampleRate)
|
||||
{
|
||||
if (!f->active) return input;
|
||||
|
||||
if (cutoff < 20.0f) cutoff = 20.0f;
|
||||
if (cutoff > sampleRate * 0.499f) cutoff = sampleRate * 0.499f;
|
||||
if (resonance < 0.0f) resonance = 0.0f;
|
||||
if (resonance > 0.99f) resonance = 0.99f;
|
||||
|
||||
// Andy Simper TPT SVF (bilinear integration — unconditionally stable)
|
||||
float g = tanf((float)M_PI * cutoff / sampleRate);
|
||||
float Q = 0.5f + resonance * 9.5f; // resonance 0..0.99 → Q 0.5..10.0
|
||||
float k = 1.0f / Q;
|
||||
float a1 = 1.0f / (1.0f + g * (g + k));
|
||||
float a2 = g * a1;
|
||||
float a3 = g * a2;
|
||||
|
||||
// f->band = s1, f->low = s2 (integrator states)
|
||||
float v3 = input - f->low;
|
||||
float v1 = a1 * f->band + a2 * v3;
|
||||
float v2 = f->low + a2 * f->band + a3 * v3;
|
||||
f->band = 2.0f * v1 - f->band;
|
||||
f->low = 2.0f * v2 - f->low;
|
||||
|
||||
switch (f->type) {
|
||||
case FILTER_LOWPASS: return v2;
|
||||
case FILTER_HIGHPASS: return input - k * v1 - v2;
|
||||
case FILTER_BANDPASS: return v1;
|
||||
default: return v2;
|
||||
}
|
||||
}
|
||||
|
||||
const char *filterTypeName(FilterType t)
|
||||
{
|
||||
switch (t) {
|
||||
case FILTER_LOWPASS: return "LP";
|
||||
case FILTER_HIGHPASS: return "HP";
|
||||
case FILTER_BANDPASS: return "BP";
|
||||
default: return "??";
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// float oscillatorTick(Oscillator *o, float freqHz, float bendMultiplier, float sampleRate,
|
||||
// float dutyCycle, float detune, float gain)
|
||||
// {
|
||||
// float detuneMultiplier = powf(2.0f, o->detune / 1200.0f);
|
||||
// float freq = freqHz * detuneMultiplier * bendMultiplier;
|
||||
//
|
||||
// // Advance phase
|
||||
// o->phase += freq / sampleRate;
|
||||
// if (o->phase >= 1.0f) o->phase -= 1.0f;
|
||||
//
|
||||
// // Clocked noise — draw a new random value once per cycle
|
||||
// if (o->waveform == WAVE_NOISE) {
|
||||
// o->noisePhase += freq / sampleRate;
|
||||
// if (o->noisePhase >= 1.0f) {
|
||||
// o->noisePhase -= 1.0f;
|
||||
// o->noiseHeld = ((float)rand() / (float)RAND_MAX) * 2.0f - 1.0f;
|
||||
// }
|
||||
// }
|
||||
//
|
||||
// return waveformSample(o->waveform, o->phase, o->dutyCycle, o->noiseHeld) * o->gain;
|
||||
// }
|
||||
|
||||
float oscillatorTick(Oscillator *o, float freqHz, float bendMultiplier, float sampleRate,
|
||||
float dutyCycle, float detune, float gain)
|
||||
{
|
||||
float detuneMultiplier = powf(2.0f, detune / 1200.0f);
|
||||
float freq = freqHz * detuneMultiplier * bendMultiplier;
|
||||
|
||||
// Advance phase
|
||||
o->phase += freq / sampleRate;
|
||||
if (o->phase >= 1.0f) o->phase -= 1.0f;
|
||||
|
||||
// Clocked noise — draw a new random value once per cycle
|
||||
if (o->waveform == WAVE_NOISE) {
|
||||
o->noisePhase += freq / sampleRate;
|
||||
if (o->noisePhase >= 1.0f) {
|
||||
o->noisePhase -= 1.0f;
|
||||
o->noiseHeld = ((float)rand() / (float)RAND_MAX) * 2.0f - 1.0f;
|
||||
}
|
||||
}
|
||||
|
||||
return waveformSample(o->waveform, o->phase, dutyCycle, o->noiseHeld) * gain;
|
||||
}
|
||||
|
||||
|
||||
|
||||
float waveformSample(Waveform w, float phase, float dutyCycle, float noiseHeld)
|
||||
{
|
||||
switch (w) {
|
||||
case WAVE_SINE:
|
||||
@@ -44,7 +250,7 @@ float waveformSample(Waveform w, float phase, float dutyCycle)
|
||||
case WAVE_PULSE:
|
||||
return (phase < dutyCycle) ? 1.0f : -1.0f;
|
||||
case WAVE_NOISE:
|
||||
return ((float)rand() / (float)RAND_MAX) * 2.0f - 1.0f;
|
||||
return noiseHeld;
|
||||
default:
|
||||
return 0.0f;
|
||||
}
|
||||
@@ -69,67 +275,95 @@ void synthNoteOn(Synth *s, int midiNote)
|
||||
|
||||
for (int i = 0; i < VOICE_COUNT; i++) {
|
||||
if (!s->voices[i].active) {
|
||||
s->voices[i].freqHz = hz;
|
||||
s->voices[i].midiNote = midiNote;
|
||||
s->voices[i].targetAmp = 1.0f;
|
||||
s->voices[i].active = 1;
|
||||
s->voices[i].freqHz = hz;
|
||||
s->voices[i].midiNote = midiNote;
|
||||
s->voices[i].active = 1;
|
||||
s->voices[i].filter.low = 0.0f;
|
||||
s->voices[i].filter.band = 0.0f;
|
||||
envelopeNoteOn(&s->voices[i].ampEnv);
|
||||
envelopeNoteOn(&s->voices[i].modEnv);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// No free voice — steal round-robin
|
||||
// Steal round-robin
|
||||
int i = s->lastStolenVoice % VOICE_COUNT;
|
||||
s->lastStolenVoice++;
|
||||
s->voices[i].freqHz = hz;
|
||||
s->voices[i].midiNote = midiNote;
|
||||
s->voices[i].targetAmp = 1.0f;
|
||||
s->voices[i].phase = 0.0f;
|
||||
s->voices[i].active = 1;
|
||||
s->voices[i].freqHz = hz;
|
||||
s->voices[i].midiNote = midiNote;
|
||||
s->voices[i].active = 1;
|
||||
s->voices[i].filter.low = 0.0f;
|
||||
s->voices[i].filter.band = 0.0f;
|
||||
envelopeNoteOn(&s->voices[i].ampEnv);
|
||||
envelopeNoteOn(&s->voices[i].modEnv);
|
||||
}
|
||||
|
||||
void synthNoteOff(Synth *s, int midiNote)
|
||||
{
|
||||
|
||||
void synthNoteOff(Synth *s, int midiNote) {
|
||||
for (int i = 0; i < VOICE_COUNT; i++) {
|
||||
if (s->voices[i].active && s->voices[i].midiNote == midiNote)
|
||||
s->voices[i].targetAmp = 0.0f;
|
||||
if (s->voices[i].active && s->voices[i].midiNote == midiNote) {
|
||||
envelopeNoteOff(&s->voices[i].ampEnv);
|
||||
envelopeNoteOff(&s->voices[i].modEnv);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void synthFillBuffer(Synth *s, int16_t *out, int frames)
|
||||
{
|
||||
const float sr = s->sampleRate;
|
||||
const float attackInc = (s->attackSec <= 0.0f) ? 1.0f : (1.0f / (s->attackSec * sr));
|
||||
const float releaseInc = (s->releaseSec <= 0.0f) ? 1.0f : (1.0f / (s->releaseSec * sr));
|
||||
void synthFillBuffer(Synth *s, int16_t *out, int frames) {
|
||||
const float sr = s->sampleRate;
|
||||
const float bendMultiplier = powf(2.0f, (s->pitchBend * s->pitchBendRange) / 12.0f);
|
||||
|
||||
for (int i = 0; i < frames; i++) {
|
||||
float mix = 0.0f;
|
||||
|
||||
float lfo0 = lfoTick(&s->lfos[0], sr);
|
||||
float lfo1 = lfoTick(&s->lfos[1], sr);
|
||||
|
||||
for (int v = 0; v < VOICE_COUNT; v++) {
|
||||
Voice *vv = &s->voices[v];
|
||||
if (!vv->active) continue;
|
||||
|
||||
if (vv->targetAmp > vv->amp) {
|
||||
vv->amp += attackInc;
|
||||
if (vv->amp > vv->targetAmp) vv->amp = vv->targetAmp;
|
||||
} else if (vv->targetAmp < vv->amp) {
|
||||
vv->amp -= releaseInc;
|
||||
if (vv->amp < vv->targetAmp) vv->amp = vv->targetAmp;
|
||||
}
|
||||
float amp = envelopeTick(&vv->ampEnv, sr);
|
||||
float mod = envelopeTick(&vv->modEnv, sr);
|
||||
|
||||
if (vv->amp <= 0.0f && vv->targetAmp == 0.0f) {
|
||||
if (vv->ampEnv.stage == ENV_IDLE) {
|
||||
vv->active = 0;
|
||||
continue;
|
||||
}
|
||||
|
||||
//vv->phase += vv->freqHz / sr;
|
||||
float bendMultiplier = powf(2.0f, (s->pitchBend * s->pitchBendRange) / 12.0f);
|
||||
vv->phase += (vv->freqHz * bendMultiplier) / sr;
|
||||
if (vv->phase >= 1.0f) vv->phase -= 1.0f;
|
||||
float oscMix = 0.0f;
|
||||
int activeOscs = 0;
|
||||
for (int o = 0; o < OSC_COUNT; o++) {
|
||||
Oscillator *osc = &vv->oscillators[o];
|
||||
if (!osc->active) continue;
|
||||
|
||||
mix += waveformSample(s->waveform, vv->phase, s->dutyCycle) * vv->amp;
|
||||
float dutyCycle = osc->dutyCycle +
|
||||
getModValue(amp, mod, lfo0, lfo1, osc->modRouting[0]) * osc->modDepth[0];
|
||||
float detune = osc->detune + (float)osc->octave * 1200.0f +
|
||||
getModValue(amp, mod, lfo0, lfo1, osc->modRouting[1]) * osc->modDepth[1];
|
||||
float gain = osc->gain +
|
||||
getModValue(amp, mod, lfo0, lfo1, osc->modRouting[2]) * osc->modDepth[2];
|
||||
|
||||
if (dutyCycle < 0.05f) dutyCycle = 0.05f;
|
||||
if (dutyCycle > 0.95f) dutyCycle = 0.95f;
|
||||
if (gain < 0.0f) gain = 0.0f;
|
||||
if (gain > OSC_MAX_GAIN) gain = OSC_MAX_GAIN;
|
||||
|
||||
oscMix += oscillatorTick(osc, vv->freqHz, bendMultiplier, sr, dutyCycle, detune, gain);
|
||||
activeOscs++;
|
||||
}
|
||||
|
||||
if (activeOscs > 0) oscMix /= activeOscs;
|
||||
|
||||
float cutoff = vv->filter.cutoff +
|
||||
getModValue(amp, mod, lfo0, lfo1, vv->filter.modRouting) * vv->filter.modDepth;
|
||||
float resonance = vv->filter.resonance +
|
||||
getModValue(amp, mod, lfo0, lfo1, vv->filter.resModRouting) * vv->filter.resModDepth;
|
||||
oscMix = filterTick(&vv->filter, oscMix, cutoff, resonance, sr);
|
||||
|
||||
mix += oscMix * amp;
|
||||
}
|
||||
|
||||
mix *= (0.2f / VOICE_COUNT) * 4.0f;
|
||||
mix *= (0.2f / VOICE_COUNT) * 4.0f * s->volume;
|
||||
|
||||
int32_t sample = (int32_t)lrintf(mix * 32767.0f);
|
||||
if (sample > 32767) sample = 32767;
|
||||
@@ -137,3 +371,109 @@ void synthFillBuffer(Synth *s, int16_t *out, int frames)
|
||||
out[i] = (int16_t)sample;
|
||||
}
|
||||
}
|
||||
|
||||
void envelopeInit(Envelope *e, float attackSec, float decaySec, float sustainLevel, float releaseSec)
|
||||
{
|
||||
e->stage = ENV_IDLE;
|
||||
e->value = 0.0f;
|
||||
e->attackSec = attackSec;
|
||||
e->decaySec = decaySec;
|
||||
e->sustainLevel = sustainLevel;
|
||||
e->releaseSec = releaseSec;
|
||||
}
|
||||
|
||||
void envelopeNoteOn(Envelope *e)
|
||||
{
|
||||
e->value = 0.0f;
|
||||
e->stage = ENV_ATTACK;
|
||||
}
|
||||
|
||||
void envelopeNoteOff(Envelope *e)
|
||||
{
|
||||
// Only trigger release if we're actually playing
|
||||
if (e->stage != ENV_IDLE)
|
||||
e->stage = ENV_RELEASE;
|
||||
}
|
||||
|
||||
float envelopeTick(Envelope *e, float sampleRate)
|
||||
{
|
||||
switch (e->stage) {
|
||||
case ENV_ATTACK: {
|
||||
float inc = (e->attackSec <= 0.0f) ? 1.0f : (1.0f / (e->attackSec * sampleRate));
|
||||
e->value += inc;
|
||||
if (e->value >= 1.0f) {
|
||||
e->value = 1.0f;
|
||||
e->stage = ENV_DECAY;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ENV_DECAY: {
|
||||
float inc = (e->decaySec <= 0.0f) ? 1.0f : (1.0f / (e->decaySec * sampleRate));
|
||||
e->value -= inc;
|
||||
if (e->value <= e->sustainLevel) {
|
||||
e->value = e->sustainLevel;
|
||||
e->stage = ENV_SUSTAIN;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ENV_SUSTAIN:
|
||||
e->value = e->sustainLevel;
|
||||
break;
|
||||
case ENV_RELEASE: {
|
||||
float inc = (e->releaseSec <= 0.0f) ? 1.0f : (1.0f / (e->releaseSec * sampleRate));
|
||||
e->value -= inc;
|
||||
if (e->value <= 0.0f) {
|
||||
e->value = 0.0f;
|
||||
e->stage = ENV_IDLE;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ENV_IDLE:
|
||||
e->value = 0.0f;
|
||||
break;
|
||||
}
|
||||
return e->value;
|
||||
}
|
||||
|
||||
void synthSyncVoices(Synth *s)
|
||||
{
|
||||
for (int v = 1; v < VOICE_COUNT; v++) {
|
||||
// Sync oscillator settings
|
||||
for (int o = 0; o < OSC_COUNT; o++) {
|
||||
s->voices[v].oscillators[o].waveform = s->voices[0].oscillators[o].waveform;
|
||||
s->voices[v].oscillators[o].dutyCycle = s->voices[0].oscillators[o].dutyCycle;
|
||||
s->voices[v].oscillators[o].detune = s->voices[0].oscillators[o].detune;
|
||||
s->voices[v].oscillators[o].gain = s->voices[0].oscillators[o].gain;
|
||||
s->voices[v].oscillators[o].active = s->voices[0].oscillators[o].active;
|
||||
s->voices[v].oscillators[o].octave = s->voices[0].oscillators[o].octave;
|
||||
s->voices[v].oscillators[o].modRouting[0] = s->voices[0].oscillators[o].modRouting[0];
|
||||
s->voices[v].oscillators[o].modRouting[1] = s->voices[0].oscillators[o].modRouting[1];
|
||||
s->voices[v].oscillators[o].modRouting[2] = s->voices[0].oscillators[o].modRouting[2];
|
||||
s->voices[v].oscillators[o].modDepth[0] = s->voices[0].oscillators[o].modDepth[0];
|
||||
s->voices[v].oscillators[o].modDepth[1] = s->voices[0].oscillators[o].modDepth[1];
|
||||
s->voices[v].oscillators[o].modDepth[2] = s->voices[0].oscillators[o].modDepth[2];
|
||||
}
|
||||
|
||||
// Sync filter params but not state (low/band are per-voice)
|
||||
s->voices[v].filter.cutoff = s->voices[0].filter.cutoff;
|
||||
s->voices[v].filter.resonance = s->voices[0].filter.resonance;
|
||||
s->voices[v].filter.type = s->voices[0].filter.type;
|
||||
s->voices[v].filter.active = s->voices[0].filter.active;
|
||||
s->voices[v].filter.modRouting = s->voices[0].filter.modRouting;
|
||||
s->voices[v].filter.modDepth = s->voices[0].filter.modDepth;
|
||||
s->voices[v].filter.resModRouting = s->voices[0].filter.resModRouting;
|
||||
s->voices[v].filter.resModDepth = s->voices[0].filter.resModDepth;
|
||||
|
||||
// Sync envelope settings but NOT runtime state
|
||||
// Each voice needs its own stage, value — just copy the parameters
|
||||
s->voices[v].ampEnv.attackSec = s->voices[0].ampEnv.attackSec;
|
||||
s->voices[v].ampEnv.decaySec = s->voices[0].ampEnv.decaySec;
|
||||
s->voices[v].ampEnv.sustainLevel = s->voices[0].ampEnv.sustainLevel;
|
||||
s->voices[v].ampEnv.releaseSec = s->voices[0].ampEnv.releaseSec;
|
||||
// Sync the mod envelope, too.
|
||||
s->voices[v].modEnv.attackSec = s->voices[0].modEnv.attackSec;
|
||||
s->voices[v].modEnv.decaySec = s->voices[0].modEnv.decaySec;
|
||||
s->voices[v].modEnv.sustainLevel = s->voices[0].modEnv.sustainLevel;
|
||||
s->voices[v].modEnv.releaseSec = s->voices[0].modEnv.releaseSec;
|
||||
}
|
||||
}
|
||||
|
||||
1719
source/ui.c
Normal file
1719
source/ui.c
Normal file
File diff suppressed because it is too large
Load Diff
Reference in New Issue
Block a user