SLAndroidSimpleBufferQueueItfとSLBufferQueueItfの違いがいまいちわからなかったので調べた。
OpenSL ES for Androidを読んだ限りだとほとんど変わらない模様。ただSLBufferQueueItfは今後インターフェースが変更されたりするかもしれないからSLAndroidSimpleBufferQueueItfを作った模様。SLAndroidSimpleBufferQueueItfは今後変更することはないとのこと。単純な録音や再生だけならこっちを使ったほうがいいとか。
インターフェイスが同じなので
SL_IID_BUFFERQUEUE を SL_IID_ANDROIDSIMPLEBUFFERQUEUE
に、
SL_DATALOCATOR_BUFFERQUEUE を SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE
に、
SLDataLocator_BufferQueue を SLDataLocator_AndroidSimpleBufferQueue
に、
SLBufferQueueItf を SLAndroidSimpleBufferQueueItfに置換するだけで変更することができる。
5.26.2012
5.16.2012
OpenSL ES その3 BufferQueueを使用する
前回のPlayerですと予めプレイヤーを音声ファイル分作成しないといけないので鳴らせる数に限りがあるため(私の環境で試した場合は31個までできた。)PCMの再生に切り替えます。
データとプレイヤーを分けます。使用方法としては最初に初期時にチャンネル数を渡し(モノラル(1)かステレオ(2))、Idleで使用可能状態か問い合わせ、Idle状態であればUseSoundDataで予め作成しておいたSoundDataを渡し、その後Playなどで再生できるようにする。尚、Progressは毎フレーム呼ぶ必要がある。
前回と被るので要所だけまとめると
データはwavの22.05Sample/secを想定しています。後はデータのパラメータkSoundLoadOnetimeかkSoundLoadStreamで初期化時にまとめて読むかProgressで必要な分ずつバッファリングしていくかの違いがあるだけでそれ以外の挙動は同じになるようにDataを実装します。
これを実装した場合ノイズが終端に走ることがあると思います。それの対処は次回ということで
enum SoundLoadType class File; class String; { kSoundLoadOnetime,//先に全て読み込む kSoundLoadStream //ストリームで必要な時に読み込む }; enum tDevice { kDeviceAsset,//アセットから読み込むか kDeviceSDCard//SDカードから読み込むか }; /** * @class * @brief サウンドデータ */ class SoundData { public: SoundData(); ~SoundData(); void Init(const cstr* file_name, tDevice device, SoundLoadType load_type); void Terminate(); SLint16* PcmData() const {return m_pcm_data;} size_t PcmDataSize() const {return m_pcm_data_size;} void LoadProgress(); void Use(); void Unuse(); SoundLoadType LoadType() const {return m_sound_load_type;} private: SLint16* m_pcm_data; size_t m_pcm_data_size; size_t m_pcm_data_loaded_size; SoundLoadType m_sound_load_type; File m_file; int m_used_ref_count; String m_file_name; tDevice m_file_device; public: const SoundDataInfo* Info() const {return &m_sound_data_info;} private: SoundDataInfo m_sound_data_info; }; /** * @class * @brief サウンド再生 */ class SoundEngine::SoundPlayer { public: SoundPlayer(); void Init(int channel); void Terminate(); void Progress(); void Play(); void Pause(); void Resume(); void Stop(); void SeekHead(); 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); void UseSoundData(SoundData* sound_data); void UnuseSoundData(); bool Idle() const; bool Enqueue(); void QueueClear(); private: static void BufferQueueCallback(SLBufferQueueItf caller, void *pContext); SLmillisecond Position()const; SLint16* m_enqueue_data_base; SLint16* m_enqueue_data; SLuint32 m_enqueue_data_size; SLPlayItf m_player_play; SLObjectItf m_player_object; SLVolumeItf m_player_volume; SLBufferQueueItf m_player_queue; SoundData* m_sound_data; bool m_playend; bool m_loop; float m_volume; };
データとプレイヤーを分けます。使用方法としては最初に初期時にチャンネル数を渡し(モノラル(1)かステレオ(2))、Idleで使用可能状態か問い合わせ、Idle状態であればUseSoundDataで予め作成しておいたSoundDataを渡し、その後Playなどで再生できるようにする。尚、Progressは毎フレーム呼ぶ必要がある。
前回と被るので要所だけまとめると
/** * @brief チャンネルの初期化 * @param チャンネル数 */ void SoundPlayer::Init(int channel) gAssert(m_player_object == NULL, "player already initialized."); { m_sound_data = NULL; } SLresult result; { // configure audio source SLDataFormat_PCM pcm_format; pcm_format.formatType = SL_DATAFORMAT_PCM; pcm_format.samplesPerSec = SL_SAMPLINGRATE_22_05; pcm_format.bitsPerSample = SL_PCMSAMPLEFORMAT_FIXED_16; pcm_format.containerSize = 16; pcm_format.endianness = SL_BYTEORDER_LITTLEENDIAN; switch (channel) { case 2: { //steleo pcm_format.numChannels = 2; pcm_format.channelMask = SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT; break; } case 1: { //mono pcm_format.numChannels = 1; pcm_format.channelMask = SL_SPEAKER_FRONT_CENTER; break; } default: gAssert(0, "t channnel isn't implemen."); } SLDataLocator_BufferQueue data_locator_buffer_queue = {SL_DATALOCATOR_BUFFERQUEUE, 4}; SLDataSource audio_src = {&data_locator_buffer_queue, &pcm_format}; // configure audio sink SLDataLocator_OutputMix loc_outmix = {SL_DATALOCATOR_OUTPUTMIX, Instance().m_output_mix_object}; SLDataSink audio_sink = {&loc_outmix, NULL}; // create audio player const SLInterfaceID iids[] = {SL_IID_BUFFERQUEUE, SL_IID_VOLUME}; const SLboolean req[] = {SL_BOOLEAN_TRUE, SL_BOOLEAN_TRUE}; SLEngineItf engine = Instance().m_engine; result = (*engine)->CreateAudioPlayer(engine, &m_player_object, &audio_src, &audio_sink, ARRAY_SIZE(iids), iids, req); CheckError(result); } // realize the player result = (*m_player_object)->Realize(m_player_object, SL_BOOLEAN_FALSE); CheckError(result); // get the play interface result = (*m_player_object)->GetInterface(m_player_object, SL_IID_PLAY, &m_player_play); CheckError(result); // get the volume interface result = (*m_player_object)->GetInterface(m_player_object, SL_IID_VOLUME, &m_player_volume); CheckError(result); // get the queue interface result = (*m_player_object)->GetInterface(m_player_object, SL_IID_BUFFERQUEUE, &m_player_queue); CheckError(result); // setup to receive buffer queue event callbacks result = (*m_player_queue)->RegisterCallback(m_player_queue, BufferQueueCallback, this); CheckError(result); } /** * @brief ストリーミング読み込み */ void SoundPlayer::Progress() { if (m_sound_data != NULL) { m_sound_data->LoadProgress(); } } /** * @brief 頭出しシーク */ void SoundPlayer::SeekHead() { Playend(false); Stop(); QueueClear(); Enqueue(); } /** * @brief サウンドデータをプレイヤーに結びつける */ void SoundPlayer::UseSoundData(SoundData* sound_data) { gAssert(m_sound_data == NULL, "sound data already used."); sound_data->Use(); m_enqueue_data_base = sound_data->PcmData(); m_enqueue_data = m_enqueue_data_base ; m_enqueue_data_size = sound_data->PcmDataSize(); m_sound_data = sound_data; } /** * @brief サウンドデータをプレイヤーから解除する */ void SoundPlayer::UnuseSoundData() { if (m_sound_data != NULL) { Stop(); m_enqueue_data_base = NULL; m_enqueue_data = NULL; m_enqueue_data_size = 0; m_sound_data->Unuse(); m_sound_data = NULL; } } /** * @brief バッファクリア */ void SoundPlayer::QueueClear() { const SLresult result = (*m_player_queue)->Clear(m_player_queue); CheckError(result); m_enqueue_data = m_enqueue_data_base; } /** * @brief キューのコールバック * @param */ void SoundPlayer::BufferQueueCallback(SLBufferQueueItf caller, void *context) { SoundPlayer* this_player = reinterpret_cast(context); if (!this_player->Enqueue()) { if (this_player->m_loop) { this_player->Play(); } else { this_player->Stop(); this_player->Playend(true); } } }
データはwavの22.05Sample/secを想定しています。後はデータのパラメータkSoundLoadOnetimeかkSoundLoadStreamで初期化時にまとめて読むかProgressで必要な分ずつバッファリングしていくかの違いがあるだけでそれ以外の挙動は同じになるようにDataを実装します。
これを実装した場合ノイズが終端に走ることがあると思います。それの対処は次回ということで
5.06.2012
OpenSL ESその2
前回はエンジンとそこから出力を作成したので今回はそれらを利用してオーディオファイルの再生を行います。
まずオーディオを再生するためのプレイヤークラスを作成します。
大まかな流れとしてはあらかじめ生成したエンジンとアウトプットミックスを渡して初期化し、その後各インターフェイスを取り出す。取り出したものに対して操作を行っていきます。
SLPlayItfは再生・停止・一時停止などの操作に使用します。
SLSeekItfは再生位置の変更やループの設定などが行えます。
SLVolumeItfは音量の設定やミュートなどに使用します。
尚音量はミリベルで指定するので
のようなものを用意しておくとよいと思います。
まずオーディオを再生するためのプレイヤークラスを作成します。
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; }
のようなものを用意しておくとよいと思います。
登録:
投稿 (Atom)