XXIIVV
Orca x Dotgrid picture
13A12 — Orca x Dotgrid

Orca is a livecoding playground.

Orca is a two-dimensional esoteric programming language in which every letter of the alphabet is an operator, where lowercase letters operate on bang, uppercase letters operate each frame. This livecoding language is designed to procedurally generate MIDI, UDP or OSC messages.

If you wish to learn more about Orca, watch a tutorial, visit the chatroom or ask your questions in the forum.

Orca is a wildly unique visual programming tool. It's also an inky black and seafoam green alphabet soup, pulsating to some species of broody electronic industrial throb.
—Ivan Reese, The Future Of Coding

Operators

Base 36 Table

Orca operates on a base of 36 increments. Operators using numeric values will typically also operate on letters and convert them into values as per the following table. For instance Do will bang every 24th frame.

0123456789AB
01234567891011
CDEFGHIJKLMN
121314151617181920212223
OPQRSTUVWXYZ
242526272829303132333435

Tutorial

Basics

This section will teach the basics of playing a note and a sequence of notes.

Send a midi note

D8...
.:03C

Play a random note

D8.aRG.
.:03D..

Make a melody

D814TCAFE
.:03A....

Play the melody

.8C4.....
D804TCAFE
.:03C....

Logic

This section will teach the basics of automating logic decisions and changing the values of operators dynamically.

Play every second note

.2I6.......
D646TCAFEDG
.:03D......

Play a note with an offset

D8.1AC.
.:03D..

Play a sequence back and forth

4C6......
.4B3.....
D414TCAFE
.:03A....

Play a note at a specific interval

I4.....
3F2.1AC
..:03D.

Projectors

This section will teach the basics of creating new operators procedurally.

Halt a moving operator

..H
E..

Read an operator at position

22O...
..E..H
.....E

Write an operator at position

22XE.
.....
.....
....E

Animate a projector

C...........
.B4.........
.1XE........
........:03C
........:03D
........:03E
........:03F
........:03G

Variables

This section will teach the basics of storing accessing and combining that stored data.

Write a variable

aV5

Read a variable

.....Va
.......
aV5..Va
.....5.
.......
aV6..Va
.....6.

Read 3 variables

iV0.oV3.nVC
...........
3Kion......
.:03C......

Carry a value horizontally and vertically

3..
J..
3Y3

Carry a bang

D43Ka...
.Y.:03C.

I hope this workshop has been enlightening, if you have questions or suggestions, please visit the forum, or the chatroom. Enjoy!

Golf

Here are a few interesting snippets to achieve various arithmetic operations.

1X..
6I4.
Modulo
Will output the modulo of 6 % 4.
cA1.
.dAZ
Uppercase
Will output uppercase C.
H...
CM1.
Lowercase
Will output lowercase C.
.L0.
..F0
Not Null
Will bang if L free input is not null.
— Submit an edit to orca_guide.htm(288 lines)

orca.c

To learn more, see the complete manual in the repository.

cc -std=c89 -O2 -DNDEBUG -g0 -s -Wall -L/usr/local/lib -lSDL2 -lportmidi orca.c -o orca

The following code is a single-file implementation written ANSI C with SDL2 and portmidi as only dependecies.

#include <stdio.h>
#include <SDL2/SDL.h>
#include <portmidi.h>
#include <porttime.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 2
#define VOICES 16
#define DEVICE 0

#define SZ (HOR * VER * 16)
#define CLIPSZ (HOR * VER) + VER + 1
#define MAXSZ (HOR * VER)

typedef unsigned char Uint8;

typedef struct Grid {
	int w, h, l, f, r;
	Uint8 var[36], data[MAXSZ], lock[MAXSZ], type[MAXSZ];
} Grid;

typedef struct {
	int unsaved;
	char name[256];
	Grid grid;
} Document;

typedef struct {
	int x, y, w, h;
} Rect2d;

typedef struct {
	int chn, val, vel, len;
} MidiNote;

Document doc;
char clip[CLIPSZ];
MidiNote voices[VOICES];
Rect2d cursor;

int WIDTH = 8 * HOR + PAD * 8 * 2;
int HEIGHT = 8 * (VER + 2) + PAD * 8 * 2;
int BPM = 128, DOWN = 0, ZOOM = 2, PAUSE = 0, GUIDES = 1, MODE = 0;

Uint32 theme[] = {
	0x000000,
	0xFFFFFF,
	0x72DEC2,
	0x666666,
	0xffb545};

Uint8 icons[][8] = {
	{0x00, 0x00, 0x10, 0x38, 0x7c, 0x38, 0x10, 0x00}, /* play */
	{0x00, 0x00, 0x48, 0x24, 0x12, 0x24, 0x48, 0x00}, /* next */
	{0x00, 0x00, 0x66, 0x42, 0x00, 0x42, 0x66, 0x00}, /* skip */
	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x00}, /* midi:1 */
	{0x00, 0x00, 0x00, 0x00, 0x00, 0x3e, 0x3e, 0x00}, /* midi:2 */
	{0x00, 0x00, 0x00, 0x00, 0x3e, 0x3e, 0x3e, 0x00}, /* midi:3 */
	{0x00, 0x00, 0x00, 0x3e, 0x3e, 0x3e, 0x3e, 0x00}, /* midi:4 */
	{0x00, 0x00, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x00}, /* midi:5 */
	{0x00, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x3e, 0x00}, /* midi:6 */
	{0x00, 0x00, 0x00, 0x82, 0x44, 0x38, 0x00, 0x00}, /* eye open */
	{0x00, 0x38, 0x44, 0x92, 0x28, 0x10, 0x00, 0x00}, /* eye closed */
	{0x10, 0x54, 0x28, 0xc6, 0x28, 0x54, 0x10, 0x00}  /* unsaved */
};

Uint8 font[][8] = {
	{0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x3c, 0x00},
	{0x00, 0x00, 0x30, 0x10, 0x10, 0x10, 0x10, 0x00},
	{0x00, 0x00, 0x7c, 0x02, 0x3c, 0x40, 0x7e, 0x00},
	{0x00, 0x00, 0x7c, 0x02, 0x7c, 0x02, 0x7c, 0x00},
	{0x00, 0x00, 0x12, 0x22, 0x42, 0x7e, 0x02, 0x00},
	{0x00, 0x00, 0x7e, 0x40, 0x3c, 0x02, 0x7e, 0x00},
	{0x00, 0x00, 0x3e, 0x40, 0x7c, 0x42, 0x3c, 0x00},
	{0x00, 0x00, 0x7e, 0x02, 0x04, 0x08, 0x10, 0x00},
	{0x00, 0x00, 0x7e, 0x42, 0x3c, 0x42, 0x7e, 0x00},
	{0x00, 0x00, 0x7e, 0x42, 0x3e, 0x02, 0x02, 0x00},
	{0x00, 0x00, 0x7c, 0x02, 0x3e, 0x42, 0x7a, 0x00},
	{0x00, 0x00, 0x40, 0x40, 0x7c, 0x42, 0x7c, 0x00},
	{0x00, 0x00, 0x00, 0x3e, 0x40, 0x40, 0x3e, 0x00},
	{0x00, 0x00, 0x02, 0x02, 0x3e, 0x42, 0x3e, 0x00},
	{0x00, 0x00, 0x3c, 0x42, 0x7c, 0x40, 0x3e, 0x00},
	{0x00, 0x00, 0x3c, 0x42, 0x70, 0x40, 0x40, 0x00},
	{0x00, 0x00, 0x3e, 0x42, 0x3e, 0x02, 0x7c, 0x00},
	{0x00, 0x00, 0x40, 0x40, 0x7c, 0x42, 0x42, 0x00},
	{0x00, 0x00, 0x10, 0x00, 0x10, 0x10, 0x10, 0x00},
	{0x00, 0x00, 0x7e, 0x04, 0x04, 0x44, 0x38, 0x00},
	{0x00, 0x00, 0x42, 0x44, 0x78, 0x44, 0x42, 0x00},
	{0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x3e, 0x00},
	{0x00, 0x00, 0x6c, 0x52, 0x52, 0x52, 0x52, 0x00},
	{0x00, 0x00, 0x5c, 0x62, 0x42, 0x42, 0x42, 0x00},
	{0x00, 0x00, 0x1c, 0x22, 0x42, 0x44, 0x38, 0x00},
	{0x00, 0x00, 0x7c, 0x42, 0x7c, 0x40, 0x40, 0x00},
	{0x00, 0x00, 0x3e, 0x42, 0x3e, 0x02, 0x02, 0x00},
	{0x00, 0x00, 0x5c, 0x62, 0x40, 0x40, 0x40, 0x00},
	{0x00, 0x00, 0x3e, 0x40, 0x3c, 0x02, 0x7c, 0x00},
	{0x00, 0x00, 0x7e, 0x10, 0x10, 0x10, 0x08, 0x00},
	{0x00, 0x00, 0x42, 0x42, 0x42, 0x46, 0x3a, 0x00},
	{0x00, 0x00, 0x42, 0x42, 0x24, 0x24, 0x18, 0x00},
	{0x00, 0x00, 0x42, 0x42, 0x52, 0x52, 0x6c, 0x00},
	{0x00, 0x00, 0x42, 0x42, 0x3c, 0x42, 0x42, 0x00},
	{0x00, 0x00, 0x42, 0x42, 0x3e, 0x02, 0x7c, 0x00},
	{0x00, 0x00, 0x7e, 0x04, 0x18, 0x20, 0x7e, 0x00},
	{0x00, 0x00, 0x3c, 0x42, 0x7e, 0x42, 0x42, 0x00},
	{0x00, 0x00, 0x7c, 0x42, 0x7c, 0x42, 0x7c, 0x00},
	{0x00, 0x00, 0x3e, 0x40, 0x40, 0x40, 0x3e, 0x00},
	{0x00, 0x00, 0x7c, 0x42, 0x42, 0x42, 0x7c, 0x00},
	{0x00, 0x00, 0x7e, 0x40, 0x7e, 0x40, 0x7e, 0x00},
	{0x00, 0x00, 0x7e, 0x40, 0x70, 0x40, 0x40, 0x00},
	{0x00, 0x00, 0x3e, 0x40, 0x5c, 0x42, 0x3e, 0x00},
	{0x00, 0x00, 0x42, 0x42, 0x7e, 0x42, 0x42, 0x00},
	{0x00, 0x00, 0x10, 0x10, 0x10, 0x10, 0x10, 0x00},
	{0x00, 0x00, 0x7e, 0x02, 0x02, 0x42, 0x3c, 0x00},
	{0x00, 0x00, 0x46, 0x48, 0x70, 0x48, 0x46, 0x00},
	{0x00, 0x00, 0x40, 0x40, 0x40, 0x40, 0x7e, 0x00},
	{0x00, 0x00, 0x6e, 0x52, 0x52, 0x52, 0x52, 0x00},
	{0x00, 0x00, 0x62, 0x52, 0x4a, 0x46, 0x42, 0x00},
	{0x00, 0x00, 0x3c, 0x42, 0x42, 0x42, 0x3c, 0x00},
	{0x00, 0x00, 0x7e, 0x42, 0x7c, 0x40, 0x40, 0x00},
	{0x00, 0x00, 0x3c, 0x42, 0x4a, 0x44, 0x3a, 0x00},
	{0x00, 0x00, 0x7e, 0x42, 0x7c, 0x42, 0x42, 0x00},
	{0x00, 0x00, 0x3e, 0x40, 0x7e, 0x02, 0x7c, 0x00},
	{0x00, 0x00, 0x7e, 0x10, 0x10, 0x10, 0x10, 0x00},
	{0x00, 0x00, 0x42, 0x42, 0x42, 0x42, 0x3c, 0x00},
	{0x00, 0x00, 0x42, 0x42, 0x42, 0x24, 0x18, 0x00},
	{0x00, 0x00, 0x52, 0x52, 0x52, 0x52, 0x6e, 0x00},
	{0x00, 0x00, 0x42, 0x24, 0x18, 0x24, 0x42, 0x00},
	{0x00, 0x00, 0x42, 0x24, 0x10, 0x10, 0x10, 0x00},
	{0x00, 0x00, 0x7e, 0x02, 0x3c, 0x40, 0x7e, 0x00},
	{0x00, 0x00, 0x5a, 0x24, 0x42, 0x24, 0x5a, 0x00},
	{0x00, 0x00, 0x24, 0x7e, 0x24, 0x7e, 0x24, 0x00},
	{0x00, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00},
	{0x00, 0x00, 0x00, 0x10, 0x00, 0x10, 0x00, 0x00},
	{0x00, 0x00, 0x66, 0x5a, 0x24, 0x5a, 0x66, 0x00},
	{0x00, 0x00, 0x00, 0x32, 0x42, 0x4c, 0x00, 0x00},
	{0x00, 0x00, 0x00, 0x28, 0x00, 0x28, 0x00, 0x00},
	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00},
	{0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}};

SDL_Window *gWindow = NULL;
SDL_Renderer *gRenderer = NULL;
SDL_Texture *gTexture = NULL;
Uint32 *pixels;
PmStream *midi;

#pragma mark - HELPERS

int
clmp(int val, int min, int max)
{
	return (val >= min) ? (val <= max) ? val : max : min;
}

char *
scpy(char *src, char *dst, int len)
{
	int i = 0;
	while((dst[i] = src[i]) && i < len - 2)
		i++;
	dst[i + 1] = '\0';
	return dst;
}

char
cchr(int v, char c)
{
	v = abs(v % 36);
	if(v >= 0 && v <= 9)
		return '0' + v;
	return (c >= 'A' && c <= 'Z' ? 'A' : 'a') + v - 10;
}

int
cb36(char c)
{
	if(c >= 'A' && c <= 'Z')
		return c - 'A' + 10;
	if(c >= 'a' && c <= 'z')
		return c - 'a' + 10;
	if(c >= '0' && c <= '9')
		return c - '0';
	return 0;
}

int
validposition(Grid *g, int x, int y)
{
	return x >= 0 && x <= g->w - 1 && y >= 0 && y <= g->h - 1;
}

int
validcharacter(char c)
{
	if(cb36(c) || c == '0')
		return 1;
	if(c == '.' || c == ':' || c == '#' || c == '*')
		return 1;
	return 0;
}

int
ctbl(char c)
{
	int sharp, uc, deg, notes[] = {0, 2, 4, 5, 7, 9, 11};
	if(c >= '0' && c <= '9')
		return c - '0';
	sharp = c >= 'a' && c <= 'z';
	uc = sharp ? c - 'a' + 'A' : c;
	deg = uc <= 'B' ? 'G' - 'B' + uc - 'A' : uc - 'C';
	return deg / 7 * 12 + notes[deg % 7] + sharp;
}

#pragma mark - IO

char
getcell(Grid *g, int x, int y)
{
	if(validposition(g, x, y))
		return g->data[x + (y * g->w)];
	return '.';
}

void
setcell(Grid *g, int x, int y, char c)
{
	if(validposition(g, x, y) && validcharacter(c))
		g->data[x + (y * g->w)] = c;
}

int
gettype(Grid *g, int x, int y)
{
	if(validposition(g, x, y))
		return g->type[x + (y * g->w)];
	return 0;
}

void
settype(Grid *g, int x, int y, int t)
{
	if(validposition(g, x, y))
		g->type[x + (y * g->w)] = t;
}

void
setlock(Grid *g, int x, int y)
{
	if(validposition(g, x, y)) {
		g->lock[x + (y * g->w)] = 1;
		if(!gettype(g, x, y))
			settype(g, x, y, 1);
	}
}

void
setport(Grid *g, int x, int y, char c)
{
	setlock(g, x, y);
	settype(g, x, y, 5);
	setcell(g, x, y, c);
}

int
getport(Grid *g, int x, int y, int l)
{
	if(l) {
		setlock(g, x, y);
		settype(g, x, y, 4);
	} else
		settype(g, x, y, 2);
	return getcell(g, x, y);
}

int
getbang(Grid *g, int x, int y)
{
	return getcell(g, x - 1, y) == '*' || getcell(g, x + 1, y) == '*' || getcell(g, x, y - 1) == '*' || getcell(g, x, y + 1) == '*';
}

#pragma mark - MIDI

MidiNote *
sendmidi(int chn, int val, int vel, int len)
{
	int i = 0;
	/* Detrigger */
	for(i = 0; i < VOICES; ++i) {
		MidiNote *n = &voices[i];
		if(!n->len || n->chn != chn || n->val != val)
			continue;
		Pm_WriteShort(midi,
			Pt_Time(),
			Pm_Message(0x90 + n->chn, n->val, 0));
		n->len = 0;
	}
	/* Trigger */
	for(i = 0; i < VOICES; ++i) {
		MidiNote *n = &voices[i];
		if(n->len < 1) {
			n->chn = chn;
			n->val = val;
			n->vel = vel;
			n->len = len;
			Pm_WriteShort(midi,
				Pt_Time(),
				Pm_Message(0x90 + chn, val, vel * 3));
			return n;
		}
	}
	return NULL;
}

void
runmidi(void)
{
	int i;
	for(i = 0; i < VOICES; ++i) {
		MidiNote *n = &voices[i];
		if(n->len > 0) {
			n->len--;
			if(n->len == 0)
				Pm_WriteShort(midi,
					Pt_Time(),
					Pm_Message(0x90 + n->chn, n->val, 0));
		}
	}
}

void
initmidi(void)
{
	int i;
	Pm_Initialize();
	for(i = 0; i < Pm_CountDevices(); ++i)
		printf("Device #%d -> %s%s\n",
			i,
			Pm_GetDeviceInfo(i)->name,
			i == DEVICE ? "[x]" : "[ ]");
	Pm_OpenOutput(&midi, DEVICE, NULL, 128, 0, NULL, 1);
}

#pragma mark - LIBRARY

void
opa(Grid *g, int x, int y, char c)
{
	char a = getport(g, x - 1, y, 0);
	char b = getport(g, x + 1, y, 1);
	setport(g, x, y + 1, cchr(cb36(a) + cb36(b), b));
	(void)c;
}

void
opb(Grid *g, int x, int y, char c)
{
	char a = getport(g, x - 1, y, 0);
	char b = getport(g, x + 1, y, 1);
	setport(g, x, y + 1, cchr(cb36(a) - cb36(b), b));
	(void)c;
}

void
opc(Grid *g, int x, int y, char c)
{
	char rate = getport(g, x - 1, y, 0);
	char mod = getport(g, x + 1, y, 1);
	int mod_ = cb36(mod);
	int rate_ = cb36(rate);
	if(!rate_)
		rate_ = 1;
	if(!mod_)
		mod_ = 8;
	setport(g, x, y + 1, cchr(g->f / rate_ % mod_, mod));
	(void)c;
}

void
opd(Grid *g, int x, int y, char c)
{
	char rate = getport(g, x - 1, y, 0);
	char mod = getport(g, x + 1, y, 1);
	int rate_ = cb36(rate);
	int mod_ = cb36(mod);
	if(!rate_)
		rate_ = 1;
	if(!mod_)
		mod_ = 8;
	setport(g, x, y + 1, g->f % (rate_ * mod_) == 0 ? '*' : '.');
	(void)c;
}

void
ope(Grid *g, int x, int y, char c)
{
	if(x >= g->h - 1 || getcell(g, x + 1, y) != '.')
		setcell(g, x, y, '*');
	else {
		setcell(g, x, y, '.');
		setport(g, x + 1, y, c);
		settype(g, x + 1, y, 0);
	}
	settype(g, x, y, 0);
}

void
opf(Grid *g, int x, int y, char c)
{
	char a = getport(g, x - 1, y, 0);
	char b = getport(g, x + 1, y, 1);
	setport(g, x, y + 1, a == b ? '*' : '.');
	(void)c;
}

void
opg(Grid *g, int x, int y, char c)
{
	char px = getport(g, x - 3, y, 0);
	char py = getport(g, x - 2, y, 0);
	char len = getport(g, x - 1, y, 0);
	int i, len_ = cb36(len);
	if(!len_)
		len_ = 1;
	for(i = 0; i < len_; ++i)
		setport(g, x + i + cb36(px), y + 1 + cb36(py), getport(g, x + 1 + i, y, 1));
	(void)c;
}

void
oph(Grid *g, int x, int y, char c)
{
	getport(g, x, y + 1, 1);
	(void)c;
}

void
opi(Grid *g, int x, int y, char c)
{
	char rate = getport(g, x - 1, y, 0);
	char mod = getport(g, x + 1, y, 1);
	char val = getport(g, x, y + 1, 1);
	int rate_ = cb36(rate);
	int mod_ = cb36(mod);
	if(!rate_)
		rate_ = 1;
	if(!mod_)
		mod_ = 36;
	setport(g, x, y + 1, cchr((cb36(val) + rate_) % mod_, mod));
	(void)c;
}

void
opj(Grid *g, int x, int y, char c)
{
	char link = getport(g, x, y - 1, 0);
	int i;
	if(link != c) {
		for(i = 1; y + i < 256; ++i)
			if(getcell(g, x, y + i) != c)
				break;
		setport(g, x, y + i, link);
	}
}

void
opk(Grid *g, int x, int y, char c)
{
	char len = getport(g, x - 1, y, 0);
	int i, len_ = cb36(len);
	if(!len_)
		len_ = 1;
	for(i = 0; i < len_; ++i) {
		char key = getport(g, x + 1 + i, y, 1);
		if(key != '.')
			setport(g, x + 1 + i, y + 1, g->var[cb36(key)]);
	}
	(void)c;
}

void
opl(Grid *g, int x, int y, char c)
{
	char a = getport(g, x - 1, y, 0);
	char b = getport(g, x + 1, y, 1);
	setport(g, x, y + 1, cb36(a) < cb36(b) ? a : b);
	(void)c;
}

void
opm(Grid *g, int x, int y, char c)
{
	char a = getport(g, x - 1, y, 0);
	char b = getport(g, x + 1, y, 1);
	setport(g, x, y + 1, cchr(cb36(a) * cb36(b), b));
	(void)c;
}

void
opn(Grid *g, int x, int y, char c)
{
	if(y <= 0 || getcell(g, x, y - 1) != '.')
		setcell(g, x, y, '*');
	else {
		setcell(g, x, y, '.');
		setport(g, x, y - 1, c);
		settype(g, x, y - 1, 0);
	}
	settype(g, x, y, 0);
}

void
opo(Grid *g, int x, int y, char c)
{
	char px = getport(g, x - 2, y, 0);
	char py = getport(g, x - 1, y, 0);
	setport(g, x, y + 1, getport(g, x + 1 + cb36(px), y + cb36(py), 1));
	(void)c;
}

void
opp(Grid *g, int x, int y, char c)
{
	char key = getport(g, x - 2, y, 0);
	char len = getport(g, x - 1, y, 0);
	char val = getport(g, x + 1, y, 1);
	int i, len_ = cb36(len);
	if(!len_)
		len_ = 1;
	for(i = 0; i < len_; ++i)
		setlock(g, x + i, y + 1);
	setport(g, x + (cb36(key) % len_), y + 1, val);
	(void)c;
}

void
opq(Grid *g, int x, int y, char c)
{
	char px = getport(g, x - 3, y, 0);
	char py = getport(g, x - 2, y, 0);
	char len = getport(g, x - 1, y, 0);
	int i, len_ = cb36(len);
	if(!len_)
		len_ = 1;
	for(i = 0; i < len_; ++i)
		setport(g, x + 1 - len_ + i, y + 1, getport(g, x + 1 + cb36(px) + i, y + cb36(py), 1));
	(void)c;
}

void
opr(Grid *g, int x, int y, char c)
{
	char min = getport(g, x - 1, y, 0);
	char max = getport(g, x + 1, y, 1);
	int min_ = cb36(min);
	int max_ = cb36(max);
	unsigned int key = (g->r + y * g->w + x) ^ (g->f << 16);
	if(!max_)
		max_ = 36;
	if(min_ == max_)
		min_ = max_ - 1;
	key = (key ^ 61U) ^ (key >> 16);
	key = key + (key << 3);
	key = key ^ (key >> 4);
	key = key * 0x27d4eb2d;
	key = key ^ (key >> 15);
	setport(g, x, y + 1, cchr(key % (max_ - min_) + min_, max));
	(void)c;
}

void
ops(Grid *g, int x, int y, char c)
{
	if(y >= g->h - 1 || getcell(g, x, y + 1) != '.')
		setcell(g, x, y, '*');
	else {
		setcell(g, x, y, '.');
		setport(g, x, y + 1, c);
		settype(g, x, y + 1, 0);
	}
	settype(g, x, y, 0);
}

void
opt(Grid *g, int x, int y, char c)
{
	char key = getport(g, x - 2, y, 0);
	char len = getport(g, x - 1, y, 0);
	int i, len_ = cb36(len);
	if(!len_)
		len_ = 1;
	for(i = 0; i < len_; ++i)
		setlock(g, x + 1 + i, y);
	setport(g, x, y + 1, getport(g, x + 1 + (cb36(key) % len_), y, 1));
	(void)c;
}

void
opu(Grid *g, int x, int y, char c)
{
	char step = getport(g, x - 1, y, 1);
	char max = getport(g, x + 1, y, 1);
	int step_ = cb36(step);
	int max_ = cb36(max);
	int bucket;
	if(!step_)
		step_ = 1;
	if(!max_)
		max_ = 8;
	bucket = (step_ * (g->f + max_ - 1)) % max_ + step_;
	setport(g, x, y + 1, bucket >= max_ ? '*' : '.');
	(void)c;
}

void
opv(Grid *g, int x, int y, char c)
{
	char w = getport(g, x - 1, y, 0);
	char r = getport(g, x + 1, y, 1);
	if(w != '.')
		g->var[cb36(w)] = r;
	else if(w == '.' && r != '.')
		setport(g, x, y + 1, g->var[cb36(r)]);
	(void)c;
}

void
opw(Grid *g, int x, int y, char c)
{
	if(x <= 0 || getcell(g, x - 1, y) != '.')
		setcell(g, x, y, '*');
	else {
		setcell(g, x, y, '.');
		setport(g, x - 1, y, c);
		settype(g, x - 1, y, 0);
	}
	settype(g, x, y, 0);
}

void
opx(Grid *g, int x, int y, char c)
{
	char px = getport(g, x - 2, y, 0);
	char py = getport(g, x - 1, y, 0);
	char val = getport(g, x + 1, y, 1);
	setport(g, x + cb36(px), y + cb36(py) + 1, val);
	(void)c;
}

void
opy(Grid *g, int x, int y, char c)
{
	char link = getport(g, x - 1, y, 0);
	int i;
	if(link != c) {
		for(i = 1; x + i < 256; ++i)
			if(getcell(g, x + i, y) != c)
				break;
		setport(g, x + i, y, link);
	}
}

void
opz(Grid *g, int x, int y, char c)
{
	char rate = getport(g, x - 1, y, 0);
	char target = getport(g, x + 1, y, 1);
	char val = getport(g, x, y + 1, 1);
	int rate_ = cb36(rate);
	int target_ = cb36(target);
	int val_ = cb36(val);
	int mod;
	if(!rate_)
		rate_ = 1;
	if(val_ <= target_ - rate_)
		mod = rate_;
	else if(val_ >= target_ + rate_)
		mod = -rate;
	else
		mod = target_ - val_;
	setport(g, x, y + 1, cchr(val_ + mod, target));
	(void)c;
}

void
opcomment(Grid *g, int x, int y)
{
	int i;
	for(i = 1; x + i < 256; ++i) {
		setlock(g, x + i, y);
		if(getcell(g, x + i, y) == '#')
			break;
	}
	settype(g, x, y, 1);
}

void
opspecial(Grid *g, int x, int y)
{
	int chn, oct, nte, vel, len;
	if(getport(g, x, y, 1) != ':')
		return;
	chn = cb36(getport(g, x + 1, y, 1));
	if(chn == '.')
		return;
	oct = cb36(getport(g, x + 2, y, 1));
	if(oct == '.')
		return;
	nte = getport(g, x + 3, y, 1);
	if(nte == '.')
		return;
	vel = getport(g, x + 4, y, 1);
	if(vel == '.')
		vel = 'z';
	len = getport(g, x + 5, y, 1);
	if(getbang(g, x, y)) {
		sendmidi(
			clmp(chn, 0, 16),
			12 * oct + ctbl(nte),
			clmp(cb36(vel), 0, 36),
			clmp(cb36(len), 1, 36));
		settype(g, x, y, 3);
	}
}

void
operate(Grid *g, int x, int y, char c)
{
	settype(g, x, y, 3);
	switch(c >= 'A' && c <= 'Z' ? c + ('a' - 'A') : c) {
	case 'a': opa(g, x, y, c); break;
	case 'b': opb(g, x, y, c); break;
	case 'c': opc(g, x, y, c); break;
	case 'd': opd(g, x, y, c); break;
	case 'e': ope(g, x, y, c); break;
	case 'f': opf(g, x, y, c); break;
	case 'g': opg(g, x, y, c); break;
	case 'h': oph(g, x, y, c); break;
	case 'i': opi(g, x, y, c); break;
	case 'k': opk(g, x, y, c); break;
	case 'j': opj(g, x, y, c); break;
	case 'l': opl(g, x, y, c); break;
	case 'm': opm(g, x, y, c); break;
	case 'n': opn(g, x, y, c); break;
	case 'o': opo(g, x, y, c); break;
	case 'p': opp(g, x, y, c); break;
	case 'q': opq(g, x, y, c); break;
	case 'r': opr(g, x, y, c); break;
	case 's': ops(g, x, y, c); break;
	case 't': opt(g, x, y, c); break;
	case 'u': opu(g, x, y, c); break;
	case 'v': opv(g, x, y, c); break;
	case 'w': opw(g, x, y, c); break;
	case 'x': opx(g, x, y, c); break;
	case 'y': opy(g, x, y, c); break;
	case 'z': opz(g, x, y, c); break;
	case '*': setcell(g, x, y, '.'); break;
	case '#': opcomment(g, x, y); break;
	default: opspecial(g, x, y);
	}
}

#pragma mark - GRID

void
initgridframe(Grid *g)
{
	int i;
	for(i = 0; i < g->l; ++i) {
		g->lock[i] = 0;
		g->type[i] = 0;
	}
	for(i = 0; i < 36; ++i)
		g->var[i] = '.';
}

int
rungrid(Grid *g)
{
	int i, x, y;
	initgridframe(g);
	for(i = 0; i < g->l; ++i) {
		char c = g->data[i];
		x = i % g->w;
		y = i / g->w;
		if(c == '.' || g->lock[i])
			continue;
		if(c >= '0' && c <= '9')
			continue;
		if(c >= 'a' && c <= 'z' && !getbang(g, x, y))
			continue;
		operate(g, x, y, c);
	}
	g->f++;
	return 1;
}

void
initgrid(Grid *g, int w, int h)
{
	int i;
	g->w = w;
	g->h = h;
	g->l = w * h;
	g->f = 0;
	g->r = 1;
	for(i = 0; i < g->l; ++i)
		setcell(g, i % g->w, i / g->w, '.');
	initgridframe(g);
}

#pragma mark - DRAW

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(cursor.x == x && cursor.y == y)
		return 66;
	if(GUIDES) {
		if(x % 8 == 0 && y % 8 == 0)
			return 68;
		if(sel || type || (x % 2 == 0 && y % 2 == 0))
			return 64;
	}
	return 70;
}

void
setpixel(Uint32 *dst, int x, int y, int color)
{
	if(x >= 0 && x < WIDTH - 8 && y >= 0 && y < HEIGHT - 8)
		dst[(y + PAD * 8) * WIDTH + (x + PAD * 8)] = theme[color];
}

void
drawicon(Uint32 *dst, int x, int y, Uint8 *icon, int fg, int bg)
{
	int v, h;
	for(v = 0; v < 8; v++)
		for(h = 0; h < 8; h++) {
			int clr = (icon[v] >> (7 - h)) & 0x1;
			setpixel(dst, x + h, y + v, clr == 1 ? fg : bg);
		}
}

void
drawui(Uint32 *dst)
{
	int i, n = 0, bottom = VER * 8 + 8;
	/* cursor */
	drawicon(dst, 0 * 8, bottom, font[cursor.x % 36], 1, 0);
	drawicon(dst, 1 * 8, bottom, font[68], 1, 0);
	drawicon(dst, 2 * 8, bottom, font[cursor.y % 36], 1, 0);
	drawicon(dst, 3 * 8, bottom, icons[2], cursor.w > 1 || cursor.h > 1 ? 4 : 3, 0);
	/* frame */
	drawicon(dst, 5 * 8, bottom, font[(doc.grid.f / 1296) % 36], 1, 0);
	drawicon(dst, 6 * 8, bottom, font[(doc.grid.f / 36) % 36], 1, 0);
	drawicon(dst, 7 * 8, bottom, font[doc.grid.f % 36], 1, 0);
	drawicon(dst, 8 * 8, bottom, icons[PAUSE ? 1 : 0], (doc.grid.f - 1) % 8 == 0 ? 2 : 3, 0);
	/* speed */
	drawicon(dst, 10 * 8, bottom, font[(BPM / 100) % 10], 1, 0);
	drawicon(dst, 11 * 8, bottom, font[(BPM / 10) % 10], 1, 0);
	drawicon(dst, 12 * 8, bottom, font[BPM % 10], 1, 0);
	/* io */
	for(i = 0; i < VOICES; ++i)
		if(voices[i].len)
			n++;
	drawicon(dst, 13 * 8, bottom, n > 0 ? icons[2 + clmp(n, 0, 6)] : font[70], 2, 0);
	/* generics */
	drawicon(dst, 15 * 8, bottom, icons[GUIDES ? 10 : 9], GUIDES ? 1 : 2, 0);
	drawicon(dst, (HOR - 1) * 8, bottom, icons[11], doc.unsaved ? 2 : 3, 0);
}

void
redraw(Uint32 *dst)
{
	int x, y;
	Rect2d *r = &cursor;
	for(y = 0; y < VER; ++y) {
		for(x = 0; x < HOR; ++x) {
			int sel = x < r->x + r->w && x >= r->x && y < r->y + r->h && y >= r->y;
			int t = gettype(&doc.grid, x, y);
			Uint8 *letter = font[getfont(x, y, getcell(&doc.grid, x, y), t, sel)];
			int fg = 0, bg = 0;
			if(sel) {
				fg = 0;
				bg = 4;
			} else {
				switch(t) {
				case 1: fg = 3; break;
				case 2: fg = 1; break;
				case 3: bg = 1; break;
				case 4: fg = 2; break;
				case 5: bg = 2; break;
				default:
					fg = 3;
				}
			}
			drawicon(dst, x * 8, y * 8, letter, fg, bg);
		}
	}
	drawui(dst);
	SDL_UpdateTexture(gTexture, NULL, dst, WIDTH * sizeof(Uint32));
	SDL_RenderClear(gRenderer);
	SDL_RenderCopy(gRenderer, gTexture, NULL, NULL);
	SDL_RenderPresent(gRenderer);
}

#pragma mark - OPTIONS

int
error(char *msg, const char *err)
{
	printf("Error %s: %s\n", msg, err);
	return 0;
}

void
makedoc(Document *d, char *name)
{
	initgrid(&d->grid, HOR, VER);
	d->unsaved = 0;
	scpy(name, d->name, 256);
	redraw(pixels);
	printf("Made: %s\n", name);
}

int
opendoc(Document *d, char *name)
{
	int x = 0, y = 0;
	char c;
	FILE *f = fopen(name, "r");
	if(!f)
		return error("Load", "Invalid input file");
	initgrid(&d->grid, HOR, VER);
	while((c = fgetc(f)) != EOF && d->grid.l <= MAXSZ) {
		if(c == '\n') {
			x = 0;
			y++;
		} else {
			setcell(&d->grid, x, y, c);
			x++;
		}
	}
	d->unsaved = 0;
	scpy(name, d->name, 256);
	redraw(pixels);
	printf("Opened: %s\n", name);
	return 1;
}

void
savedoc(Document *d, char *name)
{
	int x, y;
	FILE *f = fopen(name, "w");
	for(y = 0; y < d->grid.h; ++y) {
		for(x = 0; x < d->grid.w; ++x)
			fputc(getcell(&d->grid, x, y), f);
		fputc('\n', f);
	}
	fclose(f);
	d->unsaved = 0;
	scpy(name, d->name, 256);
	redraw(pixels);
	printf("Saved: %s\n", name);
}

void
setoption(int *i, int v)
{
	*i = v;
	redraw(pixels);
}

void
select(int x, int y, int w, int h)
{
	Rect2d r;
	r.x = clmp(x, 0, HOR - 1);
	r.y = clmp(y, 0, VER - 1);
	r.w = clmp(w, 1, HOR - x);
	r.h = clmp(h, 1, VER - y);
	if(r.x != cursor.x || r.y != cursor.y || r.w != cursor.w || r.h != cursor.h) {
		cursor = r;
		redraw(pixels);
	}
}

void
scale(int w, int h, int skip)
{
	select(cursor.x, cursor.y, cursor.w + (w * (skip ? 4 : 1)), cursor.h + (h * (skip ? 4 : 1)));
}

void
move(int x, int y, int skip)
{
	select(cursor.x + (x * (skip ? 4 : 1)), cursor.y + (y * (skip ? 4 : 1)), cursor.w, cursor.h);
}

void
reset(void)
{
	MODE = 0;
	GUIDES = 1;
	select(cursor.x, cursor.y, 1, 1);
}

void
comment(Rect2d *r)
{
	int y;
	char c = getcell(&doc.grid, r->x, r->y) == '#' ? '.' : '#';
	for(y = 0; y < r->h; ++y) {
		setcell(&doc.grid, r->x, r->y + y, c);
		setcell(&doc.grid, r->x + r->w - 1, r->y + y, c);
	}
	doc.unsaved = 1;
	redraw(pixels);
}

void
insert(char c)
{
	int x, y;
	for(x = 0; x < cursor.w; ++x)
		for(y = 0; y < cursor.h; ++y)
			setcell(&doc.grid, cursor.x + x, cursor.y + y, c);
	if(MODE)
		move(1, 0, 0);
	doc.unsaved = 1;
	redraw(pixels);
}

void
frame(void)
{
	rungrid(&doc.grid);
	redraw(pixels);
	runmidi();
}

void
selectoption(int option)
{
	switch(option) {
	case 3: select(cursor.x, cursor.y, 1, 1); break;
	case 8:
		PAUSE = 1;
		frame();
		break;
	case 15: setoption(&GUIDES, !GUIDES); break;
	case HOR - 1: savedoc(&doc, doc.name); break;
	}
}

void
quit(void)
{
	free(pixels);
	SDL_DestroyTexture(gTexture);
	gTexture = NULL;
	SDL_DestroyRenderer(gRenderer);
	gRenderer = NULL;
	SDL_DestroyWindow(gWindow);
	gWindow = NULL;
	SDL_Quit();
	exit(0);
}

#pragma mark - CLIP

void
copyclip(Rect2d *r, char *c)
{
	int x, y, i = 0;
	for(y = 0; y < r->h; ++y) {
		for(x = 0; x < r->w; ++x)
			c[i++] = getcell(&doc.grid, r->x + x, r->y + y);
		c[i++] = '\n';
	}
	c[i] = '\0';
	redraw(pixels);
}

void
cutclip(Rect2d *r, char *c)
{
	copyclip(r, c);
	insert('.');
}

void
pasteclip(Rect2d *r, char *c, int insert)
{
	int i = 0, x = r->x, y = r->y;
	char ch;
	while((ch = c[i++])) {
		if(ch == '\n') {
			x = r->x;
			y++;
		} else {
			setcell(&doc.grid, x, y, insert && ch == '.' ? getcell(&doc.grid, x, y) : ch);
			x++;
		}
	}
	doc.unsaved = 1;
	redraw(pixels);
}

void
moveclip(Rect2d *r, char *c, int x, int y, int skip)
{
	copyclip(r, c);
	insert('.');
	move(x, y, skip);
	pasteclip(r, c, 0);
}

#pragma mark - TRIGGERS

void
domouse(SDL_Event *event)
{
	int cx = event->motion.x / ZOOM / 8 - PAD;
	int cy = event->motion.y / ZOOM / 8 - PAD;
	switch(event->type) {
	case SDL_MOUSEBUTTONUP:
		DOWN = 0;
		break;
	case SDL_MOUSEBUTTONDOWN:
		if(cy == VER + 1)
			selectoption(cx);
		else {
			select(cx, cy, 1, 1);
			DOWN = 1;
		}
		break;
	case SDL_MOUSEMOTION:
		if(DOWN)
			select(cursor.x, cursor.y, cx + 1 - cursor.x, cy + 1 - cursor.y);
		break;
	}
}

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;
	int alt = SDL_GetModState() & KMOD_LALT || SDL_GetModState() & KMOD_RALT;
	if(ctrl) {
		switch(event->key.keysym.sym) {
		/* Generic */
		case SDLK_n: makedoc(&doc, "untitled.orca"); break;
		case SDLK_r: opendoc(&doc, doc.name); break;
		case SDLK_s: savedoc(&doc, doc.name); break;
		case SDLK_h: setoption(&GUIDES, !GUIDES); break;
		/* Edit */
		case SDLK_i: setoption(&MODE, !MODE); break;
		case SDLK_a: select(0, 0, doc.grid.w, doc.grid.h); break;
		case SDLK_x: cutclip(&cursor, clip); break;
		case SDLK_c: copyclip(&cursor, clip); break;
		case SDLK_v: pasteclip(&cursor, clip, shift); break;
		case SDLK_UP: moveclip(&cursor, clip, 0, -1, alt); break;
		case SDLK_DOWN: moveclip(&cursor, clip, 0, 1, alt); break;
		case SDLK_LEFT: moveclip(&cursor, clip, -1, 0, alt); break;
		case SDLK_RIGHT: moveclip(&cursor, clip, 1, 0, alt); break;
		case SDLK_SLASH: comment(&cursor); break;
		}
	} else {
		switch(event->key.keysym.sym) {
		case SDLK_ESCAPE: reset(); break;
		case SDLK_PAGEUP: setoption(&BPM, BPM + 1); break;
		case SDLK_PAGEDOWN: setoption(&BPM, BPM - 1); break;
		case SDLK_UP: shift ? scale(0, -1, alt) : move(0, -1, alt); break;
		case SDLK_DOWN: shift ? scale(0, 1, alt) : move(0, 1, alt); break;
		case SDLK_LEFT: shift ? scale(-1, 0, alt) : move(-1, 0, alt); break;
		case SDLK_RIGHT: shift ? scale(1, 0, alt) : move(1, 0, alt); break;
		case SDLK_SPACE: setoption(&PAUSE, !PAUSE); break;
		case SDLK_BACKSPACE: insert('.'); break;
		}
	}
}

void
dotext(SDL_Event *event)
{
	int i;
	for(i = 0; i < SDL_TEXTINPUTEVENT_TEXT_SIZE; ++i) {
		char c = event->text.text[i];
		if(c < ' ' || c > '~')
			break;
		insert(c);
	}
}

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] = theme[0];
	initmidi();
	return 1;
}

int
main(int argc, char *argv[])
{
	Uint8 tick = 0;
	if(!init())
		return error("Init", "Failure");
	if(argc > 1) {
		if(!opendoc(&doc, argv[1]))
			makedoc(&doc, argv[1]);
	} else
		makedoc(&doc, "untitled.orca");
	while(1) {
		SDL_Event event;
		if(!PAUSE) {
			if(tick > 3) {
				frame();
				tick = 0;
			} else
				tick++;
		}
		SDL_Delay(60000 / BPM / 16);
		while(SDL_PollEvent(&event) != 0) {
			switch(event.type) {
			case SDL_QUIT: quit(); break;
			case SDL_MOUSEBUTTONUP:
			case SDL_MOUSEBUTTONDOWN:
			case SDL_MOUSEMOTION: domouse(&event); break;
			case SDL_KEYDOWN: dokey(&event); break;
			case SDL_TEXTINPUT: dotext(&event); break;
			case SDL_WINDOWEVENT:
				if(event.window.event == SDL_WINDOWEVENT_EXPOSED)
					redraw(pixels);
				break;
			}
		}
	}
	quit();
	return 0;
}
— Submit an edit to orca.c.txt(1294 lines)
Orca Type picture
12X03 — Orca Type
Orca Glyphs picture
09Z11 — Orca Glyphs
Orca Machine picture
07R11 — Orca Machine

incoming(10): alicef azolla themes yajnev studies pilot enfer nasu norns computer juni

Last update on 15B02, edited 123 times. +557/731fh -|||||