|
Post by Yogi B on May 30, 2023 17:35:13 GMT -5
Posting this in the coffee shop rather than elsewhere (probably Effects Devices) because it's not part of a fully fleshed out idea, just a curiosity that's been itching my brain over the past week or so. Say we have a (digital) LFO with the ability to alter the waveshape: from triangle at one extreme to square at the other (preferably passing through an exact sine wave at some point) — then what method makes the most sense to obtain the in-between shapes? I can think of three reasonable approaches. The first takes inspiration from the analog world wherein the hyperbolic tangent response of an overdriven differential pair of BJTs is one method employed to create an approximate sine wave from a triangle input. This is, however, only quite a loose approximation (see, for example, the first few sections of Tillman's An Improved Sine Shaper Circuit article). Free from the encumbrances of analog circuitry, we can first utilize an actual sine response into which the triangle wave is 'driven', such that a perfect sine wave is obtained. Then, at higher levels, feed that sine wave as input to the usual tanh response to be further 'clipped'. The following is the result plotted across ten systematically* chosen intervals. The next method uses a weighted harmonic mean, first between triangle & sine waves then between sine & square waves. This produces more 'balanced' intermediate waveforms, expanding roughly equally towards both the horizontal & vertical edges of the square wave, thus more symmetrically filling out the corners. Finally, swapping out the harmonic mean for the geometric mean gives the last variation. This produces a kind of counterpart to the initial 'clipped' waveforms, flattening out towards the vertical edges quicker than towards the horizontals. For a direct comparison, here's a plot showing the three set to 70% (the brown / dark orange trace in the above) and horizontally stretched to further emphasize the differences (you can either think of this as a zoom on the two centre quadrants, or as the results of being driven from a sawtooth wave instead of a triangle). The dotted red line is the tanh variation, dash-dotted green is the harmonic mean, and solid purple is the geometric mean.
*: In order to get a fairly good spread over the range, I've spaced the divisions such that (for each variation) the RMS increases somewhat linearly (to be more specific, it approximately follows a (1-(1-x) 1.25) 0.95 curve). My original approach was to use area under the curves, rather than RMS, but that didn't give as consistent spacing across the three alternatives. Irrespective of the spacing method chosen, the best placement for the exact sine wave seems to fall at a point between the 10% increments; in the above plots it's at approximately 23.8%. I'd prefer it were at 20% (or 9 o'clock on a 300° dial), but I felt this allocated just a bit too much change over that first 20%, and somewhat cramped the remaining 80%.
So anyway, which method(s) would you choose and why?
|
|
|
Post by JohnH on May 31, 2023 14:29:59 GMT -5
Hi Yogi, could you clarify what these waveforms will be used for eg tremolo maybe?
|
|
|
Post by Yogi B on May 31, 2023 17:09:18 GMT -5
Hi Yogi, could you clarify what these waveforms will be used for eg tremolo maybe? I'm thinking delay time, so: flanging / chorus / modulated delay, as dependent on what order of magnitude the delay is. I figure for tremolo (volume/depth control) you're probably not looking for a waveform that's symmetrical about y = 0. (Not that symmetry is a strict requirement for time based effects — but swinging either side of a nominal value seems more sensible for that, than for volume.)
Having thought a little more, if I were to include at least two options and one of those was the first alternative ('clipping', the one in which the sine wave most naturally occurs), I could then avoid the peculiar change in gradient (with respect to 'mix', the variable I'm iterating over to produce the multiple traces per plot) that occurs as a result of shoehorning in the sine wave. (i.e. as long there's one way to achieve a sine wave, I could forego that requirement for the other alternatives.) I suppose whilst I figure that out, I should lock the poll.
|
|
|
Post by thetragichero on May 31, 2023 20:32:46 GMT -5
my vote is for whatever is the easiest to accomplish as they're likely indistinguishable sonically (my wild guess). that might not apply to anything in the digital world (which is greek to me), but analog i'm sure there'd be a difference in complexity
|
|
|
Post by JohnH on Jun 1, 2023 2:48:39 GMT -5
Ok I kind of follow. All the proposals have triangle and square at the ends and sine or (approximately) in between.
Given its for a modulation use at low frequency, rather than an audible direct tone, I agree they would be expected to sound fairly similar
All are symmetrical up and down. There are classic effects that use an asymmetrical control waveform, such as certain tremelos and classic univibes, that gives them their 'throb'
All of these waves can be thought of as a Fourier series of harmonics, all odd harmonics in these symmetrical shapes.
So another thought is, could the extremes be, instead of square and triangle, be square and sine? Starting with a square, a first order filtration (low pass), will be a triangle. This assumes the roll-off starts at or below the frequency in use. Its much the same as a mathematical integration function. Then more generations of a similar filtering process will further supress the harmonics, leaving almost a sine.
Could also think of this as a low pass starting at the main frequency, becoming steeper and steeper in terms of dB per octave, until only the fundamental sine is left.
Not sure if any of that is helpful, I'm just thinking around it...
|
|
|
Post by ashcatlt on Jun 1, 2023 15:50:16 GMT -5
A bunch of lowpass filters is probably more CPU ticks and definitely more finicky than direct wave shaping.
I feel like you’d get just fine results if you start with a triangle and drive it into a sine function. Scale it going in (lower values will be more linear, higher values go through sine toward square) then clip it so the sine fiction doesn’t fold over, then sine, then “unscale” it.
output = (1/scale) * sin (pi/2 * max (-1, min (1, scale * triangle)));
|
|
|
Post by Yogi B on Jun 12, 2023 22:27:42 GMT -5
So another thought is, could the extremes be, instead of square and triangle, be square and sine? Starting with a square, a first order filtration (low pass), will be a triangle. This assumes the roll-off starts at or below the frequency in use. It's much the same as a mathematical integration function. Then more generations of a similar filtering process will further suppress the harmonics, leaving almost a sine. My main issue with that approach is that the only way the actual shape of a triangle wave can be bound between a sine & square wave is if the amplitudes differ (as in the below image), and I'd prefer to keep that independent of the waveshape. Anyway, the most straight forward way I can think of performing this 'filtration' is by adjusting the individual terms of the Fourier series and in that case actual integration works out much simpler than introducing filter maths. Although it's relatively simple to approximate the transfer functions for Butterworth filters of non-integer order (by taking a weighted geometric mean of the adjacent integer-orders, or raising the next integer-order to a fractional power & adjusting the coefficients), both are much more complicated than a fractionally integrated sine wave. Which is just that fraction times a quarter period phase shift and a reduction in amplitude proportional to a power (again, that same fraction) of its frequency. For differentiation we have: \frac{\mathrm{d}^\alpha}{\mathrm{d}t^\alpha}\sin(\omega t) = \omega^\alpha \sin\mathopen{}\left(\omega t + {\large\tfrac{\alpha \pi}{2}}\right)
and this remains true for: negative α, i.e. integration (assuming we're ignoring any constants of integration, which would be irrelevant in an actual circuit due to AC coupling); and even in cases where α is not an integer. Additionally, if the actual goal is integration then any deviation a filter would have from constant rolloff and constant phase shift will contribute 'slop' to the result. Such deviations can be reduced by decreasing the cutoff frequency, but if we construct the filters from n cascaded first order filters (plus makeup gain, to keep the volume of our fundamental, ω = 1, constant) and then reduce the cutoff all the way to zero, it yields the same results as the above (with n = − α). That is, with the below transfer function, we get the following amplitude & phase: \newcommand\lo{\mathopen{}\mathclose\bgroup\left} \newcommand\rc[1]{\right#1\egroup}
\begin{aligned} &H(s; \omega_c) = \lo(\frac{\sqrt{{\omega_c}^2 + 1}}{\omega_c + s}\rc)^{\!\!n} \\\\ &\begin{aligned} \big\lvert H(j\omega; 0)\big\rvert &= \lo\lvert\frac{\sqrt{0^2 + 1}}{0 + j\omega}\rc\rvert^{\,n} \\[3ex] &= \frac{1}{\omega^n} \end{aligned} \\\\ &\begin{aligned} \arg(H(j\omega; 0)) &= \arg\lo(\lo(\frac{\sqrt{0^2 + 1}}{0 + j\omega}\rc)^{\!\!n\,}\rc) \\[3ex] &= n \arg\lo(\frac{1}{j\omega}\rc) \\[2ex] &= n \arg(-j) \\[1ex] &= -\frac{n\pi}{2} \end{aligned} \end{aligned}
However we obtain the results we can then use the magnitude & phase to either manually construct & sum the harmonics or feed them into an inverse fast Fourier transform routine. Additionally, being the sum of powers of harmonics, it's also unsurprising that the waveforms can also be obtained using some of the various extensions of the Riemann zeta function (the Hurwitz or Lerch zetas). Via those it may also be possible to find a solution for the amplitude of the resultant wave, but thus far I've only managed to do so for integer n and 0 < n < 1, because that's where the extrema fall on predictable fractions of pi. But assuming the generated waves are free of extraneous ripples it's simpler just to divide through by whatever the max value happens to be. The below plots show a square wave being integrated in steps of one-third, the last row also including a sine wave in blue for reference: showing that after two full integrations, although close, it's not yet quite a sine; and at non-integer integrals there's still a small amount of asymmetry. The 'shark-fin' wave in the top centre is probably the most interesting and would be something I'd like to include. However, rather than an entirely separate generation method (and to try to keep things generalised), it's possible to get virtually identical results starting from a wave that's the geometric mean between triangle & square (i.e. one that skips the sine requirement), and dropping alternating quadrants (scaling those that remain to fill the gaps). I'll (try to) link an interactive demo of what I'm thinking in a follow-up post.
I feel like you’d get just fine results if you start with a triangle and drive it into a sine function. Scale it going in (lower values will be more linear, higher values go through sine toward square) then clip it so the sine function doesn’t fold over, then sine, then “unscale” it. output = (1/scale) * sin (pi/2 * max (-1, min (1, scale * triangle))); Note that you only want the 'unscaling' whilst the amplitude of the scaled triangle wave is less than pi/2 (and not zero) and that, too, should follow the sine curve, i.e. something like: if A == 0: return tri(x) elif A < pi/2: return sin(A * tri(x)) / sin(A) else: return sin(clip(A * tri(x), -pi/2, pi/2))
I did look at this, but wasn't a fan of the sharp corners. The tanh version more-or-less does the same, but with softer corners, essentially replacing the last line of the above with: return tanh(A * sin(x)) / tanh(A)
(For simplicity I am omitting the calculation of A, however it does need to be performed differently for this branch.) Finally it needn't necessarily be tanh, any function with a gradient of one at zero and asymptotic to y = ±1 could be made to work, each with different 'clipping' characteristics, for example: (1 - x -2) -1/2 (i.e. sin(arctan(x))) for softer clipping; or erf(x * sqrt(pi) / 2) for harder clipping.
|
|
|
Post by Yogi B on Jun 13, 2023 0:33:49 GMT -5
I'll (try to) link an interactive demo of what I'm thinking in a follow-up post. This was hopefully going to be that link, but it appears viglink is choking on it, probably due to the length. My go-to link shortener (is.gd) also truncates it, but this tinyURL link should work correctly. As a backup here's the full link: https://share.stlite.net/#!ChBzdHJlYW1saXRfYXBwLnB5EqklChBzdHJlYW1saXRfYXBwLnB5EpQlCpElaW1wb3J0IG51bXB5IGFzIG5wCmZyb20gbnVtcHkgaW1wb3J0IHBpIGFzIM-ACmltcG9ydCBtYXRwbG90bGliIGFzIG1wbAppbXBvcnQgbWF0cGxvdGxpYi5weXBsb3QgYXMgcGx0CmltcG9ydCBzdHJlYW1saXQgYXMgc3QKCmZyb20gbXBsX3N0eWxpbmcgaW1wb3J0IHNldHVwLCBQaUZvcm1hdHRlciwgQ09MT1JTCgoKU0lOX1BPUyA9IDEtKDEtKCgxLzIqKi41IC0gMS8zKiouNSkgLyAoMSAtIDEvMyoqLjUpKSoqKDEvLjk1KSkqKigxLzEuMjUpCgphd2FpdCBzZXR1cCgpCnN0LnNldF9wYWdlX2NvbmZpZyhwYWdlX3RpdGxlPSdMRk8gV2F2ZXNoYXBlcycpCnN0Lm1hcmtkb3duKAogICAgIyBvdmVyaWRlIFN0cmVhbWxpdCdzIGRlZmF1bHQgbWF4LXdpZHRoCiAgICAnPHN0eWxlPi5zdEFwcCAuYmxvY2stY29udGFpbmVyIHsgbWF4LXdpZHRoOiA2MHJlbTsgfTwvc3R5bGU+JywKICAgIHVuc2FmZV9hbGxvd19odG1sPVRydWUsCikKCmRlZiB0cmkoeCk6CiAgICByZXR1cm4gbnAuaW50ZXJwKHggJSAoMirPgCkgLyAoMirPgCksIFswLCAuMjUsIC41LCAuNzUsIDFdLCBbMCwgMSwgMCwgLTEsIDBdKQoKZGVmIHNxcih4KToKICAgIHJldHVybiBucC53aGVyZSh4ICUgKDIqz4ApIDwgz4AsIDEsIC0xKQoKZnVuY3Rpb25zID0ge30KCkBsYW1iZGEgZjogZnVuY3Rpb25zLnNldGRlZmF1bHQoJ1RhbmggIkNsaXBwaW5nIicsIGYpCmRlZiBjbGlwKHgsIG1peCk6CiAgICBpZiBtaXggPT0gMDoKICAgICAgICByZXR1cm4gdHJpKHgpCiAgICBlbGlmIG1peCA8PSBTSU5fUE9TOgogICAgICAgIGEsIGIgPSAwLjQzODU4MTg2MDI5NjU5NjMsIDAuNTI2NDEzNDYxMjQ2NDE4NAogICAgICAgIEEgPSDPgC8yICogKGEqKihtaXgvU0lOX1BPUykqKmIgLSAxKSAvIChhIC0gMSkKICAgICAgICByZXR1cm4gbnAuc2luKEEgKiB0cmkoeCkpIC8gbnAuc2luKEEpCiAgICBlbGlmIG1peCA9PSAxOgogICAgICAgIHJldHVybiBzcXIoeCkKCiAgICBhLCBiLCBjLCBkLCBlID0gMy42MjkyNzQzMDY0MjYzMTMzLCAwLjIzNTM4MjU4Mzc4Nzg3MjMzLCAwLjU3MjM3MjIyNDk5OTU0MDQsIDEuMjgwMjQzNTAzMzIwMjE3NywgMC4yMjMxMDk3NzUwMzI1MDQxCiAgICBBID0gYSAqIChtaXggLSBiKSoqYyAvICgxLW1peCkqKmQgKiBlKiptaXgKICAgIHJldHVybiAobnAudGFuaChBKm5wLnNpbih4KSkvbnAudGFuaChBKSkKCkBsYW1iZGEgZjogZnVuY3Rpb25zLnNldGRlZmF1bHQoJ0hhcm1vbmljIE1lYW4gKHRyaS9zaW4vc3FyKScsIGYpCmRlZiBoYXJtKHgsIG1peCk6CiAgICBpZiBtaXggPD0gU0lOX1BPUzoKICAgICAgICBhID0gMC40MzIxODYyMjQ5MjY0Mzk3CiAgICAgICAgbSA9IChhKioobWl4L1NJTl9QT1MpIC0gMSkgLyAoYSAtIDEpCiAgICAgICAgcmV0dXJuIDEgLyAobS9ucC5zaW4oeCkgKyAoMS1tKS90cmkoeCkpCiAgICAKICAgIGEsIGIsIGMgPSAwLjQ3MTM1NDM2MzU0MTUxODE2LCAxLjI1NzM1NDUxODUyNTI3OCwgMy42NTQyNzQxNjU4NjM1NjQKICAgIG1peCA9ICgxIC0gYSooYi1taXgpKipjKSoqKDEvYykKICAgIG1pbmltdW0gPSAoMSAtIGEqKGItU0lOX1BPUykqKmMpKiooMS9jKQogICAgbWl4IC09IG1pbmltdW0KICAgIG1peCAvPSAoMSAtIGEqKGItMSkqKmMpKiooMS9jKSAtIG1pbmltdW0KICAgIHJldHVybiAxIC8gKCgxLW1peCkvbnAuc2luKHgpICsgbWl4L3Nxcih4KSkKCkBsYW1iZGEgZjogZnVuY3Rpb25zLnNldGRlZmF1bHQoJ0hhcm1vbmljIE1lYW4gKHRyaS9zcXIpJywgZikKZGVmIGhhcm0yKHgsIG1peCk6CiAgICBhLCBiLCBjID0gMC4zMjI4MTg3MDczNTA2NDMwNSwgMS4yNjkxODY0ODQ4ODk2OTYsIDMuMzU4NTA0Nzc5ODc1OTk0CiAgICBtaXggPSAoMSAtIGEqKGItbWl4KSoqYykqKigxL2MpCiAgICBtaW5pbXVtID0gKDEgLSBhKmIqKmMpKiooMS9jKQogICAgbWl4IC09IG1pbmltdW0KICAgIG1peCAvPSAoMSAtIGEqKGItMSkqKmMpKiooMS9jKSAtIG1pbmltdW0KICAgIHJldHVybiAxIC8gKCgxLW1peCkvdHJpKHgpICsgbWl4L3Nxcih4KSkKCkBsYW1iZGEgZjogZnVuY3Rpb25zLnNldGRlZmF1bHQoJ0dlb21ldHJpYyBNZWFuICh0cmkvc2luL3NxciknLCBmKQpkZWYgZ2VvbSh4LCBtaXgpOgogICAgaWYgbWl4IDw9IFNJTl9QT1M6CiAgICAgICAgYSA9IDAuNTY5NjkyMTIzNTA2NzUyNgogICAgICAgIG0gPSAoYSoqKG1peC9TSU5fUE9TKSAtIDEpIC8gKGEgLSAxKQogICAgICAgIHJldHVybiBzcXIoeCkgKiBhYnMobnAuc2luKHgpKSoqbSAqIGFicyh0cmkoeCkpKiooMSAtIG0pCiAgICAKICAgIGEsIGIgPSAxLjA1ODU5ODI0ODc3MTk5NjUsIDEuMTI4Nzc0MzE3OTY4ODI5NAogICAgbWl4ID0gKDEgLSBhKihiLW1peCkqKjIpKiouNQogICAgbWluaW11bSA9ICgxIC0gYSooYi1TSU5fUE9TKSoqMikqKi41CiAgICBtaXggLT0gbWluaW11bQogICAgbWl4IC89ICgxIC0gYSooYi0xKSoqMikqKi41IC0gbWluaW11bQogICAgcmV0dXJuIHNxcih4KSAqIGFicyhucC5zaW4oeCkpKiooMS1taXgpICogYWJzKHNxcih4KSkqKm1peAoKQGxhbWJkYSBmOiBmdW5jdGlvbnMuc2V0ZGVmYXVsdCgnR2VvbWV0cmljIE1lYW4gKHRyaS9zcXIpJywgZikKZGVmIGdlb20yKHgsIG1peCk6ICAKICAgIGEsIGIgPSAwLjYzNzU3NjU1MDgzMTQ0NjksIDEuMTg1NzY4MzU5MTQ2NTg5NQogICAgZiA9IGFicygxIC0gYSooYi1taXgpKioyKSoqLjUKICAgIG1pbmltdW0gPSAoMSAtIGEqYioqMikqKi41CiAgICBmIC09IG1pbmltdW0KICAgIGYgLz0gICgxIC0gYSooYi0xKSoqMikqKi41IC0gbWluaW11bQogICAgcmV0dXJuIHNxcih4KSAqIGFicyh0cmkoeCkpKiooMS1mKQoKZGVmIHNoYXBlKGYsIG1peCwgeCk6CiAgICBxMSA9IHBsc3dfYW1vdW50ICogKHdhcnBfYW1vdW50ICsgKDIqc2tld19hbW91bnQgLSAxKSAqICgKICAgICAgICB3YXJwX2Ftb3VudCBpZiBza2V3X2Ftb3VudCA8IDAuNSBlbHNlIDEgLSB3YXJwX2Ftb3VudAogICAgKSkKICAgIHEyID0gcGxzd19hbW91bnQKICAgIHEzID0gcGxzd19hbW91bnQgKyAoMSAtIHBsc3dfYW1vdW50KSAqICh3YXJwX2Ftb3VudCArICgxIC0gMipza2V3X2Ftb3VudCkgKiAoCiAgICAgICAgd2FycF9hbW91bnQgaWYgc2tld19hbW91bnQgPiAwLjUgZWxzZSAxIC0gd2FycF9hbW91bnQKICAgICkpCgogICAgeSA9IG5wLmludGVycCgKICAgICAgICB4ICUgKDIqz4ApLCAKICAgICAgICAyKs+AKm5wLmFycmF5KFswLCBxMSwgcTIsIHEzLCAxXSksCiAgICAgICAgMirPgCpucC5hcnJheShbMCwgLjI1LCAuNSwgLjc1LCAxXSksCiAgICApCiAgICBBID0gMSAtICgxIC0gMip3YXJwX2Ftb3VudCkgKiBzcXIoMip5KQogICAgYyA9IC0yICogKHdhcnBfYW1vdW50IC0gMC41KSAqIHNxcih5ICsgz4AvMikKICAgIHJldHVybiBBICogZih5LCBtaXgpICsgYwoKZGVmIHdhdmVfZm10KG4pOgogICAgaWYgbiA9PSAwOgogICAgICAgIHJldHVybiAnVHJpYW5nbGUnCiAgICBlbGlmIG4gPT0gMTAwOgogICAgICAgIHJldHVybiAnU3F1YXJlJwogICAgcmV0dXJuIGYne24vMTAwOi4wJX0nCgpmID0gZnVuY3Rpb25zW3N0LnNlbGVjdGJveCgnV2F2ZXNoYXBlIEludGVycG9sYXRpb24gTWV0aG9kOicsIGZ1bmN0aW9ucy5rZXlzKCkpXQoKY29sMSwgY29sMiwgY29sMyA9IHN0LmNvbHVtbnMoMykKc2tld19hbW91bnQgPSBjb2wxLnNsaWRlcignU2tldzonLCAwLCAxMDAsIDUwLCA1LCBmb3JtYXQ9JyVkJSUnKS8xMDAKcGxzd19hbW91bnQgPSBjb2wyLnNsaWRlcigiJ1B1bHNlIFdpZHRoJzoiLCA1LCA5NSwgNTAsIDUsIGZvcm1hdD0nJWQlJScpLzEwMAp3YXJwX2Ftb3VudCA9IGNvbDMuc2xpZGVyKCdXYXJwOicsIDAsIDEwMCwgNTAsIDUsIGZvcm1hdD0nJWQlJScpLzEwMAoKcGxvdF9jb250YWluZXIgPSBzdC5jb250YWluZXIoKQoKc2luZ2xlX3RyYWNlID0gc3QuY2hlY2tib3goJ1NpbmdsZSBUcmFjZScpCndhdmVfbWl4ID0gc3Quc2VsZWN0X3NsaWRlcignQmFzZSBXYXZlc2hhcGU6JywgcmFuZ2UoMCwgMTAxLCA1KSwgNTAsIGRpc2FibGVkPW5vdCBzaW5nbGVfdHJhY2UsIGZvcm1hdF9mdW5jPXdhdmVfZm10KS8xMDAKCmlmIHNpbmdsZV90cmFjZToKICAgIG1peGVzID0gW3dhdmVfbWl4XSAKICAgIGNtID0gbXBsLmNvbG9ycy5MaW5lYXJTZWdtZW50ZWRDb2xvcm1hcC5mcm9tX2xpc3QoJzxkb3VibGVfcmFpbmJvdz4nLCBDT0xPUlNbOi0xXSkKICAgIHBsb3Rfa3dkcyA9IHsnYyc6IGNtKHdhdmVfbWl4KX0KZWxzZToKICAgIG1peGVzID0gbnAubGluc3BhY2UoMCwgMSwgbnVtPTExKQogICAgcGxvdF9rd2RzID0ge30KCnggPSBucC5saW5zcGFjZSgwLCA0Ks+ALCBudW09NTAwKQpmb3IgaSwgbWl4IGluIGVudW1lcmF0ZShtaXhlcywgc3RhcnQ9MSk6CiAgICBwbHQucGxvdCh4LCBzaGFwZShmLCBtaXgsIHgpLCB6b3JkZXI9Mi41IC0gaS8yNCwgKipwbG90X2t3ZHMpCgpwbHQueGxpbSh4Lm1pbigpLCB4Lm1heCgpKQpwbHQueWxpbSgtMS4wNSwgMS4wNSkKZmlnID0gcGx0LmdjZigpCmZpZy5zZXRfc2l6ZV9pbmNoZXMoMTYsIDYpCmF4ID0gcGx0LmdjYSgpCmF4LnNldF9hc3BlY3Qoz4AvMikKYXgueGF4aXMuc2V0X21ham9yX2Zvcm1hdHRlcihQaUZvcm1hdHRlcigpKQpheC54YXhpcy5zZXRfbWFqb3JfbG9jYXRvcihtcGwudGlja2VyLk11bHRpcGxlTG9jYXRvcijPgC8yKSkKYXgueWF4aXMuc2V0X21ham9yX2xvY2F0b3IobXBsLnRpY2tlci5NdWx0aXBsZUxvY2F0b3IoMSkpCgpwbG90X2NvbnRhaW5lci5weXBsb3QocGx0LmdjZigpKRKuEAoObXBsX3N0eWxpbmcucHkSmxAKmBAjIFNldHVwIG1hdHBsb3RsaWIgYmFzZSBzdHlsaW5nIChpbmNsdWRpbmcgZG93bmxvYWQgb2YgU29tZXR5cGVNb25vIGZvbnQpDQppbXBvcnQgbWF0aA0KaW1wb3J0IG1hdHBsb3RsaWIucHlwbG90IGFzIHBsdA0KaW1wb3J0IG1hdHBsb3RsaWIgYXMgbXBsDQpmcm9tIHBhdGhsaWIgaW1wb3J0IFBhdGgNCmltcG9ydCBweW9kaWRlDQoNCg0KQ09MT1JTID0gWw0KICAgICcjZmYwMDAwJywgJyNmZjdmMDAnLCAnI2ZmZmYwMCcsICcjN2ZmZjAwJywgJyMwMDdmZmYnLCAnIzdmMDBmZicsDQogICAgJyM3ZjAwMDAnLCAnIzdmM2YwMCcsICcjN2Y3ZjAwJywgJyMzZjdmMDAnLCAnIzAwM2Y3ZicsICcjM2YwMDdmJywNCl0NCg0KDQphc3luYyBkZWYgc2V0dXAoKToNCiAgICBmb3Igc3R5bGUgaW4gWydSZWd1bGFyJywgJ1JlZ3VsYXJJdGFsaWMnLCAnQm9sZCcsICdCb2xkSXRhbGljJ106DQogICAgICAgIHAgPSBQYXRoKGYnLi9Tb21ldHlwZU1vbm8te3N0eWxlfS5vdGYnKQ0KICAgICAgICBpZiBub3QgcC5leGlzdHMoKToNCiAgICAgICAgICAgIHJlc3BvbnNlID0gYXdhaXQgcHlvZGlkZS5odHRwLnB5ZmV0Y2goZidodHRwczovL2ZvbnRzLmNkbmZvbnRzLmNvbS9zLzIwMDQ0L1NvbWV0eXBlTW9uby17c3R5bGV9Lm90ZicpDQogICAgICAgICAgICB3aXRoIG9wZW4ocCwgJ3diJykgYXMgZjoNCiAgICAgICAgICAgICAgICBmLndyaXRlKGF3YWl0IHJlc3BvbnNlLmJ5dGVzKCkpDQogICAgICAgIG1wbC5mb250X21hbmFnZXIuZm9udE1hbmFnZXIuYWRkZm9udChzdHIocCkpDQoNCiAgICBjb2xvcnMgPSBtcGwucmNzZXR1cC5jeWNsZXIoY29sb3I9Q09MT1JTKQ0KICAgIHBsdC5yYygnZmlndXJlJywgZmFjZWNvbG9yPSdibGFjaycpDQogICAgcGx0LnJjKCdheGVzJywgZmFjZWNvbG9yPScwJywgZWRnZWNvbG9yPScjYWZhZmFmJywgbHc9MiouNzIsIHByb3BfY3ljbGU9Y29sb3JzLCBncmlkPVRydWUpDQogICAgcGx0LnJjKCdncmlkJywgbGluZXN0eWxlPSgwLCAoMywgMykpLCBjPScjNjQ2NDY0JykNCiAgICBwbHQucmMoKCd4dGljaycsICd5dGljaycpLCBjb2xvcj0nI2FmYWZhZicsICoqeydtYWpvci53aWR0aCc6IDIqLjcyfSkNCiAgICBwbHQucmMoJ2ZvbnQnLCBmYW1pbHk9J1NvbWV0eXBlIE1vbm8nLCBzaXplPTE0LCB3ZWlnaHQ9J2JvbGQnKQ0KICAgIHBsdC5yYygNCiAgICAgICAgJ21hdGh0ZXh0JywNCiAgICAgICAgZm9udHNldD0nY3VzdG9tJywNCiAgICAgICAgcm09J1NvbWV0eXBlIE1vbm8nLA0KICAgICAgICBpdD0nU29tZXR5cGUgTW9ubzppdGFsaWMnLA0KICAgICAgICBiZj0nU29tZXR5cGUgTW9ubzpib2xkJywNCiAgICApDQoNCg0KY2xhc3MgUGlGb3JtYXR0ZXIobXBsLnRpY2tlci5TY2FsYXJGb3JtYXR0ZXIpOg0KICAgIGRlZiBfX2NhbGxfXyhzZWxmLCB4LCBwb3M9Tm9uZSk6DQogICAgICAgIGlmIGxlbihzZWxmLmxvY3MpID09IDA6DQogICAgICAgICAgICByZXR1cm4gJycNCiAgICAgICAgDQogICAgICAgIGRlbiA9IG1hdGgucGkgLyBtaW4oYWJzKGwpIGZvciBsIGluIHNlbGYubG9jcyBpZiBsICE9IDApDQogICAgICAgIGRlbiA9IG1heChyb3VuZChkZW4pLCAxKQ0KICAgICAgICBudW0gPSByb3VuZChkZW4gKiB4IC8gbWF0aC5waSkNCiAgICAgICAgZ2NkID0gbWF0aC5nY2QobnVtLCBkZW4pDQogICAgICAgIA0KICAgICAgICBudW0gLy89IGdjZA0KICAgICAgICBpZiBudW0gPT0gMDoNCiAgICAgICAgICAgIHJldHVybiAnJDAkJw0KICAgICAgICANCiAgICAgICAgZGVuIC8vPSBnY2QNCiAgICAgICAgaWYgZGVuID09IDE6DQogICAgICAgICAgICBpZiAtMSA8PSBudW0gPD0gMToNCiAgICAgICAgICAgICAgICByZXR1cm4gZnInJHsiLSJbOm51bTwwXX1ccGkkJw0KICAgICAgICAgICAgcmV0dXJuIGZyJyR7bnVtfVxwaSQnDQogICAgICAgIGVsaWYgLTEgPD0gbnVtIDw9IDE6DQogICAgICAgICAgICByZXR1cm4gZnInJHsiLSJbOm51bTwwXX1cZnJhY3t7XHBpfX17e3tkZW59fX0kJw0KICAgICAgICANCiAgICAgICAgcmV0dXJuIGZyJyR7Ii0iWzpudW08MF19XGZyYWN7e3thYnMobnVtKX0gXHBpfX17e3tkZW59fX0kJxoKbWF0cGxvdGxpYg,= That's an stlite (Streamlit lite) link. Streamlit is a Python library for producing web apps/dashboards — stlite combines this with Pyodide (an implementation of Python that uses WebAssembly) to create a version which runs locally, within the (your) web browser. When loading the page there's a roughly 20 sec (on my machine) 'installation' step, where the necessary files are retrieved & loaded into memory (temporarily setting up a Python interpreter until the page is refreshed/closed), after which the app is run. The best explanation of the controls is probably had via interacting with them, but below is a brief description. - Waveshape Interpolation Method: selects the method for obtaining the intermediate waveshapes between triangle & square — the options include the three from the OP plus another two: harmonic & geometric means, directly between triangle & square without the requirement of progressing via a sine wave.
- Skew: adjusts the base triangle wave towards a sawtooth wave at one extreme & reverse sawtooth (ramp) wave at the other extreme.
- 'Pulse Width': adjusts the ratio of the duration of first half-period to the duration of the second — that is, if we were restricted to just square waves, the pulse width.
- Warp: adjusts the ratio of the size (both duration & amplitude) of the first & third quarter-periods, at extreme values this produces the aforementioned 'shark-fin' waves.
Below the graph we have the option to plot just one specific trace rather than the sweep across the waveshapes in 10% increments.
FYI the reason that link is sooo looong is that is contains a base 64 encoded version of the source code, but if you're curious you can just prepend "edit." to the URL (i.e. "edit.share.stlite.net/...") to open it in the stlite editor.
|
|
|
Post by unreg on Jun 14, 2023 17:05:50 GMT -5
You said, “area under the curve”, and that means Calculus, along with “integral”. I would simply vote for the clipping curve bc that possibly sounds the best… I think. Sorry… really inconveniently ot: “if A == 0” seems like a waste of an if to me in NES assembly land, since loading A… like lda A automatically sets the zero flag if A is equal to zero… so no comparison needed; just directly follow that with bne second_if . (That branches to label second_if if the zero flag isn’t set; then directly after that add your code to return tri(X).) This thread is interesting! Great impressive job sir Yogi B! You’re a calculus master!
|
|