Cabbage Logo
Back to Cabbage Site

Invalid characters for soundfiler

Hi Rory,
it seems like our beloved Soundfiler doesn’t accept some special characters in the filename that GEN01 seems to accept though.

Is it restricted to UTF-8?

Would it be possible to extend the charset for it to latin-1?

That would be great, otherwise user would have to rename their samples if they’re not compatible.

If not, do you know of an elegant easy way in CSound to check if a string is utf-8 compatible? (other than to check each character manually comparing it to every possible character in the charset :exploding_head:)

I’m seeing now that the non UTF-8 chars are also not displayed right when printed in the Output Console. Which makes it somewhat mysterious that GEN01 is still able to load the file from that string. Is this more of a overall issue and not just concerning the Soundfiler?

Cabbage only supports UTF-8 at the moment. I can look into supporting other encoding, but it might be quite a bit of work :grimacing: One thing you could do, albeit a workaround, is to load the sound samples into tables for the soundfiler to show? If their locations and names are fixed that might work? But as soon as you try to load something you will hit the same problem again…

Thanks for your message Rory!
I’m also displaying the names of the samples which look messed up. Plus changing the way I load the soundfilers would be quite some work for all the different routines from state data, from importing through file drop, text drop (vst XML) e.c.t or from loading presets as I wasn’t able to put them all in one opcode/instrument. I guess the cSound string variables have the higher format, but any Cabbage routine would always downsize it. So trying to save the original file paths in the state data with the cabbage opcodes probably won’t work anyway. So I guess I’ll stick with this and I’ll have to code a is-string-utf-8 query to restrict to it.
Let me know when or if at some point you get inspired to change this.

1 Like

Found a few characters that are within UTF-8 but don’t work in the soundfiler:
()"§/´,;°
So that minus all the äöüéÛŒ ect. I’ll allow:

!$%&=?`*+’#-_.<>^@

apart from obviously

qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM1234567890

Don’t know if that’s interesting.

It’s good know what’s allowed at least. Thanks for letting us know. :+1:

Well just in case someone’s interested in how to validate a string with a filename for a soundfiler:

opcode CheckFileName ,k,S
    Sallowed    init "!$%&=?`*+'#-_.<>@qwertzuiopasdfghjklyxcvbnmQWERTZUIOPASDFGHJKLYXCVBNM1234567890"
    iallowedLen strlen Sallowed
    Sin     xin
    klen    strlenk Sin
    
    
    kcnt2    init 0
    while kcnt2 < klen do
        kcmp1   = strcmpk:k(strsubk:S(Sin,kcnt2,kcnt2+1)," ")
        if(kcmp1 != 0)then
            kcheck = 1
            goto break
        endif
        kcnt2 += 1
    od
    kcheck = -1
    goto finish
    break:
    
    
    kcnt    = 0
    kcheck  = 0
    while kcnt < klen do
        kchars  = 0
        kcheckChar = 0
        while kchars < iallowedLen do
            kcmp    = strcmpk:k(strsubk:S(Sin,kcnt,kcnt+1),strsubk:S(Sallowed,kchars,kchars+1))
            if(kcmp == 0)then
                kcheckChar = 1
            endif
            kchars += 1
        od
        if(kcheckChar == 0)then
            kcheck = -1
        endif
        kcnt += 1
    od
    if(kcheck == 0)then
        kcheck = 1
    endif
    finish:
    xout    kcheck
endop

There’s also a check if the string exists exclusively out of white spaces, because I’m also checking preset names from a texteditor with it.
I get the file name from the full path with this little one:

opcode NameFromPath ,S,S
    SPath       xin
    kStart      strrindexk SPath,"/"
    kLength     strlenk SPath
    SName       strsubk SPath,kStart+1,kLength
    xout        SName
endop
1 Like

Hi Rory,
I noticed that I’m coming across some problems with one particular character restriction for the soundfiler which are the parenthesis. It’s quite likely to come across them in names of sound files, e.g. Studio One puts them into file names that already exist, like when you consolidate a file: sound.wav becomes sound(2).wav. So then I can’t drag that file into my plugin because the soundfiler can’t read it. That’s quite a workflow killer for the user of my plugin as the exchange of sounds between DAW and plugin will be vital and even renaming in the arranger won’t do the job. They would have to look those sounds up in the “pool” to rename the actual file rather than just the region in the arranger.
Could you maybe look into why the soundfiler doesn’t accept parenthesis (Among other UFT-8 encodable plus filename valid characters)? As they are actually encodable in UTF-8, I wonder if there could be a way to have them be accepted in the soundfiler.

I just looked up the UTF-8 charset because I honestly don’t know much about it. I was surprised to see that all those Latin characters are part of it.
https://www.utf8-chartable.de/

I will look into this. Brackets should be allowed :thinking:

[edit], I just checked here. Here are my observations:

  • I am able to load files with ()s into the soundfiler from a filebutton without any issues
  • I can call cabbageSet with files that contain brackets, i.e,
    cabbageSet "soundfiler1", "file(\"/Users/rwalsh/Csoundfiles/1_3_6_8_9_\(1\).wav\")"
  • Trying to load files with ()s in the Cabbage section of code won’t work because of the way the Cabbage preprocessor parser works.

Shoot! This is embarrassing!! My code was just sluggish.
I simply forgot to put the path within the file identifier into brackets.
Quite amazing that it worked at all with most files!

Also, I just was able to parse a path to the soundfiler with an “ö” in it. I have to insert that character as ascii (%c) though. Not sure if the issue is on the cSound or the Cabbage side. I wonder if there’s any chance that I can convert a string that is supposed to contain a Latin char from what ever I get from a filebutton or “LAST_FILE_DROPPED” to the proper encoding.

You mean that Cabbage converts from “ö” to %c?

When I drop e.g. an “ö” and print is it get’s displayed as “oÌ”. I analysed what asciis actually get parsed. Most Latin characters are converted into 3 chars e.g. “ö” is 111,204,136 while it’s actual ascii code would be 246. It looks like the first one is the “root” character like in this case the “o”, the second one seems to be a flag for special or Latin character as it is always (with a few exceptions) 204 ( “Ì” ) and the third determines what kind, e.g. “à” and è" both have a 128 as the third char. That third one doesn’t get displayed in the print. They range from 128 to 138 which are quite special characters in the ascii roam like “” or “‡”, so that’s maybe why they stay invisible in the print. Some of them are different and only have 2 chars. Seems like those are considered unique not having a common “root” character as opposed to “äàáâ…” e.g. all are derivations of “a”. Those unique characters are flagged with a 195 and then a determining char from that’s in between 134 and 190. I’ve made a table with all 63 Latin characters and how they get parsed to Cabbage/cSound. So they all are unique 3 or 2 char codes. Interestingly it’s different if I assign them in the orchestra or if they are parsed through a filename. So I had to create 63 files named with the Latin chars to actually see what’s happening. It’ll be some work but with the information I gathered I can write a converter. It’s worth it because once you have the right string, the soundfiler actually can read it no matter what exotic characters are in it. Once it’s working I can post it here…

I used this to analyse the behavior:

opcode NameXFromPath ,S,S
    SPath       xin
    kStart      strrindexk SPath,"/"
    kEnd        strindexk   SPath,"."
    SName       strsubk SPath,kStart+1,kEnd
    xout        SName
endop

instr 1
    kchars[] init 4
    SDrop  cabbageGet "LAST_FILE_DROPPED"
    if(changed(SDrop)==1)then
        SName   NameXFromPath SDrop
        printks "\n SFile:%s : ",1,SName
        kNameLen    strlenk SName
        kcnt        = 0
        while kcnt <= kNameLen do
            kchars[kcnt]    strchark strsubk(SName,kcnt,kcnt+1)
            kcnt += 1
        od
        printarray kchars
    endif
endin

So you think you will find a Csound based solution?

I think so. It’s going to be quite some work for all 63 Latins. But as all the characters are uniquely parsed I can read them (strchar) and then replace those 3 or 2 char bits with the actual ascii (sprintfk “%c”,kascii). I’ll try to load the converting data into a multi array from an XML or so.
I’m busy with other stuff right now but I’ll post it here once I have it working…

1 Like

It was less painful than I though. Works perfectly. Anyone who’s interested in using this, download the text file. It contains a conversion chart that you need if you want to convert strings that have been imported as files with a fileButton or the “LAST_FILE_DROPPED” channel. Latin letters are flagged with an ASCII code of 204 that is surrounded by 2 other ones that determine the actual letter. Unfortunately there’s no system to how they are composed. Interestingly these quite complex encoded letters have a different encoding than strings with Latin letters that have been assigned in the orchestra or parsed through a texteditor widget e.t.c. Those have a simpler encoding for which you don’t need the chart. You can see in the ConvertLatins opcode that those are flagged with an ASCII code of 195 and just a second one that determines the Character which is simply just their actual ASCII code minus 64. So if you only want to display Latin letters you don’t need the text file with the conversion chart and you can delete the first part in the converting opcode and the instrument (“load Array”) that loads the text file data into some arrays.
I’m glad I’m not restricted to only English letters:ezgif-2-44f847c971
LatinConversionChart.txt (609 Bytes)
LatinCharConversion.csd (4.1 KB)

opcode ExtFromPath ,S,S
    SPath       xin
    kStart      strrindexk SPath,"."
    kLength     strlenk SPath
    SName       strsubk SPath,kStart,kLength
    xout        SName
endop

opcode NameXFromPath ,S,S
    SPath       xin
    kStart      strrindexk SPath,"/"
    kEnd        strindexk   SPath,"."
    ;kLength     strlenk SPath
    SName       strsubk SPath,kStart+1,kEnd
    xout        SName
endop

opcode DirFromPath ,S,S
    SPath       xin
    kStart      strrindexk SPath,"/"
    SName       strsubk SPath,0,kStart+1
    xout        SName
endop

gkLatinBase[]    init 53
gkLatinType[]    init 53
gkLatinAscii[]   init 53


opcode ConvertLatins ,S,S
    Sinput      xin
    SDir        DirFromPath     Sinput
    SExt        ExtFromPath     Sinput
    SName       NameXFromPath   Sinput
    
    kascii      = 0
    kNameLen    strlenk SName
    kcnt        = 0
    
    while kcnt <= kNameLen do
        kascii    strchark strsubk(SName,kcnt,kcnt+1)
        if(kascii  == 204)then
            Sleft   strsubk SName,0,kcnt-1
            Sright  strsubk SName,kcnt+2,strlenk:k(SName)
            kBase   strchark strsubk(SName,kcnt-1,kcnt)
            kType   strchark strsubk(SName,kcnt+1,kcnt+2)
            kcntChart   = 0
            while kcntChart < 53 do
                if(kBase == gkLatinBase[kcntChart] && kType == gkLatinType[kcntChart])then
                    SName   sprintfk "%s%c%s",Sleft,gkLatinAscii[kcntChart],Sright
                endif
                kcntChart += 1
            od
        endif
        if(kascii  == 195)then
            Sleft   strsubk SName,0,kcnt
            Sright  strsubk SName,kcnt+2,strlenk:k(SName)
            kType   strchark strsubk(SName,kcnt+1,kcnt+2)
            SName   sprintfk "%s%c%s",Sleft,kType+64,Sright
        endif
        kcnt += 1
    od
    SLatin  sprintfk "%s%s%s",SDir,SName,SExt
    xout    SLatin
endop

instr loadArray
    SCharChart init "GUI_Rescources/LatinConversionChart.txt"
    kline       init 0
    Sline[]     init 101
    SLine       init ""
    strset      234,"1"
    
    kcnt        init 0
    while (kcnt <= 100 && kline != -1) do
        SLine,kline  readf   SCharChart
        if(kcnt>0 && kcnt <=53)then
            String1              sprintfk "%s",strsubk:S(SLine,0,strindexk:k(SLine,","))
            String2              sprintfk "%s",strsubk:S(SLine,strindexk:k(SLine,",")+1,strindexk:k(SLine,":"))
            String3              sprintfk "%s",strsubk:S(SLine,strindexk:k(SLine,":")+1,strindexk:k(SLine,"\n"))
            String1             strget      234
            gkLatinBase[kcnt-1]   strtolk     String1
            String2             strget      234
            gkLatinType[kcnt-1]   strtolk     String2
            String3             strget      234
            gkLatinAscii[kcnt-1]  strtolk     String3
        endif
        kcnt +=1
    od
endin


instr 1
    SDrop  cabbageGet "LAST_FILE_DROPPED"
    if(changed(SDrop)==1)then
        SLatin  ConvertLatins   SDrop
        cabbageSet      1,"filename","text",SLatin
        SIdentString    sprintfk "file(\"%s\")",SLatin
        cabbageSet      1, "waveform", SIdentString 
    endif
    SRename cabbageGet "rename"
    if(changed(SRename)==1)then
        SLatin  ConvertLatins   SRename
        cabbageSet      1,"filename","text",SLatin
    endif
endin

Thanks for sharing. :+1:

Hi Rory,
So the conversion works and soundfilers, labels and texteditors work well with the converted strings. Not the cabbageSetStateValue opcode though. If I feed any Latin characters to it Cabbage crashes.
So I might have to program a routine that reverses the conversion just in order to save those strings in the state data. But I also thought I bring this to your attention. Attached is a small csd that shows the issue. Maybe you can have a look.
LatinCharCrashing.csd (1.4 KB)

button bounds(10,30,100,15) text("Change String") channel("change")
button bounds(10,10,100,15) text("Save String") channel("save")
label bounds(10,50,100,14) text("ABC") channel("label") colour("black")
image bounds(10,100,50,50) channel("box")
.....
 instr StoreData
        SData           = p4
        SPointer        = p5
        prints         "\n S T O R E   D A T A <---  %s  |  %s",SPointer,SData
        cabbageSetStateValue SPointer, SData
    endin

    instr 1
        String init "ABC"
        if(changed(cabbageGet:k("save"))==1)then
            SInstrCall      sprintfk " i \"StoreData\" 0 0 \"%s\" \"name(1)\"",String
            scoreline       SInstrCall, 1
        endif
        kchange     cabbageGet "change"
        if(changed(kchange)==1)then
            if(kchange==1)then
                String      sprintfk "%cBC",196
            endif
            if(kchange==0)then
                String      sprintfk "%s","ABC"
            endif
            cabbageSet  1,"label","text",String
        endif
        cabbageSet  metro(4),"box","colour",rand(255)
    endin