-
-
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)
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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