From 54c93760fb254547b35677a9e780923e8dd6adca Mon Sep 17 00:00:00 2001
From: Max Alyokhin <55048182+MaxAlyokhin@users.noreply.github.com>
Date: Fri, 1 Nov 2024 15:15:37 +0300
Subject: [PATCH] add import/export settings from URL
---
README.md | 32 ++++
README_RU.md | 34 +++-
dist/index.html | 6 +-
package.json | 2 +-
src/assets/styles/main.scss | 9 ++
src/components/ControlPanel.vue | 132 +--------------
.../ControlPanel/SettingsExchange.vue | 150 ++++++++++++++++++
7 files changed, 231 insertions(+), 134 deletions(-)
create mode 100644 src/components/ControlPanel/SettingsExchange.vue
diff --git a/README.md b/README.md
index a118bf9..346d536 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,26 @@ A web-synthesizer that generates sound from the binary code of any files. It can
The application reads the file sequentially, and due to the high speed of reading and random deviation of reading duration, we can get quite unpredictable generation of timbre nuances, and at certain settings we can switch to granular synthesis.
+You can try some examples of instrument settings that will sound about the same for most files:
+
+- [IDM](https:/bs.stranno.su/#{%22readingSpeed%22:0.0001,%22waveType%22:%22sine%22,%22gain%22:2.04,%22transitionType%22:%22immediately%22,%22frequencyMode%22:%22continuous%22,%22frequenciesRange%22:{%22from%22:0,%22to%22:8},%22notesRange%22:{%22from%22:48,%22to%22:60},%22commandsRange%22:{%22from%22:0,%22to%22:5000},%22midiMode%22:false,%22biquadFilterFrequency%22:33.1,%22biquadFilterQ%22:112.4,%22LFO%22:{%22enabled%22:true,%22type%22:%22triangle%22,%22rate%22:34.5,%22depth%22:1},%22bitness%22:%2216%22,%22panner%22:-0.67,%22loop%22:true,%22isRandomTimeGap%22:true,%22midi%22:{%22port%22:null,%22channel%22:0,%22pitch%22:8192,%22modulation%22:50,%22noMIDIPortsFound%22:true,%22velocity%22:120,%22solidMode%22:false,%22lastNoteOnMode%22:true}})
+
+- [Ambient](https:/bs.stranno.su/#{%22readingSpeed%22:0.0001,%22waveType%22:%22triangle%22,%22gain%22:0.41,%22transitionType%22:%22immediately%22,%22frequencyMode%22:%22continuous%22,%22frequenciesRange%22:{%22from%22:0,%22to%22:1},%22notesRange%22:{%22from%22:48,%22to%22:104},%22commandsRange%22:{%22from%22:0,%22to%22:624},%22midiMode%22:false,%22biquadFilterFrequency%22:39.3,%22biquadFilterQ%22:121.3,%22LFO%22:{%22enabled%22:true,%22type%22:%22sine%22,%22rate%22:99,%22depth%22:0.395},%22bitness%22:%228%22,%22panner%22:0,%22loop%22:true,%22isRandomTimeGap%22:true,%22midi%22:{%22port%22:null,%22channel%22:0,%22pitch%22:8192,%22modulation%22:66,%22noMIDIPortsFound%22:true,%22velocity%22:66,%22solidMode%22:true,%22lastNoteOnMode%22:true}})
+
+- [Harsh noise](https:/bs.stranno.su/#{%22readingSpeed%22:0.0001,%22waveType%22:%22sine%22,%22gain%22:2.56,%22transitionType%22:%22immediately%22,%22frequencyMode%22:%22continuous%22,%22frequenciesRange%22:{%22from%22:41,%22to%22:90},%22notesRange%22:{%22from%22:48,%22to%22:104},%22commandsRange%22:{%22from%22:0,%22to%22:100720},%22midiMode%22:false,%22biquadFilterFrequency%22:418.5,%22biquadFilterQ%22:66.6,%22LFO%22:{%22enabled%22:true,%22type%22:%22sine%22,%22rate%22:91,%22depth%22:0.546},%22bitness%22:%228%22,%22panner%22:0.15,%22loop%22:true,%22isRandomTimeGap%22:true,%22midi%22:{%22port%22:null,%22channel%22:0,%22pitch%22:8192,%22modulation%22:66,%22noMIDIPortsFound%22:true,%22velocity%22:66,%22solidMode%22:true,%22lastNoteOnMode%22:true}})
+
+## Contents
+
+- [Application principle](#principle)
+- [Switching to granular mode](#granular)
+- [Recommendations for optimal performance](#recommendation)
+- [MIDI](#midi)
+- [Interface features](#interface)
+- [Saving settings](#settings)
+- [Run locally and build the project](#run)
+
## Application principle
+
All data on any computer or smartphone is in the form of files (which are, in essence, texts). 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.
@@ -49,6 +68,7 @@ We could go further and write our own interpreter that would look at the files w
6. If we have reached the end of the file, stop execution or start again
## Switching to granular mode
+
> **Note**: Here and below the instrument interface terms are used. For their description, see below in the Interface features section
@@ -65,6 +85,7 @@ The image below shows the formation of an acoustic pixel from two commands (`com
![](https://store.stranno.su/bs/granular.jpg)
## Recommendations for optimal performance
+
- Use incognito mode with extensions disabled
- Close the non-incognito browser
@@ -75,6 +96,7 @@ BS under workload requires on average up to 7.1% CPU, in incognito mode 6%, Fire
More interesting sound is obtained with several independent instances of BS in different tabs. Theoretically, it would be possible to implement several BS threads in one tab, but this is less optimal, because browsers limit the maximum CPU usage per tab (in Chrome it is 10% of CPU). Also, each tab has its own [event loop](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Event_loop). You can use `ctrl + tab number` to quickly switch between tabs.
## MIDI
+
When the MIDI mode is enabled, the first available port and its first channel are automatically selected. Next, a `noteOn` signal is sent sequentially when reading, and a `noteOff` signal is sent after the `reading speed` time. In `continuous` mode, a `Pitch` signal is sent after each noteOn to hit the desired frequency.
@@ -91,6 +113,7 @@ To send MIDI messages to a DAW on Windows devices, you can use [loopMIDI](https:
> **Note**: MIDI messages are generated on the desktop only
## Interface features
+
- `Reading speed` — interpretation speed; at high speeds over 0.001 the application may become unstable
@@ -119,7 +142,16 @@ To send MIDI messages to a DAW on Windows devices, you can use [loopMIDI](https:
- Some input fields have a keyboard shortcut: pressing the corresponding key automatically moves the focus to the item. By pressing a key and moving the mouse at the same time, the values can be changed smoothly. The Y axis of the mouse movement determines the "power" of the value change
+## Saving settings
+
+
+You can save your settings in two ways:
+
+- via a URL link. When you click in the address bar of your browser, the application automatically generates a link to which the settings are written. You can copy it and when you open the link, the settings will be applied immediately, all you have to do is download a file for sound synthesis.
+- through a file. The interface includes Save settings and Load settings buttons, which allow you to save or load a settings file to or from your computer.
+
## Run locally and build the project
+
### Just copy the app
diff --git a/README_RU.md b/README_RU.md
index 3d689f7..4d22b78 100644
--- a/README_RU.md
+++ b/README_RU.md
@@ -14,7 +14,26 @@ _Синтез аудио из двоичного кода любого файл
Приложение последовательно читает файл и за счёт высокой скорости чтения и случайной величины отклонения длительности чтения, мы можем получить достаточно непредсказуемую генерацию нюансов тембра, а при определённых настройках перейти в гранулярный синтез.
+Вы можете попробовать несколько примеров настроек инструмента, которые будут звучать примерно одинаково для большинства файлов:
+
+- [IDM](https:/bs.stranno.su/#{%22readingSpeed%22:0.0001,%22waveType%22:%22sine%22,%22gain%22:2.04,%22transitionType%22:%22immediately%22,%22frequencyMode%22:%22continuous%22,%22frequenciesRange%22:{%22from%22:0,%22to%22:8},%22notesRange%22:{%22from%22:48,%22to%22:60},%22commandsRange%22:{%22from%22:0,%22to%22:5000},%22midiMode%22:false,%22biquadFilterFrequency%22:33.1,%22biquadFilterQ%22:112.4,%22LFO%22:{%22enabled%22:true,%22type%22:%22triangle%22,%22rate%22:34.5,%22depth%22:1},%22bitness%22:%2216%22,%22panner%22:-0.67,%22loop%22:true,%22isRandomTimeGap%22:true,%22midi%22:{%22port%22:null,%22channel%22:0,%22pitch%22:8192,%22modulation%22:50,%22noMIDIPortsFound%22:true,%22velocity%22:120,%22solidMode%22:false,%22lastNoteOnMode%22:true}})
+
+- [Ambient](https:/bs.stranno.su/#{%22readingSpeed%22:0.0001,%22waveType%22:%22triangle%22,%22gain%22:0.41,%22transitionType%22:%22immediately%22,%22frequencyMode%22:%22continuous%22,%22frequenciesRange%22:{%22from%22:0,%22to%22:1},%22notesRange%22:{%22from%22:48,%22to%22:104},%22commandsRange%22:{%22from%22:0,%22to%22:624},%22midiMode%22:false,%22biquadFilterFrequency%22:39.3,%22biquadFilterQ%22:121.3,%22LFO%22:{%22enabled%22:true,%22type%22:%22sine%22,%22rate%22:99,%22depth%22:0.395},%22bitness%22:%228%22,%22panner%22:0,%22loop%22:true,%22isRandomTimeGap%22:true,%22midi%22:{%22port%22:null,%22channel%22:0,%22pitch%22:8192,%22modulation%22:66,%22noMIDIPortsFound%22:true,%22velocity%22:66,%22solidMode%22:true,%22lastNoteOnMode%22:true}})
+
+- [Harsh noise](https:/bs.stranno.su/#{%22readingSpeed%22:0.0001,%22waveType%22:%22sine%22,%22gain%22:2.56,%22transitionType%22:%22immediately%22,%22frequencyMode%22:%22continuous%22,%22frequenciesRange%22:{%22from%22:41,%22to%22:90},%22notesRange%22:{%22from%22:48,%22to%22:104},%22commandsRange%22:{%22from%22:0,%22to%22:100720},%22midiMode%22:false,%22biquadFilterFrequency%22:418.5,%22biquadFilterQ%22:66.6,%22LFO%22:{%22enabled%22:true,%22type%22:%22sine%22,%22rate%22:91,%22depth%22:0.546},%22bitness%22:%228%22,%22panner%22:0.15,%22loop%22:true,%22isRandomTimeGap%22:true,%22midi%22:{%22port%22:null,%22channel%22:0,%22pitch%22:8192,%22modulation%22:66,%22noMIDIPortsFound%22:true,%22velocity%22:66,%22solidMode%22:true,%22lastNoteOnMode%22:true}})
+
+## Содержание
+
+- [Принцип работы](#principle)
+- [Переход в гранулярный режим](#granular)
+- [Рекомендации по оптимальной работе](#recommendation)
+- [MIDI](#midi)
+- [Интерфейс](#interface)
+- [Сохранение настроек](#settings)
+- [Запуск локально и сборка проекта](#run)
+
## Принцип работы
+
Все данные на любом компьютере или смартфоне представлены в виде файлов (являющихся, по своей сути, текстами). Содержанием этих файлов в конечном итоге являются просто нули и единицы. И эти нули и единицы, в общем-то, все одинаковые, поэтому нам нужен интерпретатор, для того чтобы извлечь смысл из этих текстов. Можно сказать, что формат файла (.mp3, .docx и т.д.) это просто указатель, какому интерпретатору надо передать текст, чтобы из него извлечь смысл.
@@ -48,6 +67,7 @@ _Синтез аудио из двоичного кода любого файл
6. Если дошли до конца файла, прекращаем исполнение, либо начинаем заново
## Переход в гранулярный режим
+
> **Note**: Здесь и далее используются термины интерфейса инструмента. Их описание смотрите ниже в разделе Особенности интерфейса
@@ -64,6 +84,7 @@ _Синтез аудио из двоичного кода любого файл
![](https://store.stranno.su/bs/granular.jpg)
## Рекомендации по оптимальной работе
+
- Использовать режим инкогнито при отключённых расширениях
- Браузер не-инкогнито закрыть
@@ -74,6 +95,7 @@ BS под нагрузкой требует в среднем до 7.1% CPU, в
Более интересный звук получается при нескольких независимых экземплярах BS в разных вкладках. Теоретически, можно было бы реализовать несколько потоков работы BS в одной вкладке, но это менее оптимально, так как браузеры ограничивают максимальное потребление CPU на вкладку (в Chrome это 10% CPU). Также, у каждой вкладки свой [event loop](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Event_loop). Для быстрого переключения между вкладками можно использовать `ctrl + номер вкладки`.
## MIDI
+
При включении MIDI-режима автоматически выбирается первый попавшийся порт из доступных и его первый канал. Далее последовательно при чтении посылается сигнал `noteOn`, через время `reading speed` посылается сигнал `noteOff`. В `continuous` режиме после каждого `noteOn` посылается `Pitch` сигнал, чтобы попасть в нужную частоту.
@@ -89,7 +111,8 @@ MIDI-сообщения могут посылаться:
> **Note**: MIDI-сообщения генерируются только на десктопе
-## Особенности интерфейса
+## Интерфейс
+
- `Reading speed` — скорость интерпретации; на высоких скоростях более 0.001 приложение может работать нестабильно
@@ -118,7 +141,16 @@ MIDI-сообщения могут посылаться:
- У некоторых полей ввода есть клавиатурное сокращение: при нажатии соответствующей клавиши автоматически наводится фокус на элемент. При зажатии клавиши и одновременном движении мышью можно плавно менять значения. По оси Y при движении мыши располагается "мощность" изменения значения
+## Сохранение настроек
+
+
+Сохранить настройки можно двумя путями:
+
+- через URL-ссылку. При щелчке в адресную строку браузера приложение автоматически формирует ссылку, в которую записываются настройки. Вы можете скопировать её и при открытии ссылки настройки сразу применятся, вам остаётся только загрузить файл для синтеза звука.
+- через файл. В интерфейсе преусмотрены кнопки Save settings и Load settings, которые позволяют сохранить на компьютер или загрузить из него файл с настройками.
+
## Запуск локально и сборка проекта
+
### Просто скопировать приложение
diff --git a/dist/index.html b/dist/index.html
index 7f96cb4..f8a22de 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/package.json b/package.json
index 7226bed..9ed9f94 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "binary-synth",
- "version": "1.5.3",
+ "version": "1.6.0",
"private": true,
"scripts": {
"dev": "vite --host",
diff --git a/src/assets/styles/main.scss b/src/assets/styles/main.scss
index ae41266..c1b054c 100644
--- a/src/assets/styles/main.scss
+++ b/src/assets/styles/main.scss
@@ -267,12 +267,21 @@ input {
height: 40px;
width: 100%;
max-width: 154px;
+ color: $white;
+ padding: 9px 20px;
+ display: flex;
+ justify-content: center;
+ align-items: center;
&:hover {
background: #090c0f;
transform: translateY(1px);
border-bottom: 2px solid $white;
}
+
+ input[type='file'] {
+ display: none;
+ }
}
.active {
diff --git a/src/components/ControlPanel.vue b/src/components/ControlPanel.vue
index d9332cc..1a43b4f 100644
--- a/src/components/ControlPanel.vue
+++ b/src/components/ControlPanel.vue
@@ -5,6 +5,7 @@ import { useFileStore, useSettingsStore, useStatusStore } from '@/stores/globalS
import { toFixedNumber, getRandomNumber, getDate } from '../assets/js/helpers.js'
import { getFrequency } from '../assets/js/getFrequency.js'
import fourierCoefficients from '../assets/js/fourierCoefficients.js'
+import SettingsExchange from './ControlPanel/SettingsExchange.vue'
import Filter from './ControlPanel/Filter.vue'
import LFO from './ControlPanel/LFO.vue'
import Oscillator from './ControlPanel/Oscillator.vue'
@@ -858,128 +859,13 @@ watch(midiMode, (newValue) => {
}
}
})
-
-// Settings saving
-function save() {
- const settingsObject = {
- readingSpeed: settings.readingSpeed,
- waveType: settings.waveType,
- gain: settings.gain,
- transitionType: settings.transitionType,
- frequencyMode: settings.frequencyMode,
- frequenciesRange: {
- from: settings.frequenciesRange.from,
- to: settings.frequenciesRange.to,
- },
- notesRange: {
- from: settings.notesRange.from,
- to: settings.notesRange.to,
- },
- commandsRange: {
- from: settings.commandsRange.from,
- to: settings.commandsRange.to,
- },
- biquadFilterFrequency: settings.biquadFilterFrequency,
- biquadFilterQ: settings.biquadFilterQ,
- LFO: {
- enabled: settings.LFO.enabled,
- type: settings.LFO.type,
- rate: settings.LFO.rate,
- depth: settings.LFO.depth,
- },
- bitness: settings.bitness,
- panner: settings.panner,
- loop: settings.loop,
- isRandomTimeGap: settings.isRandomTimeGap,
- midiMode: settings.midiMode,
- midi: {
- pitch: settings.midi.pitch,
- modulation: settings.midi.modulation,
- velocity: settings.midi.velocity,
- solidMode: settings.midi.solidMode,
- },
- }
-
- const link = document.createElement('a')
-
- link.href = URL.createObjectURL(
- new Blob([JSON.stringify(settingsObject, null, 2)], {
- type: 'application/json',
- })
- )
-
- link.setAttribute('download', `bs-${getDate()}.json`)
- document.body.appendChild(link)
- link.click()
- document.body.removeChild(link)
-}
-
-// Settings loading
-const reader = new FileReader()
-function load(settingsInJSON) {
- if (settingsInJSON.type !== 'application/json') throw new Error('JSON file is required')
-
- reader.readAsText(settingsInJSON)
-
- reader.addEventListener('load', (event) => {
- const settingsObject = JSON.parse(event.target.result)
-
- settings.$patch({
- readingSpeed: settingsObject.readingSpeed,
- waveType: settingsObject.waveType,
- gain: settingsObject.gain,
- transitionType: settingsObject.transitionType,
- frequencyMode: settingsObject.frequencyMode,
- frequenciesRange: {
- from: settingsObject.frequenciesRange.from,
- to: settingsObject.frequenciesRange.to,
- },
- notesRange: {
- from: settingsObject.notesRange.from,
- to: settingsObject.notesRange.to,
- },
- commandsRange: {
- from: settingsObject.commandsRange.from,
- to: settingsObject.commandsRange.to,
- },
- biquadFilterFrequency: settingsObject.biquadFilterFrequency,
- biquadFilterQ: settingsObject.biquadFilterQ,
- LFO: {
- enabled: settingsObject.LFO.enabled,
- type: settingsObject.LFO.type,
- rate: settingsObject.LFO.rate,
- depth: settingsObject.LFO.depth,
- },
- bitness: settingsObject.bitness,
- panner: settingsObject.panner,
- loop: settingsObject.loop,
- isRandomTimeGap: settingsObject.isRandomTimeGap,
- midiMode: settingsObject.midiMode,
- midi: {
- pitch: settingsObject.midi.pitch,
- modulation: settingsObject.midi.modulation,
- velocity: settingsObject.midi.velocity,
- solidMode: settingsObject.midi.solidMode,
- },
- })
- })
-
- reader.addEventListener('error', (event) => {
- throw new Error(event.target.error)
- })
-}