Skip to content

Instantly share code, notes, and snippets.

@bwhitman
Created January 4, 2025 02:19
Show Gist options
  • Save bwhitman/3c1be07f729380e9f03fd38ef2ed75aa to your computer and use it in GitHub Desktop.
Save bwhitman/3c1be07f729380e9f03fd38ef2ed75aa to your computer and use it in GitHub Desktop.
playdate running AMY
HEAP_SIZE = 8388208
STACK_SIZE = 61800
PRODUCT = HelloWorld.pdx
# Locate the SDK
SDK = ${PLAYDATE_SDK_PATH}
ifeq ($(SDK),)
SDK = $(shell egrep '^\s*SDKRoot' ~/.Playdate/config | head -n 1 | cut -c9-)
endif
ifeq ($(SDK),)
$(error SDK path not found; set ENV value PLAYDATE_SDK_PATH)
endif
######
# IMPORTANT: You must add your source folders to VPATH for make to find them
# ex: VPATH += src1:src2
######
VPATH += src
# List C source files here
SRC = src/main.c
SRC += $(addprefix /Users/bwhitman/outside/amy/src/, \
amy.c \
algorithms.c \
custom.c \
patches.c \
delay.c \
envelope.c \
filters.c \
oscillators.c \
transfer.c \
sequencer.c \
partials.c \
pcm.c \
log2_exp2.c \
)
# List all user directories here
UINCDIR = /Users/bwhitman/outside/amy/src/
# List user asm files
UASRC =
# List all user C define here, like -D_DEBUG=1
UDEFS = -Wno-strict-aliasing -Wextra -Wno-unused-parameter -Wpointer-arith \
-Wno-float-conversion -Wno-missing-declarations -Wno-strict-prototypes -Wno-double-promotion \
-Wno-old-style-declaration
# Define ASM defines here
UADEFS =
# List the user directory to look for the libraries here
ULIBDIR =
# List all user libraries here
ULIBS = --specs=nosys.specs
include $(SDK)/C_API/buildsupport/common.mk
//
// main.c
// Extension
//
// Created by Dave Hayden on 7/30/14.
// Copyright (c) 2014 Panic, Inc. All rights reserved.
//
#include <stdio.h>
#include <stdlib.h>
#include "pd_api.h"
#include "amy.h"
#include "examples.h"
static int update(void* userdata);
const char* fontpath = "/System/Fonts/Asheville-Sans-14-Bold.pft";
LCDFont* font = NULL;
// Context state for audio callback
typedef struct {
PlaydateAPI *pd;
SoundSource *source;
} AudioState;
void example_juno_note() {
struct event e = amy_default_event();
e.load_patch = 0;
strcpy(e.voices, "0");
amy_add_event(e);
e = amy_default_event();
strcpy(e.voices, "0");
e.midi_note = 50;
e.velocity = 1;
e.time = 1000;
amy_add_event(e);
}
// I've seen frame counts as big as 1440, I think *8 is enough room (2048)
#define OUTPUT_RING_FRAMES (AMY_BLOCK_SIZE*8)
#define OUTPUT_RING_LENGTH (OUTPUT_RING_FRAMES*AMY_NCHANS)
int16_t output_ring[OUTPUT_RING_LENGTH];
uint16_t ring_write_ptr = AMY_BLOCK_SIZE*AMY_NCHANS; // start after one AMY frame
uint16_t ring_read_ptr = 0 ;
uint16_t in_ptr = 0;
extern int16_t amy_in_block[AMY_BLOCK_SIZE*AMY_NCHANS];
// https://devforum.play.date/t/tips-and-tricks-for-processing-audio-in-c/8025/5
int amy_callback(void* context, int16_t* left, int16_t* right, int len) {
// We lag a AMY block behind input here because our frame size is never a multiple of AMY block size
for(uint16_t frame=0;frame<len;frame++) {
in_ptr+=2; // stereo
if(in_ptr == (AMY_BLOCK_SIZE*2)) { // we have a block of input ready
// render and copy into output ring buffer
int16_t * buf = amy_simple_fill_buffer();
// reset the input pointer for future input data
in_ptr = 0;
// copy this output to a ring buffer
for(uint16_t amy_frame=0;amy_frame<AMY_BLOCK_SIZE;amy_frame++) {
output_ring[ring_write_ptr++] = buf[AMY_NCHANS * amy_frame + 0];
output_ring[ring_write_ptr++] = buf[AMY_NCHANS * amy_frame + 1];
if(ring_write_ptr == OUTPUT_RING_LENGTH) ring_write_ptr = 0;
}
}
// Per audio frame, copy (lagged by AMY_BLOCK_SIZE) output data from AMY into expected audio out buffer
left[frame] = output_ring[ring_read_ptr++];
right[frame] = output_ring[ring_read_ptr++];
if(ring_read_ptr == OUTPUT_RING_LENGTH ) ring_read_ptr = 0;
}
return 1;
}
#ifdef _WINDLL
__declspec(dllexport)
#endif
int eventHandler(PlaydateAPI* pd, PDSystemEvent event, uint32_t arg)
{
(void)arg; // arg is currently only used for event = kEventKeyPressed
if ( event == kEventInit )
{
amy_start(1,1,1,1);
example_juno_note();
// Create a new audio source with a state context
AudioState *state = pd->system->realloc(NULL, sizeof (AudioState));
state->pd = pd;
state->source = pd->sound->addSource(&amy_callback, state, 1);
const char* err;
font = pd->graphics->loadFont(fontpath, &err);
if ( font == NULL )
pd->system->error("%s:%i Couldn't load font %s: %s", __FILE__, __LINE__, fontpath, err);
// Note: If you set an update callback in the kEventInit handler, the system assumes the game is pure C and doesn't run any Lua code in the game
pd->system->setUpdateCallback(update, pd);
}
return 0;
}
#define TEXT_WIDTH 86
#define TEXT_HEIGHT 16
int x = (400-TEXT_WIDTH)/2;
int y = (240-TEXT_HEIGHT)/2;
int dx = 1;
int dy = 2;
static int update(void* userdata)
{
PlaydateAPI* pd = userdata;
pd->graphics->clear(kColorWhite);
pd->graphics->setFont(font);
pd->graphics->drawText("Hello World!", strlen("Hello World!"), kASCIIEncoding, x, y);
x += dx;
y += dy;
if ( x < 0 || x > LCD_COLUMNS - TEXT_WIDTH )
dx = -dx;
if ( y < 0 || y > LCD_ROWS - TEXT_HEIGHT )
dy = -dy;
pd->system->drawFPS(0,0);
return 1;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment