前回はエンジンとそこから出力を作成したので今回はそれらを利用してオーディオファイルの再生を行います。
まずオーディオを再生するためのプレイヤークラスを作成します。
class SoundPlayer
{
public:
enum tDevice
{
kDeviceAsset,
kDeviceSDCard:
};
SoundPlayer();
~SoundPlayer();
void Init(const char* file_name, tDevice device, SLEngineItf engine, SLObjectItf output_mix_object);
void Terminate();
void Play();
void Pause();
void Stop();
void Seek(float seek_second);
void Loop(bool loop);
float Volume()const;
void Volume(float volume);
void Mute(bool mute);
bool IsPlaying() const;
bool IsStopped() const;
bool IsPaused() const;
bool IsPlayend() const;
void Playend(bool playend);
private:
void CreatePlayerFromAsset(const cstr* file_name, SLEngineItf engine, SLObjectItf output_mix_object);
void CreatePlayerFromFile(const cstr* file_name, SLEngineItf engine, SLObjectItf output_mix_object);
static void PlayCallback(SLPlayItf caller, void *pContext, SLuint32 event);
SLObjectItf m_player_object;
SLPlayItf m_player_play;
SLSeekItf m_player_seek;
SLVolumeItf m_player_volume;
bool m_playend;
float m_volume;
};
/**
* @class SoundPlayer
* @brief オーディオ再生
*/
SoundPlayer::SoundPlayer()
: m_player_object(NULL)
, m_player_play(NULL)
, m_player_seek(NULL)
, m_player_volume(NULL)
, m_playend(false)
, m_volume(1.f)
{
}
SoundPlayer::SoundPlayer()
{
Terminate();
}
/**
* @brief チャンネルの初期化
* @param 読み込むファイル名
* @param 読み込み元デバイスの指定
* @param プレイヤーを生成するためにエンジンを渡す
* @param 出力先の指定
*/
void SoundPlayer::Init(const char* file_name, tDevice device, SLEngineItf engine, SLObjectItf output_mix_object)
{
SLresult result;
switch(device)
{
case kDeviceAsset:
CreatePlayerFromAsset(file_path, engine, output_mix_object);
break;
case kDeviceSDCard:
CreatePlayerFromFile(file_path, engine, output_mix_object);
break;
default:
break;
}
result = (*m_player_object)->Realize(m_player_object, SL_BOOLEAN_FALSE);
CheckError(result);
result = (*m_player_object)->GetInterface(m_player_object, SL_IID_PLAY, &m_player_play);
CheckError(result);
result = (*m_player_object)->GetInterface(m_player_object, SL_IID_SEEK, &m_player_seek);
CheckError(result);
result = (*m_player_object)->GetInterface(m_player_object, SL_IID_VOLUME, &m_player_volume);
CheckError(result);
result = (*m_player_play)->RegisterCallback(m_player_play, PlayCallback, this);
CheckError(result);
result = (*m_player_play)->SetCallbackEventsMask(m_player_play, SL_PLAYEVENT_HEADATEND);
CheckError(result);
}
void SoundPlayer::Terminate()
{
if (m_player_object != NULL)
{
(*m_player_object)->Destroy(m_player_object);
m_player_object = NULL;
m_player_play = NULL;
m_player_seek = NULL;
m_player_volume = NULL;
}
}
/**
* @brief シーク
* @param シーク位置(ミリ秒)
*/
void SoundPlayer::Seek(float seek_milli_second)
{
SLresult result;
ASSERT(NULL != m_player_play, "player is NULL.");
{
Playend(false);
result = (*m_player_seek)->SetPosition(m_player_seek, seek_milli_second, SL_SEEKMODE_FAST);
CheckError(result);
}
}
/**
* @brief ループの設定
* @param
* @return
*/
void SoundPlayer::Loop(bool loop)
{
SLresult result;
ASSERT(NULL != m_player_play, "player is NULL.");
{
result = (*m_player_seek)->SetLoop(m_player_seek, loop ? SL_BOOLEAN_TRUE : SL_BOOLEAN_FALSE, 0, SL_TIME_UNKNOWN);
CheckError(result);
}
}
float SoundPlayer::Volume(void) const
{
return m_volume;
}
void SoundPlayer::Volume(float volume)
{
gPrintfC("vol=%f\n", volume);
m_volume = 1.f;
m_volume = volume;
SLresult result;
ASSERT(NULL != m_player_play, "player is NULL.");
{
result = (*m_player_volume)->SetVolumeLevel(m_player_volume, Volume2Milibell(volume));
CheckError(result);
}
}
/**
* @brief 再生
*/
void SoundPlayer::Play()
{
SLresult result;
ASSERT(NULL != m_player_play, "player is NULL.");
{
Playend(false);
result = (*m_player_play)->SetPlayState(m_player_play, SL_PLAYSTATE_PLAYING);
CheckError(result);
}
}
/**
* @brief 再生の一時停止
*/
void SoundPlayer::Pause()
{
SLresult result;
ASSERT(NULL != m_player_play, "player is NULL.");
{
result = (*m_player_play)->SetPlayState(m_player_play, SL_PLAYSTATE_PAUSED);
CheckError(result);
}
}
/**
* @brief 再生の停止
*/
void SoundPlayer::Stop()
{
SLresult result;
ASSERT(NULL != m_player_play, "player is NULL.");
{
result = (*m_player_play)->SetPlayState(m_player_play, SL_PLAYSTATE_STOPPED);
CheckError(result);
Seek(0.f);
}
}
/**
* @brief ミュートの設定
* @param ミュートにするか
*/
void SoundPlayer::Mute(bool mute)
{
SLresult result;
ASSERT(NULL != m_player_play, "player is NULL.");
{
result = (*m_player_volume)->SetMute(m_player_volume, mute ? SL_BOOLEAN_TRUE : SL_BOOLEAN_FALSE);
CheckError(result);
}
}
/**
* @brief 再生中か
* @return
*/
bool SoundPlayer::IsPlaying()const
{
SLresult result;
SLuint32 state;
result = (*m_player_play)->GetPlayState(m_player_play, &state);
CheckError(result);
return SL_PLAYSTATE_PLAYING == state;
}
/**
* @brief 停止したか
* @return
*/
bool SoundPlayer::IsStopped()const
{
SLresult result;
SLuint32 state;
result = (*m_player_play)->GetPlayState(m_player_play, &state);
CheckError(result);
return SL_PLAYSTATE_STOPPED == state;
}
/**
* @brief 一時停止中か
* @return
*/
bool SoundPlayer::IsPaused()const
{
SLresult result;
SLuint32 state;
result = (*m_player_play)->GetPlayState(m_player_play, &state);
CheckError(result);
return SL_PLAYSTATE_PAUSED == state;
}
/**
* @brief 再生終端か
* @return
*/
bool SoundPlayer::IsPlayend()const
{
return m_playend;
}
/**
* @brief 再生終了を設定する
* @param
* @return
*/
void SoundPlayer::Playend(bool playend)
{
m_playend = playend;
}
/**
* @brief アセットからプレイヤーを生成
* @param ファイルパス
*/
void SoundPlayer::CreatePlayerFromAsset(const cstr* file_name, SLEngineItf engine, SLObjectItf output_mix_object)
{
SLresult result;
struct android_app* pAndroidApp = nsPFW::IApplication::gArguments().mAndroid.mpAndroidApp;
AAssetManager* asset_manager = pAndroidApp->activity->assetManager;
AAsset* asset = AAssetManager_open(asset_manager, file_name, AASSET_MODE_UNKNOWN);
ASSERT(asset != NULL, "asset is not found.");
off_t start, length;
const int fd = AAsset_openFileDescriptor(asset, &start, &length);
ASSERT(0 <= fd, "asset not found.");
AAsset_close(asset);
SLDataLocator_AndroidFD loc_fd = {SL_DATALOCATOR_ANDROIDFD, fd, start, length};
SLDataFormat_MIME format = {SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_AAC};
SLDataSource audio_src = {&loc_fd, &format};
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, output_mix_object};
SLDataSink audio_sink = {&loc_outmix, NULL};
const SLInterfaceID iids[] = {SL_IID_PLAY, SL_IID_SEEK, SL_IID_VOLUME};
const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
result = (*engine)->CreateAudioPlayer(engine, &m_player_object, &audio_src, &audio_sink, SPL_ARRAY_SIZE(iids), iids, req);
CheckError(result);
}
/**
* @brief ファイルからプレイヤーを生成
* @param ファイルパス
* @return
*/
void SoundPlayer::CreatePlayerFromFile(const cstr* file_name, SLEngineItf engine, SLObjectItf output_mix_object)
{
SLresult result;
{
SLDataLocator_URI loc_uri = {SL_DATALOCATOR_URI, reinterpret_cast(file_name)};
SLDataFormat_MIME format = {SL_DATAFORMAT_MIME, NULL, SL_CONTAINERTYPE_AAC};
SLDataSource audio_src = {&loc_uri, &format};
SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, output_mix_object};
SLDataSink audio_sink = {&loc_outmix, NULL};
const SLInterfaceID iids[] = {SL_IID_PLAY, SL_IID_SEEK, SL_IID_VOLUME};
const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE};
result = (*engine)->CreateAudioPlayer(engine, &m_player_object, &audio_src, &audio_sink, SPL_ARRAY_SIZE(iids), iids, req);
CheckError(result);
}
}
/**
* @brief 再生用のコールバック関数
* @param
*/
void SoundPlayer::PlayCallback(SLPlayItf caller, void *pContext, SLuint32 event)
{
SoundPlayer* player = reinterpret_cast(pContext);
switch(event)
{
case SL_PLAYEVENT_HEADATEND:
player->Playend(true);
gPassed;
break;
}
}
大まかな流れとしてはあらかじめ生成したエンジンとアウトプットミックスを渡して初期化し、その後各インターフェイスを取り出す。取り出したものに対して操作を行っていきます。
SLPlayItfは再生・停止・一時停止などの操作に使用します。
SLSeekItfは再生位置の変更やループの設定などが行えます。
SLVolumeItfは音量の設定やミュートなどに使用します。
尚音量はミリベルで指定するので
#include
/**
* @brief 音量(0-1)をミリベルへ変換する
*/
SLint16 Volume2Milibell(float volume)
{
const float min_limit = 0.1f;
const float vol = gClamp(1.f - volume, 0.f, 1.f);
SLint16 volume_level = SL_MILLIBEL_MIN;
if (volume > min_limit)
{
const static float base2 = log(2);
const float antilogarithm = log(1.f / vol);
const float bell = base2 / antilogarithm;
volume_level = static_cast(bell * -1000.f);
}
return volume_level;
}
のようなものを用意しておくとよいと思います。