Module:MOS: Difference between revisions

Ganaram inukshuk (talk | contribs)
a bugfix; reduce functions now accept negative intervals
Ganaram inukshuk (talk | contribs)
Bugfixes; complement code handles negative intervals; code simplifications
Line 93: Line 93:
if interval['L'] == 1 then
if interval['L'] == 1 then
L_string = "L"
L_string = "L"
elseif interval['L'] ~= 0 and interval['L'] ~= 1 then
elseif interval['L'] ~= 0 or interval['L'] ~= 1 then
L_string = string.format("%dL", interval['L'])
L_string = string.format("%dL", interval['L'])
end
end
if interval['s'] == 1 then
if interval['s'] == 1 then
s_string = "s"
s_string = "s"
elseif interval['s'] ~= 0 and interval['s'] ~= 1 then
elseif interval['s'] ~= 0 or interval['s'] ~= 1 then
s_string = string.format("%ds", interval['s'])
s_string = string.format("%ds", interval['s'])
end
end
Line 216: Line 216:
function p.period(mos)  
function p.period(mos)  
local gcd = utils._gcd(mos.nL, mos.ns)
local gcd = utils._gcd(mos.nL, mos.ns)
local result = {
return {
['L'] = mos.nL / gcd,
['L'] = mos.nL / gcd,
['s'] = mos.ns / gcd
['s'] = mos.ns / gcd
}
}
return result
end
end


Line 227: Line 226:
-- the same for single-period mosses.
-- the same for single-period mosses.
function p.equave(mos)  
function p.equave(mos)  
local result = {
return {
['L'] = mos.nL,
['L'] = mos.nL,
['s'] = mos.ns
['s'] = mos.ns
}
}
return result
end
end


Line 243: Line 241:
-- The unison only has one size: perfect.
-- The unison only has one size: perfect.
function p.unison()
function p.unison()
local result = {
return { ['L'] = 0, ['s'] = 0 }
['L'] = 0,
['s'] = 0
}
return result
end
end


Line 253: Line 247:
-- Adding or subtracting any interval by this interval changes its "size".
-- Adding or subtracting any interval by this interval changes its "size".
function p.chroma()
function p.chroma()
local result = {
return { ['L'] = 1, ['s'] = -1 }
['L'] = 1,
end
['s'] = -1
 
}
-- Compute the vector for an augmented step. It's a large step plus a chroma.
return result
function p.augmented_step()
return { ['L'] = 2, ['s'] = -1 }
end
end


-- Compute the vector for a single large step.
-- Compute the vector for a single large step.
function p.large_step()
function p.large_step()
local result = {
return { ['L'] = 1, ['s'] = 0 }
['L'] = 1,
['s'] = 0
}
return result
end
end


-- Compute the vector for a single small step.
-- Compute the vector for a single small step.
function p.small_step()
function p.small_step()
local result = {
return { ['L'] = 0, ['s'] = 1 }
['L'] = 0,
['s'] = 1
}
return result
end
 
-- Compute the vector for an augmented step. It's a large step plus a chroma.
function p.augmented_step()
return p.interval_add(p.large_step(), p.chroma())
end
end


-- Compute the vector for a diminished step. It's a small step minus a chroma.
-- Compute the vector for a diminished step. It's a small step minus a chroma.
function p.diminished_step()
function p.diminished_step()
return p.interval_sub(p.small_step(), p.chroma())
return { ['L'] = -1, ['s'] = 2 }
end
end


Line 311: Line 293:
local interval_vector = p.interval_from_step_sequence(step_sequence)
local interval_vector = p.interval_from_step_sequence(step_sequence)
interval_vector['L'] = interval_vector['L'] + size_offset
local chromas = p.interval_mul(p.chroma(), size_offset)
interval_vector['s'] = interval_vector['s'] - size_offset
interval_vector = p.interval_add(interval_vector, chromas)
return interval_vector
return interval_vector
end
end
Line 326: Line 309:
function p.interval_from_step_sequence(step_sequence)
function p.interval_from_step_sequence(step_sequence)
local mossteps = #step_sequence
local mossteps = #step_sequence
local interval_vector = {
local interval_vector = p.unison()
['L'] = 0,
['s'] = 0
}
for i = 1, mossteps do
for i = 1, mossteps do
local step = string.sub(step_sequence, i, i)
local step = string.sub(step_sequence, i, i)
if step == "L" then
if step == "L" then
interval_vector['L'] = interval_vector['L'] + 1
interval_vector = p.interval_add(interval_vector, p.large_step())
elseif step == "s" or step == "S" then
elseif step == "s" or step == "S" then
interval_vector['s'] = interval_vector['s'] + 1
interval_vector = p.interval_add(interval_vector, p.small_step())
elseif step == "c" then
elseif step == "c" then
interval_vector['L'] = interval_vector['L'] + 1
interval_vector = p.interval_add(interval_vector, p.chroma())
interval_vector['s'] = interval_vector['s'] - 1
elseif step == "A" then
elseif step == "A" then
interval_vector['L'] = interval_vector['L'] + 2
interval_vector = p.interval_add(interval_vector, p.augmented_step())
interval_vector['s'] = interval_vector['s'] - 1
elseif step == "d" then
elseif step == "d" then
interval_vector['L'] = interval_vector['L'] - 1
interval_vector = p.interval_add(interval_vector, p.diminished_step())
interval_vector['s'] = interval_vector['s'] + 2
end
end
end
end
Line 419: Line 396:
-- Add two intervals together by adding their respective vectors.
-- Add two intervals together by adding their respective vectors.
function p.interval_add(interval_1, interval_2)
function p.interval_add(interval_1, interval_2)
local interval_vector = {  
return {  
['L'] = interval_1['L'] + interval_2['L'],
['L'] = interval_1['L'] + interval_2['L'],
['s'] = interval_1['s'] + interval_2['s']
['s'] = interval_1['s'] + interval_2['s']
}
}
return interval_vector
end
end
-- Subtract two intervals by subtracting their respective vectors.
-- Subtract two intervals by subtracting their respective vectors.
function p.interval_sub(interval_1, interval_2)
function p.interval_sub(interval_1, interval_2)
local interval_vector = {  
return {  
['L'] = interval_1['L'] - interval_2['L'],
['L'] = interval_1['L'] - interval_2['L'],
['s'] = interval_1['s'] - interval_2['s']
['s'] = interval_1['s'] - interval_2['s']
}
}
return interval_vector
end
end


-- Repeatedly add the same interval to itself.
-- Repeatedly add the same interval to itself.
function p.interval_mul(interval, amt)
function p.interval_mul(interval, amt)
local interval_vector = {  
return {  
['L'] = interval['L'] * amt,
['L'] = interval['L'] * amt,
['s'] = interval['s'] * amt
['s'] = interval['s'] * amt
}
}
return interval_vector
end
end


Line 448: Line 422:
--------------------------------------------------------------------------------
--------------------------------------------------------------------------------


-- Given an interval vector and a mos, find its period complement.
-- Given an interval vector and a mos, find its period complement. This is the
-- interval to add to produce the period.
function p.period_complement(interval, mos)
function p.period_complement(interval, mos)
local sign = p.interval_step_count(interval) < 0 and -1 or 1
local period_vector = p.period(mos)
local period_vector = p.period(mos)
return p.interval_sub(period_vector, interval)
return p.interval_sub(p.interval_mul(period_vector, sign), interval)
end
end


-- Given an interval vector and a mos, find its equave complement.
-- Given an interval vector and a mos, find its equave complement. This is the
-- interval to add to produce the equave.
function p.equave_complement(interval, mos)
function p.equave_complement(interval, mos)
local sign = p.interval_step_count(interval) < 0 and -1 or 1
local equave_vector = p.equave(mos, interval)
local equave_vector = p.equave(mos, interval)
return p.interval_sub(equave_vector, interval)
return p.interval_sub(p.interval_mul(equave_vector, sign), interval)
end
end


Line 463: Line 441:
-- modular arithmetic, so passing a negative interval returns a positive one.
-- modular arithmetic, so passing a negative interval returns a positive one.
function p.period_reduce(interval, mos)
function p.period_reduce(interval, mos)
local interval = p.normalize_interval(interval)
local step_count = p.interval_step_count(interval)
local step_count = p.interval_step_count(interval)
local reduce_amt = math.floor(step_count / p.period_step_count(mos))
local reduce_amt = math.floor(step_count / p.period_step_count(mos))
Line 473: Line 450:
-- Given an interval vector and a mos, equave-reduce it. This works like
-- Given an interval vector and a mos, equave-reduce it. This works like
-- modular arithmetic, so passing a negative interval returns a positive one.
-- modular arithmetic, so passing a negative interval returns a positive one.
function p.equave_reduce(interval)
function p.equave_reduce(interval, mos)
local interval = p.normalize_interval(interval)
local step_count = p.interval_step_count(interval)
local step_count = p.interval_step_count(interval)
local reduce_amt = math.floor(step_count / p.equave_step_count(mos))
local reduce_amt = math.floor(step_count / p.equave_step_count(mos))
Line 602: Line 578:
--local interval = p.dark_gen(p.new(5,2))
--local interval = p.dark_gen(p.new(5,2))
--return p.interval_chroma_count(interval, p.new(5,2), -1)
--return p.interval_chroma_count(interval, p.new(5,2), -1)
return p.equave_complement({['L']=-3,['s']=-1},p.new(5,2))
--return p.equave_reduce({['L']=-3,['s']=-1},p.new(5,2))
 
--return p.interval(p.new(5,2), 4, 1)
return p.interval_from_step_sequence("LLLdLLc")
end
end


return p
return p