Commit 5401acbd authored by Zack Lee's avatar Zack Lee
Browse files

split pdtest folder into build/project/src folders and update Makefile in emscripten directory

parent b7204780
#==============================================================================#
#
# Centralized build system for "emscripten".
# Centralized build system for "emscripten".
#
# Copyright (c) 2020 Zack Lee (cuinjune@gmail.com)
# Copyright (c) 2020 Purr Data team
#
# See https://git.purrdata.net/jwilkes/purr-data for documentation
#
#==============================================================================#
CWD := $(shell pwd)
PURR_DIR = $(CWD)/..
PROJECT_PATH = $(PURR_DIR)/emscripten/pdtest
DESTDIR = $(PURR_DIR)/emscripten/purr-data/
EM_PATH = $(PURR_DIR)/emscripten
LIBPD_PATH = $(PURR_DIR)/libpd
PD_PATH = $(PURR_DIR)/pd
BUILD_PATH = $(EM_PATH)/build
DESTDIR = $(BUILD_PATH)/purr-data/
WARN_FLAGS = -Wall -W -Wno-unused-parameter
EM_FLAGS = -s SIDE_MODULE=1 -O3
CFLAGS = -DPD -I$(PD_PATH)/src $(WARN_FLAGS) $(EM_FLAGS)
......@@ -21,9 +27,9 @@ CXX = $(CC)
CXXFLAGS = $(CFLAGS)
EXTENSION = wasm
.PHONY: all libpd externals extra abstractions doc project clean
.PHONY: all libpd externals extra $(extras) abstractions doc build clean
all: libpd externals extra abstractions doc project
all: libpd externals extra abstractions doc build
libpd: $(LIBPD_PATH)
cd $< && mkdir -p build && cd build && emcmake cmake .. -DPD_UTILS:BOOL=OFF -DCMAKE_BUILD_TYPE=Release && emmake make
......@@ -47,14 +53,14 @@ abstractions: $(PURR_DIR)/abstractions
doc: $(PD_PATH)/doc
cp -rf $</* $(DESTDIR)/doc
project: $(PROJECT_PATH)
build: $(BUILD_PATH)
make -C $<
clean:
find $(PURR_DIR)/externals -name "*.wasm" -type f -delete
find $(PURR_DIR)/pd/extra -name "*.wasm" -type f -delete
make -C $(PROJECT_PATH) clean
make -C $(LIBPD_PATH) clean
make -C $(BUILD_PATH) clean
rm -rf $(DESTDIR)
#==============================================================================#
#
# Centralized build system for "emscripten".
#
# Copyright (c) 2020 Zack Lee (cuinjune@gmail.com)
# Copyright (c) 2020 Purr Data team
#
# See https://git.purrdata.net/jwilkes/purr-data for documentation
#
#==============================================================================#
CWD := $(shell pwd)
PURR_DIR = $(CWD)/../..
EM_PATH = $(PURR_DIR)/emscripten
LIBPD_PATH = $(PURR_DIR)/libpd
PD_PATH = $(PURR_DIR)/pd
INSTALL_PATH = $(EM_PATH)/project/purr-data
DESTDIR = purr-data
SRC_DIR = $(EM_PATH)/src
SRC_FILES = $(SRC_DIR)/main.cpp $(SRC_DIR)/pd.cpp $(SRC_DIR)/js.cpp
TARGET = main.html
OUTPUT_FILES = $(TARGET) main.js main.wasm main.data
CFLAGS = -I$(PD_PATH)/src -I$(LIBPD_PATH)/libpd_wrapper -O3
LDFLAGS = -L$(LIBPD_PATH)/libs -lpd -lm
.PHONY: build install $(OUTPUT_FILES) clean
all: build install
build: $(SRC_FILES) $(DESTDIR)
emcc $(CFLAGS) -s MAIN_MODULE=1 --bind -o $(TARGET) $(SRC_FILES) \
-s ASYNCIFY -s "ASYNCIFY_IMPORTS=['__Pd_loadLib']" \
-s USE_SDL=2 -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s ALLOW_MEMORY_GROWTH=1 \
-s FORCE_FILESYSTEM=1 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['FS']" \
--no-heap-copy --preload-file $(DESTDIR) $(LDFLAGS)
@echo "emscripten build succeeded!"
install: $(OUTPUT_FILES)
@echo "emscripten install succeeded!"
$(OUTPUT_FILES): %: $(INSTALL_PATH)
cp -f $@ $<
clean:
rm -rf $(OUTPUT_FILES)
pdtest.data
pdtest.html
pdtest.js
pdtest.wasm
\ No newline at end of file
CWD := $(shell pwd)
PURR_DIR = $(CWD)/../..
DESTDIR = ../purr-data
LIBPD_PATH = $(PURR_DIR)/libpd
PD_PATH = $(PURR_DIR)/pd
SRC_FILE = pdtest.cpp
TARGET = pdtest.html
OUTPUT_FILES = $(TARGET) pdtest.js pdtest.wasm pdtest.data
CFLAGS = -I$(PD_PATH)/src -I$(LIBPD_PATH)/libpd_wrapper -O3
LDFLAGS = -L$(LIBPD_PATH)/libs -lpd -lm
.PHONY: build clean
all: build
build: $(SRC_FILE)
emcc $(CFLAGS) -s MAIN_MODULE=1 --bind -o $(TARGET) $(SRC_FILE) \
-s ASYNCIFY -s "ASYNCIFY_IMPORTS=['pd_load_external']" \
-s USE_SDL=2 -s ERROR_ON_UNDEFINED_SYMBOLS=0 -s ALLOW_MEMORY_GROWTH=1 \
-s FORCE_FILESYSTEM=1 -s "EXTRA_EXPORTED_RUNTIME_METHODS=['FS']" \
--no-heap-copy --preload-file $(DESTDIR) $(LDFLAGS)
clean:
rm -rf $(OUTPUT_FILES)
\ No newline at end of file
#include <SDL2/SDL.h>
#include <SDL2/SDL_audio.h>
#include <dirent.h>
#include <iostream>
#include <cstdio>
#include <cstring>
#include <string>
#include "z_libpd.h"
#include <emscripten.h>
#include <emscripten/bind.h>
using namespace emscripten;
static SDL_AudioDeviceID device_in = 0;
static SDL_AudioDeviceID device_out = 0;
static int num_in_channels = 1;
static int num_out_channels = 2;
static int samplerate = 44100;
static int ticks_per_buffer = 16;
static int blocksize = libpd_blocksize();
static int buffersize = ticks_per_buffer * blocksize;
static float *in_buffer = nullptr;
static bool is_buffer_cleared = true;
void audio_in(void *userdata, Uint8 *stream, int len) {
try {
if (in_buffer != nullptr) {
float *input = reinterpret_cast<float *>(stream);
memcpy(in_buffer, input, buffersize * num_in_channels * sizeof(float));
}
}
catch (...) {
SDL_Log("pd: could not copy input buffer");
}
}
void audio_out(void *userdata, Uint8 *stream, int len) {
if (in_buffer != nullptr) {
float *output = reinterpret_cast<float *>(stream);
if (canvas_dspstate == 0 && !is_buffer_cleared) {
sys_lock();
memset(in_buffer, 0, num_in_channels * buffersize * sizeof(float));
memset(output, 0, num_out_channels * buffersize * sizeof(float));
sys_unlock();
is_buffer_cleared = true;
}
if (canvas_dspstate == 1 && is_buffer_cleared) {
is_buffer_cleared = false;
}
if (libpd_process_float(ticks_per_buffer, in_buffer, output)) {
SDL_Log("pd: could not process output buffer");
}
}
}
int send_bang(const std::string &recv) {
return libpd_bang(recv.c_str());
}
int send_float(const std::string &recv, float f) {
return libpd_float(recv.c_str(), f);
}
int send_symbol(const std::string &recv, const std::string &s) {
return libpd_symbol(recv.c_str(), s.c_str());
}
int start_message(int maxlen) {
return libpd_start_message(maxlen);
}
void add_float(float f) {
libpd_add_float(f);
}
void add_symbol(const std::string &s) {
libpd_add_symbol(s.c_str());
}
int finish_list(const std::string &recv) {
return libpd_finish_list(recv.c_str());
}
int finish_message(const std::string &recv, const std::string &msg) {
return libpd_finish_message(recv.c_str(), msg.c_str());
}
void add_to_search_path(const std::string &path) {
libpd_add_to_search_path(path.c_str());
}
void open_patch(const std::string &name, const std::string &dir) {
libpd_openfile(name.c_str(), dir.c_str());
}
void close_patch(const std::string &name) {
const std::string &recv = std::string("pd-") + name;
libpd_start_message(1);
libpd_add_float(1);
libpd_finish_message(recv.c_str(), "menuclose");
}
EMSCRIPTEN_BINDINGS(my_module) {
function("send_bang", &send_bang);
function("send_float", &send_float);
function("send_symbol", &send_symbol);
function("start_message", &start_message);
function("add_float", &add_float);
function("add_symbol", &add_symbol);
function("finish_list", &finish_list);
function("finish_message", &finish_message);
function("add_to_search_path", &add_to_search_path);
function("open_patch", &open_patch);
function("close_patch", &close_patch);
}
void main_loop(void) {
}
SDL_AudioDeviceID open_audio_device(bool is_audio_in, SDL_AudioSpec &desired, SDL_AudioSpec &obtained) {
desired.freq = samplerate;
desired.format = AUDIO_F32;
desired.channels = is_audio_in ? num_in_channels : num_out_channels;
desired.samples = buffersize;
desired.callback = is_audio_in ? audio_in : audio_out;
SDL_AudioDeviceID device_id = SDL_OpenAudioDevice(nullptr, is_audio_in, &desired, &obtained, SDL_AUDIO_ALLOW_ANY_CHANGE);
const char *type_str = is_audio_in ? "audio in" : "audio out";
if (!device_id) {
SDL_Log("%s: failed to open device: %s", type_str, SDL_GetError());
}
else {
if (desired.freq != obtained.freq) {
SDL_Log("%s: desired sample rate was %d, but obtained %d", type_str, desired.freq, obtained.freq);
}
if (desired.format != obtained.format) {
SDL_Log("%s: desired format was %d, but obtained %d", type_str, desired.format, obtained.format);
}
if (desired.channels != obtained.channels) {
SDL_Log("%s: desired number of channels was %d, but obtained %d", type_str, desired.channels, obtained.channels);
}
if (desired.samples != obtained.samples) {
SDL_Log("%s: desired buffer size was %d, but obtained %d", type_str, desired.samples, obtained.samples);
}
}
return device_id;
}
void pause_audio_device(bool pauseOn) {
if (device_in) {
SDL_PauseAudioDevice(device_in, pauseOn);
}
if (device_out) {
SDL_PauseAudioDevice(device_out, pauseOn);
}
}
void close_audio_device() {
if (device_in) {
SDL_CloseAudioDevice(device_in);
}
if (device_out) {
SDL_CloseAudioDevice(device_out);
}
}
int main(int argc, char **argv) {
/* initialize audio */
SDL_Init(SDL_INIT_AUDIO);
atexit(SDL_Quit);
SDL_AudioSpec desired, obtained;
if (num_in_channels) {
device_in = open_audio_device(true, desired, obtained);
num_in_channels = obtained.channels;
samplerate = obtained.freq;
buffersize = obtained.samples;
ticks_per_buffer = buffersize / blocksize;
}
if (num_out_channels) {
device_out = open_audio_device(false, desired, obtained);
num_out_channels = obtained.channels;
samplerate = obtained.freq;
buffersize = obtained.samples;
ticks_per_buffer = buffersize / blocksize;
}
in_buffer = new float[num_in_channels * buffersize];
/* initialize libpd */
libpd_init();
libpd_start_gui(const_cast<char *>("/"));
libpd_init_audio(num_in_channels, num_out_channels, samplerate);
/* add internals help path */
const char *internals_path = "purr-data/doc/5.reference";
libpd_add_to_help_path(internals_path);
/* add externals search/help path */
const char *externals_path = "purr-data/extra";
libpd_add_to_search_path(externals_path);
libpd_add_to_help_path(externals_path);
DIR *ext_dir = opendir(externals_path);
if (ext_dir) {
struct dirent *dir;
while ((dir = readdir(ext_dir)) != nullptr) {
if (dir->d_name[0] != '.') {
const std::string &path = std::string(externals_path) + "/" + std::string(dir->d_name);
libpd_add_to_search_path(path.c_str());
libpd_add_to_help_path(path.c_str());
}
}
closedir(ext_dir);
}
/* start audio processing */
pause_audio_device(false);
emscripten_set_main_loop(main_loop, 0, true);
/* stop audio processing & close audio device */
pause_audio_device(true);
close_audio_device();
if (in_buffer != nullptr) {
delete[] in_buffer;
in_buffer = nullptr;
}
return 0;
}
main.data
main.html
main.js
main.wasm
\ No newline at end of file
This diff is collapsed.
/*
* Copyright (c) 2020 Zack Lee (cuinjune@gmail.com)
* Copyright (c) 2020 Purr Data team
*
* For information on usage and redistribution, and for a DISCLAIMER OF ALL
* WARRANTIES, see the file, "LICENSE.txt," in this distribution.
*
* See https://git.purrdata.net/jwilkes/purr-data for documentation
*
*/
#include "js.hpp"
#include <emscripten.h>
// EM_JS functions are exposed to the global scope in javascript
// we use a double underscore prefix to prevent potential namespace collision
EM_JS(void, __mainInit, (void), {
if (typeof Module.mainInit === "function") {
Module.mainInit();
}
else {
console.error("couldn't find the javascript function 'Module.mainInit'");
}
});
EM_JS(void, __mainLoop, (void), {
if (typeof Module.mainLoop === "function") {
Module.mainLoop();
}
else {
console.error("couldn't find the javascript function 'Module.mainLoop'");
}
});
EM_JS(void, __mainExit, (void), {
if (typeof Module.mainExit === "function") {
Module.mainExit();
}
else {
console.error("couldn't find the javascript function 'Module.mainExit'");
}
});
EM_JS(void, __Pd_reinit, (int newinchan, int newoutchan, int newrate), {
if (typeof Module.Pd.reinit === "function") {
Module.Pd.reinit(newinchan, newoutchan, newrate);
}
else {
console.error("couldn't find the javascript function 'Module.Pd.reinit'");
}
});
EM_JS(void, __Pd_openMidi, (int nmidiin, int *midiinvec,
int nmidiout, int *midioutvec), {
if (typeof Module.Pd.openMidi === "function") {
var midiinarr = [];
for (let i = 0; i < nmidiin; i++) {
midiinarr.push(HEAP32[(midiinvec >> 2) + i]);
}
var midioutarr = [];
for (let i = 0; i < nmidiout; i++) {
midioutarr.push(HEAP32[(midioutvec >> 2) + i]);
}
Module.Pd.openMidi(midiinarr, midioutarr);
}
else {
console.error("couldn't find the javascript function 'Module.Pd.openMidi'");
}
});
EM_JS(const char *, __Pd_getMidiInDeviceName, (int devno), {
if (typeof Module.Pd.getMidiInDeviceName === "function") {
return Module.Pd.getMidiInDeviceName(devno);
}
else {
console.error("couldn't find the javascript function 'Module.Pd.getMidiInDeviceName'");
}
});
EM_JS(const char *, __Pd_getMidiOutDeviceName, (int devno), {
if (typeof Module.Pd.getMidiOutDeviceName === "function") {
return Module.Pd.getMidiOutDeviceName(devno);
}
else {
console.error("couldn't find the javascript function 'Module.Pd.getMidiOutDeviceName'");
}
});
EM_JS(int, __Pd_loadLib, (const char *filename, const char *symname), {
return Asyncify.handleAsync(async () => {
try {
await loadDynamicLibrary(UTF8ToString(filename), {loadAsync: true, global: true, nodelete: true, fs: FS});
var makeout = Module['_' + UTF8ToString(symname)];
if (typeof makeout === "function") {
makeout();
return 1; // success
}
else {
return -1; // couldn't find the function
}
}
catch (error) {
console.error(error);
return 0; // couldn't load the external
}
});
});
EM_JS(void, __Pd_receiveCommandBuffer, (char *buf), {
if (typeof Module.Pd.receiveCommandBuffer === "function") {
Module.Pd.receiveCommandBuffer(intArrayFromString(UTF8ToString(buf), true));
}
else {
console.error("couldn't find the javascript function 'Module.Pd.receiveCommandBuffer'");
}
});
EM_JS(void, __Pd_receivePrint, (const char *s), {
if (typeof Module.Pd.receivePrint === "function") {
Module.Pd.receivePrint(UTF8ToString(s));
}
else {
console.error("couldn't find the javascript function 'Module.Pd.receivePrint'");
}
});
EM_JS(void, __Pd_receiveBang, (const char *source), {
if (typeof Module.Pd.receiveBang === "function") {
Module.Pd.receiveBang(UTF8ToString(source));
}
else {
console.error("couldn't find the javascript function 'Module.Pd.receiveBang'");
}
});
EM_JS(void, __Pd_receiveFloat, (const char *source, float value), {
if (typeof Module.Pd.receiveFloat === "function") {
Module.Pd.receiveFloat(UTF8ToString(source), value);
}
else {
console.error("couldn't find the javascript function 'Module.Pd.receiveFloat'");
}
});
EM_JS(void, __Pd_receiveSymbol, (const char *source, const char *symbol), {
if (typeof Module.Pd.receiveSymbol === "function") {
Module.Pd.receiveSymbol(UTF8ToString(source), UTF8ToString(symbol));
}
else {
console.error("couldn't find the javascript function 'Module.Pd.receiveSymbol'");
}
});
EM_JS(void, __Pd_receiveList, (const char *source, const char *list), {
if (typeof Module.Pd.receiveList === "function") {
var str = UTF8ToString(list);
var arr = str.split(",").map(item => isNaN(+item) ? item : +item);
Module.Pd.receiveList(UTF8ToString(source), arr);
}
else {
console.error("couldn't find the javascript function 'Module.Pd.receiveList'");
}
});
EM_JS(void, __Pd_receiveMessage, (const char *source, const char *symbol,
const char *list), {
if (typeof Module.Pd.receiveMessage === "function") {
var str = UTF8ToString(list);
var arr = str.split(",").map(item => isNaN(+item) ? item : +item);
Module.Pd.receiveMessage(UTF8ToString(source), UTF8ToString(symbol), arr);
}
else {
console.error("couldn't find the javascript function 'Module.Pd.receiveMessage'");
}
});
EM_JS(void, __Pd_receiveNoteOn, (int channel, int pitch, int velocity), {
if (typeof Module.Pd.receiveNoteOn === "function") {
Module.Pd.receiveNoteOn(channel, pitch, velocity);
}
else {
console.error("couldn't find the javascript function 'Module.Pd.receiveNoteOn'");
}
});
EM_JS(void, __Pd_receiveControlChange, (int channel, int controller, int value), {
if (typeof Module.Pd.receiveControlChange === "function") {
Module.Pd.receiveControlChange(channel, controller, value);
}
else {
console.error("couldn't find the javascript function 'Module.Pd.receiveControlChange'");
}
});
EM_JS(void, __Pd_receiveProgramChange, (int channel, int value), {
if (typeof Module.Pd.receiveProgramChange === "function") {
Module.Pd.receiveProgramChange(channel, value);
}
else {
console.error("couldn't find the javascript function 'Module.Pd.receiveProgramChange'");
}
});
EM_JS(void, __Pd_receivePitchBend, (int channel, int value), {
if (typeof Module.Pd.receivePitchBend === "function") {
Module.Pd.receivePitchBend(channel, value);
}
else {
console.error("couldn't find the javascript function 'Module.Pd.receivePitchBend'");
}
});
EM_JS(void, __Pd_receiveAftertouch, (int channel, int value), {
if (typeof Module.Pd.receiveAftertouch === "function") {
Module.Pd.receiveAftertouch(channel, value);
}
else {
console.error("couldn't find the javascript function 'Module.Pd.receiveAftertouch'");
}
});
EM_JS(void, __Pd_receivePolyAftertouch, (int channel, int pitch, int value), {
if (typeof Module.Pd.receivePolyAftertouch === "function") {
Module.Pd.receivePolyAftertouch(channel, pitch, value);
}
else {
console.error("couldn't find the javascript function 'Module.Pd.receivePolyAftertouch'");
}
});
EM_JS(void, __Pd_receiveMidiByte, (int port, int byte), {
if (typeof Module.Pd.receiveMidiByte === "function") {
Module.Pd.receiveMidiByte(port, byte);
}
else {
console.error("couldn't find the javascript function 'Module.Pd.receiveMidiByte'");
}
});
/*
* Copyright (c) 2020 Zack Lee (cuinjune@gmail.com)
* Copyright (c) 2020 Purr Data team