/** * FreeRDP: A Remote Desktop Protocol Implementation * Audio Formats * * Copyright 2013 Marc-Andre Moreau * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #define TAG FREERDP_TAG("codec") UINT32 audio_format_compute_time_length(const AUDIO_FORMAT* format, size_t size) { UINT32 mstime = 0; UINT32 wSamples = 0; /** * [MSDN-AUDIOFORMAT]: * http://msdn.microsoft.com/en-us/library/ms713497.aspx */ if (format->wBitsPerSample) { const size_t samples = (size * 8) / format->wBitsPerSample; WINPR_ASSERT(samples <= UINT32_MAX); wSamples = (UINT32)samples; mstime = (((wSamples * 1000) / format->nSamplesPerSec) / format->nChannels); } else { mstime = 0; if (format->wFormatTag == WAVE_FORMAT_GSM610) { UINT16 nSamplesPerBlock = 0; if ((format->cbSize == 2) && (format->data)) { nSamplesPerBlock = *((UINT16*)format->data); const size_t samples = (size / format->nBlockAlign) * nSamplesPerBlock; WINPR_ASSERT(samples <= UINT32_MAX); wSamples = (UINT32)samples; mstime = (((wSamples * 1000) / format->nSamplesPerSec) / format->nChannels); } else { WLog_ERR(TAG, "audio_format_compute_time_length: invalid WAVE_FORMAT_GSM610 format"); } } else { WLog_ERR(TAG, "audio_format_compute_time_length: unknown format %" PRIu16 "", format->wFormatTag); } } return mstime; } const char* audio_format_get_tag_string(UINT16 wFormatTag) { switch (wFormatTag) { case WAVE_FORMAT_PCM: return "WAVE_FORMAT_PCM"; case WAVE_FORMAT_ADPCM: return "WAVE_FORMAT_ADPCM"; case WAVE_FORMAT_ALAW: return "WAVE_FORMAT_ALAW"; case WAVE_FORMAT_MULAW: return "WAVE_FORMAT_MULAW"; case WAVE_FORMAT_DVI_ADPCM: return "WAVE_FORMAT_DVI_ADPCM"; case WAVE_FORMAT_GSM610: return "WAVE_FORMAT_GSM610"; case WAVE_FORMAT_MSG723: return "WAVE_FORMAT_MSG723"; case WAVE_FORMAT_DSPGROUP_TRUESPEECH: return "WAVE_FORMAT_DSPGROUP_TRUESPEECH "; case WAVE_FORMAT_MPEGLAYER3: return "WAVE_FORMAT_MPEGLAYER3"; case WAVE_FORMAT_WMAUDIO2: return "WAVE_FORMAT_WMAUDIO2"; case WAVE_FORMAT_AAC_MS: return "WAVE_FORMAT_AAC_MS"; default: return "WAVE_FORMAT_UNKNOWN"; } } void audio_format_print(wLog* log, DWORD level, const AUDIO_FORMAT* format) { WLog_Print(log, level, "%s:\t wFormatTag: 0x%04" PRIX16 " nChannels: %" PRIu16 " nSamplesPerSec: %" PRIu32 " " "nAvgBytesPerSec: %" PRIu32 " nBlockAlign: %" PRIu16 " wBitsPerSample: %" PRIu16 " cbSize: %" PRIu16 "", audio_format_get_tag_string(format->wFormatTag), format->wFormatTag, format->nChannels, format->nSamplesPerSec, format->nAvgBytesPerSec, format->nBlockAlign, format->wBitsPerSample, format->cbSize); } void audio_formats_print(wLog* log, DWORD level, const AUDIO_FORMAT* formats, UINT16 count) { if (formats) { WLog_Print(log, level, "AUDIO_FORMATS (%" PRIu16 ") ={", count); for (UINT32 index = 0; index < count; index++) { const AUDIO_FORMAT* format = &formats[index]; WLog_Print(log, level, "\t"); audio_format_print(log, level, format); } WLog_Print(log, level, "}"); } } BOOL audio_format_read(wStream* s, AUDIO_FORMAT* format) { if (!s || !format) return FALSE; if (!Stream_CheckAndLogRequiredLength(TAG, s, 18)) return FALSE; Stream_Read_UINT16(s, format->wFormatTag); Stream_Read_UINT16(s, format->nChannels); Stream_Read_UINT32(s, format->nSamplesPerSec); Stream_Read_UINT32(s, format->nAvgBytesPerSec); Stream_Read_UINT16(s, format->nBlockAlign); Stream_Read_UINT16(s, format->wBitsPerSample); Stream_Read_UINT16(s, format->cbSize); if (!Stream_CheckAndLogRequiredLength(TAG, s, format->cbSize)) return FALSE; format->data = NULL; if (format->cbSize > 0) { format->data = malloc(format->cbSize); if (!format->data) return FALSE; Stream_Read(s, format->data, format->cbSize); } return TRUE; } BOOL audio_format_write(wStream* s, const AUDIO_FORMAT* format) { if (!s || !format) return FALSE; if (!Stream_EnsureRemainingCapacity(s, 18 + format->cbSize)) return FALSE; Stream_Write_UINT16(s, format->wFormatTag); /* wFormatTag (WAVE_FORMAT_PCM) */ Stream_Write_UINT16(s, format->nChannels); /* nChannels */ Stream_Write_UINT32(s, format->nSamplesPerSec); /* nSamplesPerSec */ Stream_Write_UINT32(s, format->nAvgBytesPerSec); /* nAvgBytesPerSec */ Stream_Write_UINT16(s, format->nBlockAlign); /* nBlockAlign */ Stream_Write_UINT16(s, format->wBitsPerSample); /* wBitsPerSample */ Stream_Write_UINT16(s, format->cbSize); /* cbSize */ if (format->cbSize > 0) Stream_Write(s, format->data, format->cbSize); return TRUE; } BOOL audio_format_copy(const AUDIO_FORMAT* WINPR_RESTRICT srcFormat, AUDIO_FORMAT* WINPR_RESTRICT dstFormat) { if (!srcFormat || !dstFormat) return FALSE; *dstFormat = *srcFormat; if (srcFormat->cbSize > 0) { dstFormat->data = malloc(srcFormat->cbSize); if (!dstFormat->data) return FALSE; memcpy(dstFormat->data, srcFormat->data, dstFormat->cbSize); } return TRUE; } BOOL audio_format_compatible(const AUDIO_FORMAT* with, const AUDIO_FORMAT* what) { if (!with || !what) return FALSE; if (with->wFormatTag != WAVE_FORMAT_UNKNOWN) { if (with->wFormatTag != what->wFormatTag) return FALSE; } if (with->nChannels != 0) { if (with->nChannels != what->nChannels) return FALSE; } if (with->nSamplesPerSec != 0) { if (with->nSamplesPerSec != what->nSamplesPerSec) return FALSE; } if (with->wBitsPerSample != 0) { if (with->wBitsPerSample != what->wBitsPerSample) return FALSE; } return TRUE; } static BOOL audio_format_valid(const AUDIO_FORMAT* format) { if (!format) return FALSE; if (format->nChannels == 0) return FALSE; if (format->nSamplesPerSec == 0) return FALSE; return TRUE; } AUDIO_FORMAT* audio_format_new(void) { return audio_formats_new(1); } AUDIO_FORMAT* audio_formats_new(size_t count) { return calloc(count, sizeof(AUDIO_FORMAT)); } void audio_format_free(AUDIO_FORMAT* format) { if (format) free(format->data); } void audio_formats_free(AUDIO_FORMAT* formats, size_t count) { if (formats) { for (size_t index = 0; index < count; index++) { AUDIO_FORMAT* format = &formats[index]; audio_format_free(format); } free(formats); } }