Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ jobs:
# ===============================================================
# ===============================================================
Linux-amd64:
runs-on: ubuntu-latest
runs-on: ubuntu-22.04
steps:
- uses: actions/checkout@v6
with:
Expand Down Expand Up @@ -41,7 +41,7 @@ jobs:
# ===============================================================
# ===============================================================
Linux-arm64:
runs-on: ubuntu-24.04-arm
runs-on: ubuntu-22.04-arm
steps:
- uses: actions/checkout@v6
with:
Expand Down
6 changes: 6 additions & 0 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,9 @@
[submodule "dep/pd-lib-builder"]
path = dep/pd-lib-builder
url = https://github.com/pure-data/pd-lib-builder.git
[submodule "dep/json"]
path = dep/json
url = https://github.com/nlohmann/json.git
[submodule "dep/MicroNAM"]
path = dep/MicroNAM
url = https://github.com/jaffco/MicroNAM.git
14 changes: 9 additions & 5 deletions Makefile
Original file line number Diff line number Diff line change
@@ -1,15 +1,18 @@
lib.name = pdnam~

class.sources = pdnam~.cpp
lib.name = PDNAM

datafiles = \
pdnam~-help.pd \
pdnam+~-help.pd \
README.md \
LICENSE

datadirs = data/

common.sources = \
# pdnam~ only needs its own source (header-only MicroNAM + nlohmann/json)
pdnam~.class.sources = pdnam~.cpp

# pdnam+~ needs NeuralAudio implementation
pdnam+~.class.sources = pdnam+~.cpp \
dep/NeuralAudio/NeuralAudio/NeuralModel.cpp \
dep/NeuralAudio/NeuralAudio/RTNeuralLoader.cpp \
dep/NeuralAudio/deps/RTNeural/RTNeural/RTNeural.cpp \
Expand All @@ -32,7 +35,8 @@ cflags = -std=gnu++20 -DNAM_SAMPLE_FLOAT -DDSP_SAMPLE_FLOAT \
-Idep/NeuralAudio/deps/RTNeural/modules/json \
-Idep/NeuralAudio/deps/RTNeural-NAM \
-Idep/NeuralAudio/deps/RTNeural-NAM/wavenet \
-Idep/NeuralAudio/deps/RTNeural/modules/xsimd/include
-Idep/NeuralAudio/deps/RTNeural/modules/xsimd/include \
-Idep/MicroNAM -Idep/json/single_include

define forDarwin
cflags += -mmacosx-version-min=10.15
Expand Down
12 changes: 8 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,17 @@
# PDNAM

Basic [NAM](https://www.neuralampmodeler.com/) loader external for Pure Data. The external is deliberatily minimal with no additional options. The only argument is a relative path to a model file to load.
Basic [NAM](https://www.neuralampmodeler.com/) loader externals for Pure Data. These externals are deliberately minimal with no additional options. The only argument is a relative path to a model file to load.

It uses the [NeuralAudio](https://github.com/mikeoliphant/NeuralAudio) implementation and supports the optimized WaveNet and LSTM architectures. And it can load `.nam`, `.json` and `.aidax` files.
`pdnam~` uses the [MicroNAM](https://github.com/jaffco/MicroNAM) implementation and supports the four WaveNet model types: Nano, Feather, Lite, and Standard.

Two example models are included from the NeuralAudio library.
`pdnam+~` uses the [NeuralAudio](https://github.com/mikeoliphant/NeuralAudio) implementation and supports both the optimized WaveNet and LSTM architectures.

Several example models are included for testing.

## License

This project is released under the MIT license.
This project is released under the MIT license. As are MicroNAM and NeuralAudio.

"BossWN" WaveNet models come from NeuralAudio and are under MIT.

The "tw40_blues_deluxe_deerinkstudios.json" and "Tomato_Preamp_Maximum_Gain.aidax" models are under the CC BY-NC-ND 4.0 license.
1 change: 1 addition & 0 deletions data/BossWN-feather.nam

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions data/BossWN-standard.nam

Large diffs are not rendered by default.

1 change: 1 addition & 0 deletions dep/MicroNAM
Submodule MicroNAM added at 83dd7d
1 change: 1 addition & 0 deletions dep/json
Submodule json added at 98386e
33 changes: 33 additions & 0 deletions pdnam+~-help.pd
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
#N canvas 978 285 524 488 10;
#X obj 52 297 dac~;
#X obj 51 18 noise~;
#X obj 51 87 *~;
#X obj 118 39 line~;
#X msg 118 16 1 \, 0 300;
#X obj 119 86 *~;
#X obj 186 38 line~;
#X msg 186 15 1 \, 0 300;
#X obj 182 85 *~;
#X obj 249 37 line~;
#X msg 249 14 1 \, 0 300;
#X obj 52 154 pdnam+~ data/tw40_blues_deluxe_deerinkstudios.json;
#X obj 120 184 pdnam+~ data/BossWN-nano.nam;
#X obj 181 216 pdnam+~ data/Tomato_Preamp_Maximum_Gain.aidax;
#X connect 1 0 2 0;
#X connect 1 0 5 0;
#X connect 1 0 8 0;
#X connect 2 0 11 0;
#X connect 3 0 2 1;
#X connect 4 0 3 0;
#X connect 5 0 12 0;
#X connect 6 0 5 1;
#X connect 7 0 6 0;
#X connect 8 0 13 0;
#X connect 9 0 8 1;
#X connect 10 0 9 0;
#X connect 11 0 0 0;
#X connect 11 0 0 1;
#X connect 12 0 0 0;
#X connect 12 0 0 1;
#X connect 13 0 0 0;
#X connect 13 0 0 1;
110 changes: 110 additions & 0 deletions pdnam+~.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
// PDNAM
// Copyright (C) 2026 Wasted Audio
//
// SPDX-License-Identifier: MIT

#include "m_pd.h"
#include "g_canvas.h"
#include "NeuralAudio/NeuralModel.h"
#include <iostream>
#include <string>
#include <filesystem>

static t_class *pdnam_plus_tilde_class;

typedef struct _pdnam_plus_tilde {
t_object x_obj;
t_sample f;
t_canvas* canvas;
NeuralAudio::NeuralModel* model;
} t_pdnam_plus_tilde;

static void pdnam_plus_tilde_load(t_pdnam_plus_tilde *x, t_symbol *model_path)
{
if (x->model) {
delete x->model;
x->model = nullptr;
}

if (model_path == &s_) return;

// Resolve relative path
char buf[MAXPDSTRING];
canvas_makefilename(x->canvas, model_path->s_name, buf, MAXPDSTRING);
std::filesystem::path path(buf);

if (!std::filesystem::exists(path) || !std::filesystem::is_regular_file(path)) {
pd_error(x, "pdnam+~: model file not found: %s", buf);
return;
}

try {
x->model = NeuralAudio::NeuralModel::CreateFromFile(path);
if (x->model) {
post("pdnam+~: loaded model %s", model_path->s_name);
} else {
pd_error(x, "pdnam+~: failed to load model %s", model_path->s_name);
}
} catch (const std::exception& e) {
pd_error(x, "pdnam+~: exception loading model: %s", e.what());
} catch (...) {
pd_error(x, "pdnam+~: unknown exception loading model");
}
}

void *pdnam_plus_tilde_new(t_symbol *s, int argc, t_atom *argv)
{
t_pdnam_plus_tilde *x = (t_pdnam_plus_tilde *)pd_new(pdnam_plus_tilde_class);
x->canvas = canvas_getcurrent();
x->model = nullptr;

t_symbol* model_path = (argc >= 1 && argv[0].a_type == A_SYMBOL)
? atom_getsymbol(&argv[0])
: &s_;

pdnam_plus_tilde_load(x, model_path);

outlet_new(&x->x_obj, &s_signal);

return (void *)x;
}

t_int *pdnam_plus_tilde_perform(t_int *w)
{
t_pdnam_plus_tilde *x = (t_pdnam_plus_tilde *)(w[1]);
t_sample *in = (t_sample *)(w[2]);
t_sample *out = (t_sample *)(w[3]);
int n = (int)(w[4]);

if (x->model) {
x->model->Process(in, out, n);
} else {
while (n--) {
*out++ = *in++;
}
}

return (w+5);
}

void pdnam_plus_tilde_dsp(t_pdnam_plus_tilde *x, t_signal **sp)
{
dsp_add(pdnam_plus_tilde_perform, 4, x,
sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);
}

void pdnam_plus_tilde_free(t_pdnam_plus_tilde *x)
{
if (x->model) delete x->model;
}

extern "C" void setup_pdnam0x2b_tilde(void) {
pdnam_plus_tilde_class = class_new(gensym("pdnam+~"),
(t_newmethod)pdnam_plus_tilde_new,
(t_method)pdnam_plus_tilde_free, sizeof(t_pdnam_plus_tilde),
CLASS_DEFAULT, A_GIMME, 0);

class_addmethod(pdnam_plus_tilde_class,
(t_method)pdnam_plus_tilde_dsp, gensym("dsp"), A_CANT, 0);
CLASS_MAINSIGNALIN(pdnam_plus_tilde_class, t_pdnam_plus_tilde, f);
}
50 changes: 25 additions & 25 deletions pdnam~-help.pd
Original file line number Diff line number Diff line change
@@ -1,32 +1,32 @@
#N canvas 978 285 524 488 10;
#N canvas 1057 154 449 383 10;
#X obj 51 297 dac~;
#X obj 51 18 noise~;
#X obj 51 87 *~;
#X obj 118 39 line~;
#X msg 118 16 1 \, 0 300;
#X obj 119 86 *~;
#X obj 186 38 line~;
#X msg 186 15 1 \, 0 300;
#X obj 182 85 *~;
#X obj 249 37 line~;
#X msg 249 14 1 \, 0 300;
#X obj 51 155 pdnam~ data/tw40_blues_deluxe_deerinkstudios.json;
#X obj 120 184 pdnam~ data/BossWN-nano.nam;
#X obj 181 216 pdnam~ data/Tomato_Preamp_Maximum_Gain.aidax;
#X obj 51 16 noise~;
#X obj 50 86 *~;
#X obj 117 38 line~;
#X msg 117 15 1 \, 0 300;
#X obj 50 116 pdnam~ data/BossWN-nano.nam;
#X obj 109 86 *~;
#X obj 176 38 line~;
#X msg 176 15 1 \, 0 300;
#X obj 170 86 *~;
#X obj 237 38 line~;
#X msg 237 15 1 \, 0 300;
#X obj 109 156 pdnam~ data/BossWN-feather.nam;
#X obj 170 195 pdnam~ data/BossWN-standard.nam;
#X connect 1 0 2 0;
#X connect 1 0 5 0;
#X connect 1 0 8 0;
#X connect 2 0 11 0;
#X connect 1 0 6 0;
#X connect 1 0 9 0;
#X connect 2 0 5 0;
#X connect 3 0 2 1;
#X connect 4 0 3 0;
#X connect 5 0 12 0;
#X connect 6 0 5 1;
#X connect 7 0 6 0;
#X connect 8 0 13 0;
#X connect 9 0 8 1;
#X connect 10 0 9 0;
#X connect 11 0 0 0;
#X connect 11 0 0 1;
#X connect 5 0 0 0;
#X connect 5 0 0 1;
#X connect 6 0 12 0;
#X connect 7 0 6 1;
#X connect 8 0 7 0;
#X connect 9 0 13 0;
#X connect 10 0 9 1;
#X connect 11 0 10 0;
#X connect 12 0 0 0;
#X connect 12 0 0 1;
#X connect 13 0 0 0;
Expand Down
Loading