diff --git a/mini_al.h b/mini_al.h index 9055c01f..4b751126 100644 --- a/mini_al.h +++ b/mini_al.h @@ -249,8 +249,9 @@ typedef void (* mal_proc)(); } mal_event; #endif -#define MAL_MAX_PERIODS_DSOUND 4 -#define MAL_MAX_PERIODS_OPENAL 4 +#define MAL_MAX_CHANNELS 16 +#define MAL_MAX_PERIODS_DSOUND 4 +#define MAL_MAX_PERIODS_OPENAL 4 typedef int mal_result; #define MAL_SUCCESS 0 @@ -2120,21 +2121,29 @@ static mal_result mal_device__find_best_format__wasapi(mal_device* pDevice, WAVE wf.Format.nBlockAlign = (wf.Format.nChannels * wf.Format.wBitsPerSample) / 8; wf.Format.nAvgBytesPerSec = wf.Format.nBlockAlign * wf.Format.nSamplesPerSec; wf.Samples.wValidBitsPerSample = wf.Format.wBitsPerSample; - wf.dwChannelMask = ~(((DWORD)-1) << pDevice->channels); + wf.dwChannelMask = 0xFFFFFFFF; // Always use every channel. if (pDevice->format == mal_format_f32) { wf.SubFormat = MAL_GUID_KSDATAFORMAT_SUBTYPE_IEEE_FLOAT; } else { wf.SubFormat = MAL_GUID_KSDATAFORMAT_SUBTYPE_PCM; } + HRESULT hr = AUDCLNT_E_UNSUPPORTED_FORMAT; WAVEFORMATEXTENSIBLE* pBestFormatTemp; #ifdef __cplusplus - HRESULT hr = ((IAudioClient*)pDevice->wasapi.pAudioClient)->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*)&wf, (WAVEFORMATEX**)&pBestFormatTemp); + hr = ((IAudioClient*)pDevice->wasapi.pAudioClient)->IsFormatSupported(AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*)&wf, (WAVEFORMATEX**)&pBestFormatTemp); #else - HRESULT hr = ((IAudioClient*)pDevice->wasapi.pAudioClient)->lpVtbl->IsFormatSupported((IAudioClient*)pDevice->wasapi.pAudioClient, AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*)&wf, (WAVEFORMATEX**)&pBestFormatTemp); + hr = ((IAudioClient*)pDevice->wasapi.pAudioClient)->lpVtbl->IsFormatSupported((IAudioClient*)pDevice->wasapi.pAudioClient, AUDCLNT_SHAREMODE_SHARED, (WAVEFORMATEX*)&wf, (WAVEFORMATEX**)&pBestFormatTemp); #endif - if (hr != S_OK && hr != S_FALSE && hr != AUDCLNT_E_UNSUPPORTED_FORMAT) { - return MAL_WASAPI_FAILED_TO_FIND_BEST_FORMAT; + if (hr != S_OK && hr != S_FALSE) { + #ifdef __cplusplus + hr = ((IAudioClient*)pDevice->wasapi.pAudioClient)->GetMixFormat((WAVEFORMATEX**)&pBestFormatTemp); + #else + hr = ((IAudioClient*)pDevice->wasapi.pAudioClient)->lpVtbl->GetMixFormat((IAudioClient*)pDevice->wasapi.pAudioClient, (WAVEFORMATEX**)&pBestFormatTemp); + #endif + if (hr != S_OK) { + return MAL_WASAPI_FAILED_TO_FIND_BEST_FORMAT; + } } if (pBestFormatTemp != NULL) { @@ -2245,7 +2254,7 @@ static mal_result mal_device_init__wasapi(mal_context* pContext, mal_device_type #endif if (FAILED(hr)) { mal_device_uninit__wasapi(pDevice); - return mal_post_error(pDevice, "[WASAPI] Failed to activate device.", MAL_WASAPI_FAILED_TO_INITIALIZE_DEVICE); + return mal_post_error(pDevice, "[WASAPI] Failed to initialize device.", MAL_WASAPI_FAILED_TO_INITIALIZE_DEVICE); } #ifdef __cplusplus @@ -5254,6 +5263,10 @@ mal_result mal_device_init(mal_context* pContext, mal_device_type type, mal_devi if (pConfig == NULL || pConfig->channels == 0 || pConfig->sampleRate == 0) return mal_post_error(pDevice, "mal_device_init() called with invalid arguments.", MAL_INVALID_ARGS); + if (pConfig->channels > MAL_MAX_CHANNELS) { + return MAL_FORMAT_NOT_SUPPORTED; + } + // Default buffer size and periods. if (pConfig->bufferSizeInFrames == 0) { pConfig->bufferSizeInFrames = (pConfig->sampleRate/1000) * MAL_DEFAULT_BUFFER_SIZE_IN_MILLISECONDS; @@ -5638,7 +5651,7 @@ void mal_pcm_f32_to_s32(int* pOut, const float* pIn, unsigned int count); static void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, mal_format formatIn, unsigned int sampleCount) { if (formatOut == formatIn) { - memcpy(pOut, pIn, sampleCount * mal_get_sample_size_in_bytes(formatOut)); + mal_copy_memory(pOut, pIn, sampleCount * mal_get_sample_size_in_bytes(formatOut)); } switch (formatIn) @@ -5707,6 +5720,105 @@ static void mal_pcm_convert(void* pOut, mal_format formatOut, const void* pIn, m } } +static void mal_rearrange_channels(float* pFrame, mal_uint32 channels, mal_uint32 channelMap[16]) +{ + float temp; + switch (channels) { + case 16: temp = pFrame[15]; pFrame[15] = pFrame[channelMap[15]]; pFrame[channelMap[15]] = temp; + case 15: temp = pFrame[14]; pFrame[14] = pFrame[channelMap[14]]; pFrame[channelMap[14]] = temp; + case 14: temp = pFrame[13]; pFrame[13] = pFrame[channelMap[13]]; pFrame[channelMap[13]] = temp; + case 13: temp = pFrame[12]; pFrame[12] = pFrame[channelMap[12]]; pFrame[channelMap[12]] = temp; + case 12: temp = pFrame[11]; pFrame[11] = pFrame[channelMap[11]]; pFrame[channelMap[11]] = temp; + case 11: temp = pFrame[10]; pFrame[10] = pFrame[channelMap[10]]; pFrame[channelMap[10]] = temp; + case 10: temp = pFrame[ 9]; pFrame[ 9] = pFrame[channelMap[ 9]]; pFrame[channelMap[ 9]] = temp; + case 9: temp = pFrame[ 8]; pFrame[ 8] = pFrame[channelMap[ 8]]; pFrame[channelMap[ 8]] = temp; + case 8: temp = pFrame[ 7]; pFrame[ 7] = pFrame[channelMap[ 7]]; pFrame[channelMap[ 7]] = temp; + case 7: temp = pFrame[ 6]; pFrame[ 6] = pFrame[channelMap[ 6]]; pFrame[channelMap[ 6]] = temp; + case 6: temp = pFrame[ 5]; pFrame[ 5] = pFrame[channelMap[ 5]]; pFrame[channelMap[ 5]] = temp; + case 5: temp = pFrame[ 4]; pFrame[ 4] = pFrame[channelMap[ 4]]; pFrame[channelMap[ 4]] = temp; + case 4: temp = pFrame[ 3]; pFrame[ 3] = pFrame[channelMap[ 3]]; pFrame[channelMap[ 3]] = temp; + case 3: temp = pFrame[ 2]; pFrame[ 2] = pFrame[channelMap[ 2]]; pFrame[channelMap[ 2]] = temp; + case 2: temp = pFrame[ 1]; pFrame[ 1] = pFrame[channelMap[ 1]]; pFrame[channelMap[ 1]] = temp; + case 1: temp = pFrame[ 0]; pFrame[ 0] = pFrame[channelMap[ 0]]; pFrame[channelMap[ 0]] = temp; + } +} + +static void mal_convert_channels_to_mono(float* pFrame, mal_uint32 channels) +{ + // Mono is just an averaging of each channel. + float total = 0; + switch (channels) { + case 16: total += pFrame[15]; + case 15: total += pFrame[14]; + case 14: total += pFrame[13]; + case 13: total += pFrame[12]; + case 12: total += pFrame[11]; + case 11: total += pFrame[10]; + case 10: total += pFrame[ 9]; + case 9: total += pFrame[ 8]; + case 8: total += pFrame[ 7]; + case 7: total += pFrame[ 6]; + case 6: total += pFrame[ 5]; + case 5: total += pFrame[ 4]; + case 4: total += pFrame[ 3]; + case 3: total += pFrame[ 2]; + case 2: total += pFrame[ 1]; + case 1: total += pFrame[ 0]; + } + + pFrame[0] = total / channels; +} + +static void mal_convert_channels_from_mono(float* pFrame, mal_uint32 channels) +{ + // Just copy the mono channel to each other channel. + switch (channels) { + case 16: pFrame[15] = pFrame[0]; + case 15: pFrame[14] = pFrame[0]; + case 14: pFrame[13] = pFrame[0]; + case 13: pFrame[12] = pFrame[0]; + case 12: pFrame[11] = pFrame[0]; + case 11: pFrame[10] = pFrame[0]; + case 10: pFrame[ 9] = pFrame[0]; + case 9: pFrame[ 8] = pFrame[0]; + case 8: pFrame[ 7] = pFrame[0]; + case 7: pFrame[ 6] = pFrame[0]; + case 6: pFrame[ 5] = pFrame[0]; + case 5: pFrame[ 4] = pFrame[0]; + case 4: pFrame[ 3] = pFrame[0]; + case 3: pFrame[ 2] = pFrame[0]; + case 2: pFrame[ 1] = pFrame[0]; + case 1: pFrame[ 0] = pFrame[0]; + } +} + +static void mal_convert_channels__dec(float* pFrameIn, mal_uint32 channelsIn, mal_uint32 channelsOut) +{ + if (channelsOut == 1) { + mal_convert_channels_to_mono(pFrameIn, channelsIn); + return; + } + + // For now we are just dropping excess channels. +} + +static void mal_convert_channels__inc(float* pFrameIn, mal_uint32 channelsIn, mal_uint32 channelsOut) +{ + // For now we are just dropping excess channels. + (void)pFrameIn; + (void)channelsIn; + (void)channelsOut; +} + +static void mal_convert_channels(float* pFrameIn, mal_uint32 channelsIn, mal_uint32 channelsOut) +{ + if (channelsIn > channelsOut) { + mal_convert_channels__dec(pFrameIn, channelsIn, channelsOut); + } else if (channelsIn < channelsOut) { + mal_convert_channels__inc(pFrameIn, channelsIn, channelsOut); + } +} + mal_result mal_dsp_init(mal_dsp_config* pConfig, mal_dsp* pDSP) { if (pDSP == NULL) return MAL_INVALID_ARGS; @@ -5748,11 +5860,21 @@ mal_uint32 mal_dsp_process_no_src(mal_dsp* pDSP, mal_dsp_read_proc onRead, void* // No SRC and no channel conversion. mal_pcm_convert(pFramesOut8, pDSP->formatOut, chunkBuffer, pDSP->formatIn, framesJustRead * pDSP->channelsIn); } else { - // Channel convert and perhaps format conversion. - - - // TODO: Implement me. - framesJustRead = 0; + // Channel conversion + format conversion. + if (pDSP->formatIn == mal_format_f32) { + for (mal_uint32 iFrame = 0; iFrame < framesJustRead; ++iFrame) { + float* pTempFrameF32 = (float*)(chunkBuffer + (iFrame * pDSP->channelsOut * mal_get_sample_size_in_bytes(pDSP->formatOut))); + mal_convert_channels(pTempFrameF32, pDSP->channelsIn, pDSP->channelsOut); + mal_pcm_convert(pFramesOut8 + (iFrame * pDSP->channelsOut * mal_get_sample_size_in_bytes(pDSP->formatOut)), pDSP->formatOut, pTempFrameF32, mal_format_f32, pDSP->channelsOut); // <-- Evaluates to a memcpy(). + } + } else { + float tempFrame[MAL_MAX_CHANNELS]; + for (mal_uint32 iFrame = 0; iFrame < framesJustRead; ++iFrame) { + mal_pcm_convert(tempFrame, mal_format_f32, chunkBuffer + (iFrame * pDSP->channelsIn * mal_get_sample_size_in_bytes(pDSP->formatIn)), pDSP->formatIn, pDSP->channelsIn); + mal_convert_channels(tempFrame, pDSP->channelsIn, pDSP->channelsOut); + mal_pcm_convert(pFramesOut8 + (iFrame * pDSP->channelsOut * mal_get_sample_size_in_bytes(pDSP->formatOut)), pDSP->formatOut, tempFrame, mal_format_f32, pDSP->channelsOut); + } + } } framesRemaining -= framesJustRead;