Skip to content

Instantly share code, notes, and snippets.

@GanaramInukshuk
Last active March 26, 2022 09:31
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save GanaramInukshuk/f92db8c4d48484b51e389f79b0b7f18e to your computer and use it in GitHub Desktop.
Save GanaramInukshuk/f92db8c4d48484b51e389f79b0b7f18e to your computer and use it in GitHub Desktop.
This is code for a xenfinder program that I wrote using VBA. This is to be used as a macro for Microsoft Excel. I saved it as a .txt because I don't know the file format for VBA scripts. (I also don't know why I named it as xenfinder when it's supposed to be mosfinder... =P)
Sub MosFinder()
'
' Macro1 Macro
' Denote the starting cell as B2
Dim rowIndex As Integer
Dim colIndex As Integer
rowIndex = 2
colIndex = 2
' Make B2 the active cell
'Cells(rowIndex, colIndex).
' Get the value of ed from cell B2
Dim ed As Integer
ed = ActiveSheet.Cells(rowIndex, colIndex).Value
MsgBox ("Generating scale table for " & ed & " equal divisions of the equave.")
Dim largeStep As Integer
Dim smallStep As Integer
largeStep = 7
smallStep = ed - largeStep
Dim progenitorScale() As Variant
rowIndex = rowIndex + 1
'Call GenerateTable(ed, 1, rowIndex, colIndex)
For i = 1 To (ed / 2)
Dim period As Integer
period = i
Call GenerateTable(ed, period, rowIndex, colIndex)
Next i
' Move the active cell
Set cellToMoveTo = Cells(2, 2)
cellToMoveTo.Select
End Sub
' Generates the table for a given ed and period, starting with the header.
' This requires a row/column offset, and those are changed as the table is made
' The header cells are as follows:
' - Step pattern: spans ed cells and denotes the step pattern for each scale
' - General information: spans 3 cells and denotes temperament-agnostic information (mos, step ratio, and tamnams name)
' - Temperament information: spans 1 cell (minimum) and denotes information related to temperaments
' Parameters are as follows:
' - ed: the number of equal divisions
' - period: if ed is divisible by period, then the table produced will be a multi-period scale;
' the subroutine shouldn't make a table if ed isn't divisible by period
' - rowIndex and colIndex: the cell that the table will start at denoted by row/column indices
Public Sub GenerateTable(ByRef ed As Integer, ByRef period As Integer, ByRef rowIndex As Integer, ByRef colIndex As Integer)
' Determine whether ed is divisible by period
Dim remainder As Integer
remainder = ed Mod period
' If ed is not divisible by period, exit the subroutine early
If remainder <> 0 Then
Exit Sub
End If
' Set the column counts for general and temperament information
Dim colsForGeneralInfo As Integer
Dim colsForTemperamentInfo As Integer
colsForGeneralInfo = 3
colsForTemperamentInfo = 1
' Create the table's "superheader" (or table title)
' The number of cells the title spans is based on the number of cells for the ed,
' the number of cols for general info, and the number of cols for temperament info
Dim tableTitle As String
If period = 1 Then
tableTitle = "Single-Period Scales for " & ed & " Equal Divisions"
Else
tableTitle = "Multi-Period Scales (period = " & period & ") for " & ed & " Equal Divisions"
End If
Set cell1 = Cells(rowIndex, colIndex)
Set cell2 = Cells(rowIndex, colIndex + ed + colsForGeneralInfo + colsForTemperamentInfo - 1)
cell1.Value = tableTitle
With ActiveSheet
.Range(cell1, cell2).HorizontalAlignment = xlCenterAcrossSelection
.Range(cell1, cell2).Borders(xlEdgeBottom).LineStyle = XlLineStyle.xlContinuous
.Range(cell1, cell2).Borders(xlEdgeTop).LineStyle = XlLineStyle.xlContinuous
.Range(cell1, cell2).Borders(xlEdgeLeft).LineStyle = XlLineStyle.xlContinuous
.Range(cell1, cell2).Borders(xlEdgeRight).LineStyle = XlLineStyle.xlContinuous
End With
' Iterate the row index so that the rest of the header can be made on the row below
rowIndex = rowIndex + 1
' Set the step pattern header
' Step pattern header starts at the row/col indices and stretches for ed cells
Set cell1 = Cells(rowIndex, colIndex)
Set cell2 = Cells(rowIndex, colIndex + ed - 1)
cell1.Value = "Step Pattern"
With ActiveSheet.Range(cell1, cell2)
.HorizontalAlignment = xlCenterAcrossSelection
.Borders(xlEdgeBottom).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeTop).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeLeft).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeRight).LineStyle = XlLineStyle.xlContinuous
.ColumnWidth = 3
End With
' Set the general info header
' General info header starts one cell from the previous header
' and currently spans colsForGeneralInfo cells
Set cell1 = Cells(rowIndex, colIndex + ed)
Set cell2 = Cells(rowIndex, colIndex + ed + colsForGeneralInfo - 1)
cell1.Value = "General Information"
With ActiveSheet.Range(cell1, cell2)
.HorizontalAlignment = xlCenterAcrossSelection
.Borders(xlEdgeBottom).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeTop).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeLeft).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeRight).LineStyle = XlLineStyle.xlContinuous
.ColumnWidth = 12
End With
' Tamnams name row requires some extra room...
cell2.ColumnWidth = 24
' Set the temperament info header
' Temperamenth header starts one cell from the previous header
' and currently spans colsForTemperamentInfo cells
Set cell1 = Cells(rowIndex, colIndex + ed + colsForGeneralInfo)
Set cell2 = Cells(rowIndex, colIndex + ed + colsForGeneralInfo + colsForTemperamentInfo - 1)
cell1.Value = "Temperament Information"
With ActiveSheet.Range(cell1, cell2)
.HorizontalAlignment = xlCenterAcrossSelection
.Borders(xlEdgeBottom).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeTop).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeLeft).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeRight).LineStyle = XlLineStyle.xlContinuous
.ColumnWidth = 24
End With
rowIndex = rowIndex + 1
' Create the tables for every possible generator pair
For i = 1 To (((ed + 1) / period) / 2)
' Create the progenitor scale
Dim progenitorScale() As Variant
Dim progenitorLargeStep As Integer
Dim progenitorSmallStep As Integer
progenitorScale = Array()
progenitorSmallStep = i
progenitorLargeStep = (ed / period) - progenitorSmallStep
For j = 0 To period - 1
Call PushToBackOfArray(progenitorScale, progenitorLargeStep)
Call PushToBackOfArray(progenitorScale, progenitorSmallStep)
Next j
' Generate the subtable for the progenitor scale
Call GenerateTableForProgenitorScale(progenitorScale, ed, period, rowIndex, colIndex)
Next i
' Iterate the row index
rowIndex = rowIndex + 1
' Move the active cell
Set cellToMoveTo = Cells(rowIndex, colIndex)
cellToMoveTo.Select
End Sub
' Helper subroutine; generates a table of mosses for a given progenitor scale
' The progenitor scale is usually a generator pair of the form 1L 1s, though it
' can also be repetitions of Ls (so nL ns). This requires a rowOffset and colOffset
' so that the subroutine can navigate the spreadsheet, and those values are mutated
' by the subroutine.
' The header consist of this information:
' - Generator pair (spans a quantity of cells represented by ed)
' - Mos, step ratio, and TAMNAMS name; note that these columns must coincide with the
' header generated by GenerateTable.
' - Scales; coincides with temperament information
Public Sub GenerateTableForProgenitorScale(ByRef progenitorScale() As Variant, ByRef ed As Integer, ByRef period As Integer, ByRef rowIndex As Integer, ByRef colIndex As Integer)
' Make a copy of the progenitor scale. This way, the progenitor scale doesn't have
' to be mutated.
Dim stepCount As Integer
stepCount = GetStepCount(progenitorScale)
Dim parentScale() As Variant
parentScale = Array()
For i = 0 To (stepCount - 1)
Call PushToBackOfArray(parentScale, progenitorScale(i))
Next i
' Get the large step and small step
' These are needed to generate the progenitor scale but are used later
' like they're iterators
Dim largeStep As Integer
Dim smallStep As Integer
largeStep = GetLargeStep(parentScale)
smallStep = GetSmallStep(parentScale)
' Create the generator pair header
Set cell1 = Cells(rowIndex, colIndex)
Set cell2 = Cells(rowIndex, colIndex + ed - 1)
cell1.Value = "Generator pair of " & largeStep & "\" & ed & " and " & smallStep & "\" & ed
With ActiveSheet.Range(cell1, cell2)
.HorizontalAlignment = xlCenterAcrossSelection
.Borders(xlEdgeBottom).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeTop).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeLeft).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeRight).LineStyle = XlLineStyle.xlContinuous
End With
' Create the headers for the columns
' There are currently four columns: Mos, Step Ratio, TAMNAMS Name, and Scale
' The first three correspond to general info, the last to temperament info
Dim headerTitles() As Variant
headerTitles = Array("Mos", "Step Ratio", "TAMNAMS Name", "Scale")
For i = 0 To 3
Set cell1 = Cells(rowIndex, colIndex + ed + i)
With cell1
.HorizontalAlignment = xlCenter
.Borders(xlEdgeBottom).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeTop).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeLeft).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeRight).LineStyle = XlLineStyle.xlContinuous
.Value = headerTitles(i)
End With
Next i
' Create the table rows on the next row
rowIndex = rowIndex + 1
Do Until largeStep = smallStep
Call GenerateTableRowForParentScale(parentScale, ed, rowIndex, colIndex)
' Get the child scale and assign it to parent scale,
' then update the large step and small step
parentScale = GetChildScale(parentScale)
largeStep = GetLargeStep(parentScale)
smallStep = GetSmallStep(parentScale)
Loop
' Make one more table row for the "equal divisions" scale
Call GenerateTableRowForParentScale(parentScale, ed, rowIndex, colIndex)
' The extra iterating here isn't necessary since the helper function iterates already
'rowIndex = rowIndex + 1
' Move the active cell
Set cellToMoveTo = Cells(rowIndex, colIndex)
cellToMoveTo.Select
End Sub
' Helper subroutine for GenerateTableForProgenitorScale
' This function mutates rowOffset and colOffset
Public Sub GenerateTableRowForParentScale(ByRef parentScale() As Variant, ByRef ed As Integer, ByRef rowIndex As Integer, ByRef colIndex As Integer)
' Center across selection across all ed cells, and add borders
Set cell1 = Cells(rowIndex, colIndex)
Set cell2 = Cells(rowIndex, colIndex + ed - 1)
With ActiveSheet.Range(cell1, cell2)
.HorizontalAlignment = xlCenterAcrossSelection
.Borders(xlEdgeBottom).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeTop).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeLeft).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeRight).LineStyle = XlLineStyle.xlContinuous
End With
' Get the large and small step sizes and step count
Dim largeStepSize As Integer
Dim smallStepSize As Integer
Dim stepCount As Integer
largeStepSize = GetLargeStep(parentScale)
smallStepSize = GetSmallStep(parentScale)
stepCount = GetStepCount(parentScale)
' Iterate through the parent scale while also iterating through the
' cells in the row. For each iteration of the parent scale, whatever
' the step size is the number of times the column iterator should iterate.
' At each iteration of the parent scale, place in the corresponding
' cell the step size. The next cell is stepSize - 1 cells away and
' contains the next element in the parent scale.
' As an example, consider the parent scale of (3, 2, 2, 2).
' The step diagram spans 9 columns, but the parent scale has 3 elements.
' On the first iteration, write a 3.
' On the second iteration, write a 2 3 cells away from the last cell,
' and so on. This operation will need a few iterators.
Dim colOffset As Integer
colOffset = 0
For i = 0 To stepCount - 1
Dim stepSize As Integer
stepSize = parentScale(i)
Set cell1 = Cells(rowIndex, colIndex + colOffset)
Set cell2 = Cells(rowIndex, colIndex + colOffset + stepSize - 1)
cell1.Value = stepSize
With ActiveSheet.Range(cell1, cell2)
.Borders(xlEdgeBottom).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeTop).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeLeft).LineStyle = XlLineStyle.xlContinuous
.Borders(xlEdgeRight).LineStyle = XlLineStyle.xlContinuous
End With
colOffset = colOffset + stepSize
Next i
' Populate the remaining rows
' The first column after the step pattern columns is the mos (formatted as xL ys)
' The second column is the step ratio, and it needs to be entered in a way that
' doesn't get misinterpreted as a time (hh:mm)
' The third column is the tamnams name, which requires a lookup table
' The fourth column is the scale, formatted as temperament[n]. This has to be
' entered in manually since different edos support different temperaments.
' Populate the mos cell
Set cell1 = Cells(rowIndex, colIndex + ed)
cell1.Value = GetMosScaleAsString(parentScale)
' Populate the step ratio cell
Set cell1 = Cells(rowIndex, colIndex + ed + 1)
cell1.Value = "=" & Chr(34) & GetStepRatioAsString(parentScale) & Chr(34)
' Populate the tamnams cell
Set cell1 = Cells(rowIndex, colIndex + ed + 2)
' To populate the tamnams cell, the step counts are needed
Dim largeStepCount As Integer
Dim smallStepCount As Integer
largeStepCount = GetLargeStepCount(parentScale)
smallStepCount = GetSmallStepCount(parentScale)
Dim tamnamsName As String
tamnamsName = GetTamnamsName(parentScale, 0)
cell1.Value = tamnamsName
rowIndex = rowIndex + 1
' Move the active cell
Set cellToMoveTo = Cells(rowIndex, colIndex)
cellToMoveTo.Select
End Sub
' https://bettersolutions.com/vba/arrays/passing-arrays.htm
' If passing ByRef, the arguments can be changed by the subroutine/function
' This function takes in a parent scale (represented as an array of two numbers)
' and produces its child scale, which has more numbers than its parent.
' Here's an example: the scale represented by 5 5 5 3 5 5 3
' - There are 7 notes and the child scale (2 3 2 3 2 3 3 2 3 2 3 3) has 12 notes.
' - The rules are as follows: for a scale with x large steps and y small steps
' in the parent scale, its child scale will have 2x+y steps; this is because
' the rules of a mos are such that the large step breaks down into the child
' scale's large and small step and the parent's small step becomes either the
' child's large step or small step.
' There are a few considerations:
' - This needs to be written differently than with C++ data structures; for one,
' I can't push_back() in VBA, so instead I have to ReDim it each time I wanna
' add a new element, but I can use ReDim Preserve
' - With the rules of mosses, there is an order in which the large step breaks down
' into the next large and small steps:
' - L and s are the parent steps and L' and s' are the child steps
' - If L - s > s (or if L > 2s), then L' = L-s and s' = s
' - If L - s < s (or if L < 2s), then L' = s and s' = L-s
' - These step orderings are inherently circular, but when represented as an array,
' they'll either be in the scale's brightest mode or darkest mode. For our purposes,
' a scale is in its brightest mode when it starts with L and ends with s, and in
' its darkest mode when it starts with s and ends with L.
' - If the parent scale is in its brightest mode and L > 2s, then every instance of L
' in the parent scale translates to L' and s', and every s translates to s'.
' - If the parent scale is in its darkest mode and L > 2s, then every instance of L
' in the parent scale translates to s' and L', and every s translates to s'.
' - If the parent scale is in its brightest mode and L < 2s, then every instance of L
' in the parent scale translates to s' and L', and every s translates to L'.
' - If the parent scale is in its darkest mode and L < 2s, then every instance of L
' in the parent scale translates to L' and s' and every s translates to L'.
' - The aforementiond rules above can be simplified as such:
' - If Not ((parent scale in brightest mode) Xor (L > 2s)) then L translates to L' and s';
' otherwise, it translates to s' and L'.
' - If L > 2s then s translates to s'; othewise, it translates to L'.
' - Without getting too technical about the rules of a mos, as long as the scale started
' out as either Ls or sL, these rules will apply without issue, and any other scale
' produced this way will always be in its brightest or darkest mode. (For multi-period
' scales, it can start as a repetition of Ls or sL but not both.)
' - Technical details:
' - Just because a scale starts with L and ends with s doesn't necessarily mean it's
' the scale's brightest mode. Diatonic ionian and lydian both start with L and end
' with s, but lydian (LLLsLLs) is the brightest mode, not ionian (LLsLLLs).
' - There can be cases where a scale can start and end with the same step; this is the
' case with diatonic dorian (LsLLLsL).
' - This is why it's important for the progenitor scale (the parent scale corresponding
' to 1L 1s) to start out as either Ls or sL; there are only two possible modes for
' the scale 1L 1s and they're both the scale's brightest and darkest modes; given the
' rules for how the child scale is generated, and as long as those rules are upheld,
' the child scale will always be in either the brightest or darkest mode.
Function GetChildScale(ByRef parentScale() As Variant) As Variant
' Get the note sizes
Dim largeStep As Integer
Dim smallStep As Integer
largeStep = GetLargeStep(parentScale)
smallStep = GetSmallStep(parentScale)
' Get the note count
' This is needed to help get the index of the last step in the scale
Dim stepCount As Integer
stepCount = GetStepCount(parentScale)
' Determine whether the scale is in its brightest or darkest mode.
' If the first step is the large step and the last the small step, it's in the brightest mode.
' If the first step is the small step and the last the large step, it's in the darkest mode.
Dim parentScaleInBrightestMode As Boolean
If parentScale(0) = largeStep And parentScale(stepCount - 1) = smallStep Then
parentScaleInBrightestMode = True
Else
parentScaleInBrightestMode = False
End If
' Determine whether the large step is larger than twice the small step.
' Alternatively, if the chroma (largeStep minus smallStep) is larger than
' the small step, this is equivalent to the large step being larger than
' twice the small step.
Dim chromaIsLargerThanSmallStep As Boolean
Dim chroma As Integer
chroma = largeStep - smallStep
If chroma > smallStep Then
chromaIsLargerThanSmallStep = True
Else
chromaIsLargerThanSmallStep = False
End If
' Calculate the child step sizes
' If the chroma is larger than the small step, then childLargeStep is the chroma
' and childSmallStep is the parent scale's small step
' If the chroma is smaller than the small step, then childLargeStep is the
' parent's small step and childSmallStep is the chroma
Dim childLargeStep As Integer
Dim childSmallStep As Integer
If chromaIsLargerThanSmallStep Then
childLargeStep = chroma
childSmallStep = smallStep
Else
childLargeStep = smallStep
childSmallStep = chroma
End If
' Create the child scale given the rules described
' Since there's no operation equivalent to push_back in VBA, the array representing
' the child scale is initialized to the number of notes it will have.
Dim childScale() As Variant
childScale = Array()
For i = 0 To stepCount - 1
If parentScale(i) = largeStep Then
If Not (parentScaleInBrightestMode Xor chromaIsLargerThanSmallStep) Then
Call PushToBackOfArray(childScale, childLargeStep)
Call PushToBackOfArray(childScale, childSmallStep)
Else
Call PushToBackOfArray(childScale, childSmallStep)
Call PushToBackOfArray(childScale, childLargeStep)
End If
ElseIf parentScale(i) = smallStep Then
If chromaIsLargerThanSmallStep Then
Call PushToBackOfArray(childScale, childSmallStep)
Else
Call PushToBackOfArray(childScale, childLargeStep)
End If
End If
Next i
GetChildScale = childScale
End Function
' Get array length
' Array length is upper bound minus lower bound plus 1
' https://www.automateexcel.com/vba/array-length-size/
Public Function GetStepCount(ByRef parentScale() As Variant) As Integer
If IsEmpty(parentScale) Then
GetStepCount = 0
Else
GetStepCount = UBound(parentScale) - LBound(parentScale) + 1
End If
End Function
' Returns the largest value in an array
Public Function GetLargeStep(ByRef parentScale() As Variant) As Integer
GetLargeStep = WorksheetFunction.Max(parentScale)
End Function
' Returns the smallest value in an array
Public Function GetSmallStep(ByRef parentScale() As Variant) As Integer
GetSmallStep = WorksheetFunction.Min(parentScale)
End Function
' Returns the number of large steps in the scale
Public Function GetLargeStepCount(ByRef parentScale() As Variant) As Integer
Dim stepCount As Integer
Dim largeStep As Integer
Dim largeStepCount As Integer
stepCount = GetStepCount(parentScale)
largeStep = GetLargeStep(parentScale)
largeStepCount = 0
For i = 0 To stepCount - 1
If parentScale(i) = largeStep Then
largeStepCount = largeStepCount + 1
End If
Next i
GetLargeStepCount = largeStepCount
End Function
' Returns the number of small steps in the scale
Public Function GetSmallStepCount(ByRef parentScale() As Variant) As Integer
Dim stepCount As Integer
Dim smallStep As Integer
Dim smallStepCount As Integer
stepCount = GetStepCount(parentScale)
smallStep = GetSmallStep(parentScale)
smallStepCount = 0
For i = 0 To stepCount - 1
If parentScale(i) = smallStep Then
smallStepCount = smallStepCount + 1
End If
Next i
GetSmallStepCount = smallStepCount
End Function
' Utility subroutine; push_back() function
' This is technically slower than C++'s push_back() for vectors; vectors have a
' hidden array that's resized exponentially whenever it runs out of room, but this
' resizes by 1 every time this is called.
Public Sub PushToBackOfArray(ByRef a() As Variant, ByRef p As Variant)
' arraySize is used as the index of the new upper bound for the array
Dim arraySize As Integer
If IsEmpty(a) Then
arraySize = 0
Else
arraySize = UBound(a) - LBound(a) + 1
End If
ReDim Preserve a(arraySize)
a(arraySize) = p
End Sub
' For debug purposes; print scale information
Public Sub PrintScaleInformation(ByRef parentScale() As Variant)
MsgBox ("Scale: " & GetMosScaleAsString(parentScale) & vbNewLine & "Step count: " & GetStepCount(parentScale) & vbNewLine & "Step ratio: " & GetStepRatioAsString(parentScale) & vbNewLine & "Scale code: " & GetScaleCodeAsString(parentScale))
End Sub
' For debug purposes; get scale (formatted as xL ys)
Public Function GetMosScaleAsString(ByRef parentScale() As Variant) As String
Dim largeStepCount As Integer
Dim smallStepCount As Integer
Dim largeStep As Integer
Dim smallStep As Integer
largeStepCount = GetLargeStepCount(parentScale)
smallStepCount = GetSmallStepCount(parentScale)
largeStep = GetLargeStep(parentScale)
smallStep = GetSmallStep(parentScale)
If largeStep = smallStep Then
GetMosScaleAsString = GetStepCount(parentScale) & "ed"
Else
GetMosScaleAsString = largeStepCount & "L " & smallStepCount & "s"
End If
End Function
' For debug purposes; get step ratio
Public Function GetStepRatioAsString(ByRef parentScale() As Variant) As String
Dim largeStep As Integer
Dim smallStep As Integer
largeStep = GetLargeStep(parentScale)
smallStep = GetSmallStep(parentScale)
If largeStep = smallStep Then
GetStepRatioAsString = largeStep
Else
GetStepRatioAsString = largeStep & ":" & smallStep
End If
End Function
' For debug purposes; get scale code (as a string of L's and s's)
Public Function GetScaleCodeAsString(ByRef parentScale() As Variant) As String
Dim stepCount As Integer
Dim largeStep As Integer
Dim smallStep As Integer
largeStep = GetLargeStep(parentScale)
smallStep = GetSmallStep(parentScale)
stepCount = GetStepCount(parentScale)
Dim scaleCode As String
For i = 0 To stepCount - 1
If largeStep = smallStep Then
scaleCode = scaleCode & "u"
ElseIf parentScale(i) = smallStep Then
scaleCode = scaleCode & "s"
ElseIf parentScale(i) = largeStep Then
scaleCode = scaleCode & "L"
End If
Next i
GetScaleCodeAsString = scaleCode
End Function
' This is a lookup table for Tamnams names
' This also includes a few extended names, mainly "protic/prototonic" and "deuteric/deuterotonic"
' but those names can be configured
' - 2: Use all extended names
' - 1: tetric, kleistonic, and hexawood only
' - 0 and any other value: tamnams names only
Public Function GetTamnamsName(ByRef parentScale() As Variant, ByRef useExtendedNames As Integer) As String
' Rows of the lookup table
Dim lookupTable02() As Variant
Dim lookupTable03() As Variant
Dim lookupTable04() As Variant
Dim lookupTable05() As Variant
Dim lookupTable06() As Variant
Dim lookupTable07() As Variant
Dim lookupTable08() As Variant
Dim lookupTable09() As Variant
Dim lookupTable10() As Variant
Dim lookupTable11() As Variant
Dim lookupTable12() As Variant
' Extended tamnams names are based on existing names wherever possible
' - All 1L ns scales are named based on the nL 1s scale name with the anti- prefix added
' - prototonic/protic is used in referenc that 1L 1s is the progenitor scale for all possible (single-period) scales; can also be called monowood
' - In following similar logic, protic only has two possible children, named deuterotonic/deuteric and antideuterotonic/antideuteric
' - tetric is used to refer to 3L 1s the same way 2L 3s refers to pentic; antipentic follows as a 1L ns scale
' - diwood (2L 2s) is named in reference to the n-wood scales (nL ns)
' - kleistonic (4L 7s) appears on the wiki but not on the tamnams page
' - Many 12-note mosses are named as extensions of either 2-note (hexa-), 4-note (tri-), 6-note (di-), or 8-note (sesqui-) scales
If useExtendedNames = 2 Then
lookupTable02 = Array("protic; monowood")
lookupTable03 = Array("antideuteric", "deuteric")
lookupTable04 = Array("antitetric", "diwood", "tetric")
lookupTable05 = Array("antimanic", "pentic", "antipentic", "manic")
lookupTable06 = Array("antimachinoid", "antilemon", "triwood", "lemon", "machinoid")
lookupTable07 = Array("anti-archeotonic", "antidiatonic", "mosh", "smitonic", "diatonic", "archeotonic")
lookupTable08 = Array("antipine", "antiechidnoid", "sensoid", "tetrawood; diminished", "oneirotonic", "echidnoid", "pine")
lookupTable09 = Array("antisubneutralic", "joanatonic", "tcherepnin", "orwelloid", "semiquartal", "hyrulic", "superdiatonic", "subneutralic")
lookupTable10 = Array("antisinatonic", "antidimanic", "sephiroid", "antidipentic", "pentawood", "dipentic", "dicotonic", "dimanic", "sinatonic")
lookupTable11 = Array("", "", "", "kleistonic", "", "", "", "", "", "")
lookupTable12 = Array("", "antidimachinoid", "antitritetric, antisesqui-echidnoid", "antidilemon", "p-chromatic", "hexawood", "m-chromatic", "dilemon", "tritetric, sesqui-echidnoid", "dimachinoid", "")
ElseIf useExtendedNames = 1 Then
lookupTable02 = Array("")
lookupTable03 = Array("", "")
lookupTable04 = Array("", "diwood", "tetric")
lookupTable05 = Array("", "pentic", "antipentic", "manic")
lookupTable06 = Array("", "antilemon", "triwood", "lemon", "machinoid")
lookupTable07 = Array("", "antidiatonic", "mosh", "smitonic", "diatonic", "archeotonic")
lookupTable08 = Array("", "antiechidnoid", "sensoid", "tetrawood; diminished", "oneirotonic", "echidnoid", "pine")
lookupTable09 = Array("", "joanatonic", "tcherepnin", "orwelloid", "semiquartal", "hyrulic", "superdiatonic", "subneutralic")
lookupTable10 = Array("", "antidimanic", "sephiroid", "antidipentic", "pentawood", "dipentic", "dicotonic", "dimanic", "sinatonic")
lookupTable11 = Array("", "", "", "kleistonic", "", "", "", "", "", "")
lookupTable12 = Array("", "", "", "", "p-chromatic", "hexawood", "m-chromatic", "", "", "", "")
Else
lookupTable02 = Array("")
lookupTable03 = Array("", "")
lookupTable04 = Array("", "", "")
lookupTable05 = Array("", "pentic", "antipentic", "manic")
lookupTable06 = Array("", "antilemon", "triwood", "lemon", "machinoid")
lookupTable07 = Array("", "antidiatonic", "mosh", "smitonic", "diatonic", "archeotonic")
lookupTable08 = Array("", "antiechidnoid", "sensoid", "tetrawood; diminished", "oneirotonic", "echidnoid", "pine")
lookupTable09 = Array("", "joanatonic", "tcherepnin", "orwelloid", "semiquartal", "hyrulic", "superdiatonic", "subneutralic")
lookupTable10 = Array("", "antidimanic", "sephiroid", "antidipentic", "pentawood", "dipentic", "dicotonic", "dimanic", "sinatonic")
lookupTable11 = Array("", "", "", "", "", "", "", "", "", "")
lookupTable12 = Array("", "", "", "", "p-chromatic", "", "m-chromatic", "", "", "", "")
End If
' Calculate the large step count and note count; that's all is needed
Dim largeStepCount As Integer
Dim noteCount As Integer
largeStepCount = GetLargeStepCount(parentScale)
noteCount = GetStepCount(parentScale)
' If the large step and small steps are the same sizes, the scale doesn't
' get a tamnams name, since the steps are the same size (and technically,
' there'd be no distinction between xL ys and xL xs)
Dim largeStep As Integer
Dim smallStep As Integer
largeStep = GetLargeStep(parentScale)
smallStep = GetSmallStep(parentScale)
' Use the large step as an indexer for the row of the lookup table
Dim rowIndexer As Integer
rowIndexer = largeStepCount - 1
If (largeStep = smallStep) Then
GetTamnamsName = ""
ElseIf noteCount = 2 Then
GetTamnamsName = lookupTable02(rowIndexer)
ElseIf noteCount = 3 Then
GetTamnamsName = lookupTable03(rowIndexer)
ElseIf noteCount = 4 Then
GetTamnamsName = lookupTable04(rowIndexer)
ElseIf noteCount = 5 Then
GetTamnamsName = lookupTable05(rowIndexer)
ElseIf noteCount = 6 Then
GetTamnamsName = lookupTable06(rowIndexer)
ElseIf noteCount = 7 Then
GetTamnamsName = lookupTable07(rowIndexer)
ElseIf noteCount = 8 Then
GetTamnamsName = lookupTable08(rowIndexer)
ElseIf noteCount = 9 Then
GetTamnamsName = lookupTable09(rowIndexer)
ElseIf noteCount = 10 Then
GetTamnamsName = lookupTable10(rowIndexer)
ElseIf noteCount = 11 Then
GetTamnamsName = lookupTable11(rowIndexer)
ElseIf noteCount = 12 Then
GetTamnamsName = lookupTable12(rowIndexer)
Else
GetTamnamsName = ""
End If
End Function
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment