Enable ARM build

This commit is contained in:
Dmitry Makarenko 2025-05-02 12:40:20 +03:00
parent 13f5e0a8bf
commit 533ea2d3ed
26 changed files with 2302 additions and 0 deletions

50
.github/workflows/build.yml vendored Normal file
View File

@ -0,0 +1,50 @@
name: build
on:
push:
branches:
- main
pull_request:
workflow_dispatch:
env:
ARTIFACT_PATH: ${{ github.workspace }}/artifact
BUILD_PATH: ${{ github.workspace }}/build
PACKAGE_PATH: ${{ github.workspace }}/package
SOURCE_PATH: ${{ github.workspace }}/sources
STAGING_PATH: ${{ github.workspace }}/staging
jobs:
build-openvino-plugins-arm64:
runs-on: macos-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
path: ${{ github.workspace }}
- name: Install Apple codesigning certificates
uses: apple-actions/import-codesign-certs@v2
env:
P12_FILE_BASE64: ${{ secrets.APPLE_CERTIFICATE }}
P12_PASSWORD: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
if: ${{ env.P12_FILE_BASE64 != '' && env.P12_PASSWORD != '' }}
with:
p12-file-base64: ${{ secrets.APPLE_CERTIFICATE }}
p12-password: ${{ secrets.APPLE_CERTIFICATE_PASSWORD }}
- name: Build audacity for arm64
run: |
scripts/prepare-build.sh
scripts/build-arm64.sh
env:
APPLE_CODESIGN_IDENTITY: ${{ secrets.APPLE_CODESIGN_IDENTITY }}
- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: mod-openvino-arm64
path: ${{ env.STAGING_PATH }}/Audacity-OpenVINO.pkg

14
.gitignore vendored
View File

@ -1,5 +1,19 @@
build/*
sources/*
staging/*
packages/*
*.pkg
.DS_Store
### Xcode ###
xcuserdata/
*.xcscmblueprint
*.xccheckout
*.xcodeproj/*
!*.xcodeproj/project.pbxproj
!*.xcodeproj/xcshareddata/
!*.xcodeproj/project.xcworkspace/
!*.xcworkspace/contents.xcworkspacedata
/*.gcno
**/xcshareddata/WorkspaceSettings.xcsettings

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>Installation Complete</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.6;
color: #333;
padding: 40px;
background-color: #fff;
}
h1 {
font-size: 24px;
margin-bottom: 20px;
}
p, li {
font-size: 14px;
margin-bottom: 12px;
}
ul {
padding-left: 20px;
}
</style>
</head>
<body>
<h1>Installation Complete</h1>
<p>The Audacity OpenVINO Plugin has been successfully installed.</p>
<p>Launch Audacity to start using the new features!</p>
</body>
</html>

View File

@ -0,0 +1,31 @@
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>License Agreement</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.6;
color: #333;
padding: 40px;
background-color: #fff;
}
h1 {
font-size: 24px;
margin-bottom: 20px;
}
p, li {
font-size: 14px;
margin-bottom: 12px;
}
ul {
padding-left: 20px;
}
</style>
</head>
<body>
<h1>License Agreement</h1>
<p>This software is provided under the GNU General Public License v3 (GPLv3).</p>
<p>Please review the full license terms before proceeding.</p>
</body>
</html>

View File

@ -0,0 +1,35 @@
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>Important</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.6;
color: #333;
padding: 40px;
background-color: #fff;
}
h1 {
font-size: 24px;
margin-bottom: 20px;
}
p, li {
font-size: 14px;
margin-bottom: 12px;
}
ul {
padding-left: 20px;
}
</style>
</head>
<body>
<h1>Please read</h1>
<ul>
<li>OpenVINO toolkit runs AI models locally and requires AI models to be downloaded.</li>
<li>Models will be downloaded during installation based on your configuration.</li>
<li><strong>No progress bar will be shown.</strong> The installer will simply appear to pause for a log period of time.</li>
<li>Disk space requirements vary depending on model selection. Some models are few GB each.</li>
</ul>
</body>
</html>

View File

@ -0,0 +1,33 @@
<!DOCTYPE html>
<html>
<head><meta charset="UTF-8"><title>Welcome to Audacity OpenVINO Plugin</title>
<style>
body {
font-family: -apple-system, BlinkMacSystemFont, "Helvetica Neue", Helvetica, Arial, sans-serif;
font-size: 14px;
line-height: 1.6;
color: #333;
padding: 40px;
background-color: #fff;
}
h1 {
font-size: 24px;
margin-bottom: 20px;
}
p, li {
font-size: 14px;
margin-bottom: 12px;
}
ul {
padding-left: 20px;
}
</style>
</head>
<body>
<h1>Audacity OpenVINO Plugin</h1>
<p>This plugin adds AI-based features to Audacity using Intel's OpenVINO toolkit.</p>
<p><strong>Note:</strong> During installation, the plugin will download additional AI models.</p>
<p>These downloads can be large and may take a significant amount of time depending on your internet speed and model selection.</p>
<p>The installer will appear to "hang" on "Running package scripts" during this process — this is expected.</p>
</body>
</html>

74
installer/Scripts/postinstall Executable file
View File

@ -0,0 +1,74 @@
#!/bin/bash
# This script runs after the installation
# It updates the Audacity configuration file to enable the OpenVINO module
MODULE_NAME="mod-openvino"
MODULE_PATH="/Library/Application Support/audacity/modules/${MODULE_NAME}.so"
MODULE_DATETIME=$(stat -f "%m" "$MODULE_PATH" | xargs -I{} date -r {} +"%Y-%m-%dT%H:%M:%S")
SCRIPT_DIR="$(dirname "$0")"
APP_PATH="$SCRIPT_DIR/ResourceDownloader.app"
LOGGED_IN_USER=$(stat -f "%Su" /dev/console)
# Block until the app quits
sudo -u "$LOGGED_IN_USER" open -W "$APP_PATH"
update_config_section() {
local cfg_file="$1"
local section="$2"
local key="$3"
local value="$4"
local tmp_file="${cfg_file}.tmp"
original_owner=$(stat -f "%u" "$cfg_file")
original_group=$(stat -f "%g" "$cfg_file")
awk -v section="$section" -v key="$key" -v value="$value" '
BEGIN {
found_section = 0
found_key = 0
}
$0 == "[" section "]" {
found_section = 1
print
next
}
found_section && $0 ~ "^" key{
print key "=" value
found_key = 1
found_section = 0
next
}
found_section && !found_key && /^\[.*\]/ {
print key "=" value
found_key = 1
found_section = 0
}
{ print }
END { }
' "$cfg_file" > "$tmp_file" && mv "$tmp_file" "$cfg_file"
chown "$original_owner":"$original_group" "$cfg_file"
}
# Loop through all user home directories in /Users (excluding system users)
for USER_HOME in /Users/*; do
# Only act on real user dirs
[ -d "$USER_HOME" ] || continue
[ -f "$USER_HOME/.zshrc" ] || [ -f "$USER_HOME/.bash_profile" ] || continue
CFG_PATH="$USER_HOME/Library/Application Support/audacity/audacity.cfg"
CFG_PATH1="$USER_HOME/Library/Application Support/audacity/_audacity.cfg"
# Skip if the config doesn't exist
[ -f "$CFG_PATH" ] || continue
echo "Updating $CFG_PATH"
update_config_section "$CFG_PATH" "Module" "mod-openvino" "1"
update_config_section "$CFG_PATH" "ModuleDateTime" "mod-openvino" "$MODULE_DATETIME"
update_config_section "$CFG_PATH" "ModulePath" "mod-openvino" "$MODULE_PATH"
done
exit 0

View File

@ -0,0 +1,27 @@
<?xml version="1.0" encoding="utf-8" standalone="no"?>
<installer-gui-script minSpecVersion="1">
<title>Audacity OpenVINO module</title>
<welcome file="Welcome.html"/>
<license file="License.html"/>
<readme file="ReadMe.html"/>
<conclusion file="Conclusion.html"/>
<options customize="never" require-scripts="false" rootVolumeOnly="true" hostArchitectures="arm64,x86_64"/>
<choices-outline>
<line choice="module"/>
</choices-outline>
<choice id="module" title="Module">
<pkg-ref id="org.audacity.openvino-module"/>
</choice>
<pkg-ref id="org.audacity.openvino-module">
#openvino-module.pkg
<must-close>
<app id="org.audacityteam.audacity"/>
</must-close>
</pkg-ref>
</installer-gui-script>

View File

@ -0,0 +1,10 @@
--- modules/CMakeLists.txt.orig 2025-04-24 14:23:00.161466283 +0300
+++ modules/CMakeLists.txt 2025-04-24 14:23:19.002006483 +0300
@@ -9,6 +9,7 @@
# The list of module sub-folders is ordered so that each folder occurs after any
# others that it depends on
set( FOLDERS
+ mod-openvino
etc
import-export
track-ui

View File

@ -0,0 +1,28 @@
--- mod-openvino/htdemucs.h.orig 2024-12-20 16:59:14.000000000 +0300
+++ mod-openvino/htdemucs.h 2025-04-24 14:58:08.662787586 +0300
@@ -4,7 +4,7 @@
#include <memory>
#include <vector>
-namespace torch
+namespace at
{
class Tensor;
}
@@ -41,11 +41,11 @@
std::shared_ptr< HTDemucs_impl > _impl;
#endif
std::shared_ptr< HTDemucs_openvino_impl > _impl_ov;
- bool _apply_model_0(torch::Tensor& mix, torch::Tensor& out, int64_t shifts = 1, bool split = true, double overlap = 0.25, double transition_power = 1., int64_t static_shifts = 1);
- bool _apply_model_1(torch::Tensor& mix, torch::Tensor& out, int64_t shifts = 1, bool split = true, double overlap = 0.25, double transition_power = 1., int64_t static_shifts = 1);
- bool _apply_model_2(TensorChunk& mix, torch::Tensor& out, bool split = true, double overlap = 0.25, double transition_power = 1., int64_t static_shifts = 1);
- bool _apply_model_3(TensorChunk& mix, torch::Tensor& out);
- bool _actually_run_model(torch::Tensor& mix_tensor, torch::Tensor& x);
+ bool _apply_model_0(at::Tensor& mix, at::Tensor& out, int64_t shifts = 1, bool split = true, double overlap = 0.25, double transition_power = 1., int64_t static_shifts = 1);
+ bool _apply_model_1(at::Tensor& mix, at::Tensor& out, int64_t shifts = 1, bool split = true, double overlap = 0.25, double transition_power = 1., int64_t static_shifts = 1);
+ bool _apply_model_2(TensorChunk& mix, at::Tensor& out, bool split = true, double overlap = 0.25, double transition_power = 1., int64_t static_shifts = 1);
+ bool _apply_model_3(TensorChunk& mix, at::Tensor& out);
+ bool _actually_run_model(at::Tensor& mix_tensor, at::Tensor& x);
int64_t _shifts = 0;
int64_t _offsets = 0;

View File

@ -0,0 +1,11 @@
--- mod-openvino/musicgen/music_gen_decoder_cl.cpp.orig 2025-04-24 14:58:41.839354735 +0300
+++ mod-openvino/musicgen/music_gen_decoder_cl.cpp 2025-04-24 15:00:48.024587074 +0300
@@ -521,7 +521,7 @@
{
//slice the new key values into the existing past_key_vals buffer using OpenCL.
std::array<size_t, 3> srcOrigin = { 0, 0, 0 }; // Start at the beginning of the source buffer
- std::array<size_t, 3> dstOrigin = { 0, _past_length, 0 };
+ std::array<size_t, 3> dstOrigin = { 0, (unsigned long) _past_length, 0 };
// Size of one element
std::array<size_t, 3> region = { sizeof(ov::float16) * past_key_values_shape[3], 1, past_key_values_shape[0] * past_key_values_shape[1] };

View File

@ -0,0 +1,105 @@
diff --color -ruN mod-openvino.orig/OVAudioSR.cpp mod-openvino/OVAudioSR.cpp
--- mod-openvino.orig/OVAudioSR.cpp 2025-04-26 15:56:46.861144303 +0300
+++ mod-openvino/OVAudioSR.cpp 2025-04-26 16:00:47.323405438 +0300
@@ -52,7 +52,7 @@
{
std::vector<std::string> available_models;
- auto model_folder = wxFileName(FileNames::BaseDir(), wxT("openvino-models")).GetFullPath();
+ auto model_folder = wxFileName(FileNames::DataDir(), wxT("openvino-models")).GetFullPath();
model_folder = wxFileName(model_folder, wxT("audiosr")).GetFullPath();
//make sure that all of the 'base' models are present
@@ -607,7 +607,7 @@
//todo: Right now we're looking for the model in the 'BaseDir' (which is top-level folder of Audacity install)
// This might be okay, but some users may not have permissions to place models there. So, also look in
// DataDir(), which is the path to C:\Users\<user>\AppData\Roaming\audacity.
- auto model_folder_wx = wxFileName(FileNames::BaseDir(), wxT("openvino-models")).GetFullPath();
+ auto model_folder_wx = wxFileName(FileNames::DataDir(), wxT("openvino-models")).GetFullPath();
auto model_folder = audacity::ToUTF8(wxFileName(model_folder_wx, wxT("audiosr")).GetFullPath());
FilePath cache_folder = FileNames::MkDir(wxFileName(FileNames::DataDir(), wxT("openvino-model-cache")).GetFullPath());
diff --color -ruN mod-openvino.orig/OVMusicGenerationLLM.cpp mod-openvino/OVMusicGenerationLLM.cpp
--- mod-openvino.orig/OVMusicGenerationLLM.cpp 2025-04-26 15:56:46.862479864 +0300
+++ mod-openvino/OVMusicGenerationLLM.cpp 2025-04-26 16:00:47.329323234 +0300
@@ -70,7 +70,7 @@
{
std::vector<std::string> available_models;
- auto model_folder = wxFileName(FileNames::BaseDir(), wxT("openvino-models")).GetFullPath();
+ auto model_folder = wxFileName(FileNames::DataDir(), wxT("openvino-models")).GetFullPath();
model_folder = wxFileName(model_folder, wxT("musicgen")).GetFullPath();
//make sure that a couple of the 'base' models, like EnCodec, tokenizer are present.
@@ -301,7 +301,7 @@
bool bGoodResult = true;
{
- FilePath model_folder = FileNames::MkDir(wxFileName(FileNames::BaseDir(), wxT("openvino-models")).GetFullPath());
+ FilePath model_folder = FileNames::MkDir(wxFileName(FileNames::DataDir(), wxT("openvino-models")).GetFullPath());
std::string musicgen_model_folder = audacity::ToUTF8(wxFileName(model_folder, wxString("musicgen"))
.GetFullPath());
diff --color -ruN mod-openvino.orig/OVMusicSeparation.cpp mod-openvino/OVMusicSeparation.cpp
--- mod-openvino.orig/OVMusicSeparation.cpp 2025-04-26 15:56:46.863791883 +0300
+++ mod-openvino/OVMusicSeparation.cpp 2025-04-26 16:00:47.323431730 +0300
@@ -254,7 +254,7 @@
//todo: Right now we're looking for the model in the 'BaseDir' (which is top-level folder of Audacity install)
// This might be okay, but some users may not have permissions to place models there. So, also look in
// DataDir(), which is the path to C:\Users\<user>\AppData\Roaming\audacity.
- FilePath model_folder = FileNames::MkDir(wxFileName(FileNames::BaseDir(), wxT("openvino-models")).GetFullPath());
+ FilePath model_folder = FileNames::MkDir(wxFileName(FileNames::DataDir(), wxT("openvino-models")).GetFullPath());
std::string demucs_v4_path = audacity::ToUTF8(wxFileName(model_folder, wxT("htdemucs_v4.xml"))
.GetFullPath());
diff --color -ruN mod-openvino.orig/OVNoiseSuppression.cpp mod-openvino/OVNoiseSuppression.cpp
--- mod-openvino.orig/OVNoiseSuppression.cpp 2025-04-26 15:56:46.865271447 +0300
+++ mod-openvino/OVNoiseSuppression.cpp 2025-04-26 16:00:47.329279025 +0300
@@ -50,7 +50,7 @@
static bool is_deepfilter_model_present(std::string deepfilter_basename)
{
- auto model_folder = wxFileName(FileNames::BaseDir(), wxT("openvino-models")).GetFullPath();
+ auto model_folder = wxFileName(FileNames::DataDir(), wxT("openvino-models")).GetFullPath();
model_folder = wxFileName(model_folder, wxString(deepfilter_basename)).GetFullPath();
std::vector< std::string > model_basenames = { "enc", "erb_dec", "df_dec" };
@@ -77,7 +77,7 @@
static bool is_omz_model_present(std::string omz_model_basename)
{
- auto model_folder = wxFileName(FileNames::BaseDir(), wxT("openvino-models")).GetFullPath();
+ auto model_folder = wxFileName(FileNames::DataDir(), wxT("openvino-models")).GetFullPath();
auto binmodelpath = wxFileName(model_folder, wxString(omz_model_basename + ".bin"));
auto xmlmodelpath = wxFileName(model_folder, wxString(omz_model_basename + ".xml"));
@@ -384,7 +384,7 @@
try
{
//CompileNoiseSuppression(compiledModel);
- FilePath model_folder = FileNames::MkDir(wxFileName(FileNames::BaseDir(), wxT("openvino-models")).GetFullPath());
+ FilePath model_folder = FileNames::MkDir(wxFileName(FileNames::DataDir(), wxT("openvino-models")).GetFullPath());
FilePath cache_folder = FileNames::MkDir(wxFileName(FileNames::DataDir(), wxT("openvino-model-cache")).GetFullPath());
std::string cache_path = wstring_to_string(wxFileName(cache_folder).GetFullPath().ToStdWstring());
diff --color -ruN mod-openvino.orig/OVWhisperTranscription.cpp mod-openvino/OVWhisperTranscription.cpp
--- mod-openvino.orig/OVWhisperTranscription.cpp 2025-04-26 15:56:46.867815026 +0300
+++ mod-openvino/OVWhisperTranscription.cpp 2025-04-26 16:00:47.323457689 +0300
@@ -292,7 +292,7 @@
static bool is_whisper_model_present(std::string whisper_basename)
{
std::cout << "is_whisper_model_present(" << whisper_basename << ")" << std::endl;
- auto model_folder = wxFileName(FileNames::BaseDir(), wxT("openvino-models")).GetFullPath();
+ auto model_folder = wxFileName(FileNames::DataDir(), wxT("openvino-models")).GetFullPath();
{
std::string ggml_binname = std::string("ggml-") + whisper_basename + std::string(".bin");
@@ -877,7 +877,7 @@
params.prompt = mInitialPrompt;
//whisper init
- FilePath model_folder = FileNames::MkDir(wxFileName(FileNames::BaseDir(), wxT("openvino-models")).GetFullPath());
+ FilePath model_folder = FileNames::MkDir(wxFileName(FileNames::DataDir(), wxT("openvino-models")).GetFullPath());
std::string whisper_variant = mSupportedModels[m_modelSelectionChoice];
std::string ggml_binname = std::string("ggml-") + whisper_variant + std::string(".bin");
std::string whisper_model_path = audacity::ToUTF8(wxFileName(model_folder, wxString(ggml_binname))

179
scripts/build-arm64.sh Executable file
View File

@ -0,0 +1,179 @@
#!/bin/bash
set -e
set -x
set -o pipefail
brew install opencl-clhpp-headers
brew install libomp
MODULE_VERSION="3.7.1-R4.2"
ROOT_DIR=$(pwd)
SOURCE_PATH=$(pwd)/sources
PACKAGE_PATH=$(pwd)/packages
BUILD_PATH=$(pwd)/build
STAGING_PATH=$(pwd)/staging
echo "Applying patches..."
for repo in $(ls $SOURCE_PATH); do
patch_dir=${ROOT_DIR}/patches/$repo
if [ -d "$patch_dir" ]; then
echo "Applying patches to $repo"
for patch in $patch_dir/*.patch; do
if [ -f "$patch" ]; then
echo "Applying patch $patch"
patch -d $SOURCE_PATH/$repo < $patch
fi
done
else
echo "No patches found for $repo"
fi
done
cp -r $SOURCE_PATH/openvino-plugins-ai-audacity/mod-openvino $SOURCE_PATH/audacity/modules
cd $PACKAGE_PATH
wget https://storage.openvinotoolkit.org/repositories/openvino/packages/2024.0/macos/m_openvino_toolkit_macos_11_0_2024.0.0.14509.34caeefd078_arm64.tgz
tar xvf m_openvino_toolkit_macos_11_0_2024.0.0.14509.34caeefd078_arm64.tgz
source m_openvino_toolkit_macos_11_0_2024.0.0.14509.34caeefd078_arm64/setupvars.sh
wget https://download.pytorch.org/libtorch/cpu/libtorch-macos-arm64-2.2.2.zip
unzip libtorch-macos-arm64-2.2.2.zip
export LIBTORCH_ROOTDIR=$PACKAGE_PATH/libtorch
mkdir -p $BUILD_PATH/whisper
cd $BUILD_PATH/whisper
cmake $SOURCE_PATH/whisper.cpp -DWHISPER_OPENVINO=ON -DMACOS_ARCHITECTURE=arm64 -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0 -DWHISPER_NO_ACCELERATE=ON
make -j`sysctl -n hw.ncpu`
cmake --install . --config Release --prefix $PACKAGE_PATH/whisper
export WHISPERCPP_ROOTDIR=$PACKAGE_PATH/whisper
export LD_LIBRARY_PATH=${WHISPERCPP_ROOTDIR}/lib:$LD_LIBRARY_PATH
mkdir -p $BUILD_PATH/openvino_tokenizers
cd $BUILD_PATH/openvino_tokenizers
cmake $SOURCE_PATH/openvino_tokenizers -DMACOS_ARCHITECTURE=arm64 -DCMAKE_OSX_DEPLOYMENT_TARGET=11.0
make -j`sysctl -n hw.ncpu`
cmake --install . --config Release --prefix $PACKAGE_PATH/openvino_tokenizers
mkdir -p $BUILD_PATH/audacity
cd $BUILD_PATH/audacity
cmake -G "Unix Makefiles" \
-D CMAKE_CXX_FLAGS="-I/opt/homebrew/opt/opencl-clhpp-headers/include" \
-DMACOS_ARCHITECTURE=arm64 -DCMAKE_OSX_DEPLOYMENT_TARGET=14.0\
$SOURCE_PATH/audacity -DCMAKE_BUILD_TYPE=Release
make -j`sysctl -n hw.ncpu`
mkdir -p $STAGING_PATH
cd $STAGING_PATH
MODULE_PATH="$STAGING_PATH/dist/Library/Application Support/audacity/modules"
mkdir -p "$MODULE_PATH/libs"
cp -p $BUILD_PATH/audacity/Release/Audacity.app/Contents/modules/mod-openvino.so \
"$MODULE_PATH"
cp -p $PACKAGE_PATH/m_openvino_toolkit_macos_11_0_2024.0.0.14509.34caeefd078_arm64/runtime/lib/arm64/Release/*.so \
"$MODULE_PATH/libs"
cp -p $PACKAGE_PATH/m_openvino_toolkit_macos_11_0_2024.0.0.14509.34caeefd078_arm64/runtime/lib/arm64/Release/*.dylib \
"$MODULE_PATH/libs"
cp -p $PACKAGE_PATH/m_openvino_toolkit_macos_11_0_2024.0.0.14509.34caeefd078_arm64/runtime/3rdparty/tbb/lib/*.dylib \
"$MODULE_PATH/libs"
cp -p $PACKAGE_PATH/libtorch/lib/libc10.dylib \
"$MODULE_PATH/libs"
cp -p $PACKAGE_PATH/libtorch/lib/libtorch.dylib \
"$MODULE_PATH/libs"
cp -p $PACKAGE_PATH/libtorch/lib/libtorch_cpu.dylib \
"$MODULE_PATH/libs"
cp -P $PACKAGE_PATH/whisper/lib/*.dylib \
"$MODULE_PATH/libs"
cp -P $PACKAGE_PATH/openvino_tokenizers/lib/*.dylib \
"$MODULE_PATH/libs/"
cp /opt/homebrew/opt/libomp/lib/libomp.dylib \
"$MODULE_PATH/libs/"
chmod -R ug+w "$MODULE_PATH"
xattr -cr "$MODULE_PATH"
# Fix loading paths
cd "$MODULE_PATH"
for lib in *.so *.dylib; do
[ -e "$lib" ] || continue
echo "Processing $lib..."
deps=$(otool -L "$lib" | awk 'NR>1 {print $1}' | grep '@loader_path/../Frameworks/')
for dep in $deps; do
dep_filename=$(basename "$dep")
# If we have this file in the libs directory, use it
if [[ -f "./libs/$dep_filename" ]]; then
new_dep="@rpath/$dep_filename"
echo " Updating dependency: $dep$new_dep"
install_name_tool -change "$dep" "$new_dep" "$lib"
else
# If the file does not exist load it from the Audacity/Frameworks directory
new_dep="@executable_path/../Frameworks/$dep_filename"
echo " Updating dependency: $dep$new_dep"
install_name_tool -change "$dep" "$new_dep" "$lib"
fi
done
install_name_tool -add_rpath @loader_path/libs "$lib"
done
cd $ROOT_DIR
cp -r installer/* "$STAGING_PATH"
sudo xcode-select -s /Applications/Xcode_16.2.app
PROJECT="tools/ResourceDownloader/ResourceDownloader.xcodeproj"
SCHEME="ResourceDownloader"
CONFIG="Release"
BUILD_DIR="build"
xcodebuild \
-project "$PROJECT" \
-scheme "$SCHEME" \
-configuration "$CONFIG" \
-derivedDataPath "$BUILD_DIR" \
clean build
APP_PATH="$BUILD_DIR/Build/Products/$CONFIG/$SCHEME.app"
# codesign --verbose --timestamp --identifier "org.audacityteam.resourcedownloader" --sign "${APPLE_CODESIGN_IDENTITY}" "$APP_PATH"
cp -R "$APP_PATH" "$STAGING_PATH/Scripts"
cd "$STAGING_PATH"
mkdir -p packages
pkgbuild \
--root dist \
--scripts Scripts \
--install-location / \
--identifier org.audacityteam.audacity \
--version "$MODULE_VERSION" \
packages/openvino-module.pkg
# productsign --sign "${APPLE_CODESIGN_IDENTITY}" packages/openvino-module.pkg packages/openvino-module.pkg
productbuild --distribution distribution.xml \
--resources Resources \
--package-path ./packages \
./Audacity-OpenVINO.pkg
# productsign --sign "${APPLE_CODESIGN_IDENTITY}" final.pkg final.pkg
echo "Done."

67
scripts/postinstall Executable file
View File

@ -0,0 +1,67 @@
#!/bin/bash
# This script runs after the installation
# It updates the Audacity configuration file to enable the OpenVINO module
MODULE_NAME="mod-openvino"
MODULE_PATH="/Library/Application Support/audacity/modules/${MODULE_NAME}.so"
MODULE_DATETIME=$(stat -f "%m" "$MODULE_PATH" | xargs -I{} date -r {} +"%Y-%m-%dT%H:%M:%S")
update_config_section() {
local cfg_file="$1"
local section="$2"
local key="$3"
local value="$4"
local tmp_file="${cfg_file}.tmp"
original_owner=$(stat -f "%u" "$cfg_file")
original_group=$(stat -f "%g" "$cfg_file")
awk -v section="$section" -v key="$key" -v value="$value" '
BEGIN {
found_section = 0
found_key = 0
}
$0 == "[" section "]" {
found_section = 1
print
next
}
found_section && $0 ~ "^" key{
print key "=" value
found_key = 1
found_section = 0
next
}
found_section && !found_key && /^\[.*\]/ {
print key "=" value
found_key = 1
found_section = 0
}
{ print }
END { }
' "$cfg_file" > "$tmp_file" && mv "$tmp_file" "$cfg_file"
chown "$original_owner":"$original_group" "$cfg_file"
}
# Loop through all user home directories in /Users (excluding system users)
for USER_HOME in /Users/*; do
# Only act on real user dirs
[ -d "$USER_HOME" ] || continue
[ -f "$USER_HOME/.zshrc" ] || [ -f "$USER_HOME/.bash_profile" ] || continue
CFG_PATH="$USER_HOME/Library/Application Support/audacity/audacity.cfg"
CFG_PATH1="$USER_HOME/Library/Application Support/audacity/_audacity.cfg"
# Skip if the config doesn't exist
[ -f "$CFG_PATH" ] || continue
echo "Updating $CFG_PATH"
update_config_section "$CFG_PATH" "Module" "mod-openvino" "1"
update_config_section "$CFG_PATH" "ModuleDateTime" "mod-openvino" "$MODULE_DATETIME"
update_config_section "$CFG_PATH" "ModulePath" "mod-openvino" "$MODULE_PATH"
done
exit 0

58
scripts/prepare-build.sh Executable file
View File

@ -0,0 +1,58 @@
#!/bin/bash
set -e
set -x
ROOT_DIR=$(pwd)
SOURCE_PATH=$(pwd)/sources
PACKAGE_PATH=$(pwd)/packages
BUILD_PATH=$(pwd)/build
function download_release {
cd $SOURCE_PATH
local repo=$1
local release=${2:+tags/$2}
release=${release:-latest}
local target_dir=$(basename $repo)
echo Downloading ${release} release of $repo into $target_dir
mkdir -p $target_dir
curl -sL https://api.github.com/repos/${repo}/releases/${release}
wget -O ${target_dir}/archive.tar.gz $(wget -q -O - https://api.github.com/repos/${repo}/releases/${release} | jq -r '.tarball_url')
tar --strip-components=1 -xzf ${target_dir}/archive.tar.gz -C $target_dir
rm -f ${target_dir}/archive.tar.gz
cd $ROOT_DIR
}
function download_tarball {
cd "$SOURCE_PATH" || exit 1
local repo=$1
local url=$2
local target_dir=$(basename "$repo")
echo "Downloading from $url into $target_dir"
mkdir -p "$target_dir"
wget -O "${target_dir}/archive.tar.gz" "$url"
tar --strip-components=1 -xzf "${target_dir}/archive.tar.gz" -C "$target_dir"
rm -f "${target_dir}/archive.tar.gz"
cd "$ROOT_DIR" || exit 1
}
echo "Cleaning build directory..."
rm -rf $PACKAGE_PATH
rm -rf $BUILD_PATH
rm -rf $SOURCE_PATH
mkdir -p $PACKAGE_PATH
mkdir -p $BUILD_PATH
mkdir -p $SOURCE_PATH
# download dependencies
# download_release "audacity/audacity"
# download_release "intel/openvino-plugins-ai-audacity"
# download_release "ggerganov/whisper.cpp" "v1.6.0"
# download_release "openvinotoolkit/openvino_tokenizers" "2024.0.0.0"
download_tarball "audacity/audacity" "https://github.com/audacity/audacity/archive/refs/tags/Audacity-3.7.3.tar.gz"
download_tarball "intel/openvino-plugins-ai-audacity" "https://github.com/intel/openvino-plugins-ai-audacity/archive/refs/tags/v3.7.1-R4.2.tar.gz"
download_tarball "ggerganov/whisper.cpp" "https://github.com/ggml-org/whisper.cpp/archive/refs/tags/v1.6.0.tar.gz"
download_tarball "openvinotoolkit/openvino_tokenizers" "https://github.com/openvinotoolkit/openvino_tokenizers/archive/refs/tags/2024.0.0.0.tar.gz"

View File

@ -0,0 +1,538 @@
// !$*UTF8*$!
{
archiveVersion = 1;
classes = {
};
objectVersion = 56;
objects = {
/* Begin PBXContainerItemProxy section */
0A2E72612DBFC63900F87746 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0A2E724A2DBFC63800F87746 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 0A2E72512DBFC63800F87746;
remoteInfo = ResourceDownloader;
};
0A2E726B2DBFC63900F87746 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 0A2E724A2DBFC63800F87746 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 0A2E72512DBFC63800F87746;
remoteInfo = ResourceDownloader;
};
/* End PBXContainerItemProxy section */
/* Begin PBXFileReference section */
0A2E72522DBFC63800F87746 /* ResourceDownloader.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ResourceDownloader.app; sourceTree = BUILT_PRODUCTS_DIR; };
0A2E72602DBFC63900F87746 /* ResourceDownloaderTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ResourceDownloaderTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
0A2E726A2DBFC63900F87746 /* ResourceDownloaderUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = ResourceDownloaderUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
/* End PBXFileReference section */
/* Begin PBXFileSystemSynchronizedRootGroup section */
0A2E72542DBFC63800F87746 /* ResourceDownloader */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = ResourceDownloader; sourceTree = "<group>"; };
0A2E72632DBFC63900F87746 /* ResourceDownloaderTests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = ResourceDownloaderTests; sourceTree = "<group>"; };
0A2E726D2DBFC63900F87746 /* ResourceDownloaderUITests */ = {isa = PBXFileSystemSynchronizedRootGroup; explicitFileTypes = {}; explicitFolders = (); path = ResourceDownloaderUITests; sourceTree = "<group>"; };
/* End PBXFileSystemSynchronizedRootGroup section */
/* Begin PBXFrameworksBuildPhase section */
0A2E724F2DBFC63800F87746 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
0A2E725D2DBFC63900F87746 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
0A2E72672DBFC63900F87746 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */
0A2E72492DBFC63800F87746 = {
isa = PBXGroup;
children = (
0A2E72542DBFC63800F87746 /* ResourceDownloader */,
0A2E72632DBFC63900F87746 /* ResourceDownloaderTests */,
0A2E726D2DBFC63900F87746 /* ResourceDownloaderUITests */,
0A2E72532DBFC63800F87746 /* Products */,
);
sourceTree = "<group>";
};
0A2E72532DBFC63800F87746 /* Products */ = {
isa = PBXGroup;
children = (
0A2E72522DBFC63800F87746 /* ResourceDownloader.app */,
0A2E72602DBFC63900F87746 /* ResourceDownloaderTests.xctest */,
0A2E726A2DBFC63900F87746 /* ResourceDownloaderUITests.xctest */,
);
name = Products;
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXNativeTarget section */
0A2E72512DBFC63800F87746 /* ResourceDownloader */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0A2E72742DBFC63900F87746 /* Build configuration list for PBXNativeTarget "ResourceDownloader" */;
buildPhases = (
0A2E724E2DBFC63800F87746 /* Sources */,
0A2E724F2DBFC63800F87746 /* Frameworks */,
0A2E72502DBFC63800F87746 /* Resources */,
);
buildRules = (
);
dependencies = (
);
fileSystemSynchronizedGroups = (
0A2E72542DBFC63800F87746 /* ResourceDownloader */,
);
name = ResourceDownloader;
packageProductDependencies = (
);
productName = ResourceDownloader;
productReference = 0A2E72522DBFC63800F87746 /* ResourceDownloader.app */;
productType = "com.apple.product-type.application";
};
0A2E725F2DBFC63900F87746 /* ResourceDownloaderTests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0A2E72772DBFC63900F87746 /* Build configuration list for PBXNativeTarget "ResourceDownloaderTests" */;
buildPhases = (
0A2E725C2DBFC63900F87746 /* Sources */,
0A2E725D2DBFC63900F87746 /* Frameworks */,
0A2E725E2DBFC63900F87746 /* Resources */,
);
buildRules = (
);
dependencies = (
0A2E72622DBFC63900F87746 /* PBXTargetDependency */,
);
fileSystemSynchronizedGroups = (
0A2E72632DBFC63900F87746 /* ResourceDownloaderTests */,
);
name = ResourceDownloaderTests;
packageProductDependencies = (
);
productName = ResourceDownloaderTests;
productReference = 0A2E72602DBFC63900F87746 /* ResourceDownloaderTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
0A2E72692DBFC63900F87746 /* ResourceDownloaderUITests */ = {
isa = PBXNativeTarget;
buildConfigurationList = 0A2E727A2DBFC63900F87746 /* Build configuration list for PBXNativeTarget "ResourceDownloaderUITests" */;
buildPhases = (
0A2E72662DBFC63900F87746 /* Sources */,
0A2E72672DBFC63900F87746 /* Frameworks */,
0A2E72682DBFC63900F87746 /* Resources */,
);
buildRules = (
);
dependencies = (
0A2E726C2DBFC63900F87746 /* PBXTargetDependency */,
);
fileSystemSynchronizedGroups = (
0A2E726D2DBFC63900F87746 /* ResourceDownloaderUITests */,
);
name = ResourceDownloaderUITests;
packageProductDependencies = (
);
productName = ResourceDownloaderUITests;
productReference = 0A2E726A2DBFC63900F87746 /* ResourceDownloaderUITests.xctest */;
productType = "com.apple.product-type.bundle.ui-testing";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
0A2E724A2DBFC63800F87746 /* Project object */ = {
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1630;
LastUpgradeCheck = 1630;
TargetAttributes = {
0A2E72512DBFC63800F87746 = {
CreatedOnToolsVersion = 16.3;
};
0A2E725F2DBFC63900F87746 = {
CreatedOnToolsVersion = 16.3;
TestTargetID = 0A2E72512DBFC63800F87746;
};
0A2E72692DBFC63900F87746 = {
CreatedOnToolsVersion = 16.3;
TestTargetID = 0A2E72512DBFC63800F87746;
};
};
};
buildConfigurationList = 0A2E724D2DBFC63800F87746 /* Build configuration list for PBXProject "ResourceDownloader" */;
compatibilityVersion = "Xcode 12.0";
developmentRegion = en;
hasScannedForEncodings = 0;
knownRegions = (
en,
Base,
);
mainGroup = 0A2E72492DBFC63800F87746;
minimizedProjectReferenceProxies = 1;
productRefGroup = 0A2E72532DBFC63800F87746 /* Products */;
projectDirPath = "";
projectRoot = "";
targets = (
0A2E72512DBFC63800F87746 /* ResourceDownloader */,
0A2E725F2DBFC63900F87746 /* ResourceDownloaderTests */,
0A2E72692DBFC63900F87746 /* ResourceDownloaderUITests */,
);
};
/* End PBXProject section */
/* Begin PBXResourcesBuildPhase section */
0A2E72502DBFC63800F87746 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
0A2E725E2DBFC63900F87746 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
0A2E72682DBFC63900F87746 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
0A2E724E2DBFC63800F87746 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
0A2E725C2DBFC63900F87746 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
0A2E72662DBFC63900F87746 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
0A2E72622DBFC63900F87746 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 0A2E72512DBFC63800F87746 /* ResourceDownloader */;
targetProxy = 0A2E72612DBFC63900F87746 /* PBXContainerItemProxy */;
};
0A2E726C2DBFC63900F87746 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 0A2E72512DBFC63800F87746 /* ResourceDownloader */;
targetProxy = 0A2E726B2DBFC63900F87746 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */
0A2E72722DBFC63900F87746 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = dwarf;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
"$(inherited)",
);
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 15.2;
MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
MTL_FAST_MATH = YES;
ONLY_ACTIVE_ARCH = YES;
SDKROOT = macosx;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
};
name = Debug;
};
0A2E72732DBFC63900F87746 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ALWAYS_SEARCH_USER_PATHS = NO;
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_ANALYZER_NONNULL = YES;
CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CLANG_ENABLE_MODULES = YES;
CLANG_ENABLE_OBJC_ARC = YES;
CLANG_ENABLE_OBJC_WEAK = YES;
CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
CLANG_WARN_BOOL_CONVERSION = YES;
CLANG_WARN_COMMA = YES;
CLANG_WARN_CONSTANT_CONVERSION = YES;
CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
CLANG_WARN_STRICT_PROTOTYPES = YES;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
ENABLE_NS_ASSERTIONS = NO;
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GCC_NO_COMMON_BLOCKS = YES;
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
GCC_WARN_UNDECLARED_SELECTOR = YES;
GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MACOSX_DEPLOYMENT_TARGET = 15.2;
MTL_ENABLE_DEBUG_INFO = NO;
MTL_FAST_MATH = YES;
SDKROOT = macosx;
SWIFT_COMPILATION_MODE = wholemodule;
};
name = Release;
};
0A2E72752DBFC63900F87746 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = ResourceDownloader/ResourceDownloader.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.4;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = org.audacity.ResourceDownloader;
PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
};
name = Debug;
};
0A2E72762DBFC63900F87746 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = ResourceDownloader/ResourceDownloader.entitlements;
CODE_SIGN_STYLE = Automatic;
COMBINE_HIDPI_IMAGES = YES;
CURRENT_PROJECT_VERSION = 1;
ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "";
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/../Frameworks",
);
MACOSX_DEPLOYMENT_TARGET = 12.4;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = org.audacity.ResourceDownloader;
PRODUCT_NAME = "$(TARGET_NAME)";
REGISTER_APP_GROUPS = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
};
name = Release;
};
0A2E72782DBFC63900F87746 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MACOSX_DEPLOYMENT_TARGET = 15.2;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = org.audacity.ResourceDownloaderTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ResourceDownloader.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/ResourceDownloader";
};
name = Debug;
};
0A2E72792DBFC63900F87746 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
BUNDLE_LOADER = "$(TEST_HOST)";
"CODE_SIGN_IDENTITY[sdk=macosx*]" = "-";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MACOSX_DEPLOYMENT_TARGET = 15.2;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = org.audacity.ResourceDownloaderTests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TEST_HOST = "$(BUILT_PRODUCTS_DIR)/ResourceDownloader.app/$(BUNDLE_EXECUTABLE_FOLDER_PATH)/ResourceDownloader";
};
name = Release;
};
0A2E727B2DBFC63900F87746 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = org.audacity.ResourceDownloaderUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TEST_TARGET_NAME = ResourceDownloader;
};
name = Debug;
};
0A2E727C2DBFC63900F87746 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
GENERATE_INFOPLIST_FILE = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = org.audacity.ResourceDownloaderUITests;
PRODUCT_NAME = "$(TARGET_NAME)";
SWIFT_EMIT_LOC_STRINGS = NO;
SWIFT_VERSION = 5.0;
TEST_TARGET_NAME = ResourceDownloader;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
0A2E724D2DBFC63800F87746 /* Build configuration list for PBXProject "ResourceDownloader" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0A2E72722DBFC63900F87746 /* Debug */,
0A2E72732DBFC63900F87746 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
0A2E72742DBFC63900F87746 /* Build configuration list for PBXNativeTarget "ResourceDownloader" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0A2E72752DBFC63900F87746 /* Debug */,
0A2E72762DBFC63900F87746 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
0A2E72772DBFC63900F87746 /* Build configuration list for PBXNativeTarget "ResourceDownloaderTests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0A2E72782DBFC63900F87746 /* Debug */,
0A2E72792DBFC63900F87746 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
0A2E727A2DBFC63900F87746 /* Build configuration list for PBXNativeTarget "ResourceDownloaderUITests" */ = {
isa = XCConfigurationList;
buildConfigurations = (
0A2E727B2DBFC63900F87746 /* Debug */,
0A2E727C2DBFC63900F87746 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
};
rootObject = 0A2E724A2DBFC63800F87746 /* Project object */;
}

View File

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<Workspace
version = "1.0">
<FileRef
location = "self:">
</FileRef>
</Workspace>

View File

@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,58 @@
{
"images" : [
{
"idiom" : "mac",
"scale" : "1x",
"size" : "16x16"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "16x16"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "32x32"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "32x32"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "128x128"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "128x128"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "256x256"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "256x256"
},
{
"idiom" : "mac",
"scale" : "1x",
"size" : "512x512"
},
{
"idiom" : "mac",
"scale" : "2x",
"size" : "512x512"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.app-sandbox</key>
<false/>
<key>com.apple.security.files.user-selected.read-only</key>
<true/>
<key>com.apple.security.network.client</key>
<true/>
</dict>
</plist>

View File

@ -0,0 +1,591 @@
import SwiftUI
import Combine
struct Resource: Identifiable {
var id: UUID = UUID()
var name: String
var urls: [String] = []
}
class ModelStore: ObservableObject {
@Published var nodes: [Node] = []
var selectedItems: [Node] {
nodes.flatMap { $0.selectedItems }
}
}
@main
struct TreeTableApp: App {
@NSApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
@StateObject private var modelStore = ModelStore()
@State private var currentView = CurrentScreen.welcome
var body: some Scene {
WindowGroup {
switch currentView {
case .welcome:
WelcomeView(onContinue: {
modelStore.nodes = loadNodesFromJSON(named: "models")
currentView = .selection
})
case .selection:
ContentView(
modelStore: modelStore,
onBack: {
currentView = .welcome
},
onContinue: {
currentView = .download
}
)
.frame(width: 500, height: 400)
.onAppear() {
if let window = NSApplication.shared.windows.first {
window.center()
}
}
case .download:
DownloadView(currentView: $currentView, items: modelStore.selectedItems)
.frame(width: 500, height: 400)
case .conclusion:
CompletionView()
.frame(width: 500, height: 400)
}
}
}
}
class AppDelegate: NSObject, NSApplicationDelegate, NSWindowDelegate {
func applicationDidFinishLaunching(_ notification: Notification) {
if let window = NSApplication.shared.windows.first {
window.delegate = self
window.setContentSize(NSSize(width: 500, height: 400))
window.center()
}
}
func windowWillClose(_ notification: Notification) {
NSApp.terminate(nil)
}
}
enum CurrentScreen {
case welcome
case selection
case download
case conclusion
}
struct WelcomeView: View {
var onContinue: () -> Void
var body: some View {
VStack(spacing: 20) {
Image(systemName: "sparkles")
.resizable()
.scaledToFit()
.frame(width: 80, height: 80)
.foregroundColor(.accentColor)
Text("Welcome to the Setup Wizard")
.font(.title)
.fontWeight(.bold)
Text("This wizard will help you configure and launch OpenVINO in Audacity.")
.multilineTextAlignment(.center)
.padding()
Button("Continue") {
onContinue()
}
.buttonStyle(.borderedProminent)
}
.padding()
.frame(width: 400, height: 300)
}
}
enum CheckState {
case checked
case unchecked
case mixed
}
struct FileInfo: Codable {
let url: String
let sha256: String
let dsize: Int
let esize: Int
}
class Node: Identifiable, ObservableObject, Decodable {
let id = UUID()
let name: String
let description: String
let dir: String
let files: [FileInfo]
@Published var state: CheckState = .unchecked
@Published var children: [Node]?
weak var parent: Node?
var downloadSizeRecursive: Int {
let ownSize = files.map { $0.dsize }.reduce(0, +)
let childSize = (children ?? [])
.filter { $0.state == .checked }
.map { $0.downloadSize }
.reduce(0, +)
return ownSize + childSize
}
var extractedSizeRecursive: Int {
let ownSize = files.map { $0.esize }.reduce(0, +)
let childSize = (children ?? [])
.filter { $0.state == .checked }
.map { $0.extractedSize }
.reduce(0, +)
return ownSize + childSize
}
var downloadSize: Int {
return files.map { $0.dsize }.reduce(0, +)
}
var extractedSize: Int {
return files.map { $0.esize }.reduce(0, +)
}
var selectedItems: [Node] {
var results: [Node] = []
if state != .unchecked {
results.append(self)
}
for child in children ?? [] {
results.append(contentsOf: child.selectedItems)
}
return results
}
enum CodingKeys: CodingKey {
case name, description, dir, files, children
}
required init(from decoder: Decoder) throws {
let container = try decoder.container(keyedBy: CodingKeys.self)
self.name = try container.decode(String.self, forKey: .name)
self.description = try container.decode(String.self, forKey: .description)
self.dir = try container.decode(String.self, forKey: .dir)
self.files = try container.decodeIfPresent([FileInfo].self, forKey: .files) ?? []
self.children = try container.decodeIfPresent([Node].self, forKey: .children)
self.children?.forEach { $0.parent = self }
}
}
struct ContentView: View {
@ObservedObject var modelStore: ModelStore
@StateObject var viewModel = TreeViewModel()
var onBack: () -> Void
var onContinue: () -> Void
init(modelStore: ModelStore, onBack: @escaping () -> Void, onContinue: @escaping () -> Void) {
self.modelStore = modelStore
self.onBack = onBack
self.onContinue = onContinue
_viewModel = StateObject(wrappedValue: TreeViewModel(nodes: modelStore.nodes))
}
var body: some View {
VStack(spacing: 0) {
List {
OutlineGroup(viewModel.nodes, id: \.id, children: \.children) { node in
CheckboxView(node: node)
.onTapGesture { viewModel.toggle(node: node) }
}
}
.listStyle(.bordered(alternatesRowBackgrounds: false))
Divider()
HStack {
SizeInfoView(
label: "Total Download Size:",
size: viewModel.totalDownloadSize
)
SizeInfoView(
label: "Total Extracted Size:",
size: viewModel.totalExtractedSize
)
}
.padding()
HStack {
Spacer()
Button("Download selected", systemImage: "arrow.down.to.line") {
onContinue()
}
.buttonStyle(.borderedProminent)
Spacer()
}
.padding()
}
.navigationTitle("Download Wizard")
}
}
struct CheckboxView: View {
@ObservedObject var node: Node
var body: some View {
HStack(spacing: 8) {
VStack(alignment: .leading, spacing: 4) {
HStack {
Image(systemName: systemImageName)
.foregroundColor(node.state == .checked ? .blue : .primary)
Text(node.name)
.font(.body)
}
HStack(spacing: 16) {
Text("Download: \(node.downloadSizeRecursive.formatted(.byteCount(style: .file)))")
.foregroundColor(.secondary)
Text("Extracted: \(node.extractedSizeRecursive.formatted(.byteCount(style: .file)))")
.foregroundColor(.secondary)
}
.font(.caption)
}
}
.padding(.leading, node.children != nil ? 0 : 16)
}
private var systemImageName: String {
switch node.state {
case .checked: return "checkmark.square.fill"
case .unchecked: return "square"
case .mixed: return "minus.square.fill"
}
}
}
struct SizeInfoView: View {
let label: String
let size: Int
var body: some View {
VStack(alignment: .leading) {
Text(label)
.font(.caption)
.foregroundColor(.secondary)
Text(size.formatted(.byteCount(style: .file)))
.font(.system(.body, design: .monospaced))
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(8)
.background(RoundedRectangle(cornerRadius: 8).fill(Color(nsColor: .controlBackgroundColor)))
}
}
class TreeViewModel: ObservableObject {
@Published var nodes: [Node]
@Published var totalDownloadSize = 0
@Published var totalExtractedSize = 0
init(nodes: [Node] = []) {
self.nodes = nodes
self.totalDownloadSize = totalDownloadSize
self.totalExtractedSize = totalExtractedSize
}
func toggle(node: Node) {
let newState: CheckState = node.state == .checked ? .unchecked : .checked
node.state = newState
updateChildren(node: node, state: newState)
updateParentState(node.parent)
calculateTotals()
}
private func calculateTotals() {
totalDownloadSize = calculateTotal(for: \.downloadSizeRecursive)
totalExtractedSize = calculateTotal(for: \.extractedSizeRecursive)
}
private func updateChildren(node: Node, state: CheckState) {
node.children?.forEach {
$0.state = state
updateChildren(node: $0, state: state)
}
}
private func updateParentState(_ parent: Node?) {
guard let parent = parent else { return }
let childrenStates = parent.children?.map { $0.state } ?? []
let allChecked = childrenStates.allSatisfy { $0 == .checked }
let allUnchecked = childrenStates.allSatisfy { $0 == .unchecked }
parent.state = allChecked ? .checked :
allUnchecked ? .unchecked : .mixed
updateParentState(parent.parent)
}
private func calculateTotal(for keyPath: KeyPath<Node, Int>) -> Int {
var total = 0
for node in nodes {
if node.state != .unchecked {
total += node[keyPath: keyPath]
}
}
return total
}
}
private func loadNodesFromJSON(named filename: String) -> [Node] {
guard let url = Bundle.main.url(forResource: filename, withExtension: "json") else {
fatalError("Unable to find \(filename).json in bundle.")
}
do {
let data = try Data(contentsOf: url)
let decoder = JSONDecoder()
return try decoder.decode([Node].self, from: data)
} catch {
print("Tried to load from: \(url)")
print("Error: \(error)")
fatalError("Failed to load or decode \(filename).json: \(error)")
}
}
struct DownloadItem: Identifiable {
let id = UUID()
let item: Node
var progress: Double = 0.0
var isDownloading = false
var isCompleted = false
}
class DownloadManager: ObservableObject {
@Published var items: [DownloadItem]
@Published var currentDownloadItem: DownloadItem?
@Published var completedItems = 0 {
didSet {
if completedItems == items.count {
isFinished = true
}
}
}
@Published var isFinished: Bool
private var cancellables = Set<AnyCancellable>()
init(items: [Node]) {
self.items = items.map { DownloadItem(item: $0) }
self.isFinished = false
}
func startDownloads() {
processNextDownload()
}
private func processNextDownload() {
guard let nextItem = items.first(where: { !$0.isCompleted && !$0.isDownloading }) else {
return
}
guard !nextItem.item.files.isEmpty else {
if let index = items.firstIndex(where: { $0.id == nextItem.id }) {
items[index].isCompleted = true
}
processNextDownload()
return
}
var currentItem = nextItem
currentItem.isDownloading = true
if let index = items.firstIndex(where: { $0.id == currentItem.id }) {
items[index] = currentItem
}
currentDownloadItem = currentItem
for file in currentItem.item.files {
let url = URL(string: file.url)!
let request = URLRequest(url: url)
let session = URLSession.shared
let task = session.downloadTask(with: request) { [weak self] tempURL, response, error in
guard let self = self, let tempURL = tempURL else { return }
let homeDirectory = FileManager.default.homeDirectoryForCurrentUser
var destinationDir = homeDirectory.appendingPathComponent("Library/Application Support/audacity/openvino-models")
destinationDir = destinationDir.appendingPathComponent(currentItem.item.dir, isDirectory: true)
do {
// Create the directory if it doesn't exist
try FileManager.default.createDirectory(at: destinationDir, withIntermediateDirectories: true)
print("Directory created or already exists at: \(destinationDir.path)")
} catch {
print("Failed to create directory: \(error)")
}
do {
try FileManager.default.createDirectory(at: destinationDir, withIntermediateDirectories: true)
let filename = url.lastPathComponent
let destinationPath = destinationDir.appendingPathComponent(filename)
// Move downloaded file to destination
try FileManager.default.moveItem(at: tempURL, to: destinationPath)
// Extract based on file extension
if filename.hasSuffix(".zip") {
let unzipTask = Process()
unzipTask.executableURL = URL(fileURLWithPath: "/usr/bin/unzip")
unzipTask.arguments = ["-o", destinationPath.path, "-d", destinationDir.path]
try unzipTask.run()
unzipTask.waitUntilExit()
try FileManager.default.removeItem(at: destinationPath)
} else if filename.hasSuffix(".tar.gz") {
let tarTask = Process()
tarTask.executableURL = URL(fileURLWithPath: "/usr/bin/tar")
tarTask.arguments = ["-xzf", destinationPath.path, "-C", destinationDir.path]
try tarTask.run()
tarTask.waitUntilExit()
try FileManager.default.removeItem(at: destinationPath)
}
} catch {
print("Error during file handling: \(error)")
}
DispatchQueue.main.async {
if let index = self.items.firstIndex(where: { $0.id == currentItem.id }) {
self.items[index].isDownloading = false
self.items[index].isCompleted = true
self.completedItems += 1
self.currentDownloadItem = nil
self.processNextDownload()
}
}
}
let progressObserver = task.progress.observe(\.fractionCompleted) { [weak self] progress, _ in
DispatchQueue.main.async {
if let index = self?.items.firstIndex(where: { $0.id == currentItem.id }) {
self?.items[index].progress = progress.fractionCompleted
self?.currentDownloadItem = self?.items[index]
}
}
}
cancellables.insert(AnyCancellable {
progressObserver.invalidate()
})
task.resume()
}
}
}
struct DownloadView: View {
@Binding var currentView: CurrentScreen
@StateObject private var downloadManager: DownloadManager
private let totalItems: Int
var buttonText: String {
downloadManager.completedItems == totalItems ? "Finish" : "Cancel"
}
init(currentView: Binding<CurrentScreen>, items: [Node]) {
_currentView = currentView
_downloadManager = StateObject(wrappedValue: DownloadManager(items: items))
totalItems = items.count
}
var body: some View {
VStack(spacing: 20) {
// Overall progress
VStack {
Text("Total Progress")
.font(.headline)
ProgressView(value: Double(downloadManager.completedItems), total: Double(totalItems))
Text("\(downloadManager.completedItems)/\(totalItems) items downloaded")
.font(.caption)
}
.padding()
// Current item progress
if let currentItem = downloadManager.currentDownloadItem {
VStack {
let itemName = currentItem.item
let label = itemName.parent != nil
? "Downloading: \(itemName.parent!.name): \(itemName.name)"
: "Downloading: \(itemName.name)"
Text(label)
.font(.headline)
ProgressView(value: currentItem.progress)
Text("\(Int(currentItem.progress * 100))% complete")
.font(.caption)
}
.padding()
.transition(.opacity)
}
}
.padding()
.onAppear {
downloadManager.startDownloads()
}
.onChange(of: downloadManager.isFinished) { finished in
if (finished) {
currentView = .conclusion
}
}
.navigationTitle("Download Wizard")
}
}
struct CompletionView: View {
var body: some View {
VStack(spacing: 20) {
Image(systemName: "checkmark.seal.fill")
.resizable()
.scaledToFit()
.frame(width: 80, height: 80)
.foregroundColor(.green)
Text("All Done!")
.font(.title)
.fontWeight(.bold)
Text("All components were installed successfully. To start using OpenVINO features in Audacity, please restart the application.")
.font(.body)
.multilineTextAlignment(.center)
.padding(.horizontal)
Text("Once restarted, you'll be able to access enhanced AI-powered effects and tools powered by OpenVINO.")
.font(.footnote)
.multilineTextAlignment(.center)
.foregroundColor(.secondary)
.padding(.horizontal)
Spacer()
HStack {
Spacer()
Button("Finish") {
closeWindow()
}
.buttonStyle(.borderedProminent)
Spacer()
}
}
.padding()
.navigationTitle("Download Wizard")
}
func closeWindow() {
NSApp.keyWindow?.close()
}
}

View File

@ -0,0 +1,256 @@
[
{
"name": "Audio Super Resolution",
"description": "Versatile Audio Super Resolution",
"dir": "audiosr",
"files": [
{
"url": "https://huggingface.co/Intel/versatile_audio_super_resolution_openvino/resolve/9a97d7f128b22aea72e92862a3eccc310f88ac26/versatile_audio_sr_base_openvino_models.zip?download=true",
"sha256": "e4058862616e8eaa157a6a69d75daff49b63f997c69e59887c77a04ce6840d86",
"dsize": 930565310,
"esize": 234
}
],
"children": [
{
"name": "Basic",
"description": "Basic",
"dir": "audiosr",
"files": [
{
"url": "https://huggingface.co/Intel/versatile_audio_super_resolution_openvino/resolve/9a97d7f128b22aea72e92862a3eccc310f88ac26/versatile_audio_sr_ddpm_basic_openvino_models.zip?download=true",
"sha256": "382e0693e35e4bbeae1a0f3223954250e13427108ae16c0fe8f786489bbe2767",
"dsize": 473639145,
"esize": 234
}
]
},
{
"name": "Speech",
"description": "Speech",
"dir": "audiosr",
"files": [
{
"url": "https://huggingface.co/Intel/versatile_audio_super_resolution_openvino/resolve/9a97d7f128b22aea72e92862a3eccc310f88ac26/versatile_audio_sr_ddpm_speech_openvino_models.zip?download=true",
"sha256": "f44ee433da9e1aa6dd948142d81523c7a58213acfa0699a4014e1f6d30e8a349",
"dsize": 473616589,
"esize": 234
}
]
}
]
},
{
"name": "MusicGen",
"description": "MusicGen",
"dir": "musicgen",
"files": [
{
"url": "https://huggingface.co/Intel/musicgen-static-openvino/resolve/b2ad8083f3924ed704814b68c5df9cbbf2ad2aae/musicgen_small_enc_dec_tok_openvino_models.zip?download=true",
"sha256": "f5cdd695a52cf4c0619dd419c8bff4e71700ee805b547e86b7483423883d6f32",
"dsize": 279450559,
"esize": 234
}
],
"children":[
{
"name": "Small (mono)",
"description": "Mono",
"dir": "musicgen",
"files": [
{
"url": "https://huggingface.co/Intel/musicgen-static-openvino/resolve/b2ad8083f3924ed704814b68c5df9cbbf2ad2aae/musicgen_small_stereo_openvino_models.zip?download=true",
"sha256": "1c6496267b22175ecdd2518b9ed8c9870454674fc08fac1c864ca62e3d87e8e7",
"dsize": 1154648066,
"esize": 234
}
]
},
{
"name": "Small (stereo)",
"description": "Stereo",
"dir": "musicgen",
"files": [
{
"url": "https://huggingface.co/Intel/musicgen-static-openvino/resolve/b2ad8083f3924ed704814b68c5df9cbbf2ad2aae/musicgen_small_mono_openvino_models.zip?download=true",
"sha256": "b1e47df3ec60d0c5f70ba664fea636df9bc94710269ee72357a3353fd579afd9",
"dsize": 1098243900,
"esize": 234
}
]
}
]
},
{
"name": "Whisper",
"description": "Whisper Base",
"dir": ".",
"files": [
{
"url": "https://huggingface.co/Intel/whisper.cpp-openvino-models/resolve/194efafbee09390bbf33f33d02d9856dacfdd162/ggml-base-models.zip?download=true",
"sha256": "18f6e87146bfe9986fa764657cca26695bcb7fa3ba5d77d6d6af1d38de720e4c",
"dsize": 171306938,
"esize": 234
}
],
"children":[
{
"name": "Small",
"description": "Small",
"dir": ".",
"files": [
{
"url": "https://huggingface.co/Intel/whisper.cpp-openvino-models/resolve/194efafbee09390bbf33f33d02d9856dacfdd162/ggml-small-models.zip?download=true",
"sha256": "7efabdcb08ec09ee24b615db9fe86425114a46d87a25d1e3d14ecd7195dd4e9f",
"dsize": 610456149,
"esize": 234
}
]
},
{
"name": "Small (English only)",
"description": "Small English",
"dir": ".",
"files": [
{
"url": "https://huggingface.co/Intel/whisper.cpp-openvino-models/resolve/194efafbee09390bbf33f33d02d9856dacfdd162/ggml-small.en-tdrz-models.zip?download=true",
"sha256": "ea825cb105e48aa0796e332c93827339dd60aa1a61633df14bb6611bcef26b95",
"dsize": 609030010,
"esize": 234
}
]
},
{
"name": "Medium",
"description": "Medium",
"dir": ".",
"files": [
{
"url": "https://huggingface.co/Intel/whisper.cpp-openvino-models/resolve/5c2f20da06aa17198dbd778d4190383557c23b1b/ggml-medium-models.zip?download=true",
"sha256": "afd961c0bac5830dbcbd15826f49553a5d5fb8a09c7a24eb634304fa068fe59f",
"dsize": 2110515739,
"esize": 234
}
]
},
{
"name": "Large v1",
"description": "Large V1",
"dir": ".",
"files": [
{
"url": "https://huggingface.co/Intel/whisper.cpp-openvino-models/resolve/9731937f2b9ca0aff8b8acd031ed77a1a378cc72/ggml-large-v1-models.zip?download=true",
"sha256": "fa187861eb46b701f242d63fc0067878de6345a71714d926c4b0eecf1ec0fffa",
"dsize": 4301056953,
"esize": 234
}
]
},
{
"name": "Large v2",
"description": "Large V2",
"dir": ".",
"files": [
{
"url": "https://huggingface.co/Intel/whisper.cpp-openvino-models/resolve/dc08ae819b895594cae08b0210bee051462aba75/ggml-large-v2-models.zip?download=true",
"sha256": "ab306b0a0a93a731e56efbfa4e80448206bd9b5b863d5a99e40a3532d64ec754",
"dsize": 4285702502,
"esize": 234
}
]
},
{
"name": "Large v3",
"description": "Large V3",
"dir": ".",
"files": [
{
"url": "https://huggingface.co/Intel/whisper.cpp-openvino-models/resolve/d0c3075c40e4938fb2a4cccfc91704106d3e7d12/ggml-large-v3-models.zip?download=true",
"sha256": "d61160dc5b1c1abc1dbdd6ae571fe4da87079fa3170156408f8775b493c15cdd",
"dsize": 4277163902,
"esize": 234
}
]
}
]
},
{
"name": "Noise Suppression",
"description": "Noise Suppression",
"dir": ".",
"files": [],
"children":[
{
"name": "Deep Filter Net 2",
"description": "Deep Filter Net 2",
"dir": ".",
"files": [
{
"url": "https://huggingface.co/Intel/deepfilternet-openvino/resolve/995706bda3da69da0825074ba7dbc8a78067e980/deepfilternet2.zip?download=true",
"sha256": "9d10c9c77a64121587e7d94a3d4d5cfeb67e9bfd2e416d48d1f74ac725c4f66e",
"dsize": 8648697,
"esize": 234
}
]
},
{
"name": "Deep Filter Net 3",
"description": "Deep Filter Net 3",
"dir": ".",
"files": [
{
"url": "https://huggingface.co/Intel/deepfilternet-openvino/resolve/995706bda3da69da0825074ba7dbc8a78067e980/deepfilternet3.zip?download=true",
"sha256": "fdc74e11439ca09a106f5bd77ed24002ef4d0e02114238a271c069d06172f341",
"dsize": 8004431,
"esize": 234
}
]
},
{
"name": "Dense UNet",
"description": "Dense UNet",
"dir": ".",
"files": [
{
"url": "https://storage.openvinotoolkit.org/repositories/open_model_zoo/2023.0/models_bin/1/noise-suppression-denseunet-ll-0001/FP16/noise-suppression-denseunet-ll-0001.bin",
"sha256": "da59b41a656b2948a4b45580b4870614d2dfa071a7566555aea4547423888e08",
"dsize": 8625568,
"esize": 234
},
{
"url": "https://storage.openvinotoolkit.org/repositories/open_model_zoo/2023.0/models_bin/1/noise-suppression-denseunet-ll-0001/FP16/noise-suppression-denseunet-ll-0001.xml",
"sha256": "89116a01cc59f7ac3f1f1365c851e47c9089fd33ca86712c30dc1f0ee7d14803",
"dsize": 689820,
"esize": 234
}
]
}
]
},
{
"name": "Music Separation",
"description": "Music Separation",
"dir": ".",
"children":[
{
"name": "Basic",
"description": "Basic",
"dir": ".",
"files": [
{
"url": "https://huggingface.co/Intel/demucs-openvino/resolve/97fc578fb57650045d40b00bc84c7d156be77547/htdemucs_v4.bin?download=true",
"sha256": "7aa84fa1f2b534bd6865a5609b8b5b028802fe761d6a09b1d30a1564f8fac6f8",
"dsize": 101167138,
"esize": 234
},
{
"url": "https://huggingface.co/Intel/demucs-openvino/resolve/97fc578fb57650045d40b00bc84c7d156be77547/htdemucs_v4.xml?download=true",
"sha256": "304e24325756089d6bb6583171dd1bea2327505e87c6cb6e010afea7463d9f0a",
"dsize": 1875040,
"esize": 234
}
]
}
]
}
]

View File

@ -0,0 +1,10 @@
import Testing
@testable import ResourceDownloader
struct ResourceDownloaderTests {
@Test func example() async throws {
// Write your test here and use APIs like `#expect(...)` to check expected conditions.
}
}

View File

@ -0,0 +1,34 @@
import XCTest
final class ResourceDownloaderUITests: XCTestCase {
override func setUpWithError() throws {
// Put setup code here. This method is called before the invocation of each test method in the class.
// In UI tests it is usually best to stop immediately when a failure occurs.
continueAfterFailure = false
// In UI tests its important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
}
override func tearDownWithError() throws {
// Put teardown code here. This method is called after the invocation of each test method in the class.
}
@MainActor
func testExample() throws {
// UI tests must launch the application that they test.
let app = XCUIApplication()
app.launch()
// Use XCTAssert and related functions to verify your tests produce the correct results.
}
@MainActor
func testLaunchPerformance() throws {
// This measures how long it takes to launch your application.
measure(metrics: [XCTApplicationLaunchMetric()]) {
XCUIApplication().launch()
}
}
}

View File

@ -0,0 +1,26 @@
import XCTest
final class ResourceDownloaderUITestsLaunchTests: XCTestCase {
override class var runsForEachTargetApplicationUIConfiguration: Bool {
true
}
override func setUpWithError() throws {
continueAfterFailure = false
}
@MainActor
func testLaunch() throws {
let app = XCUIApplication()
app.launch()
// Insert steps here to perform after app launch but before taking a screenshot,
// such as logging into a test account or navigating somewhere in the app
let attachment = XCTAttachment(screenshot: app.screenshot())
attachment.name = "Launch Screen"
attachment.lifetime = .keepAlways
add(attachment)
}
}