In real-time audio and video sessions, playing an audio effect or adding background music adds more fun to use cases such as online gaming or karaoke.
This page shows how to implement audio effects or play music files in your project.
Agora provides the following APIs that help you implement playing audio effects or music files:
Audio Effect APIs: Play ambient sounds with a short duration. For example, applause, cheers, fighting sounds, and gunshots. You can play multiple audio effect files at the same time.
Audio Mixing APIs: Play a relatively long music file, such as the accompaniment and background music. You can play only one music file at a time.
These methods support the following functions:
Function | Audio Effect APIs | Audio Mixing APIs |
---|---|---|
Play and stop playing the specified audio file |
|
|
Pause and resume playing the audio file |
|
|
Get and set the playback position and volume |
|
|
Report the playback state of the audio file | onAudioEffectFinished |
onAudioMixingStateChanged |
Before proceeding, ensure that you have the following:
This section explains how to use the audio effect APIs and audio mixing APIs to implment playing audio effect and audio mixing files in your project.
Call the preloadEffect
method before joining channel to preload the audio effect file. After joining the channel, call playEffect
to play the specified audio effect. To play multiple audio effect files at the same time, call playEffect
multiple times and specify different audio effect files with the effect ID. Once the audio effect playback finishes, the SDK triggers the onAudioEffectFinished
callback.
In your Agora project, open the file used to manage audio effect playback and add the following code.
void CAgoraEffectDlg::OnBnClickedButtonPreload()
{
if (m_cmbEffect.GetCurSel() < 0)
{
return;
}
CString strEffect;
m_cmbEffect.GetWindowText(strEffect);
std::string strPath = cs2utf8(strEffect);
// Preload the effect file
m_rtcEngine->preloadEffect(m_mapEffect[strEffect], strPath.c_str());
CString strInfo;
strInfo.Format(_T("preload effect :path:%s"), strEffect);
m_lstInfo.InsertString(m_lstInfo.GetCount(), strInfo);
}
void CAgoraEffectDlg::OnBnClickedButtonUnloadEffect()
{
if (m_cmbEffect.GetCurSel() < 0)
{
return;
}
CString strEffect;
m_cmbEffect.GetWindowText(strEffect);
// Unload the effect file
m_rtcEngine->unloadEffect(m_mapEffect[strEffect]);
CString strInfo;
strInfo.Format(_T("unload effect :path:%s"), strEffect);
m_lstInfo.InsertString(m_lstInfo.GetCount(), strInfo);
}
void CAgoraEffectDlg::OnBnClickedButtonPauseEffect()
{
if (m_cmbEffect.GetCurSel() < 0)
{
return;
}
CString strEffect;
m_cmbEffect.GetWindowText(strEffect);
// Pause playing the effect file
m_rtcEngine->pauseEffect(m_mapEffect[strEffect]);
CString strInfo;
strInfo.Format(_T("pause effect :path:%s"), strEffect);
m_lstInfo.InsertString(m_lstInfo.GetCount(), strInfo);
}
void CAgoraEffectDlg::OnBnClickedButtonResumeEffect()
{
if (m_cmbEffect.GetCurSel() < 0)
{
return;
}
CString strEffect;
m_cmbEffect.GetWindowText(strEffect);
// Resume to play the effect file
int ret = m_rtcEngine->resumeEffect(m_mapEffect[strEffect]);
CString strInfo;
strInfo.Format(_T("resume effect ret:%d :path:%s"),ret, strEffect);
m_lstInfo.InsertString(m_lstInfo.GetCount(), strInfo);
}
void CAgoraEffectDlg::OnBnClickedButtonPlayEffect()
{
if (m_cmbEffect.GetCurSel() < 0)
{
return;
}
CString strEffect;
m_cmbEffect.GetWindowText(strEffect);
std::string strFile = cs2utf8(strEffect);
CString strLoops;
m_edtLoops.GetWindowText(strLoops);
int loops = _ttol(strLoops);
if (loops == 0) {
m_edtLoops.SetWindowText(_T("1"));
loops = 1;
}
CString strPitch;
m_edtPitch.GetWindowText(strPitch);
double pitch = _ttof(strPitch);
CString strGain;
m_edtGain.GetWindowText(strGain);
int gain = _ttol(strGain);
CString strPan;
m_cmbPan.GetWindowText(strPan);
double pan = _ttof(strPan);
BOOL publish = m_chkPublish.GetCheck();
// Play the effect file
int ret = m_rtcEngine->playEffect(m_mapEffect[strEffect], strFile.c_str(), loops, pitch, pan, gain, publish);
CString strInfo;
strInfo.Format(_T("play effect :path:%s, ret:%d"), strEffect, ret);
m_lstInfo.InsertString(m_lstInfo.GetCount(), strInfo);
strInfo.Format(_T("loops:%d,pitch:%.1f,pan:%.0f,gain:%d,publish:%d"),
loops, pitch, pan, gain, publish);
m_lstInfo.InsertString(m_lstInfo.GetCount(), strInfo);
}
void CAgoraEffectDlg::OnBnClickedButtonStopEffect()
{
if (m_cmbEffect.GetCurSel() < 0)
{
return;
}
CString strEffect;
m_cmbEffect.GetWindowText(strEffect);
// Stop playing the effect file
m_rtcEngine->stopEffect(m_mapEffect[strEffect]);
CString strInfo;
strInfo.Format(_T("stop effect :path:%s"), strEffect);
m_lstInfo.InsertString(m_lstInfo.GetCount(), strInfo);
}
void CAgoraEffectDlg::OnBnClickedButtonPauseAllEffect()
{
if (!m_pauseAll)
{
// Pause playing all effect files
m_rtcEngine->pauseAllEffects();
CString strInfo;
strInfo.Format(_T("pause All Effects"));
m_lstInfo.InsertString(m_lstInfo.GetCount(), strInfo);
m_btnPauseAll.SetWindowText(AudioEffectCtrlResumeEffect);
}
else {
// Resume to play all effect files
m_rtcEngine->resumeAllEffects();
CString strInfo;
strInfo.Format(_T("resume All Effects"));
m_lstInfo.InsertString(m_lstInfo.GetCount(), strInfo);
m_btnPauseAll.SetWindowText(AudioEffectCtrlPauseAllEffect);
}
m_pauseAll = !m_pauseAll;
}
void CAgoraEffectDlg::OnBnClickedButtonStopAllEffect2()
{
// Stop playing all effect files
m_rtcEngine->stopAllEffects();
CString strInfo;
strInfo.Format(_T("stop All Effects"));
m_lstInfo.InsertString(m_lstInfo.GetCount(), strInfo);
}
Call the startAudioMixing
method to play a music file before or after joining a channel. After a successful method call, the SDK triggers onAudioMixingStateChanged
callback when the audio mixing state changes. This callback also contains why the audio mixing state changes.
In your Agora project, open the file used to manage audio effect playback and add the following code.
void CAgoraAudioMixingDlg::OnBnClickedButtonSetAudioMix()
{
CString strPath;
m_edtAudioMix.GetWindowText(strPath);
strPath.Replace(_T("file\/files"), _T("file\/api\/download"));
std::string strAudioPath = cs2utf8(strPath);
strAudioPath = UrlANSI(strAudioPath.c_str());
BOOL bOnlyLocal = FALSE;
BOOL bReplaceMicroPhone = TRUE;
int iRepeatTimes = 1;
if (!m_audioMixing)
{
if (strAudioPath.empty())
{
AfxMessageBox(_T("audio path can not empty."));
return;
}
bOnlyLocal = m_chkOnlyLocal.GetCheck() ? TRUE : FALSE;
bReplaceMicroPhone = m_chkMicroPhone.GetCheck() ? TRUE : FALSE;
CString strTimes;
CString strInfo;
m_edtRepatTimes.GetWindowText(strTimes);
iRepeatTimes = _ttoi(strTimes);
if (iRepeatTimes == 0) {
iRepeatTimes = 1;
m_edtRepatTimes.SetWindowText(_T("1"));
}
// Start to play a music file
int nRet = m_rtcEngine->startAudioMixing(strAudioPath.c_str(),
bOnlyLocal,
bReplaceMicroPhone,
iRepeatTimes
);
strInfo.Format(_T("path:%s,\nonlyLocal:%s,\nReplaceMicroPhone:%s,\nRepeatTimes:%d"), strPath,
bOnlyLocal?_T("TRUE"):_T("FALSE"), bReplaceMicroPhone?_T("TRUE"):_T("FALSE"),
iRepeatTimes);
m_lstInfo.InsertString(m_lstInfo.GetCount(), strInfo);
strInfo.Format(_T("startAudioMixing,ret=%d"), nRet);
m_lstInfo.InsertString(m_lstInfo.GetCount(), strInfo);
m_btnSetAudioMix.SetWindowText(audioMixingCtrlUnSetAudioMixing);
m_chkMicroPhone.EnableWindow(FALSE);
m_chkOnlyLocal.EnableWindow(FALSE);
}
else {
// Stop playing the music file
m_rtcEngine->stopAudioMixing();
m_lstInfo.InsertString(m_lstInfo.GetCount(), _T("cancel audio mixing"));
m_btnSetAudioMix.SetWindowText(audioMixingCtrlSetAudioMixing);
m_chkOnlyLocal.EnableWindow(TRUE);
m_chkMicroPhone.EnableWindow(TRUE);
}
m_audioMixing = !m_audioMixing;
}
// Listen for the onAudioMixingStateChanged callback
void CAudioMixingEventHandler::onAudioMixingStateChanged(AUDIO_MIXING_STATE_TYPE state, AUDIO_MIXING_ERROR_TYPE errorCode)
{
if (m_hMsgHanlder) {
PAudioMixingState stateChanged = new AudioMixingState;
stateChanged->error = errorCode;
stateChanged->state = state;
::PostMessage(m_hMsgHanlder, WM_MSGID(EID_REMOTE_AUDIO_MIXING_STATE_CHANED), (WPARAM)stateChanged, 0);
}
}
This section provides reference information that you need to know when playing audio effects or implementing audio mixing.
Agora provides the following open-source sample projects on Github for your reference. You can download the project to try it or view the source code.