Class: Musicality::CounterpointGenerator

Inherits:
Object
  • Object
show all
Defined in:
lib/musicality/composition/generation/counterpoint_generator.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(rhythm, palette, max_fact = 5) ⇒ CounterpointGenerator

Returns a new instance of CounterpointGenerator.

Raises:

  • (ArgumentError)


5
6
7
8
9
10
11
12
13
14
15
16
# File 'lib/musicality/composition/generation/counterpoint_generator.rb', line 5

def initialize rhythm, palette, max_fact = 5
  raise ArgumentError, "max_fact must be >= palette size" if max_fact < palette.size
  
  @rhythm = rhythm
  @palette = palette
  @total_dur = rhythm.map {|dur| dur.abs }.inject(0,:+)
  @solution_classes = self.class.solution_classes(@total_dur, @palette)
  @solution_classes.keep_if do |sc|
    sc.map {|k,v| k*v}.inject(0,:+) == @total_dur
  end
  @solutions = figure_solutions(max_fact)
end

Instance Attribute Details

#paletteObject (readonly)

Returns the value of attribute palette.



4
5
6
# File 'lib/musicality/composition/generation/counterpoint_generator.rb', line 4

def palette
  @palette
end

#rhythmObject (readonly)

Returns the value of attribute rhythm.



4
5
6
# File 'lib/musicality/composition/generation/counterpoint_generator.rb', line 4

def rhythm
  @rhythm
end

#solution_classesObject (readonly)

Returns the value of attribute solution_classes.



4
5
6
# File 'lib/musicality/composition/generation/counterpoint_generator.rb', line 4

def solution_classes
  @solution_classes
end

#solutionsObject (readonly)

Returns the value of attribute solutions.



4
5
6
# File 'lib/musicality/composition/generation/counterpoint_generator.rb', line 4

def solutions
  @solutions
end

#total_durObject (readonly)

Returns the value of attribute total_dur.



4
5
6
# File 'lib/musicality/composition/generation/counterpoint_generator.rb', line 4

def total_dur
  @total_dur
end

Class Method Details

.rhythm_to_computer(rhythm) ⇒ Object



18
19
20
21
22
23
24
# File 'lib/musicality/composition/generation/counterpoint_generator.rb', line 18

def self.rhythm_to_computer rhythm
  rhythm_accum = AddingSequence.new(rhythm).take(rhythm.size+1).to_a
  x = rhythm_accum[0,rhythm.size]
  y = rhythm_accum[1,rhythm.size].map {|y_| Change::Immediate.new(y_) }
  value_changes = Hash[ [x,y].transpose ]
  ValueComputer.new(0, value_changes)
end

Instance Method Details

#best_solution(ideal_overlap, sample_rate) ⇒ Object



42
43
44
# File 'lib/musicality/composition/generation/counterpoint_generator.rb', line 42

def best_solution ideal_overlap, sample_rate
  @solutions.min_by {|sol| evaluate(sol,ideal_overlap,sample_rate) }
end

#best_solutions(n, ideal_overlap, sample_rate) ⇒ Object



46
47
48
# File 'lib/musicality/composition/generation/counterpoint_generator.rb', line 46

def best_solutions n, ideal_overlap, sample_rate
  @solutions.sort_by {|sol| evaluate(sol,ideal_overlap,sample_rate) }.take(n)
end

#evaluate(solution, ideal_overlap, sample_rate) ⇒ Object



26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
# File 'lib/musicality/composition/generation/counterpoint_generator.rb', line 26

def evaluate solution, ideal_overlap, sample_rate
  if solution.inject(0,:+) != @total_dur
    raise ArgumentError, "Given solution #{solution} does not have same duration as rhythm"
  end
  
  rhythm_comp = self.class.rhythm_to_computer(@rhythm)
  solution_comp = self.class.rhythm_to_computer(solution)
  
  r = rhythm_comp.sample(0...@total_dur, sample_rate)
  s = solution_comp.sample(0...@total_dur, sample_rate)
  n_same = [r,s].transpose.count {|pair| pair[0] == pair[1] }
  n_samples = (@total_dur*sample_rate).to_i
  percent_overlap = (n_same/n_samples).to_f
  return (ideal_overlap - percent_overlap).abs
end