@aituber-onair/voice
Version:
Voice synthesis library for AITuber OnAir
241 lines (240 loc) • 10.4 kB
JavaScript
"use strict";
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.VoiceEngineAdapter = void 0;
const screenplay_1 = require("../utils/screenplay");
const AudioPlayerFactory_1 = require("./audio/AudioPlayerFactory");
/**
* Adapter implementation for using existing voice engines
*/
class VoiceEngineAdapter {
/**
* Constructor
* @param options Voice service options
*/
constructor(options) {
this.options = options;
this.audioPlayer = AudioPlayerFactory_1.AudioPlayerFactory.createAudioPlayer();
// Set up completion callback
this.audioPlayer.setOnComplete(() => {
if (this.options.onComplete) {
this.options.onComplete();
}
});
}
/**
* Speak the screenplay as audio
* @param screenplay Screenplay (text and emotion)
* @param options Audio playback options
*/
async speak(screenplay, options) {
try {
if (this.audioPlayer.isPlaying()) {
this.stop();
}
// Import existing VoiceEngineFactory dynamically
const { VoiceEngineFactory } = await Promise.resolve().then(() => __importStar(require('../engines/VoiceEngineFactory')));
// Map emotion to style used by existing engine
const getStyleFromEmotion = (emotion) => {
switch (emotion) {
case 'angry':
return 'angry';
case 'happy':
return 'happy';
case 'sad':
return 'sad';
case 'surprised':
return 'surprised';
default:
return 'neutral';
}
};
// Convert to Talk type for VoiceEngine
const talk = {
style: getStyleFromEmotion(screenplay.emotion || 'neutral'),
message: screenplay.text,
};
const engine = VoiceEngineFactory.getEngine(this.options.engineType);
// カスタムエンドポイントURLの設定
if (engine.setApiEndpoint) {
// エンジンタイプに応じてカスタムエンドポイントURLを設定
switch (this.options.engineType) {
case 'voicevox':
if (this.options.voicevoxApiUrl) {
engine.setApiEndpoint(this.options.voicevoxApiUrl);
}
break;
case 'voicepeak':
if (this.options.voicepeakApiUrl) {
engine.setApiEndpoint(this.options.voicepeakApiUrl);
}
break;
case 'aivisSpeech':
if (this.options.aivisSpeechApiUrl) {
engine.setApiEndpoint(this.options.aivisSpeechApiUrl);
}
break;
}
}
// MiniMaxエンジンの場合、GroupIdを設定
if (this.options.engineType === 'minimax' && engine.setGroupId) {
if (this.options.groupId) {
engine.setGroupId(this.options.groupId);
}
else {
console.warn('MiniMax engine requires GroupId, but it is not provided in options');
}
// エンドポイントの設定もMinimaxEngineでサポートされている場合
if (this.options.endpoint && engine.setEndpoint) {
engine.setEndpoint(this.options.endpoint);
}
}
// Aivis Cloud エンジンの場合、各種パラメータを設定
if (this.options.engineType === 'aivisCloud') {
const aivisEngine = engine;
// Model UUID (required)
if (this.options.aivisCloudModelUuid) {
aivisEngine.setModelUuid(this.options.aivisCloudModelUuid);
}
// Speaker UUID (optional)
if (this.options.aivisCloudSpeakerUuid) {
aivisEngine.setSpeakerUuid(this.options.aivisCloudSpeakerUuid);
}
// Style settings (style ID or name, but not both)
if (this.options.aivisCloudStyleId !== undefined) {
aivisEngine.setStyleId(this.options.aivisCloudStyleId);
}
else if (this.options.aivisCloudStyleName) {
aivisEngine.setStyleName(this.options.aivisCloudStyleName);
}
// SSML setting
if (this.options.aivisCloudUseSSML !== undefined) {
aivisEngine.setUseSSML(this.options.aivisCloudUseSSML);
}
// Voice parameters
if (this.options.aivisCloudSpeakingRate !== undefined) {
aivisEngine.setSpeakingRate(this.options.aivisCloudSpeakingRate);
}
if (this.options.aivisCloudEmotionalIntensity !== undefined) {
aivisEngine.setEmotionalIntensity(this.options.aivisCloudEmotionalIntensity);
}
if (this.options.aivisCloudTempoDynamics !== undefined) {
aivisEngine.setTempoDynamics(this.options.aivisCloudTempoDynamics);
}
if (this.options.aivisCloudPitch !== undefined) {
aivisEngine.setPitch(this.options.aivisCloudPitch);
}
if (this.options.aivisCloudVolume !== undefined) {
aivisEngine.setVolume(this.options.aivisCloudVolume);
}
// Silence settings
if (this.options.aivisCloudLeadingSilence !== undefined ||
this.options.aivisCloudTrailingSilence !== undefined ||
this.options.aivisCloudLineBreakSilence !== undefined) {
aivisEngine.setSilenceDurations(this.options.aivisCloudLeadingSilence ?? 0.1, this.options.aivisCloudTrailingSilence ?? 0.1, this.options.aivisCloudLineBreakSilence ?? 0.4);
}
// Output format settings
if (this.options.aivisCloudOutputFormat) {
aivisEngine.setOutputFormat(this.options.aivisCloudOutputFormat);
}
if (this.options.aivisCloudOutputBitrate !== undefined) {
aivisEngine.setOutputBitrate(this.options.aivisCloudOutputBitrate);
}
if (this.options.aivisCloudOutputSamplingRate !== undefined) {
aivisEngine.setOutputSamplingRate(this.options.aivisCloudOutputSamplingRate);
}
if (this.options.aivisCloudOutputChannels) {
aivisEngine.setOutputChannels(this.options.aivisCloudOutputChannels);
}
// Billing logs setting
if (this.options.aivisCloudEnableBillingLogs !== undefined) {
aivisEngine.setEnableBillingLogs(this.options.aivisCloudEnableBillingLogs);
}
}
// Get audio data
const audioBuffer = await engine.fetchAudio(talk, // Use any for type compatibility
this.options.speaker, this.options.apiKey);
// If there is a custom playback process, use it
if (this.options.onPlay) {
await this.options.onPlay(audioBuffer, options);
return;
}
// Default playback process
await this.audioPlayer.play(audioBuffer, options?.audioElementId);
}
catch (error) {
console.error('Error in speak:', error);
throw error;
}
}
/**
* Speak text as audio
* @param text Text (with emotion tags) to speak
* @param options Audio playback options
*/
async speakText(text, options) {
// Convert text to screenplay and play
const screenplay = (0, screenplay_1.textToScreenplay)(text);
return this.speak(screenplay, options);
}
/**
* Get whether currently playing
*/
isPlaying() {
return this.audioPlayer.isPlaying();
}
/**
* Stop playback
*/
stop() {
this.audioPlayer.stop();
}
/**
* Update service settings
* @param options New settings options
*/
updateOptions(options) {
this.options = { ...this.options, ...options };
// Update audio player callback if onComplete changes
if (options.onComplete !== undefined) {
this.audioPlayer.setOnComplete(() => {
if (this.options.onComplete) {
this.options.onComplete();
}
});
}
}
}
exports.VoiceEngineAdapter = VoiceEngineAdapter;