Pengenalan ucapan real-time mentranskripsikan aliran audio menjadi teks ber-tanda baca melalui WebSocket dengan latensi rendah, cocok untuk keterangan langsung (live captions), rapat daring, obrolan suara, dan asisten cerdas.
Ikhtisar
Protokol streaming WebSocket mengirimkan audio ke layanan dan mengembalikan teks hasil transkripsi dengan latensi rendah.
-
Pengenalan akurasi tinggi untuk bahasa Mandarin dan dialeknya, termasuk Kanton dan Sichuan.
-
Kinerja tangguh di lingkungan akustik kompleks, dengan deteksi bahasa otomatis dan penyaringan non-ucapan.
-
Pengenalan emosi dalam berbagai kondisi seperti terkejut, tenang, gembira, sedih, jijik, marah, dan takut.
-
Kata kunci khusus (hotwords) yang meningkatkan akurasi pengenalan untuk istilah tertentu.
-
Output timestamp untuk hasil pengenalan terstruktur.
-
Laju sampel dan format audio yang dapat dikonfigurasi untuk menyesuaikan berbagai lingkungan perekaman.
Untuk skenario batch seperti transkripsi rapat, analisis panggilan, dan pembuatan subtitel, gunakan Pengenalan ucapan non-real-time. Untuk panduan pemilihan model, lihat Speech-to-text.
Prasyarat
-
Dapatkan Kunci API, dengan Kunci API dikonfigurasi sebagai Variabel lingkungan.
-
Untuk memanggil layanan melalui SDK DashScope, instal SDK terbaru.
Mulai cepat
Contoh berikut menunjukkan cara memanggil pengenalan ucapan real-time melalui SDK DashScope.
Fun-ASR
Kenali audio mikrofon
Tangkap audio dari mikrofon dan alirkan teks hasil transkripsi secara real-time sehingga teks muncul saat pembicara berbicara.
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 {
// Konfigurasi berikut ini untuk wilayah Singapura. Ganti "{WorkspaceId}" dengan ID ruang kerja aktual Anda. Konfigurasi berbeda-beda per wilayah.
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")
// Kunci API untuk wilayah Singapura dan Beijing berbeda. Untuk mendapatkan Kunci API, lihat https://www.alibabacloud.com/help/en/model-studio/get-api-key.
// Jika Anda belum mengonfigurasi variabel lingkungan, ganti baris berikut dengan Kunci API Model Studio Anda: .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);
// Buat format audio.
AudioFormat audioFormat = new AudioFormat(16000, 16, 1, true, false);
// Sesuaikan perangkat perekaman default berdasarkan format tersebut.
TargetDataLine targetDataLine =
AudioSystem.getTargetDataLine(audioFormat);
targetDataLine.open(audioFormat);
// Mulai merekam.
targetDataLine.start();
ByteBuffer buffer = ByteBuffer.allocate(1024);
long start = System.currentTimeMillis();
// Rekam selama 50 detik dan lakukan transkripsi real-time.
while (System.currentTimeMillis() - start < 50000) {
int read = targetDataLine.read(buffer.array(), 0, buffer.capacity());
if (read > 0) {
buffer.limit(read);
// Kirim data audio yang direkam ke layanan pengenalan streaming.
recognizer.sendAudioFrame(buffer);
buffer = ByteBuffer.allocate(1024);
// Laju perekaman dibatasi. Tidur sejenak untuk mencegah penggunaan CPU tinggi.
Thread.sleep(20);
}
}
recognizer.stop();
} catch (Exception e) {
e.printStackTrace();
} finally {
// Tutup koneksi WebSocket setelah tugas selesai.
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
Contoh Python memerlukan library pyaudio untuk penangkapan audio. Instal dengan pip install pyaudio sebelum menjalankan contoh.
import os
import signal # for keyboard events handling (press "Ctrl+C" to terminate recording)
import sys
import dashscope
import pyaudio
from dashscope.audio.asr import *
mic = None
stream = None
# Set recording parameters
sample_rate = 16000 # sampling rate (Hz)
channels = 1 # mono channel
dtype = 'int16' # data type
format_pcm = 'pcm' # the format of the audio data
block_size = 3200 # number of frames per buffer
# Real-time speech recognition callback
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.') # recognition completed
def on_error(self, message) -> None:
print('RecognitionCallback task_id: ', message.request_id)
print('RecognitionCallback error: ', message.message)
# Stop and close the audio stream if it is running
if 'stream' in globals() and stream.is_active():
stream.stop_stream()
stream.close()
# Forcefully exit the program
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 ...')
# 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(),
))
# Forcefully exit the program
sys.exit(0)
# main function
if __name__ == '__main__':
# The API keys for the Singapore and Beijing regions are different. To get an API key, see https://www.alibabacloud.com/help/en/model-studio/get-api-key
# If you have not configured an environment variable, replace the following line with your Model Studio API key: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
# The following configuration is for the Singapore region. Replace "{WorkspaceId}" with your actual workspace ID. The configuration varies by region.
dashscope.base_websocket_api_url='wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference'
# Create the recognition callback
callback = Callback()
# Call recognition service by async mode, you can customize the recognition parameters, like model, format,
# sample_rate
recognition = Recognition(
model='fun-asr-realtime',
format=format_pcm,
# 'pcm', 'wav', 'opus', 'speex', 'aac', 'amr'. You can check the supported formats in the document.
sample_rate=sample_rate,
# Supports 16000.
semantic_punctuation_enabled=False,
callback=callback)
# Start recognition
recognition.start()
signal.signal(signal.SIGINT, signal_handler)
print("Press 'Ctrl+C' to stop recording and recognition...")
# Create a keyboard listener until "Ctrl+C" is pressed
while True:
if stream:
data = stream.read(3200, exception_on_overflow=False)
recognition.send_audio_frame(data)
else:
break
recognition.stop()Kenali file audio lokal
Transkripsikan file audio lokal. Ini cocok untuk skenario real-time singkat seperti obrolan suara, perintah suara, input ucapan, dan pencarian suara.
Java
File audio yang digunakan dalam contoh adalah 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 {
// The following configuration is for the Singapore region. Replace "{WorkspaceId}" with your actual workspace ID. The configuration varies by region.
Constants.baseWebsocketApiUrl = "wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/inference";
// In a real application, call this method only once at program startup.
warmUp();
ExecutorService executorService = Executors.newSingleThreadExecutor();
executorService.submit(new RealtimeRecognitionTask(Paths.get(System.getProperty("user.dir"), "asr_example.wav")));
executorService.shutdown();
// Wait for all tasks to complete.
executorService.awaitTermination(1, TimeUnit.MINUTES);
System.exit(0);
}
public static void warmUp() {
try {
// Lightweight GET request to establish a connection.
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) {
// Allow retry if warm-up fails.
}
}
}
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")
// The API keys for the Singapore and Beijing regions are different. To get an API key, see https://www.alibabacloud.com/help/en/model-studio/get-api-key.
// If you have not configured an environment variable, replace the following line with your Model Studio API key: .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);
// Replace the path with your audio file path.
System.out.println(TimeUtils.getTimestamp()+" "+"[" + threadName + "] Input file_path is: " + this.filepath);
// Read the file and send audio in chunks.
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 {
// Close the WebSocket connection after the task is complete.
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
File audio yang digunakan dalam contoh adalah asr_example.wav.
import os
import time
import dashscope
from dashscope.audio.asr import *
# API keys differ between the Singapore and Beijing regions. Get an API key: https://www.alibabacloud.com/help/en/model-studio/get-api-key
# If you have not set an environment variable, replace the next line with your Model Studio API key: dashscope.api_key = "sk-xxx"
dashscope.api_key = os.environ.get('DASHSCOPE_API_KEY')
# The following configuration is for the Singapore region. Replace "{WorkspaceId}" with your actual workspace ID. The configuration varies by region.
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') # recognition complete
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"):
# Read the entire file into a buffer
file_buffer = f.read()
f.close()
print("Start Recognition")
recognition.start()
# Send data in chunks of 3200 bytes
buffer_size = len(file_buffer)
offset = 0
chunk_size = 3200
while offset < buffer_size:
# Calculate the size of the current chunk
remaining_bytes = buffer_size - offset
current_chunk_size = min(chunk_size, remaining_bytes)
# Extract the current chunk from the buffer
audio_data = file_buffer[offset:offset + current_chunk_size]
# Send the audio frame
recognition.send_audio_frame(audio_data)
# Update the offset
offset += current_chunk_size
# Add a delay to simulate real-time transmission
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
Berikut contoh membaca file your_audio_file.pcm (PCM16, 16 kHz, mono). Untuk mengonversi dari format MP3, WAV, atau lainnya, gunakan ffmpeg:
ffmpeg -i your_audio.mp3 -ar 16000 -ac 1 -f s16le your_audio_file.pcm
Java
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; // Audio chunk size in bytes
private static final int SLEEP_INTERVAL_MS = 30; // Sleep interval in milliseconds
public static void main(String[] args) throws InterruptedException, LineUnavailableException {
CountDownLatch finishLatch = new CountDownLatch(1);
OmniRealtimeParam param = OmniRealtimeParam.builder()
.model("qwen3-asr-flash-realtime")
// The following configuration is for the Singapore region. Replace "{WorkspaceId}" with your actual workspace ID. Configurations vary by region.
.url("wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/realtime")
// The API Key is different for the Singapore and Beijing regions. Get an API Key: https://www.alibabacloud.com/help/en/model-studio/get-api-key
// If you have not configured the environment variable, replace the line below with your Model Studio API Key: .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);
// Read and send audio data in chunks
while ((bytesRead = audioInputStream.read(audioBuffer)) != -1) {
totalBytesRead += bytesRead;
String audioB64 = Base64.getEncoder().encodeToString(audioBuffer);
// Send audio chunk to conversation
conversation.appendAudio(audioB64);
// Add small delay to simulate real-time audio streaming
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);
}
//send session.finish and wait for finish and close
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():
"""Configure logging output"""
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():
"""Initialize API Key"""
# The API Key is different for the Singapore and Beijing regions. Get an API Key: https://www.alibabacloud.com/help/en/model-studio/get-api-key
# If you have not configured the environment variable, replace the line below with your Model Studio API Key: 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):
"""Real-time recognition callback handler"""
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):
"""Read audio file in chunks"""
with open(file_path, 'rb') as f:
while chunk := f.read(chunk_size):
yield chunk
def send_audio(conversation, file_path, delay=0.1):
"""Send audio data"""
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',
# The following configuration is for the Singapore region. Replace "{WorkspaceId}" with your actual workspace ID. Configurations vary by region.
url='wss://{WorkspaceId}.ap-southeast-1.maas.aliyuncs.com/api-ws/v1/realtime',
callback=callback,
)
callback.conversation = conversation # Inject conversation into the callback so its methods can be invoked from the callback
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)
# send session.finish and wait for finished and close
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 menggunakan kembali contoh kode dari Fun-ASR. Ganti parameter model dengan nama model Paraformer.
Konfigurasi pengenalan
Mode interaksi Qwen-ASR
API Realtime Qwen-ASR mendukung dua mode interaksi:
-
Mode VAD (default): Server secara otomatis mendeteksi awal dan akhir ucapan (deteksi giliran). Mode ini cocok untuk percakapan real-time, transkripsi rapat, dan skenario serupa. Untuk mengaktifkannya, konfigurasikan
session.turn_detection(diaktifkan secara default). -
Mode manual: Klien mengontrol deteksi giliran dengan mengirim
input_audio_buffer.commit. Mode ini cocok untuk skenario yang memerlukan kontrol eksplisit atas waktu pengiriman, seperti pesan suara di aplikasi obrolan. Untuk mengaktifkannya, atursession.turn_detectionke null.
Beralih antar mode:
-
WebSocket: Atur bidang
turn_detectiondalam eventsession.update.{ "type": "session.update", "session": { "turn_detection": null } } -
SDK Python: Atur parameter
enable_turn_detectiondalam metodeupdate_session.conversation.update_session( enable_turn_detection=False ) -
SDK Java: Atur parameter
enableTurnDetectionmelaluiOmniRealtimeConfig.builder().OmniRealtimeConfig config = OmniRealtimeConfig.builder() .enableTurnDetection(false) .build(); conversation.updateSession(config);
Untuk contoh kode SDK lengkap, lihat Referensi API SDK Python Qwen-ASR-Realtime dan SDK Java. Untuk siklus hidup event WebSocket, lihat Alur event.
Deteksi giliran VAD
Voice Activity Detection (VAD) menentukan kapan ucapan berkelanjutan berakhir, yang memicu event hasil akhir. Ketiga keluarga model mengaktifkan VAD sisi server secara default, tetapi nama parameter dan kemampuan penyetelannya berbeda:
-
Qwen-ASR: Dikonfigurasi melalui
session.turn_detection, yang mencakupsilence_duration_ms(ambang batas durasi diam; giliran berakhir ketika diam melebihi nilai ini; default server800; direkomendasikan400untuk skenario percakapan dan obrolan yang memerlukan deteksi giliran cepat) danthreshold(sensitivitas deteksi VAD; default server0.2). Qwen-ASR juga mendukung Mode Manual yang menonaktifkan VAD dan memungkinkan klien mengontrol deteksi giliran melaluicommit. Lihat Mode interaksi Qwen-ASR di atas. -
Fun-ASR dan Paraformer: Dikonfigurasi melalui
max_sentence_silence(ambang batas deteksi giliran VAD, dalam milidetik). Ketika diam setelah segmen ucapan melebihi ambang batas ini, kalimat dianggap berakhir.
Nama parameter berbeda per protokol: konsep yang sama dinyatakan sebagai silence_duration_ms di Qwen-ASR dan max_sentence_silence di Fun-ASR dan Paraformer. Untuk definisi bidang lengkap, lihat Referensi API.
Fitur lanjutan
Tingkatkan akurasi dengan hotwords
Fun-ASR dan Paraformer mendukung hotwords untuk meningkatkan akurasi pengenalan istilah tertentu, seperti nama merek, nama pribadi, dan kata benda khusus.
Untuk konfigurasi dan penggunaan hotword, lihat Tingkatkan akurasi pengenalan.
Timestamp
Fun-ASR dan Paraformer mengembalikan timestamp pada dua granularitas secara default: tingkat kalimat dan tingkat kata. Fitur ini mendukung penyelarasan subtitel, penyorotan kata kunci, tampilan lirik bergaya karaoke, serta kasus penggunaan serupa. Qwen-ASR Realtime (qwen3-asr-flash-realtime) saat ini tidak mengembalikan timestamp. Untuk mendapatkan timestamp, gunakan Fun-ASR atau Paraformer. Model transkripsi file Qwen-ASR qwen3-asr-flash-filetrans mendukung timestamp tingkat kata. Lihat Pengenalan ucapan non-real-time.
Timestamp dilaporkan dalam milidetik pada dua tingkat:
-
Tingkat kalimat:
payload.output.sentence.begin_timedanpayload.output.sentence.end_timemenandai awal dan akhir kalimat dalam audio. Pada hasil antara,end_timemungkinnull. Nilai akhir diisi ketika kalimat berakhir (sentence_end = true). -
Tingkat kata: Larik
payload.output.sentence.words. Setiap elemen berisibegin_time,end_time,text(teks karakter atau kata), danpunctuation(tanda baca yang mengikuti karakter; string kosong jika tidak ada).
Contoh respons (cuplikan):
{
"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": "" }
]
}
}
}
}
Nama bidang di atas menggunakan jalur JSON WebSocket. Setiap SDK mengekspos bidang-bidang ini sesuai konvensi penamaannya sendiri (kunci kamus, properti objek, metode getter, dan sebagainya). Untuk pemetaan bidang lengkap, lihat referensi API setiap SDK.
Untuk definisi bidang lengkap, lihat Referensi API.
Pengenalan emosi
Beberapa model Qwen-ASR dan Paraformer menyertakan kondisi emosional pembicara dalam hasil transkripsi. Granularitas output dan cara mengaktifkannya berbeda antara keduanya.
Qwen-ASR (qwen3-asr-flash-realtime): Selalu aktif tanpa perlu konfigurasi. Bidang tingkat atas emotion dikembalikan baik dalam event conversation.item.input_audio_transcription.text maupun conversation.item.input_audio_transcription.completed. Nilainya adalah salah satu dari tujuh emosi detail halus: surprised, neutral, happy, sad, disgusted, angry, dan fearful.
{
"type": "conversation.item.input_audio_transcription.text",
"emotion": "neutral",
"text": "The weather is nice today.",
"stash": ""
}
Paraformer (paraformer-realtime-8k-v2): Hanya model Paraformer ini yang mendukung pengenalan emosi. Hasil dikembalikan melalui payload.output.sentence.emo_tag dan payload.output.sentence.emo_confidence. Nilainya adalah salah satu dari tiga polaritas: positive (emosi positif seperti kebahagiaan atau kepuasan), negative (emosi negatif seperti kemarahan atau semangat rendah), dan neutral (tidak ada emosi jelas). Rentang kepercayaan adalah [0.0, 1.0].
Pengenalan emosi dikembalikan hanya jika semua kondisi berikut terpenuhi:
-
Modelnya adalah
paraformer-realtime-8k-v2. -
Deteksi giliran semantik dinonaktifkan:
semantic_punctuation_enabled = false(nilai default; tidak perlu pengaturan khusus). -
Hasil dikembalikan hanya dalam event akhir kalimat di mana
sentence_end = true.
Untuk menonaktifkan bidang pengenalan emosi, atur semantic_punctuation_enabled ke true. Pengaturan ini mengaktifkan deteksi giliran semantik, sehingga bidang emo_tag dan emo_confidence tidak lagi dikembalikan.
Nama bidang di atas menggunakan jalur JSON WebSocket. Setiap SDK mengekspos bidang-bidang ini sesuai konvensi penamaannya sendiri (kunci kamus, properti objek, metode getter, dan sebagainya). Untuk pemetaan bidang lengkap, lihat referensi API setiap SDK.
Untuk definisi bidang lengkap, batasan nilai, dan contoh, lihat Referensi API.
Protokol WebSocket mentah
Contoh berikut menunjukkan cara menghubungkan ke server melalui protokol WebSocket mentah, cocok untuk skenario yang tidak menggunakan SDK DashScope. Ini merupakan implementasi minimal yang dapat dijalankan. Untuk protokol WebSocket tiap model, lihat Referensi API.
Terapkan di produksi
Penggunaan ulang koneksi (WebSocket)
Fun-ASR dan Paraformer mendukung penggunaan ulang koneksi WebSocket: setelah satu tugas pengenalan selesai, Anda dapat memulai tugas berikutnya pada koneksi yang sama tanpa perlu terhubung ulang.
Alur penggunaan ulang: Klien mengirim finish-task. Setelah server mengembalikan task-finished, klien mengirim run-task lagi untuk memulai tugas baru.
-
Tunggu hingga server mengembalikan event
task-finishedsebelum memulai tugas baru. -
Setiap tugas pada koneksi yang digunakan ulang harus menggunakan
task_idyang berbeda. -
Jika tugas gagal, server mengembalikan event error dan menutup koneksi. Koneksi tersebut tidak dapat digunakan ulang.
-
Koneksi ditutup secara otomatis jika tidak ada tugas baru yang dimulai dalam waktu 60 detik setelah tugas berakhir.
Qwen-ASR Realtime menggunakan model sesi. Tutup koneksi setelah setiap sesi berakhir karena penggunaan ulang koneksi tidak didukung.
Untuk deskripsi event setiap model, lihat Referensi API yang sesuai.
Praktik terbaik konkurensi tinggi
SDK DashScope mencakup pooling bawaan yang menggunakan ulang koneksi WebSocket dan objek pengenalan, sehingga menghindari overhead pembuatan dan penghancuran berulang. Saat ini, hanya SDK Java Paraformer yang mendukung fitur ini.
Tingkatkan akurasi pengenalan
-
Cocokkan model dengan laju sampel: Untuk audio telepon 8 kHz, gunakan model 8 kHz secara langsung. Jangan naikkan sampel ke 16 kHz karena hal tersebut mendistorsi sinyal.
-
Tingkatkan kualitas input audio: Gunakan mikrofon berkualitas tinggi di lingkungan perekaman dengan rasio sinyal-terhadap-kebisingan tinggi dan bebas gema. Di lapisan aplikasi, integrasikan pra-pemrosesan seperti pengurangan kebisingan (misalnya, RNNoise) dan pembatalan gema akustik (AEC).
Siapkan ketahanan
-
Rekoneksi sisi klien: Klien harus mengimplementasikan rekoneksi otomatis untuk menangani fluktuasi jaringan. Implementasi referensi SDK Python:
-
Tangkap pengecualian: Implementasikan metode
on_errordalam kelasCallback. SDKdashscopememanggil metode ini ketika mengalami error jaringan atau masalah lain. -
Beritahu status: Ketika
on_errordipicu, atur sinyal reconnect. Di Python, gunakanthreading.Event, yaitu bendera sinyal aman thread. -
Putaran reconnect: Bungkus logika utama dalam putaran
for(misalnya, coba ulang 3 kali). Ketika sinyal reconnect terdeteksi, pengenalan saat ini dihentikan, sumber daya dibersihkan, lalu setelah beberapa detik putaran membuat koneksi baru.
-
-
Gunakan heartbeat untuk menjaga koneksi tetap aktif: Saat memerlukan koneksi jangka panjang ke server, atur parameter heartbeat ke
true. Koneksi tetap aktif meskipun audio diam untuk waktu yang lama. -
Batas laju: Saat memanggil antarmuka model, patuhi aturan Pembatasan laju model.
Model dan wilayah yang didukung
Singapura
Untuk memanggil model berikut, gunakan Kunci API dari wilayah Singapura:
-
Fun-ASR: fun-asr-realtime (stabil, saat ini setara dengan fun-asr-realtime-2025-11-07), fun-asr-realtime-2025-11-07 (snapshot)
-
Qwen3-ASR-Flash-Realtime: qwen3-asr-flash-realtime (stabil, saat ini setara dengan qwen3-asr-flash-realtime-2025-10-27), qwen3-asr-flash-realtime-2026-02-10 (snapshot terbaru), qwen3-asr-flash-realtime-2025-10-27 (snapshot)
China (Beijing)
Untuk memanggil model berikut, gunakan Kunci API dari wilayah Beijing:
-
Fun-ASR: fun-asr-realtime (stabil, saat ini setara dengan fun-asr-realtime-2025-11-07), fun-asr-realtime-2026-02-28 (snapshot terbaru), fun-asr-realtime-2025-11-07 (snapshot), fun-asr-realtime-2025-09-15 (snapshot)
-
fun-asr-flash-8k-realtime (stabil, saat ini setara dengan fun-asr-flash-8k-realtime-2026-01-28), fun-asr-flash-8k-realtime-2026-01-28
-
-
Qwen3-ASR-Flash-Realtime: qwen3-asr-flash-realtime (stabil, saat ini setara dengan qwen3-asr-flash-realtime-2025-10-27), qwen3-asr-flash-realtime-2026-02-10 (snapshot terbaru), qwen3-asr-flash-realtime-2025-10-27 (snapshot)
-
Paraformer: paraformer-realtime-v2, paraformer-realtime-v1, paraformer-realtime-8k-v2, paraformer-realtime-8k-v1
Referensi API
FAQ
Format audio apa saja yang didukung oleh pengenalan ucapan real-time?
Fun-ASR dan Paraformer mendukung pcm, wav, mp3, opus, speex, aac, dan amr. Untuk Qwen-ASR, gunakan pcm atau opus. Format lain (seperti wav, aac, dan amr) lolos validasi session.update, tetapi mungkin gagal saat decoding di sisi server. Pastikan aliran audio menggunakan format yang direkomendasikan sebelum mengirim.
Kapan saya harus menggunakan SDK vs. API WebSocket?
SDK DashScope membungkus manajemen koneksi WebSocket, autentikasi, dan rekoneksi, sehingga menjadi pilihan tercepat untuk integrasi. API WebSocket memberikan kontrol langsung dan detail halus. Gunakan API ini jika SDK tidak mendukung bahasa pemrograman Anda atau jika Anda memerlukan penanganan koneksi khusus. SDK merupakan pilihan yang direkomendasikan untuk sebagian besar kasus penggunaan.
Bagaimana cara meningkatkan akurasi pengenalan untuk kata benda khusus?
Gunakan hotwords (didukung oleh Fun-ASR dan Paraformer). Untuk konfigurasi dan penggunaan hotword, lihat Tingkatkan akurasi pengenalan.
Apa yang dapat saya lakukan ketika koneksi terus terputus?
Implementasikan logika rekoneksi di sisi klien dan aktifkan parameter heartbeat (heartbeat=true) untuk mencegah koneksi terputus selama periode diam yang panjang. Untuk strategi toleransi kesalahan terperinci, lihat Terapkan di produksi.