Skip to content

Commit

Permalink
Merge branch 'next'
Browse files Browse the repository at this point in the history
  • Loading branch information
Eliastik committed Jun 15, 2024
2 parents eacc3e4 + a92fa1e commit e8e80d7
Show file tree
Hide file tree
Showing 26 changed files with 1,478 additions and 510 deletions.
6 changes: 6 additions & 0 deletions .github/workflows/build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,3 +23,9 @@ jobs:

- name: Build and Lint
run: npm run build

- name: Install Playwright Browsers
run: npx playwright install --with-deps

- name: Run Playwright tests
run: npm run test
4 changes: 4 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -41,3 +41,7 @@ public/workbox-*
public/worklets/*
public/workers/*
.rollup.cache
/test-results/
/playwright-report/
/blob-report/
/playwright/.cache/
5 changes: 4 additions & 1 deletion ROADMAP.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,16 +66,19 @@
* [x] - (Minor) Enable reverb filter when choosing custom environment, and no other environment was downloaded (network error)
* [ ] - (Major) Fix Soundtouch Worklet audio speed adjustment not working (as now, fallback to classic script processor node, not working in Firefox) - use another library for time stretch?
* [ ] - If fixed: enable Soundtouch Worklet in compatibility/direct mode
* [x] - (Minor) When compatibility mode is enabled and initial audio rendering is disabled, when loading a new audio file then playing audio, the compatibility mode setting is ignored (normal mode instead)
* [ ] - (Medium) Bug when changing recorder settings on Chrome mobile
* [ ] - (Medium) Vocoder doesn't work well on sample rate > 96,000 Hz
* [ ] - (Very minor) Fix sourcemaps for libraries + worklets copy (cache)

### Would be good but not important

* [x] - Simplify code: split AudioEditor classe into smaller classes
* [x] - Use dependency injection (instead of Singleton) - for AudioEditor subclasses and AudioEditorObjectsSingleton
* [x] - Unit tests (Jest) + E2E tests (Playwright)
* [ ] - Create new filters (equalizer?, volume/gain?)
* [ ] - If adding new filters: hide some advanced filters, and make possible to add them if needed in the UI
* [ ] - Save into localstorage filters settings + filter presets that can be set by the user?
* [ ] - Use dependency injection instead of Singleton?
* [ ] - Graphical visualization of audio + apply filters in a portion of the audio?
* [ ] - Real-time filters editing when recording audio (reuse existing filters)?
* [ ] - Enhance limiter?
Expand Down
1,052 changes: 586 additions & 466 deletions package-lock.json

Large diffs are not rendered by default.

42 changes: 24 additions & 18 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,12 @@
"dev": "next dev",
"build": "next build",
"start": "next start",
"lint": "next lint"
"lint": "next lint",
"test": "npx playwright test tests/voicerecorder.spec.ts"
},
"repository": {
"type": "git",
"url": "git+https://github.com/Eliastik/simple-voice-changer.git"
},
"repository": {
"type": "git",
Expand All @@ -15,34 +20,35 @@
"dependencies": {
"@eliastik/simple-sound-studio-components": "^2.2.0",
"@eliastik/simple-sound-studio-lib": "^2.2.0",
"i18next": "^23.10.1",
"i18next": "^23.11.5",
"lodash": "^4.17.21",
"next": "^14.1.4",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-i18next": "^14.1.0",
"semver": "^7.6.0"
"next": "^14.2.3",
"react": "^18.3.1",
"react-dom": "^18.3.1",
"react-i18next": "^14.1.2",
"semver": "^7.6.2"
},
"devDependencies": {
"@playwright/test": "^1.44.1",
"@types/audioworklet": "^0.0.54",
"@types/lodash": "^4.17.0",
"@types/node": "^20.12.2",
"@types/react": "^18.2.70",
"@types/react-dom": "^18.2.22",
"@types/lodash": "^4.17.4",
"@types/node": "^20.13.0",
"@types/react": "^18.3.3",
"@types/react-dom": "^18.3.0",
"@types/semver": "^7.5.8",
"@typescript-eslint/eslint-plugin": "^7.4.0",
"@typescript-eslint/parser": "^7.4.0",
"@typescript-eslint/eslint-plugin": "^7.11.0",
"@typescript-eslint/parser": "^7.11.0",
"autoprefixer": "^10.4.19",
"copy-webpack-plugin": "^12.0.2",
"daisyui": "^4.9.0",
"daisyui": "^4.11.1",
"dotenv": "^16.4.5",
"eslint": "^8.57.0",
"eslint-config-next": "^14.1.4",
"eslint-plugin-react": "^7.34.1",
"glob": "^10.3.10",
"eslint-config-next": "^14.2.3",
"eslint-plugin-react": "^7.34.2",
"glob": "^10.4.1",
"next-pwa": "^5.6.0",
"postcss": "^8.4.38",
"tailwindcss": "^3.4.3",
"typescript": "^5.4.3"
"typescript": "^5.4.5"
}
}
36 changes: 36 additions & 0 deletions playwright.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import { defineConfig, devices } from "@playwright/test";

/**
* See https://playwright.dev/docs/test-configuration.
*/
export default defineConfig({
testDir: "./tests",
fullyParallel: true,
forbidOnly: !!process.env.CI,
retries: process.env.CI ? 2 : 0,
workers: process.env.CI ? 1 : undefined,
reporter: "html",
use: {
trace: "on-first-retry",
},
projects: [
{
name: "chromium",
use: {
...devices["Desktop Chrome"]
}
},
{
name: "firefox",
use: {
...devices["Desktop Firefox"],
permissions: ["microphone"]
},
},
],
webServer: {
command: "npm run dev",
url: "http://127.0.0.1:3000",
reuseExistingServer: !process.env.CI,
}
});
13 changes: 7 additions & 6 deletions src/app/components/audioEditor/AudioPlayer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,11 @@ const AudioPlayer = () => {

return (
<>
<div className="fixed bottom-0 w-full">
<div className="fixed bottom-0 w-full" id="audioPlayer">
<div className="block w-full">
<input
type="range"
id="audioPlayerProgress"
min={0}
max={maxTime}
value={currentTime}
Expand All @@ -46,28 +47,28 @@ const AudioPlayer = () => {
<div className="flex items-center justify-between w-full bg-base-300">
<div className="flex items-center">
{!playing &&
<div className="tooltip" data-tip={t("audioPlayer.play")}><button className="btn btn-ghost" onClick={() => playAudioBuffer()}>
<div className="tooltip" id="playButton" data-tip={t("audioPlayer.play")}><button className="btn btn-ghost" onClick={() => playAudioBuffer()}>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
<path strokeLinecap="round" strokeLinejoin="round" d="M5.25 5.653c0-.856.917-1.398 1.667-.986l11.54 6.348a1.125 1.125 0 010 1.971l-11.54 6.347a1.125 1.125 0 01-1.667-.985V5.653z" />
</svg>
</button></div>}
{playing && !isCompatibilityModeEnabled &&
<div className="tooltip" data-tip={t("audioPlayer.pause")}><button className="btn btn-ghost" onClick={() => pauseAudioBuffer()}>
<div className="tooltip" id="pauseButton" data-tip={t("audioPlayer.pause")}><button className="btn btn-ghost" onClick={() => pauseAudioBuffer()}>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
<path strokeLinecap="round" strokeLinejoin="round" d="M15.75 5.25v13.5m-7.5-13.5v13.5" />
</svg>
</button></div>}
{playing && isCompatibilityModeEnabled &&
<div className="tooltip" data-tip={t("audioPlayer.stop")}><button className="btn btn-ghost" onClick={() => stopAudioBuffer()}>
<div className="tooltip" id="stopPlayingButton" data-tip={t("audioPlayer.stop")}><button className="btn btn-ghost" onClick={() => stopAudioBuffer()}>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
<path strokeLinecap="round" strokeLinejoin="round" d="M5.25 7.5A2.25 2.25 0 017.5 5.25h9a2.25 2.25 0 012.25 2.25v9a2.25 2.25 0 01-2.25 2.25h-9a2.25 2.25 0 01-2.25-2.25v-9z" />
</svg>
</button></div>}
<span className="ml-4 pointer-events-none">{currentTimeDisplay} / {maxTimeDisplay}</span>
<span className="ml-4 pointer-events-none" id="playerTimestamp"><span id="playerCurrentTime">{currentTimeDisplay}</span> / <span id="playerMaxTime">{maxTimeDisplay}</span></span>
</div>
<div className="flex items-center">
<div className="tooltip" data-tip={t("audioPlayer.loop")}>
<button className={`btn btn-ghost ${looping ? "bg-secondary" : ""}`} onClick={() => loopAudioBuffer()}>
<button className={`btn btn-ghost ${looping ? "bg-secondary" : ""}`} id="loopPlayingButton" onClick={() => loopAudioBuffer()}>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
<path strokeLinecap="round" strokeLinejoin="round" d="M16.023 9.348h4.992v-.001M2.985 19.644v-4.992m0 0h4.992m-4.993 0l3.181 3.183a8.25 8.25 0 0013.803-3.7M4.031 9.865a8.25 8.25 0 0113.803-3.7l3.181 3.182m0-4.991v4.99" />
</svg>
Expand Down
10 changes: 5 additions & 5 deletions src/app/components/audioRecorder/AudioRecorderMain.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,26 +27,26 @@ const AudioRecorderMain = () => {

return (
<>
<div className="flex justify-center items-center flex-grow flex-col pt-16 lg:gap-8 md:gap-6 gap-4">
<div className="flex justify-center items-center flex-grow flex-col pt-16 lg:gap-8 md:gap-6 gap-4" id="audioRecorder">
<span className="font-light text-6xl">{recorderDisplayTime}</span>
<div className="flex gap-2 flex-row">
{!audioRecording && <button className="btn flex-col justify-evenly pl-2 2xl:w-60 2xl:h-72 pr-2 lg:w-52 lg:h-60 md:w-44 md:h-52 w-40 h-48" onClick={() => recordAudio()}>
{!audioRecording && <button id="recordAudio" className="btn flex-col justify-evenly pl-2 2xl:w-60 2xl:h-72 pr-2 lg:w-52 lg:h-60 md:w-44 md:h-52 w-40 h-48" onClick={() => recordAudio()}>
<div className="fill-base-content">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-16 h-16">
<path strokeLinecap="round" strokeLinejoin="round" d="M5.25 5.653c0-.856.917-1.398 1.667-.986l11.54 6.348a1.125 1.125 0 010 1.971l-11.54 6.347a1.125 1.125 0 01-1.667-.985V5.653z" />
</svg>
</div>
<span>{t("audioRecorder.record")}</span>
</button>}
{audioRecording && <button className="btn flex-col justify-evenly pl-2 pr-2 2xl:w-60 2xl:h-72 lg:w-52 lg:h-60 md:w-44 md:h-52 w-40 h-48" onClick={() => pauseRecorderAudio()}>
{audioRecording && <button id="pauseRecordAudio" className="btn flex-col justify-evenly pl-2 pr-2 2xl:w-60 2xl:h-72 lg:w-52 lg:h-60 md:w-44 md:h-52 w-40 h-48" onClick={() => pauseRecorderAudio()}>
<div className="fill-base-content">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-16 h-16">
<path strokeLinecap="round" strokeLinejoin="round" d="M15.75 5.25v13.5m-7.5-13.5v13.5" />
</svg>
</div>
<span>{t("audioRecorder.pause")}</span>
</button>}
<button className="btn flex-col justify-evenly pl-2 pr-2 2xl:w-60 2xl:h-72 lg:w-52 lg:h-60 md:w-44 md:h-52 w-40 h-48" onClick={() => stopRecordAudio()} disabled={recorderTime <= 0}>
<button id="stopRecordAudio" className="btn flex-col justify-evenly pl-2 pr-2 2xl:w-60 2xl:h-72 lg:w-52 lg:h-60 md:w-44 md:h-52 w-40 h-48" onClick={() => stopRecordAudio()} disabled={recorderTime <= 0}>
<div className="fill-base-content">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-16 h-16">
<path strokeLinecap="round" strokeLinejoin="round" d="M5.25 7.5A2.25 2.25 0 017.5 5.25h9a2.25 2.25 0 012.25 2.25v9a2.25 2.25 0 01-2.25 2.25h-9a2.25 2.25 0 01-2.25-2.25v-9z" />
Expand All @@ -55,7 +55,7 @@ const AudioRecorderMain = () => {
<span>{t("audioRecorder.stop")}</span>
</button>
</div>
<button className="btn flex flex-row p-5 w-auto h-auto gap-x-4" onClick={() => (document.getElementById("recorderSettingsModal")! as DaisyUIModal).showModal()}>
<button id="audioRecorderSettings" className="btn flex flex-row p-5 w-auto h-auto gap-x-4" onClick={() => (document.getElementById("recorderSettingsModal")! as DaisyUIModal).showModal()}>
<div className="fill-base-content">
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-12 h-12">
<path strokeLinecap="round" strokeLinejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
Expand Down
2 changes: 1 addition & 1 deletion src/app/components/dialogs/AppConfigDialog.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ const AppConfigDialog = () => {
<label htmlFor="enableInitialRendering">{t("appSettings.enableInitialRendering")}</label>
</div>
<div className="flex flex-row gap-x-2 justify-center md:justify-items-end">
<input type="checkbox" className="toggle" id="compatibilityMode" checked={isInitialRenderingEnabled} onChange={(e) => toggleEnableInitialRendering(e.target.checked)} />
<input type="checkbox" className="toggle" id="enableInitialRendering" checked={isInitialRenderingEnabled} onChange={(e) => toggleEnableInitialRendering(e.target.checked)} />
<div className="tooltip tooltip-top tooltip-config-dialog md:tooltip-config-dialog-md" data-tip={t("appSettings.enableInitialRenderingInfos")}>
{InfoIcon}
</div>
Expand Down
4 changes: 2 additions & 2 deletions src/app/components/navbar/navbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,13 +26,13 @@ const Navbar = () => {
</div>
</div>
<div className="flex-none">
<button className="btn btn-square btn-ghost" onClick={() => (document.getElementById("modalSettings")! as DaisyUIModal).showModal()}>
<button className="btn btn-square btn-ghost" id="appSettingsButton" onClick={() => (document.getElementById("modalSettings")! as DaisyUIModal).showModal()}>
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
<path strokeLinecap="round" strokeLinejoin="round" d="M9.594 3.94c.09-.542.56-.94 1.11-.94h2.593c.55 0 1.02.398 1.11.94l.213 1.281c.063.374.313.686.645.87.074.04.147.083.22.127.324.196.72.257 1.075.124l1.217-.456a1.125 1.125 0 011.37.49l1.296 2.247a1.125 1.125 0 01-.26 1.431l-1.003.827c-.293.24-.438.613-.431.992a6.759 6.759 0 010 .255c-.007.378.138.75.43.99l1.005.828c.424.35.534.954.26 1.43l-1.298 2.247a1.125 1.125 0 01-1.369.491l-1.217-.456c-.355-.133-.75-.072-1.076.124a6.57 6.57 0 01-.22.128c-.331.183-.581.495-.644.869l-.213 1.28c-.09.543-.56.941-1.11.941h-2.594c-.55 0-1.02-.398-1.11-.94l-.213-1.281c-.062-.374-.312-.686-.644-.87a6.52 6.52 0 01-.22-.127c-.325-.196-.72-.257-1.076-.124l-1.217.456a1.125 1.125 0 01-1.369-.49l-1.297-2.247a1.125 1.125 0 01.26-1.431l1.004-.827c.292-.24.437-.613.43-.992a6.932 6.932 0 010-.255c.007-.378-.138-.75-.43-.99l-1.004-.828a1.125 1.125 0 01-.26-1.43l1.297-2.247a1.125 1.125 0 011.37-.491l1.216.456c.356.133.751.072 1.076-.124.072-.044.146-.087.22-.128.332-.183.582-.495.644-.869l.214-1.281z" />
<path strokeLinecap="round" strokeLinejoin="round" d="M15 12a3 3 0 11-6 0 3 3 0 016 0z" />
</svg>
</button>
<button className="btn btn-square btn-ghost" onClick={() => (document.getElementById("modalInfos")! as DaisyUIModal).showModal()}>
<button className="btn btn-square btn-ghost" id="appInfosButton" onClick={() => (document.getElementById("modalInfos")! as DaisyUIModal).showModal()}>
<div className="indicator">
{updateData && updateData.hasUpdate && <span className="indicator-item badge badge-error badge-xs"></span>}
<svg xmlns="http://www.w3.org/2000/svg" fill="none" viewBox="0 0 24 24" strokeWidth={1.5} stroke="currentColor" className="w-6 h-6">
Expand Down
4 changes: 2 additions & 2 deletions src/app/context/ApplicationConfigContext.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { createContext, useContext, useState, ReactNode, FC, useEffect } from "react";
import { AudioEditor, EventType, Constants } from "@eliastik/simple-sound-studio-lib";
import { ApplicationObjectsSingleton } from "@eliastik/simple-sound-studio-components";
import { SoundStudioApplicationFactory } from "@eliastik/simple-sound-studio-components";
import i18n from "@eliastik/simple-sound-studio-components/lib/i18n";
import i18next from "i18next";
import ApplicationConfigContextProps from "../model/contextProps/ApplicationConfigContextProps";
Expand All @@ -29,7 +29,7 @@ const getService = (): ApplicationConfigService => {
};

const getAudioEditor = (): AudioEditor => {
return ApplicationObjectsSingleton.getAudioEditorInstance()!;
return SoundStudioApplicationFactory.getAudioEditorInstance()!;
};

let isReady = false;
Expand Down
4 changes: 2 additions & 2 deletions src/app/context/AudioRecorderContext.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
"use client";

import { createContext, useContext, useState, ReactNode, FC, useEffect } from "react";
import { ApplicationObjectsSingleton } from "@eliastik/simple-sound-studio-components";
import { SoundStudioApplicationFactory } from "@eliastik/simple-sound-studio-components";
import { VoiceRecorder, EventType, RecorderSettings } from "@eliastik/simple-sound-studio-lib";
import AudioRecorderContextProps from "../model/contextProps/AudioRecorderContextProps";

Expand All @@ -20,7 +20,7 @@ interface AudioRecorderProviderProps {
}

const getRecorderInstance = (): VoiceRecorder => {
return ApplicationObjectsSingleton.getAudioRecorderInstance()!;
return SoundStudioApplicationFactory.getAudioRecorderInstance()!;
};

let isReady = false;
Expand Down
6 changes: 6 additions & 0 deletions src/app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -39,3 +39,9 @@
.collapse[open] > .collapse-content, .collapse-open > .collapse-content, .collapse:focus:not(.collapse-close) > .collapse-content, .collapse:not(.collapse-close) > input[type="checkbox"]:checked ~ .collapse-content, .collapse:not(.collapse-close) > input[type="radio"]:checked ~ .collapse-content {
display: initial;
}

@layer base {
:root:has(:is(.modal-open, .modal:target, .modal-toggle:checked + .modal, .modal[open])) {
scrollbar-gutter: revert !important;
}
}
Loading

0 comments on commit e8e80d7

Please sign in to comment.