diff --git a/.github/actions/initialize-build-environment/action.yml b/.github/actions/initialize-build-environment/action.yml
index f92910ef..12e10df1 100644
--- a/.github/actions/initialize-build-environment/action.yml
+++ b/.github/actions/initialize-build-environment/action.yml
@@ -39,11 +39,11 @@ runs:
HOMEBREW_NO_INSTALL_CLEANUP: 1
run: |
brew update
- brew install ninja
+ brew install ninja ccache imagemagick create-dmg
- name: Install Qt
uses: jurplel/install-qt-action@v4
with:
version: ${{ inputs.qt_version }}
- modules: qt5compat qtshadertools
+ modules: qt5compat qtscxml qtshadertools
cache: true
diff --git a/.github/actions/initialize-vcpkg/action.yml b/.github/actions/initialize-vcpkg/action.yml
index e19dd5ab..1d083ef5 100644
--- a/.github/actions/initialize-vcpkg/action.yml
+++ b/.github/actions/initialize-vcpkg/action.yml
@@ -31,7 +31,7 @@ runs:
QT_DIR: ${{ env.Qt_ROOT_DIR }}/lib/cmake/Qt6
Qt6_DIR: ${{ env.Qt_ROOT_DIR }}/lib/cmake/Qt6
VCPKG_CMAKE_RELEASE_BUILD_TYPE: "RelWithDebInfo"
- VCPKG_KEEP_ENV_VARS: "QT_DIR;Qt6_DIR;VCPKG_CMAKE_RELEASE_BUILD_TYPE;VCPKG_BUILD_TYPE"
+ VCPKG_KEEP_ENV_VARS: "QT_DIR;Qt6_DIR;VCPKG_CMAKE_RELEASE_BUILD_TYPE;VCPKG_BUILD_TYPE;MACOSX_DEPLOYMENT_TARGET"
shell: pwsh
run: |
if (!(Test-Path $env:VCPKG_DEFAULT_BINARY_CACHE)) {
diff --git a/.github/instructions/viewmodel-binding.instructions.md b/.github/instructions/viewmodel-binding.instructions.md
new file mode 100644
index 00000000..50eddd12
--- /dev/null
+++ b/.github/instructions/viewmodel-binding.instructions.md
@@ -0,0 +1,103 @@
+# View-Model ↔ Document Binding Prompt
+
+Use this prompt to guide new view-model bindings for future document elements. Keep property specifics out; focus on state design, controller-driven transitions, transactions, and synchronization patterns. Derive patterns from existing implementations (e.g., `TrackViewModelContextData`, `LabelViewModelContextData`, `TempoViewModelContextData`).
+
+## Core Goals
+- Mirror document collections to view-model collections with clear ownership and mapping tables in both directions.
+- Drive UI interactions through controllers; never mutate document state directly from QML.
+- Guard against infinite loops by conditioning view→doc updates on active states.
+- Wrap mutating operations in transactions with start/commit/abort semantics tied to state entry/exit.
+
+- Instrument every state with entry/exit logging (use `qCInfo` with a per-context logging category) to trace flows during debugging, even for states without handlers.
+
+## State Machine Design
+- Build a `QStateMachine` with explicit states for idle, rubber-band selection, move/drag, edit/adjust flows, and per-property operations as needed.
+- Keep transitions driven by signals emitted from interaction controllers and internal guards (e.g., started/not-started, commit/abort, finish).
+- Example (move flow, list rotation):
+ - `idle` → `movePending` on `moveTransactionWillStart` (from controller).
+ - `movePending` → `moveProcessing` on `moveTransactionStarted`; → `idle` on `moveTransactionNotStarted`.
+ - `moveProcessing` → `moveCommitting` on `moveTransactionWillCommit`; → `moveAborting` on `moveTransactionWillAbort`.
+ - `moveCommitting`/`moveAborting` → `idle` (reset guards).
+- Example (edit flow, text-like):
+ - `idle` → `namePending` on `nameTransactionWillStart`.
+ - `namePending` → `nameProgressing` on `nameTransactionStarted`; → `idle` if not started.
+ - `nameProgressing` → `nameCommitting` on commit; → `nameAborting` on abort.
+ - `nameCommitting`/`nameAborting` → `idle`.
+- Rubber-band selection: `idle` → `rubberBandDragging` on start, back to `idle` on finish.
+
+## Controller-Driven Transitions
+- Wire controller signals to emit local signals that the state machine consumes (two patterns):
+ 1) **Started/Committed/Aborted**: drag/move and text edits use started/commit/abort triplets.
+ 2) **Started/Finished**: toggle/slider/height edits use started/finished pairs.
+- Set the current target item/index when the controller signals a start; clear it on commit/finish/abort handlers.
+- For rotations or list moves: only propagate view→doc when in the move-processing state; otherwise apply doc→view rotations.
+
+## Transactions
+- Begin a transaction when entering the corresponding "pending" state. Abort immediately if the controller could not start.
+- On commit states: if the new value differs, write to the document and `commitTransaction` with a descriptive label; otherwise `abortTransaction`.
+- On abort states: always `abortTransaction` and reset local guards (`target`, flags like `moveChanged`).
+- Track whether any change occurred during move/drag (`moveChanged`) to avoid committing no-op transactions.
+
+## Synchronization Patterns
+- Maintain bidirectional maps: `doc -> view` and `view -> doc`. Insert/remove bindings on collection signals (`itemInserted`/`itemRemoved`), not "aboutTo" when you need the item fully constructed.
+- When binding a new item:
+ - Create the view-model item, insert into both maps and the view-model collection at the correct index.
+ - Connect doc→view signals to update view items, guarded by equality checks.
+ - Connect view→doc signals but gate them with state checks (only honor during the relevant progressing/doing states; otherwise revert the view to the doc value).
+ - Initialize view properties from the doc model after wiring connections.
+- Selection sync: listen to document selection model `itemSelected` and mark the view item selected; initialize selection for pre-selected items after binding.
+- Rotation sync: doc→view rotations apply when *not* moving; view→doc rotations apply only while the move state is active, and should mark a change flag.
+
+## Example Snippets
+- **Doc→View guarded update** (avoid loops):
+ ```cpp
+ connect(control, &ControlType::propertyChanged, viewItem, [=](auto value) {
+ if (viewItem->property() == value) return;
+ viewItem->setProperty(value);
+ });
+ ```
+- **View→Doc gated by state**:
+ ```cpp
+ connect(viewItem, &ViewType::propertyChanged, docItem, [=] {
+ if (!stateMachine->configuration().contains(propertyProgressingState)) {
+ viewItem->setProperty(docItem->property());
+ return;
+ }
+ // defer actual write to commit handler
+ });
+ ```
+- **Transaction commit handler**:
+ ```cpp
+ void ContextData::onNameCommittingStateEntered() {
+ if (!target || nameTxId == Invalid) { target = {}; return; }
+ auto viewItem = viewMap.value(target);
+ if (viewItem->name() == target->name()) {
+ tx->abortTransaction(nameTxId);
+ } else {
+ target->setName(viewItem->name());
+ tx->commitTransaction(nameTxId, tr("Renaming item"));
+ }
+ nameTxId = {}; target = {};
+ }
+ ```
+- **Rotate handling**:
+ ```cpp
+ connect(docList, &List::rotated, this, [=](int l, int m, int r) {
+ if (stateMachine->configuration().contains(moveProcessingState)) return;
+ viewList->rotate(l, m, r);
+ });
+ connect(viewList, &ViewList::rotated, this, [=](int l, int m, int r) {
+ if (!stateMachine->configuration().contains(moveProcessingState)) return;
+ moveChanged = true;
+ docList->rotate(l, m, r);
+ });
+ ```
+
+## Implementation Checklist
+- Define states and transitions before binding to controllers; start the state machine immediately.
+- Create controllers via context helper methods; hook all relevant signals to emit local transition signals and set the current target.
+- Bind document collections first, then replay existing selection to the view.
+- For each commit/finish handler: compare values, write document, commit transaction; otherwise abort. Always reset `target` and flags.
+- Keep all strings ASCII; add concise comments only where non-obvious.
+
+Use this prompt verbatim when extending bindings to new document elements to maintain consistent interaction, transaction, and synchronization behavior across the codebase.
diff --git a/.github/workflows/dev-build.yml b/.github/workflows/dev-build.yml
index 03dca6ce..7dd6dc8c 100644
--- a/.github/workflows/dev-build.yml
+++ b/.github/workflows/dev-build.yml
@@ -16,6 +16,7 @@ on:
- 'LICENSE'
- 'crowdin.yml'
- '.github/**'
+ - '**/translations/*.ts'
workflow_dispatch:
inputs:
identifier:
@@ -40,16 +41,16 @@ jobs:
os:
- windows-2025
# - ubuntu-24.04
- # - macos-15
-
+ - macos-15
env:
- QT_VERSION: 6.9.2
+ QT_VERSION: 6.10.1
VCPKG_REF: 74e6536215718009aae747d86d84b78376bf9e09
INNOSETUP_REF: is-6_5_4
VERSION_IDENTIFIER: ${{ github.sha }}${{ github.event.inputs.identifier && '.' || '' }}${{ github.event.inputs.identifier }}
BUILD_DIR: ${{ github.workspace }}/build/build
INSTALL_DIR: ${{ github.workspace }}/build/install
CCACHE_DIR: ${{ github.workspace }}/build/ccache
+ MACOSX_DEPLOYMENT_TARGET: 13.0
runs-on: ${{ matrix.os }}
@@ -87,7 +88,10 @@ jobs:
-InstallDir $env:INSTALL_DIR `
-VersionIdentifier $env:VERSION_IDENTIFIER `
${{ github.event.inputs.use_ccache == 'true' && '-CCache' || '' }}
- Write-Output ARTIFACT_NAME=$($output.ApplicationName)_$($output.Semver -replace '[\.\-\+]', '_') >> $env:GITHUB_ENV
+ Write-Output ARTIFACT_NAME=$($output.ApplicationName)_$($output.Semver -replace '[\.\-\+]', '_')_${{ runner.os }}_${{ runner.arch }} >> $env:GITHUB_ENV
+ Write-Output APPLICATION_SEMVER=$($output.Semver) >> $env:GITHUB_ENV
+ Write-Output APPLICATION_DISPLAY_NAME=$($output.ApplicationDisplayName) >> $env:GITHUB_ENV
+ Write-Output APPLICATION_NAME=$($output.ApplicationName) >> $env:GITHUB_ENV
Write-Output INSTALLER_FILE_BASE=$($output.InstallerFileBase) >> $env:GITHUB_ENV
- name: Save CCache cache
@@ -102,11 +106,22 @@ jobs:
$output = & ./scripts/ci/Collect-Symbol-Files.ps1 -VcpkgRootDir $env:VCPKG_ROOT_DIR -InstallDir $env:INSTALL_DIR
Write-Output SYMBOL_FILES_PATH=$($output.Path) >> $env:GITHUB_ENV
- - name: Pack
+ - name: Create InnoSetup installer (Windows)
+ if: ${{ runner.os == 'Windows' }}
run: |
$output = & ./scripts/ci/Pack.ps1 -BuildDir $env:BUILD_DIR -InstallerFileBase $env:INSTALLER_FILE_BASE -InnoSetupCommit $env:INNOSETUP_REF
Write-Output PACKAGE_PATH=$($output.Path) >> $env:GITHUB_ENV
+ - name: Create DMG installer (macOS)
+ if: ${{ runner.os == 'macOS' }}
+ run: |
+ $output = & ./scripts/ci/Create-DMG.ps1 `
+ -AppPath $(Join-Path $env:INSTALL_DIR $env:APPLICATION_NAME'.app') `
+ -Semver $env:APPLICATION_SEMVER `
+ -ApplicationDisplayName $env:APPLICATION_DISPLAY_NAME `
+ -InstallerFileBase $env:INSTALLER_FILE_BASE
+ Write-Output PACKAGE_PATH=$($output) >> $env:GITHUB_ENV
+
- name: Upload symbol files
uses: actions/upload-artifact@v4
with:
diff --git a/crowdin.yml b/crowdin.yml
index 14e32c6a..35758df7 100644
--- a/crowdin.yml
+++ b/crowdin.yml
@@ -1,13 +1,5 @@
files:
- - source: /src/plugins/coreplugin/res/translations/Core_en_US.ts
- translation: /src/plugins/coreplugin/res/translations/Core_%locale_with_underscore%.ts
- - source: /src/plugins/audio/res/translations/org.diffscope.audio_en_US.ts
- translation: /src/plugins/audio/res/translations/org.diffscope.audio_%locale_with_underscore%.ts
- - source: /src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_en_US.ts
- translation: /src/plugins/welcomewizard/res/translations/org.diffscope.welcomewizard_%locale_with_underscore%.ts
- - source: /src/plugins/achievement/res/translations/org.diffscope.achievement_en_US.ts
- translation: /src/plugins/achievement/res/translations/org.diffscope.achievement_%locale_with_underscore%.ts
- - source: /src/plugins/maintenance/res/translations/org.diffscope.maintenance_en_US.ts
- translation: /src/plugins/maintenance/res/translations/org.diffscope.maintenance_%locale_with_underscore%.ts
- - source: /src/libs/application/uishell/share/translations/uishell_en_US.ts
- translation: /src/libs/application/uishell/share/translations/uishell_%locale_with_underscore%.ts
+ - source: '/**/translations/*_en_US.ts'
+ translation: '/%original_path%/%file_name%_%locale_with_underscore%.ts'
+ translation_replace:
+ '_en_US': ''
diff --git a/scripts/ci/Build.ps1 b/scripts/ci/Build.ps1
index d7bee6de..0afa4394 100644
--- a/scripts/ci/Build.ps1
+++ b/scripts/ci/Build.ps1
@@ -78,6 +78,8 @@ Write-Host "Semver: $semver"
$installerFileBase = "${applicationName}_$($semver -replace '[\.\-\+]', '_')_installer"
+$depsDir = (Get-ChildItem -Path $(Join-Path $VcpkgRootDir installed) | Where-Object {$_.Name -ne "vcpkg"})[0].FullName
+
cmake -S . -B $(Resolve-Path $BuildDir) -G Ninja `
-DCMAKE_BUILD_TYPE=RelWithDebInfo `
"-DCMAKE_TOOLCHAIN_FILE=$(Join-Path $VcpkgRootDir scripts/buildsystems/vcpkg.cmake)" `
@@ -85,6 +87,8 @@ cmake -S . -B $(Resolve-Path $BuildDir) -G Ninja `
"-DCMAKE_CXX_COMPILER_LAUNCHER=$($CCache ? 'ccache' : '')" `
-DCMAKE_MSVC_DEBUG_INFORMATION_FORMAT=Embedded `
-DCK_ENABLE_CONSOLE:BOOL=FALSE `
+ -DQT_NO_PRIVATE_MODULE_WARNING:BOOL=ON `
+ "-DQMSETUP_APPLOCAL_DEPS_PATHS_RELWITHDEBINFO=$(Join-Path $depsDir lib)" `
-DAPPLICATION_INSTALL:BOOL=ON `
-DAPPLICATION_CONFIGURE_INSTALLER:BOOL=ON `
-DINNOSETUP_USE_UNOFFICIAL_LANGUAGE:BOOL=ON `
diff --git a/scripts/ci/Collect-Symbol-Files.ps1 b/scripts/ci/Collect-Symbol-Files.ps1
index 5c4e9206..914b3c52 100644
--- a/scripts/ci/Collect-Symbol-Files.ps1
+++ b/scripts/ci/Collect-Symbol-Files.ps1
@@ -47,6 +47,7 @@ if ($IsWindows) {
}
dsymutil $dllFile.FullName -o "$pdbTargetDirectory/$($dllFile.Name).dSYM"
strip -S $dllFile.FullName
+ codesign --force --sign - $dllFile.FullName
} else {
Write-Host "Skip: $dllFile"
}
diff --git a/scripts/ci/Create-DMG.ps1 b/scripts/ci/Create-DMG.ps1
new file mode 100644
index 00000000..c4dd5544
--- /dev/null
+++ b/scripts/ci/Create-DMG.ps1
@@ -0,0 +1,122 @@
+param (
+ [Parameter(Mandatory)]
+ [string]$AppPath,
+
+ [Parameter(Mandatory)]
+ [string]$Semver,
+
+ [Parameter(Mandatory)]
+ [string]$ApplicationDisplayName,
+
+ [Parameter(Mandatory)]
+ [string]$InstallerFileBase
+)
+
+$BackgroundSrcDir = "src/app/share/dmg"
+
+$Bg1x = Join-Path $BackgroundSrcDir "dmg_background.png"
+$Bg2x = Join-Path $BackgroundSrcDir "dmg_background@2x.png"
+
+if (!(Test-Path $Bg1x) -or !(Test-Path $Bg2x)) {
+ throw "dmg_background.png and dmg_background@2x.png do not exist in $BackgroundSrcDir"
+}
+
+if (!(Test-Path $AppPath)) {
+ throw "App bundle not exist: $AppPath"
+}
+
+# Temporary directory
+$TempDir = Join-Path ([System.IO.Path]::GetTempPath()) ("dmg-build-" + [System.Guid]::NewGuid())
+New-Item -ItemType Directory -Path $TempDir | Out-Null
+
+$Bg1xOut = Join-Path $TempDir "dmg_background.png"
+$Bg2xOut = Join-Path $TempDir "dmg_background@2x.png"
+$BgTiff = Join-Path $TempDir "dmg_background.tiff"
+$AppBundleName = "$ApplicationDisplayName.app"
+$AppBundlePath = Join-Path $TempDir $AppBundleName
+
+$VersionText = "Version $Semver"
+
+try {
+ # -----------------------------
+ # Step 1: Preprocess background
+ # -----------------------------
+
+ # 1x image
+ & magick `
+ "$Bg1x" `
+ -gravity south `
+ -pointsize 12 `
+ -fill "rgba(37,37,37,0.25)" `
+ -annotate +0+8 "$VersionText" `
+ "$Bg1xOut" | Write-Host
+
+ if ($LASTEXITCODE -ne 0) {
+ throw "ImageMagick failed to process dmg_background.png"
+ }
+
+ # 2x image (scaled)
+ & magick `
+ "$Bg2x" `
+ -gravity south `
+ -pointsize 24 `
+ -fill "rgba(37,37,37,0.25)" `
+ -annotate +0+16 "$VersionText" `
+ "$Bg2xOut" | Write-Host
+
+ if ($LASTEXITCODE -ne 0) {
+ throw "ImageMagick failed to process dmg_background@2x.png"
+ }
+
+ # Combine into TIFF
+ & tiffutil `
+ -cathidpicheck `
+ "$Bg1xOut" `
+ "$Bg2xOut" `
+ -out "$BgTiff" | Write-Host
+
+ if ($LASTEXITCODE -ne 0) {
+ throw "tiffutil failed to create TIFF"
+ }
+
+ # -----------------------------
+ # Step 2: Build DMG
+ # -----------------------------
+ $DmgName = "$InstallerFileBase.dmg"
+ $DmgPath = Join-Path (Get-Location) $DmgName
+
+ if (Test-Path $DmgPath) {
+ Remove-Item $DmgPath -Force
+ }
+
+ if (Test-Path $AppBundlePath) {
+ Remove-Item $AppBundlePath -Recurse -Force
+ }
+
+ Move-Item -Path $AppPath -Destination $AppBundlePath
+
+ & codesign --deep --force --sign - $AppBundlePath | Write-Host
+
+ <# TODO: create-dmg currently places hidden .background file to the right of the visible area, so we have to leave some space for the horizontal scroll bar #>
+ & create-dmg `
+ --volname "$ApplicationDisplayName" `
+ --background "$BgTiff" `
+ --window-size 600 448 `
+ --icon-size 128 `
+ --icon "$(Split-Path $AppBundlePath -Leaf)" 132 280 `
+ --app-drop-link 468 280 `
+ "$DmgPath" `
+ "$AppBundlePath" | Write-Host
+
+ if ($LASTEXITCODE -ne 0) {
+ throw "create-dmg failed"
+ }
+
+ Write-Output $DmgPath
+}
+finally {
+ # Cleanup temp files
+ if (Test-Path $TempDir) {
+ Remove-Item $TempDir -Recurse -Force
+ }
+}
diff --git a/scripts/vcpkg b/scripts/vcpkg
index 3561516f..6fb07583 160000
--- a/scripts/vcpkg
+++ b/scripts/vcpkg
@@ -1 +1 @@
-Subproject commit 3561516f372ae595301c5bf373ca6930766e540d
+Subproject commit 6fb07583f1e76206975508f33a21bfddce799bc0
diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt
index 4264e630..d95d1d65 100644
--- a/src/app/CMakeLists.txt
+++ b/src/app/CMakeLists.txt
@@ -29,6 +29,7 @@ endif()
ck_configure_application(
${_ico_args}
${_icns_args}
+ INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/info.plist.in
)
qm_configure_target(${PROJECT_NAME}
@@ -70,6 +71,12 @@ ck_add_shared_files(
# SRC conf/${CK_PLATFORM_LOWER}/qtmediate.json DEST ${CK_BUILD_QT_CONF_DIR} # qtmediate.json
)
+if(EXISTS ${CMAKE_CURRENT_BINARY_DIR}/icons/dspx.icns)
+ ck_add_shared_files(
+ SRC ${CMAKE_CURRENT_BINARY_DIR}/icons/dspx.icns DEST ${CK_BUILD_DATA_DIR}
+ )
+endif()
+
file(GLOB _icons icons/*)
list(FILTER _icons EXCLUDE REGEX CMakeLists.txt)
diff --git a/src/app/info.plist.in b/src/app/info.plist.in
new file mode 100644
index 00000000..c10a6e46
--- /dev/null
+++ b/src/app/info.plist.in
@@ -0,0 +1,92 @@
+
+
+
+
+
+ CFBundleDevelopmentRegion
+ English
+
+ CFBundleExecutable
+ @APPLICATION_NAME@
+
+ CFBundleIconFile
+ app.icns
+
+ CFBundleIdentifier
+ @APPLICATION_NAME@
+
+ CFBundleInfoDictionaryVersion
+ 6.0
+
+ CFBundleName
+ @APPLICATION_DISPLAY_NAME@
+
+ CFBundlePackageType
+ APPL
+
+ CFBundleShortVersionString
+ @APPLICATION_SEMVER@
+
+ CFBundleSignature
+ ????
+
+ CFBundleVersion
+ @APPLICATION_SEMVER@
+
+ CSResourcesFileMapped
+
+
+ NSHumanReadableCopyright
+ @RC_COPYRIGHT@
+
+ UTExportedTypeDeclarations
+
+
+ UTTypeIdentifier
+ org.diffscope.dspx
+
+ UTTypeDescription
+ DiffScope Project Exchange Format
+
+ UTTypeConformsTo
+
+ public.json
+
+
+ UTTypeTagSpecification
+
+ public.filename-extension
+
+ dspx
+
+
+ public.mime-type
+ application/vnd.openvpi.dspx+json
+
+
+
+
+ CFBundleDocumentTypes
+
+
+ CFBundleTypeName
+ DiffScope Project File
+
+ CFBundleTypeRole
+ Editor
+
+ LSItemContentTypes
+
+ org.diffscope.dspx
+
+
+ CFBundleTypeIconFile
+ dspx.icns
+
+ LSHandlerRank
+ Default
+
+
+
+
+
diff --git a/src/app/main.cpp b/src/app/main.cpp
index b1b4c6f8..041d94f7 100644
--- a/src/app/main.cpp
+++ b/src/app/main.cpp
@@ -39,6 +39,8 @@ static QSettings::Format getJsonSettingsFormat() {
static QQmlEngine *engine{};
+static constexpr char kNoWarningLastInitialization[] = "--no-warning-last-initialization";
+
class MyLoaderSpec : public Loader::LoaderSpec {
public:
MyLoaderSpec() {
@@ -55,6 +57,11 @@ class MyLoaderSpec : public Loader::LoaderSpec {
QStringLiteral("/config.json");
pluginPaths << ApplicationInfo::applicationLocation(ApplicationInfo::BuiltinPlugins);
coreName = QStringLiteral("org.diffscope.core");
+ extraArguments << Argument{
+ {kNoWarningLastInitialization},
+ {},
+ QStringLiteral("Suppress warning about 'Last initialization was aborted abnormally'")
+ };
}
QSettings *createExtensionSystemSettings(QSettings::Scope scope) override {
@@ -102,11 +109,18 @@ class MyLoaderSpec : public Loader::LoaderSpec {
locale.setNumberOptions(QLocale::OmitGroupSeparator);
RuntimeInterface::setTranslationManager(new TranslationManager(RuntimeInterface::instance()));
RuntimeInterface::translationManager()->setLocale(locale);
- RuntimeInterface::translationManager()->addTranslationPath(ApplicationInfo::systemLocation(ApplicationInfo::Resources) + QStringLiteral("/ChorusKit/translations"));
- RuntimeInterface::translationManager()->addTranslationPath(ApplicationInfo::systemLocation(ApplicationInfo::Resources) + QStringLiteral("/svscraft/translations"));
- RuntimeInterface::translationManager()->addTranslationPath(ApplicationInfo::systemLocation(ApplicationInfo::Resources) + QStringLiteral("/uishell/translations"));
+ auto translationBaseDir =
+#ifdef Q_OS_MAC
+ ApplicationInfo::systemLocation(ApplicationInfo::Resources) + QStringLiteral("/share");
+#else
+ ApplicationInfo::systemLocation(ApplicationInfo::Resources);
+#endif
+ RuntimeInterface::translationManager()->addTranslationPath(translationBaseDir + QStringLiteral("/ChorusKit/translations"));
+ RuntimeInterface::translationManager()->addTranslationPath(translationBaseDir + QStringLiteral("/svscraft/translations"));
+ RuntimeInterface::translationManager()->addTranslationPath(translationBaseDir + QStringLiteral("/uishell/translations"));
- if (settings->value("lastInitializationAbortedFlag").toBool()) {
+ bool lastInitializationWarningSuppressed = QApplication::arguments().contains(kNoWarningLastInitialization);
+ if (settings->value("lastInitializationAbortedFlag").toBool() && !lastInitializationWarningSuppressed) {
qInfo() << "Last initialization was aborted abnormally";
QQmlComponent component(engine, "DiffScope.UIShell", "InitializationFailureWarningDialog");
std::unique_ptr dialog(component.isError() ? nullptr : component.createWithInitialProperties({
diff --git a/src/app/share/dmg/dmg_background.png b/src/app/share/dmg/dmg_background.png
new file mode 100644
index 00000000..56a7dd80
Binary files /dev/null and b/src/app/share/dmg/dmg_background.png differ
diff --git a/src/app/share/dmg/dmg_background@2x.png b/src/app/share/dmg/dmg_background@2x.png
new file mode 100644
index 00000000..0f3a01fc
Binary files /dev/null and b/src/app/share/dmg/dmg_background@2x.png differ
diff --git a/src/libs/3rdparty/choruskit b/src/libs/3rdparty/choruskit
index 2aa4fed3..e40b3316 160000
--- a/src/libs/3rdparty/choruskit
+++ b/src/libs/3rdparty/choruskit
@@ -1 +1 @@
-Subproject commit 2aa4fed3506d79bfc0f3d1dde6acc5b4e86deb2a
+Subproject commit e40b331652f9a81eea9be505305dbb198abd8dae
diff --git a/src/libs/3rdparty/opendspx b/src/libs/3rdparty/opendspx
index 6fda26bb..45fbaf09 160000
--- a/src/libs/3rdparty/opendspx
+++ b/src/libs/3rdparty/opendspx
@@ -1 +1 @@
-Subproject commit 6fda26bb78001cf955bfd190a3bddff9c6498bde
+Subproject commit 45fbaf0987083c57bf5a06c0adcbd88956d0334a
diff --git a/src/libs/3rdparty/qactionkit b/src/libs/3rdparty/qactionkit
index 5f075d0a..573def48 160000
--- a/src/libs/3rdparty/qactionkit
+++ b/src/libs/3rdparty/qactionkit
@@ -1 +1 @@
-Subproject commit 5f075d0ac57c8316ed719f5ba077b4fa36eb626e
+Subproject commit 573def486e3f8c0f62768bcdcbc490b24cbd59af
diff --git a/src/libs/3rdparty/scopicflow b/src/libs/3rdparty/scopicflow
index 9ef06154..d32af0d9 160000
--- a/src/libs/3rdparty/scopicflow
+++ b/src/libs/3rdparty/scopicflow
@@ -1 +1 @@
-Subproject commit 9ef0615400f9835b11179c5fdc6f593f048c102a
+Subproject commit d32af0d99f7e6fd1023412c2686fde559a5da7db
diff --git a/src/libs/3rdparty/svscraft b/src/libs/3rdparty/svscraft
index c9c7d6c2..1289b602 160000
--- a/src/libs/3rdparty/svscraft
+++ b/src/libs/3rdparty/svscraft
@@ -1 +1 @@
-Subproject commit c9c7d6c28a2564c4a0ee79f485a6a15b7b81dc69
+Subproject commit 1289b602146a559e1c9d69f3347043e8cc935920
diff --git a/src/libs/application/dspxmodel/src/AnchorNode.cpp b/src/libs/application/dspxmodel/src/AnchorNode.cpp
index def64001..47aae0d2 100644
--- a/src/libs/application/dspxmodel/src/AnchorNode.cpp
+++ b/src/libs/application/dspxmodel/src/AnchorNode.cpp
@@ -1,29 +1,17 @@
#include "AnchorNode.h"
+#include "AnchorNode_p.h"
#include
#include
#include
+#include
#include
#include
namespace dspx {
- class AnchorNodePrivate {
- Q_DECLARE_PUBLIC(AnchorNode)
- public:
- AnchorNode *q_ptr;
- AnchorNode::InterpolationMode interp;
- int x;
- int y;
-
- void setInterpUnchecked(AnchorNode::InterpolationMode interp_);
- void setInterp(AnchorNode::InterpolationMode interp_);
- void setXUnchecked(int x_);
- void setX(int x_);
- };
-
void AnchorNodePrivate::setInterpUnchecked(AnchorNode::InterpolationMode interp_) {
Q_Q(AnchorNode);
q->model()->strategy()->setEntityProperty(q->handle(), ModelStrategy::P_Type, QVariant::fromValue(interp_));
@@ -31,8 +19,9 @@ namespace dspx {
void AnchorNodePrivate::setInterp(AnchorNode::InterpolationMode interp_) {
Q_Q(AnchorNode);
- if (auto engine = qjsEngine(q); engine && (interp_ != AnchorNode::None && interp_ != AnchorNode::Linear && interp_ != AnchorNode::Hermite)) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Interpolation mode must be one of None, Linear, or Hermite"));
+ if ((interp_ != AnchorNode::None && interp_ != AnchorNode::Linear && interp_ != AnchorNode::Hermite)) {
+ if (auto engine = qjsEngine(q))
+ engine->throwError(QJSValue::RangeError, QStringLiteral("Interpolation mode must be one of None, Linear, or Hermite"));
return;
}
setInterpUnchecked(interp_);
@@ -45,13 +34,22 @@ namespace dspx {
void AnchorNodePrivate::setX(int x_) {
Q_Q(AnchorNode);
- if (auto engine = qjsEngine(q); engine && x_ < 0) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Position must be greater or equal to 0"));
+ if (x_ < 0) {
+ if (auto engine = qjsEngine(q))
+ engine->throwError(QJSValue::RangeError, QStringLiteral("Position must be greater or equal to 0"));
return;
}
setXUnchecked(x_);
}
+ void AnchorNodePrivate::setAnchorNodeSequence(AnchorNode *item, AnchorNodeSequence *anchorNodeSequence) {
+ auto d = item->d_func();
+ if (d->anchorNodeSequence != anchorNodeSequence) {
+ d->anchorNodeSequence = anchorNodeSequence;
+ Q_EMIT item->anchorNodeSequenceChanged();
+ }
+ }
+
AnchorNode::AnchorNode(Handle handle, Model *model)
: EntityObject(handle, model), d_ptr(new AnchorNodePrivate) {
Q_D(AnchorNode);
@@ -112,6 +110,11 @@ namespace dspx {
setY(node.y);
}
+ AnchorNodeSequence *AnchorNode::anchorNodeSequence() const {
+ Q_D(const AnchorNode);
+ return d->anchorNodeSequence;
+ }
+
void AnchorNode::handleSetEntityProperty(int property, const QVariant &value) {
Q_D(AnchorNode);
switch (property) {
diff --git a/src/libs/application/dspxmodel/src/AnchorNode.h b/src/libs/application/dspxmodel/src/AnchorNode.h
index 7f7c7826..4eaef0e8 100644
--- a/src/libs/application/dspxmodel/src/AnchorNode.h
+++ b/src/libs/application/dspxmodel/src/AnchorNode.h
@@ -11,6 +11,10 @@ namespace QDspx {
namespace dspx {
+ class AnchorNodeSequence;
+
+ class AnchorNodeSequencePrivate;
+
class AnchorNodePrivate;
class DSPX_MODEL_EXPORT AnchorNode : public EntityObject {
@@ -21,6 +25,7 @@ namespace dspx {
Q_PRIVATE_PROPERTY(d_func(), InterpolationMode interp MEMBER interp WRITE setInterp NOTIFY interpChanged)
Q_PRIVATE_PROPERTY(d_func(), int x MEMBER x WRITE setX NOTIFY xChanged)
Q_PROPERTY(int y READ y WRITE setY NOTIFY yChanged)
+ Q_PROPERTY(AnchorNodeSequence *anchorNodeSequence READ anchorNodeSequence NOTIFY anchorNodeSequenceChanged)
public:
enum InterpolationMode {
@@ -44,10 +49,13 @@ namespace dspx {
QDspx::AnchorNode toQDspx() const;
void fromQDspx(const QDspx::AnchorNode &node);
+ AnchorNodeSequence *anchorNodeSequence() const;
+
Q_SIGNALS:
void interpChanged(InterpolationMode interp);
void xChanged(int x);
void yChanged(int y);
+ void anchorNodeSequenceChanged();
protected:
void handleSetEntityProperty(int property, const QVariant &value) override;
diff --git a/src/libs/application/dspxmodel/src/AnchorNodeSequence.cpp b/src/libs/application/dspxmodel/src/AnchorNodeSequence.cpp
index 2544e95e..2d6e9d87 100644
--- a/src/libs/application/dspxmodel/src/AnchorNodeSequence.cpp
+++ b/src/libs/application/dspxmodel/src/AnchorNodeSequence.cpp
@@ -1,4 +1,5 @@
#include "AnchorNodeSequence.h"
+#include "AnchorNodeSequence_p.h"
#include
#include
@@ -7,21 +8,27 @@
#include
#include
+#include
#include
-#include
#include
namespace dspx {
- class AnchorNodeSequencePrivate : public PointSequenceData {
- Q_DECLARE_PUBLIC(AnchorNodeSequence)
- };
-
- AnchorNodeSequence::AnchorNodeSequence(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new AnchorNodeSequencePrivate) {
+ AnchorNodeSequence::AnchorNodeSequence(ParamCurveAnchor *paramCurveAnchor, Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new AnchorNodeSequencePrivate) {
Q_D(AnchorNodeSequence);
Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::ES_ParamCurveAnchorNodes);
d->q_ptr = this;
d->pModel = ModelPrivate::get(model);
+ d->paramCurveAnchor = paramCurveAnchor;
+
+ d->init(model->strategy()->getEntitiesFromSequenceContainer(handle));
+
+ connect(this, &AnchorNodeSequence::itemInserted, this, [=](AnchorNode *item) {
+ AnchorNodePrivate::setAnchorNodeSequence(item, this);
+ });
+ connect(this, &AnchorNodeSequence::itemRemoved, this, [=](AnchorNode *item) {
+ AnchorNodePrivate::setAnchorNodeSequence(item, nullptr);
+ });
}
AnchorNodeSequence::~AnchorNodeSequence() = default;
@@ -102,6 +109,13 @@ namespace dspx {
d->handleTakeFromSequenceContainer(takenEntity, entity);
}
+ ParamCurveAnchor *AnchorNodeSequence::paramCurveAnchor() const {
+ Q_D(const AnchorNodeSequence);
+ return d->paramCurveAnchor;
+ }
+
+
+
}
#include "moc_AnchorNodeSequence.cpp"
diff --git a/src/libs/application/dspxmodel/src/AnchorNodeSequence.h b/src/libs/application/dspxmodel/src/AnchorNodeSequence.h
index adcf4727..a965ba5f 100644
--- a/src/libs/application/dspxmodel/src/AnchorNodeSequence.h
+++ b/src/libs/application/dspxmodel/src/AnchorNodeSequence.h
@@ -4,6 +4,7 @@
#include
#include
+#include
namespace QDspx {
struct AnchorNode;
@@ -12,6 +13,7 @@ namespace QDspx {
namespace dspx {
class AnchorNode;
+ class ParamCurveAnchor;
class AnchorNodeSequencePrivate;
@@ -23,6 +25,7 @@ namespace dspx {
Q_PROPERTY(int size READ size NOTIFY sizeChanged)
Q_PROPERTY(AnchorNode *firstItem READ firstItem NOTIFY firstItemChanged)
Q_PROPERTY(AnchorNode *lastItem READ lastItem NOTIFY lastItemChanged)
+ Q_PROPERTY(ParamCurveAnchor *paramCurveAnchor READ paramCurveAnchor CONSTANT)
Q_PRIVATE_PROPERTY(d_func(), QJSValue iterable READ iterable CONSTANT)
public:
~AnchorNodeSequence() override;
@@ -41,6 +44,12 @@ namespace dspx {
QList toQDspx() const;
void fromQDspx(const QList &nodes);
+ ParamCurveAnchor *paramCurveAnchor() const;
+
+ auto asRange() const {
+ return impl::SequenceRange(this);
+ }
+
Q_SIGNALS:
void itemAboutToInsert(AnchorNode *item);
void itemInserted(AnchorNode *item);
@@ -56,7 +65,7 @@ namespace dspx {
private:
friend class ModelPrivate;
- explicit AnchorNodeSequence(Handle handle, Model *model);
+ explicit AnchorNodeSequence(ParamCurveAnchor *paramCurveAnchor, Handle handle, Model *model);
QScopedPointer d_ptr;
};
diff --git a/src/libs/application/dspxmodel/src/AnchorNodeSequence_p.h b/src/libs/application/dspxmodel/src/AnchorNodeSequence_p.h
new file mode 100644
index 00000000..8b27bf67
--- /dev/null
+++ b/src/libs/application/dspxmodel/src/AnchorNodeSequence_p.h
@@ -0,0 +1,19 @@
+#ifndef DIFFSCOPE_DSPX_MODEL_ANCHORNODESEQUENCE_P_H
+#define DIFFSCOPE_DSPX_MODEL_ANCHORNODESEQUENCE_P_H
+
+#include
+
+#include
+#include
+
+namespace dspx {
+
+ class AnchorNodeSequencePrivate : public PointSequenceData {
+ Q_DECLARE_PUBLIC(AnchorNodeSequence)
+ public:
+ ParamCurveAnchor *paramCurveAnchor{};
+ };
+
+}
+
+#endif //DIFFSCOPE_DSPX_MODEL_ANCHORNODESEQUENCE_P_H
\ No newline at end of file
diff --git a/src/libs/application/dspxmodel/src/AnchorNode_p.h b/src/libs/application/dspxmodel/src/AnchorNode_p.h
new file mode 100644
index 00000000..3b2704b6
--- /dev/null
+++ b/src/libs/application/dspxmodel/src/AnchorNode_p.h
@@ -0,0 +1,27 @@
+#ifndef DIFFSCOPE_DSPX_MODEL_ANCHORNODE_P_H
+#define DIFFSCOPE_DSPX_MODEL_ANCHORNODE_P_H
+
+#include
+
+namespace dspx {
+
+ class AnchorNodePrivate {
+ Q_DECLARE_PUBLIC(AnchorNode)
+ public:
+ AnchorNode *q_ptr;
+ AnchorNode::InterpolationMode interp;
+ int x;
+ int y;
+ AnchorNodeSequence *anchorNodeSequence{};
+
+ void setInterpUnchecked(AnchorNode::InterpolationMode interp_);
+ void setInterp(AnchorNode::InterpolationMode interp_);
+ void setXUnchecked(int x_);
+ void setX(int x_);
+
+ static void setAnchorNodeSequence(AnchorNode *item, AnchorNodeSequence *anchorNodeSequence);
+ };
+
+}
+
+#endif //DIFFSCOPE_DSPX_MODEL_ANCHORNODE_P_H
\ No newline at end of file
diff --git a/src/libs/application/dspxmodel/src/BasicModelStrategy.cpp b/src/libs/application/dspxmodel/src/BasicModelStrategy.cpp
index 6773a52d..0e6e8a67 100644
--- a/src/libs/application/dspxmodel/src/BasicModelStrategy.cpp
+++ b/src/libs/application/dspxmodel/src/BasicModelStrategy.cpp
@@ -2,129 +2,18 @@
#include
-#include
-#include
-
#include
+#include
namespace dspx {
- class BasicModelStrategyEntity : public QObject {
- Q_OBJECT
- public:
- using QObject::QObject;
-
- ModelStrategy::Entity type{};
- };
-
- class BasicModelStrategyItemEntity : public BasicModelStrategyEntity {
- Q_OBJECT
- public:
- using BasicModelStrategyEntity::BasicModelStrategyEntity;
-
- QHash properties;
- QHash associatedSubEntities;
- };
-
- class BasicModelStrategySequenceContainerEntity : public BasicModelStrategyEntity {
- Q_OBJECT
- public:
- using BasicModelStrategyEntity::BasicModelStrategyEntity;
-
- QSet sequence;
- };
-
- class BasicModelStrategyListContainerEntity : public BasicModelStrategyEntity {
- Q_OBJECT
- public:
- using BasicModelStrategyEntity::BasicModelStrategyEntity;
-
- QList list;
- };
-
- class BasicModelStrategyMapContainerEntity : public BasicModelStrategyEntity {
- Q_OBJECT
- public:
- using BasicModelStrategyEntity::BasicModelStrategyEntity;
-
- QHash map;
- };
-
- class BasicModelStrategyDataArrayEntity : public BasicModelStrategyEntity {
- Q_OBJECT
- public:
- using BasicModelStrategyEntity::BasicModelStrategyEntity;
-
- QVariantList data;
- };
-
- static BasicModelStrategyEntity *createByType(ModelStrategy::Entity type, QObject *parent) {
- BasicModelStrategyEntity *obj;
- switch (type) {
- case ModelStrategy::EI_AudioClip:
- case ModelStrategy::EI_Global:
- case ModelStrategy::EI_Label:
- case ModelStrategy::EI_Note:
- case ModelStrategy::EI_Param:
- case ModelStrategy::EI_ParamCurveAnchor:
- case ModelStrategy::EI_ParamCurveFree:
- case ModelStrategy::EI_ParamCurveAnchorNode:
- case ModelStrategy::EI_Phoneme:
- case ModelStrategy::EI_SingingClip:
- case ModelStrategy::EI_Source:
- case ModelStrategy::EI_Tempo:
- case ModelStrategy::EI_TimeSignature:
- case ModelStrategy::EI_Track:
- case ModelStrategy::EI_WorkspaceInfo:
- obj = new BasicModelStrategyItemEntity(parent);
- obj->type = type;
- break;
- case ModelStrategy::ES_Clips:
- case ModelStrategy::ES_Labels:
- case ModelStrategy::ES_Notes:
- case ModelStrategy::ES_ParamCurveAnchorNodes:
- case ModelStrategy::ES_ParamCurves:
- case ModelStrategy::ES_Tempos:
- case ModelStrategy::ES_TimeSignatures:
- obj = new BasicModelStrategySequenceContainerEntity(parent);
- obj->type = type;
- break;
- case ModelStrategy::EL_Phonemes:
- case ModelStrategy::EL_Tracks:
- obj = new BasicModelStrategyListContainerEntity(parent);
- obj->type = type;
- break;
- case ModelStrategy::ED_ParamCurveFreeValues:
- case ModelStrategy::ED_VibratoPoints:
- obj = new BasicModelStrategyDataArrayEntity(parent);
- obj->type = type;
- break;
- case ModelStrategy::EM_Params:
- case ModelStrategy::EM_Sources:
- case ModelStrategy::EM_Workspace:
- obj = new BasicModelStrategyMapContainerEntity(parent);
- obj->type = type;
- break;
- default:
- Q_UNREACHABLE();
- }
- return obj;
- }
-
- template
- T *handleCast(Handle entity) {
- auto obj = qobject_cast(reinterpret_cast(entity.d));
- Q_ASSERT(obj);
- return obj;
- }
-
BasicModelStrategy::BasicModelStrategy(QObject *parent) : ModelStrategy(parent) {
}
BasicModelStrategy::~BasicModelStrategy() = default;
Handle BasicModelStrategy::createEntity(Entity entityType) {
- auto object = createByType(entityType, this);
+ auto object = BasicModelStrategyEntity::createByType(entityType, this);
Handle entity{reinterpret_cast(object)};
Q_EMIT createEntityNotified(entity, entityType);
return entity;
@@ -140,6 +29,30 @@ namespace dspx {
return reinterpret_cast(entity.d)->type;
}
+ QList BasicModelStrategy::getEntitiesFromSequenceContainer(Handle sequenceContainerEntity) {
+ QList a;
+ std::ranges::transform(handleCast(sequenceContainerEntity)->sequence, std::back_inserter(a), [](auto *obj) {
+ return Handle{reinterpret_cast(obj)};
+ });
+ return a;
+ }
+
+ QList BasicModelStrategy::getEntitiesFromListContainer(Handle listContainerEntity) {
+ QList a;
+ std::ranges::transform(handleCast(listContainerEntity)->list, std::back_inserter(a), [](auto *obj) {
+ return Handle{reinterpret_cast(obj)};
+ });
+ return a;
+ }
+
+ QList> BasicModelStrategy::getEntitiesFromMapContainer(Handle mapContainerEntity) {
+ QList> a;
+ for (auto [key, value] : handleCast(mapContainerEntity)->map.asKeyValueRange()) {
+ a.append({key, Handle{reinterpret_cast(value)}});
+ }
+ return a;
+ }
+
bool BasicModelStrategy::insertIntoSequenceContainer(Handle sequenceContainerEntity, Handle entity) {
auto sequenceContainerObject = handleCast(sequenceContainerEntity);
auto object = reinterpret_cast(entity.d);
@@ -231,12 +144,14 @@ namespace dspx {
void BasicModelStrategy::setEntityProperty(Handle entity, Property property, const QVariant &value) {
auto object = handleCast(entity);
+ Q_ASSERT(isEntityTypeAndPropertyTypeCompatible(object->type, property));
object->properties.insert(property, value);
Q_EMIT setEntityPropertyNotified(entity, property, value);
}
QVariant BasicModelStrategy::getEntityProperty(Handle entity, Property property) {
auto object = handleCast(entity);
+ Q_ASSERT(isEntityTypeAndPropertyTypeCompatible(object->type, property));
return object->properties.value(property);
}
bool BasicModelStrategy::spliceDataArray(Handle dataArrayEntity, int index, int length, const QVariantList &values) {
@@ -273,64 +188,8 @@ namespace dspx {
auto object = handleCast(entity);
auto subObject = object->associatedSubEntities.value(relationship);
if (!subObject) {
- Entity subObjectType;
- switch (relationship) {
- case R_Children:
- switch (object->type) {
- case EI_Global:
- subObjectType = EL_Tracks;
- break;
- case EI_Track:
- subObjectType = ES_Clips;
- break;
- case EI_SingingClip:
- subObjectType = ES_Notes;
- break;
- case EI_ParamCurveAnchor:
- subObjectType = ES_ParamCurveAnchorNodes;
- break;
- case EI_ParamCurveFree:
- subObjectType = ED_ParamCurveFreeValues;
- break;
- default:
- Q_UNREACHABLE();
- }
- break;
- case R_Labels:
- subObjectType = ES_Labels;
- break;
- case R_ParamCurvesEdited:
- case R_ParamCurvesOriginal:
- case R_ParamCurvesTransform:
- subObjectType = ES_ParamCurves;
- break;
- case R_Params:
- subObjectType = EM_Params;
- break;
- case R_PhonemesEdited:
- case R_PhonemesOriginal:
- subObjectType = EL_Phonemes;
- break;
- case R_Sources:
- subObjectType = EM_Sources;
- break;
- case R_Tempos:
- subObjectType = ES_Tempos;
- break;
- case R_TimeSignatures:
- subObjectType = ES_TimeSignatures;
- break;
- case R_VibratoPointsAmplitude:
- case R_VibratoPointsFrequency:
- subObjectType = ED_VibratoPoints;
- break;
- case R_Workspace:
- subObjectType = EM_Workspace;
- break;
- default:
- Q_UNREACHABLE();
- }
- subObject = createByType(subObjectType, object);
+ Entity subObjectType = getAssociatedSubEntityTypeFromEntityTypeAndRelationship(object->type, relationship);
+ subObject = BasicModelStrategyEntity::createByType(subObjectType, object);
object->associatedSubEntities.insert(relationship, subObject);
}
Handle subEntity{reinterpret_cast(subObject)};
@@ -338,5 +197,3 @@ namespace dspx {
}
}
-
-#include "BasicModelStrategy.moc"
diff --git a/src/libs/application/dspxmodel/src/BasicModelStrategy.h b/src/libs/application/dspxmodel/src/BasicModelStrategy.h
index 222f6b67..9f638397 100644
--- a/src/libs/application/dspxmodel/src/BasicModelStrategy.h
+++ b/src/libs/application/dspxmodel/src/BasicModelStrategy.h
@@ -14,6 +14,9 @@ namespace dspx {
Handle createEntity(Entity entityType) override;
void destroyEntity(Handle entity) override;
Entity getEntityType(Handle entity) override;
+ QList getEntitiesFromSequenceContainer(Handle sequenceContainerEntity) override;
+ QList getEntitiesFromListContainer(Handle listContainerEntity) override;
+ QList> getEntitiesFromMapContainer(Handle mapContainerEntity) override;
bool insertIntoSequenceContainer(Handle sequenceContainerEntity, Handle entity) override;
bool insertIntoListContainer(Handle listContainerEntity, Handle entity, int index) override;
bool insertIntoMapContainer(Handle mapContainerEntity, Handle entity, const QString &key) override;
diff --git a/src/libs/application/dspxmodel/src/BasicModelStrategyEntity_p.h b/src/libs/application/dspxmodel/src/BasicModelStrategyEntity_p.h
new file mode 100644
index 00000000..8b111d48
--- /dev/null
+++ b/src/libs/application/dspxmodel/src/BasicModelStrategyEntity_p.h
@@ -0,0 +1,123 @@
+#ifndef DIFFSCOPE_DSPX_MODEL_BASICMODELSTRATEGYENTITY_P_H
+#define DIFFSCOPE_DSPX_MODEL_BASICMODELSTRATEGYENTITY_P_H
+
+#include
+#include
+#include
+
+#include
+
+namespace dspx {
+ class BasicModelStrategyEntity : public QObject {
+ Q_OBJECT
+ public:
+ using QObject::QObject;
+
+ ModelStrategy::Entity type{};
+
+ static inline BasicModelStrategyEntity *createByType(ModelStrategy::Entity type, QObject *parent);
+ };
+
+ class BasicModelStrategyItemEntity : public BasicModelStrategyEntity {
+ Q_OBJECT
+ public:
+ using BasicModelStrategyEntity::BasicModelStrategyEntity;
+
+ QHash properties;
+ QHash associatedSubEntities;
+ };
+
+ class BasicModelStrategySequenceContainerEntity : public BasicModelStrategyEntity {
+ Q_OBJECT
+ public:
+ using BasicModelStrategyEntity::BasicModelStrategyEntity;
+
+ QSet sequence;
+ };
+
+ class BasicModelStrategyListContainerEntity : public BasicModelStrategyEntity {
+ Q_OBJECT
+ public:
+ using BasicModelStrategyEntity::BasicModelStrategyEntity;
+
+ QList list;
+ };
+
+ class BasicModelStrategyMapContainerEntity : public BasicModelStrategyEntity {
+ Q_OBJECT
+ public:
+ using BasicModelStrategyEntity::BasicModelStrategyEntity;
+
+ QHash map;
+ };
+
+ class BasicModelStrategyDataArrayEntity : public BasicModelStrategyEntity {
+ Q_OBJECT
+ public:
+ using BasicModelStrategyEntity::BasicModelStrategyEntity;
+
+ QVariantList data;
+ };
+
+ BasicModelStrategyEntity *BasicModelStrategyEntity::createByType(ModelStrategy::Entity type, QObject *parent) {
+ BasicModelStrategyEntity *obj;
+ switch (type) {
+ case ModelStrategy::EI_AudioClip:
+ case ModelStrategy::EI_Global:
+ case ModelStrategy::EI_Label:
+ case ModelStrategy::EI_Note:
+ case ModelStrategy::EI_Param:
+ case ModelStrategy::EI_ParamCurveAnchor:
+ case ModelStrategy::EI_ParamCurveFree:
+ case ModelStrategy::EI_ParamCurveAnchorNode:
+ case ModelStrategy::EI_Phoneme:
+ case ModelStrategy::EI_SingingClip:
+ case ModelStrategy::EI_Source:
+ case ModelStrategy::EI_Tempo:
+ case ModelStrategy::EI_TimeSignature:
+ case ModelStrategy::EI_Track:
+ case ModelStrategy::EI_WorkspaceInfo:
+ obj = new BasicModelStrategyItemEntity(parent);
+ obj->type = type;
+ break;
+ case ModelStrategy::ES_Clips:
+ case ModelStrategy::ES_Labels:
+ case ModelStrategy::ES_Notes:
+ case ModelStrategy::ES_ParamCurveAnchorNodes:
+ case ModelStrategy::ES_ParamCurves:
+ case ModelStrategy::ES_Tempos:
+ case ModelStrategy::ES_TimeSignatures:
+ obj = new BasicModelStrategySequenceContainerEntity(parent);
+ obj->type = type;
+ break;
+ case ModelStrategy::EL_Phonemes:
+ case ModelStrategy::EL_Tracks:
+ obj = new BasicModelStrategyListContainerEntity(parent);
+ obj->type = type;
+ break;
+ case ModelStrategy::ED_ParamCurveFreeValues:
+ case ModelStrategy::ED_VibratoPoints:
+ obj = new BasicModelStrategyDataArrayEntity(parent);
+ obj->type = type;
+ break;
+ case ModelStrategy::EM_Params:
+ case ModelStrategy::EM_Sources:
+ case ModelStrategy::EM_Workspace:
+ obj = new BasicModelStrategyMapContainerEntity(parent);
+ obj->type = type;
+ break;
+ default:
+ Q_UNREACHABLE();
+ }
+ return obj;
+ }
+
+ template
+ T *handleCast(Handle entity) {
+ auto obj = qobject_cast(reinterpret_cast(entity.d));
+ Q_ASSERT(obj);
+ return obj;
+ }
+}
+
+#endif //DIFFSCOPE_DSPX_MODEL_BASICMODELSTRATEGYENTITY_P_H
diff --git a/src/libs/application/dspxmodel/src/Clip.cpp b/src/libs/application/dspxmodel/src/Clip.cpp
index 5014cf63..4caf4492 100644
--- a/src/libs/application/dspxmodel/src/Clip.cpp
+++ b/src/libs/application/dspxmodel/src/Clip.cpp
@@ -1,4 +1,5 @@
#include "Clip.h"
+#include "Clip_p.h"
#include
#include
@@ -9,25 +10,27 @@
#include
#include
#include
-#include
+#include
#include
#include
namespace dspx {
- class ClipPrivate {
- Q_DECLARE_PUBLIC(Clip)
- public:
- Clip *q_ptr;
- ModelPrivate *pModel;
- QString name;
- BusControl *control;
- ClipTime *time;
- Clip::ClipType type;
- Track *track{};
- Workspace *workspace;
- bool overlapped{};
- };
+ void ClipPrivate::setOverlapped(Clip *item, bool overlapped) {
+ auto d = item->d_func();
+ if (d->overlapped != overlapped) {
+ d->overlapped = overlapped;
+ Q_EMIT item->overlappedChanged(overlapped);
+ }
+ }
+
+ void ClipPrivate::setClipSequence(Clip *item, ClipSequence *clipSequence) {
+ auto d = item->d_func();
+ if (d->clipSequence != clipSequence) {
+ d->clipSequence = clipSequence;
+ Q_EMIT item->clipSequenceChanged();
+ }
+ }
Clip::Clip(ClipType type, Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new ClipPrivate) {
Q_D(Clip);
@@ -107,9 +110,9 @@ namespace dspx {
}
}
- Track *Clip::track() const {
+ ClipSequence *Clip::clipSequence() const {
Q_D(const Clip);
- return d->track;
+ return d->clipSequence;
}
int Clip::position() const {
@@ -127,22 +130,6 @@ namespace dspx {
return d->overlapped;
}
- void Clip::setOverlapped(bool overlapped) {
- Q_D(Clip);
- if (d->overlapped != overlapped) {
- d->overlapped = overlapped;
- Q_EMIT overlappedChanged(overlapped);
- }
- }
-
- void Clip::setTrack(Track *track) {
- Q_D(Clip);
- if (d->track != track) {
- d->track = track;
- Q_EMIT trackChanged();
- }
- }
-
void Clip::handleSetEntityProperty(int property, const QVariant &value) {
Q_D(Clip);
switch (property) {
diff --git a/src/libs/application/dspxmodel/src/Clip.h b/src/libs/application/dspxmodel/src/Clip.h
index 5dbbcb69..c35024e9 100644
--- a/src/libs/application/dspxmodel/src/Clip.h
+++ b/src/libs/application/dspxmodel/src/Clip.h
@@ -15,9 +15,7 @@ namespace dspx {
class BusControl;
class ClipTime;
class Workspace;
- class Track;
-
- class ClipSequencePrivate;
+ class ClipSequence;
class ClipPrivate;
@@ -31,7 +29,7 @@ namespace dspx {
Q_PROPERTY(ClipTime *time READ time CONSTANT)
Q_PROPERTY(ClipType type READ type CONSTANT)
Q_PROPERTY(Workspace *workspace READ workspace CONSTANT)
- Q_PROPERTY(Track *track READ track NOTIFY trackChanged)
+ Q_PROPERTY(ClipSequence *clipSequence READ clipSequence NOTIFY clipSequenceChanged)
Q_PROPERTY(bool overlapped READ isOverlapped NOTIFY overlappedChanged)
public:
enum ClipType {
@@ -56,7 +54,7 @@ namespace dspx {
QDspx::ClipRef toQDspx() const;
void fromQDspx(const QDspx::ClipRef &clip);
- Track *track() const;
+ ClipSequence *clipSequence() const;
int position() const;
@@ -66,7 +64,7 @@ namespace dspx {
Q_SIGNALS:
void nameChanged(const QString &name);
- void trackChanged();
+ void clipSequenceChanged();
void positionChanged(int position);
void lengthChanged(int length);
void overlappedChanged(bool overlapped);
@@ -76,10 +74,7 @@ namespace dspx {
void handleSetEntityProperty(int property, const QVariant &value) override;
private:
- friend class ClipSequencePrivate;
QScopedPointer d_ptr;
- void setTrack(Track *track);
- void setOverlapped(bool overlapped);
};
} // dspx
diff --git a/src/libs/application/dspxmodel/src/ClipSequence.cpp b/src/libs/application/dspxmodel/src/ClipSequence.cpp
index ddc52650..93f6a778 100644
--- a/src/libs/application/dspxmodel/src/ClipSequence.cpp
+++ b/src/libs/application/dspxmodel/src/ClipSequence.cpp
@@ -1,4 +1,5 @@
#include "ClipSequence.h"
+#include "ClipSequence_p.h"
#include
#include
@@ -11,42 +12,24 @@
#include
#include
#include
-#include
-#include
#include
-#include
-#include
namespace dspx {
- static void setClipOverlapped(Clip *item, bool overlapped);
-
- class ClipSequencePrivate : public RangeSequenceData {
- Q_DECLARE_PUBLIC(ClipSequence)
- public:
- Track *track{};
- static void setOverlapped(Clip *item, bool overlapped) {
- item->setOverlapped(overlapped);
- }
- static void setTrack(Clip *item, Track *track) {
- item->setTrack(track);
- }
- };
-
- void setClipOverlapped(Clip *item, bool overlapped) {
- ClipSequencePrivate::setOverlapped(item, overlapped);
- }
-
- ClipSequence::ClipSequence(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new ClipSequencePrivate) {
+ ClipSequence::ClipSequence(Track *track, Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new ClipSequencePrivate) {
Q_D(ClipSequence);
Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::ES_Clips);
d->q_ptr = this;
d->pModel = ModelPrivate::get(model);
+ d->track = track;
+
+ d->init(model->strategy()->getEntitiesFromSequenceContainer(handle));
+
connect(this, &ClipSequence::itemInserted, this, [=](Clip *item) {
- ClipSequencePrivate::setTrack(item, d->track);
+ ClipPrivate::setClipSequence(item, this);
});
connect(this, &ClipSequence::itemRemoved, this, [=](Clip *item) {
- ClipSequencePrivate::setTrack(item, nullptr);
+ ClipPrivate::setClipSequence(item, nullptr);
});
}
@@ -133,11 +116,6 @@ namespace dspx {
}
}
- void ClipSequence::setTrack(Track *track) {
- Q_D(ClipSequence);
- d->track = track;
- }
-
void ClipSequence::handleInsertIntoSequenceContainer(Handle entity) {
Q_D(ClipSequence);
d->handleInsertIntoSequenceContainer(entity);
diff --git a/src/libs/application/dspxmodel/src/ClipSequence.h b/src/libs/application/dspxmodel/src/ClipSequence.h
index bbd14f69..26ae777c 100644
--- a/src/libs/application/dspxmodel/src/ClipSequence.h
+++ b/src/libs/application/dspxmodel/src/ClipSequence.h
@@ -4,6 +4,7 @@
#include
#include
+#include
namespace QDspx {
struct Clip;
@@ -46,6 +47,10 @@ namespace dspx {
Track *track() const;
+ auto asRange() const {
+ return impl::SequenceRange(this);
+ }
+
Q_SIGNALS:
void itemAboutToInsert(Clip *item);
void itemInserted(Clip *item);
@@ -61,9 +66,7 @@ namespace dspx {
private:
friend class ModelPrivate;
- friend class Track;
- explicit ClipSequence(Handle handle, Model *model);
- void setTrack(Track *track);
+ explicit ClipSequence(Track *track, Handle handle, Model *model);
QScopedPointer d_ptr;
};
diff --git a/src/libs/application/dspxmodel/src/ClipSequence_p.h b/src/libs/application/dspxmodel/src/ClipSequence_p.h
new file mode 100644
index 00000000..b6c27acb
--- /dev/null
+++ b/src/libs/application/dspxmodel/src/ClipSequence_p.h
@@ -0,0 +1,19 @@
+#ifndef DIFFSCOPE_DSPX_MODEL_CLIPSEQUENCE_P_H
+#define DIFFSCOPE_DSPX_MODEL_CLIPSEQUENCE_P_H
+
+#include
+
+#include
+#include
+
+namespace dspx {
+
+ class ClipSequencePrivate : public RangeSequenceData {
+ Q_DECLARE_PUBLIC(ClipSequence)
+ public:
+ Track *track{};
+ };
+
+}
+
+#endif //DIFFSCOPE_DSPX_MODEL_CLIPSEQUENCE_P_H
diff --git a/src/libs/application/dspxmodel/src/ClipTime.cpp b/src/libs/application/dspxmodel/src/ClipTime.cpp
index 6e80b886..478bc8d6 100644
--- a/src/libs/application/dspxmodel/src/ClipTime.cpp
+++ b/src/libs/application/dspxmodel/src/ClipTime.cpp
@@ -37,8 +37,9 @@ namespace dspx {
void ClipTimePrivate::setLength(int length_) {
Q_Q(ClipTime);
- if (auto engine = qjsEngine(q); engine && length_ < 0) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Length must be greater than or equal to 0"));
+ if (length_ < 0) {
+ if (auto engine = qjsEngine(q))
+ engine->throwError(QJSValue::RangeError, QStringLiteral("Length must be greater than or equal to 0"));
return;
}
setLengthUnchecked(length_);
@@ -51,8 +52,9 @@ namespace dspx {
void ClipTimePrivate::setClipStart(int clipStart_) {
Q_Q(ClipTime);
- if (auto engine = qjsEngine(q); engine && clipStart_ < 0) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("ClipStart must be greater than or equal to 0"));
+ if (clipStart_ < 0) {
+ if (auto engine = qjsEngine(q))
+ engine->throwError(QJSValue::RangeError, QStringLiteral("ClipStart must be greater than or equal to 0"));
return;
}
setClipStartUnchecked(clipStart_);
@@ -65,8 +67,9 @@ namespace dspx {
void ClipTimePrivate::setClipLen(int clipLen_) {
Q_Q(ClipTime);
- if (auto engine = qjsEngine(q); engine && clipLen_ < 0) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("ClipLen must be greater than or equal to 0"));
+ if (clipLen_ < 0) {
+ if (auto engine = qjsEngine(q))
+ engine->throwError(QJSValue::RangeError, QStringLiteral("ClipLen must be greater than or equal to 0"));
return;
}
setClipLenUnchecked(clipLen_);
diff --git a/src/libs/application/dspxmodel/src/Clip_p.h b/src/libs/application/dspxmodel/src/Clip_p.h
new file mode 100644
index 00000000..e3a3c02c
--- /dev/null
+++ b/src/libs/application/dspxmodel/src/Clip_p.h
@@ -0,0 +1,27 @@
+#ifndef DIFFSCOPE_DSPX_MODEL_CLIP_P_H
+#define DIFFSCOPE_DSPX_MODEL_CLIP_P_H
+
+#include
+
+namespace dspx {
+
+ class ClipPrivate {
+ Q_DECLARE_PUBLIC(Clip)
+ public:
+ Clip *q_ptr;
+ ModelPrivate *pModel;
+ QString name;
+ BusControl *control;
+ ClipTime *time;
+ Clip::ClipType type;
+ ClipSequence *clipSequence{};
+ Workspace *workspace;
+ bool overlapped{};
+
+ static void setOverlapped(Clip *item, bool overlapped);
+ static void setClipSequence(Clip *item, ClipSequence *clipSequence);
+ };
+
+}
+
+#endif //DIFFSCOPE_DSPX_MODEL_CLIP_P_H
diff --git a/src/libs/application/dspxmodel/src/Control.cpp b/src/libs/application/dspxmodel/src/Control.cpp
index d0c823ca..62d68586 100644
--- a/src/libs/application/dspxmodel/src/Control.cpp
+++ b/src/libs/application/dspxmodel/src/Control.cpp
@@ -33,8 +33,9 @@ namespace dspx {
void ControlPrivate::setGain(double gain_) {
Q_Q(Control);
- if (auto engine = qjsEngine(q); engine && (gain_ < 0)) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Gain must be greater or equal to 0"));
+ if ((gain_ < 0)) {
+ if (auto engine = qjsEngine(q))
+ engine->throwError(QJSValue::RangeError, QStringLiteral("Gain must be greater or equal to 0"));
return;
}
setGainUnchecked(gain_);
@@ -47,8 +48,9 @@ namespace dspx {
void ControlPrivate::setPan(double pan_) {
Q_Q(Control);
- if (auto engine = qjsEngine(q); engine && (pan_ < -1 || pan_ > 1)) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Pan must be in range [-1.0, 1.0]"));
+ if ((pan_ < -1 || pan_ > 1)) {
+ if (auto engine = qjsEngine(q))
+ engine->throwError(QJSValue::RangeError, QStringLiteral("Pan must be in range [-1.0, 1.0]"));
return;
}
setPanUnchecked(pan_);
diff --git a/src/libs/application/dspxmodel/src/Control.h b/src/libs/application/dspxmodel/src/Control.h
index 20135658..777294e7 100644
--- a/src/libs/application/dspxmodel/src/Control.h
+++ b/src/libs/application/dspxmodel/src/Control.h
@@ -14,7 +14,7 @@ namespace dspx {
class ControlPrivate;
- class Control : public QObject {
+ class DSPX_MODEL_EXPORT Control : public QObject {
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
diff --git a/src/libs/application/dspxmodel/src/DataArrayData_p.h b/src/libs/application/dspxmodel/src/DataArrayData_p.h
index 92770f3d..0d7c261e 100644
--- a/src/libs/application/dspxmodel/src/DataArrayData_p.h
+++ b/src/libs/application/dspxmodel/src/DataArrayData_p.h
@@ -24,6 +24,12 @@ namespace dspx {
QList data;
QJSValue iterable_;
+ void init(const QVariantList &values) {
+ for (auto v : values) {
+ data.append(v.value());
+ }
+ }
+
int size() const {
return data.size();
}
diff --git a/src/libs/application/dspxmodel/src/EntityObject.cpp b/src/libs/application/dspxmodel/src/EntityObject.cpp
index 4c89bd07..b95fc8a6 100644
--- a/src/libs/application/dspxmodel/src/EntityObject.cpp
+++ b/src/libs/application/dspxmodel/src/EntityObject.cpp
@@ -22,7 +22,7 @@ namespace dspx {
EntityObject::~EntityObject() {
Q_D(EntityObject);
if (d->model && d->handle) {
- d->model->strategy()->destroyEntity(d->handle);
+ Q_ASSERT(false && "EntityObject::~EntityObject: handle is not null. You should call Model::destroyItem() to delete EntityObject.");
}
}
diff --git a/src/libs/application/dspxmodel/src/FreeValueDataArray.cpp b/src/libs/application/dspxmodel/src/FreeValueDataArray.cpp
index 8c65fcb8..6f085afa 100644
--- a/src/libs/application/dspxmodel/src/FreeValueDataArray.cpp
+++ b/src/libs/application/dspxmodel/src/FreeValueDataArray.cpp
@@ -1,19 +1,25 @@
#include "FreeValueDataArray.h"
#include
+#include
#include
namespace dspx {
class FreeValueDataArrayPrivate : public DataArrayData {
Q_DECLARE_PUBLIC(FreeValueDataArray)
+ public:
+ ParamCurveFree *paramCurveFree{};
};
- FreeValueDataArray::FreeValueDataArray(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new FreeValueDataArrayPrivate) {
+ FreeValueDataArray::FreeValueDataArray(ParamCurveFree *paramCurveFree, Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new FreeValueDataArrayPrivate) {
Q_D(FreeValueDataArray);
Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::ED_ParamCurveFreeValues);
d->q_ptr = this;
d->pModel = ModelPrivate::get(model);
+ d->paramCurveFree = paramCurveFree;
+
+ d->init(model->strategy()->sliceDataArray(handle, 0, model->strategy()->getSizeOfDataArray(handle)));
}
FreeValueDataArray::~FreeValueDataArray() = default;
@@ -65,6 +71,13 @@ namespace dspx {
d->handleRotateDataArray(leftIndex, middleIndex, rightIndex);
}
+ ParamCurveFree *FreeValueDataArray::paramCurveFree() const {
+ Q_D(const FreeValueDataArray);
+ return d->paramCurveFree;
+ }
+
+
+
}
#include "moc_FreeValueDataArray.cpp"
diff --git a/src/libs/application/dspxmodel/src/FreeValueDataArray.h b/src/libs/application/dspxmodel/src/FreeValueDataArray.h
index f3dd0284..2c9a263a 100644
--- a/src/libs/application/dspxmodel/src/FreeValueDataArray.h
+++ b/src/libs/application/dspxmodel/src/FreeValueDataArray.h
@@ -7,6 +7,8 @@
namespace dspx {
+ class ParamCurveFree;
+
class FreeValueDataArrayPrivate;
class DSPX_MODEL_EXPORT FreeValueDataArray : public EntityObject {
@@ -15,6 +17,7 @@ namespace dspx {
QML_UNCREATABLE("")
Q_DECLARE_PRIVATE(FreeValueDataArray)
Q_PROPERTY(int size READ size NOTIFY sizeChanged)
+ Q_PROPERTY(ParamCurveFree *paramCurveFree READ paramCurveFree CONSTANT)
Q_PRIVATE_PROPERTY(d_func(), QJSValue iterable READ iterable CONSTANT)
public:
@@ -28,6 +31,8 @@ namespace dspx {
QList toQDspx() const;
void fromQDspx(const QList &values);
+ ParamCurveFree *paramCurveFree() const;
+
Q_SIGNALS:
void sizeChanged(int size);
void aboutToSplice(int index, int length, const QList &values);
@@ -41,7 +46,7 @@ namespace dspx {
private:
friend class ModelPrivate;
- explicit FreeValueDataArray(Handle handle, Model *model);
+ explicit FreeValueDataArray(ParamCurveFree *paramCurveFree, Handle handle, Model *model);
QScopedPointer d_ptr;
};
diff --git a/src/libs/application/dspxmodel/src/Global.cpp b/src/libs/application/dspxmodel/src/Global.cpp
index e687463f..3747dcaa 100644
--- a/src/libs/application/dspxmodel/src/Global.cpp
+++ b/src/libs/application/dspxmodel/src/Global.cpp
@@ -34,8 +34,9 @@ namespace dspx {
void GlobalPrivate::setCentShift(int centShift) {
Q_Q(Global);
- if (auto engine = qjsEngine(q); engine && (centShift < -50 || centShift > 50)) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Cent shift must be in range [-50, 50]"));
+ if ((centShift < -50 || centShift > 50)) {
+ if (auto engine = qjsEngine(q))
+ engine->throwError(QJSValue::RangeError, QStringLiteral("Cent shift must be in range [-50, 50]"));
return;
}
setCentShiftUnchecked(centShift);
diff --git a/src/libs/application/dspxmodel/src/Global.h b/src/libs/application/dspxmodel/src/Global.h
index 651f2129..8eeaa2ca 100644
--- a/src/libs/application/dspxmodel/src/Global.h
+++ b/src/libs/application/dspxmodel/src/Global.h
@@ -5,6 +5,8 @@
#include
+#include
+
namespace QDspx {
struct Global;
}
@@ -17,7 +19,7 @@ namespace dspx {
class GlobalPrivate;
- class Global : public QObject {
+ class DSPX_MODEL_EXPORT Global : public QObject {
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
diff --git a/src/libs/application/dspxmodel/src/Label.cpp b/src/libs/application/dspxmodel/src/Label.cpp
index c4b86312..2b508749 100644
--- a/src/libs/application/dspxmodel/src/Label.cpp
+++ b/src/libs/application/dspxmodel/src/Label.cpp
@@ -1,26 +1,17 @@
#include "Label.h"
+#include "Label_p.h"
#include
#include
#include
+#include
#include
#include
namespace dspx {
- class LabelPrivate {
- Q_DECLARE_PUBLIC(Label)
- public:
- Label *q_ptr;
- int pos;
- QString text;
-
- void setPosUnchecked(int pos_);
- void setPos(int pos_);
- };
-
void LabelPrivate::setPosUnchecked(int pos_) {
Q_Q(Label);
q->model()->strategy()->setEntityProperty(q->handle(), ModelStrategy::P_Position, pos_);
@@ -28,13 +19,22 @@ namespace dspx {
void LabelPrivate::setPos(int pos_) {
Q_Q(Label);
- if (auto engine = qjsEngine(q); engine && pos_ < -0) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Pos must be greater or equal to 0"));
+ if (pos_ < -0) {
+ if (auto engine = qjsEngine(q))
+ engine->throwError(QJSValue::RangeError, QStringLiteral("Pos must be greater or equal to 0"));
return;
}
setPosUnchecked(pos_);
}
+ void LabelPrivate::setLabelSequence(Label *item, LabelSequence *labelSequence) {
+ auto d = item->d_func();
+ if (d->labelSequence != labelSequence) {
+ d->labelSequence = labelSequence;
+ Q_EMIT item->labelSequenceChanged();
+ }
+ }
+
Label::Label(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new LabelPrivate) {
Q_D(Label);
Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::EI_Label);
@@ -66,6 +66,11 @@ namespace dspx {
model()->strategy()->setEntityProperty(handle(), ModelStrategy::P_Text, text);
}
+ LabelSequence *Label::labelSequence() const {
+ Q_D(const Label);
+ return d->labelSequence;
+ }
+
QDspx::Label Label::toQDspx() const {
return {
.pos = pos(),
diff --git a/src/libs/application/dspxmodel/src/Label.h b/src/libs/application/dspxmodel/src/Label.h
index a5b5cf8b..dd21eb3f 100644
--- a/src/libs/application/dspxmodel/src/Label.h
+++ b/src/libs/application/dspxmodel/src/Label.h
@@ -11,6 +11,7 @@ namespace QDspx {
namespace dspx {
+ class LabelSequence;
class LabelPrivate;
class DSPX_MODEL_EXPORT Label : public EntityObject {
@@ -20,6 +21,7 @@ namespace dspx {
Q_DECLARE_PRIVATE(Label);
Q_PRIVATE_PROPERTY(d_func(), int pos MEMBER pos WRITE setPos NOTIFY posChanged)
Q_PROPERTY(QString text READ text WRITE setText NOTIFY textChanged)
+ Q_PROPERTY(LabelSequence *labelSequence READ labelSequence NOTIFY labelSequenceChanged)
public:
~Label() override;
@@ -29,12 +31,15 @@ namespace dspx {
QString text() const;
void setText(const QString &text);
+ LabelSequence *labelSequence() const;
+
QDspx::Label toQDspx() const;
void fromQDspx(const QDspx::Label &label);
Q_SIGNALS:
void posChanged(int pos);
void textChanged(const QString &text);
+ void labelSequenceChanged();
protected:
void handleSetEntityProperty(int property, const QVariant &value) override;
diff --git a/src/libs/application/dspxmodel/src/LabelSequence.cpp b/src/libs/application/dspxmodel/src/LabelSequence.cpp
index 27e6e19c..2eb86c2b 100644
--- a/src/libs/application/dspxmodel/src/LabelSequence.cpp
+++ b/src/libs/application/dspxmodel/src/LabelSequence.cpp
@@ -1,4 +1,5 @@
#include "LabelSequence.h"
+#include "LabelSequence_p.h"
#include
#include
@@ -8,20 +9,24 @@
#include
#include
#include
-#include
#include
namespace dspx {
- class LabelSequencePrivate : public PointSequenceData {
- Q_DECLARE_PUBLIC(LabelSequence)
- };
-
LabelSequence::LabelSequence(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new LabelSequencePrivate) {
Q_D(LabelSequence);
Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::ES_Labels);
d->q_ptr = this;
d->pModel = ModelPrivate::get(model);
+
+ d->init(model->strategy()->getEntitiesFromSequenceContainer(handle));
+
+ connect(this, &LabelSequence::itemInserted, this, [=](Label *item) {
+ LabelPrivate::setLabelSequence(item, this);
+ });
+ connect(this, &LabelSequence::itemRemoved, this, [=](Label *item) {
+ LabelPrivate::setLabelSequence(item, nullptr);
+ });
}
LabelSequence::~LabelSequence() = default;
diff --git a/src/libs/application/dspxmodel/src/LabelSequence.h b/src/libs/application/dspxmodel/src/LabelSequence.h
index bb03d155..cca09003 100644
--- a/src/libs/application/dspxmodel/src/LabelSequence.h
+++ b/src/libs/application/dspxmodel/src/LabelSequence.h
@@ -4,6 +4,7 @@
#include
#include
+#include
namespace QDspx {
struct Label;
@@ -41,6 +42,10 @@ namespace dspx {
QList toQDspx() const;
void fromQDspx(const QList &labels);
+ auto asRange() const {
+ return impl::SequenceRange(this);
+ }
+
Q_SIGNALS:
void itemAboutToInsert(Label *item);
void itemInserted(Label *item);
diff --git a/src/libs/application/dspxmodel/src/LabelSequence_p.h b/src/libs/application/dspxmodel/src/LabelSequence_p.h
new file mode 100644
index 00000000..fe03ee4a
--- /dev/null
+++ b/src/libs/application/dspxmodel/src/LabelSequence_p.h
@@ -0,0 +1,17 @@
+#ifndef DIFFSCOPE_DSPX_MODEL_LABELSEQUENCE_P_H
+#define DIFFSCOPE_DSPX_MODEL_LABELSEQUENCE_P_H
+
+#include
+
+#include
+#include
+
+namespace dspx {
+
+ class LabelSequencePrivate : public PointSequenceData {
+ Q_DECLARE_PUBLIC(LabelSequence)
+ };
+
+}
+
+#endif //DIFFSCOPE_DSPX_MODEL_LABELSEQUENCE_P_H
\ No newline at end of file
diff --git a/src/libs/application/dspxmodel/src/Label_p.h b/src/libs/application/dspxmodel/src/Label_p.h
new file mode 100644
index 00000000..0ef4b5b3
--- /dev/null
+++ b/src/libs/application/dspxmodel/src/Label_p.h
@@ -0,0 +1,24 @@
+#ifndef DIFFSCOPE_DSPX_MODEL_LABEL_P_H
+#define DIFFSCOPE_DSPX_MODEL_LABEL_P_H
+
+#include
+
+namespace dspx {
+
+ class LabelPrivate {
+ Q_DECLARE_PUBLIC(Label)
+ public:
+ Label *q_ptr;
+ int pos;
+ QString text;
+ LabelSequence *labelSequence{};
+
+ void setPosUnchecked(int pos_);
+ void setPos(int pos_);
+
+ static void setLabelSequence(Label *item, LabelSequence *labelSequence);
+ };
+
+}
+
+#endif //DIFFSCOPE_DSPX_MODEL_LABEL_P_H
\ No newline at end of file
diff --git a/src/libs/application/dspxmodel/src/ListData_p.h b/src/libs/application/dspxmodel/src/ListData_p.h
index fd16c589..dff09987 100644
--- a/src/libs/application/dspxmodel/src/ListData_p.h
+++ b/src/libs/application/dspxmodel/src/ListData_p.h
@@ -35,6 +35,12 @@ namespace dspx {
Q_UNREACHABLE();
}
+ void init(const QList &handles) {
+ for (auto handle : handles) {
+ itemList.append(getItem(handle, true));
+ }
+ }
+
void insertItem(int index, ItemType *item) {
auto q = q_ptr;
Q_EMIT q->itemAboutToInsert(index, item);
diff --git a/src/libs/application/dspxmodel/src/MapData_p.h b/src/libs/application/dspxmodel/src/MapData_p.h
index 1c20349d..a177940b 100644
--- a/src/libs/application/dspxmodel/src/MapData_p.h
+++ b/src/libs/application/dspxmodel/src/MapData_p.h
@@ -34,6 +34,12 @@ namespace dspx {
Q_UNREACHABLE();
}
+ void init(const QList> &handles) {
+ for (auto &[key, handle] : handles) {
+ itemMap.insert(key, getItem(handle, true));
+ }
+ }
+
void insertItem(const QString &key, ItemType *item) {
auto q = q_ptr;
Q_EMIT q->itemAboutToInsert(key, item);
diff --git a/src/libs/application/dspxmodel/src/Master.cpp b/src/libs/application/dspxmodel/src/Master.cpp
index 95dd4e05..7f971fc6 100644
--- a/src/libs/application/dspxmodel/src/Master.cpp
+++ b/src/libs/application/dspxmodel/src/Master.cpp
@@ -16,18 +16,36 @@ namespace dspx {
public:
Master *q_ptr;
ModelPrivate *pModel;
+ Handle handle;
BusControl *control;
+ bool multiChannelOutput{};
};
Master::Master(Model *model) : QObject(model), d_ptr(new MasterPrivate) {
Q_D(Master);
d->q_ptr = this;
d->pModel = ModelPrivate::get(model);
+ d->handle = model->handle();
d->control = d->pModel->createObject(model->handle());
}
void Master::handleProxySetEntityProperty(int property, const QVariant &value) {
Q_D(Master);
- ModelPrivate::proxySetEntityPropertyNotify(d->control, property, value);
+ switch (property) {
+ case ModelStrategy::P_ControlGain:
+ case ModelStrategy::P_ControlPan:
+ case ModelStrategy::P_ControlMute: {
+ ModelPrivate::proxySetEntityPropertyNotify(d->control, property, value);
+ break;
+ }
+ case ModelStrategy::P_MultiChannelOutput: {
+ d->multiChannelOutput = value.toBool();
+ Q_EMIT multiChannelOutputChanged(d->multiChannelOutput);
+ break;
+ }
+ default:
+ Q_UNREACHABLE();
+ }
+
}
Master::~Master() = default;
@@ -35,6 +53,14 @@ namespace dspx {
Q_D(const Master);
return d->control;
}
+ bool Master::multiChannelOutput() const {
+ Q_D(const Master);
+ return d->multiChannelOutput;
+ }
+ void Master::setMultiChannelOutput(bool multiChannelOutput) {
+ Q_D(Master);
+ d->pModel->strategy->setEntityProperty(d->handle, ModelStrategy::P_MultiChannelOutput, multiChannelOutput);
+ }
QDspx::Master Master::toQDspx() const {
return {
diff --git a/src/libs/application/dspxmodel/src/Master.h b/src/libs/application/dspxmodel/src/Master.h
index 507ae783..d0ac456d 100644
--- a/src/libs/application/dspxmodel/src/Master.h
+++ b/src/libs/application/dspxmodel/src/Master.h
@@ -1,9 +1,10 @@
#ifndef DIFFSCOPE_DSPX_MODEL_MASTER_H
#define DIFFSCOPE_DSPX_MODEL_MASTER_H
+#include
#include
-#include
+#include
namespace QDspx {
struct Master;
@@ -19,21 +20,27 @@ namespace dspx {
class MasterPrivate;
- class Master : public QObject {
+ class DSPX_MODEL_EXPORT Master : public QObject {
Q_OBJECT
QML_ELEMENT
QML_UNCREATABLE("")
Q_DECLARE_PRIVATE(Master)
Q_PROPERTY(BusControl *control READ control CONSTANT)
-
+ Q_PROPERTY(bool multiChannelOutput READ multiChannelOutput WRITE setMultiChannelOutput NOTIFY multiChannelOutputChanged)
public:
~Master() override;
BusControl *control() const;
+ bool multiChannelOutput() const;
+ void setMultiChannelOutput(bool multiChannelOutput);
+
QDspx::Master toQDspx() const;
void fromQDspx(const QDspx::Master &master);
+ Q_SIGNALS:
+ void multiChannelOutputChanged(bool multiChannelOutput);
+
private:
friend class ModelPrivate;
explicit Master(Model *model);
diff --git a/src/libs/application/dspxmodel/src/Model.cpp b/src/libs/application/dspxmodel/src/Model.cpp
index f02d945c..cce0485a 100644
--- a/src/libs/application/dspxmodel/src/Model.cpp
+++ b/src/libs/application/dspxmodel/src/Model.cpp
@@ -63,12 +63,15 @@ namespace dspx {
labels = new LabelSequence(strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Labels), q);
tempos = new TempoSequence(strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Tempos), q);
timeSignatures = new TimeSignatureSequence(strategy->getAssociatedSubEntity(handle, ModelStrategy::R_TimeSignatures), q);
- trackList = new TrackList(strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Children), q);
+ tracks = new TrackList(strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Children), q);
workspace = new Workspace(strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Workspace), q);
}
void ModelPrivate::handleNotifications() {
Q_Q(Model);
+ QObject::connect(strategy, &ModelStrategy::destroyEntityNotified, q, [=, this](Handle handle) {
+ handleEntityDestroyed(handle);
+ });
QObject::connect(strategy, &ModelStrategy::insertIntoSequenceContainerNotified, q, [=, this](Handle sequenceContainerEntity, Handle entity) {
if (auto sequenceContainerObject = mapToObject(sequenceContainerEntity)) {
sequenceContainerObject->handleInsertIntoSequenceContainer(entity);
@@ -196,9 +199,9 @@ namespace dspx {
return d->timeline;
}
- TrackList *Model::trackList() const {
+ TrackList *Model::tracks() const {
Q_D(const Model);
- return d->trackList;
+ return d->tracks;
}
Workspace *Model::workspace() const {
@@ -207,16 +210,27 @@ namespace dspx {
}
QDspx::Model Model::toQDspx() const {
- return {
+ QDspx::Model model = {
.version = QDspx::Model::V1,
.content = {
.global = global()->toQDspx(),
.master = master()->toQDspx(),
.timeline = timeline()->toQDspx(),
- .tracks = trackList()->toQDspx(),
+ .tracks = tracks()->toQDspx(),
.workspace = workspace()->toQDspx(),
}
};
+ model.content.workspace["diffscope"] = QJsonObject{
+ {"loop", QJsonObject{
+ {"enabled", timeline()->isLoopEnabled()},
+ {"start", timeline()->loopStart()},
+ {"length", timeline()->loopLength()},
+ }},
+ {"master", QJsonObject{
+ {"multiChannelOutput", master()->multiChannelOutput()}
+ }}
+ };
+ return model;
}
void Model::fromQDspx(const QDspx::Model &model) {
@@ -224,8 +238,27 @@ namespace dspx {
d->global->fromQDspx(model.content.global);
d->master->fromQDspx(model.content.master);
d->timeline->fromQDspx(model.content.timeline);
- d->trackList->fromQDspx(model.content.tracks);
+ d->tracks->fromQDspx(model.content.tracks);
d->workspace->fromQDspx(model.content.workspace);
+ {
+ auto loop = model.content.workspace.value("diffscope").value("loop").toObject();
+ auto enabled = loop.value("enabled").toBool();
+ auto start = loop.value("start").toInt();
+ auto length = loop.value("length").toInt();
+ if (start < 0 || length <= 0) {
+ d->timeline->setLoopEnabled(false);
+ d->timeline->setLoopStart(0);
+ d->timeline->setLoopLength(1920);
+ } else {
+ d->timeline->setLoopEnabled(enabled);
+ d->timeline->setLoopStart(start);
+ d->timeline->setLoopLength(length);
+ }
+ }
+ {
+ auto master = model.content.workspace.value("diffscope").value("master").toObject();
+ d->master->setMultiChannelOutput(master.value("multiChannelOutput").toBool());
+ }
}
Label *Model::createLabel() {
@@ -312,6 +345,14 @@ namespace dspx {
return d->createObject(handle);
}
+ void Model::destroyItem(EntityObject *object) {
+ Q_D(Model);
+ auto handle = object->handle();
+ object->d_func()->handle = {};
+ d->strategy->destroyEntity(handle);
+ object->deleteLater();
+ }
+
void Model::handleSetEntityProperty(int property, const QVariant &value) {
Q_D(Model);
switch (property) {
@@ -342,10 +383,17 @@ namespace dspx {
}
case ModelStrategy::P_ControlGain:
case ModelStrategy::P_ControlPan:
- case ModelStrategy::P_ControlMute: {
+ case ModelStrategy::P_ControlMute:
+ case ModelStrategy::P_MultiChannelOutput: {
ModelPrivate::proxySetEntityPropertyNotify(d->master, property, value);
break;
}
+ case ModelStrategy::P_LoopEnabled:
+ case ModelStrategy::P_LoopLength:
+ case ModelStrategy::P_LoopStart: {
+ ModelPrivate::proxySetEntityPropertyNotify(d->timeline, property, value);
+ break;
+ }
default:
Q_UNREACHABLE();
}
diff --git a/src/libs/application/dspxmodel/src/Model.h b/src/libs/application/dspxmodel/src/Model.h
index b94df10f..02d762f2 100644
--- a/src/libs/application/dspxmodel/src/Model.h
+++ b/src/libs/application/dspxmodel/src/Model.h
@@ -44,7 +44,7 @@ namespace dspx {
Q_PROPERTY(Global *global READ global CONSTANT)
Q_PROPERTY(Master *master READ master CONSTANT)
Q_PROPERTY(Timeline *timeline READ timeline CONSTANT)
- Q_PROPERTY(TrackList *trackList READ trackList CONSTANT)
+ Q_PROPERTY(TrackList *tracks READ tracks CONSTANT)
Q_PROPERTY(Workspace *workspace READ workspace CONSTANT)
public:
@@ -56,7 +56,7 @@ namespace dspx {
Global *global() const;
Master *master() const;
Timeline *timeline() const;
- TrackList *trackList() const;
+ TrackList *tracks() const;
Workspace *workspace() const;
QDspx::Model toQDspx() const;
@@ -77,6 +77,8 @@ namespace dspx {
Q_INVOKABLE Param *createParam();
Q_INVOKABLE Source *createSource();
+ Q_INVOKABLE void destroyItem(EntityObject *object);
+
protected:
void handleSetEntityProperty(int property, const QVariant &value) override;
diff --git a/src/libs/application/dspxmodel/src/ModelStrategy.h b/src/libs/application/dspxmodel/src/ModelStrategy.h
index e500a589..d36200c6 100644
--- a/src/libs/application/dspxmodel/src/ModelStrategy.h
+++ b/src/libs/application/dspxmodel/src/ModelStrategy.h
@@ -1,6 +1,8 @@
#ifndef DIFFSCOPE_DSPX_MODEL_MODELSTRATEGY_H
#define DIFFSCOPE_DSPX_MODEL_MODELSTRATEGY_H
+#include
+
#include
#include
@@ -55,18 +57,25 @@ namespace dspx {
P_CentShift,
P_ClipStart,
P_ClipLength,
+ P_ColorId,
P_ControlGain,
P_ControlMute,
P_ControlPan,
+ P_ControlRecord,
P_ControlSolo,
P_Denominator,
P_EditorId,
P_EditorName,
+ P_Height,
P_JsonObject,
P_KeyNumber,
P_Language,
P_Length,
+ P_LoopEnabled,
+ P_LoopLength,
+ P_LoopStart,
P_Measure,
+ P_MultiChannelOutput,
P_Name,
P_Numerator,
P_Onset,
@@ -102,10 +111,29 @@ namespace dspx {
R_Workspace,
};
+ struct PropertySpec {
+ Property propertyType;
+ QMetaType::Type metaType;
+ bool (*validate)(const QVariant &value);
+
+ PropertySpec(Property propertyType, QMetaType::Type metaType, bool (*validate)(const QVariant &value) = [](const QVariant &) { return true; })
+ : propertyType(propertyType), metaType(metaType), validate(validate) {
+ }
+ };
+
+ static inline Entity getAssociatedSubEntityTypeFromEntityTypeAndRelationship(Entity entityType, Relationship relationship);
+ static inline std::vector getEntityPropertySpecsFromEntityType(Entity entityType);
+ static inline PropertySpec getPropertySpecFromEntityTypeAndPropertyType(Entity entityType, Property propertyType);
+ static inline bool isEntityTypeAndPropertyTypeCompatible(Entity entityType, Property propertyType);
+
virtual Handle createEntity(Entity entityType) = 0;
virtual void destroyEntity(Handle entity) = 0;
virtual Entity getEntityType(Handle entity) = 0;
+ virtual QList getEntitiesFromSequenceContainer(Handle sequenceContainerEntity) = 0;
+ virtual QList getEntitiesFromListContainer(Handle listContainerEntity) = 0;
+ virtual QList> getEntitiesFromMapContainer(Handle mapContainerEntity) = 0;
+
virtual bool insertIntoSequenceContainer(Handle sequenceContainerEntity, Handle entity) = 0;
virtual bool insertIntoListContainer(Handle listContainerEntity, Handle entity, int index) = 0;
virtual bool insertIntoMapContainer(Handle mapContainerEntity, Handle entity, const QString &key) = 0;
@@ -144,6 +172,222 @@ namespace dspx {
void rotateDataArrayNotified(Handle dataContainerEntity, int leftIndex, int middleIndex, int rightIndex);
};
+ ModelStrategy::Entity ModelStrategy::getAssociatedSubEntityTypeFromEntityTypeAndRelationship(Entity entityType, Relationship relationship) {
+ if (relationship == R_Children) {
+ if (entityType == EI_Global) {
+ return EL_Tracks;
+ }
+ if (entityType == EI_Track) {
+ return ES_Clips;
+ }
+ if (entityType == EI_SingingClip) {
+ return ES_Notes;
+ }
+ if (entityType == EI_ParamCurveAnchor) {
+ return ES_ParamCurveAnchorNodes;
+ }
+ if (entityType == EI_ParamCurveFree) {
+ return ED_ParamCurveFreeValues;
+ }
+ } else if (relationship == R_Labels) {
+ if (entityType == EI_Global) {
+ return ES_Labels;
+ }
+ } else if (relationship == R_ParamCurvesEdited || relationship == R_ParamCurvesOriginal || relationship == R_ParamCurvesTransform) {
+ if (entityType == EI_Param) {
+ return ES_ParamCurves;
+ }
+ } else if (relationship == R_Params) {
+ if (entityType == EI_SingingClip) {
+ return EM_Params;
+ }
+ } else if (relationship == R_PhonemesEdited || relationship == R_PhonemesOriginal) {
+ if (entityType == EI_Note) {
+ return EL_Phonemes;
+ }
+ } else if (relationship == R_Sources) {
+ if (entityType == EI_SingingClip) {
+ return EM_Sources;
+ }
+ } else if (relationship == R_Tempos) {
+ if (entityType == EI_Global) {
+ return ES_Tempos;
+ }
+ } else if (relationship == R_TimeSignatures) {
+ if (entityType == EI_Global) {
+ return ES_TimeSignatures;
+ }
+ } else if (relationship == R_VibratoPointsAmplitude || relationship == R_VibratoPointsFrequency) {
+ if (entityType == EI_Note) {
+ return ED_VibratoPoints;
+ }
+ } else if (relationship == R_Workspace) {
+ if (entityType == EI_Global || entityType == EI_Track || entityType == EI_AudioClip || entityType == EI_SingingClip || entityType == EI_Note) {
+ return EM_Workspace;
+ }
+ }
+ Q_UNREACHABLE();
+ }
+
+ std::vector ModelStrategy::getEntityPropertySpecsFromEntityType(Entity entityType) {
+ static auto validateCentShift = [](const QVariant &value) {
+ auto v = value.toInt();
+ return v >= -50 && v <= 50;
+ };
+ static auto validatePan = [](const QVariant &value) {
+ auto v = value.toDouble();
+ return v >= -1 && v <= 1;
+ };
+ static auto validateIntGreaterOrEqualZero = [](const QVariant &value) {
+ auto v = value.toInt();
+ return v >= 0;
+ };
+ static auto validateIntGreaterZero = [](const QVariant &value) {
+ auto v = value.toInt();
+ return v > 0;
+ };
+ static auto validateDoubleGreaterOrEqualZero = [](const QVariant &value) {
+ auto v = value.toDouble();
+ return v >= 0;
+ };
+ static auto validateDoubleBetweenZeroAndOne = [](const QVariant &value) {
+ auto v = value.toDouble();
+ return v >= 0 && v <= 1;
+ };
+ static auto validateKeyNumber = [](const QVariant &value) {
+ auto v = value.toInt();
+ return v >= 0 && v <= 127;
+ };
+ static auto validateTempo = [](const QVariant &value) {
+ auto v = value.toDouble();
+ return v >= 10 && v <= 1000;
+ };
+ static auto validateTimeSignatureDenominator = [](const QVariant &value) {
+ auto v = value.toInt();
+ return v == 1 || v == 2 || v == 4 || v == 8 || v == 16 || v == 32 || v == 64 || v == 128;
+ };
+ static auto validateTimeSignatureNumerator = [](const QVariant &value) {
+ auto v = value.toInt();
+ return v >= 1;
+ };
+ switch (entityType) {
+ case EI_AudioClip: return {
+ {P_Name, QMetaType::QString},
+ {P_ControlGain, QMetaType::Double},
+ {P_ControlPan, QMetaType::Double, validatePan},
+ {P_ControlMute, QMetaType::Bool},
+ {P_Position, QMetaType::Int},
+ {P_Length, QMetaType::Int, validateIntGreaterOrEqualZero},
+ {P_ClipStart, QMetaType::Int, validateIntGreaterOrEqualZero},
+ {P_ClipLength, QMetaType::Int, validateIntGreaterOrEqualZero},
+ {P_Path, QMetaType::QString}
+ };
+ case EI_Global: return {
+ {P_Name, QMetaType::QString},
+ {P_Author, QMetaType::QString},
+ {P_CentShift, QMetaType::Int, validateCentShift},
+ {P_EditorId, QMetaType::QString},
+ {P_EditorName, QMetaType::QString},
+ {P_ControlGain, QMetaType::Double},
+ {P_ControlPan, QMetaType::Double, validatePan},
+ {P_ControlMute, QMetaType::Bool},
+ {P_MultiChannelOutput, QMetaType::Bool},
+ {P_LoopEnabled, QMetaType::Bool},
+ {P_LoopStart, QMetaType::Int, validateIntGreaterOrEqualZero},
+ {P_LoopLength, QMetaType::Int, validateIntGreaterZero},
+ };
+ case EI_Label: return {
+ {P_Position, QMetaType::Int, validateIntGreaterOrEqualZero},
+ {P_Text, QMetaType::QString}
+ };
+ case EI_Note: return {
+ {P_CentShift, QMetaType::Int, validateCentShift},
+ {P_KeyNumber, QMetaType::Int, validateKeyNumber},
+ {P_Language, QMetaType::QString},
+ {P_Length, QMetaType::Int, validateIntGreaterOrEqualZero},
+ {P_Text, QMetaType::QString},
+ {P_Position, QMetaType::Int, validateIntGreaterOrEqualZero},
+ {P_PronunciationOriginal, QMetaType::QString},
+ {P_PronunciationEdited, QMetaType::QString},
+ {P_VibratoAmplitude, QMetaType::Int, validateIntGreaterOrEqualZero},
+ {P_VibratoEnd, QMetaType::Double, validateDoubleBetweenZeroAndOne},
+ {P_VibratoFrequency, QMetaType::Double, validateDoubleGreaterOrEqualZero},
+ {P_VibratoOffset, QMetaType::Int},
+ {P_VibratoPhase, QMetaType::Double, validateDoubleBetweenZeroAndOne},
+ {P_VibratoStart, QMetaType::Double, validateDoubleBetweenZeroAndOne}
+ };
+ case EI_Param: return {};
+ case EI_ParamCurveAnchor: return {
+ {P_Position, QMetaType::Int}
+ };
+ case EI_ParamCurveFree: return {
+ {P_Position, QMetaType::Int}
+ };
+ case EI_ParamCurveAnchorNode: return {
+ {P_Type, QMetaType::Int},
+ {P_Position, QMetaType::Int, validateIntGreaterOrEqualZero},
+ {P_Value, QMetaType::Int}
+ };
+ case EI_Phoneme: return {
+ {P_Language, QMetaType::QString},
+ {P_Position, QMetaType::Int},
+ {P_Text, QMetaType::QString},
+ {P_Onset, QMetaType::Bool}
+ };
+ case EI_SingingClip: return {
+ {P_Name, QMetaType::QString},
+ {P_ControlGain, QMetaType::Double},
+ {P_ControlPan, QMetaType::Double, validatePan},
+ {P_ControlMute, QMetaType::Bool},
+ {P_Position, QMetaType::Int},
+ {P_Length, QMetaType::Int, validateIntGreaterOrEqualZero},
+ {P_ClipStart, QMetaType::Int, validateIntGreaterOrEqualZero},
+ {P_ClipLength, QMetaType::Int, validateIntGreaterOrEqualZero}
+ };
+ case EI_Source: return {
+ {P_JsonObject, QMetaType::QJsonObject}
+ };
+ case EI_Tempo: return {
+ {P_Position, QMetaType::Int, validateIntGreaterOrEqualZero},
+ {P_Value, QMetaType::Double, validateTempo}
+ };
+ case EI_TimeSignature: return {
+ {P_Measure, QMetaType::Int, validateIntGreaterOrEqualZero},
+ {P_Numerator, QMetaType::Int, validateTimeSignatureNumerator},
+ {P_Denominator, QMetaType::Int, validateTimeSignatureDenominator}
+ };
+ case EI_Track: return {
+ {P_Name, QMetaType::QString},
+ {P_ColorId, QMetaType::Int},
+ {P_ControlGain, QMetaType::Double},
+ {P_ControlPan, QMetaType::Double, validatePan},
+ {P_ControlMute, QMetaType::Bool},
+ {P_ControlRecord, QMetaType::Bool},
+ {P_ControlSolo, QMetaType::Bool},
+ {P_Height, QMetaType::Double, validateDoubleGreaterOrEqualZero}
+ };
+ case EI_WorkspaceInfo: return {
+ {P_JsonObject, QMetaType::QJsonObject}
+ };
+ default: Q_UNREACHABLE();
+ }
+ }
+
+ inline ModelStrategy::PropertySpec ModelStrategy::getPropertySpecFromEntityTypeAndPropertyType(Entity entityType, Property propertyType) {
+ auto v = getEntityPropertySpecsFromEntityType(entityType);
+ auto it = std::ranges::find_if(v, [propertyType](const auto &spec) { return spec.propertyType == propertyType; });
+ if (it == v.end()) {
+ Q_UNREACHABLE();
+ }
+ return *it;
+ }
+
+ inline bool ModelStrategy::isEntityTypeAndPropertyTypeCompatible(Entity entityType, Property propertyType) {
+ return std::ranges::any_of(getEntityPropertySpecsFromEntityType(entityType), [propertyType](const auto &spec) { return spec.propertyType == propertyType; });
+ }
+
+
+
}
#endif //DIFFSCOPE_DSPX_MODEL_MODELSTRATEGY_H
diff --git a/src/libs/application/dspxmodel/src/Model_p.h b/src/libs/application/dspxmodel/src/Model_p.h
index 56b8448e..4f65328a 100644
--- a/src/libs/application/dspxmodel/src/Model_p.h
+++ b/src/libs/application/dspxmodel/src/Model_p.h
@@ -26,7 +26,7 @@ namespace dspx {
LabelSequence *labels;
TempoSequence *tempos;
TimeSignatureSequence *timeSignatures;
- TrackList *trackList;
+ TrackList *tracks;
Workspace *workspace;
QHash objectMap;
@@ -49,6 +49,12 @@ namespace dspx {
EntityObject *mapToObject(Handle handle) const;
Handle mapToHandle(EntityObject *object) const;
+ template
+ T *createObject(S *superItem, Handle handle) {
+ Q_Q(Model);
+ return new T(superItem, handle, q);
+ }
+
template
T *createObject(Handle handle) {
Q_Q(Model);
diff --git a/src/libs/application/dspxmodel/src/Note.cpp b/src/libs/application/dspxmodel/src/Note.cpp
index 9f127f29..3760f649 100644
--- a/src/libs/application/dspxmodel/src/Note.cpp
+++ b/src/libs/application/dspxmodel/src/Note.cpp
@@ -1,4 +1,5 @@
#include "Note.h"
+#include "Note_p.h"
#include
#include
@@ -8,42 +9,13 @@
#include
#include
#include
-#include
+#include
#include
#include
#include
namespace dspx {
- class NotePrivate {
- Q_DECLARE_PUBLIC(Note)
- public:
- Note *q_ptr;
- ModelPrivate *pModel;
-
- int centShift;
- int keyNum;
- QString language;
- int length;
- QString lyric;
- PhonemeInfo *phonemes;
- int pos;
- Pronunciation *pronunciation;
- Vibrato *vibrato;
- Workspace *workspace;
- SingingClip *singingClip{};
- bool overlapped{};
-
- void setCentShiftUnchecked(int centShift_);
- void setCentShift(int centShift_);
- void setKeyNumUnchecked(int keyNum_);
- void setKeyNum(int keyNum_);
- void setLengthUnchecked(int length_);
- void setLength(int length_);
- void setPosUnchecked(int pos_);
- void setPos(int pos_);
- };
-
void NotePrivate::setCentShiftUnchecked(int centShift_) {
Q_Q(Note);
pModel->strategy->setEntityProperty(q->handle(), ModelStrategy::P_CentShift, centShift_);
@@ -51,8 +23,9 @@ namespace dspx {
void NotePrivate::setCentShift(int centShift_) {
Q_Q(Note);
- if (auto engine = qjsEngine(q); engine && (centShift_ < -50 || centShift_ > 50)) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("CentShift must be in range [-50, 50]"));
+ if ((centShift_ < -50 || centShift_ > 50)) {
+ if (auto engine = qjsEngine(q))
+ engine->throwError(QJSValue::RangeError, QStringLiteral("CentShift must be in range [-50, 50]"));
return;
}
setCentShiftUnchecked(centShift_);
@@ -65,8 +38,9 @@ namespace dspx {
void NotePrivate::setKeyNum(int keyNum_) {
Q_Q(Note);
- if (auto engine = qjsEngine(q); engine && (keyNum_ < 0 || keyNum_ > 127)) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("KeyNum must be in range [0, 127]"));
+ if ((keyNum_ < 0 || keyNum_ > 127)) {
+ if (auto engine = qjsEngine(q))
+ engine->throwError(QJSValue::RangeError, QStringLiteral("KeyNum must be in range [0, 127]"));
return;
}
setKeyNumUnchecked(keyNum_);
@@ -79,8 +53,9 @@ namespace dspx {
void NotePrivate::setLength(int length_) {
Q_Q(Note);
- if (auto engine = qjsEngine(q); engine && length_ < 0) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Length must be greater than or equal to 0"));
+ if (length_ < 0) {
+ if (auto engine = qjsEngine(q))
+ engine->throwError(QJSValue::RangeError, QStringLiteral("Length must be greater than or equal to 0"));
return;
}
setLengthUnchecked(length_);
@@ -93,13 +68,30 @@ namespace dspx {
void NotePrivate::setPos(int pos_) {
Q_Q(Note);
- if (auto engine = qjsEngine(q); engine && pos_ < 0) {
- engine->throwError(QJSValue::RangeError, QStringLiteral("Pos must be greater than or equal to 0"));
+ if (pos_ < 0) {
+ if (auto engine = qjsEngine(q))
+ engine->throwError(QJSValue::RangeError, QStringLiteral("Pos must be greater than or equal to 0"));
return;
}
setPosUnchecked(pos_);
}
+ void NotePrivate::setOverlapped(Note *item, bool overlapped) {
+ auto d = item->d_func();
+ if (d->overlapped != overlapped) {
+ d->overlapped = overlapped;
+ Q_EMIT item->overlappedChanged(overlapped);
+ }
+ }
+
+ void NotePrivate::setNoteSequence(Note *item, NoteSequence *noteSequence) {
+ auto d = item->d_func();
+ if (d->noteSequence != noteSequence) {
+ d->noteSequence = noteSequence;
+ Q_EMIT item->noteSequenceChanged();
+ }
+ }
+
Note::Note(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new NotePrivate) {
Q_D(Note);
Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::EI_Note);
@@ -111,7 +103,7 @@ namespace dspx {
d->length = d->pModel->strategy->getEntityProperty(handle, ModelStrategy::P_Length).toInt();
d->lyric = d->pModel->strategy->getEntityProperty(handle, ModelStrategy::P_Text).toString();
d->pos = d->pModel->strategy->getEntityProperty(handle, ModelStrategy::P_Position).toInt();
- d->phonemes = d->pModel->createObject(handle);
+ d->phonemes = d->pModel->createObject(this, handle);
d->pronunciation = d->pModel->createObject(handle);
d->vibrato = d->pModel->createObject(handle);
d->workspace = d->pModel->createObject(d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Workspace));
@@ -203,9 +195,9 @@ namespace dspx {
return d->workspace;
}
- SingingClip *Note::singingClip() const {
+ NoteSequence *Note::noteSequence() const {
Q_D(const Note);
- return d->singingClip;
+ return d->noteSequence;
}
bool Note::isOverlapped() const {
@@ -293,18 +285,6 @@ namespace dspx {
}
}
- void Note::setSingingClip(SingingClip *singingClip) {
- Q_D(Note);
- d->singingClip = singingClip;
- Q_EMIT singingClipChanged();
- }
-
- void Note::setOverlapped(bool overlapped) {
- Q_D(Note);
- d->overlapped = overlapped;
- Q_EMIT overlappedChanged(overlapped);
- }
-
}
#include "moc_Note.cpp"
diff --git a/src/libs/application/dspxmodel/src/Note.h b/src/libs/application/dspxmodel/src/Note.h
index 71fb896f..e96311d5 100644
--- a/src/libs/application/dspxmodel/src/Note.h
+++ b/src/libs/application/dspxmodel/src/Note.h
@@ -15,7 +15,7 @@ namespace dspx {
class Pronunciation;
class Vibrato;
class Workspace;
- class SingingClip;
+ class NoteSequence;
class NoteSequencePrivate;
@@ -36,7 +36,7 @@ namespace dspx {
Q_PROPERTY(Pronunciation *pronunciation READ pronunciation CONSTANT)
Q_PROPERTY(Vibrato *vibrato READ vibrato CONSTANT)
Q_PROPERTY(Workspace *workspace READ workspace CONSTANT)
- Q_PROPERTY(SingingClip *singingClip READ singingClip NOTIFY singingClipChanged)
+ Q_PROPERTY(NoteSequence *noteSequence READ noteSequence NOTIFY noteSequenceChanged)
Q_PROPERTY(bool overlapped READ isOverlapped NOTIFY overlappedChanged)
public:
@@ -68,7 +68,7 @@ namespace dspx {
Workspace *workspace() const;
- SingingClip *singingClip() const;
+ NoteSequence *noteSequence() const;
bool isOverlapped() const;
@@ -82,7 +82,7 @@ namespace dspx {
void lengthChanged(int length);
void lyricChanged(const QString &lyric);
void posChanged(int pos);
- void singingClipChanged();
+ void noteSequenceChanged();
void overlappedChanged(bool overlapped);
protected:
@@ -90,11 +90,8 @@ namespace dspx {
private:
friend class ModelPrivate;
- friend class NoteSequencePrivate;
explicit Note(Handle handle, Model *model);
QScopedPointer d_ptr;
- void setSingingClip(SingingClip *singingClip);
- void setOverlapped(bool overlapped);
};
}
diff --git a/src/libs/application/dspxmodel/src/NoteSequence.cpp b/src/libs/application/dspxmodel/src/NoteSequence.cpp
index 8aed6974..dd73d9c4 100644
--- a/src/libs/application/dspxmodel/src/NoteSequence.cpp
+++ b/src/libs/application/dspxmodel/src/NoteSequence.cpp
@@ -1,4 +1,5 @@
#include "NoteSequence.h"
+#include "NoteSequence_p.h"
#include
#include
@@ -9,41 +10,24 @@
#include
#include
#include
-#include
#include
-#include
-#include
namespace dspx {
- static void setNoteOverlapped(Note *item, bool overlapped);
-
- class NoteSequencePrivate : public RangeSequenceData {
- Q_DECLARE_PUBLIC(NoteSequence)
- public:
- SingingClip *singingClip{};
- static void setOverlapped(Note *item, bool overlapped) {
- item->setOverlapped(overlapped);
- }
- static void setSingingClip(Note *item, SingingClip *singingClip) {
- item->setSingingClip(singingClip);
- }
- };
-
- void setNoteOverlapped(Note *item, bool overlapped) {
- NoteSequencePrivate::setOverlapped(item, overlapped);
- }
-
- NoteSequence::NoteSequence(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new NoteSequencePrivate) {
+ NoteSequence::NoteSequence(SingingClip *singingClip, Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new NoteSequencePrivate) {
Q_D(NoteSequence);
Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::ES_Notes);
d->q_ptr = this;
d->pModel = ModelPrivate::get(model);
+ d->singingClip = singingClip;
+
+ d->init(model->strategy()->getEntitiesFromSequenceContainer(handle));
+
connect(this, &NoteSequence::itemInserted, this, [=](Note *item) {
- NoteSequencePrivate::setSingingClip(item, d->singingClip);
+ NotePrivate::setNoteSequence(item, this);
});
connect(this, &NoteSequence::itemRemoved, this, [=](Note *item) {
- NoteSequencePrivate::setSingingClip(item, nullptr);
+ NotePrivate::setNoteSequence(item, nullptr);
});
}
@@ -119,11 +103,6 @@ namespace dspx {
}
}
- void NoteSequence::setSingingClip(SingingClip *singingClip) {
- Q_D(NoteSequence);
- d->singingClip = singingClip;
- }
-
void NoteSequence::handleInsertIntoSequenceContainer(Handle entity) {
Q_D(NoteSequence);
d->handleInsertIntoSequenceContainer(entity);
diff --git a/src/libs/application/dspxmodel/src/NoteSequence.h b/src/libs/application/dspxmodel/src/NoteSequence.h
index 65cb4948..211c09ee 100644
--- a/src/libs/application/dspxmodel/src/NoteSequence.h
+++ b/src/libs/application/dspxmodel/src/NoteSequence.h
@@ -4,6 +4,7 @@
#include
#include
+#include
namespace QDspx {
struct Note;
@@ -46,6 +47,10 @@ namespace dspx {
SingingClip *singingClip() const;
+ auto asRange() const {
+ return impl::SequenceRange(this);
+ }
+
Q_SIGNALS:
void itemAboutToInsert(Note *item);
void itemInserted(Note *item);
@@ -61,9 +66,7 @@ namespace dspx {
private:
friend class ModelPrivate;
- friend class SingingClip;
- explicit NoteSequence(Handle handle, Model *model);
- void setSingingClip(SingingClip *singingClip);
+ explicit NoteSequence(SingingClip *singingClip, Handle handle, Model *model);
QScopedPointer d_ptr;
};
diff --git a/src/libs/application/dspxmodel/src/NoteSequence_p.h b/src/libs/application/dspxmodel/src/NoteSequence_p.h
new file mode 100644
index 00000000..950ff19b
--- /dev/null
+++ b/src/libs/application/dspxmodel/src/NoteSequence_p.h
@@ -0,0 +1,19 @@
+#ifndef DIFFSCOPE_DSPX_MODEL_NOTESEQUENCE_P_H
+#define DIFFSCOPE_DSPX_MODEL_NOTESEQUENCE_P_H
+
+#include
+
+#include
+#include
+
+namespace dspx {
+
+ class NoteSequencePrivate : public RangeSequenceData {
+ Q_DECLARE_PUBLIC(NoteSequence)
+ public:
+ SingingClip *singingClip{};
+ };
+
+}
+
+#endif //DIFFSCOPE_DSPX_MODEL_NOTESEQUENCE_P_H
\ No newline at end of file
diff --git a/src/libs/application/dspxmodel/src/Note_p.h b/src/libs/application/dspxmodel/src/Note_p.h
new file mode 100644
index 00000000..c6a70912
--- /dev/null
+++ b/src/libs/application/dspxmodel/src/Note_p.h
@@ -0,0 +1,42 @@
+#ifndef DIFFSCOPE_DSPX_MODEL_NOTE_P_H
+#define DIFFSCOPE_DSPX_MODEL_NOTE_P_H
+
+#include
+
+namespace dspx {
+
+ class NotePrivate {
+ Q_DECLARE_PUBLIC(Note)
+ public:
+ Note *q_ptr;
+ ModelPrivate *pModel;
+
+ int centShift;
+ int keyNum;
+ QString language;
+ int length;
+ QString lyric;
+ PhonemeInfo *phonemes;
+ int pos;
+ Pronunciation *pronunciation;
+ Vibrato *vibrato;
+ Workspace *workspace;
+ NoteSequence *noteSequence{};
+ bool overlapped{};
+
+ void setCentShiftUnchecked(int centShift_);
+ void setCentShift(int centShift_);
+ void setKeyNumUnchecked(int keyNum_);
+ void setKeyNum(int keyNum_);
+ void setLengthUnchecked(int length_);
+ void setLength(int length_);
+ void setPosUnchecked(int pos_);
+ void setPos(int pos_);
+
+ static void setOverlapped(Note *item, bool overlapped);
+ static void setNoteSequence(Note *item, NoteSequence *noteSequence);
+ };
+
+}
+
+#endif //DIFFSCOPE_DSPX_MODEL_NOTE_P_H
\ No newline at end of file
diff --git a/src/libs/application/dspxmodel/src/Param.cpp b/src/libs/application/dspxmodel/src/Param.cpp
index 7c46e1ac..b81c7f12 100644
--- a/src/libs/application/dspxmodel/src/Param.cpp
+++ b/src/libs/application/dspxmodel/src/Param.cpp
@@ -1,32 +1,33 @@
#include "Param.h"
+#include "Param_p.h"
#include
#include
#include
+#include
#include
namespace dspx {
- class ParamPrivate {
- Q_DECLARE_PUBLIC(Param)
- public:
- Param *q_ptr;
- ModelPrivate *pModel;
- ParamCurveSequence *original;
- ParamCurveSequence *transform;
- ParamCurveSequence *edited;
- };
+ void ParamPrivate::setParamMap(Param *item, ParamMap *paramMap) {
+ auto d = item->d_func();
+ if (d->paramMap != paramMap) {
+ d->paramMap = paramMap;
+ Q_EMIT item->paramMapChanged();
+ }
+ }
Param::Param(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new ParamPrivate) {
Q_D(Param);
Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::EI_Param);
d->q_ptr = this;
d->pModel = ModelPrivate::get(model);
+ d->paramMap = nullptr;
- d->original = d->pModel->createObject(d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_ParamCurvesOriginal));
- d->transform = d->pModel->createObject(d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_ParamCurvesTransform));
- d->edited = d->pModel->createObject(d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_ParamCurvesEdited));
+ d->original = d->pModel->createObject(this, d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_ParamCurvesOriginal));
+ d->transform = d->pModel->createObject(this, d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_ParamCurvesTransform));
+ d->edited = d->pModel->createObject(this, d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_ParamCurvesEdited));
}
Param::~Param() = default;
@@ -62,6 +63,11 @@ namespace dspx {
d->edited->fromQDspx(param.edited);
}
+ ParamMap *Param::paramMap() const {
+ Q_D(const Param);
+ return d->paramMap;
+ }
+
}
#include "moc_Param.cpp"
diff --git a/src/libs/application/dspxmodel/src/Param.h b/src/libs/application/dspxmodel/src/Param.h
index 46d26c4a..dbb04407 100644
--- a/src/libs/application/dspxmodel/src/Param.h
+++ b/src/libs/application/dspxmodel/src/Param.h
@@ -12,6 +12,7 @@ namespace QDspx {
namespace dspx {
class ParamCurveSequence;
+ class ParamMap;
class ParamPrivate;
class DSPX_MODEL_EXPORT Param : public EntityObject {
@@ -22,6 +23,7 @@ namespace dspx {
Q_PROPERTY(ParamCurveSequence *original READ original CONSTANT)
Q_PROPERTY(ParamCurveSequence *transform READ transform CONSTANT)
Q_PROPERTY(ParamCurveSequence *edited READ edited CONSTANT)
+ Q_PROPERTY(ParamMap *paramMap READ paramMap CONSTANT)
public:
~Param() override;
@@ -30,9 +32,14 @@ namespace dspx {
ParamCurveSequence *transform() const;
ParamCurveSequence *edited() const;
+ ParamMap *paramMap() const;
+
QDspx::Param toQDspx() const;
void fromQDspx(const QDspx::Param ¶m);
+ Q_SIGNALS:
+ void paramMapChanged();
+
private:
friend class ModelPrivate;
explicit Param(Handle handle, Model *model);
diff --git a/src/libs/application/dspxmodel/src/ParamCurve.cpp b/src/libs/application/dspxmodel/src/ParamCurve.cpp
index d1336bfc..1da88780 100644
--- a/src/libs/application/dspxmodel/src/ParamCurve.cpp
+++ b/src/libs/application/dspxmodel/src/ParamCurve.cpp
@@ -1,4 +1,5 @@
#include "ParamCurve.h"
+#include "ParamCurve_p.h"
#include
#include
@@ -7,18 +8,19 @@
#include
#include
#include
+#include
+#include
#include
namespace dspx {
- class ParamCurvePrivate {
- Q_DECLARE_PUBLIC(ParamCurve)
- public:
- ParamCurve *q_ptr;
- ModelPrivate *pModel;
- ParamCurve::CurveType type;
- int start{};
- };
+ void ParamCurvePrivate::setParamCurveSequence(ParamCurve *paramCurve, ParamCurveSequence *paramCurveSequence) {
+ auto d = paramCurve->d_func();
+ if (d->paramCurveSequence == paramCurveSequence)
+ return;
+ d->paramCurveSequence = paramCurveSequence;
+ Q_EMIT paramCurve->paramCurveSequenceChanged();
+ }
ParamCurve::ParamCurve(CurveType type, Handle handle, Model *model)
: EntityObject(handle, model), d_ptr(new ParamCurvePrivate) {
@@ -26,6 +28,7 @@ namespace dspx {
d->q_ptr = this;
d->pModel = ModelPrivate::get(model);
d->type = type;
+ d->paramCurveSequence = nullptr;
}
ParamCurve::~ParamCurve() = default;
@@ -45,6 +48,11 @@ namespace dspx {
return d->type;
}
+ ParamCurveSequence *ParamCurve::paramCurveSequence() const {
+ Q_D(const ParamCurve);
+ return d->paramCurveSequence;
+ }
+
QDspx::ParamCurveRef ParamCurve::toQDspx() const {
Q_D(const ParamCurve);
switch (d->type) {
diff --git a/src/libs/application/dspxmodel/src/ParamCurve.h b/src/libs/application/dspxmodel/src/ParamCurve.h
index d7b55439..7cff8619 100644
--- a/src/libs/application/dspxmodel/src/ParamCurve.h
+++ b/src/libs/application/dspxmodel/src/ParamCurve.h
@@ -12,6 +12,7 @@ namespace QDspx {
namespace dspx {
+ class ParamCurveSequence;
class ParamCurvePrivate;
class DSPX_MODEL_EXPORT ParamCurve : public EntityObject {
@@ -21,6 +22,7 @@ namespace dspx {
Q_DECLARE_PRIVATE(ParamCurve)
Q_PROPERTY(int start READ start WRITE setStart NOTIFY startChanged)
Q_PROPERTY(CurveType type READ type CONSTANT)
+ Q_PROPERTY(ParamCurveSequence *paramCurveSequence READ paramCurveSequence NOTIFY paramCurveSequenceChanged)
public:
enum CurveType {
@@ -36,11 +38,14 @@ namespace dspx {
CurveType type() const;
+ ParamCurveSequence *paramCurveSequence() const;
+
QDspx::ParamCurveRef toQDspx() const;
void fromQDspx(const QDspx::ParamCurveRef &curve);
Q_SIGNALS:
void startChanged(int start);
+ void paramCurveSequenceChanged();
protected:
explicit ParamCurve(CurveType type, Handle handle, Model *model);
diff --git a/src/libs/application/dspxmodel/src/ParamCurveAnchor.cpp b/src/libs/application/dspxmodel/src/ParamCurveAnchor.cpp
index 348ffaa9..7e2bd226 100644
--- a/src/libs/application/dspxmodel/src/ParamCurveAnchor.cpp
+++ b/src/libs/application/dspxmodel/src/ParamCurveAnchor.cpp
@@ -23,7 +23,7 @@ namespace dspx {
d->q_ptr = this;
d->pModel = ModelPrivate::get(model);
- d->nodes = d->pModel->createObject(d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Children));
+ d->nodes = d->pModel->createObject(this, d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Children));
}
ParamCurveAnchor::~ParamCurveAnchor() = default;
diff --git a/src/libs/application/dspxmodel/src/ParamCurveFree.cpp b/src/libs/application/dspxmodel/src/ParamCurveFree.cpp
index 5c82eaa5..ed94fdb1 100644
--- a/src/libs/application/dspxmodel/src/ParamCurveFree.cpp
+++ b/src/libs/application/dspxmodel/src/ParamCurveFree.cpp
@@ -23,7 +23,7 @@ namespace dspx {
d->q_ptr = this;
d->pModel = ModelPrivate::get(model);
- d->values = d->pModel->createObject(d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Children));
+ d->values = d->pModel->createObject(this, d->pModel->strategy->getAssociatedSubEntity(handle, ModelStrategy::R_Children));
}
ParamCurveFree::~ParamCurveFree() = default;
diff --git a/src/libs/application/dspxmodel/src/ParamCurveSequence.cpp b/src/libs/application/dspxmodel/src/ParamCurveSequence.cpp
index 65026885..2ad19e7f 100644
--- a/src/libs/application/dspxmodel/src/ParamCurveSequence.cpp
+++ b/src/libs/application/dspxmodel/src/ParamCurveSequence.cpp
@@ -1,4 +1,5 @@
#include "ParamCurveSequence.h"
+#include "ParamCurveSequence_p.h"
#include
#include
@@ -9,6 +10,7 @@
#include
#include
+#include
#include
#include
#include
@@ -17,15 +19,21 @@
namespace dspx {
- class ParamCurveSequencePrivate : public PointSequenceData {
- Q_DECLARE_PUBLIC(ParamCurveSequence)
- };
-
- ParamCurveSequence::ParamCurveSequence(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new ParamCurveSequencePrivate) {
+ ParamCurveSequence::ParamCurveSequence(Param *param, Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new ParamCurveSequencePrivate) {
Q_D(ParamCurveSequence);
Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::ES_ParamCurves);
d->q_ptr = this;
d->pModel = ModelPrivate::get(model);
+ d->param = param;
+
+ d->init(model->strategy()->getEntitiesFromSequenceContainer(handle));
+
+ connect(this, &ParamCurveSequence::itemInserted, this, [this](ParamCurve *paramCurve) {
+ ParamCurvePrivate::setParamCurveSequence(paramCurve, this);
+ });
+ connect(this, &ParamCurveSequence::itemRemoved, this, [this](ParamCurve *paramCurve) {
+ ParamCurvePrivate::setParamCurveSequence(paramCurve, nullptr);
+ });
}
ParamCurveSequence::~ParamCurveSequence() = default;
@@ -116,6 +124,11 @@ namespace dspx {
d->handleTakeFromSequenceContainer(takenEntity, entity);
}
+ Param *ParamCurveSequence::param() const {
+ Q_D(const ParamCurveSequence);
+ return d->param;
+ }
+
}
#include "moc_ParamCurveSequence.cpp"
diff --git a/src/libs/application/dspxmodel/src/ParamCurveSequence.h b/src/libs/application/dspxmodel/src/ParamCurveSequence.h
index e2cb1779..b19c69f8 100644
--- a/src/libs/application/dspxmodel/src/ParamCurveSequence.h
+++ b/src/libs/application/dspxmodel/src/ParamCurveSequence.h
@@ -4,6 +4,7 @@
#include
#include
+#include
namespace QDspx {
struct ParamCurve;
@@ -13,6 +14,7 @@ namespace QDspx {
namespace dspx {
class ParamCurve;
+ class Param;
class ParamCurveSequencePrivate;
@@ -24,6 +26,7 @@ namespace dspx {
Q_PROPERTY(int size READ size NOTIFY sizeChanged)
Q_PROPERTY(ParamCurve *firstItem READ firstItem NOTIFY firstItemChanged)
Q_PROPERTY(ParamCurve *lastItem READ lastItem NOTIFY lastItemChanged)
+ Q_PROPERTY(Param *param READ param CONSTANT)
Q_PRIVATE_PROPERTY(d_func(), QJSValue iterable READ iterable CONSTANT)
public:
~ParamCurveSequence() override;
@@ -42,6 +45,12 @@ namespace dspx {
QList toQDspx() const;
void fromQDspx(const QList &curves);
+ Param *param() const;
+
+ auto asRange() const {
+ return impl::SequenceRange(this);
+ }
+
Q_SIGNALS:
void itemAboutToInsert(ParamCurve *item);
void itemInserted(ParamCurve *item);
@@ -57,7 +66,7 @@ namespace dspx {
private:
friend class ModelPrivate;
- explicit ParamCurveSequence(Handle handle, Model *model);
+ explicit ParamCurveSequence(Param *param, Handle handle, Model *model);
QScopedPointer d_ptr;
};
diff --git a/src/libs/application/dspxmodel/src/ParamCurveSequence_p.h b/src/libs/application/dspxmodel/src/ParamCurveSequence_p.h
new file mode 100644
index 00000000..8ffb2c96
--- /dev/null
+++ b/src/libs/application/dspxmodel/src/ParamCurveSequence_p.h
@@ -0,0 +1,19 @@
+#ifndef DIFFSCOPE_DSPX_MODEL_PARAMCURVESEQUENCE_P_H
+#define DIFFSCOPE_DSPX_MODEL_PARAMCURVESEQUENCE_P_H
+
+#include
+
+#include
+#include
+
+namespace dspx {
+
+ class ParamCurveSequencePrivate : public PointSequenceData {
+ Q_DECLARE_PUBLIC(ParamCurveSequence)
+ public:
+ Param *param{};
+ };
+
+}
+
+#endif //DIFFSCOPE_DSPX_MODEL_PARAMCURVESEQUENCE_P_H
\ No newline at end of file
diff --git a/src/libs/application/dspxmodel/src/ParamCurve_p.h b/src/libs/application/dspxmodel/src/ParamCurve_p.h
new file mode 100644
index 00000000..f8478651
--- /dev/null
+++ b/src/libs/application/dspxmodel/src/ParamCurve_p.h
@@ -0,0 +1,25 @@
+#ifndef DIFFSCOPE_DSPX_MODEL_PARAMCURVE_P_H
+#define DIFFSCOPE_DSPX_MODEL_PARAMCURVE_P_H
+
+#include
+
+namespace dspx {
+
+ class ParamCurveSequence;
+ class ModelPrivate;
+
+ class ParamCurvePrivate {
+ Q_DECLARE_PUBLIC(ParamCurve)
+ public:
+ ParamCurve *q_ptr;
+ ModelPrivate *pModel;
+ ParamCurve::CurveType type;
+ int start{};
+ ParamCurveSequence *paramCurveSequence{};
+
+ static void setParamCurveSequence(ParamCurve *item, ParamCurveSequence *paramCurveSequence);
+ };
+
+}
+
+#endif //DIFFSCOPE_DSPX_MODEL_PARAMCURVE_P_H
\ No newline at end of file
diff --git a/src/libs/application/dspxmodel/src/ParamMap.cpp b/src/libs/application/dspxmodel/src/ParamMap.cpp
index 89df5c92..e9537f23 100644
--- a/src/libs/application/dspxmodel/src/ParamMap.cpp
+++ b/src/libs/application/dspxmodel/src/ParamMap.cpp
@@ -4,20 +4,34 @@
#include
#include
+#include
#include
#include
+#include
namespace dspx {
class ParamMapPrivate : public MapData {
Q_DECLARE_PUBLIC(ParamMap)
+ public:
+ SingingClip *singingClip;
};
- ParamMap::ParamMap(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new ParamMapPrivate) {
+ ParamMap::ParamMap(SingingClip *singingClip, Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new ParamMapPrivate) {
Q_D(ParamMap);
Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::EM_Params);
d->q_ptr = this;
d->pModel = ModelPrivate::get(model);
+ d->singingClip = singingClip;
+
+ d->init(model->strategy()->getEntitiesFromMapContainer(handle));
+
+ connect(this, &ParamMap::itemInserted, this, [this](const QString &, Param *item) {
+ ParamPrivate::setParamMap(item, this);
+ });
+ connect(this, &ParamMap::itemRemoved, this, [this](const QString &, Param *item) {
+ ParamPrivate::setParamMap(item, nullptr);
+ });
}
ParamMap::~ParamMap() = default;
@@ -78,6 +92,11 @@ namespace dspx {
}
}
+ SingingClip *ParamMap::singingClip() const {
+ Q_D(const ParamMap);
+ return d->singingClip;
+ }
+
void ParamMap::handleInsertIntoMapContainer(Handle entity, const QString &key) {
Q_D(ParamMap);
d->handleInsertIntoMapContainer(entity, key);
diff --git a/src/libs/application/dspxmodel/src/ParamMap.h b/src/libs/application/dspxmodel/src/ParamMap.h
index e52f2f78..0338d524 100644
--- a/src/libs/application/dspxmodel/src/ParamMap.h
+++ b/src/libs/application/dspxmodel/src/ParamMap.h
@@ -12,6 +12,7 @@ namespace QDspx {
namespace dspx {
+ class SingingClip;
class Param;
class ParamMapPrivate;
@@ -23,6 +24,7 @@ namespace dspx {
Q_PROPERTY(int size READ size NOTIFY sizeChanged)
Q_PROPERTY(QStringList keys READ keys NOTIFY keysChanged)
Q_PROPERTY(QList items READ items NOTIFY itemsChanged)
+ Q_PROPERTY(SingingClip *singingClip READ singingClip CONSTANT)
Q_PRIVATE_PROPERTY(d_func(), QJSValue iterable READ iterable CONSTANT)
public:
@@ -39,6 +41,8 @@ namespace dspx {
QDspx::Params toQDspx() const;
void fromQDspx(const QDspx::Params ¶mMap);
+ SingingClip *singingClip() const;
+
Q_SIGNALS:
void itemAboutToInsert(const QString &key, Param *item);
void itemInserted(const QString &key, Param *item);
@@ -54,7 +58,7 @@ namespace dspx {
private:
friend class ModelPrivate;
- explicit ParamMap(Handle handle, Model *model);
+ explicit ParamMap(SingingClip *singingClip, Handle handle, Model *model);
QScopedPointer d_ptr;
};
diff --git a/src/libs/application/dspxmodel/src/Param_p.h b/src/libs/application/dspxmodel/src/Param_p.h
new file mode 100644
index 00000000..a179fe4f
--- /dev/null
+++ b/src/libs/application/dspxmodel/src/Param_p.h
@@ -0,0 +1,26 @@
+#ifndef DIFFSCOPE_DSPX_MODEL_PARAM_P_H
+#define DIFFSCOPE_DSPX_MODEL_PARAM_P_H
+
+#include
+
+namespace dspx {
+
+ class ParamMap;
+ class ModelPrivate;
+
+ class ParamPrivate {
+ Q_DECLARE_PUBLIC(Param)
+ public:
+ Param *q_ptr;
+ ModelPrivate *pModel;
+ ParamCurveSequence *original;
+ ParamCurveSequence *transform;
+ ParamCurveSequence *edited;
+ ParamMap *paramMap;
+
+ static void setParamMap(Param *item, ParamMap *paramMap);
+ };
+
+}
+
+#endif //DIFFSCOPE_DSPX_MODEL_PARAM_P_H
\ No newline at end of file
diff --git a/src/libs/application/dspxmodel/src/Phoneme.cpp b/src/libs/application/dspxmodel/src/Phoneme.cpp
index a025c6a5..32f6e792 100644
--- a/src/libs/application/dspxmodel/src/Phoneme.cpp
+++ b/src/libs/application/dspxmodel/src/Phoneme.cpp
@@ -1,4 +1,5 @@
#include "Phoneme.h"
+#include "Phoneme_p.h"
#include
#include
@@ -7,23 +8,25 @@
#include
#include
+#include
+#include
namespace dspx {
- class PhonemePrivate {
- Q_DECLARE_PUBLIC(Phoneme)
- public:
- Phoneme *q_ptr;
- QString language;
- int start;
- QString token;
- bool onset;
- };
+ void PhonemePrivate::setPhonemeList(Phoneme *item, PhonemeList *phonemeList) {
+ auto d = item->d_func();
+ if (d->phonemeList != phonemeList) {
+ d->phonemeList = phonemeList;
+ Q_EMIT item->phonemeListChanged();
+ }
+ }
Phoneme::Phoneme(Handle handle, Model *model) : EntityObject(handle, model), d_ptr(new PhonemePrivate) {
Q_D(Phoneme);
Q_ASSERT(model->strategy()->getEntityType(handle) == ModelStrategy::EI_Phoneme);
d->q_ptr = this;
+ d->pModel = ModelPrivate::get(model);
+ d->phonemeList = nullptr;
d->language = model->strategy()->getEntityProperty(handle, ModelStrategy::P_Language).toString();
d->start = model->strategy()->getEntityProperty(handle, ModelStrategy::P_Position).toInt();
d->token = model->strategy()->getEntityProperty(handle, ModelStrategy::P_Text).toString();
@@ -116,6 +119,11 @@ namespace dspx {
}
}
+ PhonemeList *Phoneme::phonemeList() const {
+ Q_D(const Phoneme);
+ return d->phonemeList;
+ }
+
}
#include "moc_Phoneme.cpp"
diff --git a/src/libs/application/dspxmodel/src/Phoneme.h b/src/libs/application/dspxmodel/src/Phoneme.h
index 527262f7..1698944c 100644
--- a/src/libs/application/dspxmodel/src/Phoneme.h
+++ b/src/libs/application/dspxmodel/src/Phoneme.h
@@ -11,6 +11,7 @@ namespace QDspx {
namespace dspx {
+ class PhonemeList;
class PhonemePrivate;
class DSPX_MODEL_EXPORT Phoneme : public EntityObject {
@@ -22,6 +23,7 @@ namespace dspx {
Q_PROPERTY(int start READ start WRITE setStart NOTIFY startChanged)
Q_PROPERTY(QString token READ token WRITE setToken NOTIFY tokenChanged)
Q_PROPERTY(bool onset READ onset WRITE setOnset NOTIFY onsetChanged)
+ Q_PROPERTY(PhonemeList *phonemeList READ phonemeList NOTIFY phonemeListChanged)
public:
~Phoneme() override;
@@ -38,6 +40,8 @@ namespace dspx {
bool onset() const;
void setOnset(bool onset);
+ PhonemeList *phonemeList() const;
+
QDspx::Phoneme toQDspx() const;
void fromQDspx(const QDspx::Phoneme &phoneme);
@@ -46,6 +50,7 @@ namespace dspx {
void startChanged(int start);
void tokenChanged(const QString &token);
void onsetChanged(bool onset);
+ void phonemeListChanged();
protected:
void handleSetEntityProperty(int property, const QVariant &value) override;
diff --git a/src/libs/application/dspxmodel/src/PhonemeInfo.cpp b/src/libs/application/dspxmodel/src/PhonemeInfo.cpp
index 48bca837..40008544 100644
--- a/src/libs/application/dspxmodel/src/PhonemeInfo.cpp
+++ b/src/libs/application/dspxmodel/src/PhonemeInfo.cpp
@@ -1,4 +1,5 @@
#include "PhonemeInfo.h"
+#include "PhonemeInfo_p.h"
#include
#include