• zigmhount

    Thanks @oid for your reply. Actually I'm on Android, didn't have much success with Cabbage for Android and I believe I tried csound on Android, I can't really recall but it must have been disappointing because it's not installed anymore :smile: Anyway, MobMuPlat was pretty cool to also design the UI with PD's objects. That was a year ago though, today I want to use it in a DAW (on Linux).

    the first thing I would do as a test is break it down into the three component sounds, finger strike, tine, and resonator and compare them individually, would help in identifying the problem.

    I actually did this at the time, thanks for reminding me. I compared the signals between Pd and Csound in an analyzer for all members and combinations:

    • Strike:
      • aNoise
      • aTone
    • Tine
      • aFundamental (I had to add *~ 3000 to the signal, but could not figure out why...)
      • aBeat (also *~ 3000)
      • aHarmonic
      • aReso
      • aHi
    • Resonator: that's where it starts looking bad
      • aTone1 was not great
      • aTone2 was kinda ok
      • aTone3 was terrible

    As far as I can recall and understand from these year-old screenshots (I should do it again), anything but the tonal sound (300-400Hz) was pretty much inexistent, and the tweaks I tried to add some power below 300Hz and above 600Hz (added a shelf using [rzero~] and [rpole~]) lead to strong harmonics, which causes the high-frequency sounds I hear when playing the patch I shared above. My conclusion at the time was thus that:

    "Somehow the resonator's lpbutt and hpbutt let many high freq through in CSound compared to [butterworth3~] in Pd, don't know why, but I'll just continue with that for now:"

    I'll try to investigate the Resonator again.

    That said, your comment

    I have never had much luck converting csound stuff to pd even on fairly simple patches, never seem to sound as good and sometimes sound nothing alike despite my best efforts.

    tends to motivate me to work instead on the Cabbage plugin version of the csd patch and try to improve its performance (right now it uses a full core of my CPU regardless whether or not it plays notes). Csound forums, here I come!

    posted in technical issues read more
  • zigmhount

    Hi all,

    I found a Csound patch synthesizing sounds for a handpan on this page and it sounds great.
    I then wanted to interface it with my MIDI controller so that rather than learning Csound from scratch, I've embedded the CSD synth into a Pd patch using the [csound6~] external. Works great.
    The next steps however were to use such Pd patch on my smartphone via MobMuPlat, or in a DAW using plugdata (my attempt at generating a VST with Cabbage shows very poor CPU performance), however both are based on libpd and don't support [csound6~].

    So I've taken the approach of reproducing the csound patch in vanilla Pd, which was very instructive. I've followed the same variable, function names and logic, read a lot of documentation, and came up with the patch attached.
    handpan_comparison_csd_pd.zip

    However, the sound is not quite the same. There is a huge lot of high frequencies in the Pd version, which I couldn't quite eliminate even using up to 10 successive [lop~ 900] (I've left only one in the patch for now), but more generally I can't figure which differences between the csound patch and the patch may have this effect. Note that I am neither an expert in Csound or in filters - in particular I don't quite trust the Butterworth3 filter I hacked together - can I even realistically expect that the same algorithmic logic in Csound and Pd would produce the exact same sound?

    So I'm now asking here for help, in case some experts may find the time to review what I've done in Pd and compare it with the Csound patch, and may be able to suggest improvements. I know it is quite tedious work but I am hopeful :pray: Thanks in advance to anyone who gives it a try!

    (Note that this patch was embedded in a more complicated one including interfacing with the midi controller, definition of various scales, debugging/monitoring etc., which I've removed before sharing it here, but I may have forgotten some bits here and there, let me know if it complicates the review!)

    posted in technical issues read more
  • zigmhount

    I've progressed with libpd instead :D
    I know that this will probably make things more difficult with complicated patches and externals (I would have to compile libpd with support for these externals if I understand correctly), but for simple patches it seems pretty straightforward. I haven't tested it properly yet, but this small script exposes virtual ALSA-MIDI ports in a patchbay (which I can connect to anything) and just forwards notes and CCs to Pd, and receives Pd's notes and CC back and forwards it out to ALSA:

    import mido
    # install python-rtmidi from pip
    from functools import partial
    
    from pylibpd import *
    
    my_client_name='My Libpd Client'
    input_port_name='Input'
    output_port_name='Output'
    
    # Open Input and Output ports (Rtmidi)
    # Creating multiple virtual ports will create multiple clients with the same name.
    outport = mido.open_output(output_port_name,virtual=True,client_name=my_client_name)
    inport = mido.open_input(name=input_port_name,virtual=True,client_name=my_client_name)
    
    def pd_receive(*s):
        print('Printed by pd:', s)
    
    def midi_out(*s):
        match s[0]:
            case 'note':
                if s[3] > 0:
                    m=mido.Message('note_on', channel=s[1], note=s[2], velocity=s[3])
                else:
                    m=mido.Message('note_off', channel=s[1], note=s[2], velocity=s[3])
            case 'cc':
                m.mido.Message('control_change', channel=s[1], control=s[2], value=s[3])
        
        outport.send(m)
    
    # Callbacks to receive messages from Pd:
    libpd_set_print_callback(pd_receive)
    libpd_set_midibyte_callback(pd_receive)
    libpd_set_noteon_callback(partial(midi_out,'note'))
    libpd_set_controlchange_callback(partial(midi_out,'cc'))
    
    libpd_open_patch('midi_processing.pd','.')
    
    with inport:
        for msg in inport:
            match msg.type:
                case 'note_on':
                    libpd_noteon(msg.channel,msg.note,msg.velocity)
                case 'note_off':
                    libpd_noteon(msg.channel,msg.note,0)
                case 'control_change':
                    libpd_controlchange(msg.channel,msg.controller,msg.value)
                case 'sysex':
                    libpd_sysrealtime(1,msg.byte)
    
    libpd_release()
    

    And that's it! It's quick & dirty and it will get more complicated if I need to handle any type of MIDI messages, but notes and CCs should do for now.
    Looks like this in the patchbay:
    image.png

    posted in technical issues read more
  • zigmhount

    Thanks both,
    @oid I have indeed used scripts before, calling aconnect to list, extract the name of the client/port I need to connect to Pd, and connect it to Pd's inputs and outputs. However in the current case I'd like very much something more modular to connect in a patchbay.
    @whale-av thanks, I didn't know about the mediasettings external, this will probably useful in the future. Similar to @oid's suggestion though, this is also about connecting Pd with other MIDI clients from within the Pd patch.

    I'll continue exploring the libpd route, but I'll have to learn a bit more about Python's modules management to figure out how to properly install and use it.
    Or maybe open the patch in plugdata inside a plugin host...

    posted in technical issues read more
  • zigmhount

    Hi all,

    I've built 3 small Pd patches to fit in my workflow, e.g. to convert one MIDI controller's messages into OSC, one to light up the LEDs of another controller, one to control a pedal, etc.

    If I open 2 instances of Pd, each with its own MIDI (or audio, for that matter) inputs and outputs, Alsa-Midi refers to them as follows:

    $ aconnect -l
    client 128: 'Pure Data' [type=user,pid=260121]
        0 'Pure Data Midi-In 1'
        1 'Pure Data Midi-Out 1'
    client 129: 'Pure Data' [type=user,pid=260548]
        0 'Pure Data Midi-In 1'
        1 'Pure Data Midi-Out 1'
        2 'Pure Data Midi-Out 2'
    

    The client name is the same, and the client ID cannot be predicted - depending on which controllers I have plugged in and in which order, it might be any number incremented from 128. The tools that should remember and restore the patching (jack_patch, Ray Session and the like) struggle the same as I do to figure out which instance of Pd to connect to which controllers.
    I want to keep these patches in separate PD instances so I can mix and match, and not necessarily have all of them open, and always rely on the specific instance's MIDI input number and output number.

    So, here's my question: any idea if it is possible to rename Pd's alsa-midi ports? I found surprisingly little online about this issue, yet I can't really believe that nobody met this issue before...

    I've never seen the command line option -jackname to actually have an effect, but in any case Pd does not use Jack MIDI as far as I understand.
    I've been thinking about running the patch in a libpd wrapper instead, e.g. from a Python script in which I could specify the client's and port names, but I'm struggling a bit to install pylibpd.

    posted in technical issues read more
  • zigmhount

    Thanks for your replies!
    My conclusion is indeed that I don't really need to worry about efficiency in this specific case :)

    @seb-harmonik.ar said:

    imo the best way would be to use an [array] for the button in each [clone].

    Oh interesting, I'll keep that in mind for the next time where I'll have to worry about Pd's performance. I'd have to see if I can handle it all with only numbers (while [text] leaves the possibility to also store and query symbols)..

    why would the order change? why couldn't you just write each button's info to the corresponding line number?

    The mapping between buttons and sequences may change, e.g. if I shift the buttons to cover the sequences in columns 2 to 9 rather than 1 to 8.

    @oid Ecasound is a good suggestion, I looked into it a couple of years ago when I wanted to make music on a 700Mhz single-core Celeron. For the current project I need a graphical LV2 plugin host where I can easily map controller knobs to plugin parameters, side-chain audio outputs and easily insert new busses with synths or soundfonts, i.e. basically the mixer part of a DAW (I'm on Ardour for now, Qtractor could work also) without its timeline, or Non-Mixer if it supported LV2 plugins with their UI. I might consider something modular eventually (e.g. with Carla).

    isolate a core and run the DAW on that core, can keep the random dropouts from happening

    Also a good idea, I may give it a try. Although so far I've noticed dropouts when enabling synths in Ardour, Pd's calculations are probably negligible in comparison.

    @ddw_music Thanks for the benchmark and the explanations, very interesting. Sticking to numbers in arrays rather than text is definitely something to keep in mind for future projects on smaller machines.

    posted in technical issues read more
  • zigmhount

    Hi @oid, thanks for the suggestion (I usually do split audio and control in different patches) but I forgot to mention that I am not processing any audio at all in this patch :)
    And, also, that this patch is only used to interface my MIDI controller with OSC, so real-time performance is actually not really a big deal. So maybe the performance difference between my option will not be noticeable at all.

    For reference, the machine I'm running this on has 2 cores at 2GHz, but I'm also running a DAW in parallel to Pd. My previous project was a complete MIDI sequencer, with 24 ticks per beat and relatively fancy UI (moving canvas for sequences progression bars etc.) which lead to relatively heavy load (and low performance), which is why I looked into the [text] topic that I'm asking about here. But I guess that the performance was impacted much more by the continuous UI updates than by the text search :sweat_smile:

    posted in technical issues read more
  • zigmhount

    Hey all,

    I am integrating a Pd patch with an existing sequencer/looper (Seq192) with an OSC interface, where my patch should convert my MIDI controller's button presses to OSC commands and send back MIDI signal out to lighten the controller's LEDs.
    I can retrieve periodically the status and details of all clips/sequences and aggregate it into a list of parameters for each sequence. The LED colors and the actions that the next button press will trigger depend on these parameters, so I need to store them for reuse, which I like doing with [text] objects. I am then handling buttons' light status in a [clone 90] (where each instance of the clone handles one button).

    This should be running on a fairly low-end laptop, so I'm wondering which of these approaches is the most CPU-efficient - if there is any significant difference at all - and I couldn't come up with a way to properly measure the performance difference:

    1. one [text define $1-seq-status] object in each clone, with one single line in each. I compare the new sequence status input with [text get $1-seq-status 0] so that I update only line 0 with [text set $1-seq-status] when I know that the content has changed.
    2. one single [text define all-seq-status] object with 91 lines. I compare the new sequence status with
    [ <button number> (
    |
    [text search all-seq-status 0]
    |
    [sel -1]
           |
           [text get all-seq-status]
    

    and if it has changed, I update a button's line with

    [ <new status content> (
    |
    |      [ <line number> (
    |      |
    [text set all-seq-status]
    

    The order in which buttons/sequence statuses are listed in the file might change, so I can't really avoid searching at least once per button to update.

    1. Should I instead uses simple lists in each clone instance? As far as I could test, getting a value from a list was a bit slower than getting a value from a text, but searching in a text was much slower than both. But I don't know the impact of processing 91 lists or text at the same time...

    TL;DR: Is it more efficient to [text search], [text get] and [text set] 91 times in one [text] object, or to [text get] and [text set] 1 time in each of 91 [text] objects? or in 91 [list] objects?

    Since you've gone through this long post and I gave a lot of context, I am of course very open to suggestions to do this in a completely different way :D
    Thanks!

    posted in technical issues read more
  • zigmhount

    I also noticed here..... http://www.music.mcgill.ca/~ich/classes/mumt306/StandardMIDIfileformat.html

    Great resource, thanks! that's going to be useful in future (hopefully I will finish my project before MIDI 2.0 is widely available :sweat_smile: )

    @ddw_music's video will definitely also be useful, in fact I'm working on a patch I want to run with plugdata in Ardour, so it's spot on, thank you very much!

    posted in technical issues read more
  • zigmhount

    @whale-av Oh that's great, thanks for the [makefile] trick!
    I've tried again to send the message as a list and importing it in different notation tools - both Musescore and lilypond do assume the default 120bpm when I do not send any tempo message (not sure how I got the 190bpm yesterday, I can't reproduce...), and all bars disappear when I inject the tempo message (either as a list or as a stream, which in both cases just add a line 0 255; in [seq] ). Might be a limitation of [seq] itself.

    To make that clear, I've updated the default-tempo midi file in Musescore itself to edit the tempo to 89bpm, exported it as midi (reopened it in Lilypond to confirm the tempo is properly interpreted again), and then sent it via [read file_musescore.mid( to [seq] and again [write file.mid( ... Turns out that the tempo information is lost in the transition through [seq], so this probably just means that tempo changes are not supported by [cyclone/seq] ! too bad.

    Looking for alternatives, I've found that [mrpeach/midifile] interestingly uses midi ticks.
    But I will follow the sequencing tutorial of ELSE instead, and hopefully [else/midi] also supports midi tempo.

    posted in technical issues read more
  • zigmhount

    Thanks @whale-av, I actually did that on purpose since I understand that [seq] expects a stream of real-time midi bytes from [midiin].
    But thanks for noticing the mistakes in the numbers, I'll try again with the correct ones!

    Edit: ah but wait no, I did that because 51 in hexadecimal equals 81 in decimal (just like FF->255) is it wrong?

    posted in technical issues read more
  • zigmhount

    So, I eventually got to this page and finally to this one, which describes the message FF 51 03 tt tt tt as a tempo change in the MIDI signal.
    Long story short, I built a small patch to convert 120BPM into [255, 81, 3, 7, 161, 32( , which I store in [text define midi_tempo] and then send its content to [cyclone/seq] right after [record( .

    Unfortunately I still get no tempo when importing the MIDI file to Musescore, and I can see only the first 255 byte in the seq editor (the 2nd line is when I started recording notes after ~6.9 seconds).

    image.png

    I'm open to any ideas! :)

    posted in technical issues read more
  • zigmhount

    Hi everyone,
    I've started using [cyclone/seq] for MIDI sequencing, with the intention of writing and reading saved .mid files, and also importing the .mid files into a notation editor like Lilypond or Musescore. However I realize when opening this file that I don't ever store any tempo information into [cyclone/seq], so that even though I recorded it at 120bpm, Musescore interprets the notes and rythm at 190bpm (I can make notation examples if this isn't clear :sweat_smile:).
    As far as I understood from a quick read through this and this, I could either:

    • send the midi clocks i.e. [248( 24 times per beat into [cyclone/seq] while recording. However this seems to show one single bar without tempo in Musescore. Maybe I need to send also start [250( or continue [251( and stop [252( into [seq]?
    • instead of "clock", use "ticks" every 10ms that the sequencer can somehow also use to play the midi file at a different tempo than recorded. However I couldn't figure out if ticks are indeed a different value in realtime midi, what I found was 0xF8 = 248, i.e. same as "clocks"...

    Do I understand this right? What would be the proper/preferred way to store beat information in [cyclone/seq] and eventually in the midi files?

    Thanks for your help!
    Zig

    posted in technical issues read more
  • zigmhount

    @oid Thanks for your answer.
    I followed your advice and spent quite some time learning a bit how Csound works - a bit overwhelming indeed.

    Then I realized that I'd love to be able to play this synth when connecting my MIDI controller to my smartphone, so I tried to run a slightly modified version of the CSD file on Csound for Android, but I didn't manage to get anything but crashes. Then I tried with csound6~ as a Pd external on PdDroidParty and MobMuPlat, but externals of course don't work on these. Then I considered running Linux on old smartphones and tablets, but I couldn't properly access the soundcard without root access. Then I fell back and looked for handpan soundfonts, but I could find only 1 or 2 (for free) in a single scale.

    Then I finally came back to Pure Data and realized that my best chance was to convert this Csound patch into Pd vanilla and run it on MobMuPlat.
    Which I've done! yay!
    I've had quite a few challenges and the sound isn't perfect yet, but it works, and I'm also adjusting a few things to my preferences. I'm also including the r_cycle functionalities to tune the Launchpad and it's a lot of fun! I might be able to spend my Sunday sitting in the grass and playing handpan on my MIDI controller connected to my smartphone :grinning:
    I will push it to Github eventually, right now it's still a little too messy for sharing, but if anyone passing by is interested, just let me know!
    Cheers!

    posted in technical issues read more
  • zigmhount

    Hi everyone,

    I've been playing a bit with a Csound handpan synth from here and I've used the r_cycle external to make a handpan with my Launchpad :D (the patch is not very complicated so I didn't try to make it neat, sorry!).
    The csound patch maps specific MIDI notes to the synth according to a scale, and the Pd patch lights up the pads so each colored group of pads sends the corresponding MIDI note to CSound:
    image.png

    I'm super happy with it, but the next step is to make it configurable: the csound patch contains many different scales defined with giNotes = ftgen(0, 0, -<number of notes>, -2, <list of note numbers> ) :

    ; B Kurd
    ;giNotes = ftgen(0, 0, -10, -2, 47, 54, 55, 57, 59, 61, 62, 64, 66, 69)
    
    ; B Golden Arcadia
    giNotes = ftgen(0, 0, -9, -2, 47, 51, 54, 58, 59, 61, 65, 66, 68)
    

    So instead of editing the CSD file and changing the color mapping of the launchpad when I want to change scale, I would like to send a list/array/table of note numbers from Pd to [csound6~] via a message (and adjust the MIDI notes sent by the launchpad at the same time).
    I know basically nothing about Csound, so I've read up and found examples using chnget/chnset or invalue which seem relatively straightforward to change the value of a single int variable. However I don't really have any idea on how to pass the list/array of scale notes using chnset or invalue, or if there is a better way to do it.

    Maybe someone around here has an idea? Thanks for reading so far in any case :)

    posted in technical issues read more
  • zigmhount

    @whale-av Hmm thanks, this sounds like I might actually be better off with running parallel pd processes right from a shell script and communicating via netsend (this laptop has only 2 cores, so audio processing is all running in the same patch anyway, and controls+GUI in the other), rather than trying hard to get it working with [pd~] and possibly hit some more undocumented limitations later on...
    I guess I'll try a bit more, and let's see where I end up :)

    posted in technical issues read more
  • zigmhount

    I think I finally figured it out!
    Well, not sure it solves all my problems, but at least here is a limitation of pd~: midi inputs and outputs (obviously)! There were a few of those in one of the libraries called by mybigpatch.pd (r_cycle).

    So here is my way around it, in case it might help others:

    • In the main patch, capture notein and send [notein $1 $2 $3( to [pd~]:
      image.png
    • in the pd~ subprocess, capture the incoming in an abstraction ./myfolder/notein.pd:
      image.png
      and replace all instances of [notein] with [./myfolder/notein]
    • similarly for noteout, an abstraction ./myfolder/noteout.pd to use in the pd~ subprocess:
      image.png
      and replace all instances of [noteout] with [./myfolder/noteout]
    • in the main patch, replace noteout with [r noteout]:
      image.png
    • and the same thing with ctlin, ctlout, bendin, bendout, midiin, midiout, etc.

    And the command line magic trick (that it took me a while to figure out) in order to replace all this easily in all pd files:
    $ sed -i 's+\(touchin\|polytouchin\|bendin\|notein\|ctlin\|midiin\|midiout\|noteout\|ctlout\)+'./myfolder/'&+' *.pd, which will prepend any instance of notein & co with ./myfolder/.

    Improvement suggestions are of course welcome.
    Cheers!

    posted in technical issues read more
  • zigmhount

    @mtdr2012 Hey, my bug report seems to still be open: https://git.purrdata.net/jwilkes/purr-data/-/issues/672 but maybe you can help solving it over there :)
    I've moved from Purr-data to vanilla a couple of months after that issue so I can't really help anymore, sorry ! I'm focusing less on the UI now, so whenever I need a control for a knob I just use a centered hslider instead.

    posted in technical issues read more
  • zigmhount

    Hello,

    I've been working for a few months on a patch that got more complicated than I had planned, and I finally decided to split it into subprocesses to avoid audio clicks when UI elements refresh. In the past I've done it with 2 separate pd processed and osc calls between both, but I'd like to try with [pd~] instead (which I've read about bit never actually used for real).
    So as a first step I'm just trying to load my whole complicated patch inside a [pd~] subprocess (with DSP on in both of course) using a simple

    [pd~ start mybigpatch.pd(
    |
    [pd~ -ninsig 8 -noutsig 2]
    

    It works with a simple patch in the same folder, however with the big one, the subprocess starts, loads the libraries, and immediately closes, showing only pd~: No such file or directory in the parent process' Pd console window.
    I get no more details when adding -d 4, -stderr, -verbose or even -noloadbang (in case it's the patch initiation that failed).
    My "big patch" will call many abstractions, initiate arrays, soundfilers and load text files into [text] objects so there are quite a few possibilities for not finding files - however the exact same patch starts perfectly from the same folder. Using the whole path to the patch in [pd~ ( also doesn't help.

    Are there any known limitations to what [pd~] can or cannot do compared to the main parent pd process?

    Thanks!

    posted in technical issues read more
  • zigmhount

    Also, I'm curious about the buttons you'll choose for this!
    I can't mold silicon buttons or 3d-print the enclosure myself, so I may have to go for bulky arcade buttons, not sure yet.

    posted in I/O hardware diyread more
Internal error.

Oops! Looks like something went wrong!