public
Created

hack functions to generate MIDI output from R using midicv and play using timidity, with usage example at end

  • Download Gist
midifunctions.R
R
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112
midi <- function(title="R-created Midi", bpm=120){
## Creates empty "midi" object that tracks can be inserted into
options(scipen=7) ## avoids writing anything here in scientific notation
 
## Create new data.frame and add headers
newdf <- data.frame(matrix(nrow=7,ncol=7))
names(newdf) <- c("tracknum", "time", "type","channel", "note", "velocity", "tempoonly")
newdf[1,] <- c(0,0, "Header", 1, 1, 480, NA)
newdf[2,] <- c(1,0, "Start_track", NA, NA, NA, NA)
newdf[3,] <- c(1,0, "Title_t", title, NA, NA, NA)
newdf[4,] <- c(1,0, "Time_signature", 4, 2, 24, 8)
newdf[5,] <- c(1,0, "Tempo", 60000000/bpm, NA,NA,NA)
newdf[6,] <- c(1,0, "End_track", NA, NA, NA, NA)
newdf[7,] <- c(0,0, "End_of_file", NA, NA, NA, NA)
 
options(scipen=0) ## reset option
 
## Set class and attributes
class(newdf) <- c("midi", "data.frame")
attributes(newdf)$istrack <- FALSE
attributes(newdf)$percussion <- FALSE
attributes(newdf)$maxtrack <- 0
attributes(newdf)$maxchannel <- 0
newdf
}
 
track <- function(beat, durs, notes, veloc, program = 1, percussion = FALSE) {
tracknum <- rep(NA,length(beat))
score <- data.frame(tracknum)
score$starttime <- beat
score$channel <- NA
score$note <- notes
score$velocity <- veloc
score$endtime <- score$starttime+durs
 
## Change times to MIDI quarter note
score$starttime <- score$starttime * 480
score$endtime <- score$endtime * 480
 
ons <- score[,setdiff(names(score), "endtime")]
ons$time <- ons$starttime
ons$type <- "Note_on_c"
ons <- ons[, c("tracknum", "time", "type","channel", "note", "velocity")]
 
offs <- score[, setdiff(names(score), "starttime")]
offs$time <- offs$endtime
offs$type <- "Note_off_c"
offs$velocity <- 0
offs <- offs[, c("tracknum", "time", "type","channel", "note", "velocity")]
 
all <- rbind(ons, offs)
all <- all[order(all$time, all$type),]
all$tempoonly <- NA
begintrack <- c(NA, 0, "Start_track", NA, NA, NA, NA)
chooseinstrument <- c(NA, 0, "Program_c", NA, program, NA,NA)
endtrack <- c(NA, max(all$time), "End_track", NA, NA, NA, NA)
fulltrack <- rbind(begintrack, chooseinstrument, all, endtrack)
 
class(fulltrack) <- c("midi", "data.frame")
attributes(fulltrack)$istrack <- TRUE
attributes(fulltrack)$percussion <- percussion
fulltrack
}
 
tempo <- function(bpm=120) {
class(bpm) <- c("midi", "numeric")
attributes(bpm)$istrack <- FALSE
bpm
}
 
"+.midi" <- function(midiOld, midiTrack, ...) {
if(attributes(midiTrack)$istrack){
## Add a new midi track (first argument) into an existing one (second)
## Channel numbering starts at 11 to avoid any confusion with percussion tracks
## which in MIDI are always channel 10.
if(attributes(midiOld)$percussion) {
channelnum <- 10
} else {
channelnum <- attributes(midiOld)$maxchannel + 1
tracknum <- attributes(midiOld)$maxtrack + 1
}
midiOld[1,5] <- as.numeric(midiOld[1,5])+1
midiTrack$tracknum <- tracknum
midiTrack$channel <- channelnum
midiNew <- rbind(midiOld[-nrow(midiOld),], midiTrack, midiOld[nrow(midiOld),])
##
class(midiNew) <- c("midi", "data.frame")
attributes(midiNew)$istrack <- FALSE
attributes(midiNew)$percussion <- FALSE
attributes(midiNew)$maxtrack <- attributes(midiOld)$maxchannel + 1
attributes(midiNew)$maxchannel <- attributes(midiOld)$maxtrack + 1
} else {
midiNew <- midiOld
midiNew[5,4] <- 60000000/midiTrack
}
midiNew
}
 
print.midi <- function(x) {
outfile <-tempfile()
write.table(x, file=paste(outfile,"csv",sep="."), quote=F, sep=",", row.names=F, col.names=F, na="")
system(paste("csvmidi", paste(outfile,"csv",sep="."), paste(outfile,"mid",sep=".")))
system(paste("timidity",paste(outfile,"mid",sep="."))
}
 
## Usage example
jj <- as.vector(sunspot.year, mode="numeric")
jjn <- round(((jj-min(jj))*72/(max(jj)-min(jj))-36) + 70,0)
 
midi() + track(beat=0:(length(jjn)-1), durs=0.2, notes=jjn, veloc=127, program=13) + tempo(780)

Please sign in to comment on this gist.

Something went wrong with that request. Please try again.