AIパーソナル・ラーニング
と実践的なガイダンス

Edge TTS Worker: Cloudflare、OpenAI互換フォーマット、パッケージ化されたウェブインターフェースを使用したMicrosoft音声合成APIの展開

はじめに

エッジTTSワーカー(依存関係) エッジツ Edge TTS WorkerはCloudflare Worker上にデプロイされたプロキシサービスで、Microsoft Edge TTSサービスをOpenAIフォーマットと互換性のあるAPIインターフェースにカプセル化します。Edge TTS Workerは、中国語、英語、日本語、韓国語などの多言語をサポートし、Cloudflare Worker Free Planに基づく完全無料です。また、このサービスはカスタムAPIキーにも対応しており、セキュリティと制御を確実にし、数分以内の迅速な導入が可能です。

テストAPI: https://tts.aishare.us.kg/ KEY: aisharenet


API用のシンプルなインターフェースをパッケージ化するプロジェクト

Edge TTS Worker: Cloudflareを使用したMicrosoft音声合成APIのデプロイ、OpenAIフォーマットとパッケージングWebインターフェースに対応-1

 

APIとインターフェイスをCloudflareのデプロイメントとともにパッケージ化:

完全なコードはCHATGPTによって生成され、そのコードは記事の最後に添付されている。 AIプログラミング ツールまたは使用 トリクル (オンライン・アクセシブル・スピーチ生成サービスのワンクリック生成):

エッジTTSワーカー:マイクロソフト音声合成APIをCloudflareで展開、OpenAIフォーマットとパッケージングWebインターフェースに対応-1

経験:https://edgetts.aishare.us.kg/

 

機能一覧

  • OpenAI互換のインターフェース形式を提供
  • メインランドのアクセス制限を回避し、マイクロソフト・サービスの認証ステップをなくす
  • 中国語、英語、日本語、韓国語など多言語対応。
  • Cloudflare Worker Free Planに基づく完全無料。
  • カスタムAPIキーのサポートにより、セキュリティとコントロールを確保
  • 迅速な展開、数分で準備完了
  • さまざまな音声効果をテストするためのテストスクリプトを提供する。

 

ヘルプの使用

設置プロセス

  1. ワーカーの作成
    • Cloudflare Dashboardにログインする
    • Workers & Pagesに移動し、Create Workerをクリックします。
    • ワーカーに名前をつける(例:edge-ts)
  2. 配備コード
    • エディターからデフォルトのコードを削除する
    • のコピーを取る。 ワーカー にコードを貼り付けます。
    • 保存してデプロイをクリック
  3. APIキーの設定(オプション)
    • ワーカーの設定ページでSettings -> Variablesを見つける。
    • Add variableをクリックし、API_KEYに名前とキーの値を入力する。
    • 保存してデプロイをクリック
  4. カスタムドメイン名の設定(オプション)
    • 前提条件:ドメインがすでにCloudflareでホストされており、ドメインのDNSレコードがCloudflare経由でプロキシされている(プロキシのステータスがオレンジクラウドである)
    • 設定ステップ:
      • ワーカーの詳細ページのSettingsタブをクリックする。
      • Domain and Routingセクションを探し、Addボタンをクリックする。
      • Custom Domainを選択し、使用するドメイン名(例:tts.example.com)を入力します。
      • Add Domainをクリックし、証明書の展開が完了するまで待つ(通常は数分以内)。

使用方法

  1. TTY(音声合成インターフェース)
    • 中国語のスピーチ例
     curl -X POST https://你的worker地址/v1/audio/speech
    -H "Content-Type: application/json" ¦ -H "Authorization: Bearer your-api-key
    -H "Authorization: Bearer your-api-key" ୧-͈ᴗ-͈
    -d '{
    "model": "tts-1", "input": "Hello".
    「入力": "Hello, world!,
    「音声": "zh-CN-XiaoxiaoNeural"、
    
    "speed": 1.0, "response_format".
    "ピッチ": 1.0,
    "スタイル": "一般"
    }' --出力 chinese.mp3
    
    • 英語のスピーチの例:
     curl -X POST https://你的worker地址/v1/audio/speech
    -H "Content-Type: application/json" \
    -H "Authorization: Bearer your-api-key" ୧-͈ᴗ-͈
    -d '{
    "model": "tts-1", "input": "Hello World".
    "input": "Hello, World!", "voice": "en-US".
    「voice": "en-US-JennyNeural"、
    
    
    "pitch": 1.0, "style": "general".
    "style": "general".
    }' --出力 english.mp3
    
  2. テストスクリプトの使用
    • テストスクリプトのダウンロード test_voices.sh
    • スクリプトに実行権限を追加する: バッシュ
      chmod +x test_voices.sh
    • スクリプトを実行する: バッシュ
      ./test_voices.sh [APIキー]。
    • バッシュ
      # APIキーの使用
      ./test_voices.sh https://your-worker.workers.dev your-api-key
      #はAPIキーを使用しません。
      ./test_voices.sh https://your-worker.workers.dev
    • スクリプトは、サポートされている各音声のテスト音声ファイルを生成し、それを再生して最適な音声を選択することができます。

APIパラメータ 説明

  • モデル (文字列): モデル名(固定値)。 ts-1
  • 入力 (文字列):変換するテキスト。 "ハロー、ワールド!"
  • (文字列): 声の名前。 zh-CN-XiaoxiaoNeural
  • レスポンス・フォーマット (文字列、 オプ シ ョ ナル) : 出力形式。 mp3
  • スピード (数値、オプション): 発話速度 (0.5-2.0)、デフォルトは 1.0
  • ピッチ (数値、オプション): 音色 (0.5-2.0)、デフォルトは 1.0
  • スタイル (string, optional): 感情。 一般

対応音声一覧

音声に対応する言語のテキストを必ず使用してください。例えば、中国語の音声には中国語のテキストを使用してください。以下はよく使われる音声の例です:

  • zh-CN-XiaoxiaoNeuralシャオシャオ - 温かく生き生きと
  • zh-CN-XiaoyiNeuralシャオイー - 温かさと優しさ
  • zh-CN-YunxiNeural:: Yumshi - 男声、安定した声
  • zh-中国-雲陽ニューラル:: ユン・ヤン - 男声、プロフェッショナル
  • zh-CN-XiaohanNeuralシャオハン - ナチュラル・フロー
  • zh-CN-XiaomengNeuralシャオメン - 甘くて元気
  • zh-CN-XiaochenNeuralシャオチェン - 穏やかでやさしい
  • 待って...

 

workers.js コード

const API_KEY = 'aisharenet'; // 実際のAPIキーに置き換える
const TOKEN_REFRESH_BEFORE_EXPIRY = 3 * 60; // 実際のAPIキーに置き換える。 トークン 刷新时间(秒)
const DEFAULT_VOICE = 'zh-CN-XiaoxiaoNeural'; // 默认语音
const DEFAULT_SPEED = 1.0; // 默认语速
const DEFAULT_PITCH = 1.0; // 默认音调

let tokenInfo = {
    endpoint: null,
    token: null,
    expiredAt: null
};

// 处理请求
addEventListener('fetch', event => {
    event.respondWith(handleRequest(event.request));
});

async function handleRequest(request) {
    const url = new URL(request.url);

    // 如果请求根路径,返回 Web UI
    if (url.pathname === '/') {
        return new Response(getWebUI(), {
            headers: { 'Content-Type': 'text/html' },
        });
    }

    // 处理 TTS 请求
    if (url.pathname === '/v1/audio/speech') {
        return handleTTSRequest(request);
    }

    // 默认返回 404
    return new Response('Not Found', { status: 404 });
}

// 处理 TTS 请求
async function handleTTSRequest(request) {
    if (request.method === 'OPTIONS') {
        return handleOptions(request);
    }

    // 验证 API Key
    const authHeader = request.headers.get('authorization') || request.headers.get('x-api-key');
    const apiKey = authHeader?.startsWith('Bearer ') ? authHeader.slice(7) : null;

    if (API_KEY && apiKey !== API_KEY) {
        return createErrorResponse('Invalid API key. Use \'Authorization: Bearer your-api-key\' header', 'invalid_api_key', 401);
    }

    try {
        const requestBody = await request.json();
        const { 
            model = "tts-1",
            input,
            voice = DEFAULT_VOICE,
            speed = DEFAULT_SPEED,
            pitch = DEFAULT_PITCH
        } = requestBody;

        // 验证参数范围
        validateParameterRange('speed', speed, 0.5, 2.0);
        validateParameterRange('pitch', pitch, 0.5, 2.0);

        const rate = calculateRate(speed);
        const numPitch = calculatePitch(pitch);
        const audioResponse = await getVoice(
            input,
            voice,
            rate >= 0 ? `+${rate}%` : `${rate}%`,
            numPitch >= 0 ? `+${numPitch}%` : `${numPitch}%`,
            '+0%',
            'general', // 固定风格为通用
            'audio-24khz-48kbitrate-mono-mp3' // 固定音频格式为 MP3
        );

        return audioResponse;
    } catch (error) {
        return createErrorResponse(error.message, 'edge_tts_error', 500);
    }
}

// 处理 OPTIONS 请求
function handleOptions(request) {
    return new Response(null, {
        headers: {
            ...makeCORSHeaders(),
            'Access-Control-Allow-Methods': 'GET,HEAD,POST,OPTIONS',
            'Access-Control-Allow-Headers': request.headers.get('Access-Control-Request-Headers') || 'Authorization',
        },
    });
}

// 创建错误响应
function createErrorResponse(message, code, status) {
    return new Response(
        JSON.stringify({
            error: { message, code }
        }),
        {
            status,
            headers: { 'Content-Type': 'application/json', ...makeCORSHeaders() },
        }
    );
}

// 验证参数范围
function validateParameterRange(name, value, min, max) {
    if (value < min || value > max) {
        throw new Error(`${name} must be between ${min} and ${max}`);
    }
}

// 计算语速
function calculateRate(speed) {
    return parseInt(String((parseFloat(speed) - 1.0) * 100));
}

// 计算音调
function calculatePitch(pitch) {
    return parseInt(String((parseFloat(pitch) - 1.0) * 100));
}

// 生成 CORS 头
function makeCORSHeaders() {
    return {
        'Access-Control-Allow-Origin': '*',
        'Access-Control-Allow-Methods': 'GET,HEAD,POST,OPTIONS',
        'Access-Control-Allow-Headers': 'Content-Type, x-api-key',
        'Access-Control-Max-Age': '86400',
    };
}

// 获取语音
async function getVoice(text, voiceName, rate, pitch, volume, style, outputFormat) {
    try {
        const chunks = text.trim().split("\n");
        const audioChunks = await Promise.all(chunks.map(chunk => getAudioChunk(chunk, voiceName, rate, pitch, volume, style, outputFormat)));

        // 将音频片段拼接起来
        const concatenatedAudio = new Blob(audioChunks, { type: `audio/${outputFormat.split('-').pop()}` });
        return new Response(concatenatedAudio, {
            headers: {
                'Content-Type': `audio/${outputFormat.split('-').pop()}`,
                ...makeCORSHeaders(),
            },
        });
    } catch (error) {
        console.error("语音合成失败:", error);
        return createErrorResponse(error.message, 'edge_tts_error', 500);
    }
}

// 获取单个音频片段
async function getAudioChunk(text, voiceName, rate, pitch, volume, style, outputFormat) {
    const endpoint = await getEndpoint();
    const url = `https://${endpoint.r}.tts.speech.microsoft.com/cognitiveservices/v1`;
    const slien = extractSilenceDuration(text);

    const response = await fetch(url, {
        method: "POST",
        headers: {
            "Authorization": endpoint.t,
            "Content-Type": "application/ssml+xml",
            "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0",
            "X-Microsoft-OutputFormat": outputFormat,
        },
        body: getSsml(text, voiceName, rate, pitch, volume, style, slien),
    });

    if (!response.ok) {
        const errorText = await response.text();
        throw new Error(`Edge TTS API error: ${response.status} ${errorText}`);
    }

    return response.blob();
}

// 提取静音时长
function extractSilenceDuration(text) {
    const match = text.match(/\[(\d+)\]\s*?$/);
    return match && match.length === 2 ? parseInt(match[1]) : 0;
}

// 生成 SSML
function getSsml(text, voiceName, rate, pitch, volume, style, slien) {
    const slienStr = slien > 0 ? `` : '';
    return ` 

                        ${text} 

                    ${slienStr}

            `;
}

// 获取 Endpoint
async function getEndpoint() {
    const now = Date.now() / 1000;
    if (tokenInfo.token && tokenInfo.expiredAt && now < tokenInfo.expiredAt - TOKEN_REFRESH_BEFORE_EXPIRY) {
        return tokenInfo.endpoint;
    }

    // 获取新 Token
    const endpointUrl = "https://dev.microsofttranslator.com/apps/endpoint?api-version=1.0";
    const clientId = crypto.randomUUID().replace(/-/g, "");

    try {
        const response = await fetch(endpointUrl, {
            method: "POST",
            headers: {
                "Accept-Language": "zh-Hans",
                "X-ClientVersion": "4.0.530a 5fe1dc6c",
                "X-UserId": "0f04d16a175c411e",
                "X-HomeGeographicRegion": "zh-Hans-CN",
                "X-ClientTraceId": clientId,
                "X-MT-Signature": await sign(endpointUrl),
                "User-Agent": "Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/127.0.0.0 Safari/537.36 Edg/127.0.0.0",
                "Content-Type": "application/json; charset=utf-8",
                "Content-Length": "0",
                "Accept-Encoding": "gzip",
            },
        });

        if (!response.ok) {
            throw new Error(`获取 Endpoint 失败: ${response.status}`);
        }

        const data = await response.json();
        const jwt = data.t.split(".")[1];
        const decodedJwt = JSON.parse(atob(jwt));

        tokenInfo = {
            endpoint: data,
            token: data.t,
            expiredAt: decodedJwt.exp,
        };

        return data;
    } catch (error) {
        console.error("获取 Endpoint 失败:", error);
        if (tokenInfo.token) {
            console.log("使用过期的缓存 Token");
            return tokenInfo.endpoint;
        }
        throw error;
    }
}

// 签名
async function sign(urlStr) {
    const url = urlStr.split("://")[1];
    const encodedUrl = encodeURIComponent(url);
    const uuidStr = uuid();
    const formattedDate = dateFormat();
    const bytesToSign = `MSTranslatorAndroidApp${encodedUrl}${formattedDate}${uuidStr}`.toLowerCase();
    const decode = await base64ToBytes("oik6PdDdMnOXemTbwvMn9de/h9lFnfBaCWbGMMZqqoSaQaqUOqjVGm5NqsmjcBI1x+sS9ugjB55HEJWRiFXYFw==");
    const signData = await hmacSha256(decode, bytesToSign);
    const signBase64 = await bytesToBase64(signData);
    return `MSTranslatorAndroidApp::${signBase64}::${formattedDate}::${uuidStr}`;
}

// 格式化日期
function dateFormat() {
    return (new Date()).toUTCString().replace(/GMT/, "").trim() + " GMT";
}

// HMAC SHA-256 签名
async function hmacSha256(key, data) {
    const cryptoKey = await crypto.subtle.importKey(
        "raw",
        key,
        { name: "HMAC", hash: { name: "SHA-256" } },
        false,
        ["sign"]
    );
    const signature = await crypto.subtle.sign("HMAC", cryptoKey, new TextEncoder().encode(data));
    return new Uint8Array(signature);
}

// Base64 转字节数组
async function base64ToBytes(base64) {
    const binaryString = atob(base64);
    const bytes = new Uint8Array(binaryString.length);
    for (let i = 0; i < binaryString.length; i++) {
        bytes[i] = binaryString.charCodeAt(i);
    }
    return bytes;
}

// 字节数组转 Base64
async function bytesToBase64(bytes) {
    return btoa(String.fromCharCode.apply(null, bytes));
}

// 生成 UUID
function uuid() {
    return crypto.randomUUID().replace(/-/g, "");
}

// 获取 Web UI
function getWebUI() {
    return `


 

音声合成ツール

 

 

 


 

 

 

 

音声を生成しています。

 

`; }
無断転載を禁じます:チーフAIシェアリングサークル " Edge TTS Worker: Cloudflare、OpenAI互換フォーマット、パッケージ化されたウェブインターフェースを使用したMicrosoft音声合成APIの展開

チーフAIシェアリングサークル

チーフAIシェアリングサークルは、AI学習に焦点を当て、包括的なAI学習コンテンツ、AIツール、実践指導を提供しています。私たちの目標は、高品質のコンテンツと実践的な経験の共有を通じて、ユーザーがAI技術を習得し、AIの無限の可能性を一緒に探求することです。AI初心者でも上級者でも、知識を得てスキルを向上させ、イノベーションを実現するための理想的な場所です。

お問い合わせ
ja日本語