I've noticed that pd-vanilla and purr-data currently handle pointers differently, making the is.pd object work in pd-vanilla, but not purr-data. I've created another pd-native variation of the external [is] that works in both purr-data and pd-vanilla.
While the route object in pd-vanilla can detect pointers as a type of symbol, in purr-data, a pointer is detected as a list by the symbol object, but not detected as a list by the route object. So if an atom passing through [route list] gets sent to the rejection outlet, then later reveals to have a string value of "list" when sent to a [symbol] object, we can assume that that atom is a pointer.
You can also debug with VSCode, which is something I recently just tried out for the first time. You'd need to install the VSCode extensions C/C++ and Native Debug, then you'd build pd-vanilla based on the instructions given on pd's website and the INSTALL.txt file, and using the debug build option.
Then open the pure-data folder in VSCode, either by navigating to that folder in a terminal and typing
code .or by right-clicking the folder and selecting "Open With->VSCode"
Then in VSCode, you switch to the debug pane and from a drop-down at the top of the pane, click "Add Configuration" and after selecting a config preset, a file called launch.json will pop up. You'll need to specify where the binary is. In my case, the location was:
Then lastly, and maybe there's a better way of doing this last part but the quick and easy way is just to inject your external into some part of pd's code, change the setup function so that it's static, and make sure the setup function gets called at some point, usually the setup calls are kept inside of one function at the very end of a .c file. Then it's just a matter of adding your breakpoints and re-building pd when changes are made.
Works very much the same as trigger except that when you use 'a' for creation arguments, lists that consist of only 1 atom will output as the atom itself and not as a list. This also includes pointers. You'll notice that with trigger, a pointer will output as a list, which makes it difficult to type-check.
The [unpak] object had a similar problem where it would output lists instead of raw atom values, and that has also been changed with unpak's latest update.
EDIT: I've made it so that floats default to anything outlets and no creation args defaults to 2 anything outlets.
Similar to [pack ] and [unpack ] but instead of the default atom type being a float, the default type is anything.
Also comes with a new creation arg 'a' for anything. Numbers and unrecognized types will also default to anything.
For strict type checking, use f(loat), s(ymbol), and p(ointer).
The pak object will also store unrecognized types as symbols and interpret 'b' as a 'bang' symbol.
EDIT: You can now send lists to any of the additional pak inlets and it will store the list values with the inlet as the starting point. For example, sending the message [foo bar 3 baz( to the 2nd inlet of [pak 1 2 3 4] would result in the list: 1 foo bar 3
EDIT2: pak still has a bug when setting a pointer from any inlet other than the first one. This became more apparent when trying to work with the object in pd-vanilla.
EDIT3: pak is much more stable now and works in pd-vanilla. The example includes a new object I've created called [x ], which is a kind of trigger object that dehusks lists that consist of only 1 element when you use 'a' for creation args. For pd-vanilla users, the example also requires zexy's [demux].
EDIT4: You can now use "." to skip inlets and assign only specific ones.
For example, sending the message [. . 123( would assign 123 to the 3rd inlet and leave the first two values the same as they were. You can also send these messages to any inlet.
EDIT5: I made it so that the skip arg feature only applies to list and anything type messages, so if you really want to, you can still assign a single period to an inlet by sending it as a symbol. I also needed to make it so the skip arg feature also works on strict-type inlets.
It's also worth mentioning that the proxy inlets now have their own pointer, float, symbol, and list functions, rather than an anything function that handles all message types by running string comparisons, so it should be a bit faster if only slightly, and overall better coding practice for a pure data external.
EDIT6: The skip arg feature was only added to pak so I added the same feature to unpak as well. Using a period as an arg will skip the outlet and not send any value through it.
EDIT7: fixed one more bug in pak, where the first inlet could only receive raw pointers and every other inlet could only receive pointers in lists. I also changed unpak so that when a stored symbol is "bang", it will output as an actual bang message rather than as a bang symbol.
Added two more objects [@pak] / [@unpak] - reverse pak/unpak. Outputs the lists in reverse order.
EDIT8: I've changed the pack related objects so that strict-type error messages are muted by default and to unmute them, you need to send [mute 0( to the 1st inlet. The number sent to mute acts as a bit mask for the inlets, so when a bit is turned on, the associated inlet will not output error messages.
Returns a boolean 1 or 0 if the value sent to the 1st inlet matches the type specified in the creation arg or 2nd inlet.
You can set the type to be matched against either as a creation argument or with the message [type $1(, or by sending to the 2nd inlet either the type name or an example of the atom whose type you want matched against.
EDIT: the "type" method needed a bug fix. The help file has also been slightly altered:
EDIT2: Now the type can be virtually anything, rather than one of 5 possible types. This allows us to check for specific messages. For example, sending the message [foo 1 2 3( to the object [is foo] will return true.
EDIT3: Defaults to float if no creation argument given. Also added a pointer example to the help file.
EDIT4: A memory-freeing method was necessary to clear the proxy inlet.
Allows you to play various game music formats, including:
AY - ZX Spectrum/Amstrad CPC
GBS - Nintendo Game Boy
GYM - Sega Genesis/Mega Drive
HES - NEC TurboGrafx-16/PC Engine
KSS - MSX Home Computer/other Z80 systems (doesn't support FM sound)
NSF/NSFE - Nintendo NES/Famicom (with VRC 6, Namco 106, and FME-7 sound)
SAP - Atari systems using POKEY sound chip
SPC - Super Nintendo/Super Famicom
VGM/VGZ - Sega Master System/Mark III, Sega Genesis/Mega Drive,BBC Micro
The externals use the game-music-emu library, which can be found here: https://bitbucket.org/mpyne/game-music-emu/wiki/Home
[gme~] has 2 outlets for left and right audio channels, while [gmes~] is a multi-channel variant that has 16 outlets for 8 voices, times 2 for stereo.
[gmes~] only works for certain emulator types that have implemented a special class called Classic_Emu. These types include AY, GBS, HES, KSS, NSF, SAP, and sometimes VGM. You can try loading other formats into [gmes~] but most likely all you'll get is a very sped-up version of the song and the voices will not be separated into their individual channels. Under Linux, [gmes~] doesn't appear to work even for those file types.
Luckily, there's a workaround which involves creating multiple instances of [gme~] and dedicating each one to a specific voice/channel. I've included an example of how that works in the zip file.
- [ info ( - Post game and song info, and track number in the case of multi-track formats
- this currently does not include .rsn files, though I plan to make that possible in the future. Since .rsn is essentially a .rar file, you'll need to first extract the .spc's and open them individually.
- [ path ( - Post the file's full path
- [ read $1 ( - Reads the path of a file
- To get gme~ to play music, start by reading in a file, then send either a bang or a number indicating a specific track.
- [ goto $1 ( - Seeks to a time in the track in milliseconds
- Still very buggy. It works well for some formats and not so well for others. My guess is it has something to do with emulators that implement Classic_Emu.
- [ tempo $1 ( - Sets the tempo
- 0.5 is half speed, while 2 is double. It caps at 4, though I might eventually remove or increase the cap if it's safe to do so.
- [ track $1 ( - Sets the track without playing it
- sending a float to gme~ will cause that track number to start playing if a file has been loaded.
- [ mute $1 ... ( - Mutes the channels specified.
- can be either one value or a list of values.
- [ solo $1 ... ( - Mutes all channels except the ones specified.
- it toggles between solo and unmute-all if it's the same channel(s) being solo'd.
- [ mask ($1) ( - Sets the muting mask directly, or posts its current state if no argument is given.
- muting actually works by reading an integer and interpreting each bit as an on/off switch for a channel.
- -1 mutes all channels, 0 unmutes all channels, 5 would mute the 1st and 3nd channels since 5 in binary is 0101.
- [ stop ( - Stops playback.
- start playback again by sending a bang or "play" message, or a float value
- [ play | bang ( - Starts playback or pauses/unpauses when playback has already started, or restarts playback if it has been stopped.
- "play" is just an alias for bang in the event that it needs to be interpreted as its own unique message.
Both externals optionally take a list of numbers, indicating the channels that should be played, while the rest get muted. If there are no creation arguments, all channels will play normally.
Note: included in the zip are libgme.* files. These files, depending on which OS you're running, might need to accompany the externals. In the case of Windows, libgme.dll almost definitely needs to accompany gme(s)~.dll
Also, gme can read m3u's, but not directly. When you read a file like .nsf, gme will look for a file that has the exact same name but with the extension m3u, then use that file to determine track names and in which order the tracks are to be played.
- [ info ( - Post game and song info, and track number in the case of multi-track formats
This is a spiritual successor to amPd. It's much more stable and has many more features than amPd:
reads/writes metadata to/from patches. This is done by storing the info in the form of comments, in a canvas called [pd meta] or [pd info]. If no such canvas exists, foobar will add it in the top left-hand corner of your patch.
comes with a Win32 Dialog UI element, containing sliders, toggles, buttons, and edit-text/button combos to send messages to your patch.
- foobar looks in your patch for a canvas called [pd mix] and uses the parameters of whatever sliders, bang objects, or toggles it finds there to give your UI controls similar functionality.
How playback works
- libpd sends a 1 to vol, then a bang to play.
- Generally this is where, in your patch, you have an [r play] hooked up to your metro, and an [r vol] connected to a [*~ ] before your output reaches [dac~].
- the length of the song is arbitrarily set by the user.
- This affects the trackbar's cursor visibility and ability to set a position.
- If the length is 0, there will be no trackbar cursor. Otherwise, the cursor, when moved, will send its position in seconds to pos. From there, it's up to your patch to take that information from [r pos] and work it into song events.
- The patch will not actually stop and move on to the next track until libpd receives a bang from [s stop].
How the mixer works
- all mixer controls go inside of [pd mix]
- horizontal and vertical sliders are turned into slider controls in the UI element
- labels assigned to sliders in the patch become labels for the UI element's slider controls. The same applies for send symbols.
- min and max values of sliders on the UI element work in integers only, so if you want a gradual shift from, say, 0 to 1, write "gradient" in the slider's receive symbol, and the slider's range will be broken down into roughly 200+ individual steps.
- there are currently 7 sliders in the UI element
- bang objects with no label become simple buttons in the UI
- their send symbols will be reflected in the button's name and they will send a bang when clicked.
- there are currently 3 buttons
- bang objects with a label assigned become message buttons
- these have an edit text field associated with them, where you can type out any message you want and send it to the destination.
- the bang's label is placed inside of the edit text field as a suggested message to send.
- pure data strips commas out of labels, so I'm using apostrophes to denote where commas should go. ex: do this' then this
- there are currently 2 message buttons, with the 'any' button being a potential 3rd.
- a bang object with a label written in the format dest : msg will be assigned to the 'any' button.
- the 'any' button has an editable destination field, giving you access to basically any receive symbol in your patch.
- also substitutes as a third normal message button, when the other two are already in use
- toggles become checkboxes
- each checkbox can have a label and send symbol assigned to it
- there are currently 4 checkboxes
- right-clicking a track shows the context menu entry Pd Player -> Load mixer.
- basically, you can load mixers of tracks not currently playing for some potentially interesting exchanges between patches. After loading the mixer, you still need to hit the Refresh button to show the changes.
foo_pd's copy of libpd.dll contains only the externals that I needed to run the example patches. If you want your own patches to work with foo_pd, you might need to make another build using MSYS2. If you're not sure which objects aren't instantiating, foobar's console prints all of pd's messages while audio is being processed. I'll also add more externals over time.
I'll be maintaining foo_pd at https://github.com/myQwil/foo_pd where you can also find the latest builds