From 232163064e1b0a70b3a7a8282e76c19af080010e Mon Sep 17 00:00:00 2001 From: Kevin Thomas Date: Tue, 26 Apr 2022 19:36:33 -0700 Subject: [PATCH 1/9] Start implementing record function --- src/components/Noise.vue | 72 +++++++++++++++++++++++++++++++++++++--- src/components/noise.js | 42 +++++++++++++++++++++++ 2 files changed, 109 insertions(+), 5 deletions(-) diff --git a/src/components/Noise.vue b/src/components/Noise.vue index 0e05b1c..e6e83ee 100644 --- a/src/components/Noise.vue +++ b/src/components/Noise.vue @@ -90,7 +90,7 @@ > - Profile Name + Save Profile As... @@ -168,6 +168,16 @@ Export Profile + + + mdi-record + + + Record Profile Audio + + @@ -274,6 +284,58 @@ + + + + + Start Recording + + + Start recording the currently playing audio? This is only supported on Chrome and Firefox. + + + + + Close + + + Record + + + + + + + + + Recording + + + Time Elapsed: {{ recordingTimeElapsed }} + + + + + Stop + + + + @@ -345,7 +407,7 @@ label="Volume" thumb-label max="0" - min="-30" + min="-60" class="mx-3" @input="updateVolume" /> @@ -548,7 +610,7 @@ label="Volume" thumb-label max="0" - min="-30" + min="-60" class="mx-3" @input="updateSampleVolume(sample.id, index)" /> @@ -647,7 +709,7 @@ -

WARNING: Uploaded samples are publicly accessible.

+ WARNING: Uploaded samples are publicly accessible.
@@ -711,7 +773,7 @@ > - Edit Sample + Edit Samples diff --git a/src/components/noise.js b/src/components/noise.js index 8a2b383..e671fb8 100644 --- a/src/components/noise.js +++ b/src/components/noise.js @@ -64,6 +64,9 @@ export default { previewSampleButtonText: 'Preview Sample', previewSampleLoading: true, previewSampleLength: 0, + startRecordingDialog: false, + recordingDialog: false, + recordingTimeElapsed: 0, errorSnackbar: false, errorSnackbarText: '', rules: { @@ -98,6 +101,7 @@ export default { this.players = new Tone.Players() this.samplePreviewPlayer = new Tone.Player().toDestination() this.samplePreviewPlayer.loop = true + this.recorder = new Tone.Recorder() this.populateProfileItems(0) this.populatePreviewSampleItems() @@ -631,6 +635,44 @@ export default { if (this.previewSampleLoopStart >= 0 && this.previewSampleLoopEnd <= this.previewSampleLength) { this.samplePreviewPlayer.setLoopPoints(this.previewSampleLoopStart, this.previewSampleLoopEnd) } + }, + async startRecording () { + this.startRecordingDialog = false + this.recordingDialog = true + this.recordingTimeElapsed = 0 + + if (!this.isFilterEnabled && !this.isTremoloEnabled) { + this.noise.connect(this.recorder) + } else if (!this.isFilterEnabled && this.isTremoloEnabled) { + this.tremolo = new Tone.Tremolo({ frequency: this.tremoloFrequency, depth: this.tremoloDepth }).connect(this.recorder) + } else if (this.isFilterEnabled && !this.isLFOFilterCutoffEnabled && !this.isTremoloEnabled) { + this.filter = new Tone.Filter(this.filterCutoff, this.filterType).connect(this.recorder) + } else if (this.isFilterEnabled && this.isLFOFilterCutoffEnabled && !this.isTremoloEnabled) { + this.filter = new Tone.Filter(this.filterCutoff, this.filterType).connect(this.recorder) + } else if (this.isFilterEnabled && this.isLFOFilterCutoffEnabled && this.isTremoloEnabled) { + this.tremolo = new Tone.Tremolo({ frequency: this.tremoloFrequency, depth: this.tremoloDepth }).connect(this.recorder) + } else { + this.tremolo = new Tone.Tremolo({ frequency: this.tremoloFrequency, depth: this.tremoloDepth }).connect(this.recorder) + } + + this.loadedSamples.forEach(s => { + this.players.player(s.id).connect(this.recorder) + }) + + await this.recorder.start() + this.recordingInterval = setInterval(() => this.recordingTimeElapsed++, 1000) + }, + async stopRecording () { + const recording = await this.recorder.stop() + + const url = URL.createObjectURL(recording) + const anchor = document.createElement('a') + anchor.download = this.selectedProfile.text + '.webm' + anchor.href = url + anchor.click() + + clearInterval(this.recordingInterval) + this.recordingDialog = false } } } From e13a05670661e1dd976512e4ef5a6137e34e280b Mon Sep 17 00:00:00 2001 From: Kevin Thomas Date: Thu, 28 Apr 2022 23:56:51 -0700 Subject: [PATCH 2/9] Implement record function --- src/components/Noise.vue | 111 ++++++++++++++++++++++----------------- src/components/noise.js | 101 ++++++++++++++++++++++++++++------- 2 files changed, 146 insertions(+), 66 deletions(-) diff --git a/src/components/Noise.vue b/src/components/Noise.vue index e6e83ee..1136d37 100644 --- a/src/components/Noise.vue +++ b/src/components/Noise.vue @@ -95,13 +95,11 @@ - - - + @@ -169,7 +167,7 @@ mdi-record @@ -262,7 +260,6 @@ :items="profileItems" return-object label="Profiles" - class="mx-3 mb-5" />
@@ -289,29 +286,54 @@ v-model="startRecordingDialog" max-width="600px" > - - - Start Recording - - - Start recording the currently playing audio? This is only supported on Chrome and Firefox. - - - - - Close - - - Record - - - + + + + Start Recording + + + + +

Select profile to record audio for. This is only supported on Chrome and Firefox.

+
+ + + + + + +
+
+ + + + Close + + + Record + + +
+
Recording - Time Elapsed: {{ recordingTimeElapsed }} + Time Elapsed: {{ recordingTimeElapsed }} Seconds @@ -708,9 +730,7 @@ - - WARNING: Uploaded samples are publicly accessible. - +

WARNING: Uploaded samples are publicly accessible.

- - - +
@@ -784,7 +802,6 @@ item-text="name" return-object label="Samples" - class="mx-3" @change="loadPreviewSample" /> @@ -798,7 +815,6 @@ v-model="previewSampleLoopPointsEnabled" :disabled="previewSamplePlaying" label="Use Loop Points" - class="mx-3" /> @@ -807,7 +823,7 @@ v-model="previewSampleLoopStart" type="number" label="Loop Start Time" - class="mx-3" + class="mr-3" :disabled="!previewSampleLoopPointsEnabled || previewSamplePlaying" :rules="[rules.gt(-1)]" @change="updatePreviewSamplePlayerLoopPoints" @@ -817,7 +833,7 @@ v-model="previewSampleLoopEnd" type="number" label="Loop End Time" - class="mx-3" + class="ml-3" :disabled="!previewSampleLoopPointsEnabled || previewSamplePlaying" :rules="[rules.gt(-1), rules.lt(previewSampleLength)]" @change="updatePreviewSamplePlayerLoopPoints" @@ -829,7 +845,6 @@ v-model="previewSampleFadeIn" type="number" label="Fade In Time" - class="mx-3" :disabled="previewSamplePlaying" :rules="[rules.gt(-1)]" @change="updatePreviewSamplePlayerFadeIn" diff --git a/src/components/noise.js b/src/components/noise.js index e671fb8..ae80690 100644 --- a/src/components/noise.js +++ b/src/components/noise.js @@ -67,6 +67,9 @@ export default { startRecordingDialog: false, recordingDialog: false, recordingTimeElapsed: 0, + recordedProfile: {}, + recordingFileName: '', + isRecordingValid: false, errorSnackbar: false, errorSnackbarText: '', rules: { @@ -253,6 +256,7 @@ export default { this.selectedProfile = this.profileItems.find(p => p.id === profileId) } this.exportedProfile = this.profileItems[0] + this.recordedProfile = this.profileItems[0] this.loadProfile() } } @@ -487,7 +491,7 @@ export default { } }) .catch(() => { - this.errorSnackbarText = 'Error Saving Profile' + this.errorSnackbarText = 'Error Importing Profile' this.errorSnackbar = true }) @@ -540,7 +544,7 @@ export default { } }) .catch(() => { - this.errorSnackbarText = 'Error Loading Profile' + this.errorSnackbarText = 'Error Exporting Profile' this.errorSnackbar = true }) @@ -636,43 +640,104 @@ export default { this.samplePreviewPlayer.setLoopPoints(this.previewSampleLoopStart, this.previewSampleLoopEnd) } }, - async startRecording () { - this.startRecordingDialog = false - this.recordingDialog = true - this.recordingTimeElapsed = 0 + openStartRecordingDialog () { + this.startRecordingDialog = true + this.profileMoreDialog = false + }, + startRecording () { + this.$http.get('/profiles/'.concat(this.recordedProfile.id)) + .then(async response => { + if (response.status === 200) { + const profile = response.data.profile + + this.isTimerEnabled = profile.isTimerEnabled + this.duration = profile.duration + this.volume = profile.volume + this.noiseColor = profile.noiseColor + this.isFilterEnabled = profile.isFilterEnabled + this.filterType = profile.filterType + this.filterCutoff = profile.filterCutoff + this.isLFOFilterCutoffEnabled = profile.isLFOFilterCutoffEnabled + this.lfoFilterCutoffFrequency = profile.lfoFilterCutoffFrequency + this.lfoFilterCutoffRange[0] = profile.lfoFilterCutoffLow + this.lfoFilterCutoffRange[1] = profile.lfoFilterCutoffHigh + this.isTremoloEnabled = profile.isTremoloEnabled + this.tremoloFrequency = profile.tremoloFrequency + this.tremoloDepth = profile.tremoloDepth + + this.loadedSamples = profile.samples + + this.startRecordingDialog = false + this.recordingDialog = true + this.recordingTimeElapsed = 0 + + await this.recorder.start() + this.recordingInterval = setInterval(() => this.recordingTimeElapsed++, 1000) + this.playProfileForRecording() + } + }) + .catch(() => { + this.errorSnackbarText = 'Error Recording Profile' + this.errorSnackbar = true + }) + }, + playProfileForRecording () { + this.playDisabled = true + Tone.Transport.cancel() if (!this.isFilterEnabled && !this.isTremoloEnabled) { - this.noise.connect(this.recorder) + this.noise = new Tone.Noise({ volume: this.volume, type: this.noiseColor }).connect(this.recorder).toDestination() } else if (!this.isFilterEnabled && this.isTremoloEnabled) { - this.tremolo = new Tone.Tremolo({ frequency: this.tremoloFrequency, depth: this.tremoloDepth }).connect(this.recorder) - } else if (this.isFilterEnabled && !this.isLFOFilterCutoffEnabled && !this.isTremoloEnabled) { - this.filter = new Tone.Filter(this.filterCutoff, this.filterType).connect(this.recorder) - } else if (this.isFilterEnabled && this.isLFOFilterCutoffEnabled && !this.isTremoloEnabled) { - this.filter = new Tone.Filter(this.filterCutoff, this.filterType).connect(this.recorder) - } else if (this.isFilterEnabled && this.isLFOFilterCutoffEnabled && this.isTremoloEnabled) { - this.tremolo = new Tone.Tremolo({ frequency: this.tremoloFrequency, depth: this.tremoloDepth }).connect(this.recorder) + this.tremolo = new Tone.Tremolo({ frequency: this.tremoloFrequency, depth: this.tremoloDepth }).connect(this.recorder).toDestination().start() + this.noise = new Tone.Noise({ volume: this.volume, type: this.noiseColor }).connect(this.tremolo) + } else if (this.isFilterEnabled && !this.isTremoloEnabled) { + this.filter = new Tone.Filter(this.filterCutoff, this.filterType).connect(this.recorder).toDestination() + this.noise = new Tone.Noise({ volume: this.volume, type: this.noiseColor }).connect(this.filter) + } else if (this.isFilterEnabled && this.isTremoloEnabled) { + this.tremolo = new Tone.Tremolo({ frequency: this.tremoloFrequency, depth: this.tremoloDepth }).connect(this.recorder).toDestination().start() + this.filter = new Tone.Filter(this.filterCutoff, this.filterType).connect(this.tremolo) + this.noise = new Tone.Noise({ volume: this.volume, type: this.noiseColor }).connect(this.filter) } else { - this.tremolo = new Tone.Tremolo({ frequency: this.tremoloFrequency, depth: this.tremoloDepth }).connect(this.recorder) + this.tremolo = new Tone.Tremolo({ frequency: this.tremoloFrequency, depth: this.tremoloDepth }).connect(this.recorder).toDestination().start() + this.filter = new Tone.Filter(this.filterCutoff, this.filterType).connect(this.tremolo) + this.noise = new Tone.Noise({ volume: this.volume, type: this.noiseColor }).connect(this.filter) + } + + if (this.isLFOFilterCutoffEnabled) { + this.lfo = new Tone.LFO({ frequency: this.lfoFilterCutoffFrequency, min: this.lfoFilterCutoffRange[0], max: this.lfoFilterCutoffRange[1] }) + this.lfo.connect(this.filter.frequency).start() } this.loadedSamples.forEach(s => { + this.players.player(s.id).loop = true + this.players.player(s.id).fadeIn = s.fadeIn + if (s.loopPointsEnabled) { + this.players.player(s.id).setLoopPoints(s.loopStart, s.loopEnd) + } + this.players.player(s.id).volume.value = s.volume + this.players.player(s.id).connect(this.recorder) + this.players.player(s.id).unsync().sync().start(0) }) - await this.recorder.start() - this.recordingInterval = setInterval(() => this.recordingTimeElapsed++, 1000) + this.noise.sync().start(0) + + Tone.Transport.start() }, async stopRecording () { const recording = await this.recorder.stop() + this.loadProfile() + const url = URL.createObjectURL(recording) const anchor = document.createElement('a') - anchor.download = this.selectedProfile.text + '.webm' + anchor.download = this.recordingFileName + '.webm' anchor.href = url anchor.click() clearInterval(this.recordingInterval) this.recordingDialog = false + this.stop() } } } From c485b0a786cfa0433740bcd71b6828d3213e0549 Mon Sep 17 00:00:00 2001 From: Kevin Thomas Date: Fri, 29 Apr 2022 00:08:31 -0700 Subject: [PATCH 3/9] Update README --- README.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/README.md b/README.md index 421b021..33237fa 100644 --- a/README.md +++ b/README.md @@ -9,9 +9,9 @@ Self-hostable web tool for generating ambient noises # Features * Generate and customize ambient noises and user-uploadable samples (leveraging [Tone.js](https://github.com/Tonejs/Tone.js/)) -* Save "noise profiles" so you can easily switch between your created soundscapes +* Save "noise profiles" so you can easily switch between your created soundscapes, import and export them for easy sharing * Fine-tune your noises with audio processing tools like filters, LFOs, and effects -* Upload audio samples (e.g rain, wind, thunder) to combine with your generated noises +* Upload and edit audio samples (e.g rain, wind, thunder) to combine with your generated noises * Use admin tools to manage multiple users * Mobile friendly From 8ad174b0af98d8b289748f9d155bff9153cb5438 Mon Sep 17 00:00:00 2001 From: Kevin Thomas Date: Sat, 30 Apr 2022 21:07:41 -0700 Subject: [PATCH 4/9] Add recording function improvements --- src/components/Noise.vue | 9 ++++++++- src/components/noise.js | 11 +++++++++++ 2 files changed, 19 insertions(+), 1 deletion(-) diff --git a/src/components/Noise.vue b/src/components/Noise.vue index 1136d37..97aadfb 100644 --- a/src/components/Noise.vue +++ b/src/components/Noise.vue @@ -339,6 +339,7 @@ @@ -349,11 +350,17 @@ + + Cancel + - Stop + Stop and Save diff --git a/src/components/noise.js b/src/components/noise.js index ae80690..2aee06e 100644 --- a/src/components/noise.js +++ b/src/components/noise.js @@ -727,6 +727,7 @@ export default { async stopRecording () { const recording = await this.recorder.stop() + // Set active profile back to the selected one this.loadProfile() const url = URL.createObjectURL(recording) @@ -735,6 +736,16 @@ export default { anchor.href = url anchor.click() + clearInterval(this.recordingInterval) + this.recordingDialog = false + this.stop() + }, + async cancelRecording () { + await this.recorder.stop() + + // Set active profile back to the selected one + this.loadProfile() + clearInterval(this.recordingInterval) this.recordingDialog = false this.stop() From 86141a124c2b271e63713b180106216f68c0c041 Mon Sep 17 00:00:00 2001 From: Kay Thomas Date: Sat, 30 Apr 2022 21:53:06 -0700 Subject: [PATCH 5/9] Create github workflow --- .github/workflows/docker-image.yml | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 .github/workflows/docker-image.yml diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml new file mode 100644 index 0000000..07c1b0b --- /dev/null +++ b/.github/workflows/docker-image.yml @@ -0,0 +1,21 @@ +name: Docker Image CI + +on: [push] + +jobs: + buildx: + runs-on: ubuntu-latest + steps: + - + name: Checkout + uses: actions/checkout@v3 + - + name: Set up QEMU + uses: docker/setup-qemu-action@v1 + - + name: Set up Docker Buildx + id: buildx + uses: docker/setup-buildx-action@v1 + - + name: Available platforms + run: echo ${{ steps.buildx.outputs.platforms }} From c6ff0b6def3058b720f262fe07200683ef5122fc Mon Sep 17 00:00:00 2001 From: Kay Thomas Date: Sat, 30 Apr 2022 22:29:04 -0700 Subject: [PATCH 6/9] Update docker-image.yml --- .github/workflows/docker-image.yml | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 07c1b0b..d3bfd40 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -17,5 +17,24 @@ jobs: id: buildx uses: docker/setup-buildx-action@v1 - - name: Available platforms - run: echo ${{ steps.buildx.outputs.platforms }} + name: Login to DockerHub + uses: docker/login-action@v1 + with: + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + - + name: Build and push linux/amd64 + uses: docker/build-push-action@v2 + with: + context: . + platforms: linux/amd64 + push: true + tags: noisedash/noisedash:github-test + - + name: Build and push linux/arm/v7 + uses: docker/build-push-action@v2 + with: + context: . + platforms: linux/arm/v7 + push: true + tags: noisedash/noisedash:github-test-armv7 From 4bc985d36eebcb6d6bde500b7e3bff10ca7b067e Mon Sep 17 00:00:00 2001 From: Kay Thomas Date: Sat, 30 Apr 2022 23:10:46 -0700 Subject: [PATCH 7/9] Update docker-image.yml --- .github/workflows/docker-image.yml | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index d3bfd40..1ed5f02 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -1,6 +1,8 @@ name: Docker Image CI -on: [push] +on: + push: + branches: [ main ] jobs: buildx: @@ -29,7 +31,7 @@ jobs: context: . platforms: linux/amd64 push: true - tags: noisedash/noisedash:github-test + tags: noisedash/noisedash:latest-github - name: Build and push linux/arm/v7 uses: docker/build-push-action@v2 @@ -37,4 +39,4 @@ jobs: context: . platforms: linux/arm/v7 push: true - tags: noisedash/noisedash:github-test-armv7 + tags: noisedash/noisedash:latest-armv7-github From c97c87f58c05b6d7e2c7b3a3686d0b486e5d5b59 Mon Sep 17 00:00:00 2001 From: Kay Thomas Date: Sat, 30 Apr 2022 23:13:07 -0700 Subject: [PATCH 8/9] Update docker-image.yml --- .github/workflows/docker-image.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/docker-image.yml b/.github/workflows/docker-image.yml index 1ed5f02..9fc7ded 100644 --- a/.github/workflows/docker-image.yml +++ b/.github/workflows/docker-image.yml @@ -31,7 +31,7 @@ jobs: context: . platforms: linux/amd64 push: true - tags: noisedash/noisedash:latest-github + tags: noisedash/noisedash:latest - name: Build and push linux/arm/v7 uses: docker/build-push-action@v2 @@ -39,4 +39,4 @@ jobs: context: . platforms: linux/arm/v7 push: true - tags: noisedash/noisedash:latest-armv7-github + tags: noisedash/noisedash:latest-armv7 From 5fb7066e59989553880e1b49f613340bfae9b382 Mon Sep 17 00:00:00 2001 From: Kevin Thomas Date: Sat, 30 Apr 2022 23:19:36 -0700 Subject: [PATCH 9/9] Update README --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 33237fa..791c3cc 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Self-hostable web tool for generating ambient noises # Features * Generate and customize ambient noises and user-uploadable samples (leveraging [Tone.js](https://github.com/Tonejs/Tone.js/)) -* Save "noise profiles" so you can easily switch between your created soundscapes, import and export them for easy sharing +* Save "noise profiles" so you can easily switch between your created soundscapes. Import and export them for easy sharing, record them for use elsewhere * Fine-tune your noises with audio processing tools like filters, LFOs, and effects * Upload and edit audio samples (e.g rain, wind, thunder) to combine with your generated noises * Use admin tools to manage multiple users