parent
72af0f5697
commit
b0ac915265
@ -0,0 +1,197 @@
|
|||||||
|
//
|
||||||
|
// CoremlEncoder.m
|
||||||
|
//
|
||||||
|
// This file was automatically generated and should not be edited.
|
||||||
|
//
|
||||||
|
|
||||||
|
#if !__has_feature(objc_arc)
|
||||||
|
#error This file must be compiled with automatic reference counting enabled (-fobjc-arc)
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#import "whisper-encoder-impl.h"
|
||||||
|
|
||||||
|
@implementation CoremlEncoderInput
|
||||||
|
|
||||||
|
- (instancetype)initWithMelSegment:(MLMultiArray *)melSegment {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_melSegment = melSegment;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSSet<NSString *> *)featureNames {
|
||||||
|
return [NSSet setWithArray:@[@"melSegment"]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable MLFeatureValue *)featureValueForName:(NSString *)featureName {
|
||||||
|
if ([featureName isEqualToString:@"melSegment"]) {
|
||||||
|
return [MLFeatureValue featureValueWithMultiArray:self.melSegment];
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation CoremlEncoderOutput
|
||||||
|
|
||||||
|
- (instancetype)initWithOutput:(MLMultiArray *)output {
|
||||||
|
self = [super init];
|
||||||
|
if (self) {
|
||||||
|
_output = output;
|
||||||
|
}
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
- (NSSet<NSString *> *)featureNames {
|
||||||
|
return [NSSet setWithArray:@[@"output"]];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable MLFeatureValue *)featureValueForName:(NSString *)featureName {
|
||||||
|
if ([featureName isEqualToString:@"output"]) {
|
||||||
|
return [MLFeatureValue featureValueWithMultiArray:self.output];
|
||||||
|
}
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
@implementation CoremlEncoder
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
URL of the underlying .mlmodelc directory.
|
||||||
|
*/
|
||||||
|
+ (nullable NSURL *)URLOfModelInThisBundle {
|
||||||
|
NSString *assetPath = [[NSBundle bundleForClass:[self class]] pathForResource:@"CoremlEncoder" ofType:@"mlmodelc"];
|
||||||
|
if (nil == assetPath) { os_log_error(OS_LOG_DEFAULT, "Could not load CoremlEncoder.mlmodelc in the bundle resource"); return nil; }
|
||||||
|
return [NSURL fileURLWithPath:assetPath];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Initialize CoremlEncoder instance from an existing MLModel object.
|
||||||
|
|
||||||
|
Usually the application does not use this initializer unless it makes a subclass of CoremlEncoder.
|
||||||
|
Such application may want to use `-[MLModel initWithContentsOfURL:configuration:error:]` and `+URLOfModelInThisBundle` to create a MLModel object to pass-in.
|
||||||
|
*/
|
||||||
|
- (instancetype)initWithMLModel:(MLModel *)model {
|
||||||
|
self = [super init];
|
||||||
|
if (!self) { return nil; }
|
||||||
|
_model = model;
|
||||||
|
if (_model == nil) { return nil; }
|
||||||
|
return self;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Initialize CoremlEncoder instance with the model in this bundle.
|
||||||
|
*/
|
||||||
|
- (nullable instancetype)init {
|
||||||
|
return [self initWithContentsOfURL:(NSURL * _Nonnull)self.class.URLOfModelInThisBundle error:nil];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Initialize CoremlEncoder instance with the model in this bundle.
|
||||||
|
|
||||||
|
@param configuration The model configuration object
|
||||||
|
@param error If an error occurs, upon return contains an NSError object that describes the problem. If you are not interested in possible errors, pass in NULL.
|
||||||
|
*/
|
||||||
|
- (nullable instancetype)initWithConfiguration:(MLModelConfiguration *)configuration error:(NSError * _Nullable __autoreleasing * _Nullable)error {
|
||||||
|
return [self initWithContentsOfURL:(NSURL * _Nonnull)self.class.URLOfModelInThisBundle configuration:configuration error:error];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Initialize CoremlEncoder instance from the model URL.
|
||||||
|
|
||||||
|
@param modelURL URL to the .mlmodelc directory for CoremlEncoder.
|
||||||
|
@param error If an error occurs, upon return contains an NSError object that describes the problem. If you are not interested in possible errors, pass in NULL.
|
||||||
|
*/
|
||||||
|
- (nullable instancetype)initWithContentsOfURL:(NSURL *)modelURL error:(NSError * _Nullable __autoreleasing * _Nullable)error {
|
||||||
|
MLModel *model = [MLModel modelWithContentsOfURL:modelURL error:error];
|
||||||
|
if (model == nil) { return nil; }
|
||||||
|
return [self initWithMLModel:model];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Initialize CoremlEncoder instance from the model URL.
|
||||||
|
|
||||||
|
@param modelURL URL to the .mlmodelc directory for CoremlEncoder.
|
||||||
|
@param configuration The model configuration object
|
||||||
|
@param error If an error occurs, upon return contains an NSError object that describes the problem. If you are not interested in possible errors, pass in NULL.
|
||||||
|
*/
|
||||||
|
- (nullable instancetype)initWithContentsOfURL:(NSURL *)modelURL configuration:(MLModelConfiguration *)configuration error:(NSError * _Nullable __autoreleasing * _Nullable)error {
|
||||||
|
MLModel *model = [MLModel modelWithContentsOfURL:modelURL configuration:configuration error:error];
|
||||||
|
if (model == nil) { return nil; }
|
||||||
|
return [self initWithMLModel:model];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Construct CoremlEncoder instance asynchronously with configuration.
|
||||||
|
Model loading may take time when the model content is not immediately available (e.g. encrypted model). Use this factory method especially when the caller is on the main thread.
|
||||||
|
|
||||||
|
@param configuration The model configuration
|
||||||
|
@param handler When the model load completes successfully or unsuccessfully, the completion handler is invoked with a valid CoremlEncoder instance or NSError object.
|
||||||
|
*/
|
||||||
|
+ (void)loadWithConfiguration:(MLModelConfiguration *)configuration completionHandler:(void (^)(CoremlEncoder * _Nullable model, NSError * _Nullable error))handler {
|
||||||
|
[self loadContentsOfURL:(NSURL * _Nonnull)[self URLOfModelInThisBundle]
|
||||||
|
configuration:configuration
|
||||||
|
completionHandler:handler];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
Construct CoremlEncoder instance asynchronously with URL of .mlmodelc directory and optional configuration.
|
||||||
|
|
||||||
|
Model loading may take time when the model content is not immediately available (e.g. encrypted model). Use this factory method especially when the caller is on the main thread.
|
||||||
|
|
||||||
|
@param modelURL The model URL.
|
||||||
|
@param configuration The model configuration
|
||||||
|
@param handler When the model load completes successfully or unsuccessfully, the completion handler is invoked with a valid CoremlEncoder instance or NSError object.
|
||||||
|
*/
|
||||||
|
+ (void)loadContentsOfURL:(NSURL *)modelURL configuration:(MLModelConfiguration *)configuration completionHandler:(void (^)(CoremlEncoder * _Nullable model, NSError * _Nullable error))handler {
|
||||||
|
[MLModel loadContentsOfURL:modelURL
|
||||||
|
configuration:configuration
|
||||||
|
completionHandler:^(MLModel *model, NSError *error) {
|
||||||
|
if (model != nil) {
|
||||||
|
CoremlEncoder *typedModel = [[CoremlEncoder alloc] initWithMLModel:model];
|
||||||
|
handler(typedModel, nil);
|
||||||
|
} else {
|
||||||
|
handler(nil, error);
|
||||||
|
}
|
||||||
|
}];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable CoremlEncoderOutput *)predictionFromFeatures:(CoremlEncoderInput *)input error:(NSError * _Nullable __autoreleasing * _Nullable)error {
|
||||||
|
return [self predictionFromFeatures:input options:[[MLPredictionOptions alloc] init] error:error];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable CoremlEncoderOutput *)predictionFromFeatures:(CoremlEncoderInput *)input options:(MLPredictionOptions *)options error:(NSError * _Nullable __autoreleasing * _Nullable)error {
|
||||||
|
id<MLFeatureProvider> outFeatures = [self.model predictionFromFeatures:input options:options error:error];
|
||||||
|
if (!outFeatures) { return nil; }
|
||||||
|
return [[CoremlEncoderOutput alloc] initWithOutput:(MLMultiArray *)[outFeatures featureValueForName:@"output"].multiArrayValue];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable CoremlEncoderOutput *)predictionFromMelSegment:(MLMultiArray *)melSegment error:(NSError * _Nullable __autoreleasing * _Nullable)error {
|
||||||
|
CoremlEncoderInput *input_ = [[CoremlEncoderInput alloc] initWithMelSegment:melSegment];
|
||||||
|
return [self predictionFromFeatures:input_ error:error];
|
||||||
|
}
|
||||||
|
|
||||||
|
- (nullable NSArray<CoremlEncoderOutput *> *)predictionsFromInputs:(NSArray<CoremlEncoderInput*> *)inputArray options:(MLPredictionOptions *)options error:(NSError * _Nullable __autoreleasing * _Nullable)error {
|
||||||
|
id<MLBatchProvider> inBatch = [[MLArrayBatchProvider alloc] initWithFeatureProviderArray:inputArray];
|
||||||
|
id<MLBatchProvider> outBatch = [self.model predictionsFromBatch:inBatch options:options error:error];
|
||||||
|
if (!outBatch) { return nil; }
|
||||||
|
NSMutableArray<CoremlEncoderOutput*> *results = [NSMutableArray arrayWithCapacity:(NSUInteger)outBatch.count];
|
||||||
|
for (NSInteger i = 0; i < outBatch.count; i++) {
|
||||||
|
id<MLFeatureProvider> resultProvider = [outBatch featuresAtIndex:i];
|
||||||
|
CoremlEncoderOutput * result = [[CoremlEncoderOutput alloc] initWithOutput:(MLMultiArray *)[resultProvider featureValueForName:@"output"].multiArrayValue];
|
||||||
|
[results addObject:result];
|
||||||
|
}
|
||||||
|
return results;
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
@ -0,0 +1,22 @@
|
|||||||
|
// Wrapper of the Core ML Whisper Encoder model
|
||||||
|
//
|
||||||
|
// Code is derived from the work of Github user @wangchou
|
||||||
|
// ref: https://github.com/wangchou/callCoreMLFromCpp
|
||||||
|
|
||||||
|
#if __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct whisper_coreml_context;
|
||||||
|
|
||||||
|
struct whisper_coreml_context * whisper_coreml_init(const char * path_model);
|
||||||
|
void whisper_coreml_free(struct whisper_coreml_context * ctx);
|
||||||
|
|
||||||
|
void whisper_coreml_encode(
|
||||||
|
const whisper_coreml_context * ctx,
|
||||||
|
float * mel,
|
||||||
|
float * out);
|
||||||
|
|
||||||
|
#if __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
@ -0,0 +1,61 @@
|
|||||||
|
#import "coreml/whisper-encoder.h"
|
||||||
|
#import "coreml/whisper-encoder-impl.h"
|
||||||
|
|
||||||
|
#import <CoreML/CoreML.h>
|
||||||
|
|
||||||
|
#include <stdlib.h>
|
||||||
|
|
||||||
|
#if __cplusplus
|
||||||
|
extern "C" {
|
||||||
|
#endif
|
||||||
|
|
||||||
|
struct whisper_coreml_context {
|
||||||
|
const void * data;
|
||||||
|
};
|
||||||
|
|
||||||
|
struct whisper_coreml_context * whisper_coreml_init(const char * path_model) {
|
||||||
|
NSString * path_model_str = [[NSString alloc] initWithUTF8String:path_model];
|
||||||
|
|
||||||
|
NSURL * url_model = [NSURL fileURLWithPath: path_model_str];
|
||||||
|
|
||||||
|
const void * data = CFBridgingRetain([[CoremlEncoder alloc] initWithContentsOfURL:url_model error:nil]);
|
||||||
|
|
||||||
|
if (data == NULL) {
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
whisper_coreml_context * ctx = new whisper_coreml_context;
|
||||||
|
|
||||||
|
ctx->data = data;
|
||||||
|
|
||||||
|
return ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void whisper_coreml_free(struct whisper_coreml_context * ctx) {
|
||||||
|
CFRelease(ctx->data);
|
||||||
|
delete ctx;
|
||||||
|
}
|
||||||
|
|
||||||
|
void whisper_coreml_encode(
|
||||||
|
const whisper_coreml_context * ctx,
|
||||||
|
float * mel,
|
||||||
|
float * out) {
|
||||||
|
MLMultiArray * inMultiArray = [
|
||||||
|
[MLMultiArray alloc] initWithDataPointer: mel
|
||||||
|
shape: @[@1, @80, @3000]
|
||||||
|
dataType: MLMultiArrayDataTypeFloat32
|
||||||
|
strides: @[@(240000), @(3000), @1]
|
||||||
|
deallocator: nil
|
||||||
|
error: nil
|
||||||
|
];
|
||||||
|
|
||||||
|
CoremlEncoderOutput * outCoreML = [(__bridge id) ctx->data predictionFromMelSegment:inMultiArray error:nil];
|
||||||
|
|
||||||
|
MLMultiArray * outMA = outCoreML.output;
|
||||||
|
|
||||||
|
memcpy(out, outMA.dataPointer, outMA.count * sizeof(float));
|
||||||
|
}
|
||||||
|
|
||||||
|
#if __cplusplus
|
||||||
|
}
|
||||||
|
#endif
|
@ -0,0 +1,82 @@
|
|||||||
|
#!/bin/bash
|
||||||
|
|
||||||
|
# This script downloads Whisper model files that have already been converted to Core ML format.
|
||||||
|
# This way you don't have to convert them yourself.
|
||||||
|
|
||||||
|
src="https://huggingface.co/datasets/ggerganov/whisper.cpp-coreml"
|
||||||
|
pfx="resolve/main/ggml"
|
||||||
|
|
||||||
|
# get the path of this script
|
||||||
|
function get_script_path() {
|
||||||
|
if [ -x "$(command -v realpath)" ]; then
|
||||||
|
echo "$(dirname $(realpath $0))"
|
||||||
|
else
|
||||||
|
local ret="$(cd -- "$(dirname "$0")" >/dev/null 2>&1 ; pwd -P)"
|
||||||
|
echo "$ret"
|
||||||
|
fi
|
||||||
|
}
|
||||||
|
|
||||||
|
models_path="$(get_script_path)"
|
||||||
|
|
||||||
|
# Whisper models
|
||||||
|
models=( "tiny.en" "tiny" "base.en" "base" "small.en" "small" "medium.en" "medium" "large-v1" "large" )
|
||||||
|
|
||||||
|
# list available models
|
||||||
|
function list_models {
|
||||||
|
printf "\n"
|
||||||
|
printf " Available models:"
|
||||||
|
for model in "${models[@]}"; do
|
||||||
|
printf " $model"
|
||||||
|
done
|
||||||
|
printf "\n\n"
|
||||||
|
}
|
||||||
|
|
||||||
|
if [ "$#" -ne 1 ]; then
|
||||||
|
printf "Usage: $0 <model>\n"
|
||||||
|
list_models
|
||||||
|
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
model=$1
|
||||||
|
|
||||||
|
if [[ ! " ${models[@]} " =~ " ${model} " ]]; then
|
||||||
|
printf "Invalid model: $model\n"
|
||||||
|
list_models
|
||||||
|
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
# download Core ML model
|
||||||
|
|
||||||
|
printf "Downloading Core ML model $model from '$src' ...\n"
|
||||||
|
|
||||||
|
cd $models_path
|
||||||
|
|
||||||
|
if [ -f "ggml-$model.mlmodel" ]; then
|
||||||
|
printf "Model $model already exists. Skipping download.\n"
|
||||||
|
exit 0
|
||||||
|
fi
|
||||||
|
|
||||||
|
if [ -x "$(command -v wget)" ]; then
|
||||||
|
wget --quiet --show-progress -O ggml-$model.mlmodel $src/$pfx-$model.mlmodel
|
||||||
|
elif [ -x "$(command -v curl)" ]; then
|
||||||
|
curl -L --output ggml-$model.mlmodel $src/$pfx-$model.mlmodel
|
||||||
|
else
|
||||||
|
printf "Either wget or curl is required to download models.\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
|
||||||
|
if [ $? -ne 0 ]; then
|
||||||
|
printf "Failed to download Core ML model $model \n"
|
||||||
|
printf "Please try again later or download the original Whisper model files and convert them yourself.\n"
|
||||||
|
exit 1
|
||||||
|
fi
|
||||||
|
|
||||||
|
printf "Done! Model '$model' saved in 'models/ggml-$model.mlmodel'\n"
|
||||||
|
printf "Run the following command to compile it:\n\n"
|
||||||
|
printf " $ xcrun coremlc compile ./models/ggml-$model.mlmodel ./models\n\n"
|
||||||
|
printf "You can now use it like this:\n\n"
|
||||||
|
printf " $ ./main -m models/ggml-$model.bin -f samples/jfk.wav\n"
|
||||||
|
printf "\n"
|
Loading…
Reference in new issue