• ddw_music

    @jameslo said:

    Like too many things in Pd, it feels perverse to have to tell a new user that in order to display a unique character sequence for a number, you have to turn it into a filename :)

    It's a minority case, I guess...? I'm willing to bet that in most cases, a user who summed up 0.1 ten times wants to see "1" and not "1.00001".

    And gosh, it looks like SC has several numeric datatypes, ways to cast between them, and multiple ways to display each. How profligate! ;)

    SC has 32-bit int (Integer) and 64-bit float (Float, not Double) in the language side, and the audio side runs on 32-bit floats like Pd audio does. 64-bit floats can be chopped down to 32 bits, but the value is stored as an int and you can't do 32-bit float math on it. But, converting a couple of such ints to double (Float.from32bits) and doing math on them should be (pretty much) the same as single-precision math, except maybe a bit of noise in the LSB.

    hjh

    posted in technical issues read more
  • ddw_music

    How about this... in SC:

    x = 1.00000048.as32Bits;
    
    x.asBinaryString(32).clump(8);
    -> [00111111, 10000000, 00000000, 00000100]
    

    Giving sign bit = 0, exponent = 01111111 and mantissa = (implicit) 1.00000000000000000000100 or 20 zeroes before the trailing 1.

    SC doesn't have a primitive to convert a 32-bit float into a string. But it does preserve the mantissa when converting to a 64-bit float (zero-pads the mantissa): sign bit = 0, exponent (11 bits) = 01111111111, mantissa = (implicit) 1.000000000000000000001 (same 20 zeroes, dropping the rest of the 53 mantissa bits) -- therefore the mantissas are equivalent in both 32-bit and 64-bit floats.

    y = Float.from32Bits(x);
    z = [y.high32Bits, y.low32Bits].collect { |b| b.asBinaryString(32).clump(8) };
    
    -> [[00111111, 11110000, 00000000, 00000000], [10000000, 00000000, 00000000, 00000000]]
    
    y
    -> 1.0000004768372  // rounds to 1.00000048
    

    So it should be okay to hack the 32-bit by adding to or subtracting from the least significant bits: x+1 = the next possible float for this exponent.

    [x-2, x-1, x, x+1, x+2].collect(Float.from32Bits(_))
    
    -> [1.0000002384186, 1.0000003576279, 1.0000004768372, 1.0000005960464, 1.0000007152557]
    

    ... where the difference between subsequent floats ~= 0.0000001192092 or ~= 0.00000012, meaning that at this scale it is not possible for a 32-bit floating point number to represent a different value beginning with 1.0000004. So %.7g "should" be sufficient (though I wouldn't call myself an expert -- this is an empirical demo, not a proof).

    hjh

    posted in technical issues read more
  • ddw_music

    @jameslo said:

    That Fortran discussion I linked to...

    Sadly I'm not allowed to read it at the moment because I'm on a tablet right now, and "HTML content omitted because you are logged in or using a modern mobile device" (which... I suppose it would make sense that enthusiasts of an outdated language would not favor "modern" devices for reading 🙄 )

    presented the number 1.00000048 as an example of a 9 digit decimal that has a SPFP value that's not representable in 8 decimal digits, but it appears to me that 1.0000005 works fine. How would you prove that 8 digits is sufficient for all SPFP numbers?

    No idea. At least, the 24 bit mantissa (23 explicit bits plus an implicit 1 left of the binary point) supports 16,777,216 distinct values, which does comprise 8 decimal digits. But there isn't a 1:1 correspondence in digit count when the exponent goes down, e.g. 16 has 2 decimal digits but its inverse 1/16 = 0.0625 = 6.25x10^-2. So there at least is a counterexample showing that 1/x might need more decimal digits than x (as an integer) would.

    hjh

    posted in technical issues read more
  • ddw_music

    @jameslo said:

    Would [makefilename $.7g] suffice? Or would it have to be .8 or .9 as some argue in this discussion?

    %.7g isn't enough, but %.8g seems to catch it.

    pd-precision.png

    And for all cases 7 8 or 9, it would mean that there could be different displays of floats that actually equal each other, correct?

    Yes, but keep in mind: if you ask C to convert a binary floating-point number to a decimal string with more precision than exists in the original binary, the trailing digits are basically garbage. With %.9g you will definitely be able to see that two single precision floats are different, but don't rely on the specific value.

    For [==], it's kinda better not to use it at all with fractional floats, unless you're sure the denominator will always be a power of two. [==] might be correct but might sometimes give you false negatives; the absdif approach lets you control the precision that's relevant for equivalence.

    pd-fuzzy-equals.png

    hjh

    posted in technical issues read more
  • ddw_music

    @jameslo said:

    But what prevents Pd from displaying the inexact result, e.g. in the number box above [expr]? If the goal of patching is to make things more friendly for non-programmers, how is it helpful to hide it?

    In fact, when a user types in 0.1 and it displays 0.100001 (I forget how many zeros for single-precision), this is more disturbing to non-programmers.

    ... because that's exactly what you get when you don't round off the last bit or two for string conversion: a whole lot of 0.n000001 or 0.n999999. SC tried this for awhile, but had to revert to a slightly lower precision for float-to-string because users really hated the more accurate display.

    Python can demonstrate with double precision floats -- https://en.wikipedia.org/wiki/Double-precision_floating-point_format says "The 53-bit significand precision gives from 15 to 17 significant decimal digits precision (2−53 ≈ 1.11 × 10−16)" so let's try both:

    $ python3
    >>> for x in range(1, 10): x = x * 0.1; print(f"{x:.15f}")
    ... 
    0.100000000000000
    0.200000000000000
    0.300000000000000
    0.400000000000000
    0.500000000000000
    0.600000000000000
    0.700000000000000
    0.800000000000000
    0.900000000000000
    
    >>> for x in range(1, 10): x = x * 0.1; print(f"{x:.17f}")
    ... 
    0.10000000000000001
    0.20000000000000001
    0.30000000000000004
    0.40000000000000002
    0.50000000000000000
    0.60000000000000009
    0.70000000000000007
    0.80000000000000004
    0.90000000000000002
    

    Increasing the string representation to include more digits makes it necessary to render into the UI the noise inherent in the least significant bit(s). This isn't appealing to everyone.

    Since Pd uses single precision, 6 digits is the low end (and exactly the precision Pd displays). If Pd expanded these strings to 8 digits, you would see trailing digits for fractions that seem like they should be simple.

    I think it's pretty common practice with floats to calculate with more precision than you're going to display, and round off for UI strings.

    hjh

    posted in technical issues read more
  • ddw_music

    When performing division of rational numbers, the result will be exact if the denominator factors out such that all factors are a power of a factor of the numeric base. We're used to decimal, so those factors are 2 and 5. 20 = 2•2•5 so it's ok; 27 = 3•3•3 so we know this will be a repeating fraction (since 3 is not a factor of 10).

    Floating point numbers in computers are base 2, so, for non-repeating division, the denominator must be a power of 2.

    First you have [/ 1] -- 1 = 2^0 so, ok.

    Then you have [/ 64] -- 64 = 2^6 so, ok.

    When you introduce division by 44.1, then the denominator includes two 3s and two 7s (plus 2^-1 and 5^-1). These aren't powers of 2, so the fraction will be infinite, and rounding it off to the available precision is an approximation. Multiplying again doesn't restore the precision.

    Like, 2/3 = 0.66666...7. Let's pick an arbitrary precision, say, 4 digits. 2/3 = 0.6667 (or, the floating point way, 6.667•10^(-1)). Now you multiply this back by 3 and you get 2.0001 -- there's your "higher than." This must also get rounded off, but at least it shows that inaccuracy when scaled up can eventually become visible.

    The assumption that floating point math is exact is basically a good way to set yourself up for confusion or disappointment. (That is, this isn't Pd's fault -- it's IEEE 754.)

    hjh

    posted in technical issues read more
  • ddw_music

    @willblackhurst said:

    in my one I just install pd vanilla and then do sudo apt install gem. and then I dont have to do anything else.

    It's true that in most cases this is sufficient. Anybody finding this thread in the future should try installing from packages first.

    I found in my case that, inevitably at some point, I will have to install some other media plugins and enable them in Gem. If I don't, certain image or video files will not be accessible in Gem. That's why for the last two or three OS update cycles, I have built from source instead of installing from packages, and I expect to continue doing so.

    hjh

    posted in technical issues read more
  • ddw_music

    OK, I found the problem.

    For Gem, it's mandatory to sudo make install. In Ubuntu, by default, this goes into /usr/local/lib/pd/extra. Then the problem is that this location is not added by default into the PD path, so [declare -lib Gem] doesn't know to look there.

    I had tried to work around that problem by symlinking the gem repository into my user-level externals directory, but that didn't work because the repository's folder structure doesn't match what is needed to use the external.

    So the solution was sudo make install, then add /usr/local/lib/pd/extra to the path.

    hjh

    posted in technical issues read more
  • ddw_music

    After 4 days of software reinstallation, I built Gem from sources, then this:

    gem-fail.png

    Also doesn't work with -lib Gem -path Gem either.

    I'm... just done troubleshooting.

    Help.

    hjh

    posted in technical issues read more
  • ddw_music

    @jameslo said:

    But wait! I just checked the harmonics of a full wave rectified {cos~] and all the even FFT terms have positive magnitude, so this post appears to be correct. And now I just reran my first test and set my "top only" slider to exactly 2 and am getting the fundamental + all even harmonics. Why am I getting so tripped up by this?!!!

    Hm, yes, the argument about double frequency does make sense. I guess neither of us considered the possibility that the initial test may have been flawed.

    What actually were your settings for the two sliders in the first screenshot? I'm curious to try to reproduce it but I can't see what the numbers are.

    hjh

    posted in technical issues read more
  • ddw_music

    @TheBarker

    If it's tabwrite~ then you'd have to bang it precisely when it should loop back around to the beginning of the array. That might be doable at 48 kHz because 48000 is divisible by 64. 44.1 kHz is likely to be trickier.

    For this usage, I'm a fan of count~ and poke~ in the cyclone library.

    hjh

    posted in technical issues read more
  • ddw_music

    @jameslo said:

    I think it's things like this that misled me in the first place

    YouTube channel House of El-AI calculates, based on Google's number of daily queries scaled down to an hour and multiplied by a ~9% hallucination rate, that Google's AI overview serves up 57 million wrong answers every hour.

    hjh

    posted in technical issues read more
  • ddw_music

    As you noted, [poly] works in the direction of note number --> channel: you give it a note number; it gives you a voice number, or channel; and then when you tell [poly] that you want to release that note number, it will retrieve the channel number.

    The catch for your case seems to be "when a new note comes in to a particular channel" -- which sounds like the voice/channel assignment is happening independently of anything [poly] might do.

    Does this accurately describe the messaging?

    1. Note on, say, f# above middle C = channel 2, note 66, velocity > 0.
    2. New chord overwrites all the strings, including, say g above middle C.
      • g is on the high E string, so f# needs a note off: channel 2, note 66, vel = 0.
      • Then note 67 gets a note-on.

    Is that right?

    In that case, could you not simply use an array, indexed by channel number? You have a sequential index that's coming in from outside. Arrays operate based on sequential indices. That sounds simpler to me than using a bag.

    pd-guitar-strings.png

    midi-guitar-string-tracker.pd

    If I run the message boxes left to right, it prints:

    // for 66 64 2:
    0 0 2 (note-off: redundant but not harmful)
    66 64 2 (ok)
    
    // for 67 64 2:
    66 0 2 (note off for preceding pitch on this string, ok)
    67 64 2 (ok)
    
    // for 67 0 2:
    67 0 2 (ok)
    67 0 2 (redundant but not harmful)
    

    hjh

    posted in technical issues read more
  • ddw_music

    @xaverius said:

    @ddw_music Thank you for your post, but I'm looking for a solution that does not influence the zoom level of other applications.

    In Ubuntu, for Qt apps (which is most of them), the way to set zoom per app is to hack the desktop file:

    1. App menu.
    2. Right-click > Edit app.
    3. In "Command," add env QT_SCALE_FACTOR=2 before the command. (Or =1.5, or whatever.)

    The problem for your case is that I don't know which graphics toolkit Purr Data is using. If it's Qt, then QT_SCALE_FACTOR in a .desktop file should work. If that doesn't work, then you'd have to find out what is Purr Data's GUI toolkit and find out how to affect application zoom in that framework.

    QT_SCALE_FACTOR worked for pretty much every application I use, except Pd vanilla (Tcl/Tk), Audacity 3 (never figured out how to fix that, though Audacity 4 alpha builds are Qt-based and respond to QT_SCALE_FACTOR), and Wine (winecfg exposes a different zoom setting).

    hjh

    posted in technical issues read more
  • ddw_music

    I've long since lost the reference, but I learned a neat trick from a video once: if you need a circular buffer for a grain delay, use delwrite~ and delread4~.

    You can't get a smooth circular buffer by banging control messages into a line -- well, maybe you could, but it would be delicate. You might see other tutorials that suggest running a phasor~ at samplerate / arraysize Hz and multiplying the phasor by the array size, but floating point rounding error means you have no guarantee of touching every sample (and you still need a poke~ external that way, IIRC).

    But a delay line gives you the circular buffer for free. It isn't the first thing you'd think of but it is so much easier.

    Pitch shifting can be done by modulating the delay time. If you're playing a 100 ms grain, run a line~ with "100, 0 100" as the delread4~ delay time and you'll get 2x speed, 2x frequencies.

    hjh

    posted in technical issues read more
  • ddw_music

    @xaverius said:

    Is there a possibility to set a default zoom level for all windows that are opened? There is nothing in the preferences, but maybe a kind of config file entry?

    I've just been round the bend with app zoom levels (xubuntu with Ubuntu Studio packages). XFCE does have a global zoom setting but most apps ignore it :unamused: so I had to set environment variables. One was QT_SCALE_FACTOR; this worked for most of them. A bit of a saga, but worth it.

    I don't know which graphics toolkit Purr Data uses, so a Qt variable might not make a difference.

    At least my experience might comfort you that you haven't missed something obvious; it's rather that HiDPI support in Linux is not quite ripe yet.

    hjh

    posted in technical issues read more
  • ddw_music

    @porres said:

    how about using MPE in the sfz~ object? :)

    From the first message in this thread:

    There's an ongoing pull request on the sfizz .sfz player, to support MIDI Polyphonic Expression. ... So, if you're comfortable compiling software yourself, you can have fractional notes on a sample player. sfizz was a relatively painless build in Linux...

    I'm pretty sure you haven't built else/sfz~ based on the MPE-capable fork (and then it would take some time for that to trickle into PlugData).

    hjh

    posted in patch~ read more
  • ddw_music

    To round out the topic, then --

    The limitation in [sfz~] and [sfont~] is in the libraries on which they depend, both of which assume that they will only ever be used in MIDI-based plugins.

    So I had a specific requirement, and happily got notified that someone updated sfizz to make it possible to implement using MPE -- which I did, and wanted to share the outline of the technique.

    Then the suggestion that MPE might not be necessary if I had only looked deeper into ELSE and found sfz~.

    But MPE is necessary for this case, to associate note-offs with the right note-ons.

    (Well, it would be better if the sfizz developers had been more forward-thinking from the beginning and supported fractional note numbers -- which... it's right there in the VST3 header -- float tuning !! -- the idea that "we are only getting MIDI, so there are no fractions" is simply nonsense, hasn't been true for as long as VST has been around. But they didn't, so we have to rely on clunky workarounds. MPE is still IMO not exactly less clunky but at least possible to make it work properly.)

    hjh

    posted in patch~ read more
  • ddw_music

    @porres said:

    see else/width~ and else/spread~ for stereo spreading

    Is width~ a recent object? I can't find it.

    Anyway here's a working approach, which I think uses only vanilla objects. When the incoming pitch had been steady but then changes, it should choose a new random detuning distribution. The right-hand slider changes the detuning width continuously: values around 1.02-1.04 get that Serum-y effect.

    pd-unison.png

    unison-saw.pd
    unison-group.pd

    hjh

    posted in patch~ read more
  • ddw_music

    @porres said:

    I thought you wanted particular scales, like 8th tones

    My real interest in this is (cheap[1]) just intonation, where both the Pythagorean and the harmonic-series major third enter into it (separated by a syntonic comma). If you'll need to use the pure third in one context and the Pythagorean third in another, then you don't want to reconfigure the instrument's overall tuning midstream; you just want to apply a fractional offset to a note, and be done with it.

    There is one other problem, though: with fractional MIDI notes, you may need two or more voices playing different C naturals. Then, which voice should a note-off target? I predict confusion, for instruments like the else samplers, which truncate or round the note number. MPE disambiguates it by addressing channel numbers. So I'd guess that else/sfz~ would have trouble with the microtonal clusters in the little video (as, for that matter, did vanilla's [poly]). So the MPE way is more robust in this aspect.

    Come to think of it... why does else/sfz~ truncate note numbers? You're not bound by the limits of the MIDI protocol; why not fully support fractional note numbers? (I guess it's because of a dependency on a sfz library that is stuck in the 80s/90s.)

    hjh

    [1] There's a neat little paper in Music Theory Online (can't find the reference now) that gives one approach to generating 5-limit JI scales: starting on, say, F, go up by pure 5ths, except every fourth step, adjust the 5th downward by a syntonic comma. Assuming C as the root and applying octave corrections, you'd get F = 2/3 (4/3), C = 1, G = 3/2, D = 9/8, A = 27/16 * 80/81 = 5/3 (pure m6 vs C), E = 5/4, etc with the next comma correction at C#. Doing this on C gives you another variant, as do G and D, completing the set of distinct scales. (The 5th one would start on A and comma-correct on C# and F, which IIRC would replicate the F scale a comma higher, so the author didn't consider that to be distinct.) The right one of these 4 scales can be chosen for different harmony contexts.

    Thinking of microtones in terms of applying a single scale to the whole instrument excludes this type of approach. That is, I wasn't ignoring support for scales, but rather, I was deliberately not interested in that.

    posted in patch~ read more
Internal error.

Oops! Looks like something went wrong!