M .gitignore => .gitignore +1 -1
@@ 1,2 1,2 @@
-toy
+orca
cli=
\ No newline at end of file
M build.sh => build.sh +1 -1
@@ 16,7 16,7 @@ rm -f ./orca
# orca
-cc -std=c89 -DDEBUG -Wall -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined -L/usr/local/lib -lSDL2 orca.c sim.c -o orca
+cc -std=c89 -DDEBUG -Wall -Wpedantic -Wshadow -Wextra -Werror=implicit-int -Werror=incompatible-pointer-types -Werror=int-conversion -Wvla -g -Og -fsanitize=address -fsanitize=undefined -L/usr/local/lib -lSDL2 -lportmidi orca.c sim.c -o orca
# cc orca.c sim.c -std=c89 -O2 -DNDEBUG -g0 -s -Wall -L/usr/local/lib -lSDL2 -o orca
# Size
M orca.c => orca.c +114 -5
@@ 1,5 1,7 @@
-#include <SDL2/SDL.h>
#include <stdio.h>
+#include <SDL2/SDL.h>
+#include <portmidi.h>
+#include <porttime.h>
#include "sim.h"
/*
@@ 24,6 26,7 @@ WITH REGARD TO THIS SOFTWARE.
#define PLIMIT 256
#define SZ (HOR * VER * 16)
+#define DEVICE 0
typedef struct {
int x, y;
@@ 33,6 36,8 @@ typedef struct {
int x, y, w, h;
} Rect2d;
+char OCTAVE[] = {'C', 'c', 'D', 'd', 'E', 'F', 'f', 'G', 'g', 'A', 'a', 'B'};
+
unsigned char font[1200];
int colors[] = {color1, color2, color3, color4, color0};
int WIDTH = 8 * HOR + PAD * 2;
@@ 42,6 47,7 @@ SDL_Window *gWindow = NULL;
SDL_Renderer *gRenderer = NULL;
SDL_Texture *gTexture = NULL;
Uint32 *pixels;
+PmStream *midi;
Rect2d cursor;
Grid g;
@@ 189,13 195,102 @@ scale(int w, int h)
select(cursor.x, cursor.y, cursor.w + w, cursor.h + h);
}
+int
+getoffset(int o, char n)
+{
+ int i;
+ for(i = 0; i < 12; ++i)
+ if(n == OCTAVE[i])
+ return o * 12 + i;
+ return 0;
+}
+
+int
+getnote(int o, char n)
+{
+ int offset = 0;
+ switch(n) {
+ case 'A': offset = getoffset(0, 'A'); break;
+ case 'a': offset = getoffset(0, 'a'); break;
+ case 'B': offset = getoffset(0, 'B'); break;
+ case 'C': offset = getoffset(0, 'C'); break;
+ case 'c': offset = getoffset(0, 'c'); break;
+ case 'D': offset = getoffset(0, 'D'); break;
+ case 'd': offset = getoffset(0, 'd'); break;
+ case 'E': offset = getoffset(0, 'E'); break;
+ case 'F': offset = getoffset(0, 'F'); break;
+ case 'f': offset = getoffset(0, 'f'); break;
+ case 'G': offset = getoffset(0, 'G'); break;
+ case 'g': offset = getoffset(0, 'g'); break;
+ case 'H': offset = getoffset(0, 'A'); break;
+ case 'h': offset = getoffset(0, 'a'); break;
+ case 'I': offset = getoffset(0, 'B'); break;
+ case 'J': offset = getoffset(1, 'C'); break;
+ case 'j': offset = getoffset(1, 'c'); break;
+ case 'K': offset = getoffset(1, 'D'); break;
+ case 'k': offset = getoffset(1, 'd'); break;
+ case 'L': offset = getoffset(1, 'E'); break;
+ case 'M': offset = getoffset(1, 'F'); break;
+ case 'm': offset = getoffset(1, 'f'); break;
+ case 'N': offset = getoffset(1, 'G'); break;
+ case 'n': offset = getoffset(1, 'g'); break;
+ case 'O': offset = getoffset(1, 'A'); break;
+ case 'o': offset = getoffset(1, 'a'); break;
+ case 'P': offset = getoffset(1, 'B'); break;
+ case 'Q': offset = getoffset(2, 'C'); break;
+ case 'q': offset = getoffset(2, 'c'); break;
+ case 'R': offset = getoffset(2, 'D'); break;
+ case 'r': offset = getoffset(2, 'd'); break;
+ case 'S': offset = getoffset(2, 'E'); break;
+ case 'T': offset = getoffset(2, 'F'); break;
+ case 't': offset = getoffset(2, 'f'); break;
+ case 'U': offset = getoffset(2, 'G'); break;
+ case 'u': offset = getoffset(2, 'g'); break;
+ case 'V': offset = getoffset(2, 'A'); break;
+ case 'v': offset = getoffset(2, 'a'); break;
+ case 'W': offset = getoffset(2, 'B'); break;
+ case 'X': offset = getoffset(3, 'C'); break;
+ case 'x': offset = getoffset(3, 'c'); break;
+ case 'Y': offset = getoffset(3, 'D'); break;
+ case 'y': offset = getoffset(3, 'd'); break;
+ case 'Z': offset = getoffset(3, 'E'); break;
+ case 'e': offset = getoffset(0, 'F'); break;
+ case 'l': offset = getoffset(1, 'F'); break;
+ case 's': offset = getoffset(2, 'F'); break;
+ case 'z': offset = getoffset(3, 'F'); break;
+ case 'b': offset = getoffset(1, 'C'); break;
+ case 'i': offset = getoffset(1, 'C'); break;
+ case 'p': offset = getoffset(2, 'C'); break;
+ case 'w': offset = getoffset(3, 'C'); break;
+ }
+ return o + offset;
+}
+
+void
+playmidi(int channel, int octave, int note)
+{
+ Pm_WriteShort(midi,
+ Pt_Time(),
+ Pm_Message(0x90 + channel, (octave * 12) + note, 100));
+ Pm_WriteShort(midi,
+ Pt_Time(),
+ Pm_Message(0x90 + channel, (octave * 12) + note, 0));
+ printf("%d -> %d\n", channel, (octave * 12) + note);
+}
+
void
play(void)
{
int i;
- /* TODO: Implement midi stuff */
- for(i = 0; i < g.msg_len; ++i)
- printf("%c", g.msg[i]);
+ for(i = 0; i < g.msg_len; ++i) {
+ char c, o, n;
+ if(i < 1 || g.msg[i - 1] != ':')
+ continue;
+ if(sscanf(g.msg + 1, "%c%c%c", &c, &o, &n) > 0) {
+ playmidi(c - '0', o - '0', getnote(o - '0', n));
+ i += 3;
+ }
+ }
fflush(stdout);
}
@@ 242,6 337,7 @@ void
dokey(SDL_Event *event)
{
int shift = SDL_GetModState() & KMOD_LSHIFT || SDL_GetModState() & KMOD_RSHIFT;
+ int ctrl = SDL_GetModState() & KMOD_LCTRL || SDL_GetModState() & KMOD_RCTRL;
switch(event->key.keysym.sym) {
case SDLK_EQUALS:
case SDLK_PLUS: modzoom(1); break;
@@ 268,7 364,7 @@ dokey(SDL_Event *event)
case SDLK_7: insert('7'); break;
case SDLK_8: insert('8'); break;
case SDLK_9: insert('9'); break;
- case SDLK_a: insert(shift ? 'A' : 'a'); break;
+ case SDLK_a: ctrl ? select(0, 0, g.w, g.h) : insert(shift ? 'A' : 'a'); break;
case SDLK_b: insert(shift ? 'B' : 'b'); break;
case SDLK_c: insert(shift ? 'C' : 'c'); break;
case SDLK_d: insert(shift ? 'D' : 'd'); break;
@@ 297,6 393,18 @@ dokey(SDL_Event *event)
}
}
+void
+initmidi(void)
+{
+ int i;
+ Pm_Initialize();
+ for(i = 0; i < Pm_CountDevices(); ++i) {
+ char const *name = Pm_GetDeviceInfo(i)->name;
+ printf("Device #%d -> %s%s\n", i, name, i == DEVICE ? "[x]" : "[ ]");
+ }
+ Pm_OpenOutput(&midi, DEVICE, NULL, 128, 0, NULL, 1);
+}
+
int
init(void)
{
@@ 327,6 435,7 @@ init(void)
for(i = 0; i < HEIGHT; i++)
for(j = 0; j < WIDTH; j++)
pixels[i * WIDTH + j] = color1;
+ initmidi();
return 1;
}
D toy.c => toy.c +0 -400
@@ 1,400 0,0 @@
-#include <SDL2/SDL.h>
-#include <stdio.h>
-#include "sim.h"
-
-/*
-Copyright (c) 2020 Devine Lu Linvega
-
-Permission to use, copy, modify, and distribute this software for any
-purpose with or without fee is hereby granted, provided that the above
-copyright notice and this permission notice appear in all copies.
-
-THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
-WITH REGARD TO THIS SOFTWARE.
-*/
-
-#define HOR 32
-#define VER 16
-#define PAD 8
-#define ZOOM 2
-#define color1 0x000000
-#define color2 0x72DEC2
-#define color3 0xFFFFFF
-#define color4 0x444444
-#define color0 0xffb545
-
-#define PLIMIT 256
-#define SZ (HOR * VER * 16)
-
-typedef struct {
- int x, y;
-} Point2d;
-
-typedef struct {
- int x, y, w, h;
-} Rect2d;
-
-unsigned char font[1200];
-int colors[] = {color1, color2, color3, color4, color0};
-int WIDTH = 8 * HOR + PAD * 2;
-int HEIGHT = 8 * VER + PAD * 2;
-int FPS = 30, DOWN = 0;
-
-SDL_Window *gWindow = NULL;
-SDL_Renderer *gRenderer = NULL;
-SDL_Texture *gTexture = NULL;
-Uint32 *pixels;
-
-Rect2d cursor;
-Grid g;
-
-Point2d
-Pt2d(int x, int y)
-{
- Point2d p;
- p.x = x;
- p.y = y;
- return p;
-}
-
-int
-clamp(int val, int min, int max)
-{
- return (val >= min) ? (val <= max) ? val : max : min;
-}
-
-int
-selected(int x, int y)
-{
- return x < cursor.x + cursor.w && x >= cursor.x && y < cursor.y + cursor.h && y >= cursor.y;
-}
-
-void
-insert(char c)
-{
- int x, y;
- for(x = 0; x < cursor.w; ++x)
- for(y = 0; y < cursor.h; ++y)
- set(&g, cursor.x + x, cursor.y + y, c);
-}
-
-/* misc */
-
-int
-getfont(int x, int y, char c, int type, int sel)
-{
- if(c >= 'A' && c <= 'Z')
- return c - 'A' + 36;
- if(c >= 'a' && c <= 'z')
- return c - 'a' + 10;
- if(c >= '0' && c <= '9')
- return c - '0';
- if(c == '*')
- return 62;
- if(c == '#')
- return 63;
- if(c == ':')
- return 65;
- if(x % 8 == 0 && y % 8 == 0)
- return 68;
- if(cursor.x == x && cursor.y == y)
- return 66;
- if(sel || type || (x % 2 == 0 && y % 2 == 0))
- return 64;
- return 70;
-}
-
-int
-getstyle(int clr, int type, int sel)
-{
- if(sel)
- return colors[clr == 0 ? 4 : 0];
- if(type == 2)
- return colors[clr == 0 ? 0 : 1];
- if(type == 3)
- return colors[clr == 0 ? 1 : 0];
- if(type == 4)
- return colors[clr == 0 ? 0 : 2];
- if(type == 5)
- return colors[clr == 0 ? 2 : 0];
- return colors[clr == 0 ? 0 : 3];
-}
-
-void
-drawtile(Uint32 *dst, int x, int y, char c, int type)
-{
- int v, h;
- int sel = selected(x, y);
- int offset = getfont(x, y, c, type, sel) * 8 * 2;
- for(v = 0; v < 8; v++)
- for(h = 0; h < 8; h++) {
- int px = (x * 8) + (8 - h);
- int py = (y * 8) + v;
- int ch1 = font[offset + v];
- int ch2 = font[offset + v + 8];
- int clr = ((ch1 >> h) & 0x1) + ((ch2 << h) & 0x1);
- int key = (py + PAD) * WIDTH + (px + PAD);
- dst[key] = getstyle(clr, type, sel);
- }
-}
-
-void
-draw(Uint32 *dst)
-{
- int x, y;
- for(y = 0; y < VER; ++y)
- for(x = 0; x < HOR; ++x)
- drawtile(dst, x, y, get(&g, x, y), gettype(&g, x, y));
- SDL_UpdateTexture(gTexture, NULL, dst, WIDTH * sizeof(Uint32));
- SDL_RenderClear(gRenderer);
- SDL_RenderCopy(gRenderer, gTexture, NULL, NULL);
- SDL_RenderPresent(gRenderer);
-}
-
-/* etc */
-
-int
-error(char *msg, const char *err)
-{
- printf("Error %s: %s\n", msg, err);
- return 1;
-}
-
-void
-select(int x, int y, int w, int h)
-{
- cursor.x = clamp(x, 0, HOR - 1);
- cursor.y = clamp(y, 0, VER - 1);
- cursor.w = clamp(w, 1, 36);
- cursor.h = clamp(h, 1, 36);
- draw(pixels);
-}
-
-void
-move(int x, int y)
-{
- select(cursor.x + x, cursor.y + y, cursor.w, cursor.h);
-}
-
-void
-scale(int w, int h)
-{
- select(cursor.x, cursor.y, cursor.w + w, cursor.h + h);
-}
-
-void
-play(void)
-{
- int i;
- /* TODO: Implement midi stuff */
- for(i = 0; i < g.msg_len; ++i)
- printf("%c", g.msg[i]);
- fflush(stdout);
-}
-
-void
-quit(void)
-{
- free(pixels);
- SDL_DestroyTexture(gTexture);
- gTexture = NULL;
- SDL_DestroyRenderer(gRenderer);
- gRenderer = NULL;
- SDL_DestroyWindow(gWindow);
- gWindow = NULL;
- SDL_Quit();
- exit(0);
-}
-
-void
-domouse(SDL_Event *event)
-{
- Point2d touch = Pt2d(
- (event->motion.x - (PAD * ZOOM)) / ZOOM,
- (event->motion.y - (PAD * ZOOM)) / ZOOM);
- switch(event->type) {
- case SDL_MOUSEBUTTONUP:
- DOWN = 0;
- break;
- case SDL_MOUSEBUTTONDOWN:
- select(touch.x / 8, touch.y / 8, 1, 1);
- DOWN = 1;
- break;
- case SDL_MOUSEMOTION:
- if(DOWN)
- select(
- cursor.x,
- cursor.y,
- touch.x / 8 - cursor.x + 1,
- touch.y / 8 - cursor.y + 1);
- break;
- }
-}
-
-void
-dokey(SDL_Event *event)
-{
- int shift = SDL_GetModState() & KMOD_LSHIFT || SDL_GetModState() & KMOD_RSHIFT;
- switch(event->key.keysym.sym) {
- case SDLK_UP: shift ? scale(0, -1) : move(0, -1); break;
- case SDLK_DOWN: shift ? scale(0, 1) : move(0, 1); break;
- case SDLK_LEFT: shift ? scale(-1, 0) : move(-1, 0); break;
- case SDLK_RIGHT: shift ? scale(1, 0) : move(1, 0); break;
- case SDLK_BACKSPACE: insert('.'); break;
- case SDLK_ASTERISK: insert('*'); break;
- case SDLK_HASH: insert('#'); break;
- case SDLK_PERIOD: insert('.'); break;
- case SDLK_COLON: insert(':'); break;
- case SDLK_SEMICOLON: insert(':'); break;
- case SDLK_ESCAPE: select(cursor.x, cursor.y, 1, 1); break;
- case SDLK_0: insert('0'); break;
- case SDLK_1: insert('1'); break;
- case SDLK_2: insert('2'); break;
- case SDLK_3: insert(shift ? '#' : '3'); break;
- case SDLK_4: insert('4'); break;
- case SDLK_5: insert('5'); break;
- case SDLK_6: insert('6'); break;
- case SDLK_7: insert('7'); break;
- case SDLK_8: insert('8'); break;
- case SDLK_9: insert('9'); break;
- case SDLK_a: insert(shift ? 'A' : 'a'); break;
- case SDLK_b: insert(shift ? 'B' : 'b'); break;
- case SDLK_c: insert(shift ? 'C' : 'c'); break;
- case SDLK_d: insert(shift ? 'D' : 'd'); break;
- case SDLK_e: insert(shift ? 'E' : 'e'); break;
- case SDLK_f: insert(shift ? 'F' : 'f'); break;
- case SDLK_g: insert(shift ? 'G' : 'g'); break;
- case SDLK_h: insert(shift ? 'H' : 'h'); break;
- case SDLK_i: insert(shift ? 'I' : 'i'); break;
- case SDLK_j: insert(shift ? 'J' : 'j'); break;
- case SDLK_k: insert(shift ? 'K' : 'k'); break;
- case SDLK_l: insert(shift ? 'L' : 'l'); break;
- case SDLK_m: insert(shift ? 'M' : 'm'); break;
- case SDLK_n: insert(shift ? 'N' : 'n'); break;
- case SDLK_o: insert(shift ? 'O' : 'o'); break;
- case SDLK_p: insert(shift ? 'P' : 'p'); break;
- case SDLK_q: insert(shift ? 'Q' : 'q'); break;
- case SDLK_r: insert(shift ? 'R' : 'r'); break;
- case SDLK_s: insert(shift ? 'S' : 's'); break;
- case SDLK_t: insert(shift ? 'T' : 't'); break;
- case SDLK_u: insert(shift ? 'U' : 'u'); break;
- case SDLK_v: insert(shift ? 'V' : 'v'); break;
- case SDLK_w: insert(shift ? 'W' : 'w'); break;
- case SDLK_x: insert(shift ? 'X' : 'x'); break;
- case SDLK_y: insert(shift ? 'Y' : 'y'); break;
- case SDLK_z: insert(shift ? 'Z' : 'z'); break;
- }
-}
-
-int
-init(void)
-{
- int i, j;
- if(SDL_Init(SDL_INIT_VIDEO) < 0)
- return error("Init", SDL_GetError());
- gWindow = SDL_CreateWindow("Orca",
- SDL_WINDOWPOS_UNDEFINED,
- SDL_WINDOWPOS_UNDEFINED,
- WIDTH * ZOOM,
- HEIGHT * ZOOM,
- SDL_WINDOW_SHOWN);
- if(gWindow == NULL)
- return error("Window", SDL_GetError());
- gRenderer = SDL_CreateRenderer(gWindow, -1, 0);
- if(gRenderer == NULL)
- return error("Renderer", SDL_GetError());
- gTexture = SDL_CreateTexture(gRenderer,
- SDL_PIXELFORMAT_ARGB8888,
- SDL_TEXTUREACCESS_STATIC,
- WIDTH,
- HEIGHT);
- if(gTexture == NULL)
- return error("Texture", SDL_GetError());
- pixels = (Uint32 *)malloc(WIDTH * HEIGHT * sizeof(Uint32));
- if(pixels == NULL)
- return error("Pixels", "Failed to allocate memory");
- for(i = 0; i < HEIGHT; i++)
- for(j = 0; j < WIDTH; j++)
- pixels[i * WIDTH + j] = color1;
- return 1;
-}
-
-int
-loadfont(void)
-{
- FILE *f = fopen("font-light.chr", "rb");
- if(f == NULL)
- return error("Font", "Invalid font file");
- if(!fread(font, sizeof(font), 1, f))
- return error("Font", "Invalid font size");
- fclose(f);
- return 1;
-}
-
-int
-loadorca(FILE *f)
-{
- char c;
- if(!f)
- return error("Load", "Invalid input file");
- while((c = fgetc(f)) != EOF) {
- if(c == '\n') {
- cursor.x = 0;
- cursor.y += 1;
- } else
- set(&g, cursor.x++, cursor.y, c);
- }
- return 1;
-}
-
-int
-main(int argc, char *argv[])
-{
- int ticknext = 0, tickrun = 0;
-
- if(!init())
- return error("Init", "Failure");
- if(!loadfont())
- return error("Font", "Failure");
-
- create(&g, HOR, VER);
- select(0, 0, 1, 1);
-
- if(argc > 0)
- if(!loadorca(fopen(argv[1], "r")))
- return error("Load", "Failure");
-
- select(0, 0, 1, 1);
-
- while(1) {
- int tick = SDL_GetTicks();
- SDL_Event event;
- if(tick < ticknext)
- SDL_Delay(ticknext - tick);
- ticknext = tick + (1000 / FPS);
-
- if(tickrun == 8) {
- run(&g);
- play();
- draw(pixels);
- tickrun = 0;
- }
- tickrun++;
-
- while(SDL_PollEvent(&event) != 0) {
- if(event.type == SDL_QUIT)
- quit();
- else if(event.type == SDL_MOUSEBUTTONUP ||
- event.type == SDL_MOUSEBUTTONDOWN ||
- event.type == SDL_MOUSEMOTION) {
- domouse(&event);
- } else if(event.type == SDL_KEYDOWN)
- dokey(&event);
- else if(event.type == SDL_WINDOWEVENT)
- if(event.window.event == SDL_WINDOWEVENT_EXPOSED)
- draw(pixels);
- }
- }
- quit();
- return 0;
-}