mirror of
https://github.com/moonlight-stream/moonlight-ios.git
synced 2026-06-16 05:31:01 +00:00
Implement 5.1 surround sound support. Fixes #371
This commit is contained in:
@@ -32,15 +32,13 @@ static id<ConnectionCallbacks> _callbacks;
|
|||||||
|
|
||||||
#define OUTPUT_BUS 0
|
#define OUTPUT_BUS 0
|
||||||
|
|
||||||
#define MAX_CHANNEL_COUNT 2
|
|
||||||
#define FRAME_SIZE 240
|
|
||||||
|
|
||||||
#define CIRCULAR_BUFFER_SIZE 32
|
#define CIRCULAR_BUFFER_SIZE 32
|
||||||
|
|
||||||
static int audioBufferWriteIndex;
|
static int audioBufferWriteIndex;
|
||||||
static int audioBufferReadIndex;
|
static int audioBufferReadIndex;
|
||||||
static int activeChannelCount;
|
static int audioBufferStride;
|
||||||
static short audioCircularBuffer[CIRCULAR_BUFFER_SIZE][FRAME_SIZE * MAX_CHANNEL_COUNT];
|
static int audioSamplesPerFrame;
|
||||||
|
static short* audioCircularBuffer;
|
||||||
|
|
||||||
#define AUDIO_QUEUE_BUFFERS 4
|
#define AUDIO_QUEUE_BUFFERS 4
|
||||||
|
|
||||||
@@ -90,15 +88,16 @@ int ArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, v
|
|||||||
{
|
{
|
||||||
int err;
|
int err;
|
||||||
|
|
||||||
// Clear the circular buffer
|
// Initialize the circular buffer
|
||||||
audioBufferWriteIndex = audioBufferReadIndex = 0;
|
audioBufferWriteIndex = audioBufferReadIndex = 0;
|
||||||
|
audioSamplesPerFrame = opusConfig->samplesPerFrame;
|
||||||
|
audioBufferStride = opusConfig->channelCount * opusConfig->samplesPerFrame;
|
||||||
|
audioCircularBuffer = malloc(CIRCULAR_BUFFER_SIZE * audioBufferStride * sizeof(short));
|
||||||
|
if (audioCircularBuffer == NULL) {
|
||||||
|
Log(LOG_E, @"Error allocating output queue\n");
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
// We only support stereo for now.
|
|
||||||
// TODO: Ensure AudioToolbox's channel mapping matches Opus's
|
|
||||||
// and correct if neccessary.
|
|
||||||
assert(audioConfiguration == AUDIO_CONFIGURATION_STEREO);
|
|
||||||
|
|
||||||
activeChannelCount = opusConfig->channelCount;
|
|
||||||
opusDecoder = opus_multistream_decoder_create(opusConfig->sampleRate,
|
opusDecoder = opus_multistream_decoder_create(opusConfig->sampleRate,
|
||||||
opusConfig->channelCount,
|
opusConfig->channelCount,
|
||||||
opusConfig->streams,
|
opusConfig->streams,
|
||||||
@@ -137,8 +136,29 @@ int ArInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, v
|
|||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// We need to specify a channel layout for surround sound configurations
|
||||||
|
if (opusConfig->channelCount > 2) {
|
||||||
|
AudioChannelLayout channelLayout = {};
|
||||||
|
|
||||||
|
switch (opusConfig->channelCount) {
|
||||||
|
case 6:
|
||||||
|
channelLayout.mChannelLayoutTag = kAudioChannelLayoutTag_MPEG_5_1_A;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
// Unsupported channel layout
|
||||||
|
Log(LOG_E, @"Unsupported channel layout: %d\n", opusConfig->channelCount);
|
||||||
|
abort();
|
||||||
|
}
|
||||||
|
|
||||||
|
status = AudioQueueSetProperty(audioQueue, kAudioQueueProperty_ChannelLayout, &channelLayout, sizeof(channelLayout));
|
||||||
|
if (status != noErr) {
|
||||||
|
Log(LOG_E, @"Error configuring surround channel layout: %d\n", status);
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
for (int i = 0; i < AUDIO_QUEUE_BUFFERS; i++) {
|
for (int i = 0; i < AUDIO_QUEUE_BUFFERS; i++) {
|
||||||
status = AudioQueueAllocateBuffer(audioQueue, audioFormat.mBytesPerFrame * FRAME_SIZE, &audioBuffers[i]);
|
status = AudioQueueAllocateBuffer(audioQueue, audioFormat.mBytesPerFrame * opusConfig->samplesPerFrame, &audioBuffers[i]);
|
||||||
if (status != noErr) {
|
if (status != noErr) {
|
||||||
Log(LOG_E, @"Error allocating output buffer: %d\n", status);
|
Log(LOG_E, @"Error allocating output buffer: %d\n", status);
|
||||||
return status;
|
return status;
|
||||||
@@ -170,10 +190,15 @@ void ArCleanup(void)
|
|||||||
// Also frees buffers
|
// Also frees buffers
|
||||||
AudioQueueDispose(audioQueue, true);
|
AudioQueueDispose(audioQueue, true);
|
||||||
|
|
||||||
|
// Must be freed after the queue is stopped
|
||||||
|
if (audioCircularBuffer != NULL) {
|
||||||
|
free(audioCircularBuffer);
|
||||||
|
audioCircularBuffer = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
#if TARGET_OS_IPHONE
|
#if TARGET_OS_IPHONE
|
||||||
// Audio session is now inactive
|
// Audio session is now inactive
|
||||||
AVAudioSession* audioSession = [AVAudioSession sharedInstance];
|
[[AVAudioSession sharedInstance] setActive: NO error: nil];
|
||||||
[audioSession setActive: YES error: nil];
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -188,7 +213,7 @@ void ArDecodeAndPlaySample(char* sampleData, int sampleLength)
|
|||||||
}
|
}
|
||||||
|
|
||||||
decodeLen = opus_multistream_decode(opusDecoder, (unsigned char *)sampleData, sampleLength,
|
decodeLen = opus_multistream_decode(opusDecoder, (unsigned char *)sampleData, sampleLength,
|
||||||
audioCircularBuffer[audioBufferWriteIndex], FRAME_SIZE, 0);
|
(short*)&audioCircularBuffer[audioBufferWriteIndex * audioBufferStride], audioSamplesPerFrame, 0);
|
||||||
if (decodeLen > 0) {
|
if (decodeLen > 0) {
|
||||||
// Use a full memory barrier to ensure the circular buffer is written before incrementing the index
|
// Use a full memory barrier to ensure the circular buffer is written before incrementing the index
|
||||||
__sync_synchronize();
|
__sync_synchronize();
|
||||||
@@ -382,7 +407,7 @@ void ClConnectionStatusUpdate(int status)
|
|||||||
static void FillOutputBuffer(void *aqData,
|
static void FillOutputBuffer(void *aqData,
|
||||||
AudioQueueRef inAQ,
|
AudioQueueRef inAQ,
|
||||||
AudioQueueBufferRef inBuffer) {
|
AudioQueueBufferRef inBuffer) {
|
||||||
inBuffer->mAudioDataByteSize = activeChannelCount * FRAME_SIZE * sizeof(short);
|
inBuffer->mAudioDataByteSize = audioBufferStride * sizeof(short);
|
||||||
|
|
||||||
assert(inBuffer->mAudioDataByteSize == inBuffer->mAudioDataBytesCapacity);
|
assert(inBuffer->mAudioDataByteSize == inBuffer->mAudioDataBytesCapacity);
|
||||||
|
|
||||||
@@ -390,7 +415,7 @@ static void FillOutputBuffer(void *aqData,
|
|||||||
if (audioBufferWriteIndex != audioBufferReadIndex) {
|
if (audioBufferWriteIndex != audioBufferReadIndex) {
|
||||||
// Copy data to the audio buffer
|
// Copy data to the audio buffer
|
||||||
memcpy(inBuffer->mAudioData,
|
memcpy(inBuffer->mAudioData,
|
||||||
audioCircularBuffer[audioBufferReadIndex],
|
&audioCircularBuffer[audioBufferReadIndex * audioBufferStride],
|
||||||
inBuffer->mAudioDataByteSize);
|
inBuffer->mAudioDataByteSize);
|
||||||
|
|
||||||
// Use a full memory barrier to ensure the circular buffer is read before incrementing the index
|
// Use a full memory barrier to ensure the circular buffer is read before incrementing the index
|
||||||
|
|||||||
@@ -479,10 +479,17 @@ static NSMutableSet* hostList;
|
|||||||
_streamConfig.multiController = streamSettings.multiController;
|
_streamConfig.multiController = streamSettings.multiController;
|
||||||
_streamConfig.gamepadMask = [ControllerSupport getConnectedGamepadMask:_streamConfig];
|
_streamConfig.gamepadMask = [ControllerSupport getConnectedGamepadMask:_streamConfig];
|
||||||
|
|
||||||
// TODO: Detect attached surround sound system then address 5.1 TODOs
|
|
||||||
// in Connection.m
|
// Probe for supported channel configurations
|
||||||
_streamConfig.audioChannelCount = 2;
|
Log(LOG_I, @"Audio devices supports %d channels", [AVAudioSession sharedInstance].maximumOutputNumberOfChannels);
|
||||||
_streamConfig.audioChannelMask = 0x3;
|
if ([AVAudioSession sharedInstance].maximumOutputNumberOfChannels >= 6) {
|
||||||
|
_streamConfig.audioChannelCount = 6;
|
||||||
|
_streamConfig.audioChannelMask = 0xFC;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
_streamConfig.audioChannelCount = 2;
|
||||||
|
_streamConfig.audioChannelMask = 0x3;
|
||||||
|
}
|
||||||
|
|
||||||
// HDR requires HDR10 game, HDR10 display, and HEVC Main10 decoder on the client.
|
// HDR requires HDR10 game, HDR10 display, and HEVC Main10 decoder on the client.
|
||||||
// It additionally requires an HEVC Main10 encoder on the server (GTX 1000+).
|
// It additionally requires an HEVC Main10 encoder on the server (GTX 1000+).
|
||||||
|
|||||||
Reference in New Issue
Block a user