diff --git a/articles/oscillators/make b/articles/oscillators/make new file mode 100755 index 0000000..adccd39 --- /dev/null +++ b/articles/oscillators/make @@ -0,0 +1,7 @@ +#!/bin/sh + +cd $(dirname "$0") +mkdir ./.dynamic +mkdir ./.temp +gcc waveforms.c -I../../tools ../../tools/gifenc/gifenc.c -O2 -o ./.temp/waveforms +./.temp/waveforms diff --git a/articles/oscillators/waveforms.c b/articles/oscillators/waveforms.c new file mode 100644 index 0000000..ea6f7e6 --- /dev/null +++ b/articles/oscillators/waveforms.c @@ -0,0 +1,138 @@ +#include +#include +#include +#include +#include + +#include "gifenc/gifenc.h" + +#define NOTE 440 +#define AUDIO_FRAME_RATE 44100 +#define LENGTH (AUDIO_FRAME_RATE / NOTE) + +#define WIDTH LENGTH * 4 +#define HEIGHT 128 + +#define RED 0 +#define GREEN 0 +#define BLUE 0 + +static struct sinewave { + float f, s, c; +} init_sinewave(float frequency, float phase, float amplitude) { + struct sinewave r; + r.f = 2.f * sinf((float)M_PI * frequency / (float)AUDIO_FRAME_RATE); + r.s = amplitude * sinf(phase); + r.c = amplitude * cosf(phase); + return r; +} + +static float pump_sinewave(struct sinewave *wave) { + wave->s -= wave->f * wave->c; + wave->c += wave->f * wave->s; + return wave->s; +} + +static struct sqrtwave { + struct sinewave w; + union { + float f; + uint32_t u; + } v; +} init_sqrtwave(float frequency, float phase, float amplitude) { + struct sqrtwave r; + union { + float f; + uint32_t u; + } v, a; + r.w = init_sinewave(frequency, phase, 1.f); + v.f = r.w.s; + a.f = amplitude; + r.v.u = (a.u & 0x7fffffff) | (v.u & 0x80000000); + return r; +} + +static float pump_sqrtwave(struct sqrtwave *wave) { + union { + float f; + uint32_t u; + } v; + pump_sinewave(&wave->w); + v.f = wave->w.s; + wave->v.u = (wave->v.u & 0x7fffffff) | (v.u & 0x80000000); + return wave->v.f; +} + +static struct sawtwave { + float v, a, i; +} init_sawtwave(float frequency, float phase, float amplitude) { + struct sawtwave r; + r.v = sinf(phase) * amplitude; + r.a = amplitude; + r.i = 2.f * frequency / AUDIO_FRAME_RATE * amplitude; + return r; +} + +static float pump_sawtwave(struct sawtwave *wave) { + wave->v += wave->i; + if (wave->v > wave->a) + wave->v -= wave->a * 2.f; + return wave->v; +} + +static int absi(int v) { + return v > 0 ? v : -v; +} + +static void plot_line(uint8_t *frame, int x, int xt, int y, int yt) { + int dx = xt - x; + int dy = yt - y; + int step = absi(dx) >= absi(dy) ? absi(dx) : absi(dy); + dx /= step; + dy /= step; + int xc = x; + int yc = y; + int i = 1; + while (i <= step) { + frame[xc + yc * WIDTH] = 1; + xc += dx; + yc += dy; + i++; + } +} + +static void generate_wave(char const *filepath, void *generator, float (*pumper)(void *)) { + float wave[LENGTH]; + for (int i = 0; i < LENGTH; ++i) { + wave[i] = pumper(generator); + } + uint8_t palette[6] = { [3] = RED, [4] = GREEN, [5] = BLUE }; + ge_GIF *g = ge_new_gif(filepath, WIDTH, HEIGHT, palette, 1, 0, 0); + assert(g); + for (int f = 0; f < LENGTH; ++f) { + memset(g->frame, 0, WIDTH * HEIGHT); + for (int i = 0; i < WIDTH; ++i) { + int l0 = (int)((wave[(f + i) % LENGTH] + 1.0f) * 127.5f) / 2; + int l1 = (int)((wave[(f + i + 1) % LENGTH] + 1.0f) * 127.5f) / 2; + if (i == WIDTH - 1) + g->frame[i + l0 * WIDTH] = 1; + else + plot_line(g->frame, i, i + 1, l0, l1); + } + ge_add_frame(g, 1); + } + ge_close_gif(g); +} + +int main(void) { + struct sinewave sine = init_sinewave((float)NOTE, 0.0f, 1.0f); + generate_wave(".dynamic/sine.gif", &sine, pump_sinewave); + + struct sqrtwave sqrt = init_sqrtwave((float)NOTE, 0.0f, 1.0f); + generate_wave(".dynamic/sqrt.gif", &sqrt, pump_sqrtwave); + + struct sawtwave sawt = init_sawtwave((float)NOTE, 0.0f, 1.0f); + generate_wave(".dynamic/sawt.gif", &sawt, pump_sawtwave); + + return 0; +} diff --git a/compile.sh b/compile.sh index 2be054f..1e11eb8 100755 --- a/compile.sh +++ b/compile.sh @@ -6,8 +6,15 @@ mkdir -p ./html/articles ./tools/main_page_generator.py ./articles | ./tools/mmd/build/multimarkdown > ./html/index.html -for d in ./articles/*; do +for d in ./articles/*/; do if [ -d "$d" ]; then + if test -f "$d/make"; then + "$d/make" + fi + if test -d "$d/.dynamic"; then + mkdir -p "./html/articles/$(basename -- $d)" + cp -r "$d/.dynamic/." "./html/articles/$(basename -- $d)/" + fi ./tools/article_wrapper.py "$d/page.mmd" | ./tools/mmd/build/multimarkdown > "./html/articles/$(basename -- $d).html" fi done diff --git a/tools/feed_generator.py b/tools/feed_generator.py index 29461f2..a66a3d5 100755 --- a/tools/feed_generator.py +++ b/tools/feed_generator.py @@ -57,6 +57,7 @@ for root, dirs, _ in walk(argv[1]): f""" {address}/articles/{urllib.parse.quote(d)}\n""" " \n" ) + break feed += """ """ diff --git a/tools/main_page_generator.py b/tools/main_page_generator.py index 9e1b21e..6746554 100755 --- a/tools/main_page_generator.py +++ b/tools/main_page_generator.py @@ -38,6 +38,7 @@ for root, dirs, _ in walk(argv[1]): ) if "Tags" in metadata: page += f""">*{','.join(metadata["Tags"])}*\n---\n""" + break curtime = time.gmtime(int(time.time())) page += f"Last compiled: *{MONTHS[curtime.tm_mon]} {curtime.tm_mday}, {curtime.tm_year} {curtime.tm_hour}:{curtime.tm_min:02d} UTC*\n\n" diff --git a/tools/track_listing_generator.py b/tools/track_listing_generator.py index f5ea1ca..5d17cdd 100755 --- a/tools/track_listing_generator.py +++ b/tools/track_listing_generator.py @@ -88,6 +88,7 @@ for _, _, files in walk(argv[1]): f"""

{f[:-4]}

\n""" "
\n" ) + break page += """