Code Monkey home page Code Monkey logo

Comments (6)

leonawicz avatar leonawicz commented on August 18, 2024

I like some of these ideas. I agree this string-fret combo functionality would get more use if the arguments did not have to be split apart. I for one would use it more than I do.

There are some aspects and suggestions within your examples that would pose technical challenges. I also think some aspects are not cohesive enough with the rest of the package. But overall I support this idea.

I will have to give it some thought (and find some time) so it won't happen as quickly as the last one which was a low-hanging fruit. What mainly needs to happen in terms of being able to provide a single string of information containing string numbers, fret numbers, and the remainder of the info, is that it needs to be unambiguously parsable into the three component pieces. Like currently, the string-fret approach piggybacks on phrase. As long as the input can be mapped to note, info and string, it can work.

Ideally there will be a chance for some strong assumptions that always work regarding splitting such strings into their three component parts. Again I'll have to really sit down and think about edge cases. But there is some possibility a delimiter would have to be introduced, frustratingly. But maybe not.

As much as I like shortening all the typing as much as possible, I do intend for tabr to not get so extreme that it starts to look unreadable based on syntax rather than organization. Consistency is also important. For example, I am not in favor of hex codes for fret numbers above the ninth fret: the letters a-g, among other characters, are treated as special throughout tabr. Even if here it is implicitly understandable that they would not represent notes, I don't want to introduce that just to save a few characters (as much as I truly dislike typing something like (12)(11)(10)). It's also an insufficient solution because it only gets you to the 15th fret anyway. I don't think many people would be keen on thinking in hex coding, but really I just do not wish to take tabr syntax in that direction; personally it feels too heavy on the "short as possible" side, which throughout the package's development I have been struggling to balance with the more important (to me) idea of not making the syntax to opaque or low level or inconsistent (even if that means it won't be good at certain things LilyPond can do). But maybe there is a better way for expressing that the digits in a two digit fret number is tightly bound. For now though, the clear () approach is deeply integrated into tabr parsing logic.

I'm still up in the air on string assumptions. I found that shorthand like 5s really helped with chords, avoiding things like 54321. I see some value in a starting string specification. For example, frets 987 with strings 321 can already use 3s. However if they are directly paired in time, why not just say 3 and only need to be explicit if the strings are not consecutive, such as 431? But I'd have to think about the ramifications, since the code is designed (for the most part) to reject unequal length inputs when checking notes, strings, frets, etc. in many places. Clearly it allows some obvious recycling. I'd have to think if that would work nicely here.

Making note info interchangeable between whether it is provided along with string or fret would be problematic. I think the focus should be on making a wrapper API approach that simply allows all three components to be joined in each space-delimited time step, and that can map onto the current sf_phrase internals, which in turn gets mapped to phrase args. sf_phrase could potentially be redesigned to support the current approach of individual components or, say if there is no second argument, everything is assumed to be in the first. Just thinking out loud here for now.

from tabr.

leonawicz avatar leonawicz commented on August 18, 2024

I forgot to mention re: string assumptions. One thing you seem to be proposing is carrying information about string numbers specified at one space-delimited point in time, over to subsequent time points. That would be an additional level of complexity. Of course it is doable, but I just want to point out for reference that currently the parsing logic is designed such that each time step can be parsed independently. There are no iterative operations performed in terms of the parsing of time step n being dependent on step n-1 and so on. I can see providing that to this particular single-string input sf_phrase approach so that information specified can be recycled across time until the next change, but it's worth mentioning the change in complexity involved.

I imagine it would probably be easiest to tack onto last after the rest of the functionality was in place. For example, with each time step split out into its three pieces, I could then scan for those missing string information and apply some recycling/filling in.

from tabr.

kimo-k avatar kimo-k commented on August 18, 2024

I know next to nothing about R but I made this attempt. It should accomplish goals 2-6.
I realized there is some ambiguity between 2 and 3.
Also it doesn't quite work with the standard (10)(11)(12) fret names, only the alphanumeric kind. Strangely, that was easier to work with.
(edit: added infostring)

psf_phrase <- function(pstr) {
    pstr <- strsplit(pstr, split=" ")[[1]]
    prevString <- "6"
    prevInfo <- "4"
    strings <- ""
    frets <- ""
    infos <- ""
    for (x in pstr) {
        mult <- ""
        s <- "s"
        info <- ""
                                        #get mult
       if(length(grep("\\*",x) == 1)) {
            mult <- strsplit(x,"\\*")[[1]][2]
            mult <- paste("*",mult,sep="")
            x <- strsplit(x,"\\*")[[1]][1]
       }
                                        #get info
        if(length(grep("\\-",x) == 1)) {
            info <- strsplit(x,"\\-")[[1]][2]
            x <- strsplit(x,"\\-")[[1]][1]
            prevInfo <- gsub("~","",info)
        }
        else info <- prevInfo
                                        #if token hasn't s, make s string from prevString
        if(length(grep("[0-9]{1}\\|", x)) == 0) {
            string <- prevString
            fret <- x
        }
                                        #else, make s string from token
        else {
            string <- strsplit(x, "\\|")[[1]][1]
            fret <- strsplit(x, "\\|")[[1]][2]
            prevString <- string
        }
                                        #if fret size less than string, add x's to fret
        addx <- as.numeric(string) - nchar(fret)
        if(addx > 0) {
            fret <- paste(fret, paste(rep("x", addx), collapse=""), sep="")
            }
                                        #if fret has x, turn s string into broken string, and delete x's from fret
        if (length(grep("x", fret)) > 0) {
            keep <- gregexpr(pattern = "[^x]",fret)[[1]]
            n <- as.numeric(string):1
            string <- paste(n[keep],collapse="")
            fret=gsub("x","",fret)
            s <- ""
        }
        if(string == "1") s <- ""
        if(length(grep("r",fret)) > 0) {
            string <- "r"
            s <- ""
        }
        if(length(grep("s",fret)) > 0) {
            string <- "s"
            s <- ""
        }
        if(length(grep("~",fret)) > 0) {
            info <- paste(info,"~",sep="")
            string <- paste(string,"~",sep="")
        }
                                        #cat note and string
        strings <- paste(strings, string, s, mult, " ", sep="")
        frets   <- paste(frets,   fret,      mult, " ", sep="")
        infos   <- paste(infos,   info,      mult, " ", sep="")
                                        #deal with hex-like fret names

    }
    strings <- substr(strings,1,nchar(strings)-1)
    frets <- substr(frets,1,nchar(frets)-1)
    infos <- substr(infos,1,nchar(infos)-1)

    hex <- c("a",    "b",    "c",    "d",    "e",    "f",    "g",    "h",    "i",    "j",    "k",    "l",
             "m",    "n",    "o",    "p",    "q")
    num <- c("(10)", "(11)", "(12)", "(13)", "(14)", "(15)", "(16)", "(17)", "(18)", "(19)", "(20)", "(21)",
             "(22)", "(23)", "(24)", "(25)", "(26)")
    frets <- Reduce(function(d, i) gsub(paste0(hex[i]), num[i], d), seq_along(hex), frets)
    return(sf_phrase(strings,frets,infos))
}

psf_phrase("3|987-2*3 775*2 553-4. 335-16 5|7x7-4.~*3 545-4 325 6|2x10")

from tabr.

leonawicz avatar leonawicz commented on August 18, 2024

I finally got this going. Sorry for the delay in reply, I have been busy with other things. But what you've shared here is a great help, being able to execute some examples. I was able to see the combination of suggestions more clearly.

The version I have added on GitHub now is a bit different in approach and style but aims to fold in your suggestions. It is an alternative argument specification to sf_phrase rather than yet another phrase function. You can provide the same three explicit arguments as before, closely mimicking phrase. Alternatively, you can leave fret and info as NULL and pass everything as a single input string to the first argument. The new input style allows for more things like implicitly repeating values across time as well. It sticks closely to the standard input style in terms of the string's contents. For reasons, I went with a one unique delimiter ;. The help docs have been updated and they explain things in a bit more detail.

An example:

# pass three explicit arguments
s <- "3s*5 53~*3 543*2 643"
f <- "987*2 775 553 335 77~*3 545 325 210"
i <- "2*3 4. 16 4.~*3 4*3"
sfp(s, f, i)

# or pass everything to the first argument
# Nominally shorter syntax, but also potentially much easier to reason about by time step
x <- "3;987;2*2 775 ;553;4. ;335;16 5;7x7;4.~*3 ;545;4 325 6;2x10;"
sfp(x)

I have written some unit tests, but this new input method could use additional testing before a CRAN release. I think this is a very useful approach though. I've leave this open for a while in case there are bugs I haven't found yet.

from tabr.

kimo-k avatar kimo-k commented on August 18, 2024

Hey, I really appeciate your continued efforts on this project. This library is helping tremendously in my creative work.

I think your solution makes sense here and satisfies the main concern.

I had another idea that could be worth a try. Instead of single info or fret values, could we use a set, e.g.:

x <- "3;987;(2 4. 8) 775 ;553;(8 16*2)*2 ;(335 353 535);(8. 16)"

equivalent to:

s <- "3s*18"
f <- "987*3 775*3 553*6 335*2 353*2 535*2"
i <- "2 4. 8 2 4. 8 8 16*2 8 16*2 8. 16 8. 16 8. 16'

from tabr.

leonawicz avatar leonawicz commented on August 18, 2024

Hi, I'm glad to hear you are finding use for the package. Your feedback has been very helpful as well.

I see where you are coming from here, but I don't think this would be a good addition to tabr as is. For this I would recommend if you want to write a function that can wrap around sf_phrase, you could run that in your work but I don't think I'd migrate it to tabr. It could also be a bit of a complex mapping. The abstraction level and cognitive load is getting higher.

But my primary issue is with consistent syntax rules and expectations. While sf_phrase fills an important niche, even with the latest addition allowing all input in a single string argument, it still does so while adhering to the most fundamental features (in my view) of the package's music notation syntax. Particularly in regard to space-delimited time always having a very simple and consistent implementation and interpretation.

At a minimum I want to maintain that for any given function or form of string input for creating phrases, a couple things hold true regarding timesteps. The input may look different for phrase vs. sf_phrase, and for one input method of sf_phrase vs. the other, or for notes vs. info vs. strings vs. frets. But there are a couple things I've required of all string inputs like this across the package. (1) a space always separates consecutive timesteps and does nothing else (which is still true in your example) but also (2) that the information at every timestep in a string for a given context is complete and means the same thing. I don't want it to get any more complex than that. This breaks down if you think about the above example like a vector split on the spaces:

x <- c("3;987;(2", "4.", "8)", "775", ";553;(8", "16*2)*2", ";(335", "353", "535);(8.", "16)")

This breaks a syntax property I really want to maintain consistently. But I also don't want to bind durations or other consecutive elements with another delimiter because each time step must be separated by a space. And we can both agree that adding more parentheses is no fun. I'll close this issue now. I think we've come a good way with the latest sf_phrase. But I do have one idea and that could be the next issue if you end up writing something to convert your example above.

Suggestion

I'm in favor of some string transformation functions in isolation. So, the tabr syntax remains the syntax. But if a user wants to write an alternative form, then there can be distinct string transform functions that convert various other styles you might write music notation in into "tabr standard", rather than trying to make all kinds of syntax work directly in a function like sf_phrase or make multiple wrapper functions in the package.

From an R perspective, it's an obvious clean separation like between data manipulation and graphing or data prep and analysis; preparing the right syntax and then rendering sheet music. Abstract it away from sf_phrase or phrase. Those are where building up a sheet music score begin. Everything prior to that is data prep. Make this a data pre-processing step, which makes sense for R users.

So using your x above, you could then use some function x <- to_tabr(x, syntax = "fnord-repeater") let's say, and the new x could be something like a list with three vectors string, fret and info, or notes, info and/or string. I already plan (when I ever I can possibly get to it lol) to have a collection of syntax conversion functions that map notation input from other music data sources to tabr syntax. Yours could be just one in a list of options. There's a place slated for that in tabr and I think that's a great idea compared to trying to make the phrase functions incredibly general or proliferate wrapper functions.

In fact when done from this perspective, I don't care what the original syntax looks like. I can keep the package from getting too complex or confusing, but I can offer any number of what are essentially API functions to tabr style and its LilyPond pipeline. And users can write in any style they want if there's a mapping function. In that context, as long as someone in the world uses some form of notation and there's a function to map it, I'd love to have it. I envision the utility of this mostly for importing external music notation dataset sources, which is why I hadn't really pictured users entering notation directly from scratch doing the same kind of conversion. Sounds great to me.

from tabr.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.