diff --git a/include/rnnoise.h b/include/rnnoise.h index 953fe6f6..ef81283d 100644 --- a/include/rnnoise.h +++ b/include/rnnoise.h @@ -102,6 +102,13 @@ RNNOISE_EXPORT float rnnoise_process_frame(DenoiseState* st, */ RNNOISE_EXPORT RNNModel* rnnoise_model_from_file(FILE* f); + /** + * Load a model from a string + * + * It must be deallocated with rnnoise_model_free() + */ +RNNOISE_EXPORT RNNModel* rnnoise_model_from_string(const char* s); + /** * Free a custom model * diff --git a/src/rnn_reader.c b/src/rnn_reader.c index 2dbbc366..f01149ce 100644 --- a/src/rnn_reader.c +++ b/src/rnn_reader.c @@ -142,6 +142,116 @@ RNNModel* rnnoise_model_from_file(FILE* f) { return ret; } +RNNModel* rnnoise_model_from_string(const char* s) { + int i, in; + int n; + + if (sscanf(s, "rnnoise-nu model file version %d\n%n", &in, &n) != 1 || in != 1) + return NULL; + s += n; + + RNNModel* ret = calloc(1, sizeof(RNNModel)); + if (!ret) + return NULL; + +#undef ALLOC_LAYER +#define ALLOC_LAYER(type, name) \ + type* name; \ + name = calloc(1, sizeof(type)); \ + if (!name) { \ + rnnoise_model_free(ret); \ + return NULL; \ + } \ + ret->name = name + + ALLOC_LAYER(DenseLayer, input_dense); + ALLOC_LAYER(GRULayer, vad_gru); + ALLOC_LAYER(GRULayer, noise_gru); + ALLOC_LAYER(GRULayer, denoise_gru); + ALLOC_LAYER(DenseLayer, denoise_output); + ALLOC_LAYER(DenseLayer, vad_output); + +#undef INPUT_VAL +#define INPUT_VAL(name) \ + do { \ + if (sscanf(s, "%d%n", &in, &n) != 1 || in < 0 || in > 128) { \ + rnnoise_model_free(ret); \ + return NULL; \ + } \ + s += n; \ + name = in; \ + } while (0) + +#undef INPUT_ACTIVATION +#define INPUT_ACTIVATION(name) \ + do { \ + int activation; \ + INPUT_VAL(activation); \ + switch (activation) { \ + case F_ACTIVATION_SIGMOID: \ + name = ACTIVATION_SIGMOID; \ + break; \ + case F_ACTIVATION_RELU: \ + name = ACTIVATION_RELU; \ + break; \ + default: \ + name = ACTIVATION_TANH; \ + } \ + } while (0) + +#undef INPUT_ARRAY +#define INPUT_ARRAY(name, len) \ + do { \ + rnn_weight* values = malloc((len) * sizeof(rnn_weight)); \ + if (!values) { \ + rnnoise_model_free(ret); \ + return NULL; \ + } \ + name = values; \ + for (i = 0; i < (len); i++) { \ + if (sscanf(s, "%d%n", &in, &n) != 1) { \ + rnnoise_model_free(ret); \ + return NULL; \ + } \ + s += n; \ + values[i] = in; \ + } \ + } while (0) + +#undef INPUT_DENSE +#define INPUT_DENSE(name) \ + do { \ + INPUT_VAL(name->nb_inputs); \ + INPUT_VAL(name->nb_neurons); \ + ret->name##_size = name->nb_neurons; \ + INPUT_ACTIVATION(name->activation); \ + INPUT_ARRAY(name->input_weights, name->nb_inputs * name->nb_neurons); \ + INPUT_ARRAY(name->bias, name->nb_neurons); \ + } while (0) + +#undef INPUT_GRU +#define INPUT_GRU(name) \ + do { \ + INPUT_VAL(name->nb_inputs); \ + INPUT_VAL(name->nb_neurons); \ + ret->name##_size = name->nb_neurons; \ + INPUT_ACTIVATION(name->activation); \ + INPUT_ARRAY(name->input_weights, name->nb_inputs * name->nb_neurons * 3); \ + INPUT_ARRAY(name->recurrent_weights, \ + name->nb_neurons * name->nb_neurons * 3); \ + INPUT_ARRAY(name->bias, name->nb_neurons * 3); \ + } while (0) + + INPUT_DENSE(input_dense); + INPUT_GRU(vad_gru); + INPUT_GRU(noise_gru); + INPUT_GRU(denoise_gru); + INPUT_DENSE(denoise_output); + INPUT_DENSE(vad_output); + + return ret; +} + void rnnoise_model_free(RNNModel* model) { #define FREE_MAYBE(ptr) \ do { \