リアルタイム音声認識は、WebSocket を介して音声ストリームを句読点付きのテキストに文字起こしする機能で、ライブキャプション、オンライン会議、音声チャット、スマートアシスタントに適した低レイテンシを実現します。
概要
WebSocket ストリーミングプロトコルは、音声をサービスに配信し、文字起こしされたテキストを低レイテンシで返します。
標準中国語および広東語や四川語などの方言に対する高精度な認識。
自動言語検出と非音声フィルタリングにより、複雑な音響環境でも安定したパフォーマンスを発揮。
驚き、平静、喜び、悲しみ、嫌悪、怒り、恐怖などの状態にわたる感情認識。
指定した用語の認識精度を向上させるカスタムホットワード。
構造化された認識結果のためのタイムスタンプ出力。
さまざまな録音環境に合わせて設定可能なサンプルレートと複数の音声フォーマット。
会議の文字起こし、通話分析、字幕生成などのバッチシナリオでは、非リアルタイム音声認識をご利用ください。モデル選択のガイダンスについては、音声テキスト変換をご参照ください。
前提条件
DashScope SDK を介してサービスを呼び出すには、最新の SDK をインストールします。
クイックスタート
以下の例は、DashScope SDK を介してリアルタイム音声認識を呼び出す方法を示しています。
Fun-ASR
マイク音声の認識
マイクから音声をキャプチャし、リアルタイムで文字起こしされたテキストをストリーミングすることで、話者が話すと同時にテキストが表示されます。
Java
import com.alibaba.dashscope.audio.asr.recognition.Recognition;
import com.alibaba.dashscope.audio.asr.recognition.RecognitionParam;
import com.alibaba.dashscope.audio.asr.recognition.RecognitionResult;
import com.alibaba.dashscope.common.ResultCallback;
import com.alibaba.dashscope.utils.Constants;
import javax.sound.sampled.AudioFormat;
import javax.sound.sampled.AudioSystem;
import javax.sound.sampled.TargetDataLine;
import java.nio.ByteBuffer;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
public class Main {
public static void main(String[] args) throws InterruptedException {
// 次の設定はシンガポールリージョン用です。「{WorkspaceId}」を実際のワークスペース ID に置き換えてください。設定はリージョンによって異なります。
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(new RealtimeRecognitionTask());
executorService.shutdown();
executorService.awaitTermination(1, TimeUnit.MINUTES);
System.exit(0);
}
}
class RealtimeRecognitionTask implements Runnable {
@Override
public void run() {
RecognitionParam param = RecognitionParam.builder()
.model("fun-asr-realtime")
// シンガポールリージョンと北京リージョンでは API キーが異なります。API キーを取得するには、https://www.alibabacloud.com/help/model-studio/get-api-key をご参照ください。
// 環境変数を設定していない場合は、次の行を Model Studio API キーに置き換えてください: .apiKey("sk-xxx")
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.format("wav")
.sampleRate(16000)
.build();
Recognition recognizer = new Recognition();
ResultCallback<RecognitionResult> callback = new ResultCallback<RecognitionResult>() {
@Override
public void onEvent(RecognitionResult result) {
if (result.isSentenceEnd()) {
System.out.println("Final Result: " + result.getSentence().getText());
} else {
System.out.println("Intermediate Result: " + result.getSentence().getText());
}
}
@Override
public void onComplete() {
System.out.println("Recognition complete");
}
@Override
public void onError(Exception e) {
System.out.println("RecognitionCallback error: " + e.getMessage());
}
};
try {
recognizer.call(param, callback);
// 音声フォーマットを作成します。
AudioFormat audioFormat = new AudioFormat(16000, 16, 1, true, false);
// フォーマットに基づいてデフォルトの録音デバイスを照合します。
TargetDataLine targetDataLine =
AudioSystem.getTargetDataLine(audioFormat);
targetDataLine.open(audioFormat);
// 録音を開始します。
targetDataLine.start();
ByteBuffer buffer = ByteBuffer.allocate(1024);
long start = System.currentTimeMillis();
// 50 秒間録音し、リアルタイムで文字起こしを行います。
while (System.currentTimeMillis() - start < 50000) {
int read = targetDataLine.read(buffer.array(), 0, buffer.capacity());
if (read > 0) {
buffer.limit(read);
// 録音した音声データをストリーミング認識サービスに送信します。
recognizer.sendAudioFrame(buffer);
buffer = ByteBuffer.allocate(1024);
// 録音レートは制限されています。CPU 使用率が高くなるのを防ぐため、短時間スリープします。
Thread.sleep(20);
}
}
recognizer.stop();
} catch (Exception e) {
e.printStackTrace();
} finally {
// タスク完了後、WebSocket 接続を閉じます。
recognizer.getDuplexApi().close(1000, "bye");
}
System.out.println(
"[Metric] requestId: "
+ recognizer.getLastRequestId()
+ ", first package delay ms: "
+ recognizer.getFirstPackageDelay()
+ ", last package delay ms: "
+ recognizer.getLastPackageDelay());
}
}Python
Python の例では、音声キャプチャに pyaudio ライブラリが必要です。例を実行する前に、pip install pyaudio でインストールしてください。
import os
import signal # キーボードイベント処理用 (「Ctrl+C」を押して録音を終了)
import sys
import dashscope
import pyaudio
from dashscope.audio.asr import *
mic = None
stream = None
# 録音パラメータを設定
sample_rate = 16000 # サンプリングレート (Hz)
channels = 1 # モノラルチャンネル
dtype = 'int16' # データ型
format_pcm = 'pcm' # 音声データのフォーマット
block_size = 3200 # バッファごとのフレーム数
# リアルタイム音声認識コールバック
class Callback(RecognitionCallback):
def on_open(self) -> None:
global mic
global stream
print('RecognitionCallback open.')
mic = pyaudio.PyAudio()
stream = mic.open(format=pyaudio.paInt16,
channels=1,
rate=16000,
input=True)
def on_close(self) -> None:
global mic
global stream
print('RecognitionCallback close.')
stream.stop_stream()
stream.close()
mic.terminate()
stream = None
mic = None
def on_complete(self) -> None:
print('RecognitionCallback completed.') # 認識完了
def on_error(self, message) -> None:
print('RecognitionCallback task_id: ', message.request_id)
print('RecognitionCallback error: ', message.message)
# オーディオストリームが実行中の場合は停止して閉じる
if 'stream' in globals() and stream.is_active():
stream.stop_stream()
stream.close()
# プログラムを強制的に終了
sys.exit(1)
def on_event(self, result: RecognitionResult) -> None:
sentence = result.get_sentence()
if 'text' in sentence:
print('RecognitionCallback text: ', sentence['text'])
if RecognitionResult.is_sentence_end(sentence):
print(
'RecognitionCallback sentence end, request_id:%s, usage:%s'
% (result.get_request_id(), result.get_usage(sentence)))
def signal_handler(sig, frame):
print('Ctrl+C pressed, stop recognition ...')
# 認識を停止
recognition.stop()
print('Recognition stopped.')
print(
'[Metric] requestId: {}, first package delay ms: {}, last package delay ms: {}'
.format(
recognition.get_last_request_id(),
recognition.get_first_package_delay(),
recognition.get_last_package_delay(),
))
# プログラムを強制的に終了
sys.exit(0)
# main 関数
if __name__ == '__main__':
# シンガポールリージョンと北京リージョンでは API キーが異なります。API キーを取得するには、https://www.alibabacloud.com/help/model-studio/get-api-key をご参照ください
# 環境変数を設定していない場合は、次の行を Model Studio API キーに置き換えてください: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
# 次の設定はシンガポールリージョン用です。「{WorkspaceId}」を実際のワークスペース ID に置き換えてください。設定はリージョンによって異なります。
dashscope.base_websocket_api_url='wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference'
# 認識コールバックを作成
callback = Callback()
# 非同期モードで認識サービスを呼び出す。モデル、フォーマット、サンプルレートなどの認識パラメータをカスタマイズできます
recognition = Recognition(
model='fun-asr-realtime',
format=format_pcm,
# 'pcm'、'wav'、'opus'、'speex'、'aac'、'amr'。サポートされているフォーマットはドキュメントで確認できます。
sample_rate=sample_rate,
# 16000 をサポートします。
semantic_punctuation_enabled=False,
callback=callback)
# 認識を開始
recognition.start()
signal.signal(signal.SIGINT, signal_handler)
print("Press 'Ctrl+C' to stop recording and recognition...")
# 「Ctrl+C」が押されるまでキーボードリスナーを作成
while True:
if stream:
data = stream.read(3200, exception_on_overflow=False)
recognition.send_audio_frame(data)
else:
break
recognition.stop()ローカル音声ファイルの認識
ローカル音声ファイルを文字起こしします。これは、音声チャット、音声コマンド、音声入力、音声検索などの、短時間でニアリアルタイムのシナリオに適しています。
Java
この例で使用されている音声ファイルは asr_example.wav です。
import com.alibaba.dashscope.api.GeneralApi;
import com.alibaba.dashscope.audio.asr.recognition.Recognition;
import com.alibaba.dashscope.audio.asr.recognition.RecognitionParam;
import com.alibaba.dashscope.audio.asr.recognition.RecognitionResult;
import com.alibaba.dashscope.base.HalfDuplexParamBase;
import com.alibaba.dashscope.common.GeneralListParam;
import com.alibaba.dashscope.common.ResultCallback;
import com.alibaba.dashscope.protocol.GeneralServiceOption;
import com.alibaba.dashscope.protocol.HttpMethod;
import com.alibaba.dashscope.protocol.Protocol;
import com.alibaba.dashscope.protocol.StreamingMode;
import com.alibaba.dashscope.utils.Constants;
import java.io.FileInputStream;
import java.nio.ByteBuffer;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.time.LocalDateTime;
import java.time.format.DateTimeFormatter;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.TimeUnit;
class TimeUtils {
private static final DateTimeFormatter formatter =
DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss.SSS");
public static String getTimestamp() {
return LocalDateTime.now().format(formatter);
}
}
public class Main {
public static void main(String[] args) throws InterruptedException {
// 次の設定はシンガポールリージョン用です。「{WorkspaceId}」を実際のワークスペース ID に置き換えてください。設定はリージョンによって異なります。
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
// 実際のアプリケーションでは、プログラム起動時にこのメソッドを一度だけ呼び出します。
warmUp();
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(new RealtimeRecognitionTask(Paths.get(System.getProperty("user.dir"), "asr_example.wav")));
executorService.shutdown();
// すべてのタスクが完了するのを待ちます。
executorService.awaitTermination(1, TimeUnit.MINUTES);
System.exit(0);
}
public static void warmUp() {
try {
// 接続を確立するための軽量な GET リクエスト。
GeneralServiceOption warmupOption = GeneralServiceOption.builder()
.protocol(Protocol.HTTP)
.httpMethod(HttpMethod.GET)
.streamingMode(StreamingMode.OUT)
.path("assistants")
.build();
warmupOption.setBaseHttpUrl(Constants.baseHttpApiUrl);
GeneralApi<HalfDuplexParamBase> api = new GeneralApi<>();
api.get(GeneralListParam.builder().limit(1L).build(), warmupOption);
} catch (Exception e) {
// ウォームアップが失敗した場合でもリトライを許可します。
}
}
}
class RealtimeRecognitionTask implements Runnable {
private Path filepath;
public RealtimeRecognitionTask(Path filepath) {
this.filepath = filepath;
}
@Override
public void run() {
RecognitionParam param = RecognitionParam.builder()
.model("fun-asr-realtime")
// シンガポールリージョンと北京リージョンでは API キーが異なります。API キーを取得するには、https://www.alibabacloud.com/help/model-studio/get-api-key をご参照ください。
// 環境変数を設定していない場合は、次の行を Model Studio API キーに置き換えてください: .apiKey("sk-xxx")
.apiKey(System.getenv("DASHSCOPE_API_KEY"))
.format("wav")
.sampleRate(16000)
.build();
Recognition recognizer = new Recognition();
String threadName = Thread.currentThread().getName();
ResultCallback<RecognitionResult> callback = new ResultCallback<RecognitionResult>() {
@Override
public void onEvent(RecognitionResult message) {
if (message.isSentenceEnd()) {
System.out.println(TimeUtils.getTimestamp()+" "+
"[process " + threadName + "] Final Result:" + message.getSentence().getText());
} else {
System.out.println(TimeUtils.getTimestamp()+" "+
"[process " + threadName + "] Intermediate Result: " + message.getSentence().getText());
}
}
@Override
public void onComplete() {
System.out.println(TimeUtils.getTimestamp()+" "+"[" + threadName + "] Recognition complete");
}
@Override
public void onError(Exception e) {
System.out.println(TimeUtils.getTimestamp()+" "+
"[" + threadName + "] RecognitionCallback error: " + e.getMessage());
}
};
try {
recognizer.call(param, callback);
// パスを音声ファイルのパスに置き換えてください。
System.out.println(TimeUtils.getTimestamp()+" "+"[" + threadName + "] Input file_path is: " + this.filepath);
// ファイルを読み込み、音声をチャンクで送信します。
FileInputStream fis = new FileInputStream(this.filepath.toFile());
byte[] allData = new byte[fis.available()];
int ret = fis.read(allData);
fis.close();
int sendFrameLength = 3200;
for (int i = 0; i * sendFrameLength < allData.length; i ++) {
int start = i * sendFrameLength;
int end = Math.min(start + sendFrameLength, allData.length);
ByteBuffer byteBuffer = ByteBuffer.wrap(allData, start, end - start);
recognizer.sendAudioFrame(byteBuffer);
Thread.sleep(100);
}
System.out.println(TimeUtils.getTimestamp()+" "+LocalDateTime.now());
recognizer.stop();
} catch (Exception e) {
e.printStackTrace();
} finally {
// タスク完了後、WebSocket 接続を閉じます。
recognizer.getDuplexApi().close(1000, "bye");
}
System.out.println(
"["
+ threadName
+ "][Metric] requestId: "
+ recognizer.getLastRequestId()
+ ", first package delay ms: "
+ recognizer.getFirstPackageDelay()
+ ", last package delay ms: "
+ recognizer.getLastPackageDelay());
}
}Python
この例で使用されている音声ファイルは asr_example.wav です。
import os
import time
import dashscope
from dashscope.audio.asr import *
# シンガポールリージョンと北京リージョンでは API キーが異なります。API キーを取得するには、https://www.alibabacloud.com/help/model-studio/get-api-key をご参照ください
# 環境変数を設定していない場合は、次の行を Model Studio API キーに置き換えてください: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
# 次の設定はシンガポールリージョン用です。「{WorkspaceId}」を実際のワークスペース ID に置き換えてください。設定はリージョンによって異なります。
dashscope.base_websocket_api_url='wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference'
from datetime import datetime
def get_timestamp():
now = datetime.now()
formatted_timestamp = now.strftime("[%Y-%m-%d %H:%M:%S.%f]")
return formatted_timestamp
class Callback(RecognitionCallback):
def on_complete(self) -> None:
print(get_timestamp() + ' Recognition completed') # 認識完了
def on_error(self, result: RecognitionResult) -> None:
print('Recognition task_id: ', result.request_id)
print('Recognition error: ', result.message)
exit(0)
def on_event(self, result: RecognitionResult) -> None:
sentence = result.get_sentence()
if 'text' in sentence:
print(get_timestamp() + ' RecognitionCallback text: ', sentence['text'])
if RecognitionResult.is_sentence_end(sentence):
print(get_timestamp() +
'RecognitionCallback sentence end, request_id:%s, usage:%s'
% (result.get_request_id(), result.get_usage(sentence)))
callback = Callback()
recognition = Recognition(model='fun-asr-realtime',
format='wav',
sample_rate=16000,
callback=callback)
try:
audio_data: bytes = None
f = open("asr_example.wav", 'rb')
if os.path.getsize("asr_example.wav"):
# ファイル全体をバッファに読み込む
file_buffer = f.read()
f.close()
print("Start Recognition")
recognition.start()
# 3200 バイトのチャンクでデータを送信
buffer_size = len(file_buffer)
offset = 0
chunk_size = 3200
while offset < buffer_size:
# 現在のチャンクのサイズを計算
remaining_bytes = buffer_size - offset
current_chunk_size = min(chunk_size, remaining_bytes)
# バッファから現在のチャンクを抽出
audio_data = file_buffer[offset:offset + current_chunk_size]
# 音声フレームを送信
recognition.send_audio_frame(audio_data)
# オフセットを更新
offset += current_chunk_size
# リアルタイム伝送をシミュレートするために遅延を追加
time.sleep(0.1)
recognition.stop()
else:
raise Exception(
'The supplied file was empty (zero bytes long)')
except Exception as e:
raise e
print(
'[Metric] requestId: {}, first package delay ms: {}, last package delay ms: {}'
.format(
recognition.get_last_request_id(),
recognition.get_first_package_delay(),
recognition.get_last_package_delay(),
))Qwen-ASR
この例では、your_audio_file.pcm (PCM16, 16 kHz, モノラル) を読み取ります。 MP3、WAV、またはその他のフォーマットから変換するには、ffmpeg を使用します:
ffmpeg -i your_audio.mp3 -ar 16000 -ac 1 -f s16le your_audio_file.pcmJava
import com.alibaba.dashscope.audio.omni.*;
import com.alibaba.dashscope.exception.NoApiKeyException;
import com.google.gson.JsonObject;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.sound.sampled.LineUnavailableException;
import java.io.File;
import java.io.FileInputStream;
import java.util.Base64;
import java.util.Collections;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
public class Qwen3AsrRealtimeUsage {
private static final Logger log = LoggerFactory.getLogger(Qwen3AsrRealtimeUsage.class);
private static final int AUDIO_CHUNK_SIZE = 1024; // 音声チャンクサイズ (バイト)
private static final int SLEEP_INTERVAL_MS = 30; // スリープ間隔 (ミリ秒)
public static void main(String[] args) throws InterruptedException, LineUnavailableException {
CountDownLatch finishLatch = new CountDownLatch(1);
OmniRealtimeParam param = OmniRealtimeParam.builder()
.model("qwen3-asr-flash-realtime")
// 次の設定はシンガポールリージョン用です。「{WorkspaceId}」を実際のワークスペース ID に置き換えてください。設定はリージョンによって異なります。
.url("wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/realtime")
// シンガポールリージョンと北京リージョンでは API キーが異なります。API キーを取得するには、https://www.alibabacloud.com/help/model-studio/get-api-key をご参照ください
// 環境変数を設定していない場合は、次の行を Model Studio API キーに置き換えてください: .apikey("sk-xxx")
.apikey(System.getenv("DASHSCOPE_API_KEY"))
.build();
OmniRealtimeConversation conversation = null;
final AtomicReference<OmniRealtimeConversation> conversationRef = new AtomicReference<>(null);
conversation = new OmniRealtimeConversation(param, new OmniRealtimeCallback() {
@Override
public void onOpen() {
System.out.println("connection opened");
}
@Override
public void onEvent(JsonObject message) {
String type = message.get("type").getAsString();
switch(type) {
case "session.created":
System.out.println("start session: " + message.get("session").getAsJsonObject().get("id").getAsString());
break;
case "conversation.item.input_audio_transcription.completed":
System.out.println("transcription: " + message.get("transcript").getAsString());
finishLatch.countDown();
break;
case "input_audio_buffer.speech_started":
System.out.println("======VAD Speech Start======");
break;
case "input_audio_buffer.speech_stopped":
System.out.println("======VAD Speech Stop======");
break;
case "conversation.item.input_audio_transcription.text":
System.out.println("transcription: " + message.get("text").getAsString() + message.get("stash").getAsString());
break;
default:
break;
}
}
@Override
public void onClose(int code, String reason) {
System.out.println("connection closed code: " + code + ", reason: " + reason);
}
});
conversationRef.set(conversation);
try {
conversation.connect();
} catch (NoApiKeyException e) {
throw new RuntimeException(e);
}
OmniRealtimeTranscriptionParam transcriptionParam = new OmniRealtimeTranscriptionParam();
transcriptionParam.setLanguage("en");
transcriptionParam.setInputAudioFormat("pcm");
transcriptionParam.setInputSampleRate(16000);
OmniRealtimeConfig config = OmniRealtimeConfig.builder()
.modalities(Collections.singletonList(OmniRealtimeModality.TEXT))
.transcriptionConfig(transcriptionParam)
.build();
conversation.updateSession(config);
String filePath = "your_audio_file.pcm";
File audioFile = new File(filePath);
if (!audioFile.exists()) {
log.error("Audio file not found: {}", filePath);
return;
}
try (FileInputStream audioInputStream = new FileInputStream(audioFile)) {
byte[] audioBuffer = new byte[AUDIO_CHUNK_SIZE];
int bytesRead;
int totalBytesRead = 0;
log.info("Starting to send audio data from: {}", filePath);
// 音声データをチャンクで読み込んで送信
while ((bytesRead = audioInputStream.read(audioBuffer)) != -1) {
totalBytesRead += bytesRead;
String audioB64 = Base64.getEncoder().encodeToString(audioBuffer);
// 音声チャンクを会話に送信
conversation.appendAudio(audioB64);
// リアルタイムの音声ストリーミングをシミュレートするために短い遅延を追加
Thread.sleep(SLEEP_INTERVAL_MS);
}
log.info("Finished sending audio data. Total bytes sent: {}", totalBytesRead);
} catch (Exception e) {
log.error("Error sending audio from file: {}", filePath, e);
}
//session.finish を送信し、終了して閉じるのを待つ
conversation.endSession();
log.info("task finished");
System.exit(0);
}
}Python
import logging
import os
import base64
import signal
import sys
import time
import dashscope
from dashscope.audio.qwen_omni import *
from dashscope.audio.qwen_omni.omni_realtime import TranscriptionParams
def setup_logging():
"""ロギング出力を設定"""
logger = logging.getLogger('dashscope')
logger.setLevel(logging.DEBUG)
handler = logging.StreamHandler(sys.stdout)
handler.setLevel(logging.DEBUG)
formatter = logging.Formatter('%(asctime)s - %(name)s - %(levelname)s - %(message)s')
handler.setFormatter(formatter)
logger.addHandler(handler)
logger.propagate = False
return logger
def init_api_key():
"""API キーを初期化"""
# シンガポールリージョンと北京リージョンでは API キーが異なります。API キーを取得するには、https://www.alibabacloud.com/help/model-studio/get-api-key をご参照ください
# 環境変数を設定していない場合は、次の行を Model Studio API キーに置き換えてください: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY', 'YOUR_API_KEY')
if dashscope.api_key == 'YOUR_API_KEY':
print('[Warning] Using placeholder API key, set DASHSCOPE_API_KEY environment variable.')
class MyCallback(OmniRealtimeCallback):
"""リアルタイム認識コールバックハンドラ"""
def __init__(self, conversation):
self.conversation = conversation
self.handlers = {
'session.created': self._handle_session_created,
'conversation.item.input_audio_transcription.completed': self._handle_final_text,
'conversation.item.input_audio_transcription.text': self._handle_transcription_text,
'input_audio_buffer.speech_started': lambda r: print('======Speech Start======'),
'input_audio_buffer.speech_stopped': lambda r: print('======Speech Stop======')
}
def on_open(self):
print('Connection opened')
def on_close(self, code, msg):
print(f'Connection closed, code: {code}, msg: {msg}')
def on_event(self, response):
try:
handler = self.handlers.get(response['type'])
if handler:
handler(response)
except Exception as e:
print(f'[Error] {e}')
def _handle_session_created(self, response):
print(f"Start session: {response['session']['id']}")
def _handle_final_text(self, response):
print(f"Final recognized text: {response['transcript']}")
def _handle_transcription_text(self, response):
print(f"Got transcription result: {response['text'] + response['stash']}")
def read_audio_chunks(file_path, chunk_size=3200):
"""音声ファイルをチャンクで読み込む"""
with open(file_path, 'rb') as f:
while chunk := f.read(chunk_size):
yield chunk
def send_audio(conversation, file_path, delay=0.1):
"""音声データを送信"""
if not os.path.exists(file_path):
raise FileNotFoundError(f"Audio file {file_path} does not exist.")
print("Processing audio file... Press 'Ctrl+C' to stop.")
for chunk in read_audio_chunks(file_path):
audio_b64 = base64.b64encode(chunk).decode('ascii')
conversation.append_audio(audio_b64)
time.sleep(delay)
def main():
setup_logging()
init_api_key()
audio_file_path = "./your_audio_file.pcm"
callback = MyCallback(conversation=None)
conversation = OmniRealtimeConversation(
model='qwen3-asr-flash-realtime',
# 次の設定はシンガポールリージョン用です。「{WorkspaceId}」を実際のワークスペース ID に置き換えてください。設定はリージョンによって異なります。
url='wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/realtime',
callback=callback,
)
callback.conversation = conversation # コールバックからメソッドを呼び出せるように、コールバックに会話を注入
def handle_exit(sig, frame):
print('Ctrl+C pressed, exiting...')
conversation.close()
sys.exit(0)
signal.signal(signal.SIGINT, handle_exit)
conversation.connect()
transcription_params = TranscriptionParams(
language='en',
sample_rate=16000,
input_audio_format="pcm"
)
conversation.update_session(
output_modalities=[MultiModality.TEXT],
enable_input_audio_transcription=True,
transcription_params=transcription_params
)
try:
send_audio(conversation, audio_file_path)
# session.finish を送信し、終了して閉じるのを待つ
conversation.end_session()
except Exception as e:
print(f"Error occurred: {e}")
finally:
conversation.close()
print("Audio processing completed.")
if __name__ == '__main__':
main()Paraformer
Paraformer は Fun-ASR のサンプルコードを再利用します。model パラメーターを Paraformer のモデル名に置き換えます。
認識設定
Qwen-ASR の対話モード
Qwen-ASR Realtime API は、2つの対話モードをサポートしています:
VAD モード (デフォルト): サーバーが発話の開始と終了 (ターン検出) を自動的に検出します。リアルタイムの会話、会議の文字起こし、および同様のシナリオに適しています。有効にするには、
session.turn_detectionを設定します (デフォルトで有効になっています)。手動モード: クライアントは
input_audio_buffer.commitを送信することで、発話区切り検出を制御します。チャットアプリの音声メッセージなど、送信タイミングを明示的に制御する必要があるシナリオに適しています。有効にするには、session.turn_detectionを null に設定します。
モードの切り替え:
WebSocket:
session.updateイベントのturn_detectionフィールドを設定します。{ "type": "session.update", "session": { "turn_detection": null } }Python SDK:
update_sessionメソッドでenable_turn_detectionパラメーターを設定します。conversation.update_session( enable_turn_detection=False )Java SDK:
enableTurnDetectionパラメーターをOmniRealtimeConfig.builder()を使用して設定します。OmniRealtimeConfig config = OmniRealtimeConfig.builder() .enableTurnDetection(false) .build(); conversation.updateSession(config);
完全な SDK コード例については、Qwen-ASR-Realtime Python SDK - API リファレンス および Java SDK をご参照ください。WebSocket イベントのライフサイクルについては、イベントフローをご参照ください。
VAD 発話区間検出
音声アクティビティ検出 (VAD) は、連続した音声がいつ終了するかを判断し、最終結果イベントをトリガーします。3つのモデルファミリーすべてで、デフォルトでサーバー側の VAD が有効になっていますが、パラメータ名と調整可能性は異なります:
Qwen-ASR:
session.turn_detectionを使用して設定されます。これには、silence_duration_ms(サイレンス持続時間のしきい値。サイレンスがこの値を超えるとターンが終了します。サーバーのデフォルトは800です。高速なターン検出が必要な会話やチャットのシナリオでは400が推奨されます) とthreshold(VAD 検出の秘密度。サーバーのデフォルトは0.2) が含まれます。また、Qwen-ASR は手動モードもサポートしています。このモードでは VAD が無効になり、クライアントはcommitを使用してターン検出をコントロールできます。上記の「Qwen-ASR のインタラクションモード」をご参照ください。Fun-ASR と Paraformer:
max_sentence_silence(VAD 発話区切り検出サイレンスしきい値、ミリ秒) で設定します。音声セグメントの後のサイレンスがこのしきい値を超えると、文が終了したと見なされます。
プロトコルによってパラメーター名が異なります。同じ概念は、Qwen-ASR では silence_duration_ms、Fun-ASR および Paraformer では max_sentence_silence となります。詳細については、「API リファレンス」をご参照ください。
高度な機能
ホットワードで精度を向上
Fun-ASR と Paraformer は、ブランド名、人名、固有名詞などの特定の用語の認識精度を向上させるためにホットワードをサポートしています。
ホットワードの設定と使用方法については、認識精度の向上をご参照ください。
タイムスタンプ
Fun-ASR と Paraformer は、デフォルトで文レベルと単語レベルの 2 つの粒度でタイムスタンプを返します。これにより、字幕の配置、キーワードのハイライト、カラオケスタイルのシングアロングなどのユースケースが実現できます。Qwen-ASR Realtime (qwen3-asr-flash-realtime) は現在、タイムスタンプを返しません。タイムスタンプが必要な場合は、Fun-ASR または Paraformer を使用してください。Qwen-ASR のファイル文字起こしモデル qwen3-asr-flash-filetrans は、単語レベルのタイムスタンプをサポートしています。詳細については、「非リアルタイム音声認識」をご参照ください。
タイムスタンプはミリ秒単位で2つのレベルで報告されます:
文レベル:
payload.output.sentence.begin_timeとpayload.output.sentence.end_timeは、音声内の文の開始と終了を示します。中間結果では、end_timeはnullになることがあります。最終値は、文が終了したとき (sentence_end = true) に設定されます。単語レベル:
payload.output.sentence.words配列です。各要素にはbegin_time、end_time、text(文字または単語のテキスト)、およびpunctuation(文字の後に続く句読点。ない場合は空の文字列) が含まれます。
応答例 (抜粋):
{
"payload": {
"output": {
"sentence": {
"begin_time": 170,
"end_time": 920,
"text": "Okay, I got it.",
"sentence_end": true,
"words": [
{ "begin_time": 170, "end_time": 295, "text": "Okay", "punctuation": "," },
{ "begin_time": 295, "end_time": 503, "text": "I", "punctuation": "" },
{ "begin_time": 503, "end_time": 711, "text": "got", "punctuation": "" },
{ "begin_time": 711, "end_time": 920, "text": "it", "punctuation": "" }
]
}
}
}
}上記のフィールド名は WebSocket JSON パスを使用しています。各 SDK は、独自の命名規則 (辞書キー、オブジェクトプロパティ、ゲッターメソッドなど) でこれらのフィールドを公開します。完全なフィールドマッピングについては、各 SDK の API リファレンスをご参照ください。
完全なフィールド定義については、API リファレンスをご参照ください。
感情認識
一部の Qwen-ASR および Paraformer モデルには、文字起こし結果に話者の感情状態が含まれます。出力の粒度と有効化する方法は、両者で異なります。
Qwen-ASR (qwen3-asr-flash-realtime): 常時オンで、構成は不要です。トップレベルの emotion フィールドは、conversation.item.input_audio_transcription.text イベントと conversation.item.input_audio_transcription.completed イベントの両方で返されます。値は、surprised、neutral、happy、sad、disgusted、angry、および fearful の 7 つの詳細な感情のうちの 1 つです。
{
"type": "conversation.item.input_audio_transcription.text",
"emotion": "neutral",
"text": "The weather is nice today.",
"stash": ""
}Paraformer (paraformer-realtime-8k-v2): この Paraformer モデルのみが感情認識をサポートしています。結果は payload.output.sentence.emo_tag と payload.output.sentence.emo_confidence を介して返されます。値は、positive (幸福や満足などの肯定的な感情)、negative (怒りや落ち込みなどの否定的な感情)、および neutral (明確な感情がない) の3つの感情のいずれかです。信頼度の範囲は [0.0, 1.0] です。
感情認識は、以下のすべての条件が満たされた場合にのみ返されます:
モデルは
paraformer-realtime-8k-v2です。意味的なターンの検出は無効になっています:
semantic_punctuation_enabled = false(デフォルト。特別な設定は不要です)。結果は、
sentence_end = trueのセンテンス終了イベントでのみ返されます。
感情認識フィールドを抑制するには、semantic_punctuation_enabled を true に設定します。これによりセマンティックな発話ターン検出が有効になり、emo_tag フィールドおよび emo_confidence フィールドは返されなくなります。
上記のフィールド名は WebSocket JSON パスを使用しています。各 SDK は、独自の命名規則 (辞書キー、オブジェクトプロパティ、ゲッターメソッドなど) でこれらのフィールドを公開します。完全なフィールドマッピングについては、各 SDK の API リファレンスをご参照ください。
完全なフィールド定義、値の制約、および例については、API リファレンスをご参照ください。
禁止用語フィルタリング
禁止用語フィルタリングは、認識結果から禁止用語を置き換えたり削除したりします。これは、顧客サービスの品質検査、コンテンツのコンプライアンス、字幕レビューなどのシナリオに適しています。
サポートされているモデル: Fun-ASR のみ。
制限: 最大 32 個の禁止用語。
デフォルトの動作: special_word_filter パラメーターが指定されていない場合、禁止用語フィルタリングは適用されません。
設定: special_word_filter は、3 つのサブフィールドを持つ JSON オブジェクトです:
filter_with_signed.word_list: 同じ長さのアスタリスク (*) で置き換える禁止用語をリストする文字列配列です。たとえば、["test"]を指定した場合、「please help me test it」 は 「please help me **** it」 になります。filter_with_empty.word_list: 結果から完全に削除する禁止用語を指定する文字列配列です。 たとえば、["start"]が指定されている場合、"is the game about to start now" は "is the game about to now" になります。system_reserved_filter: ブール値。デフォルトはfalse。禁止用語のフィルタリングを有効にするかどうかを指定します。
設定例:
{
"special_word_filter": {
"filter_with_signed": {
"word_list": ["test"]
},
"filter_with_empty": {
"word_list": ["start", "happen"]
},
"system_reserved_filter": true
}
}異なる SDK は、これらのパラメータを異なる命名規則 (辞書キー、オブジェクトプロパティ、メソッドなど) で公開します。完全なフィールドマッピングについては、API リファレンスをご参照ください。
生の WebSocket プロトコル
以下の例は、DashScope SDK を使用しないシナリオに適した、生の WebSocket プロトコルを介してサーバーに接続する方法を示しています。これらは最小限の実行可能な実装です。各モデルの WebSocket プロトコルについては、API リファレンスをご参照ください。
本番環境に適用
接続の再利用 (WebSocket)
Fun-ASR と Paraformer は WebSocket 接続の再利用をサポートしています。1つの認識タスクが終了した後、再接続せずに同じ接続で次のタスクを開始できます。
再利用フロー: クライアントが finish-task を送信し、サーバーが task-finished を返した後、クライアントは再び run-task を送信して新しいタスクを開始します。
新しいタスクを開始する前に、サーバーが
task-finishedイベントを返すのをお待ちください。再利用する接続では、タスクごとに異なる
task_idを使用する必要があります。タスクが失敗した場合、サーバーはエラーイベントを返し、接続を閉じます。その接続は再利用できません。
タスク終了後 60 秒以内に新しいタスクが開始されない場合、接続は自動的に閉じられます。
Qwen-ASR Realtime はセッションモデルを使用します。各セッションが終了した後に接続を閉じてください。接続の再利用はサポートされていません。
各モデルのイベントの説明については、対応するAPI リファレンスをご参照ください。
高同時実行数のベストプラクティス
DashScope SDK には、WebSocket 接続と認識オブジェクトを再利用する組み込みのプーリングが含まれており、それらを繰り返し作成および破棄するオーバーヘッドを回避します。現在、この機能をサポートしているのは Paraformer Java SDK のみです。
認識精度の向上
モデルとサンプルレートを一致させる:8 kHz の電話音声には、8 kHz モデルを直接使用してください。16 kHz にアップサンプリングすると、信号が歪むため行わないでください。
入力音声の品質を向上させる:信号対雑音比が高く、エコーのない録音環境で高品質のマイクを使用してください。アプリケーション層では、ノイズリダクション (例:RNNoise) や音響エコーキャンセル (AEC) などの前処理を統合してください。
回復性の設定
クライアント側の再接続:クライアントは、ネットワークのジッターに対応するために自動再接続を実装する必要があります。Python SDK の参照実装:
例外のキャッチ:
Callbackクラスにon_errorメソッドを実装します。dashscopeSDK は、ネットワークエラーまたはその他の問題が発生した場合にこのメソッドを呼び出します。状態の通知:
on_errorがトリガーされると、再接続信号をセットします。Python では、スレッドセーフな信号フラグであるthreading.Eventを使用します。再接続ループ: メインロジックを
forループでラップします (例: 3 回リトライ)。再接続信号が検出されると、現在の認識が中断されてリソースがクリーンアップされ、数秒後にループが新しい接続を作成します。
ハートビートを使用して接続を維持する:サーバーへの長時間持続する接続が必要な場合は、ハートビート パラメーターを
trueに設定します。音声が長時間無音の場合でも、接続は維持されます。レート制限:モデルインターフェイスを呼び出す際は、モデルのレート制限ルールを遵守してください。
サポートされているモデルとリージョン
シンガポール
以下のいずれかのモデルを呼び出すには、シンガポールリージョンのAPI キーを使用してください:
Fun-ASR:fun-asr-realtime (安定版、現在 fun-asr-realtime-2025-11-07 と同等)、fun-asr-realtime-2025-11-07 (スナップショット)
Qwen3-ASR-Flash-Realtime:qwen3-asr-flash-realtime (安定版、現在 qwen3-asr-flash-realtime-2025-10-27 と同等)、qwen3-asr-flash-realtime-2026-02-10 (最新スナップショット)、qwen3-asr-flash-realtime-2025-10-27 (スナップショット)
中国 (北京)
以下のいずれかのモデルを呼び出すには、北京リージョンの API キーを使用します:
Fun-ASR:fun-asr-realtime (安定版、現在 fun-asr-realtime-2025-11-07 と同等)、fun-asr-realtime-2026-02-28 (最新スナップショット)、fun-asr-realtime-2025-11-07 (スナップショット)、fun-asr-realtime-2025-09-15 (スナップショット)
fun-asr-flash-8k-realtime (安定版、現在 fun-asr-flash-8k-realtime-2026-01-28 と同等)、fun-asr-flash-8k-realtime-2026-01-28
Qwen3-ASR-Flash-Realtime:qwen3-asr-flash-realtime (安定版、現在 qwen3-asr-flash-realtime-2025-10-27 と同等)、qwen3-asr-flash-realtime-2026-02-10 (最新スナップショット)、qwen3-asr-flash-realtime-2025-10-27 (スナップショット)
Paraformer:paraformer-realtime-v2、paraformer-realtime-v1、paraformer-realtime-8k-v2、paraformer-realtime-8k-v1
API リファレンス
よくある質問
リアルタイム音声認識はどの音声フォーマットをサポートしていますか?
Fun-ASR と Paraformer は pcm、wav、mp3、opus、speex、aac、amr をサポートしています。Qwen-ASR では、pcm または opus を使用してください。その他のフォーマット (wav、aac、amr など) は session.update の検証に合格しますが、サーバー側のデコーディングに失敗する可能性があります。送信する前に、音声ストリームが推奨フォーマットを使用していることを確認してください。
SDK と WebSocket API はいつ使用すべきですか?
DashScope SDK は WebSocket 接続管理、認証、再接続をラップしており、統合への最速のパスとなります。WebSocket API は、直接的で詳細な制御を提供します。SDK がご使用の言語をカバーしていない場合、またはカスタムの接続処理が必要な場合に使用してください。ほとんどのユースケースでは SDK が推奨される選択肢です。
固有名詞の認識精度を向上させるにはどうすればよいですか?
ホットワードを使用します (Fun-ASR と Paraformer でサポート)。ホットワードの設定と使用方法については、認識精度の向上をご参照ください。
接続が頻繁に切断される場合はどうすればよいですか?
クライアント側の再接続ロジックを実装し、ハートビートパラメーター (heartbeat=true) を有効にして、長い無通信期間中に接続が切断されるのを防ぎます。詳細な耐障害性戦略については、「本番環境に適用する」をご参照ください。