jamshark70's code

FFT additive oversampling (graphical demo of the sampling theorem)

I originally wrote this to demonstrate what sampled audio really represents -- that is, if a series of samples represents the one and only band-limited function that passes through the sampled values, we could obtain the band-limited function by adding up the cosines given by a Fourier transform. Further, doing it additively, we could select ranges of frequencies and see, interactively, each frequency band's influence on the final waveform. - The Gibbs effect is obviously visible for any sequences of samples that have discontinuities in the value or slope (e.g. non-bandlimited sawtooth or pulse waves). x = Env([0, 0.75, -1, 1, 0], [0.1, 0.01, 0.4, 0.2]).discretize(128); - Inter-sample distortion is clearly visible for 0 dBFS pulse waves. ( var stream = Pstutter(Pseq([24, 8], inf), Pseq([1, -1], inf)).asStream; x = Signal.fill(128, stream); x.plot; ) - If you use a rectangular window and the window can't play continuously as a cycle, there will be a discontinuity from the end of the window to the beginning. The Gibbs effect is obvious here, too. This is good to demonstrate to students why phase vocoders should pretty much always use a windowing function (e.g. Hanning). x = Signal.fill(128, { |i| sin(i / 128 * 2pi * 1.1) }); - Try lots of input signals. It's quite dramatic how the partials reinforce each other in the right places, and cancel in the right places, and always add up. BTW this example has a lot of UserView tricks. Note, for example, that to do the animation, I had to set a state variable *outside* the scope of the drawFunc, and 'refresh' the UserView to update the frame. Usage: 1. Set 'x' to a Signal containing 128 or 256 values. 2. Run the long code block. 3. The range slider chooses a band of frequencies to include -- the audio equivalent is a pair of PV_BrickWall filters. 4. The left-hand button will add partials at timed intervals. 5. The right-hand button will add one partial, and animate the way that the new partial "bends" the waveform. This is *really* instructive!

Method help re-ordering utility

If you have a large class with a lot of methods, you probably want to sort the methods in the auto-generated SCDoc template into an order that's meaningful for the end user (related methods grouped together, more important methods toward the top). But SCDoc syntax, with method::, argument:: and returns:: tags, makes it a bit harder to see where entries begin and end, and provides no overview of the list of methods. This GUI reads the METHOD:: sections from the auto-generated template and presents a list box containing the method names. 1. Get the list of METHOD:: sections, as a string, into the variable 'm'. If the string is in a disk file, use the upper code block to read it. 2. Run the second block. 3. Initially, the methods are sorted in ASCII order. The ^/v buttons allow you to change the order, or use "a" and "z" keys while focusing on the list box. 4. Close the window or click the "P" button to reprint the full templates, in the new order. Then you can copy/paste these back into the schelp file.

Tracking buffer .read and .free

If you need to maintain a list of buffers (say, for a GUI) and have that list update automatically even if the user issues Buffer.read or b.free commands independently, '/done' notifications from the server can help you. For reading, you can get the Buffer object from Buffer.cachedBufferAt (and thereby determine the path that was read). If you save the Buffer in your own collection, .copy the buffer (so that you can still get its bufnum after .free). Note that buf.copy is NOT buf.copyData! For freeing, you don't have access to the buffer object. The example just posts messages in response. Substitute your own actions where "debug" appears.

GUI: Scrolling list of multi-view items

A common GUI requirement is a scrollable list of similar items. If the list consists of strings, it's easy -- use ListView. If, for example, each "line" should have a pop-up menu, a text field and a toggle button, there's ScrollView. We also have Qt layouts, which are supposed to save us from the trouble of calculating coordinates. Scrolling lists are usually arranged vertically, so: VLayout. But, there are a couple of tricks: * If you set the .layout of a ScrollView directly, the layout will use the visible area only -- no scrolling. SOLUTION: Explicitly replace the ScrollView's canvas with your own View(), and apply the layout to the canvas. (This is in the help file, but it's easy to overlook.) * Now, if you just start adding views into the VLayout, empty space will be distributed equally between them. Normal behavior is that the first few entries appear at the top, and fill downwards. SOLUTION: According to the help, 'nil', added to any layout, is a stretchable "spacer." But, this doesn't interact well with the next trick. So use an empty View() instead. * layout.insert(view, 0) will display the items in reverse order -- new items at the top. SOLUTION: layout.insert(view, scrollview.children.size - 1) Each line, then, is a View() (with appropriate size hints). Apply a layout (probably HLayout), add the operating views into that layout -- and then insert this parent view into the ScrollView's canvas layout. Scrolling behavior is as expected: No scrollbars are visible until the views go below the visible area. If you scroll all the way to the bottom, there's no empty space under the last text view. Note also that you can delete items simply by locating one of the ScrollView's children and calling .remove on it. The empty space closes up automatically!

Graphic illustration of granular time-stretching

1 1

Graphically represents a small set of granular windows, with variable offset. Move the "ratio" slider, and you can see and hear time stretching.