diff --git a/.coderabbit.yaml b/.coderabbit.yaml new file mode 100644 index 00000000..114c588a --- /dev/null +++ b/.coderabbit.yaml @@ -0,0 +1,7 @@ +reviews: + path_filters: + - "!src/stk/src/**" + - "!src/stk/include/**" + - "!src/stk/rawwaves/**" + - "!src/dssi4cs/include/dssi/**" + - "!src/dssi4cs/include/ladspa/**" diff --git a/.github/scripts/release.py b/.github/scripts/release.py new file mode 100644 index 00000000..65b19ed0 --- /dev/null +++ b/.github/scripts/release.py @@ -0,0 +1,98 @@ +"""Read the release version and select labelled merges for native releases.""" + +import argparse +import json +import os +from pathlib import Path +import re +import subprocess + + +VERSION = re.compile( + r"(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)\.(0|[1-9][0-9]*)" + r"(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?" +) +RELEASE_BRANCHES = {"refs/heads/main", "refs/heads/develop"} + + +def read_version(root): + package = json.loads((root / "package.json").read_text()) + lock = json.loads((root / "package-lock.json").read_text()) + version = package["version"] + match = VERSION.fullmatch(version) if isinstance(version, str) else None + if not match or any( + part.isdigit() and len(part) > 1 and part.startswith("0") + for part in (match[4] or "").split(".") + ): + raise ValueError("package.json must contain a SemVer version without build metadata") + if lock["version"] != version or lock["packages"][""]["version"] != version: + raise ValueError("package.json and package-lock.json versions must match") + return version + + +def release_metadata(version, event, ref, sha, repository, pull_requests): + release = event == "push" and ref in RELEASE_BRANCHES and any( + pr["merged_at"] + and pr["merge_commit_sha"] == sha + and pr["base"]["ref"] == ref.removeprefix("refs/heads/") + and pr["base"]["repo"]["full_name"] == repository + and any(label["name"] == "release" for label in pr["labels"]) + for pr in pull_requests + ) + prerelease = "-" in version + if release and ref == "refs/heads/main" and prerelease: + raise ValueError("A release from main needs a stable version in package.json") + if release and ref == "refs/heads/develop" and not prerelease: + raise ValueError("A release from develop needs a prerelease version in package.json") + return { + "version": version, + "release": str(bool(release)).lower(), + "prerelease": str(prerelease).lower(), + } + + +def check_tag(version, sha): + ref = f"refs/tags/{version}" + result = subprocess.run(["git", "show-ref", "--verify", "--quiet", ref]) + if result.returncode == 1: + return + result.check_returncode() + target = subprocess.check_output( + ["git", "rev-parse", "--verify", f"{ref}^{{commit}}"], text=True + ).strip() + if target != sha: + raise ValueError( + f"Tag {version} already points to {target}, not {sha}; choose a new version" + ) + + +def main(): + parser = argparse.ArgumentParser(description=__doc__) + parser.add_argument("--check-tag", action="store_true") + args = parser.parse_args() + root = Path(__file__).resolve().parents[2] + version = read_version(root) + if args.check_tag: + check_tag(version, os.environ["GITHUB_SHA"]) + return + + event = os.environ["GITHUB_EVENT_NAME"] + ref = os.environ["GITHUB_REF"] + sha = os.environ["GITHUB_SHA"] + repository = os.environ["GITHUB_REPOSITORY"] + pull_requests = [] + if event == "push" and ref in RELEASE_BRANCHES: + pages = json.loads(subprocess.check_output( + ["gh", "api", "--paginate", "--slurp", + f"repos/{repository}/commits/{sha}/pulls?per_page=100"], + text=True, + )) + pull_requests = [pr for page in pages for pr in page] + metadata = release_metadata(version, event, ref, sha, repository, pull_requests) + with open(os.environ["GITHUB_OUTPUT"], "a") as output: + for name, value in metadata.items(): + print(f"{name}={value}", file=output) + + +if __name__ == "__main__": + main() diff --git a/.github/scripts/test_release.py b/.github/scripts/test_release.py new file mode 100644 index 00000000..1d190bcc --- /dev/null +++ b/.github/scripts/test_release.py @@ -0,0 +1,204 @@ +import copy +import json +import os +from pathlib import Path +import subprocess +import tempfile +import unittest +from unittest.mock import patch + +import release + + +SHA = "a" * 40 +REPOSITORY = "csound/plugins" +MERGED_PR = { + "merged_at": "2026-10-29T10:35:04Z", + "merge_commit_sha": SHA, + "base": {"ref": "main", "repo": {"full_name": REPOSITORY}}, + "labels": [{"name": "release"}], +} + + +class VersionTests(unittest.TestCase): + def setUp(self): + directory = tempfile.TemporaryDirectory() + self.addCleanup(directory.cleanup) + self.root = Path(directory.name) + + def write_manifests(self, version, lock_version=None, root_version=None): + (self.root / "package.json").write_text(json.dumps({"version": version})) + (self.root / "package-lock.json").write_text(json.dumps({ + "version": lock_version or version, + "packages": {"": {"version": root_version or version}}, + })) + + def test_stable_and_prerelease_versions(self): + for version in ["7.0.0", "7.1.0-beta.2", "7.0.0-rc1", "7.0.0-alpha-beta.0"]: + with self.subTest(version=version): + self.write_manifests(version) + self.assertEqual(release.read_version(self.root), version) + + def test_invalid_versions(self): + for version in [None, 7, "", "v7.0.0", "7.0", "07.0.0", "7.0.0-beta.01", + "7.0.0-", "7.0.0-beta..1", "7.0.0+build", "7.0.0\nrelease=true"]: + with self.subTest(version=version): + self.write_manifests(version) + with self.assertRaisesRegex(ValueError, "SemVer"): + release.read_version(self.root) + + def test_both_lockfile_versions_must_match(self): + for mismatch in [{"lock_version": "7.0.1"}, {"root_version": "7.0.1"}]: + with self.subTest(mismatch=mismatch): + self.write_manifests("7.0.0", **mismatch) + with self.assertRaisesRegex(ValueError, "must match"): + release.read_version(self.root) + + +class ReleaseTests(unittest.TestCase): + def metadata(self, version="7.0.0", event="push", ref="refs/heads/main", prs=None): + return release.release_metadata( + version, event, ref, SHA, REPOSITORY, [MERGED_PR] if prs is None else prs + ) + + def test_labelled_main_merge_releases(self): + self.assertEqual(self.metadata(), { + "version": "7.0.0", "release": "true", "prerelease": "false", + }) + + def test_labelled_develop_merge_prereleases(self): + pr = copy.deepcopy(MERGED_PR) + pr["base"]["ref"] = "develop" + self.assertEqual(self.metadata("7.1.0-beta.2", ref="refs/heads/develop", prs=[pr]), { + "version": "7.1.0-beta.2", "release": "true", "prerelease": "true", + }) + + def test_only_push_events_to_release_branches_can_release(self): + for event, ref in [ + ("pull_request", "refs/heads/main"), + ("pull_request_target", "refs/heads/main"), + ("workflow_dispatch", "refs/heads/main"), + ("push", "refs/heads/topic"), + ("push", "refs/tags/7.0.0"), + ]: + with self.subTest(event=event, ref=ref): + self.assertEqual(self.metadata(event=event, ref=ref)["release"], "false") + + def test_requires_exact_merge_base_repository_and_label(self): + for field, value in [ + ("merged_at", None), + ("merge_commit_sha", "b" * 40), + ("base", {"ref": "develop", "repo": {"full_name": REPOSITORY}}), + ("base", {"ref": "main", "repo": {"full_name": "fork/plugins"}}), + ("labels", []), + ("labels", [{"name": "no-release"}]), + ("labels", [{"name": "release-candidate"}]), + ]: + with self.subTest(field=field, value=value): + pr = copy.deepcopy(MERGED_PR) + pr[field] = value + self.assertEqual(self.metadata(prs=[pr])["release"], "false") + self.assertEqual(self.metadata(prs=[])["release"], "false") + + def test_release_version_must_match_branch(self): + with self.assertRaisesRegex(ValueError, "main needs a stable version"): + self.metadata(version="7.0.0-beta.2") + pr = copy.deepcopy(MERGED_PR) + pr["base"]["ref"] = "develop" + with self.assertRaisesRegex(ValueError, "develop needs a prerelease version"): + self.metadata(ref="refs/heads/develop", prs=[pr]) + + def test_ordinary_builds_allow_both_version_types(self): + for ref in ["refs/heads/main", "refs/heads/develop"]: + for version in ["7.0.0", "7.0.0-beta.2"]: + with self.subTest(ref=ref, version=version): + self.assertEqual(self.metadata(version, ref=ref, prs=[])["release"], "false") + + +class CommandTests(unittest.TestCase): + def setUp(self): + directory = tempfile.TemporaryDirectory() + self.addCleanup(directory.cleanup) + self.output = Path(directory.name) / "output" + self.output.write_text("previous=value\n") + self.environment = { + "GITHUB_EVENT_NAME": "push", + "GITHUB_REF": "refs/heads/main", + "GITHUB_SHA": SHA, + "GITHUB_REPOSITORY": REPOSITORY, + "GITHUB_OUTPUT": str(self.output), + } + self.addCleanup(patch.stopall) + patch.dict(os.environ, self.environment).start() + patch("sys.argv", ["release.py"]).start() + patch.object(release, "read_version", return_value="7.0.0").start() + + def test_paginated_pr_lookup_and_output(self): + unrelated = copy.deepcopy(MERGED_PR) + unrelated["merge_commit_sha"] = "b" * 40 + pages = json.dumps([[unrelated] * 100, [MERGED_PR]]) + with patch.object(release.subprocess, "check_output", return_value=pages) as api: + release.main() + api.assert_called_once_with([ + "gh", "api", "--paginate", "--slurp", + f"repos/{REPOSITORY}/commits/{SHA}/pulls?per_page=100", + ], text=True) + self.assertEqual(self.output.read_text(), + "previous=value\nversion=7.0.0\nrelease=true\nprerelease=false\n") + + def test_pr_and_manual_runs_do_not_query_api(self): + for event in ["pull_request", "workflow_dispatch"]: + with self.subTest(event=event), patch.dict(os.environ, {"GITHUB_EVENT_NAME": event}): + with patch.object(release.subprocess, "check_output") as api: + release.main() + api.assert_not_called() + self.assertNotIn("release=true", self.output.read_text()) + + def test_api_failure_does_not_write_release_outputs(self): + with patch.object(release.subprocess, "check_output", + side_effect=subprocess.CalledProcessError(1, "gh")): + with self.assertRaises(subprocess.CalledProcessError): + release.main() + self.assertEqual(self.output.read_text(), "previous=value\n") + + def test_tag_check_does_not_query_api_or_write_outputs(self): + with patch("sys.argv", ["release.py", "--check-tag"]): + with patch.object(release, "check_tag") as check: + release.main() + check.assert_called_once_with("7.0.0", SHA) + self.assertEqual(self.output.read_text(), "previous=value\n") + + +class TagTests(unittest.TestCase): + def test_missing_tag_is_allowed(self): + with patch.object(release.subprocess, "run", + return_value=subprocess.CompletedProcess("git", 1)): + with patch.object(release.subprocess, "check_output") as resolve: + release.check_tag("7.0.0", SHA) + resolve.assert_not_called() + + def test_existing_tag_must_resolve_to_release_commit(self): + for target in [SHA, "b" * 40]: + with self.subTest(target=target): + with patch.object(release.subprocess, "run", + return_value=subprocess.CompletedProcess("git", 0)): + with patch.object(release.subprocess, "check_output", + return_value=f"{target}\n") as resolve: + if target == SHA: + release.check_tag("7.0.0", SHA) + else: + with self.assertRaisesRegex(ValueError, "choose a new version"): + release.check_tag("7.0.0", SHA) + resolve.assert_called_once_with( + ["git", "rev-parse", "--verify", "refs/tags/7.0.0^{commit}"], text=True + ) + + def test_git_error_does_not_count_as_missing_tag(self): + with patch.object(release.subprocess, "run", + return_value=subprocess.CompletedProcess("git", 128)): + with self.assertRaises(subprocess.CalledProcessError): + release.check_tag("7.0.0", SHA) + + +if __name__ == "__main__": + unittest.main() diff --git a/.github/workflows/plugins_build.yml b/.github/workflows/plugins_build.yml index eb7fe35c..881e27ef 100644 --- a/.github/workflows/plugins_build.yml +++ b/.github/workflows/plugins_build.yml @@ -1,98 +1,578 @@ -name: Csound plugins build +name: Csound 7 plugins build -env: - VCPKG_BINARY_SOURCES: "clear;nuget,GitHub,readwrite" +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.event.pull_request.number || github.sha }} + cancel-in-progress: ${{ github.event_name == 'pull_request' }} on: push: branches: - - master + - develop + - main pull_request: branches: - - master + - develop + - main + workflow_dispatch: + +env: + CSOUND_VERSION: 7.0.0 + FAUST_VERSION: 2.88.0 + VCPKG_BINARY_SOURCES: "clear;files,${{ github.workspace }}/vcpkg-cache,readwrite" jobs: - build_package: - runs-on: ${{ matrix.os }} - - env: - CSOUND_VERSION: 6.17.2 - - strategy: - matrix: - os: [windows-latest] - version: ["6.17.0"] - include: - - os: "windows-latest" - mono: "" - # - os: "ubuntu-latest" - # mono: "mono" - # - os: "macos-latest" - # mono: "mono" + update_version: + name: Read release version + runs-on: ubuntu-latest + permissions: + contents: read + pull-requests: read + outputs: + version: ${{ steps.release.outputs.version }} + release: ${{ steps.release.outputs.release }} + prerelease: ${{ steps.release.outputs.prerelease }} + steps: + - name: Checkout source + uses: actions/checkout@v7.0.1 + with: + persist-credentials: false + + - name: Check release rules + run: python3 -B -m unittest discover -s .github/scripts -p 'test_*.py' + - name: Read version and check the merged PR + id: release + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: python3 .github/scripts/release.py + + linux: + name: Linux (ubuntu-latest) + needs: update_version + runs-on: ubuntu-latest steps: - - name: Checkout Source Code - uses: actions/checkout@v1 + - name: Checkout source + uses: actions/checkout@v7.0.1 + + - name: Install Csound build dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + build-essential cmake pkg-config \ + libsndfile1-dev libsamplerate0-dev libasound2-dev libportaudio2 libportmidi-dev \ + libeigen3-dev libfluidsynth-dev libpng-dev \ + libwebsockets-dev libuv1-dev libhdf5-dev \ + ocl-icd-opencl-dev libtinfo-dev \ + libgtest-dev \ + flex bison + + - name: Cache Csound install + id: cache-csound + uses: actions/cache@v6.1.0 + with: + path: ~/csound-install + key: csound-${{ runner.os }}-${{ runner.arch }}-${{ env.CSOUND_VERSION }} + + - name: Build and install Csound + if: steps.cache-csound.outputs.cache-hit != 'true' + run: | + git clone --depth 1 --branch "$CSOUND_VERSION" \ + https://github.com/csound/csound.git "$RUNNER_TEMP/csound" + cmake -S "$RUNNER_TEMP/csound" -B "$RUNNER_TEMP/csound-build" \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="$HOME/csound-install" \ + -DBUILD_TESTS=OFF \ + -DINSTALL_PYTHON_INTERFACE=OFF + cmake --build "$RUNNER_TEMP/csound-build" --parallel "$(nproc)" + cmake --install "$RUNNER_TEMP/csound-build" + + - name: Cache Faust + id: cache-faust + uses: actions/cache@v4 with: - fetch-depth: 1 - submodules: true + path: ~/faust + key: faust-${{ runner.os }}-${{ runner.arch }}-${{ env.FAUST_VERSION }} + + - name: Install Faust + if: steps.cache-faust.outputs.cache-hit != 'true' + run: | + mkdir -p "$HOME/faust" + curl -sL -o "$RUNNER_TEMP/libfaust.zip" \ + "https://github.com/grame-cncm/faust/releases/download/$FAUST_VERSION/libfaust-ubuntu-x86_64.zip" + curl -sL -o "$RUNNER_TEMP/faust-src.tar.gz" \ + "https://github.com/grame-cncm/faust/releases/download/$FAUST_VERSION/faust-$FAUST_VERSION.tar.gz" + unzip -q "$RUNNER_TEMP/libfaust.zip" -d "$HOME/faust" + tar xzf "$RUNNER_TEMP/faust-src.tar.gz" -C "$RUNNER_TEMP" \ + "faust-$FAUST_VERSION/architecture" "faust-$FAUST_VERSION/libraries" + mv "$RUNNER_TEMP/faust-$FAUST_VERSION/architecture" "$HOME/faust/include" + mv "$RUNNER_TEMP/faust-$FAUST_VERSION/libraries" "$HOME/faust/stdlib" + rm -f "$RUNNER_TEMP/libfaust.zip" "$RUNNER_TEMP/faust-src.tar.gz" + + - name: Configure plugin build + run: | + { + echo "CSOUND_INCLUDE_DIR_HINT=$HOME/csound-install/include/csound" + echo "CSOUND_LIBRARY_DIR_HINT=$HOME/csound-install/lib" + echo "FAUST_INCLUDE_DIR_HINT=$HOME/faust/include" + echo "FAUST_LIB_DIR_HINT=$HOME/faust/lib" + echo "FAUST_STDLIB_DIR_HINT=$HOME/faust/stdlib" + } >> "$GITHUB_ENV" + + - name: Build plugins + run: | + cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \ + -DFAUST_STATIC=ON \ + -DCSOUND_INCLUDE_DIR_HINT="$CSOUND_INCLUDE_DIR_HINT" \ + -DCSOUND_LIBRARY_DIR_HINT="$CSOUND_LIBRARY_DIR_HINT" \ + -DFAUST_INCLUDE_DIR_HINT="$FAUST_INCLUDE_DIR_HINT" \ + -DFAUST_LIB_DIR_HINT="$FAUST_LIB_DIR_HINT" \ + -DFAUST_STDLIB_DIR_HINT="$FAUST_STDLIB_DIR_HINT" + cmake --build build --parallel "$(nproc)" - - name: Setup platform (windows) - if: startsWith(matrix.os, 'windows') + - name: Run plugin tests + run: ctest --test-dir build --output-on-failure + + - name: Stage plugins + run: | + mkdir -p package + cp build/src/chua/*.so \ + build/src/linear_algebra/*.so \ + build/src/fluidOpcodes/*.so \ + build/src/dssi4cs/*.so \ + build/src/image/*.so \ + build/src/websockets/*.so \ + build/src/hdf5/*.so \ + build/src/py/*.so \ + build/src/OpenCL/*.so \ + build/src/stk/*.so package/ + cp build/src/faustcsound/*.so package/ + mkdir -p package/stk/rawwaves + cp src/stk/rawwaves/*.raw package/stk/rawwaves/ + mkdir -p package/faust + cp -R "$HOME/faust/stdlib/." package/faust/ + + - name: Upload plugins + uses: actions/upload-artifact@v7.0.2 + with: + name: csound7-plugins-linux + path: package/ + if-no-files-found: error + + macos: + name: macOS (macos-latest) + needs: update_version + runs-on: macos-latest + steps: + - name: Checkout source + uses: actions/checkout@v7.0.1 + + - name: Install Csound build dependencies env: - FAUST_URL: https://github.com/grame-cncm/faust/releases/download/2.60.3/Faust-2.60.3-win64.exe - run: | - Invoke-WebRequest $env:FAUST_URL -OutFile faustInstaller.exe - ./faustInstaller.exe /S - echo "C:/Program Files/csound/bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - echo "OPCODE6DIR64=C:/Program Files/csound/plugins64" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - echo "PYTHONPATH=C:/Program Files/csound/bin" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - echo "RAWWAVE_PATH=C:/Program Files/csound/samples" | Out-File -FilePath $env:GITHUB_ENV -Encoding utf8 -Append - .\6.x\vcpkg\bootstrap-vcpkg.bat - choco install csound --version=6.17.0 - echo $GITHUB_ENV - ls 'C:\Program Files\Faust\' - echo "C:\Program Files\Faust\;C:\Program Files\Faust\lib" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append - - # - name: Setup platform (linux) - # if: startsWith(matrix.os, 'ubuntu') - # run: | - # ./vcpkg/bootstrap-vcpkg.sh - # sudo apt-get update - # sudo apt-get install libcsound64-dev - - # - name: Setup platform (macos) - # if: startsWith(matrix.os, 'macos') - # run: | - # ./vcpkg/bootstrap-vcpkg.sh - # brew install csound - - - name: Setup VCPKG cache - shell: bash - run: | - ${{ matrix.mono }} `./6.x/vcpkg/vcpkg fetch nuget | tail -n 1` \ - sources add \ - -source "https://nuget.pkg.github.com/csound/index.json" \ - -storepasswordincleartext \ - -name "GitHub" \ - -username "csound" \ - -password "${{ secrets.GITHUB_TOKEN }}" - ${{ matrix.mono }} `./6.x/vcpkg/vcpkg fetch nuget | tail -n 1` \ - setapikey "${{ secrets.GITHUB_TOKEN }}" \ - -source "https://nuget.pkg.github.com/csound/index.json" - - - name: Generate CMake build - run: cmake 6.x/. -B build -DUSE_VCPKG=1 -DBUILD_ABLETON_LINK_OPCODES=OFF + HOMEBREW_NO_AUTO_UPDATE: 1 + run: | + brew install cmake ninja pkg-config bison flex libsndfile + echo "$(brew --prefix bison)/bin" >> "$GITHUB_PATH" + echo "$(brew --prefix flex)/bin" >> "$GITHUB_PATH" + + - name: Checkout vcpkg submodule + run: git submodule update --init vcpkg + + - name: Set up vcpkg + run: vcpkg/bootstrap-vcpkg.sh -disableMetrics + + - name: Cache vcpkg binary cache + uses: actions/cache@v6.1.0 + with: + path: vcpkg-cache + key: vcpkg-macos-${{ github.run_id }} + restore-keys: vcpkg-macos- + + - name: Cache Csound install + id: cache-csound + uses: actions/cache@v6.1.0 + with: + path: ~/csound-install + key: csound-${{ runner.os }}-${{ runner.arch }}-${{ env.CSOUND_VERSION }} + + - name: Build and install Csound + if: steps.cache-csound.outputs.cache-hit != 'true' + run: | + git clone --depth 1 --branch "$CSOUND_VERSION" \ + https://github.com/csound/csound.git "$RUNNER_TEMP/csound" + cmake -S "$RUNNER_TEMP/csound" -B "$RUNNER_TEMP/csound-build" -G Ninja \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX="$HOME/csound-install" \ + -DCS_FRAMEWORK_DEST="$HOME/csound-install/Frameworks" \ + -DCMAKE_PREFIX_PATH="$(brew --prefix)" \ + -DBUILD_TESTS=OFF \ + -DBUILD_STATIC_LIBRARY=OFF \ + -DBUILD_VIRTUAL_KEYBOARD=OFF \ + -DBUILD_JAVA_INTERFACE=OFF \ + -DBUILD_PYTHON_INTERFACE=OFF \ + -DINSTALL_PYTHON_INTERFACE=OFF \ + -DBUILD_LUA_INTERFACE=OFF \ + -DBUILD_CSBEATS=OFF \ + -DUSE_LIBSAMPLERATE=OFF \ + -DUSE_CURL=OFF \ + -DUSE_GETTEXT=OFF \ + -DUSE_JACK=OFF \ + -DUSE_PORTAUDIO=OFF \ + -DUSE_PORTMIDI=OFF \ + -DUSE_MP3=OFF + cmake --build "$RUNNER_TEMP/csound-build" --parallel "$(sysctl -n hw.ncpu)" + cmake --install "$RUNNER_TEMP/csound-build" + + - name: Cache Faust + id: cache-faust + uses: actions/cache@v4 + with: + path: ~/faust + key: faust-${{ runner.os }}-${{ runner.arch }}-${{ env.FAUST_VERSION }} + + - name: Install Faust + if: steps.cache-faust.outputs.cache-hit != 'true' + run: | + if [ "$(uname -m)" = "arm64" ]; then + asset="Faust-$FAUST_VERSION-arm64.dmg" + else + asset="Faust-$FAUST_VERSION-x64.dmg" + fi + curl -sL -o "$RUNNER_TEMP/faust.dmg" \ + "https://github.com/grame-cncm/faust/releases/download/$FAUST_VERSION/$asset" + mkdir -p "$HOME/faust" "$RUNNER_TEMP/faust_dmg" + hdiutil attach "$RUNNER_TEMP/faust.dmg" \ + -mountpoint "$RUNNER_TEMP/faust_dmg" -nobrowse -quiet + root=$(dirname "$(find "$RUNNER_TEMP/faust_dmg" -maxdepth 2 -type d -name include | head -1)") + cp -R "$root/include" "$HOME/faust/include" + cp -R "$root/lib" "$HOME/faust/lib" + cp -R "$root/share/faust" "$HOME/faust/stdlib" + hdiutil detach "$RUNNER_TEMP/faust_dmg" -quiet + rm -f "$RUNNER_TEMP/faust.dmg" + + - name: Configure plugin build + run: | + framework="$HOME/csound-install/Frameworks/CsoundLib64.framework" + headers="$framework/Versions/7.0/Headers" + if [ ! -f "$headers/csound.h" ]; then + headers="$framework/Headers" + fi + { + echo "CSOUND_INCLUDE_DIR_HINT=$headers" + echo "CSOUND_FRAMEWORK_DIR_HINT=$HOME/csound-install/Frameworks" + echo "CSOUND_LIBRARY_DIR_HINT=$HOME/csound-install/Frameworks" + echo "FAUST_INCLUDE_DIR_HINT=$HOME/faust/include" + echo "FAUST_LIB_DIR_HINT=$HOME/faust/lib" + echo "FAUST_STDLIB_DIR_HINT=$HOME/faust/stdlib" + } >> "$GITHUB_ENV" - name: Build plugins - run: cmake --build build --config Release + run: | + cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \ + -DUSE_VCPKG=ON \ + -DFAUST_STATIC=ON \ + -DCSOUND_INCLUDE_DIR_HINT="$CSOUND_INCLUDE_DIR_HINT" \ + -DCSOUND_FRAMEWORK_DIR_HINT="$CSOUND_FRAMEWORK_DIR_HINT" \ + -DCSOUND_LIBRARY_DIR_HINT="$CSOUND_LIBRARY_DIR_HINT" \ + -DFAUST_INCLUDE_DIR_HINT="$FAUST_INCLUDE_DIR_HINT" \ + -DFAUST_LIB_DIR_HINT="$FAUST_LIB_DIR_HINT" \ + -DFAUST_STDLIB_DIR_HINT="$FAUST_STDLIB_DIR_HINT" + cmake --build build --parallel "$(sysctl -n hw.ncpu)" - - name: Build installer - run: iscc /o. .\installer\win64\Installer.iss + - name: Stage plugins + run: | + mkdir -p package + cp build/src/chua/*.dylib \ + build/src/linear_algebra/*.dylib \ + build/src/fluidOpcodes/*.dylib \ + build/src/image/*.dylib \ + build/src/websockets/*.dylib \ + build/src/hdf5/*.dylib \ + build/src/py/*.dylib \ + build/src/OpenCL/*.dylib \ + build/src/stk/*.dylib package/ + cp build/src/faustcsound/*.dylib package/ + mkdir -p package/stk/rawwaves + cp src/stk/rawwaves/*.raw package/stk/rawwaves/ + mkdir -p package/faust + cp -R "$HOME/faust/stdlib/." package/faust/ - - name: Upload installer - uses: actions/upload-artifact@v4 + - name: Upload plugins + uses: actions/upload-artifact@v7.0.2 with: - name: Csound_win_x64-${{env.CSOUND_VERSION}}.${{github.run_number}}-plugins-installer - path: ./csound6-plugins-win_x86_64-*.exe + name: csound7-plugins-macos + path: package/ if-no-files-found: error + + windows: + name: Windows (windows-latest) + needs: update_version + runs-on: windows-latest + defaults: + run: + shell: pwsh + steps: + - name: Checkout source + uses: actions/checkout@v7.0.1 + + - name: Install Csound build dependencies + run: choco install -y winflexbison3 + + - name: Clone Csound (with vcpkg) + run: | + git clone --recursive --depth 1 --branch "$env:CSOUND_VERSION" ` + https://github.com/csound/csound.git csound + + - name: Cache Csound install + id: cache-csound + uses: actions/cache@v6.1.0 + with: + path: ${{ runner.temp }}\csound-install + key: csound-${{ runner.os }}-${{ runner.arch }}-${{ env.CSOUND_VERSION }} + + - name: Bootstrap vcpkg (Csound) + if: steps.cache-csound.outputs.cache-hit != 'true' + run: .\csound\vcpkg\bootstrap-vcpkg.bat -disableMetrics + + - name: Checkout vcpkg submodule + run: git submodule update --init vcpkg + + - name: Bootstrap vcpkg (plugins) + run: .\vcpkg\bootstrap-vcpkg.bat -disableMetrics + + - name: Cache vcpkg binary cache + uses: actions/cache@v6.1.0 + with: + path: ${{ github.workspace }}\vcpkg-cache + key: vcpkg-windows-${{ github.run_id }} + restore-keys: vcpkg-windows- + + - name: Build and install Csound + if: steps.cache-csound.outputs.cache-hit != 'true' + run: | + cmake -S csound -B csound-build -A x64 ` + -DUSE_VCPKG=ON ` + -DCMAKE_INSTALL_PREFIX="$env:RUNNER_TEMP\csound-install" ` + -DBUILD_TESTS=OFF ` + -DBUILD_STATIC_LIBRARY=OFF ` + -DBUILD_VIRTUAL_KEYBOARD=OFF ` + -DBUILD_JAVA_INTERFACE=OFF ` + -DBUILD_PYTHON_INTERFACE=OFF ` + -DINSTALL_PYTHON_INTERFACE=OFF ` + -DBUILD_LUA_INTERFACE=OFF ` + -DBUILD_CSBEATS=OFF + cmake --build csound-build --config Release --parallel + cmake --install csound-build --config Release + + - name: Cache Faust + id: cache-faust + uses: actions/cache@v4 + with: + path: ~/faust + key: faust-${{ runner.os }}-${{ runner.arch }}-${{ env.FAUST_VERSION }} + + - name: Install Faust + if: steps.cache-faust.outputs.cache-hit != 'true' + run: | + $faustDir = Join-Path $env:USERPROFILE 'faust' + New-Item -ItemType Directory -Force -Path $faustDir | Out-Null + curl.exe -sL -o "$env:RUNNER_TEMP\faust.exe" ` + "https://github.com/grame-cncm/faust/releases/download/$env:FAUST_VERSION/Faust-$env:FAUST_VERSION-win64.exe" + & 'C:\Program Files\7-Zip\7z.exe' x "$env:RUNNER_TEMP\faust.exe" "-o$faustDir" -y | Out-Null + Remove-Item "$env:RUNNER_TEMP\faust.exe" + + - name: Configure plugin build + run: | + $install = Join-Path $env:RUNNER_TEMP 'csound-install' + $header = Get-ChildItem -Path $install -Recurse -File -Filter 'csound.h' | + Select-Object -First 1 + $lib = Get-ChildItem -Path $install -Recurse -File -Filter 'csound64.lib' | + Select-Object -First 1 + if (-not $header) { throw "csound.h not found under $install" } + if (-not $lib) { throw "csound64.lib not found under $install" } + "CSOUND_INCLUDE_DIR_HINT=$($header.DirectoryName)" >> $env:GITHUB_ENV + "CSOUND_LIBRARY_DIR_HINT=$($lib.DirectoryName)" >> $env:GITHUB_ENV + $faustDir = Join-Path $env:USERPROFILE 'faust' + "FAUST_INCLUDE_DIR_HINT=$faustDir\include" >> $env:GITHUB_ENV + "FAUST_LIB_DIR_HINT=$faustDir\lib" >> $env:GITHUB_ENV + "FAUST_STDLIB_DIR_HINT=$faustDir\share\faust" >> $env:GITHUB_ENV + + - name: Build plugins + run: | + cmake -S . -B build -A x64 ` + -DUSE_VCPKG=ON ` + -DFAUST_STATIC=ON ` + -DCSOUND_INCLUDE_DIR_HINT="$env:CSOUND_INCLUDE_DIR_HINT" ` + -DCSOUND_LIBRARY_DIR_HINT="$env:CSOUND_LIBRARY_DIR_HINT" ` + -DFAUST_INCLUDE_DIR_HINT="$env:FAUST_INCLUDE_DIR_HINT" ` + -DFAUST_LIB_DIR_HINT="$env:FAUST_LIB_DIR_HINT" ` + -DFAUST_STDLIB_DIR_HINT="$env:FAUST_STDLIB_DIR_HINT" + cmake --build build --config Release --parallel + + - name: Stage plugins + run: | + $package = 'package' + New-Item -ItemType Directory -Force -Path $package | Out-Null + Get-ChildItem -Path build/src -Recurse -Include *.dll | + Where-Object { $_.FullName -match 'Release' } | + Copy-Item -Destination $package + $vcpkgBin = Join-Path 'build' 'vcpkg_installed\x64-windows-csound\bin' + if (Test-Path $vcpkgBin) { + Copy-Item (Join-Path $vcpkgBin '*.dll') -Destination $package -ErrorAction SilentlyContinue + } + $rawwaves = Join-Path $package 'stk/rawwaves' + New-Item -ItemType Directory -Force -Path $rawwaves | Out-Null + Copy-Item 'src\stk\rawwaves\*.raw' -Destination $rawwaves + $faustOut = Join-Path $package 'faust' + New-Item -ItemType Directory -Force -Path $faustOut | Out-Null + Copy-Item "$env:USERPROFILE\faust\share\faust\*" -Destination $faustOut -Recurse + + - name: Upload plugins + uses: actions/upload-artifact@v7.0.2 + with: + name: csound7-plugins-windows + path: package/ + if-no-files-found: error + + linux_cuda: + name: Linux CUDA (ubuntu-latest) + needs: update_version + runs-on: ubuntu-latest + container: + image: nvidia/cuda:12.4.1-devel-ubuntu22.04 + steps: + - name: Checkout source + uses: actions/checkout@v7.0.1 + + - name: Install build dependencies + run: | + apt-get update + apt-get install -y --no-install-recommends \ + build-essential cmake git ca-certificates pkg-config \ + libsndfile1-dev libsamplerate0-dev libasound2-dev flex bison + + - name: Show CUDA toolchain + run: | + nvcc --version + nvidia-smi || true + + - name: Cache Csound install + id: cache-csound + uses: actions/cache@v6.1.0 + with: + path: /csound-install + key: csound-cuda-${{ runner.os }}-${{ runner.arch }}-${{ env.CSOUND_VERSION }} + + - name: Build and install Csound + if: steps.cache-csound.outputs.cache-hit != 'true' + run: | + git clone --depth 1 --branch "$CSOUND_VERSION" \ + https://github.com/csound/csound.git /tmp/csound + cmake -S /tmp/csound -B /tmp/csound-build \ + -DCMAKE_BUILD_TYPE=Release \ + -DCMAKE_INSTALL_PREFIX=/csound-install \ + -DBUILD_TESTS=OFF \ + -DINSTALL_PYTHON_INTERFACE=OFF + cmake --build /tmp/csound-build --parallel "$(nproc)" + cmake --install /tmp/csound-build + + - name: Build CUDA plugins + run: | + cmake -S . -B build -DCMAKE_BUILD_TYPE=Release \ + -DBUILD_CUDA_OPCODES=ON \ + -DBUILD_CHUA_OPCODES=OFF \ + -DBUILD_DSSI_OPCODES=OFF \ + -DBUILD_FLUID_OPCODES=OFF \ + -DBUILD_HDF5_OPCODES=OFF \ + -DBUILD_IMAGE_OPCODES=OFF \ + -DBUILD_LINEAR_ALGEBRA_OPCODES=OFF \ + -DBUILD_PYTHON_OPCODES=OFF \ + -DBUILD_WEBSOCKET_OPCODE=OFF \ + -DBUILD_WIIMOTE_OPCODES=OFF \ + -DBUILD_P5GLOVE_OPCODES=OFF \ + -DCMAKE_CUDA_ARCHITECTURES="50;60;70;75;80;86" \ + -DCSOUND_INCLUDE_DIR_HINT=/csound-install/include/csound \ + -DCSOUND_LIBRARY_DIR_HINT=/csound-install/lib + cmake --build build --parallel "$(nproc)" \ + --target cudaop1 cudaop2 cudaop3 cudaop4 cudaop5 + + - name: Stage plugins + run: | + mkdir -p package + cp build/src/CUDA/*.so package/ + + - name: Upload plugins + uses: actions/upload-artifact@v7.0.2 + with: + name: csound7-plugins-linux-cuda + path: package/ + if-no-files-found: error + + publish_release: + name: Publish release + if: ${{ github.event_name == 'push' && needs.update_version.outputs.release == 'true' }} + needs: [update_version, linux, linux_cuda, macos, windows] + runs-on: ubuntu-latest + concurrency: + group: plugin-release-${{ needs.update_version.outputs.version }} + cancel-in-progress: false + permissions: + contents: write + steps: + - name: Checkout release commit + uses: actions/checkout@v7.0.1 + with: + fetch-depth: 0 + persist-credentials: false + + - name: Check existing release tag + run: python3 .github/scripts/release.py --check-tag + + - name: Download Linux plugins + uses: actions/download-artifact@v8.0.2 + with: + name: csound7-plugins-linux + path: release/linux + + - name: Download Linux CUDA plugins + uses: actions/download-artifact@v8.0.2 + with: + name: csound7-plugins-linux-cuda + path: release/linux + + - name: Download macOS plugins + uses: actions/download-artifact@v8.0.2 + with: + name: csound7-plugins-macos + path: release/macos + + - name: Download Windows plugins + uses: actions/download-artifact@v8.0.2 + with: + name: csound7-plugins-windows + path: release/windows + + - name: Package release assets + run: | + version="${{ needs.update_version.outputs.version }}" + ( cd release/linux && zip -r "../../csound7-plugins-${version}-linux-x86_64.zip" . ) + ( cd release/macos && zip -r "../../csound7-plugins-${version}-macos.zip" . ) + ( cd release/windows && zip -r "../../csound7-plugins-${version}-windows.zip" . ) + ls -l csound7-plugins-*.zip + + - name: Create release + uses: softprops/action-gh-release@v3.0.3 + with: + token: ${{ secrets.GITHUB_TOKEN }} + tag_name: ${{ needs.update_version.outputs.version }} + target_commitish: ${{ github.sha }} + name: ${{ needs.update_version.outputs.version }} + generate_release_notes: true + prerelease: ${{ needs.update_version.outputs.prerelease == 'true' }} + fail_on_unmatched_files: true + files: | + csound7-plugins-${{ needs.update_version.outputs.version }}-linux-x86_64.zip + csound7-plugins-${{ needs.update_version.outputs.version }}-macos.zip + csound7-plugins-${{ needs.update_version.outputs.version }}-windows.zip diff --git a/.github/workflows/plugins_wasm.yml b/.github/workflows/plugins_wasm.yml new file mode 100644 index 00000000..c783d6d5 --- /dev/null +++ b/.github/workflows/plugins_wasm.yml @@ -0,0 +1,45 @@ +name: Csound 7 WASM plugins + +on: + pull_request: + branches: [develop] + push: + branches: [develop] + workflow_dispatch: + +permissions: + contents: read + +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + package: + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - uses: actions/checkout@v7.0.1 + with: + persist-credentials: false + - uses: actions/setup-node@v7.1.0 + with: + node-version: 22 + cache: npm + - uses: cachix/install-nix-action@v31.11.1 + - uses: cachix/cachix-action@v17 + with: + name: csound + authToken: ${{ secrets.CACHIX_AUTH_TOKEN }} + skipPush: ${{ github.event_name == 'pull_request' && github.event.pull_request.head.repo.full_name != github.repository }} + - name: Install JS dependencies + run: npm ci + - name: Build WASM plugins with CMake and Nix + run: npm run build + - name: Test and pack + run: npm pack + - uses: actions/upload-artifact@v7.0.2 + with: + name: csound-plugins-npm + path: csound-plugins-*.tgz + if-no-files-found: error diff --git a/.gitignore b/.gitignore index b80e2356..8754df97 100644 --- a/.gitignore +++ b/.gitignore @@ -1,3 +1,7 @@ build dist -csound6-plugins-win_x86_64-* +node_modules/ +result* +*.tgz +.csound/ +.wasm-dist.*/ diff --git a/.gitmodules b/.gitmodules index c309f612..a0a57f3d 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,3 @@ [submodule "vcpkg"] - path = 6.x/vcpkg + path = vcpkg url = https://github.com/microsoft/vcpkg.git diff --git a/6.x/README.md b/6.x/README.md deleted file mode 100644 index f251e7d3..00000000 --- a/6.x/README.md +++ /dev/null @@ -1,114 +0,0 @@ -Plugins for Csound 6.x -======= - -Currently, the plugins available in this tree are - -- AbletonLive Link (requires Ableton link) - **link_create link_enable link_is_enabled link_tempo_set link_tempo_get link_beat_get link_metro link_beat_request link_beat_force** - -- chua (requires Eigen library, header-only) - **chuap** - -- Faust (requires libfaust) - **faustgen faustcompile faustaudio faustdsp faustplay faustctl** - -- image (requires libpng) - **imageload imagesave imagecreate imagesize imagegetpixel imagesetpixel imagefree** - -- py (requires a Python 3.x installation with dev libs) - **Many opcodes to call Python code** - -- widgets (requires the FLTK LIB) -**FLTK-based widgets.** -NB: These opcodes do not work correctly on MacOS due -to incompatibilities with the operating system. - -- virtual keyboard (requires the FLTK LIB) -**Virtual MIDI keyboard midi backend** - -- STK opcodes (requires STK library) -**Physical model opcodes using the STK library** - - -Install location --------------- -The CMake scripts in this repository use the default CS_USER_PLUGIN -location on MacOS and Windows as defined in the Csound build, or a -library instalation directory (customisable) on LINUX. These are: - -- LINUX: depends on both `CMAKE_INSTALL_PREFIX` and `USE_LIB64`. It is then installed in: - * if `USE_LIB64=1` then to - * for doubles: `${CSOUND_INSTALL_PREFIX}/lib64/csound/plugins64-${APIVERSION}` - * for floats: `${CSOUND_INSTALL_PREFIX}/lib64/csound/plugins-${APIVERSION}` (floats)` - * if `USE_LIB64=0` then to - * for doubles: `${CSOUND_INSTALL_PREFIX}/lib/csound/plugins64-${APIVERSION}` - * for floats: `${CSOUND_INSTALL_PREFIX}/lib/csound/plugins-${APIVERSION}` - -- MACOS: - * For doubles: `$HOME/Library/csound/${APIVERSION}/plugins64` - * For floats: `$HOME/Library/csound/${APIVERSION}/plugins` -- Windows: - * For doubles: `%LOCALAPPDATA%\csound\${APIVERSION}\plugins64` - * For floats: `%LOCALAPPDATA%\csound\${APIVERSION}\plugins` - - -Build Instructions for Linux and MacOS ---- - -The build requires Csound to be installed, as well as CMake. With this -in place, you can do : - -``` -$ git clone https://github.com/csound/plugins.git -$ cd plugins -$ mkdir build -$ cd build -$ cmake ../ -$ make -``` - -By default, all the plugins are built. If one wants to exclude a -plugin from the build process, one can pass an option to the cmake command. -For example, to exclude the chua plugin, the `cmake` command would be: - -``` -$ cmake -DBUILD_CHUA_OPCODES=OFF ../ -``` - -For the FLTK dependent plugins, the configuration variable used is -`USE_FLTK`. - -To install the opcodes you have built - -``` -$ make install -``` - -Depending on your permissions, you might need to prepend `sudo` to -these commands. After the first build the plugins can be updated with - -``` -$ git pull -$ make -$ make install -``` - -using `sudo` in the last step if raised permissions are needed. On -Linux, the installation location can be set with the relevant CMake -variables as indicated above. - -Csound Location ------------- -CMake will normally find the installed Csound headers (and library) -automatically. However, if your Csound headers and library are not -placed in the usual locations, you can use the following CMake option variables -to tell CMake where they are: - -``` -CSOUND_INCLUDE_DIR_HINT -``` -and - -``` -CSOUND_LIBRARY_DIR_HINT -``` diff --git a/6.x/cmake/Modules/FindCSOUND.cmake b/6.x/cmake/Modules/FindCSOUND.cmake deleted file mode 100644 index d83c0c05..00000000 --- a/6.x/cmake/Modules/FindCSOUND.cmake +++ /dev/null @@ -1,65 +0,0 @@ -# Try to find the Csound library. -# Once done this will define: -# CSOUND_FOUND - System has the Csound library -# CSOUND_INCLUDE_DIRS - The Csound include directories. -# CSOUND_LIBRARIES - The libraries needed to use the Csound library. - -if(APPLE) - find_path(CSOUND_INCLUDE_DIR csound.h - HINTS - "$ENV{HOME}/Library/Frameworks/CsoundLib64.framework/Headers" - "$ENV{HOME}/Library/Frameworks/CsoundLib.framework/Headers" - /Library/Frameworks/CsoundLib64.framework/Headers - /Library/Frameworks/CsoundLib.framework/Headers - ${CSOUND_INCLUDE_DIR_HINT} - ) - - find_path(CSOUND_FRAMEWORK CsoundLib64 - HINTS - "$ENV{HOME}/Library/Frameworks/CsoundLib64.framework" - /Library/Frameworks/CsoundLib64.framework - ${CSOUND_FRAMEWORK_DIR_HINT} - ) - - find_path(CSOUND_FRAMEWORK32 CsoundLib - HINTS - "$ENV{HOME}/Library/Frameworks/CsoundLib64.framework" - "$ENV{HOME}/Library/Frameworks/CsoundLib.framework" - /Library/Frameworks/CsoundLib64.framework - /Library/Frameworks/CsoundLib.framework - ${CSOUND_FRAMEWORK32_DIR_HINT} - ) -else() - find_path(CSOUND_INCLUDE_DIR csound.h - PATH_SUFFIXES csound - HINTS ${CSOUND_INCLUDE_DIR_HINT} - ) -endif() - -if(APPLE) - find_library(CSOUND_LIBRARY - NAMES CsoundLib64 - HINTS - /Library/Frameworks/CsoundLib64.framework/ - "$ENV{HOME}/Library/Frameworks/CsoundLib64.framework" - ${CSOUND_LIBRARY_DIR_HINT} - ) -else() - find_library(CSOUND_LIBRARY - NAMES csound64 csound - HINTS ${CSOUND_LIBRARY_DIR_HINT} - ) -endif() - -include(FindPackageHandleStandardArgs) - -# handle the QUIETLY and REQUIRED arguments and set CSOUND_FOUND to TRUE -# if all listed variables are TRUE -find_package_handle_standard_args(CSOUND - CSOUND_LIBRARY CSOUND_INCLUDE_DIR -) - -mark_as_advanced(CSOUND_INCLUDE_DIR CSOUND_LIBRARY) - -set(CSOUND_INCLUDE_DIRS ${CSOUND_INCLUDE_DIR}) -set(CSOUND_LIBRARIES ${CSOUND_LIBRARY}) diff --git a/6.x/cmake/Modules/FindFAUST.cmake b/6.x/cmake/Modules/FindFAUST.cmake deleted file mode 100644 index d1d329e5..00000000 --- a/6.x/cmake/Modules/FindFAUST.cmake +++ /dev/null @@ -1,59 +0,0 @@ -# Find Faust2 -include(FindPackageHandleStandardArgs) - -find_path(FAUST_INCLUDE_DIR faust/dsp/llvm-dsp.h - HINTS - C:/Program Files/Faust/include/ - /opt/lib/faust/architecture/ - /usr/lib/faust/architecture/ - /usr/local/lib/faust/architecture/ - "${FAUST_INCLUDE_DIR_HINT}" -) - -find_library(FAUST_LIBRARY - NAMES - libfaust.so - libfaust.dylib - faust.dll - faust - libfaust - HINTS - "${FAUST_LIB_DIR_HINT}" -) - -find_package_handle_standard_args(FAUST FAUST_INCLUDE_DIR FAUST_LIBRARY) - -if(FAUST_FOUND) - set(FAUST_LIBRARIES ${FAUST_LIBRARY}) - set(FAUST_INCLUDE_DIRS ${FAUST_INCLUDE_DIR}) - - if("${FAUST_LIBRARY}" MATCHES ".*\\.a") - # This is a static build of faust, hence - # we have to add all the LLVM flags... - - find_program(LLVM_CONFIG llvm-config - HINTS - /usr/bin - /usr/local/bin - /usr/local/opt/llvm/bin - ) - - if(NOT LLVM_CONFIG) - message(WARNING "Using a static Faust library requires LLVM tooling to be present in the path.") - UNSET(FAUST_FOUND) - else() - exec_program(${LLVM_CONFIG} ARGS --includedir OUTPUT_VARIABLE LLVM_DIR) - exec_program(${LLVM_CONFIG} ARGS --libs OUTPUT_VARIABLE LLVM_LIBS) - exec_program(${LLVM_CONFIG} ARGS --version OUTPUT_VARIABLE LLVM_VERSION) - exec_program(${LLVM_CONFIG} ARGS --ldflags OUTPUT_VARIABLE LLVM_LDFLAGS) - - set(LLVM_VERSION LLVM_${LLVM_VERSION_MAJOR}${LLVM_VERSION_MINOR}) - - find_package(OpenSSL REQUIRED) - set(FAUST_LIBRARIES ${FAUST_LIBRARIES} dl ${OPENSSL_LIBRARIES} ncurses z ${LLVM_LDFLAGS} ${LLVM_LIBS} ) - endif() - endif() -else() - set(FAUST_LIBRARIES) - set(FAUST_INCLUDE_DIRS) -endif() diff --git a/6.x/installer/macosx/release-build.sh b/6.x/installer/macosx/release-build.sh deleted file mode 100644 index a7af92de..00000000 --- a/6.x/installer/macosx/release-build.sh +++ /dev/null @@ -1,73 +0,0 @@ -#!/bin/sh -# set -x -# directories -export OPVERSION=$2 -export BRANCH_NAME=$1 -echo "Using branch: $BRANCH_NAME" -export RELEASE_DIR=`date +%Y-%m-%d-%H%M%S` -export BASEDIR=`pwd`/$RELEASE_DIR -export INSTALLDIR=Package_Contents -export DMGNAME="Csound6-Plugins-MacOS_x86_64-${OPVERSION}.dmg" - -# build -mkdir $RELEASE_DIR -cd $RELEASE_DIR -export BUILD_DIR=build -mkdir $BUILD_DIR -git clone -b $BRANCH_NAME https://github.com/csound/plugins -cd $BUILD_DIR -cmake ../plugins -DCMAKE_BUILD_TYPE=Release -make - -# zip install - -# image opcodes (LIB_PNG needs to be set correctly) -echo "--- making image zip ---" -cd .. -export IMAGE_DIR=$BUILD_DIR/image -export LIB_PNG=/usr/local/lib/libpng16.16.dylib -cp $IMAGE_DIR/libimage.dylib . -cp $LIB_PNG . -install_name_tool -id libpng16.16.dylib libpng16.16.dylib -install_name_tool -change /usr/local/lib/libpng16.16.dylib libpng16.16.dylib libimage.dylib -mkdir image -mv *.dylib image -zip image.zip image/*.dylib - -# chua -echo "--- making chua zip ---" -export CHUA_DIR=$BUILD_DIR/chua -mkdir chua -cp $CHUA_DIR/libchua.dylib chua -zip chua.zip chua/*.dylib - -# faustcsound -echo "--- making faustcsound zip ---" -export FCS_DIR=$BUILD_DIR/faustcsound -mkdir faustcsound -cp $FCS_DIR/libfaustcsound.dylib faustcsound -zip faustcsound.zip faustcsound/*.dylib - -# python opcodes (python PYVERSION used needs to be set correctly) -echo "--- making py zip ---" -export PYVERSION=3.9 -export PY_ZIP_DIR="python${PYVERSION}-opcodes" -export PY_DIR=$BUILD_DIR/py -mkdir $PY_ZIP_DIR -cp $PY_DIR/libpy.dylib $PY_ZIP_DIR/libpy${PYVERSION}.dylib -zip $PY_ZIP_DIR.zip $PY_ZIP_DIR/*.dylib - -# installer (ALL) -cd $BASEDIR -mkdir -p $INSTALLDIR/tmp/csound - -cp chua/*.dylib $INSTALLDIR/tmp/csound/. -cp faustcsound/*.dylib $INSTALLDIR/tmp/csound/. -cp $PY_ZIP_DIR/*.dylib $INSTALLDIR/tmp/csound/. -cp image/*.dylib $INSTALLDIR/tmp/csound/. - -mkdir dmgsrc -pkgbuild --identifier com.csound.csound6Environment.csoundOps64 --root $INSTALLDIR --version 1 --scripts ../scripts dmgsrc/CsoundPlugins64.pkg -hdiutil create $DMGNAME -srcfolder dmgsrc -fs HFS+ - -open $BASEDIR diff --git a/6.x/installer/macosx/scripts/postinstall b/6.x/installer/macosx/scripts/postinstall deleted file mode 100755 index 3c7469f8..00000000 --- a/6.x/installer/macosx/scripts/postinstall +++ /dev/null @@ -1,10 +0,0 @@ -#!/bin/sh -set -x -if [ -d "$HOME/Library/csound/6.0/plugins64/" ]; then - mv /tmp/csound/*.dylib $HOME/Library/csound/6.0/plugins64/. -else - mkdir -p $HOME/Library/csound/6.0/plugins64 - mv /tmp/csound/*.dylib $HOME/Library/csound/6.0/plugins64/. -fi -chown -R $USER $HOME/Library/csound - diff --git a/6.x/installer/win64/Installer.iss b/6.x/installer/win64/Installer.iss deleted file mode 100644 index 12763dc1..00000000 --- a/6.x/installer/win64/Installer.iss +++ /dev/null @@ -1,104 +0,0 @@ -; Cabbage Installer Script, Copyright Rory Walsh 2017. - -;swap this with plugin name -#define MyAppName "Csound Plugins" -#define Manufacturer "Csound" -#define OPVERSION "1.0" -#define AppMinVersion GetEnv("CSOUND_VERSION") -#define BuildNumber GetEnv("GITHUB_RUN_NUMBER") - -[Setup] -; NOTE: The value of AppId uniquely identifies this application. -; Do not use the same AppId value in installers for other applications. -; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) -AppId={{0E50FA48-4C41-4D27-A742-C77D1A3A3905} -AppName={#MyAppName} -AppVersion={#OPVERSION} -DefaultDirName={localappdata}\csound\6.0\ -DisableDirPage=no -DisableProgramGroupPage=yes -UsePreviousAppDir=no - -;if you want to add a license.. -;LicenseFile= "../LICENSE" - -;swap this with plugin name -OutputBaseFilename=csound6-plugins-win_x86_64-{#AppMinVersion}-{#BuildNumber} - -;to add install icon -;SetupIconFile=icon.ico -Compression=lzma -SolidCompression=yes -ChangesEnvironment=yes - - -[Components] -Name: "core"; Description: "Install all plugins"; Types: full custom; Flags: fixed - -[Languages] -Name: "english"; MessagesFile: "compiler:Default.isl" -Name: "french"; MessagesFile: "compiler:Languages\French.isl" -Name: "german"; MessagesFile: "compiler:Languages\German.isl" -Name: "norwegian"; MessagesFile: "compiler:Languages\Norwegian.isl" - -; add files - dlls go standard location, or one of the users choosing, csd and other reousrce go to program data -[Files] -Source: "..\..\build\src\AbletonLinkOpcodes\Release\ableton_link_opcodes.dll"; DestDir: "{app}\plugins64\"; Flags: ignoreversion; Components: core; Permissions: users-full; -Source: "..\..\build\src\chua\Release\chua.dll"; DestDir: "{app}\plugins64\"; Flags: ignoreversion; Components: core; Permissions: users-full; -Source: "..\..\build\src\image\Release\image.dll"; DestDir: "{app}\plugins64\"; Flags: ignoreversion; Components: core; Permissions: users-full; -Source: "..\..\build\src\py\Release\py.dll"; DestDir: "{app}\plugins64"; Flags: ignoreversion; Components: core; Permissions: users-full; -Source: "..\..\build\src\stk\Release\stkops.dll"; DestDir: "{app}\plugins64\"; Flags: ignoreversion; Components: core; Permissions: users-full; -Source: "..\..\build\src\websockets\Release\websocketIO.dll"; DestDir: "{app}\plugins64\"; Flags: ignoreversion; Components: core; Permissions: users-full; - -; faust -Source: "..\..\build\src\faustcsound\Release\faustcsound.dll"; DestDir: "{app}\plugins64\"; Flags: ignoreversion; Components: core; Permissions: users-full; -Source: "C:\Program Files\Faust\lib\faust.dll"; DestDir: "{autopf64}\Csound6_x64\bin\"; Flags: ignoreversion; Components: core; Permissions: users-full; - -; widgets -Source: "..\..\build\src\widgets\Release\virtual.dll"; DestDir: "{app}\plugins64\"; Flags: ignoreversion; Components: core; Permissions: users-full; -Source: "..\..\build\src\widgets\Release\widgets.dll"; DestDir: "{app}\plugins64\"; Flags: ignoreversion; Components: core; Permissions: users-full; - -; fluidsynth -Source: "..\..\build\src\fluidOpcodes\Release\fluidOpcodes.dll"; DestDir: "{app}\plugins64\"; Flags: ignoreversion; Components: core; Permissions: users-full; -Source: "..\..\build\src\fluidOpcodes\Release\glib-2.0-0.dll"; DestDir: "{app}\plugins64\"; Flags: ignoreversion; Components: core; Permissions: users-full; -Source: "..\..\build\src\fluidOpcodes\Release\iconv-2.dll"; DestDir: "{app}\plugins64\"; Flags: ignoreversion; Components: core; Permissions: users-full; -Source: "..\..\build\src\fluidOpcodes\Release\intl-8.dll"; DestDir: "{app}\plugins64\"; Flags: ignoreversion; Components: core; Permissions: users-full; -Source: "..\..\build\src\fluidOpcodes\Release\libfluidsynth-3.dll"; DestDir: "{app}\plugins64\"; Flags: ignoreversion; Components: core; Permissions: users-full; -Source: "..\..\build\src\fluidOpcodes\Release\pcre2-8.dll"; DestDir: "{app}\plugins64\"; Flags: ignoreversion; Components: core; Permissions: users-full; - -; hdf5 -Source: "..\..\build\src\hdf5\Release\hdf5ops.dll"; DestDir: "{app}\plugins64\"; Flags: ignoreversion; Components: core; Permissions: users-full; -Source: "..\..\build\src\hdf5\Release\zlib1.dll"; DestDir: "{app}\plugins64\"; Flags: ignoreversion; Components: core; Permissions: users-full; - -[Tasks] -;Name: modifypath; Description: &Add application directory to your PATH environment variable (recommended); - -[Code] -var - NewPage: TWizardPage; - -const - installerMessage = -'This installer will install the Python, Faust, Image, Chua and Ableton Link ' + #13#10 + -'plugin opcodes for Csound.' + #13#10 + #13#10 + -'Opcodes will be installed to C:\Users\\AppData\Local\csound\6.0\plugins64' + #13#10 + -'' + #13#10 + -'Csound should detect and load these plugins whenever it runs. The Faust library will' + #13#10 + -'be installed to C:/Program Files/Csound_x64/bin. If you do not have Csound installed' + #13#10 + -'at this location, please move the faust.dll file after installation'; - - -procedure InitializeWizard(); -var - DescLabel: TNewStaticText ; -begin - NewPage := CreateCustomPage(wpWelcome, 'Csound Plugins Installer', - '') - DescLabel := TNewStaticText .Create(NewPage); - DescLabel.AutoSize := True - DescLabel.Width := NewPage.SurfaceWidth; - DescLabel.WordWrap := True - DescLabel.Parent := NewPage.Surface; - DescLabel.Caption := installerMessage; -end; - diff --git a/6.x/installer/win64/Output/Csound6-Plugins-Windows_x86_64-1.0.exe b/6.x/installer/win64/Output/Csound6-Plugins-Windows_x86_64-1.0.exe deleted file mode 100644 index c5843b82..00000000 Binary files a/6.x/installer/win64/Output/Csound6-Plugins-Windows_x86_64-1.0.exe and /dev/null differ diff --git a/6.x/installer/win64/buildWindows.sh b/6.x/installer/win64/buildWindows.sh deleted file mode 100644 index ec3e4202..00000000 --- a/6.x/installer/win64/buildWindows.sh +++ /dev/null @@ -1,21 +0,0 @@ -#Simple script to build plugins on Windows. Modify each of the paths below for your own needs.. - -cd ../../build -cmake -DFLTK_BASE_LIBRARY_DEBUG=../../../../vcpkg/installed/x64-windows-csound/lib/fltkd.lib \ --DFLTK_BASE_LIBRARY_RELEASE=../../vcpkg/installed/x64-windows-csound/lib/fltk.lib \ --DFLTK_INCLUDE_DIR=../../vcpkg/packages/fltk_x64-windows-csound/include \ --DFLTK_FLUID_EXECUTABLE=../../vcpkg/installed/x64-windows-csound/tools/fluidsynth/fluidsynth.exe \ --DFLTK_FORMS_LIBRARY_RELEASE=../../vcpkg/installed/x64-windows-csound/lib/fltk_forms.lib \ --DFLTK_IMAGES_LIBRARY_RELEASE=../../vcpkg/installed/x64-windows-csound/lib/fltk_images.lib \ --DABLETON_LINK_HOME=D:/sourcecode/link \ --DPNG_PNG_INCLUDE_DIR=../../vcpkg/installed/x64-windows-csound/include \ --DPNG_LIBRARY_RELEASE=C:/Python36/DLLs/png.lib \ --DEIGEN3_INCLUDE_DIR=D:/sourcecode/eigen-3.4-rc1 \ --DFAUST_INCLUDE_DIR_HINT="C:/Program Files/Faust/include" \ --DFAUST_LIB_DIR_HINT="C:/Program Files/Faust/lib" \ --DBUILD_FAUST_OPCODES=ON .. - -cmake --build . --config Release - - - diff --git a/6.x/src/AbletonLinkOpcodes/CMakeLists.txt b/6.x/src/AbletonLinkOpcodes/CMakeLists.txt deleted file mode 100644 index fe2e8dba..00000000 --- a/6.x/src/AbletonLinkOpcodes/CMakeLists.txt +++ /dev/null @@ -1,53 +0,0 @@ -# Build Ableton Link Opcodes -option(BUILD_ABLETON_LINK_OPCODES "Build the Ableton link opcodes" OFF) - -if(BUILD_ABLETON_LINK_OPCODES) - message(STATUS "Ableton Link home: ${ABLETON_LINK_HOME}") - - if(APPLE) - make_plugin(ableton_link_opcodes ableton_link_opcodes.cpp) - set_target_properties(ableton_link_opcodes - PROPERTIES - COMPILE_FLAGS "-std=c++11 -stdlib=libc++ -Wno-multichar -DLINK_PLATFORM_MACOSX=1" - LINK_FLAGS "-std=c++11 -stdlib=libc++" - ) - elseif(LINUX) - include(CheckCXXCompilerFlag) - CHECK_CXX_COMPILER_FLAG("-std=gnu++11" COMPILER_SUPPORTS_CXX11) - - if(COMPILER_SUPPORTS_CXX11) - make_plugin(ableton_link_opcodes ableton_link_opcodes.cpp) - set_target_properties(ableton_link_opcodes PROPERTIES COMPILE_FLAGS "-std=gnu++11 -Wno-multichar -DLINK_PLATFORM_LINUX=1") - else() - message(STATUS "Not building ableton_link_opcodes as no C++11 support found.") - endif() - else() - make_plugin(ableton_link_opcodes ableton_link_opcodes.cpp) - - if(WIN32) - if(USE_VCPKG) - find_package(AbletonLink CONFIG REQUIRED PATH_SUFFIXES share/ableton-link) - target_link_libraries(ableton_link_opcodes PRIVATE Ableton::Link) - else() - set_target_properties(ableton_link_opcodes - PROPERTIES - COMPILE_FLAGS "-std=gnu++11 -Wno-multichar -DLINK_PLATFORM_WINDOWS=1" - LINK_FLAGS "-std=gnu++11" - ) - target_link_libraries(ableton_link_opcodes wsock32 ws2_32 iphlpapi) - endif() - endif() - endif() - - target_include_directories(ableton_link_opcodes - PRIVATE - ${CSOUND_INCLUDE_DIRS} - ${ABLETON_LINK_HOME}/include - ${ABLETON_LINK_HOME}/modules/asio-standalone/asio/include - ) - - install(TARGETS ableton_link_opcodes - LIBRARY DESTINATION ${PLUGIN_INSTALL_DIR} - ARCHIVE DESTINATION ${LIBRARY_INSTALL_DIR} - ) -endif() diff --git a/6.x/src/AbletonLinkOpcodes/ableton_link_opcodes.cpp b/6.x/src/AbletonLinkOpcodes/ableton_link_opcodes.cpp deleted file mode 100644 index dd83d12f..00000000 --- a/6.x/src/AbletonLinkOpcodes/ableton_link_opcodes.cpp +++ /dev/null @@ -1,890 +0,0 @@ -/* - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ -#if defined(__MINGW64__) -#include -#include - -extern "C" { - uint64_t htonll(uint64_t value) - { - _byteswap_uint64(value); - } - uint64_t ntohll(uint64_t value) - { - _byteswap_uint64(value); - } -} -#endif -#if defined(__ANDROID__) -/* Ableton Link preupposes GNU libc, but we are using clang. - * So we must replace all the ifaddrs code. - * Pretend that ifaddrs.h has been included: - */ -#define _IFADDRS_H_ -// Put in our own ifaddrs.h from https://raw.githubusercontent.com/libpd/abl_link/master/external/android-ifaddrs/ifaddrs.h. -////////////////////////////////////////////////////////////////////////////// -/* - * libjingle - * Copyright 2013, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -#ifndef TALK_BASE_IFADDRS_ANDROID_H_ -#define TALK_BASE_IFADDRS_ANDROID_H_ -#include -#include -// Implementation of getifaddrs for Android. -// Fills out a list of ifaddr structs (see below) which contain information -// about every network interface available on the host. -// See 'man getifaddrs' on Linux or OS X (nb: it is not a POSIX function). -struct ifaddrs { - struct ifaddrs* ifa_next; - char* ifa_name; - uint32_t ifa_flags; - struct sockaddr* ifa_addr; - struct sockaddr* ifa_netmask; - // Real ifaddrs has broadcast, point to point and data members. - // We don't need them (yet?). -}; -int getifaddrs(struct ifaddrs** result); -void freeifaddrs(struct ifaddrs* addrs); -#endif // TALK_BASE_IFADDRS_ANDROID_H_ -////////////////////////////////////////////////////////////////////////////// -// Put in our own ifaddrs.cpp from https://raw.githubusercontent.com/libpd/abl_link/master/external/android-ifaddrs/ifaddrs.cpp. -////////////////////////////////////////////////////////////////////////////// -/* libjingle - * Copyright 2012, Google Inc. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions are met: - * - * 1. Redistributions of source code must retain the above copyright notice, - * this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright notice, - * this list of conditions and the following disclaimer in the documentation - * and/or other materials provided with the distribution. - * 3. The name of the author may not be used to endorse or promote products - * derived from this software without specific prior written permission. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED - * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF - * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO - * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, - * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; - * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, - * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR - * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF - * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - */ -//#include "ifaddrs.h" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -struct netlinkrequest { - nlmsghdr header; - ifaddrmsg msg; -}; -namespace { -const int kMaxReadSize = 4096; -}; -int set_ifname(struct ifaddrs* ifaddr, int interface) { - char buf[IFNAMSIZ] = {0}; - char* name = if_indextoname(interface, buf); - if (name == NULL) { - return -1; - } - ifaddr->ifa_name = new char[strlen(name) + 1]; - strncpy(ifaddr->ifa_name, name, strlen(name) + 1); - return 0; -} -int set_flags(struct ifaddrs* ifaddr) { - int fd = socket(AF_INET, SOCK_DGRAM, 0); - if (fd == -1) { - return -1; - } - ifreq ifr; - memset(&ifr, 0, sizeof(ifr)); - strncpy(ifr.ifr_name, ifaddr->ifa_name, IFNAMSIZ - 1); - int rc = ioctl(fd, SIOCGIFFLAGS, &ifr); - close(fd); - if (rc == -1) { - return -1; - } - ifaddr->ifa_flags = ifr.ifr_flags; - return 0; -} -int set_addresses(struct ifaddrs* ifaddr, ifaddrmsg* msg, void* data, - size_t len) { - if (msg->ifa_family == AF_INET) { - sockaddr_in* sa = new sockaddr_in; - sa->sin_family = AF_INET; - memcpy(&sa->sin_addr, data, len); - ifaddr->ifa_addr = reinterpret_cast(sa); - } else if (msg->ifa_family == AF_INET6) { - sockaddr_in6* sa = new sockaddr_in6; - sa->sin6_family = AF_INET6; - sa->sin6_scope_id = msg->ifa_index; - memcpy(&sa->sin6_addr, data, len); - ifaddr->ifa_addr = reinterpret_cast(sa); - } else { - return -1; - } - return 0; -} -int make_prefixes(struct ifaddrs* ifaddr, int family, int prefixlen) { - char* prefix = NULL; - if (family == AF_INET) { - sockaddr_in* mask = new sockaddr_in; - mask->sin_family = AF_INET; - memset(&mask->sin_addr, 0, sizeof(in_addr)); - ifaddr->ifa_netmask = reinterpret_cast(mask); - if (prefixlen > 32) { - prefixlen = 32; - } - prefix = reinterpret_cast(&mask->sin_addr); - } else if (family == AF_INET6) { - sockaddr_in6* mask = new sockaddr_in6; - mask->sin6_family = AF_INET6; - memset(&mask->sin6_addr, 0, sizeof(in6_addr)); - ifaddr->ifa_netmask = reinterpret_cast(mask); - if (prefixlen > 128) { - prefixlen = 128; - } - prefix = reinterpret_cast(&mask->sin6_addr); - } else { - return -1; - } - for (int i = 0; i < (prefixlen / 8); i++) { - *prefix++ = 0xFF; - } - char remainder = 0xff; - remainder <<= (8 - prefixlen % 8); - *prefix = remainder; - return 0; -} -int populate_ifaddrs(struct ifaddrs* ifaddr, ifaddrmsg* msg, void* bytes, - size_t len) { - if (set_ifname(ifaddr, msg->ifa_index) != 0) { - return -1; - } - if (set_flags(ifaddr) != 0) { - return -1; - } - if (set_addresses(ifaddr, msg, bytes, len) != 0) { - return -1; - } - if (make_prefixes(ifaddr, msg->ifa_family, msg->ifa_prefixlen) != 0) { - return -1; - } - return 0; -} -int getifaddrs(struct ifaddrs** result) { - int fd = socket(PF_NETLINK, SOCK_RAW, NETLINK_ROUTE); - if (fd < 0) { - return -1; - } - netlinkrequest ifaddr_request; - memset(&ifaddr_request, 0, sizeof(ifaddr_request)); - ifaddr_request.header.nlmsg_flags = NLM_F_ROOT | NLM_F_REQUEST; - ifaddr_request.header.nlmsg_type = RTM_GETADDR; - ifaddr_request.header.nlmsg_len = NLMSG_LENGTH(sizeof(ifaddrmsg)); - ssize_t count = send(fd, &ifaddr_request, ifaddr_request.header.nlmsg_len, 0); - if (static_cast(count) != ifaddr_request.header.nlmsg_len) { - close(fd); - return -1; - } - struct ifaddrs* start = NULL; - struct ifaddrs* current = NULL; - char buf[kMaxReadSize]; - ssize_t amount_read = recv(fd, &buf, kMaxReadSize, 0); - while (amount_read > 0) { - nlmsghdr* header = reinterpret_cast(&buf[0]); - size_t header_size = static_cast(amount_read); - for ( ; NLMSG_OK(header, header_size); - header = NLMSG_NEXT(header, header_size)) { - switch (header->nlmsg_type) { - case NLMSG_DONE: - // Success. Return. - *result = start; - close(fd); - return 0; - case NLMSG_ERROR: - close(fd); - freeifaddrs(start); - return -1; - case RTM_NEWADDR: { - ifaddrmsg* address_msg = - reinterpret_cast(NLMSG_DATA(header)); - rtattr* rta = IFA_RTA(address_msg); - ssize_t payload_len = IFA_PAYLOAD(header); - while (RTA_OK(rta, payload_len)) { - if (rta->rta_type == IFA_ADDRESS) { - int family = address_msg->ifa_family; - if (family == AF_INET || family == AF_INET6) { - ifaddrs* newest = new ifaddrs; - memset(newest, 0, sizeof(ifaddrs)); - if (current) { - current->ifa_next = newest; - } else { - start = newest; - } - if (populate_ifaddrs(newest, address_msg, RTA_DATA(rta), - RTA_PAYLOAD(rta)) != 0) { - freeifaddrs(start); - *result = NULL; - return -1; - } - current = newest; - } - } - rta = RTA_NEXT(rta, payload_len); - } - break; - } - } - } - amount_read = recv(fd, &buf, kMaxReadSize, 0); - } - close(fd); - freeifaddrs(start); - return -1; -} -void freeifaddrs(struct ifaddrs* addrs) { - struct ifaddrs* last = NULL; - struct ifaddrs* cursor = addrs; - while (cursor) { - delete[] cursor->ifa_name; - delete cursor->ifa_addr; - delete cursor->ifa_netmask; - last = cursor; - cursor = cursor->ifa_next; - delete last; - } -} -#endif // defined(ANDROID) -#include -#include - -/** - * A B L E T O N L I N K O P C O D E S - * - * Author: Michael Gogins - * January 2017 - * - * These opcodes implement the Ableton Link protocol from: - * - * https://github.com/Ableton/link - * - * The purpose of Ableton Link is to sychronize musical time, beat, and phase - * between musical applications performing in real time from separate - * programs, processes, and network addresses. This is useful e.g. for laptop - * orchestras. - * - * There is one global, peer-to-peer Link session that maintains a global time - * and beat on the local area network. Any peer may set the global tempo, - * and thereafter all peers in the session share that tempo. A process may - * have any number of peers (i.e., any number of Link objects). Each peer - * may also define its own "quantum" i.e. some multiple of the beat, e.g. a - * quantum of 4 might imply 4/4 time. The phase of the time is defined w.r.t - * the quantum, e.g. phase 0.5 of a quantum of 4 would be the second beat of - * the measure. Peers may read and write timelines with local time, beat, and - * phase, counting from when the peer is enabled, but the tempo and beat on - * all timelines for all peers in the session will coincide. - * - * The first peer to enable a session determines the initial tempo. After - * that, the tempo is changed only, and whenever, any peer explicity calls - * the set tempo functon (link_tempo_set, in Csound). - * - * The Link tempo is independent of the Csound score tempo. Performances that - * need to synchronize the score tempo with the Link tempo may use the tempo - * opcode to set the score tempo from the Link tempo; or conversely, set the - * Link tempo from the score tempo using the tempoval opcode. - * - * Please note, the phase and beat obtained or set by these opcodes is only as - * precise as allowed by the duration of Csound's kperiod, the audio driver - * used by Csound, the network stability, and the system's most precise clock. - * - * Build for testing with something like: - * - * g++ ableton_link_opcodes.cpp -std=gnu++11 -DLINK_PLATFORM_WINDOWS=1 -Werror -Wno-multichar -O2 -g -lcsound64 -I/home/restore/link/include -I/home/restore/link/modules/asio-standalone/asio/include -I../include -I../H -shared -oableton_link_opcodes.dll - * g++ ableton_link_opcodes.cpp -std=gnu++11 -DLINK_PLATFORM_LINUX=1 -Werror -Wno-multichar -O2 -g -fPIC -lcsound64 -I/home/mkg/link/include -I/home/mkg/link/modules/asio-standalone/asio/include -I/usr/local/include/csound -I/home/mkg/csound/csound/include -shared -oableton_link_opcodes.so - */ -using namespace csound; - -static bool enable_debug = 0; - -#define debug(fmt, ...) \ - do { if (enable_debug) fprintf(stderr, fmt, __VA_ARGS__); } while (0) - -using floating_point_microseconds = std::chrono::duration; -using floating_point_seconds = std::chrono::duration; - -/** - * This is used to perform a static type cast in a clear, obvious, and - * bulletproof way. Here, we want a bit-for-bit cast between a Csound MYFLT - * (usually a double) and a pointer. This will only work if sizeof(MYFLT) > - * sizeof(void *). That is always the case on 32 bit CPU architecture, and - * will be the case on 64 bit CPU architecture if MYFLT is double-precision. - */ -typedef union { - MYFLT myflt; - ableton::Link *pointer; -} link_cast_t; - -// i_link link_create [j_bpm = 60] - -class link_create_t : public OpcodeBase -{ - // Pfields out: - MYFLT *r0_link; - // Pfields in: - MYFLT *p0_bpm; - // State: - link_cast_t link; - double bpm; -public: - int init(CSOUND *csound) { - if (*p0_bpm == FL(-1.0)) { - bpm = 60; - } else { - bpm = *p0_bpm; - } - link.pointer = new ableton::Link(bpm); - debug("link_create: bpm: %f link.pointer: %p link.myflt: %g\n", bpm, link.pointer, link.myflt); - *r0_link = link.myflt; - return OK; - } -}; - -// link_enable i_link [, P_enabled = 1] - -class link_enable_t : public OpcodeBase -{ - // Pfields out: - // Pfields in: - MYFLT *p0_link; - MYFLT *p1_enabled; - // State: - link_cast_t link; - MYFLT prior_enabled; -public: - int init(CSOUND *csound) { - link.myflt = *p0_link; - link.pointer->enable(*p1_enabled); - prior_enabled = *p1_enabled; - debug("link_enable: link.pointer: %p link.myflt: %g p1_enabled: %f isEnabled: %d\n", link.pointer, link.myflt, *p1_enabled, link.pointer->isEnabled()); - return OK; - } - int kontrol(CSOUND *csound) { - if (prior_enabled != *p1_enabled) { - link.pointer->enable(*p1_enabled); - debug("link_enable: link.pointer: %p link.myflt: %g p1_enabled: %f isEnabled: %d\n", link.pointer, link.myflt, *p1_enabled, link.pointer->isEnabled()); - prior_enabled = *p1_enabled; - } - return OK; - } -}; - -// k_is_enabled link_is_enabled i_link - -class link_is_enabled_t : public OpcodeBase -{ - // Pfields out: - MYFLT *r0_is_enabled; - // Pfields in: - MYFLT *p0_link; - // State: - link_cast_t link; -public: - int init(CSOUND *csound) { - link.myflt = *p0_link; - *r0_is_enabled = link.pointer->isEnabled(); - return OK; - } - int kontrol(CSOUND *csound) { - *r0_is_enabled = link.pointer->isEnabled(); - return OK; - } -}; - -// link_tempo_set i_link, k_bpm [, P_at_time_seconds = current time] - -class link_tempo_set_t : public OpcodeBase -{ - // Pfields out: - // Pfields in: - MYFLT *p0_link; - MYFLT *p1_bpm; - MYFLT *p2_at_time_seconds; - // State: - link_cast_t link; - MYFLT prior_bpm; - std::chrono::microseconds at_time_microseconds; -public: - int init(CSOUND *csound) { - link.myflt = *p0_link; - auto timeline = link.pointer->captureAudioSessionState(); - prior_bpm = *p1_bpm; - if (*p2_at_time_seconds == FL(-1.0)) { - at_time_microseconds = link.pointer->clock().micros(); - } else { - at_time_microseconds = std::chrono::duration_cast(floating_point_seconds(*p2_at_time_seconds)); - } - timeline.setTempo(prior_bpm, at_time_microseconds); - link.pointer->commitAudioSessionState (timeline); - return OK; - } - int kontrol(CSOUND *csound) { - if (prior_bpm != *p1_bpm) { - auto timeline = link.pointer->captureAudioSessionState(); - prior_bpm = *p1_bpm; - if (*p2_at_time_seconds == FL(-1.0)) { - at_time_microseconds = link.pointer->clock().micros(); - } else { - at_time_microseconds = std::chrono::duration_cast(floating_point_seconds(*p2_at_time_seconds)); - } - timeline.setTempo(prior_bpm, at_time_microseconds); - link.pointer->commitAudioSessionState(timeline); - } - return OK; - } -}; - -// k_bpm link_tempo_get i_link - -class link_tempo_get_t : public OpcodeBase -{ - // Pfields out: - MYFLT *r0_bpm; - // Pfields in: - MYFLT *p0_link; - // State: - link_cast_t link; -public: - int init(CSOUND *csound) { - link.myflt = *p0_link; - auto timeline = link.pointer->captureAudioSessionState(); - *r0_bpm = timeline.tempo(); - return OK; - } - int kontrol(CSOUND *csound) { - auto timeline = link.pointer->captureAudioSessionState(); - *r0_bpm = timeline.tempo(); - return OK; - } -}; - -// k_beat_number, k_phase, k_current_time_seconds link_beat_get i_link [, P_quantum = 1] - -class link_beat_get_t : public OpcodeBase -{ - // Pfields out: - MYFLT *r0_beat; - MYFLT *r1_phase; - MYFLT *r2_seconds; - // Pfields in: - MYFLT *p0_link; - MYFLT *p1_quantum; - // State: - link_cast_t link; - std::chrono::microseconds at_time_microseconds; -public: - int init(CSOUND *csound) { - link.myflt = *p0_link; - auto timeline = link.pointer->captureAudioSessionState(); - at_time_microseconds = link.pointer->clock().micros(); - *r0_beat = timeline.beatAtTime(at_time_microseconds, *p1_quantum); - *r1_phase = timeline.phaseAtTime(at_time_microseconds, *p1_quantum); - *r2_seconds = std::chrono::duration_cast(at_time_microseconds).count(); - return OK; - } - int kontrol(CSOUND *csound) { - auto timeline = link.pointer->captureAudioSessionState(); - at_time_microseconds = link.pointer->clock().micros(); - *r0_beat = timeline.beatAtTime(at_time_microseconds, *p1_quantum); - *r1_phase = timeline.phaseAtTime(at_time_microseconds, *p1_quantum); - *r2_seconds = std::chrono::duration_cast(at_time_microseconds).count(); - return OK; - } -}; - -// k_trigger, k_beat, k_phase, k_current_time_seconds link_metro i_link [, P_quantum = 1] - -class link_metro_t : public OpcodeBase -{ - // Pfields out: - MYFLT *r0_trigger; - MYFLT *r1_beat; - MYFLT *r2_phase; - MYFLT *r3_seconds; - // Pfields in: - MYFLT *p0_link; - MYFLT *p1_quantum; - // State: - link_cast_t link; - std::chrono::microseconds at_time_microseconds; - MYFLT prior_phase; -public: - // The trigger is "on" when the new phase is less than the prior phase, and "off" - // when the new phase is greater than the prior phase. - int init(CSOUND *csound) { - link.myflt = *p0_link; - debug("link_metro i: link.pointer: %p link.myflt: %g\n", link.pointer, link.myflt); - auto timeline = link.pointer->captureAudioSessionState(); - at_time_microseconds = link.pointer->clock().micros(); - *r0_trigger = 0; - *r1_beat = timeline.beatAtTime(at_time_microseconds, *p1_quantum); - *r2_phase = timeline.phaseAtTime(at_time_microseconds, *p1_quantum); - prior_phase = *r2_phase; - *r3_seconds = std::chrono::duration_cast(at_time_microseconds).count(); - debug("link_metro i: r0_trigger: %f r1_beat: %f r2_phase: %f r3_seconds: %f\n", *r0_trigger, *r1_beat, *r2_phase, *r3_seconds); - return OK; - } - int kontrol(CSOUND *csound) { - auto timeline = link.pointer->captureAudioSessionState(); - at_time_microseconds = link.pointer->clock().micros(); - *r1_beat = timeline.beatAtTime(at_time_microseconds, *p1_quantum); - *r2_phase = timeline.phaseAtTime(at_time_microseconds, *p1_quantum); - if (*r2_phase < prior_phase) { - *r0_trigger = 1; - debug("link_metro k: r0_trigger: %f r1_beat: %f r2_phase: %f r3_seconds: %f\n", *r0_trigger, *r1_beat, *r2_phase, *r3_seconds); - } else { - *r0_trigger = 0; - } - prior_phase = *r2_phase; - *r3_seconds = std::chrono::duration_cast(at_time_microseconds).count(); - return OK; - } -}; - -// link_beat_request i_link k_beat [, J_at_time_seconds = current time [, P_quantum = 1]] - -class link_beat_request_t : public OpcodeBase -{ - // Pfields out: - // Pfields in: - MYFLT *p0_link; - MYFLT *p1_beat; - MYFLT *p2_at_time_seconds; - MYFLT *p3_quantum; - // State: - link_cast_t link; - MYFLT prior_beat; - MYFLT prior_at_time_seconds; - MYFLT prior_quantum; - std::chrono::microseconds at_time_microseconds; -public: - int init(CSOUND *csound) { - link.myflt = *p0_link; - prior_beat = *p1_beat; - prior_at_time_seconds = *p2_at_time_seconds; - prior_quantum = *p3_quantum; - if (*p2_at_time_seconds == FL(-1.0)) { - at_time_microseconds = link.pointer->clock().micros(); - } else { - at_time_microseconds = std::chrono::duration_cast(floating_point_seconds(*p2_at_time_seconds)); - } - auto timeline = link.pointer->captureAudioSessionState(); - timeline.requestBeatAtTime(prior_beat, at_time_microseconds, prior_quantum); - link.pointer->commitAudioSessionState(timeline); - return OK; - } - int kontrol(CSOUND *csound) { - if ((prior_beat != *p1_beat) || (prior_at_time_seconds != *p2_at_time_seconds) || (prior_quantum != *p3_quantum)) { - if (*p2_at_time_seconds == FL(-1.0)) { - at_time_microseconds = link.pointer->clock().micros(); - } else { - at_time_microseconds = std::chrono::duration_cast(floating_point_seconds(*p2_at_time_seconds)); - } - auto timeline = link.pointer->captureAudioSessionState(); - timeline.requestBeatAtTime(prior_beat, at_time_microseconds, prior_quantum); - link.pointer->commitAudioSessionState(timeline); - prior_beat = *p1_beat; - prior_at_time_seconds = *p2_at_time_seconds; - prior_quantum = *p3_quantum; - } - return OK; - } -}; - -// link_beat_force i_link k_beat [, J_at_time_seconds = current time [, P_quantum = 1]] - -class link_beat_force_t : public OpcodeBase -{ - // Pfields out: - // Pfields in: - MYFLT *p0_link; - MYFLT *p1_beat; - MYFLT *p2_at_time_seconds; - MYFLT *p3_quantum; - // State: - link_cast_t link; - MYFLT prior_beat; - MYFLT prior_at_time_seconds; - MYFLT prior_quantum; - std::chrono::microseconds at_time_microseconds; -public: - int init(CSOUND *csound) { - link.myflt = *p0_link; - prior_beat = *p1_beat; - prior_at_time_seconds = *p2_at_time_seconds; - prior_quantum = *p3_quantum; - if (*p2_at_time_seconds == FL(-1.0)) { - at_time_microseconds = link.pointer->clock().micros(); - } else { - at_time_microseconds = std::chrono::duration_cast(floating_point_seconds(*p2_at_time_seconds)); - } - auto timeline = link.pointer->captureAudioSessionState(); - timeline.forceBeatAtTime(prior_beat, at_time_microseconds, prior_quantum); - link.pointer->commitAudioSessionState(timeline); - return OK; - } - int kontrol(CSOUND *csound) { - if ((prior_beat != *p1_beat) || (prior_at_time_seconds != *p2_at_time_seconds) || (prior_quantum != *p3_quantum)) { - if (*p2_at_time_seconds == FL(-1.0)) { - at_time_microseconds = link.pointer->clock().micros(); - } else { - at_time_microseconds = std::chrono::duration_cast(floating_point_seconds(*p2_at_time_seconds)); - } - auto timeline = link.pointer->captureAudioSessionState(); - timeline.forceBeatAtTime(prior_beat, at_time_microseconds, prior_quantum); - link.pointer->commitAudioSessionState(timeline); - prior_beat = *p1_beat; - prior_at_time_seconds = *p2_at_time_seconds; - prior_quantum = *p3_quantum; - } - return OK; - } -}; - -// k_count link_peers i_link - -class link_peers_t : public OpcodeBase -{ - // Pfields out: - MYFLT *r0_peers; - // Pfields in: - MYFLT *p0_link; - // State: - link_cast_t link; -public: - int init(CSOUND *csound) { - link.myflt = *p0_link; - *r0_peers = link.pointer->numPeers(); - return OK; - } - int kontrol(CSOUND *csound) { - *r0_peers = link.pointer->numPeers(); - return OK; - } -}; - -extern "C" { - - OENTRY oentries[] = - { -// i_link link_create [j_bpm = 60] - { - (char*)"link_create", - sizeof(link_create_t), - 0, - 1, - (char*)"i", - (char*)"j", - (SUBR) link_create_t::init_, - 0, - 0, - }, -// link_enable i_link [, P_enabled = 1] - { - (char*)"link_enable", - sizeof(link_enable_t), - 0, - 3, - (char*)"", - (char*)"iP", - (SUBR) link_enable_t::init_, - (SUBR) link_enable_t::kontrol_, - 0, - }, -// k_is_enabled link_is_enabled i_link - { - (char*)"link_is_enabled", - sizeof(link_is_enabled_t), - 0, - 3, - (char*)"k", - (char*)"i", - (SUBR) link_is_enabled_t::init_, - (SUBR) link_is_enabled_t::kontrol_, - 0, - }, - // link_tempo_set i_link, k_bpm [, J_at_time_seconds = current time] - { - (char*)"link_tempo_set", - sizeof(link_tempo_set_t), - 0, - 3, - (char*)"", - (char*)"ikJ", - (SUBR) link_tempo_set_t::init_, - (SUBR) link_tempo_set_t::kontrol_, - 0, - }, -// k_bpm link_tempo_get i_link - { - (char*)"link_tempo_get", - sizeof(link_tempo_set_t), - 0, - 3, - (char*)"k", - (char*)"i", - (SUBR) link_tempo_get_t::init_, - (SUBR) link_tempo_get_t::kontrol_, - 0, - }, -// k_beat_number, k_phase, k_current_time_seconds link_beat_get i_link [, P_quantum = 1] - { - (char*)"link_beat_get", - sizeof(link_beat_get_t), - 0, - 3, - (char*)"kkk", - (char*)"iP", - (SUBR) link_beat_get_t::init_, - (SUBR) link_beat_get_t::kontrol_, - 0, - }, -// k_trigger, k_beat, k_phase, k_current_time_seconds link_metro i_link [, P_quantum = 1] - { - (char*)"link_metro", - sizeof(link_metro_t), - 0, - 3, - (char*)"kkkk", - (char*)"iP", - (SUBR) link_metro_t::init_, - (SUBR) link_metro_t::kontrol_, - 0, - }, -// link_beat_request i_link k_beat [, J_at_time_seconds = current time [, P_quantum = 1]] - { - (char*)"link_beat_request", - sizeof(link_beat_request_t), - 0, - 3, - (char*)"", - (char*)"ikJP", - (SUBR) link_beat_request_t::init_, - (SUBR) link_beat_request_t::kontrol_, - 0, - }, -// link_beat_force i_link k_beat [, J_at_time_seconds = current time [, P_quantum = 1]] - { - (char*)"link_beat_force", - sizeof(link_beat_force_t), - 0, - 3, - (char*)"", - (char*)"ikJP", - (SUBR) link_beat_force_t::init_, - (SUBR) link_beat_force_t::kontrol_, - 0, - }, -// k_count link_peers i_link - { - (char*)"link_peers", - sizeof(link_peers_t), - 0, - 3, - (char*)"k", - (char*)"i", - (SUBR) link_peers_t::init_, - (SUBR) link_peers_t::kontrol_, - 0, - }, - { - 0, - 0, - 0, - 0, - 0, - 0, - 0, - 0, - } - }; - - PUBLIC int csoundModuleCreate(CSOUND *csound) - { - return 0; - } - - PUBLIC int csoundModuleInit(CSOUND *csound) - { - int status = 0; - for(OENTRY *oentry = &oentries[0]; oentry->opname; oentry++) - { - status |= csound->AppendOpcode(csound, oentry->opname, - oentry->dsblksiz, oentry->flags, - oentry->thread, - oentry->outypes, oentry->intypes, - (int (*)(CSOUND*,void*)) oentry->iopadr, - (int (*)(CSOUND*,void*)) oentry->kopadr, - (int (*)(CSOUND*,void*)) oentry->aopadr); - } - return status; - } - - PUBLIC int csoundModuleDestroy(CSOUND *csound) - { - return 0; - } - -} diff --git a/6.x/src/AbletonLinkOpcodes/jni/Android.mk b/6.x/src/AbletonLinkOpcodes/jni/Android.mk deleted file mode 100644 index 5111a741..00000000 --- a/6.x/src/AbletonLinkOpcodes/jni/Android.mk +++ /dev/null @@ -1,20 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -CSOUND_SRC_ROOT := ../../../.. -ABLETON_LINK_HOME :=/home/mkg/link - -LOCAL_MODULE := ableton_link_opcodes - -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../Engine $(LOCAL_PATH)/../../../../H $(LOCAL_PATH)/../../../../include $(LOCAL_PATH)/../../../.. $(LOCAL_PATH)/../../../ $(NDK_MODULE_PATH)/libsndfile-android/jni $(LOCAL_PATH)/../../../CsoundAndroid/jni $(ABLETON_LINK_HOME)/include $(ABLETON_LINK_HOME)/modules/asio-standalone/asio/include -LOCAL_CFLAGS := -O3 -D__BUILDING_LIBCSOUND -DENABLE_NEW_PARSER -DLINUX -DHAVE_DIRENT_H -DHAVE_FCNTL_H -DHAVE_UNISTD_H -DHAVE_STDINT_H -DHAVE_SYS_TIME_H -DHAVE_SYS_TYPES_H -DHAVE_TERMIOS_H -DLINK_PLATFORM_LINUX=1 -DANDROID=1 -LOCAL_CPPFLAGS :=$(LOCAL_CFLAGS) -LOCAL_CPPFLAGS += -std=c++11 -pthread -frtti -fcxx-exceptions -Wno-multichar -fexceptions #-Werror -LOCAL_LDLIBS := -latomic -LOCAL_LDFLAGS += -Wl,--export-dynamic - -LOCAL_SRC_FILES := $(CSOUND_SRC_ROOT)/Opcodes/ableton_link_opcodes.cpp - -include $(BUILD_SHARED_LIBRARY) - diff --git a/6.x/src/AbletonLinkOpcodes/jni/Application.mk b/6.x/src/AbletonLinkOpcodes/jni/Application.mk deleted file mode 100644 index 27e8ae21..00000000 --- a/6.x/src/AbletonLinkOpcodes/jni/Application.mk +++ /dev/null @@ -1,6 +0,0 @@ -APP_ABI := armeabi-v7a arm64-v8a -APP_CPPFLAGS += -fexceptions -frtti -APP_OPTIM := release -APP_PLATFORM := android-21 -APP_STL := c++_shared -NDK_TOOLCHAIN_VERSION := clang diff --git a/6.x/src/CUDA/CMakeLists.txt b/6.x/src/CUDA/CMakeLists.txt deleted file mode 100644 index 608f75f2..00000000 --- a/6.x/src/CUDA/CMakeLists.txt +++ /dev/null @@ -1,68 +0,0 @@ -option(BUILD_CUDA_OPCODES "Build the CUDA GPU opcodes" OFF) - -function(make_cuda_plugin libname srcs) - SET(LIB_TYPE_DYNAMIC) #set the lib type - CUDA_ADD_LIBRARY(${libname} ${LIB_TYPE} ${srcs}) - - set(i 2) - while( ${i} LESS ${ARGC} ) - target_link_libraries(${libname} ${ARGV${i}}) - math(EXPR i "${i}+1") - endwhile() - - target_include_directories(${libname} PRIVATE ${CSOUND_INCLUDE_DIRS}) - - # set_target_properties(${libname} PROPERTIES - # RUNTIME_OUTPUT_DIRECTORY ${BUILD_PLUGINS_DIR} - # LIBRARY_OUTPUT_DIRECTORY ${BUILD_PLUGINS_DIR} - # ARCHIVE_OUTPUT_DIRECTORY ${BUILD_PLUGINS_DIR}) - - install(TARGETS ${libname} LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" ) -endfunction() - -if(BUILD_CUDA_OPCODES) - find_package(CUDA REQUIRED) - check_deps(BUILD_CUDA_OPCODES CUDA_FOUND) - message(STATUS "Building the CUDA opcodes") - - include_directories(${CMAKE_HOME_DIRECTORY}/include) - include_directories(/usr/local/include) - LIST(APPEND CUDA_NVCC_FLAGS "-use_fast_math") - - if(APPLE) - if(CMAKE_OSX_DEPLOYMENT_TARGET MATCHES 10.6) - LIST(APPEND CUDA_NVCC_FLAGS "-arch=sm_11") - make_cuda_plugin(cudaop1 adsyn11.cu) - make_cuda_plugin(cudaop2 pvsopsf.cu) - make_cuda_plugin(cudaop3 slidingm.cu) - make_cuda_plugin(cudaop4 convf.cu) - make_cuda_plugin(cudaop5 pconv11.cu) - else() - LIST(APPEND CUDA_NVCC_FLAGS "-ccbin /usr/bin/clang -arch=sm_30 -Xcompiler -stdlib=libstdc++") - make_cuda_plugin(cudaop1 adsyn.cu) - make_cuda_plugin(cudaop2 pvsops.cu) - make_cuda_plugin(cudaop3 slidingm.cu) - make_cuda_plugin(cudaop4 conv.cu) - make_cuda_plugin(cudaop5 pconv.cu) - endif() - else() - if(CUDA_VERSION VERSION_LESS 9.0) - LIST(APPEND CUDA_NVCC_FLAGS "-arch=sm_20") - else() - LIST(APPEND CUDA_NVCC_FLAGS "-arch=sm_75") - endif() - - make_cuda_plugin(cudaop1 adsyn.cu) - make_cuda_plugin(cudaop2 pvsops.cu) - # make_cuda_plugin(cudaop3 slidingm.cu) - make_cuda_plugin(cudaop4 conv.cu) - make_cuda_plugin(cudaop5 pconv.cu) - endif() - - CUDA_ADD_CUFFT_TO_TARGET(cudaop2) - CUDA_ADD_CUFFT_TO_TARGET(cudaop5) - - SET(CUDA_VERBOSE_BUILD ON CACHE BOOL "nvcc verbose" FORCE) -else() - message(STATUS "Not building the CUDA opcodes") -endif() diff --git a/6.x/src/CUDA/adsyn11.cu b/6.x/src/CUDA/adsyn11.cu deleted file mode 100644 index f104e160..00000000 --- a/6.x/src/CUDA/adsyn11.cu +++ /dev/null @@ -1,185 +0,0 @@ -// -*- c++ -*- -/* adsyn11.cu - (c) Victor Lazzarini, 2013 - - based on M Puckette's pitch tracking algorithm. - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#include -#include - -typedef struct cudadsyn_ { - OPDS h; - MYFLT *asig; - PVSDAT *fsig; - MYFLT *kamp, *kfreq; - MYFLT *inum; - float *out; - float *frame; - int64_t *ndx; - float *fp, *previous; - AUXCH out_; - int bins, blocks, threads; - int count; - int vsamps, mblocks, mthreads; - int framecount; -} CUDADSYN; - -static int destroy_cudadsyn(CSOUND *csound, void *pp); - -static int init_cudadsyn(CSOUND *csound, CUDADSYN *p){ - - int asize, ipsize, fpsize, blockspt; - if(p->fsig->overlap > 1024) - return csound->InitError(csound, "overlap is too large\n"); - cudaDeviceProp deviceProp; - cudaGetDeviceProperties(&deviceProp, 0); - blockspt = deviceProp.maxThreadsPerBlock; - if(deviceProp.major < 1) - csound->InitError(csound, - "this opcode requires device capability 1.0 minimum. Device is %d.%d\n", - deviceProp.major, deviceProp.minor ); - - p->bins = (p->fsig->N)/2; - - if(*p->inum > 0 && *p->inum < p->bins) p->bins = *p->inum; - - p->vsamps = p->fsig->overlap; - p->threads = p->bins*p->vsamps; - p->blocks = p->threads > blockspt ? p->threads/blockspt : 1; - p->mthreads = p->bins; - p->mblocks = p->mthreads > blockspt ? p->mthreads/blockspt : 1; - - p->threads /= p->blocks; - p->mthreads /= p->mblocks; - - asize = p->vsamps*p->bins*sizeof(float); - ipsize = p->fsig->N*sizeof(int64_t)/2; - fpsize = p->fsig->N*sizeof(float); - - cudaMalloc(&p->out, asize); - cudaMalloc(&p->ndx, ipsize); - cudaMalloc(&p->frame, fpsize); - cudaMalloc(&p->previous, fpsize/2); - cudaMemset(p->previous, 0, fpsize/2); - cudaMemset(p->ndx, 0, ipsize); - - asize = p->vsamps*sizeof(float); - if(p->out_.auxp == NULL || - p->out_.size < asize) - csound->AuxAlloc(csound, asize , &p->out_); - - csound->RegisterDeinitCallback(csound, p, destroy_cudadsyn); - p->count = 0; - return OK; -} - -__global__ void sample(float *out, float *frame, float pitch, int64_t *ph, - float *amps, int bins, int vsize, float sr) { - - int t = (threadIdx.x + blockIdx.x*blockDim.x); - int n = t%vsize; /* sample index */ - int h = t/vsize; /* bin index */ - int k = h<<1; - int64_t lph; - float a = amps[h], ascl = ((float)n)/vsize; - float fscal = pitch*FMAXLEN/sr; - lph = (ph[h] + (int64_t)(n*round(frame[k+1]*fscal))) & PHMASK; - a += ascl*(frame[k] - a); - out[t] = a*sinf((2*PI*lph)/FMAXLEN); - if(t >= vsize) return; - __syncthreads(); - for(int i=vsize; i < vsize*bins; i+=vsize) - out[t] += out[t + i]; -} - -__global__ void update(float *frame, float *amps, - int64_t *ph,float pitch, int vsize, float sr){ - - int h = threadIdx.x + blockIdx.x*blockDim.x; - int k = h << 1; - /* update phases and amps */ - ph[h] = (ph[h] + (int64_t)(vsize*round(pitch*frame[k+1]*FMAXLEN/sr))) & PHMASK; - amps[h] = frame[k]; -} - -static int perf_cudadsyn(CSOUND *csound, CUDADSYN *p){ - - uint32_t offset = p->h.insdshead->ksmps_offset; - uint32_t early = p->h.insdshead->ksmps_no_end; - uint32_t n, nsmps = CS_KSMPS; - float *out_ = (float *) p->out_.auxp; - MYFLT *asig = p->asig; - int count = p->count, vsamps = p->vsamps; - p->fp = (float *) (p->fsig->frame.auxp); - - if (UNLIKELY(offset)) memset(asig, '\0', offset*sizeof(MYFLT)); - if (UNLIKELY(early)) { - nsmps -= early; - memset(&asig[nsmps], '\0', early*sizeof(MYFLT)); - } - - for(n=offset; n < nsmps; n++){ - if(count == 0) { - cudaMemset(p->out, 0, sizeof(float)*vsamps); - cudaMemcpy(p->frame,p->fp,sizeof(float)*p->bins*2,cudaMemcpyHostToDevice); - sample<<blocks,p->threads>>>(p->out,p->frame, - *p->kfreq, - p->ndx, - p->previous, - p->bins, - vsamps, - csound->GetSr(csound)); - if (cudaDeviceSynchronize() != cudaSuccess) - csound->Message(csound,"Cuda error: Failed to synchronize\n"); - update<<mblocks,p->mthreads>>>(p->frame, - p->previous, - p->ndx, - *p->kfreq, - vsamps, - csound->GetSr(csound)); - cudaMemcpy(out_,p->out,vsamps*sizeof(float),cudaMemcpyDeviceToHost); - count = vsamps; - } - asig[n] = (MYFLT) out_[vsamps - count]; - count--; - } - p->count = count; - return OK; -} - -static int destroy_cudadsyn(CSOUND *csound, void *pp){ - CUDADSYN *p = (CUDADSYN *) pp; - cudaFree(p->out); - cudaFree(p->ndx); - cudaFree(p->previous); - cudaFree(p->frame); - return OK; -} - - -static OENTRY localops[] = { - {"cudasynth", sizeof(CUDADSYN),0, 5, "a", "fkko", (SUBR) init_cudadsyn, NULL, - (SUBR) perf_cudadsyn} -}; - -extern "C" { - LINKAGE -} diff --git a/6.x/src/CUDA/build.sh b/6.x/src/CUDA/build.sh deleted file mode 100644 index 52611c3e..00000000 --- a/6.x/src/CUDA/build.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh -# use -Xptxas="-v" to check register usage and --maxrregcount 32 to limit it -echo "building cuda opcodes ..." -nvcc -O3 -shared -o libcudaop1.dylib adsyn.cu -use_fast_math -I../../debug/CsoundLib64.framework/Headers -arch=sm_30 -I/usr/local/cuda/include -L/usr/local/cuda/lib -nvcc -O3 -shared -o libcudaop2.dylib pvsops.cu -use_fast_math -g -I../../debug/CsoundLib64.framework/Headers -arch=sm_30 -I/usr/local/cuda/include -L/usr/local/cuda/lib -lcufft -nvcc -O3 -shared -o libcudaop3.dylib slidingm.cu -use_fast_math -I../../debug/CsoundLib64.framework/Headers -arch=sm_30 -I/usr/local/cuda/include -L/usr/local/cuda/lib -nvcc -O3 -shared -o libcudaop4.dylib conv.cu -I../../debug/CsoundLib64.framework/Headers -arch=sm_30 -I/usr/local/cuda/include -L/usr/local/cuda/lib -nvcc -O3 -shared -o libcudaop5.dylib pconv.cu -I../../debug/CsoundLib64.framework/Headers -arch=sm_30 -I/usr/local/cuda/include -L/usr/local/cuda/lib -lcufft -echo "...done" diff --git a/6.x/src/CUDA/convf.cu b/6.x/src/CUDA/convf.cu deleted file mode 100644 index 387fafb4..00000000 --- a/6.x/src/CUDA/convf.cu +++ /dev/null @@ -1,141 +0,0 @@ -// -*- c++ -*- -/* convf.cu - (c) Victor Lazzarini, 2013 - - based on M Puckette's pitch tracking algorithm. - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#include - -__global__ void convol(float *out, float *del, float *coefs, int irsize, int rp, int vsize) { - int t = (threadIdx.x + blockIdx.x*blockDim.x); - if(t >= irsize*vsize) return; - int n = t%vsize; /* sample index */ - int h = t/vsize; /* coeff index */ - int end = irsize+vsize; - rp += n + h; /* read point, oldest -> newest */ - out[t] = del[rp < end ? rp : rp%end]*coefs[irsize-1-h]; /* single tap */ - if(t >= vsize) return; - __syncthreads(); - float a = 0.0; - for(int i=1, j=vsize; i < irsize; i++, j+=vsize) - a += out[n + j]; /* mix all taps */ - out[n] += a; -} - -typedef struct _CONV { - OPDS h; - MYFLT *aout, *asig, *ifn; - float *coeffs, *out, *del; - int wp, irsize; - AUXCH buf; - int blocks, threads; -} CONV; - - -static int destroy_conv(CSOUND *csound, void *pp){ - CONV *p = (CONV *) pp; - cudaFree(p->coeffs); - cudaFree(p->del); - cudaFree(p->out); - return OK; -} - -static int conv_init(CSOUND *csound, CONV *p){ - - FUNC *ftab = csound->FTnp2Find(csound, p->ifn); - int irsize = ftab->flen; - int nsmps = CS_KSMPS,i; - int threads = irsize*nsmps; - float *tmp; - - cudaMalloc(&p->coeffs, sizeof(float)*irsize); - - tmp = (float*) malloc(sizeof(float)*irsize); - for(i=0; i< irsize; i++) - tmp[i] = (float) ftab->ftable[i]; - cudaMemcpy(p->coeffs,tmp, sizeof(float)*irsize, - cudaMemcpyHostToDevice); - free(tmp); - - cudaMalloc(&p->del, sizeof(float)*(irsize+nsmps)); - cudaMalloc(&p->out, sizeof(float)*threads); - cudaMemset(p->del,0,sizeof(float)*(irsize+nsmps)); - cudaMemset(p->out, 0, sizeof(float)*threads); - - p->wp = 0; - p->irsize = irsize; - - cudaDeviceProp deviceProp; - cudaGetDeviceProperties(&deviceProp, 0); - int blockspt = deviceProp.maxThreadsPerBlock; - csound->Message(csound, "CUDAconv: using device %s (capability %d.%d)\n", - deviceProp.name,deviceProp.major, deviceProp.minor); - - p->blocks = threads > blockspt ? ceil(threads/blockspt) : 1; - p->threads = threads > blockspt ? blockspt : threads; - - csound->RegisterDeinitCallback(csound, p, destroy_conv); - OPARMS parms; - csound->GetOParms(csound, &parms); - if(parms.odebug) - csound->Message(csound, "blocks %d, threads %d - %d\n", p->blocks, p->threads, threads); - if(p->buf.auxp == NULL) - csound->AuxAlloc(csound, sizeof(float)*CS_KSMPS, &p->buf); - - return OK; - -} -/* the delay size is irsize + vsize so that - we can shift in a whole block of samples */ -int conv_perf(CSOUND *csound, CONV *p){ - - int nsmps = CS_KSMPS; - MYFLT *sig = p->asig, *aout = p->aout; - float *del = p->del, *out = p->out, *coefs = p->coeffs, *buf = (float *)p->buf.auxp; - int irsize = p->irsize; - int wp = p->wp, i; - - for(i=0; i < nsmps; i++) buf[i] = (float) sig[i]; - if(wp > irsize) { - int front = wp - irsize; - cudaMemcpy(&del[wp], buf, sizeof(float)*(nsmps-front), cudaMemcpyHostToDevice); - cudaMemcpy(del, &buf[nsmps-front], sizeof(float)*front, cudaMemcpyHostToDevice); - } - else cudaMemcpy(&del[wp], buf, sizeof(float)*nsmps, cudaMemcpyHostToDevice); - - wp = (wp+nsmps)%(irsize+nsmps); /* wp is now the oldest sample in the delay */ - convol<<blocks,p->threads>>>(out, del, coefs, irsize, wp, nsmps); - - cudaMemcpy(buf, out, sizeof(float)*nsmps, cudaMemcpyDeviceToHost); - - for(i=0; i < nsmps; i++) aout[i] = (float) buf[i]; - p->wp = wp; - return OK; -} - -static OENTRY localops[] = { - {"cudaconv", sizeof(CONV),0, 5, "a", "ai", (SUBR) conv_init, NULL, - (SUBR) conv_perf}, -}; - -extern "C" { - LINKAGE -} diff --git a/6.x/src/CUDA/cuda_pvs_example.csd b/6.x/src/CUDA/cuda_pvs_example.csd deleted file mode 100644 index 9eeb7716..00000000 --- a/6.x/src/CUDA/cuda_pvs_example.csd +++ /dev/null @@ -1,40 +0,0 @@ - - -;--opcode-lib=./libcudaop2.dylib - - - -ksmps = 64 -0dbfs = 1 - -instr 1 -ifftsize = 4096 -ihopsize = 512 -asig = diskin2:a("flutec3.wav",1,0,1) -fsig = cudanal(asig, - ifftsize, - ihopsize, - ifftsize, 1) -asig = cudasynth(fsig) -asig = linenr(asig,0.005,0.01,0.01) - out(asig) -endin - -instr 2 -S1 = "flutec3.wav" -ifftsize = 32768 -ihopsize = 512 -asig diskin2 S1, 1, 0, 1 -fsig pvsanal asig, ifftsize, ihopsize, ifftsize, 1 -a1 pvsynth fsig -a2 linenr a1*0.5,0.005,0.01,0.01 - out a2 -endin - - - - -i1 0 60 - - - diff --git a/6.x/src/CUDA/cuda_sliding_example.csd b/6.x/src/CUDA/cuda_sliding_example.csd deleted file mode 100644 index 847e0e2b..00000000 --- a/6.x/src/CUDA/cuda_sliding_example.csd +++ /dev/null @@ -1,24 +0,0 @@ - - ---opcode-lib=./libcudaop3.dylib - - - -ksmps = 64 -0dbfs = 1 - -instr 1 -asig,adp diskin "metheny.wav",1,0,1 -amod = p4; oscil:a(2,3) -asig2 = cudasliding(asig,amod,p5) -asig = linenr(asig2,0.005,0.01,0.01) - out(asig*0.5) -endin - - - -i1 0 60 1 512 -;i1 0 5 0.5 1024 - - - diff --git a/6.x/src/CUDA/cudaconv.csd b/6.x/src/CUDA/cudaconv.csd deleted file mode 100644 index c81f48cf..00000000 --- a/6.x/src/CUDA/cudaconv.csd +++ /dev/null @@ -1,22 +0,0 @@ - - ---opcode-lib=libcudaop4.dylib - - -ksmps = 128 -i1 ftgen 1,0,1024,1,"/Users/victor/audio/church.wav",0,0,1 - -instr 1 - -asig diskin2 "/Users/victor/audio/cornetto.wav",1,01 -a1 cudaconv asig,1 -;a1 dconv asig,ftlen(1),1 - out a1/20 -endin - - - -i1 0 10 - - - diff --git a/6.x/src/CUDA/cudadsyn_pvs_example.csd b/6.x/src/CUDA/cudadsyn_pvs_example.csd deleted file mode 100644 index 00923131..00000000 --- a/6.x/src/CUDA/cudadsyn_pvs_example.csd +++ /dev/null @@ -1,39 +0,0 @@ - - ---opcode-lib=./libcudaop1.dylib --opcode-lib=./libcudaop2.dylib - - - -ksmps = 64 -0dbfs = 1 - -instr 1 -ifftsize = 2048 -ihopsize = 512 -ibins = 1024 -asig1,adp diskin "/users/victor/audio/metheny.wav",1,0,1 -fsig = pvsanal(asig1, ifftsize,ihopsize, ifftsize, 1) -asig = cudasynth(fsig,1,1,ibins) -asig = linenr(asig,0.001,0.01,0.01) - out(asig*0.5) - -endin - -instr 2 -ifftsize = 2048 -ihopsize = 512 -ibins = 1024 -asig1,adp diskin "/users/victor/audio/metheny.wav",1,0,1 -fsig = pvsanal(asig1, ifftsize,ihopsize, ifftsize, 1) -asig = pvsadsyn(fsig,ibins,1) -asig = linenr(asig,0.001,0.01,0.01) - out(asig*0.5) - -endin - - - -i1 0 60 - - - diff --git a/6.x/src/CUDA/cudapconv.csd b/6.x/src/CUDA/cudapconv.csd deleted file mode 100644 index be2e3372..00000000 --- a/6.x/src/CUDA/cudapconv.csd +++ /dev/null @@ -1,45 +0,0 @@ - - - - -ksmps = 8 -nchnls =2 -i1 ftgen 1,0,0,1,"church.wav",0,0,1 -i2 ftgen 2,0,64,1,"church.wav",0,0,1 -;i1 ftgen 1,0,65536,7,1,1,1,0,65534,0 -i1 ftgen 1,0,0,1,"/Users/victor/audio/metheny.wav",0,0,1 -instr 1 - -;asig oscili 0dbfs/4, 440; - -asig diskin2 "/Users/victor/audio/cornetto.wav",1,0,1 -a1 cudapconv asig,1,16384 -;a2 cudaconv asig,2 -;a1 ftconv asig,1,64 -;a1 pconvolve asig,"/Users/victor/audio/church_00.wav",1024 - out a1/500, a1/500 -endin - - - -i1 0 60 - - - - - - - 100 - 100 - 320 - 240 - true - - - 255 - 255 - 255 - - - - diff --git a/6.x/src/CUDA/pconv11.cu b/6.x/src/CUDA/pconv11.cu deleted file mode 100644 index 7035ff4a..00000000 --- a/6.x/src/CUDA/pconv11.cu +++ /dev/null @@ -1,266 +0,0 @@ -// -*- c++ -*- -/* pconv.cu - experimental cuda opcodes - (c) Victor Lazzarini, 2013 - - based on M Puckette's pitch tracking algorithm. - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#include -#include - -/* - each kernel processes one bin -*/ -__global__ void pconvol(float *out,float *in, - float *coef,int rp, int dftsize, - int nparts, int end) { - float re,im,re2,im2; - - /* thread count */ - int t = (threadIdx.x + blockIdx.x*blockDim.x); - - int k = t<<1; /* coef pos */ - int n = k%(dftsize+2); /* inframe pos */ - - /* if beyond the buffer end, exit */ - if(k >= end) return; - rp += k/(dftsize+2); /* rp pos */ - - /* select correct input buffer */ - in += (rp < nparts ? rp : rp%nparts)*(dftsize+2); - - re = coef[k]; im = coef[k+1]; - re2 = in[n]; im2 = in[n+1]; - - /* complex multiplication + sums */ - out[k] = re*re2 - im*im2; - out[k+1] = re*im2 + re2*im; - - if(t > dftsize+1) return; - __syncthreads(); - for(int i=dftsize+2; i < end; i+=(dftsize+2)) - out[t] += out[t + i]; - - -} - -/* sample-by-sample overlap-save operation */ -__global__ void olapsave(float *buf, float *in, int parts){ - int n = (threadIdx.x + blockIdx.x*blockDim.x); - buf[n] = in[n] + buf[parts+n]; - buf[parts+n] = in[parts+n]; -} - - - -typedef struct _pconv{ - OPDS h; - MYFLT *aout, *asig, *ifn, *parts; - float *out, *coef, *in, *buf; - AUXCH bufin, bufout; - int wp, nparts, dftsize, cnt; - cufftHandle plan, iplan; - int threads, blocks, othreads, oblocks; -} PCONV; - - -int isPowerOfTwo (unsigned int x) -{ - return ((x != 0) && !(x & (x - 1))); -} - - -static int destroy_pconv(CSOUND *csound, void *pp){ - PCONV *p = (PCONV *) pp; - cufftDestroy(p->plan); - cufftDestroy(p->iplan); - cudaFree(p->coef); - cudaFree(p->in); - cudaFree(p->out); - cudaFree(p->buf); - return OK; -} - - -int pconv_init(CSOUND *csound, PCONV *p){ - - FUNC *ftab = csound->FTnp2Find(csound, p->ifn); - float *tmp; - int tlen = ftab->flen; - int end, i, j, k, parts = *p->parts, dftsize, nparts; - MYFLT *tab = ftab->ftable; - - if(!isPowerOfTwo(parts)) - return csound->InitError(csound, "partition size needs to be power of two\n"); - - if(parts > tlen) - return csound->InitError(csound, "partition size too big \n"); - - end = tlen + parts - 1; - - nparts = end / parts; - dftsize = parts << 1; - end = nparts*(dftsize+2); - - cudaMalloc(&p->coef, sizeof(float)*end); - cudaMalloc(&p->in, sizeof(float)*end); - cudaMalloc(&p->out, sizeof(float)*end); - cudaMalloc(&p->buf, sizeof(float)*(dftsize)); - - cudaMemset(p->in,0,sizeof(float)*end); - cudaMemset(p->out, 0, sizeof(float)*end); - cudaMemset(p->buf, 0, sizeof(float)*(dftsize)); - cudaMemset(p->coef, 0, sizeof(float)*end); - - p->wp = 0; - - if(!p->bufin.auxp || p->bufin.size < sizeof(float)*dftsize) - csound->AuxAlloc(csound, sizeof(float)*dftsize, &p->bufin); - if(!p->bufout.auxp || p->bufout.size < sizeof(float)*parts) - csound->AuxAlloc(csound, sizeof(float)*parts, &p->bufout); - - memset(p->bufout.auxp, 0, sizeof(float)*parts); - - cufftResult res; - - tmp = (float *) p->bufin.auxp; - if((res = cufftPlan1d(&p->plan, dftsize, CUFFT_R2C, 1)) - != CUFFT_SUCCESS) csound->Message(csound, "cuda fft setup error %d\n", res); - cufftSetCompatibilityMode(p->plan, CUFFT_COMPATIBILITY_NATIVE); - cufftPlan1d(&p->iplan, dftsize, CUFFT_C2R, 1); - cufftSetCompatibilityMode(p->iplan, CUFFT_COMPATIBILITY_NATIVE); - - - for(i =0, k=0; i < nparts; i++){ - for(j=0; j < dftsize; j++) - tmp[j] = j < parts && k < tlen ? tab[k++] : 0.f; - float *pp = p->coef + (nparts - 1 - i)*(dftsize+2); - cudaMemcpy(pp, tmp, sizeof(float)*dftsize, - cudaMemcpyHostToDevice); - if((res = cufftExecR2C(p->plan,pp,(cufftComplex*)pp)) - != CUFFT_SUCCESS) csound->Message(csound, "cuda fft error %d\n", res); - } - - cudaDeviceSynchronize(); - cudaDeviceProp deviceProp; - cudaGetDeviceProperties(&deviceProp, 0); - int blockspt = deviceProp.maxThreadsPerBlock; - - end >>= 1; - - p->blocks = end > blockspt ? ceil(end/blockspt) : 1; - p->threads = end > blockspt ? blockspt : end; - p->oblocks = parts > blockspt ? ceil(parts/blockspt) : 1; - p->othreads = parts > blockspt ? blockspt : parts; - - csound->RegisterDeinitCallback(csound, p, destroy_pconv); - - OPARMS parms; - csound->GetOParms(csound, &parms); - if(parms.odebug) - csound->Message(csound, - "blocks %d - threads/block %d - threads %d - dftsize %d - parts %d\n", - p->blocks, p->threads, end, dftsize, nparts); - - p->nparts = nparts; - p->dftsize = dftsize; - p->cnt = 0; - - return OK; -} - -int pconv_perf(CSOUND *csound, PCONV *p){ - - int dftsize = p->dftsize, cnt = p->cnt, wp = p->wp, nparts = p->nparts; - uint32_t offset = p->h.insdshead->ksmps_offset; - uint32_t early = p->h.insdshead->ksmps_no_end; - uint32_t n, nsmps = CS_KSMPS; - float *bufin = (float *) p->bufin.auxp, *bufout = (float *) p->bufout.auxp; - MYFLT *asig = p->asig, *aout = p->aout; - float *in = p->in, *out = p->out, *coef = p->coef, *buf = p->buf; - int end = nparts*(dftsize+2); - int parts = *p->parts; - - if (UNLIKELY(offset)) memset(asig, '\0', offset*sizeof(MYFLT)); - if (UNLIKELY(early)) { - nsmps -= early; - memset(&asig[nsmps], '\0', early*sizeof(MYFLT)); - } - - for(n = offset; n < nsmps; n++){ - bufin[cnt] = (float) asig[n]; - aout[n] = (MYFLT) bufout[cnt]/dftsize; - - if(++cnt == parts){ - /* in buffer pos */ - int pos = wp*(dftsize+2); - - /* increment delay line pos - so that it points to the oldest partition - */ - wp += 1; - if(wp == nparts) wp = 0; - - /* copy current buffer into newest partition */ - cudaMemset(out, 0, sizeof(float)*(dftsize+2)); - cudaMemcpy(&in[pos],bufin,sizeof(float)*dftsize,cudaMemcpyHostToDevice); - - /* apply transform */ - if(cufftExecR2C(p->plan,&in[pos],(cufftComplex*)&in[pos]) - != CUFFT_SUCCESS) csound->Message(csound, "cuda in fft error\n"); - if (cudaDeviceSynchronize() != cudaSuccess) - csound->Message(csound,"Cuda error: Failed to synchronize\n"); - - /* convolution */ - pconvol<<blocks,p->threads>>>(out, in, coef, wp, dftsize, nparts, end); - - if (cudaDeviceSynchronize() != cudaSuccess) - csound->Message(csound,"Cuda error: Failed to synchronize\n"); - - /* transform output */ - if(cufftExecC2R(p->iplan,(cufftComplex*)out,out) - != CUFFT_SUCCESS) csound->Message(csound, "cuda out fft error\n"); - - if (cudaDeviceSynchronize() != cudaSuccess) - csound->Message(csound,"Cuda error: Failed to synchronize\n"); - - /* overlap-save */ - olapsave<<oblocks,p->othreads>>>(buf,out,parts); - - /* copy buffer out */ - cudaMemcpy(bufout,buf, sizeof(float)*parts,cudaMemcpyDeviceToHost); - - cnt = 0; - } - } - p->cnt = cnt; - p->wp = wp; - return OK; -} - -static OENTRY localops[] = { - {"cudapconv", sizeof(PCONV),0, 5, "a", "aii", (SUBR) pconv_init, NULL, - (SUBR) pconv_perf}, -}; - -extern "C" { - LINKAGE -} diff --git a/6.x/src/CUDA/pvsops2-build.sh b/6.x/src/CUDA/pvsops2-build.sh deleted file mode 100644 index a762cf77..00000000 --- a/6.x/src/CUDA/pvsops2-build.sh +++ /dev/null @@ -1,5 +0,0 @@ -#!/bin/sh -# use -Xptxas="-v" to check register usage and --maxrregcount 32 to limit it -echo "building cuda opcodes ..." -nvcc -O3 -shared -o libcudapvs2.dylib pvsops2.cu -use_fast_math -g -I../../debug/CsoundLib64.framework/Headers -arch=sm_30 -I/usr/local/cuda/include -L/usr/local/cuda/lib -lcufft -echo "...done" diff --git a/6.x/src/CUDA/pvsops2.cu b/6.x/src/CUDA/pvsops2.cu deleted file mode 100644 index 4e1eb2ef..00000000 --- a/6.x/src/CUDA/pvsops2.cu +++ /dev/null @@ -1,530 +0,0 @@ -// -*- c++ -*- -/* pvsops.cu - experimental cuda opcodes - - (c) Victor Lazzarini, 2013 - - based on M Puckette's pitch tracking algorithm. - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#include -#include - -#include - -void AuxCudaAlloc(int size, AUXCH *p){ - float *mem; - cudaMalloc(&mem, size); - cudaMemset(mem, 0, size); - p->auxp = mem; - p->size = size; -} - - -/* kernel to convert from pvs to rectangular frame */ -__global__ void frompvs(float* inframe, float* fsig, double* lastph, - double scal, double fac) { - - int k = threadIdx.x + blockIdx.x*blockDim.x; - int i = k << 1; - float mag = fsig[i]; - double delta = (fsig[i+1] - k*scal)*fac; - double phi = fmod(lastph[k] + delta, TWOPI); - lastph[k] = phi; - inframe[i] = (float) (mag*cos(phi)); - inframe[i+1] = (float) (mag*sin(phi)); -} - -__global__ void winrotate(float* inframe2, float* inframe, float *win, - int N, int offset){ - int k = (threadIdx.x + blockIdx.x*blockDim.x); - inframe2[k] = win[k]*inframe[(k+offset)%N]; -} - -typedef struct _pvsyn{ - OPDS h; - MYFLT *asig; - PVSDAT *fsig; - float *inframe; /* N */ - float *inframe2; - double *lastph; /* N/2 */ - float *win; /* N */ - int framecount; - int curframe; - AUXCH frames; - AUXCH count; - cufftHandle plan; - double scal, fac; - int bblocks, nblocks; - int bthreads, nthreads; -} PVSYN; - -static int destroy_pvsyn(CSOUND *csound, void *pp); - -static int pvsynset(CSOUND *csound, PVSYN *p){ - - int N = p->fsig->N; - - if((N != 0) && !(N & (N - 1))) { - int hsize = p->fsig->overlap; - int size, numframes, i, blockspt; - MYFLT sum = 0.0, bins = N/2; - float *win; - cudaDeviceProp deviceProp; - cudaGetDeviceProperties(&deviceProp, 0); - blockspt = deviceProp.maxThreadsPerBlock; - csound->Message(csound, "CUDAsynth: using device %s (capability %d.%d)\n", - deviceProp.name,deviceProp.major, deviceProp.minor); - - if(p->fsig->wintype != 1) - return csound->InitError(csound, - "window type not implemented yet\n"); - numframes = N/hsize; - size = N*sizeof(float)*numframes; - if(p->frames.auxp == NULL || - p->frames.size < size) - csound->AuxAlloc(csound, size, &p->frames); - memset(p->frames.auxp, 0, size); - - size = sizeof(int)*numframes; - if(p->count.auxp == NULL || - p->count.size < size) - csound->AuxAlloc(csound, size, &p->count); - *((int *)(p->count.auxp)) = 0; - for(i=1; i < numframes; i++) - ((int *)(p->count.auxp))[i] = - (i + (1.f - (float)i/numframes))*N; - - size = (N+2)*sizeof(float); - cudaMalloc(&p->inframe, size); - cudaMemset(p->inframe, 0, size); - size = (N+2)*sizeof(float); - cudaMalloc(&p->inframe2, size); - size = (N/2)*sizeof(double); - cudaMalloc(&p->lastph, size); - cudaMemset(p->lastph, 0, size); - size = N*sizeof(float); - cudaMalloc(&p->win, size); - - win = (float *) malloc(sizeof(float)*(N+1)); - for(i=0; i <= N; i++) - win[i] = (float) (0.5 - 0.5*cos(i*TWOPI/N)); - - for(i = 0; i < N; i++) sum += win[i]; - sum = FL(2.0) / sum; - for(i = 0; i < N; i++) win[i] *= sum; - sum = FL(0.0); - for(i = 0; i <= N; i+=hsize) - sum += win[i] * win[i]; - sum = (1.0/N)/(sum); - for(i = 0; i < N; i++) win[i] *= 3*sum/sqrt(numframes); - cudaMemcpy(p->win,win,N*sizeof(float), - cudaMemcpyHostToDevice); - free(win); - - p->framecount = 0; - p->curframe = 0; - - p->fac = TWOPI*hsize/csound->GetSr(csound); - p->scal =csound->GetSr(csound)/N; - cufftPlan1d(&p->plan, N, CUFFT_C2R, 1); - cufftSetCompatibilityMode(p->plan, CUFFT_COMPATIBILITY_NATIVE); - csound->RegisterDeinitCallback(csound, p, destroy_pvsyn); - - p->bblocks = bins > blockspt? bins/blockspt : 1; - p->nblocks = N > blockspt ? N/blockspt : 1; - p->bthreads = bins/p->bblocks; - p->nthreads = N/p->nblocks; - if(csound->GetDebug(csound)) - csound->Message(csound, "%d (%d each), %d (%d each)\n", - p->nblocks, p->nthreads, p->bblocks, p->bthreads); - return OK; - } - return csound->InitError(csound, "fftsize not power-of-two \n"); - -} - -static int pvsynperf(CSOUND *csound, PVSYN *p){ - - int N = p->fsig->N, i; - int hsize = p->fsig->overlap; - uint32_t offset = p->h.insdshead->ksmps_offset; - uint32_t early = p->h.insdshead->ksmps_no_end; - uint32_t n, nsmps = CS_KSMPS; - MYFLT *asig = p->asig; - float *frames = (float *) p->frames.auxp; - int framecount = p->framecount; - int numframes = N/hsize; - int *count = (int *) p->count.auxp; - - if (UNLIKELY(offset)) memset(asig, '\0', offset*sizeof(MYFLT)); - if (UNLIKELY(early)) { - nsmps -= early; - memset(&asig[nsmps], '\0', early*sizeof(MYFLT)); - } - - for(n=offset; n < nsmps; n++) { - - if(framecount == 0) { - int curframe = p->curframe; - /* start offset for current frame */ - int start = N*curframe; - float *cur = &(frames[start]); - float *win = (float *) p->win; - float *inframe = p->inframe; - float *inframe2 = p->inframe2; - float *fsig = (float *) p->fsig->frame.auxp; - /* perf pvs to rect conversion */ - frompvs<<bblocks,p->bthreads>>>(inframe,fsig,p->lastph,p->scal,p->fac); - /* execute inverse real FFT */ - if(cufftExecC2R(p->plan,(cufftComplex*)inframe,inframe) - != CUFFT_SUCCESS) csound->Message(csound, "cuda fft error\n"); - if (cudaDeviceSynchronize() != cudaSuccess) - csound->Message(csound,"Cuda error: Failed to synchronize\n"); - /* window and rotate data on device */ - winrotate<<nblocks,p->nthreads>>>(inframe2,inframe,win,N,hsize*curframe); - /* copy data to current out frame */ - cudaMemcpy(cur,inframe2,N*sizeof(float),cudaMemcpyDeviceToHost); - /* reset counter for this frame to the start */ - count[curframe] = start; - /* move current to next frame circularly */ - p->curframe = ++(curframe) == numframes ? 0 : curframe; - framecount = hsize; - } - asig[n] = FL(0.0); - for(i=0; i < numframes; i++){ - /* overlap-add */ - asig[n] += frames[count[i]]; - count[i]++; - } - framecount--; - } - p->framecount = framecount; - return OK; -} - -static int destroy_pvsyn(CSOUND *csound, void *pp){ - PVSYN *p = (PVSYN *) pp; - cufftDestroy(p->plan); - cudaFree(p->inframe); - cudaFree(p->inframe2); - cudaFree(p->lastph); - cudaFree(p->win); - return OK; -} - -__device__ double modTwoPi(double x) -{ - x = fmod(x,TWOPI); - return x <= -PI ? x + TWOPI : - (x > PI ? x - TWOPI : x); -} - -/* kernel to convert from rectangular to pvs frame */ -__global__ void topvs(float *fsig, float *aframe, double* oldph, - double scal, double fac) { - int k = threadIdx.x + blockIdx.x*blockDim.x; - int i = k << 1; - float re = aframe[i], im = aframe[i+1]; - float mag = sqrtf(re*re + im*im); - double phi = atan2f(im,re); - double delta = phi - oldph[k]; - oldph[k] = phi; - fsig[i] = mag; - fsig[i+1] = (float) ((modTwoPi(delta) + k*scal)*fac); -} - -__global__ void rotatewin(float* aframe2, float *aframe, float *win, - int N, int offset){ - int k = threadIdx.x + blockIdx.x*blockDim.x; - aframe2[(k+offset)%N] = win[k]*aframe[k]; -} - -typedef struct _pvan { - OPDS h; - PVSDAT *fsig; - MYFLT *asig,*fftsize,*hsize,*winsize,*wintype; - - float *aframe; /* N */ - float *aframe2; - double *oldph; /* N/2 */ - float *win; /* N */ - - int framecount; - int curframe; - AUXCH frames; - AUXCH count; - cufftHandle plan; - double scal, fac; - int bblocks, nblocks; - int bthreads, nthreads; -} PVAN; - -static int destroy_pvanal(CSOUND *csound, void *pp); - -static int pvanalset(CSOUND *csound, PVAN *p){ - - int N = *p->fftsize; - if((N != 0) && !(N & (N - 1))) { - int size, numframes, i, bins = N/2; - int hsize = *p->hsize, blockspt; - float *win; - cudaDeviceProp deviceProp; - cudaGetDeviceProperties(&deviceProp, 0); - blockspt = deviceProp.maxThreadsPerBlock; - csound->Message(csound, "CUDAnal: using device %s (capability %d.%d)\n", - deviceProp.name,deviceProp.major, deviceProp.minor); - - p->fsig->N = N; - p->fsig->overlap = hsize; - p->fsig->format = -1; - /* ignore winsize & wintype */ - p->fsig->winsize = N; - p->fsig->wintype = 1; - p->fsig->framecount = 0; - - numframes = N/hsize; - size = N*sizeof(float)*numframes; - if(p->frames.auxp == NULL || - p->frames.size < size) - csound->AuxAlloc(csound, size, &p->frames); - memset(p->frames.auxp, 0, size); - - size = (N+2)*sizeof(float); - if(p->fsig->frame.auxp == NULL || - p->fsig->frame.size < size) - AuxCudaAlloc(size, &p->fsig->frame); - - size = sizeof(int)*numframes; - if(p->count.auxp == NULL || - p->count.size < size) - csound->AuxAlloc(csound, size, &p->count); - *((int *)(p->count.auxp)) = 0; - for(i=1; i < numframes; i++) - ((int *)(p->count.auxp))[i] = - (i + (float)i/numframes)*N; - - size = (N+2)*sizeof(float); - cudaMalloc(&p->aframe, size); - size = (N+2)*sizeof(float); - cudaMalloc(&p->aframe2, size); - size = (N/2)*sizeof(double); - cudaMalloc(&p->oldph, size); - cudaMemset(p->oldph, 0, size); - size = N*sizeof(float); - cudaMalloc(&p->win, size); - - - win = (float *) malloc(sizeof(float)*N); - for(i=0; i < N; i++) - win[i] = (float) (0.5 - 0.5*cos(i*TWOPI/N)); - float sum = 0.0; - for(i = 0; i < N; i++) sum += win[i]; - sum = FL(2.0) / sum; - for(i = 0; i < N; i++) win[i] *= sum; - - cudaMemcpy(p->win,win,N*sizeof(float), - cudaMemcpyHostToDevice); - free(win); - - p->framecount = 1; - p->curframe = numframes-1; - p->fac = csound->GetSr(csound)/(TWOPI*hsize); - p->scal = (TWOPI*hsize)/N; - cufftPlan1d(&p->plan, N, CUFFT_R2C, 1); - cufftSetCompatibilityMode(p->plan, CUFFT_COMPATIBILITY_NATIVE); - csound->RegisterDeinitCallback(csound, p, destroy_pvanal); - - p->bblocks = bins > blockspt? bins/blockspt : 1; - p->nblocks = N > blockspt ? N/blockspt : 1; - p->bthreads = bins/p->bblocks; - p->nthreads = N/p->nblocks; - if(csound->GetDebug(csound)) - csound->Message(csound, "%d (%d each), %d (%d each)\n", - p->nblocks, p->nthreads, p->bblocks, p->bthreads); - return OK; - } - return csound->InitError(csound, "fftsize not power-of-two \n"); -} - -static int pvanalperf(CSOUND *csound, PVAN *p){ - - int N = p->fsig->N, i; - int hsize = p->fsig->overlap; - uint32_t offset = p->h.insdshead->ksmps_offset; - uint32_t early = p->h.insdshead->ksmps_no_end; - uint32_t n, nsmps = CS_KSMPS; - MYFLT *asig = p->asig; - float *frames = (float *) p->frames.auxp; - int framecount = p->framecount; - int numframes = N/hsize; - int *count = (int *) p->count.auxp; - - if (UNLIKELY(offset)) memset(asig, '\0', offset*sizeof(MYFLT)); - if (UNLIKELY(early)) { - nsmps -= early; - memset(&asig[nsmps], '\0', early*sizeof(MYFLT)); - } - - for(n=offset; n < nsmps; n++) { - for(i=0; i < numframes; i++){ - frames[count[i]] = asig[n]; - count[i]++; - } - framecount++; - - if(framecount == hsize) { - int curframe = p->curframe; - /* start offset for current frame */ - int start = N*curframe; - float *cur = &(frames[start]); - float *win = (float *) p->win; - float *aframe = p->aframe; - float *aframe2 = p->aframe2; - float *fsig = (float *) p->fsig->frame.auxp; - cudaMemcpy(aframe,cur,N*sizeof(float), - cudaMemcpyHostToDevice); - /* window and rotate data on device */ - rotatewin<<nblocks,p->nthreads>>>(aframe2,aframe,win,N, - hsize*(numframes-curframe)); - /* execute inverse real FFT */ - if(cufftExecR2C(p->plan,aframe2,(cufftComplex*)aframe2) - != CUFFT_SUCCESS) csound->Message(csound, "cuda fft error\n"); - if (cudaDeviceSynchronize() != cudaSuccess) - csound->Message(csound,"Cuda error: Failed to synchronize\n"); - /* perf rect to pvs conversion */ - topvs<<bblocks,p->bthreads>>>(fsig,aframe2,p->oldph,p->scal,p->fac); - /* reset counter for this frame to the start */ - count[curframe] = start; - /* move current to next frame circularly */ - p->curframe = --(curframe) < 0 ? numframes-1 : curframe; - framecount = 0; - p->fsig->framecount++; - } - } - p->framecount = framecount; - return OK; -} - -static int destroy_pvanal(CSOUND *csound, void *pp){ - PVAN *p = (PVAN *) pp; - cufftDestroy(p->plan); - cudaFree(p->aframe); - cudaFree(p->aframe2); - cudaFree(p->oldph); - cudaFree(p->fsig->frame.auxp); - p->fsig->frame.size = 0; - cudaFree(p->win); - return OK; -} - -typedef struct _cudapvsgain2 { - OPDS h; - PVSDAT *fout; - PVSDAT *fa; - MYFLT *kgain; - int gridSize; // number of blocks in the grid (1D) - int blockSize; // number of threads in one block (1D) - uint32 lastframe; -} CUDAPVSGAIN2; - -// kernel for scaling PV amplitudes -__global__ void applygain(float* output, float* input, MYFLT g, int length) { - int i = threadIdx.x + blockDim.x * blockIdx.x; - int j = i<<1; - - if(j < length){ - output[j] = (float) input[j] * g; - output[j+1] = input[j+1]; - } -} - -static int free_device(CSOUND* csound, void* pp){ - CUDAPVSGAIN2* p = (CUDAPVSGAIN2*) pp; - cudaFree(p->fout->frame.auxp); - return OK; -} - -static int cudapvsgain2set(CSOUND *csound, CUDAPVSGAIN2 *p){ - - int32 N = p->fa->N; - int size = (N+2) * sizeof(float); - int maxBlockDim; - int SMcount; - int totNumThreads = (N+2)/2; - cudaDeviceProp deviceProp; - cudaGetDeviceProperties(&deviceProp,0); - maxBlockDim = deviceProp.maxThreadsPerBlock; - SMcount = deviceProp.multiProcessorCount; - csound->Message(csound, "cudapvsgain2 running on device %s (capability %d.%d)\n", deviceProp.name, - deviceProp.major, deviceProp.minor); - - p->fout->sliding = 0; - - if (p->fout->frame.auxp == NULL || p->fout->frame.size < size) - AuxCudaAlloc(size, &p->fout->frame); - - p->blockSize = (((totNumThreads/SMcount)/32)+1)*32; - if (p->blockSize > maxBlockDim) p->blockSize = maxBlockDim; - p->gridSize = totNumThreads / p->blockSize + 1; - p->fout->N = N; - p->fout->overlap = p->fa->overlap; - p->fout->winsize = p->fa->winsize; - p->fout->wintype = p->fa->wintype; - p->fout->format = p->fa->format; - p->fout->framecount = 1; - p->lastframe = 0; - - csound->RegisterDeinitCallback(csound, p, free_device); - - return OK; -} - -static int cudapvsgain2(CSOUND *csound, CUDAPVSGAIN2 *p) -{ - int32 framelength = p->fa->N + 2; - MYFLT gain = *p->kgain; - float* fo = (float*) p->fout->frame.auxp; - float* fi = (float*) p->fa->frame.auxp; - - if (p->lastframe < p->fa->framecount) { - if (cudaDeviceSynchronize() != cudaSuccess) - csound->Message(csound,"Cuda error: Failed to synchronize\n"); - applygain<<gridSize,p->blockSize>>>(fo, fi, gain, framelength); - p->fout->framecount = p->fa->framecount; - p->lastframe = p->fout->framecount; - } - - return OK; -} - - -static OENTRY localops[] = { - {"cudasynth2", sizeof(PVSYN),0, 5, "a", "f", (SUBR) pvsynset, NULL, - (SUBR) pvsynperf}, - {"cudanal2", sizeof(PVAN),0, 5, "f", "aiiii", (SUBR) pvanalset, NULL, - (SUBR) pvanalperf}, - {"cudapvsgain2", sizeof(CUDAPVSGAIN2), 0, 3, "f", "fk", - (SUBR) cudapvsgain2set, (SUBR) cudapvsgain2, NULL} -}; - -extern "C" { - LINKAGE -} diff --git a/6.x/src/CUDA/pvsopsf.cu b/6.x/src/CUDA/pvsopsf.cu deleted file mode 100644 index da778197..00000000 --- a/6.x/src/CUDA/pvsopsf.cu +++ /dev/null @@ -1,445 +0,0 @@ -// -*- c++ -*- -/* pvsopsf.cu - experimental cuda opcodes - version using single-precision floats - (c) Victor Lazzarini, 2013 - - based on M Puckette's pitch tracking algorithm. - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#include -#include - -#include - -/* kernel to convert from pvs to rectangular frame */ -__global__ void frompvs(float* inframe, float* lastph, - float scal, float fac) { - - int k = threadIdx.x + blockIdx.x*blockDim.x + 1; - int i = k << 1; - float mag = inframe[i]; - float delta = (inframe[i+1] - k*scal)*fac; - float phi = fmod((double)lastph[k-1] + delta, TWOPI); - lastph[k-1] = phi; - inframe[i] = (float) (mag*cos(phi)); - inframe[i+1] = (float) (mag*sin(phi)); - -} - -__global__ void winrotate(float* inframe2, float* inframe, float *win, - int N, int offset){ - int k = (threadIdx.x + blockIdx.x*blockDim.x); - inframe2[k] = win[k]*inframe[(k+offset)%N]; -} - -typedef struct _pvsyn{ - OPDS h; - MYFLT *asig; - PVSDAT *fsig; - float *inframe; /* N */ - float *inframe2; - float *lastph; /* N/2 */ - float *win; /* N */ - int framecount; - int curframe; - AUXCH frames; - AUXCH count; - cufftHandle plan; - float scal, fac; - int bblocks, nblocks; - int bthreads, nthreads; -} PVSYN; - -static int destroy_pvsyn(CSOUND *csound, void *pp); - -static int pvsynset(CSOUND *csound, PVSYN *p){ - - int N = p->fsig->N; - - if((N != 0) && !(N & (N - 1))) { - int hsize = p->fsig->overlap; - int size, numframes, i, blockspt; - MYFLT sum = 0.0, bins = N/2; - float *win; - cudaDeviceProp deviceProp; - cudaGetDeviceProperties(&deviceProp, 0); - blockspt = deviceProp.maxThreadsPerBlock; - csound->Message(csound, "CUDAsynth: using device %s (capability %d.%d)\n", - deviceProp.name,deviceProp.major, deviceProp.minor); - - if(p->fsig->wintype != 1) - return csound->InitError(csound, - "window type not implemented yet\n"); - numframes = N/hsize; - size = N*sizeof(float)*numframes; - if(p->frames.auxp == NULL || - p->frames.size < size) - csound->AuxAlloc(csound, size, &p->frames); - memset(p->frames.auxp, 0, size); - - size = sizeof(int)*numframes; - if(p->count.auxp == NULL || - p->count.size < size) - csound->AuxAlloc(csound, size, &p->count); - *((int *)(p->count.auxp)) = 0; - for(i=1; i < numframes; i++) - ((int *)(p->count.auxp))[i] = - (i + (1.f - (float)i/numframes))*N; - - size = (N+2)*sizeof(float); - cudaMalloc(&p->inframe, size); - cudaMemset(p->inframe, 0, size); - size = (N+2)*sizeof(float); - cudaMalloc(&p->inframe2, size); - size = (N+2)*sizeof(float); - cudaMalloc(&p->lastph, size); - cudaMemset(p->lastph, 0, size); - size = N*sizeof(float); - cudaMalloc(&p->win, size); - - win = (float *) malloc(sizeof(float)*(N+1)); - for(i=0; i <= N; i++) - win[i] = (float) (0.5 - 0.5*cos(i*TWOPI/N)); - - for(i = 0; i < N; i++) sum += win[i]; - sum = FL(2.0) / sum; - for(i = 0; i < N; i++) win[i] *= sum; - sum = FL(0.0); - for(i = 0; i <= N; i+=hsize) - sum += win[i] * win[i]; - sum = (1.0/N)/(sum); - for(i = 0; i < N; i++) win[i] *= 3*sum/sqrt(numframes); - cudaMemcpy(p->win,win,N*sizeof(float), - cudaMemcpyHostToDevice); - free(win); - - p->framecount = 0; - p->curframe = 0; - - p->fac = TWOPI*hsize/csound->GetSr(csound); - p->scal =csound->GetSr(csound)/N; - cufftPlan1d(&p->plan, N, CUFFT_C2R, 1); - cufftSetCompatibilityMode(p->plan, CUFFT_COMPATIBILITY_NATIVE); - csound->RegisterDeinitCallback(csound, p, destroy_pvsyn); - - p->bblocks = bins > blockspt? bins/blockspt : 1; - p->nblocks = N > blockspt ? N/blockspt : 1; - p->bthreads = bins/p->bblocks; - p->nthreads = N/p->nblocks; - if(csound->GetDebug(csound)) - csound->Message(csound, "%d (%d each), %d (%d each)\n", - p->nblocks, p->nthreads, p->bblocks, p->bthreads); - return OK; - } - return csound->InitError(csound, "fftsize not power-of-two \n"); - -} - -static int pvsynperf(CSOUND *csound, PVSYN *p){ - - int N = p->fsig->N, i; - int hsize = p->fsig->overlap; - uint32_t offset = p->h.insdshead->ksmps_offset; - uint32_t early = p->h.insdshead->ksmps_no_end; - uint32_t n, nsmps = CS_KSMPS; - MYFLT *asig = p->asig; - float *frames = (float *) p->frames.auxp; - int framecount = p->framecount; - int numframes = N/hsize; - int *count = (int *) p->count.auxp; - - if (UNLIKELY(offset)) memset(asig, '\0', offset*sizeof(MYFLT)); - if (UNLIKELY(early)) { - nsmps -= early; - memset(&asig[nsmps], '\0', early*sizeof(MYFLT)); - } - - for(n=offset; n < nsmps; n++) { - - if(framecount == 0) { - int curframe = p->curframe; - /* start offset for current frame */ - int start = N*curframe; - float *cur = &(frames[start]); - float *win = (float *) p->win; - float *inframe = p->inframe; - float *inframe2 = p->inframe2; - float *fsig = (float *) p->fsig->frame.auxp; - /* copy fsig data to device */ - fsig[N+1] = fsig[1] = 0.f; - cudaMemcpy(inframe,fsig,(N+2)*sizeof(float), - cudaMemcpyHostToDevice); - /* perf pvs to rect conversion */ - frompvs<<bblocks,p->bthreads-1>>>(inframe,p->lastph,p->scal,p->fac); - /* execute inverse real FFT */ - if(cufftExecC2R(p->plan,(cufftComplex*)inframe,inframe) - != CUFFT_SUCCESS) csound->Message(csound, "cuda fft error\n"); - if (cudaDeviceSynchronize() != cudaSuccess) - csound->Message(csound,"Cuda error: Failed to synchronize\n"); - /* window and rotate data on device */ - winrotate<<nblocks,p->nthreads>>>(inframe2,inframe,win,N,hsize*curframe); - /* copy data to current out frame */ - cudaMemcpy(cur,inframe2,N*sizeof(float),cudaMemcpyDeviceToHost); - /* reset counter for this frame to the start */ - count[curframe] = start; - /* move current to next frame circularly */ - p->curframe = ++(curframe) == numframes ? 0 : curframe; - framecount = hsize; - } - asig[n] = FL(0.0); - for(i=0; i < numframes; i++){ - /* overlap-add */ - asig[n] += frames[count[i]]; - count[i]++; - } - framecount--; - } - p->framecount = framecount; - return OK; -} - -static int destroy_pvsyn(CSOUND *csound, void *pp){ - PVSYN *p = (PVSYN *) pp; - cufftDestroy(p->plan); - cudaFree(p->inframe); - cudaFree(p->lastph); - cudaFree(p->win); - return OK; -} - -__device__ float modTwoPi(float x) -{ - x = fmod((double)x,TWOPI); - return x <= -PI ? x + TWOPI : - (x > PI ? x - TWOPI : x); -} - -/* kernel to convert from rectangular to pvs frame */ -__global__ void topvs(float* aframe, float* oldph, - float scal, float fac) { - int k = threadIdx.x + blockIdx.x*blockDim.x + 1; - int i = k << 1; - - float re = aframe[i], im = aframe[i+1]; - float mag = sqrtf(re*re + im*im); - float phi = atan2f(im,re); - float delta = phi - oldph[k-1]; - oldph[k-1] = phi; - aframe[i] = mag; - aframe[i+1] = (float) ((modTwoPi(delta) + k*scal)*fac); -} - -__global__ void rotatewin(float* aframe2, float *aframe, float *win, - int N, int offset){ - int k = threadIdx.x + blockIdx.x*blockDim.x; - aframe2[(k+offset)%N] = win[k]*aframe[k]; -} - -typedef struct _pvan { - OPDS h; - PVSDAT *fsig; - MYFLT *asig,*fftsize,*hsize,*winsize,*wintype; - - float *aframe; /* N */ - float *aframe2; - float *oldph; /* N/2 */ - float *win; /* N */ - - int framecount; - int curframe; - AUXCH frames; - AUXCH count; - cufftHandle plan; - float scal, fac; - int bblocks, nblocks; - int bthreads, nthreads; -} PVAN; - -static int destroy_pvanal(CSOUND *csound, void *pp); - -static int pvanalset(CSOUND *csound, PVAN *p){ - - int N = *p->fftsize; - if((N != 0) && !(N & (N - 1))) { - int size, numframes, i, bins = N/2; - int hsize = *p->hsize, blockspt; - float *win; - cudaDeviceProp deviceProp; - cudaGetDeviceProperties(&deviceProp, 0); - blockspt = deviceProp.maxThreadsPerBlock; - csound->Message(csound, "CUDAnal: using device %s (capability %d.%d)\n", - deviceProp.name,deviceProp.major, deviceProp.minor); - - p->fsig->N = N; - p->fsig->overlap = hsize; - /* ignore winsize & wintype */ - p->fsig->winsize = N; - p->fsig->wintype = 1; - p->fsig->framecount = 0; - - numframes = N/hsize; - size = N*sizeof(float)*numframes; - if(p->frames.auxp == NULL || - p->frames.size < size) - csound->AuxAlloc(csound, size, &p->frames); - memset(p->frames.auxp, 0, size); - - size = N*sizeof(float); - if(p->fsig->frame.auxp == NULL || - p->fsig->frame.size < size) - csound->AuxAlloc(csound, size, &p->fsig->frame); - memset(p->fsig->frame.auxp, 0, size); - - size = sizeof(int)*numframes; - if(p->count.auxp == NULL || - p->count.size < size) - csound->AuxAlloc(csound, size, &p->count); - *((int *)(p->count.auxp)) = 0; - for(i=1; i < numframes; i++) - ((int *)(p->count.auxp))[i] = - (i + (float)i/numframes)*N; - - size = (N+2)*sizeof(float); - cudaMalloc(&p->aframe, size); - size = (N+2)*sizeof(float); - cudaMalloc(&p->aframe2, size); - size = (N/2-1)*sizeof(float); - cudaMalloc(&p->oldph, size); - cudaMemset(p->oldph, 0, size); - size = N*sizeof(float); - cudaMalloc(&p->win, size); - - win = (float *) malloc(sizeof(float)*N); - for(i=0; i < N; i++) - win[i] = (float) (0.5 - 0.5*cos(i*TWOPI/N)); - float sum = 0.0; - for(i = 0; i < N; i++) sum += win[i]; - sum = FL(2.0) / sum; - for(i = 0; i < N; i++) win[i] *= sum; - - cudaMemcpy(p->win,win,N*sizeof(float), - cudaMemcpyHostToDevice); - free(win); - - - - p->framecount = 1; - p->curframe = numframes-1; - p->fac = csound->GetSr(csound)/(TWOPI*hsize); - p->scal = (TWOPI*hsize)/N; - cufftPlan1d(&p->plan, N, CUFFT_R2C, 1); - cufftSetCompatibilityMode(p->plan, CUFFT_COMPATIBILITY_NATIVE); - csound->RegisterDeinitCallback(csound, p, destroy_pvanal); - - p->bblocks = bins > blockspt? bins/blockspt : 1; - p->nblocks = N > blockspt ? N/blockspt : 1; - p->bthreads = bins/p->bblocks; - p->nthreads = N/p->nblocks; - if(csound->GetDebug(csound)) - csound->Message(csound, "%d (%d each), %d (%d each)\n", - p->nblocks, p->nthreads, p->bblocks, p->bthreads); - return OK; - } - return csound->InitError(csound, "fftsize not power-of-two \n"); -} - -static int pvanalperf(CSOUND *csound, PVAN *p){ - - int N = p->fsig->N, i; - int hsize = p->fsig->overlap; - uint32_t offset = p->h.insdshead->ksmps_offset; - uint32_t early = p->h.insdshead->ksmps_no_end; - uint32_t n, nsmps = CS_KSMPS; - MYFLT *asig = p->asig; - float *frames = (float *) p->frames.auxp; - int framecount = p->framecount; - int numframes = N/hsize; - int *count = (int *) p->count.auxp; - - if (UNLIKELY(offset)) memset(asig, '\0', offset*sizeof(MYFLT)); - if (UNLIKELY(early)) { - nsmps -= early; - memset(&asig[nsmps], '\0', early*sizeof(MYFLT)); - } - - for(n=offset; n < nsmps; n++) { - for(i=0; i < numframes; i++){ - frames[count[i]] = asig[n]; - count[i]++; - } - framecount++; - - if(framecount == hsize) { - int curframe = p->curframe; - /* start offset for current frame */ - int start = N*curframe; - float *cur = &(frames[start]); - float *win = (float *) p->win; - float *aframe = p->aframe; - float *aframe2 = p->aframe2; - float *fsig = (float *) p->fsig->frame.auxp; - /* copy fsig data to device */ - cudaMemcpy(aframe,cur,N*sizeof(float), - cudaMemcpyHostToDevice); - /* window and rotate data on device */ - rotatewin<<nblocks,p->nthreads>>>(aframe2,aframe,win,N, - hsize*(numframes-curframe)); - /* execute inverse real FFT */ - if(cufftExecR2C(p->plan,aframe2,(cufftComplex*)aframe2) - != CUFFT_SUCCESS) csound->Message(csound, "cuda fft error\n"); - if (cudaDeviceSynchronize() != cudaSuccess) - csound->Message(csound,"Cuda error: Failed to synchronize\n"); - /* perf rect to pvs conversion */ - topvs<<bblocks,p->bthreads-1>>>(aframe2,p->oldph,p->scal,p->fac); - /* copy data to current out frame */ - cudaMemcpy(fsig,aframe2,(N+2)*sizeof(float),cudaMemcpyDeviceToHost); - /* reset counter for this frame to the start */ - fsig[N+1] = fsig[1] = 0.f; - count[curframe] = start; - /* move current to next frame circularly */ - p->curframe = --(curframe) < 0 ? numframes-1 : curframe; - framecount = 0; - p->fsig->framecount++; - } - } - p->framecount = framecount; - return OK; -} - -static int destroy_pvanal(CSOUND *csound, void *pp){ - PVAN *p = (PVAN *) pp; - cufftDestroy(p->plan); - cudaFree(p->aframe); - cudaFree(p->oldph); - cudaFree(p->win); - return OK; -} - -static OENTRY localops[] = { - {"cudasynth", sizeof(PVSYN),0, 5, "a", "f", (SUBR) pvsynset, NULL, - (SUBR) pvsynperf}, - {"cudanal", sizeof(PVAN),0, 5, "f", "aiiii", (SUBR) pvanalset, NULL, - (SUBR) pvanalperf} -}; - -extern "C" { - LINKAGE -} diff --git a/6.x/src/chua/OpcodeBase.hpp b/6.x/src/chua/OpcodeBase.hpp deleted file mode 100644 index 21ca8a0c..00000000 --- a/6.x/src/chua/OpcodeBase.hpp +++ /dev/null @@ -1,492 +0,0 @@ -/* - OpcodeBase.hpp: - - Copyright (C) 2005, 2009, 2017 by Istva Varga, Victor Lazzarini and - Michael Gogins - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ -#pragma once - -/** - * There is a conflict between the preprocessor definition "_CR" in the - * standard C++ library and in Csound. To work around this, undefine "_CR" and - * include ALL standard library include files BEFORE including ANY Csound - * include files. - */ -#if defined(_CR) -#undef _CR -#endif - -#include -#include -#include -#if !(defined(__wasi__)) -#include -#endif -#include -#include -#include -#include - -/** - * Template base class, or pseudo-virtual base class, - * for writing Csound opcodes in C++. - * Derive opcode implementation classes like this: - * - * DerivedClass : public OpcodeBase - * { - * public: - * // All output fields must be declared first as MYFLT *: - * MYFLT *aret1; - * // All input fields must be declared next as MYFLT *: - * MYFLT *iarg1; - * MYFLT *karg2; - * MYFLT *aarg3; - * // All internal state variables must be declared after that: - * size_t state1; - * double state2; - * MYFLT state3; - * // If the opcode shares data protect it by creating one or more void - * // *mutex member pointers: - * void *mutex1; - * void *mutex2; - * // and create them usind csound->Create_Mutex() in csoundModuleCreate - * // and destroy them using csound->DeleteMutex(mutex) - * // csoundModuleDestroy, and lock them using LockGuard guard(mutex). - * int init(); - * int kontrol(); - * int audio; - * int noteoff(); - * void deinit(); - * }; - */ - -namespace csound { - -static bool diagnostics_enabled = false; - -/** - * Use this to guard against data races in opcode functions. The mutex should - * be created by csound->Create_Mutex() in csoundModuleCreate(), and should - * be destroyed by csound->DeleteMutex() in csoundModuleDestroy(). - * - * If data is shared between opcode instances, the mutex should be global to - * the opcode library; if data is shared between threads for a single instance, - * the mutex should belong to the opcode instance. - */ -struct LockGuard { - LockGuard(CSOUND *csound_, void *mutex_) : csound(csound_), mutex(mutex_) - { - csound->LockMutex(mutex); - } - ~LockGuard() - { - csound->UnlockMutex(mutex); - } - CSOUND *csound; - void *mutex; -}; - -/** - * Use this to store a pointer to a global heap-allocated object, e.g. one - * used to manage state between opcode instances. - */ - -template int CreateGlobalPointer(CSOUND *csound, const char *name, T *pointer) -{ - T **pointer_to_pointer = 0; - int result = csound->CreateGlobalVariable(csound, name, sizeof(pointer_to_pointer)); - pointer_to_pointer = static_cast(csound->QueryGlobalVariable(csound, name)); - *pointer_to_pointer = pointer; - return result; -} - -/** - * Retrieve a pointer to a global heap-allocated object, e.g. one - * used to manage state between opcode instances. - */ -template T *QueryGlobalPointer(CSOUND *csound, const char *name, T*& pointer) -{ - T **pointer_to_pointer = static_cast(csound->QueryGlobalVariableNoCheck(csound, name)); - if (pointer_to_pointer != 0) { - pointer = *pointer_to_pointer; - } else { - pointer = 0; - } - return pointer; -} - -template -class OpcodeBase -{ -public: - int init(CSOUND *csound) - { - return NOTOK; - } - static int init_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->init(csound); - } - int kontrol(CSOUND *csound) - { - return NOTOK; - } - static int kontrol_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->kontrol(csound); - } - int audio(CSOUND *csound) - { - return NOTOK; - } - static int audio_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->audio(csound); - } - /** - This is how to compute audio signals for normal opcodes: - (1) Zero all frames from 0 up to but not including Offset. - (2) Compute all frames from ksmps_offset up to but not including End. - (3) Zero all frames from End up to but not including ksmps. - Example from a C opcode: - uint32_t offset = p->h.insdshead->ksmps_offset; - uint32_t early = p->h.insdshead->ksmps_no_end; - uint32_t n, nsmps = CS_KSMPS; - if (UNLIKELY(offset)) memset(p->r, '\0', offset*sizeof(MYFLT)); - if (UNLIKELY(early)) { - nsmps -= early; - memset(&p->r[nsmps], '\0', early*sizeof(MYFLT)); - } - for (n = offset; n < nsmps; n++) { - input1 = MYFLT2LRND(p->a[n]); - p->r[n] = (MYFLT) (input1 >> input2); - } - So in C++ it should look like this (which is much easier to understand): - int frameIndex = 0; - for( ; frameIndex < kperiodOffset(); ++frameIndex) { - asignal[frameIndex] = 0; - } - for( ; frameIndex < kperiodEnd(); ++frameIndex) { - asignal[frameIndex] = compute(); - } - for( ; frameIndex < ksmps(); ++frameIndex) { - asignal[frameIndex] = 0; - } - */ - uint32_t kperiodOffset() const - { - return opds.insdshead->ksmps_offset; - } - uint32_t kperiodEnd() const - { - uint32_t end = opds.insdshead->ksmps_no_end; - if (end) { - return end; - } else { - return ksmps(); - } - } - uint32_t ksmps() const - { - return opds.insdshead->ksmps; - } - uint32_t output_arg_count() - { - return (uint32_t)opds.optext->t.outArgCount; - } - uint32_t input_arg_count() - { - return (uint32_t)opds.optext->t.inArgCount; - } - void log(CSOUND *csound, const char *format,...) - { - va_list args; - va_start(args, format); - if(csound) { - csound->MessageV(csound, 0, format, args); - } else { - vfprintf(stdout, format, args); - } - va_end(args); - } - void warn(CSOUND *csound, const char *format,...) - { - if(csound) { - if(csound->GetMessageLevel(csound) & CS_WARNMSG) { - va_list args; - va_start(args, format); - csound->MessageV(csound, CSOUNDMSG_WARNING, format, args); - va_end(args); - } - } else { - va_list args; - va_start(args, format); - vfprintf(stdout, format, args); - va_end(args); - } - } - OPDS opds; -}; - -template -class OpcodeNoteoffBase -{ -public: - int init(CSOUND *csound) - { - IGN(csound); - return NOTOK; - } - static int init_(CSOUND *csound, void *opcode) - { - if (!csound->GetReinitFlag(csound) && !csound->GetTieFlag(csound)) { - csound->RegisterDeinitCallback(csound, opcode, - &OpcodeNoteoffBase::noteoff_); - } - return reinterpret_cast(opcode)->init(csound); - } - int kontrol(CSOUND *csound) - { - IGN(csound); - return NOTOK; - } - static int kontrol_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->kontrol(csound); - } - int audio(CSOUND *csound) - { - IGN(csound); - return NOTOK; - } - static int audio_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->audio(csound); - } - /** - This is how to compute audio signals for normal opcodes: - (1) Zero all frames from 0 up to but not including Offset. - (2) Compute all frames from ksmps_offset up to but not including End. - (3) Zero all frames from End up to but not including ksmps. - Example from a C opcode: - uint32_t offset = p->h.insdshead->ksmps_offset; - uint32_t early = p->h.insdshead->ksmps_no_end; - uint32_t n, nsmps = CS_KSMPS; - if (UNLIKELY(offset)) memset(p->r, '\0', offset*sizeof(MYFLT)); - if (UNLIKELY(early)) { - nsmps -= early; - memset(&p->r[nsmps], '\0', early*sizeof(MYFLT)); - } - for (n = offset; n < nsmps; n++) { - input1 = MYFLT2LRND(p->a[n]); - p->r[n] = (MYFLT) (input1 >> input2); - } - So in C++ it should look like this (which is much easier to understand): - int frameIndex = 0; - for( ; frameIndex < kperiodOffset(); ++frameIndex) { - asignal[frameIndex] = 0; - } - for( ; frameIndex < kperiodEnd(); ++frameIndex) { - asignal[frameIndex] = compute(); - } - for( ; frameIndex < ksmps(); ++frameIndex) { - asignal[frameIndex] = 0; - } - */ - uint32_t kperiodOffset() const - { - return opds.insdshead->ksmps_offset; - } - uint32_t kperiodEnd() const - { - uint32_t end = opds.insdshead->ksmps_no_end; - if (end) { - return end; - } else { - return ksmps(); - } - } - uint32_t ksmps() const - { - return opds.insdshead->ksmps; - } - uint32_t output_arg_count() - { - return (uint32_t)opds.optext->t.outArgCount; - } - uint32_t input_arg_count() - { - return (uint32_t)opds.optext->t.inArgCount; - } - void log(CSOUND *csound, const char *format,...) - { - va_list args; - va_start(args, format); - if(csound) { - csound->MessageV(csound, 0, format, args); - } else { - vfprintf(stdout, format, args); - } - va_end(args); - } - void warn(CSOUND *csound, const char *format,...) - { - if(csound) { - if(csound->GetMessageLevel(csound) & CS_WARNMSG) { - va_list args; - va_start(args, format); - csound->MessageV(csound, CSOUNDMSG_WARNING, format, args); - va_end(args); - } - } else { - va_list args; - va_start(args, format); - vfprintf(stdout, format, args); - va_end(args); - } - } - int noteoff(CSOUND *csound) - { - return OK; - } - static int noteoff_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->noteoff(csound); - } - OPDS opds; -}; - -#if (__cplusplus >= 201103L) && !(defined(__wasi__)) -#pragma message("Defining heap_object_manager_t.") - -/** - * The memory of non-POD C++ or C objects allocated on the heap by Csound - * plugins is managed not by Csound, but by the plugin module. This class - * performs that function. - * - * As long as all memory is allocated in or after csoundModuleCreate and - * de-allocated in csoundModuleDestroy, all will be well. The danger however - * is that memory leaks will occur if allocations are not matched with - * de-allocations, or worse, that crashes will occur if any objects are - * deleted twice or de-referenced after deletion. - * - * So, the plugin should manage a collection of all heap-allocated objects, - * keyed first off the Csound instance, and second off an object handle; and - * all such objects must be accessed only by handle, from this collection. - * - * In csoundModuleDestroy, all objects for the calling instance of Csound - * should be de-allocated, and the sub-collection for that instance should - * also be de-allocated. - * - * If then there are no more sub-collections for other instances of Csound, - * the entire contents of the collection should be de-allocated. - * - * This should be done as a singleton, and in a thread-safe way. - * - * There might still be a problem if any of the heap-allocated objects, - * themselves, have global static members, perhaps created by some external - * dependency. - * - * Please note, objects O in the declaration below must not be std::shared_ptr - * objects. Deletion of objects herein must be unconditional. Rather, the - * objects passed are raw pointers to instances of class O. - */ - template class heap_object_manager_t { - private: - std::map> objects_; - std::recursive_mutex mutex; - heap_object_manager_t(){}; - ~heap_object_manager_t(){}; - public: - static heap_object_manager_t &instance() { - static heap_object_manager_t singleton; - return singleton; - } - std::map> &objects() { - std::lock_guard lock(mutex); - return objects_; - } - /** - * Returns a list of pointers to all objects allocated for this - * instance of Csound. - */ - std::vector &objects_for_csound(CSOUND *csound) { - std::lock_guard lock(mutex); - auto &objects_for_csound_ = objects()[csound]; - return objects_for_csound_; - } - /** - * Returns the handle for the object; if the object has not yet been - * stored, inserts it into the list of object pointers for this - * instance of Csound; otherwise, returns the handle of the stored - * object pointer. - */ - int handle_for_object(CSOUND *csound, O *object) { - std::lock_guard lock(mutex); - auto &objects_for_csound_ = objects_for_csound(csound); - auto iterator = std::find(objects_for_csound_.begin(), objects_for_csound_.end(), object); - if (iterator == objects_for_csound_.end()) { - int handle = objects_for_csound_.size(); - objects_for_csound_.push_back(object); - if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::handle_for_object %p: new object handle: %d (of %ld)\n", object, handle, objects_for_csound_.size()); - return handle; - } else { - int handle = static_cast(iterator - objects_for_csound_.begin()); - if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::handle_for_object: existing object handle: %d\n", handle); - return handle; - } - } - /** - * Returns the object pointer for the handle; - * if the object pointer has not been stored by - * handle, returns a null pointer. - */ - O *object_for_handle(CSOUND *csound, int handle) { - std::lock_guard lock(mutex); - auto &objects_for_csound_ = objects_for_csound(csound); - if (handle >= objects_for_csound_.size()) { - return nullptr; - } - O *object = objects_for_csound_[handle]; - if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::object_for_handle: %p %d (of %ld)\n", object, handle, objects_for_csound_.size()); - return object; - } - /** - * First destroys all objects created by the calling - * instance of Csound, then destroys the list of - * object pointers for this instance of Csound. - */ - void module_destroy(CSOUND *csound) { - std::lock_guard lock(mutex); - auto &objects_for_csound_ = objects_for_csound(csound); - for (int i = 0, n = objects_for_csound_.size(); i < n; ++i) { - delete objects_for_csound_[i]; - objects_for_csound_[i] = nullptr; - } - objects_for_csound_.clear(); - objects().erase(csound); - } -}; - -#else -#pragma message("Not defining heap_manager_t.") -#endif - -} diff --git a/6.x/src/faustcsound/CMakeLists.txt b/6.x/src/faustcsound/CMakeLists.txt deleted file mode 100644 index 45b9d3dd..00000000 --- a/6.x/src/faustcsound/CMakeLists.txt +++ /dev/null @@ -1,37 +0,0 @@ -# Build faustcsound opcodes -option(BUILD_FAUST_OPCODES "Build the Faust opcodes" ON) - -find_package(FAUST) -check_deps(BUILD_FAUST_OPCODES FAUST_FOUND) - -if(BUILD_FAUST_OPCODES AND FAUST_FOUND) - make_plugin(faustcsound faustgen.cpp) - - if(APPLE) - set_target_properties(faustcsound PROPERTIES - INSTALL_RPATH_USE_LINK_PATH TRUE - ) - endif() - - target_include_directories(faustcsound PRIVATE ${CSOUND_INCLUDE_DIRS}) - target_link_libraries(faustcsound ${PTHREAD_LIBRARY}) - target_compile_definitions(faustcsound PRIVATE "-DFAUSTFLOAT=MYFLT") - - if(PTHREAD_LIBRARY) - target_compile_definitions(faustcsound PRIVATE "-DHAVE_PTHREAD") - endif() - - if(APPLE) - if(${OSX_VERSION} STREQUAL "10.6") - target_link_libraries(faustcsound ${FAUST_LIBRARIES}) - else() - target_compile_options(faustcsound PRIVATE "-stdlib=libc++") - target_link_libraries(faustcsound ${FAUST_LIBRARIES} -stdlib=libc++) - endif() - else() - target_link_libraries(faustcsound ${FAUST_LIBRARIES}) - endif() - - target_include_directories(faustcsound PRIVATE ${FAUST_INCLUDE_DIR}) - message("-- FAUST INCLUDE DIR: ${FAUST_INCLUDE_DIR}") -endif() diff --git a/6.x/src/faustcsound/README.md b/6.x/src/faustcsound/README.md deleted file mode 100644 index da5dff5e..00000000 --- a/6.x/src/faustcsound/README.md +++ /dev/null @@ -1,29 +0,0 @@ -Faust opcodes -======= - -To build the faust opcodes, you will need the Faust library, which can downloaded -from [https://github.com/grame-cncm/faust/releases] (https://github.com/grame-cncm/faust/releases) -or built from sources (you will also need libcrypto and LLVM -installed). - -If using the pre-built binaries the following CMake variables are -important: - -`FAUST_INCLUDE_DIR_HINT` - -and - -`FAUST_LIB_DIR_HINT` - -They should point to the include and lib directories inside the faust -binary package. For example, if the package (for example, -Faust 2.33.1) is placed at the top-level user directory use the CMake command - -``` -cmake .. -DFAUST_INCLUDE_DIR_HINT=$HOME/faust-2.33.1/include --DFAUST_LIB_DIR_HINT=$HOME/faust-2.33.1/lib -``` - -to correctly find the library and headers. The library will be built -and linked to the installed faust library. - diff --git a/6.x/src/fluidOpcodes/Android/.gitignore b/6.x/src/fluidOpcodes/Android/.gitignore deleted file mode 100644 index 91e378c9..00000000 --- a/6.x/src/fluidOpcodes/Android/.gitignore +++ /dev/null @@ -1,10 +0,0 @@ -*DS_Store -obj -libs -bin -gen -src/csnd* -*swp -jni/csound_orc* -jni/csound_prelex.c -jni/java_interface* diff --git a/6.x/src/fluidOpcodes/Android/build.sh b/6.x/src/fluidOpcodes/Android/build.sh deleted file mode 100644 index 6d91356b..00000000 --- a/6.x/src/fluidOpcodes/Android/build.sh +++ /dev/null @@ -1,9 +0,0 @@ -#!/bin/sh - -export ANDROID_NDK_ROOT=$NDK -export NDK_MODULE_PATH=../../../../android - -$ANDROID_NDK_ROOT/ndk-build TARGET_PLATFORM=android-9 $@ - - - diff --git a/6.x/src/fluidOpcodes/Android/jni/Android.mk b/6.x/src/fluidOpcodes/Android/jni/Android.mk deleted file mode 100644 index da9404ba..00000000 --- a/6.x/src/fluidOpcodes/Android/jni/Android.mk +++ /dev/null @@ -1,24 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -LIBSNDFILE_SRC_DIR := $(NDK_MODULE_PATH)/libsndfile-android/jni/ -LIBFLUIDSYNTH_SRC_DIR := $(NDK_MODULE_PATH)/fluidsynth-android/jni/ - -LOCAL_MODULE := fluidOpcodes - -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../../../H $(LOCAL_PATH)/../../../../include $(LOCAL_PATH)/../../../.. $(LOCAL_PATH)/../../../ $(LIBSNDFILE_SRC_DIR) $(LIBFLUIDSYNTH_SRC_DIR)/include $(LOCAL_PATH)/../.. $(LOCAL_PATH)/../../../CsoundAndroid/jni -LOCAL_CFLAGS := -O3 -D__BUILDING_LIBCSOUND -DENABLE_NEW_PARSER -DLINUX -DHAVE_DIRENT_H -DHAVE_FCNTL_H -DHAVE_UNISTD_H -DHAVE_STDINT_H -DHAVE_SYS_TIME_H -DHAVE_SYS_TYPES_H -DHAVE_TERMIOS_H -LOCAL_CPPFLAGS :=$(LOCAL_CFLAGS) -LOCAL_CPPFLAGS += -std=c++11 -pthread -frtti -fexceptions -### - -LOCAL_SRC_FILES := ../../fluidOpcodes.cpp - -LOCAL_LDLIBS += -ldl - -LOCAL_STATIC_LIBRARIES := fluidsynth-android - -include $(BUILD_SHARED_LIBRARY) - -$(call import-module,fluidsynth-android/jni) diff --git a/6.x/src/fluidOpcodes/Android/jni/Application.mk b/6.x/src/fluidOpcodes/Android/jni/Application.mk deleted file mode 100644 index f343179d..00000000 --- a/6.x/src/fluidOpcodes/Android/jni/Application.mk +++ /dev/null @@ -1,8 +0,0 @@ -APP_ABI := armeabi-v7a arm64-v8a -APP_CPPFLAGS += -fexceptions -frtti -APP_OPTIM := release -APP_PLATFORM := android-21 -#APP_STL := gnustl_shared -#NDK_TOOLCHAIN_VERSION := 4.9 -APP_STL := c++_shared -NDK_TOOLCHAIN_VERSION := clang diff --git a/6.x/src/fluidOpcodes/CMakeLists.txt b/6.x/src/fluidOpcodes/CMakeLists.txt deleted file mode 100644 index 0e6caa37..00000000 --- a/6.x/src/fluidOpcodes/CMakeLists.txt +++ /dev/null @@ -1,11 +0,0 @@ -option(BUILD_FLUID_OPCODES "Build the fluidsynth opcodes" ON) - -find_package(FLUIDSYNTH) -check_deps(BUILD_FLUID_OPCODES FLUIDSYNTH_FOUND) - -if(BUILD_FLUID_OPCODES AND FLUIDSYNTH_FOUND) - make_plugin(fluidOpcodes fluidOpcodes.cpp) - target_include_directories(fluidOpcodes PRIVATE ${FLUIDSYNTH_INCLUDE_DIR}) - target_include_directories(fluidOpcodes PRIVATE ${CSOUND_INCLUDE_DIR}) - target_link_libraries(fluidOpcodes ${FLUIDSYNTH_LIBRARIES}) -endif() diff --git a/6.x/src/fluidOpcodes/OpcodeBase.hpp b/6.x/src/fluidOpcodes/OpcodeBase.hpp deleted file mode 100644 index 21ca8a0c..00000000 --- a/6.x/src/fluidOpcodes/OpcodeBase.hpp +++ /dev/null @@ -1,492 +0,0 @@ -/* - OpcodeBase.hpp: - - Copyright (C) 2005, 2009, 2017 by Istva Varga, Victor Lazzarini and - Michael Gogins - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ -#pragma once - -/** - * There is a conflict between the preprocessor definition "_CR" in the - * standard C++ library and in Csound. To work around this, undefine "_CR" and - * include ALL standard library include files BEFORE including ANY Csound - * include files. - */ -#if defined(_CR) -#undef _CR -#endif - -#include -#include -#include -#if !(defined(__wasi__)) -#include -#endif -#include -#include -#include -#include - -/** - * Template base class, or pseudo-virtual base class, - * for writing Csound opcodes in C++. - * Derive opcode implementation classes like this: - * - * DerivedClass : public OpcodeBase - * { - * public: - * // All output fields must be declared first as MYFLT *: - * MYFLT *aret1; - * // All input fields must be declared next as MYFLT *: - * MYFLT *iarg1; - * MYFLT *karg2; - * MYFLT *aarg3; - * // All internal state variables must be declared after that: - * size_t state1; - * double state2; - * MYFLT state3; - * // If the opcode shares data protect it by creating one or more void - * // *mutex member pointers: - * void *mutex1; - * void *mutex2; - * // and create them usind csound->Create_Mutex() in csoundModuleCreate - * // and destroy them using csound->DeleteMutex(mutex) - * // csoundModuleDestroy, and lock them using LockGuard guard(mutex). - * int init(); - * int kontrol(); - * int audio; - * int noteoff(); - * void deinit(); - * }; - */ - -namespace csound { - -static bool diagnostics_enabled = false; - -/** - * Use this to guard against data races in opcode functions. The mutex should - * be created by csound->Create_Mutex() in csoundModuleCreate(), and should - * be destroyed by csound->DeleteMutex() in csoundModuleDestroy(). - * - * If data is shared between opcode instances, the mutex should be global to - * the opcode library; if data is shared between threads for a single instance, - * the mutex should belong to the opcode instance. - */ -struct LockGuard { - LockGuard(CSOUND *csound_, void *mutex_) : csound(csound_), mutex(mutex_) - { - csound->LockMutex(mutex); - } - ~LockGuard() - { - csound->UnlockMutex(mutex); - } - CSOUND *csound; - void *mutex; -}; - -/** - * Use this to store a pointer to a global heap-allocated object, e.g. one - * used to manage state between opcode instances. - */ - -template int CreateGlobalPointer(CSOUND *csound, const char *name, T *pointer) -{ - T **pointer_to_pointer = 0; - int result = csound->CreateGlobalVariable(csound, name, sizeof(pointer_to_pointer)); - pointer_to_pointer = static_cast(csound->QueryGlobalVariable(csound, name)); - *pointer_to_pointer = pointer; - return result; -} - -/** - * Retrieve a pointer to a global heap-allocated object, e.g. one - * used to manage state between opcode instances. - */ -template T *QueryGlobalPointer(CSOUND *csound, const char *name, T*& pointer) -{ - T **pointer_to_pointer = static_cast(csound->QueryGlobalVariableNoCheck(csound, name)); - if (pointer_to_pointer != 0) { - pointer = *pointer_to_pointer; - } else { - pointer = 0; - } - return pointer; -} - -template -class OpcodeBase -{ -public: - int init(CSOUND *csound) - { - return NOTOK; - } - static int init_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->init(csound); - } - int kontrol(CSOUND *csound) - { - return NOTOK; - } - static int kontrol_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->kontrol(csound); - } - int audio(CSOUND *csound) - { - return NOTOK; - } - static int audio_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->audio(csound); - } - /** - This is how to compute audio signals for normal opcodes: - (1) Zero all frames from 0 up to but not including Offset. - (2) Compute all frames from ksmps_offset up to but not including End. - (3) Zero all frames from End up to but not including ksmps. - Example from a C opcode: - uint32_t offset = p->h.insdshead->ksmps_offset; - uint32_t early = p->h.insdshead->ksmps_no_end; - uint32_t n, nsmps = CS_KSMPS; - if (UNLIKELY(offset)) memset(p->r, '\0', offset*sizeof(MYFLT)); - if (UNLIKELY(early)) { - nsmps -= early; - memset(&p->r[nsmps], '\0', early*sizeof(MYFLT)); - } - for (n = offset; n < nsmps; n++) { - input1 = MYFLT2LRND(p->a[n]); - p->r[n] = (MYFLT) (input1 >> input2); - } - So in C++ it should look like this (which is much easier to understand): - int frameIndex = 0; - for( ; frameIndex < kperiodOffset(); ++frameIndex) { - asignal[frameIndex] = 0; - } - for( ; frameIndex < kperiodEnd(); ++frameIndex) { - asignal[frameIndex] = compute(); - } - for( ; frameIndex < ksmps(); ++frameIndex) { - asignal[frameIndex] = 0; - } - */ - uint32_t kperiodOffset() const - { - return opds.insdshead->ksmps_offset; - } - uint32_t kperiodEnd() const - { - uint32_t end = opds.insdshead->ksmps_no_end; - if (end) { - return end; - } else { - return ksmps(); - } - } - uint32_t ksmps() const - { - return opds.insdshead->ksmps; - } - uint32_t output_arg_count() - { - return (uint32_t)opds.optext->t.outArgCount; - } - uint32_t input_arg_count() - { - return (uint32_t)opds.optext->t.inArgCount; - } - void log(CSOUND *csound, const char *format,...) - { - va_list args; - va_start(args, format); - if(csound) { - csound->MessageV(csound, 0, format, args); - } else { - vfprintf(stdout, format, args); - } - va_end(args); - } - void warn(CSOUND *csound, const char *format,...) - { - if(csound) { - if(csound->GetMessageLevel(csound) & CS_WARNMSG) { - va_list args; - va_start(args, format); - csound->MessageV(csound, CSOUNDMSG_WARNING, format, args); - va_end(args); - } - } else { - va_list args; - va_start(args, format); - vfprintf(stdout, format, args); - va_end(args); - } - } - OPDS opds; -}; - -template -class OpcodeNoteoffBase -{ -public: - int init(CSOUND *csound) - { - IGN(csound); - return NOTOK; - } - static int init_(CSOUND *csound, void *opcode) - { - if (!csound->GetReinitFlag(csound) && !csound->GetTieFlag(csound)) { - csound->RegisterDeinitCallback(csound, opcode, - &OpcodeNoteoffBase::noteoff_); - } - return reinterpret_cast(opcode)->init(csound); - } - int kontrol(CSOUND *csound) - { - IGN(csound); - return NOTOK; - } - static int kontrol_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->kontrol(csound); - } - int audio(CSOUND *csound) - { - IGN(csound); - return NOTOK; - } - static int audio_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->audio(csound); - } - /** - This is how to compute audio signals for normal opcodes: - (1) Zero all frames from 0 up to but not including Offset. - (2) Compute all frames from ksmps_offset up to but not including End. - (3) Zero all frames from End up to but not including ksmps. - Example from a C opcode: - uint32_t offset = p->h.insdshead->ksmps_offset; - uint32_t early = p->h.insdshead->ksmps_no_end; - uint32_t n, nsmps = CS_KSMPS; - if (UNLIKELY(offset)) memset(p->r, '\0', offset*sizeof(MYFLT)); - if (UNLIKELY(early)) { - nsmps -= early; - memset(&p->r[nsmps], '\0', early*sizeof(MYFLT)); - } - for (n = offset; n < nsmps; n++) { - input1 = MYFLT2LRND(p->a[n]); - p->r[n] = (MYFLT) (input1 >> input2); - } - So in C++ it should look like this (which is much easier to understand): - int frameIndex = 0; - for( ; frameIndex < kperiodOffset(); ++frameIndex) { - asignal[frameIndex] = 0; - } - for( ; frameIndex < kperiodEnd(); ++frameIndex) { - asignal[frameIndex] = compute(); - } - for( ; frameIndex < ksmps(); ++frameIndex) { - asignal[frameIndex] = 0; - } - */ - uint32_t kperiodOffset() const - { - return opds.insdshead->ksmps_offset; - } - uint32_t kperiodEnd() const - { - uint32_t end = opds.insdshead->ksmps_no_end; - if (end) { - return end; - } else { - return ksmps(); - } - } - uint32_t ksmps() const - { - return opds.insdshead->ksmps; - } - uint32_t output_arg_count() - { - return (uint32_t)opds.optext->t.outArgCount; - } - uint32_t input_arg_count() - { - return (uint32_t)opds.optext->t.inArgCount; - } - void log(CSOUND *csound, const char *format,...) - { - va_list args; - va_start(args, format); - if(csound) { - csound->MessageV(csound, 0, format, args); - } else { - vfprintf(stdout, format, args); - } - va_end(args); - } - void warn(CSOUND *csound, const char *format,...) - { - if(csound) { - if(csound->GetMessageLevel(csound) & CS_WARNMSG) { - va_list args; - va_start(args, format); - csound->MessageV(csound, CSOUNDMSG_WARNING, format, args); - va_end(args); - } - } else { - va_list args; - va_start(args, format); - vfprintf(stdout, format, args); - va_end(args); - } - } - int noteoff(CSOUND *csound) - { - return OK; - } - static int noteoff_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->noteoff(csound); - } - OPDS opds; -}; - -#if (__cplusplus >= 201103L) && !(defined(__wasi__)) -#pragma message("Defining heap_object_manager_t.") - -/** - * The memory of non-POD C++ or C objects allocated on the heap by Csound - * plugins is managed not by Csound, but by the plugin module. This class - * performs that function. - * - * As long as all memory is allocated in or after csoundModuleCreate and - * de-allocated in csoundModuleDestroy, all will be well. The danger however - * is that memory leaks will occur if allocations are not matched with - * de-allocations, or worse, that crashes will occur if any objects are - * deleted twice or de-referenced after deletion. - * - * So, the plugin should manage a collection of all heap-allocated objects, - * keyed first off the Csound instance, and second off an object handle; and - * all such objects must be accessed only by handle, from this collection. - * - * In csoundModuleDestroy, all objects for the calling instance of Csound - * should be de-allocated, and the sub-collection for that instance should - * also be de-allocated. - * - * If then there are no more sub-collections for other instances of Csound, - * the entire contents of the collection should be de-allocated. - * - * This should be done as a singleton, and in a thread-safe way. - * - * There might still be a problem if any of the heap-allocated objects, - * themselves, have global static members, perhaps created by some external - * dependency. - * - * Please note, objects O in the declaration below must not be std::shared_ptr - * objects. Deletion of objects herein must be unconditional. Rather, the - * objects passed are raw pointers to instances of class O. - */ - template class heap_object_manager_t { - private: - std::map> objects_; - std::recursive_mutex mutex; - heap_object_manager_t(){}; - ~heap_object_manager_t(){}; - public: - static heap_object_manager_t &instance() { - static heap_object_manager_t singleton; - return singleton; - } - std::map> &objects() { - std::lock_guard lock(mutex); - return objects_; - } - /** - * Returns a list of pointers to all objects allocated for this - * instance of Csound. - */ - std::vector &objects_for_csound(CSOUND *csound) { - std::lock_guard lock(mutex); - auto &objects_for_csound_ = objects()[csound]; - return objects_for_csound_; - } - /** - * Returns the handle for the object; if the object has not yet been - * stored, inserts it into the list of object pointers for this - * instance of Csound; otherwise, returns the handle of the stored - * object pointer. - */ - int handle_for_object(CSOUND *csound, O *object) { - std::lock_guard lock(mutex); - auto &objects_for_csound_ = objects_for_csound(csound); - auto iterator = std::find(objects_for_csound_.begin(), objects_for_csound_.end(), object); - if (iterator == objects_for_csound_.end()) { - int handle = objects_for_csound_.size(); - objects_for_csound_.push_back(object); - if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::handle_for_object %p: new object handle: %d (of %ld)\n", object, handle, objects_for_csound_.size()); - return handle; - } else { - int handle = static_cast(iterator - objects_for_csound_.begin()); - if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::handle_for_object: existing object handle: %d\n", handle); - return handle; - } - } - /** - * Returns the object pointer for the handle; - * if the object pointer has not been stored by - * handle, returns a null pointer. - */ - O *object_for_handle(CSOUND *csound, int handle) { - std::lock_guard lock(mutex); - auto &objects_for_csound_ = objects_for_csound(csound); - if (handle >= objects_for_csound_.size()) { - return nullptr; - } - O *object = objects_for_csound_[handle]; - if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::object_for_handle: %p %d (of %ld)\n", object, handle, objects_for_csound_.size()); - return object; - } - /** - * First destroys all objects created by the calling - * instance of Csound, then destroys the list of - * object pointers for this instance of Csound. - */ - void module_destroy(CSOUND *csound) { - std::lock_guard lock(mutex); - auto &objects_for_csound_ = objects_for_csound(csound); - for (int i = 0, n = objects_for_csound_.size(); i < n; ++i) { - delete objects_for_csound_[i]; - objects_for_csound_[i] = nullptr; - } - objects_for_csound_.clear(); - objects().erase(csound); - } -}; - -#else -#pragma message("Not defining heap_manager_t.") -#endif - -} diff --git a/6.x/src/fluidOpcodes/fluidOpcodes.cpp b/6.x/src/fluidOpcodes/fluidOpcodes.cpp deleted file mode 100644 index c7bf668b..00000000 --- a/6.x/src/fluidOpcodes/fluidOpcodes.cpp +++ /dev/null @@ -1,748 +0,0 @@ -/** - * FLUID SYNTH OPCODES - * - * Adapts Fluidsynth to use global engines, soundFonts, and outputs - * - * Based on work by Michael Gogins and Steven Yi. License is identical to - * SOUNDFONTS VST License (listed below) - * - * Copyright (c) 2003 by Steven Yi. All rights reserved. - * - * [ORIGINAL INFORMATION BELOW] - * - * S O U N D F O N T S V S T - * - * Adapts Fluidsynth to be both a VST plugin instrument - * and a Csound plugin opcode. - * Copyright (c) 2001-2003 by Michael Gogins. All rights reserved. - * - * L I C E N S E - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#include -#include -#include -#include -#include -#include "OpcodeBase.hpp" -#include "csdl.h" -#include -#include - -static std::vector &fluidsynths_for_ids() { - static std::vector fluidsynths_for_ids_; - return fluidsynths_for_ids_; -} - -static void tof(fluid_synth_t *f, MYFLT *a) { - auto &instances = fluidsynths_for_ids(); - instances.push_back(f); - *a = instances.size() - 1; -}; - -static void toa(MYFLT *f, fluid_synth_t *&a) { - auto &instances = fluidsynths_for_ids(); - int index = *f; - a = instances[index]; -}; - -class FluidEngine : public csound::OpcodeBase { - // Outputs. - MYFLT *iFluidSynth; - // Inputs. - MYFLT *iChorusEnabled; - MYFLT *iReverbEnabled; - MYFLT *iChannelCount; - MYFLT *iVoiceCount; - // State. - // fluid_synth_t *fluidSynth; - // fluid_settings_t *fluidSettings; - int32_t chorusEnabled; - int32_t reverbEnabled; - int32_t channelCount; - int32_t voiceCount; - void *mutex; - -public: - int32_t init(CSOUND *csound) { - mutex = csound->Create_Mutex(0); - csound::LockGuard guard(csound, mutex); - int32_t result = OK; - fluid_synth_t *fluidSynth = 0; - fluid_settings_t *fluidSettings = 0; - chorusEnabled = (int32_t)*iChorusEnabled; - reverbEnabled = (int32_t)*iReverbEnabled; - channelCount = (int32_t)*iChannelCount; - voiceCount = (int32_t)*iVoiceCount; - if (channelCount <= 0) { - channelCount = 256; - } else if (channelCount < 16) { - channelCount = 16; - } else if (channelCount > 256) { - channelCount = 256; - } - if (voiceCount <= 0) { - voiceCount = 4096; - } else if (voiceCount < 16) { - voiceCount = 16; - } else if (voiceCount > 4096) { - voiceCount = 4096; - } - fluidSettings = new_fluid_settings(); - if (fluidSettings != NULL) { - fluid_settings_setnum(fluidSettings, (char *)"synth.sample-rate", - (double)csound->GetSr(csound)); - fluid_settings_setint(fluidSettings, (char *)"synth.midi-channels", - channelCount); - fluid_settings_setint(fluidSettings, (char *)"synth.polyphony", - voiceCount); - fluidSynth = new_fluid_synth(fluidSettings); - } - if (!fluidSynth) { - if (fluidSettings) { - delete_fluid_settings(fluidSettings); - } - result = - csound->InitError(csound, "%s", Str("error allocating fluid engine\n")); - } else { -#if (FLUIDSYNTH_VERSION_MAJOR >= 2 && FLUIDSYNTH_VERSION_MINOR >= 2) - // TODO: Change -1 to a configurable FX group? - fluid_synth_chorus_on(fluidSynth, -1, chorusEnabled); - fluid_synth_reverb_on(fluidSynth, -1, reverbEnabled); -#else - fluid_synth_set_chorus_on(fluidSynth, chorusEnabled); - fluid_synth_set_reverb_on(fluidSynth, reverbEnabled); -#endif - log(csound, "Created fluidEngine 0x%p with sampling rate = %f, " - "chorus %s, reverb %s, channels %d, voices %d.\n", - fluidSynth, (double)csound->GetSr(csound), - chorusEnabled ? "on" : "off", reverbEnabled ? "on" : "off", - channelCount, voiceCount); - tof(fluidSynth, iFluidSynth); - { - void *fluid_synths_mutex = 0; - csound::QueryGlobalPointer(csound, "fluid_synths_mutex", - fluid_synths_mutex); - csound::LockGuard synthsGuard(csound, fluid_synths_mutex); - std::vector *fluid_synths = 0; - csound::QueryGlobalPointer(csound, "fluid_synths", fluid_synths); - fluid_synths->push_back(fluidSynth); - } - } - return result; - } -}; - -class FluidLoad : public csound::OpcodeBase { - // Outputs. - MYFLT *iInstrumentNumber; - // Inputs. - MYFLT *iFilename; - MYFLT *iFluidSynth; - MYFLT *iListPresets; - // State. - char *filename; - char *filepath; - fluid_synth_t *fluidSynth; - int32_t soundFontId; - int32_t listPresets; - void *mutex; - -public: - int32_t init(CSOUND *csound) { - mutex = csound->Create_Mutex(0); - csound::LockGuard guard(csound, mutex); - int32_t result = OK; - soundFontId = -1; - toa(iFluidSynth, fluidSynth); - listPresets = (int32_t)*iListPresets; - CS_TYPE *argType = csound->GetTypeForArg(iFilename); - if (strcmp("S", argType->varTypeName) == 0) { - filename = csound->Strdup(csound, ((STRINGDAT *)iFilename)->data); - } else - filename = csound->strarg2name( - csound, (char *)NULL, - (std::isnan(*iFilename) ? csound->GetString(csound, *iFilename) - : (char *)iFilename), - (char *)"fluid.sf2.", std::isnan(*iFilename)); - - filepath = csound->FindInputFile(csound, filename, "SFDIR;SSDIR"); - if (filepath && fluid_is_soundfont(filepath)) { - log(csound, "Loading SoundFont: %s.\n", filepath); - soundFontId = fluid_synth_sfload(fluidSynth, filepath, 0); - log(csound, "fluidSynth: 0x%p soundFontId: %d.\n", fluidSynth, - soundFontId); - } - *iInstrumentNumber = (MYFLT)soundFontId; - if (UNLIKELY(soundFontId < 0)) { - return csound->InitError(csound, Str("fluid: unable to load %s"), - filename); - } - csound->NotifyFileOpened(csound, filepath, CSFTYPE_SOUNDFONT, 0, 0); - if (soundFontId < 0) { - result = NOTOK; - } else if (listPresets) { - fluid_sfont_t *fluidSoundfont = - fluid_synth_get_sfont_by_id(fluidSynth, soundFontId); -#if FLUIDSYNTH_VERSION_MAJOR < 2 - fluid_preset_t fluidPreset; - fluidSoundfont->iteration_start(fluidSoundfont); - OPARMS oparms; - csound->GetOParms(csound, &oparms); - if (oparms.msglevel & 0x7) - while (fluidSoundfont->iteration_next(fluidSoundfont, &fluidPreset)) - { - log(csound, "SoundFont: %3d Bank: %3d Preset: %3d %s\n", - soundFontId, fluidPreset.get_banknum(&fluidPreset), - fluidPreset.get_num(&fluidPreset), - fluidPreset.get_name(&fluidPreset)); -#else - fluid_preset_t *fluidPreset; - fluid_sfont_iteration_start(fluidSoundfont); - OPARMS oparms; - csound->GetOParms(csound, &oparms); - if (oparms.msglevel & 0x7) - while ((fluidPreset = fluid_sfont_iteration_next(fluidSoundfont))) - { - log(csound, "SoundFont: %3d Bank: %3d Preset: %3d %s\n", - soundFontId, fluid_preset_get_banknum(fluidPreset), - fluid_preset_get_num(fluidPreset), - fluid_preset_get_name(fluidPreset)); -#endif - } - } - return result; - } -}; - -#include "arrays.h" - -//Rory Walsh 2018 -class FluidInfo : public csound::OpcodeBase { - // Outputs. - ARRAYDAT *outArr; - // Inputs. - MYFLT *iFluidSynth; - // State. - fluid_synth_t *fluidSynth; - void *mutex; - -public: - int32_t init(CSOUND *csound) { - std::vector programs; - char *program; - mutex = csound->Create_Mutex(0); - csound::LockGuard guard(csound, mutex); - int32_t result = OK; - toa(iFluidSynth, fluidSynth); - fluid_sfont_t *fluidSoundfont = fluid_synth_get_sfont(fluidSynth, 0); -#if FLUIDSYNTH_VERSION_MAJOR < 2 - fluid_preset_t fluidPreset; - fluidSoundfont->iteration_start(fluidSoundfont); - OPARMS oparms; - csound->GetOParms(csound, &oparms); - if (oparms.msglevel & 0x7) - while (fluidSoundfont->iteration_next(fluidSoundfont, &fluidPreset)) - { - std::stringstream ss; - ss << "Bank: " << fluidPreset.get_banknum(&fluidPreset) << - " Preset: " << fluidPreset.get_num(&fluidPreset) << - " Name: " << fluidPreset.get_name(&fluidPreset); -#else - fluid_preset_t *fluidPreset; - fluid_sfont_iteration_start(fluidSoundfont); - OPARMS oparms; - csound->GetOParms(csound, &oparms); - if (oparms.msglevel & 0x7) - while ((fluidPreset = fluid_sfont_iteration_next(fluidSoundfont))) - { - std::stringstream ss; - ss << "Bank: " << fluid_preset_get_banknum(fluidPreset) << - " Preset: " << fluid_preset_get_num(fluidPreset) << - " Name: " << fluid_preset_get_name(fluidPreset); -#endif - programs.push_back(ss.str()); - } - - tabinit(csound, outArr, programs.size()); - STRINGDAT *strings = (STRINGDAT *)outArr->data; - - for (unsigned int i = 0; i < programs.size(); i++) { - program = &programs[i][0u]; - strings[i].size = strlen(program) + 1; - strings[i].data = csound->Strdup(csound, program); - } - - programs.clear(); - - return result; - } -}; - -class FluidProgramSelect : public csound::OpcodeBase -{ - // Inputs. - MYFLT *iFluidSynth; - MYFLT *iChannelNumber; - MYFLT *iInstrumentNumber; - MYFLT *iBankNumber; - MYFLT *iPresetNumber; - // State. - fluid_synth_t *fluidSynth; - int32_t channel; - uint32_t instrument; - uint32_t bank; - uint32_t preset; - void *mutex; -public: - int32_t init(CSOUND *csound) - { - mutex = csound->Create_Mutex(0); - csound::LockGuard guard(csound, mutex); - toa(iFluidSynth, fluidSynth); - channel = (int32_t) *iChannelNumber; - instrument = (uint32_t) *iInstrumentNumber; - bank = (uint32_t) *iBankNumber; - preset = (uint32_t) *iPresetNumber; - fluid_synth_program_select(fluidSynth, - channel, - instrument, - bank, - preset); - return OK; - } -}; - -class FluidCCI : public csound::OpcodeBase { - // Inputs. - MYFLT *iFluidSynth; - MYFLT *iChannelNumber; - MYFLT *iControllerNumber; - MYFLT *kVal; - // State. - fluid_synth_t *fluidSynth; - int32_t channel; - int32_t controller; - int32_t value; - void *mutex; - -public: - int32_t init(CSOUND *csound) { - mutex = csound->Create_Mutex(0); - csound::LockGuard guard(csound, mutex); - toa(iFluidSynth, fluidSynth); - channel = (int32_t)*iChannelNumber; - controller = (int32_t)*iControllerNumber; - value = (int32_t)*kVal; - fluid_synth_cc(fluidSynth, channel, controller, value); - return OK; - } -}; - -class FluidCCK : public csound::OpcodeBase { - // Inputs. - MYFLT *iFluidSynth; - MYFLT *iChannelNumber; - MYFLT *iControllerNumber; - MYFLT *kVal; - // State. - fluid_synth_t *fluidSynth; - int32_t channel; - int32_t controller; - int32_t value; - int32_t priorValue; - void *mutex; - -public: - int32_t init(CSOUND *csound) { - mutex = csound->Create_Mutex(0); - csound::LockGuard guard(csound, mutex); - toa(iFluidSynth, fluidSynth); - priorValue = -1; - return OK; - } - int32_t kontrol(CSOUND *csound) { - csound::LockGuard guard(csound, mutex); - value = (int32_t)*kVal; - if (value != priorValue) { - priorValue = value; - channel = (int32_t)*iChannelNumber; - controller = (int32_t)*iControllerNumber; - fluid_synth_cc(fluidSynth, channel, controller, value); - } - return OK; - } -}; - -class FluidNote : public csound::OpcodeNoteoffBase { - // Inputs. - MYFLT *iFluidSynth; - MYFLT *iChannelNumber; - MYFLT *iMidiKeyNumber; - MYFLT *iVelocity; - // State. - fluid_synth_t *fluidSynth; - int32_t channel; - int32_t key; - int32_t velocity; - void *mutex; - -public: - int32_t init(CSOUND *csound) { - mutex = csound->Create_Mutex(0); - csound::LockGuard guard(csound, mutex); - toa(iFluidSynth, fluidSynth); - channel = (int32_t)*iChannelNumber; - key = (int32_t)*iMidiKeyNumber; - velocity = (int32_t)*iVelocity; - fluid_synth_noteon(fluidSynth, channel, key, velocity); - return OK; - } - int32_t noteoff(CSOUND *csound) { - csound::LockGuard guard(csound, mutex); - fluid_synth_noteoff(fluidSynth, channel, key); - return OK; - } -}; - -class FluidOut : public csound::OpcodeBase { - // Outputs. - MYFLT *aLeftOut; - MYFLT *aRightOut; - // Inputs. - MYFLT *iFluidSynth; - // State. - fluid_synth_t *fluidSynth; - float leftSample; - float rightSample; - int32_t frame; - int32_t ksmps; - void *mutex; - -public: - int32_t init(CSOUND *csound) { - mutex = csound->Create_Mutex(0); - csound::LockGuard guard(csound, mutex); - toa(iFluidSynth, fluidSynth); - ksmps = opds.insdshead->ksmps; - return OK; - } - int32_t audio(CSOUND *csound) { - csound::LockGuard guard(csound, mutex); - uint32_t offset = opds.insdshead->ksmps_offset; - uint32_t early = opds.insdshead->ksmps_no_end; - if (UNLIKELY(offset)) { - memset(aLeftOut, '\0', offset * sizeof(MYFLT)); - memset(aRightOut, '\0', offset * sizeof(MYFLT)); - } - if (UNLIKELY(early)) { - ksmps -= early; - memset(&aLeftOut[ksmps], '\0', early * sizeof(MYFLT)); - memset(&aRightOut[ksmps], '\0', early * sizeof(MYFLT)); - } - for (frame = offset; frame < ksmps; frame++) { - leftSample = 0.0f; - rightSample = 0.0f; - fluid_synth_write_float(fluidSynth, 1, &leftSample, 0, 1, &rightSample, 0, - 1); - aLeftOut[frame] = leftSample /* * csound->e0dbfs */; - aRightOut[frame] = rightSample /* * csound->e0dbfs */; - } - return OK; - } -}; - -class FluidAllOut : public csound::OpcodeBase { - // Outputs. - MYFLT *aLeftOut; - MYFLT *aRightOut; - // State. - float leftSample; - float rightSample; - int32_t frame; - int32_t ksmps; - void *mutex; - -public: - int32_t init(CSOUND *csound) { - mutex = csound->Create_Mutex(0); - csound::LockGuard guard(csound, mutex); - ksmps = opds.insdshead->ksmps; - return OK; - } - int32_t audio(CSOUND *csound) { - csound::LockGuard guard(csound, mutex); - uint32_t offset = opds.insdshead->ksmps_offset; - uint32_t early = opds.insdshead->ksmps_no_end; - if (UNLIKELY(offset)) { - memset(aLeftOut, '\0', offset * sizeof(MYFLT)); - memset(aRightOut, '\0', offset * sizeof(MYFLT)); - } - if (UNLIKELY(early)) { - ksmps -= early; - memset(&aLeftOut[ksmps], '\0', early * sizeof(MYFLT)); - memset(&aRightOut[ksmps], '\0', early * sizeof(MYFLT)); - } - std::vector *fluid_synths = 0; - csound::QueryGlobalPointer(csound, "fluid_synths", fluid_synths); - void *fluid_synths_mutex = 0; - csound::QueryGlobalPointer(csound, "fluid_synths_mutex", - fluid_synths_mutex); - csound::LockGuard synthsGuard(csound, fluid_synths_mutex); - for (frame = offset; frame < ksmps; frame++) { - aLeftOut[frame] = FL(0.0); - aRightOut[frame] = FL(0.0); - for (size_t i = 0, n = fluid_synths->size(); i < n; i++) { - fluid_synth_t *fluidSynth = (*fluid_synths)[i]; - leftSample = FL(0.0); - rightSample = FL(0.0); - fluid_synth_write_float(fluidSynth, 1, &leftSample, 0, 1, &rightSample, - 0, 1); - aLeftOut[frame] += (MYFLT)leftSample /* * csound->e0dbfs */; - aRightOut[frame] += (MYFLT)rightSample /* * csound->e0dbfs */; - } - } - return OK; - } -}; - -class FluidControl : public csound::OpcodeBase { - // Inputs. - MYFLT *iFluidSynth; - MYFLT *kMidiStatus; - MYFLT *kMidiChannel; - MYFLT *kMidiData1; - MYFLT *kMidiData2; - MYFLT *imsgs; - // State. - fluid_synth_t *fluidSynth; - int32_t midiStatus; - int32_t midiChannel; - int32_t midiData1; - int32_t midiData2; - int32_t priorMidiStatus; - int32_t priorMidiChannel; - int32_t priorMidiData1; - int32_t priorMidiData2; - int32_t printMsgs; - void *mutex; - -public: - int32_t init(CSOUND *csound) { - mutex = csound->Create_Mutex(0); - csound::LockGuard guard(csound, mutex); - toa(iFluidSynth, fluidSynth); - priorMidiStatus = -1; - priorMidiChannel = -1; - priorMidiData1 = -1; - priorMidiData2 = -1; - OPARMS oparms; - csound->GetOParms(csound, &oparms); - printMsgs = (*imsgs == 0 ? 0 : 1); - return OK; - } - int32_t kontrol(CSOUND *csound) { - csound::LockGuard guard(csound, mutex); - midiStatus = 0xF0 & (int32_t)*kMidiStatus; - midiChannel = (int32_t)*kMidiChannel; - midiData1 = (int32_t)*kMidiData1; - midiData2 = (int32_t)*kMidiData2; - int32_t result = -1; - - if (midiData2 != priorMidiData2 || midiData1 != priorMidiData1 || - midiChannel != priorMidiChannel || midiStatus != priorMidiStatus) { - switch (midiStatus) { - case (int32_t)0x80: - noteOff: - result = fluid_synth_noteoff(fluidSynth, midiChannel, midiData1); - if (printMsgs) - csound->Message(csound, Str("result: %d\n Note off: c:%3d k:%3d\n"), - result, midiChannel, midiData1); - break; - case (int32_t)0x90: - if (!midiData2) { - goto noteOff; - } - result = - fluid_synth_noteon(fluidSynth, midiChannel, midiData1, midiData2); - if (printMsgs) - log(csound, "result: %d\nNote on: c:%3d k:%3d v:%3d\n", result, - midiChannel, midiData1, midiData2); - break; - case (int32_t)0xA0: - if (printMsgs) - log(csound, "Key pressure (not handled): " - "c:%3d k:%3d v:%3d\n", - midiChannel, midiData1, midiData2); - break; - case (int32_t)0xB0: - result = fluid_synth_cc(fluidSynth, midiChannel, midiData1, midiData2); - if (printMsgs) - log(csound, "Result: %d Control change: c:%3d c:%3d v:%3d\n", result, - midiChannel, midiData1, midiData2); - break; - case (int32_t)0xC0: - result = fluid_synth_program_change(fluidSynth, midiChannel, midiData1); - if (printMsgs) - log(csound, "Result: %d Program change: c:%3d p:%3d\n", result, - midiChannel, midiData1); - break; - case (int32_t)0xD0: - if (printMsgs) - log(csound, "After touch (not handled): c:%3d v:%3d\n", midiChannel, - midiData1); - break; - case (int32_t)0xE0: { - int32_t pbVal = midiData1 + (midiData2 << 7); - fluid_synth_pitch_bend(fluidSynth, midiChannel, pbVal); - if (printMsgs) - log(csound, "Result: %d, Pitch bend: c:%d b:%d\n", result, - midiChannel, pbVal); - } break; - case (int32_t)0xF0: - if (printMsgs) - log(csound, "System exclusive (not handled): " - "c:%3d v1:%3d v2:%3d\n", - midiChannel, midiData1, midiData2); - break; - } - priorMidiStatus = midiStatus; - priorMidiChannel = midiChannel; - priorMidiData1 = midiData1; - priorMidiData2 = midiData2; - } - return OK; - } -}; - -class FluidSetInterpMethod : public csound::OpcodeBase { - // Inputs. - MYFLT *iFluidSynth; - MYFLT *iChannelNumber; - MYFLT *iInterpMethod; - // State. - fluid_synth_t *fluidSynth; - int32_t channel; - int32_t interpolationMethod; - void *mutex; - -public: - int32_t init(CSOUND *csound) { - mutex = csound->Create_Mutex(0); - csound::LockGuard guard(csound, mutex); - toa(iFluidSynth, fluidSynth); - channel = (int32_t)*iChannelNumber; - interpolationMethod = (int32_t)*iInterpMethod; - if (UNLIKELY(interpolationMethod != 0 && interpolationMethod != 1 && - interpolationMethod != 4 && interpolationMethod != 7)) { - return csound->InitError(csound, "%s", - Str("Illegal Interpolation Method: Must be " - "either 0, 1, 4, or 7.\n")); - } else { - fluid_synth_set_interp_method(fluidSynth, channel, interpolationMethod); - } - return OK; - } -}; - -static OENTRY localops[] = { - {(char *)"fluidEngine", sizeof(FluidEngine), 0, 1, (char *)"i", - (char *)"ppoo", (SUBR)&FluidEngine::init_, (SUBR)0, (SUBR)0}, - {(char *)"fluidLoad", sizeof(FluidLoad), 0, 1, (char *)"i", (char *)"Tio", - (SUBR)&FluidLoad::init_, (SUBR)0, (SUBR)0}, - {(char *)"fluidInfo", sizeof(FluidInfo), 0, 1, (char *)"S[]", (char *)"i", - (SUBR)&FluidInfo::init_, (SUBR)0, (SUBR)0}, - {(char *)"fluidProgramSelect", sizeof(FluidProgramSelect), 0, 1, (char *)"", - (char *)"iiiii", (SUBR)&FluidProgramSelect::init_, (SUBR)0, (SUBR)0}, - {(char *)"fluidCCi", sizeof(FluidCCI), 0, 1, (char *)"", (char *)"iiii", - (SUBR)&FluidCCI::init_, (SUBR)0, (SUBR)0}, - {(char *)"fluidCCk", sizeof(FluidCCK), 0, 3, (char *)"", (char *)"ikkk", - (SUBR)&FluidCCK::init_, (SUBR)&FluidCCK::kontrol_, (SUBR)0}, - {(char *)"fluidNote", sizeof(FluidNote), 0, 3, (char *)"", (char *)"iiii", - (SUBR)&FluidNote::init_, (SUBR)&FluidNote::kontrol_, (SUBR)0}, - {(char *)"fluidOut", sizeof(FluidOut), 0, 3, (char *)"aa", (char *)"i", - (SUBR)&FluidOut::init_, (SUBR)&FluidOut::audio_}, - {(char *)"fluidAllOut", sizeof(FluidAllOut), 0, 3, (char *)"aa", (char *)"", - (SUBR)&FluidAllOut::init_, (SUBR)&FluidAllOut::audio_}, - {(char *)"fluidControl", sizeof(FluidControl), 0, 3, (char *)"", - (char *)"ikkkkp", (SUBR)FluidControl::init_, (SUBR)FluidControl::kontrol_, - (SUBR)0}, - {(char *)"fluidSetInterpMethod", sizeof(FluidSetInterpMethod), 0, 1, - (char *)"", (char *)"iii", (SUBR)&FluidSetInterpMethod::init_, (SUBR)0, - (SUBR)0}, - {0, 0, 0, 0, 0, 0, (SUBR)0, (SUBR)0, (SUBR)0}}; - -PUBLIC int32_t csoundModuleCreate(CSOUND *csound) { - std::vector *fluid_synths = - new std::vector(); - int32_t result = 0; - result = csound::CreateGlobalPointer(csound, "fluid_synths", fluid_synths); - void *fluid_synths_mutex = csound->Create_Mutex(0); - result = csound::CreateGlobalPointer(csound, "fluid_synths_mutex", - fluid_synths_mutex); - return result; -} - -PUBLIC int32_t csoundModuleInit(CSOUND *csound) { - // printf("csoundModuleInit: %p\n", csound); - OENTRY *ep; - int32_t err = 0; - - for (ep = (OENTRY *)&(localops[0]); ep->opname != NULL; ep++) { - err |= csound->AppendOpcode(csound, ep->opname, ep->dsblksiz, ep->flags, - ep->thread, ep->outypes, ep->intypes, - (int32_t (*)(CSOUND *, void *))ep->iopadr, - (int32_t (*)(CSOUND *, void *))ep->kopadr, - (int32_t (*)(CSOUND *, void *))ep->aopadr); - } - return err; -} - -/** - * Called by Csound to de-initialize the opcode - * just before destroying it. - */ -PUBLIC int32_t csoundModuleDestroy(CSOUND *csound) { - // printf("csoundModuleDestroy: %p\n", csound); - void *fluid_synths_mutex = 0; - csound::QueryGlobalPointer(csound, "fluid_synths_mutex", fluid_synths_mutex); - if (fluid_synths_mutex) { - std::vector *fluid_synths = 0; - csound::QueryGlobalPointer(csound, "fluid_synths", fluid_synths); - csound->LockMutex(fluid_synths_mutex); - if (fluid_synths) { - for (size_t i = 0, n = fluid_synths->size(); i < n; i++) { - fluid_synth_t *fluidSynth = (*fluid_synths)[i]; - // printf("deleting engine: %p\n", fluidSynth); - fluid_settings_t *fluidSettings = fluid_synth_get_settings(fluidSynth); - delete_fluid_synth(fluidSynth); - delete_fluid_settings(fluidSettings); - } - fluid_synths->clear(); - delete fluid_synths; - fluid_synths = 0; - } - csound->UnlockMutex(fluid_synths_mutex); - csound->DestroyMutex(fluid_synths_mutex); - fluid_synths_mutex = 0; - } - return 0; -} - -PUBLIC int32_t csoundModuleInfo(void) { - return ((CS_APIVERSION << 16) + (CS_APISUBVER << 8) + (int32_t)sizeof(MYFLT)); -} diff --git a/6.x/src/fluidOpcodes/fluidOpcodes.h b/6.x/src/fluidOpcodes/fluidOpcodes.h deleted file mode 100644 index 4441ded0..00000000 --- a/6.x/src/fluidOpcodes/fluidOpcodes.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * FLUID SYNTH OPCODES - * - * Adapts Fluidsynth to use global engines, soundFonts, and outputs - * - * Based on work by Michael Gogins. License is identical to - * SOUNDFONTS VST License (listed below) - * - * Copyright (c) 2003 by Steven Yi. All rights reserved. - * - * [ORIGINAL INFORMATION BELOW] - * - * S O U N D F O N T S V S T - * - * Adapts Fluidsynth to be both a VST plugin instrument - * and a Csound plugin opcode. - * Copyright (c) 2001-2003 by Michael Gogins. All rights reserved. - * - * L I C E N S E - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifndef FLUIDOPCODE_H -#define FLUIDOPCODE_H - -#include "csdl.h" -#include - -typedef struct { - OPDS h; - /* OUTPUTS */ - MYFLT *iEngineNum; - MYFLT *iReverbEnabled; - MYFLT *iChorusEnabled; - MYFLT *iNumChannels; - MYFLT *iPolyphony; -} FLUIDENGINE; - -typedef struct { - OPDS h; - /* INPUTS */ - MYFLT *iEngineNumber, *iChannelNumber, *iInterpMethod; -} FLUID_SET_INTERP_METHOD; - -typedef struct { - OPDS h; - /* OUTPUTS */ - MYFLT *iInstrumentNumber; - /* INPUTS */ - MYFLT *filename, *iEngineNum, *iListPresets; -} FLUIDLOAD; - -typedef struct { - OPDS h; - /* INPUTS */ - MYFLT *iEngineNumber, *iChannelNumber, *iInstrumentNumber, *iBankNumber; - MYFLT *iPresetNumber; -} FLUID_PROGRAM_SELECT; - -typedef struct { - OPDS h; - /* INPUTS */ - MYFLT *iEngineNumber, *iChannelNumber, *iControllerNumber, *kVal; - int32_t priorMidiValue; - fluid_synth_t *fluidEngine; -} FLUID_CC; - -typedef struct { - OPDS h; - /* INPUTS */ - MYFLT *iEngineNumber, *iChannelNumber, *iMidiKeyNumber, *iVelocity; - int32_t initDone, iChn, iKey; - fluid_synth_t *fluidEngine; -} FLUID_NOTE; - -typedef struct { - OPDS h; - MYFLT *aLeftOut, *aRightOut; - MYFLT *iEngineNum; - fluid_synth_t *fluidEngine; -} FLUIDOUT; - -typedef struct { - OPDS h; - MYFLT *aLeftOut, *aRightOut; - void *fluidGlobals; -} FLUIDALLOUT; - -typedef struct { - OPDS h; - /* Inputs. */ - MYFLT *iFluidEngine; - MYFLT *kMidiStatus; - MYFLT *kMidiChannel; - MYFLT *kMidiData1; - MYFLT *kMidiData2; - /* No outputs. */ - /* Internal state. */ - int32_t priorMidiStatus; - int32_t priorMidiChannel; - int32_t priorMidiData1; - int32_t priorMidiData2; - fluid_synth_t *fluidEngine; -} FLUIDCONTROL; - -#endif - diff --git a/6.x/src/fluidOpcodes/fluidOpcodes.html b/6.x/src/fluidOpcodes/fluidOpcodes.html deleted file mode 100644 index 72d3db4e..00000000 --- a/6.x/src/fluidOpcodes/fluidOpcodes.html +++ /dev/null @@ -1,80 +0,0 @@ - - -fluidOpcodes - - - -

fluidOpcodes

-
fluidEngine - creates a fluid engine
- fluidLoad - loads a soundfont into a fluid engine
- fluidProgramSelect - assign a bank and preset of a soundFont to a midi channel - as well as select
- fluidCC - send midi controller data to fluid
- fluidPlay - plays a note on a channel
- fluidOut - outputs sound from a fluid engine
-
-

Description

-

This family of opcodes are meant to be used together to load and play SoundFonts - using Peter Hannape's Fluidsynth. Use global engines, soundFonts, and outputs.
-
- Based on work by Michael Gogins.

- -
-
-

Syntax

-

iEngineNumber fluidEngine
-
- iInstrumentNumber fluidLoad sfilename, iEngineNumber
-
- fluidProgramSelect iEngineNumber, iChannelNumber, iInstrumentNumber, - iBankNumber, iPresetNumber
-
- fluidCC iEngineNumber, iChannelNumber, iControllerNumber, - kValue
-
- fluidNote iEngineNumber, iInstrumentNumber, iMidiKeyNumber, - iVelocity
-
- aLeft, aRight fluidOut iEngineNum

-

 

-
-

Initialization

-

iEngineNumber - engine number assigned from fluid_engine
-
- iInstrumentNumber - instrument number assigned from fluid_load
-
- sfilename - String specifying a SoundFont filename
-
- aLeft - left channel audio output.

-

aRight - right channel audio output.
-
- iMidiKeyNumber - midi key number to play (0-127)
-
- iVelocity - midi velocity to play at (0-127)
-
- iBankNum - bank number on soundfont to play
-
- iPresetNum - preset number on soundfont to play
-
- iprogram - Number of the fluidsynth program to be assigned to a MIDI channel.

-
-

Performance

-

In this implementation, SoundFont effects such as chorus or reverb are used - if and only if they are defaults for the preset. There is no means of turning - such effects on or off, or of changing their parameters, from Csound.

- -

 

-
-

Examples

-
-

 

-
-
-
-

Credits

-

Opcode library created by Steven Yi, based on fluid opcode - plugin by Michael Gogins.

-

 

-
- - diff --git a/6.x/src/fluidOpcodes/test.csd b/6.x/src/fluidOpcodes/test.csd deleted file mode 100644 index f6f8fd05..00000000 --- a/6.x/src/fluidOpcodes/test.csd +++ /dev/null @@ -1,58 +0,0 @@ - - - - - - - -sr=44100 -kr=441 -ksmps=100 -nchnls=2 - -giFluidEngine fluid_engine - -giSF_piano fluid_load "sf2/Piano Grand Steinway V1.0b (29,651KB).SF2", giFluidEngine -giSF_mutedViolin fluid_load "sf2/MutedViolin.sf2", giFluidEngine - -fluid_program_select giFluidEngine, 0, giSF_piano, 0, 0 -fluid_program_select giFluidEngine, 1, giSF_mutedViolin, 0, 0 - - instr 1 ;untitled - - fluid_play giFluidEngine, 0, p4, p5 - - endin - - instr 2 - - fluid_play giFluidEngine, 1, p4, p5 - - endin - - instr 10 ; fluid out instrument - -aleft, aright fluid_out giFluidEngine - - outs aleft * 0dbfs, aright * 0dbfs - endin - - - - - -i1 0 2 60 110 -i1 0 2 64 110 -i1 0 2 67 110 - -i2 0 1 72 110 -i2 0 1 76 70 -i2 0 1 79 80 - -i10 0 6 - -e - - - - diff --git a/6.x/src/hdf5/CMakeLists.txt b/6.x/src/hdf5/CMakeLists.txt deleted file mode 100644 index 7ea0f1a9..00000000 --- a/6.x/src/hdf5/CMakeLists.txt +++ /dev/null @@ -1,31 +0,0 @@ -option(BUILD_HDF5_OPCODES "Build the hdf5 read/write opcodes" ON) - -find_package(HDF5) -check_deps(BUILD_HDF5_OPCODES HDF5_FOUND) - -if(BUILD_HDF5_OPCODES AND HDF5_FOUND) - # split version string, as cmake doesn't do this automatically - string(REPLACE "." ";" VERSION_LIST ${HDF5_VERSION}) - list(GET VERSION_LIST 0 HDF5_VERSION_MAJOR) - list(GET VERSION_LIST 1 HDF5_VERSION_MINOR) - list(GET VERSION_LIST 2 HDF5_VERSION_PATCH) - - make_plugin(hdf5ops HDF5IO.c) - - target_compile_definitions(hdf5ops PRIVATE - -DHDF5_VERSION_MAJOR=${HDF5_VERSION_MAJOR} - -DHDF5_VERSION_MINOR=${HDF5_VERSION_MINOR} - -DHDF5_VERSION_PATCH=${HDF5_VERSION_PATCH} - ) - - if(USE_VCPKG) - target_include_directories(hdf5ops PRIVATE ${HDF5_INCLUDE_DIRS} ${CSOUND_INCLUDE_DIRS}) - target_link_libraries(hdf5ops PRIVATE hdf5::hdf5-static hdf5::hdf5_hl-static) - else() - message(STATUS "HDF5_INCLUDE_DIRS: ${HDF5_INCLUDE_DIRS}") - message(STATUS "HDF5_LIBRARIES: ${HDF5_LIBRARIES}") - - target_include_directories(hdf5ops PRIVATE ${HDF5_INCLUDE_DIRS} ${CSOUND_INCLUDE_DIRS}) - target_link_libraries(hdf5ops ${HDF5_LIBRARIES}) - endif() -endif() diff --git a/6.x/src/image/CMakeLists.txt b/6.x/src/image/CMakeLists.txt deleted file mode 100644 index 76123b24..00000000 --- a/6.x/src/image/CMakeLists.txt +++ /dev/null @@ -1,15 +0,0 @@ -# Build image opcodes -option(BUILD_IMAGE_OPCODES "Build the image opcodes" ON) - -if(${BUILD_IMAGE_OPCODES}) - find_package(PNG) - check_deps(BUILD_IMAGE_OPCODES PNG_FOUND) - - if(BUILD_IMAGE_OPCODES) - make_plugin(image imageOpcodes.c) - target_include_directories(image PRIVATE ${CSOUND_INCLUDE_DIRS}) - target_include_directories(image PRIVATE ${PNG_INCLUDE_DIR}) - target_link_libraries(image ${PNG_LIBRARIES}) - target_compile_definitions(image PRIVATE ${PNG_DEFINITIONS}) - endif() -endif() diff --git a/6.x/src/jackops/CMakeLists.txt b/6.x/src/jackops/CMakeLists.txt deleted file mode 100644 index c5ff8411..00000000 --- a/6.x/src/jackops/CMakeLists.txt +++ /dev/null @@ -1,23 +0,0 @@ -option(BUILD_JACK_OPCODES "Build the JACK related opcodes" ON) - -find_library(JACK_LIB NAMES jack) -find_path (JACK_INCLUDE_DIR jack/jack.h HINTS /usr/local/include) - -check_deps(BUILD_JACK_OPCODES JACK_LIB) - -if(BUILD_JACK_OPCODES AND JACK_LIB) - - list(APPEND jack_LIBS ${JACK_LIB}) - - make_plugin(jacko jacko.cpp "${jack_LIBS}") - target_include_directories(jacko PRIVATE ${CSOUND_INCLUDE_DIRS}) - target_include_directories(jacko PRIVATE ${JACK_INCLUDE_DIR}) - set_property(TARGET jacko PROPERTY CXX_STANDARD 20) - set_property(TARGET jacko PROPERTY CXX_STANDARD_REQUIRED On) - set_property(TARGET jacko PROPERTY CXX_EXTENSIONS Off) - - make_plugin(jackTransport jackTransport.c "${jack_LIBS}") - target_include_directories(jackTransport PRIVATE ${CSOUND_INCLUDE_DIRS}) - target_include_directories(jackTransport PRIVATE ${JACK_INCLUDE_DIR}) - -endif() diff --git a/6.x/src/jackops/OpcodeBase.hpp b/6.x/src/jackops/OpcodeBase.hpp deleted file mode 100644 index 21ca8a0c..00000000 --- a/6.x/src/jackops/OpcodeBase.hpp +++ /dev/null @@ -1,492 +0,0 @@ -/* - OpcodeBase.hpp: - - Copyright (C) 2005, 2009, 2017 by Istva Varga, Victor Lazzarini and - Michael Gogins - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ -#pragma once - -/** - * There is a conflict between the preprocessor definition "_CR" in the - * standard C++ library and in Csound. To work around this, undefine "_CR" and - * include ALL standard library include files BEFORE including ANY Csound - * include files. - */ -#if defined(_CR) -#undef _CR -#endif - -#include -#include -#include -#if !(defined(__wasi__)) -#include -#endif -#include -#include -#include -#include - -/** - * Template base class, or pseudo-virtual base class, - * for writing Csound opcodes in C++. - * Derive opcode implementation classes like this: - * - * DerivedClass : public OpcodeBase - * { - * public: - * // All output fields must be declared first as MYFLT *: - * MYFLT *aret1; - * // All input fields must be declared next as MYFLT *: - * MYFLT *iarg1; - * MYFLT *karg2; - * MYFLT *aarg3; - * // All internal state variables must be declared after that: - * size_t state1; - * double state2; - * MYFLT state3; - * // If the opcode shares data protect it by creating one or more void - * // *mutex member pointers: - * void *mutex1; - * void *mutex2; - * // and create them usind csound->Create_Mutex() in csoundModuleCreate - * // and destroy them using csound->DeleteMutex(mutex) - * // csoundModuleDestroy, and lock them using LockGuard guard(mutex). - * int init(); - * int kontrol(); - * int audio; - * int noteoff(); - * void deinit(); - * }; - */ - -namespace csound { - -static bool diagnostics_enabled = false; - -/** - * Use this to guard against data races in opcode functions. The mutex should - * be created by csound->Create_Mutex() in csoundModuleCreate(), and should - * be destroyed by csound->DeleteMutex() in csoundModuleDestroy(). - * - * If data is shared between opcode instances, the mutex should be global to - * the opcode library; if data is shared between threads for a single instance, - * the mutex should belong to the opcode instance. - */ -struct LockGuard { - LockGuard(CSOUND *csound_, void *mutex_) : csound(csound_), mutex(mutex_) - { - csound->LockMutex(mutex); - } - ~LockGuard() - { - csound->UnlockMutex(mutex); - } - CSOUND *csound; - void *mutex; -}; - -/** - * Use this to store a pointer to a global heap-allocated object, e.g. one - * used to manage state between opcode instances. - */ - -template int CreateGlobalPointer(CSOUND *csound, const char *name, T *pointer) -{ - T **pointer_to_pointer = 0; - int result = csound->CreateGlobalVariable(csound, name, sizeof(pointer_to_pointer)); - pointer_to_pointer = static_cast(csound->QueryGlobalVariable(csound, name)); - *pointer_to_pointer = pointer; - return result; -} - -/** - * Retrieve a pointer to a global heap-allocated object, e.g. one - * used to manage state between opcode instances. - */ -template T *QueryGlobalPointer(CSOUND *csound, const char *name, T*& pointer) -{ - T **pointer_to_pointer = static_cast(csound->QueryGlobalVariableNoCheck(csound, name)); - if (pointer_to_pointer != 0) { - pointer = *pointer_to_pointer; - } else { - pointer = 0; - } - return pointer; -} - -template -class OpcodeBase -{ -public: - int init(CSOUND *csound) - { - return NOTOK; - } - static int init_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->init(csound); - } - int kontrol(CSOUND *csound) - { - return NOTOK; - } - static int kontrol_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->kontrol(csound); - } - int audio(CSOUND *csound) - { - return NOTOK; - } - static int audio_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->audio(csound); - } - /** - This is how to compute audio signals for normal opcodes: - (1) Zero all frames from 0 up to but not including Offset. - (2) Compute all frames from ksmps_offset up to but not including End. - (3) Zero all frames from End up to but not including ksmps. - Example from a C opcode: - uint32_t offset = p->h.insdshead->ksmps_offset; - uint32_t early = p->h.insdshead->ksmps_no_end; - uint32_t n, nsmps = CS_KSMPS; - if (UNLIKELY(offset)) memset(p->r, '\0', offset*sizeof(MYFLT)); - if (UNLIKELY(early)) { - nsmps -= early; - memset(&p->r[nsmps], '\0', early*sizeof(MYFLT)); - } - for (n = offset; n < nsmps; n++) { - input1 = MYFLT2LRND(p->a[n]); - p->r[n] = (MYFLT) (input1 >> input2); - } - So in C++ it should look like this (which is much easier to understand): - int frameIndex = 0; - for( ; frameIndex < kperiodOffset(); ++frameIndex) { - asignal[frameIndex] = 0; - } - for( ; frameIndex < kperiodEnd(); ++frameIndex) { - asignal[frameIndex] = compute(); - } - for( ; frameIndex < ksmps(); ++frameIndex) { - asignal[frameIndex] = 0; - } - */ - uint32_t kperiodOffset() const - { - return opds.insdshead->ksmps_offset; - } - uint32_t kperiodEnd() const - { - uint32_t end = opds.insdshead->ksmps_no_end; - if (end) { - return end; - } else { - return ksmps(); - } - } - uint32_t ksmps() const - { - return opds.insdshead->ksmps; - } - uint32_t output_arg_count() - { - return (uint32_t)opds.optext->t.outArgCount; - } - uint32_t input_arg_count() - { - return (uint32_t)opds.optext->t.inArgCount; - } - void log(CSOUND *csound, const char *format,...) - { - va_list args; - va_start(args, format); - if(csound) { - csound->MessageV(csound, 0, format, args); - } else { - vfprintf(stdout, format, args); - } - va_end(args); - } - void warn(CSOUND *csound, const char *format,...) - { - if(csound) { - if(csound->GetMessageLevel(csound) & CS_WARNMSG) { - va_list args; - va_start(args, format); - csound->MessageV(csound, CSOUNDMSG_WARNING, format, args); - va_end(args); - } - } else { - va_list args; - va_start(args, format); - vfprintf(stdout, format, args); - va_end(args); - } - } - OPDS opds; -}; - -template -class OpcodeNoteoffBase -{ -public: - int init(CSOUND *csound) - { - IGN(csound); - return NOTOK; - } - static int init_(CSOUND *csound, void *opcode) - { - if (!csound->GetReinitFlag(csound) && !csound->GetTieFlag(csound)) { - csound->RegisterDeinitCallback(csound, opcode, - &OpcodeNoteoffBase::noteoff_); - } - return reinterpret_cast(opcode)->init(csound); - } - int kontrol(CSOUND *csound) - { - IGN(csound); - return NOTOK; - } - static int kontrol_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->kontrol(csound); - } - int audio(CSOUND *csound) - { - IGN(csound); - return NOTOK; - } - static int audio_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->audio(csound); - } - /** - This is how to compute audio signals for normal opcodes: - (1) Zero all frames from 0 up to but not including Offset. - (2) Compute all frames from ksmps_offset up to but not including End. - (3) Zero all frames from End up to but not including ksmps. - Example from a C opcode: - uint32_t offset = p->h.insdshead->ksmps_offset; - uint32_t early = p->h.insdshead->ksmps_no_end; - uint32_t n, nsmps = CS_KSMPS; - if (UNLIKELY(offset)) memset(p->r, '\0', offset*sizeof(MYFLT)); - if (UNLIKELY(early)) { - nsmps -= early; - memset(&p->r[nsmps], '\0', early*sizeof(MYFLT)); - } - for (n = offset; n < nsmps; n++) { - input1 = MYFLT2LRND(p->a[n]); - p->r[n] = (MYFLT) (input1 >> input2); - } - So in C++ it should look like this (which is much easier to understand): - int frameIndex = 0; - for( ; frameIndex < kperiodOffset(); ++frameIndex) { - asignal[frameIndex] = 0; - } - for( ; frameIndex < kperiodEnd(); ++frameIndex) { - asignal[frameIndex] = compute(); - } - for( ; frameIndex < ksmps(); ++frameIndex) { - asignal[frameIndex] = 0; - } - */ - uint32_t kperiodOffset() const - { - return opds.insdshead->ksmps_offset; - } - uint32_t kperiodEnd() const - { - uint32_t end = opds.insdshead->ksmps_no_end; - if (end) { - return end; - } else { - return ksmps(); - } - } - uint32_t ksmps() const - { - return opds.insdshead->ksmps; - } - uint32_t output_arg_count() - { - return (uint32_t)opds.optext->t.outArgCount; - } - uint32_t input_arg_count() - { - return (uint32_t)opds.optext->t.inArgCount; - } - void log(CSOUND *csound, const char *format,...) - { - va_list args; - va_start(args, format); - if(csound) { - csound->MessageV(csound, 0, format, args); - } else { - vfprintf(stdout, format, args); - } - va_end(args); - } - void warn(CSOUND *csound, const char *format,...) - { - if(csound) { - if(csound->GetMessageLevel(csound) & CS_WARNMSG) { - va_list args; - va_start(args, format); - csound->MessageV(csound, CSOUNDMSG_WARNING, format, args); - va_end(args); - } - } else { - va_list args; - va_start(args, format); - vfprintf(stdout, format, args); - va_end(args); - } - } - int noteoff(CSOUND *csound) - { - return OK; - } - static int noteoff_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->noteoff(csound); - } - OPDS opds; -}; - -#if (__cplusplus >= 201103L) && !(defined(__wasi__)) -#pragma message("Defining heap_object_manager_t.") - -/** - * The memory of non-POD C++ or C objects allocated on the heap by Csound - * plugins is managed not by Csound, but by the plugin module. This class - * performs that function. - * - * As long as all memory is allocated in or after csoundModuleCreate and - * de-allocated in csoundModuleDestroy, all will be well. The danger however - * is that memory leaks will occur if allocations are not matched with - * de-allocations, or worse, that crashes will occur if any objects are - * deleted twice or de-referenced after deletion. - * - * So, the plugin should manage a collection of all heap-allocated objects, - * keyed first off the Csound instance, and second off an object handle; and - * all such objects must be accessed only by handle, from this collection. - * - * In csoundModuleDestroy, all objects for the calling instance of Csound - * should be de-allocated, and the sub-collection for that instance should - * also be de-allocated. - * - * If then there are no more sub-collections for other instances of Csound, - * the entire contents of the collection should be de-allocated. - * - * This should be done as a singleton, and in a thread-safe way. - * - * There might still be a problem if any of the heap-allocated objects, - * themselves, have global static members, perhaps created by some external - * dependency. - * - * Please note, objects O in the declaration below must not be std::shared_ptr - * objects. Deletion of objects herein must be unconditional. Rather, the - * objects passed are raw pointers to instances of class O. - */ - template class heap_object_manager_t { - private: - std::map> objects_; - std::recursive_mutex mutex; - heap_object_manager_t(){}; - ~heap_object_manager_t(){}; - public: - static heap_object_manager_t &instance() { - static heap_object_manager_t singleton; - return singleton; - } - std::map> &objects() { - std::lock_guard lock(mutex); - return objects_; - } - /** - * Returns a list of pointers to all objects allocated for this - * instance of Csound. - */ - std::vector &objects_for_csound(CSOUND *csound) { - std::lock_guard lock(mutex); - auto &objects_for_csound_ = objects()[csound]; - return objects_for_csound_; - } - /** - * Returns the handle for the object; if the object has not yet been - * stored, inserts it into the list of object pointers for this - * instance of Csound; otherwise, returns the handle of the stored - * object pointer. - */ - int handle_for_object(CSOUND *csound, O *object) { - std::lock_guard lock(mutex); - auto &objects_for_csound_ = objects_for_csound(csound); - auto iterator = std::find(objects_for_csound_.begin(), objects_for_csound_.end(), object); - if (iterator == objects_for_csound_.end()) { - int handle = objects_for_csound_.size(); - objects_for_csound_.push_back(object); - if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::handle_for_object %p: new object handle: %d (of %ld)\n", object, handle, objects_for_csound_.size()); - return handle; - } else { - int handle = static_cast(iterator - objects_for_csound_.begin()); - if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::handle_for_object: existing object handle: %d\n", handle); - return handle; - } - } - /** - * Returns the object pointer for the handle; - * if the object pointer has not been stored by - * handle, returns a null pointer. - */ - O *object_for_handle(CSOUND *csound, int handle) { - std::lock_guard lock(mutex); - auto &objects_for_csound_ = objects_for_csound(csound); - if (handle >= objects_for_csound_.size()) { - return nullptr; - } - O *object = objects_for_csound_[handle]; - if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::object_for_handle: %p %d (of %ld)\n", object, handle, objects_for_csound_.size()); - return object; - } - /** - * First destroys all objects created by the calling - * instance of Csound, then destroys the list of - * object pointers for this instance of Csound. - */ - void module_destroy(CSOUND *csound) { - std::lock_guard lock(mutex); - auto &objects_for_csound_ = objects_for_csound(csound); - for (int i = 0, n = objects_for_csound_.size(); i < n; ++i) { - delete objects_for_csound_[i]; - objects_for_csound_[i] = nullptr; - } - objects_for_csound_.clear(); - objects().erase(csound); - } -}; - -#else -#pragma message("Not defining heap_manager_t.") -#endif - -} diff --git a/6.x/src/jackops/cs_jack.h b/6.x/src/jackops/cs_jack.h deleted file mode 100644 index c79836a4..00000000 --- a/6.x/src/jackops/cs_jack.h +++ /dev/null @@ -1,67 +0,0 @@ -/* - .h: - - Copyright (C) 2008 by Cesare Marilungo - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#define MAX_NAME_LEN 32 /* for client and port name */ - -typedef struct RtJackBuffer_ { -#ifdef LINUX - pthread_mutex_t csndLock; /* signaled by process callback */ - pthread_mutex_t jackLock; /* signaled by audio thread */ -#else - void *csndLock; /* signaled by process callback */ - void *jackLock; /* signaled by audio thread */ -#endif - jack_default_audio_sample_t **inBufs; /* 'nChannels' capture buffers */ - jack_default_audio_sample_t **outBufs; /* 'nChannels' playback buffers */ -} RtJackBuffer; - -typedef struct RtJackGlobals_ { - CSOUND *csound; /* Csound instance pointer */ - int jackState; /* 0: OK, 1: sr changed, 2: quit */ - char clientName[MAX_NAME_LEN + 1]; /* client name */ - char inputPortName[MAX_NAME_LEN + 1]; /* input port name prefix */ - char outputPortName[MAX_NAME_LEN + 1]; /* output port name prefix */ - int sleepTime; /* sleep time in us (deprecated) */ - char *inDevName; /* device name for -i adc */ - char *outDevName; /* device name for -o dac */ - int sampleRate; /* sample rate in Hz */ - int nChannels; /* number of channels */ - int nChannels_i; /* number of in channels */ - int bufSize; /* buffer size in sample frames */ - int nBuffers; /* number of buffers (>= 2) */ - int inputEnabled; /* non-zero if capture (adc) is on */ - int outputEnabled; /* non-zero if playback (dac) is on */ - int csndBufCnt; /* current buffer in Csound thread */ - int csndBufPos; /* buffer position in Csound thread */ - int jackBufCnt; /* current buffer in JACK callback */ - int jackBufPos; /* buffer position in JACK callback */ - jack_client_t *client; /* JACK client pointer */ - jack_port_t **inPorts; /* 'nChannels' ports for capture */ - jack_default_audio_sample_t **inPortBufs; - jack_port_t **outPorts; /* 'nChannels' ports for playback */ - jack_default_audio_sample_t **outPortBufs; - RtJackBuffer **bufs; /* 'nBuffers' I/O buffers */ - int xrunFlag; /* non-zero if an xrun has occured */ - jack_client_t *listclient; - int outDevNum, inDevNum; /* select devs by number */ -} RtJackGlobals; diff --git a/6.x/src/jackops/jackTransport.c b/6.x/src/jackops/jackTransport.c deleted file mode 100644 index 03d0e79c..00000000 --- a/6.x/src/jackops/jackTransport.c +++ /dev/null @@ -1,98 +0,0 @@ -/** - * jack_transport.c - * - * Copyright (c) 2008 by Cesare Marilungo. All rights reserved. - * - * L I C E N S E - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * U S A G E - * - * jack_transport 1 - to start jack transport - * - * jack_transport 0 - to stop - * - * the second optional parameter tells where the transport should be located - * - */ - -#include -#include -#include - -#include "cs_jack.h" - -enum { STOP, START }; - -typedef struct -{ - OPDS h; - MYFLT *command; - MYFLT *location; -} JACKTRANSPORT; - -static int32_t jack_transport (CSOUND *csound, JACKTRANSPORT * p) -{ - RtJackGlobals *rtjack; - jack_client_t *client; - - rtjack = (RtJackGlobals*) - csound->QueryGlobalVariableNoCheck(csound, - "_rtjackGlobals"); - client = rtjack->client; - - if (UNLIKELY(client == NULL)) { - return csound->InitError(csound, "%s", Str("Cannot find Jack client.\n")); - } - else { - //move to specified location (in seconds) - if ((int32_t)(*p->location)>=0) { - MYFLT loc_sec = *p->location; - MYFLT loc_sec_per_sr = loc_sec*csound->GetSr(csound); - jack_transport_locate(client, loc_sec_per_sr); - csound->Warning(csound, - Str("jacktransport: playback head moved " - "at %f seconds\n"), loc_sec); - } - //start or stop - switch ((int32_t - )(*p->command)) { - case START: - csound->Warning(csound, "%s", Str("jacktransport: playing.\n")); - jack_transport_start(client); - break; - case STOP: - csound->Warning(csound, "%s", Str("jacktransport: stopped.\n")); - jack_transport_stop(client); - break; - default: - csound->Warning(csound, "%s", Str("jacktransport: invalid parameter.\n")); - break; - } - } - - return OK; -} - -#define S(x) sizeof(x) - -static OENTRY jackTransport_localops[] = { - - { "jacktransport", S(JACKTRANSPORT), 0, 1, "", "ij", - (SUBR)jack_transport, NULL, NULL }, -}; - -LINKAGE_BUILTIN(jackTransport_localops) diff --git a/6.x/src/jackops/jacko.cpp b/6.x/src/jackops/jacko.cpp deleted file mode 100644 index 14021b43..00000000 --- a/6.x/src/jackops/jacko.cpp +++ /dev/null @@ -1,1468 +0,0 @@ -/* -jacko.cpp: - -Copyright (C) 2010, 2023 by Michael Gogins - -This file is part of Csound. - -The Csound Library is free software; you can redistribute it -and/or modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -Csound is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with Csound; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -02110-1301 USA -*/ - -/** -* T H E J A C K O O P C O D E S -* Michael Gogins -* -* The Jacko opcodes can be used to connect any number of Csound instances, -* instruments, or user-defined opcodes to any number of Jack ports in any -* number of external Jack servers. Both audio and MIDI signal types are -* supported. -* -* Sending MIDI and/or audio to external Jack clients, such as synthesizers, -* and receiving audio and/or MIDI back from them, is supported. -* -* Receiving MIDI and/or audio from external Jack clients, such as sequencers, -* and sending MIDI and/or audio back to them, is supported. -* -* Other uses also are supported. -* -* The Jacko opcodes do not read or write the Csound input or output buffers; -* the opcodes read Jack inputs to Csound audio rate variables, and write -* audio rate variables to Jack outputs. This provides increased flexibility. -* -* NOTE: Using Jacko with `-+rtaudio=jack`is not advisable. -* -* O P C O D E S -* -* JackoInit -- Initializes Csound as a Jack client. -* -* Description -* -* Initializes this instance of Csound as a Jack client. -* -* Csound's sr must be equal to the Jack daemon's -* frames per second. -* -* Csound's ksmps must be equal to the Jack daemon's -* frames per period. -* -* Frames per period must not only (a) be a power of 2, -* but also (b) go evenly into the frames per second, -* e.g. 128 frames per period goes into 48000 -* frames per second 375 times, for a latency or -* MIDI time granularity of about 2.7 milliseconds -* (as good as or better than the absolute best -* human performers). -* -* The order of processing of all signals that pass -* from Jack input ports, through Csound processing, -* and to Jack output ports, must be properly -* determined by sequence of instrument and -* opcode definition within the Csound orchestra. -* -* Syntax -* -* JackoInit SclientName, ServerName -* -* Initialization -* -* SclientName -- The name of the Jack client; -* normally, should be "csound". -* -* ServerName -- The name of the Jack daemon; -* normally, will be "default". -* -* This opcode must be called once and only once in the -* orchestra header, and before any other Jack opcodes. -* If more than one instance of Csound is using the Jack -* opcodes at the same time, then each instance of Csound -* must use a different client name. -* -* -* JackoInfo -- Prints information about the Jack system. -* -* Description -* -* Prints the Jack daemon and client names, the -* sampling rate and frames per period, -* and all active Jack port names, -* types, states, and connections. -* -* Syntax -* -* JackoInfo -* -* Initialization -* -* May be called any number of times in the orchestra header, -* for example both before and after creating Jack ports -* in the Csound orchestra header. -* -* -* JackoFreewheel -- Turns freewheeling mode on or off. -* -* Description -* -* Turns Jack's freewheeling mode on or off. -* -* When freewheeling is on, if supported by the rest -* of the Jack system, Csound will run as fast as possible, -* which may be either faster or slower than real time. -* -* This is essential for rendering scores that are too -* dense for real-time jacko_is_driving to a soundfile, -* without xruns or dropouts. -* -* Syntax -* -* JackoFreewheel [ienabled] -* -* Initialization -* -* ienabled -- Turns freewheeling on (the default) or off. -* -* -* JackoAudioInConnect -- Creates an audio connection -* from a Jack port to Csound. -* -* Description -* -* In the orchestra header, creates an audio connection -* from an external Jack audio output port to a -* Jack audio input port inside this instance of Csound. -* -* Syntax -* -* JackoAudioInConnect SexternalPortName, ScsoundPortName -* -* Initialization -* -* SexternalPortName -- The full name ("clientname:portname") -* of an external Jack audio output port. -* -* ScsoundPortName -- The short name ("portname") -* of the internal Jack audio input port. -* -* Performance -* -* The actual audio must be read with the JackoAudioIn opcode. -* -* -* JackoAudioOutConnect -- Creates an audio connection -* from Csound to a Jack port. -* -* Description -* -* In the orchestra header, creates an audio connection -* from a Jack audio output port inside this instance -* of Csound to an external Jack audio input port. -* -* Syntax -* -* JackoAudioOutConnect ScsoundPortName, SexternalPortName -* -* Initialization -* -* ScsoundPortName -- The short name ("portname") -* of the internal Jack audio input port. -* -* SexternalPortName -- The full name ("clientname:portname") -* of an external Jack audio output port. -* -* Performance -* -* The actual audio must be written with the JackoAudioOut -* opcode. -* -* -* JackoMidiInConnect -- Creates a MIDI connection from -* Jack to Csound. -* -* Description -* -* Creates a MIDI connection from an external Jack MIDI -* output port to this instance of Csound. -* -* Syntax -* -* JackoMidiInConnect SexternalPortName, ScsoundPortName -* -* Initialization -* -* SexternalPortName -- The full name ("clientname:portname") -* of an external Jack MIDI output port. -* -* ScsoundPortName -- The short name ("portname") -* of the internal Jack MIDI input port. -* -* Must be used in conjunction with the -* -M0 -+rtmidi=null Csound command-line options. -* Can be used in with the MIDI inter-operability -* command-line options and/or opcodes to enable the -* use of ordinary Csound instrument definitions to -* render external scores or MIDI sequences. -* -* Note that Csound can connect to ALSA ports through Jack, -* but in this case you will have to identify the port by -* its alias in the JackInfo printout. -* -* Performance -* -* The actual MIDI events will be received in the -* regular Csound way, i.e. through a MIDI driver -* and the sensevents mechanism, rather than through -* a Jack input port opcode. -* -* The granularity of timing is Csound's kperiod. -* -* -* JackoMidiOutConnect -- Creates a MIDI connection from -* Csound to Jack. -* -* Description -* -* In the orchestra header, creates a connection -* from a Jack MIDI output port inside this instance -* of Csound to an external Jack MIDI input port. -* -* Syntax -* -* JackoMidiOutConnect ScsoundPortName, SexternalPortName -* -* Initialization -* -* ScsoundPortName -- The short name ("portname") -* of the internal Jack audio input port. -* -* SexternalPortName -- The full name ("clientname:portname") -* of an external Jack audio output port. -* -* Performance -* -* The actual MIDI data must be written with the JackoMidiOut -* or JackoNoteOut opcodes. -* -* -* JackoOn -- Enables or disables all Jack ports. -* -* Description -* -* After all Jack connections have been created, enables -* or disables all Jack input and output opcodes -* inside this instance of Csound to read or write data. -* -* Syntax -* -* JackoOn [iactive] -* -* Initialization -* -* iactive -- A flag that turns the ports on (the default) -* or off. -* -* -* JackoAudioIn -- Receives an audio signal from a Jack port. -* -* Description -* -* Receives an audio signal from a Jack audio input port -* inside this instance of Csound, which in turn has -* received the signal from its connected external Jack -* audio output port. -* -* Syntax -* -* asignal JackoAudioIn ScsoundPortName -* -* Initialization -* -* ScsoundPortName -- The short name ("portname") -* of the internal Jack audio input port. -* -* Performance -* -* asignal -- Audio received from the external Jack -* output port to which ScsoundPortName is connected. -* -* -* JackoAudioOut -- Sends an audio signal to a Jack port. -* -* Description -* -* Sends an audio signal to an internal Jack audio -* output port, and in turn to its connected external -* Jack audio input port. -* -* Note that it is possible to send audio out via Jack -* to the system audio interface, while at the same time -* rendering to a regular Csound output soundfile. -* -* Syntax -* -* JackoAudioOut ScsoundPortName, asignal -* -* Initialization -* -* ScsoundPortName -- The short name ("portname") -* of the internal Jack audio output port. -* -* Performance -* -* asignal -- Audio to be sent to the external Jack audio -* input port to which CsoundPortName is connected. -* -* Audio from multiple instances of the opcode sending -* to the same Jack port is summed before sending. -* -* -* JackoMidiOut -- Sends a MIDI channel message to a -* Jack port. -* -* Description -* -* Sends a MIDI channel message to a Jack MIDI output port -* inside this instance of Csound, and in turn to its -* connected external Jack MIDI input port. -* -* Syntax -* -* JackoMidiOut ScsoundPortName, kstatus, kchannel, kdata1[, kdata2] -* -* Initialization -* -* ScsoundPortName -- The short name ("portname") -* of the internal Jack MIDI input port. -* -* Performance -* -* kstatus -- MIDI status byte; must indicate a MIDI channel -* message. -* -* kchannel -- MIDI channel (from 0 through 15). -* -* kdata1 -- First data byte of a MIDI channel message. -* -* kdata2 -- Optional second data byte of a MIDI channel message. -* -* This opcode can be called any number of times -* in the same kperiod. Messages from multiple instances -* of the opcode sending to the same port are collected -* before sending. -* -* Running status, system exclusive messages, and -* real-time messages are not supported. -* -* The granularity of timing is Csound's kperiod. -* -* -* JackoNoteOut -- Send one note to a Jack MIDI port. -* -* Description -* -* Sends one note to a Jack MIDI output port inside this -* instance of Csound, and in turn to the connected external -* Jack MIDI input port, with a duration -* specified by the score instrument statement. -* The matching MIDI note off message is generated -* and scheduled for later transmission. -* -* Notes from multiple instances of the opcode -* sending to the same output port are collected -* before sending. -* -* The granularity of timing is Csound's kperiod. -* -* Syntax -* -* JackoNoteOut ScsoundPortName, ichannel, ikey, ivelocity -* -* Initialization -* -* ScsoundPortName -- The short name ("portname") of the -* Jack MIDI output port created by jackmidioutconnect. -* -* ichannel -- The MIDI channel (from 0 through 15) to -* receive the note. -* -* ikey -- The MIDI key number (from 0 through 127, 60 is -* middle C) of the note. -* -* ivelocity -- The MIDI velocity number (from 0 through 127) -* of the note. -* -* -* JackoTransport -- Control the Jack transport. -* -* Description -* -* Starts, stops, or repositions the Jack transport. -* This is useful, e.g., for starting an external sequencer -* playing to send MIDI messages to Csound. -* -* Syntax -* -* JackoTransport kcommand, [kposition] -* -* Performance -* -* kcommand -- 0 means "no action", 1 starts the transport, -* 2 stops the transport, and 3 positions the transport -* to kposition seconds from the beginning of jacko_is_driving -* (i.e. time 0 in the score). -* -* This opcode can be used at init time or during jacko_is_driving. -* -* The granularity of timing is Csound's kperiod. -* -* -* I M P L E M E N T A T I O N -* -* Processing is done according to the callback model: -* -* 1. The jackinit opcode: -* 1.1. Creates a Jack client, -* which is associated with the -* running instance of Csound. -* 1.2. Registers a JackProcessCallback with Jack. -* 1.3. Registers a SenseEventCallback with Csound. -* 1.4. Installs a MIDI driver callback to consume -* MIDI events coming from Jack input ports. -* 1.5. Puts the Csound processing thread to sleep. -* 1.6. Activates the client. -* 2. The ports are created and connected in the orchestra header. -* 3. After all ports are connected, they are turned on in the -* orchestra header. -* 4. Every time the Jack callback fires: -* 4.1. Any MIDI events pending in the input ports are enqueued -* for dispatch via the MIDI driver callack through -* Csound's normal sensevents mechanism. -* 4.2. csoundPerformKsmps is called. -* 4.3. When the Csound jacko_is_driving is finished: -* 4.3.1. The Csound processing thread is re-awakened. -* 4.3.2. The Jack processing callback is deactivated. -* 4.3.2. The Jack client is closed. -* 5. At the end of processing, the module deinitialization -* function erases all Jack-related state. -*/ -#include "OpcodeBase.hpp" -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -/// #include -#include -#include -#include -#include - -using namespace csound; - -struct JackoInit; -struct JackoInfo; -struct JackoFreewheel; -struct JackoAudioIn; -struct JackoAudioOut; -struct JackoMidiOut; -struct JackoNoteOut; -struct JackoTransport; -struct JackoState; - -/** -* There is one and only one JackoState instance per instance of Csound. -* It is created by and only by the JackoInit opcode. -*/ - -static void SenseEventCallback_(CSOUND *csound, void *data); -static int JackProcessCallback_(jack_nframes_t frames, void *data); -static int midiDeviceOpen_(CSOUND *csound, void **userData, - const char *devName); - -static int midiRead_(CSOUND *csound, void *userData, unsigned char *midiData, - int nbytes); - -/** -* Manages all state relevant to the global -* interaction between Jack and Csound for a particular -* client and instance of Csound. The actual reading -* and writing of data is done by the input and output -* opcodes. -*/ -struct JackoState { - CSOUND *csound; - const char *serverName; - const char *clientName; - jack_client_t *jackClient; - std::atomic jacko_is_driving; - std::atomic jacko_has_finished; - jack_nframes_t csoundFramesPerTick; - jack_nframes_t jackFramesPerTick; - jack_nframes_t csoundFramesPerSecond; - jack_nframes_t jackFramesPerSecond; - jack_nframes_t jackFrameTime; - std::map audioInPorts; - std::map audioOutPorts; - std::map midiInPorts; - std::map midiOutPorts; - std::list midiInputQueue; - jack_position_t jack_position; - std::thread *closeThread; - std::mutex csoundPerformanceThreadConditionMutex; - std::condition_variable csoundPerformanceThreadCondition; - size_t sample_size; - JackoState(CSOUND *csound_, const char *serverName_, const char *clientName_) - : csound(csound_), serverName(serverName_), clientName(clientName_), - jacko_is_driving(false), /*jack_active(false),*/ jacko_has_finished(true) { - int result = 0; - csound = csound_; - sample_size = sizeof(jack_default_audio_sample_t); - csoundFramesPerTick = csound->GetKsmps(csound); - csoundFramesPerSecond = csound->GetSr(csound); - std::memset(&jack_position, 0, sizeof(jack_position_t)); - jack_options_t jack_options = - (jack_options_t)(JackServerName | JackNoStartServer | JackUseExactName); - jack_status_t status = jack_status_t(0); - jackClient = - jack_client_open(clientName, jack_options, &status, serverName); - csound->Message(csound, - "jack_client_open: status: %d\n", - status); - if (!jackClient) { - csound->Message(csound, - "Could not create Jack client \"%s\" -- is Jack server \"%s\" running? Status: %d\n", - clientName, serverName, status); - csound->LongJmp(csound, 1); - } else { - csound->Message( - csound, "Created Jack client \"%s\" for Jack server \"%s\". Status: %d\n", - clientName, serverName, status); - } - jackFramesPerTick = jack_get_buffer_size(jackClient); - if (csoundFramesPerTick != jackFramesPerTick) { - csound->Message( - csound, "Jack buffer size %d != Csound ksmps %d, exiting...\n", - jackFramesPerTick, csoundFramesPerTick); - csound->LongJmp(csound, 1); - } - jackFramesPerSecond = jack_get_sample_rate(jackClient); - if (csoundFramesPerSecond != jackFramesPerSecond) { - csound->Message( - csound, "Jack sampling rate %d != Csound sr %d, exiting...\n", - jackFramesPerSecond, csoundFramesPerSecond); - csound->LongJmp(csound, 1); - } - csound->SetExternalMidiInOpenCallback(csound, midiDeviceOpen_); - csound->SetExternalMidiReadCallback(csound, midiRead_); - csound->RegisterSenseEventCallback(csound, SenseEventCallback_, this); - jacko_has_finished = false; - result |= jack_set_process_callback(jackClient, JackProcessCallback_, this); - result |= jack_activate(jackClient); - if (!result) { - csound->Message(csound, "Activated Jack client \"%s\".\n", - jack_get_client_name(jackClient)); - } else { - csound->Message( - csound, "Failed to activate Jack client \"%s\": status %d.\n", - jack_get_client_name(jackClient), result); - return; - } - } - ~JackoState() { - } - int SenseEventCallback() { - int result = 0; - if (jacko_has_finished) { - return result; - } -// Here we must wait once and only once, in order to put -// the original Csound processing thread to sleep -- -// but we must NOT put the Jack processing callback -// to sleep when it comes here! - if (jacko_is_driving.load() == false) { - jacko_is_driving = true; -// While Jack is processing, wait here. -// The Jack process callback will then call csoundPerformKsmps -// until the Csound performance is complete. - csound->Message(csound, - "%s", "Jacko is now driving Csound performance...\n"); - std::unique_lock lock(csoundPerformanceThreadConditionMutex); - while (jacko_is_driving.load() == true) { - csoundPerformanceThreadCondition.wait(lock); - } - jacko_is_driving = false; - lock.unlock(); - csound->Message(csound, - "%s", "Jacko has quit driving Csound performance.\n"); - return 1; - } - return result; - } - int JackProcessCallback(jack_nframes_t frames) { - IGN(frames); - if (frames != csoundFramesPerTick) { - csound->Message(csound, "JackProcessCallback: frames (%d) != csoundFramesPerTick (%d)\n", frames, csoundFramesPerTick); - } - int result = 0; -// We must call PerformKsmps here ONLY after the original -// Csound jacko_is_driving thread is waiting on its condition. - jackFrameTime = jack_last_frame_time(jackClient); - if (jacko_is_driving.load() == true) { -// Enqueue any MIDI messages pending in input ports. - for (std::map::iterator it = - midiInPorts.begin(); - it != midiInPorts.end(); ++it) { - jack_port_t *midiinport = it->second; - void *portbuffer = jack_port_get_buffer(midiinport, jackFramesPerTick); - if (portbuffer) { - jack_nframes_t eventN = jack_midi_get_event_count(portbuffer); - for (jack_nframes_t eventI = 0; eventI < eventN; ++eventI) { - jack_midi_event_t event; - int result = jack_midi_event_get(&event, portbuffer, eventI); - if (result == 0) { - for (size_t i = 0; i < event.size; ++i) { - midiInputQueue.push_back(event.buffer[i]); - } - } - } - } - } -// Clear MIDI output buffers. - for (std::map::iterator it = - midiOutPorts.begin(); - it != midiOutPorts.end(); ++it) { - void *buffer = jack_port_get_buffer(it->second, jackFramesPerTick); - jack_midi_clear_buffer(buffer); - } - int finished = csound->PerformKsmps(csound); -// We break here when the Csound performance is complete, and close -// the Jack connection in a separate thread. - if (finished /* && jack_active */) { - jacko_has_finished = true; - jacko_is_driving = false; - csound->Message(csound, "%s", "Csound performance driven by Jack has finished.\n"); -// Create a thread to run the close routine. - closeThread = new std::thread(&JackoState::closeThreadRoutine, this); - return finished; - } - } - return result; - } - int close() { - jacko_has_finished = true; - csound->Message(csound, "%s", "JackoState::close...\n"); - jacko_is_driving = false; - int result = OK; -// Try not to do thread related operations more than once... - result = jack_deactivate(jackClient); - csound->Message(csound, "%s", "Jack client deactivated.\n"); -// Jack documentation says deactivating the client does the following also: - for (std::map::iterator it = - audioInPorts.begin(); - it != audioInPorts.end(); ++it) { - result = jack_port_unregister(jackClient, it->second); - } - for (std::map::iterator it = - audioOutPorts.begin(); - it != audioOutPorts.end(); ++it) { - result = jack_port_unregister(jackClient, it->second); - } - for (std::map::iterator it = - midiInPorts.begin(); - it != midiInPorts.end(); ++it) { - result = jack_port_unregister(jackClient, it->second); - } - for (std::map::iterator it = - midiOutPorts.begin(); - it != midiOutPorts.end(); ++it) { - result = jack_port_unregister(jackClient, it->second); - } - csound->Message(csound, "%s", "Jack ports unregistered.\n"); - result |= jack_client_close(jackClient); - csound->Message(csound, "%s", "Jack client closed.\n"); - std::unique_lock lock(csoundPerformanceThreadConditionMutex); - csoundPerformanceThreadCondition.notify_one(); - lock.unlock(); - audioOutPorts.clear(); - audioInPorts.clear(); - midiInPorts.clear(); - midiOutPorts.clear(); - csound->Message(csound, "%s", "JackoState::close.\n"); - return result; - } - void *closeThreadRoutine() { - int result = 0; - close(); - void *result_ = 0; - memcpy(&result_, &result, std::min(sizeof(result), sizeof(result_))); - return result_; - } - static void *closeThreadRoutine_(void *userdata) { - void *result = ((JackoState *)userdata)->closeThreadRoutine(); - return result; - } - void startTransport() { - midiInputQueue.clear(); - jack_transport_start(jackClient); - } - void stopTransport() { - jack_transport_stop(jackClient); - } - int positionTransport(double timeSeconds) { - int result = OK; - jack_position.frame_time = timeSeconds; - midiInputQueue.clear(); - result = jack_transport_reposition(jackClient, &jack_position); - return result; - } - /** - * Return a MIDI output buffer, - * clearing it if not yet cleared for this tick. - */ - jack_midi_data_t *getMidiOutBuffer(jack_port_t *csoundPort) { - jack_midi_data_t *buffer = (jack_midi_data_t *)jack_port_get_buffer( - csoundPort, csoundFramesPerTick); - return buffer; - } -}; - -static int JackProcessCallback_(jack_nframes_t frames, void *data) { - return ((JackoState *)data)->JackProcessCallback(frames); -} - -static void SenseEventCallback_(CSOUND *csound, void *data) { - IGN(csound); - ((JackoState *)data)->SenseEventCallback(); -} - -static int midiDeviceOpen_(CSOUND *csound, void **userData, - const char *devName) { - IGN(devName); - JackoState *jackoState; - csound::QueryGlobalPointer(csound, "jackoState", jackoState); - *userData = jackoState; - return 0; -} - -/** -* Dispatch any MIDI channel messages that have -* been read from Jack MIDI input ports. -*/ -static int midiRead_(CSOUND *csound, void *userData, unsigned char *midiData, - int midiN) { - IGN(csound); - JackoState *jackoState_ = (JackoState *)userData; - if (jackoState_->jacko_has_finished == true) { - return 0; - } - int midiI = 0; - while (!jackoState_->midiInputQueue.empty() && midiI < midiN) { - midiData[midiI] = jackoState_->midiInputQueue.front(); - jackoState_->midiInputQueue.pop_front(); - midiI++; - } -// if (midiI) { -// csound->Message(csound, "midiRead_: %d bytes.\n", midiI); -//} - return midiI; -} - -struct JackoInit : public OpcodeBase { - STRINGDAT *ServerName; - STRINGDAT *SclientName; - const char *serverName; - const char *clientName; - int init(CSOUND *csound) { - serverName = csound->strarg2name(csound, (char *)0, ServerName->data, - (char *)"default", (int)1); - clientName = csound->strarg2name(csound, (char *)0, SclientName->data, - (char *)"csound", (int)1); - JackoState *jackoState = new JackoState(csound, serverName, clientName); - int result = csound::CreateGlobalPointer(csound, "jackoState", jackoState); - return result; - } -}; - -struct JackoInfo : public OpcodeBase { - int init(CSOUND *csound) { - JackoState *jackoState = 0; - csound::QueryGlobalPointer(csound, "jackoState", jackoState); - log(csound, "Jack information for client: %s\n", jackoState->clientName); - log(csound, " Daemon name: %s\n", jackoState->serverName); - log(csound, " Frames per second: %d\n", - jackoState->jackFramesPerSecond); - log(csound, " Frames per period: %d\n", - jackoState->jackFramesPerTick); - const char **ports = jack_get_ports(jackoState->jackClient, 0, 0, 0); - if (ports) { - log(csound, " Ports and connections:\n"); - for (size_t i = 0; ports[i]; ++i) { - const char *PortName = ports[i]; - jack_port_t *port = jack_port_by_name(jackoState->jackClient, PortName); - int flags = jack_port_flags(port); - const char *type = jack_port_type(port); - const char *portType = " "; - if ((flags & JackPortIsOutput) == JackPortIsOutput) { - portType = "Output"; - } else if ((flags & JackPortIsInput) == JackPortIsInput) { - portType = "Input "; - } - log(csound, " %3d: %s %-25s %s\n", (i + 1), portType, type, - (PortName ? PortName : "(no name)")); - char alias1[0x100]; - char alias2[0x100]; - char *const aliases[2] = {alias1, alias2}; - size_t aliasN = jack_port_get_aliases(port, aliases); - for (size_t aliasI = 0; aliasI < aliasN; ++aliasI) { - log(csound, " Alias: %s\n", aliases[aliasI]); - } - const char **connections = - jack_port_get_all_connections(jackoState->jackClient, port); - if (connections) { - for (size_t j = 0; connections[j]; ++j) { - if ((jack_port_flags(port) & JackPortIsOutput) == - JackPortIsOutput) { - log(csound, - " Sends to: >> %s\n", - connections[j]); - } else { - log(csound, - " Receives from: << %s\n", - connections[j]); - } - } - } - std::free(connections); - } - std::free(ports); - } - return OK; - } -}; - -struct JackoFreewheel : public OpcodeBase { - MYFLT *ifreewheel; - int init(CSOUND *csound) { - JackoState *jackoState = 0; - csound::QueryGlobalPointer(csound, "jackoState", jackoState); - int freewheel = (int)*ifreewheel; - int result = jack_set_freewheel(jackoState->jackClient, freewheel); - if (result) { - warn(csound, - "Failed to set Jack freewheeling mode to \"%s\": error %d.\n", - (freewheel ? "on" : "off"), result); - } else { - log(csound, "Set Jack freewheeling mode to \"%s\".\n", - (freewheel ? "on" : "off")); - } - return result; - } -}; - -struct JackoOn : public OpcodeBase { - MYFLT *jon; - int init(CSOUND *csound) { - int result = OK; - JackoState *jackoState = 0; - csound::QueryGlobalPointer(csound, "jackoState", jackoState); - log(csound, "JackoOn is deprecated and now is a no-op.\n"); - return result; - } -}; - -struct JackoAudioInConnect : public OpcodeBase { -// Out. -// Ins. - STRINGDAT *SexternalPortName; - STRINGDAT *ScsoundPortName; -// State. - const char *csoundPortName; - char csoundFullPortName[0x100]; - const char *externalPortName; - const char *clientName; - size_t frames; - jack_port_t *csoundPort; - jack_port_t *externalPort; - JackoState *jackoState; - int init(CSOUND *csound) { - csound::QueryGlobalPointer(csound, "jackoState", jackoState); - int result = OK; - frames = opds.insdshead->ksmps; - clientName = jack_get_client_name(jackoState->jackClient); - csoundPortName = csound->strarg2name( - csound, (char *)0, ScsoundPortName->data, (char *)"", (int)1); - std::sprintf(csoundFullPortName, "%s:%s", clientName, csoundPortName); - csoundPort = jack_port_by_name(jackoState->jackClient, csoundFullPortName); - if (csoundPort) { - log(csound, "Found Csound audio in port \"%s\".\n", csoundFullPortName); - } else { - csoundPort = - jack_port_register(jackoState->jackClient, csoundPortName, - JACK_DEFAULT_AUDIO_TYPE, JackPortIsInput, 0); - if (csoundPort) { - log(csound, "Created Csound audio in port \"%s\".\n", csoundFullPortName); - } else { - warn(csound, "Could not create Csound audio in port \"%s\".\n", - csoundFullPortName); - } - } - externalPortName = csound->strarg2name( - csound, (char *)0, SexternalPortName->data, (char *)"csound", (int)1); - result = jack_connect(jackoState->jackClient, externalPortName, - csoundFullPortName); - if (result == EEXIST) { - log(csound, "Jack audio connection from \"%s\" to \"%s\" already exists.\n", - csoundFullPortName, externalPortName); - } else if (result) { - warn(csound, - "Could not create Jack audio connection from \"%s\" to \"%s\": " - "status %d %s.\n", - externalPortName, csoundFullPortName, errno, std::strerror(errno)); - return result; - } else { - log(csound, "Created Jack audio connection from \"%s\" to \"%s\".\n", - externalPortName, csoundFullPortName); - } - jackoState->audioInPorts[csoundPortName] = csoundPort; - return result; - } -}; - -struct JackoAudioIn : public OpcodeBase { -// Out. - MYFLT *asignal; -// Ins. - STRINGDAT *ScsoundPortName; -// State. - const char *csoundPortName; - jack_port_t *csoundPort; - jack_nframes_t csoundFramesPerTick; - JackoState *jackoState; - int init(CSOUND *csound) { - csound::QueryGlobalPointer(csound, "jackoState", jackoState); - int result = OK; - csoundFramesPerTick = jackoState->csoundFramesPerTick; - csoundPortName = csound->strarg2name( - csound, (char *)0, ScsoundPortName->data, (char *)"", (int)1); - csoundPort = jackoState->audioInPorts[csoundPortName]; - return result; - } - int audio(CSOUND *csound) { - IGN(csound); - if (jackoState->jacko_has_finished == true) { - return OK; - } - jack_default_audio_sample_t *buffer = - (jack_default_audio_sample_t *)jack_port_get_buffer( - csoundPort, csoundFramesPerTick); - for (size_t frame = 0; frame < csoundFramesPerTick; ++frame) { - asignal[frame] = buffer[frame]; - } - return OK; - } -}; - -struct JackoAudioOutConnect : public OpcodeBase { -// No outs. -// Ins. - STRINGDAT *ScsoundPortName; - STRINGDAT *SexternalPortName; -// State. - const char *csoundPortName; - char csoundFullPortName[0x100]; - const char *externalPortName; - const char *clientName; - size_t frames; - jack_port_t *csoundPort; - jack_port_t *externalPort; - JackoState *jackoState; - int init(CSOUND *csound) { - csound::QueryGlobalPointer(csound, "jackoState", jackoState); - int result = OK; - frames = opds.insdshead->ksmps; - clientName = jack_get_client_name(jackoState->jackClient); - csoundPortName = csound->strarg2name( - csound, (char *)0, ScsoundPortName->data, (char *)"", (int)1); - std::sprintf(csoundFullPortName, "%s:%s", clientName, csoundPortName); - externalPortName = csound->strarg2name( - csound, (char *)0, SexternalPortName->data, (char *)"csound", (int)1); - csoundPort = jack_port_by_name(jackoState->jackClient, csoundFullPortName); - if (!csoundPort) { - csoundPort = - jack_port_register(jackoState->jackClient, csoundPortName, - JACK_DEFAULT_AUDIO_TYPE, JackPortIsOutput, 0); - if (csoundPort) { - log(csound, "Created Csound audio out port \"%s\".\n", csoundFullPortName); - } else { - warn(csound, "Could not create Csound audio out port \"%s\".\n", - csoundFullPortName); - } - } - externalPort = jack_port_by_name(jackoState->jackClient, externalPortName); - result = jack_connect(jackoState->jackClient, jack_port_name(csoundPort), - jack_port_name(externalPort)); - if (result == EEXIST) { - log(csound, "Connection from \"%s\" to \"%s\" already exists.\n", - csoundFullPortName, externalPortName); - } else if (result) { - warn(csound, - "Could not create Jack connection from \"%s\" to \"%s\": " - "status %d %s.\n", - csoundFullPortName, externalPortName, errno, std::strerror(errno)); - return result; - } else { - log(csound, "Created Jack connection from \"%s\" to \"%s\".\n", - csoundFullPortName, externalPortName); - } - jackoState->audioOutPorts[csoundPortName] = csoundPort; - return result; - } -}; - -struct JackoAudioOut : public OpcodeBase { -// No outs. -// Ins. - STRINGDAT *ScsoundPortName; - MYFLT *asignal; -// State. - const char *csoundPortName; - jack_port_t *csoundPort; - jack_nframes_t csoundFramesPerTick; - JackoState *jackoState; - int init(CSOUND *csound) { - csound::QueryGlobalPointer(csound, "jackoState", jackoState); - int result = OK; - csoundFramesPerTick = jackoState->csoundFramesPerTick; - csoundPortName = csound->strarg2name( - csound, (char *)0, ScsoundPortName->data, (char *)"", (int)1); - csoundPort = jackoState->audioOutPorts[csoundPortName]; - return result; - } - int audio(CSOUND *csound) { - IGN(csound); - if (jackoState->jacko_has_finished == true) { - return OK; - } - jack_default_audio_sample_t *buffer = - (jack_default_audio_sample_t *)jack_port_get_buffer( - csoundPort, csoundFramesPerTick); - for (size_t frame = 0; frame < csoundFramesPerTick; ++frame) { - buffer[frame] = asignal[frame]; - } - return OK; - } -}; - -struct JackoMidiInConnect : public OpcodeBase { -// No outs. -// Ins. - STRINGDAT *SexternalPortName; - STRINGDAT *ScsoundPortName; -// State. - const char *csoundPortName; - char csoundFullPortName[0x100]; - const char *externalPortName; - const char *clientName; - size_t frames; - jack_port_t *csoundPort; - jack_port_t *externalPort; - JackoState *jackoState; - int init(CSOUND *csound) { - csound::QueryGlobalPointer(csound, "jackoState", jackoState); - int result = OK; - frames = opds.insdshead->ksmps; - clientName = jack_get_client_name(jackoState->jackClient); - csoundPortName = csound->strarg2name( - csound, (char *)0, ScsoundPortName->data, (char *)"", (int)1); - std::sprintf(csoundFullPortName, "%s:%s", clientName, csoundPortName); - csoundPort = jack_port_by_name(jackoState->jackClient, csoundFullPortName); - if (csoundPort) { - log(csound, "Found Csound MIDI in port \"%s\".\n", csoundFullPortName); - } else { - csoundPort = - jack_port_register(jackoState->jackClient, csoundPortName, - JACK_DEFAULT_MIDI_TYPE, JackPortIsInput, 0); - if (csoundPort) { - log(csound, "Created Csound MIDI in port \"%s\".\n", csoundFullPortName); - } else { - warn(csound, "Could not create Csound MIDI in port \"%s\".\n", - csoundFullPortName); - } - } - externalPortName = csound->strarg2name( - csound, (char *)0, SexternalPortName->data, (char *)"csound", (int)1); - result = jack_connect(jackoState->jackClient, externalPortName, csoundFullPortName); - if (result == EEXIST) { - log(csound, "Jack MIDI connection from \"%s\" to \"%s\" already exists.\n", - externalPortName, csoundFullPortName); - } else if (result) { - warn(csound, - "Could not create Jack MIDI connection from \"%s\" to \"%s\": " - "status %d.\n", - externalPortName, csoundFullPortName, result); - return result; - } else { - log(csound, "Created Jack MIDI connection from \"%s\" to \"%s\".\n", - externalPortName, csoundFullPortName); - } - jackoState->midiOutPorts[csoundPortName] = csoundPort; - return result; - } -}; - -struct JackoMidiOutConnect : public OpcodeBase { -// No outs. -// Ins. - STRINGDAT *ScsoundPortName; - STRINGDAT *SexternalPortName; -// State. - const char *csoundPortName; - char csoundFullPortName[0x100]; - const char *externalPortName; - const char *clientName; - size_t frames; - jack_port_t *csoundPort; - jack_port_t *externalPort; - JackoState *jackoState; - int init(CSOUND *csound) { - csound::QueryGlobalPointer(csound, "jackoState", jackoState); - int result = OK; - frames = opds.insdshead->ksmps; - clientName = jack_get_client_name(jackoState->jackClient); - csoundPortName = csound->strarg2name( - csound, (char *)0, ScsoundPortName->data, (char *)"", (int)1); - std::sprintf(csoundFullPortName, "%s:%s", clientName, csoundPortName); - csoundPort = jack_port_by_name(jackoState->jackClient, csoundFullPortName); - if (csoundPort) { - log(csound, "Found Csound MIDI out port \"%s\".\n", csoundFullPortName); - } else { - csoundPort = - jack_port_register(jackoState->jackClient, csoundPortName, - JACK_DEFAULT_MIDI_TYPE, JackPortIsOutput, 0); - if (csoundPort) { - log(csound, "Created Csound MIDI out port \"%s\".\n", csoundFullPortName); - } else { - warn(csound, "Could not create Csound out MIDI port \"%s\".\n", - csoundFullPortName); - } - } - externalPortName = csound->strarg2name( - csound, (char *)0, SexternalPortName->data, (char *)"csound", (int)1); - result = jack_connect(jackoState->jackClient, csoundFullPortName, - externalPortName); - if (result == EEXIST) { - log(csound, "Jack MIDI connection from \"%s\" to \"%s\" already exists.\n", - csoundFullPortName, externalPortName); - } else if (result) { - warn(csound, - "Could not create Jack MIDI connection from \"%s\" to \"%s\": " - "status %d.\n", - csoundFullPortName, externalPortName, result); - return result; - } else { - log(csound, "Created Jack MIDI connection from \"%s\" to \"%s\".\n", - csoundFullPortName, externalPortName); - } - jackoState->midiOutPorts[csoundPortName] = csoundPort; - return result; - } -}; - -struct JackoMidiOut : public OpcodeBase { -// No outs. -// Ins. - STRINGDAT *ScsoundPortName; - MYFLT *kstatus; - MYFLT *kchannel; - MYFLT *kdata1; - MYFLT *kdata2; - char status; - char channel; - char data1; - char data2; - char priorstatus; - char priorchannel; - char priordata1; - char priordata2; -// State. - const char *csoundPortName; - jack_port_t *csoundPort; - jack_nframes_t csoundFramesPerTick; - jack_midi_data_t *buffer; - JackoState *jackoState; - int init(CSOUND *csound) { - csound::QueryGlobalPointer(csound, "jackoState", jackoState); - int result = OK; - csoundFramesPerTick = jackoState->csoundFramesPerTick; - csoundPortName = csound->strarg2name( - csound, (char *)0, ScsoundPortName->data, (char *)"", (int)1); - csoundPort = jackoState->midiOutPorts[csoundPortName]; - priorstatus = -1; - priorchannel = -1; - priordata1 = -1; - priordata2 = -1; - return result; - } - int kontrol(CSOUND *csound) { - IGN(csound); - if (jackoState->jacko_has_finished == true) { - return OK; - } - int result = OK; - status = *kstatus; - channel = *kchannel; - data1 = *kdata1; - data2 = *kdata2; - if (status != priorstatus || channel != priorchannel || - data1 != priordata1 || data2 != priordata2) { - size_t dataSize = 0; - if (data2 == -1) { - dataSize = 2; - } else { - dataSize = 3; - } - buffer = jackoState->getMidiOutBuffer(csoundPort); - jack_midi_data_t *data = jack_midi_event_reserve(buffer, 0, dataSize); - data[0] = (status + channel); - data[1] = data1; - if (data2 != -1) { - data[2] = data2; -// log(csound, "MIDI: %3d %3d %3d\n", data[0], data[1], data[2]); - } else { -// log(csound, "MIDI: %3d %3d\n", data[0], data[1]); - } - } - priorstatus = status; - priorchannel = channel; - priordata1 = data1; - priordata2 = data2; - return result; - } -}; - -struct JackoNoteOut : public OpcodeNoteoffBase { -// No outs. -// Ins. - STRINGDAT *ScsoundPortName; - MYFLT *ichannel; - MYFLT *ikey; - MYFLT *ivelocity; - char status; - char channel; - char key; - char velocity; -// State. - const char *csoundPortName; - jack_port_t *csoundPort; - jack_nframes_t csoundFramesPerTick; - jack_midi_data_t *buffer; - JackoState *jackoState; - int init(CSOUND *csound) { - csound::QueryGlobalPointer(csound, "jackoState", jackoState); - int result = OK; - csoundFramesPerTick = jackoState->csoundFramesPerTick; - csoundPortName = csound->strarg2name( - csound, (char *)0, ScsoundPortName->data, (char *)"", (int)1); - csoundPort = jackoState->midiOutPorts[csoundPortName]; - channel = (char)*ichannel; - key = (char)*ikey; - velocity = (char)*ivelocity; - buffer = jackoState->getMidiOutBuffer(csoundPort); - jack_midi_data_t *data = jack_midi_event_reserve(buffer, 0, 3); - data[0] = (144 + channel); - data[1] = key; - data[2] = velocity; - log(csound, "noteon: %3d %3d %3d\n", data[0], data[1], data[2]); - return result; - } - int noteoff(CSOUND *csound) { - IGN(csound); - if (jackoState->jacko_has_finished == true) { - return OK; - } - int result = OK; - buffer = jackoState->getMidiOutBuffer(csoundPort); - jack_midi_data_t *data = jack_midi_event_reserve(buffer, 0, 3); - data[0] = (128 + channel); - data[1] = key; - data[2] = 0; - log(csound, "noteoff: %3d %3d %3d\n", data[0], data[1], data[2]); - return result; - } -}; - -struct JackoTransport : public OpcodeBase { -// Outs. -// Ins. - MYFLT *kcommand; - MYFLT *Oposition; -// State. - int command; - int priorCommand; - double positionSeconds; - double priorPositionSeconds; - JackoState *jackoState; - int init(CSOUND *csound) { - csound::QueryGlobalPointer(csound, "jackoState", jackoState); - priorCommand = -1; - priorPositionSeconds = 0.0; - return kontrol(csound); - } - int kontrol(CSOUND *csound) { - if (jackoState->jacko_has_finished == true) { - return OK; - } - int result = OK; - command = int(*kcommand); - positionSeconds = double(*Oposition); - if (command) { - if (command != priorCommand) { - priorCommand = command; - switch (command) { - case 1: - result = jackoState->positionTransport(0.0); - jackoState->startTransport(); - log(csound, "Started Jack transport.\n"); - break; - case 2: - jackoState->stopTransport(); - log(csound, "Stopped Jack transport.\n"); - break; - case 3: - if (positionSeconds != priorPositionSeconds) { - priorPositionSeconds = positionSeconds; - result = jackoState->positionTransport(positionSeconds); - jackoState->startTransport(); - if (result) { - log(csound, "Failed to start Jack transport at %f seconds with" - " result: %d\n", - positionSeconds, result); - } else { - log(csound, "Started Jack transport at %f seconds.\n", - positionSeconds); - } - } - break; - }; - } - } - return result; - } -}; - -extern "C" { - static OENTRY oentries[] = { - { (char *)"JackoInit", sizeof(JackoInit), 0, 1, (char *)"", (char *)"SS", - (SUBR)&JackoInit::init_, 0, 0 - }, - { (char *)"JackoInfo", sizeof(JackoInfo), 0, 1, (char *)"", (char *)"", - (SUBR)&JackoInfo::init_, 0, 0 - }, - { (char *)"JackoFreewheel", sizeof(JackoFreewheel), 0, 1, (char *)"", - (char *)"i", (SUBR)&JackoFreewheel::init_, 0, 0 - }, - { (char *)"JackoOn", sizeof(JackoOn), 0, 1, (char *)"", (char *)"j", - (SUBR)&JackoOn::init_, 0, 0 - }, - { - (char *)"JackoAudioInConnect", sizeof(JackoAudioInConnect), 0, 1, - (char *)"", (char *)"SS", (SUBR)&JackoAudioInConnect::init_, 0, 0, - }, - { - (char *)"JackoAudioIn", sizeof(JackoAudioIn), 0, 3, (char *)"a", - (char *)"S", (SUBR)&JackoAudioIn::init_, (SUBR)&JackoAudioIn::audio_, - }, - { - (char *)"JackoAudioOutConnect", sizeof(JackoAudioOutConnect), 0, 1, - (char *)"", (char *)"SS", (SUBR)&JackoAudioOutConnect::init_, 0, 0, - }, - { - (char *)"JackoAudioOut", sizeof(JackoAudioOut), 0, 3, (char *)"", - (char *)"Sa", (SUBR)&JackoAudioOut::init_, - (SUBR)&JackoAudioOut::audio_, - }, - { - (char *)"JackoMidiInConnect", sizeof(JackoMidiInConnect), 0, 1, - (char *)"", (char *)"SS", (SUBR)&JackoMidiInConnect::init_, 0, 0, - }, - { - (char *)"JackoMidiOutConnect", sizeof(JackoMidiOutConnect), 0, 1, - (char *)"", (char *)"SS", (SUBR)&JackoMidiOutConnect::init_, 0, 0, - }, - { - (char *)"JackoMidiOut", sizeof(JackoMidiOut), 0, 3, (char *)"", - (char *)"SkkkO", (SUBR)&JackoMidiOut::init_, - (SUBR)&JackoMidiOut::kontrol_, 0, - }, - { (char *)"JackoNoteOut", sizeof(JackoNoteOut), 0, 3, (char *)"", - (char *)"Siii", (SUBR)&JackoNoteOut::init_, (SUBR)&JackoNoteOut::kontrol_, - 0 - }, - { (char *)"JackoTransport", sizeof(JackoTransport), 0, 3, (char *)"", - (char *)"kO", // O defaults to 0. - (SUBR)&JackoTransport::init_, (SUBR)&JackoTransport::kontrol_, 0 - }, - {0, 0, 0, 0, 0, 0, (SUBR)0, (SUBR)0, (SUBR)0} - }; - - PUBLIC int csoundModuleCreate(CSOUND *csound) { - IGN(csound); - return 0; - } - - PUBLIC int csoundModuleInit(CSOUND *csound) { - - OENTRY *ep = (OENTRY *)&(oentries[0]); - int err = 0; - while (ep->opname != 0) { - err |= csound->AppendOpcode(csound, ep->opname, ep->dsblksiz, ep->flags, - ep->thread, ep->outypes, ep->intypes, - (int (*)(CSOUND *, void *))ep->iopadr, - (int (*)(CSOUND *, void *))ep->kopadr, - (int (*)(CSOUND *, void *))ep->aopadr); - ep++; - } - return err; - } - - PUBLIC int csoundModuleDestroy(CSOUND *csound) { - if (csound->GetDebug(csound)) { - csound->Message(csound, "jacko: csoundModuleDestroy(%p)...\n", - csound); - } - int result = OK; - JackoState *jackoState = 0; - csound::QueryGlobalPointer(csound, "jackoState", jackoState); - if (jackoState) { - if (jackoState->jacko_has_finished == false) { - jackoState->close(); - } - delete jackoState; - jackoState = 0; - } - if (csound->GetDebug(csound)) { - csound->Message(csound, "jacko: csoundModuleDestroy(%p).\n", - csound); - } - return result; - } -} diff --git a/6.x/src/jackops/jacko_test.csd b/6.x/src/jackops/jacko_test.csd deleted file mode 100644 index 4fbfb605..00000000 --- a/6.x/src/jackops/jacko_test.csd +++ /dev/null @@ -1,93 +0,0 @@ - - - -TEST JACKO OPCODES -Michael Gogins - -This csd tests the Jacko opcodes. - -There is a segmentation fault at the end of the performance. This does happen -in the Jacko plugin, but it does not affect the performance. - -Csound not only writes a soundfile using -o, but also sends realtime audio and -MIDI to other Jack clients using Jack connections. - -Before running this piece, in a terminal execute: - -zynaddsubfx --input JACK --output JACK --auto-connect --sample-rate 48000 --buffer-size 128 - - - - - - -; Sampling rate must be the same as Jack's. -sr=48000 -; ksmps must be the same as Jack's frames/period. -ksmps=128 -nchnls=2 -0dbfs=40000 - -ga_audio_out init 0 - -; Initialize the Jack connections. -JackoInit "default", "csound6" -prints "Initial ports and connections:\n" -JackoInfo -JackoAudioInConnect "system:capture_1", "audio_in_left" -JackoAudioInConnect "system:capture_2", "audio_in_right" -JackoAudioOutConnect "audio_out_left", "system:playback_1" -JackoAudioOutConnect "audio_out_right", "system:playback_2" -JackoMidiInConnect "system:midi_capture_1", "midiin" -JackoMidiOutConnect "midiout", "zynaddsubfx:midi_input" -prints "Final ports and connections:\n" -JackoInfo - -instr 1 -print p1, p2, p3, p4, p5 -i_frequency = cpsmidinn(p4) -i_amplitude = ampdb(p5) -print i_frequency, i_amplitude -a_out oscil i_amplitude, i_frequency -ga_audio_out += a_out; -endin - -; Sends notes to an external MIDI synthesizer. -instr 801 -print p1, p2, p3, p4, p5 -JackoNoteOut "midiout", 0, p4, p5 -prints "Sent note to midiout.\n" -endin - -instr 900 -print p1, p2, p3 -outs ga_audio_out, ga_audio_out -; Input test is only to see if connection is created. -audio_in_left_ init 0 -audio_in_right_ init 0 -audio_in_left_ JackoAudioIn "audio_in_left" -audio_in_right_ JackoAudioIn "audio_in_right" -JackoAudioOut "audio_out_left", ga_audio_out -JackoAudioOut "audio_out_right", ga_audio_out -ga_audio_out = 0 -endin - -instr 1000 -print p1, p2, p3 -; Uncomment the following two lines to test if Jacko hangs Csound. -event "e", 0, 0, 0.1 -prints "Ending Csound performance with 'e' event\n" -endin - - - -f 0 60 -i1 5 3 63 10 -i801 10 3 69 100 -i1 15 3 72 10 -i900 0 -1 -i1000 20 1 -e - - - \ No newline at end of file diff --git a/6.x/src/linear_algebra/OpcodeBase.hpp b/6.x/src/linear_algebra/OpcodeBase.hpp deleted file mode 100644 index 21ca8a0c..00000000 --- a/6.x/src/linear_algebra/OpcodeBase.hpp +++ /dev/null @@ -1,492 +0,0 @@ -/* - OpcodeBase.hpp: - - Copyright (C) 2005, 2009, 2017 by Istva Varga, Victor Lazzarini and - Michael Gogins - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ -#pragma once - -/** - * There is a conflict between the preprocessor definition "_CR" in the - * standard C++ library and in Csound. To work around this, undefine "_CR" and - * include ALL standard library include files BEFORE including ANY Csound - * include files. - */ -#if defined(_CR) -#undef _CR -#endif - -#include -#include -#include -#if !(defined(__wasi__)) -#include -#endif -#include -#include -#include -#include - -/** - * Template base class, or pseudo-virtual base class, - * for writing Csound opcodes in C++. - * Derive opcode implementation classes like this: - * - * DerivedClass : public OpcodeBase - * { - * public: - * // All output fields must be declared first as MYFLT *: - * MYFLT *aret1; - * // All input fields must be declared next as MYFLT *: - * MYFLT *iarg1; - * MYFLT *karg2; - * MYFLT *aarg3; - * // All internal state variables must be declared after that: - * size_t state1; - * double state2; - * MYFLT state3; - * // If the opcode shares data protect it by creating one or more void - * // *mutex member pointers: - * void *mutex1; - * void *mutex2; - * // and create them usind csound->Create_Mutex() in csoundModuleCreate - * // and destroy them using csound->DeleteMutex(mutex) - * // csoundModuleDestroy, and lock them using LockGuard guard(mutex). - * int init(); - * int kontrol(); - * int audio; - * int noteoff(); - * void deinit(); - * }; - */ - -namespace csound { - -static bool diagnostics_enabled = false; - -/** - * Use this to guard against data races in opcode functions. The mutex should - * be created by csound->Create_Mutex() in csoundModuleCreate(), and should - * be destroyed by csound->DeleteMutex() in csoundModuleDestroy(). - * - * If data is shared between opcode instances, the mutex should be global to - * the opcode library; if data is shared between threads for a single instance, - * the mutex should belong to the opcode instance. - */ -struct LockGuard { - LockGuard(CSOUND *csound_, void *mutex_) : csound(csound_), mutex(mutex_) - { - csound->LockMutex(mutex); - } - ~LockGuard() - { - csound->UnlockMutex(mutex); - } - CSOUND *csound; - void *mutex; -}; - -/** - * Use this to store a pointer to a global heap-allocated object, e.g. one - * used to manage state between opcode instances. - */ - -template int CreateGlobalPointer(CSOUND *csound, const char *name, T *pointer) -{ - T **pointer_to_pointer = 0; - int result = csound->CreateGlobalVariable(csound, name, sizeof(pointer_to_pointer)); - pointer_to_pointer = static_cast(csound->QueryGlobalVariable(csound, name)); - *pointer_to_pointer = pointer; - return result; -} - -/** - * Retrieve a pointer to a global heap-allocated object, e.g. one - * used to manage state between opcode instances. - */ -template T *QueryGlobalPointer(CSOUND *csound, const char *name, T*& pointer) -{ - T **pointer_to_pointer = static_cast(csound->QueryGlobalVariableNoCheck(csound, name)); - if (pointer_to_pointer != 0) { - pointer = *pointer_to_pointer; - } else { - pointer = 0; - } - return pointer; -} - -template -class OpcodeBase -{ -public: - int init(CSOUND *csound) - { - return NOTOK; - } - static int init_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->init(csound); - } - int kontrol(CSOUND *csound) - { - return NOTOK; - } - static int kontrol_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->kontrol(csound); - } - int audio(CSOUND *csound) - { - return NOTOK; - } - static int audio_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->audio(csound); - } - /** - This is how to compute audio signals for normal opcodes: - (1) Zero all frames from 0 up to but not including Offset. - (2) Compute all frames from ksmps_offset up to but not including End. - (3) Zero all frames from End up to but not including ksmps. - Example from a C opcode: - uint32_t offset = p->h.insdshead->ksmps_offset; - uint32_t early = p->h.insdshead->ksmps_no_end; - uint32_t n, nsmps = CS_KSMPS; - if (UNLIKELY(offset)) memset(p->r, '\0', offset*sizeof(MYFLT)); - if (UNLIKELY(early)) { - nsmps -= early; - memset(&p->r[nsmps], '\0', early*sizeof(MYFLT)); - } - for (n = offset; n < nsmps; n++) { - input1 = MYFLT2LRND(p->a[n]); - p->r[n] = (MYFLT) (input1 >> input2); - } - So in C++ it should look like this (which is much easier to understand): - int frameIndex = 0; - for( ; frameIndex < kperiodOffset(); ++frameIndex) { - asignal[frameIndex] = 0; - } - for( ; frameIndex < kperiodEnd(); ++frameIndex) { - asignal[frameIndex] = compute(); - } - for( ; frameIndex < ksmps(); ++frameIndex) { - asignal[frameIndex] = 0; - } - */ - uint32_t kperiodOffset() const - { - return opds.insdshead->ksmps_offset; - } - uint32_t kperiodEnd() const - { - uint32_t end = opds.insdshead->ksmps_no_end; - if (end) { - return end; - } else { - return ksmps(); - } - } - uint32_t ksmps() const - { - return opds.insdshead->ksmps; - } - uint32_t output_arg_count() - { - return (uint32_t)opds.optext->t.outArgCount; - } - uint32_t input_arg_count() - { - return (uint32_t)opds.optext->t.inArgCount; - } - void log(CSOUND *csound, const char *format,...) - { - va_list args; - va_start(args, format); - if(csound) { - csound->MessageV(csound, 0, format, args); - } else { - vfprintf(stdout, format, args); - } - va_end(args); - } - void warn(CSOUND *csound, const char *format,...) - { - if(csound) { - if(csound->GetMessageLevel(csound) & CS_WARNMSG) { - va_list args; - va_start(args, format); - csound->MessageV(csound, CSOUNDMSG_WARNING, format, args); - va_end(args); - } - } else { - va_list args; - va_start(args, format); - vfprintf(stdout, format, args); - va_end(args); - } - } - OPDS opds; -}; - -template -class OpcodeNoteoffBase -{ -public: - int init(CSOUND *csound) - { - IGN(csound); - return NOTOK; - } - static int init_(CSOUND *csound, void *opcode) - { - if (!csound->GetReinitFlag(csound) && !csound->GetTieFlag(csound)) { - csound->RegisterDeinitCallback(csound, opcode, - &OpcodeNoteoffBase::noteoff_); - } - return reinterpret_cast(opcode)->init(csound); - } - int kontrol(CSOUND *csound) - { - IGN(csound); - return NOTOK; - } - static int kontrol_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->kontrol(csound); - } - int audio(CSOUND *csound) - { - IGN(csound); - return NOTOK; - } - static int audio_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->audio(csound); - } - /** - This is how to compute audio signals for normal opcodes: - (1) Zero all frames from 0 up to but not including Offset. - (2) Compute all frames from ksmps_offset up to but not including End. - (3) Zero all frames from End up to but not including ksmps. - Example from a C opcode: - uint32_t offset = p->h.insdshead->ksmps_offset; - uint32_t early = p->h.insdshead->ksmps_no_end; - uint32_t n, nsmps = CS_KSMPS; - if (UNLIKELY(offset)) memset(p->r, '\0', offset*sizeof(MYFLT)); - if (UNLIKELY(early)) { - nsmps -= early; - memset(&p->r[nsmps], '\0', early*sizeof(MYFLT)); - } - for (n = offset; n < nsmps; n++) { - input1 = MYFLT2LRND(p->a[n]); - p->r[n] = (MYFLT) (input1 >> input2); - } - So in C++ it should look like this (which is much easier to understand): - int frameIndex = 0; - for( ; frameIndex < kperiodOffset(); ++frameIndex) { - asignal[frameIndex] = 0; - } - for( ; frameIndex < kperiodEnd(); ++frameIndex) { - asignal[frameIndex] = compute(); - } - for( ; frameIndex < ksmps(); ++frameIndex) { - asignal[frameIndex] = 0; - } - */ - uint32_t kperiodOffset() const - { - return opds.insdshead->ksmps_offset; - } - uint32_t kperiodEnd() const - { - uint32_t end = opds.insdshead->ksmps_no_end; - if (end) { - return end; - } else { - return ksmps(); - } - } - uint32_t ksmps() const - { - return opds.insdshead->ksmps; - } - uint32_t output_arg_count() - { - return (uint32_t)opds.optext->t.outArgCount; - } - uint32_t input_arg_count() - { - return (uint32_t)opds.optext->t.inArgCount; - } - void log(CSOUND *csound, const char *format,...) - { - va_list args; - va_start(args, format); - if(csound) { - csound->MessageV(csound, 0, format, args); - } else { - vfprintf(stdout, format, args); - } - va_end(args); - } - void warn(CSOUND *csound, const char *format,...) - { - if(csound) { - if(csound->GetMessageLevel(csound) & CS_WARNMSG) { - va_list args; - va_start(args, format); - csound->MessageV(csound, CSOUNDMSG_WARNING, format, args); - va_end(args); - } - } else { - va_list args; - va_start(args, format); - vfprintf(stdout, format, args); - va_end(args); - } - } - int noteoff(CSOUND *csound) - { - return OK; - } - static int noteoff_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->noteoff(csound); - } - OPDS opds; -}; - -#if (__cplusplus >= 201103L) && !(defined(__wasi__)) -#pragma message("Defining heap_object_manager_t.") - -/** - * The memory of non-POD C++ or C objects allocated on the heap by Csound - * plugins is managed not by Csound, but by the plugin module. This class - * performs that function. - * - * As long as all memory is allocated in or after csoundModuleCreate and - * de-allocated in csoundModuleDestroy, all will be well. The danger however - * is that memory leaks will occur if allocations are not matched with - * de-allocations, or worse, that crashes will occur if any objects are - * deleted twice or de-referenced after deletion. - * - * So, the plugin should manage a collection of all heap-allocated objects, - * keyed first off the Csound instance, and second off an object handle; and - * all such objects must be accessed only by handle, from this collection. - * - * In csoundModuleDestroy, all objects for the calling instance of Csound - * should be de-allocated, and the sub-collection for that instance should - * also be de-allocated. - * - * If then there are no more sub-collections for other instances of Csound, - * the entire contents of the collection should be de-allocated. - * - * This should be done as a singleton, and in a thread-safe way. - * - * There might still be a problem if any of the heap-allocated objects, - * themselves, have global static members, perhaps created by some external - * dependency. - * - * Please note, objects O in the declaration below must not be std::shared_ptr - * objects. Deletion of objects herein must be unconditional. Rather, the - * objects passed are raw pointers to instances of class O. - */ - template class heap_object_manager_t { - private: - std::map> objects_; - std::recursive_mutex mutex; - heap_object_manager_t(){}; - ~heap_object_manager_t(){}; - public: - static heap_object_manager_t &instance() { - static heap_object_manager_t singleton; - return singleton; - } - std::map> &objects() { - std::lock_guard lock(mutex); - return objects_; - } - /** - * Returns a list of pointers to all objects allocated for this - * instance of Csound. - */ - std::vector &objects_for_csound(CSOUND *csound) { - std::lock_guard lock(mutex); - auto &objects_for_csound_ = objects()[csound]; - return objects_for_csound_; - } - /** - * Returns the handle for the object; if the object has not yet been - * stored, inserts it into the list of object pointers for this - * instance of Csound; otherwise, returns the handle of the stored - * object pointer. - */ - int handle_for_object(CSOUND *csound, O *object) { - std::lock_guard lock(mutex); - auto &objects_for_csound_ = objects_for_csound(csound); - auto iterator = std::find(objects_for_csound_.begin(), objects_for_csound_.end(), object); - if (iterator == objects_for_csound_.end()) { - int handle = objects_for_csound_.size(); - objects_for_csound_.push_back(object); - if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::handle_for_object %p: new object handle: %d (of %ld)\n", object, handle, objects_for_csound_.size()); - return handle; - } else { - int handle = static_cast(iterator - objects_for_csound_.begin()); - if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::handle_for_object: existing object handle: %d\n", handle); - return handle; - } - } - /** - * Returns the object pointer for the handle; - * if the object pointer has not been stored by - * handle, returns a null pointer. - */ - O *object_for_handle(CSOUND *csound, int handle) { - std::lock_guard lock(mutex); - auto &objects_for_csound_ = objects_for_csound(csound); - if (handle >= objects_for_csound_.size()) { - return nullptr; - } - O *object = objects_for_csound_[handle]; - if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::object_for_handle: %p %d (of %ld)\n", object, handle, objects_for_csound_.size()); - return object; - } - /** - * First destroys all objects created by the calling - * instance of Csound, then destroys the list of - * object pointers for this instance of Csound. - */ - void module_destroy(CSOUND *csound) { - std::lock_guard lock(mutex); - auto &objects_for_csound_ = objects_for_csound(csound); - for (int i = 0, n = objects_for_csound_.size(); i < n; ++i) { - delete objects_for_csound_[i]; - objects_for_csound_[i] = nullptr; - } - objects_for_csound_.clear(); - objects().erase(csound); - } -}; - -#else -#pragma message("Not defining heap_manager_t.") -#endif - -} diff --git a/6.x/src/mp3/CMakeLists.txt b/6.x/src/mp3/CMakeLists.txt deleted file mode 100644 index 64f5b074..00000000 --- a/6.x/src/mp3/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -option(BUILD_MP3OUT_OPCODE "Build the mp3out opcode" ON) - -if(USE_VCPKG) - find_package(mp3lame CONFIG REQUIRED) -else() - find_package(LAME) -endif() - -if(BUILD_MP3OUT_OPCODE) - if(LAME_FOUND) - make_plugin(mp3out mp3out.c ${LAME_LIBRARY}) - target_include_directories(mp3out PRIVATE ${LAME_INCLUDE_DIR} ${CSOUND_INCLUDE_DIR}) - elseif(mp3lame_FOUND AND USE_VCPKG) - make_plugin(mp3out mp3out.c mp3lame::mp3lame) - target_include_directories(mp3out PRIVATE ${CSOUND_INCLUDE_DIR}) - endif() - - message(STATUS "Building MP3 opcodes") -endif() diff --git a/6.x/src/mp3/mp3out.c b/6.x/src/mp3/mp3out.c deleted file mode 100644 index f11e4f67..00000000 --- a/6.x/src/mp3/mp3out.c +++ /dev/null @@ -1,141 +0,0 @@ -/* - mp3out.c: - - Copyright (C) 2019 by John ffitch - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#include "csdl.h" -#include - -typedef struct _mp3out { - OPDS h; - MYFLT *al; - MYFLT *ar; - STRINGDAT *filename; - MYFLT *bitrate; - MYFLT *quality; - MYFLT *mode; - lame_global_flags *gfp; - FILE *fout; - AUXCH auxch; - void *mp3buffer; - int mp3buffer_size; - MYFLT *leftpcm; - MYFLT *rightpcm; -} MP3OUT; - -#define DEFAULT_RATE (256) - -#ifdef USE_DOUBLE -#define lame_encode_buffer_ieee_MYFLT lame_encode_buffer_ieee_double -#else -#define lame_encode_buffer_ieee_MYFLT lame_encode_buffer_ieee_float -#endif - -int mp3out_cleanup(CSOUND *csound, MP3OUT *p) -{ - IGN(csound); - int bytes = lame_encode_flush(p->gfp,p->mp3buffer, p->mp3buffer_size); - if (bytes>0) fwrite(p->mp3buffer, 1, bytes, p->fout); - lame_mp3_tags_fid(p->gfp, p->fout); - lame_close(p->gfp); - - p->gfp = NULL; - fclose(p->fout); - return OK; -} - - -int mp3out_init(CSOUND *csound, MP3OUT* p) -{ - int ret_code; - int bitrate, quality, mode; - unsigned int nsmps = csound->GetKsmps(csound); - lame_global_flags *gfp; - gfp = lame_init(); - p->gfp = gfp; - - bitrate = (*p->bitrate<0) ? DEFAULT_RATE : (int)MYFLT2LRND(*p->bitrate); - quality = (*p->quality<0) ? 2 : (int)MYFLT2LRND(*p->quality); - if (quality>9) quality = 9; - mode = MYFLT2LRND(*p->mode); - if (mode>3||mode<0) mode = 1; - lame_set_num_channels(gfp,2); - lame_set_in_samplerate(gfp,csound->GetSr(csound)); - lame_set_brate(gfp,bitrate); - lame_set_mode(gfp,mode); - lame_set_quality(gfp,quality); /* 2=high 5 = medium 7=low */ - - if (UNLIKELY(ret_code = lame_init_params(gfp)) < 0) { - return csound->InitError(csound, - Str("Failed to initialise LAME %d\n"), ret_code); - } - - p->fout = fopen(p->filename->data, "w+b"); - if (p->fout == NULL) { - return csound->InitError(csound, Str("mp3out %s: failed to open file"), p->filename->data); - } - - csound->AuxAlloc(csound, - 2*nsmps*sizeof(MYFLT)+(p->mp3buffer_size=3*nsmps/2+7200), - &p->auxch); - - p->mp3buffer = p->auxch.auxp; - p->leftpcm = (char*)p->auxch.auxp + p->mp3buffer_size; - p->rightpcm = p->leftpcm + nsmps; - - csound->RegisterDeinitCallback(csound, p, - (int32_t (*)(CSOUND*, void*)) mp3out_cleanup); - return OK; -} - -int mp3out_perf(CSOUND *csound, MP3OUT *p) -{ - int bytes; - unsigned int i, nsmps = csound->GetKsmps(csound); - MYFLT zdbfs = csound->Get0dBFS(csound); - - for (i = 0; ileftpcm[i] = p->al[i]/zdbfs; - p->rightpcm[i] = p->ar[i]/zdbfs; - } - - bytes = lame_encode_buffer_ieee_MYFLT(p->gfp, - p->leftpcm, p->rightpcm, - nsmps, p->mp3buffer, p->mp3buffer_size); - - if (bytes >= 0) { - fwrite(p->mp3buffer, 1, bytes, p->fout); - } - else if (bytes < 0) { - return csound->PerfError(csound, &(p->h), - Str("mp3out: write error %d\n"), bytes); - } - - return OK; -} - -#define S(x) sizeof(x) - -static OENTRY localops[] = { - { "mp3out", S(MP3OUT), 0, 3, "", "aaSjjp", (SUBR)mp3out_init, (SUBR)mp3out_perf} -}; - -LINKAGE diff --git a/6.x/src/opencl/CMakeLists.txt b/6.x/src/opencl/CMakeLists.txt deleted file mode 100644 index 16c73b54..00000000 --- a/6.x/src/opencl/CMakeLists.txt +++ /dev/null @@ -1,29 +0,0 @@ -option(BUILD_OPENCL_OPCODES "Build the OpenCL parallel opcodes" OFF) - -find_library (OpenCL_library NAMES OpenCL) -check_deps(BUILD_OPENCL_OPCODES OpenCL_FOUND) - -if(BUILD_OPENCL_OPCODES AND OpenCL_FOUND) - - function(make_opencl_plugin libname srcs) - if(APPLE) - add_library(${libname} SHARED ${srcs}) - else() - add_library(${libname} MODULE ${srcs}) - endif() - - set(i 2) - while( ${i} LESS ${ARGC} ) - target_link_libraries(${libname} ${ARGV${i}}) - math(EXPR i "${i}+1") - endwhile() - - target_include_directories(${libname} PRIVATE ${CSOUND_INCLUDE_DIRS}) - - install(TARGETS ${libname} - LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" ) - endfunction() - - make_opencl_plugin(clop cladsynth.c) - target_link_libraries(clop ${OpenCL_library}) -endif() diff --git a/6.x/src/opencl/build.sh b/6.x/src/opencl/build.sh deleted file mode 100644 index eeb790ec..00000000 --- a/6.x/src/opencl/build.sh +++ /dev/null @@ -1 +0,0 @@ -cc -O3 -dynamiclib -o libclop1.dylib -DUSE_DOUBLE -D_FORTIFY_SOURCE=0 cladsynth.c -I../../include -framework OpenCL -Wno-deprecated-declarations diff --git a/6.x/src/opencl/buildc.sh b/6.x/src/opencl/buildc.sh deleted file mode 100644 index 2bc48d4c..00000000 --- a/6.x/src/opencl/buildc.sh +++ /dev/null @@ -1,2 +0,0 @@ -set -x -c++ -g -O3 -dynamiclib -o libclconv.dylib -std=c++11 -DUSE_DOUBLE -D_FORTIFY_SOURCE=0 clconv.cpp -I../../include -framework OpenCL -Wno-deprecated-declarations diff --git a/6.x/src/opencl/buildcpp.sh b/6.x/src/opencl/buildcpp.sh deleted file mode 100644 index df292a38..00000000 --- a/6.x/src/opencl/buildcpp.sh +++ /dev/null @@ -1,2 +0,0 @@ -set -x -c++ -O3 -dynamiclib -o libclopp.dylib -DUSE_DOUBLE -D_FORTIFY_SOURCE=0 cladsynth2.cpp -I../../include -framework OpenCL -Wno-deprecated-declarations -std=c++11 diff --git a/6.x/src/opencl/cladsyn.csd b/6.x/src/opencl/cladsyn.csd deleted file mode 100644 index f23aba99..00000000 --- a/6.x/src/opencl/cladsyn.csd +++ /dev/null @@ -1,45 +0,0 @@ - - ---opcode-lib=./libclop1.dylib - - - -ksmps = 1 -0dbfs = 1 - - -instr 1 -ifftsize = 2048 -ihopsize = 512 -ibins = 1024 -idev = 1; /* device number */ -asig1 vco2 0.5, 300 -fsig = pvsanal(asig1, ifftsize,ihopsize, ifftsize, 1) -asig = cladsyn(fsig,1,1,ibins,idev) -asig = linenr(asig,0.001,0.01,0.01) - out(asig*0.5) - -endin - -instr 2 -ifftsize = 2048 -ihopsize = 512 -ibins = 1024 -asig1 vco2 0.5, 440 -fsig = pvsanal(asig1, ifftsize,ihopsize, ifftsize, 1) -asig = pvsadsyn(fsig,ibins,1) -asig = linenr(asig,0.001,0.01,0.01) - out(asig*0.5) - -endin - - - - - - - -i1 0 60 - - - diff --git a/6.x/src/opencl/cladsynth.cpp b/6.x/src/opencl/cladsynth.cpp deleted file mode 100644 index 8cc4c120..00000000 --- a/6.x/src/opencl/cladsynth.cpp +++ /dev/null @@ -1,404 +0,0 @@ -/* - cladsynth.cpp - - (c) Victor Lazzarini, 2019 - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#include -#include -#include -#include - -#ifdef __MACH__ -#include -#else -#include -#endif - -const char *code = R"( -#define FMAXLEN ((float)0x40000000) -#define PHMASK 0x3fffffff -#define PI 3.1415926f - -inline void AtomicAdd(volatile __global float *source, const float operand) { - union { - unsigned int intVal; - float floatVal; - } newVal; - union { - unsigned int intVal; - float floatVal; - } prevVal; - do { - prevVal.floatVal = *source; - newVal.floatVal = prevVal.floatVal + operand; - } while (atomic_cmpxchg((volatile __global unsigned int *)source, - prevVal.intVal, newVal.intVal) != prevVal.intVal); -} - -kernel void sample(global float *out, global float *frame, - global long *ph, - global float *amps, float pitch, int bins, - int vsize, float sr) { - int t = get_global_id(0); - int n = t%vsize; /* sample index */ - int h = t/vsize; /* bin index */ - int k = h<<1; - long lph; - float a = amps[h], ascl = ((float)n)/vsize; - float fscal = pitch*FMAXLEN/sr; - lph = (ph[h] + (long)(n*round(frame[k+1]*fscal))) & PHMASK; - a += ascl*(frame[k] - a); - AtomicAdd(&out[n], a*sin((2*PI*lph)/FMAXLEN)); -} - -kernel void update(global float *out, global float *frame, - global long *ph, global float *amps, float pitch, int bins, - int vsize, float sr){ - int h = get_global_id(0); - int k = h << 1,i,j; - /* update phases and amps */ - ph[h] = (ph[h] + (long)(vsize*round(pitch*frame[k+1]*FMAXLEN/sr))) & PHMASK; - amps[h] = frame[k]; - if(h >= vsize) return; - out[h] = 0.f; -} - -)"; - - -const char * cl_error_string(int err) { - switch (err) { - case CL_SUCCESS: return "Success!"; - case CL_DEVICE_NOT_FOUND: return "Device not found."; - case CL_DEVICE_NOT_AVAILABLE: return "Device not available"; - case CL_COMPILER_NOT_AVAILABLE: return "Compiler not available"; - case CL_MEM_OBJECT_ALLOCATION_FAILURE: - return "Memory object allocation failure"; - case CL_OUT_OF_RESOURCES: return "Out of resources"; - case CL_OUT_OF_HOST_MEMORY: return "Out of host memory"; - case CL_PROFILING_INFO_NOT_AVAILABLE: - return "Profiling information not available"; - case CL_MEM_COPY_OVERLAP: return "Memory copy overlap"; - case CL_IMAGE_FORMAT_MISMATCH: return "Image format mismatch"; - case CL_IMAGE_FORMAT_NOT_SUPPORTED: return "Image format not supported"; - case CL_BUILD_PROGRAM_FAILURE: return "Program build failure"; - case CL_MAP_FAILURE: return "Map failure"; - case CL_INVALID_VALUE: return "Invalid value"; - case CL_INVALID_DEVICE_TYPE: return "Invalid device type"; - case CL_INVALID_PLATFORM: return "Invalid platform"; - case CL_INVALID_DEVICE: return "Invalid device"; - case CL_INVALID_CONTEXT: return "Invalid context"; - case CL_INVALID_QUEUE_PROPERTIES: return "Invalid queue properties"; - case CL_INVALID_COMMAND_QUEUE: return "Invalid command queue"; - case CL_INVALID_HOST_PTR: return "Invalid host pointer"; - case CL_INVALID_MEM_OBJECT: return "Invalid memory object"; - case CL_INVALID_IMAGE_FORMAT_DESCRIPTOR: - return "Invalid image format descriptor"; - case CL_INVALID_IMAGE_SIZE: return "Invalid image size"; - case CL_INVALID_SAMPLER: return "Invalid sampler"; - case CL_INVALID_BINARY: return "Invalid binary"; - case CL_INVALID_BUILD_OPTIONS: return "Invalid build options"; - case CL_INVALID_PROGRAM: return "Invalid program"; - case CL_INVALID_PROGRAM_EXECUTABLE: return "Invalid program executable"; - case CL_INVALID_KERNEL_NAME: return "Invalid kernel name"; - case CL_INVALID_KERNEL_DEFINITION: return "Invalid kernel definition"; - case CL_INVALID_KERNEL: return "Invalid kernel"; - case CL_INVALID_ARG_INDEX: return "Invalid argument index"; - case CL_INVALID_ARG_VALUE: return "Invalid argument value"; - case CL_INVALID_ARG_SIZE: return "Invalid argument size"; - case CL_INVALID_KERNEL_ARGS: return "Invalid kernel arguments"; - case CL_INVALID_WORK_DIMENSION: return "Invalid work dimension"; - case CL_INVALID_WORK_GROUP_SIZE: return "Invalid work group size"; - case CL_INVALID_WORK_ITEM_SIZE: return "Invalid work item size"; - case CL_INVALID_GLOBAL_OFFSET: return "Invalid global offset"; - case CL_INVALID_EVENT_WAIT_LIST: return "Invalid event wait list"; - case CL_INVALID_EVENT: return "Invalid event"; - case CL_INVALID_OPERATION: return "Invalid operation"; - case CL_INVALID_GL_OBJECT: return "Invalid OpenGL object"; - case CL_INVALID_BUFFER_SIZE: return "Invalid buffer size"; - case CL_INVALID_MIP_LEVEL: return "Invalid mip-map level"; - default: return "Unknown error"; - } -} - -struct Cladsyn : csnd::Plugin<1, 5> { - - cl_mem out; - cl_mem frame; - cl_mem ph; - cl_mem amps; - int bins; - size_t threads; - int count; - int vsamps; - size_t mthreads; - int framecount; - cl_context context; - cl_command_queue commands; - cl_program program; - cl_kernel kernel1, kernel2; - size_t wgs1, wgs2; - csnd::AuxMem mix; - float cs_sr; - - - int init() { - int asize, ipsize, fpsize, err; - cl_device_id device_ids[32], device_id; - cl_uint num = 0, nump = 0; - cl_platform_id platforms[16]; - uint32_t i; - csnd::pv_frame &fsig = inargs.fsig_data(0); - int inum = (int) inargs[3]; - int idev = (int) inargs[4]; - - if(fsig.hop_size() > 1024) - return csound->init_error("hopsize is too large\n"); - - err = clGetDeviceIDs(NULL, CL_DEVICE_TYPE_ALL, 32, device_ids, &num); - if (err != CL_SUCCESS){ - int devs = 0; - clGetPlatformIDs(16, platforms, &nump); - for(i=0; i < nump && devs < 32; i++){ - char name[128]; - std::stringstream msg; - clGetPlatformInfo(platforms[i], CL_PLATFORM_NAME, 128, name, NULL); - msg << "available platform[" << i << "]: " << name << std::endl; - csound->message(msg.str()); - err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, - 32-devs, &device_ids[devs], &num); - if (err != CL_SUCCESS) { - std::stringstream emsg; - emsg << "failed to find an OpenCL device!" << - cl_error_string(err) << std::endl; - csound->init_error(emsg.str()); - } - } - devs += num; - } - - for(i=0; i < num; i++){ - char name[128]; - cl_device_type type; - clGetDeviceInfo(device_ids[i], CL_DEVICE_NAME, 128, name, NULL); - clGetDeviceInfo(device_ids[i], CL_DEVICE_TYPE, sizeof(cl_device_type), - &type, NULL); - if(type & CL_DEVICE_TYPE_CPU) { - std::stringstream msg; - msg << "available CPU[device " << i << "] " << name << std::endl; - csound->message(msg.str()); - } - else if(type & CL_DEVICE_TYPE_GPU) { - std::stringstream msg; - msg << "available GPU[device " << i << "] " << name << std::endl; - csound->message(msg.str()); - } - else if(type & CL_DEVICE_TYPE_ACCELERATOR) { - std::stringstream msg; - msg << "available ACCELLERATOR[device " << i << "] " << name - << std::endl; - csound->message(msg.str()); - } - else { - std::stringstream msg; - msg << "available GENERIC[device " << i << "] " << name << std::endl; - csound->message(msg.str()); - } - } - - // SELECT THE DEVICE HERE - if(idev < num) - device_id = device_ids[idev]; - else - device_id = device_ids[num-1]; - - context = clCreateContext(0, 1, &device_id, NULL, NULL, &err); - if (!context) { - std::stringstream msg; - msg << "Failed to create a compute context! " << cl_error_string(err) - << std::endl; - return csound->init_error(msg.str()); - } - - // Create commands - commands = clCreateCommandQueue(context, device_id, 0, &err); - if (!commands) { - std::stringstream msg; - msg << "Failed to create commands! " << cl_error_string(err) - << std::endl; - return csound->init_error(msg.str()); - } - // Create the compute program from the source buffer - program = clCreateProgramWithSource(context, 1, (const char **) &code, - NULL, &err); - if (!program){ - std::stringstream msg; - msg << "Failed to create program! " << cl_error_string(err) - << std::endl; - return csound->init_error(msg.str()); - } - - err = clBuildProgram(program, 0, NULL, NULL, NULL, NULL); - if (err != CL_SUCCESS) - { - size_t len; - char buffer[2048]; - std::stringstream msg; - clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, - sizeof(buffer), buffer, &len); - msg << "Failed to build program executable! " << cl_error_string(err) - << std::endl << buffer << std::endl; - return csound->init_error(msg.str()); - } - - kernel1 = clCreateKernel(program, "sample", &err); - if (!kernel1 || err != CL_SUCCESS) { - std::stringstream msg; - msg << "Failed to create sample compute kernel! " << cl_error_string(err) - << std::endl; - return csound->init_error(msg.str()); - } - - kernel2 = clCreateKernel(program, "update", &err); - if (!kernel2 || err != CL_SUCCESS) { - std::stringstream msg; - msg << "Failed to create update compute kernel! " << cl_error_string(err) - << std::endl; - return csound->init_error(msg.str()); - } - - { - char name[128]; - std::stringstream msg; - clGetDeviceInfo(device_id, CL_DEVICE_NAME, 128, name, NULL); - msg << "using device: " << name << std::endl; - csound->message(msg.str()); - } - - bins = fsig.nbins() - 1; - if(inum > 0 && inum < bins) bins = inum; - vsamps = fsig.hop_size(); - threads = bins*vsamps; - mthreads = bins > vsamps ? bins : vsamps; - asize = vsamps*sizeof(cl_float); - ipsize = mthreads*sizeof(cl_long); - fpsize = fsig.dft_size()*sizeof(cl_float); - - out = clCreateBuffer(context,0, asize, NULL, NULL); - frame = clCreateBuffer(context, CL_MEM_READ_ONLY, fpsize, NULL, NULL); - ph = clCreateBuffer(context,0, ipsize, NULL, NULL); - amps = clCreateBuffer(context,0, mthreads*sizeof(cl_float), NULL, NULL); - - // memset needed? - asize = vsamps*sizeof(float); - mix.allocate(csound, asize); - csound->plugin_deinit(this); - count = 0; - cs_sr = csound->sr(); - - clGetKernelWorkGroupInfo(kernel1, - device_id, CL_KERNEL_WORK_GROUP_SIZE, sizeof(wgs1), &wgs1, NULL); - clGetKernelWorkGroupInfo(kernel2, - device_id, CL_KERNEL_WORK_GROUP_SIZE, sizeof(wgs2), &wgs2, NULL); - - clSetKernelArg(kernel1, 0, sizeof(cl_mem), &out); - clSetKernelArg(kernel1, 1, sizeof(cl_mem), &frame); - clSetKernelArg(kernel1, 2, sizeof(cl_mem), &ph); - clSetKernelArg(kernel1, 3, sizeof(cl_mem), &s); - clSetKernelArg(kernel1, 5, sizeof(cl_int), &bins); - clSetKernelArg(kernel1, 6, sizeof(cl_int), &vsamps); - clSetKernelArg(kernel1, 7, sizeof(cl_float), &cs_sr); - - clSetKernelArg(kernel2, 0, sizeof(cl_mem), &out); - clSetKernelArg(kernel2, 1, sizeof(cl_mem), &frame); - clSetKernelArg(kernel2, 2, sizeof(cl_mem), &ph); - clSetKernelArg(kernel2, 3, sizeof(cl_mem), &s); - clSetKernelArg(kernel2, 5, sizeof(cl_int), &bins); - clSetKernelArg(kernel2, 6, sizeof(cl_int), &vsamps); - clSetKernelArg(kernel2, 7, sizeof(cl_float), &cs_sr); - - return OK; - } - - int deinit() { - clReleaseMemObject(out); - clReleaseMemObject(ph); - clReleaseMemObject(frame); - clReleaseMemObject(amps); - clReleaseProgram(program); - clReleaseKernel(kernel1); - clReleaseKernel(kernel2); - clReleaseCommandQueue(commands); - clReleaseContext(context); - return OK; - } - - int aperf() { - - uint32_t n; - csnd::AudioSig asig(this, outargs(0)); - float *fp = inargs.fsig_data(0).data(); - - for (auto &s : asig) { - if(count == 0) { - int err; - float freq = inargs[2]; - clSetKernelArg(kernel1, 4, sizeof(cl_float), &freq); - clSetKernelArg(kernel2, 4, sizeof(cl_float), &freq); - - clEnqueueWriteBuffer(commands,frame, CL_TRUE, 0, sizeof(cl_float)*bins*2, - fp, 0, NULL, NULL); - err = clEnqueueNDRangeKernel(commands, kernel1, 1, NULL, &threads, &wgs1, - 0, NULL, NULL); - if(err) { - std::stringstream msg; - msg << "Error: Failed to compute sample kernel!" << cl_error_string(err) - << std::endl; - csound->message(msg.str()); - } - clFinish(commands); - clEnqueueReadBuffer(commands, out, - CL_TRUE, 0,vsamps*sizeof(cl_float), mix.data(), 0, NULL, NULL); - err = clEnqueueNDRangeKernel(commands,kernel2, 1, NULL, &mthreads, - &wgs2, 0, NULL, NULL); - if(err) { - std::stringstream msg; - msg << "Error: Failed to compute update kernel!" << cl_error_string(err) - << std::endl; - csound->message(msg.str()); - } - count = vsamps; - } - - s = mix[vsamps - count]*inargs[1]; - count--; - } - - return OK; - } -}; - -#include -void csnd::on_load(Csound *csound) { - csnd::plugin(csound, "cladsyn", "a", "fkkii", csnd::thread::ia); -} - - diff --git a/6.x/src/opencl/cladsynth2.cpp b/6.x/src/opencl/cladsynth2.cpp deleted file mode 100644 index 410518ed..00000000 --- a/6.x/src/opencl/cladsynth2.cpp +++ /dev/null @@ -1,412 +0,0 @@ -/* - cladsynth.cpp - - (c) Victor Lazzarini, 2019 - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#include -#include -#include -#include - -#ifdef __MACH__ -#include -#else -#include -#endif - -const char *code = R"( -#define FMAXLEN ((float)0x40000000) -#define PHMASK 0x3fffffff -#define PI 3.1415926f - -inline void AtomicAdd(volatile __global float *source, const float operand) { - union { - unsigned int intVal; - float floatVal; - } newVal; - union { - unsigned int intVal; - float floatVal; - } prevVal; - do { - prevVal.floatVal = *source; - newVal.floatVal = prevVal.floatVal + operand; - } while (atomic_cmpxchg((volatile __global unsigned int *)source, - prevVal.intVal, newVal.intVal) != prevVal.intVal); -} - -kernel void sample(global float *out, global const float *frame, - global const long *ph, - global const float *amps, float pitch, int bins, - int vsize, float sr) { - int t = get_global_id(0); - int n = t%vsize; /* sample index */ - int h = t/vsize; /* bin index */ - int k = h<<1; - long lph; - float a = amps[h], ascl = ((float)n)/vsize; - float fscal = pitch*FMAXLEN/sr; - lph = (ph[h] + (long)(n*round(frame[k+1]*fscal))) & PHMASK; - a += ascl*(frame[k] - a); - AtomicAdd(&out[n], a*sin((2*PI*lph)/FMAXLEN)); -} - -kernel void update(global float *out, global float *frame, - global long *ph, global float *amps, float pitch, int bins, - int vsize, float sr){ - int h = get_global_id(0); - int k = h << 1,i,j; - /* update phases and amps */ - ph[h] = (ph[h] + (long)(vsize*round(pitch*frame[k+1]*FMAXLEN/sr))) & PHMASK; - amps[h] = frame[k]; - if(h >= vsize) return; - out[h] = 0.f; -} - -)"; - - -const char * cl_error_string(int err) { - switch (err) { - case CL_SUCCESS: return "Success!"; - case CL_DEVICE_NOT_FOUND: return "Device not found."; - case CL_DEVICE_NOT_AVAILABLE: return "Device not available"; - case CL_COMPILER_NOT_AVAILABLE: return "Compiler not available"; - case CL_MEM_OBJECT_ALLOCATION_FAILURE: - return "Memory object allocation failure"; - case CL_OUT_OF_RESOURCES: return "Out of resources"; - case CL_OUT_OF_HOST_MEMORY: return "Out of host memory"; - case CL_PROFILING_INFO_NOT_AVAILABLE: - return "Profiling information not available"; - case CL_MEM_COPY_OVERLAP: return "Memory copy overlap"; - case CL_IMAGE_FORMAT_MISMATCH: return "Image format mismatch"; - case CL_IMAGE_FORMAT_NOT_SUPPORTED: return "Image format not supported"; - case CL_BUILD_PROGRAM_FAILURE: return "Program build failure"; - case CL_MAP_FAILURE: return "Map failure"; - case CL_INVALID_VALUE: return "Invalid value"; - case CL_INVALID_DEVICE_TYPE: return "Invalid device type"; - case CL_INVALID_PLATFORM: return "Invalid platform"; - case CL_INVALID_DEVICE: return "Invalid device"; - case CL_INVALID_CONTEXT: return "Invalid context"; - case CL_INVALID_QUEUE_PROPERTIES: return "Invalid queue properties"; - case CL_INVALID_COMMAND_QUEUE: return "Invalid command queue"; - case CL_INVALID_HOST_PTR: return "Invalid host pointer"; - case CL_INVALID_MEM_OBJECT: return "Invalid memory object"; - case CL_INVALID_IMAGE_FORMAT_DESCRIPTOR: - return "Invalid image format descriptor"; - case CL_INVALID_IMAGE_SIZE: return "Invalid image size"; - case CL_INVALID_SAMPLER: return "Invalid sampler"; - case CL_INVALID_BINARY: return "Invalid binary"; - case CL_INVALID_BUILD_OPTIONS: return "Invalid build options"; - case CL_INVALID_PROGRAM: return "Invalid program"; - case CL_INVALID_PROGRAM_EXECUTABLE: return "Invalid program executable"; - case CL_INVALID_KERNEL_NAME: return "Invalid kernel name"; - case CL_INVALID_KERNEL_DEFINITION: return "Invalid kernel definition"; - case CL_INVALID_KERNEL: return "Invalid kernel"; - case CL_INVALID_ARG_INDEX: return "Invalid argument index"; - case CL_INVALID_ARG_VALUE: return "Invalid argument value"; - case CL_INVALID_ARG_SIZE: return "Invalid argument size"; - case CL_INVALID_KERNEL_ARGS: return "Invalid kernel arguments"; - case CL_INVALID_WORK_DIMENSION: return "Invalid work dimension"; - case CL_INVALID_WORK_GROUP_SIZE: return "Invalid work group size"; - case CL_INVALID_WORK_ITEM_SIZE: return "Invalid work item size"; - case CL_INVALID_GLOBAL_OFFSET: return "Invalid global offset"; - case CL_INVALID_EVENT_WAIT_LIST: return "Invalid event wait list"; - case CL_INVALID_EVENT: return "Invalid event"; - case CL_INVALID_OPERATION: return "Invalid operation"; - case CL_INVALID_GL_OBJECT: return "Invalid OpenGL object"; - case CL_INVALID_BUFFER_SIZE: return "Invalid buffer size"; - case CL_INVALID_MIP_LEVEL: return "Invalid mip-map level"; - default: return "Unknown error"; - } -} - -struct Cladsyn : csnd::Plugin<1, 6> { - - cl_mem out; - cl_mem frame; - cl_mem ph; - cl_mem amps; - int bins; - size_t threads; - int count; - int vsamps; - size_t mthreads; - int framecount; - cl_context context; - cl_command_queue commands; - cl_program program; - cl_kernel kernel1, kernel2; - size_t wgs1, wgs2; - csnd::AuxMem mix; - csnd::AuxMem spec; - csnd::Table ftab; - csnd::Table atab; - float cs_sr; - - - int init() { - int asize, ipsize, fpsize, err; - cl_device_id device_ids[32], device_id; - cl_uint num = 0, nump = 0; - cl_platform_id platforms[16]; - uint32_t i; - int idev = (int) inargs[5]; - int updt = (int) inargs[4]; - ftab.init(csound, inargs(3)); - atab.init(csound, inargs(2)); - - if(updt > 1024) - return csound->init_error("update period is too large\n"); - - err = clGetDeviceIDs(NULL, CL_DEVICE_TYPE_ALL, 32, device_ids, &num); - if (err != CL_SUCCESS){ - int devs = 0; - clGetPlatformIDs(16, platforms, &nump); - for(i=0; i < nump && devs < 32; i++){ - char name[128]; - std::stringstream msg; - clGetPlatformInfo(platforms[i], CL_PLATFORM_NAME, 128, name, NULL); - msg << "available platform[" << i << "]: " << name << std::endl; - csound->message(msg.str()); - err = clGetDeviceIDs(platforms[i], CL_DEVICE_TYPE_ALL, - 32-devs, &device_ids[devs], &num); - if (err != CL_SUCCESS) { - std::stringstream emsg; - emsg << "failed to find an OpenCL device!" << - cl_error_string(err) << std::endl; - csound->init_error(emsg.str()); - } - } - devs += num; - } - - for(i=0; i < num; i++){ - char name[128]; - cl_device_type type; - clGetDeviceInfo(device_ids[i], CL_DEVICE_NAME, 128, name, NULL); - clGetDeviceInfo(device_ids[i], CL_DEVICE_TYPE, sizeof(cl_device_type), - &type, NULL); - if(type & CL_DEVICE_TYPE_CPU) { - std::stringstream msg; - msg << "available CPU[device " << i << "] " << name << std::endl; - csound->message(msg.str()); - } - else if(type & CL_DEVICE_TYPE_GPU) { - std::stringstream msg; - msg << "available GPU[device " << i << "] " << name << std::endl; - csound->message(msg.str()); - } - else if(type & CL_DEVICE_TYPE_ACCELERATOR) { - std::stringstream msg; - msg << "available ACCELLERATOR[device " << i << "] " << name - << std::endl; - csound->message(msg.str()); - } - else { - std::stringstream msg; - msg << "available GENERIC[device " << i << "] " << name << std::endl; - csound->message(msg.str()); - } - } - - // SELECT THE DEVICE HERE - if(idev < num) - device_id = device_ids[idev]; - else - device_id = device_ids[num-1]; - - context = clCreateContext(0, 1, &device_id, NULL, NULL, &err); - if (!context) { - std::stringstream msg; - msg << "Failed to create a compute context! " << cl_error_string(err) - << std::endl; - return csound->init_error(msg.str()); - } - - // Create commands - commands = clCreateCommandQueue(context, device_id, 0, &err); - if (!commands) { - std::stringstream msg; - msg << "Failed to create commands! " << cl_error_string(err) - << std::endl; - return csound->init_error(msg.str()); - } - // Create the compute program from the source buffer - program = clCreateProgramWithSource(context, 1, (const char **) &code, - NULL, &err); - if (!program){ - std::stringstream msg; - msg << "Failed to create program! " << cl_error_string(err) - << std::endl; - return csound->init_error(msg.str()); - } - - err = clBuildProgram(program, 0, NULL, NULL, NULL, NULL); - if (err != CL_SUCCESS) - { - size_t len; - char buffer[2048]; - std::stringstream msg; - clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, - sizeof(buffer), buffer, &len); - msg << "Failed to build program executable! " << cl_error_string(err) - << std::endl << buffer << std::endl; - return csound->init_error(msg.str()); - } - - kernel1 = clCreateKernel(program, "sample", &err); - if (!kernel1 || err != CL_SUCCESS) { - std::stringstream msg; - msg << "Failed to create sample compute kernel! " << cl_error_string(err) - << std::endl; - return csound->init_error(msg.str()); - } - - kernel2 = clCreateKernel(program, "update", &err); - if (!kernel2 || err != CL_SUCCESS) { - std::stringstream msg; - msg << "Failed to create update compute kernel! " << cl_error_string(err) - << std::endl; - return csound->init_error(msg.str()); - } - - { - char name[128]; - std::stringstream msg; - clGetDeviceInfo(device_id, CL_DEVICE_NAME, 128, name, NULL); - msg << "using device: " << name << std::endl; - csound->message(msg.str()); - } - - bins = atab.len() < ftab.len() ? atab.len() : ftab.len(); - vsamps = updt; - threads = bins*vsamps; - mthreads = bins > vsamps ? bins : vsamps; - asize = vsamps*sizeof(cl_float); - ipsize = mthreads*sizeof(cl_long); - fpsize = 2*bins*sizeof(cl_float); - - out = clCreateBuffer(context,0, asize, NULL, NULL); - frame = clCreateBuffer(context, CL_MEM_READ_ONLY, fpsize, NULL, NULL); - ph = clCreateBuffer(context,0, ipsize, NULL, NULL); - amps = clCreateBuffer(context,0, mthreads*sizeof(cl_float), NULL, NULL); - - // memset needed? - asize = vsamps*sizeof(float); - mix.allocate(csound, asize); - spec.allocate(csound, bins*2); - csound->plugin_deinit(this); - count = 0; - cs_sr = csound->sr(); - - clGetKernelWorkGroupInfo(kernel1, - device_id, CL_KERNEL_WORK_GROUP_SIZE, sizeof(wgs1), &wgs1, NULL); - clGetKernelWorkGroupInfo(kernel2, - device_id, CL_KERNEL_WORK_GROUP_SIZE, sizeof(wgs2), &wgs2, NULL); - - clSetKernelArg(kernel1, 0, sizeof(cl_mem), &out); - clSetKernelArg(kernel1, 1, sizeof(cl_mem), &frame); - clSetKernelArg(kernel1, 2, sizeof(cl_mem), &ph); - clSetKernelArg(kernel1, 3, sizeof(cl_mem), &s); - clSetKernelArg(kernel1, 5, sizeof(cl_int), &bins); - clSetKernelArg(kernel1, 6, sizeof(cl_int), &vsamps); - clSetKernelArg(kernel1, 7, sizeof(cl_float), &cs_sr); - - clSetKernelArg(kernel2, 0, sizeof(cl_mem), &out); - clSetKernelArg(kernel2, 1, sizeof(cl_mem), &frame); - clSetKernelArg(kernel2, 2, sizeof(cl_mem), &ph); - clSetKernelArg(kernel2, 3, sizeof(cl_mem), &s); - clSetKernelArg(kernel2, 5, sizeof(cl_int), &bins); - clSetKernelArg(kernel2, 6, sizeof(cl_int), &vsamps); - clSetKernelArg(kernel2, 7, sizeof(cl_float), &cs_sr); - - return OK; - } - - int deinit() { - clReleaseMemObject(out); - clReleaseMemObject(ph); - clReleaseMemObject(frame); - clReleaseMemObject(amps); - clReleaseProgram(program); - clReleaseKernel(kernel1); - clReleaseKernel(kernel2); - clReleaseCommandQueue(commands); - clReleaseContext(context); - return OK; - } - - int aperf() { - - uint32_t n; - csnd::AudioSig asig(this, outargs(0)); - - for (auto &s : asig) { - if(count == 0) { - int err = 0; - float freq = inargs[1]; - clSetKernelArg(kernel1, 4, sizeof(cl_float), &freq); - clSetKernelArg(kernel2, 4, sizeof(cl_float), &freq); - - float *specf = spec.data(); - for (int k = 0, j = 0; k < bins; k++, j+=2) { - specf[j] = atab[k]; - specf[j+1] = ftab[k]; - } - - clEnqueueWriteBuffer(commands,frame, CL_TRUE, 0, sizeof(cl_float)*bins*2, - specf, 0, NULL, NULL); - err = clEnqueueNDRangeKernel(commands, kernel1, 1, NULL, &threads, &wgs1, - 0, NULL, NULL); - if(err) { - std::stringstream msg; - msg << "Error: Failed to compute sample kernel!" << cl_error_string(err) - << std::endl; - csound->message(msg.str()); - } - clFinish(commands); - clEnqueueReadBuffer(commands, out, - CL_TRUE, 0,vsamps*sizeof(cl_float), mix.data(), 0, NULL, NULL); - err = clEnqueueNDRangeKernel(commands,kernel2, 1, NULL, &mthreads, - &wgs2, 0, NULL, NULL); - if(err) { - std::stringstream msg; - msg << "Error: Failed to compute update kernel!" << cl_error_string(err) - << std::endl; - csound->message(msg.str()); - } - count = vsamps; - } - s = mix[vsamps - count]*inargs[0]; - count--; - } - - return OK; - } -}; - -#include -void csnd::on_load(Csound *csound) { - csnd::plugin(csound, "cladsyn", "a", "kkiiii", csnd::thread::ia); -} - - diff --git a/6.x/src/opencl/clconv.csd b/6.x/src/opencl/clconv.csd deleted file mode 100644 index 0de5c297..00000000 --- a/6.x/src/opencl/clconv.csd +++ /dev/null @@ -1,28 +0,0 @@ - - ---opcode-lib=./libclconv.dylib - - - -ksmps = 1 -0dbfs = 1 - -gift ftgen 0, 0, 0, 1, "pianoc2.wav", 0,0,1 -;tablew 1, 0, gift - -instr 1 -ipsize = 512 -idev = 1; /* device number */ -ain mpulse 1, 7 -asig clconv ain, gift, ipsize, idev - out(asig) - -endin - - - - -i1 0 10 - - - diff --git a/6.x/src/opencl/libcl.dylib b/6.x/src/opencl/libcl.dylib deleted file mode 100644 index fb005909..00000000 Binary files a/6.x/src/opencl/libcl.dylib and /dev/null differ diff --git a/6.x/src/opencl/libclconv.dylib b/6.x/src/opencl/libclconv.dylib deleted file mode 100644 index 49784317..00000000 Binary files a/6.x/src/opencl/libclconv.dylib and /dev/null differ diff --git a/6.x/src/opencl/libclconv.dylib.dSYM/Contents/Info.plist b/6.x/src/opencl/libclconv.dylib.dSYM/Contents/Info.plist deleted file mode 100644 index 44c27ad0..00000000 --- a/6.x/src/opencl/libclconv.dylib.dSYM/Contents/Info.plist +++ /dev/null @@ -1,20 +0,0 @@ - - - - - CFBundleDevelopmentRegion - English - CFBundleIdentifier - com.apple.xcode.dsym.libclconv.dylib - CFBundleInfoDictionaryVersion - 6.0 - CFBundlePackageType - dSYM - CFBundleSignature - ???? - CFBundleShortVersionString - 1.0 - CFBundleVersion - 1 - - diff --git a/6.x/src/opencl/libclconv.dylib.dSYM/Contents/Resources/DWARF/libclconv.dylib b/6.x/src/opencl/libclconv.dylib.dSYM/Contents/Resources/DWARF/libclconv.dylib deleted file mode 100644 index 2b1d979f..00000000 Binary files a/6.x/src/opencl/libclconv.dylib.dSYM/Contents/Resources/DWARF/libclconv.dylib and /dev/null differ diff --git a/6.x/src/opencl/openclop.cpp b/6.x/src/opencl/openclop.cpp deleted file mode 100644 index b8b74085..00000000 --- a/6.x/src/opencl/openclop.cpp +++ /dev/null @@ -1,271 +0,0 @@ -#ifdef __MACH__ -#include -#else -#include -#endif - -#include -#include -#include - -const char *cl_error_string(int err); - -class OCl { - int cl_err; - cl_context context; - cl_command_queue commands; - cl_program program; - std::vector kernel; - std::vector data; - void (*err)(std::string s, void *d); - void *userData; - - static void msg(std::string str, void *userData) { - if (userData == NULL) - std::cout << str << std::endl; - } - -public: - - OCl(cl_device_id device_id, int kernels, int arrays, const char *code, - void (*errs)(std::string s, void *d), void *uData) : - context(NULL), commands(NULL), program(NULL), kernel(kernels), - data(arrays), err(errs == NULL ? this->msg : errs), - userData(uData) { - - context = clCreateContext(0, 1, &device_id, NULL, NULL, &cl_err); - if (!context) { - err(cl_error_string(cl_err), userData); - return; - } - commands = clCreateCommandQueue(context, device_id, 0, &cl_err); - if (!commands) { - err(cl_error_string(cl_err), userData); - return; - } - program = clCreateProgramWithSource(context, 1, (const char **)&code, - NULL, &cl_err); - if (!program) { - err("error creating opencl program\n", userData); - err(cl_error_string(cl_err), userData); - return; - } - cl_err = clBuildProgram(program, 0, NULL, NULL, NULL, NULL); - if (cl_err) { - char log[2048]; - size_t llen; - clGetProgramBuildInfo(program, device_id, CL_PROGRAM_BUILD_LOG, sizeof(log), - log, &llen); - err("error building opencl program\n", userData); - err(cl_error_string(cl_err), userData); - err(log, userData); - return; - } - } - - ~OCl() { - for(cl_mem i : data) clReleaseMemObject(i); - for(cl_kernel i : kernel) clReleaseKernel(i); - clReleaseProgram(program); - clReleaseCommandQueue(commands); - clReleaseContext(context); - } - - int execute(int kern, size_t threads) { - return clEnqueueNDRangeKernel(commands, kernel[kern], 1, NULL, &threads, NULL, - 0, NULL, NULL); - } - - int setKernel(int n, const char *name) { - kernel[n] = clCreateKernel(program, name, &cl_err); - return cl_err; - } - - int alloc(int array, size_t bytes) { - data[array] = clCreateBuffer(context, 0, bytes, NULL, &cl_err); - return cl_err; - } - - int setArg(int kern, int arg, int num) { - return clSetKernelArg(kernel[kern], num, sizeof(cl_int), &arg); - } - - int setArg(int kern, float arg, int num) { - return clSetKernelArg(kernel[kern], num, sizeof(cl_float), &arg); - } - - int setArg(int kern, double arg, int num) { - return clSetKernelArg(kernel[kern], num, sizeof(cl_double), &arg); - } - - int setArg(int kern, cl_mem *arg, int num) { - return clSetKernelArg(kernel[kern], num, sizeof(cl_mem), arg); - } - - int setArg(int kern, int *arg, int size, int array, int num) { - cl_err = clEnqueueWriteBuffer(commands, data[array], 0, 0, size*sizeof(cl_int), arg, 0, - NULL, NULL); - if (cl_err != CL_SUCCESS) - return cl_err; - else - return clSetKernelArg(kernel[kern], num, sizeof(cl_mem), &data[array]); - } - - int setArg(int kern, float *arg, int size, int array, int num) { - cl_err = clEnqueueWriteBuffer(commands, data[array], 0, 0, size*sizeof(cl_float), arg, 0, - NULL, NULL); - if (cl_err != CL_SUCCESS) - return cl_err; - else - return clSetKernelArg(kernel[kern], num, sizeof(cl_mem), &data[array]); - } - - int setArg(int kern, double *arg, int size, int array, int num) { - cl_err = clEnqueueWriteBuffer(commands, data[array], 0, 0, size*sizeof(cl_float), arg, 0, - NULL, NULL); - if (cl_err != CL_SUCCESS) - return cl_err; - else - return clSetKernelArg(kernel[kern], num, sizeof(cl_mem), &data[array]); - } - - int getData(float *out, int size, int array) { - return clEnqueueReadBuffer(commands, data[array], CL_TRUE, 0, size*sizeof(cl_float), - out, 0, NULL, NULL); - } - - int getData(double *out, int size, int array) { - return clEnqueueReadBuffer(commands, data[array], CL_TRUE, 0, size*sizeof(cl_double), - out, 0, NULL, NULL); - } - - int getData(int *out, int size, int array) { - return clEnqueueReadBuffer(commands, data[array], CL_TRUE, 0, size*sizeof(cl_int), - out, 0, NULL, NULL); - } - - cl_mem *getData(int array) { - return &data[array]; - } - - int setData(float *out, int size, int array) { - return clEnqueueWriteBuffer(commands, data[array], 0, 0, size*sizeof(cl_float), - out, 0, NULL, NULL); - } - - int setData(double *out, int size, int array) { - return clEnqueueWriteBuffer(commands, data[array], 0, 0, size*sizeof(cl_double), - out, 0, NULL, NULL); - } - - int setData(int *out, int size, int array) { - return clEnqueueWriteBuffer(commands, data[array], 0, 0, size*sizeof(cl_int), - out, 0, NULL, NULL); - } - - int copyData(int array1, int array2, int bytes, - int offs1 = 0, int offs2 = 0) { - return clEnqueueCopyBuffer(commands, data[array1], data[array2], offs1, offs2, - bytes, 0, NULL, NULL); - } - -}; - -const char *cl_error_string(int err) { - switch (err) { - case CL_SUCCESS: - return "Success!"; - case CL_DEVICE_NOT_FOUND: - return "Device not found."; - case CL_DEVICE_NOT_AVAILABLE: - return "Device not available"; - case CL_COMPILER_NOT_AVAILABLE: - return "Compiler not available"; - case CL_MEM_OBJECT_ALLOCATION_FAILURE: - return "Memory object allocation failure"; - case CL_OUT_OF_RESOURCES: - return "Out of resources"; - case CL_OUT_OF_HOST_MEMORY: - return "Out of host memory"; - case CL_PROFILING_INFO_NOT_AVAILABLE: - return "Profiling information not available"; - case CL_MEM_COPY_OVERLAP: - return "Memory copy overlap"; - case CL_IMAGE_FORMAT_MISMATCH: - return "Image format mismatch"; - case CL_IMAGE_FORMAT_NOT_SUPPORTED: - return "Image format not supported"; - case CL_BUILD_PROGRAM_FAILURE: - return "Program build failure"; - case CL_MAP_FAILURE: - return "Map failure"; - case CL_INVALID_VALUE: - return "Invalid value"; - case CL_INVALID_DEVICE_TYPE: - return "Invalid device type"; - case CL_INVALID_PLATFORM: - return "Invalid platform"; - case CL_INVALID_DEVICE: - return "Invalid device"; - case CL_INVALID_CONTEXT: - return "Invalid context"; - case CL_INVALID_QUEUE_PROPERTIES: - return "Invalid queue properties"; - case CL_INVALID_COMMAND_QUEUE: - return "Invalid command queue"; - case CL_INVALID_HOST_PTR: - return "Invalid host pointer"; - case CL_INVALID_MEM_OBJECT: - return "Invalid memory object"; - case CL_INVALID_IMAGE_FORMAT_DESCRIPTOR: - return "Invalid image format descriptor"; - case CL_INVALID_IMAGE_SIZE: - return "Invalid image size"; - case CL_INVALID_SAMPLER: - return "Invalid sampler"; - case CL_INVALID_BINARY: - return "Invalid binary"; - case CL_INVALID_BUILD_OPTIONS: - return "Invalid build options"; - case CL_INVALID_PROGRAM: - return "Invalid program"; - case CL_INVALID_PROGRAM_EXECUTABLE: - return "Invalid program executable"; - case CL_INVALID_KERNEL_NAME: - return "Invalid kernel name"; - case CL_INVALID_KERNEL_DEFINITION: - return "Invalid kernel definition"; - case CL_INVALID_KERNEL: - return "Invalid kernel"; - case CL_INVALID_ARG_INDEX: - return "Invalid argument index"; - case CL_INVALID_ARG_VALUE: - return "Invalid argument value"; - case CL_INVALID_ARG_SIZE: - return "Invalid argument size"; - case CL_INVALID_KERNEL_ARGS: - return "Invalid kernel arguments"; - case CL_INVALID_WORK_DIMENSION: - return "Invalid work dimension"; - case CL_INVALID_WORK_GROUP_SIZE: - return "Invalid work group size"; - case CL_INVALID_WORK_ITEM_SIZE: - return "Invalid work item size"; - case CL_INVALID_GLOBAL_OFFSET: - return "Invalid global offset"; - case CL_INVALID_EVENT_WAIT_LIST: - return "Invalid event wait list"; - case CL_INVALID_EVENT: - return "Invalid event"; - case CL_INVALID_OPERATION: - return "Invalid operation"; - case CL_INVALID_GL_OBJECT: - return "Invalid OpenGL object"; - case CL_INVALID_BUFFER_SIZE: - return "Invalid buffer size"; - case CL_INVALID_MIP_LEVEL: - return "Invalid mip-map level"; - default: - return "Unknown error"; - } -} diff --git a/6.x/src/p5glove/CMakeLists.txt b/6.x/src/p5glove/CMakeLists.txt deleted file mode 100644 index 8e6dcc49..00000000 --- a/6.x/src/p5glove/CMakeLists.txt +++ /dev/null @@ -1,19 +0,0 @@ -option(BUILD_P5GLOVE_OPCODES "Build the P5Glove opcodes" ON) - -if (NOT APPLE) - find_path(P5GLOVE_INCLUDE_DIR p5glove.h /usr/local/include) - find_library(P5GLOVE_LIB p5glove) - check_deps(BUILD_P5GLOVE_OPCODES P5GLOVE_LIB) - - if(BUILD_P5GLOVE_OPCODES) - make_plugin(p5g p5glove.c ${P5GLOVE_LIB}) - target_include_directories(p5g PRIVATE ${P5GLOVE_INCLUDE_DIR}) - target_include_directories(p5g PRIVATE ${CSOUND_INCLUDE_DIR}) - endif() -else() - if(BUILD_P5GLOVE_OPCODES) - make_plugin(p5g "p5glove.c;libp5glove/p5glove.c;libp5glove/usb_hid_macosx.c") - target_link_libraries(p5g "-framework IOKit" "-framework CoreFoundation") - target_include_directories(p5g PRIVATE ./libp5glove ${CSOUND_INCLUDE_DIR}) - endif() -endif() diff --git a/6.x/src/p5glove/p5g_mac b/6.x/src/p5glove/p5g_mac deleted file mode 100644 index 14970924..00000000 --- a/6.x/src/p5glove/p5g_mac +++ /dev/null @@ -1,24 +0,0 @@ -#define P5G_BUTTONS #0# -#define P5G_BUTTON_A #1# -#define P5G_BUTTON_B #2# -#define P5G_BUTTON_C #4# -#define P5G_JUSTPUSH #8# -#define P5G_JUSTPU_A #9# -#define P5G_JUSTPU_B #10# -#define P5G_JUSTPU_C #12# -#define P5G_RELEASED #16# -#define P5G_RELSED_A #17# -#define P5G_RELSED_B #18# -#define P5G_RELSED_C #20# -#define P5G_FINGER_INDEX #32# -#define P5G_FINGER_MIDDLE #33# -#define P5G_FINGER_RING #34# -#define P5G_FINGER_PINKY #35# -#define P5G_FINGER_THUMB #36# -#define P5G_DELTA_X #37# -#define P5G_DELTA_Y #38# -#define P5G_DELTA_Z #39# -#define P5G_DELTA_XR #40# -#define P5G_DELTA_YR #41# -#define P5G_DELTA_ZR #42# -#define P5G_ANGLES #43# diff --git a/6.x/src/py/examples/cabrera/python0.csd b/6.x/src/py/examples/cabrera/python0.csd deleted file mode 100644 index f30512c2..00000000 --- a/6.x/src/py/examples/cabrera/python0.csd +++ /dev/null @@ -1,23 +0,0 @@ - - - -odac - - - -sr=44100 -ksmps=128 -nchnls=2 - -pyinit ;Start python interpreter - -pyruni "print(44100)" - -instr 1 -endin - - - - -i 1 0 0.1 - - \ No newline at end of file diff --git a/6.x/src/py/examples/cabrera/python1.csd b/6.x/src/py/examples/cabrera/python1.csd deleted file mode 100644 index d083c380..00000000 --- a/6.x/src/py/examples/cabrera/python1.csd +++ /dev/null @@ -1,37 +0,0 @@ - - - -odac - - - -sr=44100 -ksmps=128 -nchnls=2 - -pyinit ;Start python interpreter - -pyruni {{ -a = 2 -b = 3 -print("a + b = " + str(a+b)) -}} ;Execute a python script on the header - -instr 1 -pyruni {{a = 6 -b = 5 -print("a + b = " + str(a+b))}} -endin - - -instr 2 -pyruni {{print("a + b = " + str(a+b))}} -endin - - - - - -i 1 0 0.1 -i 2 1 0.1 - - \ No newline at end of file diff --git a/6.x/src/py/examples/cabrera/python2.csd b/6.x/src/py/examples/cabrera/python2.csd deleted file mode 100644 index 60c2398d..00000000 --- a/6.x/src/py/examples/cabrera/python2.csd +++ /dev/null @@ -1,34 +0,0 @@ - - - -odac - - - -sr=44100 -kr=100 -nchnls=2 - -pyinit - -pyruni "a = 0" - -instr 1 - pyrun "a = a + 1" -endin - - -instr 2 - pyruni {{print("a = " + str(a))}} -endin - - - - - -i 1 0 1 ;Adds to a for 1 second -i 2 1 0.1 ;Prints a -i 1 2 1 ;Adds to a for another second -i 2 3 0.1 ;Prints a - - - \ No newline at end of file diff --git a/6.x/src/py/examples/cabrera/python3.csd b/6.x/src/py/examples/cabrera/python3.csd deleted file mode 100644 index 6c393d39..00000000 --- a/6.x/src/py/examples/cabrera/python3.csd +++ /dev/null @@ -1,35 +0,0 @@ - - - -odac - - - -sr=44100 -kr=100 -nchnls=2 - -pyinit - -pyruni "a = 0.0" - -instr 1 - pyrun "a = a + 1" -endin - - -instr 2 -ival pyevali "a" -prints "a = %i\\n", ival -endin - - - - - -i 1 0 1 ;Adds to a for 1 second -i 2 1 0.1 ;Prints a -i 1 2 1 ;Adds to a for another second -i 2 3 0.1 ;Prints a - - - \ No newline at end of file diff --git a/6.x/src/py/examples/cabrera/python4.csd b/6.x/src/py/examples/cabrera/python4.csd deleted file mode 100644 index 252a6250..00000000 --- a/6.x/src/py/examples/cabrera/python4.csd +++ /dev/null @@ -1,35 +0,0 @@ - - - -odac - - - -sr=44100 -kr=100 -nchnls=2 - -pyinit - -pyassigni "a", 0 - -instr 1 - pyrun "a = a + 1" -endin - - -instr 2 -ival pyevali "a" -prints "a = %i\\n", ival -endin - - - - - -i 1 0 1 ;Adds to a for 1 second -i 2 1 0.1 ;Prints a -i 1 2 1 ;Adds to a for another second -i 2 3 0.1 ;Prints a - - - \ No newline at end of file diff --git a/6.x/src/py/examples/cabrera/python5.csd b/6.x/src/py/examples/cabrera/python5.csd deleted file mode 100644 index 3f651170..00000000 --- a/6.x/src/py/examples/cabrera/python5.csd +++ /dev/null @@ -1,32 +0,0 @@ - - - -odac - - - -sr=44100 -kr=100 -nchnls=2 - -pyinit - -pyruni {{ -def average(a,b): - ave = (a + b)/2 - return ave -}} ;Define function "average" - -instr 1 -iave pycall1i "average", p4, p5 -prints "a = %i\\n", iave -endin - - - - - -i 1 0 1 100 200 -i 1 1 1 1000 2000 - - - \ No newline at end of file diff --git a/6.x/src/py/examples/cabrera/pythonlocal.csd b/6.x/src/py/examples/cabrera/pythonlocal.csd deleted file mode 100644 index 915b25e2..00000000 --- a/6.x/src/py/examples/cabrera/pythonlocal.csd +++ /dev/null @@ -1,32 +0,0 @@ - - - -odac - - - -gkprint init 0 - -instr 1 -ivalue init p4 -ktrig changed gkprint -if (ktrig == 1) then - printk 0.5, ivalue -endif -endin - -instr 2 -gkprint init 1 -endin - - - - -; p4 -i 1 0 5 100 -i 1 1 5 200 -i 1 2 5 300 -i 1 3 5 400 - -i 2 3 1 - - diff --git a/6.x/src/py/examples/cabrera/pythonlocal2.csd b/6.x/src/py/examples/cabrera/pythonlocal2.csd deleted file mode 100644 index b31793fa..00000000 --- a/6.x/src/py/examples/cabrera/pythonlocal2.csd +++ /dev/null @@ -1,39 +0,0 @@ - - - -odac - - - -pyinit - -gkprint init 0 - -instr 1 -;assign 4th p-field to local python variable "value" -pylassigni "value", p4 -ktrig changed gkprint - -; If gkprint has changed (i.e. instr 2 was triggered) -; print the value of the local python variable "value" -if (ktrig == 1) then - kvalue pyleval "value" - printk 0.5, kvalue -endif -endin - -instr 2 -gkprint init 1 -endin - - - - -; p4 -i 1 0 5 100 -i 1 1 5 200 -i 1 2 5 300 -i 1 3 5 400 - -i 2 3 1 - - diff --git a/6.x/src/py/examples/ievent.orc b/6.x/src/py/examples/ievent.orc deleted file mode 100644 index 7afac326..00000000 --- a/6.x/src/py/examples/ievent.orc +++ /dev/null @@ -1,33 +0,0 @@ -sr=44100 -kr=4410 -ksmps=10 -nchnls=2 - -giSinusoid ftgen 0, 0, 8192, 10, 1 - -pyinit - -pyruni {{ -import ctcsound -import sys -from random import random -p = _CSOUND_ -print("\\n --> Python version number: {}\\n".format(sys.version_info[0])) -cs = ctcsound.Csound(pointer_=p) -for i in range(800): - cs.scoreEvent('i', [1, i * .2, 0.05, 6.8 + random() * 3, 70.0]) - cs.scoreEvent('i', [1, i * .2, 0.05, 8.8 + random() * 3, 70.0]) -}} - -instr 1 - - iDuration = p3 - iFrequency = cpsoct(p4) - iAmplitude = ampdb(p5) - - aAmplitude linseg iAmplitude, iDuration - 0.01, iAmplitude, 0.002 ,0, 1,0 - aOutput oscili aAmplitude, iFrequency, giSinusoid - - outs aOutput, aOutput - -endin diff --git a/6.x/src/py/examples/pyexect1.py b/6.x/src/py/examples/pyexect1.py deleted file mode 100644 index 4473b85a..00000000 --- a/6.x/src/py/examples/pyexect1.py +++ /dev/null @@ -1 +0,0 @@ -print 'zum' diff --git a/6.x/src/py/examples/pyexect2.py b/6.x/src/py/examples/pyexect2.py deleted file mode 100644 index 5f3789fb..00000000 --- a/6.x/src/py/examples/pyexect2.py +++ /dev/null @@ -1 +0,0 @@ -print 'pa' diff --git a/6.x/src/py/examples/pyexect3.py b/6.x/src/py/examples/pyexect3.py deleted file mode 100644 index 01dd4b78..00000000 --- a/6.x/src/py/examples/pyexect3.py +++ /dev/null @@ -1 +0,0 @@ -print 'zi' diff --git a/6.x/src/py/examples/pyexect4.py b/6.x/src/py/examples/pyexect4.py deleted file mode 100644 index edc17a14..00000000 --- a/6.x/src/py/examples/pyexect4.py +++ /dev/null @@ -1 +0,0 @@ -print 'zut' diff --git a/6.x/src/py/src/pycall-gen.py b/6.x/src/py/src/pycall-gen.py deleted file mode 100644 index 2246c5d6..00000000 --- a/6.x/src/py/src/pycall-gen.py +++ /dev/null @@ -1,249 +0,0 @@ -# -*- coding: utf-8 -*- - -''' -Copyright (C) 2002 Maurizio Umberto Puxeddu, Michael Gogins - -This file is part of Csound. - -The Csound Library is free software; you can redistribute it -and/or modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -Csound is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with Csound; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -02110-1301 USA - -Modification from BSD sources for strNcpy -Copyright (c) 1998 Todd C. Miller - -Modified for speed -- JPff - -Adapted for Python3 -- François Pinot, 2019 - -Automatically generate opcode structures, methods [and table entries]. Just run it. -''' - -def generate_pycall_common_init_code(f, n, pre, post, rate, triggered=0): - if triggered: - t, T = 't', 'T' - else: - t, T = '', '' - name = 'py%scall%d%s%s_%srate' % (pre, n, post, t, rate) - s = 'static int %s(CSOUND *csound, PYCALL%d%s *p)\n' % (name, n, T) - s += '{\n' - s += ' char command[1024];\n' - s += ' PyObject *result;\n' - s += ' int *py_initialize_done;\n' - s += ' if (UNLIKELY((py_initialize_done =\n' - s += ' csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL ||\n' - s += ' *py_initialize_done == 0)) {\n' - s += ' return NOTOK;\n' - s += ' }\n' - if triggered: - s += ' if (!*p->trigger) {\n' - if n == 0: - pass - if n == 1: - s += ' *p->result = p->oresult;\n' - elif n > 1: - for i in range(n): - s += ' *p->result%d = p->oresult%d;\n' % (i+1, i+1) - s += ' return OK;\n' - s += ' }\n' - print(s, file=f) - -def generate_pycall_common_call_code(f, context, withinit, triggered): - if triggered: - skip = 2 - else: - skip = 1 - s = ' format_call_statement(command, (char*) p->function->data,\n' - s += ' p->INOCOUNT, p->args, %d);\n\n' % skip - if context == 'private': - s += ' result = eval_string_in_given_context(command, 0);\n' - else: - if withinit: - s += ' create_private_namespace_if_needed(&p->h);\n' - s += ' result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead));\n' - print(s, file=f) - -def generate_pycall_exception_handling_code(f, n, pre, post, rate, triggered=0): - s = ' if (UNLIKELY(result == NULL)) {\n' - s += ' return pyErrMsg(p, "python exception");\n' - s += ' }\n' - print(s, file=f) - -def generate_pycall_result_conversion_code(f, n, pre, post, rate, triggered=0): - if triggered: - t, T = 't', 'T' - else: - t, T = '', '' - s = "" - if n == 0: - s += ' if (UNLIKELY(result != Py_None)) {\n' - s += ' return errMsg(p, "callable must return None");\n' - s += ' }\n' - elif n == 1: - s += ' if (UNLIKELY(!PyFloat_Check(result))) {\n' - s += ' return errMsg(p, "callable must return a float");\n' - s += ' }\n' - s += ' else {\n' - s += ' *p->result = PyFloat_AsDouble(result);\n' - if triggered: - s += ' p->oresult = *p->result;\n' - s += ' }\n' - s += ' return OK;\n' - else: - name = 'py%scall%d%s%s_%srate' % (pre, n, post, t, rate) - s += ' if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != %d)) {\n' % n - s += ' return errMsg(p, "callable must return %d values");\n' % n - s += ' }\n' - s += ' else {\n' - for i in range(n): - s += ' *p->result%d = PyFloat_AsDouble(PyTuple_GET_ITEM(result, %d));\n' % (i+1, i) - if triggered: - s += ' p->oresult%d = *p->result%d;\n' % (i+1, i+1) - s += ' }\n' - s += '\n' - s += ' Py_DECREF(result);\n' - s += ' return OK;\n' - s += '}\n' - print(s, file=f) - -def generate_pycall_krate_method(f, n, triggered=0): - generate_pycall_common_init_code(f, n, '', '', 'k', triggered) - generate_pycall_common_call_code(f, 'private', 1, triggered) - generate_pycall_exception_handling_code(f, n, '', '', 'k', triggered) - generate_pycall_result_conversion_code(f, n, '', '', 'k', triggered) - -def generate_pylcall_irate_method(f, n, triggered=0): - if triggered: - t, T = 't', 'T' - else: - t, T = '', '' - name = 'pylcall%d%s_irate' % (n, t) - s = 'static int %s(CSOUND *csound, PYCALL%d%s *p)\n' % (name, n, T) - s += '{\n' - s += ' int *py_initialize_done;\n' - s += ' if (UNLIKELY((py_initialize_done =\n' - s += ' csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL ||\n' - s += ' *py_initialize_done == 0)) {\n' - s += ' return NOTOK;\n' - s += ' }\n' - s += ' create_private_namespace_if_needed(&p->h);\n' - s += ' return OK;\n' - s += '}\n' - print(s, file=f) - -def generate_pylcall_krate_method(f, n, triggered=0): - generate_pycall_common_init_code(f, n, 'l', '', 'k', triggered) - generate_pycall_common_call_code(f, 'global', 0, triggered) - generate_pycall_exception_handling_code(f, n, 'l', '', 'k', triggered) - generate_pycall_result_conversion_code(f, n, 'l', '', 'k', triggered) - -def generate_pylcalli_irate_method(f, n): - generate_pycall_common_init_code(f, n, 'l', 'i', 'i') - generate_pycall_common_call_code(f, 'global', 1, 0) - generate_pycall_exception_handling_code(f, n, 'l', 'i', 'i') - generate_pycall_result_conversion_code(f, n, 'l', 'i', 'i') - -# ---------- - -def generate_pycall_opcode_struct(f, n, triggered=0): - if triggered: - T = 'T' - else: - T = '' - s = 'typedef struct {\n' - s += ' OPDS h;\n' - if n == 1: - s += ' MYFLT *result;\n' - else: - for i in range(n): - s += ' MYFLT *result%d;\n' % (i+1) - if triggered: - s += ' MYFLT *trigger;\n' - s += ' STRINGDAT *function;\n' - s += ' MYFLT *args[VARGMAX-3];\n' - if triggered: - if n == 1: - s += ' MYFLT oresult\n;' - else: - for i in range(n): - s += ' MYFLT oresult%d;\n' % (i+1) - s += '} PYCALL%d%s;\n' % (n, T) - print(s, file=f) - -# -------- - -f = open('pycall.auto.c', 'w') -s = '''/* - * pyx.auto.c - * - * Copyright (C) 2002 Maurizio Umberto Puxeddu, Michael Gogins - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Don't modify this file, it's automatically genetated by pyx-gen.py - */ -''' -print(s, file=f) -for n in range(9): - generate_pycall_krate_method(f, n) - generate_pylcall_irate_method(f, n) - generate_pylcall_krate_method(f, n) - generate_pylcalli_irate_method(f, n) - - generate_pycall_krate_method(f, n, 1) - generate_pylcall_irate_method(f, n, 1) - generate_pylcall_krate_method(f, n, 1) -f.close() - -f = open('pycall.auto.h', 'w') -s = '''/* - * pyx.auto.h - * - * Copyright (C) 2002 Maurizio Umberto Puxeddu, Michael Gogins - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Don't modify this file. It's automatically generated by pyx-gen.py - */ -''' -print(s, file=f) -for n in range(9): - generate_pycall_opcode_struct(f, n) - generate_pycall_opcode_struct(f, n, 1) -f.close() - diff --git a/6.x/src/py/src/pycall.auto.h b/6.x/src/py/src/pycall.auto.h deleted file mode 100644 index 81935c6b..00000000 --- a/6.x/src/py/src/pycall.auto.h +++ /dev/null @@ -1,247 +0,0 @@ -/* - * pyx.auto.h - * - * Copyright (C) 2002 Maurizio Umberto Puxeddu, Michael Gogins - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Don't modify this file. It's automatically generated by pyx-gen.py - */ - -typedef struct { - OPDS h; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; -} PYCALL0; - -typedef struct { - OPDS h; - MYFLT *trigger; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; -} PYCALL0T; - -typedef struct { - OPDS h; - MYFLT *result; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; -} PYCALL1; - -typedef struct { - OPDS h; - MYFLT *result; - MYFLT *trigger; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; - MYFLT oresult -;} PYCALL1T; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; -} PYCALL2; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *trigger; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; - MYFLT oresult1; - MYFLT oresult2; -} PYCALL2T; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; -} PYCALL3; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *trigger; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; - MYFLT oresult1; - MYFLT oresult2; - MYFLT oresult3; -} PYCALL3T; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *result4; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; -} PYCALL4; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *result4; - MYFLT *trigger; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; - MYFLT oresult1; - MYFLT oresult2; - MYFLT oresult3; - MYFLT oresult4; -} PYCALL4T; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *result4; - MYFLT *result5; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; -} PYCALL5; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *result4; - MYFLT *result5; - MYFLT *trigger; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; - MYFLT oresult1; - MYFLT oresult2; - MYFLT oresult3; - MYFLT oresult4; - MYFLT oresult5; -} PYCALL5T; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *result4; - MYFLT *result5; - MYFLT *result6; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; -} PYCALL6; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *result4; - MYFLT *result5; - MYFLT *result6; - MYFLT *trigger; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; - MYFLT oresult1; - MYFLT oresult2; - MYFLT oresult3; - MYFLT oresult4; - MYFLT oresult5; - MYFLT oresult6; -} PYCALL6T; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *result4; - MYFLT *result5; - MYFLT *result6; - MYFLT *result7; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; -} PYCALL7; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *result4; - MYFLT *result5; - MYFLT *result6; - MYFLT *result7; - MYFLT *trigger; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; - MYFLT oresult1; - MYFLT oresult2; - MYFLT oresult3; - MYFLT oresult4; - MYFLT oresult5; - MYFLT oresult6; - MYFLT oresult7; -} PYCALL7T; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *result4; - MYFLT *result5; - MYFLT *result6; - MYFLT *result7; - MYFLT *result8; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; -} PYCALL8; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *result4; - MYFLT *result5; - MYFLT *result6; - MYFLT *result7; - MYFLT *result8; - MYFLT *trigger; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; - MYFLT oresult1; - MYFLT oresult2; - MYFLT oresult3; - MYFLT oresult4; - MYFLT oresult5; - MYFLT oresult6; - MYFLT oresult7; - MYFLT oresult8; -} PYCALL8T; - diff --git a/6.x/src/py/src/pythonhelper.h b/6.x/src/py/src/pythonhelper.h deleted file mode 100644 index 79de5dea..00000000 --- a/6.x/src/py/src/pythonhelper.h +++ /dev/null @@ -1,39 +0,0 @@ -/* - * pythonhelper.h - * - * Copyright (C) 2002 Maurizio Umberto Puxeddu - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef _pycsound_pythonhelper_h_ -#define _pycsound_pythonhelper_h_ - -#ifdef _DEBUG -# undef _DEBUG -# include -# define _DEBUG -#else -# include -#endif - -/* - I do it because I can't #include in "csoundCore.h". - */ - -#define GETPYLOCAL(ids) ((PyObject *)ids->pylocal) -#define SETPYLOCAL(ids, p) { ids->pylocal = (void *)p; } - -#endif diff --git a/6.x/src/py/src/pythonopcodes.c b/6.x/src/py/src/pythonopcodes.c deleted file mode 100644 index b4796ec0..00000000 --- a/6.x/src/py/src/pythonopcodes.c +++ /dev/null @@ -1,435 +0,0 @@ -/* - * pythonopcodes.c - * - * Copyright (C) 2002 Maurizio Umberto Puxeddu - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -#ifdef _DEBUG -# undef _DEBUG -# include -# define _DEBUG -#else -# include -#endif -#include -#include "csdl.h" -#include "pythonopcodes.h" -#include "pythonhelper.h" - -/* HELPERS */ - -static CS_NOINLINE void create_private_namespace_if_needed(OPDS *o) -{ - if (GETPYLOCAL(o->insdshead) == 0) { - SETPYLOCAL(o->insdshead, PyDict_New()); -#ifdef DEBUG_PY_NAMESPACES - printf("Creating private namespace %p for %p\n", - (void*) GETPYLOCAL(o->insdshead), (void*) o->insdshead); -#endif - } -#ifdef DEBUG_PY_NAMESPACES - else { - printf("Private namespace for %p already allocated at %p\n", - (void*) o->insdshead, (void*) GETPYLOCAL(o->insdshead)); - } -#endif -} - -static void format_call_statement2(char *statement, char *callable, - int argc, MYFLT *argv[], long skip) -{ - int i; - - statement[0] = '\0'; - if (argc-skip > 0) { - snprintf(statement, 1024, "%s(%0.6f", callable, *(argv[skip])); - for (i = skip+1; i < argc; ++i) { - snprintf(statement + strlen(statement), 1024 - strlen(statement), - ", %f", *(argv[i])); - } - // MKG 2014 Jan 29: No linkage for strlcat in py.dll on MinGW. - //strlcat(statement, ")", 1024); - strncat(statement, ")", 1023 - strlen(statement)); statement[1023] = '\0'; - } - else { - snprintf(statement, 1024, "%s()", callable); - } - -} - -static void format_call_statement(char *statement, char *callable, - int argc, MYFLT *argv[], int skip) -{ - int i; - - statement[0] = '\0'; - if (argc > 0) { - snprintf(statement, 1024, "%s(%0.6f", callable, *(argv[0])); - for (i = 1; i < argc - skip; ++i) { - snprintf(statement + strlen(statement), 1024-strlen(statement), - ", %f", *(argv[i])); - } - // MKG 2014 Jan 29: No linkage for strlcat in py.dll on MinGW. - //strlcat(statement, ")", 1024); - strncat(statement, ")", 1023-strlen(statement)); statement[1023] = '\0'; - } - else { - snprintf(statement, 1024, "%s()", callable); - } -} - - -static PyObject * -run_statement_in_given_context(char *string, PyObject *private) -{ - PyObject *module, *public; - - module = PyImport_AddModule("__main__"); - if (module == NULL) { - PyErr_SetString(PyExc_RuntimeError, "couldn't find module __main__"); - return NULL; - } - public = PyModule_GetDict(module); - return PyRun_String(string, Py_file_input, - public, private ? private : public); -} - -static PyObject * -eval_string_in_given_context(char *string, PyObject *private) -{ - PyObject *module, *public; - - module = PyImport_AddModule("__main__"); - if (module == NULL) { - PyErr_SetString(PyExc_RuntimeError, "couldn't find module __main__"); - return NULL; - } - public = PyModule_GetDict(module); - return PyRun_String(string, Py_eval_input, - public, private ? private : public); -} - -static PyObject * -exec_file_in_given_context(CSOUND* cs, char *filename, PyObject *private) -{ - PyObject *result, *module, *public; - char *fullpath; - - fullpath = cs->FindInputFile(cs, filename, NULL); - - module = PyImport_AddModule("__main__"); - if (module == NULL) { - PyErr_SetString(PyExc_RuntimeError, "couldn't find module __main__"); - return NULL; - } - public = PyModule_GetDict(module); - FILE *fd = fopen(fullpath, "r"); - if (!fd) { - PyErr_Format(PyExc_RuntimeError, - "couldn't open script file %s", filename); - return NULL; - } - result = PyRun_File(fd, fullpath, Py_file_input, - public, private ? private : public); - fclose(fd); - return result; -} - -/* ------ OPCODES ------ */ - -static CS_NOINLINE int errMsg(void *p, const char *msg) -{ - CSOUND *csound = ((OPDS*) p)->insdshead->csound; - const char *opname = csound->GetOpcodeName(p); - csound->ErrorMsg(csound, "%s: %s", opname, msg); - - return NOTOK; -} - -static CS_NOINLINE int pyErrMsg(void *p, const char *msg) -{ - CSOUND *csound = ((OPDS*) p)->insdshead->csound; - const char *opname = csound->GetOpcodeName(p); - csound->ErrorMsg(csound, "%s: %s", opname, msg); - PyErr_Print(); - - return NOTOK; -} - -static int pyinit(CSOUND *csound, PYINIT *p) -{ - (void) csound; - (void) p; - int *py_initialize_done; - - if((py_initialize_done = csound->QueryGlobalVariable(csound, - "PY_INITIALIZE")) == NULL){ - csound->CreateGlobalVariable(csound, "PY_INITIALIZE", sizeof(int)); - py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE"); - *py_initialize_done = 0; - } - - if (*py_initialize_done == 0) { - Py_Initialize(); - *py_initialize_done = 1; - } - - PyObject *module = PyImport_AddModule("__main__"); - if (module == NULL) { - PyErr_SetString(PyExc_RuntimeError, "couldn't find module __main__"); - return NOTOK; - } - PyObject *public = PyModule_GetDict(module); - PyObject *csobj = Py_BuildValue("l", (uintptr_t) csound); - PyDict_SetItemString(public, "_CSOUND_", csobj); - return OK; -} - -#include "pyx.auto.c" -#include "pycall.auto.c" - -static int pycalln_krate(CSOUND *csound, PYCALLN *p) -{ - IGN(csound); - int i; - char command[1024]; - PyObject *result; - - - format_call_statement2(command, p->function->data, - p->INOCOUNT-2, p->args, *p->nresult); - result = eval_string_in_given_context(command, 0); - if (result != NULL && PyTuple_Check(result) && - PyTuple_Size(result) == (int) *p->nresult) { - for (i = 0; i < *p->nresult; ++i) - *p->args[i] = PyFloat_AsDouble(PyTuple_GET_ITEM(result, i)); - Py_DECREF(result); - } - else { - return pyErrMsg(p, "ERROR"); - } - return OK; -} - -static int pylcalln_irate(CSOUND *csound, PYCALLN *p) -{ - IGN(csound); - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylcalln_krate(CSOUND *csound, PYCALLN *p) -{ - IGN(csound); - int i; - char command[1024]; - PyObject *result; - - format_call_statement2(command, p->function->data, - p->INOCOUNT-2, p->args, *p->nresult); - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - if (result != NULL && PyTuple_Check(result) && - PyTuple_Size(result) == (int) *p->nresult) { - for (i = 0; i < *p->nresult; ++i) - *p->args[i] = PyFloat_AsDouble(PyTuple_GET_ITEM(result, i)); - Py_DECREF(result); - } - else { - return pyErrMsg(p, "ERROR"); - } - return OK; -} - -static int pylcallni_irate(CSOUND *csound, PYCALLN *p) -{ - IGN(csound); - int i; - char command[1024]; - PyObject *result; - - create_private_namespace_if_needed(&p->h); - format_call_statement2(command, p->function->data, - p->INOCOUNT-2, p->args, *p->nresult); - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - if (result != NULL && PyTuple_Check(result) && - PyTuple_Size(result) == (int) *p->nresult) { - for (i = 0; i < *p->nresult; ++i) - *p->args[i] = PyFloat_AsDouble(PyTuple_GET_ITEM(result, i)); - Py_DECREF(result); - } - else { - return pyErrMsg(p, "ERROR"); - } - return OK; -} - - /* PYTHON OPCODES */ - -OENTRY python_localops[] = { - - /* INITIALIZATION */ - -{ "pyinit", sizeof(PYINIT), 0, 1, "", "", (SUBR)pyinit, NULL }, - - /* RUN GROUP */ -{ "pyrun", sizeof(PYRUN), 0, 2, "", "S", NULL, (SUBR)pyrun_krate }, -{ "pyruni", sizeof(PYRUN), 0, 1, "", "S", (SUBR)pyruni_irate }, -{ "pylrun", sizeof(PYRUN), 0, 3, "", "S", - (SUBR)pylrun_irate, (SUBR)pylrun_krate }, -{ "pylruni", sizeof(PYRUN), 0, 1, "", "S", (SUBR)pylruni_irate }, - -{ "pyrunt", sizeof(PYRUNT), 0, 2, "", "kS", NULL, (SUBR)pyrunt_krate }, -{ "pylrunt", sizeof(PYRUNT), 0, 3, "", "kS", - (SUBR)pylrunt_irate, (SUBR)pylrunt_krate }, - - /* EXEC GROUP */ - -{ "pyexec", sizeof(PYEXEC), 0, 2, "", "S", NULL, (SUBR)pyexec_krate }, -{ "pyexeci", sizeof(PYEXEC), 0, 1, "", "S", (SUBR)pyexec_krate }, -{ "pylexec", sizeof(PYEXEC), 0, 3, "", "S", - (SUBR)pylexec_irate, (SUBR)pylexec_krate }, -{ "pylexeci", sizeof(PYEXEC), 0, 1, "", "S", (SUBR)pylexeci_irate }, - -{ "pyexect", sizeof(PYEXECT), 0, 2, "", "kS", NULL, (SUBR)pyexect_krate }, -{ "pylexect", sizeof(PYEXECT), 0, 3, "", "kS", - (SUBR)pylexect_irate, (SUBR)pylexect_krate }, - - /* CALL GROUP */ - -{ "pycall", sizeof(PYCALL0), 0, 2, "" , "Sz", NULL, (SUBR)pycall0_krate }, -{ "pycall1", sizeof(PYCALL1), 0, 2, "k", "Sz", NULL, (SUBR)pycall1_krate }, -{ "pycall2", sizeof(PYCALL2), 0, 2, "kk", "Sz", NULL, (SUBR)pycall2_krate }, -{ "pycall3", sizeof(PYCALL3), 0, 2, "kkk", "Sz", NULL, (SUBR)pycall3_krate }, -{ "pycall4", sizeof(PYCALL4), 0, 2, "kkkk", "Sz", NULL, (SUBR)pycall4_krate }, -{ "pycall5", sizeof(PYCALL5), 0, 2, "kkkkk", "Sz", NULL, (SUBR)pycall5_krate }, -{ "pycall6", sizeof(PYCALL6), 0, 2, "kkkkkk", "Sz", NULL, (SUBR)pycall6_krate }, -{ "pycall7", sizeof(PYCALL7), 0, 2, "kkkkkkk", "Sz", NULL, (SUBR)pycall7_krate }, -{ "pycall8", sizeof(PYCALL8), 0, 2, "kkkkkkkk", "Sz", NULL,(SUBR)pycall8_krate }, - -{ "pycalln", sizeof(PYCALLN), 0, 2, "", "Siz", NULL, (SUBR)pycalln_krate }, - -{ "pycallt", sizeof(PYCALL0T), 0, 2, "" , "kSz", NULL, (SUBR)pycall0t_krate }, -{ "pycall1t", sizeof(PYCALL1T), 0, 2, "k", "kSz", NULL, (SUBR)pycall1t_krate }, -{ "pycall2t", sizeof(PYCALL2T), 0, 2, "kk", "kSz", NULL, (SUBR)pycall2t_krate }, -{ "pycall3t", sizeof(PYCALL3T),0, 2, "kkk", "kSz", NULL, (SUBR)pycall3t_krate }, -{ "pycall4t", sizeof(PYCALL4T), 0, 2, "kkkk", "kSz", NULL, (SUBR)pycall4t_krate }, -{ "pycall5t", sizeof(PYCALL5T), 0, 2, "kkkkk", "kSz", NULL, (SUBR)pycall5t_krate }, -{ "pycall6t", sizeof(PYCALL6T), 0, 2, "kkkkkk", "kSz", NULL,(SUBR)pycall6t_krate }, -{ "pycall7t", sizeof(PYCALL7T), 0, 2, "kkkkkkk","kSz", NULL,(SUBR)pycall7t_krate }, -{ "pycall8t", sizeof(PYCALL8T), 0, 2, "kkkkkkkk","kSz",NULL,(SUBR)pycall8t_krate }, - -#if 0 -{ "pycallnt", sizeof(PYCALLNT), 0, 2, "", "Siz", NULL, (SUBR)pycallnt_krate }, -#endif - -{ "pycalli", sizeof(PYCALL0), 0, 1, "", "Sm", (SUBR)pycall0_krate }, -{ "pycall1i", sizeof(PYCALL1), 0, 1, "i", "Sm", (SUBR)pycall1_krate }, -{ "pycall2i", sizeof(PYCALL2), 0, 1, "ii", "Sm", (SUBR)pycall2_krate }, -{ "pycall3i", sizeof(PYCALL3), 0, 1, "iii", "Sm", (SUBR)pycall3_krate }, -{ "pycall4i", sizeof(PYCALL4), 0, 1, "iiii", "Sm", (SUBR)pycall4_krate }, -{ "pycall5i", sizeof(PYCALL5), 0, 1, "iiiii", "Sm", (SUBR)pycall5_krate }, -{ "pycall6i", sizeof(PYCALL6), 0, 1, "iiiiii", "Sm", (SUBR)pycall6_krate }, -{ "pycall7i", sizeof(PYCALL7), 0, 1, "iiiiiii", "Sm", (SUBR)pycall7_krate }, -{ "pycall8i", sizeof(PYCALL8), 0, 1, "iiiiiiii", "Sm", (SUBR)pycall8_krate }, - -{ "pycallni", sizeof(PYCALLN), 0, 1, "", "Sim", (SUBR)pycalln_krate }, - -{ "pylcall", sizeof(PYCALL0), 0, 3, "" , "Sz", - (SUBR)pylcall0_irate, (SUBR)pylcall0_krate }, -{ "pylcall1", sizeof(PYCALL1), 0, 3, "k", "Sz", - (SUBR)pylcall1_irate, (SUBR)pylcall1_krate }, -{ "pylcall2", sizeof(PYCALL2), 0, 3, "kk", "Sz", - (SUBR)pylcall2_irate, (SUBR)pylcall2_krate }, -{ "pylcall3", sizeof(PYCALL3), 0, 3, "kkk", "Sz", - (SUBR)pylcall3_irate, (SUBR)pylcall3_krate }, -{ "pylcall4", sizeof(PYCALL4), 0, 3, "kkkk", "Sz", - (SUBR)pylcall4_irate, (SUBR)pylcall4_krate }, -{ "pylcall5", sizeof(PYCALL5), 0, 3, "kkkkk", "Sz", - (SUBR)pylcall5_irate, (SUBR)pylcall5_krate }, -{ "pylcall6", sizeof(PYCALL6), 0, 3, "kkkkkk", "Sz", - (SUBR)pylcall6_irate, (SUBR)pylcall6_krate }, -{ "pylcall7", sizeof(PYCALL7), 0, 3, "kkkkkkk", "Sz", - (SUBR)pylcall7_irate, (SUBR)pylcall7_krate }, -{ "pylcall8", sizeof(PYCALL8), 0, 3, "kkkkkkkk", "Sz", - (SUBR)pylcall8_irate, (SUBR)pylcall8_krate }, - -{ "pylcalln", sizeof(PYCALLN), 0, 3, "", "Siz", - (SUBR)pylcalln_irate, (SUBR)pylcalln_krate }, - -{ "pylcallt", sizeof(PYCALL0T), 0, 3, "" , "kSz", - (SUBR)pylcall0t_irate, (SUBR)pylcall0t_krate }, -{ "pylcall1t", sizeof(PYCALL1T), 0, 3, "k", "kSz", - (SUBR)pylcall1t_irate, (SUBR)pylcall1t_krate }, -{ "pylcall2t", sizeof(PYCALL2T), 0, 3, "kk", "kSz", - (SUBR)pylcall2t_irate, (SUBR)pylcall2t_krate }, -{ "pylcall3t", sizeof(PYCALL3T), 0, 3, "kkk", "kSz", - (SUBR)pylcall3t_irate, (SUBR)pylcall3t_krate }, -{ "pylcall4t", sizeof(PYCALL4T), 0, 3, "kkkk", "kSz", - (SUBR)pylcall4t_irate, (SUBR)pylcall4t_krate }, -{ "pylcall5t", sizeof(PYCALL5T), 0, 3, "kkkkk", "kSz", - (SUBR)pylcall5t_irate, (SUBR)pylcall5t_krate }, -{ "pylcall6t", sizeof(PYCALL6T), 0, 3, "kkkkkk", "kSz", - (SUBR)pylcall6t_irate, (SUBR)pylcall6t_krate }, -{ "pylcall7t", sizeof(PYCALL7T), 0, 3, "kkkkkkk", "kSz", - (SUBR)pylcall7t_irate, (SUBR)pylcall7t_krate }, -{ "pylcall8t", sizeof(PYCALL8T), 0, 3, "kkkkkkkk", "kSz", - (SUBR)pylcall8t_irate, (SUBR)pylcall8t_krate }, - -#if 0 -{ "pylcallnt", sizeof(PYCALLNT), 0, 3, "", "Siz", - (SUBR)pylcalln_irate, (SUBR)pylcallnt_krate }, -#endif - -{ "pylcalli", sizeof(PYCALL0), 0, 1, "", "Sm", (SUBR)pylcall0i_irate }, -{ "pylcall1i", sizeof(PYCALL1), 0, 1, "i", "Sm", (SUBR)pylcall1i_irate }, -{ "pylcall2i", sizeof(PYCALL2), 0, 1, "ii", "Sm", (SUBR)pylcall2i_irate }, -{ "pylcall3i", sizeof(PYCALL3), 0, 1, "iii", "Sm", (SUBR)pylcall3i_irate }, -{ "pylcall4i", sizeof(PYCALL4), 0, 1, "iiii", "Sm", (SUBR)pylcall4i_irate }, -{ "pylcall5i", sizeof(PYCALL5), 0, 1, "iiiii", "Sm", (SUBR)pylcall5i_irate }, -{ "pylcall6i", sizeof(PYCALL6), 0, 1, "iiiiii", "Sm", (SUBR)pylcall6i_irate }, -{ "pylcall7i", sizeof(PYCALL7), 0, 1, "iiiiiii", "Sm", (SUBR)pylcall7i_irate }, -{ "pylcall8i", sizeof(PYCALL8), 0, 1, "iiiiiiii", "Sm", (SUBR)pylcall8i_irate }, - -{ "pylcallni", sizeof(PYCALLN), 0, 1, "", "Sim", (SUBR)pylcallni_irate }, - - /* EVAL GROUP */ - -{ "pyeval", sizeof(PYEVAL), 0, 2, "k", "S", NULL, (SUBR)pyeval_krate }, -{ "pyevali", sizeof(PYEVAL), 0, 1, "i", "S", (SUBR)pyeval_krate }, -{ "pyleval", sizeof(PYEVAL), 0, 3, "k", "S", - (SUBR)pyleval_irate, (SUBR)pyleval_krate }, -{ "pylevali", sizeof(PYEVAL), 0, 1, "i", "S", (SUBR)pylevali_irate }, - -{ "pyevalt", sizeof(PYEVALT), 0, 2, "k", "S", NULL, (SUBR)pyevalt_krate }, -{ "pylevalt", sizeof(PYEVALT), 0, 3, "k", "S", - (SUBR)pylevalt_irate, (SUBR)pylevalt_krate }, - - /* ASSIGN GROUP */ - -{ "pyassign", sizeof(PYASSIGN), 0, 2, "", "Sz", NULL, (SUBR)pyassign_krate }, -{ "pyassigni", sizeof(PYASSIGN), 0, 1, "", "Sz", (SUBR)pyassign_krate }, -{ "pylassign", sizeof(PYASSIGN), 0, 3, "", "Sz", - (SUBR)pylassign_irate, (SUBR)pylassign_krate }, -{ "pylassigni", sizeof(PYASSIGN), 0, 1, "", "Sz", (SUBR)pylassigni_irate }, - -{ "pyassignt", sizeof(PYASSIGNT), 0, 2, "", "Sz", NULL, (SUBR)pyassignt_krate }, -{ "pylassignt", sizeof(PYASSIGNT), 0, 3, "", "Sz", - (SUBR)pylassignt_irate, (SUBR)pylassignt_krate }, - -}; - -LINKAGE_BUILTIN(python_localops) diff --git a/6.x/src/stk/Android/jni/Android.mk b/6.x/src/stk/Android/jni/Android.mk deleted file mode 100644 index 71dfc3bb..00000000 --- a/6.x/src/stk/Android/jni/Android.mk +++ /dev/null @@ -1,106 +0,0 @@ -LOCAL_PATH := $(call my-dir) - -include $(CLEAR_VARS) - -CSOUND_SRC_ROOT := ../../../.. - -LOCAL_MODULE := libstk - -LOCAL_C_INCLUDES := $(LOCAL_PATH)/../../stk/include $(LOCAL_PATH)/../../stk/src $(LOCAL_PATH)/../../../../Engine $(LOCAL_PATH)/../../../../H $(LOCAL_PATH)/../../../../include $(LOCAL_PATH)/../../../.. $(LOCAL_PATH)/../../../ $(NDK_MODULE_PATH)/libsndfile-android/jni $(LOCAL_PATH)/../../../CsoundAndroid/jni -LOCAL_CFLAGS := -O3 -D__BUILDING_LIBCSOUND -DENABLE_NEW_PARSER -DLINUX -DHAVE_DIRENT_H -DHAVE_FCNTL_H -DHAVE_UNISTD_H -DHAVE_STDINT_H -DHAVE_SYS_TIME_H -DHAVE_SYS_TYPES_H -DHAVE_TERMIOS_H -# Might need this: -DDEFAULT_RAWWAVE_PATH=${DEFAULT_STK_RAWWAVE_PATH} -LOCAL_CPPFLAGS :=$(LOCAL_CFLAGS) -LOCAL_CPPFLAGS += -std=c++11 -pthread -frtti -fexceptions -LOCAL_LDFLAGS += -Wl,--export-dynamic -### - -# OMIT: -# src/InetWvIn.cpp src/InetWvOut.cpp -# src/Mutex.cpp src/RtAudio.cpp -# src/RtMidi.cpp -# src/RtWvIn.cpp src/RtWvOut.cpp -# src/Socket.cpp src/TcpClient.cpp -# src/TcpServer.cpp src/Thread.cpp -# src/UdpSocket.cpp) - -LOCAL_SRC_FILES := $(CSOUND_SRC_ROOT)/Opcodes/stk/stkOpcodes.cpp \ - ../../stk/src/ADSR.cpp \ - ../../stk/src/Asymp.cpp \ - ../../stk/src/BandedWG.cpp \ - ../../stk/src/BeeThree.cpp \ - ../../stk/src/BiQuad.cpp \ - ../../stk/src/Blit.cpp \ - ../../stk/src/BlitSaw.cpp \ - ../../stk/src/BlitSquare.cpp \ - ../../stk/src/BlowBotl.cpp \ - ../../stk/src/BlowHole.cpp \ - ../../stk/src/Bowed.cpp \ - ../../stk/src/Brass.cpp \ - ../../stk/src/Chorus.cpp \ - ../../stk/src/Clarinet.cpp \ - ../../stk/src/DelayA.cpp \ - ../../stk/src/Delay.cpp \ - ../../stk/src/DelayL.cpp \ - ../../stk/src/Drummer.cpp \ - ../../stk/src/Echo.cpp \ - ../../stk/src/Envelope.cpp \ - ../../stk/src/FileLoop.cpp \ - ../../stk/src/FileRead.cpp \ - ../../stk/src/FileWrite.cpp \ - ../../stk/src/FileWvIn.cpp \ - ../../stk/src/FileWvOut.cpp \ - ../../stk/src/Fir.cpp \ - ../../stk/src/Flute.cpp \ - ../../stk/src/FM.cpp \ - ../../stk/src/FMVoices.cpp \ - ../../stk/src/FormSwep.cpp \ - ../../stk/src/FreeVerb.cpp \ - ../../stk/src/Granulate.cpp \ - ../../stk/src/Guitar.cpp \ - ../../stk/src/HevyMetl.cpp \ - ../../stk/src/Iir.cpp \ - ../../stk/src/JCRev.cpp \ - ../../stk/src/LentPitShift.cpp \ - ../../stk/src/Mandolin.cpp \ - ../../stk/src/Mesh2D.cpp \ - ../../stk/src/Messager.cpp \ - ../../stk/src/MidiFileIn.cpp \ - ../../stk/src/ModalBar.cpp \ - ../../stk/src/Modal.cpp \ - ../../stk/src/Modulate.cpp \ - ../../stk/src/Moog.cpp \ - ../../stk/src/Noise.cpp \ - ../../stk/src/NRev.cpp \ - ../../stk/src/OnePole.cpp \ - ../../stk/src/OneZero.cpp \ - ../../stk/src/PercFlut.cpp \ - ../../stk/src/Phonemes.cpp \ - ../../stk/src/PitShift.cpp \ - ../../stk/src/Plucked.cpp \ - ../../stk/src/PoleZero.cpp \ - ../../stk/src/PRCRev.cpp \ - ../../stk/src/Resonate.cpp \ - ../../stk/src/Rhodey.cpp \ - ../../stk/src/Sampler.cpp \ - ../../stk/src/Saxofony.cpp \ - ../../stk/src/Shakers.cpp \ - ../../stk/src/Simple.cpp \ - ../../stk/src/SineWave.cpp \ - ../../stk/src/SingWave.cpp \ - ../../stk/src/Sitar.cpp \ - ../../stk/src/Skini.cpp \ - ../../stk/src/Sphere.cpp \ - ../../stk/src/StifKarp.cpp \ - ../../stk/src/Stk.cpp \ - ../../stk/src/TapDelay.cpp \ - ../../stk/src/TubeBell.cpp \ - ../../stk/src/Twang.cpp \ - ../../stk/src/TwoPole.cpp \ - ../../stk/src/TwoZero.cpp \ - ../../stk/src/Voicer.cpp \ - ../../stk/src/VoicForm.cpp \ - ../../stk/src/Whistle.cpp \ - ../../stk/src/Wurley.cpp - -include $(BUILD_SHARED_LIBRARY) - diff --git a/6.x/src/stk/Android/jni/Application.mk b/6.x/src/stk/Android/jni/Application.mk deleted file mode 100644 index a3c7dd41..00000000 --- a/6.x/src/stk/Android/jni/Application.mk +++ /dev/null @@ -1,9 +0,0 @@ -APP_ABI := armeabi-v7a arm64-v8a -APP_CPPFLAGS += -fexceptions -frtti -APP_OPTIM := release -APP_PLATFORM := android-21 -#APP_STL := gnustl_shared -#NDK_TOOLCHAIN_VERSION := 4.9 -APP_STL := c++_shared -NDK_TOOLCHAIN_VERSION := clang -APP_MODULES := stk diff --git a/6.x/src/stk/CMakeLists.txt b/6.x/src/stk/CMakeLists.txt deleted file mode 100644 index 24d006db..00000000 --- a/6.x/src/stk/CMakeLists.txt +++ /dev/null @@ -1,96 +0,0 @@ -option(BUILD_STK_OPCODES "Build the stk opcodes" ON) - -if(BUILD_STK_OPCODES) - if(DEFAULT_STK_RAWWAVE_PATH) - set(rawwave_path "-DDEFAULT_RAWWAVE_PATH=\"${DEFAULT_STK_RAWWAVE_PATH}\"") - endif() - - if(USE_VCPKG) - make_plugin(stkops stkOpcodes.cpp) - find_package(unofficial-libstk CONFIG REQUIRED) - target_link_libraries(stkops PRIVATE unofficial::libstk::libstk) - target_include_directories(stkops PRIVATE ${CSOUND_INCLUDE_DIRS}) - message(STATUS "Building STK opcodes for MSVC") - else() - find_package(STK) - - if(STK_LIBRARY) - find_path(STK_INCLUDE_DIR Stk.h - /usr/include/stk - /usr/local/include/stk) - - check_deps(BUILD_STK_OPCODES STK_INCLUDE_DIR) - - if(STK_INCLUDE_DIR) - make_plugin(stkops stkOpcodes.cpp) - target_include_directories(stkops PRIVATE ${CSOUND_INCLUDE_DIRS} ${STK_INCLUDE_DIR}) - message(STATUS "Building STK opcodes.") - - if(BUILD_STATIC_LIBRARY) - if(WIN32) - add_library(stk STATIC IMPORTED) - set_target_properties(stk PROPERTIES IMPORTED_LOCATION ${STK_LIBRARY}) - target_link_libraries(stkops stk) - else() - target_link_libraries(stkops ${STK_LIBRARY}) - endif() - else() - target_link_libraries(stkops ${STK_LIBRARIES} ${PTHREAD_LIBRARY}) - endif() - - target_compile_definitions(stkops PRIVATE ${rawwave_path}) - endif() - else() - set(stk_remove_srcs - src/InetWvIn.cpp src/InetWvOut.cpp - src/Mutex.cpp src/RtAudio.cpp - src/RtMidi.cpp src/Messager.cpp - src/RtWvIn.cpp src/RtWvOut.cpp - src/Socket.cpp src/TcpClient.cpp - src/TcpServer.cpp src/Thread.cpp - src/UdpSocket.cpp - ) - - file(GLOB stk_srcs RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} src/*) - list(REMOVE_ITEM stk_srcs ${stk_remove_srcs}) - list(APPEND stk_srcs stkOpcodes.cpp) - - # Assume that if this file exists, then we have the sources - find_file(STK_FOUND - NAMES src/InetWvIn.cpp - HINTS ${CMAKE_CURRENT_SOURCE_DIR} - ) - - find_path(STK_INCLUDE_DIR Stk.h ./include) - check_deps(BUILD_STK_OPCODES STK_FOUND STK_INCLUDE_DIR) - - make_plugin(stkops "${stk_srcs}") - target_include_directories(stkops PRIVATE - ${CSOUND_INCLUDE_DIRS} - ${CMAKE_CURRENT_SOURCE_DIR}/include - ${CMAKE_CURRENT_SOURCE_DIR}/include/stk - ${CMAKE_CURRENT_SOURCE_DIR}/src - ) - set(stkdefs "-D__STK_REALTIME__") - - if(APPLE) - list(APPEND stkdefs "-D__OS_MACOSX__") - list(REMOVE_ITEM stkdefs "-D__STK_REALTIME__") - elseif(LINUX) - list(APPEND stkdefs "-D__OS_LINUX__") - list(APPEND stkdefs "-D__LINUX_ALSA__") - elseif(WIN32) - list(APPEND stkdefs "-D__OS_WINDOWS__") - endif() - - if(BIG_ENDIAN) - list(APPEND stkdefs "-D__BIG_ENDIAN__") - else() - list(APPEND stkdefs "-D__LITTLE_ENDIAN__") - endif() - - target_compile_definitions(stkops PRIVATE ${stkdefs} ${rawwave_path}) - endif() - endif() -endif() - diff --git a/6.x/src/stk/OpcodeBase.hpp b/6.x/src/stk/OpcodeBase.hpp deleted file mode 100644 index 21ca8a0c..00000000 --- a/6.x/src/stk/OpcodeBase.hpp +++ /dev/null @@ -1,492 +0,0 @@ -/* - OpcodeBase.hpp: - - Copyright (C) 2005, 2009, 2017 by Istva Varga, Victor Lazzarini and - Michael Gogins - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ -#pragma once - -/** - * There is a conflict between the preprocessor definition "_CR" in the - * standard C++ library and in Csound. To work around this, undefine "_CR" and - * include ALL standard library include files BEFORE including ANY Csound - * include files. - */ -#if defined(_CR) -#undef _CR -#endif - -#include -#include -#include -#if !(defined(__wasi__)) -#include -#endif -#include -#include -#include -#include - -/** - * Template base class, or pseudo-virtual base class, - * for writing Csound opcodes in C++. - * Derive opcode implementation classes like this: - * - * DerivedClass : public OpcodeBase - * { - * public: - * // All output fields must be declared first as MYFLT *: - * MYFLT *aret1; - * // All input fields must be declared next as MYFLT *: - * MYFLT *iarg1; - * MYFLT *karg2; - * MYFLT *aarg3; - * // All internal state variables must be declared after that: - * size_t state1; - * double state2; - * MYFLT state3; - * // If the opcode shares data protect it by creating one or more void - * // *mutex member pointers: - * void *mutex1; - * void *mutex2; - * // and create them usind csound->Create_Mutex() in csoundModuleCreate - * // and destroy them using csound->DeleteMutex(mutex) - * // csoundModuleDestroy, and lock them using LockGuard guard(mutex). - * int init(); - * int kontrol(); - * int audio; - * int noteoff(); - * void deinit(); - * }; - */ - -namespace csound { - -static bool diagnostics_enabled = false; - -/** - * Use this to guard against data races in opcode functions. The mutex should - * be created by csound->Create_Mutex() in csoundModuleCreate(), and should - * be destroyed by csound->DeleteMutex() in csoundModuleDestroy(). - * - * If data is shared between opcode instances, the mutex should be global to - * the opcode library; if data is shared between threads for a single instance, - * the mutex should belong to the opcode instance. - */ -struct LockGuard { - LockGuard(CSOUND *csound_, void *mutex_) : csound(csound_), mutex(mutex_) - { - csound->LockMutex(mutex); - } - ~LockGuard() - { - csound->UnlockMutex(mutex); - } - CSOUND *csound; - void *mutex; -}; - -/** - * Use this to store a pointer to a global heap-allocated object, e.g. one - * used to manage state between opcode instances. - */ - -template int CreateGlobalPointer(CSOUND *csound, const char *name, T *pointer) -{ - T **pointer_to_pointer = 0; - int result = csound->CreateGlobalVariable(csound, name, sizeof(pointer_to_pointer)); - pointer_to_pointer = static_cast(csound->QueryGlobalVariable(csound, name)); - *pointer_to_pointer = pointer; - return result; -} - -/** - * Retrieve a pointer to a global heap-allocated object, e.g. one - * used to manage state between opcode instances. - */ -template T *QueryGlobalPointer(CSOUND *csound, const char *name, T*& pointer) -{ - T **pointer_to_pointer = static_cast(csound->QueryGlobalVariableNoCheck(csound, name)); - if (pointer_to_pointer != 0) { - pointer = *pointer_to_pointer; - } else { - pointer = 0; - } - return pointer; -} - -template -class OpcodeBase -{ -public: - int init(CSOUND *csound) - { - return NOTOK; - } - static int init_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->init(csound); - } - int kontrol(CSOUND *csound) - { - return NOTOK; - } - static int kontrol_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->kontrol(csound); - } - int audio(CSOUND *csound) - { - return NOTOK; - } - static int audio_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->audio(csound); - } - /** - This is how to compute audio signals for normal opcodes: - (1) Zero all frames from 0 up to but not including Offset. - (2) Compute all frames from ksmps_offset up to but not including End. - (3) Zero all frames from End up to but not including ksmps. - Example from a C opcode: - uint32_t offset = p->h.insdshead->ksmps_offset; - uint32_t early = p->h.insdshead->ksmps_no_end; - uint32_t n, nsmps = CS_KSMPS; - if (UNLIKELY(offset)) memset(p->r, '\0', offset*sizeof(MYFLT)); - if (UNLIKELY(early)) { - nsmps -= early; - memset(&p->r[nsmps], '\0', early*sizeof(MYFLT)); - } - for (n = offset; n < nsmps; n++) { - input1 = MYFLT2LRND(p->a[n]); - p->r[n] = (MYFLT) (input1 >> input2); - } - So in C++ it should look like this (which is much easier to understand): - int frameIndex = 0; - for( ; frameIndex < kperiodOffset(); ++frameIndex) { - asignal[frameIndex] = 0; - } - for( ; frameIndex < kperiodEnd(); ++frameIndex) { - asignal[frameIndex] = compute(); - } - for( ; frameIndex < ksmps(); ++frameIndex) { - asignal[frameIndex] = 0; - } - */ - uint32_t kperiodOffset() const - { - return opds.insdshead->ksmps_offset; - } - uint32_t kperiodEnd() const - { - uint32_t end = opds.insdshead->ksmps_no_end; - if (end) { - return end; - } else { - return ksmps(); - } - } - uint32_t ksmps() const - { - return opds.insdshead->ksmps; - } - uint32_t output_arg_count() - { - return (uint32_t)opds.optext->t.outArgCount; - } - uint32_t input_arg_count() - { - return (uint32_t)opds.optext->t.inArgCount; - } - void log(CSOUND *csound, const char *format,...) - { - va_list args; - va_start(args, format); - if(csound) { - csound->MessageV(csound, 0, format, args); - } else { - vfprintf(stdout, format, args); - } - va_end(args); - } - void warn(CSOUND *csound, const char *format,...) - { - if(csound) { - if(csound->GetMessageLevel(csound) & CS_WARNMSG) { - va_list args; - va_start(args, format); - csound->MessageV(csound, CSOUNDMSG_WARNING, format, args); - va_end(args); - } - } else { - va_list args; - va_start(args, format); - vfprintf(stdout, format, args); - va_end(args); - } - } - OPDS opds; -}; - -template -class OpcodeNoteoffBase -{ -public: - int init(CSOUND *csound) - { - IGN(csound); - return NOTOK; - } - static int init_(CSOUND *csound, void *opcode) - { - if (!csound->GetReinitFlag(csound) && !csound->GetTieFlag(csound)) { - csound->RegisterDeinitCallback(csound, opcode, - &OpcodeNoteoffBase::noteoff_); - } - return reinterpret_cast(opcode)->init(csound); - } - int kontrol(CSOUND *csound) - { - IGN(csound); - return NOTOK; - } - static int kontrol_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->kontrol(csound); - } - int audio(CSOUND *csound) - { - IGN(csound); - return NOTOK; - } - static int audio_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->audio(csound); - } - /** - This is how to compute audio signals for normal opcodes: - (1) Zero all frames from 0 up to but not including Offset. - (2) Compute all frames from ksmps_offset up to but not including End. - (3) Zero all frames from End up to but not including ksmps. - Example from a C opcode: - uint32_t offset = p->h.insdshead->ksmps_offset; - uint32_t early = p->h.insdshead->ksmps_no_end; - uint32_t n, nsmps = CS_KSMPS; - if (UNLIKELY(offset)) memset(p->r, '\0', offset*sizeof(MYFLT)); - if (UNLIKELY(early)) { - nsmps -= early; - memset(&p->r[nsmps], '\0', early*sizeof(MYFLT)); - } - for (n = offset; n < nsmps; n++) { - input1 = MYFLT2LRND(p->a[n]); - p->r[n] = (MYFLT) (input1 >> input2); - } - So in C++ it should look like this (which is much easier to understand): - int frameIndex = 0; - for( ; frameIndex < kperiodOffset(); ++frameIndex) { - asignal[frameIndex] = 0; - } - for( ; frameIndex < kperiodEnd(); ++frameIndex) { - asignal[frameIndex] = compute(); - } - for( ; frameIndex < ksmps(); ++frameIndex) { - asignal[frameIndex] = 0; - } - */ - uint32_t kperiodOffset() const - { - return opds.insdshead->ksmps_offset; - } - uint32_t kperiodEnd() const - { - uint32_t end = opds.insdshead->ksmps_no_end; - if (end) { - return end; - } else { - return ksmps(); - } - } - uint32_t ksmps() const - { - return opds.insdshead->ksmps; - } - uint32_t output_arg_count() - { - return (uint32_t)opds.optext->t.outArgCount; - } - uint32_t input_arg_count() - { - return (uint32_t)opds.optext->t.inArgCount; - } - void log(CSOUND *csound, const char *format,...) - { - va_list args; - va_start(args, format); - if(csound) { - csound->MessageV(csound, 0, format, args); - } else { - vfprintf(stdout, format, args); - } - va_end(args); - } - void warn(CSOUND *csound, const char *format,...) - { - if(csound) { - if(csound->GetMessageLevel(csound) & CS_WARNMSG) { - va_list args; - va_start(args, format); - csound->MessageV(csound, CSOUNDMSG_WARNING, format, args); - va_end(args); - } - } else { - va_list args; - va_start(args, format); - vfprintf(stdout, format, args); - va_end(args); - } - } - int noteoff(CSOUND *csound) - { - return OK; - } - static int noteoff_(CSOUND *csound, void *opcode) - { - return reinterpret_cast(opcode)->noteoff(csound); - } - OPDS opds; -}; - -#if (__cplusplus >= 201103L) && !(defined(__wasi__)) -#pragma message("Defining heap_object_manager_t.") - -/** - * The memory of non-POD C++ or C objects allocated on the heap by Csound - * plugins is managed not by Csound, but by the plugin module. This class - * performs that function. - * - * As long as all memory is allocated in or after csoundModuleCreate and - * de-allocated in csoundModuleDestroy, all will be well. The danger however - * is that memory leaks will occur if allocations are not matched with - * de-allocations, or worse, that crashes will occur if any objects are - * deleted twice or de-referenced after deletion. - * - * So, the plugin should manage a collection of all heap-allocated objects, - * keyed first off the Csound instance, and second off an object handle; and - * all such objects must be accessed only by handle, from this collection. - * - * In csoundModuleDestroy, all objects for the calling instance of Csound - * should be de-allocated, and the sub-collection for that instance should - * also be de-allocated. - * - * If then there are no more sub-collections for other instances of Csound, - * the entire contents of the collection should be de-allocated. - * - * This should be done as a singleton, and in a thread-safe way. - * - * There might still be a problem if any of the heap-allocated objects, - * themselves, have global static members, perhaps created by some external - * dependency. - * - * Please note, objects O in the declaration below must not be std::shared_ptr - * objects. Deletion of objects herein must be unconditional. Rather, the - * objects passed are raw pointers to instances of class O. - */ - template class heap_object_manager_t { - private: - std::map> objects_; - std::recursive_mutex mutex; - heap_object_manager_t(){}; - ~heap_object_manager_t(){}; - public: - static heap_object_manager_t &instance() { - static heap_object_manager_t singleton; - return singleton; - } - std::map> &objects() { - std::lock_guard lock(mutex); - return objects_; - } - /** - * Returns a list of pointers to all objects allocated for this - * instance of Csound. - */ - std::vector &objects_for_csound(CSOUND *csound) { - std::lock_guard lock(mutex); - auto &objects_for_csound_ = objects()[csound]; - return objects_for_csound_; - } - /** - * Returns the handle for the object; if the object has not yet been - * stored, inserts it into the list of object pointers for this - * instance of Csound; otherwise, returns the handle of the stored - * object pointer. - */ - int handle_for_object(CSOUND *csound, O *object) { - std::lock_guard lock(mutex); - auto &objects_for_csound_ = objects_for_csound(csound); - auto iterator = std::find(objects_for_csound_.begin(), objects_for_csound_.end(), object); - if (iterator == objects_for_csound_.end()) { - int handle = objects_for_csound_.size(); - objects_for_csound_.push_back(object); - if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::handle_for_object %p: new object handle: %d (of %ld)\n", object, handle, objects_for_csound_.size()); - return handle; - } else { - int handle = static_cast(iterator - objects_for_csound_.begin()); - if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::handle_for_object: existing object handle: %d\n", handle); - return handle; - } - } - /** - * Returns the object pointer for the handle; - * if the object pointer has not been stored by - * handle, returns a null pointer. - */ - O *object_for_handle(CSOUND *csound, int handle) { - std::lock_guard lock(mutex); - auto &objects_for_csound_ = objects_for_csound(csound); - if (handle >= objects_for_csound_.size()) { - return nullptr; - } - O *object = objects_for_csound_[handle]; - if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::object_for_handle: %p %d (of %ld)\n", object, handle, objects_for_csound_.size()); - return object; - } - /** - * First destroys all objects created by the calling - * instance of Csound, then destroys the list of - * object pointers for this instance of Csound. - */ - void module_destroy(CSOUND *csound) { - std::lock_guard lock(mutex); - auto &objects_for_csound_ = objects_for_csound(csound); - for (int i = 0, n = objects_for_csound_.size(); i < n; ++i) { - delete objects_for_csound_[i]; - objects_for_csound_[i] = nullptr; - } - objects_for_csound_.clear(); - objects().erase(csound); - } -}; - -#else -#pragma message("Not defining heap_manager_t.") -#endif - -} diff --git a/6.x/src/stk/stkOpcodes.cpp b/6.x/src/stk/stkOpcodes.cpp deleted file mode 100644 index f244969b..00000000 --- a/6.x/src/stk/stkOpcodes.cpp +++ /dev/null @@ -1,597 +0,0 @@ -/* - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ -/* - * CSOUND 5/6 OPCODES FOR PERRY COOK'S SYNTHESIS TOOLKIT IN C++ (STK) INSTRUMENT - * - * This code is copyright (C) 2005 by Michael Gogins. It is licensed under the - * same terms as the Synthesis Tookit in C++ by Perry R. Cook and Gary P. - * Scavone. - * - * To compile these opcodes, copy the STK include, src, and rawwaves directories - * to the csound6/Opcodes/stk directory as follows: - * - * csound6/Opcodes/stk/include - * csound6/Opcodes/stk/src - * csound6/Opcodes/stk/rawwaves - * - * To use these opcodes, define a RAWWAVE_PATH environment variable that points - * to your rawwaves directory, which contains raw soundfiles with function table - * data. - * - * All these opcodes are named "STK" + the STK classname, - * e.g. "STKBowed" for the Bowed instrument. - * - * All the STK opcodes have the same signature: - * - * aout STKName ifrequency igain {kcontroller0, kvalue1,...,kcontroller3, - * kvalue3} - * - * They take a frequency in Hertz and a gain parameter in the range [0, 1], - * plus up to four optional krate controller-value pairs, and return an arate - * signal that should be more or less in the range [-1, +1]. - * See the STK class documentation to determine the controller numbers - * used by each instrument. - */ -#if defined(_MSC_VER) -#include "stk\BandedWG.h" -#include "stk\BeeThree.h" -#include "stk\BlowBotl.h" -#include "stk\BlowHole.h" -#include "stk\Bowed.h" -#include "stk\Brass.h" -#include "stk\Clarinet.h" -#include "stk\Drummer.h" -#include "stk\FMVoices.h" -#include "stk\Flute.h" -#include "stk\HevyMetl.h" -#include "stk\Mandolin.h" -#include "stk\Stk.h" - //#include "stk\Mesh2D.h" -#include "stk\ModalBar.h" -#include "stk\Moog.h" -#include "stk\PercFlut.h" -#include "stk\Plucked.h" -#include "stk\Resonate.h" -#include "stk\Rhodey.h" -#include "stk\Saxofony.h" -#include "stk\Shakers.h" -#include "stk\Simple.h" -#include "stk\Sitar.h" -#include "stk\StifKarp.h" -#include "stk\TubeBell.h" -#include "stk\VoicForm.h" -#include "stk\Whistle.h" -#include "stk\Wurley.h" -#else -#include "BandedWG.h" -#include "BeeThree.h" -#include "BlowBotl.h" -#include "BlowHole.h" -#include "Bowed.h" -#include "Brass.h" -#include "Clarinet.h" -#include "Drummer.h" -#include "FMVoices.h" -#include "Flute.h" -#include "HevyMetl.h" -#include "Mandolin.h" -#include "Stk.h" -//#include "Mesh2D.h" -#include "ModalBar.h" -#include "Moog.h" -#include "PercFlut.h" -#include "Plucked.h" -#include "Resonate.h" -#include "Rhodey.h" -#include "Saxofony.h" -#include "Shakers.h" -#include "Simple.h" -#include "Sitar.h" -#include "StifKarp.h" -#include "TubeBell.h" -#include "VoicForm.h" -#include "Whistle.h" -#include "Wurley.h" -#endif -using namespace stk; - -#define __BUILDING_LIBCSOUND -#include -#include - -using namespace csound; - -#include -#include -#include -#include -#include -#include - -using namespace std; -using namespace csound; - -template -class STKInstrumentAdapter : public OpcodeBase> { -public: - // Outputs. - MYFLT *aoutput; - // Inputs. - MYFLT *ifrequency; - MYFLT *igain; - MYFLT *kcontroller0; - MYFLT *kvalue0; - MYFLT *kcontroller1; - MYFLT *kvalue1; - MYFLT *kcontroller2; - MYFLT *kvalue2; - MYFLT *kcontroller3; - MYFLT *kvalue3; - MYFLT *kcontroller4; - MYFLT *kvalue4; - MYFLT *kcontroller5; - MYFLT *kvalue5; - MYFLT *kcontroller6; - MYFLT *kvalue6; - MYFLT *kcontroller7; - MYFLT *kvalue7; - // State. - T *instrument; - size_t ksmps; - bool released; - MYFLT oldkcontroller0; - MYFLT oldkvalue0; - MYFLT oldkcontroller1; - MYFLT oldkvalue1; - MYFLT oldkcontroller2; - MYFLT oldkvalue2; - MYFLT oldkcontroller3; - MYFLT oldkvalue3; - MYFLT oldkcontroller4; - MYFLT oldkvalue4; - MYFLT oldkcontroller5; - MYFLT oldkvalue5; - MYFLT oldkcontroller6; - MYFLT oldkvalue6; - MYFLT oldkcontroller7; - MYFLT oldkvalue7; - STKInstrumentAdapter() : instrument(0) {} - int32_t init(CSOUND *csound) { - if (!instrument) { - Stk::setSampleRate(csound->GetSr(csound)); - instrument = new T(); - } - ksmps = OpcodeBase>::opds.insdshead->ksmps; - instrument->noteOn(*ifrequency, *igain); - released = false; - oldkcontroller0 = -1.0; - oldkvalue0 = -1.0; - oldkcontroller1 = -1.0; - oldkvalue1 = -1.0; - oldkcontroller2 = -1.0; - oldkvalue2 = -1.0; - oldkcontroller3 = -1.0; - oldkvalue3 = -1.0; - oldkcontroller4 = -1.0; - oldkvalue4 = -1.0; - oldkcontroller5 = -1.0; - oldkvalue5 = -1.0; - oldkcontroller6 = -1.0; - oldkvalue6 = -1.0; - oldkcontroller7 = -1.0; - oldkvalue7 = -1.0; - return OK; - } - int32_t kontrol(CSOUND *csound) { - uint32_t offset = - OpcodeBase>::opds.insdshead->ksmps_offset; - if (!released) { - if (*kcontroller0 != oldkcontroller0 || *kvalue0 != oldkvalue0) { - instrument->controlChange(static_cast(*kcontroller0), *kvalue0); - oldkcontroller0 = *kcontroller0; - oldkvalue0 = *kvalue0; - } - if (*kcontroller1 != oldkcontroller1 || *kvalue1 != oldkvalue1) { - instrument->controlChange(static_cast(*kcontroller1), *kvalue1); - oldkcontroller1 = *kcontroller1; - oldkvalue1 = *kvalue1; - } - if (*kcontroller2 != oldkcontroller2 || *kvalue2 != oldkvalue2) { - instrument->controlChange(static_cast(*kcontroller2), *kvalue2); - oldkcontroller2 = *kcontroller2; - oldkvalue2 = *kvalue2; - } - if (*kcontroller3 != oldkcontroller3 || *kvalue3 != oldkvalue3) { - instrument->controlChange(static_cast(*kcontroller3), *kvalue3); - oldkcontroller3 = *kcontroller3; - oldkvalue3 = *kvalue3; - } - if (*kcontroller4 != oldkcontroller4 || *kvalue4 != oldkvalue4) { - instrument->controlChange(static_cast(*kcontroller4), *kvalue4); - oldkcontroller4 = *kcontroller4; - oldkvalue4 = *kvalue4; - } - if (*kcontroller5 != oldkcontroller5 || *kvalue5 != oldkvalue5) { - instrument->controlChange(static_cast(*kcontroller5), *kvalue5); - oldkcontroller5 = *kcontroller5; - oldkvalue5 = *kvalue5; - } - if (*kcontroller6 != oldkcontroller6 || *kvalue6 != oldkvalue6) { - instrument->controlChange(static_cast(*kcontroller6), *kvalue6); - oldkcontroller6 = *kcontroller6; - oldkvalue6 = *kvalue6; - } - if (*kcontroller7 != oldkcontroller7 || *kvalue7 != oldkvalue7) { - instrument->controlChange(static_cast(*kcontroller7), *kvalue7); - oldkcontroller7 = *kcontroller7; - oldkvalue7 = *kvalue7; - } - memset(aoutput, '\0', offset * sizeof(MYFLT)); - for (size_t i = offset; i < ksmps; i++) { - aoutput[i] = instrument->tick(); - } - } else { - // memset(aoutput, 0, ksmps*sizeof(MYFLT)); - for (size_t i = 0; i < ksmps; i++) { - aoutput[i] = 0; - } - } - return OK; - } -}; - -template -class STKInstrumentAdapter1 : public OpcodeBase> { -public: - // Outputs. - MYFLT *aoutput; - // Inputs. - MYFLT *ifrequency; - MYFLT *igain; - MYFLT *kcontroller0; - MYFLT *kvalue0; - MYFLT *kcontroller1; - MYFLT *kvalue1; - MYFLT *kcontroller2; - MYFLT *kvalue2; - MYFLT *kcontroller3; - MYFLT *kvalue3; - MYFLT *kcontroller4; - MYFLT *kvalue4; - MYFLT *kcontroller5; - MYFLT *kvalue5; - MYFLT *kcontroller6; - MYFLT *kvalue6; - MYFLT *kcontroller7; - MYFLT *kvalue7; - // State. - T *instrument; - size_t ksmps; - bool released; - MYFLT oldkcontroller0; - MYFLT oldkvalue0; - MYFLT oldkcontroller1; - MYFLT oldkvalue1; - MYFLT oldkcontroller2; - MYFLT oldkvalue2; - MYFLT oldkcontroller3; - MYFLT oldkvalue3; - MYFLT oldkcontroller4; - MYFLT oldkvalue4; - MYFLT oldkcontroller5; - MYFLT oldkvalue5; - MYFLT oldkcontroller6; - MYFLT oldkvalue6; - MYFLT oldkcontroller7; - MYFLT oldkvalue7; - STKInstrumentAdapter1() : instrument(0) {} - int32_t init(CSOUND *csound) { - if (!instrument) { - Stk::setSampleRate(csound->GetSr(csound)); - instrument = new T((StkFloat)10.0); - } - ksmps = OpcodeBase>::opds.insdshead->ksmps; - instrument->noteOn(*ifrequency, *igain); - released = false; - oldkcontroller0 = -1.0; - oldkvalue0 = -1.0; - oldkcontroller1 = -1.0; - oldkvalue1 = -1.0; - oldkcontroller2 = -1.0; - oldkvalue2 = -1.0; - oldkcontroller3 = -1.0; - oldkvalue3 = -1.0; - oldkcontroller4 = -1.0; - oldkvalue4 = -1.0; - oldkcontroller5 = -1.0; - oldkvalue5 = -1.0; - oldkcontroller6 = -1.0; - oldkvalue6 = -1.0; - oldkcontroller7 = -1.0; - oldkvalue7 = -1.0; - return OK; - } - int32_t kontrol(CSOUND *csound) { - uint32_t offset = - OpcodeBase>::opds.insdshead->ksmps_offset; - if (!released) { - if (*kcontroller0 != oldkcontroller0 || *kvalue0 != oldkvalue0) { - instrument->controlChange(static_cast(*kcontroller0), *kvalue0); - oldkcontroller0 = *kcontroller0; - oldkvalue0 = *kvalue0; - } - if (*kcontroller1 != oldkcontroller1 || *kvalue1 != oldkvalue1) { - instrument->controlChange(static_cast(*kcontroller1), *kvalue1); - oldkcontroller1 = *kcontroller1; - oldkvalue1 = *kvalue1; - } - if (*kcontroller2 != oldkcontroller2 || *kvalue2 != oldkvalue2) { - instrument->controlChange(static_cast(*kcontroller2), *kvalue2); - oldkcontroller2 = *kcontroller2; - oldkvalue2 = *kvalue2; - } - if (*kcontroller3 != oldkcontroller3 || *kvalue3 != oldkvalue3) { - instrument->controlChange(static_cast(*kcontroller3), *kvalue3); - oldkcontroller3 = *kcontroller3; - oldkvalue3 = *kvalue3; - } - if (*kcontroller4 != oldkcontroller4 || *kvalue4 != oldkvalue4) { - instrument->controlChange(static_cast(*kcontroller4), *kvalue4); - oldkcontroller4 = *kcontroller4; - oldkvalue4 = *kvalue4; - } - if (*kcontroller5 != oldkcontroller5 || *kvalue5 != oldkvalue5) { - instrument->controlChange(static_cast(*kcontroller5), *kvalue5); - oldkcontroller5 = *kcontroller5; - oldkvalue5 = *kvalue5; - } - if (*kcontroller6 != oldkcontroller6 || *kvalue6 != oldkvalue6) { - instrument->controlChange(static_cast(*kcontroller6), *kvalue6); - oldkcontroller6 = *kcontroller6; - oldkvalue6 = *kvalue6; - } - if (*kcontroller7 != oldkcontroller7 || *kvalue7 != oldkvalue7) { - instrument->controlChange(static_cast(*kcontroller7), *kvalue7); - oldkcontroller7 = *kcontroller7; - oldkvalue7 = *kvalue7; - } - memset(aoutput, '\0', offset * sizeof(MYFLT)); - for (size_t i = offset; i < ksmps; i++) { - aoutput[i] = instrument->tick(); - } - } else { - // memset(aoutput, 0, ksmps*sizef(MYFLT)); - for (size_t i = 0; i < ksmps; i++) { - aoutput[i] = 0; - } - } - return OK; - } -}; - -extern "C" { -OENTRY oentries[] = { - { - (char *)"STKBandedWG", sizeof(STKInstrumentAdapter), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter::init_, - (SUBR)STKInstrumentAdapter::kontrol_, 0, - }, - { - (char *)"STKBeeThree", sizeof(STKInstrumentAdapter), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter::init_, - (SUBR)STKInstrumentAdapter::kontrol_, 0, - }, - { - (char *)"STKBlowBotl", sizeof(STKInstrumentAdapter), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter::init_, - (SUBR)STKInstrumentAdapter::kontrol_, 0, - }, - { - (char *)"STKBlowHole", sizeof(STKInstrumentAdapter1), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter1::init_, - (SUBR)STKInstrumentAdapter1::kontrol_, 0, - }, - { - (char *)"STKBowed", sizeof(STKInstrumentAdapter1), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter1::init_, - (SUBR)STKInstrumentAdapter1::kontrol_, 0, - }, - { - (char *)"STKBrass", sizeof(STKInstrumentAdapter1), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter1::init_, - (SUBR)STKInstrumentAdapter1::kontrol_, 0, - }, - { - (char *)"STKClarinet", sizeof(STKInstrumentAdapter1), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter1::init_, - (SUBR)STKInstrumentAdapter1::kontrol_, 0, - }, - { - (char *)"STKDrummer", sizeof(STKInstrumentAdapter), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter::init_, - (SUBR)STKInstrumentAdapter::kontrol_, 0, - }, - { - (char *)"STKFlute", sizeof(STKInstrumentAdapter1), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter1::init_, - (SUBR)STKInstrumentAdapter1::kontrol_, 0, - }, - { - (char *)"STKFMVoices", sizeof(STKInstrumentAdapter), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter::init_, - (SUBR)STKInstrumentAdapter::kontrol_, 0, - }, - { - (char *)"STKHevyMetl", sizeof(STKInstrumentAdapter), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter::init_, - (SUBR)STKInstrumentAdapter::kontrol_, 0, - }, - { - (char *)"STKMandolin", sizeof(STKInstrumentAdapter1), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter1::init_, - (SUBR)STKInstrumentAdapter1::kontrol_, 0, - }, - { - (char *)"STKModalBar", sizeof(STKInstrumentAdapter), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter::init_, - (SUBR)STKInstrumentAdapter::kontrol_, 0, - }, - { - (char *)"STKMoog", sizeof(STKInstrumentAdapter), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter::init_, - (SUBR)STKInstrumentAdapter::kontrol_, 0, - }, - { - (char *)"STKPercFlut", sizeof(STKInstrumentAdapter), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter::init_, - (SUBR)STKInstrumentAdapter::kontrol_, 0, - }, - { - (char *)"STKPlucked", sizeof(STKInstrumentAdapter1), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter1::init_, - (SUBR)STKInstrumentAdapter1::kontrol_, 0, - }, - { - (char *)"STKResonate", sizeof(STKInstrumentAdapter), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter::init_, - (SUBR)STKInstrumentAdapter::kontrol_, 0, - }, - { - (char *)"STKRhodey", sizeof(STKInstrumentAdapter), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter::init_, - (SUBR)STKInstrumentAdapter::kontrol_, 0, - }, - { - (char *)"STKSaxofony", sizeof(STKInstrumentAdapter1), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter1::init_, - (SUBR)STKInstrumentAdapter1::kontrol_, 0, - }, - { - (char *)"STKShakers", sizeof(STKInstrumentAdapter), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter::init_, - (SUBR)STKInstrumentAdapter::kontrol_, 0, - }, - { - (char *)"STKSimple", sizeof(STKInstrumentAdapter), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter::init_, - (SUBR)STKInstrumentAdapter::kontrol_, 0, - }, - { - (char *)"STKSitar", sizeof(STKInstrumentAdapter), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter::init_, - (SUBR)STKInstrumentAdapter::kontrol_, 0, - }, - { - (char *)"STKStifKarp", sizeof(STKInstrumentAdapter1), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter1::init_, - (SUBR)STKInstrumentAdapter1::kontrol_, 0, - }, - { - (char *)"STKTubeBell", sizeof(STKInstrumentAdapter), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter::init_, - (SUBR)STKInstrumentAdapter::kontrol_, 0, - }, - { - (char *)"STKVoicForm", sizeof(STKInstrumentAdapter), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter::init_, - (SUBR)STKInstrumentAdapter::kontrol_, 0, - }, - { - (char *)"STKWhistle", sizeof(STKInstrumentAdapter), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter::init_, - (SUBR)STKInstrumentAdapter::kontrol_, 0, - }, - { - (char *)"STKWurley", sizeof(STKInstrumentAdapter), 0, 3, - (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", - (SUBR)STKInstrumentAdapter::init_, - (SUBR)STKInstrumentAdapter::kontrol_, 0, - }, - { - 0, 0, 0, 0, 0, 0, 0, 0, - }}; - -PUBLIC int32_t csoundModuleCreate(CSOUND *csound) { return 0; } - -PUBLIC int32_t csoundModuleInit(CSOUND *csound) { - const char *path = csound->GetEnv(csound, "RAWWAVE_PATH"); -#ifdef DEFAULT_RAWWAVE_PATH - if (!path) - path = DEFAULT_RAWWAVE_PATH; -#endif - if (path == NULL) { - if (UNLIKELY(csound->GetDebug(csound))) - csound->Warning(csound, - "%s", Str("STK opcodes not available: define environment " - "variable RAWWAVE_PATH\n(points " - "to rawwaves directory) to use STK opcodes.")); - return 0; - } else { -#if !defined(MACOSX) - if (path) { - path = std::getenv("RAWWAVE_PATH"); - } -#endif - - if (path != NULL && strlen(path) != 0) { - Stk::setRawwavePath(path); - } - // csound->DebugMsg(csound, - // "%s", Str("RAWWAVE_PATH: %s\n"), Stk::rawwavePath().c_str()); - } - int32_t status = 0; - for (OENTRY *oentry = &oentries[0]; oentry->opname; oentry++) { - status |= csound->AppendOpcode(csound, oentry->opname, oentry->dsblksiz, - oentry->flags, oentry->thread, - oentry->outypes, oentry->intypes, - (int32_t (*)(CSOUND *, void *))oentry->iopadr, - (int32_t (*)(CSOUND *, void *))oentry->kopadr, - (int32_t (*)(CSOUND *, void *))oentry->aopadr); - } - return status; -} - -PUBLIC int32_t csoundModuleDestroy(CSOUND *csound) { return 0; } -} diff --git a/6.x/src/websockets/CMakeLists.txt b/6.x/src/websockets/CMakeLists.txt deleted file mode 100644 index 2438a8c3..00000000 --- a/6.x/src/websockets/CMakeLists.txt +++ /dev/null @@ -1,32 +0,0 @@ -option(BUILD_WEBSOCKET_OPCODE "Build the websocket opcode" ON) - -if(USE_VCPKG) - find_package(libuv CONFIG) - find_package(libwebsockets CONFIG) -else() - find_path(WEBSOCKETS_H libwebsockets.h /usr/local/include /usr/include) - find_library(websockets_library NAMES websockets) -endif() - -if(APPLE) - find_library(ssl_library NAMES ssl) - find_library(crypto_library NAMES crypto) - - check_deps(BUILD_WEBSOCKET_OPCODE websockets_library ssl_library crypto_library WEBSOCKETS_H) -endif() - -if(BUILD_WEBSOCKET_OPCODE) - make_plugin(websocketIO WebSocketOpcode.c) - target_include_directories(websocketIO PRIVATE ${CSOUND_INCLUDE_DIRS}) - - if(USE_VCPKG) - target_link_libraries(websocketIO websockets uv_a) - else() - target_link_libraries(websocketIO ${websockets_library}) - target_include_directories(websocketIO PRIVATE ${WEBSOCKETS_H}) - endif() - - if(APPLE) - target_link_libraries(websocketIO ${ssl_library} ${crypto_library} z) - endif() -endif() diff --git a/6.x/src/widgets/CMakeLists.txt b/6.x/src/widgets/CMakeLists.txt deleted file mode 100644 index 4dda233a..00000000 --- a/6.x/src/widgets/CMakeLists.txt +++ /dev/null @@ -1,57 +0,0 @@ -option(USE_FLTK "Use FLTK for graphs and widget opcodes" ON) - -set(FLTK_SKIP_OPENGL true) - -if(USE_VCPKG) - find_package(FLTK CONFIG) -else() - find_package(FLTK) -endif() - -find_path(CAIRO_INCLUDE_DIR cairo.h PATH_SUFFIXES cairo) - -check_deps(USE_FLTK FLTK_FOUND) - -if(USE_FLTK) - set(widgets_SRCS FL_graph.cpp winFLTK.c widgets.cpp) - - make_plugin(widgets "${widgets_SRCS}" "${FLTK_LIBRARIES}") - target_include_directories(widgets PRIVATE ${CSOUND_INCLUDE_DIRS} ${FLTK_INCLUDE_DIR}) - - if(USE_VCPKG) - target_link_libraries(widgets PRIVATE fltk fltk_gl fltk_forms fltk_images) - else() - target_link_libraries(widgets PRIVATE - ${FLTK_BASE_LIBRARY} ${FLTK_FORMS_LIBRARY} ${FLTK_IMAGES_LIBRARY} - ) - endif() -endif() - -if(USE_FLTK) - set(virtual_SRCS - virtual_keyboard/FLTKKeyboard.cpp - virtual_keyboard/FLTKKeyboardWindow.cpp - virtual_keyboard/FLTKKeyboardWidget.cpp - virtual_keyboard/virtual_keyboard.cpp - virtual_keyboard/Bank.cpp - virtual_keyboard/KeyboardMapping.cpp - virtual_keyboard/Program.cpp - virtual_keyboard/SliderBank.cpp - virtual_keyboard/SliderData.cpp) - - make_plugin(virtual "${virtual_SRCS}" "${FLTK_LIBRARIES}") - target_include_directories(virtual PRIVATE ${CSOUND_INCLUDE_DIRS} ${FLTK_INCLUDE_DIR} ".") - - if (USE_VCPKG) - target_link_libraries(virtual PRIVATE fltk fltk_gl fltk_forms fltk_images) - else() - target_link_libraries(virtual PRIVATE - ${FLTK_BASE_LIBRARY} ${FLTK_FORMS_LIBRARY} ${FLTK_IMAGES_LIBRARY} - ) - endif() -endif() - -if(USE_FLTK AND CAIRO_INCLUDE_DIR) - target_include_directories(widgets PRIVATE ${CAIRO_INCLUDE_DIR}) - target_include_directories(virtual PRIVATE ${CAIRO_INCLUDE_DIR}) -endif() diff --git a/6.x/src/widgets/FL_graph.cpp b/6.x/src/widgets/FL_graph.cpp deleted file mode 100644 index a2674ec3..00000000 --- a/6.x/src/widgets/FL_graph.cpp +++ /dev/null @@ -1,504 +0,0 @@ - -/* - FL_graph.cpp: code for drawing graphs using FLTK library - - Copyright (C) 2003 John ffitch - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#include -#include -#include -#include -#include - -#ifdef _MSC_VER -#include -#endif -#include "csdl.h" -#include "cwindow.h" -#include "winFLTK.h" -#include - -#define NUMOFWINDOWS (30) -#define XINIT 10 /* set default window location */ -#define YINIT 50 -#define WIDTH 450 /* start off small - user resizes */ -#define HEIGHT 150 -#define BDR 5 -#define BORDERW 10 /* inset from L & R edge */ -#define TXHGHT 14 /* baseline offset for text */ -#define MAXLSEGS 4096 /* X can only deal with so many linesegs .. */ - -class graph_box : public Fl_Window { - void draw(); -public: - int curr; - int last; - CSOUND *csound; - graph_box(void *cs, int x, int y, int w, int h, const char *l = 0) - : Fl_Window(x, y, w, h, l) - { - curr = last = -1; - csound = (CSOUND*)cs; - } - //void add_graph(WINDAT *wdptr); -}; - -typedef struct { - - Fl_Choice *choice; - Fl_Button *end; - Fl_Menu_Item *menu; - graph_box *graph; - int graph_created; - Fl_Window *form; -} FLGRAPH_GLOBALS; - -#define ST(x) ((flgraphGlobals)->x) - -/*void printouts(CSOUND *csound){ - csound->Message(csound, "menu object: %p\n", ST(menu)); - csound->Message(csound, "form object: %p\n", ST(form)); - csound->Message(csound, "graph object: %p\n", ST(graph)); - csound->Message(csound, "choice object: %p\n", ST(choice)); - csound->Message(csound, "globals %p\n", csound->flgraphGlobals); -}*/ - -void flgraph_init(CSOUND *csound) -{ - - FLGRAPH_GLOBALS *flgraphGlobals = - (FLGRAPH_GLOBALS *) csound->QueryGlobalVariable(csound, - "FLGRAPH_GLOBALS"); - if (flgraphGlobals==NULL) { - csound->CreateGlobalVariable(csound, "FLGRAPH_GLOBALS", - sizeof(FLGRAPH_GLOBALS)); - flgraphGlobals = - (FLGRAPH_GLOBALS *) csound->QueryGlobalVariable(csound, - "FLGRAPH_GLOBALS"); - //csound->flgraphGlobals = - //(FLGRAPH_GLOBALS*) csound->Malloc(csound,sizeof(FLGRAPH_GLOBALS)); - } - ST(form) = (Fl_Window *) 0; - ST(choice) = (Fl_Choice *) 0; - ST(end) = (Fl_Button *) 0; - ST(graph) = (graph_box *) 0; - ST(menu) = (Fl_Menu_Item *) 0; - ST(graph_created) = 0; - - - /*ST(menu) = (Fl_Menu_Item*) csound->Calloc(csound, - sizeof(Fl_Menu_Item)*(1+NUMOFWINDOWS));*/ - /* VL: moved menu object to be built at each new compilation */ - -} - -/* static graph_box *graph = (graph_box *) 0; */ - -void graph_box::draw() -{ - FLGRAPH_GLOBALS *flgraphGlobals = - (FLGRAPH_GLOBALS *) csound->QueryGlobalVariable(csound, - "FLGRAPH_GLOBALS"); - Fl_Window::draw(); - fl_color(0, 0, 0); - fl_line_style(FL_SOLID); - fl_rect(0, 0, w(), h()); - if (curr >= 0) { - WINDAT *win = (WINDAT*) ST(menu)[curr].user_data_; - if (!win) - return; - MYFLT *fdata = win->fdata; - int32 npts = win->npts; - char *msg = win->caption; - short win_x, win_y, win_h; /* window rect */ - short gra_x, gra_y, gra_w, gra_h; /* graph rect is inset */ - short y_axis; - int lsegs, pts_pls; - int pol; - char string[400]; - - pol = win->polarity; - - win_x = 0; win_y = 0; /* window pixels addressed relative */ - win_h = h(); - - /* set new width and height so we leave a 20% border around the plot */ - gra_w = w() - 2*BORDERW; - gra_h = h(); - gra_x = win_x + BORDERW; - gra_y = win_y; - /* figure height of Y axis - top, middle or bottom */ - if (pol == (short)BIPOL) - y_axis = gra_y + (gra_h/2); - else if (pol == (short)NEGPOL) - y_axis = gra_y; - else /* POSPOL */ - y_axis = gra_y + gra_h; - - if (npts < MAXLSEGS) { - lsegs = npts; /* one lineseg per datum */ - pts_pls = 1; - } - else { - pts_pls = npts/MAXLSEGS; - if (npts%MAXLSEGS) pts_pls++; - lsegs = npts/pts_pls; - } - fl_begin_line(); - { /* take scale factors out of for-loop for faster run-time */ - MYFLT x_scale = gra_w / (MYFLT) (lsegs - 1); - MYFLT y_scale = gra_h / win->oabsmax; /* unipolar default */ - MYFLT f, ma, mi, *fdptr = fdata; - int c, i = 0, j = lsegs; - - if (pol == (short) BIPOL) - y_scale /= 2.0; /* max data scales to h/2 */ - /* put x-y pairs into a point list for XDraw */ - while (j--) { - int x = gra_x + (short) ((MYFLT) i++ * x_scale); - int y; - if (pts_pls == 1) - f = *fdptr++; - else { - ma = mi = *fdptr++; - for (c = 1; c < pts_pls; ++c) - if ((f = *fdptr++) > ma) ma = f; - else if ( f 0) f = ma; - else if (ma > -mi) f = ma; - else f = mi; - } - y = y_axis - (short) (f * y_scale); - fl_vertex(x, y); - } - } - fl_end_line(); - - /* now draw axes: y-axis is always on the left edge, - x-axis height is determined by the case we're in */ - fl_line(gra_x, y_axis, (gra_x + gra_w), y_axis); - fl_line(gra_x, y_axis, (gra_x + gra_w), y_axis); - - - fl_line(gra_x, gra_y, gra_x, (gra_y + gra_h)); - if (win->danflag) { /* flag to add dotted divider */ - fl_line_style(FL_DOT); - fl_line(win_x+w()/2, win_y, win_x+w()/2, win_y+win_h); - } - if (pol != NEGPOL) - sprintf(string, "%s %" PRIi32 " points, max %5.3f", msg, npts, win->oabsmax); - else - sprintf(string, "%s %" PRIi32 " points, max %5.3f", msg, npts, win->max); - - ST(form)->label(string); - } - fl_line_style(FL_SOLID); -} - - - -void add_graph(CSOUND *csound, WINDAT *wdptr) -{ - FLGRAPH_GLOBALS *flgraphGlobals = - (FLGRAPH_GLOBALS *) csound->QueryGlobalVariable(csound, - "FLGRAPH_GLOBALS"); - WINDAT *n = (WINDAT*) malloc(sizeof(WINDAT)); - int m; - WINDAT *old; - int replacing = 0; - - memcpy(n, wdptr, sizeof(WINDAT)); - n->fdata = (MYFLT*) malloc(n->npts * sizeof(MYFLT)); - memcpy(n->fdata, wdptr->fdata, n->npts * sizeof(MYFLT)); - - - for (m = 0; m < NUMOFWINDOWS; m++) { // If text the same use slot - if (ST(menu) != NULL) { - if (ST(menu)[m].text != NULL && strlen(wdptr->caption) == 0){ - if(strcmp(wdptr->caption, ST(menu)[m].text) == 0) { - replacing = 1; - goto replace; - } - } - } - } - // Use a new slot, cycling round - ST(graph)->last++; - m = ST(graph)->last; - if (m >= NUMOFWINDOWS) - m = ST(graph)->last = 0; - replace: - - old = (WINDAT*)ST(menu)[m].user_data_; - if (old) { - free((void*) old->fdata); - free((void*) old); - } - ST(menu)[m].user_data_ = n; - if (replacing == 0) { - if (ST(menu)[m].text != NULL) - free((void*) ST(menu)[m].text); - ST(menu)[m].text = (const char*) malloc(strlen(n->caption) + 1); - strcpy((char*) ST(menu)[m].text, n->caption); - } - - - // ST(graph)->curr = m; - // ST(choice)->value(m); - - ST(graph)->curr = ST(choice)->value(); /* VL: 29.04.09 fix */ - ST(graph)->redraw(); -} - -void do_redraw(Fl_Widget *, void *cs) -{ - CSOUND *csound = (CSOUND*)cs; - FLGRAPH_GLOBALS *flgraphGlobals = - (FLGRAPH_GLOBALS *) csound->QueryGlobalVariable(csound, - "FLGRAPH_GLOBALS"); - ST(graph)->curr = ST(choice)->value(); - ST(graph)->redraw(); -} - -void makeWindow(CSOUND *csound, char *name) -{ - FLGRAPH_GLOBALS *flgraphGlobals = - (FLGRAPH_GLOBALS *) csound->QueryGlobalVariable(csound, - "FLGRAPH_GLOBALS"); - if (ST(form)) - return; - - ST(form) = new Fl_Window(WIDTH, HEIGHT, name); - ST(menu) = new Fl_Menu_Item[1+NUMOFWINDOWS]; - memset(ST(menu), 0, sizeof(Fl_Menu_Item)*(1+NUMOFWINDOWS)); - ST(choice) = new Fl_Choice(140, 0, 140, 20, "Choose Graph"); - ST(choice)->menu(ST(menu)); - ST(choice)->value(0); - ST(choice)->callback((Fl_Callback*) do_redraw, (void*)csound); - ST(graph) = new graph_box(csound, - BDR, 30 + BDR, WIDTH - 2 * BDR, HEIGHT - 30 - 2 * BDR); - ST(graph)->end(); - ST(end) = new Fl_Button(WIDTH - 40, 0, 35, 20, "Quit"); - ST(end)->hide(); - ST(form)->resizable(ST(graph)); - ST(form)->end(); - ST(graph_created) = 1; - -} - -void graphs_reset(CSOUND * csound){ - IGN(csound); - //if (csound->flgraphGlobals != NULL) - // csound->Free(csound, csound->flgraphGlobals); -} - -extern "C" { - - void DrawGraph_FLTK(CSOUND *csound, WINDAT *wdptr) - { - - add_graph(csound, wdptr); - csound->CheckEvents(csound); - } - - uintptr_t MakeWindow_FLTK(CSOUND *csound, char *name) - { - FLGRAPH_GLOBALS *flgraphGlobals = - (FLGRAPH_GLOBALS *) csound->QueryGlobalVariable(csound, - "FLGRAPH_GLOBALS"); - if (ST(form) == NULL) { - makeWindow(csound, name); - ST(form)->show(); - } - - return (uintptr_t) ST(form); - } - - int CsoundYield_FLTK(CSOUND *csound){ - -#ifndef NO_FLTK_THREADS - - /* nothing to do, unless no widget thread is running */ - if (csound->QueryGlobalVariable(csound, "_widgets_globals") != NULL) - return 1; -#endif - Fl_wait_locked(csound, 0.0); - return 1; - } - - void kill_graph(CSOUND *csound, uintptr_t m) - { - FLGRAPH_GLOBALS *flgraphGlobals = - (FLGRAPH_GLOBALS *) csound->QueryGlobalVariable(csound, - "FLGRAPH_GLOBALS"); - for (int i = 0; i < NUMOFWINDOWS; i++) { - WINDAT *n = (WINDAT*) ST(menu)[i].user_data_; - if (n != NULL && ((uintptr_t) n == m ||n->windid == m)) { - free(n->fdata); - free(n); - free((void*) ST(menu)[i].text); - ST(menu)[i].user_data_ = (void*) 0; - ST(menu)[i].text = (char*) 0; - return; - } - } - } - - int ExitGraph_FLTK(CSOUND *csound) - { - FLGRAPH_GLOBALS *flgraphGlobals = - (FLGRAPH_GLOBALS *) csound->QueryGlobalVariable(csound, - "FLGRAPH_GLOBALS"); - if (flgraphGlobals == 0) { - return OK; - } - if (ST(form) && ST(graph_created) == 1) { - - if (ST(form)->shown() && !(getFLTKFlags(csound) & 256)) { - const char *env = csound->GetEnv(csound, "CSNOSTOP"); - if (env == NULL || strcmp(env, "yes") != 0) { - ST(end)->show(); - // print click-Exit message in most recently active window - while (ST(end)->value() == 0 && ST(form)->shown()) { - Fl_wait_locked(csound, 0.03); - } - } - } - - delete ST(form); - ST(form) = (Fl_Window *) 0; - Fl_wait_locked(csound, 0.0); - - ST(choice) = (Fl_Choice *) 0; - ST(graph) = (graph_box *) 0; - ST(end) = (Fl_Button *) 0; - ST(graph_created) = 0; - - for (int i = 0; i < NUMOFWINDOWS; i++) { - WINDAT *n = (WINDAT*) ST(menu)[i].user_data_; - if (n) - kill_graph(csound, (uintptr_t) ((void*) n)); - } - if (ST(menu)){ - delete[] ST(menu); - ST(menu) = (Fl_Menu_Item *) 0; - } - - - - } - - return 0; - } - -#define GUTTERH 20 /* space for text at top & bottom */ -#define BORDERW 10 /* inset from L & R edge */ - - void MakeXYin_FLTK(CSOUND *csound, XYINDAT *w, MYFLT x, MYFLT y) - { - if (w->windid == (uintptr_t) 0) { - Fl_Window *xyin = new Fl_Window(WIDTH, WIDTH, "XY input"); - short win_x, win_y; - short gra_x, gra_y, gra_w, gra_h; - - win_x = 0; win_y = 0; /* window pixels addressed relative */ - - Fl_lock(csound); - xyin->show(); - Fl_wait(csound, 0.0); - Fl_unlock(csound); - - /* set new width and height so we leave a 20% border around the plot */ - gra_w = xyin->w() - 2*BORDERW; gra_h = xyin->h() - 2*GUTTERH; - gra_x = win_x + BORDERW; gra_y = win_y + GUTTERH; - w->m_x = gra_x + (int) (x * (MYFLT) gra_w); - w->m_y = gra_y + (int) (y * (MYFLT) gra_h); - w->down = 0; - - Fl_lock(csound); - Fl_wait(csound, 0.0); - xyin->make_current(); - fl_color(0, 0, 0); - fl_line_style(FL_DOT); - fl_line(gra_x, w->m_y, (gra_x + gra_w), w->m_y); - fl_line(w->m_x, gra_y, w->m_x, (gra_y + gra_h)); - Fl_unlock(csound); - w->windid = (uintptr_t) xyin; - } - } - - void ReadXYin_FLTK(CSOUND *csound, XYINDAT *wdptr) - { - short win_x, win_y; - short gra_x, gra_y, gra_w, gra_h; - short m_x, m_y; - Fl_Window *xwin = (Fl_Window*) wdptr->windid; - - Fl_lock(csound); - Fl_wait(csound, 0.0); - m_x = Fl::event_x(); - m_y = Fl::event_y(); - wdptr->down = (short) (Fl::event_button1() ? 1 : 0); - Fl_unlock(csound); - - if (!wdptr->down) - return; - - win_x = 0; win_y = 0; /* window pixels addressed relative */ - - /* set new width and height so we leave a 20% border around the plot */ - gra_w = xwin->w() - 2*BORDERW; gra_h = xwin->h() - 2*GUTTERH; - gra_x = win_x + BORDERW; gra_y = win_y + GUTTERH; - - /* clip mouse position */ - if (m_x < gra_x) m_x = gra_x; - else if (m_x > gra_x+gra_w) m_x = gra_x+gra_w; - if (m_y < gra_y) m_y = gra_y; - else if (m_y > gra_y+gra_h) m_y = gra_y+gra_h; - - if (m_x != wdptr->m_x || m_y != wdptr->m_y) { /* only redo if changed */ - Fl_lock(csound); - xwin->make_current(); - /* undraw old crosshairs */ - fl_color(FL_BACKGROUND_COLOR); - fl_line_style(FL_SOLID); - fl_line(gra_x, wdptr->m_y, (gra_x + gra_w), wdptr->m_y); - fl_line(wdptr->m_x, gra_y, wdptr->m_x, (gra_y + gra_h)); - // Draw new - fl_color(0, 0, 0); - fl_line_style(FL_SOLID); - fl_line(gra_x, m_y, (gra_x + gra_w), m_y); - fl_line(m_x, gra_y, m_x, (gra_y + gra_h)); - Fl_unlock(csound); - wdptr->m_x = m_x; wdptr->m_y = m_y; - wdptr->x = ((MYFLT) m_x - gra_x) / (MYFLT) gra_w; - wdptr->y = ((MYFLT) m_y - gra_y) / (MYFLT) gra_h; - } - } - - void KillXYin_FLTK(CSOUND *csound, XYINDAT *wdptr) - { - IGN(csound); - delete ((Fl_Window*) wdptr->windid); - wdptr->windid = (uintptr_t) 0; - } -} diff --git a/6.x/src/widgets/virtual_keyboard/Bank.cpp b/6.x/src/widgets/virtual_keyboard/Bank.cpp deleted file mode 100644 index 44a10aaf..00000000 --- a/6.x/src/widgets/virtual_keyboard/Bank.cpp +++ /dev/null @@ -1,190 +0,0 @@ -/* - Bank.cpp: - - Copyright (C) 2006 Steven Yi - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#include "Bank.hpp" - -static char* gm[] = { - (char *)"Acoustic Grand", - (char *)"Bright Acoustic", - (char *)"Electric Grand", - (char *)"Honky-Tonk", - (char *)"Electric Piano 1", - (char *)"Electric Piano 2", - (char *)"Harpsichord", - (char *)"Clavinet", - - (char *)"Celesta", - (char *)"Glockenspiel", - (char *)"Music Box", - (char *)"Vibraphone", - (char *)"Marimba", - (char *)"Xylophone", - (char *)"Tubular Bells", - (char *)"Dulcimer", - - (char *)"Drawbar Organ", - (char *)"Percussive Organ", - (char *)"Rock Organ", - (char *)"Church Organ", - (char *)"Reed Organ", - (char *)"Accoridan", - (char *)"Harmonica", - (char *)"Tango Accordian", - - (char *)"Nylon String Guitar", - (char *)"Steel String Guitar", - (char *)"Electric Jazz Guitar", - (char *)"Electric Clean Guitar", - (char *)"Electric Muted Guitar", - (char *)"Overdriven Guitar", - (char *)"Distortion Guitar", - (char *)"Guitar Harmonics", - - (char *)"Acoustic Bass", - (char *)"Electric Bass(finger)", - (char *)"Electric Bass(pick)", - (char *)"Fretless Bass", - (char *)"Slap Bass 1", - (char *)"Slap Bass 2", - (char *)"Synth Bass 1", - (char *)"Synth Bass 2", - - (char *)"Violin", - (char *)"Viola", - (char *)"Cello", - (char *)"Contrabass", - (char *)"Tremolo Strings", - (char *)"Pizzicato Strings", - (char *)"Orchestral Strings", - (char *)"Timpani", - - (char *)"String Ensemble 1", - (char *)"String Ensemble 2", - (char *)"SynthStrings 1", - (char *)"SynthStrings 2", - (char *)"Choir Aahs", - (char *)"Voice Oohs", - (char *)"Synth Voice", - (char *)"Orchestra Hit", - - (char *)"Trumpet", - (char *)"Trombone", - (char *)"Tuba", - (char *)"Muted Trumpet", - (char *)"French Horn", - (char *)"Brass Section", - (char *)"SynthBrass 1", - (char *)"SynthBrass 2", - - (char *)"Soprano Sax", - (char *)"Alto Sax", - (char *)"Tenor Sax", - (char *)"Baritone Sax", - (char *)"Oboe", - (char *)"English Horn", - (char *)"Bassoon", - (char *)"Clarinet", - - (char *)"Piccolo", - (char *)"Flute", - (char *)"Recorder", - (char *)"Pan Flute", - (char *)"Blown Bottle", - (char *)"Skakuhachi", - (char *)"Whistle", - (char *)"Ocarina", - - (char *)"Lead 1 (square)", - (char *)"Lead 2 (sawtooth)", - (char *)"Lead 3 (calliope)", - (char *)"Lead 4 (chiff)", - (char *)"Lead 5 (charang)", - (char *)"Lead 6 (voice)", - (char *)"Lead 7 (fifths)", - (char *)"Lead 8 (bass+lead)", - - (char *)"Pad 1 (new age)", - (char *)"Pad 2 (warm)", - (char *)"Pad 3 (polysynth)", - (char *)"Pad 4 (choir)", - (char *)"Pad 5 (bowed)", - (char *)"Pad 6 (metallic)", - (char *)"Pad 7 (halo)", - (char *)"Pad 8 (sweep)", - - (char *)"FX 1 (rain)", - (char *)"FX 2 (soundtrack)", - (char *)"FX 3 (crystal)", - (char *)"FX 4 (atmosphere)", - (char *)"FX 5 (brightness)", - (char *)"FX 6 (goblins)", - (char *)"FX 7 (echoes)", - (char *)"FX 8 (sci-fi)", - - (char *)"Sitar", - (char *)"Banjo", - (char *)"Shamisen", - (char *)"Koto", - (char *)"Kalimba", - (char *)"Bagpipe", - (char *)"Fiddle", - (char *)"Shanai", - - (char *)"Tinkle Bell", - (char *)"Agogo", - (char *)"Steel Drums", - (char *)"Woodblock", - (char *)"Taiko Drum", - (char *)"Melodic Tom", - (char *)"Synth Drum", - (char *)"Reverse Cymbal", - - (char *)"Guitar Fret Noise", - (char *)"Breath Noise", - (char *)"Seashore", - (char *)"Bird Tweet", - (char *)"Telephone Ring", - (char *)"Helicopter", - (char *)"Applause", - (char *)"Gunshot" -}; - -Bank::Bank(CSOUND *csound, char* bankName) : bankNum(0) { - this->name = bankName; - IGN(csound); - currentProgram = 0; - previousProgram = -1; -} - -Bank::~Bank() { - while( !programs.empty() ) { - programs.erase( programs.begin() ); - } -} - -void Bank::initializeGM() { - for(int i = 0; i < 128; i++) { - Program temp(i, gm[i]); - this->programs.push_back(temp); - } -} diff --git a/6.x/src/widgets/virtual_keyboard/Bank.hpp b/6.x/src/widgets/virtual_keyboard/Bank.hpp deleted file mode 100644 index e6e54eb6..00000000 --- a/6.x/src/widgets/virtual_keyboard/Bank.hpp +++ /dev/null @@ -1,50 +0,0 @@ -/* - Bank.hpp: - - Copyright (C) 2006 Steven Yi - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#ifndef BANK_HPP_ -#define BANK_HPP_ - -#include -#include "csdl.h" -#include "Program.hpp" - -using namespace std; - -class Bank -{ -public: - Bank(CSOUND *csound, char *bankName); - virtual ~Bank(); - - char *name; - int bankNum; - - vector programs; - - int previousProgram; - int currentProgram; - - void initializeGM(); -}; - -#endif /*BANK_HPP_*/ diff --git a/6.x/src/widgets/virtual_keyboard/FLTKKeyboard.cpp b/6.x/src/widgets/virtual_keyboard/FLTKKeyboard.cpp deleted file mode 100644 index e56cbb53..00000000 --- a/6.x/src/widgets/virtual_keyboard/FLTKKeyboard.cpp +++ /dev/null @@ -1,547 +0,0 @@ -/* - FLTKKeyboard.cpp: - - Copyright (C) 2006 Steven Yi - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#include -#include - -#include -#include "FLTKKeyboard.hpp" - -FLTKKeyboard::FLTKKeyboard(CSOUND *csound, SliderBank *sliderBank, - int X, int Y, int W, int H, const char *L) - : Fl_Widget(X, Y, W, H, L) { - - this->csound = csound; - this->sliderBank = sliderBank; - this->mutex = csound->Create_Mutex(0); - - FLTKKeyboard *o = this; - - o->box(FL_FLAT_BOX); - o->color(FL_BACKGROUND_COLOR); - o->selection_color(FL_BACKGROUND_COLOR); - o->labeltype(FL_NO_LABEL); - o->labelfont(0); - o->labelsize(14); - o->labelcolor(FL_FOREGROUND_COLOR); - o->user_data((void*)(this)); - o->align(FL_ALIGN_TOP); - o->when(FL_WHEN_RELEASE); - - for(int i = 0; i < 88; i++) { - keyStates[i] = 0; - changedKeyStates[i] = 0; - } - - lastMidiKey = -1; - - whiteKeys[0] = 0; - whiteKeys[1] = 2; - whiteKeys[2] = 4; - whiteKeys[3] = 5; - whiteKeys[4] = 7; - whiteKeys[5] = 9; - whiteKeys[6] = 11; - - octave = 5; - - aNotesOff = 0; -} - -FLTKKeyboard::~FLTKKeyboard() { - if (mutex) { - csound->DestroyMutex(mutex); - mutex = (void*) 0; - } -} - -void FLTKKeyboard::lock() { - if(mutex) { - csound->LockMutex(mutex); - } -} - -void FLTKKeyboard::unlock() { - if(mutex) { - csound->UnlockMutex(mutex); - } -} - -void FLTKKeyboard::allNotesOff() { - this->lock(); - - for(int i = 0; i < 88; i++) { - this->keyStates[i] = -1; - } - - this->lastMidiKey = -1; - - this->aNotesOff = 1; - - this->unlock(); - - this->redraw(); -} - -int FLTKKeyboard::getMidiValForWhiteKey(int whiteKeyNum) { - if(whiteKeyNum < 2) { - return whiteKeyNum * 2; - } - - int adjusted = whiteKeyNum - 2; - - int oct = adjusted / 7; - int key = adjusted % 7; - - return 3 + (oct * 12) + whiteKeys[key]; -} - -int FLTKKeyboard::getMIDIKey(int xVal, int yVal) { - int x = xVal - this->x(); - int y = yVal - this->y(); - - if(x > this->w()) { - return 87; - } - - if(x < 0) { - return 0; - } - - int whiteKeyHeight = this->h(); - int blackKeyHeight = (int)(whiteKeyHeight * .625); - - float whiteKeyWidth = this->w() / 52.0; - float blackKeyWidth = whiteKeyWidth * .8333333; - - float leftKeyBound = blackKeyWidth / 2.0; - float rightKeyBound = whiteKeyWidth - leftKeyBound; - - - // 52 white keys - int whiteKey = (int)(x / whiteKeyWidth); - - float extra = x - (whiteKey * whiteKeyWidth); - - if(whiteKey < 2) { - if(whiteKey == 0) { - if(y > blackKeyHeight + this->y()) { - return whiteKey; - } else { - if(extra > rightKeyBound) { - return whiteKey + 1; - } - return whiteKey; - } - } else { - if(y > blackKeyHeight) { - return getMidiValForWhiteKey(whiteKey); - } else { - if(extra < leftKeyBound) { - return getMidiValForWhiteKey(whiteKey) - 1; - } - return getMidiValForWhiteKey(whiteKey); - } - } - } - - int adjustedKey = (whiteKey - 2) % 7; - - if(adjustedKey == 0 || adjustedKey == 3) { - - if(y > blackKeyHeight) { - return getMidiValForWhiteKey(whiteKey); - } else { - if(extra > rightKeyBound) { - return getMidiValForWhiteKey(whiteKey) + 1; - } - return getMidiValForWhiteKey(whiteKey); - } - } else if(adjustedKey == 2 || adjustedKey == 6) { - if(y > blackKeyHeight) { - return getMidiValForWhiteKey(whiteKey); - } else { - if(extra < leftKeyBound) { - return getMidiValForWhiteKey(whiteKey) - 1; - } - return getMidiValForWhiteKey(whiteKey); - } - } - - if(y > blackKeyHeight) { - return getMidiValForWhiteKey(whiteKey); - } - - if(extra < leftKeyBound) { - return getMidiValForWhiteKey(whiteKey) - 1; - } - - if(extra > rightKeyBound) { - return getMidiValForWhiteKey(whiteKey) + 1; - } - - return getMidiValForWhiteKey(whiteKey); - -} - -void FLTKKeyboard::handleControl(int key) { - int index; - - switch(key) { - case '1': - index = 0; - break; - case '2': - index = 1; - break; - case '3': - index = 2; - break; - case '4': - index = 3; - break; - case '5': - index = 4; - break; - case '6': - index = 5; - break; - case '7': - index = 6; - break; - case '8': - index = 7; - break; - case '9': - index = 8; - break; - case '0': - index = 9; - break; - default: - return; - } - if(Fl::event_shift()) { - sliderBank->incrementSlider(index, -1); - } else { - sliderBank->incrementSlider(index, 1); - } - return; -} - -void FLTKKeyboard::handleKey(int key, int value) { - int index = -1; - - switch(key) { - case 'z': - index = 0; - break; - case 's': - index = 1; - break; - case 'x': - index = 2; - break; - case 'd': - index = 3; - break; - case 'c': - index = 4; - break; - case 'v': - index = 5; - break; - case 'g': - index = 6; - break; - case 'b': - index = 7; - break; - case 'h': - index = 8; - break; - case 'n': - index = 9; - break; - case 'j': - index = 10; - break; - case 'm': - index = 11; - break; - case 'q': - index = 12; - break; - case '2': - index = 13; - break; - case 'w': - index = 14; - break; - case '3': - index = 15; - break; - case 'e': - index = 16; - break; - case 'r': - index = 17; - break; - case '5': - index = 18; - break; - case 't': - index = 19; - break; - case '6': - index = 20; - break; - case 'y': - index = 21; - break; - case '7': - index = 22; - break; - case 'u': - index = 23; - break; - case 'i': - index = 24; - break; - case '9': - index = 25; - break; - case 'o': - index = 26; - break; - case '0': - index = 27; - break; - case 'p': - index = 28; - break; - default: - return; - } - - if(Fl::event_shift()) { - index += 29; - } - - // This cannot happen - // if(index < 0) { - // return; - //} - - index = ((octave * 12) + index) - 21; - - if(index < 0 || index > 87) { - return; - } - - lock(); - if(keyStates[index] != value) { - keyStates[index] = value; - } - unlock(); -} - -int FLTKKeyboard::handle(int event) { - - int key; - - switch(event) { - case FL_PUSH: - if(Fl::event_button2() || Fl::event_button3()) { - return 1; - } - - key = getMIDIKey(Fl::event_x(), Fl::event_y()); - - this->lock(); - - lastMidiKey = key; - keyStates[key] = 1; - - this->unlock(); - Fl::focus(this); - this->redraw(); - - return 1; - case FL_DRAG: - if(Fl::event_button2() || Fl::event_button3()) { - return 1; - } - key = getMIDIKey(Fl::event_x(), Fl::event_y()); - - if(key != lastMidiKey) { - - this->lock(); - - keyStates[lastMidiKey] = -1; - - if(keyStates[key] != 1) { - keyStates[key] = 1; - } - - lastMidiKey = key; - - this->unlock(); - - this->redraw(); - - } - return 1; - case FL_RELEASE: - if(Fl::event_button1()) { - return 1; - } - - key = getMIDIKey(Fl::event_x(), Fl::event_y()); - - this->lock(); - - keyStates[key] = 0; - - if(lastMidiKey >= -1) { - keyStates[lastMidiKey] = -1; - } - - lastMidiKey = -1; - - this->unlock(); - - this->redraw(); - - return 1; - case FL_MOVE: - if(lastMidiKey >= 0) { - - this->lock(); - keyStates[lastMidiKey] = 0; - lastMidiKey = -1; - this->unlock(); - - } - return 1; - case FL_KEYDOWN: - //csound->Message(csound, "Key Down: Code: %d\n", Fl::event_key()); - if(Fl::event_ctrl() && sliderBank != NULL) { - handleControl(Fl::event_key()); - } else { - handleKey(Fl::event_key(), 1); - } - Fl::focus(this); - this->redraw(); - return 1; - case FL_KEYUP: - if(Fl::focus() == this) { - //csound->Message(csound, "Key Up: Code: %d\n", Fl::event_key()); - handleKey(Fl::event_key(), -1); - this->redraw(); - return 1; - } - /* FALLTHRU */ - default: - return Fl_Widget::handle(event); - } - -} - -int FLTKKeyboard::isWhiteKey(int key) { - if(key < 3) { - return !(key % 2); - } - - int adjustedKey = (key - 3) % 12; - - switch(adjustedKey) { - case 0: - case 2: - case 4: - case 5: - case 7: - case 9: - case 11: - return 1; - } - return 0; - - -} - -void FLTKKeyboard::draw() { - int i; - - int whiteKeyHeight = this->h(); - int blackKeyHeight = (int)(whiteKeyHeight * .625); - float whiteKeyWidth = this->w() / 52.0; - int blackKeyWidth = (int)(whiteKeyWidth * .8333333); - int blackKeyOffset = blackKeyWidth / 2; - - float runningX = (float)this->x(); - int yval = this->y(); - - fl_draw_box(box(), this->x(), this->y(), this->w(), this->h(), FL_WHITE); - fl_rect(this->x(), this->y(), this->w(),this->h(), FL_BLACK); - - int lineHeight = this->y() + whiteKeyHeight - 1; - - // Draw White Keys - for(i = 0; i < 88; i++) { - if(isWhiteKey(i)) { - int newX = int(runningX + 0.5); - - if(keyStates[i] == 1) { - int newW = (int(runningX + whiteKeyWidth + 0.5) - newX); - fl_draw_box(box(), newX, yval, newW, - whiteKeyHeight - 1, FL_BLUE); - } - - runningX += whiteKeyWidth; - - fl_color(FL_BLACK); - - fl_line(newX, this->y(), newX, lineHeight); - } - } - - runningX = (float)this->x(); - - // Draw Black Keys - for(i = 0; i < 88; i++) { - if(isWhiteKey(i)) { - runningX += whiteKeyWidth; - } else { - if(keyStates[i] == 1) { - fl_draw_box(box(), (int)(runningX - blackKeyOffset), yval, - blackKeyWidth, blackKeyHeight, FL_BLUE); - } else { - fl_draw_box(box(), (int)(runningX - blackKeyOffset), yval, - blackKeyWidth, blackKeyHeight, FL_BLACK); - } - fl_rect((int)(runningX - blackKeyOffset), yval, - blackKeyWidth, blackKeyHeight, FL_BLACK); - } - } -} diff --git a/6.x/src/widgets/virtual_keyboard/FLTKKeyboard.hpp b/6.x/src/widgets/virtual_keyboard/FLTKKeyboard.hpp deleted file mode 100644 index 59d9b8bf..00000000 --- a/6.x/src/widgets/virtual_keyboard/FLTKKeyboard.hpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - FLTKKeyboard.hpp: - - Copyright (C) 2006 Steven Yi - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#ifndef FLTKKEYBOARD_HPP_ -#define FLTKKEYBOARD_HPP_ - -#include -#include -#include "csdl.h" -#include "SliderBank.hpp" - -class FLTKKeyboard : public Fl_Widget { -public: - FLTKKeyboard(CSOUND *csound, SliderBank *sliderBank, - int X, int Y, int W, int H, const char *L); - ~FLTKKeyboard(); - int handle(int event); - void draw(); - int keyStates[88]; - int changedKeyStates[88]; - int whiteKeys[7]; - void lock(); - void unlock(); - void allNotesOff(); - int aNotesOff; - int octave; -private: - int getMIDIKey(int x, int y); - int lastMidiKey; - int isWhiteKey(int key); - int getMidiValForWhiteKey(int whiteKeyNum); - void handleKey(int key, int value); - void handleControl(int key); - - CSOUND *csound; - void * mutex; - SliderBank *sliderBank; -}; - -#endif /*FLTKKEYBOARD_HPP_*/ diff --git a/6.x/src/widgets/virtual_keyboard/FLTKKeyboardWidget.cpp b/6.x/src/widgets/virtual_keyboard/FLTKKeyboardWidget.cpp deleted file mode 100644 index f5a13af1..00000000 --- a/6.x/src/widgets/virtual_keyboard/FLTKKeyboardWidget.cpp +++ /dev/null @@ -1,210 +0,0 @@ -/* - FLTKKeyboardWidget.hpp: - - Copyright (C) 2006 Steven Yi - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#include "FLTKKeyboardWidget.hpp" - -static void allNotesOff(Fl_Widget *widget, void * v) { - IGN(widget); - FLTKKeyboardWidget *win = (FLTKKeyboardWidget *)v; - win->keyboard->allNotesOff(); -} - -static void channelChange(Fl_Widget *widget, void * v) { - Fl_Spinner *spinner = (Fl_Spinner *)widget; - FLTKKeyboardWidget *win = (FLTKKeyboardWidget *)v; - - win->lock(); - - int channel = (int)spinner->value() - 1; - - win->keyboardMapping->setCurrentChannel(channel); - - win->bankChoice->value(win->keyboardMapping->getCurrentBank()); - - win->setProgramNames(); - - win->unlock(); -} - -static void bankChange(Fl_Widget *widget, void * v) { - Fl_Choice *choice = (Fl_Choice *)widget; - FLTKKeyboardWidget *win = (FLTKKeyboardWidget *)v; - - win->lock(); - - win->keyboardMapping->setCurrentBank((int)choice->value()); - - win->setProgramNames(); - - win->unlock(); -} - -static void programChange(Fl_Widget *widget, void * v) { - Fl_Choice *choice = (Fl_Choice *)widget; - FLTKKeyboardWidget *win = (FLTKKeyboardWidget *)v; - - win->lock(); - - win->keyboardMapping->setCurrentProgram((int)choice->value()); - - win->unlock(); -} - -static void octaveChange(Fl_Widget *widget, void * v) { - Fl_Choice *choice = (Fl_Choice *)widget; - FLTKKeyboardWidget *win = (FLTKKeyboardWidget *)v; - - win->lock(); - - win->keyboard->octave = (int)choice->value() + 1; - - win->unlock(); -} - -FLTKKeyboardWidget::FLTKKeyboardWidget(CSOUND *csound, - const char *deviceMap, - int X, int Y, int w, int h) - : Fl_Group(X, Y, w, h) -{ - char octave[2]; - octave[1] = 0; - - this->csound = csound; - this->mutex = csound->Create_Mutex(0); - - this->keyboardMapping = new KeyboardMapping(csound, deviceMap); - - this->begin(); - - int baseX = this->x(); - int baseY = this->y(); - - int row1 = baseY; - int row2 = row1 + 20; - int row3 = row2 + 20; - - int x1 = (int)(baseX + ((60 / 754.0) * w)); - int x2 = (int)(baseX + ((180 / 754.0) * w)); - int x3 = (int)(baseX + ((420 / 754.0) * w)); - int x4 = (int)(baseX + ((670 / 754.0) * w)); - - int w1 = (int)((80 / 754.0) * w); - int w2 = (int)((180 / 754.0) * w); - int w3 = (int)((200 / 754.0) * w); - int w4 = (int)((80 / 754.0) * w); - - - //624, 120 - - this->channelSpinner = new Fl_Spinner(x1, row1, w1, 20, "Channel"); - channelSpinner->maximum(16); - channelSpinner->minimum(1); - this->channelSpinner->callback((Fl_Callback*) channelChange, this); - - this->bankChoice = new Fl_Choice(x2, row1, w2, 20, "Bank"); - this->programChoice = new Fl_Choice(x3, row1, w3, 20, "Program"); - this->octaveChoice = new Fl_Choice(x4, row1, w4, 20, "Octave"); - - bankChoice->clear(); - - for(unsigned int i = 0; i < keyboardMapping->banks.size(); i++) { - bankChoice->add(keyboardMapping->banks[i]->name); - } - - bankChoice->value(0); - - setProgramNames(); - - octaveChoice->clear(); - - for(unsigned int i = 1; i < 8; i++) { - octave[0] = i + 48; - octaveChoice->add(octave); - } - - octaveChoice->value(4); - - this->bankChoice->callback((Fl_Callback*)bankChange, this); - this->programChoice->callback((Fl_Callback*)programChange, this); - this->octaveChoice->callback((Fl_Callback*)octaveChange, this); - - this->allNotesOffButton = new Fl_Button(baseX, row2, w, 20, "All Notes Off"); - this->allNotesOffButton->callback((Fl_Callback*) allNotesOff, this); - - this->keyboard = new FLTKKeyboard(csound, NULL, baseX, row3, w, h - 40, - "Keyboard"); - - this->end(); - -} - -FLTKKeyboardWidget::~FLTKKeyboardWidget() { - if (mutex) { - csound->DestroyMutex(mutex); - mutex = (void*) 0; - } - delete keyboardMapping; -} - -void FLTKKeyboardWidget::setProgramNames() { - - Bank* bank = keyboardMapping->banks[keyboardMapping->getCurrentBank()]; - - programChoice->clear(); - - for( vector::iterator iter = bank->programs.begin(); - iter != bank->programs.end(); iter++ ) { - programChoice->add((*iter).name); - } - - programChoice->value(bank->currentProgram); -} - -int FLTKKeyboardWidget::handle(int event) { - switch(event) { - case FL_KEYDOWN: - return this->keyboard->handle(event); - case FL_KEYUP: - return this->keyboard->handle(event); -// case FL_DEACTIVATE: -// this->keyboard->allNotesOff(); -// csound->Message(csound, "Deactivate\n"); -// return 1; - default: -// this->csound->Message(this->csound, "Keyboard event: %d\n", event); - return Fl_Group::handle(event); - } - -} - -void FLTKKeyboardWidget::lock() { - if(mutex) { - csound->LockMutex(mutex); - } -} - -void FLTKKeyboardWidget::unlock() { - if(mutex) { - csound->UnlockMutex(mutex); - } -} diff --git a/6.x/src/widgets/virtual_keyboard/FLTKKeyboardWidget.hpp b/6.x/src/widgets/virtual_keyboard/FLTKKeyboardWidget.hpp deleted file mode 100644 index 442a7607..00000000 --- a/6.x/src/widgets/virtual_keyboard/FLTKKeyboardWidget.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - FLTKKeyboardWidget.hpp: - - Copyright (C) 2006 Steven Yi - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#ifndef FLTKKEYBOARDWIDGET_HPP_ -#define FLTKKEYBOARDWIDGET_HPP_ - -#include -#include -#include -#include -#include -#include "FLTKKeyboard.hpp" -#include "KeyboardMapping.hpp" -#include "csdl.h" - -class FLTKKeyboardWidget : public Fl_Group { -public: - FLTKKeyboardWidget(CSOUND * csound, const char *deviceMap, - int x, int y, int w, int h); - ~FLTKKeyboardWidget(); - - FLTKKeyboard *keyboard; - Fl_Button *allNotesOffButton; - Fl_Spinner *channelSpinner; - Fl_Choice *bankChoice; - Fl_Choice *programChoice; - Fl_Choice *octaveChoice; - - KeyboardMapping *keyboardMapping; - - int handle(int event); - - void lock(); - void unlock(); - - void setSelectedBank(); - void setProgramNames(); - -private: - CSOUND* csound; - void * mutex; - -}; - - -#endif /*FLTKKEYBOARDWIDGET_HPP_*/ diff --git a/6.x/src/widgets/virtual_keyboard/FLTKKeyboardWindow.cpp b/6.x/src/widgets/virtual_keyboard/FLTKKeyboardWindow.cpp deleted file mode 100644 index 1a9c18c0..00000000 --- a/6.x/src/widgets/virtual_keyboard/FLTKKeyboardWindow.cpp +++ /dev/null @@ -1,200 +0,0 @@ -/* - FLTKKeyboardWindow.hpp: - - Copyright (C) 2006 Steven Yi - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#include "FLTKKeyboardWindow.hpp" - -static void allNotesOff(Fl_Widget *widget, void * v) { - IGN(widget); - FLTKKeyboardWindow *win = (FLTKKeyboardWindow *)v; - win->keyboard->allNotesOff(); -} - -static void channelChange(Fl_Widget *widget, void * v) { - Fl_Spinner *spinner = (Fl_Spinner *)widget; - FLTKKeyboardWindow *win = (FLTKKeyboardWindow *)v; - - win->lock(); - - int channel = (int)spinner->value() - 1; - - win->keyboardMapping->setCurrentChannel(channel); - - win->bankChoice->value(win->keyboardMapping->getCurrentBank()); - - win->setProgramNames(); - - win->sliderBank->setChannel(channel); - - win->unlock(); -} - -static void bankChange(Fl_Widget *widget, void * v) { - Fl_Choice *choice = (Fl_Choice *)widget; - FLTKKeyboardWindow *win = (FLTKKeyboardWindow *)v; - - win->lock(); - - win->keyboardMapping->setCurrentBank((int)choice->value()); - - win->setProgramNames(); - - win->unlock(); -} - -static void programChange(Fl_Widget *widget, void * v) { - Fl_Choice *choice = (Fl_Choice *)widget; - FLTKKeyboardWindow *win = (FLTKKeyboardWindow *)v; - - win->lock(); - - win->keyboardMapping->setCurrentProgram((int)choice->value()); - - win->unlock(); -} - -static void octaveChange(Fl_Widget *widget, void * v) { - Fl_Choice *choice = (Fl_Choice *)widget; - FLTKKeyboardWindow *win = (FLTKKeyboardWindow *)v; - - win->lock(); - - win->keyboard->octave = (int)choice->value() + 1; - - win->unlock(); -} - -FLTKKeyboardWindow::FLTKKeyboardWindow(CSOUND *csound, - const char *deviceMap, - int W, int H, const char *L = 0) - : Fl_Double_Window(W, H, L) -{ - char octave[2]; - octave[1] = 0; - - this->csound = csound; - this->mutex = csound->Create_Mutex(0); - - this->keyboardMapping = new KeyboardMapping(csound, deviceMap); - - this->begin(); - - int row1 = 0; - int row2 = row1 + 150; - int row3 = row2 + 20; - int row4 = row3 + 20; - - this->sliderBank = new SliderBank(csound, 0, row1, W, 150); - - this->channelSpinner = new Fl_Spinner(60, row2, 80, 20, "Channel"); - channelSpinner->maximum(16); - channelSpinner->minimum(1); - this->channelSpinner->callback((Fl_Callback*) channelChange, this); - - this->bankChoice = new Fl_Choice(180, row2, 180, 20, "Bank"); - this->programChoice = new Fl_Choice(420, row2, 200, 20, "Program"); - this->octaveChoice = new Fl_Choice(670, row2, 80, 20, "Octave"); - - bankChoice->clear(); - - for(unsigned int i = 0; i < keyboardMapping->banks.size(); i++) { - bankChoice->add(keyboardMapping->banks[i]->name); - } - - bankChoice->value(0); - - setProgramNames(); - - octaveChoice->clear(); - - for(unsigned int i = 1; i < 8; i++) { - octave[0] = i + 48; - octaveChoice->add(octave); - } - - octaveChoice->value(4); - - this->bankChoice->callback((Fl_Callback*)bankChange, this); - this->programChoice->callback((Fl_Callback*)programChange, this); - this->octaveChoice->callback((Fl_Callback*)octaveChange, this); - - this->allNotesOffButton = new Fl_Button(0, row3, W, 20, "All Notes Off"); - this->allNotesOffButton->callback((Fl_Callback*) allNotesOff, this); - - this->keyboard = new FLTKKeyboard(csound, this->sliderBank, 0, row4, W, 80, - "Keyboard"); - - this->end(); - -} - -FLTKKeyboardWindow::~FLTKKeyboardWindow() { - if (mutex) { - csound->DestroyMutex(mutex); - mutex = (void*) 0; - } - delete keyboardMapping; -} - -void FLTKKeyboardWindow::setProgramNames() { - - Bank* bank = keyboardMapping->banks[keyboardMapping->getCurrentBank()]; - - programChoice->clear(); - - for( vector::iterator iter = bank->programs.begin(); - iter != bank->programs.end(); iter++ ) { - programChoice->add((*iter).name); - } - - programChoice->value(bank->currentProgram); -} - -int FLTKKeyboardWindow::handle(int event) { - //this->csound->Message(this->csound, "Keyboard event: %d\n", event); - - switch(event) { - case FL_KEYDOWN: - return this->keyboard->handle(event); - case FL_KEYUP: - return this->keyboard->handle(event); -// case FL_DEACTIVATE: -// this->keyboard->allNotesOff(); -// csound->Message(csound, "Deactivate\n"); -// return 1; - default: - return Fl_Double_Window::handle(event); - } - -} - -void FLTKKeyboardWindow::lock() { - if(mutex) { - csound->LockMutex(mutex); - } -} - -void FLTKKeyboardWindow::unlock() { - if(mutex) { - csound->UnlockMutex(mutex); - } -} diff --git a/6.x/src/widgets/virtual_keyboard/FLTKKeyboardWindow.hpp b/6.x/src/widgets/virtual_keyboard/FLTKKeyboardWindow.hpp deleted file mode 100644 index 7c5ce910..00000000 --- a/6.x/src/widgets/virtual_keyboard/FLTKKeyboardWindow.hpp +++ /dev/null @@ -1,68 +0,0 @@ -/* - FLTKKeyboardWindow.hpp: - - Copyright (C) 2006 Steven Yi - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#ifndef FLTKKEYBOARDWINDOW_HPP_ -#define FLTKKEYBOARDWINDOW_HPP_ - -#include -#include -#include -#include -#include -#include "FLTKKeyboard.hpp" -#include "KeyboardMapping.hpp" -#include "SliderBank.hpp" -#include "csdl.h" - -class FLTKKeyboardWindow : public Fl_Double_Window { -public: - FLTKKeyboardWindow(CSOUND * csound, const char *deviceMap, - int w, int h, const char* t); - ~FLTKKeyboardWindow(); - - FLTKKeyboard *keyboard; - Fl_Button *allNotesOffButton; - Fl_Spinner *channelSpinner; - Fl_Choice *bankChoice; - Fl_Choice *programChoice; - Fl_Choice *octaveChoice; - - KeyboardMapping *keyboardMapping; - SliderBank *sliderBank; - - int handle(int event); - - void lock(); - void unlock(); - - void setSelectedBank(); - void setProgramNames(); - -private: - CSOUND* csound; - void * mutex; - -}; - - -#endif /*FLTKKEYBOARDWINDOW_HPP_*/ diff --git a/6.x/src/widgets/virtual_keyboard/KeyboardMapping.cpp b/6.x/src/widgets/virtual_keyboard/KeyboardMapping.cpp deleted file mode 100644 index a188da72..00000000 --- a/6.x/src/widgets/virtual_keyboard/KeyboardMapping.cpp +++ /dev/null @@ -1,233 +0,0 @@ -/* - KeyboardMapping.cpp: - - Copyright (C) 2006 Steven Yi - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#include "KeyboardMapping.hpp" -#include -#include - -#define LINELENGTH (300) - -static char *my_fgets(char *s, int n, FILE *stream) -{ - char *a = s; - if (n <= 1) return NULL; /* best of a bad deal */ - do { - int ch = getc(stream); - if (ch == EOF) { /* error or EOF */ - if (s == a) return NULL; /* no chars -> leave */ - if (ferror(stream)) a = NULL; - break; /* add NULL even if ferror(), spec says 'indeterminate' */ - } - if (ch == '\n' || ch == '\r') { /* end of line ? */ - *(s++) = '\n'; /* convert */ - if (ch == '\r') { - ch = getc(stream); - if (ch != '\n') /* Mac format */ - ungetc(ch, stream); - } - break; - } - *(s++) = ch; - } while (--n > 1); - *s = '\0'; - return a; -} - - -KeyboardMapping::KeyboardMapping(CSOUND *csound, const char *mapFileName) -{ - FILE *mapFile; - - char *mapf = strdup(mapFileName); - - void *fd = csound->FileOpen2(csound, &mapFile, CSFILE_STD, mapf, - (void *)"r", "INCDIR", CSFTYPE_OTHER_TEXT, 0); - - if (fd == NULL) { - initializeDefaults(csound); - } else { - initializeMap(csound, mapFile); - csound->FileClose(csound, fd); - } - - - - currentChannel = 0; - previousChannel = 0; - - - for(int i = 0; i < 16; i++) { - currentBank[i] = 0; - previousBank[i] = -1; - } - previousProgram = -1; -} - -KeyboardMapping::~KeyboardMapping() -{ - for(unsigned int i = 0; i < banks.size(); i++) { - delete banks[i]; - } -} - -void KeyboardMapping::initializeMap(CSOUND * csound, FILE *file) { - char buff[LINELENGTH]; - char *p; - - /* 0 = general search - * 1 = bank found but error in bank def, ignore all until next bank - */ - int state = 0; - - Bank *bank = NULL; - - while (my_fgets(buff, LINELENGTH, file)!=NULL) { - p = buff; - while (*p == ' ' || *p == '\t') p++; - - if(*p == '#') { // line comment - continue; - } if(*p == '[') { - p++; - - // cleanup current bank - if(bank != NULL) { - if(bank->programs.size() == 0) { - bank->initializeGM(); - } - } - - char *q = strstr(p, "="); - char *z = strstr(p, "]"); - - if(q == NULL || z == NULL) { - state = 1; - continue; - } - - *q = '\0'; - *z = '\0'; - - int bankNum = atoi(p) - 1; - - char *name = (char *)csound->Calloc(csound, strlen(q +1) + 1); - memcpy(name, q + 1, strlen(q + 1) + 1); - - if(bankNum >= 0 && bankNum < 16384) { - bank = new Bank(csound, name); - bank->bankNum = bankNum; - banks.push_back(bank); - - //bank->initializeGM(); - state = 0; - } else { - state = 1; - } - - } else { - if(state == 1 || bank == NULL) { - continue; - } - - char *q = strstr(p, "="); - - if(q == NULL) { - continue; - } - - *q = '\0'; - - int programNum = atoi(p) - 1; - - char *name = (char *)csound->Calloc(csound, strlen(q +1) + 1); - memcpy(name, q + 1, strlen(q + 1) + 1); - - if(programNum >= 0 && programNum < 128) { - Program program(programNum, name); - bank->programs.push_back(program); - } - } - } - -} - -void KeyboardMapping::initializeDefaults(CSOUND *csound) { - for(int i = 0; i < 128; i++) { - - char * name = (char *)csound->Calloc(csound, 9); - - sprintf(name, "Bank %d", i + 1); - - Bank *temp = new Bank(csound, name); - temp->initializeGM(); - - banks.push_back(temp); - - } -} - -int KeyboardMapping::getCurrentChannel() { - return currentChannel; -} - -int KeyboardMapping::getCurrentBank() { - return currentBank[currentChannel]; -} - -int KeyboardMapping::getPreviousBank() { - return previousBank[currentChannel]; -} - -int KeyboardMapping::getCurrentProgram() { - return banks[getCurrentBank()]->currentProgram; -} - -int KeyboardMapping::getPreviousProgram() { - return banks[getCurrentBank()]->previousProgram; -} - - -void KeyboardMapping::setCurrentChannel(int index) { - currentChannel = index; -} - -void KeyboardMapping::setCurrentBank(int index) { - currentBank[currentChannel] = index; -} - -void KeyboardMapping::setPreviousBank(int index) { - previousBank[currentChannel] = index; -} - -void KeyboardMapping::setCurrentProgram(int index) { - banks[currentBank[currentChannel]]->currentProgram = index; -} - -void KeyboardMapping::setPreviousProgram(int index) { - banks[currentBank[currentChannel]]->previousProgram = index; -} - - -int KeyboardMapping::getCurrentBankMIDINumber() { - return banks[getCurrentBank()]->bankNum; -} diff --git a/6.x/src/widgets/virtual_keyboard/KeyboardMapping.hpp b/6.x/src/widgets/virtual_keyboard/KeyboardMapping.hpp deleted file mode 100644 index 6f64c959..00000000 --- a/6.x/src/widgets/virtual_keyboard/KeyboardMapping.hpp +++ /dev/null @@ -1,67 +0,0 @@ -/* - KeyboardMapping.hpp: - - Copyright (C) 2006 Steven Yi - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#ifndef KEYBOARDMAPPING_HPP_ -#define KEYBOARDMAPPING_HPP_ - -#include "csdl.h" -#include "Bank.hpp" -#include -#include -#include - -using namespace std; - -class KeyboardMapping { -public: - KeyboardMapping(CSOUND *csound, const char *mapFileName); - ~KeyboardMapping(); - vector banks; - - int getCurrentChannel(); - int getCurrentBank(); - int getPreviousBank(); - int getCurrentProgram(); - int getPreviousProgram(); - - void setCurrentChannel(int index); - void setCurrentBank(int index); - void setPreviousBank(int index); - void setCurrentProgram(int index); - void setPreviousProgram(int index); - - int getCurrentBankMIDINumber(); - - int previousChannel; - int previousProgram; - -private: - void initializeDefaults(CSOUND *); - void initializeMap(CSOUND *, FILE *); - - int currentChannel; - int previousBank[16]; - int currentBank[16]; -}; - -#endif /*KEYBOARDMAPPING_HPP_*/ diff --git a/6.x/src/widgets/virtual_keyboard/Program.cpp b/6.x/src/widgets/virtual_keyboard/Program.cpp deleted file mode 100644 index 6292cef4..00000000 --- a/6.x/src/widgets/virtual_keyboard/Program.cpp +++ /dev/null @@ -1,31 +0,0 @@ -/* - Program.cpp: - - Copyright (C) 2006 Steven Yi - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#include "Program.hpp" - -Program::Program(int num, char *programName) -{ - this->programNum = num; - this->name = programName; -} - diff --git a/6.x/src/widgets/virtual_keyboard/Program.hpp b/6.x/src/widgets/virtual_keyboard/Program.hpp deleted file mode 100644 index ccf6f612..00000000 --- a/6.x/src/widgets/virtual_keyboard/Program.hpp +++ /dev/null @@ -1,35 +0,0 @@ -/* - Program.hpp: - - Copyright (C) 2006 Steven Yi - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#ifndef PROGRAM_HPP_ -#define PROGRAM_HPP_ - -class Program -{ -public: - Program(int num, char * programName); - int programNum; - char * name; -}; - -#endif /*PROGRAM_HPP_*/ diff --git a/6.x/src/widgets/virtual_keyboard/SliderBank.cpp b/6.x/src/widgets/virtual_keyboard/SliderBank.cpp deleted file mode 100644 index 921ea481..00000000 --- a/6.x/src/widgets/virtual_keyboard/SliderBank.cpp +++ /dev/null @@ -1,154 +0,0 @@ -/* - SliderBank.cpp: - - Copyright (C) 2006 Steven Yi - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#include "SliderBank.hpp" - -#include -#include - -int WheelSlider::handle(int event) { - int res = Fl_Value_Slider::handle(event); - if (event == FL_MOUSEWHEEL) { - int dy = Fl::event_dy(); - value(clamp(round(increment(value(), -dy)))); - return 1; - } - return res; -} - -static void spinnerCallback(Fl_Widget *widget, void *v) { - Fl_Spinner *spinner = (Fl_Spinner *)widget; - SliderBank *sliderBank = (SliderBank *)v; - - for(int i = 0; i < 10; i++) { - if(sliderBank->spinners[i] == spinner) { - sliderBank->lock(); - SliderData* data = sliderBank->getSliderData(); - data->controllerNumber[i] = (int)spinner->value(); - sliderBank->unlock(); - } - } -} - -static void sliderCallback(Fl_Widget *widget, void *v) { - Fl_Slider *slider = (Fl_Slider *)widget; - SliderBank *sliderBank = (SliderBank *)v; - - for(int i = 0; i < 10; i++) { - if(sliderBank->sliders[i] == slider) { - sliderBank->lock(); - SliderData* data = sliderBank->getSliderData(); - data->controllerValue[i] = (int)slider->value(); - sliderBank->unlock(); - } - } -} - -SliderBank::SliderBank(CSOUND *csound, - int X, int Y, int W, int H) - : Fl_Group(X, Y, W, H) -{ - - this->csound = csound; - this->mutex = csound->Create_Mutex(0); - - this->channel = 0; - - this->begin(); - - for(int i = 0; i < 10; i++) { - int x, y; - - if(i < 5) { - x = 10; - y = 10 + (i * 25); - } else { - x = 382; - y = 10 + ((i - 5) * 25); - } - - Fl_Spinner *spinner = new Fl_Spinner(x, y, 60, 20); - spinners[i] = spinner; - spinner->maximum(127); - spinner->minimum(0); - spinner->step(1); - spinner->value(i + 1); - spinner->callback((Fl_Callback*)spinnerCallback, this); - - - WheelSlider *slider = new WheelSlider(x + 70, y, 292, 20); - sliders[i] = slider; - slider->type(FL_HOR_SLIDER); - slider->maximum(127); - slider->minimum(0); - slider->step(1); - slider->value(0); - slider->callback((Fl_Callback*)sliderCallback, this); - } - - - this->end(); - -} - -SliderBank::~SliderBank() -{ - if (mutex) { - csound->DestroyMutex(mutex); - mutex = (void*) 0; - } -} - -void SliderBank::setChannel(int channel) { - this->channel = channel; - - SliderData data = sliderData[channel]; - - lock(); - for(int i = 0; i < 10; i++) { - spinners[i]->value(data.controllerNumber[i]); - sliders[i]->value(data.controllerValue[i]); - } - unlock(); -} - -SliderData * SliderBank::getSliderData() { - return &sliderData[channel]; -} - -void SliderBank::incrementSlider(int index, int n) { - Fl_Slider* slider = sliders[index]; - slider->value(slider->clamp(slider->round(slider->increment(slider->value(), n)))); -} - -void SliderBank::lock() { - if(mutex) { - csound->LockMutex(mutex); - } -} - -void SliderBank::unlock() { - if(mutex) { - csound->UnlockMutex(mutex); - } -} diff --git a/6.x/src/widgets/virtual_keyboard/SliderBank.hpp b/6.x/src/widgets/virtual_keyboard/SliderBank.hpp deleted file mode 100644 index b9e3c914..00000000 --- a/6.x/src/widgets/virtual_keyboard/SliderBank.hpp +++ /dev/null @@ -1,66 +0,0 @@ -/* - SliderBank.hpp: - - Copyright (C) 2006 Steven Yi - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#ifndef SLIDERBANK_HPP_ -#define SLIDERBANK_HPP_ - -#include -#include -#include -#include -#include "csdl.h" -#include "SliderData.hpp" - -class WheelSlider: public Fl_Value_Slider -{ - int handle(int); -public: - WheelSlider(int x, int y, int w, int h, const char *l=0) : - Fl_Value_Slider (x,y,w,h,l) {} -}; - -class SliderBank : public Fl_Group -{ -public: - SliderBank(CSOUND *csound, int X, int Y, int W, int H); - virtual ~SliderBank(); - - CSOUND *csound; - void * mutex; - - void setChannel(int channel); - SliderData *getSliderData(); - - void incrementSlider(int index, int n); - - void lock(); - void unlock(); - - WheelSlider* sliders[10]; - Fl_Spinner* spinners[10]; - -private: - int channel; - SliderData sliderData[16]; -}; -#endif /*SLIDERBANK_HPP_*/ diff --git a/6.x/src/widgets/virtual_keyboard/SliderData.cpp b/6.x/src/widgets/virtual_keyboard/SliderData.cpp deleted file mode 100644 index a8e61423..00000000 --- a/6.x/src/widgets/virtual_keyboard/SliderData.cpp +++ /dev/null @@ -1,38 +0,0 @@ -/* - SliderData.hpp: - - Copyright (C) 2006 Steven Yi - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#include "SliderData.hpp" - -SliderData::SliderData() -{ - for(int i = 0; i < 10; i++) { - controllerNumber[i] = (i + 1); - previousControllerNumber[i] = -1; - controllerValue[i] = 0; - previousControllerValue[i] = -1; - } -} - -SliderData::~SliderData() -{ -} diff --git a/6.x/src/widgets/virtual_keyboard/SliderData.hpp b/6.x/src/widgets/virtual_keyboard/SliderData.hpp deleted file mode 100644 index 22b699be..00000000 --- a/6.x/src/widgets/virtual_keyboard/SliderData.hpp +++ /dev/null @@ -1,39 +0,0 @@ -/* - SliderData.hpp: - - Copyright (C) 2006 Steven Yi - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#ifndef SLIDERDATA_HPP_ -#define SLIDERDATA_HPP_ - -class SliderData -{ -public: - SliderData(); - virtual ~SliderData(); - - int controllerNumber[10]; - int previousControllerNumber[10]; - int controllerValue[10]; - int previousControllerValue[10]; -}; - -#endif /*SLIDERDATA_HPP_*/ diff --git a/6.x/src/widgets/virtual_keyboard/keyboard.map b/6.x/src/widgets/virtual_keyboard/keyboard.map deleted file mode 100644 index 1bd80658..00000000 --- a/6.x/src/widgets/virtual_keyboard/keyboard.map +++ /dev/null @@ -1,41 +0,0 @@ -# Custom Keyboard Map for Virtual Keyboard -# Steven Yi -# -# USAGE -# -# When using the Virtual Keyboard, you can supply a filename for a mapping -# of banks and programs via the -M flag, for example: -# -# csound -+rtmidi=virtual -Mkeyboard.map my_project.csd -# -# INFORMATION ON THE FORMAT -# -# -lines that start with '#' are comments -# -lines that have [] start new bank definitions, -# the contents are bankNum=bankName, with bankNum=[1,16384] -# -lines following bank statements are program definitions -# in the format programNum=programName, with programNum=[1,128] -# -bankNumbers and programNumbers are defined in this file -# starting with 1, but are converted to midi values (starting -# with 0) when read -# -# NOTES -# -# -if an invalid bank definition is found, all program -# defintions that follow will be ignored until a new -# valid bank definition is found -# -if a valid bank is defined by no valid programs found -# for that bank, it will default to General MIDI program -# definitions -# -if an invalid program definition is found, it will be -# ignored - -[1=My Bank] -1=My Test Patch 1 -2=My Test Patch 2 -30=My Test Patch 30 - -[2=My Bank2] -1=My Test Patch 1(bank2) -2=My Test Patch 2(bank2) -30=My Test Patch 30(bank3) diff --git a/6.x/src/widgets/virtual_keyboard/virtual_keyboard.cpp b/6.x/src/widgets/virtual_keyboard/virtual_keyboard.cpp deleted file mode 100644 index 69176a73..00000000 --- a/6.x/src/widgets/virtual_keyboard/virtual_keyboard.cpp +++ /dev/null @@ -1,473 +0,0 @@ -/* - virtual_keyboard.cpp: - - Copyright (C) 2006 Steven Yi - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#include "csdl.h" -#include "winFLTK.h" -#include "FLTKKeyboardWindow.hpp" -#include "FLTKKeyboardWidget.hpp" -#include "KeyboardMapping.hpp" -#include "SliderData.hpp" -#include - -static std::map keyboardWidgets; - -static FLTKKeyboardWindow *createWindow(CSOUND *csound, const char *dev) { - return new FLTKKeyboardWindow(csound, dev, - 754, 270, "Csound Virtual Keyboard"); -} - -static FLTKKeyboardWidget *createWidget(CSOUND *csound, const char *dev, - int w, int h, int x, int y) { - return new FLTKKeyboardWidget(csound, dev, x, y, w, h); -} - -static void deleteWindow(CSOUND *csound, FLTKKeyboardWindow * keyWin) { - if(keyWin == NULL) { - return; - } - Fl_lock(csound); - - keyWin->hide(); - delete keyWin; - - Fl_awake(csound); - Fl_wait(csound, 0.0); - Fl_unlock(csound); -} - -extern "C" -{ - -typedef struct { - OPDS h; - STRINGDAT *mapFileName; - MYFLT *iwidth, *iheight, *ix, *iy; -} FLVKEYBD; - - -static int OpenMidiInDevice_(CSOUND *csound, void **userData, const char *dev) -{ - - if(keyboardWidgets.find(csound) != keyboardWidgets.end()) { - return 0; - } - - FLTKKeyboardWindow *keyboard = createWindow(csound, dev); - *userData = (void *)keyboard; - - Fl_lock(csound); - keyboard->show(); - Fl_wait(csound, 0.0); - Fl_unlock(csound); - - /* report success */ - return 0; -} - -static int OpenMidiOutDevice_(CSOUND *csound, void **userData, const char *dev) -{ - IGN(csound); IGN(userData); IGN(dev); - /* report success */ - return 0; -} - -static int ReadMidiWindow(CSOUND *csound, FLTKKeyboardWindow *keyWin, - unsigned char *mbuf, int nbytes) -{ - IGN(nbytes); - int i; - Fl_lock(csound); - Fl_awake(csound); - Fl_wait(csound, 0.0); - Fl_unlock(csound); - - if(!keyWin->visible()) { - return 0; - } - - int count = 0; - - - keyWin->lock(); - - KeyboardMapping* keyboardMapping = keyWin->keyboardMapping; - - int channel = keyboardMapping->getCurrentChannel(); - - if(keyboardMapping->getCurrentBank() != - keyboardMapping->getPreviousBank()) { - - int bankNum = keyboardMapping->getCurrentBankMIDINumber(); - unsigned char msb = (unsigned char)(bankNum >> 7) & - (unsigned char)0x7F; - unsigned char lsb = (unsigned char)bankNum & - (unsigned char)0x7F; - - *mbuf++ = (unsigned char)(0xB0 + channel); // MSB - *mbuf++ = (unsigned char)0; - *mbuf++ = msb; - - *mbuf++ = (unsigned char)(0xB0 + channel); // LSB - *mbuf++ = (unsigned char)32; - *mbuf++ = lsb; - - *mbuf++ = (unsigned char)(0xC0 + channel); // Program Change - *mbuf++ = (unsigned char)keyboardMapping->getCurrentProgram(); - - count += 8; - - keyboardMapping->setPreviousBank(keyboardMapping->getCurrentBank()); - keyboardMapping->setPreviousProgram(keyboardMapping->getCurrentProgram()); - - } else if(keyboardMapping->getCurrentProgram() != - keyboardMapping->getPreviousProgram()) { - - *mbuf++ = (unsigned char)(0xC0 + channel); // Program Change - *mbuf++ = (unsigned char)keyboardMapping->getCurrentProgram(); - - count += 2; - - keyboardMapping->setPreviousProgram(keyboardMapping->getCurrentProgram()); - } - - keyWin->sliderBank->lock(); - SliderData *sliderData = keyWin->sliderBank->getSliderData(); - - for(i = 0; i < 10; i++) { - if(sliderData->controllerNumber[i] != - sliderData->previousControllerNumber[i]) { - - *mbuf++ = (unsigned char)(0xB0 + channel); - *mbuf++ = (unsigned char)sliderData->controllerNumber[i]; - *mbuf++ = (unsigned char)sliderData->controllerValue[i]; - - count += 3; - - sliderData->previousControllerNumber[i] = - sliderData->controllerNumber[i]; - - sliderData->previousControllerValue[i] = - sliderData->controllerValue[i]; - - - } else if(sliderData->controllerValue[i] != - sliderData->previousControllerValue[i]) { - - *mbuf++ = (unsigned char)(0xB0 + channel); - *mbuf++ = (unsigned char)sliderData->controllerNumber[i]; - *mbuf++ = (unsigned char)sliderData->controllerValue[i]; - - count += 3; - - sliderData->previousControllerValue[i] = - sliderData->controllerValue[i]; - } - - } - - keyWin->sliderBank->unlock(); - keyWin->unlock(); - - - keyWin->keyboard->lock(); - - int *changedKeyStates = keyWin->keyboard->changedKeyStates; - int *keyStates = keyWin->keyboard->keyStates; - - - - for(i = 0; i < 88; i++) { - if(keyStates[i] == -1) { - *mbuf++ = (unsigned char)0x90 + channel; - *mbuf++ = (unsigned char)i + 21; - *mbuf++ = (unsigned char)0; - - count += 3; - keyStates[i] = 0; - } else if(changedKeyStates[i] != keyStates[i]) { - - if(keyStates[i] == 1) { - *mbuf++ = (unsigned char)0x90 + channel; - *mbuf++ = (unsigned char)i + 21; - *mbuf++ = (unsigned char)127; - - count += 3; - - } else { - *mbuf++ = (unsigned char)0x90 + channel; - *mbuf++ = (unsigned char)i + 21; - *mbuf++ = (unsigned char)0; - - count += 3; - } - } - - changedKeyStates[i] = keyStates[i]; - } - - if(keyWin->keyboard->aNotesOff == 1) { - keyWin->keyboard->aNotesOff = 0; - *mbuf++ = (unsigned char)0xB0; - *mbuf++ = (unsigned char)123; - *mbuf++ = (unsigned char)0; - - count += 3; - } - - keyWin->keyboard->unlock(); - - return count; -} - -static int ReadMidiWidget(CSOUND *csound, FLTKKeyboardWidget *widget, - unsigned char *mbuf, int nbytes) -{ - - IGN(csound); - IGN(nbytes); - int i; - - if(!widget->visible()) { - return 0; - } - - int count = 0; - - widget->lock(); - - KeyboardMapping* keyboardMapping = widget->keyboardMapping; - - int channel = keyboardMapping->getCurrentChannel(); - - if(keyboardMapping->getCurrentBank() != - keyboardMapping->getPreviousBank()) { - - int bankNum = keyboardMapping->getCurrentBankMIDINumber(); - unsigned char msb = (unsigned char)(bankNum >> 7) & - (unsigned char)0x7F; - unsigned char lsb = (unsigned char)bankNum & - (unsigned char)0x7F; - - *mbuf++ = (unsigned char)(0xB0 + channel); // MSB - *mbuf++ = (unsigned char)0; - *mbuf++ = msb; - - *mbuf++ = (unsigned char)(0xB0 + channel); // LSB - *mbuf++ = (unsigned char)32; - *mbuf++ = lsb; - - *mbuf++ = (unsigned char)(0xC0 + channel); // Program Change - *mbuf++ = (unsigned char)keyboardMapping->getCurrentProgram(); - - count += 8; - - keyboardMapping->setPreviousBank(keyboardMapping->getCurrentBank()); - keyboardMapping->setPreviousProgram(keyboardMapping->getCurrentProgram()); - - } else if(keyboardMapping->getCurrentProgram() != - keyboardMapping->getPreviousProgram()) { - - *mbuf++ = (unsigned char)(0xC0 + channel); // Program Change - *mbuf++ = (unsigned char)keyboardMapping->getCurrentProgram(); - - count += 2; - - keyboardMapping->setPreviousProgram(keyboardMapping->getCurrentProgram()); - } - - widget->unlock(); - - widget->keyboard->lock(); - - int *changedKeyStates = widget->keyboard->changedKeyStates; - int *keyStates = widget->keyboard->keyStates; - - - - for(i = 0; i < 88; i++) { - if(keyStates[i] == -1) { - *mbuf++ = (unsigned char)0x90 + channel; - *mbuf++ = (unsigned char)i + 21; - *mbuf++ = (unsigned char)0; - - count += 3; - keyStates[i] = 0; - } else if(changedKeyStates[i] != keyStates[i]) { - - if(keyStates[i] == 1) { - *mbuf++ = (unsigned char)0x90 + channel; - *mbuf++ = (unsigned char)i + 21; - *mbuf++ = (unsigned char)127; - - count += 3; - - } else { - *mbuf++ = (unsigned char)0x90 + channel; - *mbuf++ = (unsigned char)i + 21; - *mbuf++ = (unsigned char)0; - - count += 3; - } - } - - changedKeyStates[i] = keyStates[i]; - } - - if(widget->keyboard->aNotesOff == 1) { - widget->keyboard->aNotesOff = 0; - *mbuf++ = (unsigned char)0xB0; - *mbuf++ = (unsigned char)123; - *mbuf++ = (unsigned char)0; - - count += 3; - } - - widget->keyboard->unlock(); - - return count; -} - -static int ReadMidiData_(CSOUND *csound, void *userData, - unsigned char *mbuf, int nbytes) -{ - - if(keyboardWidgets.find(csound) == keyboardWidgets.end()) { - return ReadMidiWindow(csound, - (FLTKKeyboardWindow *)userData, mbuf, nbytes); - } - - return ReadMidiWidget(csound, - keyboardWidgets[csound], mbuf, nbytes); -} - -static int WriteMidiData_(CSOUND *csound, void *userData, - const unsigned char *mbuf, int nbytes) -{ - IGN(csound);IGN(userData);IGN(mbuf);IGN(nbytes); - /* return the number of bytes written */ - return 0; -} - -static int CloseMidiInDevice_(CSOUND *csound, void *userData) -{ - - deleteWindow(csound, (FLTKKeyboardWindow *)userData); - - return 0; -} - -static int CloseMidiOutDevice_(CSOUND *csound, void *userData) -{ - IGN(csound);IGN(userData); - return 0; -} - -/* FLvkeybd Opcode */ - -static int fl_vkeybd(CSOUND *csound, FLVKEYBD *p) { - if(keyboardWidgets.find(csound) != keyboardWidgets.end()) { - csound->ErrorMsg(csound, - "FLvkeybd may only be used once in a project.\n"); - return -1; - } - - char *mapFileName = new char[MAXNAME]; - - strncpy(mapFileName, p->mapFileName->data, MAXNAME-1); - - FLTKKeyboardWidget *widget = createWidget(csound, mapFileName, - (int)*p->iwidth, (int)*p->iheight, (int)*p->ix, (int)*p->iy); - - keyboardWidgets[csound] = widget; - - delete[] mapFileName; - return OK; -} - -#define S(x) sizeof(x) - -const OENTRY widgetOpcodes_[] = { - { (char*)"FLvkeybd", S(FLVKEYBD), 0, 1, (char*)"", (char*)"Siiii", - (SUBR) fl_vkeybd, (SUBR) NULL, (SUBR) NULL }, - { NULL, 0, 0, 0, NULL, NULL, (SUBR) NULL, (SUBR) NULL,(SUBR) NULL } -}; - - -/* module interface functions */ - -PUBLIC int csoundModuleCreate(CSOUND *csound) -{ - IGN(csound); - /* nothing to do, report success */ - //csound->Message(csound, "virtual_keyboard real time MIDI plugin for Csound\n"); - return 0; -} - -PUBLIC int csoundModuleInit(CSOUND *csound) -{ - char *drv; - const OENTRY *ep = &(widgetOpcodes_[0]); - - if (csound->QueryGlobalVariable(csound, - "FLTK_Flags") == (void*) 0) { - if (csound->CreateGlobalVariable(csound, - "FLTK_Flags", sizeof(int)) != 0) - csound->Die(csound, "%s", - Str("virtual_keyboard.cpp: error allocating FLTK flags")); - } - - for ( ; ep->opname != NULL; ep++) { - if (csound->AppendOpcode(csound, ep->opname, - (int)ep->dsblksiz, (int)ep->flags, - (int)ep->thread, ep->outypes, ep->intypes, - ep->iopadr, ep->kopadr, ep->aopadr) != 0) { - csound->ErrorMsg(csound, Str("Error registering opcode '%s'"), - ep->opname); - return -1; - } - } - - drv = (char*) (csound->QueryGlobalVariable(csound, "_RTMIDI")); - if (drv == NULL) - return 0; - if (!(strcmp(drv, "virtual") == 0)) - return 0; - csound->Message(csound, "rtmidi: virtual_keyboard module enabled\n"); - csound->SetExternalMidiInOpenCallback(csound, OpenMidiInDevice_); - csound->SetExternalMidiReadCallback(csound, ReadMidiData_); - csound->SetExternalMidiInCloseCallback(csound, CloseMidiInDevice_); - csound->SetExternalMidiOutOpenCallback(csound, OpenMidiOutDevice_); - csound->SetExternalMidiWriteCallback(csound, WriteMidiData_); - csound->SetExternalMidiOutCloseCallback(csound, CloseMidiOutDevice_); - - return 0; -} - -PUBLIC int csoundModuleInfo(void) -{ - /* does not depend on MYFLT type */ - return ((CS_APIVERSION << 16) + (CS_APISUBVER << 8)); -} - -} // END EXTERN C - diff --git a/6.x/src/widgets/widgets.cpp b/6.x/src/widgets/widgets.cpp deleted file mode 100644 index 4e5b001c..00000000 --- a/6.x/src/widgets/widgets.cpp +++ /dev/null @@ -1,6127 +0,0 @@ -/* - widgets.cpp: - - Copyright (C) 2002 Gabriel Maldonado - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#if defined(WIN32) -# include -#endif -#ifndef _MSC_VER -# include -#endif -#if !defined(_MSC_VER) -#include -#endif -#include "widglobals.h" -#include - -Fl_Font FONT_TABLE[] = { FL_HELVETICA, FL_HELVETICA, - FL_HELVETICA_BOLD, FL_HELVETICA_ITALIC, - FL_HELVETICA_BOLD_ITALIC, FL_COURIER, - FL_COURIER_BOLD, FL_COURIER_ITALIC, - FL_COURIER_BOLD_ITALIC, FL_TIMES, - FL_TIMES_BOLD, FL_TIMES_ITALIC, - FL_TIMES_BOLD_ITALIC, FL_SYMBOL, - FL_SCREEN, FL_SCREEN_BOLD, - FL_ZAPF_DINGBATS }; - -Fl_Align ALIGN_TABLE[] = { FL_ALIGN_BOTTOM, FL_ALIGN_CENTER, - FL_ALIGN_TOP, FL_ALIGN_BOTTOM, - FL_ALIGN_LEFT, FL_ALIGN_RIGHT, - FL_ALIGN_TOP_LEFT, FL_ALIGN_TOP_RIGHT, - FL_ALIGN_BOTTOM_LEFT, FL_ALIGN_BOTTOM_RIGHT }; - -Fl_Boxtype BOX_TABLE[] = { FL_FLAT_BOX, FL_FLAT_BOX, - FL_UP_BOX, FL_DOWN_BOX, - FL_THIN_UP_BOX, FL_THIN_DOWN_BOX, - FL_ENGRAVED_BOX, FL_EMBOSSED_BOX, - FL_BORDER_BOX, _FL_SHADOW_BOX, - _FL_ROUNDED_BOX, _FL_RSHADOW_BOX, - _FL_RFLAT_BOX, _FL_ROUND_UP_BOX, - _FL_ROUND_DOWN_BOX, _FL_DIAMOND_UP_BOX, - _FL_DIAMOND_DOWN_BOX,_FL_OVAL_BOX, - _FL_OSHADOW_BOX, _FL_OFLAT_BOX}; - -void widget_init(CSOUND *csound) -{ - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *) csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - if (widgetGlobals == NULL) { - csound->CreateGlobalVariable(csound, "WIDGET_GLOBALS", - sizeof(WIDGET_GLOBALS)); - widgetGlobals = - (WIDGET_GLOBALS *) csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - - //csound->widgetGlobals = new WIDGET_GLOBALS; - //csound->Calloc(csound, sizeof(WIDGET_GLOBALS)); - // widgetGlobals->indrag = 0; - // widgetGlobals->sldrag = 0; - // widgetGlobals->stack_count) = 0; - - widgetGlobals->FLcontrol_iheight = 15; - widgetGlobals->FLroller_iheight = 18; - widgetGlobals->FLcontrol_iwidth = 400; - widgetGlobals->FLroller_iwidth = 150; - widgetGlobals->FLvalue_iwidth = 100; - - widgetGlobals->FLcolor = -1; - widgetGlobals->FLcolor2 = -1; - // below was commented out, why? VL 24-04-08 - widgetGlobals->FLtext_size = 0; - widgetGlobals->FLtext_color = -1; - widgetGlobals->FLtext_font = -1; - // below was commented out, why? VL 24-04-08 - widgetGlobals->FLtext_align = 0; - - widgetGlobals->FL_ix = 10; - widgetGlobals->FL_iy = 10; - widgetGlobals->currentSnapGroup = 0; - widgetGlobals->last_KEY=0; - widgetGlobals->isKeyDown=0; - } -} - -extern void graphs_reset(CSOUND *csound); - -int widget_reset(CSOUND *csound, void *pp) -{ - (void) pp; - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - if (widgetGlobals != NULL) { - widgetGlobals->AddrStack.~vector(); - widgetGlobals->allocatedStrings.~vector(); - widgetGlobals->fl_windows.~vector(); - csound->DestroyGlobalVariable(csound, "WIDGET_GLOBALS"); - } - graphs_reset(csound); - return OK; -} - -#ifndef NO_FLTK_THREADS -extern "C" { - /* called by sensevents() once in every control period */ - static void evt_callback(CSOUND *csound, widgetsGlobals_t *p) - { - /* flush any pending real time events */ - if (UNLIKELY(p->eventQueue != NULL)) { - csound->LockMutex(p->mutex_); - while (p->eventQueue != NULL) { - rtEvt_t *ep = p->eventQueue; - p->eventQueue = ep->nxt; - ep->evt.pinstance = NULL; - csound->UnlockMutex(p->mutex_); - csound->insert_score_event_at_sample(csound, &(ep->evt), - csound->GetCurrentTimeSamples(csound)); - free(ep); - csound->LockMutex(p->mutex_); - } - csound->UnlockMutex(p->mutex_); - } - /* if all windows have been closed, terminate performance */ - if (UNLIKELY(p->exit_now)) { - EVTBLK e; - memset(&e, 0, sizeof(EVTBLK)); - e.opcod = 'e'; - csound->insert_score_event_at_sample(csound, &e, - csound->GetCurrentTimeSamples(csound)); - } - } -} // extern "C" -#endif // NO_FLTK_THREADS - -// ---- IV - Aug 23 2002 ---- included file: Fl_linux_stubs.cpp - -/* - * this is a file used in the linux distribution to be able to use - * the DirectCsound 5.1 widget code by Gabriel Maldonado on linux - * systems. - * - * This code has been written by Nicola Bernardini (nicb@centrotemporeale.it) - * mostly based on ideas by Dave Phillips (dlphip@bright.net) - */ - -extern "C" { - void ButtonSched(CSOUND *csound, MYFLT *args[], int numargs) - { /* based on code by rasmus */ -#ifndef NO_FLTK_THREADS - widgetsGlobals_t *p; - /* this is still not fully thread safe... */ - /* hope that no global variable is created just while this fn is called */ - p = (widgetsGlobals_t*) csound->QueryGlobalVariable(csound, - "_widgets_globals"); - if (p != NULL) { - rtEvt_t *evt; - int i; - - /* Create the new event */ - evt = (rtEvt_t*) malloc(sizeof(rtEvt_t)); - evt->nxt = NULL; - evt->evt.pinstance = NULL; - evt->evt.strarg = NULL; evt->evt.scnt = 0; - evt->evt.opcod = (char) *args[0]; - if (evt->evt.opcod == '\0') - evt->evt.opcod = 'i'; - evt->evt.pcnt = numargs - 1; - evt->evt.p[1] = evt->evt.p[2] = evt->evt.p[3] = MYFLT(0.0); - for (i = 1; i < numargs; i++) - evt->evt.p[i] = *args[i]; - if (evt->evt.p[2] < MYFLT(0.0)) - evt->evt.p[2] = MYFLT(0.0); - /* queue event for insertion by main Csound thread */ - csound->LockMutex(p->mutex_); - if (p->eventQueue == NULL) - p->eventQueue = evt; - else { - rtEvt_t *ep = p->eventQueue; - while (ep->nxt != NULL) - ep = ep->nxt; - ep->nxt = evt; - } - csound->UnlockMutex(p->mutex_); - } - else -#endif // NO_FLTK_THREADS - { - EVTBLK e; - int i; - /* Create the new event */ - e.strarg = NULL; e.scnt = 0; - e.opcod = (char) *args[0]; - if (e.opcod == '\0') - e.opcod = 'i'; - e.pcnt = numargs - 1; - e.p[1] = e.p[2] = e.p[3] = MYFLT(0.0); - for (i = 1; i < numargs; i++) - e.p[i] = *args[i]; - if (e.p[2] < MYFLT(0.0)) - e.p[2] = MYFLT(0.0); - e.pinstance = NULL; - csound->insert_score_event_at_sample(csound, &e, - csound->GetCurrentTimeSamples(csound)); - } - } -} - -// ---- IV - Aug 23 2002 ---- included file: Fl_Knob.cxx - -// generated by Fast Light User Interface Designer (fluid) version 2.00 - -Fl_Knob::Fl_Knob(CSOUND *cs, int xx,int yy,int ww,int hh,const char *l) - : Fl_Valuator(xx,yy,ww,hh,l) -{ - csound = cs; - a1 = 35; - a2 = 325; - _type = DOTLIN; - _percent = 0.3f; - _scaleticks = 10; -} - -Fl_Knob::~Fl_Knob(void) -{ -} - -void Fl_Knob::draw(void) -{ - int ox,oy,ww,hh,side; - unsigned char rr,gg,bb; - - ox = x(); - oy = y(); - ww = w(); - hh = h(); - draw_label(); - fl_clip(ox,oy,ww,hh); - if (ww > hh) { - side = hh; - ox = ox + (ww - side) / 2; - } - else { - side = ww; - oy = oy + (hh - side) / 2; - } - side = w() > h () ? hh:ww; - int dam = damage(); - if (dam & FL_DAMAGE_ALL) { - int col = parent()->color(); - fl_color(col); - fl_rectf(ox,oy,side,side); - Fl::get_color((Fl_Color)col,rr,gg,bb); - shadow(-60,rr,gg,bb); - fl_pie(ox+9,oy+9,side-12,side-12,0,360); - draw_scale(ox,oy,side); - col = color(); - Fl::get_color((Fl_Color)col,rr,gg,bb); - shadow(15,rr,gg,bb); - fl_pie(ox+6,oy+6,side-12,side-12,40,80); - shadow(30,rr,gg,bb); - fl_pie(ox+6,oy+6,side-12,side-12,80,220); - shadow(-15,rr,gg,bb); - fl_pie(ox+6,oy+6,side-12,side-12,220,260); - shadow(-30,rr,gg,bb); - fl_pie(ox+6,oy+6,side-12,side-12,260,400); - fl_color(FL_BLACK); - fl_arc(ox+6,oy+6,side-11,side-11,0,360); - fl_color(col); - fl_pie(ox+10,oy+10,side-20,side-20,0,360); - } - else { - fl_color(color()); - fl_pie(ox+10,oy+10,side-20,side-20,0,360); - } - Fl::get_color((Fl_Color)color(),rr,gg,bb); - shadow(10,rr,gg,bb); - fl_pie(ox+10,oy+10,side-20,side-20,110,150); - fl_pie(ox+10,oy+10,side-20,side-20,290,330); - shadow(17,rr,gg,bb); - fl_pie(ox+10,oy+10,side-20,side-20,120,140); - fl_pie(ox+10,oy+10,side-20,side-20,300,320); - shadow(25,rr,gg,bb); - fl_pie(ox+10,oy+10,side-20,side-20,127,133); - fl_pie(ox+10,oy+10,side-20,side-20,307,313); - draw_cursor(ox,oy,side); - fl_pop_clip(); -} - -int Fl_Knob::handle(int event) -{ - int ox,oy,ww,hh; - - ox = x() + 10; oy = y() + 10; - ww = w() - 20; - hh = h() - 20; - switch (event) { - case FL_PUSH: - handle_push(); - return 1; // CHECKME ***JPff added this; is that right?*** - case FL_DRAG: - { - int mx = Fl::event_x()-ox-ww/2; - int my = Fl::event_y()-oy-hh/2; - if (!mx && !my) return 1; - double angle = 270-atan2((float)-my, (float)mx)*180.0/PI; - double oldangle = (a2-a1)*(value()-minimum())/(maximum()-minimum()) + a1; - while (angle < oldangle-180) angle += 360.0; - while (angle > oldangle+180) angle -= 360.0; - double val; - if ((a1= a1)) { - val = minimum(); - } - else if ((a1= a2) : (angle <= a2)) { - val = maximum(); - } - else { - val = minimum() + (maximum()-minimum())*(angle-a1)/(a2-a1); - } - handle_drag(clamp(round(val))); - } - return 1; - case FL_RELEASE: - handle_release(); - return 1; - default: - return 0; - } - return 0; -} - -void Fl_Knob::type(int ty) -{ - _type = ty; -} - -void Fl_Knob::shadow(const int offs,const uchar r,uchar g,uchar b) -{ - int rr,gg,bb; - - rr = r + offs; - rr = rr > 255 ? 255 : rr; - rr = rr < 0 ? 0:rr; - gg = g + offs; - gg = gg > 255 ? 255 : gg; - gg = gg < 0 ? 0 :gg ; - bb = b + offs; - bb = bb > 255 ? 255 : bb; - bb = bb < 0 ? 0 : bb; - fl_color((uchar)rr,(uchar)gg,(uchar)bb); -} - -void Fl_Knob::draw_scale(const int ox,const int oy,const int side) -{ - float x1,y1,x2,y2,rds,cx,cy,ca,sa; - - rds = side * 0.5f; - cx = ox + side * 0.5f; - cy = oy + side * 0.5f; - if (!(_type & DOTLOG_3)) { - if (_scaleticks == 0) return; - double a_step = (10.0*PI/6.0) / _scaleticks; - double a_orig = -(PI/3.0); - for (int a = 0; a <= _scaleticks; a++) { - double na = a_orig + a * a_step; - ca = cos(na); - sa = sin(na); - x1 = cx + rds * ca; - y1 = cy - rds * sa; - x2 = cx + (rds-6) * ca; - y2 = cy - (rds-6) * sa; - fl_color(FL_BLACK); - fl_line((int)x1,(int)y1,(int)x2,(int)y2); - fl_color(FL_WHITE); - if (sa*ca >=0) - fl_line((int)x1+1,(int)y1+1,(int)x2+1,(int)y2+1); - else - fl_line((int)x1+1,(int)y1-1,(int)x2+1,(int)y2-1); - } - } - else { - int nb_dec = (_type & DOTLOG_3); - for (int k = 0; k < nb_dec; k++) { - double a_step = (10.0*PI/6.0) / nb_dec; - double a_orig = -(PI/3.0) + k * a_step; - for (int a = (k) ? 2:1; a <= 10; ) { - double na = a_orig + log10((double)a) * a_step; - ca = cos(na); - sa = sin(na); - x1 = cx - rds * ca; - y1 = cy - rds * sa; - x2 = cx - (rds-6) * ca; - y2 = cy - (rds-6) * sa; - fl_color(FL_BLACK); - fl_line((int)x1,(int)y1,(int)x2,(int)y2); - fl_color(FL_WHITE); - if (sa*ca <0) - fl_line((int)x1+1,(int)y1+1,(int)x2+1,(int)y2+1); - else - fl_line((int)x1+1,(int)y1-1,(int)x2+1,(int)y2-1); - if ((a == 1) || (nb_dec == 1)) - a += 1; - else - a += 2; - } - } - } -} - -void Fl_Knob::draw_cursor(const int ox,const int oy,const int side) -{ - float rds,cur,cx,cy; - double angle; - - rds = (side - 20.0f) * 0.5f; - cur = _percent * rds * 0.5f; - cx = ox + side * 0.5f; - cy = oy + side * 0.5f; - angle = (a2-a1)*(value()-minimum())/(maximum()-minimum()) + a1; - fl_push_matrix(); - fl_scale(1,1); - fl_translate(cx,cy); - fl_rotate(-angle); - fl_translate(0,rds-cur-2.0f); - if (_type 1.0f) _percent = 1.0f; - if (visible()) damage(FL_DAMAGE_CHILD); -} - -void Fl_Knob::scaleticks(const int tck) -{ - _scaleticks = tck; - if (_scaleticks < 0) _scaleticks = 0; - if (_scaleticks > 31) _scaleticks = 31; - if (visible()) damage(FL_DAMAGE_ALL); -} - -// ---- IV - Aug 23 2002 ---- included file: Fl_Spin.cpp - -void Fl_Spin::draw() -{ - int sxx = x(), syy = y(), sww = w(), shh = h(); - Fl_Boxtype box1 = (Fl_Boxtype)(box()); - int border_size=Fl::box_dx(box()); - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - - if (damage()&~FL_DAMAGE_CHILD) clear_damage(FL_DAMAGE_ALL); - - if (!box1) box1 = (Fl_Boxtype)(box()&-2); - - if ((widgetGlobals->indrag || mouseobj) && deltadir!=0) { - if (deltadir>0) { - draw_box(fl_down(box1),sxx,syy,sww,shh/2,color()); - draw_box(box1,sxx,syy+shh/2,sww,shh/2,color()); - } - else { - draw_box(box1,sxx,syy,sww,shh/2,color()); - draw_box(fl_down(box1),sxx,syy+shh/2,sww,shh/2,color()); - } - } - else { - draw_box(box1,sxx,syy,sww,shh/2,color()); - draw_box(box1,sxx,syy+shh/2,sww,shh/2,color()); - } - sxx+=border_size; - syy+=border_size; - sww-=border_size*2; - shh-=border_size*2; - if (active_r()) { - fl_color(selection_color()); - } - else { - fl_color(selection_color() | 8); - } - int w1 = (sww-1)|1; // use odd sizes only - int X = sxx+w1/2; - int W = w1/3; - int h1 = shh/2-border_size-2; - int Y= syy; - fl_polygon(X, Y, X+W,Y+h1 , X-W, Y+h1); - Y=syy+shh/2+border_size+1+h1; - fl_polygon(X, Y, X-W, Y-h1, X+W, Y-h1); - clear_damage(); -} - -void Fl_Spin::repeat_callback(void* v) -{ - Fl_Spin* b = (Fl_Spin*)v; - if (b->mouseobj) { - Fl::add_timeout(0.1, repeat_callback, b); - b->increment_cb(); - } -} - -void Fl_Spin::increment_cb(void) -{ - if (!mouseobj) return; - delta += deltadir; - double v; - switch (drag) { - case 3: v = increment(value(), deltadir*100); break; - case 2: v = increment(value(), deltadir*10); break; - default:v = increment(value(), deltadir); break; - } - v = round(v); - handle_drag(soft()?softclamp(v):clamp(v)); -} - -int Fl_Spin::handle(int event) -{ - double v; - int olddelta; - int mx = Fl::event_x(); - int my = Fl::event_y(); - int sxx = x(), syy = y(), sww = w(), shh = h(); - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - - switch (event) { - case FL_PUSH: - // if (!step()) goto DEFAULT; - iy = my; - ix = mx; - drag = Fl::event_button(); - handle_push(); - widgetGlobals->indrag=1; - mouseobj=1; - Fl::add_timeout(0.5, repeat_callback, this); - delta=0; - if (Fl::event_inside(sxx,syy,sww,shh/2)) { - deltadir=1; - } - else if (Fl::event_inside(sxx,syy+shh/2,sww,shh/2)) { - deltadir=-1; - } - else { - deltadir=0; - } - increment_cb(); - redraw(); - return 1; - case FL_DRAG: - if (mouseobj) { - mouseobj=0; - Fl::remove_timeout(repeat_callback, this); - } - // if (!step()) goto DEFAULT; - olddelta=delta; - delta = - (Fl::event_y()-iy); - if ((delta>5) || (delta<-5)) { - deltadir=((olddelta-delta)>0)?-1:(((olddelta-delta)<0)?1:0); - } - else { - deltadir=0; delta = olddelta; - } - switch (drag) { - case 3: v = increment(value(), deltadir*100); break; - case 2: v = increment(value(), deltadir*10); break; - default:v = increment(value(), deltadir); break; - } - v = round(v); - handle_drag(soft()?softclamp(v):clamp(v)); - widgetGlobals->indrag=1; - return 1; - case FL_RELEASE: - if (mouseobj) { - Fl::remove_timeout(repeat_callback, this); - } - // if (!step()) goto DEFAULT; - widgetGlobals->indrag=0; - delta=0; - deltadir=0; - mouseobj=0; - handle_release(); - redraw(); - return 1; - default: - widgetGlobals->indrag=0; - return Fl_Valuator::handle(event); - } -} - -Fl_Spin::~Fl_Spin(void) -{ - Fl::remove_timeout(repeat_callback, this); -} - -Fl_Spin::Fl_Spin(CSOUND *cs, int x, int y, int w, int h, const char* l) - : Fl_Valuator(x,y,w,h,l) -{ - - csound = cs; - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - soft_ = 0; - align(FL_ALIGN_LEFT); - ix=x; - iy=y; - drag=0; - widgetGlobals->indrag=0; - mouseobj = 0; - deltadir=0; - delta=0; - indrag=0; -} - -// ---- IV - Aug 23 2002 ---- included file: Fl_Value_Input_Spin.cpp - -void Fl_Value_Input_Spin::input_cb(Fl_Widget*, void* v) -{ - Fl_Value_Input_Spin& t = *(Fl_Value_Input_Spin*)v; - - CSOUND *csound = t.csound; - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - double nv; - if (t.step()>=1.0) nv = strtol(t.input.value(), 0, 0); - else nv = csound->strtod((char*)t.input.value(), 0); - widgetGlobals->hack_o_rama1 = 1; - t.handle_push(); - t.handle_drag(nv); - t.handle_release(); - widgetGlobals->hack_o_rama1 = 0; -} - -void Fl_Value_Input_Spin::draw(void) -{ - int sxx = x(), syy = y(), sww = w(), shh = h(); - sxx += sww - buttonssize(); sww = buttonssize(); - Fl_Boxtype box1 = (Fl_Boxtype)(box()&-2); - int border_size=Fl::box_dx(box()); - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - - if (damage()&~FL_DAMAGE_CHILD) input.clear_damage(FL_DAMAGE_ALL); - input.box(box()); - input.color(FL_WHITE, selection_color()); - input.redraw(); - input.clear_damage(); - sxx+=border_size; - syy+=border_size; - sww-=border_size*2; - shh-=border_size*2; - - if (!box1) box1 = (Fl_Boxtype)(box()&-2); - - if ((widgetGlobals->indrag || mouseobj) && deltadir!=0) { - if (deltadir>0) { - draw_box(fl_down(box1),sxx,syy,sww,shh/2,color()); - draw_box(box1,sxx,syy+shh/2,sww,shh/2,color()); - } - else { - draw_box(box1,sxx,syy,sww,shh/2,color()); - draw_box(fl_down(box1),sxx,syy+shh/2,sww,shh/2,color()); - } - } - else { - draw_box(box1,sxx,syy,sww,shh/2,color()); - draw_box(box1,sxx,syy+shh/2,sww,shh/2,color()); - } - sxx+=border_size; - syy+=border_size; - sww-=border_size*2; - shh-=border_size*2; - if (active_r()) { - fl_color(labelcolor()); - } - else { - fl_color(labelcolor() | 8); - } - int w1 = (sww-1)|1; // use odd sizes only - int X = sxx+w1/2; - int W = w1/3; - int h1 = shh/2-border_size-2; - int Y= syy; - fl_polygon(X, Y, X+W,Y+h1 , X-W, Y+h1); - Y=syy+shh/2+border_size+1+h1; - fl_polygon(X, Y, X-W, Y-h1, X+W, Y-h1); - clear_damage(); -} - -void Fl_Value_Input_Spin::resize(int X, int Y, int W, int H) -{ - input.resize(X,Y,W,H); - Fl_Valuator::resize(X,Y,W,H); -} - -void Fl_Value_Input_Spin::value_damage(void) -{ - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - if (widgetGlobals->hack_o_rama1) return; - char buf[128]; - format(buf); - input.value(buf); - input.mark(input.position()); // turn off selection highlight -} - -void Fl_Value_Input_Spin::repeat_callback(void* v) -{ - Fl_Value_Input_Spin* b = (Fl_Value_Input_Spin*)v; - if (b->mouseobj) { - Fl::add_timeout(.1, repeat_callback, b); - b->increment_cb(); - } -} - -void Fl_Value_Input_Spin::increment_cb(void) -{ - if (!mouseobj) return; - delta+=deltadir; - double v; - switch (drag) { - case 3: v = increment(value(), deltadir*100); break; - case 2: v = increment(value(), deltadir*10); break; - default:v = increment(value(), deltadir); break; - } - v = round(v); - handle_drag(soft()?softclamp(v):clamp(v)); -} - -int Fl_Value_Input_Spin::handle(int event) -{ - double v; - int olddelta; - int mx = Fl::event_x(); - int my = Fl::event_y(); - int sxx = x(), syy = y(), sww = w(), shh = h(); - sxx += sww - buttonssize(); sww = buttonssize(); - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - - if (!widgetGlobals->indrag && - ( !widgetGlobals->sldrag || !((mx>=sxx && mx<=(sxx+sww)) && - (my>=syy && my<=(syy+shh)))) ) { - widgetGlobals->indrag=0; - switch(event) { - case FL_PUSH: - case FL_DRAG: - widgetGlobals->sldrag=1; - break; - case FL_FOCUS: - input.take_focus(); - break; - case FL_UNFOCUS: - redraw(); - break; - default: - widgetGlobals->sldrag=0; - } - input.type(step()>=1.0 ? FL_INT_INPUT : FL_FLOAT_INPUT); - return input.handle(event); - } - - switch (event) { - case FL_PUSH: - // if (!step()) goto DEFAULT; - iy = my; - ix = mx; - drag = Fl::event_button(); - handle_push(); - widgetGlobals->indrag=1; - mouseobj=1; - Fl::add_timeout(.5, repeat_callback, this); - delta=0; - if (Fl::event_inside(sxx,syy,sww,shh/2)) { - deltadir=1; - } - else if (Fl::event_inside(sxx,syy+shh/2,sww,shh/2)) { - deltadir=-1; - } - else { - deltadir=0; - } - increment_cb(); - redraw(); - return 1; - case FL_DRAG: - if (mouseobj) { - mouseobj=0; - Fl::remove_timeout(repeat_callback, this); - } - // if (!step()) goto DEFAULT; - olddelta=delta; - delta = - (Fl::event_y()-iy); - if ((delta>5) || (delta<-5) ) { - deltadir=((olddelta-delta)>0)?-1:(((olddelta-delta)<0)?1:0); - } - else { - deltadir=0; delta = olddelta; - } - switch (drag) { - case 3: v = increment(value(), deltadir*100); break; - case 2: v = increment(value(), deltadir*10); break; - default:v = increment(value(), deltadir); break; - } - v = round(v); - handle_drag(soft()?softclamp(v):clamp(v)); - widgetGlobals->indrag=1; - return 1; - case FL_RELEASE: - if (mouseobj) { - Fl::remove_timeout(repeat_callback, this); - } - // if (!step()) goto DEFAULT; - widgetGlobals->indrag=0; - delta=0; - deltadir=0; - mouseobj=0; - handle_release(); - redraw(); - return 1; - case FL_FOCUS: - widgetGlobals->indrag=0; - return input.take_focus(); - default: - widgetGlobals->indrag=0; - input.type(step()>=1.0 ? FL_INT_INPUT : FL_FLOAT_INPUT); - return 1; - } -} - -Fl_Value_Input_Spin::~Fl_Value_Input_Spin(void) -{ - Fl::remove_timeout(repeat_callback, this); -} - -Fl_Value_Input_Spin::Fl_Value_Input_Spin(CSOUND * cs, int x, int y, - int w, int h, const char* l) - : Fl_Valuator(x,y,w,h,l), input(x, y, w, h, 0) -{ - csound = cs; - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - soft_ = 0; - // if (input.parent()) // defeat automatic-add - // ((Fl_Group*)input.parent())->remove(input); - // input.parent(this); // kludge! - this->parent()->add(input); - input.callback(input_cb, this); - input.when(FL_WHEN_CHANGED); - selection_color(input.selection_color()); - align(FL_ALIGN_LEFT); - box(input.box()); - value_damage(); - buttonssize(15); - ix = x; - iy = y; - drag = 0; - widgetGlobals->indrag = 0; - widgetGlobals->sldrag = 0; - mouseobj = 0; - deltadir = 0; - delta = 0; -} - -// ---- IV - Aug 23 2002 ---- included file: Fl_Value_Slider_Input.cpp - -void Fl_Value_Slider_Input::input_cb(Fl_Widget*, void* v) { - Fl_Value_Slider_Input& t = *(Fl_Value_Slider_Input*)v; - CSOUND *csound = t.csound; - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - double nv; - if (t.step()>=1.0) nv = strtol(t.input.value(), 0, 0); - else nv = csound->strtod((char*)t.input.value(), 0); - widgetGlobals->hack_o_rama2 = 1; - t.handle_push(); - t.handle_drag(nv); - t.handle_release(); - widgetGlobals->hack_o_rama2 = 0; -} - -void Fl_Value_Slider_Input::draw(void) -{ - int sxx = x(), syy = y(), sww = w(), shh = h(); - //int bww = w(); - int X = x(), Y = y(), W = w(), H = h(); - - int border_size=Fl::box_dx(box()); - - if (horizontal()) { - //bww = textboxsize(); - sxx += textboxsize(); sww -= textboxsize(); - input.resize(X,Y,W-sww,shh); - } - else { - fl_font(input.textfont(), input.textsize()); - syy += fl_height()+(border_size+1)*2; shh -= fl_height()+(border_size+1)*2; - input.resize(X,Y,W,H-shh); - } - if (damage()&~FL_DAMAGE_CHILD) input.clear_damage(FL_DAMAGE_ALL); - input.box(box()); - input.color(FL_WHITE, selection_color()); - input.redraw(); - input.resize(X,Y,W,H); - input.clear_damage(); - // if (horizontal()) input.draw(); - clear_damage(); - draw_box(box(),sxx,syy,sww,shh,color()); - sxx+=border_size; - syy+=border_size; - sww-=border_size*2; - shh-=border_size*2; - if (border_size<2) { - sxx++; - syy++; - sww--; - shh--; - } - Fl_Slider::draw(sxx,syy,sww,shh); -} - -void Fl_Value_Slider_Input::resize(int X, int Y, int W, int H) -{ - input.resize(X,Y,W,H); - Fl_Value_Slider::resize(X,Y,W,H); -} - -void Fl_Value_Slider_Input::value_damage() -{ - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - if (widgetGlobals->hack_o_rama2) return; - char buf[128]; - format(buf); - input.value(buf); - input.mark(input.position()); // turn off selection highlight -} - -int Fl_Value_Slider_Input::handle(int event) -{ - int mx = Fl::event_x(); - int my = Fl::event_y(); - int sxx = x(), syy = y(), sww = w(), shh = h(); - int border_size=Fl::box_dx(box()); - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - if (horizontal()) { - sxx += textboxsize(); sww -= textboxsize(); - } - else { - fl_font(input.textfont(), input.textsize()); - syy += fl_height()+(border_size+1)*2; shh -= fl_height()+(border_size+1)*2; - } - if ( !widgetGlobals->indrag && - ( !widgetGlobals->sldrag || !((mx>=sxx && mx<=(sxx+sww)) && - (my>=syy && my<=(syy+shh)))) ) { - widgetGlobals->indrag=0; - switch(event) { - case FL_PUSH: - case FL_DRAG: - widgetGlobals->sldrag=1; - break; - case FL_FOCUS: - input.take_focus(); - break; - case FL_UNFOCUS: - redraw(); - break; - default: - widgetGlobals->sldrag=0; - } - input.type(step()>=1.0 ? FL_INT_INPUT : FL_FLOAT_INPUT); - return input.handle(event); - } - switch (event) { - case FL_PUSH: - widgetGlobals->ix = mx; - widgetGlobals->drag = Fl::event_button(); - widgetGlobals->indrag=1; - return Fl_Slider::handle(event,sxx,syy,sww,shh); - case FL_DRAG: - widgetGlobals->indrag=1; - return Fl_Slider::handle(event,sxx,syy,sww,shh); - case FL_RELEASE: - // if (!step()) goto DEFAULT; - if (value() != previous_value() || !Fl::event_is_click()) - handle_release(); - else { - input.handle(FL_PUSH); - input.handle(FL_RELEASE); - } - widgetGlobals->indrag=0; - return 1; - case FL_FOCUS: - widgetGlobals->indrag=0; - input.take_focus(); - return Fl_Slider::handle(event,sxx,syy,sww,shh); - default: - widgetGlobals->indrag=0; - input.type(step()>=1.0 ? FL_INT_INPUT : FL_FLOAT_INPUT); - input.handle(event); - return Fl_Slider::handle(event,sxx,syy,sww,shh); - } -} - -Fl_Value_Slider_Input::Fl_Value_Slider_Input(CSOUND *cs, int x, int y, - int w, int h, const char* l) - : Fl_Value_Slider(x,y,w,h,l),input(x, y, w, h, 0) -{ - csound = cs; - soft_ = 0; - // if (input.parent()) // defeat automatic-add - // ((Fl_Group*)input.parent())->remove(input); - // input.parent(this); // kludge! - // input.callback(input_cb, this); - parent()->add(input); - // input.when(FL_WHEN_CHANGED | FL_WHEN_ENTER_KEY); - input.when(FL_WHEN_CHANGED ); - selection_color(input.selection_color()); - input.align(FL_ALIGN_LEFT); - align(FL_ALIGN_LEFT); - textboxsize(35); - value_damage(); -} - -// ---- IV - Aug 23 2002 ---- end of included files - -//static char *GetString(CSOUND *csound, MYFLT *pname, int is_string); - -//--------------- - -static void set_butbank_value(Fl_Group *o, MYFLT value) -{ - int childr = o->children(); - Fl_Button *b; - int ival = (int) value, j, label; - - if (ival < 0 || ival >= childr || (MYFLT) ival != value) - return; - for (j = 0; j < childr; j++) { - b = (Fl_Button *) o->child(j); - label = atoi(b->label()); - if (label == ival) { - b->value(1); - b->do_callback(); - } - else - b->value(0); - } -} - -inline void FLlock() { //gab - Fl::lock(); -} - -inline void FLunlock() { //gab - Fl::unlock(); - Fl::awake((void*) 0); -} - -SNAPSHOT::SNAPSHOT (vector& valuators, int snapGroup) -{ // the constructor captures current values of all widgets - // by copying all current values from "valuators" vector (AddrSetValue) - // to the "fields" vector - is_empty = 0; - FLlock(); //<================= - int i,k; - int vsize = valuators.size(); - for (i = 0, k = 0; i < vsize && k < vsize; i++, k++) { - while (valuators[k].group != snapGroup ) { - k++; - if (UNLIKELY(k >=vsize)) goto err; - } - ADDR_SET_VALUE& v = valuators[k]; - CSOUND *csound = (CSOUND*) (((OPDS *) (v.opcode))->insdshead->csound); - string opcode_name, widg_name; - fields.resize(i+1); - if ((int) fields.size() < i+1) - fields.resize(i+1); - VALUATOR_FIELD* fld = &(fields[i]); - float val, min, max; - opcode_name = fld->opcode_name = ((OPDS *) (v.opcode))->optext->t.opcod; - if (UNLIKELY(opcode_name.c_str() == NULL)) - { - csound->InitError(csound, "%s", - Str("Invalid snapshot. Perhaps you modified " - "orchestra widget code after you saved " - "the snapshot bank.")); - goto err; - } - else if (opcode_name == "FLslider") { - FLSLIDER *p = (FLSLIDER *) (v.opcode); - fld->widg_name = p->name->data; - fld->exp = (int) *p->iexp; - min = fld->min = *p->imin; max = fld->max = *p->imax; - switch (fld->exp) { - case LIN_: - case EXP_: - val = *p->kout; - if (min < max) { - if (val < min) val=min; - else if(val>max) val=max; - } - else { - if (val < max) val=max; - else if(val>min) val=min; - } - break; - default: val = ((Fl_Valuator *)v.WidgAddress)->value(); - } - fld->value = val; - } - else if (opcode_name == "FLslidBnk" || opcode_name == "FLvslidBnk") { - FLSLIDERBANK *p = (FLSLIDERBANK *) (v.opcode); - fld->widg_name = ((STRINGDAT *)p->names)->data; - int numsliders = (int) *p->inumsliders; - fld->sldbnk = p->slider_data; - // fld->sldbnkValues = new MYFLT[numsliders]; - // widgetGlobals->allocatedStrings.push_back((char *) fld->sldbnkValues); - fld->exp = numsliders; // EXCEPTIONAL CASE! fld->exp contains the number - // of sliders and not the exponential flag - for (int j =0; j < numsliders; j++) { - switch (fld->sldbnk[j].exp) { - case LIN_: case EXP_: - val = *fld->sldbnk[j].out; - min = fld->sldbnk[j].min; max = fld->sldbnk[j].max; - if (min < max) { - if (val < min) val=min; - else if(val>max) val=max; - } - else { - if (val < max) val=max; - else if(val>min) val=min; - } - break; - default: - val = ((Fl_Valuator *) ((Fl_Group*)v.WidgAddress)-> - child(j))->value(); - } - fld->set_sldbnk(j, val); - } - } - else if (opcode_name == "FLslidBnk2" || opcode_name == "FLvslidBnk2") { - FLSLIDERBANK2 *p = (FLSLIDERBANK2 *) (v.opcode); - fld->widg_name = ((STRINGDAT *)p->names)->data; - int numsliders = (int) *p->inumsliders; - fld->sldbnk = p->slider_data; - // EXCEPTIONAL CASE! fld->exp contains the number of sliders - // and not the exponential flag - fld->exp = numsliders; - for (int j =0; j < numsliders; j++) { - switch (fld->sldbnk[j].exp) { - case LIN_: case EXP_: - val = *fld->sldbnk[j].out; - min = fld->sldbnk[j].min; max = fld->sldbnk[j].max; - if (min < max) { - if (val < min) val=min; - else if(val>max) val=max; - } - else { - if (val < max) val=max; - else if(val>min) val=min; - } - break; - default: - val = ((Fl_Valuator *) ((Fl_Group*) v.WidgAddress)->child(j))->value(); - } - fld->set_sldbnk(j, val); - } - } - else if (opcode_name == "FLknob") { - FLKNOB *p = (FLKNOB *) (v.opcode); - fld->widg_name = p->name->data; - fld->exp = (int) *p->iexp; - min = fld->min = *p->imin; max = fld->max = *p->imax; - switch (fld->exp) { - case LIN_: - case EXP_: - val = *p->kout; - if (min < max) { - if (val < min) val=min; - else if(val>max) val=max; - } - else { - if (val < max) val=max; - else if(val>min) val=min; - } - break; - default: val = ((Fl_Valuator *)v.WidgAddress)->value(); - } - fld->value = val; - } - else if (opcode_name == "FLroller") { - FLROLLER *p = (FLROLLER *) (v.opcode); - fld->widg_name = p->name->data; - fld->exp = (int) *p->iexp; - min = fld->min = *p->imin; max = fld->max = *p->imax; - switch (fld->exp) { - case LIN_: - case EXP_: - val = *p->kout; - if (min < max) { - if (val < min) val=min; - else if(val>max) val=max; - } - else { - if (val < max) val=max; - else if(val>min) val=min; - } - break; - default: val = ((Fl_Valuator *)v.WidgAddress)->value(); - } - fld->value = val; - } - else if (opcode_name == "FLtext") { - FLTEXT *p = (FLTEXT *) (v.opcode); - fld->widg_name = p->name->data; - val = *p->kout; min = fld->min = *p->imin; max = fld->max = *p->imax; - if (min < max) { - if (val < min) val=min; - else if(val>max) val=max; - } - else { - if (val < max) val=max; - else if(val>min) val=min; - } - fld->value = val; - fld->exp = LIN_; - } - else if (opcode_name == "FLjoy") { - FLJOYSTICK *p = (FLJOYSTICK *) (v.opcode); - fld->widg_name = p->name->data; - fld->exp = (int) *p->iexpx; - min = fld->min = *p->iminx; max = fld->max = *p->imaxx; - switch (fld->exp) { - case LIN_: - case EXP_: - val = *p->koutx; - if (min < max) { - if (val < min) val=min; - else if(val>max) val=max; - } - else { - if (val < max) val=max; - else if(val>min) val=min; - } - break; - default: - val = ((Fl_Positioner *)v.WidgAddress)->xvalue(); - } - fld->value = val; - fld->exp2 = (int) *p->iexpy; - min = fld->min2 = *p->iminy; max = fld->max2 = *p->imaxy; - switch (fld->exp2) { - case LIN_: case EXP_: - val = *p->kouty; - if (min < max) { - if (val < min) val=min; - else if(val>max) val=max; - } - else { - if (val < max) val=max; - else if(val>min) val=min; - } - break; - default: - val = ((Fl_Positioner *)v.WidgAddress)->yvalue(); - } - fld->value2 =val; - } - else if (opcode_name == "FLbutton") { - FLBUTTON *p = (FLBUTTON *) (v.opcode); - fld->widg_name = p->name->data; - fld->value = *p->kout; - fld->min = 0; fld->max = 1; fld->exp = LIN_; - } - else if (opcode_name == "FLbutBank") { - FLBUTTONBANK *p = (FLBUTTONBANK *) (v.opcode); - fld->widg_name = Str("No name for FLbutbank"); - //fld->widg_name = GetString(csound, p->name, p->XSTRCODE); - fld->value = *p->kout; - fld->min = 0; fld->max = 1; fld->exp = LIN_; - } - else if (opcode_name == "FLcount") { - FLCOUNTER *p = (FLCOUNTER *) (v.opcode); - fld->widg_name = p->name->data; - val = *p->kout; min = *p->imin; max =*p->imax; - if (min != max) { - if (val < min) val=min; - else if (val>max) val=max; - } - fld->value = val; - fld->min = *p->imin; fld->max = *p->imax; fld->exp = LIN_; - } - else if (opcode_name == "FLvalue") { - FLVALUE *p = (FLVALUE *) (v.opcode); - fld->widg_name = p->name->data; - } - else if (opcode_name == "FLbox") { - FL_BOX *p = (FL_BOX *) (v.opcode); - fld->widg_name = p->itext->data; - } - } - FLunlock(); - return; - err: - FLunlock(); //<================= - is_empty = 1; -} - -int SNAPSHOT::get(vector& valuators, int snapGroup) -{ - if (UNLIKELY(is_empty == 1)) { - /* FIXME: should have CSOUND* pointer here */ - /* return csound->InitError(csound, "%s", Str("empty snapshot")); */ - //printf("SNAPSHOT IS EMPTY\n"); - return -1; - } - FLlock(); //<================= - int j,k; - int siz =valuators.size(); - for (j = 0, k = 0; j< siz && k < siz; j++, k++) { - // int grp = valuators[k].group; //Not used - while (valuators[k].group != snapGroup) { - k++; - if (k >= (int) valuators.size()) goto end_func; - } - Fl_Widget* o = (Fl_Widget*) (valuators[k].WidgAddress); - void *opcode = valuators[k].opcode; -//CSOUND *csound = (CSOUND*) (((OPDS*) opcode)->insdshead->csound); //Not used - VALUATOR_FIELD* fld = &fields[j]; - string opcode_name = fld->opcode_name; - - MYFLT val = fld->value, valtab = fld->value, min=fld->min, - max=fld->max, range,base; - if (min < max) { - if (val < min) val = min; - else if (val >max) val = max; - } - else { - if (val < max) val=max; - else if(val>min) val=min; - } - - if (opcode_name == "FLjoy") { - switch(fld->exp) { - case LIN_: - ((Fl_Positioner*) o)->xvalue(val); - break; - case EXP_: - range = fld->max - fld->min; - #if defined(sun) - base = ::pow(fld->max / (double)fld->min, 1.0/(double)range); - #else - base = ::pow(fld->max / fld->min, 1.0/(double)range); - #endif - ((Fl_Positioner*) o)->xvalue(log(val/fld->min) / log(base)) ; - break; - default: - ((Fl_Positioner*) o)->xvalue(valtab); - break; - } - val = fld->value2; min = fld->min2; max = fld->max2; - // if (val < min) val = min; - // else if (val >max) val = max; - switch(fld->exp2) { - case LIN_: - ((Fl_Positioner*) o)->yvalue(val); - break; - case EXP_: - range = fld->max2 - fld->min2; - #if defined(sun) - base = ::pow(fld->max2 / (double)fld->min2, 1.0/(double)range); - #else - base = ::pow(fld->max2 / fld->min2, 1.0/(double)range); - #endif - ((Fl_Positioner*) o)->yvalue(log(val/fld->min2) / log(base)) ; - break; - default: - ((Fl_Positioner*) o)->yvalue(valtab); - break; - } - o->do_callback(o, opcode); - } - else if (opcode_name == "FLbutton") { - FLBUTTON *p = (FLBUTTON*) (opcode); - // don't allow to retreive its value if >= 10 or between 21 and 29 - if ((*p->itype < 10) || (*p->itype < 30 && *p->itype > 20)) { - if(fld->value >= *p->ioff - 0.0001 && - fld->value <= *p->ioff + 0.0001) // to avoid eventual math rounding - ((Fl_Button*) o)->value(0); - else - if (fld->value >= *p->ion - 0.0001 && - fld->value <= *p->ion + 0.0001) // to avoid eventual math rounding - ((Fl_Button*) o)->value(1); - else - ((Fl_Button*) o)->value((int)fld->value); - o->do_callback(o, opcode); - } - } - else if (opcode_name == "FLbutBank") { - FLBUTTONBANK *p = (FLBUTTONBANK*) (opcode); - if (*p->itype < 10 || (*p->itype < 30 && *p->itype > 20)) { - // don't allow to retreive its value if >= 10 - //((Fl_Group*) o)->value(fld->value); - set_butbank_value((Fl_Group*) o, fld->value); - //o->do_callback(o, opcode); - *p->kout=fld->value; - if (*p->args[0] >= 0) ButtonSched(p->h.insdshead->csound, - p->args, p->INOCOUNT-7); - } - } - else if (opcode_name == "FLcount") { - FLCOUNTER *p = (FLCOUNTER*) (opcode); - if (*p->itype < 10 || (*p->itype < 30 && *p->itype > 20)) { - // don't allow to retreive its value if >= 10 - ((Fl_Counter*) o)->value(fld->value); - o->do_callback(o, opcode); - } - } - else if (opcode_name == "FLslidBnk" || opcode_name == "FLvslidBnk") { - FLSLIDERBANK *p = (FLSLIDERBANK*) (opcode); - int numsliders = (int) *p->inumsliders; - Fl_Group * grup = (Fl_Group *) o; - for (int j =0; j < numsliders; j++) { - MYFLT val = fld->get_sldbnk(j); - switch (p->slider_data[j].exp) { - case LIN_: - ((Fl_Valuator *) grup->child(j))->value(val); - break; - case EXP_: - range = p->slider_data[j].max - p->slider_data[j].min; - #if defined(sun) - base = ::pow(p->slider_data[j].max / (double)p->slider_data[j].min, - 1.0/(double)range); - #else - base = ::pow(p->slider_data[j].max / p->slider_data[j].min, - 1.0/(double)range); - #endif - ((Fl_Valuator*) grup->child(j))-> - value(log(val/p->slider_data[j].min) / log(base)) ; - break; - default:// TABLE the value must be in the 0 to 1 range... - val = (val - fld->min ) / (fld->max - fld->min); - ((Fl_Valuator *) grup->child(j))->value(val); - break; - } - grup->child(j)->do_callback( grup->child(j), - (void *) &(p->slider_data[j])); - } - } - else if (opcode_name == "FLslidBnk2" || opcode_name == "FLvslidBnk2") { - FLSLIDERBANK2 *p = (FLSLIDERBANK2*) (opcode); - int numsliders = (int) *p->inumsliders; - Fl_Group * grup = (Fl_Group *) o; - for (int j =0; j < numsliders; j++) { - MYFLT val = fld->get_sldbnk(j); - switch (p->slider_data[j].exp) { - case LIN_: - ((Fl_Valuator *) grup->child(j))->value(val); - break; - case EXP_: - range = p->slider_data[j].max - p->slider_data[j].min; - base = pow(p->slider_data[j].max / p->slider_data[j].min, 1/range); - ((Fl_Valuator*) grup->child(j))->value(log(val/p->slider_data[j].min) / - log(base)) ; - break; - default:// TABLE the value must be in the 0 to 1 range... - val = (val - fld->min ) / (fld->max - fld->min); - ((Fl_Valuator *) grup->child(j))->value(val); - break; - } - grup->child(j)->do_callback( grup->child(j), - (void *) &(p->slider_data[j])); - } - } - else { - switch(fld->exp) { - case LIN_: - if (opcode_name == "FLbox" || opcode_name == "FLvalue" ) continue; - else if (opcode_name == "FLtext" && - *((FLTEXT *)opcode)->itype == 1) { - ((Fl_Valuator*) o)->value(val); - continue; - } - ((Fl_Valuator*) o)->value(val); - break; - case EXP_: - range = fld->max - fld->min; - #if defined(sun) - base = ::pow(fld->max / (double)fld->min, 1.0/(double)range); - #else - base = ::pow(fld->max / fld->min, 1.0/(double)range); - #endif - ((Fl_Valuator*) o)->value(log(val/fld->min) / log(base)) ; - break; - default: // TABLE the value must be in the 0 to 1 range... - ((Fl_Valuator*) o)->value(valtab); - break; - } - o->do_callback(o, opcode); - } - } - end_func: - FLunlock(); //<================= - return OK; -} - -extern "C" { - - static int set_snap(CSOUND *csound, FLSETSNAP *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - SNAPSHOT snap(widgetGlobals->AddrSetValue, (int) *p->group ); - int numfields = snap.fields.size(); - int index = (int) *p->index; - int group = (int) *p->group; - SNAPVEC snapvec_init; - SNAPSHOT snap_init; - - snap_init.fields.resize(1,VALUATOR_FIELD()); - snapvec_init.resize(1,snap_init); - if (group+1 > (int) widgetGlobals->snapshots.size()) - widgetGlobals->snapshots.resize(group+1, snapvec_init); - // *p->inum_snap = widgetGlobals->snapshots.size(); - *p->inum_val = numfields; // number of snapshots - if (*p->ifn >= 1) { // if the table number is valid - FUNC *ftp; // store the snapshot into the table - if (LIKELY((ftp = csound->FTnp2Find(csound, p->ifn)) != NULL)) { - MYFLT *table = ftp->ftable; - for (int j = 0; j < numfields; j++) { - table[index*numfields+j] = snap.fields[j].value; - } - } - else return csound->InitError(csound, - "%s", Str("FLsetsnap: invalid table")); - } - else { // else store it into snapshot bank - if ((int) widgetGlobals->snapshots[group].size() < index+1) - widgetGlobals->snapshots[group].resize(index+1); - csound->Message(csound, "%s", Str("setsnap saving\n")); - widgetGlobals->snapshots[group][index]=snap; - *p->inum_snap = widgetGlobals->snapshots[group].size(); - } - return OK; - } - - static int get_snap(CSOUND *csound, FLGETSNAP *p) - { - int index = (int) *p->index; - int group = (int) *p->group; - SNAPVEC snapvec_init; - SNAPSHOT snap_init; - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - snap_init.fields.resize(1,VALUATOR_FIELD()); - snapvec_init.resize(1,snap_init); - if (group+1 > (int) widgetGlobals->snapshots.size()) - widgetGlobals->snapshots.resize(group+1, snapvec_init); - if (!widgetGlobals->snapshots[group].empty()) { - if (index >= (int) widgetGlobals->snapshots[group].size()) - index = widgetGlobals->snapshots[group].size()-1; - else if (index < 0) index=0; - if (widgetGlobals->snapshots[group][index].get(widgetGlobals->AddrSetValue, - (int) *p->group)!=OK) { - csound->Warning(csound, "could not get snapshot from group %d index %d \n", - group, index); - return OK; - } - } - *p->inum_el = widgetGlobals->snapshots[group].size(); - return OK; - } - - static int save_snap(CSOUND *csound, FLSAVESNAPS *p) - { - char s[MAXNAME], *s2; - string filename; -#ifdef WIN32 - int id = MessageBox(NULL, Str("Saving could overwrite the old file.\n" - "Are you sure to save ?"), Str("Warning"), - MB_SYSTEMMODAL | MB_ICONWARNING | MB_OKCANCEL); - if (id != IDOK) - return OK; -#else -# if !defined(NO_FLTK_THREADS) - if (!((getFLTKFlags(csound) & 260) ^ 4)) -# endif - { - int n; - Fl_lock(csound); - n = fl_choice("%s", Str("Saving could overwrite the old file.\n" - "Are you sure you want to save ?"), - Str("No"), Str("Yes"), (const char*)""); // used to be NULL - Fl_unlock(csound); - if (!n) - return OK; - } -#endif - csound->strarg2name(csound, s, p->filename->data, "snap.", 1); - s2 = csound->FindOutputFile(csound, s, "SNAPDIR"); - if (UNLIKELY(s2 == NULL)) - return csound->InitError(csound,"%s", - Str("FLsavesnap: cannot open file")); - strncpy(s, s2, MAXNAME-1); - csound->Free(csound, s2); - filename = s; - - fstream file(filename.c_str(), ios::out); - int group = (int) *p->group; - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - for (int j =0; j < (int) widgetGlobals->snapshots[group].size(); j++) { - file << "----------- "<< j << " -----------\n"; - int siz = widgetGlobals->snapshots[group][j].fields.size(); - for ( int k = 0; k < siz; k++) { - VALUATOR_FIELD& f = widgetGlobals->snapshots[group][j].fields[k]; - // if (f.group != group) continue; - if (f.opcode_name == "FLjoy") { - file <QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - csound->strarg2name(csound, s, p->filename->data, "snap.", 1); - s2 = csound->FindInputFile(csound, s, "SNAPDIR"); - if (UNLIKELY(s2 == NULL)) - return csound->InitError(csound, - "%s", Str("FLloadsnap: cannot open file")); - strncpy(s, s2, MAXNAME-1); - csound->Free(csound, s2); - filename = s; - - fstream file(filename.c_str(), ios::in); - int group = (int) *p->group; - int j=0,k=-1,q=0; - SNAPVEC snapvec_init; - SNAPSHOT snap_init; - snap_init.fields.resize(1,VALUATOR_FIELD()); - snapvec_init.resize(1,snap_init); - if (group+1 > (int) widgetGlobals->snapshots.size()) - widgetGlobals->snapshots.resize(group+1, snapvec_init); - while (!(file.eof())) { - char buf[MAXNAME]; - file.getline(buf,MAXNAME-1); - - stringstream sbuf; - sbuf << buf; - - string opc, opc_orig; - getline(sbuf, opc, ' '); - const char *ss = opc.c_str(); - if (*ss == '-') { // if it is a separation line - k++; j=0; q=0; - if ((int) widgetGlobals->snapshots[group].size() < k+1) - widgetGlobals->snapshots[group].resize(k+1); - } - else if (*ss != '\0' && *ss != ' ' && *ss != '\n'){ //ignore blank lines - ADDR_SET_VALUE* v = &(widgetGlobals->AddrSetValue[q]); - while (widgetGlobals->AddrSetValue[q].group != group) { - q++; - if (q >= (int) widgetGlobals->AddrSetValue.size()) continue; - v = &(widgetGlobals->AddrSetValue[q]); - } - if (k<0) return NOTOK; - if ((int) widgetGlobals->snapshots[group][k].fields.size() < j+1) - widgetGlobals->snapshots[group][k].fields.resize(j+1); - widgetGlobals->snapshots[group][k].is_empty = 0; - VALUATOR_FIELD& fld = widgetGlobals->snapshots[group][k].fields[j]; - opc_orig = ((OPDS *) (v->opcode))->optext->t.opcod; - - if (UNLIKELY(!(opc_orig == opc))) { - //return csound->InitError(csound, - csound->Message(csound, - "%s", Str("unmatched widget, probably due to a " - "modified orchestra. Modifying an " - "orchestra makes it incompatible with " - "old snapshot files")); - } - else if (opc == "FLjoy") { - fld.opcode_name = opc; - string s; - getline(sbuf,s, ' '); fld.value = atof(s.c_str()); - getline(sbuf,s, ' '); fld.value2 = atof(s.c_str()); - getline(sbuf,s, ' '); fld.min = atof(s.c_str()); - getline(sbuf,s, ' '); fld.max = atof(s.c_str()); - getline(sbuf,s, ' '); fld.min2 = atof(s.c_str()); - getline(sbuf,s, ' '); fld.max2 = atof(s.c_str()); - getline(sbuf,s, ' '); fld.exp = atoi(s.c_str()); - getline(sbuf,s, ' '); fld.exp2 = atoi(s.c_str()); - getline(sbuf,s, '\"'); - getline(sbuf,s, '\"'); fld.widg_name = s; - } - else if (opc == "FLslidBnk" || opc == "FLvslidBnk" - || opc == "FLslidBnk2" || opc == "FLvslidBnk2" ) { - fld.opcode_name = opc; - string s; - getline(sbuf,s, ' '); - // EXCEPTIONAL CASE! fld.exp contains the number of sliders - // and not the exponential flag - fld.exp = atoi(s.c_str()); - // fld.sldbnkValues = new MYFLT[fld.exp]; - // fld.insert_sldbnk(fld.exp); - // allocatedStrings.push_back((char *) fld.sldbnkValues); - // widgetGlobals->allocatedStrings.push_back((char *) fld.sldbnkValues); - - for (int kk =0; kk < fld.exp; kk++) { - getline(sbuf,s, ' '); - // fld.sldbnkValues[kk] = atof(s.c_str()); - fld.set_sldbnk(kk,atof(s.c_str())); - - } - getline(sbuf,s, '\"'); - getline(sbuf,s, '\"'); fld.widg_name = s; - } - else { - fld.opcode_name = opc; - string s; - getline(sbuf,s, ' '); fld.value = atof(s.c_str()); - getline(sbuf,s, ' '); fld.min = atof(s.c_str()); - getline(sbuf,s, ' '); fld.max = atof(s.c_str()); - getline(sbuf,s, ' '); fld.exp = atoi(s.c_str()); - getline(sbuf,s, '\"'); - getline(sbuf,s, '\"'); fld.widg_name = s; - } - j++; q++; - } - } - - file.close(); - return OK; - } - - static int fltkKeyboardCallback(void *, void *, unsigned int); - -} // extern "C" - -// ----------- - -/* unused -static char *GetString(CSOUND *csound, MYFLT *pname, int is_string) -{ - char *Name = new char[MAXNAME]; - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - widgetGlobals->allocatedStrings.push_back(Name); - return csound->strarg2name(csound, Name, pname, "", is_string); -} -*/ - -class CsoundFLTKKeyboardBuffer { -private: - CSOUND *csound; - WIDGET_GLOBALS *widgetGlobals; - void *mutex_; - char kbdTextBuf[64]; - int kbdEvtBuf[64]; - int kbdTextBufRPos; - int kbdTextBufWPos; - int kbdEvtBufRPos; - int kbdEvtBufWPos; - std::map keyboardState; - void lockMutex() - { - if (mutex_) - csound->LockMutex(mutex_); - } - void unlockMutex() - { - if (mutex_) - csound->UnlockMutex(mutex_); - } -public: - CsoundFLTKKeyboardBuffer(CSOUND *csound) - { - this->csound = csound; - widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - mutex_ = csound->Create_Mutex(0); - kbdTextBufRPos = 0; - kbdTextBufWPos = 0; - kbdEvtBufRPos = 0; - kbdEvtBufWPos = 0; - } - ~CsoundFLTKKeyboardBuffer() - { - if (mutex_) { - csound->DestroyMutex(mutex_); - mutex_ = (void*) 0; - } - } - CSOUND *GetCsound() - { - return csound; - } - void writeFLEvent(int evt) - { - const char *s; - int keyCode; - keyCode = (int) Fl::event_key() & (int) 0xFFFF; - if (keyCode) { - lockMutex(); - if (evt == FL_KEYDOWN) { - s = Fl::event_text(); - while (*s != (char) 0) { - kbdTextBuf[kbdTextBufWPos] = *(s++); - kbdTextBufWPos = (kbdTextBufWPos + 1) & 63; - } - if (keyboardState[keyCode] == (unsigned char) 0) { - keyboardState[keyCode] = (unsigned char) 1; - kbdEvtBuf[kbdEvtBufWPos] = keyCode; - kbdEvtBufWPos = (kbdEvtBufWPos + 1) & 63; - } - } - else if (keyboardState[keyCode] != (unsigned char) 0) { - keyboardState[keyCode] = (unsigned char) 0; - kbdEvtBuf[kbdEvtBufWPos] = keyCode | (int) 0x10000; - kbdEvtBufWPos = (kbdEvtBufWPos + 1) & 63; - } - unlockMutex(); - } - } - int getKeyboardText() - { - int retval = 0; - lockMutex(); - if (kbdTextBufRPos != kbdTextBufWPos) { - retval = (int) ((unsigned char) kbdTextBuf[kbdTextBufRPos]); - kbdTextBufRPos = (kbdTextBufRPos + 1) & 63; - } - unlockMutex(); - return retval; - } - int getKeyboardEvent() - { - int retval = 0; - lockMutex(); - if (kbdEvtBufRPos != kbdEvtBufWPos) { - retval = kbdEvtBuf[kbdEvtBufRPos]; - kbdEvtBufRPos = (kbdEvtBufRPos + 1) & 63; - } - unlockMutex(); - return retval; - } -}; - -class CsoundFLWindow : public Fl_Double_Window { -public: - CSOUND *csound_; //gab - WIDGET_GLOBALS *widgetGlobals; - CsoundFLTKKeyboardBuffer fltkKeyboardBuffer; - CsoundFLWindow(CSOUND *csound, int w, int h, const char *title = 0) - : Fl_Double_Window(w, h, title), - csound_(csound), - fltkKeyboardBuffer(csound)//gab - { - widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - csound->Set_KeyCallback(csound, fltkKeyboardCallback, (void*) this, - CSOUND_CALLBACK_KBD_EVENT | CSOUND_CALLBACK_KBD_TEXT); - } - CsoundFLWindow(CSOUND *csound, - int x, int y, int w, int h, const char *title = 0) - : Fl_Double_Window(x, y, w, h, title), - csound_(csound), - fltkKeyboardBuffer(csound)//gab - { - widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - csound->Set_KeyCallback(csound, fltkKeyboardCallback, (void*) this, - CSOUND_CALLBACK_KBD_EVENT | CSOUND_CALLBACK_KBD_TEXT); - } - virtual ~CsoundFLWindow() - { - CSOUND *csound = fltkKeyboardBuffer.GetCsound(); - csound->Remove_KeyCallback(csound, fltkKeyboardCallback); - } - virtual int handle(int evt) - { - switch (evt) { - case FL_FOCUS: - Fl::focus(this); - /* FALLTHRU */ - case FL_UNFOCUS: - return 1; - case FL_KEYDOWN: - widgetGlobals->last_KEY = Fl::event_key(); //gab - widgetGlobals->isKeyDown = true; //gab - break; - case FL_KEYUP: - widgetGlobals->last_KEY = Fl::event_key(); //gab - widgetGlobals->isKeyDown = false; //gab - if (Fl::focus() == this) - fltkKeyboardBuffer.writeFLEvent(evt); - break; - } - return Fl_Window::handle(evt); - } -}; - -extern "C" -{ - static int fltkKeyboardCallback(void *userData, void *p, unsigned int type) - { - switch (type) { - case CSOUND_CALLBACK_KBD_EVENT: - *((int*) p) = - ((CsoundFLWindow*) userData)->fltkKeyboardBuffer.getKeyboardEvent(); - break; - case CSOUND_CALLBACK_KBD_TEXT: - *((int*) p) = - ((CsoundFLWindow*) userData)->fltkKeyboardBuffer.getKeyboardText(); - break; - default: - return 1; - } - return 0; - } - - PUBLIC int csoundModuleDestroy(CSOUND *csound) - { - int j; - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); -#ifndef NO_FLTK_THREADS - int *fltkflags = getFLTKFlagsPtr(csound); - if (fltkflags) { - if ((*fltkflags & 260) ^ 4) { - widgetsGlobals_t *p = - (widgetsGlobals_t*) csound->QueryGlobalVariable(csound, - "_widgets_globals"); - if (p) { - if (!(*fltkflags & 256)) { - /* if window(s) still open: */ - if (!p->exit_now) { - /* notify GUI thread... */ - p->end_of_perf = -1; - Fl_lock(csound); - Fl_awake(csound); - Fl_unlock(csound); - /* ...and wait for it to close */ - csound->JoinThread(p->threadHandle); - p->threadHandle = NULL; - } - } - /* clean up */ - csound->LockMutex(p->mutex_); - while (p->eventQueue != NULL) { - rtEvt_t *nxt = p->eventQueue->nxt; - free(p->eventQueue); - p->eventQueue = nxt; - } - csound->UnlockMutex(p->mutex_); - csound->DestroyMutex(p->mutex_); - csound->DestroyGlobalVariable(csound, "_widgets_globals"); - } - } - } -#endif // NO_FLTK_THREADS - if(widgetGlobals != NULL) { - for (j = widgetGlobals->allocatedStrings.size() - 1; j >= 0; j--) { - delete[] widgetGlobals->allocatedStrings[j]; - widgetGlobals->allocatedStrings.pop_back(); - } - j = widgetGlobals->fl_windows.size(); - if (j > 0) { - // destroy all opened panels - do { - j--; - if (widgetGlobals->fl_windows[j].is_subwindow == 0) - delete widgetGlobals->fl_windows[j].panel; - // VL: this might leak memory, needs checking. - widgetGlobals->fl_windows.pop_back(); - } while (j); - Fl_wait_locked(csound, 0.0); - } - widgetGlobals->AddrStack.~vector(); - widgetGlobals->allocatedStrings.~vector(); - widgetGlobals->fl_windows.~vector(); - for (size_t si = 0, sn = widgetGlobals->snapshots.size(); si < sn; ++si) { - SNAPVEC &svec = widgetGlobals->snapshots[si]; - int ss = svec.size(); - for (j = 0; j < ss; j++) { - svec[j].fields.erase(svec[j].fields.begin(), - svec[j].fields.end()); - // VL: probably leaks memory, needs checking. - svec.resize(svec.size() + 1); - } - } - widgetGlobals->AddrSetValue.clear(); // VL: leaks memory, needs fixing. - widgetGlobals->stack_count = 0; - widgetGlobals->FLcontrol_iheight = 15; - widgetGlobals->FLroller_iheight = 18; - widgetGlobals->FLcontrol_iwidth = 400; - widgetGlobals->FLroller_iwidth = 150; - widgetGlobals->FLvalue_iwidth = 100; - widgetGlobals->FLcolor = -1; - widgetGlobals->FLcolor2 = -1; - widgetGlobals->FLtext_size = 0; - widgetGlobals->FLtext_color = -1; - widgetGlobals->FLtext_font = -1; - widgetGlobals->FLtext_align = 0; - widgetGlobals->FL_ix = 10; - widgetGlobals->FL_iy = 10; - - //delete (WIDGET_GLOBALS*)csound->widgetGlobals; - csound->DestroyGlobalVariable(csound, "WIDGET_GLOBALS"); - //csound->widgetGlobals = NULL; - } - return 0; - } -} // extern "C" - -//----------- - -#ifndef NO_FLTK_THREADS - -extern "C" { - - static uintptr_t fltkRun(void *userdata) - { - volatile widgetsGlobals_t *p; - CSOUND *csound = (CSOUND*) userdata; - int j; - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - p = (widgetsGlobals_t*) csound->QueryGlobalVariable(csound, - "_widgets_globals"); -#ifdef LINUX - { - struct sched_param sp; - // IV - Aug 27 2002: widget thread is always run with normal priority - memset(&sp, 0, sizeof(struct sched_param)); - pthread_setschedparam(pthread_self(), SCHED_OTHER, &sp); - } -#endif - - if (!(p->fltkFlags & 8)) - Fl::lock(); - for (j = 0; j < (int) widgetGlobals->fl_windows.size(); j++) { - widgetGlobals->fl_windows[j].panel->show(); - } -// #ifdef CS_VSTHOST -// for (size_t k=0; k < widgetGlobals->VSTplugEditors.size(); k++) { -// int panelNum = widgetGlobals->VSTplugEditors[k]->targetFLpanel; -// #ifdef WIN32 -// HWND xid = fl_xid(widgetGlobals->fl_windows[panelNum].panel); -// widgetGlobals->VSTplugEditors[k]->SetEditWindow(xid); -// #elif defined (LINUX) || defined(MACOSX) -// // put some appropriate alternative code here -// Fl_Window * xid = -// fl_find(fl_xid(widgetGlobals->fl_windows[panelNum].panel)); -// widgetGlobals->VSTplugEditors[k]->SetEditWindow(xid); -// #endif // WIN32 -// } -// #endif // CS_VSTHOST - if (!(p->fltkFlags & 16)) - Fl::awake(); - if (!(p->fltkFlags & 8)) - Fl::unlock(); - do { - if (!(p->fltkFlags & 8)) - Fl::lock(); - Fl::wait(0.02); - j = (Fl::first_window() != (Fl_Window*) 0); - if (!(p->fltkFlags & 8)) - Fl::unlock(); - } while (j && !p->end_of_perf); - csound->Message(csound, "%s", Str("end of widget thread\n")); - // IV - Jun 07 2005: exit if all windows are closed - p->exit_now = -1; - return (uintptr_t) 0; - } - -} // extern "C" - -#endif // NO_FLTK_THREADS - -extern "C" { - - int CsoundYield_FLTK(CSOUND *csound); - - int FL_run(CSOUND *csound, FLRUN *p) - { - IGN(p); - int *fltkFlags; - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - fltkFlags = getFLTKFlagsPtr(csound); - (*fltkFlags) |= 32; -#ifndef NO_FLTK_THREADS - if (((*fltkFlags) & 260) ^ 4) { - widgetsGlobals_t *pp; - - if (UNLIKELY(csound->QueryGlobalVariable(csound, - "_widgets_globals") != NULL)) - return csound->InitError(csound, "%s", Str("FLrun was already called")); - if (UNLIKELY(csound->CreateGlobalVariable(csound, "_widgets_globals", - sizeof(widgetsGlobals_t)) != 0)) - csound->Die(csound, "%s", Str("FL_run: memory allocation failure")); - pp = (widgetsGlobals_t*) csound->QueryGlobalVariable(csound, - "_widgets_globals"); - pp->fltkFlags = *fltkFlags; - /* create thread lock */ - pp->mutex_ = csound->Create_Mutex(0); - /* register callback function to be called by sensevents() */ - csound->RegisterSenseEventCallback(csound, (void (*)(CSOUND *, void *)) - evt_callback, - (void*) pp); - if (!((*fltkFlags) & 256)) { - pp->threadHandle = csound->CreateThread(fltkRun, (void*) csound); - return OK; - } - } -#endif // NO_FLTK_THREADS - { - int j; - - Fl_lock(csound); - for (j = 0; j < (int) widgetGlobals->fl_windows.size(); j++) { - widgetGlobals->fl_windows[j].panel->show(); - } - Fl_wait(csound, 0.0); - Fl_unlock(csound); - - - if (!((*fltkFlags) & 256)) - csound->SetInternalYieldCallback(csound, CsoundYield_FLTK); - } - return OK; - } - - int fl_update(CSOUND *csound, FLRUN *p) - { - IGN(p); - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - Fl_lock(csound); - for (int j=0; j< (int) widgetGlobals->AddrSetValue.size()-1; j++) { - ADDR_SET_VALUE v = widgetGlobals->AddrSetValue[j]; - Fl_Valuator *o = (Fl_Valuator *) v.WidgAddress; - o->do_callback(o, v.opcode); - } - Fl_unlock(csound); - return OK; - } -} // extern "C" - -//---------------------------------------------- - -static inline void displ(MYFLT val, MYFLT index, CSOUND *csound) -{ - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - if (index >= 0) { // display current value of valuator - char valString[MAXNAME]; - sprintf(valString, "%.5g", val); - ((Fl_Output*) (widgetGlobals->AddrSetValue[(long) index]).WidgAddress)-> - value(valString); - } -} - -static void fl_callbackButton1(Fl_Button* w, void *a) -{ - IGN(w); - FLBUTTON *p = (FLBUTTON *) a; - *((FLBUTTON*) a)->kout = *p->ion; - if (*p->args[0] >= 0) ButtonSched(p->h.insdshead->csound, - p->args, p->INOCOUNT-8); -} - -static void fl_callbackCloseButton(Fl_Button* w, void *a) -{ - IGN(w); - Fl_Window *p = (Fl_Window *) a; - p->hide(); -} - -static void fl_callbackExecButton(Fl_Button* w, void *a) -{ - IGN(w); - FLEXECBUTTON *p = (FLEXECBUTTON *)a; - CSOUND *csound = p->csound; - char *command = (char *)csound->Malloc(csound, strlen(p->commandString) + 1); - -#if defined(LINUX) || defined (MACOSX) - - - pid_t pId = vfork(); - if (pId == 0) { - char *th; - char *v[40]; - int i = 0; - - strcpy(command, p->commandString); - - char *tok = csound->strtok_r(command,(char *) " ", &th); - - if(tok != NULL) { - v[i++] = tok; - while((tok = csound->strtok_r(NULL,(char *) " ", &th)) != NULL) { - v[i++] = tok; - } - v[i] = NULL; - execvp(v[0], v); - } - - _exit(0); - } else if (UNLIKELY(pId < 0)) { - p->csound->Message(p->csound, - "%s", Str("Error: Unable to fork process\n")); - } - - csound->Free(csound, command); -#elif defined(WIN32) - { -#undef strtok_r // undefine from pthread.h on Windows - char *th; - char *v[40]; - int i = 0; - - strcpy(command, p->commandString); - char *tok = csound->strtok_r(command,(char *) " ", &th); - - if(tok != NULL) { - v[i++] = tok; - while((tok = csound->strtok_r(NULL,(char *) " ", &th)) != NULL) { - v[i++] = tok; - } - v[i] = NULL; - csound->Free(csound, command); // Otherwise will lose space - if (UNLIKELY(csound->RunCommand(v, 1)<0)) - p->csound->Message(p->csound, "%s", - Str("Error: Unable to fork process\n")); - } - } -#endif -} - -static void fl_callbackButton(Fl_Button* w, void *a) -{ - FLBUTTON *p = (FLBUTTON *) a; - *((FLBUTTON*) a)->kout = (w->value()) ? *p->ion : *p->ioff; - if (*p->args[0] >= 0) ButtonSched(p->h.insdshead->csound, - p->args, p->INOCOUNT-8); -} - -static void fl_callbackButtonBank(Fl_Button* w, void *a) -{ - FLBUTTONBANK *p = (FLBUTTONBANK *) a; - *((FLBUTTONBANK*) a)->kout = (MYFLT) atoi(w->label()); - if (*p->args[0] >= 0) ButtonSched(p->h.insdshead->csound, - p->args, p->INOCOUNT-7); -} - -static void fl_callbackCounter(Fl_Counter* w, void *a) -{ - FLCOUNTER *p = (FLCOUNTER *) a; - *((FLCOUNTER*) a)->kout = w->value(); - if (*p->args[0] >= 0) ButtonSched(p->h.insdshead->csound, - p->args, p->INOCOUNT-10); -} - -static void fl_callbackLinearSlider(Fl_Valuator* w, void *a) -{ - FLSLIDER *p = ((FLSLIDER*) a); - displ(*p->kout = w->value(), *p->idisp, p->h.insdshead->csound); -} - -static void fl_callbackExponentialSlider(Fl_Valuator* w, void *a) -{ - FLSLIDER *p = ((FLSLIDER*) a); - #if defined(sun) - displ(*p->kout = p->min * ::pow ((double)p->base, w->value()), - *p->idisp, p->h.insdshead->csound); - #else - displ(*p->kout = p->min * ::pow (p->base, w->value()), - *p->idisp, p->h.insdshead->csound); - #endif -} - -static void fl_callbackInterpTableSlider(Fl_Valuator* w, void *a) -{ - FLSLIDER *p = ((FLSLIDER*) a); - MYFLT ndx = w->value() * (p->tablen-1); - int index = (int) ndx; - MYFLT v1 = p->table[index]; - displ(*p->kout = p->min+ ( v1 + (p->table[index+1] - v1) * - (ndx - index)) * (*p->imax - p->min), - *p->idisp, p->h.insdshead->csound); -} - -static void fl_callbackTableSlider(Fl_Valuator* w, void *a) -{ - FLSLIDER *p = ((FLSLIDER*) a); - displ(*p->kout = p->min+ p->table[(long) (w->value() * p->tablen)] * - (*p->imax - p->min), - *p->idisp, p->h.insdshead->csound); -} - -static void fl_callbackLinearSliderBank(Fl_Valuator* w, void *a) -{ - SLDBK_ELEMENT* p = (SLDBK_ELEMENT*) a; - *p->out = w->value(); -} - -static void fl_callbackExponentialSliderBank(Fl_Valuator* w, void *a) -{ - SLDBK_ELEMENT* p = (SLDBK_ELEMENT*) a; - #if defined(sun) - *p->out = p->min * ::pow ((double)p->base, w->value()); - #else - *p->out = p->min * ::pow (p->base, w->value()); - #endif -} - -static void fl_callbackInterpTableSliderBank(Fl_Valuator* w, void *a) -{ - SLDBK_ELEMENT *p = ((SLDBK_ELEMENT*) a); - - MYFLT ndx = w->value() * (p->tablen-1); - int index = (int) ndx; - MYFLT v1 = p->table[index]; - *p->out = p->min + ( v1 + (p->table[index+1] - v1) * (ndx - index)) * - (p->max - p->min); -} - -static void fl_callbackTableSliderBank(Fl_Valuator* w, void *a) -{ - SLDBK_ELEMENT *p = ((SLDBK_ELEMENT*) a); - *p->out = p->min + p->table[(long)(w->value() * p->tablen)] * - (p->max - p->min); -} - -static void fl_callbackJoystick(Fl_Widget* w, void *a) -{ - FLJOYSTICK *p = (FLJOYSTICK*) a; - Fl_Positioner *j = (Fl_Positioner*) w; - MYFLT val; - int iexpx = (int) *p->iexpx, iexpy = (int) *p->iexpy; - switch (iexpx) { - case LIN_: - val = j->xvalue(); - break; - case EXP_: - #if defined(sun) - val = *p->iminx * ::pow ((double)p->basex, j->xvalue()); - #else - val = *p->iminx * ::pow (p->basex, j->xvalue()); - #endif - break; - default: - if (iexpx > 0) { //interpolated - MYFLT ndx = j->xvalue() * (p->tablenx-1); - int index = (int) ndx; - MYFLT v1 = p->tablex[index]; - val = *p->iminx + ( v1 + (p->tablex[index+1] - v1) * - (ndx - index)) * (*p->imaxx - *p->iminx); - } - else // non-interpolated - val = *p->iminx+ p->tablex[(long) (j->xvalue() * p->tablenx)] * - (*p->imaxx - *p->iminx); - } - displ(*p->koutx = val,*p->idispx, p->h.insdshead->csound); - switch (iexpy) { - case LIN_: - val = j->yvalue(); - break; - case EXP_: - #if defined(sun) - val = *p->iminy * ::pow ((double)p->basey, j->yvalue()); - #else - val = *p->iminy * ::pow (p->basey, j->yvalue()); - #endif - break; - default: - if (iexpy > 0) { //interpolated - MYFLT ndx = j->yvalue() * (p->tableny-1); - long index = (long) ndx; - MYFLT v1 = p->tabley[index]; - val = *p->iminy + ( v1 + (p->tabley[index+1] - v1) * (ndx - index)) - * (*p->imaxy - *p->iminy); - } - else { // non-interpolated - long index = (long) (j->yvalue()* p->tableny); - val = *p->iminy+ p->tabley[index] * (*p->imaxy - *p->iminy); - - } - } - displ(*p->kouty = val, *p->idispy, p->h.insdshead->csound); -} - -static void fl_callbackLinearRoller(Fl_Valuator* w, void *a) -{ - FLROLLER *p = ((FLROLLER*) a); - displ(*p->kout = w->value(),*p->idisp, p->h.insdshead->csound); -} - -static void fl_callbackExponentialRoller(Fl_Valuator* w, void *a) -{ - FLROLLER *p = ((FLROLLER*) a); - #if defined(sun) - displ(*p->kout = ((FLROLLER*) a)->min * ::pow ((double)p->base, w->value()), - *p->idisp, p->h.insdshead->csound); - #else - displ(*p->kout = ((FLROLLER*) a)->min * ::pow (p->base, w->value()), - *p->idisp, p->h.insdshead->csound); - #endif -} - -static void fl_callbackInterpTableRoller(Fl_Valuator* w, void *a) -{ - FLROLLER *p = ((FLROLLER*) a); - MYFLT ndx = w->value() * (p->tablen-1); - int index = (int) ndx; - MYFLT v1 = p->table[index]; - displ(*p->kout = p->min+ ( v1 + (p->table[index+1] - v1) * (ndx - index)) * - (*p->imax - p->min), *p->idisp, p->h.insdshead->csound); -} - -static void fl_callbackTableRoller(Fl_Valuator* w, void *a) -{ - FLROLLER *p = ((FLROLLER*) a); - displ(*p->kout = p->min+ p->table[(long) (w->value() * p->tablen)] * - (*p->imax - p->min), *p->idisp, p->h.insdshead->csound); -} - -static void fl_callbackLinearKnob(Fl_Valuator* w, void *a) -{ - FLKNOB *p = ((FLKNOB*) a); - displ( *p->kout = w->value(), *p->idisp, p->h.insdshead->csound); -} - -static void fl_callbackExponentialKnob(Fl_Valuator* w, void *a) -{ - FLKNOB *p = ((FLKNOB*) a); - #if defined(sun) - displ(*p->kout = ((FLKNOB*) a)->min * ::pow ((double)p->base, w->value()), - *p->idisp, p->h.insdshead->csound); - #else - displ(*p->kout = ((FLKNOB*) a)->min * ::pow (p->base, w->value()), - *p->idisp, p->h.insdshead->csound); - #endif -} - -static void fl_callbackInterpTableKnob(Fl_Valuator* w, void *a) -{ - FLKNOB *p = ((FLKNOB*) a); - MYFLT ndx = w->value() * (p->tablen-1); - int index = (int) ndx; - MYFLT v1 = p->table[index]; - displ(*p->kout = p->min+ ( v1 + (p->table[index+1] - v1) * (ndx - index)) * - (*p->imax - p->min), *p->idisp, p->h.insdshead->csound); -} - -static void fl_callbackTableKnob(Fl_Valuator* w, void *a) -{ - FLKNOB *p = ((FLKNOB*) a); - displ(*p->kout = p->min+ p->table[(long) (w->value() * p->tablen)] * - (*p->imax - p->min), *p->idisp, p->h.insdshead->csound); -} - -static void fl_callbackLinearValueInput(Fl_Valuator* w, void *a) -{ - *((FLTEXT*) a)->kout = w->value(); -} - -//----------- - -static int rand_31_i(CSOUND *csound, int maxVal) -{ - int seed = csound->GetRandSeed(csound,2); - double x = (double) (csound->Rand31(&seed) - 1); - return (int) (x * (double) (maxVal + 1) / 2147483646.0); -} - -static void widget_attributes(CSOUND *csound, Fl_Widget *o) -{ - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - if (widgetGlobals->FLtext_size == -2 ) { - widgetGlobals->FLtext_size = -1; - widgetGlobals->FLtext_color= -1; - widgetGlobals->FLtext_font = -1; - widgetGlobals->FLtext_align= -1; - widgetGlobals->FLcolor = -1; - } - if (widgetGlobals->FLtext_size > 0) - // if > 0 assign it, else skip, leaving default - o->labelsize(widgetGlobals->FLtext_size); - switch ((int) widgetGlobals->FLtext_color) { - case -2: // random color - o->labelcolor(fl_rgb_color(rand_31_i(csound, 255), rand_31_i(csound, 255), - rand_31_i(csound, 255))); - break; - case -1: - // if FLtext_color is == -1, color assignment is skipped, - // leaving default color - break; - default: - o->labelcolor(widgetGlobals->FLtext_color); - break; - } - if (widgetGlobals->FLtext_font> 0) { - Fl_Font font; - if (widgetGlobals->FLtext_font<0 || - widgetGlobals->FLtext_font>16) font = FL_HELVETICA; - else font = FONT_TABLE[widgetGlobals->FLtext_font]; - // switch (FLtext_font) { - // case 1: font = FL_HELVETICA; break; - // case 2: font = FL_HELVETICA_BOLD; break; - // case 3: font = FL_HELVETICA_ITALIC; break; - // case 4: font = FL_HELVETICA_BOLD_ITALIC; break; - // case 5: font = FL_COURIER; break; - // case 6: font = FL_COURIER_BOLD; break; - // case 7: font = FL_COURIER_ITALIC; break; - // case 8: font = FL_COURIER_BOLD_ITALIC; break; - // case 9: font = FL_TIMES; break; - // case 10: font = FL_TIMES_BOLD; break; - // case 11: font = FL_TIMES_ITALIC; break; - // case 12: font = FL_TIMES_BOLD_ITALIC; break; - // case 13: font = FL_SYMBOL; break; - // case 14: font = FL_SCREEN; break; - // case 15: font = FL_SCREEN_BOLD; break; - // case 16: font = FL_ZAPF_DINGBATS; break; - // default: font = FL_HELVETICA; break; - // } - o->labelfont(font); - } - if (widgetGlobals->FLtext_align > 0) { - Fl_Align type; - if (widgetGlobals->FLtext_align<0 || - widgetGlobals->FLtext_align>9) type = FL_ALIGN_BOTTOM; - else type = ALIGN_TABLE[widgetGlobals->FLtext_align]; - // switch (widgetGlobals->FLtext_align) { - // case 1: type = FL_ALIGN_CENTER; break; - // case 2: type = FL_ALIGN_TOP; break; - // case 3: type = FL_ALIGN_BOTTOM; break; - // case 4: type = FL_ALIGN_LEFT; break; - // case 5: type = FL_ALIGN_RIGHT; break; - // case 6: type = FL_ALIGN_TOP_LEFT; break; - // case 7: type = FL_ALIGN_TOP_RIGHT; break; - // case 8: type = FL_ALIGN_BOTTOM_LEFT; break; - // case 9: type = FL_ALIGN_BOTTOM_RIGHT; break; - // case -1: // What type is this? - // default: type = FL_ALIGN_BOTTOM; break; - // } - o->align(type); - } - switch ((int) widgetGlobals->FLcolor) { // random color - case -2: - o->color(FL_GRAY, - fl_rgb_color(rand_31_i(csound, 255), rand_31_i(csound, 255), - rand_31_i(csound, 255))); - break; - case -1: - // if FLcolor is == -1, color assignment is skipped, - // leaving widget default color - break; - default: - o->color(widgetGlobals->FLcolor, widgetGlobals->FLcolor2); - break; - } -} - -//----------- - -extern "C" { - - // static int FLkeyb(CSOUND *csound, FLKEYB *p) - // { - // (void) csound; - // (void) p; - // return OK; - // } - - //----------- - - void flpanel_cb(Fl_Widget *,void *) { //suppresses the close button - } - - static int StartPanel(CSOUND *csound, FLPANEL *p) - { - char *panelName; - panelName = p->name->data; - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - - *(getFLTKFlagsPtr(csound)) |= 32; - int x = (int) *p->ix, y = (int) *p->iy; - int width = (int) *p->iwidth, height = (int) *p->iheight; - if (width < 0) width = 400; // default - if (height < 0) height = 300; - - Fl_Boxtype borderType; - int iborder = (int) *p->border; - if (iborder<0 || iborder>7) borderType = FL_FLAT_BOX; - else borderType = BOX_TABLE[iborder]; - // switch( (int) *p->border ) { - // case 0: borderType = FL_FLAT_BOX; break; - // case 1: borderType = FL_DOWN_BOX; break; - // case 2: borderType = FL_UP_BOX; break; - // case 3: borderType = FL_ENGRAVED_BOX; break; - // case 4: borderType = FL_EMBOSSED_BOX; break; - // case 5: borderType = FL_BORDER_BOX; break; - // case 6: borderType = FL_THIN_DOWN_BOX; break; - // case 7: borderType = FL_THIN_UP_BOX; break; - // default: borderType = FL_FLAT_BOX; - // } - - Fl_Window *o; - if (*(p->ikbdsense) == MYFLT(0.0)) { - if (x < 0) - o = new Fl_Window(width, height, panelName); - else - o = new Fl_Window(x, y, width, height, panelName); - } - else if (x < 0) - o = new CsoundFLWindow(csound, width, height, panelName); - else - o = new CsoundFLWindow(csound, x, y, width, height, panelName); - widget_attributes(csound, o); - o->box(borderType); - o->resizable(o); - if (*p->iclose != 0) - o->callback(flpanel_cb); - widget_attributes(csound, o); - ADDR_STACK adrstk(&p->h, (void *) o, widgetGlobals->stack_count); - widgetGlobals->AddrStack.push_back(adrstk); - PANELS panel(o, (widgetGlobals->stack_count > 0) ? 1 : 0); - widgetGlobals->fl_windows.push_back(panel); - widgetGlobals->stack_count++; - - return OK; - } - - static int EndPanel(CSOUND *csound, FLPANELEND *p) - { - IGN(p); - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - widgetGlobals->stack_count--; - ADDR_STACK adrstk = widgetGlobals->AddrStack.back(); - if (UNLIKELY(adrstk.h->optext->t.opcod && - strcmp( adrstk.h->optext->t.opcod, "FLpanel"))) - return csound->InitError(csound, - "%s", Str("FLpanel_end: invalid stack pointer: " - "verify its placement")); - if (UNLIKELY(adrstk.count != widgetGlobals->stack_count)) - return csound->InitError(csound, - "%s", Str("FLpanel_end: invalid stack count: " - "verify FLpanel/FLpanel_end count and" - " placement")); - ((Fl_Window*) adrstk.WidgAddress)->end(); - widgetGlobals->AddrStack.pop_back(); - return OK; - } - - //----------- - static int StartScroll(CSOUND *csound, FLSCROLL *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - Fl_Scroll *o = new Fl_Scroll ((int) *p->ix, (int) *p->iy, - (int) *p->iwidth, (int) *p->iheight); - ADDR_STACK adrstk(&p->h,o,widgetGlobals->stack_count); - widgetGlobals->AddrStack.push_back(adrstk); - widgetGlobals->stack_count++; - return OK; - } - - static int EndScroll(CSOUND *csound, FLSCROLLEND *p) - { - IGN(p); - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - widgetGlobals->stack_count--; - ADDR_STACK adrstk = widgetGlobals->AddrStack.back(); - if (UNLIKELY(strcmp( adrstk.h->optext->t.opcod, "FLscroll"))) - return - csound->InitError(csound, - "%s", Str("FLscroll_end: invalid stack pointer: " - "verify its placement")); - if (UNLIKELY(adrstk.count != widgetGlobals->stack_count)) - return csound->InitError(csound, - "%s", Str("FLscroll_end: invalid stack count: " - "verify FLscroll/FLscroll_end count " - "and placement")); - ((Fl_Scroll*) adrstk.WidgAddress)->end(); - - widgetGlobals->AddrStack.pop_back(); - return OK; - } - - //----------- - static int StartTabs(CSOUND *csound, FLTABS *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - Fl_Tabs *o = new Fl_Tabs ((int) *p->ix, (int) *p->iy, - (int) *p->iwidth, (int) *p->iheight); - widget_attributes(csound, o); - // o->box(FL_PLASTIC_UP_BOX); - ADDR_STACK adrstk(&p->h,o,widgetGlobals->stack_count); - widgetGlobals->AddrStack.push_back(adrstk); - widgetGlobals->stack_count++; - return OK; - } - - static int EndTabs(CSOUND *csound, FLTABSEND *p) - { - IGN(p); - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - widgetGlobals->stack_count--; - ADDR_STACK adrstk = widgetGlobals->AddrStack.back(); - if (UNLIKELY(strcmp( adrstk.h->optext->t.opcod, "FLtabs"))) - return - csound->InitError(csound, - "%s", Str("FLscroll_end: invalid stack pointer: " - "verify its placement")); - if (UNLIKELY(adrstk.count != widgetGlobals->stack_count)) - return csound->InitError(csound, - "%s", Str("FLtabs_end: invalid stack count: " - "verify FLtabs/FLtabs_end count and " - "placement")); - ((Fl_Scroll*) adrstk.WidgAddress)->end(); - - widgetGlobals->AddrStack.pop_back(); - return OK; - } - - //----------- - static int StartGroup(CSOUND *csound, FLGROUP *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - char *Name = p->name->data; - Fl_Group *o = new Fl_Group ((int) *p->ix, (int) *p->iy, - (int) *p->iwidth, (int) *p->iheight,Name); - widget_attributes(csound, o); - Fl_Boxtype borderType; - int iborder = (int)*p->border; - if (iborder<0 || iborder>7) borderType = FL_FLAT_BOX; - else borderType = BOX_TABLE[iborder]; - // switch((int)*p->border ) { - // case 0: borderType = FL_FLAT_BOX; break; - // case 1: borderType = FL_DOWN_BOX; break; - // case 2: borderType = FL_UP_BOX; break; - // case 3: borderType = FL_ENGRAVED_BOX; break; - // case 4: borderType = FL_EMBOSSED_BOX; break; - // case 5: borderType = FL_BORDER_BOX; break; - // case 6: borderType = FL_THIN_DOWN_BOX; break; - // case 7: borderType = FL_THIN_UP_BOX; break; - // default: borderType = FL_FLAT_BOX; - // } - o->box(borderType); - widget_attributes(csound, o); - ADDR_STACK adrstk(&p->h,o,widgetGlobals->stack_count); - widgetGlobals->AddrStack.push_back(adrstk); - widgetGlobals->stack_count++; - return OK; - } - - static int EndGroup(CSOUND *csound, FLGROUPEND *p) - { - IGN(p); - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - widgetGlobals->stack_count--; - ADDR_STACK adrstk = widgetGlobals->AddrStack.back(); - if (UNLIKELY(strcmp( adrstk.h->optext->t.opcod, "FLgroup"))) - return csound->InitError(csound, - "%s", Str("FLgroup_end: invalid stack pointer: " - "verify its placement")); - if (UNLIKELY(adrstk.count != widgetGlobals->stack_count)) - return csound->InitError(csound, - "%s", Str("FLgroup_end: invalid stack count: " - "verify FLgroup/FLgroup_end count and" - " placement")); - ((Fl_Scroll*) adrstk.WidgAddress)->end(); - - widgetGlobals->AddrStack.pop_back(); - return OK; - } - - //----------- - - static int StartPack(CSOUND *csound, FLPACK *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - Fl_Pack *o = new Fl_Pack ((int) *p->ix, (int) *p->iy, - (int) *p->iwidth, (int) *p->iheight); - Fl_Boxtype borderType = FL_FLAT_BOX; - int iborder = (int)*p->iborder; - // fl_window->resizable(o); - if (!((iborder<0 || iborder>7))) - borderType = BOX_TABLE[iborder]; - o->box(borderType); // JPff added March 2012 - o->type((int)*p->itype); - o->spacing((int)*p->ispace); - - ADDR_STACK adrstk(&p->h,o,widgetGlobals->stack_count); - widgetGlobals->AddrStack.push_back(adrstk); - widgetGlobals->stack_count++; - return OK; - } - - static int EndPack(CSOUND *csound, FLSCROLLEND *p) - { - IGN(p); - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - widgetGlobals->stack_count--; - ADDR_STACK adrstk = widgetGlobals->AddrStack.back(); - if (UNLIKELY(strcmp( adrstk.h->optext->t.opcod, "FLpack"))) - return csound->InitError(csound, - "%s", Str("FLpack_end: invalid stack pointer: " - "verify its placement")); - if (UNLIKELY(adrstk.count != widgetGlobals->stack_count)) - return csound->InitError(csound, - "%s", Str("FLpack_end: invalid stack count: " - "verify FLpack/FLpack_end count and " - "placement")); - ((Fl_Pack*) adrstk.WidgAddress)->end(); - - widgetGlobals->AddrStack.pop_back(); - return OK; - } - - //----------- - - static int fl_widget_color(CSOUND *csound, FLWIDGCOL *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - if (*p->red1 < 0) { // reset colors to default - widgetGlobals->FLcolor = (int) *p->red1; //when called without arguments - widgetGlobals->FLcolor2 =(int) *p->red1; - } - else { - widgetGlobals->FLcolor = fl_rgb_color((int) *p->red1, - (int) *p->green1, - (int) *p->blue1); - widgetGlobals->FLcolor2 = fl_rgb_color((int) *p->red2, - (int) *p->green2, - (int) *p->blue2); - } - return OK; - } - - static int fl_widget_color2(CSOUND *csound, FLWIDGCOL2 *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - if (*p->red < 0) { // reset colors to default - widgetGlobals->FLcolor2 =(int) *p->red; - } - else { - widgetGlobals->FLcolor2 = fl_rgb_color((int) *p->red, - (int) *p->green, - (int) *p->blue); - } - return OK; - } - - static int fl_widget_label(CSOUND *csound, FLWIDGLABEL *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - if (*p->size <= 0) { // reset settings to default - widgetGlobals->FLtext_size = 0; //when called without arguments - widgetGlobals->FLtext_font = -1; - widgetGlobals->FLtext_align = 0; - widgetGlobals->FLtext_color = -1; - } - else { - widgetGlobals->FLtext_size = (int) *p->size; - - if (*p->font > -1) widgetGlobals->FLtext_font = (int) *p->font; - if (*p->align > 0) widgetGlobals->FLtext_align = (int) *p->align; - if (*p->red > -1 && *p->green > -1 && *p->blue > -1) { - widgetGlobals->FLtext_color = fl_rgb_color((int) *p->red, - (int) *p->green, - (int) *p->blue); - } - } - return OK; - } - -} // extern "C" - -// ----------- - -static int fl_getWidgetTypeFromOpcodeName(CSOUND *csound, void *p) -{ - const char *opname = csound->GetOpcodeName(p); - - if (strcmp(opname, "FLbutton") == 0) - return 1; - if (strcmp(opname, "FLbutBank") == 0) - return 2; - if (strcmp(opname, "FLjoy") == 0) - return 3; - if (strcmp(opname, "FLvalue") == 0) - return 4; - if (strcmp(opname, "FLbox") != 0) - return 0; - csound->Warning(csound, "%s", Str("System error: value() method called from " - "non-valuator object")); - return -1; -} - -static void fl_setWidgetValue_(CSOUND *csound, - ADDR_SET_VALUE &v, int widgetType, - MYFLT val, MYFLT log_base) -{ - Fl_Widget *o = (Fl_Widget *) v.WidgAddress; - void *p = v.opcode; - bool fltkLockingIsEnabled; - - if ((!widgetType || widgetType > 2) && - (v.exponential == LIN_ || v.exponential == EXP_)) { - if (val < v.min) - val = v.min; - else if (val > v.max) - val = v.max; - if (v.exponential == EXP_) - val = (MYFLT) (log(val / v.min) / log_base); - } - fltkLockingIsEnabled = ((getFLTKFlags(csound) & 8) == 0); - if (fltkLockingIsEnabled) - Fl_lock(csound); - switch (widgetType) { - case 0: // valuator - ((Fl_Valuator *) o)->value(val); - break; - case 1: // FLbutton - if (val == *(((FLBUTTON *) v.opcode)->ion)) - ((Fl_Button *) o)->value(1); - else if (val == *(((FLBUTTON *) v.opcode)->ioff)) - ((Fl_Button *) o)->value(0); - break; - case 2: // FLbutBank - set_butbank_value((Fl_Group *) o, val); - break; - case 3: // FLjoy - { - static int flag = 0; - // FLsetVal always requires two adjacent calls when setting FLjoy - if (!flag) { - ((Fl_Positioner *) o)->xvalue(val); - flag = 1; - } - else { - ((Fl_Positioner *) o)->yvalue(val); - flag = 0; - } - } - break; - default: // invalid - if (fltkLockingIsEnabled) - Fl_unlock(csound); - return; - } - o->do_callback(o, p); - if (fltkLockingIsEnabled) - Fl_unlock(csound); -} - -extern "C" { - - static int fl_setWidgetValuei(CSOUND *csound, FL_SET_WIDGET_VALUE_I *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - MYFLT log_base = MYFLT(1.0); - ADDR_SET_VALUE &v = widgetGlobals->AddrSetValue[(int) *p->ihandle]; - int widgetType; - - widgetType = fl_getWidgetTypeFromOpcodeName(csound, v.opcode); - if (UNLIKELY(widgetType == 4)) { - csound->InitError(csound, - "%s", Str("FLvalue cannot be set by FLsetVal.\n")); - return NOTOK; - } - if (widgetType < 0) - return OK; - if (!widgetType || widgetType > 2) { - switch (v.exponential) { - case LIN_: // linear - break; - case EXP_: // exponential - #if defined(sun) - log_base = (MYFLT) log(::pow(v.max / (double)v.min, - 1.0 / (v.max - v.min))); - #else - log_base = (MYFLT) log(::pow(v.max / v.min, 1.0 / (v.max - v.min))); - #endif - break; - default: - csound->Warning(csound, Str("(fl_setWidgetValuei): " - "not fully implemented yet; exp=%d"), - v.exponential); - } - } - fl_setWidgetValue_(csound, v, widgetType, *(p->ivalue), log_base); - return OK; - } - - static int fl_setWidgetValue_set(CSOUND *csound, FL_SET_WIDGET_VALUE *p) - { - p->handle = (int) *(p->ihandle); - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - MYFLT log_base = MYFLT(1.0); - ADDR_SET_VALUE &v = widgetGlobals->AddrSetValue[p->handle]; - int widgetType; - - widgetType = fl_getWidgetTypeFromOpcodeName(csound, v.opcode); - if (UNLIKELY(widgetType == 4)) { - csound->InitError(csound, - "%s", Str("FLvalue cannot be set by FLsetVal\n")); - return NOTOK; - } - if (widgetType < 0) - return OK; - if (!widgetType || widgetType > 2) { - switch (v.exponential) { - case LIN_: // linear - break; - case EXP_: // exponential - #if defined(sun) - log_base = (MYFLT) log(::pow(v.max / (double)v.min, - 1.0 / (v.max - v.min))); - #else - log_base = (MYFLT) log(::pow(v.max / v.min, 1.0 / (v.max - v.min))); - #endif - break; - default: - csound->Warning(csound, Str("(fl_setWidgetValue_set): " - "not fully implemented yet; exp=%d"), - v.exponential); - } - } - p->widgetType = widgetType; - p->log_base = log_base; - - return OK; - } - - static int fl_setWidgetValue(CSOUND *csound, FL_SET_WIDGET_VALUE *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - if (*p->ktrig != MYFLT(0.0)) - fl_setWidgetValue_(csound, - widgetGlobals->AddrSetValue[p->handle], p->widgetType, - *(p->kvalue), p->log_base); - return OK; - } - - //----------- - //----------- - - static int fl_setColor1(CSOUND *csound, FL_SET_COLOR *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - ADDR_SET_VALUE v = widgetGlobals->AddrSetValue[(int) *p->ihandle]; - Fl_Widget *o = (Fl_Widget *) v.WidgAddress; - int color = fl_rgb_color((int) *p->red, - (int) *p->green, - (int) *p->blue); - o->color(color); - o->redraw(); - return OK; - } - - static int fl_setColor2(CSOUND *csound, FL_SET_COLOR *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - ADDR_SET_VALUE v = widgetGlobals->AddrSetValue[(int) *p->ihandle]; - Fl_Widget *o = (Fl_Widget *) v.WidgAddress; - int color = fl_rgb_color((int) *p->red, (int) *p->green, (int) *p->blue); - o->selection_color(color); - o->redraw(); - return OK; - } - - static int fl_setTextColor(CSOUND *csound, FL_SET_COLOR *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - ADDR_SET_VALUE v = widgetGlobals->AddrSetValue[(int) *p->ihandle]; - Fl_Widget *o = (Fl_Widget *) v.WidgAddress; - int color = fl_rgb_color((int) *p->red, (int) *p->green, (int) *p->blue); - o->labelcolor(color); - o->window()->redraw(); - return OK; - } - - static int fl_setTextSize(CSOUND *csound, FL_SET_TEXTSIZE *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - ADDR_SET_VALUE v = widgetGlobals->AddrSetValue[(int) *p->ihandle]; - Fl_Widget *o = (Fl_Widget *) v.WidgAddress; - o->labelsize((uchar) *p->ivalue); - return OK; - } - - static int fl_setFont(CSOUND *csound, FL_SET_FONT *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - ADDR_SET_VALUE v = widgetGlobals->AddrSetValue[(int) *p->ihandle]; - Fl_Widget *o = (Fl_Widget *) v.WidgAddress; - Fl_Font font; - int ifnt = (int) *p->itype; - if (ifnt<0 || ifnt>16) font = FL_HELVETICA; - else font = FONT_TABLE[ifnt]; - // switch ((int) *p->itype) { - // case 1: font = FL_HELVETICA; break; - // case 2: font = FL_HELVETICA_BOLD; break; - // case 3: font = FL_HELVETICA_ITALIC; break; - // case 4: font = FL_HELVETICA_BOLD_ITALIC; break; - // case 5: font = FL_COURIER; break; - // case 6: font = FL_COURIER_BOLD; break; - // case 7: font = FL_COURIER_ITALIC; break; - // case 8: font = FL_COURIER_BOLD_ITALIC; break; - // case 9: font = FL_TIMES; break; - // case 10: font = FL_TIMES_BOLD; break; - // case 11: font = FL_TIMES_ITALIC; break; - // case 12: font = FL_TIMES_BOLD_ITALIC; break; - // case 13: font = FL_SYMBOL; break; - // case 14: font = FL_SCREEN; break; - // case 15: font = FL_SCREEN_BOLD; break; - // case 16: font = FL_ZAPF_DINGBATS; break; - // default: font = FL_SCREEN; - // } - o->labelfont(font); - return OK; - } - - static int fl_setTextType(CSOUND *csound, FL_SET_FONT *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - ADDR_SET_VALUE v = widgetGlobals->AddrSetValue[(int) *p->ihandle]; - Fl_Widget *o = (Fl_Widget *) v.WidgAddress; - Fl_Labeltype type; - switch ((int) *p->itype) { - case 0: type = FL_NORMAL_LABEL; break; - case 1: type = FL_NO_LABEL; break; - case 2: type = FL_SYMBOL_LABEL; break; - case 3: type = FL_SHADOW_LABEL; break; - case 4: type = FL_ENGRAVED_LABEL; break; - case 5: type = FL_EMBOSSED_LABEL; break; - /* case 6: type = _FL_BITMAP_LABEL; break; - case 7: type = _FL_PIXMAP_LABEL; break; - case 8: type = _FL_IMAGE_LABEL; break; - case 9: type = _FL_MULTI_LABEL; break; */ - case 10: type = FL_FREE_LABELTYPE; break; - default: type = FL_NORMAL_LABEL; - } - o->labeltype(type); - o->window()->redraw(); - return OK; - } - - static int fl_box_(CSOUND *csound, FL_BOX *p, char *text) - { - //char *text = p->itext->data; - Fl_Box *o = new Fl_Box((int)*p->ix, (int)*p->iy, - (int)*p->iwidth, (int)*p->iheight, strdup(text)); - widget_attributes(csound, o); - Fl_Boxtype type; - int itype = (int) *p->itype; - if (itype<0 || itype>19) type = FL_FLAT_BOX; - else type = BOX_TABLE[itype]; - // switch ((int) *p->itype) { - // case 1: type = FL_FLAT_BOX; break; - // case 2: type = FL_UP_BOX; break; - // case 3: type = FL_DOWN_BOX; break; - // case 4: type = FL_THIN_UP_BOX; break; - // case 5: type = FL_THIN_DOWN_BOX; break; - // case 6: type = FL_ENGRAVED_BOX; break; - // case 7: type = FL_EMBOSSED_BOX; break; - // case 8: type = FL_BORDER_BOX; break; - // case 9: type = _FL_SHADOW_BOX; break; - // case 10: type = _FL_ROUNDED_BOX; break; - // case 11: type = _FL_RSHADOW_BOX; break; - // case 12: type = _FL_RFLAT_BOX; break; - // case 13: type = _FL_ROUND_UP_BOX; break; - // case 14: type = _FL_ROUND_DOWN_BOX; break; - // case 15: type = _FL_DIAMOND_UP_BOX; break; - // case 16: type = _FL_DIAMOND_DOWN_BOX; break; - // case 17: type = _FL_OVAL_BOX; break; - // case 18: type = _FL_OSHADOW_BOX; break; - // case 19: type = _FL_OFLAT_BOX; break; - // default: type = FL_FLAT_BOX; - // } - o->box(type); - Fl_Font font; - int ifnt = (int) *p->ifont; - if (ifnt<0 || ifnt>16) font = FL_HELVETICA; - else font = FONT_TABLE[ifnt]; - // switch ((int) *p->ifont) { - // case 1: font = FL_HELVETICA; break; - // case 2: font = FL_HELVETICA_BOLD; break; - // case 3: font = FL_HELVETICA_ITALIC; break; - // case 4: font = FL_HELVETICA_BOLD_ITALIC; break; - // case 5: font = FL_COURIER; break; - // case 6: font = FL_COURIER_BOLD; break; - // case 7: font = FL_COURIER_ITALIC; break; - // case 8: font = FL_COURIER_BOLD_ITALIC; break; - // case 9: font = FL_TIMES; break; - // case 10: font = FL_TIMES_BOLD; break; - // case 11: font = FL_TIMES_ITALIC; break; - // case 12: font = FL_TIMES_BOLD_ITALIC; break; - // case 13: font = FL_SYMBOL; break; - // case 14: font = FL_SCREEN; break; - // case 15: font = FL_SCREEN_BOLD; break; - // case 16: font = FL_ZAPF_DINGBATS; break; - // default: font = FL_HELVETICA; - // } - o->labelfont(font); - o->labelsize((unsigned char)*p->isize); - o->align(FL_ALIGN_WRAP); - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - widgetGlobals->AddrSetValue.push_back(ADDR_SET_VALUE(0, 0, 0, (void *)o, - (void *)p, - widgetGlobals->currentSnapGroup)); - *p->ihandle = widgetGlobals->AddrSetValue.size()-1; - return OK; - } - - static int fl_box_s(CSOUND *csound, FL_BOX *p) - { - return fl_box_(csound, p, p->itext->data); - } - static int fl_box_i(CSOUND *csound, FL_BOX *p) - { - int i = (int)*((MYFLT*)p->itext); - char* text; - if (i<0 || i>csound->GetStrsmax(csound)) text = (char *) "???"; - else if ((text=csound->GetStrsets(csound,i))==NULL) text = (char *) "???"; - return fl_box_(csound, p, text); - } - - static int fl_setText(CSOUND *csound, FL_SET_TEXT *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - char *text = strdup(p->itext->data); - ADDR_SET_VALUE v = widgetGlobals->AddrSetValue[(int) *p->ihandle]; - Fl_Widget *o = (Fl_Widget *) v.WidgAddress; - free((void*)o->label()); - o->label(text); - return OK; - } - - static int fl_setTexti(CSOUND *csound, FL_SET_TEXTi *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - int i = (int)(*p->ndx); - char *text ; - ADDR_SET_VALUE v = widgetGlobals->AddrSetValue[(int) *p->ihandle]; - Fl_Widget *o = (Fl_Widget *) v.WidgAddress; - if (i<0 || i>csound->GetStrsmax(csound)) text = (char *) "???"; - else if ((text=csound->GetStrsets(csound,i))==NULL) text = (char *) "???"; - free((void*)o->label()); - o->label(strdup(text)); - return OK; - } - - static int fl_setSize(CSOUND *csound, FL_SET_SIZE *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - ADDR_SET_VALUE v = widgetGlobals->AddrSetValue[(int) *p->ihandle]; - Fl_Widget *o = (Fl_Widget *) v.WidgAddress; - o->size((short) *p->iwidth, (short) *p->iheight); - return OK; - } - - static int fl_setPosition(CSOUND *csound, FL_SET_POSITION *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - ADDR_SET_VALUE v = widgetGlobals->AddrSetValue[(int) *p->ihandle]; - Fl_Widget *o = (Fl_Widget *) v.WidgAddress; - o->position((short) *p->ix, (short) *p->iy); - return OK; - } - - static int fl_hide(CSOUND *csound, FL_WIDHIDE *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - Fl_lock(csound); - ADDR_SET_VALUE v = widgetGlobals->AddrSetValue[(int) *p->ihandle]; - Fl_Widget *o = (Fl_Widget *) v.WidgAddress; - o->hide(); - Fl_unlock(csound); - return OK; - } - - static int fl_show(CSOUND *csound, FL_WIDSHOW *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - Fl_lock(csound); - ADDR_SET_VALUE v = widgetGlobals->AddrSetValue[(int) *p->ihandle]; - Fl_Widget *o = (Fl_Widget *) v.WidgAddress; - o->show(); - Fl_unlock(csound); - return OK; - } - - static int fl_setBox(CSOUND *csound, FL_SETBOX *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - ADDR_SET_VALUE v = widgetGlobals->AddrSetValue[(int) *p->ihandle]; - Fl_Widget *o = (Fl_Widget *) v.WidgAddress; - Fl_Boxtype type; - int itype = (int) *p->itype; - if (itype<0||itype>19) type = FL_FLAT_BOX; - else type = BOX_TABLE[itype]; - // switch ((int) *p->itype) { - // case 1: type = FL_FLAT_BOX; break; - // case 2: type = FL_UP_BOX; break; - // case 3: type = FL_DOWN_BOX; break; - // case 4: type = FL_THIN_UP_BOX; break; - // case 5: type = FL_THIN_DOWN_BOX; break; - // case 6: type = FL_ENGRAVED_BOX; break; - // case 7: type = FL_EMBOSSED_BOX; break; - // case 8: type = FL_BORDER_BOX; break; - // case 9: type = FL_SHADOW_BOX; break; - // case 10: type = FL_ROUNDED_BOX; break; - // case 11: type = FL_RSHADOW_BOX; break; - // case 12: type = FL_RFLAT_BOX; break; - // case 13: type = FL_ROUND_UP_BOX; break; - // case 14: type = FL_ROUND_DOWN_BOX; break; - // case 15: type = FL_DIAMOND_UP_BOX; break; - // case 16: type = FL_DIAMOND_DOWN_BOX; break; - // case 17: type = FL_OVAL_BOX; break; - // case 18: type = FL_OSHADOW_BOX; break; - // case 19: type = FL_OFLAT_BOX; break; - // default: type = FL_FLAT_BOX; - // } - o->box(type); - return OK; - } - - static int fl_align(CSOUND *csound, FL_TALIGN *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - ADDR_SET_VALUE v = widgetGlobals->AddrSetValue[(int) *p->ihandle]; - Fl_Widget *o = (Fl_Widget *) v.WidgAddress; - Fl_Align type; - int itype= (int) *p->itype; - if (itype<0 || itype>9) type = FL_ALIGN_BOTTOM; - else type = ALIGN_TABLE[itype]; - // switch ((int) *p->itype) { - // case 1: type = FL_ALIGN_CENTER; break; - // case 2: type = FL_ALIGN_TOP; break; - // case 3: type = FL_ALIGN_BOTTOM; break; - // case 4: type = FL_ALIGN_LEFT; break; - // case 5: type = FL_ALIGN_RIGHT; break; - // case 6: type = FL_ALIGN_TOP_LEFT; break; - // case 7: type = FL_ALIGN_TOP_RIGHT; break; - // case 8: type = FL_ALIGN_BOTTOM_LEFT; break; - // case 9: type = FL_ALIGN_BOTTOM_RIGHT; break; - // default: type = FL_ALIGN_BOTTOM; - // } - o->align(type); - return OK; - } - - //----------- - //----------- - - static int fl_value(CSOUND *csound, FLVALUE *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - char *controlName = p->name->data; - int ix, iy, iwidth, iheight; - if (*p->ix<0) ix = widgetGlobals->FL_ix; - else widgetGlobals->FL_ix = ix = (int) *p->ix; - if (*p->iy<0) iy = widgetGlobals->FL_iy; - else widgetGlobals->FL_iy = iy = (int) *p->iy; - if (*p->iwidth<0) iwidth = widgetGlobals->FLvalue_iwidth; - else widgetGlobals->FLvalue_iwidth = iwidth = (int) *p->iwidth; - if (*p->iheight<0) iheight = widgetGlobals->FLroller_iheight; - else widgetGlobals->FLroller_iheight = iheight = (int) *p->iheight; - - Fl_Output *o = new Fl_Output(ix, iy, iwidth, iheight,controlName); - o->align(FL_ALIGN_BOTTOM | FL_ALIGN_WRAP); - if (widgetGlobals->FLcolor < 0 ) - o->color(FL_GRAY ); - else - o->color(widgetGlobals->FLcolor, widgetGlobals->FLcolor2); - widget_attributes(csound, o); - //AddrValue.push_back((void *) o); - widgetGlobals->AddrSetValue.push_back(ADDR_SET_VALUE(0, 0, 0, (void *) o, - (void *) p, - widgetGlobals->currentSnapGroup)); - //*p->ihandle = AddrValue.size()-1; - *p->ihandle = widgetGlobals->AddrSetValue.size()-1; - return OK; - } - - //----------- - - static int fl_slider(CSOUND *csound, FLSLIDER *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - char *controlName = p->name->data; - int ix,iy,iwidth, iheight,itype, iexp; - bool plastic = false; - - if (*p->iy < 0) { - iy = widgetGlobals->FL_iy; - widgetGlobals->FL_iy += widgetGlobals->FLcontrol_iheight + 5; - } - else { - iy = (int) *p->iy; - widgetGlobals->FL_iy = iy + widgetGlobals->FLcontrol_iheight + 5; - } - if (*p->ix < 0) ix = widgetGlobals->FL_ix; // omitted options: set default - else widgetGlobals->FL_ix = ix = (int) *p->ix; - if (*p->iwidth < 0) iwidth = widgetGlobals->FLcontrol_iwidth; - else widgetGlobals->FLcontrol_iwidth = iwidth = (int) *p->iwidth; - if (*p->iheight < 0) iheight = widgetGlobals->FLcontrol_iheight; - else widgetGlobals->FLcontrol_iheight = iheight = (int) *p->iheight; - if (*p->itype < 1) itype = 1; - else itype = (int) *p->itype; - - //if (*p->iexp == LIN_) iexp = LIN_; - //else iexp = (int) *p->iexp; - switch((int) *p->iexp) { - case -1: iexp = EXP_; break; - case 0: iexp = LIN_; break; - default: iexp = (int) *p->iexp; - } - if (itype > 19) { - plastic = true; - itype = itype - 20; - } - if (UNLIKELY(itype > 10 && iexp == EXP_)) { - csound->Warning(csound, - "%s", Str("FLslider exponential, using non-labeled slider")); - itype -= 10; - } - - Fl_Slider *o; - if (itype <= 10) o = new Fl_Slider(ix, iy, iwidth, iheight, controlName); - else { - o = new Fl_Value_Slider_Input(csound, ix, iy, - iwidth, iheight, controlName); - itype -=10; - //o->labelsize(20); - ((Fl_Value_Slider_Input*) o)->textboxsize(50); - ((Fl_Value_Slider_Input*) o)->textsize(13); - o->align(FL_ALIGN_BOTTOM | FL_ALIGN_WRAP); - } - switch (itype) { - case 1: o->type(FL_HOR_FILL_SLIDER); break; - case 2: o->type(FL_VERT_FILL_SLIDER); break; - case 3: o->type(FL_HOR_SLIDER); break; - case 4: o->type(FL_VERT_SLIDER); break; - case 5: o->type(FL_HOR_NICE_SLIDER); o->box(FL_FLAT_BOX); break; - case 6: o->type(FL_VERT_NICE_SLIDER); o->box(FL_FLAT_BOX); break; - default: return csound->InitError(csound, - "%s", Str("FLslider: invalid slider type")); - } - if (plastic) o->box(FL_PLASTIC_DOWN_BOX); - widget_attributes(csound, o); - MYFLT min = p->min = *p->imin, max = *p->imax, range; - switch (iexp) { - case LIN_: //linear - o->range(min,max); - o->callback((Fl_Callback*)fl_callbackLinearSlider,(void *) p); - break; - case EXP_ : //exponential - if (UNLIKELY(min == 0 || max == 0)) - return csound->InitError(csound, - "%s", Str("FLslider: zero is illegal " - "in exponential operations")); - range = max - min; - o->range(0,range); - #if defined(sun) - p->base = ::pow((max / (double)min), 1.0/(double)range); - #else - p->base = ::pow((max / min), 1.0/(double)range); - #endif - o->callback((Fl_Callback*)fl_callbackExponentialSlider,(void *) p); - break; - default: - { - FUNC *ftp; - MYFLT fnum = abs(iexp); - if ((ftp = csound->FTnp2Find(csound, &fnum)) != NULL) { - p->table = ftp->ftable; - p->tablen = ftp->flen; - } - else return NOTOK; - o->range(0,0.99999999); - if (iexp > 0) //interpolated - o->callback((Fl_Callback*)fl_callbackInterpTableSlider,(void *) p); - else // non-interpolated - o->callback((Fl_Callback*)fl_callbackTableSlider,(void *) p); - } - } - widgetGlobals->AddrSetValue.push_back(ADDR_SET_VALUE(iexp, *p->imin, - *p->imax, (void *) o, (void*) p)); - /*widgetGlobals->currentSnapGroup;*/ - *p->ihandle = widgetGlobals->AddrSetValue.size()-1; - return OK; - } - - static int fl_slider_bank_(CSOUND *csound, FLSLIDERBANK *p, int istring) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - char s[MAXNAME]; - bool plastic = false; - if (istring) - strncpy(s, ((STRINGDAT*) p->names)->data, MAXNAME-1); - else if ((long) *p->names <= csound->GetStrsmax(csound) && - csound->GetStrsets(csound,(long) *p->names)) { - strncpy(s, csound->GetStrsets(csound,(long) *p->names), MAXNAME-1); - } - string tempname(s); - stringstream sbuf; - sbuf << tempname; - - int width = (int) *p->iwidth; - if (width <=0) width = 100; - - Fl_Group* w = new Fl_Group((int)*p->ix, (int)*p->iy, - width, (int)*p->inumsliders*10); - FUNC *ftp; - MYFLT *minmaxtable = NULL, *typetable = NULL, *outable, *exptable = NULL; - - MYFLT *zkstart; - int zklast = csound->GetZakBounds(csound, &zkstart); - if (*p->ioutable < 1) { - if (LIKELY(zkstart != NULL && - zklast > (long)(*p->inumsliders+*p->ioutablestart_ndx))) - outable = zkstart + (long) *p->ioutablestart_ndx; - else { - return csound->InitError(csound, - "%s", Str("invalid ZAK space allocation")); - } - } - else { - if (LIKELY((ftp = csound->FTnp2Find(csound, p->ioutable)) != NULL)) - outable = ftp->ftable + (long) *p->ioutablestart_ndx; - else - return NOTOK; - } - if ((int) *p->iminmaxtable > 0) { - if (LIKELY((ftp = csound->FTnp2Find(csound, p->iminmaxtable)) != NULL)) - minmaxtable = ftp->ftable; - else return NOTOK; - } - if ((int) *p->iexptable > 0) { - if (LIKELY((ftp = csound->FTnp2Find(csound, p->iexptable)) != NULL)) - exptable = ftp->ftable; - else return NOTOK; - } - if ((int) *p->itypetable > 0) { - if (LIKELY((ftp = csound->FTnp2Find(csound, p->itypetable)) != NULL)) - typetable = ftp->ftable; - else return NOTOK; - } - - for (int j =0; j< *p->inumsliders; j++) { - string stemp; - if (tempname == " ") { - char s[40]; - sprintf(s, "%d", j); - stemp = s; - } - else - getline(sbuf, stemp, '@'); - char *Name = new char[stemp.size()+2]; - strcpy(Name,stemp.c_str()); - widgetGlobals->allocatedStrings.push_back(Name); - - int x = (int) *p->ix, y = (int) *p->iy + j*10; - Fl_Slider *o; - int slider_type; - if ((int) *p->itypetable <= 0) { // no slider type table - if (*p->itypetable >= -7) // all sliders are of the same type - slider_type = -((int) *p->itypetable); - else if (*p->itypetable >= -27 && *p->itypetable < -20) { - slider_type = -((int) *p->itypetable) - 20; - plastic = true; - } - else // random type - slider_type = rand_31_i(csound, 7) | 1; - } - else - slider_type = (int) typetable[j]; - if (slider_type > 20) { - plastic = true; - slider_type -= 20; - } - if (slider_type < 10) - o = new Fl_Slider(x, y, width, 10, Name); - else { - o = new Fl_Value_Slider_Input(csound, x, y, width, 10, Name); - slider_type -=10; - ((Fl_Value_Slider_Input*) o)->textboxsize(50); - ((Fl_Value_Slider_Input*) o)->textsize(13); - } - switch((int) slider_type) { //type - case 1: o->type(FL_HOR_FILL_SLIDER); break; - case 3: o->type(FL_HOR_SLIDER); break; - case 5: o->type(FL_HOR_NICE_SLIDER); o->box(FL_FLAT_BOX); break; - case 7: o->type(FL_HOR_NICE_SLIDER); o->box(FL_DOWN_BOX); break; - default: o->type(FL_HOR_NICE_SLIDER); o->box(FL_FLAT_BOX); break; - } - if (plastic) o->box(FL_PLASTIC_DOWN_BOX); - o->align(FL_ALIGN_LEFT); - widget_attributes(csound, o); - MYFLT min, max, range; - if ((int) *p->iminmaxtable > 0) { - min = minmaxtable[j*2]; - max = minmaxtable[j*2+1]; - } - else { - min = MYFLT(0.0); - max = MYFLT(1.0); - } - int iexp; - - p->slider_data[j].min=min; - p->slider_data[j].max=max; - p->slider_data[j].out=&outable[j]; - - if ((int) *p->iexptable <=0) - // no table, all sliders have the same behaviour - iexp = (int) *p->iexptable; - else - iexp = (int) exptable[j]; - switch (iexp) { - case -1: iexp = EXP_; break; - case 0: iexp = LIN_; break; - } - - MYFLT val = 0; - p->slider_data[j].exp = iexp; - switch (iexp) { - case LIN_: //linear - o->range(min,max); - o->callback((Fl_Callback*)fl_callbackLinearSliderBank, - (void *) &(p->slider_data[j])); - val = outable[j]; - if (val > max) val = max; - else if (val < min) val = min; - break; - case EXP_ : //exponential - if (UNLIKELY(min == 0 || max == 0)) - return - csound->InitError(csound, - "%s", Str("FLslidBnk: zero is illegal " - "in exponential operations")); - range = max - min; - o->range(0,range); - #if defined(sun) - p->slider_data[j].base = ::pow((max / (double)min), 1.0/(double)range); - #else - p->slider_data[j].base = ::pow((max / min), 1.0/(double)range); - #endif - o->callback((Fl_Callback*)fl_callbackExponentialSliderBank, - (void *) &(p->slider_data[j])); - { - val = outable[j]; - MYFLT range = max-min; - #if defined(sun) - MYFLT base = ::pow(max / (double)min, 1.0/(double)range); - #else - MYFLT base = ::pow(max / min, 1.0/(double)range); - #endif - val = (log(val/min) / log(base)) ; - } - break; - default: - { - FUNC *ftp; - MYFLT fnum = abs(iexp); - if ((ftp = csound->FTnp2Find(csound, &fnum)) != NULL) - p->slider_data[j].table = ftp->ftable; - else return NOTOK; - p->slider_data[j].tablen = ftp->flen; - o->range(0,0.99999999); - if (iexp > 0) //interpolated - o->callback((Fl_Callback*)fl_callbackInterpTableSliderBank, - (void *) &(p->slider_data[j])); - else // non-interpolated - o->callback((Fl_Callback*)fl_callbackTableSliderBank, - (void *) &(p->slider_data[j])); - } - } - o->value(val); - } - w->resizable(w); - if (*p->iwidth <=0 || *p->iheight <=0) {// default width and height - int a,b; - w->size( a= w->parent()->w() -50, b= w->parent()->h()); - w->position(50, 0); - } - else { - w->size( (int)*p->iwidth, (int)*p->iheight); - w->position((int)*p->ix, (int)*p->iy); - } - w->end(); - widgetGlobals->AddrSetValue.push_back(ADDR_SET_VALUE(LIN_, 0, 0, (void *) w, - (void *) p, - widgetGlobals->currentSnapGroup)); - //*p->ihandle = widgetGlobals->AddrSetValue.size()-1; - widgetGlobals->last_sldbnk = widgetGlobals->AddrSetValue.size()-1; //gab - return OK; - } - - static int fl_slider_bank(CSOUND *csound, FLSLIDERBANK *p){ - return fl_slider_bank_(csound,p,0); - } - - static int fl_slider_bank_S(CSOUND *csound, FLSLIDERBANK *p){ - return fl_slider_bank_(csound,p,1); - } - - static int fl_joystick(CSOUND *csound, FLJOYSTICK *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - char *Name = p->name->data; - int ix,iy,iwidth, iheight, iexpx, iexpy; - - if (*p->ix < 0) ix = 10; // omitted options: set default - else widgetGlobals->FL_ix = ix = (int) *p->ix; - if (*p->iy < 0) iy = 10; // omitted options: set default - else iy = (int) *p->iy; - if (*p->iwidth < 0) iwidth = 130; - else iwidth = (int) *p->iwidth; - if (*p->iheight < 0) iheight = 130; - else iheight = (int) *p->iheight; - - switch((int) *p->iexpx) { - case -1: iexpx = EXP_; break; - case 0: iexpx = LIN_; break; - default: iexpx = (int) *p->iexpx; - } - switch((int) *p->iexpy) { - case -1: iexpy = EXP_; break; - case 0: iexpy = LIN_; break; - default: iexpy = (int) *p->iexpy; - } - /* - if (*p->iexpx == LIN_) iexpx = LIN_; - else iexpx = (int) *p->iexpx; - if (*p->iexpy == LIN_) iexpy = LIN_; - else iexpy = (int) *p->iexpy; - */ - - Fl_Positioner *o = new Fl_Positioner(ix, iy, iwidth, iheight, Name); - widget_attributes(csound, o); - switch (iexpx) { - case LIN_: //linear - o->xbounds(*p->iminx,*p->imaxx); break; - case EXP_: //exponential - { if (UNLIKELY(*p->iminx == 0 || *p->imaxx == 0)) - return csound->InitError(csound, - "%s", Str("FLjoy X axe: zero is illegal " - "in exponential operations")); - MYFLT range = *p->imaxx - *p->iminx; - o->xbounds(0,range); - #if defined(sun) - p->basex = ::pow((*p->imaxx / (double)*p->iminx), 1.0/(double)range); - #else - p->basex = ::pow((*p->imaxx / *p->iminx), 1.0/(double)range); - #endif - } break; - default: - { - FUNC *ftp; - MYFLT fnum = abs(iexpx); - if ((ftp = csound->FTnp2Find(csound, &fnum)) != NULL) { - p->tablex = ftp->ftable; - p->tablenx = ftp->flen; - } - else return NOTOK; - o->xbounds(0,0.99999999); - /* - if (iexp > 0) //interpolated - o->callback((Fl_Callback*)fl_callbackInterpTableSlider,(void *) p); - else // non-interpolated - o->callback((Fl_Callback*)fl_callbackTableSlider,(void *) p); - */ - } - } - switch (iexpy) { - case LIN_: //linear - o->ybounds(*p->imaxy,*p->iminy); break; - case EXP_ : //exponential - { if (UNLIKELY(*p->iminy == 0 || *p->imaxy == 0)) - return csound->InitError(csound, - "%s", Str("FLjoy X axe: zero is illegal " - "in exponential operations")); - MYFLT range = *p->imaxy - *p->iminy; - o->ybounds(range,0); - #if defined(sun) - p->basey = ::pow((*p->imaxy / (double)*p->iminy), 1.0/(double)range); - #else - p->basey = ::pow((*p->imaxy / *p->iminy), 1.0/(double)range); - #endif - } break; - default: - { - FUNC *ftp; - MYFLT fnum = abs(iexpy); - if (LIKELY((ftp = csound->FTnp2Find(csound, &fnum)) != NULL)) { - p->tabley = ftp->ftable; - p->tableny = ftp->flen; - } - else return NOTOK; - o->ybounds(0,0.99999999); - /* if (iexp > 0) //interpolated - o->callback((Fl_Callback*)fl_callbackInterpTableSlider,(void *) p); - else // non-interpolated - o->callback((Fl_Callback*)fl_callbackTableSlider,(void *) p); - */ - } - } - o->align(FL_ALIGN_BOTTOM | FL_ALIGN_WRAP); - o->callback((Fl_Callback*)fl_callbackJoystick,(void *) p); - widgetGlobals->AddrSetValue.push_back(ADDR_SET_VALUE(iexpx, *p->iminx, - *p->imaxx, (void *) o, (void *) p, - widgetGlobals->currentSnapGroup)); - *p->ihandle1 = widgetGlobals->AddrSetValue.size()-1; - ADDR_SET_VALUE *asv = &widgetGlobals->AddrSetValue[(int) *p->ihandle1]; - asv->widg_type = FL_JOY; - asv->joy = JOY_X; - widgetGlobals->AddrSetValue.push_back(ADDR_SET_VALUE(iexpy, *p->iminy, - *p->imaxy, (void *) o, (void *) p, - widgetGlobals->currentSnapGroup)); - *p->ihandle2 = widgetGlobals->AddrSetValue.size()-1; - asv = &widgetGlobals->AddrSetValue[(int) *p->ihandle2]; - asv->widg_type = FL_JOY; - asv->joy = JOY_Y; - return OK; - } - - static int fl_knob(CSOUND *csound, FLKNOB *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - char *controlName = p->name->data; - int ix, iy, iwidth, itype, iexp; - - if (*p->iy < 0) iy = widgetGlobals->FL_iy; - else widgetGlobals->FL_iy = iy = (int) *p->iy; - if (*p->ix < 0) ix = widgetGlobals->FL_ix; - else widgetGlobals->FL_ix = ix = (int) *p->ix; - if (*p->iwidth < 0) iwidth = widgetGlobals->FLcontrol_iwidth; - else widgetGlobals->FLcontrol_iwidth = iwidth = (int) *p->iwidth; - if (*p->itype < 1) itype = 1; - else itype = (int) *p->itype; - /* - if (*p->iexp < LIN_) iexp = LIN_; - else iexp = (int) *p->iexp; - */ - switch((int) *p->iexp) { - case -1: iexp = EXP_; break; - case 0: iexp = LIN_; break; - default: iexp = (int) *p->iexp; - } - - Fl_Valuator* o; - switch (itype) { - case 1: - o = new Fl_Knob(csound, ix, iy, iwidth, iwidth, controlName); - o->box(FL_NO_BOX); - if (*p->icursorsize > MYFLT(0.5)) - ((Fl_Knob*) o)->cursor((int) (*p->icursorsize + MYFLT(0.5))); - break; - case 2: - o = new Fl_Dial(ix, iy, iwidth, iwidth, controlName); - o->type(FL_FILL_DIAL); - o->box(_FL_OSHADOW_BOX); - ((Fl_Dial*) o)->angles(20, 340); - break; - case 3: - o = new Fl_Dial(ix, iy, iwidth, iwidth, controlName); - o->type(FL_LINE_DIAL); - o->box(_FL_OSHADOW_BOX); - break; - case 4: - o = new Fl_Dial(ix, iy, iwidth, iwidth, controlName); - o->type(FL_NORMAL_DIAL); - o->box(_FL_OSHADOW_BOX); - break; - default: - return csound->InitError(csound, - "%s", Str("FLknob: invalid knob type")); - } - widget_attributes(csound, o); - o->align(FL_ALIGN_BOTTOM | FL_ALIGN_WRAP); - o->range(*p->imin, *p->imax); - switch (iexp) { - case LIN_: //linear - o->range(*p->imin,*p->imax); - o->callback((Fl_Callback*)fl_callbackLinearKnob,(void *) p); - o->step(0.001); - break; - case EXP_ : //exponential - { - MYFLT min = p->min = *p->imin, max = *p->imax; - if (UNLIKELY(min == 0 || max == 0)) - return csound->InitError(csound, - "%s", Str("FLknob: zero is illegal " - "in exponential operations")); - MYFLT range = max - min; - o->range(0,range); - #if defined(sun) - p->base = ::pow((max / (double)min), 1.0/(double)range); - #else - p->base = ::pow((max / min), 1.0/(double)range); - #endif - o->callback((Fl_Callback*)fl_callbackExponentialKnob,(void *) p); - } break; - default: - { - FUNC *ftp; - p->min = *p->imin; - MYFLT fnum = abs(iexp); - if ((ftp = csound->FTnp2Find(csound, &fnum)) != NULL) { - p->table = ftp->ftable; - p->tablen = ftp->flen; - } - else return OK; - o->range(0,0.99999999); - if (iexp > 0) //interpolated - o->callback((Fl_Callback*)fl_callbackInterpTableKnob,(void *) p); - else // non-interpolated - o->callback((Fl_Callback*)fl_callbackTableKnob,(void *) p); - } - } - widgetGlobals->AddrSetValue.push_back(ADDR_SET_VALUE(iexp, *p->imin, *p->imax, - (void *) o, (void *) p)); - *p->ihandle = widgetGlobals->AddrSetValue.size()-1; - return OK; - } - - static int fl_text(CSOUND *csound, FLTEXT *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - char *controlName = p->name->data; - int ix,iy,iwidth,iheight,itype; - MYFLT istep; - - if (*p->iy < 0) iy = widgetGlobals->FL_iy; - else widgetGlobals->FL_iy = iy = (int) *p->iy; - if (*p->ix < 0) ix = widgetGlobals->FL_ix; - else widgetGlobals->FL_ix = ix = (int) *p->ix; - if (*p->iwidth < 0) iwidth = widgetGlobals->FLcontrol_iwidth; - else widgetGlobals->FLcontrol_iwidth = iwidth = (int) *p->iwidth; - if (*p->iheight < 0) iheight = widgetGlobals->FLcontrol_iheight; - else widgetGlobals->FLcontrol_iheight = iheight = (int) *p->iheight; - if (*p->itype < 1) itype = 1; - else itype = (int) *p->itype; - if (*p->istep < 0) istep = MYFLT(.1); - else istep = *p->istep; - - Fl_Valuator* o; - switch(itype) { - case 1: - { - o = new Fl_Value_Input(ix, iy, iwidth, iheight, controlName); - ((Fl_Value_Input *) o)->step(istep); - ((Fl_Value_Input *) o)->range(*p->imin,*p->imax); - } - break; - case 2: - { - o = new Fl_Value_Input_Spin(csound, ix, iy, - iwidth, iheight, controlName); - ((Fl_Value_Input *) o)->step(istep); - ((Fl_Value_Input *) o)->range(*p->imin,*p->imax); - } - break; - case 3: - { - o = new Fl_Value_Output(ix, iy, iwidth, iheight, controlName); - ((Fl_Value_Output *) o)->step(istep); - ((Fl_Value_Output *) o)->range(*p->imin,*p->imax); - } - break; - default: - return NOTOK; - } - o->align(FL_ALIGN_BOTTOM | FL_ALIGN_WRAP); - widget_attributes(csound, o); - o->callback((Fl_Callback*)fl_callbackLinearValueInput,(void *) p); - widgetGlobals->AddrSetValue.push_back(ADDR_SET_VALUE(1, *p->imin, *p->imax, - (void *) o, (void *) p, - widgetGlobals->currentSnapGroup)); - *p->ihandle = widgetGlobals->AddrSetValue.size()-1; - return OK; - } - - static int fl_button(CSOUND *csound, FLBUTTON *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - char *Name = p->name->data; - int type = (int) *p->itype; - bool plastic = false; - if (type > 19) { - type = type - 20; - plastic = true; - } - if (UNLIKELY(type > 9)) { // ignored when getting widgetGlobals->snapshots - csound->Warning(csound, - Str("FLbutton \"%s\" ignoring snapshot capture retrieve"), - Name); - type = type - 10; - } - Fl_Button *w; - *p->kout = *p->ioff; // IV - Aug 27 2002 - - switch (type) { - case 1: - w = new Fl_Button((int)*p->ix, (int)*p->iy, - (int)*p->iwidth, (int)*p->iheight, Name); - if (plastic) { - w->box(FL_PLASTIC_UP_BOX); - w->down_box(FL_PLASTIC_DOWN_BOX); - } - break; - case 2: - w = new Fl_Light_Button((int)*p->ix, (int)*p->iy, - (int)*p->iwidth, (int)*p->iheight, Name); - if (plastic) { - w->box(FL_PLASTIC_UP_BOX); - // w->down_box(FL_PLASTIC_DOWN_BOX); //Doesn't work - } - break; - case 3: - w = new Fl_Check_Button((int)*p->ix, (int)*p->iy, - (int)*p->iwidth, (int)*p->iheight, Name); - if (plastic) { - w->box(FL_PLASTIC_UP_BOX); - w->down_box(FL_PLASTIC_DOWN_BOX); - } - break; - case 4: - w = new Fl_Round_Button((int)*p->ix, (int)*p->iy, - (int)*p->iwidth, (int)*p->iheight, Name); - if (plastic) { - w->box(FL_PLASTIC_UP_BOX); - w->down_box(FL_PLASTIC_DOWN_BOX); - } - break; - default: - return csound->InitError(csound, - "%s", Str("FLbutton: invalid button type")); - } - Fl_Button *o = w; - o->align(FL_ALIGN_WRAP); - widget_attributes(csound, o); - if (type == 1) - o->callback((Fl_Callback*) fl_callbackButton1, (void*) p); - else - o->callback((Fl_Callback*) fl_callbackButton, (void*) p); - widgetGlobals->AddrSetValue.push_back(ADDR_SET_VALUE(0, 0, 0, (void *) o, - (void *) p, - widgetGlobals->currentSnapGroup)); - *p->ihandle = widgetGlobals->AddrSetValue.size() - 1; - - return OK; - } - - static int fl_close_button(CSOUND *csound, FLCLOSEBUTTON *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - char *Name = p->name->data; - - Fl_Button *w; - - w = new Fl_Button((int)*p->ix, (int)*p->iy, - (int)*p->iwidth, (int)*p->iheight, Name); - - Fl_Button *o = w; - - o->align(FL_ALIGN_WRAP); - widget_attributes(csound, o); - - ADDR_STACK adrstk = widgetGlobals->AddrStack.back(); - if (UNLIKELY(strcmp( adrstk.h->optext->t.opcod, "FLpanel"))) - return csound->InitError(csound, - "%s", Str("FLcloseButton: invalid stack" - " pointer: verify its placement")); - - o->callback((Fl_Callback*) fl_callbackCloseButton, - (void*) adrstk.WidgAddress); - - widgetGlobals->AddrSetValue.push_back(ADDR_SET_VALUE(0, 0, 0, (void *) o, - (void *) p)); - *p->ihandle = widgetGlobals->AddrSetValue.size() - 1; - - return OK; - } - - static int fl_exec_button(CSOUND *csound, FLEXECBUTTON *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - p->csound = csound; - Fl_Button *w; - - p->commandString = p->command->data; - - csound->Message(csound, Str("Command Found: %s\n"), p->commandString); - - w = new Fl_Button((int)*p->ix, (int)*p->iy, - (int)*p->iwidth, (int)*p->iheight, Str("About")); - - Fl_Button *o = w; - - o->align(FL_ALIGN_WRAP); - widget_attributes(csound, o); - - o->callback((Fl_Callback*) fl_callbackExecButton, (void*) p); - - widgetGlobals->AddrSetValue.push_back(ADDR_SET_VALUE(0, 0, 0, - (void *) o, (void *) p, - widgetGlobals->currentSnapGroup)); - *p->ihandle = widgetGlobals->AddrSetValue.size() - 1; - - return OK; - } - - static int fl_button_bank(CSOUND *csound, FLBUTTONBANK *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - char *Name = (char*)"/0"; - int type = (int) *p->itype; - bool plastic = false; - if (type > 20) { - plastic = true; - type = type - 20; - } - if (UNLIKELY(type > 9)) { // ignored when getting widgetGlobals->snapshots - csound->Warning(csound, - Str("FLbutton \"%s\" ignoring snapshot capture retrieve"), - Name); - type = type - 10; - } - Fl_Group* o = new Fl_Group((int)*p->ix, (int)*p->iy, - (int)*p->inumx * 10, (int)*p->inumy*10); - int z = 0; - for (int j = 0; j < *p->inumx; j++) { - for (int k = 0; k < *p->inumy; k++) { - int x = (int) *p->ix + j*10, y = (int) *p->iy + k*10; - Fl_Button *w; - char *btName = new char[30]; - widgetGlobals->allocatedStrings.push_back(btName); - sprintf(btName, "%d", z); - switch (type) { - case 1: - w = new Fl_Button(x, y, 10, 10, btName); - if (plastic) { - w->box(FL_PLASTIC_UP_BOX); - w->down_box(FL_PLASTIC_DOWN_BOX); - } - break; - case 2: - w = new Fl_Light_Button(x, y, 10, 10, btName); - if (plastic) { - w->box(FL_PLASTIC_UP_BOX); - } - break; - case 3: - w = new Fl_Check_Button(x, y, 10, 10, btName); - if (plastic) { - w->box(FL_PLASTIC_UP_BOX); - w->down_box(FL_PLASTIC_DOWN_BOX); - } - break; - case 4: - w = new Fl_Round_Button(x, y, 10, 10, btName); - if (plastic) { - w->box(FL_PLASTIC_UP_BOX); - w->down_box(FL_PLASTIC_DOWN_BOX); - } - break; - default: return csound->InitError(csound, - "%s", Str("FLbuttonBank: " - "invalid button type")); - } - widget_attributes(csound, w); - w->type(FL_RADIO_BUTTON); - w->callback((Fl_Callback*) fl_callbackButtonBank, (void *) p); - if (!z) - w->value(1); - z++; - } - } - o->resizable(o); - //*p->ix, *p->iy, - o->size( (int)*p->iwidth, (int)*p->iheight); - o->position((int)*p->ix, (int)*p->iy); - o->align(FL_ALIGN_BOTTOM | FL_ALIGN_WRAP); - o->end(); - - widgetGlobals->AddrSetValue.push_back(ADDR_SET_VALUE(0, 0, 0, - (void *) o, (void *) p, - widgetGlobals->currentSnapGroup)); - *p->kout = MYFLT(0.0); - *p->ihandle = widgetGlobals->AddrSetValue.size()-1; - return OK; - } - - static int fl_counter(CSOUND *csound, FLCOUNTER *p) - { - char *controlName = p->name->data; - // int ix,iy,iwidth,iheight,itype; - // MYFLT istep1, istep2; - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - Fl_Counter* o = new Fl_Counter((int)*p->ix, (int)*p->iy, - (int)*p->iwidth, (int)*p->iheight, - controlName); - widget_attributes(csound, o); - int type = (int) *p->itype; - if (UNLIKELY(type >9 )) { // ignored when getting widgetGlobals->snapshots - csound->Warning(csound, - Str("FLcount \"%s\" ignoring snapshot capture retrieve"), - controlName); - type = type-10; - } - switch(type) { - case 1: o->type(FL_NORMAL_COUNTER); break; - case 2: o->type(FL_SIMPLE_COUNTER); break; - default:o->type(FL_NORMAL_COUNTER); break; - } - - o->step(*p->istep1); - o->lstep(*p->istep2); - o->align(FL_ALIGN_BOTTOM | FL_ALIGN_WRAP); - // range is accepted only if min and max are different - if (*p->imin != *p->imax) - o->range(*p->imin,*p->imax); //otherwise no-range - widget_attributes(csound, o); - o->callback((Fl_Callback*)fl_callbackCounter,(void *) p); - widgetGlobals->AddrSetValue.push_back(ADDR_SET_VALUE(1, 0, 100000, (void *) o, - (void *) p, - widgetGlobals->currentSnapGroup)); - *p->ihandle = widgetGlobals->AddrSetValue.size()-1; - return OK; - } - - static int fl_roller(CSOUND *csound, FLROLLER *p) - { - char *controlName = p->name->data; - int ix,iy,iwidth, iheight,itype, iexp ; - double istep; - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - if (*p->iy < 0) { - iy = widgetGlobals->FL_iy; - widgetGlobals->FL_iy += widgetGlobals->FLroller_iheight + 15; - } - else { - iy = (int) *p->iy; - widgetGlobals->FL_iy = iy + widgetGlobals->FLroller_iheight + 15; - } - // omitted options: set defaults - if (*p->ix<0) ix = widgetGlobals->FL_ix; - else widgetGlobals->FL_ix = ix = (int) *p->ix; - if (*p->iy<0) iy = widgetGlobals->FL_iy; - else widgetGlobals->FL_iy = iy = (int) *p->iy; - if (*p->iwidth<0) iwidth = widgetGlobals->FLroller_iwidth; - else widgetGlobals->FLroller_iwidth = iwidth = (int) *p->iwidth; - if (*p->iheight<0) iheight = widgetGlobals->FLroller_iheight; - else widgetGlobals->FLroller_iheight = iheight = (int) *p->iheight; - if (*p->itype<1) itype = 1; - else itype = (int) *p->itype; - //if (*p->iexpiexp; - switch((int) *p->iexp) { - case -1: iexp = EXP_; break; - case 0: iexp = LIN_; break; - default: iexp = (int) *p->iexp; - } - - if (*p->istep<0) istep = 1; else istep = *p->istep; - p->min = *p->imin; - Fl_Roller *o; - switch (itype) { - case 1: - o = new Fl_Roller(ix, iy, iwidth, iheight, controlName); - o->type(FL_HORIZONTAL); - break; - case 2: - o = new Fl_Roller(ix, iy, iwidth, iheight, controlName); - o->type(FL_VERTICAL); - break; - default: - return csound->InitError(csound, - "%s", Str("FLroller: invalid roller type")); - } - widget_attributes(csound, o); - o->step(istep); - switch (iexp) { - case LIN_: //linear - o->range(*p->imin,*p->imax); - o->callback((Fl_Callback*)fl_callbackLinearRoller,(void *) p); - break; - case EXP_ : //exponential - { - MYFLT min = p->min, max = *p->imax; - if (UNLIKELY(min == 0 || max == 0)) - return csound->InitError(csound, - "%s", Str("FLslider: zero is illegal " - "in exponential operations")); - MYFLT range = max - min; - o->range(0,range); - #if defined(sun) - p->base = ::pow((max / (double)min), 1.0/(double)range); - #else - p->base = ::pow((max / min), 1.0/(double)range); - #endif - o->callback((Fl_Callback*)fl_callbackExponentialRoller,(void *) p); - } - break; - default: - { - FUNC *ftp; - MYFLT fnum = abs(iexp); - if ((ftp = csound->FTnp2Find(csound, &fnum)) != NULL) { - p->table = ftp->ftable; - p->tablen = ftp->flen; - } - else return NOTOK; - o->range(0,0.99999999); - if (iexp > 0) //interpolated - o->callback((Fl_Callback*)fl_callbackInterpTableRoller,(void *) p); - else // non-interpolated - o->callback((Fl_Callback*)fl_callbackTableRoller,(void *) p); - } - } - widgetGlobals->AddrSetValue.push_back(ADDR_SET_VALUE(iexp, *p->imin, *p->imax, - (void *) o, (void *) p, - widgetGlobals->currentSnapGroup)); - *p->ihandle = widgetGlobals->AddrSetValue.size()-1; - return OK; - } - - static int FLprintkset(CSOUND *csound, FLPRINTK *p) - { - //WIDGET_GLOBALS *widgetGlobals = - //(WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - if (*p->ptime < MYFLT(1.0) / CS_EKR) - p->ctime = MYFLT(1.0) / CS_EKR; - else p->ctime = *p->ptime; - - p->initime = (MYFLT) csound->GetKcounter(csound) * (1.0/CS_EKR); - p->cysofar = -1; - return OK; - } - - static int FLprintk(CSOUND *csound, FLPRINTK *p) - { - MYFLT timel; - long cycles; - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - - timel = ((MYFLT) csound->GetKcounter(csound) * - (1.0/CS_EKR)) - p->initime; - cycles = (long)(timel / p->ctime); - if (p->cysofar < cycles) { - p->cysofar = cycles; - char valString[MAXNAME]; - sprintf(valString,"%.5g", *p->val); - ((Fl_Output*) (widgetGlobals->AddrSetValue[(long) *p->idisp]).WidgAddress)-> - value(valString ); - } - return OK; - } - - static int FLprintk2set(CSOUND *csound, FLPRINTK2 *p) // IV - Aug 27 2002 - { - IGN(csound); - p->oldvalue = MYFLT(-1.12123e35); // hack to force printing first value - return OK; - } - - static int FLprintk2(CSOUND *csound, FLPRINTK2 *p) - { - MYFLT value = *p->val; - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - if (p->oldvalue != value) { - char valString[MAXNAME]; - sprintf(valString,"%.5g", *p->val); - ((Fl_Output*) (widgetGlobals->AddrSetValue[(long) *p->idisp]).WidgAddress)-> - value(valString ); - p->oldvalue = value; - } - return OK; - } - - // New opcodes for Csound5.06 by Gabriel Maldonado, ported by Andres Cabrera - - void skin(CSOUND* csound, Fl_Widget *o, int imgNum, bool isTiled) - { - IGN(csound); IGN(o); IGN(imgNum); IGN(isTiled); - // WIDGET_GLOBALS *widgetGlobals = - // (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); -#ifdef CS_IMAGE - extern void* get_image(CSOUND* csound, int imgNum); - ImageSTRUCT *bmp = (ImageSTRUCT*) get_image(csound, imgNum); - FLlock(); - Fl_RGB_Image *img = new Fl_RGB_Image((BYTE*) (bmp->data), - bmp->xsize,bmp->ysize,bmp->csize); - widgetGlobals->allocatedStrings.push_back((char *) img); - if (isTiled) { - Fl_Tiled_Image *t_img = new Fl_Tiled_Image(img); - o->image(t_img); - widgetGlobals->allocatedStrings.push_back((char *) t_img); - } - else { - o->image(img); - } - o->align(FL_ALIGN_INSIDE); - FLunlock(); -#endif - } - - class HVS_BOX : public Fl_Box{ - int rx,ry,rw,rh; - int red, green, blue; - // Fl_PNG_Image *img; - public: - - int numLinesX,numLinesY; - MYFLT valueX, valueY; - - HVS_BOX(int numLinesX_, int numLinesY_, int x, int y, int w, int h) : - Fl_Box(x,y,w,h) { - numLinesX = numLinesX_ - 1; - numLinesY = numLinesY_ - 1; - valueX = valueY = .5; - rx = rw = ry = rh = 0; - red = green = blue = 0; - // if (filename != NULL) { - // img = new Fl_PNG_Image(filename); - // this->image(img); - // } - } - void draw() { - Fl_Box::draw(); - - fl_color(selection_color()); - MYFLT Xincr = w()/(MYFLT) numLinesX; - MYFLT Yincr = h()/(MYFLT) numLinesY; - fl_color(FL_RED); - for (int j = 1; j < numLinesX; j++) { - fl_yxline((int) (j*Xincr+x()), y(), h()+y()); - } - for (int k = 1; k QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - if (UNLIKELY(*p->numlinesX < 2 || *p->numlinesY < 2)) - return csound->InitError(csound, - "%s", Str("FLhvsBox: a square area must be" - " delimited by 2 lines at least")); - - HVS_BOX *o = new HVS_BOX((int) *p->numlinesX,(int) *p->numlinesY, - (int) *p->ix,(int) *p->iy, - (int) *p->iwidth, (int) *p->iheight); - widget_attributes(csound,o); - o->box(FL_DOWN_BOX); - if (*p->image >= 0) skin(csound, o, (int) *p->image, false); - - widgetGlobals->AddrSetValue.push_back(ADDR_SET_VALUE(0, 0, 0, - (void *) o, (void *) p, - widgetGlobals->currentSnapGroup)); - *p->ihandle = widgetGlobals->AddrSetValue.size()-1; - return OK; - - } - - static int fl_setHvsValue_set(CSOUND *csound,FL_SET_HVS_VALUE *p) - { - IGN(csound); - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - ADDR_SET_VALUE v = widgetGlobals->AddrSetValue[(int) *p->ihandle]; - p->WidgAddress = v.WidgAddress; - p->opcode = v.opcode; - return OK; - } - - static int fl_setHvsValue(CSOUND *csound,FL_SET_HVS_VALUE *p) - { - IGN(csound); - if(*p->kx != p->old_x || *p->ky != p->old_y ) { - HVS_BOX *o = (HVS_BOX *) p->WidgAddress; - FLlock(); - o->setXY(*p->kx, *p->ky); - FLunlock(); - p->old_x = *p->kx; - p->old_y = *p->ky; - } - return OK; - } - - - static int fl_keyin_set(CSOUND *csound,FLKEYIN *p) - { - FUNC* ftp; - if (*p->ifn > 0) { // mapping values - p->flag = 1; - - if (LIKELY((ftp = csound->FTnp2Find(csound,p->ifn)) != NULL)) - p->table = ftp->ftable; - else { - return csound->InitError(csound, - "%s", Str("FLkeyIn: invalid table number")); - } - if (UNLIKELY(ftp->flen < 512)) { - return csound->InitError(csound, - "%s", Str("FLkeyIn: table too short!")); - } - } - else p->flag = 0; - return OK; - } - - static int fl_keyin(CSOUND *csound,FLKEYIN *p) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - if (widgetGlobals->last_KEY) { - int key; - - if ( widgetGlobals->last_KEY > 0 && widgetGlobals->last_KEY < 256) - key = widgetGlobals->last_KEY; - else - key = (widgetGlobals->last_KEY & 0xFF) + 256; // function keys - - if(p->flag) { - if(widgetGlobals->isKeyDown) - p->table[key] = 1; - else - p->table[key] = 0; - } - if (widgetGlobals->isKeyDown) *p->kascii = key; - else *p->kascii = -key; - widgetGlobals->last_KEY = 0; - } - return OK; - } - - static int fl_setSnapGroup(CSOUND *csound, FLSETSNAPGROUP *p) - { - IGN(csound); - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - widgetGlobals->currentSnapGroup = (int) *p->group; - return OK; - } - - static int fl_mouse_set(CSOUND *csound,FLMOUSE *p) - { - IGN(csound); - p->width= Fl::w(); - p->height= Fl::h(); - return OK; - } - - static int fl_mouse(CSOUND *csound,FLMOUSE *p) - { - IGN(csound); - if (*p->flagRaw == 0) { - *p->x = (MYFLT) Fl::event_x_root()/p->width; - *p->y = 1.0 - ((MYFLT) Fl::event_y_root()/p->height); - } - else if (*p->flagRaw == 1) { - *p->x = (MYFLT) Fl::event_x_root(); - *p->y = (MYFLT) Fl::event_y_root(); - } - else if (*p->flagRaw == 2) { - *p->x = (MYFLT) Fl::event_x(); - *p->y = (MYFLT) Fl::event_y(); - } - // *p->b1 = (MYFLT) (Fl::event_button1() >> 56); - // *p->b2 = (MYFLT) (Fl::event_button2() >> 57); - // *p->b3 = (MYFLT) (Fl::event_button3() >> 58); - *p->b1 = (MYFLT) (Fl::event_button1() >> 24); - *p->b2 = (MYFLT) (Fl::event_button2() >> 25); - *p->b3 = (MYFLT) (Fl::event_button3() >> 26); - - return OK; - } - - - static int fl_vertical_slider_bank_(CSOUND *csound, FLSLIDERBANK *p, int istring) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - char s[MAXNAME]; - bool plastic = false; - if (istring) - strncpy(s, ((STRINGDAT *)p->names)->data, MAXNAME-1); - else if ((long) *p->names <= csound->GetStrsmax(csound) && - csound->GetStrsets(csound,(long) *p->names)) { - strncpy(s, csound->GetStrsets(csound,(long) *p->names), MAXNAME-1); - } - string tempname(s); - stringstream sbuf; - sbuf << tempname; - - int height = (int) *p->iheight; - if (height <=0) height = 100; - Fl_Group* w = new Fl_Group((int)*p->ix, (int)*p->iy, - (int)*p->inumsliders*10, height); - FUNC *ftp; - MYFLT *minmaxtable = NULL, *typetable = NULL, *outable, *exptable = NULL; - - MYFLT *zkstart; - int zklast = csound->GetZakBounds(csound, &zkstart); - if (*p->ioutable < 1) { - if (LIKELY(zkstart != NULL && - zklast > (long)(*p->inumsliders+*p->ioutablestart_ndx))) - outable = zkstart + (long) *p->ioutablestart_ndx; - else { - return csound->InitError(csound, - "%s", Str("invalid ZAK space allocation")); - } - } - else { - if (LIKELY((ftp = csound->FTnp2Find(csound, p->ioutable)) != NULL)) - outable = ftp->ftable + (long) *p->ioutablestart_ndx; - else - return NOTOK; - } - if ((int) *p->iminmaxtable > 0) { - if (LIKELY((ftp = csound->FTnp2Find(csound, p->iminmaxtable)) != NULL)) - minmaxtable = ftp->ftable; - else return NOTOK; - } - if ((int) *p->iexptable > 0) { - if (LIKELY((ftp = csound->FTnp2Find(csound, p->iexptable)) != NULL)) - exptable = ftp->ftable; - else return NOTOK; - } - if ((int) *p->itypetable > 0) { - if (LIKELY((ftp = csound->FTnp2Find(csound, p->itypetable)) != NULL)) - typetable = ftp->ftable; - else return NOTOK; - } - p->elements = (long) *p->inumsliders; - - for (int j =0; j< p->elements; j++) { - string stemp; - if (tempname == " ") { - char s[40]; - sprintf(s, "%d", j); - stemp = s; - } - else - getline(sbuf, stemp, '@'); - char *Name = new char[stemp.size()+2]; - strcpy(Name,stemp.c_str()); - widgetGlobals->allocatedStrings.push_back(Name); - - int x = (int) *p->ix+j*10, y = (int) *p->iy /*+ j*10*/; - Fl_Slider *o; - int slider_type; - if ((int) *p->itypetable <= 0) { // no slider type table - if (*p->itypetable >= -7) // all sliders are of the same type - slider_type = -((int) *p->itypetable); - else if (*p->itypetable >= -27 && *p->itypetable < -20) { - slider_type = -((int) *p->itypetable) - 20; - plastic = true; - } - else { // random type - slider_type = rand_31_i(csound, 7) | 1; - switch(slider_type) { - case 0: slider_type = 1; break; - case 2: slider_type = 3; break; - case 4: slider_type = 5; break; - case 6: slider_type = 7; break; - default: slider_type = 1; - } - } - } - else - slider_type = (int) typetable[j]; - if (slider_type > 20) { - plastic = true; - slider_type -= 20; - } - if (slider_type < 10) - o = new Fl_Slider(x, y, 10, height, Name); - else { - o = new Fl_Value_Slider_Input(csound, x, y, 10, height, Name); - slider_type -=10; - ((Fl_Value_Slider_Input*) o)->textboxsize(50); - ((Fl_Value_Slider_Input*) o)->textsize(13); - } - switch((int) slider_type) { //type - case 1: o->type(FL_VERT_FILL_SLIDER); break; - case 3: o->type(FL_VERT_SLIDER); break; - case 5: o->type(FL_VERT_NICE_SLIDER); o->box(FL_FLAT_BOX); break; - case 7: o->type(FL_VERT_NICE_SLIDER); o->box(FL_DOWN_BOX); break; - default: o->type(FL_VERT_NICE_SLIDER); o->box(FL_FLAT_BOX); break; - } - o->align(FL_ALIGN_BOTTOM); - if (plastic) o->box(FL_PLASTIC_DOWN_BOX); - widget_attributes(csound, o); - MYFLT min, max, range; - if ((int) *p->iminmaxtable > 0) { - min = minmaxtable[j*2]; - max = minmaxtable[j*2+1]; - } - else { - min = MYFLT(0.0); - max = MYFLT(1.0); - } - int iexp; - - p->slider_data[j].min=min; - p->slider_data[j].max=max; - p->slider_data[j].out=&outable[j]; - - if ((int) *p->iexptable <=0) - // no table, all sliders have the same behaviour - iexp = (int) *p->iexptable; - else - iexp = (int) exptable[j]; - switch (iexp) { - case -1: iexp = EXP_; break; - case 0: iexp = LIN_; break; - } - - MYFLT val = 0; - p->slider_data[j].exp = iexp; - switch (iexp) { - case LIN_: //linear - o->range(min,max); - o->callback((Fl_Callback*)fl_callbackLinearSliderBank, - (void *) &(p->slider_data[j])); - val = outable[j]; - if (val > max) val = max; - else if (val < min) val = min; - break; - case EXP_ : //exponential - if (UNLIKELY(min == 0 || max == 0)) - return - csound->InitError(csound, - "%s", Str("FLvslidBnk: zero is illegal " - "in exponential operations")); - range = max - min; - o->range(range,0); - #if defined(sun) - p->slider_data[j].base = ::pow((max / (double)min), 1.0/(double)range); - #else - p->slider_data[j].base = ::pow((max / min), 1.0/(double)range); - #endif - o->callback((Fl_Callback*)fl_callbackExponentialSliderBank, - (void *) &(p->slider_data[j])); - { - val = outable[j]; - MYFLT range = max-min; - #if defined(sun) - MYFLT base = ::pow(max / (double)min, 1.0/(double)range); - #else - MYFLT base = ::pow(max / min, 1.0/(double)range); - #endif - val = (log(val/min) / log(base)) ; - } - break; - default: - { - FUNC *ftp; - MYFLT fnum = abs(iexp); - if (LIKELY((ftp = csound->FTnp2Find(csound, &fnum)) != NULL)) - p->slider_data[j].table = ftp->ftable; - else return NOTOK; - p->slider_data[j].tablen = ftp->flen; - o->range(.99999999,0); - if (iexp > 0) //interpolated - o->callback((Fl_Callback*)fl_callbackInterpTableSliderBank, - (void *) &(p->slider_data[j])); - else // non-interpolated - o->callback((Fl_Callback*)fl_callbackTableSliderBank, - (void *) &(p->slider_data[j])); - } - } - o->value(val); - p->slider_data[j].widget_addr= (void*) o; - } - w->resizable(w); - if (*p->iwidth <=0 || *p->iheight <=0) {// default width and height - int a,b; - w->size( a= w->parent()->w(), b= w->parent()->h()-50); - w->position(0, 0); - } - else { - w->size( (int)*p->iwidth, (int)*p->iheight); - w->position((int)*p->ix, (int)*p->iy); - } - w->end(); - widgetGlobals->AddrSetValue.push_back(ADDR_SET_VALUE(LIN_, 0, 0, (void *) w, - (void *) p, - widgetGlobals->currentSnapGroup)); - widgetGlobals->last_sldbnk = widgetGlobals->AddrSetValue.size()-1; //gab - - return OK; - } - - static int fl_vertical_slider_bank(CSOUND *csound, FLSLIDERBANK *p){ - return fl_vertical_slider_bank_(csound,p,0); - } - - static int fl_vertical_slider_bank_S(CSOUND *csound, FLSLIDERBANK *p){ - return fl_vertical_slider_bank_(csound,p,1); - } - - - static int fl_slider_bank2_(CSOUND *csound, FLSLIDERBANK2 *p, int istring) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - char s[MAXNAME]; - bool plastic = false; - if (istring) - strncpy(s, ((STRINGDAT*) p->names)->data, MAXNAME-1); - else if ((long) *p->names <= csound->GetStrsmax(csound) && - csound->GetStrsets(csound,(long) *p->names)) { - strncpy(s, csound->GetStrsets(csound,(long) *p->names), MAXNAME-1); - } - string tempname(s); - stringstream sbuf; - sbuf << tempname; - - int width = (int) *p->iwidth; - if (width <=0) width = 100; - - Fl_Group* w = new Fl_Group((int)*p->ix, (int)*p->iy, - width, (int)*p->inumsliders*10); - FUNC *ftp; - MYFLT *configtable, *outable; - - MYFLT *zkstart; - int zklast = csound->GetZakBounds(csound, &zkstart); - if (*p->ioutable < 1) { - if (LIKELY(zkstart != NULL && - zklast>(long)(*p->inumsliders + *p->ioutablestart_ndx))) - outable = zkstart + (long) *p->ioutablestart_ndx; - else { - return csound->InitError(csound, - "%s", Str("invalid ZAK space allocation")); - } - } - else { - if ((ftp = csound->FTnp2Find(csound, p->ioutable)) != NULL) - outable = ftp->ftable + (long) *p->ioutablestart_ndx; - else - return NOTOK; - } - if((ftp = csound->FTnp2Find(csound, p->iconfigtable)) != NULL) - configtable = ftp->ftable; - else return NOTOK; - - p->elements = (long) *p->inumsliders; - for (int j =0; j< p->elements; j++) { - string stemp; - if (tempname == " ") { - char s[40]; - sprintf(s, "%d", j); - stemp = s; - } - else - getline(sbuf, stemp, '@'); - char *Name = new char[stemp.size()+2]; - strcpy(Name,stemp.c_str()); - widgetGlobals->allocatedStrings.push_back(Name); - - int x = (int) *p->ix, y = (int) *p->iy + j*10; - Fl_Slider *o; - int slider_type = (int) configtable[j*4+3]; - int iexp = (int) configtable[j*4+2]; - - if (slider_type > 19) { - plastic = true; - slider_type = slider_type - 20; - } - if (UNLIKELY(slider_type > 10 && iexp == EXP_)) { - csound->Warning(csound, "%s", - Str("FLslider exponential, using non-labeled slider")); - slider_type -= 10; - } - if (slider_type <= 10) - o = new Fl_Slider(x, y, width, 10, Name); - else { - o = new Fl_Value_Slider_Input( csound, x, y, width, 10, Name); - slider_type -=10; - ((Fl_Value_Slider_Input*) o)->textboxsize(20); - ((Fl_Value_Slider_Input*) o)->textsize(13); - } - switch((int) slider_type) { //type - case 1: o->type(FL_HOR_FILL_SLIDER); break; - case 3: o->type(FL_HOR_SLIDER); break; - case 5: o->type(FL_HOR_NICE_SLIDER); o->box(FL_FLAT_BOX); break; - case 7: o->type(FL_HOR_NICE_SLIDER); o->box(FL_DOWN_BOX); break; - default: o->type(FL_HOR_NICE_SLIDER); o->box(FL_FLAT_BOX); break; - } - o->align(FL_ALIGN_LEFT); - if (plastic) o->box(FL_PLASTIC_DOWN_BOX); - o->step(0.0); - widget_attributes(csound, o); - MYFLT min, max, range; - min = configtable[j*4]; - max = configtable[j*4+1]; - - p->slider_data[j].min=min; - p->slider_data[j].max=max; - p->slider_data[j].out=&outable[j]; - - switch (iexp) { - case -1: iexp = EXP_; break; - case 0: iexp = LIN_; break; - } - - MYFLT val = 0; - p->slider_data[j].exp = iexp; - - switch (iexp) { - case LIN_: //linear - o->range(min,max); - o->callback((Fl_Callback*)fl_callbackLinearSliderBank, - (void *) &(p->slider_data[j])); - val = outable[j]; - if (val > max) val = max; - else if (val < min) val = min; - break; - case EXP_ : //exponential - if (UNLIKELY(min == 0 || max == 0)) - return - csound->InitError(csound, - "%s", Str("FLsliderBank: zero is illegal " - "in exponential operations")); - range = max - min; - o->range(0,range); - #if defined(sun) - p->slider_data[j].base = ::pow((max / (double)min), 1.0/(double)range); - #else - p->slider_data[j].base = ::pow((max / min), 1.0/(double)range); - #endif - o->callback((Fl_Callback*)fl_callbackExponentialSliderBank, - (void *) &(p->slider_data[j])); - { - val = outable[j]; - MYFLT range = max-min; - #if defined(sun) - MYFLT base = ::pow(max / (double)min, 1.0/(double)range); - #else - MYFLT base = ::pow(max / min, 1.0/(double)range); - #endif - val = (log(val/min) / log(base)) ; - } - break; - default: - { - FUNC *ftp; - MYFLT fnum = abs(iexp); - if ((ftp = csound->FTnp2Find(csound, &fnum)) != NULL) - p->slider_data[j].table = ftp->ftable; - else return NOTOK; - p->slider_data[j].tablen = ftp->flen; - o->range(0,0.99999999); - if (iexp > 0) //interpolated - o->callback((Fl_Callback*)fl_callbackInterpTableSliderBank, - (void *) &(p->slider_data[j])); - else // non-interpolated - o->callback((Fl_Callback*)fl_callbackTableSliderBank, - (void *) &(p->slider_data[j])); - } - } - o->value(val); - p->slider_data[j].widget_addr= (void*) o; - } - w->resizable(w); - if (*p->iwidth <=0 || *p->iheight <=0) {// default width and height - int a,b; - w->size( a= w->parent()->w() -50, b= w->parent()->h()); - w->position(50, 0); - } - else { - w->size( (int)*p->iwidth, (int)*p->iheight); - w->position((int)*p->ix, (int)*p->iy); - } - w->end(); - widgetGlobals->AddrSetValue.push_back(ADDR_SET_VALUE(LIN_, 0, 0, (void *) w, - (void *) p, - widgetGlobals->currentSnapGroup)); - //*p->ihandle = widgetGlobals->AddrSetValue.size()-1; - widgetGlobals->last_sldbnk = widgetGlobals->AddrSetValue.size()-1; //gab - - return OK; - } - - static int fl_slider_bank2(CSOUND *csound, FLSLIDERBANK2 *p){ - return fl_slider_bank2_(csound, p,0); - } - - static int fl_slider_bank2_S(CSOUND *csound, FLSLIDERBANK2 *p){ - return fl_slider_bank2_(csound, p,1); - } - - static int fl_vertical_slider_bank2_(CSOUND *csound, - FLSLIDERBANK2 *p, int istring) - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - char s[MAXNAME]; - bool plastic = false; - if (istring) - strncpy(s, ((STRINGDAT*) p->names)->data, MAXNAME-1); - else if ((long) *p->names <= csound->GetStrsmax(csound) && - csound->GetStrsets(csound,(long) *p->names)) { - strncpy(s, csound->GetStrsets(csound,(long) *p->names), MAXNAME-1); - } - string tempname(s); - stringstream sbuf; - sbuf << tempname; - - int height = (int) *p->iheight; - if (height <=0) height = 100; - - Fl_Group* w = new Fl_Group((int)*p->ix, (int)*p->iy, - (int) *p->inumsliders*10, height); - FUNC *ftp; - MYFLT *configtable, *outable; - - MYFLT *zkstart; - int zklast = csound->GetZakBounds(csound, &zkstart); - if (*p->ioutable < 1) { - if (LIKELY(zkstart != NULL && - zklast>(long)(*p->inumsliders + *p->ioutablestart_ndx))) - outable = zkstart + (long) *p->ioutablestart_ndx; - else { - return csound->InitError(csound, - "%s", Str("invalid ZAK space allocation")); - } - } - else { - if ((ftp = csound->FTnp2Find(csound, p->ioutable)) != NULL) - outable = ftp->ftable + (long) *p->ioutablestart_ndx; - else - return NOTOK; - } - if((ftp = csound->FTnp2Find(csound, p->iconfigtable)) != NULL) - configtable = ftp->ftable; - else - return NOTOK; - - p->elements = (long) *p->inumsliders; - for (int j =0; j< p->elements; j++) { - string stemp; - if (tempname == " ") { - char s[40]; - sprintf(s, "%d", j); - stemp = s; - } - else - getline(sbuf, stemp, '@'); - char *Name = new char[stemp.size()+2]; - strcpy(Name,stemp.c_str()); - widgetGlobals->allocatedStrings.push_back(Name); - - int x = (int) *p->ix+j*10, y = (int) *p->iy /*+ j*10*/; - Fl_Slider *o; - int slider_type= (int) configtable[j*4+3]; - int iexp = (int) configtable[j*4+2]; - - if (slider_type > 19) { - plastic = true; - slider_type -= 20; - } - if (UNLIKELY(slider_type > 10 && iexp == EXP_)) { - csound->Warning(csound, - "%s", Str("FLslidBnk2: FLslider exponential, " - "using non-labeled slider")); - slider_type -= 10; - } - if (slider_type <= 10) - o = new Fl_Slider(x, y, 10, height, Name); - else { - o = new Fl_Value_Slider_Input( csound, x, y, 10, height, Name); - slider_type -=10; - ((Fl_Value_Slider_Input*) o)->textboxsize(50); - ((Fl_Value_Slider_Input*) o)->textsize(13); - } - switch((int) slider_type) { //type - case 1: case 2: o->type(FL_VERT_FILL_SLIDER); break; - case 3: case 4: o->type(FL_VERT_SLIDER); break; - case 5: case 6: o->type(FL_VERT_NICE_SLIDER); o->box(FL_FLAT_BOX); break; - case 7: case 8: o->type(FL_VERT_NICE_SLIDER); o->box(FL_DOWN_BOX); break; - default: o->type(FL_VERT_NICE_SLIDER); o->box(FL_FLAT_BOX); break; - } - o->align(FL_ALIGN_BOTTOM); - if (plastic) o->box(FL_PLASTIC_DOWN_BOX); - widget_attributes(csound, o); - MYFLT min, max, range; - min = configtable[j*4]; - max = configtable[j*4+1]; - - p->slider_data[j].min=min; - p->slider_data[j].max=max; - p->slider_data[j].out=&outable[j]; - - switch (iexp) { - case -1: iexp = EXP_; break; - case 0: iexp = LIN_; break; - } - - MYFLT val = 0; - p->slider_data[j].exp = iexp; - - switch (iexp) { - case LIN_: //linear - o->range(min,max); - o->callback((Fl_Callback*)fl_callbackLinearSliderBank, - (void *) &(p->slider_data[j])); - val = outable[j]; - if (val > max) val = max; - else if (val < min) val = min; - break; - case EXP_ : //exponential - if (UNLIKELY(min == 0 || max == 0)) - return - csound->InitError(csound, - "%s", Str("FLsliderBank: zero is illegal " - "in exponential operations")); - range = max - min; - o->range(range,0); - #if defined(sun) - p->slider_data[j].base = ::pow((max / (double)min), 1.0/(double)range); - #else - p->slider_data[j].base = ::pow((max / min), 1.0/(double)range); - #endif - o->callback((Fl_Callback*)fl_callbackExponentialSliderBank, - (void *) &(p->slider_data[j])); - { - val = outable[j]; - MYFLT range = max-min; - #if defined(sun) - MYFLT base = ::pow(max / (double)min, 1.0/(double)range); - #else - MYFLT base = ::pow(max / min, 1.0/(double)range); - #endif - val = (log(val/min) / log(base)) ; - } - break; - default: - { - FUNC *ftp; - MYFLT fnum = abs(iexp); - if ((ftp = csound->FTnp2Find(csound, &fnum)) != NULL) - p->slider_data[j].table = ftp->ftable; - else return NOTOK; - p->slider_data[j].tablen = ftp->flen; - o->range(.99999999,0); - if (iexp > 0) //interpolated - o->callback((Fl_Callback*)fl_callbackInterpTableSliderBank, - (void *) &(p->slider_data[j])); - else // non-interpolated - o->callback((Fl_Callback*)fl_callbackTableSliderBank, - (void *) &(p->slider_data[j])); - } - } - o->value(val); - p->slider_data[j].widget_addr= (void*) o; - - } - w->resizable(w); - if (*p->iwidth <=0 || *p->iheight <=0) {// default width and height - int a,b; - w->size( a= w->parent()->w(), b= w->parent()->h()-50); - w->position(0, 0); - } - else { - w->size( (int)*p->iwidth, (int)*p->iheight); - w->position((int)*p->ix, (int)*p->iy); - } - w->end(); - widgetGlobals->AddrSetValue.push_back(ADDR_SET_VALUE(LIN_, 0, 0, - (void *) w, (void *) p, - widgetGlobals->currentSnapGroup)); - //*p->ihandle = widgetGlobals->AddrSetValue.size()-1; - widgetGlobals->last_sldbnk = widgetGlobals->AddrSetValue.size()-1; //gab - - return OK; - } - - static int fl_vertical_slider_bank2(CSOUND *csound, FLSLIDERBANK2 *p){ - return fl_vertical_slider_bank2_(csound,p,0); - } - - static int fl_vertical_slider_bank2_S(CSOUND *csound, FLSLIDERBANK2 *p){ - return fl_vertical_slider_bank2_(csound,p,1); - } - - - - static int fl_slider_bank_getHandle(CSOUND *csound, FLSLDBNK_GETHANDLE *p) - //valid only if called immediately after FLslidBnk - { - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - *p->ihandle = widgetGlobals->last_sldbnk; - return OK; - } - - static int fl_slider_bank_setVal(CSOUND *csound, FLSLDBNK_SET *p) - { - FUNC* ftp; - MYFLT *table, *outable; - int numslid = (int)*p->numSlid; - int startInd = (int)*p->startInd; - int startSlid = (int)*p->startSlid; - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - - if (LIKELY((ftp = csound->FTnp2Find(csound, p->ifn)) != NULL)) - table = ftp->ftable; - else { - return csound->InitError(csound, - "%s", Str("FLsldBnkSet: invalid table number")); - } - // *startInd, *startSlid, *numSlid - if (UNLIKELY( (int) ftp->flen < startInd + numslid)) { - return csound->InitError(csound, - "%s", Str("FLslidBnkSet: table too short!")); - } - FLSLIDERBANK *q = - (FLSLIDERBANK *)widgetGlobals->AddrSetValue[ (int) *p->ihandle].opcode; - - if (LIKELY((ftp = csound->FTnp2Find(csound, q->ioutable)) != NULL)) - outable = ftp->ftable; - else { - return csound->InitError(csound, - "%s", Str("FLsldBnkSet: invalid outable number")); - } - if (numslid == 0) numslid = (int)(q->elements - *p->startSlid); - if (UNLIKELY( q->elements > startSlid + numslid)) { - return csound->InitError(csound, "%s", - Str("FLslidBnkSet: too many sliders to reset!")); - } - for (int j = startSlid, k = startInd; j< numslid + startSlid; j++, k++) { - - MYFLT val = 0; - // MYFLT base = q->slider_data[j].base; - int iexp = q->slider_data[j].exp; - MYFLT min = q->slider_data[j].min; - MYFLT max = q->slider_data[j].max; - switch (iexp) { - case LIN_: //linear - val = table[k]; - if (val > max) val = max; - else if (val < min) val = min; - break; - case EXP_ : //exponential - { - MYFLT range = max - min; - MYFLT base2 = pow(max / min, 1/range); - val = (log(table[k]/min) / log(base2)) ; - } - break; - default: - return csound->InitError(csound, - "%s", Str("FLslidBnkSet: " - "function mapping not available")); - } - - FLlock(); - ((Fl_Slider*) (q->slider_data[j].widget_addr))->value(val); - FLunlock(); - outable[j] = table[k]; - } - return OK; - } - - static int fl_slider_bank2_setVal(CSOUND *csound, FLSLDBNK_SET *p) - { - FUNC* ftp; - MYFLT *table, *outable; - int numslid = (int)*p->numSlid; - int startInd = (int)*p->startInd; - int startSlid = (int)*p->startSlid; - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - - if (LIKELY((ftp = csound->FTnp2Find(csound, p->ifn)) != NULL)) - table = ftp->ftable; - else { - return csound->InitError(csound, - "%s", Str("FLsldBnkSet: invalid table number")); - } - // *startInd, *startSlid, *numSlid - if (UNLIKELY((int) ftp->flen < startInd + numslid)) { - return csound->InitError(csound, - "%s", Str("FLslidBnkSet: table too short!")); - } - FLSLIDERBANK2 *q = - (FLSLIDERBANK2 *)widgetGlobals->AddrSetValue[ (int) *p->ihandle].opcode; - - if (LIKELY((ftp = csound->FTnp2Find(csound, q->ioutable)) != NULL)) - outable = ftp->ftable; - else { - return csound->InitError(csound, - "%s", Str("FLsldBnkSet: invalid outable number")); - } - - if (numslid == 0) numslid = (int)(q->elements - *p->startSlid); - if (UNLIKELY( q->elements > startSlid + numslid)) { - return csound->InitError(csound, "%s", - Str("FLslidBnkSet: too many sliders to reset!")); - } - - for (int j = startSlid, k = startInd; j< numslid + startSlid; j++, k++) { - - MYFLT val = 0; - // MYFLT base = q->slider_data[j].base; - int iexp = q->slider_data[j].exp; - MYFLT min = q->slider_data[j].min; - MYFLT max = q->slider_data[j].max; - switch (iexp) { - case LIN_: //linear - val = table[k]; - if (val > max) val = max; - else if (val < min) val = min; - break; - case EXP_ : //exponential - { - MYFLT range = max - min; - MYFLT base2 = pow(max / min, 1/range); - val = (log(table[k]/min) / log(base2)); - } - break; - default: // table - { - // val = table[k]; - if (UNLIKELY(val < 0 || val > 1)) { // input range must be 0 to 1 - csound->PerfError(csound, &p->h, - "%s", Str("FLslidBnk2Setk: value out of range: " - "function mapping requires a 0 to 1 " - "range for input")); - } - } - //{ - // initerror("FLslidBnkSet: function mapping not available"); - // return; - //} - } - - FLlock(); - ((Fl_Slider*) (q->slider_data[j].widget_addr))->value(val); - FLunlock(); - outable[j] = table[k]; - } - return OK; - } - static int fl_slider_bank2_setVal_k_set(CSOUND *csound, FLSLDBNK2_SETK *p) - { - FUNC* ftp; - - p->numslid = (int)*p->numSlid; - p->startind = (int)*p->startInd; - p->startslid = (int)*p->startSlid; - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - - if (LIKELY((ftp = csound->FTnp2Find(csound, p->ifn)) != NULL)) - p->table = ftp->ftable; - else { - return csound->InitError(csound, - "%s", Str("FLsldBnkSetk: invalid table number")); - } - // *startInd, *startSlid, *numSlid - if (UNLIKELY( (int) ftp->flen < p->startind + p->numslid)) { - return csound->InitError(csound, - "%s", Str("FLslidBnkSetk: table too short!")); - } - p->q = (FLSLIDERBANK2 *) - widgetGlobals->AddrSetValue[ (int) *p->ihandle].opcode; - - if (LIKELY((ftp = csound->FTnp2Find(csound, p->q->ioutable)) != NULL)) - p->outable = ftp->ftable; - else { - return csound->InitError(csound, "%s", - Str("FLsldBnkSetk: invalid outable number")); - } - - if (p->numslid == 0) p->numslid = p->q->elements - p->startslid; - if (UNLIKELY( p->q->elements < p->startslid + p->numslid)) { - return csound->InitError(csound, "%s", - Str("FLslidBnkSetk: too many sliders to reset!")); - } - return OK; - } - - - - static int fl_slider_bank2_setVal_k(CSOUND *csound, FLSLDBNK2_SETK *p) - { - //WIDGET_GLOBALS *widgetGlobals = - // (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - - if(*p->kflag) { - FLSLIDERBANK2 *q = p->q; - MYFLT *table=p->table; - for (int j = p->startslid, k = p->startind; - j< p->numslid + p->startslid; - j++, k++) { - - MYFLT val = 0; - // MYFLT base= q->slider_data[j].base; - int iexp = q->slider_data[j].exp; - MYFLT min = q->slider_data[j].min; - MYFLT max = q->slider_data[j].max; - switch (iexp) { - case LIN_: //linear - val = table[k]; - if (val > max) val = max; - else if (val < min) val = min; - break; - case EXP_ : //exponential - { - MYFLT range = max - min; - MYFLT base2 = pow(max / min, 1/range); - val = (log(table[k]/min) / log(base2)); - } - break; - default: // table - { - val = table[k]; - if (UNLIKELY(val < 0 || val > 1)) { // input range must be 0 to 1 - csound->PerfError(csound, &p->h, - "%s", Str("FLslidBnk2Setk: value out of range:" - " function mapping requires a 0 to 1" - " range for input")); - } - } - } - if (val != p->oldValues[j]) { - FLlock(); - ((Fl_Slider*) (q->slider_data[j].widget_addr))->value(val); - ((Fl_Slider*) (q->slider_data[j].widget_addr))->do_callback(); - FLunlock(); - p->oldValues[j] = val; - } - } - } - return OK; - } - - static int fl_slider_bank_setVal_k_set(CSOUND *csound, FLSLDBNK_SETK *p) - { - FUNC* ftp; - WIDGET_GLOBALS *widgetGlobals = - (WIDGET_GLOBALS *)csound->QueryGlobalVariable(csound, "WIDGET_GLOBALS"); - p->numslid = (int)*p->numSlid; - p->startind = (int)*p->startInd; - p->startslid = (int)*p->startSlid; - - if (LIKELY((ftp = csound->FTnp2Find(csound, p->ifn)) != NULL)) - p->table = ftp->ftable; - else { - return csound->InitError(csound, - "%s", Str("FLslidBnkSetk: invalid table number")); - } - // *startInd, *startSlid, *numSlid - if (UNLIKELY( (int) ftp->flen < p->startind + p->numslid)) { - return csound->InitError(csound, - "%s", Str("FLslidBnkSetk: table too short!")); - } - p->q = - (FLSLIDERBANK *) widgetGlobals->AddrSetValue[ (int) *p->ihandle].opcode; - - if (LIKELY((ftp = csound->FTnp2Find(csound, p->q->ioutable)) != NULL)) - p->outable = ftp->ftable; - else { - return csound->InitError(csound, "%s", - Str("FLslidBnkSetk: invalid outable number")); - } - - if (p->numslid == 0) p->numslid = p->q->elements - p->startslid; - if (UNLIKELY( p->q->elements < p->startslid + p->numslid)) { - return csound->InitError(csound, "%s", - Str("FLslidBnkSetk: too many sliders to reset!")); - } - return OK; - } - - - - static int fl_slider_bank_setVal_k(CSOUND *csound, FLSLDBNK_SETK *p) - { - if (*p->kflag) { - FLSLIDERBANK *q = p->q; - MYFLT *table=p->table; - for (int j = p->startslid, k = p->startind; - j< p->numslid + p->startslid; - j++, k++) { - - MYFLT val = 0; - // MYFLT base= q->slider_data[j].base; //Not used - int iexp = q->slider_data[j].exp; - MYFLT min = q->slider_data[j].min; - MYFLT max = q->slider_data[j].max; - switch (iexp) { - case LIN_: //linear - val = table[k]; - if (val > max) val = max; - else if (val < min) val = min; - break; - case EXP_ : //exponential - { - MYFLT range = max - min; - MYFLT base2 = pow(max / min, 1/range); - val = (log(table[k]/min) / log(base2)); - } - break; - default: // table - { - val = table[k]; - if (UNLIKELY(val < 0 || val > 1)) { // input range must be 0 to 1 - csound->PerfError(csound, &p->h, - "%s", Str("FLslidBnk2Setk: value out of range: " - "function mapping requires a 0 to 1 range " - "for input")); - } - } - } - if (val != p->oldValues[j]) { - FLlock(); - ((Fl_Slider*) (q->slider_data[j].widget_addr))->value(val); - ((Fl_Slider*) (q->slider_data[j].widget_addr))->do_callback(); - FLunlock(); - p->oldValues[j] = val; - } - } - } - return OK; - } - - static int FLxyin_set(CSOUND *csound, FLXYIN *p) - { - - *p->koutx = *p->ioutx; //initial values - *p->kouty = *p->iouty; - p->rangex = *p->ioutx_max - *p->ioutx_min; - p->rangey = *p->iouty_max - *p->iouty_min; - - switch((int) *p->iexpx) { - case 0: // LIN - p->expx = LIN_; break; - case -1: // EXP - p->expx = EXP_; - if (UNLIKELY(*p->ioutx_min == 0 || *p->ioutx_max==0)) - return csound->InitError(csound, "%s", - Str("FLxyin: none of X limits can be zero in" - " exponential mode!")); - p->basex = pow((double) (*p->ioutx_max / *p->ioutx_min), - (double) (1/p->rangex)); - - break; - default: // TABLE - p->expx = (int) *p->iexpx; - { - FUNC *ftp; - MYFLT fnum = abs(p->expx); - if ((ftp = csound->FTnp2Find(csound, &fnum)) != NULL) { - p->tablex = ftp->ftable; - p->tablenx = ftp->flen; - } - else return NOTOK; - } - - } - - switch((int) *p->iexpy) { - case 0: // LIN - p->expy = LIN_; break; - case -1: // EXP - p->expy = EXP_; - if (UNLIKELY(*p->iouty_min == 0 || *p->iouty_max==0)) - return csound->InitError(csound, - "%s", Str("FLxyin: none of Y limits can " - "be zero in exponential mode!")); - p->basey = pow((double) (*p->iouty_max / *p->iouty_min), - (double) (1/p->rangey)); - break; - default: // TABLE - p->expy = (int) *p->iexpy; - { - FUNC *ftp; - MYFLT fnum = abs(p->expy); - if ((ftp = csound->FTnp2Find(csound, &fnum)) != NULL) { - p->tabley = ftp->ftable; - p->tableny = ftp->flen; - } - else return NOTOK; - } - - } - return OK; - } - - static int FLxyin(CSOUND *csound, FLXYIN *p) - { - IGN(csound); - int windx_min = (int)*p->iwindx_min, windx_max = (int)*p->iwindx_max; - int windy_min = (int)*p->iwindy_min, windy_max = (int)*p->iwindy_max; - MYFLT outx_min = *p->ioutx_min, outy_min = *p->iouty_min; - - MYFLT x=Fl::event_x(); - MYFLT y=Fl::event_y(); - - *p->kinside = 1; - - if (x< windx_min) { x = windx_min; *p->kinside = 0; } - else if (x > windx_max) { x = windx_max; *p->kinside = 0; } - - if (y< windy_min) { y = windy_min; *p->kinside = 0; } - else if (y > windy_max) { y = windy_max; *p->kinside = 0; } - - MYFLT xx = x - windx_min; - xx /= windx_max - windx_min; - - MYFLT yy = windy_max - y; - yy /= windy_max - windy_min; - - switch (p->expx) { - case LIN_: - *p->koutx = outx_min + xx * p->rangex; - break; - case EXP_: - *p->koutx = outx_min * pow (p->basex, xx * p->rangex); - break; - default: // TABLE - if (p->expx > 0) { //interpolated - MYFLT ndx = xx * (p->tablenx-1); - int index = (int) ndx; - MYFLT v1 = p->tablex[index]; - *p->koutx = outx_min + ( v1 + (p->tablex[index+1] - v1) * - (ndx - index)) * p->rangex; - } - else // non-interpolated - *p->koutx = outx_min + p->tablex[(int) (xx*p->tablenx)] * p->rangex; - } - - switch (p->expy) { - case LIN_: - *p->kouty = outy_min + yy * p->rangey; - break; - case EXP_: - *p->kouty = outy_min * pow (p->basey, yy * p->rangey); - break; - default: // TABLE - if (p->expy > 0) { //interpolated - MYFLT ndx = yy * (p->tableny-1); - int index = (int) ndx; - MYFLT v1 = p->tabley[index]; - *p->kouty = outy_min + ( v1 + ( p->tabley[index+1] - v1) * - (ndx - index)) * p->rangey; - } - else // non-interpolated - *p->kouty = outy_min + p->tabley[(int) (yy*p->tableny)] * p->rangey; - } - return OK; - } - - // //FIXME: Should this be defined to 0dbfs? - // #define MAXAMPVU 32767 - // - // //TODO: Implement FLTK meter - // void VuMeter(CSOUND *csound); - // - // static int VuMeter_set(CSOUND *csound, FLTKMETER *p) { - // //isVumeterActive = 1; - // csoundSetVuMeterCallback(csound, VuMeter); - // //OPARMS *O = csound->oparms; - // int nchnls = csound->nchnls; - // Fl_Window *o; - // int iw; - // iw = (csound->oparms->sfread) ? 30 : 15; - // o = new Fl_Window(0,0, 410,nchnls * iw+35, "Meter"); - // Fl_Box *box = new Fl_Box(10,3,260,10,"Outputs"); - // box->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE); - // //else o = new Fl_Panel(x,y,width,height, panelName); - // //o->box((Fl_Boxtype) borderType); - // o->resizable(o); - // o->callback(flpanel_cb); - // //o->set_modal(); - // widget_attributes(csound,o); - // - // PANELS panel(o, (widgetGlobals->stack_count>0) ? 1 : 0); - // widgetGlobals->fl_windows.push_back(panel); - // int j; - // - // for ( j = 0; j < nchnls; j++) { - // string stemp; - // char s[10]; - // stemp = itoa(j+1,s,10); - // char *Name = new char[stemp.size()+2]; - // strcpy(Name,stemp.c_str()); - // widgetGlobals->allocatedStrings.push_back(Name); - // - // Fl_Slider *w = new Fl_Slider(20, 18+15*j, 380, 10, Name); - // w->align(FL_ALIGN_LEFT); - // p->widg_address[j] = (unsigned long) w; - // w->type(FL_HOR_FILL_SLIDER); - // w->color(FL_BACKGROUND_COLOR,FL_GREEN); - // w->box(FL_PLASTIC_DOWN_BOX); - // w->range(0,MAXAMPVU); - // } - // - // if (csound->oparms->sfread) { - // box = new Fl_Box(10,18 + 15 * nchnls ,260,10,"Inputs"); - // box->align(FL_ALIGN_LEFT | FL_ALIGN_INSIDE); - // for ( ; j < nchnls*2; j++) { - // string stemp; - // char s[10]; - // stemp = itoa(j+1- nchnls,s,10); - // char *Name = new char[stemp.size()+2]; - // strcpy(Name,stemp.c_str()); - // widgetGlobals->allocatedStrings.push_back(Name); - // Fl_Slider *w = new Fl_Slider(20, 30+15*j , 380, 10, Name); - // w->align(FL_ALIGN_LEFT); - // p->widg_address[j] = (unsigned long) w; - // w->type(FL_HOR_FILL_SLIDER); - // w->color(FL_BACKGROUND_COLOR,FL_YELLOW); - // w->box(FL_PLASTIC_DOWN_BOX); - // w->range(0,MAXAMPVU); - // } - // } - // o->end(); - // widgetGlobals->p_vumeter = p; - // p->dummycycles = csound->ekr / 16 ; - // p->dummycyc = 0; - // return OK; - // } - // - // //extern "C" MYFLT *spout,*spin; //GAB - // - // void VuMeter(CSOUND *csound){ - // FLTKMETER *p = widgetGlobals->p_vumeter; - // Fl_Slider *w; - // int nchnls = csound->nchnls; - // int n = (csound->oparms->sfread) ? nchnls * 2 : nchnls; - // MYFLT temp[MAXCHNLS]; - // int smps = csound->GetKsmps(csound); - // MYFLT max[MAXCHNLS]; - // MYFLT *spo = csound->spout; - // MYFLT *spi = csound->spin; - // - // static int overfl[MAXCHNLS]; - // int i; - // for (i=0;i max[i] ) - // max[i]= temp[i]; - // } - // if (csound->oparms->sfread) { - // for ( ; i max[i] ) - // max[i]= temp[i]; - // } - // } - // //if ((temp= (MYFLT) fabs(*a++)) > max) max = temp; - // } while (--smps); - // - // for (i=0; i p->max[i]) - // p->max[i] = (overfl[i] = max[i]>MAXAMPVU) ? MAXAMPVU : max[i]; - // } - // p->dummycyc++; - // if (!(p->dummycyc % p->dummycycles)) { - // for (int j=0; jwidg_address[j]; - // - // FLlock(); - // if (overfl[j]) { - // w->selection_color(FL_RED); - // overfl[j] = 0; - // } - // else { - // if (j < nchnls) w->selection_color(FL_GREEN); - // else w->selection_color(FL_YELLOW); - // } - // w->value( p->max[j]); - // w->do_callback(w); - // FLunlock(); - // } - // #ifdef WIN32 - // // PostMessage(callback_target,0,0,0); - // #endif - // for (i=0; imax[i] = 0; - // } - // } - // - -} // extern "C" - -#define S(x) sizeof(x) - -const OENTRY widgetOpcodes_[] = { - { (char*)"FLslider", S(FLSLIDER), 0, 1, (char*)"ki", (char*)"Siijjjjjjj", - (SUBR) fl_slider, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLslidBnk", S(FLSLIDERBANK), 0, 1, (char*)"", (char*)"Siooooooooo", - (SUBR) fl_slider_bank_S, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLslidBnk.i", S(FLSLIDERBANK), 0, 1, (char*)"", (char*)"iiooooooooo", - (SUBR) fl_slider_bank, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLknob", S(FLKNOB), 0, 1, (char*)"ki", (char*)"Siijjjjjjo", - (SUBR) fl_knob, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLroller", S(FLROLLER), 0, 1, (char*)"ki", (char*)"Siijjjjjjjj", - (SUBR) fl_roller, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLtext", S(FLTEXT), 0, 1, (char*)"ki", (char*)"Siijjjjjj", - (SUBR) fl_text, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLjoy", S(FLJOYSTICK), 0, 1, (char*)"kkii", (char*)"Siiiijjjjjjjj", - (SUBR) fl_joystick, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLcount", S(FLCOUNTER), 0, 1, (char*)"ki", (char*)"Siiiiiiiiiz", - (SUBR) fl_counter, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLbutton", S(FLBUTTON), 0, 1, (char*)"ki", (char*)"Siiiiiiiz", - (SUBR) fl_button, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLbutBank", S(FLBUTTONBANK), 0, 1, (char*)"ki", (char*)"iiiiiiiz", - (SUBR) fl_button_bank, (SUBR) NULL, (SUBR) NULL }, - // { (char*)"FLkeyb", S(FLKEYB), 0, 1, (char*)"k", (char*)"z", - // (SUBR) FLkeyb, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLcolor", S(FLWIDGCOL), 0, 1, (char*)"", (char*)"jjjjjj", - (SUBR) fl_widget_color, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLcolor2", S(FLWIDGCOL2), 0, 1, (char*)"", (char*)"jjj", - (SUBR) fl_widget_color2, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLlabel", S(FLWIDGLABEL), 0, 1, (char*)"", (char*)"ojojjj", - (SUBR) fl_widget_label, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLsetVal_i", S(FL_SET_WIDGET_VALUE_I), 0, 1, (char*)"", (char*)"ii", - (SUBR) fl_setWidgetValuei, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLsetVali", S(FL_SET_WIDGET_VALUE_I), 0, 1, (char*)"", (char*)"ii", - (SUBR) fl_setWidgetValuei, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLsetVal", S(FL_SET_WIDGET_VALUE),0, 3, (char*)"", (char*)"kki", - (SUBR) fl_setWidgetValue_set, (SUBR) fl_setWidgetValue, (SUBR) NULL }, - { (char*)"FLsetColor", S(FL_SET_COLOR), 0, 1, (char*)"", (char*)"iiii", - (SUBR) fl_setColor1, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLsetColor2", S(FL_SET_COLOR), 0, 1, (char*)"", (char*)"iiii", - (SUBR) fl_setColor2, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLsetTextSize", S(FL_SET_TEXTSIZE), 0, 1, (char*)"", (char*)"ii", - (SUBR) fl_setTextSize, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLsetTextColor", S(FL_SET_COLOR), 0, 1, (char*)"", (char*)"iiii", - (SUBR) fl_setTextColor, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLsetFont", S(FL_SET_FONT), 0, 1, (char*)"", (char*)"ii", - (SUBR) fl_setFont, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLsetTextType", S(FL_SET_FONT), 0, 1, (char*)"", (char*)"ii", - (SUBR) fl_setTextType, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLsetText", S(FL_SET_TEXT), 0, 1, (char*)"", (char*)"Si", - (SUBR) fl_setText, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLsetText", S(FL_SET_TEXTi), 0, 1, (char*)"", (char*)"ii", - (SUBR) fl_setTexti, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLsetSize", S(FL_SET_SIZE), 0, 1, (char*)"", (char*)"iii", - (SUBR) fl_setSize, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLsetPosition", S(FL_SET_POSITION), 0, 1, (char*)"", (char*)"iii", - (SUBR) fl_setPosition, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLhide", S(FL_WIDHIDE), 0, 1, (char*)"", (char*)"i", - (SUBR) fl_hide, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLshow", S(FL_WIDSHOW), 0, 1, (char*)"", (char*)"i", - (SUBR) fl_show, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLsetBox", S(FL_SETBOX), 0, 1, (char*)"", (char*)"ii", - (SUBR) fl_setBox, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLsetAlign", S(FL_TALIGN), 0, 1, (char*)"", (char*)"ii", - (SUBR) fl_align, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLbox", S(FL_BOX), 0, 1, (char*)"i", (char*)"Siiiiiii", - (SUBR) fl_box_s, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLbox", S(FL_BOX), 0, 1, (char*)"i", (char*)"iiiiiiii", - (SUBR) fl_box_i, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLvalue", S(FLVALUE), 0, 1, (char*)"i", (char*)"Sjjjj", - (SUBR) fl_value, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLpanel", S(FLPANEL), 0, 1, (char*)"", (char*)"Sjjjoooo", - (SUBR) StartPanel, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLpanelEnd", S(FLPANELEND), 0, 1, (char*)"", (char*)"", - (SUBR) EndPanel, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLpanel_end", S(FLPANELEND), 0, 1, (char*)"", (char*)"", - (SUBR) EndPanel, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLscroll", S(FLSCROLL), 0, 1, (char*)"", (char*)"iiii", - (SUBR) StartScroll, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLscrollEnd", S(FLSCROLLEND), 0, 1, (char*)"", (char*)"", - (SUBR) EndScroll, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLscroll_end",S(FLSCROLLEND), 0, 1, (char*)"", (char*)"", - (SUBR) EndScroll, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLpack", S(FLPACK), 0, 1, (char*)"", (char*)"iiiiooo", - (SUBR) StartPack, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLpackEnd", S(FLPACKEND), 0, 1, (char*)"", (char*)"", - (SUBR) EndPack, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLpack_end", S(FLPACKEND), 0, 1, (char*)"", (char*)"", - (SUBR) EndPack, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLtabs", S(FLTABS), 0, 1, (char*)"", (char*)"iiii", - (SUBR) StartTabs, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLtabsEnd", S(FLTABSEND), 0, 1, (char*)"", (char*)"", - (SUBR) EndTabs, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLtabs_end", S(FLTABSEND), 0, 1, (char*)"", (char*)"", - (SUBR) EndTabs, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLgroup", S(FLGROUP), 0, 1, (char*)"", (char*)"Siiiij", - (SUBR) StartGroup, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLgroupEnd", S(FLGROUPEND), 0, 1, (char*)"", (char*)"", - (SUBR) EndGroup, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLgroup_end", S(FLGROUPEND), 0, 1, (char*)"", (char*)"", - (SUBR) EndGroup, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLsetsnap", S(FLSETSNAP), 0, 1, (char*)"ii", (char*)"ioo", - (SUBR) set_snap, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLsetSnapGroup", S(FLSETSNAPGROUP), 0, 1, (char*)"", (char*)"i", - (SUBR)fl_setSnapGroup, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLgetsnap", S(FLGETSNAP), 0, 1, (char*)"i", (char*)"io", - (SUBR) get_snap, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLsavesnap", S(FLSAVESNAPS), 0, 1, (char*)"", (char*)"So", - (SUBR) save_snap, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLloadsnap", S(FLLOADSNAPS), 0, 1, (char*)"", (char*)"So", - (SUBR) load_snap, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLrun", S(FLRUN), 0, 1, (char*)"", (char*)"", - (SUBR) FL_run, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLupdate", S(FLRUN), 0, 1, (char*)"", (char*)"", - (SUBR) fl_update, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLprintk", S(FLPRINTK), 0, 3, (char*)"", (char*)"iki", - (SUBR) FLprintkset, (SUBR) FLprintk, (SUBR) NULL }, - { (char*)"FLprintk2", S(FLPRINTK2), 0, 3, (char*)"", (char*)"ki", - (SUBR) FLprintk2set, (SUBR) FLprintk2, (SUBR) NULL }, - { (char*)"FLcloseButton", S(FLCLOSEBUTTON), 0, 1, (char*)"i", (char*)"Siiii", - (SUBR) fl_close_button, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLexecButton", S(FLEXECBUTTON), 0, 1, (char*)"i", (char*)"Siiii", - (SUBR) fl_exec_button, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLkeyIn", S(FLKEYIN), 0, 3, (char*)"k", (char*)"o", - (SUBR)fl_keyin_set, (SUBR)fl_keyin, (SUBR) NULL }, - { (char*)"FLxyin", S(FLXYIN), 0, 3, (char*)"kkk",(char*)"iiiiiiiioooo", - (SUBR)FLxyin_set, (SUBR)FLxyin, (SUBR) NULL }, - { (char*)"FLmouse", S(FLMOUSE), 0, 3, (char*)"kkkkk",(char*)"o", - (SUBR)fl_mouse_set, (SUBR)fl_mouse, (SUBR) NULL }, - { (char*)"FLvslidBnk", S(FLSLIDERBANK), 0, 1, (char*)"", (char*)"Siooooooooo", - (SUBR)fl_vertical_slider_bank_S, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLvslidBnk.i", S(FLSLIDERBANK), 0, 1, (char*)"", (char*)"iiooooooooo", - (SUBR)fl_vertical_slider_bank, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLslidBnk2", S(FLSLIDERBANK2),0, 1, (char*)"", (char*)"Siiiooooo", - (SUBR)fl_slider_bank2_S , (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLslidBnk2.i", S(FLSLIDERBANK2),0, 1, (char*)"", (char*)"iiiiooooo", - (SUBR)fl_slider_bank2 , (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLvslidBnk2", S(FLSLIDERBANK2),0, 1, (char*)"", (char*)"Siiiooooo", - (SUBR)fl_vertical_slider_bank2_S, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLvslidBnk2.i", S(FLSLIDERBANK2),0, 1, (char*)"", (char*)"iiiiooooo", - (SUBR)fl_vertical_slider_bank2, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLslidBnkGetHandle",S(FLSLDBNK_GETHANDLE),0, 1, (char*)"i", (char*)"", - (SUBR)fl_slider_bank_getHandle, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLslidBnkSet",S(FLSLDBNK_SET), 0, 1, (char*)"", (char*)"iiooo", - (SUBR)fl_slider_bank_setVal, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLslidBnkSetk", S(FLSLDBNK2_SETK), 0, 3, (char*)"", (char*)"kiiooo", - (SUBR)fl_slider_bank_setVal_k_set,(SUBR)fl_slider_bank_setVal_k,(SUBR) NULL }, - { (char*)"FLslidBnk2Set", S(FLSLDBNK_SET), 0, 1, (char*)"", (char*)"iiooo", - (SUBR)fl_slider_bank2_setVal, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLslidBnk2Setk", S(FLSLDBNK2_SETK), 0, 3, (char*)"", (char*)"kiiooo", - (SUBR)fl_slider_bank2_setVal_k_set, (SUBR)fl_slider_bank2_setVal_k, - (SUBR) NULL }, - { (char*)"FLhvsBox", S(FL_HVSBOX), 0, 1, (char*)"i", (char*)"iiiiiio", - (SUBR)fl_hvsbox, (SUBR) NULL, (SUBR) NULL }, - { (char*)"FLhvsBoxSetValue",S(FL_SET_HVS_VALUE), 0, 3, (char*)"", (char*)"kki", - (SUBR)fl_setHvsValue_set, (SUBR)fl_setHvsValue, (SUBR) NULL }, - // { (char*)"FLmeter", S(FLTKMETER), 0, 1, (char*)"", (char*)"", - // (SUBR)VuMeter_set, (SUBR) NULL, (SUBR) NULL }, - { NULL, 0, 0, 0, NULL, NULL, - (SUBR) NULL, (SUBR) NULL, (SUBR) NULL } -}; diff --git a/6.x/src/widgets/widgets.h b/6.x/src/widgets/widgets.h deleted file mode 100644 index 62320aaf..00000000 --- a/6.x/src/widgets/widgets.h +++ /dev/null @@ -1,645 +0,0 @@ -/* - widgets.h: - - Copyright (C) 2002 Gabriel Maldonado - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#ifndef CSOUND_WIDGETS_H -#define CSOUND_WIDGETS_H - -#ifdef __cplusplus -extern "C" { -#endif - -typedef struct { - OPDS h; - MYFLT *kout, *ihandle; - STRINGDAT *name; - MYFLT *imin, *imax, *iexp; - MYFLT *itype, *idisp, *iwidth, *iheight, *ix, *iy; - MYFLT min, base, *table; - long tablen; -} FLSLIDER; - -#define MAXSLIDERBANK 128 - -typedef struct { - void *widget_addr; //gab - MYFLT min, max, *out; - MYFLT base, *table; - long tablen; - int exp; -} SLDBK_ELEMENT; - -// --------------------------------------------------------------------- - -typedef struct { - OPDS h; - MYFLT *names; - MYFLT *inumsliders, *ioutable, *iwidth, *iheight; - MYFLT *ix, *iy, *itypetable, *iexptable, *ioutablestart_ndx; - MYFLT *iminmaxtable; - SLDBK_ELEMENT slider_data[MAXSLIDERBANK]; - long elements; -} FLSLIDERBANK; - -typedef struct { - OPDS h; - MYFLT *names; - MYFLT *inumsliders, *ioutable, *iconfigtable, *iwidth, - *iheight, *ix, *iy, *ioutablestart_ndx; - SLDBK_ELEMENT slider_data[MAXSLIDERBANK]; - long elements; -} FLSLIDERBANK2; //gab - -// typedef struct { -// OPDS h; -// MYFLT *kout, *args[PMAX+1]; -// } FLKEYB; - -typedef struct { - OPDS h; - MYFLT *kvalue, *ihandle; -} FLBUTTONCALL; - -typedef struct { - OPDS h; - MYFLT *koutx, *kouty, *ihandle1, *ihandle2; - STRINGDAT *name; - MYFLT *iminx, *imaxx, *iminy, *imaxy; - MYFLT *iexpx, *iexpy, *idispx, *idispy, *iwidth, *iheight, *ix, *iy; - MYFLT basex, basey, *tablex, *tabley; - long tablenx, tableny; -} FLJOYSTICK; - -typedef struct { - OPDS h; - MYFLT *kout, *ihandle; - STRINGDAT *name; - MYFLT *imin, *imax, *istep, *iexp; - MYFLT *itype, *idisp, *iwidth, *iheight, *ix, *iy; - MYFLT min, base, *table; - long tablen; -} FLROLLER; - -typedef struct { - OPDS h; - MYFLT *kout, *ihandle; - STRINGDAT *name; - MYFLT *imin, *imax, *iexp, *itype; - MYFLT *idisp, *iwidth, *ix, *iy, *icursorsize; - MYFLT min, base, *table; - long tablen; -} FLKNOB; - -typedef struct { - OPDS h; - MYFLT *kout, *ihandle; - STRINGDAT *name; - MYFLT *imin, *imax, *istep, *itype; - MYFLT *iwidth, *iheight, *ix, *iy; -} FLTEXT; - -// --------------------------------------------------------------------- - -typedef struct { - OPDS h; - MYFLT *red1, *green1, *blue1, *red2, *green2, *blue2; -} FLWIDGCOL; - -typedef struct { - OPDS h; - MYFLT *red, *green, *blue; -} FLWIDGCOL2; - -typedef struct { - OPDS h; - MYFLT *size, *font, *align, *red, *green, *blue; -} FLWIDGLABEL; - -// --------------------------------------------------------------------- - -typedef struct { - OPDS h; - MYFLT *ivalue, *ihandle; -} FL_SET_WIDGET_VALUE_I; - -typedef struct { - OPDS h; - MYFLT *ktrig, *kvalue, *ihandle; - int handle; - int widgetType; - MYFLT log_base; -} FL_SET_WIDGET_VALUE; - -typedef struct { - OPDS h; - MYFLT *red, *green, *blue, *ihandle; -} FL_SET_COLOR; - -typedef struct { - OPDS h; - MYFLT *ivalue, *ihandle; -} FL_SET_TEXTSIZE; - -typedef struct { - OPDS h; - MYFLT *itype, *ihandle; -} FL_SET_FONT; - -typedef struct { - OPDS h; - STRINGDAT *itext; - MYFLT *ihandle; -} FL_SET_TEXT; - -typedef struct { - OPDS h; - MYFLT *ndx; - MYFLT *ihandle; -} FL_SET_TEXTi; - -typedef struct { - OPDS h; - MYFLT *itype, *ihandle; -} FL_TALIGN; - -typedef struct { - OPDS h; - MYFLT *iwidth, *iheight, *ihandle; -} FL_SET_SIZE; - -typedef struct { - OPDS h; - MYFLT *ix, *iy, *ihandle; -} FL_SET_POSITION; - -typedef struct { - OPDS h; - MYFLT *ihandle; -} FL_WIDHIDE; - -typedef struct { - OPDS h; - MYFLT *ihandle; -} FL_WIDSHOW; - -typedef struct { - OPDS h; - MYFLT *itype, *ihandle; -} FL_SETBOX; - -typedef struct { - OPDS h; - MYFLT *ihandle; - STRINGDAT *itext; - MYFLT *itype, *ifont, *isize; - MYFLT *iwidth, *iheight, *ix, *iy; -} FL_BOX; - -// --------------------------------------------------------------------- - -typedef struct { - OPDS h; - MYFLT *ihandle; - STRINGDAT *name; - MYFLT *iwidth, *iheight, *ix, *iy; -} FLVALUE; - -typedef struct { - OPDS h; -} FLRUN; - -typedef struct { - OPDS h; - STRINGDAT *name; - MYFLT *iwidth, *iheight, *ix, *iy, *border, *ikbdsense, *iclose; -} FLPANEL; - -typedef struct { - OPDS h; - MYFLT *inum_snap, *inum_val, *index, *ifn, *group; -} FLSETSNAP; - -typedef struct { - OPDS h; - MYFLT *inum_el, *index, *group; -} FLGETSNAP; - -typedef struct { - OPDS h; - STRINGDAT *filename; - MYFLT *group; -} FLSAVESNAPS; - -typedef struct { - OPDS h; - STRINGDAT *filename; - MYFLT *group; -} FLLOADSNAPS; - -typedef struct { - OPDS h; -} FLPANELEND; - -typedef struct { - OPDS h; - MYFLT *iwidth, *iheight, *ix, *iy; -} FLSCROLL; - -typedef struct { - OPDS h; -} FLSCROLLEND; - -typedef struct { - OPDS h; - MYFLT *iwidth, *iheight, *ix, *iy; -} FLTABS; - -typedef struct { - OPDS h; -} FLTABSEND; - -typedef struct { - OPDS h; - STRINGDAT *name; - MYFLT *iwidth, *iheight, *ix, *iy, *border; -} FLGROUP; - -typedef struct { - OPDS h; -} FLGROUPEND; - -typedef struct { - OPDS h; - MYFLT *iwidth, *iheight, *ix, *iy, *itype, *ispace, *iborder; -} FLPACK; - -typedef struct { - OPDS h; -} FLPACKEND; - -typedef struct { - OPDS h; - MYFLT *kout, *ihandle; - STRINGDAT *name; - MYFLT *ion, *ioff, *itype; - MYFLT *iwidth, *iheight, *ix, *iy, *args[PMAX]; -} FLBUTTON; - -typedef struct { - OPDS h; - MYFLT *kout, *ihandle; /* outs */ - MYFLT *itype, *inumx, *inumy, *iwidth, *iheight, *ix, *iy, *args[PMAX]; -} FLBUTTONBANK; - -typedef struct { - OPDS h; - MYFLT *kout, *ihandle; - STRINGDAT *name; - MYFLT *imin, *imax, *istep1, *istep2, *itype; - MYFLT *iwidth, *iheight, *ix, *iy, *args[PMAX]; -} FLCOUNTER; - -typedef struct { - OPDS h; - MYFLT *ptime, *val, *idisp; - MYFLT initime, ctime; - long cysofar; -} FLPRINTK; - -typedef struct { - OPDS h; - MYFLT *val, *idisp; - MYFLT oldvalue; - /* int pspace; */ -} FLPRINTK2; - -typedef struct { - OPDS h; - MYFLT *ihandle; - STRINGDAT *name; - MYFLT *iwidth, *iheight, *ix, *iy; -} FLCLOSEBUTTON; - -typedef struct { - OPDS h; - MYFLT *ihandle; - STRINGDAT *command; - MYFLT *iwidth, *iheight, *ix, *iy; - char *commandString; - CSOUND *csound; -} FLEXECBUTTON; - -#ifdef __cplusplus -} -#endif - -#if defined(CSOUND_WIDGETS_CPP) - -/* ---- IV - Aug 23 2002 ---- included file: Fl_Ball.H */ - -class Fl_Ball : public Fl_Valuator { -private: - CSOUND *csound; - int ix, iy, drag; - int spinning; - - int ballstacks, ballslices; - float ballsize; - - float curquat[4]; - float lastquat[4]; - float mat[4][4]; - - char soft_; - uchar mouseobj; - - static PUBLIC void repeat_callback(void *); - PUBLIC void increment_cb(); - PUBLIC void vertex_by_matrix(float &x, float &y, float &z); - - PUBLIC void transform_ball_vertex(float &x, float &y, float &z); - PUBLIC void rotate(float &x, float &y, float &z, - float rotx=0.0f, float roty=0.0f, float rotz=0.0f); - PUBLIC void draw_solid_ball(float radius, int slices, int stacks); - PUBLIC void draw_wire_ball(float radius, int slices, int stacks); -public: - PUBLIC void draw(); - PUBLIC void handle_drag(double v=0.0); - PUBLIC void handle_release(); - PUBLIC int handle(int); - PUBLIC Fl_Ball(CSOUND *cs, int x, int y, int w, int h, const char *l = 0); - - void soft(char x) {soft_ = x;} - char soft() const {return soft_;} - - float ballscale() const {return ballsize;} - void ballscale(float s) { ballsize=s;} - - void rotateball(float rotx=0.0f, float roty=0.0f, float rotz=0.0f); - void getrot(float &rotx, float &roty, float &rotz); - - void stacks(int s) { ballstacks=s; } - int stacks() const { return ballstacks; } - - void slices(int s) { ballslices=s; } - int slices() const { return ballslices; } - - ~Fl_Ball(); -}; - -/* ---- IV - Aug 23 2002 ---- included file: Fl_Knob.H */ - -/* generated by Fast Light User Interface Designer (fluid) version 2.00 */ - -class Fl_Knob : public Fl_Valuator { - public: - enum Fl_Knobtype { - DOTLIN=0, - DOTLOG_1, - DOTLOG_2, - DOTLOG_3, - LINELIN, - LINELOG_1, - LINELOG_2, - LINELOG_3 - }; - private: - CSOUND *csound; - int _type; - float _percent; - int _scaleticks; - short a1, a2; - public: - Fl_Knob(CSOUND *cs, int xx, int yy, int ww, int hh, const char *l=0); - ~Fl_Knob(); - private: - void draw(); - int handle(int event); - public: - void type(int ty); - private: - void shadow(const int offs, const uchar r, uchar g, uchar b); - void draw_scale(const int ox, const int oy, const int side); - void draw_cursor(const int ox, const int oy, const int side); - public: - void cursor(const int pc); - void scaleticks(const int tck); -}; - -/* ---- IV - Aug 23 2002 ---- included file: Fl_Spin.H */ - -class Fl_Spin : public Fl_Valuator { - private: - CSOUND * csound; - int ix, iy, drag, indrag; - int delta, deltadir; - char soft_; - uchar mouseobj; - static PUBLIC void repeat_callback(void *); - PUBLIC void increment_cb(); - - public: - PUBLIC void draw(); - PUBLIC int handle(int); - PUBLIC Fl_Spin(CSOUND *cs, int x, int y, int w, int h, const char *l = 0); - - void soft(char x) {soft_ = x;} - char soft() const {return soft_;} - - ~Fl_Spin(); -}; - -/* ---- IV - Aug 23 2002 ---- included file: Fl_Value_Input_Spin.H */ - -class Fl_Value_Input_Spin : public Fl_Valuator { - private: - CSOUND * csound; - int ix, iy, drag; - int delta, deltadir; - char soft_; - uchar mouseobj; - int butsize; - static PUBLIC void input_cb(Fl_Widget*, void*); - // cause damage() due to value() changing - virtual PUBLIC void value_damage(); - static PUBLIC void repeat_callback(void *); - PUBLIC void increment_cb(); - - public: - Fl_Input input; - PUBLIC void draw(); - PUBLIC int handle(int); - PUBLIC void resize(int, int, int, int); - PUBLIC Fl_Value_Input_Spin(CSOUND *csound, int x, int y, int w, int h, - const char *l = 0); - - void soft(char x) {soft_ = x;} - char soft() const {return soft_;} - - Fl_Font textfont() const {return input.textfont();} - void textfont(uchar s) { input.textfont(s);} - uchar textsize() const {return input.textsize();} - void textsize(uchar s) {input.textsize(s);} - Fl_Color textcolor() const {return input.textcolor();} - void textcolor(uchar n) {input.textcolor(n);} - Fl_Color cursor_color() const {return input.cursor_color();} - void cursor_color(uchar n) {input.cursor_color(n);} - int buttonssize() const {return butsize;} - void buttonssize(int s) { butsize=s;} - ~Fl_Value_Input_Spin(); -}; - -/* ---- IV - Aug 23 2002 ---- included file: Fl_Value_Slider_Input.H */ - -class Fl_Value_Slider_Input : public Fl_Value_Slider { - private: - CSOUND *csound; - char soft_; - int txtboxsize; - - static PUBLIC void input_cb(Fl_Widget*, void*); - // cause damage() due to value() changing - virtual PUBLIC void value_damage(); - - public: - Fl_Input input; - PUBLIC void draw(); - PUBLIC int handle(int); - PUBLIC void resize(int, int, int, int); - PUBLIC Fl_Value_Slider_Input(CSOUND * cs, int x, int y, int w, int h, - const char *l = 0); - - void soft(char x) {soft_ = x;} - char soft() const {return soft_;} - - Fl_Font textfont() const {return input.textfont();} - void textfont(uchar s) { input.textfont(s);} - uchar textsize() const {return input.textsize();} - void textsize(uchar s) {input.textsize(s);} - Fl_Color textcolor() const {return input.textcolor();} - void textcolor(uchar n) {input.textcolor(n);} - Fl_Color cursor_color() const {return input.cursor_color();} - void cursor_color(uchar n) {input.cursor_color(n);} - void textboxsize(int s) { txtboxsize=s;} - int textboxsize() const {return txtboxsize;} -}; - - -// New widgets for Csound5.06 by Gabriel Maldonado -// Ported by Andres Cabrera. This section below comes -// from the file newwidgets.h - -typedef struct { - OPDS h; - MYFLT *ihandle; -} FLSLDBNK_GETHANDLE; //gab - -typedef struct { - OPDS h; - MYFLT *ihandle, *ifn, *startInd, *startSlid, *numSlid; - //int oldx, oly; -} FLSLDBNK_SET; //gab - -typedef struct { - OPDS h; - MYFLT *kflag, *ihandle, *ifn, *startInd, *startSlid, *numSlid; - MYFLT oldValues[MAXSLIDERBANK]; - int numslid, startind, startslid; - FLSLIDERBANK2 *q; - MYFLT *table, *outable; - //int oldx, oly; -} FLSLDBNK2_SETK; - -typedef struct { - OPDS h; - MYFLT *kflag, *ihandle, *ifn, *startInd, *startSlid, *numSlid; - MYFLT oldValues[MAXSLIDERBANK]; - int numslid, startind, startslid; - FLSLIDERBANK *q; - MYFLT *table, *outable; - //int oldx, oly; -} FLSLDBNK_SETK; - -typedef struct { - OPDS h; - MYFLT *koutx, *kouty, *kinside; //outs - MYFLT *ioutx_min, *ioutx_max, *iouty_min, *iouty_max, - *iwindx_min, *iwindx_max, *iwindy_min, *iwindy_max, - *iexpx, *iexpy, *ioutx, *iouty; //ins - MYFLT oldx, oldy, *tablex, *tabley; - int expx, expy; - long tablenx, tableny; - double rangex, rangey, basex, basey; - //int pspace; -} FLXYIN; //gab - -typedef struct { - OPDS h; -// MYFLT *ktrig, *kvalue, *ihandle; - MYFLT *chan, *cc, *ihandle; - int ccVal, oldCCval; - MYFLT log_base, min, max, range; - void *WidgAddress, *opcode, *addrSetVal; - int exp, widg_type; -} FL_MIDI_WIDGET_VALUE; - - -typedef struct { - OPDS h; - MYFLT *kascii,*ifn;//, *ifnMap; - MYFLT *table;//, *tableMap; - int flag; -} FLKEYIN; - -typedef struct { - OPDS h; - MYFLT *group; -} FLSETSNAPGROUP; - -typedef struct { - OPDS h; - MYFLT *x,*y, *b1, *b2, *b3, *flagRaw; - MYFLT height,width; -} FLMOUSE; - -typedef struct { - OPDS h; - MYFLT *ihandle, *numlinesX, *numlinesY, *iwidth, *iheight, *ix, *iy,*image; - int width, height; -} FL_HVSBOX; - -typedef struct { - OPDS h; - MYFLT *kx, *ky, *ihandle; - void *WidgAddress, *opcode; - MYFLT old_x, old_y; -} FL_SET_HVS_VALUE; - -typedef struct { - OPDS h; - MYFLT max[MAXCHNLS]; - unsigned long widg_address[MAXCHNLS]; - int dummycycles, dummycyc; -} FLTKMETER; - -#endif /* CSOUND_WIDGETS_CPP */ -#endif /* CSOUND_WIDGETS_H */ diff --git a/6.x/src/widgets/widglobals.h b/6.x/src/widgets/widglobals.h deleted file mode 100644 index 65af7e25..00000000 --- a/6.x/src/widgets/widglobals.h +++ /dev/null @@ -1,272 +0,0 @@ -/* - wdglobals.h: - - Copyright (C) 2007 Andres Cabrera, Michael Gogins - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#include -#include -#include - -#if defined(WIN32) -# include -#endif /* defined(WIN32) */ - -#if defined(LINUX) -# include -# include -# include -#endif /* defined(LINUX) */ - -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -using namespace std; - -#include "csdl.h" -#include "winFLTK.h" - -#ifdef CS_IMAGE -# include "stdpch.h" -# include "anydec.h" -# include "anybmp.h" -# include "imagestruct.h" -#endif - -#define CSOUND_WIDGETS_CPP 1 -#include "widgets.h" - -#define LIN_ (0) -#define EXP_ (-1) -#undef min -#undef max - -typedef struct rtEvt_s { - struct rtEvt_s *nxt; - EVTBLK evt; -} rtEvt_t; - -#ifndef NO_FLTK_THREADS -typedef struct widgetsGlobals_s { - rtEvt_t *eventQueue; - void *mutex_; - int exit_now; /* set by GUI when all windows are closed */ - int end_of_perf; /* set by main thread at end of performance */ - void *threadHandle; - int fltkFlags; -} widgetsGlobals_t; -#endif - -struct ADDR { - void *opcode; - void *WidgAddress; -}; - -struct ADDR_STACK { /*: ADDR*/ - OPDS *h; - void *WidgAddress; - int count; - ADDR_STACK(OPDS *new_h,void *new_address, int new_count) - : h(new_h),WidgAddress(new_address), count(new_count) {} - ADDR_STACK() - { - h=NULL; - WidgAddress = NULL; - count=0; - } -}; - -struct VALUATOR_FIELD { - MYFLT value, value2; - MYFLT min,max, min2,max2; - int exp, exp2; - string widg_name; - string opcode_name; - SLDBK_ELEMENT *sldbnk; -private: - vector sldbnkValues; -public: - VALUATOR_FIELD() - { - value = 0; - value2 =0; - widg_name= ""; - opcode_name =""; - min = 0; - max =1; - min2=0; - max2=1; - exp=LIN_; - exp2=LIN_; - sldbnk=0; - } - void set_sldbnk(int ndx, MYFLT val) - { - if (ndx >= (int)sldbnkValues.size()) { - sldbnkValues.resize(ndx+1); - } - sldbnkValues[ndx]= val; - } - MYFLT get_sldbnk(int ndx) - { - return sldbnkValues[ndx]; - } -// ~VALUATOR_FIELD() { if (sldbnk != 0) delete sldbnk; -// if (sldbnkValues !=0) delete sldbnkValues; } -}; - - -enum FL_ENUM {FL_WIDG=0, FL_JOY, JOY_X=128, JOY_Y }; //gab - -struct ADDR_SET_VALUE { /*: ADDR*/ - int exponential; - MYFLT min,max; - void *WidgAddress, *opcode; - FL_ENUM widg_type; //gab - FL_ENUM joy; //gab - int group; // group for snapshot groups - ADDR_SET_VALUE(int new_exponential,MYFLT new_min, MYFLT new_max, - void *new_WidgAddress, void *new_opcode, int grp = 0) : - exponential(new_exponential),min(new_min), max(new_max), - WidgAddress(new_WidgAddress),opcode(new_opcode), - widg_type(FL_WIDG), joy(FL_JOY), group(grp) {} - ADDR_SET_VALUE() - { - exponential=LIN_; - min=0; - max=0; - WidgAddress=NULL; - opcode=NULL; - widg_type = FL_WIDG; - group = 0; - joy = FL_JOY; - } -}; - -struct PANELS { - Fl_Window *panel; - int is_subwindow; - PANELS(Fl_Window *new_panel, int flag) : panel(new_panel), - is_subwindow(flag) { } - PANELS() - { - panel = NULL; - is_subwindow=0; - } -}; - -struct SNAPSHOT { - int is_empty; - vector fields; - SNAPSHOT(vector& valuators, int snapGroup = 0); - SNAPSHOT() - { - is_empty = 1; - } - int get(vector& valuators, int snapGroup = 0); -}; - -typedef vector SNAPVEC; //gab - -struct WIDGET_GLOBALS { - WIDGET_GLOBALS(); - ~WIDGET_GLOBALS() - { - fl_windows.resize(0); - AddrStack.resize(0); //addresses of containers - AddrSetValue.resize(0); //addresses of valuators - for (int i = 0, n = allocatedStrings.size(); i < n; ++i) { - delete[] allocatedStrings[i]; - } - allocatedStrings.resize(0); - snapshots.resize(0); - } - char hack_o_rama1; // IV - Aug 23 2002 - char hack_o_rama2; - int ix, drag, indrag, sldrag; - int stack_count; - int FLcontrol_iheight; - int FLroller_iheight; - int FLcontrol_iwidth; - int FLroller_iwidth; - int FLvalue_iwidth; - int FLcolor; - int FLcolor2; - int FLtext_size; - int FLtext_color; - int FLtext_font; - int FLtext_align; - int currentSnapGroup; // GAB for snapshot groups - int last_KEY; // GAB - bool isKeyDown; //GAB - int FL_ix; - int FL_iy; - vector fl_windows; // all panels - vector AddrStack; //addresses of containers - vector AddrSetValue; //addresses of valuators - vector allocatedStrings; - int last_sldbnk; - FL_MIDI_WIDGET_VALUE *midiFLaddress[16][128]; // gab128 cc * 16 midi channels - int midiFLold_val[16][128]; //gab - vector snapshots; - vector::iterator snapshots_iterator; - FLTKMETER *p_vumeter; -#ifdef CS_IMAGE - vector Bm_image; // map of pointers to CAnyBmp objects - vector::iterator Bm_image_iterator; // iterator of the map -#endif -}; - - diff --git a/6.x/src/widgets/winFLTK.c b/6.x/src/widgets/winFLTK.c deleted file mode 100644 index 237037d5..00000000 --- a/6.x/src/widgets/winFLTK.c +++ /dev/null @@ -1,151 +0,0 @@ -/* - winFLTK.c: graphs using FLTK library - - Copyright (C) 2002 John ffitch - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -/* winFLTK.c */ -/* Csound FLTK/X graphs */ -/* jpff,06Oct02 */ - -#include "csdl.h" -#include -#include "cwindow.h" - -#ifdef LINUX -#include -#endif - -#include "winFLTK.h" - - - -static void MakeGraph_FLTK(CSOUND *csound, WINDAT *wdptr, const char *name) -{ - wdptr->windid = MakeWindow_FLTK(csound,(char*)name); -} - -static void KillGraph_FLTK(CSOUND *csound, WINDAT *wdptr) -{ - kill_graph(csound, wdptr->windid); -} - -static int dummyWidgetOpcode(CSOUND *csound, void *p) -{ - const char *opname; - - opname = csound->GetOpcodeName(p); - csound->Die(csound, Str("%s: widget opcodes have been disabled by " - "the host application"), opname); - return NOTOK; -} - -PUBLIC int csoundModuleCreate(CSOUND *csound) -{ - (void)csound; - return 0; -} - -PUBLIC int csoundModuleInit(CSOUND *csound) -{ - const OENTRY *ep = &(widgetOpcodes_[0]); - int initFlags = 0; - int *fltkFlags; - int enableDisplays = 0; - OPARMS oparms; - csound->GetOParms(csound, &oparms); - - if (csound->QueryGlobalVariable(csound, - "FLTK_Flags") == (void*) 0) { - if (UNLIKELY(csound->CreateGlobalVariable(csound, - "FLTK_Flags", sizeof(int)) != 0)) - csound->Die(csound, "%s", - Str("widgets.cpp: error allocating FLTK flags")); - initFlags = 1; - } - fltkFlags = getFLTKFlagsPtr(csound); - if (((*fltkFlags) & 2) == 0 && - !(oparms.graphsoff || oparms.postscript)) { -#ifdef LINUX - Display *dpy = XOpenDisplay(NULL); - if (dpy != NULL) { - XCloseDisplay(dpy); -#endif - if (csound->SetIsGraphable(csound, 1) == 0) { - enableDisplays = 1; - (*fltkFlags) |= 64; - - if (!((*fltkFlags) & 256)) - csound->SetInternalYieldCallback(csound, CsoundYield_FLTK); - flgraph_init(csound); /* Create space */ - csound->SetMakeGraphCallback(csound, MakeGraph_FLTK); - csound->SetDrawGraphCallback(csound, DrawGraph_FLTK); - csound->SetKillGraphCallback(csound, KillGraph_FLTK); - csound->SetExitGraphCallback(csound, ExitGraph_FLTK); - /* seemed to crash, but not anymore... */ - csound->RegisterResetCallback(csound, NULL, widget_reset); - csound->Message(csound, "graph init...\n"); - - } -#ifdef LINUX - } -#endif - } - if (initFlags) { -#ifndef __MACH__ - if (enableDisplays) -#endif - (*fltkFlags) |= 28; - } - if (!((*fltkFlags) & 129)) - for ( ; ep->opname != NULL; ep++) { - if (csound->AppendOpcode(csound, ep->opname, - (int)ep->dsblksiz, (int)ep->flags, (int)ep->thread, - ep->outypes, ep->intypes, - ep->iopadr, ep->kopadr, ep->aopadr) != 0) { - csound->ErrorMsg(csound, Str("Error registering opcode '%s'"), - ep->opname); - return -1; - } - } - else if (!((*fltkFlags) & 128)) { - for ( ; ep->opname != NULL; ep++) { - if (csound->AppendOpcode( - csound, ep->opname, (int)ep->dsblksiz, - (int)ep->flags,(int)ep->thread, - ep->outypes, ep->intypes, - (((int)ep->thread & 1) ? dummyWidgetOpcode : (SUBR) 0), - (((int)ep->thread & 2) ? dummyWidgetOpcode : (SUBR) 0), - (((int)ep->thread & 4) ? dummyWidgetOpcode : (SUBR) 0)) != 0) { - csound->ErrorMsg(csound, Str("Error registering opcode '%s'"), - ep->opname); - return -1; - } - } - } - - widget_init(csound); - return 0; -} - -PUBLIC int csoundModuleInfo(void) -{ - return ((CS_APIVERSION << 16) + (CS_APISUBVER << 8) + (int)sizeof(MYFLT)); -} diff --git a/6.x/src/widgets/winFLTK.h b/6.x/src/widgets/winFLTK.h deleted file mode 100644 index 68df69aa..00000000 --- a/6.x/src/widgets/winFLTK.h +++ /dev/null @@ -1,140 +0,0 @@ -/* - winFLTK.h: - - Copyright (C) 2006 Istvan Varga - - This file is part of Csound. - - The Csound Library is free software; you can redistribute it - and/or modify it under the terms of the GNU Lesser General Public - License as published by the Free Software Foundation; either - version 2.1 of the License, or (at your option) any later version. - - Csound is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU Lesser General Public License for more details. - - You should have received a copy of the GNU Lesser General Public - License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA -*/ - -#ifndef CSOUND_WINFLTK_H -#define CSOUND_WINFLTK_H - -#include "csdl.h" -#ifdef __cplusplus -#include -#endif - -/** - * FLTK flags is the sum of any of the following values: - * 1 (input): disable widget opcodes by setting up dummy opcodes instead - * 2 (input): disable FLTK graphs - * 4 (input): disable the use of a separate thread for widget opcodes - * 8 (input): disable the use of Fl::lock() and Fl::unlock() - * 16 (input): disable the use of Fl::awake() - * 32 (output): widget opcodes are used - * 64 (output): FLTK graphs are used - * 128 (input): disable widget opcodes by not registering any opcodes - * 256 (input): disable the use of Fl::wait() (implies no widget thread) - */ - -static inline int getFLTKFlags(CSOUND *csound) -{ - return (*((int*) csound->QueryGlobalVariableNoCheck(csound, "FLTK_Flags"))); -} - -static inline int *getFLTKFlagsPtr(CSOUND *csound) -{ - return ((int*) csound->QueryGlobalVariableNoCheck(csound, "FLTK_Flags")); -} - -#ifdef __cplusplus - -static inline void Fl_lock(CSOUND *csound) -{ -#ifdef NO_FLTK_THREADS - (void) csound; -#else - if (!(getFLTKFlags(csound) & 8)) { - Fl::lock(); - } -#endif -} - -static inline void Fl_unlock(CSOUND *csound) -{ -#ifdef NO_FLTK_THREADS - (void) csound; -#else - if (!(getFLTKFlags(csound) & 8)) { - Fl::unlock(); - } -#endif -} - -static inline void Fl_awake(CSOUND *csound) -{ -#ifdef NO_FLTK_THREADS - (void) csound; -#else - if (!(getFLTKFlags(csound) & 16)) { - Fl::awake(); - } -#endif -} - -static inline void Fl_wait(CSOUND *csound, double seconds) -{ - if (!(getFLTKFlags(csound) & 256)) - Fl::wait(seconds); -} - -static inline void Fl_wait_locked(CSOUND *csound, double seconds) -{ - int fltkFlags; - - fltkFlags = getFLTKFlags(csound); - if (!(fltkFlags & 256)) { -#ifndef NO_FLTK_THREADS - if (!(fltkFlags & 8)) - Fl::lock(); -#endif - Fl::wait(seconds); -#ifndef NO_FLTK_THREADS - if (!(fltkFlags & 8)) - Fl::unlock(); -#endif - } -} - -#endif /* __cplusplus */ - -#ifdef __cplusplus -extern "C" { -#endif - -extern int CsoundYield_FLTK(CSOUND *); -extern void DrawGraph_FLTK(CSOUND *, WINDAT *); -extern int ExitGraph_FLTK(CSOUND *); -extern void kill_graph(CSOUND *, uintptr_t); -extern void KillXYin_FLTK(CSOUND *, XYINDAT *); -extern uintptr_t MakeWindow_FLTK(CSOUND *, char *); -extern void MakeXYin_FLTK(CSOUND *, XYINDAT *, MYFLT, MYFLT); -extern int myFLwait(void); -extern void ReadXYin_FLTK(CSOUND *, XYINDAT *); - extern void flgraph_init(CSOUND *csound); -extern void widget_init(CSOUND *); -extern int widget_reset(CSOUND *, void *); - -extern const OENTRY widgetOpcodes_[]; - -#ifdef __cplusplus -} /* extern "C" */ -#endif - -#endif /* CSOUND_WINFLTK_H */ - diff --git a/6.x/src/wiimote/CMakeLists.txt b/6.x/src/wiimote/CMakeLists.txt deleted file mode 100644 index cb4997c0..00000000 --- a/6.x/src/wiimote/CMakeLists.txt +++ /dev/null @@ -1,10 +0,0 @@ -option(BUILD_WIIMOTE_OPCODES "Build the Wiimote opcodes" ON) - -find_path(WIIUSE_INCLUDE_DIR wiiuse.h /usr/local/include) -find_library(WIIUSE_LIB wiiuse) -check_deps(BUILD_WIIMOTE_OPCODES WIIUSE_LIB) - -if(BUILD_WIIMOTE_OPCODES) - make_plugin(wiimote wiimote.c ${WIIUSE_LIB}) - target_include_directories(wiimote PRIVATE ${WIIUSE_INCLUDE_DIR} ${CSOUND_INCLUDE_DIR}) -endif() diff --git a/6.x/vcpkg b/6.x/vcpkg deleted file mode 160000 index f6a5d4e8..00000000 --- a/6.x/vcpkg +++ /dev/null @@ -1 +0,0 @@ -Subproject commit f6a5d4e8eb7476b8d7fc12a56dff300c1c986131 diff --git a/7.x/README.md b/7.x/README.md deleted file mode 100644 index 6679ac0f..00000000 --- a/7.x/README.md +++ /dev/null @@ -1,88 +0,0 @@ -Plugins for Csound 7.x -======= - -Currently, the plugins available in this tree are - -- py (requires a Python 3.x installation with dev libs) -**Many opcodes to call Python code** - - -Install location --------------- -The CMake scripts in this repository use the default CS_USER_PLUGIN -location on MacOS and Windows as defined in the Csound build, or a -library instalation directory (customisable) on LINUX. These are: - -- LINUX: depends on both `CMAKE_INSTALL_PREFIX` and `USE_LIB64`. It is then installed in: - * if `USE_LIB64=1` then to - * for doubles: `${CSOUND_INSTALL_PREFIX}/lib64/csound/plugins64-${APIVERSION}` - * for floats: `${CSOUND_INSTALL_PREFIX}/lib64/csound/plugins-${APIVERSION}` (floats)` - * if `USE_LIB64=0` then to - * for doubles: `${CSOUND_INSTALL_PREFIX}/lib/csound/plugins64-${APIVERSION}` - * for floats: `${CSOUND_INSTALL_PREFIX}/lib/csound/plugins-${APIVERSION}` - -- MACOS: - * For doubles: `$HOME/Library/csound/${APIVERSION}/plugins64` - * For floats: `$HOME/Library/csound/${APIVERSION}/plugins` -- Windows: - * For doubles: `%LOCALAPPDATA%\csound\${APIVERSION}\plugins64` - * For floats: `%LOCALAPPDATA%\csound\${APIVERSION}\plugins` - - -Build Instructions for Linux and MacOS ---- - -The build requires Csound to be installed, as well as CMake. With this -in place, you can do : - -``` -$ git clone https://github.com/csound/plugins.git -$ cd plugins -$ mkdir build -$ cd build -$ cmake ../ -$ make -``` - -By default, all the plugins are built. If one wants to exclude a -plugin from the build process, one can pass an option to the cmake command. -For example, to exclude the XXX plugin, the `cmake` command would be: - -``` -$ cmake -DXXX_OPCODES=OFF ../ -``` - - -To install the opcodes you have built - -``` -$ make install -``` - -Depending on your permissions, you might need to prepend `sudo` to -these commands. After the first build the plugins can be updated with - -``` -$ git pull -$ make -$ make install -``` - -using `sudo` in the last step if raised permissions are needed. On -Linux, the installation location can be set with the relevant CMake -variables as indicated above. - -Csound Location ------------- -CMake will normally find the installed Csound headers (and library) -automatically. However, if your Csound headers and library are not -placed in the usual locations, you can use the following CMake option variables -to tell CMake where they are: - -``` -CSOUND_INCLUDE_DIR_HINT -``` -and - -``` -CSOUND_LIBRARY_DIR_HINT diff --git a/7.x/cmake/CompilerOptimizations.cmake b/7.x/cmake/CompilerOptimizations.cmake deleted file mode 100644 index 5888c36b..00000000 --- a/7.x/cmake/CompilerOptimizations.cmake +++ /dev/null @@ -1,53 +0,0 @@ - -check_c_compiler_flag(-ftree-vectorize HAS_TREE_VECTORIZE) -check_cxx_compiler_flag(-ftree-vectorize HAS_CXX_TREE_VECTORIZE) -if (HAS_TREE_VECTORISE) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ftree-vectorize") -endif() -if (HAS_CXX_TREE_VECTORISE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ftree-vectorize") -endif() - - -check_c_compiler_flag(-ffast-math HAS_FAST_MATH) -check_cxx_compiler_flag(-ffast-math HAS_CXX_FAST_MATH) -if (HAS_FAST_MATH AND NOT MINGW) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -ffast-math") -endif() -if (HAS_CXX_FAST_MATH AND NOT MINGW) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -ffast-math") -endif() - - - -if(NOT CMAKE_CXX_COMPILER_ID STREQUAL "Clang") -check_c_compiler_flag(-mfpmath=sse HAS_FPMATH_SSE) -check_cxx_compiler_flag(-mfpmath=sse HAS_CXX_FPMATH_SSE) - if (HAS_FPMATH_SSE) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -mfpmath=sse") -endif() -if (HAS_CXX_FPMATH_SSE) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -mfpmath=sse") -endif() - -endif() - - -check_c_compiler_flag(-msse2 HAS_SSE2) -check_cxx_compiler_flag(-msse2 HAS_CXX_SSE2) - if (HAS_SSE2 AND NOT IOS AND NOT WASM) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -msse2") -endif() -if (HAS_CXX_SSE2 AND NOT IOS AND NOT WASM) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -msse2") -endif() - - -check_c_compiler_flag(-fomit-frame-pointer HAS_OMIT_FRAME_POINTER) -check_cxx_compiler_flag(-fomit-frame-pointer HAS_CXX_OMIT_FRAME_POINTER) -if (HAS_OMIT_FRAME_POINTER) - set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -fomit-frame-pointer") -endif() -if (HAS_CXX_OMIT_FRAME_POINTER) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -fomit-frame-pointer") -endif() diff --git a/7.x/cmake/Modules/FindEIGEN3.cmake b/7.x/cmake/Modules/FindEIGEN3.cmake deleted file mode 100644 index e4e2ed8d..00000000 --- a/7.x/cmake/Modules/FindEIGEN3.cmake +++ /dev/null @@ -1,100 +0,0 @@ -# - Try to find Eigen3 lib -# -# This module supports requiring a minimum version, e.g. you can do -# find_package(Eigen3 3.1.2) -# to require version 3.1.2 or newer of Eigen3. -# -# Once done this will define -# -# EIGEN3_FOUND - system has eigen lib with correct version -# EIGEN3_INCLUDE_DIR - the eigen include directory -# EIGEN3_VERSION - eigen version -# -# This module reads hints about search locations from -# the following enviroment variables: -# -# EIGEN3_ROOT -# EIGEN3_ROOT_DIR - -# Copyright (c) 2006, 2007 Montel Laurent, -# Copyright (c) 2008, 2009 Gael Guennebaud, -# Copyright (c) 2009 Benoit Jacob -# Redistribution and use is allowed according to the terms of the 2-clause BSD license. - -if(NOT Eigen3_FIND_VERSION) - if(NOT Eigen3_FIND_VERSION_MAJOR) - set(Eigen3_FIND_VERSION_MAJOR 2) - endif(NOT Eigen3_FIND_VERSION_MAJOR) - if(NOT Eigen3_FIND_VERSION_MINOR) - set(Eigen3_FIND_VERSION_MINOR 91) - endif(NOT Eigen3_FIND_VERSION_MINOR) - if(NOT Eigen3_FIND_VERSION_PATCH) - set(Eigen3_FIND_VERSION_PATCH 0) - endif(NOT Eigen3_FIND_VERSION_PATCH) - - set(Eigen3_FIND_VERSION "${Eigen3_FIND_VERSION_MAJOR}.${Eigen3_FIND_VERSION_MINOR}.${Eigen3_FIND_VERSION_PATCH}") -endif(NOT Eigen3_FIND_VERSION) - -macro(_eigen3_check_version) - file(READ "${EIGEN3_INCLUDE_DIR}/Eigen/src/Core/util/Macros.h" _eigen3_version_header) - - string(REGEX MATCH "define[ \t]+EIGEN_WORLD_VERSION[ \t]+([0-9]+)" _eigen3_world_version_match "${_eigen3_version_header}") - set(EIGEN3_WORLD_VERSION "${CMAKE_MATCH_1}") - string(REGEX MATCH "define[ \t]+EIGEN_MAJOR_VERSION[ \t]+([0-9]+)" _eigen3_major_version_match "${_eigen3_version_header}") - set(EIGEN3_MAJOR_VERSION "${CMAKE_MATCH_1}") - string(REGEX MATCH "define[ \t]+EIGEN_MINOR_VERSION[ \t]+([0-9]+)" _eigen3_minor_version_match "${_eigen3_version_header}") - set(EIGEN3_MINOR_VERSION "${CMAKE_MATCH_1}") - - set(EIGEN3_VERSION ${EIGEN3_WORLD_VERSION}.${EIGEN3_MAJOR_VERSION}.${EIGEN3_MINOR_VERSION}) - if(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) - set(EIGEN3_VERSION_OK FALSE) - else(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) - set(EIGEN3_VERSION_OK TRUE) - endif(${EIGEN3_VERSION} VERSION_LESS ${Eigen3_FIND_VERSION}) - - if(NOT EIGEN3_VERSION_OK) - - message(STATUS "Eigen3 version ${EIGEN3_VERSION} found in ${EIGEN3_INCLUDE_DIR}, " - "but at least version ${Eigen3_FIND_VERSION} is required") - endif(NOT EIGEN3_VERSION_OK) -endmacro(_eigen3_check_version) - -if (EIGEN3_INCLUDE_DIR) - - # in cache already - _eigen3_check_version() - set(EIGEN3_FOUND ${EIGEN3_VERSION_OK}) - -else (EIGEN3_INCLUDE_DIR) - - # search first if an Eigen3Config.cmake is available in the system, - # if successful this would set EIGEN3_INCLUDE_DIR and the rest of - # the script will work as usual - find_package(Eigen3 ${Eigen3_FIND_VERSION} NO_MODULE QUIET) - - - if(NOT EIGEN3_INCLUDE_DIR) - find_path(EIGEN3_INCLUDE_DIR NAMES signature_of_eigen3_matrix_library - HINTS - ENV EIGEN3_ROOT - ENV EIGEN3_ROOT_DIR - PATHS - ${CMAKE_INSTALL_PREFIX}/include - /usr/local/include - ${CMAKE_INSTALL_PREFIX} - $ENV{HOME}/include/ - PATH_SUFFIXES eigen3 eigen Eigen - ) - endif(NOT EIGEN3_INCLUDE_DIR) - - if(EIGEN3_INCLUDE_DIR) - _eigen3_check_version() - endif(EIGEN3_INCLUDE_DIR) - - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(Eigen3 DEFAULT_MSG EIGEN3_INCLUDE_DIR EIGEN3_VERSION_OK) - - mark_as_advanced(EIGEN3_INCLUDE_DIR) - -endif(EIGEN3_INCLUDE_DIR) - diff --git a/7.x/cmake/Modules/FindFAUST.cmake b/7.x/cmake/Modules/FindFAUST.cmake deleted file mode 100644 index d1d329e5..00000000 --- a/7.x/cmake/Modules/FindFAUST.cmake +++ /dev/null @@ -1,59 +0,0 @@ -# Find Faust2 -include(FindPackageHandleStandardArgs) - -find_path(FAUST_INCLUDE_DIR faust/dsp/llvm-dsp.h - HINTS - C:/Program Files/Faust/include/ - /opt/lib/faust/architecture/ - /usr/lib/faust/architecture/ - /usr/local/lib/faust/architecture/ - "${FAUST_INCLUDE_DIR_HINT}" -) - -find_library(FAUST_LIBRARY - NAMES - libfaust.so - libfaust.dylib - faust.dll - faust - libfaust - HINTS - "${FAUST_LIB_DIR_HINT}" -) - -find_package_handle_standard_args(FAUST FAUST_INCLUDE_DIR FAUST_LIBRARY) - -if(FAUST_FOUND) - set(FAUST_LIBRARIES ${FAUST_LIBRARY}) - set(FAUST_INCLUDE_DIRS ${FAUST_INCLUDE_DIR}) - - if("${FAUST_LIBRARY}" MATCHES ".*\\.a") - # This is a static build of faust, hence - # we have to add all the LLVM flags... - - find_program(LLVM_CONFIG llvm-config - HINTS - /usr/bin - /usr/local/bin - /usr/local/opt/llvm/bin - ) - - if(NOT LLVM_CONFIG) - message(WARNING "Using a static Faust library requires LLVM tooling to be present in the path.") - UNSET(FAUST_FOUND) - else() - exec_program(${LLVM_CONFIG} ARGS --includedir OUTPUT_VARIABLE LLVM_DIR) - exec_program(${LLVM_CONFIG} ARGS --libs OUTPUT_VARIABLE LLVM_LIBS) - exec_program(${LLVM_CONFIG} ARGS --version OUTPUT_VARIABLE LLVM_VERSION) - exec_program(${LLVM_CONFIG} ARGS --ldflags OUTPUT_VARIABLE LLVM_LDFLAGS) - - set(LLVM_VERSION LLVM_${LLVM_VERSION_MAJOR}${LLVM_VERSION_MINOR}) - - find_package(OpenSSL REQUIRED) - set(FAUST_LIBRARIES ${FAUST_LIBRARIES} dl ${OPENSSL_LIBRARIES} ncurses z ${LLVM_LDFLAGS} ${LLVM_LIBS} ) - endif() - endif() -else() - set(FAUST_LIBRARIES) - set(FAUST_INCLUDE_DIRS) -endif() diff --git a/7.x/cmake/Modules/FindFLUIDSYNTH.cmake b/7.x/cmake/Modules/FindFLUIDSYNTH.cmake deleted file mode 100644 index 152604de..00000000 --- a/7.x/cmake/Modules/FindFLUIDSYNTH.cmake +++ /dev/null @@ -1,34 +0,0 @@ -# - Find fluidsynth -# Find the native fluidsynth includes and library -# -# FLUIDSYNTH_INCLUDE_DIR - where to find fluidsynth.h -# FLUIDSYNTH_LIBRARIES - List of libraries when using fluidsynth. -# FLUIDSYNTH_FOUND - True if fluidsynth found. - -if (FLUIDSYNTH_INCLUDE_DIR AND FLUIDSYNTH_LIBRARIES) - # Already in cache, be silent - set (FLUIDSYNTH_FIND_QUIETLY TRUE) -endif () - -if(APPLE) - find_path(FLUIDSYNTH_INCLUDE_DIR fluidsynth.h HINTS - /Library/Frameworks/FluidSynth.framework/Headers - ${FLUIDSYNTH_INCLUDE_DIR_HINT}) -else() - find_path (FLUIDSYNTH_INCLUDE_DIR fluidsynth.h) -endif() - -if(APPLE) - find_library(FLUIDSYNTH_LIBRARIES NAMES FluidSynth HINTS - /Library/Frameworks/FluidSynth.framework/ - ${FLUIDSYNTH_LIBRARY_DIR_HINT}) -else() - find_library (FLUIDSYNTH_LIBRARIES NAMES fluidsynth libfluidsynth) -endif() - -mark_as_advanced (FLUIDSYNTH_LIBRARIES FLUIDSYNTH_INCLUDE_DIR) - -# handle the QUIETLY and REQUIRED arguments and set FLUIDSYNTH_FOUND to TRUE if -# all listed variables are TRUE -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(FLUIDSYNTH DEFAULT_MSG FLUIDSYNTH_LIBRARIES FLUIDSYNTH_INCLUDE_DIR) diff --git a/7.x/cmake/Modules/FindGMM.cmake b/7.x/cmake/Modules/FindGMM.cmake deleted file mode 100644 index 1b4d3596..00000000 --- a/7.x/cmake/Modules/FindGMM.cmake +++ /dev/null @@ -1,20 +0,0 @@ -# Try to find the GMM library. -# Once done this will define: -# GMM_FOUND - System has the GMM library. -# GMM_VERSION -# GMM_INCLUDE_DIR - The GMM include directories. -# GMM_HAS_VECTOR_OSTREAM - Wether GMM defines operator<< for std::vector - -if (GMM_INCLUDE_DIR) - # in cache already - set(GMM_FOUND TRUE) -else (GMM_INCLUDE_DIR) - find_path(GMM_INCLUDE_DIR NAMES gmm/gmm.h - PATHS - ${INCLUDE_INSTALL_DIR} - ${GMM_INCLUDE_PATH} - ) - include(FindPackageHandleStandardArgs) - find_package_handle_standard_args(GMM DEFAULT_MSG GMM_INCLUDE_DIR) - mark_as_advanced(GMM_INCLUDE_DIR) -endif(GMM_INCLUDE_DIR) \ No newline at end of file diff --git a/7.x/cmake/Modules/FindLAME.cmake b/7.x/cmake/Modules/FindLAME.cmake deleted file mode 100644 index 9a637529..00000000 --- a/7.x/cmake/Modules/FindLAME.cmake +++ /dev/null @@ -1,20 +0,0 @@ -# - Find LAME -# Find the native LAME includes and library -# -# LAME_INCLUDE_DIR - where to find lame.h -# LAME_LIBRARY - lame library -# LAME_FOUND - lame found - -if (LAME_INCLUDE_DIR AND LAME_LIBRARY) - # Already in cache, be silent - set (LAME_FIND_QUIETLY TRUE) -endif () - -find_path (LAME_INCLUDE_DIR lame/lame.h HINTS /usr/local/include) -find_library (LAME_LIBRARY NAMES mp3lame libmp3lame HINTS /usr/local/lib) -mark_as_advanced (LAME_LIBRARIES LAME_INCLUDE_DIR) - -# handle the QUIETLY and REQUIRED arguments and set LAME_FOUND to TRUE if -# all listed variables are TRUE -include(FindPackageHandleStandardArgs) -find_package_handle_standard_args(LAME DEFAULT_MSG LAME_LIBRARY LAME_INCLUDE_DIR) diff --git a/7.x/cmake/Modules/FindSTK.cmake b/7.x/cmake/Modules/FindSTK.cmake deleted file mode 100644 index 1d889b01..00000000 --- a/7.x/cmake/Modules/FindSTK.cmake +++ /dev/null @@ -1,19 +0,0 @@ -# Try to find the STK library. -# Once done this will define: -# STK_FOUND - System has the STK library. -# STK_INCLUDE_DIRS - The STK include directories. -# STK_LIBRARIES - The libraries needed to use the STK. - -find_path(STK_INCLUDE_DIR Stk.h PATH_SUFFIXES stk HINTS "${STK_INCLUDE_DIR_HINT}") -find_library(STK_LIBRARY NAMES stk HINTS "${STK_LIBRARY_DIR_HINT}") - -set(STK_INCLUDE_DIRS ${STK_INCLUDE_DIR}) -set(STK_LIBRARIES ${STK_LIBRARY} ) - -include(FindPackageHandleStandardArgs) -# handle the QUIETLY and REQUIRED arguments and set STK_FOUND to TRUE -# if all listed variables are TRUE -find_package_handle_standard_args(STK DEFAULT_MSG - STK_LIBRARY STK_INCLUDE_DIR) - -mark_as_advanced(STK_INCLUDE_DIR STK_LIBRARY) diff --git a/7.x/src/py/CMakeLists.txt b/7.x/src/py/CMakeLists.txt deleted file mode 100644 index ab90376b..00000000 --- a/7.x/src/py/CMakeLists.txt +++ /dev/null @@ -1,18 +0,0 @@ -# Build py opcodes -option(BUILD_PYTHON_OPCODES "Build the py opcodes" ON) - -if(BUILD_PYTHON_OPCODES) - find_package(Python3 COMPONENTS Interpreter Development) - check_deps(BUILD_PYTHON_OPCODES Python3_FOUND) - - set(PYTHON_INCLUDE_DIRS ${Python3_INCLUDE_DIRS}) - set(PYTHON_LIBRARIES ${Python3_LIBRARIES}) - - message("-- Python include dirs: ${Python3_INCLUDE_DIRS}") - message("-- Python libraries: ${PYTHON_LIBRARIES}") - - make_plugin(py src/pythonopcodes.c) - target_include_directories(py PRIVATE ${CSOUND_INCLUDE_DIRS}) - target_include_directories(py PRIVATE ${PYTHON_INCLUDE_DIRS}) - target_link_libraries(py ${PYTHON_LIBRARIES}) -endif() diff --git a/7.x/src/py/README.md b/7.x/src/py/README.md deleted file mode 100644 index 04e3ab2e..00000000 --- a/7.x/src/py/README.md +++ /dev/null @@ -1,14 +0,0 @@ -Python opcodes -=== - -This is the porting to Python 3.x of the Python opcodes of Csound which ran under Python 2.7 until know. -The backward compatibility is preserved for the Csound code of the orchestra, however it is not maintained -for the Python code that would be embedded in a Csound orchestra or in Csound instruments, due to the lack -of backward compatibility between Python 3.x and Python 2.7. - -Python 2.7 will not be maintained past 2020. The -[official porting guide](https://docs.python.org/3/howto/pyporting.html) has advice for -running Python 2 code in Python 3. - -To build these opcodes, you will need to install Python from -[www.python.org](www.python.org), or the devel package (on linux). diff --git a/7.x/src/py/examples/cabrera/pythonmarkov.csd b/7.x/src/py/examples/cabrera/pythonmarkov.csd deleted file mode 100644 index 0c701f51..00000000 --- a/7.x/src/py/examples/cabrera/pythonmarkov.csd +++ /dev/null @@ -1,82 +0,0 @@ - - - -odac - - - -sr=44100 -ksmps=128 -nchnls=2 - -pyinit - -; Python script to define probabilities for each note as lists within a list -; Definition of the get_new_note function which randomly generates a new -; note based on the probabilities of each note occuring. -; Each note list must total 1, or there will be problems! - -pyruni {{c = [0.1, 0.2, 0.05, 0.4, 0.25] -d = [0.4, 0.1, 0.1, 0.2, 0.2] -e = [0.2, 0.35, 0.05, 0.4, 0] -g = [0.7, 0.1, 0.2, 0, 0] -a = [0.1, 0.2, 0.05, 0.4, 0.25] - -markov = [c, d, e, g, a] - -import random - -random.seed() - -def get_new_note(previous_note): - number = random.random() - accum = 0 - i = 0 - while accum < number: - accum = accum + markov[int(previous_note)][int(i)] - i = i + 1 - return i - 1.0 - -}} - - - -instr 1 ;Markov chain reader and note spawner -;p4 = frequency of note generation -;p5 = octave -ioct init p5 -klastnote init 0 ;Used to remember last note played (start at first note of scale) -ktrig metro p4 ;generate a trigger with frequency p4 -knewnote pycall1t ktrig, "get_new_note", klastnote ;get new note from chain -schedkwhen ktrig, 0, 10, 2, 0, 0.2, knewnote, ioct ;launch note on instrument 2 -klastnote = knewnote ;New note is now the old note -endin - - -instr 2 ;A simple sine wave instrument -;p4 = note to be played -;p5 = octave -ioct init p5 -ipclass table p4, 2 -ipclass = ioct + (ipclass / 100) ; Pitch class of the note -ifreq = cpspch(ipclass) ;Note frequency in Hertz -aenv linen 6000, 0.05, p3, 0.1 ;Amplitude envelope -aout oscil aenv, ifreq , 1 ;Simple oscillator -outs aout, aout -endin - - - - - -f 1 0 2048 10 1 ;sine wave -f 2 0 8 -2 0 2 4 7 9 ;Pitch classes for pentatonic scale - -; frequency of Octave of -; note generation melody -i 1 0 30 3 7 -i 1 5 25 6 9 -i 1 10 20 7.5 10 -i 1 15 15 1 8 - - - \ No newline at end of file diff --git a/7.x/src/py/examples/embeddedCtcsound.csd b/7.x/src/py/examples/embeddedCtcsound.csd deleted file mode 100644 index 87859e28..00000000 --- a/7.x/src/py/examples/embeddedCtcsound.csd +++ /dev/null @@ -1,38 +0,0 @@ - - - -odac - - - -sr = 48000 -ksmps = 100 -nchnls = 2 - -pyinit ;Start python interpreter - -pyruni {{ -import ctcsound -import sys - -# Get an opaque pointer to the running Csound instance -# and print Python version number -p = _CSOUND_ -print("\\n --> Python version number: {}\\n".format(sys.version_info[0])) -# Create an object called cs from the ctcsound.Csound class -# using the opaque pointer to the Csound instance -cs = ctcsound.Csound(pointer_=p) -}} - -instr 1 - ; Use the cs python object - pyruni {{ -print("\\n --> Sample Rate: {}, Control Rate: {}\\n".format(cs.sr(), cs.kr())) - }} -endin - - - -i1 0 1 - - - diff --git a/7.x/src/py/examples/ievent.sco b/7.x/src/py/examples/ievent.sco deleted file mode 100644 index 65bf76f7..00000000 --- a/7.x/src/py/examples/ievent.sco +++ /dev/null @@ -1 +0,0 @@ -f0 60 diff --git a/7.x/src/py/examples/pyassign.orc b/7.x/src/py/examples/pyassign.orc deleted file mode 100644 index 59d1ef43..00000000 --- a/7.x/src/py/examples/pyassign.orc +++ /dev/null @@ -1,13 +0,0 @@ -sr=44100 -kr=4410 -ksmps=10 -nchnls=1 - -pyinit - -instr 1 - - k1 rand 1000 - pyassign "a", k1 - pyrun "print(a)" -endin diff --git a/7.x/src/py/examples/pyassign.sco b/7.x/src/py/examples/pyassign.sco deleted file mode 100644 index d14b738e..00000000 --- a/7.x/src/py/examples/pyassign.sco +++ /dev/null @@ -1 +0,0 @@ -i1 0 0.01 diff --git a/7.x/src/py/examples/pycall1.orc b/7.x/src/py/examples/pycall1.orc deleted file mode 100644 index 50be70bb..00000000 --- a/7.x/src/py/examples/pycall1.orc +++ /dev/null @@ -1,31 +0,0 @@ -sr=44100 -kr=4410 -ksmps=10 -nchnls=1 - -giSinusoid ftgen 0, 0, 8192, 10, 1 - -pyinit - -pyruni {{ -import random - -pool = [(1 + i/10.0) ** 1.2 for i in range(100)] - -def get_number_from_pool(n, p): - if random.random() < p: - i = int(random.random() * len(pool)) - pool[i] = n - - return random.choice(pool) -}} - -instr 1 - - k1 oscil 1, 3, giSinusoid - - k2 pycall1 "get_number_from_pool", k1 + 2, p4 - - printk 0.01, k2 - -endin diff --git a/7.x/src/py/examples/pycall1.sco b/7.x/src/py/examples/pycall1.sco deleted file mode 100644 index a81415b5..00000000 --- a/7.x/src/py/examples/pycall1.sco +++ /dev/null @@ -1 +0,0 @@ -i1 0 10 0.01 diff --git a/7.x/src/py/examples/pycall2.orc b/7.x/src/py/examples/pycall2.orc deleted file mode 100644 index c289ddc8..00000000 --- a/7.x/src/py/examples/pycall2.orc +++ /dev/null @@ -1,35 +0,0 @@ -sr=44100 -kr=4410 -ksmps=10 -nchnls=1 - -giSinusoid ftgen 0, 0, 8192, 10, 1 - -pyinit - -pyruni {{ -import random - -class GetNumberFromPool: - def __init__(self, e=1.2, a=1.0, f=0.1, N=100): - self.pool = [(a + i*f) ** e for i in range(N)] - - def __call__(self, n, p): - if random.random() < p: - i = int(random.random() * len(self.pool)) - self.pool[i] = n - - return random.choice(self.pool) - -get_number_from_pool = GetNumberFromPool() -}} - -instr 1 - - k1 oscil 1, 3, giSinusoid - - k2 pycall1 "get_number_from_pool", k1 + 2, p4 - - printk 0.01, k2 - -endin diff --git a/7.x/src/py/examples/pycall2.sco b/7.x/src/py/examples/pycall2.sco deleted file mode 100644 index a81415b5..00000000 --- a/7.x/src/py/examples/pycall2.sco +++ /dev/null @@ -1 +0,0 @@ -i1 0 10 0.01 diff --git a/7.x/src/py/examples/pycall3.orc b/7.x/src/py/examples/pycall3.orc deleted file mode 100644 index a090327b..00000000 --- a/7.x/src/py/examples/pycall3.orc +++ /dev/null @@ -1,57 +0,0 @@ -sr=44100 -kr=4410 -ksmps=10 -nchnls=1 - -pyinit - -pyruni {{ -def f0(a, b, c, d, e, f, g, h): - pass - -def f1(a, b, c, d, e, f, g, h): - return a - -def f2(a, b, c, d, e, f, g, h): - return a, b - -def f3(a, b, c, d, e, f, g, h): - return a, b, c - -def f4(a, b, c, d, e, f, g, h): - return a, b, c, d - -def f5(a, b, c, d, e, f, g, h): - return a, b, c, d, e - -def f6(a, b, c, d, e, f, g, h): - return a, b, c, d, e, f - -def f7(a, b, c, d, e, f, g, h): - return a, b, c, d, e, f, g - -def f8(a, b, c, d, e, f, g, h): - return a, b, c, d, e, f, g, h -}} - -instr 1 - - pycall "f0", 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 - k1 pycall1 "f1", 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 - k1, k2 pycall2 "f2", 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 - k1, k2, k3 pycall3 "f3", 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 - k1, k2, k3, k4 pycall4 "f4", 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 - k1, k2, k3, k4, k5 pycall5 "f5", 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 - k1, k2, k3, k4, k5, k6 pycall6 "f6", 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 - k1, k2, k3, k4, k5, k6, k7 pycall7 "f7", 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 - k1, k2, k3, k4, k5, k6, k7, k8 pycall8 "f8", 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 - - printk 0.01, k1 - printk 0.01, k2 - printk 0.01, k3 - printk 0.01, k4 - printk 0.01, k5 - printk 0.01, k6 - printk 0.01, k7 - printk 0.01, k8 -endin diff --git a/7.x/src/py/examples/pycall3.sco b/7.x/src/py/examples/pycall3.sco deleted file mode 100644 index 87b69bf1..00000000 --- a/7.x/src/py/examples/pycall3.sco +++ /dev/null @@ -1 +0,0 @@ -i1 0 0.1 diff --git a/7.x/src/py/examples/pycallt.orc b/7.x/src/py/examples/pycallt.orc deleted file mode 100644 index ac939b48..00000000 --- a/7.x/src/py/examples/pycallt.orc +++ /dev/null @@ -1,67 +0,0 @@ -sr=44100 -kr=4410 -ksmps=10 -nchnls=1 - -pyinit - -pyruni {{ -def f0(a, b, c, d, e, f, g, h): - print('f0 got', a, b, c, d, e, f, g, h) - print('f0 returning None') - -def f1(a, b, c, d, e, f, g, h): - print('f1 got', a, b, c, d, e, f, g, h) - print('f1 returning', a) - return a - -def f2(a, b, c, d, e, f, g, h): - print('f2 got', a, b, c, d, e, f, g, h) - print('f2 returning', a, b) - return a, b - -def f3(a, b, c, d, e, f, g, h): - print('f3 got', a, b, c, d, e, f, g, h) - print('f3 returning', a, b, c) - return a, b, c - -def f4(a, b, c, d, e, f, g, h): - print('f4 got', a, b, c, d, e, f, g, h) - print('f4 returning', a, b, c, d) - return a, b, c, d - -def f5(a, b, c, d, e, f, g, h): - print('f5 got', a, b, c, d, e, f, g, h) - print('f5 returning', a, b, c, d, e) - return a, b, c, d, e - -def f6(a, b, c, d, e, f, g, h): - print('f6 got', a, b, c, d, e, f, g, h) - print('f6 returning', a, b, c, d, e, f) - return a, b, c, d, e, f - -def f7(a, b, c, d, e, f, g, h): - print('f7 got', a, b, c, d, e, f, g, h) - print('f7 returning', a, b, c, d, e, f, g) - return a, b, c, d, e, f, g - -def f8(a, b, c, d, e, f, g, h): - print('f8 got', a, b, c, d, e, f, g, h) - print('f8 returning', a, b, c, d, e, f, g, h) - return a, b, c, d, e, f, g, h -}} - -instr 1 - - kt metro 1.5 - - pycallt kt, "f0", 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 - k1 pycall1t kt, "f1", 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 - k1, k2 pycall2t kt, "f2", 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 - k1, k2, k3 pycall3t kt, "f3", 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 - k1, k2, k3, k4 pycall4t kt, "f4", 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 - k1, k2, k3, k4, k5 pycall5t kt, "f5", 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 - k1, k2, k3, k4, k5, k6 pycall6t kt, "f6", 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 - k1, k2, k3, k4, k5, k6, k7 pycall7t kt, "f7", 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 - k1, k2, k3, k4, k5, k6, k7, k8 pycall8t kt, "f8", 1.1, 2.2, 3.3, 4.4, 5.5, 6.6, 7.7, 8.8 -endin diff --git a/7.x/src/py/examples/pycallt.sco b/7.x/src/py/examples/pycallt.sco deleted file mode 100644 index ad27e081..00000000 --- a/7.x/src/py/examples/pycallt.sco +++ /dev/null @@ -1 +0,0 @@ -i1 0 10 diff --git a/7.x/src/py/examples/pyeval.orc b/7.x/src/py/examples/pyeval.orc deleted file mode 100644 index 37988f93..00000000 --- a/7.x/src/py/examples/pyeval.orc +++ /dev/null @@ -1,15 +0,0 @@ -sr=44100 -kr=4410 -ksmps=10 -nchnls=1 - - pyinit - - pyruni "from random import random" - -instr 1 - - pyrun "a = random()" - k1 pyeval "a" - printk 0.1, k1 -endin diff --git a/7.x/src/py/examples/pyeval.sco b/7.x/src/py/examples/pyeval.sco deleted file mode 100644 index dd813c63..00000000 --- a/7.x/src/py/examples/pyeval.sco +++ /dev/null @@ -1 +0,0 @@ -i1 0 0.5 diff --git a/7.x/src/py/examples/pyexec.orc b/7.x/src/py/examples/pyexec.orc deleted file mode 100644 index 6e9674cc..00000000 --- a/7.x/src/py/examples/pyexec.orc +++ /dev/null @@ -1,19 +0,0 @@ -sr=44100 -kr=4410 -ksmps=10 -nchnls=1 - - pyinit - - pyruni "import random" - - pyexeci "pyexec1.py" - -instr 1 - - pyexec "pyexec2.py" - - pylexeci "pyexec3.py" - pylexec "pyexec4.py" - -endin diff --git a/7.x/src/py/examples/pyexec.sco b/7.x/src/py/examples/pyexec.sco deleted file mode 100644 index 81d41dd0..00000000 --- a/7.x/src/py/examples/pyexec.sco +++ /dev/null @@ -1,2 +0,0 @@ -i1 0 0.01 -i1 0 0.01 diff --git a/7.x/src/py/examples/pyexec1.py b/7.x/src/py/examples/pyexec1.py deleted file mode 100644 index b2de022a..00000000 --- a/7.x/src/py/examples/pyexec1.py +++ /dev/null @@ -1,12 +0,0 @@ -import time, os - -print() -print("Welcome to Csound!") - -try: - s = ', %s?' % os.getenv('USER') -except: - s = '?' - -print('What sound do you want to hear today%s' % s) -answer = input('--> ') diff --git a/7.x/src/py/examples/pyexec2.py b/7.x/src/py/examples/pyexec2.py deleted file mode 100644 index ee6e4397..00000000 --- a/7.x/src/py/examples/pyexec2.py +++ /dev/null @@ -1 +0,0 @@ -print('your answer is "%s"' % answer) diff --git a/7.x/src/py/examples/pyexec3.py b/7.x/src/py/examples/pyexec3.py deleted file mode 100644 index ec0e305c..00000000 --- a/7.x/src/py/examples/pyexec3.py +++ /dev/null @@ -1 +0,0 @@ -message = 'a private random number: %f' % random.random() diff --git a/7.x/src/py/examples/pyexec4.py b/7.x/src/py/examples/pyexec4.py deleted file mode 100644 index 923bd256..00000000 --- a/7.x/src/py/examples/pyexec4.py +++ /dev/null @@ -1 +0,0 @@ -print(message) diff --git a/7.x/src/py/examples/pyexect.orc b/7.x/src/py/examples/pyexect.orc deleted file mode 100644 index 75b4805b..00000000 --- a/7.x/src/py/examples/pyexect.orc +++ /dev/null @@ -1,25 +0,0 @@ -sr=44100 -kr=4410 -ksmps=10 -nchnls=1 - -pyinit - -instr 1 - - ktrigger1 metro 3 - ktrigger2 metro 6 - ktrigger3 metro 10 - ktrigger4 metro 0.5 - - pyexect ktrigger1, "pyexect1.py" - pyexect ktrigger2, "pyexect2.py" - pyexect ktrigger3, "pyexect3.py" - - pylexect ktrigger3 + ktrigger1, "pyexect4.py" - - a1 rand 5000 - - out a1 - -endin diff --git a/7.x/src/py/examples/pyexect.sco b/7.x/src/py/examples/pyexect.sco deleted file mode 100644 index 90df1b0c..00000000 --- a/7.x/src/py/examples/pyexect.sco +++ /dev/null @@ -1,2 +0,0 @@ -i1 0 5 -i1 0 5 diff --git a/7.x/src/py/examples/pyrun.csd b/7.x/src/py/examples/pyrun.csd deleted file mode 100644 index 0a0d180c..00000000 --- a/7.x/src/py/examples/pyrun.csd +++ /dev/null @@ -1,31 +0,0 @@ - - - -odac - - -sr=44100 -kr=4410 -ksmps=10 -nchnls=1 - -pyinit - -pyruni "import random" - -instr 1 - ; This message is stored in the main namespace - ; and is the same for every instance - pyruni "message = 'a global random number: %f' % random.random()" - pyrun "print(message)" - - ; This message is stored in the private namespace - ; and is different for different instances - pylruni "message = 'a private random number: %f' % random.random()" - pylrun "print(message)" - -endin - - -i1 0 0.01 - - diff --git a/7.x/src/py/examples/pyrun.orc b/7.x/src/py/examples/pyrun.orc deleted file mode 100644 index 21413407..00000000 --- a/7.x/src/py/examples/pyrun.orc +++ /dev/null @@ -1,20 +0,0 @@ -sr=44100 -kr=4410 -ksmps=10 -nchnls=1 - -pyinit - -pyruni "import random" - -instr 1 - ; This message is stored in the main namespace - ; and is the same for every instance - pyruni "message = 'a global random number: %f' % random.random()" - pyrun "print(message)" - - ; This message is stored in the private namespace - ; and is different for different instances - pylruni "message = 'a private random number: %f' % random.random()" - pylrun "print(message)" -endin diff --git a/7.x/src/py/examples/pyrun.sco b/7.x/src/py/examples/pyrun.sco deleted file mode 100644 index 87b69bf1..00000000 --- a/7.x/src/py/examples/pyrun.sco +++ /dev/null @@ -1 +0,0 @@ -i1 0 0.1 diff --git a/7.x/src/py/examples/pyrunt.orc b/7.x/src/py/examples/pyrunt.orc deleted file mode 100644 index ffc4df11..00000000 --- a/7.x/src/py/examples/pyrunt.orc +++ /dev/null @@ -1,23 +0,0 @@ -sr=44100 -kr=4410 -ksmps=10 -nchnls=1 - -pyinit - -instr 1 - - ktrigger1 metro 3 - ktrigger2 metro 6 - ktrigger3 metro 10 - - pyrunt ktrigger1, "print('zum')" - pyrunt ktrigger2, "print('pa')" - pyrunt ktrigger3, "print('zi')" - - pylrunt ktrigger3 + ktrigger1, "print('zut')" - - a1 rand 5000 - - out a1 -endin diff --git a/7.x/src/py/examples/pyrunt.sco b/7.x/src/py/examples/pyrunt.sco deleted file mode 100644 index 90df1b0c..00000000 --- a/7.x/src/py/examples/pyrunt.sco +++ /dev/null @@ -1,2 +0,0 @@ -i1 0 5 -i1 0 5 diff --git a/7.x/src/py/src/README.md b/7.x/src/py/src/README.md deleted file mode 100644 index af366744..00000000 --- a/7.x/src/py/src/README.md +++ /dev/null @@ -1,25 +0,0 @@ -PYTHON OPCODES FOR CSOUND -=== -By Maurizio Umberto Puxeddu.
-Modified by Michael Gogins for Csound 5, 2004 July 24.
-Modified by François Pinot for using with Python 3.x, 2019 November.
- -BEFORE COMPILING ---- -To generate the source code for the Python opcodes just run the **pycall-gen.py** -and **pyx-gen.py** scripts in this directory. - -This way of doing had been broken by adding different modifications directly to -the **pycall.auto.c** and **pyx.auto.c** files. These modifications have been integrated -back into the **pycall-gen.py** and **pyx-gen.py** scripts. -So if one has to modifiy the opcodes, one should do this in the two scripts -**pycall-gen.py** and **pyx-gen.py**, and then regenerate the -opcodes files by running these scripts. - -NOTES ---- -These opcodes require the Python virtual machine to be initialized, which -can be done by putting the `pyinit` opcode in the orchestra header. - -See the [Python opcodes pages](https://csound.com/docs/manual/py.html) of the Csound manual, -and examples in the **examples** directory. diff --git a/7.x/src/py/src/pycall.auto.c b/7.x/src/py/src/pycall.auto.c deleted file mode 100644 index eb316fe1..00000000 --- a/7.x/src/py/src/pycall.auto.c +++ /dev/null @@ -1,1970 +0,0 @@ -/* - * pyx.auto.c - * - * Copyright (C) 2002 Maurizio Umberto Puxeddu, Michael Gogins - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Don't modify this file, it's automatically genetated by pyx-gen.py - */ - -static int pycall0_krate(CSOUND *csound, PYCALL0 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - result = eval_string_in_given_context(command, 0); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(result != Py_None)) { - return errMsg(p, "callable must return None"); - } - - Py_DECREF(result); - return OK; -} - -static int pylcall0_irate(CSOUND *csound, PYCALL0 *p) -{ - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylcall0_krate(CSOUND *csound, PYCALL0 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(result != Py_None)) { - return errMsg(p, "callable must return None"); - } - - Py_DECREF(result); - return OK; -} - -static int pylcall0i_irate(CSOUND *csound, PYCALL0 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - create_private_namespace_if_needed(&p->h); - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(result != Py_None)) { - return errMsg(p, "callable must return None"); - } - - Py_DECREF(result); - return OK; -} - -static int pycall0t_krate(CSOUND *csound, PYCALL0T *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - if (!*p->trigger) { - return OK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 2); - - result = eval_string_in_given_context(command, 0); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(result != Py_None)) { - return errMsg(p, "callable must return None"); - } - - Py_DECREF(result); - return OK; -} - -static int pylcall0t_irate(CSOUND *csound, PYCALL0T *p) -{ - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylcall0t_krate(CSOUND *csound, PYCALL0T *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - if (!*p->trigger) { - return OK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 2); - - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(result != Py_None)) { - return errMsg(p, "callable must return None"); - } - - Py_DECREF(result); - return OK; -} - -static int pycall1_krate(CSOUND *csound, PYCALL1 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - result = eval_string_in_given_context(command, 0); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyFloat_Check(result))) { - return errMsg(p, "callable must return a float"); - } - else { - *p->result = PyFloat_AsDouble(result); - } - return OK; - - Py_DECREF(result); - return OK; -} - -static int pylcall1_irate(CSOUND *csound, PYCALL1 *p) -{ - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylcall1_krate(CSOUND *csound, PYCALL1 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyFloat_Check(result))) { - return errMsg(p, "callable must return a float"); - } - else { - *p->result = PyFloat_AsDouble(result); - } - return OK; - - Py_DECREF(result); - return OK; -} - -static int pylcall1i_irate(CSOUND *csound, PYCALL1 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - create_private_namespace_if_needed(&p->h); - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyFloat_Check(result))) { - return errMsg(p, "callable must return a float"); - } - else { - *p->result = PyFloat_AsDouble(result); - } - return OK; - - Py_DECREF(result); - return OK; -} - -static int pycall1t_krate(CSOUND *csound, PYCALL1T *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - if (!*p->trigger) { - *p->result = p->oresult; - return OK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 2); - - result = eval_string_in_given_context(command, 0); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyFloat_Check(result))) { - return errMsg(p, "callable must return a float"); - } - else { - *p->result = PyFloat_AsDouble(result); - p->oresult = *p->result; - } - return OK; - - Py_DECREF(result); - return OK; -} - -static int pylcall1t_irate(CSOUND *csound, PYCALL1T *p) -{ - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylcall1t_krate(CSOUND *csound, PYCALL1T *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - if (!*p->trigger) { - *p->result = p->oresult; - return OK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 2); - - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyFloat_Check(result))) { - return errMsg(p, "callable must return a float"); - } - else { - *p->result = PyFloat_AsDouble(result); - p->oresult = *p->result; - } - return OK; - - Py_DECREF(result); - return OK; -} - -static int pycall2_krate(CSOUND *csound, PYCALL2 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - result = eval_string_in_given_context(command, 0); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 2)) { - return errMsg(p, "callable must return 2 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - } - - Py_DECREF(result); - return OK; -} - -static int pylcall2_irate(CSOUND *csound, PYCALL2 *p) -{ - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylcall2_krate(CSOUND *csound, PYCALL2 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 2)) { - return errMsg(p, "callable must return 2 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - } - - Py_DECREF(result); - return OK; -} - -static int pylcall2i_irate(CSOUND *csound, PYCALL2 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - create_private_namespace_if_needed(&p->h); - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 2)) { - return errMsg(p, "callable must return 2 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - } - - Py_DECREF(result); - return OK; -} - -static int pycall2t_krate(CSOUND *csound, PYCALL2T *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - if (!*p->trigger) { - *p->result1 = p->oresult1; - *p->result2 = p->oresult2; - return OK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 2); - - result = eval_string_in_given_context(command, 0); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 2)) { - return errMsg(p, "callable must return 2 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - p->oresult1 = *p->result1; - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - p->oresult2 = *p->result2; - } - - Py_DECREF(result); - return OK; -} - -static int pylcall2t_irate(CSOUND *csound, PYCALL2T *p) -{ - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylcall2t_krate(CSOUND *csound, PYCALL2T *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - if (!*p->trigger) { - *p->result1 = p->oresult1; - *p->result2 = p->oresult2; - return OK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 2); - - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 2)) { - return errMsg(p, "callable must return 2 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - p->oresult1 = *p->result1; - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - p->oresult2 = *p->result2; - } - - Py_DECREF(result); - return OK; -} - -static int pycall3_krate(CSOUND *csound, PYCALL3 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - result = eval_string_in_given_context(command, 0); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 3)) { - return errMsg(p, "callable must return 3 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - } - - Py_DECREF(result); - return OK; -} - -static int pylcall3_irate(CSOUND *csound, PYCALL3 *p) -{ - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylcall3_krate(CSOUND *csound, PYCALL3 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 3)) { - return errMsg(p, "callable must return 3 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - } - - Py_DECREF(result); - return OK; -} - -static int pylcall3i_irate(CSOUND *csound, PYCALL3 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - create_private_namespace_if_needed(&p->h); - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 3)) { - return errMsg(p, "callable must return 3 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - } - - Py_DECREF(result); - return OK; -} - -static int pycall3t_krate(CSOUND *csound, PYCALL3T *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - if (!*p->trigger) { - *p->result1 = p->oresult1; - *p->result2 = p->oresult2; - *p->result3 = p->oresult3; - return OK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 2); - - result = eval_string_in_given_context(command, 0); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 3)) { - return errMsg(p, "callable must return 3 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - p->oresult1 = *p->result1; - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - p->oresult2 = *p->result2; - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - p->oresult3 = *p->result3; - } - - Py_DECREF(result); - return OK; -} - -static int pylcall3t_irate(CSOUND *csound, PYCALL3T *p) -{ - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylcall3t_krate(CSOUND *csound, PYCALL3T *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - if (!*p->trigger) { - *p->result1 = p->oresult1; - *p->result2 = p->oresult2; - *p->result3 = p->oresult3; - return OK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 2); - - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 3)) { - return errMsg(p, "callable must return 3 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - p->oresult1 = *p->result1; - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - p->oresult2 = *p->result2; - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - p->oresult3 = *p->result3; - } - - Py_DECREF(result); - return OK; -} - -static int pycall4_krate(CSOUND *csound, PYCALL4 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - result = eval_string_in_given_context(command, 0); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 4)) { - return errMsg(p, "callable must return 4 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - } - - Py_DECREF(result); - return OK; -} - -static int pylcall4_irate(CSOUND *csound, PYCALL4 *p) -{ - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylcall4_krate(CSOUND *csound, PYCALL4 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 4)) { - return errMsg(p, "callable must return 4 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - } - - Py_DECREF(result); - return OK; -} - -static int pylcall4i_irate(CSOUND *csound, PYCALL4 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - create_private_namespace_if_needed(&p->h); - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 4)) { - return errMsg(p, "callable must return 4 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - } - - Py_DECREF(result); - return OK; -} - -static int pycall4t_krate(CSOUND *csound, PYCALL4T *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - if (!*p->trigger) { - *p->result1 = p->oresult1; - *p->result2 = p->oresult2; - *p->result3 = p->oresult3; - *p->result4 = p->oresult4; - return OK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 2); - - result = eval_string_in_given_context(command, 0); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 4)) { - return errMsg(p, "callable must return 4 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - p->oresult1 = *p->result1; - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - p->oresult2 = *p->result2; - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - p->oresult3 = *p->result3; - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - p->oresult4 = *p->result4; - } - - Py_DECREF(result); - return OK; -} - -static int pylcall4t_irate(CSOUND *csound, PYCALL4T *p) -{ - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylcall4t_krate(CSOUND *csound, PYCALL4T *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - if (!*p->trigger) { - *p->result1 = p->oresult1; - *p->result2 = p->oresult2; - *p->result3 = p->oresult3; - *p->result4 = p->oresult4; - return OK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 2); - - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 4)) { - return errMsg(p, "callable must return 4 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - p->oresult1 = *p->result1; - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - p->oresult2 = *p->result2; - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - p->oresult3 = *p->result3; - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - p->oresult4 = *p->result4; - } - - Py_DECREF(result); - return OK; -} - -static int pycall5_krate(CSOUND *csound, PYCALL5 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - result = eval_string_in_given_context(command, 0); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 5)) { - return errMsg(p, "callable must return 5 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - *p->result5 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 4)); - } - - Py_DECREF(result); - return OK; -} - -static int pylcall5_irate(CSOUND *csound, PYCALL5 *p) -{ - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylcall5_krate(CSOUND *csound, PYCALL5 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 5)) { - return errMsg(p, "callable must return 5 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - *p->result5 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 4)); - } - - Py_DECREF(result); - return OK; -} - -static int pylcall5i_irate(CSOUND *csound, PYCALL5 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - create_private_namespace_if_needed(&p->h); - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 5)) { - return errMsg(p, "callable must return 5 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - *p->result5 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 4)); - } - - Py_DECREF(result); - return OK; -} - -static int pycall5t_krate(CSOUND *csound, PYCALL5T *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - if (!*p->trigger) { - *p->result1 = p->oresult1; - *p->result2 = p->oresult2; - *p->result3 = p->oresult3; - *p->result4 = p->oresult4; - *p->result5 = p->oresult5; - return OK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 2); - - result = eval_string_in_given_context(command, 0); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 5)) { - return errMsg(p, "callable must return 5 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - p->oresult1 = *p->result1; - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - p->oresult2 = *p->result2; - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - p->oresult3 = *p->result3; - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - p->oresult4 = *p->result4; - *p->result5 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 4)); - p->oresult5 = *p->result5; - } - - Py_DECREF(result); - return OK; -} - -static int pylcall5t_irate(CSOUND *csound, PYCALL5T *p) -{ - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylcall5t_krate(CSOUND *csound, PYCALL5T *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - if (!*p->trigger) { - *p->result1 = p->oresult1; - *p->result2 = p->oresult2; - *p->result3 = p->oresult3; - *p->result4 = p->oresult4; - *p->result5 = p->oresult5; - return OK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 2); - - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 5)) { - return errMsg(p, "callable must return 5 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - p->oresult1 = *p->result1; - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - p->oresult2 = *p->result2; - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - p->oresult3 = *p->result3; - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - p->oresult4 = *p->result4; - *p->result5 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 4)); - p->oresult5 = *p->result5; - } - - Py_DECREF(result); - return OK; -} - -static int pycall6_krate(CSOUND *csound, PYCALL6 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - result = eval_string_in_given_context(command, 0); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 6)) { - return errMsg(p, "callable must return 6 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - *p->result5 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 4)); - *p->result6 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 5)); - } - - Py_DECREF(result); - return OK; -} - -static int pylcall6_irate(CSOUND *csound, PYCALL6 *p) -{ - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylcall6_krate(CSOUND *csound, PYCALL6 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 6)) { - return errMsg(p, "callable must return 6 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - *p->result5 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 4)); - *p->result6 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 5)); - } - - Py_DECREF(result); - return OK; -} - -static int pylcall6i_irate(CSOUND *csound, PYCALL6 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - create_private_namespace_if_needed(&p->h); - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 6)) { - return errMsg(p, "callable must return 6 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - *p->result5 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 4)); - *p->result6 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 5)); - } - - Py_DECREF(result); - return OK; -} - -static int pycall6t_krate(CSOUND *csound, PYCALL6T *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - if (!*p->trigger) { - *p->result1 = p->oresult1; - *p->result2 = p->oresult2; - *p->result3 = p->oresult3; - *p->result4 = p->oresult4; - *p->result5 = p->oresult5; - *p->result6 = p->oresult6; - return OK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 2); - - result = eval_string_in_given_context(command, 0); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 6)) { - return errMsg(p, "callable must return 6 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - p->oresult1 = *p->result1; - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - p->oresult2 = *p->result2; - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - p->oresult3 = *p->result3; - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - p->oresult4 = *p->result4; - *p->result5 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 4)); - p->oresult5 = *p->result5; - *p->result6 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 5)); - p->oresult6 = *p->result6; - } - - Py_DECREF(result); - return OK; -} - -static int pylcall6t_irate(CSOUND *csound, PYCALL6T *p) -{ - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylcall6t_krate(CSOUND *csound, PYCALL6T *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - if (!*p->trigger) { - *p->result1 = p->oresult1; - *p->result2 = p->oresult2; - *p->result3 = p->oresult3; - *p->result4 = p->oresult4; - *p->result5 = p->oresult5; - *p->result6 = p->oresult6; - return OK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 2); - - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 6)) { - return errMsg(p, "callable must return 6 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - p->oresult1 = *p->result1; - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - p->oresult2 = *p->result2; - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - p->oresult3 = *p->result3; - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - p->oresult4 = *p->result4; - *p->result5 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 4)); - p->oresult5 = *p->result5; - *p->result6 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 5)); - p->oresult6 = *p->result6; - } - - Py_DECREF(result); - return OK; -} - -static int pycall7_krate(CSOUND *csound, PYCALL7 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - result = eval_string_in_given_context(command, 0); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 7)) { - return errMsg(p, "callable must return 7 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - *p->result5 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 4)); - *p->result6 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 5)); - *p->result7 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 6)); - } - - Py_DECREF(result); - return OK; -} - -static int pylcall7_irate(CSOUND *csound, PYCALL7 *p) -{ - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylcall7_krate(CSOUND *csound, PYCALL7 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 7)) { - return errMsg(p, "callable must return 7 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - *p->result5 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 4)); - *p->result6 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 5)); - *p->result7 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 6)); - } - - Py_DECREF(result); - return OK; -} - -static int pylcall7i_irate(CSOUND *csound, PYCALL7 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - create_private_namespace_if_needed(&p->h); - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 7)) { - return errMsg(p, "callable must return 7 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - *p->result5 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 4)); - *p->result6 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 5)); - *p->result7 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 6)); - } - - Py_DECREF(result); - return OK; -} - -static int pycall7t_krate(CSOUND *csound, PYCALL7T *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - if (!*p->trigger) { - *p->result1 = p->oresult1; - *p->result2 = p->oresult2; - *p->result3 = p->oresult3; - *p->result4 = p->oresult4; - *p->result5 = p->oresult5; - *p->result6 = p->oresult6; - *p->result7 = p->oresult7; - return OK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 2); - - result = eval_string_in_given_context(command, 0); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 7)) { - return errMsg(p, "callable must return 7 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - p->oresult1 = *p->result1; - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - p->oresult2 = *p->result2; - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - p->oresult3 = *p->result3; - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - p->oresult4 = *p->result4; - *p->result5 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 4)); - p->oresult5 = *p->result5; - *p->result6 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 5)); - p->oresult6 = *p->result6; - *p->result7 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 6)); - p->oresult7 = *p->result7; - } - - Py_DECREF(result); - return OK; -} - -static int pylcall7t_irate(CSOUND *csound, PYCALL7T *p) -{ - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylcall7t_krate(CSOUND *csound, PYCALL7T *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - if (!*p->trigger) { - *p->result1 = p->oresult1; - *p->result2 = p->oresult2; - *p->result3 = p->oresult3; - *p->result4 = p->oresult4; - *p->result5 = p->oresult5; - *p->result6 = p->oresult6; - *p->result7 = p->oresult7; - return OK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 2); - - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 7)) { - return errMsg(p, "callable must return 7 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - p->oresult1 = *p->result1; - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - p->oresult2 = *p->result2; - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - p->oresult3 = *p->result3; - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - p->oresult4 = *p->result4; - *p->result5 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 4)); - p->oresult5 = *p->result5; - *p->result6 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 5)); - p->oresult6 = *p->result6; - *p->result7 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 6)); - p->oresult7 = *p->result7; - } - - Py_DECREF(result); - return OK; -} - -static int pycall8_krate(CSOUND *csound, PYCALL8 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - result = eval_string_in_given_context(command, 0); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 8)) { - return errMsg(p, "callable must return 8 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - *p->result5 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 4)); - *p->result6 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 5)); - *p->result7 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 6)); - *p->result8 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 7)); - } - - Py_DECREF(result); - return OK; -} - -static int pylcall8_irate(CSOUND *csound, PYCALL8 *p) -{ - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylcall8_krate(CSOUND *csound, PYCALL8 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 8)) { - return errMsg(p, "callable must return 8 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - *p->result5 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 4)); - *p->result6 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 5)); - *p->result7 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 6)); - *p->result8 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 7)); - } - - Py_DECREF(result); - return OK; -} - -static int pylcall8i_irate(CSOUND *csound, PYCALL8 *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 1); - - create_private_namespace_if_needed(&p->h); - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 8)) { - return errMsg(p, "callable must return 8 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - *p->result5 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 4)); - *p->result6 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 5)); - *p->result7 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 6)); - *p->result8 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 7)); - } - - Py_DECREF(result); - return OK; -} - -static int pycall8t_krate(CSOUND *csound, PYCALL8T *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - if (!*p->trigger) { - *p->result1 = p->oresult1; - *p->result2 = p->oresult2; - *p->result3 = p->oresult3; - *p->result4 = p->oresult4; - *p->result5 = p->oresult5; - *p->result6 = p->oresult6; - *p->result7 = p->oresult7; - *p->result8 = p->oresult8; - return OK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 2); - - result = eval_string_in_given_context(command, 0); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 8)) { - return errMsg(p, "callable must return 8 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - p->oresult1 = *p->result1; - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - p->oresult2 = *p->result2; - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - p->oresult3 = *p->result3; - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - p->oresult4 = *p->result4; - *p->result5 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 4)); - p->oresult5 = *p->result5; - *p->result6 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 5)); - p->oresult6 = *p->result6; - *p->result7 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 6)); - p->oresult7 = *p->result7; - *p->result8 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 7)); - p->oresult8 = *p->result8; - } - - Py_DECREF(result); - return OK; -} - -static int pylcall8t_irate(CSOUND *csound, PYCALL8T *p) -{ - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylcall8t_krate(CSOUND *csound, PYCALL8T *p) -{ - char command[1024]; - PyObject *result; - int *py_initialize_done; - if (UNLIKELY((py_initialize_done = - csound->QueryGlobalVariable(csound, "PY_INITIALIZE")) == NULL || - *py_initialize_done == 0)) { - return NOTOK; - } - if (!*p->trigger) { - *p->result1 = p->oresult1; - *p->result2 = p->oresult2; - *p->result3 = p->oresult3; - *p->result4 = p->oresult4; - *p->result5 = p->oresult5; - *p->result6 = p->oresult6; - *p->result7 = p->oresult7; - *p->result8 = p->oresult8; - return OK; - } - - format_call_statement(command, (char*) p->function->data, - p->INOCOUNT, p->args, 2); - - result = eval_string_in_given_context(command, GETPYLOCAL(p->h.insdshead)); - - if (UNLIKELY(result == NULL)) { - return pyErrMsg(p, "python exception"); - } - - if (UNLIKELY(!PyTuple_Check(result) || PyTuple_Size(result) != 8)) { - return errMsg(p, "callable must return 8 values"); - } - else { - *p->result1 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 0)); - p->oresult1 = *p->result1; - *p->result2 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 1)); - p->oresult2 = *p->result2; - *p->result3 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 2)); - p->oresult3 = *p->result3; - *p->result4 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 3)); - p->oresult4 = *p->result4; - *p->result5 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 4)); - p->oresult5 = *p->result5; - *p->result6 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 5)); - p->oresult6 = *p->result6; - *p->result7 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 6)); - p->oresult7 = *p->result7; - *p->result8 = PyFloat_AsDouble(PyTuple_GET_ITEM(result, 7)); - p->oresult8 = *p->result8; - } - - Py_DECREF(result); - return OK; -} - diff --git a/7.x/src/py/src/pycall.auto.h b/7.x/src/py/src/pycall.auto.h deleted file mode 100644 index 81935c6b..00000000 --- a/7.x/src/py/src/pycall.auto.h +++ /dev/null @@ -1,247 +0,0 @@ -/* - * pyx.auto.h - * - * Copyright (C) 2002 Maurizio Umberto Puxeddu, Michael Gogins - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Don't modify this file. It's automatically generated by pyx-gen.py - */ - -typedef struct { - OPDS h; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; -} PYCALL0; - -typedef struct { - OPDS h; - MYFLT *trigger; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; -} PYCALL0T; - -typedef struct { - OPDS h; - MYFLT *result; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; -} PYCALL1; - -typedef struct { - OPDS h; - MYFLT *result; - MYFLT *trigger; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; - MYFLT oresult -;} PYCALL1T; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; -} PYCALL2; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *trigger; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; - MYFLT oresult1; - MYFLT oresult2; -} PYCALL2T; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; -} PYCALL3; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *trigger; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; - MYFLT oresult1; - MYFLT oresult2; - MYFLT oresult3; -} PYCALL3T; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *result4; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; -} PYCALL4; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *result4; - MYFLT *trigger; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; - MYFLT oresult1; - MYFLT oresult2; - MYFLT oresult3; - MYFLT oresult4; -} PYCALL4T; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *result4; - MYFLT *result5; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; -} PYCALL5; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *result4; - MYFLT *result5; - MYFLT *trigger; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; - MYFLT oresult1; - MYFLT oresult2; - MYFLT oresult3; - MYFLT oresult4; - MYFLT oresult5; -} PYCALL5T; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *result4; - MYFLT *result5; - MYFLT *result6; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; -} PYCALL6; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *result4; - MYFLT *result5; - MYFLT *result6; - MYFLT *trigger; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; - MYFLT oresult1; - MYFLT oresult2; - MYFLT oresult3; - MYFLT oresult4; - MYFLT oresult5; - MYFLT oresult6; -} PYCALL6T; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *result4; - MYFLT *result5; - MYFLT *result6; - MYFLT *result7; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; -} PYCALL7; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *result4; - MYFLT *result5; - MYFLT *result6; - MYFLT *result7; - MYFLT *trigger; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; - MYFLT oresult1; - MYFLT oresult2; - MYFLT oresult3; - MYFLT oresult4; - MYFLT oresult5; - MYFLT oresult6; - MYFLT oresult7; -} PYCALL7T; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *result4; - MYFLT *result5; - MYFLT *result6; - MYFLT *result7; - MYFLT *result8; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; -} PYCALL8; - -typedef struct { - OPDS h; - MYFLT *result1; - MYFLT *result2; - MYFLT *result3; - MYFLT *result4; - MYFLT *result5; - MYFLT *result6; - MYFLT *result7; - MYFLT *result8; - MYFLT *trigger; - STRINGDAT *function; - MYFLT *args[VARGMAX-3]; - MYFLT oresult1; - MYFLT oresult2; - MYFLT oresult3; - MYFLT oresult4; - MYFLT oresult5; - MYFLT oresult6; - MYFLT oresult7; - MYFLT oresult8; -} PYCALL8T; - diff --git a/7.x/src/py/src/pythonopcodes.h b/7.x/src/py/src/pythonopcodes.h deleted file mode 100644 index 82d7d216..00000000 --- a/7.x/src/py/src/pythonopcodes.h +++ /dev/null @@ -1,47 +0,0 @@ -/* - * pythonopcodes.h - * - * Copyright (C) 2002 Maurizio Umberto Puxeddu - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA -*/ - -#ifndef _pycsound_pythonopcodes_h_ -#define _pycsound_pythonopcodes_h_ - -#ifdef _DEBUG -# undef _DEBUG -# include -# define _DEBUG -#else -# include -#endif -#include "csdl.h" -#include "pyx.auto.h" -#include "pycall.auto.h" - -typedef struct { - OPDS h; -} PYINIT; - -typedef struct { - OPDS h; - STRINGDAT *function; - MYFLT *nresult; - MYFLT *args[VARGMAX]; -} PYCALLN; - -#endif /* _pycsound_pythonopcodes_h_ */ - diff --git a/7.x/src/py/src/pyx-gen.py b/7.x/src/py/src/pyx-gen.py deleted file mode 100644 index d1e54ce9..00000000 --- a/7.x/src/py/src/pyx-gen.py +++ /dev/null @@ -1,257 +0,0 @@ -# -*- coding: utf-8 -*- - -''' -Copyright (C) 2002 Maurizio Umberto Puxeddu, Michael Gogins - -This file is part of Csound. - -The Csound Library is free software; you can redistribute it -and/or modify it under the terms of the GNU Lesser General Public -License as published by the Free Software Foundation; either -version 2.1 of the License, or (at your option) any later version. - -Csound is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU Lesser General Public License for more details. - -You should have received a copy of the GNU Lesser General Public -License along with Csound; if not, write to the Free Software -Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA -02110-1301 USA - -Modification from BSD sources for strNcpy -Copyright (c) 1998 Todd C. Miller - -Modified for speed -- JPff - -Adapted for Python3 -- François Pinot, 2019 - -Automatically generate opcode structures, methods [and table entries]. Just run it. -''' - -def generate_x_method(f, action, context, rate0, triggered): - if rate0 != 'k': - rate = rate0 - else: - rate = '' - - if action in ('exec', 'assign', 'eval'): - size = 1024 - elif action == 'run': - size = 40960 - else: - raise 'undefined action %s' % action - - if action in ('run', 'assign'): - helper = 'run_statement' - elif action == 'exec': - helper = 'exec_file' - elif action == 'eval': - helper = 'eval_string' - else: - raise 'undefined action %s' % action - - if context == 'private': - prefix = 'l' - ns = 'GETPYLOCAL(p->h.insdshead)' - elif context == 'global': - prefix = '' - ns = '0' - prepare = '' - else: - raise 'undefined context %s' % context - - if triggered: - if rate == 'i': raise 'cannot be triggered at i-rate' - t, T = 't', 'T' - else: - t, T = '', '' - - ACTION = action.upper() - RATE = rate.upper() - - name = 'py%(prefix)s%(action)s%(rate)s%(t)s_%(rate0)srate' % locals() - - s = 'static int %(name)s(CSOUND *csound, PY%(ACTION)s%(T)s *p)\n' % locals() - s += '{\n' - s += ' char source[%d];\n' % size - s += ' PyObject *result;\n' - s += ' int *py_initialize_done;\n' - s += ' if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL ||\n' - s += ' *py_initialize_done == 0) {\n' - s += ' return NOTOK;\n' - s += ' }\n\n' - - if triggered: - if action == 'eval': - s += ' if (!*p->trigger) {\n' - s += ' *p->result = p->oresult;\n' - s += ' return OK;\n' - s += ' }\n' - else: - s += ' if (!*p->trigger) {\n' - s += ' return OK;\n' - s += ' }\n' - - if context == 'private' and rate0 == 'i': - s += ' create_private_namespace_if_needed(&p->h);\n\n' - - if action == 'assign': - s += ' snprintf(source, %d, "%%s = %%f", (char*) p->string->data, *p->value);\n' % size - else: - s += " strNcpy(source, (char*) p->string->data, %d); // source[%d] = '\\0'\n\n" % (size, size-1) - - if action == 'exec': - s += ' result = %(helper)s_in_given_context(csound, source, %(ns)s);\n' % locals() - else: - s += ' result = %(helper)s_in_given_context(source, %(ns)s);\n' % locals() - s += ' if (result == NULL) {\n' - s += ' return pyErrMsg(p, "python exception");\n' - s += ' }\n' - - if action == 'eval': - s += ' else if (!PyFloat_Check(result)) {\n' - s += ' errMsg(p, "expression must evaluate in a float");\n' - s += ' }\n' - s += ' else {\n' - s += ' *p->result = PyFloat_AsDouble(result);\n' - if triggered: - s += ' p->oresult = *p->result;\n' - s += ' }\n' - - s += ' Py_DECREF(result);\n' - s += ' return OK;\n' - s += '}\n' - print(s, file=f) - -def generate_init_method(f, action, triggered): - ACTION = action.upper() - if triggered: - t, T = 't', 'T' - else: - t, T = '', '' - s = 'static int pyl%(action)s%(t)s_irate(CSOUND *csound, PY%(ACTION)s%(T)s *p)\n' % locals() - s += '{\n' - s += ' int *py_initialize_done;\n' - s += ' if((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL ||\n' - s += ' *py_initialize_done == 0) {\n' - s += ' return NOTOK;\n' - s += ' }\n' - s += ' create_private_namespace_if_needed(&p->h);\n' - s += ' return OK;\n' - s += '}\n' - print(s, file=f) - -# ---------------- - -def generate_pycall_opcode_struct(f, action, triggered): - ACTION = action.upper() - if triggered: - T = 'T' - else: - T = '' - s = 'typedef struct {\n' - s += ' OPDS h;\n' - if action == 'eval': - s += ' MYFLT *result;\n' - if triggered: - s += ' MYFLT *trigger;\n' - s += ' STRINGDAT *string;\n' - if action == 'assign': - s += ' MYFLT *value;\n' - if action == 'eval' and triggered: - s += ' MYFLT oresult;\n' - s += '} PY%(ACTION)s%(T)s;\n' % locals() - print(s, file=f) - -f = open('pyx.auto.c', 'w') -s = '''/* - * pyx.auto.c - * - * Copyright (C) 2002 Maurizio Umberto Puxeddu, Michael Gogins - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Don't modify this file, it's automatically genetated by pyx-gen.py - */ - -char *strNcpy(char *dst, const char *src, size_t siz) -{ - char *d = dst; - const char *s = src; - size_t n = siz; - - /* Copy as many bytes as will fit or until NULL */ - if (n != 0) { - while (--n != 0) { - if ((*d++ = *s++) == '\\0') - break; - } - } - - /* Not enough room in dst, add NUL */ - if (n == 0) { - if (siz != 0) - *d = '\\0'; /* NUL-terminate dst */ - - //while (*s++) ; - } - return dst; /* count does not include NUL */ -} -''' -print(s, file=f) -for action in ['exec', 'run', 'eval', 'assign']: - generate_x_method(f, action, 'global', 'k', 0) - generate_x_method(f, action, 'global', 'i', 0) - - generate_init_method(f, action, 0) - generate_x_method(f, action, 'private', 'k', 0) - generate_x_method(f, action, 'private', 'i', 0) - - generate_x_method(f, action, 'global', 'k', 1) - generate_init_method(f, action, 1) - generate_x_method(f, action, 'private', 'k', 1) -f.close() - -f = open('pyx.auto.h', 'w') -s = '''/* - * pyx.auto.h - * - * Copyright (C) 2002 Maurizio Umberto Puxeddu, Michael Gogins - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Don't modify this file. It's automatically generated by pyx-gen.py - */ -''' -print(s, file=f) -for action in ['exec', 'run', 'eval', 'assign']: - for triggered in [0, 1]: - generate_pycall_opcode_struct(f, action, triggered) -f.close() - diff --git a/7.x/src/py/src/pyx.auto.c b/7.x/src/py/src/pyx.auto.c deleted file mode 100644 index 7080bca3..00000000 --- a/7.x/src/py/src/pyx.auto.c +++ /dev/null @@ -1,680 +0,0 @@ -/* - * pyx.auto.c - * - * Copyright (C) 2002 Maurizio Umberto Puxeddu, Michael Gogins - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Don't modify this file, it's automatically genetated by pyx-gen.py - */ - -char *strNcpy(char *dst, const char *src, size_t siz) -{ - char *d = dst; - const char *s = src; - size_t n = siz; - - /* Copy as many bytes as will fit or until NULL */ - if (n != 0) { - while (--n != 0) { - if ((*d++ = *s++) == '\0') - break; - } - } - - /* Not enough room in dst, add NUL */ - if (n == 0) { - if (siz != 0) - *d = '\0'; /* NUL-terminate dst */ - - //while (*s++) ; - } - return dst; /* count does not include NUL */ -} - -static int pyexec_krate(CSOUND *csound, PYEXEC *p) -{ - char source[1024]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - strNcpy(source, (char*) p->string->data, 1024); // source[1023] = '\0' - - result = exec_file_in_given_context(csound, source, 0); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - Py_DECREF(result); - return OK; -} - -static int pyexeci_irate(CSOUND *csound, PYEXEC *p) -{ - char source[1024]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - strNcpy(source, (char*) p->string->data, 1024); // source[1023] = '\0' - - result = exec_file_in_given_context(csound, source, 0); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - Py_DECREF(result); - return OK; -} - -static int pylexec_irate(CSOUND *csound, PYEXEC *p) -{ - int *py_initialize_done; - if((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylexec_krate(CSOUND *csound, PYEXEC *p) -{ - char source[1024]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - strNcpy(source, (char*) p->string->data, 1024); // source[1023] = '\0' - - result = exec_file_in_given_context(csound, source, GETPYLOCAL(p->h.insdshead)); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - Py_DECREF(result); - return OK; -} - -static int pylexeci_irate(CSOUND *csound, PYEXEC *p) -{ - char source[1024]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - create_private_namespace_if_needed(&p->h); - - strNcpy(source, (char*) p->string->data, 1024); // source[1023] = '\0' - - result = exec_file_in_given_context(csound, source, GETPYLOCAL(p->h.insdshead)); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - Py_DECREF(result); - return OK; -} - -static int pyexect_krate(CSOUND *csound, PYEXECT *p) -{ - char source[1024]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - if (!*p->trigger) { - return OK; - } - strNcpy(source, (char*) p->string->data, 1024); // source[1023] = '\0' - - result = exec_file_in_given_context(csound, source, 0); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - Py_DECREF(result); - return OK; -} - -static int pylexect_irate(CSOUND *csound, PYEXECT *p) -{ - int *py_initialize_done; - if((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylexect_krate(CSOUND *csound, PYEXECT *p) -{ - char source[1024]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - if (!*p->trigger) { - return OK; - } - strNcpy(source, (char*) p->string->data, 1024); // source[1023] = '\0' - - result = exec_file_in_given_context(csound, source, GETPYLOCAL(p->h.insdshead)); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - Py_DECREF(result); - return OK; -} - -static int pyrun_krate(CSOUND *csound, PYRUN *p) -{ - char source[40960]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - strNcpy(source, (char*) p->string->data, 40960); // source[40959] = '\0' - - result = run_statement_in_given_context(source, 0); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - Py_DECREF(result); - return OK; -} - -static int pyruni_irate(CSOUND *csound, PYRUN *p) -{ - char source[40960]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - strNcpy(source, (char*) p->string->data, 40960); // source[40959] = '\0' - - result = run_statement_in_given_context(source, 0); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - Py_DECREF(result); - return OK; -} - -static int pylrun_irate(CSOUND *csound, PYRUN *p) -{ - int *py_initialize_done; - if((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylrun_krate(CSOUND *csound, PYRUN *p) -{ - char source[40960]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - strNcpy(source, (char*) p->string->data, 40960); // source[40959] = '\0' - - result = run_statement_in_given_context(source, GETPYLOCAL(p->h.insdshead)); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - Py_DECREF(result); - return OK; -} - -static int pylruni_irate(CSOUND *csound, PYRUN *p) -{ - char source[40960]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - create_private_namespace_if_needed(&p->h); - - strNcpy(source, (char*) p->string->data, 40960); // source[40959] = '\0' - - result = run_statement_in_given_context(source, GETPYLOCAL(p->h.insdshead)); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - Py_DECREF(result); - return OK; -} - -static int pyrunt_krate(CSOUND *csound, PYRUNT *p) -{ - char source[40960]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - if (!*p->trigger) { - return OK; - } - strNcpy(source, (char*) p->string->data, 40960); // source[40959] = '\0' - - result = run_statement_in_given_context(source, 0); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - Py_DECREF(result); - return OK; -} - -static int pylrunt_irate(CSOUND *csound, PYRUNT *p) -{ - int *py_initialize_done; - if((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylrunt_krate(CSOUND *csound, PYRUNT *p) -{ - char source[40960]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - if (!*p->trigger) { - return OK; - } - strNcpy(source, (char*) p->string->data, 40960); // source[40959] = '\0' - - result = run_statement_in_given_context(source, GETPYLOCAL(p->h.insdshead)); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - Py_DECREF(result); - return OK; -} - -static int pyeval_krate(CSOUND *csound, PYEVAL *p) -{ - char source[1024]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - strNcpy(source, (char*) p->string->data, 1024); // source[1023] = '\0' - - result = eval_string_in_given_context(source, 0); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - else if (!PyFloat_Check(result)) { - errMsg(p, "expression must evaluate in a float"); - } - else { - *p->result = PyFloat_AsDouble(result); - } - Py_DECREF(result); - return OK; -} - -static int pyevali_irate(CSOUND *csound, PYEVAL *p) -{ - char source[1024]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - strNcpy(source, (char*) p->string->data, 1024); // source[1023] = '\0' - - result = eval_string_in_given_context(source, 0); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - else if (!PyFloat_Check(result)) { - errMsg(p, "expression must evaluate in a float"); - } - else { - *p->result = PyFloat_AsDouble(result); - } - Py_DECREF(result); - return OK; -} - -static int pyleval_irate(CSOUND *csound, PYEVAL *p) -{ - int *py_initialize_done; - if((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pyleval_krate(CSOUND *csound, PYEVAL *p) -{ - char source[1024]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - strNcpy(source, (char*) p->string->data, 1024); // source[1023] = '\0' - - result = eval_string_in_given_context(source, GETPYLOCAL(p->h.insdshead)); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - else if (!PyFloat_Check(result)) { - errMsg(p, "expression must evaluate in a float"); - } - else { - *p->result = PyFloat_AsDouble(result); - } - Py_DECREF(result); - return OK; -} - -static int pylevali_irate(CSOUND *csound, PYEVAL *p) -{ - char source[1024]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - create_private_namespace_if_needed(&p->h); - - strNcpy(source, (char*) p->string->data, 1024); // source[1023] = '\0' - - result = eval_string_in_given_context(source, GETPYLOCAL(p->h.insdshead)); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - else if (!PyFloat_Check(result)) { - errMsg(p, "expression must evaluate in a float"); - } - else { - *p->result = PyFloat_AsDouble(result); - } - Py_DECREF(result); - return OK; -} - -static int pyevalt_krate(CSOUND *csound, PYEVALT *p) -{ - char source[1024]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - if (!*p->trigger) { - *p->result = p->oresult; - return OK; - } - strNcpy(source, (char*) p->string->data, 1024); // source[1023] = '\0' - - result = eval_string_in_given_context(source, 0); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - else if (!PyFloat_Check(result)) { - errMsg(p, "expression must evaluate in a float"); - } - else { - *p->result = PyFloat_AsDouble(result); - p->oresult = *p->result; - } - Py_DECREF(result); - return OK; -} - -static int pylevalt_irate(CSOUND *csound, PYEVALT *p) -{ - int *py_initialize_done; - if((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylevalt_krate(CSOUND *csound, PYEVALT *p) -{ - char source[1024]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - if (!*p->trigger) { - *p->result = p->oresult; - return OK; - } - strNcpy(source, (char*) p->string->data, 1024); // source[1023] = '\0' - - result = eval_string_in_given_context(source, GETPYLOCAL(p->h.insdshead)); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - else if (!PyFloat_Check(result)) { - errMsg(p, "expression must evaluate in a float"); - } - else { - *p->result = PyFloat_AsDouble(result); - p->oresult = *p->result; - } - Py_DECREF(result); - return OK; -} - -static int pyassign_krate(CSOUND *csound, PYASSIGN *p) -{ - char source[1024]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - snprintf(source, 1024, "%s = %f", (char*) p->string->data, *p->value); - result = run_statement_in_given_context(source, 0); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - Py_DECREF(result); - return OK; -} - -static int pyassigni_irate(CSOUND *csound, PYASSIGN *p) -{ - char source[1024]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - snprintf(source, 1024, "%s = %f", (char*) p->string->data, *p->value); - result = run_statement_in_given_context(source, 0); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - Py_DECREF(result); - return OK; -} - -static int pylassign_irate(CSOUND *csound, PYASSIGN *p) -{ - int *py_initialize_done; - if((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylassign_krate(CSOUND *csound, PYASSIGN *p) -{ - char source[1024]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - snprintf(source, 1024, "%s = %f", (char*) p->string->data, *p->value); - result = run_statement_in_given_context(source, GETPYLOCAL(p->h.insdshead)); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - Py_DECREF(result); - return OK; -} - -static int pylassigni_irate(CSOUND *csound, PYASSIGN *p) -{ - char source[1024]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - create_private_namespace_if_needed(&p->h); - - snprintf(source, 1024, "%s = %f", (char*) p->string->data, *p->value); - result = run_statement_in_given_context(source, GETPYLOCAL(p->h.insdshead)); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - Py_DECREF(result); - return OK; -} - -static int pyassignt_krate(CSOUND *csound, PYASSIGNT *p) -{ - char source[1024]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - if (!*p->trigger) { - return OK; - } - snprintf(source, 1024, "%s = %f", (char*) p->string->data, *p->value); - result = run_statement_in_given_context(source, 0); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - Py_DECREF(result); - return OK; -} - -static int pylassignt_irate(CSOUND *csound, PYASSIGNT *p) -{ - int *py_initialize_done; - if((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - create_private_namespace_if_needed(&p->h); - return OK; -} - -static int pylassignt_krate(CSOUND *csound, PYASSIGNT *p) -{ - char source[1024]; - PyObject *result; - int *py_initialize_done; - if ((py_initialize_done = csound->QueryGlobalVariable(csound,"PY_INITIALIZE")) == NULL || - *py_initialize_done == 0) { - return NOTOK; - } - - if (!*p->trigger) { - return OK; - } - snprintf(source, 1024, "%s = %f", (char*) p->string->data, *p->value); - result = run_statement_in_given_context(source, GETPYLOCAL(p->h.insdshead)); - if (result == NULL) { - return pyErrMsg(p, "python exception"); - } - Py_DECREF(result); - return OK; -} - diff --git a/7.x/src/py/src/pyx.auto.h b/7.x/src/py/src/pyx.auto.h deleted file mode 100644 index 4744dcfd..00000000 --- a/7.x/src/py/src/pyx.auto.h +++ /dev/null @@ -1,71 +0,0 @@ -/* - * pyx.auto.h - * - * Copyright (C) 2002 Maurizio Umberto Puxeddu, Michael Gogins - * - * This software is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This software is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this software; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - * - * Don't modify this file. It's automatically generated by pyx-gen.py - */ - -typedef struct { - OPDS h; - STRINGDAT *string; -} PYEXEC; - -typedef struct { - OPDS h; - MYFLT *trigger; - STRINGDAT *string; -} PYEXECT; - -typedef struct { - OPDS h; - STRINGDAT *string; -} PYRUN; - -typedef struct { - OPDS h; - MYFLT *trigger; - STRINGDAT *string; -} PYRUNT; - -typedef struct { - OPDS h; - MYFLT *result; - STRINGDAT *string; -} PYEVAL; - -typedef struct { - OPDS h; - MYFLT *result; - MYFLT *trigger; - STRINGDAT *string; - MYFLT oresult; -} PYEVALT; - -typedef struct { - OPDS h; - STRINGDAT *string; - MYFLT *value; -} PYASSIGN; - -typedef struct { - OPDS h; - MYFLT *trigger; - STRINGDAT *string; - MYFLT *value; -} PYASSIGNT; - diff --git a/6.x/CMakeLists.txt b/CMakeLists.txt similarity index 78% rename from 6.x/CMakeLists.txt rename to CMakeLists.txt index 3cea4fa0..07eb007c 100644 --- a/6.x/CMakeLists.txt +++ b/CMakeLists.txt @@ -8,10 +8,17 @@ if (USE_VCPKG) if (WIN32) set(VCPKG_HOST_TRIPLET "x64-windows-csound" CACHE STRING "") set(VCPKG_TARGET_TRIPLET "x64-windows-csound") - set(CSOUND_INCLUDE_DIR_HINT "C:/Program Files/Csound6_x64") elseif(APPLE) - set(VCPKG_HOST_TRIPLET "x64-osx" CACHE STRING "") - set(VCPKG_TARGET_TRIPLET "x64-osx") + # CMAKE_HOST_SYSTEM_PROCESSOR is not yet set before project(), so query + # the host architecture directly to pick the right vcpkg triplet. + execute_process(COMMAND uname -m OUTPUT_VARIABLE _host_arch OUTPUT_STRIP_TRAILING_WHITESPACE) + if(_host_arch STREQUAL "arm64") + set(VCPKG_HOST_TRIPLET "arm64-osx" CACHE STRING "") + set(VCPKG_TARGET_TRIPLET "arm64-osx") + else() + set(VCPKG_HOST_TRIPLET "x64-osx" CACHE STRING "") + set(VCPKG_TARGET_TRIPLET "x64-osx") + endif() elseif (UNIX) set(VCPKG_HOST_TRIPLET "x64-linux" CACHE STRING "") set(VCPKG_TARGET_TRIPLET "x64-linux") @@ -27,21 +34,30 @@ endif() project(Csound-plugins) +string(TOUPPER "${CMAKE_SYSTEM_NAME}" CMAKE_SYSTEM_NAME_UPPER) +if(CMAKE_SYSTEM_NAME_UPPER STREQUAL "WASI") + set(WASM ON) + include(cmake/WasmPlugins.cmake) +endif() + if (${CMAKE_CXX_COMPILER_ID} STREQUAL "Clang") set(CMAKE_COMPILER_IS_CLANG 1) endif() -set(APIVERSION "6.0") +set(APIVERSION "7.0") set(CMAKE_CXX_STANDARD 11) set(CMAKE_MACOSX_RPATH 1) set(CMAKE_VERBOSE_MAKEFILE ON) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} "${CMAKE_SOURCE_DIR}/cmake/Modules/") -find_package(CSOUND) +find_package(CSOUND 7.0) -if(NOT CSOUND_INCLUDE_DIRS) - message(FATAL_ERROR "Csound header files are required") +if(NOT CSOUND_FOUND) + message(FATAL_ERROR "Csound 7.0 or newer is required") +endif() +if(WASM AND NOT CSOUND_VERSION_MAJOR EQUAL 7) + message(FATAL_ERROR "WASM plugins require the Csound 7 SDK") endif() include(TestBigEndian) @@ -156,7 +172,9 @@ if(LINUX) endif() # plugin install directories (CS_USER_PLUGIN on MacOs/Windows) -if(USE_DOUBLE) +if(WASM) + set(PLUGIN_INSTALL_DIR "lib") +elseif(USE_DOUBLE) message(STATUS "Building with 64-bit floats") if(APPLE) set(PLUGIN_INSTALL_DIR "$ENV{HOME}/Library/csound/${APIVERSION}/plugins64") @@ -225,7 +243,11 @@ endfunction(check_deps) # NB - this was moved here as it needs some VARS defined above # for setting up the framework function(make_plugin libname srcs) - if(APPLE) + if(WASM) + # CMake does not model WASI side modules as shared libraries. + add_executable(${libname} ${srcs}) + configure_wasm_plugin(${libname}) + elseif(APPLE) add_library(${libname} SHARED ${srcs}) else() add_library(${libname} MODULE ${srcs}) @@ -238,6 +260,7 @@ function(make_plugin libname srcs) endwhile() install(TARGETS ${libname} + RUNTIME DESTINATION "${PLUGIN_INSTALL_DIR}" LIBRARY DESTINATION "${PLUGIN_INSTALL_DIR}" ARCHIVE DESTINATION "${PLUGIN_INSTALL_DIR}" ) endfunction(make_plugin) @@ -295,23 +318,46 @@ if(WIN32) add_definitions(-DWIN32) endif() +# Register CTest so subdirectory regression tests (e.g. dssi4cs) can be run. +enable_testing() + add_subdirectory(./src/chua) -add_subdirectory(./src/faustcsound) +add_subdirectory(./src/linear_algebra) +add_subdirectory(./src/fluidOpcodes) +add_subdirectory(./src/dssi4cs) add_subdirectory(./src/image) -add_subdirectory(./src/py) -add_subdirectory(./src/widgets) - -if(BUILD_ABLETON_LINK_OPCODES) -add_subdirectory(./src/AbletonLinkOpcodes) -endif() -add_subdirectory(./src/stk) -add_subdirectory(./src/CUDA) -add_subdirectory(./src/opencl) add_subdirectory(./src/websockets) add_subdirectory(./src/hdf5) -add_subdirectory(./src/linear_algebra) -add_subdirectory(./src/mp3) +add_subdirectory(./src/py) add_subdirectory(./src/wiimote) add_subdirectory(./src/p5glove) -add_subdirectory(./src/jackops) -add_subdirectory(./src/fluidOpcodes) +add_subdirectory(./src/CUDA) +add_subdirectory(./src/OpenCL) +add_subdirectory(./src/stk) +add_subdirectory(./src/faustcsound) + +# Exercise the native loader as well as compilation against the Csound headers. +if(NOT WASM AND (TARGET chua OR TARGET linear_algebra OR TARGET fluidOpcodes)) + find_package(GTest QUIET) + if(GTest_FOUND) + add_executable(plugin_load_tests tests/plugin_load_test.cpp) + set_target_properties(plugin_load_tests PROPERTIES + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED ON) + target_include_directories(plugin_load_tests PRIVATE ${CSOUND_INCLUDE_DIRS}) + target_link_libraries(plugin_load_tests PRIVATE GTest::gtest_main ${CSOUND_LIBRARY}) + foreach(plugin IN ITEMS chua linear_algebra fluidOpcodes) + if(TARGET ${plugin}) + if(plugin STREQUAL "fluidOpcodes") + set(test_name FLUID) + else() + string(TOUPPER "${plugin}" test_name) + endif() + target_compile_definitions(plugin_load_tests PRIVATE + CSOUND_TEST_${test_name}="$") + add_dependencies(plugin_load_tests ${plugin}) + endif() + endforeach() + add_test(NAME plugin_load_tests COMMAND plugin_load_tests) + endif() +endif() diff --git a/README.md b/README.md index 95b09a26..56559b5d 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,444 @@ CSOUND PLUGINS === -This repository contains the code for Csound plugins that require +This repository contains the code for Csound 7 plugins that require external dependencies, as well as code for new third-party contributed -plugins. +plugins. Native plugins build with CMake; the WASM package `@csound/plugins` +uses Nix and CMake and is documented in [`wasm/README.md`](wasm/README.md). +Csound 6 is end-of-life. Its plugin tree is archived on the +[`csound6` branch](https://github.com/csound/plugins/tree/csound6). +Currently, the plugins available in this tree are +- chua (requires the Eigen library, header-only) +**chuap** + +- fluidOpcodes (requires fluidsynth) +**fluidEngine fluidLoad fluidNote fluidCCi fluidCCk fluidControl fluidProgramSelect fluidAllOut fluidOut fluidInfo fluidSetInterpMethod** + +- dssi4cs (hosts native DSSI/LADSPA plugins; Linux only, requires the ALSA headers) +**dssiinit dssiactivate dssiaudio dssisynth dssictls dssiget dssinote dssievent dssinrpn dssiprogram dssiconfigure dssiprograminfo dssiinfo dssilist** + +- faust (requires the Faust compiler library and headers, `libfaust`) +**faustgen faustcompile faustaudio faustdsp faustplay faustctl** + +- image (requires libpng) +**imageload imagesave imagecreate imagesize imagegetpixel imagesetpixel imagefree** + +- hdf5 (requires the HDF5 library) +**hdf5read hdf5write** + +- linear_algebra (requires the gmm++ library, header-only; vendored under this directory) +**A large family of `la_*` opcodes, with i-rate and k-rate variants, for vector, matrix and tensor operations** + +- py (requires a Python 3.x installation with dev libs) +**Many opcodes to call Python code** + +- websockets (requires libwebsockets) +**websocket** + +- wiimote (requires the wiiuse library; CMake-only, see note below) +**wiiconnect wiidata wiisend wiirange** + +- p5glove (requires the p5glove driver; CMake-only, see note below) +**p5gconnect p5gdata** + +- CUDA (requires the CUDA toolkit; Linux only, disabled by default, see note below) +**cudasynth cudanal cudasliding cudaconv cudapconv** + +- OpenCL (requires the OpenCL headers/loader; see note below) +**cladsyn** + +- STK (port of the Synthesis ToolKit instruments; STK 4.6.2 is vendored under this directory, see note below) +**STKBandedWG STKBeeThree STKBlowBotl STKBlowHole STKBowed STKBrass STKClarinet STKDrummer STKFlute STKFMVoices STKHevyMetl STKMandolin STKModalBar STKMoog STKPercFlut STKPlucked STKResonate STKRhodey STKSaxofony STKShakers STKSimple STKSitar STKStifKarp STKTubeBell STKVoicForm STKWhistle STKWurley** + + +WebAssembly (WASM) plugins +--- + +Besides the native plugin libraries described above, this tree can also be +built for the browser: Csound 7 runs under WebAssembly (WASI) through the +`@csound/browser` runtime, and a subset of these plugins is available as WASM +side modules. The WASM build is driven by the Nix expression in +[`wasm/default.nix`](wasm/default.nix) and published as the `@csound/plugins` +npm package; see [`wasm/README.md`](wasm/README.md) for the build and test +instructions. + +Only four plugins are built for WASM: + +- chua (`chuap`) +- image +- linear_algebra +- STK (`stkops`) + +The remaining plugins are **disabled** in the WASM build, either because their +dependencies do not exist in the WASI/static-PIC toolchain or because they need +platform facilities the browser does not provide: fluidOpcodes (FluidSynth), +hdf5, faust (libfaust), websockets, py (Python), wiimote, p5glove, CUDA and +OpenCL. + +The `@csound/plugins` package exposes one asset URL per WASM plugin: + +| Export | Plugin | File | +| --- | --- | --- | +| `plugins.chua` | chua | `chua.wasm` | +| `plugins.image` | image | `image.wasm` | +| `plugins.linearAlgebra` | linear_algebra | `linear_algebra.wasm` | +| `plugins.stk` | STK | `stkops.wasm` | + +The URLs are passed to the runtime's `withPlugins` option, e.g.: + +```js +import { Csound } from "@csound/browser"; +import { plugins } from "@csound/plugins"; + +const csound = await Csound({ withPlugins: [plugins.chua, plugins.image] }); +``` + +Unlike the native build, the WASM STK plugin embeds its raw-wave samples in +`stkops.wasm` (`STK_EMBED_RAWWAVES` defaults to ON for WASM): at initialization +it writes them to Csound's virtual filesystem and sets the wave path, so no +separate download or `RAWWAVE_PATH` setup is needed. An explicit `RAWWAVE_PATH` +still overrides the embedded samples. + + +Fluid settings opcodes +--- + +Besides the engine/SoundFont/MIDI opcodes listed above, `fluidOpcodes` +provides four opcodes to read and write the settings of an engine created by +`fluidEngine` (resolved through `fluid_synth_get_settings`). In each case the +first argument is the engine id and the second is the setting name: + +| Opcode | Outputs | Inputs | Description | +| --- | --- | --- | --- | +| `fluidSettingNum` | – | `iSk` | set an int/num/bool setting | +| `fluidSettingString` | – | `iSS` | set a string setting | +| `fluidSettingGetNum` | `k` | `iS` | read a numeric setting | +| `fluidSettingGetString` | `S` | `iS` | read a string setting | + +The setters fail at init if the setting name is unknown or of the wrong type; +the getters report a message and return 0 (or an empty string) instead. + + +Wiimote opcodes +--- + +The `wiimote` opcodes (`wiiconnect`, `wiidata`, `wiisend`, `wiirange`) are a +CMake-only port of the Csound 6.x plugin. They are **not** built by the CI and +are **not** included in the release packages, because the required `wiiuse` +library is not available as a standard distribution package: it is not in the +Ubuntu repositories, and the vcpkg `wiiuse` port ships only pkg-config +metadata, so it cannot be wired into the automated builds. + +When `wiiuse` (headers and library) is found by CMake, the plugin is built and +installed like any other; otherwise `BUILD_WIIMOTE_OPCODES` is silently +disabled. To build it locally, install `wiiuse` and configure with +`-DBUILD_WIIMOTE_OPCODES=ON`. + + +P5Glove opcodes +--- + +The `p5glove` opcodes (`p5gconnect`, `p5gdata`) are a CMake-only port of the +Csound 6.x plugin. They are **not** built by the CI and are **not** included in +the release packages, because the required P5Glove driver is not widely +packaged: there is no vcpkg port for `libp5glove`, and its Linux backend needs +the legacy libusb-0.1 API (not present in vcpkg). + +When an external `libp5glove` (headers and library) is found by CMake, the +plugin is built and installed like any other; otherwise +`BUILD_P5GLOVE_OPCODES` is silently disabled. On macOS the driver can +additionally be built from the sources bundled under `src/p5glove/libp5glove` +by configuring with `-DP5GLOVE_USE_BUNDLED=ON` (in addition to +`-DBUILD_P5GLOVE_OPCODES=ON`). + + +CUDA opcodes +--- + +The `CUDA` opcodes (`cudasynth`, `cudanal`, `cudasliding`, `cudaconv`, +`cudapconv`) are a port of the Csound 6.x plugin to the Csound 7 plugin API. +They run their DSP kernels on an NVIDIA GPU; `cudanal` and `cudapconv` also +rely on cuFFT. + +They are **disabled by default** and only built when the CUDA toolkit is +found: configure with `-DBUILD_CUDA_OPCODES=ON`. CUDA is **not available on +macOS** (NVIDIA dropped macOS support after 10.13), so on macOS the option is +ignored. The Linux CI builds them compile-only (the hosted runners have no +GPU) and they are included in the Linux release package. Windows is not built +or tested, so the CUDA opcodes are currently Linux-only. + +Building them requires CMake 3.18 or later; with an older CMake the option +is ignored. The compute capabilities default to `75 80 86 89`; override with +`-DCMAKE_CUDA_ARCHITECTURES="..."` (for example `native` for the local GPU). + + +OpenCL opcodes +--- + +The `OpenCL` opcode (`cladsyn`) is a port of the Csound 6.x plugin to the +Csound 7 plugin API. It runs its DSP kernels on any available OpenCL device +(CPU or GPU) through the ICD loader. + +It is built by default when OpenCL is available: the system framework is used +on macOS, `ocl-icd-opencl-dev` on Linux and the vcpkg `opencl` port on Windows. +Configure with `-DBUILD_OPENCL_OPCODES=OFF` to exclude it. The CI builds it +compile-only (the hosted runners have no OpenCL device) and it is included in +the Linux, macOS and Windows release packages. + + +STK opcodes +--- + +The `STK` opcodes are a port of the Csound 6.x plugin to the Csound 7 plugin +API. They expose the instruments of Perry Cook and Gary Scavone's Synthesis +ToolKit in C++ (STK), each named `STK` followed by the STK class, e.g. +`STKSitar`. All of them share the same signature: + +``` +aout STKName ifrequency igain {kcontroller0, kvalue0, ..., kcontrollerN, kvalueN} +``` + +The STK sources are **vendored under this directory** (`include/`, `src/` and +`rawwaves/`) and built from source, so no external STK installation is needed +and the result is independent of any system or package-manager STK. The +vendored copy carries a fix for the sample buildup reported in +[csound/plugins#4](https://github.com/csound/plugins/issues/4): the noise +modulation of the sitar delay line in `Sitar::setFrequency`, which could make +`STKSitar` build up clipped output over time, has been removed. + +It is built by default; configure with `-DBUILD_STK_OPCODES=OFF` to exclude +it. The wavetables the instruments need are installed alongside the plugin +(under `stk/rawwaves`). At run time the `RAWWAVE_PATH` environment variable, +if set, overrides the built-in default location; otherwise the installed +`rawwaves` directory is used. The plugin is included in the Linux, macOS and +Windows release packages. + + +Faust opcodes +--- + +The `faust` opcodes embed the [Faust](https://faust.grame.fr/) compiler and +DSP runtime into Csound, so a Faust program supplied as a string is compiled +at init time and run as a Csound opcode. They are a port of the Csound 6.x +plugin to the Csound 7 plugin API: + +``` +ihandle[,asig1,...] faustgen Scode[,ain1,...] +ihandle faustcompile Scode, Sargs[, iasync, istacksize[, Starget]] +ihandle[,asig1,...] faustaudio ifactory[,ain1,...] +ihandle faustdsp ifactory +asig1[,asig2,...] faustplay ihandle[,ain1,...] + faustctl ihandle, Slabel, kval[, Slabel1, kval1, ...] +``` + +Where: + +- `faustgen` compiles `Scode` and runs it in a single opcode, returning a + handle and the audio outputs. +- `faustcompile` compiles `Scode` and returns `ihandle`, a handle to the + compiled factory, to be passed to `faustaudio` or `faustdsp`. +- `faustaudio`/`faustdsp` instantiate the factory named by `ifactory`; + `faustaudio` has audio inputs and outputs, `faustdsp` has none. +- `faustplay` runs the DSP instance named by `ihandle` (one returned by + `faustgen`, `faustaudio` or `faustdsp`) with explicit inputs and outputs. +- `faustctl` sets the control `Slabel` of a DSP instance; both an i-rate + (`iSi`) and a k-rate (`iSk`) form are provided, the latter updating the + control every k-cycle. + +`Scode` is the Faust program (programs that use the standard library need +`import("stdfaust.lib");`). `Sargs` holds any extra Faust compiler options; +`iasync` selects asynchronous compilation (on by default), `istacksize` is +accepted for backwards compatibility but ignored (the Csound 7 threading API +no longer sets the thread stack size) and `Starget` is an optional LLVM +target string (an empty string selects the native target). + +The opcodes need the Faust compiler library (`libfaust`) and headers. The +build looks for them automatically; if Faust is in a non-standard location, +point CMake at it with `FAUST_INCLUDE_DIR_HINT`, `FAUST_LIB_DIR_HINT` and +`FAUST_STDLIB_DIR_HINT`. With `-DFAUST_STATIC=ON` the plugin is linked +against the self-contained `libfaustwithllvm` archive, which bundles LLVM so +the plugin has no runtime dependency on `libfaust` (at the cost of a much +larger plugin). + +It is built by default; configure with `-DBUILD_FAUST_OPCODES=OFF` to +exclude it. The Faust standard library is installed next to the plugin (in a +`faust` subdirectory), so `import("stdfaust.lib")` works without a separate +Faust installation; set `FAUST_LIB_PATH` at run time to override it. The CI +downloads the official Faust release binaries, builds with `FAUST_STATIC=ON` +and includes the plugin (and the standard library) in the Linux, macOS and +Windows release packages. + + +DSSI and LADSPA opcodes +--- + +The `dssi4cs` plugin hosts native DSSI synths and LADSPA effects: + +``` +ih dssiinit Sfilename, iindex [, iverbose] + dssilist + dssiinfo ih + dssiactivate ih, kactive +a1[,a2,...] dssisynth ih [, ain1, ...] +a1[,a2,...] dssiaudio ih [, ain1, ...] + dssinote ktrigger, ih, knote, kvelocity, kduration [, ichannel] + dssievent ktrigger, ih, knote, kvelocity + dssievent ktrigger, ih, kstatus, kchannel, kdata1, kdata2 [, koffset] + dssinrpn ktrigger, ih, kchannel, kparameter, kvalue [, koffset] + dssictls ih, iport, kvalue, ktrigger +kvalue dssiget ih, iport + dssiprogram ih, kbank, kprogram, ktrigger +Sname, ibank, iprogram dssiprograminfo ih, iindex + dssiconfigure ih, Skey, Svalue +``` + +The opcodes load a plugin library, instantiate a DSSI synth or LADSPA effect, +feed it Csound audio and MIDI events, and return its audio. Libraries are +looked up by path or through `DSSI_PATH` and `LADSPA_PATH`. See the +[module README](src/dssi4cs/README.md) for the full opcode reference. + +Hosting native plugins is Linux-only: the opcodes load the shared objects with +`dlopen` and use the ALSA sequencer event types, so they need the ALSA headers +(`libasound2-dev` on Debian/Ubuntu) but not the ALSA library or a running +sequencer. The DSSI and LADSPA API headers are **vendored under this +directory** (in `src/dssi4cs/include`), so no system DSSI/LADSPA development +package is required. + +It is built by default on Linux when the ALSA headers are found and silently +disabled elsewhere; configure with `-DBUILD_DSSI_OPCODES=OFF` to exclude it. +It is included in the Linux release package only. + + +Releasing +-------------- + +[`package.json`](package.json) holds the plugin version for both native and npm +packages. GitHub release tags and asset names use that version as written, +without a `v` prefix. The workflow's `CSOUND_VERSION` selects the Csound build +used to compile and test native plugins; it does not set the plugin version. + +1. Set the next version with `npm version --no-git-tag-version` and + commit both `package.json` and `package-lock.json`. The workflow checks that + their versions match. Use a stable version for a release from `main`, or a + prerelease version for a release from `develop`. +2. Open a PR into the release branch (normally `develop` into `main`) and add + the `release` label before merging it. +3. Merge the PR. Once all native build jobs pass, the push workflow publishes + the release and tags that exact merge commit. A prerelease version creates + a GitHub prerelease. + +PR builds, manual runs, and pushes without a matching merged PR carrying the +`release` label only build and test. New pushes do not cancel release builds. +To retry a failed release, rerun its original workflow run. A tag that already +points to the same commit is safe to reuse; if it points elsewhere, choose a +new version. The workflow never moves an existing tag. + +npm publication remains a separate manual step; see the +[npm release instructions](wasm/README.md#npm-releases). + + +Release binaries and macOS architecture +-------------- + +The CI builds each plugin on its default hosted runner and packages the +resulting plugin libraries into the GitHub release assets. These binaries are +single-architecture — they are **not** universal fat binaries. + +On macOS the release plugins are built on the Apple Silicon (`arm64`) +`macos-latest` runner with no `CMAKE_OSX_ARCHITECTURES` override, and the +dependencies come from vcpkg using the `arm64-osx` triplet. The +`csound7-plugins-*-macos.zip` release asset therefore contains **arm64-only** +`.dylib` files: they load in the arm64 build of Csound on Apple Silicon, but +not in the x86_64 (Intel) build on Intel Macs. Users on Intel Macs should +build the plugins from source against x86_64 versions of their dependencies +(see the build instructions below). + + +Install location +-------------- +The CMake scripts in this repository use the default CS_USER_PLUGIN +location on MacOS and Windows as defined in the Csound build, or a +library instalation directory (customisable) on LINUX. These are: + +- LINUX: depends on both `CMAKE_INSTALL_PREFIX` and `USE_LIB64`. It is then installed in: + * if `USE_LIB64=1` then to + * for doubles: `${CSOUND_INSTALL_PREFIX}/lib64/csound/plugins64-${APIVERSION}` + * for floats: `${CSOUND_INSTALL_PREFIX}/lib64/csound/plugins-${APIVERSION}` (floats)` + * if `USE_LIB64=0` then to + * for doubles: `${CSOUND_INSTALL_PREFIX}/lib/csound/plugins64-${APIVERSION}` + * for floats: `${CSOUND_INSTALL_PREFIX}/lib/csound/plugins-${APIVERSION}` + +- MACOS: + * For doubles: `$HOME/Library/csound/${APIVERSION}/plugins64` + * For floats: `$HOME/Library/csound/${APIVERSION}/plugins` +- Windows: + * For doubles: `%LOCALAPPDATA%\csound\${APIVERSION}\plugins64` + * For floats: `%LOCALAPPDATA%\csound\${APIVERSION}\plugins` + + +Build Instructions for Linux and MacOS +--- + +The build requires Csound to be installed, as well as CMake. With this +in place, you can do : + +``` +$ git clone https://github.com/csound/plugins.git +$ cd plugins +$ mkdir build +$ cd build +$ cmake ../ +$ make +``` + +By default, all the plugins are built. If one wants to exclude a +plugin from the build process, one can pass an option to the cmake command. +The options are `BUILD_CHUA_OPCODES`, `BUILD_CUDA_OPCODES`, +`BUILD_DSSI_OPCODES`, `BUILD_OPENCL_OPCODES`, `BUILD_FAUST_OPCODES`, +`BUILD_FLUID_OPCODES`, `BUILD_IMAGE_OPCODES`, `BUILD_HDF5_OPCODES`, +`BUILD_LINEAR_ALGEBRA_OPCODES`, `BUILD_P5GLOVE_OPCODES`, `BUILD_STK_OPCODES`, +`BUILD_WIIMOTE_OPCODES`, `BUILD_WEBSOCKET_OPCODE` and +`BUILD_PYTHON_OPCODES`. For example, to +exclude the chua plugin, the `cmake` command would be: + +``` +$ cmake -DBUILD_CHUA_OPCODES=OFF ../ +``` + + +To install the opcodes you have built + +``` +$ make install +``` + +Depending on your permissions, you might need to prepend `sudo` to +these commands. After the first build the plugins can be updated with + +``` +$ git pull +$ make +$ make install +``` + +using `sudo` in the last step if raised permissions are needed. On +Linux, the installation location can be set with the relevant CMake +variables as indicated above. + +Csound Location +------------ +CMake will normally find the installed Csound headers (and library) +automatically. However, if your Csound headers and library are not +placed in the usual locations, you can use the following CMake option variables +to tell CMake where they are: + +``` +CSOUND_INCLUDE_DIR_HINT +``` +and + +``` +CSOUND_LIBRARY_DIR_HINT diff --git a/THIRD_PARTY.md b/THIRD_PARTY.md new file mode 100644 index 00000000..7c3f6707 --- /dev/null +++ b/THIRD_PARTY.md @@ -0,0 +1,22 @@ +# Third-party code in the WASM package + +The plugins use the LGPL-2.1-or-later license in `LICENSE`. Their source and +copyright notices live under [`src`](https://github.com/csound/plugins/tree/develop/src). +The package also contains code from these libraries: + +| Library | Used by | License notices | +| --- | --- | --- | +| Eigen | Chua | `dist/licenses/eigen/` | +| GMM++ (GetFEM) | Linear algebra | `dist/licenses/gmm.txt`; LGPL-3.0-or-later with the exception in the headers | +| libpng | Image | `dist/licenses/libpng.txt` | +| zlib | Image | `dist/licenses/zlib.txt` | +| STK and its raw-wave samples | STK instruments | `dist/licenses/stk.txt` | +| wasi-libc, musl, cloudlibc, musl-fts | C runtime | `dist/licenses/wasi-libc/` | +| LLVM libc++, libc++abi, libunwind, compiler-rt | C++ runtime and compiler support | `dist/licenses/libcxx.txt`, `libcxxabi.txt`, `libunwind.txt`, `compiler-rt.txt` | + +The Nix build copies these notices from the source used to build each library. +[`wasm/nixpkgs.nix`](https://github.com/csound/plugins/blob/develop/wasm/nixpkgs.nix) +pins their source versions. The GMM++ headers and STK sources and samples live +in this repository. The build uses upstream CMake and make options for static +PIC libraries, plus a small CMake target for LLVM's WASM unwinder; it adds no +source patches. `wasm/default.nix` records those options. diff --git a/6.x/cmake/CompilerOptimizations.cmake b/cmake/CompilerOptimizations.cmake similarity index 100% rename from 6.x/cmake/CompilerOptimizations.cmake rename to cmake/CompilerOptimizations.cmake diff --git a/7.x/cmake/Modules/FindCSOUND.cmake b/cmake/Modules/FindCSOUND.cmake similarity index 55% rename from 7.x/cmake/Modules/FindCSOUND.cmake rename to cmake/Modules/FindCSOUND.cmake index d83c0c05..a0dec67e 100644 --- a/7.x/cmake/Modules/FindCSOUND.cmake +++ b/cmake/Modules/FindCSOUND.cmake @@ -51,12 +51,46 @@ else() ) endif() +if(CSOUND_INCLUDE_DIR AND EXISTS "${CSOUND_INCLUDE_DIR}/version.h") + file(STRINGS "${CSOUND_INCLUDE_DIR}/version.h" _csound_major + REGEX "^#define[ \t]+CS_VERSION[ \t]+") + file(STRINGS "${CSOUND_INCLUDE_DIR}/version.h" _csound_minor + REGEX "^#define[ \t]+CS_SUBVER[ \t]+") + file(STRINGS "${CSOUND_INCLUDE_DIR}/version.h" _csound_patch + REGEX "^#define[ \t]+CS_PATCHLEVEL[ \t]+") + + string(REGEX REPLACE "^#define[ \t]+CS_VERSION[ \t]+\\(?([0-9]+)\\)?.*" "\\1" + CSOUND_VERSION_MAJOR "${_csound_major}") + string(REGEX REPLACE "^#define[ \t]+CS_SUBVER[ \t]+\\(?([0-9]+)\\)?.*" "\\1" + CSOUND_VERSION_MINOR "${_csound_minor}") + string(REGEX REPLACE "^#define[ \t]+CS_PATCHLEVEL[ \t]+\\(?([0-9]+)\\)?.*" "\\1" + CSOUND_VERSION_PATCH "${_csound_patch}") + + if(CSOUND_VERSION_MAJOR MATCHES "^[0-9]+$") + if(NOT CSOUND_VERSION_MINOR MATCHES "^[0-9]+$") + set(CSOUND_VERSION_MINOR 0) + endif() + if(NOT CSOUND_VERSION_PATCH MATCHES "^[0-9]+$") + set(CSOUND_VERSION_PATCH 0) + endif() + set(CSOUND_VERSION + "${CSOUND_VERSION_MAJOR}.${CSOUND_VERSION_MINOR}.${CSOUND_VERSION_PATCH}") + endif() +endif() + include(FindPackageHandleStandardArgs) # handle the QUIETLY and REQUIRED arguments and set CSOUND_FOUND to TRUE -# if all listed variables are TRUE +# if all listed variables are TRUE and the version satisfies the request +if(WASM) + # Side modules call the browser engine through its API table. + set(_csound_required_vars CSOUND_INCLUDE_DIR) +else() + set(_csound_required_vars CSOUND_LIBRARY CSOUND_INCLUDE_DIR) +endif() find_package_handle_standard_args(CSOUND - CSOUND_LIBRARY CSOUND_INCLUDE_DIR + REQUIRED_VARS ${_csound_required_vars} + VERSION_VAR CSOUND_VERSION ) mark_as_advanced(CSOUND_INCLUDE_DIR CSOUND_LIBRARY) diff --git a/6.x/cmake/Modules/FindEIGEN3.cmake b/cmake/Modules/FindEIGEN3.cmake similarity index 100% rename from 6.x/cmake/Modules/FindEIGEN3.cmake rename to cmake/Modules/FindEIGEN3.cmake diff --git a/cmake/Modules/FindFAUST.cmake b/cmake/Modules/FindFAUST.cmake new file mode 100644 index 00000000..a14dc99f --- /dev/null +++ b/cmake/Modules/FindFAUST.cmake @@ -0,0 +1,106 @@ +# Find Faust2 +include(FindPackageHandleStandardArgs) + +# When ON, link against the self-contained static library that bundles LLVM +# (libfaustwithllvm on Linux/macOS, libfaustwithllvm.lib on Windows). This +# produces a plugin with no runtime dependency on libfaust. +option(FAUST_STATIC "Link against a static libfaust that bundles LLVM" OFF) + +find_path(FAUST_INCLUDE_DIR faust/dsp/llvm-dsp.h + HINTS + C:/Program Files/Faust/include/ + /opt/lib/faust/architecture/ + /usr/lib/faust/architecture/ + /usr/local/lib/faust/architecture/ + "${FAUST_INCLUDE_DIR_HINT}" +) + +if(FAUST_STATIC) + find_library(FAUST_LIBRARY + NAMES + libfaustwithllvm.lib + libfaustwithllvm.a + faustwithllvm + HINTS + "${FAUST_LIB_DIR_HINT}" + ) +else() + find_library(FAUST_LIBRARY + NAMES + libfaust.so + libfaust.dylib + faust.dll + faust + libfaust + HINTS + "${FAUST_LIB_DIR_HINT}" + ) +endif() + +# Locate the Faust standard library (containing stdfaust.lib). It is shipped +# as share/faust in the binary packages and as libraries/ in the source tree. +find_path(FAUST_STDLIB_DIR stdfaust.lib + HINTS + "${FAUST_STDLIB_DIR_HINT}" + "${FAUST_LIB_DIR_HINT}/../share/faust" + "${FAUST_INCLUDE_DIR_HINT}/../share/faust" + "${FAUST_LIB_DIR_HINT}/../libraries" + "${FAUST_INCLUDE_DIR_HINT}/../libraries" + "${FAUST_INCLUDE_DIR_HINT}/../share/faust" +) + +find_package_handle_standard_args(FAUST FAUST_INCLUDE_DIR FAUST_LIBRARY) + +if(FAUST_FOUND) + set(FAUST_INCLUDE_DIRS ${FAUST_INCLUDE_DIR}) + + if(FAUST_STATIC) + # libfaustwithllvm bundles LLVM, so only platform libraries are needed. + if(WIN32) + set(FAUST_LIBRARIES ${FAUST_LIBRARY} + ws2_32 crypt32 dbghelp ole32 shell32 psapi bcrypt + version uuid user32 advapi32 ntdll) + elseif(APPLE) + set(FAUST_LIBRARIES ${FAUST_LIBRARY} z) + else() + set(_faust_syslibs dl pthread z) + find_library(FAUST_TINFO_LIBRARY NAMES tinfo ncurses) + if(FAUST_TINFO_LIBRARY) + list(APPEND _faust_syslibs ${FAUST_TINFO_LIBRARY}) + endif() + set(FAUST_LIBRARIES ${FAUST_LIBRARY} ${_faust_syslibs}) + endif() + else() + set(FAUST_LIBRARIES ${FAUST_LIBRARY}) + + if("${FAUST_LIBRARY}" MATCHES ".*\\.a") + # This is a static build of faust without LLVM, hence + # we have to add all the LLVM flags... + + find_program(LLVM_CONFIG llvm-config + HINTS + /usr/bin + /usr/local/bin + /usr/local/opt/llvm/bin + ) + + if(NOT LLVM_CONFIG) + message(WARNING "Using a static Faust library requires LLVM tooling to be present in the path.") + UNSET(FAUST_FOUND) + else() + exec_program(${LLVM_CONFIG} ARGS --includedir OUTPUT_VARIABLE LLVM_DIR) + exec_program(${LLVM_CONFIG} ARGS --libs OUTPUT_VARIABLE LLVM_LIBS) + exec_program(${LLVM_CONFIG} ARGS --version OUTPUT_VARIABLE LLVM_VERSION) + exec_program(${LLVM_CONFIG} ARGS --ldflags OUTPUT_VARIABLE LLVM_LDFLAGS) + + set(LLVM_VERSION LLVM_${LLVM_VERSION_MAJOR}${LLVM_VERSION_MINOR}) + + find_package(OpenSSL REQUIRED) + set(FAUST_LIBRARIES ${FAUST_LIBRARIES} dl ${OPENSSL_LIBRARIES} ncurses z ${LLVM_LDFLAGS} ${LLVM_LIBS} ) + endif() + endif() + endif() +else() + set(FAUST_LIBRARIES) + set(FAUST_INCLUDE_DIRS) +endif() diff --git a/6.x/cmake/Modules/FindFLUIDSYNTH.cmake b/cmake/Modules/FindFLUIDSYNTH.cmake similarity index 100% rename from 6.x/cmake/Modules/FindFLUIDSYNTH.cmake rename to cmake/Modules/FindFLUIDSYNTH.cmake diff --git a/6.x/cmake/Modules/FindGMM.cmake b/cmake/Modules/FindGMM.cmake similarity index 93% rename from 6.x/cmake/Modules/FindGMM.cmake rename to cmake/Modules/FindGMM.cmake index 1b4d3596..208f7983 100644 --- a/6.x/cmake/Modules/FindGMM.cmake +++ b/cmake/Modules/FindGMM.cmake @@ -10,9 +10,10 @@ if (GMM_INCLUDE_DIR) set(GMM_FOUND TRUE) else (GMM_INCLUDE_DIR) find_path(GMM_INCLUDE_DIR NAMES gmm/gmm.h - PATHS + HINTS + ${GMM_INCLUDE_PATH} + PATHS ${INCLUDE_INSTALL_DIR} - ${GMM_INCLUDE_PATH} ) include(FindPackageHandleStandardArgs) find_package_handle_standard_args(GMM DEFAULT_MSG GMM_INCLUDE_DIR) diff --git a/6.x/cmake/Modules/FindLAME.cmake b/cmake/Modules/FindLAME.cmake similarity index 100% rename from 6.x/cmake/Modules/FindLAME.cmake rename to cmake/Modules/FindLAME.cmake diff --git a/6.x/cmake/Modules/FindSTK.cmake b/cmake/Modules/FindSTK.cmake similarity index 100% rename from 6.x/cmake/Modules/FindSTK.cmake rename to cmake/Modules/FindSTK.cmake diff --git a/cmake/WasmPlugins.cmake b/cmake/WasmPlugins.cmake new file mode 100644 index 00000000..1d183cf8 --- /dev/null +++ b/cmake/WasmPlugins.cmake @@ -0,0 +1,52 @@ +# Match platform/wasm-wasi/src/plugin_example*.nix in csound/csound. +set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) +set(USE_COMPILER_OPTIMIZATIONS OFF CACHE BOOL "" FORCE) +set(REQUIRE_PTHREADS OFF CACHE BOOL "" FORCE) +set(USE_DOUBLE ON CACHE BOOL "" FORCE) +if(NOT WASM_MAX_MEMORY) + message(FATAL_ERROR "WASM_MAX_MEMORY must match the shared-memory Csound engine") +endif() +execute_process(COMMAND ${CMAKE_C_COMPILER} --print-libgcc-file-name + OUTPUT_VARIABLE WASM_BUILTINS OUTPUT_STRIP_TRAILING_WHITESPACE + COMMAND_ERROR_IS_FATAL ANY) + +function(configure_wasm_plugin target) + set_target_properties(${target} PROPERTIES SUFFIX ".wasm") + target_compile_features(${target} PRIVATE c_std_11) + target_compile_definitions(${target} PRIVATE + USE_DOUBLE=1 _WASI_EMULATED_SIGNAL _WASI_EMULATED_MMAN) + target_compile_options(${target} PRIVATE + -fPIC -pthread "SHELL:-mllvm -wasm-enable-sjlj" + "SHELL:-mllvm -wasm-use-legacy-eh=false" + "$<$:-std=gnu11>" + "$<$:-nostdinc++>" + "$<$:-isystem${WASM_CXX_INCLUDE_DIR}>") + if(WASM_CXX_EXCEPTIONS) + target_compile_options(${target} PRIVATE "$<$:-fwasm-exceptions>") + target_link_options(${target} PRIVATE "$<$:-fwasm-exceptions>") + else() + target_compile_options(${target} PRIVATE "$<$:-fno-exceptions>") + endif() + target_link_options(${target} PRIVATE + -shared -nostdlib -nostartfiles + "LINKER:-Bsymbolic" + "LINKER:--experimental-pic" + "LINKER:--no-entry" + "LINKER:--import-table" + "LINKER:--import-memory" + "LINKER:--shared-memory" + "LINKER:--max-memory=${WASM_MAX_MEMORY}" + "LINKER:--allow-undefined" + "LINKER:--export=__wasm_init_tls" + "LINKER:--export=__tls_size" + "LINKER:--export=__tls_align" + "LINKER:--export=__wasm_call_ctors" + "LINKER:--export-if-defined=__wasm_apply_data_relocs" + "LINKER:--export-if-defined=csound_opcode_init" + "LINKER:--export-if-defined=csoundModuleInfo" + "LINKER:--export-if-defined=csoundModuleInit" + "LINKER:--export-if-defined=csoundModuleCreate" + "LINKER:--export-if-defined=csoundModuleDestroy") + target_link_libraries(${target} PRIVATE + "${WASM_LIBCXX}" "${WASM_LIBC}" "${WASM_SETJMP}" "${WASM_BUILTINS}") +endfunction() diff --git a/index.d.ts b/index.d.ts new file mode 100644 index 00000000..d1c46479 --- /dev/null +++ b/index.d.ts @@ -0,0 +1,7 @@ +/** Asset URLs for Csound 7's `withPlugins` option. */ +export declare const plugins: Readonly<{ + chua: string; + image: string; + linearAlgebra: string; + stk: string; +}>; diff --git a/index.js b/index.js new file mode 100644 index 00000000..d8963c56 --- /dev/null +++ b/index.js @@ -0,0 +1,7 @@ +/** Asset URLs for Csound 7's `withPlugins` option. */ +export const plugins = Object.freeze({ + chua: new URL("./dist/chua.wasm", import.meta.url).href, + image: new URL("./dist/image.wasm", import.meta.url).href, + linearAlgebra: new URL("./dist/linear_algebra.wasm", import.meta.url).href, + stk: new URL("./dist/stkops.wasm", import.meta.url).href, +}); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 00000000..84d0b481 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,569 @@ +{ + "name": "@csound/plugins", + "version": "7.0.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "@csound/plugins", + "version": "7.0.0", + "license": "LGPL-2.1-or-later", + "devDependencies": { + "@csound/browser": "7.0.0-beta41", + "@csound/wasm-bin": "7.0.0-beta31" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@babel/runtime": { + "version": "7.29.10", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.29.10.tgz", + "integrity": "sha512-NpugTzbKgGrNe45Ui32cN8vCFFMxVtP/9rtfwv0nP5FRDAjvYBxJcLVpYA9mr1jxP6cnIT7x9LUzuDTbWYYNRA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@csound/browser": { + "version": "7.0.0-beta41", + "resolved": "https://registry.npmjs.org/@csound/browser/-/browser-7.0.0-beta41.tgz", + "integrity": "sha512-FKZ9v3HGtqksZkM+Oc7C1mUfvCk0y+PTL/iXYiCGDYj6gHVjoms34z9wc/BqYGDM+gzuAnimV65SlSb1mEqrZg==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "eventemitter3": "^5.0.4", + "google-closure-compiler": "20260923.0.0", + "google-closure-library": "^20230802.0.0", + "pako": "^3.0.2", + "ramda": "^0.32.0", + "rimraf": "^6.1.3", + "standardized-audio-context": "^25.3.77", + "text-encoding-shim": "^1.0.5", + "unmute-ios-audio": "^3.3.0", + "web-midi-api": "^2.4.0" + }, + "engines": { + "node": ">=22.13.0" + } + }, + "node_modules/@csound/wasm-bin": { + "version": "7.0.0-beta31", + "resolved": "https://registry.npmjs.org/@csound/wasm-bin/-/wasm-bin-7.0.0-beta31.tgz", + "integrity": "sha512-CaFhe9LZ/KwPQKTnVc8L5EJXaTgGCX/1/xepLlRETIv5kdbSEnhyWcrGxltxg2EPhgDK8gmvtN85cTCq21FxrQ==", + "dev": true, + "license": "LGPL-2.1" + }, + "node_modules/automation-events": { + "version": "7.1.19", + "resolved": "https://registry.npmjs.org/automation-events/-/automation-events-7.1.19.tgz", + "integrity": "sha512-cD+TLhJTI0q4AI3ktd353lrGZiVa9AchowSDzQzzGjSoYe22js4vlS32VUtWuaulghi1Yq0KYNWKk9wWuGymPA==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.29.2", + "tslib": "^2.8.1" + }, + "engines": { + "node": ">=18.2.0" + } + }, + "node_modules/b4a": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/b4a/-/b4a-1.9.0.tgz", + "integrity": "sha512-dpfcF9fDNR6++cthXR67iyhgqWy9CBouAvIWhIntzBG6cvK/cnIPiZQjBwi/ZqjjBEDGfoNDtmB0kTjroOJ3pQ==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "react-native-b4a": "*" + }, + "peerDependenciesMeta": { + "react-native-b4a": { + "optional": true + } + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/bare-events": { + "version": "2.9.2", + "resolved": "https://registry.npmjs.org/bare-events/-/bare-events-2.9.2.tgz", + "integrity": "sha512-AIPKioV7/Y/8KfZ3AAhjPJxLLbY49S64Ym5DakZlUg75qQiTgUq9hEJoEwa4eUezPUlXRy/i5NpsKvo9jgKmoA==", + "dev": true, + "license": "Apache-2.0", + "peerDependencies": { + "bare-abort-controller": "*" + }, + "peerDependenciesMeta": { + "bare-abort-controller": { + "optional": true + } + } + }, + "node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/clone": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/clone/-/clone-2.1.2.tgz", + "integrity": "sha512-3Pe/CF1Nn94hyhIYpjtiLhdCoEoz0DqQ+988E9gmeEdQZlojxnOb74wctFyuwWQHzqyf9X7C7MG8juUpqBJT8w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8" + } + }, + "node_modules/eventemitter3": { + "version": "5.0.4", + "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.4.tgz", + "integrity": "sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==", + "dev": true, + "license": "MIT" + }, + "node_modules/events-universal": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/events-universal/-/events-universal-1.0.1.tgz", + "integrity": "sha512-LUd5euvbMLpwOF8m6ivPCbhQeSiYVNb8Vs0fQ8QjXo0JTkEHpz8pxdQf0gStltaPpw0Cca8b39KxvK9cfKRiAw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "bare-events": "^2.7.0" + } + }, + "node_modules/fast-fifo": { + "version": "1.3.2", + "resolved": "https://registry.npmjs.org/fast-fifo/-/fast-fifo-1.3.2.tgz", + "integrity": "sha512-/d9sfos4yxzpwkDkuN7k2SqFKtYNmCTzgfEpz82x34IM9/zc8KGxQoXg1liNC/izpRM/MBdt44Nmx41ZWqk+FQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/google-closure-compiler": { + "version": "20260923.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler/-/google-closure-compiler-20260923.0.0.tgz", + "integrity": "sha512-45HDxy12r6D6E4nLqB/ae7qzX37x+qHYjIMX7ozYJcAeJoWx8EKxNHTP//1I+Phu62ZPW2CgLe5nq0R6xGHsDA==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "chalk": "^2.0.0 || ^3.0.0 || ^4.0.0 || ^5.0.0 <5.6.1 || ^5.6.2 >5.6.1", + "google-closure-compiler-java": "^20260923.0.0", + "minimist": "^1.0.0", + "vinyl": "^3.0.1", + "vinyl-sourcemaps-apply": "^0.2.0" + }, + "bin": { + "google-closure-compiler": "cli.js" + }, + "engines": { + "node": ">=18" + }, + "optionalDependencies": { + "google-closure-compiler-linux": "^20260923.0.0", + "google-closure-compiler-linux-arm64": "^20260923.0.0", + "google-closure-compiler-macos": "^20260923.0.0", + "google-closure-compiler-windows": "^20260923.0.0" + } + }, + "node_modules/google-closure-compiler-java": { + "version": "20260923.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-java/-/google-closure-compiler-java-20260923.0.0.tgz", + "integrity": "sha512-ySzXGXao466rqU1Bpkl/DrE7D8fo/dxkHsN1EnZ5mzr5uEM533FP16+yM6jZfAbjOcWEc48y1LzY2yFhMBsn4w==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/google-closure-compiler-linux": { + "version": "20260923.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-linux/-/google-closure-compiler-linux-20260923.0.0.tgz", + "integrity": "sha512-dO0M/kFl1U2/IPphilHmA/tUshrJ9p9TBYbqXbeaFYs0/yo/515f3qoRelGwc/EC5LB9+X1aHwiiwd+9DK1+9g==", + "cpu": [ + "x32", + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/google-closure-compiler-linux-arm64": { + "version": "20260923.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-linux-arm64/-/google-closure-compiler-linux-arm64-20260923.0.0.tgz", + "integrity": "sha512-BeuzHqu9s408xTkIuYymC+dTSOMiQvmQVGhxXkrM0SAy/aE4/Vjn6M3jRfDu4kNdzoPj3f7lbiegS20pZq/wFA==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/google-closure-compiler-macos": { + "version": "20260923.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-macos/-/google-closure-compiler-macos-20260923.0.0.tgz", + "integrity": "sha512-MnpLL5puaSU3fJtfe9DytNfPwMXQA73AqsYM0BrVlAfshKaW2I5fb11oizGbKUNcVRW5Pd9x+60rKePdTl57rQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/google-closure-compiler-windows": { + "version": "20260923.0.0", + "resolved": "https://registry.npmjs.org/google-closure-compiler-windows/-/google-closure-compiler-windows-20260923.0.0.tgz", + "integrity": "sha512-+yMoYGT0mre78YZe1RoQc4DLj7SSC423quXdslHogKXT6aXjLeP7+yPhpbBPo4ayikrGuNA3WVsoeCa/OjuAXA==", + "cpu": [ + "x32", + "x64" + ], + "dev": true, + "license": "Apache-2.0", + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/google-closure-library": { + "version": "20230802.0.0", + "resolved": "https://registry.npmjs.org/google-closure-library/-/google-closure-library-20230802.0.0.tgz", + "integrity": "sha512-h2lBEX2tjWwfR+dhFukGER15vKY+4cA3nRqwKSOsBdyizlOxTAHOWbEYiGnMoom3/SrVeQ5uG0eKsbxQ3wbsxA==", + "dev": true, + "license": "Apache-2.0" + }, + "node_modules/jazz-midi": { + "version": "1.7.9", + "resolved": "https://registry.npmjs.org/jazz-midi/-/jazz-midi-1.7.9.tgz", + "integrity": "sha512-c8c4BBgwxdsIr1iVm53nadCrtH7BUlnX3V95ciK/gbvXN/ndE5+POskBalXgqlc/r9p2XUbdLTrgrC6fou5p9w==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/jzz": { + "version": "1.9.6", + "resolved": "https://registry.npmjs.org/jzz/-/jzz-1.9.6.tgz", + "integrity": "sha512-J7ENLhXwfm2BNDKRUrL8eKtPhUS/CtMBpiafxQHDBcOWSocLhearDKEdh+ylnZFcr5OXWTed0gj6l/txeQA9vg==", + "dev": true, + "license": "MIT", + "dependencies": { + "jazz-midi": "^1.7.9" + } + }, + "node_modules/lru-cache": { + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", + "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "license": "BlueOak-1.0.0", + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, + "node_modules/pako": { + "version": "3.0.2", + "resolved": "https://registry.npmjs.org/pako/-/pako-3.0.2.tgz", + "integrity": "sha512-uBv6IT2aT1A78iU6dpNEbf6+CyhlV/6g9JlJs9kpgjFGFhruIICVRysF/W0SLzXg5+hCl+KroH7e4YUyfEmgLg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "license": "(MIT AND Zlib)" + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/ramda": { + "version": "0.32.0", + "resolved": "https://registry.npmjs.org/ramda/-/ramda-0.32.0.tgz", + "integrity": "sha512-GQWAHhxhxWBWA8oIBr1XahFVjQ9Fic6MK9ikijfd4TZHfE2+urfk+irVlR5VOn48uwMgM+loRRBJd6Yjsbc0zQ==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/ramda" + } + }, + "node_modules/remove-trailing-separator": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/remove-trailing-separator/-/remove-trailing-separator-1.1.0.tgz", + "integrity": "sha512-/hS+Y0u3aOfIETiaiirUFwDBDzmXPvO+jAfKTitUngIPzdKc6Z0LoFjM/CK5PL4C+eKwHohlHAb6H0VFfmmUsw==", + "dev": true, + "license": "ISC" + }, + "node_modules/replace-ext": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/replace-ext/-/replace-ext-2.0.0.tgz", + "integrity": "sha512-UszKE5KVK6JvyD92nzMn9cDapSk6w/CaFZ96CnmDMUqH9oowfxF/ZjRITD25H4DnOQClLA4/j7jLGXXLVKxAug==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 10" + } + }, + "node_modules/rimraf": { + "version": "6.1.3", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.1.3.tgz", + "integrity": "sha512-LKg+Cr2ZF61fkcaK1UdkH2yEBBKnYjTyWzTJT6KNPcSPaiT7HSdhtMXQuN5wkTX0Xu72KQ1l8S42rlmexS2hSA==", + "dev": true, + "license": "BlueOak-1.0.0", + "dependencies": { + "glob": "^13.0.3", + "package-json-from-dist": "^1.0.1" + }, + "bin": { + "rimraf": "dist/esm/bin.mjs" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/source-map": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.5.7.tgz", + "integrity": "sha512-LbrmJOMUSdEVxIKvdcJzQC+nQhe8FUZQTXQy6+I75skNgn3OoQ0DZA8YnFa7gp8tqtL3KPf1kmo0R5DoApeSGQ==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/standardized-audio-context": { + "version": "25.3.77", + "resolved": "https://registry.npmjs.org/standardized-audio-context/-/standardized-audio-context-25.3.77.tgz", + "integrity": "sha512-Ki9zNz6pKcC5Pi+QPjPyVsD9GwJIJWgryji0XL9cAJXMGyn+dPOf6Qik1AHei0+UNVcc4BOCa0hWLBzlwqsW/A==", + "dev": true, + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.25.6", + "automation-events": "^7.0.9", + "tslib": "^2.7.0" + } + }, + "node_modules/streamx": { + "version": "2.28.1", + "resolved": "https://registry.npmjs.org/streamx/-/streamx-2.28.1.tgz", + "integrity": "sha512-zEzXb0s5Cds7tqMH6rhZ05lcJydCWiQPEwiNngVqzsxCc962vLY4Uw+mW7od8kDH258k2Uz/JrOkdIAAhSh9VA==", + "dev": true, + "license": "MIT", + "dependencies": { + "events-universal": "^1.0.0", + "fast-fifo": "^1.3.2", + "text-decoder": "^1.1.0" + } + }, + "node_modules/teex": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/teex/-/teex-1.0.1.tgz", + "integrity": "sha512-eYE6iEI62Ni1H8oIa7KlDU6uQBtqr4Eajni3wX7rpfXD8ysFx8z0+dri+KWEPWpBsxXfxu58x/0jvTVT1ekOSg==", + "dev": true, + "license": "MIT", + "dependencies": { + "streamx": "^2.12.5" + } + }, + "node_modules/text-decoder": { + "version": "1.2.7", + "resolved": "https://registry.npmjs.org/text-decoder/-/text-decoder-1.2.7.tgz", + "integrity": "sha512-vlLytXkeP4xvEq2otHeJfSQIRyWxo/oZGEbXrtEEF9Hnmrdly59sUbzZ/QgyWuLYHctCHxFF4tRQZNQ9k60ExQ==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "b4a": "^1.6.4" + } + }, + "node_modules/text-encoding-shim": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/text-encoding-shim/-/text-encoding-shim-1.0.5.tgz", + "integrity": "sha512-H7yYW+jRn4yhu60ygZ2f/eMhXPITRt4QSUTKzLm+eCaDsdX8avmgWpmtmHAzesjBVUTAypz9odu5RKUjX5HNYA==", + "dev": true, + "license": "MIT" + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true, + "license": "0BSD" + }, + "node_modules/unmute-ios-audio": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/unmute-ios-audio/-/unmute-ios-audio-3.3.0.tgz", + "integrity": "sha512-MmoCOrsS2gn3wLT2tT+hF56Q4V4kksIKn2LHrwAtX6umzQwQHDWSh1slMzH+0WuxTZ62s3w8/wsfIII1FQ7ACg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" + }, + "node_modules/vinyl": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/vinyl/-/vinyl-3.0.1.tgz", + "integrity": "sha512-0QwqXteBNXgnLCdWdvPQBX6FXRHtIH3VhJPTd5Lwn28tJXc34YqSCWUmkOvtJHBmB3gGoPtrOKk3Ts8/kEZ9aA==", + "dev": true, + "license": "MIT", + "dependencies": { + "clone": "^2.1.2", + "remove-trailing-separator": "^1.1.0", + "replace-ext": "^2.0.0", + "teex": "^1.0.1" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/vinyl-sourcemaps-apply": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/vinyl-sourcemaps-apply/-/vinyl-sourcemaps-apply-0.2.1.tgz", + "integrity": "sha512-+oDh3KYZBoZC8hfocrbrxbLUeaYtQK7J5WU5Br9VqWqmCll3tFJqKp97GC9GmMsVIL0qnx2DgEDVxdo5EZ5sSw==", + "dev": true, + "license": "ISC", + "dependencies": { + "source-map": "^0.5.1" + } + }, + "node_modules/web-midi-api": { + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/web-midi-api/-/web-midi-api-2.4.0.tgz", + "integrity": "sha512-tTfLdxa5LpOP1NgWByV458iYKgSLhlsIwqCpfbcJuyjProNtuf5UnX97K4JNyuQCqkR+6thQAIsk2BOMSrKaCA==", + "dev": true, + "license": "MIT", + "dependencies": { + "jzz": "^1.9.5" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 00000000..168a67cb --- /dev/null +++ b/package.json @@ -0,0 +1,54 @@ +{ + "name": "@csound/plugins", + "version": "7.0.0", + "description": "Csound 7 opcode plugins for WebAssembly", + "type": "module", + "license": "LGPL-2.1-or-later", + "repository": { + "type": "git", + "url": "git+https://github.com/csound/plugins.git" + }, + "bugs": { + "url": "https://github.com/csound/plugins/issues" + }, + "homepage": "https://github.com/csound/plugins", + "publishConfig": { + "access": "public" + }, + "engines": { + "node": ">=22.13.0" + }, + "files": [ + "dist", + "index.js", + "index.d.ts", + "LICENSE", + "THIRD_PARTY.md", + "wasm/README.md" + ], + "exports": { + ".": { + "types": "./index.d.ts", + "import": "./index.js" + }, + "./dist/*": "./dist/*", + "./package.json": "./package.json" + }, + "scripts": { + "build": "bash wasm/scripts/build.sh", + "test": "node --test wasm/tests/*.test.js", + "prepack": "npm test" + }, + "devDependencies": { + "@csound/browser": "7.0.0-beta41", + "@csound/wasm-bin": "7.0.0-beta31" + }, + "keywords": [ + "csound", + "audio", + "plugins", + "wasm", + "wasi", + "webassembly" + ] +} diff --git a/src/CUDA/CMakeLists.txt b/src/CUDA/CMakeLists.txt new file mode 100644 index 00000000..ad06da78 --- /dev/null +++ b/src/CUDA/CMakeLists.txt @@ -0,0 +1,63 @@ +# Build the CUDA GPU opcodes (requires the CUDA toolkit). +# Not available on macOS: NVIDIA dropped CUDA support after macOS 10.13. + +option(BUILD_CUDA_OPCODES "Build the CUDA GPU opcodes" OFF) + +if(BUILD_CUDA_OPCODES) + if(APPLE) + message(STATUS "CUDA is not supported on macOS; disabling BUILD_CUDA_OPCODES") + set(BUILD_CUDA_OPCODES OFF) + elseif(CMAKE_VERSION VERSION_LESS 3.18) + message(STATUS "CUDA requires CMake 3.18 or later; disabling BUILD_CUDA_OPCODES") + set(BUILD_CUDA_OPCODES OFF) + else() + include(CheckLanguage) + check_language(CUDA) + if(CMAKE_CUDA_COMPILER) + enable_language(CUDA) + find_package(CUDAToolkit) + endif() + check_deps(BUILD_CUDA_OPCODES CMAKE_CUDA_COMPILER CUDAToolkit_FOUND) + endif() +endif() + +if(BUILD_CUDA_OPCODES) + message(STATUS "Building the CUDA opcodes") + + # A broad set of compute capabilities; override with + # -DCMAKE_CUDA_ARCHITECTURES="..." (e.g. "native"). + if(NOT CMAKE_CUDA_ARCHITECTURES) + set(CMAKE_CUDA_ARCHITECTURES 75 80 86 89) + endif() + set(CMAKE_CUDA_STANDARD 14) + set(CMAKE_CUDA_STANDARD_REQUIRED ON) + + function(make_cuda_plugin libname srcs) + make_plugin(${libname} ${srcs}) + target_include_directories(${libname} PRIVATE ${CSOUND_INCLUDE_DIRS}) + target_compile_options(${libname} PRIVATE + "$<$:-use_fast_math>") + endfunction() + + # cudaop1: cudasynth (additive synthesis driven by a PVS signal) + make_cuda_plugin(cudaop1 adsyn.cu) + target_link_libraries(cudaop1 PRIVATE CUDA::cudart) + + # cudaop2: cudasynth, cudanal (PVS analysis/synthesis) + make_cuda_plugin(cudaop2 pvsops.cu) + target_link_libraries(cudaop2 PRIVATE CUDA::cudart CUDA::cufft) + + # cudaop3: cudasliding (sliding DFT) + make_cuda_plugin(cudaop3 slidingm.cu) + target_link_libraries(cudaop3 PRIVATE CUDA::cudart) + + # cudaop4: cudaconv (direct convolution) + make_cuda_plugin(cudaop4 conv.cu) + target_link_libraries(cudaop4 PRIVATE CUDA::cudart) + + # cudaop5: cudapconv (partitioned convolution) + make_cuda_plugin(cudaop5 pconv.cu) + target_link_libraries(cudaop5 PRIVATE CUDA::cudart CUDA::cufft) +else() + message(STATUS "Not building the CUDA opcodes") +endif() diff --git a/6.x/src/CUDA/adsyn.cu b/src/CUDA/adsyn.cu similarity index 80% rename from 6.x/src/CUDA/adsyn.cu rename to src/CUDA/adsyn.cu index 13063478..8ab3f980 100644 --- a/6.x/src/CUDA/adsyn.cu +++ b/src/CUDA/adsyn.cu @@ -27,12 +27,14 @@ #include #include +#define S(x) sizeof(x) + typedef struct cudadsyn_ { OPDS h; - MYFLT *asig; + cs_float *asig; PVSDAT *fsig; - MYFLT *kamp, *kfreq; - MYFLT *inum; + cs_float *kamp, *kfreq; + cs_float *inum; float *out; float *frame; int64_t *ndx; @@ -64,13 +66,12 @@ static int init_cudadsyn(CSOUND *csound, CUDADSYN *p){ if(*p->inum > 0 && *p->inum < p->bins) p->bins = *p->inum; p->vsamps = p->fsig->overlap; + /* per-block/launch sizes, rounded up to cover all work items */ p->threads = p->bins*p->vsamps; - p->blocks = p->threads > blockspt ? p->threads/blockspt : 1; - p->mthreads = p->bins; - p->mblocks = p->mthreads > blockspt ? p->mthreads/blockspt : 1; - - p->threads /= p->blocks; - p->mthreads /= p->mblocks; + p->threads = p->threads < blockspt ? p->threads : blockspt; + p->blocks = (p->bins*p->vsamps + p->threads - 1)/p->threads; + p->mthreads = p->bins < blockspt ? p->bins : blockspt; + p->mblocks = (p->bins + p->mthreads - 1)/p->mthreads; asize = p->vsamps*sizeof(float); ipsize = p->fsig->N*sizeof(int64_t)/2; @@ -79,6 +80,7 @@ static int init_cudadsyn(CSOUND *csound, CUDADSYN *p){ cudaMalloc(&p->out, asize); cudaMalloc(&p->ndx, ipsize); cudaMalloc(&p->frame, fpsize); + cudaMemset(p->frame, 0, fpsize); cudaMalloc(&p->previous, fpsize/2); cudaMemset(p->previous, 0, fpsize/2); cudaMemset(p->ndx, 0, ipsize); @@ -89,17 +91,17 @@ static int init_cudadsyn(CSOUND *csound, CUDADSYN *p){ p->out_.size < asize) csound->AuxAlloc(csound, asize , &p->out_); - csound->RegisterDeinitCallback(csound, p, destroy_cudadsyn); p->count = 0; return OK; } __global__ void sample(float *out, float *frame, float pitch, int64_t *ph, - float *amps, int vsize, float sr) { + float *amps, int vsize, float sr, int bins) { int t = (threadIdx.x + blockIdx.x*blockDim.x); int n = t%vsize; /* sample index */ int h = t/vsize; /* bin index */ + if (h >= bins) return; //if(amps[h] < 0.001) return; int k = h<<1; int64_t lph; @@ -114,9 +116,10 @@ __global__ void sample(float *out, float *frame, float pitch, int64_t *ph, } __global__ void update(float *frame, float *amps, - int64_t *ph,float pitch, int vsize, float sr){ + int64_t *ph,float pitch, int vsize, float sr, int bins){ int h = threadIdx.x + blockIdx.x*blockDim.x; + if (h >= bins) return; int k = h << 1; /* update phases and amps */ ph[h] = (ph[h] + (int64_t)(vsize*round(pitch*frame[k+1]*FMAXLEN/sr))) & PHMASK; @@ -129,14 +132,14 @@ static int perf_cudadsyn(CSOUND *csound, CUDADSYN *p){ uint32_t early = p->h.insdshead->ksmps_no_end; uint32_t n, nsmps = CS_KSMPS; float *out_ = (float *) p->out_.auxp; - MYFLT *asig = p->asig; + cs_float *asig = p->asig; int count = p->count, vsamps = p->vsamps; p->fp = (float *) (p->fsig->frame.auxp); - if (UNLIKELY(offset)) memset(asig, '\0', offset*sizeof(MYFLT)); + if (UNLIKELY(offset)) memset(asig, '\0', offset*sizeof(cs_float)); if (UNLIKELY(early)) { nsmps -= early; - memset(&asig[nsmps], '\0', early*sizeof(MYFLT)); + memset(&asig[nsmps], '\0', early*sizeof(cs_float)); } for(n=offset; n < nsmps; n++){ @@ -147,7 +150,8 @@ static int perf_cudadsyn(CSOUND *csound, CUDADSYN *p){ p->ndx, *p->kfreq, vsamps, - csound->GetSr(csound)); + CS_ESR, + p->bins); cudaMemset(p->out, 0, sizeof(float)*vsamps); cudaMemcpy(p->frame,p->fp,sizeof(float)*p->bins*2,cudaMemcpyHostToDevice); sample<<blocks,p->threads>>>(p->out,p->frame, @@ -155,11 +159,12 @@ static int perf_cudadsyn(CSOUND *csound, CUDADSYN *p){ p->ndx, p->previous, vsamps, - csound->GetSr(csound)); + CS_ESR, + p->bins); count = vsamps; } - asig[n] = (MYFLT) out_[vsamps - count]; + asig[n] = (cs_float) out_[vsamps - count]; count--; } p->count = count; @@ -177,8 +182,8 @@ static int destroy_cudadsyn(CSOUND *csound, void *pp){ static OENTRY localops[] = { - {"cudasynth", sizeof(CUDADSYN),0, 3, "a", "fkko", (SUBR) init_cudadsyn, - (SUBR) perf_cudadsyn} + {"cudasynth", S(CUDADSYN), 0, "a", "fkko", (SUBR) init_cudadsyn, + (SUBR) perf_cudadsyn, (SUBR) destroy_cudadsyn, NULL, 0} }; extern "C" { diff --git a/6.x/src/CUDA/conv.cu b/src/CUDA/conv.cu similarity index 65% rename from 6.x/src/CUDA/conv.cu rename to src/CUDA/conv.cu index 43be9f33..203dbd32 100644 --- a/6.x/src/CUDA/conv.cu +++ b/src/CUDA/conv.cu @@ -23,7 +23,9 @@ #include -__global__ void convol(MYFLT *out, MYFLT *del, MYFLT *coefs, int irsize, int rp, int vsize) { +#define S(x) sizeof(x) + +__global__ void convol(cs_float *out, cs_float *del, cs_float *coefs, int irsize, int rp, int vsize) { int t = (threadIdx.x + blockIdx.x*blockDim.x); if(t >= irsize*vsize) return; int n = t%vsize; /* sample index */ @@ -32,8 +34,12 @@ __global__ void convol(MYFLT *out, MYFLT *del, MYFLT *coefs, int irsize, int rp, rp += n + h; /* read point, oldest -> newest */ out[t] = del[rp < end ? rp : rp%end]*coefs[irsize-1-h]; /* single tap */ if(t >= vsize) return; + /* FIXME: __syncthreads() only synchronizes threads within a block, but + the taps below are then mixed across blocks, which is incorrect (this + also skips the threads that returned above). Needs a two-stage scheme: + one kernel to compute every tap and a second to reduce them. */ __syncthreads(); - MYFLT a = 0.0; + cs_float a = 0.0; for(int i=1, j=vsize; i < irsize; i++, j+=vsize) a += out[n + j]; /* mix all taps */ out[n] += a; @@ -53,21 +59,21 @@ __global__ void convol(MYFLT *out, MYFLT *del, MYFLT *coefs, int irsize, int rp, // return __longlong_as_double(old); // } -// __global__ void convol2(MYFLT *out, MYFLT *del, MYFLT *coefs, int irsize, int rp, int vsize) { +// __global__ void convol2(cs_float *out, cs_float *del, cs_float *coefs, int irsize, int rp, int vsize) { // int t = (threadIdx.x + blockIdx.x*blockDim.x); // if(t >= irsize*vsize) return; // int n = t%vsize; /* sample index */ // int h = t/vsize; /* coeff index */ // int end = irsize+vsize; // rp += n + h; /* read point, oldest -> newest */ -// MYFLT s = del[rp < end ? rp : rp%end]*coefs[irsize-1-h]; /* single tap */ +// cs_float s = del[rp < end ? rp : rp%end]*coefs[irsize-1-h]; /* single tap */ // t == n ? out[n] = s : atomicAdd(&out[n], s); // } typedef struct _CONV { OPDS h; - MYFLT *aout, *asig, *ifn; - MYFLT *coeffs, *out, *del; + cs_float *aout, *asig, *ifn; + cs_float *coeffs, *out, *del; int wp, irsize; int blocks, threads; } CONV; @@ -83,19 +89,22 @@ static int destroy_conv(CSOUND *csound, void *pp){ static int conv_init(CSOUND *csound, CONV *p){ - FUNC *ftab = csound->FTnp2Find(csound, p->ifn); + FUNC *ftab = csound->FTFind(csound, p->ifn); + if (ftab == NULL) + return csound->InitError(csound, "could not find table %d\n", + (int) *p->ifn); int irsize = ftab->flen; int nsmps = CS_KSMPS; int threads = irsize*nsmps; - cudaMalloc(&p->coeffs, sizeof(MYFLT)*irsize); - cudaMemcpy(p->coeffs, ftab->ftable, sizeof(MYFLT)*irsize, + cudaMalloc(&p->coeffs, sizeof(cs_float)*irsize); + cudaMemcpy(p->coeffs, ftab->ftable, sizeof(cs_float)*irsize, cudaMemcpyHostToDevice); - cudaMalloc(&p->del, sizeof(MYFLT)*(irsize+nsmps)); - cudaMalloc(&p->out, sizeof(MYFLT)*threads); - cudaMemset(p->del,0,sizeof(MYFLT)*(irsize+nsmps)); - cudaMemset(p->out, 0, sizeof(MYFLT)*threads); + cudaMalloc(&p->del, sizeof(cs_float)*(irsize+nsmps)); + cudaMalloc(&p->out, sizeof(cs_float)*threads); + cudaMemset(p->del,0,sizeof(cs_float)*(irsize+nsmps)); + cudaMemset(p->out, 0, sizeof(cs_float)*threads); p->wp = 0; p->irsize = irsize; @@ -106,13 +115,10 @@ static int conv_init(CSOUND *csound, CONV *p){ csound->Message(csound, "CUDAconv: using device %s (capability %d.%d)\n", deviceProp.name,deviceProp.major, deviceProp.minor); - p->blocks = threads > blockspt ? ceil(threads/blockspt) : 1; + p->blocks = (threads + blockspt - 1)/blockspt; p->threads = threads > blockspt ? blockspt : threads; - csound->RegisterDeinitCallback(csound, p, destroy_conv); - OPARMS parms; - csound->GetOParms(csound, &parms); - if(parms.odebug) + if(csound->GetDebug(csound)) csound->Message(csound, "blocks %d, threads %d - %d\n", p->blocks, p->threads, threads); return OK; @@ -123,29 +129,29 @@ static int conv_init(CSOUND *csound, CONV *p){ int conv_perf(CSOUND *csound, CONV *p){ int nsmps = CS_KSMPS; - MYFLT *sig = p->asig, *aout = p->aout; - MYFLT *del = p->del, *out = p->out, *coefs = p->coeffs; + cs_float *sig = p->asig, *aout = p->aout; + cs_float *del = p->del, *out = p->out, *coefs = p->coeffs; int irsize = p->irsize; int wp = p->wp; if(wp > irsize) { int front = wp - irsize; - cudaMemcpy(&del[wp], sig, sizeof(MYFLT)*(nsmps-front), cudaMemcpyHostToDevice); - cudaMemcpy(del, &sig[nsmps-front], sizeof(MYFLT)*front, cudaMemcpyHostToDevice); + cudaMemcpy(&del[wp], sig, sizeof(cs_float)*(nsmps-front), cudaMemcpyHostToDevice); + cudaMemcpy(del, &sig[nsmps-front], sizeof(cs_float)*front, cudaMemcpyHostToDevice); } - else cudaMemcpy(&del[wp], sig, sizeof(MYFLT)*nsmps, cudaMemcpyHostToDevice); + else cudaMemcpy(&del[wp], sig, sizeof(cs_float)*nsmps, cudaMemcpyHostToDevice); wp = (wp+nsmps)%(irsize+nsmps); /* wp is now the oldest sample in the delay */ convol<<blocks,p->threads>>>(out, del, coefs, irsize, wp, nsmps); - cudaMemcpy(aout, out, sizeof(MYFLT)*nsmps, cudaMemcpyDeviceToHost); + cudaMemcpy(aout, out, sizeof(cs_float)*nsmps, cudaMemcpyDeviceToHost); p->wp = wp; return OK; } static OENTRY localops[] = { - {"cudaconv", sizeof(CONV),0, 5, "a", "ai", (SUBR) conv_init, NULL, - (SUBR) conv_perf}, + {"cudaconv", S(CONV), 0, "a", "ai", (SUBR) conv_init, + (SUBR) conv_perf, (SUBR) destroy_conv, NULL, 0}, }; extern "C" { diff --git a/6.x/src/CUDA/pconv.cu b/src/CUDA/pconv.cu similarity index 89% rename from 6.x/src/CUDA/pconv.cu rename to src/CUDA/pconv.cu index 93f72e55..5c49ba8e 100644 --- a/6.x/src/CUDA/pconv.cu +++ b/src/CUDA/pconv.cu @@ -22,6 +22,8 @@ #include #include +#define S(x) sizeof(x) + /* each kernel processes one bin */ @@ -48,13 +50,14 @@ __global__ void pconvol(float *out,float *in, /* complex multiplication + sums */ atomicAdd(&out[n], re*re2 - im*im2); - (n || n != dftsize) ? atomicAdd(&out[n+1], re*im2 + re2*im) : im; + if (n && n != dftsize) atomicAdd(&out[n+1], re*im2 + re2*im); } /* sample-by-sample overlap-add operation */ __global__ void olapadd(float *buf, float *in, int parts){ int n = (threadIdx.x + blockIdx.x*blockDim.x); + if(n >= parts) return; buf[n] = in[n] + buf[parts+n]; buf[parts+n] = in[parts+n]; } @@ -63,7 +66,7 @@ __global__ void olapadd(float *buf, float *in, int parts){ typedef struct _pconv{ OPDS h; - MYFLT *aout, *asig, *ifn, *parts; + cs_float *aout, *asig, *ifn, *parts; float *out, *coef, *in, *buf; AUXCH bufin, bufout; int wp, nparts, dftsize, cnt; @@ -92,11 +95,14 @@ static int destroy_pconv(CSOUND *csound, void *pp){ int pconv_init(CSOUND *csound, PCONV *p){ - FUNC *ftab = csound->FTnp2Find(csound, p->ifn); + FUNC *ftab = csound->FTFind(csound, p->ifn); float *tmp; + if (ftab == NULL) + return csound->InitError(csound, "could not find table %d\n", + (int) *p->ifn); int tlen = ftab->flen; int end, i, j, k, parts = *p->parts, dftsize, nparts; - MYFLT *tab = ftab->ftable; + cs_float *tab = ftab->ftable; if(!isPowerOfTwo(parts)) return csound->InitError(csound, "partition size needs to be power of two\n"); @@ -159,16 +165,12 @@ int pconv_init(CSOUND *csound, PCONV *p){ end >>= 1; - p->blocks = end > blockspt ? ceil(end/blockspt) : 1; + p->blocks = (end + blockspt - 1)/blockspt; p->threads = end > blockspt ? blockspt : end; - p->oblocks = parts > blockspt ? ceil(parts/blockspt) : 1; + p->oblocks = (parts + blockspt - 1)/blockspt; p->othreads = parts > blockspt ? blockspt : parts; - csound->RegisterDeinitCallback(csound, p, destroy_pconv); - - OPARMS parms; - csound->GetOParms(csound, &parms); - if(parms.odebug) + if(csound->GetDebug(csound)) csound->Message(csound, "blocks %d - threads/block %d - threads %d - dftsize %d - parts %d\n", p->blocks, p->threads, end, dftsize, nparts); @@ -187,21 +189,21 @@ int pconv_perf(CSOUND *csound, PCONV *p){ uint32_t early = p->h.insdshead->ksmps_no_end; uint32_t n, nsmps = CS_KSMPS; float *bufin = (float *) p->bufin.auxp, *bufout = (float *) p->bufout.auxp; - MYFLT *asig = p->asig, *aout = p->aout; + cs_float *asig = p->asig, *aout = p->aout; float *in = p->in, *out = p->out, *coef = p->coef, *buf = p->buf; int end = nparts*(dftsize+2); int parts = *p->parts; - if (UNLIKELY(offset)) memset(asig, '\0', offset*sizeof(MYFLT)); + if (UNLIKELY(offset)) memset(aout, '\0', offset*sizeof(cs_float)); if (UNLIKELY(early)) { nsmps -= early; - memset(&asig[nsmps], '\0', early*sizeof(MYFLT)); + memset(&aout[nsmps], '\0', early*sizeof(cs_float)); } //csound->Message(csound,"pconv perf count: %d\n", cnt); for(n = offset; n < nsmps; n++){ bufin[cnt] = (float) asig[n]; - aout[n] = (MYFLT) bufout[cnt]/dftsize; + aout[n] = (cs_float) bufout[cnt]/dftsize; if(++cnt == parts){ //csound->Message(csound,"CUDA execution:\n"); @@ -254,8 +256,8 @@ int pconv_perf(CSOUND *csound, PCONV *p){ } static OENTRY localops[] = { - {"cudapconv", sizeof(PCONV),0, 3, "a", "aii", (SUBR) pconv_init, - (SUBR) pconv_perf}, + {"cudapconv", S(PCONV), 0, "a", "aii", (SUBR) pconv_init, + (SUBR) pconv_perf, (SUBR) destroy_pconv, NULL, 0}, }; extern "C" { diff --git a/6.x/src/CUDA/pvsops.cu b/src/CUDA/pvsops.cu similarity index 93% rename from 6.x/src/CUDA/pvsops.cu rename to src/CUDA/pvsops.cu index df3268de..53c3b6c5 100644 --- a/6.x/src/CUDA/pvsops.cu +++ b/src/CUDA/pvsops.cu @@ -29,6 +29,8 @@ #include +#define S(x) sizeof(x) + /* kernel to convert from pvs to rectangular frame */ __global__ void frompvs(float* inframe, double* lastph, double scal, double fac) { @@ -51,7 +53,7 @@ __global__ void winrotate(float* inframe2, float* inframe, float *win, typedef struct _pvsyn{ OPDS h; - MYFLT *asig; + cs_float *asig; PVSDAT *fsig; float *inframe; /* N */ float *inframe2; @@ -76,7 +78,7 @@ static int pvsynset(CSOUND *csound, PVSYN *p){ if((N != 0) && !(N & (N - 1))) { int hsize = p->fsig->overlap; int size, numframes, i, blockspt; - MYFLT sum = 0.0, bins = N/2; + cs_float sum = 0.0, bins = N/2; float *win; cudaDeviceProp deviceProp; cudaGetDeviceProperties(&deviceProp, 0); @@ -133,14 +135,12 @@ static int pvsynset(CSOUND *csound, PVSYN *p){ p->framecount = 0; p->curframe = 0; - p->fac = TWOPI*hsize/csound->GetSr(csound); - p->scal =csound->GetSr(csound)/N; + p->fac = TWOPI*hsize/CS_ESR; + p->scal =CS_ESR/N; cufftPlan1d(&p->plan, N, CUFFT_C2R, 1); #if __CUDACC_VER_MAJOR__ < 8 cufftSetCompatibilityMode(p->plan, CUFFT_COMPATIBILITY_NATIVE); #endif - csound->RegisterDeinitCallback(csound, p, destroy_pvsyn); - p->bblocks = bins > blockspt? bins/blockspt : 1; p->nblocks = N > blockspt ? N/blockspt : 1; p->bthreads = bins/p->bblocks; @@ -161,16 +161,16 @@ static int pvsynperf(CSOUND *csound, PVSYN *p){ uint32_t offset = p->h.insdshead->ksmps_offset; uint32_t early = p->h.insdshead->ksmps_no_end; uint32_t n, nsmps = CS_KSMPS; - MYFLT *asig = p->asig; + cs_float *asig = p->asig; float *frames = (float *) p->frames.auxp; int framecount = p->framecount; int numframes = N/hsize; int *count = (int *) p->count.auxp; - if (UNLIKELY(offset)) memset(asig, '\0', offset*sizeof(MYFLT)); + if (UNLIKELY(offset)) memset(asig, '\0', offset*sizeof(cs_float)); if (UNLIKELY(early)) { nsmps -= early; - memset(&asig[nsmps], '\0', early*sizeof(MYFLT)); + memset(&asig[nsmps], '\0', early*sizeof(cs_float)); } for(n=offset; n < nsmps; n++) { @@ -258,7 +258,7 @@ __global__ void rotatewin(float* aframe2, float *aframe, float *win, typedef struct _pvan { OPDS h; PVSDAT *fsig; - MYFLT *asig,*fftsize,*hsize,*winsize,*wintype; + cs_float *asig,*fftsize,*hsize,*winsize,*wintype; float *aframe; /* N */ float *aframe2; @@ -284,6 +284,8 @@ static int pvanalset(CSOUND *csound, PVAN *p){ int size, numframes, i, bins = N/2; int hsize = *p->hsize, blockspt; float *win; + if(hsize <= 0 || hsize > N || (N % hsize) != 0) + return csound->InitError(csound, "CUDAnal: invalid hopsize\n"); cudaDeviceProp deviceProp; cudaGetDeviceProperties(&deviceProp, 0); blockspt = deviceProp.maxThreadsPerBlock; @@ -343,14 +345,12 @@ static int pvanalset(CSOUND *csound, PVAN *p){ p->framecount = 1; p->curframe = numframes-1; - p->fac = csound->GetSr(csound)/(TWOPI*hsize); + p->fac = CS_ESR/(TWOPI*hsize); p->scal = (TWOPI*hsize)/N; cufftPlan1d(&p->plan, N, CUFFT_R2C, 1); #if __CUDACC_VER_MAJOR__ < 8 cufftSetCompatibilityMode(p->plan, CUFFT_COMPATIBILITY_NATIVE); #endif - csound->RegisterDeinitCallback(csound, p, destroy_pvanal); - p->bblocks = bins > blockspt? bins/blockspt : 1; p->nblocks = N > blockspt ? N/blockspt : 1; p->bthreads = bins/p->bblocks; @@ -370,16 +370,16 @@ static int pvanalperf(CSOUND *csound, PVAN *p){ uint32_t offset = p->h.insdshead->ksmps_offset; uint32_t early = p->h.insdshead->ksmps_no_end; uint32_t n, nsmps = CS_KSMPS; - MYFLT *asig = p->asig; + cs_float *asig = p->asig; float *frames = (float *) p->frames.auxp; int framecount = p->framecount; int numframes = N/hsize; int *count = (int *) p->count.auxp; - if (UNLIKELY(offset)) memset(asig, '\0', offset*sizeof(MYFLT)); + if (UNLIKELY(offset)) memset(asig, '\0', offset*sizeof(cs_float)); if (UNLIKELY(early)) { nsmps -= early; - memset(&asig[nsmps], '\0', early*sizeof(MYFLT)); + memset(&asig[nsmps], '\0', early*sizeof(cs_float)); } for(n=offset; n < nsmps; n++) { @@ -437,10 +437,10 @@ static int destroy_pvanal(CSOUND *csound, void *pp){ } static OENTRY localops[] = { - {"cudasynth", sizeof(PVSYN),0, 3, "a", "f", (SUBR) pvsynset, - (SUBR) pvsynperf}, - {"cudanal", sizeof(PVAN),0, 3, "f", "aiiii", (SUBR) pvanalset, - (SUBR) pvanalperf} + {"cudasynth", S(PVSYN), 0, "a", "f", (SUBR) pvsynset, + (SUBR) pvsynperf, (SUBR) destroy_pvsyn, NULL, 0}, + {"cudanal", S(PVAN), 0, "f", "aiiii", (SUBR) pvanalset, + (SUBR) pvanalperf, (SUBR) destroy_pvanal, NULL, 0} }; extern "C" { diff --git a/6.x/src/CUDA/slidingm.cu b/src/CUDA/slidingm.cu similarity index 93% rename from 6.x/src/CUDA/slidingm.cu rename to src/CUDA/slidingm.cu index 69d662cd..72961d51 100644 --- a/6.x/src/CUDA/slidingm.cu +++ b/src/CUDA/slidingm.cu @@ -40,6 +40,8 @@ // #include #include +#define S(x) sizeof(x) + //#define RB #ifndef RB @@ -77,6 +79,10 @@ __constant__ real binbandwidth, nyquist; __constant__ int offset; // process in batches of this size __constant__ int nbatch; +/* FIXME: __constant__ memory is module-global, so these values clash when + more than one instance of the opcode is active, and the symbol copy + fails for some sizes (the declaration can be optimised out when unused). + These per-instance values should be passed as kernel arguments instead. */ #define CUDA(name, args ...) { name(args); checkcuda(csound, #name, __LINE__); } @@ -89,6 +95,9 @@ void checkcuda(CSOUND *csound, char* name, int line) if (err) { csound->Message(csound, "%s: %s\nLine: %d\n", name, cudaGetErrorString(err), line); + /* FIXME: this aborts the whole host process on any CUDA error; it + should propagate the error through the CUDA()/CHK() macros (and out + of init/perf) instead, which touches every call site. */ exit(1); } } @@ -375,7 +384,7 @@ void threadblock(int device, int n, int *nblocks, int *nthreads) typedef struct _SPV { OPDS h; - MYFLT *out, *in, *shift, *iN; + cs_float *out, *in, *shift, *iN; int nbins, ptr, nbtch, nblocks, nblocks1, nthreads, nthreads1, rblocks, rthreads; real *deltas, *ddeltas, *dinphase, *doutphase, *dfm; real *sine, *cosine; @@ -422,7 +431,7 @@ void cuinit(CSOUND *csound, p->nbins = nbins; nbins2 = nbins/2; p->nbtch = nframes; - p->srate = (real) csound->GetSr(csound); + p->srate = (real) CS_ESR; binbw = p->srate/p->nbins; nyq = p->srate/real(2.0); @@ -451,6 +460,8 @@ void cuinit(CSOUND *csound, deviceProp.major, deviceProp.minor); #endif // global constants + /* FIXME: see the note on the __constant__ declarations above; these are + module-global and only copied on the first init. */ CUDA(cudaMemcpyToSymbol, offset, &off, sizeof(int)); CUDA(cudaMemcpyToSymbol, nbatch, &p->nbtch, sizeof(int)); CUDA(cudaMemcpyToSymbol, binbandwidth, &binbw, sizeof(real)); @@ -532,6 +543,7 @@ int cushutdown(CSOUND *csound, void *pp) CUDA(cudaFree, p->dinphase); CUDA(cudaFree, p->doutphase); CUDA(cudaFree, p->ddeltas); + CUDA(cudaFree, p->dfm); CUDA(cudaFree, p->dFoutw); CUDA(cudaFree, p->dFout); CUDA(cudaFree, p->dFin); @@ -579,6 +591,9 @@ void cuprocess(CSOUND *csound, SPV *p, real in[/*nbatch*/], real out[/*nbatch*/] fmsyn<<nblocks,p->nthreads>>>(p->dinphase, p->dFout, p->dfm, p->doutphase, p->nbins); // reuse ddeltas array +#ifndef RB + CUDA(cudaMemset, p->ddeltas, 0, p->nbtch*sizeof(real)); +#endif reconstruct<<rblocks,p->rthreads>>>(p->dFout, p->ddeltas, p->nbins); CHK(reconstruct); // is is faster to transfer to pinned memory then copy; or just @@ -594,8 +609,11 @@ void cuprocess(CSOUND *csound, SPV *p, real in[/*nbatch*/], real out[/*nbatch*/] int spv_init(CSOUND *csound, SPV *p) { - cuinit(csound,p,NBATCH,*p->iN,0); - csound->RegisterDeinitCallback(csound, p, cushutdown); + int nbins = (int) *p->iN; + if (nbins < 2) + return csound->InitError(csound, "cudasliding: invalid size %d\n", nbins); + p->count = 0; + cuinit(csound,p,NBATCH,nbins,0); return OK; } @@ -605,10 +623,10 @@ int spv_perf(CSOUND *csound, SPV *p) { uint32_t n, nsmps = CS_KSMPS; uint32_t count = p->count; - if (UNLIKELY(offset)) memset(p->out, '\0', offset*sizeof(MYFLT)); + if (UNLIKELY(offset)) memset(p->out, '\0', offset*sizeof(cs_float)); if (UNLIKELY(early)) { nsmps -= early; - memset(&p->out[nsmps], '\0', early*sizeof(MYFLT)); + memset(&p->out[nsmps], '\0', early*sizeof(cs_float)); } for(n=offset; n < nsmps; n++) { @@ -628,8 +646,8 @@ int spv_perf(CSOUND *csound, SPV *p) { } static OENTRY localops[] = { - {"cudasliding", sizeof(SPV),0, 5, "a", "aai", (SUBR) spv_init, NULL, - (SUBR) spv_perf} + {"cudasliding", S(SPV), 0, "a", "aai", (SUBR) spv_init, + (SUBR) spv_perf, (SUBR) cushutdown, NULL, 0} }; extern "C" { diff --git a/src/OpenCL/CMakeLists.txt b/src/OpenCL/CMakeLists.txt new file mode 100644 index 00000000..1244184e --- /dev/null +++ b/src/OpenCL/CMakeLists.txt @@ -0,0 +1,24 @@ +# Build the OpenCL opcodes. +# +# The OpenCL headers and ICD loader come from the system framework on macOS, +# from vcpkg on Windows, and from ocl-icd-opencl-dev on Linux. +option(BUILD_OPENCL_OPCODES "Build the OpenCL opcodes" ON) + +if(APPLE OR NOT USE_VCPKG) + find_package(OpenCL) +else() + find_package(OpenCL CONFIG) +endif() + +check_deps(BUILD_OPENCL_OPCODES OpenCL_FOUND) + +if(BUILD_OPENCL_OPCODES AND OpenCL_FOUND) + make_plugin(clop cladsynth.c) + target_include_directories(clop PRIVATE ${CSOUND_INCLUDE_DIRS}) + if(TARGET OpenCL::OpenCL) + target_link_libraries(clop PRIVATE OpenCL::OpenCL) + else() + target_include_directories(clop PRIVATE ${OpenCL_INCLUDE_DIRS}) + target_link_libraries(clop PRIVATE ${OpenCL_LIBRARIES}) + endif() +endif() diff --git a/6.x/src/opencl/cladsynth.c b/src/OpenCL/cladsynth.c similarity index 96% rename from 6.x/src/opencl/cladsynth.c rename to src/OpenCL/cladsynth.c index ab76728d..abc0c0fa 100644 --- a/6.x/src/opencl/cladsynth.c +++ b/src/OpenCL/cladsynth.c @@ -29,6 +29,7 @@ #include #endif +#define S(x) sizeof(x) const char *code = "#define FMAXLEN ((float)0x40000000) \n" @@ -79,10 +80,10 @@ const char *code = typedef struct cladsyn_ { OPDS h; - MYFLT *asig; + cs_float *asig; PVSDAT *fsig; - MYFLT *kamp, *kfreq; - MYFLT *inum, *idev; + cs_float *kamp, *kfreq; + cs_float *inum, *idev; cl_mem out; cl_mem frame; cl_mem ph; @@ -151,7 +152,7 @@ char * cl_error_string(int err) { case CL_INVALID_EVENT_WAIT_LIST: return "Invalid event wait list"; case CL_INVALID_EVENT: return "Invalid event"; case CL_INVALID_OPERATION: return "Invalid operation"; - case CL_INVALID_GL_OBJECT: return "Invalid OpenGL object"; + case CL_INVALID_GL_OBJECT: return "Invalid GL object"; case CL_INVALID_BUFFER_SIZE: return "Invalid buffer size"; case CL_INVALID_MIP_LEVEL: return "Invalid mip-map level"; default: return "Unknown error"; @@ -288,7 +289,6 @@ static int init_cladsyn(CSOUND *csound, CLADSYN *p){ p->out_.size < (unsigned long) asize) csound->AuxAlloc(csound, asize , &p->out_); - csound->RegisterDeinitCallback(csound, p, destroy_cladsyn); p->count = 0; p->context = context; p->program = program; @@ -301,7 +301,7 @@ static int init_cladsyn(CSOUND *csound, CLADSYN *p){ clGetKernelWorkGroupInfo(p->kernel2, device_id, CL_KERNEL_WORK_GROUP_SIZE, sizeof(p->wgs2), &p->wgs2, NULL); - p->sr = csound->GetSr(csound); + p->sr = (float) CS_ESR; clSetKernelArg(p->kernel1, 0, sizeof(cl_mem), &p->out); clSetKernelArg(p->kernel1, 1, sizeof(cl_mem), &p->frame); clSetKernelArg(p->kernel1, 2, sizeof(cl_mem), &p->ph); @@ -327,14 +327,14 @@ static int perf_cladsyn(CSOUND *csound, CLADSYN *p){ uint32_t early = p->h.insdshead->ksmps_no_end; uint32_t n, nsmps = CS_KSMPS; float *out_ = (float *) p->out_.auxp; - MYFLT *asig = p->asig; + cs_float *asig = p->asig; int count = p->count, vsamps = p->vsamps; p->fp = (float *) (p->fsig->frame.auxp); - if (UNLIKELY(offset)) memset(asig, '\0', offset*sizeof(MYFLT)); + if (UNLIKELY(offset)) memset(asig, '\0', offset*sizeof(cs_float)); if (UNLIKELY(early)) { nsmps -= early; - memset(&asig[nsmps], '\0', early*sizeof(MYFLT)); + memset(&asig[nsmps], '\0', early*sizeof(cs_float)); } for(n=offset; n < nsmps; n++){ @@ -363,7 +363,7 @@ static int perf_cladsyn(CSOUND *csound, CLADSYN *p){ cl_error_string(err)); count = vsamps; } - asig[n] = (MYFLT) out_[vsamps - count]; + asig[n] = (cs_float) out_[vsamps - count]; count--; } p->count = count; @@ -386,8 +386,8 @@ static int destroy_cladsyn(CSOUND *csound, void *pp){ } static OENTRY localops[] = { - {"cladsyn", sizeof(CLADSYN),0, 3, "a", "fkkoo", (SUBR) init_cladsyn, - (SUBR) perf_cladsyn} + {"cladsyn", S(CLADSYN), 0, "a", "fkkoo", (SUBR) init_cladsyn, + (SUBR) perf_cladsyn, (SUBR) destroy_cladsyn, NULL, 0} }; LINKAGE diff --git a/6.x/src/chua/CMakeLists.txt b/src/chua/CMakeLists.txt similarity index 80% rename from 6.x/src/chua/CMakeLists.txt rename to src/chua/CMakeLists.txt index 1946da33..54c277b9 100644 --- a/6.x/src/chua/CMakeLists.txt +++ b/src/chua/CMakeLists.txt @@ -3,6 +3,8 @@ option(BUILD_CHUA_OPCODES "Build the chua opcodes" ON) if(USE_VCPKG) find_package(Eigen3 CONFIG REQUIRED) + # The Eigen3 config package sets Eigen3_FOUND; normalise for check_deps(). + set(EIGEN3_FOUND TRUE) else() find_package(EIGEN3) endif() @@ -12,6 +14,7 @@ check_deps(BUILD_CHUA_OPCODES EIGEN3_FOUND) if(BUILD_CHUA_OPCODES AND EIGEN3_FOUND) make_plugin(chua ChuaOscillator.cpp) target_include_directories(chua PRIVATE ${EIGEN3_INCLUDE_DIR} ${CSOUND_INCLUDE_DIRS}) + target_compile_definitions(chua PRIVATE BUILD_PLUGINS) if(USE_VCPKG) target_link_libraries(chua PRIVATE Eigen3::Eigen) diff --git a/6.x/src/chua/ChuaOscillator.cpp b/src/chua/ChuaOscillator.cpp similarity index 85% rename from 6.x/src/chua/ChuaOscillator.cpp rename to src/chua/ChuaOscillator.cpp index 9a8188fe..1da760c4 100644 --- a/6.x/src/chua/ChuaOscillator.cpp +++ b/src/chua/ChuaOscillator.cpp @@ -131,9 +131,9 @@ using namespace csound; class ChuasOscillatorCubic : public OpcodeNoteoffBase { public: // OUTPUTS - MYFLT *I3; - MYFLT *V2; - MYFLT *V1; + cs_float *I3; + cs_float *V2; + cs_float *V1; // INPUTS // % L,R0,C2,G,Ga,Gb,E,C1,a,b,c,d // sys_variables = [system_vars(5:12),system_vars(23:26)]; @@ -142,25 +142,25 @@ class ChuasOscillatorCubic : public OpcodeNoteoffBase { // function TimeSeries = chuacc(L,R0,C2,G,Ga,Gb,C1,E,x0,y0,z0, // dataset_size,step_size) // Circuit elements. - MYFLT *L_; - MYFLT *R0_; - MYFLT *C2_; - MYFLT *G_; + cs_float *L_; + cs_float *R0_; + cs_float *C2_; + cs_float *G_; // Omit Ga, not used with cubic nonlinearity. // Omit Gb, not used with cubic nonlinearity. - MYFLT *C1_; + cs_float *C1_; // Omit E, not used with cubic nonlinearity. // Initial values... // %x0 = I3, y0 = V2, z0 = V1 from datafiles - MYFLT *I3_; - MYFLT *V2_; - MYFLT *V1_; - MYFLT *step_size_; + cs_float *I3_; + cs_float *V2_; + cs_float *V1_; + cs_float *step_size_; // STATE // Runge-Kutta step sizes. - MYFLT h; - MYFLT h2; - MYFLT h6; + cs_float h; + cs_float h2; + cs_float h6; // Runge-Kutta slopes. Eigen::VectorXd k1; Eigen::VectorXd k2; @@ -169,17 +169,14 @@ class ChuasOscillatorCubic : public OpcodeNoteoffBase { // Temporary value. Eigen::VectorXd M; // Polynomial for nonlinear element. - MYFLT a; - MYFLT b; - MYFLT c; - MYFLT d; + cs_float a; + cs_float b; + cs_float c; + cs_float d; size_t ksmps; public: - int init(CSOUND *csound) { - if (!csound->GetReinitFlag(csound) && !csound->GetTieFlag(csound)) { - csound->RegisterDeinitCallback(csound, this, ¬eoff_); - } + int32_t init(CSOUND *csound) { // h = step_size; %*(G/C2); h = *step_size_; // h2 = (h)*(.5); @@ -219,7 +216,7 @@ class ChuasOscillatorCubic : public OpcodeNoteoffBase { b, c, d); return OK; } - int noteoff(CSOUND *csound) { + int32_t noteoff(CSOUND *csound) { warn(csound, "ChuasOscillatorCubic::noteoff\n"); k1.resize(0); k2.resize(0); @@ -228,10 +225,10 @@ class ChuasOscillatorCubic : public OpcodeNoteoffBase { M.resize(0); return OK; } - static int noteoff_(CSOUND *csound, void *opcode) { + static int32_t noteoff_(CSOUND *csound, void *opcode) { return ((ChuasOscillatorCubic *)opcode)->noteoff(csound); } - int kontrol(CSOUND *csound) { + int32_t kontrol(CSOUND *csound) { // NOTE: MATLAB code goes into eigen3 C++ code pretty straightforwardly, // probaby by design. This is very handy and should prevent mistakes. // Start with aliases for the Csound inputs, in order @@ -240,20 +237,20 @@ class ChuasOscillatorCubic : public OpcodeNoteoffBase { uint32_t early = opds.insdshead->ksmps_no_end; uint32_t nsmps = opds.insdshead->ksmps; if (UNLIKELY(offset)) { - memset(I3, '\0', offset * sizeof(MYFLT)); - memset(V1, '\0', offset * sizeof(MYFLT)); - memset(V2, '\0', offset * sizeof(MYFLT)); + memset(I3, '\0', offset * sizeof(cs_float)); + memset(V1, '\0', offset * sizeof(cs_float)); + memset(V2, '\0', offset * sizeof(cs_float)); } if (UNLIKELY(early)) { - memset(&I3[nsmps - early], '\0', early * sizeof(MYFLT)); - memset(&V1[nsmps - early], '\0', early * sizeof(MYFLT)); - memset(&V2[nsmps - early], '\0', early * sizeof(MYFLT)); + memset(&I3[nsmps - early], '\0', early * sizeof(cs_float)); + memset(&V1[nsmps - early], '\0', early * sizeof(cs_float)); + memset(&V2[nsmps - early], '\0', early * sizeof(cs_float)); } - MYFLT &L = *L_; - MYFLT &R0 = *R0_; - MYFLT &C2 = *C2_; - MYFLT &G = *G_; - MYFLT &C1 = *C1_; + cs_float &L = *L_; + cs_float &R0 = *R0_; + cs_float &C2 = *C2_; + cs_float &G = *G_; + cs_float &C1 = *C1_; // Recompute Runge-Kutta step sizes if necessary. if (h != *step_size_) { // h = step_size; %*(G/C2); @@ -305,7 +302,7 @@ class ChuasOscillatorCubic : public OpcodeNoteoffBase { } return OK; } - MYFLT gnor(MYFLT x) { // Odd to use std::pow here for integer powers when + cs_float gnor(cs_float x) { // Odd to use std::pow here for integer powers when // there is intpow function return (a * std::pow(x, FL(3.0))) + (b * std::pow(x, FL(2.0))) + (c * x) + d; @@ -398,9 +395,9 @@ class ChuasOscillatorPiecewise : public OpcodeNoteoffBase { public: // OUTPUTS - MYFLT *I3; - MYFLT *V2; - MYFLT *V1; + cs_float *I3; + cs_float *V2; + cs_float *V1; // INPUTS // sys_variables = system_vars(5:12); % L,R0,C2,G,Ga,Gb,E,C1 // % x0,y0,z0,dataset_size,step_size @@ -408,24 +405,24 @@ class ChuasOscillatorPiecewise // function TimeSeries = chuacc(L,R0,C2,G,Ga,Gb,C1,E,x0,y0,z0, // dataset_size,step_size) // Circuit elements. - MYFLT *L_; - MYFLT *R0_; - MYFLT *C2_; - MYFLT *G_; - MYFLT *Ga_; - MYFLT *Gb_; - MYFLT *E_; - MYFLT *C1_; + cs_float *L_; + cs_float *R0_; + cs_float *C2_; + cs_float *G_; + cs_float *Ga_; + cs_float *Gb_; + cs_float *E_; + cs_float *C1_; // Initial values... - MYFLT *I3_; - MYFLT *V2_; - MYFLT *V1_; - MYFLT *step_size_; + cs_float *I3_; + cs_float *V2_; + cs_float *V1_; + cs_float *step_size_; // STATE // Runge-Kutta step sizes. - MYFLT h; - MYFLT h2; - MYFLT h6; + cs_float h; + cs_float h2; + cs_float h6; // Runge-Kutta slopes. Eigen::VectorXd k1; Eigen::VectorXd k2; @@ -434,24 +431,21 @@ class ChuasOscillatorPiecewise // Temporary value. Eigen::VectorXd M; // Other variables. - MYFLT step_size; - MYFLT anor; - MYFLT bnor; - MYFLT bnorplus1; - MYFLT alpha; - MYFLT beta; - MYFLT gammaloc; - MYFLT bh; - MYFLT bh2; - MYFLT ch; - MYFLT ch2; - MYFLT omch2; - MYFLT temp; + cs_float step_size; + cs_float anor; + cs_float bnor; + cs_float bnorplus1; + cs_float alpha; + cs_float beta; + cs_float gammaloc; + cs_float bh; + cs_float bh2; + cs_float ch; + cs_float ch2; + cs_float omch2; + cs_float temp; size_t ksmps; - int init(CSOUND *csound) { - if (!csound->GetReinitFlag(csound) && !csound->GetTieFlag(csound)) { - csound->RegisterDeinitCallback(csound, this, ¬eoff_); - } + int32_t init(CSOUND *csound) { // NOTE: The original MATLAB code uses 1-based indexing. // Although the MATLAB vectors are columns, // these are rows; it doesn't matter here. @@ -479,7 +473,7 @@ class ChuasOscillatorPiecewise step_size); return OK; } - int noteoff(CSOUND *csound) { + int32_t noteoff(CSOUND *csound) { warn(csound, "ChuasOscillatorPiecewise::noteoff\n"); k1.resize(0); k2.resize(0); @@ -488,10 +482,10 @@ class ChuasOscillatorPiecewise M.resize(0); return OK; } - static int noteoff_(CSOUND *csound, void *opcode) { + static int32_t noteoff_(CSOUND *csound, void *opcode) { return ((ChuasOscillatorPiecewise *)opcode)->noteoff(csound); } - int kontrol(CSOUND *csound) { + int32_t kontrol(CSOUND *csound) { // NOTE: MATLAB code goes into eigen3 C++ code pretty straightforwardly, // probaby by design. This is very handy and should prevent mistakes. // Start with aliases for the Csound inputs, in order @@ -499,24 +493,24 @@ class ChuasOscillatorPiecewise uint32_t offset = opds.insdshead->ksmps_offset; uint32_t early = opds.insdshead->ksmps_no_end; if (UNLIKELY(offset)) { - memset(I3, '\0', offset * sizeof(MYFLT)); - memset(V1, '\0', offset * sizeof(MYFLT)); - memset(V2, '\0', offset * sizeof(MYFLT)); + memset(I3, '\0', offset * sizeof(cs_float)); + memset(V1, '\0', offset * sizeof(cs_float)); + memset(V2, '\0', offset * sizeof(cs_float)); } if (UNLIKELY(early)) { ksmps -= early; - memset(&I3[ksmps], '\0', early * sizeof(MYFLT)); - memset(&V1[ksmps], '\0', early * sizeof(MYFLT)); - memset(&V2[ksmps], '\0', early * sizeof(MYFLT)); + memset(&I3[ksmps], '\0', early * sizeof(cs_float)); + memset(&V1[ksmps], '\0', early * sizeof(cs_float)); + memset(&V2[ksmps], '\0', early * sizeof(cs_float)); } - MYFLT &L = *L_; - MYFLT &R0 = *R0_; - MYFLT &C2 = *C2_; - MYFLT &G = *G_; - MYFLT &Ga = *Ga_; - MYFLT &Gb = *Gb_; - MYFLT &E = *E_; - MYFLT &C1 = *C1_; + cs_float &L = *L_; + cs_float &R0 = *R0_; + cs_float &C2 = *C2_; + cs_float &G = *G_; + cs_float &Ga = *Ga_; + cs_float &Gb = *Gb_; + cs_float &E = *E_; + cs_float &C1 = *C1_; // Recompute Runge-Kutta stuff every kperiod in case kontrol variables // have changed. step_size = *step_size_; @@ -592,18 +586,17 @@ OENTRY oentries[] = { // (char*)"chuac", // sizeof(ChuasOscillatorCubic), // 0, - // 5, // // kL, kR0, kC2, kG, kC1, iI3, iV2, iV1, // kstep_size // // 0.00945, 7.5, 2e-007, 0.00105, 1.5e-008, 0, -0.1, 0.1, 5e-6 // (char*)"aaa", // (char*)"kkkkkiiik", // (SUBR) ChuasOscillatorCubic::init_, - // 0, // (SUBR) ChuasOscillatorCubic::kontrol_, + // (SUBR) ChuasOscillatorCubic::deinit_, // }, { - (char *)"chuap", sizeof(ChuasOscillatorPiecewise), 0, 3, + (char *)"chuap", sizeof(ChuasOscillatorPiecewise), 0, // kL, kR0, kC2, kG, kGa, kGb, kE, kC1, // iI3, iV2, iV1, kstep_size // 0.00945, 7.5, 2e-007, 0.00105, 0, -0.00121, 1.76e-005, @@ -612,25 +605,28 @@ OENTRY oentries[] = { (char *)"aaa", (char *)"kkkkkkkkiiik", (SUBR)ChuasOscillatorPiecewise::init_, (SUBR)ChuasOscillatorPiecewise::kontrol_, + (SUBR)ChuasOscillatorPiecewise::deinit_, }, { - 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, }}; -PUBLIC int csoundModuleCreate(CSOUND *csound) { return 0; } +PUBLIC int32_t csoundModuleInfo(void) { return CSOUND_MODULE_INFO; } + +PUBLIC int32_t csoundModuleCreate(CSOUND *csound) { return 0; } -PUBLIC int csoundModuleInit(CSOUND *csound) { - int status = 0; +PUBLIC int32_t csoundModuleInit(CSOUND *csound) { + int32_t status = 0; for (OENTRY *oentry = &oentries[0]; oentry->opname; oentry++) { - status |= csound->AppendOpcode(csound, oentry->opname, oentry->dsblksiz, - oentry->flags, oentry->thread, - oentry->outypes, oentry->intypes, - (int (*)(CSOUND *, void *))oentry->iopadr, - (int (*)(CSOUND *, void *))oentry->kopadr, - (int (*)(CSOUND *, void *))oentry->aopadr); + status |= csound->AppendOpcode( + csound, oentry->opname, oentry->dsblksiz, oentry->flags, + oentry->outypes, oentry->intypes, + (int32_t (*)(CSOUND *, void *))oentry->init, + (int32_t (*)(CSOUND *, void *))oentry->perf, + (int32_t (*)(CSOUND *, void *))oentry->deinit); } return status; } -PUBLIC int csoundModuleDestroy(CSOUND *csound) { return 0; } +PUBLIC int32_t csoundModuleDestroy(CSOUND *csound) { return 0; } } diff --git a/src/chua/OpcodeBase.hpp b/src/chua/OpcodeBase.hpp new file mode 100644 index 00000000..49a724fc --- /dev/null +++ b/src/chua/OpcodeBase.hpp @@ -0,0 +1,499 @@ +/* + OpcodeBase.hpp: + + Copyright (C) 2005, 2009, 2017 by Istva Varga, Victor Lazzarini and + Michael Gogins + + This file is part of Csound. + + The Csound Library is free software; you can redistribute it + and/or modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + Csound is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with Csound; if not, write to the Free Software + Foundation, Inc., 31 Milk Street, #960789, Boston, MA, 02196, USA +*/ +#pragma once + +/** + * There is a conflict between the preprocessor definition "_CR" in the + * standard C++ library and in Csound. To work around this, undefine "_CR" and + * include ALL standard library include files BEFORE including ANY Csound + * include files. + */ +#if defined(_CR) +#undef _CR +#endif + +#include +#include +#include +#if !(defined(__wasi__)) +#include +#endif +#include +#include +#include "interlocks.h" +#ifdef BUILD_PLUGINS +#include "csdl.h" +#else +#include "csoundCore.h" +#endif + +/** + * Template base class, or pseudo-virtual base class, + * for writing Csound opcodes in C++. + * Derive opcode implementation classes like this: + * + * DerivedClass : public OpcodeBase + * { + * public: + * // All output fields must be declared first as cs_float *: + * cs_float *aret1; + * // All input fields must be declared next as cs_float *: + * cs_float *iarg1; + * cs_float *karg2; + * cs_float *aarg3; + * // All internal state variables must be declared after that: + * size_t state1; + * double state2; + * cs_float state3; + * // If the opcode shares data protect it by creating one or more void + * // *mutex member pointers: + * void *mutex1; + * void *mutex2; + * // and create them usind csound->Create_Mutex() in csoundModuleCreate + * // and destroy them using csound->DeleteMutex(mutex) + * // csoundModuleDestroy, and lock them using LockGuard guard(mutex). + * int32_t init(); + * int32_t kontrol(); + * int32_t audio; + * int32_t noteoff(); + * void deinit(); + * }; + */ + +namespace csound { + + static bool diagnostics_enabled = false; + + /** + * Use this to guard against data races in opcode functions. The mutex should + * be created by csound->Create_Mutex() in csoundModuleCreate(), and should + * be destroyed by csound->DeleteMutex() in csoundModuleDestroy(). + * + * If data is shared between opcode instances, the mutex should be global to + * the opcode library; if data is shared between threads for a single instance, + * the mutex should belong to the opcode instance. + */ + struct LockGuard { + LockGuard(CSOUND *csound_, void *mutex_) : csound(csound_), mutex(mutex_) + { + csound->LockMutex(mutex); + } + ~LockGuard() + { + csound->UnlockMutex(mutex); + } + CSOUND *csound; + void *mutex; + }; + + /** + * Use this to store a pointer to a global heap-allocated object, e.g. one + * used to manage state between opcode instances. + */ + + template int32_t CreateGlobalPointer(CSOUND *csound, const char *name, T *pointer) + { + T **pointer_to_pointer = 0; + int32_t result = csound->CreateGlobalVariable(csound, name, sizeof(pointer_to_pointer)); + pointer_to_pointer = static_cast(csound->QueryGlobalVariable(csound, name)); + *pointer_to_pointer = pointer; + return result; + } + + /** + * Retrieve a pointer to a global heap-allocated object, e.g. one + * used to manage state between opcode instances. + */ + template T *QueryGlobalPointer(CSOUND *csound, const char *name, T*& pointer) + { + T **pointer_to_pointer = static_cast(csound->QueryGlobalVariableNoCheck(csound, name)); + if (pointer_to_pointer != 0) { + pointer = *pointer_to_pointer; + } else { + pointer = 0; + } + return pointer; + } + + template + class OpcodeBase + { + public: + int32_t init(CSOUND *csound) + { + return NOTOK; + } + static int32_t init_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->init(csound); + } + int32_t kontrol(CSOUND *csound) + { + return NOTOK; + } + static int32_t kontrol_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->kontrol(csound); + } + int32_t audio(CSOUND *csound) + { + return NOTOK; + } + static int32_t audio_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->audio(csound); + } + /** + This is how to compute audio signals for normal opcodes: + (1) Zero all frames from 0 up to but not including Offset. + (2) Compute all frames from ksmps_offset up to but not including End. + (3) Zero all frames from End up to but not including ksmps. + Example from a C opcode: + uint32_t offset = p->h.insdshead->ksmps_offset; + uint32_t early = p->h.insdshead->ksmps_no_end; + uint32_t n, nsmps = CS_KSMPS; + if (UNLIKELY(offset)) memset(p->r, '\0', offset*sizeof(cs_float)); + if (UNLIKELY(early)) { + nsmps -= early; + memset(&p->r[nsmps], '\0', early*sizeof(cs_float)); + } + for (n = offset; n < nsmps; n++) { + input1 = CS_FLOAT2LRND(p->a[n]); + p->r[n] = (cs_float) (input1 >> input2); + } + So in C++ it should look like this (which is much easier to understand): + int32_t frameIndex = 0; + for( ; frameIndex < kperiodOffset(); ++frameIndex) { + asignal[frameIndex] = 0; + } + for( ; frameIndex < kperiodEnd(); ++frameIndex) { + asignal[frameIndex] = compute(); + } + for( ; frameIndex < ksmps(); ++frameIndex) { + asignal[frameIndex] = 0; + } + */ + uint32_t kperiodOffset() const + { + return opds.insdshead->ksmps_offset; + } + uint32_t kperiodEnd() const + { + uint32_t end = opds.insdshead->ksmps_no_end; + if (end) { + return end; + } else { + return ksmps(); + } + } + uint32_t ksmps() const + { + return opds.insdshead->ksmps; + } + uint32_t output_arg_count() + { + return (uint32_t)opds.optext->t.outArgCount; + } + uint32_t input_arg_count() + { + return (uint32_t)opds.optext->t.inArgCount; + } + void log(CSOUND *csound, const char *format,...) + { + va_list args; + va_start(args, format); + if(csound) { + csound->MessageV(csound, 0, format, args); + } else { + vfprintf(stdout, format, args); + } + va_end(args); + } + void warn(CSOUND *csound, const char *format,...) + { + if(csound) { + if(csound->GetMessageLevel(csound) & CS_WARNMSG) { + va_list args; + va_start(args, format); + csound->MessageV(csound, CSOUNDMSG_WARNING, format, args); + va_end(args); + } + } else { + va_list args; + va_start(args, format); + vfprintf(stdout, format, args); + va_end(args); + } + } + OPDS opds; + }; + + template + class OpcodeNoteoffBase + { + public: + int32_t init(CSOUND *csound) + { + IGN(csound); + return NOTOK; + } + + static int32_t deinit_(CSOUND *csound, void *opcode) + { + if (!csound->GetReinitFlag(csound) && !csound->GetTieFlag(csound)) { + return noteoff_(csound, opcode); + } + return OK; + } + + static int32_t init_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->init(csound); + } + + int32_t kontrol(CSOUND *csound) + { + IGN(csound); + return NOTOK; + } + static int32_t kontrol_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->kontrol(csound); + } + int32_t audio(CSOUND *csound) + { + IGN(csound); + return NOTOK; + } + static int32_t audio_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->audio(csound); + } + /** + This is how to compute audio signals for normal opcodes: + (1) Zero all frames from 0 up to but not including Offset. + (2) Compute all frames from ksmps_offset up to but not including End. + (3) Zero all frames from End up to but not including ksmps. + Example from a C opcode: + uint32_t offset = p->h.insdshead->ksmps_offset; + uint32_t early = p->h.insdshead->ksmps_no_end; + uint32_t n, nsmps = CS_KSMPS; + if (UNLIKELY(offset)) memset(p->r, '\0', offset*sizeof(cs_float)); + if (UNLIKELY(early)) { + nsmps -= early; + memset(&p->r[nsmps], '\0', early*sizeof(cs_float)); + } + for (n = offset; n < nsmps; n++) { + input1 = CS_FLOAT2LRND(p->a[n]); + p->r[n] = (cs_float) (input1 >> input2); + } + So in C++ it should look like this (which is much easier to understand): + int32_t frameIndex = 0; + for( ; frameIndex < kperiodOffset(); ++frameIndex) { + asignal[frameIndex] = 0; + } + for( ; frameIndex < kperiodEnd(); ++frameIndex) { + asignal[frameIndex] = compute(); + } + for( ; frameIndex < ksmps(); ++frameIndex) { + asignal[frameIndex] = 0; + } + */ + uint32_t kperiodOffset() const + { + return opds.insdshead->ksmps_offset; + } + uint32_t kperiodEnd() const + { + uint32_t end = opds.insdshead->ksmps_no_end; + if (end) { + return end; + } else { + return ksmps(); + } + } + uint32_t ksmps() const + { + return opds.insdshead->ksmps; + } + uint32_t output_arg_count() + { + return (uint32_t)opds.optext->t.outArgCount; + } + uint32_t input_arg_count() + { + return (uint32_t)opds.optext->t.inArgCount; + } + void log(CSOUND *csound, const char *format,...) + { + va_list args; + va_start(args, format); + if(csound) { + csound->MessageV(csound, 0, format, args); + } else { + vfprintf(stdout, format, args); + } + va_end(args); + } + void warn(CSOUND *csound, const char *format,...) + { + if(csound) { + if(csound->GetMessageLevel(csound) & CS_WARNMSG) { + va_list args; + va_start(args, format); + csound->MessageV(csound, CSOUNDMSG_WARNING, format, args); + va_end(args); + } + } else { + va_list args; + va_start(args, format); + vfprintf(stdout, format, args); + va_end(args); + } + } + int32_t noteoff(CSOUND *csound) + { + return OK; + } + static int32_t noteoff_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->noteoff(csound); + } + OPDS opds; + }; + + +#if (__cplusplus >= 201103L) && !(defined(__wasi__)) && !defined(BARE_METAL) + // #pragma message("Defining heap_object_manager_t.") + /** + * The memory of non-POD C++ or C objects allocated on the heap by Csound + * plugins is managed not by Csound, but by the plugin module. This class + * performs that function. + * + * As long as all memory is allocated in or after csoundModuleCreate and + * de-allocated in csoundModuleDestroy, all will be well. The danger however + * is that memory leaks will occur if allocations are not matched with + * de-allocations, or worse, that crashes will occur if any objects are + * deleted twice or de-referenced after deletion. + * + * So, the plugin should manage a collection of all heap-allocated objects, + * keyed first off the Csound instance, and second off an object handle; and + * all such objects must be accessed only by handle, from this collection. + * + * In csoundModuleDestroy, all objects for the calling instance of Csound + * should be de-allocated, and the sub-collection for that instance should + * also be de-allocated. + * + * If then there are no more sub-collections for other instances of Csound, + * the entire contents of the collection should be de-allocated. + * + * This should be done as a singleton, and in a thread-safe way. + * + * There might still be a problem if any of the heap-allocated objects, + * themselves, have global static members, perhaps created by some external + * dependency. + * + * Please note, objects O in the declaration below must not be std::shared_ptr + * objects. Deletion of objects herein must be unconditional. Rather, the + * objects passed are raw pointers to instances of class O. + */ + template class heap_object_manager_t { + private: + std::map> objects_; + std::recursive_mutex mutex; + heap_object_manager_t(){}; + ~heap_object_manager_t(){}; + public: + static heap_object_manager_t &instance() { + static heap_object_manager_t singleton; + return singleton; + } + std::map> &objects() { + std::lock_guard lock(mutex); + return objects_; + } + /** + * Returns a list of pointers to all objects allocated for this + * instance of Csound. + */ + std::vector &objects_for_csound(CSOUND *csound) { + std::lock_guard lock(mutex); + auto &objects_for_csound_ = objects()[csound]; + return objects_for_csound_; + } + /** + * Returns the handle for the object; if the object has not yet been + * stored, inserts it into the list of object pointers for this + * instance of Csound; otherwise, returns the handle of the stored + * object pointer. + */ + int32_t handle_for_object(CSOUND *csound, O *object) { + std::lock_guard lock(mutex); + auto &objects_for_csound_ = objects_for_csound(csound); + auto iterator = std::find(objects_for_csound_.begin(), objects_for_csound_.end(), object); + if (iterator == objects_for_csound_.end()) { + int32_t handle = objects_for_csound_.size(); + objects_for_csound_.push_back(object); + if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::handle_for_object %p: new object handle: %d (of %ld)\n", object, handle, objects_for_csound_.size()); + return handle; + } else { + int32_t handle = static_cast(iterator - objects_for_csound_.begin()); + if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::handle_for_object: existing object handle: %d\n", handle); + return handle; + } + } + /** + * Returns the object pointer for the handle; + * if the object pointer has not been stored by + * handle, returns a null pointer. + */ + O *object_for_handle(CSOUND *csound, int32_t handle) { + std::lock_guard lock(mutex); + auto &objects_for_csound_ = objects_for_csound(csound); + if (handle >= objects_for_csound_.size()) { + return nullptr; + } + O *object = objects_for_csound_[handle]; + if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::object_for_handle: %p %d (of %ld)\n", object, handle, objects_for_csound_.size()); + return object; + } + /** + * First destroys all objects created by the calling + * instance of Csound, then destroys the list of + * object pointers for this instance of Csound. + */ + void module_destroy(CSOUND *csound) { + std::lock_guard lock(mutex); + auto &objects_for_csound_ = objects_for_csound(csound); + for (int32_t i = 0, n = objects_for_csound_.size(); i < n; ++i) { + delete objects_for_csound_[i]; + objects_for_csound_[i] = nullptr; + } + objects_for_csound_.clear(); + objects().erase(csound); + } + }; + +#endif + +} diff --git a/src/dssi4cs/CMakeLists.txt b/src/dssi4cs/CMakeLists.txt new file mode 100644 index 00000000..b7b2849f --- /dev/null +++ b/src/dssi4cs/CMakeLists.txt @@ -0,0 +1,66 @@ +# Build the DSSI/LADSPA hosting opcodes. +# +# Hosting native DSSI synths and LADSPA effects is Linux-only: the opcodes load +# plugin libraries with dlopen and exchange ALSA sequencer events, so they need +# the system ALSA headers (no ALSA library, and no running sequencer, is +# required). The DSSI and LADSPA API headers are vendored under ./include. +# +# The plugin is built by default and silently disabled where the platform is +# not Linux or the ALSA headers are missing. +option(BUILD_DSSI_OPCODES "Build the DSSI/LADSPA opcodes" ON) + +find_path(DSSI_ALSA_ASOUNDLIB_H alsa/asoundlib.h /usr/local/include /usr/include) +find_path(DSSI_ALSA_SEQ_EVENT_H alsa/seq_event.h /usr/local/include /usr/include) + +check_deps(BUILD_DSSI_OPCODES LINUX DSSI_ALSA_ASOUNDLIB_H DSSI_ALSA_SEQ_EVENT_H) + +if(BUILD_DSSI_OPCODES) + make_plugin(dssi4cs "load.c;dssi4cs.c" ${CMAKE_DL_LIBS} m) + target_include_directories(dssi4cs PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include/dssi + ${CMAKE_CURRENT_SOURCE_DIR}/include/ladspa + ${DSSI_ALSA_ASOUNDLIB_H} + ${CSOUND_INCLUDE_DIRS}) + target_compile_definitions(dssi4cs PRIVATE BUILD_PLUGINS) + + # On multiarch distributions (Debian, Ubuntu, ...) plugins may be + # installed under /usr/lib//dssi and /usr/lib//ladspa. + # Prepend the triplet detected for this build to the default search path; + # DSSI_PATH and LADSPA_PATH still take precedence at runtime. + if(CMAKE_LIBRARY_ARCHITECTURE) + target_compile_definitions(dssi4cs PRIVATE + "DSSI4CS_DEFAULT_PATH=\"/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}/dssi:/usr/lib/${CMAKE_LIBRARY_ARCHITECTURE}/ladspa:/usr/local/lib/dssi:/usr/lib/dssi:/usr/lib64/dssi:/usr/local/lib/ladspa:/usr/lib/ladspa:/usr/lib64/ladspa\"") + endif() + + # Regression tests for the host, built and registered with CTest when + # GoogleTest is available. The fixture is a small real DSSI/LADSPA plugin + # built from the vendored headers, so its ABI matches the host. + find_package(GTest QUIET) + if(GTest_FOUND) + add_library(dssi_test_plugin MODULE tests/fixtures/dssi_test_plugin.c) + add_library(ladspa_test_plugin MODULE tests/fixtures/dssi_test_plugin.c) + target_compile_definitions(ladspa_test_plugin PRIVATE DSSI_TEST_LADSPA) + foreach(plugin dssi_test_plugin ladspa_test_plugin) + target_include_directories(${plugin} PRIVATE + ${CMAKE_CURRENT_SOURCE_DIR}/include/dssi + ${CMAKE_CURRENT_SOURCE_DIR}/include/ladspa + ${DSSI_ALSA_ASOUNDLIB_H}) + endforeach() + + add_executable(dssi_tests tests/dssi_test.cpp) + # Recent GoogleTest releases require C++14, which is newer than the + # C++11 default used for the plugin itself. + set_target_properties(dssi_tests PROPERTIES + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED ON) + target_include_directories(dssi_tests PRIVATE ${CSOUND_INCLUDE_DIRS}) + target_link_libraries(dssi_tests PRIVATE + GTest::gtest_main ${CSOUND_LIBRARY} ${CMAKE_DL_LIBS}) + target_compile_definitions(dssi_tests PRIVATE + CSOUND_TEST_DSSI_HOST="$" + CSOUND_TEST_DSSI_PLUGIN="$" + CSOUND_TEST_LADSPA_PLUGIN="$") + add_dependencies(dssi_tests dssi4cs dssi_test_plugin ladspa_test_plugin) + add_test(NAME dssi_tests COMMAND dssi_tests) + endif() +endif() diff --git a/src/dssi4cs/README.md b/src/dssi4cs/README.md new file mode 100644 index 00000000..34211784 --- /dev/null +++ b/src/dssi4cs/README.md @@ -0,0 +1,195 @@ +# DSSI and LADSPA opcodes + +This module hosts native DSSI synths and LADSPA effects on Linux. It is built +by default on Linux when the ALSA headers are available (`libasound2-dev` on +Debian/Ubuntu); configure with `-DBUILD_DSSI_OPCODES=OFF` to exclude it, and it +is silently disabled on other platforms. The module does not need the ALSA +sequencer service or link to libasound. Plugins must match the host OS, CPU, +and DSSI/LADSPA ABI. + +## Loading + +```csound +ih dssiinit Sfilename, iindex [, iverbose] +dssilist +dssiinfo ih +``` + +`iindex` selects a plugin within a library. A successful load returns an +instance handle starting at zero. Each call creates a separate instance. +`iverbose` defaults to 1 and prints ports and MIDI mappings. `dssiinfo` prints +that information for an existing handle. `dssilist` lists both DSSI and LADSPA +libraries, including libraries that export only `dssi_descriptor`. + +A name containing `/` is a path. For other names, search `DSSI_PATH`, then +`LADSPA_PATH`, then platform defaults. Each environment variable contains +colon-separated directories; empty entries are skipped. Lookup tries the +name as supplied, then `.so`. + +Linux defaults include `/usr/local/lib`, `/usr/lib`, and `/usr/lib64`, each +with `dssi` and `ladspa` subdirectories. On multiarch distributions the build +also adds the detected triplet directory, such as +`/usr/lib/x86_64-linux-gnu/dssi`; set the environment paths to search any +other location. + +Instances and libraries remain loaded until Csound reset or destruction. +Failed loads do not publish a handle. Cleanup calls the plugin's deactivate +and cleanup methods before releasing its buffers and library. + +## Audio and activation + +```csound +dssiactivate ih, kactive +a1 [, a2, ...] dssisynth ih [, ain1, ain2, ...] +a1 [, a2, ...] dssiaudio ih [, ain1, ain2, ...] +``` + +`kactive` must be 0 or 1. Activate before sending events or rendering. +`dssisynth` requires a DSSI synth. `dssiaudio` accepts a LADSPA effect or DSSI +plugin and chooses its synth callback when available. Audio ports follow +the descriptor order, with input and output ports counted separately. +Unused inputs receive silence. Csound's `0dbfs` scales audio to/from LADSPA +float buffers. + +Give each instance one audio renderer. The renderer's `ksmps` may be smaller +than the `ksmps` used to create the instance. Partial first/last blocks produce +silence outside the instrument's active samples. Ending the renderer +instrument deactivates the instance and discards pending events. Keep a +renderer running if notes or release tails must outlive their source +instrument. Deactivation clears pending notes; resuming sends note-offs for +old sounding notes, releases sustain, and sends all-sound-off/all-notes-off +even when the plugin has no activation callbacks. + +The host supports plugins that provide only `run_multiple_synths`. Every +call includes all active instances from the same library and LADSPA label. +Activate all grouped instances before the first renderer in that block. +Late activation reports an error rather than advancing a peer twice. +For groups with more than one instance, all renderers must have the same +`ksmps`, use full blocks, and the plugin must have no audio inputs. Send events +for every instance before the group's first renderer. Later renderers read +the outputs from that same group call. + +## Notes and MIDI + +```csound +dssinote ktrigger, ih, knote, kvelocity, kduration [, ichannel] +dssievent ktrigger, ih, knote, kvelocity +dssievent ktrigger, ih, kstatus, kchannel, kdata1, kdata2 [, koffset] +dssinrpn ktrigger, ih, kchannel, kparameter, kvalue [, koffset] +``` + +Each nonzero trigger sends an event in that control cycle; it is not an +edge detector. Use a one-cycle trigger for one note. Notes/velocities range +from 0 to 127. Channels range from 0 to 15 and default to 0 in `dssinote`. +The four-argument `dssievent` sends a note on channel 0; velocity zero sends +a true note-off. `dssinote` schedules a note-off after `kduration` seconds, +rounded to the nearest sample. Its pending note-off survives the source +instrument if a separate renderer remains active. + +The full MIDI form accepts status bytes without channel bits: + +| Status | Meaning | Data | +| --- | --- | --- | +| 128 / 0x80 | Note off | note, release velocity | +| 144 / 0x90 | Note on | note, velocity; zero means note off | +| 160 / 0xA0 | Key pressure | note, pressure | +| 176 / 0xB0 | Controller | controller, value | +| 192 / 0xC0 | Program change | program, 0 | +| 208 / 0xD0 | Channel pressure | pressure, 0 | +| 224 / 0xE0 | Pitch bend | low 7 bits, high 7 bits | + +`koffset` defaults to zero and counts samples from the instrument's first +active sample in this block. It must fall within that block's active range. +Events sort by sample time; equal-time events retain insertion order. +Queue events before rendering their block. Sending an event after its +samples have rendered reports a performance error. + +Bank select controllers 0/32 and MIDI program changes call `select_program` +instead of reaching the synth as MIDI. CC and NRPN mappings use the plugin's +control-port hints and do not also deliver the mapped event. Rendering splits +at mapped control/program changes so they take effect at the requested +sample. Use `dssinrpn` for assembled 14-bit NRPN messages: parameters and values +range from 0 to 16383. The host does not assemble NRPN from raw CC 98/99/6/38. + +## Controls, programs, and configuration + +```csound +dssictls ih, iport, kvalue, ktrigger +kvalue dssiget ih, iport +dssiprogram ih, kbank, kprogram, ktrigger +Sname, ibank, iprogram dssiprograminfo ih, iindex +dssiconfigure ih, Skey, Svalue +``` + +Port numbers are the descriptor's absolute port indices, including audio +ports. `dssictls` writes input control ports on each nonzero trigger, and +retains its existing sample-rate-hint scaling. `dssiget` reads an input or +output control port in the plugin's units. Output controls reflect the last +render. Input controls start with LADSPA defaults; selecting a program may +replace them. Put control writes after program selection when overriding a +preset. Audio ports and output controls cannot be written through `dssictls`. + +`dssiprograminfo` enumerates by list index, not by bank/program number. It +copies the name because plugins may reuse their descriptor storage. An +index beyond the list returns an empty name and -1 for bank/program. +`dssiprogram` selects the given bank/program when triggered. Select a program +explicitly when the plugin needs one; plugins need not choose a default on +activation. Program selection does not imply a note-on. + +`dssiconfigure` runs at initialization. Use it to load sample files, patches, +or other plugin settings before performance. It reports and frees plugin +error strings. `GLOBAL:` keys apply to all currently loaded instances of the +same plugin. Configuration invalidates program information; enumerate and +select again afterwards. Put configuration calls and sample paths in the +orchestra so reloading it restores them. This module does not save separate +plugin state files or launch plugin GUIs. + +## Example + +```csound +sr = 48000 +ksmps = 64 +nchnls = 2 +0dbfs = 1 + +gih dssiinit "my_synth.so", 0, 1 + +instr 1 + dssiactivate gih, 1 + konce init 1 + ; If needed: dssiprogram gih, 0, 0, konce + dssinote konce, gih, 60, 100, 0.5 + konce = 0 + aleft, aright dssisynth gih + outs aleft, aright +endin +``` + +Run the instrument long enough for the note and its release tail. Request +one output for a mono plugin. + +## Limits + +- Native Linux only; no macOS or WASM backend or OSC plugin GUI host. +- One Csound performance thread (`-j` values greater than 1 are rejected). +- At most 256 instances per engine and 1024 pending events per instance. + A timed note reserves both its on and off events before enqueueing either. + Queue overflow reports an error. +- Up to nine audio inputs and nine outputs per opcode, as in the existing API. +- Grouped rendering has the restrictions described above. +- No SysEx, MIDI system messages, or raw NRPN assembly in this version. +- Control ports are scalar LADSPA controls. Mixing uses Csound; the optional + additive DSSI/LADSPA callbacks are not needed. + +## ABI sources + +The [DSSI](include/dssi/README.md) and [LADSPA](include/ladspa/README.md) +headers are unmodified copies of upstream releases, with their licenses and +source details. They retain upstream's C ABI types (`unsigned long`, `int`, +and `float`). See the [DSSI specification](https://dssi.sourceforge.net/RFC.html) +and [LADSPA SDK](https://www.ladspa.org/ladspa_sdk/). +ALSA declarations come from the installed system headers. + +Run `include/dssi/update.py` and `include/ladspa/update.py` to update the +vendored headers, or pass `--pinned` to reproduce the recorded releases and +check their archive hashes. diff --git a/src/dssi4cs/dssi4cs.c b/src/dssi4cs/dssi4cs.c new file mode 100644 index 00000000..3939a2b6 --- /dev/null +++ b/src/dssi4cs/dssi4cs.c @@ -0,0 +1,845 @@ +/* + * Copyright (C) 2005 Andres Cabrera + * + * The dssi4cs library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * The dssi4cs library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with The dssi4cs library; if not, write to the Free Software + * Foundation, Inc., 31 Milk Street, #960789, Boston, MA, 02196, USA + * + * Uses code by Richard W.E. Furse from the ladspa sdk + */ + +#include "dssi4cs.h" +#include "utils.h" +#include +#include +#include +#include +#include + +/* State belongs to one Csound engine, and survives until reset. */ +static DSSI_HOST *host(CSOUND *csound) +{ + return csound->QueryGlobalVariable(csound, "$DSSI4CS"); +} + +static int integer(cs_float value, int64_t maximum) +{ + /* Compare as integers so float builds do not round UINT32_MAX up. */ + return value >= 0 && value < 0x1p63 && floor(value) == value && + (int64_t)value <= maximum; +} + +static DSSI_PLUGIN *lookup(CSOUND *csound, cs_float id) +{ + DSSI_HOST *h = host(csound); + return h && h->count && integer(id, h->count - 1) ? + h->plugins[(unsigned int)id] : NULL; +} + +static int64_t block_start(OPDS *p) +{ + return ((int64_t)GetLocalKcounter(p) - 1) * GetLocalKsmps(p); +} + +static int64_t event_time(OPDS *p) +{ + return block_start(p) + GetKsmpsOffset(p); +} + +static void set_active(DSSI_PLUGIN *p, int active) +{ + if (active == p->active) return; + if (active) { + if (p->ladspa->activate) p->ladspa->activate(p->handle); + } + else { + if (p->ladspa->deactivate) p->ladspa->deactivate(p->handle); + p->queued = 0; + p->panic = 1; + } + p->active = active; + p->last_block = -1; +} + +static void destroy_plugin(CSOUND *csound, DSSI_PLUGIN *p) +{ + if (!p) return; + if (p->handle) { + set_active(p, 0); + p->ladspa->cleanup(p->handle); + } + if (p->audio) { + for (unsigned long i = 0; i < p->ladspa->PortCount; ++i) + if (p->audio[i]) csound->Free(csound, p->audio[i]); + csound->Free(csound, p->audio); + } + if (p->control) csound->Free(csound, p->control); + if (p->mapping) csound->Free(csound, p->mapping); + if (p->inputs) csound->Free(csound, p->inputs); + if (p->outputs) csound->Free(csound, p->outputs); + if (p->library) dlclose(p->library); + csound->Free(csound, p); +} + +static int32_t reset(CSOUND *csound, void *data) +{ + DSSI_HOST *h = data; + for (unsigned int i = 0; i < h->count; ++i) + destroy_plugin(csound, h->plugins[i]); + csound->DestroyGlobalVariable(csound, "$DSSI4CS"); + return OK; +} + +static cs_double bound(DSSI_PLUGIN *p, unsigned long port, int upper) +{ + const LADSPA_PortRangeHint *hint = &p->ladspa->PortRangeHints[port]; + return (upper ? hint->UpperBound : hint->LowerBound) * + (LADSPA_IS_HINT_SAMPLE_RATE(hint->HintDescriptor) ? p->sample_rate : 1); +} + +static cs_double interpolate(DSSI_PLUGIN *p, unsigned long port, cs_double fraction) +{ + cs_double lo = bound(p, port, 0), hi = bound(p, port, 1); + LADSPA_PortRangeHintDescriptor flags = p->ladspa->PortRangeHints[port].HintDescriptor; + if (LADSPA_IS_HINT_LOGARITHMIC(flags) && lo > 0 && hi > 0) + return exp(log(lo) * (1 - fraction) + log(hi) * fraction); + return lo * (1 - fraction) + hi * fraction; +} + +static LADSPA_Data default_control(DSSI_PLUGIN *p, unsigned long port) +{ + LADSPA_PortRangeHintDescriptor flags = p->ladspa->PortRangeHints[port].HintDescriptor; + cs_double value = 0; + switch (flags & LADSPA_HINT_DEFAULT_MASK) { + case LADSPA_HINT_DEFAULT_MINIMUM: value = bound(p, port, 0); break; + case LADSPA_HINT_DEFAULT_LOW: value = interpolate(p, port, .25); break; + case LADSPA_HINT_DEFAULT_MIDDLE: value = interpolate(p, port, .5); break; + case LADSPA_HINT_DEFAULT_HIGH: value = interpolate(p, port, .75); break; + case LADSPA_HINT_DEFAULT_MAXIMUM: value = bound(p, port, 1); break; + case LADSPA_HINT_DEFAULT_1: value = 1; break; + case LADSPA_HINT_DEFAULT_100: value = 100; break; + case LADSPA_HINT_DEFAULT_440: value = 440; break; + default: + if (LADSPA_IS_HINT_BOUNDED_BELOW(flags)) value = fmax(value, bound(p, port, 0)); + if (LADSPA_IS_HINT_BOUNDED_ABOVE(flags)) value = fmin(value, bound(p, port, 1)); + } + if (LADSPA_IS_HINT_INTEGER(flags)) value = round(value); + return (LADSPA_Data)value; +} + +static void describe(CSOUND *csound, DSSI_PLUGIN *p) +{ + csound->Message(csound, Str("DSSI4CS: %s: %s (%s), %lu audio inputs, %lu outputs\n"), + p->dssi ? "DSSI" : "LADSPA", p->ladspa->Name, + p->ladspa->Label, p->input_count, p->output_count); + for (unsigned long i = 0; i < p->ladspa->PortCount; ++i) { + LADSPA_PortDescriptor flags = p->ladspa->PortDescriptors[i]; + csound->Message(csound, " %lu: %s, %s %s", i, p->ladspa->PortNames[i], + LADSPA_IS_PORT_INPUT(flags) ? "input" : "output", + LADSPA_IS_PORT_AUDIO(flags) ? "audio" : "control"); + if (LADSPA_IS_PORT_CONTROL(flags)) + csound->Message(csound, Str(", value %g, MIDI mapping %d"), + (cs_double)p->control[i], p->mapping[i]); + csound->Message(csound, "\n"); + } +} + +static int32_t dssiinit(CSOUND *csound, DSSIINIT *op) +{ + *op->result = -1; + if (!integer(*op->index, UINT32_MAX)) + return csound->InitError(csound, Str("DSSI4CS: invalid plugin index\n")); + if (csound->GetOParms(csound)->numThreads > 1) + return csound->InitError(csound, Str("DSSI4CS: use one Csound performance thread\n")); + DSSI_HOST *h = host(csound); + if (!h) { + if (csound->CreateGlobalVariable(csound, "$DSSI4CS", sizeof(DSSI_HOST))) + return csound->InitError(csound, Str("DSSI4CS: cannot create host state\n")); + h = host(csound); + csound->RegisterResetCallback(csound, h, reset); + } + if (h->count == DSSI4CS_INSTANCES) + return csound->InitError(csound, Str("DSSI4CS: instance limit reached\n")); + char legacy_name[MAXNAME]; + const char *name; + if (!strcmp(GetTypeForArg(op->filename)->varTypeName, "S")) + name = ((STRINGDAT *)op->filename)->data; + else { + csound->StringArg2Name(csound, legacy_name, + IsStringCode(*op->filename) ? csound->GetArgString(csound, *op->filename) : + (char *)op->filename, "dssiinit.", IsStringCode(*op->filename)); + name = legacy_name; + } + DSSI_PLUGIN *p = csound->Calloc(csound, sizeof(*p)); + p->last_block = p->rendered_until = -1; + p->capacity = GetLocalKsmps(&op->h); + p->sample_rate = csound->GetEngineSr(csound); + p->library = dlopenLADSPA(csound, name, RTLD_NOW); + const char *error = Str_noop("cannot load library"); + if (!p->library) goto fail; + DSSI_Descriptor_Function dssi = (DSSI_Descriptor_Function)dlsym(p->library, "dssi_descriptor"); + LADSPA_Descriptor_Function ladspa = (LADSPA_Descriptor_Function)dlsym(p->library, "ladspa_descriptor"); + error = Str_noop("missing descriptor or plugin index"); + if (dssi) { + p->dssi = dssi((unsigned long)*op->index); + if (!p->dssi) goto fail; + error = Str_noop("unsupported DSSI API version"); + if (p->dssi->DSSI_API_Version != 1) goto fail; + p->ladspa = p->dssi->LADSPA_Plugin; + } + else if (ladspa) p->ladspa = ladspa((unsigned long)*op->index); + error = Str_noop("invalid LADSPA descriptor"); + const LADSPA_Descriptor *d = p->ladspa; + if (!d || !d->instantiate || !d->connect_port || !d->cleanup || + !d->Label || !d->Name || d->PortCount > 65536 || + (d->PortCount && (!d->PortDescriptors || !d->PortNames || !d->PortRangeHints))) goto fail; + error = Str_noop("plugin has no render callback"); + if (!d->run && !(p->dssi && (p->dssi->run_synth || p->dssi->run_multiple_synths))) goto fail; + error = Str_noop("invalid port descriptor"); + for (unsigned long i = 0; i < d->PortCount; ++i) { + LADSPA_PortDescriptor flags = d->PortDescriptors[i]; + if (!!LADSPA_IS_PORT_AUDIO(flags) == !!LADSPA_IS_PORT_CONTROL(flags) || + !!LADSPA_IS_PORT_INPUT(flags) == !!LADSPA_IS_PORT_OUTPUT(flags) || + !d->PortNames[i]) goto fail; + } + p->handle = d->instantiate(d, (unsigned long)p->sample_rate); + error = Str_noop("cannot instantiate plugin"); + if (!p->handle) goto fail; + size_t ports = d->PortCount ? d->PortCount : 1; + p->audio = csound->Calloc(csound, ports * sizeof(*p->audio)); + p->control = csound->Calloc(csound, ports * sizeof(*p->control)); + p->mapping = csound->Malloc(csound, ports * sizeof(*p->mapping)); + p->inputs = csound->Malloc(csound, ports * sizeof(*p->inputs)); + p->outputs = csound->Malloc(csound, ports * sizeof(*p->outputs)); + for (unsigned long i = 0; i < d->PortCount; ++i) { + LADSPA_PortDescriptor flags = d->PortDescriptors[i]; + p->mapping[i] = DSSI_NONE; + if (LADSPA_IS_PORT_AUDIO(flags)) { + p->audio[i] = csound->Calloc(csound, p->capacity * sizeof(LADSPA_Data)); + d->connect_port(p->handle, i, p->audio[i]); + if (LADSPA_IS_PORT_INPUT(flags)) p->inputs[p->input_count++] = i; + else p->outputs[p->output_count++] = i; + } + else { + if (LADSPA_IS_PORT_INPUT(flags)) { + p->control[i] = default_control(p, i); + if (p->dssi && p->dssi->get_midi_controller_for_port) + p->mapping[i] = p->dssi->get_midi_controller_for_port(p->handle, i); + } + d->connect_port(p->handle, i, &p->control[i]); + } + } + h->plugins[h->count] = p; + *op->result = h->count++; + if (*op->verbose != 0) describe(csound, p); + return OK; +fail: + destroy_plugin(csound, p); + return csound->InitError(csound, "DSSI4CS: %s: %s\n", name, Str(error)); +} + +static int32_t dssiactivate_init(CSOUND *csound, DSSIACTIVATE *p) +{ + p->plugin = lookup(csound, *p->id); + return p->plugin ? OK : csound->InitError(csound, Str("DSSI4CS: invalid handle\n")); +} + +static int same_plugin(DSSI_PLUGIN *, DSSI_PLUGIN *); + +static int32_t dssiactivate(CSOUND *csound, DSSIACTIVATE *p) +{ + if (!integer(*p->trigger, 1)) + return csound->PerfError(csound, &p->h, Str("DSSI4CS: activation must be 0 or 1\n")); + DSSI_PLUGIN *q = p->plugin; + if (*p->trigger && !q->active && q->dssi && + !q->dssi->run_synth && q->dssi->run_multiple_synths) { + DSSI_HOST *h = host(csound); + for (unsigned int i = 0; i < h->count; ++i) + if (same_plugin(q, h->plugins[i]) && + h->plugins[i]->rendered_until > block_start(&p->h)) + return csound->PerfError(csound, &p->h, + Str("DSSI4CS: activate all grouped instances before rendering their block\n")); + } + set_active(q, *p->trigger != 0); + return OK; +} + +static int has_synth(DSSI_PLUGIN *p) +{ + return p && p->dssi && (p->dssi->run_synth || p->dssi->run_multiple_synths); +} + +static int32_t synth_handle(CSOUND *csound, cs_float id, DSSI_PLUGIN **p) +{ + *p = lookup(csound, id); + return has_synth(*p) ? OK : csound->InitError(csound, Str("DSSI4CS: handle is not a DSSI synth\n")); +} + +/* Stable insertion preserves note-on/note-off order at the same sample. */ +static void enqueue(DSSI_PLUGIN *p, int64_t time, snd_seq_event_t event) +{ + unsigned int i = p->queued++; + while (i && p->queue[i-1].time > time) { + p->queue[i] = p->queue[i-1]; + --i; + } + p->queue[i].time = time; + p->queue[i].event = event; +} + +static int32_t reserve_events(CSOUND *csound, OPDS *op, DSSI_PLUGIN *p, + unsigned int count, int64_t time) +{ + if (!p->active) + return csound->PerfError(csound, op, Str("DSSI4CS: activate the synth before sending events\n")); + if (time < p->rendered_until) + return csound->PerfError(csound, op, Str("DSSI4CS: send events before rendering their block\n")); + if (count > DSSI4CS_EVENTS - p->queued) + return csound->PerfError(csound, op, Str("DSSI4CS: event queue is full\n")); + return OK; +} + +static int32_t dssinote_init(CSOUND *csound, DSSINOTE *p) +{ + return synth_handle(csound, *p->id, &p->plugin); +} + +static int32_t dssinote(CSOUND *csound, DSSINOTE *p) +{ + if (*p->trigger == 0) return OK; + int64_t now = event_time(&p->h); + cs_double samples = (cs_double)*p->duration * p->plugin->sample_rate; + if (!integer(*p->note, 127) || !integer(*p->velocity, 127) || + !integer(*p->channel, 15) || !isfinite(samples) || samples < 0 || + samples > (cs_double)(INT64_MAX / 2)) + return csound->PerfError(csound, &p->h, Str("DSSI4CS: invalid note, velocity, channel or duration\n")); + if (reserve_events(csound, &p->h, p->plugin, *p->velocity ? 2 : 1, now)) return NOTOK; + snd_seq_event_t event = {0}; + event.type = *p->velocity ? SND_SEQ_EVENT_NOTEON : SND_SEQ_EVENT_NOTEOFF; + event.data.note.channel = (unsigned char)*p->channel; + event.data.note.note = (unsigned char)*p->note; + event.data.note.velocity = (unsigned char)*p->velocity; + enqueue(p->plugin, now, event); + if (*p->velocity) { + event.type = SND_SEQ_EVENT_NOTEOFF; + event.data.note.velocity = 0; + enqueue(p->plugin, now + (int64_t)llround(samples), event); + } + return OK; +} + +static int32_t dssinoteon_init(CSOUND *csound, DSSINOTEON *p) +{ + return synth_handle(csound, *p->id, &p->plugin); +} + +static int32_t dssinoteon(CSOUND *csound, DSSINOTEON *p) +{ + if (*p->trigger == 0) return OK; + if (!integer(*p->note, 127) || !integer(*p->velocity, 127)) + return csound->PerfError(csound, &p->h, Str("DSSI4CS: invalid note or velocity\n")); + int64_t time = event_time(&p->h); + if (reserve_events(csound, &p->h, p->plugin, 1, time)) return NOTOK; + snd_seq_event_t event = {0}; + event.type = *p->velocity ? SND_SEQ_EVENT_NOTEON : SND_SEQ_EVENT_NOTEOFF; + event.data.note.note = (unsigned char)*p->note; + event.data.note.velocity = (unsigned char)*p->velocity; + enqueue(p->plugin, time, event); + return OK; +} + +static int32_t dssievent_init(CSOUND *csound, DSSIEVENT *p) +{ + return synth_handle(csound, *p->id, &p->plugin); +} + +static int32_t dssievent(CSOUND *csound, DSSIEVENT *p) +{ + if (*p->trigger == 0) return OK; + if (!integer(*p->channel, 15) || !integer(*p->status, 0xe0) || + !integer(*p->data1, 127) || !integer(*p->data2, 127) || + !integer(*p->offset, (int64_t)GetLocalKsmps(&p->h) - GetKsmpsOffset(&p->h) - + GetEarlySmps(&p->h) - 1)) + return csound->PerfError(csound, &p->h, Str("DSSI4CS: invalid MIDI event or sample offset\n")); + snd_seq_event_t event = {0}; + int status = (int)*p->status, channel = (int)*p->channel; + int a = (int)*p->data1, b = (int)*p->data2; + if (status == 0x80 || status == 0x90 || status == 0xa0) { + event.type = status == 0xa0 ? SND_SEQ_EVENT_KEYPRESS : + status == 0x80 || b == 0 ? SND_SEQ_EVENT_NOTEOFF : SND_SEQ_EVENT_NOTEON; + event.data.note.channel = channel; + event.data.note.note = a; + event.data.note.velocity = b; + } + else { + event.data.control.channel = channel; + switch (status) { + case 0xb0: event.type = SND_SEQ_EVENT_CONTROLLER; event.data.control.param = a; + event.data.control.value = b; break; + case 0xc0: event.type = SND_SEQ_EVENT_PGMCHANGE; event.data.control.value = a; break; + case 0xd0: event.type = SND_SEQ_EVENT_CHANPRESS; event.data.control.value = a; break; + case 0xe0: event.type = SND_SEQ_EVENT_PITCHBEND; event.data.control.value = a + 128*b - 8192; break; + default: return csound->PerfError(csound, &p->h, Str("DSSI4CS: unsupported MIDI status\n")); + } + } + int64_t time = event_time(&p->h) + (int64_t)*p->offset; + if (reserve_events(csound, &p->h, p->plugin, 1, time)) return NOTOK; + enqueue(p->plugin, time, event); + return OK; +} + +static int32_t dssinrpn_init(CSOUND *csound, DSSINRPN *p) +{ + return synth_handle(csound, *p->id, &p->plugin); +} + +static int32_t dssinrpn(CSOUND *csound, DSSINRPN *p) +{ + if (*p->trigger == 0) return OK; + if (!integer(*p->channel, 15) || !integer(*p->parameter, 16383) || + !integer(*p->value, 16383) || + !integer(*p->offset, (int64_t)GetLocalKsmps(&p->h) - GetKsmpsOffset(&p->h) - + GetEarlySmps(&p->h) - 1)) + return csound->PerfError(csound, &p->h, Str("DSSI4CS: invalid NRPN event\n")); + int64_t time = event_time(&p->h) + (int64_t)*p->offset; + if (reserve_events(csound, &p->h, p->plugin, 1, time)) return NOTOK; + snd_seq_event_t event = {0}; + event.type = SND_SEQ_EVENT_NONREGPARAM; + event.data.control.channel = (unsigned char)*p->channel; + event.data.control.param = (unsigned int)*p->parameter; + event.data.control.value = (int)*p->value; + enqueue(p->plugin, time, event); + return OK; +} + +static int mapped(DSSI_PLUGIN *p, snd_seq_event_t *e, int apply) +{ + if (e->type != SND_SEQ_EVENT_CONTROLLER && e->type != SND_SEQ_EVENT_NONREGPARAM) return 0; + int found = 0; + for (unsigned long i = 0; i < p->ladspa->PortCount; ++i) { + int m = p->mapping[i]; + if (m == DSSI_NONE) continue; + int match = e->type == SND_SEQ_EVENT_CONTROLLER ? + (DSSI_IS_CC(m) && DSSI_CC_NUMBER(m) == e->data.control.param) : + (DSSI_IS_NRPN(m) && DSSI_NRPN_NUMBER(m) == e->data.control.param); + if (!match) continue; + found = 1; + if (apply) { + cs_double f = e->data.control.value / + (e->type == SND_SEQ_EVENT_CONTROLLER ? 127. : 16383.); + LADSPA_PortRangeHintDescriptor flags = p->ladspa->PortRangeHints[i].HintDescriptor; + cs_double value = interpolate(p, i, f); + if (LADSPA_IS_HINT_TOGGLED(flags)) value = f >= .5; + else if (LADSPA_IS_HINT_INTEGER(flags)) value = round(value); + p->control[i] = (LADSPA_Data)value; + } + } + return found; +} + +static int host_event(DSSI_PLUGIN *p, snd_seq_event_t *e) +{ + return e->type == SND_SEQ_EVENT_PGMCHANGE || + (e->type == SND_SEQ_EVENT_CONTROLLER && + (e->data.control.param == 0 || e->data.control.param == 32)) || mapped(p, e, 0); +} + +static void apply_event(DSSI_PLUGIN *p, snd_seq_event_t *e) +{ + unsigned int ch = e->data.control.channel; + if (e->type == SND_SEQ_EVENT_PGMCHANGE) { + if (p->dssi->select_program) + p->dssi->select_program(p->handle, p->bank[ch], e->data.control.value); + } + else if (e->type == SND_SEQ_EVENT_CONTROLLER && e->data.control.param == 0) + p->bank[ch] = (p->bank[ch] & 127) | (e->data.control.value << 7); + else if (e->type == SND_SEQ_EVENT_CONTROLLER && e->data.control.param == 32) + p->bank[ch] = (p->bank[ch] & (127 << 7)) | e->data.control.value; + else mapped(p, e, 1); +} + +static int same_plugin(DSSI_PLUGIN *a, DSSI_PLUGIN *b) +{ + return a->library == b->library && !strcmp(a->ladspa->Label, b->ladspa->Label); +} + +static void connect_audio(DSSI_PLUGIN *p, uint32_t offset) +{ + for (unsigned long i = 0; i < p->ladspa->PortCount; ++i) + if (p->audio[i]) p->ladspa->connect_port(p->handle, i, p->audio[i] + offset); +} + +/* Split only at host-handled MIDI events, so controls/programs change at the + requested sample. Other MIDI retains its relative timestamp within a run. */ +static void render(DSSI_PLUGIN **group, unsigned long count, int multiple, + int64_t start, uint32_t frames, uint32_t offset) +{ + int64_t cursor = start, end = start + frames; + LADSPA_Handle handles[DSSI4CS_INSTANCES]; + snd_seq_event_t *events[DSSI4CS_INSTANCES]; + unsigned long counts[DSSI4CS_INSTANCES]; + while (cursor < end) { + int64_t next = end; + for (unsigned long g = 0; g < count; ++g) { + DSSI_PLUGIN *p = group[g]; + for (unsigned int i = 0; i < p->queued; ++i) + if (p->queue[i].time > cursor && p->queue[i].time < next && + host_event(p, &p->queue[i].event)) next = p->queue[i].time; + } + for (unsigned long g = 0; g < count; ++g) { + DSSI_PLUGIN *p = group[g]; + unsigned int used = 0; + p->event_count = 0; + if (p->panic) { + /* A plugin may omit activate/deactivate. End old notes before + delivering new notes when that instance resumes. */ + for (int channel = 0; channel < 16; ++channel) { + const unsigned int controllers[] = {64, 120, 123}; + for (unsigned int i = 0; i < 3; ++i) { + snd_seq_event_t event = {0}; + event.type = SND_SEQ_EVENT_CONTROLLER; + event.data.control.channel = channel; + event.data.control.param = controllers[i]; + if (!mapped(p, &event, 1)) p->events[p->event_count++] = event; + } + } + for (int channel = 0; channel < 16; ++channel) + for (int note = 0; note < 128; ++note) + if (p->notes[channel][note]) { + snd_seq_event_t *e = &p->events[p->event_count++]; + memset(e, 0, sizeof(*e)); + e->type = SND_SEQ_EVENT_NOTEOFF; + e->data.note.channel = channel; + e->data.note.note = note; + } + memset(p->notes, 0, sizeof(p->notes)); + p->panic = 0; + } + while (used < p->queued && p->queue[used].time < next) { + DSSI_EVENT *q = &p->queue[used++]; + if (host_event(p, &q->event)) apply_event(p, &q->event); + else { + snd_seq_event_t *e = &p->events[p->event_count++]; + *e = q->event; + if (e->type == SND_SEQ_EVENT_NOTEON || e->type == SND_SEQ_EVENT_NOTEOFF) + p->notes[e->data.note.channel][e->data.note.note] = e->type == SND_SEQ_EVENT_NOTEON; + e->time.tick = q->time > cursor ? (unsigned int)(q->time - cursor) : 0; + } + } + p->queued -= used; + memmove(p->queue, p->queue + used, p->queued * sizeof(*p->queue)); + connect_audio(p, offset + (uint32_t)(cursor - start)); + handles[g] = p->handle; + events[g] = p->events; + counts[g] = p->event_count; + } + DSSI_PLUGIN *p = group[0]; + if (multiple) + p->dssi->run_multiple_synths(count, handles, (unsigned long)(next-cursor), events, counts); + else if (has_synth(p)) + p->dssi->run_synth(p->handle, (unsigned long)(next-cursor), events[0], counts[0]); + else p->ladspa->run(p->handle, (unsigned long)(next-cursor)); + cursor = next; + } + for (unsigned long g = 0; g < count; ++g) { + group[g]->rendered_until = end; + connect_audio(group[g], 0); + } +} + +static int32_t dssiaudio_init(CSOUND *csound, DSSIAUDIO *p) +{ + p->plugin = lookup(csound, *p->id); + DSSI_PLUGIN *q = p->plugin; + if (!q) return csound->InitError(csound, Str("DSSI4CS: invalid handle\n")); + if (q->owner && q->owner != &p->h) + return csound->InitError(csound, Str("DSSI4CS: each instance needs exactly one audio renderer\n")); + if (GetLocalKsmps(&p->h) > q->capacity || + GetInputArgCnt(&p->h) - 1 > q->input_count || GetOutputArgCnt(&p->h) > q->output_count) + return csound->InitError(csound, Str("DSSI4CS: audio port count or ksmps mismatch\n")); + q->render_ksmps = GetLocalKsmps(&p->h); + q->owner = &p->h; + return OK; +} + +static int32_t dssisynth_init(CSOUND *csound, DSSIAUDIO *p) +{ + if (synth_handle(csound, *p->id, &p->plugin)) return NOTOK; + return dssiaudio_init(csound, p); +} + +static int32_t dssiaudio_deinit(CSOUND *csound, DSSIAUDIO *p) +{ + (void)csound; + if (p->plugin && p->plugin->owner == &p->h) { + p->plugin->owner = NULL; + set_active(p->plugin, 0); + } + return OK; +} + +static int32_t dssiaudio(CSOUND *csound, DSSIAUDIO *p) +{ + DSSI_PLUGIN *q = p->plugin; + uint32_t size = GetLocalKsmps(&p->h), offset = GetKsmpsOffset(&p->h); + uint32_t end = size - GetEarlySmps(&p->h); + int inputs = GetInputArgCnt(&p->h) - 1, outputs = GetOutputArgCnt(&p->h); + for (int i = 0; i < outputs; ++i) memset(p->out[i], 0, size * sizeof(cs_float)); + if (!q->active || offset >= end) return OK; + int64_t block = block_start(&p->h); + if (q->last_block != block) { + if (block + offset < q->rendered_until) + return csound->PerfError(csound, &p->h, Str("DSSI4CS: these samples have already rendered\n")); + for (unsigned long i = 0; i < q->input_count; ++i) { + LADSPA_Data *data = q->audio[q->inputs[i]]; + memset(data, 0, size * sizeof(*data)); + if (i < (unsigned long)inputs) + for (uint32_t n = offset; n < end; ++n) + data[n] = (LADSPA_Data)(p->in[i][n] / csound->Get0dBFS(csound)); + } + DSSI_PLUGIN *group[DSSI4CS_INSTANCES] = {q}; + unsigned long count = 1; + int multiple = has_synth(q) && !q->dssi->run_synth; + if (multiple) { + DSSI_HOST *h = host(csound); + for (unsigned int i = 0; i < h->count; ++i) { + DSSI_PLUGIN *other = h->plugins[i]; + if (other != q && other->active && same_plugin(q, other)) { + if (other->rendered_until > block + offset) + return csound->PerfError(csound, &p->h, + Str("DSSI4CS: activate all grouped instances before rendering their block\n")); + group[count++] = other; + } + } + if (count > 1) { + for (unsigned long i = 0; i < count; ++i) + if (group[i]->input_count || group[i]->render_ksmps != size || + !group[i]->owner || GetKsmpsOffset(group[i]->owner) || + GetEarlySmps(group[i]->owner) || offset || end != size) + return csound->PerfError(csound, &p->h, + Str("DSSI4CS: grouped synths need no audio inputs, matching ksmps and full blocks\n")); + } + } + render(group, count, multiple, block + offset, end - offset, offset); + for (unsigned long i = 0; i < count; ++i) group[i]->last_block = block; + } + for (int i = 0; i < outputs; ++i) + for (uint32_t n = offset; n < end; ++n) + p->out[i][n] = q->audio[q->outputs[i]][n] * csound->Get0dBFS(csound); + return OK; +} + +static int control_port(DSSI_PLUGIN *p, cs_float port, int input) +{ + if (!p || !p->ladspa->PortCount || + !integer(port, (int64_t)(p->ladspa->PortCount - 1))) return 0; + LADSPA_PortDescriptor flags = p->ladspa->PortDescriptors[(unsigned long)port]; + return LADSPA_IS_PORT_CONTROL(flags) && (!input || LADSPA_IS_PORT_INPUT(flags)); +} + +static int32_t dssictls_init(CSOUND *csound, DSSICTLS *p) +{ + p->plugin = lookup(csound, *p->id); + if (!control_port(p->plugin, *p->port, 1)) + return csound->InitError(csound, Str("DSSI4CS: expected an input control port\n")); + p->index = (unsigned long)*p->port; + return OK; +} + +static int32_t dssictls(CSOUND *csound, DSSICTLS *p) +{ + if (*p->trigger == 0) return OK; + cs_double value = *p->value; + if (LADSPA_IS_HINT_SAMPLE_RATE(p->plugin->ladspa->PortRangeHints[p->index].HintDescriptor)) + value *= p->plugin->sample_rate; + if (!isfinite(value) || fabs(value) > FLT_MAX) + return csound->PerfError(csound, &p->h, Str("DSSI4CS: invalid control value\n")); + p->plugin->control[p->index] = (LADSPA_Data)value; + return OK; +} + +static int32_t dssiget_init(CSOUND *csound, DSSIGET *p) +{ + p->plugin = lookup(csound, *p->id); + if (!control_port(p->plugin, *p->port, 0)) + return csound->InitError(csound, Str("DSSI4CS: expected a control port\n")); + p->index = (unsigned long)*p->port; + *p->value = p->plugin->control[p->index]; + return OK; +} + +static int32_t dssiget(CSOUND *csound, DSSIGET *p) +{ + (void)csound; + *p->value = p->plugin->control[p->index]; + return OK; +} + +static int32_t dssiprogram_init(CSOUND *csound, DSSIPROGRAM *p) +{ + p->plugin = lookup(csound, *p->id); + if (!p->plugin || !p->plugin->dssi || !p->plugin->dssi->select_program) + return csound->InitError(csound, Str("DSSI4CS: plugin does not support programs\n")); + return OK; +} + +static int32_t dssiprogram(CSOUND *csound, DSSIPROGRAM *p) +{ + if (*p->trigger == 0) return OK; + if (!integer(*p->bank, UINT32_MAX) || !integer(*p->program, UINT32_MAX)) + return csound->PerfError(csound, &p->h, Str("DSSI4CS: invalid bank or program\n")); + p->plugin->dssi->select_program(p->plugin->handle, (unsigned long)*p->bank, + (unsigned long)*p->program); + return OK; +} + +static int32_t dssiconfigure(CSOUND *csound, DSSICONFIGURE *p) +{ + DSSI_PLUGIN *q = lookup(csound, *p->id); + if (!q || !q->dssi || !q->dssi->configure) + return csound->InitError(csound, Str("DSSI4CS: plugin does not support configuration\n")); + DSSI_HOST *h = host(csound); + int global = !strncmp(p->key->data, DSSI_GLOBAL_CONFIGURE_PREFIX, + strlen(DSSI_GLOBAL_CONFIGURE_PREFIX)); + for (unsigned int i = 0; i < h->count; ++i) { + DSSI_PLUGIN *target = h->plugins[i]; + if (target != q && !(global && same_plugin(q, target))) continue; + char *error = target->dssi->configure(target->handle, p->key->data, p->value->data); + if (error) { + int32_t result = csound->InitError(csound, "DSSI4CS: configure: %s\n", error); + free(error); /* DSSI specifies malloc/free ownership for this string. */ + return result; + } + } + return OK; +} + +static int32_t dssiprograminfo(CSOUND *csound, DSSIPROGRAMINFO *p) +{ + DSSI_PLUGIN *q = lookup(csound, *p->id); + if (!q || !q->dssi || !q->dssi->get_program || !integer(*p->index, UINT32_MAX)) + return csound->InitError(csound, Str("DSSI4CS: cannot enumerate programs\n")); + const DSSI_Program_Descriptor *program = q->dssi->get_program(q->handle, (unsigned long)*p->index); + const char *name = program && program->Name ? program->Name : ""; + size_t size = strlen(name) + 1; + if (p->name->size < size) { + p->name->data = csound->ReAlloc(csound, p->name->data, size); + p->name->size = size; + } + memcpy(p->name->data, name, size); + *p->bank = program ? (cs_float)program->Bank : -1; + *p->program = program ? (cs_float)program->Program : -1; + return OK; +} + +static int32_t dssiinfo(CSOUND *csound, DSSIINFO *p) +{ + DSSI_PLUGIN *q = lookup(csound, *p->id); + if (!q) return csound->InitError(csound, Str("DSSI4CS: invalid handle\n")); + describe(csound, q); + return OK; +} + +static void list_directory(CSOUND *csound, const char *directory) +{ + DIR *dir = opendir(directory); + if (!dir) return; + struct dirent *entry; + while ((entry = readdir(dir))) { + if (entry->d_name[0] == '.') continue; + size_t size = strlen(directory) + strlen(entry->d_name) + 2; + char *path = csound->Malloc(csound, size); + snprintf(path, size, "%s/%s", directory, entry->d_name); + void *library = dlopen(path, RTLD_LAZY | RTLD_LOCAL); + if (library) { + DSSI_Descriptor_Function df = (DSSI_Descriptor_Function)dlsym(library, "dssi_descriptor"); + LADSPA_Descriptor_Function lf = (LADSPA_Descriptor_Function)dlsym(library, "ladspa_descriptor"); + for (unsigned long i = 0; df || lf; ++i) { + const DSSI_Descriptor *d = df ? df(i) : NULL; + const LADSPA_Descriptor *l = df ? (d ? d->LADSPA_Plugin : NULL) : lf(i); + if (!l) break; + csound->Message(csound, "%s: %lu: %s (%s)\n", path, i, l->Name, l->Label); + } + dlclose(library); + } + csound->Free(csound, path); + } + closedir(dir); +} + +static int32_t dssilist(CSOUND *csound, DSSILIST *p) +{ + (void)p; + const char *paths[] = {csound->GetEnv(csound, "DSSI_PATH"), + csound->GetEnv(csound, "LADSPA_PATH"), DSSI4CS_DEFAULT_PATH}; + for (unsigned int i = 0; i < sizeof(paths)/sizeof(paths[0]); ++i) { + const char *start = paths[i]; + while (start && *start) { + const char *end = strchr(start, ':'); + size_t length = end ? (size_t)(end-start) : strlen(start); + if (length) { + char *path = csound->Malloc(csound, length+1); + memcpy(path, start, length); + path[length] = '\0'; + list_directory(csound, path); + csound->Free(csound, path); + } + start = end ? end+1 : NULL; + } + } + return OK; +} + +static OENTRY localops[] = { + {"dssiinit", sizeof(DSSIINIT), 0, "i", "Tip", (SUBR)dssiinit}, + {"dssiactivate", sizeof(DSSIACTIVATE), 0, "", "ik", (SUBR)dssiactivate_init, (SUBR)dssiactivate}, + /* The audio inputs are optional (see README) and must be a-rate. A single + 'y' (indefinite a-rate list) entry would make every input mandatory, + because the parser only treats the 'M'/'N'/'m'/'W' lists as omittable. + Register one entry per possible input count instead: fixed 'a' inputs + are a-rate only (so the opcode never reads past a scalar) and the + matching entry is selected by argument count, allowing the inputs to be + omitted. */ + {"dssiaudio", sizeof(DSSIAUDIO), 0, "mmmmmmmmm", "i", (SUBR)dssiaudio_init, (SUBR)dssiaudio, (SUBR)dssiaudio_deinit}, + {"dssiaudio", sizeof(DSSIAUDIO), 0, "mmmmmmmmm", "ia", (SUBR)dssiaudio_init, (SUBR)dssiaudio, (SUBR)dssiaudio_deinit}, + {"dssiaudio", sizeof(DSSIAUDIO), 0, "mmmmmmmmm", "iaa", (SUBR)dssiaudio_init, (SUBR)dssiaudio, (SUBR)dssiaudio_deinit}, + {"dssiaudio", sizeof(DSSIAUDIO), 0, "mmmmmmmmm", "iaaa", (SUBR)dssiaudio_init, (SUBR)dssiaudio, (SUBR)dssiaudio_deinit}, + {"dssiaudio", sizeof(DSSIAUDIO), 0, "mmmmmmmmm", "iaaaa", (SUBR)dssiaudio_init, (SUBR)dssiaudio, (SUBR)dssiaudio_deinit}, + {"dssiaudio", sizeof(DSSIAUDIO), 0, "mmmmmmmmm", "iaaaaa", (SUBR)dssiaudio_init, (SUBR)dssiaudio, (SUBR)dssiaudio_deinit}, + {"dssiaudio", sizeof(DSSIAUDIO), 0, "mmmmmmmmm", "iaaaaaa", (SUBR)dssiaudio_init, (SUBR)dssiaudio, (SUBR)dssiaudio_deinit}, + {"dssiaudio", sizeof(DSSIAUDIO), 0, "mmmmmmmmm", "iaaaaaaa", (SUBR)dssiaudio_init, (SUBR)dssiaudio, (SUBR)dssiaudio_deinit}, + {"dssiaudio", sizeof(DSSIAUDIO), 0, "mmmmmmmmm", "iaaaaaaaa", (SUBR)dssiaudio_init, (SUBR)dssiaudio, (SUBR)dssiaudio_deinit}, + {"dssiaudio", sizeof(DSSIAUDIO), 0, "mmmmmmmmm", "iaaaaaaaaa", (SUBR)dssiaudio_init, (SUBR)dssiaudio, (SUBR)dssiaudio_deinit}, + {"dssisynth", sizeof(DSSIAUDIO), 0, "mmmmmmmmm", "i", (SUBR)dssisynth_init, (SUBR)dssiaudio, (SUBR)dssiaudio_deinit}, + {"dssisynth", sizeof(DSSIAUDIO), 0, "mmmmmmmmm", "ia", (SUBR)dssisynth_init, (SUBR)dssiaudio, (SUBR)dssiaudio_deinit}, + {"dssisynth", sizeof(DSSIAUDIO), 0, "mmmmmmmmm", "iaa", (SUBR)dssisynth_init, (SUBR)dssiaudio, (SUBR)dssiaudio_deinit}, + {"dssisynth", sizeof(DSSIAUDIO), 0, "mmmmmmmmm", "iaaa", (SUBR)dssisynth_init, (SUBR)dssiaudio, (SUBR)dssiaudio_deinit}, + {"dssisynth", sizeof(DSSIAUDIO), 0, "mmmmmmmmm", "iaaaa", (SUBR)dssisynth_init, (SUBR)dssiaudio, (SUBR)dssiaudio_deinit}, + {"dssisynth", sizeof(DSSIAUDIO), 0, "mmmmmmmmm", "iaaaaa", (SUBR)dssisynth_init, (SUBR)dssiaudio, (SUBR)dssiaudio_deinit}, + {"dssisynth", sizeof(DSSIAUDIO), 0, "mmmmmmmmm", "iaaaaaa", (SUBR)dssisynth_init, (SUBR)dssiaudio, (SUBR)dssiaudio_deinit}, + {"dssisynth", sizeof(DSSIAUDIO), 0, "mmmmmmmmm", "iaaaaaaa", (SUBR)dssisynth_init, (SUBR)dssiaudio, (SUBR)dssiaudio_deinit}, + {"dssisynth", sizeof(DSSIAUDIO), 0, "mmmmmmmmm", "iaaaaaaaa", (SUBR)dssisynth_init, (SUBR)dssiaudio, (SUBR)dssiaudio_deinit}, + {"dssisynth", sizeof(DSSIAUDIO), 0, "mmmmmmmmm", "iaaaaaaaaa", (SUBR)dssisynth_init, (SUBR)dssiaudio, (SUBR)dssiaudio_deinit}, + {"dssictls", sizeof(DSSICTLS), 0, "", "iikk", (SUBR)dssictls_init, (SUBR)dssictls}, + {"dssiget", sizeof(DSSIGET), 0, "k", "ii", (SUBR)dssiget_init, (SUBR)dssiget}, + {"dssinote", sizeof(DSSINOTE), 0, "", "kikkko", (SUBR)dssinote_init, (SUBR)dssinote}, + {"dssievent", sizeof(DSSINOTEON), 0, "", "kikk", (SUBR)dssinoteon_init, (SUBR)dssinoteon}, + {"dssievent", sizeof(DSSIEVENT), 0, "", "kikkkkO", (SUBR)dssievent_init, (SUBR)dssievent}, + {"dssinrpn", sizeof(DSSINRPN), 0, "", "kikkkO", (SUBR)dssinrpn_init, (SUBR)dssinrpn}, + {"dssiprogram", sizeof(DSSIPROGRAM), 0, "", "ikkk", (SUBR)dssiprogram_init, (SUBR)dssiprogram}, + {"dssiconfigure", sizeof(DSSICONFIGURE), 0, "", "iSS", (SUBR)dssiconfigure}, + {"dssiprograminfo", sizeof(DSSIPROGRAMINFO), 0, "Sii", "ii", (SUBR)dssiprograminfo}, + {"dssiinfo", sizeof(DSSIINFO), 0, "", "i", (SUBR)dssiinfo}, + {"dssilist", sizeof(DSSILIST), 0, "", "", (SUBR)dssilist} +}; +LINKAGE diff --git a/src/dssi4cs/dssi4cs.h b/src/dssi4cs/dssi4cs.h new file mode 100644 index 00000000..c83c083a --- /dev/null +++ b/src/dssi4cs/dssi4cs.h @@ -0,0 +1,138 @@ +/* + * Copyright (C) 2005 Andres Cabrera + * The dssi4cs library is free software; you can redistribute it + * and/or modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * The dssi4cs library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with The dssi4cs library; if not, write to the Free Software + * Foundation, Inc., 31 Milk Street, #960789, Boston, MA, 02196, USA + */ + +#ifndef DSSI4CS_H +#define DSSI4CS_H +#include "csdl.h" +#include +#include + +#define DSSI4CS_CHANNELS 9 +#define DSSI4CS_EVENTS 1024 +#define DSSI4CS_INSTANCES 256 + +typedef struct { + int64_t time; + snd_seq_event_t event; +} DSSI_EVENT; + +typedef struct DSSI_PLUGIN_ { + const LADSPA_Descriptor *ladspa; + const DSSI_Descriptor *dssi; + void *library; + LADSPA_Handle handle; + LADSPA_Data *control; + LADSPA_Data **audio; + int *mapping; + unsigned long *inputs, *outputs; + unsigned long input_count, output_count; + uint32_t capacity, render_ksmps; + cs_float sample_rate; + int active; + OPDS *owner; + int64_t last_block, rendered_until; + DSSI_EVENT queue[DSSI4CS_EVENTS]; + unsigned int queued; + snd_seq_event_t events[DSSI4CS_EVENTS + 16 * (128 + 3)]; + unsigned char notes[16][128]; + int panic; + unsigned long event_count; + unsigned int bank[16]; +} DSSI_PLUGIN; + +typedef struct { + unsigned int count; + DSSI_PLUGIN *plugins[DSSI4CS_INSTANCES]; +} DSSI_HOST; + +typedef struct { + OPDS h; + cs_float *result, *filename, *index, *verbose; +} DSSIINIT; + +typedef struct { + OPDS h; + cs_float *id, *trigger; + DSSI_PLUGIN *plugin; +} DSSIACTIVATE; + +typedef struct { + OPDS h; + cs_float *out[DSSI4CS_CHANNELS]; + cs_float *id, *in[DSSI4CS_CHANNELS]; + DSSI_PLUGIN *plugin; +} DSSIAUDIO; + +typedef struct { + OPDS h; + cs_float *id, *port, *value, *trigger; + DSSI_PLUGIN *plugin; + unsigned long index; +} DSSICTLS; + +typedef struct { + OPDS h; + cs_float *value, *id, *port; + DSSI_PLUGIN *plugin; + unsigned long index; +} DSSIGET; + +typedef struct { + OPDS h; + cs_float *trigger, *id, *note, *velocity, *duration, *channel; + DSSI_PLUGIN *plugin; +} DSSINOTE; + +typedef struct { + OPDS h; + cs_float *trigger, *id, *status, *channel, *data1, *data2, *offset; + DSSI_PLUGIN *plugin; +} DSSIEVENT; + +typedef struct { + OPDS h; + cs_float *trigger, *id, *channel, *parameter, *value, *offset; + DSSI_PLUGIN *plugin; +} DSSINRPN; + +typedef struct { + OPDS h; + cs_float *trigger, *id, *note, *velocity; + DSSI_PLUGIN *plugin; +} DSSINOTEON; + +typedef struct { + OPDS h; + cs_float *id, *bank, *program, *trigger; + DSSI_PLUGIN *plugin; +} DSSIPROGRAM; + +typedef struct { + OPDS h; + cs_float *id; + STRINGDAT *key, *value; +} DSSICONFIGURE; + +typedef struct { + OPDS h; + STRINGDAT *name; + cs_float *bank, *program, *id, *index; +} DSSIPROGRAMINFO; + +typedef struct { OPDS h; cs_float *id; } DSSIINFO; +typedef struct { OPDS h; } DSSILIST; +#endif diff --git a/src/dssi4cs/include/dssi/LICENSE b/src/dssi4cs/include/dssi/LICENSE new file mode 100644 index 00000000..6a6fbce0 --- /dev/null +++ b/src/dssi4cs/include/dssi/LICENSE @@ -0,0 +1,509 @@ + +*** NOTE: This license applies to the DSSI header file. See README +*** for a summary of the licensing of other files, and the individual +*** source code for more details. + + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. + + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. + + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. + + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. + + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. + + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. + + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under any +particular circumstance, the balance of the section is intended to apply, +and the section as a whole is intended to apply in other circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License may add +an explicit geographical distribution limitation excluding those countries, +so that distribution is permitted only in or among countries not thus +excluded. In such case, this License incorporates the limitation as if +written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. + + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS + + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms of the +ordinary General Public License). + + To apply these terms, attach the following notices to the library. It is +safest to attach them to the start of each source file to most effectively +convey the exclusion of warranty; and each file should have at least the +"copyright" line and a pointer to where the full notice is found. + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + +Also add information on how to contact you by electronic and paper mail. + +You should also get your employer (if you work as a programmer) or your +school, if any, to sign a "copyright disclaimer" for the library, if +necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + diff --git a/src/dssi4cs/include/dssi/README.md b/src/dssi4cs/include/dssi/README.md new file mode 100644 index 00000000..99103b8b --- /dev/null +++ b/src/dssi4cs/include/dssi/README.md @@ -0,0 +1,18 @@ +# DSSI + +Unmodified files from [release 1.1.1](https://downloads.sourceforge.net/project/dssi/dssi/1.1.1/dssi-1.1.1.tar.gz). + +Only the DSSI API header is vendored. +The header uses LGPL-2.1-or-later; see [LICENSE](LICENSE) and its license notice. + +[SOURCE.json](SOURCE.json) records the archive URL, SHA-256, and file paths. +Keep platform adapters in the plugin's own sources, outside this directory. + +Run `./update.py` for the latest stable release, `./update.py VERSION` for a +specific release, or `./update.py --pinned` to reproduce this copy and check its +archive hash. Downloads and extraction finish before checked-in files change. +The script requires curl and Python 3 and works from any directory. + +After an update, review the source and license diff, rebuild the DSSI/LADSPA +plugin on Linux, and verify that a native synth or effect still loads. Commit +the header, license, README, and SOURCE.json together. diff --git a/src/dssi4cs/include/dssi/SOURCE.json b/src/dssi4cs/include/dssi/SOURCE.json new file mode 100644 index 00000000..e91e3981 --- /dev/null +++ b/src/dssi4cs/include/dssi/SOURCE.json @@ -0,0 +1,9 @@ +{ + "version": "1.1.1", + "url": "https://downloads.sourceforge.net/project/dssi/dssi/1.1.1/dssi-1.1.1.tar.gz", + "sha256": "f2c82b073a947c8255284249097667f9b14e660bf86186f3fcd3b3b3e087814e", + "files": { + "dssi.h": "dssi-1.1.1/dssi/dssi.h", + "LICENSE": "dssi-1.1.1/COPYING" + } +} diff --git a/src/dssi4cs/include/dssi/dssi.h b/src/dssi4cs/include/dssi/dssi.h new file mode 100644 index 00000000..fe4a20b3 --- /dev/null +++ b/src/dssi4cs/include/dssi/dssi.h @@ -0,0 +1,426 @@ +/* -*- c-basic-offset: 4 -*- */ + +/* dssi.h + + DSSI version 1.0 + Copyright (c) 2004, 2009 Chris Cannam, Steve Harris and Sean Bolton + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, + MA 02110-1301 USA +*/ + +#ifndef DSSI_INCLUDED +#define DSSI_INCLUDED + +#include +#include + +#define DSSI_VERSION "1.0" +#define DSSI_VERSION_MAJOR 1 +#define DSSI_VERSION_MINOR 0 + +#ifdef __cplusplus +extern "C" { +#endif + +/* + There is a need for an API that supports hosted MIDI soft synths + with GUIs in Linux audio applications. In time the GMPI initiative + should comprehensively address this need, but the requirement for + Linux applications to be able to support simple hosted synths is + here now, and GMPI is not. This proposal (the "DSSI Soft Synth + Interface" or DSSI, pronounced "dizzy") aims to provide a simple + solution in a way that we hope will prove complete and compelling + enough to support now, yet not so compelling as to supplant GMPI or + any other comprehensive future proposal. + + For simplicity and familiarity, this API is based as far as + possible on existing work -- the LADSPA plugin API for control + values and audio processing, and the ALSA sequencer event types for + MIDI event communication. The GUI part of the proposal is quite + new, but may also be applicable retroactively to LADSPA plugins + that do not otherwise support this synth interface. +*/ + +typedef struct _DSSI_Program_Descriptor { + + /** Bank number for this program. Note that DSSI does not support + MIDI-style separation of bank LSB and MSB values. There is no + restriction on the set of available banks: the numbers do not + need to be contiguous, there does not need to be a bank 0, etc. */ + unsigned long Bank; + + /** Program number (unique within its bank) for this program. + There is no restriction on the set of available programs: the + numbers do not need to be contiguous, there does not need to + be a program 0, etc. */ + unsigned long Program; + + /** Name of the program. */ + const char * Name; + +} DSSI_Program_Descriptor; + + +typedef struct _DSSI_Descriptor { + + /** + * DSSI_API_Version + * + * This member indicates the DSSI API level used by this plugin. + * If we're lucky, this will never be needed. For now all plugins + * must set it to 1. + */ + int DSSI_API_Version; + + /** + * LADSPA_Plugin + * + * A DSSI synth plugin consists of a LADSPA plugin plus an + * additional framework for controlling program settings and + * transmitting MIDI events. A plugin must fully implement the + * LADSPA descriptor fields as well as the required LADSPA + * functions including instantiate() and (de)activate(). It + * should also implement run(), with the same behaviour as if + * run_synth() (below) were called with no synth events. + * + * In order to instantiate a synth the host calls the LADSPA + * instantiate function, passing in this LADSPA_Descriptor + * pointer. The returned LADSPA_Handle is used as the argument + * for the DSSI functions below as well as for the LADSPA ones. + */ + const LADSPA_Descriptor *LADSPA_Plugin; + + /** + * configure() + * + * This member is a function pointer that sends a piece of + * configuration data to the plugin. The key argument specifies + * some aspect of the synth's configuration that is to be changed, + * and the value argument specifies a new value for it. A plugin + * that does not require this facility at all may set this member + * to NULL. + * + * This call is intended to set some session-scoped aspect of a + * plugin's behaviour, for example to tell the plugin to load + * sample data from a particular file. The plugin should act + * immediately on the request. The call should return NULL on + * success, or an error string that may be shown to the user. The + * host will free the returned value after use if it is non-NULL. + * + * Calls to configure() are not automated as timed events. + * Instead, a host should remember the last value associated with + * each key passed to configure() during a given session for a + * given plugin instance, and should call configure() with the + * correct value for each key the next time it instantiates the + * "same" plugin instance, for example on reloading a project in + * which the plugin was used before. Plugins should note that a + * host may typically instantiate a plugin multiple times with the + * same configuration values, and should share data between + * instances where practical. + * + * Calling configure() completely invalidates the program and bank + * information last obtained from the plugin. + * + * Reserved and special key prefixes + * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + * The DSSI: prefix + * ---------------- + * Configure keys starting with DSSI: are reserved for particular + * purposes documented in the DSSI specification. At the moment, + * there is one such key: DSSI:PROJECT_DIRECTORY. A host may call + * configure() passing this key and a directory path value. This + * indicates to the plugin and its UI that a directory at that + * path exists and may be used for project-local data. Plugins + * may wish to use the project directory as a fallback location + * when looking for other file data, or as a base for relative + * paths in other configuration values. + * + * The GLOBAL: prefix + * ------------------ + * Configure keys starting with GLOBAL: may be used by the plugin + * and its UI for any purpose, but are treated specially by the + * host. When one of these keys is used in a configure OSC call + * from the plugin UI, the host makes the corresponding configure + * call (preserving the GLOBAL: prefix) not only to the target + * plugin but also to all other plugins in the same instance + * group, as well as their UIs. Note that if any instance + * returns non-NULL from configure to indicate error, the host + * may stop there (and the set of plugins on which configure has + * been called will thus depend on the host implementation). + * See also the configure OSC call documentation in RFC.txt. + */ + char *(*configure)(LADSPA_Handle Instance, + const char *Key, + const char *Value); + + #define DSSI_RESERVED_CONFIGURE_PREFIX "DSSI:" + #define DSSI_GLOBAL_CONFIGURE_PREFIX "GLOBAL:" + #define DSSI_PROJECT_DIRECTORY_KEY \ + DSSI_RESERVED_CONFIGURE_PREFIX "PROJECT_DIRECTORY" + + /** + * get_program() + * + * This member is a function pointer that provides a description + * of a program (named preset sound) available on this synth. A + * plugin that does not support programs at all should set this + * member to NULL. + * + * The Index argument is an index into the plugin's list of + * programs, not a program number as represented by the Program + * field of the DSSI_Program_Descriptor. (This distinction is + * needed to support synths that use non-contiguous program or + * bank numbers.) + * + * This function returns a DSSI_Program_Descriptor pointer that is + * guaranteed to be valid only until the next call to get_program, + * deactivate, or configure, on the same plugin instance. This + * function must return NULL if passed an Index argument out of + * range, so that the host can use it to query the number of + * programs as well as their properties. + */ + const DSSI_Program_Descriptor *(*get_program)(LADSPA_Handle Instance, + unsigned long Index); + + /** + * select_program() + * + * This member is a function pointer that selects a new program + * for this synth. The program change should take effect + * immediately at the start of the next run_synth() call. (This + * means that a host providing the capability of changing programs + * between any two notes on a track must vary the block size so as + * to place the program change at the right place. A host that + * wanted to avoid this would probably just instantiate a plugin + * for each program.) + * + * A plugin that does not support programs at all should set this + * member NULL. Plugins should ignore a select_program() call + * with an invalid bank or program. + * + * A plugin is not required to select any particular default + * program on activate(): it's the host's duty to set a program + * explicitly. The current program is invalidated by any call to + * configure(). + * + * A plugin is permitted to re-write the values of its input + * control ports when select_program is called. The host should + * re-read the input control port values and update its own + * records appropriately. (This is the only circumstance in + * which a DSSI plugin is allowed to modify its own input ports.) + */ + void (*select_program)(LADSPA_Handle Instance, + unsigned long Bank, + unsigned long Program); + + /** + * get_midi_controller_for_port() + * + * This member is a function pointer that returns the MIDI + * controller number or NRPN that should be mapped to the given + * input control port. If the given port should not have any MIDI + * controller mapped to it, the function should return DSSI_NONE. + * The behaviour of this function is undefined if the given port + * number does not correspond to an input control port. A plugin + * that does not want MIDI controllers mapped to ports at all may + * set this member NULL. + * + * Correct values can be got using the macros DSSI_CC(num) and + * DSSI_NRPN(num) as appropriate, and values can be combined using + * bitwise OR: e.g. DSSI_CC(23) | DSSI_NRPN(1069) means the port + * should respond to CC #23 and NRPN #1069. + * + * The host is responsible for doing proper scaling from MIDI + * controller and NRPN value ranges to port ranges according to + * the plugin's LADSPA port hints. Hosts should not deliver + * through run_synth any MIDI controller events that have already + * been mapped to control port values. + * + * A plugin should not attempt to request mappings from + * controllers 0 or 32 (MIDI Bank Select MSB and LSB). + */ + int (*get_midi_controller_for_port)(LADSPA_Handle Instance, + unsigned long Port); + + /** + * run_synth() + * + * This member is a function pointer that runs a synth for a + * block. This is identical in function to the LADSPA run() + * function, except that it also supplies events to the synth. + * + * A plugin may provide this function, run_multiple_synths() (see + * below), both, or neither (if it is not in fact a synth). A + * plugin that does not provide this function must set this member + * to NULL. Authors of synth plugins are encouraged to provide + * this function if at all possible. + * + * The Events pointer points to a block of EventCount ALSA + * sequencer events, which is used to communicate MIDI and related + * events to the synth. Each event is timestamped relative to the + * start of the block, (mis)using the ALSA "tick time" field as a + * frame count. The host is responsible for ensuring that events + * with differing timestamps are already ordered by time. + * + * See also the notes on activation, port connection etc in + * ladpsa.h, in the context of the LADSPA run() function. + * + * Note Events + * ~~~~~~~~~~~ + * There are two minor requirements aimed at making the plugin + * writer's life as simple as possible: + * + * 1. A host must never send events of type SND_SEQ_EVENT_NOTE. + * Notes should always be sent as separate SND_SEQ_EVENT_NOTE_ON + * and NOTE_OFF events. A plugin should discard any one-point + * NOTE events it sees. + * + * 2. A host must not attempt to switch notes off by sending + * zero-velocity NOTE_ON events. It should always send true + * NOTE_OFFs. It is the host's responsibility to remap events in + * cases where an external MIDI source has sent it zero-velocity + * NOTE_ONs. + * + * Bank and Program Events + * ~~~~~~~~~~~~~~~~~~~~~~~ + * Hosts must map MIDI Bank Select MSB and LSB (0 and 32) + * controllers and MIDI Program Change events onto the banks and + * programs specified by the plugin, using the DSSI select_program + * call. No host should ever deliver a program change or bank + * select controller to a plugin via run_synth. + */ + void (*run_synth)(LADSPA_Handle Instance, + unsigned long SampleCount, + snd_seq_event_t *Events, + unsigned long EventCount); + + /** + * run_synth_adding() + * + * This member is a function pointer that runs an instance of a + * synth for a block, adding its outputs to the values already + * present at the output ports. This is provided for symmetry + * with LADSPA run_adding(), and is equally optional. A plugin + * that does not provide it must set this member to NULL. + */ + void (*run_synth_adding)(LADSPA_Handle Instance, + unsigned long SampleCount, + snd_seq_event_t *Events, + unsigned long EventCount); + + /** + * run_multiple_synths() + * + * This member is a function pointer that runs multiple synth + * instances for a block. This is very similar to run_synth(), + * except that Instances, Events, and EventCounts each point to + * arrays that hold the LADSPA handles, event buffers, and + * event counts for each of InstanceCount instances. That is, + * Instances points to an array of InstanceCount pointers to + * DSSI plugin instantiations, Events points to an array of + * pointers to each instantiation's respective event list, and + * EventCounts points to an array containing each instantiation's + * respective event count. + * + * A host using this function must guarantee that ALL active + * instances of the plugin are represented in each call to the + * function -- that is, a host may not call run_multiple_synths() + * for some instances of a given plugin and then call run_synth() + * as well for others. 'All .. instances of the plugin' means + * every instance sharing the same LADSPA label and shared object + * (*.so) file (rather than every instance sharing the same *.so). + * 'Active' means any instance for which activate() has been called + * but deactivate() has not. + * + * A plugin may provide this function, run_synths() (see above), + * both, or neither (if it not in fact a synth). A plugin that + * does not provide this function must set this member to NULL. + * Plugin authors implementing run_multiple_synths are strongly + * encouraged to implement run_synth as well if at all possible, + * to aid simplistic hosts, even where it would be less efficient + * to use it. + */ + void (*run_multiple_synths)(unsigned long InstanceCount, + LADSPA_Handle *Instances, + unsigned long SampleCount, + snd_seq_event_t **Events, + unsigned long *EventCounts); + + /** + * run_multiple_synths_adding() + * + * This member is a function pointer that runs multiple synth + * instances for a block, adding each synth's outputs to the + * values already present at the output ports. This is provided + * for symmetry with both the DSSI run_multiple_synths() and LADSPA + * run_adding() functions, and is equally optional. A plugin + * that does not provide it must set this member to NULL. + */ + void (*run_multiple_synths_adding)(unsigned long InstanceCount, + LADSPA_Handle *Instances, + unsigned long SampleCount, + snd_seq_event_t **Events, + unsigned long *EventCounts); +} DSSI_Descriptor; + +/** + * DSSI supports a plugin discovery method similar to that of LADSPA: + * + * - DSSI hosts may wish to locate DSSI plugin shared object files by + * searching the paths contained in the DSSI_PATH and LADSPA_PATH + * environment variables, if they are present. Both are expected + * to be colon-separated lists of directories to be searched (in + * order), and DSSI_PATH should be searched first if both variables + * are set. + * + * - Each shared object file containing DSSI plugins must include a + * function dssi_descriptor(), with the following function prototype + * and C-style linkage. Hosts may enumerate the plugin types + * available in the shared object file by repeatedly calling + * this function with successive Index values (beginning from 0), + * until a return value of NULL indicates no more plugin types are + * available. Each non-NULL return is the DSSI_Descriptor + * of a distinct plugin type. + */ + +const DSSI_Descriptor *dssi_descriptor(unsigned long Index); + +typedef const DSSI_Descriptor *(*DSSI_Descriptor_Function)(unsigned long Index); + +/* + * Macros to specify particular MIDI controllers in return values from + * get_midi_controller_for_port() + */ + +#define DSSI_CC_BITS 0x20000000 +#define DSSI_NRPN_BITS 0x40000000 + +#define DSSI_NONE -1 +#define DSSI_CONTROLLER_IS_SET(n) (DSSI_NONE != (n)) + +#define DSSI_CC(n) (DSSI_CC_BITS | (n)) +#define DSSI_IS_CC(n) (DSSI_CC_BITS & (n)) +#define DSSI_CC_NUMBER(n) ((n) & 0x7f) + +#define DSSI_NRPN(n) (DSSI_NRPN_BITS | ((n) << 7)) +#define DSSI_IS_NRPN(n) (DSSI_NRPN_BITS & (n)) +#define DSSI_NRPN_NUMBER(n) (((n) >> 7) & 0x3fff) + +#ifdef __cplusplus +} +#endif + +#endif /* DSSI_INCLUDED */ diff --git a/src/dssi4cs/include/dssi/update.py b/src/dssi4cs/include/dssi/update.py new file mode 100755 index 00000000..6ab2551f --- /dev/null +++ b/src/dssi4cs/include/dssi/update.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 + +# Run from any directory. With no argument, select the latest numbered stable +# release. A version selects that release; --pinned reproduces SOURCE.json. +# Requires curl and Python 3. No upstream build scripts are run. +import hashlib +import json +from pathlib import Path +import re +import subprocess +import sys +import tarfile +import tempfile + +source_dir = Path(__file__).resolve().parent +if len(sys.argv) > 2 or (len(sys.argv) == 2 and sys.argv[1] == '--help'): + print(f'Usage: {sys.argv[0]} [VERSION|--pinned]') + sys.exit(0 if len(sys.argv) == 2 else 1) +selection = sys.argv[1] if len(sys.argv) == 2 else 'latest' + +def fetch(url, destination): + subprocess.run(['curl', '--fail', '--location', '--silent', '--show-error', + '--retry', '3', '--connect-timeout', '20', '--max-time', '120', + url, '-o', str(destination)], check=True) + +with tempfile.TemporaryDirectory(prefix='csound-dssi-') as temporary: + temporary = Path(temporary) + recorded = None + if selection == '--pinned': + recorded = json.loads((source_dir / 'SOURCE.json').read_text()) + version = recorded['version'] + elif selection == 'latest': + listing = temporary / 'releases.html' + fetch('https://sourceforge.net/projects/dssi/files/dssi/', listing) + versions = re.findall('/dssi/(\\d+(?:\\.\\d+)+)/', listing.read_text()) + if not versions: + sys.exit('No stable release found; checked-in files have not changed') + version = max(versions, key=lambda value: tuple(map(int, value.split('.')))) + else: + version = selection + if not re.fullmatch(r'\d+(?:\.\d+)+', version): + sys.exit('Expected a numbered release, for example 1.2.3') + + url = 'https://downloads.sourceforge.net/project/dssi/dssi/{version}/dssi-{version}.tar.gz'.format(version=version) + archive = temporary / 'source.tar' + fetch(url, archive) + digest = hashlib.sha256(archive.read_bytes()).hexdigest() + if recorded and digest != recorded['sha256']: + sys.exit('Archive differs from SOURCE.json; checked-in files have not changed') + files = {'dssi.h': 'dssi-{version}/dssi/dssi.h', 'LICENSE': 'dssi-{version}/COPYING'} + staged = {} + # Read only named files. Preserve upstream bytes, including license notices. + with tarfile.open(archive) as package: + for destination, member in files.items(): + entry = package.getmember(member.format(version=version)) + if not entry.isfile(): + sys.exit(f'Expected a file: {entry.name}') + staged[destination] = package.extractfile(entry).read() + metadata = {'version': version, 'url': url, 'sha256': digest, + 'files': {name: member.format(version=version) for name, member in files.items()}} + staged['SOURCE.json'] = (json.dumps(metadata, indent=2) + '\n').encode() + readme = f"""# DSSI + +Unmodified files from [release {version}]({url}). + +Only the DSSI API header is vendored. +The header uses LGPL-2.1-or-later; see [LICENSE](LICENSE) and its license notice. + +[SOURCE.json](SOURCE.json) records the archive URL, SHA-256, and file paths. +Keep platform adapters in the plugin's own sources, outside this directory. + +Run `./update.py` for the latest stable release, `./update.py VERSION` for a +specific release, or `./update.py --pinned` to reproduce this copy and check its +archive hash. Downloads and extraction finish before checked-in files change. +The script requires curl and Python 3 and works from any directory. + +After an update, review the source and license diff, rebuild the DSSI/LADSPA +plugin on Linux, and verify that a native synth or effect still loads. Commit +the header, license, README, and SOURCE.json together. +""" + staged['README.md'] = readme.encode() + # A failed download or missing archive member leaves the old copy intact. + for name, data in staged.items(): + destination = source_dir / name + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_bytes(data) + print(f'Updated DSSI to {version}. Source: {url}') diff --git a/src/dssi4cs/include/ladspa/LICENSE b/src/dssi4cs/include/ladspa/LICENSE new file mode 100644 index 00000000..07d2ddf3 --- /dev/null +++ b/src/dssi4cs/include/ladspa/LICENSE @@ -0,0 +1,514 @@ + + GNU LESSER GENERAL PUBLIC LICENSE + Version 2.1, February 1999 + + Copyright (C) 1991, 1999 Free Software Foundation, Inc. + 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + +[This is the first released version of the Lesser GPL. It also counts + as the successor of the GNU Library Public License, version 2, hence + the version number 2.1.] + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +Licenses are intended to guarantee your freedom to share and change +free software--to make sure the software is free for all its users. + + This license, the Lesser General Public License, applies to some +specially designated software packages--typically libraries--of the +Free Software Foundation and other authors who decide to use it. You +can use it too, but we suggest you first think carefully about whether +this license or the ordinary General Public License is the better +strategy to use in any particular case, based on the explanations +below. + + When we speak of free software, we are referring to freedom of use, +not price. Our General Public Licenses are designed to make sure that +you have the freedom to distribute copies of free software (and charge +for this service if you wish); that you receive source code or can get +it if you want it; that you can change the software and use pieces of +it in new free programs; and that you are informed that you can do +these things. + + To protect your rights, we need to make restrictions that forbid +distributors to deny you these rights or to ask you to surrender these +rights. These restrictions translate to certain responsibilities for +you if you distribute copies of the library or if you modify it. + + For example, if you distribute copies of the library, whether gratis +or for a fee, you must give the recipients all the rights that we gave +you. You must make sure that they, too, receive or can get the source +code. If you link other code with the library, you must provide +complete object files to the recipients, so that they can relink them +with the library after making changes to the library and recompiling +it. And you must show them these terms so they know their rights. + + We protect your rights with a two-step method: (1) we copyright the +library, and (2) we offer you this license, which gives you legal +permission to copy, distribute and/or modify the library. + + To protect each distributor, we want to make it very clear that +there is no warranty for the free library. Also, if the library is +modified by someone else and passed on, the recipients should know +that what they have is not the original version, so that the original +author's reputation will not be affected by problems that might be +introduced by others. +^L + Finally, software patents pose a constant threat to the existence of +any free program. We wish to make sure that a company cannot +effectively restrict the users of a free program by obtaining a +restrictive license from a patent holder. Therefore, we insist that +any patent license obtained for a version of the library must be +consistent with the full freedom of use specified in this license. + + Most GNU software, including some libraries, is covered by the +ordinary GNU General Public License. This license, the GNU Lesser +General Public License, applies to certain designated libraries, and +is quite different from the ordinary General Public License. We use +this license for certain libraries in order to permit linking those +libraries into non-free programs. + + When a program is linked with a library, whether statically or using +a shared library, the combination of the two is legally speaking a +combined work, a derivative of the original library. The ordinary +General Public License therefore permits such linking only if the +entire combination fits its criteria of freedom. The Lesser General +Public License permits more lax criteria for linking other code with +the library. + + We call this license the "Lesser" General Public License because it +does Less to protect the user's freedom than the ordinary General +Public License. It also provides other free software developers Less +of an advantage over competing non-free programs. These disadvantages +are the reason we use the ordinary General Public License for many +libraries. However, the Lesser license provides advantages in certain +special circumstances. + + For example, on rare occasions, there may be a special need to +encourage the widest possible use of a certain library, so that it +becomes +a de-facto standard. To achieve this, non-free programs must be +allowed to use the library. A more frequent case is that a free +library does the same job as widely used non-free libraries. In this +case, there is little to gain by limiting the free library to free +software only, so we use the Lesser General Public License. + + In other cases, permission to use a particular library in non-free +programs enables a greater number of people to use a large body of +free software. For example, permission to use the GNU C Library in +non-free programs enables many more people to use the whole GNU +operating system, as well as its variant, the GNU/Linux operating +system. + + Although the Lesser General Public License is Less protective of the +users' freedom, it does ensure that the user of a program that is +linked with the Library has the freedom and the wherewithal to run +that program using a modified version of the Library. + + The precise terms and conditions for copying, distribution and +modification follow. Pay close attention to the difference between a +"work based on the library" and a "work that uses the library". The +former contains code derived from the library, whereas the latter must +be combined with the library in order to run. +^L + GNU LESSER GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License Agreement applies to any software library or other +program which contains a notice placed by the copyright holder or +other authorized party saying it may be distributed under the terms of +this Lesser General Public License (also called "this License"). +Each licensee is addressed as "you". + + A "library" means a collection of software functions and/or data +prepared so as to be conveniently linked with application programs +(which use some of those functions and data) to form executables. + + The "Library", below, refers to any such software library or work +which has been distributed under these terms. A "work based on the +Library" means either the Library or any derivative work under +copyright law: that is to say, a work containing the Library or a +portion of it, either verbatim or with modifications and/or translated +straightforwardly into another language. (Hereinafter, translation is +included without limitation in the term "modification".) + + "Source code" for a work means the preferred form of the work for +making modifications to it. For a library, complete source code means +all the source code for all modules it contains, plus any associated +interface definition files, plus the scripts used to control +compilation +and installation of the library. + + Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running a program using the Library is not restricted, and output from +such a program is covered only if its contents constitute a work based +on the Library (independent of the use of the Library in a tool for +writing it). Whether that is true depends on what the Library does +and what the program that uses the Library does. + + 1. You may copy and distribute verbatim copies of the Library's +complete source code as you receive it, in any medium, provided that +you conspicuously and appropriately publish on each copy an +appropriate copyright notice and disclaimer of warranty; keep intact +all the notices that refer to this License and to the absence of any +warranty; and distribute a copy of this License along with the +Library. + + You may charge a fee for the physical act of transferring a copy, +and you may at your option offer warranty protection in exchange for a +fee. + + 2. You may modify your copy or copies of the Library or any portion +of it, thus forming a work based on the Library, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) The modified work must itself be a software library. + + b) You must cause the files modified to carry prominent notices + stating that you changed the files and the date of any change. + + c) You must cause the whole of the work to be licensed at no + charge to all third parties under the terms of this License. + + d) If a facility in the modified Library refers to a function or a + table of data to be supplied by an application program that uses + the facility, other than as an argument passed when the facility + is invoked, then you must make a good faith effort to ensure that, + in the event an application does not supply such function or + table, the facility still operates, and performs whatever part of + its purpose remains meaningful. + + (For example, a function in a library to compute square roots has + a purpose that is entirely well-defined independent of the + application. Therefore, Subsection 2d requires that any + application-supplied function or table used by this function must + be optional: if the application does not supply it, the square + root function must still compute square roots.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Library, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Library, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote +it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Library. + +In addition, mere aggregation of another work not based on the Library +with the Library (or with a work based on the Library) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may opt to apply the terms of the ordinary GNU General Public +License instead of this License to a given copy of the Library. To do +this, you must alter all the notices that refer to this License, so +that they refer to the ordinary GNU General Public License, version 2, +instead of to this License. (If a newer version than version 2 of the +ordinary GNU General Public License has appeared, then you can specify +that version instead if you wish.) Do not make any other change in +these notices. +^L + Once this change is made in a given copy, it is irreversible for +that copy, so the ordinary GNU General Public License applies to all +subsequent copies and derivative works made from that copy. + + This option is useful when you wish to copy part of the code of +the Library into a program that is not a library. + + 4. You may copy and distribute the Library (or a portion or +derivative of it, under Section 2) in object code or executable form +under the terms of Sections 1 and 2 above provided that you accompany +it with the complete corresponding machine-readable source code, which +must be distributed under the terms of Sections 1 and 2 above on a +medium customarily used for software interchange. + + If distribution of object code is made by offering access to copy +from a designated place, then offering equivalent access to copy the +source code from the same place satisfies the requirement to +distribute the source code, even though third parties are not +compelled to copy the source along with the object code. + + 5. A program that contains no derivative of any portion of the +Library, but is designed to work with the Library by being compiled or +linked with it, is called a "work that uses the Library". Such a +work, in isolation, is not a derivative work of the Library, and +therefore falls outside the scope of this License. + + However, linking a "work that uses the Library" with the Library +creates an executable that is a derivative of the Library (because it +contains portions of the Library), rather than a "work that uses the +library". The executable is therefore covered by this License. +Section 6 states terms for distribution of such executables. + + When a "work that uses the Library" uses material from a header file +that is part of the Library, the object code for the work may be a +derivative work of the Library even though the source code is not. +Whether this is true is especially significant if the work can be +linked without the Library, or if the work is itself a library. The +threshold for this to be true is not precisely defined by law. + + If such an object file uses only numerical parameters, data +structure layouts and accessors, and small macros and small inline +functions (ten lines or less in length), then the use of the object +file is unrestricted, regardless of whether it is legally a derivative +work. (Executables containing this object code plus portions of the +Library will still fall under Section 6.) + + Otherwise, if the work is a derivative of the Library, you may +distribute the object code for the work under the terms of Section 6. +Any executables containing that work also fall under Section 6, +whether or not they are linked directly with the Library itself. +^L + 6. As an exception to the Sections above, you may also combine or +link a "work that uses the Library" with the Library to produce a +work containing portions of the Library, and distribute that work +under terms of your choice, provided that the terms permit +modification of the work for the customer's own use and reverse +engineering for debugging such modifications. + + You must give prominent notice with each copy of the work that the +Library is used in it and that the Library and its use are covered by +this License. You must supply a copy of this License. If the work +during execution displays copyright notices, you must include the +copyright notice for the Library among them, as well as a reference +directing the user to the copy of this License. Also, you must do one +of these things: + + a) Accompany the work with the complete corresponding + machine-readable source code for the Library including whatever + changes were used in the work (which must be distributed under + Sections 1 and 2 above); and, if the work is an executable linked + with the Library, with the complete machine-readable "work that + uses the Library", as object code and/or source code, so that the + user can modify the Library and then relink to produce a modified + executable containing the modified Library. (It is understood + that the user who changes the contents of definitions files in the + Library will not necessarily be able to recompile the application + to use the modified definitions.) + + b) Use a suitable shared library mechanism for linking with the + Library. A suitable mechanism is one that (1) uses at run time a + copy of the library already present on the user's computer system, + rather than copying library functions into the executable, and (2) + will operate properly with a modified version of the library, if + the user installs one, as long as the modified version is + interface-compatible with the version that the work was made with. + + c) Accompany the work with a written offer, valid for at + least three years, to give the same user the materials + specified in Subsection 6a, above, for a charge no more + than the cost of performing this distribution. + + d) If distribution of the work is made by offering access to copy + from a designated place, offer equivalent access to copy the above + specified materials from the same place. + + e) Verify that the user has already received a copy of these + materials or that you have already sent this user a copy. + + For an executable, the required form of the "work that uses the +Library" must include any data and utility programs needed for +reproducing the executable from it. However, as a special exception, +the materials to be distributed need not include anything that is +normally distributed (in either source or binary form) with the major +components (compiler, kernel, and so on) of the operating system on +which the executable runs, unless that component itself accompanies +the executable. + + It may happen that this requirement contradicts the license +restrictions of other proprietary libraries that do not normally +accompany the operating system. Such a contradiction means you cannot +use both them and the Library together in an executable that you +distribute. +^L + 7. You may place library facilities that are a work based on the +Library side-by-side in a single library together with other library +facilities not covered by this License, and distribute such a combined +library, provided that the separate distribution of the work based on +the Library and of the other library facilities is otherwise +permitted, and provided that you do these two things: + + a) Accompany the combined library with a copy of the same work + based on the Library, uncombined with any other library + facilities. This must be distributed under the terms of the + Sections above. + + b) Give prominent notice with the combined library of the fact + that part of it is a work based on the Library, and explaining + where to find the accompanying uncombined form of the same work. + + 8. You may not copy, modify, sublicense, link with, or distribute +the Library except as expressly provided under this License. Any +attempt otherwise to copy, modify, sublicense, link with, or +distribute the Library is void, and will automatically terminate your +rights under this License. However, parties who have received copies, +or rights, from you under this License will not have their licenses +terminated so long as such parties remain in full compliance. + + 9. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Library or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Library (or any work based on the +Library), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Library or works based on it. + + 10. Each time you redistribute the Library (or any work based on the +Library), the recipient automatically receives a license from the +original licensor to copy, distribute, link with or modify the Library +subject to these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties with +this License. +^L + 11. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Library at all. For example, if a patent +license would not permit royalty-free redistribution of the Library by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Library. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply, and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 12. If the distribution and/or use of the Library is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Library under this License +may add an explicit geographical distribution limitation excluding those +countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 13. The Free Software Foundation may publish revised and/or new +versions of the Lesser General Public License from time to time. +Such new versions will be similar in spirit to the present version, +but may differ in detail to address new problems or concerns. + +Each version is given a distinguishing version number. If the Library +specifies a version number of this License which applies to it and +"any later version", you have the option of following the terms and +conditions either of that version or of any later version published by +the Free Software Foundation. If the Library does not specify a +license version number, you may choose any version ever published by +the Free Software Foundation. +^L + 14. If you wish to incorporate parts of the Library into other free +programs whose distribution conditions are incompatible with these, +write to the author to ask for permission. For software which is +copyrighted by the Free Software Foundation, write to the Free +Software Foundation; we sometimes make exceptions for this. Our +decision will be guided by the two goals of preserving the free status +of all derivatives of our free software and of promoting the sharing +and reuse of software generally. + + NO WARRANTY + + 15. BECAUSE THE LIBRARY IS LICENSED FREE OF CHARGE, THERE IS NO +WARRANTY FOR THE LIBRARY, TO THE EXTENT PERMITTED BY APPLICABLE LAW. +EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR +OTHER PARTIES PROVIDE THE LIBRARY "AS IS" WITHOUT WARRANTY OF ANY +KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE +IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR +PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE +LIBRARY IS WITH YOU. SHOULD THE LIBRARY PROVE DEFECTIVE, YOU ASSUME +THE COST OF ALL NECESSARY SERVICING, REPAIR OR CORRECTION. + + 16. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN +WRITING WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY +AND/OR REDISTRIBUTE THE LIBRARY AS PERMITTED ABOVE, BE LIABLE TO YOU +FOR DAMAGES, INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR +CONSEQUENTIAL DAMAGES ARISING OUT OF THE USE OR INABILITY TO USE THE +LIBRARY (INCLUDING BUT NOT LIMITED TO LOSS OF DATA OR DATA BEING +RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD PARTIES OR A +FAILURE OF THE LIBRARY TO OPERATE WITH ANY OTHER SOFTWARE), EVEN IF +SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH +DAMAGES. + + END OF TERMS AND CONDITIONS +^L + How to Apply These Terms to Your New Libraries + + If you develop a new library, and you want it to be of the greatest +possible use to the public, we recommend making it free software that +everyone can redistribute and change. You can do so by permitting +redistribution under these terms (or, alternatively, under the terms +of the ordinary General Public License). + + To apply these terms, attach the following notices to the library. +It is safest to attach them to the start of each source file to most +effectively convey the exclusion of warranty; and each file should +have at least the "copyright" line and a pointer to where the full +notice is found. + + + + Copyright (C) + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2 of the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + +Also add information on how to contact you by electronic and paper +mail. + +You should also get your employer (if you work as a programmer) or +your school, if any, to sign a "copyright disclaimer" for the library, +if necessary. Here is a sample; alter the names: + + Yoyodyne, Inc., hereby disclaims all copyright interest in the + library `Frob' (a library for tweaking knobs) written by James + Random Hacker. + + , 1 April 1990 + Ty Coon, President of Vice + +That's all there is to it! + + diff --git a/src/dssi4cs/include/ladspa/README.md b/src/dssi4cs/include/ladspa/README.md new file mode 100644 index 00000000..8722ac6f --- /dev/null +++ b/src/dssi4cs/include/ladspa/README.md @@ -0,0 +1,18 @@ +# LADSPA + +Unmodified files from [release 1.17](https://www.ladspa.org/download/ladspa_sdk_1.17.tgz). + +Only the LADSPA API header is vendored. +The header uses LGPL-2.1-or-later; see [LICENSE](LICENSE) and its license notice. + +[SOURCE.json](SOURCE.json) records the archive URL, SHA-256, and file paths. +Keep platform adapters in the plugin's own sources, outside this directory. + +Run `./update.py` for the latest stable release, `./update.py VERSION` for a +specific release, or `./update.py --pinned` to reproduce this copy and check its +archive hash. Downloads and extraction finish before checked-in files change. +The script requires curl and Python 3 and works from any directory. + +After an update, review the source and license diff, rebuild the DSSI/LADSPA +plugin on Linux, and verify that a native synth or effect still loads. Commit +the header, license, README, and SOURCE.json together. diff --git a/src/dssi4cs/include/ladspa/SOURCE.json b/src/dssi4cs/include/ladspa/SOURCE.json new file mode 100644 index 00000000..06fcef50 --- /dev/null +++ b/src/dssi4cs/include/ladspa/SOURCE.json @@ -0,0 +1,9 @@ +{ + "version": "1.17", + "url": "https://www.ladspa.org/download/ladspa_sdk_1.17.tgz", + "sha256": "27d24f279e4b81bd17ecbdcc38e4c42991bb388826c0b200067ce0eb59d3da5b", + "files": { + "ladspa.h": "ladspa_sdk_1.17/src/ladspa.h", + "LICENSE": "ladspa_sdk_1.17/doc/COPYING" + } +} diff --git a/src/dssi4cs/include/ladspa/ladspa.h b/src/dssi4cs/include/ladspa/ladspa.h new file mode 100644 index 00000000..5c30a8a4 --- /dev/null +++ b/src/dssi4cs/include/ladspa/ladspa.h @@ -0,0 +1,603 @@ +/* ladspa.h + + Linux Audio Developer's Simple Plugin API Version 1.1[LGPL]. + Copyright (C) 2000-2002 Richard W.E. Furse, Paul Barton-Davis, + Stefan Westerfeld. + + This library is free software; you can redistribute it and/or + modify it under the terms of the GNU Lesser General Public License + as published by the Free Software Foundation; either version 2.1 of + the License, or (at your option) any later version. + + This library is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with this library; if not, write to the Free Software + Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + USA. */ + +#ifndef LADSPA_INCLUDED +#define LADSPA_INCLUDED + +#define LADSPA_VERSION "1.1" +#define LADSPA_VERSION_MAJOR 1 +#define LADSPA_VERSION_MINOR 1 + +#ifdef __cplusplus +extern "C" { +#endif + +/*****************************************************************************/ + +/* Overview: + + There is a large number of synthesis packages in use or development + on the Linux platform at this time. This API (`The Linux Audio + Developer's Simple Plugin API') attempts to give programmers the + ability to write simple `plugin' audio processors in C/C++ and link + them dynamically (`plug') into a range of these packages (`hosts'). + It should be possible for any host and any plugin to communicate + completely through this interface. + + This API is deliberately short and simple. To achieve compatibility + with a range of promising Linux sound synthesis packages it + attempts to find the `greatest common divisor' in their logical + behaviour. Having said this, certain limiting decisions are + implicit, notably the use of a fixed type (LADSPA_Data) for all + data transfer and absence of a parameterised `initialisation' + phase. See below for the LADSPA_Data typedef. + + Plugins are expected to distinguish between control and audio + data. Plugins have `ports' that are inputs or outputs for audio or + control data and each plugin is `run' for a `block' corresponding + to a short time interval measured in samples. Audio data is + communicated using arrays of LADSPA_Data, allowing a block of audio + to be processed by the plugin in a single pass. Control data is + communicated using single LADSPA_Data values. Control data has a + single value at the start of a call to the `run()' or `run_adding()' + function, and may be considered to remain this value for its + duration. The plugin may assume that all its input and output ports + have been connected to the relevant data location (see the + `connect_port()' function below) before it is asked to run. + + Plugins will reside in shared object files suitable for dynamic + linking by dlopen() and family. The file will provide a number of + `plugin types' that can be used to instantiate actual plugins + (sometimes known as `plugin instances') that can be connected + together to perform tasks. + + This API contains very limited error-handling. */ + +/*****************************************************************************/ + +/* Fundamental data type passed in and out of plugin. This data type + is used to communicate audio samples and control values. It is + assumed that the plugin will work sensibly given any numeric input + value although it may have a preferred range (see hints below). + + For audio it is generally assumed that 1.0f is the `0dB' reference + amplitude and is a `normal' signal level. */ + +typedef float LADSPA_Data; + +/*****************************************************************************/ + +/* Special Plugin Properties: + + Optional features of the plugin type are encapsulated in the + LADSPA_Properties type. This is assembled by ORing individual + properties together. */ + +typedef int LADSPA_Properties; + +/* Property LADSPA_PROPERTY_REALTIME indicates that the plugin has a + real-time dependency (e.g. listens to a MIDI device) and so its + output must not be cached or subject to significant latency. */ +#define LADSPA_PROPERTY_REALTIME 0x1 + +/* Property LADSPA_PROPERTY_INPLACE_BROKEN indicates that the plugin + may cease to work correctly if the host elects to use the same data + location for both input and output (see connect_port()). This + should be avoided as enabling this flag makes it impossible for + hosts to use the plugin to process audio `in-place.' */ +#define LADSPA_PROPERTY_INPLACE_BROKEN 0x2 + +/* Property LADSPA_PROPERTY_HARD_RT_CAPABLE indicates that the plugin + is capable of running not only in a conventional host but also in a + `hard real-time' environment. To qualify for this the plugin must + satisfy all of the following: + + (1) The plugin must not use malloc(), free() or other heap memory + management within its run() or run_adding() functions. All new + memory used in run() must be managed via the stack. These + restrictions only apply to the run() function. + + (2) The plugin will not attempt to make use of any library + functions with the exceptions of functions in the ANSI standard C + and C maths libraries, which the host is expected to provide. + + (3) The plugin will not access files, devices, pipes, sockets, IPC + or any other mechanism that might result in process or thread + blocking. + + (4) The plugin will take an amount of time to execute a run() or + run_adding() call approximately of form (A+B*SampleCount) where A + and B depend on the machine and host in use. This amount of time + may not depend on input signals or plugin state. The host is left + the responsibility to perform timings to estimate upper bounds for + A and B. */ +#define LADSPA_PROPERTY_HARD_RT_CAPABLE 0x4 + +#define LADSPA_IS_REALTIME(x) ((x) & LADSPA_PROPERTY_REALTIME) +#define LADSPA_IS_INPLACE_BROKEN(x) ((x) & LADSPA_PROPERTY_INPLACE_BROKEN) +#define LADSPA_IS_HARD_RT_CAPABLE(x) ((x) & LADSPA_PROPERTY_HARD_RT_CAPABLE) + +/*****************************************************************************/ + +/* Plugin Ports: + + Plugins have `ports' that are inputs or outputs for audio or + data. Ports can communicate arrays of LADSPA_Data (for audio + inputs/outputs) or single LADSPA_Data values (for control + input/outputs). This information is encapsulated in the + LADSPA_PortDescriptor type which is assembled by ORing individual + properties together. + + Note that a port must be an input or an output port but not both + and that a port must be a control or audio port but not both. */ + +typedef int LADSPA_PortDescriptor; + +/* Property LADSPA_PORT_INPUT indicates that the port is an input. */ +#define LADSPA_PORT_INPUT 0x1 + +/* Property LADSPA_PORT_OUTPUT indicates that the port is an output. */ +#define LADSPA_PORT_OUTPUT 0x2 + +/* Property LADSPA_PORT_CONTROL indicates that the port is a control + port. */ +#define LADSPA_PORT_CONTROL 0x4 + +/* Property LADSPA_PORT_AUDIO indicates that the port is a audio + port. */ +#define LADSPA_PORT_AUDIO 0x8 + +#define LADSPA_IS_PORT_INPUT(x) ((x) & LADSPA_PORT_INPUT) +#define LADSPA_IS_PORT_OUTPUT(x) ((x) & LADSPA_PORT_OUTPUT) +#define LADSPA_IS_PORT_CONTROL(x) ((x) & LADSPA_PORT_CONTROL) +#define LADSPA_IS_PORT_AUDIO(x) ((x) & LADSPA_PORT_AUDIO) + +/*****************************************************************************/ + +/* Plugin Port Range Hints: + + The host may wish to provide a representation of data entering or + leaving a plugin (e.g. to generate a GUI automatically). To make + this more meaningful, the plugin should provide `hints' to the host + describing the usual values taken by the data. + + Note that these are only hints. The host may ignore them and the + plugin must not assume that data supplied to it is meaningful. If + the plugin receives invalid input data it is expected to continue + to run without failure and, where possible, produce a sensible + output (e.g. a high-pass filter given a negative cutoff frequency + might switch to an all-pass mode). + + Hints are meaningful for all input and output ports but hints for + input control ports are expected to be particularly useful. + + More hint information is encapsulated in the + LADSPA_PortRangeHintDescriptor type which is assembled by ORing + individual hint types together. Hints may require further + LowerBound and UpperBound information. + + All the hint information for a particular port is aggregated in the + LADSPA_PortRangeHint structure. */ + +typedef int LADSPA_PortRangeHintDescriptor; + +/* Hint LADSPA_HINT_BOUNDED_BELOW indicates that the LowerBound field + of the LADSPA_PortRangeHint should be considered meaningful. The + value in this field should be considered the (inclusive) lower + bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also + specified then the value of LowerBound should be multiplied by the + sample rate. */ +#define LADSPA_HINT_BOUNDED_BELOW 0x1 + +/* Hint LADSPA_HINT_BOUNDED_ABOVE indicates that the UpperBound field + of the LADSPA_PortRangeHint should be considered meaningful. The + value in this field should be considered the (inclusive) upper + bound of the valid range. If LADSPA_HINT_SAMPLE_RATE is also + specified then the value of UpperBound should be multiplied by the + sample rate. */ +#define LADSPA_HINT_BOUNDED_ABOVE 0x2 + +/* Hint LADSPA_HINT_TOGGLED indicates that the data item should be + considered a Boolean toggle. Data less than or equal to zero should + be considered `off' or `false,' and data above zero should be + considered `on' or `true.' LADSPA_HINT_TOGGLED may not be used in + conjunction with any other hint except LADSPA_HINT_DEFAULT_0 or + LADSPA_HINT_DEFAULT_1. */ +#define LADSPA_HINT_TOGGLED 0x4 + +/* Hint LADSPA_HINT_SAMPLE_RATE indicates that any bounds specified + should be interpreted as multiples of the sample rate. For + instance, a frequency range from 0Hz to the Nyquist frequency (half + the sample rate) could be requested by this hint in conjunction + with LowerBound = 0 and UpperBound = 0.5. Hosts that support bounds + at all must support this hint to retain meaning. */ +#define LADSPA_HINT_SAMPLE_RATE 0x8 + +/* Hint LADSPA_HINT_LOGARITHMIC indicates that it is likely that the + user will find it more intuitive to view values using a logarithmic + scale. This is particularly useful for frequencies and gains. */ +#define LADSPA_HINT_LOGARITHMIC 0x10 + +/* Hint LADSPA_HINT_INTEGER indicates that a user interface would + probably wish to provide a stepped control taking only integer + values. Any bounds set should be slightly wider than the actual + integer range required to avoid floating point rounding errors. For + instance, the integer set {0,1,2,3} might be described as [-0.1, + 3.1]. */ +#define LADSPA_HINT_INTEGER 0x20 + +/* The various LADSPA_HINT_HAS_DEFAULT_* hints indicate a `normal' + value for the port that is sensible as a default. For instance, + this value is suitable for use as an initial value in a user + interface or as a value the host might assign to a control port + when the user has not provided one. Defaults are encoded using a + mask so only one default may be specified for a port. Some of the + hints make use of lower and upper bounds, in which case the + relevant bound or bounds must be available and + LADSPA_HINT_SAMPLE_RATE must be applied as usual. The resulting + default must be rounded if LADSPA_HINT_INTEGER is present. Default + values were introduced in LADSPA v1.1. */ +#define LADSPA_HINT_DEFAULT_MASK 0x3C0 + +/* This default values indicates that no default is provided. */ +#define LADSPA_HINT_DEFAULT_NONE 0x0 + +/* This default hint indicates that the suggested lower bound for the + port should be used. */ +#define LADSPA_HINT_DEFAULT_MINIMUM 0x40 + +/* This default hint indicates that a low value between the suggested + lower and upper bounds should be chosen. For ports with + LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.75 + + log(upper) * 0.25). Otherwise, this should be (lower * 0.75 + upper + * 0.25). */ +#define LADSPA_HINT_DEFAULT_LOW 0x80 + +/* This default hint indicates that a middle value between the + suggested lower and upper bounds should be chosen. For ports with + LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.5 + + log(upper) * 0.5). Otherwise, this should be (lower * 0.5 + upper * + 0.5). */ +#define LADSPA_HINT_DEFAULT_MIDDLE 0xC0 + +/* This default hint indicates that a high value between the suggested + lower and upper bounds should be chosen. For ports with + LADSPA_HINT_LOGARITHMIC, this should be exp(log(lower) * 0.25 + + log(upper) * 0.75). Otherwise, this should be (lower * 0.25 + upper + * 0.75). */ +#define LADSPA_HINT_DEFAULT_HIGH 0x100 + +/* This default hint indicates that the suggested upper bound for the + port should be used. */ +#define LADSPA_HINT_DEFAULT_MAXIMUM 0x140 + +/* This default hint indicates that the number 0 should be used. Note + that this default may be used in conjunction with + LADSPA_HINT_TOGGLED. */ +#define LADSPA_HINT_DEFAULT_0 0x200 + +/* This default hint indicates that the number 1 should be used. Note + that this default may be used in conjunction with + LADSPA_HINT_TOGGLED. */ +#define LADSPA_HINT_DEFAULT_1 0x240 + +/* This default hint indicates that the number 100 should be used. */ +#define LADSPA_HINT_DEFAULT_100 0x280 + +/* This default hint indicates that the Hz frequency of `concert A' + should be used. This will be 440 unless the host uses an unusual + tuning convention, in which case it may be within a few Hz. */ +#define LADSPA_HINT_DEFAULT_440 0x2C0 + +#define LADSPA_IS_HINT_BOUNDED_BELOW(x) ((x) & LADSPA_HINT_BOUNDED_BELOW) +#define LADSPA_IS_HINT_BOUNDED_ABOVE(x) ((x) & LADSPA_HINT_BOUNDED_ABOVE) +#define LADSPA_IS_HINT_TOGGLED(x) ((x) & LADSPA_HINT_TOGGLED) +#define LADSPA_IS_HINT_SAMPLE_RATE(x) ((x) & LADSPA_HINT_SAMPLE_RATE) +#define LADSPA_IS_HINT_LOGARITHMIC(x) ((x) & LADSPA_HINT_LOGARITHMIC) +#define LADSPA_IS_HINT_INTEGER(x) ((x) & LADSPA_HINT_INTEGER) + +#define LADSPA_IS_HINT_HAS_DEFAULT(x) ((x) & LADSPA_HINT_DEFAULT_MASK) +#define LADSPA_IS_HINT_DEFAULT_MINIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_MINIMUM) +#define LADSPA_IS_HINT_DEFAULT_LOW(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_LOW) +#define LADSPA_IS_HINT_DEFAULT_MIDDLE(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_MIDDLE) +#define LADSPA_IS_HINT_DEFAULT_HIGH(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_HIGH) +#define LADSPA_IS_HINT_DEFAULT_MAXIMUM(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_MAXIMUM) +#define LADSPA_IS_HINT_DEFAULT_0(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_0) +#define LADSPA_IS_HINT_DEFAULT_1(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_1) +#define LADSPA_IS_HINT_DEFAULT_100(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_100) +#define LADSPA_IS_HINT_DEFAULT_440(x) (((x) & LADSPA_HINT_DEFAULT_MASK) \ + == LADSPA_HINT_DEFAULT_440) + +typedef struct _LADSPA_PortRangeHint { + + /* Hints about the port. */ + LADSPA_PortRangeHintDescriptor HintDescriptor; + + /* Meaningful when hint LADSPA_HINT_BOUNDED_BELOW is active. When + LADSPA_HINT_SAMPLE_RATE is also active then this value should be + multiplied by the relevant sample rate. */ + LADSPA_Data LowerBound; + + /* Meaningful when hint LADSPA_HINT_BOUNDED_ABOVE is active. When + LADSPA_HINT_SAMPLE_RATE is also active then this value should be + multiplied by the relevant sample rate. */ + LADSPA_Data UpperBound; + +} LADSPA_PortRangeHint; + +/*****************************************************************************/ + +/* Plugin Handles: + + This plugin handle indicates a particular instance of the plugin + concerned. It is valid to compare this to NULL (0 for C++) but + otherwise the host should not attempt to interpret it. The plugin + may use it to reference internal instance data. */ + +typedef void * LADSPA_Handle; + +/*****************************************************************************/ + +/* Descriptor for a Type of Plugin: + + This structure is used to describe a plugin type. It provides a + number of functions to examine the type, instantiate it, link it to + buffers and workspaces and to run it. */ + +typedef struct _LADSPA_Descriptor { + + /* This numeric identifier indicates the plugin type + uniquely. Plugin programmers may reserve ranges of IDs from a + central body to avoid clashes. Hosts may assume that IDs are + below 0x1000000. */ + unsigned long UniqueID; + + /* This identifier can be used as a unique, case-sensitive + identifier for the plugin type within the plugin file. Plugin + types should be identified by file and label rather than by index + or plugin name, which may be changed in new plugin + versions. Labels must not contain white-space characters. */ + const char * Label; + + /* This indicates a number of properties of the plugin. */ + LADSPA_Properties Properties; + + /* This member points to the null-terminated name of the plugin + (e.g. "Sine Oscillator"). */ + const char * Name; + + /* This member points to the null-terminated string indicating the + maker of the plugin. This can be an empty string but not NULL. */ + const char * Maker; + + /* This member points to the null-terminated string indicating any + copyright applying to the plugin. If no Copyright applies the + string "None" should be used. */ + const char * Copyright; + + /* This indicates the number of ports (input AND output) present on + the plugin. */ + unsigned long PortCount; + + /* This member indicates an array of port descriptors. Valid indices + vary from 0 to PortCount-1. */ + const LADSPA_PortDescriptor * PortDescriptors; + + /* This member indicates an array of null-terminated strings + describing ports (e.g. "Frequency (Hz)"). Valid indices vary from + 0 to PortCount-1. */ + const char * const * PortNames; + + /* This member indicates an array of range hints for each port (see + above). Valid indices vary from 0 to PortCount-1. */ + const LADSPA_PortRangeHint * PortRangeHints; + + /* This may be used by the plugin developer to pass any custom + implementation data into an instantiate call. It must not be used + or interpreted by the host. It is expected that most plugin + writers will not use this facility as LADSPA_Handle should be + used to hold instance data. */ + void * ImplementationData; + + /* This member is a function pointer that instantiates a plugin. A + handle is returned indicating the new plugin instance. The + instantiation function accepts a sample rate as a parameter. The + plugin descriptor from which this instantiate function was found + must also be passed. This function must return NULL if + instantiation fails. + + Note that instance initialisation should generally occur in + activate() rather than here. */ + LADSPA_Handle (*instantiate)(const struct _LADSPA_Descriptor * Descriptor, + unsigned long SampleRate); + + /* This member is a function pointer that connects a port on an + instantiated plugin to a memory location at which a block of data + for the port will be read/written. The data location is expected + to be an array of LADSPA_Data for audio ports or a single + LADSPA_Data value for control ports. Memory issues will be + managed by the host. The plugin must read/write the data at these + locations every time run() or run_adding() is called and the data + present at the time of this connection call should not be + considered meaningful. + + connect_port() may be called more than once for a plugin instance + to allow the host to change the buffers that the plugin is + reading or writing. These calls may be made before or after + activate() or deactivate() calls. + + connect_port() must be called at least once for each port before + run() or run_adding() is called. When working with blocks of + LADSPA_Data the plugin should pay careful attention to the block + size passed to the run function as the block allocated may only + just be large enough to contain the block of samples. + + Plugin writers should be aware that the host may elect to use the + same buffer for more than one port and even use the same buffer + for both input and output (see LADSPA_PROPERTY_INPLACE_BROKEN). + However, overlapped buffers or use of a single buffer for both + audio and control data may result in unexpected behaviour. */ + void (*connect_port)(LADSPA_Handle Instance, + unsigned long Port, + LADSPA_Data * DataLocation); + + /* This member is a function pointer that initialises a plugin + instance and activates it for use. This is separated from + instantiate() to aid real-time support and so that hosts can + reinitialise a plugin instance by calling deactivate() and then + activate(). In this case the plugin instance must reset all state + information dependent on the history of the plugin instance + except for any data locations provided by connect_port() and any + gain set by set_run_adding_gain(). If there is nothing for + activate() to do then the plugin writer may provide a NULL rather + than an empty function. + + When present, hosts must call this function once before run() (or + run_adding()) is called for the first time. This call should be + made as close to the run() call as possible and indicates to + real-time plugins that they are now live. Plugins should not rely + on a prompt call to run() after activate(). activate() may not be + called again unless deactivate() is called first. Note that + connect_port() may be called before or after a call to + activate(). */ + void (*activate)(LADSPA_Handle Instance); + + /* This method is a function pointer that runs an instance of a + plugin for a block. Two parameters are required: the first is a + handle to the particular instance to be run and the second + indicates the block size (in samples) for which the plugin + instance may run. + + Note that if an activate() function exists then it must be called + before run() or run_adding(). If deactivate() is called for a + plugin instance then the plugin instance may not be reused until + activate() has been called again. + + If the plugin has the property LADSPA_PROPERTY_HARD_RT_CAPABLE + then there are various things that the plugin should not do + within the run() or run_adding() functions (see above). */ + void (*run)(LADSPA_Handle Instance, + unsigned long SampleCount); + + /* This method is a function pointer that runs an instance of a + plugin for a block. This has identical behaviour to run() except + in the way data is output from the plugin. When run() is used, + values are written directly to the memory areas associated with + the output ports. However when run_adding() is called, values + must be added to the values already present in the memory + areas. Furthermore, output values written must be scaled by the + current gain set by set_run_adding_gain() (see below) before + addition. + + run_adding() is optional. When it is not provided by a plugin, + this function pointer must be set to NULL. When it is provided, + the function set_run_adding_gain() must be provided also. */ + void (*run_adding)(LADSPA_Handle Instance, + unsigned long SampleCount); + + /* This method is a function pointer that sets the output gain for + use when run_adding() is called (see above). If this function is + never called the gain is assumed to default to 1. Gain + information should be retained when activate() or deactivate() + are called. + + This function should be provided by the plugin if and only if the + run_adding() function is provided. When it is absent this + function pointer must be set to NULL. */ + void (*set_run_adding_gain)(LADSPA_Handle Instance, + LADSPA_Data Gain); + + /* This is the counterpart to activate() (see above). If there is + nothing for deactivate() to do then the plugin writer may provide + a NULL rather than an empty function. + + Hosts must deactivate all activated units after they have been + run() (or run_adding()) for the last time. This call should be + made as close to the last run() call as possible and indicates to + real-time plugins that they are no longer live. Plugins should + not rely on prompt deactivation. Note that connect_port() may be + called before or after a call to deactivate(). + + Deactivation is not similar to pausing as the plugin instance + will be reinitialised when activate() is called to reuse it. */ + void (*deactivate)(LADSPA_Handle Instance); + + /* Once an instance of a plugin has been finished with it can be + deleted using the following function. The instance handle passed + ceases to be valid after this call. + + If activate() was called for a plugin instance then a + corresponding call to deactivate() must be made before cleanup() + is called. */ + void (*cleanup)(LADSPA_Handle Instance); + +} LADSPA_Descriptor; + +/**********************************************************************/ + +/* Accessing a Plugin: */ + +/* The exact mechanism by which plugins are loaded is host-dependent, + however all most hosts will need to know is the name of shared + object file containing the plugin types. To allow multiple hosts to + share plugin types, hosts may wish to check for environment + variable LADSPA_PATH. If present, this should contain a + colon-separated path indicating directories that should be searched + (in order) when loading plugin types. + + A plugin programmer must include a function called + "ladspa_descriptor" with the following function prototype within + the shared object file. This function will have C-style linkage (if + you are using C++ this is taken care of by the `extern "C"' clause + at the top of the file). + + A host will find the plugin shared object file by one means or + another, find the ladspa_descriptor() function, call it, and + proceed from there. + + Plugin types are accessed by index (not ID) using values from 0 + upwards. Out of range indexes must result in this function + returning NULL, so the plugin count can be determined by checking + for the least index that results in NULL being returned. */ + +const LADSPA_Descriptor * ladspa_descriptor(unsigned long Index); + +/* Datatype corresponding to the ladspa_descriptor() function. */ +typedef const LADSPA_Descriptor * +(*LADSPA_Descriptor_Function)(unsigned long Index); + +/**********************************************************************/ + +#ifdef __cplusplus +} +#endif + +#endif /* LADSPA_INCLUDED */ + +/* EOF */ diff --git a/src/dssi4cs/include/ladspa/update.py b/src/dssi4cs/include/ladspa/update.py new file mode 100755 index 00000000..30fc0257 --- /dev/null +++ b/src/dssi4cs/include/ladspa/update.py @@ -0,0 +1,87 @@ +#!/usr/bin/env python3 + +# Run from any directory. With no argument, select the latest numbered stable +# release. A version selects that release; --pinned reproduces SOURCE.json. +# Requires curl and Python 3. No upstream build scripts are run. +import hashlib +import json +from pathlib import Path +import re +import subprocess +import sys +import tarfile +import tempfile + +source_dir = Path(__file__).resolve().parent +if len(sys.argv) > 2 or (len(sys.argv) == 2 and sys.argv[1] == '--help'): + print(f'Usage: {sys.argv[0]} [VERSION|--pinned]') + sys.exit(0 if len(sys.argv) == 2 else 1) +selection = sys.argv[1] if len(sys.argv) == 2 else 'latest' + +def fetch(url, destination): + subprocess.run(['curl', '--fail', '--location', '--silent', '--show-error', + '--retry', '3', '--connect-timeout', '20', '--max-time', '120', + url, '-o', str(destination)], check=True) + +with tempfile.TemporaryDirectory(prefix='csound-ladspa-') as temporary: + temporary = Path(temporary) + recorded = None + if selection == '--pinned': + recorded = json.loads((source_dir / 'SOURCE.json').read_text()) + version = recorded['version'] + elif selection == 'latest': + listing = temporary / 'releases.html' + fetch('https://www.ladspa.org/ladspa_sdk/download.html', listing) + versions = re.findall('ladspa_sdk_(\\d+(?:\\.\\d+)+)\\.tgz', listing.read_text()) + if not versions: + sys.exit('No stable release found; checked-in files have not changed') + version = max(versions, key=lambda value: tuple(map(int, value.split('.')))) + else: + version = selection + if not re.fullmatch(r'\d+(?:\.\d+)+', version): + sys.exit('Expected a numbered release, for example 1.2.3') + + url = 'https://www.ladspa.org/download/ladspa_sdk_{version}.tgz'.format(version=version) + archive = temporary / 'source.tar' + fetch(url, archive) + digest = hashlib.sha256(archive.read_bytes()).hexdigest() + if recorded and digest != recorded['sha256']: + sys.exit('Archive differs from SOURCE.json; checked-in files have not changed') + files = {'ladspa.h': 'ladspa_sdk_{version}/src/ladspa.h', 'LICENSE': 'ladspa_sdk_{version}/doc/COPYING'} + staged = {} + # Read only named files. Preserve upstream bytes, including license notices. + with tarfile.open(archive) as package: + for destination, member in files.items(): + entry = package.getmember(member.format(version=version)) + if not entry.isfile(): + sys.exit(f'Expected a file: {entry.name}') + staged[destination] = package.extractfile(entry).read() + metadata = {'version': version, 'url': url, 'sha256': digest, + 'files': {name: member.format(version=version) for name, member in files.items()}} + staged['SOURCE.json'] = (json.dumps(metadata, indent=2) + '\n').encode() + readme = f"""# LADSPA + +Unmodified files from [release {version}]({url}). + +Only the LADSPA API header is vendored. +The header uses LGPL-2.1-or-later; see [LICENSE](LICENSE) and its license notice. + +[SOURCE.json](SOURCE.json) records the archive URL, SHA-256, and file paths. +Keep platform adapters in the plugin's own sources, outside this directory. + +Run `./update.py` for the latest stable release, `./update.py VERSION` for a +specific release, or `./update.py --pinned` to reproduce this copy and check its +archive hash. Downloads and extraction finish before checked-in files change. +The script requires curl and Python 3 and works from any directory. + +After an update, review the source and license diff, rebuild the DSSI/LADSPA +plugin on Linux, and verify that a native synth or effect still loads. Commit +the header, license, README, and SOURCE.json together. +""" + staged['README.md'] = readme.encode() + # A failed download or missing archive member leaves the old copy intact. + for name, data in staged.items(): + destination = source_dir / name + destination.parent.mkdir(parents=True, exist_ok=True) + destination.write_bytes(data) + print(f'Updated LADSPA to {version}. Source: {url}') diff --git a/src/dssi4cs/load.c b/src/dssi4cs/load.c new file mode 100644 index 00000000..84569999 --- /dev/null +++ b/src/dssi4cs/load.c @@ -0,0 +1,49 @@ +/* load.c + + Copyright 2005 Richard W.E. Furse + with Csound adjustments by Andres Cabrera, Istvan Varga, John ffitch + + Free software by Richard W.E. Furse. Do with as you will. No + warranty. */ + +#include "utils.h" +#include + +static void *try_library(CSOUND *csound, const char *path, int flags) +{ + const char *suffixes[] = {"", ".so", NULL}; + for (int i = 0; suffixes[i]; ++i) { + size_t size = strlen(path) + strlen(suffixes[i]) + 1; + char *name = csound->Malloc(csound, size); + snprintf(name, size, "%s%s", path, suffixes[i]); + void *library = dlopen(name, flags | RTLD_LOCAL); + csound->Free(csound, name); + if (library) return library; + } + return NULL; +} + +void *dlopenLADSPA(CSOUND *csound, const char *name, int32_t flags) +{ + if (strchr(name, '/')) return try_library(csound, name, flags); + const char *paths[] = {csound->GetEnv(csound, "DSSI_PATH"), + csound->GetEnv(csound, "LADSPA_PATH"), + DSSI4CS_DEFAULT_PATH}; + for (unsigned i = 0; i < sizeof(paths)/sizeof(paths[0]); ++i) { + const char *start = paths[i]; + while (start && *start) { + const char *end = strchr(start, ':'); + size_t len = end ? (size_t)(end - start) : strlen(start); + if (len) { + size_t size = len + strlen(name) + 2; + char *path = csound->Malloc(csound, size); + snprintf(path, size, "%.*s/%s", (int)len, start, name); + void *library = try_library(csound, path, flags); + csound->Free(csound, path); + if (library) return library; + } + start = end ? end + 1 : NULL; + } + } + return NULL; +} diff --git a/src/dssi4cs/tests/dssi_test.cpp b/src/dssi4cs/tests/dssi_test.cpp new file mode 100644 index 00000000..0733dd24 --- /dev/null +++ b/src/dssi4cs/tests/dssi_test.cpp @@ -0,0 +1,446 @@ +#include "csound.h" +#include "gtest/gtest.h" +#include +#include +#include + +#ifndef CSOUND_TEST_DSSI_HOST +#error DSSI host path is required +#endif + +class DssiTests : public ::testing::Test { +protected: + CSOUND *csound = nullptr; + void *fixture = nullptr; + int (*stat)(int) = nullptr; + std::vector audio; + std::string pending; + // Move any queued messages into 'pending' so that run() can report their + // text without reading Csound internals. + void drain() { + while (csoundGetMessageCnt(csound)) { + pending += csoundGetFirstMessage(csound); + csoundPopFirstMessage(csound); + } + } + std::string messages() { + drain(); + std::string result = pending; + pending.clear(); + return result; + } + std::string load(int index = 0, const char *id = "gih", const char *path = CSOUND_TEST_DSSI_PLUGIN) { + return std::string(id) + " dssiinit \"" + path + "\", " + std::to_string(index) + ", 1\n"; + } + int run(const std::string &orc, const std::string &score = "i1 0 .01", int ksmps = 16, int dbfs = 1) { + pending.clear(); + audio.clear(); + std::string csd = "\n\n-n -d -m0 --sample-accurate\n\n" + "\nsr=8192\nksmps=" + std::to_string(ksmps) + + "\nnchnls=2\n0dbfs=" + std::to_string(dbfs) + "\n" + orc + + "\n\n\n" + score + + "\ne\n\n\n"; + // Initialisation errors (bad handle, missing DSSI/LADSPA descriptor, + // two renderers for one instance, ...) make csoundStart() fail, while + // performance errors are counted by csoundErrCnt(). Together these + // replace reading the private inerrcnt/perferrcnt fields. + int result = csoundCompileCSD(csound, csd.c_str(), 1, 0); + if (result == 0) result = csoundStart(csound); + if (result) { drain(); return result; } + for (int blocks = 0; blocks < 1000; ++blocks) { + int done = csoundPerformKsmps(csound); + const cs_float *out = csoundGetSpout(csound); + audio.insert(audio.end(), out, out + ksmps * 2); + if (done) { drain(); return csoundErrCnt(csound); } + } + ADD_FAILURE() << "Performance did not terminate"; + return -1; + } + void samples(int begin, int end, cs_double expected) { + ASSERT_GE(audio.size(), size_t(end * 2)); + for (int i = begin; i < end; ++i) { + EXPECT_NEAR(audio[2*i], expected, 1e-6) << "sample " << i; + EXPECT_NEAR(audio[2*i+1], expected, 1e-6) << "sample " << i; + } + } + void SetUp() override { + fixture = dlopen(CSOUND_TEST_DSSI_PLUGIN, RTLD_NOW | RTLD_LOCAL); + ASSERT_NE(fixture, nullptr) << dlerror(); + stat = reinterpret_cast(dlsym(fixture, "dssi_test_stat")); + ASSERT_NE(stat, nullptr); + reinterpret_cast(dlsym(fixture, "dssi_test_reset"))(); + csound = csoundCreate(nullptr, nullptr); + csoundCreateMessageBuffer(csound, 0); + csoundSetOption(csound, "-n"); + csoundSetOption(csound, "-d"); + csoundSetOption(csound, "--sample-accurate"); + csoundSetOption(csound, "--opcode-lib=" CSOUND_TEST_DSSI_HOST); + } + void TearDown() override { + csoundDestroy(csound); + EXPECT_EQ(stat(0), 0) << "plugin instances leaked"; + EXPECT_EQ(stat(2), 0) << "plugin callback contract failed"; + dlclose(fixture); + } +}; + +TEST_F(DssiTests, SynthAndNoteOpcodesAreAvailable) { + const char *orc = "sr=48000\nksmps=16\nnchnls=2\n0dbfs=1\n" + "instr 1\n" + "dssinote 1, 0, 60, 100, 1\n" + "dssievent 1, 0, 60, 100\n" + "a1, a2 dssisynth 0\n" + "endin\n"; + EXPECT_EQ(csoundCompileOrc(csound, orc, 0), 0); +} + +TEST_F(DssiTests, DurationSchedulesNoteOffAcrossBlocks) { + ASSERT_EQ(run(load() + R"( +instr 1 + dssiactivate gih, 1 + konce init 1 + dssinote konce, gih, 60, 127, 20/sr + konce = 0 + a1,a2 dssisynth gih + outs a1,a2 +endin +)"), 0) << messages(); + samples(0,20,1); + samples(20,64,0); + csoundReset(csound); + EXPECT_EQ(stat(1),1); +} + +TEST_F(DssiTests, MidiOffsetsAreSortedAndZeroVelocityBecomesNoteOff) { + ASSERT_EQ(run(load() + R"( +instr 1 + dssiactivate gih,1 + konce init 1 + dssievent konce,gih,144,0,60,0,11 + dssievent konce,gih,144,0,60,127,3 + konce=0 + a1,a2 dssisynth gih + outs a1,a2 +endin +)"), 0) << messages(); + samples(0,3,0); samples(3,11,1); samples(11,64,0); +} + +TEST_F(DssiTests, ProgramAndMappedControllerChangesSplitAtTheirSample) { + ASSERT_EQ(run(load() + R"( +instr 1 + dssiactivate gih,1 + konce init 1 + dssievent konce,gih,144,0,60,127 + dssievent konce,gih,176,0,0,1,4 + dssievent konce,gih,176,0,32,2,4 + dssievent konce,gih,192,0,5,0,4 + dssievent konce,gih,176,0,7,0,10 + konce=0 + a1,a2 dssisynth gih + kprogram dssiget gih,6 + chnset kprogram,"program" + outs a1,a2 +endin +)"), 0) << messages(); + samples(0,4,1); samples(4,10,.5); samples(10,64,0); + EXPECT_EQ(csoundGetControlChannel(csound,"program",nullptr),130*128+5); +} + +TEST_F(DssiTests, InterleavedControlPortsAndConfiguration) { + ASSERT_EQ(run(load() + R"( +dssiconfigure gih,"bias",".125" +Sname,ibank,iprog dssiprograminfo gih,1 +chnset ibank,"bank" +chnset iprog,"preset" +instr 1 + dssiactivate gih,1 + dssictls gih,4,.25,1 + dssievent 1,gih,60,127 + a1,a2 dssisynth gih + kvalue dssiget gih,4 + chnset kvalue,"bias" + outs a1,a2 +endin +)"), 0) << messages(); + samples(0,64,1.25); + EXPECT_EQ(csoundGetControlChannel(csound,"bias",nullptr),.25); + EXPECT_EQ(csoundGetControlChannel(csound,"bank",nullptr),130); + EXPECT_EQ(csoundGetControlChannel(csound,"preset",nullptr),5); +} + +TEST_F(DssiTests, OptionalCallbacksAndPartialBlocks) { + ASSERT_EQ(run(load(2) + R"( +instr 1 + dssiactivate gih,1 + konce init 1 + dssievent konce,gih,60,127 + konce=0 + a1,a2 dssisynth gih + outs a1,a2 +endin +)","i1 [3/8192] [8/8192]"), 0) << messages(); + samples(0,3,0); samples(3,11,1); samples(11,16,0); +} + +TEST_F(DssiTests, LocalKsmpsAndDbfsConversion) { + ASSERT_EQ(run(load() + R"( +opcode render,aa,i + setksmps 4 + ih xin + dssiactivate ih,1 + konce init 1 + dssinote konce,ih,60,127,7/sr + konce=0 + a1,a2 dssisynth ih + xout a1,a2 +endop +instr 1 + a1,a2 render gih + outs a1,a2 +endin +)","i1 0 .01",16,32768),0) << messages(); + samples(0,7,32768); samples(7,64,0); +} + +TEST_F(DssiTests, MultipleSynthCallbackReceivesEveryActiveInstance) { + ASSERT_EQ(run(load(1,"gih") + load(1,"gih2") + R"( +instr 1 + dssiactivate gih,1 + dssiactivate gih2,1 + konce init 1 + dssievent konce,gih,60,127 + dssievent konce,gih2,64,127 + konce=0 + a1,a2 dssisynth gih + a3,a4 dssisynth gih2 + outs a1+a3,a2+a4 +endin +)","i1 0 [64/8192]"),0) << messages(); + samples(0,64,2); + EXPECT_EQ(stat(4),2); + EXPECT_EQ(stat(3),4); +} + +TEST_F(DssiTests, LadspaEffectUsesInputAndOutputScaling) { + ASSERT_EQ(run(load(0,"gih",CSOUND_TEST_LADSPA_PLUGIN) + R"( +instr 1 + dssiactivate gih,1 + dssictls gih,1,.5,1 + ain init 16384 + a1,a2 dssiaudio gih,ain + outs a1,a2 +endin +)","i1 0 .01",16,32768),0) << messages(); + samples(0,64,8192); +} + +TEST_F(DssiTests, DssiAudioAllowsOmittedInputs) { + // The audio inputs are optional. Every other dssiaudio call in this suite + // supplies them, so a regression that made them mandatory would go + // unnoticed without this case. + ASSERT_EQ(run(load() + R"( +instr 1 + dssiactivate gih,1 + a1,a2 dssiaudio gih + outs a1,a2 +endin +)"), 0) << messages(); +} + +TEST_F(DssiTests, RejectsBadDescriptorWithoutPublishingAnInstance) { + EXPECT_NE(run(load(99)),0); + EXPECT_EQ(stat(0),0); +} + +TEST_F(DssiTests, RejectsBankBeyondUnsigned32BitRange) { + // 2^32 is exact in float, but UINT32_MAX is not. + EXPECT_NE(run(load() + R"( +instr 1 + dssiprogram gih,4294967296,0,1 +endin +)"), 0); + const auto error = messages(); + EXPECT_NE(error.find("invalid bank or program"), std::string::npos) << error; +} + +TEST_F(DssiTests, RejectsProgramBeyondUnsigned32BitRange) { + EXPECT_NE(run(load() + R"( +instr 1 + dssiprogram gih,0,4294967296,1 +endin +)"), 0); + const auto error = messages(); + EXPECT_NE(error.find("invalid bank or program"), std::string::npos) << error; +} + +TEST_F(DssiTests, RejectsMalformedDescriptor) { + EXPECT_NE(run(load(3)),0); + EXPECT_EQ(stat(0),0); +} + +TEST_F(DssiTests, RejectsAudioPortUsedAsControl) { + EXPECT_NE(run(load()+"instr 1\ndssictls gih,3,1,1\nendin\n"),0); + EXPECT_NE(messages().find("input control port"),std::string::npos); +} + +TEST_F(DssiTests, RejectsInvalidActivationHandle) { + EXPECT_NE(run("instr 1\ndssiactivate -1,1\nendin\n"),0); + EXPECT_NE(messages().find("invalid handle"),std::string::npos); +} + +TEST_F(DssiTests, RejectsTwoRenderersForOneInstance) { + EXPECT_NE(run(load()+R"( +instr 1 + a1,a2 dssisynth gih + a3,a4 dssisynth gih +endin +)"),0); + EXPECT_NE(messages().find("exactly one audio renderer"),std::string::npos); +} + +TEST_F(DssiTests, QueueOverflowReportsAnError) { + EXPECT_NE(run(load()+R"( +instr 1 + dssiactivate gih,1 + kindex=0 + while kindex < 1025 do + dssievent 1,gih,60,127 + kindex+=1 + od + a1,a2 dssisynth gih +endin +)"),0); + EXPECT_NE(messages().find("event queue is full"),std::string::npos); +} + +TEST_F(DssiTests, DiscoveryIncludesDssiOnlyLibrary) { + std::string path = CSOUND_TEST_DSSI_PLUGIN; + csoundSetOption(csound,("--env:DSSI_PATH="+path.substr(0,path.find_last_of('/'))).c_str()); + ASSERT_EQ(run("dssilist\ninstr 1\nendin\n"),0) << messages(); + EXPECT_NE(messages().find("fixture (fixture)"),std::string::npos); +} + +TEST_F(DssiTests, ConfigurationErrorsReachCsound) { + EXPECT_NE(run(load()+"dssiconfigure gih,\"bad\",\"value\"\n"),0); + EXPECT_NE(messages().find("unknown fixture key"),std::string::npos); +} + +TEST_F(DssiTests, NrpnMappingChangesControlsWithoutDuplicateEvents) { + ASSERT_EQ(run(load()+R"( +instr 1 + dssiactivate gih,1 + konce init 1 + dssievent konce,gih,60,127 + dssinrpn konce,gih,0,42,0,9 + konce=0 + a1,a2 dssisynth gih + kcount dssiget gih,2 + chnset kcount,"events" + outs a1,a2 +endin +)"),0) << messages(); + samples(0,9,1); samples(9,64,0); + EXPECT_EQ(csoundGetControlChannel(csound,"events",nullptr),1); +} + +TEST_F(DssiTests, ResumeStopsOldNotesWhenPluginOmitsActivationCallbacks) { + ASSERT_EQ(run(load(2)+R"( +instr 1 + kcycle timeinstk + kactive = (kcycle == 2 ? 0 : 1) + dssiactivate gih,kactive + dssievent (kcycle == 1 ? 1 : 0),gih,60,127 + a1,a2 dssisynth gih + outs a1,a2 +endin +)"),0) << messages(); + samples(0,16,1); samples(16,64,0); +} + +TEST_F(DssiTests, GlobalConfigurationAppliesToEachMatchingInstance) { + ASSERT_EQ(run(load()+load(0,"gih2")+R"( +dssiconfigure gih,"GLOBAL:bias",".125" +instr 1 + dssiactivate gih,1 + dssiactivate gih2,1 + a1,a2 dssisynth gih + a3,a4 dssisynth gih2 + outs a1+a3,a2+a4 +endin +)"),0) << messages(); + samples(0,64,.25); +} + +TEST_F(DssiTests, RejectsEventsSentAfterTheRenderer) { + EXPECT_NE(run(load()+R"( +instr 1 + dssiactivate gih,1 + a1,a2 dssisynth gih + dssievent 1,gih,60,127 +endin +)"),0); + EXPECT_NE(messages().find("before rendering"),std::string::npos); +} + +TEST_F(DssiTests, ProgramNamesAreCopiedBeforeNextEnumeration) { + ASSERT_EQ(run(load()+R"( +Sfirst,ibank,iprog dssiprograminfo gih,0 +Ssecond,ibank2,iprog2 dssiprograminfo gih,1 +idiff strcmp Sfirst,"first" +chnset idiff,"diff" +Snone,ibank3,iprog3 dssiprograminfo gih,2 +chnset ibank3,"end" +instr 1 +endin +)"),0) << messages(); + EXPECT_EQ(csoundGetControlChannel(csound,"diff",nullptr),0); + EXPECT_EQ(csoundGetControlChannel(csound,"end",nullptr),-1); +} + +TEST_F(DssiTests, SustainedAndRepeatedNotesDoNotSurviveResume) { + ASSERT_EQ(run(load(2)+R"( +instr 1 + kcycle timeinstk + kactive = (kcycle == 2 ? 0 : 1) + dssiactivate gih,kactive + konce = (kcycle == 1 ? 1 : 0) + dssievent konce,gih,176,0,64,127 + dssievent konce,gih,144,0,60,127 + dssievent konce,gih,144,0,60,127 + dssievent konce,gih,128,0,60,0,8 + dssievent konce,gih,128,0,60,0,8 + a1,a2 dssisynth gih + outs a1,a2 +endin +)"),0) << messages(); + samples(0,16,1); samples(16,64,0); +} + +TEST_F(DssiTests, LateActivationCannotRenderAGroupTwice) { + EXPECT_NE(run(load(1)+load(1,"gih2")+R"( +instr 1 + dssiactivate gih,1 + dssievent 1,gih,60,127 + a1,a2 dssisynth gih +endin +instr 2 + dssiactivate gih2,1 + a1,a2 dssisynth gih2 +endin +)","i1 0 .01\ni2 0 .01"),0); + EXPECT_NE(messages().find("activate all grouped instances"),std::string::npos); + EXPECT_EQ(stat(4),1); +} + +TEST_F(DssiTests, FailedFirstLoadThenInvalidHandleReportsInitErrors) { + EXPECT_NE(run(R"( +instr 1 + ih dssiinit "/does/not/exist.so",0,0 +endin +instr 2 + dssiactivate 4096,1 +endin +)","i1 0 .002\ni2 .004 .002"),0); + EXPECT_NE(messages().find("invalid handle"),std::string::npos); +} diff --git a/src/dssi4cs/tests/fixtures/dssi_test_plugin.c b/src/dssi4cs/tests/fixtures/dssi_test_plugin.c new file mode 100644 index 00000000..2f354da9 --- /dev/null +++ b/src/dssi4cs/tests/fixtures/dssi_test_plugin.c @@ -0,0 +1,195 @@ +/* Deterministic native plugin for DSSI/LADSPA host integration tests. + Built against the same vendored headers as the host, so the ABI matches. */ +#include +#include +#include +#include + +#define EXPORT __attribute__((visibility("default"))) +static int live, cleanups, failures, group_calls, largest_group; +static int active[4]; +typedef struct { + LADSPA_Data *ports[8]; + float voice; + int held, sustain; + int kind; + unsigned int events, program; +} Instance; + +EXPORT int dssi_test_stat(int which) +{ + switch (which) { + case 0: return live; + case 1: return cleanups; + case 2: return failures; + case 3: return group_calls; + case 4: return largest_group; + default: return -1; + } +} +EXPORT void dssi_test_reset(void) +{ + if (live) ++failures; + cleanups = failures = group_calls = largest_group = 0; + memset(active, 0, sizeof(active)); +} + +static LADSPA_Handle instantiate(const LADSPA_Descriptor *d, unsigned long sr) +{ + (void)sr; + Instance *p = calloc(1, sizeof(*p)); + p->kind = (int)(d->UniqueID - 900001); + ++live; + return p; +} +static void connect_port(LADSPA_Handle h, unsigned long port, LADSPA_Data *data) +{ ((Instance *)h)->ports[port] = data; } +static void activate(LADSPA_Handle h) +{ Instance *p = h; p->voice = 0; ++active[p->kind]; } +static void deactivate(LADSPA_Handle h) +{ Instance *p = h; p->voice = 0; --active[p->kind]; } +static void cleanup(LADSPA_Handle h) +{ --live; ++cleanups; free(h); } + +static void run_synth(LADSPA_Handle h, unsigned long n, snd_seq_event_t *events, + unsigned long count) +{ + Instance *p = h; + unsigned long e = 0; + for (unsigned long i = 0; i < count; ++i) + if (events[i].time.tick >= n || (i && events[i].time.tick < events[i-1].time.tick)) ++failures; + for (unsigned long i = 0; i < n; ++i) { + while (e < count && events[e].time.tick == i) { + snd_seq_event_t *event = &events[e++]; + ++p->events; + *p->ports[7] = event->time.tick; + if (event->type == SND_SEQ_EVENT_NOTEON) { + if (!event->data.note.velocity) ++failures; + ++p->held; + p->voice = event->data.note.velocity / 127.f; + } + else if (event->type == SND_SEQ_EVENT_NOTEOFF) { + if (p->held) --p->held; + if (!p->held && !p->sustain) p->voice = 0; + } + else if (event->type == SND_SEQ_EVENT_CONTROLLER && event->data.control.param == 64) { + p->sustain = event->data.control.value >= 64; + if (!p->sustain && !p->held) p->voice = 0; + } + else if (event->type == SND_SEQ_EVENT_CONTROLLER && + (event->data.control.param == 120 || event->data.control.param == 123)) { + p->held = 0; + p->voice = 0; + } + else if (event->type == SND_SEQ_EVENT_PGMCHANGE || event->type == SND_SEQ_EVENT_NOTE) + ++failures; + else if (event->type == SND_SEQ_EVENT_CONTROLLER && + (event->data.control.param == 0 || event->data.control.param == 32 || + event->data.control.param == 7)) ++failures; + } + float input = p->kind == 1 ? 0 : p->ports[3][i]; +#ifdef DSSI_TEST_LADSPA + p->voice = 0; +#endif + p->ports[0][i] = (input + p->voice) * *p->ports[1] + *p->ports[4]; + p->ports[5][i] = p->ports[0][i]; + } + *p->ports[2] = p->events; + *p->ports[6] = p->program; +} +#ifdef DSSI_TEST_LADSPA +static void run(LADSPA_Handle h, unsigned long n) { run_synth(h, n, NULL, 0); } +#endif +static void run_multiple(unsigned long count, LADSPA_Handle *handles, + unsigned long n, snd_seq_event_t **events, + unsigned long *counts) +{ + ++group_calls; + if ((int)count > largest_group) largest_group = (int)count; + if ((int)count != active[1]) ++failures; + for (unsigned long i = 0; i < count; ++i) run_synth(handles[i], n, events[i], counts[i]); +} +static int midi_mapping(LADSPA_Handle h, unsigned long port) +{ (void)h; return port == 1 ? DSSI_CC(7) | DSSI_NRPN(42) : DSSI_NONE; } +static const DSSI_Program_Descriptor *get_program(LADSPA_Handle h, unsigned long index) +{ + (void)h; + static DSSI_Program_Descriptor p; + static char name[32]; + if (index > 1) return NULL; + strcpy(name, index ? "second" : "first"); + p.Bank = index ? 130 : 0; + p.Program = index ? 5 : 0; + p.Name = name; + return &p; +} +static void select_program(LADSPA_Handle h, unsigned long bank, unsigned long program) +{ + Instance *p = h; + p->program = (unsigned int)(bank * 128 + program); + *p->ports[1] = program == 5 ? .5f : 1.f; +} +static char *configure(LADSPA_Handle h, const char *key, const char *value) +{ + Instance *p = h; + if (!strcmp(key, "bias") || !strcmp(key, "GLOBAL:bias")) { + *p->ports[4] = atof(value); + return NULL; + } + return strdup("unknown fixture key"); +} + +static const LADSPA_PortDescriptor ports[] = { + LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT, LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, + LADSPA_PORT_CONTROL | LADSPA_PORT_OUTPUT, LADSPA_PORT_AUDIO | LADSPA_PORT_INPUT, + LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT, LADSPA_PORT_AUDIO | LADSPA_PORT_OUTPUT, + LADSPA_PORT_CONTROL | LADSPA_PORT_OUTPUT, LADSPA_PORT_CONTROL | LADSPA_PORT_OUTPUT +}; +static const char *names[] = {"left", "gain", "events", "input", "bias", "right", "program", "tick"}; +static const LADSPA_PortRangeHint hints[] = { + {0,0,0}, {LADSPA_HINT_BOUNDED_BELOW | LADSPA_HINT_BOUNDED_ABOVE | LADSPA_HINT_DEFAULT_1, 0,1}, + {0,0,0}, {0,0,0}, {LADSPA_HINT_DEFAULT_0,0,0}, {0,0,0}, {0,0,0}, {0,0,0} +}; +static LADSPA_Descriptor descriptors[4]; +static DSSI_Descriptor synths[4]; +static LADSPA_PortDescriptor multi_ports[8]; +static int initialized; +static void init(void) +{ + if (initialized) return; + initialized = 1; + memcpy(multi_ports, ports, sizeof(ports)); + multi_ports[3] = LADSPA_PORT_CONTROL | LADSPA_PORT_INPUT; + const char *labels[] = {"fixture", "multi", "optional", "invalid"}; + for (int i = 0; i < 4; ++i) { + descriptors[i] = (LADSPA_Descriptor){ + .UniqueID = 900001 + i, .Label = labels[i], .Name = labels[i], + .Maker = "Csound", .Copyright = "LGPL-2.1-or-later", .PortCount = 8, + .PortDescriptors = i == 1 ? multi_ports : ports, .PortNames = names, + .PortRangeHints = hints, .instantiate = instantiate, .connect_port = connect_port, + .activate = i == 2 ? NULL : activate, .deactivate = i == 2 ? NULL : deactivate, + .cleanup = cleanup, +#ifdef DSSI_TEST_LADSPA + .run = run +#else + .run = NULL +#endif + }; + synths[i] = (DSSI_Descriptor){.DSSI_API_Version = 1, .LADSPA_Plugin = &descriptors[i], + .configure = i == 2 ? NULL : configure, + .get_program = i == 2 ? NULL : get_program, + .select_program = i == 2 ? NULL : select_program, + .get_midi_controller_for_port = i == 2 ? NULL : midi_mapping, + .run_synth = i == 1 ? NULL : run_synth, + .run_multiple_synths = i == 1 ? run_multiple : NULL}; + } + descriptors[3].connect_port = NULL; +} +#ifdef DSSI_TEST_LADSPA +EXPORT const LADSPA_Descriptor *ladspa_descriptor(unsigned long i) +{ init(); return i < 4 ? &descriptors[i] : NULL; } +#else +/* Deliberately export only dssi_descriptor: discovery must handle this. */ +EXPORT const DSSI_Descriptor *dssi_descriptor(unsigned long i) +{ init(); return i < 4 ? &synths[i] : NULL; } +#endif diff --git a/src/dssi4cs/utils.h b/src/dssi4cs/utils.h new file mode 100644 index 00000000..e882912c --- /dev/null +++ b/src/dssi4cs/utils.h @@ -0,0 +1,14 @@ +/* Native DSSI/LADSPA library lookup. */ +#ifndef DSSI4CS_UTILS_H +#define DSSI4CS_UTILS_H +#include "csdl.h" +/* Search path used when neither DSSI_PATH nor LADSPA_PATH is set. The build + may override this (see CMakeLists.txt) to prepend the multiarch library + directory detected on the build host, e.g. /usr/lib/x86_64-linux-gnu. */ +#ifndef DSSI4CS_DEFAULT_PATH +#define DSSI4CS_DEFAULT_PATH \ + "/usr/local/lib/dssi:/usr/lib/dssi:/usr/lib64/dssi:" \ + "/usr/local/lib/ladspa:/usr/lib/ladspa:/usr/lib64/ladspa" +#endif +void *dlopenLADSPA(CSOUND *, const char *, int32_t); +#endif diff --git a/src/faustcsound/CMakeLists.txt b/src/faustcsound/CMakeLists.txt new file mode 100644 index 00000000..70b8e070 --- /dev/null +++ b/src/faustcsound/CMakeLists.txt @@ -0,0 +1,57 @@ +# Build faustcsound opcodes +option(BUILD_FAUST_OPCODES "Build the Faust opcodes" ON) + +find_package(FAUST) +check_deps(BUILD_FAUST_OPCODES FAUST_FOUND) + +if(BUILD_FAUST_OPCODES AND FAUST_FOUND) + message(STATUS "Faust library: ${FAUST_LIBRARY}") + + make_plugin(faustcsound faustgen.cpp) + + target_include_directories(faustcsound PRIVATE + ${CSOUND_INCLUDE_DIRS} + ${FAUST_INCLUDE_DIR}) + target_compile_definitions(faustcsound PRIVATE "-DFAUSTFLOAT=cs_float") + + if(PTHREAD_LIBRARY) + target_compile_definitions(faustcsound PRIVATE "-DHAVE_PTHREAD") + endif() + + target_link_libraries(faustcsound + ${FAUST_LIBRARIES} + ${PTHREAD_LIBRARY} + ${CMAKE_DL_LIBS}) + + if(APPLE) + set_target_properties(faustcsound PROPERTIES + INSTALL_RPATH_USE_LINK_PATH TRUE + ) + endif() + + if(FAUST_STATIC) + # libfaustwithllvm bundles LLVM; without this the plugin would export + # the hundreds of thousands of LLVM/Faust symbols from the archive. + if(APPLE) + set_target_properties(faustcsound PROPERTIES LINK_FLAGS + "-Wl,-exported_symbols_list,${CMAKE_CURRENT_SOURCE_DIR}/faustcsound.exports") + elseif(UNIX) + set_target_properties(faustcsound PROPERTIES LINK_FLAGS + "-Wl,--exclude-libs,ALL") + endif() + endif() + + # Install the Faust standard library next to the plugin and point the + # plugin at it, so programs using import("stdfaust.lib") work without a + # separate Faust installation. + if(FAUST_STDLIB_DIR) + message(STATUS "Faust standard library: ${FAUST_STDLIB_DIR}") + install(DIRECTORY "${FAUST_STDLIB_DIR}/" + DESTINATION "${PLUGIN_INSTALL_DIR}/faust") + target_compile_definitions(faustcsound PRIVATE + "DEFAULT_FAUST_LIB_PATH=\"${PLUGIN_INSTALL_DIR}/faust\"") + else() + message(WARNING "Faust standard library (stdfaust.lib) not found; " + "set FAUST_STDLIB_DIR_HINT to enable import(\"stdfaust.lib\")") + endif() +endif() diff --git a/src/faustcsound/README.md b/src/faustcsound/README.md new file mode 100644 index 00000000..aef59eb8 --- /dev/null +++ b/src/faustcsound/README.md @@ -0,0 +1,72 @@ +Faust opcodes +======= + +These opcodes embed the [Faust](https://faust.grame.fr/) compiler and DSP +runtime into Csound: + +* `faustgen` - compiles and runs a single Faust program +* `faustcompile` - compiles a Faust program, returning a handle +* `faustaudio` - creates a DSP instance from a compiled program +* `faustdsp` - creates a DSP instance (no audio outputs) from a compiled program +* `faustplay` - runs an existing DSP instance with explicit inputs/outputs +* `faustctl` - sets the value of a control of a DSP instance + +Building +-------- + +To build the Faust opcodes you need the Faust library (headers and +`libfaust`), which can be downloaded as a binary package from + or built from source +(which also requires libcrypto and LLVM). + +When using the pre-built binary packages the following CMake variables +are important: + +* `FAUST_INCLUDE_DIR_HINT` - the `include` directory of the package + (contains `faust/dsp/llvm-dsp.h`). +* `FAUST_LIB_DIR_HINT` - the `lib` directory of the package. +* `FAUST_STDLIB_DIR_HINT` - the directory containing `stdfaust.lib` + (`share/faust` in the binary packages, `libraries` in the source tree). +* `FAUST_STATIC` - when `ON`, link against the self-contained + `libfaustwithllvm` archive, which bundles LLVM so the plugin has no + runtime dependency on `libfaust`. This produces a much larger plugin. + +For example, if Faust 2.88.0 is placed in the top-level user directory: + +``` +cmake .. -DFAUST_INCLUDE_DIR_HINT=$HOME/faust-2.88.0/include \ + -DFAUST_LIB_DIR_HINT=$HOME/faust-2.88.0/lib \ + -DFAUST_STDLIB_DIR_HINT=$HOME/faust-2.88.0/share/faust \ + -DFAUST_STATIC=ON +``` + +Without `FAUST_STATIC` the plugin is linked to `libfaust` dynamically and +`libfaust` must be locatable at runtime (the build sets an rpath to the +library directory by default). With `FAUST_STATIC=ON` the LLVM/Faust +symbols are linked in and hidden, so only the opcode entry points are +exported. + +The Faust standard library is installed next to the plugin (in a `faust` +subdirectory of the plugin directory) and the plugin looks for it there, +so `import("stdfaust.lib")` works without a separate Faust installation. +Set `FAUST_LIB_PATH` to override this, e.g. + +``` +export FAUST_LIB_PATH=$HOME/faust-2.88.0/share/faust +``` + +Faust programs that use the standard library (e.g. `os.osc`) need +`import("stdfaust.lib");`. + +Notes on the Csound 7 port +-------------------------- + +* `MYFLT` was replaced by `cs_float` and `FAUSTFLOAT` is defined to + `cs_float`, so the plugin always matches the Csound sample type. +* Opcode cleanup uses the `OENTRY` `deinit` field instead of the removed + `RegisterDeinitCallback`; `faustcompile` still uses a reset callback so + that compiled factories outlive the compiling instrument. +* `CreateThread2` was removed from the Csound API, so the `istacksize` + argument of `faustcompile` is now accepted but ignored. `faustgen` + compiles synchronously on the calling (init) thread, and asynchronous + `faustcompile` uses the default thread stack size. diff --git a/src/faustcsound/faustcsound.exports b/src/faustcsound/faustcsound.exports new file mode 100644 index 00000000..663cc217 --- /dev/null +++ b/src/faustcsound/faustcsound.exports @@ -0,0 +1,4 @@ +_csoundModuleCreate +_csoundModuleInit +_csoundModuleDestroy +_csoundModuleInfo diff --git a/6.x/src/faustcsound/faustgen.cpp b/src/faustcsound/faustgen.cpp similarity index 53% rename from 6.x/src/faustcsound/faustgen.cpp rename to src/faustcsound/faustgen.cpp index bf885305..108951fe 100644 --- a/6.x/src/faustcsound/faustgen.cpp +++ b/src/faustcsound/faustgen.cpp @@ -16,8 +16,7 @@ You should have received a copy of the GNU Lesser General Public License along with Csound; if not, write to the Free Software - Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA - 02110-1301 USA + Foundation, Inc., 31 Milk Street, #960789, Boston, MA, 02196, USA Faust Csound opcodes @@ -32,18 +31,108 @@ faustgen: compiles and creates a single DSP instance from a Faust program (convenient for one-off Faust programs) - */ +/* The C++ standard headers must be included before "csdl.h": the latter pulls + in Csound's "interlocks.h", which defines short generic macros such as + _CR and TR that clash with identifiers used inside (reached via + ) in both libstdc++ and the MSVC STL. */ +#include +#include +#include #include "csdl.h" +#ifndef FAUSTFLOAT +#define FAUSTFLOAT cs_float +#endif #include "faust/dsp/llvm-dsp.h" #include "faust/gui/UI.h" -#include -#if defined(MACOSX) || defined(linux) || defined(HAIKU) -#include +#ifdef _WIN32 +#include +#else +#include +#include #endif #define MAXARG 40 +/* Faust/LLVM factory creation is not thread-safe, so all calls to + createDSPFactoryFromString are serialised by this lock. It is deliberately + separate from the registry lock: a compilation can take a long time, and + holding the registry lock for its duration would stall factory lookups + (and thus the performance thread) in faustdsp/faustaudio. */ +static std::mutex faust_compile_mutex; + +/* Protects the shared ::factory and ::dsp registry lists. Held only for the + short operations that read or modify those lists. */ +static std::mutex faust_registry_mutex; + +static int dir_exists(const char *p) { +#ifdef _WIN32 + DWORD a = GetFileAttributesA(p); + return (a != INVALID_FILE_ATTRIBUTES && (a & FILE_ATTRIBUTE_DIRECTORY)); +#else + struct stat st; + return (stat(p, &st) == 0 && S_ISDIR(st.st_mode)); +#endif +} + +static void get_plugin_dir(char *out, size_t n) { + out[0] = '\0'; +#ifdef _WIN32 + HMODULE hm = NULL; + GetModuleHandleExA(GET_MODULE_HANDLE_EX_FLAG_FROM_ADDRESS | + GET_MODULE_HANDLE_EX_FLAG_UNCHANGED_REFCOUNT, + (LPCSTR)&get_plugin_dir, &hm); + GetModuleFileNameA(hm, out, (DWORD)n); +#else + Dl_info info; + if (dladdr((void *)&get_plugin_dir, &info) && info.dli_fname) + snprintf(out, n, "%s", info.dli_fname); +#endif + char *slash = strrchr(out, '/'); +#ifdef _WIN32 + char *bslash = strrchr(out, '\\'); + if (!slash || (bslash && bslash > slash)) + slash = bslash; +#endif + if (slash) + *slash = '\0'; +} + +static void set_faust_lib_path(const char *path) { +#ifdef _WIN32 + _putenv_s("FAUST_LIB_PATH", path); +#else + setenv("FAUST_LIB_PATH", path, 1); +#endif +} + +/* Locate the Faust standard library directory so that programs using + import("stdfaust.lib") work without a separate Faust installation. A + user-provided FAUST_LIB_PATH always takes precedence; otherwise we look + for a "faust" directory next to the plugin, then fall back to the path + configured at build time. */ +static void setup_faust_path(CSOUND *csound) { + const char *env = getenv("FAUST_LIB_PATH"); + if (env != NULL && env[0] != '\0') + return; + char dir[4096], cand[4096]; + get_plugin_dir(dir, sizeof(dir)); + if (dir[0] != '\0') { + snprintf(cand, sizeof(cand), "%s/faust", dir); + if (dir_exists(cand)) { + set_faust_lib_path(cand); + return; + } + } +#ifdef DEFAULT_FAUST_LIB_PATH + if (dir_exists(DEFAULT_FAUST_LIB_PATH)) { + set_faust_lib_path(DEFAULT_FAUST_LIB_PATH); + return; + } +#endif + (void)csound; +} + /** * Faust controls class for Csound * @@ -53,7 +142,7 @@ class controls : public UI { struct ctl { FAUSTFLOAT *zone; char label[65]; - MYFLT min, max; + cs_float min, max; ctl *nxt; } anchor; @@ -65,7 +154,7 @@ class controls : public UI { pctl->nxt = new ctl; pctl = pctl->nxt; strncpy(pctl->label, label, 63); - pctl->label[63] = '\n'; pctl->label[64] = '\0'; + pctl->label[63] = '\0'; pctl->label[64] = '\0'; pctl->zone = zone; pctl->min = min; pctl->max = max; @@ -133,7 +222,7 @@ class controls : public UI { else return NULL; } - MYFLT getMax(char *label) { + cs_float getMax(char *label) { ctl *pctl = &anchor; pctl = pctl->nxt; while (pctl) { @@ -146,7 +235,7 @@ class controls : public UI { else return 0; } - MYFLT getMin(char *label) { + cs_float getMin(char *label) { ctl *pctl = &anchor; pctl = pctl->nxt; while (pctl) { @@ -176,25 +265,36 @@ struct faustobj { * Faust compile opcode usage: - ihandle faustcompile Scode, Sargs[,iasync, istacksize] + ihandle faustcompile Scode, Sargs[,iasync, istacksize[,Starget]] ihandle - handle to compiled code Scode - Faust program Sargs - Faust compiler args - istacksize - compiler stack size in megabytes (default 1MB). + istacksize - compiler stack size in megabytes (default 1MB). NOTE: this + argument is accepted for backwards compatibility but is currently ignored, + as the Csound 7 threading API no longer supports setting the stack size. iasync - async operation (1 = on, 0 = off) default to 1 + Starget - optional LLVM target string (empty selects the native target) **/ struct faustcompile { OPDS h; - MYFLT *hptr; + cs_float *hptr; STRINGDAT *code; STRINGDAT *args; - MYFLT *async; - MYFLT *stacksize; - MYFLT *extra; + cs_float *async; + cs_float *stacksize; + STRINGDAT *target; /* optional LLVM target string */ +}; + +/* Persistent state for a compiled factory. The asynchronous compilation + thread and the reset callback must not refer to the per-note faustcompile + opcode, whose memory is released as soon as the instrument that runs + faustcompile ends. */ +struct faustcompile_state { + CSOUND *csound; llvm_dsp_factory *factory; - uintptr_t thread; - void *lock; + void *thread; + faustobj *node; /* reserved ::factory slot, published by the worker */ }; char **parse_cmd(CSOUND *csound, char *str, int32_t *argc) { @@ -233,139 +333,229 @@ char **parse_cmd(CSOUND *csound, char *str, int32_t *argc) { int32_t delete_faustcompile(CSOUND *csound, void *p) { - faustcompile *pp = ((faustcompile *)p); + faustcompile_state *st = ((faustcompile_state *)p); faustobj *fobj, *prv, **pfobj; - csound->JoinThread((void *) pp->thread); - pfobj = (faustobj **)csound->QueryGlobalVariable(csound, "::factory"); - if(pfobj != NULL) { - fobj = *pfobj; - prv = fobj; - while (fobj != NULL) { - if (fobj->obj == (void *)pp->factory) { - prv->nxt = fobj->nxt; - break; + if (st->thread) + csound->JoinThread(st->thread); + { + std::lock_guard guard(faust_registry_mutex); + pfobj = (faustobj **)csound->QueryGlobalVariable(csound, "::factory"); + if(pfobj != NULL) { + fobj = *pfobj; + prv = NULL; + while (fobj != NULL) { + if (fobj == st->node) { + if (prv == NULL) + *pfobj = fobj->nxt; + else + prv->nxt = fobj->nxt; + break; + } + prv = fobj; + fobj = fobj->nxt; + } + if (fobj != NULL) { + if (st->factory != NULL) + deleteDSPFactory(st->factory); + csound->Free(csound, fobj); } - prv = fobj; - fobj = fobj->nxt; - } - if (fobj != NULL) { - if (*pfobj == fobj) - *pfobj = fobj->nxt; - deleteDSPFactory(pp->factory); - csound->Free(csound, fobj); } } - csound->DestroyMutex(pp->lock); + csound->Free(csound, st); return OK; } struct hdata { - faustcompile *p; CSOUND *csound; + char *code; + char *args; + char *target; + faustcompile_state *state; }; +/* A factory slot is published in three states: obj == NULL while the + asynchronous compilation is still running, obj == (void *)-1 if it + failed, and otherwise obj is the llvm_dsp_factory. */ +#define FAUST_SLOT_PENDING ((void *)NULL) +#define FAUST_SLOT_FAILED ((void *)-1L) + void *init_faustcompile_thread(void *pp) { - faustcompile *p = ((hdata *)pp)->p; - faustobj **pffactory, *ffactory; - CSOUND *csound = ((hdata *)pp)->csound; + hdata *hd = (hdata *)pp; + CSOUND *csound = hd->csound; llvm_dsp_factory *factory; int32_t argc = 0; std::string err_msg; - char *cmd = (char *) csound->Calloc(csound, p->args->size + 9); - char *ccode = csound->Strdup(csound, p->code->data); - char *extra; - MYFLT test = *p->extra; - int32_t ret; - strcpy(cmd, p->args->data); + size_t alen = hd->args ? strlen(hd->args) : 0; + char *cmd = (char *) csound->Calloc(csound, alen + 9); + const char *ccode = hd->code; + const char *target = hd->target ? hd->target : ""; + if (alen) + strcpy(cmd, hd->args); #ifdef USE_DOUBLE strcat(cmd, " -double"); #endif const char **argv = (const char **) parse_cmd(csound, cmd, &argc); - const char *varname = "::factory"; - - if(test) - extra = ((STRINGDAT *) p->extra)->data; - else - extra = (char *) ""; - // Need to protect this - csound->LockMutex((void *) p->lock); - // csound->Message(csound, "lock %p\n", p->lock); - factory = createDSPFactoryFromString("faustop", (const char *) ccode, - argc, argv, extra, err_msg, 3); - // csound->Message(csound, "unlock %p\n", p->lock); - csound->UnlockMutex((void *) p->lock); + { + std::lock_guard guard(faust_compile_mutex); + factory = createDSPFactoryFromString("faustop", ccode, + argc, argv, target, err_msg, 3); + } + hd->state->factory = factory; - if (factory == NULL) { + if (factory == NULL) csound->Message(csound, Str("\nFaust compilation problem:\nline %s\n"), err_msg.c_str()); - *(p->hptr) = FL(-2.0); // error code. - csound->Free(csound, argv); - csound->Free(csound, cmd); - csound->Free(csound, ccode); - csound->Free(csound, pp); - ret = -1; - return NULL; + + /* Publish the outcome into the slot reserved during init; never touch the + per-note output pointer, which may already have been reclaimed. */ + { + std::lock_guard guard(faust_registry_mutex); + if (hd->state->node != NULL) + hd->state->node->obj = factory ? (void *)factory : FAUST_SLOT_FAILED; } + csound->Free(csound, argv); + csound->Free(csound, cmd); + csound->Free(csound, hd->code); + csound->Free(csound, hd->args); + if (hd->target) + csound->Free(csound, hd->target); + csound->Free(csound, hd); + + return NULL; +} + +static uintptr_t init_faustcompile_thread_runner(void *pp) { + init_faustcompile_thread(pp); + return 0; +} + +/* Append a new (pending) entry to the ::factory registry and return it. Must + be called with faust_registry_mutex held. */ +static faustobj *reserve_factory_slot(CSOUND *csound) { + faustobj **pffactory, *ffactory, *fnew; + const char *varname = "::factory"; + pffactory = (faustobj **)csound->QueryGlobalVariable(csound, varname); if (pffactory == NULL) { csound->CreateGlobalVariable(csound, varname, sizeof(faustobj *)); pffactory = (faustobj **)csound->QueryGlobalVariable(csound, varname); - ffactory = (faustobj *)csound->Calloc(csound, sizeof(faustobj)); - ffactory->obj = factory; - ffactory->nxt = NULL; - ffactory->cnt = 0ul; - *pffactory = ffactory; - } else { - ffactory = *pffactory; - while (ffactory->nxt) { - ffactory = ffactory->nxt; - } - ffactory->nxt = (faustobj *) csound->Calloc(csound, sizeof(faustobj)); - ffactory->nxt->cnt = ffactory->cnt+1ul; - ffactory = ffactory->nxt; - ffactory->obj = factory; } - p->factory = factory; - if(p->hptr) - *p->hptr = FL(ffactory->cnt); - csound->Free(csound, argv); - csound->Free(csound, cmd); - csound->Free(csound, ccode); - csound->Free(csound, pp); + ffactory = *pffactory; + if (ffactory == NULL) { + fnew = (faustobj *)csound->Calloc(csound, sizeof(faustobj)); + fnew->obj = FAUST_SLOT_PENDING; + fnew->nxt = NULL; + fnew->cnt = 0ul; + *pffactory = fnew; + return fnew; + } + while (ffactory->nxt != NULL) + ffactory = ffactory->nxt; + fnew = (faustobj *)csound->Calloc(csound, sizeof(faustobj)); + fnew->obj = FAUST_SLOT_PENDING; + fnew->nxt = NULL; + fnew->cnt = ffactory->cnt + 1ul; + ffactory->nxt = fnew; + return fnew; +} - //csound->Message(csound, "Successfully compiled faust code\n"); - return NULL; +/* Resolve a faustcompile handle to its registry entry, waiting for an + asynchronous compilation to publish the factory. Returns NULL (after + issuing an error) on failure. */ +static faustobj *resolve_factory(CSOUND *csound, int32_t handle) { + faustobj **fobjp, *fobj = NULL; + int timout = 0; + int failed = (handle < 0); + + if (!failed) { + fobjp = (faustobj **)csound->QueryGlobalVariable(csound, "::factory"); + if (fobjp == NULL) { + csound->InitError(csound, "%s", Str("no factory available\n")); + return NULL; + } + for (;;) { + { + std::lock_guard guard(faust_registry_mutex); + fobj = *fobjp; + while (fobj != NULL && (int32_t)fobj->cnt != handle) + fobj = fobj->nxt; + if (fobj == NULL || fobj->obj != FAUST_SLOT_PENDING) + break; + } + csound->Sleep(1); + if (++timout > 1000) { + csound->InitError(csound, "%s", Str("Faust code was not ready. Try compiling it \n" + "in a separate instrument prior to running it here\n")); + return NULL; + } + } + if (fobj == NULL) { + csound->InitError(csound, Str("factory not found %d\n"), handle); + return NULL; + } + failed = (fobj->obj == FAUST_SLOT_FAILED); + } + if (failed) { + csound->InitError(csound, "%s", Str("Faust code did not compile properly.\n" + "Check above messages for Faust compiler errors\n")); + return NULL; + } + return fobj; } -#define MBYTE 1048576 int32_t init_faustcompile(CSOUND *csound, faustcompile *p) { - uintptr_t thread; + faustcompile_state *st = + (faustcompile_state *)csound->Malloc(csound, sizeof(faustcompile_state)); + st->csound = csound; + st->factory = NULL; + st->thread = NULL; + st->node = NULL; + + /* Snapshot everything the asynchronous thread needs, so that it never + touches the per-note opcode memory (which is freed when this instrument + ends). The thread publishes its result into st->node, never into the + output pointer. */ hdata *data = (hdata *)csound->Malloc(csound, sizeof(hdata)); data->csound = csound; - data->p = p; - *p->hptr = -1.0; - p->lock = csound->Create_Mutex(0); - - /* New API function allows thread stack to be defined - new in Csound 6.17 */ - thread = (uintptr_t) - csound->CreateThread2((uintptr_t (*)(void *))init_faustcompile_thread, *p->stacksize * MBYTE, data); - - p->thread = thread; + data->code = csound->Strdup(csound, p->code->data); + data->args = csound->Strdup(csound, p->args ? p->args->data : ""); + data->target = NULL; + if (p->INOCOUNT > 4 && p->target != NULL && p->target->data != NULL) + data->target = csound->Strdup(csound, p->target->data); + data->state = st; + + /* Reserve the registry slot and publish the handle while the opcode is + still alive, so that no write to per-note memory happens after the + instrument ends. */ + { + std::lock_guard guard(faust_registry_mutex); + st->node = reserve_factory_slot(csound); + } + *p->hptr = FL(st->node->cnt); + if(!(int)*p->async) { - csound->JoinThread((void *) thread); - return OK; + /* synchronous compilation, run on the calling thread (large stack) */ + init_faustcompile_thread(data); + /* Preserve the historical output convention for synchronous failure. */ + if (st->node->obj == FAUST_SLOT_FAILED) + *p->hptr = -2.0; + } else { + st->thread = csound->CreateThread(init_faustcompile_thread_runner, data); } - else csound->RegisterResetCallback(csound, p, delete_faustcompile); + /* Factory cleanup is deferred to reset, so that instruments using the + compiled factory are not affected when the compiling instrument ends. + The callback owns the persistent state, not the opcode. */ + csound->RegisterResetCallback(csound, st, delete_faustcompile); return OK; } struct faustdsp { OPDS h; - MYFLT *ohptr; - MYFLT *code; /* faust compiled code handle */ + cs_float *ohptr; + cs_float *code; /* faust compiled code handle */ llvm_dsp *engine; /* faust DSP */ llvm_dsp_factory *factory; /* DSP factory */ }; @@ -377,24 +567,27 @@ int32_t delete_faustdsp(CSOUND *csound, void *p) { faustdsp *pp = (faustdsp *)p; faustobj *fobj, *prv, **pfobj; pfobj = (faustobj **)csound->QueryGlobalVariable(csound, "::dsp"); - fobj = *pfobj; - prv = fobj; - while (fobj != NULL) { - if (fobj->obj == (void *)pp->engine) { - prv->nxt = fobj->nxt; - break; - } + if (pfobj != NULL) { + fobj = *pfobj; prv = fobj; - fobj = fobj->nxt; + while (fobj != NULL) { + if (fobj->obj == (void *)pp->engine) { + prv->nxt = fobj->nxt; + break; + } + prv = fobj; + fobj = fobj->nxt; + } + if (fobj != NULL) { + if (*pfobj == fobj) + *pfobj = fobj->nxt; + delete fobj->ctls; + csound->Free(csound, fobj); + delete pp->engine; + } else + csound->Warning(csound, Str("could not find DSP %p for deletion"), + pp->engine); } - if (fobj != NULL) { - if (*pfobj == fobj) - *pfobj = fobj->nxt; - csound->Free(csound, fobj); - delete pp->engine; - } else - csound->Warning(csound, Str("could not find DSP %p for deletion"), - pp->engine); if (pp->factory) deleteDSPFactory(pp->factory); @@ -403,38 +596,15 @@ int32_t delete_faustdsp(CSOUND *csound, void *p) { int32_t init_faustdsp(CSOUND *csound, faustdsp *p) { int32_t factory; - faustobj *fobj, **fobjp, **pfdsp, *fdsp; + faustobj *fobj, **pfdsp, *fdsp; llvm_dsp *dsp; controls *ctls = new controls(); const char *varname = "::dsp"; - int timout = 0; - while (*((MYFLT *)p->code) == -1.0) { - csound->Sleep(1); - timout++; - if(timout > 1000) { - return csound->InitError( - csound, "%s", Str("Faust code was not ready. Try compiling it \n" - "in a separate instrument prior to running it here\n")); - } - } - - factory = (int32_t)*((MYFLT *)p->code); - if (factory == -2) - return csound->InitError( - csound, "%s", Str("Faust code did not compile properly.\n" - "Check above messages for Faust compiler errors\n")); - - fobjp = (faustobj **)csound->QueryGlobalVariable(csound, "::factory"); - if (fobjp == NULL) - return csound->InitError(csound, "%s", Str("no factory available\n")); - fobj = *fobjp; - while ((int32_t)fobj->cnt != factory) { - fobj = fobj->nxt; - if (fobj == NULL) - return csound->InitError(csound, Str("factory not found %d\n"), - (int32_t)factory); - } + factory = (int32_t)*((cs_float *)p->code); + fobj = resolve_factory(csound, factory); + if (fobj == NULL) + return CSOUND_ERROR; dsp = ((llvm_dsp_factory *)fobj->obj)->createDSPInstance(); if (dsp == NULL) @@ -474,38 +644,35 @@ int32_t init_faustdsp(CSOUND *csound, faustdsp *p) { p->factory = NULL; // this opcode does not own the factory p->engine = (llvm_dsp *)fdsp->obj; - p->engine->init(csound->GetSr(csound)); - csound->RegisterDeinitCallback(csound, p, delete_faustdsp); - *p->ohptr = (MYFLT)fdsp->cnt; + p->engine->init((int)csound->GetEngineSr(csound)); + *p->ohptr = (cs_float)fdsp->cnt; return OK; } struct faustplay { OPDS h; - MYFLT *outs[MAXARG]; /* outputs */ - MYFLT *inst; - MYFLT *ins[VARGMAX]; /* inputs */ + cs_float *outs[MAXARG]; /* outputs */ + cs_float *inst; + cs_float *ins[VARGMAX]; /* inputs */ llvm_dsp *engine; /* faust DSP */ llvm_dsp_factory *factory; /* DSP factory */ AUXCH memin; AUXCH memout; -#ifndef USE_DOUBLE - AUXCH buffin; - AUXCH buffout; -#endif }; int32_t init_faustplay(CSOUND *csound, faustplay *p) { faustobj *fobj, **fobjp; - OPARMS parms; + const OPARMS *parms = csound->GetOParms(csound); int32_t instance = (int32_t)*p->inst; fobjp = (faustobj **)csound->QueryGlobalVariable(csound, "::dsp"); if (fobjp == NULL) return csound->InitError(csound, "%s", Str("no dsp instances available\n")); fobj = *fobjp; + if (fobj == NULL) + return csound->InitError(csound, "%s", Str("no dsp instances available\n")); while ((int32_t)fobj->cnt != instance) { fobj = fobj->nxt; @@ -515,40 +682,31 @@ int32_t init_faustplay(CSOUND *csound, faustplay *p) { } p->engine = (llvm_dsp *)fobj->obj; - p->engine->init(csound->GetSr(csound)); - if (p->engine->getNumInputs() != p->INCOUNT - 1) { - delete p->engine; + /* The engine is owned and initialised by the opcode that created it + (faustdsp, faustaudio or faustgen), so it is neither re-initialised nor + deleted here; doing so would reset its state and invalidate the + registry. */ + if (p->engine->getNumInputs() != p->INOCOUNT - 1) { + p->engine = NULL; return csound->InitError(csound, "%s", Str("wrong number of input args\n")); } - if (p->engine->getNumOutputs() != p->OUTCOUNT) { - delete p->engine; + if (p->engine->getNumOutputs() != p->OUTOCOUNT) { + p->engine = NULL; return csound->InitError(csound, "%s", Str("wrong number of output args\n")); } /* memory for sampAccurate offsets */ - csound->GetOParms(csound, &parms); - if (parms.sampleAccurate) { + if (parms->sampleAccurate) { size_t size; - size = p->engine->getNumInputs() * sizeof(MYFLT *); + size = p->engine->getNumInputs() * sizeof(cs_float *); if (p->memin.auxp == NULL || p->memin.size < size) csound->AuxAlloc(csound, size, &p->memin); - size = p->engine->getNumOutputs() * sizeof(MYFLT *); + size = p->engine->getNumOutputs() * sizeof(cs_float *); if (p->memout.auxp == NULL || p->memout.size < size) csound->AuxAlloc(csound, size, &p->memout); } -#ifndef USE_DOUBLE - { - size_t size; - size = CS_KSMPS * p->engine->getNumInputs() * sizeof(double); - if (p->buffin.auxp == NULL || p->buffin.size < size) - csound->AuxAlloc(csound, size, &p->buffin); - size = CS_KSMPS * p->engine->getNumOutputs() * sizeof(double); - if (p->buffout.auxp == NULL || p->buffout.size < size) - csound->AuxAlloc(csound, size, &p->buffout); - } -#endif return OK; } @@ -556,57 +714,35 @@ int32_t perf_faustplay(CSOUND *csound, faustplay *p) { int32_t nsmps = CS_KSMPS, i; uint32_t offset = p->h.insdshead->ksmps_offset; uint32_t early = p->h.insdshead->ksmps_no_end; - MYFLT **in_tmp = (MYFLT **)p->memin.auxp; - MYFLT **out_tmp = (MYFLT **)p->memout.auxp; - AVOIDDENORMALS; + cs_float **in_tmp = (cs_float **)p->memin.auxp; + cs_float **out_tmp = (cs_float **)p->memout.auxp; if (UNLIKELY(early)) { - for (i = 0; i < p->OUTCOUNT - 1; i++) - memset(p->outs[i], '\0', nsmps * sizeof(MYFLT)); + for (i = 0; i < p->OUTOCOUNT; i++) + memset(p->outs[i], '\0', nsmps * sizeof(cs_float)); nsmps -= early; } if (UNLIKELY(offset)) { /* offset pointers, save current pos */ - for (i = 0; i < p->OUTCOUNT; i++) { - memset(p->outs[i], '\0', nsmps * sizeof(MYFLT)); + for (i = 0; i < p->OUTOCOUNT; i++) { + memset(p->outs[i], '\0', nsmps * sizeof(cs_float)); out_tmp[i] = p->outs[i]; p->outs[i] = &(p->outs[i][offset]); } - for (i = 0; i < p->INCOUNT - 1; i++) { + for (i = 0; i < p->INOCOUNT - 1; i++) { in_tmp[i] = p->ins[i]; p->ins[i] = &(p->ins[i][offset]); } nsmps -= offset; } -#ifdef USE_DOUBLE p->engine->compute(nsmps, p->ins, p->outs); -#else - { - int n; - double *buffin, **buffinp = (double **) p->buffin.auxp; - double *buffout, **buffoutp = (double **) p->buffout.auxp; - for (i = 0; i < p->INCOUNT - 1; i++) { - buffin = ((double *)p->buffin.auxp) + CS_KSMPS*i; - if(UNLIKELY(offset)) - memset(buffin, '\0', offset * sizeof(MYFLT)); - for(n = offset; n < nsmps; n++) - buffin[n] = p->ins[i][n]; - } - p->engine->compute(nsmps,buffinp, buffoutp); - for (i = 0; i < p->OUTCOUNT - 1; i++) { - buffout = ((double *)p->buffout.auxp) + CS_KSMPS*i; - for(n = offset; n < nsmps; n++) - p->ins[i][n] = buffout[n]; - } - } -#endif if (UNLIKELY(offset)) { /* restore pos */ - for (i = 0; i < p->OUTCOUNT; i++) + for (i = 0; i < p->OUTOCOUNT; i++) p->outs[i] = out_tmp[i]; - for (i = 0; i < p->INCOUNT - 1; i++) + for (i = 0; i < p->INOCOUNT - 1; i++) p->ins[i] = in_tmp[i]; } return OK; @@ -630,19 +766,15 @@ int32_t perf_faustplay(CSOUND *csound, faustplay *p) { **/ struct faustgen { OPDS h; - MYFLT *ohptr; - MYFLT *outs[MAXARG]; /* outputs */ + cs_float *ohptr; + cs_float *outs[MAXARG]; /* outputs */ STRINGDAT *code; /* faust code as string */ - MYFLT *ins[VARGMAX]; /* inputs */ + cs_float *ins[VARGMAX]; /* inputs */ llvm_dsp *engine; /* faust DSP */ llvm_dsp_factory *factory; /* DSP factory */ controls *ctls; AUXCH memin; AUXCH memout; -#ifndef USE_DOUBLE - AUXCH buffin; - AUXCH buffout; -#endif }; struct hdata2 { @@ -675,9 +807,7 @@ int32_t delete_faustgen(CSOUND *csound, void *p) { csound->Free(csound, fobj); delete pp->ctls; delete pp->engine; - } /*else - csound->Warning(csound, Str("could not find DSP %p for deletion"), - pp->engine);*/ + } } if (pp->factory) deleteDSPFactory(pp->factory); @@ -688,45 +818,34 @@ int32_t delete_faustgen(CSOUND *csound, void *p) { int32_t init_faustaudio(CSOUND *csound, faustgen *p) { int32_t factory; - OPARMS parms; - faustobj *fobj, **fobjp, **pfdsp, *fdsp; + const OPARMS *parms = csound->GetOParms(csound); + faustobj *fobj, **pfdsp, *fdsp; llvm_dsp *dsp; controls *ctls = new controls(); const char *varname = "::dsp"; - int timout = 0; - while (*((MYFLT *)p->code) == -1.0) { - csound->Sleep(1); - timout++; - if(timout > 1000) { - return csound->InitError( - csound, "%s", Str("Faust code was not ready. Try compiling it \n" - "in a separate instrument prior to running it here\n")); - } - } - - - factory = (int32_t)*((MYFLT *)p->code); - if (factory == -2) - return csound->InitError( - csound, "%s", Str("Faust code did not compile properly.\n" - "Check above messages for Faust compiler errors\n")); - - fobjp = (faustobj **)csound->QueryGlobalVariable(csound, "::factory"); - if (fobjp == NULL) - return csound->InitError(csound, "%s", Str("no factory available\n")); - fobj = *fobjp; - while ((int32_t)fobj->cnt != factory) { - fobj = fobj->nxt; - if (fobj == NULL) - return csound->InitError(csound, Str("factory not found %d\n"), - (int32_t)factory); - } + factory = (int32_t)*((cs_float *)p->code); + fobj = resolve_factory(csound, factory); + if (fobj == NULL) + return CSOUND_ERROR; dsp = ((llvm_dsp_factory *)fobj->obj)->createDSPInstance(); if (dsp == NULL) return csound->InitError(csound, "%s", Str("Faust instantiation problem\n")); + /* Validate the channel counts before publishing the instance, so that an + invalid DSP is never left reachable through the ::dsp registry. */ + if (dsp->getNumInputs() != p->INOCOUNT - 1) { + delete ctls; + delete dsp; + return csound->InitError(csound, "%s", Str("wrong number of input args\n")); + } + if (dsp->getNumOutputs() != p->OUTOCOUNT - 1) { + delete ctls; + delete dsp; + return csound->InitError(csound, "%s", Str("wrong number of output args\n")); + } + dsp->buildUserInterface(ctls); pfdsp = (faustobj **)csound->QueryGlobalVariable(csound, varname); if (pfdsp == NULL) { @@ -761,53 +880,28 @@ int32_t init_faustaudio(CSOUND *csound, faustgen *p) { p->factory = NULL; // this opcode does not own the factory p->engine = (llvm_dsp *)fdsp->obj; - p->engine->init(csound->GetSr(csound)); - - if (p->engine->getNumInputs() != p->INCOUNT - 1) { - delete p->engine; - p->engine = NULL; - return csound->InitError(csound, "%s", Str("wrong number of input args\n")); - } - if (p->engine->getNumOutputs() != p->OUTCOUNT - 1) { - delete p->engine; - p->engine = NULL; - return csound->InitError(csound, "%s", Str("wrong number of output args\n")); - } + p->engine->init((int)csound->GetEngineSr(csound)); /* memory for sampAccurate offsets */ - csound->GetOParms(csound, &parms); - if (parms.sampleAccurate) { + if (parms->sampleAccurate) { size_t size; - size = p->engine->getNumInputs() * sizeof(MYFLT *); + size = p->engine->getNumInputs() * sizeof(cs_float *); if (p->memin.auxp == NULL || p->memin.size < size) csound->AuxAlloc(csound, size, &p->memin); - size = p->engine->getNumOutputs() * sizeof(MYFLT *); + size = p->engine->getNumOutputs() * sizeof(cs_float *); if (p->memout.auxp == NULL || p->memout.size < size) csound->AuxAlloc(csound, size, &p->memout); } -#ifndef USE_DOUBLE - { - size_t size; - size = CS_KSMPS * p->engine->getNumInputs() * sizeof(double); - if (p->buffin.auxp == NULL || p->buffin.size < size) - csound->AuxAlloc(csound, size, &p->buffin); - size = CS_KSMPS * p->engine->getNumOutputs() * sizeof(double); - if (p->buffout.auxp == NULL || p->buffout.size < size) - csound->AuxAlloc(csound, size, &p->buffout); - } -#endif - p->ctls = ctls; - csound->RegisterDeinitCallback(csound, p, delete_faustgen); - *p->ohptr = (MYFLT)fdsp->cnt; + *p->ohptr = (cs_float)fdsp->cnt; return OK; } void *init_faustgen_thread(void *pp) { CSOUND *csound = ((hdata2 *)pp)->csound; faustgen *p = ((hdata2 *)pp)->p; - OPARMS parms; + const OPARMS *parms = csound->GetOParms(csound); std::string err_msg; #ifdef USE_DOUBLE int32_t argc = 4; @@ -821,25 +915,52 @@ void *init_faustgen_thread(void *pp) { controls *ctls = new controls(); const char *varname = "::dsp"; p->engine = NULL; - p->factory = createDSPFactoryFromString( - "faustop", (const char *)p->code->data, argc, argv, "", err_msg, 3); + { + std::lock_guard guard(faust_compile_mutex); + p->factory = createDSPFactoryFromString( + "faustop", (const char *)p->code->data, argc, argv, "", err_msg, 3); + } if (p->factory == NULL) { - int32_t ret = csound->InitError(csound, Str("Faust compilation problem: %s\n"), - err_msg.c_str()); + csound->InitError(csound, Str("Faust compilation problem: %s\n"), + err_msg.c_str()); csound->Free(csound, pp); return NULL; } dsp = p->factory->createDSPInstance(); if (dsp == NULL) { - int32_t ret = csound->InitError(csound, "%s", - Str("Faust instantiation problem\n")); + csound->InitError(csound, "%s", + Str("Faust instantiation problem\n")); csound->Free(csound, pp); return NULL; } dsp->buildUserInterface(ctls); + /* Validate the channel counts before publishing the instance, so that an + invalid DSP is never left reachable through the ::dsp registry. */ + if (dsp->getNumInputs() != p->INOCOUNT - 1) { + csound->InitError(csound, "%s", Str("wrong number of input args\n")); + delete ctls; + delete dsp; + deleteDSPFactory(p->factory); + p->factory = NULL; + csound->Free(csound, pp); + return NULL; + } + if (dsp->getNumOutputs() != p->OUTOCOUNT - 1) { + csound->InitError(csound, + Str("wrong number of output args: need %d had %d\n"), + dsp->getNumOutputs(), + p->OUTOCOUNT - 1); + delete ctls; + delete dsp; + deleteDSPFactory(p->factory); + p->factory = NULL; + csound->Free(csound, pp); + return NULL; + } + pfdsp = (faustobj **)csound->QueryGlobalVariable(csound, varname); if (pfdsp == NULL || *pfdsp == NULL) { csound->CreateGlobalVariable(csound, varname, sizeof(faustobj *)); @@ -856,69 +977,38 @@ void *init_faustgen_thread(void *pp) { fdsp = fdsp->nxt; } fdsp->nxt = (faustobj *)csound->Calloc(csound, sizeof(faustobj)); - fdsp->nxt->cnt = fdsp->cnt++; + fdsp->nxt->cnt = fdsp->cnt + 1; fdsp = fdsp->nxt; fdsp->obj = dsp; fdsp->ctls = ctls; } p->engine = dsp; - dsp->buildUserInterface(ctls); - dsp->init(csound->GetSr(csound)); - if (p->engine->getNumInputs() != p->INCOUNT - 1) { - int32_t ret; - ret = csound->InitError(csound, "%s", Str("wrong number of input args\n")); - delete p->engine; - deleteDSPFactory(p->factory); - p->factory = NULL; - p->engine = NULL; - csound->Free(csound, pp); - return NULL; - } - if (p->engine->getNumOutputs() != p->OUTCOUNT - 1) { - int32_t ret; - ret = csound->InitError(csound, - Str("wrong number of output args: need %d had %d\n"), - p->engine->getNumOutputs(), - p->OUTCOUNT - 1 - ); - delete p->engine; - deleteDSPFactory(p->factory); - csound->Free(csound, pp); - p->engine = NULL; - p->factory = NULL; - return NULL; - } + dsp->init((int)csound->GetEngineSr(csound)); /* memory for sampAccurate offsets */ - csound->GetOParms(csound, &parms); - if (parms.sampleAccurate) { + if (parms->sampleAccurate) { size_t size; - size = p->engine->getNumInputs() * sizeof(MYFLT *); + size = p->engine->getNumInputs() * sizeof(cs_float *); if (p->memin.auxp == NULL || p->memin.size < size) csound->AuxAlloc(csound, size, &p->memin); - size = p->engine->getNumOutputs() * sizeof(MYFLT *); + size = p->engine->getNumOutputs() * sizeof(cs_float *); if (p->memout.auxp == NULL || p->memout.size < size) csound->AuxAlloc(csound, size, &p->memout); } p->ctls = ctls; - *p->ohptr = (MYFLT)fdsp->cnt; + *p->ohptr = (cs_float)fdsp->cnt; csound->Free(csound, pp); return NULL; } int32_t init_faustgen(CSOUND *csound, faustgen *p) { - uintptr_t thread; - int32_t *ret; hdata2 *data = (hdata2 *)csound->Malloc(csound, sizeof(hdata2)); data->csound = csound; data->p = p; - /* New API function allows for threads to be given a stack */ - thread = (uintptr_t) - csound->CreateThread2((uintptr_t (*)(void *))init_faustgen_thread, MBYTE, data); - csound->RegisterDeinitCallback(csound, p, delete_faustgen); - csound->JoinThread((void *)thread); - csound->RegisterDeinitCallback(csound, p, delete_faustgen); + /* Compile synchronously on the calling thread, which has a large stack; + the original code also joined the worker thread immediately. */ + init_faustgen_thread(data); return OK; } @@ -926,57 +1016,35 @@ int32_t perf_faust(CSOUND *csound, faustgen *p) { int32_t nsmps = CS_KSMPS, i; uint32_t offset = p->h.insdshead->ksmps_offset; uint32_t early = p->h.insdshead->ksmps_no_end; - MYFLT **in_tmp = (MYFLT **)p->memin.auxp; - MYFLT **out_tmp = (MYFLT **)p->memout.auxp; - AVOIDDENORMALS; + cs_float **in_tmp = (cs_float **)p->memin.auxp; + cs_float **out_tmp = (cs_float **)p->memout.auxp; if (UNLIKELY(early)) { - for (i = 0; i < p->OUTCOUNT - 1; i++) - memset(p->outs[i], '\0', nsmps * sizeof(MYFLT)); + for (i = 0; i < p->OUTOCOUNT - 1; i++) + memset(p->outs[i], '\0', nsmps * sizeof(cs_float)); nsmps -= early; } if (UNLIKELY(offset)) { /* offset pointers, save current pos */ - for (i = 0; i < p->OUTCOUNT - 1; i++) { - memset(p->outs[i], '\0', nsmps * sizeof(MYFLT)); + for (i = 0; i < p->OUTOCOUNT - 1; i++) { + memset(p->outs[i], '\0', nsmps * sizeof(cs_float)); out_tmp[i] = p->outs[i]; p->outs[i] = &(p->outs[i][offset]); } - for (i = 0; i < p->INCOUNT - 1; i++) { + for (i = 0; i < p->INOCOUNT - 1; i++) { in_tmp[i] = p->ins[i]; p->ins[i] = &(p->ins[i][offset]); } nsmps -= offset; } -#ifdef USE_DOUBLE p->engine->compute(nsmps, p->ins, p->outs); -#else - { - int n; - double *buffin, **buffinp = (double **) p->buffin.auxp; - double *buffout, **buffoutp = (double **) p->buffout.auxp; - for (i = 0; i < p->INCOUNT - 1; i++) { - buffin = ((double *)p->buffin.auxp) + CS_KSMPS*i; - if(UNLIKELY(offset)) - memset(buffin, '\0', offset * sizeof(MYFLT)); - for(n = offset; n < nsmps; n++) - buffin[n] = p->ins[i][n]; - } - p->engine->compute(nsmps,buffinp, buffoutp); - for (i = 0; i < p->OUTCOUNT - 1; i++) { - buffout = ((double *)p->buffout.auxp) + CS_KSMPS*i; - for(n = offset; n < nsmps; n++) - p->ins[i][n] = buffout[n]; - } - } -#endif if (UNLIKELY(offset)) { - /* restore pos -- coud be memcpy?*/ - for (i = 0; i < p->OUTCOUNT - 1; i++) + /* restore pos -- could be memcpy? */ + for (i = 0; i < p->OUTOCOUNT - 1; i++) p->outs[i] = out_tmp[i]; - for (i = 0; i < p->INCOUNT - 1; i++) + for (i = 0; i < p->INOCOUNT - 1; i++) p->ins[i] = in_tmp[i]; } return OK; @@ -997,13 +1065,13 @@ int32_t perf_faust(CSOUND *csound, faustgen *p) { **/ struct faustctl { OPDS h; - MYFLT *inst; + cs_float *inst; STRINGDAT *label; - MYFLT *val; - MYFLT *extraparam[MAXPARAM]; + cs_float *val; + cs_float *extraparam[MAXPARAM]; FAUSTFLOAT *zone; - MYFLT min, max; - MYFLT minextra[MAXPARAM/2], maxextra[MAXPARAM/2]; + cs_float min, max; + cs_float minextra[MAXPARAM/2], maxextra[MAXPARAM/2]; FAUSTFLOAT *zonextra[MAXPARAM/2]; }; @@ -1013,13 +1081,15 @@ int32_t init_faustctl(CSOUND *csound, faustctl *p) { int32_t instance = (int32_t)*p->inst; /* checks that extra parameter count is even */ - if((p->INCOUNT - 3)%2) + if((p->INOCOUNT - 3)%2) return csound->InitError(csound, "unbalanced parameter count \n"); fobjp = (faustobj **)csound->QueryGlobalVariable(csound, "::dsp"); if (fobjp == NULL) return csound->InitError(csound, "%s", Str("no dsp instances available\n")); fobj = *fobjp; + if (fobj == NULL) + return csound->InitError(csound, "%s", Str("no dsp instances available\n")); while ((int32_t)fobj->cnt != instance) { fobj = fobj->nxt; @@ -1035,7 +1105,7 @@ int32_t init_faustctl(CSOUND *csound, faustctl *p) { p->max = fobj->ctls->getMax(p->label->data); p->min = fobj->ctls->getMin(p->label->data); { - MYFLT val = *p->val; + cs_float val = *p->val; if (p->min != p->max) val = val < p->min ? p->min : (val > p->max ? p->max : val); *p->zone = val; @@ -1043,7 +1113,7 @@ int32_t init_faustctl(CSOUND *csound, faustctl *p) { /* implementation of extra optional parameters */ - for(int n = 0; n < p->INCOUNT - 3; n+=2) { + for(int n = 0; n < p->INOCOUNT - 3; n+=2) { char *name = ((STRINGDAT *)p->extraparam[n])->data; p->zonextra[n/2] = fobj->ctls->getZone(name); if (p->zonextra[n/2] == NULL) @@ -1052,9 +1122,9 @@ int32_t init_faustctl(CSOUND *csound, faustctl *p) { p->maxextra[n/2] = fobj->ctls->getMax(name); p->minextra[n/2] = fobj->ctls->getMin(name); { - MYFLT val = *(p->extraparam[n+1]); - MYFLT min = p->minextra[n/2]; - MYFLT max = p->maxextra[n/2]; + cs_float val = *(p->extraparam[n+1]); + cs_float min = p->minextra[n/2]; + cs_float max = p->maxextra[n/2]; if (min != max) val = val < min ? min : (val > max ? max : val); *(p->zonextra[n/2]) = val; @@ -1065,16 +1135,16 @@ int32_t init_faustctl(CSOUND *csound, faustctl *p) { } int32_t perf_faustctl(CSOUND *csound, faustctl *p) { - MYFLT val = *p->val; + cs_float val = *p->val; if (p->min != p->max) val = val < p->min ? p->min : (val > p->max ? p->max : val); *p->zone = val; /* implementation of extra optional parameters */ - for(int n = 0; n < p->INCOUNT - 3; n+=2) { - MYFLT val = *(p->extraparam[n+1]); - MYFLT min = p->minextra[n/2]; - MYFLT max = p->maxextra[n/2]; + for(int n = 0; n < p->INOCOUNT - 3; n+=2) { + cs_float val = *(p->extraparam[n+1]); + cs_float min = p->minextra[n/2]; + cs_float max = p->maxextra[n/2]; if (min != max) val = val < min ? min : (val > max ? max : val); *(p->zonextra[n/2]) = val; @@ -1085,31 +1155,54 @@ int32_t perf_faustctl(CSOUND *csound, faustctl *p) { #define S(x) sizeof(x) static OENTRY localops[] = { - {(char *)"faustgen", S(faustgen), 0, 3, + {(char *)"faustgen", S(faustgen), 0, (char *)"immmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm", (char *)"SM", - (SUBR)init_faustgen, (SUBR)perf_faust}, - {(char *)"faustcompile", S(faustcompile), 0, 1, (char *)"i", (char *)"SSppo", - (SUBR)init_faustcompile, NULL, NULL}, - {(char *)"faustaudio", S(faustgen), 0, 3, + (SUBR)init_faustgen, (SUBR)perf_faust, (SUBR)delete_faustgen, NULL, 0}, + {(char *)"faustcompile", S(faustcompile), 0, (char *)"i", (char *)"SSpp", + (SUBR)init_faustcompile, NULL, NULL, NULL, 0}, + {(char *)"faustcompile", S(faustcompile), 0, (char *)"i", (char *)"SSppS", + (SUBR)init_faustcompile, NULL, NULL, NULL, 0}, + {(char *)"faustaudio", S(faustgen), 0, (char *)"immmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm", (char *)"iM", - (SUBR)init_faustaudio, (SUBR)perf_faust}, - {(char *)"faustdsp", S(faustdsp), 0, 1, + (SUBR)init_faustaudio, (SUBR)perf_faust, (SUBR)delete_faustgen, NULL, 0}, + {(char *)"faustdsp", S(faustdsp), 0, (char *)"i", (char *)"i", - (SUBR)init_faustdsp}, - {(char *)"faustplay", S(faustplay), 0, 3, + (SUBR)init_faustdsp, NULL, (SUBR)delete_faustdsp, NULL, 0}, + {(char *)"faustplay", S(faustplay), 0, (char *)"mmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmmm", (char *)"iM", - (SUBR)init_faustplay, (SUBR)perf_faustplay}, - {(char *)"faustctl.i", S(faustgen), 0, 1, (char *)"", (char *)"iSiN", - (SUBR)init_faustctl}, - {(char *)"faustctl.k ", S(faustgen), 0, 3, (char *)"", (char *)"iSkN", - (SUBR)init_faustctl, (SUBR)perf_faustctl}}; - -PUBLIC int64_t csound_opcode_init(CSOUND *csound, OENTRY **ep) { - IGN(csound); - *ep = localops; - return (int64_t)sizeof(localops); + (SUBR)init_faustplay, (SUBR)perf_faustplay, NULL, NULL, 0}, + {(char *)"faustctl.i", S(faustgen), 0, (char *)"", (char *)"iSiN", + (SUBR)init_faustctl, NULL, NULL, NULL, 0}, + {(char *)"faustctl.k ", S(faustgen), 0, (char *)"", (char *)"iSkN", + (SUBR)init_faustctl, (SUBR)perf_faustctl, NULL, NULL, 0}, + {NULL, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, 0}}; + +PUBLIC int32_t csoundModuleCreate(CSOUND *csound) { + (void)csound; + return 0; +} + +PUBLIC int32_t csoundModuleInit(CSOUND *csound) { + int32_t err = 0; + OENTRY *ep = (OENTRY *)&(localops[0]); + setup_faust_path(csound); + while (ep->opname != NULL) { + err |= csound->AppendOpcode(csound, + ep->opname, ep->dsblksiz, ep->flags, + ep->outypes, ep->intypes, + (int32_t (*)(CSOUND *, void *))ep->init, + (int32_t (*)(CSOUND *, void *))ep->perf, + (int32_t (*)(CSOUND *, void *))ep->deinit); + ep++; + } + return err; +} + +PUBLIC int32_t csoundModuleDestroy(CSOUND *csound) { + (void)csound; + return 0; } PUBLIC int32_t csoundModuleInfo(void) { - return ((CS_APIVERSION << 16) + (CS_APISUBVER << 8) + (int32_t)sizeof(MYFLT)); + return CSOUND_MODULE_INFO; } diff --git a/src/fluidOpcodes/CMakeLists.txt b/src/fluidOpcodes/CMakeLists.txt new file mode 100644 index 00000000..bb9b49e4 --- /dev/null +++ b/src/fluidOpcodes/CMakeLists.txt @@ -0,0 +1,22 @@ +# Build fluidsynth opcodes +option(BUILD_FLUID_OPCODES "Build the fluidsynth opcodes" ON) + +# With vcpkg prefer the CMake config package, which carries the required +# transitive dependencies (e.g. macOS CoreAudio/CoreMIDI frameworks). System +# packages may ship an incomplete config, so fall back to FindFLUIDSYNTH. +if(USE_VCPKG) + find_package(FluidSynth CONFIG REQUIRED) + set(FLUIDSYNTH_FOUND TRUE) + set(FLUIDSYNTH_LINK_LIBRARIES FluidSynth::libfluidsynth) +else() + find_package(FLUIDSYNTH) + set(FLUIDSYNTH_LINK_LIBRARIES ${FLUIDSYNTH_LIBRARIES}) +endif() +check_deps(BUILD_FLUID_OPCODES FLUIDSYNTH_FOUND) + +if(BUILD_FLUID_OPCODES AND FLUIDSYNTH_FOUND) + make_plugin(fluidOpcodes fluidOpcodes.cpp) + target_include_directories(fluidOpcodes PRIVATE ${FLUIDSYNTH_INCLUDE_DIR} ${CSOUND_INCLUDE_DIRS}) + target_compile_definitions(fluidOpcodes PRIVATE BUILD_PLUGINS) + target_link_libraries(fluidOpcodes PRIVATE ${FLUIDSYNTH_LINK_LIBRARIES}) +endif() diff --git a/src/fluidOpcodes/OpcodeBase.hpp b/src/fluidOpcodes/OpcodeBase.hpp new file mode 100644 index 00000000..49a724fc --- /dev/null +++ b/src/fluidOpcodes/OpcodeBase.hpp @@ -0,0 +1,499 @@ +/* + OpcodeBase.hpp: + + Copyright (C) 2005, 2009, 2017 by Istva Varga, Victor Lazzarini and + Michael Gogins + + This file is part of Csound. + + The Csound Library is free software; you can redistribute it + and/or modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + Csound is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with Csound; if not, write to the Free Software + Foundation, Inc., 31 Milk Street, #960789, Boston, MA, 02196, USA +*/ +#pragma once + +/** + * There is a conflict between the preprocessor definition "_CR" in the + * standard C++ library and in Csound. To work around this, undefine "_CR" and + * include ALL standard library include files BEFORE including ANY Csound + * include files. + */ +#if defined(_CR) +#undef _CR +#endif + +#include +#include +#include +#if !(defined(__wasi__)) +#include +#endif +#include +#include +#include "interlocks.h" +#ifdef BUILD_PLUGINS +#include "csdl.h" +#else +#include "csoundCore.h" +#endif + +/** + * Template base class, or pseudo-virtual base class, + * for writing Csound opcodes in C++. + * Derive opcode implementation classes like this: + * + * DerivedClass : public OpcodeBase + * { + * public: + * // All output fields must be declared first as cs_float *: + * cs_float *aret1; + * // All input fields must be declared next as cs_float *: + * cs_float *iarg1; + * cs_float *karg2; + * cs_float *aarg3; + * // All internal state variables must be declared after that: + * size_t state1; + * double state2; + * cs_float state3; + * // If the opcode shares data protect it by creating one or more void + * // *mutex member pointers: + * void *mutex1; + * void *mutex2; + * // and create them usind csound->Create_Mutex() in csoundModuleCreate + * // and destroy them using csound->DeleteMutex(mutex) + * // csoundModuleDestroy, and lock them using LockGuard guard(mutex). + * int32_t init(); + * int32_t kontrol(); + * int32_t audio; + * int32_t noteoff(); + * void deinit(); + * }; + */ + +namespace csound { + + static bool diagnostics_enabled = false; + + /** + * Use this to guard against data races in opcode functions. The mutex should + * be created by csound->Create_Mutex() in csoundModuleCreate(), and should + * be destroyed by csound->DeleteMutex() in csoundModuleDestroy(). + * + * If data is shared between opcode instances, the mutex should be global to + * the opcode library; if data is shared between threads for a single instance, + * the mutex should belong to the opcode instance. + */ + struct LockGuard { + LockGuard(CSOUND *csound_, void *mutex_) : csound(csound_), mutex(mutex_) + { + csound->LockMutex(mutex); + } + ~LockGuard() + { + csound->UnlockMutex(mutex); + } + CSOUND *csound; + void *mutex; + }; + + /** + * Use this to store a pointer to a global heap-allocated object, e.g. one + * used to manage state between opcode instances. + */ + + template int32_t CreateGlobalPointer(CSOUND *csound, const char *name, T *pointer) + { + T **pointer_to_pointer = 0; + int32_t result = csound->CreateGlobalVariable(csound, name, sizeof(pointer_to_pointer)); + pointer_to_pointer = static_cast(csound->QueryGlobalVariable(csound, name)); + *pointer_to_pointer = pointer; + return result; + } + + /** + * Retrieve a pointer to a global heap-allocated object, e.g. one + * used to manage state between opcode instances. + */ + template T *QueryGlobalPointer(CSOUND *csound, const char *name, T*& pointer) + { + T **pointer_to_pointer = static_cast(csound->QueryGlobalVariableNoCheck(csound, name)); + if (pointer_to_pointer != 0) { + pointer = *pointer_to_pointer; + } else { + pointer = 0; + } + return pointer; + } + + template + class OpcodeBase + { + public: + int32_t init(CSOUND *csound) + { + return NOTOK; + } + static int32_t init_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->init(csound); + } + int32_t kontrol(CSOUND *csound) + { + return NOTOK; + } + static int32_t kontrol_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->kontrol(csound); + } + int32_t audio(CSOUND *csound) + { + return NOTOK; + } + static int32_t audio_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->audio(csound); + } + /** + This is how to compute audio signals for normal opcodes: + (1) Zero all frames from 0 up to but not including Offset. + (2) Compute all frames from ksmps_offset up to but not including End. + (3) Zero all frames from End up to but not including ksmps. + Example from a C opcode: + uint32_t offset = p->h.insdshead->ksmps_offset; + uint32_t early = p->h.insdshead->ksmps_no_end; + uint32_t n, nsmps = CS_KSMPS; + if (UNLIKELY(offset)) memset(p->r, '\0', offset*sizeof(cs_float)); + if (UNLIKELY(early)) { + nsmps -= early; + memset(&p->r[nsmps], '\0', early*sizeof(cs_float)); + } + for (n = offset; n < nsmps; n++) { + input1 = CS_FLOAT2LRND(p->a[n]); + p->r[n] = (cs_float) (input1 >> input2); + } + So in C++ it should look like this (which is much easier to understand): + int32_t frameIndex = 0; + for( ; frameIndex < kperiodOffset(); ++frameIndex) { + asignal[frameIndex] = 0; + } + for( ; frameIndex < kperiodEnd(); ++frameIndex) { + asignal[frameIndex] = compute(); + } + for( ; frameIndex < ksmps(); ++frameIndex) { + asignal[frameIndex] = 0; + } + */ + uint32_t kperiodOffset() const + { + return opds.insdshead->ksmps_offset; + } + uint32_t kperiodEnd() const + { + uint32_t end = opds.insdshead->ksmps_no_end; + if (end) { + return end; + } else { + return ksmps(); + } + } + uint32_t ksmps() const + { + return opds.insdshead->ksmps; + } + uint32_t output_arg_count() + { + return (uint32_t)opds.optext->t.outArgCount; + } + uint32_t input_arg_count() + { + return (uint32_t)opds.optext->t.inArgCount; + } + void log(CSOUND *csound, const char *format,...) + { + va_list args; + va_start(args, format); + if(csound) { + csound->MessageV(csound, 0, format, args); + } else { + vfprintf(stdout, format, args); + } + va_end(args); + } + void warn(CSOUND *csound, const char *format,...) + { + if(csound) { + if(csound->GetMessageLevel(csound) & CS_WARNMSG) { + va_list args; + va_start(args, format); + csound->MessageV(csound, CSOUNDMSG_WARNING, format, args); + va_end(args); + } + } else { + va_list args; + va_start(args, format); + vfprintf(stdout, format, args); + va_end(args); + } + } + OPDS opds; + }; + + template + class OpcodeNoteoffBase + { + public: + int32_t init(CSOUND *csound) + { + IGN(csound); + return NOTOK; + } + + static int32_t deinit_(CSOUND *csound, void *opcode) + { + if (!csound->GetReinitFlag(csound) && !csound->GetTieFlag(csound)) { + return noteoff_(csound, opcode); + } + return OK; + } + + static int32_t init_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->init(csound); + } + + int32_t kontrol(CSOUND *csound) + { + IGN(csound); + return NOTOK; + } + static int32_t kontrol_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->kontrol(csound); + } + int32_t audio(CSOUND *csound) + { + IGN(csound); + return NOTOK; + } + static int32_t audio_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->audio(csound); + } + /** + This is how to compute audio signals for normal opcodes: + (1) Zero all frames from 0 up to but not including Offset. + (2) Compute all frames from ksmps_offset up to but not including End. + (3) Zero all frames from End up to but not including ksmps. + Example from a C opcode: + uint32_t offset = p->h.insdshead->ksmps_offset; + uint32_t early = p->h.insdshead->ksmps_no_end; + uint32_t n, nsmps = CS_KSMPS; + if (UNLIKELY(offset)) memset(p->r, '\0', offset*sizeof(cs_float)); + if (UNLIKELY(early)) { + nsmps -= early; + memset(&p->r[nsmps], '\0', early*sizeof(cs_float)); + } + for (n = offset; n < nsmps; n++) { + input1 = CS_FLOAT2LRND(p->a[n]); + p->r[n] = (cs_float) (input1 >> input2); + } + So in C++ it should look like this (which is much easier to understand): + int32_t frameIndex = 0; + for( ; frameIndex < kperiodOffset(); ++frameIndex) { + asignal[frameIndex] = 0; + } + for( ; frameIndex < kperiodEnd(); ++frameIndex) { + asignal[frameIndex] = compute(); + } + for( ; frameIndex < ksmps(); ++frameIndex) { + asignal[frameIndex] = 0; + } + */ + uint32_t kperiodOffset() const + { + return opds.insdshead->ksmps_offset; + } + uint32_t kperiodEnd() const + { + uint32_t end = opds.insdshead->ksmps_no_end; + if (end) { + return end; + } else { + return ksmps(); + } + } + uint32_t ksmps() const + { + return opds.insdshead->ksmps; + } + uint32_t output_arg_count() + { + return (uint32_t)opds.optext->t.outArgCount; + } + uint32_t input_arg_count() + { + return (uint32_t)opds.optext->t.inArgCount; + } + void log(CSOUND *csound, const char *format,...) + { + va_list args; + va_start(args, format); + if(csound) { + csound->MessageV(csound, 0, format, args); + } else { + vfprintf(stdout, format, args); + } + va_end(args); + } + void warn(CSOUND *csound, const char *format,...) + { + if(csound) { + if(csound->GetMessageLevel(csound) & CS_WARNMSG) { + va_list args; + va_start(args, format); + csound->MessageV(csound, CSOUNDMSG_WARNING, format, args); + va_end(args); + } + } else { + va_list args; + va_start(args, format); + vfprintf(stdout, format, args); + va_end(args); + } + } + int32_t noteoff(CSOUND *csound) + { + return OK; + } + static int32_t noteoff_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->noteoff(csound); + } + OPDS opds; + }; + + +#if (__cplusplus >= 201103L) && !(defined(__wasi__)) && !defined(BARE_METAL) + // #pragma message("Defining heap_object_manager_t.") + /** + * The memory of non-POD C++ or C objects allocated on the heap by Csound + * plugins is managed not by Csound, but by the plugin module. This class + * performs that function. + * + * As long as all memory is allocated in or after csoundModuleCreate and + * de-allocated in csoundModuleDestroy, all will be well. The danger however + * is that memory leaks will occur if allocations are not matched with + * de-allocations, or worse, that crashes will occur if any objects are + * deleted twice or de-referenced after deletion. + * + * So, the plugin should manage a collection of all heap-allocated objects, + * keyed first off the Csound instance, and second off an object handle; and + * all such objects must be accessed only by handle, from this collection. + * + * In csoundModuleDestroy, all objects for the calling instance of Csound + * should be de-allocated, and the sub-collection for that instance should + * also be de-allocated. + * + * If then there are no more sub-collections for other instances of Csound, + * the entire contents of the collection should be de-allocated. + * + * This should be done as a singleton, and in a thread-safe way. + * + * There might still be a problem if any of the heap-allocated objects, + * themselves, have global static members, perhaps created by some external + * dependency. + * + * Please note, objects O in the declaration below must not be std::shared_ptr + * objects. Deletion of objects herein must be unconditional. Rather, the + * objects passed are raw pointers to instances of class O. + */ + template class heap_object_manager_t { + private: + std::map> objects_; + std::recursive_mutex mutex; + heap_object_manager_t(){}; + ~heap_object_manager_t(){}; + public: + static heap_object_manager_t &instance() { + static heap_object_manager_t singleton; + return singleton; + } + std::map> &objects() { + std::lock_guard lock(mutex); + return objects_; + } + /** + * Returns a list of pointers to all objects allocated for this + * instance of Csound. + */ + std::vector &objects_for_csound(CSOUND *csound) { + std::lock_guard lock(mutex); + auto &objects_for_csound_ = objects()[csound]; + return objects_for_csound_; + } + /** + * Returns the handle for the object; if the object has not yet been + * stored, inserts it into the list of object pointers for this + * instance of Csound; otherwise, returns the handle of the stored + * object pointer. + */ + int32_t handle_for_object(CSOUND *csound, O *object) { + std::lock_guard lock(mutex); + auto &objects_for_csound_ = objects_for_csound(csound); + auto iterator = std::find(objects_for_csound_.begin(), objects_for_csound_.end(), object); + if (iterator == objects_for_csound_.end()) { + int32_t handle = objects_for_csound_.size(); + objects_for_csound_.push_back(object); + if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::handle_for_object %p: new object handle: %d (of %ld)\n", object, handle, objects_for_csound_.size()); + return handle; + } else { + int32_t handle = static_cast(iterator - objects_for_csound_.begin()); + if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::handle_for_object: existing object handle: %d\n", handle); + return handle; + } + } + /** + * Returns the object pointer for the handle; + * if the object pointer has not been stored by + * handle, returns a null pointer. + */ + O *object_for_handle(CSOUND *csound, int32_t handle) { + std::lock_guard lock(mutex); + auto &objects_for_csound_ = objects_for_csound(csound); + if (handle >= objects_for_csound_.size()) { + return nullptr; + } + O *object = objects_for_csound_[handle]; + if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::object_for_handle: %p %d (of %ld)\n", object, handle, objects_for_csound_.size()); + return object; + } + /** + * First destroys all objects created by the calling + * instance of Csound, then destroys the list of + * object pointers for this instance of Csound. + */ + void module_destroy(CSOUND *csound) { + std::lock_guard lock(mutex); + auto &objects_for_csound_ = objects_for_csound(csound); + for (int32_t i = 0, n = objects_for_csound_.size(); i < n; ++i) { + delete objects_for_csound_[i]; + objects_for_csound_[i] = nullptr; + } + objects_for_csound_.clear(); + objects().erase(csound); + } + }; + +#endif + +} diff --git a/src/fluidOpcodes/fluidOpcodes.cpp b/src/fluidOpcodes/fluidOpcodes.cpp new file mode 100644 index 00000000..4cf568b4 --- /dev/null +++ b/src/fluidOpcodes/fluidOpcodes.cpp @@ -0,0 +1,1154 @@ +/** + * FLUID SYNTH OPCODES + * + * Adapts Fluidsynth to use global engines, soundFonts, and outputs + * + * Based on work by Michael Gogins and Steven Yi. License is identical to + * SOUNDFONTS VST License (listed below) + * + * Copyright (c) 2003 by Steven Yi. All rights reserved. + * + * [ORIGINAL INFORMATION BELOW] + * + * S O U N D F O N T S V S T + * + * Adapts Fluidsynth to be both a VST plugin instrument + * and a Csound plugin opcode. + * Copyright (c) 2001-2003 by Michael Gogins. All rights reserved. + * + * L I C E N S E + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include +#include +#include +#include +#include +#include +#include +#include "OpcodeBase.hpp" +#include "csdl.h" +#include +#include + +// Per-instance state shared by the fluid* opcodes. Stored in a +// Csound-global pointer so recompiling a CSD through the API can reuse the +// engines and SoundFonts already loaded instead of growing memory each +// csoundCompileCsd/csoundCompileOrc call (github issue #5). +struct FluidSynthEngineState { + double sampleRate; + int32_t chorusEnabled; + int32_t reverbEnabled; + int32_t channelCount; + int32_t voiceCount; + // Ordinal of the fluidEngine opcode that owns this engine within the + // compiled global (instr 0) opcode chain. Reuse is keyed on this ordinal + // and the configuration, so the same opcode reuses its engine across + // compiles while two deliberately separate engines stay distinct. The + // ordinal comes from the compile's opcode chain, never from a heap address, + // which Csound may recycle between compiles. + int32_t ordinal; + std::map soundfonts; // resolved file path -> sf id +}; + +struct FluidSynthState { + std::vector synths; // engine instances + std::vector engines; // parallel to synths +}; + +static void tof(CSOUND *csound, fluid_synth_t *f, cs_float *a) { + FluidSynthState *state = 0; + if (csound::QueryGlobalPointer(csound, "fluid_synth_state", state) && + state) { + state->synths.push_back(f); + *a = (cs_float)(state->synths.size() - 1); + } else { + *a = FL(0.0); + } +}; + +static void toa(CSOUND *csound, cs_float *f, fluid_synth_t *&a) { + a = 0; + FluidSynthState *state = 0; + if (csound::QueryGlobalPointer(csound, "fluid_synth_state", state) && + state) { + int index = (int)*f; + if (index >= 0 && index < (int)state->synths.size()) { + a = state->synths[index]; + } + } +}; + +// Position of a fluidEngine opcode among the fluidEngine opcodes of the +// current compile, found by walking the global (instr 0) init chain. The +// ordinal is stable across recompiles of the same orchestra and does not +// depend on any heap address, so a given opcode always maps to the same +// engine. Csound may recycle the INSDS that holds the chain between compiles, +// which makes its address unusable as an identity. +static int32_t FluidEngineOrdinal(OPDS *op) { + OPDS *head = (OPDS *)op->insdshead; + if (!head || !op->optext) { + return 0; + } + OENTRY *entry = op->optext->t.oentry; + int32_t ordinal = 0; + for (OPDS *p = head->nxti; p != 0 && p != op; p = p->nxti) { + if (p->optext && p->optext->t.oentry == entry) { + ordinal++; + } + } + return ordinal; +}; + +static void SetStringData(CSOUND *csound, STRINGDAT *sdata, const char *str) { + if (!str) { + str = ""; + } + size_t len = strlen(str) + 1; + if (sdata->data == NULL || sdata->size < len) { + if (sdata->data != NULL && sdata->refcount == 0) { + csound->Free(csound, sdata->data); + } + sdata->data = (char *)csound->Calloc(csound, len); + sdata->size = len; + } + memcpy(sdata->data, str, len); + sdata->refcount = 0; +}; + +// fluid_settings_dupstr() allocates a copy the caller owns. Release it with +// fluid_free(), which only exists since fluidsynth 2.0.7; earlier 2.x releases +// must use free() (same allocator). +static void FreeFluidString(char *str) { +#if FLUIDSYNTH_VERSION_MAJOR > 2 || \ + (FLUIDSYNTH_VERSION_MAJOR == 2 && \ + (FLUIDSYNTH_VERSION_MINOR > 0 || FLUIDSYNTH_VERSION_MICRO >= 7)) + fluid_free(str); +#else + free(str); +#endif +}; + +class FluidEngine : public csound::OpcodeBase { + // Outputs. + cs_float *iFluidSynth; + // Inputs. + cs_float *iChorusEnabled; + cs_float *iReverbEnabled; + cs_float *iChannelCount; + cs_float *iVoiceCount; + // State. + // fluid_synth_t *fluidSynth; + // fluid_settings_t *fluidSettings; + int32_t chorusEnabled; + int32_t reverbEnabled; + int32_t channelCount; + int32_t voiceCount; + void *mutex; + +public: + int32_t init(CSOUND *csound) { + mutex = csound->Create_Mutex(0); + csound::LockGuard guard(csound, mutex); + int32_t result = OK; + fluid_synth_t *fluidSynth = 0; + fluid_settings_t *fluidSettings = 0; + chorusEnabled = (int32_t)*iChorusEnabled; + reverbEnabled = (int32_t)*iReverbEnabled; + channelCount = (int32_t)*iChannelCount; + voiceCount = (int32_t)*iVoiceCount; + if (channelCount <= 0) { + channelCount = 256; + } else if (channelCount < 16) { + channelCount = 16; + } else if (channelCount > 256) { + channelCount = 256; + } + if (voiceCount <= 0) { + voiceCount = 4096; + } else if (voiceCount < 16) { + voiceCount = 16; + } else if (voiceCount > 4096) { + voiceCount = 4096; + } + double sampleRate = (double)csound->GetEngineSr(csound); + int32_t ordinal = FluidEngineOrdinal(&opds); + void *fluid_synths_mutex = 0; + csound::QueryGlobalPointer(csound, "fluid_synths_mutex", + fluid_synths_mutex); + csound::LockGuard synthsGuard(csound, fluid_synths_mutex); + FluidSynthState *state = 0; + csound::QueryGlobalPointer(csound, "fluid_synth_state", state); + if (state) { + for (size_t i = 0, n = state->engines.size(); i < n; i++) { + FluidSynthEngineState &engine = state->engines[i]; + if (engine.ordinal == ordinal && + engine.sampleRate == sampleRate && + engine.chorusEnabled == chorusEnabled && + engine.reverbEnabled == reverbEnabled && + engine.channelCount == channelCount && + engine.voiceCount == voiceCount) { + fluidSynth = state->synths[i]; + *iFluidSynth = (cs_float)i; + log(csound, "Reusing existing fluidEngine %ld (0x%p): " + "sampling rate = %f, chorus %s, reverb %s, " + "channels %d, voices %d.\n", + (long)i, fluidSynth, sampleRate, + chorusEnabled ? "on" : "off", reverbEnabled ? "on" : "off", + channelCount, voiceCount); + return OK; + } + } + } + fluidSettings = new_fluid_settings(); + if (fluidSettings != NULL) { + fluid_settings_setnum(fluidSettings, (char *)"synth.sample-rate", + sampleRate); + fluid_settings_setint(fluidSettings, (char *)"synth.midi-channels", + channelCount); + fluid_settings_setint(fluidSettings, (char *)"synth.polyphony", + voiceCount); + fluidSynth = new_fluid_synth(fluidSettings); + } + if (!fluidSynth) { + if (fluidSettings) { + delete_fluid_settings(fluidSettings); + } + result = + csound->InitError(csound, "%s", Str("error allocating fluid engine\n")); + } else { +#if (FLUIDSYNTH_VERSION_MAJOR >= 2 && FLUIDSYNTH_VERSION_MINOR >= 2) + // TODO: Change -1 to a configurable FX group? + fluid_synth_chorus_on(fluidSynth, -1, chorusEnabled); + fluid_synth_reverb_on(fluidSynth, -1, reverbEnabled); +#else + fluid_synth_set_chorus_on(fluidSynth, chorusEnabled); + fluid_synth_set_reverb_on(fluidSynth, reverbEnabled); +#endif + log(csound, "Created fluidEngine 0x%p with sampling rate = %f, " + "chorus %s, reverb %s, channels %d, voices %d.\n", + fluidSynth, sampleRate, + chorusEnabled ? "on" : "off", reverbEnabled ? "on" : "off", + channelCount, voiceCount); + if (state) { + FluidSynthEngineState engine; + engine.sampleRate = sampleRate; + engine.chorusEnabled = chorusEnabled; + engine.reverbEnabled = reverbEnabled; + engine.channelCount = channelCount; + engine.voiceCount = voiceCount; + engine.ordinal = ordinal; + state->engines.push_back(engine); + } + tof(csound, fluidSynth, iFluidSynth); + } + return result; + } +}; + +class FluidLoad : public csound::OpcodeBase { + // Outputs. + cs_float *iInstrumentNumber; + // Inputs. + cs_float *iFilename; + cs_float *iFluidSynth; + cs_float *iListPresets; + // State. + char *filename; + char *filepath; + fluid_synth_t *fluidSynth; + int32_t soundFontId; + int32_t listPresets; + void *mutex; + +public: + int32_t init(CSOUND *csound) { + mutex = csound->Create_Mutex(0); + csound::LockGuard guard(csound, mutex); + int32_t result = OK; + soundFontId = -1; + toa(csound, iFluidSynth, fluidSynth); + listPresets = (int32_t)*iListPresets; + const CS_TYPE *argType = GetTypeForArg(iFilename); + if (strcmp("S", argType->varTypeName) == 0) { + filename = csound->Strdup(csound, ((STRINGDAT *)iFilename)->data); + } else { + char legacy_name[MAXNAME]; + csound->StringArg2Name( + csound, legacy_name, + (IsStringCode(*iFilename) ? csound->GetArgString(csound, *iFilename) + : (char *)iFilename), + (char *)"fluid.sf2.", IsStringCode(*iFilename)); + filename = legacy_name; + } + + filepath = csound->FindInputFile(csound, filename, "SFDIR;SSDIR"); + if (filepath && fluid_is_soundfont(filepath)) { + void *fluid_synths_mutex = 0; + csound::QueryGlobalPointer(csound, "fluid_synths_mutex", + fluid_synths_mutex); + csound::LockGuard synthsGuard(csound, fluid_synths_mutex); + FluidSynthState *state = 0; + csound::QueryGlobalPointer(csound, "fluid_synth_state", state); + int32_t engineIndex = (int32_t)*iFluidSynth; + std::map *soundfonts = 0; + if (state && engineIndex >= 0 && + engineIndex < (int32_t)state->engines.size()) { + soundfonts = &state->engines[engineIndex].soundfonts; + } + soundFontId = -1; + if (soundfonts) { + std::map::iterator it = + soundfonts->find(filepath); + if (it != soundfonts->end()) { + soundFontId = it->second; + log(csound, "SoundFont %s already loaded as id %d, not reloaded.\n", + filepath, soundFontId); + } + } + if (soundFontId < 0) { + log(csound, "Loading SoundFont: %s.\n", filepath); + soundFontId = fluid_synth_sfload(fluidSynth, filepath, 0); + log(csound, "fluidSynth: 0x%p soundFontId: %d.\n", fluidSynth, + soundFontId); + if (soundFontId >= 0 && soundfonts) { + (*soundfonts)[filepath] = soundFontId; + } + } + } + *iInstrumentNumber = (cs_float)soundFontId; + if (UNLIKELY(soundFontId < 0)) { + return csound->InitError(csound, Str("fluid: unable to load %s"), + filename); + } + csound->NotifyFileOpened(csound, filepath, CSFTYPE_SOUNDFONT, 0, 0); + if (soundFontId < 0) { + result = NOTOK; + } else if (listPresets) { + fluid_sfont_t *fluidSoundfont = + fluid_synth_get_sfont_by_id(fluidSynth, soundFontId); +#if FLUIDSYNTH_VERSION_MAJOR < 2 + fluid_preset_t fluidPreset; + fluidSoundfont->iteration_start(fluidSoundfont); + const OPARMS *oparms = csound->GetOParms(csound); + if (oparms->msglevel & 0x7) + while (fluidSoundfont->iteration_next(fluidSoundfont, &fluidPreset)) + { + log(csound, "SoundFont: %3d Bank: %3d Preset: %3d %s\n", + soundFontId, fluidPreset.get_banknum(&fluidPreset), + fluidPreset.get_num(&fluidPreset), + fluidPreset.get_name(&fluidPreset)); +#else + fluid_preset_t *fluidPreset; + fluid_sfont_iteration_start(fluidSoundfont); + const OPARMS *oparms = csound->GetOParms(csound); + if (oparms->msglevel & 0x7) + while ((fluidPreset = fluid_sfont_iteration_next(fluidSoundfont))) + { + log(csound, "SoundFont: %3d Bank: %3d Preset: %3d %s\n", + soundFontId, fluid_preset_get_banknum(fluidPreset), + fluid_preset_get_num(fluidPreset), + fluid_preset_get_name(fluidPreset)); +#endif + } + } + return result; + } +}; + +#include "arrays.h" + +//Rory Walsh 2018 +class FluidInfo : public csound::OpcodeBase { + // Outputs. + ARRAYDAT *outArr; + // Inputs. + cs_float *iFluidSynth; + // State. + fluid_synth_t *fluidSynth; + void *mutex; + +public: + int32_t init(CSOUND *csound) { + std::vector programs; + char *program; + mutex = csound->Create_Mutex(0); + csound::LockGuard guard(csound, mutex); + int32_t result = OK; + toa(csound, iFluidSynth, fluidSynth); + fluid_sfont_t *fluidSoundfont = fluid_synth_get_sfont(fluidSynth, 0); +#if FLUIDSYNTH_VERSION_MAJOR < 2 + fluid_preset_t fluidPreset; + fluidSoundfont->iteration_start(fluidSoundfont); + const OPARMS *oparms = csound->GetOParms(csound); + if (oparms->msglevel & 0x7) + while (fluidSoundfont->iteration_next(fluidSoundfont, &fluidPreset)) + { + std::stringstream ss; + ss << "Bank: " << fluidPreset.get_banknum(&fluidPreset) << + " Preset: " << fluidPreset.get_num(&fluidPreset) << + " Name: " << fluidPreset.get_name(&fluidPreset); +#else + fluid_preset_t *fluidPreset; + fluid_sfont_iteration_start(fluidSoundfont); + const OPARMS *oparms = csound->GetOParms(csound); + if (oparms->msglevel & 0x7) + while ((fluidPreset = fluid_sfont_iteration_next(fluidSoundfont))) + { + std::stringstream ss; + ss << "Bank: " << fluid_preset_get_banknum(fluidPreset) << + " Preset: " << fluid_preset_get_num(fluidPreset) << + " Name: " << fluid_preset_get_name(fluidPreset); +#endif + programs.push_back(ss.str()); + } + + tabinit(csound, outArr, (int32_t)programs.size(), opds.insdshead); + + for (unsigned int i = 0; i < programs.size(); i++) { + program = &programs[i][0u]; + STRINGDAT *element = csound_string_array_element(outArr, i); + element->size = strlen(program) + 1; + element->data = csound->Strdup(csound, program); + element->refcount = 0; + } + + programs.clear(); + + return result; + } +}; + +class FluidProgramSelect : public csound::OpcodeBase +{ + // Inputs. + cs_float *iFluidSynth; + cs_float *iChannelNumber; + cs_float *iInstrumentNumber; + cs_float *iBankNumber; + cs_float *iPresetNumber; + // State. + fluid_synth_t *fluidSynth; + int32_t channel; + uint32_t instrument; + uint32_t bank; + uint32_t preset; + void *mutex; +public: + int32_t init(CSOUND *csound) + { + mutex = csound->Create_Mutex(0); + csound::LockGuard guard(csound, mutex); + toa(csound, iFluidSynth, fluidSynth); + channel = (int32_t) *iChannelNumber; + instrument = (uint32_t) *iInstrumentNumber; + bank = (uint32_t) *iBankNumber; + preset = (uint32_t) *iPresetNumber; + fluid_synth_program_select(fluidSynth, + channel, + instrument, + bank, + preset); + return OK; + } +}; + +class FluidCCI : public csound::OpcodeBase { + // Inputs. + cs_float *iFluidSynth; + cs_float *iChannelNumber; + cs_float *iControllerNumber; + cs_float *kVal; + // State. + fluid_synth_t *fluidSynth; + int32_t channel; + int32_t controller; + int32_t value; + void *mutex; + +public: + int32_t init(CSOUND *csound) { + mutex = csound->Create_Mutex(0); + csound::LockGuard guard(csound, mutex); + toa(csound, iFluidSynth, fluidSynth); + channel = (int32_t)*iChannelNumber; + controller = (int32_t)*iControllerNumber; + value = (int32_t)*kVal; + fluid_synth_cc(fluidSynth, channel, controller, value); + return OK; + } +}; + +class FluidCCK : public csound::OpcodeBase { + // Inputs. + cs_float *iFluidSynth; + cs_float *iChannelNumber; + cs_float *iControllerNumber; + cs_float *kVal; + // State. + fluid_synth_t *fluidSynth; + int32_t channel; + int32_t controller; + int32_t value; + int32_t priorValue; + void *mutex; + +public: + int32_t init(CSOUND *csound) { + mutex = csound->Create_Mutex(0); + csound::LockGuard guard(csound, mutex); + toa(csound, iFluidSynth, fluidSynth); + priorValue = -1; + return OK; + } + int32_t kontrol(CSOUND *csound) { + csound::LockGuard guard(csound, mutex); + value = (int32_t)*kVal; + if (value != priorValue) { + priorValue = value; + channel = (int32_t)*iChannelNumber; + controller = (int32_t)*iControllerNumber; + fluid_synth_cc(fluidSynth, channel, controller, value); + } + return OK; + } +}; + +class FluidNote : public csound::OpcodeNoteoffBase { + // Inputs. + cs_float *iFluidSynth; + cs_float *iChannelNumber; + cs_float *iMidiKeyNumber; + cs_float *iVelocity; + // State. + fluid_synth_t *fluidSynth; + int32_t channel; + int32_t key; + int32_t velocity; + void *mutex; + +public: + int32_t init(CSOUND *csound) { + mutex = csound->Create_Mutex(0); + csound::LockGuard guard(csound, mutex); + toa(csound, iFluidSynth, fluidSynth); + channel = (int32_t)*iChannelNumber; + key = (int32_t)*iMidiKeyNumber; + velocity = (int32_t)*iVelocity; + fluid_synth_noteon(fluidSynth, channel, key, velocity); + return OK; + } + int32_t noteoff(CSOUND *csound) { + csound::LockGuard guard(csound, mutex); + fluid_synth_noteoff(fluidSynth, channel, key); + return OK; + } +}; + +class FluidOut : public csound::OpcodeBase { + // Outputs. + cs_float *aLeftOut; + cs_float *aRightOut; + // Inputs. + cs_float *iFluidSynth; + // State. + fluid_synth_t *fluidSynth; + float leftSample; + float rightSample; + int32_t frame; + int32_t ksmps; + void *mutex; + +public: + int32_t init(CSOUND *csound) { + mutex = csound->Create_Mutex(0); + csound::LockGuard guard(csound, mutex); + toa(csound, iFluidSynth, fluidSynth); + ksmps = opds.insdshead->ksmps; + return OK; + } + int32_t audio(CSOUND *csound) { + csound::LockGuard guard(csound, mutex); + uint32_t offset = opds.insdshead->ksmps_offset; + uint32_t early = opds.insdshead->ksmps_no_end; + if (UNLIKELY(offset)) { + memset(aLeftOut, '\0', offset * sizeof(cs_float)); + memset(aRightOut, '\0', offset * sizeof(cs_float)); + } + if (UNLIKELY(early)) { + ksmps -= early; + memset(&aLeftOut[ksmps], '\0', early * sizeof(cs_float)); + memset(&aRightOut[ksmps], '\0', early * sizeof(cs_float)); + } + for (frame = offset; frame < ksmps; frame++) { + leftSample = 0.0f; + rightSample = 0.0f; + fluid_synth_write_float(fluidSynth, 1, &leftSample, 0, 1, &rightSample, 0, + 1); + aLeftOut[frame] = leftSample /* * csound->e0dbfs */; + aRightOut[frame] = rightSample /* * csound->e0dbfs */; + } + return OK; + } +}; + +class FluidAllOut : public csound::OpcodeBase { + // Outputs. + cs_float *aLeftOut; + cs_float *aRightOut; + // State. + float leftSample; + float rightSample; + int32_t frame; + int32_t ksmps; + void *mutex; + +public: + int32_t init(CSOUND *csound) { + mutex = csound->Create_Mutex(0); + csound::LockGuard guard(csound, mutex); + ksmps = opds.insdshead->ksmps; + return OK; + } + int32_t audio(CSOUND *csound) { + csound::LockGuard guard(csound, mutex); + uint32_t offset = opds.insdshead->ksmps_offset; + uint32_t early = opds.insdshead->ksmps_no_end; + if (UNLIKELY(offset)) { + memset(aLeftOut, '\0', offset * sizeof(cs_float)); + memset(aRightOut, '\0', offset * sizeof(cs_float)); + } + if (UNLIKELY(early)) { + ksmps -= early; + memset(&aLeftOut[ksmps], '\0', early * sizeof(cs_float)); + memset(&aRightOut[ksmps], '\0', early * sizeof(cs_float)); + } + FluidSynthState *state = 0; + csound::QueryGlobalPointer(csound, "fluid_synth_state", state); + void *fluid_synths_mutex = 0; + csound::QueryGlobalPointer(csound, "fluid_synths_mutex", + fluid_synths_mutex); + csound::LockGuard synthsGuard(csound, fluid_synths_mutex); + for (frame = offset; frame < ksmps; frame++) { + aLeftOut[frame] = FL(0.0); + aRightOut[frame] = FL(0.0); + if (state && frame >= 0) { + for (size_t i = 0, n = state->synths.size(); i < n; i++) { + fluid_synth_t *fluidSynth = state->synths[i]; + leftSample = FL(0.0); + rightSample = FL(0.0); + fluid_synth_write_float(fluidSynth, 1, &leftSample, 0, 1, &rightSample, + 0, 1); + aLeftOut[frame] += (cs_float)leftSample /* * csound->e0dbfs */; + aRightOut[frame] += (cs_float)rightSample /* * csound->e0dbfs */; + } + } + } + return OK; + } +}; + +class FluidControl : public csound::OpcodeBase { + // Inputs. + cs_float *iFluidSynth; + cs_float *kMidiStatus; + cs_float *kMidiChannel; + cs_float *kMidiData1; + cs_float *kMidiData2; + cs_float *imsgs; + // State. + fluid_synth_t *fluidSynth; + int32_t midiStatus; + int32_t midiChannel; + int32_t midiData1; + int32_t midiData2; + int32_t priorMidiStatus; + int32_t priorMidiChannel; + int32_t priorMidiData1; + int32_t priorMidiData2; + int32_t printMsgs; + void *mutex; + +public: + int32_t init(CSOUND *csound) { + mutex = csound->Create_Mutex(0); + csound::LockGuard guard(csound, mutex); + toa(csound, iFluidSynth, fluidSynth); + priorMidiStatus = -1; + priorMidiChannel = -1; + priorMidiData1 = -1; + priorMidiData2 = -1; + printMsgs = (*imsgs == 0 ? 0 : 1); + return OK; + } + int32_t kontrol(CSOUND *csound) { + csound::LockGuard guard(csound, mutex); + midiStatus = 0xF0 & (int32_t)*kMidiStatus; + midiChannel = (int32_t)*kMidiChannel; + midiData1 = (int32_t)*kMidiData1; + midiData2 = (int32_t)*kMidiData2; + int32_t result = -1; + + if (midiData2 != priorMidiData2 || midiData1 != priorMidiData1 || + midiChannel != priorMidiChannel || midiStatus != priorMidiStatus) { + switch (midiStatus) { + case (int32_t)0x80: + noteOff: + result = fluid_synth_noteoff(fluidSynth, midiChannel, midiData1); + if (printMsgs) + csound->Message(csound, Str("result: %d\n Note off: c:%3d k:%3d\n"), + result, midiChannel, midiData1); + break; + case (int32_t)0x90: + if (!midiData2) { + goto noteOff; + } + result = + fluid_synth_noteon(fluidSynth, midiChannel, midiData1, midiData2); + if (printMsgs) + log(csound, "result: %d\nNote on: c:%3d k:%3d v:%3d\n", result, + midiChannel, midiData1, midiData2); + break; + case (int32_t)0xA0: + if (printMsgs) + log(csound, "Key pressure (not handled): " + "c:%3d k:%3d v:%3d\n", + midiChannel, midiData1, midiData2); + break; + case (int32_t)0xB0: + result = fluid_synth_cc(fluidSynth, midiChannel, midiData1, midiData2); + if (printMsgs) + log(csound, "Result: %d Control change: c:%3d c:%3d v:%3d\n", result, + midiChannel, midiData1, midiData2); + break; + case (int32_t)0xC0: + result = fluid_synth_program_change(fluidSynth, midiChannel, midiData1); + if (printMsgs) + log(csound, "Result: %d Program change: c:%3d p:%3d\n", result, + midiChannel, midiData1); + break; + case (int32_t)0xD0: + if (printMsgs) + log(csound, "After touch (not handled): c:%3d v:%3d\n", midiChannel, + midiData1); + break; + case (int32_t)0xE0: { + int32_t pbVal = midiData1 + (midiData2 << 7); + fluid_synth_pitch_bend(fluidSynth, midiChannel, pbVal); + if (printMsgs) + log(csound, "Result: %d, Pitch bend: c:%d b:%d\n", result, + midiChannel, pbVal); + } break; + case (int32_t)0xF0: + if (printMsgs) + log(csound, "System exclusive (not handled): " + "c:%3d v1:%3d v2:%3d\n", + midiChannel, midiData1, midiData2); + break; + } + priorMidiStatus = midiStatus; + priorMidiChannel = midiChannel; + priorMidiData1 = midiData1; + priorMidiData2 = midiData2; + } + return OK; + } +}; + +class FluidSetInterpMethod : public csound::OpcodeBase { + // Inputs. + cs_float *iFluidSynth; + cs_float *iChannelNumber; + cs_float *iInterpMethod; + // State. + fluid_synth_t *fluidSynth; + int32_t channel; + int32_t interpolationMethod; + void *mutex; + +public: + int32_t init(CSOUND *csound) { + mutex = csound->Create_Mutex(0); + csound::LockGuard guard(csound, mutex); + toa(csound, iFluidSynth, fluidSynth); + channel = (int32_t)*iChannelNumber; + interpolationMethod = (int32_t)*iInterpMethod; + if (UNLIKELY(interpolationMethod != 0 && interpolationMethod != 1 && + interpolationMethod != 4 && interpolationMethod != 7)) { + return csound->InitError(csound, "%s", + Str("Illegal Interpolation Method: Must be " + "either 0, 1, 4, or 7.\n")); + } else { + fluid_synth_set_interp_method(fluidSynth, channel, interpolationMethod); + } + return OK; + } +}; + +// Access to a fluid engine's settings (github issue #32). Each opcode +// resolves the engine id handed out by fluidEngine to its fluid_settings +// (obtained via fluid_synth_get_settings) and either sets a setting on the +// fly or reads its current value, dispatching on the setting's own type. +// Note: set/get return 1/0 on fluidsynth 1.x and FLUID_OK/FLUID_FAILED on +// 2.x, so failure is checked version-conditionally. +static int32_t SettingSetFailed(int result) { +#if FLUIDSYNTH_VERSION_MAJOR >= 2 + return result < int(FLUID_OK); +#else + return result == 0; +#endif +} + +class FluidSettingNum : public csound::OpcodeBase { + // Inputs. + cs_float *iFluidSynth; + cs_float *iSetting; + cs_float *kValue; + // State. + fluid_synth_t *fluidSynth; + fluid_settings_t *fluidSettings; + char *settingName; + cs_float priorValue; + int32_t initial; + void *mutex; + +public: + int32_t setValue(CSOUND *csound) { + if (UNLIKELY(!fluidSettings)) { + csound->Message(csound, Str("fluidSettingNum: invalid engine id.\n")); + return NOTOK; + } + int32_t type = + fluid_settings_get_type(fluidSettings, (char *)settingName); + if (type == FLUID_NUM_TYPE) { + if (SettingSetFailed(fluid_settings_setnum( + fluidSettings, (char *)settingName, (double)*kValue))) { + csound->Message(csound, + Str("fluidSettingNum: could not set '%s' to %g.\n"), + settingName, *kValue); + return NOTOK; + } + } else if (type == FLUID_INT_TYPE) { + if (SettingSetFailed(fluid_settings_setint( + fluidSettings, (char *)settingName, (int)*kValue))) { + csound->Message( + csound, Str("fluidSettingNum: could not set '%s' to %d.\n"), + settingName, (int)*kValue); + return NOTOK; + } + } else { + csound->Message( + csound, Str("fluidSettingNum: '%s' is not a numeric setting " + "(type %d).\n"), + settingName, type); + return NOTOK; + } + return OK; + } + + int32_t init(CSOUND *csound) { + mutex = csound->Create_Mutex(0); + csound::LockGuard guard(csound, mutex); + toa(csound, iFluidSynth, fluidSynth); + fluidSettings = + fluidSynth ? fluid_synth_get_settings(fluidSynth) : (fluid_settings_t *)0; + if (iSetting) { + settingName = ((STRINGDAT *)iSetting)->data; + } + initial = 1; + priorValue = FL(0.0); + int32_t result = setValue(csound); + if (UNLIKELY(result != OK)) { + return csound->InitError(csound, "%s", + Str("fluidSettingNum: cannot set setting.\n")); + } + priorValue = *kValue; + return OK; + } + + int32_t kontrol(CSOUND *csound) { + csound::LockGuard guard(csound, mutex); + if (initial || *kValue != priorValue) { + initial = 0; + priorValue = *kValue; + setValue(csound); + } + return OK; + } +}; + +class FluidSettingString : public csound::OpcodeBase { + // Inputs. + cs_float *iFluidSynth; + cs_float *iSetting; + cs_float *iValue; + // State. + fluid_synth_t *fluidSynth; + fluid_settings_t *fluidSettings; + char *settingName; + void *mutex; + +public: + int32_t init(CSOUND *csound) { + mutex = csound->Create_Mutex(0); + csound::LockGuard guard(csound, mutex); + toa(csound, iFluidSynth, fluidSynth); + fluidSettings = + fluidSynth ? fluid_synth_get_settings(fluidSynth) : (fluid_settings_t *)0; + if (iSetting) { + settingName = ((STRINGDAT *)iSetting)->data; + } + if (UNLIKELY(!fluidSettings)) { + return csound->InitError(csound, "%s", + Str("fluidSettingString: invalid engine id.\n")); + } + if (fluid_settings_get_type(fluidSettings, (char *)settingName) != + FLUID_STR_TYPE) { + return csound->InitError( + csound, Str("fluidSettingString: '%s' is not a string setting.\n"), + settingName); + } + char *value = ((STRINGDAT *)iValue)->data; + if (SettingSetFailed(fluid_settings_setstr(fluidSettings, (char *)settingName, + (char *)value))) { + return csound->InitError( + csound, Str("fluidSettingString: could not set '%s' to '%s'.\n"), + settingName, value); + } + return OK; + } +}; + +class FluidSettingGetNum : public csound::OpcodeBase { + // Outputs. + cs_float *kOut; + // Inputs. + cs_float *iFluidSynth; + cs_float *iSetting; + // State. + fluid_synth_t *fluidSynth; + fluid_settings_t *fluidSettings; + char *settingName; + int32_t valid; + void *mutex; + +public: + int32_t getValue(CSOUND *csound) { + if (UNLIKELY(!fluidSettings)) { + if (valid) { + valid = 0; + csound->Message(csound, + Str("fluidSettingGetNum: invalid engine id.\n")); + } + *kOut = FL(0.0); + return NOTOK; + } + int32_t type = + fluid_settings_get_type(fluidSettings, (char *)settingName); + if (type == FLUID_NUM_TYPE) { + double value = 0.0; + fluid_settings_getnum(fluidSettings, (char *)settingName, &value); + *kOut = (cs_float)value; + } else if (type == FLUID_INT_TYPE) { + int value = 0; + fluid_settings_getint(fluidSettings, (char *)settingName, &value); + *kOut = (cs_float)value; + } else { + if (valid) { + valid = 0; + csound->Message(csound, Str("fluidSettingGetNum: '%s' is not a " + "numeric setting.\n"), + settingName); + } + *kOut = FL(0.0); + } + return OK; + } + + int32_t init(CSOUND *csound) { + mutex = csound->Create_Mutex(0); + csound::LockGuard guard(csound, mutex); + valid = 1; + toa(csound, iFluidSynth, fluidSynth); + fluidSettings = + fluidSynth ? fluid_synth_get_settings(fluidSynth) : (fluid_settings_t *)0; + if (iSetting) { + settingName = ((STRINGDAT *)iSetting)->data; + } + return getValue(csound); + } + + int32_t kontrol(CSOUND *csound) { + csound::LockGuard guard(csound, mutex); + return getValue(csound); + } +}; + +class FluidSettingGetString : public csound::OpcodeBase { + // Outputs. + STRINGDAT *SOut; + // Inputs. + cs_float *iFluidSynth; + cs_float *iSetting; + // State. + fluid_synth_t *fluidSynth; + fluid_settings_t *fluidSettings; + char *settingName; + void *mutex; + +public: + int32_t init(CSOUND *csound) { + mutex = csound->Create_Mutex(0); + csound::LockGuard guard(csound, mutex); + toa(csound, iFluidSynth, fluidSynth); + fluidSettings = + fluidSynth ? fluid_synth_get_settings(fluidSynth) : (fluid_settings_t *)0; + if (iSetting) { + settingName = ((STRINGDAT *)iSetting)->data; + } + if (UNLIKELY(!fluidSettings)) { + SetStringData(csound, SOut, ""); + csound->Message(csound, "%s", + Str("fluidSettingGetString: invalid engine id.\n")); + return OK; + } + if (fluid_settings_get_type(fluidSettings, (char *)settingName) != + FLUID_STR_TYPE) { + SetStringData(csound, SOut, ""); + csound->Message( + csound, + Str("fluidSettingGetString: '%s' is not a string setting.\n"), + settingName); + return OK; + } +#if FLUIDSYNTH_VERSION_MAJOR >= 2 + char *value = 0; + if (fluid_settings_dupstr(fluidSettings, (char *)settingName, &value) == + FLUID_OK && + value) { + SetStringData(csound, SOut, value); + FreeFluidString(value); + } else { + SetStringData(csound, SOut, ""); + } +#else + char *value = 0; + fluid_settings_getstr(fluidSettings, (char *)settingName, &value); + SetStringData(csound, SOut, value ? value : ""); +#endif + return OK; + } +}; + +static OENTRY localops[] = { + {(char *)"fluidEngine", sizeof(FluidEngine), 0, (char *)"i", (char *)"ppoo", + (SUBR)&FluidEngine::init_, (SUBR)0, (SUBR)0}, + {(char *)"fluidLoad", sizeof(FluidLoad), 0, (char *)"i", (char *)"Tio", + (SUBR)&FluidLoad::init_, (SUBR)0, (SUBR)0}, + {(char *)"fluidInfo", sizeof(FluidInfo), 0, (char *)"S[]", (char *)"i", + (SUBR)&FluidInfo::init_, (SUBR)0, (SUBR)0}, + {(char *)"fluidProgramSelect", sizeof(FluidProgramSelect), 0, (char *)"", + (char *)"iiiii", (SUBR)&FluidProgramSelect::init_, (SUBR)0, (SUBR)0}, + {(char *)"fluidCCi", sizeof(FluidCCI), 0, (char *)"", (char *)"iiii", + (SUBR)&FluidCCI::init_, (SUBR)0, (SUBR)0}, + {(char *)"fluidCCk", sizeof(FluidCCK), 0, (char *)"", (char *)"ikkk", + (SUBR)&FluidCCK::init_, (SUBR)&FluidCCK::kontrol_, (SUBR)0}, + {(char *)"fluidNote", sizeof(FluidNote), 0, (char *)"", (char *)"iiii", + (SUBR)&FluidNote::init_, (SUBR)0, (SUBR)&FluidNote::deinit_}, + {(char *)"fluidOut", sizeof(FluidOut), 0, (char *)"aa", (char *)"i", + (SUBR)&FluidOut::init_, (SUBR)&FluidOut::audio_, (SUBR)0}, + {(char *)"fluidAllOut", sizeof(FluidAllOut), 0, (char *)"aa", (char *)"", + (SUBR)&FluidAllOut::init_, (SUBR)&FluidAllOut::audio_, (SUBR)0}, + {(char *)"fluidControl", sizeof(FluidControl), 0, (char *)"", + (char *)"ikkkkp", (SUBR)FluidControl::init_, (SUBR)FluidControl::kontrol_, + (SUBR)0}, + {(char *)"fluidSetInterpMethod", sizeof(FluidSetInterpMethod), 0, + (char *)"", (char *)"iii", (SUBR)&FluidSetInterpMethod::init_, (SUBR)0, + (SUBR)0}, + {(char *)"fluidSettingNum", sizeof(FluidSettingNum), 0, (char *)"", + (char *)"iSk", (SUBR)&FluidSettingNum::init_, + (SUBR)&FluidSettingNum::kontrol_, (SUBR)0}, + {(char *)"fluidSettingString", sizeof(FluidSettingString), 0, (char *)"", + (char *)"iSS", (SUBR)&FluidSettingString::init_, (SUBR)0, (SUBR)0}, + {(char *)"fluidSettingGetNum", sizeof(FluidSettingGetNum), 0, (char *)"k", + (char *)"iS", (SUBR)&FluidSettingGetNum::init_, + (SUBR)&FluidSettingGetNum::kontrol_, (SUBR)0}, + {(char *)"fluidSettingGetString", sizeof(FluidSettingGetString), 0, + (char *)"S", (char *)"iS", (SUBR)&FluidSettingGetString::init_, (SUBR)0, + (SUBR)0}, + {NULL, 0, 0, NULL, NULL, (SUBR)0, (SUBR)0, (SUBR)0, NULL, 0}}; + +PUBLIC int32_t csoundModuleCreate(CSOUND *csound) { + FluidSynthState *state = new FluidSynthState(); + int32_t result = 0; + result = csound::CreateGlobalPointer(csound, "fluid_synth_state", state); + void *fluid_synths_mutex = csound->Create_Mutex(0); + result = csound::CreateGlobalPointer(csound, "fluid_synths_mutex", + fluid_synths_mutex); + return result; +} + +PUBLIC int32_t csoundModuleInit(CSOUND *csound) { + // printf("csoundModuleInit: %p\n", csound); + OENTRY *ep; + int32_t err = 0; + + for (ep = (OENTRY *)&(localops[0]); ep->opname != NULL; ep++) { + err |= csound->AppendOpcode(csound, ep->opname, ep->dsblksiz, ep->flags, + ep->outypes, ep->intypes, + (int32_t (*)(CSOUND *, void *))ep->init, + (int32_t (*)(CSOUND *, void *))ep->perf, + (int32_t (*)(CSOUND *, void *))ep->deinit); + } + return err; +} + +/** + * Called by Csound to de-initialize the opcode + * just before destroying it. + */ +PUBLIC int32_t csoundModuleDestroy(CSOUND *csound) { + // printf("csoundModuleDestroy: %p\n", csound); + void *fluid_synths_mutex = 0; + csound::QueryGlobalPointer(csound, "fluid_synths_mutex", fluid_synths_mutex); + if (fluid_synths_mutex) { + FluidSynthState *state = 0; + csound::QueryGlobalPointer(csound, "fluid_synth_state", state); + csound->LockMutex(fluid_synths_mutex); + if (state) { + for (size_t i = 0, n = state->synths.size(); i < n; i++) { + fluid_synth_t *fluidSynth = state->synths[i]; + // printf("deleting engine: %p\n", fluidSynth); + fluid_settings_t *fluidSettings = fluid_synth_get_settings(fluidSynth); + delete_fluid_synth(fluidSynth); + delete_fluid_settings(fluidSettings); + } + state->synths.clear(); + state->engines.clear(); + delete state; + state = 0; + } + csound->UnlockMutex(fluid_synths_mutex); + csound->DestroyMutex(fluid_synths_mutex); + fluid_synths_mutex = 0; + } + return 0; +} + +PUBLIC int32_t csoundModuleInfo(void) { + return CSOUND_MODULE_INFO; +} diff --git a/src/hdf5/CMakeLists.txt b/src/hdf5/CMakeLists.txt new file mode 100644 index 00000000..18e33626 --- /dev/null +++ b/src/hdf5/CMakeLists.txt @@ -0,0 +1,29 @@ +option(BUILD_HDF5_OPCODES "Build the hdf5 read/write opcodes" ON) + +if(USE_VCPKG) + find_package(HDF5 CONFIG) +else() + find_package(HDF5) +endif() +check_deps(BUILD_HDF5_OPCODES HDF5_FOUND) + +if(BUILD_HDF5_OPCODES AND HDF5_FOUND) + make_plugin(hdf5ops HDF5IO.c) + + target_include_directories(hdf5ops PRIVATE ${HDF5_INCLUDE_DIRS} ${CSOUND_INCLUDE_DIRS}) + + if(USE_VCPKG) + if(TARGET hdf5::hdf5-static) + target_link_libraries(hdf5ops PRIVATE hdf5::hdf5-static) + elseif(TARGET hdf5::hdf5-shared) + target_link_libraries(hdf5ops PRIVATE hdf5::hdf5-shared) + else() + target_link_libraries(hdf5ops PRIVATE hdf5::hdf5) + endif() + else() + message(STATUS "HDF5_INCLUDE_DIRS: ${HDF5_INCLUDE_DIRS}") + message(STATUS "HDF5_LIBRARIES: ${HDF5_LIBRARIES}") + + target_link_libraries(hdf5ops ${HDF5_LIBRARIES}) + endif() +endif() diff --git a/6.x/src/hdf5/HDF5IO.c b/src/hdf5/HDF5IO.c similarity index 83% rename from 6.x/src/hdf5/HDF5IO.c rename to src/hdf5/HDF5IO.c index 8d45ef5f..945acf4f 100644 --- a/6.x/src/hdf5/HDF5IO.c +++ b/src/hdf5/HDF5IO.c @@ -18,6 +18,7 @@ */ #include "HDF5IO.h" +#include "arrays.h" #include #ifdef _MSC_VER #include @@ -25,7 +26,7 @@ #include #endif -#define HDF5ERROR(x) if (UNLIKELY((x) == -1)) \ +#define HDF5ERROR(x) if (UNLIKELY((x) < 0)) \ {csound->Die(csound, #x" error\nExiting\n");} // Type strings to match the enum types @@ -46,9 +47,10 @@ static const char typeStrings[8][12] = { // Get the variable type name // Do a string comparison and match the type to the enum type -ArgumentType HDF5IO_getArgumentTypeFromArgument(CSOUND *csound, MYFLT *argument) +ArgumentType HDF5IO_getArgumentTypeFromArgument(CSOUND *csound, cs_float *argument) { - const CS_TYPE *csoundType = csound->GetTypeForArg((void *)argument); + IGN(csound); + const CS_TYPE *csoundType = GetTypeForArg((void *)argument); const char *type = csoundType->varTypeName; ArgumentType argumentType = UNKNOWN; @@ -138,7 +140,7 @@ ArgumentType HDF5IO_getArgumentTypeFromString(CSOUND *csound, const char *string // If it doesn't and this function is being called by the hdf5 write function, // create the file // If it exists, open the file for appending -// Find out what size the MYFLT floating point type is and assign the correct +// Find out what size the cs_float floating point type is and assign the correct // hdf5 type to it // Return the pointer to the hdf5 file struct @@ -167,7 +169,10 @@ HDF5File *HDF5IO_newHDF5File(CSOUND *csound, AUXCH *hdf5FileMemory, } else { - hdf5File->fileHandle = H5Fopen(hdf5File->fileName, H5F_ACC_RDWR, H5P_DEFAULT); + hdf5File->fileHandle = H5Fopen(hdf5File->fileName, + openForWriting ? H5F_ACC_RDWR + : H5F_ACC_RDONLY, + H5P_DEFAULT); HDF5ERROR(hdf5File->fileHandle); } @@ -218,29 +223,24 @@ void HDF5IO_writeStringAttribute(CSOUND *csound, HDF5File *self, // Read a string attribute from the hdf5 file // -// Open the data set and get the info structure -// Get the attribute at index 0, this should probably be more general, needs -// more work -// Get the type of the attribute +// Open the data set then open the attribute by name +// Build a fixed size memory datatype bounded to the supplied buffer size // Read the attribute into memory and close all the handles void HDF5IO_readStringAttribute(CSOUND *csound, HDF5File *self, - char *datasetName, char *attributeString) + char *datasetName, char *attributeString, + size_t attributeStringSize) { hid_t dataSetID = H5Dopen2(self->fileHandle, datasetName, H5P_DEFAULT); - H5O_info_t oinfo; -#if HDF5_VERSION_MAJOR == 1 && HDF5_VERSION_MINOR >= 12 - HDF5ERROR(H5Oget_info1(dataSetID, &oinfo)); -#else - HDF5ERROR(H5Oget_info(dataSetID, &oinfo)); -#endif - hid_t attributeID = H5Aopen_by_idx(dataSetID, ".", H5_INDEX_CRT_ORDER, - H5_ITER_INC, 0, H5P_DEFAULT, H5P_DEFAULT); + HDF5ERROR(dataSetID); + hid_t attributeID = H5Aopen(dataSetID, "Variable Type", H5P_DEFAULT); HDF5ERROR(attributeID); hid_t attributeType = H5Aget_type(attributeID); HDF5ERROR(attributeType); - hid_t attributeTypeMemory = H5Tget_native_type(attributeType, H5T_DIR_ASCEND); + hid_t attributeTypeMemory = H5Tcopy(H5T_C_S1); HDF5ERROR(attributeTypeMemory); + HDF5ERROR(H5Tset_size(attributeTypeMemory, attributeStringSize)); + HDF5ERROR(H5Tset_strpad(attributeTypeMemory, H5T_STR_NULLTERM)); HDF5ERROR(H5Aread(attributeID, attributeTypeMemory, attributeString)); HDF5ERROR(H5Aclose(attributeID)); HDF5ERROR(H5Tclose(attributeType)); @@ -255,9 +255,8 @@ void HDF5IO_readStringAttribute(CSOUND *csound, HDF5File *self, bool HDF5IO_getSampleAccurate(CSOUND *csound) { - OPARMS parameters = {0}; - csound->GetOParms(csound, ¶meters); - return parameters.sampleAccurate; + const OPARMS *parameters = csound->GetOParms(csound); + return parameters->sampleAccurate; } @@ -270,16 +269,21 @@ bool HDF5IO_getSampleAccurate(CSOUND *csound) // argument which is for the filename. // Check that the first argument is a string for the filename, check others // are not strings -// Register the callback to close the hdf5 file when performance finishes // Get the path argument and open a hdf5 file, if it doesn't exist create it // Create the datasets in the file so they can be written int32_t HDF5Write_initialise(CSOUND *csound, HDF5Write *self) { - self->ksmps = csound->GetKsmps(csound); + if (UNLIKELY(self->INOCOUNT + self->OUTOCOUNT > 20)) { + + return csound->InitError(csound, + Str("hdf5write: too many arguments " + "(maximum of 20 outputs and inputs)")); + } + + self->ksmps = GetLocalKsmps(&self->h); self->inputArgumentCount = self->INOCOUNT - 1; HDF5Write_checkArgumentSanity(csound, self); - csound->RegisterDeinitCallback(csound, self, HDF5Write_finish); STRINGDAT *path = (STRINGDAT *)self->arguments[0]; self->hdf5File = HDF5IO_newHDF5File(csound, &self->hdf5FileMemory, path, true); @@ -297,7 +301,7 @@ int32_t HDF5Write_initialise(CSOUND *csound, HDF5Write *self) // Close the file space void HDF5Write_writeData(CSOUND *csound, HDF5Write *self, - HDF5Dataset *dataset, MYFLT *data) + HDF5Dataset *dataset, cs_float *data) { HDF5ERROR(H5Dset_extent(dataset->datasetID, dataset->datasetSize)); hid_t filespace = H5Dget_space(dataset->datasetID); @@ -310,6 +314,7 @@ void HDF5Write_writeData(CSOUND *csound, HDF5Write *self, HDF5ERROR(H5Dwrite(dataset->datasetID, self->hdf5File->floatSize, memspace, filespace, H5P_DEFAULT, data)); HDF5ERROR(H5Sclose(filespace)); + HDF5ERROR(H5Sclose(memspace)); } // Write a-rate variables and arrays to the specified data set @@ -323,20 +328,47 @@ void HDF5Write_writeData(CSOUND *csound, HDF5Write *self, // Increment the offset by the size of the vector that was just written void HDF5Write_writeAudioData(CSOUND *csound, HDF5Write *self, - HDF5Dataset *dataset, MYFLT *dataPointer) + HDF5Dataset *dataset, cs_float *dataPointer) { size_t offset = self->h.insdshead->ksmps_offset; size_t early = self->h.insdshead->ksmps_no_end; - int32_t vectorSize = (int32_t)(self->ksmps - offset - early); + size_t vectorSize = self->ksmps - offset - early; if (UNLIKELY(vectorSize == 0)) { return; } - dataset->datasetSize[0] += self->ksmps; + dataset->datasetSize[0] += vectorSize; + dataset->chunkDimensions[0] = vectorSize; + + if (dataset->writeType == ARATE_ARRAY) { - HDF5Write_writeData(csound, self, dataset, &dataPointer[offset]); + hsize_t i; + size_t memberCount = 1; + size_t t, member; + cs_float *staging; + + for (i = 1; i < dataset->rank; ++i) + memberCount *= (size_t)dataset->chunkDimensions[i]; + + staging = (cs_float *)csound->Malloc(csound, + vectorSize * memberCount * sizeof(cs_float)); + + for (t = 0; t < vectorSize; ++t) { + + for (member = 0; member < memberCount; ++member) + staging[t * memberCount + member] = + dataPointer[member * self->ksmps + offset + t]; + } + + HDF5Write_writeData(csound, self, dataset, staging); + csound->Free(csound, staging); + } + else { + + HDF5Write_writeData(csound, self, dataset, &dataPointer[offset]); + } dataset->offset[0] += vectorSize; } @@ -348,7 +380,7 @@ void HDF5Write_writeAudioData(CSOUND *csound, HDF5Write *self, // Increment the offset by 1 void HDF5Write_writeControlData(CSOUND *csound, HDF5Write *self, - HDF5Dataset *dataset, MYFLT *dataPointer) + HDF5Dataset *dataset, cs_float *dataPointer) { dataset->datasetSize[0]++; @@ -513,7 +545,7 @@ void HDF5Write_initialiseHDF5Dataset(CSOUND *csound, HDF5Write *self, HDF5ERROR(H5Pset_chunk(cparams, dataset->rank, dataset->chunkDimensions)); - MYFLT zero = 0; + cs_float zero = 0; HDF5ERROR(H5Pset_fill_value(cparams, self->hdf5File->floatSize, &zero)); @@ -522,6 +554,8 @@ void HDF5Write_initialiseHDF5Dataset(CSOUND *csound, HDF5Write *self, self->hdf5File->floatSize, dataspaceID, H5P_DEFAULT, cparams, H5P_DEFAULT); HDF5ERROR(dataset->datasetID); + HDF5ERROR(H5Sclose(dataspaceID)); + HDF5ERROR(H5Pclose(cparams)); HDF5IO_writeStringAttribute(csound, self->hdf5File, dataset, "Variable Type", typeStrings[dataset->writeType]); @@ -570,11 +604,13 @@ void HDF5Write_newArrayDataset(CSOUND *csound, HDF5Write *self, &dataset->offsetMemory); dataset->offset = dataset->offsetMemory.auxp; + int32_t dimensionOffset = dataset->writeType == IRATE_ARRAY ? 0 : 1; + for (i = 0; i < array->dimensions; ++i) { - dataset->chunkDimensions[i + 1] = array->sizes[i]; - dataset->maxDimensions[i + 1] = array->sizes[i]; - dataset->datasetSize[i + 1] = array->sizes[i]; + dataset->chunkDimensions[i + dimensionOffset] = array->sizes[i]; + dataset->maxDimensions[i + dimensionOffset] = array->sizes[i]; + dataset->datasetSize[i + dimensionOffset] = array->sizes[i]; } switch (dataset->writeType) { @@ -673,7 +709,7 @@ void HDF5Write_createDatasets(CSOUND *csound, HDF5Write *self) for (i = 0; i < self->inputArgumentCount; ++i) { HDF5Dataset *currentDataset = &self->datasets[i]; - currentDataset->datasetName = csound->GetInputArgName(self, (int32_t)i + 1); + currentDataset->datasetName = GetInputArgName(&self->h, (uint32_t)i + 1); currentDataset->argumentPointer = self->arguments[i + 1]; currentDataset->writeType = HDF5IO_getArgumentTypeFromArgument(csound, currentDataset->argumentPointer); @@ -735,21 +771,17 @@ void HDF5Write_createDatasets(CSOUND *csound, HDF5Write *self) // Get the amount of output arguments // Check that input == output arguments and input arguments are strings, // output not strings -// Register the finish callback to close the hdf5 file when performance -// is finished // Check csound is running in sample accurate mode // Get the path string from the first argument // Open the hdf5 file then open the hdf5 datasets int32_t HDF5Read_initialise(CSOUND *csound, HDF5Read *self) { - self->ksmps = csound->GetKsmps(csound); + self->ksmps = GetLocalKsmps(&self->h); self->inputArgumentCount = self->INOCOUNT - 1; self->outputArgumentCount = self->OUTOCOUNT; HDF5Read_checkArgumentSanity(csound, self); - csound->RegisterDeinitCallback(csound, self, HDF5Read_finish); self->isSampleAccurate = HDF5IO_getSampleAccurate(csound); - //STRINGDAT *path = (STRINGDAT *)self->arguments[self->outputArgumentCount]; self->hdf5File = HDF5IO_newHDF5File(csound, &self->hdf5FileMemory, self->path, false); HDF5Read_openDatasets(csound, self); @@ -763,16 +795,16 @@ int32_t HDF5Read_initialise(CSOUND *csound, HDF5Read *self) // mode the stride of the data read from the hdf5 file isn't correct // and must be offset properly before it is written to the array data -void HDF5Read_copySampleBufferToArray(size_t channelCount, MYFLT *sampleBuffer, - MYFLT *arrayData, size_t vectorSize, +void HDF5Read_copySampleBufferToArray(size_t channelCount, cs_float *sampleBuffer, + cs_float *arrayData, size_t vectorSize, size_t offset, size_t ksmps) { - size_t channel; + size_t channel, t; for (channel = 0; channel < channelCount; ++channel) { - memcpy(&arrayData[ksmps * channel + offset], - &sampleBuffer[vectorSize * channel], - sizeof(MYFLT) * vectorSize); + for (t = 0; t < vectorSize; ++t) + arrayData[ksmps * channel + offset + t] = + sampleBuffer[t * channelCount + channel]; } } @@ -786,15 +818,8 @@ void HDF5Read_copySampleBufferToArray(size_t channelCount, MYFLT *sampleBuffer, void HDF5Read_readData(CSOUND *csound, HDF5Read *self, HDF5Dataset *dataset, hsize_t *offset, hsize_t *chunkDimensions, - MYFLT *dataPointer) + cs_float *dataPointer) { - uint64_t kCount = csound->GetKcounter(csound); - - if (kCount > dataset->datasetSize[0]) { - - return; - } - hid_t filespace = H5Dget_space(dataset->datasetID); HDF5ERROR(filespace); HDF5ERROR(H5Sselect_hyperslab(filespace, H5S_SELECT_SET, offset, @@ -826,8 +851,15 @@ void HDF5Read_readData(CSOUND *csound, HDF5Read *self, HDF5Dataset *dataset, // Increment the offset by the vector size void HDF5Read_readAudioData(CSOUND *csound, HDF5Read *self, - HDF5Dataset *dataset, MYFLT *inputDataPointer) + HDF5Dataset *dataset, cs_float *inputDataPointer) { + { + size_t memberCount = dataset->readType == ARATE_ARRAY + ? dataset->elementCount : 1; + memset(inputDataPointer, 0, + self->ksmps * memberCount * sizeof(cs_float)); + } + if (dataset->offset[0] >= dataset->datasetSize[0]) { return; @@ -836,21 +868,21 @@ void HDF5Read_readAudioData(CSOUND *csound, HDF5Read *self, size_t offset = self->h.insdshead->ksmps_offset; size_t early = self->h.insdshead->ksmps_no_end; - size_t vectorSize = (int32_t)(self->ksmps - offset - early); + size_t vectorSize = self->ksmps - offset - early; if (vectorSize + dataset->offset[0] > dataset->datasetSize[0]) { - vectorSize = (int32_t)(dataset->datasetSize[0] - - dataset->offset[0]); + vectorSize = dataset->datasetSize[0] - dataset->offset[0]; } - MYFLT *dataPointer = - vectorSize != self->ksmps ? dataset->sampleBuffer : inputDataPointer; - // FIXME if this is called frequently or on the audio thread then this won't - // work and will need a different solution + + if (UNLIKELY(vectorSize == 0)) { + return; + } + #ifdef _MSC_VER //hsize_t* chunkDimensions = malloc (dataset->rank * sizeof (hsize_t)); - hsize_t chunkDimensions[16]; + hsize_t chunkDimensions[H5S_MAX_RANK]; #else hsize_t chunkDimensions[dataset->rank]; #endif @@ -863,15 +895,19 @@ void HDF5Read_readAudioData(CSOUND *csound, HDF5Read *self, // ); //chunkDimensions[dataset->rank - 1] = vectorSize; - HDF5Read_readData (csound, self, dataset, dataset->offset, - chunkDimensions, dataPointer); - - if (vectorSize != self->ksmps) { + if (dataset->readType == ARATE_ARRAY || vectorSize != self->ksmps) { + HDF5Read_readData(csound, self, dataset, dataset->offset, + chunkDimensions, dataset->sampleBuffer); HDF5Read_copySampleBufferToArray(dataset->elementCount, dataset->sampleBuffer, inputDataPointer, vectorSize, offset, self->ksmps); } + else { + + HDF5Read_readData(csound, self, dataset, dataset->offset, + chunkDimensions, inputDataPointer); + } dataset->offset[0] += vectorSize; @@ -890,7 +926,7 @@ void HDF5Read_readAudioData(CSOUND *csound, HDF5Read *self, // Increment the offset variable void HDF5Read_readControlData(CSOUND *csound, HDF5Read *self, - HDF5Dataset *dataset, MYFLT *dataPointer) + HDF5Dataset *dataset, cs_float *dataPointer) { if (dataset->offset[0] >= dataset->datasetSize[0]) { @@ -1070,7 +1106,8 @@ void HDF5Read_checkReadTypeSanity(CSOUND *csound, HDF5Read *self, { char attributeString[12] = {"UNKNOWN\0"}; HDF5IO_readStringAttribute(csound, self->hdf5File, - dataset->datasetName, attributeString); + dataset->datasetName, attributeString, + sizeof(attributeString)); dataset->writeType = HDF5IO_getArgumentTypeFromString(csound, attributeString); if (dataset->readType == ARATE_ARRAY @@ -1110,11 +1147,11 @@ void HDF5Read_checkReadTypeSanity(CSOUND *csound, HDF5Read *self, return; } } - else { - csound->Die(csound, "%s", Str("hdf5read: Unable to read saved type of " - "dataset, exiting")); - } + csound->Die(csound, Str("hdf5read: Error, dataset \"%s\" saved type %s " + "is incompatible with output type %s, exiting"), + dataset->datasetName, typeStrings[dataset->writeType], + typeStrings[dataset->readType]); } // Allocate the memory for an output array from the opcode @@ -1123,33 +1160,41 @@ void HDF5Read_checkReadTypeSanity(CSOUND *csound, HDF5Read *self, // Get the rank and allocate the dimension sizes memory // Assign first dimension size to array sizes and dataset element count // If the rank is greater than 1, multiply dimensions to get element count -// Allocate data space for the array using the element count +// Let the engine allocate data space for the array using the element count -void HDF5Read_allocateArray(CSOUND *csound, HDF5Dataset *dataset, +void HDF5Read_allocateArray(CSOUND *csound, HDF5Read *self, + HDF5Dataset *dataset, hsize_t rank, hsize_t *dimensions) { ARRAYDAT *array = dataset->argumentPointer; + size_t elementCount = 1; + size_t i; + array->dimensions = (int32_t)rank; array->sizes = csound->Calloc(csound, sizeof(int32_t) * rank); - array->sizes[0] = (int32_t)dimensions[0]; - dataset->elementCount = dimensions[0]; - - if (rank > 1) { - size_t i; - for (i = 1; i < rank; ++i) { + for (i = 0; i < rank; ++i) { + if (UNLIKELY(dimensions[i] > (hsize_t) INT32_MAX)) + csound->Die(csound, "%s", Str("hdf5read: Error, dataset dimension too " + "large for an array output")); + array->sizes[i] = (int32_t)dimensions[i]; + if (UNLIKELY(dimensions[i] != 0 && + elementCount > ((size_t) -1) / (size_t)dimensions[i])) + csound->Die(csound, "%s", Str("hdf5read: Error, dataset too large to " + "allocate")); + elementCount *= (size_t)dimensions[i]; + } - array->sizes[i] = (int32_t)dimensions[i]; - dataset->elementCount *= array->sizes[i]; + if (UNLIKELY(elementCount < 1)) + elementCount = 1; + dataset->elementCount = elementCount; - } + if (UNLIKELY(csound_array_ensure_capacity(csound, array, + dataset->elementCount, + self->h.insdshead) != OK)) { + csound->Die(csound, "%s", Str("hdf5read: Error, could not allocate " + "output array")); } - - CS_VARIABLE *arrayVariable = array->arrayType->createVariable(csound, NULL); - array->arrayMemberSize = arrayVariable->memBlockSize; - array->data = - csound->Calloc(csound, - arrayVariable->memBlockSize * dataset->elementCount); } @@ -1189,15 +1234,29 @@ void HDF5Read_initialiseArrayOutput(CSOUND *csound, HDF5Read *self, if (dataset->readType != IRATE_ARRAY && dataset->readAll == false) { // FIXME if this is called frequently or on the audio thread then this won't // work and will need a different solution + hsize_t elementRank = (hsize_t)(dataset->rank - 1); + hsize_t scalarDimension[1] = {1}; #ifdef _MSC_VER - hsize_t* arrayDimensions = malloc ((dataset->rank - 1) * sizeof (hsize_t)); + hsize_t* arrayDimensions = malloc ((elementRank > 0 ? elementRank : 1) + * sizeof (hsize_t)); #else - hsize_t arrayDimensions[dataset->rank - 1]; + hsize_t arrayDimensions[elementRank > 0 ? elementRank : 1]; #endif - memcpy(arrayDimensions, &dataset->datasetSize[1], - (dataset->rank - 1) * sizeof(hsize_t)); + hsize_t *elementDimensions = arrayDimensions; + + if (elementRank == 0) { + + elementRank = 1; + elementDimensions = scalarDimension; + } + else { + + memcpy(arrayDimensions, &dataset->datasetSize[1], + elementRank * sizeof(hsize_t)); + } - HDF5Read_allocateArray(csound, dataset, (dataset->rank - 1), arrayDimensions); + HDF5Read_allocateArray(csound, self, dataset, elementRank, + elementDimensions); csound->AuxAlloc(csound, sizeof(hsize_t) * dataset->rank, &dataset->offsetMemory); @@ -1208,7 +1267,7 @@ void HDF5Read_initialiseArrayOutput(CSOUND *csound, HDF5Read *self, self->isSampleAccurate == true) { csound->AuxAlloc(csound, - dataset->elementCount * self->ksmps * sizeof(MYFLT), + dataset->elementCount * self->ksmps * sizeof(cs_float), &dataset->sampleBufferMemory); dataset->sampleBuffer = dataset->sampleBufferMemory.auxp; } @@ -1224,7 +1283,7 @@ void HDF5Read_initialiseArrayOutput(CSOUND *csound, HDF5Read *self, #endif memcpy(arrayDimensions, dataset->datasetSize, dataset->rank * sizeof(hsize_t)); - HDF5Read_allocateArray(csound, dataset, dataset->rank, arrayDimensions); + HDF5Read_allocateArray(csound, self, dataset, dataset->rank, arrayDimensions); ARRAYDAT *array = dataset->argumentPointer; #ifdef _MSC_VER hsize_t* offset = malloc ((dataset->rank) * sizeof (hsize_t)); @@ -1236,6 +1295,7 @@ void HDF5Read_initialiseArrayOutput(CSOUND *csound, HDF5Read *self, arrayDimensions, array->data); #ifdef _MSC_VER free (offset); + free (arrayDimensions); #endif } } @@ -1278,11 +1338,9 @@ void HDF5Read_initialiseScalarOutput(CSOUND *csound, HDF5Read *self, dataset->offset = dataset->offsetMemory.auxp; memset(dataset->offset, 0, sizeof(hsize_t)); - if (dataset->readType == ARATE_VAR - && - self->isSampleAccurate == true) { + if (dataset->readType == ARATE_VAR) { - csound->AuxAlloc(csound, self->ksmps * sizeof(MYFLT), + csound->AuxAlloc(csound, self->ksmps * sizeof(cs_float), &dataset->sampleBufferMemory); dataset->sampleBuffer = dataset->sampleBufferMemory.auxp; dataset->elementCount = 1; @@ -1306,7 +1364,7 @@ void HDF5Read_initialiseScalarOutput(CSOUND *csound, HDF5Read *self, // Assign the input argument name to the dataset name // Get the read type from the arguments // Assign the argument data pointer -// Check that the read type and write type are compatiblea +// Check that the read type and write type are compatible // Initialise the data set using the corresponding function for read type specified void HDF5Read_openDatasets(CSOUND *csound, HDF5Read *self) @@ -1321,15 +1379,23 @@ void HDF5Read_openDatasets(CSOUND *csound, HDF5Read *self) HDF5Dataset *currentDataset = &self->datasets[i]; STRINGDAT *inputArgument = (STRINGDAT *)self->names[i]; - csound->AuxAlloc(csound, sizeof(char) * strlen(inputArgument->data)+1, + size_t nameLength = strlen(inputArgument->data); + + if (UNLIKELY(nameLength == 0)) { + + csound->Die(csound, Str("hdf5read: Error, dataset name %d is empty"), + i + 1); + } + + csound->AuxAlloc(csound, sizeof(char) * nameLength + 1, ¤tDataset->datasetNameMemory); currentDataset->datasetName = currentDataset->datasetNameMemory.auxp; - strcpy(currentDataset->datasetName, inputArgument->data); + memcpy(currentDataset->datasetName, inputArgument->data, nameLength + 1); - if (currentDataset->datasetName[strlen(inputArgument->data) - 1] == '*') { + if (currentDataset->datasetName[nameLength - 1] == '*') { currentDataset->readAll = true; - currentDataset->datasetName[strlen(inputArgument->data) - 1] = '\0'; + currentDataset->datasetName[nameLength - 1] = '\0'; } currentDataset->readType = HDF5IO_getArgumentTypeFromArgument(csound, self->arguments[i]); @@ -1380,24 +1446,28 @@ void HDF5Read_openDatasets(CSOUND *csound, HDF5Read *self) static OENTRY localops[] = { { - .opname = "hdf5write", - .dsblksiz = sizeof(HDF5Write), - .thread = 3, - .outypes = "", - .intypes = "*", - .iopadr = (SUBR)HDF5Write_initialise, - .kopadr = (SUBR)HDF5Write_process, - .aopadr = NULL + (char *)"hdf5write", + sizeof(HDF5Write), + 0, + (char *)"", + (char *)"*", + (SUBR)HDF5Write_initialise, + (SUBR)HDF5Write_process, + (SUBR)HDF5Write_finish, + NULL, + 0 }, { - .opname = "hdf5read", - .dsblksiz = sizeof(HDF5Read), - .thread = 3, - .outypes = "********************", - .intypes = "SW", - .iopadr = (SUBR)HDF5Read_initialise, - .kopadr = (SUBR)HDF5Read_process, - .aopadr = NULL + (char *)"hdf5read", + sizeof(HDF5Read), + 0, + (char *)"********************", + (char *)"SW", + (SUBR)HDF5Read_initialise, + (SUBR)HDF5Read_process, + (SUBR)HDF5Read_finish, + NULL, + 0 } }; diff --git a/6.x/src/hdf5/HDF5IO.h b/src/hdf5/HDF5IO.h similarity index 96% rename from 6.x/src/hdf5/HDF5IO.h rename to src/hdf5/HDF5IO.h index 5b7a55a3..df243cfb 100644 --- a/6.x/src/hdf5/HDF5IO.h +++ b/src/hdf5/HDF5IO.h @@ -37,8 +37,8 @@ typedef struct _fft { OPDS h; ARRAYDAT *out; ARRAYDAT *in, *in2; - MYFLT *f; - MYFLT b; + cs_float *f; + cs_float b; int32_t n; void *setup; AUXCH mem; @@ -66,7 +66,7 @@ typedef struct HDF5Dataset hid_t datasetID; size_t elementCount; - MYFLT *sampleBuffer; + cs_float *sampleBuffer; AUXCH sampleBufferMemory; bool readAll; @@ -90,7 +90,7 @@ void HDF5IO_deleteHDF5File(CSOUND *csound, HDF5File *hdf5File); typedef struct HDF5Write { OPDS h; - MYFLT *arguments[20]; + cs_float *arguments[20]; int32_t inputArgumentCount; size_t ksmps; HDF5File *hdf5File; @@ -119,7 +119,7 @@ void HDF5Write_deleteArrayDataset(CSOUND *csound, HDF5Dataset *dataset); typedef struct HDF5Read { OPDS h; - MYFLT *arguments[20]; + cs_float *arguments[20]; STRINGDAT *path; STRINGDAT *names[20]; int32_t inputArgumentCount; diff --git a/src/image/CMakeLists.txt b/src/image/CMakeLists.txt new file mode 100644 index 00000000..223ab9a5 --- /dev/null +++ b/src/image/CMakeLists.txt @@ -0,0 +1,12 @@ +# Build image opcodes +option(BUILD_IMAGE_OPCODES "Build the image opcodes" ON) + +find_package(PNG) +check_deps(BUILD_IMAGE_OPCODES PNG_FOUND) + +if(BUILD_IMAGE_OPCODES AND PNG_FOUND) + make_plugin(image imageOpcodes.c) + target_include_directories(image PRIVATE ${CSOUND_INCLUDE_DIRS}) + target_compile_definitions(image PRIVATE BUILD_PLUGINS) + target_link_libraries(image PRIVATE PNG::PNG) +endif() diff --git a/6.x/src/image/README.md b/src/image/README.md similarity index 99% rename from 6.x/src/image/README.md rename to src/image/README.md index 4a58a5a4..dbc1c199 100644 --- a/6.x/src/image/README.md +++ b/src/image/README.md @@ -3,4 +3,3 @@ Image opcodes To build the image opcodes, you will need the libpng library, which can be easily built and installed from sources using CMake, see [http://www.libpng.org/pub/png/libpng.html](http://www.libpng.org/pub/png/libpng.html) - diff --git a/6.x/src/image/imageOpcodes.c b/src/image/imageOpcodes.c similarity index 58% rename from 6.x/src/image/imageOpcodes.c rename to src/image/imageOpcodes.c index d383e179..37c3f9a3 100644 --- a/6.x/src/image/imageOpcodes.c +++ b/src/image/imageOpcodes.c @@ -64,6 +64,9 @@ /* #include */ /* #include */ +#include +#include + #include "csdl.h" #include "imageOpcodes.h" @@ -78,12 +81,12 @@ /* #undef CS_KSMPS */ /* #define CS_KSMPS (csound->GetKsmps(csound)) */ -static Image * __doOpenImage(char * filename, CSOUND *csound) +static Image * __doOpenImage(const char * filename, CSOUND *csound) { #ifdef USE_LIBPNG #define HS (8) FILE *fp; - void *fd; + char *fullname; unsigned char header[HS]; png_structp png_ptr; png_infop info_ptr; @@ -92,24 +95,30 @@ static Image * __doOpenImage(char * filename, CSOUND *csound) png_uint_32 width, height, rowbytes; int32_t bit_depth; int32_t color_type; - unsigned char *image_data; - png_bytepp row_pointers; + unsigned char * volatile image_data = NULL; + size_t datasize; + png_bytepp volatile row_pointers = NULL; uint32_t i; Image *img; - fd = csound->FileOpen2(csound, &fp, CSFILE_STD, filename, "rb", - "SFDIR;SSDIR", CSFTYPE_IMAGE_PNG, 0); - if (UNLIKELY(fd == NULL)) { + fullname = csound->FindInputFile(csound, filename, "SFDIR;SSDIR"); + fp = (fullname != NULL) ? fopen(fullname, "rb") : NULL; + if (UNLIKELY(fp == NULL)) { + if (fullname != NULL) + csound->Free(csound, fullname); csound->InitError(csound, Str("imageload: cannot open image %s.\n"), filename); return NULL; } + csound->NotifyFileOpened(csound, fullname, CSFTYPE_IMAGE_PNG, 0, 0); + csound->Free(csound, fullname); if (UNLIKELY(HS!=fread(header, 1, HS, fp))) { csound->InitError(csound, Str("imageload: file %s is not in PNG format.\n"), filename); + fclose(fp); return NULL; } is_png = !png_sig_cmp(header, 0, HS); @@ -118,21 +127,21 @@ static Image * __doOpenImage(char * filename, CSOUND *csound) csound->InitError(csound, Str("imageload: file %s is not in PNG format.\n"), filename); - csound->FileClose(csound, fd); + fclose(fp); return NULL; } png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (UNLIKELY(!png_ptr)) { csound->InitError(csound, "%s", Str("imageload: out of memory.\n")); - csound->FileClose(csound, fd); + fclose(fp); return NULL; } info_ptr = png_create_info_struct(png_ptr); if (UNLIKELY(!info_ptr)) { png_destroy_read_struct(&png_ptr, (png_infopp) NULL, (png_infopp) NULL); csound->InitError(csound, "%s", Str("imageload: out of memory.\n")); - csound->FileClose(csound, fd); + fclose(fp); return NULL; } @@ -140,10 +149,22 @@ static Image * __doOpenImage(char * filename, CSOUND *csound) /* if (UNLIKELY(!end_ptr)) { */ /* png_destroy_read_struct(&png_ptr, &info_ptr, (png_infopp) NULL); */ /* csound->InitError(csound, "%s", Str("imageload: out of memory.\n")); */ - /* csound->FileClose(csound, fd); */ + /* fclose(fp); */ /* return NULL; */ /* } */ + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + if (image_data != NULL) + csound->Free(csound, image_data); + if (row_pointers != NULL) + csound->Free(csound, row_pointers); + fclose(fp); + csound->InitError(csound, Str("imageload: error reading image %s.\n"), + filename); + return NULL; + } + png_init_io(png_ptr, fp); png_set_sig_bytes(png_ptr, HS); @@ -157,8 +178,13 @@ static Image * __doOpenImage(char * filename, CSOUND *csound) png_set_strip_alpha(png_ptr); if (bit_depth == 16) png_set_strip_16(png_ptr); - if (bit_depth < 8) - png_set_packing(png_ptr); + if (bit_depth < 8) { + if (color_type == PNG_COLOR_TYPE_GRAY || + color_type == PNG_COLOR_TYPE_GRAY_ALPHA) + png_set_expand_gray_1_2_4_to_8(png_ptr); + else + png_set_packing(png_ptr); + } if (color_type == PNG_COLOR_TYPE_GRAY || color_type == PNG_COLOR_TYPE_GRAY_ALPHA) png_set_gray_to_rgb(png_ptr); @@ -168,10 +194,18 @@ static Image * __doOpenImage(char * filename, CSOUND *csound) png_read_update_info(png_ptr, info_ptr); rowbytes = png_get_rowbytes(png_ptr, info_ptr); + if (UNLIKELY((size_t) rowbytes > ((size_t) -1) / (size_t) height)) { + png_destroy_read_struct(&png_ptr, &info_ptr, NULL); + csound->InitError(csound, "%s", Str("imageload: image too large.\n")); + fclose(fp); + return NULL; + } + datasize = (size_t) rowbytes * (size_t) height; if (UNLIKELY((image_data = - (unsigned char *)csound->Malloc(csound,rowbytes*height))==NULL)) { + (unsigned char *)csound->Malloc(csound,datasize))==NULL)) { png_destroy_read_struct(&png_ptr, &info_ptr, NULL); csound->InitError(csound, "%s", Str("imageload: out of memory.\n")); + fclose(fp); return NULL; } @@ -181,16 +215,17 @@ static Image * __doOpenImage(char * filename, CSOUND *csound) csound->Free(csound,image_data); image_data = NULL; csound->InitError(csound, "%s", Str("imageload: out of memory.\n")); + fclose(fp); return NULL; } for (i = 0; i < height; i++) - row_pointers[i] = image_data + i*rowbytes; + row_pointers[i] = image_data + (size_t) i * (size_t) rowbytes; png_read_image(png_ptr, row_pointers); csound->Free(csound,row_pointers); png_destroy_read_struct(&png_ptr, &info_ptr, NULL); - csound->FileClose(csound, fd); + fclose(fp); img = csound->Malloc(csound, sizeof(Image)); if (UNLIKELY(!img)) { @@ -272,40 +307,54 @@ static Image * __doOpenImage(char * filename, CSOUND *csound) } -static int32_t __doSaveImage(Image *image, char *filename, CSOUND *csound) +static int32_t __doSaveImage(Image *image, const char *filename, CSOUND *csound) { #ifdef USE_LIBPNG png_structp png_ptr; png_infop info_ptr; - png_bytepp row_pointers; + png_bytepp volatile row_pointers = NULL; uint32_t rowbytes; int32_t i; FILE *fp; - void *fd; + char *fullname; - fd = csound->FileOpen2(csound, &fp, CSFILE_STD, filename, "wb", - "", CSFTYPE_IMAGE_PNG, 0); - if (UNLIKELY(fd == NULL)) { + fullname = csound->FindOutputFile(csound, filename, "SFDIR;SSDIR"); + fp = (fullname != NULL) ? fopen(fullname, "wb") : NULL; + if (UNLIKELY(fp == NULL)) { + if (fullname != NULL) + csound->Free(csound, fullname); return csound->InitError(csound, - Str("imageload: cannot open image %s for writing.\n"), + Str("imagesave: cannot open image %s for writing.\n"), filename); } + csound->NotifyFileOpened(csound, fullname, CSFTYPE_IMAGE_PNG, 1, 0); + csound->Free(csound, fullname); png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); if (UNLIKELY(!png_ptr)){ - csound->FileClose(csound, fd); - return csound->InitError(csound, "%s", Str("imageload: out of memory.\n")); + fclose(fp); + return csound->InitError(csound, "%s", Str("imagesave: out of memory.\n")); } info_ptr = png_create_info_struct(png_ptr); if (UNLIKELY(!info_ptr)) { png_destroy_write_struct(&png_ptr, (png_infopp)NULL); - csound->FileClose(csound, fd); - return csound->InitError(csound, "%s", Str("imageload: out of memory.\n")); + fclose(fp); + return csound->InitError(csound, "%s", Str("imagesave: out of memory.\n")); + } + + if (setjmp(png_jmpbuf(png_ptr))) { + png_destroy_write_struct(&png_ptr, &info_ptr); + if (row_pointers != NULL) + csound->Free(csound, row_pointers); + fclose(fp); + return csound->InitError(csound, + Str("imagesave: error writing image %s.\n"), + filename); } png_init_io(png_ptr, fp); @@ -318,7 +367,8 @@ static int32_t __doSaveImage(Image *image, char *filename, CSOUND *csound) row_pointers = (png_bytepp)csound->Malloc(csound, image->h*sizeof(png_bytep)); if (UNLIKELY(row_pointers == NULL)) { png_destroy_write_struct(&png_ptr, &info_ptr); - return csound->InitError(csound, "%s", Str("imageload: out of memory.\n")); + fclose(fp); + return csound->InitError(csound, "%s", Str("imagesave: out of memory.\n")); } rowbytes = png_get_rowbytes(png_ptr, info_ptr); @@ -331,7 +381,7 @@ static int32_t __doSaveImage(Image *image, char *filename, CSOUND *csound) csound->Free(csound,row_pointers); png_destroy_write_struct(&png_ptr, &info_ptr); - csound->FileClose(csound, fd); + fclose(fp); return OK; #else @@ -357,12 +407,24 @@ static Image * createImage(CSOUND *csound, int32_t w, int32_t h) Image *img; size_t datasize; + if (UNLIKELY(w < 1 || h < 1)) + return (Image *) NULL; + if (UNLIKELY((size_t) w > ((size_t) -1) / 3 / (size_t) h)) + return (Image *) NULL; + img = csound->Malloc(csound, sizeof(Image)); + if (UNLIKELY(img == NULL)) + return (Image *) NULL; + img->w = w; img->h = h; - datasize = img->w*img->h*3 * sizeof(unsigned char); - img->imageData = csound->Malloc(csound, datasize); + datasize = (size_t) w * (size_t) h * 3; + img->imageData = csound->Calloc(csound, datasize); + if (UNLIKELY(img->imageData == NULL)) { + csound->Free(csound, img); + return (Image *) NULL; + } return img; } @@ -384,84 +446,101 @@ static Images * getImages(CSOUND *csound) return pimages; } +static Image * getImage(CSOUND *csound, cs_float kn) +{ + Images *pimages; + int32_t n; + + pimages = (Images *) csound->QueryGlobalVariable(csound, + "imageOpcodes.images"); + if (UNLIKELY(pimages == NULL)) + return (Image *) NULL; + n = (int32_t) kn; + if (UNLIKELY(n < 1 || (size_t) n > pimages->cnt)) + return (Image *) NULL; + return pimages->images[n-1]; +} + static int32_t imagecreate (CSOUND *csound, IMGCREATE * p) { Images *pimages; Image *img; + Image **newImages; pimages = getImages(csound); - pimages->cnt++; - pimages->images = - (Image **) csound->ReAlloc(csound, pimages->images, - sizeof(Image *) * pimages->cnt); - img = createImage(csound, *p->kw, *p->kh); - - if (UNLIKELY(img==NULL)) { + if (UNLIKELY(img == NULL)) { return csound->InitError(csound, "%s", Str("Cannot allocate memory.\n")); } - else { - pimages->images[pimages->cnt-1] = img; - *(p->kn) = (MYFLT) pimages->cnt; - return OK; + + newImages = (Image **) csound->ReAlloc(csound, pimages->images, + sizeof(Image *) * (pimages->cnt + 1)); + if (UNLIKELY(newImages == NULL)) { + csound->Free(csound, img->imageData); + csound->Free(csound, img); + return csound->InitError(csound, "%s", Str("Cannot allocate memory.\n")); } + + pimages->images = newImages; + pimages->images[pimages->cnt] = img; + pimages->cnt++; + *(p->kn) = (cs_float) pimages->cnt; + return OK; } static int32_t imageload (CSOUND *csound, IMGLOAD * p) { - char filename[256]; Images *pimages; Image *img; + Image **newImages; pimages = getImages(csound); - pimages->cnt++; - pimages->images = - (Image **) csound->ReAlloc(csound, pimages->images, - sizeof(Image *) * pimages->cnt); - - strncpy(filename, (char*) (p->ifilnam->data), 255); filename[255]='\0'; + img = __doOpenImage((const char*) (p->ifilnam->data), csound); + if (UNLIKELY(img == NULL)) + return NOTOK; - img = __doOpenImage(filename, csound); - - if (LIKELY(img)) { - pimages->images[pimages->cnt-1] = img; - *(p->kn) = (MYFLT) pimages->cnt; - return OK; - } - else { - return csound->InitError(csound, - Str("imageload: cannot open image %s.\n"), - filename); + newImages = (Image **) csound->ReAlloc(csound, pimages->images, + sizeof(Image *) * (pimages->cnt + 1)); + if (UNLIKELY(newImages == NULL)) { + csound->Free(csound, img->imageData); + csound->Free(csound, img); + return csound->InitError(csound, "%s", Str("Cannot allocate memory.\n")); } + + pimages->images = newImages; + pimages->images[pimages->cnt] = img; + pimages->cnt++; + *(p->kn) = (cs_float) pimages->cnt; + return OK; } static int32_t imagesize (CSOUND *csound, IMGSIZE * p) { - Images *pimages; Image *img; - pimages = (Images *) csound->QueryGlobalVariable(csound, - "imageOpcodes.images"); - img = pimages->images[(int32_t)(*p->kn)-1]; + img = getImage(csound, *p->kn); + if (UNLIKELY(img == NULL)) + return csound->InitError(csound, "%s", + Str("imagesize: invalid image handle.\n")); - *(p->kw) = (MYFLT) img->w; - *(p->kh) = (MYFLT) img->h; + *(p->kw) = (cs_float) img->w; + *(p->kh) = (cs_float) img->h; return OK; } static int32_t imagegetpixel (CSOUND *csound, IMGGETPIXEL * p) { - Images *pimages; Image *img; int32_t w, h, x, y; - pimages = (Images *) csound->QueryGlobalVariable(csound, - "imageOpcodes.images"); - img = pimages->images[(int32_t)(*p->kn)-1]; + img = getImage(csound, *p->kn); + if (UNLIKELY(img == NULL)) + return csound->InitError(csound, "%s", + Str("imagegetpixel: invalid image handle.\n")); w = img->w; h = img->h; @@ -470,7 +549,7 @@ static int32_t imagegetpixel (CSOUND *csound, IMGGETPIXEL * p) y = *p->ky*h; if (x >= 0 && x < w && y >= 0 && y < h ) { - int32_t pixel = (w*y+x)*3; + size_t pixel = ((size_t) w * (size_t) y + (size_t) x) * 3; *p->kr = img->imageData[pixel]/FL(255.0); *p->kg = img->imageData[pixel+1]/FL(255.0); *p->kb = img->imageData[pixel+2]/FL(255.0); @@ -486,46 +565,50 @@ static int32_t imagegetpixel (CSOUND *csound, IMGGETPIXEL * p) static int32_t imagegetpixel_a (CSOUND *csound, IMGGETPIXEL * p) { - Images *pimages; Image *img; - MYFLT *r = p->kr; - MYFLT *g = p->kg; - MYFLT *b = p->kb; + cs_float *r = p->kr; + cs_float *g = p->kg; + cs_float *b = p->kb; - MYFLT *tx = p->kx; - MYFLT *ty = p->ky; + cs_float *tx = p->kx; + cs_float *ty = p->ky; uint32_t offset = p->h.insdshead->ksmps_offset; uint32_t early = p->h.insdshead->ksmps_no_end; uint32_t i, nsmps = CS_KSMPS; - int32_t w, h, x, y, pixel; + int32_t w, h, x, y; + size_t pixel; - pimages = (Images *) csound->QueryGlobalVariable(csound, - "imageOpcodes.images"); - img = pimages->images[(int32_t)(*p->kn)-1]; + img = getImage(csound, *p->kn); + if (UNLIKELY(img == NULL)) { + memset(r, '\0', nsmps*sizeof(cs_float)); + memset(g, '\0', nsmps*sizeof(cs_float)); + memset(b, '\0', nsmps*sizeof(cs_float)); + return OK; + } w = img->w; h = img->h; if (UNLIKELY(offset)) { - memset(r, '\0', offset*sizeof(MYFLT)); - memset(g, '\0', offset*sizeof(MYFLT)); - memset(b, '\0', offset*sizeof(MYFLT)); + memset(r, '\0', offset*sizeof(cs_float)); + memset(g, '\0', offset*sizeof(cs_float)); + memset(b, '\0', offset*sizeof(cs_float)); } if (UNLIKELY(early)) { nsmps -= early; - memset(&r[nsmps], '\0', early*sizeof(MYFLT)); - memset(&g[nsmps], '\0', early*sizeof(MYFLT)); - memset(&b[nsmps], '\0', early*sizeof(MYFLT)); + memset(&r[nsmps], '\0', early*sizeof(cs_float)); + memset(&g[nsmps], '\0', early*sizeof(cs_float)); + memset(&b[nsmps], '\0', early*sizeof(cs_float)); } - for (i = 0; i < nsmps; i++) { + for (i = offset; i < nsmps; i++) { x = tx[i]*w; y = ty[i]*h; if ( x >= 0 && x < w && y >= 0 && y < h ) { - pixel = (w*y+x)*3; + pixel = ((size_t) w * (size_t) y + (size_t) x) * 3; r[i] = img->imageData[pixel]/FL(255.0); g[i] = img->imageData[pixel+1]/FL(255.0); b[i] = img->imageData[pixel+2]/FL(255.0); @@ -540,26 +623,34 @@ static int32_t imagegetpixel_a (CSOUND *csound, IMGGETPIXEL * p) return OK; } +static int32_t imagesetpixel_a_init (CSOUND *csound, IMGSETPIXEL * p) +{ + if (UNLIKELY(getImage(csound, *p->kn) == NULL)) + return csound->InitError(csound, "%s", + Str("imagesetpixel: invalid image handle.\n")); + return OK; +} + static int32_t imagesetpixel_a (CSOUND *csound, IMGSETPIXEL * p) { - Images *pimages; Image *img; - MYFLT *r = p->kr; - MYFLT *g = p->kg; - MYFLT *b = p->kb; + cs_float *r = p->kr; + cs_float *g = p->kg; + cs_float *b = p->kb; - MYFLT *tx = p->kx; - MYFLT *ty = p->ky; + cs_float *tx = p->kx; + cs_float *ty = p->ky; uint32_t offset = p->h.insdshead->ksmps_offset; uint32_t early = p->h.insdshead->ksmps_no_end; uint32_t i, nsmps = CS_KSMPS; - int32_t h,w,x, y, pixel; + int32_t h,w,x, y; + size_t pixel; - pimages = (Images *) csound->QueryGlobalVariable(csound, - "imageOpcodes.images"); - img = pimages->images[(int32_t)(*p->kn)-1]; + img = getImage(csound, *p->kn); + if (UNLIKELY(img == NULL)) + return OK; w = img->w; h = img->h; @@ -571,7 +662,7 @@ static int32_t imagesetpixel_a (CSOUND *csound, IMGSETPIXEL * p) y = ty[i]*h; if (x >= 0 && x < w && y >= 0 && y < h ) { - pixel = (w*y+x)*3; + pixel = ((size_t) w * (size_t) y + (size_t) x) * 3; img->imageData[pixel] = (unsigned char)(r[i]*255) % 256; img->imageData[pixel+1] = (unsigned char)(g[i]*255) % 256; img->imageData[pixel+2] = (unsigned char)(b[i]*255) % 256; @@ -584,13 +675,14 @@ static int32_t imagesetpixel_a (CSOUND *csound, IMGSETPIXEL * p) static int32_t imagesetpixel (CSOUND *csound, IMGSETPIXEL * p) { - Images *pimages; Image *img; - int32_t w, h, x, y, pixel; + int32_t w, h, x, y; + size_t pixel; - pimages = (Images *) csound->QueryGlobalVariable(csound, - "imageOpcodes.images"); - img = pimages->images[(int32_t)(*p->kn)-1]; + img = getImage(csound, *p->kn); + if (UNLIKELY(img == NULL)) + return csound->InitError(csound, "%s", + Str("imagesetpixel: invalid image handle.\n")); w = img->w; h = img->h; @@ -599,7 +691,7 @@ static int32_t imagesetpixel (CSOUND *csound, IMGSETPIXEL * p) y = *p->ky*h; if (x >= 0 && x < w && y >= 0 && y < h ) { - pixel = (w*y+x)*3; + pixel = ((size_t) w * (size_t) y + (size_t) x) * 3; img->imageData[pixel] = (unsigned char)((*p->kr)*255) % 256; img->imageData[pixel+1] = (unsigned char)((*p->kg)*255) % 256; img->imageData[pixel+2] = (unsigned char)((*p->kb)*255) % 256; @@ -609,51 +701,78 @@ static int32_t imagesetpixel (CSOUND *csound, IMGSETPIXEL * p) static int32_t imagesave (CSOUND *csound, IMGSAVE * p) { - Images *pimages; Image *img; - char filename[256]; - - strncpy(filename, (char*) (p->ifilnam->data), 254); filename[255]='\0'; - pimages = (Images *) csound->QueryGlobalVariable(csound, - "imageOpcodes.images"); - img = pimages->images[(int32_t)(*p->kn)-1]; + img = getImage(csound, *p->kn); + if (UNLIKELY(img == NULL)) + return csound->InitError(csound, "%s", + Str("imagesave: invalid image handle.\n")); - return __doSaveImage(img, filename, csound); + return __doSaveImage(img, (const char*) (p->ifilnam->data), csound); } -static int32_t imagefree (CSOUND *csound, IMGSAVE * p) +static int32_t imagefree (CSOUND *csound, IMGFREE * p) { Images *pimages; Image *img; + int32_t n; pimages = (Images *) csound->QueryGlobalVariable(csound, "imageOpcodes.images"); - img = pimages->images[(int32_t - )(*p->kn)-1]; - csound->Free(csound,img->imageData); - csound->Free(csound,img); + n = (int32_t) (*p->kn); + if (UNLIKELY(pimages == NULL || n < 1 || (size_t) n > pimages->cnt)) + return csound->InitError(csound, "%s", + Str("imagefree: invalid image handle.\n")); + + img = pimages->images[n-1]; + if (img != NULL) { + csound->Free(csound,img->imageData); + csound->Free(csound,img); + pimages->images[n-1] = (Image *) NULL; + } return OK; } -#define S(x) sizeof(x) - -static OENTRY image_localops[] = { - { "imageload", S(IMGLOAD), 0, 1, "i", "S", (SUBR)imageload, NULL, NULL }, - { "imagecreate",S(IMGCREATE),0, 1, "i", "ii", (SUBR)imagecreate, NULL, NULL }, - { "imagesize", S(IMGSIZE), 0, 1, "ii", "i", (SUBR)imagesize, NULL, NULL }, - { "imagegetpixel", S(IMGGETPIXEL), 0, 3, "kkk", "ixx", - (SUBR)imagegetpixel, (SUBR)imagegetpixel, }, - { "imagegetpixel", S(IMGGETPIXEL), 0, 3, "aaa", "ixx", - (SUBR)imagegetpixel, (SUBR)imagegetpixel_a }, - { "imagesetpixel", S(IMGSETPIXEL), 0, 3, "", "ikkkkk", - (SUBR)imagesetpixel, (SUBR)imagesetpixel, (SUBR)imagesetpixel_a }, - { "imagesetpixel", S(IMGSETPIXEL), 0, 3, "", "iaaaaa", - (SUBR)imagesetpixel, (SUBR)imagesetpixel_a }, - { "imagesave", S(IMGSAVE), 0, 1, "", "iS", (SUBR)imagesave, NULL, NULL }, - { "imagefree", S(IMGFREE), 0, 1, "", "i", (SUBR)imagefree, NULL, NULL }, -}; +PUBLIC int32_t csoundModuleDestroy(CSOUND *csound) +{ + Images *pimages = (Images *) csound->QueryGlobalVariable(csound, + "imageOpcodes.images"); + if (pimages != NULL && pimages->images != NULL) { + size_t i; + for (i = 0; i < pimages->cnt; ++i) { + Image *img = pimages->images[i]; + if (img != NULL) { + csound->Free(csound, img->imageData); + csound->Free(csound, img); + } + } + csound->Free(csound, pimages->images); + pimages->images = (Image **) NULL; + pimages->cnt = (size_t) 0; + } + return OK; +} +static OENTRY localops[] = { + { "imageload", sizeof(IMGLOAD), 0, "i", "S", + (SUBR)imageload, NULL, NULL, NULL, 0 }, + { "imagecreate", sizeof(IMGCREATE),0, "i", "ii", + (SUBR)imagecreate, NULL, NULL, NULL, 0 }, + { "imagesize", sizeof(IMGSIZE), 0, "ii", "i", + (SUBR)imagesize, NULL, NULL, NULL, 0 }, + { "imagegetpixel", sizeof(IMGGETPIXEL), 0, "kkk", "ikk", + (SUBR)imagegetpixel, (SUBR)imagegetpixel, NULL, NULL, 0 }, + { "imagegetpixel", sizeof(IMGGETPIXEL), 0, "aaa", "iaa", + (SUBR)imagegetpixel, (SUBR)imagegetpixel_a, NULL, NULL, 0 }, + { "imagesetpixel", sizeof(IMGSETPIXEL), 0, "", "ikkkkk", + (SUBR)imagesetpixel, (SUBR)imagesetpixel, NULL, NULL, 0 }, + { "imagesetpixel", sizeof(IMGSETPIXEL), 0, "", "iaaaaa", + (SUBR)imagesetpixel_a_init, (SUBR)imagesetpixel_a, NULL, NULL, 0 }, + { "imagesave", sizeof(IMGSAVE), 0, "", "iS", + (SUBR)imagesave, NULL, NULL, NULL, 0 }, + { "imagefree", sizeof(IMGFREE), 0, "", "i", + (SUBR)imagefree, NULL, NULL, NULL, 0 }, +}; -LINKAGE_BUILTIN(image_localops) +LINKAGE diff --git a/6.x/src/image/imageOpcodes.h b/src/image/imageOpcodes.h similarity index 84% rename from 6.x/src/image/imageOpcodes.h rename to src/image/imageOpcodes.h index a1b9febd..f6889a98 100644 --- a/6.x/src/image/imageOpcodes.h +++ b/src/image/imageOpcodes.h @@ -23,6 +23,9 @@ * */ +#ifndef IMAGE_OPCODES_H +#define IMAGE_OPCODES_H + typedef struct { unsigned char *imageData; @@ -39,42 +42,44 @@ typedef struct typedef struct { OPDS h; - MYFLT *kn; + cs_float *kn; STRINGDAT *ifilnam ; } IMGLOAD; typedef struct { OPDS h; - MYFLT *kn, *kw, *kh ; + cs_float *kn, *kw, *kh ; } IMGCREATE; typedef struct { OPDS h; - MYFLT *kw, *kh, *kn; + cs_float *kw, *kh, *kn; } IMGSIZE; typedef struct { OPDS h; - MYFLT *kr,*kg,*kb, *kn, *kx,*ky; + cs_float *kr,*kg,*kb, *kn, *kx,*ky; } IMGGETPIXEL; typedef struct { OPDS h; - MYFLT *kn,*kx,*ky,*kr,*kg,*kb; + cs_float *kn,*kx,*ky,*kr,*kg,*kb; } IMGSETPIXEL; typedef struct { OPDS h; - MYFLT *kn; + cs_float *kn; STRINGDAT *ifilnam; } IMGSAVE; typedef struct { OPDS h; - MYFLT *kn; + cs_float *kn; } IMGFREE; + +#endif /* IMAGE_OPCODES_H */ diff --git a/6.x/src/linear_algebra/CMakeLists.txt b/src/linear_algebra/CMakeLists.txt similarity index 58% rename from 6.x/src/linear_algebra/CMakeLists.txt rename to src/linear_algebra/CMakeLists.txt index 355d4369..24961c3f 100644 --- a/6.x/src/linear_algebra/CMakeLists.txt +++ b/src/linear_algebra/CMakeLists.txt @@ -1,12 +1,16 @@ option(BUILD_LINEAR_ALGEBRA_OPCODES "Build the linear algebra opcodes" ON) +# gmm++ is header-only and vendored under this directory as gmm/gmm.h. +# GMM_INCLUDE_PATH can be overridden to use a system gmm++ installation. +set(GMM_INCLUDE_PATH "${CMAKE_CURRENT_SOURCE_DIR}" CACHE PATH "Path to the gmm++ headers") + find_package(GMM) check_deps(BUILD_LINEAR_ALGEBRA_OPCODES GMM_FOUND) if(BUILD_LINEAR_ALGEBRA_OPCODES AND GMM_FOUND) make_plugin(linear_algebra linear_algebra.cpp) - target_include_directories(linear_algebra PRIVATE ${GMM_INCLUDE_DIR} ${CSOUND_INCLUDE_DIR}) - target_compile_options(linear_algebra PRIVATE "-DGMM_VERSION") + target_include_directories(linear_algebra PRIVATE ${GMM_INCLUDE_DIR} ${CSOUND_INCLUDE_DIRS}) + target_compile_definitions(linear_algebra PRIVATE BUILD_PLUGINS) if (MSVC) target_compile_options(linear_algebra PRIVATE "/D_SCL_SECURE_NO_DEPRECATE") diff --git a/src/linear_algebra/OpcodeBase.hpp b/src/linear_algebra/OpcodeBase.hpp new file mode 100644 index 00000000..49a724fc --- /dev/null +++ b/src/linear_algebra/OpcodeBase.hpp @@ -0,0 +1,499 @@ +/* + OpcodeBase.hpp: + + Copyright (C) 2005, 2009, 2017 by Istva Varga, Victor Lazzarini and + Michael Gogins + + This file is part of Csound. + + The Csound Library is free software; you can redistribute it + and/or modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + Csound is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with Csound; if not, write to the Free Software + Foundation, Inc., 31 Milk Street, #960789, Boston, MA, 02196, USA +*/ +#pragma once + +/** + * There is a conflict between the preprocessor definition "_CR" in the + * standard C++ library and in Csound. To work around this, undefine "_CR" and + * include ALL standard library include files BEFORE including ANY Csound + * include files. + */ +#if defined(_CR) +#undef _CR +#endif + +#include +#include +#include +#if !(defined(__wasi__)) +#include +#endif +#include +#include +#include "interlocks.h" +#ifdef BUILD_PLUGINS +#include "csdl.h" +#else +#include "csoundCore.h" +#endif + +/** + * Template base class, or pseudo-virtual base class, + * for writing Csound opcodes in C++. + * Derive opcode implementation classes like this: + * + * DerivedClass : public OpcodeBase + * { + * public: + * // All output fields must be declared first as cs_float *: + * cs_float *aret1; + * // All input fields must be declared next as cs_float *: + * cs_float *iarg1; + * cs_float *karg2; + * cs_float *aarg3; + * // All internal state variables must be declared after that: + * size_t state1; + * double state2; + * cs_float state3; + * // If the opcode shares data protect it by creating one or more void + * // *mutex member pointers: + * void *mutex1; + * void *mutex2; + * // and create them usind csound->Create_Mutex() in csoundModuleCreate + * // and destroy them using csound->DeleteMutex(mutex) + * // csoundModuleDestroy, and lock them using LockGuard guard(mutex). + * int32_t init(); + * int32_t kontrol(); + * int32_t audio; + * int32_t noteoff(); + * void deinit(); + * }; + */ + +namespace csound { + + static bool diagnostics_enabled = false; + + /** + * Use this to guard against data races in opcode functions. The mutex should + * be created by csound->Create_Mutex() in csoundModuleCreate(), and should + * be destroyed by csound->DeleteMutex() in csoundModuleDestroy(). + * + * If data is shared between opcode instances, the mutex should be global to + * the opcode library; if data is shared between threads for a single instance, + * the mutex should belong to the opcode instance. + */ + struct LockGuard { + LockGuard(CSOUND *csound_, void *mutex_) : csound(csound_), mutex(mutex_) + { + csound->LockMutex(mutex); + } + ~LockGuard() + { + csound->UnlockMutex(mutex); + } + CSOUND *csound; + void *mutex; + }; + + /** + * Use this to store a pointer to a global heap-allocated object, e.g. one + * used to manage state between opcode instances. + */ + + template int32_t CreateGlobalPointer(CSOUND *csound, const char *name, T *pointer) + { + T **pointer_to_pointer = 0; + int32_t result = csound->CreateGlobalVariable(csound, name, sizeof(pointer_to_pointer)); + pointer_to_pointer = static_cast(csound->QueryGlobalVariable(csound, name)); + *pointer_to_pointer = pointer; + return result; + } + + /** + * Retrieve a pointer to a global heap-allocated object, e.g. one + * used to manage state between opcode instances. + */ + template T *QueryGlobalPointer(CSOUND *csound, const char *name, T*& pointer) + { + T **pointer_to_pointer = static_cast(csound->QueryGlobalVariableNoCheck(csound, name)); + if (pointer_to_pointer != 0) { + pointer = *pointer_to_pointer; + } else { + pointer = 0; + } + return pointer; + } + + template + class OpcodeBase + { + public: + int32_t init(CSOUND *csound) + { + return NOTOK; + } + static int32_t init_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->init(csound); + } + int32_t kontrol(CSOUND *csound) + { + return NOTOK; + } + static int32_t kontrol_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->kontrol(csound); + } + int32_t audio(CSOUND *csound) + { + return NOTOK; + } + static int32_t audio_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->audio(csound); + } + /** + This is how to compute audio signals for normal opcodes: + (1) Zero all frames from 0 up to but not including Offset. + (2) Compute all frames from ksmps_offset up to but not including End. + (3) Zero all frames from End up to but not including ksmps. + Example from a C opcode: + uint32_t offset = p->h.insdshead->ksmps_offset; + uint32_t early = p->h.insdshead->ksmps_no_end; + uint32_t n, nsmps = CS_KSMPS; + if (UNLIKELY(offset)) memset(p->r, '\0', offset*sizeof(cs_float)); + if (UNLIKELY(early)) { + nsmps -= early; + memset(&p->r[nsmps], '\0', early*sizeof(cs_float)); + } + for (n = offset; n < nsmps; n++) { + input1 = CS_FLOAT2LRND(p->a[n]); + p->r[n] = (cs_float) (input1 >> input2); + } + So in C++ it should look like this (which is much easier to understand): + int32_t frameIndex = 0; + for( ; frameIndex < kperiodOffset(); ++frameIndex) { + asignal[frameIndex] = 0; + } + for( ; frameIndex < kperiodEnd(); ++frameIndex) { + asignal[frameIndex] = compute(); + } + for( ; frameIndex < ksmps(); ++frameIndex) { + asignal[frameIndex] = 0; + } + */ + uint32_t kperiodOffset() const + { + return opds.insdshead->ksmps_offset; + } + uint32_t kperiodEnd() const + { + uint32_t end = opds.insdshead->ksmps_no_end; + if (end) { + return end; + } else { + return ksmps(); + } + } + uint32_t ksmps() const + { + return opds.insdshead->ksmps; + } + uint32_t output_arg_count() + { + return (uint32_t)opds.optext->t.outArgCount; + } + uint32_t input_arg_count() + { + return (uint32_t)opds.optext->t.inArgCount; + } + void log(CSOUND *csound, const char *format,...) + { + va_list args; + va_start(args, format); + if(csound) { + csound->MessageV(csound, 0, format, args); + } else { + vfprintf(stdout, format, args); + } + va_end(args); + } + void warn(CSOUND *csound, const char *format,...) + { + if(csound) { + if(csound->GetMessageLevel(csound) & CS_WARNMSG) { + va_list args; + va_start(args, format); + csound->MessageV(csound, CSOUNDMSG_WARNING, format, args); + va_end(args); + } + } else { + va_list args; + va_start(args, format); + vfprintf(stdout, format, args); + va_end(args); + } + } + OPDS opds; + }; + + template + class OpcodeNoteoffBase + { + public: + int32_t init(CSOUND *csound) + { + IGN(csound); + return NOTOK; + } + + static int32_t deinit_(CSOUND *csound, void *opcode) + { + if (!csound->GetReinitFlag(csound) && !csound->GetTieFlag(csound)) { + return noteoff_(csound, opcode); + } + return OK; + } + + static int32_t init_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->init(csound); + } + + int32_t kontrol(CSOUND *csound) + { + IGN(csound); + return NOTOK; + } + static int32_t kontrol_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->kontrol(csound); + } + int32_t audio(CSOUND *csound) + { + IGN(csound); + return NOTOK; + } + static int32_t audio_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->audio(csound); + } + /** + This is how to compute audio signals for normal opcodes: + (1) Zero all frames from 0 up to but not including Offset. + (2) Compute all frames from ksmps_offset up to but not including End. + (3) Zero all frames from End up to but not including ksmps. + Example from a C opcode: + uint32_t offset = p->h.insdshead->ksmps_offset; + uint32_t early = p->h.insdshead->ksmps_no_end; + uint32_t n, nsmps = CS_KSMPS; + if (UNLIKELY(offset)) memset(p->r, '\0', offset*sizeof(cs_float)); + if (UNLIKELY(early)) { + nsmps -= early; + memset(&p->r[nsmps], '\0', early*sizeof(cs_float)); + } + for (n = offset; n < nsmps; n++) { + input1 = CS_FLOAT2LRND(p->a[n]); + p->r[n] = (cs_float) (input1 >> input2); + } + So in C++ it should look like this (which is much easier to understand): + int32_t frameIndex = 0; + for( ; frameIndex < kperiodOffset(); ++frameIndex) { + asignal[frameIndex] = 0; + } + for( ; frameIndex < kperiodEnd(); ++frameIndex) { + asignal[frameIndex] = compute(); + } + for( ; frameIndex < ksmps(); ++frameIndex) { + asignal[frameIndex] = 0; + } + */ + uint32_t kperiodOffset() const + { + return opds.insdshead->ksmps_offset; + } + uint32_t kperiodEnd() const + { + uint32_t end = opds.insdshead->ksmps_no_end; + if (end) { + return end; + } else { + return ksmps(); + } + } + uint32_t ksmps() const + { + return opds.insdshead->ksmps; + } + uint32_t output_arg_count() + { + return (uint32_t)opds.optext->t.outArgCount; + } + uint32_t input_arg_count() + { + return (uint32_t)opds.optext->t.inArgCount; + } + void log(CSOUND *csound, const char *format,...) + { + va_list args; + va_start(args, format); + if(csound) { + csound->MessageV(csound, 0, format, args); + } else { + vfprintf(stdout, format, args); + } + va_end(args); + } + void warn(CSOUND *csound, const char *format,...) + { + if(csound) { + if(csound->GetMessageLevel(csound) & CS_WARNMSG) { + va_list args; + va_start(args, format); + csound->MessageV(csound, CSOUNDMSG_WARNING, format, args); + va_end(args); + } + } else { + va_list args; + va_start(args, format); + vfprintf(stdout, format, args); + va_end(args); + } + } + int32_t noteoff(CSOUND *csound) + { + return OK; + } + static int32_t noteoff_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->noteoff(csound); + } + OPDS opds; + }; + + +#if (__cplusplus >= 201103L) && !(defined(__wasi__)) && !defined(BARE_METAL) + // #pragma message("Defining heap_object_manager_t.") + /** + * The memory of non-POD C++ or C objects allocated on the heap by Csound + * plugins is managed not by Csound, but by the plugin module. This class + * performs that function. + * + * As long as all memory is allocated in or after csoundModuleCreate and + * de-allocated in csoundModuleDestroy, all will be well. The danger however + * is that memory leaks will occur if allocations are not matched with + * de-allocations, or worse, that crashes will occur if any objects are + * deleted twice or de-referenced after deletion. + * + * So, the plugin should manage a collection of all heap-allocated objects, + * keyed first off the Csound instance, and second off an object handle; and + * all such objects must be accessed only by handle, from this collection. + * + * In csoundModuleDestroy, all objects for the calling instance of Csound + * should be de-allocated, and the sub-collection for that instance should + * also be de-allocated. + * + * If then there are no more sub-collections for other instances of Csound, + * the entire contents of the collection should be de-allocated. + * + * This should be done as a singleton, and in a thread-safe way. + * + * There might still be a problem if any of the heap-allocated objects, + * themselves, have global static members, perhaps created by some external + * dependency. + * + * Please note, objects O in the declaration below must not be std::shared_ptr + * objects. Deletion of objects herein must be unconditional. Rather, the + * objects passed are raw pointers to instances of class O. + */ + template class heap_object_manager_t { + private: + std::map> objects_; + std::recursive_mutex mutex; + heap_object_manager_t(){}; + ~heap_object_manager_t(){}; + public: + static heap_object_manager_t &instance() { + static heap_object_manager_t singleton; + return singleton; + } + std::map> &objects() { + std::lock_guard lock(mutex); + return objects_; + } + /** + * Returns a list of pointers to all objects allocated for this + * instance of Csound. + */ + std::vector &objects_for_csound(CSOUND *csound) { + std::lock_guard lock(mutex); + auto &objects_for_csound_ = objects()[csound]; + return objects_for_csound_; + } + /** + * Returns the handle for the object; if the object has not yet been + * stored, inserts it into the list of object pointers for this + * instance of Csound; otherwise, returns the handle of the stored + * object pointer. + */ + int32_t handle_for_object(CSOUND *csound, O *object) { + std::lock_guard lock(mutex); + auto &objects_for_csound_ = objects_for_csound(csound); + auto iterator = std::find(objects_for_csound_.begin(), objects_for_csound_.end(), object); + if (iterator == objects_for_csound_.end()) { + int32_t handle = objects_for_csound_.size(); + objects_for_csound_.push_back(object); + if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::handle_for_object %p: new object handle: %d (of %ld)\n", object, handle, objects_for_csound_.size()); + return handle; + } else { + int32_t handle = static_cast(iterator - objects_for_csound_.begin()); + if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::handle_for_object: existing object handle: %d\n", handle); + return handle; + } + } + /** + * Returns the object pointer for the handle; + * if the object pointer has not been stored by + * handle, returns a null pointer. + */ + O *object_for_handle(CSOUND *csound, int32_t handle) { + std::lock_guard lock(mutex); + auto &objects_for_csound_ = objects_for_csound(csound); + if (handle >= objects_for_csound_.size()) { + return nullptr; + } + O *object = objects_for_csound_[handle]; + if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::object_for_handle: %p %d (of %ld)\n", object, handle, objects_for_csound_.size()); + return object; + } + /** + * First destroys all objects created by the calling + * instance of Csound, then destroys the list of + * object pointers for this instance of Csound. + */ + void module_destroy(CSOUND *csound) { + std::lock_guard lock(mutex); + auto &objects_for_csound_ = objects_for_csound(csound); + for (int32_t i = 0, n = objects_for_csound_.size(); i < n; ++i) { + delete objects_for_csound_[i]; + objects_for_csound_[i] = nullptr; + } + objects_for_csound_.clear(); + objects().erase(csound); + } + }; + +#endif + +} diff --git a/src/linear_algebra/gmm/gmm.h b/src/linear_algebra/gmm/gmm.h new file mode 100644 index 00000000..b7d989a3 --- /dev/null +++ b/src/linear_algebra/gmm/gmm.h @@ -0,0 +1,54 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm.h + @author Yves Renard + @date October 13, 2002. + @brief Include common gmm files. +*/ +#ifndef GMM_H__ +#define GMM_H__ + +#include "gmm_kernel.h" +#include "gmm_dense_lu.h" +#include "gmm_dense_qr.h" + +#include "gmm_iter_solvers.h" +#include "gmm_condition_number.h" +#include "gmm_inoutput.h" + +#include "gmm_lapack_interface.h" +#include "gmm_superlu_interface.h" +#include "gmm_range_basis.h" + +#include "gmm_domain_decomp.h" + +#endif // GMM_H__ diff --git a/src/linear_algebra/gmm/gmm_MUMPS_interface.h b/src/linear_algebra/gmm/gmm_MUMPS_interface.h new file mode 100644 index 00000000..3325690e --- /dev/null +++ b/src/linear_algebra/gmm/gmm_MUMPS_interface.h @@ -0,0 +1,355 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2003-2020 Yves Renard, Julien Pommier + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_MUMPS_interface.h + @author Yves Renard , + @author Julien Pommier + @date December 8, 2005. + @brief Interface with MUMPS (LU direct solver for sparse matrices). +*/ +#if defined(GMM_USES_MUMPS) || defined(HAVE_DMUMPS_C_H) + +#ifndef GMM_MUMPS_INTERFACE_H +#define GMM_MUMPS_INTERFACE_H + +#include "gmm_kernel.h" + + +extern "C" { + +#include +#undef F_INT +#undef F_DOUBLE +#undef F_DOUBLE2 +#include +#undef F_INT +#undef F_DOUBLE +#undef F_DOUBLE2 +#include +#undef F_INT +#undef F_DOUBLE +#undef F_DOUBLE2 +#include +#undef F_INT +#undef F_DOUBLE +#undef F_DOUBLE2 + +} + +namespace gmm { + +#define ICNTL(I) icntl[(I)-1] +#define INFO(I) info[(I)-1] +#define INFOG(I) infog[(I)-1] +#define RINFOG(I) rinfog[(I)-1] + + template struct ij_sparse_matrix { + std::vector irn; + std::vector jcn; + std::vector a; + bool sym; + + template void store(const L& l, size_type i) { + typename linalg_traits::const_iterator it = vect_const_begin(l), + ite = vect_const_end(l); + for (; it != ite; ++it) { + int ir = (int)i + 1, jc = (int)it.index() + 1; + if (*it != T(0) && (!sym || ir >= jc)) + { irn.push_back(ir); jcn.push_back(jc); a.push_back(*it); } + } + } + + template void build_from(const L& l, row_major) { + for (size_type i = 0; i < mat_nrows(l); ++i) + store(mat_const_row(l, i), i); + } + + template void build_from(const L& l, col_major) { + for (size_type i = 0; i < mat_ncols(l); ++i) + store(mat_const_col(l, i), i); + irn.swap(jcn); + } + + template ij_sparse_matrix(const L& A, bool sym_) { + size_type nz = nnz(A); + sym = sym_; + irn.reserve(nz); jcn.reserve(nz); a.reserve(nz); + build_from(A, typename principal_orientation_type::sub_orientation>::potype()); + } + }; + + /* ********************************************************************* */ + /* MUMPS solve interface */ + /* ********************************************************************* */ + + template struct mumps_interf {}; + + template <> struct mumps_interf { + typedef SMUMPS_STRUC_C MUMPS_STRUC_C; + typedef float value_type; + + static void mumps_c(MUMPS_STRUC_C &id) { smumps_c(&id); } + }; + + template <> struct mumps_interf { + typedef DMUMPS_STRUC_C MUMPS_STRUC_C; + typedef double value_type; + static void mumps_c(MUMPS_STRUC_C &id) { dmumps_c(&id); } + }; + + template <> struct mumps_interf > { + typedef CMUMPS_STRUC_C MUMPS_STRUC_C; + typedef mumps_complex value_type; + static void mumps_c(MUMPS_STRUC_C &id) { cmumps_c(&id); } + }; + + template <> struct mumps_interf > { + typedef ZMUMPS_STRUC_C MUMPS_STRUC_C; + typedef mumps_double_complex value_type; + static void mumps_c(MUMPS_STRUC_C &id) { zmumps_c(&id); } + }; + + + template + static inline bool mumps_error_check(MUMPS_STRUCT &id) { + if (id.INFO(1) < 0) { + switch (id.INFO(1)) { + case -2: + GMM_ASSERT1(false, "Solve with MUMPS failed: NZ = " << id.INFO(2) + << " is out of range"); + case -6 : case -10 : + GMM_WARNING1("Solve with MUMPS failed: matrix is singular"); + return false; + case -9: + GMM_ASSERT1(false, "Solve with MUMPS failed: error " + << id.INFO(1) << ", increase ICNTL(14)"); + case -13 : + GMM_ASSERT1(false, "Solve with MUMPS failed: not enough memory"); + default : + GMM_ASSERT1(false, "Solve with MUMPS failed with error " + << id.INFO(1)); + } + } + return true; + } + + + /** MUMPS solve interface + * Works only with sparse or skyline matrices + */ + template + bool MUMPS_solve(const MAT &A, const VECTX &X_, const VECTB &B, + bool sym = false, bool distributed = false) { + VECTX &X = const_cast(X_); + + typedef typename linalg_traits::value_type T; + typedef typename mumps_interf::value_type MUMPS_T; + GMM_ASSERT2(gmm::mat_nrows(A) == gmm::mat_ncols(A), "Non-square matrix"); + + std::vector rhs(gmm::vect_size(B)); gmm::copy(B, rhs); + + ij_sparse_matrix AA(A, sym); + + const int JOB_INIT = -1; + const int JOB_END = -2; + const int USE_COMM_WORLD = -987654; + + typename mumps_interf::MUMPS_STRUC_C id; + + int rank(0); +#ifdef GMM_USES_MPI + MPI_Comm_rank(MPI_COMM_WORLD, &rank); +#endif + + id.job = JOB_INIT; + id.par = 1; + id.sym = sym ? 2 : 0; + id.comm_fortran = USE_COMM_WORLD; + mumps_interf::mumps_c(id); + + if (rank == 0 || distributed) { + id.n = int(gmm::mat_nrows(A)); + if (distributed) { + id.nz_loc = int(AA.irn.size()); + id.irn_loc = &(AA.irn[0]); + id.jcn_loc = &(AA.jcn[0]); + id.a_loc = (MUMPS_T*)(&(AA.a[0])); + } else { + id.nz = int(AA.irn.size()); + id.irn = &(AA.irn[0]); + id.jcn = &(AA.jcn[0]); + id.a = (MUMPS_T*)(&(AA.a[0])); + } + if (rank == 0) + id.rhs = (MUMPS_T*)(&(rhs[0])); + } + + id.ICNTL(1) = -1; // output stream for error messages + id.ICNTL(2) = -1; // output stream for other messages + id.ICNTL(3) = -1; // output stream for global information + id.ICNTL(4) = 0; // verbosity level + + if (distributed) + id.ICNTL(5) = 0; // assembled input matrix (default) + + id.ICNTL(14) += 80; /* small boost to the workspace size as we have encountered some problem + who did not fit in the default settings of mumps.. + by default, ICNTL(14) = 15 or 20 + */ + //cout << "ICNTL(14): " << id.ICNTL(14) << "\n"; + + if (distributed) + id.ICNTL(18) = 3; // strategy for distributed input matrix + + // id.ICNTL(22) = 1; /* enables out-of-core support */ + + id.job = 6; + mumps_interf::mumps_c(id); + bool ok = mumps_error_check(id); + + id.job = JOB_END; + mumps_interf::mumps_c(id); + +#ifdef GMM_USES_MPI + MPI_Bcast(&(rhs[0]),id.n,gmm::mpi_type(T()),0,MPI_COMM_WORLD); +#endif + + gmm::copy(rhs, X); + + return ok; + + } + + + + /** MUMPS solve interface for distributed matrices + * Works only with sparse or skyline matrices + */ + template + bool MUMPS_distributed_matrix_solve(const MAT &A, const VECTX &X_, + const VECTB &B, bool sym = false) { + return MUMPS_solve(A, X_, B, sym, true); + } + + + + template + inline T real_or_complex(std::complex a) { return a.real(); } + template + inline T real_or_complex(T &a) { return a; } + + + /** Evaluate matrix determinant with MUMPS + * Works only with sparse or skyline matrices + */ + template ::value_type> + T MUMPS_determinant(const MAT &A, int &exponent, + bool sym = false, bool distributed = false) { + exponent = 0; + typedef typename mumps_interf::value_type MUMPS_T; + typedef typename number_traits::magnitude_type R; + GMM_ASSERT2(gmm::mat_nrows(A) == gmm::mat_ncols(A), "Non-square matrix"); + + ij_sparse_matrix AA(A, sym); + + const int JOB_INIT = -1; + const int JOB_END = -2; + const int USE_COMM_WORLD = -987654; + + typename mumps_interf::MUMPS_STRUC_C id; + + int rank(0); +#ifdef GMM_USES_MPI + MPI_Comm_rank(MPI_COMM_WORLD, &rank); +#endif + + id.job = JOB_INIT; + id.par = 1; + id.sym = sym ? 2 : 0; + id.comm_fortran = USE_COMM_WORLD; + mumps_interf::mumps_c(id); + + if (rank == 0 || distributed) { + id.n = int(gmm::mat_nrows(A)); + if (distributed) { + id.nz_loc = int(AA.irn.size()); + id.irn_loc = &(AA.irn[0]); + id.jcn_loc = &(AA.jcn[0]); + id.a_loc = (MUMPS_T*)(&(AA.a[0])); + } else { + id.nz = int(AA.irn.size()); + id.irn = &(AA.irn[0]); + id.jcn = &(AA.jcn[0]); + id.a = (MUMPS_T*)(&(AA.a[0])); + } + } + + id.ICNTL(1) = -1; // output stream for error messages + id.ICNTL(2) = -1; // output stream for other messages + id.ICNTL(3) = -1; // output stream for global information + id.ICNTL(4) = 0; // verbosity level + + if (distributed) + id.ICNTL(5) = 0; // assembled input matrix (default) + +// id.ICNTL(14) += 80; // small boost to the workspace size + + if (distributed) + id.ICNTL(18) = 3; // strategy for distributed input matrix + + id.ICNTL(31) = 1; // only factorization, no solution to follow + id.ICNTL(33) = 1; // request determinant calculation + + id.job = 4; // abalysis (job=1) + factorization (job=2) + mumps_interf::mumps_c(id); + mumps_error_check(id); + + T det = real_or_complex(std::complex(id.RINFOG(12),id.RINFOG(13))); + exponent = id.INFOG(34); + + id.job = JOB_END; + mumps_interf::mumps_c(id); + + return det; + } + +#undef ICNTL +#undef INFO +#undef INFOG +#undef RINFOG + +} + + +#endif // GMM_MUMPS_INTERFACE_H + +#endif // GMM_USES_MUMPS diff --git a/src/linear_algebra/gmm/gmm_algobase.h b/src/linear_algebra/gmm/gmm_algobase.h new file mode 100644 index 00000000..b07b6d93 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_algobase.h @@ -0,0 +1,223 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2000-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/** @file gmm_algobase.h + @author Yves Renard + @date September 28, 2000. + @brief Miscelleanous algorithms on containers. +*/ + +#ifndef GMM_ALGOBASE_H__ +#define GMM_ALGOBASE_H__ +#include "gmm_std.h" +#include "gmm_except.h" +#include + +namespace gmm { + + /* ********************************************************************* */ + /* Definitition de classes de comparaison. */ + /* retournant un int. */ + /* ********************************************************************* */ + + template + struct less { + inline int operator()(const T& x, const T& y) const + { return (x < y) ? -1 : ((y < x) ? 1 : 0); } + }; + + template<> struct less { + int operator()(int x, int y) const { return x-y; } }; + template<> struct less { + int operator()(char x, char y) const { return int(x-y); } }; + template<> struct less { + int operator()(short x, short y) const { return int(x-y); } }; + template<> struct less { + int operator()(unsigned char x, unsigned char y) const + { return int(x)-int(y); } + }; + + + template + struct greater { + inline int operator()(const T& x, const T& y) const + { return (y < x) ? -1 : ((x < y) ? 1 : 0); } + }; + + template<> struct greater { + int operator()(int x, int y) const { return y-x; } }; + template<> struct greater { + int operator()(char x, char y) const { return int(y-x); } }; + template<> struct greater { + int operator()(short x, short y) const { return int(y-x); } }; + template<> struct greater { + int operator()(unsigned char x, unsigned char y) const + { return int(y)-int(x); } + }; + + template inline T my_abs(T a) { return (a < T(0)) ? T(-a) : a; } + + template + struct approx_less { + double eps; + inline int operator()(const T &x, const T &y) const + { if (my_abs(x - y) <= eps) return 0; if (x < y) return -1; return 1; } + approx_less(double e = 1E-13) { eps = e; } + }; + + template + struct approx_greater { + double eps; + inline int operator()(const T &x, const T &y) const + { if (my_abs(x - y) <= eps) return 0; if (x > y) return -1; return 1; } + approx_greater(double e = 1E-13) { eps = e; } + }; + + template + int lexicographical_compare(ITER1 b1, const ITER1 &e1, + ITER2 b2, const ITER2 &e2, const COMP &c) { + int i; + for ( ; b1 != e1 && b2 != e2; ++b1, ++b2) + if ((i = c(*b1, *b2)) != 0) return i; + if (b1 != e1) return 1; + if (b2 != e2) return -1; + return 0; + } + + template > + struct lexicographical_less { + COMP c; + int operator()(const CONT &x, const CONT &y) const { + return gmm::lexicographical_compare(x.begin(), x.end(), + y.begin(), y.end(), c); + } + lexicographical_less(const COMP &d = COMP()) { c = d; } + }; + + template > + struct lexicographical_greater { + COMP c; + int operator()(const CONT &x, const CONT &y) const { + return -gmm::lexicographical_compare(x.begin(), x.end(), + y.begin(), y.end(), c); + } + lexicographical_greater(const COMP &d = COMP()) { c = d; } + }; + + + /* ********************************************************************* */ + /* "Virtual" iterators on sequences. */ + /* The class T represent a class of sequence. */ + /* ********************************************************************* */ + + template struct sequence_iterator { + + typedef T value_type; + typedef value_type* pointer; + typedef value_type& reference; + typedef const value_type& const_reference; + typedef std::forward_iterator_tag iterator_category; + + T Un; + + sequence_iterator(T U0 = T(0)) { Un = U0; } + + sequence_iterator &operator ++() + { ++Un; return *this; } + sequence_iterator operator ++(int) + { sequence_iterator tmp = *this; (*this)++; return tmp; } + + const_reference operator *() const { return Un; } + reference operator *() { return Un; } + + bool operator ==(const sequence_iterator &i) const { return (i.Un==Un);} + bool operator !=(const sequence_iterator &i) const { return (i.Un!=Un);} + }; + + /* ********************************************************************* */ + /* generic algorithms. */ + /* ********************************************************************* */ + + template + ITER2 copy_n(ITER1 first, SIZE count, ITER2 result) { + for ( ; count > 0; --count, ++first, ++result) *result = *first; + return result; + } + + template + typename std::iterator_traits::value_type + mean_value(ITER first, const ITER &last) { + GMM_ASSERT2(first != last, "mean value of empty container"); + size_t n = 1; + typename std::iterator_traits::value_type res = *first++; + while (first != last) { res += *first; ++first; ++n; } + res /= float(n); + return res; + } + + template + typename CONT::value_type + mean_value(const CONT &c) { return mean_value(c.begin(), c.end()); } + + template /* hum ... */ + void minmax_box(typename std::iterator_traits::value_type &pmin, + typename std::iterator_traits::value_type &pmax, + ITER first, const ITER &last) { + typedef typename std::iterator_traits::value_type PT; + if (first != last) { pmin = pmax = *first; ++first; } + while (first != last) { + typename PT::const_iterator b = (*first).begin(), e = (*first).end(); + typename PT::iterator b1 = pmin.begin(), b2 = pmax.begin(); + while (b != e) + { *b1 = std::min(*b1, *b); *b2 = std::max(*b2, *b); ++b; ++b1; ++b2; } + } + } + + template struct sorted_indexes_aux { + const VEC &v; + public: + sorted_indexes_aux(const VEC& v_) : v(v_) {} + template + bool operator()(const IDX &ia, const IDX &ib) const + { return v[ia] < v[ib]; } + }; + + template + void sorted_indexes(const VEC &v, IVEC &iv) { + iv.clear(); iv.resize(v.size()); + for (size_t i=0; i < v.size(); ++i) iv[i] = i; + std::sort(iv.begin(), iv.end(), sorted_indexes_aux(v)); + } + +} + + +#endif /* GMM_ALGOBASE_H__ */ diff --git a/src/linear_algebra/gmm/gmm_arch_config.h b/src/linear_algebra/gmm/gmm_arch_config.h new file mode 100644 index 00000000..8d3ce66f --- /dev/null +++ b/src/linear_algebra/gmm/gmm_arch_config.h @@ -0,0 +1,7 @@ +# ifndef _SRC_GMM_GMM_ARCH_CONFIG_H +#define _SRC_GMM_GMM_ARCH_CONFIG_H 1 +// enable the following line for OpenMP support +// #define GMM_HAVE_OPENMP +// +/* once: _SRC_GMM_GMM_ARCH_CONFIG_H */ +#endif diff --git a/src/linear_algebra/gmm/gmm_blas.h b/src/linear_algebra/gmm/gmm_blas.h new file mode 100644 index 00000000..e41b5da1 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_blas.h @@ -0,0 +1,2287 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_blas.h + @author Yves Renard + @date October 13, 2002. + @brief Basic linear algebra functions. +*/ + +#ifndef GMM_BLAS_H__ +#define GMM_BLAS_H__ + +#include "gmm_scaled.h" +#include "gmm_transposed.h" +#include "gmm_conjugated.h" + +namespace gmm { + + /* ******************************************************************** */ + /* */ + /* Generic algorithms */ + /* */ + /* ******************************************************************** */ + + + /* ******************************************************************** */ + /* Miscellaneous */ + /* ******************************************************************** */ + + /** clear (fill with zeros) a vector or matrix. */ + template inline void clear(L &l) + { linalg_traits::do_clear(l); } + /** @cond DOXY_SHOW_ALL_FUNCTIONS + skip all these redundant definitions in doxygen documentation.. + */ + template inline void clear(const L &l) + { linalg_traits::do_clear(linalg_const_cast(l)); } + + ///@endcond + /** count the number of non-zero entries of a vector or matrix. */ template inline size_type nnz(const L& l) + { return nnz(l, typename linalg_traits::linalg_type()); } + + ///@cond DOXY_SHOW_ALL_FUNCTIONS + template inline size_type nnz(const L& l, abstract_vector) { + auto it = vect_const_begin(l), ite = vect_const_end(l); + size_type res(0); + for (; it != ite; ++it) ++res; + return res; + } + + template inline size_type nnz(const L& l, abstract_matrix) { + return nnz(l, typename principal_orientation_type::sub_orientation>::potype()); + } + + template inline size_type nnz(const L& l, row_major) { + size_type res(0); + for (size_type i = 0; i < mat_nrows(l); ++i) + res += nnz(mat_const_row(l, i)); + return res; + } + + template inline size_type nnz(const L& l, col_major) { + size_type res(0); + for (size_type i = 0; i < mat_ncols(l); ++i) + res += nnz(mat_const_col(l, i)); + return res; + } + + ///@endcond + + + /** fill a vector or matrix with x. */ + template inline + void fill(L& l, typename gmm::linalg_traits::value_type x) { + typedef typename gmm::linalg_traits::value_type T; + if (x == T(0)) gmm::clear(l); + fill(l, x, typename linalg_traits::linalg_type()); + } + + template inline + void fill(const L& l, typename gmm::linalg_traits::value_type x) { + fill(linalg_const_cast(l), x); + } + + template inline // to be optimized for dense vectors ... + void fill(L& l, typename gmm::linalg_traits::value_type x, + abstract_vector) { + for (size_type i = 0; i < vect_size(l); ++i) l[i] = x; + } + + template inline // to be optimized for dense matrices ... + void fill(L& l, typename gmm::linalg_traits::value_type x, + abstract_matrix) { + for (size_type i = 0; i < mat_nrows(l); ++i) + for (size_type j = 0; j < mat_ncols(l); ++j) + l(i,j) = x; + } + + /** fill a vector or matrix with random value (uniform [-1,1]). */ + template inline void fill_random(L& l) + { fill_random(l, typename linalg_traits::linalg_type()); } + + ///@cond DOXY_SHOW_ALL_FUNCTIONS + template inline void fill_random(const L& l) { + fill_random(linalg_const_cast(l), + typename linalg_traits::linalg_type()); + } + + template inline void fill_random(L& l, abstract_vector) { + for (size_type i = 0; i < vect_size(l); ++i) + l[i] = gmm::random(typename linalg_traits::value_type()); + } + + template inline void fill_random(L& l, abstract_matrix) { + for (size_type i = 0; i < mat_nrows(l); ++i) + for (size_type j = 0; j < mat_ncols(l); ++j) + l(i,j) = gmm::random(typename linalg_traits::value_type()); + } + + ///@endcond + /** fill a vector or matrix with random value. + @param l a vector or matrix. + @param cfill probability of a non-zero value. + */ + template inline void fill_random(L& l, double cfill) + { fill_random(l, cfill, typename linalg_traits::linalg_type()); } + ///@cond DOXY_SHOW_ALL_FUNCTIONS + + template inline void fill_random(const L& l, double cfill) { + fill_random(linalg_const_cast(l), cfill, + typename linalg_traits::linalg_type()); + } + + template inline + void fill_random(L& l, double cfill, abstract_vector) { + typedef typename linalg_traits::value_type T; + size_type ntot = std::min(vect_size(l), + size_type(double(vect_size(l))*cfill) + 1); + for (size_type nb = 0; nb < ntot;) { + size_type i = gmm::irandom(vect_size(l)); + if (l[i] == T(0)) { + l[i] = gmm::random(typename linalg_traits::value_type()); + ++nb; + } + } + } + + template inline + void fill_random(L& l, double cfill, abstract_matrix) { + fill_random(l, cfill, typename principal_orientation_type::sub_orientation>::potype()); + } + + template inline + void fill_random(L& l, double cfill, row_major) { + for (size_type i=0; i < mat_nrows(l); ++i) fill_random(mat_row(l,i),cfill); + } + + template inline + void fill_random(L& l, double cfill, col_major) { + for (size_type j=0; j < mat_ncols(l); ++j) fill_random(mat_col(l,j),cfill); + } + + /* resize a vector */ + template inline + void resize(V &v, size_type n, linalg_false) + { linalg_traits::resize(v, n); } + + template inline + void resize(V &, size_type , linalg_modifiable) + { GMM_ASSERT1(false, "You cannot resize a reference"); } + + template inline + void resize(V &, size_type , linalg_const) + { GMM_ASSERT1(false, "You cannot resize a reference"); } + + ///@endcond + /** resize a vector. */ + template inline + void resize(V &v, size_type n) { + resize(v, n, typename linalg_traits::is_reference()); + } + ///@cond DOXY_SHOW_ALL_FUNCTIONS + + /** resize a matrix **/ + template inline + void resize(M &v, size_type m, size_type n, linalg_false) { + linalg_traits::resize(v, m, n); + } + + template inline + void resize(M &, size_type, size_type, linalg_modifiable) + { GMM_ASSERT1(false, "You cannot resize a reference"); } + + template inline + void resize(M &, size_type, size_type, linalg_const) + { GMM_ASSERT1(false, "You cannot resize a reference"); } + + ///@endcond + /** resize a matrix */ + template inline + void resize(M &v, size_type m, size_type n) + { resize(v, m, n, typename linalg_traits::is_reference()); } + ///@cond + + template inline + void reshape(M &v, size_type m, size_type n, linalg_false) + { linalg_traits::reshape(v, m, n); } + + template inline + void reshape(M &, size_type, size_type, linalg_modifiable) + { GMM_ASSERT1(false, "You cannot reshape a reference"); } + + template inline + void reshape(M &, size_type, size_type, linalg_const) + { GMM_ASSERT1(false, "You cannot reshape a reference"); } + + ///@endcond + /** reshape a matrix */ + template inline + void reshape(M &v, size_type m, size_type n) + { reshape(v, m, n, typename linalg_traits::is_reference()); } + ///@cond DOXY_SHOW_ALL_FUNCTIONS + + + /* ******************************************************************** */ + /* Scalar product */ + /* ******************************************************************** */ + + ///@endcond + /** scalar product between two vectors */ + template inline + typename strongest_value_type::value_type + vect_sp(const V1 &v1, const V2 &v2) { + GMM_ASSERT2(vect_size(v1) == vect_size(v2), "dimensions mismatch, " + << vect_size(v1) << " !=" << vect_size(v2)); + return vect_sp(v1, v2, + typename linalg_traits::storage_type(), + typename linalg_traits::storage_type()); + } + + /** scalar product between two vectors, using a matrix. + @param ps the matrix of the scalar product. + @param v1 the first vector + @param v2 the second vector + */ + template inline + typename strongest_value_type3::value_type + vect_sp(const MATSP &ps, const V1 &v1, const V2 &v2) { + return vect_sp_with_mat(ps, v1, v2, + typename linalg_traits::sub_orientation()); + } + ///@cond DOXY_SHOW_ALL_FUNCTIONS + + template inline + typename strongest_value_type3::value_type + vect_sp_with_mat(const MATSP &ps, const V1 &v1, const V2 &v2, row_major) { + return vect_sp_with_matr(ps, v1, v2, + typename linalg_traits::storage_type()); + } + + template inline + typename strongest_value_type3::value_type + vect_sp_with_matr(const MATSP &ps, const V1 &v1, const V2 &v2, + abstract_sparse) { + GMM_ASSERT2(vect_size(v1) == mat_ncols(ps) && + vect_size(v2) == mat_nrows(ps), "dimensions mismatch"); + size_type nr = mat_nrows(ps); + typename linalg_traits::const_iterator + it = vect_const_begin(v2), ite = vect_const_end(v2); + typename strongest_value_type3::value_type res(0); + for (; it != ite; ++it) + res += vect_sp(mat_const_row(ps, it.index()), v1)* (*it); + return res; + } + + template inline + typename strongest_value_type3::value_type + vect_sp_with_matr(const MATSP &ps, const V1 &v1, const V2 &v2, + abstract_skyline) + { return vect_sp_with_matr(ps, v1, v2, abstract_sparse()); } + + template inline + typename strongest_value_type3::value_type + vect_sp_with_matr(const MATSP &ps, const V1 &v1, const V2 &v2, + abstract_dense) { + GMM_ASSERT2(vect_size(v1) == mat_ncols(ps) && + vect_size(v2) == mat_nrows(ps), "dimensions mismatch"); + typename linalg_traits::const_iterator + it = vect_const_begin(v2), ite = vect_const_end(v2); + typename strongest_value_type3::value_type res(0); + for (size_type i = 0; it != ite; ++i, ++it) + res += vect_sp(mat_const_row(ps, i), v1) * (*it); + return res; + } + + template inline + typename strongest_value_type3::value_type + vect_sp_with_mat(const MATSP &ps, const V1 &v1,const V2 &v2,row_and_col) + { return vect_sp_with_mat(ps, v1, v2, row_major()); } + + template inline + typename strongest_value_type3::value_type + vect_sp_with_mat(const MATSP &ps, const V1 &v1, const V2 &v2,col_major){ + return vect_sp_with_matc(ps, v1, v2, + typename linalg_traits::storage_type()); + } + + template inline + typename strongest_value_type3::value_type + vect_sp_with_matc(const MATSP &ps, const V1 &v1, const V2 &v2, + abstract_sparse) { + GMM_ASSERT2(vect_size(v1) == mat_ncols(ps) && + vect_size(v2) == mat_nrows(ps), "dimensions mismatch"); + typename linalg_traits::const_iterator + it = vect_const_begin(v1), ite = vect_const_end(v1); + typename strongest_value_type3::value_type res(0); + for (; it != ite; ++it) + res += vect_sp(mat_const_col(ps, it.index()), v2) * (*it); + return res; + } + + template inline + typename strongest_value_type3::value_type + vect_sp_with_matc(const MATSP &ps, const V1 &v1, const V2 &v2, + abstract_skyline) + { return vect_sp_with_matc(ps, v1, v2, abstract_sparse()); } + + template inline + typename strongest_value_type3::value_type + vect_sp_with_matc(const MATSP &ps, const V1 &v1, const V2 &v2, + abstract_dense) { + GMM_ASSERT2(vect_size(v1) == mat_ncols(ps) && + vect_size(v2) == mat_nrows(ps), "dimensions mismatch"); + typename linalg_traits::const_iterator + it = vect_const_begin(v1), ite = vect_const_end(v1); + typename strongest_value_type3::value_type res(0); + for (size_type i = 0; it != ite; ++i, ++it) + res += vect_sp(mat_const_col(ps, i), v2) * (*it); + return res; + } + + template inline + typename strongest_value_type3::value_type + vect_sp_with_mat(const MATSP &ps, const V1 &v1,const V2 &v2,col_and_row) + { return vect_sp_with_mat(ps, v1, v2, col_major()); } + + template inline + typename strongest_value_type3::value_type + vect_sp_with_mat(const MATSP &ps, const V1 &v1, const V2 &v2, + abstract_null_type) { + typename temporary_vector::vector_type w(mat_nrows(ps)); + GMM_WARNING2("Warning, a temporary is used in scalar product\n"); + mult(ps, v1, w); + return vect_sp(w, v2); + } + + template inline + typename strongest_numeric_type::value_type, + typename std::iterator_traits::value_type>::T + vect_sp_dense_(IT1 it, IT1 ite, IT2 it2) { + typename strongest_numeric_type::value_type, + typename std::iterator_traits::value_type>::T res(0); + for (; it != ite; ++it, ++it2) res += (*it) * (*it2); + return res; + } + + template inline + typename strongest_numeric_type::value_type, + typename linalg_traits::value_type>::T + vect_sp_sparse_(IT1 it, IT1 ite, const V &v) { + typename strongest_numeric_type::value_type, + typename linalg_traits::value_type>::T res(0); + for (; it != ite; ++it) res += (*it) * v[it.index()]; + return res; + } + + template inline + typename strongest_value_type::value_type + vect_sp(const V1 &v1, const V2 &v2, abstract_dense, abstract_dense) { + return vect_sp_dense_(vect_const_begin(v1), vect_const_end(v1), + vect_const_begin(v2)); + } + + template inline + typename strongest_value_type::value_type + vect_sp(const V1 &v1, const V2 &v2, abstract_skyline, abstract_dense) { + typename linalg_traits::const_iterator it1 = vect_const_begin(v1), + ite = vect_const_end(v1); + typename linalg_traits::const_iterator it2 = vect_const_begin(v2); + return vect_sp_dense_(it1, ite, it2 + it1.index()); + } + + template inline + typename strongest_value_type::value_type + vect_sp(const V1 &v1, const V2 &v2, abstract_dense, abstract_skyline) { + typename linalg_traits::const_iterator it1 = vect_const_begin(v2), + ite = vect_const_end(v2); + typename linalg_traits::const_iterator it2 = vect_const_begin(v1); + return vect_sp_dense_(it1, ite, it2 + it1.index()); + } + + template inline + typename strongest_value_type::value_type + vect_sp(const V1 &v1, const V2 &v2, abstract_skyline, abstract_skyline) { + typedef typename strongest_value_type::value_type T; + auto it1 = vect_const_begin(v1), ite1 = vect_const_end(v1); + auto it2 = vect_const_begin(v2), ite2 = vect_const_end(v2); + size_type n = std::min(ite1.index(), ite2.index()); + size_type l = std::max(it1.index(), it2.index()); + + if (l < n) { + size_type m = l - it1.index(), p = l - it2.index(), q = m + n - l; + return vect_sp_dense_(it1+m, it1+q, it2 + p); + } + return T(0); + } + + template inline + typename strongest_value_type::value_type + vect_sp(const V1 &v1, const V2 &v2,abstract_sparse,abstract_dense) { + return vect_sp_sparse_(vect_const_begin(v1), vect_const_end(v1), v2); + } + + template inline + typename strongest_value_type::value_type + vect_sp(const V1 &v1, const V2 &v2, abstract_sparse, abstract_skyline) { + return vect_sp_sparse_(vect_const_begin(v1), vect_const_end(v1), v2); + } + + template inline + typename strongest_value_type::value_type + vect_sp(const V1 &v1, const V2 &v2, abstract_skyline, abstract_sparse) { + return vect_sp_sparse_(vect_const_begin(v2), vect_const_end(v2), v1); + } + + template inline + typename strongest_value_type::value_type + vect_sp(const V1 &v1, const V2 &v2, abstract_dense,abstract_sparse) { + return vect_sp_sparse_(vect_const_begin(v2), vect_const_end(v2), v1); + } + + + template inline + typename strongest_value_type::value_type + vect_sp_sparse_sparse(const V1 &v1, const V2 &v2, linalg_true) { + typename linalg_traits::const_iterator it1 = vect_const_begin(v1), + ite1 = vect_const_end(v1); + typename linalg_traits::const_iterator it2 = vect_const_begin(v2), + ite2 = vect_const_end(v2); + typename strongest_value_type::value_type res(0); + + while (it1 != ite1 && it2 != ite2) { + if (it1.index() == it2.index()) + { res += (*it1) * *it2; ++it1; ++it2; } + else if (it1.index() < it2.index()) ++it1; else ++it2; + } + return res; + } + + template inline + typename strongest_value_type::value_type + vect_sp_sparse_sparse(const V1 &v1, const V2 &v2, linalg_false) { + return vect_sp_sparse_(vect_const_begin(v1), vect_const_end(v1), v2); + } + + template inline + typename strongest_value_type::value_type + vect_sp(const V1 &v1, const V2 &v2,abstract_sparse,abstract_sparse) { + return vect_sp_sparse_sparse(v1, v2, + typename linalg_and::index_sorted, + typename linalg_traits::index_sorted>::bool_type()); + } + + /* ******************************************************************** */ + /* Hermitian product */ + /* ******************************************************************** */ + ///@endcond + /** Hermitian product. */ + template + inline typename strongest_value_type::value_type + vect_hp(const V1 &v1, const V2 &v2) + { return vect_sp(v1, conjugated(v2)); } + + /** Hermitian product with a matrix. */ + template inline + typename strongest_value_type3::value_type + vect_hp(const MATSP &ps, const V1 &v1, const V2 &v2) { + return vect_sp(ps, v1, gmm::conjugated(v2)); + } + + /* ******************************************************************** */ + /* Trace of a matrix */ + /* ******************************************************************** */ + + /** Trace of a matrix */ + template + typename linalg_traits::value_type + mat_trace(const M &m) { + typedef typename linalg_traits::value_type T; + T res(0); + for (size_type i = 0; i < std::min(mat_nrows(m), mat_ncols(m)); ++i) + res += m(i,i); + return res; + } + + /* ******************************************************************** */ + /* Euclidean norm */ + /* ******************************************************************** */ + + /** squared Euclidean norm of a vector. */ + template + typename number_traits::value_type> + ::magnitude_type + vect_norm2_sqr(const V &v) { + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + auto it = vect_const_begin(v), ite = vect_const_end(v); + R res(0); + for (; it != ite; ++it) res += gmm::abs_sqr(*it); + return res; + } + + /** Euclidean norm of a vector. */ + template inline + typename number_traits::value_type> + ::magnitude_type + vect_norm2(const V &v) + { return sqrt(vect_norm2_sqr(v)); } + + + /** squared Euclidean distance between two vectors */ + template inline + typename number_traits::value_type> + ::magnitude_type + vect_dist2_sqr(const V1 &v1, const V2 &v2) { // not fully optimized + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + auto it1 = vect_const_begin(v1), ite1 = vect_const_end(v1); + auto it2 = vect_const_begin(v2), ite2 = vect_const_end(v2); + size_type k1(0), k2(0); + R res(0); + while (it1 != ite1 && it2 != ite2) { + size_type i1 = index_of_it(it1, k1, + typename linalg_traits::storage_type()); + size_type i2 = index_of_it(it2, k2, + typename linalg_traits::storage_type()); + + if (i1 == i2) { + res += gmm::abs_sqr(*it2 - *it1); ++it1; ++k1; ++it2; ++k2; + } + else if (i1 < i2) { + res += gmm::abs_sqr(*it1); ++it1; ++k1; + } + else { + res += gmm::abs_sqr(*it2); ++it2; ++k2; + } + } + while (it1 != ite1) { res += gmm::abs_sqr(*it1); ++it1; } + while (it2 != ite2) { res += gmm::abs_sqr(*it2); ++it2; } + return res; + } + + /** Euclidean distance between two vectors */ + template inline + typename number_traits::value_type> + ::magnitude_type + vect_dist2(const V1 &v1, const V2 &v2) + { return sqrt(vect_dist2_sqr(v1, v2)); } + ///@cond DOXY_SHOW_ALL_FUNCTIONS + template + typename number_traits::value_type> + ::magnitude_type + mat_euclidean_norm_sqr(const M &m, row_major) { + typename number_traits::value_type> + ::magnitude_type res(0); + for (size_type i = 0; i < mat_nrows(m); ++i) + res += vect_norm2_sqr(mat_const_row(m, i)); + return res; + } + + template + typename number_traits::value_type> + ::magnitude_type + mat_euclidean_norm_sqr(const M &m, col_major) { + typename number_traits::value_type> + ::magnitude_type res(0); + for (size_type i = 0; i < mat_ncols(m); ++i) + res += vect_norm2_sqr(mat_const_col(m, i)); + return res; + } + ///@endcond + /** squared Euclidean norm of a matrix. */ + template inline + typename number_traits::value_type> + ::magnitude_type + mat_euclidean_norm_sqr(const M &m) { + return mat_euclidean_norm_sqr(m, + typename principal_orientation_type::sub_orientation>::potype()); + } + + /** Euclidean norm of a matrix. */ + template inline + typename number_traits::value_type> + ::magnitude_type + mat_euclidean_norm(const M &m) + { return gmm::sqrt(mat_euclidean_norm_sqr(m)); } + + /* ******************************************************************** */ + /* vector norm1 */ + /* ******************************************************************** */ + /** 1-norm of a vector */ + template + typename number_traits::value_type> + ::magnitude_type + vect_norm1(const V &v) { + auto it = vect_const_begin(v), ite = vect_const_end(v); + typename number_traits::value_type> + ::magnitude_type res(0); + for (; it != ite; ++it) res += gmm::abs(*it); + return res; + } + + /** 1-distance between two vectors */ + template inline + typename number_traits::value_type> + ::magnitude_type + vect_dist1(const V1 &v1, const V2 &v2) { // not fully optimized + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + auto it1 = vect_const_begin(v1), ite1 = vect_const_end(v1); + auto it2 = vect_const_begin(v2), ite2 = vect_const_end(v2); + size_type k1(0), k2(0); + R res(0); + while (it1 != ite1 && it2 != ite2) { + size_type i1 = index_of_it(it1, k1, + typename linalg_traits::storage_type()); + size_type i2 = index_of_it(it2, k2, + typename linalg_traits::storage_type()); + + if (i1 == i2) { + res += gmm::abs(*it2 - *it1); ++it1; ++k1; ++it2; ++k2; + } + else if (i1 < i2) { + res += gmm::abs(*it1); ++it1; ++k1; + } + else { + res += gmm::abs(*it2); ++it2; ++k2; + } + } + while (it1 != ite1) { res += gmm::abs(*it1); ++it1; } + while (it2 != ite2) { res += gmm::abs(*it2); ++it2; } + return res; + } + + /* ******************************************************************** */ + /* vector Infinity norm */ + /* ******************************************************************** */ + /** Infinity norm of a vector. */ + template + typename number_traits::value_type> + ::magnitude_type + vect_norminf(const V &v) { + auto it = vect_const_begin(v), ite = vect_const_end(v); + typename number_traits::value_type> + ::magnitude_type res(0); + for (; it != ite; ++it) res = std::max(res, gmm::abs(*it)); + return res; + } + + /** Infinity distance between two vectors */ + template inline + typename number_traits::value_type> + ::magnitude_type + vect_distinf(const V1 &v1, const V2 &v2) { // not fully optimized + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + auto it1 = vect_const_begin(v1), ite1 = vect_const_end(v1); + auto it2 = vect_const_begin(v2), ite2 = vect_const_end(v2); + size_type k1(0), k2(0); + R res(0); + while (it1 != ite1 && it2 != ite2) { + size_type i1 = index_of_it(it1, k1, + typename linalg_traits::storage_type()); + size_type i2 = index_of_it(it2, k2, + typename linalg_traits::storage_type()); + + if (i1 == i2) { + res = std::max(res, gmm::abs(*it2 - *it1)); ++it1; ++k1; ++it2; ++k2; + } + else if (i1 < i2) { + res = std::max(res, gmm::abs(*it1)); ++it1; ++k1; + } + else { + res = std::max(res, gmm::abs(*it2)); ++it2; ++k2; + } + } + while (it1 != ite1) { res = std::max(res, gmm::abs(*it1)); ++it1; } + while (it2 != ite2) { res = std::max(res, gmm::abs(*it2)); ++it2; } + return res; + } + + /* ******************************************************************** */ + /* matrix norm1 */ + /* ******************************************************************** */ + ///@cond DOXY_SHOW_ALL_FUNCTIONS + template + typename number_traits::value_type> + ::magnitude_type + mat_norm1(const M &m, col_major) { + typename number_traits::value_type> + ::magnitude_type res(0); + for (size_type i = 0; i < mat_ncols(m); ++i) + res = std::max(res, vect_norm1(mat_const_col(m,i))); + return res; + } + + template + typename number_traits::value_type> + ::magnitude_type + mat_norm1(const M &m, row_major) { + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + typedef typename linalg_traits::storage_type store_type; + + std::vector aux(mat_ncols(m)); + for (size_type i = 0; i < mat_nrows(m); ++i) { + typename linalg_traits::const_sub_row_type row = mat_const_row(m, i); + auto it = vect_const_begin(row), ite = vect_const_end(row); + for (size_type k = 0; it != ite; ++it, ++k) + aux[index_of_it(it, k, store_type())] += gmm::abs(*it); + } + return vect_norminf(aux); + } + + template + typename number_traits::value_type> + ::magnitude_type + mat_norm1(const M &m, col_and_row) + { return mat_norm1(m, col_major()); } + + template + typename number_traits::value_type> + ::magnitude_type + mat_norm1(const M &m, row_and_col) + { return mat_norm1(m, col_major()); } + ///@endcond + /** 1-norm of a matrix */ + template + typename number_traits::value_type> + ::magnitude_type + mat_norm1(const M &m) { + return mat_norm1(m, typename linalg_traits::sub_orientation()); + } + + + /* ******************************************************************** */ + /* matrix Infinity norm */ + /* ******************************************************************** */ + ///@cond DOXY_SHOW_ALL_FUNCTIONS + template + typename number_traits::value_type> + ::magnitude_type + mat_norminf(const M &m, row_major) { + typename number_traits::value_type> + ::magnitude_type res(0); + for (size_type i = 0; i < mat_nrows(m); ++i) + res = std::max(res, vect_norm1(mat_const_row(m,i))); + return res; + } + + template + typename number_traits::value_type> + ::magnitude_type + mat_norminf(const M &m, col_major) { + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + typedef typename linalg_traits::storage_type store_type; + + std::vector aux(mat_nrows(m)); + for (size_type i = 0; i < mat_ncols(m); ++i) { + typename linalg_traits::const_sub_col_type col = mat_const_col(m, i); + auto it = vect_const_begin(col), ite = vect_const_end(col); + for (size_type k = 0; it != ite; ++it, ++k) + aux[index_of_it(it, k, store_type())] += gmm::abs(*it); + } + return vect_norminf(aux); + } + + template + typename number_traits::value_type> + ::magnitude_type + mat_norminf(const M &m, col_and_row) + { return mat_norminf(m, row_major()); } + + template + typename number_traits::value_type> + ::magnitude_type + mat_norminf(const M &m, row_and_col) + { return mat_norminf(m, row_major()); } + ///@endcond + /** infinity-norm of a matrix.*/ + template + typename number_traits::value_type> + ::magnitude_type + mat_norminf(const M &m) { + return mat_norminf(m, typename linalg_traits::sub_orientation()); + } + + /* ******************************************************************** */ + /* Max norm for matrices */ + /* ******************************************************************** */ + ///@cond DOXY_SHOW_ALL_FUNCTIONS + template + typename number_traits::value_type> + ::magnitude_type + mat_maxnorm(const M &m, row_major) { + typename number_traits::value_type> + ::magnitude_type res(0); + for (size_type i = 0; i < mat_nrows(m); ++i) + res = std::max(res, vect_norminf(mat_const_row(m,i))); + return res; + } + + template + typename number_traits::value_type> + ::magnitude_type + mat_maxnorm(const M &m, col_major) { + typename number_traits::value_type> + ::magnitude_type res(0); + for (size_type i = 0; i < mat_ncols(m); ++i) + res = std::max(res, vect_norminf(mat_const_col(m,i))); + return res; + } + ///@endcond + /** max-norm of a matrix. */ + template + typename number_traits::value_type> + ::magnitude_type + mat_maxnorm(const M &m) { + return mat_maxnorm(m, + typename principal_orientation_type::sub_orientation>::potype()); + } + + /* ******************************************************************** */ + /* Clean */ + /* ******************************************************************** */ + /** Clean a vector or matrix (replace near-zero entries with zeroes). */ + + template inline void clean(L &l, double threshold); + + ///@cond DOXY_SHOW_ALL_FUNCTIONS + + template + void clean(L &l, double threshold, abstract_dense, T) { + typedef typename number_traits::magnitude_type R; + auto it = vect_begin(l), ite = vect_end(l); + for (; it != ite; ++it) + if (gmm::abs(*it) < R(threshold)) *it = T(0); + } + + template + void clean(L &l, double threshold, abstract_skyline, T) + { gmm::clean(l, threshold, abstract_dense(), T()); } + + template + void clean(L &l, double threshold, abstract_sparse, T) { + typedef typename number_traits::magnitude_type R; + auto it = vect_begin(l), ite = vect_end(l); + std::vector ind; + for (; it != ite; ++it) + if (gmm::abs(*it) < R(threshold)) ind.push_back(it.index()); + for (size_type i = 0; i < ind.size(); ++i) l[ind[i]] = T(0); + } + + template + void clean(L &l, double threshold, abstract_dense, std::complex) { + auto it = vect_begin(l), ite = vect_end(l); + for (; it != ite; ++it){ + if (gmm::abs((*it).real()) < T(threshold)) + *it = std::complex(T(0), (*it).imag()); + if (gmm::abs((*it).imag()) < T(threshold)) + *it = std::complex((*it).real(), T(0)); + } + } + + template + void clean(L &l, double threshold, abstract_skyline, std::complex) + { gmm::clean(l, threshold, abstract_dense(), std::complex()); } + + template + void clean(L &l, double threshold, abstract_sparse, std::complex) { + auto it = vect_begin(l), ite = vect_end(l); + std::vector ind; + for (; it != ite; ++it) { + bool r = (gmm::abs((*it).real()) < T(threshold)); + bool i = (gmm::abs((*it).imag()) < T(threshold)); + if (r && i) ind.push_back(it.index()); + else if (r) *it = std::complex(T(0), (*it).imag()); + else if (i) *it = std::complex((*it).real(), T(0)); + } + for (size_type i = 0; i < ind.size(); ++i) + l[ind[i]] = std::complex(T(0),T(0)); + } + + template inline void clean(L &l, double threshold, + abstract_vector) { + gmm::clean(l, threshold, typename linalg_traits::storage_type(), + typename linalg_traits::value_type()); + } + + template inline void clean(const L &l, double threshold); + + template void clean(L &l, double threshold, row_major) { + for (size_type i = 0; i < mat_nrows(l); ++i) + gmm::clean(mat_row(l, i), threshold); + } + + template void clean(L &l, double threshold, col_major) { + for (size_type i = 0; i < mat_ncols(l); ++i) + gmm::clean(mat_col(l, i), threshold); + } + + template inline void clean(L &l, double threshold, + abstract_matrix) { + gmm::clean(l, threshold, + typename principal_orientation_type::sub_orientation>::potype()); + } + + template inline void clean(L &l, double threshold) + { clean(l, threshold, typename linalg_traits::linalg_type()); } + + template inline void clean(const L &l, double threshold) + { gmm::clean(linalg_const_cast(l), threshold); } + + /* ******************************************************************** */ + /* Copy */ + /* ******************************************************************** */ + ///@endcond + /** Copy vectors or matrices. + @param l1 source vector or matrix. + @param l2 destination. + */ + template inline + void copy(const L1& l1, L2& l2) { + if ((const void *)(&l1) != (const void *)(&l2)) { + if (same_origin(l1,l2)) + GMM_WARNING2("Warning : a conflict is possible in copy\n"); + + copy(l1, l2, typename linalg_traits::linalg_type(), + typename linalg_traits::linalg_type()); + } + } + ///@cond DOXY_SHOW_ALL_FUNCTIONS + + template inline + void copy(const L1& l1, const L2& l2) { copy(l1, linalg_const_cast(l2)); } + + template inline + void copy(const L1& l1, L2& l2, abstract_vector, abstract_vector) { + GMM_ASSERT2(vect_size(l1) == vect_size(l2), "dimensions mismatch, " + << vect_size(l1) << " !=" << vect_size(l2)); + copy_vect(l1, l2, typename linalg_traits::storage_type(), + typename linalg_traits::storage_type()); + } + + template inline + void copy(const L1& l1, L2& l2, abstract_matrix, abstract_matrix) { + size_type m = mat_nrows(l1), n = mat_ncols(l1); + if (!m || !n) return; + GMM_ASSERT2(n==mat_ncols(l2) && m==mat_nrows(l2), "dimensions mismatch"); + copy_mat(l1, l2, typename linalg_traits::sub_orientation(), + typename linalg_traits::sub_orientation()); + } + + template inline + void copy_vect(const V1 &v1, const V2 &v2, C1, C2) + { copy_vect(v1, const_cast(v2), C1(), C2()); } + + + template + void copy_mat_by_row(const L1& l1, L2& l2) { + size_type nbr = mat_nrows(l1); + for (size_type i = 0; i < nbr; ++i) + copy(mat_const_row(l1, i), mat_row(l2, i)); + } + + template + void copy_mat_by_col(const L1 &l1, L2 &l2) { + size_type nbc = mat_ncols(l1); + for (size_type i = 0; i < nbc; ++i) { + copy(mat_const_col(l1, i), mat_col(l2, i)); + } + } + + template inline + void copy_mat(const L1& l1, L2& l2, row_major, row_major) + { copy_mat_by_row(l1, l2); } + + template inline + void copy_mat(const L1& l1, L2& l2, row_major, row_and_col) + { copy_mat_by_row(l1, l2); } + + template inline + void copy_mat(const L1& l1, L2& l2, row_and_col, row_and_col) + { copy_mat_by_row(l1, l2); } + + template inline + void copy_mat(const L1& l1, L2& l2, row_and_col, row_major) + { copy_mat_by_row(l1, l2); } + + template inline + void copy_mat(const L1& l1, L2& l2, col_and_row, row_major) + { copy_mat_by_row(l1, l2); } + + template inline + void copy_mat(const L1& l1, L2& l2, row_major, col_and_row) + { copy_mat_by_row(l1, l2); } + + template inline + void copy_mat(const L1& l1, L2& l2, col_and_row, row_and_col) + { copy_mat_by_row(l1, l2); } + + template inline + void copy_mat(const L1& l1, L2& l2, row_and_col, col_and_row) + { copy_mat_by_row(l1, l2); } + + template inline + void copy_mat(const L1& l1, L2& l2, col_major, col_major) + { copy_mat_by_col(l1, l2); } + + template inline + void copy_mat(const L1& l1, L2& l2, col_major, col_and_row) + { copy_mat_by_col(l1, l2); } + + template inline + void copy_mat(const L1& l1, L2& l2, col_major, row_and_col) + { copy_mat_by_col(l1, l2); } + + template inline + void copy_mat(const L1& l1, L2& l2, row_and_col, col_major) + { copy_mat_by_col(l1, l2); } + + template inline + void copy_mat(const L1& l1, L2& l2, col_and_row, col_major) + { copy_mat_by_col(l1, l2); } + + template inline + void copy_mat(const L1& l1, L2& l2, col_and_row, col_and_row) + { copy_mat_by_col(l1, l2); } + + template inline + void copy_mat_mixed_rc(const L1& l1, L2& l2, size_type i) { + copy_mat_mixed_rc(l1, l2, i, typename linalg_traits::storage_type()); + } + + template + void copy_mat_mixed_rc(const L1& l1, L2& l2, size_type i, abstract_sparse) { + auto it = vect_const_begin(l1), ite = vect_const_end(l1); + for (; it != ite; ++it) + l2(i, it.index()) = *it; + } + + template + void copy_mat_mixed_rc(const L1& l1, L2& l2, size_type i, abstract_skyline) { + auto it = vect_const_begin(l1), ite = vect_const_end(l1); + for (; it != ite; ++it) + l2(i, it.index()) = *it; + } + + template + void copy_mat_mixed_rc(const L1& l1, L2& l2, size_type i, abstract_dense) { + auto it = vect_const_begin(l1), ite = vect_const_end(l1); + for (size_type j = 0; it != ite; ++it, ++j) l2(i, j) = *it; + } + + template inline + void copy_mat_mixed_cr(const L1& l1, L2& l2, size_type i) { + copy_mat_mixed_cr(l1, l2, i, typename linalg_traits::storage_type()); + } + + template + void copy_mat_mixed_cr(const L1& l1, L2& l2, size_type i, abstract_sparse) { + auto it = vect_const_begin(l1), ite = vect_const_end(l1); + for (; it != ite; ++it) l2(it.index(), i) = *it; + } + + template + void copy_mat_mixed_cr(const L1& l1, L2& l2, size_type i, abstract_skyline) { + auto it = vect_const_begin(l1), ite = vect_const_end(l1); + for (; it != ite; ++it) l2(it.index(), i) = *it; + } + + template + void copy_mat_mixed_cr(const L1& l1, L2& l2, size_type i, abstract_dense) { + auto it = vect_const_begin(l1), ite = vect_const_end(l1); + for (size_type j = 0; it != ite; ++it, ++j) l2(j, i) = *it; + } + + template + void copy_mat(const L1& l1, L2& l2, row_major, col_major) { + clear(l2); + size_type nbr = mat_nrows(l1); + for (size_type i = 0; i < nbr; ++i) + copy_mat_mixed_rc(mat_const_row(l1, i), l2, i); + } + + template + void copy_mat(const L1& l1, L2& l2, col_major, row_major) { + clear(l2); + size_type nbc = mat_ncols(l1); + for (size_type i = 0; i < nbc; ++i) + copy_mat_mixed_cr(mat_const_col(l1, i), l2, i); + } + + template inline + void copy_vect(const L1 &l1, L2 &l2, abstract_dense, abstract_dense) { + std::copy(vect_const_begin(l1), vect_const_end(l1), vect_begin(l2)); + } + + template inline // to be optimised ? + void copy_vect(const L1 &l1, L2 &l2, abstract_skyline, abstract_skyline) { + auto it1 = vect_const_begin(l1), ite1 = vect_const_end(l1); + while (it1 != ite1 && *it1 == typename linalg_traits::value_type(0)) + ++it1; + + if (ite1 - it1 > 0) { + clear(l2); + auto it2 = vect_begin(l2), ite2 = vect_end(l2); + while (*(ite1-1) == typename linalg_traits::value_type(0)) ite1--; + + if (it2 == ite2) { + l2[it1.index()] = *it1; ++it1; + l2[ite1.index()-1] = *(ite1-1); --ite1; + if (it1 < ite1) + { it2 = vect_begin(l2); ++it2; std::copy(it1, ite1, it2); } + } + else { + ptrdiff_t m = it1.index() - it2.index(); + if (m >= 0 && ite1.index() <= ite2.index()) + std::copy(it1, ite1, it2 + m); + else { + if (m < 0) l2[it1.index()] = *it1; + if (ite1.index() > ite2.index()) l2[ite1.index()-1] = *(ite1-1); + it2 = vect_begin(l2); ite2 = vect_end(l2); + m = it1.index() - it2.index(); + std::copy(it1, ite1, it2 + m); + } + } + } + } + + template + void copy_vect(const L1& l1, L2& l2, abstract_sparse, abstract_dense) { + clear(l2); + auto it = vect_const_begin(l1), ite = vect_const_end(l1); + for (; it != ite; ++it) { l2[it.index()] = *it; } + } + + template + void copy_vect(const L1& l1, L2& l2, abstract_sparse, abstract_skyline) { + clear(l2); + auto it = vect_const_begin(l1), ite = vect_const_end(l1); + for (; it != ite; ++it) l2[it.index()] = *it; + } + + template + void copy_vect(const L1& l1, L2& l2, abstract_skyline, abstract_dense) { + typedef typename linalg_traits::value_type T; + auto it = vect_const_begin(l1), ite = vect_const_end(l1); + if (it == ite) + gmm::clear(l2); + else { + auto it2 = vect_begin(l2), ite2 = vect_end(l2); + + size_type i = it.index(), j; + for (j = 0; j < i; ++j, ++it2) *it2 = T(0); + for (; it != ite; ++it, ++it2) *it2 = *it; + for (; it2 != ite2; ++it2) *it2 = T(0); + } + } + + template + void copy_vect(const L1& l1, L2& l2, abstract_sparse, abstract_sparse) { + auto it = vect_const_begin(l1), ite = vect_const_end(l1); + clear(l2); + // cout << "copy " << l1 << " of size " << vect_size(l1) << " nnz = " << nnz(l1) << endl; + for (; it != ite; ++it) { + // cout << "*it = " << *it << endl; + // cout << "it.index() = " << it.index() << endl; + if (*it != (typename linalg_traits::value_type)(0)) + l2[it.index()] = *it; + } + } + + template + void copy_vect(const L1& l1, L2& l2, abstract_dense, abstract_sparse) { + clear(l2); + auto it = vect_const_begin(l1), ite = vect_const_end(l1); + for (size_type i = 0; it != ite; ++it, ++i) + if (*it != (typename linalg_traits::value_type)(0)) + l2[i] = *it; + } + + template // to be optimised ... + void copy_vect(const L1& l1, L2& l2, abstract_dense, abstract_skyline) { + clear(l2); + auto it = vect_const_begin(l1), ite = vect_const_end(l1); + for (size_type i = 0; it != ite; ++it, ++i) + if (*it != (typename linalg_traits::value_type)(0)) + l2[i] = *it; + } + + + template + void copy_vect(const L1& l1, L2& l2, abstract_skyline, abstract_sparse) { + clear(l2); + auto it = vect_const_begin(l1), ite = vect_const_end(l1); + for (; it != ite; ++it) + if (*it != (typename linalg_traits::value_type)(0)) + l2[it.index()] = *it; + } + + /* ******************************************************************** */ + /* Matrix and vector addition */ + /* algorithms are built in order to avoid some conflicts with */ + /* repeated arguments or with overlapping part of a same object. */ + /* In the latter case, conflicts are still possible. */ + /* ******************************************************************** */ + ///@endcond + /** Add two vectors or matrices + @param l1 + @param l2 contains on output, l2+l1. + */ + template inline + void add(const L1& l1, L2& l2) { + add_spec(l1, l2, typename linalg_traits::linalg_type()); + } + ///@cond + + template inline + void add(const L1& l1, const L2& l2) { add(l1, linalg_const_cast(l2)); } + + template inline + void add_spec(const L1& l1, L2& l2, abstract_vector) { + GMM_ASSERT2(vect_size(l1) == vect_size(l2), "dimensions mismatch, " + << vect_size(l1) << " !=" << vect_size(l2)); + add(l1, l2, typename linalg_traits::storage_type(), + typename linalg_traits::storage_type()); + } + + template inline + void add_spec(const L1& l1, L2& l2, abstract_matrix) { + GMM_ASSERT2(mat_nrows(l1)==mat_nrows(l2) && mat_ncols(l1)==mat_ncols(l2), + "dimensions mismatch l1 is " << mat_nrows(l1) << "x" + << mat_ncols(l1) << " and l2 is " << mat_nrows(l2) + << "x" << mat_ncols(l2)); + add(l1, l2, typename linalg_traits::sub_orientation(), + typename linalg_traits::sub_orientation()); + } + + template + void add(const L1& l1, L2& l2, row_major, row_major) { + auto it1 = mat_row_begin(l1), ite = mat_row_end(l1); + auto it2 = mat_row_begin(l2); + for ( ; it1 != ite; ++it1, ++it2) + add(linalg_traits::row(it1), linalg_traits::row(it2)); + } + + template + void add(const L1& l1, L2& l2, col_major, col_major) { + auto it1 = mat_col_const_begin(l1), ite = mat_col_const_end(l1); + typename linalg_traits::col_iterator it2 = mat_col_begin(l2); + for ( ; it1 != ite; ++it1, ++it2) + add(linalg_traits::col(it1), linalg_traits::col(it2)); + } + + template inline + void add_mat_mixed_rc(const L1& l1, L2& l2, size_type i) { + add_mat_mixed_rc(l1, l2, i, typename linalg_traits::storage_type()); + } + + template + void add_mat_mixed_rc(const L1& l1, L2& l2, size_type i, abstract_sparse) { + auto it = vect_const_begin(l1), ite = vect_const_end(l1); + for (; it != ite; ++it) l2(i, it.index()) += *it; + } + + template + void add_mat_mixed_rc(const L1& l1, L2& l2, size_type i, abstract_skyline) { + auto it = vect_const_begin(l1), ite = vect_const_end(l1); + for (; it != ite; ++it) l2(i, it.index()) += *it; + } + + template + void add_mat_mixed_rc(const L1& l1, L2& l2, size_type i, abstract_dense) { + auto it = vect_const_begin(l1), ite = vect_const_end(l1); + for (size_type j = 0; it != ite; ++it, ++j) l2(i, j) += *it; + } + + template inline + void add_mat_mixed_cr(const L1& l1, L2& l2, size_type i) { + add_mat_mixed_cr(l1, l2, i, typename linalg_traits::storage_type()); + } + + template + void add_mat_mixed_cr(const L1& l1, L2& l2, size_type i, abstract_sparse) { + auto it = vect_const_begin(l1), ite = vect_const_end(l1); + for (; it != ite; ++it) l2(it.index(), i) += *it; + } + + template + void add_mat_mixed_cr(const L1& l1, L2& l2, size_type i, abstract_skyline) { + auto it = vect_const_begin(l1), ite = vect_const_end(l1); + for (; it != ite; ++it) l2(it.index(), i) += *it; + } + + template + void add_mat_mixed_cr(const L1& l1, L2& l2, size_type i, abstract_dense) { + auto it = vect_const_begin(l1), ite = vect_const_end(l1); + for (size_type j = 0; it != ite; ++it, ++j) l2(j, i) += *it; + } + + template + void add(const L1& l1, L2& l2, row_major, col_major) { + size_type nbr = mat_nrows(l1); + for (size_type i = 0; i < nbr; ++i) + add_mat_mixed_rc(mat_const_row(l1, i), l2, i); + } + + template + void add(const L1& l1, L2& l2, col_major, row_major) { + size_type nbc = mat_ncols(l1); + for (size_type i = 0; i < nbc; ++i) + add_mat_mixed_cr(mat_const_col(l1, i), l2, i); + } + + template inline + void add(const L1& l1, L2& l2, row_and_col, row_major) + { add(l1, l2, row_major(), row_major()); } + + template inline + void add(const L1& l1, L2& l2, row_and_col, row_and_col) + { add(l1, l2, row_major(), row_major()); } + + template inline + void add(const L1& l1, L2& l2, row_and_col, col_and_row) + { add(l1, l2, row_major(), row_major()); } + + template inline + void add(const L1& l1, L2& l2, col_and_row, row_and_col) + { add(l1, l2, row_major(), row_major()); } + + template inline + void add(const L1& l1, L2& l2, row_major, row_and_col) + { add(l1, l2, row_major(), row_major()); } + + template inline + void add(const L1& l1, L2& l2, col_and_row, row_major) + { add(l1, l2, row_major(), row_major()); } + + template inline + void add(const L1& l1, L2& l2, row_major, col_and_row) + { add(l1, l2, row_major(), row_major()); } + + template inline + void add(const L1& l1, L2& l2, row_and_col, col_major) + { add(l1, l2, col_major(), col_major()); } + + template inline + void add(const L1& l1, L2& l2, col_major, row_and_col) + { add(l1, l2, col_major(), col_major()); } + + template inline + void add(const L1& l1, L2& l2, col_and_row, col_major) + { add(l1, l2, col_major(), col_major()); } + + template inline + void add(const L1& l1, L2& l2, col_and_row, col_and_row) + { add(l1, l2, col_major(), col_major()); } + + template inline + void add(const L1& l1, L2& l2, col_major, col_and_row) + { add(l1, l2, col_major(), col_major()); } + + ///@endcond + /** Addition of two vectors/matrices + @param l1 + @param l2 + @param l3 contains l1+l2 on output + */ + template inline + void add(const L1& l1, const L2& l2, L3& l3) { + add_spec(l1, l2, l3, typename linalg_traits::linalg_type()); + } + ///@cond DOXY_SHOW_ALL_FUNCTIONS + + template inline + void add(const L1& l1, const L2& l2, const L3& l3) + { add(l1, l2, linalg_const_cast(l3)); } + + template inline + void add_spec(const L1& l1, const L2& l2, L3& l3, abstract_matrix) + { copy(l2, l3); add(l1, l3); } + + template inline + void add_spec(const L1& l1, const L2& l2, L3& l3, abstract_vector) { + GMM_ASSERT2(vect_size(l1) == vect_size(l2), "dimensions mismatch, " + << vect_size(l1) << " !=" << vect_size(l2)); + GMM_ASSERT2(vect_size(l1) == vect_size(l3), "dimensions mismatch, " + << vect_size(l1) << " !=" << vect_size(l3)); + if ((const void *)(&l1) == (const void *)(&l3)) + add(l2, l3); + else if ((const void *)(&l2) == (const void *)(&l3)) + add(l1, l3); + else + add(l1, l2, l3, typename linalg_traits::storage_type(), + typename linalg_traits::storage_type(), + typename linalg_traits::storage_type()); + } + + template + void add_full_(IT1 it1, IT2 it2, IT3 it3, IT3 ite) { + for (; it3 != ite; ++it3, ++it2, ++it1) *it3 = *it1 + *it2; + } + + template + void add_almost_full_(IT1 it1, IT1 ite1, IT2 it2, IT3 it3, IT3 ite3) { + IT3 it = it3; + for (; it != ite3; ++it, ++it2) *it = *it2; + for (; it1 != ite1; ++it1) + *(it3 + it1.index()) += *it1; + } + + template + void add_to_full_(IT1 it1, IT1 ite1, IT2 it2, IT2 ite2, + IT3 it3, IT3 ite3) { + typedef typename std::iterator_traits::value_type T; + IT3 it = it3; + for (; it != ite3; ++it) *it = T(0); + for (; it1 != ite1; ++it1) *(it3 + it1.index()) = *it1; + for (; it2 != ite2; ++it2) *(it3 + it2.index()) += *it2; + } + + template inline + void add(const L1& l1, const L2& l2, L3& l3, + abstract_dense, abstract_dense, abstract_dense) { + add_full_(vect_const_begin(l1), vect_const_begin(l2), + vect_begin(l3), vect_end(l3)); + } + + // generic function for add(v1, v2, v3). + // Need to be specialized to optimize particular additions. + template + inline void add(const L1& l1, const L2& l2, L3& l3, ST1, ST2, ST3) + { copy(l2, l3); add(l1, l3, ST1(), ST3()); } + + template inline + void add(const L1& l1, const L2& l2, L3& l3, + abstract_sparse, abstract_dense, abstract_dense) { + add_almost_full_(vect_const_begin(l1), vect_const_end(l1), + vect_const_begin(l2), vect_begin(l3), vect_end(l3)); + } + + template inline + void add(const L1& l1, const L2& l2, L3& l3, + abstract_dense, abstract_sparse, abstract_dense) + { add(l2, l1, l3, abstract_sparse(), abstract_dense(), abstract_dense()); } + + template inline + void add(const L1& l1, const L2& l2, L3& l3, + abstract_sparse, abstract_sparse, abstract_dense) { + add_to_full_(vect_const_begin(l1), vect_const_end(l1), + vect_const_begin(l2), vect_const_end(l2), + vect_begin(l3), vect_end(l3)); + } + + + template + void add_spspsp(const L1& l1, const L2& l2, L3& l3, linalg_true) { + auto it1 = vect_const_begin(l1), ite1 = vect_const_end(l1); + auto it2 = vect_const_begin(l2), ite2 = vect_const_end(l2); + clear(l3); + while (it1 != ite1 && it2 != ite2) { + ptrdiff_t d = it1.index() - it2.index(); + if (d < 0) + { l3[it1.index()] += *it1; ++it1; } + else if (d > 0) + { l3[it2.index()] += *it2; ++it2; } + else + { l3[it1.index()] = *it1 + *it2; ++it1; ++it2; } + } + for (; it1 != ite1; ++it1) l3[it1.index()] += *it1; + for (; it2 != ite2; ++it2) l3[it2.index()] += *it2; + } + + template + void add_spspsp(const L1& l1, const L2& l2, L3& l3, linalg_false) + { copy(l2, l3); add(l2, l3); } + + template + void add(const L1& l1, const L2& l2, L3& l3, + abstract_sparse, abstract_sparse, abstract_sparse) { + add_spspsp(l1, l2, l3, typename linalg_and::index_sorted, + typename linalg_traits::index_sorted>::bool_type()); + } + + template + void add(const L1& l1, L2& l2, abstract_dense, abstract_dense) { + auto it1 = vect_const_begin(l1); + auto it2 = vect_begin(l2), ite = vect_end(l2); + for (; it2 != ite; ++it2, ++it1) *it2 += *it1; + } + + template + void add(const L1& l1, L2& l2, abstract_dense, abstract_skyline) { + typedef typename linalg_traits::value_type T; + + auto it1 = vect_const_begin(l1), ite1 = vect_const_end(l1); + size_type i1 = 0, ie1 = vect_size(l1); + while (it1 != ite1 && *it1 == T(0)) { ++it1; ++i1; } + if (it1 != ite1) { + auto it2 = vect_begin(l2), ite2 = vect_end(l2); + while (ie1 && *(ite1-1) == T(0)) { ite1--; --ie1; } + + if (it2 == ite2 || i1 < it2.index()) { + l2[i1] = *it1; ++i1; ++it1; + if (it1 == ite1) return; + it2 = vect_begin(l2); ite2 = vect_end(l2); + } + if (ie1 > ite2.index()) { + --ite1; l2[ie1 - 1] = *ite1; + it2 = vect_begin(l2); + } + it2 += i1 - it2.index(); + for (; it1 != ite1; ++it1, ++it2) { *it2 += *it1; } + } + } + + + template + void add(const L1& l1, L2& l2, abstract_skyline, abstract_dense) { + auto it1 = vect_const_begin(l1), ite1 = vect_const_end(l1); + if (it1 != ite1) { + auto it2 = vect_begin(l2); + it2 += it1.index(); + for (; it1 != ite1; ++it2, ++it1) *it2 += *it1; + } + } + + + template + void add(const L1& l1, L2& l2, abstract_sparse, abstract_dense) { + auto it1 = vect_const_begin(l1), ite1 = vect_const_end(l1); + for (; it1 != ite1; ++it1) l2[it1.index()] += *it1; + } + + template + void add(const L1& l1, L2& l2, abstract_sparse, abstract_sparse) { + auto it1 = vect_const_begin(l1), ite1 = vect_const_end(l1); + for (; it1 != ite1; ++it1) l2[it1.index()] += *it1; + } + + template + void add(const L1& l1, L2& l2, abstract_sparse, abstract_skyline) { + auto it1 = vect_const_begin(l1), ite1 = vect_const_end(l1); + for (; it1 != ite1; ++it1) l2[it1.index()] += *it1; + } + + + template + void add(const L1& l1, L2& l2, abstract_skyline, abstract_sparse) { + auto it1 = vect_const_begin(l1), ite1 = vect_const_end(l1); + for (; it1 != ite1; ++it1) + if (*it1 != typename linalg_traits::value_type(0)) + l2[it1.index()] += *it1; + } + + template + void add(const L1& l1, L2& l2, abstract_skyline, abstract_skyline) { + typedef typename linalg_traits::value_type T1; + typedef typename linalg_traits::value_type T2; + + auto it1 = vect_const_begin(l1), ite1 = vect_const_end(l1); + + while (it1 != ite1 && *it1 == T1(0)) ++it1; + if (ite1 != it1) { + auto it2 = vect_begin(l2), ite2 = vect_end(l2); + while (*(ite1-1) == T1(0)) ite1--; + if (it2 == ite2 || it1.index() < it2.index()) { + l2[it1.index()] = T2(0); + it2 = vect_begin(l2); ite2 = vect_end(l2); + } + if (ite1.index() > ite2.index()) { + l2[ite1.index() - 1] = T2(0); + it2 = vect_begin(l2); + } + it2 += it1.index() - it2.index(); + for (; it1 != ite1; ++it1, ++it2) *it2 += *it1; + } + } + + template + void add(const L1& l1, L2& l2, abstract_dense, abstract_sparse) { + auto it1 = vect_const_begin(l1), ite1 = vect_const_end(l1); + for (size_type i = 0; it1 != ite1; ++it1, ++i) + if (*it1 != typename linalg_traits::value_type(0)) l2[i] += *it1; + } + + /* ******************************************************************** */ + /* Matrix-vector mult */ + /* ******************************************************************** */ + ///@endcond + /** matrix-vector or matrix-matrix product. + @param l1 a matrix. + @param l2 a vector or matrix. + @param l3 the product l1*l2. + */ + template inline + void mult(const L1& l1, const L2& l2, L3& l3) { + mult_dispatch(l1, l2, l3, typename linalg_traits::linalg_type()); + } + ///@cond DOXY_SHOW_ALL_FUNCTIONS + + template inline + void mult(const L1& l1, const L2& l2, const L3& l3) + { mult(l1, l2, linalg_const_cast(l3)); } + + template inline + void mult_dispatch(const L1& l1, const L2& l2, L3& l3, abstract_vector) { + size_type m = mat_nrows(l1), n = mat_ncols(l1); + if (!m || !n) { gmm::clear(l3); return; } + GMM_ASSERT2(n==vect_size(l2) && m==vect_size(l3), "dimensions mismatch"); + if (!same_origin(l2, l3)) + mult_spec(l1, l2, l3, typename principal_orientation_type::sub_orientation>::potype()); + else { + GMM_WARNING2("Warning, A temporary is used for mult\n"); + typename temporary_vector::vector_type temp(vect_size(l3)); + mult_spec(l1, l2, temp, typename principal_orientation_type::sub_orientation>::potype()); + copy(temp, l3); + } + } + + template + void mult_by_row(const L1& l1, const L2& l2, L3& l3, abstract_sparse) { + typedef typename linalg_traits::value_type T; + clear(l3); + size_type nr = mat_nrows(l1); + for (size_type i = 0; i < nr; ++i) { + T aux = vect_sp(mat_const_row(l1, i), l2); + if (aux != T(0)) l3[i] = aux; + } + } + + template + void mult_by_row(const L1& l1, const L2& l2, L3& l3, abstract_skyline) { + typedef typename linalg_traits::value_type T; + clear(l3); + size_type nr = mat_nrows(l1); + for (size_type i = 0; i < nr; ++i) { + T aux = vect_sp(mat_const_row(l1, i), l2); + if (aux != T(0)) l3[i] = aux; + } + } + + template + void mult_by_row(const L1& l1, const L2& l2, L3& l3, abstract_dense) { + typename linalg_traits::iterator it=vect_begin(l3), ite=vect_end(l3); + auto itr = mat_row_const_begin(l1); + for (; it != ite; ++it, ++itr) + *it = vect_sp(linalg_traits::row(itr), l2, + typename linalg_traits::storage_type(), + typename linalg_traits::storage_type()); + } + + template + void mult_by_col(const L1& l1, const L2& l2, L3& l3, abstract_dense) { + clear(l3); + size_type nc = mat_ncols(l1); + for (size_type i = 0; i < nc; ++i) + add(scaled(mat_const_col(l1, i), l2[i]), l3); + } + + template + void mult_by_col(const L1& l1, const L2& l2, L3& l3, abstract_sparse) { + typedef typename linalg_traits::value_type T; + clear(l3); + auto it = vect_const_begin(l2), ite = vect_const_end(l2); + for (; it != ite; ++it) + if (*it != T(0)) add(scaled(mat_const_col(l1, it.index()), *it), l3); + } + + template + void mult_by_col(const L1& l1, const L2& l2, L3& l3, abstract_skyline) { + typedef typename linalg_traits::value_type T; + clear(l3); + auto it = vect_const_begin(l2), ite = vect_const_end(l2); + for (; it != ite; ++it) + if (*it != T(0)) add(scaled(mat_const_col(l1, it.index()), *it), l3); + } + + template inline + void mult_spec(const L1& l1, const L2& l2, L3& l3, row_major) + { mult_by_row(l1, l2, l3, typename linalg_traits::storage_type()); } + + template inline + void mult_spec(const L1& l1, const L2& l2, L3& l3, col_major) + { mult_by_col(l1, l2, l3, typename linalg_traits::storage_type()); } + + template inline + void mult_spec(const L1& l1, const L2& l2, L3& l3, abstract_null_type) + { mult_ind(l1, l2, l3, typename linalg_traits::storage_type()); } + + template + void mult_ind(const L1& l1, const L2& l2, L3& l3, abstract_indirect) { + GMM_ASSERT1(false, "gmm::mult(m, ., .) undefined for this kind of matrix"); + } + + template inline + void mult(const L1& l1, const L2& l2, const L3& l3, L4& l4) { + size_type m = mat_nrows(l1), n = mat_ncols(l1); + copy(l3, l4); + if (!m || !n) { gmm::copy(l3, l4); return; } + GMM_ASSERT2(n==vect_size(l2) && m==vect_size(l4), "dimensions mismatch"); + if (!same_origin(l2, l4)) { + mult_add_spec(l1, l2, l4, typename principal_orientation_type::sub_orientation>::potype()); + } + else { + GMM_WARNING2("Warning, A temporary is used for mult\n"); + typename temporary_vector::vector_type temp(vect_size(l2)); + copy(l2, temp); + mult_add_spec(l1,temp, l4, typename principal_orientation_type::sub_orientation>::potype()); + } + } + + template inline + void mult(const L1& l1, const L2& l2, const L3& l3, const L4& l4) + { mult(l1, l2, l3, linalg_const_cast(l4)); } + + ///@endcond + /** Multiply-accumulate. l3 += l1*l2; */ + template inline + void mult_add(const L1& l1, const L2& l2, L3& l3) { + size_type m = mat_nrows(l1), n = mat_ncols(l1); + if (!m || !n) return; + GMM_ASSERT2(n==vect_size(l2) && m==vect_size(l3), "dimensions mismatch"); + if (!same_origin(l2, l3)) { + mult_add_spec(l1, l2, l3, typename principal_orientation_type::sub_orientation>::potype()); + } + else { + GMM_WARNING2("Warning, A temporary is used for mult\n"); + typename temporary_vector::vector_type temp(vect_size(l2)); + copy(l2, temp); + mult_add_spec(l1,temp, l3, typename principal_orientation_type::sub_orientation>::potype()); + } + } + ///@cond DOXY_SHOW_ALL_FUNCTIONS + + template inline + void mult_add(const L1& l1, const L2& l2, const L3& l3) + { mult_add(l1, l2, linalg_const_cast(l3)); } + + template + void mult_add_by_row(const L1& l1, const L2& l2, L3& l3, abstract_sparse) { + typedef typename linalg_traits::value_type T; + size_type nr = mat_nrows(l1); + for (size_type i = 0; i < nr; ++i) { + T aux = vect_sp(mat_const_row(l1, i), l2); + if (aux != T(0)) l3[i] += aux; + } + } + + template + void mult_add_by_row(const L1& l1, const L2& l2, L3& l3, abstract_skyline) { + typedef typename linalg_traits::value_type T; + size_type nr = mat_nrows(l1); + for (size_type i = 0; i < nr; ++i) { + T aux = vect_sp(mat_const_row(l1, i), l2); + if (aux != T(0)) l3[i] += aux; + } + } + + template + void mult_add_by_row(const L1& l1, const L2& l2, L3& l3, abstract_dense) { + auto it=vect_begin(l3), ite=vect_end(l3); + auto itr = mat_row_const_begin(l1); + for (; it != ite; ++it, ++itr) + *it += vect_sp(linalg_traits::row(itr), l2); + } + + template + void mult_add_by_col(const L1& l1, const L2& l2, L3& l3, abstract_dense) { + size_type nc = mat_ncols(l1); + for (size_type i = 0; i < nc; ++i) + add(scaled(mat_const_col(l1, i), l2[i]), l3); + } + + template + void mult_add_by_col(const L1& l1, const L2& l2, L3& l3, abstract_sparse) { + auto it = vect_const_begin(l2), ite = vect_const_end(l2); + for (; it != ite; ++it) + if (*it != typename linalg_traits::value_type(0)) + add(scaled(mat_const_col(l1, it.index()), *it), l3); + } + + template + void mult_add_by_col(const L1& l1, const L2& l2, L3& l3, abstract_skyline) { + auto it = vect_const_begin(l2), ite = vect_const_end(l2); + for (; it != ite; ++it) + if (*it != typename linalg_traits::value_type(0)) + add(scaled(mat_const_col(l1, it.index()), *it), l3); + } + + template inline + void mult_add_spec(const L1& l1, const L2& l2, L3& l3, row_major) + { mult_add_by_row(l1, l2, l3, typename linalg_traits::storage_type()); } + + template inline + void mult_add_spec(const L1& l1, const L2& l2, L3& l3, col_major) + { mult_add_by_col(l1, l2, l3, typename linalg_traits::storage_type()); } + + template inline + void mult_add_spec(const L1& l1, const L2& l2, L3& l3, abstract_null_type) + { mult_ind(l1, l2, l3, typename linalg_traits::storage_type()); } + + template + void transposed_mult(const L1& l1, const L2& l2, const L3& l3) + { mult(gmm::transposed(l1), l2, l3); } + + + /* ******************************************************************** */ + /* Matrix-matrix mult */ + /* ******************************************************************** */ + + + struct g_mult {}; // generic mult, less optimized + struct c_mult {}; // col x col -> col mult + struct r_mult {}; // row x row -> row mult + struct rcmult {}; // row x col -> col mult + struct crmult {}; // col x row -> row mult + + + template struct mult_t; + #define DEFMU__ template<> struct mult_t + DEFMU__ { typedef r_mult t; }; + DEFMU__ { typedef g_mult t; }; + DEFMU__ { typedef r_mult t; }; + DEFMU__ { typedef r_mult t; }; + DEFMU__ { typedef rcmult t; }; + DEFMU__ { typedef rcmult t; }; + DEFMU__ { typedef rcmult t; }; + DEFMU__ { typedef rcmult t; }; + DEFMU__ { typedef r_mult t; }; + DEFMU__ { typedef rcmult t; }; + DEFMU__ { typedef rcmult t; }; + DEFMU__ { typedef rcmult t; }; + DEFMU__ { typedef r_mult t; }; + DEFMU__ { typedef rcmult t; }; + DEFMU__ { typedef r_mult t; }; + DEFMU__ { typedef r_mult t; }; + DEFMU__ { typedef crmult t; }; + DEFMU__ { typedef g_mult t; }; + DEFMU__ { typedef crmult t; }; + DEFMU__ { typedef crmult t; }; + DEFMU__ { typedef g_mult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef crmult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef crmult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef r_mult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef r_mult t; }; + DEFMU__ { typedef r_mult t; }; + DEFMU__ { typedef rcmult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef r_mult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef r_mult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef r_mult t; }; + DEFMU__ { typedef r_mult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef r_mult t; }; + DEFMU__ { typedef r_mult t; }; + DEFMU__ { typedef rcmult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef rcmult t; }; + DEFMU__ { typedef rcmult t; }; + DEFMU__ { typedef rcmult t; }; + DEFMU__ { typedef rcmult t; }; + DEFMU__ { typedef r_mult t; }; + DEFMU__ { typedef c_mult t; }; + DEFMU__ { typedef r_mult t; }; + DEFMU__ { typedef r_mult t; }; + + template + void mult_dispatch(const L1& l1, const L2& l2, L3& l3, abstract_matrix) { + typedef typename temporary_matrix::matrix_type temp_mat_type; + size_type n = mat_ncols(l1); + if (n == 0) { gmm::clear(l3); return; } + GMM_ASSERT2(n == mat_nrows(l2) && mat_nrows(l1) == mat_nrows(l3) && + mat_ncols(l2) == mat_ncols(l3), "dimensions mismatch"); + + if (same_origin(l2, l3) || same_origin(l1, l3)) { + GMM_WARNING2("A temporary is used for mult"); + temp_mat_type temp(mat_nrows(l3), mat_ncols(l3)); + mult_spec(l1, l2, temp, typename mult_t< + typename linalg_traits::sub_orientation, + typename linalg_traits::sub_orientation, + typename linalg_traits::sub_orientation>::t()); + copy(temp, l3); + } + else + mult_spec(l1, l2, l3, typename mult_t< + typename linalg_traits::sub_orientation, + typename linalg_traits::sub_orientation, + typename linalg_traits::sub_orientation>::t()); + } + + // Completely generic but inefficient + + template + void mult_spec(const L1& l1, const L2& l2, L3& l3, g_mult) { + typedef typename linalg_traits::value_type T; + GMM_WARNING2("Inefficient generic matrix-matrix mult is used"); + for (size_type i = 0; i < mat_nrows(l3) ; ++i) + for (size_type j = 0; j < mat_ncols(l3) ; ++j) { + T a(0); + for (size_type k = 0; k < mat_nrows(l2) ; ++k) a += l1(i, k)*l2(k, j); + l3(i, j) = a; + } + } + + // row x col matrix-matrix mult + + template + void mult_row_col_with_temp(const L1& l1, const L2& l2, L3& l3, col_major) { + typedef typename temporary_col_matrix::matrix_type temp_col_mat; + temp_col_mat temp(mat_nrows(l1), mat_ncols(l1)); + copy(l1, temp); + mult(temp, l2, l3); + } + + template + void mult_row_col_with_temp(const L1& l1, const L2& l2, L3& l3, row_major) { + typedef typename temporary_row_matrix::matrix_type temp_row_mat; + temp_row_mat temp(mat_nrows(l2), mat_ncols(l2)); + copy(l2, temp); + mult(l1, temp, l3); + } + + template + void mult_spec(const L1& l1, const L2& l2, L3& l3, rcmult) { + if (is_sparse(l1) && is_sparse(l2)) { + GMM_WARNING3("Inefficient row matrix - col matrix mult for " + "sparse matrices, using temporary"); + mult_row_col_with_temp(l1, l2, l3, + typename principal_orientation_type::sub_orientation>::potype()); + } + else { + auto it2b = linalg_traits::col_begin(l2), it2 = it2b, + ite = linalg_traits::col_end(l2); + size_type i,j, k = mat_nrows(l1); + + for (i = 0; i < k; ++i) { + typename linalg_traits::const_sub_row_type r1=mat_const_row(l1, i); + for (it2 = it2b, j = 0; it2 != ite; ++it2, ++j) + l3(i,j) = vect_sp(r1, linalg_traits::col(it2)); + } + } + } + + // row - row matrix-matrix mult + + template inline + void mult_spec(const L1& l1, const L2& l2, L3& l3, r_mult) { + mult_spec(l1, l2, l3,r_mult(),typename linalg_traits::storage_type()); + } + + template + void mult_spec(const L1& l1, const L2& l2, L3& l3, r_mult, abstract_dense) { + // optimizable + clear(l3); + size_type nn = mat_nrows(l3), mm = mat_nrows(l2); + for (size_type i = 0; i < nn; ++i) { + for (size_type j = 0; j < mm; ++j) { + add(scaled(mat_const_row(l2, j), l1(i, j)), mat_row(l3, i)); + } + } + } + + template + void mult_spec(const L1& l1, const L2& l2, L3& l3, r_mult, abstract_sparse) { + // optimizable + clear(l3); + size_type nn = mat_nrows(l3); + for (size_type i = 0; i < nn; ++i) { + typename linalg_traits::const_sub_row_type rl1=mat_const_row(l1, i); + auto it = vect_const_begin(rl1), ite = vect_const_end(rl1); + for (; it != ite; ++it) + add(scaled(mat_const_row(l2, it.index()), *it), mat_row(l3, i)); + } + } + + template + void mult_spec(const L1& l1, const L2& l2, L3& l3, r_mult, abstract_skyline) + { mult_spec(l1, l2, l3, r_mult(), abstract_sparse()); } + + // col - col matrix-matrix mult + + template inline + void mult_spec(const L1& l1, const L2& l2, L3& l3, c_mult) { + mult_spec(l1, l2,l3,c_mult(),typename linalg_traits::storage_type(), + typename linalg_traits::sub_orientation()); + } + + + template + void mult_spec(const L1& l1, const L2& l2, L3& l3, c_mult, + abstract_dense, ORIEN) { + typedef typename linalg_traits::value_type T; + size_type nn = mat_ncols(l3), mm = mat_ncols(l1); + + for (size_type i = 0; i < nn; ++i) { + clear(mat_col(l3, i)); + for (size_type j = 0; j < mm; ++j) { + T b = l2(j, i); + if (b != T(0)) add(scaled(mat_const_col(l1, j), b), mat_col(l3, i)); + } + } + } + + template + void mult_spec(const L1& l1, const L2& l2, L3& l3, c_mult, + abstract_sparse, ORIEN) { + // optimizable + clear(l3); + size_type nn = mat_ncols(l3); + for (size_type i = 0; i < nn; ++i) { + typename linalg_traits::const_sub_col_type rc2 = mat_const_col(l2, i); + auto it = vect_const_begin(rc2), ite = vect_const_end(rc2); + for (; it != ite; ++it) + add(scaled(mat_const_col(l1, it.index()), *it), mat_col(l3, i)); + } + } + + template + void mult_spec(const L1& l1, const L2& l2, L3& l3, c_mult, + abstract_sparse, row_major) { + typedef typename linalg_traits::value_type T; + GMM_WARNING3("Inefficient matrix-matrix mult for sparse matrices"); + clear(l3); + size_type mm = mat_nrows(l2), nn = mat_ncols(l3); + for (size_type i = 0; i < nn; ++i) + for (size_type j = 0; j < mm; ++j) { + T a = l2(i,j); + if (a != T(0)) add(scaled(mat_const_col(l1, j), a), mat_col(l3, i)); + } + } + + template inline + void mult_spec(const L1& l1, const L2& l2, L3& l3, c_mult, + abstract_skyline, ORIEN) + { mult_spec(l1, l2, l3, c_mult(), abstract_sparse(), ORIEN()); } + + + // col - row matrix-matrix mult + + template inline + void mult_spec(const L1& l1, const L2& l2, L3& l3, crmult) + { mult_spec(l1,l2,l3,crmult(), typename linalg_traits::storage_type()); } + + + template + void mult_spec(const L1& l1, const L2& l2, L3& l3, crmult, abstract_dense) { + // optimizable + clear(l3); + size_type nn = mat_ncols(l1), mm = mat_nrows(l1); + for (size_type i = 0; i < nn; ++i) { + for (size_type j = 0; j < mm; ++j) + add(scaled(mat_const_row(l2, i), l1(j, i)), mat_row(l3, j)); + } + } + + template + void mult_spec(const L1& l1, const L2& l2, L3& l3, crmult, abstract_sparse) { + // optimizable + clear(l3); + size_type nn = mat_ncols(l1); + for (size_type i = 0; i < nn; ++i) { + typename linalg_traits::const_sub_col_type rc1 = mat_const_col(l1, i); + auto it = vect_const_begin(rc1), ite = vect_const_end(rc1); + for (; it != ite; ++it) + add(scaled(mat_const_row(l2, i), *it), mat_row(l3, it.index())); + } + } + + template inline + void mult_spec(const L1& l1, const L2& l2, L3& l3, crmult, abstract_skyline) + { mult_spec(l1, l2, l3, crmult(), abstract_sparse()); } + + + /* ******************************************************************** */ + /* Symmetry test. */ + /* ******************************************************************** */ + + ///@endcond + /** test if A is symmetric. + @param A a matrix. + @param tol a threshold. + */ + template inline + bool is_symmetric(const MAT &A, magnitude_of_linalg(MAT) tol + = magnitude_of_linalg(MAT)(-1)) { + typedef magnitude_of_linalg(MAT) R; + if (tol < R(0)) tol = default_tol(R()) * mat_maxnorm(A); + if (mat_nrows(A) != mat_ncols(A)) return false; + return is_symmetric(A, tol, typename linalg_traits::storage_type()); + } + ///@cond DOXY_SHOW_ALL_FUNCTIONS + + template + bool is_symmetric(const MAT &A, magnitude_of_linalg(MAT) tol, + abstract_dense) { + size_type m = mat_nrows(A); + for (size_type i = 1; i < m; ++i) + for (size_type j = 0; j < i; ++j) + if (gmm::abs(A(i, j)-A(j, i)) > tol) return false; + return true; + } + + template + bool is_symmetric(const MAT &A, magnitude_of_linalg(MAT) tol, + abstract_sparse) { + return is_symmetric(A, tol, typename principal_orientation_type::sub_orientation>::potype()); + } + + template + bool is_symmetric(const MAT &A, magnitude_of_linalg(MAT) tol, + row_major) { + for (size_type i = 0; i < mat_nrows(A); ++i) { + typename linalg_traits::const_sub_row_type row = mat_const_row(A, i); + auto it = vect_const_begin(row), ite = vect_const_end(row); + for (; it != ite; ++it) + if (gmm::abs(*it - A(it.index(), i)) > tol) return false; + } + return true; + } + + template + bool is_symmetric(const MAT &A, magnitude_of_linalg(MAT) tol, + col_major) { + for (size_type i = 0; i < mat_ncols(A); ++i) { + typename linalg_traits::const_sub_col_type col = mat_const_col(A, i); + auto it = vect_const_begin(col), ite = vect_const_end(col); + for (; it != ite; ++it) + if (gmm::abs(*it - A(i, it.index())) > tol) return false; + } + return true; + } + + template + bool is_symmetric(const MAT &A, magnitude_of_linalg(MAT) tol, + abstract_skyline) + { return is_symmetric(A, tol, abstract_sparse()); } + + ///@endcond + /** test if A is Hermitian. + @param A a matrix. + @param tol a threshold. + */ + template inline + bool is_hermitian(const MAT &A, magnitude_of_linalg(MAT) tol + = magnitude_of_linalg(MAT)(-1)) { + typedef magnitude_of_linalg(MAT) R; + if (tol < R(0)) tol = default_tol(R()) * mat_maxnorm(A); + if (mat_nrows(A) != mat_ncols(A)) return false; + return is_hermitian(A, tol, typename linalg_traits::storage_type()); + } + ///@cond DOXY_SHOW_ALL_FUNCTIONS + + template + bool is_hermitian(const MAT &A, magnitude_of_linalg(MAT) tol, + abstract_dense) { + size_type m = mat_nrows(A); + for (size_type i = 1; i < m; ++i) + for (size_type j = 0; j < i; ++j) + if (gmm::abs(A(i, j)-gmm::conj(A(j, i))) > tol) return false; + return true; + } + + template + bool is_hermitian(const MAT &A, magnitude_of_linalg(MAT) tol, + abstract_sparse) { + return is_hermitian(A, tol, typename principal_orientation_type::sub_orientation>::potype()); + } + + template + bool is_hermitian(const MAT &A, magnitude_of_linalg(MAT) tol, + row_major) { + for (size_type i = 0; i < mat_nrows(A); ++i) { + typename linalg_traits::const_sub_row_type row = mat_const_row(A, i); + auto it = vect_const_begin(row), ite = vect_const_end(row); + for (; it != ite; ++it) + if (gmm::abs(gmm::conj(*it) - A(it.index(), i)) > tol) return false; + } + return true; + } + + template + bool is_hermitian(const MAT &A, magnitude_of_linalg(MAT) tol, + col_major) { + for (size_type i = 0; i < mat_ncols(A); ++i) { + typename linalg_traits::const_sub_col_type col = mat_const_col(A, i); + auto it = vect_const_begin(col), ite = vect_const_end(col); + for (; it != ite; ++it) + if (gmm::abs(gmm::conj(*it) - A(i, it.index())) > tol) return false; + } + return true; + } + + template + bool is_hermitian(const MAT &A, magnitude_of_linalg(MAT) tol, + abstract_skyline) + { return is_hermitian(A, tol, abstract_sparse()); } + ///@endcond +} + + +#endif // GMM_BLAS_H__ diff --git a/src/linear_algebra/gmm/gmm_blas_interface.h b/src/linear_algebra/gmm/gmm_blas_interface.h new file mode 100644 index 00000000..c724a3b4 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_blas_interface.h @@ -0,0 +1,1055 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2003-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_blas_interface.h + @author Yves Renard + @date October 7, 2003. + @brief gmm interface for fortran BLAS. +*/ + +#if defined(GETFEM_USES_BLAS) || defined(GMM_USES_BLAS) \ + || defined(GMM_USES_LAPACK) || defined(GMM_USES_ATLAS) + +#ifndef GMM_BLAS_INTERFACE_H +#define GMM_BLAS_INTERFACE_H + +#include "gmm_blas.h" +#include "gmm_interface.h" +#include "gmm_matrix.h" + +namespace gmm { + + // Use ./configure --enable-blas-interface to activate this interface. + +#define GMMLAPACK_TRACE(f) + // #define GMMLAPACK_TRACE(f) cout << "function " << f << " called" << endl; + +#if defined(WeirdNEC) || defined(GMM_USE_BLAS64_INTERFACE) + #define BLAS_INT long +#else // By default BLAS_INT will just be int in C + #define BLAS_INT int +#endif + + /* ********************************************************************* */ + /* Operations interfaced for T = float, double, std::complex */ + /* or std::complex : */ + /* */ + /* vect_norm2(std::vector) */ + /* */ + /* vect_sp(std::vector, std::vector) */ + /* vect_sp(scaled(std::vector), std::vector) */ + /* vect_sp(std::vector, scaled(std::vector)) */ + /* vect_sp(scaled(std::vector), scaled(std::vector)) */ + /* */ + /* vect_hp(std::vector, std::vector) */ + /* vect_hp(scaled(std::vector), std::vector) */ + /* vect_hp(std::vector, scaled(std::vector)) */ + /* vect_hp(scaled(std::vector), scaled(std::vector)) */ + /* */ + /* add(std::vector, std::vector) */ + /* add(scaled(std::vector, a), std::vector) */ + /* */ + /* mult(dense_matrix, dense_matrix, dense_matrix) */ + /* mult(transposed(dense_matrix), dense_matrix, dense_matrix) */ + /* mult(dense_matrix, transposed(dense_matrix), dense_matrix) */ + /* mult(transposed(dense_matrix), transposed(dense_matrix), */ + /* dense_matrix) */ + /* mult(conjugated(dense_matrix), dense_matrix, dense_matrix) */ + /* mult(dense_matrix, conjugated(dense_matrix), dense_matrix) */ + /* mult(conjugated(dense_matrix), conjugated(dense_matrix), */ + /* dense_matrix) */ + /* */ + /* mult(dense_matrix, std::vector, std::vector) */ + /* mult(transposed(dense_matrix), std::vector, std::vector) */ + /* mult(conjugated(dense_matrix), std::vector, std::vector) */ + /* mult(dense_matrix, scaled(std::vector), std::vector) */ + /* mult(transposed(dense_matrix), scaled(std::vector), */ + /* std::vector) */ + /* mult(conjugated(dense_matrix), scaled(std::vector), */ + /* std::vector) */ + /* */ + /* mult_add(dense_matrix, std::vector, std::vector) */ + /* mult_add(transposed(dense_matrix), std::vector, std::vector) */ + /* mult_add(conjugated(dense_matrix), std::vector, std::vector) */ + /* mult_add(dense_matrix, scaled(std::vector), std::vector) */ + /* mult_add(transposed(dense_matrix), scaled(std::vector), */ + /* std::vector) */ + /* mult_add(conjugated(dense_matrix), scaled(std::vector), */ + /* std::vector) */ + /* */ + /* mult(dense_matrix, std::vector, std::vector, std::vector) */ + /* mult(transposed(dense_matrix), std::vector, std::vector, */ + /* std::vector) */ + /* mult(conjugated(dense_matrix), std::vector, std::vector, */ + /* std::vector) */ + /* mult(dense_matrix, scaled(std::vector), std::vector, */ + /* std::vector) */ + /* mult(transposed(dense_matrix), scaled(std::vector), */ + /* std::vector, std::vector) */ + /* mult(conjugated(dense_matrix), scaled(std::vector), */ + /* std::vector, std::vector) */ + /* mult(dense_matrix, std::vector, scaled(std::vector), */ + /* std::vector) */ + /* mult(transposed(dense_matrix), std::vector, */ + /* scaled(std::vector), std::vector) */ + /* mult(conjugated(dense_matrix), std::vector, */ + /* scaled(std::vector), std::vector) */ + /* mult(dense_matrix, scaled(std::vector), scaled(std::vector), */ + /* std::vector) */ + /* mult(transposed(dense_matrix), scaled(std::vector), */ + /* scaled(std::vector), std::vector) */ + /* mult(conjugated(dense_matrix), scaled(std::vector), */ + /* scaled(std::vector), std::vector) */ + /* */ + /* lower_tri_solve(dense_matrix, std::vector, k, b) */ + /* upper_tri_solve(dense_matrix, std::vector, k, b) */ + /* lower_tri_solve(transposed(dense_matrix), std::vector, k, b) */ + /* upper_tri_solve(transposed(dense_matrix), std::vector, k, b) */ + /* lower_tri_solve(conjugated(dense_matrix), std::vector, k, b) */ + /* upper_tri_solve(conjugated(dense_matrix), std::vector, k, b) */ + /* */ + /* rank_one_update(dense_matrix, std::vector, std::vector) */ + /* rank_one_update(dense_matrix, scaled(std::vector), */ + /* std::vector) */ + /* rank_one_update(dense_matrix, std::vector, */ + /* scaled(std::vector)) */ + /* */ + /* ********************************************************************* */ + + /* ********************************************************************* */ + /* Basic defines. */ + /* ********************************************************************* */ + +# define BLAS_S float +# define BLAS_D double +# define BLAS_C std::complex +# define BLAS_Z std::complex + + /* ********************************************************************* */ + /* BLAS functions used. */ + /* ********************************************************************* */ + extern "C" { + void daxpy_(const BLAS_INT *n, const double *alpha, const double *x, + const BLAS_INT *incx, double *y, const BLAS_INT *incy); + void dgemm_(const char *tA, const char *tB, const BLAS_INT *m, + const BLAS_INT *n, const BLAS_INT *k, const double *alpha, + const double *A, const BLAS_INT *ldA, const double *B, + const BLAS_INT *ldB, const double *beta, double *C, + const BLAS_INT *ldC); + void sgemm_(...); void cgemm_(...); void zgemm_(...); + void sgemv_(...); void dgemv_(...); void cgemv_(...); void zgemv_(...); + void strsv_(...); void dtrsv_(...); void ctrsv_(...); void ztrsv_(...); + void saxpy_(...); /*void daxpy_(...); */void caxpy_(...); void zaxpy_(...); + BLAS_S sdot_ (...); BLAS_D ddot_ (...); + BLAS_C cdotu_(...); BLAS_Z zdotu_(...); + BLAS_C cdotc_(...); BLAS_Z zdotc_(...); + BLAS_S snrm2_(...); BLAS_D dnrm2_(...); + BLAS_S scnrm2_(...); BLAS_D dznrm2_(...); + void sger_(...); void dger_(...); void cgerc_(...); void zgerc_(...); + } + +#if 1 + + /* ********************************************************************* */ + /* vect_norm2(x). */ + /* ********************************************************************* */ + +# define nrm2_interface(param1, trans1, blas_name, base_type) \ + inline number_traits::magnitude_type \ + vect_norm2(param1(base_type)) { \ + GMMLAPACK_TRACE("nrm2_interface"); \ + BLAS_INT inc(1), n(BLAS_INT(vect_size(x))); trans1(base_type); \ + return blas_name(&n, &x[0], &inc); \ + } + +# define nrm2_p1(base_type) const std::vector &x +# define nrm2_trans1(base_type) + + nrm2_interface(nrm2_p1, nrm2_trans1, snrm2_ , BLAS_S) + nrm2_interface(nrm2_p1, nrm2_trans1, dnrm2_ , BLAS_D) + nrm2_interface(nrm2_p1, nrm2_trans1, scnrm2_, BLAS_C) + nrm2_interface(nrm2_p1, nrm2_trans1, dznrm2_, BLAS_Z) + + /* ********************************************************************* */ + /* vect_sp(x, y). */ + /* ********************************************************************* */ + +# define dot_interface(param1, trans1, mult1, param2, trans2, mult2, \ + blas_name, base_type) \ + inline base_type vect_sp(param1(base_type), param2(base_type)) { \ + GMMLAPACK_TRACE("dot_interface"); \ + trans1(base_type); trans2(base_type); \ + BLAS_INT inc(1), n(BLAS_INT(vect_size(y))); \ + return mult1 mult2 blas_name(&n, &x[0], &inc, &y[0], &inc); \ + } + +# define dot_p1(base_type) const std::vector &x +# define dot_trans1(base_type) +# define dot_p1_s(base_type) \ + const scaled_vector_const_ref, base_type > &x_ +# define dot_trans1_s(base_type) \ + std::vector &x = \ + const_cast &>(*(linalg_origin(x_))); \ + base_type a(x_.r) + +# define dot_p2(base_type) const std::vector &y +# define dot_trans2(base_type) +# define dot_p2_s(base_type) \ + const scaled_vector_const_ref, base_type > &y_ +# define dot_trans2_s(base_type) \ + std::vector &y = \ + const_cast &>(*(linalg_origin(y_))); \ + base_type b(y_.r) + + dot_interface(dot_p1, dot_trans1, (BLAS_S), dot_p2, dot_trans2, (BLAS_S), + sdot_ , BLAS_S) + dot_interface(dot_p1, dot_trans1, (BLAS_D), dot_p2, dot_trans2, (BLAS_D), + ddot_ , BLAS_D) + dot_interface(dot_p1, dot_trans1, (BLAS_C), dot_p2, dot_trans2, (BLAS_C), + cdotu_, BLAS_C) + dot_interface(dot_p1, dot_trans1, (BLAS_Z), dot_p2, dot_trans2, (BLAS_Z), + zdotu_, BLAS_Z) + + dot_interface(dot_p1_s, dot_trans1_s, a*, dot_p2, dot_trans2, (BLAS_S), + sdot_ ,BLAS_S) + dot_interface(dot_p1_s, dot_trans1_s, a*, dot_p2, dot_trans2, (BLAS_D), + ddot_ ,BLAS_D) + dot_interface(dot_p1_s, dot_trans1_s, a*, dot_p2, dot_trans2, (BLAS_C), + cdotu_,BLAS_C) + dot_interface(dot_p1_s, dot_trans1_s, a*, dot_p2, dot_trans2, (BLAS_Z), + zdotu_,BLAS_Z) + + dot_interface(dot_p1, dot_trans1, (BLAS_S), dot_p2_s, dot_trans2_s, b*, + sdot_ ,BLAS_S) + dot_interface(dot_p1, dot_trans1, (BLAS_D), dot_p2_s, dot_trans2_s, b*, + ddot_ ,BLAS_D) + dot_interface(dot_p1, dot_trans1, (BLAS_C), dot_p2_s, dot_trans2_s, b*, + cdotu_,BLAS_C) + dot_interface(dot_p1, dot_trans1, (BLAS_Z), dot_p2_s, dot_trans2_s, b*, + zdotu_,BLAS_Z) + + dot_interface(dot_p1_s,dot_trans1_s,a*,dot_p2_s,dot_trans2_s,b*,sdot_ , + BLAS_S) + dot_interface(dot_p1_s,dot_trans1_s,a*,dot_p2_s,dot_trans2_s,b*,ddot_ , + BLAS_D) + dot_interface(dot_p1_s,dot_trans1_s,a*,dot_p2_s,dot_trans2_s,b*,cdotu_, + BLAS_C) + dot_interface(dot_p1_s,dot_trans1_s,a*,dot_p2_s,dot_trans2_s,b*,zdotu_, + BLAS_Z) + + + /* ********************************************************************* */ + /* vect_hp(x, y). */ + /* ********************************************************************* */ + +# define dotc_interface(param1, trans1, mult1, param2, trans2, mult2, \ + blas_name, base_type) \ + inline base_type vect_hp(param1(base_type), param2(base_type)) { \ + GMMLAPACK_TRACE("dotc_interface"); \ + trans1(base_type); trans2(base_type); \ + BLAS_INT inc(1), n(BLAS_INT(vect_size(y))); \ + return mult1 mult2 blas_name(&n, &x[0], &inc, &y[0], &inc); \ + } + +# define dotc_p1(base_type) const std::vector &x +# define dotc_trans1(base_type) +# define dotc_p1_s(base_type) \ + const scaled_vector_const_ref, base_type > &x_ +# define dotc_trans1_s(base_type) \ + std::vector &x = \ + const_cast &>(*(linalg_origin(x_))); \ + base_type a(x_.r) + +# define dotc_p2(base_type) const std::vector &y +# define dotc_trans2(base_type) +# define dotc_p2_s(base_type) \ + const scaled_vector_const_ref, base_type > &y_ +# define dotc_trans2_s(base_type) \ + std::vector &y = \ + const_cast &>(*(linalg_origin(y_))); \ + base_type b(gmm::conj(y_.r)) + + dotc_interface(dotc_p1, dotc_trans1, (BLAS_S), dotc_p2, dotc_trans2, + (BLAS_S),sdot_ ,BLAS_S) + dotc_interface(dotc_p1, dotc_trans1, (BLAS_D), dotc_p2, dotc_trans2, + (BLAS_D),ddot_ ,BLAS_D) + dotc_interface(dotc_p1, dotc_trans1, (BLAS_C), dotc_p2, dotc_trans2, + (BLAS_C),cdotc_,BLAS_C) + dotc_interface(dotc_p1, dotc_trans1, (BLAS_Z), dotc_p2, dotc_trans2, + (BLAS_Z),zdotc_,BLAS_Z) + + dotc_interface(dotc_p1_s, dotc_trans1_s, a*, dotc_p2, dotc_trans2, + (BLAS_S),sdot_, BLAS_S) + dotc_interface(dotc_p1_s, dotc_trans1_s, a*, dotc_p2, dotc_trans2, + (BLAS_D),ddot_ , BLAS_D) + dotc_interface(dotc_p1_s, dotc_trans1_s, a*, dotc_p2, dotc_trans2, + (BLAS_C),cdotc_, BLAS_C) + dotc_interface(dotc_p1_s, dotc_trans1_s, a*, dotc_p2, dotc_trans2, + (BLAS_Z),zdotc_, BLAS_Z) + + dotc_interface(dotc_p1, dotc_trans1, (BLAS_S), dotc_p2_s, dotc_trans2_s, + b*,sdot_ , BLAS_S) + dotc_interface(dotc_p1, dotc_trans1, (BLAS_D), dotc_p2_s, dotc_trans2_s, + b*,ddot_ , BLAS_D) + dotc_interface(dotc_p1, dotc_trans1, (BLAS_C), dotc_p2_s, dotc_trans2_s, + b*,cdotc_, BLAS_C) + dotc_interface(dotc_p1, dotc_trans1, (BLAS_Z), dotc_p2_s, dotc_trans2_s, + b*,zdotc_, BLAS_Z) + + dotc_interface(dotc_p1_s,dotc_trans1_s,a*,dotc_p2_s,dotc_trans2_s,b*,sdot_ , + BLAS_S) + dotc_interface(dotc_p1_s,dotc_trans1_s,a*,dotc_p2_s,dotc_trans2_s,b*,ddot_ , + BLAS_D) + dotc_interface(dotc_p1_s,dotc_trans1_s,a*,dotc_p2_s,dotc_trans2_s,b*,cdotc_, + BLAS_C) + dotc_interface(dotc_p1_s,dotc_trans1_s,a*,dotc_p2_s,dotc_trans2_s,b*,zdotc_, + BLAS_Z) + + /* ********************************************************************* */ + /* add(x, y). */ + /* ********************************************************************* */ + template + inline void add_fixed(const V1 &x, V2 &y) + { + for(size_type i = 0; i != N; ++i) y[i] += x[i]; + } + + template + inline void add_for_short_vectors(const V1 &x, V2 &y, size_type n) + { + switch(n) + { + case 1: add_fixed<1>(x, y); break; + case 2: add_fixed<2>(x, y); break; + case 3: add_fixed<3>(x, y); break; + case 4: add_fixed<4>(x, y); break; + case 5: add_fixed<5>(x, y); break; + case 6: add_fixed<6>(x, y); break; + case 7: add_fixed<7>(x, y); break; + case 8: add_fixed<8>(x, y); break; + case 9: add_fixed<9>(x, y); break; + case 10: add_fixed<10>(x, y); break; + case 11: add_fixed<11>(x, y); break; + case 12: add_fixed<12>(x, y); break; + case 13: add_fixed<13>(x, y); break; + case 14: add_fixed<14>(x, y); break; + case 15: add_fixed<15>(x, y); break; + case 16: add_fixed<16>(x, y); break; + case 17: add_fixed<17>(x, y); break; + case 18: add_fixed<18>(x, y); break; + case 19: add_fixed<19>(x, y); break; + case 20: add_fixed<20>(x, y); break; + case 21: add_fixed<21>(x, y); break; + case 22: add_fixed<22>(x, y); break; + case 23: add_fixed<23>(x, y); break; + case 24: add_fixed<24>(x, y); break; + default: GMM_ASSERT2(false, "add_for_short_vectors used with unsupported size"); break; + } + } + + template + inline void add_fixed(const V1 &x, V2 &y, const T &a) + { + for(size_type i = 0; i != N; ++i) y[i] += a*x[i]; + } + + template + inline void add_for_short_vectors(const V1 &x, V2 &y, const T &a, size_type n) + { + switch(n) + { + case 1: add_fixed<1>(x, y, a); break; + case 2: add_fixed<2>(x, y, a); break; + case 3: add_fixed<3>(x, y, a); break; + case 4: add_fixed<4>(x, y, a); break; + case 5: add_fixed<5>(x, y, a); break; + case 6: add_fixed<6>(x, y, a); break; + case 7: add_fixed<7>(x, y, a); break; + case 8: add_fixed<8>(x, y, a); break; + case 9: add_fixed<9>(x, y, a); break; + case 10: add_fixed<10>(x, y, a); break; + case 11: add_fixed<11>(x, y, a); break; + case 12: add_fixed<12>(x, y, a); break; + case 13: add_fixed<13>(x, y, a); break; + case 14: add_fixed<14>(x, y, a); break; + case 15: add_fixed<15>(x, y, a); break; + case 16: add_fixed<16>(x, y, a); break; + case 17: add_fixed<17>(x, y, a); break; + case 18: add_fixed<18>(x, y, a); break; + case 19: add_fixed<19>(x, y, a); break; + case 20: add_fixed<20>(x, y, a); break; + case 21: add_fixed<21>(x, y, a); break; + case 22: add_fixed<22>(x, y, a); break; + case 23: add_fixed<23>(x, y, a); break; + case 24: add_fixed<24>(x, y, a); break; + default: GMM_ASSERT2(false, "add_for_short_vectors used with unsupported size"); break; + } + } + + +# define axpy_interface(param1, trans1, blas_name, base_type) \ + inline void add(param1(base_type), std::vector &y) { \ + GMMLAPACK_TRACE("axpy_interface"); \ + BLAS_INT inc(1), n(BLAS_INT(vect_size(y))); trans1(base_type); \ + if(n == 0) return; \ + else if(n < 25) add_for_short_vectors(x, y, n); \ + else blas_name(&n, &a, &x[0], &inc, &y[0], &inc); \ + } + +# define axpy2_interface(param1, trans1, blas_name, base_type) \ + inline void add(param1(base_type), std::vector &y) { \ + GMMLAPACK_TRACE("axpy_interface"); \ + BLAS_INT inc(1), n(BLAS_INT(vect_size(y))); trans1(base_type); \ + if(n == 0) return; \ + else if(n < 25) add_for_short_vectors(x, y, a, n); \ + else blas_name(&n, &a, &x[0], &inc, &y[0], &inc); \ + } + +# define axpy_p1(base_type) const std::vector &x +# define axpy_trans1(base_type) base_type a(1) +# define axpy_p1_s(base_type) \ + const scaled_vector_const_ref, base_type > &x_ +# define axpy_trans1_s(base_type) \ + std::vector &x = \ + const_cast &>(*(linalg_origin(x_))); \ + base_type a(x_.r) + + axpy_interface(axpy_p1, axpy_trans1, saxpy_, BLAS_S) + axpy_interface(axpy_p1, axpy_trans1, daxpy_, BLAS_D) + axpy_interface(axpy_p1, axpy_trans1, caxpy_, BLAS_C) + axpy_interface(axpy_p1, axpy_trans1, zaxpy_, BLAS_Z) + + axpy2_interface(axpy_p1_s, axpy_trans1_s, saxpy_, BLAS_S) + axpy2_interface(axpy_p1_s, axpy_trans1_s, daxpy_, BLAS_D) + axpy2_interface(axpy_p1_s, axpy_trans1_s, caxpy_, BLAS_C) + axpy2_interface(axpy_p1_s, axpy_trans1_s, zaxpy_, BLAS_Z) + + + /* ********************************************************************* */ + /* mult_add(A, x, z). */ + /* ********************************************************************* */ + +# define gemv_interface(param1, trans1, param2, trans2, blas_name, \ + base_type, orien) \ + inline void mult_add_spec(param1(base_type), param2(base_type), \ + std::vector &z, orien) { \ + GMMLAPACK_TRACE("gemv_interface"); \ + trans1(base_type); trans2(base_type); base_type beta(1); \ + BLAS_INT m(BLAS_INT(mat_nrows(A))), lda(m); \ + BLAS_INT n(BLAS_INT(mat_ncols(A))), inc(1); \ + if (m && n) blas_name(&t, &m, &n, &alpha, &A(0,0), &lda, &x[0], &inc, \ + &beta, &z[0], &inc); \ + else gmm::clear(z); \ + } + + // First parameter +# define gem_p1_n(base_type) const dense_matrix &A +# define gem_trans1_n(base_type) const char t = 'N' +# define gem_p1_t(base_type) \ + const transposed_col_ref *> &A_ +# define gem_trans1_t(base_type) dense_matrix &A = \ + const_cast &>(*(linalg_origin(A_))); \ + const char t = 'T' +# define gem_p1_tc(base_type) \ + const transposed_col_ref *> &A_ +# define gem_p1_c(base_type) \ + const conjugated_col_matrix_const_ref > &A_ +# define gem_trans1_c(base_type) dense_matrix &A = \ + const_cast &>(*(linalg_origin(A_))); \ + const char t = 'C' + + // second parameter +# define gemv_p2_n(base_type) const std::vector &x +# define gemv_trans2_n(base_type) base_type alpha(1) +# define gemv_p2_s(base_type) \ + const scaled_vector_const_ref, base_type > &x_ +# define gemv_trans2_s(base_type) std::vector &x = \ + const_cast &>(*(linalg_origin(x_))); \ + base_type alpha(x_.r) + + // Z <- AX + Z. + gemv_interface(gem_p1_n, gem_trans1_n, gemv_p2_n, gemv_trans2_n, sgemv_, + BLAS_S, col_major) + gemv_interface(gem_p1_n, gem_trans1_n, gemv_p2_n, gemv_trans2_n, dgemv_, + BLAS_D, col_major) + gemv_interface(gem_p1_n, gem_trans1_n, gemv_p2_n, gemv_trans2_n, cgemv_, + BLAS_C, col_major) + gemv_interface(gem_p1_n, gem_trans1_n, gemv_p2_n, gemv_trans2_n, zgemv_, + BLAS_Z, col_major) + + // Z <- transposed(A)X + Z. + gemv_interface(gem_p1_t, gem_trans1_t, gemv_p2_n, gemv_trans2_n, sgemv_, + BLAS_S, row_major) + gemv_interface(gem_p1_t, gem_trans1_t, gemv_p2_n, gemv_trans2_n, dgemv_, + BLAS_D, row_major) + gemv_interface(gem_p1_t, gem_trans1_t, gemv_p2_n, gemv_trans2_n, cgemv_, + BLAS_C, row_major) + gemv_interface(gem_p1_t, gem_trans1_t, gemv_p2_n, gemv_trans2_n, zgemv_, + BLAS_Z, row_major) + + // Z <- transposed(const A)X + Z. + gemv_interface(gem_p1_tc, gem_trans1_t, gemv_p2_n, gemv_trans2_n, sgemv_, + BLAS_S, row_major) + gemv_interface(gem_p1_tc, gem_trans1_t, gemv_p2_n, gemv_trans2_n, dgemv_, + BLAS_D, row_major) + gemv_interface(gem_p1_tc, gem_trans1_t, gemv_p2_n, gemv_trans2_n, cgemv_, + BLAS_C, row_major) + gemv_interface(gem_p1_tc, gem_trans1_t, gemv_p2_n, gemv_trans2_n, zgemv_, + BLAS_Z, row_major) + + // Z <- conjugated(A)X + Z. + gemv_interface(gem_p1_c, gem_trans1_c, gemv_p2_n, gemv_trans2_n, sgemv_, + BLAS_S, row_major) + gemv_interface(gem_p1_c, gem_trans1_c, gemv_p2_n, gemv_trans2_n, dgemv_, + BLAS_D, row_major) + gemv_interface(gem_p1_c, gem_trans1_c, gemv_p2_n, gemv_trans2_n, cgemv_, + BLAS_C, row_major) + gemv_interface(gem_p1_c, gem_trans1_c, gemv_p2_n, gemv_trans2_n, zgemv_, + BLAS_Z, row_major) + + // Z <- A scaled(X) + Z. + gemv_interface(gem_p1_n, gem_trans1_n, gemv_p2_s, gemv_trans2_s, sgemv_, + BLAS_S, col_major) + gemv_interface(gem_p1_n, gem_trans1_n, gemv_p2_s, gemv_trans2_s, dgemv_, + BLAS_D, col_major) + gemv_interface(gem_p1_n, gem_trans1_n, gemv_p2_s, gemv_trans2_s, cgemv_, + BLAS_C, col_major) + gemv_interface(gem_p1_n, gem_trans1_n, gemv_p2_s, gemv_trans2_s, zgemv_, + BLAS_Z, col_major) + + // Z <- transposed(A) scaled(X) + Z. + gemv_interface(gem_p1_t, gem_trans1_t, gemv_p2_s, gemv_trans2_s, sgemv_, + BLAS_S, row_major) + gemv_interface(gem_p1_t, gem_trans1_t, gemv_p2_s, gemv_trans2_s, dgemv_, + BLAS_D, row_major) + gemv_interface(gem_p1_t, gem_trans1_t, gemv_p2_s, gemv_trans2_s, cgemv_, + BLAS_C, row_major) + gemv_interface(gem_p1_t, gem_trans1_t, gemv_p2_s, gemv_trans2_s, zgemv_, + BLAS_Z, row_major) + + // Z <- transposed(const A) scaled(X) + Z. + gemv_interface(gem_p1_tc, gem_trans1_t, gemv_p2_s, gemv_trans2_s, sgemv_, + BLAS_S, row_major) + gemv_interface(gem_p1_tc, gem_trans1_t, gemv_p2_s, gemv_trans2_s, dgemv_, + BLAS_D, row_major) + gemv_interface(gem_p1_tc, gem_trans1_t, gemv_p2_s, gemv_trans2_s, cgemv_, + BLAS_C, row_major) + gemv_interface(gem_p1_tc, gem_trans1_t, gemv_p2_s, gemv_trans2_s, zgemv_, + BLAS_Z, row_major) + + // Z <- conjugated(A) scaled(X) + Z. + gemv_interface(gem_p1_c, gem_trans1_c, gemv_p2_s, gemv_trans2_s, sgemv_, + BLAS_S, row_major) + gemv_interface(gem_p1_c, gem_trans1_c, gemv_p2_s, gemv_trans2_s, dgemv_, + BLAS_D, row_major) + gemv_interface(gem_p1_c, gem_trans1_c, gemv_p2_s, gemv_trans2_s, cgemv_, + BLAS_C, row_major) + gemv_interface(gem_p1_c, gem_trans1_c, gemv_p2_s, gemv_trans2_s, zgemv_, + BLAS_Z, row_major) + + + /* ********************************************************************* */ + /* mult(A, x, y). */ + /* ********************************************************************* */ + +# define gemv_interface2(param1, trans1, param2, trans2, blas_name, \ + base_type, orien) \ + inline void mult_spec(param1(base_type), param2(base_type), \ + std::vector &z, orien) { \ + GMMLAPACK_TRACE("gemv_interface2"); \ + trans1(base_type); trans2(base_type); base_type beta(0); \ + BLAS_INT m(BLAS_INT(mat_nrows(A))), lda(m); \ + BLAS_INT n(BLAS_INT(mat_ncols(A))), inc(1); \ + if (m && n) \ + blas_name(&t, &m, &n, &alpha, &A(0,0), &lda, &x[0], &inc, &beta, \ + &z[0], &inc); \ + else gmm::clear(z); \ + } + + // Y <- AX. + gemv_interface2(gem_p1_n, gem_trans1_n, gemv_p2_n, gemv_trans2_n, sgemv_, + BLAS_S, col_major) + gemv_interface2(gem_p1_n, gem_trans1_n, gemv_p2_n, gemv_trans2_n, dgemv_, + BLAS_D, col_major) + gemv_interface2(gem_p1_n, gem_trans1_n, gemv_p2_n, gemv_trans2_n, cgemv_, + BLAS_C, col_major) + gemv_interface2(gem_p1_n, gem_trans1_n, gemv_p2_n, gemv_trans2_n, zgemv_, + BLAS_Z, col_major) + + // Y <- transposed(A)X. + gemv_interface2(gem_p1_t, gem_trans1_t, gemv_p2_n, gemv_trans2_n, sgemv_, + BLAS_S, row_major) + gemv_interface2(gem_p1_t, gem_trans1_t, gemv_p2_n, gemv_trans2_n, dgemv_, + BLAS_D, row_major) + gemv_interface2(gem_p1_t, gem_trans1_t, gemv_p2_n, gemv_trans2_n, cgemv_, + BLAS_C, row_major) + gemv_interface2(gem_p1_t, gem_trans1_t, gemv_p2_n, gemv_trans2_n, zgemv_, + BLAS_Z, row_major) + + // Y <- transposed(const A)X. + gemv_interface2(gem_p1_tc, gem_trans1_t, gemv_p2_n, gemv_trans2_n, sgemv_, + BLAS_S, row_major) + gemv_interface2(gem_p1_tc, gem_trans1_t, gemv_p2_n, gemv_trans2_n, dgemv_, + BLAS_D, row_major) + gemv_interface2(gem_p1_tc, gem_trans1_t, gemv_p2_n, gemv_trans2_n, cgemv_, + BLAS_C, row_major) + gemv_interface2(gem_p1_tc, gem_trans1_t, gemv_p2_n, gemv_trans2_n, zgemv_, + BLAS_Z, row_major) + + // Y <- conjugated(A)X. + gemv_interface2(gem_p1_c, gem_trans1_c, gemv_p2_n, gemv_trans2_n, sgemv_, + BLAS_S, row_major) + gemv_interface2(gem_p1_c, gem_trans1_c, gemv_p2_n, gemv_trans2_n, dgemv_, + BLAS_D, row_major) + gemv_interface2(gem_p1_c, gem_trans1_c, gemv_p2_n, gemv_trans2_n, cgemv_, + BLAS_C, row_major) + gemv_interface2(gem_p1_c, gem_trans1_c, gemv_p2_n, gemv_trans2_n, zgemv_, + BLAS_Z, row_major) + + // Y <- A scaled(X). + gemv_interface2(gem_p1_n, gem_trans1_n, gemv_p2_s, gemv_trans2_s, sgemv_, + BLAS_S, col_major) + gemv_interface2(gem_p1_n, gem_trans1_n, gemv_p2_s, gemv_trans2_s, dgemv_, + BLAS_D, col_major) + gemv_interface2(gem_p1_n, gem_trans1_n, gemv_p2_s, gemv_trans2_s, cgemv_, + BLAS_C, col_major) + gemv_interface2(gem_p1_n, gem_trans1_n, gemv_p2_s, gemv_trans2_s, zgemv_, + BLAS_Z, col_major) + + // Y <- transposed(A) scaled(X). + gemv_interface2(gem_p1_t, gem_trans1_t, gemv_p2_s, gemv_trans2_s, sgemv_, + BLAS_S, row_major) + gemv_interface2(gem_p1_t, gem_trans1_t, gemv_p2_s, gemv_trans2_s, dgemv_, + BLAS_D, row_major) + gemv_interface2(gem_p1_t, gem_trans1_t, gemv_p2_s, gemv_trans2_s, cgemv_, + BLAS_C, row_major) + gemv_interface2(gem_p1_t, gem_trans1_t, gemv_p2_s, gemv_trans2_s, zgemv_, + BLAS_Z, row_major) + + // Y <- transposed(const A) scaled(X). + gemv_interface2(gem_p1_tc, gem_trans1_t, gemv_p2_s, gemv_trans2_s, sgemv_, + BLAS_S, row_major) + gemv_interface2(gem_p1_tc, gem_trans1_t, gemv_p2_s, gemv_trans2_s, dgemv_, + BLAS_D, row_major) + gemv_interface2(gem_p1_tc, gem_trans1_t, gemv_p2_s, gemv_trans2_s, cgemv_, + BLAS_C, row_major) + gemv_interface2(gem_p1_tc, gem_trans1_t, gemv_p2_s, gemv_trans2_s, zgemv_, + BLAS_Z, row_major) + + // Y <- conjugated(A) scaled(X). + gemv_interface2(gem_p1_c, gem_trans1_c, gemv_p2_s, gemv_trans2_s, sgemv_, + BLAS_S, row_major) + gemv_interface2(gem_p1_c, gem_trans1_c, gemv_p2_s, gemv_trans2_s, dgemv_, + BLAS_D, row_major) + gemv_interface2(gem_p1_c, gem_trans1_c, gemv_p2_s, gemv_trans2_s, cgemv_, + BLAS_C, row_major) + gemv_interface2(gem_p1_c, gem_trans1_c, gemv_p2_s, gemv_trans2_s, zgemv_, + BLAS_Z, row_major) + + + /* ********************************************************************* */ + /* Rank one update. */ + /* ********************************************************************* */ + +# define ger_interface(blas_name, base_type) \ + inline void rank_one_update(const dense_matrix &A, \ + const std::vector &V, \ + const std::vector &W) { \ + GMMLAPACK_TRACE("ger_interface"); \ + BLAS_INT m(BLAS_INT(mat_nrows(A))), lda = m; \ + BLAS_INT n(BLAS_INT(mat_ncols(A))); \ + BLAS_INT incx = 1, incy = 1; \ + base_type alpha(1); \ + if (m && n) \ + blas_name(&m, &n, &alpha, &V[0], &incx, &W[0], &incy, &A(0,0), &lda);\ + } + + ger_interface(sger_, BLAS_S) + ger_interface(dger_, BLAS_D) + ger_interface(cgerc_, BLAS_C) + ger_interface(zgerc_, BLAS_Z) + +# define ger_interface_sn(blas_name, base_type) \ + inline void rank_one_update(const dense_matrix &A, \ + gemv_p2_s(base_type), \ + const std::vector &W) { \ + GMMLAPACK_TRACE("ger_interface"); \ + gemv_trans2_s(base_type); \ + BLAS_INT m(BLAS_INT(mat_nrows(A))), lda = m; \ + BLAS_INT n(BLAS_INT(mat_ncols(A))); \ + BLAS_INT incx = 1, incy = 1; \ + if (m && n) \ + blas_name(&m, &n, &alpha, &x[0], &incx, &W[0], &incy, &A(0,0), &lda);\ + } + + ger_interface_sn(sger_, BLAS_S) + ger_interface_sn(dger_, BLAS_D) + ger_interface_sn(cgerc_, BLAS_C) + ger_interface_sn(zgerc_, BLAS_Z) + +# define ger_interface_ns(blas_name, base_type) \ + inline void rank_one_update(const dense_matrix &A, \ + const std::vector &V, \ + gemv_p2_s(base_type)) { \ + GMMLAPACK_TRACE("ger_interface"); \ + gemv_trans2_s(base_type); \ + BLAS_INT m(BLAS_INT(mat_nrows(A))), lda = m; \ + BLAS_INT n(BLAS_INT(mat_ncols(A))); \ + BLAS_INT incx = 1, incy = 1; \ + base_type al2 = gmm::conj(alpha); \ + if (m && n) \ + blas_name(&m, &n, &al2, &V[0], &incx, &x[0], &incy, &A(0,0), &lda); \ + } + + ger_interface_ns(sger_, BLAS_S) + ger_interface_ns(dger_, BLAS_D) + ger_interface_ns(cgerc_, BLAS_C) + ger_interface_ns(zgerc_, BLAS_Z) + + /* ********************************************************************* */ + /* dense matrix x dense matrix multiplication. */ + /* ********************************************************************* */ + +# define gemm_interface_nn(blas_name, base_type) \ + inline void mult_spec(const dense_matrix &A, \ + const dense_matrix &B, \ + dense_matrix &C, c_mult) { \ + GMMLAPACK_TRACE("gemm_interface_nn"); \ + const char t = 'N'; \ + BLAS_INT m(BLAS_INT(mat_nrows(A))), lda = m; \ + BLAS_INT k(BLAS_INT(mat_ncols(A))); \ + BLAS_INT n(BLAS_INT(mat_ncols(B))); \ + BLAS_INT ldb = k, ldc = m; \ + base_type alpha(1), beta(0); \ + if (m && k && n) \ + blas_name(&t, &t, &m, &n, &k, &alpha, \ + &A(0,0), &lda, &B(0,0), &ldb, &beta, &C(0,0), &ldc); \ + else gmm::clear(C); \ + } + + gemm_interface_nn(sgemm_, BLAS_S) + gemm_interface_nn(dgemm_, BLAS_D) + gemm_interface_nn(cgemm_, BLAS_C) + gemm_interface_nn(zgemm_, BLAS_Z) + + /* ********************************************************************* */ + /* transposed(dense matrix) x dense matrix multiplication. */ + /* ********************************************************************* */ + +# define gemm_interface_tn(blas_name, base_type, is_const) \ + inline void mult_spec( \ + const transposed_col_ref *> &A_,\ + const dense_matrix &B, \ + dense_matrix &C, rcmult) { \ + GMMLAPACK_TRACE("gemm_interface_tn"); \ + dense_matrix &A \ + = const_cast &>(*(linalg_origin(A_))); \ + const char t = 'T', u = 'N'; \ + BLAS_INT m(BLAS_INT(mat_ncols(A))), k(BLAS_INT(mat_nrows(A))); \ + BLAS_INT n(BLAS_INT(mat_ncols(B))); \ + BLAS_INT lda = k, ldb = k, ldc = m; \ + base_type alpha(1), beta(0); \ + if (m && k && n) \ + blas_name(&t, &u, &m, &n, &k, &alpha, \ + &A(0,0), &lda, &B(0,0), &ldb, &beta, &C(0,0), &ldc); \ + else gmm::clear(C); \ + } + + gemm_interface_tn(sgemm_, BLAS_S, dense_matrix) + gemm_interface_tn(dgemm_, BLAS_D, dense_matrix) + gemm_interface_tn(cgemm_, BLAS_C, dense_matrix) + gemm_interface_tn(zgemm_, BLAS_Z, dense_matrix) + gemm_interface_tn(sgemm_, BLAS_S, const dense_matrix) + gemm_interface_tn(dgemm_, BLAS_D, const dense_matrix) + gemm_interface_tn(cgemm_, BLAS_C, const dense_matrix) + gemm_interface_tn(zgemm_, BLAS_Z, const dense_matrix) + + /* ********************************************************************* */ + /* dense matrix x transposed(dense matrix) multiplication. */ + /* ********************************************************************* */ + +# define gemm_interface_nt(blas_name, base_type, is_const) \ + inline void mult_spec(const dense_matrix &A, \ + const transposed_col_ref *> &B_, \ + dense_matrix &C, r_mult) { \ + GMMLAPACK_TRACE("gemm_interface_nt"); \ + dense_matrix &B \ + = const_cast &>(*(linalg_origin(B_))); \ + const char t = 'N', u = 'T'; \ + BLAS_INT m(BLAS_INT(mat_nrows(A))), lda = m; \ + BLAS_INT k(BLAS_INT(mat_ncols(A))); \ + BLAS_INT n(BLAS_INT(mat_nrows(B))); \ + BLAS_INT ldb = n, ldc = m; \ + base_type alpha(1), beta(0); \ + if (m && k && n) \ + blas_name(&t, &u, &m, &n, &k, &alpha, \ + &A(0,0), &lda, &B(0,0), &ldb, &beta, &C(0,0), &ldc); \ + else gmm::clear(C); \ + } + + gemm_interface_nt(sgemm_, BLAS_S, dense_matrix) + gemm_interface_nt(dgemm_, BLAS_D, dense_matrix) + gemm_interface_nt(cgemm_, BLAS_C, dense_matrix) + gemm_interface_nt(zgemm_, BLAS_Z, dense_matrix) + gemm_interface_nt(sgemm_, BLAS_S, const dense_matrix) + gemm_interface_nt(dgemm_, BLAS_D, const dense_matrix) + gemm_interface_nt(cgemm_, BLAS_C, const dense_matrix) + gemm_interface_nt(zgemm_, BLAS_Z, const dense_matrix) + + /* ********************************************************************* */ + /* transposed(dense matrix) x transposed(dense matrix) multiplication. */ + /* ********************************************************************* */ + +# define gemm_interface_tt(blas_name, base_type, isA_const, isB_const) \ + inline void mult_spec( \ + const transposed_col_ref *> &A_, \ + const transposed_col_ref *> &B_, \ + dense_matrix &C, r_mult) { \ + GMMLAPACK_TRACE("gemm_interface_tt"); \ + dense_matrix &A \ + = const_cast &>(*(linalg_origin(A_))); \ + dense_matrix &B \ + = const_cast &>(*(linalg_origin(B_))); \ + const char t = 'T', u = 'T'; \ + BLAS_INT m(BLAS_INT(mat_ncols(A))), k(BLAS_INT(mat_nrows(A))); \ + BLAS_INT n(BLAS_INT(mat_nrows(B))); \ + BLAS_INT lda = k, ldb = n, ldc = m; \ + base_type alpha(1), beta(0); \ + if (m && k && n) \ + blas_name(&t, &u, &m, &n, &k, &alpha, \ + &A(0,0), &lda, &B(0,0), &ldb, &beta, &C(0,0), &ldc); \ + else gmm::clear(C); \ + } + + gemm_interface_tt(sgemm_, BLAS_S, dense_matrix, dense_matrix) + gemm_interface_tt(dgemm_, BLAS_D, dense_matrix, dense_matrix) + gemm_interface_tt(cgemm_, BLAS_C, dense_matrix, dense_matrix) + gemm_interface_tt(zgemm_, BLAS_Z, dense_matrix, dense_matrix) + gemm_interface_tt(sgemm_, BLAS_S, const dense_matrix, dense_matrix) + gemm_interface_tt(dgemm_, BLAS_D, const dense_matrix, dense_matrix) + gemm_interface_tt(cgemm_, BLAS_C, const dense_matrix, dense_matrix) + gemm_interface_tt(zgemm_, BLAS_Z, const dense_matrix, dense_matrix) + gemm_interface_tt(sgemm_, BLAS_S, dense_matrix, const dense_matrix) + gemm_interface_tt(dgemm_, BLAS_D, dense_matrix, const dense_matrix) + gemm_interface_tt(cgemm_, BLAS_C, dense_matrix, const dense_matrix) + gemm_interface_tt(zgemm_, BLAS_Z, dense_matrix, const dense_matrix) + gemm_interface_tt(sgemm_, BLAS_S, const dense_matrix, const dense_matrix) + gemm_interface_tt(dgemm_, BLAS_D, const dense_matrix, const dense_matrix) + gemm_interface_tt(cgemm_, BLAS_C, const dense_matrix, const dense_matrix) + gemm_interface_tt(zgemm_, BLAS_Z, const dense_matrix, const dense_matrix) + + + /* ********************************************************************* */ + /* conjugated(dense matrix) x dense matrix multiplication. */ + /* ********************************************************************* */ + +# define gemm_interface_cn(blas_name, base_type) \ + inline void mult_spec( \ + const conjugated_col_matrix_const_ref > &A_,\ + const dense_matrix &B, \ + dense_matrix &C, rcmult) { \ + GMMLAPACK_TRACE("gemm_interface_cn"); \ + dense_matrix &A \ + = const_cast &>(*(linalg_origin(A_))); \ + const char t = 'C', u = 'N'; \ + BLAS_INT m(BLAS_INT(mat_ncols(A))), k(BLAS_INT(mat_nrows(A))); \ + BLAS_INT n(BLAS_INT(mat_ncols(B))); \ + BLAS_INT lda = k, ldb = k, ldc = m; \ + base_type alpha(1), beta(0); \ + if (m && k && n) \ + blas_name(&t, &u, &m, &n, &k, &alpha, \ + &A(0,0), &lda, &B(0,0), &ldb, &beta, &C(0,0), &ldc); \ + else gmm::clear(C); \ + } + + gemm_interface_cn(sgemm_, BLAS_S) + gemm_interface_cn(dgemm_, BLAS_D) + gemm_interface_cn(cgemm_, BLAS_C) + gemm_interface_cn(zgemm_, BLAS_Z) + + /* ********************************************************************* */ + /* dense matrix x conjugated(dense matrix) multiplication. */ + /* ********************************************************************* */ + +# define gemm_interface_nc(blas_name, base_type) \ + inline void mult_spec(const dense_matrix &A, \ + const conjugated_col_matrix_const_ref > &B_,\ + dense_matrix &C, c_mult, row_major) { \ + GMMLAPACK_TRACE("gemm_interface_nc"); \ + dense_matrix &B \ + = const_cast &>(*(linalg_origin(B_))); \ + const char t = 'N', u = 'C'; \ + BLAS_INT m(BLAS_INT(mat_nrows(A))), lda = m; \ + BLAS_INT k(BLAS_INT(mat_ncols(A))); \ + BLAS_INT n(BLAS_INT(mat_nrows(B))), ldb = n, ldc = m; \ + base_type alpha(1), beta(0); \ + if (m && k && n) \ + blas_name(&t, &u, &m, &n, &k, &alpha, \ + &A(0,0), &lda, &B(0,0), &ldb, &beta, &C(0,0), &ldc); \ + else gmm::clear(C); \ + } + + gemm_interface_nc(sgemm_, BLAS_S) + gemm_interface_nc(dgemm_, BLAS_D) + gemm_interface_nc(cgemm_, BLAS_C) + gemm_interface_nc(zgemm_, BLAS_Z) + + /* ********************************************************************* */ + /* conjugated(dense matrix) x conjugated(dense matrix) multiplication. */ + /* ********************************************************************* */ + +# define gemm_interface_cc(blas_name, base_type) \ + inline void mult_spec( \ + const conjugated_col_matrix_const_ref > &A_,\ + const conjugated_col_matrix_const_ref > &B_,\ + dense_matrix &C, r_mult) { \ + GMMLAPACK_TRACE("gemm_interface_cc"); \ + dense_matrix &A \ + = const_cast &>(*(linalg_origin(A_))); \ + dense_matrix &B \ + = const_cast &>(*(linalg_origin(B_))); \ + const char t = 'C', u = 'C'; \ + BLAS_INT m(BLAS_INT(mat_ncols(A))), k(BLAS_INT(mat_nrows(A))); \ + BLAS_INT lda = k, n(BLAS_INT(mat_nrows(B))), ldb = n, ldc = m; \ + base_type alpha(1), beta(0); \ + if (m && k && n) \ + blas_name(&t, &u, &m, &n, &k, &alpha, \ + &A(0,0), &lda, &B(0,0), &ldb, &beta, &C(0,0), &ldc); \ + else gmm::clear(C); \ + } + + gemm_interface_cc(sgemm_, BLAS_S) + gemm_interface_cc(dgemm_, BLAS_D) + gemm_interface_cc(cgemm_, BLAS_C) + gemm_interface_cc(zgemm_, BLAS_Z) + + /* ********************************************************************* */ + /* Tri solve. */ + /* ********************************************************************* */ + +# define trsv_interface(f_name, loru, param1, trans1, blas_name, base_type)\ + inline void f_name(param1(base_type), std::vector &x, \ + size_type k, bool is_unit) { \ + GMMLAPACK_TRACE("trsv_interface"); \ + loru; trans1(base_type); char d = is_unit ? 'U' : 'N'; \ + BLAS_INT lda(BLAS_INT(mat_nrows(A))), inc(1), n = BLAS_INT(k); \ + if (lda) blas_name(&l, &t, &d, &n, &A(0,0), &lda, &x[0], &inc); \ + } + +# define trsv_upper const char l = 'U' +# define trsv_lower const char l = 'L' + + // X <- LOWER(A)^{-1}X. + trsv_interface(lower_tri_solve, trsv_lower, gem_p1_n, gem_trans1_n, + strsv_, BLAS_S) + trsv_interface(lower_tri_solve, trsv_lower, gem_p1_n, gem_trans1_n, + dtrsv_, BLAS_D) + trsv_interface(lower_tri_solve, trsv_lower, gem_p1_n, gem_trans1_n, + ctrsv_, BLAS_C) + trsv_interface(lower_tri_solve, trsv_lower, gem_p1_n, gem_trans1_n, + ztrsv_, BLAS_Z) + + // X <- UPPER(A)^{-1}X. + trsv_interface(upper_tri_solve, trsv_upper, gem_p1_n, gem_trans1_n, + strsv_, BLAS_S) + trsv_interface(upper_tri_solve, trsv_upper, gem_p1_n, gem_trans1_n, + dtrsv_, BLAS_D) + trsv_interface(upper_tri_solve, trsv_upper, gem_p1_n, gem_trans1_n, + ctrsv_, BLAS_C) + trsv_interface(upper_tri_solve, trsv_upper, gem_p1_n, gem_trans1_n, + ztrsv_, BLAS_Z) + + // X <- LOWER(transposed(A))^{-1}X. + trsv_interface(lower_tri_solve, trsv_upper, gem_p1_t, gem_trans1_t, + strsv_, BLAS_S) + trsv_interface(lower_tri_solve, trsv_upper, gem_p1_t, gem_trans1_t, + dtrsv_, BLAS_D) + trsv_interface(lower_tri_solve, trsv_upper, gem_p1_t, gem_trans1_t, + ctrsv_, BLAS_C) + trsv_interface(lower_tri_solve, trsv_upper, gem_p1_t, gem_trans1_t, + ztrsv_, BLAS_Z) + + // X <- UPPER(transposed(A))^{-1}X. + trsv_interface(upper_tri_solve, trsv_lower, gem_p1_t, gem_trans1_t, + strsv_, BLAS_S) + trsv_interface(upper_tri_solve, trsv_lower, gem_p1_t, gem_trans1_t, + dtrsv_, BLAS_D) + trsv_interface(upper_tri_solve, trsv_lower, gem_p1_t, gem_trans1_t, + ctrsv_, BLAS_C) + trsv_interface(upper_tri_solve, trsv_lower, gem_p1_t, gem_trans1_t, + ztrsv_, BLAS_Z) + + // X <- LOWER(transposed(const A))^{-1}X. + trsv_interface(lower_tri_solve, trsv_upper, gem_p1_tc, gem_trans1_t, + strsv_, BLAS_S) + trsv_interface(lower_tri_solve, trsv_upper, gem_p1_tc, gem_trans1_t, + dtrsv_, BLAS_D) + trsv_interface(lower_tri_solve, trsv_upper, gem_p1_tc, gem_trans1_t, + ctrsv_, BLAS_C) + trsv_interface(lower_tri_solve, trsv_upper, gem_p1_tc, gem_trans1_t, + ztrsv_, BLAS_Z) + + // X <- UPPER(transposed(const A))^{-1}X. + trsv_interface(upper_tri_solve, trsv_lower, gem_p1_tc, gem_trans1_t, + strsv_, BLAS_S) + trsv_interface(upper_tri_solve, trsv_lower, gem_p1_tc, gem_trans1_t, + dtrsv_, BLAS_D) + trsv_interface(upper_tri_solve, trsv_lower, gem_p1_tc, gem_trans1_t, + ctrsv_, BLAS_C) + trsv_interface(upper_tri_solve, trsv_lower, gem_p1_tc, gem_trans1_t, + ztrsv_, BLAS_Z) + + // X <- LOWER(conjugated(A))^{-1}X. + trsv_interface(lower_tri_solve, trsv_upper, gem_p1_c, gem_trans1_c, + strsv_, BLAS_S) + trsv_interface(lower_tri_solve, trsv_upper, gem_p1_c, gem_trans1_c, + dtrsv_, BLAS_D) + trsv_interface(lower_tri_solve, trsv_upper, gem_p1_c, gem_trans1_c, + ctrsv_, BLAS_C) + trsv_interface(lower_tri_solve, trsv_upper, gem_p1_c, gem_trans1_c, + ztrsv_, BLAS_Z) + + // X <- UPPER(conjugated(A))^{-1}X. + trsv_interface(upper_tri_solve, trsv_lower, gem_p1_c, gem_trans1_c, + strsv_, BLAS_S) + trsv_interface(upper_tri_solve, trsv_lower, gem_p1_c, gem_trans1_c, + dtrsv_, BLAS_D) + trsv_interface(upper_tri_solve, trsv_lower, gem_p1_c, gem_trans1_c, + ctrsv_, BLAS_C) + trsv_interface(upper_tri_solve, trsv_lower, gem_p1_c, gem_trans1_c, + ztrsv_, BLAS_Z) + +#endif +} + +#endif // GMM_BLAS_INTERFACE_H + +#endif // GMM_USES_BLAS diff --git a/src/linear_algebra/gmm/gmm_condition_number.h b/src/linear_algebra/gmm/gmm_condition_number.h new file mode 100644 index 00000000..d5b97386 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_condition_number.h @@ -0,0 +1,147 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2003-2020 Yves Renard, Julien Pommier + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_condition_number.h + @author Yves Renard , Julien Pommier + @date August 27, 2003. + @brief computation of the condition number of dense matrices. +*/ +#ifndef GMM_CONDITION_NUMBER_H__ +#define GMM_CONDITION_NUMBER_H__ + +#include "gmm_dense_qr.h" + +namespace gmm { + + /** computation of the condition number of dense matrices using SVD. + + Uses symmetric_qr_algorithm => dense matrices only. + + @param M a matrix. + @param emin smallest (in magnitude) eigenvalue + @param emax largest eigenvalue. + */ + template + typename number_traits::value_type>::magnitude_type + condition_number(const MAT& M, + typename number_traits::value_type>::magnitude_type& emin, + typename number_traits::value_type>::magnitude_type& emax) { + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + + // Added because of errors in complex with zero det + if (sizeof(T) != sizeof(R) && gmm::abs(gmm::lu_det(M)) == R(0)) + return gmm::default_max(R()); + + size_type m = mat_nrows(M), n = mat_ncols(M); + emax = emin = R(0); + std::vector eig(m+n); + + if (m+n == 0) return R(0); + if (is_hermitian(M)) { + eig.resize(m); + gmm::symmetric_qr_algorithm(M, eig); + } + else { + dense_matrix B(m+n, m+n); // not very efficient ?? + gmm::copy(conjugated(M), sub_matrix(B, sub_interval(m, n), sub_interval(0, m))); + gmm::copy(M, sub_matrix(B, sub_interval(0, m), + sub_interval(m, n))); + gmm::symmetric_qr_algorithm(B, eig); + } + emin = emax = gmm::abs(eig[0]); + for (size_type i = 1; i < eig.size(); ++i) { + R e = gmm::abs(eig[i]); + emin = std::min(emin, e); + emax = std::max(emax, e); + } + // cout << "emin = " << emin << " emax = " << emax << endl; + if (emin == R(0)) return gmm::default_max(R()); + return emax / emin; + } + + template + typename number_traits::value_type>::magnitude_type + condition_number(const MAT& M) { + typename number_traits::value_type>::magnitude_type emax, emin; + return condition_number(M, emin, emax); + } + + template + typename number_traits::value_type>::magnitude_type + Frobenius_condition_number_sqr(const MAT& M) { + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + size_type m = mat_nrows(M), n = mat_ncols(M); + dense_matrix B(std::min(m,n), std::min(m,n)); + if (m < n) mult(M,gmm::conjugated(M),B); + else mult(gmm::conjugated(M),M,B); + R trB = abs(mat_trace(B)); + lu_inverse(B); + return trB*abs(mat_trace(B)); + } + + template + typename number_traits::value_type>::magnitude_type + Frobenius_condition_number(const MAT& M) + { return sqrt(Frobenius_condition_number_sqr(M)); } + + /** estimation of the condition number (TO BE DONE...) + */ + template + typename number_traits::value_type>::magnitude_type + condest(const MAT& M, + typename number_traits::value_type>::magnitude_type& emin, + typename number_traits::value_type>::magnitude_type& emax) { + return condition_number(M, emin, emax); + } + + template + typename number_traits::value_type>::magnitude_type + condest(const MAT& M) { + typename number_traits::value_type>::magnitude_type emax, emin; + return condest(M, emin, emax); + } +} + +#endif diff --git a/src/linear_algebra/gmm/gmm_conjugated.h b/src/linear_algebra/gmm/gmm_conjugated.h new file mode 100644 index 00000000..2ff8ad19 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_conjugated.h @@ -0,0 +1,398 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2003-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_conjugated.h + @author Yves Renard + @date September 18, 2003. + @brief handle conjugation of complex matrices/vectors. +*/ +#ifndef GMM_CONJUGATED_H__ +#define GMM_CONJUGATED_H__ + +#include "gmm_def.h" + +namespace gmm { + ///@cond DOXY_SHOW_ALL_FUNCTIONS + + /* ********************************************************************* */ + /* Conjugated references on vectors */ + /* ********************************************************************* */ + + template struct conjugated_const_iterator { + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::pointer pointer; + typedef typename std::iterator_traits::reference reference; + typedef typename std::iterator_traits::difference_type difference_type; + typedef typename std::iterator_traits::iterator_category + iterator_category; + + IT it; + + conjugated_const_iterator(void) {} + conjugated_const_iterator(const IT &i) : it(i) {} + + inline size_type index(void) const { return it.index(); } + conjugated_const_iterator operator ++(int) + { conjugated_const_iterator tmp = *this; ++it; return tmp; } + conjugated_const_iterator operator --(int) + { conjugated_const_iterator tmp = *this; --it; return tmp; } + conjugated_const_iterator &operator ++() { ++it; return *this; } + conjugated_const_iterator &operator --() { --it; return *this; } + conjugated_const_iterator &operator +=(difference_type i) + { it += i; return *this; } + conjugated_const_iterator &operator -=(difference_type i) + { it -= i; return *this; } + conjugated_const_iterator operator +(difference_type i) const + { conjugated_const_iterator itb = *this; return (itb += i); } + conjugated_const_iterator operator -(difference_type i) const + { conjugated_const_iterator itb = *this; return (itb -= i); } + difference_type operator -(const conjugated_const_iterator &i) const + { return difference_type(it - i.it); } + + value_type operator *() const { return gmm::conj(*it); } + value_type operator [](size_type ii) const { return gmm::conj(it[ii]); } + + bool operator ==(const conjugated_const_iterator &i) const + { return (i.it == it); } + bool operator !=(const conjugated_const_iterator &i) const + { return (i.it != it); } + bool operator < (const conjugated_const_iterator &i) const + { return (it < i.it); } + }; + + template struct conjugated_vector_const_ref { + typedef conjugated_vector_const_ref this_type; + typedef typename linalg_traits::value_type value_type; + typedef typename linalg_traits::const_iterator iterator; + typedef typename linalg_traits::reference reference; + typedef typename linalg_traits::origin_type origin_type; + + iterator begin_, end_; + const origin_type *origin; + size_type size_; + + conjugated_vector_const_ref(const V &v) + : begin_(vect_const_begin(v)), end_(vect_const_end(v)), + origin(linalg_origin(v)), + size_(vect_size(v)) {} + + reference operator[](size_type i) const + { return gmm::conj(linalg_traits::access(origin, begin_, end_, i)); } + }; + + template struct linalg_traits > { + typedef conjugated_vector_const_ref this_type; + typedef typename linalg_traits::origin_type origin_type; + typedef linalg_const is_reference; + typedef abstract_vector linalg_type; + typedef typename linalg_traits::value_type value_type; + typedef value_type reference; + typedef abstract_null_type iterator; + typedef conjugated_const_iterator::const_iterator> const_iterator; + typedef typename linalg_traits::storage_type storage_type; + typedef typename linalg_traits::index_sorted index_sorted; + static size_type size(const this_type &v) { return v.size_; } + static iterator begin(this_type &v) { return iterator(v.begin_); } + static const_iterator begin(const this_type &v) + { return const_iterator(v.begin_); } + static iterator end(this_type &v) + { return iterator(v.end_); } + static const_iterator end(const this_type &v) + { return const_iterator(v.end_); } + static value_type access(const origin_type *o, const const_iterator &it, + const const_iterator &ite, size_type i) + { return gmm::conj(linalg_traits::access(o, it.it, ite.it, i)); } + static const origin_type* origin(const this_type &v) { return v.origin; } + }; + + template std::ostream &operator << + (std::ostream &o, const conjugated_vector_const_ref& m) + { gmm::write(o,m); return o; } + + /* ********************************************************************* */ + /* Conjugated references on matrices */ + /* ********************************************************************* */ + + template struct conjugated_row_const_iterator { + typedef conjugated_row_const_iterator iterator; + typedef typename linalg_traits::const_row_iterator ITER; + typedef typename linalg_traits::value_type value_type; + typedef ptrdiff_t difference_type; + typedef size_t size_type; + + ITER it; + + iterator operator ++(int) { iterator tmp = *this; it++; return tmp; } + iterator operator --(int) { iterator tmp = *this; it--; return tmp; } + iterator &operator ++() { it++; return *this; } + iterator &operator --() { it--; return *this; } + iterator &operator +=(difference_type i) { it += i; return *this; } + iterator &operator -=(difference_type i) { it -= i; return *this; } + iterator operator +(difference_type i) const + { iterator itt = *this; return (itt += i); } + iterator operator -(difference_type i) const + { iterator itt = *this; return (itt -= i); } + difference_type operator -(const iterator &i) const + { return it - i.it; } + + ITER operator *() const { return it; } + ITER operator [](int i) { return it + i; } + + bool operator ==(const iterator &i) const { return (it == i.it); } + bool operator !=(const iterator &i) const { return !(i == *this); } + bool operator < (const iterator &i) const { return (it < i.it); } + + conjugated_row_const_iterator(void) {} + conjugated_row_const_iterator(const ITER &i) : it(i) { } + + }; + + template struct conjugated_row_matrix_const_ref { + + typedef conjugated_row_matrix_const_ref this_type; + typedef typename linalg_traits::const_row_iterator iterator; + typedef typename linalg_traits::value_type value_type; + typedef typename linalg_traits::origin_type origin_type; + + iterator begin_, end_; + const origin_type *origin; + size_type nr, nc; + + conjugated_row_matrix_const_ref(const M &m) + : begin_(mat_row_begin(m)), end_(mat_row_end(m)), + origin(linalg_origin(m)), nr(mat_ncols(m)), nc(mat_nrows(m)) {} + + value_type operator()(size_type i, size_type j) const + { return gmm::conj(linalg_traits::access(begin_+j, i)); } + }; + + template std::ostream &operator << + (std::ostream &o, const conjugated_row_matrix_const_ref& m) + { gmm::write(o,m); return o; } + + + template struct conjugated_col_const_iterator { + typedef conjugated_col_const_iterator iterator; + typedef typename linalg_traits::const_col_iterator ITER; + typedef typename linalg_traits::value_type value_type; + typedef ptrdiff_t difference_type; + typedef size_t size_type; + + ITER it; + + iterator operator ++(int) { iterator tmp = *this; it++; return tmp; } + iterator operator --(int) { iterator tmp = *this; it--; return tmp; } + iterator &operator ++() { it++; return *this; } + iterator &operator --() { it--; return *this; } + iterator &operator +=(difference_type i) { it += i; return *this; } + iterator &operator -=(difference_type i) { it -= i; return *this; } + iterator operator +(difference_type i) const + { iterator itt = *this; return (itt += i); } + iterator operator -(difference_type i) const + { iterator itt = *this; return (itt -= i); } + difference_type operator -(const iterator &i) const + { return it - i.it; } + + ITER operator *() const { return it; } + ITER operator [](int i) { return it + i; } + + bool operator ==(const iterator &i) const { return (it == i.it); } + bool operator !=(const iterator &i) const { return !(i == *this); } + bool operator < (const iterator &i) const { return (it < i.it); } + + conjugated_col_const_iterator(void) {} + conjugated_col_const_iterator(const ITER &i) : it(i) { } + + }; + + template struct conjugated_col_matrix_const_ref { + + typedef conjugated_col_matrix_const_ref this_type; + typedef typename linalg_traits::const_col_iterator iterator; + typedef typename linalg_traits::value_type value_type; + typedef typename linalg_traits::origin_type origin_type; + + iterator begin_, end_; + const origin_type *origin; + size_type nr, nc; + + conjugated_col_matrix_const_ref(const M &m) + : begin_(mat_col_begin(m)), end_(mat_col_end(m)), + origin(linalg_origin(m)), nr(mat_ncols(m)), nc(mat_nrows(m)) {} + + value_type operator()(size_type i, size_type j) const + { return gmm::conj(linalg_traits::access(begin_+i, j)); } + }; + + + + template std::ostream &operator << + (std::ostream &o, const conjugated_col_matrix_const_ref& m) + { gmm::write(o,m); return o; } + + + template struct conjugated_return__ { + typedef conjugated_row_matrix_const_ref return_type; + }; + template struct conjugated_return__ { + typedef conjugated_col_matrix_const_ref return_type; + }; + template struct conjugated_return_ { + typedef const L & return_type; + }; + template + struct conjugated_return_, abstract_vector> { + typedef conjugated_vector_const_ref return_type; + }; + template + struct conjugated_return_ { + typedef typename conjugated_return__::sub_orientation>::potype + >::return_type return_type; + }; + template struct conjugated_return { + typedef typename + conjugated_return_::value_type, + typename linalg_traits::linalg_type + >::return_type return_type; + }; + + ///@endcond + /** return a conjugated view of the input matrix or vector. */ + template inline + typename conjugated_return::return_type + conjugated(const L &v) { + return conjugated(v, typename linalg_traits::value_type(), + typename linalg_traits::linalg_type()); + } + ///@cond DOXY_SHOW_ALL_FUNCTIONS + + template inline + const L & conjugated(const L &v, T, LT) { return v; } + + template inline + conjugated_vector_const_ref conjugated(const L &v, std::complex, + abstract_vector) + { return conjugated_vector_const_ref(v); } + + template inline + typename conjugated_return__::sub_orientation>::potype>::return_type + conjugated(const L &v, T, abstract_matrix) { + return conjugated(v, typename principal_orientation_type::sub_orientation>::potype()); + } + + template inline + conjugated_row_matrix_const_ref conjugated(const L &v, row_major) + { return conjugated_row_matrix_const_ref(v); } + + template inline + conjugated_col_matrix_const_ref conjugated(const L &v, col_major) + { return conjugated_col_matrix_const_ref(v); } + + template + struct linalg_traits > { + typedef conjugated_row_matrix_const_ref this_type; + typedef typename linalg_traits::origin_type origin_type; + typedef linalg_const is_reference; + typedef abstract_matrix linalg_type; + typedef typename linalg_traits::value_type value_type; + typedef value_type reference; + typedef typename linalg_traits::storage_type storage_type; + typedef typename org_type::const_sub_row_type>::t vector_type; + typedef conjugated_vector_const_ref sub_col_type; + typedef conjugated_vector_const_ref const_sub_col_type; + typedef conjugated_row_const_iterator col_iterator; + typedef conjugated_row_const_iterator const_col_iterator; + typedef abstract_null_type const_sub_row_type; + typedef abstract_null_type sub_row_type; + typedef abstract_null_type const_row_iterator; + typedef abstract_null_type row_iterator; + typedef col_major sub_orientation; + typedef typename linalg_traits::index_sorted index_sorted; + static inline size_type ncols(const this_type &m) { return m.nc; } + static inline size_type nrows(const this_type &m) { return m.nr; } + static inline const_sub_col_type col(const const_col_iterator &it) + { return conjugated(linalg_traits::row(it.it)); } + static inline const_col_iterator col_begin(const this_type &m) + { return const_col_iterator(m.begin_); } + static inline const_col_iterator col_end(const this_type &m) + { return const_col_iterator(m.end_); } + static inline const origin_type* origin(const this_type &m) + { return m.origin; } + static value_type access(const const_col_iterator &it, size_type i) + { return gmm::conj(linalg_traits::access(it.it, i)); } + }; + + template + struct linalg_traits > { + typedef conjugated_col_matrix_const_ref this_type; + typedef typename linalg_traits::origin_type origin_type; + typedef linalg_const is_reference; + typedef abstract_matrix linalg_type; + typedef typename linalg_traits::value_type value_type; + typedef value_type reference; + typedef typename linalg_traits::storage_type storage_type; + typedef typename org_type::const_sub_col_type>::t vector_type; + typedef conjugated_vector_const_ref sub_row_type; + typedef conjugated_vector_const_ref const_sub_row_type; + typedef conjugated_col_const_iterator row_iterator; + typedef conjugated_col_const_iterator const_row_iterator; + typedef abstract_null_type const_sub_col_type; + typedef abstract_null_type sub_col_type; + typedef abstract_null_type const_col_iterator; + typedef abstract_null_type col_iterator; + typedef row_major sub_orientation; + typedef typename linalg_traits::index_sorted index_sorted; + static inline size_type nrows(const this_type &m) { return m.nr; } + static inline size_type ncols(const this_type &m) { return m.nc; } + static inline const_sub_row_type row(const const_row_iterator &it) + { return conjugated(linalg_traits::col(it.it)); } + static inline const_row_iterator row_begin(const this_type &m) + { return const_row_iterator(m.begin_); } + static inline const_row_iterator row_end(const this_type &m) + { return const_row_iterator(m.end_); } + static inline const origin_type* origin(const this_type &m) + { return m.origin; } + static value_type access(const const_row_iterator &it, size_type i) + { return gmm::conj(linalg_traits::access(it.it, i)); } + }; + + ///@endcond + + +} + +#endif // GMM_CONJUGATED_H__ diff --git a/src/linear_algebra/gmm/gmm_def.h b/src/linear_algebra/gmm/gmm_def.h new file mode 100644 index 00000000..25468c78 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_def.h @@ -0,0 +1,1122 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_def.h + @author Yves Renard + @date October 13, 2002. + @brief Basic definitions and tools of GMM. +*/ +#ifndef GMM_DEF_H__ +#define GMM_DEF_H__ + +#include "gmm_ref.h" +#include + +#ifndef M_PI +# define M_E 2.7182818284590452354 /* e */ +# define M_LOG2E 1.4426950408889634074 /* 1/ln(2) */ +# define M_LOG10E 0.43429448190325182765 /* 1/ln(10) */ +# define M_LN2 0.69314718055994530942 /* ln(2) */ +# define M_LN10 2.30258509299404568402 /* ln(10) */ +# define M_PI 3.14159265358979323846 /* pi */ +# define M_PI_2 1.57079632679489661923 /* pi/2 */ +# define M_PI_4 0.78539816339744830962 /* pi/4 */ +# define M_1_PI 0.31830988618379067154 /* 1/pi */ +# define M_2_PI 0.63661977236758134308 /* 2/pi */ +# define M_2_SQRTPI 1.12837916709551257390 /* 2/sqrt(pi) */ +# define M_SQRT2 1.41421356237309504880 /* sqrt(2) */ +# define M_SQRT1_2 0.70710678118654752440 /* sqrt(2)/2 */ +#endif + +#ifndef M_PIl +# define M_PIl 3.1415926535897932384626433832795029L /* pi */ +# define M_PI_2l 1.5707963267948966192313216916397514L /* pi/2 */ +# define M_PI_4l 0.7853981633974483096156608458198757L /* pi/4 */ +# define M_1_PIl 0.3183098861837906715377675267450287L /* 1/pi */ +# define M_2_PIl 0.6366197723675813430755350534900574L /* 2/pi */ +# define M_2_SQRTPIl 1.1283791670955125738961589031215452L /* 2/sqrt(pi) */ +#endif + +namespace gmm { + + typedef size_t size_type; + + /* ******************************************************************** */ + /* Specifier types */ + /* ******************************************************************** */ + /* not perfectly null, required by aCC 3.33 */ + struct abstract_null_type { + abstract_null_type(int=0) {} + template void operator()(A,B,C) {} + }; // specify an information lake. + + struct linalg_true {}; + struct linalg_false {}; + + template struct linalg_and + { typedef linalg_false bool_type; }; + template <> struct linalg_and + { typedef linalg_true bool_type; }; + template struct linalg_or + { typedef linalg_true bool_type; }; + template <> struct linalg_and + { typedef linalg_false bool_type; }; + + struct linalg_const {}; // A reference is either linalg_const, + struct linalg_modifiable {}; // linalg_modifiable or linalg_false. + + struct abstract_vector {}; // The object is a vector + struct abstract_matrix {}; // The object is a matrix + + struct abstract_sparse {}; // sparse matrix or vector + struct abstract_skyline {}; // 'sky-line' matrix or vector + struct abstract_dense {}; // dense matrix or vector + struct abstract_indirect {}; // matrix given by the product with a vector + + struct row_major {}; // matrix with a row access. + struct col_major {}; // matrix with a column access + struct row_and_col {}; // both accesses but row preference + struct col_and_row {}; // both accesses but column preference + + template struct transposed_type; + template<> struct transposed_type {typedef col_major t_type;}; + template<> struct transposed_type {typedef row_major t_type;}; + template<> struct transposed_type {typedef col_and_row t_type;}; + template<> struct transposed_type {typedef row_and_col t_type;}; + + template struct principal_orientation_type + { typedef abstract_null_type potype; }; + template<> struct principal_orientation_type + { typedef row_major potype; }; + template<> struct principal_orientation_type + { typedef col_major potype; }; + template<> struct principal_orientation_type + { typedef row_major potype; }; + template<> struct principal_orientation_type + { typedef col_major potype; }; + + // template struct linalg_traits; + template struct linalg_traits { + typedef abstract_null_type this_type; + typedef abstract_null_type linalg_type; + typedef abstract_null_type value_type; + typedef abstract_null_type is_reference; + typedef abstract_null_type& reference; + typedef abstract_null_type* iterator; + typedef const abstract_null_type* const_iterator; + typedef abstract_null_type index_sorted; + typedef abstract_null_type storage_type; + typedef abstract_null_type origin_type; + typedef abstract_null_type const_sub_row_type; + typedef abstract_null_type sub_row_type; + typedef abstract_null_type const_row_iterator; + typedef abstract_null_type row_iterator; + typedef abstract_null_type const_sub_col_type; + typedef abstract_null_type sub_col_type; + typedef abstract_null_type const_col_iterator; + typedef abstract_null_type col_iterator; + typedef abstract_null_type sub_orientation; + }; + + template struct vect_ref_type; + template struct vect_ref_type

{ + typedef typename linalg_traits::reference access_type; + typedef typename linalg_traits::iterator iterator; + }; + template struct vect_ref_type { + typedef typename linalg_traits::value_type access_type; + typedef typename linalg_traits::const_iterator iterator; + }; + + template struct const_pointer; + template struct const_pointer

+ { typedef const P* pointer; }; + template struct const_pointer + { typedef const P* pointer; }; + + template struct modifiable_pointer; + template struct modifiable_pointer

+ { typedef P* pointer; }; + template struct modifiable_pointer + { typedef P* pointer; }; + + template struct const_reference; + template struct const_reference + { typedef const R &reference; }; + template struct const_reference + { typedef const R &reference; }; + + + inline bool is_sparse(abstract_sparse) { return true; } + inline bool is_sparse(abstract_dense) { return false; } + inline bool is_sparse(abstract_skyline) { return true; } + inline bool is_sparse(abstract_indirect) { return false; } + + template inline bool is_sparse(const L &) + { return is_sparse(typename linalg_traits::storage_type()); } + + inline bool is_row_matrix_(row_major) { return true; } + inline bool is_row_matrix_(col_major) { return false; } + inline bool is_row_matrix_(row_and_col) { return true; } + inline bool is_row_matrix_(col_and_row) { return true; } + + template inline bool is_row_matrix(const L &) + { return is_row_matrix_(typename linalg_traits::sub_orientation()); } + + inline bool is_col_matrix_(row_major) { return false; } + inline bool is_col_matrix_(col_major) { return true; } + inline bool is_col_matrix_(row_and_col) { return true; } + inline bool is_col_matrix_(col_and_row) { return true; } + + template inline bool is_col_matrix(const L &) + { return is_col_matrix_(typename linalg_traits::sub_orientation()); } + + inline bool is_col_matrix(row_major) { return false; } + inline bool is_col_matrix(col_major) { return true; } + inline bool is_row_matrix(row_major) { return true; } + inline bool is_row_matrix(col_major) { return false; } + + template inline bool is_const_reference(L) { return false; } + inline bool is_const_reference(linalg_const) { return true; } + + + template struct is_gmm_interfaced_ { + typedef linalg_true result; + }; + + template<> struct is_gmm_interfaced_ { + typedef linalg_false result; + }; + + template struct is_gmm_interfaced { + typedef typename is_gmm_interfaced_::this_type >::result result; + }; + + /* ******************************************************************** */ + /* Original type from a pointer or a reference. */ + /* ******************************************************************** */ + + template struct org_type { typedef V t; }; + template struct org_type { typedef V t; }; + template struct org_type { typedef V t; }; + template struct org_type { typedef V t; }; + template struct org_type { typedef V t; }; + + /* ******************************************************************** */ + /* Types to deal with const object representing a modifiable reference */ + /* ******************************************************************** */ + + template struct mref_type_ + { typedef abstract_null_type return_type; }; + template struct mref_type_ + { typedef typename org_type::t & return_type; }; + template struct mref_type_ + { typedef const typename org_type::t & return_type; }; + template struct mref_type_ + { typedef const typename org_type::t & return_type; }; + template struct mref_type_ + { typedef const typename org_type::t & return_type; }; + template struct mref_type_ + { typedef typename org_type::t & return_type; }; + template struct mref_type_ + { typedef typename org_type::t & return_type; }; + + template struct mref_type { + typedef typename std::iterator_traits::value_type L; + typedef typename mref_type_::is_reference>::return_type return_type; + }; + + template typename mref_type::return_type + linalg_cast(const L &l) + { return const_cast::return_type>(l); } + + template typename mref_type::return_type linalg_cast(L &l) + { return const_cast::return_type>(l); } + + template struct cref_type_ + { typedef abstract_null_type return_type; }; + template struct cref_type_ + { typedef typename org_type::t & return_type; }; + template struct cref_type { + typedef typename cref_type_::is_reference>::return_type return_type; + }; + + template typename cref_type::return_type + linalg_const_cast(const L &l) + { return const_cast::return_type>(l); } + + + // To be used to select between a reference or a const refercence for + // the return type of a function + // select_return return C1 if L is a const reference, + // C2 otherwise. + // select_return return C2 if L is a modifiable reference + // C1 otherwise. + template struct select_return_ { + typedef abstract_null_type return_type; + }; + template + struct select_return_ { typedef C1 return_type; }; + template + struct select_return_ { typedef C2 return_type; }; + template struct select_return { + typedef typename std::iterator_traits::value_type L; + typedef typename select_return_::return_type>::return_type return_type; + }; + + + // To be used to select between a reference or a const refercence inside + // a structure or a linagl_traits + // select_ref return C1 if L is a const reference, + // C2 otherwise. + // select_ref return C2 in any case. + template struct select_ref_ + { typedef abstract_null_type ref_type; }; + template + struct select_ref_ { typedef C1 ref_type; }; + template + struct select_ref_ { typedef C2 ref_type; }; + template struct select_ref { + typedef typename std::iterator_traits::value_type L; + typedef typename select_ref_::return_type>::ref_type ref_type; + }; + template + struct select_ref + { typedef C1 ref_type; }; + + + template struct is_a_reference_ + { typedef linalg_true reference; }; + template<> struct is_a_reference_ + { typedef linalg_false reference; }; + + template struct is_a_reference { + typedef typename is_a_reference_::is_reference> + ::reference reference; + }; + + + template inline bool is_original_linalg(const L &) + { return is_original_linalg(typename is_a_reference::reference()); } + inline bool is_original_linalg(linalg_false) { return true; } + inline bool is_original_linalg(linalg_true) { return false; } + + + template struct which_reference + { typedef abstract_null_type is_reference; }; + template struct which_reference + { typedef linalg_modifiable is_reference; }; + template struct which_reference + { typedef linalg_const is_reference; }; + + + template struct select_orientation_ + { typedef abstract_null_type return_type; }; + template + struct select_orientation_ + { typedef C1 return_type; }; + template + struct select_orientation_ + { typedef C2 return_type; }; + template struct select_orientation { + typedef typename select_orientation_::sub_orientation>::potype>::return_type return_type; + }; + + /* ******************************************************************** */ + /* Operations on scalars */ + /* ******************************************************************** */ + + template inline T sqr(T a) { return T(a * a); } + template inline T abs(T a) { return (a < T(0)) ? T(-a) : a; } + template inline T abs(std::complex a) + { T x = a.real(), y = a.imag(); return T(::sqrt(x*x+y*y)); } + template inline T abs_sqr(T a) { return T(a*a); } + template inline T abs_sqr(std::complex a) + { return gmm::sqr(a.real()) + gmm::sqr(a.imag()); } + template inline T pos(T a) { return (a < T(0)) ? T(0) : a; } + template inline T neg(T a) { return (a < T(0)) ? T(-a) : T(0); } + template inline T sgn(T a) { return (a < T(0)) ? T(-1) : T(1); } + template inline T Heaviside(T a) { return (a < T(0)) ? T(0) : T(1); } + inline double random() { return double(rand())/(RAND_MAX+0.5); } + template inline T random(T) + { return T(rand()*2.0)/(T(RAND_MAX)+T(1)/T(2)) - T(1); } + template inline std::complex random(std::complex) + { return std::complex(gmm::random(T()), gmm::random(T())); } + template inline T irandom(T max) + { return T(gmm::random() * double(max)); } + template inline T conj(T a) { return a; } + template inline std::complex conj(std::complex a) + { return std::conj(a); } + template inline T real(T a) { return a; } + template inline T real(std::complex a) { return a.real(); } + template inline T imag(T ) { return T(0); } + template inline T imag(std::complex a) { return a.imag(); } + template inline T sqrt(T a) { return T(::sqrt(a)); } + template inline std::complex sqrt(std::complex a) { + T x = a.real(), y = a.imag(); + if (x == T(0)) { + T t = T(::sqrt(gmm::abs(y) / T(2))); + return std::complex(t, y < T(0) ? -t : t); + } + T t = T(::sqrt(T(2) * (gmm::abs(a) + gmm::abs(x)))), u = t / T(2); + return x > T(0) ? std::complex(u, y / t) + : std::complex(gmm::abs(y) / t, y < T(0) ? -u : u); + } + using std::swap; + + + template struct number_traits { + typedef T magnitude_type; + }; + + template struct number_traits > { + typedef T magnitude_type; + }; + + template inline T conj_product(T a, T b) { return a * b; } + template inline + std::complex conj_product(std::complex a, std::complex b) + { return std::conj(a) * b; } // to be optimized ? + + template inline bool is_complex(T) { return false; } + template inline bool is_complex(std::complex ) + { return true; } + +# define magnitude_of_linalg(M) typename number_traits::value_type>::magnitude_type + + /* ******************************************************************** */ + /* types promotion */ + /* ******************************************************************** */ + + /* should be completed for more specific cases etc */ + template + struct strongest_numeric_type_aux { + typedef T1 T; + }; + template + struct strongest_numeric_type_aux { + typedef T2 T; + }; + + template + struct strongest_numeric_type { + typedef typename + strongest_numeric_type_auxsizeof(T2))>::T T; + }; + template + struct strongest_numeric_type > { + typedef typename number_traits::magnitude_type R1; + typedef std::complex::T > T; + }; + template + struct strongest_numeric_type,T2 > { + typedef typename number_traits::magnitude_type R2; + typedef std::complex::T > T; + }; + template + struct strongest_numeric_type,std::complex > { + typedef std::complex::T > T; + }; + + template<> struct strongest_numeric_type { typedef float T; }; + template<> struct strongest_numeric_type { typedef float T; }; + template<> struct strongest_numeric_type { typedef float T; }; + template<> struct strongest_numeric_type { typedef float T; }; + template<> struct strongest_numeric_type { typedef double T; }; + template<> struct strongest_numeric_type { typedef double T; }; + + template + struct strongest_value_type { + typedef typename + strongest_numeric_type::value_type, + typename linalg_traits::value_type>::T + value_type; + }; + template + struct strongest_value_type3 { + typedef typename + strongest_value_type::value_type>::value_type + value_type; + }; + + + + /* ******************************************************************** */ + /* Basic vectors used */ + /* ******************************************************************** */ + + template struct dense_vector_type + { typedef std::vector vector_type; }; + + template class wsvector; + template class rsvector; + template class dsvector; + template struct sparse_vector_type + { typedef wsvector vector_type; }; + + template class slvector; + template class dense_matrix; + template class row_matrix; + template class col_matrix; + + + /* ******************************************************************** */ + /* Selects a temporary vector type */ + /* V if V is a valid vector type, */ + /* wsvector if V is a reference on a sparse vector, */ + /* std::vector if V is a reference on a dense vector. */ + /* ******************************************************************** */ + + + template + struct temporary_vector_ { + typedef abstract_null_type vector_type; + }; + template + struct temporary_vector_ + { typedef wsvector::value_type> vector_type; }; + template + struct temporary_vector_ + { typedef slvector::value_type> vector_type; }; + template + struct temporary_vector_ + { typedef std::vector::value_type> vector_type; }; + template + struct temporary_vector_ + { typedef V vector_type; }; + template + struct temporary_vector_ + { typedef std::vector::value_type> vector_type; }; + template + struct temporary_vector_ + { typedef wsvector::value_type> vector_type; }; + + template struct temporary_vector { + typedef typename temporary_vector_::reference, + typename linalg_traits::storage_type, + typename linalg_traits::linalg_type, + V>::vector_type vector_type; + }; + + /* ******************************************************************** */ + /* Selects a temporary matrix type */ + /* M if M is a valid matrix type, */ + /* row_matrix if M is a reference on a sparse matrix, */ + /* dense_matrix if M is a reference on a dense matrix. */ + /* ******************************************************************** */ + + + template + struct temporary_matrix_ { typedef abstract_null_type matrix_type; }; + template + struct temporary_matrix_ { + typedef typename linalg_traits::value_type T; + typedef row_matrix > matrix_type; + }; + template + struct temporary_matrix_ { + typedef typename linalg_traits::value_type T; + typedef row_matrix > matrix_type; + }; + template + struct temporary_matrix_ + { typedef dense_matrix::value_type> matrix_type; }; + template + struct temporary_matrix_ + { typedef V matrix_type; }; + + template struct temporary_matrix { + typedef typename temporary_matrix_::reference, + typename linalg_traits::storage_type, + typename linalg_traits::linalg_type, + V>::matrix_type matrix_type; + }; + + + template + struct temporary_col_matrix_ { typedef abstract_null_type matrix_type; }; + template + struct temporary_col_matrix_ { + typedef typename linalg_traits::value_type T; + typedef col_matrix > matrix_type; + }; + template + struct temporary_col_matrix_ { + typedef typename linalg_traits::value_type T; + typedef col_matrix > matrix_type; + }; + template + struct temporary_col_matrix_ + { typedef dense_matrix::value_type> matrix_type; }; + + template struct temporary_col_matrix { + typedef typename temporary_col_matrix_< + typename linalg_traits::storage_type, + typename linalg_traits::linalg_type, + V>::matrix_type matrix_type; + }; + + + + + template + struct temporary_row_matrix_ { typedef abstract_null_type matrix_type; }; + template + struct temporary_row_matrix_ { + typedef typename linalg_traits::value_type T; + typedef row_matrix > matrix_type; + }; + template + struct temporary_row_matrix_ { + typedef typename linalg_traits::value_type T; + typedef row_matrix > matrix_type; + }; + template + struct temporary_row_matrix_ + { typedef dense_matrix::value_type> matrix_type; }; + + template struct temporary_row_matrix { + typedef typename temporary_row_matrix_< + typename linalg_traits::storage_type, + typename linalg_traits::linalg_type, + V>::matrix_type matrix_type; + }; + + + + /* ******************************************************************** */ + /* Selects a temporary dense vector type */ + /* V if V is a valid dense vector type, */ + /* std::vector if V is a reference or another type of vector */ + /* ******************************************************************** */ + + template + struct temporary_dense_vector_ { typedef abstract_null_type vector_type; }; + template + struct temporary_dense_vector_ + { typedef std::vector::value_type> vector_type; }; + template + struct temporary_dense_vector_ + { typedef std::vector::value_type> vector_type; }; + template + struct temporary_dense_vector_ + { typedef std::vector::value_type> vector_type; }; + template + struct temporary_dense_vector_ + { typedef V vector_type; }; + + template struct temporary_dense_vector { + typedef typename temporary_dense_vector_::reference, + typename linalg_traits::storage_type, V>::vector_type vector_type; + }; + + /* ******************************************************************** */ + /* Selects a temporary sparse vector type */ + /* V if V is a valid sparse vector type, */ + /* wsvector if V is a reference or another type of vector */ + /* ******************************************************************** */ + + template + struct temporary_sparse_vector_ { typedef abstract_null_type vector_type; }; + template + struct temporary_sparse_vector_ + { typedef wsvector::value_type> vector_type; }; + template + struct temporary_sparse_vector_ + { typedef V vector_type; }; + template + struct temporary_sparse_vector_ + { typedef wsvector::value_type> vector_type; }; + template + struct temporary_sparse_vector_ + { typedef wsvector::value_type> vector_type; }; + + template struct temporary_sparse_vector { + typedef typename temporary_sparse_vector_::reference, + typename linalg_traits::storage_type, V>::vector_type vector_type; + }; + + /* ******************************************************************** */ + /* Selects a temporary sky-line vector type */ + /* V if V is a valid sky-line vector type, */ + /* slvector if V is a reference or another type of vector */ + /* ******************************************************************** */ + + template + struct temporary_skyline_vector_ + { typedef abstract_null_type vector_type; }; + template + struct temporary_skyline_vector_ + { typedef slvector::value_type> vector_type; }; + template + struct temporary_skyline_vector_ + { typedef V vector_type; }; + template + struct temporary_skyline_vector_ + { typedef slvector::value_type> vector_type; }; + template + struct temporary_skyline_vector_ + { typedef slvector::value_type> vector_type; }; + + template struct temporary_skylines_vector { + typedef typename temporary_skyline_vector_::reference, + typename linalg_traits::storage_type, V>::vector_type vector_type; + }; + + /* ********************************************************************* */ + /* Definition & Comparison of origins. */ + /* ********************************************************************* */ + + template + typename select_return::origin_type *, + typename linalg_traits::origin_type *, + L *>::return_type + linalg_origin(L &l) + { return linalg_traits::origin(linalg_cast(l)); } + + template + typename select_return::origin_type *, + typename linalg_traits::origin_type *, + const L *>::return_type + linalg_origin(const L &l) + { return linalg_traits::origin(linalg_cast(l)); } + + template + bool same_porigin(PT1, PT2) { return false; } + + template + bool same_porigin(PT pt1, PT pt2) { return (pt1 == pt2); } + + template + bool same_origin(const L1 &l1, const L2 &l2) + { return same_porigin(linalg_origin(l1), linalg_origin(l2)); } + + + /* ******************************************************************** */ + /* Miscellaneous */ + /* ******************************************************************** */ + + template inline size_type vect_size(const V &v) + { return linalg_traits::size(v); } + + template inline size_type mat_nrows(const MAT &m) + { return linalg_traits::nrows(m); } + + template inline size_type mat_ncols(const MAT &m) + { return linalg_traits::ncols(m); } + + + template inline + typename select_return::const_iterator, + typename linalg_traits::iterator, V *>::return_type + vect_begin(V &v) + { return linalg_traits::begin(linalg_cast(v)); } + + template inline + typename select_return::const_iterator, + typename linalg_traits::iterator, const V *>::return_type + vect_begin(const V &v) + { return linalg_traits::begin(linalg_cast(v)); } + + template inline + typename linalg_traits::const_iterator + vect_const_begin(const V &v) + { return linalg_traits::begin(v); } + + template inline + typename select_return::const_iterator, + typename linalg_traits::iterator, V *>::return_type + vect_end(V &v) + { return linalg_traits::end(linalg_cast(v)); } + + template inline + typename select_return::const_iterator, + typename linalg_traits::iterator, const V *>::return_type + vect_end(const V &v) + { return linalg_traits::end(linalg_cast(v)); } + + template inline + typename linalg_traits::const_iterator + vect_const_end(const V &v) + { return linalg_traits::end(v); } + + template inline + typename select_return::const_row_iterator, + typename linalg_traits::row_iterator, M *>::return_type + mat_row_begin(M &m) { return linalg_traits::row_begin(linalg_cast(m)); } + + template inline + typename select_return::const_row_iterator, + typename linalg_traits::row_iterator, const M *>::return_type + mat_row_begin(const M &m) + { return linalg_traits::row_begin(linalg_cast(m)); } + + template inline typename linalg_traits::const_row_iterator + mat_row_const_begin(const M &m) + { return linalg_traits::row_begin(m); } + + template inline + typename select_return::const_row_iterator, + typename linalg_traits::row_iterator, M *>::return_type + mat_row_end(M &v) { + return linalg_traits::row_end(linalg_cast(v)); + } + + template inline + typename select_return::const_row_iterator, + typename linalg_traits::row_iterator, const M *>::return_type + mat_row_end(const M &v) { + return linalg_traits::row_end(linalg_cast(v)); + } + + template inline + typename linalg_traits::const_row_iterator + mat_row_const_end(const M &v) + { return linalg_traits::row_end(v); } + + template inline + typename select_return::const_col_iterator, + typename linalg_traits::col_iterator, M *>::return_type + mat_col_begin(M &v) { + return linalg_traits::col_begin(linalg_cast(v)); + } + + template inline + typename select_return::const_col_iterator, + typename linalg_traits::col_iterator, const M *>::return_type + mat_col_begin(const M &v) { + return linalg_traits::col_begin(linalg_cast(v)); + } + + template inline + typename linalg_traits::const_col_iterator + mat_col_const_begin(const M &v) + { return linalg_traits::col_begin(v); } + + template inline + typename linalg_traits::const_col_iterator + mat_col_const_end(const M &v) + { return linalg_traits::col_end(v); } + + template inline + typename select_return::const_col_iterator, + typename linalg_traits::col_iterator, + M *>::return_type + mat_col_end(M &m) + { return linalg_traits::col_end(linalg_cast(m)); } + + template inline + typename select_return::const_col_iterator, + typename linalg_traits::col_iterator, + const M *>::return_type + mat_col_end(const M &m) + { return linalg_traits::col_end(linalg_cast(m)); } + + template inline + typename select_return::const_sub_row_type, + typename linalg_traits::sub_row_type, + const MAT *>::return_type + mat_row(const MAT &m, size_type i) + { return linalg_traits::row(mat_row_begin(m) + i); } + + template inline + typename select_return::const_sub_row_type, + typename linalg_traits::sub_row_type, + MAT *>::return_type + mat_row(MAT &m, size_type i) + { return linalg_traits::row(mat_row_begin(m) + i); } + + template inline + typename linalg_traits::const_sub_row_type + mat_const_row(const MAT &m, size_type i) + { return linalg_traits::row(mat_row_const_begin(m) + i); } + + template inline + typename select_return::const_sub_col_type, + typename linalg_traits::sub_col_type, + const MAT *>::return_type + mat_col(const MAT &m, size_type i) + { return linalg_traits::col(mat_col_begin(m) + i); } + + + template inline + typename select_return::const_sub_col_type, + typename linalg_traits::sub_col_type, + MAT *>::return_type + mat_col(MAT &m, size_type i) + { return linalg_traits::col(mat_col_begin(m) + i); } + + template inline + typename linalg_traits::const_sub_col_type + mat_const_col(const MAT &m, size_type i) + { return linalg_traits::col(mat_col_const_begin(m) + i); } + + /* ********************************************************************* */ + /* Set to begin end set to end for iterators on non-const sparse vectors.*/ + /* ********************************************************************* */ + + template inline + void set_to_begin(IT &it, ORG o, VECT *, linalg_false) + { it = vect_begin(*o); } + + template inline + void set_to_begin(IT &it, ORG o, const VECT *, linalg_false) + { it = vect_const_begin(*o); } + + template inline + void set_to_end(IT &it, ORG o, VECT *, linalg_false) + { it = vect_end(*o); } + + template inline + void set_to_end(IT &it, ORG o, const VECT *, linalg_false) + { it = vect_const_end(*o); } + + + template inline + void set_to_begin(IT &, ORG, VECT *, linalg_const) { } + + template inline + void set_to_begin(IT &, ORG, const VECT *, linalg_const) { } + + template inline + void set_to_end(IT &, ORG, VECT *, linalg_const) { } + + template inline + void set_to_end(IT &, ORG, const VECT *, linalg_const) { } + + template inline + void set_to_begin(IT &, ORG, VECT *v, linalg_modifiable) + { GMM_ASSERT3(!is_sparse(*v), "internal_error"); (void)v; } + + template inline + void set_to_begin(IT &, ORG, const VECT *v, linalg_modifiable) + { GMM_ASSERT3(!is_sparse(*v), "internal_error"); (void)v; } + + template inline + void set_to_end(IT &, ORG, VECT *v, linalg_modifiable) + { GMM_ASSERT3(!is_sparse(*v), "internal_error"); (void)v; } + + template inline + void set_to_end(IT &, ORG, const VECT *v, linalg_modifiable) + { GMM_ASSERT3(!is_sparse(*v), "internal_error"); (void)v; } + + /* ******************************************************************** */ + /* General index for certain algorithms. */ + /* ******************************************************************** */ + + template + size_type index_of_it(const IT &it, size_type, abstract_sparse) + { return it.index(); } + template + size_type index_of_it(const IT &it, size_type, abstract_skyline) + { return it.index(); } + template + size_type index_of_it(const IT &, size_type k, abstract_dense) + { return k; } + + /* ********************************************************************* */ + /* Numeric limits. */ + /* ********************************************************************* */ + + template inline T default_tol(T) { + using namespace std; + static T tol(10); + if (tol == T(10)) { + if (numeric_limits::is_specialized) + tol = numeric_limits::epsilon(); + else { + int i=int(sizeof(T)/4); while(i-- > 0) tol*=T(1E-8); + GMM_WARNING1("The numeric type " << typeid(T).name() + << " has no numeric_limits defined !!\n" + << "Taking " << tol << " as default tolerance"); + } + } + return tol; + } + template inline T default_tol(std::complex) + { return default_tol(T()); } + + template inline T default_min(T) { + using namespace std; + static T mi(10); + if (mi == T(10)) { + if (numeric_limits::is_specialized) + mi = std::numeric_limits::min(); + else { + mi = T(0); + GMM_WARNING1("The numeric type " << typeid(T).name() + << " has no numeric_limits defined !!\n" + << "Taking 0 as default minimum"); + } + } + return mi; + } + template inline T default_min(std::complex) + { return default_min(T()); } + + template inline T default_max(T) { + using namespace std; + static T mi(10); + if (mi == T(10)) { + if (numeric_limits::is_specialized) + mi = std::numeric_limits::max(); + else { + mi = T(1); + GMM_WARNING1("The numeric type " << typeid(T).name() + << " has no numeric_limits defined !!\n" + << "Taking 1 as default maximum !"); + } + } + return mi; + } + template inline T default_max(std::complex) + { return default_max(T()); } + + + /* + use safe_divide to avoid NaNs when dividing very small complex + numbers, for example + std::complex(1e-23,1e-30)/std::complex(1e-23,1e-30) + */ + template inline T safe_divide(T a, T b) { return a/b; } + template inline std::complex + safe_divide(std::complex a, std::complex b) { + T m = std::max(gmm::abs(b.real()), gmm::abs(b.imag())); + a = std::complex(a.real()/m, a.imag()/m); + b = std::complex(b.real()/m, b.imag()/m); + return a / b; + } + + + /* ******************************************************************** */ + /* Write */ + /* ******************************************************************** */ + + template struct cast_char_type { typedef T return_type; }; + template <> struct cast_char_type { typedef int return_type; }; + template <> struct cast_char_type + { typedef unsigned int return_type; }; + template inline typename cast_char_type::return_type + cast_char(const T &c) { return typename cast_char_type::return_type(c); } + + + template inline void write(std::ostream &o, const L &l) + { write(o, l, typename linalg_traits::linalg_type()); } + + template void write(std::ostream &o, const L &l, + abstract_vector) { + o << "vector(" << vect_size(l) << ") ["; + write(o, l, typename linalg_traits::storage_type()); + o << " ]"; + } + + template void write(std::ostream &o, const L &l, + abstract_sparse) { + typename linalg_traits::const_iterator it = vect_const_begin(l), + ite = vect_const_end(l); + for (; it != ite; ++it) + o << " (r" << it.index() << ", " << cast_char(*it) << ")"; + } + + template void write(std::ostream &o, const L &l, + abstract_dense) { + typename linalg_traits::const_iterator it = vect_const_begin(l), + ite = vect_const_end(l); + if (it != ite) o << " " << cast_char(*it++); + for (; it != ite; ++it) o << ", " << cast_char(*it); + } + + template void write(std::ostream &o, const L &l, + abstract_skyline) { + typedef typename linalg_traits::const_iterator const_iterator; + const_iterator it = vect_const_begin(l), ite = vect_const_end(l); + if (it != ite) { + o << ""; + if (it != ite) o << " " << cast_char(*it++); + for (; it != ite; ++it) { o << ", " << cast_char(*it); } + } + } + + template inline void write(std::ostream &o, const L &l, + abstract_matrix) { + write(o, l, typename linalg_traits::sub_orientation()); + } + + + template void write(std::ostream &o, const L &l, + row_major) { + o << "matrix(" << mat_nrows(l) << ", " << mat_ncols(l) << ")" << endl; + for (size_type i = 0; i < mat_nrows(l); ++i) { + o << "("; + write(o, mat_const_row(l, i), typename linalg_traits::storage_type()); + o << " )\n"; + } + } + + template inline + void write(std::ostream &o, const L &l, row_and_col) + { write(o, l, row_major()); } + + template inline + void write(std::ostream &o, const L &l, col_and_row) + { write(o, l, row_major()); } + + template void write(std::ostream &o, const L &l, col_major) { + o << "matrix(" << mat_nrows(l) << ", " << mat_ncols(l) << ")" << endl; + for (size_type i = 0; i < mat_nrows(l); ++i) { + o << "("; + if (is_sparse(l)) { // not optimized ... + for (size_type j = 0; j < mat_ncols(l); ++j) + if (l(i,j) != typename linalg_traits::value_type(0)) + o << " (r" << j << ", " << l(i,j) << ")"; + } + else { + if (mat_ncols(l) != 0) o << ' ' << l(i, 0); + for (size_type j = 1; j < mat_ncols(l); ++j) o << ", " << l(i, j); + } + o << " )\n"; + } + } + +} + +#endif // GMM_DEF_H__ diff --git a/src/linear_algebra/gmm/gmm_dense_Householder.h b/src/linear_algebra/gmm/gmm_dense_Householder.h new file mode 100644 index 00000000..ea010ed5 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_dense_Householder.h @@ -0,0 +1,317 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2003-2020 Yves Renard, Caroline Lecalvez + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_dense_Householder.h + @author Caroline Lecalvez + @author Yves Renard + @date June 5, 2003. + @brief Householder for dense matrices. +*/ + +#ifndef GMM_DENSE_HOUSEHOLDER_H +#define GMM_DENSE_HOUSEHOLDER_H + +#include "gmm_kernel.h" + +namespace gmm { + ///@cond DOXY_SHOW_ALL_FUNCTIONS + + /* ********************************************************************* */ + /* Rank one update (complex and real version) */ + /* ********************************************************************* */ + + template + inline void rank_one_update(Matrix &A, const VecX& x, + const VecY& y, row_major) { + typedef typename linalg_traits::value_type T; + size_type N = mat_nrows(A); + GMM_ASSERT2(N <= vect_size(x) && mat_ncols(A) <= vect_size(y), + "dimensions mismatch"); + typename linalg_traits::const_iterator itx = vect_const_begin(x); + for (size_type i = 0; i < N; ++i, ++itx) { + typedef typename linalg_traits::sub_row_type row_type; + row_type row = mat_row(A, i); + typename linalg_traits::t>::iterator + it = vect_begin(row), ite = vect_end(row); + typename linalg_traits::const_iterator ity = vect_const_begin(y); + T tx = *itx; + for (; it != ite; ++it, ++ity) *it += conj_product(*ity, tx); + } + } + + template + inline void rank_one_update(Matrix &A, const VecX& x, + const VecY& y, col_major) { + typedef typename linalg_traits::value_type T; + size_type M = mat_ncols(A); + GMM_ASSERT2(mat_nrows(A) <= vect_size(x) && M <= vect_size(y), + "dimensions mismatch"); + typename linalg_traits::const_iterator ity = vect_const_begin(y); + for (size_type i = 0; i < M; ++i, ++ity) { + typedef typename linalg_traits::sub_col_type col_type; + col_type col = mat_col(A, i); + typename linalg_traits::t>::iterator + it = vect_begin(col), ite = vect_end(col); + typename linalg_traits::const_iterator itx = vect_const_begin(x); + T ty = *ity; + for (; it != ite; ++it, ++itx) *it += conj_product(ty, *itx); + } + } + + ///@endcond + template + inline void rank_one_update(const Matrix &AA, const VecX& x, + const VecY& y) { + Matrix& A = const_cast(AA); + rank_one_update(A, x, y, typename principal_orientation_type::sub_orientation>::potype()); + } + ///@cond DOXY_SHOW_ALL_FUNCTIONS + + /* ********************************************************************* */ + /* Rank two update (complex and real version) */ + /* ********************************************************************* */ + + template + inline void rank_two_update(Matrix &A, const VecX& x, + const VecY& y, row_major) { + typedef typename linalg_traits::value_type value_type; + size_type N = mat_nrows(A); + GMM_ASSERT2(N <= vect_size(x) && mat_ncols(A) <= vect_size(y), + "dimensions mismatch"); + typename linalg_traits::const_iterator itx1 = vect_const_begin(x); + typename linalg_traits::const_iterator ity2 = vect_const_begin(y); + for (size_type i = 0; i < N; ++i, ++itx1, ++ity2) { + typedef typename linalg_traits::sub_row_type row_type; + row_type row = mat_row(A, i); + typename linalg_traits::t>::iterator + it = vect_begin(row), ite = vect_end(row); + typename linalg_traits::const_iterator itx2 = vect_const_begin(x); + typename linalg_traits::const_iterator ity1 = vect_const_begin(y); + value_type tx = *itx1, ty = *ity2; + for (; it != ite; ++it, ++ity1, ++itx2) + *it += conj_product(*ity1, tx) + conj_product(*itx2, ty); + } + } + + template + inline void rank_two_update(Matrix &A, const VecX& x, + const VecY& y, col_major) { + typedef typename linalg_traits::value_type value_type; + size_type M = mat_ncols(A); + GMM_ASSERT2(mat_nrows(A) <= vect_size(x) && M <= vect_size(y), + "dimensions mismatch"); + typename linalg_traits::const_iterator itx2 = vect_const_begin(x); + typename linalg_traits::const_iterator ity1 = vect_const_begin(y); + for (size_type i = 0; i < M; ++i, ++ity1, ++itx2) { + typedef typename linalg_traits::sub_col_type col_type; + col_type col = mat_col(A, i); + typename linalg_traits::t>::iterator + it = vect_begin(col), ite = vect_end(col); + typename linalg_traits::const_iterator itx1 = vect_const_begin(x); + typename linalg_traits::const_iterator ity2 = vect_const_begin(y); + value_type ty = *ity1, tx = *itx2; + for (; it != ite; ++it, ++itx1, ++ity2) + *it += conj_product(ty, *itx1) + conj_product(tx, *ity2); + } + } + + ///@endcond + template + inline void rank_two_update(const Matrix &AA, const VecX& x, + const VecY& y) { + Matrix& A = const_cast(AA); + rank_two_update(A, x, y, typename principal_orientation_type::sub_orientation>::potype()); + } + ///@cond DOXY_SHOW_ALL_FUNCTIONS + + /* ********************************************************************* */ + /* Householder vector computation (complex and real version) */ + /* ********************************************************************* */ + + template void house_vector(const VECT &VV) { + VECT &V = const_cast(VV); + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + + R mu = vect_norm2(V), abs_v0 = gmm::abs(V[0]); + if (mu != R(0)) + gmm::scale(V, (abs_v0 == R(0)) ? T(R(1) / mu) + : (safe_divide(T(abs_v0), V[0]) / (abs_v0 + mu))); + if (gmm::real(V[vect_size(V)-1]) * R(0) != R(0)) gmm::clear(V); + V[0] = T(1); + } + + template void house_vector_last(const VECT &VV) { + VECT &V = const_cast(VV); + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + + size_type m = vect_size(V); + R mu = vect_norm2(V), abs_v0 = gmm::abs(V[m-1]); + if (mu != R(0)) + gmm::scale(V, (abs_v0 == R(0)) ? T(R(1) / mu) + : ((abs_v0 / V[m-1]) / (abs_v0 + mu))); + if (gmm::real(V[0]) * R(0) != R(0)) gmm::clear(V); + V[m-1] = T(1); + } + + /* ********************************************************************* */ + /* Householder updates (complex and real version) */ + /* ********************************************************************* */ + + // multiply A to the left by the reflector stored in V. W is a temporary. + template inline + void row_house_update(const MAT &AA, const VECT1 &V, const VECT2 &WW) { + VECT2 &W = const_cast(WW); MAT &A = const_cast(AA); + typedef typename linalg_traits::value_type value_type; + typedef typename number_traits::magnitude_type magnitude_type; + + gmm::mult(conjugated(A), + scaled(V, value_type(magnitude_type(-2)/vect_norm2_sqr(V))), W); + rank_one_update(A, V, W); + } + + // multiply A to the right by the reflector stored in V. W is a temporary. + template inline + void col_house_update(const MAT &AA, const VECT1 &V, const VECT2 &WW) { + VECT2 &W = const_cast(WW); MAT &A = const_cast(AA); + typedef typename linalg_traits::value_type value_type; + typedef typename number_traits::magnitude_type magnitude_type; + + gmm::mult(A, + scaled(V, value_type(magnitude_type(-2)/vect_norm2_sqr(V))), W); + rank_one_update(A, W, V); + } + + ///@endcond + + /* ********************************************************************* */ + /* Hessenberg reduction with Householder. */ + /* ********************************************************************* */ + + template + void Hessenberg_reduction(const MAT1& AA, const MAT2 &QQ, bool compute_Q){ + MAT1& A = const_cast(AA); MAT2& Q = const_cast(QQ); + typedef typename linalg_traits::value_type value_type; + if (compute_Q) gmm::copy(identity_matrix(), Q); + size_type n = mat_nrows(A); if (n < 2) return; + std::vector v(n), w(n); + sub_interval SUBK(0,n); + for (size_type k = 1; k+1 < n; ++k) { + sub_interval SUBI(k, n-k), SUBJ(k-1,n-k+1); + v.resize(n-k); + for (size_type j = k; j < n; ++j) v[j-k] = A(j, k-1); + house_vector(v); + row_house_update(sub_matrix(A, SUBI, SUBJ), v, sub_vector(w, SUBJ)); + col_house_update(sub_matrix(A, SUBK, SUBI), v, w); + // is it possible to "unify" the two on the common part of the matrix? + if (compute_Q) col_house_update(sub_matrix(Q, SUBK, SUBI), v, w); + } + } + + /* ********************************************************************* */ + /* Householder tridiagonalization for symmetric matrices */ + /* ********************************************************************* */ + + template + void Householder_tridiagonalization(const MAT1 &AA, const MAT2 &QQ, + bool compute_q) { + MAT1 &A = const_cast(AA); MAT2 &Q = const_cast(QQ); + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + + size_type n = mat_nrows(A); if (n < 2) return; + std::vector v(n), p(n), w(n), ww(n); + sub_interval SUBK(0,n); + + for (size_type k = 1; k+1 < n; ++k) { // not optimized ... + sub_interval SUBI(k, n-k); + v.resize(n-k); p.resize(n-k); w.resize(n-k); + for (size_type l = k; l < n; ++l) + { v[l-k] = w[l-k] = A(l, k-1); A(l, k-1) = A(k-1, l) = T(0); } + house_vector(v); + R norm = vect_norm2_sqr(v); + A(k-1, k) = gmm::conj(A(k, k-1) = w[0] - T(2)*v[0]*vect_hp(w, v)/norm); + + gmm::mult(sub_matrix(A, SUBI), gmm::scaled(v, T(-2) / norm), p); + gmm::add(p, gmm::scaled(v, -vect_hp(v, p) / norm), w); + rank_two_update(sub_matrix(A, SUBI), v, w); + // it should be possible to compute only the upper or lower part + + if (compute_q) col_house_update(sub_matrix(Q, SUBK, SUBI), v, ww); + } + } + + /* ********************************************************************* */ + /* Real and complex Givens rotations */ + /* ********************************************************************* */ + + template void Givens_rotation(T a, T b, T &c, T &s) { + typedef typename number_traits::magnitude_type R; + R aa = gmm::abs(a), bb = gmm::abs(b); + if (bb == R(0)) { c = T(1); s = T(0); return; } + if (aa == R(0)) { c = T(0); s = b / bb; return; } + if (bb > aa) + { T t = -safe_divide(a,b); s = T(R(1) / (sqrt(R(1)+gmm::abs_sqr(t)))); c = s * t; } + else + { T t = -safe_divide(b,a); c = T(R(1) / (sqrt(R(1)+gmm::abs_sqr(t)))); s = c * t; } + } + + // Apply Q* v + template inline + void Apply_Givens_rotation_left(T &x, T &y, T c, T s) + { T t1=x, t2=y; x = gmm::conj(c)*t1 - gmm::conj(s)*t2; y = c*t2 + s*t1; } + + // Apply v^T Q + template inline + void Apply_Givens_rotation_right(T &x, T &y, T c, T s) + { T t1=x, t2=y; x = c*t1 - s*t2; y = gmm::conj(c)*t2 + gmm::conj(s)*t1; } + + template + void row_rot(const MAT &AA, T c, T s, size_type i, size_type k) { + MAT &A = const_cast(AA); // can be specialized for row matrices + for (size_type j = 0; j < mat_ncols(A); ++j) + Apply_Givens_rotation_left(A(i,j), A(k,j), c, s); + } + + template + void col_rot(const MAT &AA, T c, T s, size_type i, size_type k) { + MAT &A = const_cast(AA); // can be specialized for column matrices + for (size_type j = 0; j < mat_nrows(A); ++j) + Apply_Givens_rotation_right(A(j,i), A(j,k), c, s); + } + +} + +#endif + diff --git a/src/linear_algebra/gmm/gmm_dense_lu.h b/src/linear_algebra/gmm/gmm_dense_lu.h new file mode 100644 index 00000000..33fbbcb1 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_dense_lu.h @@ -0,0 +1,288 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2003-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +// This file is a modified version of lu.h from MTL. +// See http://osl.iu.edu/research/mtl/ +// Following the corresponding Copyright notice. +//=========================================================================== +// +// Copyright (c) 1998-2020, University of Notre Dame. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the University of Notre Dame nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE TRUSTEES OF INDIANA UNIVERSITY AND +// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES +// OF INDIANA UNIVERSITY AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//=========================================================================== + +/**@file gmm_dense_lu.h + @author Andrew Lumsdaine, Jeremy G. Siek, Lie-Quan Lee, Y. Renard + @date June 5, 2003. + @brief LU factorizations and determinant computation for dense matrices. +*/ +#ifndef GMM_DENSE_LU_H +#define GMM_DENSE_LU_H + +#include "gmm_dense_Householder.h" + +namespace gmm { + + /* ********************************************************************** */ + /* IPVT structure. */ + /* ********************************************************************** */ + // For compatibility with lapack version with 64 or 32 bit integer. + // Should be replaced by std::vector if 32 bit integer version + // of lapack is not used anymore (and lapack_ipvt_int set to size_type) + + // Do not use iterators of this interface container + class lapack_ipvt : public std::vector { + bool is_int64; + size_type &operator[](size_type i) + { return std::vector::operator[](i); } + size_type operator[] (size_type i) const + { return std::vector::operator[](i); } + void begin(void) const {} + void begin(void) {} + void end(void) const {} + void end(void) {} + + public: + void set_to_int32() { is_int64 = false; } + const size_type *pfirst() const + { return &(*(std::vector::begin())); } + size_type *pfirst() { return &(*(std::vector::begin())); } + + lapack_ipvt(size_type n) : std::vector(n), is_int64(true) {} + + size_type get(size_type i) const { + const size_type *p = pfirst(); + return is_int64 ? p[i] : size_type(((const int *)(p))[i]); + } + void set(size_type i, size_type val) { + size_type *p = pfirst(); + if (is_int64) p[i] = val; else ((int *)(p))[i] = int(val); + } + }; +} + +#include "gmm_opt.h" + +namespace gmm { + + /** LU Factorization of a general (dense) matrix (real or complex). + + This is the outer product (a level-2 operation) form of the LU + Factorization with pivoting algorithm . This is equivalent to + LAPACK's dgetf2. Also see "Matrix Computations" 3rd Ed. by Golub + and Van Loan section 3.2.5 and especially page 115. + + The pivot indices in ipvt are indexed starting from 1 + so that this is compatible with LAPACK (Fortran). + */ + template + size_type lu_factor(DenseMatrix& A, lapack_ipvt& ipvt) { + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + size_type info(0), i, j, jp, M(mat_nrows(A)), N(mat_ncols(A)); + size_type NN = std::min(M, N); + std::vector c(M), r(N); + + GMM_ASSERT2(ipvt.size()+1 >= NN, "IPVT too small"); + for (i = 0; i+1 < NN; ++i) ipvt.set(i, i); + + if (M || N) { + for (j = 0; j+1 < NN; ++j) { + R max = gmm::abs(A(j,j)); jp = j; + for (i = j+1; i < M; ++i) /* find pivot. */ + if (gmm::abs(A(i,j)) > max) { jp = i; max = gmm::abs(A(i,j)); } + ipvt.set(j, jp + 1); + + if (max == R(0)) { info = j + 1; break; } + if (jp != j) for (i = 0; i < N; ++i) std::swap(A(jp, i), A(j, i)); + + for (i = j+1; i < M; ++i) { A(i, j) /= A(j,j); c[i-j-1] = -A(i, j); } + for (i = j+1; i < N; ++i) r[i-j-1] = A(j, i); // avoid the copy ? + rank_one_update(sub_matrix(A, sub_interval(j+1, M-j-1), + sub_interval(j+1, N-j-1)), c, conjugated(r)); + } + ipvt.set(NN-1, NN); + } + return info; + } + + /** LU Solve : Solve equation Ax=b, given an LU factored matrix.*/ + // Thanks to Valient Gough for this routine! + template + void lu_solve(const DenseMatrix &LU, const Pvector& pvector, + VectorX &x, const VectorB &b) { + typedef typename linalg_traits::value_type T; + copy(b, x); + for(size_type i = 0; i < pvector.size(); ++i) { + size_type perm = pvector.get(i)-1; // permutations stored in 1's offset + if(i != perm) { T aux = x[i]; x[i] = x[perm]; x[perm] = aux; } + } + /* solve Ax = b -> LUx = b -> Ux = L^-1 b. */ + lower_tri_solve(LU, x, true); + upper_tri_solve(LU, x, false); + } + + template + void lu_solve(const DenseMatrix &A, VectorX &x, const VectorB &b) { + typedef typename linalg_traits::value_type T; + dense_matrix B(mat_nrows(A), mat_ncols(A)); + lapack_ipvt ipvt(mat_nrows(A)); + gmm::copy(A, B); + size_type info = lu_factor(B, ipvt); + GMM_ASSERT1(!info, "Singular system, pivot = " << info); + lu_solve(B, ipvt, x, b); + } + + template + void lu_solve_transposed(const DenseMatrix &LU, const Pvector& pvector, + VectorX &x, const VectorB &b) { + typedef typename linalg_traits::value_type T; + copy(b, x); + lower_tri_solve(transposed(LU), x, false); + upper_tri_solve(transposed(LU), x, true); + for(size_type i = pvector.size(); i > 0; --i) { + size_type perm = pvector.get(i-1)-1; // permutations stored in 1's offset + if(i-1 != perm) { T aux = x[i-1]; x[i-1] = x[perm]; x[perm] = aux; } + } + } + + + ///@cond DOXY_SHOW_ALL_FUNCTIONS + template + void lu_inverse(const DenseMatrixLU& LU, const Pvector& pvector, + DenseMatrix& AInv, col_major) { + typedef typename linalg_traits::value_type T; + std::vector tmp(pvector.size(), T(0)); + std::vector result(pvector.size()); + for(size_type i = 0; i < pvector.size(); ++i) { + tmp[i] = T(1); + lu_solve(LU, pvector, result, tmp); + copy(result, mat_col(AInv, i)); + tmp[i] = T(0); + } + } + + template + void lu_inverse(const DenseMatrixLU& LU, const Pvector& pvector, + DenseMatrix& AInv, row_major) { + typedef typename linalg_traits::value_type T; + std::vector tmp(pvector.size(), T(0)); + std::vector result(pvector.size()); + for(size_type i = 0; i < pvector.size(); ++i) { + tmp[i] = T(1); // to be optimized !! + // on peut sur le premier tri solve reduire le systeme + // et peut etre faire un solve sur une serie de vecteurs au lieu + // de vecteur a vecteur (accumulation directe de l'inverse dans la + // matrice au fur et a mesure du calcul ... -> evite la copie finale + lu_solve_transposed(LU, pvector, result, tmp); + copy(result, mat_row(AInv, i)); + tmp[i] = T(0); + } + } + ///@endcond + + /** Given an LU factored matrix, build the inverse of the matrix. */ + template + void lu_inverse(const DenseMatrixLU& LU, const Pvector& pvector, + const DenseMatrix& AInv_) { + DenseMatrix& AInv = const_cast(AInv_); + lu_inverse(LU, pvector, AInv, typename principal_orientation_type::sub_orientation>::potype()); + } + + /** Given a dense matrix, build the inverse of the matrix, and + return the determinant */ + template + typename linalg_traits::value_type + lu_inverse(const DenseMatrix& A_, bool doassert = true) { + typedef typename linalg_traits::value_type T; + DenseMatrix& A = const_cast(A_); + dense_matrix B(mat_nrows(A), mat_ncols(A)); + lapack_ipvt ipvt(mat_nrows(A)); + gmm::copy(A, B); + size_type info = lu_factor(B, ipvt); + if (doassert) GMM_ASSERT1(!info, "Non invertible matrix, pivot = "< + typename linalg_traits::value_type + lu_det(const DenseMatrixLU& LU, const Pvector &pvector) { + typedef typename linalg_traits::value_type T; + T det(1); + for (size_type j = 0; j < std::min(mat_nrows(LU), mat_ncols(LU)); ++j) + det *= LU(j,j); + for(size_type i = 0; i < pvector.size(); ++i) + if (i != size_type(pvector.get(i)-1)) { det = -det; } + return det; + } + + template + typename linalg_traits::value_type + lu_det(const DenseMatrix& A) { + typedef typename linalg_traits::value_type T; + dense_matrix B(mat_nrows(A), mat_ncols(A)); + lapack_ipvt ipvt(mat_nrows(A)); + gmm::copy(A, B); + lu_factor(B, ipvt); + return lu_det(B, ipvt); + } + +} + +#endif + diff --git a/src/linear_algebra/gmm/gmm_dense_matrix_functions.h b/src/linear_algebra/gmm/gmm_dense_matrix_functions.h new file mode 100644 index 00000000..3cd18b60 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_dense_matrix_functions.h @@ -0,0 +1,302 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2014-2020 Konstantinos Poulios + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_dense_matrix_functions.h + @author Konstantinos Poulios + @date December 10, 2014. + @brief Common matrix functions for dense matrices. +*/ +#ifndef GMM_DENSE_MATRIX_FUNCTIONS_H +#define GMM_DENSE_MATRIX_FUNCTIONS_H + + +namespace gmm { + + + /** + Matrix square root for upper triangular matrices (from GNU Octave). + */ + template + void sqrtm_utri_inplace(dense_matrix& A) + { + typedef typename number_traits::magnitude_type R; + bool singular = false; + + // The following code is equivalent to this triple loop: + // + // n = rows (A); + // for j = 1:n + // A(j,j) = sqrt (A(j,j)); + // for i = j-1:-1:1 + // A(i,j) /= (A(i,i) + A(j,j)); + // k = 1:i-1; + // t storing a A(k,j) -= A(k,i) * A(i,j); + // endfor + // endfor + + R tol = R(0); // default_tol(R()) * gmm::mat_maxnorm(A); + + const size_type n = mat_nrows(A); + for (int j=0; j < int(n); j++) { + typename dense_matrix::iterator colj = A.begin() + j*n; + if (gmm::abs(colj[j]) > tol) + colj[j] = gmm::sqrt(colj[j]); + else + singular = true; + + for (int i=j-1; i >= 0; i--) { + typename dense_matrix::const_iterator coli = A.begin() + i*n; + T colji = colj[i] = safe_divide(colj[i], (coli[i] + colj[j])); + for (int k = 0; k < i; k++) + colj[k] -= coli[k] * colji; + } + } + + if (singular) + GMM_WARNING1("Matrix is singular, may not have a square root"); + } + + + template + void sqrtm(const dense_matrix >& A, + dense_matrix >& SQRTMA) + { + GMM_ASSERT1(gmm::mat_nrows(A) == gmm::mat_ncols(A), + "Matrix square root requires a square matrix"); + gmm::resize(SQRTMA, gmm::mat_nrows(A), gmm::mat_ncols(A)); + dense_matrix > S(A), Q(A), TMP(A); + #if defined(GMM_USES_LAPACK) + schur(TMP, S, Q); + #else + GMM_ASSERT1(false, "Please recompile with lapack and blas librairies " + "to use sqrtm matrix function."); + #endif + sqrtm_utri_inplace(S); + gmm::mult(Q, S, TMP); + gmm::mult(TMP, gmm::transposed(Q), SQRTMA); + } + + template + void sqrtm(const dense_matrix& A, + dense_matrix >& SQRTMA) + { + dense_matrix > cA(mat_nrows(A), mat_ncols(A)); + gmm::copy(A, gmm::real_part(cA)); + sqrtm(cA, SQRTMA); + } + + template + void sqrtm(const dense_matrix& A, dense_matrix& SQRTMA) + { + dense_matrix > cA(mat_nrows(A), mat_ncols(A)); + gmm::copy(A, gmm::real_part(cA)); + dense_matrix > cSQRTMA(cA); + sqrtm(cA, cSQRTMA); + gmm::resize(SQRTMA, gmm::mat_nrows(A), gmm::mat_ncols(A)); + gmm::copy(gmm::real_part(cSQRTMA), SQRTMA); +// dense_matrix >::const_reference +// it = cSQRTMA.begin(), ite = cSQRTMA.end(); +// dense_matrix >::reference +// rit = SQRTMA.begin(); +// for (; it != ite; ++it, ++rit) *rit = it->real(); + } + + + /** + Matrix logarithm for upper triangular matrices (from GNU/Octave) + */ + template + void logm_utri_inplace(dense_matrix& S) + { + typedef typename number_traits::magnitude_type R; + + size_type n = gmm::mat_nrows(S); + GMM_ASSERT1(n == gmm::mat_ncols(S), + "Matrix logarithm is not defined for non-square matrices"); + for (size_type i=0; i < n-1; ++i) + if (gmm::abs(S(i+1,i)) > default_tol(T())) { + GMM_ASSERT1(false, "An upper triangular matrix is expected"); + break; + } + for (size_type i=0; i < n-1; ++i) + if (gmm::real(S(i,i)) <= -default_tol(R()) && + gmm::abs(gmm::imag(S(i,i))) <= default_tol(R())) { + GMM_ASSERT1(false, "Principal matrix logarithm is not defined " + "for matrices with negative eigenvalues"); + break; + } + + // Algorithm 11.9 in "Function of matrices", by N. Higham + R theta[] = { R(0),R(0),R(1.61e-2),R(5.38e-2),R(1.13e-1),R(1.86e-1),R(2.6429608311114350e-1) }; + + R scaling(1); + size_type p(0), m(6), opt_iters(100); + for (size_type k=0; k < opt_iters; ++k, scaling *= R(2)) { + dense_matrix auxS(S); + for (size_type i = 0; i < n; ++i) auxS(i,i) -= R(1); + R tau = gmm::mat_norm1(auxS); + if (tau <= theta[6]) { + ++p; + size_type j1(6), j2(6); + for (size_type j=0; j < 6; ++j) + if (tau <= theta[j]) { j1 = j; break; } + for (size_type j=0; j < j1; ++j) + if (tau <= 2*theta[j]) { j2 = j; break; } + if (j1 - j2 <= 1 || p == 2) { m = j1; break; } + } + sqrtm_utri_inplace(S); + if (k == opt_iters-1) + GMM_WARNING1 ("Maximum number of square roots exceeded; " + "the calculated matrix logarithm may still be accurate"); + } + + for (size_type i = 0; i < n; ++i) S(i,i) -= R(1); + + if (m > 0) { + + std::vector nodes, wts; + switch(m) { + case 0: { + R nodes_[] = { R(0.5) }; + R wts_[] = { R(1) }; + nodes.assign(nodes_, nodes_+m+1); + wts.assign(wts_, wts_+m+1); + } break; + case 1: { + R nodes_[] = { R(0.211324865405187),R(0.788675134594813) }; + R wts_[] = { R(0.5),R(0.5) }; + nodes.assign(nodes_, nodes_+m+1); + wts.assign(wts_, wts_+m+1); + } break; + case 2: { + R nodes_[] = { R(0.112701665379258),R(0.500000000000000),R(0.887298334620742) }; + R wts_[] = { R(0.277777777777778),R(0.444444444444444),R(0.277777777777778) }; + nodes.assign(nodes_, nodes_+m+1); + wts.assign(wts_, wts_+m+1); + } break; + case 3: { + R nodes_[] = { R(0.0694318442029737),R(0.3300094782075718),R(0.6699905217924281),R(0.9305681557970263) }; + R wts_[] = { R(0.173927422568727),R(0.326072577431273),R(0.326072577431273),R(0.173927422568727) }; + nodes.assign(nodes_, nodes_+m+1); + wts.assign(wts_, wts_+m+1); + } break; + case 4: { + R nodes_[] = { R(0.0469100770306681),R(0.2307653449471584),R(0.5000000000000000),R(0.7692346550528415),R(0.9530899229693319) }; + R wts_[] = { R(0.118463442528095),R(0.239314335249683),R(0.284444444444444),R(0.239314335249683),R(0.118463442528094) }; + nodes.assign(nodes_, nodes_+m+1); + wts.assign(wts_, wts_+m+1); + } break; + case 5: { + R nodes_[] = { R(0.0337652428984240),R(0.1693953067668678),R(0.3806904069584015),R(0.6193095930415985),R(0.8306046932331322),R(0.9662347571015761) }; + R wts_[] = { R(0.0856622461895853),R(0.1803807865240693),R(0.2339569672863452),R(0.2339569672863459),R(0.1803807865240693),R(0.0856622461895852) }; + nodes.assign(nodes_, nodes_+m+1); + wts.assign(wts_, wts_+m+1); + } break; + case 6: { + R nodes_[] = { R(0.0254460438286208),R(0.1292344072003028),R(0.2970774243113015),R(0.4999999999999999),R(0.7029225756886985),R(0.8707655927996973),R(0.9745539561713792) }; + R wts_[] = { R(0.0647424830844348),R(0.1398526957446384),R(0.1909150252525594),R(0.2089795918367343),R(0.1909150252525595),R(0.1398526957446383),R(0.0647424830844349) }; + nodes.assign(nodes_, nodes_+m+1); + wts.assign(wts_, wts_+m+1); + } break; + } + + dense_matrix auxS1(S), auxS2(S); + std::vector auxvec(n); + gmm::clear(S); + for (size_type j=0; j <= m; ++j) { + gmm::copy(gmm::scaled(auxS1, nodes[j]), auxS2); + gmm::add(gmm::identity_matrix(), auxS2); + // S += wts[i] * auxS1 * inv(auxS2) + for (size_type i=0; i < n; ++i) { + gmm::copy(gmm::mat_row(auxS1, i), auxvec); + gmm::lower_tri_solve(gmm::transposed(auxS2), auxvec, false); + gmm::add(gmm::scaled(auxvec, wts[j]), gmm::mat_row(S, i)); + } + } + } + gmm::scale(S, scaling); + } + + /** + Matrix logarithm (from GNU/Octave) + */ + template + void logm(const dense_matrix& A, dense_matrix& LOGMA) + { + typedef typename number_traits::magnitude_type R; + size_type n = gmm::mat_nrows(A); + GMM_ASSERT1(n == gmm::mat_ncols(A), + "Matrix logarithm is not defined for non-square matrices"); + dense_matrix S(A), Q(A); + #if defined(GMM_USES_LAPACK) + schur(A, S, Q); // A = Q * S * Q^T + #else + GMM_ASSERT1(false, "Please recompile with lapack and blas librairies " + "to use logm matrix function."); + #endif + + bool convert_to_complex(false); + if (!is_complex(T())) + for (size_type i=0; i < n-1; ++i) + if (gmm::abs(S(i+1,i)) > default_tol(T())) { + convert_to_complex = true; + break; + } + + gmm::resize(LOGMA, n, n); + if (convert_to_complex) { + dense_matrix > cS(n,n), cQ(n,n), auxmat(n,n); + gmm::copy(gmm::real_part(S), gmm::real_part(cS)); + gmm::copy(gmm::real_part(Q), gmm::real_part(cQ)); + block2x2_reduction(cS, cQ, default_tol(R())*R(3)); + for (size_type j=0; j < n-1; ++j) + for (size_type i=j+1; i < n; ++i) + cS(i,j) = T(0); + logm_utri_inplace(cS); + gmm::mult(cQ, cS, auxmat); + gmm::mult(auxmat, gmm::transposed(cQ), cS); + // Remove small complex values which may have entered calculation + gmm::copy(gmm::real_part(cS), LOGMA); +// GMM_ASSERT1(gmm::mat_norm1(gmm::imag_part(cS)) < n*default_tol(T()), +// "Internal error, imag part should be zero"); + } else { + dense_matrix auxmat(n,n); + logm_utri_inplace(S); + gmm::mult(Q, S, auxmat); + gmm::mult(auxmat, gmm::transposed(Q), LOGMA); + } + + } + +} + +#endif + diff --git a/src/linear_algebra/gmm/gmm_dense_qr.h b/src/linear_algebra/gmm/gmm_dense_qr.h new file mode 100644 index 00000000..1fcf03f5 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_dense_qr.h @@ -0,0 +1,789 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2003-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_dense_qr.h + @author Caroline Lecalvez, Caroline.Lecalvez@gmm.insa-tlse.fr, Yves Renard + @date September 12, 2003. + @brief Dense QR factorization. +*/ +#ifndef GMM_DENSE_QR_H +#define GMM_DENSE_QR_H + +#include "gmm_dense_Householder.h" + +namespace gmm { + + + /** + QR factorization using Householder method (complex and real version). + */ + template + void qr_factor(const MAT1 &A_) { + MAT1 &A = const_cast(A_); + typedef typename linalg_traits::value_type value_type; + + size_type m = mat_nrows(A), n = mat_ncols(A); + GMM_ASSERT2(m >= n, "dimensions mismatch"); + + std::vector W(m), V(m); + + for (size_type j = 0; j < n; ++j) { + sub_interval SUBI(j, m-j), SUBJ(j, n-j); + V.resize(m-j); W.resize(n-j); + + for (size_type i = j; i < m; ++i) V[i-j] = A(i, j); + house_vector(V); + + row_house_update(sub_matrix(A, SUBI, SUBJ), V, W); + for (size_type i = j+1; i < m; ++i) A(i, j) = V[i-j]; + } + } + + + // QR comes from QR_factor(QR) where the upper triangular part stands for R + // and the lower part contains the Householder reflectors. + // A <- AQ + template + void apply_house_right(const MAT1 &QR, const MAT2 &A_) { + MAT2 &A = const_cast(A_); + typedef typename linalg_traits::value_type T; + size_type m = mat_nrows(QR), n = mat_ncols(QR); + GMM_ASSERT2(m == mat_ncols(A), "dimensions mismatch"); + if (m == 0) return; + std::vector V(m), W(mat_nrows(A)); + V[0] = T(1); + for (size_type j = 0; j < n; ++j) { + V.resize(m-j); + for (size_type i = j+1; i < m; ++i) V[i-j] = QR(i, j); + col_house_update(sub_matrix(A, sub_interval(0, mat_nrows(A)), + sub_interval(j, m-j)), V, W); + } + } + + // QR comes from QR_factor(QR) where the upper triangular part stands for R + // and the lower part contains the Householder reflectors. + // A <- Q*A + template + void apply_house_left(const MAT1 &QR, const MAT2 &A_) { + MAT2 &A = const_cast(A_); + typedef typename linalg_traits::value_type T; + size_type m = mat_nrows(QR), n = mat_ncols(QR); + GMM_ASSERT2(m == mat_nrows(A), "dimensions mismatch"); + if (m == 0) return; + std::vector V(m), W(mat_ncols(A)); + V[0] = T(1); + for (size_type j = 0; j < n; ++j) { + V.resize(m-j); + for (size_type i = j+1; i < m; ++i) V[i-j] = QR(i, j); + row_house_update(sub_matrix(A, sub_interval(j, m-j), + sub_interval(0, mat_ncols(A))), V, W); + } + } + + /** Compute the QR factorization, where Q is assembled. */ + template + void qr_factor(const MAT1 &A, const MAT2 &QQ, const MAT3 &RR) { + MAT2 &Q = const_cast(QQ); MAT3 &R = const_cast(RR); + typedef typename linalg_traits::value_type value_type; + + size_type m = mat_nrows(A), n = mat_ncols(A); + GMM_ASSERT2(m >= n, "dimensions mismatch"); + gmm::copy(A, Q); + + std::vector W(m); + dense_matrix VV(m, n); + + for (size_type j = 0; j < n; ++j) { + sub_interval SUBI(j, m-j), SUBJ(j, n-j); + + for (size_type i = j; i < m; ++i) VV(i,j) = Q(i, j); + house_vector(sub_vector(mat_col(VV,j), SUBI)); + + row_house_update(sub_matrix(Q, SUBI, SUBJ), + sub_vector(mat_col(VV,j), SUBI), sub_vector(W, SUBJ)); + } + + gmm::copy(sub_matrix(Q, sub_interval(0, n), sub_interval(0, n)), R); + gmm::copy(identity_matrix(), Q); + + for (size_type j = n-1; j != size_type(-1); --j) { + sub_interval SUBI(j, m-j), SUBJ(j, n-j); + row_house_update(sub_matrix(Q, SUBI, SUBJ), + sub_vector(mat_col(VV,j), SUBI), sub_vector(W, SUBJ)); + } + } + + ///@cond DOXY_SHOW_ALL_FUNCTIONS + template + void extract_eig(const MAT &A, VECT &V, Ttol tol, TA, TV) { + size_type n = mat_nrows(A); + if (n == 0) return; + tol *= Ttol(2); + Ttol tol_i = tol * gmm::abs(A(0,0)), tol_cplx = tol_i; + for (size_type i = 0; i < n; ++i) { + if (i < n-1) { + tol_i = (gmm::abs(A(i,i))+gmm::abs(A(i+1,i+1)))*tol; + tol_cplx = std::max(tol_cplx, tol_i); + } + if ((i < n-1) && gmm::abs(A(i+1,i)) >= tol_i) { + TA tr = A(i,i) + A(i+1, i+1); + TA det = A(i,i)*A(i+1, i+1) - A(i,i+1)*A(i+1, i); + TA delta = tr*tr - TA(4) * det; + if (delta < -tol_cplx) { + GMM_WARNING1("A complex eigenvalue has been detected : " + << std::complex(tr/TA(2), gmm::sqrt(-delta)/TA(2))); + V[i] = V[i+1] = tr / TA(2); + } + else { + delta = std::max(TA(0), delta); + V[i ] = TA(tr + gmm::sqrt(delta))/ TA(2); + V[i+1] = TA(tr - gmm::sqrt(delta))/ TA(2); + } + ++i; + } + else + V[i] = TV(A(i,i)); + } + } + + template + void extract_eig(const MAT &A, VECT &V, Ttol tol, TA, std::complex) { + size_type n = mat_nrows(A); + tol *= Ttol(2); + for (size_type i = 0; i < n; ++i) + if ((i == n-1) || + gmm::abs(A(i+1,i)) < (gmm::abs(A(i,i))+gmm::abs(A(i+1,i+1)))*tol) + V[i] = std::complex(A(i,i)); + else { + TA tr = A(i,i) + A(i+1, i+1); + TA det = A(i,i)*A(i+1, i+1) - A(i,i+1)*A(i+1, i); + TA delta = tr*tr - TA(4) * det; + if (delta < TA(0)) { + V[i] = std::complex(tr / TA(2), gmm::sqrt(-delta) / TA(2)); + V[i+1] = std::complex(tr / TA(2), -gmm::sqrt(-delta)/ TA(2)); + } + else { + V[i ] = TA(tr + gmm::sqrt(delta)) / TA(2); + V[i+1] = TA(tr - gmm::sqrt(delta)) / TA(2); + } + ++i; + } + } + + template + void extract_eig(const MAT &A, VECT &V, Ttol tol, std::complex, TV) { + typedef std::complex T; + size_type n = mat_nrows(A); + if (n == 0) return; + tol *= Ttol(2); + Ttol tol_i = tol * gmm::abs(A(0,0)), tol_cplx = tol_i; + for (size_type i = 0; i < n; ++i) { + if (i < n-1) { + tol_i = (gmm::abs(A(i,i))+gmm::abs(A(i+1,i+1)))*tol; + tol_cplx = std::max(tol_cplx, tol_i); + } + if ((i == n-1) || gmm::abs(A(i+1,i)) < tol_i) { + if (gmm::abs(std::imag(A(i,i))) > tol_cplx) + GMM_WARNING1("A complex eigenvalue has been detected : " + << T(A(i,i)) << " : " << gmm::abs(std::imag(A(i,i))) + / gmm::abs(std::real(A(i,i))) << " : " << tol_cplx); + V[i] = std::real(A(i,i)); + } + else { + T tr = A(i,i) + A(i+1, i+1); + T det = A(i,i)*A(i+1, i+1) - A(i,i+1)*A(i+1, i); + T delta = tr*tr - TA(4) * det; + T a1 = (tr + gmm::sqrt(delta)) / TA(2); + T a2 = (tr - gmm::sqrt(delta)) / TA(2); + if (gmm::abs(std::imag(a1)) > tol_cplx) + GMM_WARNING1("A complex eigenvalue has been detected : " << a1); + if (gmm::abs(std::imag(a2)) > tol_cplx) + GMM_WARNING1("A complex eigenvalue has been detected : " << a2); + + V[i] = std::real(a1); V[i+1] = std::real(a2); + ++i; + } + } + } + + template + void extract_eig(const MAT &A, VECT &V, Ttol tol, + std::complex, std::complex) { + size_type n = mat_nrows(A); + tol *= Ttol(2); + for (size_type i = 0; i < n; ++i) + if ((i == n-1) || + gmm::abs(A(i+1,i)) < (gmm::abs(A(i,i))+gmm::abs(A(i+1,i+1)))*tol) + V[i] = std::complex(A(i,i)); + else { + std::complex tr = A(i,i) + A(i+1, i+1); + std::complex det = A(i,i)*A(i+1, i+1) - A(i,i+1)*A(i+1, i); + std::complex delta = tr*tr - TA(4) * det; + V[i] = (tr + gmm::sqrt(delta)) / TA(2); + V[i+1] = (tr - gmm::sqrt(delta)) / TA(2); + ++i; + } + } + + ///@endcond + /** + Compute eigenvalue vector. + */ + template inline + void extract_eig(const MAT &A, const VECT &V, Ttol tol) { + extract_eig(A, const_cast(V), tol, + typename linalg_traits::value_type(), + typename linalg_traits::value_type()); + } + + /* ********************************************************************* */ + /* Stop criterion for QR algorithms */ + /* ********************************************************************* */ + + template + void qr_stop_criterion(MAT &A, size_type &p, size_type &q, Ttol tol) { + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + R rmin = default_min(R()) * R(2); + size_type n = mat_nrows(A); + if (n <= 2) { q = n; p = 0; } + else { + for (size_type i = 1; i < n-q; ++i) + if (gmm::abs(A(i,i-1)) < (gmm::abs(A(i,i))+ gmm::abs(A(i-1,i-1)))*tol + || gmm::abs(A(i,i-1)) < rmin) + A(i,i-1) = T(0); + + while ((q < n-1 && A(n-1-q, n-2-q) == T(0)) || + (q < n-2 && A(n-2-q, n-3-q) == T(0))) ++q; + if (q >= n-2) q = n; + p = n-q; if (p) --p; if (p) --p; + while (p > 0 && A(p,p-1) != T(0)) --p; + } + } + + template inline + void symmetric_qr_stop_criterion(const MAT &AA, size_type &p, size_type &q, + Ttol tol) { + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + R rmin = default_min(R()) * R(2); + MAT& A = const_cast(AA); + size_type n = mat_nrows(A); + if (n <= 1) { q = n; p = 0; } + else { + for (size_type i = 1; i < n-q; ++i) + if (gmm::abs(A(i,i-1)) < (gmm::abs(A(i,i))+ gmm::abs(A(i-1,i-1)))*tol + || gmm::abs(A(i,i-1)) < rmin) + A(i,i-1) = T(0); + + while (q < n-1 && A(n-1-q, n-2-q) == T(0)) ++q; + if (q >= n-1) q = n; + p = n-q; if (p) --p; if (p) --p; + while (p > 0 && A(p,p-1) != T(0)) --p; + } + } + + template inline + void symmetric_qr_stop_criterion(const VECT1 &diag, const VECT2 &sdiag_, + size_type &p, size_type &q, Ttol tol) { + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + R rmin = default_min(R()) * R(2); + VECT2 &sdiag = const_cast(sdiag_); + size_type n = vect_size(diag); + if (n <= 1) { q = n; p = 0; return; } + for (size_type i = 1; i < n-q; ++i) + if (gmm::abs(sdiag[i-1]) < (gmm::abs(diag[i])+ gmm::abs(diag[i-1]))*tol + || gmm::abs(sdiag[i-1]) < rmin) + sdiag[i-1] = T(0); + while (q < n-1 && sdiag[n-2-q] == T(0)) ++q; + if (q >= n-1) q = n; + p = n-q; if (p) --p; if (p) --p; + while (p > 0 && sdiag[p-1] != T(0)) --p; + } + + /* ********************************************************************* */ + /* 2x2 blocks reduction for Schur vectors */ + /* ********************************************************************* */ + + template + void block2x2_reduction(MATH &H, MATQ &Q, Ttol tol) { + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + + size_type n = mat_nrows(H), nq = mat_nrows(Q); + if (n < 2) return; + sub_interval SUBQ(0, nq), SUBL(0, 2); + std::vector v(2), w(std::max(n, nq)); v[0] = T(1); + tol *= Ttol(2); + Ttol tol_i = tol * gmm::abs(H(0,0)), tol_cplx = tol_i; + for (size_type i = 0; i < n-1; ++i) { + tol_i = (gmm::abs(H(i,i))+gmm::abs(H(i+1,i+1)))*tol; + tol_cplx = std::max(tol_cplx, tol_i); + + if (gmm::abs(H(i+1,i)) > tol_i) { // 2x2 block detected + T tr = (H(i+1, i+1) - H(i,i)) / T(2); + T delta = tr*tr + H(i,i+1)*H(i+1, i); + + if (is_complex(T()) || gmm::real(delta) >= R(0)) { + sub_interval SUBI(i, 2); + T theta = (tr - gmm::sqrt(delta)) / H(i+1,i); + R a = gmm::abs(theta); + v[1] = (a == R(0)) ? T(-1) + : gmm::conj(theta) * (R(1) - gmm::sqrt(a*a + R(1)) / a); + row_house_update(sub_matrix(H, SUBI), v, sub_vector(w, SUBL)); + col_house_update(sub_matrix(H, SUBI), v, sub_vector(w, SUBL)); + col_house_update(sub_matrix(Q, SUBQ, SUBI), v, sub_vector(w, SUBQ)); + } + ++i; + } + } + } + + /* ********************************************************************* */ + /* Basic qr algorithm. */ + /* ********************************************************************* */ + + #define tol_type_for_qr typename number_traits::value_type>::magnitude_type + #define default_tol_for_qr \ + (gmm::default_tol(tol_type_for_qr()) * tol_type_for_qr(3)) + + // QR method for real or complex square matrices based on QR factorisation. + // eigval has to be a complex vector if A has complex eigeinvalues. + // Very slow method. Use implicit_qr_method instead. + template + void rudimentary_qr_algorithm(const MAT1 &A, const VECT &eigval_, + const MAT2 &eigvect_, + tol_type_for_qr tol = default_tol_for_qr, + bool compvect = true) { + VECT &eigval = const_cast(eigval_); + MAT2 &eigvect = const_cast(eigvect_); + + typedef typename linalg_traits::value_type value_type; + + size_type n = mat_nrows(A), p, q = 0, ite = 0; + dense_matrix Q(n, n), R(n,n), A1(n,n); + gmm::copy(A, A1); + + Hessenberg_reduction(A1, eigvect, compvect); + qr_stop_criterion(A1, p, q, tol); + + while (q < n) { + qr_factor(A1, Q, R); + gmm::mult(R, Q, A1); + if (compvect) { gmm::mult(eigvect, Q, R); gmm::copy(R, eigvect); } + + qr_stop_criterion(A1, p, q, tol); + ++ite; + GMM_ASSERT1(ite < n*1000, "QR algorithm failed"); + } + if (compvect) block2x2_reduction(A1, Q, tol); + extract_eig(A1, eigval, tol); + } + + template + void rudimentary_qr_algorithm(const MAT1 &a, VECT &eigval, + tol_type_for_qr tol = default_tol_for_qr) { + dense_matrix::value_type> m(0,0); + rudimentary_qr_algorithm(a, eigval, m, tol, false); + } + + /* ********************************************************************* */ + /* Francis QR step. */ + /* ********************************************************************* */ + + template + void Francis_qr_step(const MAT1& HH, const MAT2 &QQ, bool compute_Q) { + MAT1& H = const_cast(HH); MAT2& Q = const_cast(QQ); + typedef typename linalg_traits::value_type value_type; + size_type n = mat_nrows(H), nq = mat_nrows(Q); + + std::vector v(3), w(std::max(n, nq)); + + value_type s = H(n-2, n-2) + H(n-1, n-1); + value_type t = H(n-2, n-2) * H(n-1, n-1) - H(n-2, n-1) * H(n-1, n-2); + value_type x = H(0, 0) * H(0, 0) + H(0,1) * H(1, 0) - s * H(0,0) + t; + value_type y = H(1, 0) * (H(0,0) + H(1,1) - s); + value_type z = H(1, 0) * H(2, 1); + + sub_interval SUBQ(0, nq); + + for (size_type k = 0; k < n - 2; ++k) { + v[0] = x; v[1] = y; v[2] = z; + house_vector(v); + size_type r = std::min(k+4, n), q = (k==0) ? 0 : k-1; + sub_interval SUBI(k, 3), SUBJ(0, r), SUBK(q, n-q); + + row_house_update(sub_matrix(H, SUBI, SUBK), v, sub_vector(w, SUBK)); + col_house_update(sub_matrix(H, SUBJ, SUBI), v, sub_vector(w, SUBJ)); + + if (compute_Q) + col_house_update(sub_matrix(Q, SUBQ, SUBI), v, sub_vector(w, SUBQ)); + + x = H(k+1, k); y = H(k+2, k); + if (k < n-3) z = H(k+3, k); + } + sub_interval SUBI(n-2,2), SUBJ(0, n), SUBK(n-3,3), SUBL(0, 3); + v.resize(2); + v[0] = x; v[1] = y; + house_vector(v); + row_house_update(sub_matrix(H, SUBI, SUBK), v, sub_vector(w, SUBL)); + col_house_update(sub_matrix(H, SUBJ, SUBI), v, sub_vector(w, SUBJ)); + if (compute_Q) + col_house_update(sub_matrix(Q, SUBQ, SUBI), v, sub_vector(w, SUBQ)); + } + + /* ********************************************************************* */ + /* Wilkinson Double shift QR step (from Lapack). */ + /* ********************************************************************* */ + + template + void Wilkinson_double_shift_qr_step(const MAT1& HH, const MAT2 &QQ, + Ttol tol, bool exc, bool compute_Q) { + MAT1& H = const_cast(HH); MAT2& Q = const_cast(QQ); + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + + size_type n = mat_nrows(H), nq = mat_nrows(Q), m; + std::vector v(3), w(std::max(n, nq)); + const R dat1(0.75), dat2(-0.4375); + T h33, h44, h43h34, v1(0), v2(0), v3(0); + + if (exc) { /* Exceptional shift. */ + R s = gmm::abs(H(n-1, n-2)) + gmm::abs(H(n-2, n-3)); + h33 = h44 = dat1 * s; + h43h34 = dat2*s*s; + } + else { /* Wilkinson double shift. */ + h44 = H(n-1,n-1); h33 = H(n-2, n-2); + h43h34 = H(n-1, n-2) * H(n-2, n-1); + } + + /* Look for two consecutive small subdiagonal elements. */ + /* Determine the effect of starting the double-shift QR iteration at */ + /* row m, and see if this would make H(m-1, m-2) negligible. */ + for (m = n-2; m != 0; --m) { + T h11 = H(m-1, m-1), h22 = H(m, m); + T h21 = H(m, m-1), h12 = H(m-1, m); + T h44s = h44 - h11, h33s = h33 - h11; + v1 = (h33s*h44s-h43h34) / h21 + h12; + v2 = h22 - h11 - h33s - h44s; + v3 = H(m+1, m); + R s = gmm::abs(v1) + gmm::abs(v2) + gmm::abs(v3); + v1 /= s; v2 /= s; v3 /= s; + if (m == 1) break; + T h00 = H(m-2, m-2); + T h10 = H(m-1, m-2); + R tst1 = gmm::abs(v1)*(gmm::abs(h00)+gmm::abs(h11)+gmm::abs(h22)); + if (gmm::abs(h10)*(gmm::abs(v2)+gmm::abs(v3)) <= tol * tst1) break; + } + + /* Double shift QR step. */ + sub_interval SUBQ(0, nq); + for (size_type k = (m == 0) ? 0 : m-1; k < n-2; ++k) { + v[0] = v1; v[1] = v2; v[2] = v3; + house_vector(v); + size_type r = std::min(k+4, n), q = (k==0) ? 0 : k-1; + sub_interval SUBI(k, 3), SUBJ(0, r), SUBK(q, n-q); + + row_house_update(sub_matrix(H, SUBI, SUBK), v, sub_vector(w, SUBK)); + col_house_update(sub_matrix(H, SUBJ, SUBI), v, sub_vector(w, SUBJ)); + if (k > m-1) { H(k+1, k-1) = T(0); if (k < n-3) H(k+2, k-1) = T(0); } + + if (compute_Q) + col_house_update(sub_matrix(Q, SUBQ, SUBI), v, sub_vector(w, SUBQ)); + + v1 = H(k+1, k); v2 = H(k+2, k); + if (k < n-3) v3 = H(k+3, k); + } + sub_interval SUBI(n-2,2), SUBJ(0, n), SUBK(n-3,3), SUBL(0, 3); + v.resize(2); v[0] = v1; v[1] = v2; + house_vector(v); + row_house_update(sub_matrix(H, SUBI, SUBK), v, sub_vector(w, SUBL)); + col_house_update(sub_matrix(H, SUBJ, SUBI), v, sub_vector(w, SUBJ)); + if (compute_Q) + col_house_update(sub_matrix(Q, SUBQ, SUBI), v, sub_vector(w, SUBQ)); + } + + /* ********************************************************************* */ + /* Implicit QR algorithm. */ + /* ********************************************************************* */ + + // QR method for real or complex square matrices based on an + // implicit QR factorisation. eigval has to be a complex vector + // if A has complex eigenvalues. Complexity about 10n^3, 25n^3 if + // eigenvectors are computed + template + void implicit_qr_algorithm(const MAT1 &A, const VECT &eigval_, + const MAT2 &Q_, + tol_type_for_qr tol = default_tol_for_qr, + bool compvect = true) { + VECT &eigval = const_cast(eigval_); + MAT2 &Q = const_cast(Q_); + typedef typename linalg_traits::value_type value_type; + + size_type n(mat_nrows(A)), q(0), q_old, p(0), ite(0), its(0); + dense_matrix H(n,n); + sub_interval SUBK(0,0); + + gmm::copy(A, H); + Hessenberg_reduction(H, Q, compvect); + qr_stop_criterion(H, p, q, tol); + + while (q < n) { + sub_interval SUBI(p, n-p-q), SUBJ(0, mat_ncols(Q)); + if (compvect) SUBK = SUBI; +// Francis_qr_step(sub_matrix(H, SUBI), +// sub_matrix(Q, SUBJ, SUBK), compvect); + Wilkinson_double_shift_qr_step(sub_matrix(H, SUBI), + sub_matrix(Q, SUBJ, SUBK), + tol, (its == 10 || its == 20), compvect); + q_old = q; + qr_stop_criterion(H, p, q, tol*2); + if (q != q_old) its = 0; + ++its; ++ite; + GMM_ASSERT1(ite < n*100, "QR algorithm failed"); + } + if (compvect) block2x2_reduction(H, Q, tol); + extract_eig(H, eigval, tol); + } + + + template + void implicit_qr_algorithm(const MAT1 &a, VECT &eigval, + tol_type_for_qr tol = default_tol_for_qr) { + dense_matrix::value_type> m(1,1); + implicit_qr_algorithm(a, eigval, m, tol, false); + } + + /* ********************************************************************* */ + /* Implicit symmetric QR step with Wilkinson Shift. */ + /* ********************************************************************* */ + + template + void symmetric_Wilkinson_qr_step(const MAT1& MM, const MAT2 &ZZ, + bool compute_z) { + MAT1& M = const_cast(MM); MAT2& Z = const_cast(ZZ); + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + size_type n = mat_nrows(M); + + for (size_type i = 0; i < n; ++i) { + M(i, i) = T(gmm::real(M(i, i))); + if (i > 0) { + T a = (M(i, i-1) + gmm::conj(M(i-1, i)))/R(2); + M(i, i-1) = a; M(i-1, i) = gmm::conj(a); + } + } + + R d = gmm::real(M(n-2, n-2) - M(n-1, n-1)) / R(2); + R e = gmm::abs_sqr(M(n-1, n-2)); + R nu = d + gmm::sgn(d)*gmm::sqrt(d*d+e); + if (nu == R(0)) { M(n-1, n-2) = T(0); return; } + R mu = gmm::real(M(n-1, n-1)) - e / nu; + T x = M(0,0) - T(mu), z = M(1, 0), c, s; + + for (size_type k = 1; k < n; ++k) { + Givens_rotation(x, z, c, s); + + if (k > 1) Apply_Givens_rotation_left(M(k-1,k-2), M(k,k-2), c, s); + Apply_Givens_rotation_left(M(k-1,k-1), M(k,k-1), c, s); + Apply_Givens_rotation_left(M(k-1,k ), M(k,k ), c, s); + if (k < n-1) Apply_Givens_rotation_left(M(k-1,k+1), M(k,k+1), c, s); + if (k > 1) Apply_Givens_rotation_right(M(k-2,k-1), M(k-2,k), c, s); + Apply_Givens_rotation_right(M(k-1,k-1), M(k-1,k), c, s); + Apply_Givens_rotation_right(M(k ,k-1), M(k,k) , c, s); + if (k < n-1) Apply_Givens_rotation_right(M(k+1,k-1), M(k+1,k), c, s); + + if (compute_z) col_rot(Z, c, s, k-1, k); + if (k < n-1) { x = M(k, k-1); z = M(k+1, k-1); } + } + + } + + template + void symmetric_Wilkinson_qr_step(const VECT1& diag_, const VECT2& sdiag_, + const MAT &ZZ, bool compute_z) { + VECT1& diag = const_cast(diag_); + VECT2& sdiag = const_cast(sdiag_); + MAT& Z = const_cast(ZZ); + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + + size_type n = vect_size(diag); + R d = (diag[n-2] - diag[n-1]) / R(2); + R e = gmm::abs_sqr(sdiag[n-2]); + R nu = d + gmm::sgn(d)*gmm::sqrt(d*d+e); + if (nu == R(0)) { sdiag[n-2] = T(0); return; } + R mu = diag[n-1] - e / nu; + T x = diag[0] - T(mu), z = sdiag[0], c, s; + + T a01(0), a02(0); + T a10(0), a11(diag[0]), a12(gmm::conj(sdiag[0])), a13(0); + T a20(0), a21(sdiag[0]), a22(diag[1]), a23(gmm::conj(sdiag[1])); + T a31(0), a32(sdiag[1]); + + for (size_type k = 1; k < n; ++k) { + Givens_rotation(x, z, c, s); + + if (k > 1) Apply_Givens_rotation_left(a10, a20, c, s); + Apply_Givens_rotation_left(a11, a21, c, s); + Apply_Givens_rotation_left(a12, a22, c, s); + if (k < n-1) Apply_Givens_rotation_left(a13, a23, c, s); + + if (k > 1) Apply_Givens_rotation_right(a01, a02, c, s); + Apply_Givens_rotation_right(a11, a12, c, s); + Apply_Givens_rotation_right(a21, a22, c, s); + if (k < n-1) Apply_Givens_rotation_right(a31, a32, c, s); + + if (compute_z) col_rot(Z, c, s, k-1, k); + + diag[k-1] = gmm::real(a11); + diag[k] = gmm::real(a22); + if (k > 1) sdiag[k-2] = (gmm::conj(a01) + a10) / R(2); + sdiag[k-1] = (gmm::conj(a12) + a21) / R(2); + + x = sdiag[k-1]; z = (gmm::conj(a13) + a31) / R(2); + + a01 = a12; a02 = a13; + a10 = a21; a11 = a22; a12 = a23; a13 = T(0); + a20 = a31; a21 = a32; a31 = T(0); + + if (k < n-1) { + sdiag[k] = (gmm::conj(a23) + a32) / R(2); + a22 = T(diag[k+1]); a32 = sdiag[k+1]; a23 = gmm::conj(a32); + } + } + } + + /* ********************************************************************* */ + /* Implicit QR algorithm for symmetric or hermitian matrices. */ + /* ********************************************************************* */ + + // implicit QR method for real square symmetric matrices or complex + // hermitian matrices. + // eigval has to be a complex vector if A has complex eigeinvalues. + // complexity about 4n^3/3, 9n^3 if eigenvectors are computed + template + void symmetric_qr_algorithm_old(const MAT1 &A, const VECT &eigval_, + const MAT2 &eigvect_, + tol_type_for_qr tol = default_tol_for_qr, + bool compvect = true) { + VECT &eigval = const_cast(eigval_); + MAT2 &eigvect = const_cast(eigvect_); + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + + if (compvect) gmm::copy(identity_matrix(), eigvect); + size_type n = mat_nrows(A), q = 0, p, ite = 0; + dense_matrix Tri(n, n); + gmm::copy(A, Tri); + + Householder_tridiagonalization(Tri, eigvect, compvect); + + symmetric_qr_stop_criterion(Tri, p, q, tol); + + while (q < n) { + + sub_interval SUBI(p, n-p-q), SUBJ(0, mat_ncols(eigvect)), SUBK(p, n-p-q); + if (!compvect) SUBK = sub_interval(0,0); + symmetric_Wilkinson_qr_step(sub_matrix(Tri, SUBI), + sub_matrix(eigvect, SUBJ, SUBK), compvect); + + symmetric_qr_stop_criterion(Tri, p, q, tol*R(2)); + ++ite; + GMM_ASSERT1(ite < n*100, "QR algorithm failed. Probably, your matrix" + " is not real symmetric or complex hermitian"); + } + + extract_eig(Tri, eigval, tol); + } + + template + void symmetric_qr_algorithm(const MAT1 &A, const VECT &eigval_, + const MAT2 &eigvect_, + tol_type_for_qr tol = default_tol_for_qr, + bool compvect = true) { + VECT &eigval = const_cast(eigval_); + MAT2 &eigvect = const_cast(eigvect_); + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + + size_type n = mat_nrows(A), q = 0, p, ite = 0; + if (compvect) gmm::copy(identity_matrix(), eigvect); + if (n == 0) return; + if (n == 1) { eigval[0]=gmm::real(A(0,0)); return; } + dense_matrix Tri(n, n); + gmm::copy(A, Tri); + + Householder_tridiagonalization(Tri, eigvect, compvect); + + std::vector diag(n); + std::vector sdiag(n); + for (size_type i = 0; i < n; ++i) + { diag[i] = gmm::real(Tri(i, i)); if (i+1 < n) sdiag[i] = Tri(i+1, i); } + + symmetric_qr_stop_criterion(diag, sdiag, p, q, tol); + + while (q < n) { + sub_interval SUBI(p, n-p-q), SUBJ(0, mat_ncols(eigvect)), SUBK(p, n-p-q); + if (!compvect) SUBK = sub_interval(0,0); + + symmetric_Wilkinson_qr_step(sub_vector(diag, SUBI), + sub_vector(sdiag, SUBI), + sub_matrix(eigvect, SUBJ, SUBK), compvect); + + symmetric_qr_stop_criterion(diag, sdiag, p, q, tol*R(3)); + ++ite; + GMM_ASSERT1(ite < n*100, "QR algorithm failed."); + } + + gmm::copy(diag, eigval); + } + + + template + void symmetric_qr_algorithm(const MAT1 &a, VECT &eigval, + tol_type_for_qr tol = default_tol_for_qr) { + dense_matrix::value_type> m(0,0); + symmetric_qr_algorithm(a, eigval, m, tol, false); + } + + +} + +#endif + diff --git a/src/linear_algebra/gmm/gmm_dense_sylvester.h b/src/linear_algebra/gmm/gmm_dense_sylvester.h new file mode 100644 index 00000000..88c9724d --- /dev/null +++ b/src/linear_algebra/gmm/gmm_dense_sylvester.h @@ -0,0 +1,174 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2003-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/** @file gmm_dense_sylvester.h + @author Yves Renard + @date June 5, 2003. + @brief Sylvester equation solver. +*/ +#ifndef GMM_DENSE_SYLVESTER_H +#define GMM_DENSE_SYLVESTER_H + +#include "gmm_kernel.h" + +namespace gmm { + + /* ********************************************************************* */ + /* Kronecker system matrix. */ + /* ********************************************************************* */ + template + void kron(const MAT1 &m1, const MAT2 &m2, const MAT3 &m3_, + bool init = true) { + MAT3 &m3 = const_cast(m3_); + size_type m = mat_nrows(m1), n = mat_ncols(m1); + size_type l = mat_nrows(m2), k = mat_ncols(m2); + + GMM_ASSERT2(mat_nrows(m3) == m*l && mat_ncols(m3) == n*k, + "dimensions mismatch"); + + for (size_type i = 0; i < m; ++i) + for (size_type j = 0; j < m; ++j) + if (init) + gmm::copy(gmm::scaled(m2, m1(i,j)), + gmm::sub_matrix(m3, sub_interval(l*i, l), + sub_interval(k*j, k))); + else + gmm::add(gmm::scaled(m2, m1(i,j)), + gmm::sub_matrix(m3, sub_interval(l*i, l), + sub_interval(k*j, k))); + } + + + /* ********************************************************************* */ + /* Copy a matrix into a vector. */ + /* ********************************************************************* */ + + template + colmatrix_to_vector(const MAT &A, VECT &v, col_major) { + size_type m = mat_nrows(A), n = mat_ncols(A); + GMM_ASSERT2(m*n == vect_size(v), "dimensions mismatch"); + for (size_type i = 0; i < n; ++i) + gmm::copy(mat_col(A, i), sub_vector(v, sub_interval(i*m, m))); + } + + template + colmatrix_to_vector(const MAT &A, VECT &v, row_and_col) + { colmatrix_to_vector(A, v, col_major()); } + + template + colmatrix_to_vector(const MAT &A, VECT &v, col_and_row) + { colmatrix_to_vector(A, v, col_major()); } + + template + colmatrix_to_vector(const MAT &A, VECT &v, row_major) { + size_type m = mat_nrows(mat), n = mat_ncols(A); + GMM_ASSERT2(m*n == vect_size(v), "dimensions mismatch"); + for (size_type i = 0; i < m; ++i) + gmm::copy(mat_row(A, i), sub_vector(v, sub_slice(i, n, m))); + } + + template inline + colmatrix_to_vector(const MAT &A, const VECT &v_) { + VECT &v = const_cast(v_); + colmatrix_to_vector(A, v, typename linalg_traits::sub_orientation()); + } + + + /* ********************************************************************* */ + /* Copy a vector into a matrix. */ + /* ********************************************************************* */ + + template + vector_to_colmatrix(const VECT &v, MAT &A, col_major) { + size_type m = mat_nrows(A), n = mat_ncols(A); + GMM_ASSERT2(m*n == vect_size(v), "dimensions mismatch"); + for (size_type i = 0; i < n; ++i) + gmm::copy(sub_vector(v, sub_interval(i*m, m)), mat_col(A, i)); + } + + template + vector_to_colmatrix(const VECT &v, MAT &A, row_and_col) + { vector_to_colmatrix(v, A, col_major()); } + + template + vector_to_colmatrix(const VECT &v, MAT &A, col_and_row) + { vector_to_colmatrix(v, A, col_major()); } + + template + vector_to_colmatrix(const VECT &v, MAT &A, row_major) { + size_type m = mat_nrows(mat), n = mat_ncols(A); + GMM_ASSERT2(m*n == vect_size(v), "dimensions mismatch"); + for (size_type i = 0; i < m; ++i) + gmm::copy(sub_vector(v, sub_slice(i, n, m)), mat_row(A, i)); + } + + template inline + vector_to_colmatrix(const VECT &v, const MAT &A_) { + MAT &A = const_cast(A_); + vector_to_colmatrix(v, A, typename linalg_traits::sub_orientation()); + } + + /* ********************************************************************* */ + /* Solve sylvester equation. */ + /* ********************************************************************* */ + + // very prohibitive solver, to be replaced ... + template + void sylvester(const MAT1 &m1, const MAT2 &m2, const MAT3 &m3, + const MAT4 &m4_) { + typedef typename linalg_traits::value_type T; + + MAT3 &m4 = const_cast(m4_); + size_type m = mat_nrows(m1), n = mat_ncols(m1); + size_type l = mat_nrows(m2), k = mat_ncols(m2); + + GMM_ASSERT2(m == n && l == k && m == mat_nrows(m3) && + l == mat_ncols(m3) && m == mat_nrows(m4) && l == mat_ncols(m4), + "dimensions mismatch"); + + gmm::dense_matrix akronb(m*l, m*l); + gmm::dense_matrix idm(m, m), idl(l,l); + gmm::copy(identity_matrix(), idm); + gmm::copy(identity_matrix(), idl); + std::vector x(m*l), c(m*l); + + kron(idl, m1, akronb); + kron(gmm::transposed(m2), idm, akronb, false); + + colmatrix_to_vector(m3, c); + lu_solve(akronb, c, x); + vector_to_colmatrix(x, m4); + + } +} + +#endif + diff --git a/src/linear_algebra/gmm/gmm_domain_decomp.h b/src/linear_algebra/gmm/gmm_domain_decomp.h new file mode 100644 index 00000000..9bf663a2 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_domain_decomp.h @@ -0,0 +1,165 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2004-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/** @file gmm_domain_decomp.h + @author Yves Renard + @date May 21, 2004. + @brief Domain decomposition. +*/ +#ifndef GMM_DOMAIN_DECOMP_H__ +#define GMM_DOMAIN_DECOMP_H__ + +#include "gmm_kernel.h" +#include + + +namespace gmm { + + /** This function separates into small boxes of size msize with a ratio + * of overlap (in [0,1[) a set of points. The result is given into a + * vector of sparse matrices vB. + */ + template + void rudimentary_regular_decomposition(std::vector pts, + double msize, + double overlap, + std::vector &vB) { + typedef typename linalg_traits::value_type value_type; + typedef abstract_null_type void_type; + typedef std::map map_type; + + size_type nbpts = pts.size(); + if (!nbpts || pts[0].size() == 0) { vB.resize(0); return; } + int dim = int(pts[0].size()); + + // computation of the global box and the number of sub-domains + Point pmin = pts[0], pmax = pts[0]; + for (size_type i = 1; i < nbpts; ++i) + for (int k = 0; k < dim; ++k) { + pmin[k] = std::min(pmin[k], pts[i][k]); + pmax[k] = std::max(pmax[k], pts[i][k]); + } + + std::vector nbsub(dim), mult(dim); + std::vector pts1(dim), pts2(dim); + size_type nbtotsub = 1; + for (int k = 0; k < dim; ++k) { + nbsub[k] = size_type((pmax[k] - pmin[k]) / msize)+1; + mult[k] = nbtotsub; nbtotsub *= nbsub[k]; + } + + std::vector subs(nbtotsub); + // points ventilation + std::vector ns(dim), na(dim), nu(dim); + for (size_type i = 0; i < nbpts; ++i) { + for (int k = 0; k < dim; ++k) { + double a = (pts[i][k] - pmin[k]) / msize; + ns[k] = size_type(a) - 1; na[k] = 0; + pts1[k] = int(a + overlap); pts2[k] = int(ceil(a-1.0-overlap)); + } + size_type sum = 0; + do { + bool ok = 1; + for (int k = 0; k < dim; ++k) + if ((ns[k] >= nbsub[k]) || (pts1[k] < int(ns[k])) + || (pts2[k] > int(ns[k]))) { ok = false; break; } + if (ok) { + size_type ind = ns[0]; + for (int k=1; k < dim; ++k) ind += ns[k]*mult[k]; + subs[ind][i] = void_type(); + } + for (int k = 0; k < dim; ++k) { + if (na[k] < 2) { na[k]++; ns[k]++; ++sum; break; } + na[k] = 0; ns[k] -= 2; sum -= 2; + } + } while (sum); + } + // delete too small domains. + size_type nbmaxinsub = 0; + for (size_type i = 0; i < nbtotsub; ++i) + nbmaxinsub = std::max(nbmaxinsub, subs[i].size()); + + std::fill(ns.begin(), ns.end(), size_type(0)); + for (size_type i = 0; i < nbtotsub; ++i) { + if (subs[i].size() > 0 && subs[i].size() < nbmaxinsub / 10) { + + for (int k = 0; k < dim; ++k) nu[k] = ns[k]; + size_type nbmax = 0, imax = 0; + + for (int l = 0; l < dim; ++l) { + nu[l]--; + for (int m = 0; m < 2; ++m, nu[l]+=2) { + bool ok = true; + for (int k = 0; k < dim && ok; ++k) + if (nu[k] >= nbsub[k]) ok = false; + if (ok) { + size_type ind = ns[0]; + for (int k=1; k < dim; ++k) ind += ns[k]*mult[k]; + if (subs[ind].size() > nbmax) + { nbmax = subs[ind].size(); imax = ind; } + } + } + nu[l]--; + } + + if (nbmax > subs[i].size()) { + for (map_type::iterator it=subs[i].begin(); it!=subs[i].end(); ++it) + subs[imax][it->first] = void_type(); + subs[i].clear(); + } + } + for (int k = 0; k < dim; ++k) + { ns[k]++; if (ns[k] < nbsub[k]) break; ns[k] = 0; } + } + + // delete empty domains. + size_type effnb = 0; + for (size_type i = 0; i < nbtotsub; ++i) { + if (subs[i].size() > 0) + { if (i != effnb) std::swap(subs[i], subs[effnb]); ++effnb; } + } + + // build matrices + subs.resize(effnb); + vB.resize(effnb); + for (size_type i = 0; i < effnb; ++i) { + clear(vB[i]); resize(vB[i], nbpts, subs[i].size()); + size_type j = 0; + for (map_type::iterator it=subs[i].begin(); it!=subs[i].end(); ++it, ++j) + vB[i](it->first, j) = value_type(1); + } + } + + +} + + +#endif diff --git a/src/linear_algebra/gmm/gmm_except.h b/src/linear_algebra/gmm/gmm_except.h new file mode 100644 index 00000000..3b38aa16 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_except.h @@ -0,0 +1,406 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/** @file gmm_except.h + @author Yves Renard + @author Julien Pommier + @date September 01, 2002. + @brief Definition of basic exceptions. +*/ + +#ifndef GMM_EXCEPT_H__ +#define GMM_EXCEPT_H__ + +#include +#include "gmm_std.h" +#include "gmm_feedback_management.h" + + +//provides external implementation of gmm_exception and logging. + +namespace gmm { + +/* *********************************************************************** */ +/* GetFEM generic errors. */ +/* *********************************************************************** */ + + // std logic_error with error level information + class gmm_error: public std::logic_error { + public: + gmm_error(const std::string& what_arg, int errorLevel = 1): + std::logic_error (what_arg), errorLevel_(errorLevel) {} + int errLevel() const { return errorLevel_; } + + private: + int errorLevel_; + }; + +#ifdef GETFEM_HAVE_PRETTY_FUNCTION +# define GMM_PRETTY_FUNCTION __PRETTY_FUNCTION__ +#elif _MSC_VER +# define GMM_PRETTY_FUNCTION __FUNCTION__ +#else +# define GMM_PRETTY_FUNCTION "" +#endif + + + // Errors : GMM_THROW should not be used on its own. + // GMM_ASSERT1 : Non-maskable errors. Typically for in/ouput and + // when the test do not significantly reduces the performance. + // GMM_ASSERT2 : All tests which are potentially performance + // consuming. Not hidden by default. Hidden when NDEBUG is + // defined. + // GMM_ASSERT3 : For internal checks. Hidden by default. Active + // only when DEBUG_MODE is defined. + // __EXCEPTIONS is defined by gcc, _CPPUNWIND is defined by visual c++ +#if defined(__EXCEPTIONS) || defined(_CPPUNWIND) + inline void short_error_throw(const char *file, int line, const char *func, + const char *errormsg) { + std::stringstream msg__; + msg__ << "Error in " << file << ", line " << line << " " << func + << ": \n" << errormsg << std::ends; + throw gmm::gmm_error(msg__.str()); + } +# define GMM_THROW_(type, errormsg) { \ + std::stringstream msg__; \ + msg__ << "Error in " << __FILE__ << ", line " \ + << __LINE__ << " " << GMM_PRETTY_FUNCTION << ": \n" \ + << errormsg << std::ends; \ + throw (type)(msg__.str()); \ + } + +# define GMM_THROW_AT_LEVEL(errormsg, level) \ + { \ + std::stringstream msg; \ + msg << "Error in " << __FILE__ << ", line " \ + << __LINE__ << " " << GMM_PRETTY_FUNCTION << ": \n" \ + << errormsg << std::ends; \ + throw gmm::gmm_error(msg.str(), level); \ + } + +#else +#ifndef _MSC_VER +# define abort_no_return() ::abort() +#else +// apparently ::abort() on windows is not declared with __declspec(noreturn) so the compiler spits a lot of warnings when abort is used. +# define abort_no_return() { assert("GMM ABORT"==0); throw "GMM ABORT"; } +#endif + + inline void short_error_throw(const char *file, int line, const char *func, + const char *errormsg) { + std::stringstream msg__; + msg__ << "Error in " << file << ", line " << line << " " << func + << ": \n" << errormsg << std::ends; + std::cerr << msg__.str() << std::endl; + abort_no_return(); + } + +# define GMM_THROW_(type, errormsg) { \ + std::stringstream msg__; \ + msg__ << "Error in " << __FILE__ << ", line " \ + << __LINE__ << " " << GMM_PRETTY_FUNCTION << ": \n" \ + << errormsg; \ + std::cerr << msg__.str() << std::endl; \ + abort_no_return(); \ + } + +# define GMM_THROW_AT_LEVEL(errormsg, level) \ + { \ + std::stringstream msg__; \ + msg__ << "Error in " << __FILE__ << ", line " \ + << __LINE__ << " " << GMM_PRETTY_FUNCTION << ": \n" \ + << errormsg << " at level " << level; \ + std::cerr << msg__.str() << std::endl; \ + abort_no_return(); \ + } +#endif + + +inline void GMM_THROW() {} +#define GMM_THROW(a, b) { GMM_THROW_(a,b); gmm::GMM_THROW(); } + +# define GMM_THROW_DEFAULT(errormsg) GMM_THROW_AT_LEVEL(errormsg, 1) + +// This allows not to compile some assertions +#ifndef GMM_ASSERT_LEVEL +#if defined(NDEBUG) +# define GMM_ASSERT_LEVEL 1 +#elif defined(DEBUG_MODE) +# define GMM_ASSERT_LEVEL 3 +#else +# define GMM_ASSERT_LEVEL 2 +#endif +#endif + + +# define GMM_ASSERT1(test, errormsg) { if (!(test)) GMM_THROW_AT_LEVEL(errormsg, 1); } + +#if GMM_ASSERT_LEVEL < 2 +# define GMM_ASSERT2(test, errormsg) {} +# define GMM_ASSERT3(test, errormsg) {} +#elif GMM_ASSERT_LEVEL < 3 +# define GMM_ASSERT2(test, errormsg){ if (!(test)) GMM_THROW_AT_LEVEL(errormsg, 2); } +# define GMM_ASSERT3(test, errormsg){} +#else +# define GMM_ASSERT2(test, errormsg){ if (!(test)) GMM_THROW_AT_LEVEL(errormsg, 2); } +# define GMM_ASSERT3(test, errormsg){ if (!(test)) GMM_THROW_AT_LEVEL(errormsg, 3); } +#endif + +/* *********************************************************************** */ +/* GetFEM warnings. */ +/* *********************************************************************** */ + + // This allows not to compile some Warnings +#ifndef GMM_WARNING_LEVEL +# define GMM_WARNING_LEVEL 4 +#endif + + // Warning levels : 0 always printed + // 1 very important : specify a possible error in the code. + // 2 important : specify a default of optimization for inst. + // 3 remark + // 4 ignored by default. + +#define GMM_WARNING_MSG(level_, thestr) { \ + std::stringstream msg__; \ + msg__ << "Level " << level_ << " Warning in " << __FILE__ << ", line " \ + << __LINE__ << ": " << thestr; \ + gmm::feedback_manager::manage()->send(msg__.str(), gmm::FeedbackType::WARNING, level_); \ +} + +#define GMM_WARNING0(thestr) GMM_WARNING_MSG(0, thestr) + + +#if GMM_WARNING_LEVEL > 0 +# define GMM_WARNING1(thestr) \ + { if (1 <= gmm::feedback_manager::warning_level()) GMM_WARNING_MSG(1, thestr) } +#else +# define GMM_WARNING1(thestr) {} +#endif + +#if GMM_WARNING_LEVEL > 1 +# define GMM_WARNING2(thestr) \ + { if (2 <= gmm::feedback_manager::warning_level()) GMM_WARNING_MSG(2, thestr) } +#else +# define GMM_WARNING2(thestr) {} +#endif + +#if GMM_WARNING_LEVEL > 2 +# define GMM_WARNING3(thestr) \ + { if (3 <= gmm::feedback_manager::warning_level()) GMM_WARNING_MSG(3, thestr) } +#else +# define GMM_WARNING3(thestr) {} +#endif + +#if GMM_WARNING_LEVEL > 3 +# define GMM_WARNING4(thestr) \ + { if (4 <= gmm::feedback_manager::warning_level()) GMM_WARNING_MSG(4, thestr) } +#else +# define GMM_WARNING4(thestr) {} +#endif + +/* *********************************************************************** */ +/* GetFEM traces. */ +/* *********************************************************************** */ + + // This allow not too compile some Warnings +#ifndef GMM_TRACES_LEVEL +# define GMM_TRACES_LEVEL 4 +#endif + + // Traces levels : 0 always printed + // 1 Susceptible to occur once in a program. + // 2 Susceptible to occur occasionnaly in a program (10). + // 3 Susceptible to occur often (100). + // 4 Susceptible to occur very often (>1000). + +#define GMM_TRACE_MSG_MPI // for Parallelized version +#define GMM_TRACE_MSG(level_, thestr) { \ + GMM_TRACE_MSG_MPI { \ + std::stringstream msg__; \ + msg__ << "Trace " << level_ << " in " << __FILE__ << ", line " \ + << __LINE__ << ": " << thestr; \ + gmm::feedback_manager::send(msg__.str(), gmm::FeedbackType::TRACE, level_); \ + } \ +} + +#define GMM_TRACE_SIMPLE_MSG(level_, thestr) { \ + GMM_TRACE_MSG_MPI { \ + std::stringstream msg__; \ + msg__ << "Trace " << level_ << ": " << thestr; \ + gmm::feedback_manager::send(msg__.str(), gmm::FeedbackType::TRACE, level_); \ + } \ +} + +#define GMM_TRACE0(thestr) GMM_TRACE_MSG(0, thestr) +#define GMM_SIMPLE_TRACE0(thestr) GMM_TRACE_MSG_SIMPLE(0, thestr) + +#if GMM_TRACES_LEVEL > 0 +# define GMM_TRACE1(thestr) \ + { if (1 <= gmm::feedback_manager::traces_level()) GMM_TRACE_MSG(1, thestr) } +# define GMM_SIMPLE_TRACE1(thestr) \ + { if (1 <= gmm::feedback_manager::traces_level()) GMM_TRACE_SIMPLE_MSG(1, thestr) } +#else +# define GMM_TRACE1(thestr) {} +# define GMM_SIMPLE_TRACE1(thestr) {} +#endif + +#if GMM_TRACES_LEVEL > 1 +# define GMM_TRACE2(thestr) \ + { if (2 <= gmm::feedback_manager::traces_level()) GMM_TRACE_MSG(2, thestr) } +# define GMM_SIMPLE_TRACE2(thestr) \ + { if (2 <= gmm::feedback_manager::traces_level()) GMM_TRACE_SIMPLE_MSG(2, thestr) } +#else +# define GMM_TRACE2(thestr) {} +# define GMM_SIMPLE_TRACE2(thestr) {} +#endif + +#if GMM_TRACES_LEVEL > 2 +# define GMM_TRACE3(thestr) \ + { if (3 <= gmm::feedback_manager::traces_level()) GMM_TRACE_MSG(3, thestr) } +# define GMM_SIMPLE_TRACE3(thestr) \ + { if (3 <= gmm::feedback_manager::traces_level()) GMM_TRACE_SIMPLE_MSG(3, thestr) } +#else +# define GMM_TRACE3(thestr) {} +# define GMM_SIMPLE_TRACE3(thestr) {} +#endif + +#if GMM_TRACES_LEVEL > 3 +# define GMM_TRACE4(thestr) \ + { if (4 <= gmm::feedback_manager::traces_level()) GMM_TRACE_MSG(4, thestr) } +# define GMM_SIMPLE_TRACE4(thestr) \ + { if (4 <= gmm::feedback_manager::traces_level()) GMM_TRACE_SIMPLE_MSG(4, thestr) } +#else +# define GMM_TRACE4(thestr) {} +# define GMM_SIMPLE_TRACE4(thestr) {} +#endif + +#define GMM_STANDARD_CATCH_ERROR \ + catch(const gmm::gmm_error &e) \ + { \ + std::stringstream strStream; \ + strStream << "============================================\n"; \ + strStream << "| A GMM error has been detected !!! |\n"; \ + strStream << "============================================\n"; \ + strStream << e.what() << std::endl << std::endl; \ + gmm::feedback_manager::send(strStream.str(), \ + gmm::FeedbackType::ASSERT, e.errLevel()); \ + gmm::feedback_manager::terminating_action(); \ + } \ + catch(const std::logic_error &e) \ + { \ + std::stringstream strStream; \ + strStream << "============================================\n"; \ + strStream << "| An error has been detected !!! |\n"; \ + strStream << "============================================\n"; \ + strStream << e.what() << std::endl << std::endl; \ + gmm::feedback_manager::send(strStream.str(), \ + gmm::FeedbackType::ASSERT, 0); \ + gmm::feedback_manager::terminating_action(); \ + } \ + catch(const std::runtime_error &e) \ + { \ + std::stringstream strStream; \ + strStream << "============================================\n"; \ + strStream << "| A runtime error has been detected !!! |\n"; \ + strStream << "============================================\n"; \ + strStream << e.what() << std::endl << std::endl; \ + gmm::feedback_manager::send(strStream.str(), \ + gmm::FeedbackType::ASSERT, 0); \ + gmm::feedback_manager::terminating_action(); \ + } \ + catch(const std::bad_alloc &) \ + { \ + std::stringstream strStream; \ + strStream << "============================================\n"; \ + strStream << "| A bad allocation has been detected !!! |\n"; \ + strStream << "============================================\n"; \ + gmm::feedback_manager::send(strStream.str(), \ + gmm::FeedbackType::ASSERT, 0); \ + gmm::feedback_manager::terminating_action(); \ + } \ + catch(const std::bad_typeid &) \ + { \ + std::stringstream strStream; \ + strStream << "============================================\n"; \ + strStream << "| A bad typeid has been detected !!! |\n"; \ + strStream << "============================================\n"; \ + gmm::feedback_manager::send(strStream.str(), \ + gmm::FeedbackType::ASSERT, 0); \ + gmm::feedback_manager::terminating_action(); \ + } \ + catch(const std::bad_exception &) \ + { \ + std::stringstream strStream; \ + strStream << "============================================\n"; \ + strStream << "| A bad exception has been detected !!! |\n"; \ + strStream << "============================================\n"; \ + gmm::feedback_manager::send(strStream.str(), \ + gmm::FeedbackType::ASSERT, 0); \ + gmm::feedback_manager::terminating_action(); \ + } \ + catch(const std::bad_cast &) \ + { \ + std::stringstream strStream; \ + strStream << "============================================\n"; \ + strStream << "| A bad_cast has been detected !!! |\n"; \ + strStream << "============================================\n"; \ + gmm::feedback_manager::send(strStream.str(), \ + gmm::FeedbackType::ASSERT, 0); \ + gmm::feedback_manager::terminating_action(); \ + } \ + catch(...) \ + { \ + std::stringstream strStream; \ + strStream << "============================================\n"; \ + strStream << "| An unknown error has been detected !!! |\n"; \ + strStream << "============================================\n"; \ + gmm::feedback_manager::send(strStream.str(), \ + gmm::FeedbackType::ASSERT, 0); \ + gmm::feedback_manager::terminating_action(); \ + } + // catch(ios_base::failure) { + // std::cerr << "============================================\n"; + // std::cerr << "| A ios_base::failure has been detected !!!|\n"; + // std::cerr << "============================================\n"; + // exit(1); + // } + +#if defined(__GNUC__) && (__GNUC__ > 3) +# define GMM_SET_EXCEPTION_DEBUG \ + std::set_terminate(__gnu_cxx::__verbose_terminate_handler); +#else +# define GMM_SET_EXCEPTION_DEBUG +#endif + +} // namespace gmm + +#endif /* GMM_EXCEPT_H__ */ diff --git a/src/linear_algebra/gmm/gmm_feedback_management.h b/src/linear_algebra/gmm/gmm_feedback_management.h new file mode 100644 index 00000000..f445b16d --- /dev/null +++ b/src/linear_algebra/gmm/gmm_feedback_management.h @@ -0,0 +1,174 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/** @file gmm_feedback_management.h + @date July 03, 2017. + @brief Support for run time management of trace, warning and assert + feedback. +*/ + +#ifndef GMM_FEEDBACK_MANAGEMENT_H__ +#define GMM_FEEDBACK_MANAGEMENT_H__ +#include +#include +#include + +namespace gmm { + +/* *********************************************************************** */ +/* GetFEM warnings. */ +/* *********************************************************************** */ + +// This allows to dynamically hide warnings +struct warning_level { + static int level(int l = -2) + { static int level_ = 3; return (l != -2) ? (level_ = l) : level_; } +}; + +inline void set_warning_level(int l) { warning_level::level(l>0 ? l : 0); } +inline int get_warning_level(void) { return warning_level::level(-2); } + +/* *********************************************************************** */ +/* GetFEM traces. */ +/* *********************************************************************** */ + +// This allows to dynamically hide traces +struct traces_level { + static int level(int l = -2) + { static int level_ = 3; return (l != -2) ? (level_ = l) : level_; } +}; + +inline void set_traces_level(int l) { traces_level::level(l>0 ? l : 0); } +inline int get_traces_level(void) { return traces_level::level(); } + + +/* *********************************************************************** */ +/* GetFEM feedback management */ +/* *********************************************************************** */ + +enum class FeedbackType { + TRACE = 0, + WARNING, + ASSERT +}; + +// Abstract class providing feedback management interface. +// The interface consist of three functions: +// * for sending feedback message +// * for getting traces level +// * for getting warning level +// * for action to be done after feedback is handled +struct base_feedback_handler { + virtual ~base_feedback_handler() = default; + virtual void send(const std::string &message, FeedbackType messageType, size_t level) = 0; + virtual size_t traces_level() { return get_traces_level(); } + virtual size_t warning_level() { return get_warning_level(); } + virtual void terminating_action() = 0; +}; + + +// Provides the default implementation of feedback handling. +struct default_feedback_handler final : public base_feedback_handler { + void send(const std::string &message, FeedbackType, size_t) override { + std::cerr << message << std::endl; + } + void terminating_action() override { + std::exit(1); + } +}; + +// This class acts as a run-time dispatcher for sending feedback +// messages and getting trace and warning levels. +class feedback_manager { +public: + // Management actions + enum Action { + SET, // Sets the feedback handler, releasing the one previously set. + GET, // Returns currently managed handler (it is still managed). + REPLACE, // Replace manager with a new one, stop managing the old one + // and returns it unmanaged to the caller. The caller is + // then responsible for managing the memory of the handler. + }; + + // Steals the pointer to a messenger object that provides + // feedback handling implementation. + // + // Example: + // feedback_manager::manage(new default_feedback_handler); + // + static base_feedback_handler* manage(enum Action action=GET, base_feedback_handler *pHandler=nullptr); + static void send(const std::string &message, FeedbackType type, size_t level); + static size_t traces_level(); + static size_t warning_level(); + // Action to be taken when feedback handling is done + static void terminating_action(); +}; + +// Depending on action either gets, sets or replaces feedback handler. Setting handler to null resets +// it to gmm::default_feedback_handler. +inline base_feedback_handler* feedback_manager::manage(enum Action action, base_feedback_handler *pHandler) { + static std::unique_ptr pHandler_ = + std::move(std::unique_ptr(new default_feedback_handler)); + base_feedback_handler *rethandler = nullptr; + switch(action) { + case SET: + pHandler_.reset(pHandler != nullptr ? pHandler : new default_feedback_handler); + rethandler = pHandler_.get(); + break; + case GET: + rethandler = pHandler_.get(); + break; + case REPLACE: + rethandler = pHandler_.release(); + pHandler_.reset(pHandler != nullptr ? pHandler : new default_feedback_handler); + break; + } + return rethandler; +} + +inline void feedback_manager::send(const std::string &message, FeedbackType type, size_t level) { + feedback_manager::manage()->send(message, type, level); +} + +inline void feedback_manager::terminating_action() { + feedback_manager::manage()->terminating_action(); +} + +inline size_t feedback_manager::traces_level() { + return feedback_manager::manage()->traces_level(); +} + +inline size_t feedback_manager::warning_level() { + return feedback_manager::manage()->warning_level(); +} + +} // namespace gmm +#endif /* GMM_FEEDBACK_MANAGEMENT_H__ */ diff --git a/src/linear_algebra/gmm/gmm_inoutput.h b/src/linear_algebra/gmm/gmm_inoutput.h new file mode 100644 index 00000000..9bb51713 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_inoutput.h @@ -0,0 +1,1184 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2003-2020 Yves Renard, Julien Pommier + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_inoutput.h + @author Yves Renard + @author Julien Pommier + @date July 8, 2003. + @brief Input/output on sparse matrices + + Support Harwell-Boeing and Matrix-Market formats. +*/ +#ifndef GMM_INOUTPUT_H +#define GMM_INOUTPUT_H + +#include +#include "gmm_kernel.h" +namespace gmm { + + /*************************************************************************/ + /* */ + /* Functions to read and write Harwell Boeing format. */ + /* */ + /*************************************************************************/ + + // Fri Aug 15 16:29:47 EDT 1997 + // + // Harwell-Boeing File I/O in C + // V. 1.0 + // + // National Institute of Standards and Technology, MD. + // K.A. Remington + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + // NOTICE + // + // Permission to use, copy, modify, and distribute this software and + // its documentation for any purpose and without fee is hereby granted + // provided that the above copyright notice appear in all copies and + // that both the copyright notice and this permission notice appear in + // supporting documentation. + // + // Neither the Author nor the Institution (National Institute of Standards + // and Technology) make any representations about the suitability of this + // software for any purpose. This software is provided "as is" without + // expressed or implied warranty. + // ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ + + inline void IOHBTerminate(const char *a) { GMM_ASSERT1(false, a);} + + inline bool is_complex_double__(std::complex) { return true; } + inline bool is_complex_double__(double) { return false; } + + inline int ParseIfmt(const char *fmt, int* perline, int* width) { + if (SECURE_NONCHAR_SSCANF(fmt, " (%dI%d)", perline, width) != 2) { + *perline = 1; + int s = SECURE_NONCHAR_SSCANF(fmt, " (I%d)", width); + GMM_ASSERT1(s == 1, "invalid HB I-format: " << fmt); + } + return *width; + } + + inline int ParseRfmt(const char *fmt, int* perline, int* width, + int* prec, int* flag) { + char p; + *perline = *width = *flag = *prec = 0; +#ifdef GMM_SECURE_CRT + if (sscanf_s(fmt, " (%d%c%d.%d)", perline, &p, sizeof(char), width, prec) + < 3 || !strchr("PEDF", p)) +#else + if (sscanf(fmt, " (%d%c%d.%d)", perline, &p, width, prec) < 3 + || !strchr("PEDF", p)) +#endif + { + *perline = 1; +#ifdef GMM_SECURE_CRT + int s = sscanf_s(fmt, " (%c%d.%d)", &p, sizeof(char), width, prec); +#else + int s = sscanf(fmt, " (%c%d.%d)", &p, width, prec); +#endif + GMM_ASSERT1(s>=2 && strchr("PEDF",p), "invalid HB REAL format: " << fmt); + } + *flag = p; + return *width; + } + + /** matrix input/output for Harwell-Boeing format */ + struct HarwellBoeing_IO { + int nrows() const { return Nrow; } + int ncols() const { return Ncol; } + int nnz() const { return Nnzero; } + int is_complex() const { return Type[0] == 'C'; } + int is_symmetric() const { return Type[1] == 'S'; } + int is_hermitian() const { return Type[1] == 'H'; } + HarwellBoeing_IO() { clear(); } + HarwellBoeing_IO(const char *filename) { clear(); open(filename); } + ~HarwellBoeing_IO() { close(); } + /** open filename and reads header */ + void open(const char *filename); + /** read the opened file */ + template void read(csc_matrix& A); + template void read(MAT &M) IS_DEPRECATED; + template + static void write(const char *filename, const csc_matrix& A); + template + static void write(const char *filename, const csc_matrix& A, + const std::vector &rhs); + template + static void write(const char *filename, + const csc_matrix_ref& A); + template + static void write(const char *filename, + const csc_matrix_ref& A, + const std::vector &rhs); + + /** static method for saving the matrix */ + template static void write(const char *filename, + const MAT& A) IS_DEPRECATED; + private: + FILE *f; + char Title[73], Key[9], Rhstype[4], Type[4]; + int Nrow, Ncol, Nnzero, Nrhs; + char Ptrfmt[17], Indfmt[17], Valfmt[21], Rhsfmt[21]; + int Ptrcrd, Indcrd, Valcrd, Rhscrd; + int lcount; + + + void close() { if (f) fclose(f); clear(); } + void clear() { + Nrow = Ncol = Nnzero = Nrhs = 0; f = 0; lcount = 0; + memset(Type, 0, sizeof Type); + memset(Key, 0, sizeof Key); + memset(Title, 0, sizeof Title); + } + char *getline(char *buf) { + char *p = fgets(buf, BUFSIZ, f); ++lcount; + int s = SECURE_NONCHAR_SSCANF(buf,"%*s"); + GMM_ASSERT1(s >= 0 && p != 0, + "blank line in HB file at line " << lcount); + return buf; + } + + int substrtoi(const char *p, size_type len) { + char s[100]; len = std::min(len, sizeof s - 1); + SECURE_STRNCPY(s, 100, p, len); s[len] = 0; return atoi(s); + } + double substrtod(const char *p, size_type len, int Valflag) { + char s[100]; len = std::min(len, sizeof s - 1); + SECURE_STRNCPY(s, 100, p, len); s[len] = 0; + if ( Valflag != 'F' && !strchr(s,'E')) { + /* insert a char prefix for exp */ + int last = int(strlen(s)); + for (int j=last+1;j>=0;j--) { + s[j] = s[j-1]; + if ( s[j] == '+' || s[j] == '-' ) { + s[j-1] = char(Valflag); + break; + } + } + } + return atof(s); + } + template + int readHB_data(IND_TYPE colptr[], IND_TYPE rowind[], + double val[]) { + /***********************************************************************/ + /* This function opens and reads the specified file, interpreting its */ + /* contents as a sparse matrix stored in the Harwell/Boeing standard */ + /* format and creating compressed column storage scheme vectors to */ + /* hold the index and nonzero value information. */ + /* */ + /* ---------- */ + /* **CAVEAT** */ + /* ---------- */ + /* Parsing real formats from Fortran is tricky, and this file reader */ + /* does not claim to be foolproof. It has been tested for cases */ + /* when the real values are printed consistently and evenly spaced on */ + /* each line, with Fixed (F), and Exponential (E or D) formats. */ + /* */ + /* ** If the input file does not adhere to the H/B format, the ** */ + /* ** results will be unpredictable. ** */ + /* */ + /***********************************************************************/ + int i,ind,col,offset,count; + int Ptrperline, Ptrwidth, Indperline, Indwidth; + int Valperline, Valwidth, Valprec, Nentries; + int Valflag = 'D'; /* Indicates 'E','D', or 'F' float format */ + char line[BUFSIZ]; + gmm::standard_locale sl; + + + /* Parse the array input formats from Line 3 of HB file */ + ParseIfmt(Ptrfmt,&Ptrperline,&Ptrwidth); + ParseIfmt(Indfmt,&Indperline,&Indwidth); + if ( Type[0] != 'P' ) { /* Skip if pattern only */ + ParseRfmt(Valfmt,&Valperline,&Valwidth,&Valprec,&Valflag); + } + + /* Read column pointer array: */ + offset = 0; /* if base 0 storage is declared (via macro def), */ + /* then storage entries are offset by 1 */ + + for (count = 0, i=0;i Ncol) break; + colptr[count] = substrtoi(line+col,Ptrwidth)-offset; + count++; col += Ptrwidth; + } + } + + /* Read row index array: */ + for (count = 0, i=0;i(strchr(line,'D')) )) *p = 'E'; + } + for (col = 0, ind = 0;ind csc matrices */ + template void + HarwellBoeing_IO::read(csc_matrix& A) { + + // typedef typename csc_matrix::IND_TYPE IND_TYPE; + + GMM_ASSERT1(f, "no file opened!"); + GMM_ASSERT1(Type[0] != 'P', + "Bad HB matrix format (pattern matrices not supported)"); + GMM_ASSERT1(!is_complex_double__(T()) || Type[0] != 'R', + "Bad HB matrix format (file contains a REAL matrix)"); + GMM_ASSERT1(is_complex_double__(T()) || Type[0] != 'C', + "Bad HB matrix format (file contains a COMPLEX matrix)"); + A.nc = ncols(); A.nr = nrows(); + A.jc.resize(ncols()+1); + A.ir.resize(nnz()); + A.pr.resize(nnz()); + readHB_data(&A.jc[0], &A.ir[0], (double*)&A.pr[0]); + for (int i = 0; i <= ncols(); ++i) { A.jc[i] += shift; A.jc[i] -= 1; } + for (int i = 0; i < nnz(); ++i) { A.ir[i] += shift; A.ir[i] -= 1; } + } + + template void + HarwellBoeing_IO::read(MAT &M) { + csc_matrix::value_type> csc; + read(csc); + resize(M, mat_nrows(csc), mat_ncols(csc)); + copy(csc, M); + } + + template + inline int writeHB_mat_double(const char* filename, int M, int N, int nz, + const IND_TYPE colptr[], + const IND_TYPE rowind[], + const double val[], int Nrhs, + const double rhs[], const double guess[], + const double exact[], const char* Title, + const char* Key, const char* Type, + const char* Ptrfmt, const char* Indfmt, + const char* Valfmt, const char* Rhsfmt, + const char* Rhstype, int shift) { + /************************************************************************/ + /* The writeHB function opens the named file and writes the specified */ + /* matrix and optional right-hand-side(s) to that file in */ + /* Harwell-Boeing format. */ + /* */ + /* For a description of the Harwell Boeing standard, see: */ + /* Duff, et al., ACM TOMS Vol.15, No.1, March 1989 */ + /* */ + /************************************************************************/ + FILE *out_file; + int i, entry, offset, j, acount, linemod; + int totcrd, ptrcrd, indcrd, valcrd, rhscrd; + int nvalentries, nrhsentries; + int Ptrperline, Ptrwidth, Indperline, Indwidth; + int Rhsperline, Rhswidth, Rhsprec, Rhsflag; + int Valperline, Valwidth, Valprec; + int Valflag; /* Indicates 'E','D', or 'F' float format */ + char pformat[16],iformat[16],vformat[19],rformat[19]; + // char *pValflag, *pRhsflag; + gmm::standard_locale sl; + + if ( Type[0] == 'C' ) + { nvalentries = 2*nz; nrhsentries = 2*M; } + else + { nvalentries = nz; nrhsentries = M; } + + if ( filename != NULL ) { + SECURE_FOPEN(&out_file, filename, "w"); + GMM_ASSERT1(out_file != NULL, "Error: Cannot open file: " << filename); + } else out_file = stdout; + + if ( Ptrfmt == NULL ) Ptrfmt = "(8I10)"; + ParseIfmt(Ptrfmt, &Ptrperline, &Ptrwidth); + SECURE_SPRINTF1(pformat,sizeof(pformat),"%%%dd",Ptrwidth); + ptrcrd = (N+1)/Ptrperline; + if ( (N+1)%Ptrperline != 0) ptrcrd++; + + if ( Indfmt == NULL ) Indfmt = Ptrfmt; + ParseIfmt(Indfmt, &Indperline, &Indwidth); + SECURE_SPRINTF1(iformat,sizeof(iformat), "%%%dd",Indwidth); + indcrd = nz/Indperline; + if ( nz%Indperline != 0) indcrd++; + + if ( Type[0] != 'P' ) { /* Skip if pattern only */ + if ( Valfmt == NULL ) Valfmt = "(4E21.13)"; + ParseRfmt(Valfmt, &Valperline, &Valwidth, &Valprec, &Valflag); +// if (Valflag == 'D') { +// pValflag = (char *) strchr(Valfmt,'D'); +// *pValflag = 'E'; +// } + if (Valflag == 'F') + SECURE_SPRINTF2(vformat, sizeof(vformat), "%% %d.%df", Valwidth, + Valprec); + else + SECURE_SPRINTF2(vformat, sizeof(vformat), "%% %d.%dE", Valwidth, + Valprec); + valcrd = nvalentries/Valperline; + if ( nvalentries%Valperline != 0) valcrd++; + } else valcrd = 0; + + if ( Nrhs > 0 ) { + if ( Rhsfmt == NULL ) Rhsfmt = Valfmt; + ParseRfmt(Rhsfmt,&Rhsperline,&Rhswidth,&Rhsprec, &Rhsflag); + if (Rhsflag == 'F') + SECURE_SPRINTF2(rformat,sizeof(rformat), "%% %d.%df",Rhswidth,Rhsprec); + else + SECURE_SPRINTF2(rformat,sizeof(rformat), "%% %d.%dE",Rhswidth,Rhsprec); +// if (Valflag == 'D') { +// pRhsflag = (char *) strchr(Rhsfmt,'D'); +// *pRhsflag = 'E'; +// } + rhscrd = nrhsentries/Rhsperline; + if ( nrhsentries%Rhsperline != 0) rhscrd++; + if ( Rhstype[1] == 'G' ) rhscrd+=rhscrd; + if ( Rhstype[2] == 'X' ) rhscrd+=rhscrd; + rhscrd*=Nrhs; + } else rhscrd = 0; + + totcrd = 4+ptrcrd+indcrd+valcrd+rhscrd; + + + /* Print header information: */ + + fprintf(out_file,"%-72s%-8s\n%14d%14d%14d%14d%14d\n",Title, Key, totcrd, + ptrcrd, indcrd, valcrd, rhscrd); + fprintf(out_file,"%3s%11s%14d%14d%14d%14d\n",Type," ", M, N, nz, 0); + fprintf(out_file,"%-16s%-16s%-20s", Ptrfmt, Indfmt, Valfmt); + if ( Nrhs != 0 ) { + /* Print Rhsfmt on fourth line and */ + /* optional fifth header line for auxillary vector information:*/ + fprintf(out_file,"%-20s\n%-14s%d\n",Rhsfmt,Rhstype,Nrhs); + } + else + fprintf(out_file,"\n"); + + offset = 1 - shift; /* if base 0 storage is declared (via macro def), */ + /* then storage entries are offset by 1 */ + + /* Print column pointers: */ + for (i = 0; i < N+1; i++) { + entry = colptr[i]+offset; + fprintf(out_file,pformat,entry); + if ( (i+1)%Ptrperline == 0 ) fprintf(out_file,"\n"); + } + + if ( (N+1) % Ptrperline != 0 ) fprintf(out_file,"\n"); + + /* Print row indices: */ + for (i=0;i 0 ) { + for (j=0;j void + HarwellBoeing_IO::write(const char *filename, + const csc_matrix& A) { + write(filename, csc_matrix_ref + (&A.pr[0], &A.ir[0], &A.jc[0], A.nr, A.nc)); + } + + template void + HarwellBoeing_IO::write(const char *filename, + const csc_matrix& A, + const std::vector &rhs) { + write(filename, csc_matrix_ref + (&A.pr[0], &A.ir[0], &A.jc[0], A.nr, A.nc), rhs); + } + + template void + HarwellBoeing_IO::write(const char *filename, + const csc_matrix_ref& A) { + const char *t = 0; + if (is_complex_double__(T())) + if (mat_nrows(A) == mat_ncols(A)) t = "CUA"; else t = "CRA"; + else + if (mat_nrows(A) == mat_ncols(A)) t = "RUA"; else t = "RRA"; + writeHB_mat_double(filename, int(mat_nrows(A)), int(mat_ncols(A)), + A.jc[mat_ncols(A)], A.jc, A.ir, + (const double *)A.pr, + 0, 0, 0, 0, "GETFEM++ CSC MATRIX", "CSCMAT", + t, 0, 0, 0, 0, "F", shift); + } + + template void + HarwellBoeing_IO::write(const char *filename, + const csc_matrix_ref& A, + const std::vector &rhs) { + const char *t = 0; + if (is_complex_double__(T())) + if (mat_nrows(A) == mat_ncols(A)) t = "CUA"; else t = "CRA"; + else + if (mat_nrows(A) == mat_ncols(A)) t = "RUA"; else t = "RRA"; + int Nrhs = gmm::vect_size(rhs) / mat_nrows(A); + writeHB_mat_double(filename, int(mat_nrows(A)), int(mat_ncols(A)), + A.jc[mat_ncols(A)], A.jc, A.ir, + (const double *)A.pr, + Nrhs, (const double *)(&rhs[0]), 0, 0, + "GETFEM++ CSC MATRIX", "CSCMAT", + t, 0, 0, 0, 0, "F ", shift); + } + + + template void + HarwellBoeing_IO::write(const char *filename, const MAT& A) { + gmm::csc_matrix::value_type> + tmp(gmm::mat_nrows(A), gmm::mat_ncols(A)); + gmm::copy(A,tmp); + HarwellBoeing_IO::write(filename, tmp); + } + + /** save a "double" or "std::complex" csc matrix into a + HarwellBoeing file + */ + template inline void + Harwell_Boeing_save(const std::string &filename, + const csc_matrix& A) + { HarwellBoeing_IO::write(filename.c_str(), A); } + + /** save a reference on "double" or "std::complex" csc matrix + into a HarwellBoeing file + */ + template inline void + Harwell_Boeing_save(const std::string &filename, + const csc_matrix_ref& A) + { HarwellBoeing_IO::write(filename.c_str(), A); } + + /** save a "double" or "std::complex" generic matrix + into a HarwellBoeing file making a copy in a csc matrix + */ + template inline void + Harwell_Boeing_save(const std::string &filename, const MAT& A) { + gmm::csc_matrix::value_type> + tmp(gmm::mat_nrows(A), gmm::mat_ncols(A)); + gmm::copy(A, tmp); + HarwellBoeing_IO::write(filename.c_str(), tmp); + } + + template inline void + Harwell_Boeing_save(const std::string &filename, const MAT& A, + const VECT &RHS) { + typedef typename gmm::linalg_traits::value_type T; + gmm::csc_matrix tmp(gmm::mat_nrows(A), gmm::mat_ncols(A)); + gmm::copy(A, tmp); + std::vector tmprhs(gmm::vect_size(RHS)); + gmm::copy(RHS, tmprhs); + HarwellBoeing_IO::write(filename.c_str(), tmp, tmprhs); + } + + /** load a "double" or "std::complex" csc matrix from a + HarwellBoeing file + */ + template void + Harwell_Boeing_load(const std::string &filename, csc_matrix& A) { + HarwellBoeing_IO h(filename.c_str()); h.read(A); + } + + /** load a "double" or "std::complex" generic matrix from a + HarwellBoeing file + */ + template void + Harwell_Boeing_load(const std::string &filename, MAT& A) { + csc_matrix::value_type> csc; + Harwell_Boeing_load(filename, csc); + resize(A, mat_nrows(csc), mat_ncols(csc)); + copy(csc, A); + } + + /*************************************************************************/ + /* */ + /* Functions to read and write MatrixMarket format. */ + /* */ + /*************************************************************************/ + + /* + * Matrix Market I/O library for ANSI C + * + * See http://math.nist.gov/MatrixMarket for details. + * + * + */ + +#define MM_MAX_LINE_LENGTH 1025 +#define MatrixMarketBanner "%%MatrixMarket" +#define MM_MAX_TOKEN_LENGTH 64 + + typedef char MM_typecode[4]; + + /******************* MM_typecode query functions *************************/ + +#define mm_is_matrix(typecode) ((typecode)[0]=='M') + +#define mm_is_sparse(typecode) ((typecode)[1]=='C') +#define mm_is_coordinate(typecode) ((typecode)[1]=='C') +#define mm_is_dense(typecode) ((typecode)[1]=='A') +#define mm_is_array(typecode) ((typecode)[1]=='A') + +#define mm_is_complex(typecode) ((typecode)[2]=='C') +#define mm_is_real(typecode) ((typecode)[2]=='R') +#define mm_is_pattern(typecode) ((typecode)[2]=='P') +#define mm_is_integer(typecode) ((typecode)[2]=='I') + +#define mm_is_symmetric(typecode) ((typecode)[3]=='S') +#define mm_is_general(typecode) ((typecode)[3]=='G') +#define mm_is_skew(typecode) ((typecode)[3]=='K') +#define mm_is_hermitian(typecode) ((typecode)[3]=='H') + + /******************* MM_typecode modify fucntions ************************/ + +#define mm_set_matrix(typecode) ((*typecode)[0]='M') +#define mm_set_coordinate(typecode) ((*typecode)[1]='C') +#define mm_set_array(typecode) ((*typecode)[1]='A') +#define mm_set_dense(typecode) mm_set_array(typecode) +#define mm_set_sparse(typecode) mm_set_coordinate(typecode) + +#define mm_set_complex(typecode) ((*typecode)[2]='C') +#define mm_set_real(typecode) ((*typecode)[2]='R') +#define mm_set_pattern(typecode) ((*typecode)[2]='P') +#define mm_set_integer(typecode) ((*typecode)[2]='I') + + +#define mm_set_symmetric(typecode) ((*typecode)[3]='S') +#define mm_set_general(typecode) ((*typecode)[3]='G') +#define mm_set_skew(typecode) ((*typecode)[3]='K') +#define mm_set_hermitian(typecode) ((*typecode)[3]='H') + +#define mm_clear_typecode(typecode) ((*typecode)[0]=(*typecode)[1]= \ + (*typecode)[2]=' ',(*typecode)[3]='G') + +#define mm_initialize_typecode(typecode) mm_clear_typecode(typecode) + + + /******************* Matrix Market error codes ***************************/ + + +#define MM_COULD_NOT_READ_FILE 11 +#define MM_PREMATURE_EOF 12 +#define MM_NOT_MTX 13 +#define MM_NO_HEADER 14 +#define MM_UNSUPPORTED_TYPE 15 +#define MM_LINE_TOO_LONG 16 +#define MM_COULD_NOT_WRITE_FILE 17 + + + /******************** Matrix Market internal definitions ***************** + + MM_matrix_typecode: 4-character sequence + + object sparse/ data storage + dense type scheme + + string position: [0] [1] [2] [3] + + Matrix typecode: M(atrix) C(oord) R(eal) G(eneral) + A(array) C(omplex) H(ermitian) + P(attern) S(ymmetric) + I(nteger) K(kew) + + ***********************************************************************/ + +#define MM_MTX_STR "matrix" +#define MM_ARRAY_STR "array" +#define MM_DENSE_STR "array" +#define MM_COORDINATE_STR "coordinate" +#define MM_SPARSE_STR "coordinate" +#define MM_COMPLEX_STR "complex" +#define MM_REAL_STR "real" +#define MM_INT_STR "integer" +#define MM_GENERAL_STR "general" +#define MM_SYMM_STR "symmetric" +#define MM_HERM_STR "hermitian" +#define MM_SKEW_STR "skew-symmetric" +#define MM_PATTERN_STR "pattern" + + inline char *mm_typecode_to_str(MM_typecode matcode) { + char buffer[MM_MAX_LINE_LENGTH]; + const char *types[4] = {0,0,0,0}; + /* int error =0; */ + /* int i; */ + + /* check for MTX type */ + if (mm_is_matrix(matcode)) + types[0] = MM_MTX_STR; + /* + else + error=1; + */ + /* check for CRD or ARR matrix */ + if (mm_is_sparse(matcode)) + types[1] = MM_SPARSE_STR; + else + if (mm_is_dense(matcode)) + types[1] = MM_DENSE_STR; + else + return NULL; + + /* check for element data type */ + if (mm_is_real(matcode)) + types[2] = MM_REAL_STR; + else + if (mm_is_complex(matcode)) + types[2] = MM_COMPLEX_STR; + else + if (mm_is_pattern(matcode)) + types[2] = MM_PATTERN_STR; + else + if (mm_is_integer(matcode)) + types[2] = MM_INT_STR; + else + return NULL; + + + /* check for symmetry type */ + if (mm_is_general(matcode)) + types[3] = MM_GENERAL_STR; + else if (mm_is_symmetric(matcode)) + types[3] = MM_SYMM_STR; + else if (mm_is_hermitian(matcode)) + types[3] = MM_HERM_STR; + else if (mm_is_skew(matcode)) + types[3] = MM_SKEW_STR; + else + return NULL; + + SECURE_SPRINTF4(buffer, sizeof(buffer), "%s %s %s %s", types[0], types[1], + types[2], types[3]); + return SECURE_STRDUP(buffer); + + } + + inline int mm_read_banner(FILE *f, MM_typecode *matcode) { + char line[MM_MAX_LINE_LENGTH]; + char banner[MM_MAX_TOKEN_LENGTH]; + char mtx[MM_MAX_TOKEN_LENGTH]; + char crd[MM_MAX_TOKEN_LENGTH]; + char data_type[MM_MAX_TOKEN_LENGTH]; + char storage_scheme[MM_MAX_TOKEN_LENGTH]; + char *p; + gmm::standard_locale sl; + /* int ret_code; */ + + mm_clear_typecode(matcode); + + if (fgets(line, MM_MAX_LINE_LENGTH, f) == NULL) + return MM_PREMATURE_EOF; + +#ifdef GMM_SECURE_CRT + if (sscanf_s(line, "%s %s %s %s %s", banner, sizeof(banner), + mtx, sizeof(mtx), crd, sizeof(crd), data_type, + sizeof(data_type), storage_scheme, + sizeof(storage_scheme)) != 5) +#else + if (sscanf(line, "%s %s %s %s %s", banner, mtx, crd, + data_type, storage_scheme) != 5) +#endif + return MM_PREMATURE_EOF; + + for (p=mtx; *p!='\0'; *p=char(tolower(*p)),p++) {}; /* convert to lower case */ + for (p=crd; *p!='\0'; *p=char(tolower(*p)),p++) {}; + for (p=data_type; *p!='\0'; *p=char(tolower(*p)),p++) {}; + for (p=storage_scheme; *p!='\0'; *p=char(tolower(*p)),p++) {}; + + /* check for banner */ + if (strncmp(banner, MatrixMarketBanner, strlen(MatrixMarketBanner)) != 0) + return MM_NO_HEADER; + + /* first field should be "mtx" */ + if (strcmp(mtx, MM_MTX_STR) != 0) + return MM_UNSUPPORTED_TYPE; + mm_set_matrix(matcode); + + + /* second field describes whether this is a sparse matrix (in coordinate + storgae) or a dense array */ + + + if (strcmp(crd, MM_SPARSE_STR) == 0) + mm_set_sparse(matcode); + else + if (strcmp(crd, MM_DENSE_STR) == 0) + mm_set_dense(matcode); + else + return MM_UNSUPPORTED_TYPE; + + + /* third field */ + + if (strcmp(data_type, MM_REAL_STR) == 0) + mm_set_real(matcode); + else + if (strcmp(data_type, MM_COMPLEX_STR) == 0) + mm_set_complex(matcode); + else + if (strcmp(data_type, MM_PATTERN_STR) == 0) + mm_set_pattern(matcode); + else + if (strcmp(data_type, MM_INT_STR) == 0) + mm_set_integer(matcode); + else + return MM_UNSUPPORTED_TYPE; + + + /* fourth field */ + + if (strcmp(storage_scheme, MM_GENERAL_STR) == 0) + mm_set_general(matcode); + else + if (strcmp(storage_scheme, MM_SYMM_STR) == 0) + mm_set_symmetric(matcode); + else + if (strcmp(storage_scheme, MM_HERM_STR) == 0) + mm_set_hermitian(matcode); + else + if (strcmp(storage_scheme, MM_SKEW_STR) == 0) + mm_set_skew(matcode); + else + return MM_UNSUPPORTED_TYPE; + + return 0; + } + + inline int mm_read_mtx_crd_size(FILE *f, int *M, int *N, int *nz ) { + char line[MM_MAX_LINE_LENGTH]; + /* int ret_code;*/ + int num_items_read; + + /* set return null parameter values, in case we exit with errors */ + *M = *N = *nz = 0; + + /* now continue scanning until you reach the end-of-comments */ + do { + if (fgets(line,MM_MAX_LINE_LENGTH,f) == NULL) + return MM_PREMATURE_EOF; + } while (line[0] == '%'); + + /* line[] is either blank or has M,N, nz */ + if (SECURE_NONCHAR_SSCANF(line, "%d %d %d", M, N, nz) == 3) return 0; + else + do { + num_items_read = SECURE_NONCHAR_FSCANF(f, "%d %d %d", M, N, nz); + if (num_items_read == EOF) return MM_PREMATURE_EOF; + } + while (num_items_read != 3); + + return 0; + } + + + inline int mm_read_mtx_crd_data(FILE *f, int, int, int nz, int II[], + int J[], double val[], MM_typecode matcode) { + int i; + if (mm_is_complex(matcode)) { + for (i=0; i void read(Matrix &A); + /* write a matrix */ + template static void + write(const char *filename, const csc_matrix& A); + template static void + write(const char *filename, + const csc_matrix_ref& A); + template static void + write(const char *filename, const MAT& A); + }; + + /** load a matrix-market file */ + template inline void + MatrixMarket_load(const char *filename, Matrix& A) { + MatrixMarket_IO mm; mm.open(filename); + mm.read(A); + } + /** write a matrix-market file */ + template void + MatrixMarket_save(const char *filename, const csc_matrix& A) { + MatrixMarket_IO mm; mm.write(filename, A); + } + + template inline void + MatrixMarket_save(const char *filename, + const csc_matrix_ref& A) { + MatrixMarket_IO mm; mm.write(filename, A); + } + + + inline void MatrixMarket_IO::open(const char *filename) { + gmm::standard_locale sl; + if (f) { fclose(f); } + SECURE_FOPEN(&f, filename, "r"); + GMM_ASSERT1(f, "Sorry, cannot open file " << filename); + int s1 = mm_read_banner(f, &matcode); + GMM_ASSERT1(s1 == 0, "Sorry, cannnot find the matrix market banner in " + << filename); + int s2 = mm_is_coordinate(matcode), s3 = mm_is_matrix(matcode); + GMM_ASSERT1(s2 > 0 && s3 > 0, + "file is not coordinate storage or is not a matrix"); + int s4 = mm_is_pattern(matcode); + GMM_ASSERT1(s4 == 0, + "the file does only contain the pattern of a sparse matrix"); + int s5 = mm_is_skew(matcode); + GMM_ASSERT1(s5 == 0, "not currently supporting skew symmetric"); + isSymmetric = mm_is_symmetric(matcode) || mm_is_hermitian(matcode); + isHermitian = mm_is_hermitian(matcode); + isComplex = mm_is_complex(matcode); + mm_read_mtx_crd_size(f, &row, &col, &nz); + } + + template void MatrixMarket_IO::read(Matrix &A) { + gmm::standard_locale sl; + typedef typename linalg_traits::value_type T; + GMM_ASSERT1(f, "no file opened!"); + GMM_ASSERT1(!is_complex_double__(T()) || isComplex, + "Bad MM matrix format (complex matrix expected)"); + GMM_ASSERT1(is_complex_double__(T()) || !isComplex, + "Bad MM matrix format (real matrix expected)"); + A = Matrix(row, col); + gmm::clear(A); + + std::vector II(nz), J(nz); + std::vector PR(nz); + mm_read_mtx_crd_data(f, row, col, nz, &II[0], &J[0], + (double*)&PR[0], matcode); + + for (size_type i = 0; i < size_type(nz); ++i) { + A(II[i]-1, J[i]-1) = PR[i]; + + // FIXED MM Format + if (mm_is_hermitian(matcode) && (II[i] != J[i]) ) { + A(J[i]-1, II[i]-1) = gmm::conj(PR[i]); + } + + if (mm_is_symmetric(matcode) && (II[i] != J[i]) ) { + A(J[i]-1, II[i]-1) = PR[i]; + } + + if (mm_is_skew(matcode) && (II[i] != J[i]) ) { + A(J[i]-1, II[i]-1) = -PR[i]; + } + } + } + + template void + MatrixMarket_IO::write(const char *filename, const csc_matrix& A) { + write(filename, csc_matrix_ref + (&A.pr[0], &A.ir[0], &A.jc[0], A.nr, A.nc)); + } + + template void + MatrixMarket_IO::write(const char *filename, + const csc_matrix_ref& A) { + gmm::standard_locale sl; + static MM_typecode t1 = {'M', 'C', 'R', 'G'}; + static MM_typecode t2 = {'M', 'C', 'C', 'G'}; + MM_typecode t; + + if (is_complex_double__(T())) std::copy(&(t2[0]), &(t2[0])+4, &(t[0])); + else std::copy(&(t1[0]), &(t1[0])+4, &(t[0])); + size_type nz = A.jc[mat_ncols(A)]; + std::vector II(nz), J(nz); + for (size_type j=0; j < mat_ncols(A); ++j) { + for (size_type i = A.jc[j]; i < A.jc[j+1]; ++i) { + II[i] = A.ir[i] + 1 - shift; + J[i] = int(j + 1); + } + } + mm_write_mtx_crd(filename, int(mat_nrows(A)), int(mat_ncols(A)), + int(nz), &II[0], &J[0], (const double *)A.pr, t); + } + + + template void + MatrixMarket_IO::write(const char *filename, const MAT& A) { + gmm::csc_matrix::value_type> + tmp(gmm::mat_nrows(A), gmm::mat_ncols(A)); + gmm::copy(A,tmp); + MatrixMarket_IO::write(filename, tmp); + } + + template static void vecsave(std::string fname, const VEC& V, + bool binary=false, std::string Vformat="") { + if (binary) { + std::ofstream f(fname.c_str(), std::ofstream::binary); + for (size_type i=0; i < gmm::vect_size(V); ++i) + f.write(reinterpret_cast(&V[i]), sizeof(V[i])); + } + else { + if (Vformat.empty()){ + std::ofstream f(fname.c_str()); f.imbue(std::locale("C")); + f.precision(16); + for (size_type i=0; i < gmm::vect_size(V); ++i) f << V[i] << "\n"; + } + else { + FILE* f = fopen(fname.c_str(), "w"); + for (size_type i=0; i < gmm::vect_size(V); ++i) fprintf(f, Vformat.c_str(), V[i]); + fclose(f); + } + } + } + + template static void vecload(std::string fname, const VEC& V_, + bool binary=false) { + VEC &V(const_cast(V_)); + if (binary) { + std::ifstream f(fname.c_str(), std::ifstream::binary); + for (size_type i=0; i < gmm::vect_size(V); ++i) + f.read(reinterpret_cast(&V[i]), sizeof(V[i])); + } + else { + std::ifstream f(fname.c_str()); f.imbue(std::locale("C")); + for (size_type i=0; i < gmm::vect_size(V); ++i) f >> V[i]; + } + } +} + + +#endif // GMM_INOUTPUT_H diff --git a/src/linear_algebra/gmm/gmm_interface.h b/src/linear_algebra/gmm/gmm_interface.h new file mode 100644 index 00000000..6635a25b --- /dev/null +++ b/src/linear_algebra/gmm/gmm_interface.h @@ -0,0 +1,1068 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + + +/**@file gmm_interface.h + @author Yves Renard + @date October 13, 2002. + @brief gmm interface for STL vectors. +*/ + +#ifndef GMM_INTERFACE_H__ +#define GMM_INTERFACE_H__ + +#include "gmm_blas.h" +#include "gmm_sub_index.h" + +namespace gmm { + + /* ********************************************************************* */ + /* */ + /* What is needed for a Vector type : */ + /* Vector v(n) defines a vector with n components. */ + /* v[i] allows to access to the ith component of v. */ + /* linalg_traits should be filled with appropriate definitions */ + /* */ + /* for a dense vector : the minimum is two random iterators (begin and */ + /* end) and a pointer to a valid origin. */ + /* for a sparse vector : the minimum is two forward iterators, with */ + /* a method it.index() which gives the index of */ + /* a non zero element, an interface object */ + /* should describe the method to add new non */ + /* zero element, and a pointer to a valid */ + /* origin. */ + /* */ + /* What is needed for a Matrix type : */ + /* Matrix m(n, m) defines a matrix with n rows and m columns. */ + /* m(i, j) allows to access to the element at row i and column j. */ + /* linalg_traits should be filled with appropriate definitions */ + /* */ + /* What is needed for an iterator on dense vector */ + /* to be standard random access iterator */ + /* */ + /* What is needed for an iterator on a sparse vector */ + /* to be a standard bidirectional iterator */ + /* elt should be sorted with increasing indices. */ + /* it.index() gives the index of the non-zero element. */ + /* */ + /* Remark : If original iterators are not convenient, they could be */ + /* redefined and interfaced in linalg_traits without changing */ + /* the original Vector type. */ + /* */ + /* ********************************************************************* */ + + /* ********************************************************************* */ + /* Simple references on vectors */ + /* ********************************************************************* */ + + template struct simple_vector_ref { + typedef simple_vector_ref this_type; + typedef typename std::iterator_traits::value_type V; + typedef V * CPT; + typedef typename std::iterator_traits::reference ref_V; + typedef typename linalg_traits::iterator iterator; + typedef typename linalg_traits::reference reference; + typedef typename linalg_traits::porigin_type porigin_type; + + iterator begin_, end_; + porigin_type origin; + size_type size_; + + simple_vector_ref(ref_V v) : begin_(vect_begin(const_cast(v))), + end_(vect_end(const_cast(v))), + origin(linalg_origin(const_cast(v))), + size_(vect_size(v)) {} + + simple_vector_ref(const simple_vector_ref &cr) + : begin_(cr.begin_),end_(cr.end_),origin(cr.origin),size_(cr.size_) {} + + simple_vector_ref(void) {} + + reference operator[](size_type i) const + { return linalg_traits::access(origin, begin_, end_, i); } + }; + + template inline + void set_to_begin(IT &it, ORG o, simple_vector_ref *,linalg_modifiable) { + typedef typename linalg_traits >::V_reference ref_t; + set_to_begin(it, o, PT(), ref_t()); + } + + template inline + void set_to_begin(IT &it, ORG o, const simple_vector_ref *, + linalg_modifiable) { + typedef typename linalg_traits >::V_reference ref_t; + set_to_begin(it, o, PT(), ref_t()); + } + + template inline + void set_to_end(IT &it, ORG o, simple_vector_ref *, linalg_modifiable) { + typedef typename linalg_traits >::V_reference ref_t; + set_to_end(it, o, PT(), ref_t()); + } + + template inline + void set_to_end(IT &it, ORG o, const simple_vector_ref *, + linalg_modifiable) { + typedef typename linalg_traits >::V_reference ref_t; + set_to_end(it, o, PT(), ref_t()); + } + + + template struct linalg_traits > { + typedef simple_vector_ref this_type; + typedef this_type *pthis_type; + typedef typename std::iterator_traits::value_type V; + typedef typename linalg_traits::origin_type origin_type; + typedef V *pV; + typedef typename linalg_traits::is_reference V_reference; + typedef typename which_reference::is_reference is_reference; + typedef abstract_vector linalg_type; + typedef typename linalg_traits::value_type value_type; + typedef typename select_ref::reference, PT>::ref_type reference; + typedef typename select_ref::ref_type porigin_type; + typedef typename select_ref::const_iterator, + typename linalg_traits::iterator, PT>::ref_type iterator; + typedef typename linalg_traits::const_iterator const_iterator; + typedef typename linalg_traits::storage_type storage_type; + typedef linalg_true index_sorted; + static size_type size(const this_type &v) { return v.size_; } + static inline iterator begin(this_type &v) { + iterator it = v.begin_; + set_to_begin(it, v.origin, pthis_type(), is_reference()); + return it; + } + static inline const_iterator begin(const this_type &v) { + const_iterator it = v.begin_; + set_to_begin(it, v.origin, pthis_type(), is_reference()); + return it; + } + static inline iterator end(this_type &v) { + iterator it = v.end_; + set_to_end(it, v.origin, pthis_type(), is_reference()); + return it; + } + static inline const_iterator end(const this_type &v) { + const_iterator it = v.end_; + set_to_end(it, v.origin, pthis_type(), is_reference()); + return it; + } + static origin_type* origin(this_type &v) { return v.origin; } + static const origin_type* origin(const this_type &v) { return v.origin; } + static void clear(origin_type* o, const iterator &it, const iterator &ite) + { linalg_traits::clear(o, it, ite); } + static void do_clear(this_type &v) { clear(v.origin, v.begin_, v.end_); } + static value_type access(const origin_type *o, const const_iterator &it, + const const_iterator &ite, size_type i) + { return linalg_traits::access(o, it, ite, i); } + static reference access(origin_type *o, const iterator &it, + const iterator &ite, size_type i) + { return linalg_traits::access(o, it, ite, i); } + }; + + template + std::ostream &operator << (std::ostream &o, const simple_vector_ref& v) + { gmm::write(o,v); return o; } + + template + simple_vector_ref *> + vref(const std::vector &vv) + { return simple_vector_ref *>(vv); } + + + /* ********************************************************************* */ + /* */ + /* Traits for S.T.L. object */ + /* */ + /* ********************************************************************* */ + + template + struct linalg_traits > { + typedef std::vector this_type; + typedef this_type origin_type; + typedef linalg_false is_reference; + typedef abstract_vector linalg_type; + typedef T value_type; + typedef T& reference; + typedef typename this_type::iterator iterator; + typedef typename this_type::const_iterator const_iterator; + typedef abstract_dense storage_type; + typedef linalg_true index_sorted; + static size_type size(const this_type &v) { return v.size(); } + static iterator begin(this_type &v) { return v.begin(); } + static const_iterator begin(const this_type &v) { return v.begin(); } + static iterator end(this_type &v) { return v.end(); } + static const_iterator end(const this_type &v) { return v.end(); } + static origin_type* origin(this_type &v) { return &v; } + static const origin_type* origin(const this_type &v) { return &v; } + static void clear(origin_type*, const iterator &it, const iterator &ite) + { std::fill(it, ite, value_type(0)); } + static void do_clear(this_type &v) { std::fill(v.begin(), v.end(), T(0)); } + static value_type access(const origin_type *, const const_iterator &it, + const const_iterator &, size_type i) + { return it[i]; } + static reference access(origin_type *, const iterator &it, + const iterator &, size_type i) + { return it[i]; } + static void resize(this_type &v, size_type n) { v.resize(n); } + }; + + + + template + inline size_type nnz(const std::vector& l) { return l.size(); } + + /* ********************************************************************* */ + /* */ + /* Traits for ref objects */ + /* */ + /* ********************************************************************* */ + + template + struct tab_ref_with_origin : public gmm::tab_ref { + typedef tab_ref_with_origin this_type; + // next line replaced by the 4 following lines in order to please aCC + //typedef typename linalg_traits::porigin_type porigin_type; + typedef typename linalg_traits::origin_type origin_type; + typedef typename std::iterator_traits::pointer PT; + typedef typename select_ref::ref_type porigin_type; + + + porigin_type origin; + + tab_ref_with_origin(void) {} + template tab_ref_with_origin(const IT &b, const IT &e, PT p) + : gmm::tab_ref(b,e), origin(porigin_type(p)) {} + tab_ref_with_origin(const IT &b, const IT &e, porigin_type p) + : gmm::tab_ref(b,e), origin(p) {} + + tab_ref_with_origin(const V &v, const sub_interval &si) + : gmm::tab_ref(vect_begin(const_cast(v))+si.min, + vect_begin(const_cast(v))+si.max), + origin(linalg_origin(const_cast(v))) {} + tab_ref_with_origin(V &v, const sub_interval &si) + : gmm::tab_ref(vect_begin(const_cast(v))+si.min, + vect_begin(const_cast(v))+si.max), + origin(linalg_origin(const_cast(v))) {} + }; + + template + struct linalg_traits > { + typedef typename std::iterator_traits::pointer PT; + typedef typename linalg_traits::origin_type origin_type; + typedef tab_ref_with_origin this_type; + typedef typename which_reference::is_reference is_reference; + typedef abstract_vector linalg_type; + typedef typename select_ref::ref_type porigin_type; + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::reference reference; + typedef typename this_type::iterator iterator; + typedef typename this_type::iterator const_iterator; + typedef abstract_dense storage_type; + typedef linalg_true index_sorted; + static size_type size(const this_type &v) { return v.size(); } + static iterator begin(this_type &v) { return v.begin(); } + static const_iterator begin(const this_type &v) { return v.begin(); } + static iterator end(this_type &v) { return v.end(); } + static const_iterator end(const this_type &v) { return v.end(); } + static origin_type* origin(this_type &v) { return v.origin; } + static const origin_type* origin(const this_type &v) { return v.origin; } + static void clear(origin_type*, const iterator &it, const iterator &ite) + { std::fill(it, ite, value_type(0)); } + static inline void do_clear(this_type &v) + { std::fill(v.begin(), v.end(), value_type(0)); } + static value_type access(const origin_type *, const const_iterator &it, + const const_iterator &, size_type i) + { return it[i]; } + static reference access(origin_type *, const iterator &it, + const iterator &, size_type i) + { return it[i]; } + }; + + template std::ostream &operator << + (std::ostream &o, const tab_ref_with_origin& m) + { gmm::write(o,m); return o; } + + + template + struct tab_ref_reg_spaced_with_origin : public gmm::tab_ref_reg_spaced { + typedef tab_ref_reg_spaced_with_origin this_type; + typedef typename linalg_traits::porigin_type porigin_type; + + porigin_type origin; + + tab_ref_reg_spaced_with_origin(void) {} + tab_ref_reg_spaced_with_origin(const IT &b, size_type n, size_type s, + const porigin_type p) + : gmm::tab_ref_reg_spaced(b,n,s), origin(p) {} + tab_ref_reg_spaced_with_origin(const V &v, const sub_slice &si) + : gmm::tab_ref_reg_spaced(vect_begin(const_cast(v)) + si.min, + si.N, (si.max - si.min)/si.N), + origin(linalg_origin(const_cast(v))) {} + tab_ref_reg_spaced_with_origin(V &v, const sub_slice &si) + : gmm::tab_ref_reg_spaced(vect_begin(const_cast(v)) + si.min, + si.N, (si.max - si.min)/si.N), + origin(linalg_origin(const_cast(v))) {} + }; + + template + struct linalg_traits > { + typedef typename std::iterator_traits::pointer PT; + typedef tab_ref_reg_spaced_with_origin this_type; + typedef typename linalg_traits::origin_type origin_type; + typedef typename select_ref::ref_type porigin_type; + typedef typename which_reference::is_reference is_reference; + typedef abstract_vector linalg_type; + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::reference reference; + typedef typename this_type::iterator iterator; + typedef typename this_type::iterator const_iterator; + typedef abstract_dense storage_type; + typedef linalg_true index_sorted; + static size_type size(const this_type &v) { return v.size(); } + static iterator begin(this_type &v) { return v.begin(); } + static const_iterator begin(const this_type &v) { return v.begin(); } + static iterator end(this_type &v) { return v.end(); } + static const_iterator end(const this_type &v) { return v.end(); } + static origin_type* origin(this_type &v) { return v.origin; } + static const origin_type* origin(const this_type &v) { return v.origin; } + static void clear(origin_type*, const iterator &it, const iterator &ite) + { std::fill(it, ite, value_type(0)); } + static void do_clear(this_type &v) + { std::fill(v.begin(), v.end(), value_type(0)); } + static value_type access(const origin_type *, const const_iterator &it, + const const_iterator &, size_type i) + { return it[i]; } + static reference access(origin_type *, const iterator &it, + const iterator &, size_type i) + { return it[i]; } + }; + + template std::ostream &operator << + (std::ostream &o, const tab_ref_reg_spaced_with_origin& m) + { gmm::write(o,m); return o; } + + + template + struct tab_ref_index_ref_with_origin + : public gmm::tab_ref_index_ref { + typedef tab_ref_index_ref_with_origin this_type; + typedef typename linalg_traits::porigin_type porigin_type; + + porigin_type origin; + + tab_ref_index_ref_with_origin(void) {} + tab_ref_index_ref_with_origin(const IT &b, const ITINDEX &bi, + const ITINDEX &ei, porigin_type p) + : gmm::tab_ref_index_ref(b, bi, ei), origin(p) {} + + tab_ref_index_ref_with_origin(const V &v, const sub_index &si) + : gmm::tab_ref_index_ref(vect_begin(const_cast(v)), + si.begin(), si.end()), + origin(linalg_origin(const_cast(v))) {} + tab_ref_index_ref_with_origin(V &v, const sub_index &si) + : gmm::tab_ref_index_ref(vect_begin(const_cast(v)), + si.begin(), si.end()), + origin(linalg_origin(const_cast(v))) {} + }; + + template + struct linalg_traits > { + typedef typename std::iterator_traits::pointer PT; + typedef tab_ref_index_ref_with_origin this_type; + typedef typename linalg_traits::origin_type origin_type; + typedef typename select_ref::ref_type porigin_type; + typedef typename which_reference::is_reference is_reference; + typedef abstract_vector linalg_type; + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::reference reference; + typedef typename this_type::iterator iterator; + typedef typename this_type::iterator const_iterator; + typedef abstract_dense storage_type; + typedef linalg_true index_sorted; + static size_type size(const this_type &v) { return v.size(); } + static iterator begin(this_type &v) { return v.begin(); } + static const_iterator begin(const this_type &v) { return v.begin(); } + static iterator end(this_type &v) { return v.end(); } + static const_iterator end(const this_type &v) { return v.end(); } + static origin_type* origin(this_type &v) { return v.origin; } + static const origin_type* origin(const this_type &v) { return v.origin; } + static void clear(origin_type*, const iterator &it, const iterator &ite) + { std::fill(it, ite, value_type(0)); } + static void do_clear(this_type &v) + { std::fill(v.begin(), v.end(), value_type(0)); } + static value_type access(const origin_type *, const const_iterator &it, + const const_iterator &, size_type i) + { return it[i]; } + static reference access(origin_type *, const iterator &it, + const iterator &, size_type i) + { return it[i]; } + }; + + template + std::ostream &operator << + (std::ostream &o, const tab_ref_index_ref_with_origin& m) + { gmm::write(o,m); return o; } + + + template + struct dense_compressed_iterator { + typedef ITER value_type; + typedef ITER *pointer; + typedef ITER &reference; + typedef ptrdiff_t difference_type; + typedef std::random_access_iterator_tag iterator_category; + typedef size_t size_type; + typedef dense_compressed_iterator iterator; + typedef typename std::iterator_traits::value_type *MPT; + + ITER it; + size_type N, nrows, ncols, i; + PT origin; + + iterator operator ++(int) { iterator tmp = *this; i++; return tmp; } + iterator operator --(int) { iterator tmp = *this; i--; return tmp; } + iterator &operator ++() { ++i; return *this; } + iterator &operator --() { --i; return *this; } + iterator &operator +=(difference_type ii) { i += ii; return *this; } + iterator &operator -=(difference_type ii) { i -= ii; return *this; } + iterator operator +(difference_type ii) const + { iterator itt = *this; return (itt += ii); } + iterator operator -(difference_type ii) const + { iterator itt = *this; return (itt -= ii); } + difference_type operator -(const iterator &ii) const + { return (N ? (it - ii.it) / N : 0) + i - ii.i; } + + ITER operator *() const { return it+i*N; } + ITER operator [](int ii) const { return it + (i+ii) * N; } + + bool operator ==(const iterator &ii) const + { return (*this - ii) == difference_type(0); } + bool operator !=(const iterator &ii) const { return !(ii == *this); } + bool operator < (const iterator &ii) const + { return (*this - ii) < difference_type(0); } + + dense_compressed_iterator(void) {} + dense_compressed_iterator(const dense_compressed_iterator &ii) + : it(ii.it), N(ii.N), nrows(ii.nrows), ncols(ii.ncols), i(ii.i), + origin(ii.origin) {} + dense_compressed_iterator(const ITER &iter, size_type n, size_type r, + size_type c, size_type ii, PT o) + : it(iter), N(n), nrows(r), ncols(c), i(ii), origin(o) { } + + }; + + /* ******************************************************************** */ + /* Read only reference on a compressed sparse vector */ + /* ******************************************************************** */ + + template + struct cs_vector_ref_iterator { + PT1 pr; + PT2 ir; + + typedef typename std::iterator_traits::value_type value_type; + typedef PT1 pointer; + typedef typename std::iterator_traits::reference reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + typedef cs_vector_ref_iterator iterator; + + cs_vector_ref_iterator(void) {} + cs_vector_ref_iterator(PT1 p1, PT2 p2) : pr(p1), ir(p2) {} + + inline size_type index(void) const { return (*ir) - shift; } + iterator &operator ++() { ++pr; ++ir; return *this; } + iterator operator ++(int) { iterator tmp = *this; ++(*this); return tmp; } + iterator &operator --() { --pr; --ir; return *this; } + iterator operator --(int) { iterator tmp = *this; --(*this); return tmp; } + + reference operator *() const { return *pr; } + pointer operator ->() const { return pr; } + + bool operator ==(const iterator &i) const { return (i.pr==pr);} + bool operator !=(const iterator &i) const { return (i.pr!=pr);} + }; + + template struct cs_vector_ref { + PT1 pr; + PT2 ir; + size_type n, size_; + + typedef cs_vector_ref this_type; + typedef typename std::iterator_traits::value_type value_type; + typedef typename linalg_traits::const_iterator const_iterator; + + cs_vector_ref(PT1 pt1, PT2 pt2, size_type nnz, size_type ns) + : pr(pt1), ir(pt2), n(nnz), size_(ns) {} + cs_vector_ref(void) {} + + size_type size(void) const { return size_; } + + const_iterator begin(void) const { return const_iterator(pr, ir); } + const_iterator end(void) const { return const_iterator(pr+n, ir+n); } + + value_type operator[](size_type i) const + { return linalg_traits::access(pr, begin(), end(),i); } + }; + + template + struct linalg_traits > { + typedef cs_vector_ref this_type; + typedef linalg_const is_reference; + typedef abstract_vector linalg_type; + typedef typename std::iterator_traits::value_type value_type; + typedef value_type origin_type; + typedef typename std::iterator_traits::value_type reference; + typedef cs_vector_ref_iterator::pointer, + typename const_pointer::pointer, shift> const_iterator; + typedef abstract_null_type iterator; + typedef abstract_sparse storage_type; + typedef linalg_true index_sorted; + static size_type size(const this_type &v) { return v.size(); } + static iterator begin(this_type &v) { return v.begin(); } + static const_iterator begin(const this_type &v) { return v.begin(); } + static iterator end(this_type &v) { return v.end(); } + static const_iterator end(const this_type &v) { return v.end(); } + static const origin_type* origin(const this_type &v) { return v.pr; } + static value_type access(const origin_type *, const const_iterator &b, + const const_iterator &e, size_type i) { + if (b.ir == e.ir) return value_type(0); + PT2 p = std::lower_bound(b.ir, e.ir, i+shift); + return (*p == i+shift && p != e.ir) ? b.pr[p-b.ir] : value_type(0); + } + }; + + template + std::ostream &operator << + (std::ostream &o, const cs_vector_ref& m) + { gmm::write(o,m); return o; } + + template + inline size_type nnz(const cs_vector_ref& l) { return l.n; } + + /* ******************************************************************** */ + /* Read only reference on a compressed sparse column matrix */ + /* ******************************************************************** */ + + template + struct sparse_compressed_iterator { + typedef typename std::iterator_traits::value_type value_type; + typedef const value_type *pointer; + typedef const value_type &reference; + typedef ptrdiff_t difference_type; + typedef size_t size_type; + typedef std::random_access_iterator_tag iterator_category; + typedef sparse_compressed_iterator iterator; + + PT1 pr; + PT2 ir; + PT3 jc; + size_type n; + const value_type *origin; + + iterator operator ++(int) { iterator tmp = *this; jc++; return tmp; } + iterator operator --(int) { iterator tmp = *this; jc--; return tmp; } + iterator &operator ++() { jc++; return *this; } + iterator &operator --() { jc--; return *this; } + iterator &operator +=(difference_type i) { jc += i; return *this; } + iterator &operator -=(difference_type i) { jc -= i; return *this; } + iterator operator +(difference_type i) const + { iterator itt = *this; return (itt += i); } + iterator operator -(difference_type i) const + { iterator itt = *this; return (itt -= i); } + difference_type operator -(const iterator &i) const { return jc - i.jc; } + + reference operator *() const { return pr + *jc - shift; } + reference operator [](int ii) { return pr + *(jc+ii) - shift; } + + bool operator ==(const iterator &i) const { return (jc == i.jc); } + bool operator !=(const iterator &i) const { return !(i == *this); } + bool operator < (const iterator &i) const { return (jc < i.jc); } + + sparse_compressed_iterator(void) {} + sparse_compressed_iterator(PT1 p1, PT2 p2, PT3 p3, size_type nn, + const value_type *o) + : pr(p1), ir(p2), jc(p3), n(nn), origin(o) { } + + }; + + template + struct csc_matrix_ref { + PT1 pr; // values. + PT2 ir; // row indexes. + PT3 jc; // column repartition on pr and ir. + size_type nc, nr; + + typedef typename std::iterator_traits::value_type value_type; + csc_matrix_ref(PT1 pt1, PT2 pt2, PT3 pt3, size_type nrr, size_type ncc) + : pr(pt1), ir(pt2), jc(pt3), nc(ncc), nr(nrr) {} + csc_matrix_ref(void) {} + + size_type nrows(void) const { return nr; } + size_type ncols(void) const { return nc; } + + value_type operator()(size_type i, size_type j) const + { return mat_col(*this, j)[i]; } + }; + + template + struct linalg_traits > { + typedef csc_matrix_ref this_type; + typedef linalg_const is_reference; + typedef abstract_matrix linalg_type; + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::value_type reference; + typedef value_type origin_type; + typedef abstract_sparse storage_type; + typedef abstract_null_type sub_row_type; + typedef abstract_null_type const_sub_row_type; + typedef abstract_null_type row_iterator; + typedef abstract_null_type const_row_iterator; + typedef abstract_null_type sub_col_type; + typedef cs_vector_ref::pointer, + typename const_pointer::pointer, shift> const_sub_col_type; + typedef sparse_compressed_iterator::pointer, + typename const_pointer::pointer, + typename const_pointer::pointer, + shift> const_col_iterator; + typedef abstract_null_type col_iterator; + typedef col_major sub_orientation; + typedef linalg_true index_sorted; + static size_type nrows(const this_type &m) { return m.nrows(); } + static size_type ncols(const this_type &m) { return m.ncols(); } + static const_col_iterator col_begin(const this_type &m) + { return const_col_iterator(m.pr, m.ir, m.jc, m.nr, m.pr); } + static const_col_iterator col_end(const this_type &m) + { return const_col_iterator(m.pr, m.ir, m.jc + m.nc, m.nr, m.pr); } + static const_sub_col_type col(const const_col_iterator &it) { + return const_sub_col_type(it.pr + *(it.jc) - shift, + it.ir + *(it.jc) - shift, *(it.jc + 1) - *(it.jc), it.n); + } + static const origin_type* origin(const this_type &m) { return m.pr; } + static value_type access(const const_col_iterator &itcol, size_type j) + { return col(itcol)[j]; } + }; + + + template + std::ostream &operator << + (std::ostream &o, const csc_matrix_ref& m) + { gmm::write(o,m); return o; } + + /* ******************************************************************** */ + /* Read only reference on a compressed sparse row matrix */ + /* ******************************************************************** */ + + template + struct csr_matrix_ref { + PT1 pr; // values. + PT2 ir; // column indexes. + PT3 jc; // row repartition on pr and ir. + size_type nc, nr; + + typedef typename std::iterator_traits::value_type value_type; + csr_matrix_ref(PT1 pt1, PT2 pt2, PT3 pt3, size_type nrr, size_type ncc) + : pr(pt1), ir(pt2), jc(pt3), nc(ncc), nr(nrr) {} + csr_matrix_ref(void) {} + + size_type nrows(void) const { return nr; } + size_type ncols(void) const { return nc; } + + value_type operator()(size_type i, size_type j) const + { return mat_row(*this, i)[j]; } + }; + + template + struct linalg_traits > { + typedef csr_matrix_ref this_type; + typedef linalg_const is_reference; + typedef abstract_matrix linalg_type; + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::value_type reference; + typedef value_type origin_type; + typedef abstract_sparse storage_type; + typedef abstract_null_type sub_col_type; + typedef abstract_null_type const_sub_col_type; + typedef abstract_null_type col_iterator; + typedef abstract_null_type const_col_iterator; + typedef abstract_null_type sub_row_type; + typedef cs_vector_ref::pointer, + typename const_pointer::pointer, shift> + const_sub_row_type; + typedef sparse_compressed_iterator::pointer, + typename const_pointer::pointer, + typename const_pointer::pointer, + shift> const_row_iterator; + typedef abstract_null_type row_iterator; + typedef row_major sub_orientation; + typedef linalg_true index_sorted; + static size_type nrows(const this_type &m) { return m.nrows(); } + static size_type ncols(const this_type &m) { return m.ncols(); } + static const_row_iterator row_begin(const this_type &m) + { return const_row_iterator(m.pr, m.ir, m.jc, m.nc, m.pr); } + static const_row_iterator row_end(const this_type &m) + { return const_row_iterator(m.pr, m.ir, m.jc + m.nr, m.nc, m.pr); } + static const_sub_row_type row(const const_row_iterator &it) { + return const_sub_row_type(it.pr + *(it.jc) - shift, + it.ir + *(it.jc) - shift, *(it.jc + 1) - *(it.jc), it.n); + } + static const origin_type* origin(const this_type &m) { return m.pr; } + static value_type access(const const_row_iterator &itrow, size_type j) + { return row(itrow)[j]; } + }; + + template + std::ostream &operator << + (std::ostream &o, const csr_matrix_ref& m) + { gmm::write(o,m); return o; } + + /* ********************************************************************* */ + /* */ + /* Simple interface for C arrays */ + /* */ + /* ********************************************************************* */ + + template struct array1D_reference { + + typedef typename std::iterator_traits::value_type value_type; + + PT begin, end; + + const value_type &operator[](size_type i) const { return *(begin+i); } + value_type &operator[](size_type i) { return *(begin+i); } + + array1D_reference(PT begin_, size_type s) : begin(begin_), end(begin_+s) {} + }; + + template + struct linalg_traits > { + typedef array1D_reference this_type; + typedef this_type origin_type; + typedef typename which_reference::is_reference is_reference; + typedef abstract_vector linalg_type; + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::reference reference; + typedef PT iterator; + typedef PT const_iterator; + typedef abstract_dense storage_type; + typedef linalg_true index_sorted; + static size_type size(const this_type &v) { return v.end - v.begin; } + static iterator begin(this_type &v) { return v.begin; } + static const_iterator begin(const this_type &v) { return v.begin; } + static iterator end(this_type &v) { return v.end; } + static const_iterator end(const this_type &v) { return v.end; } + static origin_type* origin(this_type &v) { return &v; } + static const origin_type* origin(const this_type &v) { return &v; } + static void clear(origin_type*, const iterator &it, const iterator &ite) + { std::fill(it, ite, value_type(0)); } + static void do_clear(this_type &v) + { std::fill(v.begin, v.end, value_type(0)); } + static value_type access(const origin_type *, const const_iterator &it, + const const_iterator &, size_type i) + { return it[i]; } + static reference access(origin_type *, const iterator &it, + const iterator &, size_type i) + { return it[i]; } + static void resize(this_type &, size_type ) + { GMM_ASSERT1(false, "Not resizable vector"); } + }; + + template std::ostream &operator << + (std::ostream &o, const array1D_reference& v) + { gmm::write(o,v); return o; } + + template struct array2D_col_reference { + + typedef typename std::iterator_traits::value_type T; + typedef typename std::iterator_traits::reference reference; + typedef typename const_reference::reference const_reference; + typedef PT iterator; + typedef typename const_pointer::pointer const_iterator; + + PT begin_; + size_type nbl, nbc; + + inline const_reference operator ()(size_type l, size_type c) const { + GMM_ASSERT2(l < nbl && c < nbc, "out of range"); + return *(begin_ + c*nbl+l); + } + inline reference operator ()(size_type l, size_type c) { + GMM_ASSERT2(l < nbl && c < nbc, "out of range"); + return *(begin_ + c*nbl+l); + } + + void resize(size_type, size_type); + void reshape(size_type m, size_type n) { + GMM_ASSERT2(n*m == nbl*nbc, "dimensions mismatch"); + nbl = m; nbc = n; + } + + void fill(T a, T b = T(0)) { + std::fill(begin_, begin_+nbc*nbl, b); + iterator p = begin_, e = begin_+nbc*nbl; + while (p < e) { *p = a; p += nbl+1; } + } + inline size_type nrows(void) const { return nbl; } + inline size_type ncols(void) const { return nbc; } + + iterator begin(void) { return begin_; } + const_iterator begin(void) const { return begin_; } + iterator end(void) { return begin_+nbl*nbc; } + const_iterator end(void) const { return begin_+nbl*nbc; } + + array2D_col_reference(PT begin__, size_type nrows_, size_type ncols_) + : begin_(begin__), nbl(nrows_), nbc(ncols_) {} + }; + + template struct linalg_traits > { + typedef array2D_col_reference this_type; + typedef this_type origin_type; + typedef typename which_reference::is_reference is_reference; + typedef abstract_matrix linalg_type; + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::reference reference; + typedef abstract_dense storage_type; + typedef tab_ref_reg_spaced_with_origin sub_row_type; + typedef tab_ref_reg_spaced_with_origin const_sub_row_type; + typedef dense_compressed_iterator row_iterator; + typedef dense_compressed_iterator const_row_iterator; + typedef tab_ref_with_origin sub_col_type; + typedef tab_ref_with_origin const_sub_col_type; + typedef dense_compressed_iterator col_iterator; + typedef dense_compressed_iterator const_col_iterator; + typedef col_and_row sub_orientation; + typedef linalg_true index_sorted; + static size_type nrows(const this_type &m) { return m.nrows(); } + static size_type ncols(const this_type &m) { return m.ncols(); } + static const_sub_row_type row(const const_row_iterator &it) + { return const_sub_row_type(*it, it.nrows, it.ncols, it.origin); } + static const_sub_col_type col(const const_col_iterator &it) + { return const_sub_col_type(*it, *it + it.nrows, it.origin); } + static sub_row_type row(const row_iterator &it) + { return sub_row_type(*it, it.nrows, it.ncols, it.origin); } + static sub_col_type col(const col_iterator &it) + { return sub_col_type(*it, *it + it.nrows, it.origin); } + static row_iterator row_begin(this_type &m) + { return row_iterator(m.begin(), 1, m.nrows(), m.ncols(), 0, &m); } + static row_iterator row_end(this_type &m) + { return row_iterator(m.begin(), 1, m.nrows(), m.ncols(), m.nrows(), &m); } + static const_row_iterator row_begin(const this_type &m) + { return const_row_iterator(m.begin(), 1, m.nrows(), m.ncols(), 0, &m); } + static const_row_iterator row_end(const this_type &m) { + return const_row_iterator(m.begin(), 1, m.nrows(), + m.ncols(), m.nrows(), &m); + } + static col_iterator col_begin(this_type &m) + { return col_iterator(m.begin(), m.nrows(), m.nrows(), m.ncols(), 0, &m); } + static col_iterator col_end(this_type &m) { + return col_iterator(m.begin(), m.nrows(), m.nrows(), m.ncols(), + m.ncols(), &m); + } + static const_col_iterator col_begin(const this_type &m) { + return const_col_iterator(m.begin(), m.nrows(), m.nrows(), + m.ncols(), 0, &m); + } + static const_col_iterator col_end(const this_type &m) { + return const_col_iterator(m.begin(), m.nrows(),m.nrows(),m.ncols(), + m.ncols(), &m); + } + static origin_type* origin(this_type &m) { return &m; } + static const origin_type* origin(const this_type &m) { return &m; } + static void do_clear(this_type &m) { m.fill(value_type(0)); } + static value_type access(const const_col_iterator &itcol, size_type j) + { return (*itcol)[j]; } + static reference access(const col_iterator &itcol, size_type j) + { return (*itcol)[j]; } + static void resize(this_type &v, size_type m, size_type n) + { v.resize(m,n); } + static void reshape(this_type &v, size_type m, size_type n) + { v.reshape(m, n); } + }; + + template std::ostream &operator << + (std::ostream &o, const array2D_col_reference& m) + { gmm::write(o,m); return o; } + + + + template struct array2D_row_reference { + + typedef typename std::iterator_traits::value_type T; + typedef typename std::iterator_traits::reference reference; + typedef typename const_reference::reference const_reference; + typedef PT iterator; + typedef typename const_pointer::pointer const_iterator; + + PT begin_; + size_type nbl, nbc; + + inline const_reference operator ()(size_type l, size_type c) const { + GMM_ASSERT2(l < nbl && c < nbc, "out of range"); + return *(begin_ + l*nbc+c); + } + inline reference operator ()(size_type l, size_type c) { + GMM_ASSERT2(l < nbl && c < nbc, "out of range"); + return *(begin_ + l*nbc+c); + } + + void resize(size_type, size_type); + void reshape(size_type m, size_type n) { + GMM_ASSERT2(n*m == nbl*nbc, "dimensions mismatch"); + nbl = m; nbc = n; + } + + void fill(T a, T b = T(0)) { + std::fill(begin_, begin_+nbc*nbl, b); + iterator p = begin_, e = begin_+nbc*nbl; + while (p < e) { *p = a; p += nbc+1; } + } + inline size_type nrows(void) const { return nbl; } + inline size_type ncols(void) const { return nbc; } + + iterator begin(void) { return begin_; } + const_iterator begin(void) const { return begin_; } + iterator end(void) { return begin_+nbl*nbc; } + const_iterator end(void) const { return begin_+nbl*nbc; } + + array2D_row_reference(PT begin__, size_type nrows_, size_type ncols_) + : begin_(begin__), nbl(nrows_), nbc(ncols_) {} + }; + + template struct linalg_traits > { + typedef array2D_row_reference this_type; + typedef this_type origin_type; + typedef typename which_reference::is_reference is_reference; + typedef abstract_matrix linalg_type; + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::reference reference; + typedef abstract_dense storage_type; + typedef tab_ref_reg_spaced_with_origin sub_col_type; + typedef tab_ref_reg_spaced_with_origin const_sub_col_type; + typedef dense_compressed_iterator col_iterator; + typedef dense_compressed_iterator const_col_iterator; + typedef tab_ref_with_origin sub_row_type; + typedef tab_ref_with_origin const_sub_row_type; + typedef dense_compressed_iterator row_iterator; + typedef dense_compressed_iterator const_row_iterator; + typedef col_and_row sub_orientation; + typedef linalg_true index_sorted; + static size_type ncols(const this_type &m) { return m.ncols(); } + static size_type nrows(const this_type &m) { return m.nrows(); } + static const_sub_col_type col(const const_col_iterator &it) + { return const_sub_col_type(*it, it.ncols, it.nrows, it.origin); } + static const_sub_row_type row(const const_row_iterator &it) + { return const_sub_row_type(*it, *it + it.ncols, it.origin); } + static sub_col_type col(const col_iterator &it) + { return sub_col_type(*it, *it, it.ncols, it.nrows, it.origin); } + static sub_row_type row(const row_iterator &it) + { return sub_row_type(*it, *it + it.ncols, it.origin); } + static col_iterator col_begin(this_type &m) + { return col_iterator(m.begin(), 1, m.ncols(), m.nrows(), 0, &m); } + static col_iterator col_end(this_type &m) + { return col_iterator(m.begin(), 1, m.ncols(), m.nrows(), m.ncols(), &m); } + static const_col_iterator col_begin(const this_type &m) + { return const_col_iterator(m.begin(), 1, m.ncols(), m.nrows(), 0, &m); } + static const_col_iterator col_end(const this_type &m) { + return const_col_iterator(m.begin(), 1, m.ncols(), + m.nrows(), m.ncols(), &m); + } + static row_iterator row_begin(this_type &m) + { return row_iterator(m.begin(), m.ncols(), m.ncols(), m.nrows(), 0, &m); } + static row_iterator row_end(this_type &m) { + return row_iterator(m.begin(), m.ncols(), m.ncols(), m.nrows(), + m.nrows(), &m); + } + static const_row_iterator row_begin(const this_type &m) { + return const_row_iterator(m.begin(), m.ncols(), m.ncols(), m.nrows(), + 0, &m); + } + static const_row_iterator row_end(const this_type &m) { + return const_row_iterator(m.begin(), m.ncols(), m.ncols(), m.nrows(), + m.nrows(), &m); + } + static origin_type* origin(this_type &m) { return &m; } + static const origin_type* origin(const this_type &m) { return &m; } + static void do_clear(this_type &m) { m.fill(value_type(0)); } + static value_type access(const const_row_iterator &itrow, size_type j) + { return (*itrow)[j]; } + static reference access(const row_iterator &itrow, size_type j) + { return (*itrow)[j]; } + static void resize(this_type &v, size_type m, size_type n) + { v.resize(m,n); } + static void reshape(this_type &v, size_type m, size_type n) + { v.reshape(m, n); } + }; + + template std::ostream &operator << + (std::ostream &o, const array2D_row_reference& m) + { gmm::write(o,m); return o; } + + + + + + +} + + +#endif // GMM_INTERFACE_H__ diff --git a/src/linear_algebra/gmm/gmm_interface_bgeot.h b/src/linear_algebra/gmm/gmm_interface_bgeot.h new file mode 100644 index 00000000..69a34bdf --- /dev/null +++ b/src/linear_algebra/gmm/gmm_interface_bgeot.h @@ -0,0 +1,83 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_interface_bgeot.h + @author Yves Renard + @date October 13, 2002. + @brief interface for bgeot::small_vector +*/ +#ifndef GMM_INTERFACE_BGEOT_H__ +#define GMM_INTERFACE_BGEOT_H__ + + +namespace gmm { + + /* ********************************************************************* */ + /* */ + /* Traits for bgeot objects */ + /* */ + /* ********************************************************************* */ + + template struct linalg_traits > { + typedef bgeot::small_vector this_type; + typedef this_type origin_type; + typedef linalg_false is_reference; + typedef abstract_vector linalg_type; + typedef T value_type; + typedef T& reference; + typedef typename this_type::iterator iterator; + typedef typename this_type::const_iterator const_iterator; + typedef abstract_dense storage_type; + typedef linalg_true index_sorted; + static size_type size(const this_type &v) { return v.size(); } + static iterator begin(this_type &v) { return v.begin(); } + static const_iterator begin(const this_type &v) { return v.begin(); } + static iterator end(this_type &v) { return v.end(); } + static const_iterator end(const this_type &v) { return v.end(); } + static origin_type* origin(this_type &v) { return &v; } + static const origin_type* origin(const this_type &v) { return &v; } + static void clear(origin_type* o, const iterator &it, const iterator &ite) + { std::fill(it, ite, value_type(0)); } + static void do_clear(this_type &v) + { std::fill(v.begin(), v.end(), value_type(0)); } + static value_type access(const origin_type *, const const_iterator &it, + const const_iterator &, size_type i) + { return it[i]; } + static reference access(origin_type *, const iterator &it, + const iterator &, size_type i) + { return it[i]; } + static void resize(this_type &v, size_type n) { v.resize(n); } + }; + +} + + +#endif // GMM_INTERFACE_BGEOT_H__ diff --git a/src/linear_algebra/gmm/gmm_iter.h b/src/linear_algebra/gmm/gmm_iter.h new file mode 100644 index 00000000..b8db9ddc --- /dev/null +++ b/src/linear_algebra/gmm/gmm_iter.h @@ -0,0 +1,162 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_iter.h + @author Yves Renard + @date February 10, 2003. + @brief Iteration object. +*/ + +#ifndef GMM_ITER_H__ +#define GMM_ITER_H__ + +#include "gmm_kernel.h" +#include + +namespace gmm { + + /** The Iteration object calculates whether the solution has reached the + desired accuracy, or whether the maximum number of iterations has + been reached. + + The method finished() checks the convergence. The first() + method is used to determine the first iteration of the loop. + */ + class iteration { + protected : + double rhsn; /* Right hand side norm. */ + size_type maxiter; /* Max. number of iterations. */ + int noise; /* if noise > 0 iterations are printed. */ + double resmax; /* maximum residu. */ + double resminreach, resadd; + double diverged_res; /* Threshold beyond which the iterative */ + /* is considered to diverge. */ + size_type nit; /* iteration number. */ + double res; /* last computed residu. */ + std::string name; /* eventually, name of the method. */ + bool written; + void (*callback)(const gmm::iteration&); + public : + + void init(void) { + nit = 0; res = 0.0; written = false; + resminreach = 1E200; resadd = 0.0; + callback = 0; + } + + iteration(double r = 1.0E-8, int noi = 0, size_type mit = size_type(-1), + double div_res = 1E200) + : rhsn(1.0), maxiter(mit), noise(noi), resmax(r), diverged_res(div_res) + { init(); } + + void operator ++(int) { nit++; written = false; resadd += res; } + void operator ++() { (*this)++; } + + bool first(void) { return nit == 0; } + + /* get/set the "noisyness" (verbosity) of the solvers */ + int get_noisy(void) const { return noise; } + void set_noisy(int n) { noise = n; } + void reduce_noisy(void) { if (noise > 0) noise--; } + + double get_resmax(void) const { return resmax; } + void set_resmax(double r) { resmax = r; } + + double get_res() const { return res; } + void enforce_converged(bool c = true) + { if (c) res = double(0); else res = rhsn * resmax + double(1); } + + /* change the user-definable callback, called after each iteration */ + void set_callback(void (*t)(const gmm::iteration&)) { + callback = t; + } + + double get_diverged_residual(void) const { return diverged_res; } + void set_diverged_residual(double r) { diverged_res = r; } + + size_type get_iteration(void) const { return nit; } + void set_iteration(size_type i) { nit = i; } + + size_type get_maxiter(void) const { return maxiter; } + void set_maxiter(size_type i) { maxiter = i; } + + double get_rhsnorm(void) const { return rhsn; } + void set_rhsnorm(double r) { rhsn = r; } + + bool converged(void) { + return !isnan(res) && res <= rhsn * resmax; + } + bool converged(double nr) { + res = gmm::abs(nr); + resminreach = std::min(resminreach, res); + return converged(); + } + template bool converged(const VECT &v) + { return converged(gmm::vect_norm2(v)); } + bool diverged(void) { + return isnan(res) || (nit>=maxiter) + || (res>=rhsn*diverged_res && nit > 4); + } + bool diverged(double nr) { + res = gmm::abs(nr); + resminreach = std::min(resminreach, res); + return diverged(); + } + + bool finished(double nr) { + if (callback) callback(*this); + if (noise > 0 && !written) { + double a = (rhsn == 0) ? 1.0 : rhsn; + converged(nr); + cout << name << " iter " << std::setw(3) << nit << " residual " + << std::setw(12) << gmm::abs(nr) / a; +// if (nit % 100 == 0 && nit > 0) { +// cout << " (residual min " << resminreach / a << " mean val " +// << resadd / (100.0 * a) << " )"; +// resadd = 0.0; +// } + cout << endl; + written = true; + } + return (converged(nr) || diverged(nr)); + } + template bool finished_vect(const VECT &v) + { return finished(double(gmm::vect_norm2(v))); } + + + void set_name(const std::string &n) { name = n; } + const std::string &get_name(void) const { return name; } + + }; + +} + +#endif /* GMM_ITER_H__ */ diff --git a/src/linear_algebra/gmm/gmm_iter_solvers.h b/src/linear_algebra/gmm/gmm_iter_solvers.h new file mode 100644 index 00000000..a2406f35 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_iter_solvers.h @@ -0,0 +1,111 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_iter_solvers.h + @author Yves Renard + @date October 13, 2002. + @brief Include standard gmm iterative solvers (cg, gmres, ...) +*/ +#ifndef GMM_ITER_SOLVERS_H__ +#define GMM_ITER_SOLVERS_H__ + +#include "gmm_iter.h" + + +namespace gmm { + + /** mixed method to find a zero of a real function G, a priori + * between a and b. If the zero is not between a and b, iterations + * of secant are applied. When a convenient interval is found, + * iterations of dichotomie and regula falsi are applied. + */ + template + T find_root(const FUNC &G, T a = T(0), T b = T(1), + T tol = gmm::default_tol(T())) { + T c, Ga = G(a), Gb = G(b), Gc, d; + d = gmm::abs(b - a); +#if 0 + for (int i = 0; i < 4; i++) { /* secant iterations. */ + if (d < tol) return (b + a) / 2.0; + c = b - Gb * (b - a) / (Gb - Ga); Gc = G(c); + a = b; b = c; Ga = Gb; Gb = Gc; + d = gmm::abs(b - a); + } +#endif + while (Ga * Gb > 0.0) { /* secant iterations. */ + if (d < tol) return (b + a) / 2.0; + c = b - Gb * (b - a) / (Gb - Ga); Gc = G(c); + a = b; b = c; Ga = Gb; Gb = Gc; + d = gmm::abs(b - a); + } + + c = std::max(a, b); a = std::min(a, b); b = c; + while (d > tol) { + c = b - (b - a) * (Gb / (Gb - Ga)); /* regula falsi. */ + if (c > b) c = b; + if (c < a) c = a; + Gc = G(c); + if (Gc*Gb > 0) { b = c; Gb = Gc; } else { a = c; Ga = Gc; } + c = (b + a) / 2.0 ; Gc = G(c); /* Dichotomie. */ + if (Gc*Gb > 0) { b = c; Gb = Gc; } else { a = c; Ga = Gc; } + d = gmm::abs(b - a); c = (b + a) / 2.0; if ((c == a) || (c == b)) d = 0.0; + } + return (b + a) / 2.0; + } + +} + +#include "gmm_precond_diagonal.h" +#include "gmm_precond_ildlt.h" +#include "gmm_precond_ildltt.h" +#include "gmm_precond_mr_approx_inverse.h" +#include "gmm_precond_ilu.h" +#include "gmm_precond_ilut.h" +#include "gmm_precond_ilutp.h" + + + +#include "gmm_solver_cg.h" +#include "gmm_solver_bicgstab.h" +#include "gmm_solver_qmr.h" +#include "gmm_solver_constrained_cg.h" +#include "gmm_solver_Schwarz_additive.h" +#include "gmm_modified_gram_schmidt.h" +#include "gmm_tri_solve.h" +#include "gmm_solver_gmres.h" +#include "gmm_solver_bfgs.h" +#include "gmm_least_squares_cg.h" + +// #include "gmm_solver_idgmres.h" + + + +#endif // GMM_ITER_SOLVERS_H__ diff --git a/src/linear_algebra/gmm/gmm_kernel.h b/src/linear_algebra/gmm/gmm_kernel.h new file mode 100644 index 00000000..7fdae248 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_kernel.h @@ -0,0 +1,55 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_kernel.h + @author Yves Renard + @date November 15, 2003. + @brief Include the base gmm files. + */ + +#ifndef GMM_KERNEL_H__ +#define GMM_KERNEL_H__ + +#include "gmm_def.h" +#include "gmm_blas.h" +#include "gmm_real_part.h" +#include "gmm_interface.h" +#include "gmm_sub_vector.h" +#include "gmm_sub_matrix.h" +#include "gmm_vector_to_matrix.h" +#include "gmm_vector.h" +#include "gmm_matrix.h" +#include "gmm_tri_solve.h" +#include "gmm_blas_interface.h" +#include "gmm_lapack_interface.h" + + +#endif // GMM_KERNEL_H__ diff --git a/src/linear_algebra/gmm/gmm_lapack_interface.h b/src/linear_algebra/gmm/gmm_lapack_interface.h new file mode 100644 index 00000000..37bc48f5 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_lapack_interface.h @@ -0,0 +1,484 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2003-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_lapack_interface.h + @author Yves Renard + @date October 7, 2003. + @brief gmm interface for LAPACK +*/ + +#ifndef GMM_LAPACK_INTERFACE_H +#define GMM_LAPACK_INTERFACE_H + +#include "gmm_blas_interface.h" +#include "gmm_dense_lu.h" +#include "gmm_dense_qr.h" + + +#if defined(GMM_USES_LAPACK) + +namespace gmm { + + /* ********************************************************************** */ + /* Operations interfaced for T = float, double, std::complex */ + /* or std::complex : */ + /* */ + /* lu_factor(dense_matrix, std::vector) */ + /* lu_solve(dense_matrix, std::vector, std::vector) */ + /* lu_solve(dense_matrix, std::vector, std::vector, */ + /* std::vector) */ + /* lu_solve_transposed(dense_matrix, std::vector, std::vector,*/ + /* std::vector) */ + /* lu_inverse(dense_matrix) */ + /* lu_inverse(dense_matrix, std::vector, dense_matrix) */ + /* */ + /* qr_factor(dense_matrix, dense_matrix, dense_matrix) */ + /* */ + /* implicit_qr_algorithm(dense_matrix, std::vector) */ + /* implicit_qr_algorithm(dense_matrix, std::vector, */ + /* dense_matrix) */ + /* implicit_qr_algorithm(dense_matrix, std::vector >) */ + /* implicit_qr_algorithm(dense_matrix, std::vector >, */ + /* dense_matrix) */ + /* */ + /* geev_interface_right */ + /* geev_interface_left */ + /* */ + /* schur(dense_matrix, dense_matrix, dense_matrix) */ + /* */ + /* svd(dense_matrix, dense_matrix, dense_matrix, std::vector) */ + /* svd(dense_matrix, dense_matrix, dense_matrix, */ + /* std::vector >) */ + /* */ + /* ********************************************************************** */ + + /* ********************************************************************** */ + /* LAPACK functions used. */ + /* ********************************************************************** */ + + extern "C" { + void sgetrf_(...); void dgetrf_(...); void cgetrf_(...); void zgetrf_(...); + void sgetrs_(...); void dgetrs_(...); void cgetrs_(...); void zgetrs_(...); + void sgetri_(...); void dgetri_(...); void cgetri_(...); void zgetri_(...); + void sgeqrf_(...); void dgeqrf_(...); void cgeqrf_(...); void zgeqrf_(...); + void sorgqr_(...); void dorgqr_(...); void cungqr_(...); void zungqr_(...); + void sormqr_(...); void dormqr_(...); void cunmqr_(...); void zunmqr_(...); + void sgees_ (...); void dgees_ (...); void cgees_ (...); void zgees_ (...); + void sgeev_ (...); void dgeev_ (...); void cgeev_ (...); void zgeev_ (...); + void sgeesx_(...); void dgeesx_(...); void cgeesx_(...); void zgeesx_(...); + void sgesvd_(...); void dgesvd_(...); void cgesvd_(...); void zgesvd_(...); + } + + /* ********************************************************************** */ + /* LU decomposition. */ + /* ********************************************************************** */ + +# define getrf_interface(lapack_name, base_type) inline \ + size_type lu_factor(dense_matrix &A, lapack_ipvt &ipvt){ \ + GMMLAPACK_TRACE("getrf_interface"); \ + BLAS_INT m = BLAS_INT(mat_nrows(A)), n = BLAS_INT(mat_ncols(A)), lda(m); \ + long info(-1L); \ + if (m && n) lapack_name(&m, &n, &A(0,0), &lda, ipvt.pfirst(), &info); \ + if ((sizeof(BLAS_INT) == 4) || \ + ((info & 0xFFFFFFFF00000000L) && !(info & 0x00000000FFFFFFFFL))) \ + /* For compatibility with lapack version with 32 bit integer. */ \ + ipvt.set_to_int32(); \ + return size_type(int(info & 0x00000000FFFFFFFFL)); \ + } + + getrf_interface(sgetrf_, BLAS_S) + getrf_interface(dgetrf_, BLAS_D) + getrf_interface(cgetrf_, BLAS_C) + getrf_interface(zgetrf_, BLAS_Z) + + /* ********************************************************************* */ + /* LU solve. */ + /* ********************************************************************* */ + +# define getrs_interface(f_name, trans1, lapack_name, base_type) inline \ + void f_name(const dense_matrix &A, \ + const lapack_ipvt &ipvt, std::vector &x, \ + const std::vector &b) { \ + GMMLAPACK_TRACE("getrs_interface"); \ + BLAS_INT n = BLAS_INT(mat_nrows(A)), info(0), nrhs(1); \ + gmm::copy(b, x); trans1; \ + if (n) \ + lapack_name(&t,&n,&nrhs,&(A(0,0)),&n,ipvt.pfirst(),&x[0],&n,&info); \ + } + +# define getrs_trans_n const char t = 'N' +# define getrs_trans_t const char t = 'T' + + getrs_interface(lu_solve, getrs_trans_n, sgetrs_, BLAS_S) + getrs_interface(lu_solve, getrs_trans_n, dgetrs_, BLAS_D) + getrs_interface(lu_solve, getrs_trans_n, cgetrs_, BLAS_C) + getrs_interface(lu_solve, getrs_trans_n, zgetrs_, BLAS_Z) + getrs_interface(lu_solve_transposed, getrs_trans_t, sgetrs_, BLAS_S) + getrs_interface(lu_solve_transposed, getrs_trans_t, dgetrs_, BLAS_D) + getrs_interface(lu_solve_transposed, getrs_trans_t, cgetrs_, BLAS_C) + getrs_interface(lu_solve_transposed, getrs_trans_t, zgetrs_, BLAS_Z) + + /* ********************************************************************* */ + /* LU inverse. */ + /* ********************************************************************* */ + +# define getri_interface(lapack_name, base_type) inline \ + void lu_inverse(const dense_matrix &LU, \ + const lapack_ipvt &ipvt, const dense_matrix &A_) { \ + GMMLAPACK_TRACE("getri_interface"); \ + dense_matrix& \ + A = const_cast &>(A_); \ + BLAS_INT n = BLAS_INT(mat_nrows(A)), info(0), lwork(-1); \ + base_type work1; \ + if (n) { \ + gmm::copy(LU, A); \ + lapack_name(&n, &A(0,0), &n, ipvt.pfirst(), &work1, &lwork, &info); \ + lwork = int(gmm::real(work1)); \ + std::vector work(lwork); \ + lapack_name(&n, &A(0,0), &n, ipvt.pfirst(), &work[0], &lwork,&info); \ + } \ + } + + getri_interface(sgetri_, BLAS_S) + getri_interface(dgetri_, BLAS_D) + getri_interface(cgetri_, BLAS_C) + getri_interface(zgetri_, BLAS_Z) + + /* ********************************************************************** */ + /* QR factorization. */ + /* ********************************************************************** */ + +# define geqrf_interface(lapack_name1, base_type) inline \ + void qr_factor(dense_matrix &A){ \ + GMMLAPACK_TRACE("geqrf_interface"); \ + BLAS_INT m = BLAS_INT(mat_nrows(A)), n=BLAS_INT(mat_ncols(A)); \ + BLAS_INT info(0), lwork(-1); \ + base_type work1; \ + if (m && n) { \ + std::vector tau(n); \ + lapack_name1(&m, &n, &A(0,0), &m, &tau[0], &work1 , &lwork, &info); \ + lwork = BLAS_INT(gmm::real(work1)); \ + std::vector work(lwork); \ + lapack_name1(&m, &n, &A(0,0), &m, &tau[0], &work[0], &lwork, &info); \ + GMM_ASSERT1(!info, "QR factorization failed"); \ + } \ + } + + geqrf_interface(sgeqrf_, BLAS_S) + geqrf_interface(dgeqrf_, BLAS_D) + // For complex values, housholder vectors are not the same as in + // gmm::lu_factor. Impossible to interface for the moment. + // geqrf_interface(cgeqrf_, BLAS_C) + // geqrf_interface(zgeqrf_, BLAS_Z) + +# define geqrf_interface2(lapack_name1, lapack_name2, base_type) inline \ + void qr_factor(const dense_matrix &A, \ + dense_matrix &Q, dense_matrix &R) { \ + GMMLAPACK_TRACE("geqrf_interface2"); \ + BLAS_INT m = BLAS_INT(mat_nrows(A)), n=BLAS_INT(mat_ncols(A)); \ + BLAS_INT info(0), lwork(-1); \ + base_type work1; \ + if (m && n) { \ + std::copy(A.begin(), A.end(), Q.begin()); \ + std::vector tau(n); \ + lapack_name1(&m, &n, &Q(0,0), &m, &tau[0], &work1 , &lwork, &info); \ + lwork = BLAS_INT(gmm::real(work1)); \ + std::vector work(lwork); \ + lapack_name1(&m, &n, &Q(0,0), &m, &tau[0], &work[0], &lwork, &info); \ + GMM_ASSERT1(!info, "QR factorization failed"); \ + base_type *p = &R(0,0), *q = &Q(0,0); \ + for (BLAS_INT j = 0; j < n; ++j, q += m-n) \ + for (BLAS_INT i = 0; i < n; ++i, ++p, ++q) \ + *p = (j < i) ? base_type(0) : *q; \ + lapack_name2(&m, &n, &n, &Q(0,0), &m,&tau[0],&work[0],&lwork,&info); \ + } \ + else gmm::clear(Q); \ + } + + geqrf_interface2(sgeqrf_, sorgqr_, BLAS_S) + geqrf_interface2(dgeqrf_, dorgqr_, BLAS_D) + geqrf_interface2(cgeqrf_, cungqr_, BLAS_C) + geqrf_interface2(zgeqrf_, zungqr_, BLAS_Z) + + /* ********************************************************************** */ + /* QR algorithm for eigenvalues search. */ + /* ********************************************************************** */ + +# define gees_interface(lapack_name, base_type) \ + template inline void implicit_qr_algorithm( \ + const dense_matrix &A, const VECT &eigval_, \ + dense_matrix &Q, \ + double tol=gmm::default_tol(base_type()), bool compvect = true) { \ + GMMLAPACK_TRACE("gees_interface"); \ + typedef bool (*L_fp)(...); L_fp p = 0; \ + BLAS_INT n=BLAS_INT(mat_nrows(A)), info(0), lwork(-1), sdim; \ + base_type work1; \ + if (!n) return; \ + dense_matrix H(n,n); gmm::copy(A, H); \ + char jobvs = (compvect ? 'V' : 'N'), sort = 'N'; \ + std::vector rwork(n), eigv1(n), eigv2(n); \ + lapack_name(&jobvs, &sort, p, &n, &H(0,0), &n, &sdim, &eigv1[0], \ + &eigv2[0], &Q(0,0), &n, &work1, &lwork, &rwork[0], &info); \ + lwork = BLAS_INT(gmm::real(work1)); \ + std::vector work(lwork); \ + lapack_name(&jobvs, &sort, p, &n, &H(0,0), &n, &sdim, &eigv1[0], \ + &eigv2[0], &Q(0,0), &n, &work[0], &lwork, &rwork[0],&info);\ + GMM_ASSERT1(!info, "QR algorithm failed"); \ + extract_eig(H, const_cast(eigval_), tol); \ + } + +# define gees_interface2(lapack_name, base_type) \ + template inline void implicit_qr_algorithm( \ + const dense_matrix &A, const VECT &eigval_, \ + dense_matrix &Q, \ + double tol=gmm::default_tol(base_type()), bool compvect = true) { \ + GMMLAPACK_TRACE("gees_interface2"); \ + typedef bool (*L_fp)(...); L_fp p = 0; \ + BLAS_INT n=BLAS_INT(mat_nrows(A)), info(0), lwork(-1), sdim; \ + base_type work1; \ + if (!n) return; \ + dense_matrix H(n,n); gmm::copy(A, H); \ + char jobvs = (compvect ? 'V' : 'N'), sort = 'N'; \ + std::vector rwork(n), eigvv(n*2); \ + lapack_name(&jobvs, &sort, p, &n, &H(0,0), &n, &sdim, &eigvv[0], \ + &Q(0,0), &n, &work1, &lwork, &rwork[0], &rwork[0], &info); \ + lwork = BLAS_INT(gmm::real(work1)); \ + std::vector work(lwork); \ + lapack_name(&jobvs, &sort, p, &n, &H(0,0), &n, &sdim, &eigvv[0], \ + &Q(0,0), &n, &work[0], &lwork, &rwork[0], &rwork[0],&info);\ + GMM_ASSERT1(!info, "QR algorithm failed"); \ + extract_eig(H, const_cast(eigval_), tol); \ + } + + gees_interface(sgees_, BLAS_S) + gees_interface(dgees_, BLAS_D) + gees_interface2(cgees_, BLAS_C) + gees_interface2(zgees_, BLAS_Z) + + +# define jobv_right char jobvl = 'N', jobvr = 'V'; +# define jobv_left char jobvl = 'V', jobvr = 'N'; + +# define geev_interface(lapack_name, base_type, side) \ + template inline void geev_interface_ ## side( \ + const dense_matrix &A, const VECT &eigval_, \ + dense_matrix &Q) { \ + GMMLAPACK_TRACE("geev_interface"); \ + BLAS_INT n = BLAS_INT(mat_nrows(A)), info(0), lwork(-1); \ + base_type work1; \ + if (!n) return; \ + dense_matrix H(n,n); gmm::copy(A, H); \ + jobv_ ## side \ + std::vector eigvr(n), eigvi(n); \ + lapack_name(&jobvl, &jobvr, &n, &H(0,0), &n, &eigvr[0], &eigvi[0], \ + &Q(0,0), &n, &Q(0,0), &n, &work1, &lwork, &info); \ + lwork = BLAS_INT(gmm::real(work1)); \ + std::vector work(lwork); \ + lapack_name(&jobvl, &jobvr, &n, &H(0,0), &n, &eigvr[0], &eigvi[0], \ + &Q(0,0), &n, &Q(0,0), &n, &work[0], &lwork, &info); \ + GMM_ASSERT1(!info, "QR algorithm failed"); \ + gmm::copy(eigvr, gmm::real_part(const_cast(eigval_))); \ + gmm::copy(eigvi, gmm::imag_part(const_cast(eigval_))); \ + } + +# define geev_interface2(lapack_name, base_type, side) \ + template inline void geev_interface_ ## side( \ + const dense_matrix &A, const VECT &eigval_, \ + dense_matrix &Q) { \ + GMMLAPACK_TRACE("geev_interface"); \ + BLAS_INT n = BLAS_INT(mat_nrows(A)), info(0), lwork(-1); \ + base_type work1; \ + if (!n) return; \ + dense_matrix H(n,n); gmm::copy(A, H); \ + jobv_ ## side \ + std::vector rwork(2*n); \ + std::vector eigv(n); \ + lapack_name(&jobvl, &jobvr, &n, &H(0,0), &n, &eigv[0], &Q(0,0), &n, \ + &Q(0,0), &n, &work1, &lwork, &rwork[0], &info); \ + lwork = BLAS_INT(gmm::real(work1)); \ + std::vector work(lwork); \ + lapack_name(&jobvl, &jobvr, &n, &H(0,0), &n, &eigv[0], &Q(0,0), &n, \ + &Q(0,0), &n, &work[0], &lwork, &rwork[0], &info); \ + GMM_ASSERT1(!info, "QR algorithm failed"); \ + gmm::copy(eigv, const_cast(eigval_)); \ + } + + geev_interface(sgeev_, BLAS_S, right) + geev_interface(dgeev_, BLAS_D, right) + geev_interface2(cgeev_, BLAS_C, right) + geev_interface2(zgeev_, BLAS_Z, right) + + geev_interface(sgeev_, BLAS_S, left) + geev_interface(dgeev_, BLAS_D, left) + geev_interface2(cgeev_, BLAS_C, left) + geev_interface2(zgeev_, BLAS_Z, left) + + + /* ********************************************************************** */ + /* SCHUR algorithm: */ + /* A = Q*S*(Q^T), with Q orthogonal and S upper quasi-triangula */ + /* ********************************************************************** */ + +# define geesx_interface(lapack_name, base_type) inline \ + void schur(dense_matrix &A, \ + dense_matrix &S, \ + dense_matrix &Q) { \ + GMMLAPACK_TRACE("geesx_interface"); \ + BLAS_INT m = BLAS_INT(mat_nrows(A)), n = BLAS_INT(mat_ncols(A)); \ + GMM_ASSERT1(m == n, "Schur decomposition requires square matrix"); \ + char jobvs = 'V', sort = 'N', sense = 'N'; \ + bool select = false; \ + BLAS_INT lwork = 8*n, sdim = 0, liwork = 1; \ + std::vector work(lwork), wr(n), wi(n); \ + std::vector iwork(liwork); \ + std::vector bwork(1); \ + resize(S, n, n); copy(A, S); \ + resize(Q, n, n); \ + base_type rconde(0), rcondv(0); \ + BLAS_INT info(0); \ + lapack_name(&jobvs, &sort, &select, &sense, &n, &S(0,0), &n, \ + &sdim, &wr[0], &wi[0], &Q(0,0), &n, &rconde, &rcondv, \ + &work[0], &lwork, &iwork[0], &liwork, &bwork[0], &info);\ + GMM_ASSERT1(!info, "SCHUR algorithm failed"); \ + } + +# define geesx_interface2(lapack_name, base_type) inline \ + void schur(dense_matrix &A, \ + dense_matrix &S, \ + dense_matrix &Q) { \ + GMMLAPACK_TRACE("geesx_interface"); \ + BLAS_INT m = BLAS_INT(mat_nrows(A)), n = BLAS_INT(mat_ncols(A)); \ + GMM_ASSERT1(m == n, "Schur decomposition requires square matrix"); \ + char jobvs = 'V', sort = 'N', sense = 'N'; \ + bool select = false; \ + BLAS_INT lwork = 8*n, sdim = 0; \ + std::vector rwork(lwork); \ + std::vector work(lwork), w(n); \ + std::vector bwork(1); \ + resize(S, n, n); copy(A, S); \ + resize(Q, n, n); \ + base_type rconde(0), rcondv(0); \ + BLAS_INT info(0); \ + lapack_name(&jobvs, &sort, &select, &sense, &n, &S(0,0), &n, \ + &sdim, &w[0], &Q(0,0), &n, &rconde, &rcondv, \ + &work[0], &lwork, &rwork[0], &bwork[0], &info); \ + GMM_ASSERT1(!info, "SCHUR algorithm failed"); \ + } + + geesx_interface(sgeesx_, BLAS_S) + geesx_interface(dgeesx_, BLAS_D) + geesx_interface2(cgeesx_, BLAS_C) + geesx_interface2(zgeesx_, BLAS_Z) + + template + void schur(const MAT &A_, MAT &S, MAT &Q) { + MAT A(A_); + schur(A, S, Q); + } + + + /* ********************************************************************** */ + /* Interface to SVD. Does not correspond to a Gmm++ functionnality. */ + /* Author : Sebastian Nowozin */ + /* ********************************************************************** */ + +# define gesvd_interface(lapack_name, base_type) inline \ + void svd(dense_matrix &X, \ + dense_matrix &U, \ + dense_matrix &Vtransposed, \ + std::vector &sigma) { \ + GMMLAPACK_TRACE("gesvd_interface"); \ + BLAS_INT m = BLAS_INT(mat_nrows(X)), n = BLAS_INT(mat_ncols(X)); \ + BLAS_INT mn_min = m < n ? m : n; \ + sigma.resize(mn_min); \ + std::vector work(15 * mn_min); \ + BLAS_INT lwork = BLAS_INT(work.size()); \ + resize(U, m, m); \ + resize(Vtransposed, n, n); \ + char job = 'A'; \ + BLAS_INT info(0); \ + lapack_name(&job, &job, &m, &n, &X(0,0), &m, &sigma[0], &U(0,0), \ + &m, &Vtransposed(0,0), &n, &work[0], &lwork, &info); \ + } + +# define cgesvd_interface(lapack_name, base_type, base_type2) inline \ + void svd(dense_matrix &X, \ + dense_matrix &U, \ + dense_matrix &Vtransposed, \ + std::vector &sigma) { \ + GMMLAPACK_TRACE("gesvd_interface"); \ + BLAS_INT m = BLAS_INT(mat_nrows(X)), n = BLAS_INT(mat_ncols(X)); \ + BLAS_INT mn_min = m < n ? m : n; \ + sigma.resize(mn_min); \ + std::vector work(15 * mn_min); \ + std::vector rwork(5 * mn_min); \ + BLAS_INT lwork = BLAS_INT(work.size()); \ + resize(U, m, m); \ + resize(Vtransposed, n, n); \ + char job = 'A'; \ + BLAS_INT info(0); \ + lapack_name(&job, &job, &m, &n, &X(0,0), &m, &sigma[0], &U(0,0), \ + &m, &Vtransposed(0,0), &n, &work[0], &lwork, \ + &rwork[0], &info); \ + } + + gesvd_interface(sgesvd_, BLAS_S) + gesvd_interface(dgesvd_, BLAS_D) + cgesvd_interface(cgesvd_, BLAS_C, BLAS_S) + cgesvd_interface(zgesvd_, BLAS_Z, BLAS_D) + + template + void svd(const MAT &X_, MAT &U, MAT &Vtransposed, VEC &sigma) { + MAT X(X_); + svd(X, U, Vtransposed, sigma); + } + + + + +} + +#else + +namespace gmm +{ +template +void schur(const MAT &A_, MAT &S, MAT &Q) +{ + GMM_ASSERT1(false, "Use of function schur(A,S,Q) requires GetFEM " + "to be built with Lapack"); +} + +}// namespace gmm + +#endif // GMM_USES_LAPACK + +#endif // GMM_LAPACK_INTERFACE_H diff --git a/src/linear_algebra/gmm/gmm_least_squares_cg.h b/src/linear_algebra/gmm/gmm_least_squares_cg.h new file mode 100644 index 00000000..e4e22ae0 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_least_squares_cg.h @@ -0,0 +1,96 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard, Benjamin Schleimer + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_leastsquares_cg.h + @author Benjamin Schleimer + @date January 23, 2007. + @brief Conjugate gradient least squares algorithm. + Algorithm taken from http://www.stat.washington.edu/wxs/Stat538-w05/Notes/conjugate-gradients.pdf page 6 +*/ +#ifndef GMM_LEAST_SQUARES_CG_H__ +#define GMM_LEAST_SQUARES_CG_H__ + +#include "gmm_kernel.h" +#include "gmm_iter.h" +#include "gmm_conjugated.h" + +namespace gmm { + + template + void least_squares_cg(const Matrix& C, Vector1& x, const Vector2& y, + iteration &iter) { + + typedef typename temporary_dense_vector::vector_type temp_vector; + typedef typename linalg_traits::value_type T; + + T rho, rho_1(0), a; + temp_vector p(vect_size(x)), q(vect_size(y)), g(vect_size(x)); + temp_vector r(vect_size(y)); + iter.set_rhsnorm(gmm::sqrt(gmm::abs(vect_hp(y, y)))); + + if (iter.get_rhsnorm() == 0.0) + clear(x); + else { + mult(C, scaled(x, T(-1)), y, r); + mult(conjugated(C), r, g); + rho = vect_hp(g, g); + copy(g, p); + + while (!iter.finished_vect(g)) { + + if (!iter.first()) { + rho = vect_hp(g, g); + add(g, scaled(p, rho / rho_1), p); + } + + mult(C, p, q); + + a = rho / vect_hp(q, q); + add(scaled(p, a), x); + add(scaled(q, -a), r); + // NOTE: how do we minimize the impact to the transpose? + mult(conjugated(C), r, g); + rho_1 = rho; + + ++iter; + } + } + } + + template inline + void least_squares_cg(const Matrix& C, const Vector1& x, const Vector2& y, + iteration &iter) + { least_squares_cg(C, linalg_const_cast(x), y, iter); } +} + + +#endif // GMM_SOLVER_CG_H__ diff --git a/src/linear_algebra/gmm/gmm_matrix.h b/src/linear_algebra/gmm/gmm_matrix.h new file mode 100644 index 00000000..60b29178 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_matrix.h @@ -0,0 +1,1195 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/** @file gmm_matrix.h + @author Yves Renard + @date October 13, 2002. + @brief Declaration of some matrix types (gmm::dense_matrix, + gmm::row_matrix, gmm::col_matrix, gmm::csc_matrix, etc.) +*/ + +#ifndef GMM_MATRIX_H__ +#define GMM_MATRIX_H__ + +#include "gmm_vector.h" +#include "gmm_sub_vector.h" +#include "gmm_sub_matrix.h" +#include "gmm_transposed.h" + +namespace gmm +{ + + /* ******************************************************************** */ + /* */ + /* Identity matrix */ + /* */ + /* ******************************************************************** */ + + struct identity_matrix { + template void build_with(const MAT &) {} + }; + + template inline + void add(const identity_matrix&, M &v1) { + size_type n = std::min(gmm::mat_nrows(v1), gmm::mat_ncols(v1)); + for (size_type i = 0; i < n; ++i) + v1(i,i) += typename linalg_traits::value_type(1); + } + template inline + void add(const identity_matrix &II, const M &v1) + { add(II, linalg_const_cast(v1)); } + + template inline + void mult(const identity_matrix&, const V1 &v1, V2 &v2) + { copy(v1, v2); } + template inline + void mult(const identity_matrix&, const V1 &v1, const V2 &v2) + { copy(v1, v2); } + template inline + void mult(const identity_matrix&, const V1 &v1, const V2 &v2, V3 &v3) + { add(v1, v2, v3); } + template inline + void mult(const identity_matrix&, const V1 &v1, const V2 &v2, const V3 &v3) + { add(v1, v2, v3); } + template inline + void left_mult(const identity_matrix&, const V1 &v1, V2 &v2) + { copy(v1, v2); } + template inline + void left_mult(const identity_matrix&, const V1 &v1, const V2 &v2) + { copy(v1, v2); } + template inline + void right_mult(const identity_matrix&, const V1 &v1, V2 &v2) + { copy(v1, v2); } + template inline + void right_mult(const identity_matrix&, const V1 &v1, const V2 &v2) + { copy(v1, v2); } + template inline + void transposed_left_mult(const identity_matrix&, const V1 &v1, V2 &v2) + { copy(v1, v2); } + template inline + void transposed_left_mult(const identity_matrix&, const V1 &v1,const V2 &v2) + { copy(v1, v2); } + template inline + void transposed_right_mult(const identity_matrix&, const V1 &v1, V2 &v2) + { copy(v1, v2); } + template inline + void transposed_right_mult(const identity_matrix&,const V1 &v1,const V2 &v2) + { copy(v1, v2); } + template void copy_ident(const identity_matrix&, M &m) { + size_type i = 0, n = std::min(mat_nrows(m), mat_ncols(m)); + clear(m); + for (; i < n; ++i) m(i,i) = typename linalg_traits::value_type(1); + } + template inline void copy(const identity_matrix&, M &m) + { copy_ident(identity_matrix(), m); } + template inline void copy(const identity_matrix &, const M &m) + { copy_ident(identity_matrix(), linalg_const_cast(m)); } + template inline + typename linalg_traits::value_type + vect_sp(const identity_matrix &, const V1 &v1, const V2 &v2) + { return vect_sp(v1, v2); } + template inline + typename linalg_traits::value_type + vect_hp(const identity_matrix &, const V1 &v1, const V2 &v2) + { return vect_hp(v1, v2); } + template inline bool is_identity(const M&) { return false; } + inline bool is_identity(const identity_matrix&) { return true; } + + /* ******************************************************************** */ + /* */ + /* Row matrix */ + /* */ + /* ******************************************************************** */ + + template class row_matrix { + protected : + std::vector li; /* array of rows. */ + size_type nc; + + public : + + typedef typename linalg_traits::reference reference; + typedef typename linalg_traits::value_type value_type; + + row_matrix(size_type r, size_type c) : li(r, V(c)), nc(c) {} + row_matrix(void) : nc(0) {} + reference operator ()(size_type l, size_type c) + { return li[l][c]; } + value_type operator ()(size_type l, size_type c) const + { return li[l][c]; } + + void clear_mat(); + void resize(size_type m, size_type n); + + typename std::vector::iterator begin(void) + { return li.begin(); } + typename std::vector::iterator end(void) + { return li.end(); } + typename std::vector::const_iterator begin(void) const + { return li.begin(); } + typename std::vector::const_iterator end(void) const + { return li.end(); } + + + V& row(size_type i) { return li[i]; } + const V& row(size_type i) const { return li[i]; } + V& operator[](size_type i) { return li[i]; } + const V& operator[](size_type i) const { return li[i]; } + + inline size_type nrows(void) const { return li.size(); } + inline size_type ncols(void) const { return nc; } + + void swap(row_matrix &m) { std::swap(li, m.li); std::swap(nc, m.nc); } + void swap_row(size_type i, size_type j) { std::swap(li[i], li[j]); } + }; + + template void row_matrix::resize(size_type m, size_type n) { + size_type nr = std::min(nrows(), m); + li.resize(m); + for (size_type i=nr; i < m; ++i) gmm::resize(li[i], n); + if (n != nc) { + for (size_type i=0; i < nr; ++i) gmm::resize(li[i], n); + nc = n; + } + } + + + template void row_matrix::clear_mat() + { for (size_type i=0; i < nrows(); ++i) clear(li[i]); } + + template struct linalg_traits > { + typedef row_matrix this_type; + typedef this_type origin_type; + typedef linalg_false is_reference; + typedef abstract_matrix linalg_type; + typedef typename linalg_traits::value_type value_type; + typedef typename linalg_traits::reference reference; + typedef typename linalg_traits::storage_type storage_type; + typedef V & sub_row_type; + typedef const V & const_sub_row_type; + typedef typename std::vector::iterator row_iterator; + typedef typename std::vector::const_iterator const_row_iterator; + typedef abstract_null_type sub_col_type; + typedef abstract_null_type const_sub_col_type; + typedef abstract_null_type col_iterator; + typedef abstract_null_type const_col_iterator; + typedef row_major sub_orientation; + typedef linalg_true index_sorted; + static size_type nrows(const this_type &m) { return m.nrows(); } + static size_type ncols(const this_type &m) { return m.ncols(); } + static row_iterator row_begin(this_type &m) { return m.begin(); } + static row_iterator row_end(this_type &m) { return m.end(); } + static const_row_iterator row_begin(const this_type &m) + { return m.begin(); } + static const_row_iterator row_end(const this_type &m) + { return m.end(); } + static const_sub_row_type row(const const_row_iterator &it) + { return const_sub_row_type(*it); } + static sub_row_type row(const row_iterator &it) + { return sub_row_type(*it); } + static origin_type* origin(this_type &m) { return &m; } + static const origin_type* origin(const this_type &m) { return &m; } + static void do_clear(this_type &m) { m.clear_mat(); } + static value_type access(const const_row_iterator &itrow, size_type j) + { return (*itrow)[j]; } + static reference access(const row_iterator &itrow, size_type j) + { return (*itrow)[j]; } + static void resize(this_type &v, size_type m, size_type n) + { v.resize(m, n); } + static void reshape(this_type &, size_type, size_type) + { GMM_ASSERT1(false, "Sorry, to be done"); } + }; + + template std::ostream &operator << + (std::ostream &o, const row_matrix& m) { gmm::write(o,m); return o; } + + /* ******************************************************************** */ + /* */ + /* Column matrix */ + /* */ + /* ******************************************************************** */ + + template class col_matrix { + protected : + std::vector li; /* array of columns. */ + size_type nr; + + public : + + typedef typename linalg_traits::reference reference; + typedef typename linalg_traits::value_type value_type; + + col_matrix(size_type r, size_type c) : li(c, V(r)), nr(r) { } + col_matrix(void) : nr(0) {} + reference operator ()(size_type l, size_type c) + { return li[c][l]; } + value_type operator ()(size_type l, size_type c) const + { return li[c][l]; } + + void clear_mat(); + void resize(size_type, size_type); + + V& col(size_type i) { return li[i]; } + const V& col(size_type i) const { return li[i]; } + V& operator[](size_type i) { return li[i]; } + const V& operator[](size_type i) const { return li[i]; } + + typename std::vector::iterator begin(void) + { return li.begin(); } + typename std::vector::iterator end(void) + { return li.end(); } + typename std::vector::const_iterator begin(void) const + { return li.begin(); } + typename std::vector::const_iterator end(void) const + { return li.end(); } + + inline size_type ncols(void) const { return li.size(); } + inline size_type nrows(void) const { return nr; } + + void swap(col_matrix &m) { std::swap(li, m.li); std::swap(nr, m.nr); } + void swap_col(size_type i, size_type j) { std::swap(li[i], li[j]); } + }; + + template void col_matrix::resize(size_type m, size_type n) { + size_type nc = std::min(ncols(), n); + li.resize(n); + for (size_type i=nc; i < n; ++i) gmm::resize(li[i], m); + if (m != nr) { + for (size_type i=0; i < nc; ++i) gmm::resize(li[i], m); + nr = m; + } + } + + template void col_matrix::clear_mat() + { for (size_type i=0; i < ncols(); ++i) clear(li[i]); } + + template struct linalg_traits > { + typedef col_matrix this_type; + typedef this_type origin_type; + typedef linalg_false is_reference; + typedef abstract_matrix linalg_type; + typedef typename linalg_traits::value_type value_type; + typedef typename linalg_traits::reference reference; + typedef typename linalg_traits::storage_type storage_type; + typedef V &sub_col_type; + typedef const V &const_sub_col_type; + typedef typename std::vector::iterator col_iterator; + typedef typename std::vector::const_iterator const_col_iterator; + typedef abstract_null_type sub_row_type; + typedef abstract_null_type const_sub_row_type; + typedef abstract_null_type row_iterator; + typedef abstract_null_type const_row_iterator; + typedef col_major sub_orientation; + typedef linalg_true index_sorted; + static size_type nrows(const this_type &m) { return m.nrows(); } + static size_type ncols(const this_type &m) { return m.ncols(); } + static col_iterator col_begin(this_type &m) { return m.begin(); } + static col_iterator col_end(this_type &m) { return m.end(); } + static const_col_iterator col_begin(const this_type &m) + { return m.begin(); } + static const_col_iterator col_end(const this_type &m) + { return m.end(); } + static const_sub_col_type col(const const_col_iterator &it) + { return *it; } + static sub_col_type col(const col_iterator &it) + { return *it; } + static origin_type* origin(this_type &m) { return &m; } + static const origin_type* origin(const this_type &m) { return &m; } + static void do_clear(this_type &m) { m.clear_mat(); } + static value_type access(const const_col_iterator &itcol, size_type j) + { return (*itcol)[j]; } + static reference access(const col_iterator &itcol, size_type j) + { return (*itcol)[j]; } + static void resize(this_type &v, size_type m, size_type n) + { v.resize(m,n); } + static void reshape(this_type &, size_type, size_type) + { GMM_ASSERT1(false, "Sorry, to be done"); } + }; + + template std::ostream &operator << + (std::ostream &o, const col_matrix& m) { gmm::write(o,m); return o; } + + /* ******************************************************************** */ + /* */ + /* Dense matrix */ + /* */ + /* ******************************************************************** */ + + template class dense_matrix : public std::vector { + public: + typedef typename std::vector::size_type size_type; + typedef typename std::vector::iterator iterator; + typedef typename std::vector::const_iterator const_iterator; + typedef typename std::vector::reference reference; + typedef typename std::vector::const_reference const_reference; + + protected: + size_type nbc, nbl; + + public: + + inline const_reference operator ()(size_type l, size_type c) const { + GMM_ASSERT2(l < nbl && c < nbc, "out of range"); + return *(this->begin() + c*nbl+l); + } + inline reference operator ()(size_type l, size_type c) { + GMM_ASSERT2(l < nbl && c < nbc, "out of range"); + return *(this->begin() + c*nbl+l); + } + + std::vector &as_vector(void) { return *this; } + const std::vector &as_vector(void) const { return *this; } + + void resize(size_type, size_type); + void base_resize(size_type, size_type); + void reshape(size_type, size_type); + + void fill(T a, T b = T(0)); + inline size_type nrows(void) const { return nbl; } + inline size_type ncols(void) const { return nbc; } + void swap(dense_matrix &m) + { std::vector::swap(m); std::swap(nbc, m.nbc); std::swap(nbl, m.nbl); } + + dense_matrix(size_type l, size_type c) + : std::vector(c*l), nbc(c), nbl(l) {} + dense_matrix(void) { nbl = nbc = 0; } + }; + + template void dense_matrix::reshape(size_type m,size_type n) { + GMM_ASSERT2(n*m == nbl*nbc, "dimensions mismatch"); + nbl = m; nbc = n; + } + + template void dense_matrix::base_resize(size_type m, + size_type n) + { std::vector::resize(n*m); nbl = m; nbc = n; } + + template void dense_matrix::resize(size_type m, size_type n) { + if (n*m > nbc*nbl) std::vector::resize(n*m); + if (m < nbl) { + for (size_type i = 1; i < std::min(nbc, n); ++i) + std::copy(this->begin()+i*nbl, this->begin()+(i*nbl+m), + this->begin()+i*m); + for (size_type i = std::min(nbc, n); i < n; ++i) + std::fill(this->begin()+(i*m), this->begin()+(i+1)*m, T(0)); + } + else if (m > nbl) { /* do nothing when the nb of rows does not change */ + for (size_type i = std::min(nbc, n); i > 1; --i) + std::copy(this->begin()+(i-1)*nbl, this->begin()+i*nbl, + this->begin()+(i-1)*m); + for (size_type i = 0; i < std::min(nbc, n); ++i) + std::fill(this->begin()+(i*m+nbl), this->begin()+(i+1)*m, T(0)); + } + if (n*m < nbc*nbl) std::vector::resize(n*m); + nbl = m; nbc = n; + } + + template void dense_matrix::fill(T a, T b) { + std::fill(this->begin(), this->end(), b); + size_type n = std::min(nbl, nbc); + if (a != b) for (size_type i = 0; i < n; ++i) (*this)(i,i) = a; + } + + template struct linalg_traits > { + typedef dense_matrix this_type; + typedef this_type origin_type; + typedef linalg_false is_reference; + typedef abstract_matrix linalg_type; + typedef T value_type; + typedef T& reference; + typedef abstract_dense storage_type; + typedef tab_ref_reg_spaced_with_origin sub_row_type; + typedef tab_ref_reg_spaced_with_origin const_sub_row_type; + typedef dense_compressed_iterator row_iterator; + typedef dense_compressed_iterator const_row_iterator; + typedef tab_ref_with_origin sub_col_type; + typedef tab_ref_with_origin const_sub_col_type; + typedef dense_compressed_iterator col_iterator; + typedef dense_compressed_iterator const_col_iterator; + typedef col_and_row sub_orientation; + typedef linalg_true index_sorted; + static size_type nrows(const this_type &m) { return m.nrows(); } + static size_type ncols(const this_type &m) { return m.ncols(); } + static const_sub_row_type row(const const_row_iterator &it) + { return const_sub_row_type(*it, it.nrows, it.ncols, it.origin); } + static const_sub_col_type col(const const_col_iterator &it) + { return const_sub_col_type(*it, *it + it.nrows, it.origin); } + static sub_row_type row(const row_iterator &it) + { return sub_row_type(*it, it.nrows, it.ncols, it.origin); } + static sub_col_type col(const col_iterator &it) + { return sub_col_type(*it, *it + it.nrows, it.origin); } + static row_iterator row_begin(this_type &m) + { return row_iterator(m.begin(), m.size() ? 1 : 0, m.nrows(), m.ncols(), 0, &m); } + static row_iterator row_end(this_type &m) + { return row_iterator(m.begin(), m.size() ? 1 : 0, m.nrows(), m.ncols(), m.nrows(), &m); } + static const_row_iterator row_begin(const this_type &m) + { return const_row_iterator(m.begin(), m.size() ? 1 : 0, m.nrows(), m.ncols(), 0, &m); } + static const_row_iterator row_end(const this_type &m) + { return const_row_iterator(m.begin(), m.size() ? 1 : 0, m.nrows(), m.ncols(), m.nrows(), &m); } + static col_iterator col_begin(this_type &m) + { return col_iterator(m.begin(), m.nrows(), m.nrows(), m.ncols(), 0, &m); } + static col_iterator col_end(this_type &m) + { return col_iterator(m.begin(), m.nrows(), m.nrows(), m.ncols(), m.ncols(), &m); } + static const_col_iterator col_begin(const this_type &m) + { return const_col_iterator(m.begin(), m.nrows(), m.nrows(), m.ncols(), 0, &m); } + static const_col_iterator col_end(const this_type &m) + { return const_col_iterator(m.begin(),m.nrows(),m.nrows(),m.ncols(),m.ncols(), &m); } + static origin_type* origin(this_type &m) { return &m; } + static const origin_type* origin(const this_type &m) { return &m; } + static void do_clear(this_type &m) { m.fill(value_type(0)); } + static value_type access(const const_col_iterator &itcol, size_type j) + { return (*itcol)[j]; } + static reference access(const col_iterator &itcol, size_type j) + { return (*itcol)[j]; } + static void resize(this_type &v, size_type m, size_type n) + { v.resize(m,n); } + static void reshape(this_type &v, size_type m, size_type n) + { v.reshape(m, n); } + }; + + template std::ostream &operator << + (std::ostream &o, const dense_matrix& m) { gmm::write(o,m); return o; } + + + /* ******************************************************************** */ + /* */ + /* Read only compressed sparse column matrix */ + /* */ + /* ******************************************************************** */ + + template + struct csc_matrix { + + std::vector pr; + std::vector ir; + std::vector jc; + size_type nc, nr; + + typedef T value_type; + typedef T& access_type; + + template void init_with_good_format(const Matrix &B); + template void init_with(const Matrix &A); + void init_with(const col_matrix > &B) + { init_with_good_format(B); } + void init_with(const col_matrix > &B) + { init_with_good_format(B); } + template + void init_with(const csc_matrix_ref& B) + { init_with_good_format(B); } + template + void init_with(const csc_matrix& B) + { init_with_good_format(B); } + + void init_with_identity(size_type n); + + csc_matrix(void) : nc(0), nr(0) {} + csc_matrix(size_type nnr, size_type nnc); + + size_type nrows(void) const { return nr; } + size_type ncols(void) const { return nc; } + void swap(csc_matrix &m) { + std::swap(pr, m.pr); + std::swap(ir, m.ir); std::swap(jc, m.jc); + std::swap(nc, m.nc); std::swap(nr, m.nr); + } + value_type operator()(size_type i, size_type j) const + { return mat_col(*this, j)[i]; } + }; + + template template + void csc_matrix::init_with_good_format(const Matrix &B) { + typedef typename linalg_traits::const_sub_col_type col_type; + nc = mat_ncols(B); nr = mat_nrows(B); + jc.resize(nc+1); + jc[0] = shift; + for (size_type j = 0; j < nc; ++j) { + jc[j+1] = IND_TYPE(jc[j] + nnz(mat_const_col(B, j))); + } + pr.resize(jc[nc]); + ir.resize(jc[nc]); + for (size_type j = 0; j < nc; ++j) { + col_type col = mat_const_col(B, j); + typename linalg_traits::t>::const_iterator + it = vect_const_begin(col), ite = vect_const_end(col); + for (size_type k = 0; it != ite; ++it, ++k) { + pr[jc[j]-shift+k] = *it; + ir[jc[j]-shift+k] = IND_TYPE(it.index() + shift); + } + } + } + + template + template + void csc_matrix::init_with(const Matrix &A) { + col_matrix > B(mat_nrows(A), mat_ncols(A)); + copy(A, B); + init_with_good_format(B); + } + + template + void csc_matrix::init_with_identity(size_type n) { + nc = nr = n; + pr.resize(nc); ir.resize(nc); jc.resize(nc+1); + for (size_type j = 0; j < nc; ++j) + { ir[j] = jc[j] = shift + j; pr[j] = T(1); } + jc[nc] = shift + nc; + } + + template + csc_matrix::csc_matrix(size_type nnr, size_type nnc) + : nc(nnc), nr(nnr) { + pr.resize(1); ir.resize(1); jc.resize(nc+1); + for (size_type j = 0; j <= nc; ++j) jc[j] = shift; + } + + template + struct linalg_traits > { + typedef csc_matrix this_type; + typedef linalg_const is_reference; + typedef abstract_matrix linalg_type; + typedef T value_type; + typedef T origin_type; + typedef T reference; + typedef abstract_sparse storage_type; + typedef abstract_null_type sub_row_type; + typedef abstract_null_type const_sub_row_type; + typedef abstract_null_type row_iterator; + typedef abstract_null_type const_row_iterator; + typedef abstract_null_type sub_col_type; + typedef cs_vector_ref + const_sub_col_type; + typedef sparse_compressed_iterator + const_col_iterator; + typedef abstract_null_type col_iterator; + typedef col_major sub_orientation; + typedef linalg_true index_sorted; + static size_type nrows(const this_type &m) { return m.nrows(); } + static size_type ncols(const this_type &m) { return m.ncols(); } + static const_col_iterator col_begin(const this_type &m) + { return const_col_iterator(&m.pr[0],&m.ir[0],&m.jc[0], m.nr, &m.pr[0]); } + static const_col_iterator col_end(const this_type &m) { + return const_col_iterator(&m.pr[0],&m.ir[0],&m.jc[0]+m.nc, + m.nr,&m.pr[0]); + } + static const_sub_col_type col(const const_col_iterator &it) { + return const_sub_col_type(it.pr + *(it.jc) - shift, + it.ir + *(it.jc) - shift, + *(it.jc + 1) - *(it.jc), it.n); + } + static const origin_type* origin(const this_type &m) { return &m.pr[0]; } + static void do_clear(this_type &m) { m.do_clear(); } + static value_type access(const const_col_iterator &itcol, size_type j) + { return col(itcol)[j]; } + }; + + template + std::ostream &operator << + (std::ostream &o, const csc_matrix& m) + { gmm::write(o,m); return o; } + + template + inline void copy(const identity_matrix &, csc_matrix& M) + { M.init_with_identity(mat_nrows(M)); } + + template + inline void copy(const Matrix &A, csc_matrix& M) + { M.init_with(A); } + + /* ******************************************************************** */ + /* */ + /* Read only compressed sparse row matrix */ + /* */ + /* ******************************************************************** */ + + template + struct csr_matrix { + + std::vector pr; // values. + std::vector ir; // col indices. + std::vector jc; // row repartition on pr and ir. + size_type nc, nr; + + typedef T value_type; + typedef T& access_type; + + + template void init_with_good_format(const Matrix &B); + void init_with(const row_matrix > &B) + { init_with_good_format(B); } + void init_with(const row_matrix > &B) + { init_with_good_format(B); } + template + void init_with(const csr_matrix_ref& B) + { init_with_good_format(B); } + template + void init_with(const csr_matrix& B) + { init_with_good_format(B); } + + template void init_with(const Matrix &A); + void init_with_identity(size_type n); + + csr_matrix(void) : nc(0), nr(0) {} + csr_matrix(size_type nnr, size_type nnc); + + size_type nrows(void) const { return nr; } + size_type ncols(void) const { return nc; } + void swap(csr_matrix &m) { + std::swap(pr, m.pr); + std::swap(ir,m.ir); std::swap(jc, m.jc); + std::swap(nc, m.nc); std::swap(nr,m.nr); + } + + value_type operator()(size_type i, size_type j) const + { return mat_row(*this, i)[j]; } + }; + + template template + void csr_matrix::init_with_good_format(const Matrix &B) { + typedef typename linalg_traits::const_sub_row_type row_type; + nc = mat_ncols(B); nr = mat_nrows(B); + jc.resize(nr+1); + jc[0] = shift; + for (size_type j = 0; j < nr; ++j) { + jc[j+1] = IND_TYPE(jc[j] + nnz(mat_const_row(B, j))); + } + pr.resize(jc[nr]); + ir.resize(jc[nr]); + for (size_type j = 0; j < nr; ++j) { + row_type row = mat_const_row(B, j); + typename linalg_traits::t>::const_iterator + it = vect_const_begin(row), ite = vect_const_end(row); + for (size_type k = 0; it != ite; ++it, ++k) { + pr[jc[j]-shift+k] = *it; + ir[jc[j]-shift+k] = IND_TYPE(it.index()+shift); + } + } + } + + template template + void csr_matrix::init_with(const Matrix &A) { + row_matrix > B(mat_nrows(A), mat_ncols(A)); + copy(A, B); + init_with_good_format(B); + } + + template + void csr_matrix::init_with_identity(size_type n) { + nc = nr = n; + pr.resize(nr); ir.resize(nr); jc.resize(nr+1); + for (size_type j = 0; j < nr; ++j) + { ir[j] = jc[j] = shift + j; pr[j] = T(1); } + jc[nr] = shift + nr; + } + + template + csr_matrix::csr_matrix(size_type nnr, size_type nnc) + : nc(nnc), nr(nnr) { + pr.resize(1); ir.resize(1); jc.resize(nr+1); + for (size_type j = 0; j < nr; ++j) jc[j] = shift; + jc[nr] = shift; + } + + + template + struct linalg_traits > { + typedef csr_matrix this_type; + typedef linalg_const is_reference; + typedef abstract_matrix linalg_type; + typedef T value_type; + typedef T origin_type; + typedef T reference; + typedef abstract_sparse storage_type; + typedef abstract_null_type sub_col_type; + typedef abstract_null_type const_sub_col_type; + typedef abstract_null_type col_iterator; + typedef abstract_null_type const_col_iterator; + typedef abstract_null_type sub_row_type; + typedef cs_vector_ref + const_sub_row_type; + typedef sparse_compressed_iterator + const_row_iterator; + typedef abstract_null_type row_iterator; + typedef row_major sub_orientation; + typedef linalg_true index_sorted; + static size_type nrows(const this_type &m) { return m.nrows(); } + static size_type ncols(const this_type &m) { return m.ncols(); } + static const_row_iterator row_begin(const this_type &m) + { return const_row_iterator(&m.pr[0], &m.ir[0], &m.jc[0], m.nc, &m.pr[0]); } + static const_row_iterator row_end(const this_type &m) + { return const_row_iterator(&m.pr[0], &m.ir[0], &m.jc[0] + m.nr, m.nc, &m.pr[0]); } + static const_sub_row_type row(const const_row_iterator &it) { + return const_sub_row_type(it.pr + *(it.jc) - shift, + it.ir + *(it.jc) - shift, + *(it.jc + 1) - *(it.jc), it.n); + } + static const origin_type* origin(const this_type &m) { return &m.pr[0]; } + static void do_clear(this_type &m) { m.do_clear(); } + static value_type access(const const_row_iterator &itrow, size_type j) + { return row(itrow)[j]; } + }; + + template + std::ostream &operator << + (std::ostream &o, const csr_matrix& m) + { gmm::write(o,m); return o; } + + template + inline void copy(const identity_matrix &, csr_matrix& M) + { M.init_with_identity(mat_nrows(M)); } + + template + inline void copy(const Matrix &A, csr_matrix& M) + { M.init_with(A); } + + /* ******************************************************************** */ + /* */ + /* Block matrix */ + /* */ + /* ******************************************************************** */ + + template class block_matrix { + protected : + std::vector blocks; + size_type nrowblocks_; + size_type ncolblocks_; + std::vector introw, intcol; + + public : + typedef typename linalg_traits::value_type value_type; + typedef typename linalg_traits::reference reference; + + size_type nrows(void) const { return introw[nrowblocks_-1].max; } + size_type ncols(void) const { return intcol[ncolblocks_-1].max; } + size_type nrowblocks(void) const { return nrowblocks_; } + size_type ncolblocks(void) const { return ncolblocks_; } + const sub_interval &subrowinterval(size_type i) const { return introw[i]; } + const sub_interval &subcolinterval(size_type i) const { return intcol[i]; } + const MAT &block(size_type i, size_type j) const + { return blocks[j*ncolblocks_+i]; } + MAT &block(size_type i, size_type j) + { return blocks[j*ncolblocks_+i]; } + void do_clear(void); + // to be done : read and write access to a component + value_type operator() (size_type i, size_type j) const { + size_type k, l; + for (k = 0; k < nrowblocks_; ++k) + if (i >= introw[k].min && i < introw[k].max) break; + for (l = 0; l < nrowblocks_; ++l) + if (j >= introw[l].min && j < introw[l].max) break; + return (block(k, l))(i - introw[k].min, j - introw[l].min); + } + reference operator() (size_type i, size_type j) { + size_type k, l; + for (k = 0; k < nrowblocks_; ++k) + if (i >= introw[k].min && i < introw[k].max) break; + for (l = 0; l < nrowblocks_; ++l) + if (j >= introw[l].min && j < introw[l].max) break; + return (block(k, l))(i - introw[k].min, j - introw[l].min); + } + + template void resize(const CONT &c1, const CONT &c2); + template block_matrix(const CONT &c1, const CONT &c2) + { resize(c1, c2); } + block_matrix(void) {} + + }; + + template struct linalg_traits > { + typedef block_matrix this_type; + typedef linalg_false is_reference; + typedef abstract_matrix linalg_type; + typedef this_type origin_type; + typedef typename linalg_traits::value_type value_type; + typedef typename linalg_traits::reference reference; + typedef typename linalg_traits::storage_type storage_type; + typedef abstract_null_type sub_row_type; // to be done ... + typedef abstract_null_type const_sub_row_type; // to be done ... + typedef abstract_null_type row_iterator; // to be done ... + typedef abstract_null_type const_row_iterator; // to be done ... + typedef abstract_null_type sub_col_type; // to be done ... + typedef abstract_null_type const_sub_col_type; // to be done ... + typedef abstract_null_type col_iterator; // to be done ... + typedef abstract_null_type const_col_iterator; // to be done ... + typedef abstract_null_type sub_orientation; // to be done ... + typedef linalg_true index_sorted; + static size_type nrows(const this_type &m) { return m.nrows(); } + static size_type ncols(const this_type &m) { return m.ncols(); } + static origin_type* origin(this_type &m) { return &m; } + static const origin_type* origin(const this_type &m) { return &m; } + static void do_clear(this_type &m) { m.do_clear(); } + // access to be done ... + static void resize(this_type &, size_type , size_type) + { GMM_ASSERT1(false, "Sorry, to be done"); } + static void reshape(this_type &, size_type , size_type) + { GMM_ASSERT1(false, "Sorry, to be done"); } + }; + + template void block_matrix::do_clear(void) { + for (size_type j = 0, l = 0; j < ncolblocks_; ++j) + for (size_type i = 0, k = 0; i < nrowblocks_; ++i) + clear(block(i,j)); + } + + template template + void block_matrix::resize(const CONT &c1, const CONT &c2) { + nrowblocks_ = c1.size(); ncolblocks_ = c2.size(); + blocks.resize(nrowblocks_ * ncolblocks_); + intcol.resize(ncolblocks_); + introw.resize(nrowblocks_); + for (size_type j = 0, l = 0; j < ncolblocks_; ++j) { + intcol[j] = sub_interval(l, c2[j]); l += c2[j]; + for (size_type i = 0, k = 0; i < nrowblocks_; ++i) { + if (j == 0) { introw[i] = sub_interval(k, c1[i]); k += c1[i]; } + block(i, j) = MAT(c1[i], c2[j]); + } + } + } + + template + void copy(const block_matrix &m1, M2 &m2) { + for (size_type j = 0; j < m1.ncolblocks(); ++j) + for (size_type i = 0; i < m1.nrowblocks(); ++i) + copy(m1.block(i,j), sub_matrix(m2, m1.subrowinterval(i), + m1.subcolinterval(j))); + } + + template + void copy(const block_matrix &m1, const M2 &m2) + { copy(m1, linalg_const_cast(m2)); } + + + template + void mult(const block_matrix &m, const V1 &v1, V2 &v2) { + clear(v2); + typename sub_vector_type::vector_type sv; + for (size_type i = 0; i < m.nrowblocks() ; ++i) + for (size_type j = 0; j < m.ncolblocks() ; ++j) { + sv = sub_vector(v2, m.subrowinterval(i)); + mult(m.block(i,j), + sub_vector(v1, m.subcolinterval(j)), sv, sv); + } + } + + template + void mult(const block_matrix &m, const V1 &v1, const V2 &v2, V3 &v3) { + typename sub_vector_type::vector_type sv; + for (size_type i = 0; i < m.nrowblocks() ; ++i) + for (size_type j = 0; j < m.ncolblocks() ; ++j) { + sv = sub_vector(v3, m.subrowinterval(i)); + if (j == 0) + mult(m.block(i,j), + sub_vector(v1, m.subcolinterval(j)), + sub_vector(v2, m.subrowinterval(i)), sv); + else + mult(m.block(i,j), + sub_vector(v1, m.subcolinterval(j)), sv, sv); + } + + } + + template + void mult(const block_matrix &m, const V1 &v1, const V2 &v2) + { mult(m, v1, linalg_const_cast(v2)); } + + template + void mult(const block_matrix &m, const V1 &v1, const V2 &v2, + const V3 &v3) + { mult_const(m, v1, v2, linalg_const_cast(v3)); } + +} + /* ******************************************************************** */ + /* */ + /* Distributed matrices */ + /* */ + /* ******************************************************************** */ + +#ifdef GMM_USES_MPI +# include + +namespace gmm { + + + + template inline MPI_Datatype mpi_type(T) + { GMM_ASSERT1(false, "Sorry unsupported type"); return MPI_FLOAT; } + inline MPI_Datatype mpi_type(double) { return MPI_DOUBLE; } + inline MPI_Datatype mpi_type(float) { return MPI_FLOAT; } + inline MPI_Datatype mpi_type(long double) { return MPI_LONG_DOUBLE; } +#ifndef LAM_MPI + inline MPI_Datatype mpi_type(std::complex) { return MPI_COMPLEX; } + inline MPI_Datatype mpi_type(std::complex) { return MPI_DOUBLE_COMPLEX; } +#endif + inline MPI_Datatype mpi_type(int) { return MPI_INT; } + inline MPI_Datatype mpi_type(unsigned int) { return MPI_UNSIGNED; } + inline MPI_Datatype mpi_type(long) { return MPI_LONG; } + inline MPI_Datatype mpi_type(unsigned long) { return MPI_UNSIGNED_LONG; } + + template struct mpi_distributed_matrix { + MAT M; + + mpi_distributed_matrix(size_type n, size_type m) : M(n, m) {} + mpi_distributed_matrix() {} + + const MAT &local_matrix(void) const { return M; } + MAT &local_matrix(void) { return M; } + }; + + template inline MAT &eff_matrix(MAT &m) { return m; } + template inline + const MAT &eff_matrix(const MAT &m) { return m; } + template inline + MAT &eff_matrix(mpi_distributed_matrix &m) { return m.M; } + template inline + const MAT &eff_matrix(const mpi_distributed_matrix &m) { return m.M; } + + + template + inline void copy(const mpi_distributed_matrix &m1, + mpi_distributed_matrix &m2) + { copy(eff_matrix(m1), eff_matrix(m2)); } + template + inline void copy(const mpi_distributed_matrix &m1, + const mpi_distributed_matrix &m2) + { copy(m1.M, m2.M); } + + template + inline void copy(const mpi_distributed_matrix &m1, MAT2 &m2) + { copy(m1.M, m2); } + template + inline void copy(const mpi_distributed_matrix &m1, const MAT2 &m2) + { copy(m1.M, m2); } + + + template inline + typename strongest_value_type3::value_type + vect_sp(const mpi_distributed_matrix &ps, const V1 &v1, + const V2 &v2) { + typedef typename strongest_value_type3::value_type T; + T res = vect_sp(ps.M, v1, v2), rest; + MPI_Allreduce(&res, &rest, 1, mpi_type(T()), MPI_SUM,MPI_COMM_WORLD); + return rest; + } + + template + inline void mult_add(const mpi_distributed_matrix &m, const V1 &v1, + V2 &v2) { + typedef typename linalg_traits::value_type T; + std::vector v3(vect_size(v2)), v4(vect_size(v2)); + static double tmult_tot = 0.0; + static double tmult_tot2 = 0.0; + double t_ref = MPI_Wtime(); + gmm::mult(m.M, v1, v3); + if (is_sparse(v2)) GMM_WARNING2("Using a plain temporary, here."); + double t_ref2 = MPI_Wtime(); + MPI_Allreduce(&(v3[0]), &(v4[0]),gmm::vect_size(v2), mpi_type(T()), + MPI_SUM,MPI_COMM_WORLD); + tmult_tot2 = MPI_Wtime()-t_ref2; + cout << "reduce mult mpi = " << tmult_tot2 << endl; + gmm::add(v4, v2); + tmult_tot = MPI_Wtime()-t_ref; + cout << "tmult mpi = " << tmult_tot << endl; + } + + template + void mult_add(const mpi_distributed_matrix &m, const V1 &v1, + const V2 &v2_) + { mult_add(m, v1, const_cast(v2_)); } + + template + inline void mult(const mpi_distributed_matrix &m, const V1 &v1, + const V2 &v2_) + { V2 &v2 = const_cast(v2_); clear(v2); mult_add(m, v1, v2); } + + template + inline void mult(const mpi_distributed_matrix &m, const V1 &v1, + V2 &v2) + { clear(v2); mult_add(m, v1, v2); } + + template + inline void mult(const mpi_distributed_matrix &m, const V1 &v1, + const V2 &v2, const V3 &v3_) + { V3 &v3 = const_cast(v3_); gmm::copy(v2, v3); mult_add(m, v1, v3); } + + template + inline void mult(const mpi_distributed_matrix &m, const V1 &v1, + const V2 &v2, V3 &v3) + { gmm::copy(v2, v3); mult_add(m, v1, v3); } + + + template inline + size_type mat_nrows(const mpi_distributed_matrix &M) + { return mat_nrows(M.M); } + template inline + size_type mat_ncols(const mpi_distributed_matrix &M) + { return mat_nrows(M.M); } + template inline + void resize(mpi_distributed_matrix &M, size_type m, size_type n) + { resize(M.M, m, n); } + template inline void clear(mpi_distributed_matrix &M) + { clear(M.M); } + + + // For compute reduced system + template inline + void mult(const MAT1 &M1, const mpi_distributed_matrix &M2, + mpi_distributed_matrix &M3) + { mult(M1, M2.M, M3.M); } + template inline + void mult(const mpi_distributed_matrix &M2, + const MAT1 &M1, mpi_distributed_matrix &M3) + { mult(M2.M, M1, M3.M); } + template inline + void mult(const MAT1 &M1, const mpi_distributed_matrix &M2, + MAT3 &M3) + { mult(M1, M2.M, M3); } + template inline + void mult(const MAT1 &M1, const mpi_distributed_matrix &M2, + const MAT3 &M3) + { mult(M1, M2.M, M3); } + + template + struct sub_matrix_type *, SUBI1, SUBI2> + { typedef abstract_null_type matrix_type; }; + + template + struct sub_matrix_type *, SUBI1, SUBI2> + { typedef abstract_null_type matrix_type; }; + + template inline + typename select_return + ::matrix_type, typename sub_matrix_type::matrix_type, + M *>::return_type + sub_matrix(mpi_distributed_matrix &m, const SUBI1 &si1, const SUBI2 &si2) + { return sub_matrix(m.M, si1, si2); } + + template inline + typename select_return + ::matrix_type, typename sub_matrix_type::matrix_type, + const MAT *>::return_type + sub_matrix(const mpi_distributed_matrix &m, const SUBI1 &si1, + const SUBI2 &si2) + { return sub_matrix(m.M, si1, si2); } + + template inline + typename select_return + ::matrix_type, typename sub_matrix_type::matrix_type, + M *>::return_type + sub_matrix(mpi_distributed_matrix &m, const SUBI1 &si1) + { return sub_matrix(m.M, si1, si1); } + + template inline + typename select_return + ::matrix_type, typename sub_matrix_type::matrix_type, + const M *>::return_type + sub_matrix(const mpi_distributed_matrix &m, const SUBI1 &si1) + { return sub_matrix(m.M, si1, si1); } + + + template struct transposed_return *> + { typedef abstract_null_type return_type; }; + template struct transposed_return *> + { typedef abstract_null_type return_type; }; + + template inline typename transposed_return::return_type + transposed(const mpi_distributed_matrix &l) + { return transposed(l.M); } + + template inline typename transposed_return::return_type + transposed(mpi_distributed_matrix &l) + { return transposed(l.M); } + + + template + struct linalg_traits > { + typedef mpi_distributed_matrix this_type; + typedef MAT origin_type; + typedef linalg_false is_reference; + typedef abstract_matrix linalg_type; + typedef typename linalg_traits::value_type value_type; + typedef typename linalg_traits::reference reference; + typedef typename linalg_traits::storage_type storage_type; + typedef abstract_null_type sub_row_type; + typedef abstract_null_type const_sub_row_type; + typedef abstract_null_type row_iterator; + typedef abstract_null_type const_row_iterator; + typedef abstract_null_type sub_col_type; + typedef abstract_null_type const_sub_col_type; + typedef abstract_null_type col_iterator; + typedef abstract_null_type const_col_iterator; + typedef abstract_null_type sub_orientation; + typedef abstract_null_type index_sorted; + static size_type nrows(const this_type &m) { return nrows(m.M); } + static size_type ncols(const this_type &m) { return ncols(m.M); } + static void do_clear(this_type &m) { clear(m.M); } + }; + +} + + +#endif // GMM_USES_MPI + +namespace std { + template + void swap(gmm::row_matrix &m1, gmm::row_matrix &m2) + { m1.swap(m2); } + template + void swap(gmm::col_matrix &m1, gmm::col_matrix &m2) + { m1.swap(m2); } + template + void swap(gmm::dense_matrix &m1, gmm::dense_matrix &m2) + { m1.swap(m2); } + template void + swap(gmm::csc_matrix &m1, gmm::csc_matrix &m2) + { m1.swap(m2); } + template void + swap(gmm::csr_matrix &m1, gmm::csr_matrix &m2) + { m1.swap(m2); } +} + + + + +#endif /* GMM_MATRIX_H__ */ diff --git a/src/linear_algebra/gmm/gmm_modified_gram_schmidt.h b/src/linear_algebra/gmm/gmm_modified_gram_schmidt.h new file mode 100644 index 00000000..fa459176 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_modified_gram_schmidt.h @@ -0,0 +1,127 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +//=========================================================================== +// +// Copyright (c) 1998-2020, University of Notre Dame. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the University of Notre Dame nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE TRUSTEES OF INDIANA UNIVERSITY AND +// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES +// OF INDIANA UNIVERSITY AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//=========================================================================== + +/**@file gmm_modified_gram_schmidt.h + @author Andrew Lumsdaine , Lie-Quan Lee + @date October 13, 2002. + @brief Modified Gram-Schmidt orthogonalization +*/ + +#ifndef GMM_MODIFIED_GRAM_SCHMIDT_H +#define GMM_MODIFIED_GRAM_SCHMIDT_H + +#include "gmm_kernel.h" + +namespace gmm { + + template + class modified_gram_schmidt { + protected: + typedef dense_matrix MAT; + MAT M; + + public: + + modified_gram_schmidt(int restart, size_t s) : M(s, restart+1) {} + + typename linalg_traits::const_sub_col_type + operator[](size_t i) const { return mat_const_col(M, i); } + + typename linalg_traits::sub_col_type + operator[](size_t i) { return mat_col(M, i); } + + inline size_type nrows(void) const { return M.nrows(); } + inline size_type ncols(void) const { return M.ncols(); } + MAT &mat(void) { return M; } + const MAT &mat(void) const { return M; } + + }; + + template inline + void orthogonalize(modified_gram_schmidt& V, const VecHi& Hi_, size_t i) { + VecHi& Hi = const_cast(Hi_); + + for (size_t k = 0; k <= i; k++) { + Hi[k] = gmm::vect_hp(V[i+1], V[k]); + gmm::add(gmm::scaled(V[k], -Hi[k]), V[i+1]); + } + } + + template + void orthogonalize_with_refinment(modified_gram_schmidt& V, + const VecHi& Hi_, size_t i) { + VecHi& Hi = const_cast(Hi_); + orthogonalize(V, Hi_, i); + + sub_interval SUBI(0, V.nrows()), SUBJ(0, i+1); + std::vector corr(i+1); + gmm::mult(conjugated(sub_matrix(V.mat(), SUBI, SUBJ)), + V[i+1], corr); + gmm::mult(sub_matrix(V.mat(), SUBI, SUBJ), + scaled(corr, T(-1)), V[i+1],V[i+1]); + gmm::add(corr, sub_vector(Hi, SUBJ)); + } + + template + void combine(modified_gram_schmidt& V, const VecS& s, VecX& x, size_t i) + { for (size_t j = 0; j < i; ++j) gmm::add(gmm::scaled(V[j], s[j]), x); } +} + +#endif diff --git a/src/linear_algebra/gmm/gmm_opt.h b/src/linear_algebra/gmm/gmm_opt.h new file mode 100644 index 00000000..7be45c44 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_opt.h @@ -0,0 +1,132 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2003-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_opt.h + @author Yves Renard + @date July 9, 2003. + @brief Optimization for some small cases (inversion of 2x2 matrices etc.) +*/ +#ifndef GMM_OPT_H__ +#define GMM_OPT_H__ + +#include + +namespace gmm { + + /* ********************************************************************* */ + /* Optimized determinant and inverse for small matrices (2x2 and 3x3) */ + /* with dense_matrix. */ + /* ********************************************************************* */ + + template T lu_det(const dense_matrix &A) { + size_type n(mat_nrows(A)); + if (n) { + const T *p = &(A(0,0)); + switch (n) { + case 1 : return (*p); + case 2 : return (*p) * (*(p+3)) - (*(p+1)) * (*(p+2)); +// Not stable for nearly singular matrices +// case 3 : return (*p) * ((*(p+4)) * (*(p+8)) - (*(p+5)) * (*(p+7))) +// - (*(p+1)) * ((*(p+3)) * (*(p+8)) - (*(p+5)) * (*(p+6))) +// + (*(p+2)) * ((*(p+3)) * (*(p+7)) - (*(p+4)) * (*(p+6))); + default : + { + dense_matrix B(mat_nrows(A), mat_ncols(A)); + lapack_ipvt ipvt(mat_nrows(A)); + gmm::copy(A, B); + lu_factor(B, ipvt); + return lu_det(B, ipvt); + } + } + } + return T(1); + } + + + template T lu_inverse(const dense_matrix &A_, + bool doassert = true) { + dense_matrix& A = const_cast &>(A_); + size_type N = mat_nrows(A); + T det(1); + if (N) { + T *p = &(A(0,0)); + switch (N) { + case 1 : { + det = *p; + if (doassert) GMM_ASSERT1(det!=T(0), "non invertible matrix"); + if (det == T(0)) break; + *p = T(1) / det; + } break; + case 2 : { + det = (*p) * (*(p+3)) - (*(p+1)) * (*(p+2)); + if (doassert) GMM_ASSERT1(det!=T(0), "non invertible matrix"); + if (det == T(0)) break; + std::swap(*p, *(p+3)); + *p++ /= det; *p++ /= -det; *p++ /= -det; *p++ /= det; + } break; + default : { + if (N == 3) { // not stable for nearly singular matrices + T a, b, c, d, e, f, g, h, i; + a = (*(p+4)) * (*(p+8)) - (*(p+5)) * (*(p+7)); + b = - (*(p+1)) * (*(p+8)) + (*(p+2)) * (*(p+7)); + c = (*(p+1)) * (*(p+5)) - (*(p+2)) * (*(p+4)); + d = - (*(p+3)) * (*(p+8)) + (*(p+5)) * (*(p+6)); + e = (*(p+0)) * (*(p+8)) - (*(p+2)) * (*(p+6)); + f = - (*(p+0)) * (*(p+5)) + (*(p+2)) * (*(p+3)); + g = (*(p+3)) * (*(p+7)) - (*(p+4)) * (*(p+6)); + h = - (*(p+0)) * (*(p+7)) + (*(p+1)) * (*(p+6)); + i = (*(p+0)) * (*(p+4)) - (*(p+1)) * (*(p+3)); + det = (*p) * a + (*(p+1)) * d + (*(p+2)) * g; + if (std::abs(det) > 1e-5) { + *p++ = a / det; *p++ = b / det; *p++ = c / det; + *p++ = d / det; *p++ = e / det; *p++ = f / det; + *p++ = g / det; *p++ = h / det; *p++ = i / det; + break; + } + } + dense_matrix B(mat_nrows(A), mat_ncols(A)); + lapack_ipvt ipvt(mat_nrows(A)); + gmm::copy(A, B); + size_type info = lu_factor(B, ipvt); + GMM_ASSERT1(!info, "non invertible matrix"); + lu_inverse(B, ipvt, A); + det = lu_det(B, ipvt); + break; + } + } + } + return det; + } + + +} + +#endif // GMM_OPT_H__ diff --git a/src/linear_algebra/gmm/gmm_precond.h b/src/linear_algebra/gmm/gmm_precond.h new file mode 100644 index 00000000..17405a2c --- /dev/null +++ b/src/linear_algebra/gmm/gmm_precond.h @@ -0,0 +1,65 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2004-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ +#ifndef GMM_PRECOND_H +#define GMM_PRECOND_H + +#include "gmm_kernel.h" + +/** @file gmm_precond.h + @author Yves Renard + @date March 29, 2004. + @brief gmm preconditioners. + */ + +/* Preconditioner concept : */ +/* */ +/* A the matrix, P the preconditioner PA well conditioned. */ +/* PRECOND preconditioner type. */ +/* mult(P, v, w) : w <- P v */ +/* transposed_mult(P, v, w) : w <- transposed(P) v */ +/* left_mult(P, v, w) : see qmr solver */ +/* right_mult(P, v, w) : see qmr solver */ +/* transposed_left_mult(P, v, w) : see qmr solver */ +/* transposed_right_mult(P, v, w) : see qmr solver */ +/* */ +/* PRECOND P() : empty preconditioner. */ +/* PRECOND P(A, ...) : preconditioner for the matrix A, with optional */ +/* parameters */ +/* PRECOND(...) : empty precondtioner with parameters set. */ +/* P.build_with(A) : build a precondtioner for A. */ +/* */ +/* *********************************************************************** */ + + + + +#endif + diff --git a/src/linear_algebra/gmm/gmm_precond_diagonal.h b/src/linear_algebra/gmm/gmm_precond_diagonal.h new file mode 100644 index 00000000..c8ccebc6 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_precond_diagonal.h @@ -0,0 +1,132 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2003-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_precond_diagonal.h + @author Yves Renard + @date June 5, 2003. + @brief Diagonal matrix preconditoner. +*/ + +#ifndef GMM_PRECOND_DIAGONAL_H +#define GMM_PRECOND_DIAGONAL_H + +#include "gmm_precond.h" + +namespace gmm { + + /** Diagonal preconditioner. */ + template struct diagonal_precond { + typedef typename linalg_traits::value_type value_type; + typedef typename number_traits::magnitude_type magnitude_type; + + std::vector diag; + + void build_with(const Matrix &M) { + diag.resize(mat_nrows(M)); + for (size_type i = 0; i < mat_nrows(M); ++i) { + magnitude_type x = gmm::abs(M(i, i)); + if (x == magnitude_type(0)) { + x = magnitude_type(1); + GMM_WARNING2("The matrix has a zero on its diagonal"); + } + diag[i] = magnitude_type(1) / x; + } + } + size_type memsize() const { return sizeof(*this) + diag.size() * sizeof(value_type); } + diagonal_precond(const Matrix &M) { build_with(M); } + diagonal_precond(void) {} + }; + + template inline + void mult_diag_p(const diagonal_precond& P, V2 &v2, abstract_sparse){ + typename linalg_traits::iterator it = vect_begin(v2), + ite = vect_end(v2); + for (; it != ite; ++it) *it *= P.diag[it.index()]; + } + + template inline + void mult_diag_p(const diagonal_precond& P,V2 &v2, abstract_skyline) + { mult_diag_p(P, v2, abstract_sparse()); } + + template inline + void mult_diag_p(const diagonal_precond& P, V2 &v2, abstract_dense){ + for (size_type i = 0; i < P.diag.size(); ++i) v2[i] *= P.diag[i]; + } + + template inline + void mult(const diagonal_precond& P, const V1 &v1, V2 &v2) { + GMM_ASSERT2(P.diag.size() == vect_size(v2),"dimensions mismatch"); + copy(v1, v2); + mult_diag_p(P, v2, typename linalg_traits::storage_type()); + } + + template inline + void transposed_mult(const diagonal_precond& P,const V1 &v1,V2 &v2) { + mult(P, v1, v2); + } + + // # define DIAG_LEFT_MULT_SQRT + + template inline + void left_mult(const diagonal_precond& P, const V1 &v1, V2 &v2) { + GMM_ASSERT2(P.diag.size() == vect_size(v2), "dimensions mismatch"); + copy(v1, v2); +# ifdef DIAG_LEFT_MULT_SQRT + for (size_type i= 0; i < P.diag.size(); ++i) v2[i] *= gmm::sqrt(P.diag[i]); +# else + for (size_type i= 0; i < P.diag.size(); ++i) v2[i] *= P.diag[i]; +# endif + } + + template inline + void transposed_left_mult(const diagonal_precond& P, + const V1 &v1, V2 &v2) + { left_mult(P, v1, v2); } + + template inline + void right_mult(const diagonal_precond& P, const V1 &v1, V2 &v2) { + // typedef typename linalg_traits::value_type T; + GMM_ASSERT2(P.diag.size() == vect_size(v2), "dimensions mismatch"); + copy(v1, v2); +# ifdef DIAG_LEFT_MULT_SQRT + for (size_type i= 0; i < P.diag.size(); ++i) v2[i] *= gmm::sqrt(P.diag[i]); +# endif + } + + template inline + void transposed_right_mult(const diagonal_precond& P, + const V1 &v1, V2 &v2) + { right_mult(P, v1, v2); } + +} + +#endif + diff --git a/src/linear_algebra/gmm/gmm_precond_ildlt.h b/src/linear_algebra/gmm/gmm_precond_ildlt.h new file mode 100644 index 00000000..226213ea --- /dev/null +++ b/src/linear_algebra/gmm/gmm_precond_ildlt.h @@ -0,0 +1,241 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2003-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +// This file is a modified version of cholesky.h from ITL. +// See http://osl.iu.edu/research/itl/ +// Following the corresponding Copyright notice. +//=========================================================================== +// +// Copyright (c) 1998-2020, University of Notre Dame. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the University of Notre Dame nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE TRUSTEES OF INDIANA UNIVERSITY AND +// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES +// OF INDIANA UNIVERSITY AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//=========================================================================== + +#ifndef GMM_PRECOND_ILDLT_H +#define GMM_PRECOND_ILDLT_H + +/**@file gmm_precond_ildlt.h + @author Andrew Lumsdaine + @author Lie-Quan Lee + @author Yves Renard + @date June 5, 2003. + @brief Incomplete Level 0 ILDLT Preconditioner. +*/ + +#include "gmm_precond.h" + +namespace gmm { + + /** Incomplete Level 0 LDLT Preconditioner. + + For use with symmetric real or hermitian complex sparse matrices. + + Notes: The idea under a concrete Preconditioner such as Incomplete + Cholesky is to create a Preconditioner object to use in iterative + methods. + + + Y. Renard : Transformed in LDLT for stability reason. + + U=LT is stored in csr format. D is stored on the diagonal of U. + */ + template + class ildlt_precond { + + public : + typedef typename linalg_traits::value_type value_type; + typedef typename number_traits::magnitude_type magnitude_type; + typedef csr_matrix_ref tm_type; + + tm_type U; + + protected : + std::vector Tri_val; + std::vector Tri_ind, Tri_ptr; + + template void do_ildlt(const M& A, row_major); + void do_ildlt(const Matrix& A, col_major); + + public: + + size_type nrows(void) const { return mat_nrows(U); } + size_type ncols(void) const { return mat_ncols(U); } + value_type &D(size_type i) { return Tri_val[Tri_ptr[i]]; } + const value_type &D(size_type i) const { return Tri_val[Tri_ptr[i]]; } + ildlt_precond(void) {} + void build_with(const Matrix& A) { + Tri_ptr.resize(mat_nrows(A)+1); + do_ildlt(A, typename principal_orientation_type::sub_orientation>::potype()); + } + ildlt_precond(const Matrix& A) { build_with(A); } + size_type memsize() const { + return sizeof(*this) + + Tri_val.size() * sizeof(value_type) + + (Tri_ind.size()+Tri_ptr.size()) * sizeof(size_type); + } + }; + + template template + void ildlt_precond::do_ildlt(const M& A, row_major) { + typedef typename linalg_traits::storage_type store_type; + typedef value_type T; + typedef typename number_traits::magnitude_type R; + + size_type Tri_loc = 0, n = mat_nrows(A), d, g, h, i, j, k; + if (n == 0) return; + T z, zz; + Tri_ptr[0] = 0; + R prec = default_tol(R()); + R max_pivot = gmm::abs(A(0,0)) * prec; + + for (int count = 0; count < 2; ++count) { + if (count) { Tri_val.resize(Tri_loc); Tri_ind.resize(Tri_loc); } + for (Tri_loc = 0, i = 0; i < n; ++i) { + typedef typename linalg_traits::const_sub_row_type row_type; + row_type row = mat_const_row(A, i); + typename linalg_traits::t>::const_iterator + it = vect_const_begin(row), ite = vect_const_end(row); + + if (count) { Tri_val[Tri_loc] = T(0); Tri_ind[Tri_loc] = i; } + ++Tri_loc; // diagonal element + + for (k = 0; it != ite; ++it, ++k) { + j = index_of_it(it, k, store_type()); + if (i == j) { + if (count) Tri_val[Tri_loc-1] = *it; + } + else if (j > i) { + if (count) { Tri_val[Tri_loc] = *it; Tri_ind[Tri_loc]=j; } + ++Tri_loc; + } + } + Tri_ptr[i+1] = Tri_loc; + } + } + + if (A(0,0) == T(0)) { + Tri_val[Tri_ptr[0]] = T(1); + GMM_WARNING2("pivot 0 is too small"); + } + + for (k = 0; k < n; k++) { + d = Tri_ptr[k]; + z = T(gmm::real(Tri_val[d])); Tri_val[d] = z; + if (gmm::abs(z) <= max_pivot) { + Tri_val[d] = z = T(1); + GMM_WARNING2("pivot " << k << " is too small [" << gmm::abs(z) << "]"); + } + max_pivot = std::max(max_pivot, std::min(gmm::abs(z) * prec, R(1))); + + for (i = d + 1; i < Tri_ptr[k+1]; ++i) Tri_val[i] /= z; + for (i = d + 1; i < Tri_ptr[k+1]; ++i) { + zz = gmm::conj(Tri_val[i] * z); + h = Tri_ind[i]; + g = i; + + for (j = Tri_ptr[h] ; j < Tri_ptr[h+1]; ++j) + for ( ; g < Tri_ptr[k+1] && Tri_ind[g] <= Tri_ind[j]; ++g) + if (Tri_ind[g] == Tri_ind[j]) + Tri_val[j] -= zz * Tri_val[g]; + } + } + U = tm_type(&(Tri_val[0]), &(Tri_ind[0]), &(Tri_ptr[0]), + n, mat_ncols(A)); + } + + template + void ildlt_precond::do_ildlt(const Matrix& A, col_major) + { do_ildlt(gmm::conjugated(A), row_major()); } + + template inline + void mult(const ildlt_precond& P, const V1 &v1, V2 &v2) { + gmm::copy(v1, v2); + gmm::lower_tri_solve(gmm::conjugated(P.U), v2, true); + for (size_type i = 0; i < mat_nrows(P.U); ++i) v2[i] /= P.D(i); + gmm::upper_tri_solve(P.U, v2, true); + } + + template inline + void transposed_mult(const ildlt_precond& P,const V1 &v1,V2 &v2) + { mult(P, v1, v2); } + + template inline + void left_mult(const ildlt_precond& P, const V1 &v1, V2 &v2) { + copy(v1, v2); + gmm::lower_tri_solve(gmm::conjugated(P.U), v2, true); + for (size_type i = 0; i < mat_nrows(P.U); ++i) v2[i] /= P.D(i); + } + + template inline + void right_mult(const ildlt_precond& P, const V1 &v1, V2 &v2) + { copy(v1, v2); gmm::upper_tri_solve(P.U, v2, true); } + + template inline + void transposed_left_mult(const ildlt_precond& P, const V1 &v1, + V2 &v2) { + copy(v1, v2); + gmm::upper_tri_solve(P.U, v2, true); + for (size_type i = 0; i < mat_nrows(P.U); ++i) v2[i] /= P.D(i); + } + + template inline + void transposed_right_mult(const ildlt_precond& P, const V1 &v1, + V2 &v2) + { copy(v1, v2); gmm::lower_tri_solve(gmm::conjugated(P.U), v2, true); } + + +} + +#endif + diff --git a/src/linear_algebra/gmm/gmm_precond_ildltt.h b/src/linear_algebra/gmm/gmm_precond_ildltt.h new file mode 100644 index 00000000..54713df9 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_precond_ildltt.h @@ -0,0 +1,174 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2003-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_precond_ildltt.h + @author Yves Renard + @date June 30, 2003. + @brief incomplete LDL^t (cholesky) preconditioner with fill-in and threshold. +*/ + +#ifndef GMM_PRECOND_ILDLTT_H +#define GMM_PRECOND_ILDLTT_H + +// Store U = LT and D in indiag. On each line, the fill-in is the number +// of non-zero elements on the line of the original matrix plus K, except if +// the matrix is dense. In this case the fill-in is K on each line. + +#include "gmm_precond_ilut.h" + +namespace gmm { + /** incomplete LDL^t (cholesky) preconditioner with fill-in and + threshold. */ + template + class ildltt_precond { + public : + typedef typename linalg_traits::value_type value_type; + typedef typename number_traits::magnitude_type magnitude_type; + + typedef rsvector svector; + + row_matrix U; + std::vector indiag; + + protected: + size_type K; + double eps; + + template void do_ildltt(const M&, row_major); + void do_ildltt(const Matrix&, col_major); + + public: + void build_with(const Matrix& A, int k_ = -1, double eps_ = double(-1)) { + if (k_ >= 0) K = k_; + if (eps_ >= double(0)) eps = eps_; + gmm::resize(U, mat_nrows(A), mat_ncols(A)); + indiag.resize(std::min(mat_nrows(A), mat_ncols(A))); + do_ildltt(A, typename principal_orientation_type::sub_orientation>::potype()); + } + ildltt_precond(const Matrix& A, int k_, double eps_) + : U(mat_nrows(A),mat_ncols(A)), K(k_), eps(eps_) { build_with(A); } + ildltt_precond(void) { K=10; eps = 1E-7; } + ildltt_precond(size_type k_, double eps_) : K(k_), eps(eps_) {} + size_type memsize() const { + return sizeof(*this) + nnz(U)*sizeof(value_type) + indiag.size() * sizeof(magnitude_type); + } + }; + + template template + void ildltt_precond::do_ildltt(const M& A,row_major) { + typedef value_type T; + typedef typename number_traits::magnitude_type R; + + size_type n = mat_nrows(A); + if (n == 0) return; + svector w(n); + T tmp; + R prec = default_tol(R()), max_pivot = gmm::abs(A(0,0)) * prec; + + gmm::clear(U); + for (size_type i = 0; i < n; ++i) { + gmm::copy(mat_const_row(A, i), w); + double norm_row = gmm::vect_norm2(w); + + for (size_type krow = 0, k; krow < w.nb_stored(); ++krow) { + typename svector::iterator wk = w.begin() + krow; + if ((k = wk->c) >= i) break; + if (gmm::is_complex(wk->e)) { + tmp = gmm::conj(U(k, i))/indiag[k]; // not completely satisfactory .. + gmm::add(scaled(mat_row(U, k), -tmp), w); + } + else { + tmp = wk->e; + if (gmm::abs(tmp) < eps * norm_row) { w.sup(k); --krow; } + else { wk->e += tmp; gmm::add(scaled(mat_row(U, k), -tmp), w); } + } + } + tmp = w[i]; + + if (gmm::abs(gmm::real(tmp)) <= max_pivot) + { GMM_WARNING2("pivot " << i << " is too small"); tmp = T(1); } + + max_pivot = std::max(max_pivot, std::min(gmm::abs(tmp) * prec, R(1))); + indiag[i] = R(1) / gmm::real(tmp); + gmm::clean(w, eps * norm_row); + gmm::scale(w, T(indiag[i])); + std::sort(w.begin(), w.end(), elt_rsvector_value_less_()); + typename svector::const_iterator wit = w.begin(), wite = w.end(); + for (size_type nnu = 0; wit != wite; ++wit) // copy to be optimized ... + if (wit->c > i) { if (nnu < K) { U(i, wit->c) = wit->e; ++nnu; } } + } + } + + template + void ildltt_precond::do_ildltt(const Matrix& A, col_major) + { do_ildltt(gmm::conjugated(A), row_major()); } + + template inline + void mult(const ildltt_precond& P, const V1 &v1, V2 &v2) { + gmm::copy(v1, v2); + gmm::lower_tri_solve(gmm::conjugated(P.U), v2, true); + for (size_type i = 0; i < P.indiag.size(); ++i) v2[i] *= P.indiag[i]; + gmm::upper_tri_solve(P.U, v2, true); + } + + template inline + void transposed_mult(const ildltt_precond& P,const V1 &v1, V2 &v2) + { mult(P, v1, v2); } + + template inline + void left_mult(const ildltt_precond& P, const V1 &v1, V2 &v2) { + copy(v1, v2); + gmm::lower_tri_solve(gmm::conjugated(P.U), v2, true); + for (size_type i = 0; i < P.indiag.size(); ++i) v2[i] *= P.indiag[i]; + } + + template inline + void right_mult(const ildltt_precond& P, const V1 &v1, V2 &v2) + { copy(v1, v2); gmm::upper_tri_solve(P.U, v2, true); } + + template inline + void transposed_left_mult(const ildltt_precond& P, const V1 &v1, + V2 &v2) { + copy(v1, v2); + gmm::upper_tri_solve(P.U, v2, true); + for (size_type i = 0; i < P.indiag.size(); ++i) v2[i] *= P.indiag[i]; + } + + template inline + void transposed_right_mult(const ildltt_precond& P, const V1 &v1, + V2 &v2) + { copy(v1, v2); gmm::lower_tri_solve(gmm::conjugated(P.U), v2, true); } + +} + +#endif + diff --git a/src/linear_algebra/gmm/gmm_precond_ilu.h b/src/linear_algebra/gmm/gmm_precond_ilu.h new file mode 100644 index 00000000..25d322d0 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_precond_ilu.h @@ -0,0 +1,280 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +// This file is a modified version of ilu.h from ITL. +// See http://osl.iu.edu/research/itl/ +// Following the corresponding Copyright notice. +//=========================================================================== +// +// Copyright (c) 1998-2020, University of Notre Dame. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the University of Notre Dame nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE TRUSTEES OF INDIANA UNIVERSITY AND +// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES +// OF INDIANA UNIVERSITY AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//=========================================================================== + +/**@file gmm_precond_ilu.h + @author Andrew Lumsdaine + @author Lie-Quan Lee + @author Yves Renard + @date June 5, 2003. + @brief Incomplete LU without fill-in Preconditioner. +*/ + +#ifndef GMM_PRECOND_ILU_H +#define GMM_PRECOND_ILU_H + +// +// Notes: The idea under a concrete Preconditioner such +// as Incomplete LU is to create a Preconditioner +// object to use in iterative methods. +// + +#include "gmm_precond.h" + +namespace gmm { + /** Incomplete LU without fill-in Preconditioner. */ + template + class ilu_precond { + + public : + typedef typename linalg_traits::value_type value_type; + typedef csr_matrix_ref tm_type; + + tm_type U, L; + bool invert; + protected : + std::vector L_val, U_val; + std::vector L_ind, U_ind, L_ptr, U_ptr; + + template void do_ilu(const M& A, row_major); + void do_ilu(const Matrix& A, col_major); + + public: + + size_type nrows(void) const { return mat_nrows(L); } + size_type ncols(void) const { return mat_ncols(U); } + + void build_with(const Matrix& A) { + invert = false; + L_ptr.resize(mat_nrows(A)+1); + U_ptr.resize(mat_nrows(A)+1); + do_ilu(A, typename principal_orientation_type::sub_orientation>::potype()); + } + ilu_precond(const Matrix& A) { build_with(A); } + ilu_precond(void) {} + size_type memsize() const { + return sizeof(*this) + + (L_val.size()+U_val.size()) * sizeof(value_type) + + (L_ind.size()+L_ptr.size()) * sizeof(size_type) + + (U_ind.size()+U_ptr.size()) * sizeof(size_type); + } + }; + + template template + void ilu_precond::do_ilu(const M& A, row_major) { + typedef typename linalg_traits::storage_type store_type; + typedef value_type T; + typedef typename number_traits::magnitude_type R; + + size_type L_loc = 0, U_loc = 0, n = mat_nrows(A), i, j, k; + if (n == 0) return; + L_ptr[0] = 0; U_ptr[0] = 0; + R prec = default_tol(R()); + R max_pivot = gmm::abs(A(0,0)) * prec; + + + for (int count = 0; count < 2; ++count) { + if (count) { + L_val.resize(L_loc); L_ind.resize(L_loc); + U_val.resize(U_loc); U_ind.resize(U_loc); + } + L_loc = U_loc = 0; + for (i = 0; i < n; ++i) { + typedef typename linalg_traits::const_sub_row_type row_type; + row_type row = mat_const_row(A, i); + typename linalg_traits::t>::const_iterator + it = vect_const_begin(row), ite = vect_const_end(row); + + if (count) { U_val[U_loc] = T(0); U_ind[U_loc] = i; } + ++U_loc; // diagonal element + + for (k = 0; it != ite && k < 1000; ++it, ++k) { + // if a plain row is present, retains only the 1000 firsts + // nonzero elements. ---> a sort should be done. + j = index_of_it(it, k, store_type()); + if (j < i) { + if (count) { L_val[L_loc] = *it; L_ind[L_loc] = j; } + L_loc++; + } + else if (i == j) { + if (count) U_val[U_loc-1] = *it; + } + else { + if (count) { U_val[U_loc] = *it; U_ind[U_loc] = j; } + U_loc++; + } + } + L_ptr[i+1] = L_loc; U_ptr[i+1] = U_loc; + } + } + + if (A(0,0) == T(0)) { + U_val[U_ptr[0]] = T(1); + GMM_WARNING2("pivot 0 is too small"); + } + + size_type qn, pn, rn; + for (i = 1; i < n; i++) { + + pn = U_ptr[i]; + if (gmm::abs(U_val[pn]) <= max_pivot) { + U_val[pn] = T(1); + GMM_WARNING2("pivot " << i << " is too small"); + } + max_pivot = std::max(max_pivot, + std::min(gmm::abs(U_val[pn]) * prec, R(1))); + + for (j = L_ptr[i]; j < L_ptr[i+1]; j++) { + pn = U_ptr[L_ind[j]]; + + T multiplier = (L_val[j] /= U_val[pn]); + + qn = j + 1; + rn = U_ptr[i]; + + for (pn++; pn < U_ptr[L_ind[j]+1] && U_ind[pn] < i; pn++) { + while (qn < L_ptr[i+1] && L_ind[qn] < U_ind[pn]) + qn++; + if (qn < L_ptr[i+1] && U_ind[pn] == L_ind[qn]) + L_val[qn] -= multiplier * U_val[pn]; + } + for (; pn < U_ptr[L_ind[j]+1]; pn++) { + while (rn < U_ptr[i+1] && U_ind[rn] < U_ind[pn]) + rn++; + if (rn < U_ptr[i+1] && U_ind[pn] == U_ind[rn]) + U_val[rn] -= multiplier * U_val[pn]; + } + } + } + + L = tm_type(&(L_val[0]), &(L_ind[0]), &(L_ptr[0]), n, mat_ncols(A)); + U = tm_type(&(U_val[0]), &(U_ind[0]), &(U_ptr[0]), n, mat_ncols(A)); + } + + template + void ilu_precond::do_ilu(const Matrix& A, col_major) { + do_ilu(gmm::transposed(A), row_major()); + invert = true; + } + + template inline + void mult(const ilu_precond& P, const V1 &v1, V2 &v2) { + gmm::copy(v1, v2); + if (P.invert) { + gmm::lower_tri_solve(gmm::transposed(P.U), v2, false); + gmm::upper_tri_solve(gmm::transposed(P.L), v2, true); + } + else { + gmm::lower_tri_solve(P.L, v2, true); + gmm::upper_tri_solve(P.U, v2, false); + } + } + + template inline + void transposed_mult(const ilu_precond& P,const V1 &v1,V2 &v2) { + gmm::copy(v1, v2); + if (P.invert) { + gmm::lower_tri_solve(P.L, v2, true); + gmm::upper_tri_solve(P.U, v2, false); + } + else { + gmm::lower_tri_solve(gmm::transposed(P.U), v2, false); + gmm::upper_tri_solve(gmm::transposed(P.L), v2, true); + } + } + + template inline + void left_mult(const ilu_precond& P, const V1 &v1, V2 &v2) { + copy(v1, v2); + if (P.invert) gmm::lower_tri_solve(gmm::transposed(P.U), v2, false); + else gmm::lower_tri_solve(P.L, v2, true); + } + + template inline + void right_mult(const ilu_precond& P, const V1 &v1, V2 &v2) { + copy(v1, v2); + if (P.invert) gmm::upper_tri_solve(gmm::transposed(P.L), v2, true); + else gmm::upper_tri_solve(P.U, v2, false); + } + + template inline + void transposed_left_mult(const ilu_precond& P, const V1 &v1, + V2 &v2) { + copy(v1, v2); + if (P.invert) gmm::upper_tri_solve(P.U, v2, false); + else gmm::upper_tri_solve(gmm::transposed(P.L), v2, true); + } + + template inline + void transposed_right_mult(const ilu_precond& P, const V1 &v1, + V2 &v2) { + copy(v1, v2); + if (P.invert) gmm::lower_tri_solve(P.L, v2, true); + else gmm::lower_tri_solve(gmm::transposed(P.U), v2, false); + } + + +} + +#endif + diff --git a/src/linear_algebra/gmm/gmm_precond_ilut.h b/src/linear_algebra/gmm/gmm_precond_ilut.h new file mode 100644 index 00000000..04bbe7df --- /dev/null +++ b/src/linear_algebra/gmm/gmm_precond_ilut.h @@ -0,0 +1,263 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +// This file is a modified version of ilut.h from ITL. +// See http://osl.iu.edu/research/itl/ +// Following the corresponding Copyright notice. +//=========================================================================== +// +// Copyright (c) 1998-2020, University of Notre Dame. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the University of Notre Dame nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE TRUSTEES OF INDIANA UNIVERSITY AND +// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES +// OF INDIANA UNIVERSITY AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//=========================================================================== + +#ifndef GMM_PRECOND_ILUT_H +#define GMM_PRECOND_ILUT_H + +/**@file gmm_precond_ilut.h + @author Andrew Lumsdaine , Lie-Quan Lee + @date June 5, 2003. + @brief ILUT: Incomplete LU with threshold and K fill-in Preconditioner. +*/ + +/* + Performane comparing for SSOR, ILU and ILUT based on sherman 5 matrix + in Harwell-Boeing collection on Sun Ultra 30 UPA/PCI (UltraSPARC-II 296MHz) + Preconditioner & Factorization time & Number of Iteration \\ \hline + SSOR & 0.010577 & 41 \\ + ILU & 0.019336 & 32 \\ + ILUT with 0 fill-in and threshold of 1.0e-6 & 0.343612 & 23 \\ + ILUT with 5 fill-in and threshold of 1.0e-6 & 0.343612 & 18 \\ \hline +*/ + +#include "gmm_precond.h" + +namespace gmm { + + template struct elt_rsvector_value_less_ { + inline bool operator()(const elt_rsvector_& a, + const elt_rsvector_& b) const + { return (gmm::abs(a.e) > gmm::abs(b.e)); } + }; + + /** Incomplete LU with threshold and K fill-in Preconditioner. + + The algorithm of ILUT(A, 0, 1.0e-6) is slower than ILU(A). If No + fill-in is arrowed, you can use ILU instead of ILUT. + + Notes: The idea under a concrete Preconditioner such as ilut is to + create a Preconditioner object to use in iterative methods. + */ + template + class ilut_precond { + public : + typedef typename linalg_traits::value_type value_type; + typedef wsvector _wsvector; + typedef rsvector _rsvector; + typedef row_matrix<_rsvector> LU_Matrix; + + bool invert; + LU_Matrix L, U; + + protected: + size_type K; + double eps; + + template void do_ilut(const M&, row_major); + void do_ilut(const Matrix&, col_major); + + public: + void build_with(const Matrix& A, int k_ = -1, double eps_ = double(-1)) { + if (k_ >= 0) K = k_; + if (eps_ >= double(0)) eps = eps_; + invert = false; + gmm::resize(L, mat_nrows(A), mat_ncols(A)); + gmm::resize(U, mat_nrows(A), mat_ncols(A)); + do_ilut(A, typename principal_orientation_type::sub_orientation>::potype()); + } + ilut_precond(const Matrix& A, int k_, double eps_) + : L(mat_nrows(A), mat_ncols(A)), U(mat_nrows(A), mat_ncols(A)), + K(k_), eps(eps_) { build_with(A); } + ilut_precond(size_type k_, double eps_) : K(k_), eps(eps_) {} + ilut_precond(void) { K = 10; eps = 1E-7; } + size_type memsize() const { + return sizeof(*this) + (nnz(U)+nnz(L))*sizeof(value_type); + } + }; + + template template + void ilut_precond::do_ilut(const M& A, row_major) { + typedef value_type T; + typedef typename number_traits::magnitude_type R; + + size_type n = mat_nrows(A); + if (n == 0) return; + std::vector indiag(n); + _wsvector w(mat_ncols(A)); + _rsvector ww(mat_ncols(A)), wL(mat_ncols(A)), wU(mat_ncols(A)); + T tmp; + gmm::clear(U); gmm::clear(L); + R prec = default_tol(R()); + R max_pivot = gmm::abs(A(0,0)) * prec; + + for (size_type i = 0; i < n; ++i) { + gmm::copy(mat_const_row(A, i), w); + double norm_row = gmm::vect_norm2(w); + + typename _wsvector::iterator wkold = w.end(); + for (typename _wsvector::iterator wk = w.begin(); + wk != w.end() && wk->first < i; ) { + size_type k = wk->first; + tmp = (wk->second) * indiag[k]; + if (gmm::abs(tmp) < eps * norm_row) w.erase(k); + else { wk->second += tmp; gmm::add(scaled(mat_row(U, k), -tmp), w); } + if (wkold == w.end()) wk = w.begin(); else { wk = wkold; ++wk; } + if (wk != w.end() && wk->first == k) + { if (wkold == w.end()) wkold = w.begin(); else ++wkold; ++wk; } + } + tmp = w[i]; + + if (gmm::abs(tmp) <= max_pivot) { + GMM_WARNING2("pivot " << i << " too small. try with ilutp ?"); + w[i] = tmp = T(1); + } + + max_pivot = std::max(max_pivot, std::min(gmm::abs(tmp) * prec, R(1))); + indiag[i] = T(1) / tmp; + gmm::clean(w, eps * norm_row); + gmm::copy(w, ww); + std::sort(ww.begin(), ww.end(), elt_rsvector_value_less_()); + typename _rsvector::const_iterator wit = ww.begin(), wite = ww.end(); + + size_type nnl = 0, nnu = 0; + wL.base_resize(K); wU.base_resize(K+1); + typename _rsvector::iterator witL = wL.begin(), witU = wU.begin(); + for (; wit != wite; ++wit) + if (wit->c < i) { if (nnl < K) { *witL++ = *wit; ++nnl; } } + else { if (nnu < K || wit->c == i) { *witU++ = *wit; ++nnu; } } + wL.base_resize(nnl); wU.base_resize(nnu); + std::sort(wL.begin(), wL.end()); + std::sort(wU.begin(), wU.end()); + gmm::copy(wL, L.row(i)); + gmm::copy(wU, U.row(i)); + } + + } + + template + void ilut_precond::do_ilut(const Matrix& A, col_major) { + do_ilut(gmm::transposed(A), row_major()); + invert = true; + } + + template inline + void mult(const ilut_precond& P, const V1 &v1, V2 &v2) { + gmm::copy(v1, v2); + if (P.invert) { + gmm::lower_tri_solve(gmm::transposed(P.U), v2, false); + gmm::upper_tri_solve(gmm::transposed(P.L), v2, true); + } + else { + gmm::lower_tri_solve(P.L, v2, true); + gmm::upper_tri_solve(P.U, v2, false); + } + } + + template inline + void transposed_mult(const ilut_precond& P,const V1 &v1,V2 &v2) { + gmm::copy(v1, v2); + if (P.invert) { + gmm::lower_tri_solve(P.L, v2, true); + gmm::upper_tri_solve(P.U, v2, false); + } + else { + gmm::lower_tri_solve(gmm::transposed(P.U), v2, false); + gmm::upper_tri_solve(gmm::transposed(P.L), v2, true); + } + } + + template inline + void left_mult(const ilut_precond& P, const V1 &v1, V2 &v2) { + copy(v1, v2); + if (P.invert) gmm::lower_tri_solve(gmm::transposed(P.U), v2, false); + else gmm::lower_tri_solve(P.L, v2, true); + } + + template inline + void right_mult(const ilut_precond& P, const V1 &v1, V2 &v2) { + copy(v1, v2); + if (P.invert) gmm::upper_tri_solve(gmm::transposed(P.L), v2, true); + else gmm::upper_tri_solve(P.U, v2, false); + } + + template inline + void transposed_left_mult(const ilut_precond& P, const V1 &v1, + V2 &v2) { + copy(v1, v2); + if (P.invert) gmm::upper_tri_solve(P.U, v2, false); + else gmm::upper_tri_solve(gmm::transposed(P.L), v2, true); + } + + template inline + void transposed_right_mult(const ilut_precond& P, const V1 &v1, + V2 &v2) { + copy(v1, v2); + if (P.invert) gmm::lower_tri_solve(P.L, v2, true); + else gmm::lower_tri_solve(gmm::transposed(P.U), v2, false); + } + +} + +#endif + diff --git a/src/linear_algebra/gmm/gmm_precond_ilutp.h b/src/linear_algebra/gmm/gmm_precond_ilutp.h new file mode 100644 index 00000000..785bf35f --- /dev/null +++ b/src/linear_algebra/gmm/gmm_precond_ilutp.h @@ -0,0 +1,284 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2004-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_precond_ilutp.h + @author Yves Renard + @date October 14, 2004. + @brief ILUTP: Incomplete LU with threshold and K fill-in Preconditioner and + column pivoting. + + +*/ +#ifndef GMM_PRECOND_ILUTP_H +#define GMM_PRECOND_ILUTP_H + +#include "gmm_precond_ilut.h" + +namespace gmm { + + /** + ILUTP: Incomplete LU with threshold and K fill-in Preconditioner and + column pivoting. + + See Yousef Saad, Iterative Methods for + sparse linear systems, PWS Publishing Company, section 10.4.4 + + TODO : store the permutation by cycles to avoid the temporary vector + */ + template + class ilutp_precond { + public : + typedef typename linalg_traits::value_type value_type; + typedef wsvector _wsvector; + typedef rsvector _rsvector; + typedef row_matrix<_rsvector> LU_Matrix; + typedef col_matrix<_wsvector> CLU_Matrix; + + bool invert; + LU_Matrix L, U; + gmm::unsorted_sub_index indperm; + gmm::unsorted_sub_index indperminv; + mutable std::vector temporary; + + protected: + size_type K; + double eps; + + template void do_ilutp(const M&, row_major); + void do_ilutp(const Matrix&, col_major); + + public: + void build_with(const Matrix& A, int k_ = -1, double eps_ = double(-1)) { + if (k_ >= 0) K = k_; + if (eps_ >= double(0)) eps = eps_; + invert = false; + gmm::resize(L, mat_nrows(A), mat_ncols(A)); + gmm::resize(U, mat_nrows(A), mat_ncols(A)); + do_ilutp(A, typename principal_orientation_type::sub_orientation>::potype()); + } + ilutp_precond(const Matrix& A, size_type k_, double eps_) + : L(mat_nrows(A), mat_ncols(A)), U(mat_nrows(A), mat_ncols(A)), + K(k_), eps(eps_) { build_with(A); } + ilutp_precond(int k_, double eps_) : K(k_), eps(eps_) {} + ilutp_precond(void) { K = 10; eps = 1E-7; } + size_type memsize() const { + return sizeof(*this) + (nnz(U)+nnz(L))*sizeof(value_type); + } + }; + + + template template + void ilutp_precond::do_ilutp(const M& A, row_major) { + typedef value_type T; + typedef typename number_traits::magnitude_type R; + + size_type n = mat_nrows(A); + CLU_Matrix CU(n,n); + if (n == 0) return; + std::vector indiag(n); + temporary.resize(n); + std::vector ipvt(n), ipvtinv(n); + for (size_type i = 0; i < n; ++i) ipvt[i] = ipvtinv[i] = i; + indperm = unsorted_sub_index(ipvt); + indperminv = unsorted_sub_index(ipvtinv); + _wsvector w(mat_ncols(A)); + _rsvector ww(mat_ncols(A)); + + T tmp = T(0); + gmm::clear(L); gmm::clear(U); + R prec = default_tol(R()); + R max_pivot = gmm::abs(A(0,0)) * prec; + + for (size_type i = 0; i < n; ++i) { + + copy(sub_vector(mat_const_row(A, i), indperm), w); + double norm_row = gmm::vect_norm2(mat_const_row(A, i)); + + typename _wsvector::iterator wkold = w.end(); + for (typename _wsvector::iterator wk = w.begin(); + wk != w.end() && wk->first < i; ) { + size_type k = wk->first; + tmp = (wk->second) * indiag[k]; + if (gmm::abs(tmp) < eps * norm_row) w.erase(k); + else { wk->second += tmp; gmm::add(scaled(mat_row(U, k), -tmp), w); } + if (wkold == w.end()) wk = w.begin(); else { wk = wkold; ++wk; } + if (wk != w.end() && wk->first == k) + { if (wkold == w.end()) wkold = w.begin(); else ++wkold; ++wk; } + } + + gmm::clean(w, eps * norm_row); + gmm::copy(w, ww); + + std::sort(ww.begin(), ww.end(), elt_rsvector_value_less_()); + typename _rsvector::const_iterator wit = ww.begin(), wite = ww.end(); + size_type ip = size_type(-1); + + for (; wit != wite; ++wit) + if (wit->c >= i) { ip = wit->c; tmp = wit->e; break; } + if (ip == size_type(-1) || gmm::abs(tmp) <= max_pivot) + { GMM_WARNING2("pivot " << i << " too small"); ip=i; ww[i]=tmp=T(1); } + max_pivot = std::max(max_pivot, std::min(gmm::abs(tmp) * prec, R(1))); + indiag[i] = T(1) / tmp; + wit = ww.begin(); + + size_type nnl = 0, nnu = 0; + L[i].base_resize(K); U[i].base_resize(K+1); + typename _rsvector::iterator witL = L[i].begin(), witU = U[i].begin(); + for (; wit != wite; ++wit) { + if (wit->c < i) { if (nnl < K) { *witL++ = *wit; ++nnl; } } + else if (nnu < K || wit->c == i) + { CU(i, wit->c) = wit->e; *witU++ = *wit; ++nnu; } + } + L[i].base_resize(nnl); U[i].base_resize(nnu); + std::sort(L[i].begin(), L[i].end()); + std::sort(U[i].begin(), U[i].end()); + + if (ip != i) { + typename _wsvector::const_iterator iti = CU.col(i).begin(); + typename _wsvector::const_iterator itie = CU.col(i).end(); + typename _wsvector::const_iterator itp = CU.col(ip).begin(); + typename _wsvector::const_iterator itpe = CU.col(ip).end(); + + while (iti != itie && itp != itpe) { + if (iti->first < itp->first) + { U.row(iti->first).swap_indices(i, ip); ++iti; } + else if (iti->first > itp->first) + { U.row(itp->first).swap_indices(i,ip);++itp; } + else + { U.row(iti->first).swap_indices(i, ip); ++iti; ++itp; } + } + + for( ; iti != itie; ++iti) U.row(iti->first).swap_indices(i, ip); + for( ; itp != itpe; ++itp) U.row(itp->first).swap_indices(i, ip); + + CU.swap_col(i, ip); + + indperm.swap(i, ip); + indperminv.swap(ipvt[i], ipvt[ip]); + std::swap(ipvtinv[ipvt[i]], ipvtinv[ipvt[ip]]); + std::swap(ipvt[i], ipvt[ip]); + } + } + } + + template + void ilutp_precond::do_ilutp(const Matrix& A, col_major) { + do_ilutp(gmm::transposed(A), row_major()); + invert = true; + } + + template inline + void mult(const ilutp_precond& P, const V1 &v1, V2 &v2) { + if (P.invert) { + gmm::copy(gmm::sub_vector(v1, P.indperm), v2); + gmm::lower_tri_solve(gmm::transposed(P.U), v2, false); + gmm::upper_tri_solve(gmm::transposed(P.L), v2, true); + } + else { + gmm::copy(v1, P.temporary); + gmm::lower_tri_solve(P.L, P.temporary, true); + gmm::upper_tri_solve(P.U, P.temporary, false); + gmm::copy(gmm::sub_vector(P.temporary, P.indperminv), v2); + } + } + + template inline + void transposed_mult(const ilutp_precond& P,const V1 &v1,V2 &v2) { + if (P.invert) { + gmm::copy(v1, P.temporary); + gmm::lower_tri_solve(P.L, P.temporary, true); + gmm::upper_tri_solve(P.U, P.temporary, false); + gmm::copy(gmm::sub_vector(P.temporary, P.indperminv), v2); + } + else { + gmm::copy(gmm::sub_vector(v1, P.indperm), v2); + gmm::lower_tri_solve(gmm::transposed(P.U), v2, false); + gmm::upper_tri_solve(gmm::transposed(P.L), v2, true); + } + } + + template inline + void left_mult(const ilutp_precond& P, const V1 &v1, V2 &v2) { + if (P.invert) { + gmm::copy(gmm::sub_vector(v1, P.indperm), v2); + gmm::lower_tri_solve(gmm::transposed(P.U), v2, false); + } + else { + copy(v1, v2); + gmm::lower_tri_solve(P.L, v2, true); + } + } + + template inline + void right_mult(const ilutp_precond& P, const V1 &v1, V2 &v2) { + if (P.invert) { + copy(v1, v2); + gmm::upper_tri_solve(gmm::transposed(P.L), v2, true); + } + else { + copy(v1, P.temporary); + gmm::upper_tri_solve(P.U, P.temporary, false); + gmm::copy(gmm::sub_vector(P.temporary, P.indperminv), v2); + } + } + + template inline + void transposed_left_mult(const ilutp_precond& P, const V1 &v1, + V2 &v2) { + if (P.invert) { + copy(v1, P.temporary); + gmm::upper_tri_solve(P.U, P.temporary, false); + gmm::copy(gmm::sub_vector(P.temporary, P.indperminv), v2); + } + else { + copy(v1, v2); + gmm::upper_tri_solve(gmm::transposed(P.L), v2, true); + } + } + + template inline + void transposed_right_mult(const ilutp_precond& P, const V1 &v1, + V2 &v2) { + if (P.invert) { + copy(v1, v2); + gmm::lower_tri_solve(P.L, v2, true); + } + else { + gmm::copy(gmm::sub_vector(v1, P.indperm), v2); + gmm::lower_tri_solve(gmm::transposed(P.U), v2, false); + } + } + +} + +#endif + diff --git a/src/linear_algebra/gmm/gmm_precond_mr_approx_inverse.h b/src/linear_algebra/gmm/gmm_precond_mr_approx_inverse.h new file mode 100644 index 00000000..91a749da --- /dev/null +++ b/src/linear_algebra/gmm/gmm_precond_mr_approx_inverse.h @@ -0,0 +1,149 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + + +// This file is a modified version of approximate_inverse.h from ITL. +// See http://osl.iu.edu/research/itl/ +// Following the corresponding Copyright notice. +//=========================================================================== +// +// Copyright (c) 1998-2020, University of Notre Dame. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the University of Notre Dame nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE TRUSTEES OF INDIANA UNIVERSITY AND +// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES +// OF INDIANA UNIVERSITY AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//=========================================================================== + +/**@file gmm_precond_mr_approx_inverse.h + @author Andrew Lumsdaine + @author Lie-Quan Lee + @author Yves Renard + @date June 5, 2003. + @brief Approximate inverse via MR iteration. +*/ + +#ifndef GMM_PRECOND_MR_APPROX_INVERSE_H +#define GMM_PRECOND_MR_APPROX_INVERSE_H + + +#include "gmm_precond.h" + +namespace gmm { + + /** Approximate inverse via MR iteration (see P301 of Saad book). + */ + template + struct mr_approx_inverse_precond { + + typedef typename linalg_traits::value_type value_type; + typedef typename number_traits::magnitude_type magnitude_type; + typedef typename principal_orientation_type::sub_orientation>::potype sub_orientation; + typedef wsvector VVector; + typedef col_matrix MMatrix; + + MMatrix M; + size_type nb_it; + magnitude_type threshold; + + void build_with(const Matrix& A); + mr_approx_inverse_precond(const Matrix& A, size_type nb_it_, + magnitude_type threshold_) + : M(mat_nrows(A), mat_ncols(A)) + { threshold = threshold_; nb_it = nb_it_; build_with(A); } + mr_approx_inverse_precond(void) + { threshold = magnitude_type(1E-7); nb_it = 5; } + mr_approx_inverse_precond(size_type nb_it_, magnitude_type threshold_) + { threshold = threshold_; nb_it = nb_it_; } + const MMatrix &approx_inverse(void) const { return M; } + }; + + template inline + void mult(const mr_approx_inverse_precond& P, const V1 &v1, V2 &v2) + { mult(P.M, v1, v2); } + + template inline + void transposed_mult(const mr_approx_inverse_precond& P, + const V1 &v1,V2 &v2) + { mult(gmm::conjugated(P.M), v1, v2); } + + template + void mr_approx_inverse_precond::build_with(const Matrix& A) { + gmm::resize(M, mat_nrows(A), mat_ncols(A)); + typedef value_type T; + typedef magnitude_type R; + VVector m(mat_ncols(A)),r(mat_ncols(A)),ei(mat_ncols(A)),Ar(mat_ncols(A)); + T alpha = mat_trace(A)/ mat_euclidean_norm_sqr(A); + if (alpha == T(0)) alpha = T(1); + + for (size_type i = 0; i < mat_nrows(A); ++i) { + gmm::clear(m); gmm::clear(ei); + m[i] = alpha; + ei[i] = T(1); + + for (size_type j = 0; j < nb_it; ++j) { + gmm::mult(A, gmm::scaled(m, T(-1)), r); + gmm::add(ei, r); + gmm::mult(A, r, Ar); + T nAr = vect_sp(Ar,Ar); + if (gmm::abs(nAr) > R(0)) { + gmm::add(gmm::scaled(r, gmm::safe_divide(vect_sp(r, Ar), vect_sp(Ar, Ar))), m); + gmm::clean(m, threshold * gmm::vect_norm2(m)); + } else gmm::clear(m); + } + if (gmm::vect_norm2(m) == R(0)) m[i] = alpha; + gmm::copy(m, M.col(i)); + } + } +} + +#endif + diff --git a/src/linear_algebra/gmm/gmm_range_basis.h b/src/linear_algebra/gmm/gmm_range_basis.h new file mode 100644 index 00000000..7d7eeb58 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_range_basis.h @@ -0,0 +1,499 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2009-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_range_basis.h + @author Yves Renard + @date March 10, 2009. + @brief Extract a basis of the range of a (large sparse) matrix from the + columns of this matrix. +*/ +#ifndef GMM_RANGE_BASIS_H +#define GMM_RANGE_BASIS_H +#include "gmm_dense_qr.h" +#include "gmm_dense_lu.h" + +#include "gmm_kernel.h" +#include "gmm_iter.h" +#include +#include + + +namespace gmm { + + + template + void tridiag_qr_algorithm + (std::vector::magnitude_type> diag, + std::vector sdiag, const VECT &eigval_, const MAT1 &eigvect_, + bool compvect, tol_type_for_qr tol = default_tol_for_qr) { + VECT &eigval = const_cast(eigval_); + MAT1 &eigvect = const_cast(eigvect_); + typedef typename number_traits::magnitude_type R; + + if (compvect) gmm::copy(identity_matrix(), eigvect); + + size_type n = diag.size(), q = 0, p, ite = 0; + if (n == 0) return; + if (n == 1) { eigval[0] = gmm::real(diag[0]); return; } + + symmetric_qr_stop_criterion(diag, sdiag, p, q, tol); + + while (q < n) { + sub_interval SUBI(p, n-p-q), SUBJ(0, mat_ncols(eigvect)), SUBK(p, n-p-q); + if (!compvect) SUBK = sub_interval(0,0); + + symmetric_Wilkinson_qr_step(sub_vector(diag, SUBI), + sub_vector(sdiag, SUBI), + sub_matrix(eigvect, SUBJ, SUBK), compvect); + + symmetric_qr_stop_criterion(diag, sdiag, p, q, tol*R(3)); + ++ite; + GMM_ASSERT1(ite < n*100, "QR algorithm failed."); + } + + gmm::copy(diag, eigval); + } + + // Range basis with a restarted Lanczos method + template + void range_basis_eff_Lanczos(const Mat &BB, std::set &columns, + double EPS=1E-12) { + typedef std::set TAB; + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + + size_type nc_r = columns.size(), k; + col_matrix< rsvector > B(mat_nrows(BB), mat_ncols(BB)); + + k = 0; + for (TAB::iterator it = columns.begin(); it!=columns.end(); ++it, ++k){ + gmm::copy(scaled(mat_col(BB, *it), T(1)/vect_norm2(mat_col(BB, *it))), + mat_col(B, *it)); + } + std::vector w(mat_nrows(B)); + size_type restart = 120; + std::vector sdiag(restart); + std::vector eigval(restart), diag(restart); + dense_matrix eigvect(restart, restart); + + R rho = R(-1), rho2; + while (nc_r) { + + std::vector v(nc_r), v0(nc_r), wl(nc_r); + dense_matrix lv(nc_r, restart); + + if (rho < R(0)) { // Estimate of the spectral radius of B^* B + gmm::fill_random(v); + for (size_type i = 0; i < 100; ++i) { + gmm::scale(v, T(1)/vect_norm2(v)); + gmm::copy(v, v0); + k = 0; gmm::clear(w); + for (TAB::iterator it=columns.begin(); it!=columns.end(); ++it, ++k) + add(scaled(mat_col(B, *it), v[k]), w); + k = 0; + for (TAB::iterator it=columns.begin(); it!=columns.end(); ++it, ++k) + v[k] = vect_hp(w, mat_col(B, *it)); + rho = gmm::abs(vect_hp(v, v0) / vect_hp(v0, v0)); + } + rho *= R(2); + } + + // Computing vectors of the null space of de B^* B with restarted Lanczos + rho2 = 0; + gmm::fill_random(v); + size_type iter = 0; + for(;;++iter) { + R rho_old = rho2; + R beta = R(0), alpha; + gmm::scale(v, T(1)/vect_norm2(v)); + size_type eff_restart = restart; + if (sdiag.size() != restart) { + sdiag.resize(restart); eigval.resize(restart); diag.resize(restart); gmm::resize(eigvect, restart, restart); + gmm::resize(lv, nc_r, restart); + } + + for (size_type i = 0; i < restart; ++i) { // Lanczos iterations + gmm::copy(v, mat_col(lv, i)); + gmm::clear(w); + k = 0; + for (TAB::iterator it=columns.begin(); it!=columns.end(); ++it, ++k) + add(scaled(mat_col(B, *it), v[k]), w); + + k = 0; + for (TAB::iterator it=columns.begin(); it!=columns.end(); ++it, ++k) + wl[k] = v[k]*rho - vect_hp(w, mat_col(B, *it)) - beta*v0[k]; + alpha = gmm::real(vect_hp(wl, v)); + diag[i] = alpha; + gmm::add(gmm::scaled(v, -alpha), wl); + sdiag[i] = beta = vect_norm2(wl); + gmm::copy(v, v0); + if (beta < EPS) { eff_restart = i+1; break; } + gmm::copy(gmm::scaled(wl, T(1) / beta), v); + } + if (eff_restart != restart) { + sdiag.resize(eff_restart); eigval.resize(eff_restart); diag.resize(eff_restart); + gmm::resize(eigvect, eff_restart, eff_restart); gmm::resize(lv, nc_r, eff_restart); + } + tridiag_qr_algorithm(diag, sdiag, eigval, eigvect, true); + + size_type num = size_type(-1); + rho2 = R(0); + for (size_type j = 0; j < eff_restart; ++j) + { R nvp=gmm::abs(eigval[j]); if (nvp > rho2) { rho2=nvp; num=j; }} + + GMM_ASSERT1(num != size_type(-1), "Internal error"); + + gmm::mult(lv, mat_col(eigvect, num), v); + + if (gmm::abs(rho2-rho_old) < rho_old*R(EPS)) break; + // if (gmm::abs(rho-rho2) < rho*R(gmm::sqrt(EPS))) break; + if (gmm::abs(rho-rho2) < rho*R(EPS)*R(100)) break; + } + + if (gmm::abs(rho-rho2) < rho*R(EPS*10.)) { + size_type j_max = size_type(-1), j = 0; + R val_max = R(0); + for (TAB::iterator it=columns.begin(); it!=columns.end(); ++it, ++j) + if (gmm::abs(v[j]) > val_max) + { val_max = gmm::abs(v[j]); j_max = *it; } + columns.erase(j_max); nc_r = columns.size(); + } + else break; + } + } + + // Range basis with LU decomposition. Not stable from a numerical viewpoint. + // Complex version not verified + template + void range_basis_eff_lu(const Mat &B, std::set &columns, + std::vector &c_ortho, double EPS) { + + typedef std::set TAB; + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + + size_type nc_r = 0, nc_o = 0, nc = mat_ncols(B), nr = mat_nrows(B), i, j; + + for (TAB::iterator it=columns.begin(); it!=columns.end(); ++it) + if (!(c_ortho[*it])) ++nc_r; else nc_o++; + + if (nc_r > 0) { + + gmm::row_matrix< gmm::rsvector > Hr(nc, nc_r), Ho(nc, nc_o); + gmm::row_matrix< gmm::rsvector > BBr(nr, nc_r), BBo(nr, nc_o); + + i = j = 0; + for (TAB::iterator it=columns.begin(); it!=columns.end(); ++it) + if (!(c_ortho[*it])) + { Hr(*it, i) = T(1)/ vect_norminf(mat_col(B, *it)); ++i; } + else + { Ho(*it, j) = T(1)/ vect_norm2(mat_col(B, *it)); ++j; } + + gmm::mult(B, Hr, BBr); + gmm::mult(B, Ho, BBo); + gmm::dense_matrix M(nc_r, nc_r), BBB(nc_r, nc_o), MM(nc_r, nc_r); + gmm::mult(gmm::conjugated(BBr), BBr, M); + gmm::mult(gmm::conjugated(BBr), BBo, BBB); + gmm::mult(BBB, gmm::conjugated(BBB), MM); + gmm::add(gmm::scaled(MM, T(-1)), M); + + std::vector ipvt(nc_r); + gmm::lu_factor(M, ipvt); + + R emax = R(0); + for (i = 0; i < nc_r; ++i) emax = std::max(emax, gmm::abs(M(i,i))); + + i = 0; + std::set c = columns; + for (TAB::iterator it = c.begin(); it != c.end(); ++it) + if (!(c_ortho[*it])) { + if (gmm::abs(M(i,i)) <= R(EPS)*emax) columns.erase(*it); + ++i; + } + } + } + + + // Range basis with Gram-Schmidt orthogonalization (sparse version) + // The sparse version is better when the sparsity is high and less efficient + // than the dense version for high degree elements (P3, P4 ...) + // Complex version not verified + template + void range_basis_eff_Gram_Schmidt_sparse(const Mat &BB, + std::set &columns, + std::vector &c_ortho, + double EPS) { + + typedef std::set TAB; + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + + size_type nc = mat_ncols(BB), nr = mat_nrows(BB); + std::set c = columns, rc = columns; + + gmm::col_matrix< rsvector > B(nr, nc); + for (std::set::iterator it = columns.begin(); + it != columns.end(); ++it) { + gmm::copy(mat_col(BB, *it), mat_col(B, *it)); + gmm::scale(mat_col(B, *it), T(1)/vect_norm2(mat_col(B, *it))); + } + + for (std::set::iterator it = c.begin(); it != c.end(); ++it) + if (c_ortho[*it]) { + for (std::set::iterator it2 = rc.begin(); + it2 != rc.end(); ++it2) + if (!(c_ortho[*it2])) { + T r = -vect_hp(mat_col(B, *it2), mat_col(B, *it)); + if (r != T(0)) add(scaled(mat_col(B, *it), r), mat_col(B, *it2)); + } + rc.erase(*it); + } + + while (rc.size()) { + R nmax = R(0); size_type cmax = size_type(-1); + for (std::set::iterator it=rc.begin(); it != rc.end();) { + TAB::iterator itnext = it; ++itnext; + R n = vect_norm2(mat_col(B, *it)); + if (nmax < n) { nmax = n; cmax = *it; } + if (n < R(EPS)) { columns.erase(*it); rc.erase(*it); } + it = itnext; + } + + if (nmax < R(EPS)) break; + + gmm::scale(mat_col(B, cmax), T(1)/vect_norm2(mat_col(B, cmax))); + rc.erase(cmax); + for (std::set::iterator it=rc.begin(); it!=rc.end(); ++it) { + T r = -vect_hp(mat_col(B, *it), mat_col(B, cmax)); + if (r != T(0)) add(scaled(mat_col(B, cmax), r), mat_col(B, *it)); + } + } + for (std::set::iterator it=rc.begin(); it!=rc.end(); ++it) + columns.erase(*it); + } + + + // Range basis with Gram-Schmidt orthogonalization (dense version) + template + void range_basis_eff_Gram_Schmidt_dense(const Mat &B, + std::set &columns, + std::vector &c_ortho, + double EPS) { + + typedef std::set TAB; + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + + size_type nc_r = columns.size(), nc = mat_ncols(B), nr = mat_nrows(B), i; + std::set rc; + + row_matrix< gmm::rsvector > H(nc, nc_r), BB(nr, nc_r); + std::vector v(nc_r); + std::vector ind(nc_r); + + i = 0; + for (TAB::iterator it = columns.begin(); it != columns.end(); ++it, ++i) + H(*it, i) = T(1) / vect_norm2(mat_col(B, *it)); + + mult(B, H, BB); + dense_matrix M(nc_r, nc_r); + mult(gmm::conjugated(BB), BB, M); + + i = 0; + for (TAB::iterator it = columns.begin(); it != columns.end(); ++it, ++i) + if (c_ortho[*it]) { + gmm::copy(mat_row(M, i), v); + rank_one_update(M, scaled(v, T(-1)), v); + M(i, i) = T(1); + } + else { rc.insert(i); ind[i] = *it; } + + while (rc.size() > 0) { + + // Next pivot + R nmax = R(0); size_type imax = size_type(-1); + for (TAB::iterator it = rc.begin(); it != rc.end();) { + TAB::iterator itnext = it; ++itnext; + R a = gmm::abs(M(*it, *it)); + if (a > nmax) { nmax = a; imax = *it; } + if (a < R(EPS)) { columns.erase(ind[*it]); rc.erase(*it); } + it = itnext; + } + + if (nmax < R(EPS)) break; + + // Normalization + gmm::scale(mat_row(M, imax), T(1) / sqrt(nmax)); + gmm::scale(mat_col(M, imax), T(1) / sqrt(nmax)); + + // orthogonalization + copy(mat_row(M, imax), v); + rank_one_update(M, scaled(v, T(-1)), v); + M(imax, imax) = T(1); + + rc.erase(imax); + } + for (std::set::iterator it=rc.begin(); it!=rc.end(); ++it) + columns.erase(ind[*it]); + } + + template size_type nnz_eps(const L& l, double eps) { + typename linalg_traits::const_iterator it = vect_const_begin(l), + ite = vect_const_end(l); + size_type res(0); + for (; it != ite; ++it) if (gmm::abs(*it) >= eps) ++res; + return res; + } + + template + bool reserve__rb(const L& l, std::vector &b, double eps) { + typename linalg_traits::const_iterator it = vect_const_begin(l), + ite = vect_const_end(l); + bool ok = true; + for (; it != ite; ++it) + if (gmm::abs(*it) >= eps && b[it.index()]) ok = false; + if (ok) { + for (it = vect_const_begin(l); it != ite; ++it) + if (gmm::abs(*it) >= eps) b[it.index()] = true; + } + return ok; + } + + template + void range_basis(const Mat &B, std::set &columns, + double EPS, col_major, bool skip_init=false) { + + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + + size_type nc = mat_ncols(B), nr = mat_nrows(B); + + std::vector norms(nc); + std::vector c_ortho(nc), booked(nr); + std::vector< std::set > nnzs(mat_nrows(B)); + + if (!skip_init) { + + R norm_max = R(0); + for (size_type i = 0; i < nc; ++i) { + norms[i] = vect_norminf(mat_col(B, i)); + norm_max = std::max(norm_max, norms[i]); + } + + columns.clear(); + for (size_type i = 0; i < nc; ++i) + if (norms[i] > norm_max*R(EPS)) { + columns.insert(i); + nnzs[nnz_eps(mat_col(B, i), R(EPS) * norms[i])].insert(i); + } + + for (size_type i = 1; i < nr; ++i) + for (std::set::iterator it = nnzs[i].begin(); + it != nnzs[i].end(); ++it) + if (reserve__rb(mat_col(B, *it), booked, R(EPS) * norms[*it])) + c_ortho[*it] = true; + } + + size_type sizesm[7] = {125, 200, 350, 550, 800, 1100, 1500}, actsize; + for (int k = 0; k < 7; ++k) { + size_type nc_r = columns.size(); + std::set c1, cres; + actsize = sizesm[k]; + for (std::set::iterator it = columns.begin(); + it != columns.end(); ++it) { + c1.insert(*it); + if (c1.size() >= actsize) { + range_basis_eff_Gram_Schmidt_dense(B, c1, c_ortho, EPS); + for (std::set::iterator it2=c1.begin(); it2 != c1.end(); + ++it2) cres.insert(*it2); + c1.clear(); + } + } + if (c1.size() > 1) + range_basis_eff_Gram_Schmidt_dense(B, c1, c_ortho, EPS); + for (std::set::iterator it = c1.begin(); it != c1.end(); ++it) + cres.insert(*it); + columns = cres; + if (nc_r <= actsize) return; + if (columns.size() == nc_r) break; + if (sizesm[k] >= 350 && columns.size() > (nc_r*19)/20) break; + } + if (columns.size() > std::max(size_type(10), actsize)) + range_basis_eff_Lanczos(B, columns, EPS); + else + range_basis_eff_Gram_Schmidt_dense(B, columns, c_ortho, EPS); + } + + + template + void range_basis(const Mat &B, std::set &columns, + double EPS, row_major) { + typedef typename linalg_traits::value_type T; + gmm::col_matrix< rsvector > BB(mat_nrows(B), mat_ncols(B)); + GMM_WARNING3("A copy of a row matrix is done into a column matrix " + "for range basis algorithm."); + gmm::copy(B, BB); + range_basis(BB, columns, EPS); + } + + /** Range Basis : + Extract a basis of the range of a (large sparse) matrix selecting some + column vectors of this matrix. This is in particular useful to select + an independent set of linear constraints. + + The algorithm is optimized for two cases : + - when the (non trivial) kernel is small. An iterativ algorithm + based on Lanczos method is applied + - when the (non trivial) kernel is large and most of the dependencies + can be detected locally. A block Gram-Schmidt is applied first then + a restarted Lanczos method when the remaining kernel is greatly + smaller. + The restarted Lanczos method could be improved or replaced by a block + Lanczos method, a block Wiedelann method (in order to be parallelized for + instance) or simply could compute more than one vector of the null + space at each iteration. + The LU decomposition has been tested for local elimination but gives bad + results : the algorithm is unstable and do not permit to give the right + number of vector at the end of the process. Moreover, the number of final + vectors depends greatly on the number of vectors in a block of the local + analysis. + */ + template + void range_basis(const Mat &B, std::set &columns, + double EPS=1E-12) { + range_basis(B, columns, EPS, + typename principal_orientation_type + ::sub_orientation>::potype()); +} + +} + +#endif diff --git a/src/linear_algebra/gmm/gmm_real_part.h b/src/linear_algebra/gmm/gmm_real_part.h new file mode 100644 index 00000000..0ea6d4dd --- /dev/null +++ b/src/linear_algebra/gmm/gmm_real_part.h @@ -0,0 +1,608 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2003-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_real_part.h + @author Yves Renard + @date September 18, 2003. + @brief extract the real/imaginary part of vectors/matrices +*/ +#ifndef GMM_REAL_PART_H +#define GMM_REAL_PART_H + +#include "gmm_def.h" +#include "gmm_vector.h" +#include + +namespace gmm { + + struct linalg_real_part {}; + struct linalg_imag_part {}; + template struct which_part {}; + + template typename number_traits::magnitude_type + real_or_imag_part(C x, linalg_real_part) { return gmm::real(x); } + template typename number_traits::magnitude_type + real_or_imag_part(C x, linalg_imag_part) { return gmm::imag(x); } + template C + complex_from(T x, C y, OP op, linalg_real_part) { return std::complex(op(std::real(y), x), std::imag(y)); } + template C + complex_from(T x, C y, OP op,linalg_imag_part) { return std::complex(std::real(y), op(std::imag(y), x)); } + + template struct project2nd { + T operator()(T , T b) const { return b; } + }; + + template class ref_elt_vector > { + + R r; + + public : + + operator T() const { return real_or_imag_part(std::complex(r), PART()); } + ref_elt_vector(R r_) : r(r_) {} + inline ref_elt_vector &operator =(T v) + { r = complex_from(v, std::complex(r), gmm::project2nd(), PART()); return *this; } + inline bool operator ==(T v) const { return (r == v); } + inline bool operator !=(T v) const { return (r != v); } + inline ref_elt_vector &operator +=(T v) + { r = complex_from(v, std::complex(r), std::plus(), PART()); return *this; } + inline ref_elt_vector &operator -=(T v) + { r = complex_from(v, std::complex(r), std::minus(), PART()); return *this; } + inline ref_elt_vector &operator /=(T v) + { r = complex_from(v, std::complex(r), std::divides(), PART()); return *this; } + inline ref_elt_vector &operator *=(T v) + { r = complex_from(v, std::complex(r), std::multiplies(), PART()); return *this; } + inline ref_elt_vector &operator =(const ref_elt_vector &re) + { *this = T(re); return *this; } + T operator +() { return T(*this); } // necessary for unknow reason + T operator -() { return -T(*this); } // necessary for unknow reason + T operator +(T v) { return T(*this)+ v; } // necessary for unknow reason + T operator -(T v) { return T(*this)- v; } // necessary for unknow reason + T operator *(T v) { return T(*this)* v; } // necessary for unknow reason + T operator /(T v) { return T(*this)/ v; } // necessary for unknow reason + }; + + template struct ref_or_value_type { + template + static W r(const T &x, linalg_real_part, W) { + return gmm::real(x); + } + template + static W r(const T &x, linalg_imag_part, W) { + return gmm::imag(x); + } + }; + + template + struct ref_or_value_type > > { + template + static const T &r(const T &x, linalg_real_part, W) + { return x; } + template + static const T &r(const T &x, linalg_imag_part, W) { + return x; + } + template + static T &r(T &x, linalg_real_part, W) + { return x; } + template + static T &r(T &x, linalg_imag_part, W) { + return x; + } + }; + + + /* ********************************************************************* */ + /* Reference to the real part of (complex) vectors */ + /* ********************************************************************* */ + + template + struct part_vector_iterator { + typedef typename std::iterator_traits::value_type vtype; + typedef typename gmm::number_traits::magnitude_type value_type; + typedef value_type *pointer; + typedef ref_elt_vector::reference, PART> > reference; + typedef typename std::iterator_traits::difference_type difference_type; + typedef typename std::iterator_traits::iterator_category + iterator_category; + + IT it; + + part_vector_iterator(void) {} + explicit part_vector_iterator(const IT &i) : it(i) {} + part_vector_iterator(const part_vector_iterator &i) + : it(i.it) {} + part_vector_iterator &operator = + (const part_vector_iterator &i) { it = i.it; return *this; } + + size_type index(void) const { return it.index(); } + part_vector_iterator operator ++(int) + { part_vector_iterator tmp = *this; ++it; return tmp; } + part_vector_iterator operator --(int) + { part_vector_iterator tmp = *this; --it; return tmp; } + part_vector_iterator &operator ++() { ++it; return *this; } + part_vector_iterator &operator --() { --it; return *this; } + part_vector_iterator &operator +=(difference_type i) + { it += i; return *this; } + part_vector_iterator &operator -=(difference_type i) + { it -= i; return *this; } + part_vector_iterator operator +(difference_type i) const + { part_vector_iterator itb = *this; return (itb += i); } + part_vector_iterator operator -(difference_type i) const + { part_vector_iterator itb = *this; return (itb -= i); } + difference_type operator -(const part_vector_iterator &i) const + { return difference_type(it - i.it); } + + reference operator *() const { return reference(*it); } + reference operator [](size_type ii) const { return reference(it[ii]); } + + bool operator ==(const part_vector_iterator &i) const + { return (i.it == it); } + bool operator !=(const part_vector_iterator &i) const + { return (i.it != it); } + bool operator < (const part_vector_iterator &i) const + { return (it < i.it); } + }; + + + template struct part_vector { + typedef part_vector this_type; + typedef typename std::iterator_traits::value_type V; + typedef V * CPT; + typedef typename select_ref::const_iterator, + typename linalg_traits::iterator, PT>::ref_type iterator; + typedef typename linalg_traits::reference reference; + typedef typename linalg_traits::value_type value_type; + typedef typename linalg_traits::porigin_type porigin_type; + + iterator begin_, end_; + porigin_type origin; + size_type size_; + + size_type size(void) const { return size_; } + + reference operator[](size_type i) const { + return reference(ref_or_value_type::r( + linalg_traits::access(origin, begin_, end_, i), + PART(), value_type())); + } + + part_vector(V &v) + : begin_(vect_begin(v)), end_(vect_end(v)), + origin(linalg_origin(v)), size_(gmm::vect_size(v)) {} + part_vector(const V &v) + : begin_(vect_begin(const_cast(v))), + end_(vect_end(const_cast(v))), + origin(linalg_origin(const_cast(v))), size_(gmm::vect_size(v)) {} + part_vector() {} + part_vector(const part_vector &cr) + : begin_(cr.begin_),end_(cr.end_),origin(cr.origin), size_(cr.size_) {} + }; + + template inline + void set_to_begin(part_vector_iterator &it, + ORG o, part_vector *, linalg_modifiable) { + typedef part_vector VECT; + typedef typename linalg_traits::V_reference ref_t; + set_to_begin(it.it, o, typename linalg_traits::pV(), ref_t()); + } + template inline + void set_to_begin(part_vector_iterator &it, + ORG o, const part_vector *, linalg_modifiable) { + typedef part_vector VECT; + typedef typename linalg_traits::V_reference ref_t; + set_to_begin(it.it, o, typename linalg_traits::pV(), ref_t()); + } + template inline + void set_to_end(part_vector_iterator &it, + ORG o, part_vector *, linalg_modifiable) { + typedef part_vector VECT; + typedef typename linalg_traits::V_reference ref_t; + set_to_end(it.it, o, typename linalg_traits::pV(), ref_t()); + } + template inline + void set_to_end(part_vector_iterator &it, + ORG o, const part_vector *, + linalg_modifiable) { + typedef part_vector VECT; + typedef typename linalg_traits::V_reference ref_t; + set_to_end(it.it, o, typename linalg_traits::pV(), ref_t()); + } + + template std::ostream &operator << + (std::ostream &o, const part_vector& m) + { gmm::write(o,m); return o; } + + + /* ********************************************************************* */ + /* Reference to the real or imaginary part of (complex) matrices */ + /* ********************************************************************* */ + + + template struct part_row_ref { + + typedef part_row_ref this_type; + typedef typename std::iterator_traits::value_type M; + typedef M * CPT; + typedef typename std::iterator_traits::reference ref_M; + typedef typename select_ref + ::const_row_iterator, typename linalg_traits + ::row_iterator, PT>::ref_type iterator; + typedef typename linalg_traits::value_type value_type; + typedef typename linalg_traits::reference reference; + typedef typename linalg_traits::porigin_type porigin_type; + + iterator begin_, end_; + porigin_type origin; + size_type nr, nc; + + part_row_ref(ref_M m) + : begin_(mat_row_begin(m)), end_(mat_row_end(m)), + origin(linalg_origin(m)), nr(mat_nrows(m)), nc(mat_ncols(m)) {} + + part_row_ref(const part_row_ref &cr) : + begin_(cr.begin_),end_(cr.end_), origin(cr.origin),nr(cr.nr),nc(cr.nc) {} + + reference operator()(size_type i, size_type j) const { + return reference(ref_or_value_type::r( + linalg_traits::access(begin_+i, j), + PART(), value_type())); + } + }; + + template std::ostream &operator << + (std::ostream &o, const part_row_ref& m) + { gmm::write(o,m); return o; } + + template struct part_col_ref { + + typedef part_col_ref this_type; + typedef typename std::iterator_traits::value_type M; + typedef M * CPT; + typedef typename std::iterator_traits::reference ref_M; + typedef typename select_ref + ::const_col_iterator, typename linalg_traits + ::col_iterator, PT>::ref_type iterator; + typedef typename linalg_traits::value_type value_type; + typedef typename linalg_traits::reference reference; + typedef typename linalg_traits::porigin_type porigin_type; + + iterator begin_, end_; + porigin_type origin; + size_type nr, nc; + + part_col_ref(ref_M m) + : begin_(mat_col_begin(m)), end_(mat_col_end(m)), + origin(linalg_origin(m)), nr(mat_nrows(m)), nc(mat_ncols(m)) {} + + part_col_ref(const part_col_ref &cr) : + begin_(cr.begin_),end_(cr.end_), origin(cr.origin),nr(cr.nr),nc(cr.nc) {} + + reference operator()(size_type i, size_type j) const { + return reference(ref_or_value_type::r( + linalg_traits::access(begin_+j, i), + PART(), value_type())); + } + }; + + + + template std::ostream &operator << + (std::ostream &o, const part_col_ref& m) + { gmm::write(o,m); return o; } + + + + + + +template + struct part_return_ { + typedef abstract_null_type return_type; + }; + template + struct part_return_ { + typedef typename std::iterator_traits::value_type L; + typedef typename select_return, + part_row_ref< L *, PART>, PT>::return_type return_type; + }; + template + struct part_return_ { + typedef typename std::iterator_traits::value_type L; + typedef typename select_return, + part_col_ref, PT>::return_type return_type; + }; + + template struct part_return__{ + typedef abstract_null_type return_type; + }; + + template + struct part_return__ { + typedef typename std::iterator_traits::value_type L; + typedef typename part_return_::sub_orientation>::potype, PART, + PT>::return_type return_type; + }; + + template + struct part_return__ { + typedef typename std::iterator_traits::value_type L; + typedef typename select_return, + part_vector, PT>::return_type return_type; + }; + + template struct part_return { + typedef typename std::iterator_traits::value_type L; + typedef typename part_return__::linalg_type>::return_type return_type; + }; + + template inline + typename part_return::return_type + real_part(const L &l) { + return typename part_return::return_type + (linalg_cast(const_cast(l))); + } + + template inline + typename part_return::return_type + real_part(L &l) { + return typename part_return::return_type(linalg_cast(l)); + } + + template inline + typename part_return::return_type + imag_part(const L &l) { + return typename part_return::return_type + (linalg_cast(const_cast(l))); + } + + template inline + typename part_return::return_type + imag_part(L &l) { + return typename part_return::return_type(linalg_cast(l)); + } + + + template + struct linalg_traits > { + typedef part_vector this_type; + typedef this_type * pthis_type; + typedef PT pV; + typedef typename std::iterator_traits::value_type V; + typedef typename linalg_traits::index_sorted index_sorted; + typedef typename linalg_traits::is_reference V_reference; + typedef typename linalg_traits::origin_type origin_type; + typedef typename select_ref::ref_type porigin_type; + typedef typename which_reference::is_reference is_reference; + typedef abstract_vector linalg_type; + typedef typename linalg_traits::value_type vtype; + typedef typename number_traits::magnitude_type value_type; + typedef typename select_ref::reference, + PART> >, PT>::ref_type reference; + typedef typename select_ref::const_iterator, + typename linalg_traits::iterator, PT>::ref_type pre_iterator; + typedef typename select_ref, + PT>::ref_type iterator; + typedef part_vector_iterator::const_iterator, + pre_iterator, PART> const_iterator; + typedef typename linalg_traits::storage_type storage_type; + static size_type size(const this_type &v) { return v.size(); } + static iterator begin(this_type &v) { + iterator it; it.it = v.begin_; + if (!is_const_reference(is_reference()) && is_sparse(storage_type())) + set_to_begin(it, v.origin, pthis_type(), is_reference()); + return it; + } + static const_iterator begin(const this_type &v) { + const_iterator it(v.begin_); + if (!is_const_reference(is_reference()) && is_sparse(storage_type())) + { set_to_begin(it, v.origin, pthis_type(), is_reference()); } + return it; + } + static iterator end(this_type &v) { + iterator it(v.end_); + if (!is_const_reference(is_reference()) && is_sparse(storage_type())) + set_to_end(it, v.origin, pthis_type(), is_reference()); + return it; + } + static const_iterator end(const this_type &v) { + const_iterator it(v.end_); + if (!is_const_reference(is_reference()) && is_sparse(storage_type())) + set_to_end(it, v.origin, pthis_type(), is_reference()); + return it; + } + static origin_type* origin(this_type &v) { return v.origin; } + static const origin_type* origin(const this_type &v) { return v.origin; } + + static void clear(origin_type* o, const iterator &begin_, + const iterator &end_, abstract_sparse) { + std::deque ind; + iterator it = begin_; + for (; it != end_; ++it) ind.push_front(it.index()); + for (; !(ind.empty()); ind.pop_back()) + access(o, begin_, end_, ind.back()) = value_type(0); + } + static void clear(origin_type* o, const iterator &begin_, + const iterator &end_, abstract_skyline) { + clear(o, begin_, end_, abstract_sparse()); + } + static void clear(origin_type* o, const iterator &begin_, + const iterator &end_, abstract_dense) { + for (iterator it = begin_; it != end_; ++it) *it = value_type(0); + } + + static void clear(origin_type* o, const iterator &begin_, + const iterator &end_) + { clear(o, begin_, end_, storage_type()); } + static void do_clear(this_type &v) { clear(v.origin, begin(v), end(v)); } + static value_type access(const origin_type *o, const const_iterator &it, + const const_iterator &ite, size_type i) { + return real_or_imag_part(linalg_traits::access(o, it.it, ite.it,i), + PART()); + } + static reference access(origin_type *o, const iterator &it, + const iterator &ite, size_type i) + { return reference(linalg_traits::access(o, it.it, ite.it,i)); } + }; + + template + struct linalg_traits > { + typedef part_row_ref this_type; + typedef typename std::iterator_traits::value_type M; + typedef typename linalg_traits::origin_type origin_type; + typedef typename select_ref::ref_type porigin_type; + typedef typename which_reference::is_reference is_reference; + typedef abstract_matrix linalg_type; + typedef typename linalg_traits::value_type vtype; + typedef typename number_traits::magnitude_type value_type; + typedef typename linalg_traits::storage_type storage_type; + typedef abstract_null_type sub_col_type; + typedef abstract_null_type const_sub_col_type; + typedef abstract_null_type col_iterator; + typedef abstract_null_type const_col_iterator; + typedef typename org_type::const_sub_row_type>::t + pre_const_sub_row_type; + typedef typename org_type::sub_row_type>::t pre_sub_row_type; + typedef part_vector + const_sub_row_type; + typedef typename select_ref, PT>::ref_type sub_row_type; + typedef typename linalg_traits::const_row_iterator const_row_iterator; + typedef typename select_ref::row_iterator, PT>::ref_type row_iterator; + typedef typename select_ref< + typename linalg_traits::reference, + typename linalg_traits::reference, + PT>::ref_type reference; + typedef row_major sub_orientation; + typedef typename linalg_traits::index_sorted index_sorted; + static size_type ncols(const this_type &v) { return v.nc; } + static size_type nrows(const this_type &v) { return v.nr; } + static const_sub_row_type row(const const_row_iterator &it) + { return const_sub_row_type(linalg_traits::row(it)); } + static sub_row_type row(const row_iterator &it) + { return sub_row_type(linalg_traits::row(it)); } + static row_iterator row_begin(this_type &m) { return m.begin_; } + static row_iterator row_end(this_type &m) { return m.end_; } + static const_row_iterator row_begin(const this_type &m) + { return m.begin_; } + static const_row_iterator row_end(const this_type &m) { return m.end_; } + static origin_type* origin(this_type &v) { return v.origin; } + static const origin_type* origin(const this_type &v) { return v.origin; } + static void do_clear(this_type &v); + static value_type access(const const_row_iterator &itrow, size_type i) + { return real_or_imag_part(linalg_traits::access(itrow, i), PART()); } + static reference access(const row_iterator &itrow, size_type i) { + return reference(ref_or_value_type::r( + linalg_traits::access(itrow, i), + PART(), value_type())); + } + }; + + template + struct linalg_traits > { + typedef part_col_ref this_type; + typedef typename std::iterator_traits::value_type M; + typedef typename linalg_traits::origin_type origin_type; + typedef typename select_ref::ref_type porigin_type; + typedef typename which_reference::is_reference is_reference; + typedef abstract_matrix linalg_type; + typedef typename linalg_traits::value_type vtype; + typedef typename number_traits::magnitude_type value_type; + typedef typename linalg_traits::storage_type storage_type; + typedef abstract_null_type sub_row_type; + typedef abstract_null_type const_sub_row_type; + typedef abstract_null_type row_iterator; + typedef abstract_null_type const_row_iterator; + typedef typename org_type::const_sub_col_type>::t + pre_const_sub_col_type; + typedef typename org_type::sub_col_type>::t pre_sub_col_type; + typedef part_vector + const_sub_col_type; + typedef typename select_ref, PT>::ref_type sub_col_type; + typedef typename linalg_traits::const_col_iterator const_col_iterator; + typedef typename select_ref::col_iterator, PT>::ref_type col_iterator; + typedef typename select_ref< + typename linalg_traits::reference, + typename linalg_traits::reference, + PT>::ref_type reference; + typedef col_major sub_orientation; + typedef typename linalg_traits::index_sorted index_sorted; + static size_type nrows(const this_type &v) { return v.nr; } + static size_type ncols(const this_type &v) { return v.nc; } + static const_sub_col_type col(const const_col_iterator &it) + { return const_sub_col_type(linalg_traits::col(it)); } + static sub_col_type col(const col_iterator &it) + { return sub_col_type(linalg_traits::col(it)); } + static col_iterator col_begin(this_type &m) { return m.begin_; } + static col_iterator col_end(this_type &m) { return m.end_; } + static const_col_iterator col_begin(const this_type &m) + { return m.begin_; } + static const_col_iterator col_end(const this_type &m) { return m.end_; } + static origin_type* origin(this_type &v) { return v.origin; } + static const origin_type* origin(const this_type &v) { return v.origin; } + static void do_clear(this_type &v); + static value_type access(const const_col_iterator &itcol, size_type i) + { return real_or_imag_part(linalg_traits::access(itcol, i), PART()); } + static reference access(const col_iterator &itcol, size_type i) { + return reference(ref_or_value_type::r( + linalg_traits::access(itcol, i), + PART(), value_type())); + } + }; + + template + void linalg_traits >::do_clear(this_type &v) { + col_iterator it = mat_col_begin(v), ite = mat_col_end(v); + for (; it != ite; ++it) clear(col(it)); + } + + template + void linalg_traits >::do_clear(this_type &v) { + row_iterator it = mat_row_begin(v), ite = mat_row_end(v); + for (; it != ite; ++it) clear(row(it)); + } +} + +#endif // GMM_REAL_PART_H diff --git a/src/linear_algebra/gmm/gmm_ref.h b/src/linear_algebra/gmm/gmm_ref.h new file mode 100644 index 00000000..b41fbdd5 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_ref.h @@ -0,0 +1,526 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2000-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + + +#ifndef GMM_REF_H__ +#define GMM_REF_H__ + +/** @file gmm_ref.h + @author Yves Renard + @date August 26, 2000. + * @brief Provide some simple pseudo-containers. + * + * WARNING : modifiying the container infirm the validity of references. + */ + + +#include +#include "gmm_except.h" + +namespace gmm { + + /* ********************************************************************* */ + /* Simple reference. */ + /* ********************************************************************* */ + + template class tab_ref { + + protected : + + ITER begin_, end_; + + public : + + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::pointer pointer; + typedef typename std::iterator_traits::pointer const_pointer; + typedef typename std::iterator_traits::reference reference; + typedef typename std::iterator_traits::reference const_reference; + typedef typename std::iterator_traits::difference_type + difference_type; + typedef ITER iterator; + typedef ITER const_iterator; + typedef std::reverse_iterator const_reverse_iterator; + typedef std::reverse_iterator reverse_iterator; + typedef size_t size_type; + + bool empty(void) const { return begin_ == end_; } + size_type size(void) const { return end_ - begin_; } + + const iterator &begin(void) { return begin_; } + const const_iterator &begin(void) const { return begin_; } + const iterator &end(void) { return end_; } + const const_iterator &end(void) const { return end_; } + reverse_iterator rbegin(void) { return reverse_iterator(end()); } + const_reverse_iterator rbegin(void) const + { return const_reverse_iterator(end()); } + reverse_iterator rend(void) { return reverse_iterator(begin()); } + const_reverse_iterator rend(void) const + { return const_reverse_iterator(begin()); } + + reference front(void) { return *begin(); } + const_reference front(void) const { return *begin(); } + reference back(void) { return *(--(end())); } + const_reference back(void) const { return *(--(end())); } + void pop_front(void) { ++begin_; } + + const_reference operator [](size_type ii) const { return *(begin_ + ii);} + reference operator [](size_type ii) { return *(begin_ + ii); } + + tab_ref(void) {} + tab_ref(const ITER &b, const ITER &e) : begin_(b), end_(e) {} + }; + + + /* ********************************************************************* */ + /* Reference with index. */ + /* ********************************************************************* */ + +// template struct tab_ref_index_iterator_ +// : public dynamic_array::const_iterator +// { +// typedef typename std::iterator_traits::value_type value_type; +// typedef typename std::iterator_traits::pointer pointer; +// typedef typename std::iterator_traits::reference reference; +// typedef typename std::iterator_traits::difference_type +// difference_type; +// typedef std::random_access_iterator_tag iterator_category; +// typedef size_t size_type; +// typedef dynamic_array::const_iterator dnas_iterator_; +// typedef tab_ref_index_iterator_ iterator; + + +// ITER piter; + +// iterator operator ++(int) +// { iterator tmp = *this; ++(*((dnas_iterator_ *)(this))); return tmp; } +// iterator operator --(int) +// { iterator tmp = *this; --(*((dnas_iterator_ *)(this))); return tmp; } +// iterator &operator ++() +// { ++(*((dnas_iterator_ *)(this))); return *this; } +// iterator &operator --() +// { --(*((dnas_iterator_ *)(this))); return *this; } +// iterator &operator +=(difference_type i) +// { (*((dnas_iterator_ *)(this))) += i; return *this; } +// iterator &operator -=(difference_type i) +// { (*((dnas_iterator_ *)(this))) -= i; return *this; } +// iterator operator +(difference_type i) const +// { iterator it = *this; return (it += i); } +// iterator operator -(difference_type i) const +// { iterator it = *this; return (it -= i); } +// difference_type operator -(const iterator &i) const +// { return *((dnas_iterator_ *)(this)) - *((dnas_iterator_ *)(&i)); } + +// reference operator *() const +// { return *(piter + *((*((dnas_iterator_ *)(this))))); } +// reference operator [](int ii) +// { return *(piter + *((*((dnas_iterator_ *)(this+ii))))); } + +// bool operator ==(const iterator &i) const +// { +// return ((piter) == ((i.piter)) +// && *((dnas_iterator_ *)(this)) == *((*((dnas_iterator_ *)(this))))); +// } +// bool operator !=(const iterator &i) const +// { return !(i == *this); } +// bool operator < (const iterator &i) const +// { +// return ((piter) == ((i.piter)) +// && *((dnas_iterator_ *)(this)) < *((*((dnas_iterator_ *)(this))))); +// } + +// tab_ref_index_iterator_(void) {} +// tab_ref_index_iterator_(const ITER &iter, const dnas_iterator_ &dnas_iter) +// : dnas_iterator_(dnas_iter), piter(iter) {} +// }; + + +// template class tab_ref_index +// { +// public : + +// typedef typename std::iterator_traits::value_type value_type; +// typedef typename std::iterator_traits::pointer pointer; +// typedef typename std::iterator_traits::pointer const_pointer; +// typedef typename std::iterator_traits::reference reference; +// typedef typename std::iterator_traits::reference const_reference; +// typedef typename std::iterator_traits::difference_type +// difference_type; +// typedef size_t size_type; +// typedef tab_ref_index_iterator_ iterator; +// typedef iterator const_iterator; +// typedef std::reverse_iterator const_reverse_iterator; +// typedef std::reverse_iterator reverse_iterator; + +// protected : + +// ITER begin_; +// dynamic_array index_; + +// public : + +// bool empty(void) const { return index_.empty(); } +// size_type size(void) const { return index_.size(); } + + +// iterator begin(void) { return iterator(begin_, index_.begin()); } +// const_iterator begin(void) const +// { return iterator(begin_, index_.begin()); } +// iterator end(void) { return iterator(begin_, index_.end()); } +// const_iterator end(void) const { return iterator(begin_, index_.end()); } +// reverse_iterator rbegin(void) { return reverse_iterator(end()); } +// const_reverse_iterator rbegin(void) const +// { return const_reverse_iterator(end()); } +// reverse_iterator rend(void) { return reverse_iterator(begin()); } +// const_reverse_iterator rend(void) const +// { return const_reverse_iterator(begin()); } + + +// reference front(void) { return *(begin_ +index_[0]); } +// const_reference front(void) const { return *(begin_ +index_[0]); } +// reference back(void) { return *(--(end())); } +// const_reference back(void) const { return *(--(end())); } + +// tab_ref_index(void) {} +// tab_ref_index(const ITER &b, const dynamic_array &ind) +// { begin_ = b; index_ = ind; } + +// // to be changed in a const_reference ? +// value_type operator [](size_type ii) const +// { return *(begin_ + index_[ii]);} +// reference operator [](size_type ii) { return *(begin_ + index_[ii]); } + +// }; + + + /// iterator over a gmm::tab_ref_index_ref + template + struct tab_ref_index_ref_iterator_ + { + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::pointer pointer; + typedef typename std::iterator_traits::reference reference; + typedef typename std::iterator_traits::difference_type + difference_type; + typedef std::random_access_iterator_tag iterator_category; + typedef tab_ref_index_ref_iterator_ iterator; + typedef size_t size_type; + + ITER piter; + ITER_INDEX iter_index; + + iterator operator ++(int) + { iterator tmp = *this; ++iter_index; return tmp; } + iterator operator --(int) + { iterator tmp = *this; --iter_index; return tmp; } + iterator &operator ++() { ++iter_index; return *this; } + iterator &operator --() { --iter_index; return *this; } + iterator &operator +=(difference_type i) + { iter_index += i; return *this; } + iterator &operator -=(difference_type i) + { iter_index -= i; return *this; } + iterator operator +(difference_type i) const + { iterator it = *this; return (it += i); } + iterator operator -(difference_type i) const + { iterator it = *this; return (it -= i); } + difference_type operator -(const iterator &i) const + { return iter_index - i.iter_index; } + + reference operator *() const + { return *(piter + *iter_index); } + reference operator [](size_type ii) const + { return *(piter + *(iter_index+ii)); } + + bool operator ==(const iterator &i) const + { return ((piter) == ((i.piter)) && iter_index == i.iter_index); } + bool operator !=(const iterator &i) const { return !(i == *this); } + bool operator < (const iterator &i) const + { return ((piter) == ((i.piter)) && iter_index < i.iter_index); } + + tab_ref_index_ref_iterator_(void) {} + tab_ref_index_ref_iterator_(const ITER &iter, + const ITER_INDEX &dnas_iter) + : piter(iter), iter_index(dnas_iter) {} + + }; + + /** + convenience template function for quick obtention of a indexed iterator + without having to specify its (long) typename + */ + template + tab_ref_index_ref_iterator_ + index_ref_iterator(ITER it, ITER_INDEX it_i) { + return tab_ref_index_ref_iterator_(it, it_i); + } + + /** indexed array reference (given a container X, and a set of indexes I, + this class provides a pseudo-container Y such that + @code Y[i] = X[I[i]] @endcode + */ + template class tab_ref_index_ref { + public : + + typedef std::iterator_traits traits_type; + typedef typename traits_type::value_type value_type; + typedef typename traits_type::pointer pointer; + typedef typename traits_type::pointer const_pointer; + typedef typename traits_type::reference reference; + typedef typename traits_type::reference const_reference; + typedef typename traits_type::difference_type difference_type; + typedef size_t size_type; + typedef tab_ref_index_ref_iterator_ iterator; + typedef iterator const_iterator; + typedef std::reverse_iterator const_reverse_iterator; + typedef std::reverse_iterator reverse_iterator; + + protected : + + ITER begin_; + ITER_INDEX index_begin_, index_end_; + + public : + + bool empty(void) const { return index_begin_ == index_end_; } + size_type size(void) const { return index_end_ - index_begin_; } + + iterator begin(void) { return iterator(begin_, index_begin_); } + const_iterator begin(void) const + { return iterator(begin_, index_begin_); } + iterator end(void) { return iterator(begin_, index_end_); } + const_iterator end(void) const { return iterator(begin_, index_end_); } + reverse_iterator rbegin(void) { return reverse_iterator(end()); } + const_reverse_iterator rbegin(void) const + { return const_reverse_iterator(end()); } + reverse_iterator rend(void) { return reverse_iterator(begin()); } + const_reverse_iterator rend(void) const + { return const_reverse_iterator(begin()); } + + reference front(void) { return *(begin_ + *index_begin_); } + const_reference front(void) const { return *(begin_ + *index_begin_); } + reference back(void) { return *(--(end())); } + const_reference back(void) const { return *(--(end())); } + void pop_front(void) { ++index_begin_; } + + tab_ref_index_ref(void) {} + tab_ref_index_ref(const ITER &b, const ITER_INDEX &bi, + const ITER_INDEX &ei) + : begin_(b), index_begin_(bi), index_end_(ei) {} + + // to be changed in a const_reference ? + const_reference operator [](size_type ii) const + { return *(begin_ + index_begin_[ii]);} + reference operator [](size_type ii) + { return *(begin_ + index_begin_[ii]); } + + }; + + + /* ********************************************************************* */ + /* Reference on regularly spaced elements. */ + /* ********************************************************************* */ + + template struct tab_ref_reg_spaced_iterator_ { + + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::pointer pointer; + typedef typename std::iterator_traits::reference reference; + typedef typename std::iterator_traits::difference_type + difference_type; + typedef typename std::iterator_traits::iterator_category + iterator_category; + typedef size_t size_type; + typedef tab_ref_reg_spaced_iterator_ iterator; + + ITER it; + size_type N, i; + + iterator operator ++(int) { iterator tmp = *this; i++; return tmp; } + iterator operator --(int) { iterator tmp = *this; i--; return tmp; } + iterator &operator ++() { i++; return *this; } + iterator &operator --() { i--; return *this; } + iterator &operator +=(difference_type ii) { i+=ii; return *this; } + iterator &operator -=(difference_type ii) { i-=ii; return *this; } + iterator operator +(difference_type ii) const + { iterator itt = *this; return (itt += ii); } + iterator operator -(difference_type ii) const + { iterator itt = *this; return (itt -= ii); } + difference_type operator -(const iterator &ii) const + { return (N ? (it - ii.it) / N : 0) + i - ii.i; } + + reference operator *() const { return *(it + i*N); } + reference operator [](size_type ii) const { return *(it + (i+ii)*N); } + + bool operator ==(const iterator &ii) const + { return (*this - ii) == difference_type(0); } + bool operator !=(const iterator &ii) const + { return (*this - ii) != difference_type(0); } + bool operator < (const iterator &ii) const + { return (*this - ii) < difference_type(0); } + + tab_ref_reg_spaced_iterator_(void) {} + tab_ref_reg_spaced_iterator_(const ITER &iter, size_type n, size_type ii) + : it(iter), N(n), i(ii) { } + + }; + + /** + convenience template function for quick obtention of a strided iterator + without having to specify its (long) typename + */ + template tab_ref_reg_spaced_iterator_ + reg_spaced_iterator(ITER it, size_t stride) { + return tab_ref_reg_spaced_iterator_(it, stride); + } + + /** + provide a "strided" view a of container + */ + template class tab_ref_reg_spaced { + public : + + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::pointer pointer; + typedef typename std::iterator_traits::pointer const_pointer; + typedef typename std::iterator_traits::reference reference; + typedef typename std::iterator_traits::reference const_reference; + typedef typename std::iterator_traits::difference_type + difference_type; + typedef size_t size_type; + typedef tab_ref_reg_spaced_iterator_ iterator; + typedef iterator const_iterator; + typedef std::reverse_iterator const_reverse_iterator; + typedef std::reverse_iterator reverse_iterator; + + protected : + + ITER begin_; + size_type N, size_; + + public : + + bool empty(void) const { return size_ == 0; } + size_type size(void) const { return size_; } + + iterator begin(void) { return iterator(begin_, N, 0); } + const_iterator begin(void) const { return iterator(begin_, N, 0); } + iterator end(void) { return iterator(begin_, N, size_); } + const_iterator end(void) const { return iterator(begin_, N, size_); } + reverse_iterator rbegin(void) { return reverse_iterator(end()); } + const_reverse_iterator rbegin(void) const + { return const_reverse_iterator(end()); } + reverse_iterator rend(void) { return reverse_iterator(begin()); } + const_reverse_iterator rend(void) const + { return const_reverse_iterator(begin()); } + + reference front(void) { return *begin_; } + const_reference front(void) const { return *begin_; } + reference back(void) { return *(begin_ + N * (size_-1)); } + const_reference back(void) const { return *(begin_ + N * (size_-1)); } + void pop_front(void) { begin_ += N; } + + tab_ref_reg_spaced(void) {} + tab_ref_reg_spaced(const ITER &b, size_type n, size_type s) + : begin_(b), N(n), size_(s) {} + + + const_reference operator [](size_type ii) const + { return *(begin_ + ii * N);} + reference operator [](size_type ii) { return *(begin_ + ii * N); } + + }; + + /// iterator over a tab_ref_with_selection + template + struct tab_ref_with_selection_iterator_ : public ITER { + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::pointer pointer; + typedef typename std::iterator_traits::reference reference; + typedef typename std::iterator_traits::difference_type + difference_type; + typedef std::forward_iterator_tag iterator_category; + typedef tab_ref_with_selection_iterator_ iterator; + const COND cond; + + void forward(void) { while (!(cond)(*this)) ITER::operator ++(); } + iterator &operator ++() + { ITER::operator ++(); forward(); return *this; } + iterator operator ++(int) + { iterator tmp = *this; ++(*this); return tmp; } + + tab_ref_with_selection_iterator_(void) {} + tab_ref_with_selection_iterator_(const ITER &iter, const COND c) + : ITER(iter), cond(c) {} + + }; + + /** + given a container X and a predicate P, provide pseudo-container Y + of all elements of X such that P(X[i]). + */ + template class tab_ref_with_selection { + + protected : + + ITER begin_, end_; + COND cond; + + public : + + typedef typename std::iterator_traits::value_type value_type; + typedef typename std::iterator_traits::pointer pointer; + typedef typename std::iterator_traits::pointer const_pointer; + typedef typename std::iterator_traits::reference reference; + typedef typename std::iterator_traits::reference const_reference; + typedef size_t size_type; + typedef tab_ref_with_selection_iterator_ iterator; + typedef iterator const_iterator; + + iterator begin(void) const + { iterator it(begin_, cond); it.forward(); return it; } + iterator end(void) const { return iterator(end_, cond); } + bool empty(void) const { return begin_ == end_; } + + value_type front(void) const { return *begin(); } + void pop_front(void) { ++begin_; begin_ = begin(); } + + COND &condition(void) { return cond; } + const COND &condition(void) const { return cond; } + + tab_ref_with_selection(void) {} + tab_ref_with_selection(const ITER &b, const ITER &e, const COND &c) + : begin_(b), end_(e), cond(c) { begin_ = begin(); } + + }; + +} + +#endif /* GMM_REF_H__ */ diff --git a/src/linear_algebra/gmm/gmm_scaled.h b/src/linear_algebra/gmm/gmm_scaled.h new file mode 100644 index 00000000..0d7d563c --- /dev/null +++ b/src/linear_algebra/gmm/gmm_scaled.h @@ -0,0 +1,434 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_scaled.h + @author Yves Renard + @date November 10, 2002. + @brief get a scaled view of a vector/matrix. +*/ +#ifndef GMM_SCALED_H__ +#define GMM_SCALED_H__ + +#include "gmm_def.h" + +namespace gmm { + + /* ********************************************************************* */ + /* Scaled references on vectors */ + /* ********************************************************************* */ + + template struct scaled_const_iterator { + typedef typename strongest_numeric_type::value_type, + S>::T value_type; + + typedef typename std::iterator_traits::pointer pointer; + typedef typename std::iterator_traits::reference reference; + typedef typename std::iterator_traits::difference_type difference_type; + typedef typename std::iterator_traits::iterator_category + iterator_category; + + IT it; + S r; + + scaled_const_iterator(void) {} + scaled_const_iterator(const IT &i, S x) : it(i), r(x) {} + + inline size_type index(void) const { return it.index(); } + inline scaled_const_iterator operator ++(int) + { scaled_const_iterator tmp = *this; ++it; return tmp; } + inline scaled_const_iterator operator --(int) + { scaled_const_iterator tmp = *this; --it; return tmp; } + inline scaled_const_iterator &operator ++() { ++it; return *this; } + inline scaled_const_iterator &operator --() { --it; return *this; } + inline scaled_const_iterator &operator +=(difference_type i) + { it += i; return *this; } + inline scaled_const_iterator &operator -=(difference_type i) + { it -= i; return *this; } + inline scaled_const_iterator operator +(difference_type i) const + { scaled_const_iterator itb = *this; return (itb += i); } + inline scaled_const_iterator operator -(difference_type i) const + { scaled_const_iterator itb = *this; return (itb -= i); } + inline difference_type operator -(const scaled_const_iterator &i) const + { return difference_type(it - i.it); } + + inline value_type operator *() const { return (*it) * value_type(r); } + inline value_type operator [](size_type ii) const { return it[ii] * r; } + + inline bool operator ==(const scaled_const_iterator &i) const + { return (i.it == it); } + inline bool operator !=(const scaled_const_iterator &i) const + { return (i.it != it); } + inline bool operator < (const scaled_const_iterator &i) const + { return (it < i.it); } + }; + + template struct scaled_vector_const_ref { + typedef scaled_vector_const_ref this_type; + typedef typename linalg_traits::value_type value_type; + typedef typename linalg_traits::const_iterator iterator; + typedef typename linalg_traits::reference reference; + typedef typename linalg_traits::origin_type origin_type; + + iterator begin_, end_; + const origin_type *origin; + size_type size_; + S r; + + scaled_vector_const_ref(const V &v, S rr) + : begin_(vect_const_begin(v)), end_(vect_const_end(v)), + origin(linalg_origin(v)), size_(vect_size(v)), r(rr) {} + + reference operator[](size_type i) const + { return value_type(r) * linalg_traits::access(origin, begin_, end_, i); } + }; + + + template std::ostream &operator << + (std::ostream &o, const scaled_vector_const_ref& m) + { gmm::write(o,m); return o; } + + /* ********************************************************************* */ + /* Scaled references on matrices */ + /* ********************************************************************* */ + + template struct scaled_row_const_iterator { + typedef scaled_row_const_iterator iterator; + typedef typename linalg_traits::const_row_iterator ITER; + typedef ptrdiff_t difference_type; + typedef size_t size_type; + + ITER it; + S r; + + inline iterator operator ++(int) { iterator tmp=*this; it++; return tmp; } + inline iterator operator --(int) { iterator tmp=*this; it--; return tmp; } + inline iterator &operator ++() { it++; return *this; } + inline iterator &operator --() { it--; return *this; } + iterator &operator +=(difference_type i) { it += i; return *this; } + iterator &operator -=(difference_type i) { it -= i; return *this; } + iterator operator +(difference_type i) const + { iterator itt = *this; return (itt += i); } + iterator operator -(difference_type i) const + { iterator itt = *this; return (itt -= i); } + difference_type operator -(const iterator &i) const + { return it - i.it; } + + inline ITER operator *() const { return it; } + inline ITER operator [](int i) { return it + i; } + + inline bool operator ==(const iterator &i) const { return (it == i.it); } + inline bool operator !=(const iterator &i) const { return !(i == *this); } + inline bool operator < (const iterator &i) const { return (it < i.it); } + + scaled_row_const_iterator(void) {} + scaled_row_const_iterator(const ITER &i, S rr) + : it(i), r(rr) { } + + }; + + template struct scaled_row_matrix_const_ref { + + typedef scaled_row_matrix_const_ref this_type; + typedef typename linalg_traits::const_row_iterator iterator; + typedef typename linalg_traits::value_type value_type; + typedef typename linalg_traits::origin_type origin_type; + + iterator begin_, end_; + const origin_type *origin; + S r; + size_type nr, nc; + + scaled_row_matrix_const_ref(const M &m, S rr) + : begin_(mat_row_begin(m)), end_(mat_row_end(m)), + origin(linalg_origin(m)), r(rr), nr(mat_nrows(m)), nc(mat_ncols(m)) {} + + value_type operator()(size_type i, size_type j) const + { return r * linalg_traits::access(begin_+i, j); } + }; + + + template std::ostream &operator << + (std::ostream &o, const scaled_row_matrix_const_ref& m) + { gmm::write(o,m); return o; } + + + template struct scaled_col_const_iterator { + typedef scaled_col_const_iterator iterator; + typedef typename linalg_traits::const_col_iterator ITER; + typedef ptrdiff_t difference_type; + typedef size_t size_type; + + ITER it; + S r; + + iterator operator ++(int) { iterator tmp = *this; it++; return tmp; } + iterator operator --(int) { iterator tmp = *this; it--; return tmp; } + iterator &operator ++() { it++; return *this; } + iterator &operator --() { it--; return *this; } + iterator &operator +=(difference_type i) { it += i; return *this; } + iterator &operator -=(difference_type i) { it -= i; return *this; } + iterator operator +(difference_type i) const + { iterator itt = *this; return (itt += i); } + iterator operator -(difference_type i) const + { iterator itt = *this; return (itt -= i); } + difference_type operator -(const iterator &i) const + { return it - i.it; } + + ITER operator *() const { return it; } + ITER operator [](int i) { return it + i; } + + bool operator ==(const iterator &i) const { return (it == i.it); } + bool operator !=(const iterator &i) const { return !(i == *this); } + bool operator < (const iterator &i) const { return (it < i.it); } + + scaled_col_const_iterator(void) {} + scaled_col_const_iterator(const ITER &i, S rr) + : it(i), r(rr) { } + + }; + + template struct scaled_col_matrix_const_ref { + + typedef scaled_col_matrix_const_ref this_type; + typedef typename linalg_traits::const_col_iterator iterator; + typedef typename linalg_traits::value_type value_type; + typedef typename linalg_traits::origin_type origin_type; + + iterator begin_, end_; + const origin_type *origin; + S r; + size_type nr, nc; + + scaled_col_matrix_const_ref(const M &m, S rr) + : begin_(mat_col_begin(m)), end_(mat_col_end(m)), + origin(linalg_origin(m)), r(rr), nr(mat_nrows(m)), nc(mat_ncols(m)) {} + + value_type operator()(size_type i, size_type j) const + { return r * linalg_traits::access(begin_+j, i); } + }; + + + + template std::ostream &operator << + (std::ostream &o, const scaled_col_matrix_const_ref& m) + { gmm::write(o,m); return o; } + + + template struct scaled_return__ { + typedef abstract_null_type return_type; + }; + template struct scaled_return__ + { typedef scaled_row_matrix_const_ref return_type; }; + template struct scaled_return__ + { typedef scaled_col_matrix_const_ref return_type; }; + + + template struct scaled_return_ { + typedef abstract_null_type return_type; + }; + template struct scaled_return_ + { typedef scaled_vector_const_ref return_type; }; + template struct scaled_return_ { + typedef typename scaled_return__::sub_orientation>::potype>::return_type return_type; + }; + + template struct scaled_return { + typedef typename scaled_return_::linalg_type>::return_type return_type; + }; + + template inline + typename scaled_return::return_type + scaled(const L &v, S x) + { return scaled(v, x, typename linalg_traits::linalg_type()); } + + template inline + typename scaled_return::return_type + scaled(const V &v, S x, abstract_vector) + { return scaled_vector_const_ref(v, x); } + + template inline + typename scaled_return::return_type + scaled(const M &m, S x,abstract_matrix) { + return scaled(m, x, typename principal_orientation_type::sub_orientation>::potype()); + } + + template inline + typename scaled_return::return_type + scaled(const M &m, S x, row_major) { + return scaled_row_matrix_const_ref(m, x); + } + + template inline + typename scaled_return::return_type + scaled(const M &m, S x, col_major) { + return scaled_col_matrix_const_ref(m, x); + } + + + /* ******************************************************************** */ + /* matrix or vector scale */ + /* ******************************************************************** */ + + template inline + void scale(L& l, typename linalg_traits::value_type a) + { scale(l, a, typename linalg_traits::linalg_type()); } + + template inline + void scale(const L& l, typename linalg_traits::value_type a) + { scale(linalg_const_cast(l), a); } + + template inline + void scale(L& l, typename linalg_traits::value_type a, abstract_vector) { + typename linalg_traits::iterator it = vect_begin(l), ite = vect_end(l); + for ( ; it != ite; ++it) *it *= a; + } + + template + void scale(L& l, typename linalg_traits::value_type a, abstract_matrix) { + scale(l, a, typename principal_orientation_type::sub_orientation>::potype()); + } + + template + void scale(L& l, typename linalg_traits::value_type a, row_major) { + typename linalg_traits::row_iterator it = mat_row_begin(l), + ite = mat_row_end(l); + for ( ; it != ite; ++it) scale(linalg_traits::row(it), a); + } + + template + void scale(L& l, typename linalg_traits::value_type a, col_major) { + typename linalg_traits::col_iterator it = mat_col_begin(l), + ite = mat_col_end(l); + for ( ; it != ite; ++it) scale(linalg_traits::col(it), a); + } + + template struct linalg_traits > { + typedef scaled_vector_const_ref this_type; + typedef linalg_const is_reference; + typedef abstract_vector linalg_type; + typedef typename strongest_numeric_type::value_type>::T value_type; + typedef typename linalg_traits::origin_type origin_type; + typedef value_type reference; + typedef abstract_null_type iterator; + typedef scaled_const_iterator::const_iterator, S> + const_iterator; + typedef typename linalg_traits::storage_type storage_type; + typedef typename linalg_traits::index_sorted index_sorted; + static size_type size(const this_type &v) { return v.size_; } + static const_iterator begin(const this_type &v) + { return const_iterator(v.begin_, v.r); } + static const_iterator end(const this_type &v) + { return const_iterator(v.end_, v.r); } + static const origin_type* origin(const this_type &v) { return v.origin; } + static value_type access(const origin_type *o, const const_iterator &it, + const const_iterator &ite, size_type i) + { return it.r * (linalg_traits::access(o, it.it, ite.it, i)); } + + }; + + + template struct linalg_traits > { + typedef scaled_row_matrix_const_ref this_type; + typedef linalg_const is_reference; + typedef abstract_matrix linalg_type; + typedef typename linalg_traits::origin_type origin_type; + typedef typename strongest_numeric_type::value_type>::T value_type; + typedef value_type reference; + typedef typename linalg_traits::storage_type storage_type; + typedef typename org_type::const_sub_row_type>::t vector_type; + typedef scaled_vector_const_ref sub_row_type; + typedef scaled_vector_const_ref const_sub_row_type; + typedef scaled_row_const_iterator row_iterator; + typedef scaled_row_const_iterator const_row_iterator; + typedef abstract_null_type const_sub_col_type; + typedef abstract_null_type sub_col_type; + typedef abstract_null_type const_col_iterator; + typedef abstract_null_type col_iterator; + typedef row_major sub_orientation; + typedef typename linalg_traits::index_sorted index_sorted; + static size_type nrows(const this_type &m) + { return m.nr; } + static size_type ncols(const this_type &m) + { return m.nc; } + static const_sub_row_type row(const const_row_iterator &it) + { return scaled(linalg_traits::row(it.it), it.r); } + static const_row_iterator row_begin(const this_type &m) + { return const_row_iterator(m.begin_, m.r); } + static const_row_iterator row_end(const this_type &m) + { return const_row_iterator(m.end_, m.r); } + static const origin_type* origin(const this_type &m) { return m.origin; } + static value_type access(const const_row_iterator &it, size_type i) + { return it.r * (linalg_traits::access(it.it, i)); } + }; + + template struct linalg_traits > { + typedef scaled_col_matrix_const_ref this_type; + typedef linalg_const is_reference; + typedef abstract_matrix linalg_type; + typedef typename strongest_numeric_type::value_type>::T value_type; + typedef typename linalg_traits::origin_type origin_type; + typedef value_type reference; + typedef typename linalg_traits::storage_type storage_type; + typedef typename org_type::const_sub_col_type>::t vector_type; + typedef abstract_null_type sub_col_type; + typedef scaled_vector_const_ref const_sub_col_type; + typedef abstract_null_type col_iterator; + typedef scaled_col_const_iterator const_col_iterator; + typedef abstract_null_type const_sub_row_type; + typedef abstract_null_type sub_row_type; + typedef abstract_null_type const_row_iterator; + typedef abstract_null_type row_iterator; + typedef col_major sub_orientation; + typedef typename linalg_traits::index_sorted index_sorted; + static size_type ncols(const this_type &m) + { return m.nc; } + static size_type nrows(const this_type &m) + { return m.nr; } + static const_sub_col_type col(const const_col_iterator &it) + { return scaled(linalg_traits::col(it.it), it.r); } + static const_col_iterator col_begin(const this_type &m) + { return const_col_iterator(m.begin_, m.r); } + static const_col_iterator col_end(const this_type &m) + { return const_col_iterator(m.end_, m.r); } + static const origin_type* origin(const this_type &m) { return m.origin; } + static value_type access(const const_col_iterator &it, size_type i) + { return it.r * (linalg_traits::access(it.it, i)); } + }; + + +} + +#endif // GMM_SCALED_H__ diff --git a/src/linear_algebra/gmm/gmm_solver_Schwarz_additive.h b/src/linear_algebra/gmm/gmm_solver_Schwarz_additive.h new file mode 100644 index 00000000..5585e027 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_solver_Schwarz_additive.h @@ -0,0 +1,805 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_solver_Schwarz_additive.h + @author Yves Renard + @author Michel Fournie + @date October 13, 2002. +*/ + +#ifndef GMM_SOLVERS_SCHWARZ_ADDITIVE_H__ +#define GMM_SOLVERS_SCHWARZ_ADDITIVE_H__ + +#include "gmm_kernel.h" +#include "gmm_superlu_interface.h" +#include "gmm_solver_cg.h" +#include "gmm_solver_gmres.h" +#include "gmm_solver_bicgstab.h" +#include "gmm_solver_qmr.h" + +namespace gmm { + + /* ******************************************************************** */ + /* Additive Schwarz interfaced local solvers */ + /* ******************************************************************** */ + + struct using_cg {}; + struct using_gmres {}; + struct using_bicgstab {}; + struct using_qmr {}; + + template + struct actual_precond { + typedef P APrecond; + static const APrecond &transform(const P &PP) { return PP; } + }; + + template + void AS_local_solve(using_cg, const Matrix1 &A, Vector &x, const Vector &b, + const Precond &P, iteration &iter) + { cg(A, x, b, P, iter); } + + template + void AS_local_solve(using_gmres, const Matrix1 &A, Vector &x, + const Vector &b, const Precond &P, iteration &iter) + { gmres(A, x, b, P, 100, iter); } + + template + void AS_local_solve(using_bicgstab, const Matrix1 &A, Vector &x, + const Vector &b, const Precond &P, iteration &iter) + { bicgstab(A, x, b, P, iter); } + + template + void AS_local_solve(using_qmr, const Matrix1 &A, Vector &x, + const Vector &b, const Precond &P, iteration &iter) + { qmr(A, x, b, P, iter); } + +#if defined(GMM_USES_SUPERLU) + struct using_superlu {}; + + template + struct actual_precond { + typedef typename linalg_traits::value_type value_type; + typedef SuperLU_factor APrecond; + template + static APrecond transform(const PR &) { return APrecond(); } + static const APrecond &transform(const APrecond &PP) { return PP; } + }; + + template + void AS_local_solve(using_superlu, const Matrix1 &, Vector &x, + const Vector &b, const Precond &P, iteration &iter) + { P.solve(x, b); iter.set_iteration(1); } +#endif + + /* ******************************************************************** */ + /* Additive Schwarz Linear system */ + /* ******************************************************************** */ + + template + struct add_schwarz_mat{ + typedef typename linalg_traits::value_type value_type; + + const Matrix1 *A; + const std::vector *vB; + std::vector vAloc; + mutable iteration iter; + double residual; + mutable size_type itebilan; + mutable std::vector > gi, fi; + std::vector::APrecond> precond1; + + void init(const Matrix1 &A_, const std::vector &vB_, + iteration iter_, const Precond &P, double residual_); + + add_schwarz_mat(void) {} + add_schwarz_mat(const Matrix1 &A_, const std::vector &vB_, + iteration iter_, const Precond &P, double residual_) + { init(A_, vB_, iter_, P, residual_); } + }; + + template + void add_schwarz_mat::init( + const Matrix1 &A_, const std::vector &vB_, + iteration iter_, const Precond &P, double residual_) { + + vB = &vB_; A = &A_; iter = iter_; + residual = residual_; + + size_type nb_sub = vB->size(); + vAloc.resize(nb_sub); + gi.resize(nb_sub); fi.resize(nb_sub); + precond1.resize(nb_sub); + std::fill(precond1.begin(), precond1.end(), + actual_precond::transform(P)); + itebilan = 0; + + if (iter.get_noisy()) cout << "Init pour sub dom "; +#ifdef GMM_USES_MPI + int size,tranche,borne_sup,borne_inf,rank,tag1=11,tag2=12,tag3=13,sizepr = 0; + // int tab[4]; + double t_ref,t_final; + MPI_Status status; + t_ref=MPI_Wtime(); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + tranche=nb_sub/size; + borne_inf=rank*tranche; + borne_sup=(rank+1)*tranche; + // if (rank==size-1) borne_sup = nb_sub; + + cout << "Nombre de sous domaines " << borne_sup - borne_inf << endl; + + int sizeA = mat_nrows(*A); + gmm::csr_matrix Acsr(sizeA, sizeA), Acsrtemp(sizeA, sizeA); + gmm::copy(gmm::eff_matrix(*A), Acsr); + int next = (rank + 1) % size; + int previous = (rank + size - 1) % size; + //communication of local information on ring pattern + //Each process receive Nproc-1 contributions + + for (int nproc = 0; nproc < size; ++nproc) { + for (size_type i = size_type(borne_inf); i < size_type(borne_sup); ++i) { +// for (size_type i = 0; i < nb_sub/size; ++i) { +// for (size_type i = 0; i < nb_sub; ++i) { + // size_type i=(rank+size*(j-1)+nb_sub)%nb_sub; + + cout << "Sous domaines " << i << " : " << mat_ncols((*vB)[i]) << endl; +#else + for (size_type i = 0; i < nb_sub; ++i) { +#endif + + if (iter.get_noisy()) cout << i << " " << std::flush; + Matrix2 Maux(mat_ncols((*vB)[i]), mat_nrows((*vB)[i])); + +#ifdef GMM_USES_MPI + Matrix2 Maux2(mat_ncols((*vB)[i]), mat_ncols((*vB)[i])); + if (nproc == 0) { + gmm::resize(vAloc[i], mat_ncols((*vB)[i]), mat_ncols((*vB)[i])); + gmm::clear(vAloc[i]); + } + gmm::mult(gmm::transposed((*vB)[i]), Acsr, Maux); + gmm::mult(Maux, (*vB)[i], Maux2); + gmm::add(Maux2, vAloc[i]); +#else + gmm::resize(vAloc[i], mat_ncols((*vB)[i]), mat_ncols((*vB)[i])); + gmm::mult(gmm::transposed((*vB)[i]), *A, Maux); + gmm::mult(Maux, (*vB)[i], vAloc[i]); +#endif + +#ifdef GMM_USES_MPI + if (nproc == size - 1 ) { +#endif + precond1[i].build_with(vAloc[i]); + gmm::resize(fi[i], mat_ncols((*vB)[i])); + gmm::resize(gi[i], mat_ncols((*vB)[i])); +#ifdef GMM_USES_MPI + } +#else + } +#endif +#ifdef GMM_USES_MPI + } + if (nproc != size - 1) { + MPI_Sendrecv(&(Acsr.jc[0]), sizeA+1, MPI_INT, next, tag2, + &(Acsrtemp.jc[0]), sizeA+1, MPI_INT, previous, tag2, + MPI_COMM_WORLD, &status); + if (Acsrtemp.jc[sizeA] > size_type(sizepr)) { + sizepr = Acsrtemp.jc[sizeA]; + gmm::resize(Acsrtemp.pr, sizepr); + gmm::resize(Acsrtemp.ir, sizepr); + } + MPI_Sendrecv(&(Acsr.ir[0]), Acsr.jc[sizeA], MPI_INT, next, tag1, + &(Acsrtemp.ir[0]), Acsrtemp.jc[sizeA], MPI_INT, previous, tag1, + MPI_COMM_WORLD, &status); + + MPI_Sendrecv(&(Acsr.pr[0]), Acsr.jc[sizeA], mpi_type(value_type()), next, tag3, + &(Acsrtemp.pr[0]), Acsrtemp.jc[sizeA], mpi_type(value_type()), previous, tag3, + MPI_COMM_WORLD, &status); + gmm::copy(Acsrtemp, Acsr); + } + } + t_final=MPI_Wtime(); + cout<<"temps boucle precond "<< t_final-t_ref< + void mult(const add_schwarz_mat &M, + const Vector2 &p, Vector3 &q) { + size_type itebilan = 0; +#ifdef GMM_USES_MPI + static double tmult_tot = 0.0; + double t_ref = MPI_Wtime(); +#endif + // cout << "tmult AS begin " << endl; + mult(*(M.A), p, q); +#ifdef GMM_USES_MPI + tmult_tot += MPI_Wtime()-t_ref; + cout << "tmult_tot = " << tmult_tot << endl; +#endif + std::vector qbis(gmm::vect_size(q)); + std::vector qter(gmm::vect_size(q)); +#ifdef GMM_USES_MPI + // MPI_Status status; + // MPI_Request request,request1; + // int tag=111; + int size,tranche,borne_sup,borne_inf,rank; + size_type nb_sub=M.fi.size(); + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + tranche=nb_sub/size; + borne_inf=rank*tranche; + borne_sup=(rank+1)*tranche; + // if (rank==size-1) borne_sup=nb_sub; + // int next = (rank + 1) % size; + // int previous = (rank + size - 1) % size; + t_ref = MPI_Wtime(); + for (size_type i = size_type(borne_inf); i < size_type(borne_sup); ++i) +// for (size_type i = 0; i < nb_sub/size; ++i) + // for (size_type j = 0; j < nb_sub; ++j) +#else + for (size_type i = 0; i < M.fi.size(); ++i) +#endif + { +#ifdef GMM_USES_MPI + // size_type i=j; // (rank+size*(j-1)+nb_sub)%nb_sub; +#endif + gmm::mult(gmm::transposed((*(M.vB))[i]), q, M.fi[i]); + M.iter.init(); + AS_local_solve(local_solver(), (M.vAloc)[i], (M.gi)[i], + (M.fi)[i],(M.precond1)[i],M.iter); + itebilan = std::max(itebilan, M.iter.get_iteration()); + } + +#ifdef GMM_USES_MPI + cout << "First AS loop time " << MPI_Wtime() - t_ref << endl; +#endif + + gmm::clear(q); +#ifdef GMM_USES_MPI + t_ref = MPI_Wtime(); + // for (size_type j = 0; j < nb_sub; ++j) + for (size_type i = size_type(borne_inf); i < size_type(borne_sup); ++i) + +#else + for (size_type i = 0; i < M.gi.size(); ++i) +#endif + { + +#ifdef GMM_USES_MPI + // size_type i=j; // (rank+size*(j-1)+nb_sub)%nb_sub; +// gmm::mult((*(M.vB))[i], M.gi[i], qbis,qbis); + gmm::mult((*(M.vB))[i], M.gi[i], qter); + add(qter,qbis,qbis); +#else + gmm::mult((*(M.vB))[i], M.gi[i], q, q); +#endif + } +#ifdef GMM_USES_MPI + //WARNING this add only if you use the ring pattern below + // need to do this below if using a n explicit ring pattern communication + +// add(qbis,q,q); + cout << "Second AS loop time " << MPI_Wtime() - t_ref << endl; +#endif + + +#ifdef GMM_USES_MPI + // int tag1=11; + static double t_tot = 0.0; + double t_final; + t_ref=MPI_Wtime(); +// int next = (rank + 1) % size; +// int previous = (rank + size - 1) % size; + //communication of local information on ring pattern + //Each process receive Nproc-1 contributions + +// if (size > 1) { +// for (int nproc = 0; nproc < size-1; ++nproc) +// { + +// MPI_Sendrecv(&(qbis[0]), gmm::vect_size(q), MPI_DOUBLE, next, tag1, +// &(qter[0]), gmm::vect_size(q),MPI_DOUBLE,previous,tag1, +// MPI_COMM_WORLD,&status); +// gmm::copy(qter, qbis); +// add(qbis,q,q); +// } +// } + MPI_Allreduce(&(qbis[0]), &(q[0]),gmm::vect_size(q), MPI_DOUBLE, + MPI_SUM,MPI_COMM_WORLD); + t_final=MPI_Wtime(); + t_tot += t_final-t_ref; + cout<<"["<< rank<<"] temps reduce Resol "<< t_final-t_ref << " t_tot = " << t_tot << endl; +#endif + + if (M.iter.get_noisy() > 0) cout << "itebloc = " << itebilan << endl; + M.itebilan += itebilan; + M.iter.set_resmax((M.iter.get_resmax() + M.residual) * 0.5); + } + + template + void mult(const add_schwarz_mat &M, + const Vector2 &p, const Vector3 &q) { + mult(M, p, const_cast(q)); + } + + template + void mult(const add_schwarz_mat &M, + const Vector2 &p, const Vector3 &p2, Vector4 &q) + { mult(M, p, q); add(p2, q); } + + template + void mult(const add_schwarz_mat &M, + const Vector2 &p, const Vector3 &p2, const Vector4 &q) + { mult(M, p, const_cast(q)); add(p2, q); } + + /* ******************************************************************** */ + /* Additive Schwarz interfaced global solvers */ + /* ******************************************************************** */ + + template + void AS_global_solve(using_cg, const ASM_type &ASM, Vect &x, + const Vect &b, iteration &iter) + { cg(ASM, x, b, *(ASM.A), identity_matrix(), iter); } + + template + void AS_global_solve(using_gmres, const ASM_type &ASM, Vect &x, + const Vect &b, iteration &iter) + { gmres(ASM, x, b, identity_matrix(), 100, iter); } + + template + void AS_global_solve(using_bicgstab, const ASM_type &ASM, Vect &x, + const Vect &b, iteration &iter) + { bicgstab(ASM, x, b, identity_matrix(), iter); } + + template + void AS_global_solve(using_qmr,const ASM_type &ASM, Vect &x, + const Vect &b, iteration &iter) + { qmr(ASM, x, b, identity_matrix(), iter); } + +#if defined(GMM_USES_SUPERLU) + template + void AS_global_solve(using_superlu, const ASM_type &, Vect &, + const Vect &, iteration &) { + GMM_ASSERT1(false, "You cannot use SuperLU as " + "global solver in additive Schwarz meethod"); + } +#endif + + /* ******************************************************************** */ + /* Linear Additive Schwarz method */ + /* ******************************************************************** */ + /* ref : Domain decomposition algorithms for the p-version finite */ + /* element method for elliptic problems, Luca F. Pavarino, */ + /* PhD thesis, Courant Institute of Mathematical Sciences, 1992. */ + /* ******************************************************************** */ + + /** Function to call if the ASM matrix is precomputed for successive solve + * with the same system. + */ + template + void additive_schwarz( + add_schwarz_mat &ASM, Vector3 &u, + const Vector2 &f, iteration &iter, const global_solver&) { + + typedef typename linalg_traits::value_type value_type; + + size_type nb_sub = ASM.vB->size(), nb_dof = gmm::vect_size(f); + ASM.itebilan = 0; + std::vector g(nb_dof); + std::vector gbis(nb_dof); +#ifdef GMM_USES_MPI + double t_init=MPI_Wtime(); + int size,tranche,borne_sup,borne_inf,rank; + MPI_Comm_rank(MPI_COMM_WORLD, &rank); + MPI_Comm_size(MPI_COMM_WORLD, &size); + tranche=nb_sub/size; + borne_inf=rank*tranche; + borne_sup=(rank+1)*tranche; + // if (rank==size-1) borne_sup=nb_sub*size; + for (size_type i = size_type(borne_inf); i < size_type(borne_sup); ++i) +// for (size_type i = 0; i < nb_sub/size; ++i) + // for (size_type j = 0; j < nb_sub; ++j) + // for (size_type i = rank; i < nb_sub; i+=size) +#else + for (size_type i = 0; i < nb_sub; ++i) +#endif + { + +#ifdef GMM_USES_MPI + // size_type i=j; // (rank+size*(j-1)+nb_sub)%nb_sub; +#endif + gmm::mult(gmm::transposed((*(ASM.vB))[i]), f, ASM.fi[i]); + ASM.iter.init(); + AS_local_solve(local_solver(), ASM.vAloc[i], ASM.gi[i], ASM.fi[i], + ASM.precond1[i], ASM.iter); + ASM.itebilan = std::max(ASM.itebilan, ASM.iter.get_iteration()); +#ifdef GMM_USES_MPI + gmm::mult((*(ASM.vB))[i], ASM.gi[i], gbis,gbis); +#else + gmm::mult((*(ASM.vB))[i], ASM.gi[i], g, g); +#endif + } +#ifdef GMM_USES_MPI + cout<<"temps boucle init "<< MPI_Wtime()-t_init< + void additive_schwarz(const Matrix1 &A, Vector3 &u, + const Vector2 &f, const Precond &P, + const std::vector &vB, + iteration &iter, local_solver, + global_solver) { + iter.set_rhsnorm(vect_norm2(f)); + if (iter.get_rhsnorm() == 0.0) { gmm::clear(u); return; } + iteration iter2 = iter; iter2.reduce_noisy(); + iter2.set_maxiter(size_type(-1)); + add_schwarz_mat + ASM(A, vB, iter2, P, iter.get_resmax()); + additive_schwarz(ASM, u, f, iter, global_solver()); + } + + /* ******************************************************************** */ + /* Sequential Non-Linear Additive Schwarz method */ + /* ******************************************************************** */ + /* ref : Nonlinearly Preconditionned Inexact Newton Algorithms, */ + /* Xiao-Chuan Cai, David E. Keyes, */ + /* SIAM J. Sci. Comp. 24: p183-200. l */ + /* ******************************************************************** */ + + template + class NewtonAS_struct { + + public : + typedef Matrixt tangent_matrix_type; + typedef MatrixBi B_matrix_type; + typedef typename linalg_traits::value_type value_type; + typedef std::vector Vector; + + virtual size_type size(void) = 0; + virtual const std::vector &get_vB() = 0; + + virtual void compute_F(Vector &f, Vector &x) = 0; + virtual void compute_tangent_matrix(Matrixt &M, Vector &x) = 0; + // compute Bi^T grad(F(X)) Bi + virtual void compute_sub_tangent_matrix(Matrixt &Mloc, Vector &x, + size_type i) = 0; + // compute Bi^T F(X) + virtual void compute_sub_F(Vector &fi, Vector &x, size_type i) = 0; + + virtual ~NewtonAS_struct() {} + }; + + template + struct AS_exact_gradient { + const std::vector &vB; + std::vector vM; + std::vector vMloc; + + void init(void) { + for (size_type i = 0; i < vB.size(); ++i) { + Matrixt aux(gmm::mat_ncols(vB[i]), gmm::mat_ncols(vM[i])); + gmm::resize(vMloc[i], gmm::mat_ncols(vB[i]), gmm::mat_ncols(vB[i])); + gmm::mult(gmm::transposed(vB[i]), vM[i], aux); + gmm::mult(aux, vB[i], vMloc[i]); + } + } + AS_exact_gradient(const std::vector &vB_) : vB(vB_) { + vM.resize(vB.size()); vMloc.resize(vB.size()); + for (size_type i = 0; i < vB.size(); ++i) { + gmm::resize(vM[i], gmm::mat_nrows(vB[i]), gmm::mat_nrows(vB[i])); + } + } + }; + + template + void mult(const AS_exact_gradient &M, + const Vector2 &p, Vector3 &q) { + gmm::clear(q); + typedef typename gmm::linalg_traits::value_type T; + std::vector v(gmm::vect_size(p)), w, x; + for (size_type i = 0; i < M.vB.size(); ++i) { + w.resize(gmm::mat_ncols(M.vB[i])); + x.resize(gmm::mat_ncols(M.vB[i])); + gmm::mult(M.vM[i], p, v); + gmm::mult(gmm::transposed(M.vB[i]), v, w); + double rcond; + SuperLU_solve(M.vMloc[i], x, w, rcond); + // gmm::iteration iter(1E-10, 0, 100000); + //gmm::gmres(M.vMloc[i], x, w, gmm::identity_matrix(), 50, iter); + gmm::mult_add(M.vB[i], x, q); + } + } + + template + void mult(const AS_exact_gradient &M, + const Vector2 &p, const Vector3 &q) { + mult(M, p, const_cast(q)); + } + + template + void mult(const AS_exact_gradient &M, + const Vector2 &p, const Vector3 &p2, Vector4 &q) + { mult(M, p, q); add(p2, q); } + + template + void mult(const AS_exact_gradient &M, + const Vector2 &p, const Vector3 &p2, const Vector4 &q) + { mult(M, p, const_cast(q)); add(p2, q); } + + struct S_default_newton_line_search { + + double conv_alpha, conv_r; + size_t it, itmax, glob_it; + + double alpha, alpha_old, alpha_mult, first_res, alpha_max_ratio; + double alpha_min_ratio, alpha_min; + size_type count, count_pat; + bool max_ratio_reached; + double alpha_max_ratio_reached, r_max_ratio_reached; + size_type it_max_ratio_reached; + + + double converged_value(void) { return conv_alpha; }; + double converged_residual(void) { return conv_r; }; + + virtual void init_search(double r, size_t git, double = 0.0) { + alpha_min_ratio = 0.9; + alpha_min = 1e-10; + alpha_max_ratio = 10.0; + alpha_mult = 0.25; + itmax = size_type(-1); + glob_it = git; if (git <= 1) count_pat = 0; + conv_alpha = alpha = alpha_old = 1.; + conv_r = first_res = r; it = 0; + count = 0; + max_ratio_reached = false; + } + virtual double next_try(void) { + alpha_old = alpha; + if (alpha >= 0.4) alpha *= 0.5; else alpha *= alpha_mult; ++it; + return alpha_old; + } + virtual bool is_converged(double r, double = 0.0) { + // cout << "r = " << r << " alpha = " << alpha / alpha_mult << " count_pat = " << count_pat << endl; + if (!max_ratio_reached && r < first_res * alpha_max_ratio) { + alpha_max_ratio_reached = alpha_old; r_max_ratio_reached = r; + it_max_ratio_reached = it; max_ratio_reached = true; + } + if (max_ratio_reached && r < r_max_ratio_reached * 0.5 + && r > first_res * 1.1 && it <= it_max_ratio_reached+1) { + alpha_max_ratio_reached = alpha_old; r_max_ratio_reached = r; + it_max_ratio_reached = it; + } + if (count == 0 || r < conv_r) + { conv_r = r; conv_alpha = alpha_old; count = 1; } + if (conv_r < first_res) ++count; + + if (r < first_res * alpha_min_ratio) + { count_pat = 0; return true; } + if (count >= 5 || (alpha < alpha_min && max_ratio_reached)) { + if (conv_r < first_res * 0.99) count_pat = 0; + if (/*gmm::random() * 50. < -log(conv_alpha)-4.0 ||*/ count_pat >= 3) + { conv_r=r_max_ratio_reached; conv_alpha=alpha_max_ratio_reached; } + if (conv_r >= first_res * 0.9999) count_pat++; + return true; + } + return false; + } + S_default_newton_line_search(void) { count_pat = 0; } + }; + + + + template + void Newton_additive_Schwarz(NewtonAS_struct &NS, + const Vector &u_, + iteration &iter, const Precond &P, + local_solver, global_solver) { + Vector &u = const_cast(u_); + typedef typename linalg_traits::value_type value_type; + typedef typename number_traits::magnitude_type mtype; + typedef actual_precond chgt_precond; + + double residual = iter.get_resmax(); + + S_default_newton_line_search internal_ls; + S_default_newton_line_search external_ls; + + typename chgt_precond::APrecond PP = chgt_precond::transform(P); + iter.set_rhsnorm(mtype(1)); + iteration iternc(iter); + iternc.reduce_noisy(); iternc.set_maxiter(size_type(-1)); + iteration iter2(iternc); + iteration iter3(iter2); iter3.reduce_noisy(); + iteration iter4(iter3); + iternc.set_name("Local Newton"); + iter2.set_name("Linear System for Global Newton"); + iternc.set_resmax(residual/100.0); + iter3.set_resmax(residual/10000.0); + iter2.set_resmax(residual/1000.0); + iter4.set_resmax(residual/1000.0); + std::vector rhs(NS.size()), x(NS.size()), d(NS.size()); + std::vector xi, xii, fi, di; + + std::vector< std::vector > vx(NS.get_vB().size()); + for (size_type i = 0; i < NS.get_vB().size(); ++i) // for exact gradient + vx[i].resize(NS.size()); // for exact gradient + + Matrixt Mloc, M(NS.size(), NS.size()); + NS.compute_F(rhs, u); + mtype act_res=gmm::vect_norm2(rhs), act_res_new(0), precond_res = act_res; + mtype alpha; + + while(!iter.finished(std::min(act_res, precond_res))) { + for (int SOR_step = 0; SOR_step >= 0; --SOR_step) { + gmm::clear(rhs); + for (size_type isd = 0; isd < NS.get_vB().size(); ++isd) { + const MatrixBi &Bi = (NS.get_vB())[isd]; + size_type si = mat_ncols(Bi); + gmm::resize(Mloc, si, si); + xi.resize(si); xii.resize(si); fi.resize(si); di.resize(si); + + iternc.init(); + iternc.set_maxiter(30); // ? + if (iternc.get_noisy()) + cout << "Non-linear local problem " << isd << endl; + gmm::clear(xi); + gmm::copy(u, x); + NS.compute_sub_F(fi, x, isd); gmm::scale(fi, value_type(-1)); + mtype r = gmm::vect_norm2(fi), r_t(r); + if (r > value_type(0)) { + iternc.set_rhsnorm(std::max(r, mtype(1))); + while(!iternc.finished(r)) { + NS.compute_sub_tangent_matrix(Mloc, x, isd); + + PP.build_with(Mloc); + iter3.init(); + AS_local_solve(local_solver(), Mloc, di, fi, PP, iter3); + + internal_ls.init_search(r, iternc.get_iteration()); + do { + alpha = internal_ls.next_try(); + gmm::add(xi, gmm::scaled(di, -alpha), xii); + gmm::mult(Bi, gmm::scaled(xii, -1.0), u, x); + NS.compute_sub_F(fi, x, isd); gmm::scale(fi, value_type(-1)); + r_t = gmm::vect_norm2(fi); + } while (!internal_ls.is_converged(r_t)); + + if (alpha != internal_ls.converged_value()) { + alpha = internal_ls.converged_value(); + gmm::add(xi, gmm::scaled(di, -alpha), xii); + gmm::mult(Bi, gmm::scaled(xii, -1.0), u, x); + NS.compute_sub_F(fi, x, isd); gmm::scale(fi, value_type(-1)); + r_t = gmm::vect_norm2(fi); + } + gmm::copy(x, vx[isd]); // for exact gradient + + if (iternc.get_noisy()) cout << "(step=" << alpha << ")\t"; + ++iternc; r = r_t; gmm::copy(xii, xi); + } + if (SOR_step) gmm::mult(Bi, gmm::scaled(xii, -1.0), u, u); + gmm::mult(Bi, gmm::scaled(xii, -1.0), rhs, rhs); + } + } + precond_res = gmm::vect_norm2(rhs); + if (SOR_step) cout << "SOR step residual = " << precond_res << endl; + if (precond_res < residual) break; + cout << "Precond residual = " << precond_res << endl; + } + + iter2.init(); + // solving linear system for the global Newton method + if (0) { + NS.compute_tangent_matrix(M, u); + add_schwarz_mat + ASM(M, NS.get_vB(), iter4, P, iter.get_resmax()); + AS_global_solve(global_solver(), ASM, d, rhs, iter2); + } + else { // for exact gradient + AS_exact_gradient eg(NS.get_vB()); + for (size_type i = 0; i < NS.get_vB().size(); ++i) { + NS.compute_tangent_matrix(eg.vM[i], vx[i]); + } + eg.init(); + gmres(eg, d, rhs, gmm::identity_matrix(), 50, iter2); + } + + // gmm::add(gmm::scaled(rhs, 0.1), u); ++iter; + external_ls.init_search(act_res, iter.get_iteration()); + do { + alpha = external_ls.next_try(); + gmm::add(gmm::scaled(d, alpha), u, x); + NS.compute_F(rhs, x); + act_res_new = gmm::vect_norm2(rhs); + } while (!external_ls.is_converged(act_res_new)); + + if (alpha != external_ls.converged_value()) { + alpha = external_ls.converged_value(); + gmm::add(gmm::scaled(d, alpha), u, x); + NS.compute_F(rhs, x); + act_res_new = gmm::vect_norm2(rhs); + } + + if (iter.get_noisy() > 1) cout << endl; + act_res = act_res_new; + if (iter.get_noisy()) cout << "(step=" << alpha << ")\t unprecond res = " << act_res << " "; + + + ++iter; gmm::copy(x, u); + } + } + +} + + +#endif // GMM_SOLVERS_SCHWARZ_ADDITIVE_H__ diff --git a/src/linear_algebra/gmm/gmm_solver_bfgs.h b/src/linear_algebra/gmm/gmm_solver_bfgs.h new file mode 100644 index 00000000..d92abbca --- /dev/null +++ b/src/linear_algebra/gmm/gmm_solver_bfgs.h @@ -0,0 +1,210 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2004-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_solver_bfgs.h + @author Yves Renard + @date October 14 2004. + @brief Implements BFGS (Broyden, Fletcher, Goldfarb, Shanno) algorithm. + */ +#ifndef GMM_BFGS_H +#define GMM_BFGS_H + +#include "gmm_kernel.h" +#include "gmm_iter.h" + +namespace gmm { + + // BFGS algorithm (Broyden, Fletcher, Goldfarb, Shanno) + // Quasi Newton method for optimization problems. + // with Wolfe Line search. + + + // delta[k] = x[k+1] - x[k] + // gamma[k] = grad f(x[k+1]) - grad f(x[k]) + // H[0] = I + // BFGS : zeta[k] = delta[k] - H[k] gamma[k] + // DFP : zeta[k] = H[k] gamma[k] + // tau[k] = gamma[k]^T zeta[k] + // rho[k] = 1 / gamma[k]^T delta[k] + // BFGS : H[k+1] = H[k] + rho[k](zeta[k] delta[k]^T + delta[k] zeta[k]^T) + // - rho[k]^2 tau[k] delta[k] delta[k]^T + // DFP : H[k+1] = H[k] + rho[k] delta[k] delta[k]^T + // - (1/tau[k])zeta[k] zeta[k]^T + + // Object representing the inverse of the Hessian + template struct bfgs_invhessian { + + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + + std::vector delta, gamma, zeta; + std::vector tau, rho; + int version; + + template void hmult(const VEC1 &X, VEC2 &Y) { + copy(X, Y); + for (size_type k = 0 ; k < delta.size(); ++k) { + T xdelta = vect_sp(X, delta[k]), xzeta = vect_sp(X, zeta[k]); + switch (version) { + case 0 : // BFGS + add(scaled(zeta[k], rho[k]*xdelta), Y); + add(scaled(delta[k], rho[k]*(xzeta-rho[k]*tau[k]*xdelta)), Y); + break; + case 1 : // DFP + add(scaled(delta[k], rho[k]*xdelta), Y); + add(scaled(zeta[k], -xzeta/tau[k]), Y); + break; + } + } + } + + void restart(void) { + delta.resize(0); gamma.resize(0); zeta.resize(0); + tau.resize(0); rho.resize(0); + } + + template + void update(const VECT1 &deltak, const VECT2 &gammak) { + T vsp = vect_sp(deltak, gammak); + if (vsp == T(0)) return; + size_type N = vect_size(deltak), k = delta.size(); + VECTOR Y(N); + hmult(gammak, Y); + delta.resize(k+1); gamma.resize(k+1); zeta.resize(k+1); + tau.resize(k+1); rho.resize(k+1); + resize(delta[k], N); resize(gamma[k], N); resize(zeta[k], N); + gmm::copy(deltak, delta[k]); + gmm::copy(gammak, gamma[k]); + rho[k] = R(1) / vsp; + if (version == 0) + add(delta[k], scaled(Y, -1), zeta[k]); + else + gmm::copy(Y, zeta[k]); + tau[k] = vect_sp(gammak, zeta[k]); + } + + bfgs_invhessian(int v = 0) { version = v; } + }; + + + template + void bfgs(const FUNCTION &f, const DERIVATIVE &grad, VECTOR &x, + int restart, iteration& iter, int version = 0, + double lambda_init=0.001, double print_norm=1.0) { + + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + + bfgs_invhessian invhessian(version); + VECTOR r(vect_size(x)), d(vect_size(x)), y(vect_size(x)), r2(vect_size(x)); + grad(x, r); + R lambda = lambda_init, valx = f(x), valy; + int nb_restart(0); + + if (iter.get_noisy() >= 1) cout << "value " << valx / print_norm << " "; + while (! iter.finished_vect(r)) { + + invhessian.hmult(r, d); gmm::scale(d, T(-1)); + + // Wolfe Line search + R derivative = gmm::vect_sp(r, d); + R lambda_min(0), lambda_max(0), m1 = 0.27, m2 = 0.57; + bool unbounded = true, blocked = false, grad_computed = false; + + for(;;) { + add(x, scaled(d, lambda), y); + valy = f(y); + if (iter.get_noisy() >= 2) { + cout.precision(15); + cout << "Wolfe line search, lambda = " << lambda + << " value = " << valy /print_norm << endl; +// << " derivative = " << derivative +// << " lambda min = " << lambda_min << " lambda max = " +// << lambda_max << endl; getchar(); + } + if (valy <= valx + m1 * lambda * derivative) { + grad(y, r2); grad_computed = true; + T derivative2 = gmm::vect_sp(r2, d); + if (derivative2 >= m2*derivative) break; + lambda_min = lambda; + } + else { + lambda_max = lambda; + unbounded = false; + } + if (unbounded) lambda *= R(10); + else lambda = (lambda_max + lambda_min) / R(2); + if (lambda == lambda_max || lambda == lambda_min) break; + // valy <= R(2)*valx replaced by + // valy <= valx + gmm::abs(derivative)*lambda_init + // for compatibility with negative values (08.24.07). + if (valy <= valx + R(2)*gmm::abs(derivative)*lambda && + (lambda < R(lambda_init*1E-8) || + (!unbounded && lambda_max-lambda_min < R(lambda_init*1E-8)))) + { blocked = true; lambda = lambda_init; break; } + } + + // Rank two update + ++iter; + if (!grad_computed) grad(y, r2); + gmm::add(scaled(r2, -1), r); + if ((iter.get_iteration() % restart) == 0 || blocked) { + if (iter.get_noisy() >= 1) cout << "Restart\n"; + invhessian.restart(); + if (++nb_restart > 10) { + if (iter.get_noisy() >= 1) cout << "BFGS is blocked, exiting\n"; + return; + } + } + else { + invhessian.update(gmm::scaled(d,lambda), gmm::scaled(r,-1)); + nb_restart = 0; + } + copy(r2, r); copy(y, x); valx = valy; + if (iter.get_noisy() >= 1) + cout << "BFGS value " << valx/print_norm << "\t"; + } + + } + + + template + inline void dfp(const FUNCTION &f, const DERIVATIVE &grad, VECTOR &x, + int restart, iteration& iter, int version = 1) { + bfgs(f, grad, x, restart, iter, version); + + } + + +} + +#endif + diff --git a/src/linear_algebra/gmm/gmm_solver_bicgstab.h b/src/linear_algebra/gmm/gmm_solver_bicgstab.h new file mode 100644 index 00000000..7581847a --- /dev/null +++ b/src/linear_algebra/gmm/gmm_solver_bicgstab.h @@ -0,0 +1,160 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +// This file is a modified version of bicgstab.h from ITL. +// See http://osl.iu.edu/research/itl/ +// Following the corresponding Copyright notice. +//=========================================================================== +// +// Copyright (c) 1998-2020, University of Notre Dame. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the University of Notre Dame nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE TRUSTEES OF INDIANA UNIVERSITY AND +// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES +// OF INDIANA UNIVERSITY AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//=========================================================================== + +/**@file gmm_solver_bicgstab.h + @author Andrew Lumsdaine + @author Lie-Quan Lee + @author Yves Renard + @date October 13, 2002. + @brief BiCGStab iterative solver. +*/ + +#ifndef GMM_SOLVER_BICGSTAB_H__ +#define GMM_SOLVER_BICGSTAB_H__ + +#include "gmm_kernel.h" +#include "gmm_iter.h" + +namespace gmm { + + /* ******************************************************************** */ + /* BiConjugate Gradient Stabilized */ + /* (preconditionned, with parametrable scalar product) */ + /* ******************************************************************** */ + + template + void bicgstab(const Matrix& A, Vector& x, const VectorB& b, + const Preconditioner& M, iteration &iter) { + + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + typedef typename temporary_dense_vector::vector_type temp_vector; + + T rho_1, rho_2(0), alpha(0), beta, omega(0); + temp_vector p(vect_size(x)), phat(vect_size(x)), s(vect_size(x)), + shat(vect_size(x)), + t(vect_size(x)), v(vect_size(x)), r(vect_size(x)), rtilde(vect_size(x)); + + gmm::mult(A, gmm::scaled(x, -T(1)), b, r); + gmm::copy(r, rtilde); + R norm_r = gmm::vect_norm2(r); + iter.set_rhsnorm(gmm::vect_norm2(b)); + + if (iter.get_rhsnorm() == 0.0) { clear(x); return; } + + while (!iter.finished(norm_r)) { + + rho_1 = gmm::vect_sp(rtilde, r); + if (rho_1 == T(0)) { + if (iter.get_maxiter() == size_type(-1)) + { GMM_ASSERT1(false, "Bicgstab failed to converge"); } + else { GMM_WARNING1("Bicgstab failed to converge"); return; } + } + + if (iter.first()) + gmm::copy(r, p); + else { + if (omega == T(0)) { + if (iter.get_maxiter() == size_type(-1)) + { GMM_ASSERT1(false, "Bicgstab failed to converge"); } + else { GMM_WARNING1("Bicgstab failed to converge"); return; } + } + + beta = (rho_1 / rho_2) * (alpha / omega); + + gmm::add(gmm::scaled(v, -omega), p); + gmm::add(r, gmm::scaled(p, beta), p); + } + gmm::mult(M, p, phat); + gmm::mult(A, phat, v); + alpha = rho_1 / gmm::vect_sp(v, rtilde); + gmm::add(r, gmm::scaled(v, -alpha), s); + + if (iter.finished_vect(s)) + { gmm::add(gmm::scaled(phat, alpha), x); break; } + + gmm::mult(M, s, shat); + gmm::mult(A, shat, t); + omega = gmm::vect_sp(t, s) / gmm::vect_norm2_sqr(t); + + gmm::add(gmm::scaled(phat, alpha), x); + gmm::add(gmm::scaled(shat, omega), x); + gmm::add(s, gmm::scaled(t, -omega), r); + norm_r = gmm::vect_norm2(r); + rho_2 = rho_1; + + ++iter; + } + } + + template + void bicgstab(const Matrix& A, const Vector& x, const VectorB& b, + const Preconditioner& M, iteration &iter) + { bicgstab(A, linalg_const_cast(x), b, M, iter); } + +} + + +#endif // GMM_SOLVER_BICGSTAB_H__ diff --git a/src/linear_algebra/gmm/gmm_solver_cg.h b/src/linear_algebra/gmm/gmm_solver_cg.h new file mode 100644 index 00000000..c81a6e9e --- /dev/null +++ b/src/linear_algebra/gmm/gmm_solver_cg.h @@ -0,0 +1,180 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +// This file is a modified version of cg.h from ITL. +// See http://osl.iu.edu/research/itl/ +// Following the corresponding Copyright notice. +//=========================================================================== +// +// Copyright (c) 1998-2020, University of Notre Dame. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the University of Notre Dame nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE TRUSTEES OF INDIANA UNIVERSITY AND +// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES +// OF INDIANA UNIVERSITY AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//=========================================================================== + +/**@file gmm_solver_cg.h + @author Andrew Lumsdaine + @author Lie-Quan Lee + @author Yves Renard + @date October 13, 2002. + @brief Conjugate gradient iterative solver. +*/ +#ifndef GMM_SOLVER_CG_H__ +#define GMM_SOLVER_CG_H__ + +#include "gmm_kernel.h" +#include "gmm_iter.h" + +namespace gmm { + + /* ******************************************************************** */ + /* conjugate gradient */ + /* (preconditionned, with parametrable additional scalar product) */ + /* ******************************************************************** */ + + template + void cg(const Matrix& A, Vector1& x, const Vector2& b, const Matps& PS, + const Precond &P, iteration &iter) { + + typedef typename temporary_dense_vector::vector_type temp_vector; + typedef typename linalg_traits::value_type T; + + T rho, rho_1(0), a; + temp_vector p(vect_size(x)), q(vect_size(x)), r(vect_size(x)), + z(vect_size(x)); + iter.set_rhsnorm(gmm::sqrt(gmm::abs(vect_hp(PS, b, b)))); + + if (iter.get_rhsnorm() == 0.0) + clear(x); + else { + mult(A, scaled(x, T(-1)), b, r); + mult(P, r, z); + rho = vect_hp(PS, z, r); + copy(z, p); + + while (!iter.finished_vect(r)) { + + if (!iter.first()) { + mult(P, r, z); + rho = vect_hp(PS, z, r); + add(z, scaled(p, rho / rho_1), p); + } + mult(A, p, q); + + a = rho / vect_hp(PS, q, p); + add(scaled(p, a), x); + add(scaled(q, -a), r); + rho_1 = rho; + + ++iter; + } + } + } + + template + void cg(const Matrix& A, Vector1& x, const Vector2& b, const Matps& PS, + const gmm::identity_matrix &, iteration &iter) { + + typedef typename temporary_dense_vector::vector_type temp_vector; + typedef typename linalg_traits::value_type T; + + T rho, rho_1(0), a; + temp_vector p(vect_size(x)), q(vect_size(x)), r(vect_size(x)); + iter.set_rhsnorm(gmm::sqrt(gmm::abs(vect_hp(PS, b, b)))); + + if (iter.get_rhsnorm() == 0.0) + clear(x); + else { + mult(A, scaled(x, T(-1)), b, r); + rho = vect_hp(PS, r, r); + copy(r, p); + + while (!iter.finished_vect(r)) { + + if (!iter.first()) { + rho = vect_hp(PS, r, r); + add(r, scaled(p, rho / rho_1), p); + } + mult(A, p, q); + a = rho / vect_hp(PS, q, p); + add(scaled(p, a), x); + add(scaled(q, -a), r); + rho_1 = rho; + ++iter; + } + } + } + + template inline + void cg(const Matrix& A, const Vector1& x, const Vector2& b, const Matps& PS, + const Precond &P, iteration &iter) + { cg(A, linalg_const_cast(x), b, PS, P, iter); } + + template inline + void cg(const Matrix& A, Vector1& x, const Vector2& b, + const Precond &P, iteration &iter) + { cg(A, x , b, identity_matrix(), P, iter); } + + template inline + void cg(const Matrix& A, const Vector1& x, const Vector2& b, + const Precond &P, iteration &iter) + { cg(A, x , b , identity_matrix(), P , iter); } + +} + + +#endif // GMM_SOLVER_CG_H__ diff --git a/src/linear_algebra/gmm/gmm_solver_constrained_cg.h b/src/linear_algebra/gmm/gmm_solver_constrained_cg.h new file mode 100644 index 00000000..c599bc94 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_solver_constrained_cg.h @@ -0,0 +1,165 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_solver_constrained_cg.h + @author Yves Renard + @date October 13, 2002. + @brief Constrained conjugate gradient. */ +// preconditionning does not work + +#ifndef GMM_SOLVER_CCG_H__ +#define GMM_SOLVER_CCG_H__ + +#include "gmm_kernel.h" +#include "gmm_iter.h" + +namespace gmm { + + template + void pseudo_inverse(const CMatrix &C, CINVMatrix &CINV, + const Matps& /* PS */, VectorX&) { + // compute the pseudo inverse of the non-square matrix C such + // CINV = inv(C * trans(C)) * C. + // based on a conjugate gradient method. + + // optimisable : copie de la ligne, precalcul de C * trans(C). + + typedef VectorX TmpVec; + typedef typename linalg_traits::value_type value_type; + + size_type nr = mat_nrows(C), nc = mat_ncols(C); + + TmpVec d(nr), e(nr), l(nc), p(nr), q(nr), r(nr); + value_type rho, rho_1, alpha; + clear(d); + clear(CINV); + + for (size_type i = 0; i < nr; ++i) { + d[i] = 1.0; rho = 1.0; + clear(e); + copy(d, r); + copy(d, p); + + while (rho >= 1E-38) { /* conjugate gradient to compute e */ + /* which is the i nd row of inv(C * trans(C)) */ + mult(gmm::transposed(C), p, l); + mult(C, l, q); + alpha = rho / vect_sp(p, q); + add(scaled(p, alpha), e); + add(scaled(q, -alpha), r); + rho_1 = rho; + rho = vect_sp(r, r); + add(r, scaled(p, rho / rho_1), p); + } + + mult(transposed(C), e, l); /* l is the i nd row of CINV */ + // cout << "l = " << l << endl; + clean(l, 1E-15); + copy(l, mat_row(CINV, i)); + + d[i] = 0.0; + } + } + + /** Compute the minimum of @f$ 1/2((Ax).x) - bx @f$ under the contraint @f$ Cx <= f @f$ */ + template < typename Matrix, typename CMatrix, typename Matps, + typename VectorX, typename VectorB, typename VectorF, + typename Preconditioner > + void constrained_cg(const Matrix& A, const CMatrix& C, VectorX& x, + const VectorB& b, const VectorF& f,const Matps& PS, + const Preconditioner& M, iteration &iter) { + typedef typename temporary_dense_vector::vector_type TmpVec; + typedef typename temporary_vector::vector_type TmpCVec; + typedef row_matrix TmpCmat; + + typedef typename linalg_traits::value_type value_type; + value_type rho = 1.0, rho_1, lambda, gamma; + TmpVec p(vect_size(x)), q(vect_size(x)), q2(vect_size(x)), + r(vect_size(x)), old_z(vect_size(x)), z(vect_size(x)), + memox(vect_size(x)); + std::vector satured(mat_nrows(C)); + clear(p); + iter.set_rhsnorm(sqrt(vect_sp(PS, b, b))); + if (iter.get_rhsnorm() == 0.0) iter.set_rhsnorm(1.0); + + TmpCmat CINV(mat_nrows(C), mat_ncols(C)); + pseudo_inverse(C, CINV, PS, x); + + while(true) { + // computation of residu + copy(z, old_z); + copy(x, memox); + mult(A, scaled(x, -1.0), b, r); + mult(M, r, z); // preconditionner not coherent + bool transition = false; + for (size_type i = 0; i < mat_nrows(C); ++i) { + value_type al = vect_sp(mat_row(C, i), x) - f[i]; + if (al >= -1.0E-15) { + if (!satured[i]) { satured[i] = true; transition = true; } + value_type bb = vect_sp(mat_row(CINV, i), z); + if (bb > 0.0) add(scaled(mat_row(C, i), -bb), z); + } + else + satured[i] = false; + } + + // descent direction + rho_1 = rho; rho = vect_sp(PS, r, z); // ... + + if (iter.finished(rho)) break; + + if (iter.get_noisy() > 0 && transition) std::cout << "transition\n"; + if (transition || iter.first()) gamma = 0.0; + else gamma = std::max(0.0, (rho - vect_sp(PS, old_z, z) ) / rho_1); + // std::cout << "gamma = " << gamma << endl; + // itl::add(r, itl::scaled(p, gamma), p); + add(z, scaled(p, gamma), p); // ... + + ++iter; + // one dimensional optimization + mult(A, p, q); + lambda = rho / vect_sp(PS, q, p); + for (size_type i = 0; i < mat_nrows(C); ++i) + if (!satured[i]) { + value_type bb = vect_sp(mat_row(C, i), p) - f[i]; + if (bb > 0.0) + lambda = std::min(lambda, (f[i]-vect_sp(mat_row(C, i), x)) / bb); + } + add(x, scaled(p, lambda), x); + add(memox, scaled(x, -1.0), memox); + + } + } + +} + +#endif // GMM_SOLVER_CCG_H__ diff --git a/src/linear_algebra/gmm/gmm_solver_gmres.h b/src/linear_algebra/gmm/gmm_solver_gmres.h new file mode 100644 index 00000000..96f81158 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_solver_gmres.h @@ -0,0 +1,173 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +// This file is a modified version of gmres.h from ITL. +// See http://osl.iu.edu/research/itl/ +// Following the corresponding Copyright notice. +//=========================================================================== +// +// Copyright (c) 1998-2020, University of Notre Dame. All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the University of Notre Dame nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE TRUSTEES OF INDIANA UNIVERSITY AND +// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES +// OF INDIANA UNIVERSITY AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//=========================================================================== + +/**@file gmm_solver_gmres.h + @author Andrew Lumsdaine + @author Lie-Quan Lee + @author Yves Renard + @date October 13, 2002. + @brief GMRES (Generalized Minimum Residual) iterative solver. +*/ +#ifndef GMM_KRYLOV_GMRES_H +#define GMM_KRYLOV_GMRES_H + +#include "gmm_kernel.h" +#include "gmm_iter.h" +#include "gmm_modified_gram_schmidt.h" + +namespace gmm { + + /** Generalized Minimum Residual + + This solve the unsymmetric linear system Ax = b using restarted GMRES. + + See: Y. Saad and M. Schulter. GMRES: A generalized minimum residual + algorithm for solving nonsysmmetric linear systems, SIAM + J. Sci. Statist. Comp. 7(1986), pp, 856-869 + */ + template + void gmres(const Mat &A, Vec &x, const VecB &b, const Precond &M, + int restart, iteration &outer, Basis& KS) { + + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + + std::vector w(vect_size(x)), r(vect_size(x)), u(vect_size(x)); + std::vector c_rot(restart+1), s_rot(restart+1), s(restart+1); + gmm::dense_matrix H(restart+1, restart); +#ifdef GMM_USES_MPI + double t_ref, t_prec = MPI_Wtime(), t_tot = 0; + static double tmult_tot = 0.0; +t_ref = MPI_Wtime(); + cout << "GMRES " << endl; +#endif + mult(M,b,r); + outer.set_rhsnorm(gmm::vect_norm2(r)); + if (outer.get_rhsnorm() == 0.0) { clear(x); return; } + + mult(A, scaled(x, T(-1)), b, w); + mult(M, w, r); + R beta = gmm::vect_norm2(r), beta_old = beta; + int blocked = 0; + + iteration inner = outer; + inner.reduce_noisy(); + inner.set_maxiter(restart); + inner.set_name("GMRes inner"); + + while (! outer.finished(beta)) { + + gmm::copy(gmm::scaled(r, R(1)/beta), KS[0]); + gmm::clear(s); + s[0] = beta; + + size_type i = 0; inner.init(); + + do { + mult(A, KS[i], u); + mult(M, u, KS[i+1]); + orthogonalize(KS, mat_col(H, i), i); + R a = gmm::vect_norm2(KS[i+1]); + H(i+1, i) = T(a); + gmm::scale(KS[i+1], T(1) / a); + for (size_type k = 0; k < i; ++k) + Apply_Givens_rotation_left(H(k,i), H(k+1,i), c_rot[k], s_rot[k]); + + Givens_rotation(H(i,i), H(i+1,i), c_rot[i], s_rot[i]); + Apply_Givens_rotation_left(H(i,i), H(i+1,i), c_rot[i], s_rot[i]); + Apply_Givens_rotation_left(s[i], s[i+1], c_rot[i], s_rot[i]); + + ++inner, ++outer, ++i; + } while (! inner.finished(gmm::abs(s[i]))); + + upper_tri_solve(H, s, i, false); + combine(KS, s, x, i); + mult(A, gmm::scaled(x, T(-1)), b, w); + mult(M, w, r); + beta_old = std::min(beta, beta_old); beta = gmm::vect_norm2(r); + if (int(inner.get_iteration()) < restart -1 || beta_old <= beta) + ++blocked; else blocked = 0; + if (blocked > 10) { + if (outer.get_noisy()) cout << "Gmres is blocked, exiting\n"; + break; + } +#ifdef GMM_USES_MPI + t_tot = MPI_Wtime() - t_ref; + cout << "temps GMRES : " << t_tot << endl; +#endif + } + } + + + template + void gmres(const Mat &A, Vec &x, const VecB &b, + const Precond &M, int restart, iteration& outer) { + typedef typename linalg_traits::value_type T; + modified_gram_schmidt orth(restart, vect_size(x)); + gmres(A, x, b, M, restart, outer, orth); + } + +} + +#endif diff --git a/src/linear_algebra/gmm/gmm_solver_idgmres.h b/src/linear_algebra/gmm/gmm_solver_idgmres.h new file mode 100644 index 00000000..d6cc0460 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_solver_idgmres.h @@ -0,0 +1,805 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2003-2020 Yves Renard, Caroline Lecalvez + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_solver_idgmres.h + @author Caroline Lecalvez + @author Yves Renard + @date October 6, 2003. + @brief Implicitly restarted and deflated Generalized Minimum Residual. +*/ +#ifndef GMM_IDGMRES_H +#define GMM_IDGMRES_H + +#include "gmm_kernel.h" +#include "gmm_iter.h" +#include "gmm_dense_sylvester.h" + +namespace gmm { + + template compare_vp { + bool operator()(const std::pair &a, + const std::pair &b) const + { return (gmm::abs(a.first) > gmm::abs(b.first)); } + } + + struct idgmres_state { + size_type m, tb_deb, tb_def, p, k, nb_want, nb_unwant; + size_type nb_nolong, tb_deftot, tb_defwant, conv, nb_un, fin; + bool ok; + + idgmres_state(size_type mm, size_type pp, size_type kk) + : m(mm), tb_deb(1), tb_def(0), p(pp), k(kk), nb_want(0), + nb_unwant(0), nb_nolong(0), tb_deftot(0), tb_defwant(0), + conv(0), nb_un(0), fin(0), ok(false); {} + } + + idgmres_state(size_type mm, size_type pp, size_type kk) + : m(mm), tb_deb(1), tb_def(0), p(pp), k(kk), nb_want(0), + nb_unwant(0), nb_nolong(0), tb_deftot(0), tb_defwant(0), + conv(0), nb_un(0), fin(0), ok(false); {} + + + template + apply_permutation(CONT &cont, const IND &ind) { + size_type m = ind.end() - ind.begin(); + std::vector sorted(m, false); + + for (size_type l = 0; l < m; ++l) + if (!sorted[l] && ind[l] != l) { + + typeid(cont[0]) aux = cont[l]; + k = ind[l]; + cont[l] = cont[k]; + sorted[l] = true; + + for(k2 = ind[k]; k2 != l; k2 = ind[k]) { + cont[k] = cont[k2]; + sorted[k] = true; + k = k2; + } + cont[k] = aux; + } + } + + + /** Implicitly restarted and deflated Generalized Minimum Residual + + See: C. Le Calvez, B. Molina, Implicitly restarted and deflated + FOM and GMRES, numerical applied mathematics, + (30) 2-3 (1999) pp191-212. + + @param A Real or complex unsymmetric matrix. + @param x initial guess vector and final result. + @param b right hand side + @param M preconditionner + @param m size of the subspace between two restarts + @param p number of converged ritz values seeked + @param k size of the remaining Krylov subspace when the p ritz values + have not yet converged 0 <= p <= k < m. + @param tol_vp : tolerance on the ritz values. + @param outer + @param KS + */ + template < typename Mat, typename Vec, typename VecB, typename Precond, + typename Basis > + void idgmres(const Mat &A, Vec &x, const VecB &b, const Precond &M, + size_type m, size_type p, size_type k, double tol_vp, + iteration &outer, Basis& KS) { + + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + + R a, beta; + idgmres_state st(m, p, k); + + std::vector w(vect_size(x)), r(vect_size(x)), u(vect_size(x)); + std::vector c_rot(m+1), s_rot(m+1), s(m+1); + std::vector y(m+1), ztest(m+1), gam(m+1); + std::vector gamma(m+1); + gmm::dense_matrix H(m+1, m), Hess(m+1, m), + Hobl(m+1, m), W(vect_size(x), m+1); + + gmm::clear(H); + + outer.set_rhsnorm(gmm::vect_norm2(b)); + if (outer.get_rhsnorm() == 0.0) { clear(x); return; } + + mult(A, scaled(x, -1.0), b, w); + mult(M, w, r); + beta = gmm::vect_norm2(r); + + iteration inner = outer; + inner.reduce_noisy(); + inner.set_maxiter(m); + inner.set_name("GMRes inner iter"); + + while (! outer.finished(beta)) { + + gmm::copy(gmm::scaled(r, 1.0/beta), KS[0]); + gmm::clear(s); + s[0] = beta; + gmm::copy(s, gamma); + + inner.set_maxiter(m - st.tb_deb + 1); + size_type i = st.tb_deb - 1; inner.init(); + + do { + mult(A, KS[i], u); + mult(M, u, KS[i+1]); + orthogonalize_with_refinment(KS, mat_col(H, i), i); + H(i+1, i) = a = gmm::vect_norm2(KS[i+1]); + gmm::scale(KS[i+1], R(1) / a); + + gmm::copy(mat_col(H, i), mat_col(Hess, i)); + gmm::copy(mat_col(H, i), mat_col(Hobl, i)); + + + for (size_type l = 0; l < i; ++l) + Apply_Givens_rotation_left(H(l,i), H(l+1,i), c_rot[l], s_rot[l]); + + Givens_rotation(H(i,i), H(i+1,i), c_rot[i], s_rot[i]); + Apply_Givens_rotation_left(H(i,i), H(i+1,i), c_rot[i], s_rot[i]); + H(i+1, i) = T(0); + Apply_Givens_rotation_left(s[i], s[i+1], c_rot[i], s_rot[i]); + + ++inner, ++outer, ++i; + } while (! inner.finished(gmm::abs(s[i]))); + + if (inner.converged()) { + gmm::copy(s, y); + upper_tri_solve(H, y, i, false); + combine(KS, y, x, i); + mult(A, gmm::scaled(x, T(-1)), b, w); + mult(M, w, r); + beta = gmm::vect_norm2(r); // + verif sur beta ... à faire + break; + } + + gmm::clear(gam); gam[m] = s[i]; + for (size_type l = m; l > 0; --l) + Apply_Givens_rotation_left(gam[l-1], gam[l], gmm::conj(c_rot[l-1]), + -s_rot[l-1]); + + mult(KS.mat(), gam, r); + beta = gmm::vect_norm2(r); + + mult(Hess, scaled(y, T(-1)), gamma, ztest); + // En fait, d'après Caroline qui s'y connait ztest et gam devrait + // être confondus + // Quand on aura vérifié que ça marche, il faudra utiliser gam à la + // place de ztest. + if (st.tb_def < p) { + T nss = H(m,m-1) / ztest[m]; + nss /= gmm::abs(nss); // ns à calculer plus tard aussi + gmm::copy(KS.mat(), W); gmm::copy(scaled(r, nss /beta), mat_col(W, m)); + + // Computation of the oblique matrix + sub_interval SUBI(0, m); + add(scaled(sub_vector(ztest, SUBI), -Hobl(m, m-1) / ztest[m]), + sub_vector(mat_col(Hobl, m-1), SUBI)); + Hobl(m, m-1) *= nss * beta / ztest[m]; + + /* **************************************************************** */ + /* Locking */ + /* **************************************************************** */ + + // Computation of the Ritz eigenpairs. + std::vector > eval(m); + dense_matrix YB(m-st.tb_def, m-st.tb_def); + std::vector pure(m-st.tb_def, 0); + gmm::clear(YB); + + select_eval(Hobl, eval, YB, pure, st); + + if (st.conv != 0) { + // DEFLATION using the QR Factorization of YB + + T alpha = Lock(W, Hobl, + sub_matrix(YB, sub_interval(0, m-st.tb_def)), + sub_interval(st.tb_def, m-st.tb_def), + (st.tb_defwant < p)); + // ns *= alpha; // à calculer plus tard ?? + // V(:,m+1) = alpha*V(:, m+1); ça devait servir à qlq chose ... + + + // Clean the portions below the diagonal corresponding + // to the lock Schur vectors + + for (size_type j = st.tb_def; j < st.tb_deftot; ++j) { + if ( pure[j-st.tb_def] == 0) + gmm::clear(sub_vector(mat_col(Hobl,j), sub_interval(j+1,m-j))); + else if (pure[j-st.tb_def] == 1) { + gmm::clear(sub_matrix(Hobl, sub_interval(j+2,m-j-1), + sub_interval(j, 2))); + ++j; + } + else GMM_ASSERT3(false, "internal error"); + } + + if (!st.ok) { + + // attention si m = 0; + size_type mm = std::min(k+st.nb_unwant+st.nb_nolong, m-1); + + if (eval_sort[m-mm-1].second != R(0) + && eval_sort[m-mm-1].second == -eval_sort[m-mm].second) ++mm; + + std::vector > shifts(m-mm); + for (size_type i = 0; i < m-mm; ++i) + shifts[i] = eval_sort[i].second; + + apply_shift_to_Arnoldi_factorization(W, Hobl, shifts, mm, + m-mm, true); + + st.fin = mm; + } + else + st.fin = st.tb_deftot; + + + /* ************************************************************** */ + /* Purge */ + /* ************************************************************** */ + + if (st.nb_nolong + st.nb_unwant > 0) { + + std::vector > eval(m); + dense_matrix YB(st.fin, st.tb_deftot); + std::vector pure(st.tb_deftot, 0); + gmm::clear(YB); + st.nb_un = st.nb_nolong + st.nb_unwant; + + select_eval_for_purging(Hobl, eval, YB, pure, st); + + T alpha = Lock(W, Hobl, YB, sub_interval(0, st.fin), ok); + + // Clean the portions below the diagonal corresponding + // to the unwanted lock Schur vectors + + for (size_type j = 0; j < st.tb_deftot; ++j) { + if ( pure[j] == 0) + gmm::clear(sub_vector(mat_col(Hobl,j), sub_interval(j+1,m-j))); + else if (pure[j] == 1) { + gmm::clear(sub_matrix(Hobl, sub_interval(j+2,m-j-1), + sub_interval(j, 2))); + ++j; + } + else GMM_ASSERT3(false, "internal error"); + } + + gmm::dense_matrix z(st.nb_un, st.fin - st.nb_un); + sub_interval SUBI(0, st.nb_un), SUBJ(st.nb_un, st.fin - st.nb_un); + sylvester(sub_matrix(Hobl, SUBI), + sub_matrix(Hobl, SUBJ), + sub_matrix(gmm::scaled(Hobl, -T(1)), SUBI, SUBJ), z); + + } + + } + + } + } + } + + + template < typename Mat, typename Vec, typename VecB, typename Precond > + void idgmres(const Mat &A, Vec &x, const VecB &b, + const Precond &M, size_type m, iteration& outer) { + typedef typename linalg_traits::value_type T; + modified_gram_schmidt orth(m, vect_size(x)); + gmres(A, x, b, M, m, outer, orth); + } + + + // Lock stage of an implicit restarted Arnoldi process. + // 1- QR factorization of YB through Householder matrices + // Q(Rl) = YB + // (0 ) + // 2- Update of the Arnoldi factorization. + // H <- Q*HQ, W <- WQ + // 3- Restore the Hessemberg form of H. + + template + void Lock(gmm::dense_matrix &W, gmm::dense_matrix &H, + const MATYB &YB, const sub_interval SUB, + bool restore, T &ns) { + + size_type n = mat_nrows(W), m = mat_ncols(W) - 1; + size_type ncols = mat_ncols(YB), nrows = mat_nrows(YB); + size_type begin = min(SUB); end = max(SUB) - 1; + sub_interval SUBR(0, nrows), SUBC(0, ncols); + T alpha(1); + + GMM_ASSERT2(((end-begin) == ncols) && (m == mat_nrows(H)) + && (m+1 == mat_ncols(H)), "dimensions mismatch"); + + // DEFLATION using the QR Factorization of YB + + dense_matrix QR(n_rows, n_rows); + gmmm::copy(YB, sub_matrix(QR, SUBR, SUBC)); + gmm::clear(submatrix(QR, SUBR, sub_interval(ncols, nrows-ncols))); + qr_factor(QR); + + + apply_house_left(QR, sub_matrix(H, SUB)); + apply_house_right(QR, sub_matrix(H, SUBR, SUB)); + apply_house_right(QR, sub_matrix(W, sub_interval(0, n), SUB)); + + // Restore to the initial block hessenberg form + + if (restore) { + + // verifier quand m = 0 ... + gmm::dense_matrix tab_p(end - st.tb_deftot, end - st.tb_deftot); + gmm::copy(identity_matrix(), tab_p); + + for (size_type j = end-1; j >= st.tb_deftot+2; --j) { + + size_type jm = j-1; + std::vector v(jm - st.tb_deftot); + sub_interval SUBtot(st.tb_deftot, jm - st.tb_deftot); + sub_interval SUBtot2(st.tb_deftot, end - st.tb_deftot); + gmm::copy(sub_vector(mat_row(H, j), SUBtot), v); + house_vector_last(v); + w.resize(end); + col_house_update(sub_matrix(H, SUBI, SUBtot), v, w); + w.resize(end - st.tb_deftot); + row_house_update(sub_matrix(H, SUBtot, SUBtot2), v, w); + gmm::clear(sub_vector(mat_row(H, j), + sub_interval(st.tb_deftot, j-1-st.tb_deftot))); + w.resize(end - st.tb_deftot); + col_house_update(sub_matrix(tab_p, sub_interval(0, end-st.tb_deftot), + sub_interval(0, jm-st.tb_deftot)), v, w); + w.resize(n); + col_house_update(sub_matrix(W, sub_interval(0, n), SUBtot), v, w); + } + + // restore positive subdiagonal elements + + std::vector d(fin-st.tb_deftot); d[0] = T(1); + + // We compute d[i+1] in order + // (d[i+1] * H(st.tb_deftot+i+1,st.tb_deftoti)) / d[i] + // be equal to |H(st.tb_deftot+i+1,st.tb_deftot+i))|. + for (size_type j = 0; j+1 < end-st.tb_deftot; ++j) { + T e = H(st.tb_deftot+j, st.tb_deftot+j-1); + d[j+1] = (e == T(0)) ? T(1) : d[j] * gmm::abs(e) / e; + scale(sub_vector(mat_row(H, st.tb_deftot+j+1), + sub_interval(st.tb_deftot, m-st.tb_deftot)), d[j+1]); + scale(mat_col(H, st.tb_deftot+j+1), T(1) / d[j+1]); + scale(mat_col(W, st.tb_deftot+j+1), T(1) / d[j+1]); + } + + alpha = tab_p(end-st.tb_deftot-1, end-st.tb_deftot-1) / d[end-st.tb_deftot-1]; + alpha /= gmm::abs(alpha); + scale(mat_col(W, m), alpha); + + } + + return alpha; + } + + + + + + + + + // Apply p implicit shifts to the Arnoldi factorization + // AV = VH+H(k+p+1,k+p) V(:,k+p+1) e_{k+p}* + // and produces the following new Arnoldi factorization + // A(VQ) = (VQ)(Q*HQ)+H(k+p+1,k+p) V(:,k+p+1) e_{k+p}* Q + // where only the first k columns are relevant. + // + // Dan Sorensen and Richard J. Radke, 11/95 + template + apply_shift_to_Arnoldi_factorization(dense_matrix V, dense_matrix H, + std::vector Lambda, size_type &k, + size_type p, bool true_shift = false) { + + + size_type k1 = 0, num = 0, kend = k+p, kp1 = k + 1; + bool mark = false; + T c, s, x, y, z; + + dense_matrix q(1, kend); + gmm::clear(q); q(0,kend-1) = T(1); + std::vector hv(3), w(std::max(kend, mat_nrows(V))); + + for(size_type jj = 0; jj < p; ++jj) { + // compute and apply a bulge chase sweep initiated by the + // implicit shift held in w(jj) + + if (abs(Lambda[jj].real()) == 0.0) { + // apply a real shift using 2 by 2 Givens rotations + + for (size_type k1 = 0, k2 = 0; k2 != kend-1; k1 = k2+1) { + k2 = k1; + while (h(k2+1, k2) != T(0) && k2 < kend-1) ++k2; + + Givens_rotation(H(k1, k1) - Lambda[jj], H(k1+1, k1), c, s); + + for (i = k1; i <= k2; ++i) { + if (i > k1) Givens_rotation(H(i, i-1), H(i+1, i-1), c, s); + + // Ne pas oublier de nettoyer H(i+1,i-1) (le mettre à zéro). + // Vérifier qu'au final H(i+1,i) est bien un réel positif. + + // apply rotation from left to rows of H + row_rot(sub_matrix(H, sub_interval(i,2), sub_interval(i, kend-i)), + c, s, 0, 0); + + // apply rotation from right to columns of H + size_type ip2 = std::min(i+2, kend); + col_rot(sub_matrix(H, sub_interval(0, ip2), sub_interval(i, 2)) + c, s, 0, 0); + + // apply rotation from right to columns of V + col_rot(V, c, s, i, i+1); + + // accumulate e' Q so residual can be updated k+p + Apply_Givens_rotation_left(q(0,i), q(0,i+1), c, s); + // peut être que nous utilisons G au lieu de G* et que + // nous allons trop loin en k2. + } + } + + num = num + 1; + } + else { + + // Apply a double complex shift using 3 by 3 Householder + // transformations + + if (jj == p || mark) + mark = false; // skip application of conjugate shift + else { + num = num + 2; // mark that a complex conjugate + mark = true; // pair has been applied + + // Indices de fin de boucle à surveiller... de près ! + for (size_type k1 = 0, k3 = 0; k3 != kend-2; k1 = k3+1) { + k3 = k1; + while (h(k3+1, k3) != T(0) && k3 < kend-2) ++k3; + size_type k2 = k1+1; + + + x = H(k1,k1) * H(k1,k1) + H(k1,k2) * H(k2,k1) + - 2.0*Lambda[jj].real() * H(k1,k1) + gmm::abs_sqr(Lambda[jj]); + y = H(k2,k1) * (H(k1,k1) + H(k2,k2) - 2.0*Lambda[jj].real()); + z = H(k2+1,k2) * H(k2,k1); + + for (size_type i = k1; i <= k3; ++i) { + if (i > k1) { + x = H(i, i-1); + y = H(i+1, i-1); + z = H(i+2, i-1); + // Ne pas oublier de nettoyer H(i+1,i-1) et H(i+2,i-1) + // (les mettre à zéro). + } + + hv[0] = x; hv[1] = y; hv[2] = z; + house_vector(v); + + // Vérifier qu'au final H(i+1,i) est bien un réel positif + + // apply transformation from left to rows of H + w.resize(kend-i); + row_house_update(sub_matrix(H, sub_interval(i, 2), + sub_interval(i, kend-i)), v, w); + + // apply transformation from right to columns of H + + size_type ip3 = std::min(kend, i + 3); + w.resize(ip3); + col_house_update(sub_matrix(H, sub_interval(0, ip3), + sub_interval(i, 2)), v, w); + + // apply transformation from right to columns of V + + w.resize(mat_nrows(V)); + col_house_update(sub_matrix(V, sub_interval(0, mat_nrows(V)), + sub_interval(i, 2)), v, w); + + // accumulate e' Q so residual can be updated k+p + + w.resize(1); + col_house_update(sub_matrix(q, sub_interval(0,1), + sub_interval(i,2)), v, w); + + } + } + + // clean up step with Givens rotation + + i = kend-2; + c = x; s = y; + if (i > k1) Givens_rotation(H(i, i-1), H(i+1, i-1), c, s); + + // Ne pas oublier de nettoyer H(i+1,i-1) (le mettre à zéro). + // Vérifier qu'au final H(i+1,i) est bien un réel positif. + + // apply rotation from left to rows of H + row_rot(sub_matrix(H, sub_interval(i,2), sub_interval(i, kend-i)), + c, s, 0, 0); + + // apply rotation from right to columns of H + size_type ip2 = std::min(i+2, kend); + col_rot(sub_matrix(H, sub_interval(0, ip2), sub_interval(i, 2)) + c, s, 0, 0); + + // apply rotation from right to columns of V + col_rot(V, c, s, i, i+1); + + // accumulate e' Q so residual can be updated k+p + Apply_Givens_rotation_left(q(0,i), q(0,i+1), c, s); + + } + } + } + + // update residual and store in the k+1 -st column of v + + k = kend - num; + scale(mat_col(V, kend), q(0, k)); + + if (k < mat_nrows(H)) { + if (true_shift) + gmm::copy(mat_col(V, kend), mat_col(V, k)); + else + // v(:,k+1) = v(:,kend+1) + v(:,k+1)*h(k+1,k); + // v(:,k+1) = v(:,kend+1) ; + gmm::add(scaled(mat_col(V, kend), H(kend, kend-1)), + scaled(mat_col(V, k), H(k, k-1)), mat_col(V, k)); + } + + H(k, k-1) = vect_norm2(mat_col(V, k)); + scale(mat_col(V, kend), T(1) / H(k, k-1)); + } + + + + template + void select_eval(const MAT &Hobl, EVAL &eval, MAT &YB, PURE &pure, + idgmres_state &st) { + + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + size_type m = st.m; + + // Computation of the Ritz eigenpairs. + + col_matrix< std::vector > evect(m-st.tb_def, m-st.tb_def); + // std::vector > eval(m); + std::vector ritznew(m, T(-1)); + + // dense_matrix evect_lock(st.tb_def, st.tb_def); + + sub_interval SUB1(st.tb_def, m-st.tb_def); + implicit_qr_algorithm(sub_matrix(Hobl, SUB1), + sub_vector(eval, SUB1), evect); + sub_interval SUB2(0, st.tb_def); + implicit_qr_algorithm(sub_matrix(Hobl, SUB2), + sub_vector(eval, SUB2), /* evect_lock */); + + for (size_type l = st.tb_def; l < m; ++l) + ritznew[l] = gmm::abs(evect(m-st.tb_def-1, l-st.tb_def) * Hobl(m, m-1)); + + std::vector< std::pair > eval_sort(m); + for (size_type l = 0; l < m; ++l) + eval_sort[l] = std::pair(eval[l], l); + std::sort(eval_sort.begin(), eval_sort.end(), compare_vp()); + + std::vector index(m); + for (size_type l = 0; l < m; ++l) index[l] = eval_sort[l].second; + + std::vector kept(m, false); + std::fill(kept.begin(), kept.begin()+st.tb_def, true); + + apply_permutation(eval, index); + apply_permutation(evect, index); + apply_permutation(ritznew, index); + apply_permutation(kept, index); + + // Which are the eigenvalues that converged ? + // + // nb_want is the number of eigenvalues of + // Hess(tb_def+1:n,tb_def+1:n) that converged and are WANTED + // + // nb_unwant is the number of eigenvalues of + // Hess(tb_def+1:n,tb_def+1:n) that converged and are UNWANTED + // + // nb_nolong is the number of eigenvalues of + // Hess(1:tb_def,1:tb_def) that are NO LONGER WANTED. + // + // tb_deftot is the number of the deflated eigenvalues + // that is tb_def + nb_want + nb_unwant + // + // tb_defwant is the number of the wanted deflated eigenvalues + // that is tb_def + nb_want - nb_nolong + + st.nb_want = 0, st.nb_unwant = 0, st.nb_nolong = 0; + size_type j, ind; + + for (j = 0, ind = 0; j < m-p; ++j) { + if (ritznew[j] == R(-1)) { + if (std::imag(eval[j]) != R(0)) { + st.nb_nolong += 2; ++j; // à adapter dans le cas complexe ... + } + else st.nb_nolong++; + } + else { + if (ritznew[j] + < tol_vp * gmm::abs(eval[j])) { + + for (size_type l = 0, l < m-st.tb_def; ++l) + YB(l, ind) = std::real(evect(l, j)); + kept[j] = true; + ++j; ++st.nb_unwant; ind++; + + if (std::imag(eval[j]) != R(0)) { + for (size_type l = 0, l < m-st.tb_def; ++l) + YB(l, ind) = std::imag(evect(l, j)); + pure[ind-1] = 1; + pure[ind] = 2; + + kept[j] = true; + + st.nb_unwant++; + ++ind; + } + } + } + } + + + for (; j < m; ++j) { + if (ritznew[j] != R(-1)) { + + for (size_type l = 0, l < m-st.tb_def; ++l) + YB(l, ind) = std::real(evect(l, j)); + pure[ind] = 1; + ++ind; + kept[j] = true; + ++st.nb_want; + + if (ritznew[j] + < tol_vp * gmm::abs(eval[j])) { + for (size_type l = 0, l < m-st.tb_def; ++l) + YB(l, ind) = std::imag(evect(l, j)); + pure[ind] = 2; + + j++; + kept[j] = true; + + st.nb_want++; + ++ind; + } + } + } + + std::vector shift(m - st.tb_def - st.nb_want - st.nb_unwant); + for (size_type j = 0, i = 0; j < m; ++j) + if (!kept[j]) shift[i++] = eval[j]; + + // st.conv (st.nb_want+st.nb_unwant) is the number of eigenpairs that + // have just converged. + // st.tb_deftot is the total number of eigenpairs that have converged. + + size_type st.conv = ind; + size_type st.tb_deftot = st.tb_def + st.conv; + size_type st.tb_defwant = st.tb_def + st.nb_want - st.nb_nolong; + + sub_interval SUBYB(0, st.conv); + + if ( st.tb_defwant >= p ) { // An invariant subspace has been found. + + st.nb_unwant = 0; + st.nb_want = p + st.nb_nolong - st.tb_def; + st.tb_defwant = p; + + if ( pure[st.conv - st.nb_want + 1] == 2 ) { + ++st.nb_want; st.tb_defwant = ++p;// il faudrait que ce soit un p local + } + + SUBYB = sub_interval(st.conv - st.nb_want, st.nb_want); + // YB = YB(:, st.conv-st.nb_want+1 : st.conv); // On laisse en suspend .. + // pure = pure(st.conv-st.nb_want+1 : st.conv,1); // On laisse suspend .. + st.conv = st.nb_want; + st.tb_deftot = st.tb_def + st.conv; + st.ok = true; + } + + } + + + + template + void select_eval_for_purging(const MAT &Hobl, EVAL &eval, MAT &YB, + PURE &pure, idgmres_state &st) { + + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + size_type m = st.m; + + // Computation of the Ritz eigenpairs. + + col_matrix< std::vector > evect(st.tb_deftot, st.tb_deftot); + + sub_interval SUB1(0, st.tb_deftot); + implicit_qr_algorithm(sub_matrix(Hobl, SUB1), + sub_vector(eval, SUB1), evect); + std::fill(eval.begin() + st.tb_deftot, eval.end(), std::complex(0)); + + std::vector< std::pair > eval_sort(m); + for (size_type l = 0; l < m; ++l) + eval_sort[l] = std::pair(eval[l], l); + std::sort(eval_sort.begin(), eval_sort.end(), compare_vp()); + + std::vector sorted(m); + std::fill(sorted.begin(), sorted.end(), false); + + std::vector ind(m); + for (size_type l = 0; l < m; ++l) ind[l] = eval_sort[l].second; + + std::vector kept(m, false); + std::fill(kept.begin(), kept.begin()+st.tb_def, true); + + apply_permutation(eval, ind); + apply_permutation(evect, ind); + + size_type j; + for (j = 0; j < st.tb_deftot; ++j) { + + for (size_type l = 0, l < st.tb_deftot; ++l) + YB(l, j) = std::real(evect(l, j)); + + if (std::imag(eval[j]) != R(0)) { + for (size_type l = 0, l < m-st.tb_def; ++l) + YB(l, j+1) = std::imag(evect(l, j)); + pure[j] = 1; + pure[j+1] = 2; + + j += 2; + } + else ++j; + } + } + + + + + + +} + +#endif diff --git a/src/linear_algebra/gmm/gmm_solver_qmr.h b/src/linear_algebra/gmm/gmm_solver_qmr.h new file mode 100644 index 00000000..9ff0ddce --- /dev/null +++ b/src/linear_algebra/gmm/gmm_solver_qmr.h @@ -0,0 +1,210 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +// This file is a modified version of qmr.h from ITL. +// See http://osl.iu.edu/research/itl/ +// Following the corresponding Copyright notice. +//=========================================================================== +// +// Copyright (c) 1997-2020, The Trustees of Indiana University. +// All rights reserved. +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above copyright +// notice, this list of conditions and the following disclaimer in the +// documentation and/or other materials provided with the distribution. +// * Neither the name of the University of Notre Dame nor the +// names of its contributors may be used to endorse or promote products +// derived from this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE TRUSTEES OF INDIANA UNIVERSITY AND +// CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, +// BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +// FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE TRUSTEES +// OF INDIANA UNIVERSITY AND CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +// INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT +// NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF +// THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +//=========================================================================== + +/**@file gmm_solver_qmr.h + @author Andrew Lumsdaine + @author Lie-Quan Lee + @author Yves Renard + @date October 13, 2002. + @brief Quasi-Minimal Residual iterative solver. +*/ +#ifndef GMM_QMR_H +#define GMM_QMR_H + +#include "gmm_kernel.h" +#include "gmm_iter.h" + +namespace gmm { + + /** Quasi-Minimal Residual. + + This routine solves the unsymmetric linear system Ax = b using + the Quasi-Minimal Residual method. + + See: R. W. Freund and N. M. Nachtigal, A quasi-minimal residual + method for non-Hermitian linear systems, Numerical Math., + 60(1991), pp. 315-339 + + Preconditioner - Incomplete LU, Incomplete LU with threshold, + SSOR or identity_preconditioner. + */ + template + void qmr(const Matrix &A, Vector &x, const VectorB &b, const Precond1 &M1, + iteration& iter) { + + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + + T delta(0), ep(0), beta(0), theta_1(0), gamma_1(0); + T theta(0), gamma(1), eta(-1); + R rho_1(0), rho, xi; + + typedef typename temporary_vector::vector_type TmpVec; + size_type nn = vect_size(x); + TmpVec r(nn), v_tld(nn), y(nn), w_tld(nn), z(nn), v(nn), w(nn); + TmpVec y_tld(nn), z_tld(nn), p(nn), q(nn), p_tld(nn), d(nn), s(nn); + + iter.set_rhsnorm(double(gmm::vect_norm2(b))); + if (iter.get_rhsnorm() == 0.0) { clear(x); return; } + + gmm::mult(A, gmm::scaled(x, T(-1)), b, r); + gmm::copy(r, v_tld); + + gmm::left_mult(M1, v_tld, y); + rho = gmm::vect_norm2(y); + + gmm::copy(r, w_tld); + gmm::transposed_right_mult(M1, w_tld, z); + xi = gmm::vect_norm2(z); + + while (! iter.finished_vect(r)) { + + if (rho == R(0) || xi == R(0)) { + if (iter.get_maxiter() == size_type(-1)) + { GMM_ASSERT1(false, "QMR failed to converge"); } + else { GMM_WARNING1("QMR failed to converge"); return; } + } + gmm::copy(gmm::scaled(v_tld, T(R(1)/rho)), v); + gmm::scale(y, T(R(1)/rho)); + + gmm::copy(gmm::scaled(w_tld, T(R(1)/xi)), w); + gmm::scale(z, T(R(1)/xi)); + + delta = gmm::vect_sp(z, y); + if (delta == T(0)) { + if (iter.get_maxiter() == size_type(-1)) + { GMM_ASSERT1(false, "QMR failed to converge"); } + else { GMM_WARNING1("QMR failed to converge"); return; } + } + gmm::right_mult(M1, y, y_tld); + gmm::transposed_left_mult(M1, z, z_tld); + + if (iter.first()) { + gmm::copy(y_tld, p); + gmm::copy(z_tld, q); + } else { + gmm::add(y_tld, gmm::scaled(p, -(T(xi * delta) / ep)), p); + gmm::add(z_tld, gmm::scaled(q, -(T(rho * delta) / ep)), q); + } + + gmm::mult(A, p, p_tld); + + ep = gmm::vect_sp(q, p_tld); + if (ep == T(0)) { + if (iter.get_maxiter() == size_type(-1)) + { GMM_ASSERT1(false, "QMR failed to converge"); } + else { GMM_WARNING1("QMR failed to converge"); return; } + } + beta = ep / delta; + if (beta == T(0)) { + if (iter.get_maxiter() == size_type(-1)) + { GMM_ASSERT1(false, "QMR failed to converge"); } + else { GMM_WARNING1("QMR failed to converge"); return; } + } + gmm::add(p_tld, gmm::scaled(v, -beta), v_tld); + gmm::left_mult(M1, v_tld, y); + + rho_1 = rho; + rho = gmm::vect_norm2(y); + + gmm::mult(gmm::transposed(A), q, w_tld); + gmm::add(w_tld, gmm::scaled(w, -beta), w_tld); + gmm::transposed_right_mult(M1, w_tld, z); + + xi = gmm::vect_norm2(z); + + gamma_1 = gamma; + theta_1 = theta; + + theta = rho / (gamma_1 * beta); + gamma = T(1) / gmm::sqrt(T(1) + gmm::sqr(theta)); + + if (gamma == T(0)) { + if (iter.get_maxiter() == size_type(-1)) + { GMM_ASSERT1(false, "QMR failed to converge"); } + else { GMM_WARNING1("QMR failed to converge"); return; } + } + eta = -eta * T(rho_1) * gmm::sqr(gamma) / (beta * gmm::sqr(gamma_1)); + + if (iter.first()) { + gmm::copy(gmm::scaled(p, eta), d); + gmm::copy(gmm::scaled(p_tld, eta), s); + } else { + T tmp = gmm::sqr(theta_1 * gamma); + gmm::add(gmm::scaled(p, eta), gmm::scaled(d, tmp), d); + gmm::add(gmm::scaled(p_tld, eta), gmm::scaled(s, tmp), s); + } + gmm::add(d, x); + gmm::add(gmm::scaled(s, T(-1)), r); + + ++iter; + } + } + + +} + +#endif + diff --git a/src/linear_algebra/gmm/gmm_std.h b/src/linear_algebra/gmm/gmm_std.h new file mode 100644 index 00000000..413df776 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_std.h @@ -0,0 +1,352 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_std.h +@author Yves Renard , +@author Julien Pommier +@date June 01, 1995. +@brief basic setup for gmm (includes, typedefs etc.) +*/ +#ifndef GMM_STD_H__ +#define GMM_STD_H__ + +#ifndef __USE_STD_IOSTREAM +# define __USE_STD_IOSTREAM +#endif + +#ifndef __USE_BSD +# define __USE_BSD +#endif + +#ifndef __USE_ISOC99 +# define __USE_ISOC99 +#endif + +#if defined(_MSC_VER) && _MSC_VER >= 1400 // Secure versions for VC++ +# define GMM_SECURE_CRT +# define SECURE_NONCHAR_SSCANF sscanf_s +# define SECURE_NONCHAR_FSCANF fscanf_s +# define SECURE_STRNCPY(a, la, b, lb) strncpy_s(a, la, b, lb) +# define SECURE_FOPEN(F, filename, mode) (*(F) = 0, fopen_s(F, filename, mode)) +# define SECURE_SPRINTF1(S, l, st, p1) sprintf_s(S, l, st, p1) +# define SECURE_SPRINTF2(S, l, st, p1, p2) sprintf_s(S, l, st, p1, p2) +# define SECURE_SPRINTF4(S, l, st, p1, p2, p3, p4) sprintf_s(S, l, st, p1, p2, p3, p4) +# define SECURE_STRDUP(s) _strdup(s) +# ifndef _SCL_SECURE_NO_DEPRECATE +# error Add the option /D_SCL_SECURE_NO_DEPRECATE to the compilation command +# endif +#else +# define SECURE_NONCHAR_SSCANF sscanf +# define SECURE_NONCHAR_FSCANF fscanf +# define SECURE_STRNCPY(a, la, b, lb) strncpy(a, b, lb) +# define SECURE_FOPEN(F, filename, mode) ((*(F)) = fopen(filename, mode)) +# define SECURE_SPRINTF1(S, l, st, p1) sprintf(S, st, p1) +# define SECURE_SPRINTF2(S, l, st, p1, p2) sprintf(S, st, p1, p2) +# define SECURE_SPRINTF4(S, l, st, p1, p2, p3, p4) sprintf(S, st, p1, p2, p3, p4) +# define SECURE_STRDUP(s) strdup(s) +#endif + +inline void GMM_NOPERATION_(int) { } +#define GMM_NOPERATION(a) { GMM_NOPERATION_(abs(&(a) != &(a))); } + +/* ********************************************************************** */ +/* Compilers detection. */ +/* ********************************************************************** */ + +/* for VISUAL C++ ... +#if defined(_MSC_VER) // && !defined(__MWERKS__) +#define _GETFEM_MSVCPP_ _MSC_VER +#endif +*/ + +#if defined(__GNUC__) +# if (__GNUC__ < 4) +# error : PLEASE UPDATE g++ TO AT LEAST 4.8 VERSION +# endif +#endif + +/* ********************************************************************** */ +/* C++ Standard Headers. */ +/* ********************************************************************** */ +#include +#include +#include +#include +#include +#include +#include +#include +#include +//#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +namespace std { +#if defined(__GNUC__) && (__cplusplus <= 201103L) + template + struct _MakeUniq + { typedef unique_ptr<_Tp> __single_object; }; + template + struct _MakeUniq<_Tp[]> + { typedef unique_ptr<_Tp[]> __array; }; + template + struct _MakeUniq<_Tp[_Bound]> + { struct __invalid_type { }; }; + /// std::make_unique for single objects + template + inline typename _MakeUniq<_Tp>::__single_object + make_unique(_Args&&... __args) + { return unique_ptr<_Tp>(new _Tp(std::forward<_Args>(__args)...)); } + /// std::make_unique for arrays of unknown bound + template + inline typename _MakeUniq<_Tp>::__array + make_unique(size_t __num) + { return unique_ptr<_Tp>(new typename remove_extent<_Tp>::type[__num]()); } + /// Disable std::make_unique for arrays of known bound + template + inline typename _MakeUniq<_Tp>::__invalid_type + make_unique(_Args&&...) = delete; +#endif + + + // Should simply be replaced by std::shared_ptr when it will be supported + // by the STL + template class shared_array_ptr : shared_ptr { + public: + shared_array_ptr() {} + shared_array_ptr(T *q) : std::shared_ptr(q, default_delete()) {} + template shared_array_ptr(const std::shared_ptr &p, T *q) + : std::shared_ptr(p, q) {} + T *get() const { return shared_ptr::get(); } + T& operator*() const { return shared_ptr::operator*(); } + T* operator->() const { return shared_ptr::operator->(); } + }; + + template shared_array_ptr make_shared_array(size_t num) + { return shared_array_ptr(new T[num]); } +} + +namespace gmm { + + using std::endl; using std::cout; using std::cerr; + using std::ends; using std::cin; using std::isnan; + + class standard_locale { + std::string cloc; + std::locale cinloc; + + public : + inline standard_locale(void) + : cloc(setlocale(LC_NUMERIC, 0)), cinloc(cin.getloc()) + { setlocale(LC_NUMERIC,"C"); cin.imbue(std::locale("C")); } + inline ~standard_locale() + { setlocale(LC_NUMERIC, cloc.c_str()); cin.imbue(cinloc); } + }; + + class stream_standard_locale { + std::locale cloc; + std::ios &io; + + public : + inline stream_standard_locale(std::ios &i) + : cloc(i.getloc()), io(i) { io.imbue(std::locale("C")); } + inline ~stream_standard_locale() { io.imbue(cloc); } + }; + + /* ******************************************************************* */ + /* Clock functions. */ + /* ******************************************************************* */ + +# if defined(HAVE_SYS_TIMES) + inline double uclock_sec(void) { + static double ttclk = 0.; + if (ttclk == 0.) ttclk = sysconf(_SC_CLK_TCK); + tms t; times(&t); return double(t.tms_utime) / ttclk; + } +# else + inline double uclock_sec(void) + { return double(clock())/double(CLOCKS_PER_SEC); } +# endif + + /* ******************************************************************** */ + /* Fixed size integer types. */ + /* ******************************************************************** */ + // Remark : the test program dynamic_array tests the length of + // resulting integers + + template struct fixed_size_integer_generator { + typedef void int_base_type; + typedef void uint_base_type; + }; + + template <> struct fixed_size_integer_generator { + typedef signed char int_base_type; + typedef unsigned char uint_base_type; + }; + + template <> struct fixed_size_integer_generator { + typedef signed short int int_base_type; + typedef unsigned short int uint_base_type; +}; + +template <> struct fixed_size_integer_generator { + typedef signed int int_base_type; + typedef unsigned int uint_base_type; + }; + +template <> struct fixed_size_integer_generator { + typedef signed long int_base_type; + typedef unsigned long uint_base_type; + }; + +template <> struct fixed_size_integer_generator { + typedef signed long long int_base_type; + typedef unsigned long long uint_base_type; + }; + +typedef fixed_size_integer_generator<1>::int_base_type int8_type; +typedef fixed_size_integer_generator<1>::uint_base_type uint8_type; +typedef fixed_size_integer_generator<2>::int_base_type int16_type; +typedef fixed_size_integer_generator<2>::uint_base_type uint16_type; +typedef fixed_size_integer_generator<4>::int_base_type int32_type; +typedef fixed_size_integer_generator<4>::uint_base_type uint32_type; +typedef fixed_size_integer_generator<8>::int_base_type int64_type; +typedef fixed_size_integer_generator<8>::uint_base_type uint64_type; + +// #if INT_MAX == 32767 +// typedef signed int int16_type; +// typedef unsigned int uint16_type; +// #elif SHRT_MAX == 32767 +// typedef signed short int int16_type; +// typedef unsigned short int uint16_type; +// #else +// # error "impossible to build a 16 bits integer" +// #endif + +// #if INT_MAX == 2147483647 +// typedef signed int int32_type; +// typedef unsigned int uint32_type; +// #elif SHRT_MAX == 2147483647 +// typedef signed short int int32_type; +// typedef unsigned short int uint32_type; +// #elif LONG_MAX == 2147483647 +// typedef signed long int int32_type; +// typedef unsigned long int uint32_type; +// #else +// # error "impossible to build a 32 bits integer" +// #endif + +// #if INT_MAX == 9223372036854775807L || INT_MAX == 9223372036854775807 +// typedef signed int int64_type; +// typedef unsigned int uint64_type; +// #elif LONG_MAX == 9223372036854775807L || LONG_MAX == 9223372036854775807 +// typedef signed long int int64_type; +// typedef unsigned long int uint64_type; +// #elif LLONG_MAX == 9223372036854775807LL || LLONG_MAX == 9223372036854775807L || LLONG_MAX == 9223372036854775807 +// typedef signed long long int int64_type; +// typedef unsigned long long int uint64_type; +// #else +// # error "impossible to build a 64 bits integer" +// #endif + +#if defined(__GNUC__) && !defined(__ICC) +/* + g++ can issue a warning at each usage of a function declared with this special attribute + (also works with typedefs and variable declarations) +*/ +# define IS_DEPRECATED __attribute__ ((__deprecated__)) +/* + the specified function is inlined at any optimization level +*/ +# define ALWAYS_INLINE __attribute__((always_inline)) +#else +# define IS_DEPRECATED +# define ALWAYS_INLINE +#endif + +} + + /* ******************************************************************** */ + /* Import/export classes and interfaces from a shared library */ + /* ******************************************************************** */ + +#if defined(EXPORTED_TO_SHARED_LIB) +# if defined(_MSC_VER) || defined(__INTEL_COMPILER) +# define APIDECL __declspec(dllexport) +# elif defined(__GNUC__) +# define __attribute__((visibility("default"))) +# else +# define APIDECL +# endif +# if defined(IMPORTED_FROM_SHARED_LIB) +# error INTENTIONAL COMPILCATION ERROR, DLL IMPORT AND EXPORT ARE INCOMPITABLE +# endif +#endif + +#if defined(IMPORTED_FROM_SHARED_LIB) +# if defined(_MSC_VER) || defined(__INTEL_COMPILER) +# define APIDECL __declspec(dllimport) +# else +# define APIDECL +# endif +# if defined(EXPORTED_TO_SHARED_LIB) +# error INTENTIONAL COMPILCATION ERROR, DLL IMPORT AND EXPORT ARE INCOMPITABLE +# endif +#endif + +#ifndef EXPORTED_TO_SHARED_LIB +# ifndef IMPORTED_FROM_SHARED_LIB +# define APIDECL //empty, used during static linking +# endif +#endif + +#endif /* GMM_STD_H__ */ diff --git a/src/linear_algebra/gmm/gmm_sub_index.h b/src/linear_algebra/gmm/gmm_sub_index.h new file mode 100644 index 00000000..28c95e0d --- /dev/null +++ b/src/linear_algebra/gmm/gmm_sub_index.h @@ -0,0 +1,224 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_sub_index.h + @author Yves Renard + @date October 13, 2002. + @brief sub-indices. +*/ + +#ifndef GMM_SUB_INDEX_H__ +#define GMM_SUB_INDEX_H__ + +#include "gmm_def.h" + +namespace gmm { + + /* ******************************************************************** */ + /* sub indices */ + /* ******************************************************************** */ + + struct basic_index : public std::vector { + + mutable size_type nb_ref; + // size_type key1; faire la somme des composantes + // const basic_index *rind; rindex s'il existe + + + size_t operator[](size_type i) const { + return (i < size()) ? std::vector::operator[](i) : size_type(-1); + } + + basic_index() : nb_ref(1) {} + basic_index(size_type j) : std::vector(j), nb_ref(1) {} + template basic_index(IT b, IT e) + : std::vector(e-b), nb_ref(1) { std::copy(b, e, begin()); } + basic_index(const basic_index *pbi) : nb_ref(1) { + const_iterator it = pbi->begin(), ite = pbi->end(); + size_type i = 0; + for ( ; it != ite; ++it) i = std::max(i, *it); + resize(i+1); std::fill(begin(), end(), size_type(-1)); + for (it = pbi->begin(), i = 0; it != ite; ++it, ++i) + std::vector::operator[](*it) = i; + } + void swap(size_type i, size_type j) { + std::swap(std::vector::operator[](i), + std::vector::operator[](j)); + } + + }; + + typedef basic_index *pbasic_index; + + struct index_generator { + + template static pbasic_index create_index(IT begin, IT end) + { return new basic_index(begin, end); } + static pbasic_index create_rindex(pbasic_index pbi) + { return new basic_index(pbi); } + static void attach(pbasic_index pbi) { if (pbi) pbi->nb_ref++; } + static void unattach(pbasic_index pbi) + { if (pbi && --(pbi->nb_ref) == 0) delete pbi; } + + }; + + struct sub_index { + + size_type first_, last_; + typedef basic_index base_type; + typedef base_type::const_iterator const_iterator; + + mutable pbasic_index ind; + mutable pbasic_index rind; + + void comp_extr(void) { + std::vector::const_iterator it = ind->begin(), ite = ind->end(); + if (it != ite) { first_=last_= *it; ++it; } else { first_=last_= 0; } + for (; it != ite; ++it) + { first_ = std::min(first_, *it); last_ = std::max(last_, *it); } + } + + inline void test_rind(void) const + { if (!rind) rind = index_generator::create_rindex(ind); } + size_type size(void) const { return ind->size(); } + size_type first(void) const { return first_; } + size_type last(void) const { return last_; } + size_type index(size_type i) const { return (*ind)[i]; } + size_type rindex(size_type i) const { + test_rind(); + if (i < rind->size()) return (*rind)[i]; else return size_type(-1); + } + + const_iterator begin(void) const { return ind->begin(); } + const_iterator end(void) const { return ind->end(); } + const_iterator rbegin(void) const { test_rind(); return rind->begin(); } + const_iterator rend(void) const { test_rind(); return rind->end(); } + + sub_index() : ind(0), rind(0) {} + template sub_index(IT it, IT ite) + : ind(index_generator::create_index(it, ite)), + rind(0) { comp_extr(); } + template sub_index(const CONT &c) + : ind(index_generator::create_index(c.begin(), c.end())), + rind(0) { comp_extr(); } + ~sub_index() { + index_generator::unattach(rind); + index_generator::unattach(ind); + } + sub_index(const sub_index &si) : first_(si.first_), last_(si.last_), + ind(si.ind), rind(si.rind) + { index_generator::attach(rind); index_generator::attach(ind); } + sub_index &operator =(const sub_index &si) { + index_generator::unattach(rind); + index_generator::unattach(ind); + ind = si.ind; rind = si.rind; + index_generator::attach(rind); + index_generator::attach(ind); + first_ = si.first_; last_ = si.last_; + return *this; + } + }; + + struct unsorted_sub_index : public sub_index { + typedef basic_index base_type; + typedef base_type::const_iterator const_iterator; + + template unsorted_sub_index(IT it, IT ite) + : sub_index(it, ite) {} + template unsorted_sub_index(const CONT &c) + : sub_index(c) {} + unsorted_sub_index() {} + unsorted_sub_index(const unsorted_sub_index &si) : sub_index((const sub_index &)(si)) { } + unsorted_sub_index &operator =(const unsorted_sub_index &si) + { sub_index::operator =(si); return *this; } + void swap(size_type i, size_type j) { + GMM_ASSERT2(ind->nb_ref <= 1, "Operation not allowed on this index"); + if (rind) rind->swap((*ind)[i], (*ind)[j]); + ind->swap(i, j); + } + }; + + inline std::ostream &operator << (std::ostream &o, const sub_index &si) { + o << "sub_index("; + if (si.size() != 0) o << si.index(0); + for (size_type i = 1; i < si.size(); ++i) o << ", " << si.index(i); + o << ")"; + return o; + } + + struct sub_interval { + size_type min, max; + + size_type size(void) const { return max - min; } + size_type first(void) const { return min; } + size_type last(void) const { return max; } + size_type index(size_type i) const { return min + i; } + size_type step(void) const { return 1; } + size_type rindex(size_type i) const + { if (i >= min && i < max) return i - min; return size_type(-1); } + sub_interval(size_type mi, size_type l) : min(mi), max(mi+l) {} + sub_interval() {} + }; + + inline std::ostream &operator << (std::ostream &o, const sub_interval &si) + { o << "sub_interval(" << si.min << ", " << si.size() << ")"; return o; } + + struct sub_slice { + size_type min, max, N; + + size_type size(void) const { return (max - min) / N; } + size_type first(void) const { return min; } + size_type last(void) const { return (min == max) ? max : max+1-N; } + size_type step(void) const { return N; } + size_type index(size_type i) const { return min + N * i; } + size_type rindex(size_type i) const { + if (i >= min && i < max) + { size_type j = (i - min); if (j % N == 0) return j / N; } + return size_type(-1); + } + sub_slice(size_type mi, size_type l, size_type n) + : min(mi), max(mi+l*n), N(n) {} + sub_slice(void) {} + }; + + inline std::ostream &operator << (std::ostream &o, const sub_slice &si) { + o << "sub_slice(" << si.min << ", " << si.size() << ", " << si.step() + << ")"; return o; + } + + template struct index_is_sorted + { typedef linalg_true bool_type; }; + template<> struct index_is_sorted + { typedef linalg_false bool_type; }; + +} + +#endif // GMM_SUB_INDEX_H__ diff --git a/src/linear_algebra/gmm/gmm_sub_matrix.h b/src/linear_algebra/gmm/gmm_sub_matrix.h new file mode 100644 index 00000000..998a7662 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_sub_matrix.h @@ -0,0 +1,406 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_sub_matrix.h + @author Yves Renard + @date October 13, 2002. + @brief Generic sub-matrices. +*/ + +#ifndef GMM_SUB_MATRIX_H__ +#define GMM_SUB_MATRIX_H__ + +#include "gmm_sub_vector.h" + +namespace gmm { + + /* ********************************************************************* */ + /* sub row matrices type */ + /* ********************************************************************* */ + + template + struct gen_sub_row_matrix { + typedef gen_sub_row_matrix this_type; + typedef typename std::iterator_traits::value_type M; + typedef M * CPT; + typedef typename std::iterator_traits::reference ref_M; + typedef typename select_ref + ::const_row_iterator, typename linalg_traits::row_iterator, + PT>::ref_type iterator; + typedef typename linalg_traits::reference reference; + typedef typename linalg_traits::porigin_type porigin_type; + + SUBI1 si1; + SUBI2 si2; + iterator begin_; + porigin_type origin; + + reference operator()(size_type i, size_type j) const + { return linalg_traits::access(begin_ + si1.index(i), si2.index(j)); } + + size_type nrows(void) const { return si1.size(); } + size_type ncols(void) const { return si2.size(); } + + gen_sub_row_matrix(ref_M m, const SUBI1 &s1, const SUBI2 &s2) + : si1(s1), si2(s2), begin_(mat_row_begin(m)), + origin(linalg_origin(m)) {} + gen_sub_row_matrix() {} + gen_sub_row_matrix(const gen_sub_row_matrix &cr) : + si1(cr.si1), si2(cr.si2), begin_(cr.begin_),origin(cr.origin) {} + }; + + template + struct gen_sub_row_matrix_iterator { + typedef gen_sub_row_matrix this_type; + typedef typename modifiable_pointer::pointer MPT; + typedef typename std::iterator_traits::value_type M; + typedef typename select_ref + ::const_row_iterator, typename linalg_traits::row_iterator, + PT>::ref_type ITER; + typedef ITER value_type; + typedef ITER *pointer; + typedef ITER &reference; + typedef ptrdiff_t difference_type; + typedef size_t size_type; + typedef std::random_access_iterator_tag iterator_category; + typedef gen_sub_row_matrix_iterator iterator; + + ITER it; + SUBI1 si1; + SUBI2 si2; + size_type ii; + + iterator operator ++(int) { iterator tmp = *this; ii++; return tmp; } + iterator operator --(int) { iterator tmp = *this; ii--; return tmp; } + iterator &operator ++() { ii++; return *this; } + iterator &operator --() { ii--; return *this; } + iterator &operator +=(difference_type i) { ii += i; return *this; } + iterator &operator -=(difference_type i) { ii -= i; return *this; } + iterator operator +(difference_type i) const + { iterator itt = *this; return (itt += i); } + iterator operator -(difference_type i) const + { iterator itt = *this; return (itt -= i); } + difference_type operator -(const iterator &i) const { return ii - i.ii; } + + ITER operator *() const { return it + si1.index(ii); } + ITER operator [](int i) { return it + si1.index(ii+i); } + + bool operator ==(const iterator &i) const { return (ii == i.ii); } + bool operator !=(const iterator &i) const { return !(i == *this); } + bool operator < (const iterator &i) const { return (ii < i.ii); } + + gen_sub_row_matrix_iterator(void) {} + gen_sub_row_matrix_iterator(const + gen_sub_row_matrix_iterator &itm) + : it(itm.it), si1(itm.si1), si2(itm.si2), ii(itm.ii) {} + gen_sub_row_matrix_iterator(const ITER &iter, const SUBI1 &s1, + const SUBI2 &s2, size_type i) + : it(iter), si1(s1), si2(s2), ii(i) { } + + }; + + template + struct linalg_traits > { + typedef gen_sub_row_matrix this_type; + typedef typename std::iterator_traits::value_type M; + typedef typename which_reference::is_reference is_reference; + typedef abstract_matrix linalg_type; + typedef typename linalg_traits::origin_type origin_type; + typedef typename select_ref::ref_type porigin_type; + typedef typename linalg_traits::value_type value_type; + typedef typename select_ref::reference, PT>::ref_type reference; + typedef abstract_null_type sub_col_type; + typedef abstract_null_type col_iterator; + typedef abstract_null_type const_sub_col_type; + typedef abstract_null_type const_col_iterator; + typedef typename sub_vector_type::const_sub_row_type>::t *, SUBI2>::vector_type + const_sub_row_type; + typedef typename select_ref::sub_row_type>::t *, + SUBI2>::vector_type, PT>::ref_type sub_row_type; + typedef gen_sub_row_matrix_iterator::pointer, + SUBI1, SUBI2> const_row_iterator; + typedef typename select_ref, PT>::ref_type + row_iterator; + typedef typename linalg_traits::storage_type + storage_type; + typedef row_major sub_orientation; + typedef linalg_true index_sorted; + static size_type nrows(const this_type &m) { return m.nrows(); } + static size_type ncols(const this_type &m) { return m.ncols(); } + static const_sub_row_type row(const const_row_iterator &it) + { return const_sub_row_type(linalg_traits::row(*it), it.si2); } + static sub_row_type row(const row_iterator &it) + { return sub_row_type(linalg_traits::row(*it), it.si2); } + static const_row_iterator row_begin(const this_type &m) + { return const_row_iterator(m.begin_, m.si1, m.si2, 0); } + static row_iterator row_begin(this_type &m) + { return row_iterator(m.begin_, m.si1, m.si2, 0); } + static const_row_iterator row_end(const this_type &m) + { return const_row_iterator(m.begin_, m.si1, m.si2, m.nrows()); } + static row_iterator row_end(this_type &m) + { return row_iterator(m.begin_, m.si1, m.si2, m.nrows()); } + static origin_type* origin(this_type &v) { return v.origin; } + static const origin_type* origin(const this_type &v) { return v.origin; } + static void do_clear(this_type &m) { + row_iterator it = mat_row_begin(m), ite = mat_row_end(m); + for (; it != ite; ++it) clear(row(it)); + } + static value_type access(const const_row_iterator &itrow, size_type i) + { return linalg_traits::access(*itrow, itrow.si2.index(i)); } + static reference access(const row_iterator &itrow, size_type i) + { return linalg_traits::access(*itrow, itrow.si2.index(i)); } + }; + + template + std::ostream &operator <<(std::ostream &o, + const gen_sub_row_matrix& m) + { gmm::write(o,m); return o; } + + + /* ********************************************************************* */ + /* sub column matrices type */ + /* ********************************************************************* */ + + template + struct gen_sub_col_matrix { + typedef gen_sub_col_matrix this_type; + typedef typename std::iterator_traits::value_type M; + typedef M * CPT; + typedef typename std::iterator_traits::reference ref_M; + typedef typename select_ref + ::const_col_iterator, typename linalg_traits::col_iterator, + PT>::ref_type iterator; + typedef typename linalg_traits::reference reference; + typedef typename linalg_traits::porigin_type porigin_type; + + SUBI1 si1; + SUBI2 si2; + iterator begin_; + porigin_type origin; + + reference operator()(size_type i, size_type j) const + { return linalg_traits::access(begin_ + si2.index(j), si1.index(i)); } + + size_type nrows(void) const { return si1.size(); } + size_type ncols(void) const { return si2.size(); } + + gen_sub_col_matrix(ref_M m, const SUBI1 &s1, const SUBI2 &s2) + : si1(s1), si2(s2), begin_(mat_col_begin(m)), + origin(linalg_origin(m)) {} + gen_sub_col_matrix() {} + gen_sub_col_matrix(const gen_sub_col_matrix &cr) : + si1(cr.si1), si2(cr.si2), begin_(cr.begin_),origin(cr.origin) {} + }; + + template + struct gen_sub_col_matrix_iterator { + typedef gen_sub_col_matrix this_type; + typedef typename modifiable_pointer::pointer MPT; + typedef typename std::iterator_traits::value_type M; + typedef typename select_ref::const_col_iterator, + typename linalg_traits::col_iterator, + PT>::ref_type ITER; + typedef ITER value_type; + typedef ITER *pointer; + typedef ITER &reference; + typedef ptrdiff_t difference_type; + typedef size_t size_type; + typedef std::random_access_iterator_tag iterator_category; + typedef gen_sub_col_matrix_iterator iterator; + + ITER it; + SUBI1 si1; + SUBI2 si2; + size_type ii; + + iterator operator ++(int) { iterator tmp = *this; ii++; return tmp; } + iterator operator --(int) { iterator tmp = *this; ii--; return tmp; } + iterator &operator ++() { ii++; return *this; } + iterator &operator --() { ii--; return *this; } + iterator &operator +=(difference_type i) { ii += i; return *this; } + iterator &operator -=(difference_type i) { ii -= i; return *this; } + iterator operator +(difference_type i) const + { iterator itt = *this; return (itt += i); } + iterator operator -(difference_type i) const + { iterator itt = *this; return (itt -= i); } + difference_type operator -(const iterator &i) const { return ii - i.ii; } + + ITER operator *() const { return it + si2.index(ii); } + ITER operator [](int i) { return it + si2.index(ii+i); } + + bool operator ==(const iterator &i) const { return (ii == i.ii); } + bool operator !=(const iterator &i) const { return !(i == *this); } + bool operator < (const iterator &i) const { return (ii < i.ii); } + + gen_sub_col_matrix_iterator(void) {} + gen_sub_col_matrix_iterator(const + gen_sub_col_matrix_iterator &itm) + : it(itm.it), si1(itm.si1), si2(itm.si2), ii(itm.ii) {} + gen_sub_col_matrix_iterator(const ITER &iter, const SUBI1 &s1, + const SUBI2 &s2, size_type i) + : it(iter), si1(s1), si2(s2), ii(i) { } + }; + + template + struct linalg_traits > { + typedef gen_sub_col_matrix this_type; + typedef typename std::iterator_traits::value_type M; + typedef typename linalg_traits::origin_type origin_type; + typedef typename select_ref::ref_type porigin_type; + typedef typename which_reference::is_reference is_reference; + typedef abstract_matrix linalg_type; + typedef typename linalg_traits::value_type value_type; + typedef typename select_ref::reference, PT>::ref_type reference; + typedef abstract_null_type sub_row_type; + typedef abstract_null_type row_iterator; + typedef abstract_null_type const_sub_row_type; + typedef abstract_null_type const_row_iterator; + typedef typename sub_vector_type::const_sub_col_type>::t *, SUBI1>::vector_type const_sub_col_type; + typedef typename select_ref::sub_col_type>::t *, SUBI1>::vector_type, PT>::ref_type sub_col_type; + typedef gen_sub_col_matrix_iterator::pointer, + SUBI1, SUBI2> const_col_iterator; + typedef typename select_ref, PT>::ref_type + col_iterator; + typedef col_major sub_orientation; + typedef linalg_true index_sorted; + typedef typename linalg_traits::storage_type + storage_type; + static size_type nrows(const this_type &m) { return m.nrows(); } + static size_type ncols(const this_type &m) { return m.ncols(); } + static const_sub_col_type col(const const_col_iterator &it) + { return const_sub_col_type(linalg_traits::col(*it), it.si1); } + static sub_col_type col(const col_iterator &it) + { return sub_col_type(linalg_traits::col(*it), it.si1); } + static const_col_iterator col_begin(const this_type &m) + { return const_col_iterator(m.begin_, m.si1, m.si2, 0); } + static col_iterator col_begin(this_type &m) + { return col_iterator(m.begin_, m.si1, m.si2, 0); } + static const_col_iterator col_end(const this_type &m) + { return const_col_iterator(m.begin_, m.si1, m.si2, m.ncols()); } + static col_iterator col_end(this_type &m) + { return col_iterator(m.begin_, m.si1, m.si2, m.ncols()); } + static origin_type* origin(this_type &v) { return v.origin; } + static const origin_type* origin(const this_type &v) { return v.origin; } + static void do_clear(this_type &m) { + col_iterator it = mat_col_begin(m), ite = mat_col_end(m); + for (; it != ite; ++it) clear(col(it)); + } + static value_type access(const const_col_iterator &itcol, size_type i) + { return linalg_traits::access(*itcol, itcol.si1.index(i)); } + static reference access(const col_iterator &itcol, size_type i) + { return linalg_traits::access(*itcol, itcol.si1.index(i)); } + }; + + template std::ostream &operator << + (std::ostream &o, const gen_sub_col_matrix& m) + { gmm::write(o,m); return o; } + + /* ******************************************************************** */ + /* sub matrices */ + /* ******************************************************************** */ + + template + struct sub_matrix_type_ { + typedef abstract_null_type return_type; + }; + template + struct sub_matrix_type_ + { typedef gen_sub_col_matrix matrix_type; }; + template + struct sub_matrix_type_ + { typedef gen_sub_row_matrix matrix_type; }; + template + struct sub_matrix_type { + typedef typename std::iterator_traits::value_type M; + typedef typename sub_matrix_type_::sub_orientation>::potype>::matrix_type matrix_type; + }; + + template inline + typename select_return + ::matrix_type, typename sub_matrix_type::matrix_type, + M *>::return_type + sub_matrix(M &m, const SUBI1 &si1, const SUBI2 &si2) { + GMM_ASSERT2(si1.last() <= mat_nrows(m) && si2.last() <= mat_ncols(m), + "sub matrix too large"); + return typename select_return::matrix_type, typename sub_matrix_type + ::matrix_type, M *>::return_type(linalg_cast(m), si1, si2); + } + + template inline + typename select_return + ::matrix_type, typename sub_matrix_type::matrix_type, + const M *>::return_type + sub_matrix(const M &m, const SUBI1 &si1, const SUBI2 &si2) { + GMM_ASSERT2(si1.last() <= mat_nrows(m) && si2.last() <= mat_ncols(m), + "sub matrix too large"); + return typename select_return::matrix_type, typename sub_matrix_type + ::matrix_type, const M *>::return_type(linalg_cast(m), si1, si2); + } + + template inline + typename select_return + ::matrix_type, typename sub_matrix_type::matrix_type, + M *>::return_type + sub_matrix(M &m, const SUBI1 &si1) { + GMM_ASSERT2(si1.last() <= mat_nrows(m) && si1.last() <= mat_ncols(m), + "sub matrix too large"); + return typename select_return::matrix_type, typename sub_matrix_type + ::matrix_type, M *>::return_type(linalg_cast(m), si1, si1); + } + + template inline + typename select_return + ::matrix_type, typename sub_matrix_type::matrix_type, + const M *>::return_type + sub_matrix(const M &m, const SUBI1 &si1) { + GMM_ASSERT2(si1.last() <= mat_nrows(m) && si1.last() <= mat_ncols(m), + "sub matrix too large"); + return typename select_return::matrix_type, typename sub_matrix_type + ::matrix_type, const M *>::return_type(linalg_cast(m), si1, si1); + } + +} + +#endif // GMM_SUB_MATRIX_H__ diff --git a/src/linear_algebra/gmm/gmm_sub_vector.h b/src/linear_algebra/gmm/gmm_sub_vector.h new file mode 100644 index 00000000..2ca8df8d --- /dev/null +++ b/src/linear_algebra/gmm/gmm_sub_vector.h @@ -0,0 +1,565 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_sub_vector.h + @author Yves Renard + @date October 13, 2002. + @brief Generic sub-vectors. +*/ + +#ifndef GMM_SUB_VECTOR_H__ +#define GMM_SUB_VECTOR_H__ + +#include "gmm_interface.h" +#include "gmm_sub_index.h" + +namespace gmm { + + /* ********************************************************************* */ + /* sparse sub-vectors */ + /* ********************************************************************* */ + + template + struct sparse_sub_vector_iterator { + + IT itb, itbe; + SUBI si; + + typedef std::iterator_traits traits_type; + typedef typename traits_type::value_type value_type; + typedef typename traits_type::pointer pointer; + typedef typename traits_type::reference reference; + typedef typename traits_type::difference_type difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + typedef size_t size_type; + typedef sparse_sub_vector_iterator iterator; + + size_type index(void) const { return si.rindex(itb.index()); } + void forward(void); + void backward(void); + iterator &operator ++() + { ++itb; forward(); return *this; } + iterator operator ++(int) { iterator tmp = *this; ++(*this); return tmp; } + iterator &operator --() + { --itb; backward(); return *this; } + iterator operator --(int) { iterator tmp = *this; --(*this); return tmp; } + reference operator *() const { return *itb; } + + bool operator ==(const iterator &i) const { return itb == i.itb; } + bool operator !=(const iterator &i) const { return !(i == *this); } + + sparse_sub_vector_iterator(void) {} + sparse_sub_vector_iterator(const IT &it, const IT &ite, const SUBI &s) + : itb(it), itbe(ite), si(s) { forward(); } + sparse_sub_vector_iterator + (const sparse_sub_vector_iterator &it) + : itb(it.itb), itbe(it.itbe), si(it.si) {} + sparse_sub_vector_iterator &operator = + (const sparse_sub_vector_iterator &it) + { itb = it.itb; itbe = it.itbe; si = it.si; return *this; } + + }; + + template + void sparse_sub_vector_iterator::forward(void) + { while(itb!=itbe && index()==size_type(-1)) { ++itb; } } + + template + void sparse_sub_vector_iterator::backward(void) + { while(itb!=itbe && index()==size_type(-1)) --itb; } + + template struct sparse_sub_vector { + typedef sparse_sub_vector this_type; + typedef typename std::iterator_traits::value_type V; + typedef V * CPT; + typedef typename select_ref::const_iterator, + typename linalg_traits::iterator, PT>::ref_type iterator; + typedef typename linalg_traits::reference reference; + typedef typename linalg_traits::porigin_type porigin_type; + + iterator begin_, end_; + porigin_type origin; + SUBI si; + + size_type size(void) const { return si.size(); } + + reference operator[](size_type i) const + { return linalg_traits::access(origin, begin_, end_, si.index(i)); } + + sparse_sub_vector(V &v, const SUBI &s) : begin_(vect_begin(v)), + end_(vect_end(v)), origin(linalg_origin(v)), si(s) {} + sparse_sub_vector(const V &v, const SUBI &s) + : begin_(vect_begin(const_cast(v))), + end_(vect_end(const_cast(v))), + origin(linalg_origin(const_cast(v))), si(s) {} + sparse_sub_vector() {} + sparse_sub_vector(const sparse_sub_vector &cr) + : begin_(cr.begin_),end_(cr.end_),origin(cr.origin), si(cr.si) {} + }; + + template inline + void set_to_begin(sparse_sub_vector_iterator &it, + ORG o, sparse_sub_vector *, + linalg_modifiable) { + typedef sparse_sub_vector VECT; + typedef typename linalg_traits::V_reference ref_t; + set_to_begin(it.itb, o, typename linalg_traits::pV(), ref_t()); + set_to_end(it.itbe, o, typename linalg_traits::pV(), ref_t()); + it.forward(); + } + template inline + void set_to_begin(sparse_sub_vector_iterator &it, + ORG o, const sparse_sub_vector *, + linalg_modifiable) { + typedef sparse_sub_vector VECT; + typedef typename linalg_traits::V_reference ref_t; + set_to_begin(it.itb, o, typename linalg_traits::pV(), ref_t()); + set_to_end(it.itbe, o, typename linalg_traits::pV(), ref_t()); + it.forward(); + } + + template inline + void set_to_end(sparse_sub_vector_iterator &it, + ORG o, sparse_sub_vector *, linalg_modifiable) { + typedef sparse_sub_vector VECT; + typedef typename linalg_traits::V_reference ref_t; + set_to_end(it.itb, o, typename linalg_traits::pV(), ref_t()); + set_to_end(it.itbe, o, typename linalg_traits::pV(), ref_t()); + it.forward(); + } + template inline + void set_to_end(sparse_sub_vector_iterator &it, + ORG o, const sparse_sub_vector *, + linalg_modifiable) { + typedef sparse_sub_vector VECT; + typedef typename linalg_traits::V_reference ref_t; + set_to_end(it.itb, o, typename linalg_traits::pV(), ref_t()); + set_to_end(it.itbe, o, typename linalg_traits::pV(), ref_t()); + it.forward(); + } + + template + struct linalg_traits > { + typedef sparse_sub_vector this_type; + typedef this_type * pthis_type; + typedef PT pV; + typedef typename std::iterator_traits::value_type V; + typedef typename linalg_and::bool_type, + typename linalg_traits::index_sorted>::bool_type index_sorted; + typedef typename linalg_traits::is_reference V_reference; + typedef typename linalg_traits::origin_type origin_type; + typedef typename select_ref::ref_type porigin_type; + typedef typename which_reference::is_reference is_reference; + typedef abstract_vector linalg_type; + typedef typename linalg_traits::value_type value_type; + typedef typename select_ref::reference, PT>::ref_type reference; + typedef typename select_ref::const_iterator, + typename linalg_traits::iterator, PT>::ref_type pre_iterator; + typedef typename select_ref, + PT>::ref_type iterator; + typedef sparse_sub_vector_iterator + ::const_iterator, pre_iterator, SUBI> const_iterator; + typedef abstract_sparse storage_type; + static size_type size(const this_type &v) { return v.size(); } + static iterator begin(this_type &v) { + iterator it; + it.itb = v.begin_; it.itbe = v.end_; it.si = v.si; + if (!is_const_reference(is_reference())) + set_to_begin(it, v.origin, pthis_type(), is_reference()); + else it.forward(); + return it; + } + static const_iterator begin(const this_type &v) { + const_iterator it; it.itb = v.begin_; it.itbe = v.end_; it.si = v.si; + if (!is_const_reference(is_reference())) + { set_to_begin(it, v.origin, pthis_type(), is_reference()); } + else it.forward(); + return it; + } + static iterator end(this_type &v) { + iterator it; + it.itb = v.end_; it.itbe = v.end_; it.si = v.si; + if (!is_const_reference(is_reference())) + set_to_end(it, v.origin, pthis_type(), is_reference()); + else it.forward(); + return it; + } + static const_iterator end(const this_type &v) { + const_iterator it; it.itb = v.end_; it.itbe = v.end_; it.si = v.si; + if (!is_const_reference(is_reference())) + set_to_end(it, v.origin, pthis_type(), is_reference()); + else it.forward(); + return it; + } + static origin_type* origin(this_type &v) { return v.origin; } + static const origin_type* origin(const this_type &v) { return v.origin; } + static void clear(origin_type* o, const iterator &begin_, + const iterator &end_) { + std::deque ind; + iterator it = begin_; + for (; it != end_; ++it) ind.push_front(it.index()); + for (; !(ind.empty()); ind.pop_back()) + access(o, begin_, end_, ind.back()) = value_type(0); + } + static void do_clear(this_type &v) { clear(v.origin, begin(v), end(v)); } + static value_type access(const origin_type *o, const const_iterator &it, + const const_iterator &ite, size_type i) + { return linalg_traits::access(o, it.itb, ite.itb, it.si.index(i)); } + static reference access(origin_type *o, const iterator &it, + const iterator &ite, size_type i) + { return linalg_traits::access(o, it.itb, ite.itb, it.si.index(i)); } + }; + + template std::ostream &operator << + (std::ostream &o, const sparse_sub_vector& m) + { gmm::write(o,m); return o; } + + /* ********************************************************************* */ + /* skyline sub-vectors */ + /* ********************************************************************* */ + + template + struct skyline_sub_vector_iterator { + + IT itb; + SUBI si; + + typedef std::iterator_traits traits_type; + typedef typename traits_type::value_type value_type; + typedef typename traits_type::pointer pointer; + typedef typename traits_type::reference reference; + typedef typename traits_type::difference_type difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + typedef size_t size_type; + typedef skyline_sub_vector_iterator iterator; + + size_type index(void) const + { return (itb.index() - si.min + si.step() - 1) / si.step(); } + void backward(void); + iterator &operator ++() + { itb += si.step(); return *this; } + iterator operator ++(int) { iterator tmp = *this; ++(*this); return tmp; } + iterator &operator --() + { itb -= si.step(); return *this; } + iterator operator --(int) { iterator tmp = *this; --(*this); return tmp; } + + iterator &operator +=(difference_type i) + { itb += si.step() * i; return *this; } + iterator &operator -=(difference_type i) + { itb -= si.step() * i; return *this; } + iterator operator +(difference_type i) const + { iterator ii = *this; return (ii += i); } + iterator operator -(difference_type i) const + { iterator ii = *this; return (ii -= i); } + difference_type operator -(const iterator &i) const + { return (itb - i.itb) / si.step(); } + + reference operator *() const { return *itb; } + reference operator [](int ii) { return *(itb + ii * si.step()); } + + bool operator ==(const iterator &i) const { return index() == i.index();} + bool operator !=(const iterator &i) const { return !(i == *this); } + bool operator < (const iterator &i) const { return index() < i.index();} + + skyline_sub_vector_iterator(void) {} + skyline_sub_vector_iterator(const IT &it, const SUBI &s) + : itb(it), si(s) {} + skyline_sub_vector_iterator(const skyline_sub_vector_iterator &it) : itb(it.itb), si(it.si) {} + }; + + template + void update_for_sub_skyline(IT &it, IT &ite, const SUBI &si) { + if (it.index() >= si.max || ite.index() <= si.min) { it = ite; return; } + ptrdiff_t dec1 = si.min - it.index(), dec2 = ite.index() - si.max; + it += (dec1 < 0) ? ((si.step()-((-dec1) % si.step())) % si.step()) : dec1; + ite -= (dec2 < 0) ? -((-dec2) % si.step()) : dec2; + } + + template struct skyline_sub_vector { + typedef skyline_sub_vector this_type; + typedef typename std::iterator_traits::value_type V; + typedef V * pV; + typedef typename select_ref::const_iterator, + typename linalg_traits::iterator, PT>::ref_type iterator; + typedef typename linalg_traits::reference reference; + typedef typename linalg_traits::porigin_type porigin_type; + + iterator begin_, end_; + porigin_type origin; + SUBI si; + + size_type size(void) const { return si.size(); } + + reference operator[](size_type i) const + { return linalg_traits::access(origin, begin_, end_, si.index(i)); } + + skyline_sub_vector(V &v, const SUBI &s) : begin_(vect_begin(v)), + end_(vect_end(v)), origin(linalg_origin(v)), si(s) { + update_for_sub_skyline(begin_, end_, si); + } + skyline_sub_vector(const V &v, const SUBI &s) + : begin_(vect_begin(const_cast(v))), + end_(vect_end(const_cast(v))), + origin(linalg_origin(const_cast(v))), si(s) { + update_for_sub_skyline(begin_, end_, si); + } + skyline_sub_vector() {} + skyline_sub_vector(const skyline_sub_vector &cr) + : begin_(cr.begin_),end_(cr.end_),origin(cr.origin), si(cr.si) {} + }; + + template inline + void set_to_begin(skyline_sub_vector_iterator &it, + ORG o, skyline_sub_vector *, + linalg_modifiable) { + typedef skyline_sub_vector VECT; + typedef typename linalg_traits::V_reference ref_t; + IT itbe = it.itb; + set_to_begin(it.itb, o, typename linalg_traits::pV(), ref_t()); + set_to_end(itbe, o, typename linalg_traits::pV(), ref_t()); + update_for_sub_skyline(it.itb, itbe, it.si); + } + template inline + void set_to_begin(skyline_sub_vector_iterator &it, + ORG o, const skyline_sub_vector *, + linalg_modifiable) { + typedef skyline_sub_vector VECT; + typedef typename linalg_traits::V_reference ref_t; + IT itbe = it.itb; + set_to_begin(it.itb, o, typename linalg_traits::pV(), ref_t()); + set_to_end(itbe, o, typename linalg_traits::pV(), ref_t()); + update_for_sub_skyline(it.itb, itbe, it.si); + } + + template inline + void set_to_end(skyline_sub_vector_iterator &it, + ORG o, skyline_sub_vector *, + linalg_modifiable) { + typedef skyline_sub_vector VECT; + typedef typename linalg_traits::V_reference ref_t; + IT itb = it.itb; + set_to_begin(itb, o, typename linalg_traits::pV(), ref_t()); + set_to_end(it.itb, o, typename linalg_traits::pV(), ref_t()); + update_for_sub_skyline(itb, it.itb, it.si); + } + template inline + void set_to_end(skyline_sub_vector_iterator &it, + ORG o, const skyline_sub_vector *, + linalg_modifiable) { + typedef skyline_sub_vector VECT; + typedef typename linalg_traits::V_reference ref_t; + IT itb = it.itb; + set_to_begin(itb, o, typename linalg_traits::pV(), ref_t()); + set_to_end(it.itb, o, typename linalg_traits::pV(), ref_t()); + update_for_sub_skyline(itb, it.itb, it.si); + } + + + template + struct linalg_traits > { + typedef skyline_sub_vector this_type; + typedef this_type *pthis_type; + typedef typename std::iterator_traits::value_type V; + typedef typename linalg_traits::is_reference V_reference; + typedef typename linalg_traits::origin_type origin_type; + typedef typename select_ref::ref_type porigin_type; + typedef V * pV; + typedef typename which_reference::is_reference is_reference; + typedef abstract_vector linalg_type; + typedef typename linalg_traits::value_type value_type; + typedef typename select_ref::reference, PT>::ref_type reference; + typedef typename linalg_traits::const_iterator const_V_iterator; + typedef typename linalg_traits::iterator V_iterator; + typedef typename select_ref::ref_type pre_iterator; + typedef typename select_ref, + PT>::ref_type iterator; + typedef skyline_sub_vector_iterator + const_iterator; + typedef abstract_skyline storage_type; + typedef linalg_true index_sorted; + static size_type size(const this_type &v) { return v.size(); } + static iterator begin(this_type &v) { + iterator it; + it.itb = v.begin_; it.si = v.si; + if (!is_const_reference(is_reference())) + set_to_begin(it, v.origin, pthis_type(), is_reference()); + return it; + } + static const_iterator begin(const this_type &v) { + const_iterator it; it.itb = v.begin_; it.si = v.si; + if (!is_const_reference(is_reference())) + { set_to_begin(it, v.origin, pthis_type(), is_reference()); } + return it; + } + static iterator end(this_type &v) { + iterator it; + it.itb = v.end_; it.si = v.si; + if (!is_const_reference(is_reference())) + set_to_end(it, v.origin, pthis_type(), is_reference()); + return it; + } + static const_iterator end(const this_type &v) { + const_iterator it; it.itb = v.end_; it.si = v.si; + if (!is_const_reference(is_reference())) + set_to_end(it, v.origin, pthis_type(), is_reference()); + return it; + } + static origin_type* origin(this_type &v) { return v.origin; } + static const origin_type* origin(const this_type &v) { return v.origin; } + static void clear(origin_type*, const iterator &it, const iterator &ite) + { std::fill(it, ite, value_type(0)); } + static void do_clear(this_type &v) { clear(v.origin, begin(v), end(v)); } + static value_type access(const origin_type *o, const const_iterator &it, + const const_iterator &ite, size_type i) + { return linalg_traits::access(o, it.itb, ite.itb, it.si.index(i)); } + static reference access(origin_type *o, const iterator &it, + const iterator &ite, size_type i) + { return linalg_traits::access(o, it.itb, ite.itb, it.si.index(i)); } + }; + + template std::ostream &operator << + (std::ostream &o, const skyline_sub_vector& m) + { gmm::write(o,m); return o; } + + /* ******************************************************************** */ + /* sub vector. */ + /* ******************************************************************** */ + /* sub_vector_type::vector_type is the sub vector type */ + /* returned by sub_vector(v, sub_index) */ + /************************************************************************/ + + template struct svrt_ir { + typedef abstract_null_type vector_type; + }; + + template + struct svrt_ir { + typedef typename std::iterator_traits::value_type V; + typedef typename vect_ref_type::iterator iterator; + typedef tab_ref_index_ref_with_origin vector_type; + }; + + template + struct svrt_ir { + typedef typename std::iterator_traits::value_type V; + typedef typename vect_ref_type::iterator iterator; + typedef tab_ref_index_ref_with_origin vector_type; + }; + + template + struct svrt_ir { + typedef typename std::iterator_traits::value_type V; + typedef typename vect_ref_type::iterator iterator; + typedef tab_ref_with_origin vector_type; + }; + + template + struct svrt_ir { + typedef typename std::iterator_traits::value_type V; + typedef typename vect_ref_type::iterator iterator; + typedef tab_ref_reg_spaced_with_origin vector_type; + }; + + template + struct svrt_ir { + typedef skyline_sub_vector vector_type; + }; + + template + struct svrt_ir { + typedef sparse_sub_vector vector_type; + }; + + template + struct svrt_ir { + typedef sparse_sub_vector vector_type; + }; + + + template + struct svrt_ir { + typedef sparse_sub_vector vector_type; + }; + + template + struct sub_vector_type { + typedef typename std::iterator_traits::value_type V; + typedef typename svrt_ir::storage_type>::vector_type vector_type; + }; + + template + typename select_return< + typename sub_vector_type::vector_type, + typename sub_vector_type::vector_type, const V *>::return_type + sub_vector(const V &v, const SUBI &si) { + GMM_ASSERT2(si.last() <= vect_size(v), + "sub vector too large, " << si.last() << " > " << vect_size(v)); + return typename select_return< + typename sub_vector_type::vector_type, + typename sub_vector_type::vector_type, const V *>::return_type + (linalg_cast(v), si); + } + + template + typename select_return< + typename sub_vector_type::vector_type, + typename sub_vector_type::vector_type, V *>::return_type + sub_vector(V &v, const SUBI &si) { + GMM_ASSERT2(si.last() <= vect_size(v), + "sub vector too large, " << si.last() << " > " << vect_size(v)); + return typename select_return< + typename sub_vector_type::vector_type, + typename sub_vector_type::vector_type, V *>::return_type + (linalg_cast(v), si); + } + +} + +#endif // GMM_SUB_VECTOR_H__ diff --git a/src/linear_algebra/gmm/gmm_superlu_interface.h b/src/linear_algebra/gmm/gmm_superlu_interface.h new file mode 100644 index 00000000..2a7a2a4a --- /dev/null +++ b/src/linear_algebra/gmm/gmm_superlu_interface.h @@ -0,0 +1,410 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2003-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_superlu_interface.h + @author Yves Renard + @date October 17, 2003. + @brief Interface with SuperLU (LU direct solver for sparse matrices). +*/ +#if defined(GMM_USES_SUPERLU) && !defined(GETFEM_VERSION) + +#ifndef GMM_SUPERLU_INTERFACE_H +#define GMM_SUPERLU_INTERFACE_H + +#include "gmm_kernel.h" + +typedef int int_t; + +/* because SRC/util.h defines TRUE and FALSE ... */ +#ifdef TRUE +# undef TRUE +#endif +#ifdef FALSE +# undef FALSE +#endif + +#include "superlu/slu_Cnames.h" +#include "superlu/supermatrix.h" +#include "superlu/slu_util.h" + +namespace SuperLU_S { +#include "superlu/slu_sdefs.h" +} +namespace SuperLU_D { +#include "superlu/slu_ddefs.h" +} +namespace SuperLU_C { +#include "superlu/slu_cdefs.h" +} +namespace SuperLU_Z { +#include "superlu/slu_zdefs.h" +} + + + +namespace gmm { + + /* interface for Create_CompCol_Matrix */ + + inline void Create_CompCol_Matrix(SuperMatrix *A, int m, int n, int nnz, + float *a, int *ir, int *jc) { + SuperLU_S::sCreate_CompCol_Matrix(A, m, n, nnz, a, ir, jc, + SLU_NC, SLU_S, SLU_GE); + } + + inline void Create_CompCol_Matrix(SuperMatrix *A, int m, int n, int nnz, + double *a, int *ir, int *jc) { + SuperLU_D::dCreate_CompCol_Matrix(A, m, n, nnz, a, ir, jc, + SLU_NC, SLU_D, SLU_GE); + } + + inline void Create_CompCol_Matrix(SuperMatrix *A, int m, int n, int nnz, + std::complex *a, int *ir, int *jc) { + SuperLU_C::cCreate_CompCol_Matrix(A, m, n, nnz, (SuperLU_C::complex *)(a), + ir, jc, SLU_NC, SLU_C, SLU_GE); + } + + inline void Create_CompCol_Matrix(SuperMatrix *A, int m, int n, int nnz, + std::complex *a, int *ir, int *jc) { + SuperLU_Z::zCreate_CompCol_Matrix(A, m, n, nnz, + (SuperLU_Z::doublecomplex *)(a), ir, jc, + SLU_NC, SLU_Z, SLU_GE); + } + + /* interface for Create_Dense_Matrix */ + + inline void Create_Dense_Matrix(SuperMatrix *A, int m, int n, float *a, int k) + { SuperLU_S::sCreate_Dense_Matrix(A, m, n, a, k, SLU_DN, SLU_S, SLU_GE); } + inline void Create_Dense_Matrix(SuperMatrix *A, int m, int n, double *a, int k) + { SuperLU_D::dCreate_Dense_Matrix(A, m, n, a, k, SLU_DN, SLU_D, SLU_GE); } + inline void Create_Dense_Matrix(SuperMatrix *A, int m, int n, + std::complex *a, int k) { + SuperLU_C::cCreate_Dense_Matrix(A, m, n, (SuperLU_C::complex *)(a), + k, SLU_DN, SLU_C, SLU_GE); + } + inline void Create_Dense_Matrix(SuperMatrix *A, int m, int n, + std::complex *a, int k) { + SuperLU_Z::zCreate_Dense_Matrix(A, m, n, (SuperLU_Z::doublecomplex *)(a), + k, SLU_DN, SLU_Z, SLU_GE); + } + + /* interface for gssv */ + +#define DECL_GSSV(NAMESPACE,FNAME,FLOATTYPE,KEYTYPE) \ + inline void SuperLU_gssv(superlu_options_t *options, SuperMatrix *A, int *p, \ + int *q, SuperMatrix *L, SuperMatrix *U, SuperMatrix *B, \ + SuperLUStat_t *stats, int *info, KEYTYPE) { \ + NAMESPACE::FNAME(options, A, p, q, L, U, B, stats, info); \ + } + + DECL_GSSV(SuperLU_S,sgssv,float,float) + DECL_GSSV(SuperLU_C,cgssv,float,std::complex) + DECL_GSSV(SuperLU_D,dgssv,double,double) + DECL_GSSV(SuperLU_Z,zgssv,double,std::complex) + + /* interface for gssvx */ + +#define DECL_GSSVX(NAMESPACE,FNAME,FLOATTYPE,KEYTYPE) \ + inline float SuperLU_gssvx(superlu_options_t *options, SuperMatrix *A, \ + int *perm_c, int *perm_r, int *etree, char *equed, \ + FLOATTYPE *R, FLOATTYPE *C, SuperMatrix *L, \ + SuperMatrix *U, void *work, int lwork, \ + SuperMatrix *B, SuperMatrix *X, \ + FLOATTYPE *recip_pivot_growth, \ + FLOATTYPE *rcond, FLOATTYPE *ferr, FLOATTYPE *berr, \ + SuperLUStat_t *stats, int *info, KEYTYPE) { \ + NAMESPACE::mem_usage_t mem_usage; \ + NAMESPACE::FNAME(options, A, perm_c, perm_r, etree, equed, R, C, L, \ + U, work, lwork, B, X, recip_pivot_growth, rcond, \ + ferr, berr, &mem_usage, stats, info); \ + return mem_usage.for_lu; /* bytes used by the factor storage */ \ + } + + DECL_GSSVX(SuperLU_S,sgssvx,float,float) + DECL_GSSVX(SuperLU_C,cgssvx,float,std::complex) + DECL_GSSVX(SuperLU_D,dgssvx,double,double) + DECL_GSSVX(SuperLU_Z,zgssvx,double,std::complex) + + /* ********************************************************************* */ + /* SuperLU solve interface */ + /* ********************************************************************* */ + + template + int SuperLU_solve(const MAT &A, const VECTX &X_, const VECTB &B, + double& rcond_, int permc_spec = 3) { + VECTX &X = const_cast(X_); + /* + * Get column permutation vector perm_c[], according to permc_spec: + * permc_spec = 0: use the natural ordering + * permc_spec = 1: use minimum degree ordering on structure of A'*A + * permc_spec = 2: use minimum degree ordering on structure of A'+A + * permc_spec = 3: use approximate minimum degree column ordering + */ + typedef typename linalg_traits::value_type T; + typedef typename number_traits::magnitude_type R; + + int m = mat_nrows(A), n = mat_ncols(A), nrhs = 1, info = 0; + + csc_matrix csc_A(m, n); gmm::copy(A, csc_A); + std::vector rhs(m), sol(m); + gmm::copy(B, rhs); + + int nz = nnz(csc_A); + if ((2 * nz / n) >= m) + GMM_WARNING2("CAUTION : it seems that SuperLU has a problem" + " for nearly dense sparse matrices"); + + superlu_options_t options; + set_default_options(&options); + options.ColPerm = NATURAL; + options.PrintStat = NO; + options.ConditionNumber = YES; + switch (permc_spec) { + case 1 : options.ColPerm = MMD_ATA; break; + case 2 : options.ColPerm = MMD_AT_PLUS_A; break; + case 3 : options.ColPerm = COLAMD; break; + } + SuperLUStat_t stat; + StatInit(&stat); + + SuperMatrix SA, SL, SU, SB, SX; // SuperLU format. + Create_CompCol_Matrix(&SA, m, n, nz, (double *)(&(csc_A.pr[0])), + (int *)(&(csc_A.ir[0])), (int *)(&(csc_A.jc[0]))); + Create_Dense_Matrix(&SB, m, nrhs, &rhs[0], m); + Create_Dense_Matrix(&SX, m, nrhs, &sol[0], m); + memset(&SL,0,sizeof SL); + memset(&SU,0,sizeof SU); + + std::vector etree(n); + char equed[] = "B"; + std::vector Rscale(m),Cscale(n); // row scale factors + std::vector ferr(nrhs), berr(nrhs); + R recip_pivot_gross, rcond; + std::vector perm_r(m), perm_c(n); + + SuperLU_gssvx(&options, &SA, &perm_c[0], &perm_r[0], + &etree[0] /* output */, equed /* output */, + &Rscale[0] /* row scale factors (output) */, + &Cscale[0] /* col scale factors (output) */, + &SL /* fact L (output)*/, &SU /* fact U (output)*/, + NULL /* work */, + 0 /* lwork: superlu auto allocates (input) */, + &SB /* rhs */, &SX /* solution */, + &recip_pivot_gross /* reciprocal pivot growth */ + /* factor max_j( norm(A_j)/norm(U_j) ). */, + &rcond /*estimate of the reciprocal condition */ + /* number of the matrix A after equilibration */, + &ferr[0] /* estimated forward error */, + &berr[0] /* relative backward error */, + &stat, &info, T()); + rcond_ = rcond; + Destroy_SuperMatrix_Store(&SB); + Destroy_SuperMatrix_Store(&SX); + Destroy_SuperMatrix_Store(&SA); + Destroy_SuperNode_Matrix(&SL); + Destroy_CompCol_Matrix(&SU); + StatFree(&stat); + GMM_ASSERT1(info >= 0, "SuperLU solve failed: info =" << info); + if (info > 0) GMM_WARNING1("SuperLU solve failed: info =" << info); + gmm::copy(sol, X); + return info; + } + + template class SuperLU_factor { + typedef typename number_traits::magnitude_type R; + + csc_matrix csc_A; + mutable SuperMatrix SA, SL, SB, SU, SX; + mutable SuperLUStat_t stat; + mutable superlu_options_t options; + float memory_used; + mutable std::vector etree, perm_r, perm_c; + mutable std::vector Rscale, Cscale; + mutable std::vector ferr, berr; + mutable std::vector rhs; + mutable std::vector sol; + mutable bool is_init; + mutable char equed; + + public : + enum { LU_NOTRANSP, LU_TRANSP, LU_CONJUGATED }; + void free_supermatrix(void); + template void build_with(const MAT &A, int permc_spec = 3); + template + /* transp = LU_NOTRANSP -> solves Ax = B + transp = LU_TRANSP -> solves A'x = B + transp = LU_CONJUGATED -> solves conj(A)X = B */ + void solve(const VECTX &X_, const VECTB &B, int transp=LU_NOTRANSP) const; + SuperLU_factor(void) { is_init = false; } + SuperLU_factor(const SuperLU_factor& other) { + GMM_ASSERT2(!(other.is_init), + "copy of initialized SuperLU_factor is forbidden"); + is_init = false; + } + SuperLU_factor& operator=(const SuperLU_factor& other) { + GMM_ASSERT2(!(other.is_init) && !is_init, + "assignment of initialized SuperLU_factor is forbidden"); + return *this; + } + ~SuperLU_factor() { free_supermatrix(); } + float memsize() { return memory_used; } + }; + + + template void SuperLU_factor::free_supermatrix(void) { + if (is_init) { + if (SB.Store) Destroy_SuperMatrix_Store(&SB); + if (SX.Store) Destroy_SuperMatrix_Store(&SX); + if (SA.Store) Destroy_SuperMatrix_Store(&SA); + if (SL.Store) Destroy_SuperNode_Matrix(&SL); + if (SU.Store) Destroy_CompCol_Matrix(&SU); + } + } + + + template template + void SuperLU_factor::build_with(const MAT &A, int permc_spec) { + /* + * Get column permutation vector perm_c[], according to permc_spec: + * permc_spec = 0: use the natural ordering + * permc_spec = 1: use minimum degree ordering on structure of A'*A + * permc_spec = 2: use minimum degree ordering on structure of A'+A + * permc_spec = 3: use approximate minimum degree column ordering + */ + free_supermatrix(); + int n = mat_nrows(A), m = mat_ncols(A), info = 0; + csc_A.init_with(A); + + rhs.resize(m); sol.resize(m); + gmm::clear(rhs); + int nz = nnz(csc_A); + + set_default_options(&options); + options.ColPerm = NATURAL; + options.PrintStat = NO; + options.ConditionNumber = NO; + switch (permc_spec) { + case 1 : options.ColPerm = MMD_ATA; break; + case 2 : options.ColPerm = MMD_AT_PLUS_A; break; + case 3 : options.ColPerm = COLAMD; break; + } + StatInit(&stat); + + Create_CompCol_Matrix(&SA, m, n, nz, (double *)(&(csc_A.pr[0])), + (int *)(&(csc_A.ir[0])), (int *)(&(csc_A.jc[0]))); + + Create_Dense_Matrix(&SB, m, 0, &rhs[0], m); + Create_Dense_Matrix(&SX, m, 0, &sol[0], m); + memset(&SL,0,sizeof SL); + memset(&SU,0,sizeof SU); + equed = 'B'; + Rscale.resize(m); Cscale.resize(n); etree.resize(n); + ferr.resize(1); berr.resize(1); + R recip_pivot_gross, rcond; + perm_r.resize(m); perm_c.resize(n); + memory_used = SuperLU_gssvx(&options, &SA, &perm_c[0], &perm_r[0], + &etree[0] /* output */, &equed /* output */, + &Rscale[0] /* row scale factors (output) */, + &Cscale[0] /* col scale factors (output) */, + &SL /* fact L (output)*/, &SU /* fact U (output)*/, + NULL /* work */, + 0 /* lwork: superlu auto allocates (input) */, + &SB /* rhs */, &SX /* solution */, + &recip_pivot_gross /* reciprocal pivot growth */ + /* factor max_j( norm(A_j)/norm(U_j) ). */, + &rcond /*estimate of the reciprocal condition */ + /* number of the matrix A after equilibration */, + &ferr[0] /* estimated forward error */, + &berr[0] /* relative backward error */, + &stat, &info, T()); + + Destroy_SuperMatrix_Store(&SB); + Destroy_SuperMatrix_Store(&SX); + Create_Dense_Matrix(&SB, m, 1, &rhs[0], m); + Create_Dense_Matrix(&SX, m, 1, &sol[0], m); + StatFree(&stat); + + GMM_ASSERT1(info == 0, "SuperLU solve failed: info=" << info); + is_init = true; + } + + template template + void SuperLU_factor::solve(const VECTX &X_, const VECTB &B, + int transp) const { + VECTX &X = const_cast(X_); + gmm::copy(B, rhs); + options.Fact = FACTORED; + options.IterRefine = NOREFINE; + switch (transp) { + case LU_NOTRANSP: options.Trans = NOTRANS; break; + case LU_TRANSP: options.Trans = TRANS; break; + case LU_CONJUGATED: options.Trans = CONJ; break; + default: GMM_ASSERT1(false, "invalid value for transposition option"); + } + StatInit(&stat); + int info = 0; + R recip_pivot_gross, rcond; + SuperLU_gssvx(&options, &SA, &perm_c[0], &perm_r[0], + &etree[0] /* output */, &equed /* output */, + &Rscale[0] /* row scale factors (output) */, + &Cscale[0] /* col scale factors (output) */, + &SL /* fact L (output)*/, &SU /* fact U (output)*/, + NULL /* work */, + 0 /* lwork: superlu auto allocates (input) */, + &SB /* rhs */, &SX /* solution */, + &recip_pivot_gross /* reciprocal pivot growth */ + /* factor max_j( norm(A_j)/norm(U_j) ). */, + &rcond /*estimate of the reciprocal condition */ + /* number of the matrix A after equilibration */, + &ferr[0] /* estimated forward error */, + &berr[0] /* relative backward error */, + &stat, &info, T()); + StatFree(&stat); + GMM_ASSERT1(info == 0, "SuperLU solve failed: info=" << info); + gmm::copy(sol, X); + } + + template inline + void mult(const SuperLU_factor& P, const V1 &v1, const V2 &v2) { + P.solve(v2,v1); + } + + template inline + void transposed_mult(const SuperLU_factor& P,const V1 &v1,const V2 &v2) { + P.solve(v2, v1, SuperLU_factor::LU_TRANSP); + } + +} + + +#endif // GMM_SUPERLU_INTERFACE_H + +#endif // GMM_USES_SUPERLU diff --git a/src/linear_algebra/gmm/gmm_transposed.h b/src/linear_algebra/gmm/gmm_transposed.h new file mode 100644 index 00000000..77cf460e --- /dev/null +++ b/src/linear_algebra/gmm/gmm_transposed.h @@ -0,0 +1,244 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_transposed.h + @author Yves Renard + @date November 10, 2002. + @brief Generic transposed matrices +*/ +#ifndef GMM_TRANSPOSED_H__ +#define GMM_TRANSPOSED_H__ + +#include "gmm_def.h" + +namespace gmm { + + /* ********************************************************************* */ + /* transposed reference */ + /* ********************************************************************* */ + + template struct transposed_row_ref { + + typedef transposed_row_ref this_type; + typedef typename std::iterator_traits::value_type M; + typedef M * CPT; + typedef typename std::iterator_traits::reference ref_M; + typedef typename select_ref + ::const_col_iterator, typename linalg_traits + ::col_iterator, PT>::ref_type iterator; + typedef typename linalg_traits::reference reference; + typedef typename linalg_traits::porigin_type porigin_type; + + iterator begin_, end_; + porigin_type origin; + size_type nr, nc; + + transposed_row_ref(ref_M m) + : begin_(mat_row_begin(m)), end_(mat_row_end(m)), + origin(linalg_origin(m)), nr(mat_ncols(m)), nc(mat_nrows(m)) {} + + transposed_row_ref(const transposed_row_ref &cr) : + begin_(cr.begin_),end_(cr.end_), origin(cr.origin),nr(cr.nr),nc(cr.nc) {} + + reference operator()(size_type i, size_type j) const + { return linalg_traits::access(begin_+j, i); } + }; + + template struct linalg_traits > { + typedef transposed_row_ref this_type; + typedef typename std::iterator_traits::value_type M; + typedef typename linalg_traits::origin_type origin_type; + typedef typename select_ref::ref_type porigin_type; + typedef typename which_reference::is_reference is_reference; + typedef abstract_matrix linalg_type; + typedef typename linalg_traits::value_type value_type; + typedef typename select_ref::reference, PT>::ref_type reference; + typedef typename linalg_traits::storage_type storage_type; + typedef abstract_null_type sub_row_type; + typedef abstract_null_type const_sub_row_type; + typedef abstract_null_type row_iterator; + typedef abstract_null_type const_row_iterator; + typedef typename linalg_traits::const_sub_row_type const_sub_col_type; + typedef typename select_ref::sub_row_type, PT>::ref_type sub_col_type; + typedef typename linalg_traits::const_row_iterator const_col_iterator; + typedef typename select_ref::row_iterator, PT>::ref_type col_iterator; + typedef col_major sub_orientation; + typedef typename linalg_traits::index_sorted index_sorted; + static size_type ncols(const this_type &v) { return v.nc; } + static size_type nrows(const this_type &v) { return v.nr; } + static const_sub_col_type col(const const_col_iterator &it) + { return linalg_traits::row(it); } + static sub_col_type col(const col_iterator &it) + { return linalg_traits::row(it); } + static col_iterator col_begin(this_type &m) { return m.begin_; } + static col_iterator col_end(this_type &m) { return m.end_; } + static const_col_iterator col_begin(const this_type &m) + { return m.begin_; } + static const_col_iterator col_end(const this_type &m) { return m.end_; } + static origin_type* origin(this_type &v) { return v.origin; } + static const origin_type* origin(const this_type &v) { return v.origin; } + static void do_clear(this_type &v); + static value_type access(const const_col_iterator &itcol, size_type i) + { return linalg_traits::access(itcol, i); } + static reference access(const col_iterator &itcol, size_type i) + { return linalg_traits::access(itcol, i); } + }; + + template + void linalg_traits >::do_clear(this_type &v) { + col_iterator it = mat_col_begin(v), ite = mat_col_end(v); + for (; it != ite; ++it) clear(col(it)); + } + + template std::ostream &operator << + (std::ostream &o, const transposed_row_ref& m) + { gmm::write(o,m); return o; } + + template struct transposed_col_ref { + + typedef transposed_col_ref this_type; + typedef typename std::iterator_traits::value_type M; + typedef M * CPT; + typedef typename std::iterator_traits::reference ref_M; + typedef typename select_ref + ::const_row_iterator, typename linalg_traits + ::row_iterator, PT>::ref_type iterator; + typedef typename linalg_traits::reference reference; + typedef typename linalg_traits::porigin_type porigin_type; + + iterator begin_, end_; + porigin_type origin; + size_type nr, nc; + + transposed_col_ref(ref_M m) + : begin_(mat_col_begin(m)), end_(mat_col_end(m)), + origin(linalg_origin(m)), nr(mat_ncols(m)), nc(mat_nrows(m)) {} + + transposed_col_ref(const transposed_col_ref &cr) : + begin_(cr.begin_),end_(cr.end_), origin(cr.origin),nr(cr.nr),nc(cr.nc) {} + + reference operator()(size_type i, size_type j) const + { return linalg_traits::access(begin_+i, j); } + }; + + template struct linalg_traits > { + typedef transposed_col_ref this_type; + typedef typename std::iterator_traits::value_type M; + typedef typename linalg_traits::origin_type origin_type; + typedef typename select_ref::ref_type porigin_type; + typedef typename which_reference::is_reference is_reference; + typedef abstract_matrix linalg_type; + typedef typename linalg_traits::value_type value_type; + typedef typename select_ref::reference, PT>::ref_type reference; + typedef typename linalg_traits::storage_type storage_type; + typedef abstract_null_type sub_col_type; + typedef abstract_null_type const_sub_col_type; + typedef abstract_null_type col_iterator; + typedef abstract_null_type const_col_iterator; + typedef typename linalg_traits::const_sub_col_type const_sub_row_type; + typedef typename select_ref::sub_col_type, PT>::ref_type sub_row_type; + typedef typename linalg_traits::const_col_iterator const_row_iterator; + typedef typename select_ref::col_iterator, PT>::ref_type row_iterator; + typedef row_major sub_orientation; + typedef typename linalg_traits::index_sorted index_sorted; + static size_type nrows(const this_type &v) + { return v.nr; } + static size_type ncols(const this_type &v) + { return v.nc; } + static const_sub_row_type row(const const_row_iterator &it) + { return linalg_traits::col(it); } + static sub_row_type row(const row_iterator &it) + { return linalg_traits::col(it); } + static row_iterator row_begin(this_type &m) { return m.begin_; } + static row_iterator row_end(this_type &m) { return m.end_; } + static const_row_iterator row_begin(const this_type &m) + { return m.begin_; } + static const_row_iterator row_end(const this_type &m) { return m.end_; } + static origin_type* origin(this_type &v) { return v.origin; } + static const origin_type* origin(const this_type &v) { return v.origin; } + static void do_clear(this_type &m); + static value_type access(const const_row_iterator &itrow, size_type i) + { return linalg_traits::access(itrow, i); } + static reference access(const row_iterator &itrow, size_type i) + { return linalg_traits::access(itrow, i); } + }; + + template + void linalg_traits >::do_clear(this_type &v) { + row_iterator it = mat_row_begin(v), ite = mat_row_end(v); + for (; it != ite; ++it) clear(row(it)); + } + + template std::ostream &operator << + (std::ostream &o, const transposed_col_ref& m) + { gmm::write(o,m); return o; } + + template struct transposed_return_ { + typedef abstract_null_type return_type; + }; + template struct transposed_return_ { + typedef typename std::iterator_traits::value_type L; + typedef typename select_return, + transposed_row_ref< L *>, PT>::return_type return_type; + }; + template struct transposed_return_ { + typedef typename std::iterator_traits::value_type L; + typedef typename select_return, + transposed_col_ref< L *>, PT>::return_type return_type; + }; + template struct transposed_return { + typedef typename std::iterator_traits::value_type L; + typedef typename transposed_return_::sub_orientation>::potype, + PT>::return_type return_type; + }; + + template inline + typename transposed_return::return_type transposed(const L &l) { + return typename transposed_return::return_type + (linalg_cast(const_cast(l))); + } + + template inline + typename transposed_return::return_type transposed(L &l) + { return typename transposed_return::return_type(linalg_cast(l)); } + +} + +#endif // GMM_TRANSPOSED_H__ diff --git a/src/linear_algebra/gmm/gmm_tri_solve.h b/src/linear_algebra/gmm/gmm_tri_solve.h new file mode 100644 index 00000000..ad2590f1 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_tri_solve.h @@ -0,0 +1,222 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_tri_solve.h + @author Yves Renard + @date October 13, 2002. + @brief Solve triangular linear system for dense matrices. +*/ + +#ifndef GMM_TRI_SOLVE_H__ +#define GMM_TRI_SOLVE_H__ + +#include "gmm_interface.h" + +namespace gmm { + + template + void upper_tri_solve__(const TriMatrix& T, VecX& x, size_t k, + col_major, abstract_sparse, bool is_unit) { + typename linalg_traits::value_type x_j; + for (int j = int(k) - 1; j >= 0; --j) { + typedef typename linalg_traits::const_sub_col_type COL; + COL c = mat_const_col(T, j); + typename linalg_traits::t>::const_iterator + it = vect_const_begin(c), ite = vect_const_end(c); + if (!is_unit) x[j] /= c[j]; + for (x_j = x[j]; it != ite ; ++it) + if (int(it.index()) < j) x[it.index()] -= x_j * (*it); + } + } + + template + void upper_tri_solve__(const TriMatrix& T, VecX& x, size_t k, + col_major, abstract_dense, bool is_unit) { + typename linalg_traits::value_type x_j; + for (int j = int(k) - 1; j >= 0; --j) { + typedef typename linalg_traits::const_sub_col_type COL; + COL c = mat_const_col(T, j); + typename linalg_traits::t>::const_iterator + it = vect_const_begin(c), ite = it + j; + typename linalg_traits::iterator itx = vect_begin(x); + if (!is_unit) x[j] /= c[j]; + for (x_j = x[j]; it != ite ; ++it, ++itx) *itx -= x_j * (*it); + } + } + + template + void lower_tri_solve__(const TriMatrix& T, VecX& x, size_t k, + col_major, abstract_sparse, bool is_unit) { + typename linalg_traits::value_type x_j; + // cout << "(lower col)The Tri Matrix = " << T << endl; + // cout << "k = " << endl; + for (int j = 0; j < int(k); ++j) { + typedef typename linalg_traits::const_sub_col_type COL; + COL c = mat_const_col(T, j); + typename linalg_traits::t>::const_iterator + it = vect_const_begin(c), ite = vect_const_end(c); + if (!is_unit) x[j] /= c[j]; + for (x_j = x[j]; it != ite ; ++it) + if (int(it.index()) > j && it.index() < k) x[it.index()] -= x_j*(*it); + } + } + + template + void lower_tri_solve__(const TriMatrix& T, VecX& x, size_t k, + col_major, abstract_dense, bool is_unit) { + typename linalg_traits::value_type x_j; + for (int j = 0; j < int(k); ++j) { + typedef typename linalg_traits::const_sub_col_type COL; + COL c = mat_const_col(T, j); + typename linalg_traits::t>::const_iterator + it = vect_const_begin(c) + (j+1), ite = vect_const_begin(c) + k; + typename linalg_traits::iterator itx = vect_begin(x) + (j+1); + if (!is_unit) x[j] /= c[j]; + for (x_j = x[j]; it != ite ; ++it, ++itx) *itx -= x_j * (*it); + } + } + + + template + void upper_tri_solve__(const TriMatrix& T, VecX& x, size_t k, + row_major, abstract_sparse, bool is_unit) { + typedef typename linalg_traits::const_sub_row_type ROW; + typename linalg_traits::value_type t; + typename linalg_traits::const_row_iterator + itr = mat_row_const_end(T); + for (int i = int(k) - 1; i >= 0; --i) { + --itr; + ROW c = linalg_traits::row(itr); + typename linalg_traits::t>::const_iterator + it = vect_const_begin(c), ite = vect_const_end(c); + for (t = x[i]; it != ite; ++it) + if (int(it.index()) > i && it.index() < k) t -= (*it) * x[it.index()]; + if (!is_unit) x[i] = t / c[i]; else x[i] = t; + } + } + + template + void upper_tri_solve__(const TriMatrix& T, VecX& x, size_t k, + row_major, abstract_dense, bool is_unit) { + typename linalg_traits::value_type t; + + for (int i = int(k) - 1; i >= 0; --i) { + typedef typename linalg_traits::const_sub_row_type ROW; + ROW c = mat_const_row(T, i); + typename linalg_traits::t>::const_iterator + it = vect_const_begin(c) + (i + 1), ite = vect_const_begin(c) + k; + typename linalg_traits::iterator itx = vect_begin(x) + (i+1); + + for (t = x[i]; it != ite; ++it, ++itx) t -= (*it) * (*itx); + if (!is_unit) x[i] = t / c[i]; else x[i] = t; + } + } + + template + void lower_tri_solve__(const TriMatrix& T, VecX& x, size_t k, + row_major, abstract_sparse, bool is_unit) { + typename linalg_traits::value_type t; + + for (int i = 0; i < int(k); ++i) { + typedef typename linalg_traits::const_sub_row_type ROW; + ROW c = mat_const_row(T, i); + typename linalg_traits::t>::const_iterator + it = vect_const_begin(c), ite = vect_const_end(c); + + for (t = x[i]; it != ite; ++it) + if (int(it.index()) < i) t -= (*it) * x[it.index()]; + if (!is_unit) x[i] = t / c[i]; else x[i] = t; + } + } + + template + void lower_tri_solve__(const TriMatrix& T, VecX& x, size_t k, + row_major, abstract_dense, bool is_unit) { + typename linalg_traits::value_type t; + + for (int i = 0; i < int(k); ++i) { + typedef typename linalg_traits::const_sub_row_type ROW; + ROW c = mat_const_row(T, i); + typename linalg_traits::t>::const_iterator + it = vect_const_begin(c), ite = it + i; + typename linalg_traits::iterator itx = vect_begin(x); + + for (t = x[i]; it != ite; ++it, ++itx) t -= (*it) * (*itx); + if (!is_unit) x[i] = t / c[i]; else x[i] = t; + } + } + + +// Triangular Solve: x <-- T^{-1} * x + + template inline + void upper_tri_solve(const TriMatrix& T, VecX &x_, bool is_unit = false) + { upper_tri_solve(T, x_, mat_nrows(T), is_unit); } + + template inline + void lower_tri_solve(const TriMatrix& T, VecX &x_, bool is_unit = false) + { lower_tri_solve(T, x_, mat_nrows(T), is_unit); } + + template inline + void upper_tri_solve(const TriMatrix& T, VecX &x_, size_t k, + bool is_unit) { + VecX& x = const_cast(x_); + GMM_ASSERT2(mat_nrows(T) >= k && vect_size(x) >= k + && mat_ncols(T) >= k && !is_sparse(x_), "dimensions mismatch"); + upper_tri_solve__(T, x, k, + typename principal_orientation_type::sub_orientation>::potype(), + typename linalg_traits::storage_type(), + is_unit); + } + + template inline + void lower_tri_solve(const TriMatrix& T, VecX &x_, size_t k, + bool is_unit) { + VecX& x = const_cast(x_); + GMM_ASSERT2(mat_nrows(T) >= k && vect_size(x) >= k + && mat_ncols(T) >= k && !is_sparse(x_), "dimensions mismatch"); + lower_tri_solve__(T, x, k, + typename principal_orientation_type::sub_orientation>::potype(), + typename linalg_traits::storage_type(), + is_unit); + } + + + + + + +} + + +#endif // GMM_TRI_SOLVE_H__ diff --git a/src/linear_algebra/gmm/gmm_vector.h b/src/linear_algebra/gmm/gmm_vector.h new file mode 100644 index 00000000..2e4919bf --- /dev/null +++ b/src/linear_algebra/gmm/gmm_vector.h @@ -0,0 +1,1590 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2002-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ +/**@file gmm_vector.h + @author Yves Renard + @date October 13, 2002. + @brief Declaration of the vector types (gmm::rsvector, gmm::wsvector, + gmm::slvector ,..) +*/ +#ifndef GMM_VECTOR_H__ +#define GMM_VECTOR_H__ + +#include +#include "gmm_interface.h" + +namespace gmm { + + /*************************************************************************/ + /* */ + /* Class ref_elt_vector: reference on a vector component. */ + /* */ + /*************************************************************************/ + + + template class ref_elt_vector { + + V *pm; + size_type l; + + public : + + operator T() const { return pm->r(l); } + ref_elt_vector(V *p, size_type ll) : pm(p), l(ll) {} + inline bool operator ==(T v) const { return ((*pm).r(l) == v); } + inline bool operator !=(T v) const { return ((*pm).r(l) != v); } + inline bool operator ==(std::complex v) const + { return ((*pm).r(l) == v); } + inline bool operator !=(std::complex v) const + { return ((*pm).r(l) != v); } + inline ref_elt_vector &operator +=(T v) + { (*pm).wa(l, v); return *this; } + inline ref_elt_vector &operator -=(T v) + { (*pm).wa(l, -v); return *this; } + inline ref_elt_vector &operator /=(T v) + { (*pm).w(l,(*pm).r(l) / v); return *this; } + inline ref_elt_vector &operator *=(T v) + { (*pm).w(l,(*pm).r(l) * v); return *this; } + inline ref_elt_vector &operator =(const ref_elt_vector &re) + { *this = T(re); return *this; } + inline ref_elt_vector &operator =(T v) + { (*pm).w(l,v); return *this; } + T operator +() { return T(*this); } + T operator -() { return -T(*this); } + T operator +(T v) { return T(*this)+ v; } + T operator -(T v) { return T(*this)- v; } + T operator *(T v) { return T(*this)* v; } + T operator /(T v) { return T(*this)/ v; } + std::complex operator +(std::complex v) { return T(*this)+ v; } + std::complex operator -(std::complex v) { return T(*this)- v; } + std::complex operator *(std::complex v) { return T(*this)* v; } + std::complex operator /(std::complex v) { return T(*this)/ v; } + }; + + template class ref_elt_vector,V> { + + V *pm; + size_type l; + + public : + + operator std::complex() const { return pm->r(l); } + ref_elt_vector(V *p, size_type ll) : pm(p), l(ll) {} + ref_elt_vector(const ref_elt_vector &re) : pm(re.pm), l(re.l) {} + inline bool operator ==(std::complex v) const + { return ((*pm).r(l) == v); } + inline bool operator !=(std::complex v) const + { return ((*pm).r(l) != v); } + inline bool operator ==(T v) const { return ((*pm).r(l) == v); } + inline bool operator !=(T v) const { return ((*pm).r(l) != v); } + inline ref_elt_vector &operator +=(std::complex v) + { (*pm).w(l,(*pm).r(l) + v); return *this; } + inline ref_elt_vector &operator -=(std::complex v) + { (*pm).w(l,(*pm).r(l) - v); return *this; } + inline ref_elt_vector &operator /=(std::complex v) + { (*pm).w(l,(*pm).r(l) / v); return *this; } + inline ref_elt_vector &operator *=(std::complex v) + { (*pm).w(l,(*pm).r(l) * v); return *this; } + inline ref_elt_vector &operator =(const ref_elt_vector &re) + { *this = std::complex(re); return *this; } + inline ref_elt_vector &operator =(std::complex v) + { (*pm).w(l,v); return *this; } + inline ref_elt_vector &operator =(T v) + { (*pm).w(l,std::complex(v)); return *this; } + inline ref_elt_vector &operator +=(T v) + { (*pm).w(l,(*pm).r(l) + v); return *this; } + inline ref_elt_vector &operator -=(T v) + { (*pm).w(l,(*pm).r(l) - v); return *this; } + inline ref_elt_vector &operator /=(T v) + { (*pm).w(l,(*pm).r(l) / v); return *this; } + inline ref_elt_vector &operator *=(T v) + { (*pm).w(l,(*pm).r(l) * v); return *this; } + std::complex operator +() { return std::complex(*this); } + std::complex operator -() { return -std::complex(*this); } + std::complex operator +(T v) { return std::complex(*this)+ v; } + std::complex operator -(T v) { return std::complex(*this)- v; } + std::complex operator *(T v) { return std::complex(*this)* v; } + std::complex operator /(T v) { return std::complex(*this)/ v; } + std::complex operator +(std::complex v) + { return std::complex(*this)+ v; } + std::complex operator -(std::complex v) + { return std::complex(*this)- v; } + std::complex operator *(std::complex v) + { return std::complex(*this)* v; } + std::complex operator /(std::complex v) + { return std::complex(*this)/ v; } + }; + + + template inline + bool operator ==(T v, const ref_elt_vector &re) { return (v==T(re)); } + template inline + bool operator !=(T v, const ref_elt_vector &re) { return (v!=T(re)); } + template inline + T &operator +=(T &v, const ref_elt_vector &re) + { v += T(re); return v; } + template inline + T &operator -=(T &v, const ref_elt_vector &re) + { v -= T(re); return v; } + template inline + T &operator *=(T &v, const ref_elt_vector &re) + { v *= T(re); return v; } + template inline + T &operator /=(T &v, const ref_elt_vector &re) + { v /= T(re); return v; } + template inline + T operator +(T v, const ref_elt_vector &re) { return v+ T(re); } + template inline + T operator -(T v, const ref_elt_vector &re) { return v- T(re); } + template inline + T operator *(T v, const ref_elt_vector &re) { return v* T(re); } + template inline + T operator /(T v, const ref_elt_vector &re) { return v/ T(re); } + template inline + std::complex operator +(std::complex v, const ref_elt_vector &re) + { return v+ T(re); } + template inline + std::complex operator -(std::complex v, const ref_elt_vector &re) + { return v- T(re); } + template inline + std::complex operator *(std::complex v, const ref_elt_vector &re) + { return v* T(re); } + template inline + std::complex operator /(std::complex v, const ref_elt_vector &re) + { return v/ T(re); } + template inline + std::complex operator +(T v, const ref_elt_vector, V> &re) + { return v+ std::complex(re); } + template inline + std::complex operator -(T v, const ref_elt_vector, V> &re) + { return v- std::complex(re); } + template inline + std::complex operator *(T v, const ref_elt_vector, V> &re) + { return v* std::complex(re); } + template inline + std::complex operator /(T v, const ref_elt_vector, V> &re) + { return v/ std::complex(re); } + template inline + typename number_traits::magnitude_type + abs(const ref_elt_vector &re) { return gmm::abs(T(re)); } + template inline + T sqr(const ref_elt_vector &re) { return gmm::sqr(T(re)); } + template inline + typename number_traits::magnitude_type + abs_sqr(const ref_elt_vector &re) { return gmm::abs_sqr(T(re)); } + template inline + T conj(const ref_elt_vector &re) { return gmm::conj(T(re)); } + template std::ostream &operator << + (std::ostream &o, const ref_elt_vector &re) { o << T(re); return o; } + template inline + typename number_traits::magnitude_type + real(const ref_elt_vector &re) { return gmm::real(T(re)); } + template inline + typename number_traits::magnitude_type + imag(const ref_elt_vector &re) { return gmm::imag(T(re)); } + + /*************************************************************************/ + /* */ + /* Class dsvector: sparse vector optimized for random write operations */ + /* with constant complexity for read and write operations. */ + /* Based on distribution sort principle. */ + /* Cheap for densely populated vectors. */ + /* */ + /*************************************************************************/ + + template class dsvector; + + template struct dsvector_iterator { + size_type i; // Current index. + T* p; // Pointer to the current position. + dsvector *v; // Pointer to the vector. + + typedef T value_type; + typedef value_type* pointer; + typedef const value_type* const_pointer; + typedef value_type& reference; + // typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + typedef dsvector_iterator iterator; + + reference operator *() const { return *p; } + pointer operator->() const { return &(operator*()); } + + iterator &operator ++() { + for (size_type k = (i & 15); k < 15; ++k) + { ++p; ++i; if (*p != T(0)) return *this; } + v->next_pos(*(const_cast(&(p))), i); + return *this; + } + iterator operator ++(int) { iterator tmp = *this; ++(*this); return tmp; } + iterator &operator --() { + for (size_type k = (i & 15); k > 0; --k) + { --p; --i; if (*p != T(0)) return *this; } + v->previous_pos(p, i); + return *this; + } + iterator operator --(int) { iterator tmp = *this; --(*this); return tmp; } + + bool operator ==(const iterator &it) const + { return (i == it.i && p == it.p && v == it.v); } + bool operator !=(const iterator &it) const + { return !(it == *this); } + + size_type index() const { return i; } + + dsvector_iterator() : i(size_type(-1)), p(0), v(0) {} + dsvector_iterator(dsvector &w) : i(size_type(-1)), p(0), v(&w) {}; + }; + + + template struct dsvector_const_iterator { + size_type i; // Current index. + const T* p; // Pointer to the current position. + const dsvector *v; // Pointer to the vector. + + typedef T value_type; + typedef const value_type* pointer; + typedef const value_type& reference; + // typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + typedef dsvector_const_iterator iterator; + + reference operator *() const { return *p; } + pointer operator->() const { return &(operator*()); } + iterator &operator ++() { + for (size_type k = (i & 15); k < 15; ++k) + { ++p; ++i; if (*p != T(0)) return *this; } + v->next_pos(p, i); + return *this; + } + iterator operator ++(int) { iterator tmp = *this; ++(*this); return tmp; } + iterator &operator --() { + for (size_type k = (i & 15); k > 0; --k) + { --p; --i; if (*p != T(0)) return *this; } + v->previous_pos(p, i); + return *this; + } + iterator operator --(int) { iterator tmp = *this; --(*this); return tmp; } + + bool operator ==(const iterator &it) const + { return (i == it.i && p == it.p && v == it.v); } + bool operator !=(const iterator &it) const + { return !(it == *this); } + + size_type index() const { return i; } + + dsvector_const_iterator() : i(size_type(-1)), p(0) {} + dsvector_const_iterator(const dsvector_iterator &it) + : i(it.i), p(it.p), v(it.v) {} + dsvector_const_iterator(const dsvector &w) + : i(size_type(-1)), p(0), v(&w) {}; + }; + + + /** + Sparse vector built on distribution sort principle. + Read and write access have a constant complexity depending only on the + vector size. + */ + template class dsvector { + + typedef dsvector_iterator iterator; + typedef dsvector_const_iterator const_iterator; + typedef dsvector this_type; + typedef T * pointer; + typedef const T * const_pointer; + typedef void * void_pointer; + typedef const void * const_void_pointer; + + protected: + size_type n; // Potential vector size + size_type depth; // Number of row of pointer arrays + size_type mask; // Mask for the first pointer array + size_type shift; // Shift for the first pointer array + void_pointer root_ptr; // Root pointer + + const T *read_access(size_type i) const { + GMM_ASSERT1(i < n, "index out of range"); + size_type my_mask = mask, my_shift = shift; + void_pointer p = root_ptr; + if (!p) return 0; + for (size_type k = 0; k < depth; ++k) { + p = ((void **)(p))[(i & my_mask) >> my_shift]; + if (!p) return 0; + my_mask = (my_mask >> 4); + my_shift -= 4; + } + GMM_ASSERT1(my_shift == 0, "internal error"); + GMM_ASSERT1(my_mask == 15, "internal error"); + return &(((const T *)(p))[i & 15]); + } + + T *write_access(size_type i) { + GMM_ASSERT1(i < n, "index " << i << " out of range (size " << n << ")"); + size_type my_mask = mask, my_shift = shift; + if (!root_ptr) { + if (depth) { + root_ptr = new void_pointer[16]; + std::memset(root_ptr, 0, 16*sizeof(void_pointer)); + } else { + root_ptr = new T[16]; + for (size_type l = 0; l < 16; ++l) ((T *)(root_ptr))[l] = T(0); + } + } + + void_pointer p = root_ptr; + for (size_type k = 0; k < depth; ++k) { + size_type j = (i & my_mask) >> my_shift; + void_pointer q = ((void_pointer *)(p))[j]; + if (!q) { + if (k+1 != depth) { + q = new void_pointer[16]; + std::memset(q, 0, 16*sizeof(void_pointer)); + } else { + q = new T[16]; + for (size_type l = 0; l < 16; ++l) ((T *)(q))[l] = T(0); + } + ((void_pointer *)(p))[j] = q; + } + p = q; + my_mask = (my_mask >> 4); + my_shift -= 4; + } + GMM_ASSERT1(my_shift == 0, "internal error"); + GMM_ASSERT1(my_mask == 15, "internal error " << my_mask); + return &(((T *)(p))[i & 15]); + } + + void init(size_type n_) { + n = n_; depth = 0; shift = 0; mask = 1; if (n_) --n_; + while (n_) { n_ /= 16; ++depth; shift += 4; mask *= 16; } + mask--; if (shift) shift -= 4; if (depth) --depth; + root_ptr = 0; + } + + void rec_del(void_pointer p, size_type my_depth) { + if (my_depth) { + for (size_type k = 0; k < 16; ++k) + if (((void_pointer *)(p))[k]) + rec_del(((void_pointer *)(p))[k], my_depth-1); + delete[] ((void_pointer *)(p)); + } else { + delete[] ((T *)(p)); + } + } + + void rec_clean(void_pointer p, size_type my_depth, double eps) { + if (my_depth) { + for (size_type k = 0; k < 16; ++k) + if (((void_pointer *)(p))[k]) + rec_clean(((void_pointer *)(p))[k], my_depth-1, eps); + } else { + for (size_type k = 0; k < 16; ++k) + if (gmm::abs(((T *)(p))[k]) <= eps) ((T *)(p))[k] = T(0); + } + } + + void rec_clean_i(void_pointer p, size_type my_depth, size_type my_mask, + size_type i, size_type base) { + if (my_depth) { + my_mask = (my_mask >> 4); + for (size_type k = 0; k < 16; ++k) + if (((void_pointer *)(p))[k] && (base + (k+1)*(mask+1)) >= i) + rec_clean_i(((void_pointer *)(p))[k], my_depth-1, my_mask, + i, base + k*(my_mask+1)); + } else { + for (size_type k = 0; k < 16; ++k) + if (base+k > i) ((T *)(p))[k] = T(0); + } + } + + + size_type rec_nnz(void_pointer p, size_type my_depth) const { + size_type nn = 0; + if (my_depth) { + for (size_type k = 0; k < 16; ++k) + if (((void_pointer *)(p))[k]) + nn += rec_nnz(((void_pointer *)(p))[k], my_depth-1); + } else { + for (size_type k = 0; k < 16; ++k) + if (((const T *)(p))[k] != T(0)) nn++; + } + return nn; + } + + void copy_rec(void_pointer &p, const_void_pointer q, size_type my_depth) { + if (my_depth) { + p = new void_pointer[16]; + std::memset(p, 0, 16*sizeof(void_pointer)); + for (size_type l = 0; l < 16; ++l) + if (((const const_void_pointer *)(q))[l]) + copy_rec(((void_pointer *)(p))[l], + ((const const_void_pointer *)(q))[l], my_depth-1); + } else { + p = new T[16]; + for (size_type l = 0; l < 16; ++l) ((T *)(p))[l] = ((const T *)(q))[l]; + } + } + + void copy(const dsvector &v) { + if (root_ptr) rec_del(root_ptr, depth); + root_ptr = 0; + mask = v.mask; depth = v.depth; n = v.n; shift = v.shift; + if (v.root_ptr) copy_rec(root_ptr, v.root_ptr, depth); + } + + void next_pos_rec(void_pointer p, size_type my_depth, size_type my_mask, + const_pointer &pp, size_type &i, size_type base) const { + size_type ii = i; + if (my_depth) { + my_mask = (my_mask >> 4); + for (size_type k = 0; k < 16; ++k) + if (((void_pointer *)(p))[k] && (base + (k+1)*(my_mask+1)) >= i) { + next_pos_rec(((void_pointer *)(p))[k], my_depth-1, my_mask, + pp, i, base + k*(my_mask+1)); + if (i != size_type(-1)) return; else i = ii; + } + i = size_type(-1); pp = 0; + } else { + for (size_type k = 0; k < 16; ++k) + if (base+k > i && ((const_pointer)(p))[k] != T(0)) + { i = base+k; pp = &(((const_pointer)(p))[k]); return; } + i = size_type(-1); pp = 0; + } + } + + void previous_pos_rec(void_pointer p, size_type my_depth, size_type my_mask, + const_pointer &pp, size_type &i, + size_type base) const { + size_type ii = i; + if (my_depth) { + my_mask = (my_mask >> 4); + for (size_type k = 15; k != size_type(-1); --k) + if (((void_pointer *)(p))[k] && ((base + k*(my_mask+1)) < i)) { + previous_pos_rec(((void_pointer *)(p))[k], my_depth-1, + my_mask, pp, i, base + k*(my_mask+1)); + if (i != size_type(-1)) return; else i = ii; + } + i = size_type(-1); pp = 0; + } else { + for (size_type k = 15; k != size_type(-1); --k) + if (base+k < i && ((const_pointer)(p))[k] != T(0)) + { i = base+k; pp = &(((const_pointer)(p))[k]); return; } + i = size_type(-1); pp = 0; + } + } + + + public: + void clean(double eps) { if (root_ptr) rec_clean(root_ptr, depth); } + void resize(size_type n_) { + if (n_ != n) { + n = n_; + if (n_ < n) { // Depth unchanged (a choice) + if (root_ptr) rec_clean_i(root_ptr, depth, mask, n_, 0); + } else { + // may change the depth (add some levels) + size_type my_depth = 0, my_shift = 0, my_mask = 1; if (n_) --n_; + while (n_) { n_ /= 16; ++my_depth; my_shift += 4; my_mask *= 16; } + my_mask--; if (my_shift) my_shift -= 4; if (my_depth) --my_depth; + if (my_depth > depth || depth == 0) { + if (root_ptr) { + for (size_type k = depth; k < my_depth; ++k) { + void_pointer *q = new void_pointer [16]; + std::memset(q, 0, 16*sizeof(void_pointer)); + q[0] = root_ptr; root_ptr = q; + } + } + mask = my_mask; depth = my_depth; shift = my_shift; + } + } + } + } + + void clear() { if (root_ptr) rec_del(root_ptr, depth); root_ptr = 0; } + + void next_pos(const_pointer &pp, size_type &i) const { + if (!root_ptr || i >= n) { pp = 0, i = size_type(-1); return; } + next_pos_rec(root_ptr, depth, mask, pp, i, 0); + } + + void previous_pos(const_pointer &pp, size_type &i) const { + if (!root_ptr) { pp = 0, i = size_type(-1); return; } + if (i == size_type(-1)) { i = n; } + previous_pos_rec(root_ptr, depth, mask, pp, i, 0); + } + + iterator begin() { + iterator it(*this); + if (n && root_ptr) { + it.i = 0; it.p = const_cast(read_access(0)); + if (!(it.p) || *(it.p) == T(0)) + next_pos(*(const_cast(&(it.p))), it.i); + } + return it; + } + + iterator end() { return iterator(*this); } + + const_iterator begin() const { + const_iterator it(*this); + if (n && root_ptr) { + it.i = 0; it.p = read_access(0); + if (!(it.p) || *(it.p) == T(0)) next_pos(it.p, it.i); + } + return it; + } + + const_iterator end() const { return const_iterator(*this); } + + inline ref_elt_vector > operator [](size_type c) + { return ref_elt_vector >(this, c); } + + inline void w(size_type c, const T &e) { + if (e == T(0)) { if (read_access(c)) *(write_access(c)) = e; } + else *(write_access(c)) = e; + } + + inline void wa(size_type c, const T &e) + { if (e != T(0)) { *(write_access(c)) += e; } } + + inline T r(size_type c) const + { const T *p = read_access(c); if (p) return *p; else return T(0); } + + inline T operator [](size_type c) const { return r(c); } + + size_type nnz() const + { if (root_ptr) return rec_nnz(root_ptr, depth); else return 0; } + size_type size() const { return n; } + + void swap(dsvector &v) { + std::swap(n, v.n); std::swap(root_ptr, v.root_ptr); + std::swap(depth, v.depth); std::swap(shift, v.shift); + std::swap(mask, v.mask); + } + + /* Constructors */ + dsvector(const dsvector &v) { init(0); copy(v); } + dsvector &operator =(const dsvector &v) { copy(v); return *this; } + explicit dsvector(size_type l){ init(l); } + dsvector() { init(0); } + ~dsvector() { if (root_ptr) rec_del(root_ptr, depth); root_ptr = 0; } + }; + + template struct linalg_traits> { + typedef dsvector this_type; + typedef this_type origin_type; + typedef linalg_false is_reference; + typedef abstract_vector linalg_type; + typedef T value_type; + typedef ref_elt_vector > reference; + typedef dsvector_iterator iterator; + typedef dsvector_const_iterator const_iterator; + typedef abstract_sparse storage_type; + typedef linalg_true index_sorted; + static size_type size(const this_type &v) { return v.size(); } + static iterator begin(this_type &v) { return v.begin(); } + static const_iterator begin(const this_type &v) { return v.begin(); } + static iterator end(this_type &v) { return v.end(); } + static const_iterator end(const this_type &v) { return v.end(); } + static origin_type* origin(this_type &v) { return &v; } + static const origin_type* origin(const this_type &v) { return &v; } + static void clear(origin_type* o, const iterator &, const iterator &) + { o->clear(); } + static void do_clear(this_type &v) { v.clear(); } + static value_type access(const origin_type *o, const const_iterator &, + const const_iterator &, size_type i) + { return (*o)[i]; } + static reference access(origin_type *o, const iterator &, const iterator &, + size_type i) + { return (*o)[i]; } + static void resize(this_type &v, size_type n) { v.resize(n); } + }; + + template std::ostream &operator << + (std::ostream &o, const dsvector& v) { gmm::write(o,v); return o; } + + /******* Optimized operations for dsvector ****************************/ + + template inline void copy(const dsvector &v1, + dsvector &v2) { + GMM_ASSERT2(v1.size() == v2.size(), "dimensions mismatch"); + v2 = v1; + } + template inline void copy(const dsvector &v1, + const dsvector &v2) { + GMM_ASSERT2(v1.size() == v2.size(), "dimensions mismatch"); + v2 = const_cast &>(v1); + } + template inline + void copy(const dsvector &v1, const simple_vector_ref *> &v2){ + simple_vector_ref *> + *svr = const_cast *> *>(&v2); + dsvector + *pv = const_cast *>((v2.origin)); + GMM_ASSERT2(vect_size(v1) == vect_size(v2), "dimensions mismatch"); + *pv = v1; svr->begin_ = vect_begin(*pv); svr->end_ = vect_end(*pv); + } + template inline + void copy(const simple_vector_ref *> &v1, + dsvector &v2) + { copy(*(v1.origin), v2); } + template inline + void copy(const simple_vector_ref *> &v1, dsvector &v2) + { copy(*(v1.origin), v2); } + template inline + void copy(const simple_vector_ref *> &v1, + const simple_vector_ref *> &v2) + { copy(*(v1.origin), v2); } + template inline + void copy(const simple_vector_ref *> &v1, + const simple_vector_ref *> &v2) + { copy(*(v1.origin), v2); } + + template + inline size_type nnz(const dsvector& l) { return l.nnz(); } + + /*************************************************************************/ + /* */ + /* Class wsvector: sparse vector optimized for random write operations, */ + /* with log(n) complexity for read and write operations. */ + /* Based on std::map */ + /* */ + /*************************************************************************/ + + template struct wsvector_iterator + : public std::map::iterator { + typedef typename std::map::iterator base_it_type; + typedef T value_type; + typedef value_type* pointer; + typedef value_type& reference; + // typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + + reference operator *() const { return (base_it_type::operator*()).second; } + pointer operator->() const { return &(operator*()); } + size_type index() const { return (base_it_type::operator*()).first; } + + wsvector_iterator() {} + wsvector_iterator(const base_it_type &it) : base_it_type(it) {} + }; + + template struct wsvector_const_iterator + : public std::map::const_iterator { + typedef typename std::map::const_iterator base_it_type; + typedef T value_type; + typedef const value_type* pointer; + typedef const value_type& reference; + // typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + + reference operator *() const { return (base_it_type::operator*()).second; } + pointer operator->() const { return &(operator*()); } + size_type index() const { return (base_it_type::operator*()).first; } + + wsvector_const_iterator() {} + wsvector_const_iterator(const wsvector_iterator &it) + : base_it_type(typename std::map::iterator(it)) {} + wsvector_const_iterator(const base_it_type &it) : base_it_type(it) {} + }; + + + /** + sparse vector built upon std::map. + Read and write access are quite fast (log n) + */ + template class wsvector : public std::map { + public: + + typedef typename std::map::size_type size_type; + typedef std::map base_type; + typedef typename base_type::iterator iterator; + typedef typename base_type::const_iterator const_iterator; + + protected: + size_type nbl; + + public: + void clean(double eps); + void resize(size_type); + + inline ref_elt_vector > operator [](size_type c) + { return ref_elt_vector >(this, c); } + + inline void w(size_type c, const T &e) { + GMM_ASSERT2(c < nbl, "out of range"); + if (e == T(0)) { this->erase(c); } + else base_type::operator [](c) = e; + } + + inline void wa(size_type c, const T &e) { + GMM_ASSERT2(c < nbl, "out of range"); + if (e != T(0)) { + iterator it = this->lower_bound(c); + if (it != this->end() && it->first == c) it->second += e; + else base_type::operator [](c) = e; + } + } + + inline T r(size_type c) const { + GMM_ASSERT2(c < nbl, "out of range"); + const_iterator it = this->lower_bound(c); + if (it != this->end() && c == it->first) return it->second; + else return T(0); + } + + inline T operator [](size_type c) const { return r(c); } + + size_type nb_stored() const { return base_type::size(); } + size_type size() const { return nbl; } + + void swap(wsvector &v) + { std::swap(nbl, v.nbl); std::map::swap(v); } + + + /* Constructors */ + void init(size_type l) { nbl = l; this->clear(); } + explicit wsvector(size_type l){ init(l); } + wsvector() { init(0); } + }; + + template void wsvector::clean(double eps) { + iterator it = this->begin(), itf = it, ite = this->end(); + while (it != ite) { + ++itf; if (gmm::abs(it->second) <= eps) this->erase(it); it = itf; + } + } + + template void wsvector::resize(size_type n) { + if (n < nbl) { + iterator it = this->begin(), itf = it, ite = this->end(); + while (it != ite) { ++itf; if (it->first >= n) this->erase(it); it=itf; } + } + nbl = n; + } + + template struct linalg_traits > { + typedef wsvector this_type; + typedef this_type origin_type; + typedef linalg_false is_reference; + typedef abstract_vector linalg_type; + typedef T value_type; + typedef ref_elt_vector > reference; + typedef wsvector_iterator iterator; + typedef wsvector_const_iterator const_iterator; + typedef abstract_sparse storage_type; + typedef linalg_true index_sorted; + static size_type size(const this_type &v) { return v.size(); } + static iterator begin(this_type &v) { return v.begin(); } + static const_iterator begin(const this_type &v) { return v.begin(); } + static iterator end(this_type &v) { return v.end(); } + static const_iterator end(const this_type &v) { return v.end(); } + static origin_type* origin(this_type &v) { return &v; } + static const origin_type* origin(const this_type &v) { return &v; } + static void clear(origin_type* o, const iterator &, const iterator &) + { o->clear(); } + static void do_clear(this_type &v) { v.clear(); } + static value_type access(const origin_type *o, const const_iterator &, + const const_iterator &, size_type i) + { return (*o)[i]; } + static reference access(origin_type *o, const iterator &, const iterator &, + size_type i) + { return (*o)[i]; } + static void resize(this_type &v, size_type n) { v.resize(n); } + }; + + template std::ostream &operator << + (std::ostream &o, const wsvector& v) { gmm::write(o,v); return o; } + + /******* Optimized BLAS for wsvector **********************************/ + + template inline void copy(const wsvector &v1, + wsvector &v2) { + GMM_ASSERT2(vect_size(v1) == vect_size(v2), "dimensions mismatch"); + v2 = v1; + } + template inline + void copy(const wsvector &v1, const simple_vector_ref *> &v2){ + simple_vector_ref *> + *svr = const_cast *> *>(&v2); + wsvector + *pv = const_cast *>(v2.origin); + GMM_ASSERT2(vect_size(v1) == vect_size(v2), "dimensions mismatch"); + *pv = v1; svr->begin_ = vect_begin(*pv); svr->end_ = vect_end(*pv); + } + template inline + void copy(const simple_vector_ref *> &v1, + wsvector &v2) + { copy(*(v1.origin), v2); } + template inline + void copy(const simple_vector_ref *> &v1, wsvector &v2) + { copy(*(v1.origin), v2); } + + template inline void clean(wsvector &v, double eps) { + typedef typename number_traits::magnitude_type R; + typename wsvector::iterator it = v.begin(), ite = v.end(), itc; + while (it != ite) + if (gmm::abs((*it).second) <= R(eps)) + { itc=it; ++it; v.erase(itc); } else ++it; + } + + template + inline void clean(const simple_vector_ref *> &l, double eps) { + simple_vector_ref *> + *svr = const_cast *> *>(&l); + wsvector + *pv = const_cast *>((l.origin)); + clean(*pv, eps); + svr->begin_ = vect_begin(*pv); svr->end_ = vect_end(*pv); + } + + template + inline size_type nnz(const wsvector& l) { return l.nb_stored(); } + + /*************************************************************************/ + /* */ + /* rsvector: sparse vector optimized for linear algebra operations. */ + /* */ + /*************************************************************************/ + + template struct elt_rsvector_ { + size_type c; T e; + /* e is initialized by default to avoid some false warnings of valgrind. + (from http://valgrind.org/docs/manual/mc-manual.html: + + When memory is read into the CPU's floating point registers, the + relevant V bits are read from memory and they are immediately + checked. If any are invalid, an uninitialised value error is + emitted. This precludes using the floating-point registers to copy + possibly-uninitialised memory, but simplifies Valgrind in that it + does not have to track the validity status of the floating-point + registers. + */ + elt_rsvector_() : e(0) {} + elt_rsvector_(size_type cc) : c(cc), e(0) {} + elt_rsvector_(size_type cc, const T &ee) : c(cc), e(ee) {} + bool operator < (const elt_rsvector_ &a) const { return c < a.c; } + bool operator == (const elt_rsvector_ &a) const { return c == a.c; } + bool operator != (const elt_rsvector_ &a) const { return c != a.c; } + }; + + template struct rsvector_iterator { + typedef typename std::vector >::iterator IT; + typedef T value_type; + typedef value_type* pointer; + typedef value_type& reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef std::bidirectional_iterator_tag iterator_category; + typedef rsvector_iterator iterator; + + IT it; + + reference operator *() const { return it->e; } + pointer operator->() const { return &(operator*()); } + + iterator &operator ++() { ++it; return *this; } + iterator operator ++(int) { iterator tmp = *this; ++(*this); return tmp; } + iterator &operator --() { --it; return *this; } + iterator operator --(int) { iterator tmp = *this; --(*this); return tmp; } + + bool operator ==(const iterator &i) const { return it == i.it; } + bool operator !=(const iterator &i) const { return !(i == *this); } + + size_type index() const { return it->c; } + rsvector_iterator() {} + rsvector_iterator(const IT &i) : it(i) {} + }; + + template struct rsvector_const_iterator { + typedef typename std::vector >::const_iterator IT; + typedef T value_type; + typedef const value_type* pointer; + typedef const value_type& reference; + typedef size_t size_type; + typedef ptrdiff_t difference_type; + typedef std::forward_iterator_tag iterator_category; + typedef rsvector_const_iterator iterator; + + IT it; + + reference operator *() const { return it->e; } + pointer operator->() const { return &(operator*()); } + size_type index() const { return it->c; } + + iterator &operator ++() { ++it; return *this; } + iterator operator ++(int) { iterator tmp = *this; ++(*this); return tmp; } + iterator &operator --() { --it; return *this; } + iterator operator --(int) { iterator tmp = *this; --(*this); return tmp; } + + bool operator ==(const iterator &i) const { return it == i.it; } + bool operator !=(const iterator &i) const { return !(i == *this); } + + rsvector_const_iterator() {} + rsvector_const_iterator(const rsvector_iterator &i) : it(i.it) {} + rsvector_const_iterator(const IT &i) : it(i) {} + }; + + /** + sparse vector built upon std::vector. Read access is fast, + but insertion is O(n) + */ + template class rsvector : public std::vector > { + public: + + typedef std::vector > base_type_; + typedef typename base_type_::iterator iterator; + typedef typename base_type_::const_iterator const_iterator; + typedef typename base_type_::size_type size_type; + typedef T value_type; + + protected: + size_type nbl; // size of the vector + + public: + + void sup(size_type j); + void base_resize(size_type n) { base_type_::resize(n); } + void resize(size_type); + + ref_elt_vector > operator [](size_type c) + { return ref_elt_vector >(this, c); } + + void w(size_type c, const T &e); + void wa(size_type c, const T &e); + T r(size_type c) const; + void swap_indices(size_type i, size_type j); + + inline T operator [](size_type c) const { return r(c); } + + size_type nb_stored() const { return base_type_::size(); } + size_type size() const { return nbl; } + void clear() { base_type_::resize(0); } + void swap(rsvector &v) + { std::swap(nbl, v.nbl); std::vector >::swap(v); } + + /* Constructeurs */ + explicit rsvector(size_type l) : nbl(l) { } + rsvector() : nbl(0) { } + }; + + template + void rsvector::swap_indices(size_type i, size_type j) { + if (i > j) std::swap(i, j); + if (i != j) { + int situation = 0; + elt_rsvector_ ei(i), ej(j), a; + iterator it, ite, iti, itj; + iti = std::lower_bound(this->begin(), this->end(), ei); + if (iti != this->end() && iti->c == i) situation += 1; + itj = std::lower_bound(this->begin(), this->end(), ej); + if (itj != this->end() && itj->c == j) situation += 2; + + switch (situation) { + case 1 : a = *iti; a.c = j; it = iti; ++it; ite = this->end(); + for (; it != ite && it->c <= j; ++it, ++iti) *iti = *it; + *iti = a; + break; + case 2 : a = *itj; a.c = i; it = itj; ite = this->begin(); + if (it != ite) { + --it; + while (it->c >= i) { *itj = *it; --itj; if (it==ite) break; --it; } + } + *itj = a; + break; + case 3 : std::swap(iti->e, itj->e); + break; + } + } + } + + template void rsvector::sup(size_type j) { + if (nb_stored() != 0) { + elt_rsvector_ ev(j); + iterator it = std::lower_bound(this->begin(), this->end(), ev); + if (it != this->end() && it->c == j) { + for (iterator ite = this->end() - 1; it != ite; ++it) *it = *(it+1); + base_resize(nb_stored()-1); + } + } + } + + template void rsvector::resize(size_type n) { + if (n < nbl) { + for (size_type i = 0; i < nb_stored(); ++i) + if (base_type_::operator[](i).c >= n) { base_resize(i); break; } + } + nbl = n; + } + + template void rsvector::w(size_type c, const T &e) { + GMM_ASSERT2(c < nbl, "out of range"); + if (e == T(0)) sup(c); + else { + elt_rsvector_ ev(c, e); + if (nb_stored() == 0) { + base_type_::push_back(ev); + } + else { + iterator it = std::lower_bound(this->begin(), this->end(), ev); + if (it != this->end() && it->c == c) it->e = e; + else { + size_type ind = it - this->begin(), nb = this->nb_stored(); + if (nb - ind > 1100) + GMM_WARNING2("Inefficient addition of element in rsvector with " + << this->nb_stored() - ind << " non-zero entries"); + base_type_::push_back(ev); + if (ind != nb) { + it = this->begin() + ind; + iterator ite = this->end(); --ite; iterator itee = ite; + for (; ite != it; --ite) { --itee; *ite = *itee; } + *it = ev; + } + } + } + } + } + + template void rsvector::wa(size_type c, const T &e) { + GMM_ASSERT2(c < nbl, "out of range"); + if (e != T(0)) { + elt_rsvector_ ev(c, e); + if (nb_stored() == 0) { + base_type_::push_back(ev); + } + else { + iterator it = std::lower_bound(this->begin(), this->end(), ev); + if (it != this->end() && it->c == c) it->e += e; + else { + size_type ind = it - this->begin(), nb = this->nb_stored(); + if (nb - ind > 1100) + GMM_WARNING2("Inefficient addition of element in rsvector with " + << this->nb_stored() - ind << " non-zero entries"); + base_type_::push_back(ev); + if (ind != nb) { + it = this->begin() + ind; + iterator ite = this->end(); --ite; iterator itee = ite; + for (; ite != it; --ite) { --itee; *ite = *itee; } + *it = ev; + } + } + } + } + } + + template T rsvector::r(size_type c) const { + GMM_ASSERT2(c < nbl, "out of range. Index " << c + << " for a length of " << nbl); + if (nb_stored() != 0) { + elt_rsvector_ ev(c); + const_iterator it = std::lower_bound(this->begin(), this->end(), ev); + if (it != this->end() && it->c == c) return it->e; + } + return T(0); + } + + template struct linalg_traits > { + typedef rsvector this_type; + typedef this_type origin_type; + typedef linalg_false is_reference; + typedef abstract_vector linalg_type; + typedef T value_type; + typedef ref_elt_vector > reference; + typedef rsvector_iterator iterator; + typedef rsvector_const_iterator const_iterator; + typedef abstract_sparse storage_type; + typedef linalg_true index_sorted; + static size_type size(const this_type &v) { return v.size(); } + static iterator begin(this_type &v) { return iterator(v.begin()); } + static const_iterator begin(const this_type &v) + { return const_iterator(v.begin()); } + static iterator end(this_type &v) { return iterator(v.end()); } + static const_iterator end(const this_type &v) + { return const_iterator(v.end()); } + static origin_type* origin(this_type &v) { return &v; } + static const origin_type* origin(const this_type &v) { return &v; } + static void clear(origin_type* o, const iterator &, const iterator &) + { o->clear(); } + static void do_clear(this_type &v) { v.clear(); } + static value_type access(const origin_type *o, const const_iterator &, + const const_iterator &, size_type i) + { return (*o)[i]; } + static reference access(origin_type *o, const iterator &, const iterator &, + size_type i) + { return (*o)[i]; } + static void resize(this_type &v, size_type n) { v.resize(n); } + }; + + template std::ostream &operator << + (std::ostream &o, const rsvector& v) { gmm::write(o,v); return o; } + + /******* Optimized operations for rsvector ****************************/ + + template inline void copy(const rsvector &v1, + rsvector &v2) { + GMM_ASSERT2(vect_size(v1) == vect_size(v2), "dimensions mismatch"); + v2 = v1; + } + template inline + void copy(const rsvector &v1, const simple_vector_ref *> &v2){ + simple_vector_ref *> + *svr = const_cast *> *>(&v2); + rsvector + *pv = const_cast *>((v2.origin)); + GMM_ASSERT2(vect_size(v1) == vect_size(v2), "dimensions mismatch"); + *pv = v1; svr->begin_ = vect_begin(*pv); svr->end_ = vect_end(*pv); + } + template inline + void copy(const simple_vector_ref *> &v1, + rsvector &v2) + { copy(*(v1.origin), v2); } + template inline + void copy(const simple_vector_ref *> &v1, rsvector &v2) + { copy(*(v1.origin), v2); } + + template inline void add(const V &v1, + rsvector &v2) { + if ((const void *)(&v1) != (const void *)(&v2)) { + GMM_ASSERT2(vect_size(v1) == vect_size(v2), "dimensions mismatch"); + add_rsvector(v1, v2, typename linalg_traits::storage_type()); + } + } + + template + inline void add_rsvector(const V &v1, rsvector &v2, abstract_dense) + { add(v1, v2, abstract_dense(), abstract_sparse()); } + + template + inline void add_rsvector(const V &v1, rsvector &v2, abstract_skyline) + { add(v1, v2, abstract_skyline(), abstract_sparse()); } + + template + void add_rsvector(const V &v1, rsvector &v2, abstract_sparse) { + add_rsvector(v1, v2, typename linalg_traits::index_sorted()); + } + + template + void add_rsvector(const V &v1, rsvector &v2, linalg_false) { + add(v1, v2, abstract_sparse(), abstract_sparse()); + } + + template + void add_rsvector(const V &v1, rsvector &v2, linalg_true) { + typename linalg_traits::const_iterator it1 = vect_const_begin(v1), + ite1 = vect_const_end(v1); + typename rsvector::iterator it2 = v2.begin(), ite2 = v2.end(), it3; + size_type nbc = 0, old_nbc = v2.nb_stored(); + for (; it1 != ite1 && it2 != ite2 ; ++nbc) + if (it1.index() == it2->c) { ++it1; ++it2; } + else if (it1.index() < it2->c) ++it1; else ++it2; + for (; it1 != ite1; ++it1) ++nbc; + for (; it2 != ite2; ++it2) ++nbc; + + v2.base_resize(nbc); + it3 = v2.begin() + old_nbc; + it2 = v2.end(); + ite2 = v2.begin(); + it1 = vect_end(v1); + ite1 = vect_const_begin(v1); + while (it1 != ite1 && it2 != ite2 && it3 != ite2){ + --it3; + --it1; + --it2; + if (it3->c > it1.index()) { + *it2 = *it3; + ++it1; + } + else if (it3->c == it1.index()) { + *it2=*it3; + it2->e+=*it1; + } + else { + it2->c = it1.index(); + it2->e = *it1; ++it3; + } + } + while (it1 != ite1 && it2 != ite2) { + --it1; + --it2; + it2->c = it1.index(); + it2->e = *it1; + } + } + + template void copy(const V &v1, rsvector &v2) { + if ((const void *)(&v1) != (const void *)(&v2)) { + GMM_ASSERT2(vect_size(v1) == vect_size(v2), "dimensions mismatch"); + if (same_origin(v1, v2)) + GMM_WARNING2("a conflict is possible in vector copy\n"); + copy_rsvector(v1, v2, typename linalg_traits::storage_type()); + } + } + + template + void copy_rsvector(const V &v1, rsvector &v2, abstract_dense) + { copy_vect(v1, v2, abstract_dense(), abstract_sparse()); } + + template + void copy_rsvector(const V &v1, rsvector &v2, abstract_skyline) + { copy_vect(v1, v2, abstract_skyline(), abstract_sparse()); } + + template + void copy_rsvector(const V &v1, rsvector &v2, abstract_sparse) { + copy_rsvector(v1, v2, typename linalg_traits::index_sorted()); + } + + template + void copy_rsvector(const V &v1, rsvector &v2, linalg_true) { + typedef typename linalg_traits::value_type T1; + typename linalg_traits::const_iterator it = vect_const_begin(v1), + ite = vect_const_end(v1); + v2.base_resize(nnz(v1)); + typename rsvector::iterator it2 = v2.begin(); + size_type nn = 0; + for (; it != ite; ++it) + if ((*it) != T1(0)) { it2->c = it.index(); it2->e = *it; ++it2; ++nn; } + v2.base_resize(nn); + } + + template + void copy_rsvector(const V &v1, rsvector &v2, linalg_false) { + typedef typename linalg_traits::value_type T1; + typename linalg_traits::const_iterator it = vect_const_begin(v1), + ite = vect_const_end(v1); + v2.base_resize(nnz(v1)); + typename rsvector::iterator it2 = v2.begin(); + size_type nn = 0; + for (; it != ite; ++it) + if ((*it) != T1(0)) { it2->c = it.index(); it2->e = *it; ++it2; ++nn; } + v2.base_resize(nn); + std::sort(v2.begin(), v2.end()); + } + + template inline void clean(rsvector &v, double eps) { + typedef typename number_traits::magnitude_type R; + typename rsvector::iterator it = v.begin(), ite = v.end(); + for (; it != ite; ++it) if (gmm::abs((*it).e) <= eps) break; + if (it != ite) { + typename rsvector::iterator itc = it; + size_type erased = 1; + for (++it; it != ite; ++it) + { *itc = *it; if (gmm::abs((*it).e) <= R(eps)) ++erased; else ++itc; } + v.base_resize(v.nb_stored() - erased); + } + } + + template + inline void clean(const simple_vector_ref *> &l, double eps) { + simple_vector_ref *> + *svr = const_cast *> *>(&l); + rsvector + *pv = const_cast *>((l.origin)); + clean(*pv, eps); + svr->begin_ = vect_begin(*pv); svr->end_ = vect_end(*pv); + } + + template + inline size_type nnz(const rsvector& l) { return l.nb_stored(); } + + /*************************************************************************/ + /* */ + /* Class slvector: 'sky-line' vector. */ + /* */ + /*************************************************************************/ + + template struct slvector_iterator { + typedef T value_type; + typedef T *pointer; + typedef T &reference; + typedef ptrdiff_t difference_type; + typedef std::random_access_iterator_tag iterator_category; + typedef size_t size_type; + typedef slvector_iterator iterator; + typedef typename std::vector::iterator base_iterator; + + base_iterator it; + size_type shift; + + + iterator &operator ++() + { ++it; ++shift; return *this; } + iterator &operator --() + { --it; --shift; return *this; } + iterator operator ++(int) + { iterator tmp = *this; ++(*(this)); return tmp; } + iterator operator --(int) + { iterator tmp = *this; --(*(this)); return tmp; } + iterator &operator +=(difference_type i) + { it += i; shift += i; return *this; } + iterator &operator -=(difference_type i) + { it -= i; shift -= i; return *this; } + iterator operator +(difference_type i) const + { iterator tmp = *this; return (tmp += i); } + iterator operator -(difference_type i) const + { iterator tmp = *this; return (tmp -= i); } + difference_type operator -(const iterator &i) const + { return it - i.it; } + + reference operator *() const + { return *it; } + reference operator [](int ii) + { return *(it + ii); } + + bool operator ==(const iterator &i) const + { return it == i.it; } + bool operator !=(const iterator &i) const + { return !(i == *this); } + bool operator < (const iterator &i) const + { return it < i.it; } + size_type index() const { return shift; } + + slvector_iterator() {} + slvector_iterator(const base_iterator &iter, size_type s) + : it(iter), shift(s) {} + }; + + template struct slvector_const_iterator { + typedef T value_type; + typedef const T *pointer; + typedef value_type reference; + typedef ptrdiff_t difference_type; + typedef std::random_access_iterator_tag iterator_category; + typedef size_t size_type; + typedef slvector_const_iterator iterator; + typedef typename std::vector::const_iterator base_iterator; + + base_iterator it; + size_type shift; + + + iterator &operator ++() + { ++it; ++shift; return *this; } + iterator &operator --() + { --it; --shift; return *this; } + iterator operator ++(int) + { iterator tmp = *this; ++(*(this)); return tmp; } + iterator operator --(int) + { iterator tmp = *this; --(*(this)); return tmp; } + iterator &operator +=(difference_type i) + { it += i; shift += i; return *this; } + iterator &operator -=(difference_type i) + { it -= i; shift -= i; return *this; } + iterator operator +(difference_type i) const + { iterator tmp = *this; return (tmp += i); } + iterator operator -(difference_type i) const + { iterator tmp = *this; return (tmp -= i); } + difference_type operator -(const iterator &i) const + { return it - i.it; } + + value_type operator *() const + { return *it; } + value_type operator [](int ii) + { return *(it + ii); } + + bool operator ==(const iterator &i) const + { return it == i.it; } + bool operator !=(const iterator &i) const + { return !(i == *this); } + bool operator < (const iterator &i) const + { return it < i.it; } + size_type index() const { return shift; } + + slvector_const_iterator() {} + slvector_const_iterator(const slvector_iterator& iter) + : it(iter.it), shift(iter.shift) {} + slvector_const_iterator(const base_iterator &iter, size_type s) + : it(iter), shift(s) {} + }; + + + /** skyline vector. + */ + template class slvector { + + public : + typedef slvector_iterator iterators; + typedef slvector_const_iterator const_iterators; + typedef typename std::vector::size_type size_type; + typedef T value_type; + + protected : + std::vector data; + size_type shift; + size_type size_; + + + public : + + size_type size() const { return size_; } + size_type first() const { return shift; } + size_type last() const { return shift + data.size(); } + ref_elt_vector > operator [](size_type c) + { return ref_elt_vector >(this, c); } + + typename std::vector::iterator data_begin() { return data.begin(); } + typename std::vector::iterator data_end() { return data.end(); } + typename std::vector::const_iterator data_begin() const + { return data.begin(); } + typename std::vector::const_iterator data_end() const + { return data.end(); } + + void w(size_type c, const T &e); + void wa(size_type c, const T &e); + T r(size_type c) const { + GMM_ASSERT2(c < size_, "out of range"); + if (c < shift || c >= shift + data.size()) return T(0); + return data[c - shift]; + } + + inline T operator [](size_type c) const { return r(c); } + void resize(size_type); + void clear() { data.resize(0); shift = 0; } + void swap(slvector &v) { + std::swap(data, v.data); + std::swap(shift, v.shift); + std::swap(size_, v.size_); + } + + + slvector() : data(0), shift(0), size_(0) {} + explicit slvector(size_type l) : data(0), shift(0), size_(l) {} + slvector(size_type l, size_type d, size_type s) + : data(d), shift(s), size_(l) {} + + }; + + template void slvector::resize(size_type n) { + if (n < last()) { + if (shift >= n) clear(); else { data.resize(n-shift); } + } + size_ = n; + } + + template void slvector::w(size_type c, const T &e) { + GMM_ASSERT2(c < size_, "out of range"); + size_type s = data.size(); + if (!s) { data.resize(1); shift = c; } + else if (c < shift) { + data.resize(s + shift - c); + typename std::vector::iterator it = data.begin(),it2=data.end()-1; + typename std::vector::iterator it3 = it2 - shift + c; + for (; it3 >= it; --it3, --it2) *it2 = *it3; + std::fill(it, it + shift - c, T(0)); + shift = c; + } + else if (c >= shift + s) { + data.resize(c - shift + 1, T(0)); + // std::fill(data.begin() + s, data.end(), T(0)); + } + data[c - shift] = e; + } + + template void slvector::wa(size_type c, const T &e) { + GMM_ASSERT2(c < size_, "out of range"); + size_type s = data.size(); + if (!s) { data.resize(1, e); shift = c; return; } + else if (c < shift) { + data.resize(s + shift - c); + typename std::vector::iterator it = data.begin(),it2=data.end()-1; + typename std::vector::iterator it3 = it2 - shift + c; + for (; it3 >= it; --it3, --it2) *it2 = *it3; + std::fill(it, it + shift - c, T(0)); + shift = c; + data[c - shift] = e; + return; + } + else if (c >= shift + s) { + data.resize(c - shift + 1, T(0)); + data[c - shift] = e; + return; + // std::fill(data.begin() + s, data.end(), T(0)); + } + data[c - shift] += e; + } + + + template struct linalg_traits > { + typedef slvector this_type; + typedef this_type origin_type; + typedef linalg_false is_reference; + typedef abstract_vector linalg_type; + typedef T value_type; + typedef ref_elt_vector > reference; + typedef slvector_iterator iterator; + typedef slvector_const_iterator const_iterator; + typedef abstract_skyline storage_type; + typedef linalg_true index_sorted; + static size_type size(const this_type &v) { return v.size(); } + static iterator begin(this_type &v) + { return iterator(v.data_begin(), v.first()); } + static const_iterator begin(const this_type &v) + { return const_iterator(v.data_begin(), v.first()); } + static iterator end(this_type &v) + { return iterator(v.data_end(), v.last()); } + static const_iterator end(const this_type &v) + { return const_iterator(v.data_end(), v.last()); } + static origin_type* origin(this_type &v) { return &v; } + static const origin_type* origin(const this_type &v) { return &v; } + static void clear(origin_type* o, const iterator &, const iterator &) + { o->clear(); } + static void do_clear(this_type &v) { v.clear(); } + static value_type access(const origin_type *o, const const_iterator &, + const const_iterator &, size_type i) + { return (*o)[i]; } + static reference access(origin_type *o, const iterator &, const iterator &, + size_type i) + { return (*o)[i]; } + static void resize(this_type &v, size_type n) { v.resize(n); } + }; + + template std::ostream &operator << + (std::ostream &o, const slvector& v) { gmm::write(o,v); return o; } + + template + inline size_type nnz(const slvector& l) { return l.last() - l.first(); } + +} + +namespace std { + template void swap(gmm::wsvector &v, gmm::wsvector &w) + { v.swap(w);} + template void swap(gmm::rsvector &v, gmm::rsvector &w) + { v.swap(w);} + template void swap(gmm::slvector &v, gmm::slvector &w) + { v.swap(w);} +} + + + +#endif /* GMM_VECTOR_H__ */ diff --git a/src/linear_algebra/gmm/gmm_vector_to_matrix.h b/src/linear_algebra/gmm/gmm_vector_to_matrix.h new file mode 100644 index 00000000..95b0bea1 --- /dev/null +++ b/src/linear_algebra/gmm/gmm_vector_to_matrix.h @@ -0,0 +1,340 @@ +/* -*- c++ -*- (enables emacs c++ mode) */ +/*=========================================================================== + + Copyright (C) 2003-2020 Yves Renard + + This file is a part of GetFEM + + GetFEM is free software; you can redistribute it and/or modify it + under the terms of the GNU Lesser General Public License as published + by the Free Software Foundation; either version 3 of the License, or + (at your option) any later version along with the GCC Runtime Library + Exception either version 3.1 or (at your option) any later version. + This program is distributed in the hope that it will be useful, but + WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY + or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public + License and GCC Runtime Library Exception for more details. + You should have received a copy of the GNU Lesser General Public License + along with this program; if not, write to the Free Software Foundation, + Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA. + + As a special exception, you may use this file as it is a part of a free + software library without restriction. Specifically, if other files + instantiate templates or use macros or inline functions from this file, + or you compile this file and link it with other files to produce an + executable, this file does not by itself cause the resulting executable + to be covered by the GNU Lesser General Public License. This exception + does not however invalidate any other reasons why the executable file + might be covered by the GNU Lesser General Public License. + +===========================================================================*/ + +/**@file gmm_vector_to_matrix.h + @author Yves Renard + @date December 6, 2003. + @brief View vectors as row or column matrices. */ +#ifndef GMM_VECTOR_TO_MATRIX_H__ +#define GMM_VECTOR_TO_MATRIX_H__ + +#include "gmm_interface.h" + +namespace gmm { + + /* ********************************************************************* */ + /* row vector -> transform a vector in a (1, n) matrix. */ + /* ********************************************************************* */ + + template struct gen_row_vector { + typedef gen_row_vector this_type; + typedef typename std::iterator_traits::value_type V; + typedef V * CPT; + typedef typename std::iterator_traits::reference ref_V; + typedef typename linalg_traits::reference reference; + + simple_vector_ref vec; + + reference operator()(size_type, size_type j) const { return vec[j]; } + + size_type nrows(void) const { return 1; } + size_type ncols(void) const { return vect_size(vec); } + + gen_row_vector(ref_V v) : vec(v) {} + gen_row_vector() {} + gen_row_vector(const gen_row_vector &cr) : vec(cr.vec) {} + }; + + template + struct gen_row_vector_iterator { + typedef gen_row_vector this_type; + typedef typename modifiable_pointer::pointer MPT; + typedef typename std::iterator_traits::value_type V; + typedef simple_vector_ref value_type; + typedef const simple_vector_ref *pointer; + typedef const simple_vector_ref &reference; + typedef ptrdiff_t difference_type; + typedef size_t size_type; + typedef std::random_access_iterator_tag iterator_category; + typedef gen_row_vector_iterator iterator; + + simple_vector_ref vec; + bool isend; + + iterator &operator ++() { isend = true; return *this; } + iterator &operator --() { isend = false; return *this; } + iterator operator ++(int) { iterator tmp = *this; ++(*this); return tmp; } + iterator operator --(int) { iterator tmp = *this; --(*this); return tmp; } + iterator &operator +=(difference_type i) + { if (i) isend = false; return *this; } + iterator &operator -=(difference_type i) + { if (i) isend = true; return *this; } + iterator operator +(difference_type i) const + { iterator itt = *this; return (itt += i); } + iterator operator -(difference_type i) const + { iterator itt = *this; return (itt -= i); } + difference_type operator -(const iterator &i) const { + return (isend == true) ? ((i.isend == true) ? 0 : 1) + : ((i.isend == true) ? -1 : 0); + } + + const simple_vector_ref& operator *() const { return vec; } + const simple_vector_ref& operator [](int i) { return vec; } + + bool operator ==(const iterator &i) const { return (isend == i.isend); } + bool operator !=(const iterator &i) const { return !(i == *this); } + bool operator < (const iterator &i) const { return (*this - i < 0); } + + gen_row_vector_iterator(void) {} + gen_row_vector_iterator(const gen_row_vector_iterator &itm) + : vec(itm.vec), isend(itm.isend) {} + gen_row_vector_iterator(const gen_row_vector &m, bool iis_end) + : vec(m.vec), isend(iis_end) { } + + }; + + template + struct linalg_traits > { + typedef gen_row_vector this_type; + typedef typename std::iterator_traits::value_type V; + typedef typename which_reference::is_reference is_reference; + typedef abstract_matrix linalg_type; + typedef typename linalg_traits::origin_type origin_type; + typedef typename select_ref::ref_type porigin_type; + typedef typename linalg_traits::value_type value_type; + typedef typename select_ref::reference, PT>::ref_type reference; + typedef abstract_null_type sub_col_type; + typedef abstract_null_type col_iterator; + typedef abstract_null_type const_sub_col_type; + typedef abstract_null_type const_col_iterator; + typedef simple_vector_ref const_sub_row_type; + typedef typename select_ref, PT>::ref_type sub_row_type; + typedef gen_row_vector_iterator::pointer> + const_row_iterator; + typedef typename select_ref, PT>::ref_type row_iterator; + typedef typename linalg_traits::storage_type storage_type; + typedef row_major sub_orientation; + typedef typename linalg_traits::index_sorted index_sorted; + static size_type nrows(const this_type &) { return 1; } + static size_type ncols(const this_type &m) { return m.ncols(); } + static const_sub_row_type row(const const_row_iterator &it) { return *it; } + static sub_row_type row(const row_iterator &it) { return *it; } + static const_row_iterator row_begin(const this_type &m) + { return const_row_iterator(m, false); } + static row_iterator row_begin(this_type &m) + { return row_iterator(m, false); } + static const_row_iterator row_end(const this_type &m) + { return const_row_iterator(m, true); } + static row_iterator row_end(this_type &m) + { return row_iterator(m, true); } + static origin_type* origin(this_type &m) { return m.vec.origin; } + static const origin_type* origin(const this_type &m) + { return m.vec.origin; } + static void do_clear(this_type &m) + { clear(row(mat_row_begin(m))); } + static value_type access(const const_row_iterator &itrow, size_type i) + { return itrow.vec[i]; } + static reference access(const row_iterator &itrow, size_type i) + { return itrow.vec[i]; } + }; + + template + std::ostream &operator <<(std::ostream &o, const gen_row_vector& m) + { gmm::write(o,m); return o; } + + /* ********************************************************************* */ + /* col vector -> transform a vector in a (n, 1) matrix. */ + /* ********************************************************************* */ + + template struct gen_col_vector { + typedef gen_col_vector this_type; + typedef typename std::iterator_traits::value_type V; + typedef V * CPT; + typedef typename std::iterator_traits::reference ref_V; + typedef typename linalg_traits::reference reference; + + simple_vector_ref vec; + + reference operator()(size_type i, size_type) const { return vec[i]; } + + size_type ncols(void) const { return 1; } + size_type nrows(void) const { return vect_size(vec); } + + gen_col_vector(ref_V v) : vec(v) {} + gen_col_vector() {} + gen_col_vector(const gen_col_vector &cr) : vec(cr.vec) {} + }; + + template + struct gen_col_vector_iterator { + typedef gen_col_vector this_type; + typedef typename modifiable_pointer::pointer MPT; + typedef typename std::iterator_traits::value_type V; + typedef simple_vector_ref value_type; + typedef const simple_vector_ref *pointer; + typedef const simple_vector_ref &reference; + typedef ptrdiff_t difference_type; + typedef size_t size_type; + typedef std::random_access_iterator_tag iterator_category; + typedef gen_col_vector_iterator iterator; + + simple_vector_ref vec; + bool isend; + + iterator &operator ++() { isend = true; return *this; } + iterator &operator --() { isend = false; return *this; } + iterator operator ++(int) { iterator tmp = *this; ++(*this); return tmp; } + iterator operator --(int) { iterator tmp = *this; --(*this); return tmp; } + iterator &operator +=(difference_type i) + { if (i) isend = false; return *this; } + iterator &operator -=(difference_type i) + { if (i) isend = true; return *this; } + iterator operator +(difference_type i) const + { iterator itt = *this; return (itt += i); } + iterator operator -(difference_type i) const + { iterator itt = *this; return (itt -= i); } + difference_type operator -(const iterator &i) const { + return (isend == true) ? ((i.isend == true) ? 0 : 1) + : ((i.isend == true) ? -1 : 0); + } + + const simple_vector_ref& operator *() const { return vec; } + const simple_vector_ref& operator [](int i) { return vec; } + + bool operator ==(const iterator &i) const { return (isend == i.isend); } + bool operator !=(const iterator &i) const { return !(i == *this); } + bool operator < (const iterator &i) const { return (*this - i < 0); } + + gen_col_vector_iterator(void) {} + gen_col_vector_iterator(const gen_col_vector_iterator &itm) + : vec(itm.vec), isend(itm.isend) {} + gen_col_vector_iterator(const gen_col_vector &m, bool iis_end) + : vec(m.vec), isend(iis_end) { } + + }; + + template + struct linalg_traits > { + typedef gen_col_vector this_type; + typedef typename std::iterator_traits::value_type V; + typedef typename which_reference::is_reference is_reference; + typedef abstract_matrix linalg_type; + typedef typename linalg_traits::origin_type origin_type; + typedef typename select_ref::ref_type porigin_type; + typedef typename linalg_traits::value_type value_type; + typedef typename select_ref::reference, PT>::ref_type reference; + typedef abstract_null_type sub_row_type; + typedef abstract_null_type row_iterator; + typedef abstract_null_type const_sub_row_type; + typedef abstract_null_type const_row_iterator; + typedef simple_vector_ref const_sub_col_type; + typedef typename select_ref, PT>::ref_type sub_col_type; + typedef gen_col_vector_iterator::pointer> + const_col_iterator; + typedef typename select_ref, PT>::ref_type col_iterator; + typedef typename linalg_traits::storage_type storage_type; + typedef col_major sub_orientation; + typedef typename linalg_traits::index_sorted index_sorted; + static size_type ncols(const this_type &) { return 1; } + static size_type nrows(const this_type &m) { return m.nrows(); } + static const_sub_col_type col(const const_col_iterator &it) { return *it; } + static sub_col_type col(const col_iterator &it) { return *it; } + static const_col_iterator col_begin(const this_type &m) + { return const_col_iterator(m, false); } + static col_iterator col_begin(this_type &m) + { return col_iterator(m, false); } + static const_col_iterator col_end(const this_type &m) + { return const_col_iterator(m, true); } + static col_iterator col_end(this_type &m) + { return col_iterator(m, true); } + static origin_type* origin(this_type &m) { return m.vec.origin; } + static const origin_type* origin(const this_type &m) + { return m.vec.origin; } + static void do_clear(this_type &m) + { clear(col(mat_col_begin(m))); } + static value_type access(const const_col_iterator &itcol, size_type i) + { return itcol.vec[i]; } + static reference access(const col_iterator &itcol, size_type i) + { return itcol.vec[i]; } + }; + + template + std::ostream &operator <<(std::ostream &o, const gen_col_vector& m) + { gmm::write(o,m); return o; } + + /* ******************************************************************** */ + /* col and row vectors */ + /* ******************************************************************** */ + + + template inline + typename select_return< gen_row_vector, gen_row_vector, + const V *>::return_type + row_vector(const V& v) { + return typename select_return< gen_row_vector, + gen_row_vector, const V *>::return_type(linalg_cast(v)); + } + + template inline + typename select_return< gen_row_vector, gen_row_vector, + V *>::return_type + row_vector(V& v) { + return typename select_return< gen_row_vector, + gen_row_vector, V *>::return_type(linalg_cast(v)); + } + + template inline gen_row_vector + const_row_vector(V& v) + { return gen_row_vector(v); } + + + template inline + typename select_return< gen_col_vector, gen_col_vector, + const V *>::return_type + col_vector(const V& v) { + return typename select_return< gen_col_vector, + gen_col_vector, const V *>::return_type(linalg_cast(v)); + } + + template inline + typename select_return< gen_col_vector, gen_col_vector, + V *>::return_type + col_vector(V& v) { + return typename select_return< gen_col_vector, + gen_col_vector, V *>::return_type(linalg_cast(v)); + } + + template inline gen_col_vector + const_col_vector(V& v) + { return gen_col_vector(v); } + + +} + +#endif // GMM_VECTOR_TO_MATRIX_H__ diff --git a/6.x/src/linear_algebra/linear_algebra.cpp b/src/linear_algebra/linear_algebra.cpp similarity index 60% rename from 6.x/src/linear_algebra/linear_algebra.cpp rename to src/linear_algebra/linear_algebra.cpp index a37da7c5..ac6eaea4 100644 --- a/6.x/src/linear_algebra/linear_algebra.cpp +++ b/src/linear_algebra/linear_algebra.cpp @@ -72,7 +72,7 @@ * An array can be of type code vr, vc, mr, or mc * and is stored in an i-rate object. * In orchestra code, an array is passed as a - * MYFLT i-rate variable that contains the address + * cs_float i-rate variable that contains the address * of the array object, which is actually stored * in the allocator opcode instance. * Although array variables are i-rate, of course @@ -384,9 +384,28 @@ static std::ostream &operator<<(std::ostream &o, return o; } +// gmm++ 5.4.3 changed gmm::lapack_ipvt into a std::vector typedef and replaced +// its get() accessor with operator[]. These overloads select whichever accessor +// the installed gmm++ provides. +template +static auto la_ipvt_get(const Pivot &pivot, size_t i, int) + -> decltype(pivot.get(i)) { + return pivot.get(i); +} + +template +static size_t la_ipvt_get(const Pivot &pivot, size_t i, long) { + return static_cast(pivot[i]); +} + +template +static size_t la_ipvt_get(const Pivot &pivot, size_t i) { + return la_ipvt_get(pivot, i, 0); +} + /** * Template union for safely and efficiently - * typecasting the value of a MYFLT variable + * typecasting the value of a cs_float variable * to the address of an array, and vice versa. */ template struct ArrayCaster { @@ -398,7 +417,7 @@ template struct ArrayCaster { /** * Safely and efficiently typecast the address - * of an array to the value of a MYFLT variable. + * of an array to the value of a cs_float variable. */ template void tof(A *a, F *f) { ArrayCaster arrayCaster; @@ -408,7 +427,7 @@ template void tof(A *a, F *f) { /** * Safely and efficiently typecast the value of a - * a MYFLT variable to the address of an array. + * a cs_float variable to the address of an array. */ template void toa(F *f, A *&a) { ArrayCaster arrayCaster; @@ -418,15 +437,15 @@ template void toa(F *f, A *&a) { class la_i_vr_create_t : public OpcodeNoteoffBase { public: - MYFLT *i_vr; - MYFLT *i_rows; - std::vector vr; - int init(CSOUND *) { + cs_float *i_vr; + cs_float *i_rows; + std::vector vr; + int32_t init(CSOUND *) { vr.resize(size_t(*i_rows)); tof(this, i_vr); return OK; } - int noteoff(CSOUND *) { + int32_t noteoff(CSOUND *) { vr.resize(0); return OK; } @@ -434,15 +453,15 @@ class la_i_vr_create_t : public OpcodeNoteoffBase { class la_i_vc_create_t : public OpcodeNoteoffBase { public: - MYFLT *i_vc; - MYFLT *i_rows; - std::vector> vc; - int init(CSOUND *) { + cs_float *i_vc; + cs_float *i_rows; + std::vector> vc; + int32_t init(CSOUND *) { vc.resize(size_t(*i_rows)); tof(this, i_vc); return OK; } - int noteoff(CSOUND *) { + int32_t noteoff(CSOUND *) { vc.resize(0); return OK; } @@ -450,12 +469,12 @@ class la_i_vc_create_t : public OpcodeNoteoffBase { class la_i_mr_create_t : public OpcodeNoteoffBase { public: - MYFLT *i_mr; - MYFLT *i_rows; - MYFLT *i_columns; - MYFLT *o_diagonal; - gmm::dense_matrix mr; - int init(CSOUND *) { + cs_float *i_mr; + cs_float *i_rows; + cs_float *i_columns; + cs_float *o_diagonal; + gmm::dense_matrix mr; + int32_t init(CSOUND *) { gmm::resize(mr, size_t(*i_rows), size_t(*i_columns)); if (*o_diagonal) { for (size_t i = 0, n = size_t(*i_rows); i < n; ++i) { @@ -465,7 +484,7 @@ class la_i_mr_create_t : public OpcodeNoteoffBase { tof(this, i_mr); return OK; } - int noteoff(CSOUND *) { + int32_t noteoff(CSOUND *) { mr.resize(0, 0); return OK; } @@ -473,23 +492,23 @@ class la_i_mr_create_t : public OpcodeNoteoffBase { class la_i_mc_create_t : public OpcodeNoteoffBase { public: - MYFLT *i_mc; - MYFLT *i_rows; - MYFLT *i_columns; - MYFLT *o_diagonal_r; - MYFLT *o_diagonal_i; - gmm::dense_matrix> mc; - int init(CSOUND *) { + cs_float *i_mc; + cs_float *i_rows; + cs_float *i_columns; + cs_float *o_diagonal_r; + cs_float *o_diagonal_i; + gmm::dense_matrix> mc; + int32_t init(CSOUND *) { gmm::resize(mc, size_t(*i_rows), size_t(*i_columns)); if (*o_diagonal_r || *o_diagonal_i) { for (size_t i = 0, n = size_t(*i_rows); i < n; ++i) { - mc(i, i) = std::complex(*o_diagonal_r, *o_diagonal_i); + mc(i, i) = std::complex(*o_diagonal_r, *o_diagonal_i); } } tof(this, i_mc); return OK; } - int noteoff(CSOUND *) { + int32_t noteoff(CSOUND *) { mc.resize(0, 0); return OK; } @@ -497,52 +516,52 @@ class la_i_mc_create_t : public OpcodeNoteoffBase { class la_i_size_vr_t : public OpcodeBase { public: - MYFLT *i_size; - MYFLT *i_vr; - int init(CSOUND *) { + cs_float *i_size; + cs_float *i_vr; + int32_t init(CSOUND *) { la_i_vr_create_t *array = 0; toa(i_vr, array); - *i_size = (MYFLT)gmm::vect_size(array->vr); + *i_size = (cs_float)gmm::vect_size(array->vr); return OK; } }; class la_i_size_vc_t : public OpcodeBase { public: - MYFLT *i_size; - MYFLT *i_vc; - int init(CSOUND *) { + cs_float *i_size; + cs_float *i_vc; + int32_t init(CSOUND *) { la_i_vc_create_t *array = 0; toa(i_vc, array); - *i_size = (MYFLT)gmm::vect_size(array->vc); + *i_size = (cs_float)gmm::vect_size(array->vc); return OK; } }; class la_i_size_mr_t : public OpcodeBase { public: - MYFLT *i_rows; - MYFLT *i_columns; - MYFLT *i_mr; - int init(CSOUND *) { + cs_float *i_rows; + cs_float *i_columns; + cs_float *i_mr; + int32_t init(CSOUND *) { la_i_mr_create_t *array = 0; toa(i_mr, array); - *i_rows = (MYFLT)gmm::mat_nrows(array->mr); - *i_columns = (MYFLT)gmm::mat_ncols(array->mr); + *i_rows = (cs_float)gmm::mat_nrows(array->mr); + *i_columns = (cs_float)gmm::mat_ncols(array->mr); return OK; } }; class la_i_size_mc_t : public OpcodeBase { public: - MYFLT *i_rows; - MYFLT *i_columns; - MYFLT *i_mc; - int init(CSOUND *) { + cs_float *i_rows; + cs_float *i_columns; + cs_float *i_mc; + int32_t init(CSOUND *) { la_i_mc_create_t *array = 0; toa(i_mc, array); - *i_rows = (MYFLT)gmm::mat_nrows(array->mc); - *i_columns = (MYFLT)gmm::mat_ncols(array->mc); + *i_rows = (cs_float)gmm::mat_nrows(array->mc); + *i_columns = (cs_float)gmm::mat_ncols(array->mc); return OK; } }; @@ -561,14 +580,14 @@ class la_i_size_mc_t : public OpcodeBase { */ class la_k_current_f_t : public OpcodeBase { public: - MYFLT *k_current; + cs_float *k_current; PVSDAT *f_sig; size_t last_frame; - int init(CSOUND *) { + int32_t init(CSOUND *) { last_frame = f_sig->framecount; return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { if (last_frame < f_sig->framecount) { last_frame = f_sig->framecount; *k_current = 1.0; @@ -602,19 +621,19 @@ class la_k_current_f_t : public OpcodeBase { class la_k_current_vr_t : public OpcodeBase { public: - MYFLT *k_current; - MYFLT *rhs_ivr; + cs_float *k_current; + cs_float *rhs_ivr; la_i_vr_create_t *rhs; size_t ksmps; size_t vector_size; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { rhs = 0; toa(rhs_ivr, rhs); ksmps = opds.insdshead->ksmps; vector_size = gmm::vect_size(rhs->vr); return OK; } - int kontrol(CSOUND *csound) { + int32_t kontrol(CSOUND *csound) { size_t frame_count = opds.insdshead->kcounter * ksmps; size_t index = frame_count % vector_size; if (index == 0) { @@ -628,8 +647,8 @@ class la_k_current_vr_t : public OpcodeBase { class la_i_print_vr_t : public OpcodeBase { public: - MYFLT *i_vr; - int init(CSOUND *csound) { + cs_float *i_vr; + int32_t init(CSOUND *csound) { la_i_vr_create_t *array = 0; toa(i_vr, array); std::ostringstream stream; @@ -641,8 +660,8 @@ class la_i_print_vr_t : public OpcodeBase { class la_i_print_vc_t : public OpcodeBase { public: - MYFLT *i_vc; - int init(CSOUND *csound) { + cs_float *i_vc; + int32_t init(CSOUND *csound) { la_i_vc_create_t *array = 0; toa(i_vc, array); std::ostringstream stream; @@ -654,8 +673,8 @@ class la_i_print_vc_t : public OpcodeBase { class la_i_print_mr_t : public OpcodeBase { public: - MYFLT *i_mr; - int init(CSOUND *csound) { + cs_float *i_mr; + int32_t init(CSOUND *csound) { la_i_mr_create_t *array = 0; toa(i_mr, array); std::ostringstream stream; @@ -667,8 +686,8 @@ class la_i_print_mr_t : public OpcodeBase { class la_i_print_mc_t : public OpcodeBase { public: - MYFLT *i_mc; - int init(CSOUND *csound) { + cs_float *i_mc; + int32_t init(CSOUND *csound) { la_i_mc_create_t *array = 0; toa(i_mc, array); std::ostringstream stream; @@ -680,11 +699,11 @@ class la_i_print_mc_t : public OpcodeBase { class la_i_assign_vr_t : public OpcodeBase { public: - MYFLT *i_vr_lhs; - MYFLT *i_vr_rhs; + cs_float *i_vr_lhs; + cs_float *i_vr_rhs; la_i_vr_create_t *lhs; la_i_vr_create_t *rhs; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_vr_lhs, lhs); toa(i_vr_rhs, rhs); gmm::copy(rhs->vr, lhs->vr); @@ -694,16 +713,16 @@ class la_i_assign_vr_t : public OpcodeBase { class la_k_assign_vr_t : public OpcodeBase { public: - MYFLT *i_vr_lhs; - MYFLT *i_vr_rhs; + cs_float *i_vr_lhs; + cs_float *i_vr_rhs; la_i_vr_create_t *lhs; la_i_vr_create_t *rhs; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_vr_lhs, lhs); toa(i_vr_rhs, rhs); return OK; } - int kontrol(CSOUND *csound) { + int32_t kontrol(CSOUND *csound) { gmm::copy(rhs->vr, lhs->vr); return OK; } @@ -711,11 +730,11 @@ class la_k_assign_vr_t : public OpcodeBase { class la_i_assign_vc_t : public OpcodeBase { public: - MYFLT *i_vc_lhs; - MYFLT *i_vc_rhs; + cs_float *i_vc_lhs; + cs_float *i_vc_rhs; la_i_vc_create_t *lhs; la_i_vc_create_t *rhs; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_vc_lhs, lhs); toa(i_vc_rhs, rhs); gmm::copy(rhs->vc, lhs->vc); @@ -725,16 +744,16 @@ class la_i_assign_vc_t : public OpcodeBase { class la_k_assign_vc_t : public OpcodeBase { public: - MYFLT *i_vc_lhs; - MYFLT *i_vc_rhs; + cs_float *i_vc_lhs; + cs_float *i_vc_rhs; la_i_vc_create_t *lhs; la_i_vc_create_t *rhs; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_vc_lhs, lhs); toa(i_vc_rhs, rhs); return OK; } - int kontrol(CSOUND *csound) { + int32_t kontrol(CSOUND *csound) { gmm::copy(rhs->vc, lhs->vc); return OK; } @@ -742,11 +761,11 @@ class la_k_assign_vc_t : public OpcodeBase { class la_i_assign_mr_t : public OpcodeBase { public: - MYFLT *i_mr_lhs; - MYFLT *i_mr_rhs; + cs_float *i_mr_lhs; + cs_float *i_mr_rhs; la_i_mr_create_t *lhs; la_i_mr_create_t *rhs; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_mr_lhs, lhs); toa(i_mr_rhs, rhs); gmm::copy(rhs->mr, lhs->mr); @@ -756,16 +775,16 @@ class la_i_assign_mr_t : public OpcodeBase { class la_k_assign_mr_t : public OpcodeBase { public: - MYFLT *i_mr_lhs; - MYFLT *i_mr_rhs; + cs_float *i_mr_lhs; + cs_float *i_mr_rhs; la_i_mr_create_t *lhs; la_i_mr_create_t *rhs; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_mr_lhs, lhs); toa(i_mr_rhs, rhs); return OK; } - int kontrol(CSOUND *csound) { + int32_t kontrol(CSOUND *csound) { gmm::copy(rhs->mr, lhs->mr); return OK; } @@ -773,11 +792,11 @@ class la_k_assign_mr_t : public OpcodeBase { class la_i_assign_mc_t : public OpcodeBase { public: - MYFLT *i_mc_lhs; - MYFLT *i_mc_rhs; + cs_float *i_mc_lhs; + cs_float *i_mc_rhs; la_i_mc_create_t *lhs; la_i_mc_create_t *rhs; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_mc_lhs, lhs); toa(i_mc_rhs, rhs); gmm::copy(rhs->mc, lhs->mc); @@ -787,16 +806,16 @@ class la_i_assign_mc_t : public OpcodeBase { class la_k_assign_mc_t : public OpcodeBase { public: - MYFLT *i_mc_lhs; - MYFLT *i_mc_rhs; + cs_float *i_mc_lhs; + cs_float *i_mc_rhs; la_i_mc_create_t *lhs; la_i_mc_create_t *rhs; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_mc_lhs, lhs); toa(i_mc_rhs, rhs); return OK; } - int kontrol(CSOUND *csound) { + int32_t kontrol(CSOUND *csound) { gmm::copy(rhs->mc, lhs->mc); return OK; } @@ -804,18 +823,18 @@ class la_k_assign_mc_t : public OpcodeBase { class la_k_assign_a_t : public OpcodeBase { public: - MYFLT *i_vr; - MYFLT *a_a; + cs_float *i_vr; + cs_float *a_a; la_i_vr_create_t *lhs; size_t ksmps; size_t vector_size; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_vr, lhs); ksmps = opds.insdshead->ksmps; vector_size = gmm::vect_size(lhs->vr); return OK; } - int kontrol(CSOUND *csound) { + int32_t kontrol(CSOUND *csound) { uint32_t offset = opds.insdshead->ksmps_offset; uint32_t early = opds.insdshead->ksmps_no_end; size_t frame_count = opds.insdshead->ksmps * ksmps; @@ -831,18 +850,20 @@ class la_k_assign_a_t : public OpcodeBase { class la_i_assign_t_t : public OpcodeBase { public: - MYFLT *i_vr; - MYFLT *i_tablenumber; + cs_float *i_vr; + cs_float *i_tablenumber; la_i_vr_create_t *lhs; int tablenumber; int n; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_vr, lhs); tablenumber = int(std::floor(*i_tablenumber)); - n = csound->TableLength(csound, tablenumber); + cs_float tn = (cs_float)tablenumber; + FUNC *ftp = csound->FTFind(csound, &tn); + n = ftp != NULL ? (int)ftp->flen : 0; gmm::resize(lhs->vr, n); for (int i = 0; i < n; ++i) { - lhs->vr[i] = csound->TableGet(csound, tablenumber, i); + lhs->vr[i] = ftp->ftable[i]; } return OK; } @@ -850,21 +871,27 @@ class la_i_assign_t_t : public OpcodeBase { class la_k_assign_t_t : public OpcodeBase { public: - MYFLT *i_vr; - MYFLT *i_tablenumber; + cs_float *i_vr; + cs_float *i_tablenumber; la_i_vr_create_t *lhs; int tablenumber; int n; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_vr, lhs); tablenumber = int(std::floor(*i_tablenumber)); - n = csound->TableLength(csound, tablenumber); + cs_float tn = (cs_float)tablenumber; + FUNC *ftp = csound->FTFind(csound, &tn); + n = ftp != NULL ? (int)ftp->flen : 0; gmm::resize(lhs->vr, n); return OK; } - int kontrol(CSOUND *csound) { - for (int i = 0; i < n; ++i) { - lhs->vr[i] = csound->TableGet(csound, tablenumber, i); + int32_t kontrol(CSOUND *csound) { + cs_float tn = (cs_float)tablenumber; + FUNC *ftp = csound->FTFind(csound, &tn); + if (ftp != NULL) { + for (int i = 0; i < n; ++i) { + lhs->vr[i] = ftp->ftable[i]; + } } return OK; } @@ -872,20 +899,20 @@ class la_k_assign_t_t : public OpcodeBase { class la_k_assign_f_t : public OpcodeBase { public: - MYFLT *i_vc; + cs_float *i_vc; PVSDAT *f_fsig; la_i_vc_create_t *lhs; int n; - std::complex *f; - int init(CSOUND *csound) { + std::complex *f; + int32_t init(CSOUND *csound) { toa(i_vc, lhs); n = f_fsig->N; - // std::complex has the same layout as CMPLX. - f = (std::complex *)f_fsig->frame.auxp; + // std::complex has the same layout as CMPLX. + f = (std::complex *)f_fsig->frame.auxp; gmm::resize(lhs->vc, n); return OK; } - int kontrol(CSOUND *csound) { + int32_t kontrol(CSOUND *csound) { for (int i = 0; i < n; ++i) { lhs->vc[i] = f[i]; } @@ -895,19 +922,19 @@ class la_k_assign_f_t : public OpcodeBase { class la_k_a_assign_t : public OpcodeBase { public: - MYFLT *a_a; - MYFLT *i_vr; + cs_float *a_a; + cs_float *i_vr; la_i_vr_create_t *rhs; size_t ksmps; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_vr, rhs); ksmps = opds.insdshead->ksmps; return OK; } - int kontrol(CSOUND *csound) { + int32_t kontrol(CSOUND *csound) { uint32_t offset = opds.insdshead->ksmps_offset; uint32_t early = opds.insdshead->ksmps_no_end; - memset(a_a, '\0', offset * sizeof(MYFLT)); + memset(a_a, '\0', offset * sizeof(cs_float)); size_t frameCount = opds.insdshead->kcounter * opds.insdshead->ksmps; size_t vectorSize = gmm::vect_size(rhs->vr); size_t array_i = frameCount % vectorSize; @@ -922,18 +949,20 @@ class la_k_a_assign_t : public OpcodeBase { class la_i_t_assign_t : public OpcodeBase { public: - MYFLT *i_tablenumber; - MYFLT *i_vr; + cs_float *i_tablenumber; + cs_float *i_vr; la_i_vr_create_t *rhs; int tablenumber; int n; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_vr, rhs); tablenumber = int(std::floor(*i_tablenumber)); - n = csound->TableLength(csound, tablenumber); + cs_float tn = (cs_float)tablenumber; + FUNC *ftp = csound->FTFind(csound, &tn); + n = ftp != NULL ? (int)ftp->flen : 0; gmm::resize(rhs->vr, n); for (int i = 0; i < n; ++i) { - csound->TableSet(csound, tablenumber, i, rhs->vr[i]); + ftp->ftable[i] = rhs->vr[i]; } return OK; } @@ -941,21 +970,27 @@ class la_i_t_assign_t : public OpcodeBase { class la_k_t_assign_t : public OpcodeBase { public: - MYFLT *i_tablenumber; - MYFLT *i_vr; + cs_float *i_tablenumber; + cs_float *i_vr; la_i_vr_create_t *rhs; int tablenumber; int n; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_vr, rhs); tablenumber = int(std::floor(*i_tablenumber)); - n = csound->TableLength(csound, tablenumber); + cs_float tn = (cs_float)tablenumber; + FUNC *ftp = csound->FTFind(csound, &tn); + n = ftp != NULL ? (int)ftp->flen : 0; gmm::resize(rhs->vr, n); return OK; } - int kontrol(CSOUND *csound) { - for (int i = 0; i < n; ++i) { - csound->TableSet(csound, tablenumber, i, rhs->vr[i]); + int32_t kontrol(CSOUND *csound) { + cs_float tn = (cs_float)tablenumber; + FUNC *ftp = csound->FTFind(csound, &tn); + if (ftp != NULL) { + for (int i = 0; i < n; ++i) { + ftp->ftable[i] = rhs->vr[i]; + } } return OK; } @@ -964,19 +999,19 @@ class la_k_t_assign_t : public OpcodeBase { class la_k_f_assign_t : public OpcodeBase { public: PVSDAT *f_fsig; - MYFLT *i_vc; + cs_float *i_vc; la_i_vc_create_t *rhs; int n; - std::complex *f; - int init(CSOUND *csound) { + std::complex *f; + int32_t init(CSOUND *csound) { toa(i_vc, rhs); n = f_fsig->N; - // std::complex has the same layout as CMPLX. - f = (std::complex *)f_fsig->frame.auxp; + // std::complex has the same layout as CMPLX. + f = (std::complex *)f_fsig->frame.auxp; gmm::resize(rhs->vc, n); return OK; } - int kontrol(CSOUND *csound) { + int32_t kontrol(CSOUND *csound) { for (int i = 0; i < n; ++i) { f[i] = rhs->vc[i]; } @@ -986,10 +1021,10 @@ class la_k_f_assign_t : public OpcodeBase { class la_i_random_vr_t : public OpcodeBase { public: - MYFLT *i_vr_lhs; - MYFLT *i_fraction; + cs_float *i_vr_lhs; + cs_float *i_fraction; la_i_vr_create_t *lhs; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_vr_lhs, lhs); gmm::fill_random(lhs->vr, *i_fraction); return OK; @@ -998,14 +1033,14 @@ class la_i_random_vr_t : public OpcodeBase { class la_k_random_vr_t : public OpcodeBase { public: - MYFLT *i_vr_lhs; - MYFLT *i_fraction; + cs_float *i_vr_lhs; + cs_float *i_fraction; la_i_vr_create_t *lhs; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_vr_lhs, lhs); return OK; } - int kontrol(CSOUND *csound) { + int32_t kontrol(CSOUND *csound) { gmm::fill_random(lhs->vr, *i_fraction); return OK; } @@ -1013,10 +1048,10 @@ class la_k_random_vr_t : public OpcodeBase { class la_i_random_vc_t : public OpcodeBase { public: - MYFLT *i_vc_lhs; - MYFLT *i_fraction; + cs_float *i_vc_lhs; + cs_float *i_fraction; la_i_vc_create_t *lhs; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_vc_lhs, lhs); gmm::fill_random(lhs->vc, *i_fraction); return OK; @@ -1025,14 +1060,14 @@ class la_i_random_vc_t : public OpcodeBase { class la_k_random_vc_t : public OpcodeBase { public: - MYFLT *i_vc_lhs; - MYFLT *i_fraction; + cs_float *i_vc_lhs; + cs_float *i_fraction; la_i_vc_create_t *lhs; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_vc_lhs, lhs); return OK; } - int kontrol(CSOUND *csound) { + int32_t kontrol(CSOUND *csound) { gmm::fill_random(lhs->vc, *i_fraction); return OK; } @@ -1040,10 +1075,10 @@ class la_k_random_vc_t : public OpcodeBase { class la_i_random_mr_t : public OpcodeBase { public: - MYFLT *i_mr_lhs; - MYFLT *i_fraction; + cs_float *i_mr_lhs; + cs_float *i_fraction; la_i_mr_create_t *lhs; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_mr_lhs, lhs); gmm::fill_random(lhs->mr, *i_fraction); return OK; @@ -1052,14 +1087,14 @@ class la_i_random_mr_t : public OpcodeBase { class la_k_random_mr_t : public OpcodeBase { public: - MYFLT *i_mr_lhs; - MYFLT *i_fraction; + cs_float *i_mr_lhs; + cs_float *i_fraction; la_i_mr_create_t *lhs; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_mr_lhs, lhs); return OK; } - int kontrol(CSOUND *csound) { + int32_t kontrol(CSOUND *csound) { gmm::fill_random(lhs->mr, *i_fraction); return OK; } @@ -1067,10 +1102,10 @@ class la_k_random_mr_t : public OpcodeBase { class la_i_random_mc_t : public OpcodeBase { public: - MYFLT *i_mc_lhs; - MYFLT *i_fraction; + cs_float *i_mc_lhs; + cs_float *i_fraction; la_i_mc_create_t *lhs; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_mc_lhs, lhs); gmm::fill_random(lhs->mc, *i_fraction); return OK; @@ -1079,14 +1114,14 @@ class la_i_random_mc_t : public OpcodeBase { class la_k_random_mc_t : public OpcodeBase { public: - MYFLT *i_mc_lhs; - MYFLT *i_fraction; + cs_float *i_mc_lhs; + cs_float *i_fraction; la_i_mc_create_t *lhs; - int init(CSOUND *csound) { + int32_t init(CSOUND *csound) { toa(i_mc_lhs, lhs); return OK; } - int kontrol(CSOUND *csound) { + int32_t kontrol(CSOUND *csound) { gmm::fill_random(lhs->mc, *i_fraction); return OK; } @@ -1094,11 +1129,11 @@ class la_k_random_mc_t : public OpcodeBase { class la_i_vr_set_t : public OpcodeBase { public: - MYFLT *i_vr; - MYFLT *i_row; - MYFLT *i_value; + cs_float *i_vr; + cs_float *i_row; + cs_float *i_value; la_i_vr_create_t *vr; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(i_vr, vr); vr->vr[size_t(*i_row)] = *i_value; return OK; @@ -1107,15 +1142,15 @@ class la_i_vr_set_t : public OpcodeBase { class la_k_vr_set_t : public OpcodeBase { public: - MYFLT *i_vr; - MYFLT *k_row; - MYFLT *k_value; + cs_float *i_vr; + cs_float *k_row; + cs_float *k_value; la_i_vr_create_t *vr; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(i_vr, vr); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { vr->vr[size_t(*k_row)] = *k_value; return OK; } @@ -1123,43 +1158,43 @@ class la_k_vr_set_t : public OpcodeBase { class la_i_vc_set_t : public OpcodeBase { public: - MYFLT *i_vc; - MYFLT *i_row; - MYFLT *i_value_r; - MYFLT *i_value_i; + cs_float *i_vc; + cs_float *i_row; + cs_float *i_value_r; + cs_float *i_value_i; la_i_vc_create_t *vc; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(i_vc, vc); - vc->vc[size_t(*i_row)] = std::complex(*i_value_r, *i_value_i); + vc->vc[size_t(*i_row)] = std::complex(*i_value_r, *i_value_i); return OK; } }; class la_k_vc_set_t : public OpcodeBase { public: - MYFLT *i_vc; - MYFLT *k_row; - MYFLT *k_value_r; - MYFLT *k_value_i; + cs_float *i_vc; + cs_float *k_row; + cs_float *k_value_r; + cs_float *k_value_i; la_i_vc_create_t *vc; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(i_vc, vc); return OK; } - int kontrol(CSOUND *) { - vc->vc[size_t(*k_row)] = std::complex(*k_value_r, *k_value_i); + int32_t kontrol(CSOUND *) { + vc->vc[size_t(*k_row)] = std::complex(*k_value_r, *k_value_i); return OK; } }; class la_i_mr_set_t : public OpcodeBase { public: - MYFLT *i_mr; - MYFLT *i_row; - MYFLT *i_column; - MYFLT *i_value; + cs_float *i_mr; + cs_float *i_row; + cs_float *i_column; + cs_float *i_value; la_i_mr_create_t *mr; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(i_mr, mr); mr->mr(size_t(*i_row), size_t(*i_column)) = *i_value; return OK; @@ -1168,16 +1203,16 @@ class la_i_mr_set_t : public OpcodeBase { class la_k_mr_set_t : public OpcodeBase { public: - MYFLT *i_mr; - MYFLT *k_row; - MYFLT *k_column; - MYFLT *k_value; + cs_float *i_mr; + cs_float *k_row; + cs_float *k_column; + cs_float *k_value; la_i_mr_create_t *mr; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(i_mr, mr); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { mr->mr(size_t(*k_row), size_t(*k_column)) = *k_value; return OK; } @@ -1185,46 +1220,46 @@ class la_k_mr_set_t : public OpcodeBase { class la_i_mc_set_t : public OpcodeBase { public: - MYFLT *i_mc; - MYFLT *i_row; - MYFLT *i_column; - MYFLT *i_value_r; - MYFLT *i_value_i; + cs_float *i_mc; + cs_float *i_row; + cs_float *i_column; + cs_float *i_value_r; + cs_float *i_value_i; la_i_mc_create_t *mc; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(i_mc, mc); mc->mc(size_t(*i_row), size_t(*i_column)) = - std::complex(*i_value_r, *i_value_i); + std::complex(*i_value_r, *i_value_i); return OK; } }; class la_k_mc_set_t : public OpcodeBase { public: - MYFLT *i_mc; - MYFLT *k_row; - MYFLT *k_column; - MYFLT *k_value_r; - MYFLT *k_value_i; + cs_float *i_mc; + cs_float *k_row; + cs_float *k_column; + cs_float *k_value_r; + cs_float *k_value_i; la_i_mc_create_t *mc; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(i_mc, mc); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { mc->mc(size_t(*k_row), size_t(*k_column)) = - std::complex(*k_value_r, *k_value_i); + std::complex(*k_value_r, *k_value_i); return OK; } }; class la_i_get_vr_t : public OpcodeBase { public: - MYFLT *i_value; - MYFLT *i_vr; - MYFLT *i_row; + cs_float *i_value; + cs_float *i_vr; + cs_float *i_row; la_i_vr_create_t *vr; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(i_vr, vr); *i_value = vr->vr[size_t(*i_row)]; return OK; @@ -1233,15 +1268,15 @@ class la_i_get_vr_t : public OpcodeBase { class la_k_get_vr_t : public OpcodeBase { public: - MYFLT *k_value; - MYFLT *i_vr; - MYFLT *k_row; + cs_float *k_value; + cs_float *i_vr; + cs_float *k_row; la_i_vr_create_t *vr; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(i_vr, vr); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { *k_value = vr->vr[size_t(*k_row)]; return OK; } @@ -1249,14 +1284,14 @@ class la_k_get_vr_t : public OpcodeBase { class la_i_get_vc_t : public OpcodeBase { public: - MYFLT *i_value_r; - MYFLT *i_value_i; - MYFLT *i_vc; - MYFLT *i_row; + cs_float *i_value_r; + cs_float *i_value_i; + cs_float *i_vc; + cs_float *i_row; la_i_vc_create_t *vc; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(i_vc, vc); - const std::complex &lhs = vc->vc[size_t(*i_row)]; + const std::complex &lhs = vc->vc[size_t(*i_row)]; *i_value_r = lhs.real(); *i_value_i = lhs.imag(); return OK; @@ -1265,17 +1300,17 @@ class la_i_get_vc_t : public OpcodeBase { class la_k_get_vc_t : public OpcodeBase { public: - MYFLT *k_value_r; - MYFLT *k_value_i; - MYFLT *i_vc; - MYFLT *k_row; + cs_float *k_value_r; + cs_float *k_value_i; + cs_float *i_vc; + cs_float *k_row; la_i_vc_create_t *vc; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(i_vc, vc); return OK; } - int kontrol(CSOUND *) { - const std::complex &lhs = vc->vc[size_t(*k_row)]; + int32_t kontrol(CSOUND *) { + const std::complex &lhs = vc->vc[size_t(*k_row)]; *k_value_r = lhs.real(); *k_value_i = lhs.imag(); return OK; @@ -1284,12 +1319,12 @@ class la_k_get_vc_t : public OpcodeBase { class la_i_get_mr_t : public OpcodeBase { public: - MYFLT *i_value; - MYFLT *i_mr; - MYFLT *i_row; - MYFLT *i_column; + cs_float *i_value; + cs_float *i_mr; + cs_float *i_row; + cs_float *i_column; la_i_mr_create_t *mr; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(i_mr, mr); *i_value = mr->mr(size_t(*i_row), size_t(*i_column)); return OK; @@ -1298,16 +1333,16 @@ class la_i_get_mr_t : public OpcodeBase { class la_k_get_mr_t : public OpcodeBase { public: - MYFLT *k_value; - MYFLT *i_mr; - MYFLT *k_row; - MYFLT *k_column; + cs_float *k_value; + cs_float *i_mr; + cs_float *k_row; + cs_float *k_column; la_i_mr_create_t *mr; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(i_mr, mr); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { *k_value = mr->mr(size_t(*k_row), size_t(*k_column)); return OK; } @@ -1315,15 +1350,15 @@ class la_k_get_mr_t : public OpcodeBase { class la_i_get_mc_t : public OpcodeBase { public: - MYFLT *i_value_r; - MYFLT *i_value_i; - MYFLT *i_mc; - MYFLT *i_row; - MYFLT *i_column; + cs_float *i_value_r; + cs_float *i_value_i; + cs_float *i_mc; + cs_float *i_row; + cs_float *i_column; la_i_mc_create_t *mc; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(i_mc, mc); - const std::complex &lhs = mc->mc(size_t(*i_row), size_t(*i_column)); + const std::complex &lhs = mc->mc(size_t(*i_row), size_t(*i_column)); *i_value_r = lhs.real(); *i_value_i = lhs.imag(); return OK; @@ -1332,18 +1367,18 @@ class la_i_get_mc_t : public OpcodeBase { class la_k_get_mc_t : public OpcodeBase { public: - MYFLT *k_value_r; - MYFLT *k_value_i; - MYFLT *i_mc; - MYFLT *k_row; - MYFLT *k_column; + cs_float *k_value_r; + cs_float *k_value_i; + cs_float *i_mc; + cs_float *k_row; + cs_float *k_column; la_i_mc_create_t *mc; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(i_mc, mc); return OK; } - int kontrol(CSOUND *) { - const std::complex &lhs = mc->mc(size_t(*k_row), size_t(*k_column)); + int32_t kontrol(CSOUND *) { + const std::complex &lhs = mc->mc(size_t(*k_row), size_t(*k_column)); *k_value_r = lhs.real(); *k_value_i = lhs.imag(); return OK; @@ -1352,11 +1387,11 @@ class la_k_get_mc_t : public OpcodeBase { class la_i_transpose_mr_t : public OpcodeBase { public: - MYFLT *imr_lhs; - MYFLT *imr_rhs; + cs_float *imr_lhs; + cs_float *imr_rhs; la_i_mr_create_t *lhs; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(imr_lhs, lhs); toa(imr_rhs, rhs); gmm::copy(gmm::transposed(rhs->mr), lhs->mr); @@ -1366,16 +1401,16 @@ class la_i_transpose_mr_t : public OpcodeBase { class la_k_transpose_mr_t : public OpcodeBase { public: - MYFLT *imr_lhs; - MYFLT *imr_rhs; + cs_float *imr_lhs; + cs_float *imr_rhs; la_i_mr_create_t *lhs; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(imr_lhs, lhs); toa(imr_rhs, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::copy(gmm::transposed(rhs->mr), lhs->mr); return OK; } @@ -1383,11 +1418,11 @@ class la_k_transpose_mr_t : public OpcodeBase { class la_i_transpose_mc_t : public OpcodeBase { public: - MYFLT *imc_lhs; - MYFLT *imc_rhs; + cs_float *imc_lhs; + cs_float *imc_rhs; la_i_mc_create_t *lhs; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(imc_lhs, lhs); toa(imc_rhs, rhs); gmm::copy(gmm::transposed(rhs->mc), lhs->mc); @@ -1397,16 +1432,16 @@ class la_i_transpose_mc_t : public OpcodeBase { class la_k_transpose_mc_t : public OpcodeBase { public: - MYFLT *imc_lhs; - MYFLT *imc_rhs; + cs_float *imc_lhs; + cs_float *imc_rhs; la_i_mc_create_t *lhs; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(imc_lhs, lhs); toa(imc_rhs, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::copy(gmm::transposed(rhs->mc), lhs->mc); return OK; } @@ -1414,11 +1449,11 @@ class la_k_transpose_mc_t : public OpcodeBase { class la_i_conjugate_vr_t : public OpcodeBase { public: - MYFLT *ivr_lhs; - MYFLT *ivr_rhs; + cs_float *ivr_lhs; + cs_float *ivr_rhs; la_i_vr_create_t *lhs; la_i_vr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(ivr_lhs, lhs); toa(ivr_rhs, rhs); gmm::copy(gmm::conjugated(rhs->vr), lhs->vr); @@ -1428,16 +1463,16 @@ class la_i_conjugate_vr_t : public OpcodeBase { class la_k_conjugate_vr_t : public OpcodeBase { public: - MYFLT *ivr_lhs; - MYFLT *ivr_rhs; + cs_float *ivr_lhs; + cs_float *ivr_rhs; la_i_vr_create_t *lhs; la_i_vr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(ivr_lhs, lhs); toa(ivr_rhs, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::copy(gmm::conjugated(rhs->vr), lhs->vr); return OK; } @@ -1445,11 +1480,11 @@ class la_k_conjugate_vr_t : public OpcodeBase { class la_i_conjugate_vc_t : public OpcodeBase { public: - MYFLT *ivc_lhs; - MYFLT *ivc_rhs; + cs_float *ivc_lhs; + cs_float *ivc_rhs; la_i_vc_create_t *lhs; la_i_vc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(ivc_lhs, lhs); toa(ivc_rhs, rhs); gmm::copy(gmm::conjugated(rhs->vc), lhs->vc); @@ -1459,16 +1494,16 @@ class la_i_conjugate_vc_t : public OpcodeBase { class la_k_conjugate_vc_t : public OpcodeBase { public: - MYFLT *ivc_lhs; - MYFLT *ivc_rhs; + cs_float *ivc_lhs; + cs_float *ivc_rhs; la_i_vc_create_t *lhs; la_i_vc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(ivc_lhs, lhs); toa(ivc_rhs, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::copy(gmm::conjugated(rhs->vc), lhs->vc); return OK; } @@ -1476,11 +1511,11 @@ class la_k_conjugate_vc_t : public OpcodeBase { class la_i_conjugate_mr_t : public OpcodeBase { public: - MYFLT *imr_lhs; - MYFLT *imr_rhs; + cs_float *imr_lhs; + cs_float *imr_rhs; la_i_mr_create_t *lhs; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(imr_lhs, lhs); toa(imr_rhs, rhs); gmm::copy(gmm::conjugated(rhs->mr), lhs->mr); @@ -1490,16 +1525,16 @@ class la_i_conjugate_mr_t : public OpcodeBase { class la_k_conjugate_mr_t : public OpcodeBase { public: - MYFLT *imr_lhs; - MYFLT *imr_rhs; + cs_float *imr_lhs; + cs_float *imr_rhs; la_i_mr_create_t *lhs; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(imr_lhs, lhs); toa(imr_rhs, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::copy(gmm::conjugated(rhs->mr), lhs->mr); return OK; } @@ -1507,11 +1542,11 @@ class la_k_conjugate_mr_t : public OpcodeBase { class la_i_conjugate_mc_t : public OpcodeBase { public: - MYFLT *imc_lhs; - MYFLT *imc_rhs; + cs_float *imc_lhs; + cs_float *imc_rhs; la_i_mc_create_t *lhs; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(imc_lhs, lhs); toa(imc_rhs, rhs); gmm::copy(gmm::conjugated(rhs->mc), lhs->mc); @@ -1521,16 +1556,16 @@ class la_i_conjugate_mc_t : public OpcodeBase { class la_k_conjugate_mc_t : public OpcodeBase { public: - MYFLT *imc_lhs; - MYFLT *imc_rhs; + cs_float *imc_lhs; + cs_float *imc_rhs; la_i_mc_create_t *lhs; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(imc_lhs, lhs); toa(imc_rhs, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::copy(gmm::conjugated(rhs->mc), lhs->mc); return OK; } @@ -1538,10 +1573,10 @@ class la_k_conjugate_mc_t : public OpcodeBase { class la_i_norm1_vr_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_vr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::vect_norm1(rhs->vr); return OK; @@ -1550,14 +1585,14 @@ class la_i_norm1_vr_t : public OpcodeBase { class la_k_norm1_vr_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_vr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::vect_norm1(rhs->vr); return OK; @@ -1566,10 +1601,10 @@ class la_k_norm1_vr_t : public OpcodeBase { class la_i_norm1_vc_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_vc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::vect_norm1(rhs->vc); return OK; @@ -1578,14 +1613,14 @@ class la_i_norm1_vc_t : public OpcodeBase { class la_k_norm1_vc_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_vc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::vect_norm1(rhs->vc); return OK; @@ -1594,10 +1629,10 @@ class la_k_norm1_vc_t : public OpcodeBase { class la_i_norm1_mr_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::mat_norm1(rhs->mr); return OK; @@ -1606,14 +1641,14 @@ class la_i_norm1_mr_t : public OpcodeBase { class la_k_norm1_mr_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::mat_norm1(rhs->mr); return OK; @@ -1622,10 +1657,10 @@ class la_k_norm1_mr_t : public OpcodeBase { class la_i_norm1_mc_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::mat_norm1(rhs->mc); return OK; @@ -1634,14 +1669,14 @@ class la_i_norm1_mc_t : public OpcodeBase { class la_k_norm1_mc_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::mat_norm1(rhs->mc); return OK; @@ -1650,10 +1685,10 @@ class la_k_norm1_mc_t : public OpcodeBase { class la_i_norm_euclid_vr_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_vr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::vect_norm2(rhs->vr); return OK; @@ -1662,14 +1697,14 @@ class la_i_norm_euclid_vr_t : public OpcodeBase { class la_k_norm_euclid_vr_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_vr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::vect_norm2(rhs->vr); return OK; @@ -1678,10 +1713,10 @@ class la_k_norm_euclid_vr_t : public OpcodeBase { class la_i_norm_euclid_vc_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_vc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::vect_norm2(rhs->vc); return OK; @@ -1690,14 +1725,14 @@ class la_i_norm_euclid_vc_t : public OpcodeBase { class la_k_norm_euclid_vc_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_vc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::vect_norm2(rhs->vc); return OK; @@ -1706,10 +1741,10 @@ class la_k_norm_euclid_vc_t : public OpcodeBase { class la_i_norm_euclid_mr_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::mat_euclidean_norm(rhs->mr); return OK; @@ -1718,14 +1753,14 @@ class la_i_norm_euclid_mr_t : public OpcodeBase { class la_k_norm_euclid_mr_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::mat_euclidean_norm(rhs->mr); return OK; @@ -1734,10 +1769,10 @@ class la_k_norm_euclid_mr_t : public OpcodeBase { class la_i_norm_euclid_mc_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::mat_euclidean_norm(rhs->mc); return OK; @@ -1746,14 +1781,14 @@ class la_i_norm_euclid_mc_t : public OpcodeBase { class la_k_norm_euclid_mc_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::mat_euclidean_norm(rhs->mc); return OK; @@ -1762,12 +1797,12 @@ class la_k_norm_euclid_mc_t : public OpcodeBase { class la_i_distance_vr_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_1_; - MYFLT *rhs_2_; + cs_float *lhs; + cs_float *rhs_1_; + cs_float *rhs_2_; la_i_vr_create_t *rhs_1; la_i_vr_create_t *rhs_2; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_1_, rhs_1); toa(rhs_2_, rhs_2); *lhs = gmm::vect_dist2(rhs_1->vr, rhs_2->vr); @@ -1777,17 +1812,17 @@ class la_i_distance_vr_t : public OpcodeBase { class la_k_distance_vr_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_1_; - MYFLT *rhs_2_; + cs_float *lhs; + cs_float *rhs_1_; + cs_float *rhs_2_; la_i_vr_create_t *rhs_1; la_i_vr_create_t *rhs_2; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_1_, rhs_1); toa(rhs_2_, rhs_2); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { *lhs = gmm::vect_dist2(rhs_1->vr, rhs_2->vr); return OK; } @@ -1795,12 +1830,12 @@ class la_k_distance_vr_t : public OpcodeBase { class la_i_distance_vc_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_1_; - MYFLT *rhs_2_; + cs_float *lhs; + cs_float *rhs_1_; + cs_float *rhs_2_; la_i_vc_create_t *rhs_1; la_i_vc_create_t *rhs_2; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_1_, rhs_1); toa(rhs_2_, rhs_2); *lhs = gmm::vect_dist2(rhs_1->vc, rhs_2->vc); @@ -1810,17 +1845,17 @@ class la_i_distance_vc_t : public OpcodeBase { class la_k_distance_vc_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_1_; - MYFLT *rhs_2_; + cs_float *lhs; + cs_float *rhs_1_; + cs_float *rhs_2_; la_i_vc_create_t *rhs_1; la_i_vc_create_t *rhs_2; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_1_, rhs_1); toa(rhs_2_, rhs_2); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { *lhs = gmm::vect_dist2(rhs_1->vc, rhs_2->vc); return OK; } @@ -1828,10 +1863,10 @@ class la_k_distance_vc_t : public OpcodeBase { class la_i_norm_max_mr_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::mat_maxnorm(rhs->mr); return OK; @@ -1840,14 +1875,14 @@ class la_i_norm_max_mr_t : public OpcodeBase { class la_k_norm_max_mr_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::mat_maxnorm(rhs->mr); return OK; @@ -1856,10 +1891,10 @@ class la_k_norm_max_mr_t : public OpcodeBase { class la_i_norm_max_mc_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::mat_maxnorm(rhs->mc); return OK; @@ -1868,14 +1903,14 @@ class la_i_norm_max_mc_t : public OpcodeBase { class la_k_norm_max_mc_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::mat_maxnorm(rhs->mc); return OK; @@ -1884,10 +1919,10 @@ class la_k_norm_max_mc_t : public OpcodeBase { class la_i_norm_inf_vr_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_vr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::vect_norminf(rhs->vr); return OK; @@ -1896,14 +1931,14 @@ class la_i_norm_inf_vr_t : public OpcodeBase { class la_k_norm_inf_vr_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_vr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::vect_norminf(rhs->vr); return OK; @@ -1912,10 +1947,10 @@ class la_k_norm_inf_vr_t : public OpcodeBase { class la_i_norm_inf_vc_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_vc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::vect_norminf(rhs->vc); return OK; @@ -1924,14 +1959,14 @@ class la_i_norm_inf_vc_t : public OpcodeBase { class la_k_norm_inf_vc_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_vc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::vect_norminf(rhs->vc); return OK; @@ -1940,10 +1975,10 @@ class la_k_norm_inf_vc_t : public OpcodeBase { class la_i_norm_inf_mr_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::mat_norminf(rhs->mr); return OK; @@ -1952,14 +1987,14 @@ class la_i_norm_inf_mr_t : public OpcodeBase { class la_k_norm_inf_mr_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::mat_norminf(rhs->mr); return OK; @@ -1968,10 +2003,10 @@ class la_k_norm_inf_mr_t : public OpcodeBase { class la_i_norm_inf_mc_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::mat_norminf(rhs->mc); return OK; @@ -1980,14 +2015,14 @@ class la_i_norm_inf_mc_t : public OpcodeBase { class la_k_norm_inf_mc_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::mat_norminf(rhs->mc); return OK; @@ -1996,10 +2031,10 @@ class la_k_norm_inf_mc_t : public OpcodeBase { class la_i_trace_mr_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::mat_trace(rhs->mr); return OK; @@ -2008,14 +2043,14 @@ class la_i_trace_mr_t : public OpcodeBase { class la_k_trace_mr_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::mat_trace(rhs->mr); return OK; @@ -2024,13 +2059,13 @@ class la_k_trace_mr_t : public OpcodeBase { class la_i_trace_mc_t : public OpcodeBase { public: - MYFLT *lhs_r; - MYFLT *lhs_i; - MYFLT *rhs_; + cs_float *lhs_r; + cs_float *lhs_i; + cs_float *rhs_; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); - const std::complex &lhs = gmm::mat_trace(rhs->mc); + const std::complex &lhs = gmm::mat_trace(rhs->mc); *lhs_r = lhs.real(); *lhs_i = lhs.imag(); return OK; @@ -2039,17 +2074,17 @@ class la_i_trace_mc_t : public OpcodeBase { class la_k_trace_mc_t : public OpcodeBase { public: - MYFLT *lhs_r; - MYFLT *lhs_i; - MYFLT *rhs_; + cs_float *lhs_r; + cs_float *lhs_i; + cs_float *rhs_; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { toa(rhs_, rhs); - const std::complex &lhs = gmm::mat_trace(rhs->mc); + const std::complex &lhs = gmm::mat_trace(rhs->mc); *lhs_r = lhs.real(); *lhs_i = lhs.imag(); return OK; @@ -2058,10 +2093,10 @@ class la_k_trace_mc_t : public OpcodeBase { class la_i_lu_det_mr_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::lu_det(rhs->mr); return OK; @@ -2070,14 +2105,14 @@ class la_i_lu_det_mr_t : public OpcodeBase { class la_k_lu_det_mr_t : public OpcodeBase { public: - MYFLT *lhs; - MYFLT *rhs_; + cs_float *lhs; + cs_float *rhs_; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { toa(rhs_, rhs); *lhs = gmm::lu_det(rhs->mr); return OK; @@ -2086,13 +2121,13 @@ class la_k_lu_det_mr_t : public OpcodeBase { class la_i_lu_det_mc_t : public OpcodeBase { public: - MYFLT *lhs_r; - MYFLT *lhs_i; - MYFLT *rhs_; + cs_float *lhs_r; + cs_float *lhs_i; + cs_float *rhs_; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); - const std::complex &lhs = gmm::lu_det(rhs->mc); + const std::complex &lhs = gmm::lu_det(rhs->mc); *lhs_r = lhs.real(); *lhs_i = lhs.imag(); return OK; @@ -2101,17 +2136,17 @@ class la_i_lu_det_mc_t : public OpcodeBase { class la_k_lu_det_mc_t : public OpcodeBase { public: - MYFLT *lhs_r; - MYFLT *lhs_i; - MYFLT *rhs_; + cs_float *lhs_r; + cs_float *lhs_i; + cs_float *rhs_; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { toa(rhs_, rhs); - const std::complex &lhs = gmm::lu_det(rhs->mc); + const std::complex &lhs = gmm::lu_det(rhs->mc); *lhs_r = lhs.real(); *lhs_i = lhs.imag(); return OK; @@ -2125,13 +2160,13 @@ class la_k_lu_det_mc_t : public OpcodeBase { */ class la_i_add_vr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vr_create_t *lhs; la_i_vr_create_t *rhs_a; la_i_vr_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2149,19 +2184,19 @@ class la_i_add_vr_t : public OpcodeBase { */ class la_k_add_vr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vr_create_t *lhs; la_i_vr_create_t *rhs_a; la_i_vr_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { for (size_t i = 0, n = rhs_a->vr.size(); i < n; ++i) { lhs->vr[i] = rhs_a->vr[i] + rhs_b->vr[i]; } @@ -2176,13 +2211,13 @@ class la_k_add_vr_t : public OpcodeBase { */ class la_i_add_vc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vc_create_t *lhs; la_i_vc_create_t *rhs_a; la_i_vc_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2200,19 +2235,19 @@ class la_i_add_vc_t : public OpcodeBase { */ class la_k_add_vc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vc_create_t *lhs; la_i_vc_create_t *rhs_a; la_i_vc_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { for (size_t i = 0, n = rhs_a->vc.size(); i < n; ++i) { lhs->vc[i] = rhs_a->vc[i] + rhs_b->vc[i]; } @@ -2227,13 +2262,13 @@ class la_k_add_vc_t : public OpcodeBase { */ class la_i_add_mr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_mr_create_t *lhs; la_i_mr_create_t *rhs_a; la_i_mr_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2256,15 +2291,15 @@ class la_i_add_mr_t : public OpcodeBase { */ class la_k_add_mr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_mr_create_t *lhs; la_i_mr_create_t *rhs_a; la_i_mr_create_t *rhs_b; size_t rowN; size_t columnN; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2272,7 +2307,7 @@ class la_k_add_mr_t : public OpcodeBase { columnN = gmm::mat_ncols(rhs_a->mr); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { for (size_t rowI = 0; rowI < rowN; ++rowI) { for (size_t columnI = 0; columnI < columnN; ++columnI) { lhs->mr(rowI, columnI) = @@ -2290,13 +2325,13 @@ class la_k_add_mr_t : public OpcodeBase { */ class la_i_add_mc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_mc_create_t *lhs; la_i_mc_create_t *rhs_a; la_i_mc_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2319,15 +2354,15 @@ class la_i_add_mc_t : public OpcodeBase { */ class la_k_add_mc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_mc_create_t *lhs; la_i_mc_create_t *rhs_a; la_i_mc_create_t *rhs_b; size_t rowN; size_t columnN; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2335,7 +2370,7 @@ class la_k_add_mc_t : public OpcodeBase { columnN = gmm::mat_ncols(rhs_a->mc); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { for (size_t rowI = 0; rowI < rowN; ++rowI) { for (size_t columnI = 0; columnI < columnN; ++columnI) { lhs->mc(rowI, columnI) = @@ -2353,13 +2388,13 @@ class la_k_add_mc_t : public OpcodeBase { */ class la_i_subtract_vr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vr_create_t *lhs; la_i_vr_create_t *rhs_a; la_i_vr_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2377,19 +2412,19 @@ class la_i_subtract_vr_t : public OpcodeBase { */ class la_k_subtract_vr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vr_create_t *lhs; la_i_vr_create_t *rhs_a; la_i_vr_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { for (size_t i = 0, n = rhs_a->vr.size(); i < n; ++i) { lhs->vr[i] = rhs_a->vr[i] - rhs_b->vr[i]; } @@ -2404,13 +2439,13 @@ class la_k_subtract_vr_t : public OpcodeBase { */ class la_i_subtract_vc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vc_create_t *lhs; la_i_vc_create_t *rhs_a; la_i_vc_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2428,19 +2463,19 @@ class la_i_subtract_vc_t : public OpcodeBase { */ class la_k_subtract_vc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vc_create_t *lhs; la_i_vc_create_t *rhs_a; la_i_vc_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { for (size_t i = 0, n = rhs_a->vc.size(); i < n; ++i) { lhs->vc[i] = rhs_a->vc[i] - rhs_b->vc[i]; } @@ -2455,13 +2490,13 @@ class la_k_subtract_vc_t : public OpcodeBase { */ class la_i_subtract_mr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_mr_create_t *lhs; la_i_mr_create_t *rhs_a; la_i_mr_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2484,15 +2519,15 @@ class la_i_subtract_mr_t : public OpcodeBase { */ class la_k_subtract_mr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_mr_create_t *lhs; la_i_mr_create_t *rhs_a; la_i_mr_create_t *rhs_b; size_t rowN; size_t columnN; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2500,7 +2535,7 @@ class la_k_subtract_mr_t : public OpcodeBase { columnN = gmm::mat_ncols(rhs_a->mr); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { for (size_t rowI = 0; rowI < rowN; ++rowI) { for (size_t columnI = 0; columnI < columnN; ++columnI) { lhs->mr(rowI, columnI) = @@ -2518,13 +2553,13 @@ class la_k_subtract_mr_t : public OpcodeBase { */ class la_i_subtract_mc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_mc_create_t *lhs; la_i_mc_create_t *rhs_a; la_i_mc_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2547,15 +2582,15 @@ class la_i_subtract_mc_t : public OpcodeBase { */ class la_k_subtract_mc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_mc_create_t *lhs; la_i_mc_create_t *rhs_a; la_i_mc_create_t *rhs_b; size_t rowN; size_t columnN; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2563,7 +2598,7 @@ class la_k_subtract_mc_t : public OpcodeBase { columnN = gmm::mat_ncols(rhs_a->mc); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { for (size_t rowI = 0; rowI < rowN; ++rowI) { for (size_t columnI = 0; columnI < columnN; ++columnI) { lhs->mc(rowI, columnI) = @@ -2581,13 +2616,13 @@ class la_k_subtract_mc_t : public OpcodeBase { */ class la_i_multiply_vr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vr_create_t *lhs; la_i_vr_create_t *rhs_a; la_i_vr_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2605,19 +2640,19 @@ class la_i_multiply_vr_t : public OpcodeBase { */ class la_k_multiply_vr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vr_create_t *lhs; la_i_vr_create_t *rhs_a; la_i_vr_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { for (size_t i = 0, n = rhs_a->vr.size(); i < n; ++i) { lhs->vr[i] = rhs_a->vr[i] * rhs_b->vr[i]; } @@ -2632,13 +2667,13 @@ class la_k_multiply_vr_t : public OpcodeBase { */ class la_i_multiply_vc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vc_create_t *lhs; la_i_vc_create_t *rhs_a; la_i_vc_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2656,19 +2691,19 @@ class la_i_multiply_vc_t : public OpcodeBase { */ class la_k_multiply_vc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vc_create_t *lhs; la_i_vc_create_t *rhs_a; la_i_vc_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { for (size_t i = 0, n = rhs_a->vc.size(); i < n; ++i) { lhs->vc[i] = rhs_a->vc[i] * rhs_b->vc[i]; } @@ -2683,13 +2718,13 @@ class la_k_multiply_vc_t : public OpcodeBase { */ class la_i_multiply_mr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_mr_create_t *lhs; la_i_mr_create_t *rhs_a; la_i_mr_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2712,15 +2747,15 @@ class la_i_multiply_mr_t : public OpcodeBase { */ class la_k_multiply_mr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_mr_create_t *lhs; la_i_mr_create_t *rhs_a; la_i_mr_create_t *rhs_b; size_t rowN; size_t columnN; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2728,7 +2763,7 @@ class la_k_multiply_mr_t : public OpcodeBase { columnN = gmm::mat_ncols(rhs_a->mr); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { for (size_t rowI = 0; rowI < rowN; ++rowI) { for (size_t columnI = 0; columnI < columnN; ++columnI) { lhs->mr(rowI, columnI) = @@ -2746,13 +2781,13 @@ class la_k_multiply_mr_t : public OpcodeBase { */ class la_i_multiply_mc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_mc_create_t *lhs; la_i_mc_create_t *rhs_a; la_i_mc_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2775,15 +2810,15 @@ class la_i_multiply_mc_t : public OpcodeBase { */ class la_k_multiply_mc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_mc_create_t *lhs; la_i_mc_create_t *rhs_a; la_i_mc_create_t *rhs_b; size_t rowN; size_t columnN; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2791,7 +2826,7 @@ class la_k_multiply_mc_t : public OpcodeBase { columnN = gmm::mat_ncols(rhs_a->mc); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { for (size_t rowI = 0; rowI < rowN; ++rowI) { for (size_t columnI = 0; columnI < columnN; ++columnI) { lhs->mc(rowI, columnI) = @@ -2809,13 +2844,13 @@ class la_k_multiply_mc_t : public OpcodeBase { */ class la_i_divide_vr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vr_create_t *lhs; la_i_vr_create_t *rhs_a; la_i_vr_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2833,19 +2868,19 @@ class la_i_divide_vr_t : public OpcodeBase { */ class la_k_divide_vr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vr_create_t *lhs; la_i_vr_create_t *rhs_a; la_i_vr_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { for (size_t i = 0, n = rhs_a->vr.size(); i < n; ++i) { lhs->vr[i] = rhs_a->vr[i] / rhs_b->vr[i]; } @@ -2860,13 +2895,13 @@ class la_k_divide_vr_t : public OpcodeBase { */ class la_i_divide_vc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vc_create_t *lhs; la_i_vc_create_t *rhs_a; la_i_vc_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2884,19 +2919,19 @@ class la_i_divide_vc_t : public OpcodeBase { */ class la_k_divide_vc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vc_create_t *lhs; la_i_vc_create_t *rhs_a; la_i_vc_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { for (size_t i = 0, n = rhs_a->vc.size(); i < n; ++i) { lhs->vc[i] = rhs_a->vc[i] / rhs_b->vc[i]; } @@ -2911,13 +2946,13 @@ class la_k_divide_vc_t : public OpcodeBase { */ class la_i_divide_mr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_mr_create_t *lhs; la_i_mr_create_t *rhs_a; la_i_mr_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2940,15 +2975,15 @@ class la_i_divide_mr_t : public OpcodeBase { */ class la_k_divide_mr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_mr_create_t *lhs; la_i_mr_create_t *rhs_a; la_i_mr_create_t *rhs_b; size_t rowN; size_t columnN; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -2956,7 +2991,7 @@ class la_k_divide_mr_t : public OpcodeBase { columnN = gmm::mat_ncols(rhs_a->mr); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { for (size_t rowI = 0; rowI < rowN; ++rowI) { for (size_t columnI = 0; columnI < columnN; ++columnI) { lhs->mr(rowI, columnI) = @@ -2974,13 +3009,13 @@ class la_k_divide_mr_t : public OpcodeBase { */ class la_i_divide_mc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_mc_create_t *lhs; la_i_mc_create_t *rhs_a; la_i_mc_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -3003,15 +3038,15 @@ class la_i_divide_mc_t : public OpcodeBase { */ class la_k_divide_mc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_mc_create_t *lhs; la_i_mc_create_t *rhs_a; la_i_mc_create_t *rhs_b; size_t rowN; size_t columnN; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -3019,7 +3054,7 @@ class la_k_divide_mc_t : public OpcodeBase { columnN = gmm::mat_ncols(rhs_a->mc); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { for (size_t rowI = 0; rowI < rowN; ++rowI) { for (size_t columnI = 0; columnI < columnN; ++columnI) { lhs->mc(rowI, columnI) = @@ -3037,12 +3072,12 @@ class la_k_divide_mc_t : public OpcodeBase { */ class la_i_dot_vr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vr_create_t *rhs_a; la_i_vr_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); *lhs_ = gmm::vect_sp(rhs_a->vr, rhs_b->vr); @@ -3057,17 +3092,17 @@ class la_i_dot_vr_t : public OpcodeBase { */ class la_k_dot_vr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vr_create_t *rhs_a; la_i_vr_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { *lhs_ = gmm::vect_sp(rhs_a->vr, rhs_b->vr); return OK; } @@ -3080,16 +3115,16 @@ class la_k_dot_vr_t : public OpcodeBase { */ class la_i_dot_vc_t : public OpcodeBase { public: - MYFLT *lhs_r; - MYFLT *lhs_i; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_r; + cs_float *lhs_i; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vc_create_t *rhs_a; la_i_vc_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); - std::complex lhs = gmm::vect_sp(rhs_a->vc, rhs_b->vc); + std::complex lhs = gmm::vect_sp(rhs_a->vc, rhs_b->vc); *lhs_r = lhs.real(); *lhs_i = lhs.imag(); return OK; @@ -3103,19 +3138,19 @@ class la_i_dot_vc_t : public OpcodeBase { */ class la_k_dot_vc_t : public OpcodeBase { public: - MYFLT *lhs_r; - MYFLT *lhs_i; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_r; + cs_float *lhs_i; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vc_create_t *rhs_a; la_i_vc_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); return OK; } - int kontrol(CSOUND *) { - std::complex lhs = gmm::vect_sp(rhs_a->vc, rhs_b->vc); + int32_t kontrol(CSOUND *) { + std::complex lhs = gmm::vect_sp(rhs_a->vc, rhs_b->vc); *lhs_r = lhs.real(); *lhs_i = lhs.imag(); return OK; @@ -3124,13 +3159,13 @@ class la_k_dot_vc_t : public OpcodeBase { class la_i_dot_mr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_mr_create_t *lhs; la_i_mr_create_t *rhs_a; la_i_mr_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -3141,20 +3176,20 @@ class la_i_dot_mr_t : public OpcodeBase { class la_k_dot_mr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_mr_create_t *lhs; la_i_mr_create_t *rhs_a; la_i_mr_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); gmm::mult(rhs_a->mr, rhs_b->mr, lhs->mr); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::mult(rhs_a->mr, rhs_b->mr, lhs->mr); return OK; } @@ -3162,13 +3197,13 @@ class la_k_dot_mr_t : public OpcodeBase { class la_i_dot_mc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_mc_create_t *lhs; la_i_mc_create_t *rhs_a; la_i_mc_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -3179,19 +3214,19 @@ class la_i_dot_mc_t : public OpcodeBase { class la_k_dot_mc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_mc_create_t *lhs; la_i_mc_create_t *rhs_a; la_i_mc_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::mult(rhs_a->mc, rhs_b->mc, lhs->mc); return OK; } @@ -3199,13 +3234,13 @@ class la_k_dot_mc_t : public OpcodeBase { class la_i_dot_mr_vr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vr_create_t *lhs; la_i_mr_create_t *rhs_a; la_i_vr_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -3216,19 +3251,19 @@ class la_i_dot_mr_vr_t : public OpcodeBase { class la_k_dot_mr_vr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vr_create_t *lhs; la_i_mr_create_t *rhs_a; la_i_vr_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::mult(rhs_a->mr, rhs_b->vr, lhs->vr); return OK; } @@ -3236,13 +3271,13 @@ class la_k_dot_mr_vr_t : public OpcodeBase { class la_i_dot_mc_vc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vc_create_t *lhs; la_i_mc_create_t *rhs_a; la_i_vc_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); @@ -3253,19 +3288,19 @@ class la_i_dot_mc_vc_t : public OpcodeBase { class la_k_dot_mc_vc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_a_; - MYFLT *rhs_b_; + cs_float *lhs_; + cs_float *rhs_a_; + cs_float *rhs_b_; la_i_vc_create_t *lhs; la_i_mc_create_t *rhs_a; la_i_vc_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_a_, rhs_a); toa(rhs_b_, rhs_b); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::mult(rhs_a->mc, rhs_b->vc, lhs->vc); return OK; } @@ -3273,12 +3308,12 @@ class la_k_dot_mc_vc_t : public OpcodeBase { class la_i_invert_mr_t : public OpcodeBase { public: - MYFLT *imr_lhs; - MYFLT *icondition; - MYFLT *imr_rhs; + cs_float *imr_lhs; + cs_float *icondition; + cs_float *imr_rhs; la_i_mr_create_t *lhs; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(imr_lhs, lhs); toa(imr_rhs, rhs); gmm::copy(rhs->mr, lhs->mr); @@ -3289,17 +3324,17 @@ class la_i_invert_mr_t : public OpcodeBase { class la_k_invert_mr_t : public OpcodeBase { public: - MYFLT *imr_lhs; - MYFLT *kcondition; - MYFLT *imr_rhs; + cs_float *imr_lhs; + cs_float *kcondition; + cs_float *imr_rhs; la_i_mr_create_t *lhs; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(imr_lhs, lhs); toa(imr_rhs, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::copy(rhs->mr, lhs->mr); *kcondition = gmm::lu_inverse(lhs->mr); return OK; @@ -3308,17 +3343,17 @@ class la_k_invert_mr_t : public OpcodeBase { class la_i_invert_mc_t : public OpcodeBase { public: - MYFLT *imc_lhs; - MYFLT *icondition_r; - MYFLT *icondition_i; - MYFLT *imc_rhs; + cs_float *imc_lhs; + cs_float *icondition_r; + cs_float *icondition_i; + cs_float *imc_rhs; la_i_mc_create_t *lhs; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(imc_lhs, lhs); toa(imc_rhs, rhs); gmm::copy(rhs->mc, lhs->mc); - std::complex condition = gmm::lu_inverse(lhs->mc); + std::complex condition = gmm::lu_inverse(lhs->mc); *icondition_r = condition.real(); *icondition_i = condition.imag(); return OK; @@ -3327,20 +3362,20 @@ class la_i_invert_mc_t : public OpcodeBase { class la_k_invert_mc_t : public OpcodeBase { public: - MYFLT *imc_lhs; - MYFLT *kcondition_r; - MYFLT *kcondition_i; - MYFLT *imc_rhs; + cs_float *imc_lhs; + cs_float *kcondition_r; + cs_float *kcondition_i; + cs_float *imc_rhs; la_i_mc_create_t *lhs; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(imc_lhs, lhs); toa(imc_rhs, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::copy(rhs->mc, lhs->mc); - std::complex condition = gmm::lu_inverse(lhs->mc); + std::complex condition = gmm::lu_inverse(lhs->mc); *kcondition_r = condition.real(); *kcondition_i = condition.imag(); return OK; @@ -3349,12 +3384,12 @@ class la_k_invert_mc_t : public OpcodeBase { class la_i_upper_solve_mr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_; - MYFLT *is_unit; + cs_float *lhs_; + cs_float *rhs_; + cs_float *is_unit; la_i_vr_create_t *lhs; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_, rhs); gmm::upper_tri_solve(rhs->mr, lhs->vr, bool(*is_unit)); @@ -3364,17 +3399,17 @@ class la_i_upper_solve_mr_t : public OpcodeBase { class la_k_upper_solve_mr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_; - MYFLT *is_unit; + cs_float *lhs_; + cs_float *rhs_; + cs_float *is_unit; la_i_vr_create_t *lhs; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::upper_tri_solve(rhs->mr, lhs->vr, bool(*is_unit)); return OK; } @@ -3382,12 +3417,12 @@ class la_k_upper_solve_mr_t : public OpcodeBase { class la_i_upper_solve_mc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_; - MYFLT *is_unit; + cs_float *lhs_; + cs_float *rhs_; + cs_float *is_unit; la_i_vc_create_t *lhs; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_, rhs); gmm::upper_tri_solve(rhs->mc, lhs->vc, bool(*is_unit)); @@ -3397,17 +3432,17 @@ class la_i_upper_solve_mc_t : public OpcodeBase { class la_k_upper_solve_mc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_; - MYFLT *is_unit; + cs_float *lhs_; + cs_float *rhs_; + cs_float *is_unit; la_i_vc_create_t *lhs; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::upper_tri_solve(rhs->mc, lhs->vc, bool(*is_unit)); return OK; } @@ -3415,12 +3450,12 @@ class la_k_upper_solve_mc_t : public OpcodeBase { class la_i_lower_solve_mr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_; - MYFLT *is_unit; + cs_float *lhs_; + cs_float *rhs_; + cs_float *is_unit; la_i_vr_create_t *lhs; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_, rhs); gmm::lower_tri_solve(rhs->mr, lhs->vr, bool(*is_unit)); @@ -3430,17 +3465,17 @@ class la_i_lower_solve_mr_t : public OpcodeBase { class la_k_lower_solve_mr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_; - MYFLT *is_unit; + cs_float *lhs_; + cs_float *rhs_; + cs_float *is_unit; la_i_vr_create_t *lhs; la_i_mr_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::lower_tri_solve(rhs->mr, lhs->vr, bool(*is_unit)); return OK; } @@ -3448,12 +3483,12 @@ class la_k_lower_solve_mr_t : public OpcodeBase { class la_i_lower_solve_mc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_; - MYFLT *is_unit; + cs_float *lhs_; + cs_float *rhs_; + cs_float *is_unit; la_i_vc_create_t *lhs; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_, rhs); gmm::lower_tri_solve(rhs->mc, lhs->vc, bool(*is_unit)); @@ -3463,17 +3498,17 @@ class la_i_lower_solve_mc_t : public OpcodeBase { class la_k_lower_solve_mc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *rhs_; - MYFLT *is_unit; + cs_float *lhs_; + cs_float *rhs_; + cs_float *is_unit; la_i_vc_create_t *lhs; la_i_mc_create_t *rhs; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::lower_tri_solve(rhs->mc, lhs->vc, bool(*is_unit)); return OK; } @@ -3481,20 +3516,16 @@ class la_k_lower_solve_mc_t : public OpcodeBase { class la_i_lu_factor_mr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *pivot_; - MYFLT *isize; - MYFLT *rhs_; + cs_float *lhs_; + cs_float *pivot_; + cs_float *isize; + cs_float *rhs_; la_i_mr_create_t *lhs; la_i_vr_create_t *pivot; la_i_mr_create_t *rhs; -#if defined(GMM_VERSION) gmm::lapack_ipvt pivot__; -#else - std::vector pivot__; -#endif size_t pivot_size; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_, pivot); toa(rhs_, rhs); @@ -3503,11 +3534,7 @@ class la_i_lu_factor_mr_t : public OpcodeBase { gmm::copy(rhs->mr, lhs->mr); *isize = gmm::lu_factor(lhs->mr, pivot__); for (size_t i = 0; i < pivot_size; ++i) { -#if defined(GMM_VERSION) - pivot->vr[i] = pivot__.get(i); -#else - pivot->vr[i] = pivot__[i]; -#endif + pivot->vr[i] = la_ipvt_get(pivot__, i); } return OK; } @@ -3515,36 +3542,28 @@ class la_i_lu_factor_mr_t : public OpcodeBase { class la_k_lu_factor_mr_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *pivot_; - MYFLT *ksize; - MYFLT *rhs_; + cs_float *lhs_; + cs_float *pivot_; + cs_float *ksize; + cs_float *rhs_; la_i_mr_create_t *lhs; la_i_vr_create_t *pivot; la_i_mr_create_t *rhs; -#if defined(GMM_VERSION) gmm::lapack_ipvt pivot__; -#else - std::vector pivot__; -#endif size_t pivot_size; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_, pivot); toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { pivot_size = gmm::mat_nrows(rhs->mr); pivot__.resize(pivot_size); gmm::copy(rhs->mr, lhs->mr); *ksize = gmm::lu_factor(lhs->mr, pivot__); for (size_t i = 0; i < pivot_size; ++i) { -#if defined(GMM_VERSION) - pivot->vr[i] = pivot__.get(i); -#else - pivot->vr[i] = pivot__[i]; -#endif + pivot->vr[i] = la_ipvt_get(pivot__, i); } return OK; } @@ -3552,20 +3571,16 @@ class la_k_lu_factor_mr_t : public OpcodeBase { class la_i_lu_factor_mc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *pivot_; - MYFLT *isize; - MYFLT *rhs_; + cs_float *lhs_; + cs_float *pivot_; + cs_float *isize; + cs_float *rhs_; la_i_mc_create_t *lhs; la_i_vr_create_t *pivot; la_i_mc_create_t *rhs; -#if defined(GMM_VERSION) gmm::lapack_ipvt pivot__; -#else - std::vector pivot__; -#endif size_t pivot_size; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_, pivot); toa(rhs_, rhs); @@ -3574,11 +3589,7 @@ class la_i_lu_factor_mc_t : public OpcodeBase { gmm::copy(rhs->mc, lhs->mc); *isize = gmm::lu_factor(lhs->mc, pivot__); for (size_t i = 0; i < pivot_size; ++i) { -#if defined(GMM_VERSION) - pivot->vr[i] = pivot__.get(i); -#else - pivot->vr[i] = pivot__[i]; -#endif + pivot->vr[i] = la_ipvt_get(pivot__, i); } return OK; } @@ -3586,36 +3597,28 @@ class la_i_lu_factor_mc_t : public OpcodeBase { class la_k_lu_factor_mc_t : public OpcodeBase { public: - MYFLT *lhs_; - MYFLT *pivot_; - MYFLT *ksize; - MYFLT *rhs_; + cs_float *lhs_; + cs_float *pivot_; + cs_float *ksize; + cs_float *rhs_; la_i_mc_create_t *lhs; la_i_vr_create_t *pivot; la_i_mc_create_t *rhs; -#if defined(GMM_VERSION) gmm::lapack_ipvt pivot__; -#else - std::vector pivot__; -#endif size_t pivot_size; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_, lhs); toa(rhs_, pivot); toa(rhs_, rhs); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { pivot_size = gmm::mat_nrows(rhs->mc); pivot__.resize(pivot_size); gmm::copy(rhs->mc, lhs->mc); *ksize = gmm::lu_factor(lhs->mc, pivot__); for (size_t i = 0; i < pivot_size; ++i) { -#if defined(GMM_VERSION) - pivot->vr[i] = pivot__.get(i); -#else - pivot->vr[i] = pivot__[i]; -#endif + pivot->vr[i] = la_ipvt_get(pivot__, i); } return OK; } @@ -3623,13 +3626,13 @@ class la_k_lu_factor_mc_t : public OpcodeBase { class la_i_lu_solve_mr_t : public OpcodeBase { public: - MYFLT *lhs_x_; - MYFLT *rhs_A_; - MYFLT *rhs_b_; + cs_float *lhs_x_; + cs_float *rhs_A_; + cs_float *rhs_b_; la_i_vr_create_t *lhs_x; la_i_mr_create_t *rhs_A; la_i_vr_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_x_, lhs_x); toa(rhs_A_, rhs_A); toa(rhs_b_, rhs_b); @@ -3640,19 +3643,19 @@ class la_i_lu_solve_mr_t : public OpcodeBase { class la_k_lu_solve_mr_t : public OpcodeBase { public: - MYFLT *lhs_x_; - MYFLT *rhs_A_; - MYFLT *rhs_b_; + cs_float *lhs_x_; + cs_float *rhs_A_; + cs_float *rhs_b_; la_i_vr_create_t *lhs_x; la_i_mr_create_t *rhs_A; la_i_vr_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_x_, lhs_x); toa(rhs_A_, rhs_A); toa(rhs_b_, rhs_b); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::lu_solve(rhs_A->mr, lhs_x->vr, rhs_b->vr); return OK; } @@ -3660,13 +3663,13 @@ class la_k_lu_solve_mr_t : public OpcodeBase { class la_i_lu_solve_mc_t : public OpcodeBase { public: - MYFLT *lhs_x_; - MYFLT *rhs_A_; - MYFLT *rhs_b_; + cs_float *lhs_x_; + cs_float *rhs_A_; + cs_float *rhs_b_; la_i_vc_create_t *lhs_x; la_i_mc_create_t *rhs_A; la_i_vc_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_x_, lhs_x); toa(rhs_A_, rhs_A); toa(rhs_b_, rhs_b); @@ -3677,19 +3680,19 @@ class la_i_lu_solve_mc_t : public OpcodeBase { class la_k_lu_solve_mc_t : public OpcodeBase { public: - MYFLT *lhs_x_; - MYFLT *rhs_A_; - MYFLT *rhs_b_; + cs_float *lhs_x_; + cs_float *rhs_A_; + cs_float *rhs_b_; la_i_vc_create_t *lhs_x; la_i_mc_create_t *rhs_A; la_i_vc_create_t *rhs_b; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_x_, lhs_x); toa(rhs_A_, rhs_A); toa(rhs_b_, rhs_b); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::lu_solve(rhs_A->mc, lhs_x->vc, rhs_b->vc); return OK; } @@ -3697,13 +3700,13 @@ class la_k_lu_solve_mc_t : public OpcodeBase { class la_i_qr_factor_mr_t : public OpcodeBase { public: - MYFLT *lhs_Q_; - MYFLT *lhs_R_; - MYFLT *rhs_A_; + cs_float *lhs_Q_; + cs_float *lhs_R_; + cs_float *rhs_A_; la_i_mr_create_t *lhs_Q; la_i_mr_create_t *lhs_R; la_i_mr_create_t *rhs_A; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_Q_, lhs_Q); toa(lhs_R_, lhs_R); toa(rhs_A_, rhs_A); @@ -3714,19 +3717,19 @@ class la_i_qr_factor_mr_t : public OpcodeBase { class la_k_qr_factor_mr_t : public OpcodeBase { public: - MYFLT *lhs_Q_; - MYFLT *lhs_R_; - MYFLT *rhs_A_; + cs_float *lhs_Q_; + cs_float *lhs_R_; + cs_float *rhs_A_; la_i_mr_create_t *lhs_Q; la_i_mr_create_t *lhs_R; la_i_mr_create_t *rhs_A; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_Q_, lhs_Q); toa(lhs_R_, lhs_R); toa(rhs_A_, rhs_A); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::qr_factor(rhs_A->mr, lhs_Q->mr, lhs_R->mr); return OK; } @@ -3734,13 +3737,13 @@ class la_k_qr_factor_mr_t : public OpcodeBase { class la_i_qr_factor_mc_t : public OpcodeBase { public: - MYFLT *lhs_Q_; - MYFLT *lhs_R_; - MYFLT *rhs_A_; + cs_float *lhs_Q_; + cs_float *lhs_R_; + cs_float *rhs_A_; la_i_mc_create_t *lhs_Q; la_i_mc_create_t *lhs_R; la_i_mc_create_t *rhs_A; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_Q_, lhs_Q); toa(lhs_R_, lhs_R); toa(rhs_A_, rhs_A); @@ -3751,19 +3754,19 @@ class la_i_qr_factor_mc_t : public OpcodeBase { class la_k_qr_factor_mc_t : public OpcodeBase { public: - MYFLT *lhs_Q_; - MYFLT *lhs_R_; - MYFLT *rhs_A_; + cs_float *lhs_Q_; + cs_float *lhs_R_; + cs_float *rhs_A_; la_i_mc_create_t *lhs_Q; la_i_mc_create_t *lhs_R; la_i_mc_create_t *rhs_A; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_Q_, lhs_Q); toa(lhs_R_, lhs_R); toa(rhs_A_, rhs_A); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::qr_factor(rhs_A->mc, lhs_Q->mc, lhs_R->mc); return OK; } @@ -3771,12 +3774,12 @@ class la_k_qr_factor_mc_t : public OpcodeBase { class la_i_qr_eigen_mr_t : public OpcodeBase { public: - MYFLT *lhs_eigenvalues_; - MYFLT *rhs_A_; - MYFLT *itolerance; + cs_float *lhs_eigenvalues_; + cs_float *rhs_A_; + cs_float *itolerance; la_i_vr_create_t *lhs_eigenvalues; la_i_mr_create_t *rhs_A; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_eigenvalues_, lhs_eigenvalues); toa(rhs_A_, rhs_A); gmm::implicit_qr_algorithm(rhs_A->mr, lhs_eigenvalues->vr, @@ -3787,17 +3790,17 @@ class la_i_qr_eigen_mr_t : public OpcodeBase { class la_k_qr_eigen_mr_t : public OpcodeBase { public: - MYFLT *lhs_eigenvalues_; - MYFLT *rhs_A_; - MYFLT *ktolerance; + cs_float *lhs_eigenvalues_; + cs_float *rhs_A_; + cs_float *ktolerance; la_i_vr_create_t *lhs_eigenvalues; la_i_mr_create_t *rhs_A; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_eigenvalues_, lhs_eigenvalues); toa(rhs_A_, rhs_A); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::implicit_qr_algorithm(rhs_A->mr, lhs_eigenvalues->vr, double(*ktolerance)); return OK; @@ -3806,12 +3809,12 @@ class la_k_qr_eigen_mr_t : public OpcodeBase { class la_i_qr_eigen_mc_t : public OpcodeBase { public: - MYFLT *lhs_eigenvalues_; - MYFLT *rhs_A_; - MYFLT *itolerance; + cs_float *lhs_eigenvalues_; + cs_float *rhs_A_; + cs_float *itolerance; la_i_vc_create_t *lhs_eigenvalues; la_i_mc_create_t *rhs_A; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_eigenvalues_, lhs_eigenvalues); toa(rhs_A_, rhs_A); gmm::implicit_qr_algorithm(rhs_A->mc, lhs_eigenvalues->vc, @@ -3822,17 +3825,17 @@ class la_i_qr_eigen_mc_t : public OpcodeBase { class la_k_qr_eigen_mc_t : public OpcodeBase { public: - MYFLT *lhs_eigenvalues_; - MYFLT *rhs_A_; - MYFLT *ktolerance; + cs_float *lhs_eigenvalues_; + cs_float *rhs_A_; + cs_float *ktolerance; la_i_vc_create_t *lhs_eigenvalues; la_i_mc_create_t *rhs_A; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_eigenvalues_, lhs_eigenvalues); toa(rhs_A_, rhs_A); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::implicit_qr_algorithm(rhs_A->mc, lhs_eigenvalues->vc, double(*ktolerance)); return OK; @@ -3841,14 +3844,14 @@ class la_k_qr_eigen_mc_t : public OpcodeBase { class la_i_qr_sym_eigen_mr_t : public OpcodeBase { public: - MYFLT *lhs_eigenvalues_; - MYFLT *lhs_eigenvectors_; - MYFLT *rhs_A_; - MYFLT *itolerance; + cs_float *lhs_eigenvalues_; + cs_float *lhs_eigenvectors_; + cs_float *rhs_A_; + cs_float *itolerance; la_i_vr_create_t *lhs_eigenvalues; la_i_mr_create_t *lhs_eigenvectors; la_i_mr_create_t *rhs_A; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_eigenvalues_, lhs_eigenvalues); toa(lhs_eigenvectors_, lhs_eigenvectors); toa(rhs_A_, rhs_A); @@ -3860,20 +3863,20 @@ class la_i_qr_sym_eigen_mr_t : public OpcodeBase { class la_k_qr_sym_eigen_mr_t : public OpcodeBase { public: - MYFLT *lhs_eigenvalues_; - MYFLT *lhs_eigenvectors_; - MYFLT *rhs_A_; - MYFLT *ktolerance; + cs_float *lhs_eigenvalues_; + cs_float *lhs_eigenvectors_; + cs_float *rhs_A_; + cs_float *ktolerance; la_i_vr_create_t *lhs_eigenvalues; la_i_mr_create_t *lhs_eigenvectors; la_i_mr_create_t *rhs_A; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_eigenvalues_, lhs_eigenvalues); toa(lhs_eigenvectors_, lhs_eigenvectors); toa(rhs_A_, rhs_A); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::implicit_qr_algorithm(rhs_A->mr, lhs_eigenvalues->vr, lhs_eigenvectors->mr, double(*ktolerance)); return OK; @@ -3882,14 +3885,14 @@ class la_k_qr_sym_eigen_mr_t : public OpcodeBase { class la_i_qr_sym_eigen_mc_t : public OpcodeBase { public: - MYFLT *lhs_eigenvalues_; - MYFLT *lhs_eigenvectors_; - MYFLT *rhs_A_; - MYFLT *itolerance; + cs_float *lhs_eigenvalues_; + cs_float *lhs_eigenvectors_; + cs_float *rhs_A_; + cs_float *itolerance; la_i_vc_create_t *lhs_eigenvalues; la_i_mc_create_t *lhs_eigenvectors; la_i_mc_create_t *rhs_A; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_eigenvalues_, lhs_eigenvalues); toa(lhs_eigenvectors_, lhs_eigenvectors); toa(rhs_A_, rhs_A); @@ -3901,20 +3904,20 @@ class la_i_qr_sym_eigen_mc_t : public OpcodeBase { class la_k_qr_sym_eigen_mc_t : public OpcodeBase { public: - MYFLT *lhs_eigenvalues_; - MYFLT *lhs_eigenvectors_; - MYFLT *rhs_A_; - MYFLT *ktolerance; + cs_float *lhs_eigenvalues_; + cs_float *lhs_eigenvectors_; + cs_float *rhs_A_; + cs_float *ktolerance; la_i_vc_create_t *lhs_eigenvalues; la_i_mc_create_t *lhs_eigenvectors; la_i_mc_create_t *rhs_A; - int init(CSOUND *) { + int32_t init(CSOUND *) { toa(lhs_eigenvalues_, lhs_eigenvalues); toa(lhs_eigenvectors_, lhs_eigenvectors); toa(rhs_A_, rhs_A); return OK; } - int kontrol(CSOUND *) { + int32_t kontrol(CSOUND *) { gmm::implicit_qr_algorithm(rhs_A->mc, lhs_eigenvalues->vc, lhs_eigenvectors->mc, double(*ktolerance)); return OK; @@ -3923,831 +3926,837 @@ class la_k_qr_sym_eigen_mc_t : public OpcodeBase { extern "C" { -PUBLIC int csoundModuleCreate(CSOUND *csound) { return 0; } +PUBLIC int32_t csoundModuleInfo(void) { return CSOUND_MODULE_INFO; } -PUBLIC int csoundModuleInit(CSOUND *csound) { - int status = 0; - status |= csound->AppendOpcode( - csound, "la_i_vr_create", sizeof(la_i_vr_create_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_vr_create_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_vc_create", sizeof(la_i_vr_create_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_vc_create_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_mr_create", sizeof(la_i_mr_create_t), 0, 1, "i", "iio", - (int (*)(CSOUND *, void *)) & la_i_mr_create_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_mc_create", sizeof(la_i_mc_create_t), 0, 1, "i", "iioo", - (int (*)(CSOUND *, void *)) & la_i_mc_create_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_size_vr", sizeof(la_i_size_vr_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_size_vr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_size_vc", sizeof(la_i_size_vc_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_size_vc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_size_mr", sizeof(la_i_size_mr_t), 0, 1, "ii", "i", - (int (*)(CSOUND *, void *)) & la_i_size_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_size_mc", sizeof(la_i_size_mc_t), 0, 1, "ii", "i", - (int (*)(CSOUND *, void *)) & la_i_size_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_current_f", sizeof(la_k_current_f_t), 0, 3, "k", "f", - (int (*)(CSOUND *, void *)) & la_k_current_f_t::init_, - (int (*)(CSOUND *, void *)) & la_k_current_f_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_current_vr", sizeof(la_k_current_vr_t), 0, 3, "k", "i", - (int (*)(CSOUND *, void *)) & la_k_current_vr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_current_vr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_print_vr", sizeof(la_i_print_vr_t), 0, 1, "", "i", - (int (*)(CSOUND *, void *)) & la_i_print_vr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_print_vc", sizeof(la_i_print_vc_t), 0, 1, "", "i", - (int (*)(CSOUND *, void *)) & la_i_print_vc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_print_mr", sizeof(la_i_print_mr_t), 0, 1, "", "i", - (int (*)(CSOUND *, void *)) & la_i_print_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_print_mc", sizeof(la_i_print_mc_t), 0, 1, "", "i", - (int (*)(CSOUND *, void *)) & la_i_print_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_assign_vr", sizeof(la_i_assign_vr_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_assign_vr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_assign_vr", sizeof(la_k_assign_vr_t), 0, 3, "i", "k", - (int (*)(CSOUND *, void *)) & la_k_assign_vr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_assign_vr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_assign_vc", sizeof(la_i_assign_vc_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_assign_vc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_assign_vc", sizeof(la_k_assign_vc_t), 0, 3, "i", "k", - (int (*)(CSOUND *, void *)) & la_k_assign_vc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_assign_vc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_assign_mr", sizeof(la_i_assign_mr_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_assign_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_assign_mr", sizeof(la_k_assign_mr_t), 0, 3, "i", "k", - (int (*)(CSOUND *, void *)) & la_k_assign_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_assign_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_assign_mc", sizeof(la_i_assign_mc_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_assign_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_assign_mc", sizeof(la_k_assign_mc_t), 0, 3, "i", "k", - (int (*)(CSOUND *, void *)) & la_k_assign_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_assign_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_assign_a", sizeof(la_k_assign_a_t), 0, 3, "i", "a", - (int (*)(CSOUND *, void *)) & la_k_assign_a_t::init_, - (int (*)(CSOUND *, void *)) & la_k_assign_a_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_assign_t", sizeof(la_i_assign_t_t), TR, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_assign_t_t::init_, - (int (*)(CSOUND *, void *)) & la_i_assign_t_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_assign_t", sizeof(la_k_assign_t_t), TR, 3, "i", "k", - (int (*)(CSOUND *, void *)) & la_k_assign_t_t::init_, - (int (*)(CSOUND *, void *)) & la_k_assign_t_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_assign_f", sizeof(la_k_assign_f_t), 0, 3, "i", "f", - (int (*)(CSOUND *, void *)) & la_k_assign_f_t::init_, - (int (*)(CSOUND *, void *)) & la_k_assign_f_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_a_assign", sizeof(la_k_a_assign_t), 0, 3, "a", "k", - (int (*)(CSOUND *, void *)) & la_k_a_assign_t::init_, - (int (*)(CSOUND *, void *)) & la_k_a_assign_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_t_assign", sizeof(la_i_t_assign_t), TW, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_t_assign_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_t_assign", sizeof(la_k_t_assign_t), TW, 3, "i", "k", - (int (*)(CSOUND *, void *)) & la_k_t_assign_t::init_, - (int (*)(CSOUND *, void *)) & la_k_t_assign_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_f_assign", sizeof(la_k_f_assign_t), 0, 3, "f", "p", - (int (*)(CSOUND *, void *)) & la_k_f_assign_t::init_, - (int (*)(CSOUND *, void *)) & la_k_f_assign_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_random_vr", sizeof(la_i_random_vr_t), 0, 1, "i", "p", - (int (*)(CSOUND *, void *)) & la_i_random_vr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_random_vr", sizeof(la_k_random_vr_t), 0, 3, "i", "p", - (int (*)(CSOUND *, void *)) & la_k_random_vr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_random_vr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_random_vc", sizeof(la_i_random_vc_t), 0, 1, "i", "p", - (int (*)(CSOUND *, void *)) & la_i_random_vc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_random_vc", sizeof(la_k_random_vc_t), 0, 3, "i", "p", - (int (*)(CSOUND *, void *)) & la_k_random_vc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_random_vc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_random_mr", sizeof(la_i_random_mr_t), 0, 1, "i", "p", - (int (*)(CSOUND *, void *)) & la_i_random_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_random_mr", sizeof(la_k_random_mr_t), 0, 3, "i", "p", - (int (*)(CSOUND *, void *)) & la_k_random_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_random_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_random_mc", sizeof(la_i_random_mc_t), 0, 1, "i", "p", - (int (*)(CSOUND *, void *)) & la_i_random_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_random_mc", sizeof(la_k_random_mc_t), 0, 3, "i", "p", - (int (*)(CSOUND *, void *)) & la_k_random_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_random_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_vr_set", sizeof(la_i_vr_set_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_vr_set_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_vr_set", sizeof(la_k_vr_set_t), 0, 3, "i", "kk", - (int (*)(CSOUND *, void *)) & la_k_vr_set_t::init_, - (int (*)(CSOUND *, void *)) & la_k_vr_set_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_vc_set", sizeof(la_i_vc_set_t), 0, 1, "i", "iii", - (int (*)(CSOUND *, void *)) & la_i_vc_set_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_vc_set", sizeof(la_k_vc_set_t), 0, 3, "i", "kkk", - (int (*)(CSOUND *, void *)) & la_k_vc_set_t::init_, - (int (*)(CSOUND *, void *)) & la_k_vc_set_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_mr_set", sizeof(la_i_mr_set_t), 0, 1, "i", "iii", - (int (*)(CSOUND *, void *)) & la_i_mr_set_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_mr_set", sizeof(la_k_mr_set_t), 0, 3, "i", "kkk", - (int (*)(CSOUND *, void *)) & la_k_mr_set_t::init_, - (int (*)(CSOUND *, void *)) & la_k_mr_set_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_mc_set", sizeof(la_i_mc_set_t), 0, 1, "i", "iiii", - (int (*)(CSOUND *, void *)) & la_i_mc_set_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_mc_set", sizeof(la_k_mc_set_t), 0, 3, "i", "kkkk", - (int (*)(CSOUND *, void *)) & la_k_mc_set_t::init_, - (int (*)(CSOUND *, void *)) & la_k_mc_set_t::kontrol_, - (int (*)(CSOUND *, void *))0); - - status |= csound->AppendOpcode( - csound, "la_i_get_vr", sizeof(la_i_get_vr_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_get_vr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_get_vr", sizeof(la_k_get_vr_t), 0, 3, "k", "ik", - (int (*)(CSOUND *, void *)) & la_k_get_vr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_get_vr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_get_vc", sizeof(la_i_get_vc_t), 0, 1, "ii", "ii", - (int (*)(CSOUND *, void *)) & la_i_get_vc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_get_vc", sizeof(la_k_get_vc_t), 0, 3, "kk", "ik", - (int (*)(CSOUND *, void *)) & la_k_get_vc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_get_vc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_get_mr", sizeof(la_i_get_mr_t), 0, 1, "i", "iii", - (int (*)(CSOUND *, void *)) & la_i_get_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_get_mr", sizeof(la_k_get_mr_t), 0, 3, "k", "ikk", - (int (*)(CSOUND *, void *)) & la_k_get_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_get_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_get_mc", sizeof(la_i_get_mc_t), 0, 1, "ii", "iii", - (int (*)(CSOUND *, void *)) & la_i_get_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_get_mc", sizeof(la_k_get_mc_t), 0, 3, "kk", "ikk", - (int (*)(CSOUND *, void *)) & la_k_get_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_get_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_transpose_mr", sizeof(la_i_transpose_mr_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_transpose_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_transpose_mr", sizeof(la_k_transpose_mr_t), 0, 3, "i", "k", - (int (*)(CSOUND *, void *)) & la_k_transpose_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_transpose_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_transpose_mc", sizeof(la_i_transpose_mc_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_transpose_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_transpose_mc", sizeof(la_k_transpose_mc_t), 0, 2, "i", "i", - (int (*)(CSOUND *, void *)) & la_k_transpose_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_transpose_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_conjugate_vr", sizeof(la_i_conjugate_vr_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_conjugate_vr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_conjugate_vr", sizeof(la_k_conjugate_vr_t), 0, 3, "i", "i", - (int (*)(CSOUND *, void *)) & la_k_conjugate_vr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_conjugate_vr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_conjugate_vc", sizeof(la_i_conjugate_vc_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_conjugate_vc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_conjugate_vc", sizeof(la_k_conjugate_vc_t), 0, 3, "i", "i", - (int (*)(CSOUND *, void *)) & la_k_conjugate_vc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_conjugate_vc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_conjugate_mr", sizeof(la_i_conjugate_mr_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_conjugate_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_conjugate_mr", sizeof(la_k_conjugate_mr_t), 0, 3, "i", "i", - (int (*)(CSOUND *, void *)) & la_k_conjugate_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_conjugate_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_conjugate_mc", sizeof(la_i_conjugate_mc_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_conjugate_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_conjugate_mc", sizeof(la_k_conjugate_mc_t), 0, 3, "i", "i", - (int (*)(CSOUND *, void *)) & la_k_conjugate_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_conjugate_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_norm1_vr", sizeof(la_i_norm1_vr_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_norm1_vr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_norm1_vr", sizeof(la_k_norm1_vr_t), 0, 3, "k", "i", - (int (*)(CSOUND *, void *)) & la_k_norm1_vr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_norm1_vr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_norm1_vc", sizeof(la_i_norm1_vc_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_norm1_vc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_norm1_vc", sizeof(la_k_norm1_vc_t), 0, 3, "k", "i", - (int (*)(CSOUND *, void *)) & la_k_norm1_vc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_norm1_vc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_norm1_mr", sizeof(la_i_norm1_mr_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_norm1_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_norm1_mr", sizeof(la_k_norm1_mr_t), 0, 3, "k", "i", - (int (*)(CSOUND *, void *)) & la_k_norm1_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_norm1_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_norm1_mc", sizeof(la_i_norm1_mc_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_norm1_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_norm1_mc", sizeof(la_k_norm1_mc_t), 0, 3, "k", "i", - (int (*)(CSOUND *, void *)) & la_k_norm1_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_norm1_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_norm_euclid_vr", sizeof(la_i_norm_euclid_vr_t), 0, 1, "i", - "i", (int (*)(CSOUND *, void *)) & la_i_norm_euclid_vr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_norm_euclid_vr", sizeof(la_k_norm_euclid_vr_t), 0, 3, "k", - "i", (int (*)(CSOUND *, void *)) & la_k_norm_euclid_vr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_norm_euclid_vr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_norm_euclid_vc", sizeof(la_i_norm_euclid_vc_t), 0, 1, "i", - "i", (int (*)(CSOUND *, void *)) & la_i_norm_euclid_vc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_norm_euclid_vc", sizeof(la_k_norm_euclid_vc_t), 0, 3, "k", - "i", (int (*)(CSOUND *, void *)) & la_k_norm_euclid_vc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_norm_euclid_vc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_norm_euclid_mr", sizeof(la_i_norm_euclid_mr_t), 0, 1, "i", - "i", (int (*)(CSOUND *, void *)) & la_i_norm_euclid_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_norm_euclid_mr", sizeof(la_k_norm_euclid_mr_t), 0, 3, "k", - "i", (int (*)(CSOUND *, void *)) & la_k_norm_euclid_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_norm_euclid_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_norm_euclid_mc", sizeof(la_i_norm_euclid_mc_t), 0, 1, "i", - "i", (int (*)(CSOUND *, void *)) & la_i_norm_euclid_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_norm_euclid_mc", sizeof(la_k_norm_euclid_mc_t), 0, 3, "k", - "i", (int (*)(CSOUND *, void *)) & la_k_norm_euclid_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_norm_euclid_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_distance_vr", sizeof(la_i_distance_vr_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_distance_vr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_distance_vr", sizeof(la_k_distance_vr_t), 0, 3, "k", "ii", - (int (*)(CSOUND *, void *)) & la_k_distance_vr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_distance_vr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_distance_vc", sizeof(la_i_distance_vc_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_distance_vc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_distance_vc", sizeof(la_k_distance_vc_t), 0, 3, "k", "ii", - (int (*)(CSOUND *, void *)) & la_k_distance_vc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_distance_vc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_norm_max_mr", sizeof(la_i_norm_max_mr_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_norm_max_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_norm_max_mr", sizeof(la_k_norm_max_mr_t), 0, 3, "k", "i", - (int (*)(CSOUND *, void *)) & la_k_norm_max_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_norm_max_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_norm_max_mc", sizeof(la_i_norm_max_mc_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_norm_max_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_norm_max_mc", sizeof(la_k_norm_max_mc_t), 0, 3, "k", "i", - (int (*)(CSOUND *, void *)) & la_k_norm_max_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_norm_max_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_norm_inf_vr", sizeof(la_i_norm_inf_vr_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_norm_inf_vr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_norm_inf_vr", sizeof(la_k_norm_inf_vr_t), 0, 3, "k", "i", - (int (*)(CSOUND *, void *)) & la_k_norm_inf_vr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_norm_inf_vr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_norm_inf_vc", sizeof(la_i_norm_inf_vc_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_norm_inf_vc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_norm_inf_vc", sizeof(la_k_norm_inf_vc_t), 0, 3, "k", "i", - (int (*)(CSOUND *, void *)) & la_k_norm_inf_vc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_norm_inf_vc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_norm_inf_mr", sizeof(la_i_norm_inf_mr_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_norm_inf_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_norm_inf_mr", sizeof(la_k_norm_inf_mr_t), 0, 3, "k", "i", - (int (*)(CSOUND *, void *)) & la_k_norm_inf_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_norm_inf_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_norm_inf_mc", sizeof(la_i_norm_inf_mc_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_norm_inf_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_norm_inf_mc", sizeof(la_k_norm_inf_mc_t), 0, 3, "k", "i", - (int (*)(CSOUND *, void *)) & la_k_norm_inf_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_norm_inf_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_trace_mr", sizeof(la_i_trace_mr_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_trace_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_trace_mr", sizeof(la_k_trace_mr_t), 0, 3, "k", "i", - (int (*)(CSOUND *, void *)) & la_k_trace_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_trace_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_trace_mc", sizeof(la_i_trace_mc_t), 0, 1, "ii", "i", - (int (*)(CSOUND *, void *)) & la_i_trace_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_trace_mc", sizeof(la_k_trace_mc_t), 0, 3, "kk", "i", - (int (*)(CSOUND *, void *)) & la_k_trace_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_trace_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_lu_det_mr", sizeof(la_i_lu_det_mr_t), 0, 1, "i", "i", - (int (*)(CSOUND *, void *)) & la_i_lu_det_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_lu_det_mr", sizeof(la_k_lu_det_mr_t), 0, 3, "k", "i", - (int (*)(CSOUND *, void *)) & la_k_lu_det_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_lu_det_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_lu_det_mc", sizeof(la_i_lu_det_mc_t), 0, 1, "ii", "i", - (int (*)(CSOUND *, void *)) & la_i_lu_det_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_lu_det_mc", sizeof(la_k_lu_det_mc_t), 0, 3, "kk", "i", - (int (*)(CSOUND *, void *)) & la_k_lu_det_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_lu_det_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_add_vr", sizeof(la_i_add_vr_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_add_vr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_add_vr", sizeof(la_k_add_vr_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_add_vr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_add_vr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_add_vc", sizeof(la_i_add_vc_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_add_vc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_add_vc", sizeof(la_k_add_vc_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_add_vc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_add_vc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_add_mr", sizeof(la_i_add_mr_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_add_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_add_mr", sizeof(la_k_add_mr_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_add_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_add_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_add_mc", sizeof(la_i_add_mc_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_add_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_add_mc", sizeof(la_k_add_mc_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_add_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_add_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_subtract_vr", sizeof(la_i_subtract_vr_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_subtract_vr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_subtract_vr", sizeof(la_k_subtract_vr_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_subtract_vr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_subtract_vr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_subtract_vc", sizeof(la_i_subtract_vc_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_subtract_vc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_subtract_vc", sizeof(la_k_subtract_vc_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_subtract_vc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_subtract_vc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_subtract_mr", sizeof(la_i_subtract_mr_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_subtract_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_subtract_mr", sizeof(la_k_subtract_mr_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_subtract_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_subtract_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_subtract_mc", sizeof(la_i_subtract_mc_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_subtract_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_subtract_mc", sizeof(la_k_subtract_mc_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_subtract_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_subtract_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_multiply_vr", sizeof(la_i_multiply_vr_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_multiply_vr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_multiply_vr", sizeof(la_k_multiply_vr_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_multiply_vr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_multiply_vr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_multiply_vc", sizeof(la_i_multiply_vc_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_multiply_vc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_multiply_vc", sizeof(la_k_multiply_vc_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_multiply_vc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_multiply_vc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_multiply_mr", sizeof(la_i_multiply_mr_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_multiply_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_multiply_mr", sizeof(la_k_multiply_mr_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_multiply_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_multiply_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_multiply_mc", sizeof(la_i_multiply_mc_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_multiply_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_multiply_mc", sizeof(la_k_multiply_mc_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_multiply_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_multiply_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - - status |= csound->AppendOpcode( - csound, "la_i_divide_vr", sizeof(la_i_divide_vr_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_divide_vr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_divide_vr", sizeof(la_k_divide_vr_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_divide_vr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_divide_vr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_divide_vc", sizeof(la_i_divide_vc_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_divide_vc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_divide_vc", sizeof(la_k_divide_vc_t), 0, 3, "i", "kk", - (int (*)(CSOUND *, void *)) & la_k_divide_vc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_divide_vc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_divide_mr", sizeof(la_i_divide_mr_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_divide_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_divide_mr", sizeof(la_k_divide_mr_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_divide_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_divide_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_divide_mc", sizeof(la_i_divide_mc_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_divide_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_divide_mc", sizeof(la_k_divide_mc_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_divide_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_divide_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_dot_vr", sizeof(la_i_dot_vr_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_dot_vr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_dot_vr", sizeof(la_k_dot_vr_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_dot_vr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_dot_vr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_dot_vc", sizeof(la_i_dot_vc_t), 0, 1, "ii", "ii", - (int (*)(CSOUND *, void *)) & la_i_dot_vc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_dot_vc", sizeof(la_k_dot_vc_t), 0, 3, "ii", "ii", - (int (*)(CSOUND *, void *)) & la_k_dot_vc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_dot_vc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_dot_mr", sizeof(la_i_dot_mr_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_dot_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_dot_mr", sizeof(la_k_dot_mr_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_dot_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_dot_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_dot_mc", sizeof(la_i_dot_mc_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_dot_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_dot_mc", sizeof(la_k_dot_mc_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_dot_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_dot_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_dot_mr_vr", sizeof(la_i_dot_mr_vr_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_dot_mr_vr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_dot_mr_vr", sizeof(la_k_dot_mr_vr_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_dot_mr_vr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_dot_mr_vr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_dot_mc_vc", sizeof(la_i_dot_mc_vc_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_dot_mc_vc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_dot_mc_vc", sizeof(la_k_dot_mc_vc_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_dot_mc_vc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_dot_mc_vc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_invert_mr", sizeof(la_i_invert_mr_t), 0, 1, "ii", "i", - (int (*)(CSOUND *, void *)) & la_i_invert_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_invert_mr", sizeof(la_k_invert_mr_t), 0, 3, "ik", "i", - (int (*)(CSOUND *, void *)) & la_k_invert_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_invert_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_invert_mc", sizeof(la_i_invert_mc_t), 0, 1, "iii", "i", - (int (*)(CSOUND *, void *)) & la_i_invert_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_invert_mc", sizeof(la_k_invert_mc_t), 0, 3, "ikk", "i", - (int (*)(CSOUND *, void *)) & la_k_invert_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_invert_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_upper_solve_mr", sizeof(la_i_upper_solve_mr_t), 0, 1, "i", - "io", (int (*)(CSOUND *, void *)) & la_i_upper_solve_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_upper_solve_mr", sizeof(la_k_upper_solve_mr_t), 0, 3, "i", - "iO", (int (*)(CSOUND *, void *)) & la_k_upper_solve_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_upper_solve_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_upper_solve_mc", sizeof(la_i_upper_solve_mc_t), 0, 1, "i", - "io", (int (*)(CSOUND *, void *)) & la_i_upper_solve_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_upper_solve_mc", sizeof(la_k_upper_solve_mc_t), 0, 3, "i", - "iO", (int (*)(CSOUND *, void *)) & la_k_upper_solve_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_upper_solve_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_lower_solve_mr", sizeof(la_i_lower_solve_mr_t), 0, 1, "i", - "io", (int (*)(CSOUND *, void *)) & la_i_lower_solve_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_lower_solve_mr", sizeof(la_k_lower_solve_mr_t), 0, 3, "i", - "iO", (int (*)(CSOUND *, void *)) & la_k_lower_solve_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_lower_solve_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_lower_solve_mc", sizeof(la_i_lower_solve_mc_t), 0, 1, "i", - "io", (int (*)(CSOUND *, void *)) & la_i_lower_solve_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_lower_solve_mc", sizeof(la_k_lower_solve_mc_t), 0, 3, "i", - "iO", (int (*)(CSOUND *, void *)) & la_k_lower_solve_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_lower_solve_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_lu_factor_mr", sizeof(la_i_lu_factor_mr_t), 0, 1, "iii", - "i", (int (*)(CSOUND *, void *)) & la_i_lu_factor_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_lu_factor_mr", sizeof(la_k_lu_factor_mr_t), 0, 3, "iik", - "i", (int (*)(CSOUND *, void *)) & la_k_lu_factor_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_lu_factor_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_lu_factor_mc", sizeof(la_i_lu_factor_mc_t), 0, 1, "iii", - "i", (int (*)(CSOUND *, void *)) & la_i_lu_factor_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_lu_factor_mc", sizeof(la_k_lu_factor_mc_t), 0, 3, "i", "i", - (int (*)(CSOUND *, void *)) & la_k_lu_factor_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_lu_factor_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_lu_solve_mr", sizeof(la_i_lu_solve_mr_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_lu_solve_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_lu_solve_mr", sizeof(la_k_lu_solve_mr_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_lu_solve_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_lu_solve_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_lu_solve_mc", sizeof(la_i_lu_solve_mc_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_lu_solve_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_lu_solve_mc", sizeof(la_k_lu_solve_mc_t), 0, 3, "i", "ii", - (int (*)(CSOUND *, void *)) & la_k_lu_solve_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_lu_solve_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_qr_factor_mr", sizeof(la_i_qr_factor_mr_t), 0, 1, "ii", "i", - (int (*)(CSOUND *, void *)) & la_i_qr_factor_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_qr_factor_mr", sizeof(la_k_qr_factor_mr_t), 0, 3, "ii", "i", - (int (*)(CSOUND *, void *)) & la_k_qr_factor_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_qr_factor_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_qr_factor_mc", sizeof(la_i_qr_factor_mc_t), 0, 1, "ii", "i", - (int (*)(CSOUND *, void *)) & la_i_qr_factor_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_qr_factor_mc", sizeof(la_k_qr_factor_mc_t), 0, 3, "ii", "i", - (int (*)(CSOUND *, void *)) & la_k_qr_factor_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_qr_factor_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_qr_eigen_mr", sizeof(la_i_qr_eigen_mr_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_qr_eigen_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_qr_eigen_mr", sizeof(la_k_qr_eigen_mr_t), 0, 3, "i", "ik", - (int (*)(CSOUND *, void *)) & la_k_qr_eigen_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_qr_eigen_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_qr_eigen_mc", sizeof(la_i_qr_eigen_mc_t), 0, 1, "i", "ii", - (int (*)(CSOUND *, void *)) & la_i_qr_eigen_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_qr_eigen_mc", sizeof(la_k_qr_eigen_mc_t), 0, 3, "i", "ik", - (int (*)(CSOUND *, void *)) & la_k_qr_eigen_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_qr_eigen_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_qr_sym_eigen_mr", sizeof(la_i_qr_sym_eigen_mr_t), 0, 1, - "ii", "ii", (int (*)(CSOUND *, void *)) & la_i_qr_sym_eigen_mr_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_qr_sym_eigen_mr", sizeof(la_k_qr_sym_eigen_mr_t), 0, 3, - "ii", "ik", (int (*)(CSOUND *, void *)) & la_k_qr_sym_eigen_mr_t::init_, - (int (*)(CSOUND *, void *)) & la_k_qr_sym_eigen_mr_t::kontrol_, - (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_i_qr_sym_eigen_mc", sizeof(la_i_qr_sym_eigen_mc_t), 0, 1, - "ii", "ii", (int (*)(CSOUND *, void *)) & la_i_qr_sym_eigen_mc_t::init_, - (int (*)(CSOUND *, void *))0, (int (*)(CSOUND *, void *))0); - status |= csound->AppendOpcode( - csound, "la_k_qr_sym_eigen_mc", sizeof(la_k_qr_sym_eigen_mc_t), 0, 3, - "ii", "ik", (int (*)(CSOUND *, void *)) & la_k_qr_sym_eigen_mc_t::init_, - (int (*)(CSOUND *, void *)) & la_k_qr_sym_eigen_mc_t::kontrol_, - (int (*)(CSOUND *, void *))0); +PUBLIC int32_t csoundModuleCreate(CSOUND *csound) { return 0; } + +PUBLIC int32_t csoundModuleInit(CSOUND *csound) { + int32_t status = 0; + status |= csound->AppendOpcode( + csound, "la_i_vr_create", sizeof(la_i_vr_create_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_vr_create_t::init_, + (int32_t (*)(CSOUND *, void *))0, + (int32_t (*)(CSOUND *, void *)) & la_i_vr_create_t::deinit_); + status |= csound->AppendOpcode( + csound, "la_i_vc_create", sizeof(la_i_vr_create_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_vc_create_t::init_, + (int32_t (*)(CSOUND *, void *))0, + (int32_t (*)(CSOUND *, void *)) & la_i_vc_create_t::deinit_); + status |= csound->AppendOpcode( + csound, "la_i_mr_create", sizeof(la_i_mr_create_t), 0, "i", "iio", + (int32_t (*)(CSOUND *, void *)) & la_i_mr_create_t::init_, + (int32_t (*)(CSOUND *, void *))0, + (int32_t (*)(CSOUND *, void *)) & la_i_mr_create_t::deinit_); + status |= csound->AppendOpcode( + csound, "la_i_mc_create", sizeof(la_i_mc_create_t), 0, "i", "iioo", + (int32_t (*)(CSOUND *, void *)) & la_i_mc_create_t::init_, + (int32_t (*)(CSOUND *, void *))0, + (int32_t (*)(CSOUND *, void *)) & la_i_mc_create_t::deinit_); + status |= csound->AppendOpcode( + csound, "la_i_size_vr", sizeof(la_i_size_vr_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_size_vr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_size_vc", sizeof(la_i_size_vc_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_size_vc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_size_mr", sizeof(la_i_size_mr_t), 0, "ii", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_size_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_size_mc", sizeof(la_i_size_mc_t), 0, "ii", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_size_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_current_f", sizeof(la_k_current_f_t), 0, "k", "f", + (int32_t (*)(CSOUND *, void *)) & la_k_current_f_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_current_f_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_current_vr", sizeof(la_k_current_vr_t), 0, "k", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_current_vr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_current_vr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_print_vr", sizeof(la_i_print_vr_t), 0, "", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_print_vr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_print_vc", sizeof(la_i_print_vc_t), 0, "", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_print_vc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_print_mr", sizeof(la_i_print_mr_t), 0, "", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_print_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_print_mc", sizeof(la_i_print_mc_t), 0, "", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_print_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_assign_vr", sizeof(la_i_assign_vr_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_assign_vr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_assign_vr", sizeof(la_k_assign_vr_t), 0, "i", "k", + (int32_t (*)(CSOUND *, void *)) & la_k_assign_vr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_assign_vr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_assign_vc", sizeof(la_i_assign_vc_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_assign_vc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_assign_vc", sizeof(la_k_assign_vc_t), 0, "i", "k", + (int32_t (*)(CSOUND *, void *)) & la_k_assign_vc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_assign_vc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_assign_mr", sizeof(la_i_assign_mr_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_assign_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_assign_mr", sizeof(la_k_assign_mr_t), 0, "i", "k", + (int32_t (*)(CSOUND *, void *)) & la_k_assign_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_assign_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_assign_mc", sizeof(la_i_assign_mc_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_assign_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_assign_mc", sizeof(la_k_assign_mc_t), 0, "i", "k", + (int32_t (*)(CSOUND *, void *)) & la_k_assign_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_assign_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_assign_a", sizeof(la_k_assign_a_t), 0, "i", "a", + (int32_t (*)(CSOUND *, void *)) & la_k_assign_a_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_assign_a_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_assign_t", sizeof(la_i_assign_t_t), TR, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_assign_t_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_i_assign_t_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_assign_t", sizeof(la_k_assign_t_t), TR, "i", "k", + (int32_t (*)(CSOUND *, void *)) & la_k_assign_t_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_assign_t_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_assign_f", sizeof(la_k_assign_f_t), 0, "i", "f", + (int32_t (*)(CSOUND *, void *)) & la_k_assign_f_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_assign_f_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_a_assign", sizeof(la_k_a_assign_t), 0, "a", "k", + (int32_t (*)(CSOUND *, void *)) & la_k_a_assign_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_a_assign_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_t_assign", sizeof(la_i_t_assign_t), TW, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_t_assign_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_t_assign", sizeof(la_k_t_assign_t), TW, "i", "k", + (int32_t (*)(CSOUND *, void *)) & la_k_t_assign_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_t_assign_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_f_assign", sizeof(la_k_f_assign_t), 0, "f", "p", + (int32_t (*)(CSOUND *, void *)) & la_k_f_assign_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_f_assign_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_random_vr", sizeof(la_i_random_vr_t), 0, "i", "p", + (int32_t (*)(CSOUND *, void *)) & la_i_random_vr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_random_vr", sizeof(la_k_random_vr_t), 0, "i", "p", + (int32_t (*)(CSOUND *, void *)) & la_k_random_vr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_random_vr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_random_vc", sizeof(la_i_random_vc_t), 0, "i", "p", + (int32_t (*)(CSOUND *, void *)) & la_i_random_vc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_random_vc", sizeof(la_k_random_vc_t), 0, "i", "p", + (int32_t (*)(CSOUND *, void *)) & la_k_random_vc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_random_vc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_random_mr", sizeof(la_i_random_mr_t), 0, "i", "p", + (int32_t (*)(CSOUND *, void *)) & la_i_random_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_random_mr", sizeof(la_k_random_mr_t), 0, "i", "p", + (int32_t (*)(CSOUND *, void *)) & la_k_random_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_random_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_random_mc", sizeof(la_i_random_mc_t), 0, "i", "p", + (int32_t (*)(CSOUND *, void *)) & la_i_random_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_random_mc", sizeof(la_k_random_mc_t), 0, "i", "p", + (int32_t (*)(CSOUND *, void *)) & la_k_random_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_random_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_vr_set", sizeof(la_i_vr_set_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_vr_set_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_vr_set", sizeof(la_k_vr_set_t), 0, "i", "kk", + (int32_t (*)(CSOUND *, void *)) & la_k_vr_set_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_vr_set_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_vc_set", sizeof(la_i_vc_set_t), 0, "i", "iii", + (int32_t (*)(CSOUND *, void *)) & la_i_vc_set_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_vc_set", sizeof(la_k_vc_set_t), 0, "i", "kkk", + (int32_t (*)(CSOUND *, void *)) & la_k_vc_set_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_vc_set_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_mr_set", sizeof(la_i_mr_set_t), 0, "i", "iii", + (int32_t (*)(CSOUND *, void *)) & la_i_mr_set_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_mr_set", sizeof(la_k_mr_set_t), 0, "i", "kkk", + (int32_t (*)(CSOUND *, void *)) & la_k_mr_set_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_mr_set_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_mc_set", sizeof(la_i_mc_set_t), 0, "i", "iiii", + (int32_t (*)(CSOUND *, void *)) & la_i_mc_set_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_mc_set", sizeof(la_k_mc_set_t), 0, "i", "kkkk", + (int32_t (*)(CSOUND *, void *)) & la_k_mc_set_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_mc_set_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + + status |= csound->AppendOpcode( + csound, "la_i_get_vr", sizeof(la_i_get_vr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_get_vr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_get_vr", sizeof(la_k_get_vr_t), 0, "k", "ik", + (int32_t (*)(CSOUND *, void *)) & la_k_get_vr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_get_vr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_get_vc", sizeof(la_i_get_vc_t), 0, "ii", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_get_vc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_get_vc", sizeof(la_k_get_vc_t), 0, "kk", "ik", + (int32_t (*)(CSOUND *, void *)) & la_k_get_vc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_get_vc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_get_mr", sizeof(la_i_get_mr_t), 0, "i", "iii", + (int32_t (*)(CSOUND *, void *)) & la_i_get_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_get_mr", sizeof(la_k_get_mr_t), 0, "k", "ikk", + (int32_t (*)(CSOUND *, void *)) & la_k_get_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_get_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_get_mc", sizeof(la_i_get_mc_t), 0, "ii", "iii", + (int32_t (*)(CSOUND *, void *)) & la_i_get_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_get_mc", sizeof(la_k_get_mc_t), 0, "kk", "ikk", + (int32_t (*)(CSOUND *, void *)) & la_k_get_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_get_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_transpose_mr", sizeof(la_i_transpose_mr_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_transpose_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_transpose_mr", sizeof(la_k_transpose_mr_t), 0, "i", "k", + (int32_t (*)(CSOUND *, void *)) & la_k_transpose_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_transpose_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_transpose_mc", sizeof(la_i_transpose_mc_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_transpose_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_transpose_mc", sizeof(la_k_transpose_mc_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_transpose_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_transpose_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_conjugate_vr", sizeof(la_i_conjugate_vr_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_conjugate_vr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_conjugate_vr", sizeof(la_k_conjugate_vr_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_conjugate_vr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_conjugate_vr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_conjugate_vc", sizeof(la_i_conjugate_vc_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_conjugate_vc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_conjugate_vc", sizeof(la_k_conjugate_vc_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_conjugate_vc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_conjugate_vc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_conjugate_mr", sizeof(la_i_conjugate_mr_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_conjugate_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_conjugate_mr", sizeof(la_k_conjugate_mr_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_conjugate_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_conjugate_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_conjugate_mc", sizeof(la_i_conjugate_mc_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_conjugate_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_conjugate_mc", sizeof(la_k_conjugate_mc_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_conjugate_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_conjugate_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_norm1_vr", sizeof(la_i_norm1_vr_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_norm1_vr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_norm1_vr", sizeof(la_k_norm1_vr_t), 0, "k", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_norm1_vr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_norm1_vr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_norm1_vc", sizeof(la_i_norm1_vc_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_norm1_vc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_norm1_vc", sizeof(la_k_norm1_vc_t), 0, "k", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_norm1_vc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_norm1_vc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_norm1_mr", sizeof(la_i_norm1_mr_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_norm1_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_norm1_mr", sizeof(la_k_norm1_mr_t), 0, "k", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_norm1_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_norm1_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_norm1_mc", sizeof(la_i_norm1_mc_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_norm1_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_norm1_mc", sizeof(la_k_norm1_mc_t), 0, "k", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_norm1_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_norm1_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_norm_euclid_vr", sizeof(la_i_norm_euclid_vr_t), 0, "i", + "i", (int32_t (*)(CSOUND *, void *)) & la_i_norm_euclid_vr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_norm_euclid_vr", sizeof(la_k_norm_euclid_vr_t), 0, "k", + "i", (int32_t (*)(CSOUND *, void *)) & la_k_norm_euclid_vr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_norm_euclid_vr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_norm_euclid_vc", sizeof(la_i_norm_euclid_vc_t), 0, "i", + "i", (int32_t (*)(CSOUND *, void *)) & la_i_norm_euclid_vc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_norm_euclid_vc", sizeof(la_k_norm_euclid_vc_t), 0, "k", + "i", (int32_t (*)(CSOUND *, void *)) & la_k_norm_euclid_vc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_norm_euclid_vc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_norm_euclid_mr", sizeof(la_i_norm_euclid_mr_t), 0, "i", + "i", (int32_t (*)(CSOUND *, void *)) & la_i_norm_euclid_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_norm_euclid_mr", sizeof(la_k_norm_euclid_mr_t), 0, "k", + "i", (int32_t (*)(CSOUND *, void *)) & la_k_norm_euclid_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_norm_euclid_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_norm_euclid_mc", sizeof(la_i_norm_euclid_mc_t), 0, "i", + "i", (int32_t (*)(CSOUND *, void *)) & la_i_norm_euclid_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_norm_euclid_mc", sizeof(la_k_norm_euclid_mc_t), 0, "k", + "i", (int32_t (*)(CSOUND *, void *)) & la_k_norm_euclid_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_norm_euclid_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_distance_vr", sizeof(la_i_distance_vr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_distance_vr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_distance_vr", sizeof(la_k_distance_vr_t), 0, "k", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_distance_vr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_distance_vr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_distance_vc", sizeof(la_i_distance_vc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_distance_vc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_distance_vc", sizeof(la_k_distance_vc_t), 0, "k", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_distance_vc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_distance_vc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_norm_max_mr", sizeof(la_i_norm_max_mr_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_norm_max_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_norm_max_mr", sizeof(la_k_norm_max_mr_t), 0, "k", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_norm_max_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_norm_max_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_norm_max_mc", sizeof(la_i_norm_max_mc_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_norm_max_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_norm_max_mc", sizeof(la_k_norm_max_mc_t), 0, "k", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_norm_max_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_norm_max_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_norm_inf_vr", sizeof(la_i_norm_inf_vr_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_norm_inf_vr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_norm_inf_vr", sizeof(la_k_norm_inf_vr_t), 0, "k", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_norm_inf_vr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_norm_inf_vr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_norm_inf_vc", sizeof(la_i_norm_inf_vc_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_norm_inf_vc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_norm_inf_vc", sizeof(la_k_norm_inf_vc_t), 0, "k", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_norm_inf_vc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_norm_inf_vc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_norm_inf_mr", sizeof(la_i_norm_inf_mr_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_norm_inf_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_norm_inf_mr", sizeof(la_k_norm_inf_mr_t), 0, "k", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_norm_inf_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_norm_inf_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_norm_inf_mc", sizeof(la_i_norm_inf_mc_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_norm_inf_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_norm_inf_mc", sizeof(la_k_norm_inf_mc_t), 0, "k", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_norm_inf_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_norm_inf_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_trace_mr", sizeof(la_i_trace_mr_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_trace_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_trace_mr", sizeof(la_k_trace_mr_t), 0, "k", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_trace_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_trace_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_trace_mc", sizeof(la_i_trace_mc_t), 0, "ii", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_trace_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_trace_mc", sizeof(la_k_trace_mc_t), 0, "kk", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_trace_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_trace_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_lu_det_mr", sizeof(la_i_lu_det_mr_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_lu_det_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_lu_det_mr", sizeof(la_k_lu_det_mr_t), 0, "k", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_lu_det_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_lu_det_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_lu_det_mc", sizeof(la_i_lu_det_mc_t), 0, "ii", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_lu_det_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_lu_det_mc", sizeof(la_k_lu_det_mc_t), 0, "kk", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_lu_det_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_lu_det_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_add_vr", sizeof(la_i_add_vr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_add_vr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_add_vr", sizeof(la_k_add_vr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_add_vr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_add_vr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_add_vc", sizeof(la_i_add_vc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_add_vc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_add_vc", sizeof(la_k_add_vc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_add_vc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_add_vc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_add_mr", sizeof(la_i_add_mr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_add_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_add_mr", sizeof(la_k_add_mr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_add_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_add_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_add_mc", sizeof(la_i_add_mc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_add_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_add_mc", sizeof(la_k_add_mc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_add_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_add_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_subtract_vr", sizeof(la_i_subtract_vr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_subtract_vr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_subtract_vr", sizeof(la_k_subtract_vr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_subtract_vr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_subtract_vr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_subtract_vc", sizeof(la_i_subtract_vc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_subtract_vc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_subtract_vc", sizeof(la_k_subtract_vc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_subtract_vc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_subtract_vc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_subtract_mr", sizeof(la_i_subtract_mr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_subtract_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_subtract_mr", sizeof(la_k_subtract_mr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_subtract_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_subtract_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_subtract_mc", sizeof(la_i_subtract_mc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_subtract_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_subtract_mc", sizeof(la_k_subtract_mc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_subtract_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_subtract_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_multiply_vr", sizeof(la_i_multiply_vr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_multiply_vr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_multiply_vr", sizeof(la_k_multiply_vr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_multiply_vr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_multiply_vr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_multiply_vc", sizeof(la_i_multiply_vc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_multiply_vc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_multiply_vc", sizeof(la_k_multiply_vc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_multiply_vc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_multiply_vc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_multiply_mr", sizeof(la_i_multiply_mr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_multiply_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_multiply_mr", sizeof(la_k_multiply_mr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_multiply_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_multiply_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_multiply_mc", sizeof(la_i_multiply_mc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_multiply_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_multiply_mc", sizeof(la_k_multiply_mc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_multiply_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_multiply_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + + status |= csound->AppendOpcode( + csound, "la_i_divide_vr", sizeof(la_i_divide_vr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_divide_vr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_divide_vr", sizeof(la_k_divide_vr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_divide_vr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_divide_vr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_divide_vc", sizeof(la_i_divide_vc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_divide_vc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_divide_vc", sizeof(la_k_divide_vc_t), 0, "i", "kk", + (int32_t (*)(CSOUND *, void *)) & la_k_divide_vc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_divide_vc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_divide_mr", sizeof(la_i_divide_mr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_divide_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_divide_mr", sizeof(la_k_divide_mr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_divide_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_divide_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_divide_mc", sizeof(la_i_divide_mc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_divide_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_divide_mc", sizeof(la_k_divide_mc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_divide_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_divide_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_dot_vr", sizeof(la_i_dot_vr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_dot_vr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_dot_vr", sizeof(la_k_dot_vr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_dot_vr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_dot_vr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_dot_vc", sizeof(la_i_dot_vc_t), 0, "ii", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_dot_vc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_dot_vc", sizeof(la_k_dot_vc_t), 0, "ii", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_dot_vc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_dot_vc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_dot_mr", sizeof(la_i_dot_mr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_dot_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_dot_mr", sizeof(la_k_dot_mr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_dot_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_dot_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_dot_mc", sizeof(la_i_dot_mc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_dot_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_dot_mc", sizeof(la_k_dot_mc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_dot_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_dot_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_dot_mr_vr", sizeof(la_i_dot_mr_vr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_dot_mr_vr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_dot_mr_vr", sizeof(la_k_dot_mr_vr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_dot_mr_vr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_dot_mr_vr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_dot_mc_vc", sizeof(la_i_dot_mc_vc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_dot_mc_vc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_dot_mc_vc", sizeof(la_k_dot_mc_vc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_dot_mc_vc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_dot_mc_vc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_invert_mr", sizeof(la_i_invert_mr_t), 0, "ii", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_invert_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_invert_mr", sizeof(la_k_invert_mr_t), 0, "ik", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_invert_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_invert_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_invert_mc", sizeof(la_i_invert_mc_t), 0, "iii", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_invert_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_invert_mc", sizeof(la_k_invert_mc_t), 0, "ikk", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_invert_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_invert_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_upper_solve_mr", sizeof(la_i_upper_solve_mr_t), 0, "i", + "io", (int32_t (*)(CSOUND *, void *)) & la_i_upper_solve_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_upper_solve_mr", sizeof(la_k_upper_solve_mr_t), 0, "i", + "iO", (int32_t (*)(CSOUND *, void *)) & la_k_upper_solve_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_upper_solve_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_upper_solve_mc", sizeof(la_i_upper_solve_mc_t), 0, "i", + "io", (int32_t (*)(CSOUND *, void *)) & la_i_upper_solve_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_upper_solve_mc", sizeof(la_k_upper_solve_mc_t), 0, "i", + "iO", (int32_t (*)(CSOUND *, void *)) & la_k_upper_solve_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_upper_solve_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_lower_solve_mr", sizeof(la_i_lower_solve_mr_t), 0, "i", + "io", (int32_t (*)(CSOUND *, void *)) & la_i_lower_solve_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_lower_solve_mr", sizeof(la_k_lower_solve_mr_t), 0, "i", + "iO", (int32_t (*)(CSOUND *, void *)) & la_k_lower_solve_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_lower_solve_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_lower_solve_mc", sizeof(la_i_lower_solve_mc_t), 0, "i", + "io", (int32_t (*)(CSOUND *, void *)) & la_i_lower_solve_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_lower_solve_mc", sizeof(la_k_lower_solve_mc_t), 0, "i", + "iO", (int32_t (*)(CSOUND *, void *)) & la_k_lower_solve_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_lower_solve_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_lu_factor_mr", sizeof(la_i_lu_factor_mr_t), 0, "iii", + "i", (int32_t (*)(CSOUND *, void *)) & la_i_lu_factor_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_lu_factor_mr", sizeof(la_k_lu_factor_mr_t), 0, "iik", + "i", (int32_t (*)(CSOUND *, void *)) & la_k_lu_factor_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_lu_factor_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_lu_factor_mc", sizeof(la_i_lu_factor_mc_t), 0, "iii", + "i", (int32_t (*)(CSOUND *, void *)) & la_i_lu_factor_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_lu_factor_mc", sizeof(la_k_lu_factor_mc_t), 0, "i", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_lu_factor_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_lu_factor_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_lu_solve_mr", sizeof(la_i_lu_solve_mr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_lu_solve_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_lu_solve_mr", sizeof(la_k_lu_solve_mr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_lu_solve_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_lu_solve_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_lu_solve_mc", sizeof(la_i_lu_solve_mc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_lu_solve_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_lu_solve_mc", sizeof(la_k_lu_solve_mc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_k_lu_solve_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_lu_solve_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_qr_factor_mr", sizeof(la_i_qr_factor_mr_t), 0, "ii", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_qr_factor_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_qr_factor_mr", sizeof(la_k_qr_factor_mr_t), 0, "ii", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_qr_factor_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_qr_factor_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_qr_factor_mc", sizeof(la_i_qr_factor_mc_t), 0, "ii", "i", + (int32_t (*)(CSOUND *, void *)) & la_i_qr_factor_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_qr_factor_mc", sizeof(la_k_qr_factor_mc_t), 0, "ii", "i", + (int32_t (*)(CSOUND *, void *)) & la_k_qr_factor_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_qr_factor_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_qr_eigen_mr", sizeof(la_i_qr_eigen_mr_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_qr_eigen_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_qr_eigen_mr", sizeof(la_k_qr_eigen_mr_t), 0, "i", "ik", + (int32_t (*)(CSOUND *, void *)) & la_k_qr_eigen_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_qr_eigen_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_qr_eigen_mc", sizeof(la_i_qr_eigen_mc_t), 0, "i", "ii", + (int32_t (*)(CSOUND *, void *)) & la_i_qr_eigen_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_qr_eigen_mc", sizeof(la_k_qr_eigen_mc_t), 0, "i", "ik", + (int32_t (*)(CSOUND *, void *)) & la_k_qr_eigen_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_qr_eigen_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_qr_sym_eigen_mr", sizeof(la_i_qr_sym_eigen_mr_t), 0, + "ii", "ii", (int32_t (*)(CSOUND *, void *)) & la_i_qr_sym_eigen_mr_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_qr_sym_eigen_mr", sizeof(la_k_qr_sym_eigen_mr_t), 0, + "ii", "ik", (int32_t (*)(CSOUND *, void *)) & la_k_qr_sym_eigen_mr_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_qr_sym_eigen_mr_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_i_qr_sym_eigen_mc", sizeof(la_i_qr_sym_eigen_mc_t), 0, + "ii", "ii", (int32_t (*)(CSOUND *, void *)) & la_i_qr_sym_eigen_mc_t::init_, + (int32_t (*)(CSOUND *, void *))0, (int32_t (*)(CSOUND *, void *))0); + status |= csound->AppendOpcode( + csound, "la_k_qr_sym_eigen_mc", sizeof(la_k_qr_sym_eigen_mc_t), 0, + "ii", "ik", (int32_t (*)(CSOUND *, void *)) & la_k_qr_sym_eigen_mc_t::init_, + (int32_t (*)(CSOUND *, void *)) & la_k_qr_sym_eigen_mc_t::kontrol_, + (int32_t (*)(CSOUND *, void *))0); return status; } -PUBLIC int csoundModuleDestroy(CSOUND *csound) { return 0; } +PUBLIC int32_t csoundModuleDestroy(CSOUND *csound) { return 0; } } diff --git a/src/p5glove/CMakeLists.txt b/src/p5glove/CMakeLists.txt new file mode 100644 index 00000000..591a72bc --- /dev/null +++ b/src/p5glove/CMakeLists.txt @@ -0,0 +1,30 @@ +# Build the P5Glove opcodes. +# +# The P5Glove driver is not available as a standard distribution package, and +# the vcpkg registry has neither libp5glove nor the libusb-0.1 compatibility +# layer its Linux backend needs, so this plugin is disabled in the automated +# CI/release builds (see the README for details). +# +# On macOS the driver can additionally be built from the bundled sources under +# ./libp5glove by setting P5GLOVE_USE_BUNDLED=ON. +option(BUILD_P5GLOVE_OPCODES "Build the P5Glove opcodes" ON) +option(P5GLOVE_USE_BUNDLED "Build p5glove from the bundled libp5glove sources (macOS)" OFF) + +if(APPLE AND P5GLOVE_USE_BUNDLED) + if(BUILD_P5GLOVE_OPCODES) + make_plugin(p5g "p5glove.c;libp5glove/p5glove.c;libp5glove/usb_hid_macosx.c") + target_link_libraries(p5g PRIVATE "-framework IOKit" "-framework CoreFoundation") + target_include_directories(p5g PRIVATE ./libp5glove ${CSOUND_INCLUDE_DIRS}) + target_compile_definitions(p5g PRIVATE BUILD_PLUGINS) + endif() +else() + find_path(P5GLOVE_INCLUDE_DIR p5glove.h /usr/local/include /usr/include) + find_library(P5GLOVE_LIB p5glove) + check_deps(BUILD_P5GLOVE_OPCODES P5GLOVE_INCLUDE_DIR P5GLOVE_LIB) + + if(BUILD_P5GLOVE_OPCODES) + make_plugin(p5g p5glove.c ${P5GLOVE_LIB}) + target_include_directories(p5g PRIVATE ${P5GLOVE_INCLUDE_DIR} ${CSOUND_INCLUDE_DIRS}) + target_compile_definitions(p5g PRIVATE BUILD_PLUGINS) + endif() +endif() diff --git a/6.x/src/p5glove/libp5glove/macosx_usb_hid.h b/src/p5glove/libp5glove/macosx_usb_hid.h similarity index 100% rename from 6.x/src/p5glove/libp5glove/macosx_usb_hid.h rename to src/p5glove/libp5glove/macosx_usb_hid.h diff --git a/6.x/src/p5glove/libp5glove/p5glove.c b/src/p5glove/libp5glove/p5glove.c similarity index 96% rename from 6.x/src/p5glove/libp5glove/p5glove.c rename to src/p5glove/libp5glove/p5glove.c index 5abc3111..8d469320 100644 --- a/6.x/src/p5glove/libp5glove/p5glove.c +++ b/src/p5glove/libp5glove/p5glove.c @@ -38,7 +38,6 @@ #include #include "p5glove.h" -#define DEBUG #ifdef DEBUG #define DPRINTF(fmt,args...) fprintf(stderr,"p5: " fmt , ##args ) #else @@ -114,8 +113,8 @@ static int32_t get_bits_signed(uint8_t *data,int pos,int len) uint32_t value,mask=0; value = get_bits(data,pos,len); - if (value & (1 << (len-1))) - mask = 0xffffffff << (len - 32); + if (len < 32 && (value & (1u << (len-1)))) + mask = 0xffffffffu << len; return value | mask; } @@ -283,7 +282,7 @@ static int p5g_best_leds(P5Glove p5,int led[4],double pos[4][3]) double err = fabs(dist-ref_dist)/ref_dist; DPRINTF("%d - %d: %.4lf ",led[i],led[j],ref_dist); - DPRINTF("(%.4lf) [%.4lf]\n",led[i],led[j],dist,err); + DPRINTF("(%.4lf) [%.4lf]\n",dist,err); /* Max 9% error margin on distance */ if (err > 0.20) { @@ -302,9 +301,9 @@ static int p5g_best_leds(P5Glove p5,int led[4],double pos[4][3]) DPRINTF("Dead: Led %d\n",led[i]); leds--; if (i != leds) { - memcpy(&error[i],&error[i+1],sizeof(int)*(leds-i)); - memcpy(&led[i],&led[i+1],sizeof(double)*(leds-i)); - memcpy(pos[i],pos[i+1],sizeof(double)*3*(leds-i)); + memmove(&error[i],&error[i+1],sizeof(error[0])*(leds-i)); + memmove(&led[i],&led[i+1],sizeof(led[0])*(leds-i)); + memmove(pos[i],pos[i+1],sizeof(pos[0])*(leds-i)); } i--; if (leds == 1) @@ -354,9 +353,9 @@ static int p5g_best_leds(P5Glove p5,int led[4],double pos[4][3]) DPRINTF("Dead: Led %d\n",led[i]); leds--; if (i != leds) { - memcpy(&error[i],&error[i+1],sizeof(int)*(leds-i)); - memcpy(&led[i],&led[i+1],sizeof(double)*(leds-i)); - memcpy(pos[i],pos[i+1],sizeof(double)*3*(leds-i)); + memmove(&error[i],&error[i+1],sizeof(error[0])*(leds-i)); + memmove(&led[i],&led[i+1],sizeof(led[0])*(leds-i)); + memmove(pos[i],pos[i+1],sizeof(pos[0])*(leds-i)); } i--; if (leds == 1) @@ -591,6 +590,11 @@ P5Glove p5glove_open(int glove_number) return NULL; p5=calloc(1,sizeof(*p5)); + if (p5 == NULL) { + CloseUSBHID(usb); + errno = ENOMEM; + return NULL; + } p5->usb=usb; err = p5glove_calibrate(p5); if (err < 0) { diff --git a/6.x/src/p5glove/libp5glove/p5glove.h b/src/p5glove/libp5glove/p5glove.h similarity index 100% rename from 6.x/src/p5glove/libp5glove/p5glove.h rename to src/p5glove/libp5glove/p5glove.h diff --git a/6.x/src/p5glove/libp5glove/usb_hid.h b/src/p5glove/libp5glove/usb_hid.h similarity index 100% rename from 6.x/src/p5glove/libp5glove/usb_hid.h rename to src/p5glove/libp5glove/usb_hid.h diff --git a/6.x/src/p5glove/libp5glove/usb_hid_libusb.c b/src/p5glove/libp5glove/usb_hid_libusb.c similarity index 100% rename from 6.x/src/p5glove/libp5glove/usb_hid_libusb.c rename to src/p5glove/libp5glove/usb_hid_libusb.c diff --git a/6.x/src/p5glove/libp5glove/usb_hid_macosx.c b/src/p5glove/libp5glove/usb_hid_macosx.c similarity index 95% rename from 6.x/src/p5glove/libp5glove/usb_hid_macosx.c rename to src/p5glove/libp5glove/usb_hid_macosx.c index 6359fd6e..c82ddfbd 100644 --- a/6.x/src/p5glove/libp5glove/usb_hid_macosx.c +++ b/src/p5glove/libp5glove/usb_hid_macosx.c @@ -142,6 +142,7 @@ USBHIDHandle OpenUSBHID( int index, int idVendor, int idProduct, int versionNumb if (err) { fprintf( stderr, "dealWithDevice: unable to open device. ret = %08x\n", err); + (*dev)->Release(dev); return 0; } @@ -185,6 +186,7 @@ USBHIDHandle OpenUSBHID( int index, int idVendor, int idProduct, int versionNumb interfaceRequest.bInterfaceProtocol = kIOUSBFindInterfaceDontCare; // requested protocol interfaceRequest.bAlternateSetting = kIOUSBFindInterfaceDontCare; // requested alt setting + IOObjectRelease(iterator); // done with the device iterator err = (*dev)->CreateInterfaceIterator(dev, &interfaceRequest, &iterator); if (err) { @@ -221,15 +223,21 @@ USBHIDHandle OpenUSBHID( int index, int idVendor, int idProduct, int versionNumb if (err) { (*intf)->Release( intf ); + (*dev)->USBDeviceClose(dev); + (*dev)->Release(dev); fprintf( stderr, "dealWithInterface: unable to open interface. ret = %08x, score = %d \n", err, (int)score); return 0; } + /* the interface stays usable; drop the device reference */ + (*dev)->USBDeviceClose(dev); + (*dev)->Release(dev); }else{ return 0; } + IOObjectRelease(iterator); // done with the interface iterator err = (*intf)->GetNumEndpoints(intf, &numPipes); if (err) { @@ -290,8 +298,11 @@ int ReadUSBHID( USBHIDHandle usbHidHandle, void *data, int count ) { IOUSBInterfaceInterface183 **intf = usbHidHandle; UInt32 readSize = count; + IOReturn ret; - (*intf)->ReadPipe(intf, 1, data, &readSize ); // 1 should be inPipeRef + ret = (*intf)->ReadPipe(intf, 1, data, &readSize ); // 1 should be inPipeRef + if (ret != kIOReturnSuccess) + return -1; return readSize; } diff --git a/6.x/src/p5glove/libp5glove/usb_hid_win32.c b/src/p5glove/libp5glove/usb_hid_win32.c similarity index 99% rename from 6.x/src/p5glove/libp5glove/usb_hid_win32.c rename to src/p5glove/libp5glove/usb_hid_win32.c index 6987acf4..3b597bcb 100644 --- a/6.x/src/p5glove/libp5glove/usb_hid_win32.c +++ b/src/p5glove/libp5glove/usb_hid_win32.c @@ -26,6 +26,7 @@ #include #include #include +#include #ifdef HAVE_HIDSDI_DDK_HEADERS @@ -340,7 +341,7 @@ int SetUSBHIDFeature( USBHID handle, char *report, int count ) return HidD_SetFeature( handle, report, count ) == TRUE ? 0 : -EINVAL; } -void GetUSBHIDFeature( USBHID handle, char *report, int count ) +int GetUSBHIDFeature( USBHID handle, char *report, int count ) { return HidD_GetFeature( handle, report, count ) == TRUE ? 0 : -EINVAL; } diff --git a/6.x/src/p5glove/p5glove.c b/src/p5glove/p5glove.c similarity index 68% rename from 6.x/src/p5glove/p5glove.c rename to src/p5glove/p5glove.c index 41a1bab6..cf6ab2ce 100644 --- a/6.x/src/p5glove/p5glove.c +++ b/src/p5glove/p5glove.c @@ -30,26 +30,17 @@ typedef struct { OPDS h; /* ------------------------------------- */ void *thread; - int32_t on; } P5GLOVEINIT; typedef struct { OPDS h; - MYFLT *res; - MYFLT *kControl; - MYFLT *num; + cs_float *res; + cs_float *kControl; + cs_float *num; /* ------------------------------------- */ uint32_t last; } P5GLOVE; -typedef struct { - OPDS h; - MYFLT *iControl; - MYFLT *iMin; - MYFLT *iMax; - MYFLT *num; -} P5GRANGE; - #define P5G_BUTTONS 0 #define P5G_BUTTON_A 1 #define P5G_BUTTON_B 2 @@ -75,10 +66,7 @@ typedef struct { #define P5G_DELTA_ZR 42 #define P5G_ANGLES 43 -//P5Glove myGlove; - - -uintptr_t runp5thread(void *g) +static uintptr_t runp5thread(void *g) { P5Glove *glove = (P5Glove *)g; while (*glove) { @@ -89,8 +77,23 @@ uintptr_t runp5thread(void *g) int32_t p5g_deinit(CSOUND *csound, P5GLOVEINIT *p) { - p->on = 0; - return pthread_cancel((pthread_t)(p->thread)); + P5Glove *glove = (P5Glove*)csound->QueryGlobalVariable(csound, "p5glove"); + P5Glove handle = NULL; + if (glove != NULL) { + handle = *glove; + *glove = NULL; /* ask the reader thread to stop */ + } + if (p->thread != NULL) { + csound->JoinThread(p->thread); + p->thread = NULL; + } + if (handle != NULL) { + p5glove_close(handle); + } + if (glove != NULL) { + csound->DestroyGlobalVariable(csound, "p5glove"); + } + return OK; } int32_t p5glove_find(CSOUND *csound, P5GLOVEINIT *p) @@ -99,15 +102,25 @@ int32_t p5glove_find(CSOUND *csound, P5GLOVEINIT *p) if (glove == NULL) { csound->CreateGlobalVariable(csound, "p5glove", sizeof(P5Glove)); glove = (P5Glove*)csound->QueryGlobalVariable(csound, "p5glove"); + if (UNLIKELY(glove == NULL)) { + return csound->InitError(csound, "%s", + Str("cannot allocate p5glove state")); + } + } + if (UNLIKELY(*glove != NULL)) { + return csound->InitError(csound, "%s", + Str("p5glove is already connected")); } *glove = p5glove_open(0); if (UNLIKELY(*glove==NULL)) { return csound->InitError(csound, "%s", Str("unable to open p5glove\n")); } - p->on = 1; - csound->RegisterDeinitCallback(csound, p, - (int32_t (*)(CSOUND *, void *)) p5g_deinit); p->thread = csound->CreateThread(runp5thread, glove); + if (UNLIKELY(p->thread == NULL)) { + p5glove_close(*glove); + *glove = NULL; + return csound->InitError(csound, "%s", Str("unable to start p5glove thread\n")); + } return OK; } @@ -127,17 +140,6 @@ int32_t p5glove_poll(CSOUND *csound, P5GLOVE *p) return OK; } -int32_t p5glove_closer(CSOUND *csound, P5GLOVE *p) -{ - P5Glove *glove = (P5Glove*)csound->QueryGlobalVariable(csound, "p5glove"); - if (glove==NULL) return NOTOK; - printf("Closer called\n"); - p5glove_close(*glove); - *glove = NULL; - csound->DestroyGlobalVariable(csound, "p5glove"); - return OK; -} - int32_t p5g_data_init(CSOUND *csound, P5GLOVE *p) { /* P5Glove p5g; */ @@ -151,16 +153,15 @@ int32_t p5g_data_init(CSOUND *csound, P5GLOVE *p) int32_t p5g_data(CSOUND *csound, P5GLOVE *p) { P5Glove *glove = (P5Glove*)csound->QueryGlobalVariable(csound, "p5glove"); - int32_t kontrol = (int)(*p->kControl+FL(0.5)); + int32_t kontrol = (int32_t)(*p->kControl+FL(0.5)); uint32_t buttons, just, rels; - if (glove==NULL) - csound->PerfError(csound, &p->h, "%s", Str("No open glove")); + if (UNLIKELY(glove==NULL || *glove==NULL)) + return csound->PerfError(csound, &(p->h), "%s", Str("No open glove")); p5glove_get_buttons(*glove,&buttons); - just = ((!p->last) & buttons); - rels = (p->last & !buttons); + just = ((~p->last) & buttons); + rels = (p->last & ~buttons); p->last = buttons; if (kontrol<0) { - printf("debug:\n"); *p->res = FL(0.0); return OK; } @@ -168,51 +169,51 @@ int32_t p5g_data(CSOUND *csound, P5GLOVE *p) /* printf("buttons=%.1x\tjust=%.1x\trels=%.1x\n", buttons, just, rels); */ switch (kontrol) { case P5G_BUTTONS: - *p->res = (MYFLT)(buttons); + *p->res = (cs_float)(buttons); return OK; case P5G_BUTTON_A: - *p->res = (MYFLT)(buttons & P5GLOVE_BUTTON_A); + *p->res = (cs_float)(buttons & P5GLOVE_BUTTON_A); return OK; case P5G_BUTTON_B: - *p->res = (MYFLT)(buttons & P5GLOVE_BUTTON_B); + *p->res = (cs_float)(buttons & P5GLOVE_BUTTON_B); return OK; case P5G_BUTTON_C: - *p->res = (MYFLT)(buttons & P5GLOVE_BUTTON_C); + *p->res = (cs_float)(buttons & P5GLOVE_BUTTON_C); return OK; case P5G_JUSTPUSH: - *p->res = (MYFLT)just; + *p->res = (cs_float)just; return OK; case P5G_JUSTPU_A: - *p->res = (MYFLT)(just & P5GLOVE_BUTTON_A); + *p->res = (cs_float)(just & P5GLOVE_BUTTON_A); return OK; case P5G_JUSTPU_B: - *p->res = (MYFLT)(just & P5GLOVE_BUTTON_B); + *p->res = (cs_float)(just & P5GLOVE_BUTTON_B); return OK; case P5G_JUSTPU_C: - *p->res = (MYFLT)(just & P5GLOVE_BUTTON_C); + *p->res = (cs_float)(just & P5GLOVE_BUTTON_C); return OK; case P5G_RELEASED: - *p->res = (MYFLT)rels; + *p->res = (cs_float)rels; return OK; case P5G_RELSED_A: - *p->res = (MYFLT)(rels & P5GLOVE_BUTTON_A); + *p->res = (cs_float)(rels & P5GLOVE_BUTTON_A); return OK; case P5G_RELSED_B: - *p->res = (MYFLT)(rels & P5GLOVE_BUTTON_B); + *p->res = (cs_float)(rels & P5GLOVE_BUTTON_B); return OK; case P5G_RELSED_C: - *p->res = (MYFLT)(rels & P5GLOVE_BUTTON_C); + *p->res = (cs_float)(rels & P5GLOVE_BUTTON_C); return OK; default: - *p->res = 0.0; + *p->res = FL(0.0); return NOTOK; } } else if (kontrol<=P5G_FINGER_THUMB) { double clench; p5glove_get_finger(*glove,kontrol-P5G_FINGER_INDEX,&clench); - *p->res = (MYFLT)clench; - /* *p->res = (MYFLT)(p5->data.finger[finger]/63.0); */ + *p->res = (cs_float)clench; + /* *p->res = (cs_float)(p5->data.finger[finger]/63.0); */ return OK; } else { @@ -224,25 +225,25 @@ int32_t p5g_data(CSOUND *csound, P5GLOVE *p) /* printf("angle: %f\n", angle); */ switch (kontrol) { case P5G_DELTA_X: - *p->res = (MYFLT)pos[0]; + *p->res = (cs_float)pos[0]; return OK; case P5G_DELTA_Y: - *p->res = (MYFLT)pos[1]; + *p->res = (cs_float)pos[1]; return OK; case P5G_DELTA_Z: - *p->res = (MYFLT)pos[2]; + *p->res = (cs_float)pos[2]; return OK; case P5G_DELTA_XR: - *p->res = (MYFLT)axis[0]; + *p->res = (cs_float)axis[0]; return OK; case P5G_DELTA_YR: - *p->res = (MYFLT)axis[1]; + *p->res = (cs_float)axis[1]; return OK; case P5G_DELTA_ZR: - *p->res = (MYFLT)axis[2]; + *p->res = (cs_float)axis[2]; return OK; case P5G_ANGLES: - *p->res = (MYFLT)angle; + *p->res = (cs_float)angle; return OK; default: break; @@ -253,10 +254,11 @@ int32_t p5g_data(CSOUND *csound, P5GLOVE *p) #define S(x) sizeof(x) -static OENTRY p5g_localops[] = { - {"p5gconnect", S(P5GLOVEINIT), 0, 3, "", "", - (SUBR)p5glove_find, (SUBR)p5glove_poll, NULL, (SUBR)p5glove_closer }, - {"p5gdata", S(P5GLOVE), 0, 3, "k", "k", (SUBR)p5g_data_init, (SUBR)p5g_data } +static OENTRY localops[] = { + {"p5gconnect", S(P5GLOVEINIT), 0, "", "", + (SUBR)p5glove_find, (SUBR)p5glove_poll, (SUBR)p5g_deinit, NULL, 0 }, + {"p5gdata", S(P5GLOVE), 0, "k", "k", + (SUBR)p5g_data_init, (SUBR)p5g_data, NULL, NULL, 0 } }; -LINKAGE_BUILTIN(p5g_localops) +LINKAGE diff --git a/6.x/src/py/CMakeLists.txt b/src/py/CMakeLists.txt similarity index 100% rename from 6.x/src/py/CMakeLists.txt rename to src/py/CMakeLists.txt diff --git a/6.x/src/py/README.md b/src/py/README.md similarity index 100% rename from 6.x/src/py/README.md rename to src/py/README.md diff --git a/7.x/src/py/examples/cabrera/python0.csd b/src/py/examples/cabrera/python0.csd similarity index 100% rename from 7.x/src/py/examples/cabrera/python0.csd rename to src/py/examples/cabrera/python0.csd diff --git a/7.x/src/py/examples/cabrera/python1.csd b/src/py/examples/cabrera/python1.csd similarity index 100% rename from 7.x/src/py/examples/cabrera/python1.csd rename to src/py/examples/cabrera/python1.csd diff --git a/7.x/src/py/examples/cabrera/python2.csd b/src/py/examples/cabrera/python2.csd similarity index 100% rename from 7.x/src/py/examples/cabrera/python2.csd rename to src/py/examples/cabrera/python2.csd diff --git a/7.x/src/py/examples/cabrera/python3.csd b/src/py/examples/cabrera/python3.csd similarity index 100% rename from 7.x/src/py/examples/cabrera/python3.csd rename to src/py/examples/cabrera/python3.csd diff --git a/7.x/src/py/examples/cabrera/python4.csd b/src/py/examples/cabrera/python4.csd similarity index 100% rename from 7.x/src/py/examples/cabrera/python4.csd rename to src/py/examples/cabrera/python4.csd diff --git a/7.x/src/py/examples/cabrera/python5.csd b/src/py/examples/cabrera/python5.csd similarity index 100% rename from 7.x/src/py/examples/cabrera/python5.csd rename to src/py/examples/cabrera/python5.csd diff --git a/7.x/src/py/examples/cabrera/pythonlocal.csd b/src/py/examples/cabrera/pythonlocal.csd similarity index 100% rename from 7.x/src/py/examples/cabrera/pythonlocal.csd rename to src/py/examples/cabrera/pythonlocal.csd diff --git a/7.x/src/py/examples/cabrera/pythonlocal2.csd b/src/py/examples/cabrera/pythonlocal2.csd similarity index 100% rename from 7.x/src/py/examples/cabrera/pythonlocal2.csd rename to src/py/examples/cabrera/pythonlocal2.csd diff --git a/6.x/src/py/examples/cabrera/pythonmarkov.csd b/src/py/examples/cabrera/pythonmarkov.csd similarity index 100% rename from 6.x/src/py/examples/cabrera/pythonmarkov.csd rename to src/py/examples/cabrera/pythonmarkov.csd diff --git a/6.x/src/py/examples/embeddedCtcsound.csd b/src/py/examples/embeddedCtcsound.csd similarity index 100% rename from 6.x/src/py/examples/embeddedCtcsound.csd rename to src/py/examples/embeddedCtcsound.csd diff --git a/7.x/src/py/examples/ievent.orc b/src/py/examples/ievent.orc similarity index 100% rename from 7.x/src/py/examples/ievent.orc rename to src/py/examples/ievent.orc diff --git a/6.x/src/py/examples/ievent.sco b/src/py/examples/ievent.sco similarity index 100% rename from 6.x/src/py/examples/ievent.sco rename to src/py/examples/ievent.sco diff --git a/6.x/src/py/examples/pyassign.orc b/src/py/examples/pyassign.orc similarity index 100% rename from 6.x/src/py/examples/pyassign.orc rename to src/py/examples/pyassign.orc diff --git a/6.x/src/py/examples/pyassign.sco b/src/py/examples/pyassign.sco similarity index 100% rename from 6.x/src/py/examples/pyassign.sco rename to src/py/examples/pyassign.sco diff --git a/6.x/src/py/examples/pycall1.orc b/src/py/examples/pycall1.orc similarity index 100% rename from 6.x/src/py/examples/pycall1.orc rename to src/py/examples/pycall1.orc diff --git a/6.x/src/py/examples/pycall1.sco b/src/py/examples/pycall1.sco similarity index 100% rename from 6.x/src/py/examples/pycall1.sco rename to src/py/examples/pycall1.sco diff --git a/6.x/src/py/examples/pycall2.orc b/src/py/examples/pycall2.orc similarity index 100% rename from 6.x/src/py/examples/pycall2.orc rename to src/py/examples/pycall2.orc diff --git a/6.x/src/py/examples/pycall2.sco b/src/py/examples/pycall2.sco similarity index 100% rename from 6.x/src/py/examples/pycall2.sco rename to src/py/examples/pycall2.sco diff --git a/6.x/src/py/examples/pycall3.orc b/src/py/examples/pycall3.orc similarity index 100% rename from 6.x/src/py/examples/pycall3.orc rename to src/py/examples/pycall3.orc diff --git a/6.x/src/py/examples/pycall3.sco b/src/py/examples/pycall3.sco similarity index 100% rename from 6.x/src/py/examples/pycall3.sco rename to src/py/examples/pycall3.sco diff --git a/6.x/src/py/examples/pycallt.orc b/src/py/examples/pycallt.orc similarity index 100% rename from 6.x/src/py/examples/pycallt.orc rename to src/py/examples/pycallt.orc diff --git a/6.x/src/py/examples/pycallt.sco b/src/py/examples/pycallt.sco similarity index 100% rename from 6.x/src/py/examples/pycallt.sco rename to src/py/examples/pycallt.sco diff --git a/6.x/src/py/examples/pyeval.orc b/src/py/examples/pyeval.orc similarity index 100% rename from 6.x/src/py/examples/pyeval.orc rename to src/py/examples/pyeval.orc diff --git a/6.x/src/py/examples/pyeval.sco b/src/py/examples/pyeval.sco similarity index 100% rename from 6.x/src/py/examples/pyeval.sco rename to src/py/examples/pyeval.sco diff --git a/6.x/src/py/examples/pyexec.orc b/src/py/examples/pyexec.orc similarity index 100% rename from 6.x/src/py/examples/pyexec.orc rename to src/py/examples/pyexec.orc diff --git a/6.x/src/py/examples/pyexec.sco b/src/py/examples/pyexec.sco similarity index 100% rename from 6.x/src/py/examples/pyexec.sco rename to src/py/examples/pyexec.sco diff --git a/6.x/src/py/examples/pyexec1.py b/src/py/examples/pyexec1.py similarity index 100% rename from 6.x/src/py/examples/pyexec1.py rename to src/py/examples/pyexec1.py diff --git a/6.x/src/py/examples/pyexec2.py b/src/py/examples/pyexec2.py similarity index 100% rename from 6.x/src/py/examples/pyexec2.py rename to src/py/examples/pyexec2.py diff --git a/6.x/src/py/examples/pyexec3.py b/src/py/examples/pyexec3.py similarity index 100% rename from 6.x/src/py/examples/pyexec3.py rename to src/py/examples/pyexec3.py diff --git a/6.x/src/py/examples/pyexec4.py b/src/py/examples/pyexec4.py similarity index 100% rename from 6.x/src/py/examples/pyexec4.py rename to src/py/examples/pyexec4.py diff --git a/6.x/src/py/examples/pyexect.orc b/src/py/examples/pyexect.orc similarity index 100% rename from 6.x/src/py/examples/pyexect.orc rename to src/py/examples/pyexect.orc diff --git a/6.x/src/py/examples/pyexect.sco b/src/py/examples/pyexect.sco similarity index 100% rename from 6.x/src/py/examples/pyexect.sco rename to src/py/examples/pyexect.sco diff --git a/7.x/src/py/examples/pyexect1.py b/src/py/examples/pyexect1.py similarity index 100% rename from 7.x/src/py/examples/pyexect1.py rename to src/py/examples/pyexect1.py diff --git a/7.x/src/py/examples/pyexect2.py b/src/py/examples/pyexect2.py similarity index 100% rename from 7.x/src/py/examples/pyexect2.py rename to src/py/examples/pyexect2.py diff --git a/7.x/src/py/examples/pyexect3.py b/src/py/examples/pyexect3.py similarity index 100% rename from 7.x/src/py/examples/pyexect3.py rename to src/py/examples/pyexect3.py diff --git a/7.x/src/py/examples/pyexect4.py b/src/py/examples/pyexect4.py similarity index 100% rename from 7.x/src/py/examples/pyexect4.py rename to src/py/examples/pyexect4.py diff --git a/6.x/src/py/examples/pyrun.csd b/src/py/examples/pyrun.csd similarity index 100% rename from 6.x/src/py/examples/pyrun.csd rename to src/py/examples/pyrun.csd diff --git a/6.x/src/py/examples/pyrun.orc b/src/py/examples/pyrun.orc similarity index 100% rename from 6.x/src/py/examples/pyrun.orc rename to src/py/examples/pyrun.orc diff --git a/6.x/src/py/examples/pyrun.sco b/src/py/examples/pyrun.sco similarity index 100% rename from 6.x/src/py/examples/pyrun.sco rename to src/py/examples/pyrun.sco diff --git a/6.x/src/py/examples/pyrunt.orc b/src/py/examples/pyrunt.orc similarity index 100% rename from 6.x/src/py/examples/pyrunt.orc rename to src/py/examples/pyrunt.orc diff --git a/6.x/src/py/examples/pyrunt.sco b/src/py/examples/pyrunt.sco similarity index 100% rename from 6.x/src/py/examples/pyrunt.sco rename to src/py/examples/pyrunt.sco diff --git a/6.x/src/py/src/README.md b/src/py/src/README.md similarity index 100% rename from 6.x/src/py/src/README.md rename to src/py/src/README.md diff --git a/7.x/src/py/src/pycall-gen.py b/src/py/src/pycall-gen.py similarity index 96% rename from 7.x/src/py/src/pycall-gen.py rename to src/py/src/pycall-gen.py index 2246c5d6..9a6b283d 100644 --- a/7.x/src/py/src/pycall-gen.py +++ b/src/py/src/pycall-gen.py @@ -164,20 +164,20 @@ def generate_pycall_opcode_struct(f, n, triggered=0): s = 'typedef struct {\n' s += ' OPDS h;\n' if n == 1: - s += ' MYFLT *result;\n' + s += ' cs_float *result;\n' else: for i in range(n): - s += ' MYFLT *result%d;\n' % (i+1) + s += ' cs_float *result%d;\n' % (i+1) if triggered: - s += ' MYFLT *trigger;\n' + s += ' cs_float *trigger;\n' s += ' STRINGDAT *function;\n' - s += ' MYFLT *args[VARGMAX-3];\n' + s += ' cs_float *args[VARGMAX-3];\n' if triggered: if n == 1: - s += ' MYFLT oresult\n;' + s += ' cs_float oresult;\n' else: for i in range(n): - s += ' MYFLT oresult%d;\n' % (i+1) + s += ' cs_float oresult%d;\n' % (i+1) s += '} PYCALL%d%s;\n' % (n, T) print(s, file=f) diff --git a/6.x/src/py/src/pycall.auto.c b/src/py/src/pycall.auto.c similarity index 100% rename from 6.x/src/py/src/pycall.auto.c rename to src/py/src/pycall.auto.c diff --git a/src/py/src/pycall.auto.h b/src/py/src/pycall.auto.h new file mode 100644 index 00000000..aa59ce14 --- /dev/null +++ b/src/py/src/pycall.auto.h @@ -0,0 +1,247 @@ +/* + * pyx.auto.h + * + * Copyright (C) 2002 Maurizio Umberto Puxeddu, Michael Gogins + * + * This software is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This software is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this software; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + * + * Don't modify this file. It's automatically generated by pyx-gen.py + */ + +typedef struct { + OPDS h; + STRINGDAT *function; + cs_float *args[VARGMAX-3]; +} PYCALL0; + +typedef struct { + OPDS h; + cs_float *trigger; + STRINGDAT *function; + cs_float *args[VARGMAX-3]; +} PYCALL0T; + +typedef struct { + OPDS h; + cs_float *result; + STRINGDAT *function; + cs_float *args[VARGMAX-3]; +} PYCALL1; + +typedef struct { + OPDS h; + cs_float *result; + cs_float *trigger; + STRINGDAT *function; + cs_float *args[VARGMAX-3]; + cs_float oresult; +} PYCALL1T; + +typedef struct { + OPDS h; + cs_float *result1; + cs_float *result2; + STRINGDAT *function; + cs_float *args[VARGMAX-3]; +} PYCALL2; + +typedef struct { + OPDS h; + cs_float *result1; + cs_float *result2; + cs_float *trigger; + STRINGDAT *function; + cs_float *args[VARGMAX-3]; + cs_float oresult1; + cs_float oresult2; +} PYCALL2T; + +typedef struct { + OPDS h; + cs_float *result1; + cs_float *result2; + cs_float *result3; + STRINGDAT *function; + cs_float *args[VARGMAX-3]; +} PYCALL3; + +typedef struct { + OPDS h; + cs_float *result1; + cs_float *result2; + cs_float *result3; + cs_float *trigger; + STRINGDAT *function; + cs_float *args[VARGMAX-3]; + cs_float oresult1; + cs_float oresult2; + cs_float oresult3; +} PYCALL3T; + +typedef struct { + OPDS h; + cs_float *result1; + cs_float *result2; + cs_float *result3; + cs_float *result4; + STRINGDAT *function; + cs_float *args[VARGMAX-3]; +} PYCALL4; + +typedef struct { + OPDS h; + cs_float *result1; + cs_float *result2; + cs_float *result3; + cs_float *result4; + cs_float *trigger; + STRINGDAT *function; + cs_float *args[VARGMAX-3]; + cs_float oresult1; + cs_float oresult2; + cs_float oresult3; + cs_float oresult4; +} PYCALL4T; + +typedef struct { + OPDS h; + cs_float *result1; + cs_float *result2; + cs_float *result3; + cs_float *result4; + cs_float *result5; + STRINGDAT *function; + cs_float *args[VARGMAX-3]; +} PYCALL5; + +typedef struct { + OPDS h; + cs_float *result1; + cs_float *result2; + cs_float *result3; + cs_float *result4; + cs_float *result5; + cs_float *trigger; + STRINGDAT *function; + cs_float *args[VARGMAX-3]; + cs_float oresult1; + cs_float oresult2; + cs_float oresult3; + cs_float oresult4; + cs_float oresult5; +} PYCALL5T; + +typedef struct { + OPDS h; + cs_float *result1; + cs_float *result2; + cs_float *result3; + cs_float *result4; + cs_float *result5; + cs_float *result6; + STRINGDAT *function; + cs_float *args[VARGMAX-3]; +} PYCALL6; + +typedef struct { + OPDS h; + cs_float *result1; + cs_float *result2; + cs_float *result3; + cs_float *result4; + cs_float *result5; + cs_float *result6; + cs_float *trigger; + STRINGDAT *function; + cs_float *args[VARGMAX-3]; + cs_float oresult1; + cs_float oresult2; + cs_float oresult3; + cs_float oresult4; + cs_float oresult5; + cs_float oresult6; +} PYCALL6T; + +typedef struct { + OPDS h; + cs_float *result1; + cs_float *result2; + cs_float *result3; + cs_float *result4; + cs_float *result5; + cs_float *result6; + cs_float *result7; + STRINGDAT *function; + cs_float *args[VARGMAX-3]; +} PYCALL7; + +typedef struct { + OPDS h; + cs_float *result1; + cs_float *result2; + cs_float *result3; + cs_float *result4; + cs_float *result5; + cs_float *result6; + cs_float *result7; + cs_float *trigger; + STRINGDAT *function; + cs_float *args[VARGMAX-3]; + cs_float oresult1; + cs_float oresult2; + cs_float oresult3; + cs_float oresult4; + cs_float oresult5; + cs_float oresult6; + cs_float oresult7; +} PYCALL7T; + +typedef struct { + OPDS h; + cs_float *result1; + cs_float *result2; + cs_float *result3; + cs_float *result4; + cs_float *result5; + cs_float *result6; + cs_float *result7; + cs_float *result8; + STRINGDAT *function; + cs_float *args[VARGMAX-3]; +} PYCALL8; + +typedef struct { + OPDS h; + cs_float *result1; + cs_float *result2; + cs_float *result3; + cs_float *result4; + cs_float *result5; + cs_float *result6; + cs_float *result7; + cs_float *result8; + cs_float *trigger; + STRINGDAT *function; + cs_float *args[VARGMAX-3]; + cs_float oresult1; + cs_float oresult2; + cs_float oresult3; + cs_float oresult4; + cs_float oresult5; + cs_float oresult6; + cs_float oresult7; + cs_float oresult8; +} PYCALL8T; + diff --git a/7.x/src/py/src/pythonhelper.h b/src/py/src/pythonhelper.h similarity index 100% rename from 7.x/src/py/src/pythonhelper.h rename to src/py/src/pythonhelper.h diff --git a/7.x/src/py/src/pythonopcodes.c b/src/py/src/pythonopcodes.c similarity index 99% rename from 7.x/src/py/src/pythonopcodes.c rename to src/py/src/pythonopcodes.c index 71689c0d..b4cc4fee 100644 --- a/7.x/src/py/src/pythonopcodes.c +++ b/src/py/src/pythonopcodes.c @@ -55,7 +55,7 @@ static CS_NOINLINE void create_private_namespace_if_needed(OPDS *o) } static void format_call_statement2(char *statement, char *callable, - int argc, MYFLT *argv[], long skip) + int argc, cs_float *argv[], long skip) { int i; @@ -77,7 +77,7 @@ static void format_call_statement2(char *statement, char *callable, } static void format_call_statement(char *statement, char *callable, - int argc, MYFLT *argv[], int skip) + int argc, cs_float *argv[], int skip) { int i; diff --git a/6.x/src/py/src/pythonopcodes.h b/src/py/src/pythonopcodes.h similarity index 96% rename from 6.x/src/py/src/pythonopcodes.h rename to src/py/src/pythonopcodes.h index 82d7d216..84d17c1a 100644 --- a/6.x/src/py/src/pythonopcodes.h +++ b/src/py/src/pythonopcodes.h @@ -39,8 +39,8 @@ typedef struct { typedef struct { OPDS h; STRINGDAT *function; - MYFLT *nresult; - MYFLT *args[VARGMAX]; + cs_float *nresult; + cs_float *args[VARGMAX]; } PYCALLN; #endif /* _pycsound_pythonopcodes_h_ */ diff --git a/6.x/src/py/src/pyx-gen.py b/src/py/src/pyx-gen.py similarity index 97% rename from 6.x/src/py/src/pyx-gen.py rename to src/py/src/pyx-gen.py index d1e54ce9..bbccee8e 100644 --- a/6.x/src/py/src/pyx-gen.py +++ b/src/py/src/pyx-gen.py @@ -154,14 +154,14 @@ def generate_pycall_opcode_struct(f, action, triggered): s = 'typedef struct {\n' s += ' OPDS h;\n' if action == 'eval': - s += ' MYFLT *result;\n' + s += ' cs_float *result;\n' if triggered: - s += ' MYFLT *trigger;\n' + s += ' cs_float *trigger;\n' s += ' STRINGDAT *string;\n' if action == 'assign': - s += ' MYFLT *value;\n' + s += ' cs_float *value;\n' if action == 'eval' and triggered: - s += ' MYFLT oresult;\n' + s += ' cs_float oresult;\n' s += '} PY%(ACTION)s%(T)s;\n' % locals() print(s, file=f) diff --git a/6.x/src/py/src/pyx.auto.c b/src/py/src/pyx.auto.c similarity index 100% rename from 6.x/src/py/src/pyx.auto.c rename to src/py/src/pyx.auto.c diff --git a/6.x/src/py/src/pyx.auto.h b/src/py/src/pyx.auto.h similarity index 86% rename from 6.x/src/py/src/pyx.auto.h rename to src/py/src/pyx.auto.h index 4744dcfd..21703ef9 100644 --- a/6.x/src/py/src/pyx.auto.h +++ b/src/py/src/pyx.auto.h @@ -27,7 +27,7 @@ typedef struct { typedef struct { OPDS h; - MYFLT *trigger; + cs_float *trigger; STRINGDAT *string; } PYEXECT; @@ -38,34 +38,34 @@ typedef struct { typedef struct { OPDS h; - MYFLT *trigger; + cs_float *trigger; STRINGDAT *string; } PYRUNT; typedef struct { OPDS h; - MYFLT *result; + cs_float *result; STRINGDAT *string; } PYEVAL; typedef struct { OPDS h; - MYFLT *result; - MYFLT *trigger; + cs_float *result; + cs_float *trigger; STRINGDAT *string; - MYFLT oresult; + cs_float oresult; } PYEVALT; typedef struct { OPDS h; STRINGDAT *string; - MYFLT *value; + cs_float *value; } PYASSIGN; typedef struct { OPDS h; - MYFLT *trigger; + cs_float *trigger; STRINGDAT *string; - MYFLT *value; + cs_float *value; } PYASSIGNT; diff --git a/src/stk/CMakeLists.txt b/src/stk/CMakeLists.txt new file mode 100644 index 00000000..b98aa7c0 --- /dev/null +++ b/src/stk/CMakeLists.txt @@ -0,0 +1,75 @@ +option(BUILD_STK_OPCODES "Build the stk opcodes" ON) +option(STK_EMBED_RAWWAVES "Embed STK's raw-wave files in the plugin" ${WASM}) + +# The STK sources are bundled under this directory (include/, src/ and +# rawwaves/) so that a patched copy is always built, independent of any system +# or package-manager STK installation. In particular, src/Sitar.cpp carries a +# fix for the sample buildup described in csound/plugins#4. +if(BUILD_STK_OPCODES) + file(GLOB stk_srcs "${CMAKE_CURRENT_SOURCE_DIR}/src/*.cpp") + list(APPEND stk_srcs "${CMAKE_CURRENT_SOURCE_DIR}/stkOpcodes.cpp") + + if(WASM) + if(NOT WASM_LIBCXX_EXCEPTIONS OR NOT WASM_CXX_EXCEPTIONS_INCLUDE_DIR OR NOT WASM_LIBUNWIND) + message(FATAL_ERROR "STK needs the WASM C++ exception runtime and unwinder") + endif() + set(WASM_CXX_EXCEPTIONS ON) + set(WASM_LIBCXX "${WASM_LIBCXX_EXCEPTIONS}") + set(WASM_CXX_INCLUDE_DIR "${WASM_CXX_EXCEPTIONS_INCLUDE_DIR}") + endif() + make_plugin(stkops "${stk_srcs}") + if(WASM) + target_link_libraries(stkops PRIVATE "${WASM_LIBUNWIND}") + endif() + target_include_directories(stkops PRIVATE + ${CSOUND_INCLUDE_DIRS} + "${CMAKE_CURRENT_SOURCE_DIR}/include" + "${CMAKE_CURRENT_SOURCE_DIR}/src") + target_compile_definitions(stkops PRIVATE BUILD_PLUGINS) + + if(STK_EMBED_RAWWAVES) + file(GLOB rawwaves CONFIGURE_DEPENDS "${CMAKE_CURRENT_SOURCE_DIR}/rawwaves/*.raw") + set(embedded_waves "${CMAKE_CURRENT_BINARY_DIR}/stk_rawwaves.inc") + add_custom_command(OUTPUT "${embedded_waves}" + COMMAND ${CMAKE_COMMAND} + "-DRAWWAVE_DIR=${CMAKE_CURRENT_SOURCE_DIR}/rawwaves" + "-DOUTPUT_FILE=${embedded_waves}" + -P "${CMAKE_CURRENT_SOURCE_DIR}/EmbedRawwaves.cmake" + DEPENDS ${rawwaves} "${CMAKE_CURRENT_SOURCE_DIR}/EmbedRawwaves.cmake" + VERBATIM) + target_sources(stkops PRIVATE "${embedded_waves}") + target_include_directories(stkops PRIVATE "${CMAKE_CURRENT_BINARY_DIR}") + target_compile_definitions(stkops PRIVATE STK_EMBED_RAWWAVES) + endif() + + # Install the rawwaves used by the STK instruments and make them the + # default raw wave path; the RAWWAVE_PATH environment variable overrides it. + if(NOT STK_EMBED_RAWWAVES) + install(DIRECTORY "${CMAKE_CURRENT_SOURCE_DIR}/rawwaves" + DESTINATION "${PLUGIN_INSTALL_DIR}/stk") + if(PLUGIN_INSTALL_DIR) + target_compile_definitions(stkops PRIVATE + DEFAULT_RAWWAVE_PATH="${PLUGIN_INSTALL_DIR}/stk/rawwaves") + endif() + endif() + + # Native unit tests exercise both adapters without an audio device or engine. + if(NOT WASM) + find_package(GTest QUIET) + if(GTest_FOUND) + add_executable(stk_adapter_tests tests/stk_adapter_test.cpp src/Stk.cpp) + set_target_properties(stk_adapter_tests PROPERTIES + CXX_STANDARD 14 + CXX_STANDARD_REQUIRED ON) + target_include_directories(stk_adapter_tests PRIVATE + ${CSOUND_INCLUDE_DIRS} + "${CMAKE_CURRENT_SOURCE_DIR}" + "${CMAKE_CURRENT_SOURCE_DIR}/include") + target_compile_definitions(stk_adapter_tests PRIVATE BUILD_PLUGINS) + target_link_libraries(stk_adapter_tests PRIVATE GTest::gtest_main) + add_test(NAME stk_adapter_tests COMMAND stk_adapter_tests) + endif() + endif() + + message(STATUS "Building STK opcodes") +endif() diff --git a/src/stk/EmbedRawwaves.cmake b/src/stk/EmbedRawwaves.cmake new file mode 100644 index 00000000..78204d23 --- /dev/null +++ b/src/stk/EmbedRawwaves.cmake @@ -0,0 +1,21 @@ +# Generate byte arrays from the bundled files; no decoder is needed at runtime. +file(GLOB waves "${RAWWAVE_DIR}/*.raw") +list(SORT waves) +file(WRITE "${OUTPUT_FILE}" "// Generated by EmbedRawwaves.cmake.\n") +set(entries "") +foreach(wave IN LISTS waves) + get_filename_component(name "${wave}" NAME) + string(MAKE_C_IDENTIFIER "${name}" symbol) + file(READ "${wave}" bytes HEX) + string(REGEX REPLACE "([0-9a-f][0-9a-f])" "0x\\1," bytes "${bytes}") + file(APPEND "${OUTPUT_FILE}" "static const unsigned char ${symbol}[] = {${bytes}};\n") + string(APPEND entries " {\"${name}\", ${symbol}, sizeof(${symbol})},\n") +endforeach() +file(APPEND "${OUTPUT_FILE}" " +static const struct { + const char *name; + const unsigned char *bytes; + size_t size; +} stkRawwaves[] = { +${entries}}; +") diff --git a/src/stk/LICENSE b/src/stk/LICENSE new file mode 100644 index 00000000..c6069996 --- /dev/null +++ b/src/stk/LICENSE @@ -0,0 +1,27 @@ +The Synthesis ToolKit in C++ (STK) + +Copyright (c) 1995-2021 Perry R. Cook and Gary P. Scavone + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +Any person wishing to distribute modifications to the Software is +asked to send the modifications to the original developer so that they +can be incorporated into the canonical version. This is, however, not +a binding provision of this license. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF +CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/stk/OpcodeBase.hpp b/src/stk/OpcodeBase.hpp new file mode 100644 index 00000000..f0e38433 --- /dev/null +++ b/src/stk/OpcodeBase.hpp @@ -0,0 +1,489 @@ +/* + OpcodeBase.hpp: + + Copyright (C) 2005, 2009, 2017 by Istva Varga, Victor Lazzarini and + Michael Gogins + + This file is part of Csound. + + The Csound Library is free software; you can redistribute it + and/or modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + Csound is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with Csound; if not, write to the Free Software + Foundation, Inc., 31 Milk Street, #960789, Boston, MA, 02196, USA +*/ +#pragma once + +/** + * There is a conflict between the preprocessor definition "_CR" in the + * standard C++ library and in Csound. To work around this, undefine "_CR" and + * include ALL standard library include files BEFORE including ANY Csound + * include files. + */ +#if defined(_CR) +#undef _CR +#endif + +#include +#include +#include +#if !(defined(__wasi__)) +#include +#endif +#include +#include +#include "interlocks.h" +#ifdef BUILD_PLUGINS +#include "csdl.h" +#else +#include "csoundCore.h" +#endif + +/** + * Template base class, or pseudo-virtual base class, + * for writing Csound opcodes in C++. + * Derive opcode implementation classes like this: + * + * DerivedClass : public OpcodeBase + * { + * public: + * // All output fields must be declared first as cs_float *: + * cs_float *aret1; + * // All input fields must be declared next as cs_float *: + * cs_float *iarg1; + * cs_float *karg2; + * cs_float *aarg3; + * // All internal state variables must be declared after that: + * size_t state1; + * double state2; + * cs_float state3; + * // If the opcode shares data protect it by creating one or more void + * // *mutex member pointers: + * void *mutex1; + * void *mutex2; + * // and create them usind csound->Create_Mutex() in csoundModuleCreate + * // and destroy them using csound->DeleteMutex(mutex) + * // csoundModuleDestroy, and lock them using LockGuard guard(mutex). + * int32_t init(); + * int32_t kontrol(); + * int32_t audio; + * int32_t noteoff(); + * void deinit(); + * }; + */ + +namespace csound { + + static bool diagnostics_enabled = false; + + /** + * Use this to guard against data races in opcode functions. The mutex should + * be created by csound->Create_Mutex() in csoundModuleCreate(), and should + * be destroyed by csound->DeleteMutex() in csoundModuleDestroy(). + * + * If data is shared between opcode instances, the mutex should be global to + * the opcode library; if data is shared between threads for a single instance, + * the mutex should belong to the opcode instance. + */ + struct LockGuard { + LockGuard(CSOUND *csound_, void *mutex_) : csound(csound_), mutex(mutex_) + { + csound->LockMutex(mutex); + } + ~LockGuard() + { + csound->UnlockMutex(mutex); + } + CSOUND *csound; + void *mutex; + }; + + /** + * Use this to store a pointer to a global heap-allocated object, e.g. one + * used to manage state between opcode instances. + */ + + template int32_t CreateGlobalPointer(CSOUND *csound, const char *name, T *pointer) + { + T **pointer_to_pointer = 0; + int32_t result = csound->CreateGlobalVariable(csound, name, sizeof(pointer_to_pointer)); + pointer_to_pointer = static_cast(csound->QueryGlobalVariable(csound, name)); + *pointer_to_pointer = pointer; + return result; + } + + /** + * Retrieve a pointer to a global heap-allocated object, e.g. one + * used to manage state between opcode instances. + */ + template T *QueryGlobalPointer(CSOUND *csound, const char *name, T*& pointer) + { + T **pointer_to_pointer = static_cast(csound->QueryGlobalVariableNoCheck(csound, name)); + if (pointer_to_pointer != 0) { + pointer = *pointer_to_pointer; + } else { + pointer = 0; + } + return pointer; + } + + template + class OpcodeBase + { + public: + int32_t init(CSOUND *csound) + { + return NOTOK; + } + static int32_t init_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->init(csound); + } + int32_t kontrol(CSOUND *csound) + { + return NOTOK; + } + static int32_t kontrol_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->kontrol(csound); + } + int32_t audio(CSOUND *csound) + { + return NOTOK; + } + static int32_t audio_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->audio(csound); + } + /** + This is how to compute audio signals for normal opcodes: + (1) Zero all frames from 0 up to but not including Offset. + (2) Compute all frames from ksmps_offset up to but not including End. + (3) Zero all frames from End up to but not including ksmps. + Example from a C opcode: + uint32_t offset = p->h.insdshead->ksmps_offset; + uint32_t early = p->h.insdshead->ksmps_no_end; + uint32_t n, nsmps = CS_KSMPS; + if (UNLIKELY(offset)) memset(p->r, '\0', offset*sizeof(cs_float)); + if (UNLIKELY(early)) { + nsmps -= early; + memset(&p->r[nsmps], '\0', early*sizeof(cs_float)); + } + for (n = offset; n < nsmps; n++) { + input1 = CS_FLOAT2LRND(p->a[n]); + p->r[n] = (cs_float) (input1 >> input2); + } + So in C++ it should look like this (which is much easier to understand): + int32_t frameIndex = 0; + for( ; frameIndex < kperiodOffset(); ++frameIndex) { + asignal[frameIndex] = 0; + } + for( ; frameIndex < kperiodEnd(); ++frameIndex) { + asignal[frameIndex] = compute(); + } + for( ; frameIndex < ksmps(); ++frameIndex) { + asignal[frameIndex] = 0; + } + */ + uint32_t kperiodOffset() const + { + return opds.insdshead->ksmps_offset; + } + uint32_t kperiodEnd() const + { + return ksmps() - opds.insdshead->ksmps_no_end; + } + uint32_t ksmps() const + { + return opds.insdshead->ksmps; + } + uint32_t output_arg_count() + { + return (uint32_t)opds.optext->t.outArgCount; + } + uint32_t input_arg_count() + { + return (uint32_t)opds.optext->t.inArgCount; + } + void log(CSOUND *csound, const char *format,...) + { + va_list args; + va_start(args, format); + if(csound) { + csound->MessageV(csound, 0, format, args); + } else { + vfprintf(stdout, format, args); + } + va_end(args); + } + void warn(CSOUND *csound, const char *format,...) + { + if(csound) { + if(csound->GetMessageLevel(csound) & CS_WARNMSG) { + va_list args; + va_start(args, format); + csound->MessageV(csound, CSOUNDMSG_WARNING, format, args); + va_end(args); + } + } else { + va_list args; + va_start(args, format); + vfprintf(stdout, format, args); + va_end(args); + } + } + OPDS opds; + }; + + template + class OpcodeNoteoffBase + { + public: + int32_t init(CSOUND *csound) + { + IGN(csound); + return NOTOK; + } + + static int32_t deinit_(CSOUND *csound, void *opcode) + { + if (!csound->GetReinitFlag(csound) && !csound->GetTieFlag(csound)) { + return noteoff_(csound, opcode); + } + return OK; + } + + static int32_t init_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->init(csound); + } + + int32_t kontrol(CSOUND *csound) + { + IGN(csound); + return NOTOK; + } + static int32_t kontrol_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->kontrol(csound); + } + int32_t audio(CSOUND *csound) + { + IGN(csound); + return NOTOK; + } + static int32_t audio_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->audio(csound); + } + /** + This is how to compute audio signals for normal opcodes: + (1) Zero all frames from 0 up to but not including Offset. + (2) Compute all frames from ksmps_offset up to but not including End. + (3) Zero all frames from End up to but not including ksmps. + Example from a C opcode: + uint32_t offset = p->h.insdshead->ksmps_offset; + uint32_t early = p->h.insdshead->ksmps_no_end; + uint32_t n, nsmps = CS_KSMPS; + if (UNLIKELY(offset)) memset(p->r, '\0', offset*sizeof(cs_float)); + if (UNLIKELY(early)) { + nsmps -= early; + memset(&p->r[nsmps], '\0', early*sizeof(cs_float)); + } + for (n = offset; n < nsmps; n++) { + input1 = CS_FLOAT2LRND(p->a[n]); + p->r[n] = (cs_float) (input1 >> input2); + } + So in C++ it should look like this (which is much easier to understand): + int32_t frameIndex = 0; + for( ; frameIndex < kperiodOffset(); ++frameIndex) { + asignal[frameIndex] = 0; + } + for( ; frameIndex < kperiodEnd(); ++frameIndex) { + asignal[frameIndex] = compute(); + } + for( ; frameIndex < ksmps(); ++frameIndex) { + asignal[frameIndex] = 0; + } + */ + uint32_t kperiodOffset() const + { + return opds.insdshead->ksmps_offset; + } + uint32_t kperiodEnd() const + { + return ksmps() - opds.insdshead->ksmps_no_end; + } + uint32_t ksmps() const + { + return opds.insdshead->ksmps; + } + uint32_t output_arg_count() + { + return (uint32_t)opds.optext->t.outArgCount; + } + uint32_t input_arg_count() + { + return (uint32_t)opds.optext->t.inArgCount; + } + void log(CSOUND *csound, const char *format,...) + { + va_list args; + va_start(args, format); + if(csound) { + csound->MessageV(csound, 0, format, args); + } else { + vfprintf(stdout, format, args); + } + va_end(args); + } + void warn(CSOUND *csound, const char *format,...) + { + if(csound) { + if(csound->GetMessageLevel(csound) & CS_WARNMSG) { + va_list args; + va_start(args, format); + csound->MessageV(csound, CSOUNDMSG_WARNING, format, args); + va_end(args); + } + } else { + va_list args; + va_start(args, format); + vfprintf(stdout, format, args); + va_end(args); + } + } + int32_t noteoff(CSOUND *csound) + { + return OK; + } + static int32_t noteoff_(CSOUND *csound, void *opcode) + { + return reinterpret_cast(opcode)->noteoff(csound); + } + OPDS opds; + }; + + +#if (__cplusplus >= 201103L) && !(defined(__wasi__)) && !defined(BARE_METAL) + // #pragma message("Defining heap_object_manager_t.") + /** + * The memory of non-POD C++ or C objects allocated on the heap by Csound + * plugins is managed not by Csound, but by the plugin module. This class + * performs that function. + * + * As long as all memory is allocated in or after csoundModuleCreate and + * de-allocated in csoundModuleDestroy, all will be well. The danger however + * is that memory leaks will occur if allocations are not matched with + * de-allocations, or worse, that crashes will occur if any objects are + * deleted twice or de-referenced after deletion. + * + * So, the plugin should manage a collection of all heap-allocated objects, + * keyed first off the Csound instance, and second off an object handle; and + * all such objects must be accessed only by handle, from this collection. + * + * In csoundModuleDestroy, all objects for the calling instance of Csound + * should be de-allocated, and the sub-collection for that instance should + * also be de-allocated. + * + * If then there are no more sub-collections for other instances of Csound, + * the entire contents of the collection should be de-allocated. + * + * This should be done as a singleton, and in a thread-safe way. + * + * There might still be a problem if any of the heap-allocated objects, + * themselves, have global static members, perhaps created by some external + * dependency. + * + * Please note, objects O in the declaration below must not be std::shared_ptr + * objects. Deletion of objects herein must be unconditional. Rather, the + * objects passed are raw pointers to instances of class O. + */ + template class heap_object_manager_t { + private: + std::map> objects_; + std::recursive_mutex mutex; + heap_object_manager_t(){}; + ~heap_object_manager_t(){}; + public: + static heap_object_manager_t &instance() { + static heap_object_manager_t singleton; + return singleton; + } + std::map> &objects() { + std::lock_guard lock(mutex); + return objects_; + } + /** + * Returns a list of pointers to all objects allocated for this + * instance of Csound. + */ + std::vector &objects_for_csound(CSOUND *csound) { + std::lock_guard lock(mutex); + auto &objects_for_csound_ = objects()[csound]; + return objects_for_csound_; + } + /** + * Returns the handle for the object; if the object has not yet been + * stored, inserts it into the list of object pointers for this + * instance of Csound; otherwise, returns the handle of the stored + * object pointer. + */ + int32_t handle_for_object(CSOUND *csound, O *object) { + std::lock_guard lock(mutex); + auto &objects_for_csound_ = objects_for_csound(csound); + auto iterator = std::find(objects_for_csound_.begin(), objects_for_csound_.end(), object); + if (iterator == objects_for_csound_.end()) { + int32_t handle = objects_for_csound_.size(); + objects_for_csound_.push_back(object); + if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::handle_for_object %p: new object handle: %d (of %ld)\n", object, handle, objects_for_csound_.size()); + return handle; + } else { + int32_t handle = static_cast(iterator - objects_for_csound_.begin()); + if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::handle_for_object: existing object handle: %d\n", handle); + return handle; + } + } + /** + * Returns the object pointer for the handle; + * if the object pointer has not been stored by + * handle, returns a null pointer. + */ + O *object_for_handle(CSOUND *csound, int32_t handle) { + std::lock_guard lock(mutex); + auto &objects_for_csound_ = objects_for_csound(csound); + if (handle >= objects_for_csound_.size()) { + return nullptr; + } + O *object = objects_for_csound_[handle]; + if (diagnostics_enabled) std::fprintf(stderr, "heap_object_manager_t::object_for_handle: %p %d (of %ld)\n", object, handle, objects_for_csound_.size()); + return object; + } + /** + * First destroys all objects created by the calling + * instance of Csound, then destroys the list of + * object pointers for this instance of Csound. + */ + void module_destroy(CSOUND *csound) { + std::lock_guard lock(mutex); + auto &objects_for_csound_ = objects_for_csound(csound); + for (int32_t i = 0, n = objects_for_csound_.size(); i < n; ++i) { + delete objects_for_csound_[i]; + objects_for_csound_[i] = nullptr; + } + objects_for_csound_.clear(); + objects().erase(csound); + } + }; + +#endif + +} diff --git a/src/stk/STKInstrumentAdapter.hpp b/src/stk/STKInstrumentAdapter.hpp new file mode 100644 index 00000000..0b94e275 --- /dev/null +++ b/src/stk/STKInstrumentAdapter.hpp @@ -0,0 +1,327 @@ +/* + This file is part of Csound. + Copyright (C) 2005 Michael Gogins. + + The Csound Library is free software; you can redistribute it + and/or modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + Csound is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with Csound; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + 02110-1301 USA +*/ +#pragma once + +#include "Stk.h" +#include "OpcodeBase.hpp" + +namespace csound { + +template +class STKInstrumentAdapter : public OpcodeBase> { +public: + // Outputs. + cs_float *aoutput; + // Inputs. + cs_float *ifrequency; + cs_float *igain; + cs_float *kcontroller0; + cs_float *kvalue0; + cs_float *kcontroller1; + cs_float *kvalue1; + cs_float *kcontroller2; + cs_float *kvalue2; + cs_float *kcontroller3; + cs_float *kvalue3; + cs_float *kcontroller4; + cs_float *kvalue4; + cs_float *kcontroller5; + cs_float *kvalue5; + cs_float *kcontroller6; + cs_float *kvalue6; + cs_float *kcontroller7; + cs_float *kvalue7; + // State. + T *instrument; + bool released; + cs_float oldkcontroller0; + cs_float oldkvalue0; + cs_float oldkcontroller1; + cs_float oldkvalue1; + cs_float oldkcontroller2; + cs_float oldkvalue2; + cs_float oldkcontroller3; + cs_float oldkvalue3; + cs_float oldkcontroller4; + cs_float oldkvalue4; + cs_float oldkcontroller5; + cs_float oldkvalue5; + cs_float oldkcontroller6; + cs_float oldkvalue6; + cs_float oldkcontroller7; + cs_float oldkvalue7; + STKInstrumentAdapter() : instrument(0) {} + int32_t init(CSOUND *csound) { + // Constructors size delay lines and filters from STK's shared sample rate. + // Set it on reinit too, before noteOn() retunes an existing instrument. + stk::Stk::setSampleRate(static_cast(this->opds.insdshead->esr)); + if (!instrument) { + try { + instrument = new T(); + } catch (stk::StkError &e) { + return csound->InitError(csound, "STK: %s", e.getMessage().c_str()); + } + } + instrument->noteOn(*ifrequency, *igain); + released = false; + oldkcontroller0 = -1.0; + oldkvalue0 = -1.0; + oldkcontroller1 = -1.0; + oldkvalue1 = -1.0; + oldkcontroller2 = -1.0; + oldkvalue2 = -1.0; + oldkcontroller3 = -1.0; + oldkvalue3 = -1.0; + oldkcontroller4 = -1.0; + oldkvalue4 = -1.0; + oldkcontroller5 = -1.0; + oldkvalue5 = -1.0; + oldkcontroller6 = -1.0; + oldkvalue6 = -1.0; + oldkcontroller7 = -1.0; + oldkvalue7 = -1.0; + return OK; + } + int32_t kontrol(CSOUND *csound) { + if (!released) { + if (*kcontroller0 != oldkcontroller0 || *kvalue0 != oldkvalue0) { + instrument->controlChange(static_cast(*kcontroller0), *kvalue0); + oldkcontroller0 = *kcontroller0; + oldkvalue0 = *kvalue0; + } + if (*kcontroller1 != oldkcontroller1 || *kvalue1 != oldkvalue1) { + instrument->controlChange(static_cast(*kcontroller1), *kvalue1); + oldkcontroller1 = *kcontroller1; + oldkvalue1 = *kvalue1; + } + if (*kcontroller2 != oldkcontroller2 || *kvalue2 != oldkvalue2) { + instrument->controlChange(static_cast(*kcontroller2), *kvalue2); + oldkcontroller2 = *kcontroller2; + oldkvalue2 = *kvalue2; + } + if (*kcontroller3 != oldkcontroller3 || *kvalue3 != oldkvalue3) { + instrument->controlChange(static_cast(*kcontroller3), *kvalue3); + oldkcontroller3 = *kcontroller3; + oldkvalue3 = *kvalue3; + } + if (*kcontroller4 != oldkcontroller4 || *kvalue4 != oldkvalue4) { + instrument->controlChange(static_cast(*kcontroller4), *kvalue4); + oldkcontroller4 = *kcontroller4; + oldkvalue4 = *kvalue4; + } + if (*kcontroller5 != oldkcontroller5 || *kvalue5 != oldkvalue5) { + instrument->controlChange(static_cast(*kcontroller5), *kvalue5); + oldkcontroller5 = *kcontroller5; + oldkvalue5 = *kvalue5; + } + if (*kcontroller6 != oldkcontroller6 || *kvalue6 != oldkvalue6) { + instrument->controlChange(static_cast(*kcontroller6), *kvalue6); + oldkcontroller6 = *kcontroller6; + oldkvalue6 = *kvalue6; + } + if (*kcontroller7 != oldkcontroller7 || *kvalue7 != oldkvalue7) { + instrument->controlChange(static_cast(*kcontroller7), *kvalue7); + oldkcontroller7 = *kcontroller7; + oldkvalue7 = *kvalue7; + } + } + uint32_t offset = this->kperiodOffset(); + uint32_t end = this->kperiodEnd(); + uint32_t nsmps = this->ksmps(); + for (uint32_t i = 0; i < offset; i++) { + aoutput[i] = 0; + } + if (!released) { + for (uint32_t i = offset; i < end; i++) { + aoutput[i] = instrument->tick(); + } + } else { + for (uint32_t i = offset; i < end; i++) { + aoutput[i] = 0; + } + } + for (uint32_t i = end; i < nsmps; i++) { + aoutput[i] = 0; + } + return OK; + } + int32_t deinit(CSOUND *csound) { + if (instrument) { + delete instrument; + instrument = 0; + } + return OK; + } + static int32_t deinit_(CSOUND *csound, void *opcode) { + return reinterpret_cast *>(opcode)->deinit(csound); + } +}; + +template +class STKInstrumentAdapter1 : public OpcodeBase> { +public: + // Outputs. + cs_float *aoutput; + // Inputs. + cs_float *ifrequency; + cs_float *igain; + cs_float *kcontroller0; + cs_float *kvalue0; + cs_float *kcontroller1; + cs_float *kvalue1; + cs_float *kcontroller2; + cs_float *kvalue2; + cs_float *kcontroller3; + cs_float *kvalue3; + cs_float *kcontroller4; + cs_float *kvalue4; + cs_float *kcontroller5; + cs_float *kvalue5; + cs_float *kcontroller6; + cs_float *kvalue6; + cs_float *kcontroller7; + cs_float *kvalue7; + // State. + T *instrument; + bool released; + cs_float oldkcontroller0; + cs_float oldkvalue0; + cs_float oldkcontroller1; + cs_float oldkvalue1; + cs_float oldkcontroller2; + cs_float oldkvalue2; + cs_float oldkcontroller3; + cs_float oldkvalue3; + cs_float oldkcontroller4; + cs_float oldkvalue4; + cs_float oldkcontroller5; + cs_float oldkvalue5; + cs_float oldkcontroller6; + cs_float oldkvalue6; + cs_float oldkcontroller7; + cs_float oldkvalue7; + STKInstrumentAdapter1() : instrument(0) {} + int32_t init(CSOUND *csound) { + // Constructors size delay lines and filters from STK's shared sample rate. + // Set it on reinit too, before noteOn() retunes an existing instrument. + stk::Stk::setSampleRate(static_cast(this->opds.insdshead->esr)); + if (!instrument) { + try { + instrument = new T((stk::StkFloat)10.0); + } catch (stk::StkError &e) { + return csound->InitError(csound, "STK: %s", e.getMessage().c_str()); + } + } + instrument->noteOn(*ifrequency, *igain); + released = false; + oldkcontroller0 = -1.0; + oldkvalue0 = -1.0; + oldkcontroller1 = -1.0; + oldkvalue1 = -1.0; + oldkcontroller2 = -1.0; + oldkvalue2 = -1.0; + oldkcontroller3 = -1.0; + oldkvalue3 = -1.0; + oldkcontroller4 = -1.0; + oldkvalue4 = -1.0; + oldkcontroller5 = -1.0; + oldkvalue5 = -1.0; + oldkcontroller6 = -1.0; + oldkvalue6 = -1.0; + oldkcontroller7 = -1.0; + oldkvalue7 = -1.0; + return OK; + } + int32_t kontrol(CSOUND *csound) { + if (!released) { + if (*kcontroller0 != oldkcontroller0 || *kvalue0 != oldkvalue0) { + instrument->controlChange(static_cast(*kcontroller0), *kvalue0); + oldkcontroller0 = *kcontroller0; + oldkvalue0 = *kvalue0; + } + if (*kcontroller1 != oldkcontroller1 || *kvalue1 != oldkvalue1) { + instrument->controlChange(static_cast(*kcontroller1), *kvalue1); + oldkcontroller1 = *kcontroller1; + oldkvalue1 = *kvalue1; + } + if (*kcontroller2 != oldkcontroller2 || *kvalue2 != oldkvalue2) { + instrument->controlChange(static_cast(*kcontroller2), *kvalue2); + oldkcontroller2 = *kcontroller2; + oldkvalue2 = *kvalue2; + } + if (*kcontroller3 != oldkcontroller3 || *kvalue3 != oldkvalue3) { + instrument->controlChange(static_cast(*kcontroller3), *kvalue3); + oldkcontroller3 = *kcontroller3; + oldkvalue3 = *kvalue3; + } + if (*kcontroller4 != oldkcontroller4 || *kvalue4 != oldkvalue4) { + instrument->controlChange(static_cast(*kcontroller4), *kvalue4); + oldkcontroller4 = *kcontroller4; + oldkvalue4 = *kvalue4; + } + if (*kcontroller5 != oldkcontroller5 || *kvalue5 != oldkvalue5) { + instrument->controlChange(static_cast(*kcontroller5), *kvalue5); + oldkcontroller5 = *kcontroller5; + oldkvalue5 = *kvalue5; + } + if (*kcontroller6 != oldkcontroller6 || *kvalue6 != oldkvalue6) { + instrument->controlChange(static_cast(*kcontroller6), *kvalue6); + oldkcontroller6 = *kcontroller6; + oldkvalue6 = *kvalue6; + } + if (*kcontroller7 != oldkcontroller7 || *kvalue7 != oldkvalue7) { + instrument->controlChange(static_cast(*kcontroller7), *kvalue7); + oldkcontroller7 = *kcontroller7; + oldkvalue7 = *kvalue7; + } + } + uint32_t offset = this->kperiodOffset(); + uint32_t end = this->kperiodEnd(); + uint32_t nsmps = this->ksmps(); + for (uint32_t i = 0; i < offset; i++) { + aoutput[i] = 0; + } + if (!released) { + for (uint32_t i = offset; i < end; i++) { + aoutput[i] = instrument->tick(); + } + } else { + for (uint32_t i = offset; i < end; i++) { + aoutput[i] = 0; + } + } + for (uint32_t i = end; i < nsmps; i++) { + aoutput[i] = 0; + } + return OK; + } + int32_t deinit(CSOUND *csound) { + if (instrument) { + delete instrument; + instrument = 0; + } + return OK; + } + static int32_t deinit_(CSOUND *csound, void *opcode) { + return reinterpret_cast *>(opcode)->deinit(csound); + } +}; + +} // namespace csound diff --git a/src/stk/include/ADSR.h b/src/stk/include/ADSR.h new file mode 100644 index 00000000..777778ed --- /dev/null +++ b/src/stk/include/ADSR.h @@ -0,0 +1,179 @@ +#ifndef STK_ADSR_H +#define STK_ADSR_H + +#include "Generator.h" + +namespace stk { + +/***************************************************/ +/*! \class ADSR + \brief STK ADSR envelope class. + + This class implements a traditional ADSR (Attack, Decay, Sustain, + Release) envelope. It responds to simple keyOn and keyOff + messages, keeping track of its state. The \e state = ADSR::IDLE + before being triggered and after the envelope value reaches 0.0 in + the ADSR::RELEASE state. All rate, target and level settings must + be non-negative. All time settings are in seconds and must be + positive. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class ADSR : public Generator +{ + public: + + //! ADSR envelope states. + enum { + ATTACK, /*!< Attack */ + DECAY, /*!< Decay */ + SUSTAIN, /*!< Sustain */ + RELEASE, /*!< Release */ + IDLE /*!< Before attack / after release */ + }; + + //! Default constructor. + ADSR( void ); + + //! Class destructor. + ~ADSR( void ); + + //! Set target = 1, state = \e ADSR::ATTACK. + void keyOn( void ); + + //! Set target = 0, state = \e ADSR::RELEASE. + void keyOff( void ); + + //! Set the attack rate (gain / sample). + void setAttackRate( StkFloat rate ); + + //! Set the target value for the attack (default = 1.0). + void setAttackTarget( StkFloat target ); + + //! Set the decay rate (gain / sample). + void setDecayRate( StkFloat rate ); + + //! Set the sustain level. + void setSustainLevel( StkFloat level ); + + //! Set the release rate (gain / sample). + void setReleaseRate( StkFloat rate ); + + //! Set the attack rate based on a time duration (seconds). + void setAttackTime( StkFloat time ); + + //! Set the decay rate based on a time duration (seconds). + void setDecayTime( StkFloat time ); + + //! Set the release rate based on a time duration (seconds). + void setReleaseTime( StkFloat time ); + + //! Set sustain level and attack, decay, and release time durations (seconds). + void setAllTimes( StkFloat aTime, StkFloat dTime, StkFloat sLevel, StkFloat rTime ); + + //! Set a sustain target value and attack or decay from current value to target. + void setTarget( StkFloat target ); + + //! Return the current envelope \e state (ATTACK, DECAY, SUSTAIN, RELEASE, IDLE). + int getState( void ) const { return state_; }; + + //! Set to state = ADSR::SUSTAIN with current and target values of \e value. + void setValue( StkFloat value ); + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Compute and return one output sample. + StkFloat tick( void ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + + void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); + + int state_; + StkFloat value_; + StkFloat target_; + StkFloat attackRate_; + StkFloat decayRate_; + StkFloat releaseRate_; + StkFloat releaseTime_; + StkFloat sustainLevel_; +}; + +inline StkFloat ADSR :: tick( void ) +{ + switch ( state_ ) { + + case ATTACK: + value_ += attackRate_; + if ( value_ >= target_ ) { + value_ = target_; + target_ = sustainLevel_; + state_ = DECAY; + } + lastFrame_[0] = value_; + break; + + case DECAY: + if ( value_ > sustainLevel_ ) { + value_ -= decayRate_; + if ( value_ <= sustainLevel_ ) { + value_ = sustainLevel_; + state_ = SUSTAIN; + } + } + else { + value_ += decayRate_; // attack target < sustain level + if ( value_ >= sustainLevel_ ) { + value_ = sustainLevel_; + state_ = SUSTAIN; + } + } + lastFrame_[0] = value_; + break; + + case RELEASE: + value_ -= releaseRate_; + if ( value_ <= 0.0 ) { + value_ = 0.0; + state_ = IDLE; + } + lastFrame_[0] = value_; + + } + + return value_; +} + +inline StkFrames& ADSR :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "ADSR::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i 0). + /*! + The rate is computed as described above. The value of \e tau + must be greater than zero. Values of \e tau close to zero produce + fast approach rates, while values greater than 1.0 produce rather + slow rates. + */ + void setTau( StkFloat tau ); + + //! Set the asymptotic rate based on a time duration (must be > 0). + void setTime( StkFloat time ); + + //! Set the asymptotic rate such that the target value is perceptually reached (to within -60dB of the target) in \e t60 seconds. + void setT60( StkFloat t60 ); + + //! Set the target value. + void setTarget( StkFloat target ); + + //! Set current and target values to \e value. + void setValue( StkFloat value ); + + //! Return the current envelope \e state (0 = at target, 1 otherwise). + int getState( void ) const { return state_; }; + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Compute and return one output sample. + StkFloat tick( void ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + + void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); + + StkFloat value_; + StkFloat target_; + StkFloat factor_; + StkFloat constant_; + int state_; +}; + +inline StkFloat Asymp :: tick( void ) +{ + if ( state_ ) { + + value_ = factor_ * value_ + constant_; + + // Check threshold. + if ( target_ > value_ ) { + if ( target_ - value_ <= TARGET_THRESHOLD ) { + value_ = target_; + state_ = 0; + } + } + else { + if ( value_ - target_ <= TARGET_THRESHOLD ) { + value_ = target_; + state_ = 0; + } + } + lastFrame_[0] = value_; + } + + return value_; +} + +inline StkFrames& Asymp :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "Asymp::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i frames.channels() - nChannels ) { + oStream_ << "BandedWG::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i Out + 3 -/| + 4 -- + \endcode + + Control Change Numbers: + - Operator 4 (feedback) Gain = 2 + - Operator 3 Gain = 4 + - LFO Speed = 11 + - LFO Depth = 1 + - ADSR 2 & 4 Target = 128 + + The basic Chowning/Stanford FM patent expired + in 1995, but there exist follow-on patents, + mostly assigned to Yamaha. If you are of the + type who should worry about this (making + money) worry away. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class BeeThree : public FM +{ + public: + //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ + BeeThree( void ); + + //! Class destructor. + ~BeeThree( void ); + + //! Start a note with the given frequency and amplitude. + void noteOn( StkFloat frequency, StkFloat amplitude ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + +}; + +inline StkFloat BeeThree :: tick( unsigned int ) +{ + StkFloat temp; + + if ( modDepth_ > 0.0 ) { + temp = 1.0 + ( modDepth_ * vibrato_.tick() * 0.1 ); + waves_[0]->setFrequency( baseFrequency_ * temp * ratios_[0] ); + waves_[1]->setFrequency( baseFrequency_ * temp * ratios_[1] ); + waves_[2]->setFrequency( baseFrequency_ * temp * ratios_[2] ); + waves_[3]->setFrequency( baseFrequency_ * temp * ratios_[3] ); + } + + waves_[3]->addPhaseOffset( twozero_.lastOut() ); + temp = control1_ * 2.0 * gains_[3] * adsr_[3]->tick() * waves_[3]->tick(); + twozero_.tick( temp ); + + temp += control2_ * 2.0 * gains_[2] * adsr_[2]->tick() * waves_[2]->tick(); + temp += gains_[1] * adsr_[1]->tick() * waves_[1]->tick(); + temp += gains_[0] * adsr_[0]->tick() * waves_[0]->tick(); + + lastFrame_[0] = temp * 0.125; + return lastFrame_[0]; +} + +inline StkFrames& BeeThree :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "BeeThree::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i= 1.0. The + \e frequency value should be between zero and half the sample rate. + */ + void setResonance( StkFloat frequency, StkFloat radius, bool normalize = false ); + + //! Set the filter coefficients for a notch at \e frequency (in Hz). + /*! + This method determines the filter coefficients corresponding to + two complex-conjugate zeros with the given \e frequency (in Hz) + and \e radius from the z-plane origin. No filter normalization is + attempted. The \e frequency value should be between zero and half + the sample rate. The \e radius value should be positive. + */ + void setNotch( StkFloat frequency, StkFloat radius ); + + //! Sets the filter zeroes for equal resonance gain. + /*! + When using the filter as a resonator, zeroes places at z = 1, z + = -1 will result in a constant gain at resonance of 1 / (1 - R), + where R is the pole radius setting. + + */ + void setEqualGainZeroes( void ); + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Input one sample to the filter and return a reference to one output. + StkFloat tick( StkFloat input ); + + //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. + /*! + The StkFrames argument reference is returned. The \c channel + argument must be less than the number of channels in the + StkFrames argument (the first channel is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the filter and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. Each channel + argument must be less than the number of channels in the + corresponding StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); + + protected: + + virtual void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); +}; + +inline StkFloat BiQuad :: tick( StkFloat input ) +{ + inputs_[0] = gain_ * input; + lastFrame_[0] = b_[0] * inputs_[0] + b_[1] * inputs_[1] + b_[2] * inputs_[2]; + lastFrame_[0] -= a_[2] * outputs_[2] + a_[1] * outputs_[1]; + inputs_[2] = inputs_[1]; + inputs_[1] = inputs_[0]; + outputs_[2] = outputs_[1]; + outputs_[1] = lastFrame_[0]; + + return lastFrame_[0]; +} + +inline StkFrames& BiQuad :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "BiQuad::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + oStream_ << "BiQuad::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i +#include + +namespace stk { + +/***************************************************/ +/*! \class Blit + \brief STK band-limited impulse train class. + + This class generates a band-limited impulse train using a + closed-form algorithm reported by Stilson and Smith in "Alias-Free + Digital Synthesis of Classic Analog Waveforms", 1996. The user + can specify both the fundamental frequency of the impulse train + and the number of harmonics contained in the resulting signal. + + The signal is normalized so that the peak value is +/-1.0. + + If nHarmonics is 0, then the signal will contain all harmonics up + to half the sample rate. Note, however, that this setting may + produce aliasing in the signal when the frequency is changing (no + automatic modification of the number of harmonics is performed by + the setFrequency() function). + + Original code by Robin Davies, 2005. + Revisions by Gary Scavone for STK, 2005. +*/ +/***************************************************/ + +class Blit: public Generator +{ + public: + //! Default constructor that initializes BLIT frequency to 220 Hz. + Blit( StkFloat frequency = 220.0 ); + + //! Class destructor. + ~Blit(); + + //! Resets the oscillator state and phase to 0. + void reset(); + + //! Set the phase of the signal. + /*! + Set the phase of the signal, in the range 0 to 1. + */ + void setPhase( StkFloat phase ) { phase_ = PI * phase; }; + + //! Get the current phase of the signal. + /*! + Get the phase of the signal, in the range [0 to 1.0). + */ + StkFloat getPhase() const { return phase_ / PI; }; + + //! Set the impulse train rate in terms of a frequency in Hz. + void setFrequency( StkFloat frequency ); + + //! Set the number of harmonics generated in the signal. + /*! + This function sets the number of harmonics contained in the + resulting signal. It is equivalent to (2 * M) + 1 in the BLIT + algorithm. The default value of 0 sets the algorithm for maximum + harmonic content (harmonics up to half the sample rate). This + parameter is not checked against the current sample rate and + fundamental frequency. Thus, aliasing can result if one or more + harmonics for a given fundamental frequency exceeds fs / 2. This + behavior was chosen over the potentially more problematic solution + of automatically modifying the M parameter, which can produce + audible clicks in the signal. + */ + void setHarmonics( unsigned int nHarmonics = 0 ); + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Compute and return one output sample. + StkFloat tick( void ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + + void updateHarmonics( void ); + + unsigned int nHarmonics_; + unsigned int m_; + StkFloat rate_; + StkFloat phase_; + StkFloat p_; + +}; + +inline StkFloat Blit :: tick( void ) +{ + // The code below implements the SincM algorithm of Stilson and + // Smith with an additional scale factor of P / M applied to + // normalize the output. + + // A fully optimized version of this code would replace the two sin + // calls with a pair of fast sin oscillators, for which stable fast + // two-multiply algorithms are well known. In the spirit of STK, + // which favors clarity over performance, the optimization has not + // been made here. + + // Avoid a divide by zero at the sinc peak, which has a limiting + // value of 1.0. + StkFloat tmp, denominator = sin( phase_ ); + if ( denominator <= std::numeric_limits::epsilon() ) + tmp = 1.0; + else { + tmp = sin( m_ * phase_ ); + tmp /= m_ * denominator; + } + + phase_ += rate_; + if ( phase_ >= PI ) phase_ -= PI; + + lastFrame_[0] = tmp; + return lastFrame_[0]; +} + +inline StkFrames& Blit :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "Blit::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i +#include + +namespace stk { + +/***************************************************/ +/*! \class BlitSaw + \brief STK band-limited sawtooth wave class. + + This class generates a band-limited sawtooth waveform using a + closed-form algorithm reported by Stilson and Smith in "Alias-Free + Digital Synthesis of Classic Analog Waveforms", 1996. The user + can specify both the fundamental frequency of the sawtooth and the + number of harmonics contained in the resulting signal. + + If nHarmonics is 0, then the signal will contain all harmonics up + to half the sample rate. Note, however, that this setting may + produce aliasing in the signal when the frequency is changing (no + automatic modification of the number of harmonics is performed by + the setFrequency() function). + + Based on initial code of Robin Davies, 2005. + Modified algorithm code by Gary Scavone, 2005. +*/ +/***************************************************/ + +class BlitSaw: public Generator +{ + public: + //! Class constructor. + BlitSaw( StkFloat frequency = 220.0 ); + + //! Class destructor. + ~BlitSaw(); + + //! Resets the oscillator state and phase to 0. + void reset(); + + //! Set the sawtooth oscillator rate in terms of a frequency in Hz. + void setFrequency( StkFloat frequency ); + + //! Set the number of harmonics generated in the signal. + /*! + This function sets the number of harmonics contained in the + resulting signal. It is equivalent to (2 * M) + 1 in the BLIT + algorithm. The default value of 0 sets the algorithm for maximum + harmonic content (harmonics up to half the sample rate). This + parameter is not checked against the current sample rate and + fundamental frequency. Thus, aliasing can result if one or more + harmonics for a given fundamental frequency exceeds fs / 2. This + behavior was chosen over the potentially more problematic solution + of automatically modifying the M parameter, which can produce + audible clicks in the signal. + */ + void setHarmonics( unsigned int nHarmonics = 0 ); + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Compute and return one output sample. + StkFloat tick( void ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + + void updateHarmonics( void ); + + unsigned int nHarmonics_; + unsigned int m_; + StkFloat rate_; + StkFloat phase_; + StkFloat p_; + StkFloat C2_; + StkFloat a_; + StkFloat state_; + +}; + +inline StkFloat BlitSaw :: tick( void ) +{ + // The code below implements the BLIT algorithm of Stilson and + // Smith, followed by a summation and filtering operation to produce + // a sawtooth waveform. After experimenting with various approaches + // to calculate the average value of the BLIT over one period, I + // found that an estimate of C2_ = 1.0 / period (in samples) worked + // most consistently. A "leaky integrator" is then applied to the + // difference of the BLIT output and C2_. (GPS - 1 October 2005) + + // A fully optimized version of this code would replace the two sin + // calls with a pair of fast sin oscillators, for which stable fast + // two-multiply algorithms are well known. In the spirit of STK, + // which favors clarity over performance, the optimization has + // not been made here. + + // Avoid a divide by zero, or use of a denormalized divisor + // at the sinc peak, which has a limiting value of m_ / p_. + StkFloat tmp, denominator = sin( phase_ ); + if ( fabs(denominator) <= std::numeric_limits::epsilon() ) + tmp = a_; + else { + tmp = sin( m_ * phase_ ); + tmp /= p_ * denominator; + } + + tmp += state_ - C2_; + state_ = tmp * 0.995; + + phase_ += rate_; + if ( phase_ >= PI ) phase_ -= PI; + + lastFrame_[0] = tmp; + return lastFrame_[0]; +} + +inline StkFrames& BlitSaw :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "BlitSaw::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i +#include + +namespace stk { + +/***************************************************/ +/*! \class BlitSquare + \brief STK band-limited square wave class. + + This class generates a band-limited square wave signal. It is + derived in part from the approach reported by Stilson and Smith in + "Alias-Free Digital Synthesis of Classic Analog Waveforms", 1996. + The algorithm implemented in this class uses a SincM function with + an even M value to achieve a bipolar bandlimited impulse train. + This signal is then integrated to achieve a square waveform. The + integration process has an associated DC offset so a DC blocking + filter is applied at the output. + + The user can specify both the fundamental frequency of the + waveform and the number of harmonics contained in the resulting + signal. + + If nHarmonics is 0, then the signal will contain all harmonics up + to half the sample rate. Note, however, that this setting may + produce aliasing in the signal when the frequency is changing (no + automatic modification of the number of harmonics is performed by + the setFrequency() function). Also note that the harmonics of a + square wave fall at odd integer multiples of the fundamental, so + aliasing will happen with a lower fundamental than with the other + Blit waveforms. This class is not guaranteed to be well behaved + in the presence of significant aliasing. + + Based on initial code of Robin Davies, 2005. + Modified algorithm code by Gary Scavone, 2005--2006. +*/ +/***************************************************/ + +class BlitSquare: public Generator +{ + public: + //! Default constructor that initializes BLIT frequency to 220 Hz. + BlitSquare( StkFloat frequency = 220.0 ); + + //! Class destructor. + ~BlitSquare(); + + //! Resets the oscillator state and phase to 0. + void reset(); + + //! Set the phase of the signal. + /*! + Set the phase of the signal, in the range 0 to 1. + */ + void setPhase( StkFloat phase ) { phase_ = PI * phase; }; + + //! Get the current phase of the signal. + /*! + Get the phase of the signal, in the range [0 to 1.0). + */ + StkFloat getPhase() const { return phase_ / PI; }; + + //! Set the impulse train rate in terms of a frequency in Hz. + void setFrequency( StkFloat frequency ); + + //! Set the number of harmonics generated in the signal. + /*! + This function sets the number of harmonics contained in the + resulting signal. It is equivalent to (2 * M) + 1 in the BLIT + algorithm. The default value of 0 sets the algorithm for maximum + harmonic content (harmonics up to half the sample rate). This + parameter is not checked against the current sample rate and + fundamental frequency. Thus, aliasing can result if one or more + harmonics for a given fundamental frequency exceeds fs / 2. This + behavior was chosen over the potentially more problematic solution + of automatically modifying the M parameter, which can produce + audible clicks in the signal. + */ + void setHarmonics( unsigned int nHarmonics = 0 ); + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Compute and return one output sample. + StkFloat tick( void ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + + void updateHarmonics( void ); + + unsigned int nHarmonics_; + unsigned int m_; + StkFloat rate_; + StkFloat phase_; + StkFloat p_; + StkFloat a_; + StkFloat lastBlitOutput_; + StkFloat dcbState_; +}; + +inline StkFloat BlitSquare :: tick( void ) +{ + StkFloat temp = lastBlitOutput_; + + // A fully optimized version of this would replace the two sin calls + // with a pair of fast sin oscillators, for which stable fast + // two-multiply algorithms are well known. In the spirit of STK, + // which favors clarity over performance, the optimization has + // not been made here. + + // Avoid a divide by zero, or use of a denomralized divisor + // at the sinc peak, which has a limiting value of 1.0. + StkFloat denominator = sin( phase_ ); + if ( fabs( denominator ) < std::numeric_limits::epsilon() ) { + // Inexact comparison safely distinguishes betwen *close to zero*, and *close to PI*. + if ( phase_ < 0.1f || phase_ > TWO_PI - 0.1f ) + lastBlitOutput_ = a_; + else + lastBlitOutput_ = -a_; + } + else { + lastBlitOutput_ = sin( m_ * phase_ ); + lastBlitOutput_ /= p_ * denominator; + } + + lastBlitOutput_ += temp; + + // Now apply DC blocker. + lastFrame_[0] = lastBlitOutput_ - dcbState_ + 0.999 * lastFrame_[0]; + dcbState_ = lastBlitOutput_; + + phase_ += rate_; + if ( phase_ >= TWO_PI ) phase_ -= TWO_PI; + + return lastFrame_[0]; +} + +inline StkFrames& BlitSquare :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "BlitSquare::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i frames.channels() - nChannels ) { + oStream_ << "BlowBotl::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i frames.channels() - nChannels ) { + oStream_ << "BlowHole::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i + +namespace stk { + +/***************************************************/ +/*! \class BowTable + \brief STK bowed string table class. + + This class implements a simple bowed string + non-linear function, as described by Smith + (1986). The output is an instantaneous + reflection coefficient value. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class BowTable : public Function +{ +public: + //! Default constructor. + BowTable( void ) : offset_(0.0), slope_(0.1), minOutput_(0.01), maxOutput_(0.98) {}; + + //! Set the table offset value. + /*! + The table offset is a bias which controls the + symmetry of the friction. If you want the + friction to vary with direction, use a non-zero + value for the offset. The default value is zero. + */ + void setOffset( StkFloat offset ) { offset_ = offset; }; + + //! Set the table slope value. + /*! + The table slope controls the width of the friction + pulse, which is related to bow force. + */ + void setSlope( StkFloat slope ) { slope_ = slope; }; + + //! Set the minimum table output value (0.0 - 1.0). + void setMinOutput( StkFloat minimum ) { minOutput_ = minimum; }; + + //! Set the maximum table output value (0.0 - 1.0). + void setMaxOutput( StkFloat maximum ) { maxOutput_ = maximum; }; + + //! Take one sample input and map to one sample of output. + StkFloat tick( StkFloat input ); + + //! Take a channel of the StkFrames object as inputs to the table and replace with corresponding outputs. + /*! + The StkFrames argument reference is returned. The \c channel + argument must be less than the number of channels in the + StkFrames argument (the first channel is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the table and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. Each channel + argument must be less than the number of channels in the + corresponding StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); + +protected: + + StkFloat offset_; + StkFloat slope_; + StkFloat minOutput_; + StkFloat maxOutput_; + +}; + +inline StkFloat BowTable :: tick( StkFloat input ) +{ + // The input represents differential string vs. bow velocity. + StkFloat sample = input + offset_; // add bias to input + sample *= slope_; // then scale it + lastFrame_[0] = (StkFloat) fabs( (double) sample ) + (StkFloat) 0.75; + lastFrame_[0] = (StkFloat) pow( lastFrame_[0], (StkFloat) -4.0 ); + + // Set minimum threshold + if ( lastFrame_[0] < minOutput_ ) lastFrame_[0] = minOutput_; + + // Set maximum threshold + if ( lastFrame_[0] > maxOutput_ ) lastFrame_[0] = maxOutput_; + + return lastFrame_[0]; +} + +inline StkFrames& BowTable :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "BowTable::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i 1.0) *samples = 1.0; + } + + lastFrame_[0] = *(samples-hop); + return frames; +} + +inline StkFrames& BowTable :: tick( StkFrames& iFrames, StkFrames& oFrames, unsigned int iChannel, unsigned int oChannel ) +{ +#if defined(_STK_DEBUG_) + if ( iChannel >= iFrames.channels() || oChannel >= oFrames.channels() ) { + oStream_ << "BowTable::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i 1.0) *oSamples = 1.0; + } + + lastFrame_[0] = *(oSamples-oHop); + return iFrames; +} + +} // stk namespace + +#endif diff --git a/src/stk/include/Bowed.h b/src/stk/include/Bowed.h new file mode 100644 index 00000000..4ad57268 --- /dev/null +++ b/src/stk/include/Bowed.h @@ -0,0 +1,157 @@ +#ifndef STK_BOWED_H +#define STK_BOWED_H + +#include "Instrmnt.h" +#include "DelayL.h" +#include "BowTable.h" +#include "OnePole.h" +#include "BiQuad.h" +#include "SineWave.h" +#include "ADSR.h" + +namespace stk { + +/***************************************************/ +/*! \class Bowed + \brief STK bowed string instrument class. + + This class implements a bowed string model, a + la Smith (1986), after McIntyre, Schumacher, + Woodhouse (1983). + + This is a digital waveguide model, making its + use possibly subject to patents held by + Stanford University, Yamaha, and others. + + Control Change Numbers: + - Bow Pressure = 2 + - Bow Position = 4 + - Vibrato Frequency = 11 + - Vibrato Gain = 1 + - Bow Velocity = 100 + - Frequency = 101 + - Volume = 128 + + by Perry R. Cook and Gary P. Scavone, 1995--2021. + Contributions by Esteban Maestre, 2011. +*/ +/***************************************************/ + +class Bowed : public Instrmnt +{ + public: + //! Class constructor, taking the lowest desired playing frequency. + Bowed( StkFloat lowestFrequency = 8.0 ); + + //! Class destructor. + ~Bowed( void ); + + //! Reset and clear all internal state. + void clear( void ); + + //! Set instrument parameters for a particular frequency. + void setFrequency( StkFloat frequency ); + + //! Set vibrato gain. + void setVibrato( StkFloat gain ) { vibratoGain_ = gain; }; + + //! Apply breath pressure to instrument with given amplitude and rate of increase. + void startBowing( StkFloat amplitude, StkFloat rate ); + + //! Decrease breath pressure with given rate of decrease. + void stopBowing( StkFloat rate ); + + //! Start a note with the given frequency and amplitude. + void noteOn( StkFloat frequency, StkFloat amplitude ); + + //! Stop a note with the given amplitude (speed of decay). + void noteOff( StkFloat amplitude ); + + //! Perform the control change specified by \e number and \e value (0.0 - 128.0). + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + + DelayL neckDelay_; + DelayL bridgeDelay_; + BowTable bowTable_; + OnePole stringFilter_; + BiQuad bodyFilters_[6]; + SineWave vibrato_; + ADSR adsr_; + + bool bowDown_; + StkFloat maxVelocity_; + StkFloat baseDelay_; + StkFloat vibratoGain_; + StkFloat betaRatio_; + +}; + +inline StkFloat Bowed :: tick( unsigned int ) +{ + StkFloat bowVelocity = maxVelocity_ * adsr_.tick(); + StkFloat bridgeReflection = -stringFilter_.tick( bridgeDelay_.lastOut() ); + StkFloat nutReflection = -neckDelay_.lastOut(); + StkFloat stringVelocity = bridgeReflection + nutReflection; + StkFloat deltaV = bowVelocity - stringVelocity; // Differential velocity + + StkFloat newVelocity = 0.0; + if ( bowDown_ ) + newVelocity = deltaV * bowTable_.tick( deltaV ); // Non-Linear bow function + neckDelay_.tick( bridgeReflection + newVelocity); // Do string propagations + bridgeDelay_.tick(nutReflection + newVelocity); + + if ( vibratoGain_ > 0.0 ) { + neckDelay_.setDelay( (baseDelay_ * (1.0 - betaRatio_) ) + + (baseDelay_ * vibratoGain_ * vibrato_.tick()) ); + } + + lastFrame_[0] = 0.1248 * bodyFilters_[5].tick( bodyFilters_[4].tick( bodyFilters_[3].tick( bodyFilters_[2].tick( bodyFilters_[1].tick( bodyFilters_[0].tick( bridgeDelay_.lastOut() ) ) ) ) ) ); + + return lastFrame_[0]; +} + +inline StkFrames& Bowed :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "Bowed::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i position. + deltaPressure *= deltaPressure; // Basic position to area mapping. + if ( deltaPressure > 1.0 ) deltaPressure = 1.0; // Non-linear saturation. + + // The following input scattering assumes the mouthPressure = area. + lastFrame_[0] = deltaPressure * mouthPressure + ( 1.0 - deltaPressure) * borePressure; + lastFrame_[0] = delayLine_.tick( dcBlock_.tick( lastFrame_[0] ) ); + + return lastFrame_[0]; +} + +inline StkFrames& Brass :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "Brass::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i 1 ) { + oStream_ << "Chorus::lastOut(): channel argument must be less than 2!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + return lastFrame_[channel]; +} + +inline StkFloat Chorus :: tick( StkFloat input, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel > 1 ) { + oStream_ << "Chorus::tick(): channel argument must be less than 2!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + delayLine_[0].setDelay( baseLength_ * 0.707 * ( 1.0 + modDepth_ * mods_[0].tick() ) ); + delayLine_[1].setDelay( baseLength_ * 0.5 * ( 1.0 - modDepth_ * mods_[1].tick() ) ); + lastFrame_[0] = effectMix_ * ( delayLine_[0].tick( input ) - input ) + input; + lastFrame_[1] = effectMix_ * ( delayLine_[1].tick( input ) - input ) + input; + return lastFrame_[channel]; +} + +inline StkFrames& Chorus :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() - 1 ) { + oStream_ << "Chorus::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels() - 1; + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() - 1 ) { + oStream_ << "Chorus::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i frames.channels() - nChannels ) { + oStream_ << "Clarinet::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i + +namespace stk { + +/***************************************************/ +/*! \class Cubic + \brief STK cubic non-linearity class. + + This class implements the cubic non-linearity + that was used in SynthBuilder. + + The formula implemented is: + + \code + output = gain * (a1 * input + a2 * input^2 + a3 * input^3) + \endcode + + followed by a limiter for values outside +-threshold. + + Ported to STK by Nick Porcaro, 2007. Updated for inclusion + in STK distribution by Gary Scavone, 2011. +*/ +/***************************************************/ + +class Cubic : public Function +{ +public: + //! Default constructor. + Cubic( void ) : a1_(0.5), a2_(0.5), a3_(0.5), gain_(1.0), threshold_(1.0) {}; + + //! Set the a1 coefficient value. + void setA1( StkFloat a1 ) { a1_ = a1; }; + + //! Set the a2 coefficient value. + void setA2( StkFloat a2 ) { a2_ = a2; }; + + //! Set the a3 coefficient value. + void setA3( StkFloat a3 ) { a3_ = a3; }; + + //! Set the gain value. + void setGain( StkFloat gain ) { gain_ = gain; }; + + //! Set the threshold value. + void setThreshold( StkFloat threshold ) { threshold_ = threshold; }; + + //! Input one sample to the function and return one output. + StkFloat tick( StkFloat input ); + + //! Take a channel of the StkFrames object as inputs to the function and replace with corresponding outputs. + /*! + The StkFrames argument reference is returned. The \c channel + argument must be less than the number of channels in the + StkFrames argument (the first channel is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the function and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. Each channel + argument must be less than the number of channels in the + corresponding StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); + +protected: + + StkFloat a1_; + StkFloat a2_; + StkFloat a3_; + StkFloat gain_; + StkFloat threshold_; +}; + +inline StkFloat Cubic :: tick( StkFloat input ) +{ + StkFloat inSquared = input * input; + StkFloat inCubed = inSquared * input; + + lastFrame_[0] = gain_ * (a1_ * input + a2_ * inSquared + a3_ * inCubed); + + // Apply threshold if we are out of range. + if ( fabs( lastFrame_[0] ) > threshold_ ) { + lastFrame_[0] = ( lastFrame_[0] < 0 ? -threshold_ : threshold_ ); + } + + return lastFrame_[0]; +} + +inline StkFrames& Cubic :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "Cubic::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + oStream_ << "Cubic::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i= frames.channels() ) { + oStream_ << "Delay::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + oStream_ << "Delay::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i= frames.channels() ) { + oStream_ << "DelayA::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + oStream_ << "DelayA::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i inputs_.size() ) { // The value is too big. + oStream_ << "DelayL::setDelay: argument (" << delay << ") greater than maximum!"; + handleError( StkError::WARNING ); return; + } + + if (delay < 0 ) { + oStream_ << "DelayL::setDelay: argument (" << delay << ") less than zero!"; + handleError( StkError::WARNING ); return; + } + + StkFloat outPointer = inPoint_ - delay; // read chases write + delay_ = delay; + + while ( outPointer < 0 ) + outPointer += inputs_.size(); // modulo maximum length + + outPoint_ = (long) outPointer; // integer part + + alpha_ = outPointer - outPoint_; // fractional part + omAlpha_ = (StkFloat) 1.0 - alpha_; + + if ( outPoint_ == inputs_.size() ) outPoint_ = 0; + doNextOut_ = true; +} + +inline StkFloat DelayL :: tick( StkFloat input ) +{ + inputs_[inPoint_++] = input * gain_; + + // Increment input pointer modulo length. + if ( inPoint_ == inputs_.size() ) + inPoint_ = 0; + + lastFrame_[0] = nextOut(); + doNextOut_ = true; + + // Increment output pointer modulo length. + if ( ++outPoint_ == inputs_.size() ) + outPoint_ = 0; + + return lastFrame_[0]; +} + +inline StkFrames& DelayL :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "DelayL::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + oStream_ << "DelayL::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i soundOrder_; + std::vector soundNumber_; + int nSounding_; +}; + +inline StkFloat Drummer :: tick( unsigned int ) +{ + lastFrame_[0] = 0.0; + if ( nSounding_ == 0 ) return lastFrame_[0]; + + for ( int i=0; i= 0 ) { + if ( waves_[i].isFinished() ) { + // Re-order the list. + for ( int j=0; j soundOrder_[i] ) + soundOrder_[j] -= 1; + } + soundOrder_[i] = -1; + nSounding_--; + } + else + lastFrame_[0] += filters_[i].tick( waves_[i].tick() ); + } + } + + return lastFrame_[0]; +} + +inline StkFrames& Drummer :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "Drummer::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i= frames.channels() ) { + oStream_ << "Echo::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + oStream_ << "Echo::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i + +namespace stk { + +/***************************************************/ +/*! \class Effect + \brief STK abstract effects parent class. + + This class provides common functionality for STK effects + subclasses. It is general enough to support both monophonic and + polyphonic input/output classes. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class Effect : public Stk +{ + public: + //! Class constructor. + Effect( void ) { lastFrame_.resize( 1, 1, 0.0 ); }; + + //! Return the number of output channels for the class. + unsigned int channelsOut( void ) const { return lastFrame_.channels(); }; + + //! Return an StkFrames reference to the last output sample frame. + const StkFrames& lastFrame( void ) const { return lastFrame_; }; + + //! Reset and clear all internal state. + virtual void clear() = 0; + + //! Set the mixture of input and "effected" levels in the output (0.0 = input only, 1.0 = effect only). + virtual void setEffectMix( StkFloat mix ); + + protected: + + // Returns true if argument value is prime. + bool isPrime( unsigned int number ); + + StkFrames lastFrame_; + StkFloat effectMix_; + +}; + +inline void Effect :: setEffectMix( StkFloat mix ) +{ + if ( mix < 0.0 ) { + oStream_ << "Effect::setEffectMix: mix parameter is less than zero ... setting to zero!"; + handleError( StkError::WARNING ); + effectMix_ = 0.0; + } + else if ( mix > 1.0 ) { + oStream_ << "Effect::setEffectMix: mix parameter is greater than 1.0 ... setting to one!"; + handleError( StkError::WARNING ); + effectMix_ = 1.0; + } + else + effectMix_ = mix; +} + +inline bool Effect :: isPrime( unsigned int number ) +{ + if ( number == 2 ) return true; + if ( number & 1 ) { + for ( int i=3; i<(int)sqrt((double)number)+1; i+=2 ) + if ( (number % i) == 0 ) return false; + return true; // prime + } + else return false; // even +} + +} // stk namespace + +#endif + diff --git a/src/stk/include/Envelope.h b/src/stk/include/Envelope.h new file mode 100644 index 00000000..f104169a --- /dev/null +++ b/src/stk/include/Envelope.h @@ -0,0 +1,131 @@ +#ifndef STK_ENVELOPE_H +#define STK_ENVELOPE_H + +#include "Generator.h" + +namespace stk { + +/***************************************************/ +/*! \class Envelope + \brief STK linear line envelope class. + + This class implements a simple linear line envelope generator + which is capable of ramping to an arbitrary target value by a + specified \e rate. It also responds to simple \e keyOn and \e + keyOff messages, ramping to a specified target (default = 1.0) on + keyOn and to a specified target (default = 0.0) on keyOff. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class Envelope : public Generator +{ + public: + + //! Default constructor. + Envelope( void ); + + //! Class destructor. + ~Envelope( void ); + + //! Assignment operator. + Envelope& operator= ( const Envelope& e ); + + //! Start ramping to specified target (default = 1). + void keyOn( StkFloat target = 1.0 ) { this->setTarget( target ); }; + + //! Start ramping to specified target (default = 0). + void keyOff( StkFloat target = 0.0 ) { this->setTarget( target ); }; + + //! Set the \e rate. + /*! + The \e rate must be positive (though a value of 0.0 is allowed). + */ + void setRate( StkFloat rate ); + + //! Set the \e rate based on a positive time duration (seconds). + /*! + The \e rate is calculated such that the envelope will ramp from + the current value to the current target in the specified time duration. + */ + void setTime( StkFloat time ); + + //! Set the target value. + void setTarget( StkFloat target ); + + //! Set current and target values to \e value. + void setValue( StkFloat value ); + + //! Return the current envelope \e state (0 = at target, 1 otherwise). + int getState( void ) const { return state_; }; + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Compute and return one output sample. + StkFloat tick( void ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + + void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); + + StkFloat value_; + StkFloat target_; + StkFloat rate_; + int state_; +}; + +inline StkFloat Envelope :: tick( void ) +{ + if ( state_ ) { + if ( target_ > value_ ) { + value_ += rate_; + if ( value_ >= target_ ) { + value_ = target_; + state_ = 0; + } + } + else { + value_ -= rate_; + if ( value_ <= target_ ) { + value_ = target_; + state_ = 0; + } + } + lastFrame_[0] = value_; + } + + return value_; +} + +inline StkFrames& Envelope :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "Envelope::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i adsr_; + std::vector waves_; + SineWave vibrato_; + TwoZero twozero_; + unsigned int nOperators_; + StkFloat baseFrequency_; + std::vector ratios_; + std::vector gains_; + StkFloat modDepth_; + StkFloat control1_; + StkFloat control2_; + StkFloat fmGains_[100]; + StkFloat fmSusLevels_[16]; + StkFloat fmAttTimes_[32]; + +}; + +} // stk namespace + +#endif diff --git a/src/stk/include/FMVoices.h b/src/stk/include/FMVoices.h new file mode 100644 index 00000000..120d80a7 --- /dev/null +++ b/src/stk/include/FMVoices.h @@ -0,0 +1,135 @@ +#ifndef STK_FMVOICES_H +#define STK_FMVOICES_H + +#include "FM.h" + +namespace stk { + +/***************************************************/ +/*! \class FMVoices + \brief STK singing FM synthesis instrument. + + This class implements 3 carriers and a common + modulator, also referred to as algorithm 6 of + the TX81Z. + + \code + Algorithm 6 is : + /->1 -\ + 4-|-->2 - +-> Out + \->3 -/ + \endcode + + Control Change Numbers: + - Vowel = 2 + - Spectral Tilt = 4 + - LFO Speed = 11 + - LFO Depth = 1 + - ADSR 2 & 4 Target = 128 + + The basic Chowning/Stanford FM patent expired + in 1995, but there exist follow-on patents, + mostly assigned to Yamaha. If you are of the + type who should worry about this (making + money) worry away. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class FMVoices : public FM +{ + public: + //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ + FMVoices( void ); + + //! Class destructor. + ~FMVoices( void ); + + //! Set instrument parameters for a particular frequency. + void setFrequency( StkFloat frequency ); + + //! Start a note with the given frequency and amplitude. + void noteOn( StkFloat frequency, StkFloat amplitude ); + + //! Perform the control change specified by \e number and \e value (0.0 - 128.0). + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + + int currentVowel_; + StkFloat tilt_[3]; + StkFloat mods_[3]; +}; + +inline StkFloat FMVoices :: tick( unsigned int ) +{ + StkFloat temp, temp2; + + temp = gains_[3] * adsr_[3]->tick() * waves_[3]->tick(); + temp2 = vibrato_.tick() * modDepth_ * 0.1; + + waves_[0]->setFrequency(baseFrequency_ * (1.0 + temp2) * ratios_[0]); + waves_[1]->setFrequency(baseFrequency_ * (1.0 + temp2) * ratios_[1]); + waves_[2]->setFrequency(baseFrequency_ * (1.0 + temp2) * ratios_[2]); + waves_[3]->setFrequency(baseFrequency_ * (1.0 + temp2) * ratios_[3]); + + waves_[0]->addPhaseOffset(temp * mods_[0]); + waves_[1]->addPhaseOffset(temp * mods_[1]); + waves_[2]->addPhaseOffset(temp * mods_[2]); + waves_[3]->addPhaseOffset( twozero_.lastOut() ); + twozero_.tick( temp ); + temp = gains_[0] * tilt_[0] * adsr_[0]->tick() * waves_[0]->tick(); + temp += gains_[1] * tilt_[1] * adsr_[1]->tick() * waves_[1]->tick(); + temp += gains_[2] * tilt_[2] * adsr_[2]->tick() * waves_[2]->tick(); + + lastFrame_[0] = temp * 0.33; + return lastFrame_[0]; +} + +inline StkFrames& FMVoices :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "FMVoices::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; isetRate( fileSize_ * frequency / Stk::sampleRate() ); }; + + //! Increment the read pointer by \e time samples, modulo file size. + void addTime( StkFloat time ); + + //! Increment current read pointer by \e angle, relative to a looping frequency. + /*! + This function increments the read pointer based on the file + size and the current Stk::sampleRate. The \e anAngle value + is a multiple of file size. + */ + void addPhase( StkFloat angle ); + + //! Add a phase offset to the current read pointer. + /*! + This function determines a time offset based on the file + size and the current Stk::sampleRate. The \e angle value + is a multiple of file size. + */ + void addPhaseOffset( StkFloat angle ); + + //! Return the specified channel value of the last computed frame. + /*! + For multi-channel files, use the lastFrame() function to get + all values from the last computed frame. If no file data is + loaded, the returned value is 0.0. The \c channel argument must + be less than the number of channels in the file data (the first + channel is specified by 0). However, range checking is only + performed if _STK_DEBUG_ is defined during compilation, in which + case an out-of-range value will trigger an StkError exception. + */ + StkFloat lastOut( unsigned int channel = 0 ) { return FileWvIn::lastOut( channel ); }; + + //! Compute a sample frame and return the specified \c channel value. + /*! + For multi-channel files, use the lastFrame() function to get + all values from the computed frame. If no file data is loaded, + the returned value is 0.0. The \c channel argument must be less + than the number of channels in the file data (the first channel is + specified by 0). However, range checking is only performed if + _STK_DEBUG_ is defined during compilation, in which case an + out-of-range value will trigger an StkError exception. + */ + StkFloat tick( unsigned int channel = 0 ); + + //! Fill the StkFrames object with computed sample frames, starting at the specified channel and return the same reference. + /*! + The \c channel argument plus the number of output channels must + be less than the number of channels in the StkFrames argument (the + first channel is specified by 0). However, range checking is only + performed if _STK_DEBUG_ is defined during compilation, in which + case an out-of-range value will trigger an StkError exception. + */ + virtual StkFrames& tick( StkFrames& frames,unsigned int channel = 0 ); + + protected: + + StkFrames firstFrame_; + StkFloat phaseOffset_; + +}; + +} // stk namespace + +#endif diff --git a/src/stk/include/FileRead.h b/src/stk/include/FileRead.h new file mode 100644 index 00000000..e65b49c7 --- /dev/null +++ b/src/stk/include/FileRead.h @@ -0,0 +1,141 @@ +#ifndef STK_FILEREAD_H +#define STK_FILEREAD_H + +#include "Stk.h" + +namespace stk { + +/***************************************************/ +/*! \class FileRead + \brief STK audio file input class. + + This class provides input support for various + audio file formats. Multi-channel (>2) + soundfiles are supported. The file data is + returned via an external StkFrames object + passed to the read() function. This class + does not store its own copy of the file data, + rather the data is read directly from disk. + + FileRead currently supports uncompressed WAV, + AIFF/AIFC, SND (AU), MAT-file (Matlab), and + STK RAW file formats. Signed integer (8-, + 16-, 24-, and 32-bit) and floating-point (32- and + 64-bit) data types are supported. Compressed + data types are not supported. + + STK RAW files have no header and are assumed to + contain a monophonic stream of 16-bit signed + integers in big-endian byte order at a sample + rate of 22050 Hz. MAT-file data should be saved + in an array with each data channel filling a + matrix row. The sample rate for MAT-files should + be specified in a variable named "fs". If no + such variable is found, the sample rate is + assumed to be 44100 Hz. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class FileRead : public Stk +{ +public: + //! Default constructor. + FileRead( void ); + + //! Overloaded constructor that opens a file during instantiation. + /*! + An StkError will be thrown if the file is not found or its + format is unknown or unsupported. The optional arguments allow a + headerless file type to be supported. If \c typeRaw is false (the + default), the subsequent parameters are ignored. + */ + FileRead( std::string fileName, bool typeRaw = false, unsigned int nChannels = 1, + StkFormat format = STK_SINT16, StkFloat rate = 22050.0 ); + + //! Class destructor. + ~FileRead( void ); + + //! Open the specified file and determine its formatting. + /*! + An StkError will be thrown if the file is not found or its + format is unknown or unsupported. The optional arguments allow a + headerless file type to be supported. If \c typeRaw is false (the + default), the subsequent parameters are ignored. + */ + void open( std::string fileName, bool typeRaw = false, unsigned int nChannels = 1, + StkFormat format = STK_SINT16, StkFloat rate = 22050.0 ); + + //! If a file is open, close it. + void close( void ); + + //! Returns \e true if a file is currently open. + bool isOpen( void ); + + //! Return the file size in sample frames. + unsigned long fileSize( void ) const { return fileSize_; }; + + //! Return the number of audio channels in the file. + unsigned int channels( void ) const { return channels_; }; + + //! Return the data format of the file. + StkFormat format( void ) const { return dataType_; }; + + //! Return the file sample rate in Hz. + /*! + WAV, SND, and AIF formatted files specify a sample rate in + their headers. By definition, STK RAW files have a sample rate of + 22050 Hz. MAT-files are assumed to have a rate of 44100 Hz. + */ + StkFloat fileRate( void ) const { return fileRate_; }; + + //! Read sample frames from the file into an StkFrames object. + /*! + The number of sample frames to read will be determined from the + number of frames of the StkFrames argument. If this size is + larger than the available data in the file (given the file size + and starting frame index), the extra frames will be unaffected + (the StkFrames object will not be resized). Optional parameters + are provided to specify the starting sample frame within the file + (default = 0) and whether to normalize the data with respect to + fixed-point limits (default = true). An StkError will be thrown + if a file error occurs or if the number of channels in the + StkFrames argument is not equal to that in the file. + */ + void read( StkFrames& buffer, unsigned long startFrame = 0, bool doNormalize = true ); + +protected: + + // Get STK RAW file information. + bool getRawInfo( const char *fileName, unsigned int nChannels, + StkFormat format, StkFloat rate ); + + // Get WAV file header information. + bool getWavInfo( const char *fileName ); + + // Get SND (AU) file header information. + bool getSndInfo( const char *fileName ); + + // Get AIFF file header information. + bool getAifInfo( const char *fileName ); + + // Get MAT-file header information. + bool getMatInfo( const char *fileName ); + + // Helper function for MAT-file parsing. + bool findNextMatArray( SINT32 *chunkSize, SINT32 *rows, SINT32 *columns, SINT32 *nametype ); + + FILE *fd_; + bool byteswap_; + bool wavFile_; + unsigned long fileSize_; + unsigned long dataOffset_; + unsigned int channels_; + StkFormat dataType_; + StkFloat fileRate_; +}; + +} // stk namespace + +#endif diff --git a/src/stk/include/FileWrite.h b/src/stk/include/FileWrite.h new file mode 100644 index 00000000..6378339c --- /dev/null +++ b/src/stk/include/FileWrite.h @@ -0,0 +1,116 @@ +#ifndef STK_FILEWRITE_H +#define STK_FILEWRITE_H + +#include "Stk.h" + +namespace stk { + +/***************************************************/ +/*! \class FileWrite + \brief STK audio file output class. + + This class provides output support for various + audio file formats. + + FileWrite writes samples to an audio file. It supports + multi-channel data. + + FileWrite currently supports uncompressed WAV, AIFF, AIFC, SND + (AU), MAT-file (Matlab), and STK RAW file formats. Signed integer + (8-, 16-, 24-, and 32-bit) and floating- point (32- and 64-bit) + data types are supported. STK RAW files use 16-bit integers by + definition. MAT-files will always be written as 64-bit floats. + If a data type specification does not match the specified file + type, the data type will automatically be modified. Compressed + data types are not supported. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class FileWrite : public Stk +{ + public: + + typedef unsigned long FILE_TYPE; + + static const FILE_TYPE FILE_RAW; /*!< STK RAW file type. */ + static const FILE_TYPE FILE_WAV; /*!< WAV file type. */ + static const FILE_TYPE FILE_SND; /*!< SND (AU) file type. */ + static const FILE_TYPE FILE_AIF; /*!< AIFF file type. */ + static const FILE_TYPE FILE_MAT; /*!< Matlab MAT-file type. */ + + //! Default constructor. + FileWrite( void ); + + //! Overloaded constructor used to specify a file name, type, and data format with this object. + /*! + An StkError is thrown for invalid argument values or if an error occurs when initializing the output file. + */ + FileWrite( std::string fileName, unsigned int nChannels = 1, FILE_TYPE type = FILE_WAV, Stk::StkFormat format = STK_SINT16 ); + + //! Class destructor. + virtual ~FileWrite(); + + //! Create a file of the specified type and name and output samples to it in the given data format. + /*! + An StkError is thrown for invalid argument values or if an error occurs when initializing the output file. + */ + void open( std::string fileName, unsigned int nChannels = 1, + FileWrite::FILE_TYPE type = FILE_WAV, Stk::StkFormat format = STK_SINT16 ); + + //! If a file is open, write out samples in the queue and then close it. + void close( void ); + + //! Returns \e true if a file is currently open. + bool isOpen( void ); + + //! Write sample frames from the StkFrames object to the file. + /*! + An StkError will be thrown if the number of channels in the + StkFrames argument does not agree with the number of channels + specified when opening the file. + */ + void write( StkFrames& buffer ); + + protected: + + // Write STK RAW file header. + bool setRawFile( std::string fileName ); + + // Write WAV file header. + bool setWavFile( std::string fileName ); + + // Close WAV file, updating the header. + void closeWavFile( void ); + + // Write SND (AU) file header. + bool setSndFile( std::string fileName ); + + // Close SND file, updating the header. + void closeSndFile( void ); + + // Write AIFF file header. + bool setAifFile( std::string fileName ); + + // Close AIFF file, updating the header. + void closeAifFile( void ); + + // Write MAT-file header. + bool setMatFile( std::string fileName ); + + // Close MAT-file, updating the header. + void closeMatFile( void ); + + FILE *fd_; + FILE_TYPE fileType_; + StkFormat dataType_; + unsigned int channels_; + unsigned long frameCounter_; + bool byteswap_; + +}; + +} // stk namespace + +#endif diff --git a/src/stk/include/FileWvIn.h b/src/stk/include/FileWvIn.h new file mode 100644 index 00000000..208bafd3 --- /dev/null +++ b/src/stk/include/FileWvIn.h @@ -0,0 +1,208 @@ +#ifndef STK_FILEWVIN_H +#define STK_FILEWVIN_H + +#include "WvIn.h" +#include "FileRead.h" + +namespace stk { + +/***************************************************/ +/*! \class FileWvIn + \brief STK audio file input class. + + This class inherits from WvIn. It provides a "tick-level" + interface to the FileRead class. It also provides variable-rate + playback functionality. Audio file support is provided by the + FileRead class. Linear interpolation is used for fractional read + rates. + + FileWvIn supports multi-channel data. It is important to + distinguish the tick() method that computes a single frame (and + returns only the specified sample of a multi-channel frame) from + the overloaded one that takes an StkFrames object for + multi-channel and/or multi-frame data. + + FileWvIn will either load the entire content of an audio file into + local memory or incrementally read file data from disk in chunks. + This behavior is controlled by the optional constructor arguments + \e chunkThreshold and \e chunkSize. File sizes greater than \e + chunkThreshold (in sample frames) will be read incrementally in + chunks of \e chunkSize each (also in sample frames). + + For file data read completely into local memory, the \e doNormalize + flag can be used to normalize all values with respect to the maximum + absolute value of the data. + + If the file data format is fixed point, the flag \e doInt2FloatScaling + can be used to control whether the values are scaled with respect to + the corresponding fixed-point maximum. For example, if reading 16-bit + signed integers, the input values will be scaled by 1 / 32768.0. This + scaling will not happen for floating-point file data formats. + + When the file end is reached, subsequent calls to the tick() + functions return zeros and isFinished() returns \e true. + + See the FileRead class for a description of the supported audio + file formats. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class FileWvIn : public WvIn +{ +public: + //! Default constructor. + FileWvIn( unsigned long chunkThreshold = 1000000, unsigned long chunkSize = 1024 ); + + //! Overloaded constructor for file input. + /*! + An StkError will be thrown if the file is not found, its format is + unknown, or a read error occurs. + */ + FileWvIn( std::string fileName, bool raw = false, bool doNormalize = true, + unsigned long chunkThreshold = 1000000, unsigned long chunkSize = 1024, + bool doInt2FloatScaling = true ); + + //! Class destructor. + ~FileWvIn( void ); + + //! Open the specified file and load its data. + /*! + Data from a previously opened file will be overwritten by this + function. An StkError will be thrown if the file is not found, + its format is unknown, or a read error occurs. If the file length + is less than the chunkThreshold limit and \e doNormalize is true, + the file data will be normalized with respect to the maximum absolute + value of the data. If the \e doInt2FloatScaling flag is true and the + input data is fixed-point, a scaling will be applied with respect to + the fixed-point limits. + */ + virtual void openFile( std::string fileName, bool raw = false, bool doNormalize = true, bool doInt2FloatScaling = true ); + + //! Close a file if one is open. + virtual void closeFile( void ); + + //! Clear outputs and reset time (file) pointer to zero. + virtual void reset( void ); + + //! Normalize data to a maximum of +-1.0. + /*! + This function has no effect when data is incrementally loaded + from disk. + */ + virtual void normalize( void ); + + //! Normalize data to a maximum of \e +-peak. + /*! + This function has no effect when data is incrementally loaded + from disk. + */ + virtual void normalize( StkFloat peak ); + + //! Return the file size in sample frames. + virtual unsigned long getSize( void ) const { return fileSize_; }; + + //! Return the input file sample rate in Hz (not the data read rate). + /*! + WAV, SND, and AIF formatted files specify a sample rate in + their headers. STK RAW files have a sample rate of 22050 Hz + by definition. MAT-files are assumed to have a rate of 44100 Hz. + */ + virtual StkFloat getFileRate( void ) const { return data_.dataRate(); }; + + //! Query whether a file is open. + bool isOpen( void ) { return file_.isOpen(); }; + + //! Query whether reading is complete. + bool isFinished( void ) const { return finished_; }; + + //! Set the data read rate in samples. The rate can be negative. + /*! + If the rate value is negative, the data is read in reverse order. + */ + virtual void setRate( StkFloat rate ); + + //! Increment the read pointer by \e time samples. + /*! + Note that this function will not modify the interpolation flag status. + */ + virtual void addTime( StkFloat time ); + + //! Turn linear interpolation on/off. + /*! + Interpolation is automatically off when the read rate is + an integer value. If interpolation is turned off for a + fractional rate, the time index is truncated to an integer + value. + */ + void setInterpolate( bool doInterpolate ) { interpolate_ = doInterpolate; }; + + //! Return the specified channel value of the last computed frame. + /*! + If no file is loaded, the returned value is 0.0. The \c + channel argument must be less than the number of output channels, + which can be determined with the channelsOut() function (the first + channel is specified by 0). However, range checking is only + performed if _STK_DEBUG_ is defined during compilation, in which + case an out-of-range value will trigger an StkError exception. \sa + lastFrame() + */ + StkFloat lastOut( unsigned int channel = 0 ); + + //! Compute a sample frame and return the specified \c channel value. + /*! + For multi-channel files, use the lastFrame() function to get + all values from the computed frame. If no file data is loaded, + the returned value is 0.0. The \c channel argument must be less + than the number of channels in the file data (the first channel is + specified by 0). However, range checking is only performed if + _STK_DEBUG_ is defined during compilation, in which case an + out-of-range value will trigger an StkError exception. + */ + virtual StkFloat tick( unsigned int channel = 0 ); + + //! Fill the StkFrames object with computed sample frames, starting at the specified channel and return the same reference. + /*! + The \c channel argument plus the number of input channels must + be less than the number of channels in the StkFrames argument (the + first channel is specified by 0). However, range checking is only + performed if _STK_DEBUG_ is defined during compilation, in which + case an out-of-range value will trigger an StkError exception. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + +protected: + + void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); + + FileRead file_; + bool finished_; + bool interpolate_; + bool int2floatscaling_; + bool chunking_; + StkFloat time_; + StkFloat rate_; + unsigned long fileSize_; + unsigned long chunkThreshold_; + unsigned long chunkSize_; + long chunkPointer_; + +}; + +inline StkFloat FileWvIn :: lastOut( unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= data_.channels() ) { + oStream_ << "FileWvIn::lastOut(): channel argument and soundfile data are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + if ( finished_ ) return 0.0; + return lastFrame_[channel]; +} + +} // stk namespace + +#endif diff --git a/src/stk/include/FileWvOut.h b/src/stk/include/FileWvOut.h new file mode 100644 index 00000000..886c85f3 --- /dev/null +++ b/src/stk/include/FileWvOut.h @@ -0,0 +1,102 @@ +#ifndef STK_FILEWVOUT_H +#define STK_FILEWVOUT_H + +#include "WvOut.h" +#include "FileWrite.h" + +namespace stk { + +/***************************************************/ +/*! \class FileWvOut + \brief STK audio file output class. + + This class inherits from WvOut. It provides a "tick-level" + interface to the FileWrite class. + + FileWvOut writes samples to an audio file and supports + multi-channel data. It is important to distinguish the tick() + method that outputs a single sample to all channels in a sample + frame from the overloaded one that takes a reference to an + StkFrames object for multi-channel and/or multi-frame data. + + See the FileWrite class for a description of the supported audio + file formats. + + Currently, FileWvOut is non-interpolating and the output rate is + always Stk::sampleRate(). + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class FileWvOut : public WvOut +{ + public: + + //! Default constructor with optional output buffer size argument. + /*! + The output buffer size defines the number of frames that are + accumulated between writes to disk. + */ + FileWvOut( unsigned int bufferFrames = 1024 ); + + //! Overloaded constructor used to specify a file name, type, and data format with this object. + /*! + An StkError is thrown for invalid argument values or if an error occurs when initializing the output file. + */ + FileWvOut( std::string fileName, + unsigned int nChannels = 1, + FileWrite::FILE_TYPE type = FileWrite::FILE_WAV, + Stk::StkFormat format = STK_SINT16, + unsigned int bufferFrames = 1024 ); + + //! Class destructor. + virtual ~FileWvOut(); + + //! Open a new file with the specified parameters. + /*! + If a file was previously open, it will be closed. An StkError + will be thrown if any of the specified arguments are invalid or a + file error occurs during opening. + */ + void openFile( std::string fileName, + unsigned int nChannels, + FileWrite::FILE_TYPE type, + Stk::StkFormat format ); + + //! Close a file if one is open. + /*! + Any data remaining in the internal buffer will be written to + the file before closing. + */ + void closeFile( void ); + + //! Output a single sample to all channels in a sample frame. + /*! + An StkError is thrown if an output error occurs. + */ + void tick( const StkFloat sample ); + + //! Output the StkFrames data. + /*! + An StkError will be thrown if an output error occurs. An + StkError will also be thrown if _STK_DEBUG_ is defined during + compilation and there is an incompatability between the number of + channels in the FileWvOut object and that in the StkFrames object. + */ + void tick( const StkFrames& frames ); + + protected: + + void incrementFrame( void ); + + FileWrite file_; + unsigned int bufferFrames_; + unsigned int bufferIndex_; + unsigned int iData_; + +}; + +} // stk namespace + +#endif diff --git a/src/stk/include/Filter.h b/src/stk/include/Filter.h new file mode 100644 index 00000000..b267e200 --- /dev/null +++ b/src/stk/include/Filter.h @@ -0,0 +1,124 @@ +#ifndef STK_FILTER_H +#define STK_FILTER_H + +#include "Stk.h" +#include +#include + +namespace stk { + +/***************************************************/ +/*! \class Filter + \brief STK abstract filter class. + + This class provides limited common functionality for STK digital + filter subclasses. It is general enough to support both + monophonic and polyphonic input/output classes. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class Filter : public Stk +{ +public: + //! Class constructor. + Filter( void ) { gain_ = 1.0; channelsIn_ = 1; lastFrame_.resize( 1, 1, 0.0 ); }; + + //! Return the number of input channels for the class. + unsigned int channelsIn( void ) const { return channelsIn_; }; + + //! Return the number of output channels for the class. + unsigned int channelsOut( void ) const { return lastFrame_.channels(); }; + + //! Clears all internal states of the filter. + virtual void clear( void ); + + //! Set the filter gain. + /*! + The gain is applied at the filter input and does not affect the + coefficient values. The default gain value is 1.0. + */ + void setGain( StkFloat gain ) { gain_ = gain; }; + + //! Return the current filter gain. + StkFloat getGain( void ) const { return gain_; }; + + //! Return the filter phase delay at the specified frequency. + /*! + Note that the phase delay calculation accounts for the filter + gain. The frequency value should be greater than 0.0 and less + than or equal to one-half the sample rate. + */ + StkFloat phaseDelay( StkFloat frequency ); + + //! Return an StkFrames reference to the last output sample frame. + const StkFrames& lastFrame( void ) const { return lastFrame_; }; + + //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. + /*! + The StkFrames argument reference is returned. The \c channel + argument must be less than the number of channels in the + StkFrames argument (the first channel is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ) = 0; + +protected: + + StkFloat gain_; + unsigned int channelsIn_; + StkFrames lastFrame_; + + std::vector b_; + std::vector a_; + StkFrames outputs_; + StkFrames inputs_; + +}; + +inline void Filter :: clear( void ) +{ + unsigned int i; + for ( i=0; i 0.5 * Stk::sampleRate() ) { + oStream_ << "Filter::phaseDelay: argument (" << frequency << ") is out of range!"; + handleError( StkError::WARNING ); return 0.0; + } + + StkFloat omegaT = 2 * PI * frequency / Stk::sampleRate(); + StkFloat real = 0.0, imag = 0.0; + for ( unsigned int i=0; i &coefficients ); + + //! Class destructor. + ~Fir( void ); + + //! Set filter coefficients. + /*! + An StkError can be thrown if the coefficient vector size is + zero. The internal state of the filter is not cleared unless the + \e clearState flag is \c true. + */ + void setCoefficients( std::vector &coefficients, bool clearState = false ); + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Input one sample to the filter and return one output. + StkFloat tick( StkFloat input ); + + //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. + /*! + The StkFrames argument reference is returned. The \c channel + argument must be less than the number of channels in the + StkFrames argument (the first channel is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the filter and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. Each channel + argument must be less than the number of channels in the + corresponding StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); + +protected: + +}; + +inline StkFloat Fir :: tick( StkFloat input ) +{ + lastFrame_[0] = 0.0; + inputs_[0] = gain_ * input; + + for ( unsigned int i=(unsigned int)(b_.size())-1; i>0; i-- ) { + lastFrame_[0] += b_[i] * inputs_[i]; + inputs_[i] = inputs_[i-1]; + } + lastFrame_[0] += b_[0] * inputs_[0]; + + return lastFrame_[0]; +} + +inline StkFrames& Fir :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "Fir::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int i, hop = frames.channels(); + for ( unsigned int j=0; j0; i-- ) { + *samples += b_[i] * inputs_[i]; + inputs_[i] = inputs_[i-1]; + } + *samples += b_[0] * inputs_[0]; + } + + lastFrame_[0] = *(samples-hop); + return frames; +} + +inline StkFrames& Fir :: tick( StkFrames& iFrames, StkFrames& oFrames, unsigned int iChannel, unsigned int oChannel ) +{ +#if defined(_STK_DEBUG_) + if ( iChannel >= iFrames.channels() || oChannel >= oFrames.channels() ) { + oStream_ << "Fir::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int i, iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int j=0; j0; i-- ) { + *oSamples += b_[i] * inputs_[i]; + inputs_[i] = inputs_[i-1]; + } + *oSamples += b_[0] * inputs_[0]; + } + + lastFrame_[0] = *(oSamples-oHop); + return iFrames; +} + +} // stk namespace + +#endif diff --git a/src/stk/include/Flute.h b/src/stk/include/Flute.h new file mode 100644 index 00000000..8a27181d --- /dev/null +++ b/src/stk/include/Flute.h @@ -0,0 +1,167 @@ +#ifndef STK_FLUTE_H +#define STK_FLUTE_H + +#include "Instrmnt.h" +#include "JetTable.h" +#include "DelayL.h" +#include "OnePole.h" +#include "PoleZero.h" +#include "Noise.h" +#include "ADSR.h" +#include "SineWave.h" + +namespace stk { + +/***************************************************/ +/*! \class Flute + \brief STK flute physical model class. + + This class implements a simple flute + physical model, as discussed by Karjalainen, + Smith, Waryznyk, etc. The jet model uses + a polynomial, a la Cook. + + This is a digital waveguide model, making its + use possibly subject to patents held by Stanford + University, Yamaha, and others. + + Control Change Numbers: + - Jet Delay = 2 + - Noise Gain = 4 + - Vibrato Frequency = 11 + - Vibrato Gain = 1 + - Breath Pressure = 128 + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class Flute : public Instrmnt +{ + public: + //! Class constructor, taking the lowest desired playing frequency. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ + Flute( StkFloat lowestFrequency ); + + //! Class destructor. + ~Flute( void ); + + //! Reset and clear all internal state. + void clear( void ); + + //! Set instrument parameters for a particular frequency. + void setFrequency( StkFloat frequency ); + + //! Set the reflection coefficient for the jet delay (-1.0 - 1.0). + void setJetReflection( StkFloat coefficient ) { jetReflection_ = coefficient; }; + + //! Set the reflection coefficient for the air column delay (-1.0 - 1.0). + void setEndReflection( StkFloat coefficient ) { endReflection_ = coefficient; }; + + //! Set the length of the jet delay in terms of a ratio of jet delay to air column delay lengths. + void setJetDelay( StkFloat aRatio ); + + //! Apply breath velocity to instrument with given amplitude and rate of increase. + void startBlowing( StkFloat amplitude, StkFloat rate ); + + //! Decrease breath velocity with given rate of decrease. + void stopBlowing( StkFloat rate ); + + //! Start a note with the given frequency and amplitude. + void noteOn( StkFloat frequency, StkFloat amplitude ); + + //! Stop a note with the given amplitude (speed of decay). + void noteOff( StkFloat amplitude ); + + //! Perform the control change specified by \e number and \e value (0.0 - 128.0). + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + + DelayL jetDelay_; + DelayL boreDelay_; + JetTable jetTable_; + OnePole filter_; + PoleZero dcBlock_; + Noise noise_; + ADSR adsr_; + SineWave vibrato_; + + StkFloat lastFrequency_; + StkFloat maxPressure_; + StkFloat jetReflection_; + StkFloat endReflection_; + StkFloat noiseGain_; + StkFloat vibratoGain_; + StkFloat outputGain_; + StkFloat jetRatio_; + +}; + +inline StkFloat Flute :: tick( unsigned int ) +{ + StkFloat pressureDiff; + StkFloat breathPressure; + + // Calculate the breath pressure (envelope + noise + vibrato) + breathPressure = maxPressure_ * adsr_.tick(); + breathPressure += breathPressure * ( noiseGain_ * noise_.tick() + vibratoGain_ * vibrato_.tick() ); + + StkFloat temp = -filter_.tick( boreDelay_.lastOut() ); + //temp = dcBlock_.tick( temp ); // Block DC on reflection. + + pressureDiff = breathPressure - (jetReflection_ * temp); + pressureDiff = jetDelay_.tick( pressureDiff ); + //pressureDiff = jetTable_.tick( pressureDiff ) + (endReflection_ * temp); + pressureDiff = dcBlock_.tick(jetTable_.tick( pressureDiff )) + (endReflection_ * temp); // moved the DC blocker to after the jet non-linearity (GPS, 29 Jan. 2020) + lastFrame_[0] = (StkFloat) 0.3 * boreDelay_.tick( pressureDiff ); + + lastFrame_[0] *= outputGain_; + return lastFrame_[0]; +} + +inline StkFrames& Flute :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "Flute::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i= 1.0. The \e frequency value should be between zero and + half the sample rate. + */ + void setResonance( StkFloat frequency, StkFloat radius ); + + //! Set both the current and target resonance parameters. + void setStates( StkFloat frequency, StkFloat radius, StkFloat gain = 1.0 ); + + //! Set target resonance parameters. + void setTargets( StkFloat frequency, StkFloat radius, StkFloat gain = 1.0 ); + + //! Set the sweep rate (between 0.0 - 1.0). + /*! + The formant parameters are varied in increments of the + sweep rate between their current and target values. + A sweep rate of 1.0 will produce an immediate change in + resonance parameters from their current values to the + target values. A sweep rate of 0.0 will produce no + change in resonance parameters. + */ + void setSweepRate( StkFloat rate ); + + //! Set the sweep rate in terms of a time value in seconds. + /*! + This method adjusts the sweep rate based on a + given time for the formant parameters to reach + their target values. + */ + void setSweepTime( StkFloat time ); + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Input one sample to the filter and return a reference to one output. + StkFloat tick( StkFloat input ); + + //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. + /*! + The StkFrames argument reference is returned. The \c channel + argument must be less than the number of channels in the + StkFrames argument (the first channel is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the filter and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. Each channel + argument must be less than the number of channels in the + corresponding StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); + + protected: + + virtual void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); + + bool dirty_; + StkFloat frequency_; + StkFloat radius_; + StkFloat startFrequency_; + StkFloat startRadius_; + StkFloat startGain_; + StkFloat targetFrequency_; + StkFloat targetRadius_; + StkFloat targetGain_; + StkFloat deltaFrequency_; + StkFloat deltaRadius_; + StkFloat deltaGain_; + StkFloat sweepState_; + StkFloat sweepRate_; + +}; + +inline StkFloat FormSwep :: tick( StkFloat input ) +{ + if ( dirty_ ) { + sweepState_ += sweepRate_; + if ( sweepState_ >= 1.0 ) { + sweepState_ = 1.0; + dirty_ = false; + radius_ = targetRadius_; + frequency_ = targetFrequency_; + gain_ = targetGain_; + } + else { + radius_ = startRadius_ + (deltaRadius_ * sweepState_); + frequency_ = startFrequency_ + (deltaFrequency_ * sweepState_); + gain_ = startGain_ + (deltaGain_ * sweepState_); + } + this->setResonance( frequency_, radius_ ); + } + + inputs_[0] = gain_ * input; + lastFrame_[0] = b_[0] * inputs_[0] + b_[1] * inputs_[1] + b_[2] * inputs_[2]; + lastFrame_[0] -= a_[2] * outputs_[2] + a_[1] * outputs_[1]; + inputs_[2] = inputs_[1]; + inputs_[1] = inputs_[0]; + outputs_[2] = outputs_[1]; + outputs_[1] = lastFrame_[0]; + + return lastFrame_[0]; +} + +inline StkFrames& FormSwep :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "FormSwep::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + oStream_ << "FormSwep::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i 1 ) { + oStream_ << "FreeVerb::lastOut(): channel argument must be less than 2!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + return lastFrame_[channel]; +} + +inline StkFloat FreeVerb::tick( StkFloat inputL, StkFloat inputR, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel > 1 ) { + oStream_ << "FreeVerb::tick(): channel argument must be less than 2!"; + handleError(StkError::FUNCTION_ARGUMENT); + } +#endif + + StkFloat fInput = (inputL + inputR) * gain_; + StkFloat outL = 0.0; + StkFloat outR = 0.0; + + // Parallel LBCF filters + for ( int i = 0; i < nCombs; i++ ) { + // Left channel + //StkFloat yn = fInput + (roomSize_ * FreeVerb::undenormalize(combLPL_[i].tick(FreeVerb::undenormalize(combDelayL_[i].nextOut())))); + StkFloat yn = fInput + (roomSize_ * combLPL_[i].tick( combDelayL_[i].nextOut() ) ); + combDelayL_[i].tick(yn); + outL += yn; + + // Right channel + //yn = fInput + (roomSize_ * FreeVerb::undenormalize(combLPR_[i].tick(FreeVerb::undenormalize(combDelayR_[i].nextOut())))); + yn = fInput + (roomSize_ * combLPR_[i].tick( combDelayR_[i].nextOut() ) ); + combDelayR_[i].tick(yn); + outR += yn; + } + + // Series allpass filters + for ( int i = 0; i < nAllpasses; i++ ) { + // Left channel + //StkFloat vn_m = FreeVerb::undenormalize(allPassDelayL_[i].nextOut()); + StkFloat vn_m = allPassDelayL_[i].nextOut(); + StkFloat vn = outL + (g_ * vn_m); + allPassDelayL_[i].tick(vn); + + // calculate output + outL = -vn + (1.0 + g_)*vn_m; + + // Right channel + //vn_m = FreeVerb::undenormalize(allPassDelayR_[i].nextOut()); + vn_m = allPassDelayR_[i].nextOut(); + vn = outR + (g_ * vn_m); + allPassDelayR_[i].tick(vn); + + // calculate output + outR = -vn + (1.0 + g_)*vn_m; + } + + // Mix output + lastFrame_[0] = outL*wet1_ + outR*wet2_ + inputL*dry_; + lastFrame_[1] = outR*wet1_ + outL*wet2_ + inputR*dry_; + + /* + // Hard limiter ... there's not much else we can do at this point + if ( lastFrame_[0] >= 1.0 ) { + lastFrame_[0] = 0.9999; + } + if ( lastFrame_[0] <= -1.0 ) { + lastFrame_[0] = -0.9999; + } + if ( lastFrame_[1] >= 1.0 ) { + lastFrame_[1] = 0.9999; + } + if ( lastFrame_[1] <= -1.0 ) { + lastFrame_[1] = -0.9999; + } + */ + + return lastFrame_[channel]; +} + +} + +#endif diff --git a/src/stk/include/Function.h b/src/stk/include/Function.h new file mode 100644 index 00000000..87aa5679 --- /dev/null +++ b/src/stk/include/Function.h @@ -0,0 +1,41 @@ +#ifndef STK_FUNCTION_H +#define STK_FUNCTION_H + +#include "Stk.h" + +namespace stk { + +/***************************************************/ +/*! \class Function + \brief STK abstract function parent class. + + This class provides common functionality for STK classes that + implement tables or other types of input to output function + mappings. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class Function : public Stk +{ + public: + //! Class constructor. + Function( void ) { lastFrame_.resize( 1, 1, 0.0 ); }; + + //! Return the last computed output sample. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Take one sample input and compute one sample of output. + virtual StkFloat tick( StkFloat input ) = 0; + + protected: + + StkFrames lastFrame_; + +}; + +} // stk namespace + +#endif + diff --git a/src/stk/include/Generator.h b/src/stk/include/Generator.h new file mode 100644 index 00000000..ef64c9b0 --- /dev/null +++ b/src/stk/include/Generator.h @@ -0,0 +1,50 @@ +#ifndef STK_GENERATOR_H +#define STK_GENERATOR_H + +#include "Stk.h" + +namespace stk { + +/***************************************************/ +/*! \class Generator + \brief STK abstract unit generator parent class. + + This class provides limited common functionality for STK unit + generator sample-source subclasses. It is general enough to + support both monophonic and polyphonic output classes. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class Generator : public Stk +{ + public: + + //! Class constructor. + Generator( void ) { lastFrame_.resize( 1, 1, 0.0 ); }; + + //! Return the number of output channels for the class. + unsigned int channelsOut( void ) const { return lastFrame_.channels(); }; + + //! Return an StkFrames reference to the last output sample frame. + const StkFrames& lastFrame( void ) const { return lastFrame_; }; + + //! Fill the StkFrames object with computed sample frames, starting at the specified channel. + /*! + The \c channel argument plus the number of output channels must + be less than the number of channels in the StkFrames argument (the + first channel is specified by 0). However, range checking is only + performed if _STK_DEBUG_ is defined during compilation, in which + case an out-of-range value will trigger an StkError exception. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ) = 0; + + protected: + + StkFrames lastFrame_; +}; + +} // stk namespace + +#endif diff --git a/src/stk/include/Granulate.h b/src/stk/include/Granulate.h new file mode 100644 index 00000000..cbaf4457 --- /dev/null +++ b/src/stk/include/Granulate.h @@ -0,0 +1,209 @@ +#ifndef STK_GRANULATE_H +#define STK_GRANULATE_H + +#include +#include "Generator.h" +#include "Envelope.h" +#include "Noise.h" + +namespace stk { + +/***************************************************/ +/*! \class Granulate + \brief STK granular synthesis class. + + This class implements a real-time granular synthesis algorithm + that operates on an input soundfile. Multi-channel files are + supported. Various functions are provided to allow control over + voice and grain parameters. + + The functionality of this class is based on the program MacPod by + Chris Rolfe and Damian Keller, though there are likely to be a + number of differences in the actual implementation. + + by Gary Scavone, 2005--2021. +*/ +/***************************************************/ + +class Granulate: public Generator +{ + public: + //! Default constructor. + Granulate( void ); + + //! Constructor taking input audio file and number of voices arguments. + Granulate( unsigned int nVoices, std::string fileName, bool typeRaw = false ); + + //! Class destructor. + ~Granulate( void ); + + //! Load a monophonic soundfile to be "granulated". + /*! + An StkError will be thrown if the file is not found, its format + is unknown or unsupported, or the file has more than one channel. + */ + void openFile( std::string fileName, bool typeRaw = false ); + + //! Reset the file pointer and all existing grains to the file start. + /*! + Multiple grains are offset from one another in time by grain + duration / nVoices. + */ + void reset( void ); + + //! Set the number of simultaneous grain "voices" to use. + /*! + Multiple grains are offset from one another in time by grain + duration / nVoices. For this reason, it is best to set the grain + parameters before calling this function (during initialization). + */ + void setVoices( unsigned int nVoices = 1 ); + + //! Set the stretch factor used for grain playback (1 - 1000). + /*! + Granular synthesis allows for time-stetching without affecting + the original pitch of a sound. A stretch factor of 4 will produce + a resulting sound of length 4 times the orignal sound. The + default parameter of 1 produces no stretching. + */ + void setStretch( unsigned int stretchFactor = 1 ); + + //! Set global grain parameters used to determine individual grain settings. + /*! + Each grain is defined as having a length of \e duration + milliseconds which must be greater than zero. For values of \e + rampPercent (0 - 100) greater than zero, a linear envelope will be + applied to each grain. If \e rampPercent = 100, the resultant + grain "window" is triangular while \e rampPercent = 50 produces a + trapezoidal window. In addition, each grain can have a time delay + of length \e delay and a grain pointer increment of length \e + offset, which can be negative, before the next ramp onset (in + milliseconds). The \e offset parameter controls grain pointer + jumps between enveloped grain segments, while the \e delay + parameter causes grain calculations to pause between grains. The + actual values calculated for each grain will be randomized by a + factor set using the setRandomFactor() function. + */ + void setGrainParameters( unsigned int duration = 30, unsigned int rampPercent = 50, + int offset = 0, unsigned int delay = 0 ); + + //! This factor is used when setting individual grain parameters (0.0 - 1.0). + /*! + This random factor is applied when all grain state durations + are calculated. If set to 0.0, no randomness occurs. When + randomness = 1.0, a grain segment of length \e duration will be + randomly augmented by up to +- \e duration seconds (i.e., a 30 + millisecond length will be augmented by an extra length of up to + +30 or -30 milliseconds). + */ + void setRandomFactor( StkFloat randomness = 0.1 ); + + //! Return the specified channel value of the last computed frame. + /*! + The \c channel argument must be less than the number of output + channels, which can be determined with the channelsOut() function + (the first channel is specified by 0). However, range checking is + only performed if _STK_DEBUG_ is defined during compilation, in + which case an out-of-range value will trigger an StkError + exception. \sa lastFrame() + */ + StkFloat lastOut( unsigned int channel = 0 ); + + //! Compute one sample frame and return the specified \c channel value. + StkFloat tick( unsigned int channel = 0 ); + + //! Fill the StkFrames object with computed sample frames, starting at the specified channel. + /*! + The \c channel argument plus the number of output channels must + be less than the number of channels in the StkFrames argument (the + first channel is specified by 0). However, range checking is only + performed if _STK_DEBUG_ is defined during compilation, in which + case an out-of-range value will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + enum GrainState { + GRAIN_STOPPED, + GRAIN_FADEIN, + GRAIN_SUSTAIN, + GRAIN_FADEOUT + }; + + protected: + + struct Grain { + StkFloat eScaler; + StkFloat eRate; + unsigned long attackCount; + unsigned long sustainCount; + unsigned long decayCount; + unsigned long delayCount; + unsigned long counter; + //unsigned long pointer; + StkFloat pointer; + unsigned long startPointer; + unsigned int repeats; + GrainState state; + + // Default constructor. + Grain() + :eScaler(0.0), eRate(0.0), attackCount(0), sustainCount(0), decayCount(0), + delayCount(0), counter(0), pointer(0), startPointer(0), repeats(0), state(GRAIN_STOPPED) {} + }; + + void calculateGrain( Granulate::Grain& grain ); + + StkFrames data_; + std::vector grains_; + Noise noise; + //long gPointer_; + StkFloat gPointer_; + + // Global grain parameters. + unsigned int gDuration_; + unsigned int gRampPercent_; + unsigned int gDelay_; + unsigned int gStretch_; + unsigned int stretchCounter_; + int gOffset_; + StkFloat gRandomFactor_; + StkFloat gain_; + +}; + +inline StkFloat Granulate :: lastOut( unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= lastFrame_.channels() ) { + oStream_ << "Granulate::lastOut(): channel argument is invalid!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + return lastFrame_[channel]; +} + +inline StkFrames& Granulate :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "Granulate::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + for ( unsigned int i=0; i strings_; + std::vector< int > stringState_; // 0 = off, 1 = decaying, 2 = on + std::vector< unsigned int > decayCounter_; + std::vector< unsigned int > filePointer_; + std::vector< StkFloat > pluckGains_; + + OnePole pickFilter_; + OnePole couplingFilter_; + StkFloat couplingGain_; + StkFrames excitation_; + StkFrames lastFrame_; +}; + +// NOTE: It is not possible to implement the Smith coupled string model here because the Twang class does +// not currently offer the chance to have access to a traveling-wave component. Thus, the coupling +// implemented here is approximate. +inline StkFloat Guitar :: tick( StkFloat input ) +{ + StkFloat temp, output = 0.0; + lastFrame_[0] = couplingGain_ * couplingFilter_.tick( lastFrame_[0] ) / strings_.size(); + for ( unsigned int i=0; i 0.2 ) + temp += pluckGains_[i] * excitation_[filePointer_[i]++]; + temp += lastFrame_[0]; // bridge coupling + output += strings_[i].tick( temp ); + // Check if string energy has decayed sufficiently to turn it off. + if ( stringState_[i] == 1 ) { + if ( fabs( strings_[i].lastOut() ) < 0.001 ) decayCounter_[i]++; + else decayCounter_[i] = 0; + if ( decayCounter_[i] > (unsigned int) floor( 0.1 * Stk::sampleRate() ) ) { + stringState_[i] = 0; + decayCounter_[i] = 0; + } + } + } + } + + return lastFrame_[0] = output; +} + +inline StkFrames& Guitar :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "Guitar::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + oStream_ << "Guitar::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i2-- + -->1-->Out + \endcode + + Control Change Numbers: + - Total Modulator Index = 2 + - Modulator Crossfade = 4 + - LFO Speed = 11 + - LFO Depth = 1 + - ADSR 2 & 4 Target = 128 + + The basic Chowning/Stanford FM patent expired + in 1995, but there exist follow-on patents, + mostly assigned to Yamaha. If you are of the + type who should worry about this (making + money) worry away. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class HevyMetl : public FM +{ + public: + //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ + HevyMetl( void ); + + //! Class destructor. + ~HevyMetl( void ); + + //! Start a note with the given frequency and amplitude. + void noteOn( StkFloat frequency, StkFloat amplitude ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + +}; + +inline StkFloat HevyMetl :: tick( unsigned int ) +{ + StkFloat temp; + + temp = vibrato_.tick() * modDepth_ * 0.2; + waves_[0]->setFrequency(baseFrequency_ * (1.0 + temp) * ratios_[0]); + waves_[1]->setFrequency(baseFrequency_ * (1.0 + temp) * ratios_[1]); + waves_[2]->setFrequency(baseFrequency_ * (1.0 + temp) * ratios_[2]); + waves_[3]->setFrequency(baseFrequency_ * (1.0 + temp) * ratios_[3]); + + temp = gains_[2] * adsr_[2]->tick() * waves_[2]->tick(); + waves_[1]->addPhaseOffset( temp ); + + waves_[3]->addPhaseOffset( twozero_.lastOut() ); + temp = (1.0 - (control2_ * 0.5)) * gains_[3] * adsr_[3]->tick() * waves_[3]->tick(); + twozero_.tick(temp); + + temp += control2_ * 0.5 * gains_[1] * adsr_[1]->tick() * waves_[1]->tick(); + temp = temp * control1_; + + waves_[0]->addPhaseOffset( temp ); + temp = gains_[0] * adsr_[0]->tick() * waves_[0]->tick(); + + lastFrame_[0] = temp * 0.5; + return lastFrame_[0]; +} + +inline StkFrames& HevyMetl :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "HevyMetl::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i &bCoefficients, std::vector &aCoefficients ); + + //! Class destructor. + ~Iir( void ); + + //! Set filter coefficients. + /*! + An StkError can be thrown if either of the coefficient vector + sizes is zero, or if the a[0] coefficient is equal to zero. If + a[0] is not equal to 1, the filter coeffcients are normalized by + a[0]. The internal state of the filter is not cleared unless the + \e clearState flag is \c true. + */ + void setCoefficients( std::vector &bCoefficients, std::vector &aCoefficients, bool clearState = false ); + + //! Set numerator coefficients. + /*! + An StkError can be thrown if coefficient vector is empty. Any + previously set denominator coefficients are left unaffected. Note + that the default constructor sets the single denominator + coefficient a[0] to 1.0. The internal state of the filter is not + cleared unless the \e clearState flag is \c true. + */ + void setNumerator( std::vector &bCoefficients, bool clearState = false ); + + //! Set denominator coefficients. + /*! + An StkError can be thrown if the coefficient vector is empty or + if the a[0] coefficient is equal to zero. Previously set + numerator coefficients are unaffected unless a[0] is not equal to + 1, in which case all coeffcients are normalized by a[0]. Note + that the default constructor sets the single numerator coefficient + b[0] to 1.0. The internal state of the filter is not cleared + unless the \e clearState flag is \c true. + */ + void setDenominator( std::vector &aCoefficients, bool clearState = false ); + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Input one sample to the filter and return one output. + StkFloat tick( StkFloat input ); + + //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. + /*! + The StkFrames argument reference is returned. The \c channel + argument must be less than the number of channels in the + StkFrames argument (the first channel is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the filter and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. Each channel + argument must be less than the number of channels in the + corresponding StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); + +protected: + +}; + +inline StkFloat Iir :: tick( StkFloat input ) +{ + size_t i; + + outputs_[0] = 0.0; + inputs_[0] = gain_ * input; + for ( i=b_.size()-1; i>0; i-- ) { + outputs_[0] += b_[i] * inputs_[i]; + inputs_[i] = inputs_[i-1]; + } + outputs_[0] += b_[0] * inputs_[0]; + + for ( i=a_.size()-1; i>0; i-- ) { + outputs_[0] += -a_[i] * outputs_[i]; + outputs_[i] = outputs_[i-1]; + } + + lastFrame_[0] = outputs_[0]; + return lastFrame_[0]; +} + +inline StkFrames& Iir :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "Iir::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + size_t i; + unsigned int hop = frames.channels(); + for ( unsigned int j=0; j0; i-- ) { + outputs_[0] += b_[i] * inputs_[i]; + inputs_[i] = inputs_[i-1]; + } + outputs_[0] += b_[0] * inputs_[0]; + + for ( i=a_.size()-1; i>0; i-- ) { + outputs_[0] += -a_[i] * outputs_[i]; + outputs_[i] = outputs_[i-1]; + } + + *samples = outputs_[0]; + } + + lastFrame_[0] = *(samples-hop); + return frames; +} + +inline StkFrames& Iir :: tick( StkFrames& iFrames, StkFrames& oFrames, unsigned int iChannel, unsigned int oChannel ) +{ +#if defined(_STK_DEBUG_) + if ( iChannel >= iFrames.channels() || oChannel >= oFrames.channels() ) { + oStream_ << "Iir::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + size_t i; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int j=0; j0; i-- ) { + outputs_[0] += b_[i] * inputs_[i]; + inputs_[i] = inputs_[i-1]; + } + outputs_[0] += b_[0] * inputs_[0]; + + for ( i=a_.size()-1; i>0; i-- ) { + outputs_[0] += -a_[i] * outputs_[i]; + outputs_[i] = outputs_[i-1]; + } + + *oSamples = outputs_[0]; + } + + lastFrame_[0] = *(oSamples-oHop); + return iFrames; +} + +} // stk namespace + +#endif diff --git a/src/stk/include/Instrmnt.h b/src/stk/include/Instrmnt.h new file mode 100644 index 00000000..c15410c5 --- /dev/null +++ b/src/stk/include/Instrmnt.h @@ -0,0 +1,108 @@ +#ifndef STK_INSTRMNT_H +#define STK_INSTRMNT_H + +#include "Stk.h" + +namespace stk { + +/***************************************************/ +/*! \class Instrmnt + \brief STK instrument abstract base class. + + This class provides a common interface for + all STK instruments. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class Instrmnt : public Stk +{ + public: + //! Class constructor. + Instrmnt( void ) { lastFrame_.resize( 1, 1, 0.0 ); }; + + //! Reset and clear all internal state (for subclasses). + /*! + Not all subclasses implement a clear() function. + */ + virtual void clear( void ) {}; + + //! Start a note with the given frequency and amplitude. + virtual void noteOn( StkFloat frequency, StkFloat amplitude ) = 0; + + //! Stop a note with the given amplitude (speed of decay). + virtual void noteOff( StkFloat amplitude ) = 0; + + //! Set instrument parameters for a particular frequency. + virtual void setFrequency( StkFloat frequency ); + + //! Perform the control change specified by \e number and \e value (0.0 - 128.0). + virtual void controlChange(int number, StkFloat value); + + //! Return the number of output channels for the class. + unsigned int channelsOut( void ) const { return lastFrame_.channels(); }; + + //! Return an StkFrames reference to the last output sample frame. + const StkFrames& lastFrame( void ) const { return lastFrame_; }; + + //! Return the specified channel value of the last computed frame. + /*! + The \c channel argument must be less than the number of output + channels, which can be determined with the channelsOut() function + (the first channel is specified by 0). However, range checking is + only performed if _STK_DEBUG_ is defined during compilation, in + which case an out-of-range value will trigger an StkError + exception. \sa lastFrame() + */ + StkFloat lastOut( unsigned int channel = 0 ); + + //! Compute one sample frame and return the specified \c channel value. + /*! + For monophonic instruments, the \c channel argument is ignored. + */ + virtual StkFloat tick( unsigned int channel = 0 ) = 0; + + //! Fill the StkFrames object with computed sample frames, starting at the specified channel. + /*! + The \c channel argument plus the number of output channels must + be less than the number of channels in the StkFrames argument (the + first channel is specified by 0). However, range checking is only + performed if _STK_DEBUG_ is defined during compilation, in which + case an out-of-range value will trigger an StkError exception. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ) = 0; + + protected: + + StkFrames lastFrame_; + +}; + +inline void Instrmnt :: setFrequency( StkFloat frequency ) +{ + oStream_ << "Instrmnt::setFrequency: virtual setFrequency function call!"; + handleError( StkError::WARNING ); +} + +inline StkFloat Instrmnt :: lastOut( unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= lastFrame_.channels() ) { + oStream_ << "Instrmnt::lastOut(): channel argument is invalid!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + return lastFrame_[channel]; +} + +inline void Instrmnt :: controlChange( int number, StkFloat value ) +{ + oStream_ << "Instrmnt::controlChange: virtual function call!"; + handleError( StkError::WARNING ); +} + +} // stk namespace + +#endif diff --git a/src/stk/include/JCRev.h b/src/stk/include/JCRev.h new file mode 100644 index 00000000..7acd0eb3 --- /dev/null +++ b/src/stk/include/JCRev.h @@ -0,0 +1,166 @@ +#ifndef STK_JCREV_H +#define STK_JCREV_H + +#include "Effect.h" +#include "Delay.h" +#include "OnePole.h" + +namespace stk { + +/***************************************************/ +/*! \class JCRev + \brief John Chowning's reverberator class. + + This class takes a monophonic input signal and + produces a stereo output signal. It is derived + from the CLM JCRev function, which is based on + the use of networks of simple allpass and comb + delay filters. This class implements three + series allpass units, followed by four parallel + comb filters, and two decorrelation delay lines + in parallel at the output. + + Although not in the original JC reverberator, + one-pole lowpass filters have been added inside + the feedback comb filters. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class JCRev : public Effect +{ + public: + //! Class constructor taking a T60 decay time argument (one second default value). + JCRev( StkFloat T60 = 1.0 ); + + //! Reset and clear all internal state. + void clear( void ); + + //! Set the reverberation T60 decay time. + void setT60( StkFloat T60 ); + + //! Return the specified channel value of the last computed stereo frame. + /*! + Use the lastFrame() function to get both values of the last + computed stereo frame. The \c channel argument must be 0 or 1 + (the first channel is specified by 0). However, range checking is + only performed if _STK_DEBUG_ is defined during compilation, in + which case an out-of-range value will trigger an StkError + exception. + */ + StkFloat lastOut( unsigned int channel = 0 ); + + //! Input one sample to the effect and return the specified \c channel value of the computed stereo frame. + /*! + Use the lastFrame() function to get both values of the computed + stereo output frame. The \c channel argument must be 0 or 1 (the + first channel is specified by 0). However, range checking is only + performed if _STK_DEBUG_ is defined during compilation, in which + case an out-of-range value will trigger an StkError exception. + */ + StkFloat tick( StkFloat input, unsigned int channel = 0 ); + + //! Take a channel of the StkFrames object as inputs to the effect and replace with stereo outputs. + /*! + The StkFrames argument reference is returned. The stereo + outputs are written to the StkFrames argument starting at the + specified \c channel. Therefore, the \c channel argument must be + less than ( channels() - 1 ) of the StkFrames argument (the first + channel is specified by 0). However, range checking is only + performed if _STK_DEBUG_ is defined during compilation, in which + case an out-of-range value will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the effect and write stereo outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. The \c iChannel + argument must be less than the number of channels in the \c + iFrames argument (the first channel is specified by 0). The \c + oChannel argument must be less than ( channels() - 1 ) of the \c + oFrames argument. However, range checking is only performed if + _STK_DEBUG_ is defined during compilation, in which case an + out-of-range value will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); + + protected: + + Delay allpassDelays_[3]; + Delay combDelays_[4]; + OnePole combFilters_[4]; + Delay outLeftDelay_; + Delay outRightDelay_; + StkFloat allpassCoefficient_; + StkFloat combCoefficient_[4]; + +}; + +inline StkFloat JCRev :: lastOut( unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel > 1 ) { + oStream_ << "JCRev::lastOut(): channel argument must be less than 2!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + return lastFrame_[channel]; +} + +inline StkFloat JCRev :: tick( StkFloat input, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel > 1 ) { + oStream_ << "JCRev::tick(): channel argument must be less than 2!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat temp, temp0, temp1, temp2, temp3, temp4, temp5, temp6; + StkFloat filtout; + + temp = allpassDelays_[0].lastOut(); + temp0 = allpassCoefficient_ * temp; + temp0 += input; + allpassDelays_[0].tick(temp0); + temp0 = -(allpassCoefficient_ * temp0) + temp; + + temp = allpassDelays_[1].lastOut(); + temp1 = allpassCoefficient_ * temp; + temp1 += temp0; + allpassDelays_[1].tick(temp1); + temp1 = -(allpassCoefficient_ * temp1) + temp; + + temp = allpassDelays_[2].lastOut(); + temp2 = allpassCoefficient_ * temp; + temp2 += temp1; + allpassDelays_[2].tick(temp2); + temp2 = -(allpassCoefficient_ * temp2) + temp; + + temp3 = temp2 + ( combFilters_[0].tick( combCoefficient_[0] * combDelays_[0].lastOut() ) ); + temp4 = temp2 + ( combFilters_[1].tick( combCoefficient_[1] * combDelays_[1].lastOut() ) ); + temp5 = temp2 + ( combFilters_[2].tick( combCoefficient_[2] * combDelays_[2].lastOut() ) ); + temp6 = temp2 + ( combFilters_[3].tick( combCoefficient_[3] * combDelays_[3].lastOut() ) ); + + combDelays_[0].tick(temp3); + combDelays_[1].tick(temp4); + combDelays_[2].tick(temp5); + combDelays_[3].tick(temp6); + + filtout = temp3 + temp4 + temp5 + temp6; + + lastFrame_[0] = effectMix_ * (outLeftDelay_.tick(filtout)); + lastFrame_[1] = effectMix_ * (outRightDelay_.tick(filtout)); + temp = (1.0 - effectMix_) * input; + lastFrame_[0] += temp; + lastFrame_[1] += temp; + + return 0.7 * lastFrame_[channel]; +} + +} // stk namespace + +#endif + diff --git a/src/stk/include/JetTable.h b/src/stk/include/JetTable.h new file mode 100644 index 00000000..1d628c8c --- /dev/null +++ b/src/stk/include/JetTable.h @@ -0,0 +1,112 @@ +#ifndef STK_JETTABL_H +#define STK_JETTABL_H + +#include "Function.h" + +namespace stk { + +/***************************************************/ +/*! \class JetTable + \brief STK jet table class. + + This class implements a flue jet non-linear + function, computed by a polynomial calculation. + Contrary to the name, this is not a "table". + + Consult Fletcher and Rossing, Karjalainen, + Cook, and others for more information. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class JetTable : public Function +{ +public: + + //! Take one sample input and map to one sample of output. + StkFloat tick( StkFloat input ); + + //! Take a channel of the StkFrames object as inputs to the table and replace with corresponding outputs. + /*! + The StkFrames argument reference is returned. The \c channel + argument must be less than the number of channels in the + StkFrames argument (the first channel is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the table and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. Each channel + argument must be less than the number of channels in the + corresponding StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); + +}; + +inline StkFloat JetTable :: tick( StkFloat input ) +{ + // Perform "table lookup" using a polynomial + // calculation (x^3 - x), which approximates + // the jet sigmoid behavior. + lastFrame_[0] = input * (input * input - 1.0); + + // Saturate at +/- 1.0. + if ( lastFrame_[0] > 1.0 ) lastFrame_[0] = 1.0; + if ( lastFrame_[0] < -1.0 ) lastFrame_[0] = -1.0; + return lastFrame_[0]; +} + +inline StkFrames& JetTable :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "JetTable::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i 1.0) *samples = 1.0; + if ( *samples < -1.0) *samples = -1.0; + } + + lastFrame_[0] = *(samples-hop); + return frames; +} + +inline StkFrames& JetTable :: tick( StkFrames& iFrames, StkFrames& oFrames, unsigned int iChannel, unsigned int oChannel ) +{ +#if defined(_STK_DEBUG_) + if ( iChannel >= iFrames.channels() || oChannel >= oFrames.channels() ) { + oStream_ << "JetTable::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i 1.0) *oSamples = 1.0; + if ( *oSamples < -1.0) *oSamples = -1.0; + } + + lastFrame_[0] = *(oSamples-oHop); + return iFrames; +} + +} // stk namespace + +#endif diff --git a/src/stk/include/LentPitShift.h b/src/stk/include/LentPitShift.h new file mode 100644 index 00000000..dff56483 --- /dev/null +++ b/src/stk/include/LentPitShift.h @@ -0,0 +1,263 @@ +#ifndef STK_LENTPITSHIFT_H +#define STK_LENTPITSHIFT_H + +#include "Effect.h" +#include "Delay.h" + +namespace stk { + +/***************************************************/ +/*! \class LentPitShift + \brief Pitch shifter effect class based on the Lent algorithm. + + This class implements a pitch shifter using pitch + tracking and sample windowing and shifting. + + by Francois Germain, 2009. +*/ +/***************************************************/ + +class LentPitShift : public Effect +{ + public: + //! Class constructor. + LentPitShift( StkFloat periodRatio = 1.0, int tMax = RT_BUFFER_SIZE ); + + ~LentPitShift( void ) { + delete window; + delete dt; + delete dpt; + delete cumDt; + } + + //! Reset and clear all internal state. + void clear( void ); + + //! Set the pitch shift factor (1.0 produces no shift). + void setShift( StkFloat shift ); + + //! Input one sample to the filter and return one output. + StkFloat tick( StkFloat input ); + + //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. + /*! + The StkFrames argument reference is returned. The \c channel + argument must be less than the number of channels in the + StkFrames argument (the first channel is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the filter and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. Each channel + argument must be less than the number of channels in the + corresponding StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); + + protected: + + //! Apply the effect on the input samples and store it. + /*! + The samples stored in the input frame vector are processed + and the delayed result are stored in the output frame vector. + */ + void process( ); + + // Frame storage vectors for process function + StkFrames inputFrames; + StkFrames outputFrames; + int ptrFrames; // writing pointer + + // Input delay line + Delay inputLine_; + int inputPtr; + + // Output delay line + Delay outputLine_; + double outputPtr; + + // Pitch tracker variables + unsigned long tMax_; // Maximal period measurable by the pitch tracker. + // It is also the size of the window used by the pitch tracker and + // the size of the frames that can be computed by the tick function + + StkFloat threshold_; // Threshold of detection for the pitch tracker + unsigned long lastPeriod_; // Result of the last pitch tracking loop + StkFloat* dt; // Array containing the euclidian distance coefficients + StkFloat* cumDt; // Array containing the cumulative sum of the coefficients in dt + StkFloat* dpt; // Array containing the pitch tracking function coefficients + + // Pitch shifter variables + StkFloat env[2]; // Coefficients for the linear interpolation when modifying the output samples + StkFloat* window; // Hamming window used for the input portion extraction + double periodRatio_; // Ratio of modification of the signal period + StkFrames zeroFrame; // Frame of tMax_ zero samples + + + // Coefficient delay line that could be used for a dynamic calculation of the pitch + //Delay* coeffLine_; + +}; + +inline void LentPitShift::process() +{ + StkFloat x_t; // input coefficient + StkFloat x_t_T; // previous input coefficient at T samples + StkFloat coeff; // new coefficient for the difference function + + unsigned long alternativePitch = tMax_; // Global minimum storage + lastPeriod_ = tMax_+1; // Storage of the lowest local minimum under the threshold + + // Loop variables + unsigned long delay_; + unsigned int n; + + // Initialization of the dt coefficients. Since the + // frames are of tMax_ length, there is no overlapping + // between the successive windows where pitch tracking + // is performed. + for ( delay_=1; delay_<=tMax_; delay_++ ) + dt[delay_] = 0.; + + // Calculation of the dt coefficients and update of the input delay line. + for ( n=0; n 1 && dpt[delay_-1]-dpt[delay_-2] < 0 && dpt[delay_]-dpt[delay_-1] > 0 ) { + // Check if the minimum is under the threshold + if ( dpt[delay_-1] < threshold_ ){ + lastPeriod_ = delay_-1; + // If a minimum is found, we can stop the loop + break; + } + else if ( dpt[alternativePitch] > dpt[delay_-1] ) + // Otherwise we store it if it is the current global minimum + alternativePitch = delay_-1; + } + } + + // Test for the last period length. + if ( dpt[delay_]-dpt[delay_-1] < 0 ) { + if ( dpt[delay_] < threshold_ ) + lastPeriod_ = delay_; + else if ( dpt[alternativePitch] > dpt[delay_] ) + alternativePitch = delay_; + } + + if ( lastPeriod_ == tMax_+1 ) + // No period has been under the threshold so we used the global minimum + lastPeriod_ = alternativePitch; + + // We put the new zero output coefficients in the output delay line and + // we get the previous calculated coefficients + outputLine_.tick( zeroFrame, outputFrames ); + + // Initialization of the Hamming window used in the algorithm + for ( int n=-(int)lastPeriod_; n<(int)lastPeriod_; n++ ) + window[n+lastPeriod_] = (1 + cos(PI*n/lastPeriod_)) / 2 ; + + long M; // Index of reading in the input delay line + long N; // Index of writing in the output delay line + double sample; // Temporary storage for the new coefficient + + // We loop for all the frames of length lastPeriod_ presents between inputPtr and tMax_ + for ( ; inputPtr<(int)(tMax_-lastPeriod_); inputPtr+=lastPeriod_ ) { + // Test for the decision of compression/expansion + while ( outputPtr < inputPtr ) { + // Coefficients for the linear interpolation + env[1] = fmod( outputPtr + tMax_, 1.0 ); + env[0] = 1.0 - env[1]; + M = tMax_ - inputPtr + lastPeriod_ - 1; // New reading pointer + N = 2*tMax_ - (unsigned long)floor(outputPtr + tMax_) + lastPeriod_ - 1; // New writing pointer + for ( unsigned int j=0; j<2*lastPeriod_; j++,M--,N-- ) { + sample = inputLine_.tapOut(M) * window[j] / 2.; + // Linear interpolation + outputLine_.addTo(env[0] * sample, N); + outputLine_.addTo(env[1] * sample, N-1); + } + outputPtr = outputPtr + lastPeriod_ * periodRatio_; // new output pointer + } + } + // Shifting of the pointers waiting for the new frame of length tMax_. + outputPtr -= tMax_; + inputPtr -= tMax_; +} + + +inline StkFloat LentPitShift :: tick( StkFloat input ) +{ + StkFloat sample; + + inputFrames[ptrFrames] = input; + + sample = outputFrames[ptrFrames++]; + + // Check for end condition + if ( ptrFrames == (int) inputFrames.size() ){ + ptrFrames = 0; + process( ); + } + + return sample; +} + +inline StkFrames& LentPitShift :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "LentPitShift::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + oStream_ << "LentPitShift::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i frames.channels() - nChannels ) { + oStream_ << "Mandolin::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i frames.channels() - nChannels ) { + oStream_ << "Mesh2D::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i +#include +#include +#include + +namespace stk { + +/**********************************************************************/ +/*! \class MidiFileIn + \brief A standard MIDI file reading/parsing class. + + This class can be used to read events from a standard MIDI file. + Event bytes are copied to a C++ vector and must be subsequently + interpreted by the user. The function getNextMidiEvent() skips + meta and sysex events, returning only MIDI channel messages. + Event delta-times are returned in the form of "ticks" and a + function is provided to determine the current "seconds per tick". + Tempo changes are internally tracked by the class and reflected in + the values returned by the function getTickSeconds(). + + by Gary P. Scavone, 2003--2021. +*/ +/**********************************************************************/ + +class MidiFileIn : public Stk +{ + public: + //! Default constructor. + /*! + If an error occurs while opening or parsing the file header, an + StkError exception will be thrown. + */ + MidiFileIn( std::string fileName ); + + //! Class destructor. + ~MidiFileIn(); + + //! Return the MIDI file format (0, 1, or 2). + int getFileFormat() const { return format_; }; + + //! Return the number of tracks in the MIDI file. + unsigned int getNumberOfTracks() const { return nTracks_; }; + + //! Return the MIDI file division value from the file header. + /*! + Note that this value must be "parsed" in accordance with the + MIDI File Specification. In particular, if the MSB is set, the + file uses time-code representations for delta-time values. + */ + int getDivision() const { return division_; }; + + //! Move the specified track event reader to the beginning of its track. + /*! + The relevant track tempo value is reset as well. If an invalid + track number is specified, an StkError exception will be thrown. + */ + void rewindTrack( unsigned int track = 0 ); + + //! Get the current value, in seconds, of delta-time ticks for the specified track. + /*! + This value can change as events are read (via "Set Tempo" + Meta-Events). Therefore, one should call this function after + every call to getNextEvent() or getNextMidiEvent(). If an + invalid track number is specified, an StkError exception will be + thrown. + */ + double getTickSeconds( unsigned int track = 0 ); + + //! Fill the user-provided vector with the next event in the specified track and return the event delta-time in ticks. + /*! + MIDI File events consist of a delta time and a sequence of event + bytes. This function returns the delta-time value and writes + the subsequent event bytes directly to the event vector. The + user must parse the event bytes in accordance with the MIDI File + Specification. All returned MIDI channel events are complete + ... a status byte is provided even when running status is used + in the file. If the track has reached its end, no bytes will be + written and the event vector size will be zero. If an invalid + track number is specified or an error occurs while reading the + file, an StkError exception will be thrown. + */ + unsigned long getNextEvent( std::vector *event, unsigned int track = 0 ); + + //! Fill the user-provided vector with the next MIDI channel event in the specified track and return the event delta time in ticks. + /*! + All returned MIDI events are complete ... a status byte is + provided even when running status is used in the file. Meta and + sysex events in the track are skipped though "Set Tempo" events + are properly parsed for use by the getTickSeconds() function. + If the track has reached its end, no bytes will be written and + the event vector size will be zero. If an invalid track number + is specified or an error occurs while reading the file, an + StkError exception will be thrown. + */ + unsigned long getNextMidiEvent( std::vector *midiEvent, unsigned int track = 0 ); + + protected: + + // This protected class function is used for reading variable-length + // MIDI file values. It is assumed that this function is called with + // the file read pointer positioned at the start of a + // variable-length value. The function returns true if the value is + // successfully parsed. Otherwise, it returns false. + bool readVariableLength( unsigned long *value ); + + std::ifstream file_; + unsigned int nTracks_; + int format_; + int division_; + bool usingTimeCode_; + std::vector tickSeconds_; + std::vector trackPointers_; + std::vector trackOffsets_; + std::vector trackLengths_; + std::vector trackStatus_; + + // This structure and the following variables are used to save and + // keep track of a format 1 tempo map (and the initial tickSeconds + // parameter for formats 0 and 2). + struct TempoChange { + unsigned long count; + double tickSeconds; + }; + std::vector tempoEvents_; + std::vector trackCounters_; + std::vector trackTempoIndex_; +}; + +} // stk namespace + +#endif diff --git a/src/stk/include/Modal.h b/src/stk/include/Modal.h new file mode 100644 index 00000000..626ac6bd --- /dev/null +++ b/src/stk/include/Modal.h @@ -0,0 +1,154 @@ +#ifndef STK_MODAL_H +#define STK_MODAL_H + +#include "Instrmnt.h" +#include "Envelope.h" +#include "FileLoop.h" +#include "SineWave.h" +#include "BiQuad.h" +#include "OnePole.h" + +namespace stk { + +/***************************************************/ +/*! \class Modal + \brief STK resonance model abstract base class. + + This class contains an excitation wavetable, + an envelope, an oscillator, and N resonances + (non-sweeping BiQuad filters), where N is set + during instantiation. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class Modal : public Instrmnt +{ +public: + //! Class constructor, taking the desired number of modes to create. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ + Modal( unsigned int modes = 4 ); + + //! Class destructor. + virtual ~Modal( void ); + + //! Reset and clear all internal state. + void clear( void ); + + //! Set instrument parameters for a particular frequency. + virtual void setFrequency( StkFloat frequency ); + + //! Set the ratio and radius for a specified mode filter. + void setRatioAndRadius( unsigned int modeIndex, StkFloat ratio, StkFloat radius ); + + //! Set the master gain. + void setMasterGain( StkFloat aGain ) { masterGain_ = aGain; }; + + //! Set the direct gain. + void setDirectGain( StkFloat aGain ) { directGain_ = aGain; }; + + //! Set the gain for a specified mode filter. + void setModeGain( unsigned int modeIndex, StkFloat gain ); + + //! Initiate a strike with the given amplitude (0.0 - 1.0). + virtual void strike( StkFloat amplitude ); + + //! Damp modes with a given decay factor (0.0 - 1.0). + void damp( StkFloat amplitude ); + + //! Start a note with the given frequency and amplitude. + void noteOn( StkFloat frequency, StkFloat amplitude ); + + //! Stop a note with the given amplitude (speed of decay). + void noteOff( StkFloat amplitude ); + + //! Perform the control change specified by \e number and \e value (0.0 - 128.0). + virtual void controlChange( int number, StkFloat value ) = 0; + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + +protected: + + Envelope envelope_; + FileWvIn *wave_; + BiQuad **filters_; + OnePole onepole_; + SineWave vibrato_; + + unsigned int nModes_; + std::vector ratios_; + std::vector radii_; + + StkFloat vibratoGain_; + StkFloat masterGain_; + StkFloat directGain_; + StkFloat stickHardness_; + StkFloat strikePosition_; + StkFloat baseFrequency_; +}; + +inline StkFloat Modal :: tick( unsigned int ) +{ + StkFloat temp = masterGain_ * onepole_.tick( wave_->tick() * envelope_.tick() ); + + StkFloat temp2 = 0.0; + for ( unsigned int i=0; itick(temp); + + temp2 -= temp2 * directGain_; + temp2 += directGain_ * temp; + + if ( vibratoGain_ != 0.0 ) { + // Calculate AM and apply to master out + temp = 1.0 + ( vibrato_.tick() * vibratoGain_ ); + temp2 = temp * temp2; + } + + lastFrame_[0] = temp2; + return lastFrame_[0]; +} + +inline StkFrames& Modal :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "Modal::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i= noiseRate_ ) { + noise_.tick(); + noiseCounter_ = 0; + } + lastFrame_[0] += filter_.tick( noise_.lastOut() ); + return lastFrame_[0]; +} + +inline StkFrames& Modulate :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "Modulate::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; isetFrequency( mSpeed ); }; + + //! Set the modulation (vibrato) depth. + void setModulationDepth( StkFloat mDepth ) { modDepth_ = mDepth * 0.5; }; + + //! Perform the control change specified by \e number and \e value (0.0 - 128.0). + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + + FormSwep filters_[2]; + StkFloat modDepth_; + StkFloat filterQ_; + StkFloat filterRate_; + +}; + +inline StkFloat Moog :: tick( unsigned int ) +{ + StkFloat temp; + + if ( modDepth_ != 0.0 ) { + temp = loops_[1]->tick() * modDepth_; + loops_[0]->setFrequency( baseFrequency_ * (1.0 + temp) ); + } + + temp = attackGain_ * attacks_[0]->tick(); + temp += loopGain_ * loops_[0]->tick(); + temp = filter_.tick( temp ); + temp *= adsr_.tick(); + temp = filters_[0].tick( temp ); + lastFrame_[0] = filters_[1].tick( temp ); + return lastFrame_[0] * 6.0; +} + +inline StkFrames& Moog :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "Moog::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i 1 ) { + oStream_ << "NRev::lastOut(): channel argument must be less than 2!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + return lastFrame_[channel]; +} + +inline StkFloat NRev :: tick( StkFloat input, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel > 1 ) { + oStream_ << "NRev::tick(): channel argument must be less than 2!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat temp, temp0, temp1, temp2, temp3; + int i; + + temp0 = 0.0; + for ( i=0; i<6; i++ ) { + temp = input + (combCoefficient_[i] * combDelays_[i].lastOut()); + temp0 += combDelays_[i].tick(temp); + } + + for ( i=0; i<3; i++ ) { + temp = allpassDelays_[i].lastOut(); + temp1 = allpassCoefficient_ * temp; + temp1 += temp0; + allpassDelays_[i].tick(temp1); + temp0 = -(allpassCoefficient_ * temp1) + temp; + } + + // One-pole lowpass filter. + lowpassState_ = 0.7 * lowpassState_ + 0.3 * temp0; + temp = allpassDelays_[3].lastOut(); + temp1 = allpassCoefficient_ * temp; + temp1 += lowpassState_; + allpassDelays_[3].tick( temp1 ); + temp1 = -( allpassCoefficient_ * temp1 ) + temp; + + temp = allpassDelays_[4].lastOut(); + temp2 = allpassCoefficient_ * temp; + temp2 += temp1; + allpassDelays_[4].tick( temp2 ); + lastFrame_[0] = effectMix_*( -( allpassCoefficient_ * temp2 ) + temp ); + + temp = allpassDelays_[5].lastOut(); + temp3 = allpassCoefficient_ * temp; + temp3 += temp1; + allpassDelays_[5].tick( temp3 ); + lastFrame_[1] = effectMix_*( - ( allpassCoefficient_ * temp3 ) + temp ); + + temp = ( 1.0 - effectMix_ ) * input; + lastFrame_[0] += temp; + lastFrame_[1] += temp; + + return lastFrame_[channel]; +} + +} // stk namespace + +#endif + diff --git a/src/stk/include/Noise.h b/src/stk/include/Noise.h new file mode 100644 index 00000000..3ea46e57 --- /dev/null +++ b/src/stk/include/Noise.h @@ -0,0 +1,84 @@ +#ifndef STK_NOISE_H +#define STK_NOISE_H + +#include "Generator.h" +#include + +namespace stk { + +/***************************************************/ +/*! \class Noise + \brief STK noise generator. + + Generic random number generation using the + C rand() function. The quality of the rand() + function varies from one OS to another. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class Noise : public Generator +{ +public: + + //! Default constructor that can also take a specific seed value. + /*! + If the seed value is zero (the default value), the random number generator is + seeded with the system time. + */ + Noise( unsigned int seed = 0 ); + + //! Seed the random number generator with a specific seed value. + /*! + If no seed is provided or the seed value is zero, the random + number generator is seeded with the current system time. + */ + void setSeed( unsigned int seed = 0 ); + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Compute and return one output sample. + StkFloat tick( void ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + +protected: + +}; + +inline StkFloat Noise :: tick( void ) +{ + return lastFrame_[0] = (StkFloat) ( 2.0 * rand() / (RAND_MAX + 1.0) - 1.0 ); +} + +inline StkFrames& Noise :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "Noise::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= frames.channels() ) { + oStream_ << "OnePole::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + oStream_ << "OnePole::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i= frames.channels() ) { + oStream_ << "OneZero::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + oStream_ << "OneZero::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i 1 ) { + oStream_ << "PRCRev::lastOut(): channel argument must be less than 2!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + return lastFrame_[channel]; +} + + inline StkFloat PRCRev :: tick( StkFloat input, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel > 1 ) { + oStream_ << "PRCRev::tick(): channel argument must be less than 2!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat temp, temp0, temp1, temp2, temp3; + + temp = allpassDelays_[0].lastOut(); + temp0 = allpassCoefficient_ * temp; + temp0 += input; + allpassDelays_[0].tick(temp0); + temp0 = -(allpassCoefficient_ * temp0) + temp; + + temp = allpassDelays_[1].lastOut(); + temp1 = allpassCoefficient_ * temp; + temp1 += temp0; + allpassDelays_[1].tick(temp1); + temp1 = -(allpassCoefficient_ * temp1) + temp; + + temp2 = temp1 + ( combCoefficient_[0] * combDelays_[0].lastOut() ); + temp3 = temp1 + ( combCoefficient_[1] * combDelays_[1].lastOut() ); + + lastFrame_[0] = effectMix_ * (combDelays_[0].tick(temp2)); + lastFrame_[1] = effectMix_ * (combDelays_[1].tick(temp3)); + temp = (1.0 - effectMix_) * input; + lastFrame_[0] += temp; + lastFrame_[1] += temp; + + return lastFrame_[channel]; +} + +} // stk namespace + +#endif + diff --git a/src/stk/include/PercFlut.h b/src/stk/include/PercFlut.h new file mode 100644 index 00000000..1dbedefa --- /dev/null +++ b/src/stk/include/PercFlut.h @@ -0,0 +1,127 @@ +#ifndef STK_PERCFLUT_H +#define STK_PERCFLUT_H + +#include "FM.h" + +namespace stk { + +/***************************************************/ +/*! \class PercFlut + \brief STK percussive flute FM synthesis instrument. + + This class implements algorithm 4 of the TX81Z. + + \code + Algorithm 4 is : 4->3--\ + 2-- + -->1-->Out + \endcode + + Control Change Numbers: + - Total Modulator Index = 2 + - Modulator Crossfade = 4 + - LFO Speed = 11 + - LFO Depth = 1 + - ADSR 2 & 4 Target = 128 + + The basic Chowning/Stanford FM patent expired + in 1995, but there exist follow-on patents, + mostly assigned to Yamaha. If you are of the + type who should worry about this (making + money) worry away. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class PercFlut : public FM +{ + public: + //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ + PercFlut( void ); + + //! Class destructor. + ~PercFlut( void ); + + //! Set instrument parameters for a particular frequency. + void setFrequency( StkFloat frequency ); + + //! Start a note with the given frequency and amplitude. + void noteOn( StkFloat frequency, StkFloat amplitude ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + +}; + +inline StkFloat PercFlut :: tick( unsigned int ) +{ + StkFloat temp; + + temp = vibrato_.tick() * modDepth_ * 0.2; + waves_[0]->setFrequency(baseFrequency_ * (1.0 + temp) * ratios_[0]); + waves_[1]->setFrequency(baseFrequency_ * (1.0 + temp) * ratios_[1]); + waves_[2]->setFrequency(baseFrequency_ * (1.0 + temp) * ratios_[2]); + waves_[3]->setFrequency(baseFrequency_ * (1.0 + temp) * ratios_[3]); + + waves_[3]->addPhaseOffset( twozero_.lastOut() ); + temp = gains_[3] * adsr_[3]->tick() * waves_[3]->tick(); + + twozero_.tick(temp); + waves_[2]->addPhaseOffset( temp ); + temp = (1.0 - (control2_ * 0.5)) * gains_[2] * adsr_[2]->tick() * waves_[2]->tick(); + + temp += control2_ * 0.5 * gains_[1] * adsr_[1]->tick() * waves_[1]->tick(); + temp = temp * control1_; + + waves_[0]->addPhaseOffset(temp); + temp = gains_[0] * adsr_[0]->tick() * waves_[0]->tick(); + + lastFrame_[0] = temp * 0.5; + return lastFrame_[0]; +} + +inline StkFrames& PercFlut :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "PercFlut::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i maxDelay-12 ) delay_[0] -= delayLength_; + while ( delay_[0] < 12 ) delay_[0] += delayLength_; + + delay_[1] = delay_[0] + halfLength_; + while ( delay_[1] > maxDelay-12 ) delay_[1] -= delayLength_; + while ( delay_[1] < 12 ) delay_[1] += delayLength_; + + // Set the new delay line lengths. + delayLine_[0].setDelay( delay_[0] ); + delayLine_[1].setDelay( delay_[1] ); + + // Calculate a triangular envelope. + env_[1] = fabs( ( delay_[0] - halfLength_ + 12 ) * ( 1.0 / (halfLength_ + 12 ) ) ); + env_[0] = 1.0 - env_[1]; + + // Delay input and apply envelope. + lastFrame_[0] = env_[0] * delayLine_[0].tick( input ); + lastFrame_[0] += env_[1] * delayLine_[1].tick( input ); + + // Compute effect mix and output. + lastFrame_[0] *= effectMix_; + lastFrame_[0] += ( 1.0 - effectMix_ ) * input; + + return lastFrame_[0]; +} + +} // stk namespace + +#endif + diff --git a/src/stk/include/Plucked.h b/src/stk/include/Plucked.h new file mode 100644 index 00000000..26a22577 --- /dev/null +++ b/src/stk/include/Plucked.h @@ -0,0 +1,117 @@ +#ifndef STK_PLUCKED_H +#define STK_PLUCKED_H + +#include "Instrmnt.h" +#include "DelayA.h" +#include "OneZero.h" +#include "OnePole.h" +#include "Noise.h" + +namespace stk { + +/***************************************************/ +/*! \class Plucked + \brief STK basic plucked string class. + + This class implements a simple plucked string + physical model based on the Karplus-Strong + algorithm. + + For a more advanced plucked string implementation, + see the stk::Twang class. + + This is a digital waveguide model, making its + use possibly subject to patents held by + Stanford University, Yamaha, and others. + There exist at least two patents, assigned to + Stanford, bearing the names of Karplus and/or + Strong. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class Plucked : public Instrmnt +{ + public: + //! Class constructor, taking the lowest desired playing frequency. + Plucked( StkFloat lowestFrequency = 10.0 ); + + //! Class destructor. + ~Plucked( void ); + + //! Reset and clear all internal state. + void clear( void ); + + //! Set instrument parameters for a particular frequency. + void setFrequency( StkFloat frequency ); + + //! Pluck the string with the given amplitude using the current frequency. + void pluck( StkFloat amplitude ); + + //! Start a note with the given frequency and amplitude. + void noteOn( StkFloat frequency, StkFloat amplitude ); + + //! Stop a note with the given amplitude (speed of decay). + void noteOff( StkFloat amplitude ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + + DelayA delayLine_; + OneZero loopFilter_; + OnePole pickFilter_; + Noise noise_; + + StkFloat loopGain_; +}; + +inline StkFloat Plucked :: tick( unsigned int ) +{ + // Here's the whole inner loop of the instrument!! + return lastFrame_[0] = 3.0 * delayLine_.tick( loopFilter_.tick( delayLine_.lastOut() * loopGain_ ) ); +} + +inline StkFrames& Plucked :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "Plucked::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i= frames.channels() ) { + oStream_ << "PoleZero::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i frames.channels() - nChannels ) { + oStream_ << "Recorder::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i 1, the reed has slammed shut and the + // reflection function value saturates at 1.0. + if ( lastFrame_[0] > 1.0) lastFrame_[0] = (StkFloat) 1.0; + + // This is nearly impossible in a physical system, but + // a reflection function value of -1.0 corresponds to + // an open end (and no discontinuity in bore profile). + if ( lastFrame_[0] < -1.0) lastFrame_[0] = (StkFloat) -1.0; + + return lastFrame_[0]; +} + +inline StkFrames& ReedTable :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "ReedTable::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i 1.0) *samples = 1.0; + if ( *samples < -1.0) *samples = -1.0; + } + + lastFrame_[0] = *(samples-hop); + return frames; +} + +inline StkFrames& ReedTable :: tick( StkFrames& iFrames, StkFrames& oFrames, unsigned int iChannel, unsigned int oChannel ) +{ +#if defined(_STK_DEBUG_) + if ( iChannel >= iFrames.channels() || oChannel >= oFrames.channels() ) { + oStream_ << "ReedTable::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i 1.0) *oSamples = 1.0; + if ( *oSamples < -1.0) *oSamples = -1.0; + } + + lastFrame_[0] = *(oSamples-oHop); + return iFrames; +} + +} // stk namespace + +#endif diff --git a/src/stk/include/Resonate.h b/src/stk/include/Resonate.h new file mode 100644 index 00000000..4a3b7d45 --- /dev/null +++ b/src/stk/include/Resonate.h @@ -0,0 +1,124 @@ +#ifndef STK_RESONATE_H +#define STK_RESONATE_H + +#include "Instrmnt.h" +#include "ADSR.h" +#include "BiQuad.h" +#include "Noise.h" + +namespace stk { + +/***************************************************/ +/*! \class Resonate + \brief STK noise driven formant filter. + + This instrument contains a noise source, which + excites a biquad resonance filter, with volume + controlled by an ADSR. + + Control Change Numbers: + - Resonance Frequency (0-Nyquist) = 2 + - Pole Radii = 4 + - Notch Frequency (0-Nyquist) = 11 + - Zero Radii = 1 + - Envelope Gain = 128 + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class Resonate : public Instrmnt +{ + public: + //! Class constructor. + Resonate( void ); + + //! Class destructor. + ~Resonate( void ); + + //! Set the filter for a resonance at the given frequency (Hz) and radius. + void setResonance( StkFloat frequency, StkFloat radius ); + + //! Set the filter for a notch at the given frequency (Hz) and radius. + void setNotch( StkFloat frequency, StkFloat radius ); + + //! Set the filter zero coefficients for contant resonance gain. + void setEqualGainZeroes( void ) { filter_.setEqualGainZeroes(); }; + + //! Initiate the envelope with a key-on event. + void keyOn( void ) { adsr_.keyOn(); }; + + //! Signal a key-off event to the envelope. + void keyOff( void ) { adsr_.keyOff(); }; + + //! Start a note with the given frequency and amplitude. + void noteOn( StkFloat frequency, StkFloat amplitude ); + + //! Stop a note with the given amplitude (speed of decay). + void noteOff( StkFloat amplitude ); + + //! Perform the control change specified by \e number and \e value (0.0 - 128.0). + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + + ADSR adsr_; + BiQuad filter_; + Noise noise_; + StkFloat poleFrequency_; + StkFloat poleRadius_; + StkFloat zeroFrequency_; + StkFloat zeroRadius_; + +}; + +inline StkFloat Resonate :: tick( unsigned int ) +{ + lastFrame_[0] = filter_.tick( noise_.tick() ); + lastFrame_[0] *= adsr_.tick(); + return lastFrame_[0]; +} + +inline StkFrames& Resonate :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "Resonate::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i3--\ + + --> Out + 2->1--/ + \endcode + + Control Change Numbers: + - Modulator Index One = 2 + - Crossfade of Outputs = 4 + - LFO Speed = 11 + - LFO Depth = 1 + - ADSR 2 & 4 Target = 128 + + The basic Chowning/Stanford FM patent expired + in 1995, but there exist follow-on patents, + mostly assigned to Yamaha. If you are of the + type who should worry about this (making + money) worry away. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class Rhodey : public FM +{ + public: + //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ + Rhodey( void ); + + //! Class destructor. + ~Rhodey( void ); + + //! Set instrument parameters for a particular frequency. + void setFrequency( StkFloat frequency ); + + //! Start a note with the given frequency and amplitude. + void noteOn( StkFloat frequency, StkFloat amplitude ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + +}; + +inline StkFloat Rhodey :: tick( unsigned int ) +{ + StkFloat temp, temp2; + + temp = gains_[1] * adsr_[1]->tick() * waves_[1]->tick(); + temp = temp * control1_; + + waves_[0]->addPhaseOffset( temp ); + waves_[3]->addPhaseOffset( twozero_.lastOut() ); + temp = gains_[3] * adsr_[3]->tick() * waves_[3]->tick(); + twozero_.tick(temp); + + waves_[2]->addPhaseOffset( temp ); + temp = ( 1.0 - (control2_ * 0.5)) * gains_[0] * adsr_[0]->tick() * waves_[0]->tick(); + temp += control2_ * 0.5 * gains_[2] * adsr_[2]->tick() * waves_[2]->tick(); + + // Calculate amplitude modulation and apply it to output. + temp2 = vibrato_.tick() * modDepth_; + temp = temp * (1.0 + temp2); + + lastFrame_[0] = temp * 0.5; + return lastFrame_[0]; +} + +inline StkFrames& Rhodey :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "Rhodey::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i_ + + where is the string used in the SKINI stream. + + by Perry R. Cook, 1995--2021. +*/ +/*********************************************************/ + +namespace stk { + +#define NOPE -32767 +#define YEP 1 +#define SK_DBL -32766 +#define SK_INT -32765 +#define SK_STR -32764 +#define __SK_Exit_ 999 + +/***** MIDI COMPATIBLE MESSAGES *****/ +/*** (Status bytes for channel=0) ***/ + +#define __SK_NoteOff_ 128 +#define __SK_NoteOn_ 144 +#define __SK_PolyPressure_ 160 +#define __SK_ControlChange_ 176 +#define __SK_ProgramChange_ 192 +#define __SK_AfterTouch_ 208 +#define __SK_ChannelPressure_ __SK_AfterTouch_ +#define __SK_PitchWheel_ 224 +#define __SK_PitchBend_ __SK_PitchWheel_ +#define __SK_PitchChange_ 49 + +#define __SK_Clock_ 248 +#define __SK_SongStart_ 250 +#define __SK_Continue_ 251 +#define __SK_SongStop_ 252 +#define __SK_ActiveSensing_ 254 +#define __SK_SystemReset_ 255 + +#define __SK_Volume_ 7 +#define __SK_ModWheel_ 1 +#define __SK_Modulation_ __SK_ModWheel_ +#define __SK_Breath_ 2 +#define __SK_FootControl_ 4 +#define __SK_Portamento_ 65 +#define __SK_Balance_ 8 +#define __SK_Pan_ 10 +#define __SK_Sustain_ 64 +#define __SK_Damper_ __SK_Sustain_ +#define __SK_Expression_ 11 + +#define __SK_AfterTouch_Cont_ 128 +#define __SK_ModFrequency_ __SK_Expression_ + +#define __SK_ProphesyRibbon_ 16 +#define __SK_ProphesyWheelUp_ 2 +#define __SK_ProphesyWheelDown_ 3 +#define __SK_ProphesyPedal_ 18 +#define __SK_ProphesyKnob1_ 21 +#define __SK_ProphesyKnob2_ 22 + +/*** Instrument Family Specific ***/ + +#define __SK_NoiseLevel_ __SK_FootControl_ + +#define __SK_PickPosition_ __SK_FootControl_ +#define __SK_StringDamping_ __SK_Expression_ +#define __SK_StringDetune_ __SK_ModWheel_ +#define __SK_BodySize_ __SK_Breath_ +#define __SK_BowPressure_ __SK_Breath_ +#define __SK_BowPosition_ __SK_PickPosition_ +#define __SK_BowBeta_ __SK_BowPosition_ + +#define __SK_ReedStiffness_ __SK_Breath_ +#define __SK_ReedRestPos_ __SK_FootControl_ + +#define __SK_FluteEmbouchure_ __SK_Breath_ +#define __SK_JetDelay_ __SK_FluteEmbouchure_ + +#define __SK_LipTension_ __SK_Breath_ +#define __SK_SlideLength_ __SK_FootControl_ + +#define __SK_StrikePosition_ __SK_PickPosition_ +#define __SK_StickHardness_ __SK_Breath_ + +#define __SK_TrillDepth_ 1051 +#define __SK_TrillSpeed_ 1052 +#define __SK_StrumSpeed_ __SK_TrillSpeed_ +#define __SK_RollSpeed_ __SK_TrillSpeed_ + +#define __SK_FilterQ_ __SK_Breath_ +#define __SK_FilterFreq_ 1062 +#define __SK_FilterSweepRate_ __SK_FootControl_ + +#define __SK_ShakerInst_ 1071 +#define __SK_ShakerEnergy_ __SK_Breath_ +#define __SK_ShakerDamping_ __SK_ModFrequency_ +#define __SK_ShakerNumObjects_ __SK_FootControl_ + +#define __SK_Strumming_ 1090 +#define __SK_NotStrumming_ 1091 +#define __SK_Trilling_ 1092 +#define __SK_NotTrilling_ 1093 +#define __SK_Rolling_ __SK_Strumming_ +#define __SK_NotRolling_ __SK_NotStrumming_ + +#define __SK_PlayerSkill_ 2001 +#define __SK_Chord_ 2002 +#define __SK_ChordOff_ 2003 + +#define __SK_SINGER_FilePath_ 3000 +#define __SK_SINGER_Frequency_ 3001 +#define __SK_SINGER_NoteName_ 3002 +#define __SK_SINGER_Shape_ 3003 +#define __SK_SINGER_Glot_ 3004 +#define __SK_SINGER_VoicedUnVoiced_ 3005 +#define __SK_SINGER_Synthesize_ 3006 +#define __SK_SINGER_Silence_ 3007 +#define __SK_SINGER_VibratoAmt_ __SK_ModWheel_ +#define __SK_SINGER_RndVibAmt_ 3008 +#define __SK_SINGER_VibFreq_ __SK_Expression_ + +} // stk namespace diff --git a/src/stk/include/SKINItbl.h b/src/stk/include/SKINItbl.h new file mode 100644 index 00000000..cfe1fda6 --- /dev/null +++ b/src/stk/include/SKINItbl.h @@ -0,0 +1,135 @@ + +#include "SKINImsg.h" + +namespace stk { + +#define __SK_MaxMsgTypes_ 80 + +struct SkiniSpec { char messageString[32]; + long type; + long data2; + long data3; + }; + +/* SEE COMMENT BLOCK AT BOTTOM FOR FIELDS AND USES */ +/* MessageString , type, data2, data3 */ + +struct SkiniSpec skini_msgs[__SK_MaxMsgTypes_] = +{ + {"NoteOff" , __SK_NoteOff_, SK_DBL, SK_DBL}, + {"NoteOn" , __SK_NoteOn_, SK_DBL, SK_DBL}, + {"PolyPressure" , __SK_PolyPressure_, SK_DBL, SK_DBL}, + {"ControlChange" , __SK_ControlChange_, SK_INT, SK_DBL}, + {"ProgramChange" , __SK_ProgramChange_, SK_DBL, NOPE}, + {"AfterTouch" , __SK_AfterTouch_, SK_DBL, NOPE}, + {"ChannelPressure" ,__SK_ChannelPressure_, SK_DBL, NOPE}, + {"PitchWheel" , __SK_PitchWheel_, SK_DBL, NOPE}, + {"PitchBend" , __SK_PitchBend_, SK_DBL, NOPE}, + {"PitchChange" , __SK_PitchChange_, SK_DBL, NOPE}, + + {"Clock" , __SK_Clock_, NOPE, NOPE}, + {"Undefined" , 249, NOPE, NOPE}, + {"SongStart" , __SK_SongStart_, NOPE, NOPE}, + {"Continue" , __SK_Continue_, NOPE, NOPE}, + {"SongStop" , __SK_SongStop_, NOPE, NOPE}, + {"Undefined" , 253, NOPE, NOPE}, + {"ActiveSensing" , __SK_ActiveSensing_, NOPE, NOPE}, + {"SystemReset" , __SK_SystemReset_, NOPE, NOPE}, + + {"Volume" , __SK_ControlChange_, __SK_Volume_ , SK_DBL}, + {"ModWheel" , __SK_ControlChange_, __SK_ModWheel_ , SK_DBL}, + {"Modulation" , __SK_ControlChange_, __SK_Modulation_ , SK_DBL}, + {"Breath" , __SK_ControlChange_, __SK_Breath_ , SK_DBL}, + {"FootControl" , __SK_ControlChange_, __SK_FootControl_ , SK_DBL}, + {"Portamento" , __SK_ControlChange_, __SK_Portamento_ , SK_DBL}, + {"Balance" , __SK_ControlChange_, __SK_Balance_ , SK_DBL}, + {"Pan" , __SK_ControlChange_, __SK_Pan_ , SK_DBL}, + {"Sustain" , __SK_ControlChange_, __SK_Sustain_ , SK_DBL}, + {"Damper" , __SK_ControlChange_, __SK_Damper_ , SK_DBL}, + {"Expression" , __SK_ControlChange_, __SK_Expression_ , SK_DBL}, + + {"NoiseLevel" , __SK_ControlChange_, __SK_NoiseLevel_ , SK_DBL}, + {"PickPosition" , __SK_ControlChange_, __SK_PickPosition_ , SK_DBL}, + {"StringDamping" , __SK_ControlChange_, __SK_StringDamping_ , SK_DBL}, + {"StringDetune" , __SK_ControlChange_, __SK_StringDetune_ , SK_DBL}, + {"BodySize" , __SK_ControlChange_, __SK_BodySize_ , SK_DBL}, + {"BowPressure" , __SK_ControlChange_, __SK_BowPressure_ , SK_DBL}, + {"BowPosition" , __SK_ControlChange_, __SK_BowPosition_ , SK_DBL}, + {"BowBeta" , __SK_ControlChange_, __SK_BowBeta_ , SK_DBL}, + + {"ReedStiffness" , __SK_ControlChange_, __SK_ReedStiffness_ , SK_DBL}, + {"ReedRestPos" , __SK_ControlChange_, __SK_ReedRestPos_ , SK_DBL}, + {"FluteEmbouchure" , __SK_ControlChange_, __SK_FluteEmbouchure_ , SK_DBL}, + {"LipTension" , __SK_ControlChange_, __SK_LipTension_ , SK_DBL}, + {"StrikePosition" , __SK_ControlChange_, __SK_StrikePosition_ , SK_DBL}, + {"StickHardness" , __SK_ControlChange_, __SK_StickHardness_ , SK_DBL}, + + {"TrillDepth" , __SK_ControlChange_, __SK_TrillDepth_ , SK_DBL}, + {"TrillSpeed" , __SK_ControlChange_, __SK_TrillSpeed_ , SK_DBL}, + + {"Strumming" , __SK_ControlChange_, __SK_Strumming_ , 127 }, + {"NotStrumming" , __SK_ControlChange_, __SK_Strumming_ , 0 }, + + {"PlayerSkill" , __SK_ControlChange_, __SK_PlayerSkill_ , SK_DBL}, + + {"Chord" , __SK_Chord_ , SK_DBL, SK_STR}, + {"ChordOff" , __SK_ChordOff_ , SK_DBL, NOPE}, + + {"ShakerInst" , __SK_ControlChange_, __SK_ShakerInst_ , SK_DBL}, + {"Maraca" , __SK_ControlChange_, __SK_ShakerInst_ , 0 }, + {"Sekere" , __SK_ControlChange_, __SK_ShakerInst_ , 1 }, + {"Cabasa" , __SK_ControlChange_, __SK_ShakerInst_ , 2 }, + {"Bamboo" , __SK_ControlChange_, __SK_ShakerInst_ , 3 }, + {"Waterdrp" , __SK_ControlChange_, __SK_ShakerInst_ , 4 }, + {"Tambourn" , __SK_ControlChange_, __SK_ShakerInst_ , 5 }, + {"Sleighbl" , __SK_ControlChange_, __SK_ShakerInst_ , 6 }, + {"Guiro" , __SK_ControlChange_, __SK_ShakerInst_ , 7 }, + + {"OpenFile" , 256, SK_STR, NOPE}, + {"SetPath" , 257, SK_STR, NOPE}, + + {"FilePath" , __SK_SINGER_FilePath_ , SK_STR, NOPE}, + {"Frequency" , __SK_SINGER_Frequency_ , SK_STR, NOPE}, + {"NoteName" , __SK_SINGER_NoteName_ , SK_STR, NOPE}, + {"VocalShape" , __SK_SINGER_Shape_ , SK_STR, NOPE}, + {"Glottis" , __SK_SINGER_Glot_ , SK_STR, NOPE}, + {"VoicedUnVoiced" , __SK_SINGER_VoicedUnVoiced_, SK_DBL, SK_STR}, + {"Synthesize" , __SK_SINGER_Synthesize_ , SK_STR, NOPE}, + {"Silence" , __SK_SINGER_Silence_ , SK_STR, NOPE}, + {"RndVibAmt" , __SK_SINGER_RndVibAmt_ , SK_STR, NOPE}, + {"VibratoAmt" , __SK_ControlChange_ ,__SK_SINGER_VibratoAmt_,SK_DBL}, + {"VibFreq" , __SK_ControlChange_ ,__SK_SINGER_VibFreq_ ,SK_DBL} +}; + + +/** FORMAT: *************************************************************/ +/* */ +/* MessageStr$ , type, data2, data3, */ +/* */ +/* type is the message type sent back from the SKINI line parser. */ +/* data is either */ +/* NOPE : field not used, specifically, there aren't going */ +/* to be any more fields on this line. So if there */ +/* is NOPE in data2, data3 won't even be checked */ +/* SK_INT : byte (actually scanned as 32 bit signed integer) */ +/* If it's a MIDI data field which is required to */ +/* be an integer, like a controller number, it's */ +/* 0-127. Otherwise, get creative with SK_INTs. */ +/* SK_DBL : double precision floating point. SKINI uses these */ +/* in the MIDI context for note numbers with micro */ +/* tuning, velocities, controller values, etc. */ +/* SK_STR : only valid in final field. This allows (nearly) */ +/* arbitrary message types to be supported by simply */ +/* scanning the string to EndOfLine and then passing */ +/* it to a more intelligent handler. For example, */ +/* MIDI SYSEX (system exclusive) messages of up to */ +/* 256 bytes can be read as space-delimited integers */ +/* into the SK_STR buffer. Longer bulk dumps, */ +/* soundfiles, etc. should be handled as a new */ +/* message type pointing to a FileName stored in the */ +/* SK_STR field, or as a new type of multi-line */ +/* message. */ +/* */ +/*************************************************************************/ + +} // stk namespace diff --git a/src/stk/include/Sampler.h b/src/stk/include/Sampler.h new file mode 100644 index 00000000..3bc4d7e9 --- /dev/null +++ b/src/stk/include/Sampler.h @@ -0,0 +1,75 @@ +#ifndef STK_SAMPLER_H +#define STK_SAMPLER_H + +#include "Instrmnt.h" +#include "ADSR.h" +#include "FileLoop.h" +#include "OnePole.h" + +namespace stk { + +/***************************************************/ +/*! \class Sampler + \brief STK sampling synthesis abstract base class. + + This instrument provides an ADSR envelope, a one-pole filter, and + structures for an arbitrary number of attack and looped files. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class Sampler : public Instrmnt +{ + public: + //! Default constructor. + Sampler( void ); + + //! Class destructor. + virtual ~Sampler( void ); + + //! Set instrument parameters for a particular frequency. + virtual void setFrequency( StkFloat frequency ) = 0; + + //! Initiate the envelopes with a key-on event and reset the attack waves. + void keyOn( void ); + + //! Signal a key-off event to the envelopes. + void keyOff( void ); + + //! Stop a note with the given amplitude (speed of decay). + virtual void noteOff( StkFloat amplitude ); + + //! Perform the control change specified by \e number and \e value (0.0 - 128.0). + virtual void controlChange( int number, StkFloat value ) = 0; + + //! Compute and return one output sample. + virtual StkFloat tick( unsigned int channel = 0 ) = 0; + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + virtual StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ) = 0; + + protected: + + ADSR adsr_; + std::vector attacks_; + std::vector loops_; + OnePole filter_; + StkFloat baseFrequency_; + std::vector attackRatios_; + std::vector loopRatios_; + StkFloat attackGain_; + StkFloat loopGain_; + +}; + +} // stk namespace + +#endif diff --git a/src/stk/include/Saxofony.h b/src/stk/include/Saxofony.h new file mode 100644 index 00000000..110ec029 --- /dev/null +++ b/src/stk/include/Saxofony.h @@ -0,0 +1,166 @@ +#ifndef STK_SAXOFONY_H +#define STK_SAXOFONY_H + +#include "Instrmnt.h" +#include "DelayL.h" +#include "ReedTable.h" +#include "OneZero.h" +#include "Envelope.h" +#include "Noise.h" +#include "SineWave.h" + +namespace stk { + +/***************************************************/ +/*! \class Saxofony + \brief STK faux conical bore reed instrument class. + + This class implements a "hybrid" digital + waveguide instrument that can generate a + variety of wind-like sounds. It has also been + referred to as the "blowed string" model. The + waveguide section is essentially that of a + string, with one rigid and one lossy + termination. The non-linear function is a + reed table. The string can be "blown" at any + point between the terminations, though just as + with strings, it is impossible to excite the + system at either end. If the excitation is + placed at the string mid-point, the sound is + that of a clarinet. At points closer to the + "bridge", the sound is closer to that of a + saxophone. See Scavone (2002) for more details. + + This is a digital waveguide model, making its + use possibly subject to patents held by Stanford + University, Yamaha, and others. + + Control Change Numbers: + - Reed Stiffness = 2 + - Reed Aperture = 26 + - Noise Gain = 4 + - Blow Position = 11 + - Vibrato Frequency = 29 + - Vibrato Gain = 1 + - Breath Pressure = 128 + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class Saxofony : public Instrmnt +{ + public: + //! Class constructor, taking the lowest desired playing frequency. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ + Saxofony( StkFloat lowestFrequency ); + + //! Class destructor. + ~Saxofony( void ); + + //! Reset and clear all internal state. + void clear( void ); + + //! Set instrument parameters for a particular frequency. + void setFrequency( StkFloat frequency ); + + //! Set the "blowing" position between the air column terminations (0.0 - 1.0). + void setBlowPosition( StkFloat aPosition ); + + //! Apply breath pressure to instrument with given amplitude and rate of increase. + void startBlowing( StkFloat amplitude, StkFloat rate ); + + //! Decrease breath pressure with given rate of decrease. + void stopBlowing( StkFloat rate ); + + //! Start a note with the given frequency and amplitude. + void noteOn( StkFloat frequency, StkFloat amplitude ); + + //! Stop a note with the given amplitude (speed of decay). + void noteOff( StkFloat amplitude ); + + //! Perform the control change specified by \e number and \e value (0.0 - 128.0). + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + + DelayL delays_[2]; + ReedTable reedTable_; + OneZero filter_; + Envelope envelope_; + Noise noise_; + SineWave vibrato_; + + StkFloat outputGain_; + StkFloat noiseGain_; + StkFloat vibratoGain_; + StkFloat position_; + +}; + +inline StkFloat Saxofony :: tick( unsigned int ) +{ + StkFloat pressureDiff; + StkFloat breathPressure; + StkFloat temp; + + // Calculate the breath pressure (envelope + noise + vibrato) + breathPressure = envelope_.tick(); + breathPressure += breathPressure * noiseGain_ * noise_.tick(); + breathPressure += breathPressure * vibratoGain_ * vibrato_.tick(); + + temp = -0.95 * filter_.tick( delays_[0].lastOut() ); + lastFrame_[0] = temp - delays_[1].lastOut(); + pressureDiff = breathPressure - lastFrame_[0]; + delays_[1].tick( temp ); + delays_[0].tick( breathPressure - (pressureDiff * reedTable_.tick(pressureDiff)) - temp ); + + lastFrame_[0] *= outputGain_; + return lastFrame_[0]; +} + +inline StkFrames& Saxofony :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "Saxofony::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i +#include + +namespace stk { + +/***************************************************/ +/*! \class Shakers + \brief PhISEM and PhOLIES class. + + PhISEM (Physically Informed Stochastic Event Modeling) is an + algorithmic approach for simulating collisions of multiple + independent sound producing objects. This class is a meta-model + that can simulate a Maraca, Sekere, Cabasa, Bamboo Wind Chimes, + Water Drops, Tambourine, Sleighbells, and a Guiro. + + PhOLIES (Physically-Oriented Library of Imitated Environmental + Sounds) is a similar approach for the synthesis of environmental + sounds. This class implements simulations of breaking sticks, + crunchy snow (or not), a wrench, sandpaper, and more. + + Control Change Numbers: + - Shake Energy = 2 + - System Decay = 4 + - Number Of Objects = 11 + - Resonance Frequency = 1 + - Shake Energy = 128 + - Instrument Selection = 1071 + - Maraca = 0 + - Cabasa = 1 + - Sekere = 2 + - Tambourine = 3 + - Sleigh Bells = 4 + - Bamboo Chimes = 5 + - Sand Paper = 6 + - Coke Can = 7 + - Sticks = 8 + - Crunch = 9 + - Big Rocks = 10 + - Little Rocks = 11 + - Next Mug = 12 + - Penny + Mug = 13 + - Nickle + Mug = 14 + - Dime + Mug = 15 + - Quarter + Mug = 16 + - Franc + Mug = 17 + - Peso + Mug = 18 + - Guiro = 19 + - Wrench = 20 + - Water Drops = 21 + - Tuned Bamboo Chimes = 22 + + by Perry R. Cook with updates by Gary Scavone, 1995--2021. +*/ +/***************************************************/ + +class Shakers : public Instrmnt +{ + public: + //! Class constructor taking instrument type argument. + Shakers( int type = 0 ); + + //! Start a note with the given instrument and amplitude. + /*! + Use the instrument numbers above, converted to frequency values + as if MIDI note numbers, to select a particular instrument. + */ + void noteOn( StkFloat instrument, StkFloat amplitude ); + + //! Stop a note with the given amplitude (speed of decay). + void noteOff( StkFloat amplitude ); + + //! Perform the control change specified by \e number and \e value (0.0 - 128.0). + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + struct BiQuad { + StkFloat gain; + StkFloat b[3]; + StkFloat a[3]; // a0 term assumed equal to 1.0 + StkFloat inputs[3]; + StkFloat outputs[3]; + + // Default constructor. + BiQuad() + { + gain = 0.0; + for ( int i=0; i<3; i++ ) { + b[i] = 0.0; + a[i] = 0.0; + inputs[i] = 0.0; + outputs[i] = 0.0; + } + } + }; + + protected: + + void setType( int type ); + void setResonance( BiQuad &filter, StkFloat frequency, StkFloat radius ); + StkFloat tickResonance( BiQuad &filter, StkFloat input ); + void setEqualization( StkFloat b0, StkFloat b1, StkFloat b2 ); + StkFloat tickEqualize( StkFloat input ); + int randomInt( int max ); + StkFloat randomFloat( StkFloat max = 1.0 ); + StkFloat noise( void ); + void waterDrop( void ); + + int shakerType_; + unsigned int nResonances_; + StkFloat shakeEnergy_; + StkFloat soundDecay_; + StkFloat systemDecay_; + StkFloat nObjects_; + StkFloat sndLevel_; + StkFloat baseGain_; + StkFloat currentGain_; + StkFloat baseDecay_; + StkFloat baseObjects_; + StkFloat decayScale_; + BiQuad equalizer_; + StkFloat ratchetCount_; + StkFloat ratchetDelta_; + StkFloat baseRatchetDelta_; + int lastRatchetValue_; + + std::vector< BiQuad > filters_; + std::vector< StkFloat > baseFrequencies_; + std::vector< StkFloat > baseRadii_; + std::vector< bool > doVaryFrequency_; + std::vector< StkFloat > tempFrequencies_; + StkFloat varyFactor_; +}; + +inline void Shakers :: setResonance( BiQuad &filter, StkFloat frequency, StkFloat radius ) +{ + filter.a[1] = -2.0 * radius * cos( TWO_PI * frequency / Stk::sampleRate()); + filter.a[2] = radius * radius; +} + +inline StkFloat Shakers :: tickResonance( BiQuad &filter, StkFloat input ) +{ + filter.outputs[0] = input * filter.gain * currentGain_; + filter.outputs[0] -= filter.a[1] * filter.outputs[1] + filter.a[2] * filter.outputs[2]; + filter.outputs[2] = filter.outputs[1]; + filter.outputs[1] = filter.outputs[0]; + return filter.outputs[0]; +} + +inline void Shakers :: setEqualization( StkFloat b0, StkFloat b1, StkFloat b2 ) +{ + equalizer_.b[0] = b0; + equalizer_.b[1] = b1; + equalizer_.b[2] = b2; +} + +inline StkFloat Shakers :: tickEqualize( StkFloat input ) +{ + equalizer_.inputs[0] = input; + equalizer_.outputs[0] = equalizer_.b[0] * equalizer_.inputs[0] + equalizer_.b[1] * equalizer_.inputs[1] + equalizer_.b[2] * equalizer_.inputs[2]; + equalizer_.inputs[2] = equalizer_.inputs[1]; + equalizer_.inputs[1] = equalizer_.inputs[0]; + return equalizer_.outputs[0]; +} + +inline int Shakers :: randomInt( int max ) // Return random integer between 0 and max-1 +{ + return (int) ((float)max * rand() / (RAND_MAX + 1.0) ); +} + +inline StkFloat Shakers :: randomFloat( StkFloat max ) // Return random float between 0.0 and max +{ + return (StkFloat) (max * rand() / (RAND_MAX + 1.0) ); +} + +inline StkFloat Shakers :: noise( void ) // Return random StkFloat float between -1.0 and 1.0 +{ + return ( (StkFloat) ( 2.0 * rand() / (RAND_MAX + 1.0) ) - 1.0 ); +} + +const StkFloat MIN_ENERGY = 0.001; +const StkFloat WATER_FREQ_SWEEP = 1.0001; + +inline void Shakers :: waterDrop( void ) +{ + if ( randomInt( 32767 ) < nObjects_) { + sndLevel_ = shakeEnergy_; + unsigned int j = randomInt( 3 ); + if ( j == 0 && filters_[0].gain == 0.0 ) { // don't change unless fully decayed + tempFrequencies_[0] = baseFrequencies_[1] * (0.75 + (0.25 * noise())); + filters_[0].gain = fabs( noise() ); + } + else if (j == 1 && filters_[1].gain == 0.0) { + tempFrequencies_[1] = baseFrequencies_[1] * (1.0 + (0.25 * noise())); + filters_[1].gain = fabs( noise() ); + } + else if ( filters_[2].gain == 0.0 ) { + tempFrequencies_[2] = baseFrequencies_[1] * (1.25 + (0.25 * noise())); + filters_[2].gain = fabs( noise() ); + } + } + + // Sweep center frequencies. + for ( unsigned int i=0; i<3; i++ ) { // WATER_RESONANCES = 3 + filters_[i].gain *= baseRadii_[i]; + if ( filters_[i].gain > 0.001 ) { + tempFrequencies_[i] *= WATER_FREQ_SWEEP; + filters_[i].a[1] = -2.0 * baseRadii_[i] * cos( TWO_PI * tempFrequencies_[i] / Stk::sampleRate() ); + } + else + filters_[i].gain = 0.0; + } +} + +inline StkFloat Shakers :: tick( unsigned int ) +{ + unsigned int iTube = 0; + StkFloat input = 0.0; + if ( shakerType_ == 19 || shakerType_ == 20 ) { + if ( ratchetCount_ <= 0 ) return lastFrame_[0] = 0.0; + + shakeEnergy_ -= ( ratchetDelta_ + ( 0.002 * shakeEnergy_ ) ); + if ( shakeEnergy_ < 0.0 ) { + shakeEnergy_ = 1.0; + ratchetCount_--; + } + + if ( randomFloat( 1024 ) < nObjects_ ) + sndLevel_ += shakeEnergy_ * shakeEnergy_; + + // Sound is enveloped noise + input = sndLevel_ * noise() * shakeEnergy_; + } + else { + if ( shakeEnergy_ < MIN_ENERGY ) return lastFrame_[0] = 0.0; + + // Exponential system decay + shakeEnergy_ *= systemDecay_; + + // Random events + if ( shakerType_ == 21 ) { + waterDrop(); + input = sndLevel_; + } + else { + if ( randomFloat( 1024.0 ) < nObjects_ ) { + sndLevel_ += shakeEnergy_; + input = sndLevel_; + // Vary resonance frequencies if specified. + for ( unsigned int i=0; i 1.0 ) + // std::cout << "lastOutput = " << lastFrame_[0] << std::endl; + + return lastFrame_[0]; +} + +inline StkFrames& Shakers :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "Shakers::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; itick(); + biquad_.tick( noise_.tick() ); + lastFrame_[0] += (1.0 - loopGain_) * biquad_.lastOut(); + lastFrame_[0] = filter_.tick( lastFrame_[0] ); + lastFrame_[0] *= adsr_.tick(); + return lastFrame_[0]; +} + +inline StkFrames& Simple :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "Simple::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i= TABLE_SIZE ) + time_ -= TABLE_SIZE; + + iIndex_ = (unsigned int) time_; + alpha_ = time_ - iIndex_; + StkFloat tmp = table_[ iIndex_ ]; + tmp += ( alpha_ * ( table_[ iIndex_ + 1 ] - tmp ) ); + + // Increment time, which can be negative. + time_ += rate_; + + lastFrame_[0] = tmp; + return lastFrame_[0]; +} + +inline StkFrames& SineWave :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "SineWave::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + StkFloat tmp = 0.0; + + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= TABLE_SIZE ) + time_ -= TABLE_SIZE; + + iIndex_ = (unsigned int) time_; + alpha_ = time_ - iIndex_; + tmp = table_[ iIndex_ ]; + tmp += ( alpha_ * ( table_[ iIndex_ + 1 ] - tmp ) ); + *samples = tmp; + + // Increment time, which can be negative. + time_ += rate_; + } + + lastFrame_[0] = tmp; + return frames; +} + +} // stk namespace + +#endif + diff --git a/src/stk/include/SingWave.h b/src/stk/include/SingWave.h new file mode 100644 index 00000000..34107b69 --- /dev/null +++ b/src/stk/include/SingWave.h @@ -0,0 +1,136 @@ +#ifndef STK_SINGWAVE_H +#define STK_SINGWAVE_H + +#include "FileLoop.h" +#include "Modulate.h" +#include "Envelope.h" + +namespace stk { + +/***************************************************/ +/*! \class SingWave + \brief STK "singing" looped soundfile class. + + This class loops a specified soundfile and modulates it both + periodically and randomly to produce a pitched musical sound, like + a simple voice or violin. In general, it is not be used alone + because of "munchkinification" effects from pitch shifting. + Within STK, it is used as an excitation source for other + instruments. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class SingWave : public Generator +{ + public: + //! Class constructor taking filename argument. + /*! + An StkError will be thrown if the file is not found, its format + is unknown, or a read error occurs. If the soundfile has no + header, the second argument should be \e true and the file data + will be assumed to consist of 16-bit signed integers in big-endian + byte order at a sample rate of 22050 Hz. + */ + SingWave( std::string fileName, bool raw = false ); + + //! Class destructor. + ~SingWave( void ); + + //! Reset file to beginning. + void reset( void ) { wave_.reset(); lastFrame_[0] = 0.0; }; + + //! Normalize the file to a maximum of +-1.0. + void normalize( void ) { wave_.normalize(); }; + + //! Normalize the file to a maximum of \e +- peak. + void normalize( StkFloat peak ) { wave_.normalize( peak ); }; + + //! Set looping parameters for a particular frequency. + void setFrequency( StkFloat frequency ); + + //! Set the vibrato frequency in Hz. + void setVibratoRate( StkFloat rate ) { modulator_.setVibratoRate( rate ); }; + + //! Set the vibrato gain. + void setVibratoGain( StkFloat gain ) { modulator_.setVibratoGain( gain ); }; + + //! Set the random-ness amount. + void setRandomGain( StkFloat gain ) { modulator_.setRandomGain( gain ); }; + + //! Set the sweep rate. + void setSweepRate( StkFloat rate ) { sweepRate_ = rate; }; + + //! Set the gain rate. + void setGainRate( StkFloat rate ) { envelope_.setRate( rate ); }; + + //! Set the gain target value. + void setGainTarget( StkFloat target ) { envelope_.setTarget( target ); }; + + //! Start a note. + void noteOn( void ) { envelope_.keyOn(); }; + + //! Stop a note. + void noteOff( void ) { envelope_.keyOff(); }; + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Compute and return one output sample. + StkFloat tick( void ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + + FileLoop wave_; + Modulate modulator_; + Envelope envelope_; + Envelope pitchEnvelope_; + StkFloat rate_; + StkFloat sweepRate_; + +}; + +inline StkFloat SingWave :: tick( void ) +{ + // Set the wave rate. + StkFloat newRate = pitchEnvelope_.tick(); + newRate += newRate * modulator_.tick(); + wave_.setRate( newRate ); + + lastFrame_[0] = wave_.tick(); + lastFrame_[0] *= envelope_.tick(); + + return lastFrame_[0]; +} + +inline StkFrames& SingWave :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "SingWave::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i + +namespace stk { + +/***************************************************/ +/*! \class Sitar + \brief STK sitar string model class. + + This class implements a sitar plucked string + physical model based on the Karplus-Strong + algorithm. + + This is a digital waveguide model, making its + use possibly subject to patents held by + Stanford University, Yamaha, and others. + There exist at least two patents, assigned to + Stanford, bearing the names of Karplus and/or + Strong. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class Sitar : public Instrmnt +{ + public: + //! Class constructor, taking the lowest desired playing frequency. + Sitar( StkFloat lowestFrequency = 8.0 ); + + //! Class destructor. + ~Sitar( void ); + + //! Reset and clear all internal state. + void clear( void ); + + //! Set instrument parameters for a particular frequency. + void setFrequency( StkFloat frequency ); + + //! Pluck the string with the given amplitude using the current frequency. + void pluck( StkFloat amplitude ); + + //! Start a note with the given frequency and amplitude. + void noteOn( StkFloat frequency, StkFloat amplitude ); + + //! Stop a note with the given amplitude (speed of decay). + void noteOff( StkFloat amplitude ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + + DelayA delayLine_; + OneZero loopFilter_; + Noise noise_; + ADSR envelope_; + + StkFloat loopGain_; + StkFloat amGain_; + StkFloat delay_; + StkFloat targetDelay_; + +}; + +inline StkFloat Sitar :: tick( unsigned int ) +{ + if ( fabs(targetDelay_ - delay_) > 0.001 ) { + if ( targetDelay_ < delay_ ) + delay_ *= 0.99999; + else + delay_ *= 1.00001; + delayLine_.setDelay( delay_ ); + } + + lastFrame_[0] = delayLine_.tick( loopFilter_.tick( delayLine_.lastOut() * loopGain_ ) + + (amGain_ * envelope_.tick() * noise_.tick())); + + return lastFrame_[0]; +} + +inline StkFrames& Sitar :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "Sitar::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i +#include +#include + +namespace stk { + +/***************************************************/ +/*! \class Skini + \brief STK SKINI parsing class + + This class parses SKINI formatted text + messages. It can be used to parse individual + messages or it can be passed an entire file. + The SKINI specification is Perry's and his + alone, but it's all text so it shouldn't be too + hard to figure out. + + SKINI (Synthesis toolKit Instrument Network + Interface) is like MIDI, but allows for + floating-point control changes, note numbers, + etc. The following example causes a sharp + middle C to be played with a velocity of 111.132: + + \code + noteOn 60.01 111.132 + \endcode + + \sa \ref skini + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class Skini : public Stk +{ + public: + + //! A message structure to store and pass parsed SKINI messages. + struct Message { + long type; /*!< The message type, as defined in SKINImsg.h. */ + long channel; /*!< The message channel (not limited to 16!). */ + StkFloat time; /*!< The message time stamp in seconds (delta or absolute). */ + std::vector floatValues; /*!< The message values read as floats (values are type-specific). */ + std::vector intValues; /*!< The message values read as ints (number and values are type-specific). */ + std::string remainder; /*!< Any remaining message data, read as ascii text. */ + + // Default constructor. + Message() + :type(0), channel(0), time(0.0), floatValues(2), intValues(2) {} + }; + + //! Default constructor. + Skini(); + + //! Class destructor + ~Skini(); + + //! Set a SKINI formatted file for reading. + /*! + If the file is successfully opened, this function returns \e + true. Otherwise, \e false is returned. + */ + bool setFile( std::string fileName ); + + //! Parse the next file message (if a file is loaded) and return the message type. + /*! + This function skips over lines in a file which cannot be + parsed. A type value equal to zero in the referenced message + structure (and the returned value) indicates the file end is + reached or no file is open for reading. + */ + long nextMessage( Skini::Message& message ); + + //! Attempt to parse the given string and returning the message type. + /*! + A type value equal to zero in the referenced message structure + indicates an invalid message. + */ + long parseString( std::string& line, Skini::Message& message ); + + //! Return the SKINI type string for the given type value. + static std::string whatsThisType(long type); + + //! Return the SKINI controller string for the given controller number. + static std::string whatsThisController(long number); + + protected: + + void tokenize( const std::string& str, std::vector& tokens, const std::string& delimiters ); + + std::ifstream file_; +}; + +//! A static table of equal-tempered MIDI to frequency (Hz) values. +static const double Midi2Pitch[129] = { + 8.176, 8.662, 9.177, 9.723, 10.301, 10.913, 11.562, 12.25, + 12.978, 13.75, 14.568, 15.434, 16.352, 17.324, 18.354, 19.445, + 20.602, 21.827, 23.125, 24.50, 25.957, 27.50, 29.135, 30.868, + 32.703, 34.648, 36.708, 38.891, 41.203, 43.654, 46.249, 49.0, + 51.913, 55.0, 58.271, 61.735, 65.406, 69.296, 73.416, 77.782, + 82.407, 87.307, 92.499, 97.999, 103.826, 110.0, 116.541, 123.471, + 130.813, 138.591, 146.832, 155.563, 164.814, 174.614, 184.997, 195.998, + 207.652, 220.0, 233.082, 246.942, 261.626, 277.183, 293.665, 311.127, + 329.628, 349.228, 369.994, 391.995, 415.305, 440.0, 466.164, 493.883, + 523.251, 554.365, 587.33, 622.254, 659.255, 698.456, 739.989, 783.991, + 830.609, 880.0, 932.328, 987.767, 1046.502, 1108.731, 1174.659, 1244.508, + 1318.51, 1396.913, 1479.978, 1567.982, 1661.219, 1760.0, 1864.655, 1975.533, + 2093.005, 2217.461, 2349.318, 2489.016, 2637.02, 2793.826, 2959.955, 3135.963, + 3322.438, 3520.0, 3729.31, 3951.066, 4186.009, 4434.922, 4698.636, 4978.032, + 5274.041, 5587.652, 5919.911, 6271.927, 6644.875, 7040.0, 7458.62, 7902.133, + 8372.018, 8869.844, 9397.273, 9956.063, 10548.082, 11175.303, 11839.822, 12543.854, + 13289.75}; + +} // stk namespace + +#endif + + diff --git a/src/stk/include/Sphere.h b/src/stk/include/Sphere.h new file mode 100644 index 00000000..f54ada5e --- /dev/null +++ b/src/stk/include/Sphere.h @@ -0,0 +1,79 @@ +#ifndef STK_SPHERE_H +#define STK_SPHERE_H + +#include "Stk.h" +#include "Vector3D.h" + +namespace stk { + +/***************************************************/ +/*! \class Sphere + \brief STK sphere class. + + This class implements a spherical ball with + radius, mass, position, and velocity parameters. + + by Perry R. Cook, 1995--2021. +*/ +/***************************************************/ + +class Sphere : public Stk +{ +public: + //! Constructor taking an initial radius value. + Sphere( StkFloat radius = 1.0 ) { radius_ = radius; mass_ = 1.0; }; + + //! Set the 3D center position of the sphere. + void setPosition( StkFloat x, StkFloat y, StkFloat z ) { position_.setXYZ(x, y, z); }; + + //! Set the 3D velocity of the sphere. + void setVelocity( StkFloat x, StkFloat y, StkFloat z ) { velocity_.setXYZ(x, y, z); }; + + //! Set the radius of the sphere. + void setRadius( StkFloat radius ) { radius_ = radius; }; + + //! Set the mass of the sphere. + void setMass( StkFloat mass ) { mass_ = mass; }; + + //! Get the current position of the sphere as a 3D vector. + Vector3D* getPosition( void ) { return &position_; }; + + //! Get the relative position of the given point to the sphere as a 3D vector. + Vector3D* getRelativePosition( Vector3D *position ); + + //! Set the velocity of the sphere as a 3D vector. + StkFloat getVelocity( Vector3D* velocity ); + + //! Returns the distance from the sphere boundary to the given position (< 0 if inside). + StkFloat isInside( Vector3D *position ); + + //! Get the current sphere radius. + StkFloat getRadius( void ) { return radius_; }; + + //! Get the current sphere mass. + StkFloat getMass( void ) { return mass_; }; + + //! Increase the current sphere velocity by the given 3D components. + void addVelocity( StkFloat x, StkFloat y, StkFloat z ); + + //! Move the sphere for the given time increment. + void tick( StkFloat timeIncrement ); + +private: + Vector3D position_; + Vector3D velocity_; + Vector3D workingVector_; + StkFloat radius_; + StkFloat mass_; +}; + +inline void Sphere::tick( StkFloat timeIncrement ) +{ + position_.setX(position_.getX() + (timeIncrement * velocity_.getX())); + position_.setY(position_.getY() + (timeIncrement * velocity_.getY())); + position_.setZ(position_.getZ() + (timeIncrement * velocity_.getZ())); +}; + +} // stk namespace + +#endif diff --git a/src/stk/include/StifKarp.h b/src/stk/include/StifKarp.h new file mode 100644 index 00000000..51ed96fd --- /dev/null +++ b/src/stk/include/StifKarp.h @@ -0,0 +1,153 @@ +#ifndef STK_STIFKARP_H +#define STK_STIFKARP_H + +#include "Instrmnt.h" +#include "DelayL.h" +#include "DelayA.h" +#include "OneZero.h" +#include "Noise.h" +#include "BiQuad.h" + +namespace stk { + +/***************************************************/ +/*! \class StifKarp + \brief STK plucked stiff string instrument. + + This class implements a simple plucked string + algorithm (Karplus Strong) with enhancements + (Jaffe-Smith, Smith, and others), including + string stiffness and pluck position controls. + The stiffness is modeled with allpass filters. + + This is a digital waveguide model, making its + use possibly subject to patents held by + Stanford University, Yamaha, and others. + + Control Change Numbers: + - Pickup Position = 4 + - String Sustain = 11 + - String Stretch = 1 + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class StifKarp : public Instrmnt +{ + public: + //! Class constructor, taking the lowest desired playing frequency. + StifKarp( StkFloat lowestFrequency = 10.0 ); + + //! Class destructor. + ~StifKarp( void ); + + //! Reset and clear all internal state. + void clear( void ); + + //! Set instrument parameters for a particular frequency. + void setFrequency( StkFloat frequency ); + + //! Set the stretch "factor" of the string (0.0 - 1.0). + void setStretch( StkFloat stretch ); + + //! Set the pluck or "excitation" position along the string (0.0 - 1.0). + void setPickupPosition( StkFloat position ); + + //! Set the base loop gain. + /*! + The actual loop gain is set according to the frequency. + Because of high-frequency loop filter roll-off, higher + frequency settings have greater loop gains. + */ + void setBaseLoopGain( StkFloat aGain ); + + //! Pluck the string with the given amplitude using the current frequency. + void pluck( StkFloat amplitude ); + + //! Start a note with the given frequency and amplitude. + void noteOn( StkFloat frequency, StkFloat amplitude ); + + //! Stop a note with the given amplitude (speed of decay). + void noteOff( StkFloat amplitude ); + + //! Perform the control change specified by \e number and \e value (0.0 - 128.0). + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + + DelayA delayLine_; + DelayL combDelay_; + OneZero filter_; + Noise noise_; + BiQuad biquad_[4]; + + StkFloat loopGain_; + StkFloat baseLoopGain_; + StkFloat lastFrequency_; + StkFloat lastLength_; + StkFloat stretching_; + StkFloat pluckAmplitude_; + StkFloat pickupPosition_; + +}; + +inline StkFloat StifKarp :: tick( unsigned int ) +{ + StkFloat temp = delayLine_.lastOut() * loopGain_; + + // Calculate allpass stretching. + for (int i=0; i<4; i++) + temp = biquad_[i].tick(temp); + + // Moving average filter. + temp = filter_.tick(temp); + + lastFrame_[0] = delayLine_.tick(temp); + lastFrame_[0] = lastFrame_[0] - combDelay_.tick( lastFrame_[0] ); + return lastFrame_[0]; +} + +inline StkFrames& StifKarp :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "StifKarp::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i +#include +#include +#include +#include +#include +//#include + +/*! \namespace stk + \brief The STK namespace. + + Most Stk classes are defined within the STK namespace. Exceptions + to this include the classes RtAudio and RtMidi. +*/ +namespace stk { + +/***************************************************/ +/*! \class Stk + \brief STK base class + + Nearly all STK classes inherit from this class. + The global sample rate and rawwave path variables + can be queried and modified via Stk. In addition, + this class provides error handling and + byte-swapping functions. + + The Synthesis ToolKit in C++ (STK) is a set of open source audio + signal processing and algorithmic synthesis classes written in the + C++ programming language. STK was designed to facilitate rapid + development of music synthesis and audio processing software, with + an emphasis on cross-platform functionality, realtime control, + ease of use, and educational example code. STK currently runs + with realtime support (audio and MIDI) on Linux, Macintosh OS X, + and Windows computer platforms. Generic, non-realtime support has + been tested under NeXTStep, Sun, and other platforms and should + work with any standard C++ compiler. + + STK WWW site: http://ccrma.stanford.edu/software/stk/ + + The Synthesis ToolKit in C++ (STK) + Copyright (c) 1995--2021 Perry R. Cook and Gary P. Scavone + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + asked to send the modifications to the original developer so that + they can be incorporated into the canonical version. This is, + however, not a binding provision of this license. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +/***************************************************/ + +//#define _STK_DEBUG_ + +// Most data in STK is passed and calculated with the +// following user-definable floating-point type. You +// can change this to "float" if you prefer or perhaps +// a "long double" in the future. +typedef double StkFloat; + +//! STK error handling class. +/*! + This is a fairly abstract exception handling class. There could + be sub-classes to take care of more specific error conditions ... or + not. +*/ +class StkError : public std::exception +{ +public: + enum Type { + STATUS, + WARNING, + DEBUG_PRINT, + MEMORY_ALLOCATION, + MEMORY_ACCESS, + FUNCTION_ARGUMENT, + FILE_NOT_FOUND, + FILE_UNKNOWN_FORMAT, + FILE_ERROR, + PROCESS_THREAD, + PROCESS_SOCKET, + PROCESS_SOCKET_IPADDR, + AUDIO_SYSTEM, + MIDI_SYSTEM, + UNSPECIFIED + }; + +protected: + std::string message_; + Type type_; + +public: + //! The constructor. + StkError(const std::string& message, Type type = StkError::UNSPECIFIED) + : std::exception(), message_(message), type_(type) {} + + //! The destructor. + virtual ~StkError(void) throw() {}; + + //! Prints thrown error message to stderr. + virtual void printMessage(void) { std::cerr << '\n' << message_ << "\n\n"; } + + //! Returns the thrown error message type. + virtual const Type& getType(void) { return type_; } + + //! Returns the thrown error message string. + virtual const std::string& getMessage(void) { return message_; } + + //! Returns the thrown error message as a C string. + virtual const char *getMessageCString(void) { return message_.c_str(); } + + virtual const char *what(void) const throw() { return message_.c_str(); } +}; + + +class Stk +{ +public: + + typedef unsigned long StkFormat; + static const StkFormat STK_SINT8; /*!< -128 to +127 */ + static const StkFormat STK_SINT16; /*!< -32768 to +32767 */ + static const StkFormat STK_SINT24; /*!< Lower 3 bytes of 32-bit signed integer. */ + static const StkFormat STK_SINT32; /*!< -2147483648 to +2147483647. */ + static const StkFormat STK_FLOAT32; /*!< Normalized between plus/minus 1.0. */ + static const StkFormat STK_FLOAT64; /*!< Normalized between plus/minus 1.0. */ + + //! Static method that returns the current STK sample rate. + static StkFloat sampleRate( void ) { return srate_; } + + //! Static method that sets the STK sample rate. + /*! + The sample rate set using this method is queried by all STK + classes that depend on its value. It is initialized to the + default SRATE set in Stk.h. Many STK classes use the sample rate + during instantiation. Therefore, if you wish to use a rate that + is different from the default rate, it is imperative that it be + set \e BEFORE STK objects are instantiated. A few classes that + make use of the global STK sample rate are automatically notified + when the rate changes so that internal class data can be + appropriately updated. However, this has not been fully + implemented. Specifically, classes that appropriately update + their own data when either a setFrequency() or noteOn() function + is called do not currently receive the automatic notification of + rate change. If the user wants a specific class instance to + ignore such notifications, perhaps in a multi-rate context, the + function Stk::ignoreSampleRateChange() should be called. + */ + static void setSampleRate( StkFloat rate ); + + //! A function to enable/disable the automatic updating of class data when the STK sample rate changes. + /*! + This function allows the user to enable or disable class data + updates in response to global sample rate changes on a class by + class basis. + */ + void ignoreSampleRateChange( bool ignore = true ) { ignoreSampleRateChange_ = ignore; }; + + //! Static method that frees memory from alertList_. + static void clear_alertList(){std::vector().swap(alertList_);}; + + //! Static method that returns the current rawwave path. + static std::string rawwavePath(void) { return rawwavepath_; } + + //! Static method that sets the STK rawwave path. + static void setRawwavePath( std::string path ); + + //! Static method that byte-swaps a 16-bit data type. + static void swap16( unsigned char *ptr ); + + //! Static method that byte-swaps a 32-bit data type. + static void swap32( unsigned char *ptr ); + + //! Static method that byte-swaps a 64-bit data type. + static void swap64( unsigned char *ptr ); + + //! Static cross-platform method to sleep for a number of milliseconds. + static void sleep( unsigned long milliseconds ); + + //! Static method to check whether a value is within a specified range. + static bool inRange( StkFloat value, StkFloat min, StkFloat max ) { + if ( value < min ) return false; + else if ( value > max ) return false; + else return true; + } + + //! Static function for error reporting and handling using c-strings. + static void handleError( const char *message, StkError::Type type ); + + //! Static function for error reporting and handling using c++ strings. + static void handleError( std::string message, StkError::Type type ); + + //! Toggle display of WARNING and STATUS messages. + static void showWarnings( bool status ) { showWarnings_ = status; } + + //! Toggle display of error messages before throwing exceptions. + static void printErrors( bool status ) { printErrors_ = status; } + +private: + static StkFloat srate_; + static std::string rawwavepath_; + static bool showWarnings_; + static bool printErrors_; + static std::vector alertList_; + +protected: + + static std::ostringstream oStream_; + bool ignoreSampleRateChange_; + + //! Default constructor. + Stk( void ); + + //! Class destructor. + virtual ~Stk( void ); + + //! This function should be implemented in subclasses that depend on the sample rate. + virtual void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); + + //! Add class pointer to list for sample rate change notification. + void addSampleRateAlert( Stk *ptr ); + + //! Remove class pointer from list for sample rate change notification. + void removeSampleRateAlert( Stk *ptr ); + + //! Internal function for error reporting that assumes message in \c oStream_ variable. + void handleError( StkError::Type type ) const; +}; + + +/***************************************************/ +/*! \class StkFrames + \brief An STK class to handle vectorized audio data. + + This class can hold single- or multi-channel audio data. The data + type is always StkFloat and the channel format is always + interleaved. In an effort to maintain efficiency, no + out-of-bounds checks are performed in this class unless + _STK_DEBUG_ is defined. + + Internally, the data is stored in a one-dimensional C array. An + indexing operator is available to set and retrieve data values. + Alternately, one can use pointers to access the data, using the + index operator to get an address for a particular location in the + data: + + StkFloat* ptr = &myStkFrames[0]; + + Note that this class can also be used as a table with interpolating + lookup. + + Possible future improvements in this class could include functions + to convert to and return other data types. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class StkFrames +{ +public: + + //! The default constructor initializes the frame data structure to size zero. + StkFrames( unsigned int nFrames = 0, unsigned int nChannels = 0 ); + + //! Overloaded constructor that initializes the frame data to the specified size with \c value. + StkFrames( const StkFloat& value, unsigned int nFrames, unsigned int nChannels ); + + //! The destructor. + virtual ~StkFrames(); + + // A copy constructor. + StkFrames( const StkFrames& f ); + + // Assignment operator that returns a reference to self. + virtual StkFrames& operator= ( const StkFrames& f ); + + //! Subscript operator that returns a reference to element \c n of self. + /*! + The result can be used as an lvalue. This reference is valid + until the resize function is called or the array is destroyed. The + index \c n must be between 0 and size less one. No range checking + is performed unless _STK_DEBUG_ is defined. + */ + StkFloat& operator[] ( size_t n ); + + //! Subscript operator that returns the value at element \c n of self. + /*! + The index \c n must be between 0 and size less one. No range + checking is performed unless _STK_DEBUG_ is defined. + */ + StkFloat operator[] ( size_t n ) const; + + //! Sum operator + /*! + The dimensions of the argument are expected to be the same as + self. No range checking is performed unless _STK_DEBUG_ is + defined. + */ + StkFrames operator+(const StkFrames &frames) const; + + //! Assignment by sum operator into self. + /*! + The dimensions of the argument are expected to be the same as + self. No range checking is performed unless _STK_DEBUG_ is + defined. + */ + StkFrames& operator+= ( StkFrames& f ); + + //! Assignment by product operator into self. + /*! + The dimensions of the argument are expected to be the same as + self. No range checking is performed unless _STK_DEBUG_ is + defined. + */ + StkFrames& operator*= ( StkFrames& f ); + + //! Scaling operator (StkFrame * StkFloat). + StkFrames operator* ( StkFloat v ) const; + + //! Scaling operator (StkFloat * StkFrame) + friend StkFrames operator*(StkFloat v, const StkFrames& f); + + //! Scaling operator (inline). + StkFrames& operator*= ( StkFloat v ); + + //! Channel / frame subscript operator that returns a reference. + /*! + The result can be used as an lvalue. This reference is valid + until the resize function is called or the array is destroyed. The + \c frame index must be between 0 and frames() - 1. The \c channel + index must be between 0 and channels() - 1. No range checking is + performed unless _STK_DEBUG_ is defined. + */ + StkFloat& operator() ( size_t frame, unsigned int channel ); + + //! Channel / frame subscript operator that returns a value. + /*! + The \c frame index must be between 0 and frames() - 1. The \c + channel index must be between 0 and channels() - 1. No range checking + is performed unless _STK_DEBUG_ is defined. + */ + StkFloat operator() ( size_t frame, unsigned int channel ) const; + + //! Return an interpolated value at the fractional frame index and channel. + /*! + This function performs linear interpolation. The \c frame + index must be between 0.0 and frames() - 1. The \c channel index + must be between 0 and channels() - 1. No range checking is + performed unless _STK_DEBUG_ is defined. + */ + StkFloat interpolate( StkFloat frame, unsigned int channel = 0 ) const; + + //! Returns the total number of audio samples represented by the object. + size_t size() const { return size_; }; + + //! Returns \e true if the object size is zero and \e false otherwise. + bool empty() const; + + //! Resize self to represent the specified number of channels and frames. + /*! + Changes the size of self based on the number of frames and + channels. No element assignment is performed. No memory + deallocation occurs if the new size is smaller than the previous + size. Further, no new memory is allocated when the new size is + smaller or equal to a previously allocated size. + */ + virtual void resize( size_t nFrames, unsigned int nChannels = 1 ); + + //! Resize self to represent the specified number of channels and frames and perform element initialization. + /*! + Changes the size of self based on the number of frames and + channels, and assigns \c value to every element. No memory + deallocation occurs if the new size is smaller than the previous + size. Further, no new memory is allocated when the new size is + smaller or equal to a previously allocated size. + */ + virtual void resize( size_t nFrames, unsigned int nChannels, StkFloat value ); + + //! Retrieves a single channel + /*! + Copies the specified \c channel into \c destinationFrames's \c destinationChannel. \c destinationChannel must be between 0 and destination.channels() - 1 and + \c channel must be between 0 and channels() - 1. destination.frames() must be >= frames(). + No range checking is performed unless _STK_DEBUG_ is defined. + */ + StkFrames& getChannel(unsigned int channel,StkFrames& destinationFrames, unsigned int destinationChannel) const; + + //! Sets a single channel + /*! + Copies the \c sourceChannel of \c sourceFrames into the \c channel of self. + SourceFrames.frames() must be equal to frames(). + No range checking is performed unless _STK_DEBUG_ is defined. + */ + void setChannel(unsigned int channel,const StkFrames &sourceFrames,unsigned int sourceChannel); + + //! Return the number of channels represented by the data. + unsigned int channels( void ) const { return nChannels_; }; + + //! Return the number of sample frames represented by the data. + unsigned int frames( void ) const { return (unsigned int)nFrames_; }; + + //! Set the sample rate associated with the StkFrames data. + /*! + By default, this value is set equal to the current STK sample + rate at the time of instantiation. + */ + void setDataRate( StkFloat rate ) { dataRate_ = rate; }; + + //! Return the sample rate associated with the StkFrames data. + /*! + By default, this value is set equal to the current STK sample + rate at the time of instantiation. + */ + StkFloat dataRate( void ) const { return dataRate_; }; + +protected: + + StkFloat *data_; + StkFloat dataRate_; + size_t nFrames_; + unsigned int nChannels_; + size_t size_; + size_t bufferSize_; + +}; + +inline bool StkFrames :: empty() const +{ + if ( size_ > 0 ) return false; + else return true; +} + +inline StkFloat& StkFrames :: operator[] ( size_t n ) +{ +#if defined(_STK_DEBUG_) + if ( n >= size_ ) { + std::ostringstream error; + error << "StkFrames::operator[]: invalid index (" << n << ") value!"; + Stk::handleError( error.str(), StkError::MEMORY_ACCESS ); + } +#endif + + return data_[n]; +} + +inline StkFloat StkFrames :: operator[] ( size_t n ) const +{ +#if defined(_STK_DEBUG_) + if ( n >= size_ ) { + std::ostringstream error; + error << "StkFrames::operator[]: invalid index (" << n << ") value!"; + Stk::handleError( error.str(), StkError::MEMORY_ACCESS ); + } +#endif + + return data_[n]; +} + +inline StkFloat& StkFrames :: operator() ( size_t frame, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( frame >= nFrames_ || channel >= nChannels_ ) { + std::ostringstream error; + error << "StkFrames::operator(): invalid frame (" << frame << ") or channel (" << channel << ") value!"; + Stk::handleError( error.str(), StkError::MEMORY_ACCESS ); + } +#endif + + return data_[ frame * nChannels_ + channel ]; +} + +inline StkFloat StkFrames :: operator() ( size_t frame, unsigned int channel ) const +{ +#if defined(_STK_DEBUG_) + if ( frame >= nFrames_ || channel >= nChannels_ ) { + std::ostringstream error; + error << "StkFrames::operator(): invalid frame (" << frame << ") or channel (" << channel << ") value!"; + Stk::handleError( error.str(), StkError::MEMORY_ACCESS ); + } +#endif + + return data_[ frame * nChannels_ + channel ]; +} + +inline StkFrames StkFrames::operator+(const StkFrames &f) const +{ +#if defined(_STK_DEBUG_) + if ( f.frames() != nFrames_ || f.channels() != nChannels_ ) { + std::ostringstream error; + error << "StkFrames::operator+: frames argument must be of equal dimensions!"; + Stk::handleError( error.str(), StkError::MEMORY_ACCESS ); + } +#endif + StkFrames sum((unsigned int)nFrames_,nChannels_); + StkFloat *sumPtr = &sum[0]; + const StkFloat *fptr = f.data_; + const StkFloat *dPtr = data_; + for (unsigned int i = 0; i < size_; i++) { + *sumPtr++ = *fptr++ + *dPtr++; + } + return sum; +} + +inline StkFrames& StkFrames :: operator+= ( StkFrames& f ) +{ +#if defined(_STK_DEBUG_) + if ( f.frames() != nFrames_ || f.channels() != nChannels_ ) { + std::ostringstream error; + error << "StkFrames::operator+=: frames argument must be of equal dimensions!"; + Stk::handleError( error.str(), StkError::MEMORY_ACCESS ); + } +#endif + + StkFloat *fptr = &f[0]; + StkFloat *dptr = data_; + for ( unsigned int i=0; i taps = std::vector( 1, 0 ), unsigned long maxDelay = 4095 ); + + //! Class destructor. + ~TapDelay(); + + //! Set the maximum delay-line length. + /*! + This method should generally only be used during initial setup + of the delay line. If it is used between calls to the tick() + function, without a call to clear(), a signal discontinuity will + likely occur. If the current maximum length is greater than the + new length, no change will be made. + */ + void setMaximumDelay( unsigned long delay ); + + //! Set the delay-line tap lengths. + /*! + The valid range for each tap length is from 0 to the maximum delay-line length. + */ + void setTapDelays( std::vector taps ); + + //! Return the current delay-line length. + std::vector getTapDelays( void ) const { return delays_; }; + + //! Return the specified tap value of the last computed frame. + /*! + Use the lastFrame() function to get all tap values from the + last computed frame. The \c tap argument must be less than the + number of delayline taps (the first tap is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFloat lastOut( unsigned int tap = 0 ) const; + + //! Input one sample to the delayline and return outputs at all tap positions. + /*! + The StkFrames argument reference is returned. The output + values are ordered according to the tap positions set using the + setTapDelays() function (no sorting is performed). The StkFrames + argument must contain at least as many channels as the number of + taps. However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFloat input, StkFrames& outputs ); + + //! Take a channel of the StkFrames object as inputs to the filter and write outputs back to the same object. + /*! + The StkFrames argument reference is returned. The output + values are ordered according to the tap positions set using the + setTapDelays() function (no sorting is performed). The StkFrames + argument must contain at least as many channels as the number of + taps. However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the filter and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. The output values + are ordered according to the tap positions set using the + setTapDelays() function (no sorting is performed). The \c + iChannel argument must be less than the number of channels in + the \c iFrames argument (the first channel is specified by 0). + The \c oFrames argument must contain at least as many channels as + the number of taps. However, range checking is only performed if + _STK_DEBUG_ is defined during compilation, in which case an + out-of-range value will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0 ); + + protected: + + unsigned long inPoint_; + std::vector outPoint_; + std::vector delays_; + +}; + +inline StkFloat TapDelay :: lastOut( unsigned int tap ) const +{ +#if defined(_STK_DEBUG_) + if ( tap >= lastFrame_.size() ) { + oStream_ << "TapDelay::lastOut(): tap argument and number of taps are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + return lastFrame_[tap]; +} + +inline StkFrames& TapDelay :: tick( StkFloat input, StkFrames& outputs ) +{ +#if defined(_STK_DEBUG_) + if ( outputs.channels() < outPoint_.size() ) { + oStream_ << "TapDelay::tick(): number of taps > channels in StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + inputs_[inPoint_++] = input * gain_; + + // Check for end condition + if ( inPoint_ == inputs_.size() ) + inPoint_ = 0; + + // Read out next values + StkFloat *outs = &outputs[0]; + for ( unsigned int i=0; i= frames.channels() ) { + oStream_ << "TapDelay::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + if ( frames.channels() < outPoint_.size() ) { + oStream_ << "TapDelay::tick(): number of taps > channels in StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &frames[channel]; + StkFloat *oSamples = &frames[0]; + std::size_t j; + unsigned int iHop = frames.channels(); + std::size_t oHop = frames.channels() - outPoint_.size(); + for ( unsigned long i=0; i= iFrames.channels() ) { + oStream_ << "TapDelay::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + if ( oFrames.channels() < outPoint_.size() ) { + oStream_ << "TapDelay::tick(): number of taps > channels in output StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[0]; + std::size_t j; + unsigned int iHop = iFrames.channels(); + std::size_t oHop = oFrames.channels() - outPoint_.size(); + for ( unsigned long i=0; i3--\ + + --> Out + 2->1--/ + \endcode + + Control Change Numbers: + - Modulator Index One = 2 + - Crossfade of Outputs = 4 + - LFO Speed = 11 + - LFO Depth = 1 + - ADSR 2 & 4 Target = 128 + + The basic Chowning/Stanford FM patent expired + in 1995, but there exist follow-on patents, + mostly assigned to Yamaha. If you are of the + type who should worry about this (making + money) worry away. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class TubeBell : public FM +{ + public: + //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ + TubeBell( void ); + + //! Class destructor. + ~TubeBell( void ); + + //! Start a note with the given frequency and amplitude. + void noteOn( StkFloat frequency, StkFloat amplitude ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + +}; + +inline StkFloat TubeBell :: tick( unsigned int ) +{ + StkFloat temp, temp2; + + temp = gains_[1] * adsr_[1]->tick() * waves_[1]->tick(); + temp = temp * control1_; + + waves_[0]->addPhaseOffset( temp ); + waves_[3]->addPhaseOffset( twozero_.lastOut() ); + temp = gains_[3] * adsr_[3]->tick() * waves_[3]->tick(); + twozero_.tick( temp ); + + waves_[2]->addPhaseOffset( temp ); + temp = ( 1.0 - (control2_ * 0.5)) * gains_[0] * adsr_[0]->tick() * waves_[0]->tick(); + temp += control2_ * 0.5 * gains_[2] * adsr_[2]->tick() * waves_[2]->tick(); + + // Calculate amplitude modulation and apply it to output. + temp2 = vibrato_.tick() * modDepth_; + temp = temp * (1.0 + temp2); + + lastFrame_[0] = temp * 0.5; + return lastFrame_[0]; +} + +inline StkFrames& TubeBell :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "TubeBell::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i coefficients ); + + //! Return an StkFrames reference to the last output sample frame. + //const StkFrames& lastFrame( void ) const { return lastFrame_; }; + + //! Return the last computed output value. + // StkFloat lastOut( void ) { return lastFrame_[0]; }; + StkFloat lastOut( void ) { return lastOutput_; }; + + //! Compute and return one output sample. + StkFloat tick( StkFloat input ); + + //! Take a channel of the \c iFrames object as inputs to the class and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. Each channel + argument must be less than the number of channels in the + corresponding StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the effect and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. Each channel + argument must be less than the number of channels in the + corresponding StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); + + protected: + + DelayA delayLine_; + DelayL combDelay_; + Fir loopFilter_; + + StkFloat lastOutput_; + StkFloat frequency_; + StkFloat loopGain_; + StkFloat pluckPosition_; +}; + +inline StkFloat Twang :: tick( StkFloat input ) +{ + lastOutput_ = delayLine_.tick( input + loopFilter_.tick( delayLine_.lastOut() ) ); + lastOutput_ -= combDelay_.tick( lastOutput_ ); // comb filtering on output + lastOutput_ *= 0.5; + + return lastOutput_; +} + +inline StkFrames& Twang :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "Twang::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + oStream_ << "Twang::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i= 1.0. The + \e frequency value should be between zero and half the sample rate. + For a better resonance filter, use a BiQuad filter. \sa BiQuad + filter class + */ + void setResonance(StkFloat frequency, StkFloat radius, bool normalize = false); + + //! Return the last computed output value. + StkFloat lastOut( void ) const { return lastFrame_[0]; }; + + //! Input one sample to the filter and return one output. + StkFloat tick( StkFloat input ); + + //! Take a channel of the StkFrames object as inputs to the filter and replace with corresponding outputs. + /*! + The StkFrames argument reference is returned. The \c channel + argument must be less than the number of channels in the + StkFrames argument (the first channel is specified by 0). + However, range checking is only performed if _STK_DEBUG_ is + defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + //! Take a channel of the \c iFrames object as inputs to the filter and write outputs to the \c oFrames object. + /*! + The \c iFrames object reference is returned. Each channel + argument must be less than the number of channels in the + corresponding StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& iFrames, StkFrames &oFrames, unsigned int iChannel = 0, unsigned int oChannel = 0 ); + + protected: + + virtual void sampleRateChanged( StkFloat newRate, StkFloat oldRate ); +}; + +inline StkFloat TwoPole :: tick( StkFloat input ) +{ + inputs_[0] = gain_ * input; + lastFrame_[0] = b_[0] * inputs_[0] - a_[1] * outputs_[1] - a_[2] * outputs_[2]; + outputs_[2] = outputs_[1]; + outputs_[1] = lastFrame_[0]; + + return lastFrame_[0]; +} + +inline StkFrames& TwoPole :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "TwoPole::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + oStream_ << "TwoPole::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i= frames.channels() ) { + oStream_ << "TwoZero::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + oStream_ << "TwoZero::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i + +namespace stk { + +/***************************************************/ +/*! \class Vector3D + \brief STK 3D vector class. + + This class implements a three-dimensional vector. + + by Perry R. Cook, 1995--2021. +*/ +/***************************************************/ + +class Vector3D : public Stk +{ + +public: + //! Default constructor taking optional initial X, Y, and Z values. + Vector3D( StkFloat x = 0.0, StkFloat y = 0.0, StkFloat z = 0.0 ) { setXYZ( x, y, z ); }; + + //! Get the current X value. + StkFloat getX( void ) { return X_; }; + + //! Get the current Y value. + StkFloat getY( void ) { return Y_; }; + + //! Get the current Z value. + StkFloat getZ( void ) { return Z_; }; + + //! Calculate the vector length. + StkFloat getLength( void ); + + //! Set the X, Y, and Z values simultaniously. + void setXYZ( StkFloat x, StkFloat y, StkFloat z ) { X_ = x; Y_ = y; Z_ = z; }; + + //! Set the X value. + void setX( StkFloat x ) { X_ = x; }; + + //! Set the Y value. + void setY( StkFloat y ) { Y_ = y; }; + + //! Set the Z value. + void setZ( StkFloat z ) { Z_ = z; }; + +protected: + StkFloat X_; + StkFloat Y_; + StkFloat Z_; +}; + +inline StkFloat Vector3D :: getLength( void ) +{ + StkFloat temp; + temp = X_ * X_; + temp += Y_ * Y_; + temp += Z_ * Z_; + temp = sqrt( temp ); + return temp; +} + +} // stk namespace + +#endif diff --git a/src/stk/include/VoicForm.h b/src/stk/include/VoicForm.h new file mode 100644 index 00000000..3c24104f --- /dev/null +++ b/src/stk/include/VoicForm.h @@ -0,0 +1,161 @@ +#ifndef STK_VOICFORM_H +#define STK_VOICFORM_H + +#include "Instrmnt.h" +#include "Envelope.h" +#include "Noise.h" +#include "SingWave.h" +#include "FormSwep.h" +#include "OnePole.h" +#include "OneZero.h" + +namespace stk { + +/***************************************************/ +/*! \class VoicForm + \brief Four formant synthesis instrument. + + This instrument contains an excitation singing + wavetable (looping wave with random and + periodic vibrato, smoothing on frequency, + etc.), excitation noise, and four sweepable + complex resonances. + + Measured formant data is included, and enough + data is there to support either parallel or + cascade synthesis. In the floating point case + cascade synthesis is the most natural so + that's what you'll find here. + + Control Change Numbers: + - Voiced/Unvoiced Mix = 2 + - Vowel/Phoneme Selection = 4 + - Vibrato Frequency = 11 + - Vibrato Gain = 1 + - Loudness (Spectral Tilt) = 128 + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class VoicForm : public Instrmnt +{ + public: + //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ + VoicForm( void ); + + //! Class destructor. + ~VoicForm( void ); + + //! Reset and clear all internal state. + void clear( void ); + + //! Set instrument parameters for a particular frequency. + void setFrequency( StkFloat frequency ); + + //! Set instrument parameters for the given phoneme. Returns false if phoneme not found. + bool setPhoneme( const char* phoneme ); + + //! Set the voiced component gain. + void setVoiced( StkFloat vGain ) { voiced_->setGainTarget(vGain); }; + + //! Set the unvoiced component gain. + void setUnVoiced( StkFloat nGain ) { noiseEnv_.setTarget(nGain); }; + + //! Set the sweep rate for a particular formant filter (0-3). + void setFilterSweepRate( unsigned int whichOne, StkFloat rate ); + + //! Set voiced component pitch sweep rate. + void setPitchSweepRate( StkFloat rate ) { voiced_->setSweepRate(rate); }; + + //! Start the voice. + void speak( void ) { voiced_->noteOn(); }; + + //! Stop the voice. + void quiet( void ); + + //! Start a note with the given frequency and amplitude. + void noteOn( StkFloat frequency, StkFloat amplitude ); + + //! Stop a note with the given amplitude (speed of decay). + void noteOff( StkFloat amplitude ) { this->quiet(); }; + + //! Perform the control change specified by \e number and \e value (0.0 - 128.0). + void controlChange( int number, StkFloat value ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + +protected: + + SingWave *voiced_; + Noise noise_; + Envelope noiseEnv_; + FormSwep filters_[4]; + OnePole onepole_; + OneZero onezero_; + +}; + +inline StkFloat VoicForm :: tick( unsigned int ) +{ + StkFloat temp; + temp = onepole_.tick( onezero_.tick( voiced_->tick() ) ); + temp += noiseEnv_.tick() * noise_.tick(); + lastFrame_[0] = filters_[0].tick(temp); + lastFrame_[0] += filters_[1].tick(temp); + lastFrame_[0] += filters_[2].tick(temp); + lastFrame_[0] += filters_[3].tick(temp); + /* + temp += noiseEnv_.tick() * noise_.tick(); + lastFrame_[0] = filters_[0].tick(temp); + lastFrame_[0] = filters_[1].tick(lastFrame_[0]); + lastFrame_[0] = filters_[2].tick(lastFrame_[0]); + lastFrame_[0] = filters_[3].tick(lastFrame_[0]); + */ + return lastFrame_[0]; +} + +inline StkFrames& VoicForm :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "VoicForm::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i + +namespace stk { + +/***************************************************/ +/*! \class Voicer + \brief STK voice manager class. + + This class can be used to manage a group of STK instrument + classes. Individual voices can be controlled via unique note + tags. Instrument groups can be controlled by group number. + + A previously constructed STK instrument class is linked with a + voice manager using the addInstrument() function. An optional + group number argument can be specified to the addInstrument() + function as well (default group = 0). The voice manager does not + delete any instrument instances ... it is the responsibility of + the user to allocate and deallocate all instruments. + + The tick() function returns the mix of all sounding voices. Each + noteOn returns a unique tag (credits to the NeXT MusicKit), so you + can send control changes to specific voices within an ensemble. + Alternately, control changes can be sent to all voices in a given + group. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class Voicer : public Stk +{ + public: + //! Class constructor taking an optional note decay time (in seconds). + Voicer( StkFloat decayTime = 0.2 ); + + //! Add an instrument with an optional group number to the voice manager. + /*! + A set of instruments can be grouped by group number and + controlled via the functions that take a group number argument. + */ + void addInstrument( Instrmnt *instrument, int group=0 ); + + //! Remove the given instrument pointer from the voice manager's control. + /*! + It is important that any instruments which are to be deleted by + the user while the voice manager is running be first removed from + the manager's control via this function!! + */ + void removeInstrument( Instrmnt *instrument ); + + //! Initiate a noteOn event with the given note number and amplitude and return a unique note tag. + /*! + Send the noteOn message to the first available unused voice. + If all voices are sounding, the oldest voice is interrupted and + sent the noteOn message. If the optional group argument is + non-zero, only voices in that group are used. If no voices are + found for a specified non-zero group value, the function returns + -1. The amplitude value should be in the range 0.0 - 128.0. + */ + long noteOn( StkFloat noteNumber, StkFloat amplitude, int group=0 ); + + //! Send a noteOff to all voices having the given noteNumber and optional group (default group = 0). + /*! + The amplitude value should be in the range 0.0 - 128.0. + */ + void noteOff( StkFloat noteNumber, StkFloat amplitude, int group=0 ); + + //! Send a noteOff to the voice with the given note tag. + /*! + The amplitude value should be in the range 0.0 - 128.0. + */ + void noteOff( long tag, StkFloat amplitude ); + + //! Send a frequency update message to all voices assigned to the optional group argument (default group = 0). + /*! + The \e noteNumber argument corresponds to a MIDI note number, though it is a floating-point value and can range beyond the normal 0-127 range. + */ + void setFrequency( StkFloat noteNumber, int group=0 ); + + //! Send a frequency update message to the voice with the given note tag. + /*! + The \e noteNumber argument corresponds to a MIDI note number, though it is a floating-point value and can range beyond the normal 0-127 range. + */ + void setFrequency( long tag, StkFloat noteNumber ); + + //! Send a pitchBend message to all voices assigned to the optional group argument (default group = 0). + void pitchBend( StkFloat value, int group=0 ); + + //! Send a pitchBend message to the voice with the given note tag. + void pitchBend( long tag, StkFloat value ); + + //! Send a controlChange to all instruments assigned to the optional group argument (default group = 0). + void controlChange( int number, StkFloat value, int group=0 ); + + //! Send a controlChange to the voice with the given note tag. + void controlChange( long tag, int number, StkFloat value ); + + //! Send a noteOff message to all existing voices. + void silence( void ); + + //! Return the current number of output channels. + unsigned int channelsOut( void ) const { return lastFrame_.channels(); }; + + //! Return an StkFrames reference to the last output sample frame. + const StkFrames& lastFrame( void ) const { return lastFrame_; }; + + //! Return the specified channel value of the last computed frame. + /*! + The \c channel argument must be less than the number of output + channels, which can be determined with the channelsOut() function + (the first channel is specified by 0). However, range checking is + only performed if _STK_DEBUG_ is defined during compilation, in + which case an out-of-range value will trigger an StkError + exception. \sa lastFrame() + */ + StkFloat lastOut( unsigned int channel = 0 ); + + //! Mix one sample frame of all sounding voices and return the specified \c channel value. + /*! + The \c channel argument must be less than the number of output + channels, which can be determined with the channelsOut() function + (the first channel is specified by 0). However, range checking is + only performed if _STK_DEBUG_ is defined during compilation, in + which case an out-of-range value will trigger an StkError + exception. + */ + StkFloat tick( unsigned int channel = 0 ); + + //! Fill the StkFrames argument with computed frames and return the same reference. + /*! + The number of channels in the StkFrames argument must equal + the number of channels in the file data. However, this is only + checked if _STK_DEBUG_ is defined during compilation, in which + case an incompatibility will trigger an StkError exception. If no + file data is loaded, the function does nothing (a warning will be + issued if _STK_DEBUG_ is defined during compilation). + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + + struct Voice { + Instrmnt *instrument; + long tag; + StkFloat noteNumber; + StkFloat frequency; + int sounding; + int group; + + // Default constructor. + Voice() + :instrument(0), tag(0), noteNumber(-1.0), frequency(0.0), sounding(0), group(0) {} + }; + + std::vector voices_; + long tags_; + int muteTime_; + StkFrames lastFrame_; +}; + +inline StkFloat Voicer :: lastOut( unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= lastFrame_.channels() ) { + oStream_ << "Voicer::lastOut(): channel argument is invalid!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + return lastFrame_[channel]; +} + + +inline StkFloat Voicer :: tick( unsigned int channel ) +{ + unsigned int j; + for ( j=0; jtick(); + for ( j=0; jchannelsOut(); j++ ) lastFrame_[j] += voices_[i].instrument->lastOut( j ); + } + if ( voices_[i].sounding < 0 ) + voices_[i].sounding++; + if ( voices_[i].sounding == 0 ) + voices_[i].noteNumber = -1; + } + + return lastFrame_[channel]; +} + +inline StkFrames& Voicer :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "Voicer::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + for ( unsigned int i=0; i frames.channels() - nChannels ) { + oStream_ << "Whistle::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i3--\ + + --> Out + 2->1--/ + \endcode + + Control Change Numbers: + - Modulator Index One = 2 + - Crossfade of Outputs = 4 + - LFO Speed = 11 + - LFO Depth = 1 + - ADSR 2 & 4 Target = 128 + + The basic Chowning/Stanford FM patent expired + in 1995, but there exist follow-on patents, + mostly assigned to Yamaha. If you are of the + type who should worry about this (making + money) worry away. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +class Wurley : public FM +{ + public: + //! Class constructor. + /*! + An StkError will be thrown if the rawwave path is incorrectly set. + */ + Wurley( void ); + + //! Class destructor. + ~Wurley( void ); + + //! Set instrument parameters for a particular frequency. + void setFrequency( StkFloat frequency ); + + //! Start a note with the given frequency and amplitude. + void noteOn( StkFloat frequency, StkFloat amplitude ); + + //! Compute and return one output sample. + StkFloat tick( unsigned int channel = 0 ); + + //! Fill a channel of the StkFrames object with computed outputs. + /*! + The \c channel argument must be less than the number of + channels in the StkFrames argument (the first channel is specified + by 0). However, range checking is only performed if _STK_DEBUG_ + is defined during compilation, in which case an out-of-range value + will trigger an StkError exception. + */ + StkFrames& tick( StkFrames& frames, unsigned int channel = 0 ); + + protected: + +}; + +inline StkFloat Wurley :: tick( unsigned int ) +{ + StkFloat temp, temp2; + + temp = gains_[1] * adsr_[1]->tick() * waves_[1]->tick(); + temp = temp * control1_; + + waves_[0]->addPhaseOffset( temp ); + waves_[3]->addPhaseOffset( twozero_.lastOut() ); + temp = gains_[3] * adsr_[3]->tick() * waves_[3]->tick(); + twozero_.tick(temp); + + waves_[2]->addPhaseOffset( temp ); + temp = ( 1.0 - (control2_ * 0.5)) * gains_[0] * adsr_[0]->tick() * waves_[0]->tick(); + temp += control2_ * 0.5 * gains_[2] * adsr_[2]->tick() * waves_[2]->tick(); + + // Calculate amplitude modulation and apply it to output. + temp2 = vibrato_.tick() * modDepth_; + temp = temp * (1.0 + temp2); + + lastFrame_[0] = temp * 0.5; + return lastFrame_[0]; +} + +inline StkFrames& Wurley :: tick( StkFrames& frames, unsigned int channel ) +{ + unsigned int nChannels = lastFrame_.channels(); +#if defined(_STK_DEBUG_) + if ( channel > frames.channels() - nChannels ) { + oStream_ << "Wurley::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i 1.0 ) { + sample = 1.0; + clip = true; + } + else if ( sample < -1.0 ) { + sample = -1.0; + clip = true; + } + + if ( clip == true && clipping_ == false ) { + // First occurrence of clipping since instantiation or reset. + clipping_ = true; + oStream_ << "WvOut: data value(s) outside +-1.0 detected ... clamping at outer bound!"; + handleError( StkError::WARNING ); + } + + return sample; +} + +} // stk namespace + +#endif diff --git a/src/stk/rawwaves/ahh.raw b/src/stk/rawwaves/ahh.raw new file mode 100644 index 00000000..dda5f557 Binary files /dev/null and b/src/stk/rawwaves/ahh.raw differ diff --git a/src/stk/rawwaves/bassdrum.raw b/src/stk/rawwaves/bassdrum.raw new file mode 100644 index 00000000..16b1ecef Binary files /dev/null and b/src/stk/rawwaves/bassdrum.raw differ diff --git a/src/stk/rawwaves/britestk.raw b/src/stk/rawwaves/britestk.raw new file mode 100644 index 00000000..4bf9ba68 Binary files /dev/null and b/src/stk/rawwaves/britestk.raw differ diff --git a/src/stk/rawwaves/cowbell1.raw b/src/stk/rawwaves/cowbell1.raw new file mode 100644 index 00000000..4cd641f0 Binary files /dev/null and b/src/stk/rawwaves/cowbell1.raw differ diff --git a/src/stk/rawwaves/crashcym.raw b/src/stk/rawwaves/crashcym.raw new file mode 100644 index 00000000..21318b2b Binary files /dev/null and b/src/stk/rawwaves/crashcym.raw differ diff --git a/src/stk/rawwaves/dope.raw b/src/stk/rawwaves/dope.raw new file mode 100644 index 00000000..6a7a1ff6 Binary files /dev/null and b/src/stk/rawwaves/dope.raw differ diff --git a/src/stk/rawwaves/eee.raw b/src/stk/rawwaves/eee.raw new file mode 100644 index 00000000..d3888db6 Binary files /dev/null and b/src/stk/rawwaves/eee.raw differ diff --git a/src/stk/rawwaves/fwavblnk.raw b/src/stk/rawwaves/fwavblnk.raw new file mode 100644 index 00000000..14da0748 Binary files /dev/null and b/src/stk/rawwaves/fwavblnk.raw differ diff --git a/src/stk/rawwaves/halfwave.raw b/src/stk/rawwaves/halfwave.raw new file mode 100644 index 00000000..b34639b0 Binary files /dev/null and b/src/stk/rawwaves/halfwave.raw differ diff --git a/src/stk/rawwaves/hihatcym.raw b/src/stk/rawwaves/hihatcym.raw new file mode 100644 index 00000000..f61389ec Binary files /dev/null and b/src/stk/rawwaves/hihatcym.raw differ diff --git a/src/stk/rawwaves/impuls10.raw b/src/stk/rawwaves/impuls10.raw new file mode 100644 index 00000000..e18689dd Binary files /dev/null and b/src/stk/rawwaves/impuls10.raw differ diff --git a/src/stk/rawwaves/impuls20.raw b/src/stk/rawwaves/impuls20.raw new file mode 100644 index 00000000..9d89aec1 Binary files /dev/null and b/src/stk/rawwaves/impuls20.raw differ diff --git a/src/stk/rawwaves/impuls40.raw b/src/stk/rawwaves/impuls40.raw new file mode 100644 index 00000000..9a3619b3 Binary files /dev/null and b/src/stk/rawwaves/impuls40.raw differ diff --git a/src/stk/rawwaves/makefunc.c b/src/stk/rawwaves/makefunc.c new file mode 100644 index 00000000..b3180659 --- /dev/null +++ b/src/stk/rawwaves/makefunc.c @@ -0,0 +1,55 @@ +/**********************************************/ +/** Utility to make various functions **/ +/** like exponential and log gain curves. **/ +/** **/ +/** Included here: **/ +/** Yamaha TX81Z curves for master gain, **/ +/** Envelope Rates (in normalized units), **/ +/** envelope sustain level, and more.... **/ +/**********************************************/ + +#include +#include +#include + +void main() +{ + int i,j; + double temp; + double data[128]; + + /*************** TX81Z Master Gain *************/ + for (i=0;i<100;i++) { + data[i] = pow(2.0,-(99-i)/10.0); + } + data[0] = 0.0; + printf("double __FM4Op_gains[99] = {"); + for (i=0;i<100;i++) { + if (i%8 == 0) printf("\n"); + printf("%lf,",data[i]); + } + printf("};\n"); + /*************** TX81Z Sustain Level ***********/ + for (i=0;i<16;i++) { + data[i] = pow(2.0,-(15-i)/2.0); + } + data[0] = 0.0; + printf("double __FM4Op_susLevels[16] = {"); + for (i=0;i<16;i++) { + if (i%8 == 0) printf("\n"); + printf("%lf,",data[i]); + } + printf("};\n"); + /****************** Attack Rate ***************/ + for (i=0;i<32;i++) { + data[i] = 6.0 * pow(5.7,-(i-1)/5.0); + } + printf("double __FM4Op_attTimes[16] = {"); + for (i=0;i<32;i++) { + if (i%8 == 0) printf("\n"); + printf("%lf,",data[i]); + } + printf("};\n"); + exit(1); +} + diff --git a/src/stk/rawwaves/makemidi.c b/src/stk/rawwaves/makemidi.c new file mode 100644 index 00000000..480f6380 --- /dev/null +++ b/src/stk/rawwaves/makemidi.c @@ -0,0 +1,33 @@ +/**********************************************/ +/** Utility to make various functions **/ +/** like exponential and log gain curves. **/ +/** Specifically for direct MIDI parameter **/ +/** conversions. **/ +/** Included here: **/ +/** A440 Referenced Equal Tempered Pitches **/ +/** as a function of MIDI note number. **/ +/** **/ +/**********************************************/ + +#include +#include +#include + +void main() +{ + int i,j; + double temp; + double data[128]; + + /********* Pitch as fn. of MIDI Note **********/ + + printf("double __MIDI_To_Pitch[128] = {"); + for (i=0;i<128;i++) { + if (i%8 == 0) printf("\n"); + temp = 220.0 * pow(2.0,((double) i - 57) / 12.0); + printf("%.2lf,",temp); + } + printf("};\n"); + exit(1); +} + diff --git a/src/stk/rawwaves/makewavs.c b/src/stk/rawwaves/makewavs.c new file mode 100644 index 00000000..20a89acb --- /dev/null +++ b/src/stk/rawwaves/makewavs.c @@ -0,0 +1,116 @@ +/**********************************************/ +/** Utility to make various flavors of **/ +/** sine wave (rectified, etc), and **/ +/** other commonly needed waveforms, like **/ +/** triangles, ramps, etc. **/ +/** The files generated are all 16 bit **/ +/** linear signed integer, of length **/ +/** as defined by LENGTH below **/ +/**********************************************/ + +#include +#include +#include + +#define LENGTH 256 +#define PI 3.14159265358979323846 + +void main() +{ + int i,j; + double temp; + short data[LENGTH + 2]; + FILE *fd; + + /////////// Yer Basic TX81Z Waves, Including Sine /////////// + fd = fopen("halfwave.raw","wb"); + for (i=0;i +#include +#include + +#define LENGTH 1024 +#define PI 3.14159265358979323846 + +void main() +{ + int i,j; + double temp; + short data[LENGTH + 2]; + FILE *fd; + + fd = fopen("sinewave.raw","wb"); + for (i=0; i 0.0 ) + releaseRate_ = value_ / ( releaseTime_ * Stk::sampleRate() ); +} + +void ADSR :: setAttackRate( StkFloat rate ) +{ + if ( rate < 0.0 ) { + oStream_ << "ADSR::setAttackRate: argument must be >= 0.0!"; + handleError( StkError::WARNING ); return; + } + + attackRate_ = rate; +} + +void ADSR :: setAttackTarget( StkFloat target ) +{ + if ( target < 0.0 ) { + oStream_ << "ADSR::setAttackTarget: negative target not allowed!"; + handleError( StkError::WARNING ); return; + } + + target_ = target; +} + +void ADSR :: setDecayRate( StkFloat rate ) +{ + if ( rate < 0.0 ) { + oStream_ << "ADSR::setDecayRate: negative rates not allowed!"; + handleError( StkError::WARNING ); return; + } + + decayRate_ = rate; +} + +void ADSR :: setSustainLevel( StkFloat level ) +{ + if ( level < 0.0 ) { + oStream_ << "ADSR::setSustainLevel: negative level not allowed!"; + handleError( StkError::WARNING ); return; + } + + sustainLevel_ = level; +} + +void ADSR :: setReleaseRate( StkFloat rate ) +{ + if ( rate < 0.0 ) { + oStream_ << "ADSR::setReleaseRate: negative rates not allowed!"; + handleError( StkError::WARNING ); return; + } + + releaseRate_ = rate; + + // Set to negative value so we don't update the release rate on keyOff() + releaseTime_ = -1.0; +} + +void ADSR :: setAttackTime( StkFloat time ) +{ + if ( time <= 0.0 ) { + oStream_ << "ADSR::setAttackTime: negative or zero times not allowed!"; + handleError( StkError::WARNING ); return; + } + + attackRate_ = 1.0 / ( time * Stk::sampleRate() ); +} + +void ADSR :: setDecayTime( StkFloat time ) +{ + if ( time <= 0.0 ) { + oStream_ << "ADSR::setDecayTime: negative or zero times not allowed!"; + handleError( StkError::WARNING ); return; + } + + decayRate_ = (1.0 - sustainLevel_) / ( time * Stk::sampleRate() ); +} + +void ADSR :: setReleaseTime( StkFloat time ) +{ + if ( time <= 0.0 ) { + oStream_ << "ADSR::setReleaseTime: negative or zero times not allowed!"; + handleError( StkError::WARNING ); return; + } + + releaseRate_ = sustainLevel_ / ( time * Stk::sampleRate() ); + releaseTime_ = time; +} + +void ADSR :: setAllTimes( StkFloat aTime, StkFloat dTime, StkFloat sLevel, StkFloat rTime ) +{ + this->setAttackTime( aTime ); + this->setSustainLevel( sLevel ); + this->setDecayTime( dTime ); + this->setReleaseTime( rTime ); +} + +void ADSR :: setTarget( StkFloat target ) +{ + if ( target < 0.0 ) { + oStream_ << "ADSR::setTarget: negative target not allowed!"; + handleError( StkError::WARNING ); return; + } + + target_ = target; + + this->setSustainLevel( target_ ); + if ( value_ < target_ ) state_ = ATTACK; + if ( value_ > target_ ) state_ = DECAY; +} + +void ADSR :: setValue( StkFloat value ) +{ + state_ = SUSTAIN; + target_ = value; + value_ = value; + this->setSustainLevel( value ); + lastFrame_[0] = value; +} + +} // stk namespace diff --git a/src/stk/src/Asymp.cpp b/src/stk/src/Asymp.cpp new file mode 100644 index 00000000..7be0519f --- /dev/null +++ b/src/stk/src/Asymp.cpp @@ -0,0 +1,112 @@ +/***************************************************/ +/*! \class Asymp + \brief STK asymptotic curve envelope class + + This class implements a simple envelope generator + which asymptotically approaches a target value. + The algorithm used is of the form: + + y[n] = a y[n-1] + (1-a) target, + + where a = exp(-T/tau), T is the sample period, and + tau is a time constant. The user can set the time + constant (default value = 0.3) and target value. + Theoretically, this recursion never reaches its + target, though the calculations in this class are + stopped when the current value gets within a small + threshold value of the target (at which time the + current value is set to the target). It responds + to \e keyOn and \e keyOff messages by ramping to + 1.0 on keyOn and to 0.0 on keyOff. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Asymp.h" +#include + +namespace stk { + +Asymp :: Asymp( void ) +{ + value_ = 0.0; + target_ = 0.0; + state_ = 0; + factor_ = exp( -1.0 / ( 0.3 * Stk::sampleRate() ) ); + constant_ = 0.0; + Stk::addSampleRateAlert( this ); +} + +Asymp :: ~Asymp( void ) +{ + Stk::removeSampleRateAlert( this ); +} + +void Asymp :: sampleRateChanged( StkFloat newRate, StkFloat oldRate ) +{ + if ( !ignoreSampleRateChange_ ) { + StkFloat tau = -1.0 / ( std::log( factor_ ) * oldRate ); + factor_ = std::exp( -1.0 / ( tau * newRate ) ); + } +} + +void Asymp :: keyOn( void ) +{ + this->setTarget( 1.0 ); +} + +void Asymp :: keyOff( void ) +{ + this->setTarget( 0.0 ); +} + +void Asymp :: setTau( StkFloat tau ) +{ + if ( tau <= 0.0 ) { + oStream_ << "Asymp::setTau: negative or zero tau not allowed!"; + handleError( StkError::WARNING ); return; + } + + factor_ = std::exp( -1.0 / ( tau * Stk::sampleRate() ) ); + constant_ = ( 1.0 - factor_ ) * target_; +} + +void Asymp :: setTime( StkFloat time ) +{ + if ( time <= 0.0 ) { + oStream_ << "Asymp::setTime: negative or zero times not allowed!"; + handleError( StkError::WARNING ); return; + } + + StkFloat tau = -time / std::log( TARGET_THRESHOLD ); + factor_ = std::exp( -1.0 / ( tau * Stk::sampleRate() ) ); + constant_ = ( 1.0 - factor_ ) * target_; +} + +void Asymp :: setT60( StkFloat t60 ) +{ + if ( t60 <= 0.0 ) { + oStream_ << "Asymp::setT60: negative or zero t60 not allowed!"; + handleError( StkError::WARNING ); return; + } + + setTau( t60 / 6.91 ); +} + +void Asymp :: setTarget( StkFloat target ) +{ + target_ = target; + if ( value_ != target_ ) state_ = 1; + constant_ = ( 1.0 - factor_ ) * target_; +} + +void Asymp :: setValue( StkFloat value ) +{ + state_ = 0; + target_ = value; + value_ = value; +} + +} // stk namespace + diff --git a/src/stk/src/BandedWG.cpp b/src/stk/src/BandedWG.cpp new file mode 100644 index 00000000..5df80ac0 --- /dev/null +++ b/src/stk/src/BandedWG.cpp @@ -0,0 +1,364 @@ +/***************************************************/ +/*! \class BandedWG + \brief Banded waveguide modeling class. + + This class uses banded waveguide techniques to + model a variety of sounds, including bowed + bars, glasses, and bowls. For more + information, see Essl, G. and Cook, P. "Banded + Waveguides: Towards Physical Modelling of Bar + Percussion Instruments", Proceedings of the + 1999 International Computer Music Conference. + + Control Change Numbers: + - Bow Pressure = 2 + - Bow Motion = 4 + - Strike Position = 8 (not implemented) + - Vibrato Frequency = 11 + - Gain = 1 + - Bow Velocity = 128 + - Set Striking = 64 + - Instrument Presets = 16 + - Uniform Bar = 0 + - Tuned Bar = 1 + - Glass Harmonica = 2 + - Tibetan Bowl = 3 + + by Georg Essl, 1999--2004. + Modified for STK 4.0 by Gary Scavone. +*/ +/***************************************************/ + +#include "BandedWG.h" +#include "SKINImsg.h" +#include + +namespace stk { + +BandedWG :: BandedWG( void ) +{ + doPluck_ = true; + + bowTable_.setSlope( 3.0 ); + adsr_.setAllTimes( 0.02, 0.005, 0.9, 0.01 ); + + frequency_ = 220.0; + this->setPreset(0); + + bowPosition_ = 0; + baseGain_ = 0.999; + + integrationConstant_ = 0.0; + trackVelocity_ = false; + + bowVelocity_ = 0.0; + bowTarget_ = 0.0; + + strikeAmp_ = 0.0; +} + +BandedWG :: ~BandedWG( void ) +{ +} + +void BandedWG :: clear( void ) +{ + for ( int i=0; i 1568.0) frequency = 1568.0; + + StkFloat radius; + StkFloat base = Stk::sampleRate() / frequency; + StkFloat length; + for (int i=0; i 2.0) { + delay_[i].setDelay( length ); + gains_[i]=basegains_[i]; + // gains_[i]=(StkFloat) pow(basegains_[i], 1/((StkFloat)delay_[i].getDelay())); + // std::cerr << gains_[i]; + } + else { + nModes_ = i; + break; + } + // std::cerr << std::endl; + + // Set the bandpass filter resonances + radius = 1.0 - PI * 32 / Stk::sampleRate(); //frequency_ * modes_[i] / Stk::sampleRate()/32; + if ( radius < 0.0 ) radius = 0.0; + bandpass_[i].setResonance(frequency * modes_[i], radius, true); + + delay_[i].clear(); + bandpass_[i].clear(); + } + + //int olen = (int)(delay_[0].getDelay()); + //strikePosition_ = (int)(strikePosition_*(length/modes_[0])/olen); +} + +void BandedWG :: setStrikePosition( StkFloat position ) +{ + strikePosition_ = (int)(delay_[0].getDelay() * position / 2.0); +} + +void BandedWG :: startBowing( StkFloat amplitude, StkFloat rate ) +{ + adsr_.setAttackRate(rate); + adsr_.keyOn(); + maxVelocity_ = 0.03 + (0.1 * amplitude); +} + +void BandedWG :: stopBowing( StkFloat rate ) +{ + adsr_.setReleaseRate(rate); + adsr_.keyOff(); +} + +void BandedWG :: pluck( StkFloat amplitude ) +{ + int j; + StkFloat min_len = delay_[nModes_-1].getDelay(); + for (int i=0; isetFrequency( frequency ); + + if ( doPluck_ ) + this->pluck( amplitude ); + else + this->startBowing( amplitude, amplitude * 0.001 ); +} + +void BandedWG :: noteOff( StkFloat amplitude ) +{ + if ( !doPluck_ ) + this->stopBowing( (1.0 - amplitude) * 0.005 ); +} + +StkFloat BandedWG :: tick( unsigned int ) +{ + int k; + + StkFloat input = 0.0; + if ( doPluck_ ) { + input = 0.0; + // input = strikeAmp_/nModes_; + // strikeAmp_ = 0.0; + } + else { + if ( integrationConstant_ == 0.0 ) + velocityInput_ = 0.0; + else + velocityInput_ = integrationConstant_ * velocityInput_; + + for ( k=0; k 128.0 ) ) { + oStream_ << "BandedWG::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if (number == __SK_BowPressure_) { // 2 + if ( normalizedValue == 0.0 ) + doPluck_ = true; + else { + doPluck_ = false; + bowTable_.setSlope( 10.0 - (9.0 * normalizedValue)); + } + } + else if (number == 4) { // 4 + if ( !trackVelocity_ ) trackVelocity_ = true; + bowTarget_ += 0.005 * (normalizedValue - bowPosition_); + bowPosition_ = normalizedValue; + //adsr_.setTarget(bowPosition_); + } + else if (number == 8) // 8 + this->setStrikePosition( normalizedValue ); + else if (number == __SK_AfterTouch_Cont_) { // 128 + //bowTarget_ += 0.02 * (normalizedValue - bowPosition_); + //bowPosition_ = normalizedValue; + if ( trackVelocity_ ) trackVelocity_ = false; + maxVelocity_ = 0.13 * normalizedValue; + adsr_.setTarget(normalizedValue); + } + else if (number == __SK_ModWheel_) { // 1 + // baseGain_ = 0.9989999999 + (0.001 * normalizedValue ); + baseGain_ = 0.8999999999999999 + (0.1 * normalizedValue); + // std::cerr << "Yuck!" << std::endl; + for (int i=0; isetPreset((int) value); +#if defined(_STK_DEBUG_) + else { + oStream_ << "BandedWG::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/BeeThree.cpp b/src/stk/src/BeeThree.cpp new file mode 100644 index 00000000..a9e53b80 --- /dev/null +++ b/src/stk/src/BeeThree.cpp @@ -0,0 +1,79 @@ +/***************************************************/ +/*! \class BeeThree + \brief STK Hammond-oid organ FM synthesis instrument. + + This class implements a simple 4 operator + topology, also referred to as algorithm 8 of + the TX81Z. + + \code + Algorithm 8 is : + 1 --. + 2 -\| + +-> Out + 3 -/| + 4 -- + \endcode + + Control Change Numbers: + - Operator 4 (feedback) Gain = 2 + - Operator 3 Gain = 4 + - LFO Speed = 11 + - LFO Depth = 1 + - ADSR 2 & 4 Target = 128 + + The basic Chowning/Stanford FM patent expired + in 1995, but there exist follow-on patents, + mostly assigned to Yamaha. If you are of the + type who should worry about this (making + money) worry away. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "BeeThree.h" + +namespace stk { + +BeeThree :: BeeThree( void ) + : FM() +{ + // Concatenate the STK rawwave path to the rawwave files + for ( unsigned int i=0; i<3; i++ ) + waves_[i] = new FileLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + waves_[3] = new FileLoop( (Stk::rawwavePath() + "fwavblnk.raw").c_str(), true ); + + this->setRatio( 0, 0.999 ); + this->setRatio( 1, 1.997 ); + this->setRatio( 2, 3.006 ); + this->setRatio( 3, 6.009 ); + + gains_[0] = fmGains_[95]; + gains_[1] = fmGains_[95]; + gains_[2] = fmGains_[99]; + gains_[3] = fmGains_[95]; + + adsr_[0]->setAllTimes( 0.005, 0.003, 1.0, 0.01 ); + adsr_[1]->setAllTimes( 0.005, 0.003, 1.0, 0.01 ); + adsr_[2]->setAllTimes( 0.005, 0.003, 1.0, 0.01 ); + adsr_[3]->setAllTimes( 0.005, 0.001, 0.4, 0.03 ); + + twozero_.setGain( 0.1 ); +} + +BeeThree :: ~BeeThree( void ) +{ +} + +void BeeThree :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + gains_[0] = amplitude * fmGains_[95]; + gains_[1] = amplitude * fmGains_[95]; + gains_[2] = amplitude * fmGains_[99]; + gains_[3] = amplitude * fmGains_[95]; + this->setFrequency( frequency ); + this->keyOn(); +} + +} // stk namespace diff --git a/src/stk/src/BiQuad.cpp b/src/stk/src/BiQuad.cpp new file mode 100644 index 00000000..024a65f3 --- /dev/null +++ b/src/stk/src/BiQuad.cpp @@ -0,0 +1,103 @@ +/***************************************************/ +/*! \class BiQuad + \brief STK biquad (two-pole, two-zero) filter class. + + This class implements a two-pole, two-zero digital filter. + Methods are provided for creating a resonance or notch in the + frequency response while maintaining a constant filter gain. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "BiQuad.h" +#include + +namespace stk { + +BiQuad :: BiQuad() : Filter() +{ + b_.resize( 3, 0.0 ); + a_.resize( 3, 0.0 ); + b_[0] = 1.0; + a_[0] = 1.0; + inputs_.resize( 3, 1, 0.0 ); + outputs_.resize( 3, 1, 0.0 ); + + Stk::addSampleRateAlert( this ); +} + +BiQuad :: ~BiQuad() +{ + Stk::removeSampleRateAlert( this ); +} + +void BiQuad :: setCoefficients( StkFloat b0, StkFloat b1, StkFloat b2, StkFloat a1, StkFloat a2, bool clearState ) +{ + b_[0] = b0; + b_[1] = b1; + b_[2] = b2; + a_[1] = a1; + a_[2] = a2; + + if ( clearState ) this->clear(); +} + +void BiQuad :: sampleRateChanged( StkFloat newRate, StkFloat oldRate ) +{ + if ( !ignoreSampleRateChange_ ) { + oStream_ << "BiQuad::sampleRateChanged: you may need to recompute filter coefficients!"; + handleError( StkError::WARNING ); + } +} + +void BiQuad :: setResonance( StkFloat frequency, StkFloat radius, bool normalize ) +{ +#if defined(_STK_DEBUG_) + if ( frequency < 0.0 || frequency > 0.5 * Stk::sampleRate() ) { + oStream_ << "BiQuad::setResonance: frequency argument (" << frequency << ") is out of range!"; + handleError( StkError::WARNING ); return; + } + if ( radius < 0.0 || radius >= 1.0 ) { + oStream_ << "BiQuad::setResonance: radius argument (" << radius << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + a_[2] = radius * radius; + a_[1] = -2.0 * radius * cos( TWO_PI * frequency / Stk::sampleRate() ); + + if ( normalize ) { + // Use zeros at +- 1 and normalize the filter peak gain. + b_[0] = 0.5 - 0.5 * a_[2]; + b_[1] = 0.0; + b_[2] = -b_[0]; + } +} + +void BiQuad :: setNotch( StkFloat frequency, StkFloat radius ) +{ +#if defined(_STK_DEBUG_) + if ( frequency < 0.0 || frequency > 0.5 * Stk::sampleRate() ) { + oStream_ << "BiQuad::setNotch: frequency argument (" << frequency << ") is out of range!"; + handleError( StkError::WARNING ); return; + } + if ( radius < 0.0 ) { + oStream_ << "BiQuad::setNotch: radius argument (" << radius << ") is negative!"; + handleError( StkError::WARNING ); return; + } +#endif + + // This method does not attempt to normalize the filter gain. + b_[2] = radius * radius; + b_[1] = (StkFloat) -2.0 * radius * cos( TWO_PI * (double) frequency / Stk::sampleRate() ); +} + +void BiQuad :: setEqualGainZeroes( void ) +{ + b_[0] = 1.0; + b_[1] = 0.0; + b_[2] = -1.0; +} + +} // stk namespace diff --git a/src/stk/src/Blit.cpp b/src/stk/src/Blit.cpp new file mode 100644 index 00000000..cff786ed --- /dev/null +++ b/src/stk/src/Blit.cpp @@ -0,0 +1,78 @@ +/***************************************************/ +/*! \class Blit + \brief STK band-limited impulse train class. + + This class generates a band-limited impulse train using a + closed-form algorithm reported by Stilson and Smith in "Alias-Free + Digital Synthesis of Classic Analog Waveforms", 1996. The user + can specify both the fundamental frequency of the impulse train + and the number of harmonics contained in the resulting signal. + + The signal is normalized so that the peak value is +/-1.0. + + If nHarmonics is 0, then the signal will contain all harmonics up + to half the sample rate. Note, however, that this setting may + produce aliasing in the signal when the frequency is changing (no + automatic modification of the number of harmonics is performed by + the setFrequency() function). + + Original code by Robin Davies, 2005. + Revisions by Gary Scavone for STK, 2005. +*/ +/***************************************************/ + +#include "Blit.h" + +namespace stk { + +Blit:: Blit( StkFloat frequency ) +{ + if ( frequency <= 0.0 ) { + oStream_ << "Blit::Blit: argument (" << frequency << ") must be positive!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + nHarmonics_ = 0; + this->setFrequency( frequency ); + this->reset(); +} + +Blit :: ~Blit() +{ +} + +void Blit :: reset() +{ + phase_ = 0.0; + lastFrame_[0] = 0.0; +} + +void Blit :: setFrequency( StkFloat frequency ) +{ + if ( frequency <= 0.0 ) { + oStream_ << "Blit::setFrequency: argument (" << frequency << ") must be positive!"; + handleError( StkError::WARNING ); return; + } + + p_ = Stk::sampleRate() / frequency; + rate_ = PI / p_; + this->updateHarmonics(); +} + +void Blit :: setHarmonics( unsigned int nHarmonics ) +{ + nHarmonics_ = nHarmonics; + this->updateHarmonics(); +} + +void Blit :: updateHarmonics( void ) +{ + if ( nHarmonics_ <= 0 ) { + unsigned int maxHarmonics = (unsigned int) floor( 0.5 * p_ ); + m_ = 2 * maxHarmonics + 1; + } + else + m_ = 2 * nHarmonics_ + 1; +} + +} // stk namespace diff --git a/src/stk/src/BlitSaw.cpp b/src/stk/src/BlitSaw.cpp new file mode 100644 index 00000000..f732a8e4 --- /dev/null +++ b/src/stk/src/BlitSaw.cpp @@ -0,0 +1,91 @@ +/***************************************************/ +/*! \class BlitSaw + \brief STK band-limited sawtooth wave class. + + This class generates a band-limited sawtooth waveform using a + closed-form algorithm reported by Stilson and Smith in "Alias-Free + Digital Synthesis of Classic Analog Waveforms", 1996. The user + can specify both the fundamental frequency of the sawtooth and the + number of harmonics contained in the resulting signal. + + If nHarmonics is 0, then the signal will contain all harmonics up + to half the sample rate. Note, however, that this setting may + produce aliasing in the signal when the frequency is changing (no + automatic modification of the number of harmonics is performed by + the setFrequency() function). + + Based on initial code of Robin Davies, 2005. + Modified algorithm code by Gary Scavone, 2005. +*/ +/***************************************************/ + +#include "BlitSaw.h" + +namespace stk { + +BlitSaw:: BlitSaw( StkFloat frequency ) +{ + if ( frequency <= 0.0 ) { + oStream_ << "BlitSaw::BlitSaw: argument (" << frequency << ") must be positive!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + nHarmonics_ = 0; + this->reset(); + this->setFrequency( frequency ); +} + +BlitSaw :: ~BlitSaw() +{ +} + +void BlitSaw :: reset() +{ + phase_ = 0.0f; + state_ = 0.0; + lastFrame_[0] = 0.0; +} + +void BlitSaw :: setFrequency( StkFloat frequency ) +{ + if ( frequency <= 0.0 ) { + oStream_ << "BlitSaw::setFrequency: argument (" << frequency << ") must be positive!"; + handleError( StkError::WARNING ); return; + } + + p_ = Stk::sampleRate() / frequency; + C2_ = 1 / p_; + rate_ = PI * C2_; + this->updateHarmonics(); +} + +void BlitSaw :: setHarmonics( unsigned int nHarmonics ) +{ + nHarmonics_ = nHarmonics; + this->updateHarmonics(); + + // I found that the initial DC offset could be minimized with an + // initial state setting as given below. This initialization should + // only happen before starting the oscillator for the first time + // (but after setting the frequency and number of harmonics). I + // struggled a bit to decide where best to put this and finally + // settled on here. In general, the user shouldn't be messing with + // the number of harmonics once the oscillator is running because + // this is automatically taken care of in the setFrequency() + // function. (GPS - 1 October 2005) + state_ = -0.5 * a_; +} + +void BlitSaw :: updateHarmonics( void ) +{ + if ( nHarmonics_ <= 0 ) { + unsigned int maxHarmonics = (unsigned int) floor( 0.5 * p_ ); + m_ = 2 * maxHarmonics + 1; + } + else + m_ = 2 * nHarmonics_ + 1; + + a_ = m_ / p_; +} + +} // stk namespace diff --git a/src/stk/src/BlitSquare.cpp b/src/stk/src/BlitSquare.cpp new file mode 100644 index 00000000..2a51b07a --- /dev/null +++ b/src/stk/src/BlitSquare.cpp @@ -0,0 +1,95 @@ +/***************************************************/ +/*! \class BlitSquare + \brief STK band-limited square wave class. + + This class generates a band-limited square wave signal. It is + derived in part from the approach reported by Stilson and Smith in + "Alias-Free Digital Synthesis of Classic Analog Waveforms", 1996. + The algorithm implemented in this class uses a SincM function with + an even M value to achieve a bipolar bandlimited impulse train. + This signal is then integrated to achieve a square waveform. The + integration process has an associated DC offset so a DC blocking + filter is applied at the output. + + The user can specify both the fundamental frequency of the + waveform and the number of harmonics contained in the resulting + signal. + + If nHarmonics is 0, then the signal will contain all harmonics up + to half the sample rate. Note, however, that this setting may + produce aliasing in the signal when the frequency is changing (no + automatic modification of the number of harmonics is performed by + the setFrequency() function). Also note that the harmonics of a + square wave fall at odd integer multiples of the fundamental, so + aliasing will happen with a lower fundamental than with the other + Blit waveforms. This class is not guaranteed to be well behaved + in the presence of significant aliasing. + + Based on initial code of Robin Davies, 2005 + Modified algorithm code by Gary Scavone, 2005--2010. +*/ +/***************************************************/ + +#include "BlitSquare.h" + +namespace stk { + +BlitSquare:: BlitSquare( StkFloat frequency ) +{ + if ( frequency <= 0.0 ) { + oStream_ << "BlitSquare::BlitSquare: argument (" << frequency << ") must be positive!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + nHarmonics_ = 0; + this->setFrequency( frequency ); + this->reset(); +} + +BlitSquare :: ~BlitSquare() +{ +} + +void BlitSquare :: reset() +{ + phase_ = 0.0; + lastFrame_[0] = 0.0; + dcbState_ = 0.0; + lastBlitOutput_ = 0; +} + +void BlitSquare :: setFrequency( StkFloat frequency ) +{ + if ( frequency <= 0.0 ) { + oStream_ << "BlitSquare::setFrequency: argument (" << frequency << ") must be positive!"; + handleError( StkError::WARNING ); return; + } + + // By using an even value of the parameter M, we get a bipolar blit + // waveform at half the blit frequency. Thus, we need to scale the + // frequency value here by 0.5. (GPS, 2006). + p_ = 0.5 * Stk::sampleRate() / frequency; + rate_ = PI / p_; + this->updateHarmonics(); +} + +void BlitSquare :: setHarmonics( unsigned int nHarmonics ) +{ + nHarmonics_ = nHarmonics; + this->updateHarmonics(); +} + +void BlitSquare :: updateHarmonics( void ) +{ + // Make sure we end up with an even value of the parameter M here. + if ( nHarmonics_ <= 0 ) { + unsigned int maxHarmonics = (unsigned int) floor( 0.5 * p_ ); + m_ = 2 * (maxHarmonics + 1); + } + else + m_ = 2 * (nHarmonics_ + 1); + + a_ = m_ / p_; +} + +} // stk namespace diff --git a/src/stk/src/BlowBotl.cpp b/src/stk/src/BlowBotl.cpp new file mode 100644 index 00000000..2882712d --- /dev/null +++ b/src/stk/src/BlowBotl.cpp @@ -0,0 +1,122 @@ +/***************************************************/ +/*! \class BlowBotl + \brief STK blown bottle instrument class. + + This class implements a helmholtz resonator + (biquad filter) with a polynomial jet + excitation (a la Cook). + + Control Change Numbers: + - Noise Gain = 4 + - Vibrato Frequency = 11 + - Vibrato Gain = 1 + - Volume = 128 + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "BlowBotl.h" +#include "SKINImsg.h" + +namespace stk { + +#define __BOTTLE_RADIUS_ 0.999 + +BlowBotl :: BlowBotl( void ) +{ + dcBlock_.setBlockZero(); + + vibrato_.setFrequency( 5.925 ); + vibratoGain_ = 0.0; + + resonator_.setResonance( 500.0, __BOTTLE_RADIUS_, true ); + adsr_.setAllTimes( 0.005, 0.01, 0.8, 0.010 ); + + noiseGain_ = 20.0; + maxPressure_ = 0.0; +} + +BlowBotl :: ~BlowBotl( void ) +{ +} + +void BlowBotl :: clear( void ) +{ + resonator_.clear(); +} + +void BlowBotl :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "BlowBotl::setFrequency: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + resonator_.setResonance( frequency, __BOTTLE_RADIUS_, true ); +} + +void BlowBotl :: startBlowing( StkFloat amplitude, StkFloat rate ) +{ + if ( amplitude <= 0.0 || rate <= 0.0 ) { + oStream_ << "BlowBotl::startBowing: one or more arguments is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } + + adsr_.setAttackRate( rate ); + maxPressure_ = amplitude; + adsr_.keyOn(); +} + +void BlowBotl :: stopBlowing( StkFloat rate ) +{ + if ( rate <= 0.0 ) { + oStream_ << "BlowBotl::stopBowing: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } + + adsr_.setReleaseRate( rate ); + adsr_.keyOff(); +} + +void BlowBotl :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + this->setFrequency( frequency ); + startBlowing( 1.1 + (amplitude * 0.20), amplitude * 0.02); + outputGain_ = amplitude + 0.001; +} + +void BlowBotl :: noteOff( StkFloat amplitude ) +{ + this->stopBlowing( amplitude * 0.02 ); +} + +void BlowBotl :: controlChange( int number, StkFloat value ) +{ +#if defined(_STK_DEBUG_) + if ( value < 0 || ( number != 101 && value > 128.0 ) ) { + oStream_ << "BlowBotl::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if (number == __SK_NoiseLevel_) // 4 + noiseGain_ = normalizedValue * 30.0; + else if (number == __SK_ModFrequency_) // 11 + vibrato_.setFrequency( normalizedValue * 12.0 ); + else if (number == __SK_ModWheel_) // 1 + vibratoGain_ = normalizedValue * 0.4; + else if (number == __SK_AfterTouch_Cont_) // 128 + adsr_.setTarget( normalizedValue ); +#if defined(_STK_DEBUG_) + else { + oStream_ << "BlowBotl::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/BlowHole.cpp b/src/stk/src/BlowHole.cpp new file mode 100644 index 00000000..5f342517 --- /dev/null +++ b/src/stk/src/BlowHole.cpp @@ -0,0 +1,224 @@ +/***************************************************/ +/*! \class BlowHole + \brief STK clarinet physical model with one + register hole and one tonehole. + + This class is based on the clarinet model, + with the addition of a two-port register hole + and a three-port dynamic tonehole + implementation, as discussed by Scavone and + Cook (1998). + + In this implementation, the distances between + the reed/register hole and tonehole/bell are + fixed. As a result, both the tonehole and + register hole will have variable influence on + the playing frequency, which is dependent on + the length of the air column. In addition, + the highest playing freqeuency is limited by + these fixed lengths. + + This is a digital waveguide model, making its + use possibly subject to patents held by Stanford + University, Yamaha, and others. + + Control Change Numbers: + - Reed Stiffness = 2 + - Noise Gain = 4 + - Tonehole State = 11 + - Register State = 1 + - Breath Pressure = 128 + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "BlowHole.h" +#include "SKINImsg.h" +#include + +namespace stk { + +BlowHole :: BlowHole( StkFloat lowestFrequency ) +{ + if ( lowestFrequency <= 0.0 ) { + oStream_ << "BlowHole::BlowHole: argument is less than or equal to zero!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + unsigned long nDelays = (unsigned long) ( 0.5 * Stk::sampleRate() / lowestFrequency ); + + // delays[0] is the delay line between the reed and the register vent. + delays_[0].setDelay( 5.0 * Stk::sampleRate() / 22050.0 ); + // delays[1] is the delay line between the register vent and the tonehole. + delays_[1].setMaximumDelay( nDelays + 1 ); + // delays[2] is the delay line between the tonehole and the end of the bore. + delays_[2].setDelay( 4.0 * Stk::sampleRate() / 22050.0 ); + + reedTable_.setOffset( 0.7 ); + reedTable_.setSlope( -0.3 ); + + // Calculate the initial tonehole three-port scattering coefficient + StkFloat rb = 0.0075; // main bore radius + StkFloat rth = 0.003; // tonehole radius + scatter_ = -pow(rth,2) / ( pow(rth,2) + 2*pow(rb,2) ); + + // Calculate tonehole coefficients and set for initially open. + StkFloat te = 1.4 * rth; // effective length of the open hole + thCoeff_ = (te*2*Stk::sampleRate() - 347.23) / (te*2*Stk::sampleRate() + 347.23); + tonehole_.setA1( -thCoeff_ ); + tonehole_.setB0( thCoeff_ ); + tonehole_.setB1( -1.0 ); + + // Calculate register hole filter coefficients + double r_rh = 0.0015; // register vent radius + te = 1.4 * r_rh; // effective length of the open hole + double xi = 0.0; // series resistance term + double zeta = 347.23 + 2*PI*pow(rb,2)*xi/1.1769; + double psi = 2*PI*pow(rb,2)*te / (PI*pow(r_rh,2)); + StkFloat rhCoeff = (zeta - 2 * Stk::sampleRate() * psi) / (zeta + 2 * Stk::sampleRate() * psi); + rhGain_ = -347.23 / (zeta + 2 * Stk::sampleRate() * psi); + vent_.setA1( rhCoeff ); + vent_.setB0( 1.0 ); + vent_.setB1( 1.0 ); + // Start with register vent closed + vent_.setGain( 0.0 ); + + vibrato_.setFrequency((StkFloat) 5.735); + outputGain_ = 1.0; + noiseGain_ = 0.2; + vibratoGain_ = 0.01; + + this->setFrequency( 220.0 ); + this->clear(); +} + +BlowHole :: ~BlowHole( void ) +{ +} + +void BlowHole :: clear( void ) +{ + delays_[0].clear(); + delays_[1].clear(); + delays_[2].clear(); + filter_.tick( 0.0 ); + tonehole_.tick( 0.0 ); + vent_.tick( 0.0 ); +} + +void BlowHole :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "BlowHole::setFrequency: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + // Account for approximate filter delays and one sample "lastOut" delay. + StkFloat delay = ( Stk::sampleRate() / frequency ) * 0.5 - 3.5; + delay -= delays_[0].getDelay() + delays_[2].getDelay(); + + delays_[1].setDelay( delay ); +} + +void BlowHole :: setVent( StkFloat newValue ) +{ + // This method allows setting of the register vent "open-ness" at + // any point between "Open" (newValue = 1) and "Closed" + // (newValue = 0). + + StkFloat gain; + + if ( newValue <= 0.0 ) + gain = 0.0; + else if ( newValue >= 1.0 ) + gain = rhGain_; + else + gain = newValue * rhGain_; + + vent_.setGain( gain ); +} + +void BlowHole :: setTonehole( StkFloat newValue ) +{ + // This method allows setting of the tonehole "open-ness" at + // any point between "Open" (newValue = 1) and "Closed" + // (newValue = 0). + StkFloat new_coeff; + + if ( newValue <= 0.0 ) + new_coeff = 0.9995; + else if ( newValue >= 1.0 ) + new_coeff = thCoeff_; + else + new_coeff = ( newValue * (thCoeff_ - 0.9995) ) + 0.9995; + + tonehole_.setA1( -new_coeff ); + tonehole_.setB0( new_coeff ); +} + +void BlowHole :: startBlowing( StkFloat amplitude, StkFloat rate ) +{ + if ( amplitude <= 0.0 || rate <= 0.0 ) { + oStream_ << "BlowHole::startBlowing: one or more arguments is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } + + envelope_.setRate( rate ); + envelope_.setTarget( amplitude ); +} + +void BlowHole :: stopBlowing( StkFloat rate ) +{ + if ( rate <= 0.0 ) { + oStream_ << "BlowHole::stopBlowing: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } + + envelope_.setRate( rate ); + envelope_.setTarget( 0.0 ); +} + +void BlowHole :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + this->setFrequency( frequency ); + this->startBlowing( 0.55 + (amplitude * 0.30), amplitude * 0.005 ); + outputGain_ = amplitude + 0.001; +} + +void BlowHole :: noteOff( StkFloat amplitude ) +{ + this->stopBlowing( amplitude * 0.01 ); +} + +void BlowHole :: controlChange( int number, StkFloat value ) +{ +#if defined(_STK_DEBUG_) + if ( Stk::inRange( value, 0.0, 128.0 ) == false ) { + oStream_ << "BlowHole::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if (number == __SK_ReedStiffness_) // 2 + reedTable_.setSlope( -0.44 + (0.26 * normalizedValue) ); + else if (number == __SK_NoiseLevel_) // 4 + noiseGain_ = ( normalizedValue * 0.4); + else if (number == __SK_ModFrequency_) // 11 + this->setTonehole( normalizedValue ); + else if (number == __SK_ModWheel_) // 1 + this->setVent( normalizedValue ); + else if (number == __SK_AfterTouch_Cont_) // 128 + envelope_.setValue( normalizedValue ); +#if defined(_STK_DEBUG_) + else { + oStream_ << "BlowHole::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/Bowed.cpp b/src/stk/src/Bowed.cpp new file mode 100644 index 00000000..7e955bac --- /dev/null +++ b/src/stk/src/Bowed.cpp @@ -0,0 +1,180 @@ +/***************************************************/ +/*! \class Bowed + \brief STK bowed string instrument class. + + This class implements a bowed string model, a + la Smith (1986), after McIntyre, Schumacher, + Woodhouse (1983). + + This is a digital waveguide model, making its + use possibly subject to patents held by + Stanford University, Yamaha, and others. + + Control Change Numbers: + - Bow Pressure = 2 + - Bow Position = 4 + - Vibrato Frequency = 11 + - Vibrato Gain = 1 + - Bow Velocity = 100 + - Frequency = 101 + - Volume = 128 + + by Perry R. Cook and Gary P. Scavone, 1995--2021. + Contributions by Esteban Maestre, 2011. +*/ +/***************************************************/ + +#include "Bowed.h" +#include "SKINImsg.h" + +namespace stk { + +Bowed :: Bowed( StkFloat lowestFrequency ) +{ + if ( lowestFrequency <= 0.0 ) { + oStream_ << "Bowed::Bowed: argument is less than or equal to zero!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + unsigned long nDelays = (unsigned long) ( Stk::sampleRate() / lowestFrequency ); + + neckDelay_.setMaximumDelay( nDelays + 1 ); + neckDelay_.setDelay( 100.0 ); + + bridgeDelay_.setMaximumDelay( nDelays + 1 ); + bridgeDelay_.setDelay( 29.0 ); + + bowTable_.setSlope( 3.0 ); + bowTable_.setOffset( 0.001); + bowDown_ = false; + maxVelocity_ = 0.25; + + vibrato_.setFrequency( 6.12723 ); + vibratoGain_ = 0.0; + + stringFilter_.setPole( 0.75 - (0.2 * 22050.0 / Stk::sampleRate()) ); + stringFilter_.setGain( 0.95 ); + + // Old single body filter + //bodyFilter_.setResonance( 500.0, 0.85, true ); + //bodyFilter_.setGain( 0.2 ); + + // New body filter provided by Esteban Maestre (cascade of second-order sections) + bodyFilters_[0].setCoefficients( 1.0, 1.5667, 0.3133, -0.5509, -0.3925 ); + bodyFilters_[1].setCoefficients( 1.0, -1.9537, 0.9542, -1.6357, 0.8697 ); + bodyFilters_[2].setCoefficients( 1.0, -1.6683, 0.8852, -1.7674, 0.8735 ); + bodyFilters_[3].setCoefficients( 1.0, -1.8585, 0.9653, -1.8498, 0.9516 ); + bodyFilters_[4].setCoefficients( 1.0, -1.9299, 0.9621, -1.9354, 0.9590 ); + bodyFilters_[5].setCoefficients( 1.0, -1.9800, 0.9888, -1.9867, 0.9923 ); + + adsr_.setAllTimes( 0.02, 0.005, 0.9, 0.01 ); + + betaRatio_ = 0.127236; + + // Necessary to initialize internal variables. + this->setFrequency( 220.0 ); + this->clear(); +} + +Bowed :: ~Bowed( void ) +{ +} + +void Bowed :: clear( void ) +{ + neckDelay_.clear(); + bridgeDelay_.clear(); + stringFilter_.clear(); + for ( int i=0; i<6; i++ ) bodyFilters_[i].clear(); +} + +void Bowed :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "Bowed::setFrequency: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + // Delay = length - approximate filter delay. + baseDelay_ = Stk::sampleRate() / frequency - 4.0; + if ( baseDelay_ <= 0.0 ) baseDelay_ = 0.3; + bridgeDelay_.setDelay( baseDelay_ * betaRatio_ ); // bow to bridge length + neckDelay_.setDelay( baseDelay_ * (1.0 - betaRatio_) ); // bow to nut (finger) length +} + +void Bowed :: startBowing( StkFloat amplitude, StkFloat rate ) +{ + if ( amplitude <= 0.0 || rate <= 0.0 ) { + oStream_ << "Bowed::startBowing: one or more arguments is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } + + adsr_.setAttackRate( rate ); + adsr_.keyOn(); + maxVelocity_ = 0.03 + ( 0.2 * amplitude ); + bowDown_ = true; +} + +void Bowed :: stopBowing( StkFloat rate ) +{ + if ( rate <= 0.0 ) { + oStream_ << "Bowed::stopBowing: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } + + adsr_.setReleaseRate( rate ); + adsr_.keyOff(); +} + +void Bowed :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + this->startBowing( amplitude, amplitude * 0.001 ); + this->setFrequency( frequency ); +} + +void Bowed :: noteOff( StkFloat amplitude ) +{ + this->stopBowing( (1.0 - amplitude) * 0.005 ); +} + +void Bowed :: controlChange( int number, StkFloat value ) +{ +#if defined(_STK_DEBUG_) + if ( value < 0 || ( number != 101 && value > 128.0 ) ) { + oStream_ << "Bowed::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if ( number == __SK_BowPressure_ ) { // 2 + if ( normalizedValue > 0.0 ) bowDown_ = true; + else bowDown_ = false; + bowTable_.setSlope( 5.0 - (4.0 * normalizedValue) ); + } + else if ( number == __SK_BowPosition_ ) { // 4 + betaRatio_ = normalizedValue; + bridgeDelay_.setDelay( baseDelay_ * betaRatio_ ); + neckDelay_.setDelay( baseDelay_ * (1.0 - betaRatio_) ); + } + else if ( number == __SK_ModFrequency_ ) // 11 + vibrato_.setFrequency( normalizedValue * 12.0 ); + else if ( number == __SK_ModWheel_ ) // 1 + vibratoGain_ = ( normalizedValue * 0.4 ); + else if ( number == 100 ) // 100: set instantaneous bow velocity + adsr_.setTarget( normalizedValue ); + else if ( number == 101 ) // 101: set instantaneous value of frequency + this->setFrequency( value ); + else if (number == __SK_AfterTouch_Cont_) // 128 + adsr_.setTarget( normalizedValue ); +#if defined(_STK_DEBUG_) + else { + oStream_ << "Bowed::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/Brass.cpp b/src/stk/src/Brass.cpp new file mode 100644 index 00000000..6a659835 --- /dev/null +++ b/src/stk/src/Brass.cpp @@ -0,0 +1,158 @@ +/***************************************************/ +/*! \class Brass + \brief STK simple brass instrument class. + + This class implements a simple brass instrument + waveguide model, a la Cook (TBone, HosePlayer). + + This is a digital waveguide model, making its + use possibly subject to patents held by + Stanford University, Yamaha, and others. + + Control Change Numbers: + - Lip Tension = 2 + - Slide Length = 4 + - Vibrato Frequency = 11 + - Vibrato Gain = 1 + - Volume = 128 + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Brass.h" +#include "SKINImsg.h" +#include + +namespace stk { + +Brass :: Brass( StkFloat lowestFrequency ) +{ + if ( lowestFrequency <= 0.0 ) { + oStream_ << "Brass::Brass: argument is less than or equal to zero!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + unsigned long nDelays = (unsigned long) ( Stk::sampleRate() / lowestFrequency ); + delayLine_.setMaximumDelay( nDelays + 1 ); + + lipFilter_.setGain( 0.03 ); + dcBlock_.setBlockZero(); + adsr_.setAllTimes( 0.005, 0.001, 1.0, 0.010 ); + + vibrato_.setFrequency( 6.137 ); + vibratoGain_ = 0.0; + maxPressure_ = 0.0; + lipTarget_ = 0.0; + + this->clear(); + + // This is necessary to initialize variables. + this->setFrequency( 220.0 ); +} + +Brass :: ~Brass( void ) +{ +} + +void Brass :: clear( void ) +{ + delayLine_.clear(); + lipFilter_.clear(); + dcBlock_.clear(); +} + +void Brass :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "Brass::setFrequency: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + // Fudge correction for filter delays. + slideTarget_ = ( Stk::sampleRate() / frequency * 2.0 ) + 3.0; + delayLine_.setDelay( slideTarget_ ); // play a harmonic + + lipTarget_ = frequency; + lipFilter_.setResonance( frequency, 0.997 ); +} + +void Brass :: setLip( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "Brass::setLip: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + lipFilter_.setResonance( frequency, 0.997 ); +} + +void Brass :: startBlowing( StkFloat amplitude, StkFloat rate ) +{ + if ( amplitude <= 0.0 || rate <= 0.0 ) { + oStream_ << "Brass::startBlowing: one or more arguments is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } + + adsr_.setAttackRate( rate ); + maxPressure_ = amplitude; + adsr_.keyOn(); +} + +void Brass :: stopBlowing( StkFloat rate ) +{ + if ( rate <= 0.0 ) { + oStream_ << "Brass::stopBlowing: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } + + adsr_.setReleaseRate( rate ); + adsr_.keyOff(); +} + +void Brass :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + this->setFrequency( frequency ); + this->startBlowing( amplitude, amplitude * 0.001 ); +} + +void Brass :: noteOff( StkFloat amplitude ) +{ + this->stopBlowing( amplitude * 0.005 ); +} + +void Brass :: controlChange( int number, StkFloat value ) +{ +#if defined(_STK_DEBUG_) + if ( Stk::inRange( value, 0.0, 128.0 ) == false ) { + oStream_ << "Brass::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if (number == __SK_LipTension_) { // 2 + StkFloat temp = lipTarget_ * pow( 4.0, (2.0 * normalizedValue) - 1.0 ); + this->setLip( temp ); + } + else if (number == __SK_SlideLength_) // 4 + delayLine_.setDelay( slideTarget_ * (0.5 + normalizedValue) ); + else if (number == __SK_ModFrequency_) // 11 + vibrato_.setFrequency( normalizedValue * 12.0 ); + else if (number == __SK_ModWheel_ ) // 1 + vibratoGain_ = normalizedValue * 0.4; + else if (number == __SK_AfterTouch_Cont_) // 128 + adsr_.setTarget( normalizedValue ); +#if defined(_STK_DEBUG_) + else { + oStream_ << "Brass::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/Chorus.cpp b/src/stk/src/Chorus.cpp new file mode 100644 index 00000000..1efd1db1 --- /dev/null +++ b/src/stk/src/Chorus.cpp @@ -0,0 +1,56 @@ +/***************************************************/ +/*! \class Chorus + \brief STK chorus effect class. + + This class implements a chorus effect. It takes a monophonic + input signal and produces a stereo output signal. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Chorus.h" + +namespace stk { + +Chorus :: Chorus( StkFloat baseDelay ) +{ + lastFrame_.resize( 1, 2, 0.0 ); // resize lastFrame_ for stereo output + delayLine_[0].setMaximumDelay( (unsigned long) (baseDelay * 1.414) + 2); + delayLine_[0].setDelay( baseDelay ); + delayLine_[1].setMaximumDelay( (unsigned long) (baseDelay * 1.414) + 2); + delayLine_[1].setDelay( baseDelay ); + baseLength_ = baseDelay; + + mods_[0].setFrequency( 0.2 ); + mods_[1].setFrequency( 0.222222 ); + modDepth_ = 0.05; + effectMix_ = 0.5; + this->clear(); +} + +void Chorus :: clear( void ) +{ + delayLine_[0].clear(); + delayLine_[1].clear(); + lastFrame_[0] = 0.0; + lastFrame_[1] = 0.0; +} + + void Chorus :: setModDepth( StkFloat depth ) +{ + if ( depth < 0.0 || depth > 1.0 ) { + oStream_ << "Chorus::setModDepth(): depth argument must be between 0.0 - 1.0!"; + handleError( StkError::WARNING ); return; + } + + modDepth_ = depth; +}; + +void Chorus :: setModFrequency( StkFloat frequency ) +{ + mods_[0].setFrequency( frequency ); + mods_[1].setFrequency( frequency * 1.1111 ); +} + +} // stk namespace diff --git a/src/stk/src/Clarinet.cpp b/src/stk/src/Clarinet.cpp new file mode 100644 index 00000000..f81e0bf2 --- /dev/null +++ b/src/stk/src/Clarinet.cpp @@ -0,0 +1,138 @@ +/***************************************************/ +/*! \class Clarinet + \brief STK clarinet physical model class. + + This class implements a simple clarinet + physical model, as discussed by Smith (1986), + McIntyre, Schumacher, Woodhouse (1983), and + others. + + This is a digital waveguide model, making its + use possibly subject to patents held by Stanford + University, Yamaha, and others. + + Control Change Numbers: + - Reed Stiffness = 2 + - Noise Gain = 4 + - Vibrato Frequency = 11 + - Vibrato Gain = 1 + - Breath Pressure = 128 + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Clarinet.h" +#include "SKINImsg.h" + +namespace stk { + +Clarinet :: Clarinet( StkFloat lowestFrequency ) +{ + if ( lowestFrequency <= 0.0 ) { + oStream_ << "Clarinet::Clarinet: argument is less than or equal to zero!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + unsigned long nDelays = (unsigned long) ( 0.5 * Stk::sampleRate() / lowestFrequency ); + delayLine_.setMaximumDelay( nDelays + 1 ); + + reedTable_.setOffset( 0.7 ); + reedTable_.setSlope( -0.3 ); + + vibrato_.setFrequency( 5.735 ); + outputGain_ = 1.0; + noiseGain_ = 0.2; + vibratoGain_ = 0.1; + + this->setFrequency( 220.0 ); + this->clear(); +} + +Clarinet :: ~Clarinet( void ) +{ +} + +void Clarinet :: clear( void ) +{ + delayLine_.clear(); + filter_.tick( 0.0 ); +} + +void Clarinet :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "Clarinet::setFrequency: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + // Account for filter delay and one sample "lastOut" delay. + StkFloat delay = ( Stk::sampleRate() / frequency ) * 0.5 - filter_.phaseDelay( frequency ) - 1.0; + delayLine_.setDelay( delay ); +} + +void Clarinet :: startBlowing( StkFloat amplitude, StkFloat rate ) +{ + if ( amplitude <= 0.0 || rate <= 0.0 ) { + oStream_ << "Clarinet::startBlowing: one or more arguments is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } + + envelope_.setRate( rate ); + envelope_.setTarget( amplitude ); +} + +void Clarinet :: stopBlowing( StkFloat rate ) +{ + if ( rate <= 0.0 ) { + oStream_ << "Clarinet::stopBlowing: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } + + envelope_.setRate( rate ); + envelope_.setTarget( 0.0 ); +} + +void Clarinet :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + this->setFrequency( frequency ); + this->startBlowing( 0.55 + (amplitude * 0.30), amplitude * 0.005 ); + outputGain_ = amplitude + 0.001; +} + +void Clarinet :: noteOff( StkFloat amplitude ) +{ + this->stopBlowing( amplitude * 0.01 ); +} + +void Clarinet :: controlChange( int number, StkFloat value ) +{ +#if defined(_STK_DEBUG_) + if ( Stk::inRange( value, 0.0, 128.0 ) == false ) { + oStream_ << "Clarinet::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if ( number == __SK_ReedStiffness_ ) // 2 + reedTable_.setSlope( -0.44 + ( 0.26 * normalizedValue ) ); + else if ( number == __SK_NoiseLevel_ ) // 4 + noiseGain_ = ( normalizedValue * 0.4 ); + else if ( number == __SK_ModFrequency_ ) // 11 + vibrato_.setFrequency( normalizedValue * 12.0 ); + else if ( number == __SK_ModWheel_ ) // 1 + vibratoGain_ = ( normalizedValue * 0.5 ); + else if ( number == __SK_AfterTouch_Cont_ ) // 128 + envelope_.setValue( normalizedValue ); +#if defined(_STK_DEBUG_) + else { + oStream_ << "Clarinet::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/Delay.cpp b/src/stk/src/Delay.cpp new file mode 100644 index 00000000..27e1cc83 --- /dev/null +++ b/src/stk/src/Delay.cpp @@ -0,0 +1,110 @@ +/***************************************************/ +/*! \class Delay + \brief STK non-interpolating delay line class. + + This class implements a non-interpolating digital delay-line. If + the delay and maximum length are not specified during + instantiation, a fixed maximum length of 4095 and a delay of zero + is set. + + A non-interpolating delay line is typically used in fixed + delay-length applications, such as for reverberation. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Delay.h" + +namespace stk { + +Delay :: Delay( unsigned long delay, unsigned long maxDelay ) +{ + // Writing before reading allows delays from 0 to length-1. + // If we want to allow a delay of maxDelay, we need a + // delay-line of length = maxDelay+1. + if ( delay > maxDelay ) { + oStream_ << "Delay::Delay: maxDelay must be > than delay argument!\n"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( ( maxDelay + 1 ) > inputs_.size() ) + inputs_.resize( maxDelay + 1, 1, 0.0 ); + + inPoint_ = 0; + this->setDelay( delay ); +} + +Delay :: ~Delay() +{ +} + +void Delay :: setMaximumDelay( unsigned long delay ) +{ + if ( delay < inputs_.size() ) return; + inputs_.resize( delay + 1, 1, 0.0 ); +} + +void Delay :: setDelay( unsigned long delay ) +{ + if ( delay > inputs_.size() - 1 ) { // The value is too big. + oStream_ << "Delay::setDelay: argument (" << delay << ") greater than maximum!\n"; + handleError( StkError::WARNING ); return; + } + + // read chases write + if ( inPoint_ >= delay ) outPoint_ = inPoint_ - delay; + else outPoint_ = inputs_.size() + inPoint_ - delay; + delay_ = delay; +} + +StkFloat Delay :: energy( void ) const +{ + unsigned long i; + StkFloat e = 0; + if ( inPoint_ >= outPoint_ ) { + for ( i=outPoint_; i= 0.5!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( delay > (StkFloat) maxDelay ) { + oStream_ << "DelayA::DelayA: maxDelay must be > than delay argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + // Writing before reading allows delays from 0 to length-1. + if ( maxDelay + 1 > inputs_.size() ) + inputs_.resize( maxDelay + 1, 1, 0.0 ); + + inPoint_ = 0; + this->setDelay( delay ); + apInput_ = 0.0; + doNextOut_ = true; +} + +DelayA :: ~DelayA() +{ +} + +void DelayA :: clear() +{ + for ( unsigned int i=0; i length ) { // The value is too big. + oStream_ << "DelayA::setDelay: argument (" << delay << ") greater than maximum!"; + handleError( StkError::WARNING ); return; + } + + if ( delay < 0.5 ) { + oStream_ << "DelayA::setDelay: argument (" << delay << ") less than 0.5 not possible!"; + handleError( StkError::WARNING ); + } + + StkFloat outPointer = inPoint_ - delay + 1.0; // outPoint chases inpoint + delay_ = delay; + + while ( outPointer < 0 ) + outPointer += length; // modulo maximum length + + outPoint_ = (long) outPointer; // integer part + if ( outPoint_ == length ) outPoint_ = 0; + alpha_ = 1.0 + outPoint_ - outPointer; // fractional part + + if ( alpha_ < 0.5 ) { + // The optimal range for alpha is about 0.5 - 1.5 in order to + // achieve the flattest phase delay response. + outPoint_ += 1; + if ( outPoint_ >= length ) outPoint_ -= length; + alpha_ += (StkFloat) 1.0; + } + + coeff_ = (1.0 - alpha_) / (1.0 + alpha_); // coefficient for allpass +} + +StkFloat DelayA :: tapOut( unsigned long tapDelay ) +{ + long tap = inPoint_ - tapDelay - 1; + while ( tap < 0 ) // Check for wraparound. + tap += inputs_.size(); + + return inputs_[tap]; +} + +void DelayA :: tapIn( StkFloat value, unsigned long tapDelay ) +{ + long tap = inPoint_ - tapDelay - 1; + while ( tap < 0 ) // Check for wraparound. + tap += inputs_.size(); + + inputs_[tap] = value; +} + +} // stk namespace diff --git a/src/stk/src/DelayL.cpp b/src/stk/src/DelayL.cpp new file mode 100644 index 00000000..1dfc8d43 --- /dev/null +++ b/src/stk/src/DelayL.cpp @@ -0,0 +1,73 @@ +/***************************************************/ +/*! \class DelayL + \brief STK linear interpolating delay line class. + + This class implements a fractional-length digital delay-line using + first-order linear interpolation. If the delay and maximum length + are not specified during instantiation, a fixed maximum length of + 4095 and a delay of zero is set. + + Linear interpolation is an efficient technique for achieving + fractional delay lengths, though it does introduce high-frequency + signal attenuation to varying degrees depending on the fractional + delay setting. The use of higher order Lagrange interpolators can + typically improve (minimize) this attenuation characteristic. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "DelayL.h" + +namespace stk { + +DelayL :: DelayL( StkFloat delay, unsigned long maxDelay ) +{ + if ( delay < 0.0 ) { + oStream_ << "DelayL::DelayL: delay must be >= 0.0!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( delay > (StkFloat) maxDelay ) { + oStream_ << "DelayL::DelayL: maxDelay must be > than delay argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + // Writing before reading allows delays from 0 to length-1. + if ( maxDelay + 1 > inputs_.size() ) + inputs_.resize( maxDelay + 1, 1, 0.0 ); + + inPoint_ = 0; + this->setDelay( delay ); + doNextOut_ = true; +} + +DelayL :: ~DelayL() +{ +} + +void DelayL :: setMaximumDelay( unsigned long delay ) +{ + if ( delay < inputs_.size() ) return; + inputs_.resize(delay + 1, 1, 0.0); +} + +StkFloat DelayL :: tapOut( unsigned long tapDelay ) +{ + long tap = inPoint_ - tapDelay - 1; + while ( tap < 0 ) // Check for wraparound. + tap += inputs_.size(); + + return inputs_[tap]; +} + +void DelayL :: tapIn( StkFloat value, unsigned long tapDelay ) +{ + long tap = inPoint_ - tapDelay - 1; + while ( tap < 0 ) // Check for wraparound. + tap += inputs_.size(); + + inputs_[tap] = value; +} + +} // stk namespace diff --git a/src/stk/src/Drummer.cpp b/src/stk/src/Drummer.cpp new file mode 100644 index 00000000..51f627c8 --- /dev/null +++ b/src/stk/src/Drummer.cpp @@ -0,0 +1,141 @@ +/***************************************************/ +/*! \class Drummer + \brief STK drum sample player class. + + This class implements a drum sampling + synthesizer using FileWvIn objects and one-pole + filters. The drum rawwave files are sampled + at 22050 Hz, but will be appropriately + interpolated for other sample rates. You can + specify the maximum polyphony (maximum number + of simultaneous voices) via a #define in the + Drummer.h. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Drummer.h" +#include + +namespace stk { + +// Not really General MIDI yet. +unsigned char genMIDIMap[128] = + { 0,0,0,0,0,0,0,0, // 0-7 + 0,0,0,0,0,0,0,0, // 8-15 + 0,0,0,0,0,0,0,0, // 16-23 + 0,0,0,0,0,0,0,0, // 24-31 + 0,0,0,0,1,0,2,0, // 32-39 + 2,3,6,3,6,4,7,4, // 40-47 + 5,8,5,0,0,0,10,0, // 48-55 + 9,0,0,0,0,0,0,0, // 56-63 + 0,0,0,0,0,0,0,0, // 64-71 + 0,0,0,0,0,0,0,0, // 72-79 + 0,0,0,0,0,0,0,0, // 80-87 + 0,0,0,0,0,0,0,0, // 88-95 + 0,0,0,0,0,0,0,0, // 96-103 + 0,0,0,0,0,0,0,0, // 104-111 + 0,0,0,0,0,0,0,0, // 112-119 + 0,0,0,0,0,0,0,0 // 120-127 + }; + +char waveNames[DRUM_NUMWAVES][16] = + { + "dope.raw", + "bassdrum.raw", + "snardrum.raw", + "tomlowdr.raw", + "tommiddr.raw", + "tomhidrm.raw", + "hihatcym.raw", + "ridecymb.raw", + "crashcym.raw", + "cowbell1.raw", + "tambourn.raw" + }; + +Drummer :: Drummer( void ) : Instrmnt() +{ + // This counts the number of sounding voices. + nSounding_ = 0; + soundOrder_ = std::vector (DRUM_POLYPHONY, -1); + soundNumber_ = std::vector (DRUM_POLYPHONY, -1); +} + +Drummer :: ~Drummer( void ) +{ +} + +void Drummer :: noteOn( StkFloat instrument, StkFloat amplitude ) +{ + if ( amplitude < 0.0 || amplitude > 1.0 ) { + oStream_ << "Drummer::noteOn: amplitude parameter is out of bounds!"; + handleError( StkError::WARNING ); return; + } + + // Yes, this is tres kludgey. + int noteNumber = (int) ( ( 12 * log( instrument / 220.0 ) / log( 2.0 ) ) + 57.01 ); + + // If we already have a wave of this note number loaded, just reset + // it. Otherwise, look first for an unused wave or preempt the + // oldest if already at maximum polyphony. + int iWave; + for ( iWave=0; iWave soundOrder_[iWave] ) + soundOrder_[j] -= 1; + } + } + soundOrder_[iWave] = nSounding_ - 1; + soundNumber_[iWave] = noteNumber; + //std::cout << "iWave = " << iWave << ", nSounding = " << nSounding_ << ", soundOrder[] = " << soundOrder_[iWave] << std::endl; + + // Concatenate the STK rawwave path to the rawwave file + waves_[iWave].openFile( (Stk::rawwavePath() + waveNames[ genMIDIMap[ noteNumber ] ]).c_str(), true ); + if ( Stk::sampleRate() != 22050.0 ) + waves_[iWave].setRate( 22050.0 / Stk::sampleRate() ); + filters_[iWave].setPole( 0.999 - (amplitude * 0.6) ); + filters_[iWave].setGain( amplitude ); + } + + /* +#if defined(_STK_DEBUG_) + oStream_ << "Drummer::noteOn: number sounding = " << nSounding_ << ", notes: "; + for ( int i=0; i + +namespace stk { + +Echo :: Echo( unsigned long maximumDelay ) : Effect() +{ + this->setMaximumDelay( maximumDelay ); + delayLine_.setDelay( length_ >> 1 ); + effectMix_ = 0.5; + this->clear(); +} + +void Echo :: clear( void ) +{ + delayLine_.clear(); + lastFrame_[0] = 0.0; +} + +void Echo :: setMaximumDelay( unsigned long delay ) +{ + if ( delay == 0 ) { + oStream_ << "Echo::setMaximumDelay: parameter cannot be zero!"; + handleError( StkError::WARNING ); return; + } + + length_ = delay; + delayLine_.setMaximumDelay( delay ); +} + +void Echo :: setDelay( unsigned long delay ) +{ + if ( delay > length_ ) { + oStream_ << "Echo::setDelay: parameter is greater than maximum delay length!"; + handleError( StkError::WARNING ); return; + } + + delayLine_.setDelay( delay ); +} + +} // stk namespace diff --git a/src/stk/src/Envelope.cpp b/src/stk/src/Envelope.cpp new file mode 100644 index 00000000..0c1afeb4 --- /dev/null +++ b/src/stk/src/Envelope.cpp @@ -0,0 +1,87 @@ +/***************************************************/ +/*! \class Envelope + \brief STK linear line envelope class. + + This class implements a simple linear line envelope generator + which is capable of ramping to an arbitrary target value by a + specified \e rate. It also responds to simple \e keyOn and \e + keyOff messages, ramping to a specified target (default = 1.0) on + keyOn and to a specified target (default = 0.0) on keyOff. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Envelope.h" +#include + +namespace stk { + +Envelope :: Envelope( void ) : Generator() +{ + target_ = 0.0; + value_ = 0.0; + rate_ = 0.001; + state_ = 0; + Stk::addSampleRateAlert( this ); +} + +Envelope :: ~Envelope( void ) +{ + Stk::removeSampleRateAlert( this ); +} + +Envelope& Envelope :: operator= ( const Envelope& e ) +{ + if ( this != &e ) { + target_ = e.target_; + value_ = e.value_; + rate_ = e.rate_; + state_ = e.state_; + } + + return *this; +} + +void Envelope :: sampleRateChanged( StkFloat newRate, StkFloat oldRate ) +{ + if ( !ignoreSampleRateChange_ ) + rate_ = oldRate * rate_ / newRate; +} + +void Envelope :: setRate( StkFloat rate ) +{ + if ( rate < 0.0 ) { + oStream_ << "Envelope::setRate: argument must be >= 0.0!"; + handleError( StkError::WARNING ); return; + } + + rate_ = rate; +} + +void Envelope :: setTime( StkFloat time ) +{ + if ( time <= 0.0 ) { + oStream_ << "Envelope::setTime: argument must be > 0.0!"; + handleError( StkError::WARNING ); return; + } + + //rate_ = 1.0 / ( time * Stk::sampleRate() ); + rate_ = fabs(target_ - value_) / ( time * Stk::sampleRate() ); +} + +void Envelope :: setTarget( StkFloat target ) +{ + target_ = target; + if ( value_ != target_ ) state_ = 1; +} + +void Envelope :: setValue( StkFloat value ) +{ + state_ = 0; + target_ = value; + value_ = value; + lastFrame_[0] = value_; +} + +} // stk namespace diff --git a/src/stk/src/FM.cpp b/src/stk/src/FM.cpp new file mode 100644 index 00000000..d5f3ae72 --- /dev/null +++ b/src/stk/src/FM.cpp @@ -0,0 +1,179 @@ +/***************************************************/ +/*! \class FM + \brief STK abstract FM synthesis base class. + + This class controls an arbitrary number of + waves and envelopes, determined via a + constructor argument. + + Control Change Numbers: + - Control One = 2 + - Control Two = 4 + - LFO Speed = 11 + - LFO Depth = 1 + - ADSR 2 & 4 Target = 128 + + The basic Chowning/Stanford FM patent expired + in 1995, but there exist follow-on patents, + mostly assigned to Yamaha. If you are of the + type who should worry about this (making + money) worry away. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "FM.h" +#include "SKINImsg.h" + +namespace stk { + +FM :: FM( unsigned int operators ) + : nOperators_(operators) +{ + if ( nOperators_ == 0 ) { + oStream_ << "FM::FM: Number of operators must be greater than zero!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + twozero_.setB2( -1.0 ); + twozero_.setGain( 0.0 ); + + vibrato_.setFrequency( 6.0 ); + + unsigned int j; + adsr_.resize( nOperators_ ); + waves_.resize( nOperators_ ); + for (j=0; j=0; i--) { + fmGains_[i] = temp; + temp *= 0.933033; + } + + temp = 1.0; + for (i=15; i>=0; i--) { + fmSusLevels_[i] = temp; + temp *= 0.707101; + } + + temp = 8.498186; + for (i=0; i<32; i++) { + fmAttTimes_[i] = temp; + temp *= 0.707101; + } +} + +FM :: ~FM( void ) +{ + for (unsigned int i=0; isetFrequency( baseFrequency_ * ratios_[i] ); +} + +void FM :: setRatio( unsigned int waveIndex, StkFloat ratio ) +{ + if ( waveIndex >= nOperators_ ) { + oStream_ << "FM:setRatio: waveIndex parameter is greater than the number of operators!"; + handleError( StkError::WARNING ); return; + } + + ratios_[waveIndex] = ratio; + if (ratio > 0.0) + waves_[waveIndex]->setFrequency( baseFrequency_ * ratio ); + else + waves_[waveIndex]->setFrequency( ratio ); +} + +void FM :: setGain( unsigned int waveIndex, StkFloat gain ) +{ + if ( waveIndex >= nOperators_ ) { + oStream_ << "FM::setGain: waveIndex parameter is greater than the number of operators!"; + handleError( StkError::WARNING ); return; + } + + gains_[waveIndex] = gain; +} + +void FM :: keyOn( void ) +{ + for ( unsigned int i=0; ikeyOn(); +} + +void FM :: keyOff( void ) +{ + for ( unsigned int i=0; ikeyOff(); +} + +void FM :: noteOff( StkFloat amplitude ) +{ + this->keyOff(); +} + +void FM :: controlChange( int number, StkFloat value ) +{ +#if defined(_STK_DEBUG_) + if ( Stk::inRange( value, 0.0, 128.0 ) == false ) { + oStream_ << "FM::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if (number == __SK_Breath_) // 2 + this->setControl1( normalizedValue ); + else if (number == __SK_FootControl_) // 4 + this->setControl2( normalizedValue ); + else if (number == __SK_ModFrequency_) // 11 + this->setModulationSpeed( normalizedValue * 12.0); + else if (number == __SK_ModWheel_) // 1 + this->setModulationDepth( normalizedValue ); + else if (number == __SK_AfterTouch_Cont_) { // 128 + //adsr_[0]->setTarget( normalizedValue ); + adsr_[1]->setTarget( normalizedValue ); + //adsr_[2]->setTarget( normalizedValue ); + adsr_[3]->setTarget( normalizedValue ); + } +#if defined(_STK_DEBUG_) + else { + oStream_ << "FM::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/FMVoices.cpp b/src/stk/src/FMVoices.cpp new file mode 100644 index 00000000..469e1a36 --- /dev/null +++ b/src/stk/src/FMVoices.cpp @@ -0,0 +1,164 @@ +/***************************************************/ +/*! \class FMVoices + \brief STK singing FM synthesis instrument. + + This class implements 3 carriers and a common + modulator, also referred to as algorithm 6 of + the TX81Z. + + \code + Algorithm 6 is : + /->1 -\ + 4-|-->2 - +-> Out + \->3 -/ + \endcode + + Control Change Numbers: + - Vowel = 2 + - Spectral Tilt = 4 + - LFO Speed = 11 + - LFO Depth = 1 + - ADSR 2 & 4 Target = 128 + + The basic Chowning/Stanford FM patent expired + in 1995, but there exist follow-on patents, + mostly assigned to Yamaha. If you are of the + type who should worry about this (making + money) worry away. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "FMVoices.h" +#include "SKINImsg.h" +#include "Phonemes.h" + +namespace stk { + +FMVoices :: FMVoices( void ) + : FM() +{ + // Concatenate the STK rawwave path to the rawwave files + for ( unsigned int i=0; i<3; i++ ) + waves_[i] = new FileLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + waves_[3] = new FileLoop( (Stk::rawwavePath() + "fwavblnk.raw").c_str(), true ); + + this->setRatio(0, 2.00); + this->setRatio(1, 4.00); + this->setRatio(2, 12.0); + this->setRatio(3, 1.00); + + gains_[3] = fmGains_[80]; + + adsr_[0]->setAllTimes( 0.05, 0.05, fmSusLevels_[15], 0.05); + adsr_[1]->setAllTimes( 0.05, 0.05, fmSusLevels_[15], 0.05); + adsr_[2]->setAllTimes( 0.05, 0.05, fmSusLevels_[15], 0.05); + adsr_[3]->setAllTimes( 0.01, 0.01, fmSusLevels_[15], 0.5); + + twozero_.setGain( 0.0 ); + modDepth_ = (StkFloat) 0.005; + currentVowel_ = 0; + tilt_[0] = 1.0; + tilt_[1] = 0.5; + tilt_[2] = 0.2; + mods_[0] = 1.0; + mods_[1] = 1.1; + mods_[2] = 1.1; + baseFrequency_ = 110.0; + this->setFrequency( 110.0 ); +} + +FMVoices :: ~FMVoices( void ) +{ +} + +void FMVoices :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "FMVoices::setFrequency: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat temp, temp2 = 0.0; + int tempi = 0; + unsigned int i = 0; + + if (currentVowel_ < 32) { + i = currentVowel_; + temp2 = 0.9; + } + else if (currentVowel_ < 64) { + i = currentVowel_ - 32; + temp2 = 1.0; + } + else if (currentVowel_ < 96) { + i = currentVowel_ - 64; + temp2 = 1.1; + } + else if (currentVowel_ < 128) { + i = currentVowel_ - 96; + temp2 = 1.2; + } + else return; + + baseFrequency_ = frequency; + temp = (temp2 * Phonemes::formantFrequency(i, 0) / baseFrequency_) + 0.5; + tempi = (int) temp; + this->setRatio( 0, (StkFloat) tempi ); + temp = (temp2 * Phonemes::formantFrequency(i, 1) / baseFrequency_) + 0.5; + tempi = (int) temp; + this->setRatio( 1, (StkFloat) tempi ); + temp = (temp2 * Phonemes::formantFrequency(i, 2) / baseFrequency_) + 0.5; + tempi = (int) temp; + this->setRatio( 2, (StkFloat) tempi ); + gains_[0] = 1.0; + gains_[1] = 1.0; + gains_[2] = 1.0; +} + +void FMVoices :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + this->setFrequency( frequency ); + tilt_[0] = amplitude; + tilt_[1] = amplitude * amplitude; + tilt_[2] = tilt_[1] * amplitude; + this->keyOn(); +} + +void FMVoices :: controlChange( int number, StkFloat value ) +{ +#if defined(_STK_DEBUG_) + if ( Stk::inRange( value, 0.0, 128.0 ) == false ) { + oStream_ << "FMVoices::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if (number == __SK_Breath_) // 2 + gains_[3] = fmGains_[(int) ( normalizedValue * 99.9 )]; + else if (number == __SK_FootControl_) { // 4 + currentVowel_ = (int) (normalizedValue * 127.0); + this->setFrequency(baseFrequency_); + } + else if (number == __SK_ModFrequency_) // 11 + this->setModulationSpeed( normalizedValue * 12.0); + else if (number == __SK_ModWheel_) // 1 + this->setModulationDepth( normalizedValue ); + else if (number == __SK_AfterTouch_Cont_) { // 128 + tilt_[0] = normalizedValue; + tilt_[1] = normalizedValue * normalizedValue; + tilt_[2] = tilt_[1] * normalizedValue; + } +#if defined(_STK_DEBUG_) + else { + oStream_ << "FMVoices::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/FileLoop.cpp b/src/stk/src/FileLoop.cpp new file mode 100644 index 00000000..67ff0e99 --- /dev/null +++ b/src/stk/src/FileLoop.cpp @@ -0,0 +1,237 @@ +/***************************************************/ +/*! \class FileLoop + \brief STK file looping / oscillator class. + + This class provides audio file looping functionality. Any audio + file that can be loaded by FileRead can be looped using this + class. + + FileLoop supports multi-channel data. It is important to + distinguish the tick() method that computes a single frame (and + returns only the specified sample of a multi-channel frame) from + the overloaded one that takes an StkFrames object for + multi-channel and/or multi-frame data. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "FileLoop.h" +#include + +namespace stk { + +FileLoop :: FileLoop( unsigned long chunkThreshold, unsigned long chunkSize ) + : FileWvIn( chunkThreshold, chunkSize ), phaseOffset_(0.0) +{ + Stk::addSampleRateAlert( this ); +} + +FileLoop :: FileLoop( std::string fileName, bool raw, bool doNormalize, + unsigned long chunkThreshold, unsigned long chunkSize, + bool doInt2FloatScaling ) + : FileWvIn( chunkThreshold, chunkSize ), phaseOffset_(0.0) +{ + this->openFile( fileName, raw, doNormalize, doInt2FloatScaling ); + Stk::addSampleRateAlert( this ); +} + +FileLoop :: ~FileLoop( void ) +{ + Stk::removeSampleRateAlert( this ); +} + +void FileLoop :: openFile( std::string fileName, bool raw, bool doNormalize, bool doInt2FloatScaling ) +{ + // Call close() in case another file is already open. + this->closeFile(); + + // Attempt to open the file ... an error might be thrown here. + file_.open( fileName, raw ); + + // Determine whether chunking or not. + if ( file_.fileSize() > chunkThreshold_ ) { + chunking_ = true; + chunkPointer_ = 0; + data_.resize( chunkSize_ + 1, file_.channels() ); + } + else { + chunking_ = false; + data_.resize( file_.fileSize() + 1, file_.channels() ); + } + + if ( doInt2FloatScaling ) + int2floatscaling_ = true; + else + int2floatscaling_ = false; + + // Load all or part of the data. + file_.read( data_, 0, int2floatscaling_ ); + + if ( chunking_ ) { // If chunking, save the first sample frame for later. + firstFrame_.resize( 1, data_.channels() ); + for ( unsigned int i=0; isetRate( data_.dataRate() / Stk::sampleRate() ); + + if ( doNormalize & !chunking_ ) this->normalize(); + + this->reset(); +} + +void FileLoop :: setRate( StkFloat rate ) +{ + rate_ = rate; + + if ( fmod( rate_, 1.0 ) != 0.0 ) interpolate_ = true; + else interpolate_ = false; +} + +void FileLoop :: addTime( StkFloat time ) +{ + // Add an absolute time in samples. + time_ += time; + + while ( time_ < 0.0 ) + time_ += fileSize_; + while ( time_ >= fileSize_ ) + time_ -= fileSize_; +} + +void FileLoop :: addPhase( StkFloat angle ) +{ + // Add a time in cycles (one cycle = fileSize). + time_ += fileSize_ * angle; + + while ( time_ < 0.0 ) + time_ += fileSize_; + while ( time_ >= fileSize_ ) + time_ -= fileSize_; +} + +void FileLoop :: addPhaseOffset( StkFloat angle ) +{ + // Add a phase offset in cycles, where 1.0 = fileSize. + phaseOffset_ = fileSize_ * angle; +} + +StkFloat FileLoop :: tick( unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= data_.channels() ) { + oStream_ << "FileLoop::tick(): channel argument and soundfile data are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + if ( finished_ ) return 0.0; + + // Check limits of time address ... if necessary, recalculate modulo + // fileSize. + while ( time_ < 0.0 ) + time_ += fileSize_; + while ( time_ >= fileSize_ ) + time_ -= fileSize_; + + StkFloat tyme = time_; + if ( phaseOffset_ ) { + tyme += phaseOffset_; + while ( tyme < 0.0 ) + tyme += fileSize_; + while ( tyme >= fileSize_ ) + tyme -= fileSize_; + } + + if ( chunking_ ) { + + // Check the time address vs. our current buffer limits. + if ( ( time_ < (StkFloat) chunkPointer_ ) || + ( time_ > (StkFloat) ( chunkPointer_ + chunkSize_ - 1 ) ) ) { + + while ( time_ < (StkFloat) chunkPointer_ ) { // negative rate + chunkPointer_ -= chunkSize_ - 1; // overlap chunks by one frame + if ( chunkPointer_ < 0 ) chunkPointer_ = 0; + } + while ( time_ > (StkFloat) ( chunkPointer_ + chunkSize_ - 1 ) ) { // positive rate + chunkPointer_ += chunkSize_ - 1; // overlap chunks by one frame + if ( chunkPointer_ + chunkSize_ > fileSize_ ) { // at end of file + chunkPointer_ = fileSize_ - chunkSize_ + 1; // leave extra frame at end of buffer + // Now fill extra frame with first frame data. + for ( unsigned int j=0; j frames.channels() - nChannels ) { + oStream_ << "FileLoop::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; i2) + soundfiles are supported. The file data is + returned via an external StkFrames object + passed to the read() function. This class + does not store its own copy of the file data, + rather the data is read directly from disk. + + FileRead currently supports uncompressed WAV, + AIFF/AIFC, SND (AU), MAT-file (Matlab), and + STK RAW file formats. Signed integer (8-, + 16-, 24- and 32-bit) and floating-point (32- and + 64-bit) data types are supported. Compressed + data types are not supported. + + STK RAW files have no header and are assumed to + contain a monophonic stream of 16-bit signed + integers in big-endian byte order at a sample + rate of 22050 Hz. MAT-file data should be saved + in an array with each data channel filling a + matrix row. The sample rate for MAT-files should + be specified in a variable named "fs". If no + such variable is found, the sample rate is + assumed to be 44100 Hz. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "FileRead.h" +#include +#include +#include +#include +#include + +namespace stk { + +FileRead :: FileRead() + : fd_(0), fileSize_(0), channels_(0), dataType_(0), fileRate_(0.0) +{ +} + +FileRead :: FileRead( std::string fileName, bool typeRaw, unsigned int nChannels, + StkFormat format, StkFloat rate ) + : fd_(0) +{ + open( fileName, typeRaw, nChannels, format, rate ); +} + +FileRead :: ~FileRead() +{ + if ( fd_ ) + fclose( fd_ ); +} + +void FileRead :: close( void ) +{ + if ( fd_ ) fclose( fd_ ); + fd_ = 0; + wavFile_ = false; + fileSize_ = 0; + channels_ = 0; + dataType_ = 0; + fileRate_ = 0.0; +} + +bool FileRead :: isOpen( void ) +{ + if ( fd_ ) return true; + else return false; +} + +void FileRead :: open( std::string fileName, bool typeRaw, unsigned int nChannels, + StkFormat format, StkFloat rate ) +{ + // If another file is open, close it. + close(); + + // Try to open the file. + fd_ = fopen( fileName.c_str(), "rb" ); + if ( !fd_ ) { + oStream_ << "FileRead::open: could not open or find file (" << fileName << ")!"; + handleError( StkError::FILE_NOT_FOUND ); + } + + // Attempt to determine file type from header (unless RAW). + bool result = false; + if ( typeRaw ) + result = getRawInfo( fileName.c_str(), nChannels, format, rate ); + else { + char header[12]; + if ( fread( &header, 4, 3, fd_ ) != 3 ) goto error; + if ( !strncmp( header, "RIFF", 4 ) && + !strncmp( &header[8], "WAVE", 4 ) ) + result = getWavInfo( fileName.c_str() ); + else if ( !strncmp( header, ".snd", 4 ) ) + result = getSndInfo( fileName.c_str() ); + else if ( !strncmp( header, "FORM", 4 ) && + ( !strncmp( &header[8], "AIFF", 4 ) || !strncmp(&header[8], "AIFC", 4) ) ) + result = getAifInfo( fileName.c_str() ); + else { + if ( fseek( fd_, 126, SEEK_SET ) == -1 ) goto error; + if ( fread( &header, 2, 1, fd_ ) != 1 ) goto error; + if ( !strncmp( header, "MI", 2 ) || + !strncmp( header, "IM", 2 ) ) + result = getMatInfo( fileName.c_str() ); + else { + oStream_ << "FileRead::open: file (" << fileName << ") format unknown."; + handleError( StkError::FILE_UNKNOWN_FORMAT ); + } + } + } + + // If here, we had a file type candidate but something else went wrong. + if ( result == false ) + handleError( StkError::FILE_ERROR ); + + // Check for empty files. + if ( fileSize_ == 0 ) { + oStream_ << "FileRead::open: file (" << fileName << ") data size is zero!"; + handleError( StkError::FILE_ERROR ); + } + + return; + + error: + oStream_ << "FileRead::open: error reading file (" << fileName << ")!"; + handleError( StkError::FILE_ERROR ); +} + +bool FileRead :: getRawInfo( const char *fileName, unsigned int nChannels, StkFormat format, StkFloat rate ) +{ + // Use the system call "stat" to determine the file length. + struct stat filestat; + if ( stat(fileName, &filestat) == -1 ) { + oStream_ << "FileRead: Could not stat RAW file (" << fileName << ")."; + return false; + } + if ( nChannels == 0 ) { + oStream_ << "FileRead: number of channels can't be 0 (" << fileName << ")."; + return false; + } + + // Rawwave files have no header and by default, are assumed to + // contain a monophonic stream of 16-bit signed integers in + // big-endian byte order at a sample rate of 22050 Hz. However, + // different parameters can be specified if desired. + dataOffset_ = 0; + channels_ = nChannels; + dataType_ = format; + fileRate_ = rate; + int sampleBytes = 0; + if ( format == STK_SINT8 ) sampleBytes = 1; + else if ( format == STK_SINT16 ) sampleBytes = 2; + else if ( format == STK_SINT32 || format == STK_FLOAT32 ) sampleBytes = 4; + else if ( format == STK_FLOAT64 ) sampleBytes = 8; + else { + oStream_ << "FileRead: StkFormat " << format << " is invalid (" << fileName << ")."; + return false; + } + + fileSize_ = (long) filestat.st_size / sampleBytes / channels_; // length in frames + + byteswap_ = false; +#ifdef __LITTLE_ENDIAN__ + byteswap_ = true; +#endif + + return true; +} + +bool FileRead :: getWavInfo( const char *fileName ) +{ + // Find "format" chunk ... it must come before the "data" chunk. + char id[4]; + SINT32 chunkSize; + if ( fread(&id, 4, 1, fd_) != 1 ) goto error; + while ( strncmp(id, "fmt ", 4) ) { + if ( fread(&chunkSize, 4, 1, fd_) != 1 ) goto error; +#ifndef __LITTLE_ENDIAN__ + swap32((unsigned char *)&chunkSize); +#endif + if ( fseek(fd_, chunkSize, SEEK_CUR) == -1 ) goto error; + if ( fread(&id, 4, 1, fd_) != 1 ) goto error; + } + + // Check that the data is not compressed. + unsigned short format_tag; + if ( fread(&chunkSize, 4, 1, fd_) != 1 ) goto error; // Read fmt chunk size. + if ( fread(&format_tag, 2, 1, fd_) != 1 ) goto error; +#ifndef __LITTLE_ENDIAN__ + swap16((unsigned char *)&format_tag); + swap32((unsigned char *)&chunkSize); +#endif + if ( format_tag == 0xFFFE ) { // WAVE_FORMAT_EXTENSIBLE + dataOffset_ = ftell(fd_); + if ( fseek(fd_, 14, SEEK_CUR) == -1 ) goto error; + unsigned short extSize; + if ( fread(&extSize, 2, 1, fd_) != 1 ) goto error; +#ifndef __LITTLE_ENDIAN__ + swap16((unsigned char *)&extSize); +#endif + if ( extSize == 0 ) goto error; + if ( fseek(fd_, 6, SEEK_CUR) == -1 ) goto error; + if ( fread(&format_tag, 2, 1, fd_) != 1 ) goto error; +#ifndef __LITTLE_ENDIAN__ + swap16((unsigned char *)&format_tag); +#endif + if ( fseek(fd_, dataOffset_, SEEK_SET) == -1 ) goto error; + } + if ( format_tag != 1 && format_tag != 3 ) { // PCM = 1, FLOAT = 3 + oStream_ << "FileRead: "<< fileName << " contains an unsupported data format type (" << format_tag << ")."; + return false; + } + + // Get number of channels from the header. + SINT16 temp; + if ( fread(&temp, 2, 1, fd_) != 1 ) goto error; +#ifndef __LITTLE_ENDIAN__ + swap16((unsigned char *)&temp); +#endif + channels_ = (unsigned int ) temp; + + // Get file sample rate from the header. + SINT32 srate; + if ( fread(&srate, 4, 1, fd_) != 1 ) goto error; +#ifndef __LITTLE_ENDIAN__ + swap32((unsigned char *)&srate); +#endif + fileRate_ = (StkFloat) srate; + + // Determine the data type. + dataType_ = 0; + if ( fseek(fd_, 6, SEEK_CUR) == -1 ) goto error; // Locate bits_per_sample info. + if ( fread(&temp, 2, 1, fd_) != 1 ) goto error; +#ifndef __LITTLE_ENDIAN__ + swap16((unsigned char *)&temp); +#endif + if ( format_tag == 1 ) { + if ( temp == 8 ) + dataType_ = STK_SINT8; + else if ( temp == 16 ) + dataType_ = STK_SINT16; + else if ( temp == 24 ) + dataType_ = STK_SINT24; + else if ( temp == 32 ) + dataType_ = STK_SINT32; + } + else if ( format_tag == 3 ) { + if ( temp == 32 ) + dataType_ = STK_FLOAT32; + else if ( temp == 64 ) + dataType_ = STK_FLOAT64; + } + if ( dataType_ == 0 ) { + oStream_ << "FileRead: " << temp << " bits per sample with data format " << format_tag << " are not supported (" << fileName << ")."; + return false; + } + + // Jump over any remaining part of the "fmt" chunk. + if ( fseek(fd_, chunkSize-16, SEEK_CUR) == -1 ) goto error; + + // Find "data" chunk ... it must come after the "fmt" chunk. + if ( fread(&id, 4, 1, fd_) != 1 ) goto error; + + while ( strncmp(id, "data", 4) ) { + if ( fread(&chunkSize, 4, 1, fd_) != 1 ) goto error; +#ifndef __LITTLE_ENDIAN__ + swap32((unsigned char *)&chunkSize); +#endif + chunkSize += chunkSize % 2; // chunk sizes must be even + if ( fseek(fd_, chunkSize, SEEK_CUR) == -1 ) goto error; + if ( fread(&id, 4, 1, fd_) != 1 ) goto error; + } + + // Get length of data from the header. + SINT32 bytes; + if ( fread(&bytes, 4, 1, fd_) != 1 ) goto error; +#ifndef __LITTLE_ENDIAN__ + swap32((unsigned char *)&bytes); +#endif + fileSize_ = bytes / temp / channels_; // sample frames + fileSize_ *= 8; // sample frames + + dataOffset_ = ftell(fd_); + byteswap_ = false; +#ifndef __LITTLE_ENDIAN__ + byteswap_ = true; +#endif + + wavFile_ = true; + return true; + + error: + oStream_ << "FileRead: error reading WAV file (" << fileName << ")."; + return false; +} + +bool FileRead :: getSndInfo( const char *fileName ) +{ + // Determine the data type. + UINT32 format; + if ( fseek(fd_, 12, SEEK_SET) == -1 ) goto error; // Locate format + if ( fread(&format, 4, 1, fd_) != 1 ) goto error; +#ifdef __LITTLE_ENDIAN__ + swap32((unsigned char *)&format); +#endif + + if (format == 2) dataType_ = STK_SINT8; + else if (format == 3) dataType_ = STK_SINT16; + else if (format == 4) dataType_ = STK_SINT24; + else if (format == 5) dataType_ = STK_SINT32; + else if (format == 6) dataType_ = STK_FLOAT32; + else if (format == 7) dataType_ = STK_FLOAT64; + else { + oStream_ << "FileRead: data format in file " << fileName << " is not supported."; + return false; + } + + // Get file sample rate from the header. + UINT32 srate; + if ( fread(&srate, 4, 1, fd_) != 1 ) goto error; +#ifdef __LITTLE_ENDIAN__ + swap32((unsigned char *)&srate); +#endif + fileRate_ = (StkFloat) srate; + + // Get number of channels from the header. + UINT32 chans; + if ( fread(&chans, 4, 1, fd_) != 1 ) goto error; +#ifdef __LITTLE_ENDIAN__ + swap32((unsigned char *)&chans); +#endif + channels_ = chans; + + UINT32 offset; + if ( fseek(fd_, 4, SEEK_SET) == -1 ) goto error; + if ( fread(&offset, 4, 1, fd_) != 1 ) goto error; +#ifdef __LITTLE_ENDIAN__ + swap32((unsigned char *)&offset); +#endif + dataOffset_ = offset; + + // Get length of data from the header. + if ( fread(&fileSize_, 4, 1, fd_) != 1 ) goto error; +#ifdef __LITTLE_ENDIAN__ + swap32((unsigned char *)&fileSize_); +#endif + // Convert to sample frames. + if ( dataType_ == STK_SINT8 ) + fileSize_ /= channels_; + if ( dataType_ == STK_SINT16 ) + fileSize_ /= 2 * channels_; + else if ( dataType_ == STK_SINT24 ) + fileSize_ /= 3 * channels_; + else if ( dataType_ == STK_SINT32 || dataType_ == STK_FLOAT32 ) + fileSize_ /= 4 * channels_; + else if ( dataType_ == STK_FLOAT64 ) + fileSize_ /= 8 * channels_; + + byteswap_ = false; +#ifdef __LITTLE_ENDIAN__ + byteswap_ = true; +#endif + + return true; + + error: + oStream_ << "FileRead: Error reading SND file (" << fileName << ")."; + return false; +} + +bool FileRead :: getAifInfo( const char *fileName ) +{ + bool aifc = false; + char id[4]; + + // Determine whether this is AIFF or AIFC. + if ( fseek(fd_, 8, SEEK_SET) == -1 ) goto error; + if ( fread(&id, 4, 1, fd_) != 1 ) goto error; + if ( !strncmp(id, "AIFC", 4) ) aifc = true; + + // Find "common" chunk + SINT32 chunkSize; + if ( fread(&id, 4, 1, fd_) != 1) goto error; + while ( strncmp(id, "COMM", 4) ) { + if ( fread(&chunkSize, 4, 1, fd_) != 1 ) goto error; +#ifdef __LITTLE_ENDIAN__ + swap32((unsigned char *)&chunkSize); +#endif + chunkSize += chunkSize % 2; // chunk sizes must be even + if ( fseek(fd_, chunkSize, SEEK_CUR) == -1 ) goto error; + if ( fread(&id, 4, 1, fd_) != 1 ) goto error; + } + + // Get number of channels from the header. + SINT16 temp; + if ( fseek(fd_, 4, SEEK_CUR) == -1 ) goto error; // Jump over chunk size + if ( fread(&temp, 2, 1, fd_) != 1 ) goto error; +#ifdef __LITTLE_ENDIAN__ + swap16((unsigned char *)&temp); +#endif + channels_ = temp; + + // Get length of data from the header. + SINT32 frames; + if ( fread(&frames, 4, 1, fd_) != 1 ) goto error; +#ifdef __LITTLE_ENDIAN__ + swap32((unsigned char *)&frames); +#endif + fileSize_ = frames; // sample frames + + // Read the number of bits per sample. + if ( fread(&temp, 2, 1, fd_) != 1 ) goto error; +#ifdef __LITTLE_ENDIAN__ + swap16((unsigned char *)&temp); +#endif + + // Get file sample rate from the header. For AIFF files, this value + // is stored in a 10-byte, IEEE Standard 754 floating point number, + // so we need to convert it first. + unsigned char srate[10]; + unsigned char exp; + unsigned long mantissa; + unsigned long last; + if ( fread(&srate, 10, 1, fd_) != 1 ) goto error; + mantissa = (unsigned long) *(unsigned long *)(srate+2); +#ifdef __LITTLE_ENDIAN__ + swap32((unsigned char *)&mantissa); +#endif + exp = 30 - *(srate+1); + last = 0; + while (exp--) { + last = mantissa; + mantissa >>= 1; + } + if (last & 0x00000001) mantissa++; + fileRate_ = (StkFloat) mantissa; + + byteswap_ = false; +#ifdef __LITTLE_ENDIAN__ + byteswap_ = true; +#endif + + // Determine the data format. + dataType_ = 0; + if ( aifc == false ) { + if ( temp <= 8 ) dataType_ = STK_SINT8; + else if ( temp <= 16 ) dataType_ = STK_SINT16; + else if ( temp <= 24 ) dataType_ = STK_SINT24; + else if ( temp <= 32 ) dataType_ = STK_SINT32; + } + else { + if ( fread(&id, 4, 1, fd_) != 1 ) goto error; + if ( !strncmp(id, "sowt", 4) ) { // uncompressed little-endian + if ( byteswap_ == false ) byteswap_ = true; + else byteswap_ = false; + } + if ( !strncmp(id, "NONE", 4) || !strncmp(id, "sowt", 4) ) { + if ( temp <= 8 ) dataType_ = STK_SINT8; + else if ( temp <= 16 ) dataType_ = STK_SINT16; + else if ( temp <= 24 ) dataType_ = STK_SINT24; + else if ( temp <= 32 ) dataType_ = STK_SINT32; + } + else if ( (!strncmp(id, "fl32", 4) || !strncmp(id, "FL32", 4)) && temp == 32 ) dataType_ = STK_FLOAT32; + else if ( (!strncmp(id, "fl64", 4) || !strncmp(id, "FL64", 4)) && temp == 64 ) dataType_ = STK_FLOAT64; + } + if ( dataType_ == 0 ) { + oStream_ << "FileRead: AIFF/AIFC file (" << fileName << ") has unsupported data type (" << id << ")."; + return false; + } + + // Start at top to find data (SSND) chunk ... chunk order is undefined. + if ( fseek(fd_, 12, SEEK_SET) == -1 ) goto error; + + // Find data (SSND) chunk + if ( fread(&id, 4, 1, fd_) != 1 ) goto error; + while ( strncmp(id, "SSND", 4) ) { + if ( fread(&chunkSize, 4, 1, fd_) != 1 ) goto error; +#ifdef __LITTLE_ENDIAN__ + swap32((unsigned char *)&chunkSize); +#endif + chunkSize += chunkSize % 2; // chunk sizes must be even + if ( fseek(fd_, chunkSize, SEEK_CUR) == -1 ) goto error; + if ( fread(&id, 4, 1, fd_) != 1 ) goto error; + } + + // Skip over chunk size, offset, and blocksize fields + if ( fseek(fd_, 12, SEEK_CUR) == -1 ) goto error; + + dataOffset_ = ftell(fd_); + return true; + + error: + oStream_ << "FileRead: Error reading AIFF file (" << fileName << ")."; + return false; +} + +bool FileRead :: findNextMatArray( SINT32 *chunkSize, SINT32 *rows, SINT32 *columns, SINT32 *nametype ) +{ + // Look for the next data array element. The file pointer should be + // at the data element type when this function is called. + SINT32 datatype; + *chunkSize = 0; + do { + if ( fseek(fd_, *chunkSize, SEEK_CUR) == -1 ) return false; + if ( fread(&datatype, 4, 1, fd_) != 1 ) return false; + if ( byteswap_ ) swap32((unsigned char *)&datatype); + if ( fread(chunkSize, 4, 1, fd_) != 1 ) return false; + if ( byteswap_ ) swap32((unsigned char *)chunkSize); + } while ( datatype != 14 ); + + // Check dimension subelement size to make sure 2D + if ( fseek(fd_, 20, SEEK_CUR) == -1 ) return false; + SINT32 size; + if ( fread(&size, 4, 1, fd_) != 1 ) return false; + if ( byteswap_ ) swap32((unsigned char *)&size); + if ( size != 8 ) return false; + + // Read dimensions data + if ( fread(rows, 4, 1, fd_) != 1 ) return false; + if ( byteswap_ ) swap32((unsigned char *)rows); + if ( fread(columns, 4, 1, fd_) != 1 ) return false; + if ( byteswap_ ) swap32((unsigned char *)columns); + + // Read array name subelement type + if ( fread(nametype, 4, 1, fd_) != 1 ) return false; + if ( byteswap_ ) swap32((unsigned char *)nametype); + + return true; +} + +bool FileRead :: getMatInfo( const char *fileName ) +{ + // MAT-file formatting information is available at: + // http://www.mathworks.com/access/helpdesk/help/pdf_doc/matlab/matfile_format.pdf + + // Verify this is a version 5 MAT-file format. + char head[5]; + if ( fseek(fd_, 0, SEEK_SET) == -1 ) goto error; + if ( fread(&head, 4, 1, fd_) != 1 ) goto error; + // If any of the first 4 characters of the header = 0, then this is + // a Version 4 MAT-file. + head[4] = '\0'; + if ( strstr(head, "0") ) { + oStream_ << "FileRead: " << fileName << " appears to be a Version 4 MAT-file, which is not currently supported."; + return false; + } + + // Determine the endian-ness of the file. + char mi[2]; + byteswap_ = false; + // Locate "M" and "I" characters in header. + if ( fseek(fd_, 126, SEEK_SET) == -1 ) goto error; + if ( fread(&mi, 2, 1, fd_) != 1) goto error; +#ifdef __LITTLE_ENDIAN__ + if ( !strncmp(mi, "MI", 2) ) + byteswap_ = true; + else if ( strncmp(mi, "IM", 2) ) goto error; +#else + if ( !strncmp(mi, "IM", 2)) + byteswap_ = true; + else if ( strncmp(mi, "MI", 2) ) goto error; +#endif + + // We are expecting a data element containing the audio data and an + // optional data element containing the sample rate (with an array + // name of "fs"). Both elements should be stored as a Matlab array + // type (14). + + bool doneParsing, haveData, haveSampleRate; + SINT32 chunkSize, rows, columns, nametype; + long dataoffset; + doneParsing = false; + haveData = false; + haveSampleRate = false; + while ( !doneParsing ) { + + dataoffset = ftell( fd_ ); // save location in file + if ( findNextMatArray( &chunkSize, &rows, &columns, &nametype ) == false ) { + // No more Matlab array type chunks found. + if ( !haveData ) { + oStream_ << "FileRead: No audio data found in MAT-file (" << fileName << ")."; + return false; + } + else if ( !haveSampleRate ) { + fileRate_ = 44100.0; + oStream_ << "FileRead: No sample rate found ... assuming 44100.0"; + handleError( StkError::WARNING ); + return true; + } + else return true; + } + + if ( !haveSampleRate && rows == 1 && columns == 1 ) { // Parse for sample rate. + + SINT32 namesize = 4; + if ( nametype == 1 ) { // array name > 4 characters + if ( fread(&namesize, 4, 1, fd_) != 1 ) goto error; + if ( byteswap_ ) swap32((unsigned char *)&namesize); + if ( namesize != 2 ) goto tryagain; // expecting name = "fs" + namesize = 8; // field must be padded to multiple of 8 bytes + } + char name[3]; name[2] = '\0'; + if ( fread(&name, 2, 1, fd_) != 1) goto error; + if ( strncmp(name, "fs", 2) ) goto tryagain; + + // Jump to real part data subelement, which is likely to be in a + // small data format. + if ( fseek(fd_, namesize-2, SEEK_CUR) == -1 ) goto error; + UINT32 type; + StkFloat srate; + if ( fread(&type, 4, 1, fd_) != 1 ) goto error; + if ( byteswap_ ) swap32((unsigned char *)&type); + if ( (type & 0xffff0000) != 0 ) // small data format + type = (type & 0x0000ffff); + else + if ( fseek(fd_, 4, SEEK_CUR) == -1 ) goto error; + if ( type == 1 ) { // SINT8 + signed char rate; + if ( fread(&rate, 1, 1, fd_) != 1 ) goto error; + srate = (StkFloat) rate; + } + else if ( type == 2 ) { // UINT8 + unsigned char rate; + if ( fread(&rate, 1, 1, fd_) != 1 ) goto error; + srate = (StkFloat) rate; + } + else if ( type == 3 ) { // SINT16 + SINT16 rate; + if ( fread(&rate, 2, 1, fd_) != 1 ) goto error; + if ( byteswap_ ) swap16((unsigned char *)&rate); + srate = (StkFloat) rate; + } + else if ( type == 4 ) { // UINT16 + UINT16 rate; + if ( fread(&rate, 2, 1, fd_) != 1 ) goto error; + if ( byteswap_ ) swap16((unsigned char *)&rate); + srate = (StkFloat) rate; + } + else if ( type == 5 ) { // SINT32 + SINT32 rate; + if ( fread(&rate, 4, 1, fd_) != 1 ) goto error; + if ( byteswap_ ) swap32((unsigned char *)&rate); + srate = (StkFloat) rate; + } + else if ( type == 6 ) { // UINT32 + UINT32 rate; + if ( fread(&rate, 4, 1, fd_) != 1 ) goto error; + if ( byteswap_ ) swap32((unsigned char *)&rate); + srate = (StkFloat) rate; + } + else if ( type == 7 ) { // FLOAT32 + FLOAT32 rate; + if ( fread(&rate, 4, 1, fd_) != 1 ) goto error; + if ( byteswap_ ) swap32((unsigned char *)&rate); + srate = (StkFloat) rate; + } + else if ( type == 9 ) { // FLOAT64 + FLOAT64 rate; + if ( fread(&rate, 8, 1, fd_) != 1 ) goto error; + if ( byteswap_ ) swap64((unsigned char *)&rate); + srate = (StkFloat) rate; + } + else + goto tryagain; + + if ( srate > 0 ) fileRate_ = srate; + haveSampleRate = true; + } + else if ( !haveData ) { // Parse for data. + + // Assume channels = smaller of rows or columns. + if ( rows < columns ) { + channels_ = rows; + fileSize_ = columns; + } + else { + oStream_ << "FileRead: Transpose the MAT-file array so that audio channels fill matrix rows (not columns)."; + return false; + } + + SINT32 namesize = 4; + if ( nametype == 1 ) { // array name > 4 characters + if ( fread(&namesize, 4, 1, fd_) != 1 ) goto error; + if ( byteswap_ ) swap32((unsigned char *)&namesize); + namesize = (SINT32) ceil((float)namesize / 8); + if ( fseek( fd_, namesize*8, SEEK_CUR) == -1 ) goto error; // jump over array name + } + else { + if ( fseek( fd_, 4, SEEK_CUR ) == -1 ) goto error; + } + + // Now at real part data subelement + SINT32 type; + if ( fread(&type, 4, 1, fd_) != 1 ) goto error; + if ( byteswap_ ) swap32((unsigned char *)&type); + if ( type == 1 ) dataType_ = STK_SINT8; + else if ( type == 3 ) dataType_ = STK_SINT16; + else if ( type == 5 ) dataType_ = STK_SINT32; + else if ( type == 7 ) dataType_ = STK_FLOAT32; + else if ( type == 9 ) dataType_ = STK_FLOAT64; + else { + oStream_ << "FileRead: The MAT-file array data format (" << type << ") is not supported."; + return false; + } + + // Jump to the data. + if ( fseek(fd_, 4, SEEK_CUR) == -1 ) goto error; + dataOffset_ = ftell(fd_); + haveData = true; + } + + tryagain: + if ( haveData && haveSampleRate ) doneParsing = true; + else // jump to end of data element and keep trying + if ( fseek( fd_, dataoffset+chunkSize+8, SEEK_SET) == -1 ) goto error; + } + + return true; + + error: + oStream_ << "FileRead: Error reading MAT-file (" << fileName << ") header."; + return false; +} + +void FileRead :: read( StkFrames& buffer, unsigned long startFrame, bool doNormalize ) +{ + // Make sure we have an open file. + if ( fd_ == 0 ) { + oStream_ << "FileRead::read: a file is not open!"; + Stk::handleError( StkError::WARNING ); return; + } + + // Check the buffer size. + unsigned long nFrames = buffer.frames(); + if ( nFrames == 0 ) { + oStream_ << "FileRead::read: StkFrames buffer size is zero ... no data read!"; + Stk::handleError( StkError::WARNING ); return; + } + + if ( buffer.channels() != channels_ ) { + oStream_ << "FileRead::read: StkFrames argument has incompatible number of channels!"; + Stk::handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( startFrame >= fileSize_ ) { + oStream_ << "FileRead::read: startFrame argument is greater than or equal to the file size!"; + Stk::handleError( StkError::FUNCTION_ARGUMENT ); + } + + // Check for file end. + if ( startFrame + nFrames > fileSize_ ) + nFrames = fileSize_ - startFrame; + + long i, nSamples = (long) ( nFrames * channels_ ); + unsigned long offset = startFrame * channels_; + + // Read samples into StkFrames data buffer. + if ( dataType_ == STK_SINT16 ) { + SINT16 *buf = (SINT16 *) &buffer[0]; + if ( fseek( fd_, dataOffset_+(offset*2), SEEK_SET ) == -1 ) goto error; + if ( fread( buf, nSamples * 2, 1, fd_ ) != 1 ) goto error; + if ( byteswap_ ) { + SINT16 *ptr = buf; + for ( i=nSamples-1; i>=0; i-- ) + swap16( (unsigned char *) ptr++ ); + } + if ( doNormalize ) { + StkFloat gain = 1.0 / 32768.0; + for ( i=nSamples-1; i>=0; i-- ) + buffer[i] = buf[i] * gain; + } + else { + for ( i=nSamples-1; i>=0; i-- ) + buffer[i] = buf[i]; + } + } + else if ( dataType_ == STK_SINT32 ) { + SINT32 *buf = (SINT32 *) &buffer[0]; + if ( fseek( fd_, dataOffset_+(offset*4 ), SEEK_SET ) == -1 ) goto error; + if ( fread( buf, nSamples * 4, 1, fd_ ) != 1 ) goto error; + if ( byteswap_ ) { + SINT32 *ptr = buf; + for ( i=nSamples-1; i>=0; i-- ) + swap32( (unsigned char *) ptr++ ); + } + if ( doNormalize ) { + StkFloat gain = 1.0 / 2147483648.0; + for ( i=nSamples-1; i>=0; i-- ) + buffer[i] = buf[i] * gain; + } + else { + for ( i=nSamples-1; i>=0; i-- ) + buffer[i] = buf[i]; + } + } + else if ( dataType_ == STK_FLOAT32 ) { + FLOAT32 *buf = (FLOAT32 *) &buffer[0]; + if ( fseek( fd_, dataOffset_+(offset*4), SEEK_SET ) == -1 ) goto error; + if ( fread( buf, nSamples * 4, 1, fd_ ) != 1 ) goto error; + if ( byteswap_ ) { + FLOAT32 *ptr = buf; + for ( i=nSamples-1; i>=0; i-- ) + swap32( (unsigned char *) ptr++ ); + } + for ( i=nSamples-1; i>=0; i-- ) + buffer[i] = buf[i]; + } + else if ( dataType_ == STK_FLOAT64 ) { + FLOAT64 *buf = (FLOAT64 *) &buffer[0]; + if ( fseek( fd_, dataOffset_+(offset*8), SEEK_SET ) == -1 ) goto error; + if ( fread( buf, nSamples * 8, 1, fd_ ) != 1 ) goto error; + if ( byteswap_ ) { + FLOAT64 *ptr = buf; + for ( i=nSamples-1; i>=0; i-- ) + swap64( (unsigned char *) ptr++ ); + } + for ( i=nSamples-1; i>=0; i-- ) + buffer[i] = buf[i]; + } + else if ( dataType_ == STK_SINT8 && wavFile_ ) { // 8-bit WAV data is unsigned! + unsigned char *buf = (unsigned char *) &buffer[0]; + if ( fseek( fd_, dataOffset_+offset, SEEK_SET ) == -1 ) goto error; + if ( fread( buf, nSamples, 1, fd_) != 1 ) goto error; + if ( doNormalize ) { + StkFloat gain = 1.0 / 128.0; + for ( i=nSamples-1; i>=0; i-- ) + buffer[i] = ( buf[i] - 128 ) * gain; + } + else { + for ( i=nSamples-1; i>=0; i-- ) + buffer[i] = buf[i] - 128.0; + } + } + else if ( dataType_ == STK_SINT8 ) { // signed 8-bit data + char *buf = (char *) &buffer[0]; + if ( fseek( fd_, dataOffset_+offset, SEEK_SET ) == -1 ) goto error; + if ( fread( buf, nSamples, 1, fd_ ) != 1 ) goto error; + if ( doNormalize ) { + StkFloat gain = 1.0 / 128.0; + for ( i=nSamples-1; i>=0; i-- ) + buffer[i] = buf[i] * gain; + } + else { + for ( i=nSamples-1; i>=0; i-- ) + buffer[i] = buf[i]; + } + } + else if ( dataType_ == STK_SINT24 ) { + // 24-bit values are harder to import efficiently since there is + // no native 24-bit type. The following routine works but is much + // less efficient than that used for the other data types. + SINT32 temp; + unsigned char *ptr = (unsigned char *) &temp; + StkFloat gain = 1.0 / 2147483648.0; + if ( fseek(fd_, dataOffset_+(offset*3), SEEK_SET ) == -1 ) goto error; + for ( i=0; i +#include +#include +#include + +namespace stk { + +const FileWrite::FILE_TYPE FileWrite :: FILE_RAW = 1; +const FileWrite::FILE_TYPE FileWrite :: FILE_WAV = 2; +const FileWrite::FILE_TYPE FileWrite :: FILE_SND = 3; +const FileWrite::FILE_TYPE FileWrite :: FILE_AIF = 4; +const FileWrite::FILE_TYPE FileWrite :: FILE_MAT = 5; + +// WAV header structure. See +// http://www-mmsp.ece.mcgill.ca/documents/audioformats/WAVE/Docs/rfc2361.txt +// for information regarding format codes. +struct WaveHeader { + char riff[4]; // "RIFF" + SINT32 fileSize; // in bytes + char wave[4]; // "WAVE" + char fmt[4]; // "fmt " + SINT32 chunkSize; // in bytes (16 for PCM) + SINT16 formatCode; // 1=PCM, 2=ADPCM, 3=IEEE float, 6=A-Law, 7=Mu-Law + SINT16 nChannels; // 1=mono, 2=stereo + SINT32 sampleRate; + SINT32 bytesPerSecond; + SINT16 bytesPerSample; // 2=16-bit mono, 4=16-bit stereo + SINT16 bitsPerSample; + SINT16 cbSize; // size of extension + SINT16 validBits; // valid bits per sample + SINT32 channelMask; // speaker position mask + char subformat[16]; // format code and GUID + char fact[4]; // "fact" + SINT32 factSize; // fact chunk size + SINT32 frames; // sample frames +}; + +// SND (AU) header structure (NeXT and Sun). +struct SndHeader { + char pref[4]; + SINT32 headerBytes; + SINT32 dataBytes; + SINT32 format; + SINT32 sampleRate; + SINT32 nChannels; + char comment[16]; +}; + +// AIFF/AIFC header structure ... only the part common to both +// formats. +struct AifHeader { + char form[4]; // "FORM" + SINT32 formSize; // in bytes + char aiff[4]; // "AIFF" or "AIFC" + char comm[4]; // "COMM" + SINT32 commSize; // "COMM" chunk size (18 for AIFF, 24 for AIFC) + SINT16 nChannels; // number of channels + unsigned long sampleFrames; // sample frames of audio data + SINT16 sampleSize; // in bits + unsigned char srate[10]; // IEEE 754 floating point format +}; + +struct AifSsnd { + char ssnd[4]; // "SSND" + SINT32 ssndSize; // "SSND" chunk size + unsigned long offset; // data offset in data block (should be 0) + unsigned long blockSize; // not used by STK (should be 0) +}; + +// MAT-file 5 header structure. +struct MatHeader { + char heading[124]; // Header text field + SINT16 hff[2]; // Header flag fields + SINT32 fs[16]; // Sample rate data element + SINT32 adf[11]; // Array data format fields + // There's more, but it's of variable length +}; + +FileWrite :: FileWrite() + : fd_( 0 ) +{ +} + +FileWrite::FileWrite( std::string fileName, unsigned int nChannels, FILE_TYPE type, Stk::StkFormat format ) + : fd_( 0 ) +{ + this->open( fileName, nChannels, type, format ); +} + +FileWrite :: ~FileWrite() +{ + this->close(); +} + +void FileWrite :: close( void ) +{ + if ( fd_ == 0 ) return; + + if ( fileType_ == FILE_RAW ) + fclose( fd_ ); + else if ( fileType_ == FILE_WAV ) + this->closeWavFile(); + else if ( fileType_ == FILE_SND ) + this->closeSndFile(); + else if ( fileType_ == FILE_AIF ) + this->closeAifFile(); + else if ( fileType_ == FILE_MAT ) + this->closeMatFile(); + + fd_ = 0; +} + +bool FileWrite :: isOpen( void ) +{ + if ( fd_ ) return true; + else return false; +} + +void FileWrite :: open( std::string fileName, unsigned int nChannels, FileWrite::FILE_TYPE type, Stk::StkFormat format ) +{ + // Call close() in case another file is already open. + this->close(); + + if ( nChannels < 1 ) { + oStream_ << "FileWrite::open: then channels argument must be greater than zero!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + channels_ = nChannels; + fileType_ = type; + + if ( format != STK_SINT8 && format != STK_SINT16 && + format != STK_SINT24 && format != STK_SINT32 && + format != STK_FLOAT32 && format != STK_FLOAT64 ) { + oStream_ << "FileWrite::open: unknown data type (" << format << ") specified!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + dataType_ = format; + + bool result = false; + if ( fileType_ == FILE_RAW ) { + if ( channels_ != 1 ) { + oStream_ << "FileWrite::open: STK RAW files are, by definition, always monaural (channels = " << nChannels << " not supported)!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + result = setRawFile( fileName ); + } + else if ( fileType_ == FILE_WAV ) + result = setWavFile( fileName ); + else if ( fileType_ == FILE_SND ) + result = setSndFile( fileName ); + else if ( fileType_ == FILE_AIF ) + result = setAifFile( fileName ); + else if ( fileType_ == FILE_MAT ) + result = setMatFile( fileName ); + else { + oStream_ << "FileWrite::open: unknown file type (" << fileType_ << ") specified!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( result == false ) + handleError( StkError::FILE_ERROR ); + + frameCounter_ = 0; +} + +bool FileWrite :: setRawFile( std::string fileName ) +{ + if ( fileName.find( ".raw" ) == std::string::npos ) fileName += ".raw"; + fd_ = fopen( fileName.c_str(), "wb" ); + if ( !fd_ ) { + oStream_ << "FileWrite: could not create RAW file: " << fileName << '.'; + return false; + } + + if ( dataType_ != STK_SINT16 ) { + dataType_ = STK_SINT16; + oStream_ << "FileWrite: using 16-bit signed integer data format for file " << fileName << '.'; + handleError( StkError::WARNING ); + } + + byteswap_ = false; +#ifdef __LITTLE_ENDIAN__ + byteswap_ = true; +#endif + + oStream_ << "FileWrite: creating RAW file: " << fileName; + handleError( StkError::STATUS ); + return true; +} + +bool FileWrite :: setWavFile( std::string fileName ) +{ + if ( fileName.find( ".wav" ) == std::string::npos ) fileName += ".wav"; + fd_ = fopen( fileName.c_str(), "wb" ); + if ( !fd_ ) { + oStream_ << "FileWrite: could not create WAV file: " << fileName; + return false; + } + + struct WaveHeader hdr = { {'R','I','F','F'}, 44, {'W','A','V','E'}, {'f','m','t',' '}, 16, 1, 1, + (SINT32) Stk::sampleRate(), 0, 2, 16, 0, 0, 0, + {'\x01','\x00','\x00','\x00','\x00','\x00','\x10','\x00','\x80','\x00','\x00','\xAA','\x00','\x38','\x9B','\x71'}, + {'f','a','c','t'}, 4, 0 }; + hdr.nChannels = (SINT16) channels_; + if ( dataType_ == STK_SINT8 ) + hdr.bitsPerSample = 8; + else if ( dataType_ == STK_SINT16 ) + hdr.bitsPerSample = 16; + else if ( dataType_ == STK_SINT24 ) + hdr.bitsPerSample = 24; + else if ( dataType_ == STK_SINT32 ) + hdr.bitsPerSample = 32; + else if ( dataType_ == STK_FLOAT32 ) { + hdr.formatCode = 3; + hdr.bitsPerSample = 32; + } + else if ( dataType_ == STK_FLOAT64 ) { + hdr.formatCode = 3; + hdr.bitsPerSample = 64; + } + hdr.bytesPerSample = (SINT16) (channels_ * hdr.bitsPerSample / 8); + hdr.bytesPerSecond = (SINT32) (hdr.sampleRate * hdr.bytesPerSample); + + unsigned int bytesToWrite = 36; + if ( channels_ > 2 || hdr.bitsPerSample > 16 ) { // use extensible format + bytesToWrite = 72; + hdr.chunkSize += 24; + hdr.formatCode = 0xFFFE; + hdr.cbSize = 22; + hdr.validBits = hdr.bitsPerSample; + SINT16 *subFormat = (SINT16 *)&hdr.subformat[0]; + if ( dataType_ == STK_FLOAT32 || dataType_ == STK_FLOAT64 ) + *subFormat = 3; + else *subFormat = 1; + } + + byteswap_ = false; +#ifndef __LITTLE_ENDIAN__ + byteswap_ = true; + swap32((unsigned char *)&hdr.chunkSize); + swap16((unsigned char *)&hdr.formatCode); + swap16((unsigned char *)&hdr.nChannels); + swap32((unsigned char *)&hdr.sampleRate); + swap32((unsigned char *)&hdr.bytesPerSecond); + swap16((unsigned char *)&hdr.bytesPerSample); + swap16((unsigned char *)&hdr.bitsPerSample); + swap16((unsigned char *)&hdr.cbSize); + swap16((unsigned char *)&hdr.validBits); + swap16((unsigned char *)&hdr.subformat[0]); + swap32((unsigned char *)&hdr.factSize); +#endif + + char data[4] = {'d','a','t','a'}; + SINT32 dataSize = 0; + if ( fwrite(&hdr, 1, bytesToWrite, fd_) != bytesToWrite ) goto error; + if ( fwrite(&data, 4, 1, fd_) != 1 ) goto error; + if ( fwrite(&dataSize, 4, 1, fd_) != 1 ) goto error; + + oStream_ << "FileWrite: creating WAV file: " << fileName; + handleError( StkError::STATUS ); + return true; + + error: + oStream_ << "FileWrite: could not write WAV header for file: " << fileName; + return false; +} + +void FileWrite :: closeWavFile( void ) +{ + int bytesPerSample = 1; + if ( dataType_ == STK_SINT16 ) + bytesPerSample = 2; + else if ( dataType_ == STK_SINT24 ) + bytesPerSample = 3; + else if ( dataType_ == STK_SINT32 || dataType_ == STK_FLOAT32 ) + bytesPerSample = 4; + else if ( dataType_ == STK_FLOAT64 ) + bytesPerSample = 8; + + bool useExtensible = false; + int dataLocation = 40; + if ( bytesPerSample > 2 || channels_ > 2 ) { + useExtensible = true; + dataLocation = 76; + } + + SINT32 bytes = (SINT32) (frameCounter_ * channels_ * bytesPerSample); + if ( bytes % 2 ) { // pad extra byte if odd + signed char sample = 0; + fwrite( &sample, 1, 1, fd_ ); + } +#ifndef __LITTLE_ENDIAN__ + swap32((unsigned char *)&bytes); +#endif + fseek( fd_, dataLocation, SEEK_SET ); // jump to data length + fwrite( &bytes, 4, 1, fd_ ); + + bytes = (SINT32) (frameCounter_ * channels_ * bytesPerSample + 44); + if ( useExtensible ) bytes += 36; +#ifndef __LITTLE_ENDIAN__ + swap32((unsigned char *)&bytes); +#endif + fseek( fd_, 4, SEEK_SET ); // jump to file size + fwrite( &bytes, 4, 1, fd_ ); + + if ( useExtensible ) { // fill in the "fact" chunk frames value + bytes = (SINT32) frameCounter_; +#ifndef __LITTLE_ENDIAN__ + swap32((unsigned char *)&bytes); +#endif + fseek( fd_, 68, SEEK_SET ); + fwrite( &bytes, 4, 1, fd_ ); + } + + fclose( fd_ ); +} + +bool FileWrite :: setSndFile( std::string fileName ) +{ + std::string name( fileName ); + if ( fileName.find( ".snd" ) == std::string::npos ) fileName += ".snd"; + fd_ = fopen( fileName.c_str(), "wb" ); + if ( !fd_ ) { + oStream_ << "FileWrite: could not create SND file: " << fileName; + return false; + } + + struct SndHeader hdr = {".sn", 40, 0, 3, (SINT32) Stk::sampleRate(), 1, "Created by STK"}; + hdr.pref[3] = 'd'; + hdr.nChannels = channels_; + if ( dataType_ == STK_SINT8 ) + hdr.format = 2; + else if ( dataType_ == STK_SINT16 ) + hdr.format = 3; + else if ( dataType_ == STK_SINT24 ) + hdr.format = 4; + else if ( dataType_ == STK_SINT32 ) + hdr.format = 5; + else if ( dataType_ == STK_FLOAT32 ) + hdr.format = 6; + else if ( dataType_ == STK_FLOAT64 ) + hdr.format = 7; + + byteswap_ = false; +#ifdef __LITTLE_ENDIAN__ + byteswap_ = true; + swap32 ((unsigned char *)&hdr.headerBytes); + swap32 ((unsigned char *)&hdr.format); + swap32 ((unsigned char *)&hdr.sampleRate); + swap32 ((unsigned char *)&hdr.nChannels); +#endif + + if ( fwrite(&hdr, 4, 10, fd_) != 10 ) { + oStream_ << "FileWrite: Could not write SND header for file " << fileName << '.'; + return false; + } + + oStream_ << "FileWrite: creating SND file: " << fileName; + handleError( StkError::STATUS ); + return true; +} + +void FileWrite :: closeSndFile( void ) +{ + int bytesPerSample = 1; + if ( dataType_ == STK_SINT16 ) + bytesPerSample = 2; + else if ( dataType_ == STK_SINT24 ) + bytesPerSample = 3; + else if ( dataType_ == STK_SINT32 ) + bytesPerSample = 4; + else if ( dataType_ == STK_FLOAT32 ) + bytesPerSample = 4; + else if ( dataType_ == STK_FLOAT64 ) + bytesPerSample = 8; + + SINT32 bytes = (SINT32) (frameCounter_ * bytesPerSample * channels_); +#ifdef __LITTLE_ENDIAN__ + swap32 ((unsigned char *)&bytes); +#endif + fseek(fd_, 8, SEEK_SET); // jump to data size + fwrite(&bytes, 4, 1, fd_); + fclose(fd_); +} + +bool FileWrite :: setAifFile( std::string fileName ) +{ + std::string name( fileName ); + if ( fileName.find( ".aif" ) == std::string::npos ) fileName += ".aif"; + fd_ = fopen( fileName.c_str(), "wb" ); + if ( !fd_ ) { + oStream_ << "FileWrite: could not create AIF file: " << fileName; + return false; + } + + // Common parts of AIFF/AIFC header. + struct AifHeader hdr = {{'F','O','R','M'}, 46, {'A','I','F','F'}, {'C','O','M','M'}, 18, 0, 0, 16, "0"}; + struct AifSsnd ssnd = {{'S','S','N','D'}, 8, 0, 0}; + hdr.nChannels = channels_; + if ( dataType_ == STK_SINT8 ) + hdr.sampleSize = 8; + else if ( dataType_ == STK_SINT16 ) + hdr.sampleSize = 16; + else if ( dataType_ == STK_SINT24 ) + hdr.sampleSize = 24; + else if ( dataType_ == STK_SINT32 ) + hdr.sampleSize = 32; + else if ( dataType_ == STK_FLOAT32 ) { + hdr.aiff[3] = 'C'; + hdr.sampleSize = 32; + hdr.commSize = 24; + } + else if ( dataType_ == STK_FLOAT64 ) { + hdr.aiff[3] = 'C'; + hdr.sampleSize = 64; + hdr.commSize = 24; + } + + // For AIFF files, the sample rate is stored in a 10-byte, + // IEEE Standard 754 floating point number, so we need to + // convert to that. + SINT16 i; + unsigned long exp; + unsigned long rate = (unsigned long) Stk::sampleRate(); + memset( hdr.srate, 0, 10 ); + exp = rate; + for ( i=0; i<32; i++ ) { + exp >>= 1; + if ( !exp ) break; + } + i += 16383; +#ifdef __LITTLE_ENDIAN__ + swap16((unsigned char *)&i); +#endif + memcpy( hdr.srate, &i, sizeof(SINT16) ); + + for ( i=32; i; i-- ) { + if ( rate & 0x80000000 ) break; + rate <<= 1; + } + +#ifdef __LITTLE_ENDIAN__ + swap32((unsigned char *)&rate); +#endif + memcpy( hdr.srate + 2, &rate, sizeof(rate) ); + + byteswap_ = false; +#ifdef __LITTLE_ENDIAN__ + byteswap_ = true; + swap32((unsigned char *)&hdr.formSize); + swap32((unsigned char *)&hdr.commSize); + swap16((unsigned char *)&hdr.nChannels); + swap16((unsigned char *)&hdr.sampleSize); + swap32((unsigned char *)&ssnd.ssndSize); + swap32((unsigned char *)&ssnd.offset); + swap32((unsigned char *)&ssnd.blockSize); +#endif + + // The structure boundaries don't allow a single write of 54 bytes. + if ( fwrite(&hdr, 4, 5, fd_) != 5 ) goto error; + if ( fwrite(&hdr.nChannels, 2, 1, fd_) != 1 ) goto error; + if ( fwrite(&hdr.sampleFrames, 4, 1, fd_) != 1 ) goto error; + if ( fwrite(&hdr.sampleSize, 2, 1, fd_) != 1 ) goto error; + if ( fwrite(&hdr.srate, 10, 1, fd_) != 1 ) goto error; + + if ( dataType_ == STK_FLOAT32 ) { + char type[4] = {'f','l','3','2'}; + char zeroes[2] = { 0, 0 }; + if ( fwrite(&type, 4, 1, fd_) != 1 ) goto error; + if ( fwrite(&zeroes, 2, 1, fd_) != 1 ) goto error; + } + else if ( dataType_ == STK_FLOAT64 ) { + char type[4] = {'f','l','6','4'}; + char zeroes[2] = { 0, 0 }; + if ( fwrite(&type, 4, 1, fd_) != 1 ) goto error; + if ( fwrite(&zeroes, 2, 1, fd_) != 1 ) goto error; + } + + if ( fwrite(&ssnd, 4, 4, fd_) != 4 ) goto error; + + oStream_ << "FileWrite: creating AIF file: " << fileName; + handleError( StkError::STATUS ); + return true; + + error: + oStream_ << "FileWrite: could not write AIF header for file: " << fileName; + return false; +} + +void FileWrite :: closeAifFile( void ) +{ + unsigned long frames = (unsigned long) frameCounter_; +#ifdef __LITTLE_ENDIAN__ + swap32((unsigned char *)&frames); +#endif + fseek(fd_, 22, SEEK_SET); // jump to "COMM" sampleFrames + fwrite(&frames, 4, 1, fd_); + + int bytesPerSample = 1; + if ( dataType_ == STK_SINT16 ) + bytesPerSample = 2; + if ( dataType_ == STK_SINT24 ) + bytesPerSample = 3; + else if ( dataType_ == STK_SINT32 || dataType_ == STK_FLOAT32 ) + bytesPerSample = 4; + else if ( dataType_ == STK_FLOAT64 ) + bytesPerSample = 8; + + unsigned long bytes = frameCounter_ * bytesPerSample * channels_ + 46; + if ( dataType_ == STK_FLOAT32 || dataType_ == STK_FLOAT64 ) bytes += 6; +#ifdef __LITTLE_ENDIAN__ + swap32((unsigned char *)&bytes); +#endif + fseek(fd_, 4, SEEK_SET); // jump to file size + fwrite(&bytes, 4, 1, fd_); + + bytes = frameCounter_ * bytesPerSample * channels_ + 8; + if ( dataType_ == STK_FLOAT32 || dataType_ == STK_FLOAT64 ) bytes += 6; +#ifdef __LITTLE_ENDIAN__ + swap32((unsigned char *)&bytes); +#endif + if ( dataType_ == STK_FLOAT32 || dataType_ == STK_FLOAT64 ) + fseek(fd_, 48, SEEK_SET); // jump to "SSND" chunk size + else + fseek(fd_, 42, SEEK_SET); // jump to "SSND" chunk size + fwrite(&bytes, 4, 1, fd_); + + fclose( fd_ ); +} + +bool FileWrite :: setMatFile( std::string fileName ) +{ + if ( fileName.find( ".mat" ) == std::string::npos ) fileName += ".mat"; + fd_ = fopen( fileName.c_str(), "w+b" ); + if ( !fd_ ) { + oStream_ << "FileWrite: could not create MAT file: " << fileName; + return false; + } + + if ( dataType_ != STK_FLOAT64 ) { + dataType_ = STK_FLOAT64; + oStream_ << "FileWrite: using 64-bit floating-point data format for file " << fileName << '.'; + handleError( StkError::DEBUG_PRINT ); + } + + struct MatHeader hdr; + strcpy( hdr.heading,"MATLAB 5.0 MAT-file, Generated using the Synthesis ToolKit in C++ (STK). By Perry R. Cook and Gary P. Scavone." ); + for ( size_t i =strlen(hdr.heading); i<124; i++ ) hdr.heading[i] = ' '; + + // Header Flag Fields + hdr.hff[0] = (SINT16) 0x0100; // Version field + hdr.hff[1] = (SINT16) 'M'; // Endian indicator field ("MI") + hdr.hff[1] <<= 8; + hdr.hff[1] += 'I'; + + // Write sample rate in array data element + hdr.fs[0] = (SINT32) 14; // Matlab array data type value + hdr.fs[1] = (SINT32) 56; // Size of data element to follow (in bytes) + + // Numeric Array Subelements (4): + // 1. Array Flags + hdr.fs[2] = (SINT32) 6; // Matlab 32-bit unsigned integer data type value + hdr.fs[3] = (SINT32) 8; // 8 bytes of data to follow + hdr.fs[4] = (SINT32) 6; // Double-precision array, no array flags set + hdr.fs[5] = (SINT32) 0; // 4 bytes undefined + // 2. Array Dimensions + hdr.fs[6] = (SINT32) 5; // Matlab 32-bit signed integer data type value + hdr.fs[7] = (SINT32) 8; // 8 bytes of data to follow (2D array) + hdr.fs[8] = (SINT32) 1; // 1 row + hdr.fs[9] = (SINT32) 1; // 1 column + // 3. Array Name (small data format element) + hdr.fs[10] = 0x00020001; + hdr.fs[11] = 's' << 8; + hdr.fs[11] += 'f'; + // 4. Real Part + hdr.fs[12] = 9; // Matlab IEEE 754 double data type + hdr.fs[13] = 8; // 8 bytes of data to follow + FLOAT64 *sampleRate = (FLOAT64 *)&hdr.fs[14]; + *sampleRate = (FLOAT64) Stk::sampleRate(); + + // Write audio samples in array data element + hdr.adf[0] = (SINT32) 14; // Matlab array data type value + hdr.adf[1] = (SINT32) 0; // Size of file after this point to end (in bytes) + + // Numeric Array Subelements (4): + // 1. Array Flags + hdr.adf[2] = (SINT32) 6; // Matlab 32-bit unsigned integer data type value + hdr.adf[3] = (SINT32) 8; // 8 bytes of data to follow + hdr.adf[4] = (SINT32) 6; // Double-precision array, no array flags set + hdr.adf[5] = (SINT32) 0; // 4 bytes undefined + // 2. Array Dimensions + hdr.adf[6] = (SINT32) 5; // Matlab 32-bit signed integer data type value + hdr.adf[7] = (SINT32) 8; // 8 bytes of data to follow (2D array) + hdr.adf[8] = (SINT32) channels_; // This is the number of rows + hdr.adf[9] = (SINT32) 0; // This is the number of columns + + // 3. Array Name We'll use fileName for the matlab array name (as + // well as the file name), though we might need to strip off a + // leading directory path. If fileName is 4 characters or less, we + // have to use a small data format element for the array name data + // element. Otherwise, the array name must be formatted in 8-byte + // increments (up to 31 characters + NULL). + std::string name = fileName; + size_t found; + found = name.find_last_of("/\\"); + name = name.substr(found+1); + SINT32 namelength = (SINT32) name.size() - 4; // strip off the ".mat" extension + if ( namelength > 31 ) namelength = 31; // Truncate name to 31 characters. + if ( namelength > 4 ) { + hdr.adf[10] = (SINT32) 1; // Matlab 8-bit signed integer data type value + } + else { // Compressed data element format + hdr.adf[10] = (namelength << 16) + 1; + } + + SINT32 headsize = 40; // Number of bytes in audio data element so far. + + // Write the fixed portion of the header + if ( fwrite(&hdr, 236, 1, fd_) != 1 ) goto error; + + // Write MATLAB array name + SINT32 tmp; + if ( namelength > 4 ) { + if ( fwrite(&namelength, 4, 1, fd_) != 1) goto error; + if ( fwrite(name.c_str(), namelength, 1, fd_) != 1 ) goto error; + tmp = (SINT32) ceil((float)namelength / 8); + if ( fseek(fd_, tmp*8-namelength, SEEK_CUR) == -1 ) goto error; + headsize += tmp * 8; + } + else { // Compressed data element format + if ( fwrite(name.c_str(), namelength, 1, fd_) != 1 ) goto error; + tmp = 4 - namelength; + if ( fseek(fd_, tmp, SEEK_CUR) == -1 ) goto error; + } + + // Finish writing known header information + //4. Real Part + tmp = 9; // Matlab IEEE 754 double data type + if ( fwrite(&tmp, 4, 1, fd_) != 1 ) goto error; + tmp = 0; // Size of real part subelement in bytes (8 per sample) + if ( fwrite(&tmp, 4, 1, fd_) != 1 ) goto error; + headsize += 8; // Total number of bytes in data element so far + + if ( fseek(fd_, 196, SEEK_SET) == -1 ) goto error; + if ( fwrite(&headsize, 4, 1, fd_) != 1 ) goto error; // Write header size ... will update at end + if ( fseek(fd_, 0, SEEK_END) == -1 ) goto error; + + byteswap_ = false; + oStream_ << "FileWrite: creating MAT-file: " << fileName; + handleError( StkError::STATUS ); + + return true; + + error: + oStream_ << "FileWrite: could not write MAT-file header for file " << fileName << '.'; + return false; +} + +void FileWrite :: closeMatFile( void ) +{ + fseek(fd_, 228, SEEK_SET); // jump to number of columns + fwrite(&frameCounter_, 4, 1, fd_); + + SINT32 headsize, temp; + fseek(fd_, 196, SEEK_SET); // jump to header size + if (fread(&headsize, 4, 1, fd_) != 1) { + oStream_ << "FileWrite: could not read MAT-file header size."; + handleError( StkError::WARNING ); + goto close_file; + } + + temp = headsize; + headsize += (SINT32) (frameCounter_ * 8 * channels_); + fseek(fd_, 196, SEEK_SET); + // Write file size (minus some header info) + fwrite(&headsize, 4, 1, fd_); + + fseek(fd_, temp+196, SEEK_SET); // jump to data size (in bytes) + temp = (SINT32) (frameCounter_ * 8 * channels_); + fwrite(&temp, 4, 1, fd_); + +close_file: + fclose(fd_); +} + +void FileWrite :: write( StkFrames& buffer ) +{ + if ( fd_ == 0 ) { + oStream_ << "FileWrite::write(): a file has not yet been opened!"; + handleError( StkError::WARNING ); + return; + } + + if ( buffer.channels() != channels_ ) { + oStream_ << "FileWrite::write(): number of channels in the StkFrames argument does not match that specified to open() function!"; + handleError( StkError::FUNCTION_ARGUMENT ); + return; + } + + unsigned long nSamples = buffer.size(); + if ( dataType_ == STK_SINT16 ) { + SINT16 sample; + for ( unsigned long k=0; k + +namespace stk { + +FileWvIn :: FileWvIn( unsigned long chunkThreshold, unsigned long chunkSize ) + : finished_(true), interpolate_(false), time_(0.0), rate_(0.0), + chunkThreshold_(chunkThreshold), chunkSize_(chunkSize) +{ + Stk::addSampleRateAlert( this ); +} + +FileWvIn :: FileWvIn( std::string fileName, bool raw, bool doNormalize, + unsigned long chunkThreshold, unsigned long chunkSize, + bool doInt2FloatScaling ) + : finished_(true), interpolate_(false), time_(0.0), rate_(0.0), + chunkThreshold_(chunkThreshold), chunkSize_(chunkSize) +{ + openFile( fileName, raw, doNormalize, doInt2FloatScaling ); + Stk::addSampleRateAlert( this ); +} + +FileWvIn :: ~FileWvIn() +{ + this->closeFile(); + Stk::removeSampleRateAlert( this ); +} + +void FileWvIn :: sampleRateChanged( StkFloat newRate, StkFloat oldRate ) +{ + if ( !ignoreSampleRateChange_ ) + this->setRate( oldRate * rate_ / newRate ); +} + +void FileWvIn :: closeFile( void ) +{ + if ( file_.isOpen() ) file_.close(); + finished_ = true; + lastFrame_.resize( 0, 0 ); +} + +void FileWvIn :: openFile( std::string fileName, bool raw, bool doNormalize, bool doInt2FloatScaling ) +{ + // Call close() in case another file is already open. + this->closeFile(); + + // Attempt to open the file ... an error might be thrown here. + file_.open( fileName, raw ); + + // Determine whether chunking or not. + if ( file_.fileSize() > chunkThreshold_ ) { + chunking_ = true; + chunkPointer_ = 0; + data_.resize( chunkSize_, file_.channels() ); + } + else { + chunking_ = false; + data_.resize( (size_t) file_.fileSize(), file_.channels() ); + } + + if ( doInt2FloatScaling ) + int2floatscaling_ = true; + else + int2floatscaling_ = false; + + // Load all or part of the data. + file_.read( data_, 0, int2floatscaling_ ); + + // Resize our lastFrame container. + lastFrame_.resize( 1, file_.channels() ); + + // Close the file unless chunking + fileSize_ = file_.fileSize(); + if ( !chunking_ ) file_.close(); + + // Set default rate based on file sampling rate. + this->setRate( data_.dataRate() / Stk::sampleRate() ); + + if ( doNormalize & !chunking_ ) this->normalize(); + + this->reset(); +} + +void FileWvIn :: reset(void) +{ + time_ = (StkFloat) 0.0; + for ( unsigned int i=0; inormalize( 1.0 ); +} + +// Normalize all channels equally by the greatest magnitude in all of the data. +void FileWvIn :: normalize( StkFloat peak ) +{ + // When chunking, the "normalization" scaling is performed by FileRead. + if ( chunking_ ) return; + + size_t i; + StkFloat max = 0.0; + + for ( i=0; i max ) + max = (StkFloat) fabs((double) data_[i]); + } + + if ( max > 0.0 ) { + max = 1.0 / max; + max *= peak; + for ( i=0; i fileSize_ - 1.0 ) { + time_ = fileSize_ - 1.0; + for ( unsigned int i=0; i= data_.channels() ) { + oStream_ << "FileWvIn::tick(): channel argument and soundfile data are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + if ( finished_ ) return 0.0; + + if ( time_ < 0.0 || time_ > (StkFloat) ( fileSize_ - 1.0 ) ) { + for ( unsigned int i=0; i (StkFloat) ( chunkPointer_ + chunkSize_ - 1 ) ) ) { + + while ( time_ < (StkFloat) chunkPointer_ ) { // negative rate + chunkPointer_ -= chunkSize_ - 1; // overlap chunks by one frame + if ( chunkPointer_ < 0 ) chunkPointer_ = 0; + } + while ( time_ > (StkFloat) ( chunkPointer_ + chunkSize_ - 1 ) ) { // positive rate + chunkPointer_ += chunkSize_ - 1; // overlap chunks by one frame + if ( chunkPointer_ + chunkSize_ > fileSize_ ) // at end of file + chunkPointer_ = fileSize_ - chunkSize_; + } + + // Load more data. + file_.read( data_, chunkPointer_, int2floatscaling_ ); + } + + // Adjust index for the current buffer. + tyme -= chunkPointer_; + } + + if ( interpolate_ ) { + for ( unsigned int i=0; i frames.channels() - nChannels ) { + oStream_ << "FileWvIn::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int j, hop = frames.channels() - nChannels; + if ( nChannels == 1 ) { + for ( unsigned int i=0; iopenFile( fileName, nChannels, type, format ); +} + +FileWvOut :: ~FileWvOut() +{ + this->closeFile(); +} + +void FileWvOut :: closeFile( void ) +{ + if ( file_.isOpen() ) { + + // Output any remaining samples in the buffer before closing. + if ( bufferIndex_ > 0 ) { + data_.resize( bufferIndex_, data_.channels() ); + file_.write( data_ ); + } + + file_.close(); + frameCounter_ = 0; + } +} + +void FileWvOut :: openFile( std::string fileName, + unsigned int nChannels, + FileWrite::FILE_TYPE type, + Stk::StkFormat format ) +{ + closeFile(); + + if ( nChannels < 1 ) { + oStream_ << "FileWvOut::openFile: the channels argument must be greater than zero!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + // An StkError can be thrown by the FileWrite class here. + file_.open( fileName, nChannels, type, format ); + + // Allocate new memory if necessary. + data_.resize( bufferFrames_, nChannels ); + + bufferIndex_ = 0; + iData_ = 0; +} + +void FileWvOut :: incrementFrame( void ) +{ + frameCounter_++; + bufferIndex_++; + + if ( bufferIndex_ == bufferFrames_ ) { + file_.write( data_ ); + bufferIndex_ = 0; + iData_ = 0; + } +} + +void FileWvOut :: tick( const StkFloat sample ) +{ +#if defined(_STK_DEBUG_) + if ( !file_.isOpen() ) { + oStream_ << "FileWvOut::tick(): no file open!"; + handleError( StkError::WARNING ); + return; + } +#endif + + unsigned int nChannels = data_.channels(); + StkFloat input = sample; + clipTest( input ); + for ( unsigned int j=0; jincrementFrame(); +} + +void FileWvOut :: tick( const StkFrames& frames ) +{ +#if defined(_STK_DEBUG_) + if ( !file_.isOpen() ) { + oStream_ << "FileWvOut::tick(): no file open!"; + handleError( StkError::WARNING ); + return; + } + + if ( data_.channels() != frames.channels() ) { + oStream_ << "FileWvOut::tick(): incompatible channel value in StkFrames argument!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + unsigned int iFrames = 0; + unsigned int j, nChannels = data_.channels(); + for ( unsigned int i=0; iincrementFrame(); + } +} + +} // stk namespace diff --git a/src/stk/src/Fir.cpp b/src/stk/src/Fir.cpp new file mode 100644 index 00000000..42c4b7fd --- /dev/null +++ b/src/stk/src/Fir.cpp @@ -0,0 +1,74 @@ +/***************************************************/ +/*! \class Fir + \brief STK general finite impulse response filter class. + + This class provides a generic digital filter structure that can be + used to implement FIR filters. For filters with feedback terms, + the Iir class should be used. + + In particular, this class implements the standard difference + equation: + + y[n] = b[0]*x[n] + ... + b[nb]*x[n-nb] + + The \e gain parameter is applied at the filter input and does not + affect the coefficient values. The default gain value is 1.0. + This structure results in one extra multiply per computed sample, + but allows easy control of the overall filter gain. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Fir.h" +#include + +namespace stk { + +Fir :: Fir() +{ + // The default constructor should setup for pass-through. + b_.push_back( 1.0 ); + + inputs_.resize( 1, 1, 0.0 ); +} + +Fir :: Fir( std::vector &coefficients ) +{ + // Check the arguments. + if ( coefficients.size() == 0 ) { + oStream_ << "Fir: coefficient vector must have size > 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + gain_ = 1.0; + b_ = coefficients; + + inputs_.resize( b_.size(), 1, 0.0 ); + this->clear(); +} + +Fir :: ~Fir() +{ +} + +void Fir :: setCoefficients( std::vector &coefficients, bool clearState ) +{ + // Check the argument. + if ( coefficients.size() == 0 ) { + oStream_ << "Fir::setCoefficients: coefficient vector must have size > 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( b_.size() != coefficients.size() ) { + b_ = coefficients; + inputs_.resize( b_.size(), 1, 0.0 ); + } + else { + for ( unsigned int i=0; iclear(); +} + +} // stk namespace diff --git a/src/stk/src/Flute.cpp b/src/stk/src/Flute.cpp new file mode 100644 index 00000000..887a4726 --- /dev/null +++ b/src/stk/src/Flute.cpp @@ -0,0 +1,163 @@ +/***************************************************/ +/*! \Class Flute + \brief STK flute physical model class. + + This class implements a simple flute + physical model, as discussed by Karjalainen, + Smith, Waryznyk, etc. The jet model uses + a polynomial, a la Cook. + + This is a digital waveguide model, making its + use possibly subject to patents held by Stanford + University, Yamaha, and others. + + Control Change Numbers: + - Jet Delay = 2 + - Noise Gain = 4 + - Vibrato Frequency = 11 + - Vibrato Gain = 1 + - Breath Pressure = 128 + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Flute.h" +#include "SKINImsg.h" + +namespace stk { + +Flute :: Flute( StkFloat lowestFrequency ) +{ + if ( lowestFrequency <= 0.0 ) { + oStream_ << "Flute::Flute: argument is less than or equal to zero!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + unsigned long nDelays = (unsigned long) ( Stk::sampleRate() / lowestFrequency ); + boreDelay_.setMaximumDelay( nDelays + 1 ); + + jetDelay_.setMaximumDelay( nDelays + 1 ); + jetDelay_.setDelay( 49.0 ); + + vibrato_.setFrequency( 5.925 ); + filter_.setPole( 0.7 - ( 0.1 * 22050.0 / Stk::sampleRate() ) ); + dcBlock_.setBlockZero(); + + adsr_.setAllTimes( 0.005, 0.01, 0.8, 0.010 ); + endReflection_ = 0.5; + jetReflection_ = 0.5; + noiseGain_ = 0.15; // Breath pressure random component + vibratoGain_ = 0.05; // Breath periodic vibrato component + jetRatio_ = 0.32; + + maxPressure_ = 0.0; + this->clear(); + this->setFrequency( 220.0 ); +} + +Flute :: ~Flute( void ) +{ +} + +void Flute :: clear( void ) +{ + jetDelay_.clear(); + boreDelay_.clear(); + filter_.clear(); + dcBlock_.clear(); +} + +void Flute :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "Flute::setFrequency: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + // We're overblowing here. + lastFrequency_ = frequency * 0.66666; + + // Account for filter delay and one sample "lastOut" delay + // (previously approximated as 2.0 samples). The tuning is still + // not perfect but I'm not sure why. Also, we are not accounting + // for the dc blocking filter delay. + StkFloat delay = Stk::sampleRate() / lastFrequency_ - filter_.phaseDelay( lastFrequency_ ) - 1.0; + + boreDelay_.setDelay( delay ); + jetDelay_.setDelay( delay * jetRatio_ ); +} + +void Flute :: setJetDelay( StkFloat aRatio ) +{ + jetRatio_ = aRatio; + jetDelay_.setDelay( boreDelay_.getDelay() * aRatio ); // Scaled by ratio. +} + +void Flute :: startBlowing( StkFloat amplitude, StkFloat rate ) +{ + if ( amplitude <= 0.0 || rate <= 0.0 ) { + oStream_ << "Flute::startBlowing: one or more arguments is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } + + adsr_.setAttackRate( rate ); + maxPressure_ = amplitude / (StkFloat) 0.8; + adsr_.keyOn(); +} + +void Flute :: stopBlowing( StkFloat rate ) +{ + if ( rate <= 0.0 ) { + oStream_ << "Flute::stopBlowing: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } + + adsr_.setReleaseRate( rate ); + adsr_.keyOff(); +} + +void Flute :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + this->setFrequency( frequency ); + this->startBlowing( 1.1 + (amplitude * 0.20), amplitude * 0.02 ); + outputGain_ = amplitude + 0.001; +} + +void Flute :: noteOff( StkFloat amplitude ) +{ + this->stopBlowing( amplitude * 0.02 ); +} + + +void Flute :: controlChange( int number, StkFloat value ) +{ +#if defined(_STK_DEBUG_) + if ( Stk::inRange( value, 0.0, 128.0 ) == false ) { + oStream_ << "Flute::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if (number == __SK_JetDelay_) // 2 + this->setJetDelay( (StkFloat) (0.08 + (0.48 * normalizedValue)) ); + else if (number == __SK_NoiseLevel_) // 4 + noiseGain_ = ( normalizedValue * 0.4); + else if (number == __SK_ModFrequency_) // 11 + vibrato_.setFrequency( normalizedValue * 12.0); + else if (number == __SK_ModWheel_) // 1 + vibratoGain_ = ( normalizedValue * 0.4 ); + else if (number == __SK_AfterTouch_Cont_) // 128 + adsr_.setTarget( normalizedValue ); +#if defined(_STK_DEBUG_) + else { + oStream_ << "Flute::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/FormSwep.cpp b/src/stk/src/FormSwep.cpp new file mode 100644 index 00000000..dd8abaae --- /dev/null +++ b/src/stk/src/FormSwep.cpp @@ -0,0 +1,136 @@ +/***************************************************/ +/*! \class FormSwep + \brief STK sweepable formant filter class. + + This class implements a formant (resonance) which can be "swept" + over time from one frequency setting to another. It provides + methods for controlling the sweep rate and target frequency. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "FormSwep.h" +#include + +namespace stk { + +FormSwep :: FormSwep( void ) +{ + frequency_ = 0.0; + radius_ = 0.0; + targetGain_ = 1.0; + targetFrequency_ = 0.0; + targetRadius_ = 0.0; + deltaGain_ = 0.0; + deltaFrequency_ = 0.0; + deltaRadius_ = 0.0; + sweepState_ = 0.0; + sweepRate_ = 0.002; + dirty_ = false; + + b_.resize( 3, 0.0 ); + a_.resize( 3, 0.0 ); + a_[0] = 1.0; + inputs_.resize( 3, 1, 0.0 ); + outputs_.resize( 3, 1, 0.0 ); + + Stk::addSampleRateAlert( this ); +} + +FormSwep :: ~FormSwep() +{ + Stk::removeSampleRateAlert( this ); +} + +void FormSwep :: sampleRateChanged( StkFloat newRate, StkFloat oldRate ) +{ + if ( !ignoreSampleRateChange_ ) { + oStream_ << "FormSwep::sampleRateChanged: you may need to recompute filter coefficients!"; + handleError( StkError::WARNING ); + } +} + +void FormSwep :: setResonance( StkFloat frequency, StkFloat radius ) +{ +#if defined(_STK_DEBUG_) + if ( frequency < 0.0 || frequency > 0.5 * Stk::sampleRate() ) { + oStream_ << "FormSwep::setResonance: frequency argument (" << frequency << ") is out of range!"; + handleError( StkError::WARNING ); return; + } + if ( radius < 0.0 || radius >= 1.0 ) { + oStream_ << "FormSwep::setResonance: radius argument (" << radius << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + radius_ = radius; + frequency_ = frequency; + + a_[2] = radius * radius; + a_[1] = -2.0 * radius * cos( TWO_PI * frequency / Stk::sampleRate() ); + + // Use zeros at +- 1 and normalize the filter peak gain. + b_[0] = 0.5 - 0.5 * a_[2]; + b_[1] = 0.0; + b_[2] = -b_[0]; +} + +void FormSwep :: setStates( StkFloat frequency, StkFloat radius, StkFloat gain ) +{ + dirty_ = false; + + if ( frequency_ != frequency || radius_ != radius ) + this->setResonance( frequency, radius ); + + gain_ = gain; + targetFrequency_ = frequency; + targetRadius_ = radius; + targetGain_ = gain; +} + +void FormSwep :: setTargets( StkFloat frequency, StkFloat radius, StkFloat gain ) +{ + if ( frequency < 0.0 || frequency > 0.5 * Stk::sampleRate() ) { + oStream_ << "FormSwep::setTargets: frequency argument (" << frequency << ") is out of range!"; + handleError( StkError::WARNING ); return; + } + if ( radius < 0.0 || radius >= 1.0 ) { + oStream_ << "FormSwep::setTargets: radius argument (" << radius << ") is out of range!"; + handleError( StkError::WARNING ); return; + } + + dirty_ = true; + startFrequency_ = frequency_; + startRadius_ = radius_; + startGain_ = gain_; + targetFrequency_ = frequency; + targetRadius_ = radius; + targetGain_ = gain; + deltaFrequency_ = frequency - frequency_; + deltaRadius_ = radius - radius_; + deltaGain_ = gain - gain_; + sweepState_ = 0.0; +} + +void FormSwep :: setSweepRate( StkFloat rate ) +{ + if ( rate < 0.0 || rate > 1.0 ) { + oStream_ << "FormSwep::setSweepRate: argument (" << rate << ") is out of range!"; + handleError( StkError::WARNING ); return; + } + + sweepRate_ = rate; +} + +void FormSwep :: setSweepTime( StkFloat time ) +{ + if ( time <= 0.0 ) { + oStream_ << "FormSwep::setSweepTime: argument (" << time << ") must be > 0.0!"; + handleError( StkError::WARNING ); return; + } + + this->setSweepRate( 1.0 / ( time * Stk::sampleRate() ) ); +} + +} // stk namespace diff --git a/src/stk/src/FreeVerb.cpp b/src/stk/src/FreeVerb.cpp new file mode 100644 index 00000000..24617772 --- /dev/null +++ b/src/stk/src/FreeVerb.cpp @@ -0,0 +1,227 @@ +/***********************************************************************/ +/*! \class FreeVerb + \brief Jezar at Dreampoint's FreeVerb, implemented in STK. + + Freeverb is a free and open-source Schroeder reverberator + originally implemented in C++. The parameters of the reverberation + model are exceptionally well tuned. FreeVerb uses 8 + lowpass-feedback-comb-filters in parallel, followed by 4 Schroeder + allpass filters in series. The input signal can be either mono or + stereo, and the output signal is stereo. The delay lengths are + optimized for a sample rate of 44100 Hz. + + Ported to STK by Gregory Burlet, 2012. +*/ +/***********************************************************************/ + +#include "FreeVerb.h" +#include +#include + +using namespace stk; + +// Set static delay line lengths +const StkFloat FreeVerb::fixedGain = 0.015; +const StkFloat FreeVerb::scaleWet = 3; +const StkFloat FreeVerb::scaleDry = 2; +const StkFloat FreeVerb::scaleDamp = 0.4; +const StkFloat FreeVerb::scaleRoom = 0.28; +const StkFloat FreeVerb::offsetRoom = 0.7; +int FreeVerb::cDelayLengths[] = {1617, 1557, 1491, 1422, 1356, 1277, 1188, 1116}; +int FreeVerb::aDelayLengths[] = {225, 556, 441, 341}; + +FreeVerb::FreeVerb( void ) +{ + // Resize lastFrame_ for stereo output + lastFrame_.resize( 1, 2, 0.0 ); + + // Initialize parameters + Effect::setEffectMix( 0.75 ); // set initially to 3/4 wet 1/4 dry signal (different than original freeverb) + roomSizeMem_ = (0.75 * scaleRoom) + offsetRoom; // feedback attenuation in LBFC + dampMem_ = 0.25 * scaleDamp; // pole of lowpass filters in the LBFC + width_ = 1.0; + frozenMode_ = false; + update(); + + gain_ = fixedGain; // input gain before sending to filters + g_ = 0.5; // allpass coefficient, immutable in FreeVerb + + // Scale delay line lengths according to the current sampling rate + double fsScale = Stk::sampleRate() / 44100.0; + if ( fsScale != 1.0 ) { + // scale comb filter delay lines + for ( int i = 0; i < nCombs; i++ ) { + cDelayLengths[i] = (int) floor(fsScale * cDelayLengths[i]); + } + + // Scale allpass filter delay lines + for ( int i = 0; i < nAllpasses; i++ ) { + aDelayLengths[i] = (int) floor(fsScale * aDelayLengths[i]); + } + } + + // Initialize delay lines for the LBFC filters + for ( int i = 0; i < nCombs; i++ ) { + combDelayL_[i].setMaximumDelay( cDelayLengths[i] ); + combDelayL_[i].setDelay( cDelayLengths[i] ); + combDelayR_[i].setMaximumDelay( cDelayLengths[i] + stereoSpread ); + combDelayR_[i].setDelay( cDelayLengths[i] + stereoSpread ); + } + + // initialize delay lines for the allpass filters + for (int i = 0; i < nAllpasses; i++) { + allPassDelayL_[i].setMaximumDelay( aDelayLengths[i] ); + allPassDelayL_[i].setDelay( aDelayLengths[i] ); + allPassDelayR_[i].setMaximumDelay( aDelayLengths[i] + stereoSpread ); + allPassDelayR_[i].setDelay( aDelayLengths[i] + stereoSpread ); + } +} + +FreeVerb::~FreeVerb() +{ +} + +void FreeVerb::setEffectMix( StkFloat mix ) +{ + Effect::setEffectMix( mix ); + update(); +} + +void FreeVerb::setRoomSize( StkFloat roomSize ) +{ + roomSizeMem_ = (roomSize * scaleRoom) + offsetRoom; + update(); +} + +StkFloat FreeVerb::getRoomSize() +{ + return (roomSizeMem_ - offsetRoom) / scaleRoom; +} + +void FreeVerb::setDamping( StkFloat damping ) +{ + dampMem_ = damping * scaleDamp; + update(); +} + +StkFloat FreeVerb::getDamping() +{ + return dampMem_ / scaleDamp; +} + +void FreeVerb::setWidth( StkFloat width ) +{ + width_ = width; + update(); +} + +StkFloat FreeVerb::getWidth() +{ + return width_; +} + +void FreeVerb::setMode( bool isFrozen ) +{ + frozenMode_ = isFrozen; + update(); +} + +StkFloat FreeVerb::getMode() +{ + return frozenMode_; +} + +void FreeVerb::update() +{ + StkFloat wet = scaleWet * effectMix_; + dry_ = scaleDry * (1.0-effectMix_); + + // Use the L1 norm so the output gain will sum to one while still + // preserving the ratio of scalings in original FreeVerb + wet /= (wet + dry_); + dry_ /= (wet + dry_); + + wet1_ = wet * (width_/2.0 + 0.5); + wet2_ = wet * (1.0 - width_)/2.0; + + if ( frozenMode_ ) { + // put into freeze mode + roomSize_ = 1.0; + damp_ = 0.0; + gain_ = 0.0; + } + else { + roomSize_ = roomSizeMem_; + damp_ = dampMem_; + gain_ = fixedGain; + } + + for ( int i=0; i= frames.channels() - 1 ) { + oStream_ << "FreeVerb::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() - 1 ) { + oStream_ << "FreeVerb::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(); + unsigned int oHop = oFrames.channels(); + bool stereoInput = ( iFrames.channels() > iChannel+1 ) ? true : false; + for ( unsigned int i=0; i + +namespace stk { + +Granulate :: Granulate( void ) +{ + this->setGrainParameters(); // use default values + this->setRandomFactor(); + gStretch_ = 0; + stretchCounter_ = 0; + gain_ = 1.0; +} + +Granulate :: Granulate( unsigned int nVoices, std::string fileName, bool typeRaw ) +{ + this->setGrainParameters(); // use default values + this->setRandomFactor(); + gStretch_ = 0; + stretchCounter_ = 0; + this->openFile( fileName, typeRaw ); + this->setVoices( nVoices ); +} + +Granulate :: ~Granulate( void ) +{ +} + +void Granulate :: setStretch( unsigned int stretchFactor ) +{ + if ( stretchFactor <= 1 ) + gStretch_ = 0; + else if ( gStretch_ >= 1000 ) + gStretch_ = 1000; + else + gStretch_ = stretchFactor - 1; +} + +void Granulate :: setGrainParameters( unsigned int duration, unsigned int rampPercent, + int offset, unsigned int delay ) +{ + gDuration_ = duration; + if ( gDuration_ == 0 ) { + gDuration_ = 1; + oStream_ << "Granulate::setGrainParameters: duration argument cannot be zero ... setting to 1 millisecond."; + handleError( StkError::WARNING ); + } + + gRampPercent_ = rampPercent; + if ( gRampPercent_ > 100 ) { + gRampPercent_ = 100; + oStream_ << "Granulate::setGrainParameters: rampPercent argument cannot be greater than 100 ... setting to 100."; + handleError( StkError::WARNING ); + } + + gOffset_ = offset; + gDelay_ = delay; +} + +void Granulate :: setRandomFactor( StkFloat randomness ) +{ + if ( randomness < 0.0 ) gRandomFactor_ = 0.0; + else if ( randomness > 1.0 ) gRandomFactor_ = 0.97; + else gRandomFactor_ = 0.97 * randomness; +} + +void Granulate :: openFile( std::string fileName, bool typeRaw ) +{ + // Attempt to load the soundfile data. + FileRead file( fileName, typeRaw ); + data_.resize( file.fileSize(), file.channels() ); + file.read( data_ ); + lastFrame_.resize( 1, file.channels(), 0.0 ); + + this->reset(); + +#if defined(_STK_DEBUG_) + std::ostringstream message; + message << "Granulate::openFile: file = " << fileName << ", file frames = " << file.fileSize() << '.'; + handleError( message.str(), StkError::DEBUG_PRINT ); +#endif + +} + +void Granulate :: reset( void ) +{ + gPointer_ = 0; + + // Reset grain parameters. + size_t count; + size_t nVoices = (unsigned int)grains_.size(); + for ( unsigned int i=0; icalculateGrain( grains_[i] ); + break; + + case GRAIN_FADEIN: + // We're done ramping up the envelope + if ( grains_[i].sustainCount > 0 ) { + grains_[i].counter = grains_[i].sustainCount; + grains_[i].state = GRAIN_SUSTAIN; + break; + } + // else no sustain state (i.e. perfect triangle window) + + case GRAIN_SUSTAIN: + // We're done with flat part of envelope ... setup to ramp down + if ( grains_[i].decayCount > 0 ) { + grains_[i].counter = grains_[i].decayCount; + grains_[i].eRate = -grains_[i].eRate; + grains_[i].state = GRAIN_FADEOUT; + break; + } + // else no fade out state (gRampPercent = 0) + + case GRAIN_FADEOUT: + // We're done ramping down ... setup for wait between grains + if ( grains_[i].delayCount > 0 ) { + grains_[i].counter = grains_[i].delayCount; + grains_[i].state = GRAIN_STOPPED; + break; + } + // else no delay (gDelay = 0) + + this->calculateGrain( grains_[i] ); + } + } + + // Accumulate the grain outputs. + if ( grains_[i].state > 0 ) { + for ( j=0; j= data_.frames() ) + grains_[i].pointer = 0; + } + + // Decrement counter for all states. + grains_[i].counter--; + } + + // Increment our global file pointer at the stretch rate. + if ( stretchCounter_++ == gStretch_ ) { + gPointer_++; + if ( (unsigned long) gPointer_ >= data_.frames() ) gPointer_ = 0; + stretchCounter_ = 0; + } + + return lastFrame_[channel]; +} + +} // stk namespace diff --git a/src/stk/src/Guitar.cpp b/src/stk/src/Guitar.cpp new file mode 100644 index 00000000..22183483 --- /dev/null +++ b/src/stk/src/Guitar.cpp @@ -0,0 +1,244 @@ +/***************************************************/ +/*! \class Guitar + \brief STK guitar model class. + + This class implements a guitar model with an arbitrary number of + strings (specified during instantiation). Each string is + represented by an stk::Twang object. The model supports commuted + synthesis, as discussed by Smith and Karjalainen. It also includes + a basic body coupling model and supports feedback. + + This class does not attempt voice management. Rather, most + functions support a parameter to specify a particular string + number and string (voice) management is assumed to occur + externally. Note that this class does not inherit from + stk::Instrmnt because of API inconsistencies. + + This is a digital waveguide model, making its use possibly subject + to patents held by Stanford University, Yamaha, and others. + + Control Change Numbers: + - Bridge Coupling Gain = 2 + - Pluck Position = 4 + - Loop Gain = 11 + - Coupling Filter Pole = 1 + - Pick Filter Pole = 128 + + by Gary P. Scavone, 2012. +*/ +/***************************************************/ + +#include "Guitar.h" +#include "FileWvIn.h" +#include "Noise.h" +#include "SKINImsg.h" +#include + +namespace stk { + +#define BASE_COUPLING_GAIN 0.01 + +Guitar :: Guitar( unsigned int nStrings, std::string bodyfile ) +{ + strings_.resize( nStrings ); + stringState_.resize( nStrings, 0 ); + decayCounter_.resize( nStrings, 0 ); + filePointer_.resize( nStrings, 0 ); + pluckGains_.resize( nStrings, 0 ); + + setBodyFile( bodyfile ); + + couplingGain_ = BASE_COUPLING_GAIN; + couplingFilter_.setPole( 0.9 ); + pickFilter_.setPole( 0.95 ); + lastFrame_.resize(1, 1, 0.0); +} + +void Guitar :: clear( void ) +{ + for ( unsigned int i=0; i 1.0 ) { + oStream_ << "Guitar::setPluckPosition: position parameter out of range!"; + handleError( StkError::WARNING ); return; + } + + if ( string >= (int) strings_.size() ) { + oStream_ << "Guitar::setPluckPosition: string parameter is greater than number of strings!"; + handleError( StkError::WARNING ); return; + } + + if ( string < 0 ) // set all strings + for ( unsigned int i=0; i 1.0 ) { + oStream_ << "Guitar::setLoopGain: gain parameter out of range!"; + handleError( StkError::WARNING ); return; + } + + if ( string >= (int) strings_.size() ) { + oStream_ << "Guitar::setLoopGain: string parameter is greater than number of strings!"; + handleError( StkError::WARNING ); return; + } + + if ( string < 0 ) // set all strings + for ( unsigned int i=0; i= strings_.size() ) { + oStream_ << "Guitar::setFrequency: string parameter is greater than number of strings!"; + handleError( StkError::WARNING ); return; + } +#endif + + strings_[string].setFrequency( frequency ); +} + +void Guitar :: noteOn( StkFloat frequency, StkFloat amplitude, unsigned int string ) +{ +#if defined(_STK_DEBUG_) + if ( string >= strings_.size() ) { + oStream_ << "Guitar::noteOn: string parameter is greater than number of strings!"; + handleError( StkError::WARNING ); return; + } + + if ( Stk::inRange( amplitude, 0.0, 1.0 ) == false ) { + oStream_ << "Guitar::noteOn: amplitude parameter is outside range 0.0 - 1.0!"; + handleError( StkError::WARNING ); return; + } +#endif + + this->setFrequency( frequency, string ); + stringState_[string] = 2; + filePointer_[string] = 0; + strings_[string].setLoopGain( 0.995 ); + pluckGains_[string] = amplitude; +} + +void Guitar :: noteOff( StkFloat amplitude, unsigned int string ) +{ +#if defined(_STK_DEBUG_) + if ( string >= strings_.size() ) { + oStream_ << "Guitar::noteOff: string parameter is greater than number of strings!"; + handleError( StkError::WARNING ); return; + } + + if ( Stk::inRange( amplitude, 0.0, 1.0 ) == false ) { + oStream_ << "Guitar::noteOff: amplitude parameter is outside range 0.0 - 1.0!"; + handleError( StkError::WARNING ); return; + } +#endif + + strings_[string].setLoopGain( (1.0 - amplitude) * 0.9 ); + stringState_[string] = 1; +} + +void Guitar :: controlChange( int number, StkFloat value, int string ) +{ +#if defined(_STK_DEBUG_) + if ( Stk::inRange( value, 0.0, 128.0 ) == false ) { + oStream_ << "Guitar::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } + + if ( string > 0 && string >= (int) strings_.size() ) { + oStream_ << "Guitar::controlChange: string parameter is greater than number of strings!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if ( number == 2 ) + couplingGain_ = 1.5 * BASE_COUPLING_GAIN * normalizedValue; + else if ( number == __SK_PickPosition_ ) // 4 + this->setPluckPosition( normalizedValue, string ); + else if ( number == __SK_StringDamping_ ) // 11 + this->setLoopGain( 0.97 + (normalizedValue * 0.03), string ); + else if ( number == __SK_ModWheel_ ) // 1 + couplingFilter_.setPole( 0.98 * normalizedValue ); + else if (number == __SK_AfterTouch_Cont_) // 128 + pickFilter_.setPole( 0.95 * normalizedValue ); +#if defined(_STK_DEBUG_) + else { + oStream_ << "Guitar::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/HevyMetl.cpp b/src/stk/src/HevyMetl.cpp new file mode 100644 index 00000000..e295fbba --- /dev/null +++ b/src/stk/src/HevyMetl.cpp @@ -0,0 +1,75 @@ +/***************************************************/ +/*! \class HevyMetl + \brief STK heavy metal FM synthesis instrument. + + This class implements 3 cascade operators with + feedback modulation, also referred to as + algorithm 3 of the TX81Z. + + Algorithm 3 is : 4--\ + 3-->2-- + -->1-->Out + + Control Change Numbers: + - Total Modulator Index = 2 + - Modulator Crossfade = 4 + - LFO Speed = 11 + - LFO Depth = 1 + - ADSR 2 & 4 Target = 128 + + The basic Chowning/Stanford FM patent expired + in 1995, but there exist follow-on patents, + mostly assigned to Yamaha. If you are of the + type who should worry about this (making + money) worry away. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "HevyMetl.h" + +namespace stk { + +HevyMetl :: HevyMetl( void ) + : FM() +{ + // Concatenate the STK rawwave path to the rawwave files + for ( unsigned int i=0; i<3; i++ ) + waves_[i] = new FileLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + waves_[3] = new FileLoop( (Stk::rawwavePath() + "fwavblnk.raw").c_str(), true ); + + this->setRatio(0, 1.0 * 1.000); + this->setRatio(1, 4.0 * 0.999); + this->setRatio(2, 3.0 * 1.001); + this->setRatio(3, 0.5 * 1.002); + + gains_[0] = fmGains_[92]; + gains_[1] = fmGains_[76]; + gains_[2] = fmGains_[91]; + gains_[3] = fmGains_[68]; + + adsr_[0]->setAllTimes( 0.001, 0.001, 1.0, 0.01); + adsr_[1]->setAllTimes( 0.001, 0.010, 1.0, 0.50); + adsr_[2]->setAllTimes( 0.010, 0.005, 1.0, 0.20); + adsr_[3]->setAllTimes( 0.030, 0.010, 0.2, 0.20); + + twozero_.setGain( 2.0 ); + vibrato_.setFrequency( 5.5 ); + modDepth_ = 0.0; +} + +HevyMetl :: ~HevyMetl( void ) +{ +} + +void HevyMetl :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + gains_[0] = amplitude * fmGains_[92]; + gains_[1] = amplitude * fmGains_[76]; + gains_[2] = amplitude * fmGains_[91]; + gains_[3] = amplitude * fmGains_[68]; + this->setFrequency( frequency ); + this->keyOn(); +} + +} // stk namespace diff --git a/src/stk/src/Iir.cpp b/src/stk/src/Iir.cpp new file mode 100644 index 00000000..d4a69362 --- /dev/null +++ b/src/stk/src/Iir.cpp @@ -0,0 +1,125 @@ +/***************************************************/ +/*! \class Iir + \brief STK general infinite impulse response filter class. + + This class provides a generic digital filter structure that can be + used to implement IIR filters. For filters containing only + feedforward terms, the Fir class is slightly more efficient. + + In particular, this class implements the standard difference + equation: + + a[0]*y[n] = b[0]*x[n] + ... + b[nb]*x[n-nb] - + a[1]*y[n-1] - ... - a[na]*y[n-na] + + If a[0] is not equal to 1, the filter coeffcients are normalized + by a[0]. + + The \e gain parameter is applied at the filter input and does not + affect the coefficient values. The default gain value is 1.0. + This structure results in one extra multiply per computed sample, + but allows easy control of the overall filter gain. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Iir.h" + +namespace stk { + +Iir :: Iir() +{ + // The default constructor should setup for pass-through. + b_.push_back( 1.0 ); + a_.push_back( 1.0 ); + + inputs_.resize( 1, 1, 0.0 ); + outputs_.resize( 1, 1, 0.0 ); +} + +Iir :: Iir( std::vector &bCoefficients, std::vector &aCoefficients ) +{ + // Check the arguments. + if ( bCoefficients.size() == 0 || aCoefficients.size() == 0 ) { + oStream_ << "Iir: a and b coefficient vectors must both have size > 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( aCoefficients[0] == 0.0 ) { + oStream_ << "Iir: a[0] coefficient cannot == 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + gain_ = 1.0; + b_ = bCoefficients; + a_ = aCoefficients; + + inputs_.resize( b_.size(), 1, 0.0 ); + outputs_.resize( a_.size(), 1, 0.0 ); + this->clear(); +} + +Iir :: ~Iir() +{ +} + +void Iir :: setCoefficients( std::vector &bCoefficients, std::vector &aCoefficients, bool clearState ) +{ + this->setNumerator( bCoefficients, false ); + this->setDenominator( aCoefficients, false ); + + if ( clearState ) this->clear(); +} + +void Iir :: setNumerator( std::vector &bCoefficients, bool clearState ) +{ + // Check the argument. + if ( bCoefficients.size() == 0 ) { + oStream_ << "Iir::setNumerator: coefficient vector must have size > 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( b_.size() != bCoefficients.size() ) { + b_ = bCoefficients; + inputs_.resize( b_.size(), 1, 0.0 ); + } + else { + for ( unsigned int i=0; iclear(); +} + +void Iir :: setDenominator( std::vector &aCoefficients, bool clearState ) +{ + // Check the argument. + if ( aCoefficients.size() == 0 ) { + oStream_ << "Iir::setDenominator: coefficient vector must have size > 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( aCoefficients[0] == 0.0 ) { + oStream_ << "Iir::setDenominator: a[0] coefficient cannot == 0!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + if ( a_.size() != aCoefficients.size() ) { + a_ = aCoefficients; + outputs_.resize( a_.size(), 1, 0.0 ); + } + else { + for ( unsigned int i=0; iclear(); + + // Scale coefficients by a[0] if necessary + if ( a_[0] != 1.0 ) { + unsigned int i; + for ( i=0; i + +namespace stk { + +JCRev :: JCRev( StkFloat T60 ) +{ + if ( T60 <= 0.0 ) { + oStream_ << "JCRev::JCRev: argument (" << T60 << ") must be positive!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + lastFrame_.resize( 1, 2, 0.0 ); // resize lastFrame_ for stereo output + + // Delay lengths for 44100 Hz sample rate. + int lengths[9] = {1116, 1356, 1422, 1617, 225, 341, 441, 211, 179}; + double scaler = Stk::sampleRate() / 44100.0; + + int delay, i; + if ( scaler != 1.0 ) { + for ( i=0; i<9; i++ ) { + delay = (int) floor( scaler * lengths[i] ); + if ( (delay & 1) == 0) delay++; + while ( !this->isPrime(delay) ) delay += 2; + lengths[i] = delay; + } + } + + for ( i=0; i<3; i++ ) { + allpassDelays_[i].setMaximumDelay( lengths[i+4] ); + allpassDelays_[i].setDelay( lengths[i+4] ); + } + + for ( i=0; i<4; i++ ) { + combDelays_[i].setMaximumDelay( lengths[i] ); + combDelays_[i].setDelay( lengths[i] ); + combFilters_[i].setPole( 0.2 ); + } + + this->setT60( T60 ); + outLeftDelay_.setMaximumDelay( lengths[7] ); + outLeftDelay_.setDelay( lengths[7] ); + outRightDelay_.setMaximumDelay( lengths[8] ); + outRightDelay_.setDelay( lengths[8] ); + allpassCoefficient_ = 0.7; + effectMix_ = 0.3; + this->clear(); +} + +void JCRev :: clear() +{ + allpassDelays_[0].clear(); + allpassDelays_[1].clear(); + allpassDelays_[2].clear(); + combDelays_[0].clear(); + combDelays_[1].clear(); + combDelays_[2].clear(); + combDelays_[3].clear(); + outRightDelay_.clear(); + outLeftDelay_.clear(); + lastFrame_[0] = 0.0; + lastFrame_[1] = 0.0; +} + +void JCRev :: setT60( StkFloat T60 ) +{ + if ( T60 <= 0.0 ) { + oStream_ << "JCRev::setT60: argument (" << T60 << ") must be positive!"; + handleError( StkError::WARNING ); return; + } + + for ( int i=0; i<4; i++ ) + combCoefficient_[i] = pow(10.0, (-3.0 * combDelays_[i].getDelay() / (T60 * Stk::sampleRate()))); +} + +StkFrames& JCRev :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() - 1 ) { + oStream_ << "JCRev::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() - 1 ) { + oStream_ << "JCRev::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; isetFrequency( 220.0 ); + this->setPluckPosition( 0.4 ); +} + +Mandolin :: ~Mandolin( void ) +{ +} + +void Mandolin :: clear( void ) +{ + strings_[0].clear(); + strings_[1].clear(); +} + +void Mandolin :: setPluckPosition( StkFloat position ) +{ + if ( position < 0.0 || position > 1.0 ) { + oStream_ << "Mandolin::setPluckPosition: position parameter out of range!"; + handleError( StkError::WARNING ); return; + } + + strings_[0].setPluckPosition( position ); + strings_[1].setPluckPosition( position ); +} + +void Mandolin :: setDetune( StkFloat detune ) +{ + if ( detune <= 0.0 ) { + oStream_ << "Mandolin::setDeturn: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } + + detuning_ = detune; + strings_[1].setFrequency( frequency_ * detuning_ ); +} + +void Mandolin :: setBodySize( StkFloat size ) +{ + // Scale the commuted body response by its sample rate (22050). + StkFloat rate = size * 22050.0 / Stk::sampleRate(); + for ( int i=0; i<12; i++ ) + soundfile_[i].setRate( rate ); +} + +void Mandolin :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "Mandolin::setFrequency: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + frequency_ = frequency; + strings_[0].setFrequency( frequency_ ); + strings_[1].setFrequency( frequency_ * detuning_ ); +} + +void Mandolin :: pluck( StkFloat amplitude ) +{ + if ( amplitude < 0.0 || amplitude > 1.0 ) { + oStream_ << "Mandolin::pluck: amplitude parameter out of range!"; + handleError( StkError::WARNING ); return; + } + + soundfile_[mic_].reset(); + pluckAmplitude_ = amplitude; + + //strings_[0].setLoopGain( 0.97 + pluckAmplitude_ * 0.03 ); + //strings_[1].setLoopGain( 0.97 + pluckAmplitude_ * 0.03 ); +} + +void Mandolin :: pluck( StkFloat amplitude, StkFloat position ) +{ + this->setPluckPosition( position ); + this->pluck( amplitude ); +} + +void Mandolin :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + this->setFrequency( frequency ); + this->pluck( amplitude ); +} + +void Mandolin :: noteOff( StkFloat amplitude ) +{ + if ( amplitude < 0.0 || amplitude > 1.0 ) { + oStream_ << "Mandolin::noteOff: amplitude is out of range!"; + handleError( StkError::WARNING ); return; + } + + //strings_[0].setLoopGain( 0.97 + (1 - amplitude) * 0.03 ); + //strings_[1].setLoopGain( 0.97 + (1 - amplitude) * 0.03 ); +} + +void Mandolin :: controlChange( int number, StkFloat value ) +{ +#if defined(_STK_DEBUG_) + if ( Stk::inRange( value, 0.0, 128.0 ) == false ) { + oStream_ << "Mandolin::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if ( number == __SK_BodySize_ ) // 2 + this->setBodySize( normalizedValue * 2.0 ); + else if ( number == __SK_PickPosition_ ) // 4 + this->setPluckPosition( normalizedValue ); + else if ( number == __SK_StringDamping_ ) { // 11 + strings_[0].setLoopGain( 0.97 + (normalizedValue * 0.03) ); + strings_[1].setLoopGain( 0.97 + (normalizedValue * 0.03) ); + } + else if ( number == __SK_StringDetune_ ) // 1 + this->setDetune( 1.0 - (normalizedValue * 0.1) ); + else if ( number == __SK_AfterTouch_Cont_ ) // 128 + mic_ = (int) (normalizedValue * 11.0); +#if defined(_STK_DEBUG_) + else { + oStream_ << "Mandolin::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/Mesh2D.cpp b/src/stk/src/Mesh2D.cpp new file mode 100644 index 00000000..d126f998 --- /dev/null +++ b/src/stk/src/Mesh2D.cpp @@ -0,0 +1,367 @@ +/***************************************************/ +/*! \class Mesh2D + \brief Two-dimensional rectilinear waveguide mesh class. + + This class implements a rectilinear, + two-dimensional digital waveguide mesh + structure. For details, see Van Duyne and + Smith, "Physical Modeling with the 2-D Digital + Waveguide Mesh", Proceedings of the 1993 + International Computer Music Conference. + + This is a digital waveguide model, making its + use possibly subject to patents held by Stanford + University, Yamaha, and others. + + Control Change Numbers: + - X Dimension = 2 + - Y Dimension = 4 + - Mesh Decay = 11 + - X-Y Input Position = 1 + + by Julius Smith, 2000--2002. + Revised by Gary Scavone for STK, 2002. +*/ +/***************************************************/ + +#include "Mesh2D.h" +#include "SKINImsg.h" + +namespace stk { + +Mesh2D :: Mesh2D( unsigned short nX, unsigned short nY ) +{ + if ( nX == 0.0 || nY == 0.0 ) { + oStream_ << "Mesh2D::Mesh2D: one or more argument is equal to zero!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + this->setNX( nX ); + this->setNY( nY ); + + StkFloat pole = 0.05; + unsigned short i; + for ( i=0; iclearMesh(); + + counter_ = 0; + xInput_ = 0; + yInput_ = 0; +} + +Mesh2D :: ~Mesh2D( void ) +{ +} + +void Mesh2D :: clear( void ) +{ + this->clearMesh(); + + unsigned short i; + for ( i=0; i NXMAX ) { + oStream_ << "Mesh2D::setNX(" << lenX << "): Maximum length is " << NXMAX << '!'; + handleError( StkError::WARNING ); return; + } + + NX_ = lenX; +} + +void Mesh2D :: setNY( unsigned short lenY ) +{ + if ( lenY < 2 ) { + oStream_ << "Mesh2D::setNY(" << lenY << "): Minimum length is 2!"; + handleError( StkError::WARNING ); return; + } + else if ( lenY > NYMAX ) { + oStream_ << "Mesh2D::setNY(" << lenY << "): Maximum length is " << NXMAX << '!'; + handleError( StkError::WARNING ); return; + } + + NY_ = lenY; +} + +void Mesh2D :: setDecay( StkFloat decayFactor ) +{ + if ( decayFactor < 0.0 || decayFactor > 1.0 ) { + oStream_ << "Mesh2D::setDecay: decayFactor is out of range!"; + handleError( StkError::WARNING ); return; + } + + int i; + for ( i=0; i 1.0 ) { + oStream_ << "Mesh2D::setInputPosition xFactor value is out of range!"; + handleError( StkError::WARNING ); return; + } + + if ( yFactor < 0.0 || yFactor > 1.0 ) { + oStream_ << "Mesh2D::setInputPosition yFactor value is out of range!"; + handleError( StkError::WARNING ); return; + } + + xInput_ = (unsigned short) (xFactor * (NX_ - 1)); + yInput_ = (unsigned short) (yFactor * (NY_ - 1)); +} + +void Mesh2D :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + // Input at corner. + if ( counter_ & 1 ) { + vxp1_[xInput_][yInput_] += amplitude; + vyp1_[xInput_][yInput_] += amplitude; + } + else { + vxp_[xInput_][yInput_] += amplitude; + vyp_[xInput_][yInput_] += amplitude; + } +} + +void Mesh2D :: noteOff( StkFloat amplitude ) +{ + return; +} + +StkFloat Mesh2D :: inputTick( StkFloat input ) +{ + if ( counter_ & 1 ) { + vxp1_[xInput_][yInput_] += input; + vyp1_[xInput_][yInput_] += input; + lastFrame_[0] = tick1(); + } + else { + vxp_[xInput_][yInput_] += input; + vyp_[xInput_][yInput_] += input; + lastFrame_[0] = tick0(); + } + + counter_++; + return lastFrame_[0]; +} + +StkFloat Mesh2D :: tick( unsigned int ) +{ + lastFrame_[0] = ((counter_ & 1) ? this->tick1() : this->tick0()); + counter_++; + return lastFrame_[0]; +} + +const StkFloat VSCALE = 0.5; + +StkFloat Mesh2D :: tick0( void ) +{ + int x, y; + StkFloat outsamp = 0; + + // Update junction velocities. + for (x=0; xsetNX( (unsigned short) (normalizedValue * (NXMAX-2) + 2) ); + else if ( number == 4 ) // 4 + this->setNY( (unsigned short) (normalizedValue * (NYMAX-2) + 2) ); + else if ( number == 11 ) // 11 + this->setDecay( 0.9 + (normalizedValue * 0.1) ); + else if ( number == __SK_ModWheel_ ) // 1 + this->setInputPosition( normalizedValue, normalizedValue ); +#if defined(_STK_DEBUG_) + else { + oStream_ << "Mesh2D::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/MidiFileIn.cpp b/src/stk/src/MidiFileIn.cpp new file mode 100644 index 00000000..d5c8384d --- /dev/null +++ b/src/stk/src/MidiFileIn.cpp @@ -0,0 +1,353 @@ +/**********************************************************************/ +/*! \class MidiFileIn + \brief A standard MIDI file reading/parsing class. + + This class can be used to read events from a standard MIDI file. + Event bytes are copied to a C++ vector and must be subsequently + interpreted by the user. The function getNextMidiEvent() skips + meta and sysex events, returning only MIDI channel messages. + Event delta-times are returned in the form of "ticks" and a + function is provided to determine the current "seconds per tick". + Tempo changes are internally tracked by the class and reflected in + the values returned by the function getTickSeconds(). + + by Gary P. Scavone, 2003--2021. +*/ +/**********************************************************************/ + +#include "MidiFileIn.h" +#include +#include + +namespace stk { + +MidiFileIn :: MidiFileIn( std::string fileName ) +{ + // Attempt to open the file. + file_.open( fileName.c_str(), std::ios::in | std::ios::binary ); + if ( !file_ ) { + oStream_ << "MidiFileIn: error opening or finding file (" << fileName << ")."; + handleError( StkError::FILE_NOT_FOUND ); + } + + // Parse header info. + char chunkType[4]; + char buffer[4]; + SINT32 *length; + if ( !file_.read( chunkType, 4 ) ) goto error; + if ( !file_.read( buffer, 4 ) ) goto error; +#ifdef __LITTLE_ENDIAN__ + swap32((unsigned char *)&buffer); +#endif + length = (SINT32 *) &buffer; + if ( strncmp( chunkType, "MThd", 4 ) || ( *length != 6 ) ) { + oStream_ << "MidiFileIn: file (" << fileName << ") does not appear to be a MIDI file!"; + handleError( StkError::FILE_UNKNOWN_FORMAT ); + } + + // Read the MIDI file format. + SINT16 *data; + if ( !file_.read( buffer, 2 ) ) goto error; +#ifdef __LITTLE_ENDIAN__ + swap16((unsigned char *)&buffer); +#endif + data = (SINT16 *) &buffer; + if ( *data < 0 || *data > 2 ) { + oStream_ << "MidiFileIn: the file (" << fileName << ") format is invalid!"; + handleError( StkError::FILE_ERROR ); + } + format_ = *data; + + // Read the number of tracks. + if ( !file_.read( buffer, 2 ) ) goto error; +#ifdef __LITTLE_ENDIAN__ + swap16((unsigned char *)&buffer); +#endif + if ( format_ == 0 && *data != 1 ) { + oStream_ << "MidiFileIn: invalid number of tracks (>1) for a file format = 0!"; + handleError( StkError::FILE_ERROR ); + } + nTracks_ = *data; + + // Read the beat division. + if ( !file_.read( buffer, 2 ) ) goto error; +#ifdef __LITTLE_ENDIAN__ + swap16((unsigned char *)&buffer); +#endif + division_ = (int) *data; + double tickrate; + usingTimeCode_ = false; + if ( *data & 0x8000 ) { + // Determine ticks per second from time-code formats. + signed char tmp = -(*data & 0xFF00)>>8; + tickrate = (double) tmp; + // If frames per second value is 29, it really should be 29.97. + if ( tickrate == 29.0 ) tickrate = 29.97; + tickrate *= (*data & 0x00FF); + usingTimeCode_ = true; + } + else { + tickrate = (double) (*data & 0x7FFF); // ticks per quarter note + } + + // Now locate the track offsets and lengths. If not using time + // code, we can initialize the "tick time" using a default tempo of + // 120 beats per minute. We will then check for tempo meta-events + // afterward. + unsigned int i; + for ( i=0; i event; + unsigned long value, count; + + // We need to temporarily change the usingTimeCode_ value here so + // that the getNextEvent() function doesn't try to check the tempo + // map (which we're creating here). + usingTimeCode_ = true; + count = getNextEvent( &event, 0 ); + while ( event.size() ) { + if ( ( event.size() == 6 ) && ( event[0] == 0xff ) && + ( event[1] == 0x51 ) && ( event[2] == 0x03 ) ) { + tempoEvent.count = count; + value = ( event[3] << 16 ) + ( event[4] << 8 ) + event[5]; + tempoEvent.tickSeconds = (double) (0.000001 * value / tickrate); + if ( count > tempoEvents_.back().count ) + tempoEvents_.push_back( tempoEvent ); + else + tempoEvents_.back() = tempoEvent; + } + count += getNextEvent( &event, 0 ); + } + rewindTrack( 0 ); + for ( unsigned int i=0; i= nTracks_ ) { + oStream_ << "MidiFileIn::getNextEvent: invalid track argument (" << track << ")."; + handleError( StkError::WARNING ); return; + } + + trackPointers_[track] = trackOffsets_[track]; + trackStatus_[track] = 0; + tickSeconds_[track] = tempoEvents_[0].tickSeconds; +} + +double MidiFileIn :: getTickSeconds( unsigned int track ) +{ + // Return the current tick value in seconds for the given track. + if ( track >= nTracks_ ) { + oStream_ << "MidiFileIn::getTickSeconds: invalid track argument (" << track << ")."; + handleError( StkError::WARNING ); return 0.0; + } + + return tickSeconds_[track]; +} + +unsigned long MidiFileIn :: getNextEvent( std::vector *event, unsigned int track ) +{ + // Fill the user-provided vector with the next event in the + // specified track (default = 0) and return the event delta time in + // ticks. This function assumes that the stored track pointer is + // positioned at the start of a track event. If the track has + // reached its end, the event vector size will be zero. + // + // If we have a format 0 or 2 file and we're not using timecode, we + // should check every meta-event for tempo changes and make + // appropriate updates to the tickSeconds_ parameter if so. + // + // If we have a format 1 file and we're not using timecode, keep a + // running sum of ticks for each track and update the tickSeconds_ + // parameter as needed based on the stored tempo map. + + event->clear(); + if ( track >= nTracks_ ) { + oStream_ << "MidiFileIn::getNextEvent: invalid track argument (" << track << ")."; + handleError( StkError::WARNING ); return 0; + } + + // Check for the end of the track. + if ( (trackPointers_[track] - trackOffsets_[track]) >= trackLengths_[track] ) + return 0; + + unsigned long ticks = 0, bytes = 0; + bool isTempoEvent = false; + + // Read the event delta time. + file_.seekg( trackPointers_[track], std::ios_base::beg ); + if ( !readVariableLength( &ticks ) ) goto error; + + // Parse the event stream to determine the event length. + unsigned char c; + if ( !file_.read( (char *)&c, 1 ) ) goto error; + switch ( c ) { + + case 0xFF: // A Meta-Event + unsigned long position; + trackStatus_[track] = 0; + event->push_back( c ); + if ( !file_.read( (char *)&c, 1 ) ) goto error; + event->push_back( c ); + if ( format_ != 1 && ( c == 0x51 ) ) isTempoEvent = true; + position = (unsigned long)file_.tellg(); + if ( !readVariableLength( &bytes ) ) goto error; + bytes += ( (unsigned long)file_.tellg() - position ); + file_.seekg( position, std::ios_base::beg ); + break; + + case 0xF0: + case 0xF7: // The start or continuation of a Sysex event + trackStatus_[track] = 0; + event->push_back( c ); + position = (unsigned long)file_.tellg(); + if ( !readVariableLength( &bytes ) ) goto error; + bytes += ( (unsigned long)file_.tellg() - position ); + file_.seekg( position, std::ios_base::beg ); + break; + + default: // Should be a MIDI channel event + if ( c & 0x80 ) { // MIDI status byte + if ( c > 0xF0 ) goto error; + trackStatus_[track] = c; + event->push_back( c ); + c &= 0xF0; + if ( (c == 0xC0) || (c == 0xD0) ) bytes = 1; + else bytes = 2; + } + else if ( trackStatus_[track] & 0x80 ) { // Running status + event->push_back( trackStatus_[track] ); + event->push_back( c ); + c = trackStatus_[track] & 0xF0; + if ( (c != 0xC0) && (c != 0xD0) ) bytes = 1; + } + else goto error; + + } + + // Read the rest of the event into the event vector. + unsigned long i; + for ( i=0; ipush_back( c ); + } + + if ( !usingTimeCode_ ) { + if ( isTempoEvent ) { + // Parse the tempo event and update tickSeconds_[track]. + double tickrate = (double) (division_ & 0x7FFF); + unsigned long value = ( event->at(3) << 16 ) + ( event->at(4) << 8 ) + event->at(5); + tickSeconds_[track] = (double) (0.000001 * value / tickrate); + } + + if ( format_ == 1 ) { + // Update track counter and check the tempo map. + trackCounters_[track] += ticks; + TempoChange tempoEvent = tempoEvents_[ trackTempoIndex_[track] ]; + if ( trackCounters_[track] >= tempoEvent.count && trackTempoIndex_[track] < tempoEvents_.size() - 1 ) { + trackTempoIndex_[track]++; + tickSeconds_[track] = tempoEvent.tickSeconds; + } + } + } + + // Save the current track pointer value. + trackPointers_[track] = (long)file_.tellg(); + + return ticks; + + error: + oStream_ << "MidiFileIn::getNextEvent: file read error!"; + handleError( StkError::FILE_ERROR ); + return 0; +} + +unsigned long MidiFileIn :: getNextMidiEvent( std::vector *midiEvent, unsigned int track ) +{ + // Fill the user-provided vector with the next MIDI event in the + // specified track (default = 0) and return the event delta time in + // ticks. Meta-Events preceeding this event are skipped and ignored. + if ( track >= nTracks_ ) { + oStream_ << "MidiFileIn::getNextMidiEvent: invalid track argument (" << track << ")."; + handleError( StkError::WARNING ); return 0; + } + + unsigned long ticks = getNextEvent( midiEvent, track ); + while ( midiEvent->size() && ( midiEvent->at(0) >= 0xF0 ) ) { + //for ( unsigned int i=0; isize(); i++ ) + //std::cout << "event byte = " << i << ", value = " << (int)midiEvent->at(i) << std::endl; + ticks = getNextEvent( midiEvent, track ); + } + + //for ( unsigned int i=0; isize(); i++ ) + //std::cout << "event byte = " << i << ", value = " << (int)midiEvent->at(i) << std::endl; + + return ticks; +} + +bool MidiFileIn :: readVariableLength( unsigned long *value ) +{ + // It is assumed that this function is called with the file read + // pointer positioned at the start of a variable-length value. The + // function returns "true" if the value is successfully parsed and + // "false" otherwise. + *value = 0; + char c; + + if ( !file_.read( &c, 1 ) ) return false; + *value = (unsigned long) c; + if ( *value & 0x80 ) { + *value &= 0x7f; + do { + if ( !file_.read( &c, 1 ) ) return false; + *value = ( *value << 7 ) + ( c & 0x7f ); + } while ( c & 0x80 ); + } + + return true; +} + +} // stk namespace diff --git a/src/stk/src/Modal.cpp b/src/stk/src/Modal.cpp new file mode 100644 index 00000000..f096abdf --- /dev/null +++ b/src/stk/src/Modal.cpp @@ -0,0 +1,169 @@ +/***************************************************/ +/*! \class Modal + \brief STK resonance model abstract base class. + + This class contains an excitation wavetable, + an envelope, an oscillator, and N resonances + (non-sweeping BiQuad filters), where N is set + during instantiation. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Modal.h" +#include + +namespace stk { + +Modal :: Modal( unsigned int modes ) + : nModes_(modes) +{ + if ( nModes_ == 0 ) { + oStream_ << "Modal: 'modes' argument to constructor is zero!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + // We don't make the excitation wave here yet, because we don't know + // what it's going to be. + + ratios_.resize( nModes_ ); + radii_.resize( nModes_ ); + filters_ = (BiQuad **) calloc( nModes_, sizeof(BiQuad *) ); + for (unsigned int i=0; isetEqualGainZeroes(); + } + + // Set some default values. + vibrato_.setFrequency( 6.0 ); + vibratoGain_ = 0.0; + directGain_ = 0.0; + masterGain_ = 1.0; + baseFrequency_ = 440.0; + + this->clear(); + + stickHardness_ = 0.5; + strikePosition_ = 0.561; +} + +Modal :: ~Modal( void ) +{ + for ( unsigned int i=0; iclear(); +} + +void Modal :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "Modal::setFrequency: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + baseFrequency_ = frequency; + for ( unsigned int i=0; isetRatioAndRadius( i, ratios_[i], radii_[i] ); +} + +void Modal :: setRatioAndRadius( unsigned int modeIndex, StkFloat ratio, StkFloat radius ) +{ + if ( modeIndex >= nModes_ ) { + oStream_ << "Modal::setRatioAndRadius: modeIndex parameter is greater than number of modes!"; + handleError( StkError::WARNING ); return; + } + + StkFloat nyquist = Stk::sampleRate() / 2.0; + StkFloat temp; + + if ( ratio * baseFrequency_ < nyquist ) { + ratios_[modeIndex] = ratio; + } + else { + temp = ratio; + while (temp * baseFrequency_ > nyquist) temp *= 0.5; + ratios_[modeIndex] = temp; +#if defined(_STK_DEBUG_) + oStream_ << "Modal::setRatioAndRadius: aliasing would occur here ... correcting."; + handleError( StkError::DEBUG_PRINT ); +#endif + } + radii_[modeIndex] = radius; + if (ratio < 0) + temp = -ratio; + else + temp = ratio * baseFrequency_; + + filters_[modeIndex]->setResonance(temp, radius); +} + +void Modal :: setModeGain( unsigned int modeIndex, StkFloat gain ) +{ + if ( modeIndex >= nModes_ ) { + oStream_ << "Modal::setModeGain: modeIndex parameter is greater than number of modes!"; + handleError( StkError::WARNING ); return; + } + + filters_[modeIndex]->setGain( gain ); +} + +void Modal :: strike( StkFloat amplitude ) +{ + if ( amplitude < 0.0 || amplitude > 1.0 ) { + oStream_ << "Modal::strike: amplitude is out of range!"; + handleError( StkError::WARNING ); + } + + envelope_.setRate( 1.0 ); + envelope_.setTarget( amplitude ); + onepole_.setPole( 1.0 - amplitude ); + envelope_.tick(); + wave_->reset(); + + StkFloat temp; + for ( unsigned int i=0; isetResonance(temp, radii_[i]); + } +} + +void Modal :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + this->strike( amplitude ); + this->setFrequency( frequency ); +} + +void Modal :: noteOff( StkFloat amplitude ) +{ + // This calls damp, but inverts the meaning of amplitude (high + // amplitude means fast damping). + this->damp( 1.0 - (amplitude * 0.03) ); +} + +void Modal :: damp( StkFloat amplitude ) +{ + StkFloat temp; + for ( unsigned int i=0; isetResonance( temp, radii_[i]*amplitude ); + } +} + +} // stk namespace diff --git a/src/stk/src/ModalBar.cpp b/src/stk/src/ModalBar.cpp new file mode 100644 index 00000000..6274cb29 --- /dev/null +++ b/src/stk/src/ModalBar.cpp @@ -0,0 +1,182 @@ +/***************************************************/ +/*! \class ModalBar + \brief STK resonant bar instrument class. + + This class implements a number of different + struck bar instruments. It inherits from the + Modal class. + + Control Change Numbers: + - Stick Hardness = 2 + - Stick Position = 4 + - Vibrato Gain = 8 + - Vibrato Frequency = 11 + - Direct Stick Mix = 1 + - Volume = 128 + - Modal Presets = 16 + - Marimba = 0 + - Vibraphone = 1 + - Agogo = 2 + - Wood1 = 3 + - Reso = 4 + - Wood2 = 5 + - Beats = 6 + - Two Fixed = 7 + - Clump = 8 + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "ModalBar.h" +#include "SKINImsg.h" +#include + +namespace stk { + +ModalBar :: ModalBar( void ) + : Modal() +{ + // Concatenate the STK rawwave path to the rawwave file + wave_ = new FileWvIn( (Stk::rawwavePath() + "marmstk1.raw").c_str(), true ); + wave_->setRate( 0.5 * 22050.0 / Stk::sampleRate() ); + + // Set the resonances for preset 0 (marimba). + this->setPreset( 0 ); +} + +ModalBar :: ~ModalBar( void ) +{ + delete wave_; +} + +void ModalBar :: setStickHardness( StkFloat hardness ) +{ + if ( hardness < 0.0 || hardness > 1.0 ) { + oStream_ << "ModalBar::setStickHardness: parameter is out of range!"; + handleError( StkError::WARNING ); return; + } + + stickHardness_ = hardness; + wave_->setRate( (0.25 * pow(4.0, stickHardness_) ) ); + masterGain_ = 0.1 + (1.8 * stickHardness_); +} + +void ModalBar :: setStrikePosition( StkFloat position ) +{ + if ( position < 0.0 || position > 1.0 ) { + oStream_ << "ModalBar::setStrikePosition: parameter is out of range!"; + handleError( StkError::WARNING ); return; + } + + strikePosition_ = position; + + // Hack only first three modes. + StkFloat temp2 = position * PI; + StkFloat temp = sin(temp2); + this->setModeGain(0, 0.12 * temp); + + temp = sin(0.05 + (3.9 * temp2)); + this->setModeGain(1, -0.03 * temp); + + temp = sin(-0.05 + (11 * temp2)); + this->setModeGain(2, 0.11 * temp); +} + +void ModalBar :: setPreset( int preset ) +{ + // Presets: + // First line: relative modal frequencies (negative number is + // a fixed mode that doesn't scale with frequency + // Second line: resonances of the modes + // Third line: mode volumes + // Fourth line: stickHardness, strikePosition, and direct stick + // gain (mixed directly into the output + static StkFloat presets[9][4][4] = { + {{1.0, 3.99, 10.65, -2443}, // Marimba + {0.9996, 0.9994, 0.9994, 0.999}, + {0.04, 0.01, 0.01, 0.008}, + {0.429688, 0.445312, 0.093750}}, + {{1.0, 2.01, 3.9, 14.37}, // Vibraphone + {0.99995, 0.99991, 0.99992, 0.9999}, + {0.025, 0.015, 0.015, 0.015 }, + {0.390625,0.570312,0.078125}}, + {{1.0, 4.08, 6.669, -3725.0}, // Agogo + {0.999, 0.999, 0.999, 0.999}, + {0.06, 0.05, 0.03, 0.02}, + {0.609375,0.359375,0.140625}}, + {{1.0, 2.777, 7.378, 15.377}, // Wood1 + {0.996, 0.994, 0.994, 0.99}, + {0.04, 0.01, 0.01, 0.008}, + {0.460938,0.375000,0.046875}}, + {{1.0, 2.777, 7.378, 15.377}, // Reso + {0.99996, 0.99994, 0.99994, 0.9999}, + {0.02, 0.005, 0.005, 0.004}, + {0.453125,0.250000,0.101562}}, + {{1.0, 1.777, 2.378, 3.377}, // Wood2 + {0.996, 0.994, 0.994, 0.99}, + {0.04, 0.01, 0.01, 0.008}, + {0.312500,0.445312,0.109375}}, + {{1.0, 1.004, 1.013, 2.377}, // Beats + {0.9999, 0.9999, 0.9999, 0.999}, + {0.02, 0.005, 0.005, 0.004}, + {0.398438,0.296875,0.070312}}, + {{1.0, 4.0, -1320.0, -3960.0}, // 2Fix + {0.9996, 0.999, 0.9994, 0.999}, + {0.04, 0.01, 0.01, 0.008}, + {0.453125,0.453125,0.070312}}, + {{1.0, 1.217, 1.475, 1.729}, // Clump + {0.999, 0.999, 0.999, 0.999}, + {0.03, 0.03, 0.03, 0.03 }, + {0.390625,0.570312,0.078125}}, + }; + + int temp = (preset % 9); + for (unsigned int i=0; isetRatioAndRadius(i, presets[temp][0][i], presets[temp][1][i]); + this->setModeGain(i, presets[temp][2][i]); + } + + this->setStickHardness(presets[temp][3][0]); + this->setStrikePosition(presets[temp][3][1]); + directGain_ = presets[temp][3][2]; + + if (temp == 1) // vibraphone + vibratoGain_ = 0.2; + else + vibratoGain_ = 0.0; +} + +void ModalBar :: controlChange( int number, StkFloat value ) +{ +#if defined(_STK_DEBUG_) + if ( Stk::inRange( value, 0.0, 128.0 ) == false ) { + oStream_ << "ModalBar::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if (number == __SK_StickHardness_) // 2 + this->setStickHardness( normalizedValue ); + else if (number == __SK_StrikePosition_) // 4 + this->setStrikePosition( normalizedValue ); + else if (number == __SK_ProphesyRibbon_) // 16 + this->setPreset((int) value); + else if (number == __SK_Balance_) // 8 + vibratoGain_ = normalizedValue * 0.3; + else if (number == __SK_ModWheel_) // 1 + directGain_ = normalizedValue; + else if (number == __SK_ModFrequency_) // 11 + vibrato_.setFrequency( normalizedValue * 12.0 ); + else if (number == __SK_AfterTouch_Cont_) // 128 + envelope_.setTarget( normalizedValue ); +#if defined(_STK_DEBUG_) + else { + oStream_ << "ModalBar::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/Modulate.cpp b/src/stk/src/Modulate.cpp new file mode 100644 index 00000000..c81eae50 --- /dev/null +++ b/src/stk/src/Modulate.cpp @@ -0,0 +1,49 @@ +/***************************************************/ +/*! \class Modulate + \brief STK periodic/random modulator. + + This class combines random and periodic + modulations to give a nice, natural human + modulation function. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Modulate.h" + +namespace stk { + +Modulate :: Modulate( void ) +{ + vibrato_.setFrequency( 6.0 ); + vibratoGain_ = 0.04; + + noiseRate_ = (unsigned int) ( 330.0 * Stk::sampleRate() / 22050.0 ); + noiseCounter_ = noiseRate_; + + randomGain_ = 0.05; + filter_.setPole( 0.999 ); + filter_.setGain( randomGain_ ); + + Stk::addSampleRateAlert( this ); +} + +Modulate :: ~Modulate( void ) +{ + Stk::removeSampleRateAlert( this ); +} + +void Modulate :: sampleRateChanged( StkFloat newRate, StkFloat oldRate ) +{ + if ( !ignoreSampleRateChange_ ) + noiseRate_ = (unsigned int ) ( newRate * noiseRate_ / oldRate ); +} + +void Modulate :: setRandomGain( StkFloat gain ) +{ + randomGain_ = gain; + filter_.setGain( randomGain_ ); +} + +} // stk namespace diff --git a/src/stk/src/Moog.cpp b/src/stk/src/Moog.cpp new file mode 100644 index 00000000..4451b21d --- /dev/null +++ b/src/stk/src/Moog.cpp @@ -0,0 +1,111 @@ +/***************************************************/ +/*! \class Moog + \brief STK moog-like swept filter sampling synthesis class. + + This instrument uses one attack wave, one + looped wave, and an ADSR envelope (inherited + from the Sampler class) and adds two sweepable + formant (FormSwep) filters. + + Control Change Numbers: + - Filter Q = 2 + - Filter Sweep Rate = 4 + - Vibrato Frequency = 11 + - Vibrato Gain = 1 + - Gain = 128 + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Moog.h" +#include "SKINImsg.h" + +namespace stk { + +Moog :: Moog( void ) +{ + // Concatenate the STK rawwave path to the rawwave file + attacks_.push_back( new FileWvIn( (Stk::rawwavePath() + "mandpluk.raw").c_str(), true ) ); + loops_.push_back ( new FileLoop( (Stk::rawwavePath() + "impuls20.raw").c_str(), true ) ); + loops_.push_back ( new FileLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ) ); // vibrato + loops_[1]->setFrequency( 6.122 ); + + filters_[0].setTargets( 0.0, 0.7 ); + filters_[1].setTargets( 0.0, 0.7 ); + + adsr_.setAllTimes( 0.001, 1.5, 0.6, 0.250 ); + filterQ_ = 0.85; + filterRate_ = 0.0001; + modDepth_ = 0.0; +} + +Moog :: ~Moog( void ) +{ +} + +void Moog :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "Moog::setFrequency: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + baseFrequency_ = frequency; + StkFloat rate = attacks_[0]->getSize() * 0.01 * baseFrequency_ / Stk::sampleRate(); + attacks_[0]->setRate( rate ); + loops_[0]->setFrequency( baseFrequency_ ); +} + +void Moog :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + StkFloat temp; + + this->setFrequency( frequency ); + this->keyOn(); + attackGain_ = amplitude * 0.5; + loopGain_ = amplitude; + + temp = filterQ_ + 0.05; + filters_[0].setStates( 2000.0, temp ); + filters_[1].setStates( 2000.0, temp ); + + temp = filterQ_ + 0.099; + filters_[0].setTargets( frequency, temp ); + filters_[1].setTargets( frequency, temp ); + + filters_[0].setSweepRate( filterRate_ * 22050.0 / Stk::sampleRate() ); + filters_[1].setSweepRate( filterRate_ * 22050.0 / Stk::sampleRate() ); +} + +void Moog :: controlChange( int number, StkFloat value ) +{ +#if defined(_STK_DEBUG_) + if ( Stk::inRange( value, 0.0, 128.0 ) == false ) { + oStream_ << "Moog::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if (number == __SK_FilterQ_) // 2 + filterQ_ = 0.80 + ( 0.1 * normalizedValue ); + else if (number == __SK_FilterSweepRate_) // 4 + filterRate_ = normalizedValue * 0.0002; + else if (number == __SK_ModFrequency_) // 11 + this->setModulationSpeed( normalizedValue * 12.0 ); + else if (number == __SK_ModWheel_) // 1 + this->setModulationDepth( normalizedValue ); + else if (number == __SK_AfterTouch_Cont_) // 128 + adsr_.setTarget( normalizedValue ); +#if defined(_STK_DEBUG_) + else { + oStream_ << "Moog::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/NRev.cpp b/src/stk/src/NRev.cpp new file mode 100644 index 00000000..63686c8a --- /dev/null +++ b/src/stk/src/NRev.cpp @@ -0,0 +1,119 @@ +/***************************************************/ +/*! \class NRev + \brief CCRMA's NRev reverberator class. + + This class takes a monophonic input signal and produces a stereo + output signal. It is derived from the CLM NRev function, which is + based on the use of networks of simple allpass and comb delay + filters. This particular arrangement consists of 6 comb filters + in parallel, followed by 3 allpass filters, a lowpass filter, and + another allpass in series, followed by two allpass filters in + parallel with corresponding right and left outputs. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "NRev.h" +#include + +namespace stk { + +NRev :: NRev( StkFloat T60 ) +{ + if ( T60 <= 0.0 ) { + oStream_ << "NRev::NRev: argument (" << T60 << ") must be positive!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + lastFrame_.resize( 1, 2, 0.0 ); // resize lastFrame_ for stereo output + + int lengths[15] = {1433, 1601, 1867, 2053, 2251, 2399, 347, 113, 37, 59, 53, 43, 37, 29, 19}; + double scaler = Stk::sampleRate() / 25641.0; + + int delay, i; + for ( i=0; i<15; i++ ) { + delay = (int) floor(scaler * lengths[i]); + if ( (delay & 1) == 0) delay++; + while ( !this->isPrime(delay) ) delay += 2; + lengths[i] = delay; + } + + for ( i=0; i<6; i++ ) { + combDelays_[i].setMaximumDelay( lengths[i] ); + combDelays_[i].setDelay( lengths[i] ); + combCoefficient_[i] = pow(10.0, (-3 * lengths[i] / (T60 * Stk::sampleRate()))); + } + + for ( i=0; i<8; i++ ) { + allpassDelays_[i].setMaximumDelay( lengths[i+6] ); + allpassDelays_[i].setDelay( lengths[i+6] ); + } + + this->setT60( T60 ); + allpassCoefficient_ = 0.7; + effectMix_ = 0.3; + this->clear(); +} + +void NRev :: clear() +{ + int i; + for (i=0; i<6; i++) combDelays_[i].clear(); + for (i=0; i<8; i++) allpassDelays_[i].clear(); + lastFrame_[0] = 0.0; + lastFrame_[1] = 0.0; + lowpassState_ = 0.0; +} + +void NRev :: setT60( StkFloat T60 ) +{ + if ( T60 <= 0.0 ) { + oStream_ << "NRev::setT60: argument (" << T60 << ") must be positive!"; + handleError( StkError::WARNING ); return; + } + + for ( int i=0; i<6; i++ ) + combCoefficient_[i] = pow(10.0, (-3.0 * combDelays_[i].getDelay() / (T60 * Stk::sampleRate()))); +} + +StkFrames& NRev :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() - 1 ) { + oStream_ << "NRev::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() - 1 ) { + oStream_ << "NRev::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i + +namespace stk { + +Noise :: Noise( unsigned int seed ) +{ + // Seed the random number generator + this->setSeed( seed ); +} + +void Noise :: setSeed( unsigned int seed ) +{ + if ( seed == 0 ) + srand( (unsigned int) time( NULL ) ); + else + srand( seed ); +} + +} // stk namespace + + diff --git a/src/stk/src/OnePole.cpp b/src/stk/src/OnePole.cpp new file mode 100644 index 00000000..bbdfa617 --- /dev/null +++ b/src/stk/src/OnePole.cpp @@ -0,0 +1,61 @@ +/***************************************************/ +/*! \class OnePole + \brief STK one-pole filter class. + + This class implements a one-pole digital filter. A method is + provided for setting the pole position along the real axis of the + z-plane while maintaining a constant peak filter gain. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "OnePole.h" + +namespace stk { + +OnePole :: OnePole( StkFloat thePole ) +{ + b_.resize( 1 ); + a_.resize( 2 ); + a_[0] = 1.0; + inputs_.resize( 1, 1, 0.0 ); + outputs_.resize( 2, 1, 0.0 ); + + this->setPole( thePole ); +} + +OnePole :: ~OnePole() +{ +} + +void OnePole :: setPole( StkFloat thePole ) +{ + if ( std::abs( thePole ) >= 1.0 ) { + oStream_ << "OnePole::setPole: argument (" << thePole << ") should be less than 1.0!"; + handleError( StkError::WARNING ); return; + } + + // Normalize coefficients for peak unity gain. + if ( thePole > 0.0 ) + b_[0] = (StkFloat) (1.0 - thePole); + else + b_[0] = (StkFloat) (1.0 + thePole); + + a_[1] = -thePole; +} + +void OnePole :: setCoefficients( StkFloat b0, StkFloat a1, bool clearState ) +{ + if ( std::abs( a1 ) >= 1.0 ) { + oStream_ << "OnePole::setCoefficients: a1 argument (" << a1 << ") should be less than 1.0!"; + handleError( StkError::WARNING ); return; + } + + b_[0] = b0; + a_[1] = a1; + + if ( clearState ) this->clear(); +} + +} // stk namespace diff --git a/src/stk/src/OneZero.cpp b/src/stk/src/OneZero.cpp new file mode 100644 index 00000000..bf16c856 --- /dev/null +++ b/src/stk/src/OneZero.cpp @@ -0,0 +1,49 @@ +/***************************************************/ +/*! \class OneZero + \brief STK one-zero filter class. + + This class implements a one-zero digital filter. A method is + provided for setting the zero position along the real axis of the + z-plane while maintaining a constant filter gain. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "OneZero.h" +#include + +namespace stk { + +OneZero :: OneZero( StkFloat theZero ) +{ + b_.resize( 2 ); + inputs_.resize( 2, 1, 0.0 ); + + this->setZero( theZero ); +} + +OneZero :: ~OneZero( void ) +{ +} + +void OneZero :: setZero( StkFloat theZero ) +{ + // Normalize coefficients for unity gain. + if ( theZero > 0.0 ) + b_[0] = 1.0 / ((StkFloat) 1.0 + theZero); + else + b_[0] = 1.0 / ((StkFloat) 1.0 - theZero); + + b_[1] = -theZero * b_[0]; +} + +void OneZero :: setCoefficients( StkFloat b0, StkFloat b1, bool clearState ) +{ + b_[0] = b0; + b_[1] = b1; + + if ( clearState ) this->clear(); +} + +} // stk namespace diff --git a/src/stk/src/PRCRev.cpp b/src/stk/src/PRCRev.cpp new file mode 100644 index 00000000..f2ecd21c --- /dev/null +++ b/src/stk/src/PRCRev.cpp @@ -0,0 +1,120 @@ +/***************************************************/ +/*! \class PRCRev + \brief Perry's simple reverberator class. + + This class is based on some of the famous + Stanford/CCRMA reverbs (NRev, KipRev), which + were based on the Chowning/Moorer/Schroeder + reverberators using networks of simple allpass + and comb delay filters. This class implements + two series allpass units and two parallel comb + filters. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "PRCRev.h" +#include + +namespace stk { + +PRCRev :: PRCRev( StkFloat T60 ) +{ + if ( T60 <= 0.0 ) { + oStream_ << "PRCRev::PRCRev: argument (" << T60 << ") must be positive!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + lastFrame_.resize( 1, 2, 0.0 ); // resize lastFrame_ for stereo output + + // Delay lengths for 44100 Hz sample rate. + int lengths[4]= {341, 613, 1557, 2137}; + double scaler = Stk::sampleRate() / 44100.0; + + // Scale the delay lengths if necessary. + int delay, i; + if ( scaler != 1.0 ) { + for (i=0; i<4; i++) { + delay = (int) floor(scaler * lengths[i]); + if ( (delay & 1) == 0) delay++; + while ( !this->isPrime(delay) ) delay += 2; + lengths[i] = delay; + } + } + + for ( i=0; i<2; i++ ) { + allpassDelays_[i].setMaximumDelay( lengths[i] ); + allpassDelays_[i].setDelay( lengths[i] ); + + combDelays_[i].setMaximumDelay( lengths[i+2] ); + combDelays_[i].setDelay( lengths[i+2] ); + } + + this->setT60( T60 ); + allpassCoefficient_ = 0.7; + effectMix_ = 0.5; + this->clear(); +} + +void PRCRev :: clear( void ) +{ + allpassDelays_[0].clear(); + allpassDelays_[1].clear(); + combDelays_[0].clear(); + combDelays_[1].clear(); + lastFrame_[0] = 0.0; + lastFrame_[1] = 0.0; +} + +void PRCRev :: setT60( StkFloat T60 ) +{ + if ( T60 <= 0.0 ) { + oStream_ << "PRCRev::setT60: argument (" << T60 << ") must be positive!"; + handleError( StkError::WARNING ); return; + } + + combCoefficient_[0] = pow(10.0, (-3.0 * combDelays_[0].getDelay() / (T60 * Stk::sampleRate()))); + combCoefficient_[1] = pow(10.0, (-3.0 * combDelays_[1].getDelay() / (T60 * Stk::sampleRate()))); +} + +StkFrames& PRCRev :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() - 1 ) { + oStream_ << "PRCRev::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() - 1 ) { + oStream_ << "PRCRev::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; i3--\ + 2-- + -->1-->Out + \endcode + + Control Change Numbers: + - Total Modulator Index = 2 + - Modulator Crossfade = 4 + - LFO Speed = 11 + - LFO Depth = 1 + - ADSR 2 & 4 Target = 128 + + The basic Chowning/Stanford FM patent expired + in 1995, but there exist follow-on patents, + mostly assigned to Yamaha. If you are of the + type who should worry about this (making + money) worry away. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "PercFlut.h" + +namespace stk { + +PercFlut :: PercFlut( void ) + : FM() +{ + // Concatenate the STK rawwave path to the rawwave files + for ( unsigned int i=0; i<3; i++ ) + waves_[i] = new FileLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + waves_[3] = new FileLoop( (Stk::rawwavePath() + "fwavblnk.raw").c_str(), true ); + + this->setRatio(0, 1.50 * 1.000); + this->setRatio(1, 3.00 * 0.995); + this->setRatio(2, 2.99 * 1.005); + this->setRatio(3, 6.00 * 0.997); + gains_[0] = fmGains_[99]; + gains_[1] = fmGains_[71]; + gains_[2] = fmGains_[93]; + gains_[3] = fmGains_[85]; + + adsr_[0]->setAllTimes( 0.05, 0.05, fmSusLevels_[14], 0.05); + adsr_[1]->setAllTimes( 0.02, 0.50, fmSusLevels_[13], 0.5); + adsr_[2]->setAllTimes( 0.02, 0.30, fmSusLevels_[11], 0.05); + adsr_[3]->setAllTimes( 0.02, 0.05, fmSusLevels_[13], 0.01); + + twozero_.setGain( 0.0 ); + modDepth_ = 0.005; +} + +PercFlut :: ~PercFlut( void ) +{ +} + +void PercFlut :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "PercFlut::setFrequency: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + baseFrequency_ = frequency; +} + +void PercFlut :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + gains_[0] = amplitude * fmGains_[99] * 0.5; + gains_[1] = amplitude * fmGains_[71] * 0.5; + gains_[2] = amplitude * fmGains_[93] * 0.5; + gains_[3] = amplitude * fmGains_[85] * 0.5; + this->setFrequency( frequency ); + this->keyOn(); +} + +} // stk namespace diff --git a/src/stk/src/Phonemes.cpp b/src/stk/src/Phonemes.cpp new file mode 100644 index 00000000..fa3d52b0 --- /dev/null +++ b/src/stk/src/Phonemes.cpp @@ -0,0 +1,292 @@ +/***************************************************/ +/*! \class Phonemes + \brief STK phonemes table. + + This class does nothing other than declare a + set of 32 static phoneme formant parameters + and provide access to those values. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Phonemes.h" +#include + +namespace stk { + +const char Phonemes :: phonemeNames[32][4] = + {"eee", "ihh", "ehh", "aaa", + "ahh", "aww", "ohh", "uhh", + "uuu", "ooo", "rrr", "lll", + "mmm", "nnn", "nng", "ngg", + "fff", "sss", "thh", "shh", + "xxx", "hee", "hoo", "hah", + "bbb", "ddd", "jjj", "ggg", + "vvv", "zzz", "thz", "zhh" + }; + +const StkFloat Phonemes :: phonemeGains[32][2] = + {{1.0, 0.0}, // eee + {1.0, 0.0}, // ihh + {1.0, 0.0}, // ehh + {1.0, 0.0}, // aaa + + {1.0, 0.0}, // ahh + {1.0, 0.0}, // aww + {1.0, 0.0}, // ohh + {1.0, 0.0}, // uhh + + {1.0, 0.0}, // uuu + {1.0, 0.0}, // ooo + {1.0, 0.0}, // rrr + {1.0, 0.0}, // lll + + {1.0, 0.0}, // mmm + {1.0, 0.0}, // nnn + {1.0, 0.0}, // nng + {1.0, 0.0}, // ngg + + {0.0, 0.7}, // fff + {0.0, 0.7}, // sss + {0.0, 0.7}, // thh + {0.0, 0.7}, // shh + + {0.0, 0.7}, // xxx + {0.0, 0.1}, // hee + {0.0, 0.1}, // hoo + {0.0, 0.1}, // hah + + {1.0, 0.1}, // bbb + {1.0, 0.1}, // ddd + {1.0, 0.1}, // jjj + {1.0, 0.1}, // ggg + + {1.0, 1.0}, // vvv + {1.0, 1.0}, // zzz + {1.0, 1.0}, // thz + {1.0, 1.0} // zhh + }; + +const StkFloat Phonemes :: phonemeParameters[32][4][3] = + {{ { 273, 0.996, 10}, // eee (beet) + {2086, 0.945, -16}, + {2754, 0.979, -12}, + {3270, 0.440, -17}}, + { { 385, 0.987, 10}, // ihh (bit) + {2056, 0.930, -20}, + {2587, 0.890, -20}, + {3150, 0.400, -20}}, + { { 515, 0.977, 10}, // ehh (bet) + {1805, 0.810, -10}, + {2526, 0.875, -10}, + {3103, 0.400, -13}}, + { { 773, 0.950, 10}, // aaa (bat) + {1676, 0.830, -6}, + {2380, 0.880, -20}, + {3027, 0.600, -20}}, + + { { 770, 0.950, 0}, // ahh (father) + {1153, 0.970, -9}, + {2450, 0.780, -29}, + {3140, 0.800, -39}}, + { { 637, 0.910, 0}, // aww (bought) + { 895, 0.900, -3}, + {2556, 0.950, -17}, + {3070, 0.910, -20}}, + { { 637, 0.910, 0}, // ohh (bone) NOTE:: same as aww (bought) + { 895, 0.900, -3}, + {2556, 0.950, -17}, + {3070, 0.910, -20}}, + { { 561, 0.965, 0}, // uhh (but) + {1084, 0.930, -10}, + {2541, 0.930, -15}, + {3345, 0.900, -20}}, + + { { 515, 0.976, 0}, // uuu (foot) + {1031, 0.950, -3}, + {2572, 0.960, -11}, + {3345, 0.960, -20}}, + { { 349, 0.986, -10}, // ooo (boot) + { 918, 0.940, -20}, + {2350, 0.960, -27}, + {2731, 0.950, -33}}, + { { 394, 0.959, -10}, // rrr (bird) + {1297, 0.780, -16}, + {1441, 0.980, -16}, + {2754, 0.950, -40}}, + { { 462, 0.990, +5}, // lll (lull) + {1200, 0.640, -10}, + {2500, 0.200, -20}, + {3000, 0.100, -30}}, + + { { 265, 0.987, -10}, // mmm (mom) + {1176, 0.940, -22}, + {2352, 0.970, -20}, + {3277, 0.940, -31}}, + { { 204, 0.980, -10}, // nnn (nun) + {1570, 0.940, -15}, + {2481, 0.980, -12}, + {3133, 0.800, -30}}, + { { 204, 0.980, -10}, // nng (sang) NOTE:: same as nnn + {1570, 0.940, -15}, + {2481, 0.980, -12}, + {3133, 0.800, -30}}, + { { 204, 0.980, -10}, // ngg (bong) NOTE:: same as nnn + {1570, 0.940, -15}, + {2481, 0.980, -12}, + {3133, 0.800, -30}}, + + { {1000, 0.300, 0}, // fff + {2800, 0.860, -10}, + {7425, 0.740, 0}, + {8140, 0.860, 0}}, + { {0, 0.000, 0}, // sss + {2000, 0.700, -15}, + {5257, 0.750, -3}, + {7171, 0.840, 0}}, + { { 100, 0.900, 0}, // thh + {4000, 0.500, -20}, + {5500, 0.500, -15}, + {8000, 0.400, -20}}, + { {2693, 0.940, 0}, // shh + {4000, 0.720, -10}, + {6123, 0.870, -10}, + {7755, 0.750, -18}}, + + { {1000, 0.300, -10}, // xxx NOTE:: Not Really Done Yet + {2800, 0.860, -10}, + {7425, 0.740, 0}, + {8140, 0.860, 0}}, + { { 273, 0.996, -40}, // hee (beet) (noisy eee) + {2086, 0.945, -16}, + {2754, 0.979, -12}, + {3270, 0.440, -17}}, + { { 349, 0.986, -40}, // hoo (boot) (noisy ooo) + { 918, 0.940, -10}, + {2350, 0.960, -17}, + {2731, 0.950, -23}}, + { { 770, 0.950, -40}, // hah (father) (noisy ahh) + {1153, 0.970, -3}, + {2450, 0.780, -20}, + {3140, 0.800, -32}}, + + { {2000, 0.700, -20}, // bbb NOTE:: Not Really Done Yet + {5257, 0.750, -15}, + {7171, 0.840, -3}, + {9000, 0.900, 0}}, + { { 100, 0.900, 0}, // ddd NOTE:: Not Really Done Yet + {4000, 0.500, -20}, + {5500, 0.500, -15}, + {8000, 0.400, -20}}, + { {2693, 0.940, 0}, // jjj NOTE:: Not Really Done Yet + {4000, 0.720, -10}, + {6123, 0.870, -10}, + {7755, 0.750, -18}}, + { {2693, 0.940, 0}, // ggg NOTE:: Not Really Done Yet + {4000, 0.720, -10}, + {6123, 0.870, -10}, + {7755, 0.750, -18}}, + + { {2000, 0.700, -20}, // vvv NOTE:: Not Really Done Yet + {5257, 0.750, -15}, + {7171, 0.840, -3}, + {9000, 0.900, 0}}, + { { 100, 0.900, 0}, // zzz NOTE:: Not Really Done Yet + {4000, 0.500, -20}, + {5500, 0.500, -15}, + {8000, 0.400, -20}}, + { {2693, 0.940, 0}, // thz NOTE:: Not Really Done Yet + {4000, 0.720, -10}, + {6123, 0.870, -10}, + {7755, 0.750, -18}}, + { {2693, 0.940, 0}, // zhh NOTE:: Not Really Done Yet + {4000, 0.720, -10}, + {6123, 0.870, -10}, + {7755, 0.750, -18}} + }; + +Phonemes :: Phonemes(void) +{ +} + +Phonemes :: ~Phonemes(void) +{ +} + +const char *Phonemes :: name( unsigned int index ) +{ + if ( index > 31 ) { + oStream_ << "Phonemes::name: index is greater than 31!"; + handleError( oStream_.str(), StkError::WARNING ); + return 0; + } + return phonemeNames[index]; +} + +StkFloat Phonemes :: voiceGain( unsigned int index ) +{ + if ( index > 31 ) { + oStream_ << "Phonemes::voiceGain: index is greater than 31!"; + handleError( oStream_.str(), StkError::WARNING ); + return 0.0; + } + return phonemeGains[index][0]; +} + +StkFloat Phonemes :: noiseGain( unsigned int index ) +{ + if ( index > 31 ) { + oStream_ << "Phonemes::noiseGain: index is greater than 31!"; + handleError( oStream_.str(), StkError::WARNING ); + return 0.0; + } + return phonemeGains[index][1]; +} + +StkFloat Phonemes :: formantFrequency( unsigned int index, unsigned int partial ) +{ + if ( index > 31 ) { + oStream_ << "Phonemes::formantFrequency: index is greater than 31!"; + handleError( oStream_.str(), StkError::WARNING ); + return 0.0; + } + if ( partial > 3 ) { + oStream_ << "Phonemes::formantFrequency: partial is greater than 3!"; + handleError( oStream_.str(), StkError::WARNING ); + return 0.0; + } + return phonemeParameters[index][partial][0]; +} + +StkFloat Phonemes :: formantRadius( unsigned int index, unsigned int partial ) +{ + if ( index > 31 ) { + oStream_ << "Phonemes::formantRadius: index is greater than 31!"; + handleError( oStream_.str(), StkError::WARNING ); + return 0.0; + } + if ( partial > 3 ) { + oStream_ << "Phonemes::formantRadius: partial is greater than 3!"; + handleError( oStream_.str(), StkError::WARNING ); + return 0.0; + } + return phonemeParameters[index][partial][1]; +} + +StkFloat Phonemes :: formantGain( unsigned int index, unsigned int partial ) +{ + if ( index > 31 ) { + oStream_ << "Phonemes::formantGain: index is greater than 31!"; + handleError( oStream_.str(), StkError::WARNING ); + return 0.0; + } + if ( partial > 3 ) { + oStream_ << "Phonemes::formantGain: partial is greater than 3!"; + handleError( oStream_.str(), StkError::WARNING ); + return 0.0; + } + return phonemeParameters[index][partial][2]; +} + +} // stk namespace diff --git a/src/stk/src/PitShift.cpp b/src/stk/src/PitShift.cpp new file mode 100644 index 00000000..eaead218 --- /dev/null +++ b/src/stk/src/PitShift.cpp @@ -0,0 +1,88 @@ +/***************************************************/ +/*! \class PitShift + \brief STK simple pitch shifter effect class. + + This class implements a simple pitch shifter + using delay lines. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "PitShift.h" +#include + +namespace stk { + +PitShift :: PitShift( void ) +{ + delayLength_ = maxDelay - 24; + halfLength_ = delayLength_ / 2; + delay_[0] = 12; + delay_[1] = maxDelay / 2; + + delayLine_[0].setMaximumDelay( maxDelay ); + delayLine_[0].setDelay( delay_[0] ); + delayLine_[1].setMaximumDelay( maxDelay ); + delayLine_[1].setDelay( delay_[1] ); + effectMix_ = 0.5; + rate_ = 1.0; +} + +void PitShift :: clear() +{ + delayLine_[0].clear(); + delayLine_[1].clear(); + lastFrame_[0] = 0.0; +} + +void PitShift :: setShift( StkFloat shift ) +{ + if ( shift < 1.0 ) { + rate_ = 1.0 - shift; + } + else if ( shift > 1.0 ) { + rate_ = 1.0 - shift; + } + else { + rate_ = 0.0; + delay_[0] = halfLength_ + 12; + } +} + +StkFrames& PitShift :: tick( StkFrames& frames, unsigned int channel ) +{ +#if defined(_STK_DEBUG_) + if ( channel >= frames.channels() ) { + oStream_ << "PitShift::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *samples = &frames[channel]; + unsigned int hop = frames.channels(); + for ( unsigned int i=0; i= iFrames.channels() || oChannel >= oFrames.channels() ) { + oStream_ << "PitShift::tick(): channel and StkFrames arguments are incompatible!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } +#endif + + StkFloat *iSamples = &iFrames[iChannel]; + StkFloat *oSamples = &oFrames[oChannel]; + unsigned int iHop = iFrames.channels(), oHop = oFrames.channels(); + for ( unsigned int i=0; isetFrequency( 220.0 ); +} + +Plucked :: ~Plucked( void ) +{ +} + +void Plucked :: clear( void ) +{ + delayLine_.clear(); + loopFilter_.clear(); + pickFilter_.clear(); +} + +void Plucked :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "Plucked::setFrequency: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + // Delay = length - filter delay. + StkFloat delay = ( Stk::sampleRate() / frequency ) - loopFilter_.phaseDelay( frequency ); + delayLine_.setDelay( delay ); + + loopGain_ = 0.995 + (frequency * 0.000005); + if ( loopGain_ >= 1.0 ) loopGain_ = 0.99999; +} + +void Plucked :: pluck( StkFloat amplitude ) +{ + if ( amplitude < 0.0 || amplitude > 1.0 ) { + oStream_ << "Plucked::pluck: amplitude is out of range!"; + handleError( StkError::WARNING ); return; + } + + pickFilter_.setPole( 0.999 - (amplitude * 0.15) ); + pickFilter_.setGain( amplitude * 0.5 ); + for ( unsigned long i=0; isetFrequency( frequency ); + this->pluck( amplitude ); +} + +void Plucked :: noteOff( StkFloat amplitude ) +{ + if ( amplitude < 0.0 || amplitude > 1.0 ) { + oStream_ << "Plucked::noteOff: amplitude is out of range!"; + handleError( StkError::WARNING ); return; + } + + loopGain_ = 1.0 - amplitude; +} + +} // stk namespace diff --git a/src/stk/src/PoleZero.cpp b/src/stk/src/PoleZero.cpp new file mode 100644 index 00000000..2f2e8215 --- /dev/null +++ b/src/stk/src/PoleZero.cpp @@ -0,0 +1,73 @@ +/***************************************************/ +/*! \class PoleZero + \brief STK one-pole, one-zero filter class. + + This class implements a one-pole, one-zero digital filter. A + method is provided for creating an allpass filter with a given + coefficient. Another method is provided to create a DC blocking + filter. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "PoleZero.h" + +namespace stk { + +PoleZero :: PoleZero() +{ + // Default setting for pass-through. + b_.resize( 2, 0.0 ); + a_.resize( 2, 0.0 ); + b_[0] = 1.0; + a_[0] = 1.0; + inputs_.resize( 2, 1, 0.0 ); + outputs_.resize( 2, 1, 0.0 ); +} + +PoleZero :: ~PoleZero() +{ +} + +void PoleZero :: setCoefficients( StkFloat b0, StkFloat b1, StkFloat a1, bool clearState ) +{ + if ( std::abs( a1 ) >= 1.0 ) { + oStream_ << "PoleZero::setCoefficients: a1 argument (" << a1 << ") should be less than 1.0!"; + handleError( StkError::WARNING ); return; + } + + b_[0] = b0; + b_[1] = b1; + a_[1] = a1; + + if ( clearState ) this->clear(); +} + +void PoleZero :: setAllpass( StkFloat coefficient ) +{ + if ( std::abs( coefficient ) >= 1.0 ) { + oStream_ << "PoleZero::setAllpass: argument (" << coefficient << ") makes filter unstable!"; + handleError( StkError::WARNING ); return; + } + + b_[0] = coefficient; + b_[1] = 1.0; + a_[0] = 1.0; // just in case + a_[1] = coefficient; +} + +void PoleZero :: setBlockZero( StkFloat thePole ) +{ + if ( std::abs( thePole ) >= 1.0 ) { + oStream_ << "PoleZero::setBlockZero: argument (" << thePole << ") makes filter unstable!"; + handleError( StkError::WARNING ); return; + } + + b_[0] = 1.0; + b_[1] = -1.0; + a_[0] = 1.0; // just in case + a_[1] = -thePole; +} + +} // stk namespace diff --git a/src/stk/src/Recorder.cpp b/src/stk/src/Recorder.cpp new file mode 100644 index 00000000..8dc0eec2 --- /dev/null +++ b/src/stk/src/Recorder.cpp @@ -0,0 +1,345 @@ +/***************************************************/ +/*! \class Recorder + \brief A recorder / flute physical model. + + This class implements a physical model of a recorder / + flute instrument, based on the paper "Sound production + in recorderlike instruments. II. A simulation model." + by M.P. Verge, A. Hirschberg and R. Causse, Journal of + the Acoustical Society of America, 1997. + + Control Change Numbers: + - Softness = 2 + - Noise Gain = 4 + - Noise Cutoff = 16 + - Vibrato Frequency = 11 + - Vibrato Gain = 1 + - Breath Pressure = 128 + + by Mathias Bredholt, McGill University. + Formatted for STK by Gary Scavone, 2021. +*/ +/***************************************************/ + +#include "Recorder.h" +#include "SKINImsg.h" + +namespace stk { + +// Air constants +const stk::StkFloat rho = 1.2041; // density of air +const stk::StkFloat c0 = 343.21; // speed of sound in air + +// Flute constants +const stk::StkFloat lc = 0.02; // length of flue canal +const stk::StkFloat h = 0.001; // height of flue exit +const stk::StkFloat H = 0.02; // pipe diameter +const stk::StkFloat W = 4 * h; // width of mouth +const stk::StkFloat Sp = H * H; // cross-section of pipe +const stk::StkFloat Sm = W * H; // cross-section of mouth +const stk::StkFloat din = 0.0030; // end correction +const stk::StkFloat dout = 0.0063; // end correction +const stk::StkFloat dm = din + dout; // end correction of mouth +const stk::StkFloat dd = 0.0035; // acoustic distance between Q1 and Q2 +const stk::StkFloat rp = sqrt(Sp / stk::PI); +const stk::StkFloat b = 0.4 * h; // jet width + +// Calculation coefficients +const stk::StkFloat b2 = Sp / (rho * c0); + +Recorder :: Recorder() +{ + vibratoGain_ = 0.0; + noiseGain_ = 0.2; + breathCutoff_ = 0.0; + outputGain_ = 0.0; + psi_ = 1.0; + poutL_ = 0; + pout_ = 0; + poutm1_ = 0; + poutm2_ = 0; + pin_ = 0; + pinm1_ = 0; + pinm2_ = 0; + Uj_ = 0; + Ujm1_ = 0; + Qj_ = 0; + Qjm1_ = 0; + Qjm2_ = 0; + Q1_ = 0; + Q1m1_ = 0; + Q1m2_ = 0; + Qp_ = 0; + Qpm1_ = 0; + pm_ = 0; + + pinDelay_.tick( 0 ); + poutDelay_.tick( 0 ); + jetDelay_.tick( 0 ); + + jetDelay_.setDelay(200); + vibrato_.setFrequency(4); + + // Calculation coefficients ... would need to be recalculated if sample rate changes + StkFloat T = 1.0 / Stk::sampleRate(); + b1 = rho / (4.0 * PI * c0 * T * T); + b3 = dm * Sp / (T * Sm * c0); + b4 = rho * dout / (Sm * T); + + // Radiation loss filter + StkFloat A = rp * rp / (4 * c0 * c0 * T * T); + StkFloat B = 0.82 * rp / (c0*T); + StkFloat b_rad[3] = { 1 + A - B, B - 2 * A, A }; + StkFloat a_rad[3] = { A - B - 1, B - 2 * A, A }; + std::vector b_coeffs( &b_rad[0], &b_rad[0]+3 ); + std::vector a_coeffs( &a_rad[0], &a_rad[0]+3 ); + radiation_filter_.setCoefficients(b_coeffs, a_coeffs); + + // Visco-thermal loss filter + StkFloat b_visco[4] = { 0.83820223947141, -0.16888603248373, -0.64759781930259, 0.07424498608506 }; + StkFloat a_visco[4] = { 1.0, -0.33623476246554, -0.71257915055968, 0.14508304017256 }; + b_coeffs.clear(); + b_coeffs.assign( &b_visco[0], &b_visco[0]+4 ); + a_coeffs.clear(); + a_coeffs.assign( &a_visco[0], &a_visco[0]+4 ); + visco_in_filter_.setCoefficients(b_coeffs, a_coeffs); + visco_out_filter_.setCoefficients(b_coeffs, a_coeffs); + + setBreathCutoff( 500 ); + setFrequency( 880 ); +} + +Recorder :: ~Recorder( void ) +{ +} + +void Recorder :: clear( void ) +{ + pinDelay_.clear(); + poutDelay_.clear(); + jetDelay_.clear(); + radiation_filter_.clear(); + visco_in_filter_.clear(); + visco_out_filter_.clear(); + turbFilter_.clear(); +} + +void Recorder :: setFrequency( StkFloat val ) +{ +#if defined(_STK_DEBUG_) + if ( val <= 0.0 ) { + oStream_ << "Recorder::setFrequency: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat M = Stk::sampleRate() / val - 4 - 3; + pinDelay_.setDelay( M ); + poutDelay_.setDelay( M ); +} + +void Recorder :: setBlowPressure( StkFloat val ) +{ + maxPressure_ = val; +} + +void Recorder :: setVibratoGain( StkFloat val ) +{ + vibratoGain_ = val; +} + +void Recorder :: setVibratoFrequency( StkFloat val ) +{ + vibrato_.setFrequency( val ); +} + +void Recorder :: setNoiseGain( StkFloat val ) +{ + noiseGain_ = val; +} + +void Recorder :: setBreathCutoff( StkFloat val ) +{ + // The gain of this filter is quite high + breathCutoff_ = val; + StkFloat Q = 0.99; + StkFloat r = 2.0 * sin(PI * val / sampleRate()); + StkFloat q = 1.0 - r * Q; + StkFloat as[3] = { 1.0, r * r - q - 1, q }; + std::vector b_turb(1, r*r); + std::vector a_turb( &as[0], &as[0]+3 ); + turbFilter_.setCoefficients(b_turb, a_turb); +} + +void Recorder :: setSoftness( StkFloat val ) +{ + psi_ = val; +} + +void Recorder :: startBlowing( StkFloat amplitude, StkFloat rate ) +{ + if ( amplitude <= 0.0 || rate <= 0.0 ) { + oStream_ << "Recorder::startBlowing: one or more arguments is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } + + adsr_.setAttackRate( rate ); + //maxPressure_ = amplitude / (StkFloat) 0.8; + maxPressure_ = 35 * amplitude; + adsr_.keyOn(); +} + +void Recorder :: stopBlowing( StkFloat rate ) +{ + if ( rate <= 0.0 ) { + oStream_ << "Recorder::stopBlowing: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } + + adsr_.setReleaseRate( rate ); + adsr_.keyOff(); +} + +void Recorder :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + this->setFrequency( frequency ); + this->startBlowing( 1.1 + (amplitude * 0.20), amplitude * 0.02 ); + outputGain_ = amplitude / 40.0; +} + +void Recorder :: noteOff( StkFloat amplitude ) +{ + this->stopBlowing( amplitude * 0.02 ); +} + +void Recorder :: controlChange( int number, StkFloat value ) +{ +#if defined(_STK_DEBUG_) + if ( Stk::inRange( value, 0.0, 128.0 ) == false ) { + oStream_ << "Recorder::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if (number == 2) // 2 + psi_ = 2.0 * normalizedValue; + else if (number == 16) + setBreathCutoff( normalizedValue * 2000 ); + else if (number == __SK_NoiseLevel_) // 4 + noiseGain_ = normalizedValue; + else if (number == __SK_ModFrequency_) // 11 + vibrato_.setFrequency( normalizedValue * 12.0); + else if (number == __SK_ModWheel_) // 1 + vibratoGain_ = ( normalizedValue * 0.4 ); + else if (number == __SK_AfterTouch_Cont_) // 128 + maxPressure_ = 35.0 * normalizedValue; +#if defined(_STK_DEBUG_) + else { + oStream_ << "Recorder::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +StkFloat Recorder::tick( unsigned int ) +{ + // Read in from delay lines + pinm2_ = pinm1_; + pinm1_ = pin_; + pin_ = pinDelay_.lastOut(); + + poutm2_ = poutm1_; + poutm1_ = pout_; + poutL_ = poutDelay_.lastOut(); + + // Filter wave components for visco-thermal losses + pin_ = visco_in_filter_.tick(pin_); + poutL_ = visco_out_filter_.tick(poutL_); + + // Get input blow pressure + StkFloat pf = maxPressure_ * adsr_.tick() * (vibrato_.tick() * vibratoGain_ + (1 - vibratoGain_)); + + StkFloat T = 1.0 / sampleRate(); + + // Jet velocity at flue exit + Ujm1_ = Uj_; + Uj_ = Ujm1_ + T / (rho * lc) * (pf - pm_ - 0.5 * rho * Ujm1_ * Ujm1_); + + // Jet flow at flue exit + Qjm2_ = Qjm1_; + Qjm1_ = Qj_; + Qj_ = h * H * Uj_; + + // Jet drive + StkFloat Uj_steady = fmax(sqrt(2 * pf / rho), 0.1); + StkFloat fc_jet = 0.36 / W * Uj_steady; + StkFloat g_jet = 0.002004 * exp(-0.06046 * Uj_steady); + StkFloat r_jet = 0.95 - Uj_steady * 0.015; + StkFloat b0_jet = g_jet * (1 - r_jet * r_jet) / 2; + + // Calculate coefficients for resonant filter + StkFloat b_jet[3] = { b0_jet, 0, -b0_jet }; + StkFloat a_jet[3] = { 1, -2 * r_jet * cos(2 * PI * fc_jet * T), r_jet * r_jet }; + std::vector b_jetcoeffs( &b_jet[0], &b_jet[0]+3 ); + std::vector a_jetcoeffs( &a_jet[0], &a_jet[0]+3 ); + jetFilter_.setCoefficients( b_jetcoeffs, a_jetcoeffs ); + StkFloat eta = jetFilter_.tick(jetDelay_.lastOut()); + + // Calculate flow source Q1 + Q1m1_ = Q1_; + Q1_ = b * H * Uj_ * (1 + tanh(eta / (psi_ * b))); + + // Calculate pressure pulse modeling the jet drive + StkFloat pjd = -rho * dd / Sm * (Q1_ - Q1m1_) / T; + + // Vortex shedding + int Qp_sign = 0; + if (Qp_ < 0) Qp_sign = -1; + else if (Qp_ > 0) Qp_sign = 1; + + StkFloat pa = -0.5 * rho * (Qp_ / (0.6 * Sm)) * (Qp_ / (0.6 * Sm)) * Qp_sign; + + // Turbulence + StkFloat pt = turbFilter_.tick(noiseGain_ * turb_.tick() * 0.5 * rho * Uj_ * Uj_); + + // Pressure pulse delta p + StkFloat dp = pjd + pa + pt; + + // Calculate outgoing pressure pout + pout_ = ((b3 - b1 * b2 - 1) * pin_ + + (2 * b1 * b2 - b3) * (pinm1_ - poutm1_) + + b1 * b2 * (poutm2_ - pinm2_) - + b1 * (Qj_ - 2 * Qjm1_ + Qjm2_) + + b4 * (Qj_ - Qjm1_) + dp) / (1 - b1 * b2 + b3); + + // Flow in the pipe + Qpm1_ = Qp_; + Qp_ = Sp / (rho * c0) * (pout_ - pin_); + + // Mouth pressure + pm_ = pout_ + pin_ - dp + rho * din / Sm * (Qp_ - Qpm1_)/T; + + // Calculate transverse acoustic velocity + StkFloat Q1d = Q1_ - 0.5 * b * H * Uj_; + StkFloat Vac = 2.0 / PI * Qp_ / Sm - 0.38 * Q1d / Sm; + jetDelay_.tick(Vac); + + // Calculate new jet delay line length + //jet_.setDelay(fmin(W / (0.6 * Uj_steady) * sampleRate(), 200.0)); + jetDelay_.setDelay(fmin(W / (0.6 * Uj_steady * T), 200.0)); + + // Radiation loss filtering + StkFloat pin_L = radiation_filter_.tick(poutL_); + + // Write to delay lines + poutDelay_.tick(pout_); + pinDelay_.tick(pin_L); + + lastFrame_[0] = outputGain_ * (pout_ + pin_); + return lastFrame_[0]; + + //return (pout_0 + pin_0) * 0.01; +} + +} // stk namespace diff --git a/src/stk/src/Resonate.cpp b/src/stk/src/Resonate.cpp new file mode 100644 index 00000000..3dc42fd4 --- /dev/null +++ b/src/stk/src/Resonate.cpp @@ -0,0 +1,113 @@ +/***************************************************/ +/*! \class Resonate + \brief STK noise driven formant filter. + + This instrument contains a noise source, which + excites a biquad resonance filter, with volume + controlled by an ADSR. + + Control Change Numbers: + - Resonance Frequency (0-Nyquist) = 2 + - Pole Radii = 4 + - Notch Frequency (0-Nyquist) = 11 + - Zero Radii = 1 + - Envelope Gain = 128 + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Resonate.h" +#include "SKINImsg.h" + +namespace stk { + +Resonate :: Resonate( void ) +{ + poleFrequency_ = 4000.0; + poleRadius_ = 0.95; + // Set the filter parameters. + filter_.setResonance( poleFrequency_, poleRadius_, true ); + zeroFrequency_ = 0.0; + zeroRadius_ = 0.0; +} + +Resonate :: ~Resonate( void ) +{ +} + +void Resonate :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + adsr_.setTarget( amplitude ); + this->keyOn(); + this->setResonance( frequency, poleRadius_ ); +} + +void Resonate :: noteOff( StkFloat amplitude ) +{ + this->keyOff(); +} + +void Resonate :: setResonance( StkFloat frequency, StkFloat radius ) +{ + if ( frequency < 0.0 ) { + oStream_ << "Resonate::setResonance: frequency parameter is less than zero!"; + handleError( StkError::WARNING ); return; + } + + if ( radius < 0.0 || radius >= 1.0 ) { + oStream_ << "Resonate::setResonance: radius parameter is out of range!"; + handleError( StkError::WARNING ); return; + } + + poleFrequency_ = frequency; + poleRadius_ = radius; + filter_.setResonance( poleFrequency_, poleRadius_, true ); +} + +void Resonate :: setNotch( StkFloat frequency, StkFloat radius ) +{ + if ( frequency < 0.0 ) { + oStream_ << "Resonate::setNotch: frequency parameter is less than zero ... setting to 0.0!"; + handleError( StkError::WARNING ); return; + } + + if ( radius < 0.0 ) { + oStream_ << "Resonate::setNotch: radius parameter is less than 0.0!"; + handleError( StkError::WARNING ); return; + } + + zeroFrequency_ = frequency; + zeroRadius_ = radius; + filter_.setNotch( zeroFrequency_, zeroRadius_ ); +} + +void Resonate :: controlChange( int number, StkFloat value ) +{ +#if defined(_STK_DEBUG_) + if ( Stk::inRange( value, 0.0, 128.0 ) == false ) { + oStream_ << "Resonate::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if (number == 2) // 2 + setResonance( normalizedValue * Stk::sampleRate() * 0.5, poleRadius_ ); + else if (number == 4) // 4 + setResonance( poleFrequency_, normalizedValue * 0.9999 ); + else if (number == 11) // 11 + this->setNotch( normalizedValue * Stk::sampleRate() * 0.5, zeroRadius_ ); + else if (number == 1) + this->setNotch( zeroFrequency_, normalizedValue ); + else if (number == __SK_AfterTouch_Cont_) // 128 + adsr_.setTarget( normalizedValue ); +#if defined(_STK_DEBUG_) + else { + oStream_ << "Resonate::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/Rhodey.cpp b/src/stk/src/Rhodey.cpp new file mode 100644 index 00000000..f1a415ce --- /dev/null +++ b/src/stk/src/Rhodey.cpp @@ -0,0 +1,92 @@ +/***************************************************/ +/*! \class Rhodey + \brief STK Fender Rhodes-like electric piano FM + synthesis instrument. + + This class implements two simple FM Pairs + summed together, also referred to as algorithm + 5 of the TX81Z. + + \code + Algorithm 5 is : 4->3--\ + + --> Out + 2->1--/ + \endcode + + Control Change Numbers: + - Modulator Index One = 2 + - Crossfade of Outputs = 4 + - LFO Speed = 11 + - LFO Depth = 1 + - ADSR 2 & 4 Target = 128 + + The basic Chowning/Stanford FM patent expired + in 1995, but there exist follow-on patents, + mostly assigned to Yamaha. If you are of the + type who should worry about this (making + money) worry away. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Rhodey.h" + +namespace stk { + +Rhodey :: Rhodey( void ) + : FM() +{ + // Concatenate the STK rawwave path to the rawwave files + for ( unsigned int i=0; i<3; i++ ) + waves_[i] = new FileLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + waves_[3] = new FileLoop( (Stk::rawwavePath() + "fwavblnk.raw").c_str(), true ); + + this->setRatio(0, 1.0); + this->setRatio(1, 0.5); + this->setRatio(2, 1.0); + this->setRatio(3, 15.0); + + gains_[0] = fmGains_[99]; + gains_[1] = fmGains_[90]; + gains_[2] = fmGains_[99]; + gains_[3] = fmGains_[67]; + + adsr_[0]->setAllTimes( 0.001, 1.50, 0.0, 0.04); + adsr_[1]->setAllTimes( 0.001, 1.50, 0.0, 0.04); + adsr_[2]->setAllTimes( 0.001, 1.00, 0.0, 0.04); + adsr_[3]->setAllTimes( 0.001, 0.25, 0.0, 0.04); + + twozero_.setGain( 1.0 ); +} + +Rhodey :: ~Rhodey( void ) +{ +} + +void Rhodey :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "Rhodey::setFrequency: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + baseFrequency_ = frequency * 2.0; + + for (unsigned int i=0; isetFrequency( baseFrequency_ * ratios_[i] ); +} + +void Rhodey :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + gains_[0] = amplitude * fmGains_[99]; + gains_[1] = amplitude * fmGains_[90]; + gains_[2] = amplitude * fmGains_[99]; + gains_[3] = amplitude * fmGains_[67]; + this->setFrequency( frequency ); + this->keyOn(); +} + +} // stk namespace diff --git a/src/stk/src/Sampler.cpp b/src/stk/src/Sampler.cpp new file mode 100644 index 00000000..f373b1f6 --- /dev/null +++ b/src/stk/src/Sampler.cpp @@ -0,0 +1,53 @@ +/***************************************************/ +/*! \class Sampler + \brief STK sampling synthesis abstract base class. + + This instrument provides an ADSR envelope, a one-pole filter, and + structures for an arbitrary number of attack and looped files. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Sampler.h" + +namespace stk { + +Sampler :: Sampler( void ) +{ + // We don't make the waves here yet, because + // we don't know what they will be. + baseFrequency_ = 440.0; + attackGain_ = 0.25; + loopGain_ = 0.25; +} + +Sampler :: ~Sampler( void ) +{ + unsigned int i; + for ( i=0; ireset(); + + // Start the envelope. + adsr_.keyOn(); + +} + +void Sampler :: keyOff( void ) +{ + adsr_.keyOff(); +} + +void Sampler :: noteOff( StkFloat amplitude ) +{ + this->keyOff(); +} + +} // stk namespace diff --git a/src/stk/src/Saxofony.cpp b/src/stk/src/Saxofony.cpp new file mode 100644 index 00000000..0877a547 --- /dev/null +++ b/src/stk/src/Saxofony.cpp @@ -0,0 +1,177 @@ +/***************************************************/ +/*! \class Saxofony + \brief STK faux conical bore reed instrument class. + + This class implements a "hybrid" digital + waveguide instrument that can generate a + variety of wind-like sounds. It has also been + referred to as the "blowed string" model. The + waveguide section is essentially that of a + string, with one rigid and one lossy + termination. The non-linear function is a + reed table. The string can be "blown" at any + point between the terminations, though just as + with strings, it is impossible to excite the + system at either end. If the excitation is + placed at the string mid-point, the sound is + that of a clarinet. At points closer to the + "bridge", the sound is closer to that of a + saxophone. See Scavone (2002) for more details. + + This is a digital waveguide model, making its + use possibly subject to patents held by Stanford + University, Yamaha, and others. + + Control Change Numbers: + - Reed Stiffness = 2 + - Reed Aperture = 26 + - Noise Gain = 4 + - Blow Position = 11 + - Vibrato Frequency = 29 + - Vibrato Gain = 1 + - Breath Pressure = 128 + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Saxofony.h" +#include "SKINImsg.h" + +namespace stk { + +Saxofony :: Saxofony( StkFloat lowestFrequency ) +{ + if ( lowestFrequency <= 0.0 ) { + oStream_ << "Saxofony::Saxofony: argument is less than or equal to zero!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + unsigned long nDelays = (unsigned long) ( Stk::sampleRate() / lowestFrequency ); + delays_[0].setMaximumDelay( nDelays + 1 ); + delays_[1].setMaximumDelay( nDelays + 1 ); + + // Initialize blowing position to 0.2 of length. + position_ = 0.2; + + reedTable_.setOffset( 0.7 ); + reedTable_.setSlope( 0.3 ); + + vibrato_.setFrequency( 5.735 ); + + outputGain_ = 0.3; + noiseGain_ = 0.2; + vibratoGain_ = 0.1; + + this->setFrequency( 220.0 ); + this->clear(); +} + +Saxofony :: ~Saxofony( void ) +{ +} + +void Saxofony :: clear( void ) +{ + delays_[0].clear(); + delays_[1].clear(); + filter_.clear(); +} + +void Saxofony :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "Saxofony::setFrequency: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + // Account for filter delay and one sample "lastOut" delay. + StkFloat delay = ( Stk::sampleRate() / frequency ) - filter_.phaseDelay( frequency ) - 1.0; + delays_[0].setDelay( (1.0-position_) * delay ); + delays_[1].setDelay( position_ * delay ); +} + +void Saxofony :: setBlowPosition( StkFloat position ) +{ + if ( position_ == position ) return; + + if ( position < 0.0 ) position_ = 0.0; + else if ( position > 1.0 ) position_ = 1.0; + else position_ = position; + + StkFloat totalDelay = delays_[0].getDelay(); + totalDelay += delays_[1].getDelay(); + + delays_[0].setDelay( (1.0-position_) * totalDelay ); + delays_[1].setDelay( position_ * totalDelay ); +} + +void Saxofony :: startBlowing( StkFloat amplitude, StkFloat rate ) +{ + if ( amplitude <= 0.0 || rate <= 0.0 ) { + oStream_ << "Saxofony::startBlowing: one or more arguments is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } + + envelope_.setRate( rate ); + envelope_.setTarget( amplitude ); +} + +void Saxofony :: stopBlowing( StkFloat rate ) +{ + if ( rate <= 0.0 ) { + oStream_ << "Saxofony::stopBlowing: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } + + envelope_.setRate( rate ); + envelope_.setTarget( 0.0 ); +} + +void Saxofony :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + this->setFrequency( frequency ); + this->startBlowing( 0.55 + (amplitude * 0.30), amplitude * 0.005 ); + outputGain_ = amplitude + 0.001; +} + +void Saxofony :: noteOff( StkFloat amplitude ) +{ + this->stopBlowing( amplitude * 0.01 ); +} + +void Saxofony :: controlChange( int number, StkFloat value ) +{ +#if defined(_STK_DEBUG_) + if ( Stk::inRange( value, 0.0, 128.0 ) == false ) { + oStream_ << "Saxofony::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if (number == __SK_ReedStiffness_) // 2 + reedTable_.setSlope( 0.1 + (0.4 * normalizedValue) ); + else if (number == __SK_NoiseLevel_) // 4 + noiseGain_ = ( normalizedValue * 0.4 ); + else if (number == 29) // 29 + vibrato_.setFrequency( normalizedValue * 12.0 ); + else if (number == __SK_ModWheel_) // 1 + vibratoGain_ = ( normalizedValue * 0.5 ); + else if (number == __SK_AfterTouch_Cont_) // 128 + envelope_.setValue( normalizedValue ); + else if (number == 11) // 11 + this->setBlowPosition( normalizedValue ); + else if (number == 26) // reed table offset + reedTable_.setOffset(0.4 + ( normalizedValue * 0.6)); +#if defined(_STK_DEBUG_) + else { + oStream_ << "Saxofony::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/Shakers.cpp b/src/stk/src/Shakers.cpp new file mode 100644 index 00000000..48ced914 --- /dev/null +++ b/src/stk/src/Shakers.cpp @@ -0,0 +1,729 @@ +/***************************************************/ +/*! \class Shakers + \brief PhISEM and PhOLIES class. + + PhISEM (Physically Informed Stochastic Event Modeling) is an + algorithmic approach for simulating collisions of multiple + independent sound producing objects. This class is a meta-model + that can simulate a Maraca, Sekere, Cabasa, Bamboo Wind Chimes, + Water Drops, Tambourine, Sleighbells, and a Guiro. + + PhOLIES (Physically-Oriented Library of Imitated Environmental + Sounds) is a similar approach for the synthesis of environmental + sounds. This class implements simulations of breaking sticks, + crunchy snow (or not), a wrench, sandpaper, and more. + + Control Change Numbers: + - Shake Energy = 2 + - System Decay = 4 + - Number Of Objects = 11 + - Resonance Frequency = 1 + - Shake Energy = 128 + - Instrument Selection = 1071 + - Maraca = 0 + - Cabasa = 1 + - Sekere = 2 + - Tambourine = 3 + - Sleigh Bells = 4 + - Bamboo Chimes = 5 + - Sand Paper = 6 + - Coke Can = 7 + - Sticks = 8 + - Crunch = 9 + - Big Rocks = 10 + - Little Rocks = 11 + - Next Mug = 12 + - Penny + Mug = 13 + - Nickle + Mug = 14 + - Dime + Mug = 15 + - Quarter + Mug = 16 + - Franc + Mug = 17 + - Peso + Mug = 18 + - Guiro = 19 + - Wrench = 20 + - Water Drops = 21 + - Tuned Bamboo Chimes = 22 + + by Perry R. Cook with updates by Gary Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Shakers.h" +#include "SKINImsg.h" + +namespace stk { + +// Maraca +const StkFloat MARACA_SOUND_DECAY = 0.95; +const StkFloat MARACA_SYSTEM_DECAY = 0.999; +const StkFloat MARACA_GAIN = 4.0; +const StkFloat MARACA_NUM_BEANS = 25; +const int MARACA_RESONANCES = 1; +const StkFloat MARACA_FREQUENCIES[MARACA_RESONANCES] = { 3200 }; +const StkFloat MARACA_RADII[MARACA_RESONANCES] = { 0.96 }; +const StkFloat MARACA_GAINS[MARACA_RESONANCES] = { 1.0 }; + +// Cabasa +const StkFloat CABASA_SOUND_DECAY = 0.96; +const StkFloat CABASA_SYSTEM_DECAY = 0.997; +const StkFloat CABASA_GAIN = 8.0; +const StkFloat CABASA_NUM_BEADS = 512; +const int CABASA_RESONANCES = 1; +const StkFloat CABASA_FREQUENCIES[CABASA_RESONANCES] = { 3000 }; +const StkFloat CABASA_RADII[CABASA_RESONANCES] = { 0.7 }; +const StkFloat CABASA_GAINS[CABASA_RESONANCES] = { 1.0 }; + +// Sekere +const StkFloat SEKERE_SOUND_DECAY = 0.96; +const StkFloat SEKERE_SYSTEM_DECAY = 0.999; +const StkFloat SEKERE_GAIN = 4.0; +const StkFloat SEKERE_NUM_BEANS = 64; +const int SEKERE_RESONANCES = 1; +const StkFloat SEKERE_FREQUENCIES[SEKERE_RESONANCES] = { 5500 }; +const StkFloat SEKERE_RADII[SEKERE_RESONANCES] = { 0.6 }; +const StkFloat SEKERE_GAINS[SEKERE_RESONANCES] = { 1.0 }; + +// Bamboo Wind Chimes +const StkFloat BAMBOO_SOUND_DECAY = 0.9; +const StkFloat BAMBOO_SYSTEM_DECAY = 0.9999; +const StkFloat BAMBOO_GAIN = 0.4; +const StkFloat BAMBOO_NUM_TUBES = 1.2; +const int BAMBOO_RESONANCES = 3; +const StkFloat BAMBOO_FREQUENCIES[BAMBOO_RESONANCES] = { 2800, 0.8 * 2800.0, 1.2 * 2800.0 }; +const StkFloat BAMBOO_RADII[BAMBOO_RESONANCES] = { 0.995, 0.995, 0.995 }; +const StkFloat BAMBOO_GAINS[BAMBOO_RESONANCES] = { 1.0, 1.0, 1.0 }; + +// Tambourine +const StkFloat TAMBOURINE_SOUND_DECAY = 0.95; +const StkFloat TAMBOURINE_SYSTEM_DECAY = 0.9985; +const StkFloat TAMBOURINE_GAIN = 1.0; +const StkFloat TAMBOURINE_NUM_TIMBRELS = 32; +const int TAMBOURINE_RESONANCES = 3; // Fixed shell + 2 moving cymbal resonances +const StkFloat TAMBOURINE_FREQUENCIES[TAMBOURINE_RESONANCES] = { 2300, 5600, 8100 }; +const StkFloat TAMBOURINE_RADII[TAMBOURINE_RESONANCES] = { 0.96, 0.99, 0.99 }; +const StkFloat TAMBOURINE_GAINS[TAMBOURINE_RESONANCES] = { 0.1, 0.8, 1.0 }; + +// Sleighbells +const StkFloat SLEIGH_SOUND_DECAY = 0.97; +const StkFloat SLEIGH_SYSTEM_DECAY = 0.9994; +const StkFloat SLEIGH_GAIN = 1.0; +const StkFloat SLEIGH_NUM_BELLS = 32; +const int SLEIGH_RESONANCES = 5; +const StkFloat SLEIGH_FREQUENCIES[SLEIGH_RESONANCES] = { 2500, 5300, 6500, 8300, 9800 }; +const StkFloat SLEIGH_RADII[SLEIGH_RESONANCES] = { 0.99, 0.99, 0.99, 0.99, 0.99 }; +const StkFloat SLEIGH_GAINS[SLEIGH_RESONANCES] = { 1.0, 1.0, 1.0, 0.5, 0.3 }; + +// Sandpaper +const StkFloat SANDPAPER_SOUND_DECAY = 0.999; +const StkFloat SANDPAPER_SYSTEM_DECAY = 0.999; +const StkFloat SANDPAPER_GAIN = 0.5; +const StkFloat SANDPAPER_NUM_GRAINS = 128; +const int SANDPAPER_RESONANCES = 1; +const StkFloat SANDPAPER_FREQUENCIES[SANDPAPER_RESONANCES] = { 4500 }; +const StkFloat SANDPAPER_RADII[SANDPAPER_RESONANCES] = { 0.6 }; +const StkFloat SANDPAPER_GAINS[SANDPAPER_RESONANCES] = { 1.0 }; + +// Cokecan +const StkFloat COKECAN_SOUND_DECAY = 0.97; +const StkFloat COKECAN_SYSTEM_DECAY = 0.999; +const StkFloat COKECAN_GAIN = 0.5; +const StkFloat COKECAN_NUM_PARTS = 48; +const int COKECAN_RESONANCES = 5; // Helmholtz + 4 metal resonances +const StkFloat COKECAN_FREQUENCIES[COKECAN_RESONANCES] = { 370, 1025, 1424, 2149, 3596 }; +const StkFloat COKECAN_RADII[COKECAN_RESONANCES] = { 0.99, 0.992, 0.992, 0.992, 0.992 }; +const StkFloat COKECAN_GAINS[COKECAN_RESONANCES] = { 1.0, 1.8, 1.8, 1.8, 1.8 }; + +// Tuned Bamboo Wind Chimes (Angklung) +const StkFloat ANGKLUNG_SOUND_DECAY = 0.95; +const StkFloat ANGKLUNG_SYSTEM_DECAY = 0.9999; +const StkFloat ANGKLUNG_GAIN = 0.5; +const StkFloat ANGKLUNG_NUM_TUBES = 1.2; +const int ANGKLUNG_RESONANCES = 7; +const StkFloat ANGKLUNG_FREQUENCIES[ANGKLUNG_RESONANCES] = { 1046.6, 1174.8, 1397.0, 1568, 1760, 2093.3, 2350 }; +const StkFloat ANGKLUNG_RADII[ANGKLUNG_RESONANCES] = { 0.996, 0.996, 0.996, 0.996, 0.996, 0.996, 0.996 }; +const StkFloat ANGKLUNG_GAINS[ANGKLUNG_RESONANCES] = { 1.0, 1.0, 1.0, 1.0, 1.0, 1.0, 1.0 }; + +// Guiro +const StkFloat GUIRO_SOUND_DECAY = 0.95; +const StkFloat GUIRO_GAIN = 0.4; +const StkFloat GUIRO_NUM_PARTS = 128; +const int GUIRO_RESONANCES = 2; +const StkFloat GUIRO_FREQUENCIES[GUIRO_RESONANCES] = { 2500, 4000 }; +const StkFloat GUIRO_RADII[GUIRO_RESONANCES] = { 0.97, 0.97 }; +const StkFloat GUIRO_GAINS[GUIRO_RESONANCES] = { 1.0, 1.0 }; + +// Wrench +const StkFloat WRENCH_SOUND_DECAY = 0.95; +const StkFloat WRENCH_GAIN = 0.4; +const StkFloat WRENCH_NUM_PARTS = 128; +const int WRENCH_RESONANCES = 2; +const StkFloat WRENCH_FREQUENCIES[WRENCH_RESONANCES] = { 3200, 8000 }; +const StkFloat WRENCH_RADII[WRENCH_RESONANCES] = { 0.99, 0.992 }; +const StkFloat WRENCH_GAINS[WRENCH_RESONANCES] = { 1.0, 1.0 }; + +// Water Drops +const StkFloat WATER_SOUND_DECAY = 0.95; +const StkFloat WATER_SYSTEM_DECAY = 0.996; +const StkFloat WATER_GAIN = 1.0; +const StkFloat WATER_NUM_SOURCES = 10; +const int WATER_RESONANCES = 3; +const StkFloat WATER_FREQUENCIES[WATER_RESONANCES] = { 450, 600, 750 }; +const StkFloat WATER_RADII[WATER_RESONANCES] = { 0.9985, 0.9985, 0.9985 }; +const StkFloat WATER_GAINS[WATER_RESONANCES] = { 1.0, 1.0, 1.0 }; + +// PhOLIES (Physically-Oriented Library of Imitated Environmental +// Sounds), Perry Cook, 1997-8 + +// Stix1 +const StkFloat STIX1_SOUND_DECAY = 0.96; +const StkFloat STIX1_SYSTEM_DECAY = 0.998; +const StkFloat STIX1_GAIN = 6.0; +const StkFloat STIX1_NUM_BEANS = 2; +const int STIX1_RESONANCES = 1; +const StkFloat STIX1_FREQUENCIES[STIX1_RESONANCES] = { 5500 }; +const StkFloat STIX1_RADII[STIX1_RESONANCES] = { 0.6 }; +const StkFloat STIX1_GAINS[STIX1_RESONANCES] = { 1.0 }; + +// Crunch1 +const StkFloat CRUNCH1_SOUND_DECAY = 0.95; +const StkFloat CRUNCH1_SYSTEM_DECAY = 0.99806; +const StkFloat CRUNCH1_GAIN = 4.0; +const StkFloat CRUNCH1_NUM_BEADS = 7; +const int CRUNCH1_RESONANCES = 1; +const StkFloat CRUNCH1_FREQUENCIES[CRUNCH1_RESONANCES] = { 800 }; +const StkFloat CRUNCH1_RADII[CRUNCH1_RESONANCES] = { 0.95 }; +const StkFloat CRUNCH1_GAINS[CRUNCH1_RESONANCES] = { 1.0 }; + +// Nextmug + Coins +const StkFloat NEXTMUG_SOUND_DECAY = 0.97; +const StkFloat NEXTMUG_SYSTEM_DECAY = 0.9995; +const StkFloat NEXTMUG_GAIN = 0.8; +const StkFloat NEXTMUG_NUM_PARTS = 3; +const int NEXTMUG_RESONANCES = 4; +const StkFloat NEXTMUG_FREQUENCIES[NEXTMUG_RESONANCES] = { 2123, 4518, 8856, 10753 }; +const StkFloat NEXTMUG_RADII[NEXTMUG_RESONANCES] = { 0.997, 0.997, 0.997, 0.997 }; +const StkFloat NEXTMUG_GAINS[NEXTMUG_RESONANCES] = { 1.0, 0.8, 0.6, 0.4 }; + +const int COIN_RESONANCES = 3; +const StkFloat PENNY_FREQUENCIES[COIN_RESONANCES] = { 11000, 5200, 3835 }; +const StkFloat PENNY_RADII[COIN_RESONANCES] = { 0.999, 0.999, 0.999 }; +const StkFloat PENNY_GAINS[COIN_RESONANCES] = { 1.0, 0.8, 0.5 }; + +const StkFloat NICKEL_FREQUENCIES[COIN_RESONANCES] = { 5583, 9255, 9805 }; +const StkFloat NICKEL_RADII[COIN_RESONANCES] = { 0.9992, 0.9992, 0.9992 }; +const StkFloat NICKEL_GAINS[COIN_RESONANCES] = { 1.0, 0.8, 0.5 }; + +const StkFloat DIME_FREQUENCIES[COIN_RESONANCES] = { 4450, 4974, 9945 }; +const StkFloat DIME_RADII[COIN_RESONANCES] = { 0.9993, 0.9993, 0.9993 }; +const StkFloat DIME_GAINS[COIN_RESONANCES] = { 1.0, 0.8, 0.5 }; + +const StkFloat QUARTER_FREQUENCIES[COIN_RESONANCES] = { 1708, 8863, 9045 }; +const StkFloat QUARTER_RADII[COIN_RESONANCES] = { 0.9995, 0.9995, 0.9995 }; +const StkFloat QUARTER_GAINS[COIN_RESONANCES] = { 1.0, 0.8, 0.5 }; + +const StkFloat FRANC_FREQUENCIES[COIN_RESONANCES] = { 5583, 11010, 1917 }; +const StkFloat FRANC_RADII[COIN_RESONANCES] = { 0.9995, 0.9995, 0.9995 }; +const StkFloat FRANC_GAINS[COIN_RESONANCES] = { 0.7, 0.4, 0.3 }; + +const StkFloat PESO_FREQUENCIES[COIN_RESONANCES] = { 7250, 8150, 10060 }; +const StkFloat PESO_RADII[COIN_RESONANCES] = { 0.9996, 0.9996, 0.9996 }; +const StkFloat PESO_GAINS[COIN_RESONANCES] = { 1.0, 1.2, 0.7 }; + +// Big Gravel +const StkFloat BIGROCKS_SOUND_DECAY = 0.98; +const StkFloat BIGROCKS_SYSTEM_DECAY = 0.9965; +const StkFloat BIGROCKS_GAIN = 4.0; +const StkFloat BIGROCKS_NUM_PARTS = 23; +const int BIGROCKS_RESONANCES = 1; +const StkFloat BIGROCKS_FREQUENCIES[BIGROCKS_RESONANCES] = { 6460 }; +const StkFloat BIGROCKS_RADII[BIGROCKS_RESONANCES] = { 0.932 }; +const StkFloat BIGROCKS_GAINS[BIGROCKS_RESONANCES] = { 1.0 }; + +// Little Gravel +const StkFloat LITTLEROCKS_SOUND_DECAY = 0.98; +const StkFloat LITTLEROCKS_SYSTEM_DECAY = 0.99586; +const StkFloat LITTLEROCKS_GAIN = 4.0; +const StkFloat LITTLEROCKS_NUM_PARTS = 1600; +const int LITTLEROCKS_RESONANCES = 1; +const StkFloat LITTLEROCKS_FREQUENCIES[LITTLEROCKS_RESONANCES] = { 9000 }; +const StkFloat LITTLEROCKS_RADII[LITTLEROCKS_RESONANCES] = { 0.843 }; +const StkFloat LITTLEROCKS_GAINS[LITTLEROCKS_RESONANCES] = { 1.0 }; + +Shakers :: Shakers( int type ) +{ + shakerType_ = -1; + this->setType( type ); +} + +void Shakers :: setType( int type ) +{ + if ( shakerType_ == type ) return; + varyFactor_ = 0.0; + shakerType_ = type; + if ( type == 1 ) { // Cabasa + nResonances_ = CABASA_RESONANCES; + filters_.resize( nResonances_ ); + baseFrequencies_.resize( nResonances_ ); + baseRadii_.resize( nResonances_ ); + doVaryFrequency_.resize( nResonances_ ); + baseObjects_ = CABASA_NUM_BEADS; + for ( unsigned int i=0; i 11 && type < 19 ) { // Nextmug + nResonances_ = NEXTMUG_RESONANCES; + if ( type > 12 ) // mug + coin + nResonances_ += COIN_RESONANCES; + filters_.resize( nResonances_ ); + baseFrequencies_.resize( nResonances_ ); + baseRadii_.resize( nResonances_ ); + doVaryFrequency_.resize( nResonances_ ); + baseObjects_ = NEXTMUG_NUM_PARTS; + for ( int i=0; isetType( noteNumber ); + + shakeEnergy_ += amplitude * MAX_SHAKE * 0.1; + if ( shakeEnergy_ > MAX_SHAKE ) shakeEnergy_ = MAX_SHAKE; + if ( shakerType_==19 || shakerType_==20 ) ratchetCount_ += 1; +} + +void Shakers :: noteOff( StkFloat amplitude ) +{ + shakeEnergy_ = 0.0; + if ( shakerType_==19 || shakerType_==20 ) ratchetCount_ = 0; +} + +void Shakers :: controlChange( int number, StkFloat value ) +{ +#if defined(_STK_DEBUG_) + if ( Stk::inRange( value, 0.0, 128.0 ) == false ) { + oStream_ << "Shakers::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if ( number == __SK_Breath_ || number == __SK_AfterTouch_Cont_ ) { // 2 or 128 ... energy + if ( shakerType_ == 19 || shakerType_ == 20 ) { + if ( lastRatchetValue_ < 0.0 ) ratchetCount_++; + else ratchetCount_ = (int) fabs(value - lastRatchetValue_); + ratchetDelta_ = baseRatchetDelta_ * ratchetCount_; + lastRatchetValue_ = (int) value; + } + else { + shakeEnergy_ += normalizedValue * MAX_SHAKE * 0.1; + if ( shakeEnergy_ > MAX_SHAKE ) shakeEnergy_ = MAX_SHAKE; + } + } + else if ( number == __SK_ModFrequency_ ) { // 4 ... decay + systemDecay_ = baseDecay_ + ( 2.0 * (normalizedValue - 0.5) * decayScale_ * (1.0 - baseDecay_) ); + } + else if ( number == __SK_FootControl_ ) { // 11 ... number of objects + nObjects_ = (StkFloat) ( 2.0 * normalizedValue * baseObjects_ ) + 1.1; + currentGain_ = log( nObjects_ ) * baseGain_ / nObjects_; + } + else if ( number == __SK_ModWheel_ ) { // 1 ... resonance frequency + for ( unsigned int i=0; isetType( type ); + } +#if defined(_STK_DEBUG_) + else { + oStream_ << "Shakers::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/Simple.cpp b/src/stk/src/Simple.cpp new file mode 100644 index 00000000..69fc83d4 --- /dev/null +++ b/src/stk/src/Simple.cpp @@ -0,0 +1,105 @@ +/***************************************************/ +/*! \class Simple + \brief STK wavetable/noise instrument. + + This class combines a looped wave, a + noise source, a biquad resonance filter, + a one-pole filter, and an ADSR envelope + to create some interesting sounds. + + Control Change Numbers: + - Filter Pole Position = 2 + - Noise/Pitched Cross-Fade = 4 + - Envelope Rate = 11 + - Gain = 128 + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Simple.h" +#include "SKINImsg.h" + +namespace stk { + +Simple :: Simple( void ) +{ + // Concatenate the STK rawwave path to the rawwave file + loop_ = new FileLoop( (Stk::rawwavePath() + "impuls10.raw").c_str(), true ); + + filter_.setPole( 0.5 ); + baseFrequency_ = 440.0; + setFrequency( baseFrequency_ ); + loopGain_ = 0.5; +} + +Simple :: ~Simple( void ) +{ + delete loop_; +} + +void Simple :: keyOn( void ) +{ + adsr_.keyOn(); +} + +void Simple :: keyOff( void ) +{ + adsr_.keyOff(); +} + +void Simple :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + this->keyOn(); + this->setFrequency( frequency ); + filter_.setGain( amplitude ); +} +void Simple :: noteOff( StkFloat amplitude ) +{ + this->keyOff(); +} + +void Simple :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "Simple::setFrequency: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + biquad_.setResonance( frequency, 0.98, true ); + loop_->setFrequency( frequency ); +} + +void Simple :: controlChange( int number, StkFloat value ) +{ +#if defined(_STK_DEBUG_) + if ( Stk::inRange( value, 0.0, 128.0 ) == false ) { + oStream_ << "Simple::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if (number == __SK_Breath_) // 2 + filter_.setPole( 0.99 * (1.0 - (normalizedValue * 2.0)) ); + else if (number == __SK_NoiseLevel_) // 4 + loopGain_ = normalizedValue; + else if (number == __SK_ModFrequency_) { // 11 + normalizedValue /= 0.2 * Stk::sampleRate(); + adsr_.setAttackRate( normalizedValue ); + adsr_.setDecayRate( normalizedValue ); + adsr_.setReleaseRate( normalizedValue ); + } + else if (number == __SK_AfterTouch_Cont_) // 128 + adsr_.setTarget( normalizedValue ); +#if defined(_STK_DEBUG_) + else { + oStream_ << "Simple::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/SineWave.cpp b/src/stk/src/SineWave.cpp new file mode 100644 index 00000000..c3629685 --- /dev/null +++ b/src/stk/src/SineWave.cpp @@ -0,0 +1,78 @@ +/***************************************************/ +/*! \class SineWave + \brief STK sinusoid oscillator class. + + This class computes and saves a static sine "table" that can be + shared by multiple instances. It has an interface similar to the + WaveLoop class but inherits from the Generator class. Output + values are computed using linear interpolation. + + The "table" length, set in SineWave.h, is 2048 samples by default. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "SineWave.h" +#include + +namespace stk { + +StkFrames SineWave :: table_; + +SineWave :: SineWave( void ) + : time_(0.0), rate_(1.0), phaseOffset_(0.0) +{ + if ( table_.empty() ) { + table_.resize( TABLE_SIZE + 1, 1 ); + StkFloat temp = 1.0 / TABLE_SIZE; + for ( unsigned long i=0; i<=TABLE_SIZE; i++ ) + table_[i] = sin( TWO_PI * i * temp ); + } + + Stk::addSampleRateAlert( this ); +} + +SineWave :: ~SineWave() +{ + Stk::removeSampleRateAlert( this ); +} + +void SineWave :: sampleRateChanged( StkFloat newRate, StkFloat oldRate ) +{ + if ( !ignoreSampleRateChange_ ) + this->setRate( oldRate * rate_ / newRate ); +} + +void SineWave :: reset( void ) +{ + time_ = 0.0; + lastFrame_[0] = 0; +} + +void SineWave :: setFrequency( StkFloat frequency ) +{ + // This is a looping frequency. + this->setRate( TABLE_SIZE * frequency / Stk::sampleRate() ); +} + +void SineWave :: addTime( StkFloat time ) +{ + // Add an absolute time in samples. + time_ += time; +} + +void SineWave :: addPhase( StkFloat phase ) +{ + // Add a time in cycles (one cycle = TABLE_SIZE). + time_ += TABLE_SIZE * phase; +} + +void SineWave :: addPhaseOffset( StkFloat phaseOffset ) +{ + // Add a phase offset relative to any previous offset value. + time_ += ( phaseOffset - phaseOffset_ ) * TABLE_SIZE; + phaseOffset_ = phaseOffset; +} + +} // stk namespace diff --git a/src/stk/src/SingWave.cpp b/src/stk/src/SingWave.cpp new file mode 100644 index 00000000..6d80770b --- /dev/null +++ b/src/stk/src/SingWave.cpp @@ -0,0 +1,53 @@ +/***************************************************/ +/*! \class SingWave + \brief STK "singing" looped soundfile class. + + This class loops a specified soundfile and modulates it both + periodically and randomly to produce a pitched musical sound, like + a simple voice or violin. In general, it is not be used alone + because of "munchkinification" effects from pitch shifting. + Within STK, it is used as an excitation source for other + instruments. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "SingWave.h" + +namespace stk { + +SingWave :: SingWave( std::string fileName, bool raw ) +{ + // An exception could be thrown here. + wave_.openFile( fileName, raw ); + + rate_ = 1.0; + sweepRate_ = 0.001; + + modulator_.setVibratoRate( 6.0 ); + modulator_.setVibratoGain( 0.04 ); + modulator_.setRandomGain( 0.005 ); + + this->setFrequency( 75.0 ); + pitchEnvelope_.setRate( 1.0 ); + this->tick(); + this->tick(); + pitchEnvelope_.setRate( sweepRate_ * rate_ ); +} + +SingWave :: ~SingWave() +{ +} + +void SingWave :: setFrequency( StkFloat frequency ) +{ + StkFloat temp = rate_; + rate_ = wave_.getSize() * frequency / Stk::sampleRate(); + temp -= rate_; + if ( temp < 0) temp = -temp; + pitchEnvelope_.setTarget( rate_ ); + pitchEnvelope_.setRate( sweepRate_ * temp ); +} + +} // stk namespace diff --git a/src/stk/src/Sitar.cpp b/src/stk/src/Sitar.cpp new file mode 100644 index 00000000..ab5f405a --- /dev/null +++ b/src/stk/src/Sitar.cpp @@ -0,0 +1,95 @@ +/***************************************************/ +/*! \class Sitar + \brief STK sitar string model class. + + This class implements a sitar plucked string + physical model based on the Karplus-Strong + algorithm. + + This is a digital waveguide model, making its + use possibly subject to patents held by + Stanford University, Yamaha, and others. + There exist at least two patents, assigned to + Stanford, bearing the names of Karplus and/or + Strong. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Sitar.h" + +namespace stk { + +Sitar :: Sitar( StkFloat lowestFrequency ) +{ + if ( lowestFrequency <= 0.0 ) { + oStream_ << "Sitar::Sitar: argument is less than or equal to zero!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + unsigned long length = (unsigned long) ( Stk::sampleRate() / lowestFrequency + 1 ); + delayLine_.setMaximumDelay( length ); + delay_ = 0.5 * length; + delayLine_.setDelay( delay_ ); + targetDelay_ = delay_; + + loopFilter_.setZero( 0.01 ); + loopGain_ = 0.999; + + envelope_.setAllTimes( 0.001, 0.04, 0.0, 0.5 ); + this->clear(); +} + +Sitar :: ~Sitar( void ) +{ +} + +void Sitar :: clear( void ) +{ + delayLine_.clear(); + loopFilter_.clear(); +} + +void Sitar :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "Sitar::setFrequency: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + targetDelay_ = (Stk::sampleRate() / frequency); + // Do not modulate the delay line with noise here: doing so injects energy + // on every frequency change and can make the sitar build up clipped samples + // over time (csound/plugins#4). The noise excitation is applied in tick(). + delay_ = targetDelay_; + delayLine_.setDelay( delay_ ); + loopGain_ = 0.995 + (frequency * 0.0000005); + if ( loopGain_ > 0.9995 ) loopGain_ = 0.9995; +} + +void Sitar :: pluck( StkFloat amplitude ) +{ + envelope_.keyOn(); +} + +void Sitar :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + this->setFrequency( frequency ); + this->pluck( amplitude ); + amGain_ = 0.1 * amplitude; +} + +void Sitar :: noteOff( StkFloat amplitude ) +{ + if ( amplitude < 0.0 || amplitude > 1.0 ) { + oStream_ << "Sitar::noteOff: amplitude is out of range!"; + handleError( StkError::WARNING ); return; + } + + loopGain_ = (StkFloat) 1.0 - amplitude; +} + +} // stk namespace diff --git a/src/stk/src/Skini.cpp b/src/stk/src/Skini.cpp new file mode 100644 index 00000000..aec29a5e --- /dev/null +++ b/src/stk/src/Skini.cpp @@ -0,0 +1,228 @@ +/***************************************************/ +/*! \class Skini + \brief STK SKINI parsing class + + This class parses SKINI formatted text + messages. It can be used to parse individual + messages or it can be passed an entire file. + The SKINI specification is Perry's and his + alone, but it's all text so it shouldn't be too + hard to figure out. + + SKINI (Synthesis toolKit Instrument Network + Interface) is like MIDI, but allows for + floating-point control changes, note numbers, + etc. The following example causes a sharp + middle C to be played with a velocity of 111.132: + + noteOn 60.01 111.132 + + See also SKINI.txt. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Skini.h" +#include "SKINItbl.h" +#include +#include + +namespace stk { + +Skini :: Skini() +{ +} + +Skini :: ~Skini() +{ +} + +bool Skini :: setFile( std::string fileName ) +{ + if ( file_.is_open() ) { + oStream_ << "Skini::setFile: already reaading a file!"; + handleError( StkError::WARNING ); + return false; + } + + file_.open( fileName.c_str() ); + if ( !file_ ) { + oStream_ << "Skini::setFile: unable to open file (" << fileName << ")"; + handleError( StkError::WARNING ); + return false; + } + + return true; +} + +long Skini :: nextMessage( Message& message ) +{ + if ( !file_.is_open() ) return 0; + + std::string line; + bool done = false; + while ( !done ) { + + // Read a line from the file and skip over invalid messages. + if ( std::getline( file_, line ).eof() ) { + oStream_ << "// End of Score. Thanks for using SKINI!!"; + handleError( StkError::STATUS ); + file_.close(); + message.type = 0; + done = true; + } + else if ( parseString( line, message ) > 0 ) done = true; + } + + return message.type; +} + +void Skini :: tokenize( const std::string& str, + std::vector& tokens, + const std::string& delimiters ) +{ + // Skip delimiters at beginning. + std::string::size_type lastPos = str.find_first_not_of(delimiters, 0); + // Find first "non-delimiter". + std::string::size_type pos = str.find_first_of(delimiters, lastPos); + + while (std::string::npos != pos || std::string::npos != lastPos) { + // Found a token, add it to the vector. + tokens.push_back(str.substr(lastPos, pos - lastPos)); + // Skip delimiters. Note the "not_of" + lastPos = str.find_first_not_of(delimiters, pos); + // Find next "non-delimiter" + pos = str.find_first_of(delimiters, lastPos); + } +} + +long Skini :: parseString( std::string& line, Message& message ) +{ + message.type = 0; + if ( line.empty() ) return message.type; + + // Check for comment lines. + std::string::size_type lastPos = line.find_first_not_of(" ,\t", 0); + std::string::size_type pos = line.find_first_of("/", lastPos); + if ( std::string::npos != pos ) { + oStream_ << "// Comment Line: " << line; + handleError( StkError::STATUS ); + return message.type; + } + + // Tokenize the string. + std::vector tokens; + this->tokenize( line, tokens, " ,\t"); + + // Valid SKINI messages must have at least three fields (type, time, + // and channel). + if ( tokens.size() < 3 ) return message.type; + + // Determine message type. + int iSkini = 0; + while ( iSkini < __SK_MaxMsgTypes_ ) { + if ( tokens[0] == skini_msgs[iSkini].messageString ) break; + iSkini++; + } + + if ( iSkini >= __SK_MaxMsgTypes_ ) { + oStream_ << "Skini::parseString: couldn't parse this line:\n " << line; + handleError( StkError::WARNING ); + return message.type; + } + + // Found the type. + message.type = skini_msgs[iSkini].type; + + // Parse time field. + if ( tokens[1][0] == '=' ) { + tokens[1].erase( tokens[1].begin() ); + if ( tokens[1].empty() ) { + oStream_ << "Skini::parseString: couldn't parse time field in line:\n " << line; + handleError( StkError::WARNING ); + return message.type = 0; + } + message.time = (StkFloat) -atof( tokens[1].c_str() ); + } + else + message.time = (StkFloat) atof( tokens[1].c_str() ); + + // Parse the channel field. + message.channel = atoi( tokens[2].c_str() ); + + // Parse the remaining fields (maximum of 2 more). + int iValue = 0; + unsigned int iToken = iValue + 3; //rgh: MIDI extension argument counts are different from regular MIDI + long dataType = skini_msgs[iSkini].data2; + while ( dataType != NOPE ) { + +// if ( tokens.size() <= (unsigned int) (iValue+3) ) { //rgh: test iToken rather than always testing iValue+3 +// if (tokens.size() <= iToken) { //rgh: iToken only tests it more tokens are to be consumed (SK_INT, SK_DBL, SK_STR) + if ((tokens.size() <= iToken) && (dataType < 0)) { //Don't fail if remaining iValues come from skini_msgs[] rather than tokens[]. + oStream_ << "Skini::parseString: inconsistency between type table and parsed line:\n " << line; + handleError( StkError::WARNING ); + return message.type = 0; + } + + switch ( dataType ) { + + case SK_INT: + message.intValues[iValue] = atoi( tokens[iToken].c_str() ); //rgh: use new index + message.floatValues[iValue] = (StkFloat) message.intValues[iValue]; + ++iToken; //rgh: increment token index and value index (below) + break; + + case SK_DBL: + message.floatValues[iValue] = atof( tokens[iToken].c_str() ); //rgh: use new index + message.intValues[iValue] = (long) message.floatValues[iValue]; + ++iToken; //rgh: increment token index and value index (below) + break; + + case SK_STR: // Must be the last field. + message.remainder = tokens[iToken]; //rgh: use new index + return message.type; + + default: // MIDI extension message + message.intValues[iValue] = dataType; + message.floatValues[iValue] = (StkFloat) message.intValues[iValue]; + //iValue--; //rgh: iValue must increment even when iToken does not; resetting iValue only works sometimes + } + + if ( ++iValue == 1 ) + dataType = skini_msgs[iSkini].data3; + else + dataType = NOPE; + } + + return message.type; +} + +std::string Skini :: whatsThisType(long type) +{ + std::string typeString; + + for ( int i=0; i<__SK_MaxMsgTypes_; i++ ) { + if ( type == skini_msgs[i].type ) { + typeString = skini_msgs[i].messageString; + break; + } + } + return typeString; +} + +std::string Skini :: whatsThisController( long number ) +{ + std::string controller; + + for ( int i=0; i<__SK_MaxMsgTypes_; i++) { + if ( skini_msgs[i].type == __SK_ControlChange_ && + number == skini_msgs[i].data2) { + controller = skini_msgs[i].messageString; + break; + } + } + return controller; +} + +} // stk namespace diff --git a/src/stk/src/Sphere.cpp b/src/stk/src/Sphere.cpp new file mode 100644 index 00000000..90b2a14d --- /dev/null +++ b/src/stk/src/Sphere.cpp @@ -0,0 +1,50 @@ +/***************************************************/ +/*! \class Sphere + \brief STK sphere class. + + This class implements a spherical ball with + radius, mass, position, and velocity parameters. + + by Perry R. Cook, 1995--2021. +*/ +/***************************************************/ + +#include "Sphere.h" +#include + +namespace stk { + +Vector3D* Sphere::getRelativePosition( Vector3D* position ) +{ + workingVector_.setXYZ(position->getX() - position_.getX(), + position->getY() - position_.getY(), + position->getZ() - position_.getZ()); + return &workingVector_; +}; + +StkFloat Sphere::getVelocity( Vector3D* velocity ) +{ + velocity->setXYZ( velocity_.getX(), velocity_.getY(), velocity_.getZ() ); + return velocity_.getLength(); +}; + +StkFloat Sphere::isInside( Vector3D *position ) +{ + // Return directed distance from aPosition to spherical boundary ( < + // 0 if inside). + StkFloat distance; + Vector3D *tempVector; + + tempVector = this->getRelativePosition( position ); + distance = tempVector->getLength(); + return distance - radius_; +}; + +void Sphere::addVelocity(StkFloat x, StkFloat y, StkFloat z) +{ + velocity_.setX(velocity_.getX() + x); + velocity_.setY(velocity_.getY() + y); + velocity_.setZ(velocity_.getZ() + z); +} + +} // stk namespace diff --git a/src/stk/src/StifKarp.cpp b/src/stk/src/StifKarp.cpp new file mode 100644 index 00000000..c8a48812 --- /dev/null +++ b/src/stk/src/StifKarp.cpp @@ -0,0 +1,181 @@ +/***************************************************/ +/*! \class StifKarp + \brief STK plucked stiff string instrument. + + This class implements a simple plucked string + algorithm (Karplus Strong) with enhancements + (Jaffe-Smith, Smith, and others), including + string stiffness and pluck position controls. + The stiffness is modeled with allpass filters. + + This is a digital waveguide model, making its + use possibly subject to patents held by + Stanford University, Yamaha, and others. + + Control Change Numbers: + - Pickup Position = 4 + - String Sustain = 11 + - String Stretch = 1 + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "StifKarp.h" +#include "SKINImsg.h" +#include + +namespace stk { + +StifKarp :: StifKarp( StkFloat lowestFrequency ) +{ + if ( lowestFrequency <= 0.0 ) { + oStream_ << "StifKarp::StifKarp: argument is less than or equal to zero!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + unsigned long nDelays = (unsigned long) ( Stk::sampleRate() / lowestFrequency ); + delayLine_.setMaximumDelay( nDelays + 1 ); + combDelay_.setMaximumDelay( nDelays + 1 ); + + pluckAmplitude_ = 0.3; + pickupPosition_ = 0.4; + + stretching_ = 0.9999; + baseLoopGain_ = 0.995; + loopGain_ = 0.999; + + this->clear(); + this->setFrequency( 220.0 ); +} + +StifKarp :: ~StifKarp( void ) +{ +} + +void StifKarp :: clear( void ) +{ + delayLine_.clear(); + combDelay_.clear(); + filter_.clear(); +} + +void StifKarp :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "StifKarp::setFrequency: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + lastFrequency_ = frequency; + lastLength_ = Stk::sampleRate() / lastFrequency_; + StkFloat delay = lastLength_ - 0.5; + delayLine_.setDelay( delay ); + + loopGain_ = baseLoopGain_ + (frequency * 0.000005); + if (loopGain_ >= 1.0) loopGain_ = 0.99999; + + setStretch(stretching_); + + combDelay_.setDelay( 0.5 * pickupPosition_ * lastLength_ ); +} + +void StifKarp :: setStretch( StkFloat stretch ) +{ + stretching_ = stretch; + StkFloat coefficient; + StkFloat freq = lastFrequency_ * 2.0; + StkFloat dFreq = ( (0.5 * Stk::sampleRate()) - freq ) * 0.25; + StkFloat temp = 0.5 + (stretch * 0.5); + if ( temp > 0.99999 ) temp = 0.99999; + for ( int i=0; i<4; i++ ) { + coefficient = temp * temp; + biquad_[i].setA2( coefficient ); + biquad_[i].setB0( coefficient ); + biquad_[i].setB2( 1.0 ); + + coefficient = -2.0 * temp * cos(TWO_PI * freq / Stk::sampleRate()); + biquad_[i].setA1( coefficient ); + biquad_[i].setB1( coefficient ); + + freq += dFreq; + } +} + +void StifKarp :: setPickupPosition( StkFloat position ) { + + if ( position < 0.0 || position > 1.0 ) { + oStream_ << "StifKarp::setPickupPosition: parameter is out of range!"; + handleError( StkError::WARNING ); return; + } + + // Set the pick position, which puts zeroes at position * length. + pickupPosition_ = position; + combDelay_.setDelay( 0.5 * pickupPosition_ * lastLength_ ); +} + +void StifKarp :: setBaseLoopGain( StkFloat aGain ) +{ + baseLoopGain_ = aGain; + loopGain_ = baseLoopGain_ + (lastFrequency_ * 0.000005); + if ( loopGain_ > 0.99999 ) loopGain_ = (StkFloat) 0.99999; +} + +void StifKarp :: pluck( StkFloat amplitude ) +{ + if ( amplitude < 0.0 || amplitude > 1.0 ) { + oStream_ << "StifKarp::pluck: amplitude is out of range!"; + handleError( StkError::WARNING ); return; + } + + pluckAmplitude_ = amplitude; + for ( unsigned long i=0; itick() * pluckAmplitude_) ); + } +} + +void StifKarp :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + this->setFrequency( frequency ); + this->pluck( amplitude ); +} + +void StifKarp :: noteOff( StkFloat amplitude ) +{ + if ( amplitude < 0.0 || amplitude > 1.0 ) { + oStream_ << "StifKarp::noteOff: amplitude is out of range!"; + handleError( StkError::WARNING ); return; + } + + loopGain_ = (1.0 - amplitude) * 0.5; +} + +void StifKarp :: controlChange( int number, StkFloat value ) +{ +#if defined(_STK_DEBUG_) + if ( Stk::inRange( value, 0.0, 128.0 ) == false ) { + oStream_ << "Clarinet::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if (number == __SK_PickPosition_) // 4 + this->setPickupPosition( normalizedValue ); + else if (number == __SK_StringDamping_) // 11 + this->setBaseLoopGain( 0.97 + (normalizedValue * 0.03) ); + else if (number == __SK_StringDetune_) // 1 + this->setStretch( 0.9 + (0.1 * (1.0 - normalizedValue)) ); +#if defined(_STK_DEBUG_) + else { + oStream_ << "StifKarp::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/Stk.cpp b/src/stk/src/Stk.cpp new file mode 100644 index 00000000..e080415c --- /dev/null +++ b/src/stk/src/Stk.cpp @@ -0,0 +1,401 @@ +/***************************************************/ +/*! \class Stk + \brief STK base class + + Nearly all STK classes inherit from this class. + The global sample rate can be queried and + modified via Stk. In addition, this class + provides error handling and byte-swapping + functions. + + The Synthesis ToolKit in C++ (STK) is a set of open source audio + signal processing and algorithmic synthesis classes written in the + C++ programming language. STK was designed to facilitate rapid + development of music synthesis and audio processing software, with + an emphasis on cross-platform functionality, realtime control, + ease of use, and educational example code. STK currently runs + with realtime support (audio and MIDI) on Linux, Macintosh OS X, + and Windows computer platforms. Generic, non-realtime support has + been tested under NeXTStep, Sun, and other platforms and should + work with any standard C++ compiler. + + STK WWW site: http://ccrma.stanford.edu/software/stk/ + + The Synthesis ToolKit in C++ (STK) + Copyright (c) 1995--2021 Perry R. Cook and Gary P. Scavone + + Permission is hereby granted, free of charge, to any person + obtaining a copy of this software and associated documentation files + (the "Software"), to deal in the Software without restriction, + including without limitation the rights to use, copy, modify, merge, + publish, distribute, sublicense, and/or sell copies of the Software, + and to permit persons to whom the Software is furnished to do so, + subject to the following conditions: + + The above copyright notice and this permission notice shall be + included in all copies or substantial portions of the Software. + + Any person wishing to distribute modifications to the Software is + asked to send the modifications to the original developer so that + they can be incorporated into the canonical version. This is, + however, not a binding provision of this license. + + THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, + EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF + MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. + IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR + ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF + CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION + WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ +/***************************************************/ + +#include "Stk.h" +#include + +namespace stk { + +StkFloat Stk :: srate_ = (StkFloat) SRATE; +std::string Stk :: rawwavepath_ = RAWWAVE_PATH; +const Stk::StkFormat Stk :: STK_SINT8 = 0x1; +const Stk::StkFormat Stk :: STK_SINT16 = 0x2; +const Stk::StkFormat Stk :: STK_SINT24 = 0x4; +const Stk::StkFormat Stk :: STK_SINT32 = 0x8; +const Stk::StkFormat Stk :: STK_FLOAT32 = 0x10; +const Stk::StkFormat Stk :: STK_FLOAT64 = 0x20; +bool Stk :: showWarnings_ = true; +bool Stk :: printErrors_ = true; +std::vector Stk :: alertList_; +std::ostringstream Stk :: oStream_; + +Stk :: Stk( void ) + : ignoreSampleRateChange_(false) +{ +} + +Stk :: ~Stk( void ) +{ +} + +void Stk :: setSampleRate( StkFloat rate ) +{ + if ( rate > 0.0 && rate != srate_ ) { + StkFloat oldRate = srate_; + srate_ = rate; + + for ( unsigned int i=0; isampleRateChanged( srate_, oldRate ); + } +} + +void Stk :: sampleRateChanged( StkFloat /*newRate*/, StkFloat /*oldRate*/ ) +{ + // This function should be reimplemented in classes that need to + // make internal variable adjustments in response to a global sample + // rate change. +} + +void Stk :: addSampleRateAlert( Stk *ptr ) +{ + for ( unsigned int i=0; i +#elif defined(__OS_WINDOWS__) + #include +#endif + +void Stk :: sleep(unsigned long milliseconds) +{ +#if defined(__OS_WINDOWS__) + Sleep((DWORD) milliseconds); +#elif (defined(__OS_IRIX__) || defined(__OS_LINUX__) || defined(__OS_MACOSX__)) + usleep( (unsigned long) (milliseconds * 1000.0) ); +#endif +} + +void Stk :: handleError( StkError::Type type ) const +{ + handleError( oStream_.str(), type ); + oStream_.str( std::string() ); // reset the ostringstream buffer +} + +void Stk :: handleError( const char *message, StkError::Type type ) +{ + std::string msg( message ); + handleError( msg, type ); +} + +void Stk :: handleError( std::string message, StkError::Type type ) +{ + if ( type == StkError::WARNING || type == StkError::STATUS ) { + if ( !showWarnings_ ) return; + std::cerr << '\n' << message << '\n' << std::endl; + } + else if (type == StkError::DEBUG_PRINT) { +#if defined(_STK_DEBUG_) + std::cerr << '\n' << message << '\n' << std::endl; +#endif + } + else { + if ( printErrors_ ) { + // Print error message before throwing. + std::cerr << '\n' << message << '\n' << std::endl; + } + throw StkError(message, type); + } +} + +// +// StkFrames definitions +// + +StkFrames :: StkFrames( unsigned int nFrames, unsigned int nChannels ) + : data_( 0 ), nFrames_( nFrames ), nChannels_( nChannels ) +{ + size_ = nFrames_ * nChannels_; + bufferSize_ = size_; + + if ( size_ > 0 ) { + data_ = (StkFloat *) calloc( size_, sizeof( StkFloat ) ); +#if defined(_STK_DEBUG_) + if ( data_ == NULL ) { + std::string error = "StkFrames: memory allocation error in constructor!"; + Stk::handleError( error, StkError::MEMORY_ALLOCATION ); + } +#endif + } + + dataRate_ = Stk::sampleRate(); +} + +StkFrames :: StkFrames( const StkFloat& value, unsigned int nFrames, unsigned int nChannels ) + : data_( 0 ), nFrames_( nFrames ), nChannels_( nChannels ) +{ + size_ = nFrames_ * nChannels_; + bufferSize_ = size_; + if ( size_ > 0 ) { + data_ = (StkFloat *) malloc( size_ * sizeof( StkFloat ) ); +#if defined(_STK_DEBUG_) + if ( data_ == NULL ) { + std::string error = "StkFrames: memory allocation error in constructor!"; + Stk::handleError( error, StkError::MEMORY_ALLOCATION ); + } +#endif + for ( long i=0; i<(long)size_; i++ ) data_[i] = value; + } + + dataRate_ = Stk::sampleRate(); +} + +StkFrames :: ~StkFrames() +{ + if ( data_ ) free( data_ ); +} + +StkFrames :: StkFrames( const StkFrames& f ) + : data_(0), size_(0), bufferSize_(0) +{ + resize( f.frames(), f.channels() ); + dataRate_ = Stk::sampleRate(); + for ( unsigned int i=0; i bufferSize_ ) { + if ( data_ ) free( data_ ); + data_ = (StkFloat *) malloc( size_ * sizeof( StkFloat ) ); +#if defined(_STK_DEBUG_) + if ( data_ == NULL ) { + std::string error = "StkFrames::resize: memory allocation error!"; + Stk::handleError( error, StkError::MEMORY_ALLOCATION ); + } +#endif + bufferSize_ = size_; + } +} + +void StkFrames :: resize( size_t nFrames, unsigned int nChannels, StkFloat value ) +{ + this->resize( nFrames, nChannels ); + + for ( size_t i=0; i channels() - 1) { + std::ostringstream error; + error << "StkFrames::getChannel invalid sourceChannel (" << sourceChannel << ")"; + Stk::handleError( error.str(), StkError::FUNCTION_ARGUMENT); + } + if (destinationChannel > destinationFrames.channels() - 1) { + std::ostringstream error; + error << "StkFrames::getChannel invalid destinationChannel (" << destinationChannel << ")"; + Stk::handleError( error.str(), StkError::FUNCTION_ARGUMENT ); + } + if (destinationFrames.frames() < frames()) { + std::ostringstream error; + error << "StkFrames::getChannel destination.frames() < frames()"; + Stk::handleError( error.str(), StkError::MEMORY_ACCESS); + } +#endif + int sourceHop = nChannels_; + int destinationHop = destinationFrames.nChannels_; + for (unsigned int i = sourceChannel, j= destinationChannel; + i < nFrames_ * nChannels_; + i+=sourceHop,j+=destinationHop) + { + destinationFrames[j] = data_[i]; + } + return destinationFrames; + +} + +void StkFrames::setChannel(unsigned int destinationChannel, const stk::StkFrames &sourceFrames,unsigned int sourceChannel) +{ +#if defined(_STK_DEBUG_) + if (sourceChannel > sourceFrames.channels() - 1) { + std::ostringstream error; + error << "StkFrames::setChannel invalid sourceChannel (" << sourceChannel << ")"; + Stk::handleError( error.str(), StkError::FUNCTION_ARGUMENT); + } + if (destinationChannel > channels() - 1) { + std::ostringstream error; + error << "StkFrames::setChannel invalid channel (" << destinationChannel << ")"; + Stk::handleError( error.str(), StkError::FUNCTION_ARGUMENT ); + } + if (sourceFrames.frames() != frames()) { + std::ostringstream error; + error << "StkFrames::setChannel sourceFrames.frames() != frames()"; + Stk::handleError( error.str(), StkError::MEMORY_ACCESS); + } +#endif + unsigned int sourceHop = sourceFrames.nChannels_; + unsigned int destinationHop = nChannels_; + for (unsigned int i = destinationChannel,j = sourceChannel; + i < nFrames_ * nChannels_; + i+=destinationHop,j+=sourceHop) + { + data_[i] = sourceFrames[j]; + } +} + +StkFloat StkFrames :: interpolate( StkFloat frame, unsigned int channel ) const +{ +#if defined(_STK_DEBUG_) + if ( frame < 0.0 || frame > (StkFloat) ( nFrames_ - 1 ) || channel >= nChannels_ ) { + std::ostringstream error; + error << "StkFrames::interpolate: invalid frame (" << frame << ") or channel (" << channel << ") value!"; + Stk::handleError( error.str(), StkError::MEMORY_ACCESS ); + } +#endif + + size_t iIndex = ( size_t ) frame; // integer part of index + StkFloat output, alpha = frame - (StkFloat) iIndex; // fractional part of index + + iIndex = iIndex * nChannels_ + channel; + output = data_[ iIndex ]; + if ( alpha > 0.0 ) + output += ( alpha * ( data_[ iIndex + nChannels_ ] - output ) ); + + return output; +} + +} // stk namespace diff --git a/src/stk/src/TapDelay.cpp b/src/stk/src/TapDelay.cpp new file mode 100644 index 00000000..49bca6ac --- /dev/null +++ b/src/stk/src/TapDelay.cpp @@ -0,0 +1,87 @@ +/***************************************************/ +/*! \class TapDelay + \brief STK non-interpolating tapped delay line class. + + This class implements a non-interpolating digital delay-line with + an arbitrary number of output "taps". If the maximum length and + tap delays are not specified during instantiation, a fixed maximum + length of 4095 and a single tap delay of zero is set. + + A non-interpolating delay line is typically used in fixed + delay-length applications, such as for reverberation. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "TapDelay.h" + +namespace stk { + +TapDelay :: TapDelay( std::vector taps, unsigned long maxDelay ) +{ + // Writing before reading allows delays from 0 to length-1. + // If we want to allow a delay of maxDelay, we need a + // delayline of length = maxDelay+1. + if ( maxDelay < 1 ) { + oStream_ << "TapDelay::TapDelay: maxDelay must be > 0!\n"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + for ( unsigned int i=0; i maxDelay ) { + oStream_ << "TapDelay::TapDelay: maxDelay must be > than all tap delay values!\n"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + } + + if ( ( maxDelay + 1 ) > inputs_.size() ) + inputs_.resize( maxDelay + 1, 1, 0.0 ); + + inPoint_ = 0; + this->setTapDelays( taps ); +} + +TapDelay :: ~TapDelay() +{ +} + +void TapDelay :: setMaximumDelay( unsigned long delay ) +{ + if ( delay < inputs_.size() ) return; + + for ( unsigned int i=0; i taps ) +{ + for ( unsigned int i=0; i inputs_.size() - 1 ) { // The value is too big. + oStream_ << "TapDelay::setTapDelay: argument (" << taps[i] << ") greater than maximum!\n"; + handleError( StkError::WARNING ); return; + } + } + + if ( taps.size() != outPoint_.size() ) { + outPoint_.resize( taps.size() ); + delays_.resize( taps.size() ); + lastFrame_.resize( 1, (unsigned int)taps.size(), 0.0 ); + } + + for ( unsigned int i=0; i= taps[i] ) outPoint_[i] = inPoint_ - taps[i]; + else outPoint_[i] = inputs_.size() + inPoint_ - taps[i]; + delays_[i] = taps[i]; + } +} + +} // stk namespace + diff --git a/src/stk/src/TubeBell.cpp b/src/stk/src/TubeBell.cpp new file mode 100644 index 00000000..83eac78b --- /dev/null +++ b/src/stk/src/TubeBell.cpp @@ -0,0 +1,78 @@ +/***************************************************/ +/*! \class TubeBell + \brief STK tubular bell (orchestral chime) FM + synthesis instrument. + + This class implements two simple FM Pairs + summed together, also referred to as algorithm + 5 of the TX81Z. + + \code + Algorithm 5 is : 4->3--\ + + --> Out + 2->1--/ + \endcode + + Control Change Numbers: + - Modulator Index One = 2 + - Crossfade of Outputs = 4 + - LFO Speed = 11 + - LFO Depth = 1 + - ADSR 2 & 4 Target = 128 + + The basic Chowning/Stanford FM patent expired + in 1995, but there exist follow-on patents, + mostly assigned to Yamaha. If you are of the + type who should worry about this (making + money) worry away. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "TubeBell.h" + +namespace stk { + +TubeBell :: TubeBell( void ) + : FM() +{ + // Concatenate the STK rawwave path to the rawwave files + for ( unsigned int i=0; i<3; i++ ) + waves_[i] = new FileLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + waves_[3] = new FileLoop( (Stk::rawwavePath() + "fwavblnk.raw").c_str(), true ); + + this->setRatio(0, 1.0 * 0.995); + this->setRatio(1, 1.414 * 0.995); + this->setRatio(2, 1.0 * 1.005); + this->setRatio(3, 1.414 * 1.000); + + gains_[0] = fmGains_[94]; + gains_[1] = fmGains_[76]; + gains_[2] = fmGains_[99]; + gains_[3] = fmGains_[71]; + + adsr_[0]->setAllTimes( 0.005, 4.0, 0.0, 0.04); + adsr_[1]->setAllTimes( 0.005, 4.0, 0.0, 0.04); + adsr_[2]->setAllTimes( 0.001, 2.0, 0.0, 0.04); + adsr_[3]->setAllTimes( 0.004, 4.0, 0.0, 0.04); + + twozero_.setGain( 0.5 ); + vibrato_.setFrequency( 2.0 ); +} + +TubeBell :: ~TubeBell( void ) +{ +} + +void TubeBell :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + gains_[0] = amplitude * fmGains_[94]; + gains_[1] = amplitude * fmGains_[76]; + gains_[2] = amplitude * fmGains_[99]; + gains_[3] = amplitude * fmGains_[71]; + this->setFrequency( frequency ); + this->keyOn(); +} + +} // stk namespace diff --git a/src/stk/src/Twang.cpp b/src/stk/src/Twang.cpp new file mode 100644 index 00000000..17f09256 --- /dev/null +++ b/src/stk/src/Twang.cpp @@ -0,0 +1,103 @@ +/***************************************************/ +/*! \class Twang + \brief STK enhanced plucked string class. + + This class implements an enhanced plucked-string + physical model, a la Jaffe-Smith, Smith, + Karjalainen and others. It includes a comb + filter to simulate pluck position. The tick() + function takes an input sample, which is added + to the delayline input. This can be used to + implement commuted synthesis (if the input + samples are derived from the impulse response of + a body filter) or feedback (as in an electric + guitar model). + + This is a digital waveguide model, making its + use possibly subject to patents held by Stanford + University, Yamaha, and others. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Twang.h" + +namespace stk { + +Twang :: Twang( StkFloat lowestFrequency ) +{ + if ( lowestFrequency <= 0.0 ) { + oStream_ << "Twang::Twang: argument is less than or equal to zero!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + this->setLowestFrequency( lowestFrequency ); + + std::vector coefficients( 2, 0.5 ); + loopFilter_.setCoefficients( coefficients ); + + loopGain_ = 0.995; + pluckPosition_ = 0.4; + this->setFrequency( 220.0 ); +} + +void Twang :: clear( void ) +{ + delayLine_.clear(); + combDelay_.clear(); + loopFilter_.clear(); + lastOutput_ = 0.0; +} + +void Twang :: setLowestFrequency( StkFloat frequency ) +{ + unsigned long nDelays = (unsigned long) ( Stk::sampleRate() / frequency ); + delayLine_.setMaximumDelay( nDelays + 1 ); + combDelay_.setMaximumDelay( nDelays + 1 ); +} + +void Twang :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "Twang::setFrequency: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + frequency_ = frequency; + // Delay = length - filter delay. + StkFloat delay = ( Stk::sampleRate() / frequency ) - loopFilter_.phaseDelay( frequency ); + delayLine_.setDelay( delay ); + + this->setLoopGain( loopGain_ ); + + // Set the pluck position, which puts zeroes at position * length. + combDelay_.setDelay( 0.5 * pluckPosition_ * delay ); +} + +void Twang :: setLoopGain( StkFloat loopGain ) +{ + if ( loopGain < 0.0 || loopGain >= 1.0 ) { + oStream_ << "Twang::setLoopGain: parameter is out of range!"; + handleError( StkError::WARNING ); return; + } + + loopGain_ = loopGain; + StkFloat gain = loopGain_ + (frequency_ * 0.000005); + if ( gain >= 1.0 ) gain = 0.99999; + loopFilter_.setGain( gain ); +} + +void Twang :: setPluckPosition( StkFloat position ) +{ + if ( position < 0.0 || position > 1.0 ) { + oStream_ << "Twang::setPluckPosition: argument (" << position << ") is out of range!"; + handleError( StkError::WARNING ); return; + } + + pluckPosition_ = position; +} + +} // stk namespace diff --git a/src/stk/src/TwoPole.cpp b/src/stk/src/TwoPole.cpp new file mode 100644 index 00000000..8964a1b7 --- /dev/null +++ b/src/stk/src/TwoPole.cpp @@ -0,0 +1,76 @@ +/***************************************************/ +/*! \class TwoPole + \brief STK two-pole filter class. + + This class implements a two-pole digital filter. A method is + provided for creating a resonance in the frequency response while + maintaining a nearly constant filter gain. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "TwoPole.h" +#include + +namespace stk { + +TwoPole :: TwoPole( void ) +{ + b_.resize( 1 ); + a_.resize( 3 ); + inputs_.resize( 1, 1, 0.0 ); + outputs_.resize( 3, 1, 0.0 ); + b_[0] = 1.0; + a_[0] = 1.0; + + Stk::addSampleRateAlert( this ); +} + +TwoPole :: ~TwoPole() +{ + Stk::removeSampleRateAlert( this ); +} + +void TwoPole :: sampleRateChanged( StkFloat newRate, StkFloat oldRate ) +{ + if ( !ignoreSampleRateChange_ ) { + oStream_ << "TwoPole::sampleRateChanged: you may need to recompute filter coefficients!"; + handleError( StkError::WARNING ); + } +} + +void TwoPole :: setResonance( StkFloat frequency, StkFloat radius, bool normalize ) +{ +#if defined(_STK_DEBUG_) + if ( frequency < 0.0 || frequency > 0.5 * Stk::sampleRate() ) { + oStream_ << "TwoPole::setResonance: frequency argument (" << frequency << ") is out of range!"; + handleError( StkError::WARNING ); return; + } + if ( radius < 0.0 || radius >= 1.0 ) { + oStream_ << "TwoPole::setResonance: radius argument (" << radius << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + a_[2] = radius * radius; + a_[1] = (StkFloat) -2.0 * radius * cos(TWO_PI * frequency / Stk::sampleRate()); + + if ( normalize ) { + // Normalize the filter gain ... not terribly efficient. + StkFloat real = 1 - radius + (a_[2] - radius) * cos(TWO_PI * 2 * frequency / Stk::sampleRate()); + StkFloat imag = (a_[2] - radius) * sin(TWO_PI * 2 * frequency / Stk::sampleRate()); + b_[0] = sqrt( pow(real, 2) + pow(imag, 2) ); + } +} + +void TwoPole :: setCoefficients( StkFloat b0, StkFloat a1, StkFloat a2, bool clearState ) +{ + b_[0] = b0; + a_[1] = a1; + a_[2] = a2; + + if ( clearState ) this->clear(); +} + +} // stk namespace diff --git a/src/stk/src/TwoZero.cpp b/src/stk/src/TwoZero.cpp new file mode 100644 index 00000000..8742bf45 --- /dev/null +++ b/src/stk/src/TwoZero.cpp @@ -0,0 +1,74 @@ +/***************************************************/ +/*! \class TwoZero + \brief STK two-zero filter class. + + This class implements a two-zero digital filter. A method is + provided for creating a "notch" in the frequency response while + maintaining a constant filter gain. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "TwoZero.h" +#include + +namespace stk { + +TwoZero :: TwoZero( void ) +{ + b_.resize( 3, 0.0 ); + inputs_.resize( 3, 1, 0.0 ); + b_[0] = 1.0; + + Stk::addSampleRateAlert( this ); +} + +TwoZero :: ~TwoZero() +{ + Stk::removeSampleRateAlert( this ); +} + +void TwoZero :: sampleRateChanged( StkFloat newRate, StkFloat oldRate ) +{ + if ( !ignoreSampleRateChange_ ) { + oStream_ << "TwoZero::sampleRateChanged: you may need to recompute filter coefficients!"; + handleError( StkError::WARNING ); + } +} + +void TwoZero :: setCoefficients( StkFloat b0, StkFloat b1, StkFloat b2, bool clearState ) +{ + b_[0] = b0; + b_[1] = b1; + b_[2] = b2; + + if ( clearState ) this->clear(); +} + +void TwoZero :: setNotch( StkFloat frequency, StkFloat radius ) +{ +#if defined(_STK_DEBUG_) + if ( frequency < 0.0 || frequency > 0.5 * Stk::sampleRate() ) { + oStream_ << "TwoZero::setNotch: frequency argument (" << frequency << ") is out of range!"; + handleError( StkError::WARNING ); return; + } + if ( radius < 0.0 ) { + oStream_ << "TwoZero::setNotch: radius argument (" << radius << ") is negative!"; + handleError( StkError::WARNING ); return; + } +#endif + + b_[2] = radius * radius; + b_[1] = -2.0 * radius * cos(TWO_PI * frequency / Stk::sampleRate()); + + // Normalize the filter gain. + if ( b_[1] > 0.0 ) // Maximum at z = 0. + b_[0] = 1.0 / ( 1.0 + b_[1] + b_[2] ); + else // Maximum at z = -1. + b_[0] = 1.0 / ( 1.0 - b_[1] + b_[2] ); + b_[1] *= b_[0]; + b_[2] *= b_[0]; +} + +} // stk namespace diff --git a/src/stk/src/VoicForm.cpp b/src/stk/src/VoicForm.cpp new file mode 100644 index 00000000..ef1b5d08 --- /dev/null +++ b/src/stk/src/VoicForm.cpp @@ -0,0 +1,189 @@ +/***************************************************/ +/*! \class VoicForm + \brief Four formant synthesis instrument. + + This instrument contains an excitation singing + wavetable (looping wave with random and + periodic vibrato, smoothing on frequency, + etc.), excitation noise, and four sweepable + complex resonances. + + Measured formant data is included, and enough + data is there to support either parallel or + cascade synthesis. In the floating point case + cascade synthesis is the most natural so + that's what you'll find here. + + Control Change Numbers: + - Voiced/Unvoiced Mix = 2 + - Vowel/Phoneme Selection = 4 + - Vibrato Frequency = 11 + - Vibrato Gain = 1 + - Loudness (Spectral Tilt) = 128 + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "VoicForm.h" +#include "Phonemes.h" +#include "SKINImsg.h" +#include +#include + +namespace stk { + +VoicForm :: VoicForm( void ) : Instrmnt() +{ + // Concatenate the STK rawwave path to the rawwave file + voiced_ = new SingWave( (Stk::rawwavePath() + "impuls20.raw").c_str(), true ); + voiced_->setGainRate( 0.001 ); + voiced_->setGainTarget( 0.0 ); + + for ( int i=0; i<4; i++ ) + filters_[i].setSweepRate( 0.001 ); + + onezero_.setZero( -0.9 ); + onepole_.setPole( 0.9 ); + + noiseEnv_.setRate( 0.001 ); + noiseEnv_.setTarget( 0.0 ); + + this->setPhoneme( "eee" ); + this->clear(); +} + +VoicForm :: ~VoicForm( void ) +{ + delete voiced_; +} + +void VoicForm :: clear( void ) +{ + onezero_.clear(); + onepole_.clear(); + for ( int i=0; i<4; i++ ) { + filters_[i].clear(); + } +} + +void VoicForm :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "VoicForm::setFrequency: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + voiced_->setFrequency( frequency ); +} + +bool VoicForm :: setPhoneme( const char *phoneme ) +{ + bool found = false; + unsigned int i = 0; + while( i < 32 && !found ) { + if ( !strcmp( Phonemes::name(i), phoneme ) ) { + found = true; + filters_[0].setTargets( Phonemes::formantFrequency(i, 0), Phonemes::formantRadius(i, 0), pow(10.0, Phonemes::formantGain(i, 0 ) / 20.0) ); + filters_[1].setTargets( Phonemes::formantFrequency(i, 1), Phonemes::formantRadius(i, 1), pow(10.0, Phonemes::formantGain(i, 1 ) / 20.0) ); + filters_[2].setTargets( Phonemes::formantFrequency(i, 2), Phonemes::formantRadius(i, 2), pow(10.0, Phonemes::formantGain(i, 2 ) / 20.0) ); + filters_[3].setTargets( Phonemes::formantFrequency(i, 3), Phonemes::formantRadius(i, 3), pow(10.0, Phonemes::formantGain(i, 3 ) / 20.0) ); + this->setVoiced( Phonemes::voiceGain( i ) ); + this->setUnVoiced( Phonemes::noiseGain( i ) ); + } + i++; + } + + if ( !found ) { + oStream_ << "VoicForm::setPhoneme: phoneme " << phoneme << " not found!"; + handleError( StkError::WARNING ); + } + + return found; +} + +void VoicForm :: setFilterSweepRate( unsigned int whichOne, StkFloat rate ) +{ + if ( whichOne > 3 ) { + oStream_ << "VoicForm::setFilterSweepRate: filter select argument outside range 0-3!"; + handleError( StkError::WARNING ); return; + } + + filters_[whichOne].setSweepRate(rate); +} + +void VoicForm :: quiet( void ) +{ + voiced_->noteOff(); + noiseEnv_.setTarget( 0.0 ); +} + +void VoicForm :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + this->setFrequency( frequency ); + voiced_->setGainTarget( amplitude ); + onepole_.setPole( 0.97 - (amplitude * 0.2) ); +} + +void VoicForm :: controlChange( int number, StkFloat value ) +{ +#if defined(_STK_DEBUG_) + if ( Stk::inRange( value, 0.0, 128.0 ) == false ) { + oStream_ << "VoicForm::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if (number == __SK_Breath_) { // 2 + this->setVoiced( 1.0 - normalizedValue ); + this->setUnVoiced( 0.01 * normalizedValue ); + } + else if (number == __SK_FootControl_) { // 4 + StkFloat temp = 0.0; + unsigned int i = (int) value; + if (i < 32) { + temp = 0.9; + } + else if (i < 64) { + i -= 32; + temp = 1.0; + } + else if (i < 96) { + i -= 64; + temp = 1.1; + } + else if (i < 128) { + i -= 96; + temp = 1.2; + } + else if (i == 128) { + i = 0; + temp = 1.4; + } + filters_[0].setTargets( temp * Phonemes::formantFrequency(i, 0), Phonemes::formantRadius(i, 0), pow(10.0, Phonemes::formantGain(i, 0 ) / 20.0) ); + filters_[1].setTargets( temp * Phonemes::formantFrequency(i, 1), Phonemes::formantRadius(i, 1), pow(10.0, Phonemes::formantGain(i, 1 ) / 20.0) ); + filters_[2].setTargets( temp * Phonemes::formantFrequency(i, 2), Phonemes::formantRadius(i, 2), pow(10.0, Phonemes::formantGain(i, 2 ) / 20.0) ); + filters_[3].setTargets( temp * Phonemes::formantFrequency(i, 3), Phonemes::formantRadius(i, 3), pow(10.0, Phonemes::formantGain(i, 3 ) / 20.0) ); + this->setVoiced( Phonemes::voiceGain( i ) ); + this->setUnVoiced( Phonemes::noiseGain( i ) ); + } + else if (number == __SK_ModFrequency_) // 11 + voiced_->setVibratoRate( normalizedValue * 12.0); // 0 to 12 Hz + else if (number == __SK_ModWheel_) // 1 + voiced_->setVibratoGain( normalizedValue * 0.2); + else if (number == __SK_AfterTouch_Cont_) { // 128 + this->setVoiced( normalizedValue ); + onepole_.setPole( 0.97 - ( normalizedValue * 0.2) ); + } +#if defined(_STK_DEBUG_) + else { + oStream_ << "VoicForm::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/Voicer.cpp b/src/stk/src/Voicer.cpp new file mode 100644 index 00000000..543cffb1 --- /dev/null +++ b/src/stk/src/Voicer.cpp @@ -0,0 +1,224 @@ +/***************************************************/ +/*! \class Voicer + \brief STK voice manager class. + + This class can be used to manage a group of STK instrument + classes. Individual voices can be controlled via unique note + tags. Instrument groups can be controlled by group number. + + A previously constructed STK instrument class is linked with a + voice manager using the addInstrument() function. An optional + group number argument can be specified to the addInstrument() + function as well (default group = 0). The voice manager does not + delete any instrument instances ... it is the responsibility of + the user to allocate and deallocate all instruments. + + The tick() function returns the mix of all sounding voices. Each + noteOn returns a unique tag (credits to the NeXT MusicKit), so you + can send control changes to specific voices within an ensemble. + Alternately, control changes can be sent to all voices in a given + group. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Voicer.h" +#include + +namespace stk { + +Voicer :: Voicer( StkFloat decayTime ) +{ + if ( decayTime < 0.0 ) { + oStream_ << "Voicer::Voicer: argument (" << decayTime << ") must be positive!"; + handleError( StkError::FUNCTION_ARGUMENT ); + } + + tags_ = 23456; + muteTime_ = (int) ( decayTime * Stk::sampleRate() ); + lastFrame_.resize( 1, 1, 0.0 ); +} + +void Voicer :: addInstrument( Instrmnt *instrument, int group ) +{ + Voicer::Voice voice; + voice.instrument = instrument; + voice.group = group; + voice.noteNumber = -1; + voices_.push_back( voice ); + + // Check output channels and resize lastFrame_ if necessary. + if ( instrument->channelsOut() > lastFrame_.channels() ) { + unsigned int startChannel = lastFrame_.channels(); + lastFrame_.resize( 1, instrument->channelsOut() ); + for ( unsigned int i=startChannel; i::iterator i; + for ( i=voices_.begin(); i!=voices_.end(); ++i ) { + if ( (*i).instrument != instrument ) continue; + voices_.erase( i ); + found = true; + break; + } + + if ( found ) { + // Check output channels and resize lastFrame_ if necessary. + unsigned int maxChannels = 1; + for ( i=voices_.begin(); i!=voices_.end(); ++i ) { + if ( (*i).instrument->channelsOut() > maxChannels ) maxChannels = (*i).instrument->channelsOut(); + } + if ( maxChannels < lastFrame_.channels() ) + lastFrame_.resize( 1, maxChannels ); + } + else { + oStream_ << "Voicer::removeInstrument: instrument pointer not found in current voices!"; + handleError( StkError::WARNING ); + } +} + +long Voicer :: noteOn(StkFloat noteNumber, StkFloat amplitude, int group ) +{ + unsigned int i; + StkFloat frequency = (StkFloat) 220.0 * pow( 2.0, (noteNumber - 57.0) / 12.0 ); + for ( i=0; inoteOn( frequency, amplitude * ONE_OVER_128 ); + voices_[i].sounding = 1; + return voices_[i].tag; + } + } + + // All voices are sounding, so interrupt the oldest voice. + int voice = -1; + for ( i=0; i= 0 ) { + voices_[voice].tag = tags_++; + voices_[voice].group = group; + voices_[voice].noteNumber = noteNumber; + voices_[voice].frequency = frequency; + voices_[voice].instrument->noteOn( frequency, amplitude * ONE_OVER_128 ); + voices_[voice].sounding = 1; + return voices_[voice].tag; + } + + return -1; +} + +void Voicer :: noteOff( StkFloat noteNumber, StkFloat amplitude, int group ) +{ + for ( unsigned int i=0; inoteOff( amplitude * ONE_OVER_128 ); + voices_[i].sounding = -muteTime_; + } + } +} + +void Voicer :: noteOff( long tag, StkFloat amplitude ) +{ + for ( unsigned int i=0; inoteOff( amplitude * ONE_OVER_128 ); + voices_[i].sounding = -muteTime_; + break; + } + } +} + +void Voicer :: setFrequency( StkFloat noteNumber, int group ) +{ + StkFloat frequency = (StkFloat) 220.0 * pow( 2.0, (noteNumber - 57.0) / 12.0 ); + for ( unsigned int i=0; isetFrequency( frequency ); + } + } +} + +void Voicer :: setFrequency( long tag, StkFloat noteNumber ) +{ + StkFloat frequency = (StkFloat) 220.0 * pow( 2.0, (noteNumber - 57.0) / 12.0 ); + for ( unsigned int i=0; isetFrequency( frequency ); + break; + } + } +} + +void Voicer :: pitchBend( StkFloat value, int group ) +{ + StkFloat pitchScaler; + if ( value < 8192.0 ) + pitchScaler = pow( 0.5, (8192.0-value) / 8192.0 ); + else + pitchScaler = pow( 2.0, (value-8192.0) / 8192.0 ); + for ( unsigned int i=0; isetFrequency( (StkFloat) (voices_[i].frequency * pitchScaler) ); + } +} + +void Voicer :: pitchBend( long tag, StkFloat value ) +{ + StkFloat pitchScaler; + if ( value < 8192.0 ) + pitchScaler = pow( 0.5, (8192.0-value) / 8192.0 ); + else + pitchScaler = pow( 2.0, (value-8192.0) / 8192.0 ); + for ( unsigned int i=0; isetFrequency( (StkFloat) (voices_[i].frequency * pitchScaler) ); + break; + } + } +} + +void Voicer :: controlChange( int number, StkFloat value, int group ) +{ + for ( unsigned int i=0; icontrolChange( number, value ); + } +} + +void Voicer :: controlChange( long tag, int number, StkFloat value ) +{ + for ( unsigned int i=0; icontrolChange( number, value ); + break; + } + } +} + +void Voicer :: silence( void ) +{ + for ( unsigned int i=0; i 0 ) + voices_[i].instrument->noteOff( 0.5 ); + } +} + +} // stk namespace diff --git a/src/stk/src/Whistle.cpp b/src/stk/src/Whistle.cpp new file mode 100644 index 00000000..636861ef --- /dev/null +++ b/src/stk/src/Whistle.cpp @@ -0,0 +1,256 @@ +/***************************************************/ +/*! \Class Whistle + \brief STK police/referee whistle instrument class. + + This class implements a hybrid physical/spectral + model of a police whistle (a la Cook). + + Control Change Numbers: + - Noise Gain = 4 + - Fipple Modulation Frequency = 11 + - Fipple Modulation Gain = 1 + - Blowing Frequency Modulation = 2 + - Volume = 128 + + by Perry R. Cook 1995--2021. +*/ +/***************************************************/ + +#include "Whistle.h" +#include "SKINImsg.h" +#include + +namespace stk { + +const int CAN_RADIUS = 100; +const int PEA_RADIUS = 30; +const int BUMP_RADIUS = 5; + +const StkFloat NORM_CAN_LOSS = 0.97; +//const StkFloat SLOW_CAN_LOSS = 0.90; +const StkFloat GRAVITY = 20.0; + +const StkFloat NORM_TICK_SIZE = 0.004; +//const StkFloat SLOW_TICK_SIZE = 0.0001; + +const StkFloat ENV_RATE = 0.001; + +Whistle :: Whistle( void ) +{ + sine_.setFrequency( 2800.0 ); + + can_.setRadius( CAN_RADIUS ); + can_.setPosition(0, 0, 0); // set can location + can_.setVelocity(0, 0, 0); // and the velocity + + onepole_.setPole(0.95); // 0.99 + + bumper_.setRadius( BUMP_RADIUS ); + bumper_.setPosition(0.0, CAN_RADIUS-BUMP_RADIUS, 0); + bumper_.setPosition(0.0, CAN_RADIUS-BUMP_RADIUS, 0); + + pea_.setRadius( PEA_RADIUS ); + pea_.setPosition(0, CAN_RADIUS/2, 0); + pea_.setVelocity(35, 15, 0); + + envelope_.setRate( ENV_RATE ); + envelope_.keyOn(); + + fippleFreqMod_ = 0.5; + fippleGainMod_ = 0.5; + blowFreqMod_ = 0.25; + noiseGain_ = 0.125; + baseFrequency_ = 2000; + + tickSize_ = NORM_TICK_SIZE; + canLoss_ = NORM_CAN_LOSS; + + subSample_ = 1; + subSampCount_ = subSample_; +} + +Whistle :: ~Whistle( void ) +{ +#ifdef WHISTLE_ANIMATION + printf("Exit, Whistle bye bye!!\n"); +#endif +} + +void Whistle :: clear( void ) +{ +} + +void Whistle :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "Whistle::setFrequency: parameter is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + baseFrequency_ = frequency * 4; // the whistle is a transposing instrument +} + +void Whistle :: startBlowing( StkFloat amplitude, StkFloat rate ) +{ + if ( amplitude <= 0.0 || rate <= 0.0 ) { + oStream_ << "Whistle::startBlowing: one or more arguments is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } + + envelope_.setRate( ENV_RATE ); + envelope_.setTarget( amplitude ); +} + +void Whistle :: stopBlowing( StkFloat rate ) +{ + if ( rate <= 0.0 ) { + oStream_ << "Whistle::stopBlowing: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } + + envelope_.setRate( rate ); + envelope_.keyOff(); +} + +void Whistle :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + this->setFrequency( frequency ); + this->startBlowing( amplitude*2.0 ,amplitude * 0.2 ); +} + +void Whistle :: noteOff( StkFloat amplitude ) +{ + this->stopBlowing( amplitude * 0.02 ); +} + +int frameCount = 0; + +StkFloat Whistle :: tick( unsigned int ) +{ + StkFloat soundMix, tempFreq; + StkFloat envOut = 0, temp, temp1, temp2, tempX, tempY; + double phi, cosphi, sinphi; + double gain = 0.5, mod = 0.0; + + if ( --subSampCount_ <= 0 ) { + tempVectorP_ = pea_.getPosition(); + subSampCount_ = subSample_; + temp = bumper_.isInside( tempVectorP_ ); +#ifdef WHISTLE_ANIMATION + frameCount += 1; + if ( frameCount >= (1470 / subSample_) ) { + frameCount = 0; + printf("%f %f %f\n",tempVectorP_->getX(),tempVectorP_->getY(),envOut); + fflush(stdout); + } +#endif + envOut = envelope_.tick(); + + if (temp < (BUMP_RADIUS + PEA_RADIUS)) { + tempX = envOut * tickSize_ * 2000 * noise_.tick(); + tempY = -envOut * tickSize_ * 1000 * (1.0 + noise_.tick()); + pea_.addVelocity( tempX, tempY, 0 ); + pea_.tick( tickSize_ ); + } + + mod = exp(-temp * 0.01); // exp. distance falloff of fipple/pea effect + temp = onepole_.tick(mod); // smooth it a little + gain = (1.0 - (fippleGainMod_*0.5)) + (2.0 * fippleGainMod_ * temp); + gain *= gain; // squared distance/gain + // tempFreq = 1.0 // Normalized Base Freq + // + (fippleFreqMod_ * 0.25) - (fippleFreqMod_ * temp) // fippleModulation + // - (blowFreqMod_) + (blowFreqMod_ * envOut); // blowingModulation + // short form of above + tempFreq = 1.0 + fippleFreqMod_*(0.25-temp) + blowFreqMod_*(envOut-1.0); + tempFreq *= baseFrequency_; + + sine_.setFrequency(tempFreq); + + tempVectorP_ = pea_.getPosition(); + temp = can_.isInside(tempVectorP_); + temp = -temp; // We know (hope) it's inside, just how much?? + if (temp < (PEA_RADIUS * 1.25)) { + pea_.getVelocity( &tempVector_ ); // This is the can/pea collision + tempX = tempVectorP_->getX(); // calculation. Could probably + tempY = tempVectorP_->getY(); // simplify using tables, etc. + phi = -atan2(tempY,tempX); + + cosphi = cos(phi); + sinphi = sin(phi); + temp1 = (cosphi*tempVector_.getX()) - (sinphi*tempVector_.getY()); + temp2 = (sinphi*tempVector_.getX()) + (cosphi*tempVector_.getY()); + temp1 = -temp1; + tempX = (cosphi*temp1) + (sinphi*temp2); + tempY = (-sinphi*temp1) + (cosphi*temp2); + pea_.setVelocity(tempX, tempY, 0); + pea_.tick(tickSize_); + pea_.setVelocity( tempX*canLoss_, tempY*canLoss_, 0 ); + pea_.tick(tickSize_); + } + + temp = tempVectorP_->getLength(); + if (temp > 0.01) { + tempX = tempVectorP_->getX(); + tempY = tempVectorP_->getY(); + phi = atan2( tempY, tempX ); + phi += 0.3 * temp / CAN_RADIUS; + cosphi = cos(phi); + sinphi = sin(phi); + tempX = 3.0 * temp * cosphi; + tempY = 3.0 * temp * sinphi; + } + else { + tempX = 0.0; + tempY = 0.0; + } + + temp = (0.9 + 0.1*subSample_*noise_.tick()) * envOut * 0.6 * tickSize_; + pea_.addVelocity( temp * tempX, (temp*tempY) - (GRAVITY*tickSize_), 0 ); + pea_.tick( tickSize_ ); + + // bumper_.tick(0.0); + } + + temp = envOut * envOut * gain / 2; + soundMix = temp * ( sine_.tick() + ( noiseGain_*noise_.tick() ) ); + lastFrame_[0] = 0.20 * soundMix; // should probably do one-zero filter here + + return lastFrame_[0]; +} + +void Whistle :: controlChange( int number, StkFloat value ) +{ +#if defined(_STK_DEBUG_) + if ( Stk::inRange( value, 0.0, 128.0 ) == false ) { + oStream_ << "Whistle::controlChange: value (" << value << ") is out of range!"; + handleError( StkError::WARNING ); return; + } +#endif + + StkFloat normalizedValue = value * ONE_OVER_128; + if ( number == __SK_NoiseLevel_ ) // 4 + noiseGain_ = 0.25 * normalizedValue; + else if ( number == __SK_ModFrequency_ ) // 11 + fippleFreqMod_ = normalizedValue; + else if ( number == __SK_ModWheel_ ) // 1 + fippleGainMod_ = normalizedValue; + else if ( number == __SK_AfterTouch_Cont_ ) // 128 + envelope_.setTarget( normalizedValue * 2.0 ); + else if ( number == __SK_Breath_ ) // 2 + blowFreqMod_ = normalizedValue * 0.5; + else if ( number == __SK_Sustain_ ) { // 64 + subSample_ = (int) value; + if ( subSample_ < 1.0 ) subSample_ = 1; + envelope_.setRate( ENV_RATE / subSample_ ); + } +#if defined(_STK_DEBUG_) + else { + oStream_ << "Whistle::controlChange: undefined control number (" << number << ")!"; + handleError( StkError::WARNING ); + } +#endif +} + +} // stk namespace diff --git a/src/stk/src/Wurley.cpp b/src/stk/src/Wurley.cpp new file mode 100644 index 00000000..68425399 --- /dev/null +++ b/src/stk/src/Wurley.cpp @@ -0,0 +1,94 @@ +/***************************************************/ +/*! \class Wurley + \brief STK Wurlitzer electric piano FM + synthesis instrument. + + This class implements two simple FM Pairs + summed together, also referred to as algorithm + 5 of the TX81Z. + + \code + Algorithm 5 is : 4->3--\ + + --> Out + 2->1--/ + \endcode + + Control Change Numbers: + - Modulator Index One = 2 + - Crossfade of Outputs = 4 + - LFO Speed = 11 + - LFO Depth = 1 + - ADSR 2 & 4 Target = 128 + + The basic Chowning/Stanford FM patent expired + in 1995, but there exist follow-on patents, + mostly assigned to Yamaha. If you are of the + type who should worry about this (making + money) worry away. + + by Perry R. Cook and Gary P. Scavone, 1995--2021. +*/ +/***************************************************/ + +#include "Wurley.h" + +namespace stk { + +Wurley :: Wurley( void ) + : FM() +{ + // Concatenate the STK rawwave path to the rawwave files + for ( unsigned int i=0; i<3; i++ ) + waves_[i] = new FileLoop( (Stk::rawwavePath() + "sinewave.raw").c_str(), true ); + waves_[3] = new FileLoop( (Stk::rawwavePath() + "fwavblnk.raw").c_str(), true ); + + this->setRatio(0, 1.0); + this->setRatio(1, 4.0); + this->setRatio(2, -510.0); + this->setRatio(3, -510.0); + + gains_[0] = fmGains_[99]; + gains_[1] = fmGains_[82]; + gains_[2] = fmGains_[92]; + gains_[3] = fmGains_[68]; + + adsr_[0]->setAllTimes( 0.001, 1.50, 0.0, 0.04); + adsr_[1]->setAllTimes( 0.001, 1.50, 0.0, 0.04); + adsr_[2]->setAllTimes( 0.001, 0.25, 0.0, 0.04); + adsr_[3]->setAllTimes( 0.001, 0.15, 0.0, 0.04); + + twozero_.setGain( 2.0 ); + vibrato_.setFrequency( 8.0 ); +} + +Wurley :: ~Wurley( void ) +{ +} + +void Wurley :: setFrequency( StkFloat frequency ) +{ +#if defined(_STK_DEBUG_) + if ( frequency <= 0.0 ) { + oStream_ << "Wurley::setFrequency: argument is less than or equal to zero!"; + handleError( StkError::WARNING ); return; + } +#endif + + baseFrequency_ = frequency; + waves_[0]->setFrequency( baseFrequency_ * ratios_[0]); + waves_[1]->setFrequency( baseFrequency_ * ratios_[1]); + waves_[2]->setFrequency( ratios_[2] ); // Note here a 'fixed resonance'. + waves_[3]->setFrequency( ratios_[3] ); +} + +void Wurley :: noteOn( StkFloat frequency, StkFloat amplitude ) +{ + gains_[0] = amplitude * fmGains_[99]; + gains_[1] = amplitude * fmGains_[82]; + gains_[2] = amplitude * fmGains_[82]; + gains_[3] = amplitude * fmGains_[68]; + this->setFrequency( frequency ); + this->keyOn(); +} + +} // stk namespace diff --git a/src/stk/stkOpcodes.cpp b/src/stk/stkOpcodes.cpp new file mode 100644 index 00000000..5db1c941 --- /dev/null +++ b/src/stk/stkOpcodes.cpp @@ -0,0 +1,370 @@ +/* + This file is part of Csound. + + The Csound Library is free software; you can redistribute it + and/or modify it under the terms of the GNU Lesser General Public + License as published by the Free Software Foundation; either + version 2.1 of the License, or (at your option) any later version. + + Csound is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU Lesser General Public License for more details. + + You should have received a copy of the GNU Lesser General Public + License along with Csound; if not, write to the Free Software + Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA + 02110-1301 USA +*/ +/* + * CSOUND 7 OPCODES FOR PERRY COOK'S SYNTHESIS TOOLKIT IN C++ (STK) INSTRUMENT + * + * This code is copyright (C) 2005 by Michael Gogins. It is licensed under the + * same terms as the Synthesis Toolkit in C++ by Perry R. Cook and Gary P. + * Scavone. + * + * The STK sources are bundled in this directory (include/, src/ and + * rawwaves/) so that a patched copy is always used, independent of any system + * or package-manager STK installation. STK_EMBED_RAWWAVES stages the bundled + * samples at module initialization; otherwise the rawwaves path defaults to + * the installed location (DEFAULT_RAWWAVE_PATH). The RAWWAVE_PATH environment + * variable overrides either default. + * + * All these opcodes are named "STK" + the STK classname, + * e.g. "STKBowed" for the Bowed instrument. + * + * All the STK opcodes have the same signature: + * + * aout STKName ifrequency igain {kcontroller0, kvalue1,...,kcontroller7, + * kvalue7} + * + * They take a frequency in Hertz and a gain parameter in the range [0, 1], + * plus up to eight optional krate controller-value pairs, and return an arate + * signal that should be more or less in the range [-1, +1]. + * See the STK class documentation to determine the controller numbers + * used by each instrument. + */ + +/* Standard library headers must be included before any STK or Csound headers so + that STK's and Csound's "PI" do not clash with the standard library. */ +#include +#include +#include +#include +#include +#include + +#ifdef STK_EMBED_RAWWAVES +#include +#ifdef _WIN32 +#include +#else +#include +#endif +#include "stk_rawwaves.inc" +#endif + +#include "BandedWG.h" +#include "BeeThree.h" +#include "BlowBotl.h" +#include "BlowHole.h" +#include "Bowed.h" +#include "Brass.h" +#include "Clarinet.h" +#include "Drummer.h" +#include "FMVoices.h" +#include "Flute.h" +#include "HevyMetl.h" +#include "Mandolin.h" +#include "Stk.h" +#include "ModalBar.h" +#include "Moog.h" +#include "PercFlut.h" +#include "Plucked.h" +#include "Resonate.h" +#include "Rhodey.h" +#include "Saxofony.h" +#include "Shakers.h" +#include "Simple.h" +#include "Sitar.h" +#include "StifKarp.h" +#include "TubeBell.h" +#include "VoicForm.h" +#include "Whistle.h" +#include "Wurley.h" + +extern "C" { +#include +} + +#include "STKInstrumentAdapter.hpp" + +using namespace stk; +using namespace csound; +using namespace std; + +extern "C" { +OENTRY oentries[] = { + { + (char *)"STKBandedWG", sizeof(STKInstrumentAdapter), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter::init_, + (SUBR)STKInstrumentAdapter::kontrol_, + (SUBR)STKInstrumentAdapter::deinit_, 0, 0, + }, + { + (char *)"STKBeeThree", sizeof(STKInstrumentAdapter), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter::init_, + (SUBR)STKInstrumentAdapter::kontrol_, + (SUBR)STKInstrumentAdapter::deinit_, 0, 0, + }, + { + (char *)"STKBlowBotl", sizeof(STKInstrumentAdapter), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter::init_, + (SUBR)STKInstrumentAdapter::kontrol_, + (SUBR)STKInstrumentAdapter::deinit_, 0, 0, + }, + { + (char *)"STKBlowHole", sizeof(STKInstrumentAdapter1), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter1::init_, + (SUBR)STKInstrumentAdapter1::kontrol_, + (SUBR)STKInstrumentAdapter1::deinit_, 0, 0, + }, + { + (char *)"STKBowed", sizeof(STKInstrumentAdapter1), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter1::init_, + (SUBR)STKInstrumentAdapter1::kontrol_, + (SUBR)STKInstrumentAdapter1::deinit_, 0, 0, + }, + { + (char *)"STKBrass", sizeof(STKInstrumentAdapter1), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter1::init_, + (SUBR)STKInstrumentAdapter1::kontrol_, + (SUBR)STKInstrumentAdapter1::deinit_, 0, 0, + }, + { + (char *)"STKClarinet", sizeof(STKInstrumentAdapter1), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter1::init_, + (SUBR)STKInstrumentAdapter1::kontrol_, + (SUBR)STKInstrumentAdapter1::deinit_, 0, 0, + }, + { + (char *)"STKDrummer", sizeof(STKInstrumentAdapter), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter::init_, + (SUBR)STKInstrumentAdapter::kontrol_, + (SUBR)STKInstrumentAdapter::deinit_, 0, 0, + }, + { + (char *)"STKFlute", sizeof(STKInstrumentAdapter1), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter1::init_, + (SUBR)STKInstrumentAdapter1::kontrol_, + (SUBR)STKInstrumentAdapter1::deinit_, 0, 0, + }, + { + (char *)"STKFMVoices", sizeof(STKInstrumentAdapter), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter::init_, + (SUBR)STKInstrumentAdapter::kontrol_, + (SUBR)STKInstrumentAdapter::deinit_, 0, 0, + }, + { + (char *)"STKHevyMetl", sizeof(STKInstrumentAdapter), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter::init_, + (SUBR)STKInstrumentAdapter::kontrol_, + (SUBR)STKInstrumentAdapter::deinit_, 0, 0, + }, + { + (char *)"STKMandolin", sizeof(STKInstrumentAdapter1), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter1::init_, + (SUBR)STKInstrumentAdapter1::kontrol_, + (SUBR)STKInstrumentAdapter1::deinit_, 0, 0, + }, + { + (char *)"STKModalBar", sizeof(STKInstrumentAdapter), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter::init_, + (SUBR)STKInstrumentAdapter::kontrol_, + (SUBR)STKInstrumentAdapter::deinit_, 0, 0, + }, + { + (char *)"STKMoog", sizeof(STKInstrumentAdapter), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter::init_, + (SUBR)STKInstrumentAdapter::kontrol_, + (SUBR)STKInstrumentAdapter::deinit_, 0, 0, + }, + { + (char *)"STKPercFlut", sizeof(STKInstrumentAdapter), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter::init_, + (SUBR)STKInstrumentAdapter::kontrol_, + (SUBR)STKInstrumentAdapter::deinit_, 0, 0, + }, + { + (char *)"STKPlucked", sizeof(STKInstrumentAdapter1), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter1::init_, + (SUBR)STKInstrumentAdapter1::kontrol_, + (SUBR)STKInstrumentAdapter1::deinit_, 0, 0, + }, + { + (char *)"STKResonate", sizeof(STKInstrumentAdapter), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter::init_, + (SUBR)STKInstrumentAdapter::kontrol_, + (SUBR)STKInstrumentAdapter::deinit_, 0, 0, + }, + { + (char *)"STKRhodey", sizeof(STKInstrumentAdapter), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter::init_, + (SUBR)STKInstrumentAdapter::kontrol_, + (SUBR)STKInstrumentAdapter::deinit_, 0, 0, + }, + { + (char *)"STKSaxofony", sizeof(STKInstrumentAdapter1), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter1::init_, + (SUBR)STKInstrumentAdapter1::kontrol_, + (SUBR)STKInstrumentAdapter1::deinit_, 0, 0, + }, + { + (char *)"STKShakers", sizeof(STKInstrumentAdapter), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter::init_, + (SUBR)STKInstrumentAdapter::kontrol_, + (SUBR)STKInstrumentAdapter::deinit_, 0, 0, + }, + { + (char *)"STKSimple", sizeof(STKInstrumentAdapter), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter::init_, + (SUBR)STKInstrumentAdapter::kontrol_, + (SUBR)STKInstrumentAdapter::deinit_, 0, 0, + }, + { + (char *)"STKSitar", sizeof(STKInstrumentAdapter), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter::init_, + (SUBR)STKInstrumentAdapter::kontrol_, + (SUBR)STKInstrumentAdapter::deinit_, 0, 0, + }, + { + (char *)"STKStifKarp", sizeof(STKInstrumentAdapter1), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter1::init_, + (SUBR)STKInstrumentAdapter1::kontrol_, + (SUBR)STKInstrumentAdapter1::deinit_, 0, 0, + }, + { + (char *)"STKTubeBell", sizeof(STKInstrumentAdapter), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter::init_, + (SUBR)STKInstrumentAdapter::kontrol_, + (SUBR)STKInstrumentAdapter::deinit_, 0, 0, + }, + { + (char *)"STKVoicForm", sizeof(STKInstrumentAdapter), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter::init_, + (SUBR)STKInstrumentAdapter::kontrol_, + (SUBR)STKInstrumentAdapter::deinit_, 0, 0, + }, + { + (char *)"STKWhistle", sizeof(STKInstrumentAdapter), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter::init_, + (SUBR)STKInstrumentAdapter::kontrol_, + (SUBR)STKInstrumentAdapter::deinit_, 0, 0, + }, + { + (char *)"STKWurley", sizeof(STKInstrumentAdapter), 0, + (char *)"a", (char *)"iiJJJJJJJJJJJJJJJJ", + (SUBR)STKInstrumentAdapter::init_, + (SUBR)STKInstrumentAdapter::kontrol_, + (SUBR)STKInstrumentAdapter::deinit_, 0, 0, + }, + { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + }}; +} + +PUBLIC int32_t csoundModuleCreate(CSOUND *csound) { return 0; } + +#ifdef STK_EMBED_RAWWAVES +// Keep STK's file reader unchanged by staging the embedded bytes at module init. +static bool writeEmbeddedRawwaves(CSOUND *csound, const char *directory) { +#ifdef _WIN32 + const int result = _mkdir(directory); +#else + const int result = mkdir(directory, 0700); +#endif + if (result != 0 && errno != EEXIST) { + csound->Message(csound, "STK: cannot create raw-wave directory %s\n", directory); + return false; + } + for (const auto &wave : stkRawwaves) { + const std::string filename = std::string(directory) + "/" + wave.name; + FILE *file = fopen(filename.c_str(), "wb"); + if (!file) { + csound->Message(csound, "STK: cannot open embedded raw wave %s\n", filename.c_str()); + return false; + } + const bool written = fwrite(wave.bytes, 1, wave.size, file) == wave.size; + const int closed = fclose(file); + if (!written || closed != 0) { + csound->Message(csound, "STK: cannot write embedded raw wave %s\n", filename.c_str()); + return false; + } + } + return true; +} +#endif + +PUBLIC int32_t csoundModuleInit(CSOUND *csound) { + const char *path = csound->GetEnv(csound, "RAWWAVE_PATH"); +#ifdef STK_EMBED_RAWWAVES + if (path == NULL || strlen(path) == 0) { + path = ".csound-stk-rawwaves"; + if (!writeEmbeddedRawwaves(csound, path)) + return NOTOK; + } +#endif +#ifdef DEFAULT_RAWWAVE_PATH + if (path == NULL || strlen(path) == 0) + path = DEFAULT_RAWWAVE_PATH; +#endif + if (path == NULL || strlen(path) == 0) { + if (UNLIKELY(csound->GetDebug(csound))) + csound->Warning(csound, + "%s", Str("STK opcodes not available: define environment " + "variable RAWWAVE_PATH\n(points " + "to rawwaves directory) to use STK opcodes.")); + return 0; + } else { + Stk::setRawwavePath(path); + } + int32_t status = 0; + for (OENTRY *ep = &oentries[0]; ep->opname; ep++) { + status |= csound->AppendOpcode( + csound, ep->opname, ep->dsblksiz, ep->flags, ep->outypes, ep->intypes, + (int32_t (*)(CSOUND *, void *))ep->init, + (int32_t (*)(CSOUND *, void *))ep->perf, + (int32_t (*)(CSOUND *, void *))ep->deinit); + } + return status; +} + +PUBLIC int32_t csoundModuleDestroy(CSOUND *csound) { return 0; } + +PUBLIC int32_t csoundModuleInfo(void) { return CSOUND_MODULE_INFO; } diff --git a/src/stk/tests/stk_adapter_test.cpp b/src/stk/tests/stk_adapter_test.cpp new file mode 100644 index 00000000..d249d019 --- /dev/null +++ b/src/stk/tests/stk_adapter_test.cpp @@ -0,0 +1,75 @@ +#include +#include "STKInstrumentAdapter.hpp" + +// Observe the rate the instrument sees, without depending on any sound model. +struct SampleRateProbe { + double constructedAt = stk::Stk::sampleRate(); + double noteStartedAt = 0; + + SampleRateProbe() = default; + explicit SampleRateProbe(stk::StkFloat) {} + + void noteOn(stk::StkFloat, stk::StkFloat) { + noteStartedAt = stk::Stk::sampleRate(); + } +}; + +template +class StkAdapterTest : public ::testing::Test { +protected: + INSDS instrument = {}; + cs_float frequency = 440; + cs_float gain = 0.5; + Adapter adapter; + + void SetUp() override { + stk::Stk::setSampleRate(44100); + adapter.opds.insdshead = &instrument; + adapter.ifrequency = &frequency; + adapter.igain = &gain; + } + + void TearDown() override { + adapter.deinit(nullptr); + stk::Stk::setSampleRate(44100); + } +}; + +// Default construction (e.g. BeeThree) and a lowest-frequency argument (Saxofony). +using Adapters = ::testing::Types< + csound::STKInstrumentAdapter, + csound::STKInstrumentAdapter1>; +TYPED_TEST_SUITE(StkAdapterTest, Adapters); + +TYPED_TEST(StkAdapterTest, SetsSampleRateBeforeConstructingInstrument) { + this->instrument.esr = 48000; + ASSERT_EQ(this->adapter.init(nullptr), OK); + + EXPECT_DOUBLE_EQ(this->adapter.instrument->constructedAt, 48000); + EXPECT_DOUBLE_EQ(this->adapter.instrument->noteStartedAt, 48000); +} + +TYPED_TEST(StkAdapterTest, NewInstrumentsFollowSampleRateChanges) { + for (double rate : {48000, 96000, 44100}) { + SCOPED_TRACE(rate); + this->instrument.esr = rate; + ASSERT_EQ(this->adapter.init(nullptr), OK); + + EXPECT_DOUBLE_EQ(this->adapter.instrument->constructedAt, rate); + EXPECT_DOUBLE_EQ(this->adapter.instrument->noteStartedAt, rate); + this->adapter.deinit(nullptr); + } +} + +TYPED_TEST(StkAdapterTest, ReinitializationSetsSampleRateBeforeNoteOn) { + this->instrument.esr = 48000; + ASSERT_EQ(this->adapter.init(nullptr), OK); + auto *existing = this->adapter.instrument; + + // Another initialization may have changed STK's shared sample rate. + stk::Stk::setSampleRate(44100); + ASSERT_EQ(this->adapter.init(nullptr), OK); + + EXPECT_EQ(this->adapter.instrument, existing); + EXPECT_DOUBLE_EQ(this->adapter.instrument->noteStartedAt, 48000); +} diff --git a/src/websockets/CMakeLists.txt b/src/websockets/CMakeLists.txt new file mode 100644 index 00000000..8ef97b3c --- /dev/null +++ b/src/websockets/CMakeLists.txt @@ -0,0 +1,49 @@ +# Build the websocket opcode (websocket), which exposes Csound variables over a +# libwebsockets server. +option(BUILD_WEBSOCKET_OPCODE "Build the websocket opcode" ON) + +if(USE_VCPKG) + find_package(libwebsockets CONFIG) + find_package(libuv CONFIG) + set(WEBSOCKET_DEPS libwebsockets_FOUND libuv_FOUND) +else() + find_path(WEBSOCKETS_H libwebsockets.h /usr/local/include /usr/include) + find_library(websockets_library NAMES websockets) + set(WEBSOCKET_DEPS websockets_library WEBSOCKETS_H) + + if(APPLE) + find_library(ssl_library NAMES ssl) + find_library(crypto_library NAMES crypto) + list(APPEND WEBSOCKET_DEPS ssl_library crypto_library) + endif() +endif() + +check_deps(BUILD_WEBSOCKET_OPCODE ${WEBSOCKET_DEPS}) + +if(BUILD_WEBSOCKET_OPCODE) + make_plugin(websocketIO WebSocketOpcode.c) + target_include_directories(websocketIO PRIVATE ${CSOUND_INCLUDE_DIRS}) + target_compile_definitions(websocketIO PRIVATE BUILD_PLUGINS) + + if(USE_VCPKG) + target_link_libraries(websocketIO websockets) + if(TARGET libuv::uv_a) + target_link_libraries(websocketIO libuv::uv_a) + elseif(TARGET libuv::uv) + target_link_libraries(websocketIO libuv::uv) + else() + target_link_libraries(websocketIO uv_a) + endif() + else() + target_link_libraries(websocketIO ${websockets_library}) + target_include_directories(websocketIO PRIVATE ${WEBSOCKETS_H}) + endif() + + if(APPLE) + if(USE_VCPKG) + target_link_libraries(websocketIO z) + else() + target_link_libraries(websocketIO ${ssl_library} ${crypto_library} z) + endif() + endif() +endif() diff --git a/6.x/src/websockets/WebSocketOpcode.c b/src/websockets/WebSocketOpcode.c similarity index 84% rename from 6.x/src/websockets/WebSocketOpcode.c rename to src/websockets/WebSocketOpcode.c index a0964943..88849f3b 100644 --- a/6.x/src/websockets/WebSocketOpcode.c +++ b/src/websockets/WebSocketOpcode.c @@ -24,12 +24,10 @@ */ #include "WebSocketOpcode.h" -#ifndef _MSC_VER -# include -#endif #include -#include #include +#include +#include typedef enum ArgumentType { @@ -47,7 +45,7 @@ typedef enum ArgumentType struct OpcodeArgument{ void *dataPointer; - MYFLT *argumentPointer; + cs_float *argumentPointer; bool receivedData; ArgumentType argumentType; AUXCH auxillaryMemory; @@ -82,16 +80,22 @@ static const size_t ringBufferItemsCount = 2048 * 16; void WebSocketOpcode_initialiseWebSocket(WebSocketOpcode *self, CSOUND *csound); int32_t WebSocketOpcode_finish(CSOUND *csound, void *opaqueReference); void WebSocketOpcode_initialiseArguments(WebSocketOpcode *self, CSOUND *csound); -ArgumentType WebSocketOpcode_getArgumentType(CSOUND *csound, MYFLT *argument); +ArgumentType WebSocketOpcode_getArgumentType(CSOUND *csound, cs_float *argument); int32_t WebSocketOpcode_initialise(CSOUND *csound, WebSocketOpcode *self) { + if (UNLIKELY(self->INOCOUNT + self->OUTOCOUNT > 20)) { + + return csound->InitError(csound, + Str("websocket: too many arguments " + "(maximum of 20 outputs and inputs)")); + } + self->inputArgumentCount = self->INOCOUNT - 1; self->outputArgumentCount = self->OUTOCOUNT; self->csound = csound; WebSocketOpcode_initialiseArguments(self, csound); WebSocketOpcode_initialiseWebSocket(self, csound); - csound->RegisterDeinitCallback(csound, self, WebSocketOpcode_finish); return OK; } @@ -141,12 +145,6 @@ void WebSocketOpcode_receiveOutputArgumentData(CSOUND *csound, currentArgument->dataPointer, currentArgument->itemsCount); - if (currentArgument->argumentType == STRING_VAR) { - - STRINGDAT *string = (STRINGDAT *)currentArgument->argumentPointer; - string->size = (int32_t)strlen(currentArgument->dataPointer); - } - currentArgument->receivedData = false; } @@ -166,10 +164,23 @@ int32_t WebSocketOpcode_finish(CSOUND *csound, void *opaqueReference) WebSocketOpcode *self = opaqueReference; self->isRunning = false; - csound->JoinThread(self->webSocket->processThread); + if (self->webSocket != NULL) { + + if (self->webSocket->context != NULL) { - lws_cancel_service(self->webSocket->context); - lws_context_destroy(self->webSocket->context); + lws_cancel_service(self->webSocket->context); + } + + if (self->webSocket->processThread != NULL) { + + csound->JoinThread(self->webSocket->processThread); + } + + if (self->webSocket->context != NULL) { + + lws_context_destroy(self->webSocket->context); + } + } for (i = 0; i < self->outputArgumentCount; ++i) { @@ -181,9 +192,12 @@ int32_t WebSocketOpcode_finish(CSOUND *csound, void *opaqueReference) csound->DestroyCircularBuffer(csound, self->inputArguments[i].circularBuffer); } - csound->Free(csound, self->webSocket->protocols); - csound->Free(csound, self->webSocket->messageBuffer); - csound->Free(csound, self->webSocket); + if (self->webSocket != NULL) { + + csound->Free(csound, self->webSocket->protocols); + csound->Free(csound, self->webSocket->messageBuffer); + csound->Free(csound, self->webSocket); + } if (self->inputArgumentCount > 0) { csound->Free(csound, self->inputArguments); @@ -208,7 +222,7 @@ int32_t WebSocketOpcode_getArrayElementCount(ARRAYDAT *array) } -void WebSocketOpcode_allocateStringArgument(MYFLT *argument, +void WebSocketOpcode_allocateStringArgument(cs_float *argument, OpcodeArgument *argumentArrayItem, CSOUND *csound, bool isInputArgument) { @@ -222,7 +236,7 @@ void WebSocketOpcode_allocateStringArgument(MYFLT *argument, } else { - if (UNLIKELY(string->size != 0)) { + if (UNLIKELY(string->data != NULL && string->data[0] != '\0')) { csound->Die(csound, Str("websocket: error output string variable %s must not " @@ -232,8 +246,17 @@ void WebSocketOpcode_allocateStringArgument(MYFLT *argument, else { argumentArrayItem->itemsCount = stringVarMaximumBytesCount; - string->data = csound->ReAlloc(csound, string->data, - stringVarMaximumBytesCount); + if (string->data == NULL) { + + string->data = csound->Calloc(csound, stringVarMaximumBytesCount); + } + else { + + string->data = csound->ReAlloc(csound, string->data, + stringVarMaximumBytesCount); + } + string->size = stringVarMaximumBytesCount; + string->refcount = 0; memset(string->data, 0, stringVarMaximumBytesCount); } } @@ -250,7 +273,7 @@ void WebSocketOpcode_allocateStringArgument(MYFLT *argument, argumentArrayItem->readBuffer = argumentArrayItem->auxillaryMemory.auxp; } -void WebSocketOpcode_allocateArrayArgument(MYFLT *argument, +void WebSocketOpcode_allocateArrayArgument(cs_float *argument, OpcodeArgument *argumentArrayItem, CSOUND *csound) { @@ -278,7 +301,7 @@ void WebSocketOpcode_allocateArrayArgument(MYFLT *argument, argumentArrayItem->readBuffer = argumentArrayItem->auxillaryMemory.auxp; } -void WebSocketOpcode_allocateVariableArgument(MYFLT *argument, +void WebSocketOpcode_allocateVariableArgument(cs_float *argument, OpcodeArgument *argumentArrayItem, CSOUND *csound, int32_t bytesCount) { @@ -297,7 +320,7 @@ void WebSocketOpcode_initialiseArgumentsArray(CSOUND *csound, WebSocketOpcode *self, OpcodeArgument *argumentsArray, size_t argumentsCount, - MYFLT **arguments, + cs_float **arguments, bool areInputArguments) { uint32_t i; @@ -308,8 +331,8 @@ void WebSocketOpcode_initialiseArgumentsArray(CSOUND *csound, argumentArrayItem->argumentType = WebSocketOpcode_getArgumentType(csound, arguments[i]); argumentArrayItem->name = - areInputArguments ? csound->GetInputArgName((void *)self, i + 1) - : csound->GetOutputArgName((void *)self, i); + areInputArguments ? GetInputArgName(&self->h, i + 1) + : GetOutputArgName(&self->h, i); switch (argumentsArray[i].argumentType) { @@ -332,14 +355,14 @@ void WebSocketOpcode_initialiseArgumentsArray(CSOUND *csound, WebSocketOpcode_allocateVariableArgument(arguments[i], argumentArrayItem, csound, - sizeof(MYFLT) * csound->GetKsmps(csound)); + sizeof(cs_float) * GetLocalKsmps(&self->h)); break; } case IRATE_VAR: case KRATE_VAR: { WebSocketOpcode_allocateVariableArgument(arguments[i], argumentArrayItem, - csound, sizeof(MYFLT)); + csound, sizeof(cs_float)); break; } default: { @@ -434,7 +457,7 @@ void WebSocketOpcode_handleServerWritable(struct lws *websocket, return; } - int32_t inputIndex = 0; + int32_t inputIndex = protocol->id - INPUT_OFFSET; OpcodeArgument *argument = &self->inputArguments[inputIndex]; @@ -494,20 +517,32 @@ void WebSocketOpcode_handleReceive(struct lws *websocket, WebSocketOpcode *self, return; } - if (UNLIKELY(argument->argumentType == STRING_VAR - && - argument->bytesCount > stringVarMaximumBytesCount)) { + int32_t writtenItems; - csound->Message(csound, Str("websocket: received string message from " - "%s is too large, message dumped"), - protocol->name); + if (argument->argumentType == STRING_VAR) { - return; + if (UNLIKELY(inputDataSize >= stringVarMaximumBytesCount)) { + + csound->Message(csound, Str("websocket: received string message from " + "%s is too large, message dumped"), + protocol->name); + + return; + } + + char *stagingBuffer = (char *)argument->readBuffer; + memset(stagingBuffer, 0, stringVarMaximumBytesCount); + memcpy(stagingBuffer, inputData, inputDataSize); + writtenItems = + csound->WriteCircularBuffer(csound, argument->circularBuffer, + stagingBuffer, argument->itemsCount); } + else { - int32_t writtenItems = - csound->WriteCircularBuffer(csound, argument->circularBuffer, - inputData, argument->itemsCount); + writtenItems = + csound->WriteCircularBuffer(csound, argument->circularBuffer, + inputData, argument->itemsCount); + } argument->receivedData = true; if (UNLIKELY(writtenItems == 0)) { @@ -555,7 +590,7 @@ static int32_t Websocket_callback(struct lws *websocket, case LWS_CALLBACK_SERVER_WRITEABLE: { void *messageBuffer = - (void *)&self->webSocket->messageBuffer[LWS_SEND_BUFFER_PRE_PADDING]; + (void *)&self->webSocket->messageBuffer[LWS_PRE]; WebSocketOpcode_handleServerWritable(websocket, self, csound, messageBuffer); break; @@ -581,14 +616,19 @@ static int32_t Websocket_callback(struct lws *websocket, uintptr_t WebSocketOpcode_processThread(void *opaquePointer) { + int32_t i; WebSocketOpcode *self = opaquePointer; while (self->isRunning == 1) { lws_service(self->webSocket->context, 10); - lws_callback_on_writable_all_protocol(self->webSocket->context, - &self->webSocket->protocols[0]); + + for (i = 0; i < self->inputArgumentCount; ++i) { + + lws_callback_on_writable_all_protocol(self->webSocket->context, + &self->webSocket->protocols[i]); + } } return 0; @@ -631,12 +671,10 @@ void WebSocketOpcode_initialiseWebSocket(WebSocketOpcode *self, CSOUND *csound) self->webSocket->info.gid = -1; self->webSocket->info.uid = -1; - lws_set_log_level(LLL_DEBUG, NULL); + lws_set_log_level(LLL_ERR | LLL_WARN, NULL); self->webSocket->context = lws_create_context(&self->webSocket->info); self->webSocket->messageBuffer = - csound->Calloc(csound, LWS_SEND_BUFFER_PRE_PADDING + - (sizeof(char) * writeBufferBytesCount) + - LWS_SEND_BUFFER_POST_PADDING); + csound->Calloc(csound, LWS_PRE + (sizeof(char) * writeBufferBytesCount)); if (UNLIKELY(self->webSocket->context == NULL)) { csound->Die(csound, @@ -649,9 +687,10 @@ void WebSocketOpcode_initialiseWebSocket(WebSocketOpcode *self, CSOUND *csound) csound->CreateThread(WebSocketOpcode_processThread, self); } -ArgumentType WebSocketOpcode_getArgumentType(CSOUND *csound, MYFLT *argument) +ArgumentType WebSocketOpcode_getArgumentType(CSOUND *csound, cs_float *argument) { - const CS_TYPE *csoundType = csound->GetTypeForArg((void *)argument); + IGN(csound); + const CS_TYPE *csoundType = GetTypeForArg((void *)argument); const char *type = csoundType->varTypeName; ArgumentType argumentType = UNKNOWN; @@ -696,14 +735,16 @@ ArgumentType WebSocketOpcode_getArgumentType(CSOUND *csound, MYFLT *argument) static OENTRY localops[] = { { - .opname = "websocket", - .dsblksiz = sizeof(WebSocketOpcode), - .thread = 3, - .outypes = "*", - .intypes = "i.", - .iopadr = (SUBR)WebSocketOpcode_initialise, - .kopadr = (SUBR)WebSocketOpcode_process, - .aopadr = NULL + (char *)"websocket", + sizeof(WebSocketOpcode), + 0, + (char *)"*", + (char *)"i*", + (SUBR)WebSocketOpcode_initialise, + (SUBR)WebSocketOpcode_process, + (SUBR)WebSocketOpcode_finish, + NULL, + 0 } }; diff --git a/6.x/src/websockets/WebSocketOpcode.h b/src/websockets/WebSocketOpcode.h similarity index 89% rename from 6.x/src/websockets/WebSocketOpcode.h rename to src/websockets/WebSocketOpcode.h index 0747cfe2..8df08b8d 100644 --- a/6.x/src/websockets/WebSocketOpcode.h +++ b/src/websockets/WebSocketOpcode.h @@ -24,17 +24,18 @@ 02110-1301 USA */ +#ifndef WEBSOCKET_OPCODE_H +#define WEBSOCKET_OPCODE_H #include #include "csdl.h" -//#include "csound.h" -#ifdef WIN32 +#if defined(WIN32) || defined(_WIN32) #define WIN32_LEAN_AND_MEAN #include -void usleep(unsigned int usec) +static void usleep(unsigned int usec) { HANDLE timer; LARGE_INTEGER ft; @@ -47,9 +48,10 @@ void usleep(unsigned int usec) CloseHandle(timer); } +#else +# include #endif - #ifdef __cplusplus extern "C" { @@ -60,7 +62,7 @@ extern "C" typedef struct WebSocketOpcode { OPDS h; - MYFLT *arguments[20]; + cs_float *arguments[20]; int32_t inputArgumentCount; int32_t outputArgumentCount; WebSocket *webSocket; @@ -75,3 +77,5 @@ extern "C" #ifdef __cplusplus } #endif + +#endif /* WEBSOCKET_OPCODE_H */ diff --git a/src/wiimote/CMakeLists.txt b/src/wiimote/CMakeLists.txt new file mode 100644 index 00000000..4a9b1387 --- /dev/null +++ b/src/wiimote/CMakeLists.txt @@ -0,0 +1,12 @@ +# Build the wiimote opcodes (requires the wiiuse library) +option(BUILD_WIIMOTE_OPCODES "Build the Wiimote opcodes" ON) + +find_path(WIIUSE_INCLUDE_DIR wiiuse.h /usr/local/include /usr/include) +find_library(WIIUSE_LIB wiiuse) +check_deps(BUILD_WIIMOTE_OPCODES WIIUSE_INCLUDE_DIR WIIUSE_LIB) + +if(BUILD_WIIMOTE_OPCODES) + make_plugin(wiimote wiimote.c ${WIIUSE_LIB}) + target_include_directories(wiimote PRIVATE ${WIIUSE_INCLUDE_DIR} ${CSOUND_INCLUDE_DIRS}) + target_compile_definitions(wiimote PRIVATE BUILD_PLUGINS) +endif() diff --git a/6.x/src/wiimote/wii_mac.h b/src/wiimote/wii_mac.h similarity index 100% rename from 6.x/src/wiimote/wii_mac.h rename to src/wiimote/wii_mac.h diff --git a/6.x/src/wiimote/wiimote.c b/src/wiimote/wiimote.c similarity index 71% rename from 6.x/src/wiimote/wiimote.c rename to src/wiimote/wiimote.c index 2e62eacc..df2ac5ec 100644 --- a/6.x/src/wiimote/wiimote.c +++ b/src/wiimote/wiimote.c @@ -22,40 +22,42 @@ */ /* wiimote.c */ +#include + #include "csdl.h" #include "wiiuse.h" /* Uses WIIUSE library which is LGPL */ #define MAX_WIIMOTES 4 #include "wii_mac.h" typedef struct { - MYFLT axis_x_min; /* 0 -> 255 */ - MYFLT axis_x_scale; /* 1 */ - MYFLT axis_y_min; - MYFLT axis_y_scale; - MYFLT axis_z_min; - MYFLT axis_z_scale; - MYFLT pitch_min; /* -90 -> +90 */ - MYFLT pitch_scale; /* 1 */ - MYFLT roll_min; - MYFLT roll_scale; - /* MYFLT tilt_z_min; */ - /* MYFLT tilt_z_scale; */ - MYFLT joy_min; - MYFLT joy_scale; - MYFLT nunchuk_pitch_min; - MYFLT nunchuk_pitch_scale; - MYFLT nunchuk_roll_min; - MYFLT nunchuk_roll_scale; - /* MYFLT naxis_z_min; */ - /* MYFLT naxis_z_max; */ + cs_float axis_x_min; /* 0 -> 255 */ + cs_float axis_x_scale; /* 1 */ + cs_float axis_y_min; + cs_float axis_y_scale; + cs_float axis_z_min; + cs_float axis_z_scale; + cs_float pitch_min; /* -90 -> +90 */ + cs_float pitch_scale; /* 1 */ + cs_float roll_min; + cs_float roll_scale; + /* cs_float tilt_z_min; */ + /* cs_float tilt_z_scale; */ + cs_float joy_min; + cs_float joy_scale; + cs_float nunchuk_pitch_min; + cs_float nunchuk_pitch_scale; + cs_float nunchuk_roll_min; + cs_float nunchuk_roll_scale; + /* cs_float naxis_z_min; */ + /* cs_float naxis_z_max; */ } wiirange_t; typedef struct { OPDS h; - MYFLT *res; - MYFLT *kControl; - MYFLT *num; + cs_float *res; + cs_float *kControl; + cs_float *num; /* ------------------------------------- */ wiimote **wii; wiirange_t *wiir; @@ -64,9 +66,9 @@ typedef struct { typedef struct { OPDS h; - MYFLT *kControl; - MYFLT *kValue; - MYFLT *num; + cs_float *kControl; + cs_float *kValue; + cs_float *num; /* ------------------------------------- */ wiimote **wii; wiirange_t *wiir; @@ -74,10 +76,10 @@ typedef struct { typedef struct { OPDS h; - MYFLT *iControl; - MYFLT *iMin; - MYFLT *iMax; - MYFLT *num; + cs_float *iControl; + cs_float *iMin; + cs_float *iMax; + cs_float *num; } WIIRANGE; #ifndef WIIMOTE_STATE_CONNECTED @@ -100,15 +102,24 @@ int32_t wiimote_find(CSOUND *csound, WIIMOTE *p) csound->CreateGlobalVariable(csound, "wiiMote", MAX_WIIMOTES*sizeof(wiimote*)); wiimotes = (wiimote**)csound->QueryGlobalVariable(csound, "wiiMote"); + if (UNLIKELY(wiimotes == NULL)) + return csound->InitError(csound, "%s", + Str("wiiconnect: cannot allocate wiimote state")); } wiirange = (wiirange_t *)csound->QueryGlobalVariable(csound, "wiiRange"); if (wiirange == NULL) { csound->CreateGlobalVariable(csound, "wiiRange", MAX_WIIMOTES*sizeof(wiirange_t)); wiirange = (wiirange_t *)csound->QueryGlobalVariable(csound, "wiiRange"); + if (UNLIKELY(wiirange == NULL)) + return csound->InitError(csound, "%s", + Str("wiiconnect: cannot allocate wii range state")); } { /* Use our copy not his */ wiimote** ww = wiiuse_init(MAX_WIIMOTES); + if (UNLIKELY(ww == NULL)) + return csound->InitError(csound, "%s", + Str("wiiconnect: cannot initialise wiimotes")); memcpy(wiimotes, ww, MAX_WIIMOTES*sizeof(wiimote*)); free(ww); } @@ -186,6 +197,10 @@ int32_t wiimote_poll(CSOUND *csound, WIIMOTE *p) default: break; } + if (UNLIKELY(!(wiimotes[i]->state & WIIMOTE_STATE_CONNECTED))) { + *p->res = FL(0.0); + return OK; + } } *p->res = FL(1.0); return OK; @@ -213,8 +228,8 @@ int32_t wii_data(CSOUND *csound, WIIMOTE *p) wiirange_t *wiir = p->wiir; int32_t n = (int32_t)*p->num; int32_t kontrol = (int32_t)(*p->kControl+FL(0.5)); - if (UNLIKELY(n>=MAX_WIIMOTES || !(wii[n]->state & WIIMOTE_STATE_CONNECTED))) { - printf("state of wii %d is %x\n", n, wii[n]->state); + if (UNLIKELY(n<0 || n>=MAX_WIIMOTES || wii[n]==NULL || + !(wii[n]->state & WIIMOTE_STATE_CONNECTED))) { return csound->PerfError(csound, &(p->h), Str("wiimote %d does not exist"), n); } @@ -223,62 +238,63 @@ int32_t wii_data(CSOUND *csound, WIIMOTE *p) "tilt=[%f %f];\nforce=(%f %f %f)\n", 100.0*wii[n]->battery_level, wii[n]->btns, wiir[n].pitch_min+wiir[n].pitch_scale*(FL(90.0)+ - (MYFLT)wii[n]->orient.pitch), + (cs_float)wii[n]->orient.pitch), wiir[n].roll_min+wiir[n].roll_scale*(FL(90.0)- - (MYFLT)wii[n]->orient.roll), + (cs_float)wii[n]->orient.roll), wii[n]->gforce.x, wii[n]->gforce.y, wii[n]->gforce.z); *p->res = FL(0.0); return OK; } if (kontrol>0 && kontrol<17) { - *p->res = (MYFLT)IS_JUST_PRESSED(wii[n], 1<<(kontrol-1)); + *p->res = (cs_float)IS_JUST_PRESSED(wii[n], 1<<(kontrol-1)); + return OK; } if (kontrol>100 && kontrol<117) { - *p->res = (MYFLT)IS_PRESSED(wii[n], 1<<(kontrol-101)); + *p->res = (cs_float)IS_PRESSED(wii[n], 1<<(kontrol-101)); return OK; } if (kontrol>200 && kontrol<217) { - *p->res = (MYFLT)IS_HELD(wii[n], 1<<(kontrol-201)); + *p->res = (cs_float)IS_HELD(wii[n], 1<<(kontrol-201)); return OK; } if (kontrol>300 && kontrol<317) { - *p->res = (MYFLT)IS_RELEASED(wii[n], 1<<(kontrol-301)); + *p->res = (cs_float)IS_RELEASED(wii[n], 1<<(kontrol-301)); return OK; } else switch (kontrol) { case WII_BUTTONS: - *p->res = (MYFLT)(wii[n]->btns&WIIMOTE_BUTTON_ALL); + *p->res = (cs_float)(wii[n]->btns&WIIMOTE_BUTTON_ALL); return OK; /* case 17: */ - /* *p->res = wiir[n].axis_x_min+wiir[n].axis_x_scale*(MYFLT)wii[n]->axis.x; */ + /* *p->res = wiir[n].axis_x_min+wiir[n].axis_x_scale*(cs_float)wii[n]->axis.x; */ /* return OK; */ /* case 18: */ - /* *p->res = wiir[n].axis_y_min+wiir[n].axis_y_scale*(MYFLT)wii[n]->axis.y; */ + /* *p->res = wiir[n].axis_y_min+wiir[n].axis_y_scale*(cs_float)wii[n]->axis.y; */ /* return OK; */ /* case 19: */ - /* *p->res = wiir[n].axis_z_min+wiir[n].axis_z_scale*(MYFLT)wii[n]->axis.z; */ + /* *p->res = wiir[n].axis_z_min+wiir[n].axis_z_scale*(cs_float)wii[n]->axis.z; */ /* return OK; */ case WII_PITCH: /* I think the sign is wrong so negated; inplies negative is down */ *p->res = wiir[n].pitch_min+ - wiir[n].pitch_scale*(FL(90.0)-(MYFLT)wii[n]->orient.pitch); + wiir[n].pitch_scale*(FL(90.0)-(cs_float)wii[n]->orient.pitch); return OK; case WII_ROLL: *p->res = wiir[n].roll_min+ - wiir[n].roll_scale*(FL(90.0)+(MYFLT)wii[n]->orient.roll); + wiir[n].roll_scale*(FL(90.0)+(cs_float)wii[n]->orient.roll); return OK; /* case 22: */ /* *p->res = wiir[n].tilt_z_min+ - wiir[n].tilt_z_scale*(FL(90.0)-(MYFLT)wii[n]->tilt.z); */ + wiir[n].tilt_z_scale*(FL(90.0)-(cs_float)wii[n]->tilt.z); */ /* return OK; */ case WII_FORCE_X: - *p->res = (MYFLT)wii[n]->gforce.x; + *p->res = (cs_float)wii[n]->gforce.x; return OK; case WII_FORCE_Y: - *p->res = (MYFLT)wii[n]->gforce.y; + *p->res = (cs_float)wii[n]->gforce.y; return OK; case WII_FORCE_Z: - *p->res = (MYFLT)wii[n]->gforce.z; + *p->res = (cs_float)wii[n]->gforce.z; return OK; case WII_FORCE_TOTAL: *p->res = SQRT(wii[n]->gforce.x*wii[n]->gforce.x+ @@ -286,43 +302,43 @@ int32_t wii_data(CSOUND *csound, WIIMOTE *p) wii[n]->gforce.z*wii[n]->gforce.z); return OK; case WII_BATTERY: - *p->res = FL(100.0)*(MYFLT)wii[n]->battery_level; + *p->res = FL(100.0)*(cs_float)wii[n]->battery_level; return OK; case WII_NUNCHUK_ANG: - *p->res = (MYFLT)wii[n]->exp.nunchuk.js.ang; + *p->res = (cs_float)wii[n]->exp.nunchuk.js.ang; return OK; case WII_NUNCHUK_MAG: - *p->res = (MYFLT)wii[n]->exp.nunchuk.js.mag; + *p->res = (cs_float)wii[n]->exp.nunchuk.js.mag; return OK; case WII_NUNCHUK_PITCH: *p->res = wiir[n].nunchuk_pitch_min+ wiir[n].nunchuk_pitch_scale*(FL(90.0)- - (MYFLT)wii[n]->exp.nunchuk.orient.pitch); + (cs_float)wii[n]->exp.nunchuk.orient.pitch); return OK; case WII_NUNCHUK_ROLL: *p->res = wiir[n].nunchuk_roll_min+ wiir[n].nunchuk_roll_scale*(FL(90.0)- - (MYFLT)wii[n]->exp.nunchuk.orient.roll); + (cs_float)wii[n]->exp.nunchuk.orient.roll); return OK; /* case 32: */ - /* *p->res = (MYFLT)wii[n]->exp.nunchuk.axis.z; */ + /* *p->res = (cs_float)wii[n]->exp.nunchuk.axis.z; */ /* return OK; */ case WII_NUNCHUK_Z: - *p->res = (MYFLT)((wii[n]->exp.nunchuk.btns & NUNCHUK_BUTTON_Z)== - NUNCHUK_BUTTON_Z); + *p->res = (cs_float)((wii[n]->exp.nunchuk.btns & NUNCHUK_BUTTON_Z)== + NUNCHUK_BUTTON_Z); return OK; case WII_NUNCHUK_C: - *p->res = (MYFLT)((wii[n]->exp.nunchuk.btns & NUNCHUK_BUTTON_C)== - NUNCHUK_BUTTON_C); + *p->res = (cs_float)((wii[n]->exp.nunchuk.btns & NUNCHUK_BUTTON_C)== + NUNCHUK_BUTTON_C); return OK; case WII_IR1_X: - *p->res = (MYFLT)wii[n]->ir.x; + *p->res = (cs_float)wii[n]->ir.x; return OK; case WII_IR1_Y: - *p->res = (MYFLT)wii[n]->ir.y; + *p->res = (cs_float)wii[n]->ir.y; return OK; case WII_IR1_Z: - *p->res = (MYFLT)wii[n]->ir.z; + *p->res = (cs_float)wii[n]->ir.z; return OK; } return NOTOK; @@ -347,7 +363,8 @@ int32_t wii_send(CSOUND *csound, WIIMOTES *p) { wiimote **wii = p->wii; int32_t num = (int32_t)*p->num; - if (UNLIKELY(!(wii[num]->state & WIIMOTE_STATE_CONNECTED))) + if (UNLIKELY(num < 0 || num >= MAX_WIIMOTES || wii[num]==NULL || + !(wii[num]->state & WIIMOTE_STATE_CONNECTED))) return csound->PerfError(csound, &(p->h), "%s", Str("Not open")); switch ((int32_t)(*p->kControl+FL(0.5))) { /* case 1: */ @@ -368,10 +385,15 @@ int32_t wii_send(CSOUND *csound, WIIMOTES *p) int32_t wiimote_range(CSOUND *csound, WIIRANGE *p) { - wiirange_t *wiirange = + int32_t n = (int32_t)*p->num; + wiirange_t *wiiranges = (wiirange_t *)csound->QueryGlobalVariable(csound, "wiiRange"); - if (UNLIKELY(wiirange==NULL)) + wiirange_t *wiirange; + if (UNLIKELY(wiiranges==NULL)) return csound->InitError(csound, "%s", Str("No wii range")); + if (UNLIKELY(n<0 || n>=MAX_WIIMOTES)) + return csound->InitError(csound, Str("wiirange: invalid wiimote %d"), n); + wiirange = &wiiranges[n]; switch ((int32_t)(*p->iControl+FL(0.5))) { /* case 17: */ /* wiirange->axis_x_min = *p->iMin; */ @@ -414,12 +436,15 @@ int32_t wiimote_range(CSOUND *csound, WIIRANGE *p) #define S(x) sizeof(x) -static OENTRY wiimote_localops[] = { - {"wiiconnect", S(WIIMOTE), 0, 3, "i", "oo", (SUBR)wiimote_find, - (SUBR)wiimote_poll }, - {"wiidata", S(WIIMOTE), 0, 3, "k", "ko", (SUBR)wii_data_init, (SUBR)wii_data }, - {"wiisend", S(WIIMOTES), 0, 3, "", "kko", (SUBR)wii_data_inits, (SUBR)wii_send }, - {"wiirange", S(WIIRANGE), 0, 1, "", "iiio", (SUBR)wiimote_range, NULL, NULL } +static OENTRY localops[] = { + {"wiiconnect", S(WIIMOTE), 0, "i", "oo", (SUBR)wiimote_find, + (SUBR)wiimote_poll, NULL, NULL, 0 }, + {"wiidata", S(WIIMOTE), 0, "k", "ko", (SUBR)wii_data_init, + (SUBR)wii_data, NULL, NULL, 0 }, + {"wiisend", S(WIIMOTES), 0, "", "kko", (SUBR)wii_data_inits, + (SUBR)wii_send, NULL, NULL, 0 }, + {"wiirange", S(WIIRANGE), 0, "", "iiio", (SUBR)wiimote_range, + NULL, NULL, NULL, 0 } }; -LINKAGE_BUILTIN(wiimote_localops) +LINKAGE diff --git a/tests/plugin_load_test.cpp b/tests/plugin_load_test.cpp new file mode 100644 index 00000000..88711d4f --- /dev/null +++ b/tests/plugin_load_test.cpp @@ -0,0 +1,70 @@ +#include "csound.h" +#include "gtest/gtest.h" +#include + +class PluginLoadTests : public ::testing::Test { +protected: + CSOUND *csound = nullptr; + + void SetUp() override { + // Skip the default plugin directories; rc files may still add libraries. + csound = csoundCreate(nullptr, ""); + ASSERT_NE(csound, nullptr); + csoundCreateMessageBuffer(csound, 0); + } + + void TearDown() override { + csoundDestroy(csound); + } + + std::string messages() { + std::string result; + while (csoundGetMessageCnt(csound)) { + result += csoundGetFirstMessage(csound); + csoundPopFirstMessage(csound); + } + return result; + } + + void compile(const char *plugin, const char *orchestra) { + std::string option = std::string("--opcode-lib=\"") + plugin + "\""; + ASSERT_EQ(csoundSetOption(csound, option.c_str()), 0); + const int result = csoundCompileOrc(csound, orchestra, 0); + const std::string log = messages(); + EXPECT_EQ(result, 0) << log; + // An installed copy can supply the opcodes if the requested library fails. + const std::string loaded = std::string("\nloaded ") + plugin + "\n"; + EXPECT_NE(("\n" + log).find(loaded), std::string::npos) << log; + } +}; + +#ifdef CSOUND_TEST_CHUA +TEST_F(PluginLoadTests, ChuaRegistersItsOpcode) { + compile(CSOUND_TEST_CHUA, R"( + instr 1 + a1, a2, a3 chuap 0.00945, 7.5, 2e-7, 0.00105, 0, -0.00121, 1.76e-5, 1.5e-8, 0, -0.1, 0.1, 5e-6 + endin + )"); +} +#endif + +#ifdef CSOUND_TEST_LINEAR_ALGEBRA +TEST_F(PluginLoadTests, LinearAlgebraRegistersItsOpcodes) { + compile(CSOUND_TEST_LINEAR_ALGEBRA, R"( + instr 1 + ivector la_i_vr_create 3 + endin + )"); +} +#endif + +#ifdef CSOUND_TEST_FLUID +TEST_F(PluginLoadTests, FluidRegistersItsOpcodes) { + compile(CSOUND_TEST_FLUID, R"( + instr 1 + iengine fluidEngine + aleft, aright fluidOut iengine + endin + )"); +} +#endif diff --git a/vcpkg b/vcpkg new file mode 160000 index 00000000..9e593bb1 --- /dev/null +++ b/vcpkg @@ -0,0 +1 @@ +Subproject commit 9e593bb18ea69cc5095e012465dcd675a822ed0d diff --git a/6.x/vcpkg.json b/vcpkg.json similarity index 55% rename from 6.x/vcpkg.json rename to vcpkg.json index 21ef94d8..ed84db37 100644 --- a/6.x/vcpkg.json +++ b/vcpkg.json @@ -1,17 +1,15 @@ { "$schema": "https://raw.githubusercontent.com/microsoft/vcpkg-tool/main/docs/vcpkg.schema.json", "name": "csound-plugins", - "version-string": "6.16.2", - "builtin-baseline": "f6a5d4e8eb7476b8d7fc12a56dff300c1c986131", + "version-string": "7.0.0", + "builtin-baseline": "9e593bb18ea69cc5095e012465dcd675a822ed0d", "dependencies": [ - "ableton-link", "eigen3", - "fltk", "fluidsynth", + "libpng", "hdf5", - "libstk", + "libuv", "libwebsockets", - "mp3lame", - "zlib" + { "name": "opencl", "platform": "windows" } ] -} \ No newline at end of file +} diff --git a/wasm/README.md b/wasm/README.md new file mode 100644 index 00000000..3ad2d0b7 --- /dev/null +++ b/wasm/README.md @@ -0,0 +1,107 @@ +# @csound/plugins + +WASM opcode plugins for the double-precision Csound 7 browser engine. + +This package includes Chua (`chuap`), image, linear algebra, and STK. This build +disables FluidSynth, HDF5, Python, WebSockets, device, and GPU plugins. +Csound 6 is not supported. + +## Runtime dependency + +Builds use the shared-memory plugin SDK and toolchain from `@csound/wasm-bin`. +Tests use the published `@csound/browser` runtime. [package.json](../package.json) and +[package-lock.json](../package-lock.json) record the versions used together. +Rebuild after updating these dependencies. Plugins built for the old, +non-shared engine cannot load in the shared-memory runtime. + +## Use + +Use a compatible `@csound/browser` release, such as the version pinned in +[package.json](../package.json): + +```js +import { Csound } from "@csound/browser"; +import { plugins } from "@csound/plugins"; + +const csound = await Csound({ withPlugins: [plugins.chua, plugins.image] }); +``` + +Use `plugins.stk` to load the STK instruments. Its single `stkops.wasm` file +contains all bundled raw-wave samples. At initialization, the plugin writes +them to Csound's virtual filesystem and sets its wave path. No extra downloads +or `RAWWAVE_PATH` setup are needed. An explicit `RAWWAVE_PATH` overrides the +embedded samples; that directory must already contain the files STK needs. + +The exports are asset URL strings. Bundlers must copy the WASM files referenced +by `new URL(..., import.meta.url)`. You can also serve files from +`@csound/plugins/dist/` yourself and pass their URLs to `withPlugins`. + +To use `--opcode-lib`, write the plugin to Csound's virtual filesystem before +compiling your CSD. This does not need `withPlugins`: + +```js +const csound = await Csound(); +const response = await fetch(plugins.image); +if (!response.ok) throw new Error(`Plugin download failed: ${response.status}`); +await csound.fs.writeFile("/image.wasm", new Uint8Array(await response.arrayBuffer())); +await csound.setOption("--opcode-lib=/image.wasm"); +await csound.compileCSD(csd); +``` + +You can also put the option in ``. Its path refers to the virtual +filesystem, not a URL. + +## Build and test + +Install Nix and a Node.js version that meets [package.json](../package.json)'s +`engines` requirement, then run: + +```sh +npm ci +npm run build +npm test +npm pack +``` + +Nix pins the toolchain and builds the CMake targets in the repository root with Ninja. +Dependencies use threaded static PIC builds. Each plugin exports its TLS size, +alignment, and initializer so the engine can set up thread-local storage. +The C runtime imports the engine's allocator; it does not link its own heap. +STK uses a separate libc++ build with C++ exceptions +and a static WASM unwinder. Other C++ plugins keep exceptions disabled. +CMake's `STK_EMBED_RAWWAVES` option embeds the samples and defaults to on for WASM. +Native builds keep using the installed raw-wave directory by default. +To use another Csound 7 SDK, set `CSOUND_PLUGIN_SDK` to an absolute archive path. +Set `NIX_SYSTEM=x86_64-linux` when using a Linux remote builder from macOS. + +Tests load all plugins together, render Chua and STK, compute a vector norm, and +save and reload a PNG through the WASI filesystem. STK tests also cover loading +it alone, overriding its wave path, and catching a missing-sample error. +Packing runs the tests and includes the WASM files and their license notices. +For work on the browser loader, +`CSOUND_BROWSER_URL` can point to an absolute file URL of a local browser bundle; +leave it unset to test the published package as CI does. + +## Updating dependencies + +Keep version pins in these files, rather than copying them into commands or docs: + +- [package.json](../package.json) and [package-lock.json](../package-lock.json): + update npm dependencies with `npm install --save-dev --save-exact @`. + The Nix build reads the SDK version, URL, and hash from the lockfile. +- [nixpkgs.nix](nixpkgs.nix): the Nix toolchain revision and hash, shared by the + plugin build and its static dependencies. + +After an update, run the build, test, and pack commands above. The WASM workflow +uses the `csound` Cachix cache and uploads builds when `CACHIX_AUTH_TOKEN` is +available. Fork PRs read from the cache without the secret. Native builds use +GitHub Actions caches. + +## npm releases + +Native and npm packages share the version in `package.json`; see the +[release process](../README.md#releasing) for version changes and Git tags. +Run the build, test, and pack commands against the published browser package +with `CSOUND_BROWSER_URL` unset. Publish a prerelease with +`npm publish --tag beta --access public`, or a stable release with +`npm publish --access public`, from that build. No workflow publishes to npm. diff --git a/wasm/default.nix b/wasm/default.nix new file mode 100644 index 00000000..7d1b3ec4 --- /dev/null +++ b/wasm/default.nix @@ -0,0 +1,166 @@ +{ system ? builtins.currentSystem +, pkgs ? import (import ./nixpkgs.nix) { inherit system; } +, csoundSdkArchive ? null +}: +let + lock = builtins.fromJSON (builtins.readFile ../package-lock.json); + wasmBin = lock.packages."node_modules/@csound/wasm-bin"; + wasmPackage = pkgs.runCommand "csound-wasm-bin-${wasmBin.version}" {} '' + mkdir -p $out + tar -xzf ${pkgs.fetchurl { url = wasmBin.resolved; hash = wasmBin.integrity; }} --strip-components=1 -C $out + ''; + # Use the same threaded WASI toolchain and memory limit as the engine. + toolchain = import "${wasmPackage}/src/wasi-realtime-toolchain.nix" { inherit pkgs; }; + maxMemory = builtins.fromJSON (builtins.readFile "${wasmPackage}/src/shared-memory-max.txt"); + wasi = toolchain.stdenv; + # Nix's WASI wrapper otherwise forces -fno-exceptions after CMake's flags. + # Let each target select its exception mode instead. + wasiCxx = pkgs.overrideCC wasi (wasi.cc.override (old: { + nixSupport = old.nixSupport // { + cc-cflags = builtins.filter (flag: flag != "-fno-exceptions") old.nixSupport.cc-cflags; + }; + })); + libc = toolchain.libc.overrideAttrs (old: { + NIX_CFLAGS_COMPILE = (old.NIX_CFLAGS_COMPILE or []) ++ [ + "-fPIC" "-mllvm" "-wasm-enable-sjlj" "-mllvm" "-wasm-use-legacy-eh=false" + ]; + # Use the engine's allocator. wasi-libc also skips its fixed symbol list + # for this mode; PIC adds imports absent from that non-PIC list. + makeFlags = (old.makeFlags or []) ++ [ "MALLOC_IMPL=none" ]; + preFixup = ""; + dontStrip = true; + }); + libcxx = toolchain.libcxx.overrideAttrs (old: { + cmakeFlags = old.cmakeFlags ++ [ + "-DCMAKE_POSITION_INDEPENDENT_CODE=ON" + "-DLIBCXX_ENABLE_FILESYSTEM=ON" + ]; + dontStrip = true; + }); + libunwindSource = pkgs.pkgsCross.wasi32.llvmPackages.libunwind.src; + libunwind = wasi.mkDerivation { + pname = "libunwind-wasm-pic"; + inherit (libcxx) version; + src = ./libunwind; + nativeBuildInputs = [ pkgs.cmake pkgs.ninja ]; + cmakeFlags = [ "-DLIBUNWIND_SOURCE_DIR=${libunwindSource}/libunwind" ]; + dontStrip = true; + }; + libcxxExceptions = (libcxx.override { stdenv = wasiCxx; }).overrideAttrs (old: { + pname = "libcxx-wasm-exceptions"; + cmakeFlags = old.cmakeFlags ++ [ + "-DLIBCXX_ENABLE_EXCEPTIONS=ON" + "-DLIBCXX_ADDITIONAL_LIBRARIES=" + "-DLIBCXXABI_ENABLE_EXCEPTIONS=ON" + ]; + preConfigure = (old.preConfigure or "") + '' + cmakeFlagsArray+=("-DCMAKE_CXX_FLAGS=-fexceptions -fwasm-exceptions -mllvm -wasm-use-legacy-eh=false -I${libunwindSource}/libunwind/include") + ''; + }); + sdkArchive = if csoundSdkArchive == null then null else builtins.path { + path = csoundSdkArchive; + name = "csound-plugin-sdk.tar.gz"; + }; + sdk = pkgs.runCommand "csound-wasm-plugin-sdk-${wasmBin.version}" {} '' + mkdir -p $out + ${if csoundSdkArchive == null then '' + tar -xzf ${wasmPackage}/lib/csound-plugin-sdk.tar.gz --strip-components=1 -C $out + '' else '' + tar -xzf ${sdkArchive} --strip-components=1 -C $out + ''} + ''; + zlib = wasi.mkDerivation { + pname = "zlib-wasm-pic"; + inherit (pkgs.zlib) version src; + nativeBuildInputs = [ pkgs.cmake pkgs.ninja ]; + cmakeFlags = [ "-DCMAKE_POSITION_INDEPENDENT_CODE=ON" "-DCMAKE_INSTALL_LIBDIR=lib" "-DZLIB_BUILD_EXAMPLES=OFF" ]; + # zlib 1.3.1 has no CMake switch to omit its shared library. Build only + # the static target so it does not link the toolchain's non-PIC libc. + ninjaFlags = [ "zlibstatic" ]; + installPhase = '' + runHook preInstall + install -Dm644 libzlibstatic.a $out/lib/libzlibstatic.a + install -Dm644 ../zlib.h $out/include/zlib.h + install -Dm644 zconf.h $out/include/zconf.h + runHook postInstall + ''; + dontStrip = true; + }; + libpng = wasi.mkDerivation { + pname = "libpng-wasm-pic"; + inherit (pkgs.libpng) version src; + nativeBuildInputs = [ pkgs.cmake pkgs.ninja ]; + buildInputs = [ zlib ]; + cmakeFlags = [ + "-DUNIX=ON" + "-DCMAKE_POSITION_INDEPENDENT_CODE=ON" + "-DCMAKE_INSTALL_LIBDIR=lib" + "-DPNG_SHARED=OFF" "-DPNG_STATIC=ON" "-DPNG_TESTS=OFF" "-DPNG_TOOLS=OFF" + "-DZLIB_LIBRARY=${zlib}/lib/libzlibstatic.a" + ]; + NIX_CFLAGS_COMPILE = [ "-mllvm" "-wasm-enable-sjlj" "-mllvm" "-wasm-use-legacy-eh=false" ]; + dontStrip = true; + }; +in wasiCxx.mkDerivation { + pname = "csound-plugins-wasm"; + version = (builtins.fromJSON (builtins.readFile ../package.json)).version; + src = pkgs.lib.sourceByRegex ../. [ + "CMakeLists\\.txt" + "cmake" + "cmake/.*" + "src" + "src/.*" + "tests" + "tests/.*" + ]; + nativeBuildInputs = [ pkgs.cmake pkgs.ninja ]; + buildInputs = [ libpng zlib ]; + dontStrip = true; + cmakeFlags = [ + "-DCMAKE_POLICY_VERSION_MINIMUM=3.5" + "-DCMAKE_BUILD_TYPE=Release" + "-DCSOUND_INCLUDE_DIR=${sdk}/include/csound" + "-DWASM_MAX_MEMORY=${toString maxMemory}" + "-DWASM_LIBC=${libc}/lib/libc.a" + "-DWASM_SETJMP=${libc}/lib/libsetjmp.a" + "-DWASM_LIBCXX=${libcxx}/lib/libc++.a" + "-DWASM_CXX_INCLUDE_DIR=${libcxx.dev}/include/c++/v1" + "-DWASM_LIBCXX_EXCEPTIONS=${libcxxExceptions}/lib/libc++.a" + "-DWASM_CXX_EXCEPTIONS_INCLUDE_DIR=${libcxxExceptions.dev}/include/c++/v1" + "-DWASM_LIBUNWIND=${libunwind}/lib/libunwind.a" + "-DEIGEN3_INCLUDE_DIR=${pkgs.eigen}/include/eigen3" + "-DZLIB_LIBRARY=${zlib}/lib/libzlibstatic.a" + "-DPNG_LIBRARY=${libpng}/lib/libpng.a" + "-DBUILD_CHUA_OPCODES=ON" + "-DBUILD_IMAGE_OPCODES=ON" + "-DBUILD_LINEAR_ALGEBRA_OPCODES=ON" + "-DBUILD_STK_OPCODES=ON" + "-DBUILD_FLUID_OPCODES=OFF" + "-DBUILD_HDF5_OPCODES=OFF" + "-DBUILD_WEBSOCKET_OPCODE=OFF" + "-DBUILD_PYTHON_OPCODES=OFF" + "-DBUILD_WIIMOTE_OPCODES=OFF" + "-DBUILD_P5GLOVE_OPCODES=OFF" + "-DBUILD_CUDA_OPCODES=OFF" + "-DBUILD_OPENCL_OPCODES=OFF" + ]; + postInstall = '' + mkdir -p $out/share/licenses/wasi-libc $out/share/licenses/eigen + cp ${libc.src}/LICENSE* $out/share/licenses/wasi-libc/ + cp ${libc.src}/libc-top-half/musl/COPYRIGHT $out/share/licenses/wasi-libc/musl.txt + cp ${libc.src}/libc-bottom-half/cloudlibc/LICENSE $out/share/licenses/wasi-libc/cloudlibc.txt + cp ${libc.src}/fts/musl-fts/COPYING $out/share/licenses/wasi-libc/musl-fts.txt + cp ${libcxx.src}/libcxx/LICENSE.TXT $out/share/licenses/libcxx.txt + cp ${libcxx.src}/libcxxabi/LICENSE.TXT $out/share/licenses/libcxxabi.txt + cp ${libunwindSource}/libunwind/LICENSE.TXT $out/share/licenses/libunwind.txt + cp ${../src/stk/LICENSE} $out/share/licenses/stk.txt + cp ${pkgs.pkgsCross.wasi32.llvmPackages.compiler-rt.src}/compiler-rt/LICENSE.TXT $out/share/licenses/compiler-rt.txt + cp ${pkgs.eigen.src}/COPYING* $out/share/licenses/eigen/ + sed -n '1,/^=*[*][/]$/p' ${../src/linear_algebra/gmm/gmm.h} > $out/share/licenses/gmm.txt + tar -xOf ${pkgs.libpng.src} libpng-${pkgs.libpng.version}/LICENSE > $out/share/licenses/libpng.txt + tar -xOf ${pkgs.zlib.src} zlib-${pkgs.zlib.version}/LICENSE > $out/share/licenses/zlib.txt + for plugin in chua image linear_algebra stkops; do + test -s "$out/lib/$plugin.wasm" + done + ''; +} diff --git a/wasm/libunwind/CMakeLists.txt b/wasm/libunwind/CMakeLists.txt new file mode 100644 index 00000000..c722bd23 --- /dev/null +++ b/wasm/libunwind/CMakeLists.txt @@ -0,0 +1,12 @@ +cmake_minimum_required(VERSION 3.16) +project(CsoundWasmUnwind C) + +# LLVM 21's main build also selects assembly files that do not support WASM. +# The VM unwinds the stack; this source supplies the C++ exception interface. +add_library(unwind STATIC "${LIBUNWIND_SOURCE_DIR}/src/Unwind-wasm.c") +set_target_properties(unwind PROPERTIES POSITION_INDEPENDENT_CODE ON) +target_compile_features(unwind PRIVATE c_std_11) +target_compile_options(unwind PRIVATE -fwasm-exceptions "SHELL:-mllvm -wasm-use-legacy-eh=false") +target_compile_definitions(unwind PRIVATE _LIBUNWIND_HIDE_SYMBOLS) +target_include_directories(unwind PRIVATE "${LIBUNWIND_SOURCE_DIR}/include") +install(TARGETS unwind ARCHIVE DESTINATION lib) diff --git a/wasm/nixpkgs.nix b/wasm/nixpkgs.nix new file mode 100644 index 00000000..89b6f907 --- /dev/null +++ b/wasm/nixpkgs.nix @@ -0,0 +1,5 @@ +# Keep in sync with csound/csound's platform/wasm-wasi/scripts/nixpkgs-pin.sh. +builtins.fetchTarball { + url = "https://github.com/NixOS/nixpkgs/archive/4db2c220f32fd162658ed1b7bb2f46a82996ddbe.tar.gz"; + sha256 = "08b1k0zrbal2if94bph2ap9f73zlplsh05a65jbdkp5ipycmg92s"; +} diff --git a/wasm/scripts/build.sh b/wasm/scripts/build.sh new file mode 100644 index 00000000..87afa3c9 --- /dev/null +++ b/wasm/scripts/build.sh @@ -0,0 +1,24 @@ +#!/usr/bin/env bash +set -euo pipefail +cd "$(dirname "$0")/../.." + +args=(--show-trace -o result-wasm) +if [[ -n "${NIX_SYSTEM:-}" ]]; then + args+=(--argstr system "$NIX_SYSTEM") +fi +if [[ -n "${CSOUND_PLUGIN_SDK:-}" ]]; then + args+=(--argstr csoundSdkArchive "$CSOUND_PLUGIN_SDK") +fi +nix-build wasm/default.nix "${args[@]}" +staging=$(mktemp -d .wasm-dist.XXXXXX) +trap 'rm -rf "$staging"' EXIT +for plugin in chua image linear_algebra stkops; do + install -m 644 "result-wasm/lib/$plugin.wasm" "$staging/$plugin.wasm" +done +cp -R result-wasm/share/licenses "$staging/licenses" +chmod -R u+w "$staging/licenses" +if [[ -d dist ]]; then + chmod -R u+w dist +fi +rm -rf dist +mv "$staging" dist diff --git a/wasm/tests/runtime.test.js b/wasm/tests/runtime.test.js new file mode 100644 index 00000000..3b69d970 --- /dev/null +++ b/wasm/tests/runtime.test.js @@ -0,0 +1,181 @@ +import assert from "node:assert/strict"; +import { readFile } from "node:fs/promises"; +import test from "node:test"; +import { plugins } from "../../index.js"; + +// The in-process browser API needs these names, but no audio device or DOM. +globalThis.self = globalThis; +globalThis.window = globalThis; +const { libcsound } = await import(process.env.CSOUND_BROWSER_URL || "@csound/browser/dist/csound.js"); + +const binaries = await Promise.all(Object.values(plugins).map((url) => readFile(new URL(url)))); + +const document = (body) => ` + +-n -d -m0 + + +sr = 48000 +ksmps = 32 +nchnls = 1 +0dbfs = 1 +instr 1 +${body} +endin + + +i 1 0 0.02 +e + +`; + +/** + * Load plugin bytes into Csound 7 and render the given instrument body. + * Run prepare before compilation to set options or supply files. + * Call check with the API, Csound instance, and peak sample magnitude before + * destroying the instance. + */ +async function run(body, check, withPlugins = binaries, prepare = () => {}) { + const errors = []; + const originalError = console.error; + console.error = (...args) => errors.push(args.join(" ")); + let api; + try { + api = await libcsound({ withPlugins }); + } finally { + console.error = originalError; + } + assert.deepEqual(errors, [], "every plugin must load without missing imports"); + assert.ok(api.csoundGetVersion() >= 7000 && api.csoundGetVersion() < 8000); + assert.equal(api.csoundGetSizeOfMYFLT(), 8); + const csound = api.csoundCreate(); + try { + await prepare(api, csound); + assert.equal(api.csoundCompileCSD(csound, document(body)), 0); + assert.equal(api.csoundStart(csound), 0); + let peak = 0; + let blocks = 0; + while (api.csoundPerformKsmps(csound) === 0) { + const samples = new Float64Array(api.getMemory().buffer, api.csoundGetSpout(csound), 32); + for (const sample of samples) { + assert.ok(Number.isFinite(sample), "audio samples must be finite"); + peak = Math.max(peak, Math.abs(sample)); + } + assert.ok(++blocks < 100, "score must finish"); + } + assert.ok(blocks > 0); + await check(api, csound, peak); + } finally { + api.csoundDestroy(csound); + } +} + +test("all plugins are WASI side modules with the shared-memory TLS exports", async () => { + for (const bytes of binaries) { + const module = await WebAssembly.compile(bytes); + const imports = WebAssembly.Module.imports(module); + const exports = WebAssembly.Module.exports(module); + assert.ok(WebAssembly.Module.customSections(module, "dylink.0").length > 0); + assert.ok(imports.some(({ kind, name }) => kind === "memory" && name === "memory")); + assert.ok(imports.some(({ kind }) => kind === "table")); + assert.ok(exports.some(({ name }) => name === "__wasm_call_ctors")); + for (const [name, kind] of [ + ["__wasm_init_tls", "function"], + ["__tls_size", "global"], + ["__tls_align", "global"], + ]) { + assert.ok( + exports.some((entry) => entry.name === name && entry.kind === kind), + `${name} must be exported as a ${kind}`, + ); + } + assert.ok(exports.some(({ name }) => ["csoundModuleInit", "csound_opcode_init"].includes(name))); + assert.ok(!exports.some(({ name }) => name === "_start")); + } +}); + +test("chua produces finite, nonzero audio alongside the other plugins", async () => { + await run(` + a1, a2, a3 chuap 0.018, 0, 1e-7, 1/1500, -0.0008, -0.0005, 1, 1e-8, 0, 0, 0.1, 1e-5 + out a3 * 0.1 + `, (_, __, peak) => assert.ok(peak > 0)); +}); + +for (const instrument of ["STKMandolin", "STKModalBar", "STKBeeThree", "STKDrummer"]) { + test(`${instrument} renders with embedded waves alongside the other plugins`, async () => { + await run(` + a1 ${instrument} 220, 0.5 + out a1 + `, (_, __, peak) => assert.ok(peak > 0)); + }); +} + +test("STK loads on its own without a raw-wave path or extra files", async () => { + const stk = await readFile(new URL(plugins.stk)); + await run("a1 STKMandolin 220, 0.5\nout a1", (_, __, peak) => assert.ok(peak > 0), [stk]); +}); + +test("RAWWAVE_PATH uses caller-supplied samples without overwriting them", async () => { + const silentWave = new Uint8Array(512); + await run("a1 STKModalBar 220, 0.5\nout a1", async (api, csound, peak) => { + assert.equal(peak, 0); + assert.deepEqual(await api.readFile(csound, "/custom-waves/marmstk1.raw"), silentWave); + }, binaries, async (api, csound) => { + await api.writeFile(csound, "/custom-waves/marmstk1.raw", silentWave); + assert.equal(api.csoundSetOption(csound, "--env:RAWWAVE_PATH=/custom-waves"), 0); + }); +}); + +test("STK reports a missing sample through Csound instead of throwing out of WASM", async () => { + const api = await libcsound({ withPlugins: binaries }); + const csound = api.csoundCreate(); + const messages = []; + const originalLog = console.log; + console.log = (...args) => messages.push(args.join(" ")); + try { + assert.equal(api.csoundSetOption(csound, "--env:RAWWAVE_PATH=/missing-stk-waves"), 0); + assert.equal(api.csoundCompileCSD(csound, document("a1 STKMandolin 220, 0.5\nout a1")), 0); + assert.equal(api.csoundStart(csound), 0); + let result = 0; + for (let block = 0; block < 100 && result === 0; block++) { + result = api.csoundPerformKsmps(csound); + } + assert.notEqual(result, 0, "the failed score must finish"); + assert.ok(messages.some((message) => message.includes("STK: FileRead::open")), messages.join("\n")); + } finally { + api.csoundDestroy(csound); + console.log = originalLog; + } +}); + +test("linear algebra computes a vector norm", async () => { + await run(` + iv la_i_vr_create 2 + iv la_i_vr_set 0, 3 + iv la_i_vr_set 1, 4 + inorm la_i_norm_euclid_vr iv + chnset inorm, "norm" + `, (api, csound) => assert.equal(api.csoundGetControlChannel(csound, "norm"), 5)); +}); + +test("image writes and reads a PNG through the WASI filesystem", async () => { + await run(` + ih imagecreate 2, 3 + imagesetpixel ih, 0, 0, 1, 0, 0 + imagesave ih, "roundtrip.png" + iload imageload "roundtrip.png" + iw, ihgt imagesize iload + kred, kgreen, kblue imagegetpixel iload, 0, 0 + chnset iw, "width" + chnset ihgt, "height" + chnset kred, "red" + chnset kgreen, "green" + `, async (api, csound) => { + assert.equal(api.csoundGetControlChannel(csound, "width"), 2); + assert.equal(api.csoundGetControlChannel(csound, "height"), 3); + assert.equal(api.csoundGetControlChannel(csound, "red"), 1); + assert.equal(api.csoundGetControlChannel(csound, "green"), 0); + const bytes = await api.readFile(csound, "roundtrip.png"); + assert.deepEqual([...bytes.slice(0, 8)], [137, 80, 78, 71, 13, 10, 26, 10]); + }); +}); diff --git a/6.x/x64-windows-csound.cmake b/x64-windows-csound.cmake similarity index 84% rename from 6.x/x64-windows-csound.cmake rename to x64-windows-csound.cmake index dc862f9a..ad3e3915 100644 --- a/6.x/x64-windows-csound.cmake +++ b/x64-windows-csound.cmake @@ -1,7 +1,7 @@ set(VCPKG_TARGET_ARCHITECTURE x64) set(VCPKG_CRT_LINKAGE dynamic) -if(${PORT} MATCHES "fluidsynth|glib|zlib|pcre|libffi|gettext|libiconv|portmidi|portaudio|liblo") +if(${PORT} MATCHES "fluidsynth|glib|zlib|pcre|libffi|gettext|libiconv|portmidi|portaudio|liblo|opencl") set(VCPKG_LIBRARY_LINKAGE dynamic) else() set(VCPKG_LIBRARY_LINKAGE static)