/* * OpenAL Helpers * * Copyright (c) 2011 by Chris Robinson * * Permission is hereby granted, free of charge, to any person obtaining a copy * of this software and associated documentation files (the "Software"), to deal * in the Software without restriction, including without limitation the rights * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell * copies of the Software, and to permit persons to whom the Software is * furnished to do so, subject to the following conditions: * * The above copyright notice and this permission notice shall be included in * all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN * THE SOFTWARE. */ /* This file contains routines to help with some menial OpenAL-related tasks, * such as opening a device and setting up a context, closing the device and * destroying its context, converting between frame counts and byte lengths, * finding an appropriate buffer format, and getting readable strings for * channel configs and sample types. */ package com.jogamp.openal.util; import com.jogamp.openal.AL; import com.jogamp.openal.ALConstants; import static com.jogamp.openal.ALConstants.*; import com.jogamp.openal.ALExt; // import com.jogamp.openal.ALExtConstants; import static com.jogamp.openal.ALExtConstants.*; /* This file contains routines to help with some menial OpenAL-related tasks, * such as converting between frame counts and byte lengths, * finding an appropriate buffer format, and getting readable strings for * channel configs and sample types. */ public class ALHelpers { /** * Returns a compatible AL buffer format given the AL channel layout and * AL sample type. If hasSOFTBufferSamples is true, * it will be called to find the closest-matching format from * AL_SOFT_buffer_samples. *

* Returns {@link ALConstants#AL_NONE} if no supported format can be found. *

* * @param alChannelLayout AL channel layout, see {@link #getDefaultALChannelLayout(int)} * @param alSampleType AL sample type, see {@link #getALSampleType(int, boolean, boolean)}. * @param hasSOFTBufferSamples true if having extension AL_SOFT_buffer_samples, otherwise false * @param al AL instance * @param alExt ALExt instance * @return AL buffer format */ public static final int getALFormat(final int alChannelLayout, final int alSampleType, final boolean hasSOFTBufferSamples, final AL al, final ALExt alExt) { int format = AL_NONE; /* If using AL_SOFT_buffer_samples, try looking through its formats */ if(hasSOFTBufferSamples) { /* AL_SOFT_buffer_samples is more lenient with matching formats. The * specified sample type does not need to match the returned format, * but it is nice to try to get something close. */ if(alSampleType == AL_UNSIGNED_BYTE_SOFT || alSampleType == AL_BYTE_SOFT) { if(alChannelLayout == AL_MONO_SOFT) format = AL_MONO8_SOFT; else if(alChannelLayout == AL_STEREO_SOFT) format = AL_STEREO8_SOFT; else if(alChannelLayout == AL_QUAD_SOFT) format = AL_QUAD8_SOFT; else if(alChannelLayout == AL_5POINT1_SOFT) format = AL_5POINT1_8_SOFT; else if(alChannelLayout == AL_6POINT1_SOFT) format = AL_6POINT1_8_SOFT; else if(alChannelLayout == AL_7POINT1_SOFT) format = AL_7POINT1_8_SOFT; } else if(alSampleType == AL_UNSIGNED_SHORT_SOFT || alSampleType == AL_SHORT_SOFT) { if(alChannelLayout == AL_MONO_SOFT) format = AL_MONO16_SOFT; else if(alChannelLayout == AL_STEREO_SOFT) format = AL_STEREO16_SOFT; else if(alChannelLayout == AL_QUAD_SOFT) format = AL_QUAD16_SOFT; else if(alChannelLayout == AL_5POINT1_SOFT) format = AL_5POINT1_16_SOFT; else if(alChannelLayout == AL_6POINT1_SOFT) format = AL_6POINT1_16_SOFT; else if(alChannelLayout == AL_7POINT1_SOFT) format = AL_7POINT1_16_SOFT; } else if(alSampleType == AL_UNSIGNED_BYTE3_SOFT || alSampleType == AL_BYTE3_SOFT || alSampleType == AL_UNSIGNED_INT_SOFT || alSampleType == AL_INT_SOFT || alSampleType == AL_FLOAT_SOFT || alSampleType == AL_DOUBLE_SOFT) { if(alChannelLayout == AL_MONO_SOFT) format = AL_MONO32F_SOFT; else if(alChannelLayout == AL_STEREO_SOFT) format = AL_STEREO32F_SOFT; else if(alChannelLayout == AL_QUAD_SOFT) format = AL_QUAD32F_SOFT; else if(alChannelLayout == AL_5POINT1_SOFT) format = AL_5POINT1_32F_SOFT; else if(alChannelLayout == AL_6POINT1_SOFT) format = AL_6POINT1_32F_SOFT; else if(alChannelLayout == AL_7POINT1_SOFT) format = AL_7POINT1_32F_SOFT; } if(format != AL_NONE && !alExt.alIsBufferFormatSupportedSOFT(format)) format = AL_NONE; /* A matching format was not found or supported. Try 32-bit float. */ if(format == AL_NONE) { if(alChannelLayout == AL_MONO_SOFT) format = AL_MONO32F_SOFT; else if(alChannelLayout == AL_STEREO_SOFT) format = AL_STEREO32F_SOFT; else if(alChannelLayout == AL_QUAD_SOFT) format = AL_QUAD32F_SOFT; else if(alChannelLayout == AL_5POINT1_SOFT) format = AL_5POINT1_32F_SOFT; else if(alChannelLayout == AL_6POINT1_SOFT) format = AL_6POINT1_32F_SOFT; else if(alChannelLayout == AL_7POINT1_SOFT) format = AL_7POINT1_32F_SOFT; if(format != AL_NONE && !alExt.alIsBufferFormatSupportedSOFT(format)) format = AL_NONE; } /* 32-bit float not supported. Try 16-bit int. */ if(format == AL_NONE) { if(alChannelLayout == AL_MONO_SOFT) format = AL_MONO16_SOFT; else if(alChannelLayout == AL_STEREO_SOFT) format = AL_STEREO16_SOFT; else if(alChannelLayout == AL_QUAD_SOFT) format = AL_QUAD16_SOFT; else if(alChannelLayout == AL_5POINT1_SOFT) format = AL_5POINT1_16_SOFT; else if(alChannelLayout == AL_6POINT1_SOFT) format = AL_6POINT1_16_SOFT; else if(alChannelLayout == AL_7POINT1_SOFT) format = AL_7POINT1_16_SOFT; if(format != AL_NONE && !alExt.alIsBufferFormatSupportedSOFT(format)) format = AL_NONE; } /* 16-bit int not supported. Try 8-bit int. */ if(format == AL_NONE) { if(alChannelLayout == AL_MONO_SOFT) format = AL_MONO8_SOFT; else if(alChannelLayout == AL_STEREO_SOFT) format = AL_STEREO8_SOFT; else if(alChannelLayout == AL_QUAD_SOFT) format = AL_QUAD8_SOFT; else if(alChannelLayout == AL_5POINT1_SOFT) format = AL_5POINT1_8_SOFT; else if(alChannelLayout == AL_6POINT1_SOFT) format = AL_6POINT1_8_SOFT; else if(alChannelLayout == AL_7POINT1_SOFT) format = AL_7POINT1_8_SOFT; if(format != AL_NONE && !alExt.alIsBufferFormatSupportedSOFT(format)) format = AL_NONE; } return format; } /* We use the AL_EXT_MCFORMATS extension to provide output of Quad, 5.1, * and 7.1 channel configs, AL_EXT_FLOAT32 for 32-bit float samples, and * AL_EXT_DOUBLE for 64-bit float samples. */ if(alSampleType == AL_UNSIGNED_BYTE_SOFT) { if(alChannelLayout == AL_MONO_SOFT) format = AL_FORMAT_MONO8; else if(alChannelLayout == AL_STEREO_SOFT) format = AL_FORMAT_STEREO8; else if(al.alIsExtensionPresent("AL_EXT_MCFORMATS")) { if(alChannelLayout == AL_QUAD_SOFT) format = al.alGetEnumValue("AL_FORMAT_QUAD8"); else if(alChannelLayout == AL_5POINT1_SOFT) format = al.alGetEnumValue("AL_FORMAT_51CHN8"); else if(alChannelLayout == AL_6POINT1_SOFT) format = al.alGetEnumValue("AL_FORMAT_61CHN8"); else if(alChannelLayout == AL_7POINT1_SOFT) format = al.alGetEnumValue("AL_FORMAT_71CHN8"); } } else if(alSampleType == AL_SHORT_SOFT) { if(alChannelLayout == AL_MONO_SOFT) format = AL_FORMAT_MONO16; else if(alChannelLayout == AL_STEREO_SOFT) format = AL_FORMAT_STEREO16; else if(al.alIsExtensionPresent("AL_EXT_MCFORMATS")) { if(alChannelLayout == AL_QUAD_SOFT) format = al.alGetEnumValue("AL_FORMAT_QUAD16"); else if(alChannelLayout == AL_5POINT1_SOFT) format = al.alGetEnumValue("AL_FORMAT_51CHN16"); else if(alChannelLayout == AL_6POINT1_SOFT) format = al.alGetEnumValue("AL_FORMAT_61CHN16"); else if(alChannelLayout == AL_7POINT1_SOFT) format = al.alGetEnumValue("AL_FORMAT_71CHN16"); } } else if(alSampleType == AL_FLOAT_SOFT && al.alIsExtensionPresent("AL_EXT_FLOAT32")) { if(alChannelLayout == AL_MONO_SOFT) format = al.alGetEnumValue("AL_FORMAT_MONO_FLOAT32"); else if(alChannelLayout == AL_STEREO_SOFT) format = al.alGetEnumValue("AL_FORMAT_STEREO_FLOAT32"); else if(al.alIsExtensionPresent("AL_EXT_MCFORMATS")) { if(alChannelLayout == AL_QUAD_SOFT) format = al.alGetEnumValue("AL_FORMAT_QUAD32"); else if(alChannelLayout == AL_5POINT1_SOFT) format = al.alGetEnumValue("AL_FORMAT_51CHN32"); else if(alChannelLayout == AL_6POINT1_SOFT) format = al.alGetEnumValue("AL_FORMAT_61CHN32"); else if(alChannelLayout == AL_7POINT1_SOFT) format = al.alGetEnumValue("AL_FORMAT_71CHN32"); } } else if(alSampleType == AL_DOUBLE_SOFT && al.alIsExtensionPresent("AL_EXT_DOUBLE")) { if(alChannelLayout == AL_MONO_SOFT) format = al.alGetEnumValue("AL_FORMAT_MONO_DOUBLE"); else if(alChannelLayout == AL_STEREO_SOFT) format = al.alGetEnumValue("AL_FORMAT_STEREO_DOUBLE"); } /* NOTE: It seems OSX returns -1 from alGetEnumValue for unknown enums, as * opposed to 0. Correct it. */ if(format == -1) { format = AL_NONE; } return format; } /** * Returns the default AL channel layout matching the given channel count, or {@link ALConstants#AL_NONE}. * @param channelCount number of channels * @param signed true if signed number, false for unsigned * @param fixedP true for fixed point value, false for floating point value with a sampleSize of 32 (float) or 64 (double) */ public static final int getDefaultALChannelLayout(final int channelCount) { switch(channelCount) { case 1: return AL_MONO_SOFT; case 2: return AL_STEREO_SOFT; // case 2: return AL_REAR_SOFT; case 4: return AL_QUAD_SOFT; case 6: return AL_5POINT1_SOFT; case 7: return AL_6POINT1_SOFT; case 8: return AL_7POINT1_SOFT; } return AL_NONE; } /** * Returns the readable name of the given AL channel layout */ public static final String alChannelLayoutName(final int alChannelLayout) { switch(alChannelLayout) { case AL_MONO_SOFT: return "Mono"; case AL_STEREO_SOFT: return "Stereo"; case AL_REAR_SOFT: return "Rear"; case AL_QUAD_SOFT: return "Quadraphonic"; case AL_5POINT1_SOFT: return "5.1 Surround"; case AL_6POINT1_SOFT: return "6.1 Surround"; case AL_7POINT1_SOFT: return "7.1 Surround"; } return "Unknown AL-Channel-Layout 0x"+Integer.toHexString(alChannelLayout); } /** * Returns the AL sample type matching the given audio type attributes, or {@link ALConstants#AL_NONE}. * @param sampleSize sample size in bits * @param signed true if signed number, false for unsigned * @param fixedP true for fixed point value, false for floating point value with a sampleSize of 32 (float) or 64 (double) */ public static final int getALSampleType(final int sampleSize, boolean signed, boolean fixedP) { if( fixedP ) { if( signed ) { switch( sampleSize ) { case 8: return AL_BYTE_SOFT; case 16: return AL_SHORT_SOFT; case 32: return AL_INT_SOFT; } } else { switch( sampleSize ) { case 8: return AL_UNSIGNED_BYTE_SOFT; case 16: return AL_UNSIGNED_SHORT_SOFT; case 32: return AL_UNSIGNED_INT_SOFT; } } } else { if( signed ) { switch( sampleSize ) { case 32: return AL_FLOAT_SOFT; case 64: return AL_DOUBLE_SOFT; } } } return AL_NONE; } /** * Returns the readable name of the given AL sample type */ public static final String alSampleTypeName(final int alSampleType) { switch(alSampleType) { case AL_BYTE_SOFT: return "S8"; case AL_UNSIGNED_BYTE_SOFT: return "U8"; case AL_SHORT_SOFT: return "S16"; case AL_UNSIGNED_SHORT_SOFT: return "U16"; case AL_INT_SOFT: return "S32"; case AL_UNSIGNED_INT_SOFT: return "U32"; case AL_FLOAT_SOFT: return "F32"; case AL_DOUBLE_SOFT: return "F64"; } return "Unknown AL-Type 0x"+Integer.toHexString(alSampleType); } /** * Returns the byte size of the given AL sample type * @throws IllegalArgumentException for unknown alChannelLayout or alSampleType values. */ public static final int sizeOfALSampleType(final int alSampleType) throws IllegalArgumentException { switch(alSampleType) { case AL_BYTE_SOFT: case AL_UNSIGNED_BYTE_SOFT: return 1; case AL_SHORT_SOFT: case AL_UNSIGNED_SHORT_SOFT: return 2; case AL_INT_SOFT: case AL_UNSIGNED_INT_SOFT: case AL_FLOAT_SOFT: return 4; case AL_DOUBLE_SOFT: return 8; default: throw new IllegalArgumentException("Unknown al-type 0x"+Integer.toHexString(alSampleType)); } } /** * @param sampleCount number of samples per channel * @param alChannelLayout AL channel layout * @param alSampleType AL sample type * @return bytes count required * @throws IllegalArgumentException for unknown alChannelLayout or alSampleType values. */ public static final int samplesToByteCount(int sampleCount, final int alChannelLayout, final int alSampleType) throws IllegalArgumentException { switch(alChannelLayout) { case AL_MONO_SOFT: sampleCount *= 1; break; case AL_STEREO_SOFT: sampleCount *= 2; break; case AL_REAR_SOFT: sampleCount *= 2; break; case AL_QUAD_SOFT: sampleCount *= 4; break; case AL_5POINT1_SOFT: sampleCount *= 6; break; case AL_6POINT1_SOFT: sampleCount *= 7; break; case AL_7POINT1_SOFT: sampleCount *= 8; break; default: throw new IllegalArgumentException("Unknown al-channel-layout 0x"+Integer.toHexString(alChannelLayout)); } switch(alSampleType) { case AL_BYTE_SOFT: case AL_UNSIGNED_BYTE_SOFT: break; case AL_SHORT_SOFT: case AL_UNSIGNED_SHORT_SOFT: sampleCount *= 2; break; case AL_INT_SOFT: case AL_UNSIGNED_INT_SOFT: case AL_FLOAT_SOFT: sampleCount *= 4; break; case AL_DOUBLE_SOFT: sampleCount *= 8; break; default: throw new IllegalArgumentException("Unknown al-type 0x"+Integer.toHexString(alSampleType)); } return sampleCount; } public static final int bytesToSampleCount(final int byteCount, final int alChannelLayout, final int alSampleType) { return byteCount / samplesToByteCount(1, alChannelLayout, alSampleType); } }