From 16899b823a4c001e25b8cdbff285ec6898e443c4 Mon Sep 17 00:00:00 2001 From: suroh Date: Sat, 16 Mar 2019 17:16:24 +0100 Subject: [PATCH] Created a Synth class, updated page layouts and interactivity. todo: improve audio feedback, flesh out teach section, and find a system to theme site on. --- css/master.css | 50 ++++++++++++++++++++--- index.html | 70 ++++++++++++++++++++++++++++++--- js/audio.js | 45 +-------------------- js/interaction.js | 59 ++++++++++++++++++++++++--- js/synth.js | 65 ++++++++++++++++++++++++++++++ work/autonomousArchiveWork.html | 4 +- work/chagrinWork.html | 4 +- work/euclidWork.html | 4 +- work/fragileMasteryWork.html | 2 +- work/postoastWork.html | 4 +- work/soloWork.html | 4 +- 11 files changed, 241 insertions(+), 70 deletions(-) create mode 100644 js/synth.js diff --git a/css/master.css b/css/master.css index 119fc3f..cbedbb3 100644 --- a/css/master.css +++ b/css/master.css @@ -23,7 +23,7 @@ nav { nav>span { font-size: 1.5em; color: #1e1e3c33; - transition: color ease 0.1s; + transition: color ease 0.5s; } nav>span:hover { @@ -32,17 +32,27 @@ nav>span:hover { cursor: pointer; } +nav>span.active { + color: #1e1e3cff; + transition: color 1s ease; +} + +nav>span.active::after { + color: #1e1e3cff; + margin-left: 14px; + content: attr(data-link); +} + h1#menuItem { position: absolute; left: 20px; bottom: 20px; margin: 0; padding: 0; - font-size: 31vw; + font-size: 32vw; opacity: 0; transform: translateY(300px); transition: opacity ease-in-out 0.1s, transform ease-in-out 0.1s; - } h1#menuItem.active { @@ -52,6 +62,36 @@ h1#menuItem.active { /* --> MAINPAGE CONSTRUCTION <-- */ -section#content { - margin: 40px 20px; +section.content { + display: grid; + margin: 0; + padding: 0; + opacity: 0; + max-height: 0px; + overflow: hidden; + transition: opacity 0.3s ease; +} + +section.content.visible { + margin: 20px 67px; + opacity: 1; + height: inherit; + overflow: inherit; +} + +section.content ul.subMenu { + margin: 0; + padding: 0; + list-style: none; + font-size: 1.5em; +} + +section.content ul.subMenu li { + border-left: thin solid #d2d2d8; + padding: 5px 10px; +} + +section.content ul.subMenu li:hover { + background: #d2d2d8; + cursor: pointer; } diff --git a/index.html b/index.html index f9bdd8f..e86684c 100755 --- a/index.html +++ b/index.html @@ -8,18 +8,20 @@ - + + + -
@@ -28,7 +30,63 @@
-
+ +
+ + + +
+ +
+ + + +
+
+ +
+ + << home + +
+

Solo

+ +

My solo practice embraces the unstable nature of improvisation in software and in music. I aim to examine the agency that improvisation can provide in the context of creative software and performance. I build custom software that acts both as an instrument and collaborator which I perform alongside with a combination of digital hardware and traditional musical instruments. Through the employment of these tools I challenge my ingrained jazz improvisational training, and examine creative relationships we can have with machines.

+ + + +

My improvisations to date have included hacking MIDI controllers and playing with audio analysis. Such approaches deconstruct my traditional musical training on the saxophone as I find myself attempting to embody routines as defined by software. It is either the programming of my years of formal music training, or a conceived imposition by the "perfection" of technology that makes me question my agency, and even my role as an interactor.

+ +

My performance practice is coupled with research that takes many other forms, where I engage in discussion around agencies a wider adoption of improvisation could provide. Building these instruments is an extension of my improvisational method, which acts as a form of liberation against the standardisation of both musical practices and software development. By employing improvisation towards the development of my tools, I expose the agencies that it might afford us in realms outside of creative practices, and how they might be used to combat outdated structures of hierarchy and value.

+ +

All of the code is freely accessible on my git repository here : git.suroh.tk.

+ + + +
+ +
+ + + +
+
+ +
+ +
+ +

teach content

+

Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute + irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

diff --git a/js/audio.js b/js/audio.js index f9d106d..36deba1 100644 --- a/js/audio.js +++ b/js/audio.js @@ -1,52 +1,11 @@ // audio for events // AUDIO SETUP -let audioEngine = new(window.AudioContext || window.webkitAudioContext)() -let masterGain = audioEngine.createGain() - -let voice = { - gain: audioEngine.createGain(), - osc: audioEngine.createOscillator(), - adsr: { - a: typeof amount === 'number' ? amount * 1000 : 0.025, - d: typeof amount === 'number' ? amount * 1000 : 0.5, - sv: typeof amount === 'number' ? amount : 0.8, - r: typeof amount === 'number' ? amount * 1000 : 0.5, - t: audioEngine.currentTime - }, - play: function(note) { - freq = typeof note === 'number' ? note : (Math.random() * 350) + 50 - this.osc.frequency.setValueAtTime(freq, audioEngine.currentTime) - this.engage() - }, - engage: function() { - - // clear preScheduled events - this.gain.gain.cancelScheduledValues(this.adsr.t) - // initial value - this.gain.gain.setValueAtTime(0.0, this.adsr.t) - // attack - this.gain.gain.setTargetAtTime(0.9, this.adsr.t, this.adsr.a) - // decay - this.gain.gain.setTargetAtTime(this.adsr.sv, this.adsr.t + this.adsr.a, this.adsr.d) - }, - release: function() { - this.adsr.t = audioEngine.currentTime - // release - this.gain.gain.setTargetAtTime(0.0, this.adsr.t, this.adsr.r) - } -} +const audioEngine = new(window.AudioContext || window.webkitAudioContext)() +const masterGain = audioEngine.createGain() window.onload = function() { // create gain - voice.gain.gain.setValueAtTime(0.0001, audioEngine.currentTime) masterGain.gain.value = 0.9 - voice.gain.connect(masterGain) - - // create oscilator - voice.osc.type = 'sine' - voice.osc.connect(voice.gain) - voice.osc.start() - masterGain.connect(audioEngine.destination) } diff --git a/js/interaction.js b/js/interaction.js index c4c9795..06323dd 100644 --- a/js/interaction.js +++ b/js/interaction.js @@ -2,27 +2,76 @@ const primaryNav = document.querySelector('nav') const menuItem = document.querySelector('#menuItem') +const content = document.querySelectorAll('.content') // TODO: (create voice class per button) -// let menuVoices = [] +let menuVoices = [] + +const subMenu = (activePage) => { + let menu = document.querySelector(`\#${activePage} \.subMenu`) + if (menu) { + return menu + } else { + return null + } +} + +const showContent = (section) => { + for (let sect of content) { + if (c.dataset.link == sect.id) { + sect.className = 'content visible' + } else if (!c.dataset.link) { + sect.className = 'content' + } else { + sect.className = 'content' + } + } +} for (let c of primaryNav.children) { // TODO: (create voice class per button) - // menuVoices[c.dataset.link] = new voice() + menuVoices[c.dataset.link] = new Synth(audioEngine) + menuVoices[c.dataset.link].gain.connect(masterGain) c.addEventListener('click', () => { - console.log(`clicked : ${c.dataset.link}`) + menuVoices[c.dataset.link].mod() + + for (let _c of primaryNav.children) { + if (c === _c && c.dataset.link != '~') { + c.className = 'active' + + if (subMenu(c.dataset.link)) { + for (let s of subMenu(c.dataset.link).children) { + s.addEventListener('click', () => { + console.log(`clicked : ${s.dataset.link}`) + }) + } + } + } else { + _c.className = '' + } + } + + for (let sect of content) { + if (c.dataset.link == sect.id) { + sect.className = 'content visible' + } else if (!c.dataset.link) { + sect.className = 'content' + } else { + sect.className = 'content' + } + } }) c.addEventListener('mouseenter', () => { menuItem.innerHTML = c.dataset.link menuItem.className = 'active' - voice.play() + menuVoices[c.dataset.link].noteOn() }) c.addEventListener('mouseout', () => { menuItem.className = 'inactive' - voice.release() + menuVoices[c.dataset.link].noteOff() }) menuItem.addEventListener('transitionend', () => { diff --git a/js/synth.js b/js/synth.js new file mode 100644 index 0000000..c8d0791 --- /dev/null +++ b/js/synth.js @@ -0,0 +1,65 @@ +class Synth { + // SYNTH VOICE CONSTRUCTOR + // Variables? + constructor(audio, type, env = { + a: 0.2, + r: 0.5, + }) { + + this.audio = audio + this.t = this.audio.currentTime + + this.env = { + a: env.a, + r: env.r, + } + + // PRIMARY GAIN + this.gain = this.audio.createGain() + this.gain.gain.value = 0 + + // PRIMARY VOICE + this.primVoice = this.audio.createOscillator() + this.primVoice.type = typeof type === 'string' ? type : 'sine' + this.primVoice.connect(this.gain) + this.primVoice.start() + + // MOD GAIN + this.modGain = this.audio.createGain() + this.modGain.gain.value = 0 + this.modGain.connect(this.gain) + + // MOD VOICE + this.modVoice = this.audio.createOscillator() + this.modVoice.type = 'triangle' + this.modVoice.connect(this.modGain) + this.modVoice.start() + + } + + // OSC -> ADSR -> GAIN + noteOn(freq) { + // note frequency + this.freq = freq ? freq : (Math.random() * 350) + 50 + this.primVoice.frequency.setValueAtTime(this.freq, this.t) + this.modVoice.frequency.setValueAtTime(this.freq - 20, this.t) + // clear preScheduled events + this.gain.gain.cancelScheduledValues(this.audio.currentTime) + // attack + this.gain.gain.setTargetAtTime(0.9, this.t + this.env.a, 0.2) + } + + noteOff() { + // release + this.gain.gain.setTargetAtTime(0.0, this.t + this.env.r, 1) + } + + mod() { + this.modGain.gain.cancelScheduledValues(this.audio.currentTime) + // attack + this.modGain.gain.setTargetAtTime(0.9, this.audio.currentTime, 0.5) + // release + this.modGain.gain.setTargetAtTime(0.0, this.audio.currentTime + 0.4, 0.5) + } + +} diff --git a/work/autonomousArchiveWork.html b/work/autonomousArchiveWork.html index 9595fe9..24d9b5f 100755 --- a/work/autonomousArchiveWork.html +++ b/work/autonomousArchiveWork.html @@ -5,7 +5,7 @@ - + @@ -68,4 +68,4 @@
- \ No newline at end of file + diff --git a/work/chagrinWork.html b/work/chagrinWork.html index 20e897c..d3308bd 100755 --- a/work/chagrinWork.html +++ b/work/chagrinWork.html @@ -5,7 +5,7 @@ - + @@ -41,4 +41,4 @@ - \ No newline at end of file + diff --git a/work/euclidWork.html b/work/euclidWork.html index fbc1faf..2a3c7bc 100755 --- a/work/euclidWork.html +++ b/work/euclidWork.html @@ -5,7 +5,7 @@ - + @@ -49,4 +49,4 @@ - \ No newline at end of file + diff --git a/work/fragileMasteryWork.html b/work/fragileMasteryWork.html index 307a3fa..729766c 100755 --- a/work/fragileMasteryWork.html +++ b/work/fragileMasteryWork.html @@ -5,7 +5,7 @@ - + diff --git a/work/postoastWork.html b/work/postoastWork.html index cdb03b9..93cc629 100755 --- a/work/postoastWork.html +++ b/work/postoastWork.html @@ -5,7 +5,7 @@ - + @@ -37,4 +37,4 @@ - \ No newline at end of file + diff --git a/work/soloWork.html b/work/soloWork.html index eba4274..28bf609 100755 --- a/work/soloWork.html +++ b/work/soloWork.html @@ -5,7 +5,7 @@ - + @@ -43,4 +43,4 @@ - \ No newline at end of file +