# make everything reproducible with this seed
set.seed(24601)
Design algorithms
Algorithms for use in the present design.
Note that stimulus files created from these scripts were saved to the experiment folder.
Frequency vectors with systematically varied entropy
The purpose of this code is to create frequency vectors with systematically varied frequencies, from equal frequency to maximally unequal frequency.
library(dplyr)
library(glue)
# rich to poor
<- 8 # number of unique items
cells <- 64 # total number of occurrences across items
total_sum <- 1:cells # cell ids
all_elements
# the most equal vector
<- rep(total_sum/cells,cells)
equal_vector
# the most unequal vector
<- c(rep(1,(cells-1)),total_sum-(cells-1))
un_equal_vector
# empty matrix to collect combinations of frequency vectors
<- matrix(0,ncol=cells,nrow=500)
frequency_matrix
# start with the most unequal vector
1,] <- un_equal_vector
frequency_matrix[
# run a loop
# take from the max and give to the min
# stop when the new vector = equal_vector
for(i in 2:500){
# if the last vector is the equal_vector break
if(sum(frequency_matrix[i-1, ] == equal_vector) == cells){
<- frequency_matrix[1:(i-1),]
frequency_matrix break
}
# get the last vector
<- frequency_matrix[i-1,]
last_vector
# choose a max cell to take away
<- which(last_vector == max(last_vector))
max_id if(length(max_id) > 1) max_id <- sample(max_id,1)
# choose a cell to add
<- which(last_vector != last_vector[max_id])
possible_add if(length(possible_add) > 1 ) choose_add <- sample(possible_add,1)
if(length(possible_add) == 1 ) choose_add <- possible_add
# create modified vector
<- frequency_matrix[i-1,]
redistribute <- redistribute[max_id] - 1
redistribute[max_id] <- redistribute[choose_add] + 1
redistribute[choose_add]
# assign to frequency matrix
<- redistribute
frequency_matrix[i,]
}
# function to calculate Shannon entropy in bits
<- function(x){
entropy -1*sum(x*log2(x))
}
# remove any duplicate rows in frequency matrix
<- t(apply(frequency_matrix, 1, sort))
sort_frequency <- duplicated(sort_frequency) == FALSE
unique_rows <- sort_frequency[unique_rows,]
unique_frequency
# convert rows to probability vectors
<- unique_frequency/rowSums(unique_frequency)
prob_matrix
# calculate bits for each row
<- apply(prob_matrix,1,entropy)
bits
# find equal intervals in bits
<- 11
num_intervals <- seq(min(bits),max(bits),
equal_interval_bits by = ((max(bits) - min(bits))/num_intervals))
# make a tibble with frequency vectors at equal intervals across bit range
<- tibble::tibble(equal_intervals = equal_interval_bits,
interval_bits id = 1:length(equal_interval_bits),
closest = 0 ) %>%
%>%
rowwise mutate(closest = which.min(abs(bits - equal_interval_bits[id]))) %>%
mutate(bits = bits[closest]) %>%
mutate(frequency_vector = list(unique_frequency[closest,]))
<- function(length = 8){
generate_ordered_permutations sapply(1:length, function(x) rep(1:length,2)[x:(x+(length-1))] )
}
Generating midi files from frequency vectors
library(midiblender)
library(pyramidi)
<- data.frame()
stimlist
# note parameters
<- 4
bars <- 16
possible_time_steps <- 24
note_duration <- c(60, 63, 65, 66, 67, 70, 72, 75)
possible_notes
# order to assign biased frequencies
<- generate_ordered_permutations(length(possible_notes))
permutation_matrix
<- 8
total_notes <- bars*possible_time_steps
total_beats
for(t in 1:dim(interval_bits)[1]) {
for (n in 1:length(possible_notes)) {
<- tibble::tibble(
compose_notes note_id = integer(),
note = integer(),
beat_on = integer(),
note_on = integer(),
note_off = integer()
%>%
) # 1 beat every time_step
rowwise() %>%
add_row(beat_on = 1,
note = sample(rep(possible_notes,times=interval_bits$frequency_vector[[t]][permutation_matrix[n,]]))
%>%
)ungroup() %>%
# handle note times
mutate(
note_id = 1:n(),
note_on = (1:n() - 1) * note_duration,
note_off = note_on + note_duration
%>%
) filter(beat_on == 1) %>%
#pivot to long
::pivot_longer(c("note_on", "note_off"),
tidyrnames_to = "type",
values_to = "time") %>%
mutate(time = time - lag(time, default = 0))
## add to a new midi df
<- create_empty_midi_df() %>% # initialize
new_midi_df add_meta_track_name(name = "My track") %>%
add_meta_tempo(tempo = 500000) %>%
add_meta_time_sig(
numerator = 4,
denominator = 4,
clocks_per_click = 36,
notated_32nd_notes_per_beat = 8
%>%
) add_program_change(program = 0,
channel = 0) %>%
add_control_change(control = 0, value = 0) %>%
#use dplyr::add_row to add a bunch of notes
add_row(
i_track = rep(0, dim(compose_notes)[1]),
meta = rep(FALSE, dim(compose_notes)[1]),
note = compose_notes$note,
type = compose_notes$type,
time = compose_notes$time,
velocity = 120
%>%
) add_meta_end_of_track()
#write midi
#Initialize new pyramidi object
<- pyramidi::MidiFramer$new()
new_pyramidi_object # update ticks per beat
$ticks_per_beat <- 96L
new_pyramidi_object# update object with new midi df
$mf$midi_frame_unnested$update_unnested_mf(new_midi_df)
new_pyramidi_object# write to midi file
<- glue::glue("midi/freq_vec_{t}_{n}.mid")
file_string $mf$write_file(file_string)
new_pyramidi_object
<- interval_bits[t, ] %>%
new_df mutate(stimulus = file_string,
possible_notes = list(possible_notes),
frequency_vector = list(interval_bits$frequency_vector[[t]][permutation_matrix[n,]]))
<- rbind(stimlist,new_df)
stimlist
} }
to js
<- stimlist %>%
stimlist rowwise() %>%
mutate(stimulus = gsub("midi","mp3s", stimulus)) %>%
mutate(stimulus = gsub("mid","mp3",stimulus))
<- jsonlite::toJSON(stimlist)
json_stimlist #cat(json_stimlist)
<-file("stimlist.js")
fileConn writeLines(paste("stimlist = ",json_stimlist,";"), fileConn)
close(fileConn)
to mp3
library(fluidsynth)
# get midi file_names
<- list.files("midi",include.dirs = T)
midi_files
#write to mp3
for(i in midi_files){
print(i)
::midi_convert(paste0("midi/",i),
fluidsynthoutput = paste0("mp3s/",gsub("mid","mp3",i)),
verbose = F)
}
crop to 8 seconds
library(av)
# get midi file_names
<- list.files("mp3s",include.dirs = T)
mp3_files
#crop to 8 seconds
for(i in mp3_files){
print(i)
::av_audio_convert(paste0("mp3s/",i),paste0("cropped_mp3s/",i),total_time = 8)
av
}
#delete mp3s folder to clean up
unlink("mp3s",recursive = T)
# rename folder
file.rename("cropped_mp3s","mp3s")
save.image("stimgen-2-28-24.RData")