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を実装します。
これを実装した場合ノイズが終端に走ることがあると思います。それの対処は次回ということで
終端にノイズが走っていたのはPCMデータの取得のし方が悪かっただけらしい。
返信削除BufferQueueCallbackのループ時にPlayだとちょっとおかしいかも。
this_player->m_enqueue_data = this_player->m_enqueue_data_base;
this_player->Enqueue();
の方が自然。