Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 20 additions & 1 deletion .github/workflows/build-release.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,10 @@ on:
NPM_TOKEN:
required: true

env:
HEXAGON_SDK_VERSION: '6.4.0.2'
OPENCL_VERSION: '2025.07.22'

jobs:
build:
runs-on: ${{ matrix.os }}
Expand Down Expand Up @@ -113,6 +117,19 @@ jobs:
target: vulkan
package: node-whisper-win32-arm64-vulkan
toolchain: mingw-clang
# Linux arm64 Snapdragon (cross-compiled on x86_64)
- os: ubuntu-24.04
arch: arm64
target: snapdragon
package: node-whisper-linux-arm64-snapdragon
cross-compile: true
# TODO: uncomment this when Windows arm64 Snapdragon is supported
# # Windows arm64 Snapdragon
# - os: windows-latest
# arch: arm64
# target: snapdragon
# package: node-whisper-win32-arm64-snapdragon
# toolchain: mingw-clang
steps:
- name: Run this job?
id: run
Expand Down Expand Up @@ -186,7 +203,9 @@ jobs:
uses: actions/upload-artifact@v4
with:
name: bin-${{ matrix.package }}
path: packages/${{ matrix.package }}
path: |
build
packages/${{ matrix.package }}
retention-days: ${{ inputs.artifacts-retention-days }}
- name: Publish to NPM
if: steps.run.outputs.should-run == 'true' && (github.event.inputs.publish == 'YES' || inputs.publish == 'YES')
Expand Down
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,8 @@ tmp.sess
*.node
package-lock.json
.cache
*.dll
*.so
*.dylib

.python-version
24 changes: 22 additions & 2 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,8 @@ else()
endif()

if (TO_PACKAGE)
set(PLATFORM_BINARY_DIR ${CMAKE_SOURCE_DIR}/packages/node-whisper-${PLATFORM}-${ARCH}${VARIANT})
set(PACKAGE_NAME "node-whisper-${PLATFORM}-${ARCH}${VARIANT}")
set(PLATFORM_BINARY_DIR ${CMAKE_SOURCE_DIR}/packages/${PACKAGE_NAME})
else()
set(PLATFORM_BINARY_DIR ${CMAKE_SOURCE_DIR}/build/Release)
endif()
Expand Down Expand Up @@ -148,7 +149,7 @@ if (NOT MSVC AND CMAKE_SYSTEM_NAME STREQUAL "Windows")

add_library(win_dynamic_load ${WIN_DYNAMIC_LOAD_SRC})
set_target_properties(win_dynamic_load PROPERTIES COMPILE_FLAGS "-Wno-implicit-function-declaration")

unset(CMAKE_JS_SRC)
unset(CMAKE_JS_LIB)
unset(CMAKE_JS_NODELIB_DEF)
Expand All @@ -159,6 +160,13 @@ if (NOT MSVC AND CMAKE_SYSTEM_NAME STREQUAL "Windows")
set(CMAKE_JS_LIB win_dynamic_load)
endif()

if (TO_PACKAGE AND GGML_HEXAGON)
set(NODE_RPATH "node_modules/@fugood/${PACKAGE_NAME}")
set(ELECTRON_ASAR_RPATH "resources/app.asar.unpacked/node_modules/@fugood/${PACKAGE_NAME}")
set(ELECTRON_RES_RPATH "resources/node_modules/@fugood/${PACKAGE_NAME}")
set(CMAKE_SHARED_LINKER_FLAGS "${CMAKE_SHARED_LINKER_FLAGS} -Wl,-rpath,${NODE_RPATH} -Wl,-rpath,${ELECTRON_ASAR_RPATH} -Wl,-rpath,${ELECTRON_RES_RPATH}")
endif()

add_library(${PROJECT_NAME} SHARED ${SOURCE_FILES} ${CMAKE_JS_SRC})
set_target_properties(${PROJECT_NAME} PROPERTIES PREFIX "" SUFFIX ".node")
target_link_libraries(${PROJECT_NAME} ${CMAKE_JS_LIB} whisper ggml ${CMAKE_THREAD_LIBS_INIT})
Expand Down Expand Up @@ -208,3 +216,15 @@ if (GGML_CLBLAST AND TO_PACKAGE)
)
endif()
endif()

if (GGML_HEXAGON)
get_target_property(HTP_LIBS_DIR ggml-hexagon BINARY_DIR)
add_custom_command(
TARGET copy_assets
COMMAND ${CMAKE_COMMAND} -E copy ${HTP_LIBS_DIR}/libggml-htp-v73.so ${PLATFORM_BINARY_DIR}
COMMAND ${CMAKE_COMMAND} -E copy ${HTP_LIBS_DIR}/libggml-htp-v75.so ${PLATFORM_BINARY_DIR}
COMMAND ${CMAKE_COMMAND} -E copy ${HTP_LIBS_DIR}/libggml-htp-v79.so ${PLATFORM_BINARY_DIR}
COMMAND ${CMAKE_COMMAND} -E copy ${HTP_LIBS_DIR}/libggml-htp-v81.so ${PLATFORM_BINARY_DIR}
COMMENT "Copying HTP libraries to bin folder"
)
endif()
13 changes: 13 additions & 0 deletions cmake/aarch64-linux-gnu.toolchain.cmake
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
SET(CMAKE_SYSTEM_NAME Linux)
SET(CMAKE_SYSTEM_PROCESSOR aarch64)

set(ARCH_PREFIX aarch64-linux-gnu)

SET(CMAKE_C_COMPILER ${ARCH_PREFIX}-gcc)
SET(CMAKE_CXX_COMPILER ${ARCH_PREFIX}-g++)

SET(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
SET(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
SET(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

SET(CROSS_COMPILE TRUE)
12 changes: 8 additions & 4 deletions examples/basicTranscription.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,14 @@ async function main() {
console.log(`Model: ${MODEL_PATH}`)

// Initialize whisper context
const context = await initWhisper({
filePath: MODEL_PATH,
useGpu: true, // Set to false if GPU is not available
})
const context = await initWhisper(
{
filePath: MODEL_PATH,
useGpu: true, // Set to false if GPU is not available
// useFlashAttn: true, // Recommend for GPU
},
process.env.WHISPER_LIB_VARIANT, // 'default' | 'vulkan' | 'cuda' | 'snapdragon'
)

console.log('Model loaded successfully!')
console.log()
Expand Down
12 changes: 8 additions & 4 deletions examples/bench.js
Original file line number Diff line number Diff line change
Expand Up @@ -19,10 +19,14 @@ async function main() {

// Initialize whisper context
console.log('Loading model...')
const context = await initWhisper({
filePath: modelPath,
useGpu: true, // Set to false to benchmark CPU only
})
const context = await initWhisper(
{
filePath: modelPath,
useGpu: true, // Set to false to benchmark CPU only
// useFlashAttn: true, // Recommend for GPU
},
process.env.WHISPER_LIB_VARIANT, // 'default' | 'vulkan' | 'cuda' | 'snapdragon'
)
console.log('Model loaded!')
console.log()

Expand Down
58 changes: 40 additions & 18 deletions lib/binding.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,15 @@
import path from 'path'

export interface NativeContextOptions {
filePath: string,
useFlashAttn?: boolean,
useGpu?: boolean,
filePath: string
useFlashAttn?: boolean
useGpu?: boolean
}

export interface NativeVadContextOptions {
filePath: string,
useGpu?: boolean,
nThreads?: number,
filePath: string
useGpu?: boolean
nThreads?: number
}

export interface TranscribeOptions {
Expand Down Expand Up @@ -56,17 +58,17 @@ export interface TranscribeResult {

export interface VadOptions {
/** Probability threshold to consider as speech (Default: 0.5) */
threshold?: number,
threshold?: number
/** Min duration for a valid speech segment in ms (Default: 250) */
minSpeechDurationMs?: number,
minSpeechDurationMs?: number
/** Min silence duration to consider speech as ended in ms (Default: 100) */
minSilenceDurationMs?: number,
minSilenceDurationMs?: number
/** Max duration of a speech segment before forcing a new segment in seconds (Default: 30) */
maxSpeechDurationS?: number,
maxSpeechDurationS?: number
/** Padding added before and after speech segments in ms (Default: 30) */
speechPadMs?: number,
speechPadMs?: number
/** Overlap in seconds when copying audio samples from speech segment (Default: 0.1) */
samplesOverlap?: number,
samplesOverlap?: number
}

export interface VadSegment {
Expand Down Expand Up @@ -109,7 +111,7 @@ export interface WhisperContext {
bench(nThreads: number): Promise<BenchResult>
release(): Promise<void>
getModelInfo(): object

// static methods
toggleNativeLog(
enable: boolean,
Expand All @@ -120,14 +122,17 @@ export interface WhisperContext {
export interface WhisperVadContext {
new (options: NativeVadContextOptions): WhisperVadContext
detectSpeech(filePath: string, options?: VadOptions): Promise<VadSegment[]>
detectSpeechFile(filePath: string, options?: VadOptions): Promise<VadSegment[]>
detectSpeechFile(
filePath: string,
options?: VadOptions,
): Promise<VadSegment[]>
detectSpeechData(
audioData: ArrayBuffer,
options?: VadOptions,
): Promise<VadSegment[]>
release(): Promise<void>
getModelInfo(): object

// static methods
toggleNativeLog(
enable: boolean,
Expand All @@ -140,7 +145,7 @@ export interface Module {
WhisperVadContext: WhisperVadContext
}

export type LibVariant = 'default' | 'vulkan' | 'cuda'
export type LibVariant = 'default' | 'vulkan' | 'cuda' | 'snapdragon'

const getPlatformPackageName = (variant?: LibVariant): string => {
const platform = process.platform
Expand All @@ -160,8 +165,25 @@ const loadPlatformPackage = async (
}

export const loadModule = async (variant?: LibVariant): Promise<Module> => {
// Try to load the requested variant
let module = await loadPlatformPackage(getPlatformPackageName(variant))
const packageName = getPlatformPackageName(variant)

// Set ADSP_LIBRARY_PATH for load HTP libs
if (variant === 'snapdragon') {
const adspLibraryPath = process.env.ADSP_LIBRARY_PATH
if (!adspLibraryPath) {
try {
process.env.ADSP_LIBRARY_PATH = path.dirname(
require.resolve(packageName),
)
} catch {
/* no-op */
}
}
const nDev = process.env.GGML_HEXAGON_NDEV
if (!nDev) process.env.GGML_HEXAGON_NDEV = '16'
}

let module = await loadPlatformPackage(packageName)
if (module) {
return module
}
Expand Down
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,7 @@
"@fugood/node-whisper-linux-arm64": "1.0.9",
"@fugood/node-whisper-linux-arm64-vulkan": "1.0.9",
"@fugood/node-whisper-linux-arm64-cuda": "1.0.9",
"@fugood/node-whisper-linux-arm64-snapdragon": "1.0.9",
"@fugood/node-whisper-win32-x64": "1.0.9",
"@fugood/node-whisper-win32-x64-vulkan": "1.0.9",
"@fugood/node-whisper-win32-x64-cuda": "1.0.9",
Expand Down Expand Up @@ -118,4 +119,4 @@
"singleQuote": true,
"printWidth": 80
}
}
}
16 changes: 16 additions & 0 deletions packages/node-whisper-linux-arm64-snapdragon/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# @fugood/node-whisper-linux-arm64-snapdragon

Native module for whisper.node targeting linux-arm64-snapdragon.

This package contains the pre-compiled native module for the specified platform and architecture with Qualcomm Snapdragon OpenCL and Hexagon support.

## Installation

This package is typically installed automatically as a dependency of `@fugood/whisper.node`.

## Platform Support

- **OS**: linux
- **Architecture**: arm64
- **Variant**: snapdragon
- **Backends**: OpenCL, Hexagon NPU
53 changes: 53 additions & 0 deletions packages/node-whisper-linux-arm64-snapdragon/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
{
"name": "@fugood/node-whisper-linux-arm64-snapdragon",
"version": "1.0.8",
"description": "Native module for An another Node binding of whisper.cpp (linux-arm64-snapdragon)",
"main": "index.node",
"os": [
"linux"
],
"cpu": [
"arm64"
],
"repository": {
"type": "git",
"url": "git+https://github.com/mybigday/whisper.node.git"
},
"keywords": [
"whisper",
"speech-recognition",
"audio",
"transcription",
"voice-activity-detection",
"vad",
"cpp",
"node-addon",
"whisper.cpp",
"linux",
"arm64",
"native",
"qualcomm",
"snapdragon",
"opencl",
"hexagon"
],
"authors": [
"ggml / whisper.cpp contributors",
"Hans <hans.chen@bricks.tools>",
"Jhen <developer@jhen.me>"
],
"license": "MIT",
"bugs": {
"url": "https://github.com/mybigday/whisper.node/issues"
},
"homepage": "https://github.com/mybigday/whisper.node#readme",
"publishConfig": {
"registry": "https://registry.npmjs.org",
"access": "public"
},
"files": [
"index.node",
"*.so"
],
"variant": "snapdragon"
}
16 changes: 16 additions & 0 deletions packages/node-whisper-win32-arm64-snapdragon/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
# @fugood/node-whisper-win32-arm64-snapdragon

Native module for whisper.node targeting win32-arm64-snapdragon.

This package contains the pre-compiled native module for the specified platform and architecture with Qualcomm Snapdragon OpenCL and Hexagon support.

## Installation

This package is typically installed automatically as a dependency of `@fugood/whisper.node`.

## Platform Support

- **OS**: win32
- **Architecture**: arm64
- **Variant**: snapdragon
- **Backends**: OpenCL, Hexagon NPU
Loading