Comments (6)
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.
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.
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.
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.
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.
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)
- notate() with rests not phrasey HOT 2
- Invalid `key` with ms_transpose HOT 3
- Volta renders wrong HOT 2
- Specifying strings in music strings HOT 3
- (some) paper specifications do not work HOT 6
- `lyrics` class object HOT 2
- Using fretboard_plot on guitarChords HOT 1
- macOS: unable to open HOT 4
- Q: how to create end bar? HOT 5
- Request: support "s" as invisible rest in notes HOT 3
- Mystery of the missing 7th scale degree HOT 1
- using a list in trackbind() HOT 2
- tab() with trackbind only returns two phrases HOT 1
- Relative music strings HOT 2
- Tuning render error HOT 1
- Integrating tabr output into greater lilypond score HOT 2
- Vectorized sfp input HOT 1
- plot 5 string instrument HOT 1
- Can plot_fretboard print "dot" fret numbers? (3, 5, 7, etc) HOT 2
- *Error: Explicit fret range will cut off notes.* when using "fret_range" parameter, function plot_fretboard HOT 1
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from tabr.