Skip to content

Instantly share code, notes, and snippets.

@brunaw
Forked from lucacarbonelc/chords + lyrics
Created June 8, 2020 11:46
Show Gist options
  • Save brunaw/26cca1ed1cdc6d463d043130edebdb68 to your computer and use it in GitHub Desktop.
Save brunaw/26cca1ed1cdc6d463d043130edebdb68 to your computer and use it in GitHub Desktop.
link chords to lyrics
library(chorrrds)
library(tidyverse)
# Chords ------------------------------------------------------------------
notes = c('A','B','C','D','E','F','G')
flats = 'b'
sharps = '#'
minor = 'm'
all_notes = c(notes,
paste0(notes, flats),
paste0(notes, sharps),
paste0(notes, minor),
paste0(notes, flats, sharps),
paste0(notes, minor, sharps),
paste0(notes, flats, minor),
paste0(notes, flats, sharps, minor))
# Example -----------------------------------------------------------------
url <- get_songs("The Weeknd")
url_something <- url$url[2]
x <- xml2::read_html(paste0("https://www.cifraclub.com.br",
url_something))
chords_lyrics <- rvest::html_nodes(x, "pre") %>%
rvest::html_text()
chords_lyrics <-
chords_lyrics %>%
str_remove_all(pattern = "[0-9]|[-][0-9][-]|\\||[0-9][br]|~")
chords <- tibble(sapply(chords_lyrics, function(x) strsplit(x, "\n")[[1]], USE.NAMES=FALSE))
chords.dat <- as.data.frame(cbind(rep(NA, nrow(chords)/2),
rep(NA, nrow(chords)/2)))
for (i in 1:nrow(chords)) {
ifelse(any(unlist(str_split(as.character(chords[i, 1]), " ")) %in% all_notes) == TRUE,
chords.dat[i, 1] <- as.character(chords[i, 1]),
chords.dat[i, 2] <- as.character(chords[i, 1])
)
}
chords.dat <- chords.dat[ grep("Intro:", chords.dat$V1, invert = TRUE), ]
chords.dat <- chords.dat[ grep("--", chords.dat$V2, invert = TRUE), ]
chords.dat[,1] <- sub("( *)(\\w+)", "\\2\\1", chords.dat[,1]) # put all the first chords at the beginning of the verse
# (keeping he same spaces between more than two chords)
chords.dat <- chords.dat %>%
mutate_at(c("V2"), funs(lead), n = 1 )
chords.dat <- chords.dat[complete.cases(chords.dat), ]
rownames(chords.dat) <- NULL
rownames(chords.dat) <- as.numeric(rownames(chords.dat))
# Connect chords with lyrics ----------------------------------------------
# The idea is to have a data frame with two columns, one for chords and the other for the connected lyrics,
# such that each row contains one chord (col1) and the lyrics linked to that (col2)
chords.net <- as.data.frame(
bind_cols(
chord = rep(NA, sum(na.omit(str_count(chords.dat[, 1], "\\S+")))*2), # nrow = number of chords in the song
lyric = rep(NA, sum(na.omit(str_count(chords.dat[, 1], "\\S+")))*2)
))
# chords
for(i in 1:nrow(chords.dat)) {
if(str_count(chords.dat[i, 1], "\\S+") > 1){ # if there is more than 1 chord
for (
j in 1:str_count(substring(chords.dat[i, 1], 1, nchar(chords.dat[i, 2])), "\\S+")) { # for each chord in verse i
chords.net[i+j, 1] <- substring(chords.dat[i, 1], # find chords in verse i
sort(unique(na.omit(str_locate(chords.dat[i, 1], all_notes)[,1])))[j], # from this position
sort(unique(na.omit(str_locate(chords.dat[i, 1], all_notes)[,1])))[j]+1) # until this position
}
} else { chords.net[i, 1] <- chords.dat[i, 1] } # otherwise, put that chord in the row
i
}
# lyrics
for (i in 1:nrow(chords.dat)) {
if(str_count(chords.dat[i, 1], "\\S+") > 1){ # if there is more than 1 chord
for (j in 1:str_count(substring(chords.dat[i, 1], 1, nchar(chords.dat[i, 2])), "\\S+")) { # for each chord in verse i
chords.net[i+j, 2] <- substring(chords.dat[i, 2], # find words in verse i linked to chord j
sort(unique(na.omit(str_locate(chords.dat[i, 1], all_notes)[,1])))[j], # from this position (position of chord j)
sort(unique(na.omit(str_locate(chords.dat[i, 1], all_notes)[,1])))[j+1]-1) # until this position (position of next chord, j+1)
}
} else { chords.net[i, 2] <- chords.dat[i, 2] } # otherwise, all the words in the verse are linked to the single chord
}
@lucacarbonelc
Copy link

Cumbersome solution for i+j:

chords.net[match(as.numeric(do.call(paste0, expand.grid(i,j))), sort(as.numeric(do.call(paste0, expand.grid(1:sum(na.omit(str_count(chords.dat[, 1], "\\S+"))),1:max(str_count(substring(chords.dat[, 1], 1, nchar(chords.dat[, 2])), "\\S+"))))))), 1]

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment