Compare commits

...

10 Commits

Author SHA1 Message Date
Khaled Aldayeh 5e334d3bfd new Signal naming 2024-01-09 21:19:13 +01:00
Robert Rapp 653b9220f8 ChannelSteamer und Octave.js2 2023-12-16 22:46:22 +01:00
Robert Rapp fd3ccb8fa2 Test mit octave.js 2023-12-16 22:45:56 +01:00
Robert Rapp f33e61f2d3 Merge branch 'robbi-inspect' of https://gitea.mindboost.team/Mindboost/dev-audioprocessing into robbi-inspect 2023-12-07 18:20:21 +01:00
Robert Rapp 23ac4743f5 merged gitignores 2023-12-07 18:20:16 +01:00
Robert Rapp 7a40953f04 revert 63f795d4c9
revert Dockerfile aktualisiert
2023-12-07 04:33:10 +00:00
Robert Rapp cfc32b0229 improvement dockerfile multistage and production 2023-12-07 05:30:05 +01:00
Robert Rapp 63f795d4c9 Dockerfile aktualisiert 2023-12-07 03:11:46 +00:00
Robert Rapp 771a847148 Changed the path of octave.js to public subfolder called script 2023-12-07 01:42:46 +01:00
Robert Rapp bc54b1a089 updated readme and have a running version 2023-12-06 23:00:21 +01:00
21 changed files with 827 additions and 8417 deletions

BIN
.DS_Store vendored

Binary file not shown.

49
.dockerignore Normal file
View File

@ -0,0 +1,49 @@
# Dependency directories
node_modules
npm-debug.log
# Build directory
/dist
# Various log files
logs
*.log
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Editor directories and files
.editorconfig
.idea
*.suo
*.ntvs*
*.njsproj
*.sln
.vs/
.vscode/
# Operating system files
.DS_Store
Thumbs.db
# Optional npm cache directory
.npm
# Optional eslint cache
.eslintcache
# Optional gitignore file
.gitignore
# Optional markdown files
*.md
# Optional configuration files
*.env
*.yml
# Other unnecessary files
*.tar.gz
*.zip
*.tgz
*.gzip

41
.gitignore vendored
View File

@ -1,8 +1,35 @@
node_modules # Dependency directories
*.log* node_modules/
.nuxt yarn.lock
.nitro package-lock.json
.cache
.output # Nuxt build output
.nuxt/
dist/
.nuxt-build/
.output/
# Production build files
build/
# Generated files
.nuxtignore
nuxt.config.js
nuxt.config.ts
# Dotfiles and directories
.env .env
dist .env.*
!.env.example
.DS_Store
.gitattributes
.editorconfig
.vscode/
# Log files
npm-debug.log*
yarn-debug.log*
yarn-error.log*
# Local development
local/

50
Dockerfile Normal file
View File

@ -0,0 +1,50 @@
#FROM node:18.14.2 as builder1
#WORKDIR /app
#ENV NODE_OPTIONS=--openssl-legacy-provider
#COPY package*.json ./
#RUN npm install
#COPY . .
#FROM node:18.14.2 as run1
#WORKDIR /app
#COPY package*.json ./
#COPY --from=builder /app/.output /.output
#EXPOSE 3000
#CMD [ "npm", "run", "start" ]
# Stage 1: Build
FROM node:18.14.2 as builder
WORKDIR /app
# Install only necessary dependencies for building
COPY package*.json ./
RUN npm install --only=production
# Copy the necessary source files to build your application
COPY . .
# Build the application
# Add any build scripts here. For example, if you're using a framework
# like Next.js, you might run `npm run build` here.
RUN npx nuxt build
# Stage 2: Runtime
FROM node:18.14.2-slim as run
WORKDIR /app
# Copy only necessary files from the builder stage
COPY --from=builder /app/package*.json ./
COPY --from=builder /app/node_modules ./node_modules
# If you have any build output, copy it here
COPY --from=builder /app/.output ./.output
# Copy only the production dependencies
RUN npm prune --production
EXPOSE 3000
CMD [ "npm", "run", "start" ]

View File

@ -1,6 +1,63 @@
pro Messpunkt
125ms
LAF90
LAF10
TargetGain
CurrentGain
MicrofonSignal clear
MicofoneSigal gedämpft (AWeighted)
[time: 125, values: [35,50,50,40]
# Nuxt 3 Minimal Starter # Nuxt 3 Minimal Starter
Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more. Look at the [Nuxt 3 documentation](https://nuxt.com/docs/getting-started/introduction) to learn more.
## Preparation
To run the project you need to have node 19 installed. As the current version is newer you cannot just start the application if you have downloaded and installed node on your machine. In docker this is solved by the docker script but to run test on your application you need to install the node version manager (nvm).
1. Install nvm using your terminal:
curl -o- https://raw.githubusercontent.com/nvm-sh/nvm/v0.39.1/install.sh | bash
2. Make nvm available in your terminal. Note: If you install a programm like you cannot just access it via the terminal because it is not registered in the list of commands for your terminal. What you need to do is register it manually. Just paste the following command and hit enter.
export NVM_DIR="$([ -z "${XDG_CONFIG_HOME-}" ] && printf %s "${HOME}/.nvm" || printf %s "${XDG_CONFIG_HOME}/nvm")"
[ -s "$NVM_DIR/nvm.sh" ] && \. "$NVM_DIR/nvm.sh" # This loads nvm
3. You have now nvm available in your terminal so you will install node with version 19 by the command
nvm install 19
4. Now you set the node version of your current terminal to 19. This is necessary every time you run a new terminal, because the default node version is the newest. Just type
nvm use 19
## Setup ## Setup
@ -40,3 +97,5 @@ npm run preview
``` ```
Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information. Check out the [deployment documentation](https://nuxt.com/docs/getting-started/deployment) for more information.

112
components/BandProcessor.vue Executable file
View File

@ -0,0 +1,112 @@
<template>
<div>
<button @click="startMicrophone">Start Microphone</button>
<button @click="stopMicrophone">Stop Microphone</button>
<div v-if="errorMessage">{{ errorMessage }}</div>
</div>
</template>
<script>
export default {
mounted() {
console.log("mounted Bandprocessor");
//this.initAudioContext();
},
data() {
return {
audioContext: null,
microphoneStream: null,
audioProcessorNode: null,
errorMessage: null,
};
},
methods: {
async initAudioContext() {
try {
const audioContext = new AudioContext();
console.log("Current URL:", window.location.href);
await audioContext.audioWorklet.addModule("/scripts/octave.js"
);
// Now the 'bandpass-processor' is registered and can be used
} catch (e) {
console.error("Error loading audio worklet:", e);
}
},
async startMicrophone() {
console.log("Start initialization of AudioContext");
try {
this.initializeAudioContext();
await this.requestMicrophoneAccess();
await this.setupAudioProcessing();
console.log("AudioContext sucessful initialized");
} catch (error) {
console.error("Error starting microphone:", error.message);
this.errorMessage = error.message;
}
},
stopMicrophone() {
this.cleanup();
},
initializeAudioContext() {
this.audioContext = new AudioContext();
},
async requestMicrophoneAccess() {
try {
this.microphoneStream = await navigator.mediaDevices.getUserMedia({
audio: true,
});
} catch (error) {
console.error("Error accessing microphone:", error.message);
this.errorMessage =
"Microphone access denied. Please grant permission in your browser settings.";
throw new Error("Microphone access denied.");
}
if (
!this.microphoneStream ||
!(this.microphoneStream instanceof MediaStream)
) {
throw new Error("Microphone stream is not available.");
}
},
async setupAudioProcessing() {
try {
const microphoneSource = this.audioContext.createMediaStreamSource(
this.microphoneStream
);
await this.audioContext.audioWorklet.addModule("/scripts/octave.js");
this.audioProcessorNode = new AudioWorkletNode(this.audioContext, 'octave');
microphoneSource.connect(this.audioProcessorNode);
this.audioProcessorNode.connect(this.audioContext.destination);
} catch (error) {
console.error("Error setting up audio processing:", error.message);
this.errorMessage =
"Error setting up audio processing. Please check your microphone and try again.";
throw new Error("Audio processing setup failed.");
}
},
cleanup() {
if (this.audioContext) {
if (this.audioProcessorNode) {
this.audioProcessorNode.disconnect();
this.audioProcessorNode.port.postMessage({ command: "stop" });
}
this.audioContext.close();
this.resetVariables();
}
},
resetVariables() {
this.audioContext = null;
this.microphoneStream = null;
this.audioProcessorNode = null;
this.errorMessage = null;
},
},
beforeDestroy() {
this.cleanup();
},
};
</script>

View File

@ -137,7 +137,7 @@
<span><i class="fa-solid fs-3 fa-arrow-left-long" style="cursor: pointer" data-bs-dismiss="modal"></i></span> <span><i class="fa-solid fs-3 fa-arrow-left-long" style="cursor: pointer" data-bs-dismiss="modal"></i></span>
</div> </div>
<div class="col-8"> <div class="col-8">
<h4 class="text-center fw-bolder">Adaptive Soundscape</h4> <h4 class="text-center fw-bolder">{{t('Adaptive Soundscape')}} </h4>
</div> </div>
<div class="col-2"> <div class="col-2">
<div class="form-check form-switch float-end"> <div class="form-check form-switch float-end">
@ -148,9 +148,9 @@
</div> </div>
<div class="row px-2 pt-4"> <div class="row px-2 pt-4">
<div class="col-12 ps-3 fs-5" style="line-height: 25px"> <div class="col-12 ps-3 fs-5" style="line-height: 25px">
<p class="p-0 m-0"> The Mindboost sounscape responds to the acousitcs in your room.</p> <p class="p-0 m-0"> {{t('The Mindboost sounscape responds to the acousitcs in your room.')}}</p>
<p class="p-0 m-0"></p> <p class="p-0 m-0"></p>
<p class="">Currently, your room has a:</p> <p class="">{{t('Currently, your room has a:')}}</p>
</div> </div>
</div> </div>
<div class="row pt-4 ps-3"> <div class="row pt-4 ps-3">
@ -162,8 +162,8 @@
</span> </span>
</div> </div>
<div class="col-11 col-md-10 ps-3"> <div class="col-11 col-md-10 ps-3">
<h5>Noisy Environment</h5> <h5>{{t('Noisy Environment')}}</h5>
<p>The background noise at your workplace is disturbing. Your concentration is severely impaired. Mindboost protects you from the disturbing background noise.</p> <p>{{ t('The background noise at your workplace is disturbing. Your concentration is severely impaired. Mindboost protects you from the disturbing background noise.')}}</p>
</div> </div>
</div> </div>
@ -177,8 +177,8 @@
</span> </span>
</div> </div>
<div class="col-11 col-md-10 ps-3"> <div class="col-11 col-md-10 ps-3">
<h5>Medium-noise Environment</h5> <h5>{{t('Medium-noise Environment')}}</h5>
<p>The background noise at your workplace should be optimized. In the long term, it could disturb and have a negative impact on your health. Protect yourself with mindboost.</p> <p>{{t('The background noise at your workplace should be optimized. In the long term, it could disturb and have a negative impact on your health. Protect yourself with mindboost.')}}</p>
</div> </div>
</div> </div>
@ -191,9 +191,9 @@
</span> </span>
</div> </div>
<div class="col-11 col-md-10 ps-3"> <div class="col-11 col-md-10 ps-3">
<h5>Good Environment</h5> <h5>{{ t('Good Environment')}}</h5>
<p> <p>
The background noise at your workplace provides a longterm healthy basis for concentrated work. With Mindboost you make sure that even sudden disturbances do not distract you. {{t('The background noise at your workplace provides a longterm healthy basis for concentrated work. With Mindboost you make sure that even sudden disturbances do not distract you.')}}
</p> </p>
</div> </div>
</div> </div>

View File

@ -285,6 +285,13 @@ export default defineNuxtConfig({
"All soundscapes in Mindboost have been tested and optimized in listening tests in cooperation with the Fraunhofer IBP. So you can be sure that Mindboost supports you optimally with your concentration.":"All soundscapes in Mindboost have been tested and optimized in listening tests in cooperation with the Fraunhofer IBP. So you can be sure that Mindboost supports you optimally with your concentration.", "All soundscapes in Mindboost have been tested and optimized in listening tests in cooperation with the Fraunhofer IBP. So you can be sure that Mindboost supports you optimally with your concentration.":"All soundscapes in Mindboost have been tested and optimized in listening tests in cooperation with the Fraunhofer IBP. So you can be sure that Mindboost supports you optimally with your concentration.",
'Audio_Output':'Audio Output', 'Audio_Output':'Audio Output',
'Audio_Input':'Audio Input', 'Audio_Input':'Audio Input',
'Language':'Language',
'How is your audio hardware connected?':'How is your audio hardware connected?',
'select laptop or mobile device microphone':'select laptop or mobile device microphone',
'select headphones or headphone output':'select headphones or headphone output',
'Output device:':'Output device',
'Input device:':'Input device:',
'As input, please select the microphone of your laptop or mobile device not of your headphones.':'As input, please select the microphone of your laptop or mobile device not of your headphones.',
}, },
de: { de: {
"welcome": "Willkommen", "welcome": "Willkommen",
@ -491,6 +498,13 @@ export default defineNuxtConfig({
"gemäß Art. 77 DSGVO bei einer Aufsichtsbehörde zu beschweren. In der Regel können Sie sich hierfür an die Aufsichtsbehörde an Ihrem üblichen Aufenthaltsort oder Arbeitsplatz oder unserem Firmensitz wenden.":"gemäß Art. 77 DSGVO bei einer Aufsichtsbehörde zu beschweren. In der Regel können Sie sich hierfür an die Aufsichtsbehörde an Ihrem üblichen Aufenthaltsort oder Arbeitsplatz oder unserem Firmensitz wenden.", "gemäß Art. 77 DSGVO bei einer Aufsichtsbehörde zu beschweren. In der Regel können Sie sich hierfür an die Aufsichtsbehörde an Ihrem üblichen Aufenthaltsort oder Arbeitsplatz oder unserem Firmensitz wenden.":"gemäß Art. 77 DSGVO bei einer Aufsichtsbehörde zu beschweren. In der Regel können Sie sich hierfür an die Aufsichtsbehörde an Ihrem üblichen Aufenthaltsort oder Arbeitsplatz oder unserem Firmensitz wenden.",
'Audio_Output':'Audioausgang', 'Audio_Output':'Audioausgang',
'Audio_Input':'Audioeingang', 'Audio_Input':'Audioeingang',
'Language':'Sprache',
'How is your audio hardware connected?':'Wie ist Ihre Audio-Hardware angeschlossen?',
'select laptop or mobile device microphone':'Eingangsgerät:',
'select headphones or headphone output':'(Mikrofon von Laptop oder Mobilgerät auswählen)',
'Output device:':'Ausgangsgerät:',
'Input device:':'Eingangsgerät:',
'As input, please select the microphone of your laptop or mobile device not of your headphones.':'Als Eingang wählen Sie bitte das Mikrofon Ihres Laptops oder mobilen Geräts - nicht das Ihres Kopfhörers.',
} }
} }
} }

8379
package-lock.json generated

File diff suppressed because it is too large Load Diff

View File

@ -1,21 +1,23 @@
{ {
"private": true, "private": true,
"scripts": { "scripts": {
"build": "nuxt build", "build": "npx nuxt build",
"dev": "nuxt dev", "dev": "npx nuxt dev",
"generate": "nuxt generate", "generate": "npx nuxt generate",
"preview": "nuxt preview", "preview": "npx nuxt preview",
"postinstall": "nuxt prepare" "postinstall": "npx nuxt prepare",
"start": "node .output/server/index.mjs"
}, },
"devDependencies": { "devDependencies": {
"@nuxt/vite-builder": "^3.0.0", "@nuxt/vite-builder": "^3.0.0",
"@nuxtjs/i18n": "^8.0.0-beta.10",
"@nuxtjs/tailwindcss": "^6.1.3", "@nuxtjs/tailwindcss": "^6.1.3",
"nuxt": "3.0.0",
"nuxt-headlessui": "^1.0.4", "nuxt-headlessui": "^1.0.4",
"vue-stripe-js": "^1.0.1" "vue-stripe-js": "^1.0.1"
}, },
"dependencies": { "dependencies": {
"nuxt": "3.0.0",
"@nuxtjs/i18n": "^8.0.0-beta.10",
"nuxt-headlessui": "^1.0.4",
"@heroicons/vue": "^2.0.13", "@heroicons/vue": "^2.0.13",
"@incuca/vue3-toaster": "^1.1.1", "@incuca/vue3-toaster": "^1.1.1",
"@meforma/vue-toaster": "^1.3.0", "@meforma/vue-toaster": "^1.3.0",

89
pages/ChannelStreamer.vue Normal file
View File

@ -0,0 +1,89 @@
<template>
<div>
<button @click="startMicrophone">Start Microphone</button>
<button @click="stopMicrophone">Stop Microphone</button>
<canvas ref="visualizationCanvas" width="800" height="200"></canvas>
<div v-if="errorMessage">{{ errorMessage }}</div>
</div>
</template>
<script>
export default {
data() {
return {
audioContext: null,
microphoneStream: null,
audioProcessorNode: null,
errorMessage: null,
};
},
methods: {
async startMicrophone() {
try {
this.initializeAudioContext();
await this.requestMicrophoneAccess();
await this.setupAudioProcessing();
} catch (error) {
console.error('Error starting microphone:', error.message);
this.errorMessage = error.message;
}
finally{
console.log("Microphone started")
}
},
stopMicrophone() {
this.cleanup();
},
initializeAudioContext() {
this.audioContext = new window.AudioContext();
},
async requestMicrophoneAccess() {
try {
this.microphoneStream = await navigator.mediaDevices.getUserMedia({ audio: true });
} catch (error) {
console.error('Error accessing microphone:', error.message);
this.errorMessage = 'Microphone access denied. Please grant permission in your browser settings.';
throw new Error('Microphone access denied.');
}
if (!this.microphoneStream || !(this.microphoneStream instanceof MediaStream)) {
throw new Error('Microphone stream is not available.');
}
},
async setupAudioProcessing() {
try {
const microphoneSource = this.audioContext.createMediaStreamSource(this.microphoneStream);
await this.audioContext.audioWorklet.addModule('/scripts/octave2.js');
//this.audioProcessorNode = new AudioWorkletNode(this.audioContext, 'octave');
//microphoneSource.connect(this.audioProcessorNode);
//this.audioProcessorNode.connect(this.audioContext.destination);
} catch (error) {
console.error('Error setting up audio processing:', error.message);
this.errorMessage = 'Error setting up audio processing. Please check your microphone and try again.';
throw new Error('Audio processing setup failed.');
}
},
cleanup() {
if (this.audioContext) {
if (this.audioProcessorNode) {
this.audioProcessorNode.disconnect();
this.audioProcessorNode.port.postMessage({ command: 'stop' });
}
this.audioContext.close();
this.resetVariables();
}
},
resetVariables() {
this.audioContext = null;
this.microphoneStream = null;
this.audioProcessorNode = null;
this.errorMessage = null;
},
},
beforeDestroy() {
this.cleanup();
},
};
</script>

View File

@ -6,6 +6,7 @@
poster="/images/poster.png" poster="/images/poster.png"
> >
<div class="container-fluid overflow-auto" > <div class="container-fluid overflow-auto" >
<div class="row"> <BandProcessor /> <ChannelStreamer /> </div>
<div class="row "> <div class="row ">
<div class="col-12 col-lg-4 bg-img d-none d-lg-block" style="background-image: url('/images/login.svg');background-size: cover;height: 100vh;" > <div class="col-12 col-lg-4 bg-img d-none d-lg-block" style="background-image: url('/images/login.svg');background-size: cover;height: 100vh;" >
</div> </div>
@ -76,7 +77,10 @@
import backgroundImagePath from '~/assets/image/login4.png'; import backgroundImagePath from '~/assets/image/login4.png';
import {useUserStore} from '@/stores/user'; import {useUserStore} from '@/stores/user';
import {mapState,mapActions} from "pinia"; import {mapState,mapActions} from "pinia";
import BandProcessor from "@/components/BandProcessor";
import ChannelStreamer from "@/pages/ChannelStreamer";
export default { export default {
setup() { setup() {
const { t } = useI18n() const { t } = useI18n()
const localePath = useLocalePath() const localePath = useLocalePath()
@ -86,6 +90,9 @@ export default {
localePath, localePath,
} }
}, },
//Components BEGIN
components: {BandProcessor,ChannelStreamer},
//Components END
mounted() { mounted() {
// if (this.is_login){ // if (this.is_login){
// this.$router.push('/onboarding'); // this.$router.push('/onboarding');

View File

@ -37,7 +37,7 @@
<div class="col-12 text-center pt-5 mt-3 pb-2 mb-2" > <div class="col-12 text-center pt-5 mt-3 pb-2 mb-2" >
<NuxtLink class="btn btn-warning px-2 mx-1" exact-active-class="px-4 mx-2" :to="localePath('/onboarding/selectinput')"></NuxtLink> <NuxtLink class="btn btn-warning px-2 mx-1" exact-active-class="px-4 mx-2" :to="localePath('/onboarding/selectinput')"></NuxtLink>
<NuxtLink class="btn btn-warning px-2" exact-active-class="px-4 mx-2" :to="localePath('/onboarding')"></NuxtLink> <NuxtLink class="btn btn-warning px-2" exact-active-class="px-4 mx-2" :to="localePath('/onboarding')"></NuxtLink>
<NuxtLink class="btn btn-warning mx-2" exact-active-class="px-4 mx-2" :to="localePath('/onboarding/onboarding2')"></NuxtLink> <NuxtLink class="btn btn-warning px-2 mx-2" exact-active-class="px-4 mx-2" :to="localePath('/onboarding/onboarding2')"></NuxtLink>
<NuxtLink class="btn btn-warning px-2 " exact-active-class="px-4 mx-2" :to="localePath('/onboarding/onboarding3')"></NuxtLink> <NuxtLink class="btn btn-warning px-2 " exact-active-class="px-4 mx-2" :to="localePath('/onboarding/onboarding3')"></NuxtLink>
<NuxtLink class="btn btn-warning px-2 mx-2" exact-active-class="px-4 mx-2" :to="localePath('/onboarding/onboarding4')"></NuxtLink> <NuxtLink class="btn btn-warning px-2 mx-2" exact-active-class="px-4 mx-2" :to="localePath('/onboarding/onboarding4')"></NuxtLink>
<h6 class="text-muted text-center pt-3">You can customize your selection later</h6> <h6 class="text-muted text-center pt-3">You can customize your selection later</h6>

View File

@ -3,8 +3,8 @@
<div class="row"> <div class="row">
<div class="col-12 "> <div class="col-12 ">
<h4 class="text-center fw-bold pt-5">{{t("How is your audio hardware connected?")}}</h4> <h4 class="text-center fw-bold pt-5">{{t("How is your audio hardware connected?")}}</h4>
<p class="text-center mb-0 pb-0 text-muted">As input, please select the microphone of your laptop or mobile device not of your headphones.</p> <p class="text-center mb-0 pb-0 text-muted">{{t('As input, please select the microphone of your laptop or mobile device not of your headphones.')}}</p>
<p class="text-center mt-0 pt-0 text-muted">To use Mindboost, headphones are required.</p> <p class="text-center mt-0 pt-0 text-muted">{{t('To use Mindboost, headphones are required.')}}</p>
</div> </div>
</div> </div>
<div class="row justify-content-center"> <div class="row justify-content-center">
@ -12,8 +12,8 @@
<form> <form>
<div class="row justify-content-center "> <div class="row justify-content-center ">
<div class="col-md-3 text-center"> <div class="col-md-3 text-center">
<h6 class="pb-0 mb-0">Input device:</h6> <h6 class="pb-0 mb-0">{{t('Input device:')}}</h6>
<p class="pt-0 mt-0 text-muted pb-0 mb-0" style="font-size: 14px;font-weight: 500">(select laptop or mobile device microphone)</p> <p class="pt-0 mt-0 text-muted pb-0 mb-0" style="font-size: 14px;font-weight: 500">({{t('select laptop or mobile device microphone')}})</p>
<select class="form-select pt-1 mt-0 select-box " v-model="selectedInput"> <select class="form-select pt-1 mt-0 select-box " v-model="selectedInput">
<option :value="index" v-for="(item,index) in audioInputDevices" :key="index" >{{item.label}}</option> <option :value="index" v-for="(item,index) in audioInputDevices" :key="index" >{{item.label}}</option>
</select> </select>
@ -22,8 +22,8 @@
<div class="row justify-content-center pt-3"> <div class="row justify-content-center pt-3">
<div class="col-md-3 text-center"> <div class="col-md-3 text-center">
<h6 class="pb-0 mb-0 " >Output device:</h6> <h6 class="pb-0 mb-0 " >{{t('Output device:')}}</h6>
<p class="pt-0 mt-0 text-muted pb-0 mb-0" style="font-size: 14px;font-weight: 500">(select headphones or headphone output)</p> <p class="pt-0 mt-0 text-muted pb-0 mb-0" style="font-size: 14px;font-weight: 500">({{ t('select headphones or headphone output')}})</p>
<select class="form-select pt-1 mt-0 select-box " v-model="selectedOutput"> <select class="form-select pt-1 mt-0 select-box " v-model="selectedOutput">
<option :value="index" v-for="(item,index) in audioOutputDevices" :key="index">{{item.label}}</option> <option :value="index" v-for="(item,index) in audioOutputDevices" :key="index">{{item.label}}</option>
</select> </select>
@ -32,7 +32,7 @@
<div class="row justify-content-center pt-3"> <div class="row justify-content-center pt-3">
<div class="col-md-3 text-center" style="z-index: 1000000;"> <div class="col-md-3 text-center" style="z-index: 1000000;">
<a href="#" @click.prevent="saveDevices" style="z-index: 1000000" class="btn col-4 next-btn" >NEXT</a> <a href="#" @click.prevent="saveDevices" style="z-index: 1000000" class="btn col-4 next-btn" >{{t("Next")}}</a>
</div> </div>
</div> </div>
</form> </form>

View File

@ -37,6 +37,17 @@
<div class="invalid-feedback d-block" v-if="errors.password">{{errors.password[0]}}</div> <div class="invalid-feedback d-block" v-if="errors.password">{{errors.password[0]}}</div>
</div> </div>
</div> </div>
<div class="row pt-3">
<div class="col-12">
<label class="text-muted ">{{t("Language")}} </label>
<select @change="changeLanguage" v-model="form.language" class="form-select">
<option value="en">English</option>
<option value="de">German</option>
</select>
<div class="invalid-feedback d-block" v-if="errors.language">{{errors.language[0]}}</div>
</div>
</div>
<div class="row pt-5 "> <div class="row pt-5 ">
<div class="col-12 text-center"> <div class="col-12 text-center">
<button type="submit" class="btn text-white fs-5 col-12 fw-bold py-2 " style="background-color: #e9c046">{{t("Save Changes")}} <div v-if="loading" class="spinner-border spinner-border-sm" role="status"> <button type="submit" class="btn text-white fs-5 col-12 fw-bold py-2 " style="background-color: #e9c046">{{t("Save Changes")}} <div v-if="loading" class="spinner-border spinner-border-sm" role="status">
@ -64,14 +75,23 @@ export default {
}, },
setup(){ setup(){
const { t } = useI18n() const { t } = useI18n()
const localePath = useLocalePath() const localePath = useLocalePath();
return {t,localePath} const switchLocalePath = useSwitchLocalePath();
let changeLanguage=(event)=>{
console.log('switch',event.target.value)
useRouter().push(switchLocalePath(event.target.value));
// i18n.global.locale.value=
}
return {t,localePath,changeLanguage}
}, },
mounted() { mounted() {
console.log(this.user) console.log(this.user)
this.form.first_name=this.user.first_name; this.form.first_name=this.user.first_name;
this.form.email=this.user.email; this.form.email=this.user.email;
this.form.surname=this.user.surname; this.form.surname=this.user.surname;
this.form.language=this.user.language;
}, },
data(){ data(){
return { return {
@ -80,7 +100,8 @@ export default {
first_name:"", first_name:"",
surname:"", surname:"",
email:"email", email:"email",
password:"" password:"",
language:'en'
}, },
errors:[], errors:[],
} }
@ -90,6 +111,10 @@ export default {
...mapActions(useUserStore,['updateUser']), ...mapActions(useUserStore,['updateUser']),
saveUser(){ saveUser(){
this.loading=true; this.loading=true;
this.t.locale.value=this.form.language;
this.$axios.post('/api/account/update',this.form).then(({data})=>{ this.$axios.post('/api/account/update',this.form).then(({data})=>{
this.loading=false; this.loading=false;
if(data.success){ if(data.success){

View File

@ -38,6 +38,16 @@
<div class="col-6"> <div class="col-6">
<h5 class="fw-bold text-end"></h5> </div> <h5 class="fw-bold text-end"></h5> </div>
</div> </div>
<div class="row pt-4">
<div class="col-6">
<h5 class="fw-bold text-muted">{{ t('Language') }}</h5>
</div>
<div class="col-6">
<h5 v-if="user.language=='de'" class="fw-bold text-end">German</h5>
<h5 v-else class="fw-bold text-end">English</h5>
</div>
</div>
<div class="row pt-4"> <div class="row pt-4">
<div class="col-12 text-center"> <div class="col-12 text-center">
<button @click="logoutNow" class="btn col-12 col-sm-12 col-md-3 fw-bold btn-outline-dark">{{t("Log Out")}} </button> <button @click="logoutNow" class="btn col-12 col-sm-12 col-md-3 fw-bold btn-outline-dark">{{t("Log Out")}} </button>

BIN
public/.DS_Store vendored

Binary file not shown.

View File

180
public/scripts/octave.js Normal file
View File

@ -0,0 +1,180 @@
class OctaveBandProcessor extends AudioWorkletProcessor {
constructor() {
super()
// Define center frequencies for 9 octave bands
this.centerFrequencies = [63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000]
this.filters = []
this.lastUpdateTimestamp = 0
this.updateInterval = 0.125 // Update every 0.125 seconds
this.filteredMicSignal =[]
// Create an A-weighting filter for specific frequencies
this.createAWeightingFilter()
// Create bandpass filters for each center frequency
this.centerFrequencies.forEach((frequency) => {
const filter = new BiquadFilterNode(
audioContext,
{
type: "bandpass",
frequency: frequency,
Q: 1.41, // Set the desired Q value
},
this.filters.push(filter),
)
})
// Set up analyzers for calculating percentiles
this.setupAnalyzers()
}
setupAnalyzers() {
this.analyzers = []
this.centerFrequencies.forEach((frequency) => {
this.analyzers.push([])
for (let i = 0; i < 5; i++) {
// Unique identifiers from 0 to 4
const analyzer = audioContext.createAnalyser()
analyzer.fftSize = 2048
// Check if the identifier is 0 (microphone audio) before connecting to the A-weighting filter
if (i === 0) {
this.aWeightingFilter.connect(analyzer)
}
this.analyzers[this.analyzers.length - 1].push(analyzer)
}
})
}
process(inputs, outputs) {
const numOutputoctaves = filters.length
for (let i = 0; i < numOutputoctaves; i++) {
// const outputChannel = outputs[i][0]
const mic = inputs[0]
const maskingsig = inputs[1]
const harmonic = inputs[2]
// Apply the filter to the input channel
const filteredMicSignal[i] = this.filters[i].process(mic)
const filteredMaskSignal = this.filters[i].process(maskingsig)
const filteredHarmoSignal = this.filters[i].process(harmonic)
// Apply A-weighting only to the microphone signal (channel 0)
const aWeightedSignal[i] = this.aWeightingFilter.process(filteredMicSignal)
this.calculateRMSLevel(aWeightedSignal)
outputChannel.set(aWeightedSignal)
// For other channels, pass the signal without A-weighting
outputChannel.set(filteredSignal)
// Check if it's time to update percentiles
const currentTime = this.currentTime
if (currentTime - this.lastUpdateTimestamp >= this.updateInterval) {
this.updatePercentiles(i)
this.lastUpdateTimestamp = currentTime
}
}
return true
}
calculateRMSLevel(signal, channelIndex) {
const data = new Float32Array(signal.length)
signal.copyFromChannel(data, 0)
const sum = data.reduce((acc, val) => acc + val * val, 0)
const rmsLevel = Math.sqrt(sum / data.length)
const dBLevel = 20 * Math.log10(rmsLevel) // Convert to dB
return dBLevel
}
updatePercentiles(channelIndex) {
for (let i = 0; i < this.centerFrequencies.length; i++) {
//const analyzer = this.analyzers[i][channelIndex]
//const levelData = new Float32Array(analyzer.frequencyBinCount)
analyzer.getFloatFrequencyData(levelData)
// Calculate percentiles for each octave band and each channel
const percentile10 = this.calculatePercentile(levelData, 10)
const percentile90 = this.calculatePercentile(levelData, 90)
const percentileDiff = percentile10 - percentile90
// Store the percentile difference for each channel and each octave band
// You can use suitable data structures to store these values for future comparisons
}
}
calculatePercentile(data, percentile) {
const sortedData = data.slice().sort((a, b) => a - b)
const index = Math.floor((percentile / 100) * sortedData.length)
return sortedData[index]
}
createAWeightingFilter() {
// Use the provided A-weighting filter coefficients
const aWeightingCoefficients = [
0, -0.051, -0.142, -0.245, -0.383, -0.65, -1.293, -2.594, -6.554,
] //David
// Create a custom IIR filter node with the A-weighting coefficients
this.aWeightingFilter = new IIRFilterNode(audioContext, { //infinit Impuls Response
feedforward: aWeightingCoefficients,
feedback: [1],
})
}
// combineAndCalculate() {
// let LAF10_90_total = 0 // Initialize the total LAF10%-90%
// for (let i = 0; i < this.centerFrequencies.length; i++) {
// const micAnalyzer = this.analyzers[i][0] // Analyzer for microphone audio (identifier 0)
// const audioFile1Analyzer = this.analyzers[i][3] // Analyzer for audioFile1 (identifier 3)
// const audioFile2Analyzer = this.analyzers[i][4] // Analyzer for audioFile2 (identifier 4)
// // Calculate percentiles for the microphone audio
// const micPercentile10 = this.calculatePercentile(micAnalyzer, 10)
// const micPercentile90 = this.calculatePercentile(micAnalyzer, 90)
// // Calculate percentiles for audioFile1
// const audioFile1Percentile10 = this.calculatePercentile(
// audioFile1Analyzer,
// 10,
// )
// const audioFile1Percentile90 = this.calculatePercentile(
// audioFile1Analyzer,
// 90,
// )
// // Calculate percentiles for audioFile2
// const audioFile2Percentile10 = this.calculatePercentile(
// audioFile2Analyzer,
// 10,
// )
// const audioFile2Percentile90 = this.calculatePercentile(
// audioFile2Analyzer,
// 90,
// )
// // Calculate LAF10%-90% for microphone audio, audioFile1, and audioFile2 separately
// const micLAF10_90 = micPercentile10 - micPercentile90
// const audioFile1LAF10_90 = audioFile1Percentile10 - audioFile1Percentile90
// const audioFile2LAF10_90 = audioFile2Percentile10 - audioFile2Percentile90
// // Calculate combined LAF10%-90% for microphone audio, audioFile1, and audioFile2
// const combinedLAF10_90 =
// micLAF10_90 + audioFile1LAF10_90 + audioFile2LAF10_90
// // Add the combined LAF10%-90% to the total
// LAF10_90_total += combinedLAF10_90
// }
// // return LAF10_90_total;
// }
}
registerProcessor("octave", OctaveBandProcessor)

153
public/scripts/octave2.js Normal file
View File

@ -0,0 +1,153 @@
class OctaveBandProcessor extends AudioWorkletProcessor {
constructor() {
super();
// Define center frequencies for 9 octave bands
this.centerFrequencies = [63, 125, 250, 500, 1000, 2000, 4000, 8000, 16000];
this.filters = [];
this.lastUpdateTimestamp = 0;
this.updateInterval = 0.125; // Update every 0.125 seconds
// Create an A-weighting filter for specific frequencies
this.createAWeightingFilter();
// Create bandpass filters for each center frequency
this.centerFrequencies.forEach(frequency => {
const filter = new BiquadFilterNode(audioContext, {
type: 'bandpass',
frequency: frequency,
Q: 1.41, // Set the desired Q value
});
this.filters.push(filter);
});
// Set up analyzers for calculating percentiles
this.setupAnalyzers();
}
createAWeightingFilter() {
// Use the provided A-weighting filter coefficients
const aWeightingCoefficients = [0, -0.051, -0.142, -0.245, -0.383, -0.65, -1.293, -2.594, -6.554]; //David
// Create a custom IIR filter node with the A-weighting coefficients
this.aWeightingFilter = new IIRFilterNode(audioContext, {
feedforward: aWeightingCoefficients,
feedback: [1],
});
}
setupAnalyzers() {
this.analyzers = [];
this.centerFrequencies.forEach((frequency) => {
this.analyzers.push([]);
for (let i = 0; i < 5; i++) { // Unique identifiers from 0 to 4
const analyzer = audioContext.createAnalyser();
analyzer.fftSize = 2048;
// Check if the identifier is 0 (microphone audio) before connecting to the A-weighting filter
if (i === 0) {
this.aWeightingFilter.connect(analyzer);
}
this.analyzers[this.analyzers.length - 1].push(analyzer);
}
})
}
process(inputs, outputs) {
const numOutputChannels = outputs.length;
for (let i = 0; i < numOutputChannels; i++) {
const outputChannel = outputs[i][0];
const inputChannel = inputs[i][0];
// Apply the filter to the input channel
const filteredSignal = this.filters[i].process(inputChannel);
// Apply A-weighting only to the microphone signal (channel 0)
if (i === 0) {
const aWeightedSignal = this.aWeightingFilter.process(filteredSignal);
outputChannel.set(aWeightedSignal);
} else {
// For other channels, pass the signal without A-weighting
outputChannel.set(filteredSignal);
}
// Check if it's time to update percentiles
const currentTime = this.currentTime;
if (currentTime - this.lastUpdateTimestamp >= this.updateInterval) {
this.updatePercentiles(i);
this.lastUpdateTimestamp = currentTime;
}
}
return true;
}
calculateRMSLevel(signal, channelIndex) {
const data = new Float32Array(signal.length);
signal.copyFromChannel(data, 0);
const sum = data.reduce((acc, val) => acc + val * val, 0);
const rmsLevel = Math.sqrt(sum / data.length);
const dBLevel = 20 * Math.log10(rmsLevel); // Convert to dB
return dBLevel;
}
updatePercentiles(channelIndex) {
for (let i = 0; i < this.centerFrequencies.length; i++) {
const analyzer = this.analyzers[i][channelIndex];
const levelData = new Float32Array(analyzer.frequencyBinCount);
analyzer.getFloatFrequencyData(levelData);
// Calculate percentiles for each octave band and each channel
const percentile10 = this.calculatePercentile(levelData, 10);
const percentile90 = this.calculatePercentile(levelData, 90);
const percentileDiff = percentile10 - percentile90;
// Store the percentile difference for each channel and each octave band
// You can use suitable data structures to store these values for future comparisons
}
}
calculatePercentile(data, percentile) {
const sortedData = data.slice().sort((a, b) => a - b);
const index = Math.floor((percentile / 100) * sortedData.length);
return sortedData[index];
}
combineAndCalculate() {
let LAF10_90_total = 0; // Initialize the total LAF10%-90%
for (let i = 0; i < this.centerFrequencies.length; i++) {
const micAnalyzer = this.analyzers[i][0]; // Analyzer for microphone audio (identifier 0)
const audioFile1Analyzer = this.analyzers[i][3]; // Analyzer for audioFile1 (identifier 3)
const audioFile2Analyzer = this.analyzers[i][4]; // Analyzer for audioFile2 (identifier 4)
// Calculate percentiles for the microphone audio
const micPercentile10 = this.calculatePercentile(micAnalyzer, 10);
const micPercentile90 = this.calculatePercentile(micAnalyzer, 90);
// Calculate percentiles for audioFile1
const audioFile1Percentile10 = this.calculatePercentile(audioFile1Analyzer, 10);
const audioFile1Percentile90 = this.calculatePercentile(audioFile1Analyzer, 90);
// Calculate percentiles for audioFile2
const audioFile2Percentile10 = this.calculatePercentile(audioFile2Analyzer, 10);
const audioFile2Percentile90 = this.calculatePercentile(audioFile2Analyzer, 90);
// Calculate LAF10%-90% for microphone audio, audioFile1, and audioFile2 separately
const micLAF10_90 = micPercentile10 - micPercentile90;
const audioFile1LAF10_90 = audioFile1Percentile10 - audioFile1Percentile90;
const audioFile2LAF10_90 = audioFile2Percentile10 - audioFile2Percentile90;
// Calculate combined LAF10%-90% for microphone audio, audioFile1, and audioFile2
const combinedLAF10_90 = micLAF10_90 + audioFile1LAF10_90 + audioFile2LAF10_90;
// Add the combined LAF10%-90% to the total
LAF10_90_total += combinedLAF10_90;
}
return LAF10_90_total;
}
}
registerProcessor('octave', OctaveBandProcessor);

View File

@ -2471,10 +2471,10 @@ es-module-lexer@^0.9.0:
resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz" resolved "https://registry.npmjs.org/es-module-lexer/-/es-module-lexer-0.9.3.tgz"
integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ== integrity sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==
esbuild-darwin-arm64@0.15.18: esbuild-darwin-64@0.15.18:
version "0.15.18" version "0.15.18"
resolved "https://registry.npmjs.org/esbuild-darwin-arm64/-/esbuild-darwin-arm64-0.15.18.tgz" resolved "https://registry.npmjs.org/esbuild-darwin-64/-/esbuild-darwin-64-0.15.18.tgz"
integrity sha512-tKPSxcTJ5OmNb1btVikATJ8NftlyNlc8BVNtyT/UAr62JFOhwHlnoPrhYWz09akBLHI9nElFVfWSTSRsrZiDUA== integrity sha512-2WAvs95uPnVJPuYKP0Eqx+Dl/jaYseZEUUT1sjg97TJa4oBtbAKnPnl3b5M9l51/nbx7+QAEtuummJZW0sBEmg==
esbuild@^0.15.14, esbuild@^0.15.9: esbuild@^0.15.14, esbuild@^0.15.9:
version "0.15.18" version "0.15.18"
@ -4082,7 +4082,7 @@ nuxt-headlessui@^1.0.4:
"@nuxt/kit" "^3.0.0" "@nuxt/kit" "^3.0.0"
pathe "^1.0.0" pathe "^1.0.0"
nuxt@3.0.0: nuxt@^3.0.0:
version "3.0.0" version "3.0.0"
resolved "https://registry.npmjs.org/nuxt/-/nuxt-3.0.0.tgz" resolved "https://registry.npmjs.org/nuxt/-/nuxt-3.0.0.tgz"
integrity sha512-RNlD78uv04ZiXWmlx9f1tnJfrqsYAWHU+4gbgOTQpIBmQzHWPWiox+fm/1m93iKfEd5sJi9TJUoXX5yBObVZYw== integrity sha512-RNlD78uv04ZiXWmlx9f1tnJfrqsYAWHU+4gbgOTQpIBmQzHWPWiox+fm/1m93iKfEd5sJi9TJUoXX5yBObVZYw==