Skip to content

Commit

Permalink
Use only a single source of truth for base frequency
Browse files Browse the repository at this point in the history
Do the same with base MIDI note.

ref #658
  • Loading branch information
frostburn committed Apr 18, 2024
1 parent 885d1ac commit aa10774
Show file tree
Hide file tree
Showing 23 changed files with 40 additions and 56 deletions.
2 changes: 1 addition & 1 deletion CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
* Feature: Implement multi-channel MIDI mode compatible with the Lumatone [#649](https://github.com/xenharmonic-devs/scale-workshop/pull/649)
* Bug fix: Extreme ratios now only break parts of the tuning table that do not have IEEE floating point representation and format better when non-finite [#631](https://github.com/xenharmonic-devs/scale-workshop/issues/631), [#632](https://github.com/xenharmonic-devs/scale-workshop/issues/632)
* Style fix: Make checkbox and radio button labels more consistent [#644](https://github.com/xenharmonic-devs/scale-workshop/issues/644)
* Beta cycle issues: [#643](https://github.com/xenharmonic-devs/scale-workshop/issues/643), [#640](https://github.com/xenharmonic-devs/scale-workshop/issues/640), [#577](https://github.com/xenharmonic-devs/scale-workshop/issues/577), [#513](https://github.com/xenharmonic-devs/scale-workshop/issues/513)
* Beta cycle issues: [#643](https://github.com/xenharmonic-devs/scale-workshop/issues/643), [#640](https://github.com/xenharmonic-devs/scale-workshop/issues/640), [#577](https://github.com/xenharmonic-devs/scale-workshop/issues/577), [#513](https://github.com/xenharmonic-devs/scale-workshop/issues/513), [#658](https://github.com/xenharmonic-devs/scale-workshop/issues/658)
* Alpha cycle issues: [#574](https://github.com/xenharmonic-devs/scale-workshop/issues/574), [#579](https://github.com/xenharmonic-devs/scale-workshop/issues/579)

## 2.4.1
Expand Down
6 changes: 3 additions & 3 deletions src/App.vue
Original file line number Diff line number Diff line change
Expand Up @@ -310,7 +310,7 @@ function typingKeydown(event: CoordinateKeyboardEvent) {
return emptyKeyup
}
let index = scale.baseMidiNote + scale.scale.size * scale.equaveShift + scale.degreeShift
let index = scale.scale.baseMidiNote + scale.scale.size * scale.equaveShift + scale.degreeShift
if (scale.keyboardMode === 'isomorphic') {
index += x * state.isomorphicHorizontal + (2 - y) * state.isomorphicVertical
Expand Down Expand Up @@ -353,7 +353,7 @@ onMounted(() => {
const scaleWorkshopOneData = new ScaleWorkshopOneData()
scale.name = scaleWorkshopOneData.name
scale.baseFrequency = scaleWorkshopOneData.freq
scale.userBaseFrequency = scaleWorkshopOneData.freq
scale.autoFrequency = false
scale.baseMidiNote = scaleWorkshopOneData.midi
state.isomorphicHorizontal = scaleWorkshopOneData.horizontal
Expand Down Expand Up @@ -391,7 +391,7 @@ onMounted(() => {
}
scale.name = decodedState.scaleName
scale.baseFrequency = decodedState.baseFrequency
scale.userBaseFrequency = decodedState.baseFrequency
scale.autoFrequency = false
scale.baseMidiNote = decodedState.baseMidiNote
state.isomorphicHorizontal = decodedState.isomorphicHorizontal
Expand Down
8 changes: 0 additions & 8 deletions src/components/ExporterButtons.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,8 +46,6 @@ function doExport(exporter: ExporterKey) {
scaleUrl: window.location.href,
filename: sanitizeFilename(scale.name),
relativeIntervals: scale.relativeIntervals,
baseFrequency: scale.baseFrequency,
baseMidiNote: scale.baseMidiNote,
scale: scale.scale,
labels: scale.labels,
midiOctaveOffset: -1,
Expand All @@ -68,8 +66,6 @@ function doExport(exporter: ExporterKey) {
@cancel="showKorgExportModal = false"
:newline="state.newline"
:scaleName="scale.name"
:baseMidiNote="scale.baseMidiNote"
:baseFrequency="scale.baseFrequency"
:relativeIntervals="scale.relativeIntervals"
:midiOctaveOffset="-1"
:scale="scale.scale"
Expand All @@ -82,8 +78,6 @@ function doExport(exporter: ExporterKey) {
@cancel="showMtsSysexExportModal = false"
:newline="state.newline"
:scaleName="scale.name"
:baseMidiNote="scale.baseMidiNote"
:baseFrequency="scale.baseFrequency"
:relativeIntervals="scale.relativeIntervals"
:midiOctaveOffset="-1"
:scale="scale.scale"
Expand All @@ -96,8 +90,6 @@ function doExport(exporter: ExporterKey) {
@cancel="showReaperExportModal = false"
:newline="state.newline"
:scaleName="scale.name"
:baseMidiNote="scale.baseMidiNote"
:baseFrequency="scale.baseFrequency"
:relativeIntervals="scale.relativeIntervals"
:midiOctaveOffset="-1"
:scale="scale.scale"
Expand Down
2 changes: 1 addition & 1 deletion src/components/NewScale.vue
Original file line number Diff line number Diff line change
Expand Up @@ -120,7 +120,7 @@ function blur() {
}
function updateBaseFrequency(value: number) {
scale.baseFrequency = value
scale.userBaseFrequency = value
scale.autoFrequency = false
}
Expand Down
2 changes: 1 addition & 1 deletion src/components/ScaleControls.vue
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ defineExpose({ focus, clearPaletteInfo })
id="base-frequency"
type="number"
step="any"
v-model="scale.baseFrequency"
v-model="scale.baseFrequencyDisplay"
:disabled="scale.autoFrequency"
@input="updateScale"
/>
Expand Down
4 changes: 0 additions & 4 deletions src/components/modals/export/KorgExport.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import type { Scale } from '@/scale'
const props = defineProps<{
newline: string
scaleName: string
baseMidiNote: number
baseFrequency: number
midiOctaveOffset: number
relativeIntervals: Interval[]
labels: string[]
Expand Down Expand Up @@ -50,9 +48,7 @@ async function doExport() {
scale: props.scale,
relativeIntervals: props.relativeIntervals,
labels: props.labels,
baseFrequency: props.baseFrequency,
filename: sanitizeFilename(props.scaleName),
baseMidiNote: props.baseMidiNote,
midiOctaveOffset: props.midiOctaveOffset
}
Expand Down
4 changes: 0 additions & 4 deletions src/components/modals/export/MtsSysexExport.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import type { Interval } from 'sonic-weave'
const props = defineProps<{
newline: string
scaleName: string
baseMidiNote: number
baseFrequency: number
midiOctaveOffset: number
relativeIntervals: Interval[]
scale: Scale
Expand Down Expand Up @@ -58,8 +56,6 @@ function doExport() {
newline: props.newline,
scale: props.scale,
filename: sanitizeFilename(props.scaleName),
baseMidiNote: props.baseMidiNote,
baseFrequency: props.baseFrequency,
relativeIntervals: props.relativeIntervals,
midiOctaveOffset: props.midiOctaveOffset,
labels: props.labels,
Expand Down
4 changes: 0 additions & 4 deletions src/components/modals/export/ReaperExport.vue
Original file line number Diff line number Diff line change
Expand Up @@ -10,8 +10,6 @@ import type { Interval } from 'sonic-weave'
const props = defineProps<{
newline: string
scaleName: string
baseMidiNote: number
baseFrequency: number
midiOctaveOffset: number
relativeIntervals: Interval[]
scale: Scale
Expand Down Expand Up @@ -39,8 +37,6 @@ function doExport() {
newline: props.newline,
scale: props.scale,
filename: sanitizeFilename(props.scaleName),
baseMidiNote: props.baseMidiNote,
baseFrequency: props.baseFrequency,
midiOctaveOffset: props.midiOctaveOffset,
relativeIntervals: props.relativeIntervals,
labels: props.labels,
Expand Down
2 changes: 0 additions & 2 deletions src/exporters/__tests__/test-data.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,8 +78,6 @@ export function getTestData(appTitle: string) {
scale,
appTitle,
description: 'A scale for testing if the exporter works',
baseMidiNote: 69,
baseFrequency: 440,
midiOctaveOffset: 0,
sourceText: '100.\nC5_5\n4\\5\n5/3\n1,3591409142295225r\n3486784401/3276800000\n2/1',
labels: ['100.', 'C5_5', '4\\5', '5/3', '1,3591409142295225r', '3486784401/3276800000', '2/1'],
Expand Down
4 changes: 2 additions & 2 deletions src/exporters/ableton.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default class AbletonAsclExporter extends BaseExporter {
const newline = this.params.newline
const intervals = this.params.relativeIntervals
const labels = this.params.labels
const referenceFrequency = this.params.baseFrequency.toFixed(8)
const referenceFrequency = this.params.scale.baseFrequency.toFixed(8)
// assemble the .ascl file contents
let file = '! ' + this.params.filename + '.ascl' + newline
file += '! Created using ' + this.appTitle + newline
Expand Down Expand Up @@ -59,7 +59,7 @@ export default class AbletonAsclExporter extends BaseExporter {
}

// It's unclear what "octave number" means in the spec
const octave = Math.floor(ftom(this.params.baseFrequency)[0] / 12) - 1
const octave = Math.floor(ftom(this.params.scale.baseFrequency)[0] / 12) - 1

file += '!' + newline
file += '! @ABL NOTE_NAMES ' + names.join(' ') + newline
Expand Down
3 changes: 1 addition & 2 deletions src/exporters/anamark.ts
Original file line number Diff line number Diff line change
Expand Up @@ -106,8 +106,7 @@ class AnaMarkExporter extends BaseExporter {
newline +
'; Set reference key to absolute frequency (not scale note but midi key)' +
newline
file +=
'note ' + this.params.baseMidiNote + '="! ' + scale.baseFrequency.toFixed(6) + '"' + newline
file += 'note ' + scale.baseMidiNote + '="! ' + scale.baseFrequency.toFixed(6) + '"' + newline
}

file += newline + '[Scale End]' + newline
Expand Down
2 changes: 0 additions & 2 deletions src/exporters/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,6 @@ export type ExporterParams = {
newline: string
filename: string
relativeIntervals: Interval[]
baseFrequency: number
baseMidiNote: number
midiOctaveOffset: number
scale: Scale
labels: string[]
Expand Down
2 changes: 1 addition & 1 deletion src/exporters/image-line.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ class ImageLineExporter extends BaseExporter {
getFileContents(range: number) {
const scale = this.params.scale

const baseFreqOffset = Math.log2(this.params.baseFrequency / 440) // in number of octaves
const baseFreqOffset = Math.log2(this.params.scale.baseFrequency / 440) // in number of octaves

// construct point data
const points = new ArrayBuffer(121 * 24)
Expand Down
4 changes: 2 additions & 2 deletions src/exporters/kontakt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ export default class KontaktExporter extends BaseExporter {

getFileContents() {
const newline = this.params.newline
const baseMidiNote = this.params.baseMidiNote
const baseMidiNote = this.params.scale.baseMidiNote

// assemble the kontakt script contents
let file = '{**************************************' + newline
Expand All @@ -30,7 +30,7 @@ export default class KontaktExporter extends BaseExporter {
' (' +
midiNoteNumberToName(baseMidiNote, this.params.midiOctaveOffset) +
') = ' +
this.params.baseFrequency.toString() +
this.params.scale.baseFrequency.toString() +
' Hz' +
newline
file += 'Created using ' + this.appTitle + newline + newline
Expand Down
4 changes: 2 additions & 2 deletions src/exporters/korg.ts
Original file line number Diff line number Diff line change
Expand Up @@ -158,12 +158,12 @@ export class KorgExporter extends BaseExporter {

getFileContents(): [JSZip, string] {
const scale = this.params.scale
const baseMidiNote = this.params.baseMidiNote
const baseMidiNote = scale.baseMidiNote

let frequencies: number[]
if (this.useOctaveFormat) {
const rootFreq = mtof(0)
const transposeRatio = rootFreq / this.params.baseFrequency
const transposeRatio = rootFreq / scale.baseFrequency
frequencies = scale
.getFrequencyRange(baseMidiNote, baseMidiNote + OCTAVE_FORMAT_SIZE)
.map((f: number) => f * transposeRatio)
Expand Down
4 changes: 2 additions & 2 deletions src/exporters/reaper.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export default class ReaperExporter extends BaseExporter {
const digits = ReaperExporter.fractionDigits
const scale = this.params.scale
const labels = this.params.labels
const baseFrequency = this.params.baseFrequency
const baseFrequency = scale.baseFrequency
const format = this.params.format
const basePeriod = this.params.basePeriod || 0
const baseDegree = this.params.baseDegree || 0
Expand All @@ -27,7 +27,7 @@ export default class ReaperExporter extends BaseExporter {
for (let i = ReaperExporter.tuningMaxSize - 1; i >= 0; i--) {
file += i.toString() + ' '

let index = i - this.params.baseMidiNote
let index = i - scale.baseMidiNote
const period = basePeriod + Math.floor(index / scale.size)
if (modBySize) {
index = mmod(index, scale.size)
Expand Down
7 changes: 4 additions & 3 deletions src/exporters/scala.ts
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,8 @@ export class ScalaKbmExporter extends BaseExporter {
getFileContents() {
const newline = this.params.newline
const intervals = this.params.relativeIntervals
const baseFrequency = this.params.baseFrequency
const baseFrequency = this.params.scale.baseFrequency
const baseMidiNote = this.params.scale.baseMidiNote
// assemble the .kbm file contents
let file = '! Template for a keyboard mapping' + newline
file += '!' + newline
Expand All @@ -74,9 +75,9 @@ export class ScalaKbmExporter extends BaseExporter {
file += '! Last MIDI note number to retune:' + newline
file += '127' + newline
file += '! Middle note where the first entry of the mapping is mapped to:' + newline
file += this.params.baseMidiNote.toString() + newline
file += baseMidiNote.toString() + newline
file += '! Reference note for which frequency is given:' + newline
file += this.params.baseMidiNote.toString() + newline
file += baseMidiNote.toString() + newline
file += '! Frequency to tune the above note to' + newline
file += baseFrequency.toString() + newline
file += '! Scale degree to consider as formal octave (determines difference in pitch' + newline
Expand Down
16 changes: 12 additions & 4 deletions src/stores/scale.ts
Original file line number Diff line number Diff line change
Expand Up @@ -64,22 +64,30 @@ export const useScaleStore = defineStore('scale', () => {
const gas = ref(parseInt(localStorage.getItem('gas') ?? '10000', 10))

const name = ref('')
// For the v-model. Consider stores.scale.scale.baseMidiNote the source of truth.
const baseMidiNote = ref(60)

// The concept of base frequency is a little confusing so here's an explanation:
// The user can either set the base frequency or have it be automatically calculated.
// baseFrequencyDisplay reflects the initial value passed to the SonicWeave runtime.
// However the runtime may assign a different unison frequency and that's what ends up in scale.value.baseFrequency.
// Threrefore stores.scale.scale.baseFrequency is the source of truth, while stores.scale.baseFrequencyDisplay is the v-model.
const userBaseFrequency = ref(261.63)
const autoFrequency = ref(true)
const baseFrequency = computed({
const baseFrequencyDisplay = computed({
get() {
return autoFrequency.value ? mtof(baseMidiNote.value) : userBaseFrequency.value
},
set(value: number) {
userBaseFrequency.value = value
}
})
// XXX: baseFrequencyDisplay is merely used for convenience here. This is the last time there's a direct connection.
const scale = ref(new Scale(TET12, baseFrequencyDisplay.value, baseMidiNote.value))
const autoColors = ref<'silver' | 'cents' | 'factors'>('silver')
const sourceText = ref('')
const relativeIntervals = ref(INTERVALS_12TET)
const latticeIntervals = ref(INTERVALS_12TET)
const scale = ref(new Scale(TET12, baseFrequency.value, baseMidiNote.value))
const colors = ref(defaultColors(baseMidiNote.value))
const labels = ref(defaultLabels(baseMidiNote.value, accidentalPreference.value))
const error = ref('')
Expand Down Expand Up @@ -302,7 +310,7 @@ export const useScaleStore = defineStore('scale', () => {
// Inject global variables
const _ = Interval.fromInteger(baseMidiNote.value)
const baseFreq = new Interval(
TimeMonzo.fromFractionalFrequency(new Fraction(baseFrequency.value).simplify(1e-8)),
TimeMonzo.fromFractionalFrequency(new Fraction(baseFrequencyDisplay.value).simplify(1e-8)),
'linear'
)
const extraBuiltins: Record<string, SonicWeaveValue> = {
Expand Down Expand Up @@ -433,7 +441,7 @@ export const useScaleStore = defineStore('scale', () => {
userBaseFrequency,
autoFrequency,
autoColors,
baseFrequency,
baseFrequencyDisplay,
sourcePrefix,
sourceText,
scale,
Expand Down
2 changes: 1 addition & 1 deletion src/views/AnalysisView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ const heldScaleDegrees = computed(() => {
const result: Set<number> = new Set()
for (const midiIndex of state.heldNotes.keys()) {
if (state.heldNotes.get(midiIndex)! > 0) {
result.add(mmod(midiIndex - scale.baseMidiNote, scale.scale.size))
result.add(mmod(midiIndex - scale.scale.baseMidiNote, scale.scale.size))
}
}
return result
Expand Down
2 changes: 1 addition & 1 deletion src/views/LatticeView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ const heldNotes = computed(() => {
for (const midiIndex of state.heldNotes.keys()) {
if (state.heldNotes.get(midiIndex)! > 0) {
// Offset by 1 to match relativeIntervals
result.add(perm[mmod(midiIndex - scale.baseMidiNote - 1, scale.scale.size)])
result.add(perm[mmod(midiIndex - scale.scale.baseMidiNote - 1, scale.scale.size)])
}
}
return result
Expand Down
4 changes: 2 additions & 2 deletions src/views/ScaleView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ onMounted(() => {
:heldNotes="state.heldNotes"
:frequencies="scale.frequencies"
:centss="scale.centss"
:baseFrequency="scale.baseFrequency"
:baseMidiNote="scale.baseMidiNote"
:baseFrequency="scale.scale.baseFrequency"
:baseMidiNote="scale.scale.baseMidiNote"
:colors="scale.colors"
:labels="scale.labels"
/>
Expand Down
6 changes: 3 additions & 3 deletions src/views/VirtualKeyboardView.vue
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ const state = useStateStore()
const scale = useScaleStore()
const baseIndex = computed(
() => scale.baseMidiNote + scale.equaveShift * scale.scale.size + scale.degreeShift
() => scale.scale.baseMidiNote + scale.equaveShift * scale.scale.size + scale.degreeShift
)
type NoteOff = () => void
Expand All @@ -25,7 +25,7 @@ type NoteOnCallback = (index: number) => NoteOff
<VirtualPiano
v-if="scale.keyboardMode === 'piano'"
:baseIndex="baseIndex"
:baseMidiNote="scale.baseMidiNote"
:baseMidiNote="scale.scale.baseMidiNote"
:colorMap="scale.colorForIndex"
:splitAccidentals="scale.splitAccidentals"
:accidentalColor="scale.accidentalColor"
Expand All @@ -38,7 +38,7 @@ type NoteOnCallback = (index: number) => NoteOff
<VirtualKeyboard
v-else
:baseIndex="baseIndex"
:baseMidiNote="scale.baseMidiNote"
:baseMidiNote="scale.scale.baseMidiNote"
:isomorphicHorizontal="state.isomorphicHorizontal"
:isomorphicVertical="state.isomorphicVertical"
:keyColors="scale.colors"
Expand Down
2 changes: 1 addition & 1 deletion src/views/VirtualQwerty.vue
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ defineProps<{
}>()
const baseIndex = computed(
() => scale.baseMidiNote + scale.equaveShift * scale.scale.size + scale.degreeShift
() => scale.scale.baseMidiNote + scale.equaveShift * scale.scale.size + scale.degreeShift
)
</script>

Expand Down

0 comments on commit aa10774

Please sign in to comment.