commit 34958ac65f6d2e35f294ded5434afdd84b5af3e6
Author: Nihal Jere <nihal@nihaljere.xyz>
Date: Mon, 22 Aug 2022 20:40:36 -0500
initial commit: set clef and draw notes
Diffstat:
A | Makefile | | | 5 | +++++ |
A | main.c | | | 100 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
A | smallpond.lua | | | 85 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 190 insertions(+), 0 deletions(-)
diff --git a/Makefile b/Makefile
@@ -0,0 +1,5 @@
+smallpond: main.c
+ gcc -o smallpond main.c $(shell pkg-config --cflags --libs lua) $(shell pkg-config --cflags --libs freetype2) $(shell pkg-config --cflags --libs cairo)
+
+clean:
+ rm -f smallpond
diff --git a/main.c b/main.c
@@ -0,0 +1,99 @@
+#include <stdio.h>
+
+#include <ft2build.h>
+#include FT_FREETYPE_H
+
+#include <cairo-ft.h>
+#include <cairo-pdf.h>
+#include <cairo.h>
+
+#include <lua.h>
+#include <lauxlib.h>
+#include <lualib.h>
+
+cairo_t *cr;
+FT_Face face;
+cairo_font_face_t *cface;
+
+int
+draw_line(lua_State *L)
+{
+ double x1 = lua_tonumber(L, -4);
+ double y1 = lua_tonumber(L, -3);
+ double x2 = lua_tonumber(L, -2);
+ double y2 = lua_tonumber(L, -1);
+
+ cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
+
+ cairo_move_to(cr, x1, y1);
+ cairo_line_to(cr, x2, y2);
+ cairo_set_line_width(cr, 1);
+ cairo_stroke(cr);
+
+ return 0;
+}
+
+int
+draw_glyph(lua_State *L)
+{
+ unsigned int glyph = lua_tonumber(L, -3);
+ double x = lua_tonumber(L, -2);
+ double y = lua_tonumber(L, -1);
+
+ int index = FT_Get_Char_Index(face, glyph);
+ cairo_glyph_t treble_clef = {index, x, y};
+
+ cairo_text_extents_t extents;
+ cairo_glyph_extents(cr, &treble_clef, 1, &extents);
+
+ cairo_set_font_face(cr, cface);
+ cairo_set_font_size(cr, 32.0);
+
+ cairo_set_source_rgb(cr, 0.0, 0.0, 0.0);
+ cairo_show_glyphs(cr, &treble_clef, 1);
+
+ return 0;
+}
+
+int
+main(int argc, char *argv[])
+{
+ lua_State *L = luaL_newstate();
+ luaL_openlibs(L);
+ lua_pushcfunction(L, draw_glyph);
+ lua_setglobal(L, "draw_glyph");
+ lua_pushcfunction(L, draw_line);
+ lua_setglobal(L, "draw_line");
+ FT_Library library;
+ int error = FT_Init_FreeType(&library);
+
+ // TODO: print the actual error
+ if (error) {
+ fprintf(stderr, "freetype init error");
+ return 1;
+ }
+
+ error = FT_New_Face(library, "/usr/share/fonts/OTF/Bravura.otf", 0, &face);
+ if (error) {
+ fprintf(stderr, "freetype font load error");
+ return 1;
+ }
+
+ cface = cairo_ft_font_face_create_for_ft_face(face, 0);
+ if (!cface) {
+ fprintf(stderr, "cairo font face load error");
+ return 1;
+ }
+
+ cairo_surface_t *surface = cairo_pdf_surface_create("out.pdf", 648, 864);
+ cr = cairo_create(surface);
+
+ if (luaL_dofile(L, "smallpond.lua")) {
+ fprintf(stderr, "lua error: %s\n", lua_tostring(L, -1));
+ return 1;
+ }
+
+ cairo_destroy(cr);
+ cairo_surface_destroy(surface);
+ lua_close(L);
+}
+\ No newline at end of file
diff --git a/smallpond.lua b/smallpond.lua
@@ -0,0 +1,85 @@
+-- parse
+local em = 8
+
+function placement(char)
+ assert(char)
+ local NOTES = "abcdefg"
+ local s, _ = string.find(NOTES, char)
+ return s
+end
+
+commands = {
+ clef = {
+ parse = function(text, start)
+ -- move past "\clef "
+ start = start + 6
+ if string.match(text, "^treble", start) then
+ return #"\\clef treble", {command="changeclef", kind="treble"}
+ else
+ error(string.format("unknown clef at offset %s", string.sub(text, start)))
+ end
+ end
+ }
+}
+
+function parse(text)
+ local i = 1
+ return function()
+ if i >= #text then return nil end
+ local cmd = string.match(text, "^\\(%a+)", i)
+ if cmd then
+ local size, data = commands[cmd].parse(text, i)
+ i = i + size
+ return data
+ end
+
+ local s, e, note = string.find(text, "^%s*([abcdefg])", i)
+ if note then
+ i = i + e - s + 1
+ return {command="newnote", note=note}
+ end
+
+ error("unknown token")
+ end
+end
+
+f = assert(io.open("score.sp"))
+
+local x = 10
+staff = {}
+command_dispatch = {
+ newnote = function(data)
+ local i = placement(data.note)
+ table.insert(staff, {kind="notehead", x=x, y=(em*i) / 2})
+ x = x + 20
+ end,
+ changeclef = function(data)
+ table.insert(staff, {kind="clef", x=x, y=4*em})
+ x = x + 40
+ end
+}
+
+for tok in parse(f:read("*a")) do
+ local func = assert(command_dispatch[tok.command])
+ func(tok)
+end
+
+-- determine staff width, the +20 is a hack, should be determined from notehead width + some padding
+local staff_width = staff[#staff].x + 20
+local yoffset = 20
+local xoffset = 20
+
+-- draw staff
+for y=0,em*5,em do
+ draw_line(xoffset, y + yoffset, staff_width + xoffset, y + yoffset)
+end
+
+local noteheadBlock = 0xE0A4
+local gClef = 0xE050
+for i, el in ipairs(staff) do
+ if el.kind == "notehead" then
+ draw_glyph(noteheadBlock, xoffset + el.x, yoffset + el.y)
+ elseif el.kind == "clef" then
+ draw_glyph(gClef, xoffset + el.x, yoffset + el.y)
+ end
+end