smallpond

Unnamed repository; edit this file 'description' to name the repository.
git clone git://git.nihaljere.xyz/smallpond
Log | Files | Refs

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:
AMakefile | 5+++++
Amain.c | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asmallpond.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