From: Paul Brossier Date: Sun, 27 Oct 2013 11:44:29 +0000 (+0100) Subject: Merge branch 'develop' of aubio.org:/git/aubio/aubio into wavetable X-Git-Tag: 0.4.0-beta1~110 X-Git-Url: https://git.aubio.org/?p=aubio.git;a=commitdiff_plain;h=dc467b5d5a7c321235d4defb3ce8541e70460fc9;hp=7fc5ba23e5afdc3bbafb180aa6736958304c5b77 Merge branch 'develop' of aubio.org:/git/aubio/aubio into wavetable --- diff --git a/src/aubio.h b/src/aubio.h index 59133d1d..5f8b518a 100644 --- a/src/aubio.h +++ b/src/aubio.h @@ -186,6 +186,7 @@ extern "C" #include "io/sink.h" #include "io/audio_unit.h" #include "synth/sampler.h" +#include "synth/wavetable.h" #if AUBIO_UNSTABLE #include "mathutils.h" diff --git a/src/synth/wavetable.c b/src/synth/wavetable.c new file mode 100644 index 00000000..b89af1e3 --- /dev/null +++ b/src/synth/wavetable.c @@ -0,0 +1,213 @@ +/* + Copyright (C) 2003-2013 Paul Brossier + + This file is part of aubio. + + aubio is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + aubio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with aubio. If not, see . + +*/ + + +#include "config.h" +#include "aubio_priv.h" +#include "fvec.h" +#include "fmat.h" +#include "io/source.h" +#include "synth/wavetable.h" + +#define WAVETABLE_LEN 4096 + +struct _aubio_wavetable_t { + uint_t samplerate; + uint_t blocksize; + uint_t wavetable_length; + fvec_t *wavetable; + uint_t playing; + smpl_t last_pos; + + smpl_t target_freq; + smpl_t freq; + smpl_t inc_freq; + + smpl_t target_amp; + smpl_t amp; + smpl_t inc_amp; +}; + +aubio_wavetable_t *new_aubio_wavetable(uint_t samplerate, uint_t blocksize) +{ + aubio_wavetable_t *s = AUBIO_NEW(aubio_wavetable_t); + uint_t i = 0; + s->samplerate = samplerate; + s->blocksize = blocksize; + s->wavetable_length = WAVETABLE_LEN; + s->wavetable = new_fvec(s->wavetable_length + 3); + for (i = 0; i < s->wavetable_length; i++) { + s->wavetable->data[i] = SIN(TWO_PI * i / (smpl_t) s->wavetable_length ); + } + s->wavetable->data[s->wavetable_length] = s->wavetable->data[0]; + s->wavetable->data[s->wavetable_length + 1] = s->wavetable->data[1]; + s->wavetable->data[s->wavetable_length + 2] = s->wavetable->data[2]; + s->playing = 0; + s->last_pos = 0.; + s->freq = 0.; + s->target_freq = 0.; + s->inc_freq = 0.; + + s->amp = 0.; + s->target_amp = 0.; + s->inc_amp = 0.; + return s; +} + +static smpl_t interp_2(fvec_t *input, smpl_t pos) { + uint_t idx = (uint_t)FLOOR(pos); + smpl_t frac = pos - (smpl_t)idx; + smpl_t a = input->data[idx]; + smpl_t b = input->data[idx + 1]; + return a + frac * ( b - a ); +} + +void aubio_wavetable_do ( aubio_wavetable_t * s, fvec_t * input, fvec_t * output) +{ + uint_t i; + if (s->playing) { + smpl_t pos = s->last_pos; + for (i = 0; i < output->length; i++) { + if (s->freq != s->target_freq) + s->freq += s->inc_freq; + smpl_t inc = s->freq * (smpl_t)(s->wavetable_length) / (smpl_t) (s->samplerate); + pos += inc; + while (pos > s->wavetable_length) { + pos -= s->wavetable_length; + } + if ( ABS(s->amp - s->target_amp) > ABS(s->inc_amp) ) + s->amp += s->inc_amp; + else + s->amp = s->target_amp; + output->data[i] = s->amp * interp_2(s->wavetable, pos); + } + s->last_pos = pos; + } else { + fvec_set(output, 0.); + } + // add input to output if needed + if (input && input != output) { + for (i = 0; i < output->length; i++) { + output->data[i] += input->data[i]; + } + } +} + +void aubio_wavetable_do_multi ( aubio_wavetable_t * s, fmat_t * input, fmat_t * output) +{ + uint_t i, j; + if (s->playing) { + smpl_t pos = s->last_pos; + for (j = 0; j < output->length; j++) { + if (s->freq != s->target_freq) + s->freq += s->inc_freq; + smpl_t inc = s->freq * (smpl_t)(s->wavetable_length) / (smpl_t) (s->samplerate); + pos += inc; + while (pos > s->wavetable_length) { + pos -= s->wavetable_length; + } + for (i = 0; i < output->height; i++) { + output->data[i][j] = interp_2(s->wavetable, pos); + } + } + s->last_pos = pos; + } else { + for (j = 0; j < output->length; j++) { + if (s->freq != s->target_freq) + s->freq += s->inc_freq; + } + fmat_set(output, 0.); + } + // add output to input if needed + if (input && input != output) { + for (i = 0; i < output->height; i++) { + for (j = 0; j < output->length; j++) { + output->data[i][j] += input->data[i][j]; + } + } + } +} + +uint_t aubio_wavetable_get_playing ( aubio_wavetable_t * s ) +{ + return s->playing; +} + +uint_t aubio_wavetable_set_playing ( aubio_wavetable_t * s, uint_t playing ) +{ + s->playing = (playing == 1) ? 1 : 0; + return 0; +} + +uint_t aubio_wavetable_play ( aubio_wavetable_t * s ) +{ + aubio_wavetable_set_amp (s, 0.7); + return aubio_wavetable_set_playing (s, 1); +} + +uint_t aubio_wavetable_stop ( aubio_wavetable_t * s ) +{ + //aubio_wavetable_set_freq (s, 0.); + aubio_wavetable_set_amp (s, 0.); + //s->last_pos = 0; + return aubio_wavetable_set_playing (s, 1); +} + +uint_t aubio_wavetable_set_freq ( aubio_wavetable_t * s, smpl_t freq ) +{ + if (freq >= 0 && freq < s->samplerate / 2.) { + uint_t steps = 10; + s->inc_freq = (freq - s->freq) / steps; + s->target_freq = freq; + return 0; + } else { + return 1; + } +} + +smpl_t aubio_wavetable_get_freq ( aubio_wavetable_t * s) { + return s->freq; +} + +uint_t aubio_wavetable_set_amp ( aubio_wavetable_t * s, smpl_t amp ) +{ + AUBIO_MSG("amp: %f, s->amp: %f, target_amp: %f, inc_amp: %f\n", + amp, s->amp, s->target_amp, s->inc_amp); + if (amp >= 0. && amp < 1.) { + uint_t steps = 100; + s->inc_amp = (amp - s->amp) / steps; + s->target_amp = amp; + AUBIO_ERR("amp: %f, s->amp: %f, target_amp: %f, inc_amp: %f\n", + amp, s->amp, s->target_amp, s->inc_amp); + return 0; + } else { + return 1; + } +} + +smpl_t aubio_wavetable_get_amp ( aubio_wavetable_t * s) { + return s->amp; +} + +void del_aubio_wavetable( aubio_wavetable_t * s ) +{ + del_fvec(s->wavetable); + AUBIO_FREE(s); +} diff --git a/src/synth/wavetable.h b/src/synth/wavetable.h new file mode 100644 index 00000000..aaf2a2e0 --- /dev/null +++ b/src/synth/wavetable.h @@ -0,0 +1,179 @@ +/* + Copyright (C) 2003-2013 Paul Brossier + + This file is part of aubio. + + aubio is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + aubio is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with aubio. If not, see . + +*/ + +#ifndef _AUBIO_SYNTH_WAVETABLE_H +#define _AUBIO_SYNTH_WAVETABLE_H + +/** \file + + Load and play sound files. + + This file loads a sample and gets ready to play it. + + The `_do` function adds the new samples to the input, and write the result as + the output. + + \example synth/test-wavetable.c + +*/ + +#ifdef __cplusplus +extern "C" { +#endif + +/** wavetable object */ +typedef struct _aubio_wavetable_t aubio_wavetable_t; + +/** create new wavetable object + + \param samplerate the sampling rate of the new wavetable + + \return the newly created ::aubio_wavetable_t + +*/ +aubio_wavetable_t * new_aubio_wavetable(uint_t samplerate, uint_t hop_size); + +/** load source in wavetable + + \param o wavetable, created by ::new_aubio_wavetable + \param uri the uri of the source to load + + \return 0 if successful, non-zero otherwise + +*/ +uint_t aubio_wavetable_load( aubio_wavetable_t * o, char_t * uri ); + +/** process wavetable function + + \param o wavetable, created by ::new_aubio_wavetable + \param input input of the wavetable, to be added to the output + \param output output of the wavetable + +This function adds the new samples from the playing sample to the output. + +If `input` is not NULL and different from `output`, then the samples from `input` +are added to the output. + +*/ +void aubio_wavetable_do ( aubio_wavetable_t * o, fvec_t * input, fvec_t * output); + +/** process wavetable function, multiple channels + + \param o wavetable, created by ::new_aubio_wavetable + \param input input of the wavetable, to be added to the output + \param output output of the wavetable + +This function adds the new samples from the playing sample to the output. + +If `input` is not NULL and different from `output`, then the samples from `input` +are added to the output. + +*/ +void aubio_wavetable_do_multi ( aubio_wavetable_t * o, fmat_t * input, fmat_t * output); + +/** get current playing state + + \param o wavetable, created by ::new_aubio_wavetable + + \return 0 if not playing, 1 if playing + +*/ +uint_t aubio_wavetable_get_playing ( aubio_wavetable_t * o ); + +/** set current playing state + + \param o wavetable, created by ::new_aubio_wavetable + \param playing 0 for not playing, 1 for playing + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_wavetable_set_playing ( aubio_wavetable_t * o, uint_t playing ); + +/** play sample from start + + \param o wavetable, created by ::new_aubio_wavetable + \param playing 0 for not playing, 1 for playing + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_wavetable_play ( aubio_wavetable_t * o ); + +/** stop sample from start + + \param o wavetable, created by ::new_aubio_wavetable + \param playing 0 for not playing, 1 for playing + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_wavetable_stop ( aubio_wavetable_t * o ); + +/** set wavetable frequency + + \param o wavetable, created by ::new_aubio_wavetable + \param playing 0 for not playing, 1 for playing + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_wavetable_set_freq ( aubio_wavetable_t * o, smpl_t freq ); + +/** get wavetable frequency + + \param o wavetable, created by ::new_aubio_wavetable + + \return current frequency, in Hz + +*/ +smpl_t aubio_wavetable_get_freq ( aubio_wavetable_t * o); + +/** set wavetable amplitude + + \param o wavetable, created by ::new_aubio_wavetable + \param playing 0 for not playing, 1 for playing + + \return 0 if successful, 1 otherwise + +*/ +uint_t aubio_wavetable_set_amp ( aubio_wavetable_t * o, smpl_t amp ); + +/** get wavetable amplitude + + \param o wavetable, created by ::new_aubio_wavetable + + \return current amplitude + +*/ +smpl_t aubio_wavetable_get_amp ( aubio_wavetable_t * o); + +/** destroy ::aubio_wavetable_t object + + \param o wavetable, created by ::new_aubio_wavetable + +*/ +void del_aubio_wavetable( aubio_wavetable_t * o ); + +#ifdef __cplusplus +} +#endif + +#endif /* _AUBIO_SYNTH_WAVETABLE_H */ diff --git a/tests/src/synth/test-wavetable.c b/tests/src/synth/test-wavetable.c new file mode 100644 index 00000000..f569277c --- /dev/null +++ b/tests/src/synth/test-wavetable.c @@ -0,0 +1,68 @@ +#include +#include "utils_tests.h" + +int main (int argc, char **argv) +{ + sint_t err = 0; + + if (argc < 2) { + err = 2; + PRINT_ERR("not enough arguments\n"); + PRINT_MSG("usage: %s [freq] [samplerate]\n", argv[0]); + return err; + } + + uint_t samplerate = 44100; // default is the samplerate of input_path + uint_t hop_size = 256; + smpl_t freq = 440.; + + char_t *sink_path = argv[1]; + if ( argc == 4 ) samplerate = atoi(argv[3]); + if ( argc == 3 ) freq = atof(argv[2]); + + fvec_t *vec = new_fvec(hop_size); + aubio_sink_t *sink = new_aubio_sink(sink_path, samplerate); + + aubio_wavetable_t * wavetable = new_aubio_wavetable (samplerate, hop_size); + + // 2 seconds duration, in samples + uint_t duration = 2 * samplerate; + + uint_t n_frames = 0; + uint_t write = 0; + + aubio_wavetable_play (wavetable); + aubio_wavetable_set_freq ( wavetable, freq ); + + uint_t region = 0; + + do { + if ( n_frames > duration / 3 && region < 1) { + aubio_wavetable_stop (wavetable); + region++; + } + if ( n_frames > 2 * duration / 3 && region < 2) { + aubio_wavetable_play (wavetable); + aubio_wavetable_set_freq ( wavetable, freq / 2.); + region++; + } + if (duration - n_frames < hop_size * 2 ) { + aubio_wavetable_stop( wavetable ); + } + if (duration - n_frames < hop_size ) { + write = duration - n_frames; + } else { + write = hop_size; + } + aubio_wavetable_do (wavetable, vec, vec); + aubio_sink_do(sink, vec, write); + n_frames += hop_size; + } while ( n_frames <= duration ); + + del_aubio_wavetable (wavetable); + del_aubio_sink(sink); + del_fvec(vec); + aubio_cleanup(); + + return 0; +}