demonstrating the sampling theorem in Chuck
Hi, As a first step into the wonderful and time consuming world of DSP<=>Chuck I want to make audible the effect of disregarding the sampling theorem. So all I'm trying to do at the moment is creating a simple down-sampler without any proper filtering. As it is said in the sampling theorem this should not create any distortion as long as my new sampling rate is at least twice as high as the highest sine wave in my input signal. However, when I'm sampling down a sine wave of for example 440 Hz in Chuck, I get a lot of funny noises long before going down to a SR of 880 Hz. For example: SinOsc inputSignal; Step stepSeq; inputSignal => blackhole; stepSeq=> dac; 440 => inputSignal.freq; 0.5 => inputSignal.gain; //takin every nTh sample of the inputSignal 10 => int n; <<<"real sample rate: " + 1::second/1::samp>>>; <<<"new sample rate: " + 1::second/(1::samp*n)>>>; //duration of the output signal 1::second => dur durationOfTest; now + durationOfTest => time timeToFinish; //don't-care-about-Nyquist-downsampling while (now < timeToFinish) { inputSignal.last() => stepSeq.next; n::samp => now; } By using a common sample-rate like e.g. 44100 Hz in Chuck this should satisfy the sampling theorem. I guess the reason, why I doesn't get a nice 440-sine wave here is because I have to reconstruct the sine wave after the down sampling with the remaining samples, as the output cannot know that they are part of a proper sine wave. Is this right? If yes, could someone give me a hint how to realize something like this? Of not, what is the real problem? My target is a downsampler which for example would produce an undistorted sine wave of 440 Hz after downsampling to 880 Hz (i.e. a working demonstration of the sampling theorem) A second and a bit stupid question: As far as I understand it, it is not possible to downsample a signal with a given SR to any new SR, but only to a new SR which is = (Old SR)/N, whereas N is a simple positive number (of course after converting the new SR to integer). Did I get this right? OK, I would be very grateful if someone could give me a hint or two. I => bed, have a nice day! Jakob _________________________________________________________________ Invite your mail contacts to join your friends list with Windows Live Spaces. It's easy! http://spaces.live.com/spacesapi.aspx?wx_action=create&wx_url=/friends.aspx&mkt=en-us
Hi, Jacob!
As a first step into the wonderful and time consuming world of DSP<=>Chuck I want to make audible the effect of disregarding the sampling theorem.
Breakin' the law! :¬) Very good! So all I'm trying to do at the moment is creating a simple down-sampler
without any proper filtering. As it is said in the sampling theorem this should not create any distortion as long as my new sampling rate is at least twice as high as the highest sine wave in my input signal.
That's right. As long as you're sampling at at least twice the frequency of the bandwith of the signal the signal can be completely re-constructed. The easy way yo guarantee that is to chop off a bit of the bandwith with a LPF but you can indeed also make sure the signal fall in that range. So far so good.
By using a common sample-rate like e.g. 44100 Hz in Chuck this should satisfy the sampling theorem. I guess the reason, why I doesn't get a nice 440-sine wave here is because I have to reconstruct the sine wave after the down sampling with the remaining samples, as the output cannot know that they are part of a proper sine wave. Is this right? If yes, could someone give me a hint how to realize something like this?
Yes, that's right. The sampling theorem says the signal can be completely recovered, however, it doesn't say you can "recover" it by simply listening to the values as a train. What's happening is that you are creating a wave with steps in the amplitude and such steps will have harmonics, some within the range below 22.05KHz, some above. Fortunately this is simple to solve, all you need is a brick-wall lowpass filter with a linear phase response set to cut off at your Nyquist. Unfortunately, those don't grow on trees. You can try various techniques to approximate one, I'd start by simply chucking my Step into a lowpass filter ( LPF ) and seeing how that affects matters. That's not going to cure all of your woes but it will greatly decrease them. From there on it's diminishing returns. I don't think you will realize true mathematical perfection but with some reading up on DSP you could get close to what most ears stop finding objectionable... Or you might find cases in which you like the artifacts better then the perfection. Happy ChucKing! Kas.
Hello list, Does anyone know if there is any UGen to store waveforms in a table? Thus, wavetable synthesis. I've come up with a few ideas, but none of them seem to work: 1. The first was to use a Gen7 together with a phasor. However, I get no sound. See the following patch which I think should synthesise a sine wave: 512 => int length; 440. => float freq; 1./ length => float step; float coefs[ 2*length-1]; for( 1 => int i; i < coefs.cap() ; 2 +=> i ) { Math.sin((i-1)*2.*pi/511.) => coefs[i-1]; // value at pos step => coefs[i]; // duration of value, 1 samp } Math.sin( length*2.*pi/length ) => coefs[ length - 1]; // last value Phasor p => Gen7 gen7 => WvOut file => dac; coefs => gen7.coefs; freq => p.freq; while( true ) 1::samp => now; 2. The second idea was to use a sndbuf, but the results are far from satisfactory. see example: //first you need to generate one period of a sine wave: SinOsc s => WvOut file => blackhole; "sine_period.wav" => file.wavFilename; 1024 => int length; second/samp => float sr; sr/length => s.freq; now + length::samp => time later; while( now < later ) 1::samp=>now; ----- separate chuck file ---- // wavetable synth part: synthesise a sine wave at any freq SndBuf snd => blackhole; Impulse imp => dac; "sine_period.wav" => snd.read; snd.samples() => int length; // must be power of 2 length - 1 => int bitmask; second/samp => float sr; 440. => float freq; freq*length/sr => float delta; delta $int => int int_delta; 0 => float pos; 0 => int int_pos; 1 => int interpolate; if( me.args() ) Std.atoi( me.arg(0) ) => interpolate; while( true ) { if( !interpolate ) { int_delta::samp => now; snd.last() => imp.next; snd.pos( snd.pos()&bitmask ); continue; } delta + pos => pos; pos $int => int_pos; (snd.valueAt( (int_pos&bitmask) + 1 ) - snd.valueAt(int_pos&bitmask))*(pos-int_pos) + snd.valueAt (int_pos) => imp.next; 1::samp => now; } Could someone give me a hand? thanks, eduard
hello, Checking out the code today with a fresher mind, just realised that the second method does work. I forgot to bitmask the last int_pos. So the last line that reads: (snd.valueAt( (int_pos&bitmask) + 1 ) - snd.valueAt(int_pos&bitmask))*(pos-int_pos) + snd.valueAt (int_pos) => imp.next; Should be: (snd.valueAt( (int_pos&bitmask) + 1 ) - snd.valueAt(int_pos&bitmask))*(pos-int_pos) + snd.valueAt (int_pos&bitmask) => imp.next; Still think method 1 should be doable... am i missing something? thanks eduard On Oct 19, 2007, at 4:40 PM, eduard aylon wrote:
Hello list,
Does anyone know if there is any UGen to store waveforms in a table? Thus, wavetable synthesis. I've come up with a few ideas, but none of them seem to work:
1. The first was to use a Gen7 together with a phasor. However, I get no sound. See the following patch which I think should synthesise a sine wave:
512 => int length; 440. => float freq; 1./ length => float step;
float coefs[ 2*length-1]; for( 1 => int i; i < coefs.cap() ; 2 +=> i ) { Math.sin((i-1)*2.*pi/511.) => coefs[i-1]; // value at pos step => coefs[i]; // duration of value, 1 samp } Math.sin( length*2.*pi/length ) => coefs[ length - 1]; // last value Phasor p => Gen7 gen7 => WvOut file => dac; coefs => gen7.coefs; freq => p.freq; while( true ) 1::samp => now;
2. The second idea was to use a sndbuf, but the results are far from satisfactory. see example:
//first you need to generate one period of a sine wave:
SinOsc s => WvOut file => blackhole; "sine_period.wav" => file.wavFilename; 1024 => int length; second/samp => float sr; sr/length => s.freq; now + length::samp => time later; while( now < later ) 1::samp=>now;
----- separate chuck file ----
// wavetable synth part: synthesise a sine wave at any freq
SndBuf snd => blackhole; Impulse imp => dac; "sine_period.wav" => snd.read; snd.samples() => int length; // must be power of 2 length - 1 => int bitmask; second/samp => float sr;
440. => float freq; freq*length/sr => float delta; delta $int => int int_delta; 0 => float pos; 0 => int int_pos; 1 => int interpolate; if( me.args() ) Std.atoi( me.arg(0) ) => interpolate; while( true ) { if( !interpolate ) { int_delta::samp => now; snd.last() => imp.next; snd.pos( snd.pos()&bitmask ); continue; }
delta + pos => pos; pos $int => int_pos; (snd.valueAt( (int_pos&bitmask) + 1 ) - snd.valueAt(int_pos&bitmask))*(pos-int_pos) + snd.valueAt (int_pos) => imp.next; 1::samp => now;
}
Could someone give me a hand?
thanks,
eduard
hihihi, couple things. you can do this kind of thing with LiSa using the "track" method. i should have called this "sync" and will have it accessible that way in the future, but when you set 1 => lisa.track, the input chucked to LiSa sets the playback position. see: http://chuck.cs.princeton.edu/doc/examples/special/LiSa-track1.ck http://chuck.cs.princeton.edu/doc/examples/special/LiSa-track2.ck http://chuck.cs.princeton.edu/doc/examples/special/LiSa-track3.ck there is more LiSa documentation coming soon, i promise! and in the next release the "valueAt" method will allow you to algorithmically set the values in LiSa super easily. now, regarding the gen7 problem; this appears to be a recurrence of the bug we've encountered before with arrays; for some reason dynamically generated arrays don't chuck properly to the GenX ugens and i think the problem has to do with how arrays are chucked to methods in general. i've asked Ge about this and he's looking into it. i'm also assuming there was a reason you were trying to use Gen7 that way and didn't just want to use Gen9 or Gen10 to make a sine wave table.... ok, i hope this helps, dt On Oct 20, 2007, at 6:57 AM, eduard wrote:
hello,
Checking out the code today with a fresher mind, just realised that the second method does work. I forgot to bitmask the last int_pos. So the last line that reads: (snd.valueAt( (int_pos&bitmask) + 1 ) - snd.valueAt(int_pos&bitmask))*(pos-int_pos) + snd.valueAt (int_pos) => imp.next;
Should be: (snd.valueAt( (int_pos&bitmask) + 1 ) - snd.valueAt(int_pos&bitmask))*(pos-int_pos) + snd.valueAt (int_pos&bitmask) => imp.next;
Still think method 1 should be doable... am i missing something?
thanks
eduard
On Oct 19, 2007, at 4:40 PM, eduard aylon wrote:
Hello list,
Does anyone know if there is any UGen to store waveforms in a table? Thus, wavetable synthesis. I've come up with a few ideas, but none of them seem to work:
1. The first was to use a Gen7 together with a phasor. However, I get no sound. See the following patch which I think should synthesise a sine wave:
512 => int length; 440. => float freq; 1./ length => float step;
float coefs[ 2*length-1]; for( 1 => int i; i < coefs.cap() ; 2 +=> i ) { Math.sin((i-1)*2.*pi/511.) => coefs[i-1]; // value at pos step => coefs[i]; // duration of value, 1 samp } Math.sin( length*2.*pi/length ) => coefs[ length - 1]; // last value Phasor p => Gen7 gen7 => WvOut file => dac; coefs => gen7.coefs; freq => p.freq; while( true ) 1::samp => now;
2. The second idea was to use a sndbuf, but the results are far from satisfactory. see example:
//first you need to generate one period of a sine wave:
SinOsc s => WvOut file => blackhole; "sine_period.wav" => file.wavFilename; 1024 => int length; second/samp => float sr; sr/length => s.freq; now + length::samp => time later; while( now < later ) 1::samp=>now;
----- separate chuck file ----
// wavetable synth part: synthesise a sine wave at any freq
SndBuf snd => blackhole; Impulse imp => dac; "sine_period.wav" => snd.read; snd.samples() => int length; // must be power of 2 length - 1 => int bitmask; second/samp => float sr;
440. => float freq; freq*length/sr => float delta; delta $int => int int_delta; 0 => float pos; 0 => int int_pos; 1 => int interpolate; if( me.args() ) Std.atoi( me.arg(0) ) => interpolate; while( true ) { if( !interpolate ) { int_delta::samp => now; snd.last() => imp.next; snd.pos( snd.pos()&bitmask ); continue; }
delta + pos => pos; pos $int => int_pos; (snd.valueAt( (int_pos&bitmask) + 1 ) - snd.valueAt(int_pos&bitmask))*(pos-int_pos) + snd.valueAt (int_pos) => imp.next; 1::samp => now;
}
Could someone give me a hand?
thanks,
eduard
_______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
Hello dan, thanks for your advice. I haven't played with LiSa much yet, but hope to do that soon. In fact what I am trying to do can be done in many many ways: using an array and chucking it into a Step, with sndbuf and chucking buffer values to a Step (a bit roundabout), with LiSa as you pointed out, probably soon with Gen7, looping thru sndbuf and setting frequencies with playing rate, etc. Actually, the latter is probably one of the simplest and more direct way. However, I've been surprised that using sndbuf.interp( 2 ) (i.e.sync interpolation) has the same/similar results (or at least to my ears) as using sndbuf.interp(0) (i.e. drop sample). Should it be like that? I also want to take the chance to comment on a two (separate) things: 1. Arrays which are declared as floats will give compilation errors when only and only the first index is an integer. It seems as if chuck is comparing the type of the first index with the user-defined type of the array. If they differ, one gets an error something like "array[...] contains incompatible types". Example: [1, 2., 3, 4. ] => float array; // this crashes. Note that 1 and 3 are integers [1., 2, 3, 4 ] => int array; // also crashes. Note first index is a float but array is defined as int. [1., 2., 3, 4. => float array; // this doesn't crash. Note 3 is still an integer It's not a big deal, but just for correctness should be addressed in some way, I guess. 2. Regarding spectral analysis, I've noticed that assigning the window function to the ifft gives poorer results. You can check that in /examples/win.ck. Assign windowing to ifft and you'll notice a rougher (fast beating) sound especially at the beginning. eduard On Oct 21, 2007, at 3:47 AM, dan trueman wrote:
hihihi,
couple things. you can do this kind of thing with LiSa using the "track" method. i should have called this "sync" and will have it accessible that way in the future, but when you set 1 => lisa.track, the input chucked to LiSa sets the playback position. see:
http://chuck.cs.princeton.edu/doc/examples/special/LiSa-track1.ck http://chuck.cs.princeton.edu/doc/examples/special/LiSa-track2.ck http://chuck.cs.princeton.edu/doc/examples/special/LiSa-track3.ck
there is more LiSa documentation coming soon, i promise! and in the next release the "valueAt" method will allow you to algorithmically set the values in LiSa super easily.
now, regarding the gen7 problem; this appears to be a recurrence of the bug we've encountered before with arrays; for some reason dynamically generated arrays don't chuck properly to the GenX ugens and i think the problem has to do with how arrays are chucked to methods in general. i've asked Ge about this and he's looking into it. i'm also assuming there was a reason you were trying to use Gen7 that way and didn't just want to use Gen9 or Gen10 to make a sine wave table....
ok, i hope this helps, dt
On Oct 20, 2007, at 6:57 AM, eduard wrote:
hello,
Checking out the code today with a fresher mind, just realised that the second method does work. I forgot to bitmask the last int_pos. So the last line that reads: (snd.valueAt( (int_pos&bitmask) + 1 ) - snd.valueAt(int_pos&bitmask))*(pos-int_pos) + snd.valueAt (int_pos) => imp.next;
Should be: (snd.valueAt( (int_pos&bitmask) + 1 ) - snd.valueAt(int_pos&bitmask))*(pos-int_pos) + snd.valueAt (int_pos&bitmask) => imp.next;
Still think method 1 should be doable... am i missing something?
thanks
eduard
On Oct 19, 2007, at 4:40 PM, eduard aylon wrote:
Hello list,
Does anyone know if there is any UGen to store waveforms in a table? Thus, wavetable synthesis. I've come up with a few ideas, but none of them seem to work:
1. The first was to use a Gen7 together with a phasor. However, I get no sound. See the following patch which I think should synthesise a sine wave:
512 => int length; 440. => float freq; 1./ length => float step;
float coefs[ 2*length-1]; for( 1 => int i; i < coefs.cap() ; 2 +=> i ) { Math.sin((i-1)*2.*pi/511.) => coefs[i-1]; // value at pos step => coefs[i]; // duration of value, 1 samp } Math.sin( length*2.*pi/length ) => coefs[ length - 1]; // last value Phasor p => Gen7 gen7 => WvOut file => dac; coefs => gen7.coefs; freq => p.freq; while( true ) 1::samp => now;
2. The second idea was to use a sndbuf, but the results are far from satisfactory. see example:
//first you need to generate one period of a sine wave:
SinOsc s => WvOut file => blackhole; "sine_period.wav" => file.wavFilename; 1024 => int length; second/samp => float sr; sr/length => s.freq; now + length::samp => time later; while( now < later ) 1::samp=>now;
----- separate chuck file ----
// wavetable synth part: synthesise a sine wave at any freq
SndBuf snd => blackhole; Impulse imp => dac; "sine_period.wav" => snd.read; snd.samples() => int length; // must be power of 2 length - 1 => int bitmask; second/samp => float sr;
440. => float freq; freq*length/sr => float delta; delta $int => int int_delta; 0 => float pos; 0 => int int_pos; 1 => int interpolate; if( me.args() ) Std.atoi( me.arg(0) ) => interpolate; while( true ) { if( !interpolate ) { int_delta::samp => now; snd.last() => imp.next; snd.pos( snd.pos()&bitmask ); continue; }
delta + pos => pos; pos $int => int_pos; (snd.valueAt( (int_pos&bitmask) + 1 ) - snd.valueAt(int_pos&bitmask))*(pos-int_pos) + snd.valueAt (int_pos) => imp.next; 1::samp => now;
}
Could someone give me a hand?
thanks,
eduard
_______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
_______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
hi eduard, it's good to have lots of ways to do things! regarding the bugs and related issues, i'm sure Ge et. al. would love it if you'd note them on the wiki: http://wiki.cs.princeton.edu/index.php/ChucK/Bugs/Reports i know they refer to that when making updates, while it might get lost on this list.... best, dan On Oct 21, 2007, at 11:18 AM, eduard aylon wrote:
Hello dan,
thanks for your advice. I haven't played with LiSa much yet, but hope to do that soon.
In fact what I am trying to do can be done in many many ways: using an array and chucking it into a Step, with sndbuf and chucking buffer values to a Step (a bit roundabout), with LiSa as you pointed out, probably soon with Gen7, looping thru sndbuf and setting frequencies with playing rate, etc. Actually, the latter is probably one of the simplest and more direct way. However, I've been surprised that using sndbuf.interp( 2 ) (i.e.sync interpolation) has the same/similar results (or at least to my ears) as using sndbuf.interp(0) (i.e. drop sample). Should it be like that?
I also want to take the chance to comment on a two (separate) things:
1. Arrays which are declared as floats will give compilation errors when only and only the first index is an integer. It seems as if chuck is comparing the type of the first index with the user-defined type of the array. If they differ, one gets an error something like "array[...] contains incompatible types". Example:
[1, 2., 3, 4. ] => float array; // this crashes. Note that 1 and 3 are integers [1., 2, 3, 4 ] => int array; // also crashes. Note first index is a float but array is defined as int.
[1., 2., 3, 4. => float array; // this doesn't crash. Note 3 is still an integer
It's not a big deal, but just for correctness should be addressed in some way, I guess.
2. Regarding spectral analysis, I've noticed that assigning the window function to the ifft gives poorer results. You can check that in /examples/win.ck. Assign windowing to ifft and you'll notice a rougher (fast beating) sound especially at the beginning.
eduard
On Oct 21, 2007, at 3:47 AM, dan trueman wrote:
hihihi,
couple things. you can do this kind of thing with LiSa using the "track" method. i should have called this "sync" and will have it accessible that way in the future, but when you set 1 => lisa.track, the input chucked to LiSa sets the playback position. see:
http://chuck.cs.princeton.edu/doc/examples/special/LiSa-track1.ck http://chuck.cs.princeton.edu/doc/examples/special/LiSa-track2.ck http://chuck.cs.princeton.edu/doc/examples/special/LiSa-track3.ck
there is more LiSa documentation coming soon, i promise! and in the next release the "valueAt" method will allow you to algorithmically set the values in LiSa super easily.
now, regarding the gen7 problem; this appears to be a recurrence of the bug we've encountered before with arrays; for some reason dynamically generated arrays don't chuck properly to the GenX ugens and i think the problem has to do with how arrays are chucked to methods in general. i've asked Ge about this and he's looking into it. i'm also assuming there was a reason you were trying to use Gen7 that way and didn't just want to use Gen9 or Gen10 to make a sine wave table....
ok, i hope this helps, dt
On Oct 20, 2007, at 6:57 AM, eduard wrote:
hello,
Checking out the code today with a fresher mind, just realised that the second method does work. I forgot to bitmask the last int_pos. So the last line that reads: (snd.valueAt( (int_pos&bitmask) + 1 ) - snd.valueAt(int_pos&bitmask))*(pos-int_pos) + snd.valueAt (int_pos) => imp.next;
Should be: (snd.valueAt( (int_pos&bitmask) + 1 ) - snd.valueAt(int_pos&bitmask))*(pos-int_pos) + snd.valueAt (int_pos&bitmask) => imp.next;
Still think method 1 should be doable... am i missing something?
thanks
eduard
On Oct 19, 2007, at 4:40 PM, eduard aylon wrote:
Hello list,
Does anyone know if there is any UGen to store waveforms in a table? Thus, wavetable synthesis. I've come up with a few ideas, but none of them seem to work:
1. The first was to use a Gen7 together with a phasor. However, I get no sound. See the following patch which I think should synthesise a sine wave:
512 => int length; 440. => float freq; 1./ length => float step;
float coefs[ 2*length-1]; for( 1 => int i; i < coefs.cap() ; 2 +=> i ) { Math.sin((i-1)*2.*pi/511.) => coefs[i-1]; // value at pos step => coefs[i]; // duration of value, 1 samp } Math.sin( length*2.*pi/length ) => coefs[ length - 1]; // last value Phasor p => Gen7 gen7 => WvOut file => dac; coefs => gen7.coefs; freq => p.freq; while( true ) 1::samp => now;
2. The second idea was to use a sndbuf, but the results are far from satisfactory. see example:
//first you need to generate one period of a sine wave:
SinOsc s => WvOut file => blackhole; "sine_period.wav" => file.wavFilename; 1024 => int length; second/samp => float sr; sr/length => s.freq; now + length::samp => time later; while( now < later ) 1::samp=>now;
----- separate chuck file ----
// wavetable synth part: synthesise a sine wave at any freq
SndBuf snd => blackhole; Impulse imp => dac; "sine_period.wav" => snd.read; snd.samples() => int length; // must be power of 2 length - 1 => int bitmask; second/samp => float sr;
440. => float freq; freq*length/sr => float delta; delta $int => int int_delta; 0 => float pos; 0 => int int_pos; 1 => int interpolate; if( me.args() ) Std.atoi( me.arg(0) ) => interpolate; while( true ) { if( !interpolate ) { int_delta::samp => now; snd.last() => imp.next; snd.pos( snd.pos()&bitmask ); continue; }
delta + pos => pos; pos $int => int_pos; (snd.valueAt( (int_pos&bitmask) + 1 ) - snd.valueAt(int_pos&bitmask))*(pos-int_pos) + snd.valueAt (int_pos) => imp.next; 1::samp => now;
}
Could someone give me a hand?
thanks,
eduard
_______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
_______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
_______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
On 10/21/07, dan trueman
hihihi,
couple things. you can do this kind of thing with LiSa using the "track" method. i should have called this "sync" and will have it accessible that way in the future, but when you set 1 => lisa.track, the input chucked to LiSa sets the playback position. see:
Looks brilliant! Out little LiSa is growing up so quickly! :-) It's not yet 100% clear to me how this will interact with using multiple voices... Each voice will/can have it's own loop setting and track will determine playback within those loops, right? And clearly everything shares the same tracking signal? So this will result in a sort of "unison sample playback" for lack of a better word? This is starting to look like one of those Serge-Modular modules that can do everything. Enthousiastically yours, Kas.
It's not yet 100% clear to me how this will interact with using multiple voices... Each voice will/can have it's own loop setting and track will determine playback within those loops, right? And clearly everything shares the same tracking signal? So this will result in a sort of "unison sample playback" for lack of a better word?
well, that *would* be a cool way to do it! at the moment, it only works with one voice (assuming the settings of voice 0) when 1=>track, but i'll look at it some more and see if we can do what you suggest. should be possible. good idea! but, you can already play with the current implementation; should be in the release that is out there. in the next release there will also be a 2=>track mode, where it interprets the input as a duration value (or literal time point) within the buffer. this will obviously override any individual voice loop-point settings, so will only make sense with one voice, but it's kind of nice to be able to think in terms of time instead of phase sometimes. dan
This is starting to look like one of those Serge-Modular modules that can do everything.
Enthousiastically yours, Kas. _______________________________________________ chuck-users mailing list chuck-users@lists.cs.princeton.edu https://lists.cs.princeton.edu/mailman/listinfo/chuck-users
On 10/22/07, dan trueman
works with one voice (assuming the settings of voice 0) when 1=>track, but i'll look at it some more and see if we can do what you suggest. should be possible. good idea!
Ah, ok, that's clear then. but, you can already play with the current implementation; should be
in the release that is out there.
I plan to! I need something to play lead-joypad on Sunday and this sounds like just the thing. in the next release there will also be a 2=>track mode, where it
interprets the input as a duration value (or literal time point) within the buffer. this will obviously override any individual voice loop-point settings, so will only make sense with one voice, but it's kind of nice to be able to think in terms of time instead of phase sometimes.
Right! How about scaling the input signal (-1 to 1, most likely) by multiplying it by the duration that the loop length is and scale it that way? We need some way to map values to time. Do I understand correctly that what we are after here is similar to that mod where you hack CV controll into a S-612's loop setting sliders? Yours Kas.
On Oct 18, 2007, at 9:04 PM, Kassen wrote:
You can try various techniques to approximate one, I'd start by simply chucking my Step into a lowpass filter ( LPF ) and seeing how that affects matters. That's not going to cure all of your woes but it will greatly decrease them. From there on it's diminishing returns. I don't think you will realize true mathematical perfection but with some reading up on DSP you could get close to what most ears stop finding objectionable... Or you might find cases in which you like the artifacts better then the perfection.
Ideally when you upsample your low sample rate signal back to 44100Hz, you convolve it with the sinc function, which is an ideal low pass filter with a cutoff at Nyquist. See: http://en.wikipedia.org/wiki/Whittaker%E2%80% 93Shannon_interpolation_formula for more info. I wonder if this could be implemented in ChucK without resorting to 1::samp... maybe with lots of Delay ugens... as far as C code goes, there is a PRC classic here for doing sinc interpolation: http://www.cs.princeton.edu/courses/archive/spr07/cos325/src/OLD/ TimeStuf/srconvrt.c spencer
On 10/20/07, Spencer Salazar
44100Hz, you convolve it with the sinc function, which is an ideal low pass filter with a cutoff at Nyquist. See: http://en.wikipedia.org/wiki/Whittaker%E2%80% 93Shannon_interpolation_formula for more info.
Yes, I wrote about that just a few days back when we had another discussion about Nyquist use/abuse. I wonder if this could be implemented in ChucK without resorting to
1::samp... maybe with lots of Delay ugens..
I tried and couldn't get it to work at a realistic CPU usage buit that was a long time ago. I also went about it rather literally, using a actual sine. What if we'd allow negative targets for Envelope.target()? that would make linear interpolation a breeze. I still don't see what would be wrong with negative Envelope targets and if I remember corectly that would work with the removal of one check from the code. . as far as C code goes,
there is a PRC classic here for doing sinc interpolation: http://www.cs.princeton.edu/courses/archive/spr07/cos325/src/OLD/ TimeStuf/srconvrt.c
There is also a implementation in SndBuf, maybe LiSa could use one too. We could also think about a dedicated Ugen for this, I'm still very interested in having something like SndBuf but asigning (I'd like to keep it editable) a array of floats to it instead of a .wav file. Yours, Kas.
participants (6)
-
dan trueman
-
eduard
-
eduard aylon
-
jakob kaiser
-
Kassen
-
Spencer Salazar