ChucK/Patches/Clip
From CSWiki
This patch for ChucK implements two signal processing UGens, for clipping and soft clipping sample values at a set level. The levels can be set separately for positive and negative values for asymmetric clipping. This is "free software". You may use it for whatever purpose you will.
Index: ugen_xxx.cpp =================================================================== RCS file: /cvs/chuck_dev/v2/ugen_xxx.cpp,v retrieving revision 1.59 diff -u -r1.59 ugen_xxx.cpp --- ugen_xxx.cpp 11 Sep 2006 22:48:50 -0000 1.59 +++ ugen_xxx.cpp 15 Oct 2006 21:06:23 -0000 @@ -38,6 +38,7 @@ #include <string.h> #include <sys/stat.h> #include <limits.h> +#include <float.h> #if defined(__CK_SNDFILE_NATIVE__) #include <sndfile.h> @@ -65,6 +66,8 @@ static t_CKUINT zerox_offset_data = 0; static t_CKUINT delayp_offset_data = 0; static t_CKUINT sndbuf_offset_data = 0; +static t_CKUINT clip_offset_data = 0; +static t_CKUINT softclip_offset_data = 0; Chuck_Type * g_t_dac = NULL; Chuck_Type * g_t_adc = NULL; @@ -653,6 +656,74 @@ if( !type_engine_import_class_end( env ) ) return FALSE; + // add clip + //! hard clip + //! clamp sample values to be between nlimit (<0) and plimit (>0) + //! both limits can be set simultaneously using 'limit' + // ------------------------------------------------------------ + // init as a base class: Clip + // ------------------------------------------------------------ + + if( !type_engine_import_ugen_begin( env, "Clip", "UGen", env->global(), + clip_ctor, clip_tick, NULL ) ) + return FALSE; + + func = make_new_mfun( "float", "limit", clip_ctrl_limit ); + func->add_arg( "float", "limit") ; + if (!type_engine_import_mfun( env, func ) ) goto error; + + func = make_new_mfun( "float", "plimit", clip_ctrl_plimit ); + func->add_arg( "float", "plimit") ; + if (!type_engine_import_mfun( env, func ) ) goto error; + + func = make_new_mfun( "float", "plimit", clip_cget_plimit ); + if (!type_engine_import_mfun( env, func ) ) goto error; + + func = make_new_mfun( "float", "nlimit", clip_ctrl_nlimit ); + func->add_arg( "float", "nlimit") ; + if (!type_engine_import_mfun( env, func ) ) goto error; + + func = make_new_mfun( "float", "nlimit", clip_cget_nlimit ); + if (!type_engine_import_mfun( env, func ) ) goto error; + + if( !type_engine_import_class_end( env ) ) + return FALSE; + + // add softclip + //! soft clip + //! process sample values using an increasing function which is + //! approximately linear near 0 and which always stays between + //! nlimit (<0) and plimit (>0) + //! both limits can be set simultaneously using 'limit' + // ------------------------------------------------------------ + // init as a base class: SoftClip + // ------------------------------------------------------------ + + if( !type_engine_import_ugen_begin( env, "SoftClip", "UGen", env->global(), + softclip_ctor, softclip_tick, NULL ) ) + return FALSE; + + func = make_new_mfun( "float", "limit", softclip_ctrl_limit ); + func->add_arg( "float", "limit") ; + if (!type_engine_import_mfun( env, func ) ) goto error; + + func = make_new_mfun( "float", "plimit", softclip_ctrl_plimit ); + func->add_arg( "float", "plimit") ; + if (!type_engine_import_mfun( env, func ) ) goto error; + + func = make_new_mfun( "float", "plimit", softclip_cget_plimit ); + if (!type_engine_import_mfun( env, func ) ) goto error; + + func = make_new_mfun( "float", "nlimit", softclip_ctrl_nlimit ); + func->add_arg( "float", "nlimit") ; + if (!type_engine_import_mfun( env, func ) ) goto error; + + func = make_new_mfun( "float", "nlimit", softclip_cget_nlimit ); + if (!type_engine_import_mfun( env, func ) ) goto error; + + if( !type_engine_import_class_end( env ) ) + return FALSE; + return TRUE; error: @@ -2234,3 +2305,159 @@ if( d->fd ) sndbuf_load( d, i ); RETURN->v_float = ( i > d->num_frames || i < 0 ) ? 0 : d->buffer[i]; } + +// clip + +struct Clip_Data +{ + SAMPLE plimit, nlimit; + Clip_Data( ) { + plimit = 1.0f; + nlimit = -1.0f; + } +}; + +CK_DLL_CTOR( clip_ctor ) +{ + // return data to be used later + OBJ_MEMBER_UINT( SELF, clip_offset_data ) = (t_CKUINT)new Clip_Data; +} + +CK_DLL_DTOR( clip_dtor ) +{ + delete (Clip_Data *)OBJ_MEMBER_UINT( SELF, clip_offset_data ); +} + +CK_DLL_TICK( clip_tick ) +{ + Clip_Data *d = (Clip_Data *)OBJ_MEMBER_UINT(SELF, clip_offset_data ); + if (in > d->plimit ) { + *out = d->plimit; + } else if (in < d->nlimit) { + *out = d->nlimit; + } else { + *out = in; + } + return TRUE; +} + +CK_DLL_CTRL( clip_ctrl_limit ) +{ + Clip_Data *d = (Clip_Data *)OBJ_MEMBER_UINT(SELF, clip_offset_data ); + d->plimit = GET_CK_FLOAT(ARGS); + if (d->plimit < 0.0f) { + d->plimit = 0.0f; + } + d->nlimit = -d->plimit; + RETURN->v_float = (t_CKFLOAT)d->plimit; +} + +CK_DLL_CTRL( clip_ctrl_plimit ) +{ + Clip_Data *d = (Clip_Data *)OBJ_MEMBER_UINT(SELF, clip_offset_data ); + d->plimit = GET_CK_FLOAT(ARGS); + if (d->plimit < 0.0f) { + d->plimit = 0.0f; + } + RETURN->v_float = (t_CKFLOAT)d->plimit; +} + +CK_DLL_CGET( clip_cget_plimit ) +{ + Clip_Data * d = (Clip_Data *)OBJ_MEMBER_UINT( SELF, clip_offset_data ); + RETURN->v_float = (t_CKFLOAT)d->plimit; +} + +CK_DLL_CTRL( clip_ctrl_nlimit ) +{ + Clip_Data *d = (Clip_Data *)OBJ_MEMBER_UINT(SELF, clip_offset_data ); + d->nlimit = GET_CK_FLOAT(ARGS); + if (d->nlimit > 0.0f) { + d->nlimit = 0.0f; + } + RETURN->v_float = (t_CKFLOAT)d->nlimit; +} + +CK_DLL_CGET( clip_cget_nlimit ) +{ + Clip_Data * d = (Clip_Data *)OBJ_MEMBER_UINT( SELF, clip_offset_data ); + RETURN->v_float = (t_CKFLOAT)d->nlimit; +} + + +// soft clip + +struct SoftClip_Data +{ + SAMPLE plimit, nlimit; + SoftClip_Data( ) { + plimit = 1.0f; + nlimit = -1.0f; + } +}; + +CK_DLL_CTOR( softclip_ctor ) +{ + // return data to be used later + OBJ_MEMBER_UINT( SELF, softclip_offset_data ) = (t_CKUINT)new SoftClip_Data; +} + +CK_DLL_DTOR( softclip_dtor ) +{ + delete (SoftClip_Data *)OBJ_MEMBER_UINT( SELF, softclip_offset_data ); +} + +CK_DLL_TICK( softclip_tick ) +{ + SoftClip_Data *d = (SoftClip_Data *)OBJ_MEMBER_UINT(SELF, softclip_offset_data ); + if (in < 0.0f ) { + *out = d->nlimit * in / (d->nlimit + in); + } else { + *out = d->plimit * in / (d->plimit + in); + } + return TRUE; +} + +CK_DLL_CTRL( softclip_ctrl_limit ) +{ + SoftClip_Data *d = (SoftClip_Data *)OBJ_MEMBER_UINT(SELF, softclip_offset_data ); + d->plimit = GET_CK_FLOAT(ARGS); + if (d->plimit <= 0.0f) { + d->plimit = FLT_MIN; + } + d->nlimit = -d->plimit; + RETURN->v_float = (t_CKFLOAT)d->plimit; +} + +CK_DLL_CTRL( softclip_ctrl_plimit ) +{ + SoftClip_Data *d = (SoftClip_Data *)OBJ_MEMBER_UINT(SELF, softclip_offset_data ); + d->plimit = GET_CK_FLOAT(ARGS); + if (d->plimit <= 0.0f) { + d->plimit = FLT_MIN; + } + RETURN->v_float = (t_CKFLOAT)d->plimit; +} + +CK_DLL_CGET( softclip_cget_plimit ) +{ + SoftClip_Data * d = (SoftClip_Data *)OBJ_MEMBER_UINT( SELF, softclip_offset_data ); + RETURN->v_float = (t_CKFLOAT)d->plimit; +} + +CK_DLL_CTRL( softclip_ctrl_nlimit ) +{ + SoftClip_Data *d = (SoftClip_Data *)OBJ_MEMBER_UINT(SELF, softclip_offset_data ); + d->nlimit = GET_CK_FLOAT(ARGS); + if (d->nlimit >= 0.0f) { + d->nlimit = -FLT_MIN; + } + RETURN->v_float = (t_CKFLOAT)d->nlimit; +} + +CK_DLL_CGET( softclip_cget_nlimit ) +{ + SoftClip_Data * d = (SoftClip_Data *)OBJ_MEMBER_UINT( SELF, softclip_offset_data ); + RETURN->v_float = (t_CKFLOAT)d->nlimit; +} + Index: ugen_xxx.h =================================================================== RCS file: /cvs/chuck_dev/v2/ugen_xxx.h,v retrieving revision 1.12 diff -u -r1.12 ugen_xxx.h --- ugen_xxx.h 2 Mar 2006 00:46:12 -0000 1.12 +++ ugen_xxx.h 15 Oct 2006 21:06:23 -0000 @@ -157,6 +157,23 @@ CK_DLL_CGET( sndbuf_cget_channels ); CK_DLL_CGET( sndbuf_cget_valueAt ); +// clip +CK_DLL_CTOR( clip_ctor ); +CK_DLL_TICK( clip_tick ); +CK_DLL_CGET( clip_ctrl_limit ); +CK_DLL_CGET( clip_ctrl_plimit ); +CK_DLL_CGET( clip_cget_plimit ); +CK_DLL_CGET( clip_ctrl_nlimit ); +CK_DLL_CGET( clip_cget_nlimit ); + +// soft clip +CK_DLL_CTOR( softclip_ctor ); +CK_DLL_TICK( softclip_tick ); +CK_DLL_CGET( softclip_ctrl_limit ); +CK_DLL_CGET( softclip_ctrl_plimit ); +CK_DLL_CGET( softclip_cget_plimit ); +CK_DLL_CGET( softclip_ctrl_nlimit ); +CK_DLL_CGET( softclip_cget_nlimit );