/* * vibrating_string.h - model of a vibrating string lifted from pluckedSynth * * Copyright (c) 2006-2007 Danny McRae * * This file is part of Linux MultiMedia Studio - http://lmms.sourceforge.net * * This program 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 2 of the License, or (at your option) any later version. * * This program 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 this program (see COPYING); if not, write to the * Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301 USA. * */ #ifndef _VIBRATING_STRING_H #define _VIBRATING_STRING_H #include #include #include "lmms_basics.h" class vibratingString { public: vibratingString( float _pitch, float _pick, float _pickup, float * impluse, Uint32 _len, sample_rate_t _sample_rate, Uint8 _oversample, float _randomize, float _string_loss, float _detune, bool _state ); inline ~vibratingString() { delete[] m_outsamp; delete[] m_impulse; vibratingString::freeDelayLine( m_fromBridge ); vibratingString::freeDelayLine( m_toBridge ); } inline sample_t nextSample() { sample_t ym0; sample_t ypM; for( Uint8 i = 0; i < m_oversample; i++) { // Output at pickup position m_outsamp[i] = fromBridgeAccess( m_fromBridge, m_pickupLoc ); m_outsamp[i] += toBridgeAccess( m_toBridge, m_pickupLoc ); // Sample traveling into "bridge" ym0 = toBridgeAccess( m_toBridge, 1 ); // Sample to "nut" ypM = fromBridgeAccess( m_fromBridge, m_fromBridge->length - 2 ); // String state update // Decrement pointer and then update fromBridgeUpdate( m_fromBridge, -bridgeReflection( ym0 ) ); // Update and then increment pointer toBridgeUpdate( m_toBridge, -ypM ); } return( m_outsamp[m_choice] ); } private: struct delayLine { sample_t * data; int length; sample_t * pointer; sample_t * end; } ; delayLine * m_fromBridge; delayLine * m_toBridge; int m_pickupLoc; Uint8 m_oversample; float m_randomize; float m_stringLoss; float * m_impulse; int m_choice; float m_state; sample_t * m_outsamp; delayLine * initDelayLine( int _len, int _pick ); static void freeDelayLine( delayLine * _dl ); void resample( float *_src, f_cnt_t _src_frames, f_cnt_t _dst_frames ); /* setDelayLine initializes the string with an impulse at the pick * position unless the impulse is longer than the string, in which * case the impulse gets truncated. */ inline void setDelayLine( delayLine * _dl, int _pick, const float * _values, int _len, float _scale, bool _state ) { float r; float offset; if( not _state ) { for( int i = 0; i < _pick; i++ ) { r = static_cast( rand() ) / RAND_MAX; offset = ( m_randomize / 2.0f - m_randomize ) * r; _dl->data[i] = _scale * _values[_dl->length - i] + offset; } for( int i = _pick; i < _dl->length; i++ ) { r = static_cast( rand() ) / RAND_MAX; offset = ( m_randomize / 2.0f - m_randomize ) * r; _dl->data[i] = _scale * _values[i - _pick] + offset ; } } else { if( _len + _pick > _dl->length ) { for( int i = _pick; i < _dl->length; i++ ) { r = static_cast( rand() ) / RAND_MAX; offset = ( m_randomize / 2.0f - m_randomize ) * r; _dl->data[i] = _scale * _values[i-_pick] + offset; } } else { for( int i = 0; i < _len; i++ ) { r = static_cast( rand() ) / RAND_MAX; offset = ( m_randomize / 2.0f - m_randomize ) * r; _dl->data[i+_pick] = _scale * _values[i] + offset; } } } } /* toBridgeUpdate(dl, insamp); * Places "nut-reflected" sample from upper delay-line into * current lower delay-line pointer position (which represents * x = 0 position). The pointer is then incremented (i.e. the * wave travels one sample to the left), turning the previous * position into an "effective" x = L position for the next * iteration. */ inline void toBridgeUpdate( delayLine * _dl, sample_t _insamp ) { register sample_t * ptr = _dl->pointer; *ptr = _insamp * m_stringLoss; ++ptr; if( ptr > _dl->end ) { ptr = _dl->data; } _dl->pointer = ptr; } /* fromBridgeUpdate(dl, insamp); * Decrements current upper delay-line pointer position (i.e. * the wave travels one sample to the right), moving it to the * "effective" x = 0 position for the next iteration. The * "bridge-reflected" sample from lower delay-line is then placed * into this position. */ inline void fromBridgeUpdate( delayLine * _dl, sample_t _insamp ) { register sample_t * ptr = _dl->pointer; --ptr; if( ptr < _dl->data ) { ptr = _dl->end; } *ptr = _insamp * m_stringLoss; _dl->pointer = ptr; } /* dlAccess(dl, position); * Returns sample "position" samples into delay-line's past. * Position "0" points to the most recently inserted sample. */ static inline sample_t dlAccess( delayLine * _dl, int _position ) { sample_t * outpos = _dl->pointer + _position; while( outpos < _dl->data ) { outpos += _dl->length; } while( outpos > _dl->end ) { outpos -= _dl->length; } return( *outpos ); } /* * Right-going delay line: * -->---->---->--- * x=0 * (pointer) * Left-going delay line: * --<----<----<--- * x=0 * (pointer) */ /* fromBridgeAccess(dl, position); * Returns spatial sample at position "position", where position zero * is equal to the current upper delay-line pointer position (x = 0). * In a right-going delay-line, position increases to the right, and * delay increases to the right => left = past and right = future. */ static inline sample_t fromBridgeAccess( delayLine * _dl, int _position ) { return( dlAccess( _dl, _position ) ); } /* toBridgeAccess(dl, position); * Returns spatial sample at position "position", where position zero * is equal to the current lower delay-line pointer position (x = 0). * In a left-going delay-line, position increases to the right, and * delay DEcreases to the right => left = future and right = past. */ static inline sample_t toBridgeAccess( delayLine * _dl, int _position ) { return( dlAccess( _dl, _position ) ); } inline sample_t bridgeReflection( sample_t _insamp ) { return( m_state = ( m_state + _insamp ) * 0.5 ); } } ; #endif