tencent cloud

Android

PDF
フォーカスモード
フォントサイズ
最終更新日: 2026-02-06 09:27:46

業務フロー

このセクションでは、オンラインカラオケにおけるいくつかの一般的な業務フローをまとめて、全体のシナリオの実装をよりよく理解するのに役立ちます。
曲予約の手順
ソロの流れ
リードシンガーの流れ
合唱の流れ
聞き手の流れ
下図は、業務側が音楽ライブラリを組み合わせて曲を予約し、TRTC SDKを使用して再生するプロセスを示しています。



下の図は、マイク順番待ちソロプレイで、歌い手が入室して歌い、歌い終わって退室するプロセスを示しています。



下図は、リアルタイム合唱プレイ中に、リードシンガーが合唱を開始し、合唱を停止して退室するプロセスを示しています。


下図はリアルタイム合唱プレイ中、合唱者が合唱に参加し、合唱を停止して退室するプロセスを示しています。



下図は、オンラインカラオケシナリオにおいて、聞き手が入室して歌を聞き、歌詞同期プロセスを示しています。




アクセスの準備

ステップ1:サービスを利用する

オンラインカラオケシナリオでは通常、Tencent CloudのReal-Time Communication TRTCIntelligent Music Solutionの2つの有料PaaSサービスに依存して構築されます。その中でTRTCはリアルタイムオーディオインタラクションを提供し、スマートミュージックソリューションは歌詞認識、スマート作曲、曲を聴いて曲を識別する機能、音楽スコアリングなどの能力を提供します。
TRTCサービスを利用する
スマート音楽サービスを利用する
1. まず、Real-Time Communication TRTCコントロールパネルにログインしてアプリケーションを作成する必要があります。必要に応じてTRTCアプリケーションのバージョンをアップグレードすることができます。例えば、プロフェッショナル版ではより多くの付加価値機能サービスが使えます。



説明:
テスト環境と本番環境にそれぞれ使用するために2つのアプリケーションを作成することをお勧めします。1年間に各Tencent Cloudアカウント(UIN)には、毎月10,000分の無料時間が提供されます。
TRTC月額プランは体験版(デフォルト)、基本版、プロフェッショナル版に分かれており、異なる付加価値機能サービスが使えます。詳細はバージョン機能と月額プラン説明を参照してください。
2. アプリケーションが作成された後、アプリケーション管理-アプリケーション概要セクションでそのアプリケーションの基本情報を確認できます。その中で、後で使用するためにSDKAppIDSDKSecretKeyを大切に保管してください。同時に、キーの漏洩はトラフィックの不正利用に繋がりますのでご注意ください。




前置き準備

1. 音楽サービスの利用を開始するには、購入ページに進み、音楽スコアリングなどの適切な機能を選択して利用開始してください。
2. CAMでAK/SKキーペアを作成します(プログラムアクセスユーザーとしてのアクセス方法、ログインやユーザー権限は必要ない)。
3. COS(Cloud Object Storage)バケットを作成し、COSバケット管理インターフェースで作成済みのプログラム専用ユーザーにCOSバケットの読み書き権限を付与してください。
4. パラメータを準備します。
operateUin: Tencent CloudサブユーザーのアカウントID
cosConfig: Cloud Object Storage関連パラメータ
secretId: ストレージバケットのsecretId
secretKey: ストレージバケットのsecretKey
bucket: ストレージバケットの名称
region: ストレージバケットの地域、例えば"ap-guangzhou"

登録をアクティブ化

前置き準備を完了した後、リクエストを送信して登録とアクティベーションを行い、約2分間待機する予定です。
リクエストを開始
リクエスト結果
curl -X POST \\
http://service-mqk0mc83-1257411467.bj.apigw.tencentcs.com/release/register \\
-H 'Content-Type: application/json' \\
-H 'Cache-control: no-cache' \\
-d '{
"requestId": "test-regisiter-service",
"action": "Register",
"registerRequest": {
"operateUin": <operateUin>,
"userName": <customedName>,
"cosConfig": {
"secretId": <CosConfig.secretId>,
"secretKey": <CosConfig.secretKey>,
"bucket": <CosConfig.bucket>,
"region": <CosConfig.region>
}
}
}'
{
"requestId": "test-regisiter-service",
"registerInfo": {
"tmpContentId": <tmpContentId>,
"tmpSecretId": <tmpSecretId>,
"tmpSecretKey": <tmpSecretKey>,
"apiGateSecretId": <apiGateSecretId>,
"apiGateSecretKey": <apiGateSecretKey>,
"demoCosPath": "UIN_demo/run_musicBeat.py",
"usageDescription": "COSバケット[CosConfig.bucket]からpythonのdemoファイル[UIN_demo/run_musicBeat.py], をダウンロードし、demoの入力ファイルを置き換えた後、python run_musicBeat.py",を実行してください
"message": "登録が成功しました。登録していただきありがとうございます",
"createdAt": <createdAt>,
"updatedAt": <updatedAt>
}
}

実行検証

上記のアクティベーション登録サービスを完了した後、demoCosPathディレクトリーに音楽のビート認識機能をサンプルとするpythonで実行可能なdemoが生成されます。ネットワーク環境がある場合に、コマンドpython run_musicBeat.pyを実行して確認してください。
説明:
より詳細なスマートミュージックソリューションのアクセス説明については、アクセスガイドを参照してください。

ステップ2:SDKをインポートする

TRTC SDKはmavenCentralレポジトリーにパブリッシュされました。gradleを設定することで自動的に更新をダウンロードできます。
1. dependenciesに適切なバージョンのSDK依存を追加してください。
dependencies {
// TRTC Lite版SDK、TRTCとライブ再生の2つの機能を含み、コンパクトなサイズ
implementation 'com.tencent.liteav:LiteAVSDK_TRTC:latest.release'
// TRTCフル機能版SDK, 、ライブ配信、ショートビデオ、ビデオオンデマンドなど多機能, を含み、やや大きめのサイズ
// implementation 'com.tencent.liteav:LiteAVSDK_Professional:latest.release'
}
説明:
推奨される自動読み込みの方法以外に、SDKをダウンロードして手動でインポートすることもできます。詳細はTRTC SDKの手動統合を参照してください。
2. defaultConfigで、Appが使用するCPUアーキテクチャを指定します。
defaultConfig {
ndk {
abiFilters "armeabi-v7a", "arm64-v8a"
}
}
説明:
TRTC SDKはarmeabi/armeabi-v7a/arm64-v8aアーキテクチャをサポートし、さらにエミュレータ専用のx86/x86_64アーキテクチャもサポートします。

ステップ3:プロジェクトの設定

1. 権限設定
AndroidManifest.xmlでAppの権限を設定し、TRTC SDKがカラオケシナリオにおいては、以下の権限が必要です。
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.ACCESS_WIFI_STATE" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
<uses-permission android:name="android.permission.BLUETOOTH" />
注意:
TRTC SDKは権限申請ロジックが内蔵していないため、対応する権限と特性を自分で宣言する必要があります。一部の権限(例えば、ストレージ、録音など)は、実行時に動的な申請が必要です。
もしAndroidプロジェクトのtargetSdkVersionが31または対象デバイスがAndroid 12以上のシステムバージョンに関連している場合、Bluetooth機能を正常に使用するため、公式にはコード内でandroid.permission.BLUETOOTH_CONNECT権限を動的な申請する必要があります。詳細はBluetooth権限を参照してください。
2. 難読化設定
私たちはSDK内部でJavaのリフレクション特性を使用しているため、proguard-rules.proファイル内でSDK関連クラスを難読化しないリストに追加する必要があります。
-keep class com.tencent.** { *; }

ステップ4:認証とライセンス

UserSigはTencent Cloudが設計した一種のセキュリティ保護署名で、悪意のある攻撃者がクラウドサービスの使用権を盗用するのを防ぐためのものです。TRTCは入室時に認証クレデンシャルを検証します。
デバッグフェーズ:クライアントサンプルコードコントロールパネル取得の2つの方法でUserSigを計算生成でき、デバッグテストのみに使用します。
本番フェーズ:クライアントがリバースエンジニアリングでキーが漏洩するのを防ぐため、より高いセキュリティレベルのサーバー側UserSig計算を推奨します。
具体的な実装は以下の通りです:
1. AppがSDKの初期化関数を呼び出す前に、最初にサーバーにUserSigをリクエストします。
2. サーバーはSDKAppIDとUserIDに基づいてUserSigを計算します。
3. サーバーは計算されたUserSigをAppに返します。
4. Appは、特定のAPIを通じてSDKにUserSigを伝達します。
5. SDKがSDKAppID + UserID + UserSigをTencent Cloudのクラウドサーバーに提出して検証します。
6. Tencent CloudはUserSigを検証し、合法性を確認します。
7. 検証を通過した後、TRTC SDKにReal-Time Communicationサービスを提供します。



注意:
デバッグフェーズのローカルUserSig計算方式は、オンライン環境に適用することは推奨しません。逆コンパイルによって容易に解読され、キーが漏洩する可能性があります。
複数の言語(Java/GO/PHP/Node.js/Python/C#/C++)のUserSigサーバーサイド計算のソースコードを提供しています。詳細はサーバーサイドUserSig計算を参照してください。

ステップ5:SDKの初期化

// TRTC SDKのインスタンスを作成する(シングルトンパターン)
TRTCCloud mTRTCCloud = TRTCCloud.sharedInstance(context);
// イベントリスナーを設定する
mTRTCCloud.addListener(trtcSdkListener);

// SDKからの各種イベント通知(例:エラーコード、警告コード、オーディオ・ビデオの状態パラメータなど)
private TRTCCloudListener trtcSdkListener = new TRTCCloudListener() {
@Override
public void onError(int errCode, String errMsg, Bundle extraInfo) {
Log.d(TAG, errCode + errMsg);
}
@Override
public void onWarning(int warningCode, String warningMsg, Bundle extraInfo) {
Log.d(TAG, warningCode + warningMsg);
}
};

// イベントリスナーを削除
mTRTCCloud.removeListener(trtcSdkListener);
// TRTC SDKインスタンスを破棄する(シングルトンパターン)
TRTCCloud.destroySharedInstance();
説明:
SDKイベント通知のリスニングを推奨します。一般的なエラーに関するログ出力と処理についての詳細はエラーコード表を参照してください。

シナリオ1:マイク順番待ちソロ

視点1:歌い手のアクション

タイムライン図




1. ルームに入る
public void enterRoom(String roomId, String userId) {
TRTCCloudDef.TRTCParams params = new TRTCCloudDef.TRTCParams();
// 文字列のルーム番号を例に
params.strRoomId = roomId;
params.userId = userId;
// 業務バックエンドから取得したUserSig
params.userSig = getUserSig(userId);
// 自分のSDKAppIDに置き換える
params.sdkAppId = SDKAppID;
// すべて視聴者ロールで入室する事がお勧め
params.role = TRTCCloudDef.TRTCRoleAudience;
// 入室シナリオはLIVEを選択
mTRTCCloud.enterRoom(params, TRTCCloudDef.TRTC_APP_SCENE_LIVE);
}
注意:
歌詞同期用のSEIメッセージをより効果的にパススルーするために、入室シナリオではTRTC_APP_SCENE_LIVEを選択することをお勧めします。
// 入室結果イベントコールバック
@Override
public void onEnterRoom(long result) {
if (result > 0) {
// resultは入室にかかった時間(ミリ秒)
Log.d(TAG, "Enter room succeed");
// ブラックフレーム挿入の実験的インターフェースを有効にする
mTRTCCloud.callExperimentalAPI("{\\"api\\":\\"enableBlackStream\\",\\"params\\": {\\"enable\\":true}}");
} else {
// result入室失敗のエラーコード
Log.d(TAG, "Enter room failed");
}
}
注意:
オーディオオンリーモードでの歌い手は、SEIメッセージを送信するためにブラックフレーム挿入を有効にする必要があります。このインターフェースは、ルームへの入室成功後に呼び出す必要があります。
2. マイクオンプッシュ
// アンカーロールに切り替え
mTRTCCloud.switchRole(TRTCCloudDef.TRTCRoleAnchor);

// ロール切り替えイベントコールバック
@Override
public void onSwitchRole(int errCode, String errMsg) {
if (errCode == TXLiteAVCode.ERR_NULL) {
// メディア音量タイプの設定
mTRTCCloud.setSystemVolumeType(TRTCCloudDef.TRTCSystemVolumeTypeMedia);
// ローカルオーディオストリームを配布し、音質を設定する
mTRTCCloud.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_MUSIC);
}
}
注意:
カラオケシナリオでは、全過程のメディア音量とMusic音質を設定することをお勧めします。これにより、高音質の聴覚体験を得ることができます。
3. 曲の予約
曲を検索し、音楽リソースを取得する
業務バックエンドで目的の曲を検索し、曲の識別子MusicId、曲のアドレスMusicUrl、歌詞のアドレスLyricsUrlなどの音楽リソースを取得します。
業務側には、適切な音楽ライブラリ製品を自ら選択し、正規の音楽リソースを提供することをお勧めします。
伴奏を再生し、歌い始めます。
// オーディオエフェクト管理クラスを取得
TXAudioEffectManager mTXAudioEffectManager = mTRTCCloud.getAudioEffectManager();

// originMusicId: カスタム原曲識別子。originMusicUrl: 原曲リソースアドレス
TXAudioEffectManager.AudioMusicParam originMusicParam = new TXAudioEffectManager.AudioMusicParam(originMusicId, originMusicUrl);
// 原曲をリモートにパブリッシュ(またはローカルのみで再生)
originMusicParam.publish = true;

// accompMusicId: カスタム伴奏識別子。accompMusicUrl: 伴奏リソースアドレス
TXAudioEffectManager.AudioMusicParam accompMusicParam = new TXAudioEffectManager.AudioMusicParam(accompMusicId, accompMusicUrl);
// 伴奏をリモートにパブリッシュ(またはローカルのみで再生)
accompMusicParam.publish = true;

// 原曲の再生を開始
mTXAudioEffectManager.startPlayMusic(originMusicParam);
// 伴奏の再生を開始
mTXAudioEffectManager.startPlayMusic(accompMusicParam);

// 原曲に切り替える
mTXAudioEffectManager.setMusicPlayoutVolume(originMusicId, 100);
mTXAudioEffectManager.setMusicPlayoutVolume(accompMusicId, 0);
mTXAudioEffectManager.setMusicPublishVolume(originMusicId, 100);
mTXAudioEffectManager.setMusicPublishVolume(accompMusicId, 0);

// 伴奏に切り替える
mTXAudioEffectManager.setMusicPlayoutVolume(originMusicId, 0);
mTXAudioEffectManager.setMusicPlayoutVolume(accompMusicId, 100);
mTXAudioEffectManager.setMusicPublishVolume(originMusicId, 0);
mTXAudioEffectManager.setMusicPublishVolume(accompMusicId, 100);
注意:
カラオケシナリオでは、原曲と伴奏を同時に再生する必要があります(MusicIDで区別)。ローカルとリモートの再生音量を調整することで、原曲と伴奏の切り替えを実現します。
二重音声トラック(原曲と伴奏を含む)の音楽が再生されている場合、setMusicTrackを使用して音楽の再生トラックを指定し、原曲と伴奏を切り替えることができます。
4. 歌詞同期
歌詞をダウンロード
業務バックエンドから目標の歌詞のダウンロードリンクLyricsUrlを取得し、目標の歌詞をローカルにキャッシュします。
ローカル歌詞同期、及びSEIで曲の進行状況の伝達
mTXAudioEffectManager.setMusicObserver(musicId, new TXAudioEffectManager.TXMusicPlayObserver() {
@Override
public void onStart(int id, int errCode) {
// 音楽再生を開始
}
@Override
public void onPlayProgress(int id, long curPtsMs, long durationMs) {
// 最新の進行状況とローカルの歌詞の進行状況の誤差に基づいて、seekが必要かどうかを判断する
// SEIメッセージを送信して曲の進行状況を伝える
try {
JSONObject jsonObject = new JSONObject();
jsonObject.put("musicId", id);
jsonObject.put("progress", curPtsMs);
jsonObject.put("duration", durationMs);
mTRTCCloud.sendSEIMsg(jsonObject.toString().getBytes(), 1);
} catch (JSONException e) {
e.printStackTrace();
}
}
@Override
public void onComplete(int id, int errCode) {
// 音楽再生完了
}
});
注意:
BGMを再生する前に、このインターフェースを使用して再生イベントのコールバックを設定し、BGMの再生進行状況を把握してください。
歌い手がSEIメッセージを送信する頻度はイベントコールバックの頻度によって決まります。ここでは、getMusicCurrentPosInMSを使用して再生進行状況を定期的に同期することもできます。
5. マイクオフして退室
// 視聴者ロールに切り替える
mTRTCCloud.switchRole(TRTCCloudDef.TRTCRoleAudience);

// ロール切り替えイベントコールバック
@Override
public void onSwitchRole(int errCode, String errMsg) {
if (errCode == TXLiteAVCode.ERR_NULL) {
// 伴奏の再生を停止
mTRTCCloud.getAudioEffectManager().stopPlayMusic(musicId);
// ローカルオーディオのキャプチャーとパブリッシュを停止する
mTRTCCloud.stopLocalAudio();
}
}

// ルームから退出
mTRTCCloud.exitRoom();

// 退室イベントコールバック
@Override
public void onExitRoom(int reason) {
if (reason == 0) {
Log.d(TAG, "exitRoomアクティブコールでルーム退出します");
} else if (reason == 1) {
Log.d(TAG, "現在のルームからサーバーによってキックされました");
} else if (reason == 2) {
Log.d(TAG, "現在のルームは解散されました");
}
}
注意:
SDKが使うすべてのリソースがリリースされた後、SDKはonExitRoomコールバック通知をスローして知らせます。
再度enterRoomを呼び出す場合や他のオーディオ・ビデオSDKに切り替える場合は、onExitRoomのコールバックが返ってくるまで関連操作を行わないでください。そうしないと、カメラやマイクが強制的に使用されるなど、さまざまな異常が発生する可能性があります。

視点2:聞き手のアクション

タイムライン図




1. ルームに入る
public void enterRoom(String roomId, String userId) {
TRTCCloudDef.TRTCParams params = new TRTCCloudDef.TRTCParams();
// 文字列のルーム番号を例に
params.strRoomId = roomId;
params.userId = userId;
// 業務バックエンドから取得したUserSig
params.userSig = getUserSig(userId);
// 自分のSDKAppIDに置き換える
params.sdkAppId = SDKAppID;
// すべて視聴者ロールで入室する事がお勧め
params.role = TRTCCloudDef.TRTCRoleAudience;
// 入室シナリオはLIVEを選択
mTRTCCloud.enterRoom(params, TRTCCloudDef.TRTC_APP_SCENE_LIVE);
}

// 入室結果イベントコールバック
@Override
public void onEnterRoom(long result) {
if (result > 0) {
// resultは入室にかかった時間(ミリ秒)
Log.d(TAG, "Enter room succeed");
} else {
// result入室失敗のエラーコード
Log.d(TAG, "Enter room failed");
}
}
注意:
歌詞同期用のSEIメッセージをより効果的にパススルーするために、入室シナリオではTRTC_APP_SCENE_LIVEを選択することをお勧めします。
自動購読モード(デフォルト)では、視聴者が入室すると、自動的にマイクオンアンカーのオーディオストリームを購読して再生します。
2. 歌詞同期
歌詞をダウンロード
業務バックエンドから目標の歌詞のダウンロードリンクLyricsUrlを取得し、目標の歌詞をローカルにキャッシュします。
聞き手側の歌詞同期
@Override
public void onUserVideoAvailable(String userId, boolean available) {
if (available) {
mTRTCCloud.startRemoteView(userId, null);
} else {
mTRTCCloud.stopRemoteView(userId);
}
}

@Override
public void onRecvSEIMsg(String userId, byte[] data) {
String result = new String(data);
try {
JSONObject jsonObject = new JSONObject(result);
int musicId = jsonObject.getInt("musicId");
long progress = jsonObject.getLong("progress");
long duration = jsonObject.getLong("duration");
} catch (JSONException e) {
e.printStackTrace();
}
...
// TODO歌詞コントロールロジックの更新:
// 受信した最新の進行状況とローカルの歌詞進行の誤差に基づいて、歌詞コントロールがseekする必要があるかどうかを判断します。
...
}
注意:
聞き手は、ブラックフレームに含まれるSEIメッセージを受信するために、歌い手のビデオストリームを自分から購読する必要があります。
3. ルームから退出
// ルームから退出
mTRTCCloud.exitRoom();

// 退室イベントコールバック
@Override
public void onExitRoom(int reason) {
if (reason == 0) {
Log.d(TAG, "exitRoomアクティブコールでルーム退出します");
} else if (reason == 1) {
Log.d(TAG, "現在のルームからサーバーによってキックされました");
} else if (reason == 2) {
Log.d(TAG, "現在のルームは解散されました");
}
}

シナリオ2:リアルタイム合唱

視点1:リードシンガーのアクション

タイムライン図




1. ダブルインスタンスで入室
// TRTCCloudマスターインスタンス(ボーカルインスタンス)を作成
TRTCCloud mTRTCCloud = TRTCCloud.sharedInstance(context);
// TRTCCloudサブインスタンス(インストゥルメンタルインスタンス)を作成
TRTCCloud subCloud = mTRTCCloud.createSubCloud();

// マスターインスタンス(ボーカルインスタンス)が入室
TRTCCloudDef.TRTCParams params = new TRTCCloudDef.TRTCParams();
params.sdkAppId = SDKAppId;
params.userId = UserId;
params.userSig = UserSig;
params.role = TRTCCloudDef.TRTCRoleAnchor;
params.strRoomId = RoomId;
mTRTCCloud.enterRoom(params, TRTCCloudDef.TRTC_APP_SCENE_LIVE);

// サブインスタンスが手動購読モードを開始し、デフォルトではリモートストリームを購読しない
subCloud.setDefaultStreamRecvMode(false, false);

// サブインスタンス(インストゥルメンタルインスタンス)が入室
TRTCCloudDef.TRTCParams bgmParams = new TRTCCloudDef.TRTCParams();
bgmParams.sdkAppId = SDKAppId;
// サブインスタンスのユーザー名は、ルーム内の他のユーザーと重複してはいけません。
bgmParams.userId = UserId + "_bgm";
bgmParams.userSig = UserSig;
bgmParams.role = TRTCCloudDef.TRTCRoleAnchor;
bgmParams.strRoomId = RoomId;
subCloud.enterRoom(bgmParams, TRTCCloudDef.TRTC_APP_SCENE_LIVE);
注意:
リアルタイム合唱プランでは、リードシンガー側は、それぞれボーカルおよび伴奏のアップストリームに使用するマスターインスタンスとサブインスタンスを作成する必要があります。
サブインスタンスは、ルーム内の他のユーザーのオーディオストリームを購読する必要がないため、手動購読モードを有効にすることをお勧めします。これは入室前に有効にする必要があります。
2. 入室後の設定
// マスターインスタンスの入室結果イベントコールバック
@Override
public void onEnterRoom(long result) {
if (result > 0) {
// マスターインスタンスがサブインスタンスがパブリッシュした音楽ストリームの購読をキャンセル
mTRTCCloud.muteRemoteAudio(UserId + "_bgm", true);
// マスターインスタンスがブラックフレーム挿入の実験的インターフェースを開始
mTRTCCloud.callExperimentalAPI(
"{\\"api\\":\\"enableBlackStream\\",\\"params\\": {\\"enable\\":true}}");
// マスターインスタンスが合唱モードの実験的インターフェースを開始
mTRTCCloud.callExperimentalAPI(
"{\\"api\\":\\"enableChorus\\",\\"params\\":{\\"enable\\":true,\\"audioSource\\":0}}");
// マスターインスタンスが低遅延モードの実験的インターフェースを開始
mTRTCCloud.callExperimentalAPI(
"{\\"api\\":\\"setLowLatencyModeEnabled\\",\\"params\\":{\\"enable\\":true}}");
// マスターインスタンスが音量コールバックを有効にする
mTRTCCloud.enableAudioVolumeEvaluation(300, false);
// マスターインスタンス全過程のメディア音量タイプを設定
mTRTCCloud.setSystemVolumeType(TRTCCloudDef.TRTCSystemVolumeTypeMedia);
// マスターインスタンスがローカルオーディオをキャプチャー・パブリッシュし、同時に音質を設定
mTRTCCloud.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_MUSIC);
} else {
// result入室失敗のエラーコード
Log.d(TAG, "Enter room failed");
}
}

// サブインスタンスの入室結果イベントコールバック
@Override
public void onEnterRoom(long result) {
if (result > 0) {
// サブインスタンスで合唱モードの実験的インターフェースを有効にする
subCloud.callExperimentalAPI(
"{\\"api\\":\\"enableChorus\\",\\"params\\":{\\"enable\\":true,\\"audioSource\\":1}}");
// サブインスタンスが低遅延モードの実験的インターフェースを開始
subCloud.callExperimentalAPI(
"{\\"api\\":\\"setLowLatencyModeEnabled\\",\\"params\\":{\\"enable\\":true}}");
// サブインスタンスの全過程のメディア音量タイプを設定
subCloud.setSystemVolumeType(TRTCCloudDef.TRTCSystemVolumeTypeMedia);
// サブインスタンスの音質設定
subCloud.setAudioQuality(TRTCCloudDef.TRTC_AUDIO_QUALITY_MUSIC);
} else {
// result入室失敗のエラーコード
Log.d(TAG, "Enter room failed");
}
}
注意:
マスターインスタンスとサブインスタンスは、合唱体験を最適化するために実験的なインターフェースを使用して合唱モードと低遅延モードを有効にする必要があります。audioSourceパラメータの違いに注意する必要があります。
3. ルームにオーディオ・ビデオストリームプッシュバック
private void startPublishMediaToRoom(String roomId, String userId) {
// TRTCPublishTargetオブジェクトを作成
TRTCCloudDef.TRTCPublishTarget target = new TRTCCloudDef.TRTCPublishTarget();
// ミキシング後にルームにプッシュバック
target.mode = TRTCCloudDef.TRTC_PublishMixStream_ToRoom;
target.mixStreamIdentity.strRoomId = roomId;
// ミキシングロボットのユーザー名は、ルーム内の他のユーザーと重複してはいけません。
target.mixStreamIdentity.userId = userId + "_robot";

// トランスコード後のオーディオストリームのエンコードパラメータを設定する(カスタマイズ可能)
TRTCCloudDef.TRTCStreamEncoderParam trtcStreamEncoderParam = new TRTCCloudDef.TRTCStreamEncoderParam();
trtcStreamEncoderParam.audioEncodedChannelNum = 2;
trtcStreamEncoderParam.audioEncodedKbps = 64;
trtcStreamEncoderParam.audioEncodedCodecType = 2;
trtcStreamEncoderParam.audioEncodedSampleRate = 48000;

// トランスコード後のビデオストリームのエンコードパラメータを設定する(ブラックフレームの挿入が必要)
trtcStreamEncoderParam.videoEncodedFPS = 15;
trtcStreamEncoderParam.videoEncodedGOP = 3;
trtcStreamEncoderParam.videoEncodedKbps = 30;
trtcStreamEncoderParam.videoEncodedWidth = 64;
trtcStreamEncoderParam.videoEncodedHeight = 64;

// オーディオストリームミックスのパラメータを設定
TRTCCloudDef.TRTCStreamMixingConfig trtcStreamMixingConfig = new TRTCCloudDef.TRTCStreamMixingConfig();
// デフォルトでは空欄のままで大丈夫です。これは、ルーム内のすべてのオーディオがミキシングされることを意味します。
trtcStreamMixingConfig.audioMixUserList = null;

// ビデオストリームミックステンプレートの配置(ブラックフレームを挿入する場合は必須)
TRTCCloudDef.TRTCVideoLayout videoLayout = new TRTCCloudDef.TRTCVideoLayout();
trtcStreamMixingConfig.videoLayoutList.add(videoLayout);

// オーディオ・ビデオストリームプッシュバックを開始
mTRTCCloud.startPublishMediaStream(target, trtcStreamEncoderParam, trtcStreamMixingConfig);
}
注意:
合唱のボーカルと伴奏を揃えるために、ルームにオーディオ・ビデオストリームプッシュバックを開始することをお勧めします。マイクオンの合唱者は互いにシングルストリームを購読し、マイクオフの聞き手はデフォルトでストリームミックスのみを購読します。
ミキシングロボットは独立したユーザーとして入室し、ストリームをプル、ミキシング、転送します。そのユーザー名はルームの他のユーザー名と重複してはならず、そうでないと相互にキックされる原因となります。
4. 検索と曲予約
業務バックエンドで目的の曲を検索し、曲の識別子MusicId、曲のアドレスMusicUrl、歌詞のアドレスLyricsUrlなどの音楽リソースを取得します。
業務側には、適切な音楽ライブラリ製品を自ら選択し、正規の音楽リソースを提供することをお勧めします。
5. NTP時刻同期
TXLiveBase.setListener(new TXLiveBaseListener() {
@Override
public void onUpdateNetworkTime(int errCode, String errMsg) {
super.onUpdateNetworkTime(errCode, errMsg);
// errCode 0: 時刻同期成功、偏差は30ms以内。1: 時刻同期成功、しかし偏差は30ms以上の可能性あります。-1: 時刻同期失敗
if (errCode == 0) {
// 時刻同期成功、NTPタイムスタンプを取得
long ntpTime = TXLiveBase.getNetworkTimestamp();
} else {
// 時刻同期に失敗しました。時刻同期を再試行してください。
TXLiveBase.updateNetworkTime();
}
}
});

TXLiveBase.updateNetworkTime();
注意:
NTP時刻同期の結果は、ユーザーの現在のネットワーク品質を反映することができます。良好な合唱体験を保証するために、時刻同期に失敗した場合はユーザーが合唱を開始することを許可しないことをお勧めします。
6. 合唱シグナルを送信
Timer mTimer = new Timer();
mTimer.schedule(new TimerTask() {
@Override
public void run() {
try {
JSONObject jsonObject = new JSONObject();
jsonObject.put("cmd", "startChorus");
// 合唱開始時間を設定: 現在のNTP時間+遅延再生時間(例:3秒)
jsonObject.put("startPlayMusicTS", TXLiveBase.getNetworkTimestamp() + 3000);
jsonObject.put("musicId", musicId);
jsonObject.put("musicDuration", subCloud.getAudioEffectManager().getMusicDurationInMS(originMusicUri));
mTRTCCloud.sendCustomCmdMsg(1, jsonObject.toString().getBytes(), false, false);
} catch (JSONException e) {
e.printStackTrace();
}
}
}, 0, 1000);
注意:
リードシンガーは、新しいユーザーが途中入室しても合唱に参加できるように、定期的な時間間隔(例えば1秒)でルーム内に合唱シグナルをブロードキャストする必要があります。
7. 伴奏の読み込み再生
// オーディオエフェクト管理クラスを取得
TXAudioEffectManager mTXAudioEffectManager = subCloud.getAudioEffectManager();

// originMusicId: カスタム原曲識別子。originMusicUrl: 原曲リソースアドレス
TXAudioEffectManager.AudioMusicParam originMusicParam = new TXAudioEffectManager.AudioMusicParam(originMusicId, originMusicUrl);
// 原曲をリモートにパブリッシュ
originMusicParam.publish = true;
// 音楽再生を開始するタイムスタンプ(ミリ秒)
originMusicParam.startTimeMS = 0;

// accompMusicId: カスタム伴奏識別子。accompMusicUrl: 伴奏リソースアドレス
TXAudioEffectManager.AudioMusicParam accompMusicParam = new TXAudioEffectManager.AudioMusicParam(accompMusicId, accompMusicUrl);
// 伴奏をリモートにパブリッシュ
accompMusicParam.publish = true;
// 音楽再生を開始するタイムスタンプ(ミリ秒)
accompMusicParam.startTimeMS = 0;

// 原曲のプリロード
mTXAudioEffectManager.preloadMusic(originMusicParam);
// 伴奏をプリロード
mTXAudioEffectManager.preloadMusic(accompMusicParam);

// 遅延再生時間(例えば3秒)後に原曲を再生開始
mTXAudioEffectManager.startPlayMusic(originMusicParam);
// 遅延再生時間(例えば3秒)後に伴奏を再生開始
mTXAudioEffectManager.startPlayMusic(accompMusicParam);

// 原曲に切り替える
mTXAudioEffectManager.setMusicPlayoutVolume(originMusicId, 100);
mTXAudioEffectManager.setMusicPlayoutVolume(accompMusicId, 0);
mTXAudioEffectManager.setMusicPublishVolume(originMusicId, 100);
mTXAudioEffectManager.setMusicPublishVolume(accompMusicId, 0);

// 伴奏に切り替える
mTXAudioEffectManager.setMusicPlayoutVolume(originMusicId, 0);
mTXAudioEffectManager.setMusicPlayoutVolume(accompMusicId, 100);
mTXAudioEffectManager.setMusicPublishVolume(originMusicId, 0);
mTXAudioEffectManager.setMusicPublishVolume(accompMusicId, 100);
注意:
音楽を再生する前に、事前に音楽リソースをメモリにプリロードすることをお勧めします。これにより、音楽再生のローディング遅延を効果的に低減できます。
カラオケシナリオでは、原曲と伴奏を同時に再生する必要があります(MusicIDで区別)。ローカルとリモートの再生音量を調整することで、原曲と伴奏の切り替えを実現します。
二重音声トラック(原曲と伴奏を含む)の音楽が再生されている場合、setMusicTrackを使用して音楽の再生トラックを指定し、原曲と伴奏を切り替えることができます。
8. 伴奏同期
// 約束の合唱開始時間
long mStartPlayMusicTs = jsonObject.getLong("startPlayMusicTS");
// 現在伴奏の実際の再生進行状況
long currentProgress = subCloud.getAudioEffectManager().getMusicCurrentPosInMS(musicId);
// 現在伴奏の理想的な再生進行状況
long estimatedProgress = TXLiveBase.getNetworkTimestamp() - mStartPlayMusicTs;
// 進捗差が50msを超えた場合、修正を行う
if (estimatedProgress >= 0 && Math.abs(currentProgress - estimatedProgress) > 50) {
subCloud.getAudioEffectManager().seekMusicToPosInMS(musicId, (int) estimatedProgress);
}
9. 歌詞同期
歌詞をダウンロード
業務バックエンドから目標の歌詞のダウンロードリンクLyricsUrlを取得し、目標の歌詞をローカルにキャッシュします。
ローカル歌詞同期、及びSEIで曲の進行状況の伝達
mTXAudioEffectManager.setMusicObserver(musicId, new TXAudioEffectManager.TXMusicPlayObserver() {
@Override
public void onStart(int id, int errCode) {
// 音楽再生を開始
}
@Override
public void onPlayProgress(int id, long curPtsMs, long durationMs) {
// 最新の進行状況とローカルの歌詞の進行状況の誤差に基づいて、seekが必要かどうかを判断する
// SEIメッセージを送信して曲の進行状況を伝える
try {
JSONObject jsonObject = new JSONObject();
jsonObject.put("musicId", id);
jsonObject.put("progress", curPtsMs);
jsonObject.put("duration", durationMs);
mTRTCCloud.sendSEIMsg(jsonObject.toString().getBytes(), 1);
} catch (JSONException e) {
e.printStackTrace();
}
}
@Override
public void onComplete(int id, int errCode) {
// 音楽再生完了
}
});
注意:
BGMを再生する前に、このインターフェースを使用して再生イベントのコールバックを設定し、BGMの再生進行状況を把握してください。
歌い手がSEIメッセージを送信する頻度はイベントコールバックの頻度によって決まります。ここでは、getMusicCurrentPosInMSを使用して再生進行状況を定期的に同期することもできます。
10. マイクオフして退室
// サブインスタンスが合唱モードの実験的インターフェースを停止
subCloud.callExperimentalAPI(
"{\\"api\\":\\"enableChorus\\",\\"params\\":{\\"enable\\":false,\\"audioSource\\":1}}");
// サブインスタンスが低遅延モードの実験的インターフェースを停止
subCloud.callExperimentalAPI(
"{\\"api\\":\\"setLowLatencyModeEnabled\\",\\"params\\":{\\"enable\\":false}}");
// サブインスタンスが視聴者ロールに切り替わる
subCloud.switchRole(TRTCCloudDef.TRTCRoleAudience);
// サブインスタンスが伴奏の再生を停止
subCloud.getAudioEffectManager().stopPlayMusic(musicId);
// サブインスタンスがルームから退出
subCloud.exitRoom();

// マスターインスタンスがブラックフレーム挿入の実験的インターフェースを停止
mTRTCCloud.callExperimentalAPI(
"{\\"api\\":\\"enableBlackStream\\",\\"params\\": {\\"enable\\":false}}");
// マスターインスタンスが合唱モードの実験的インターフェースを停止
mTRTCCloud.callExperimentalAPI(
"{\\"api\\":\\"enableChorus\\",\\"params\\":{\\"enable\\":false,\\"audioSource\\":0}}");
// マスターインスタンスが低遅延モードの実験的インターフェイスを停止
mTRTCCloud.callExperimentalAPI(
"{\\"api\\":\\"setLowLatencyModeEnabled\\",\\"params\\":{\\"enable\\":false}}");
// マスターインスタンスが視聴者ロールに切り替わる
mTRTCCloud.switchRole(TRTCCloudDef.TRTCRoleAudience);
// マスターインスタンスがローカルオーディオのキャプチャーとパブリッシュを停止
mTRTCCloud.stopLocalAudio();
// マスターインスタンスがルームから退出
mTRTCCloud.exitRoom();

視点2:合唱のアクション

タイムライン図




1. ルームに入る
public void enterRoom(String roomId, String userId) {
TRTCCloudDef.TRTCParams params = new TRTCCloudDef.TRTCParams();
// 文字列のルーム番号を例に
params.strRoomId = roomId;
params.userId = userId;
// 業務バックエンドから取得したUserSig
params.userSig = getUserSig(userId);
// 自分のSDKAppIDに置き換える
params.sdkAppId = SDKAppID;
// サンプルは視聴者ロールとして入室
params.role = TRTCCloudDef.TRTCRoleAudience;
// 入室シナリオはLIVEを選択
mTRTCCloud.enterRoom(params, TRTCCloudDef.TRTC_APP_SCENE_LIVE);
}

// 入室結果イベントコールバック
@Override
public void onEnterRoom(long result) {
if (result > 0) {
// resultは入室にかかった時間(ミリ秒)
Log.d(TAG, "Enter room succeed");
} else {
// result入室失敗のエラーコード
Log.d(TAG, "Enter room failed");
}
}
2. マイクオンプッシュ
// アンカーロールに切り替え
mTRTCCloud.switchRole(TRTCCloudDef.TRTCRoleAnchor);

// ロール切り替えイベントコールバック
@Override
public void onSwitchRole(int errCode, String errMsg) {
if (errCode == TXLiteAVCode.ERR_NULL) {
// リードシンガーのサブインスタンスがパブリッシュする音楽ストリームの購読を解除
mTRTCCloud.muteRemoteAudio(mBgmUserId, true);
// 合唱モードの実験的インターフェースを開始
mTRTCCloud.callExperimentalAPI(
"{\\"api\\":\\"enableChorus\\",\\"params\\":{\\"enable\\":true,\\"audioSource\\":0}}");
// 低遅延モードの実験的インターフェースを開始
mTRTCCloud.callExperimentalAPI(
"{\\"api\\":\\"setLowLatencyModeEnabled\\",\\"params\\":{\\"enable\\":true}}");
// メディア音量タイプの設定
mTRTCCloud.setSystemVolumeType(TRTCCloudDef.TRTCSystemVolumeTypeMedia);
// ローカルオーディオストリームを配布し、音質を設定する
mTRTCCloud.startLocalAudio(TRTCCloudDef.TRTC_AUDIO_QUALITY_MUSIC);
}
}
注意:
遅延をできるだけ低減するために、合唱者は伴奏をローカルで再生するため、リードシンガーがパブリッシュする音楽ストリームの購読を解除する必要があります。
合唱者も実験的なインターフェースを使用して合唱モードと低遅延モードを有効にする必要があり、合唱体験を最適化します。
カラオケシナリオでは、全過程のメディア音量とMusic音質を設定することをお勧めします。これにより、高音質の聴覚体験を得ることができます。
3. NTP時刻同期
TXLiveBase.setListener(new TXLiveBaseListener() {
@Override
public void onUpdateNetworkTime(int errCode, String errMsg) {
super.onUpdateNetworkTime(errCode, errMsg);
// errCode 0: 時刻同期成功、偏差は30ms以内;1: 時刻同期成功、しかし偏差は30ms以上の可能性あり;-1: 時刻同期失敗
if (errCode == 0) {
// 時刻同期成功、NTPタイムスタンプを取得
long ntpTime = TXLiveBase.getNetworkTimestamp();
} else {
// 時刻同期に失敗しました。時刻同期を再試行してください
TXLiveBase.updateNetworkTime();
}
}
});

TXLiveBase.updateNetworkTime();
注意:
NTP時刻同期の結果は、ユーザーの現在のネットワーク品質を反映することができます。良好な合唱体験を保証するために、時刻同期に失敗した場合はユーザーが合唱に参加することを許可しないことをお勧めします。
4. 合唱シグナルの受信
@Override
public void onRecvCustomCmdMsg(String userId, int cmdID, int seq, byte[] message) {
try {
JSONObject json = new JSONObject(new String(message, "UTF-8"));
// 合唱シグナルのマッチング
if (json.getString("cmd").equals("startChorus")) {
long startPlayMusicTs = json.getLong("startPlayMusicTS");
int musicId = json.getInt("musicId");
long musicDuration = json.getLong("musicDuration");
// 約束合唱時間と現在の時間との差
long delayMs = startPlayMusicTs - TXLiveBase.getNetworkTimestamp();
}
} catch (JSONException e) {
e.printStackTrace();
}
}
注意:
合唱者が合唱シグナルを受信し、合唱に参加した後、状態は「合唱中」に変わり、このラウンドの合唱が終了するまで合唱シグナルに再度応答しません。
5. 伴奏を再生し、合唱を始めます。
if (delayMs > 0) { // 合唱未開始
// 音楽のプリロードを開始
preloadMusic(musicId, 0L);
// delayMs遅延後に音楽の再生を開始
startPlayMusic(musicId, 0L);
} else if (Math.abs(delayMs) < musicDuration) { // 合唱進行中
// 再生開始時間: 時間差の絶対値+プリロード遅延(例:400ms)
long startTimeMS = Math.abs(delayMs) + 400;
// 音楽のプリロードを開始
preloadMusic(musicId, startTimeMS);
// プリロード遅延(例:400ms)後に音楽の再生を開始
startPlayMusic(musicId, startTimeMS);
} else { // 合唱終了
// 合唱への参加は許可されない
}

// 音楽をプリロードする
public void preloadMusic(int musicId, long startTimeMS) {
// musicId: 合唱シグナルから取得。musicUrl: 対応する音楽リソースのアドレス
TXAudioEffectManager.AudioMusicParam musicParam = new
TXAudioEffectManager.AudioMusicParam(musicId, musicUrl);
// 音楽をローカルでのみ再生
musicParam.publish = false;
// 音楽再生を開始するタイムスタンプ(ミリ秒)
musicParam.startTimeMS = startTimeMS;
mTRTCCloud.getAudioEffectManager().preloadMusic(musicParam);
}

// 音楽の再生を開始
public void startPlayMusic(int musicId, long startTimeMS) {
// musicId: 合唱シグナルから取得。musicUrl: 対応する音楽リソースのアドレス
TXAudioEffectManager.AudioMusicParam musicParam = new
TXAudioEffectManager.AudioMusicParam(musicId, musicUrl);
// 音楽をローカルでのみ再生
musicParam.publish = false;
// 音楽再生を開始するタイムスタンプ(ミリ秒)
musicParam.startTimeMS = startTimeMS;
mTRTCCloud.getAudioEffectManager().startPlayMusic(musicParam);
}
注意:
できるだけ伝送遅延を低減するために、合唱者はローカルで再生される伴奏に合わせて歌い、リモートの音楽をパブリッシュおよび受信する必要はありません。
delayMsに基づいて、現在の合唱状態を判断できます。異なる状態のstartPlayMusic遅延呼び出しは、開発者が自分で実装する必要があります。
6. 伴奏同期
// 約束の合唱開始時間
long mStartPlayMusicTs = jsonObject.getLong("startPlayMusicTS");
// 現在伴奏の実際の再生進行状況
long currentProgress = mTRTCCloud.getAudioEffectManager().getMusicCurrentPosInMS(musicId);
// 現在伴奏の理想的な再生進行状況
long estimatedProgress = TXLiveBase.getNetworkTimestamp() - mStartPlayMusicTs;
// 進捗差が50msを超えた場合、修正を行う
if (estimatedProgress >= 0 && Math.abs(currentProgress - estimatedProgress) > 50) {
mTRTCCloud.getAudioEffectManager().seekMusicToPosInMS(musicId, (int) estimatedProgress);
}
7. 歌詞同期
歌詞をダウンロード
業務バックエンドから目標の歌詞のダウンロードリンクLyricsUrlを取得し、目標の歌詞をローカルにキャッシュします。
ローカル歌詞同期
mTXAudioEffectManager.setMusicObserver(musicId, new TXAudioEffectManager.TXMusicPlayObserver() {
@Override
public void onStart(int id, int errCode) {
// 音楽再生を開始
}
@Override
public void onPlayProgress(int id, long curPtsMs, long durationMs) {
// TODO歌詞コントロールロジックの更新:
// 最新の進行状況とローカルの歌詞進行の誤差に基づいて、seek歌詞コントロールが必要があるかどうかを判断

}
@Override
public void onComplete(int id, int errCode) {
// 音楽再生完了
}
});
注意:
BGMを再生する前に、このインターフェースを使用して再生イベントのコールバックを設定し、BGMの再生進行状況を把握してください。
8. マイクオフして退室
// 合唱モードの実験的インターフェースを停止
mTRTCCloud.callExperimentalAPI(
"{\\"api\\":\\"enableChorus\\",\\"params\\":{\\"enable\\":false,\\"audioSource\\":0}}");
// 低遅延モードの実験的インターフェースを停止
mTRTCCloud.callExperimentalAPI(
"{\\"api\\":\\"setLowLatencyModeEnabled\\",\\"params\\":{\\"enable\\":false}}");
// 視聴者ロールに切り替える
mTRTCCloud.switchRole(TRTCCloudDef.TRTCRoleAudience);
// 伴奏の再生を停止
mTRTCCloud.getAudioEffectManager().stopPlayMusic(musicId);
// ローカルオーディオのキャプチャーとパブリッシュを停止
mTRTCCloud.stopLocalAudio();
// ルームから退出
mTRTCCloud.exitRoom();

視点3:聞き手のアクション

タイムライン図




1. ルームに入る
public void enterRoom(String roomId, String userId) {
TRTCCloudDef.TRTCParams params = new TRTCCloudDef.TRTCParams();
// 文字列のルーム番号を例に
params.strRoomId = roomId;
params.userId = userId;
// 業務バックエンドから取得したUserSig
params.userSig = getUserSig(userId);
// 自分のSDKAppIDに置き換える
params.sdkAppId = SDKAppID;
// すべて視聴者ロールで入室する事がお勧め
params.role = TRTCCloudDef.TRTCRoleAudience;
// 入室シナリオはLIVEを選択
mTRTCCloud.enterRoom(params, TRTCCloudDef.TRTC_APP_SCENE_LIVE);
}

// 入室結果イベントコールバック
@Override
public void onEnterRoom(long result) {
if (result > 0) {
// resultは入室にかかった時間(ミリ秒)
Log.d(TAG, "Enter room succeed");
} else {
// result入室失敗のエラーコード
Log.d(TAG, "Enter room failed");
}
}
注意:
歌詞同期用のSEIメッセージをより効果的にパススルーするために、入室シナリオではTRTC_APP_SCENE_LIVEを選択することをお勧めします。
自動購読モード(デフォルト)では、視聴者が入室すると、自動的にマイクオンアンカーのオーディオストリームを購読して再生します。
2. 歌詞同期
歌詞をダウンロード
業務バックエンドから目標の歌詞のダウンロードリンクLyricsUrlを取得し、目標の歌詞をローカルにキャッシュします。
聞き手側の歌詞同期
@Override
public void onUserVideoAvailable(String userId, boolean available) {
if (available) {
mTRTCCloud.startRemoteView(userId, null);
} else {
mTRTCCloud.stopRemoteView(userId);
}
}

@Override
public void onRecvSEIMsg(String userId, byte[] data) {
String result = new String(data);
try {
JSONObject jsonObject = new JSONObject(result);
int musicId = jsonObject.getInt("musicId");
long progress = jsonObject.getLong("progress");
long duration = jsonObject.getLong("duration");
} catch (JSONException e) {
e.printStackTrace();
}
...
// TODO歌詞コントロールロジックの更新:
// 受信した最新の進行状況とローカルの歌詞進行の誤差に基づいて、歌詞コントロールがseekする必要があるかどうかを判断します。
...
}
注意:
聞き手は、ブラックフレームに含まれるSEIメッセージを受信するために、リードシンガーのビデオストリームを自分から購読する必要があります。
リードシンガーのストリームミックスにブラックフレームが同時に挿入される場合、ここではミキシングロボットのビデオストリームを購読するだけで済みます。
3. ルームから退出
// ルームから退出
mTRTCCloud.exitRoom();

// 退室イベントコールバック
@Override
public void onExitRoom(int reason) {
if (reason == 0) {
Log.d(TAG, "exitRoomアクティブコールでルーム退出します");
} else if (reason == 1) {
Log.d(TAG, "現在のルームからサーバーによってキックされました");
} else if (reason == 2) {
Log.d(TAG, "現在のルームは解散されました");
}
}

高度機能

音楽スコアリングモジュールアクセス

音楽スコアリングはユーザーにマルチディメンションの歌唱採点能力を提供し、現在サポートされている採点ディメンションには、音の正確さとリズムが含まれます。
1. 評価関連ドキュメントの準備
採点するための歌唱録音ファイル、オリジナルの音楽標準ファイル、MIDI楽譜ファイルなどを事前に準備し、それらをCOSストレージにアップロードしてください。
2. 音楽スコアリングタスクを作成
リクエスト方法:POST(HTTP)
リクエストアドレス:https://api.mediax.tencent.com/job
リクエストヘッダー:Content-Type: application/json
リクエストサンプル:
リクエストサンプル
レスポンスサンプル
{
"action": "CreateJob",
"secretId": "{secretId}",
"secretKey": "{secretKey}",
"createJobRequest": {
"customId": "{customId}",
"callback": "{callback}",
"inputs": [{ "url": "{url}" }],
"outputs": [
{
"inputSelectors": [0],
"smartContentDescriptor": {
"outputPrefix": "{outputPrefix}",
"vocalScore": {
"standardAudio": {
"midi": {"url":"{url}"},
"standardWav": {"url":"{url}"},
"alignWav": {"url":"{url}"}
}
}
}
}
]
}
}
{
"requestId": "ac004192-110b-46e3-ade8-4e449df84d60",
"createJobResponse": {
"job": {
"id": "13f342e4-6866-450e-b44e-3151431c578b",
"state": 1,
"customId": "{customId}",
"callback": "{callback}",
"inputs": [ { "url": "{url}" } ],
"outputs": [
{
"inputSelectors": [ 0 ],
"smartContentDescriptor": {
"outputPrefix": "{outputPrefix}",
"vocalScore": {
"standardAudio": {
"midi": {"url":"{url}"},
"standardWav": {"url":"{url}"},
"alignWav": {"url":"{url}"}
}
}
}
}
],
"timing": {
"createdAt": "1603432763000",
"startedAt": "0",
"completedAt": "0"
}
}
}
}
3. 音楽スコアリングの結果を取得
取得方法:アクティブ取得とパッシブコールバックに分かれます。
タスクを作成した後のレスポンスパケット内のidクエリを通じて、もしクエリしたタスクが成功した場合(state=3)、タスクのOutputにはsmartContentResult構造体が含まれ、その中のvocalScoreフィールドに結果jsonファイル名が保存されます。ユーザーはOutput内のcosおよびdestination情報を基に、出力ファイルのcosパスを自分で結合することができます。
リクエストサンプル
レスポンスサンプル
{
"action": "GetJob",
"secretId": "{secretId}",
"secretKey": "{secretKey}",
"getJobRequest": {
"id": "{id}"
}
}
{
"requestId": "c9845a99-34e3-4b0f-80f5-f0a2a0ee8896",
"getJobResponse": {
"job": {
"id": "a95e9d74-6602-4405-a3fc-6408a76bcc98",
"state": 3,
"customId": "{customId}",
"callback": "{callback}",
"timing": {
"createdAt": "1610513575000",
"startedAt": "1610513575000",
"completedAt": "1610513618000"
},
"inputs": [{ "url": "{url}" }],
"outputs": [
{
"contentId": "{contentId}",
"destination": "{destination}",
"inputSelectors": [0],
"smartContentDescriptor": {
"outputPrefix": "{outputPrefix}",
"vocalScore": {
"standardAudio": {
"midi": {"url":"{url}"},
"standardWav": {"url":"{url}"},
"alignWav": {"url":"{url}"}
}
}
},
"smartContentResult": {
"vocalScore": "out.json"
}
}
]
}
}
}
パッシブコールバックは、タスクを作成する際にcallbackフィールドに記入する必要があります。プラットフォームは、タスクが完了状態(COMPLETED/ERROR)に入ると、Job構造体をcallbackで指定したアドレスに送信します。プラットフォーム側は、タスク結果を取得するためにパッシブコールバックの使用を推奨します。完了状態(COMPLETED/ERROR)に入ったタスクの全Job構造体を、タスク作成時に指定されたcallbackフィールドに対応するアドレスに送信します。Job構造体は、アクティブクエリの例(getJobResponse以下)に記載されています。
説明:
より詳細なスマートミュージックソリューションの音楽スコアリングモジュールのアクセス説明については、音楽スコアリングアクセスを参照してください。

ストリームミックスパススルーシングルストリームの音量

ミキシングを有効にした後、聞き手はマイクオンアンカーのシングルストリーム音量を直接取得することができません。この時、ルームマスターがマイクオンアンカー全員のコールバック音量値をSEIを通じて送信する方法でシングルストリームの音量をパススルーできます。
@Override
public void onUserVoiceVolume(ArrayList<TRTCCloudDef.TRTCVolumeInfo> userVolumes, int totalVolume) {
super.onUserVoiceVolume(userVolumes, totalVolume);
if (userVolumes != null && userVolumes.size() > 0) {
// マイクオンユーザーに対応する音量値を保存するために使用
HashMap<String, Integer> volumesMap = new HashMap<>();
for (TRTCCloudDef.TRTCVolumeInfo user : userVolumes) {
// 適切な音量のしきい値を設定できます
if (user.volume > 10) {
volumesMap.put(user.userId, user.volume);
}
}
Gson gson = new Gson();
String body = gson.toJson(volumesMap);
// SEIメッセージを通じてマイクオンユーザーの音量セットを送信
mTRTCCloud.sendSEIMsg(body.getBytes(), 1);
}
}

@Override
public void onRecvSEIMsg(String userId, byte[] data) {
Gson gson = new Gson();
HashMap<String, Integer> volumesMap = new HashMap<>();
try {
String message = new String(data, "UTF-8");
volumesMap = gson.fromJson(message, volumesMap.getClass());
for (String userId : volumesMap.keySet()) {
// すべてのマイクオンユーザーのシングルストリームの音量をプリント
Log.i(userId, String.valueOf(volumesMap.get(userId)));
}
} catch (UnsupportedEncodingException e) {
e.printStackTrace();
}
}
注意:
SEIメッセージでストリームミックスからパススルーでシングルストリームの音量を得るには、ルームマスターがビデオストリームをプッシュしているか、またはブラックフレーム挿入を有効にしていること、そして聞き手がルームマスターのビデオストリームを自分から購読する必要があります。

ネットワーク品質リアルタイムコールバック

ローカルおよびリモートユーザーのネットワーク品質をリアルタイムで統計するために、onNetworkQualityをリスニングすることができます。このコールバックは2秒ごとに一度発生します。
private class TRTCCloudImplListener extends TRTCCloudListener {
@Override
public void onNetworkQuality(TRTCCloudDef.TRTCQuality localQuality,
ArrayList<TRTCCloudDef.TRTCQuality> remoteQuality) {
// localQuality userIdは空、ローカルユーザーのネットワーク品質評価結果を表します
// remoteQualityは、リモートユーザーのネットワーク品質評価結果を表しており、その結果はリモートとローカルの両方の影響を受けます。
switch (localQuality.quality) {
case TRTCCloudDef.TRTC_QUALITY_Excellent:
Log.i(TAG, "現在のネットワークは非常に良い");
break;
case TRTCCloudDef.TRTC_QUALITY_Good:
Log.i(TAG, "現在のネットワークは比較的良い");
break;
case TRTCCloudDef.TRTC_QUALITY_Poor:
Log.i(TAG, "現在のネットワークは普通");
break;
case TRTCCloudDef.TRTC_QUALITY_Bad:
Log.i(TAG, "現在のネットワークが不安定");
break;
case TRTCCloudDef.TRTC_QUALITY_Vbad:
Log.i(TAG, "現在のネットワークが非常に悪い");
break;
case TRTCCloudDef.TRTC_QUALITY_Down:
Log.i(TAG, "現在のネットワークはTRTC最低要件を満たしていない");
break;
default:
Log.i(TAG, "未定義");
break;
}
}
}

高度な権限制御

TRTC高度な権限制御を使用して、異なるルームに異なるアクセス権を設定できます。例えば、高級VIPルームなど。また、聞き手のマイクオン権限を制御するためにも使用できます。例えば、ゴーストマイクの処理など。
ステップ1:TRTCコントロールパネルアプリケーションの高度な機能ページで、高度な権限制御スイッチをオンにします。



注意:
特定のSDKAppIDが高度な権限制御を有効にした後、そのSDKAppIDを使用する入室するすべてのユーザーはTRTCParamsprivateMapKeyパラメータを渡す必要があります。したがって、このSDKAppIDをオンラインで使用しているユーザーがいる場合、この機能を安易には有効にしないでください。
ステップ2:業務バックエンドでPrivateMapKeyを生成し、コード例はPrivateMapKey計算ソースコードを参照してください。
ステップ3:PrivateMapKeyの入室検証&マイクオン検証。
入室検証
TRTCCloudDef.TRTCParams mTRTCParams = new TRTCCloudDef.TRTCParams();
mTRTCParams.sdkAppId = SDKAPPID;
mTRTCParams.userId = mUserId;
mTRTCParams.strRoomId = mRoomId;
// 業務バックエンドから取得したUserSig
mTRTCParams.userSig = getUserSig();
// 業務バックエンドから取得したPrivateMapKey
mTRTCParams.privateMapKey = getPrivateMapKey();
mTRTCParams.role = TRTCCloudDef.TRTCRoleAudience;
mTRTCCloud.enterRoom(mTRTCParams, TRTCCloudDef.TRTC_APP_SCENE_LIVE);
マイクオン検証
// 業務バックエンドから最新のPrivateMapKeyを取得し、ロール切り替えインターフェースに渡します。
mTRTCCloud.switchRole(TRTCCloudDef.TRTCRoleAnchor, getPrivateMapKey());

異常処理

異常エラー処理

TRTC SDK回復不可能なエラーが発生した場合、onErrorコールバックでスローされます。詳細はエラーコード表を参照してください。
1. UserSig関連
UserSig検証に失敗すると入室に失敗します。UserSigツールを使用して検証できます。
列挙値
取得値
説明
ERR_TRTC_INVALID_USER_SIG
-3320
入室パラメータuserSigが正しくありません。TRTCParams.userSigが空であるかどうかを確認してください。
ERR_TRTC_USER_SIG_CHECK_FAILED
-100018
UserSig検証失敗、パラメータTRTCParams.userSigが正しく入力されているか、または期限切れでないかを確認してください。
2. 入退室関連
入室に失敗した場合は、まず入室パラメータが正しいかどうかを確認してください。また、入退室インターフェースは必ずペアで呼び出す必要があります。入室に失敗した場合でも、退室インターフェースを呼び出す必要があります。
列挙値
取得値
説明
ERR_TRTC_CONNECT_SERVER_TIMEOUT
-3308
入室リクエストがタイムアウトしました。ネットワークが切断されているか、VPNが使用されているかを確認してください。また、4Gに切り替えてテストすることもできます。
ERR_TRTC_INVALID_SDK_APPID
-3317
入室パラメータsdkAppIdエラー。TRTCParams.sdkAppIdが空であるかどうか確認してください。
ERR_TRTC_INVALID_ROOM_ID
-3318
入室パラメータroomIdエラー。TRTCParams.roomIdまたはTRTCParams.strRoomIdが空であるかどうか確認してください。roomIdとstrRoomIdは混在できません。
ERR_TRTC_INVALID_USER_ID
-3319
入室パラメータuserIdが正しくありません。TRTCParams.userIdが空であるかどうかを確認してください。
ERR_TRTC_ENTER_ROOM_REFUSED
-3340
入室リクエストが拒否されました。enterRoomで同じIdのルームに連続して入室しようとしていないか確認してください。
3. デバイス関連
デバイス関連のエラーをリスニングし、関連するエラーが発生した場合にUIでユーザーに通知します。
列挙値
取得値
説明
ERR_MIC_START_FAIL
-1302
マイクの起動に失敗しました。例えば、WindowsまたはMacデバイスで、マイクの設定プログラム(ドライバー)に異常があります。デバイスを無効にしてから再度有効にするか、マシンを再起動するか、設定プログラムを更新してください。
ERR_SPEAKER_START_FAIL
-1321
スピーカーの起動に失敗しました。例えば、WindowsやMacのデバイスで、スピーカーの設定プログラム(ドライバー)に異常があります。デバイスを無効にしてから再度有効にするか、マシンを再起動するか、設定プログラムを更新してください。
ERR_MIC_OCCUPY
-1319
マイクが使用中です。たとえば、モバイルデバイスが通話中の場合、マイクを開くと失敗します。

イヤーモニター関連の問題

1. イヤーモニター機能の開始方法とイヤーモニター音量の設定方法
// イヤーモニターを有効にする
mTRTCCloud.getAudioEffectManager().enableVoiceEarMonitor(true);
// イヤーモニター音量の設定
mTRTCCloud.getAudioEffectManager().setVoiceEarMonitorVolume(int volume);
注意:
事前にイヤーモニターを設定でき、オーディオルーティングの変更をリスニングする必要はありません。ヘッドフォンを接続すると、イヤーモニター機能が自動的に有効になります。
2. イヤーモニター機能をオンにしても有効にならない
Bluetoothヘッドセットのハードウェア遅延が非常に高いため、ユーザーインターフェース上でアンカーに有線ヘッドセットの着用を促すようにしてください。また、すべてのスマートフォンがこの機能を有効にした後に優れたイヤーモニター効果を達成できるわけではないことにも注意が必要です。TRTC SDKはイヤーモニター効果が悪い一部のスマートフォンに大して、この機能をブロックしています。
3. イヤーモニターの遅延が高すぎる
Bluetoothヘッドセットを使用しているかどうかを確認してください。Bluetoothヘッドセットのハードウェア遅延は非常に高いため、可能な限り有線ヘッドセットの使用をお勧めします。また、実験的インターフェースsetSystemAudioKitEnabledを通じてハードウェアイヤーモニターを有効にすることで、イヤーモニターの遅延が過度に高い問題を改善することができます。ハードウェアイヤーモニターはパフォーマンスが良く、遅延が少ないですが、ソフトウェアイヤーモニターは遅延が高いものの、互換性が良いです。現在、HuaweiおよびVIVOデバイスについては、SDKはデフォルトでハードウェアイヤーモニターを使用していますが、他のデバイスはデフォルトでソフトウェアイヤーモニターを使用しています。ハードウェアイヤーモニターに互換性の問題がある場合は、お問い合わせにてソフトウェアイヤーモニターを強制的に使用するよう設定することができます。

NTP時刻同期の問題

1. NTP time sync finished, but result maybe inaccurate
NTP時刻同期に成功したけど、偏差が30ms以上の可能性があります。これはクライアントのネットワーク環境が悪いことを示しています。rttが継続的にジッタしています。
2. Error in AddressResolver: No address associated with hostname
NTP時刻同期に失敗しました。現在のネットワーク環境でローカルの運営業者DNSの解析が一時的に異常となっている可能性があります。しばらくしてから再試行してください。
3. NTPサービスリトライ処理ロジック




音楽再生リソースパスの問題

カラオケシナリオで使用するTRTC SDK伴奏の再生では、ローカルまたはネットワークの音楽リソースを選択して再生できます。再生パスは現在、ネットワークリソースURL、デバイスの外部ストレージ、およびアプリのプライベートディレクトリ内の音楽ファイルの絶対パスのみをサポートしており、Android開発中のassetsなどのディレクトリ内のファイルパスはサポートしていません。
この問題を回避する方法として、assetsディレクトリ内のリソースファイルを事前にデバイスの外部ストレージまたはアプリのプライベートディレクトリにコピーすることができます。サンプルコードは以下の通りです。
public static void copyAssetsToFile(Context context, String name) {
// アプリケーション自体のディレクトリ内のfilesディレクトリ
String savePath = ContextCompat.getExternalFilesDirs(context, null)[0].getAbsolutePath();
// アプリケーション自体のディレクトリ内のcacheディレクトリ
// String savePath = getApplication().getExternalCacheDir().getAbsolutePath();
// アプリケーションのプライベートストレージディレクトリ内のfilesディレクトリ
// String savePath = getApplication().getFilesDir().getAbsolutePath();
String filename = savePath + "/" + name;
File dir = new File(savePath);
// ディレクトリが存在しない場合、このディレクトリを作成
if (!dir.exists()) {
dir.mkdir();
}
try {
if (!(new File(filename)).exists()) {
InputStream is = context.getResources().getAssets().open(name);
FileOutputStream fos = new FileOutputStream(filename);
byte[] buffer = new byte[1024];
int count = 0;
while ((count = is.read(buffer)) > 0) {
fos.write(buffer, 0, count);
}
fos.close();
is.close();
}
} catch (Exception e) {
e.printStackTrace();
}
}
外部ストレージを使用するfilesディレクトリパス:/storage/emulated/0/Android/data/<package_name>/files/<file_name>
外部ストレージを使用するcacheディレクトリパス:/storage/emulated/0/Android/data/<package_name>/cache/<file_name>
プライベートストレージを適用filesディレクトリパス:/data/user/0/<package_name>/files/<file_name>
注意:
もし渡されたパスがアプリケーション自体の特定ディレクトリ以外の他の外部ストレージパスである場合、Android 10以降のデバイスでは、Googleに新しいストレージ管理システム、パーティションストレージが導入されたため、アクセス拒否される可能性があります。AndroidManifest.xmlファイルの<application>タグ内に以下のコードを追加することで、一時的に回避することができます:android:requestLegacyExternalStorage="true"。この属性はtargetSdkVersionが29(Android 10)のアプリケーションでのみ有効で、より高いバージョンのtargetSdkVersionのアプリケーションでは、アプリのプライベートまたは外部ストレージパスの使用を引き続き推奨します。
TRTC SDK 11.5以上のバージョンでは、Content ProviderコンポーネントのContent URIを使用して、Androidデバイス上のローカル音楽リソースを再生することがサポートされています。
Android 11およびHarmonyOS 3.0以上のシステムでは、外部ストレージディレクトリのリソースファイルにアクセスできない場合、MANAGE_EXTERNAL_STORAGE権限の申請が必要です。
まず、アプリケーションのAndroidManifestファイルに以下のエントリを追加する必要があります。
<manifest ...>
<!-- This is the permission itself -->
<uses-permission android:name="android.permission.MANAGE_EXTERNAL_STORAGE" />

<application ...>
...
</application>
</manifest>
その後、アプリがこの権限を使用する必要がある箇所で、ユーザーに手動での承認を促してください。
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) {
if (!Environment.isExternalStorageManager()) {
Intent intent = new Intent(Settings.ACTION_MANAGE_APP_ALL_FILES_ACCESS_PERMISSION);
Uri uri = Uri.fromParts("package", getPackageName(), null);
intent.setData(uri);
startActivity(intent);
}
} else {
// For Android versions less than Android 11, you can use the old permissions model
ActivityCompat.requestPermissions(this, new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE}, REQUEST_CODE);
}

リアルタイム合唱関連の使用法に関する問題

1. リアルタイム合唱シナリオで、なぜリードシンガーはダブルインスタンスでプッシュする必要がある?
リアルタイム合唱シナリオでは、エンドツーエンドの遅延をできるだけ低減し、ボーカルと伴奏を同期させるために、通常、リードシンガー側は、ボーカルと伴奏をそれぞれアップストリームするためのダブルインスタンスを使用し、他の合唱側はボーカルのみをアップストリーム、伴奏はローカルで再生する方法が採用されます。この場合、合唱側はリードシンガーストリームを購読する必要があり、同時にリードシンガー音楽ストリームは購読しないようにします。これにより、ダブルインスタンスが分離してプッシュすることが実現できます。
2. リアルタイム合唱シナリオでなぜオーディオ・ビデオストリームプッシュバックを有効にすることを推奨する?
聞き手側が同時に複数のシングルストリームをプルすると、複数のボーカルストリームと伴奏ストリームが同期しない可能性が高くなりますが、ストリームミックスをプルすることで、すべてのストリームの完全な同期を保証し、同時にダウンストリームの帯域幅を低減することができます。
3. リアルタイム合唱シナリオで、SEIの用途は?
伴奏の進行を伝達し、聞き手側の歌詞同期に使用します。
ストリームミックスパススルーシングルストリーム音量、聞き手側でのオシロスコープ表示に使用します。
4. 伴奏のロードに時間がかかり、大きな再生遅延がある時?
SDKがネットワーク音楽リソースの読み込みには時間がかかるため、再生開始前に音楽のプリロードをお勧めします。
mTRTCCloud.getAudioEffectManager().preloadMusic(musicParam);
5. 伴奏を再生する際にボーカルが聞こえない、音楽がボーカルを圧倒している?
デフォルトの音量を使用すると伴奏がボーカルを圧倒する場合がありますので、伴奏とボーカルの音量のバランスを適切に調整することをお勧めします。
// BGMのローカル再生ボリュームの設定
mTRTCCloud.getAudioEffectManager().setMusicPlayoutVolume(musicID, volume);
// BGMのリモート再生ボリュームの設定
mTRTCCloud.getAudioEffectManager().setMusicPublishVolume(musicID, volume);
// すべてのBGMのローカルとリモート音量の設定
mTRTCCloud.getAudioEffectManager().setAllMusicVolume(volume);
// ボーカルのキャプチャーボリュームの設定
mTRTCCloud.getAudioEffectManager().setVoiceCaptureVolume(volume);


ヘルプとサポート

この記事はお役に立ちましたか?

フィードバック