From dfd08d55f844d793b8a982c3ed75af5f77b6875a Mon Sep 17 00:00:00 2001
From: Max Alyokhin <55048182+MaxAlyokhin@users.noreply.github.com>
Date: Tue, 12 Sep 2023 00:01:40 +0300
Subject: [PATCH] translate comments and bugfixes
---
README.md | 4 +
README_RU.md | 53 +++++++----
dist/index.html | 6 +-
src/BinarySynth.vue | 17 ++++
src/assets/js/getMIDINote.js | 14 +--
src/assets/js/helpers.js | 6 +-
src/components/ControlPanel.vue | 102 +++++++++++-----------
src/components/ControlPanel/Frequency.vue | 2 +-
src/components/ControlPanel/Global.vue | 2 +-
src/components/FileInput.vue | 38 +++++++-
src/components/Status.vue | 22 ++---
11 files changed, 168 insertions(+), 98 deletions(-)
diff --git a/README.md b/README.md
index 3b45e6e..77813fe 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,12 @@
_Binary file interpreter for audio synthesis_
+[![Uptime Robot status](https://img.shields.io/uptimerobot/status/m795264551-bb4c959b31b6ff94b02f9545)](https://bs.stranno.su) [![Uptime Robot status](https://img.shields.io/uptimerobot/ratio/m795264551-bb4c959b31b6ff94b02f9545)](https://bs.stranno.su)
+
**Demo**: https://bs.stranno.su
+![](https://store.stranno.su/bs/design.png)
+
_Эта страница есть также на русском_
All data on any computer or smartphone is in the form of files. The contents of these files are ultimately just zeros and ones. And these zeros and ones are basically all the same, so we need an interpreter to extract meaning from these texts. Basically, the file format (.mp3, .docx, etc.) is just a pointer to which interpreter we need to pass the text in order to extract meaning from it.
diff --git a/README_RU.md b/README_RU.md
index 4ff86da..74c6b3f 100644
--- a/README_RU.md
+++ b/README_RU.md
@@ -2,8 +2,12 @@
_Интерпретатор двоичных файлов для аудио-синтеза_
+[![Uptime Robot status](https://img.shields.io/uptimerobot/status/m795264551-bb4c959b31b6ff94b02f9545)](https://bs.stranno.su) [![Uptime Robot status](https://img.shields.io/uptimerobot/ratio/m795264551-bb4c959b31b6ff94b02f9545)](https://bs.stranno.su)
+
**Демо**: https://bs.stranno.su
+![](https://store.stranno.su/bs/design.png)
+
Все данные на любом компьютере или смартфоне представлены в виде файлов. Содержанием этих файлов в конечном итоге являются просто нули и единицы. И эти нули и единицы, в общем-то, все одинаковые, поэтому нам нужен интерпретатор, для того чтобы извлечь смысл из этих текстов. По сути, формат файла (.mp3, .docx и т.д.) это просто указатель, какому интерпретатору надо передать текст, чтобы из него извлечь смысл.
Но что, если формат файла и интерпретатор не совпадают? Что касается музыкальных экспериментов, то ранее были, например, попытки "воспроизвести" текстовый или иной файл через аудио-редактор, что ожидаемо рождало в результате в основном глитч и нойз; это может быть интересно больше с концептуальной, чем с музыкальной точки зрения.
@@ -22,7 +26,8 @@ _Интерпретатор двоичных файлов для аудио-си
3. На уровне всей системы мы задаём глобальные параметры:
- скорость интерпретации
-- музыкальный строй (или его отсутствие), диапазон нот/частот
+- наличие случайной величины разброса скорости интерпретации
+- музыкальный строй (или его отсутствие), диапазон нот/частот; по этому диапазону равномерно сопоставляются частоты по 256 или 65 536 возможным комбинациям нулей и единиц
- зацикленность воспроизведения
- режим MIDI
- плавный или резкий переход между командами
@@ -34,27 +39,19 @@ _Интерпретатор двоичных файлов для аудио-си
6. Если дошли до конца файла, прекращаем исполнение, либо начинаем заново
-## Запуск локально и сборка проекта
-
-### Просто скопировать приложение
-
-Всё необходимое для работы системы заложено в единственный `.html` файл, который можно скачать в папке `dist`, либо просто перейти на https://bs.strannо.su и, нажав правую кнопку мыши, в меню выбрать Сохранить как.
+## MIDI
-### Собрать билд локально для доработки кода
+При включении MIDI-режима автоматически выбирается первый попавшийся порт из доступных и его первый канал. Далее последовательно при чтении посылается сигнал noteOn, через время Reading speed посылается сигнал noteOff. В Continuous режиме после каждого noteOn посылается Pitch сигнал, чтобы попасть в нужную частоту.
-Tech stack: Vue3 + Pinia + Vite.
+MIDI-сообщения могут посылаться:
-1. Скачать и установить LTS версию Node.js
-2. Скачать код напрямую с Github, либо через `git clone`
-3. В папке с проектом в терминале выполнить:
+- в соседние вкладки и окна браузеров, если они слушают MIDI (например, в веб-аналог [DX7](http://mmontag.github.io/dx7-synth-js))
+- в DAW и прочие приложения, где есть виртуальные синтезаторы (то есть BS может управлять, например, синтезатором в Ableton)
+- во внешние устройства, поддерживающие MIDI и подключённые к компьютеру
-```bash
-npm i
-npm run dev # development-сборка
-npm run build # production-сборка, генерирует index.html со всем необходимым
-```
+> **Note**: После любых манипуляций в MIDI-портами (подключение/отключение/переподключение) необходимо полностью перезапустить браузер, закрыв все окна браузера если их несколько
-Для тестов MIDI можно пользоваться этим ресурсом https://studiocode.dev/midi-monitor/
+> **Note**: MIDI-сообщения генерируются только на десктопе
## Особенности интерфейса
@@ -76,3 +73,25 @@ npm run build # production-сборка, генерирует index.html со в
- Random time gap - добавление случайной величины времени до следующего звука в пределах параметра Reading speed. Делает звук менее "роботизированным", так как расстояние до каждого звука немного отличается и это добавляет больше "живости" игре
- Commands range - позволяет играть не весь файл, а его определённую часть
+
+## Запуск локально и сборка проекта
+
+### Просто скопировать приложение
+
+Всё необходимое для работы системы заложено в единственный `.html` файл, который можно скачать в папке `dist`, либо просто перейти на https://bs.strannо.su и, нажав правую кнопку мыши, в меню выбрать Сохранить как.
+
+### Собрать билд локально для доработки кода
+
+Tech stack: Vue3 + Pinia + Vite.
+
+1. Скачать и установить LTS версию Node.js
+2. Скачать код напрямую с Github, либо через `git clone`
+3. В папке с проектом в терминале выполнить:
+
+```bash
+npm i
+npm run dev # development-сборка
+npm run build # production-сборка, генерирует index.html со всем необходимым
+```
+
+Для тестов MIDI можно пользоваться этим ресурсом https://studiocode.dev/midi-monitor/
diff --git a/dist/index.html b/dist/index.html
index f04685e..bd37788 100644
--- a/dist/index.html
+++ b/dist/index.html
@@ -35,15 +35,15 @@
href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAMAAAAoLQ9TAAAABGdBTUEAALGPC/xhBQAAACBjSFJNAAB6JgAAgIQAAPoAAACA6AAAdTAAAOpgAAA6mAAAF3CculE8AAABCFBMVEUAAAANERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcNERcLDxUKDhQMEBY/REdobm9YXl8dISYiJytkamtiaGkgJCl4fn6EiomLkZBqcHFyeXmIj453fX1NU1WSmZeVnJpVWlxhZ2ico6B8goJ6gIBzeXkkKC1QVlhtc3MLDxaLkpBwdnZjaGl+hISPlZRqcHA2Oj5LUFMZHSMfIyhWXF5SV1kaHiT///9VYDZ7AAAAKnRSTlMAAAZHpuP75KhJBxiR7u+TGbCyBZRI7Uuq4uX6/P3mp6tNlrQa8ErnTAjc5selAAAAAWJLR0RXfQrZHwAAAAd0SU1FB+cJBRUNJ/cSuWQAAADLSURBVBjTVY95c8EAEMV304hKiCCOqriPdomradUZtLRuqnz/j2Ix43gz+8f7zdsLAFB4EB2S5Hx0yQhsUXF76CTVqyECKj66yK8hBNx0I12AYIioWCqZJheRJwwikVmuVGv1N+udSQSeOPDR+Gy22p1ukSgKzww6Pbs/GFp1TsRAOoKv79H4x/6dEBngOIJpezZfLFcM4pDgoevq5m/7v9tzSxJcd2vVFAje28PSMqDmv/pMln9BTVfPNpfO4ulfOR+JG0Yh+fLK9gCSyCS/pajeSAAAACV0RVh0ZGF0ZTpjcmVhdGUAMjAyMy0wOS0wNVQyMToxMzozOSswMDowMJyosKIAAAAldEVYdGRhdGU6bW9kaWZ5ADIwMjMtMDktMDVUMjE6MTM6MzkrMDA6MDDt9QgeAAAAV3pUWHRSYXcgcHJvZmlsZSB0eXBlIGlwdGMAAHic4/IMCHFWKCjKT8vMSeVSAAMjCy5jCxMjE0uTFAMTIESANMNkAyOzVCDL2NTIxMzEHMQHy4BIoEouAOoXEXTyQjWVAAAAAElFTkSuQmCC"
/>
diff --git a/src/BinarySynth.vue b/src/BinarySynth.vue
index 1062b0d..2a047af 100644
--- a/src/BinarySynth.vue
+++ b/src/BinarySynth.vue
@@ -13,6 +13,9 @@ const file = useFileStore()
@@ -37,5 +40,19 @@ const file = useFileStore()
opacity: 0;
position: fixed;
}
+
+ .about {
+ padding-top: 10px;
+ text-align: center;
+
+ a {
+ color: rgb(101 106 113);
+ text-decoration: none;
+
+ &:hover {
+ text-decoration: underline;
+ }
+ }
+ }
}
diff --git a/src/assets/js/getMIDINote.js b/src/assets/js/getMIDINote.js
index 5780fcc..e8cf6a9 100644
--- a/src/assets/js/getMIDINote.js
+++ b/src/assets/js/getMIDINote.js
@@ -18,18 +18,18 @@ function getNearbyValues(number, array) {
return [nearbyLess, nearbyOver]
}
-// Вычисляет массив: номер ноты и, при непрерывном режиме, величину питча
+// Calculates an array of: note number and, in continuous mode, pitch value
let frequency = null
let nearbyValues = null
let percent = null
let pitchValue = null
export function getMIDINote(byte, bitness, mode, coefficients, minimumFrequency, minimumNote) {
- // Возвращается номер ноты + питч
- // 1. Вычислить частоту
- // 2. Найти ближайщую нижнюю ноту в массиве к этой частоте
- // 3. Вычислить разницу между этой нотой и исходной частотой
- // 4. Эту разницу перевести в величину питча
+ // Note number + pitch is returned
+ // 1. Calculate frequency
+ // 2. Find the nearest lower note in the array to this frequency
+ // 3. Calculate the difference between this note and the original frequency
+ // 4. Convert this difference into a pitch value
if (mode === 'continuous') {
// 1.
if (byte === 0) frequency = minimumFrequency
@@ -55,7 +55,7 @@ export function getMIDINote(byte, bitness, mode, coefficients, minimumFrequency,
}
}
- // Возвращается номер ноты
+ // The note number returned
if (mode === 'tempered') {
if (bitness === '8') return [Math.floor(coefficients.tempered8 * byte) + minimumNote]
if (bitness === '16') return [Math.floor(coefficients.tempered16 * byte) + minimumNote]
diff --git a/src/assets/js/helpers.js b/src/assets/js/helpers.js
index 6341eb5..ea5c7cc 100644
--- a/src/assets/js/helpers.js
+++ b/src/assets/js/helpers.js
@@ -20,9 +20,9 @@ export function getRandomNumber(min, max) {
}
/**
- * Преобразовывает строки 'true' || 'false' в соответствующее булево значение
- * @param {String} value - строка для преобразования
- * @return {Boolean} Возвращает булево значение
+ * Converts 'true' || 'false' strings to the corresponding boolean value
+ * @param {String} value - conversion string
+ * @return {Boolean} Returns a boolean value
*/
export function getBooleanFromString(value) {
// prettier-ignore
diff --git a/src/components/ControlPanel.vue b/src/components/ControlPanel.vue
index cec3348..38dff3d 100644
--- a/src/components/ControlPanel.vue
+++ b/src/components/ControlPanel.vue
@@ -22,7 +22,7 @@ const iterationTime = computed(() =>
)
const bynaryInSelectedBitness = computed(() => (settings.bitness === '8' ? file.binary8 : file.binary16))
-// Создание
+// Creating
let audioContext = new AudioContext()
let oscillator = null
let gain = audioContext.createGain()
@@ -31,7 +31,7 @@ let lfoDepth = audioContext.createGain()
let lfoOsc = audioContext.createOscillator()
let masterGain = audioContext.createGain()
-// Настройка
+// Setup
filter.type = 'lowpass'
filter.frequency.value = settings.biquadFilterFrequency
filter.Q.value = settings.biquadFilterQ
@@ -42,7 +42,7 @@ lfoDepth.gain.value = settings.LFO.depth
gain.gain.value = settings.gain
masterGain.gain.value = 1
-// Соединение
+// Connection
filter.connect(gain)
lfoDepth.connect(masterGain.gain)
gain.connect(masterGain)
@@ -57,8 +57,8 @@ const sawtoothWave = audioContext.createPeriodicWave(
Float32Array.from(fourierCoefficients.sawtooth.imag)
)
-// При каждом play создаём новый осциллятор и подключаем его
-// Изменение его частоты будет планироваться в nextIteration()
+// At each play, create a new oscillator and connect it
+// Changing its frequency will be planned in nextIteration()
function audioInit() {
oscillator = audioContext.createOscillator()
oscillator.type = settings.waveType
@@ -69,12 +69,12 @@ audioInit()
const getRandomTimeGap = () => (settings.isRandomTimeGap ? getRandomNumber(0, settings.readingSpeed) : 0)
-// Чтобы снизить нагрузку на процессор, мы делим планирование композиции на итерации
+// To reduce CPU overhead, we divide composition planning into iterations
let nextIterationTimeoutID = null
let midiTimeoutIDs = []
let commands = []
function nextIteration(iterationNumber, scheduledCommands) {
- // Если дошли до конца команд или нажали stop, то выходим из рекурсии
+ // If we have reached the end of the commands or pressed stop, we exit the recursion
if (!status.playing) {
stop()
return
@@ -99,7 +99,7 @@ function nextIteration(iterationNumber, scheduledCommands) {
}
}
- // Определяем блок команд (500 штук)
+ // Define block of commands (500 pieces)
// prettier-ignore
const end = scheduledCommands + fileReadingLimit.value < settings.commandsRange.to
? scheduledCommands + fileReadingLimit.value - 1
@@ -108,13 +108,13 @@ function nextIteration(iterationNumber, scheduledCommands) {
status.currentCommandsBlock = [scheduledCommands, end]
status.iterationNumber = iterationNumber
- // Планируем композицию
+ // Planning the composition
let command = null
- // noteIndex - порядковый номер элемента во всём массиве команд
- // index - порядковый номер элемента в контексте итерации
+ // noteIndex - serial number of the element in the whole command array
+ // index - sequence number of the element in the iteration context
- // Для обычного режима
+ // For normal mode
if (!settings.midiMode) {
switch (settings.transitionType) {
@@ -128,7 +128,7 @@ function nextIteration(iterationNumber, scheduledCommands) {
settings.frequenciesRange.from,
settings.notesRange.from
)
- if (!isFinite(command)) command = 0 // На больших readingSpeed бывают глюки
+ if (!isFinite(command)) command = 0 // There are glitches on large readingSpeeds
oscillator.frequency.setValueAtTime(
command,
@@ -147,7 +147,7 @@ function nextIteration(iterationNumber, scheduledCommands) {
settings.frequenciesRange.from,
settings.notesRange.from
)
- if (!isFinite(command)) command = 0 // На больших readingSpeed бывают глюки
+ if (!isFinite(command)) command = 0 // There are glitches on large readingSpeeds
oscillator.frequency.linearRampToValueAtTime(
command,
audioContext.currentTime + (index * settings.readingSpeed + getRandomTimeGap())
@@ -165,7 +165,7 @@ function nextIteration(iterationNumber, scheduledCommands) {
settings.frequenciesRange.from,
settings.notesRange.from
)
- if (!isFinite(command)) command = 0.01 // На больших readingSpeed бывают глюки
+ if (!isFinite(command)) command = 0.01 // There are glitches on large readingSpeeds
oscillator.frequency.exponentialRampToValueAtTime(
command,
audioContext.currentTime + (index * settings.readingSpeed + getRandomTimeGap())
@@ -174,7 +174,7 @@ function nextIteration(iterationNumber, scheduledCommands) {
break
}
}
- // Для MIDI-режима
+ // For MIDI-mode
else {
midiTimeoutIDs.forEach((id) => {
clearTimeout(id)
@@ -208,11 +208,11 @@ function nextIteration(iterationNumber, scheduledCommands) {
}
}
- // Если в файле байтов меньше, чем fileReadingLimit.value, то рекурсия отменяется
+ // If there are fewer bytes in the file than fileReadingLimit.value, recursion is canceled
if (fileReadingLimit.value >= settings.commandsRange.to - settings.commandsRange.from) {
if (!settings.loop) {
nextIterationTimeoutID = setTimeout(() => {
- // Чтобы последняя нота не затягивалась
+ // So that the last note doesn't take too long
if (settings.midiMode) {
sendMIDIMessage.noteOff(
commands[settings.commandsRange.to - settings.commandsRange.from][0],
@@ -225,7 +225,7 @@ function nextIteration(iterationNumber, scheduledCommands) {
}, (settings.commandsRange.to - settings.commandsRange.from + 1) * settings.readingSpeed * 1000)
} else {
nextIterationTimeoutID = setTimeout(() => {
- // Чтобы последняя нота не затягивалась
+ // So that the last note doesn't take too long
if (settings.midiMode) {
sendMIDIMessage.noteOff(
commands[settings.commandsRange.to - settings.commandsRange.from][0],
@@ -239,7 +239,7 @@ function nextIteration(iterationNumber, scheduledCommands) {
}
} else {
nextIterationTimeoutID = setTimeout(() => {
- // Чтобы последняя нота не затягивалась
+ // So that the last note doesn't take too long
if (settings.midiMode) {
sendMIDIMessage.noteOff(commands[commands.length - 1][0], settings.midi.velocity, settings.midi.port, settings.midi.channel)
}
@@ -314,7 +314,7 @@ const gainValue = computed(() => settings.gain)
watch(gainValue, (newValue) => {
gain.gain.value = newValue
})
-// При изменении типа волны в UI сразу передаём его в осциллятор
+// When we change the wave type in the UI, we immediately pass it to the oscillator
const wave = computed(() => settings.waveType)
watch(wave, (newValue) => {
if (newValue === 'square2') {
@@ -325,7 +325,7 @@ watch(wave, (newValue) => {
oscillator.type = newValue
}
})
-// По окончании композиции заново создаём связку
+// At the end of the composition, re-create the binder
const playing = computed(() => status.playing)
watch(playing, (newValue) => {
if (newValue === false) {
@@ -342,7 +342,7 @@ const frequencyCoefficients = computed(() => {
}
})
-// При изменении этих параметров полностью заново пересчитать планирование
+// If these parameters are changed, completely recalculate the scheduling again
const readingSpeed = computed(() => settings.readingSpeed)
const transitionType = computed(() => settings.transitionType)
const isRandomTimeGap = computed(() => settings.isRandomTimeGap)
@@ -350,13 +350,13 @@ watch([readingSpeed, transitionType, isRandomTimeGap], () => {
if (status.playing) {
let command = null
- clearTimeout(nextIterationTimeoutID) // Отменяем рекурсию
+ clearTimeout(nextIterationTimeoutID) // Cancel the recursion
if (!settings.midiMode) {
- // Отменяем уже запланированное для осциллятора
+ // Cancel already planned for the oscillator
oscillator.frequency.cancelScheduledValues(audioContext.currentTime)
- // Перепланируем изменения в осцилляторе, начиная с последней команды на которой остановились
+ // Reschedule the changes in the oscillator, starting from the last command where we left off
switch (settings.transitionType) {
case 'immediately':
for (
@@ -373,7 +373,7 @@ watch([readingSpeed, transitionType, isRandomTimeGap], () => {
settings.notesRange.from
)
- if (!isFinite(command)) command = 0 // На больших readingSpeed бывают глюки
+ if (!isFinite(command)) command = 0 // There are glitches on large readingSpeeds
oscillator.frequency.setValueAtTime(
command,
@@ -397,7 +397,7 @@ watch([readingSpeed, transitionType, isRandomTimeGap], () => {
settings.notesRange.from
)
- if (!isFinite(command)) command = 0 // На больших readingSpeed бывают глюки
+ if (!isFinite(command)) command = 0 // There are glitches on large readingSpeeds
oscillator.frequency.linearRampToValueAtTime(
command,
audioContext.currentTime + (index * settings.readingSpeed + getRandomTimeGap())
@@ -420,7 +420,7 @@ watch([readingSpeed, transitionType, isRandomTimeGap], () => {
settings.notesRange.from
)
- if (!isFinite(command)) command = 0.01 // На больших readingSpeed бывают глюки
+ if (!isFinite(command)) command = 0.01 // There are glitches on large readingSpeeds
oscillator.frequency.exponentialRampToValueAtTime(
command,
audioContext.currentTime + (index * settings.readingSpeed + getRandomTimeGap())
@@ -471,12 +471,12 @@ watch([readingSpeed, transitionType, isRandomTimeGap], () => {
}
}
- // Заново планируем рекурсию
- // Время следующей рекурсии это количество оставшихся в итерации команд * settings.readingSpeed
+ // Reschedule the recursion
+ // The time of the next recursion is the number of commands remaining in the iteration * settings.readingSpeed
if (fileReadingLimit.value >= settings.commandsRange.to - settings.commandsRange.from) {
if (!settings.loop) {
nextIterationTimeoutID = setTimeout(() => {
- // Чтобы последняя нота не затягивалась
+ // So that the last note doesn't take too long
if (settings.midiMode) {
sendMIDIMessage.noteOff(
commands[settings.commandsRange.to - settings.commandsRange.from][0],
@@ -489,7 +489,7 @@ watch([readingSpeed, transitionType, isRandomTimeGap], () => {
}, (settings.commandsRange.to - settings.commandsRange.from - status.currentCommand) * settings.readingSpeed * 1000)
} else {
nextIterationTimeoutID = setTimeout(() => {
- // Чтобы последняя нота не затягивалась
+ // So that the last note doesn't take too long
if (settings.midiMode) {
sendMIDIMessage.noteOff(
commands[settings.commandsRange.to - settings.commandsRange.from][0],
@@ -521,7 +521,7 @@ watch([readingSpeed, transitionType, isRandomTimeGap], () => {
}
})
-// При изменении этих параметров пересчитать только частоты
+// When changing these parameters, only the frequencies are recalculated
const frequenciesRange = computed(() => settings.frequenciesRange)
const notesRange = computed(() => settings.notesRange)
const frequencyMode = computed(() => settings.frequencyMode)
@@ -530,10 +530,10 @@ watch([frequenciesRange.value, notesRange.value, frequencyMode], () => {
let command = null
if (!settings.midiMode) {
- // Отменяем уже запланированное для осциллятора
+ // Cancel already planned for the oscillator
oscillator.frequency.cancelScheduledValues(audioContext.currentTime)
- // Перепланируем изменения в осцилляторе, начиная с последней команды на которой остановились
+ // Reschedule the changes in the oscillator, starting from the last command where we left off
switch (settings.transitionType) {
case 'immediately':
for (
@@ -549,7 +549,7 @@ watch([frequenciesRange.value, notesRange.value, frequencyMode], () => {
settings.frequenciesRange.from,
settings.notesRange.from
)
- if (!isFinite(command)) command = 0 // На больших readingSpeed бывают глюки
+ if (!isFinite(command)) command = 0 // There are glitches on large readingSpeeds
oscillator.frequency.setValueAtTime(command, audioContext.currentTime + index * settings.readingSpeed)
}
break
@@ -568,7 +568,7 @@ watch([frequenciesRange.value, notesRange.value, frequencyMode], () => {
settings.frequenciesRange.from,
settings.notesRange.from
)
- if (!isFinite(command)) command = 0 // На больших readingSpeed бывают глюки
+ if (!isFinite(command)) command = 0 // There are glitches on large readingSpeeds
oscillator.frequency.linearRampToValueAtTime(command, audioContext.currentTime + index * settings.readingSpeed)
}
break
@@ -587,7 +587,7 @@ watch([frequenciesRange.value, notesRange.value, frequencyMode], () => {
settings.frequenciesRange.from,
settings.notesRange.from
)
- if (!isFinite(command)) command = 0.01 // На больших readingSpeed бывают глюки
+ if (!isFinite(command)) command = 0.01 // There are glitches on large readingSpeeds
oscillator.frequency.exponentialRampToValueAtTime(command, audioContext.currentTime + index * settings.readingSpeed)
}
break
@@ -637,7 +637,7 @@ watch([frequenciesRange.value, notesRange.value, frequencyMode], () => {
}
})
-// При изменении этих параметров начать игру заново
+// If you change these parameters, start the game over again
const bitness = computed(() => settings.bitness)
const commandsRange = computed(() => settings.commandsRange)
watch([bitness, commandsRange.value], () => {
@@ -658,15 +658,15 @@ watch(midiMode, (newValue) => {
audioInit()
}
- // Если перешли на MIDI-режим, то гасим осциллятор
+ // If the user has switched to MIDI mode, cancel the oscillator
if (playing.value) {
let command = null
- // Если включили MIDI
+ // If MIDI is on
if (newValue === true) {
oscillator.stop(audioContext.currentTime)
oscillator.frequency.cancelScheduledValues(audioContext.currentTime)
- clearTimeout(nextIterationTimeoutID) // Отменяем запланированную рекурсию
+ clearTimeout(nextIterationTimeoutID) // Cancel the scheduled recursion
for (
let noteIndex = status.currentCommandsBlock[0] + status.currentCommand, index = 0;
@@ -704,18 +704,18 @@ watch(midiMode, (newValue) => {
)
}
}
- // Если отключили MIDI
+ // If MIDI is off
else {
midiTimeoutIDs.forEach((id) => {
clearTimeout(id)
})
sendMIDIMessage.allSoundOff(settings.midi.port, settings.midi.channel)
- clearTimeout(nextIterationTimeoutID) // Отменяем запланированную рекурсию
+ clearTimeout(nextIterationTimeoutID) // Cancel the scheduled recursion
oscillator.start()
- // Перепланируем изменения в осцилляторе, начиная с последней команды на которой остановились
+ // Reschedule the changes in the oscillator, starting from the last command where we left off
switch (settings.transitionType) {
case 'immediately':
for (
@@ -732,7 +732,7 @@ watch(midiMode, (newValue) => {
settings.notesRange.from
)
- if (!isFinite(command)) command = 0 // На больших readingSpeed бывают глюки
+ if (!isFinite(command)) command = 0 // There are glitches on large readingSpeeds
oscillator.frequency.setValueAtTime(command, audioContext.currentTime + index * settings.readingSpeed)
}
break
@@ -752,7 +752,7 @@ watch(midiMode, (newValue) => {
settings.notesRange.from
)
- if (!isFinite(command)) command = 0 // На больших readingSpeed бывают глюки
+ if (!isFinite(command)) command = 0 // There are glitches on large readingSpeeds
oscillator.frequency.linearRampToValueAtTime(command, audioContext.currentTime + index * settings.readingSpeed)
}
break
@@ -772,15 +772,15 @@ watch(midiMode, (newValue) => {
settings.notesRange.from
)
- if (!isFinite(command)) command = 0.01 // На больших readingSpeed бывают глюки
+ if (!isFinite(command)) command = 0.01 // There are glitches on large readingSpeeds
oscillator.frequency.exponentialRampToValueAtTime(command, audioContext.currentTime + index * settings.readingSpeed)
}
break
}
}
- // Заново планируем рекурсию
- // Время следующей рекурсии это количество оставшихся в итерации команд * settings.readingSpeed
+ // Reschedule the recursion
+ // The time of the next recursion is the number of commands remaining in the iteration * settings.readingSpeed
if (fileReadingLimit.value >= settings.commandsRange.to) {
if (!settings.loop) {
nextIterationTimeoutID = setTimeout(() => {
diff --git a/src/components/ControlPanel/Frequency.vue b/src/components/ControlPanel/Frequency.vue
index 2d605ff..7c65551 100644
--- a/src/components/ControlPanel/Frequency.vue
+++ b/src/components/ControlPanel/Frequency.vue
@@ -21,7 +21,7 @@ watch(frequencyFrom, (newValue) => {
const frequencyTo = ref(settings.frequenciesRange.to)
watch(frequencyTo, (newValue) => {
- // setTimeout чтобы эта проверка сработала после frequencyFrom
+ // setTimeout to make this check triggered after frequencyFrom
setTimeout(() => {
if (isNaN(newValue)) {
return
diff --git a/src/components/ControlPanel/Global.vue b/src/components/ControlPanel/Global.vue
index 1bd31da..e606923 100644
--- a/src/components/ControlPanel/Global.vue
+++ b/src/components/ControlPanel/Global.vue
@@ -55,7 +55,7 @@ watch(commandsFrom, (newValue) => {
const commandsTo = ref(settings.commandsRange.to)
watch(commandsTo, (newValue) => {
- // setTimeout чтобы эта проверка сработала после commandsFrom
+ // setTimeout to make this check triggered after frequencyFrom
setTimeout(() => {
if (isNaN(newValue)) {
return
diff --git a/src/components/FileInput.vue b/src/components/FileInput.vue
index bb35bec..318241b 100644
--- a/src/components/FileInput.vue
+++ b/src/components/FileInput.vue
@@ -2,8 +2,8 @@
import { ref, onMounted, onBeforeMount } from 'vue'
import { useFileStore, useStatusStore } from '@/stores/global.js'
-// Принимает файл
-// Пишет в стор представления файла и информацию о нём
+// Receives the file
+// Writes the file representation and information about the file to the store
const reader = new FileReader()
const file = useFileStore()
const status = useStatusStore()
@@ -28,8 +28,8 @@ reader.addEventListener('loadend', async (event) => {
status.currentCommandsBlock = [0, 499]
}
- // Для файлов с нечётным количеством байт нельзя создать Uint16Array
- // Поэтому мы можем заполнить недостающее нулями
+ // For files with an odd number of bytes we cannot create a Uint16Array
+ // So we can fill the missing with zeros
let binary8 = new Uint8Array(event.target.result)
let binary16 = null
@@ -131,6 +131,10 @@ onMounted(() => {
CLICK FOR UPLOAD FILE
OR DROP FILE HERE
+
+
@@ -191,17 +195,43 @@ onMounted(() => {
.title {
font-size: 6vw;
+
+ @media (max-width: 768px) {
+ font-size: calc(0.525em + 6vw);
+ }
}
.description {
text-transform: uppercase;
font-size: 2vw;
font-style: italic;
+
+ @media (max-width: 768px) {
+ font-size: calc(0.325em + 2vw);
+ }
}
.cta {
font-size: 2vw;
margin-top: 50px;
+
+ @media (max-width: 768px) {
+ font-size: calc(0.325em + 2vw);
+ }
+ }
+}
+
+.about-on-first-screen {
+ position: absolute;
+ bottom: 10px;
+
+ a {
+ text-decoration: none;
+ color: rgb(101 106 113);
+
+ &:hover {
+ text-decoration: underline;
+ }
}
}
diff --git a/src/components/Status.vue b/src/components/Status.vue
index 14923a5..536d191 100644
--- a/src/components/Status.vue
+++ b/src/components/Status.vue
@@ -24,16 +24,16 @@ function timer() {
}, 1000)
}
-// Подсветка текущей команды в UI
+// Highlighting the current command in the UI
let commandIteratorInterval = null
let currentIteration = 0
-// По каждому play создаём новый итератор
+// Create a new iterator for each play
function commandIterator() {
- // Максимальная скорость setInterval 5мс
+ // Maximum speed of setInterval 5ms
if (readingSpeed.value >= 0.005) {
return setInterval(() => {
- // Если у нас один лист
+ // If we have one sheet
if (settings.commandsRange.to - settings.commandsRange.from <= commandsOnList.value) {
if (status.currentCommand >= settings.commandsRange.to - settings.commandsRange.from) {
status.currentCommand = 0
@@ -41,8 +41,8 @@ function commandIterator() {
status.currentCommand++
}
}
- // Если несколько листов
- // Мы можем определить переход на следующую порцию команд при изменении status.iterationNumber
+ // If multiple sheets
+ // We can define the transition to the next instruction portion when status.iterationNumber changes
else {
if (currentIteration !== status.iterationNumber) {
currentIteration = status.iterationNumber
@@ -53,9 +53,9 @@ function commandIterator() {
}
}, readingSpeed.value * 1000)
} else {
- // Если скорость большая, то отображаем активную команду через каждые 5 команд
+ // If the speed is high, we display the active command every 5 commands.
return setInterval(() => {
- // Если у нас один лист
+ // If we have one sheet
if (settings.commandsRange.to - settings.commandsRange.from <= commandsOnList.value) {
if (status.currentCommand >= settings.commandsRange.to - settings.commandsRange.from) {
status.currentCommand = 0
@@ -63,8 +63,8 @@ function commandIterator() {
status.currentCommand += 5 * (readingSpeed.value * 1000)
}
}
- // Если несколько листов
- // Мы можем определить переход на следующую порцию команд при изменении status.iterationNumber
+ // If multiple sheets
+ // We can define the transition to the next instruction portion when status.iterationNumber changes
else {
if (currentIteration !== status.iterationNumber) {
currentIteration = status.iterationNumber
@@ -128,7 +128,7 @@ watch(playing, (newValue) => {
}
})
-// При изменении в настройках скорости чтения заново определяем интервалы
+// When changing the reading speed settings, redefine the intervals
watch(readingSpeed, () => {
if (playing.value) {
clearInterval(timerInterval)