Sleeping for total duration

Hello! I’m having a hard time understanding why this code isn’t sleeping for the whole duration. What I am trying to do is iterate through these rows of arrays. I want each row to play for a certain duration, then pause before the next one. In each row, there are samples. These samples are split among each other. What is happening, however, is that samples from the next row are playing over before one row is finished. Does this make sense? Let me know if I need to explain it further and thanks for your help.

require 'csv'
path = '/Users/maxgraze/Desktop/Projects/German speaking'

processGap = 0.5

define :convertToMin do |string|
  return (string[1..3].sub(':','.').to_f*100 + string[0].to_f*60)/100
end


processGap = 0.5


data = []

CSV.foreach(path + '/data.csv', headers: true) do |row|
  types = row[3]
  next if types.nil?
  if types.include?(',')
    # Split the row into an array
    types = types.split(',').map(&:strip)
  end
  data << row.to_h
end

puts("Transformed", data[3]['type'])


def normalize(x, xmin, xmax, ymin, ymax)
  xrange = xmax - xmin
  yrange = ymax - ymin
  ymin + (x - xmin) * (yrange.to_f / xrange)
end

def sample_duration(sample_path)
  info = SonicPi::Core::SampleInfo.new(sample_path)
  info.duration
end

live_loop :westbam do
  sample path + '/westbam.wav', amp: 0.2, attack: 2
end
total_duration = 0

##| sleep 5
data.each do |row|
  types = row['type'].look
  puts("data types:", row['type'])
  bucket = row["bucket"].look.strip.to_i
  norm_bucket = normalize(bucket, 0, 8, 0, 1)
  puts("norm_bucket:", norm_bucket)
  length = row["type"].split(", ").length
  puts("length:", length)
  total_duration = norm_bucket/length
  puts("total duration:", total_duration)
  # Calculate the total duration of all the samples being played
  row["type"].split(", ").each do |type|
    puts("type:", type)
    filename = "#{type}.wav"
    full_path = "#{path}/#{filename}"
    sample full_path, finish: total_duration
    ##| sample_duration = norm_bucket
    ##| total_duration += sample_duration
    ##| puts("sample duration:", sample_duration)
    ##|   puts("total duration:", total_duration)
    ##| end
    ##| sleep norm_bucket + 2
    sleep norm_bucket + 2
    
  end
  sleep 2
  # Sleep for the total duration of the samples being played
end

Hi there,

unfortunately the code you have posted is rather complex so it’s hard to see where your issue is.

Please could you try to reduce the complexity (i.e. remove all csv processing etc.) and share the smallest code which produces the issues you’re obvserving. Then at that point it will be useful to share the code, share what you expect it to do and then what you’re observing so we can see what the issue. might be.

While I haven’t really parsed everything going on in your code, at first glance, I am curious why you have chosen to define your own function called sample_duration when there is already a built in function called sample_duration that can be used to play a sample for the exact duration of the sample length. It would seem like that would be your best bet if you are trying to make sure your samples don’t overlap. This would make more sense to use with each individual sample in the array as opposed to calculating the total duration of the samples in the array. Is there a specific reason you want to do that?

Here is a very simple example of how you could play through an array of samples, wait a bit and then play through a different array of samples:

samps = [:elec_bong, :ambi_drone, :loop_amen, :mehackit_robot1]

samps2 = [:misc_crow, :glitch_perc5, :guit_em9, :vinyl_backspin]


define :play_sample_array do |s|
  s.length.times do |i|
    sample s[i]
    sleep sample_duration s[i]
  end
end


play_sample_array samps
sleep 2
play_sample_array samps2

Apologies if this is missing the point of what you are trying to do.

Hi! Thanks so much for your help. I realize I didn’t explain well what I am trying to do. The point of it is to create a sonification. Each row has certain samples associated with it (conversation, service, etc) and a value (‘bucket’ 0 - 7). I want the entirety of the samples in a given row to respond to the bucket value. For example, if one row’s value is 2, all the samples in that row will only play for however long ‘2’ is normalized to. Does that make sense? But now when I play them, they are all overlapping, and samples in one row are not finished before the samples in the next row start.

all the samples in that row will only play for however long ‘2’ is normalized to.

I am not clear on what you mean by “normalized to”. What is happening when something is “normalized”? Can you give an example of what this would look like? What would ‘2’ be normalized to?

I see the “normalize” function you have defined in your code. it looks like you are “mapping” one range of values to another range of values. This post shows a formula that looks similar to the one you have. Is this what you are trying to do? If so, how would this connect to the duration for each row of samples?

Example of csv reading with three parameters per row, sample name, sustain and sleep

ambi_choir,1,2
ambi_drone,2,1
ambi_swoosh,2,2
ambi_glass_hum,3,1

require 'csv'
sp = '/home/raulmate/Audio/sample.csv'
data = CSV.read(sp)
s = data.length

s.times do |i|
  d = data[i][1].to_i
  ds = data[i][2].to_i
  sample data[i][0]+'.flac',sustain: d
  sleep ds
end

The normalization method is because ‘finish’ needs to be a value between 0 & 1. So I normalize the buckets (0-7) so that they have a value between 0 & 1.

The data looks like this. “bucket” just refers to the approximate total duration of the activities on a given day. I only want the entire row to play for as long as the bucket is. So if the bucket is shorter, the samples will place for less time.

date minutes bucket type
1. February 65-95 7 service, lunch, service, service, service
2. February 15-30 4 tandem
3. February 0 0
4. February 0-5 1 service

In this example, is data[i][0] referring to a sample title? The issue with mine is that any column can have 0-5 types.

I just realized the problem. It’s with the ‘finish.’ I didn’t realize that ‘finish’ calculated a point in the sample. I just want the sample to finish at a time I specify, like 2 seconds, for example. How can I do this?

s = :loop_mika #long sample

duration = sample_duration(s)
normalized_duration = 1.0 / duration
two_seconds = 2.0 * normalized_duration

sample s, finish: two_seconds
1 Like

This works!

Here is a function that will execute this code for any sample and any duration as long as it is less than the duration of the sample.

The first argument for the function is the sample and the second argument is the duration you want.

define :play_normalized_sample do |s, dur| 
  normalized_duration = 1.0 / sample_duration(s)
  sample s, finish: dur * normalized_duration
end

play_normalized_sample :loop_mika, 2

I ended up figuring it out, but this version is MUCH nicer! Thanks so much. My next improvement will be to try to get the first item in the row to play for the entirety of the row’s duration if it belongs to a certain type…but I am doubting I will ever get there since this project dragged out for too long.

You could prevent an error occurring if dur is greater than the sample duration

define :play_normalized_sample do |s, dur|
  normalized_duration = 1.0 / sample_duration(s)
  sample s, finish: [1,(dur * normalized_duration)].min #prevents finish: > 1
end

play_normalized_sample :loop_mika, 10 #this will play for 8 beats the sample duration
1 Like

Thanks @robin.newman! I was wondering how to deal with handling a parameter that would result in an error.

@datagrazer I know how that goes! If you do ever finish it, please share! Good luck!