shithub: orca

ref: dd470b171237d28bca26abd57bdecbbedba13f48
dir: /plan9.c/

View raw version
#include "plan9.h"
#include "field.h"
#include "gbuffer.h"
#include "sim.h"
#include <draw.h>
#include <mouse.h>
#include <keyboard.h>
#include <thread.h>

static struct {
  u8int u[4];
  int at;
}noteoff[16*128]; /* 16 channels, 128 notes each */

static char *buf;

Usz orca_round_up_power2(Usz x) {
  assert(x <= SIZE_MAX / 2 + 1);
  x -= 1;
  x |= x >> 1;
  x |= x >> 2;
  x |= x >> 4;
  x |= x >> 8;
  x |= x >> 16;
#if SIZE_MAX > UINT32_MAX
  x |= x >> 32;
#endif
  return x + 1;
}

bool orca_is_valid_glyph(Glyph c) {
  if (c >= '0' && c <= '9')
    return true;
  if (c >= 'A' && c <= 'Z')
    return true;
  if (c >= 'a' && c <= 'z')
    return true;
  switch (c) {
  case '!':
  case '#':
  case '%':
  case '*':
  case '.':
  case ':':
  case ';':
  case '=':
  case '?':
    return true;
  }
  return false;
}

static void
usage(void)
{
  print("usage: orca [-q] [-t ticks] [-f file]\n");
  exits("usage");
}

static void
process(Oevent_list *el, int tick)
{
  int i, off;
  Oevent *e;
  u8int u[4];

  for (i = 0; i < el->count; i++) {
    e = &el->buffer[i];
    if (e->any.oevent_type == Oevent_type_midi_note) {
      Oevent_midi_note const *n = &e->midi_note;
      u[0] = 1;
      u[1] = 0x90 | n->channel;
      u[2] = (n->octave + 1)*12 + n->note;
      u[3] = n->velocity;
      write(1, u, 4);

      off = n->channel*128 + u[2];
      noteoff[off].u[1] = 0x80 | n->channel;
      noteoff[off].u[2] = u[2];
      noteoff[off].u[3] = 0;//u[3];
      noteoff[off].at = tick + n->duration;
    }
  }

  sleep(150);

  for (i = 0; i < nelem(noteoff); i++) {
    if (noteoff[i].at > 0 && noteoff[i].at < tick) {
      write(1, noteoff[i].u, 4);
      noteoff[i].at = 0;
    }
  }
}

static void
field_draw(Field *f, int tick)
{
  Point p;
  int x, y;

  lockdisplay(display);
  draw(screen, screen->r, display->black, nil, ZP);
  p = screen->r.min;
  p.x += 8;
  p.y += 8;
  for (y = 0; y < f->height; y++) {
    for (x = 0; x < f->width; x++) {
      u8int c = f->buffer[f->width*y + x];
      buf[x] = (c > '\n' && c < 128) ? c : '?';
    }
    buf[x] = 0;
    string(screen, p, display->white, ZP, display->defaultfont, buf);
    p.y += display->defaultfont->height;
  }

  p.x = screen->r.min.x + 8;
  p.y += 2 * display->defaultfont->height;
  sprint(buf, "%df", tick);
  string(screen, p, display->white, ZP, display->defaultfont, buf);

  flushimage(display, 1);
  unlockdisplay(display);
}

void
threadmain(int argc, char **argv)
{
  const char *input_file = "/fd/0";
  int ticks = 1;
  bool print_output = true;
  Field field;
  Mousectl *mctl;
  Keyboardctl *kctl;
  Rune key;
  Mouse m;
  Alt a[] = {
    { nil, &m, CHANRCV },
    { nil, nil, CHANRCV },
    { nil, &key, CHANRCV },
    { nil, nil,  CHANNOBLK},
  };

  ARGBEGIN{
  case 't':
    ticks = atoi(EARGF(usage()));
    break;
  case 'f':
    input_file = EARGF(usage());
    break;
  case 'q':
    print_output = false;
    break;
  }ARGEND;

  field_init(&field);
  Field_load_error fle = field_load_file(input_file, &field);
  if (fle != Field_load_error_ok) {
    field_deinit(&field);
    char const *errstr = "Unknown";
    switch (fle) {
    case Field_load_error_ok:
      break;
    case Field_load_error_cant_open_file:
      errstr = "Unable to open file";
      break;
    case Field_load_error_too_many_columns:
      errstr = "Grid file has too many columns";
      break;
    case Field_load_error_too_many_rows:
      errstr = "Grid file has too many rows";
      break;
    case Field_load_error_no_rows_read:
      errstr = "Grid file has no rows";
      break;
    case Field_load_error_not_a_rectangle:
      errstr = "Grid file is not a rectangle";
      break;
    }
    exits(errstr);
  }
  buf = malloc(field.width+1);
  memset(noteoff, 0, sizeof(noteoff));
  Mbuf_reusable mbuf_r;
  mbuf_reusable_init(&mbuf_r);
  mbuf_reusable_ensure_size(&mbuf_r, field.height, field.width);
  Oevent_list oevent_list;
  oevent_list_init(&oevent_list);
  Usz max_ticks = (Usz)ticks;

  if(initdraw(nil, nil, "orca") < 0)
    sysfatal("initdraw: %r");
  if ((mctl = initmouse(nil, screen)) == nil)
    sysfatal("initmouse: %r");
  if ((kctl = initkeyboard(nil)) == nil)
    sysfatal("initkeyboard: %r");

  a[0].c = mctl->c;
  a[1].c = mctl->resizec;
  a[2].c = kctl->c;
  unlockdisplay(display);

  srand(time(0));
  threadsetname("orca");

  for (Usz i = 0; i < max_ticks; ++i) {
    mbuffer_clear(mbuf_r.buffer, field.height, field.width);
    oevent_list_clear(&oevent_list);
    orca_run(field.buffer, mbuf_r.buffer, field.height, field.width, i,
             &oevent_list, 0);
    process(&oevent_list, i);
    field_draw(&field, i);

    switch (alt(a)) {
    case 0: /* mouse */
      break;
    case 1: /* resize */
      getwindow(display, Refnone);
      break;
    case 2: /* keyboard */
      switch (key) {
      case Kdel:
        goto end;
      }
    }
  }

end:
  mbuf_reusable_deinit(&mbuf_r);
  oevent_list_deinit(&oevent_list);
  if (print_output)
    field_fput(&field, stderr);
  field_deinit(&field);

  threadexitsall(nil);
}