mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-05-11 07:35:42 -04:00
210 lines
6.1 KiB
C++
210 lines
6.1 KiB
C++
#include "opensles_audio_stream.h"
|
|
#include "common/assert.h"
|
|
#include "common/log.h"
|
|
#include <cmath>
|
|
Log_SetChannel(OpenSLESAudioStream);
|
|
|
|
// Based off Dolphin's OpenSLESStream class.
|
|
|
|
OpenSLESAudioStream::OpenSLESAudioStream() = default;
|
|
|
|
OpenSLESAudioStream::~OpenSLESAudioStream()
|
|
{
|
|
if (IsOpen())
|
|
OpenSLESAudioStream::CloseDevice();
|
|
}
|
|
|
|
std::unique_ptr<AudioStream> OpenSLESAudioStream::Create()
|
|
{
|
|
return std::make_unique<OpenSLESAudioStream>();
|
|
}
|
|
|
|
bool OpenSLESAudioStream::OpenDevice()
|
|
{
|
|
DebugAssert(!IsOpen());
|
|
|
|
SLresult res = slCreateEngine(&m_engine, 0, nullptr, 0, nullptr, nullptr);
|
|
if (res != SL_RESULT_SUCCESS)
|
|
{
|
|
Log_ErrorPrintf("slCreateEngine failed: %d", res);
|
|
return false;
|
|
}
|
|
|
|
res = (*m_engine)->Realize(m_engine, SL_BOOLEAN_FALSE);
|
|
if (res != SL_RESULT_SUCCESS)
|
|
{
|
|
Log_ErrorPrintf("Realize(Engine) failed: %d", res);
|
|
CloseDevice();
|
|
return false;
|
|
}
|
|
|
|
res = (*m_engine)->GetInterface(m_engine, SL_IID_ENGINE, &m_engine_engine);
|
|
if (res != SL_RESULT_SUCCESS)
|
|
{
|
|
Log_ErrorPrintf("GetInterface(SL_IID_ENGINE) failed: %d", res);
|
|
CloseDevice();
|
|
return false;
|
|
}
|
|
|
|
res = (*m_engine_engine)->CreateOutputMix(m_engine_engine, &m_output_mix, 0, 0, 0);
|
|
if (res != SL_RESULT_SUCCESS)
|
|
{
|
|
Log_ErrorPrintf("CreateOutputMix failed: %d", res);
|
|
CloseDevice();
|
|
return false;
|
|
}
|
|
|
|
res = (*m_output_mix)->Realize(m_output_mix, SL_BOOLEAN_FALSE);
|
|
if (res != SL_RESULT_SUCCESS)
|
|
{
|
|
Log_ErrorPrintf("Realize(OutputMix) mix failed: %d", res);
|
|
CloseDevice();
|
|
return false;
|
|
}
|
|
|
|
SLDataLocator_AndroidSimpleBufferQueue dloc_bq{SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE, NUM_BUFFERS};
|
|
SLDataFormat_PCM format = {SL_DATAFORMAT_PCM,
|
|
m_channels,
|
|
m_output_sample_rate * 1000u,
|
|
SL_PCMSAMPLEFORMAT_FIXED_16,
|
|
SL_PCMSAMPLEFORMAT_FIXED_16,
|
|
SL_SPEAKER_FRONT_LEFT | SL_SPEAKER_FRONT_RIGHT,
|
|
SL_BYTEORDER_LITTLEENDIAN};
|
|
SLDataSource dsrc{&dloc_bq, &format};
|
|
SLDataLocator_OutputMix dloc_outputmix{SL_DATALOCATOR_OUTPUTMIX, m_output_mix};
|
|
SLDataSink dsink{&dloc_outputmix, nullptr};
|
|
|
|
const std::array<SLInterfaceID, 2> ap_interfaces = {{SL_IID_BUFFERQUEUE, SL_IID_VOLUME}};
|
|
const std::array<SLboolean, 2> ap_interfaces_req = {{true, true}};
|
|
res = (*m_engine_engine)
|
|
->CreateAudioPlayer(m_engine_engine, &m_player, &dsrc, &dsink, static_cast<u32>(ap_interfaces.size()),
|
|
ap_interfaces.data(), ap_interfaces_req.data());
|
|
if (res != SL_RESULT_SUCCESS)
|
|
{
|
|
Log_ErrorPrintf("CreateAudioPlayer failed: %d", res);
|
|
CloseDevice();
|
|
return false;
|
|
}
|
|
|
|
res = (*m_player)->Realize(m_player, SL_BOOLEAN_FALSE);
|
|
if (res != SL_RESULT_SUCCESS)
|
|
{
|
|
Log_ErrorPrintf("Realize(AudioPlayer) failed: %d", res);
|
|
CloseDevice();
|
|
return false;
|
|
}
|
|
|
|
res = (*m_player)->GetInterface(m_player, SL_IID_PLAY, &m_play_interface);
|
|
if (res != SL_RESULT_SUCCESS)
|
|
{
|
|
Log_ErrorPrintf("GetInterface(SL_IID_PLAY) failed: %d", res);
|
|
CloseDevice();
|
|
return false;
|
|
}
|
|
|
|
res = (*m_player)->GetInterface(m_player, SL_IID_BUFFERQUEUE, &m_buffer_queue_interface);
|
|
if (res != SL_RESULT_SUCCESS)
|
|
{
|
|
Log_ErrorPrintf("GetInterface(SL_IID_BUFFERQUEUE) failed: %d", res);
|
|
CloseDevice();
|
|
return false;
|
|
}
|
|
|
|
res = (*m_player)->GetInterface(m_player, SL_IID_VOLUME, &m_volume_interface);
|
|
if (res != SL_RESULT_SUCCESS)
|
|
{
|
|
Log_ErrorPrintf("GetInterface(SL_IID_VOLUME) failed: %d", res);
|
|
CloseDevice();
|
|
return false;
|
|
}
|
|
|
|
res = (*m_buffer_queue_interface)->RegisterCallback(m_buffer_queue_interface, BufferCallback, this);
|
|
if (res != SL_RESULT_SUCCESS)
|
|
{
|
|
Log_ErrorPrintf("Failed to register callback: %d", res);
|
|
CloseDevice();
|
|
return false;
|
|
}
|
|
|
|
for (u32 i = 0; i < NUM_BUFFERS; i++)
|
|
m_buffers[i] = std::make_unique<SampleType[]>(m_buffer_size * m_channels);
|
|
|
|
Log_InfoPrintf("OpenSL ES device opened: %uhz, %u channels, %u buffer size, %u buffers",
|
|
m_output_sample_rate, m_channels, m_buffer_size, NUM_BUFFERS);
|
|
return true;
|
|
}
|
|
|
|
void OpenSLESAudioStream::PauseDevice(bool paused)
|
|
{
|
|
if (m_paused == paused)
|
|
return;
|
|
|
|
SLresult res = (*m_play_interface)->SetPlayState(m_play_interface, paused ? SL_PLAYSTATE_PAUSED : SL_PLAYSTATE_PLAYING);
|
|
if (res != SL_RESULT_SUCCESS)
|
|
Log_ErrorPrintf("SetPlayState failed: %d", res);
|
|
|
|
if (!paused && !m_buffer_enqueued)
|
|
{
|
|
m_buffer_enqueued = true;
|
|
EnqueueBuffer();
|
|
}
|
|
|
|
m_paused = paused;
|
|
}
|
|
|
|
void OpenSLESAudioStream::CloseDevice()
|
|
{
|
|
m_buffers = {};
|
|
m_current_buffer = 0;
|
|
m_paused = true;
|
|
m_buffer_enqueued = false;
|
|
|
|
if (m_player)
|
|
{
|
|
(*m_player)->Destroy(m_player);
|
|
m_volume_interface = {};
|
|
m_buffer_queue_interface = {};
|
|
m_play_interface = {};
|
|
m_player = {};
|
|
}
|
|
if (m_output_mix)
|
|
{
|
|
(*m_output_mix)->Destroy(m_output_mix);
|
|
m_output_mix = {};
|
|
}
|
|
(*m_engine)->Destroy(m_engine);
|
|
m_engine_engine = {};
|
|
m_engine = {};
|
|
}
|
|
|
|
void OpenSLESAudioStream::SetOutputVolume(u32 volume)
|
|
{
|
|
const SLmillibel attenuation = (volume == 0) ?
|
|
SL_MILLIBEL_MIN :
|
|
static_cast<SLmillibel>(2000.0f * std::log10(static_cast<float>(volume) / 100.0f));
|
|
SLresult res = (*m_volume_interface)->SetVolumeLevel(m_volume_interface, attenuation);
|
|
if (res != SL_RESULT_SUCCESS)
|
|
Log_ErrorPrintf("SetVolumeLevel failed: %d", res);
|
|
}
|
|
|
|
void OpenSLESAudioStream::EnqueueBuffer()
|
|
{
|
|
SampleType* samples = m_buffers[m_current_buffer].get();
|
|
ReadFrames(samples, m_buffer_size, false);
|
|
|
|
SLresult res = (*m_buffer_queue_interface)
|
|
->Enqueue(m_buffer_queue_interface, samples, m_buffer_size * m_channels * sizeof(SampleType));
|
|
if (res != SL_RESULT_SUCCESS)
|
|
Log_ErrorPrintf("Enqueue buffer failed: %d", res);
|
|
|
|
m_current_buffer = (m_current_buffer + 1) % NUM_BUFFERS;
|
|
}
|
|
|
|
void OpenSLESAudioStream::BufferCallback(SLAndroidSimpleBufferQueueItf buffer_queue, void* context)
|
|
{
|
|
OpenSLESAudioStream* const this_ptr = static_cast<OpenSLESAudioStream*>(context);
|
|
this_ptr->EnqueueBuffer();
|
|
}
|
|
|
|
void OpenSLESAudioStream::FramesAvailable() {}
|