-
weightless
@RandLaneFly Hi, I'll do my best to give you some replies:
- yes, offset just adds a fixed frequency in hertz regardless of the ratio or the note played. It's useful if you want to use an operator as an LFO;
- I used clip because the patch can receive values from external messages from the parent patch, the clip avoids unwanted values whatever you send to it. It's got to do with the GUI of the patch rather than with the synth itself;
- here is pphasor~.pd, it's just a phasor with sample accurate phase reset to be able to sync the operators accurately. I adapted it from some old patch I found, but I can't remember what I've changed to be honest. It only replaces phasor~ so you'd still need cos~ afterwards;
- about wrap~ and cos~: in a normal situation, where you do phase modulation with phasor and cos~, you don't need wrap~ because cos~ already wraps whatever phase it receives, even if it's outside the 0..1 range. In this case, instead of cos~, I used a table to store the waveform. Two reasons for this: 1) I wanted to be able to change the resolution of the sine and 2) I wanted to be able to use other waveforms other than a sine wave. Having said that, if your phase (after being scrambled around by all the modulators acting on it) has to address a table, you need it to be within the range of that table, otherwise you would be addressing points that are outside of the table (which would result in silence). This is why the phase is wrap~ped to the 0..1 range, and then multiplied by the length of the table in samples (4096 in this case);
- I don't understand what you mean by:
@RandLaneFly said:
But I take it after that it's sending out the phases to the tables, what is the purpose of the line being formed and sent into the right inlet?
The tabread4~ object is your output waveform for this specific operator you're looking at. Afterwards it's multiplied by the envelope (received on the right inlet of the *~ object), and then it's sent to the output (aka, what you hear) and to the phase tables.
- If by "the math in the table patch" you mean the fexpr~ object, that is exactly the same as found in the thread I mentioned in the first post. It averages the last two samples in order to filter very high, harsh frequencies in the feedback path. It's possible that FM8 uses a different filter, which would result in a different sound, but I have no way of knowing that.
I'm afraid the only resource I used for making this mess of a patch was that single thread about feedback, and then I expanded it for multiple operators. I very much like how it sounds, but it's incredibly expansive with polyphony, and on my computer 3 operators seem to be the limit, I wanted to try six but could never manage.
-
weightless
@dadasane Here is the sampler I mentioned. I left it before finishing it but it kind of works like a hardware sampler would in principle. Open sampler16~-help.pd, load and play an audio file in the yellow abstraction on top (this would be the equivalent of your live input, you can connect an adc~ there as well). Then on the purple abstraction click the red R button, this records 1 second of the live input with the specified bitrate (you can change both length and bitrate in the number boxes). I suppose you can add the samplehold~ mechanism to reduce the sample rate as well as the bitrate. Once you see the updated waveform you can play it like a sampler. Might not be what you were after, but I like how it sounds.
I hope I've included all abstraction to make it work, let me know if something is missing. You also need shadylib for the envelope.
sampler16~.zip -
weightless
@dadasane Why not load the sample in an array, play it through the sample rate/bit depth reduction and record the output into another array, and then have the sampler read that instead of the original sample? I made a sampler like that once and it sounds interesting, it might not be exactly what you're after but you could try it all the same. Another important thing is to use [tabread~] instead of [tabread4~] for more crunchiness.
I'm not sure how you would do it faster than realtime but for short samples of a few seconds it shouldn't be a big issue, and you can save the aliased samples as well.
-
weightless
@DizeHD If the last object is a signal object, you need to use [outlet~] instead.
-
weightless
@boonier said:
How would this work in the context of [savestate] - the text files would be loaded into each instance of the abstraction individually, or as one global preset load?
They would be loaded only in the instance of the abstraction on which you save the presets. Think of it this way: the abstraction contains a [text] object, and you save its state (ie, the text it contains, whether it's a set of presets or a poem) which can be different for each instance of the abstraction. I can't see any advantages with this approach, but it may work for you.
-
weightless
@boonier I haven't had time to think about it, but in fact my feeling is that it wouldn't have any advantages at all. With presets you have to chose when to save a new one, compared to saving the "state" in which the abstraction was when you saved and closed the patch. At that point, I think it's more efficient if the presets are stored in text files so that they can be shared by all instances of the abstraction.
-
weightless
@djpersonalspace By the same token, this produces a triangle wave:
How bizarre! -
weightless
@rotho In Pd vanilla [text] could do it I think. [text search] is not very versatile but there are workarounds. For example, if you have one line of text for each file, and each field is a fixed tag (first field: filename, second field: key, third field: bpm etc), you can do searches with [text sequence] followed by list split, select etc.
Hope that makes sense. -
weightless
@zxcvbs Hi, yes I realise the patch is very counterintuitive, I'll do my best to try and explain what's going on. Let's forget about polyphony and consider one voice.
The PMops abstraction is the single voice, inside it are the three operators (PMop, both badly named sorry) and the tables subpatch. The basic structure of each operator could be reduced to the simple:
but the +~ object, instead of receiving the phase from a fixed modulator, it receives it from a table. Each operator has its own table, so operator 1 receives the phase from tabreceive~ $0-phase1 which is connected to +~.
At the same time (to populate those tables), the output from cos~ of each operator is sent to all tables, how much of the signal goes to each is decided in the modulation matrix.
The reason I decided for this arrangement is because this way each operator can send its phase to any operator, including itself (feedback). As explained in the first post, feedback has to be done with the operator working at block~ 1 in order to sound good, but if an operator receives the feedback every sample (block~ 1) but to that you add the phase of the other modulators every 64 samples (the default block size), the result sounds bad. This is the reason I used this mess of tables, to have an FM-8 style modulation matrix.
If you want to implement hard-coded algorithms, the same thing can be done without tables, patching each algorithm in its own abstraction or subpatch and switch between them as @whale-av suggested in your thread. Plus I see none of your algorithms have feedback, so they don't even need to run at block~ 1, and therefore the approach with tables is really not necessary.
Hope this clarifies things a bit. -
weightless
@Revoan Hi, instead of [table notes], you can use [array define -k notes]. The -k flag means that the content of the table is saved with the patch. Alternatively, you can connect a [loadbang] to the message which sends the notes to the table. Loadbang sends a bang as soon as the patch has loaded.
-
weightless
@nickporcaro If it helps, this is a version of [receive] which lets you switch the source using dynamic patching. dreceive.zip
For me the trickiest part is keeping track of duplicates in the chain. If you are willing to manually specify duplicates in the text (something like: 1-lop~ 400, hip~ 20, 2-lop~ 400 etc) then my patch could be made usable I think, otherwise if we are talking about a chain of hundreds of filters, making a duplicate checker would require some work.
How many filters are you looking at using? -
weightless
@nickporcaro Ah yes, of course. Each filter would have to have a unique name otherwise if you have, say, two [lop~ 400] you wouldn't know which is which. If this is a one-off sort of application with just simple filter objects in the chain, you could modify the patch (and the list in text) as to give unique names to double filters, something like [lop~ 400 1], [lop~ 400 2] etc. When you want to move them around you can just cut and paste the corresponding line in text and put it somewhere else (it doesn't matter if lop~ 1 comes after lop~ 2 as they would be the same).
If you are looking for a more universal method which includes more complicated stuff in the chain, you'd probably have to somehow check upon creation whether the object you are creating already exists. Perhaps DRFX can provide some helpful insights on how to do it, but I haven't checked it out yet. -
weightless
@nickporcaro As far as I understand it what @whale-av was saying is that you get a click when changing the order. This could be forced to happen between dsp cycles, I think. What do you mean by "depend on the order of catch~ and throw~"?
-
weightless
@nickporcaro Hi, this is something I thought up in response to your question, I haven't actually used it so I'm not sure if it works reliably, but at least it gives you an idea on a possible approach. This method basically uses abstractions and their arguments to create the filter themselves and their respective catch~/throw~ objects, then the order of execution is specified in a text. This way you can save/load different routings to .txt files. signal_order.zip
It's not exactly "on the fly", but I don't think there are many other ways that would scale up easily.
Hope it helps. -
weightless
This is a vanilla version of the external @Johnny-Mauser mentioned.
vsplitfilename.pd
vsplitfilename-help.pd -
weightless
@prandam You can have many different variables in a message, which you call with $1, $2 etc. You can put them in whichever order you want to construct the message. This would be an example:
Also this abstraction might be useful (I don't remember who made it) to split the filename from its path.
vsplitfilename.pd
vsplitfilename-help.pd -
weightless
@prandam Hi, what you are doing in [swavfile] can be done with one message box, you just have to insert $1 in place of the name stored in the text file, the message substitutes $1 with the first item it receives. Once you sent the open message, readsf~ needs a start message sent each time you want to play the newly loaded sample. You can send consecutive messages in the same message box by using the comma as a separator.
solsolsol_2.pd -
weightless
@metaphysician The way I've been doing it for enveloped synths is to send 1 to switch with the note on message, and if the sustain is equal to zero, schedule a 0 to send to switch after the attack+decay times. If sustain is different than zero, with the note off message schedule a 0 to switch after the release time. This assumes you use an ADSR type of envelope, but the concept can be easily adapted I think. logic_switch.pd