|
|
|
@ -12,31 +12,216 @@ namespace DotSDL.Audio {
|
|
|
|
|
/// <param name="buffer">A DotSDL <see cref="AudioBuffer"/>.</param> |
|
|
|
|
/// <param name="stream">The byte array to write the converted data to.</param> |
|
|
|
|
/// <param name="format">The SDL <see cref="AudioFormat"/> to convert to.</param> |
|
|
|
|
internal static void ConvertFormat(AudioBuffer buffer, ref byte[] stream, SdlAudio.AudioFormat format) { |
|
|
|
|
/// <param name="channels">The number of channels that the final mix should be converted to.</param> |
|
|
|
|
internal static void ConvertFormat(ref AudioBuffer buffer, ref byte[] stream, SdlAudio.AudioFormat format, ChannelCount channels) { |
|
|
|
|
double[] samples; |
|
|
|
|
|
|
|
|
|
switch(channels) { |
|
|
|
|
case ChannelCount.Mono: |
|
|
|
|
ToMono(ref buffer, out samples); |
|
|
|
|
break; |
|
|
|
|
case ChannelCount.Stereo: |
|
|
|
|
ToStereo(ref buffer, out samples); |
|
|
|
|
break; |
|
|
|
|
case ChannelCount.Quadraphonic: |
|
|
|
|
ToQuadraphonic(ref buffer, out samples); |
|
|
|
|
break; |
|
|
|
|
case ChannelCount.FiveOne: |
|
|
|
|
ToFiveOne(ref buffer, out samples); |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
throw new NotImplementedException(); |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
switch(format) { |
|
|
|
|
case SdlAudio.AudioFormat.SignedByte: |
|
|
|
|
case SdlAudio.AudioFormat.UnsignedByte: |
|
|
|
|
ToInt8(buffer, ref stream); |
|
|
|
|
ToInt8(ref samples, ref stream); |
|
|
|
|
break; |
|
|
|
|
case SdlAudio.AudioFormat.SignedShortLittleEndian: |
|
|
|
|
case SdlAudio.AudioFormat.UnsignedShortLittleEndian: |
|
|
|
|
ToInt16(buffer, ref stream, true); |
|
|
|
|
ToInt16(ref samples, ref stream, true); |
|
|
|
|
break; |
|
|
|
|
case SdlAudio.AudioFormat.SignedShortBigEndian: |
|
|
|
|
case SdlAudio.AudioFormat.UnsignedShortBigEndian: |
|
|
|
|
ToInt16(buffer, ref stream, false); |
|
|
|
|
ToInt16(ref samples, ref stream, false); |
|
|
|
|
break; |
|
|
|
|
case SdlAudio.AudioFormat.SignedIntLittleEndian: |
|
|
|
|
ToInt32(buffer, ref stream, true); |
|
|
|
|
ToInt32(ref samples, ref stream, true); |
|
|
|
|
break; |
|
|
|
|
case SdlAudio.AudioFormat.SignedIntBigEndian: |
|
|
|
|
ToInt32(buffer, ref stream, false); |
|
|
|
|
ToInt32(ref samples, ref stream, false); |
|
|
|
|
break; |
|
|
|
|
case SdlAudio.AudioFormat.FloatLittleEndian: |
|
|
|
|
ToFloat32(buffer, ref stream, true); |
|
|
|
|
ToFloat32(ref samples, ref stream, true); |
|
|
|
|
break; |
|
|
|
|
case SdlAudio.AudioFormat.FloatBigEndian: |
|
|
|
|
ToFloat32(buffer, ref stream, false); |
|
|
|
|
ToFloat32(ref samples, ref stream, false); |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
throw new NotImplementedException(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// Converts the contents of an <see cref="AudioBuffer"/> into a monophonic stream. |
|
|
|
|
/// </summary> |
|
|
|
|
/// <param name="buffer">An <see cref="AudioBuffer"/> to process.</param> |
|
|
|
|
/// <param name="samples">The double array to write the converted data to.</param> |
|
|
|
|
private static void ToMono(ref AudioBuffer buffer, out double[] samples) { |
|
|
|
|
samples = new double[buffer.Length * (int)ChannelCount.Mono]; |
|
|
|
|
|
|
|
|
|
switch(buffer.Channels) { |
|
|
|
|
case ChannelCount.Mono: |
|
|
|
|
samples = buffer.Samples[Channel.Mono]; |
|
|
|
|
break; |
|
|
|
|
case ChannelCount.Stereo: |
|
|
|
|
for(var i = 0; i < buffer.Length; i++) |
|
|
|
|
samples[i] = (buffer.Samples[Channel.StereoLeft][i] |
|
|
|
|
+ buffer.Samples[Channel.StereoRight][i]) / 2; |
|
|
|
|
break; |
|
|
|
|
case ChannelCount.Quadraphonic: |
|
|
|
|
for(var i = 0; i < buffer.Length; i++) |
|
|
|
|
samples[i] = (buffer.Samples[Channel.QuadFrontLeft][i] |
|
|
|
|
+ buffer.Samples[Channel.QuadFrontRight][i] |
|
|
|
|
+ buffer.Samples[Channel.QuadRearLeft][i] |
|
|
|
|
+ buffer.Samples[Channel.QuadRearRight][i]) / 4; |
|
|
|
|
break; |
|
|
|
|
case ChannelCount.FiveOne: |
|
|
|
|
throw new NotImplementedException(); |
|
|
|
|
default: |
|
|
|
|
throw new NotImplementedException(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// Converts the contents of an <see cref="AudioBuffer"/> into a stereo stream. |
|
|
|
|
/// </summary> |
|
|
|
|
/// <param name="buffer">An <see cref="AudioBuffer"/> to process.</param> |
|
|
|
|
/// <param name="samples">The double array to write the converted data to</param> |
|
|
|
|
private static void ToStereo(ref AudioBuffer buffer, out double[] samples) { |
|
|
|
|
const int ch = (int)ChannelCount.Stereo; |
|
|
|
|
samples = new double[buffer.Length * (int)ChannelCount.Stereo]; |
|
|
|
|
|
|
|
|
|
switch(buffer.Channels) { |
|
|
|
|
case ChannelCount.Mono: |
|
|
|
|
for(var i = 0; i < buffer.Length; i++) { |
|
|
|
|
samples[i * ch] = buffer.Samples[Channel.Mono][i]; |
|
|
|
|
samples[i * ch + 1] = buffer.Samples[Channel.Mono][i]; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case ChannelCount.Stereo: |
|
|
|
|
for(var i = 0; i < buffer.Length; i++) { |
|
|
|
|
samples[i * ch] = buffer.Samples[Channel.StereoLeft][i]; |
|
|
|
|
samples[i * ch + 1] = buffer.Samples[Channel.StereoRight][i]; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case ChannelCount.Quadraphonic: |
|
|
|
|
for(var i = 0; i < buffer.Length; i++) { |
|
|
|
|
samples[i * ch] = (buffer.Samples[Channel.QuadFrontLeft][i] |
|
|
|
|
+ buffer.Samples[Channel.QuadRearLeft][i]) / 2; |
|
|
|
|
samples[i * ch + 1] = (buffer.Samples[Channel.QuadFrontRight][i] |
|
|
|
|
+ buffer.Samples[Channel.QuadRearRight][i]) / 2; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case ChannelCount.FiveOne: |
|
|
|
|
throw new NotImplementedException(); |
|
|
|
|
default: |
|
|
|
|
throw new NotImplementedException(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// Converts the contents of an <see cref="AudioBuffer"/> into a quadrophonic stream. |
|
|
|
|
/// </summary> |
|
|
|
|
/// <param name="buffer">An <see cref="AudioBuffer"/> to process.</param> |
|
|
|
|
/// <param name="samples">The double array to write the converted data to.</param> |
|
|
|
|
private static void ToQuadraphonic(ref AudioBuffer buffer, out double[] samples) { |
|
|
|
|
const int ch = (int)ChannelCount.Quadraphonic; |
|
|
|
|
samples = new double[buffer.Length * (int)ChannelCount.Quadraphonic]; |
|
|
|
|
|
|
|
|
|
switch(buffer.Channels) { |
|
|
|
|
case ChannelCount.Mono: |
|
|
|
|
for(var i = 0; i < buffer.Length; i++) { |
|
|
|
|
samples[i * ch] = buffer.Samples[Channel.Mono][i]; |
|
|
|
|
samples[i * ch + 1] = buffer.Samples[Channel.Mono][i]; |
|
|
|
|
samples[i * ch + 2] = buffer.Samples[Channel.Mono][i]; |
|
|
|
|
samples[i * ch + 3] = buffer.Samples[Channel.Mono][i]; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case ChannelCount.Stereo: |
|
|
|
|
for(var i = 0; i < buffer.Length; i++) { |
|
|
|
|
samples[i * ch] = buffer.Samples[Channel.StereoLeft][i]; |
|
|
|
|
samples[i * ch + 1] = buffer.Samples[Channel.StereoRight][i]; |
|
|
|
|
samples[i * ch + 2] = buffer.Samples[Channel.StereoLeft][i]; |
|
|
|
|
samples[i * ch + 3] = buffer.Samples[Channel.StereoRight][i]; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case ChannelCount.Quadraphonic: |
|
|
|
|
for(var i = 0; i < buffer.Length; i++) { |
|
|
|
|
samples[i * ch] = buffer.Samples[Channel.QuadFrontLeft][i]; |
|
|
|
|
samples[i * ch + 1] = buffer.Samples[Channel.QuadFrontRight][i]; |
|
|
|
|
samples[i * ch + 2] = buffer.Samples[Channel.QuadRearLeft][i]; |
|
|
|
|
samples[i * ch + 3] = buffer.Samples[Channel.QuadRearRight][i]; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case ChannelCount.FiveOne: |
|
|
|
|
throw new NotImplementedException(); |
|
|
|
|
default: |
|
|
|
|
throw new NotImplementedException(); |
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// Converts the contents of an <see cref="AudioBuffer"/> into a 5.1 stream. |
|
|
|
|
/// </summary> |
|
|
|
|
/// <param name="buffer">An <see cref="AudioBuffer"/> to process.</param> |
|
|
|
|
/// <param name="samples">The double array to write the converted data to.</param> |
|
|
|
|
private static void ToFiveOne(ref AudioBuffer buffer, out double[] samples) { |
|
|
|
|
const int ch = (int)ChannelCount.FiveOne; |
|
|
|
|
samples = new double[buffer.Length * (int)ChannelCount.FiveOne]; |
|
|
|
|
|
|
|
|
|
// TODO: Improve upmixing. |
|
|
|
|
switch(buffer.Channels) { |
|
|
|
|
case ChannelCount.Mono: |
|
|
|
|
for(var i = 0; i < buffer.Length; i++) { |
|
|
|
|
samples[i * ch] = buffer.Samples[Channel.Mono][i]; |
|
|
|
|
samples[i * ch + 1] = buffer.Samples[Channel.Mono][i]; |
|
|
|
|
samples[i * ch + 2] = 0; // Center channel. |
|
|
|
|
samples[i * ch + 3] = 0; // LFE. |
|
|
|
|
samples[i * ch + 4] = buffer.Samples[Channel.Mono][i]; |
|
|
|
|
samples[i * ch + 5] = buffer.Samples[Channel.Mono][i]; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case ChannelCount.Stereo: |
|
|
|
|
for(var i = 0; i < buffer.Length; i++) { |
|
|
|
|
samples[i * ch] = buffer.Samples[Channel.StereoLeft][i]; |
|
|
|
|
samples[i * ch + 1] = buffer.Samples[Channel.StereoRight][i]; |
|
|
|
|
samples[i * ch + 2] = 0; // Center channel. |
|
|
|
|
samples[i * ch + 3] = 0; // LFE. |
|
|
|
|
samples[i * ch + 4] = buffer.Samples[Channel.StereoLeft][i]; |
|
|
|
|
samples[i * ch + 5] = buffer.Samples[Channel.StereoRight][i]; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case ChannelCount.Quadraphonic: |
|
|
|
|
for(var i = 0; i < buffer.Length; i++) { |
|
|
|
|
samples[i * ch] = buffer.Samples[Channel.QuadFrontLeft][i]; |
|
|
|
|
samples[i * ch + 1] = buffer.Samples[Channel.QuadFrontRight][i]; |
|
|
|
|
samples[i * ch + 2] = 0; // Center channel. |
|
|
|
|
samples[i * ch + 3] = 0; // LFE. |
|
|
|
|
samples[i * ch + 4] = buffer.Samples[Channel.QuadRearLeft][i]; |
|
|
|
|
samples[i * ch + 5] = buffer.Samples[Channel.QuadRearRight][i]; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
case ChannelCount.FiveOne: |
|
|
|
|
for(var i = 0; i < buffer.Length; i++) { |
|
|
|
|
samples[i * ch] = buffer.Samples[Channel.FiveOneFrontLeft][i]; |
|
|
|
|
samples[i * ch + 1] = buffer.Samples[Channel.FiveOneFrontRight][i]; |
|
|
|
|
samples[i * ch + 2] = buffer.Samples[Channel.FiveOneCenter][i]; |
|
|
|
|
samples[i * ch + 3] = buffer.Samples[Channel.FiveOneLfe][i]; |
|
|
|
|
samples[i * ch + 4] = buffer.Samples[Channel.FiveOneRearLeft][i]; |
|
|
|
|
samples[i * ch + 5] = buffer.Samples[Channel.FiveOneRearRight][i]; |
|
|
|
|
} |
|
|
|
|
break; |
|
|
|
|
default: |
|
|
|
|
throw new NotImplementedException(); |
|
|
|
@ -46,11 +231,11 @@ namespace DotSDL.Audio {
|
|
|
|
|
/// <summary> |
|
|
|
|
/// Converts an audio buffer to a byte array with 8-bit samples. |
|
|
|
|
/// </summary> |
|
|
|
|
/// <param name="buffer">A DotSDL <see cref="AudioBuffer"/>.</param> |
|
|
|
|
/// <param name="samples">An array of double-precision floating point samples.</param> |
|
|
|
|
/// <param name="stream">The byte array to write the converted data to.</param> |
|
|
|
|
private static void ToInt8(AudioBuffer buffer, ref byte[] stream) { |
|
|
|
|
for(var i = 0; i < buffer.Samples.Length; i++) { |
|
|
|
|
var sample = buffer.Samples[i]; |
|
|
|
|
private static void ToInt8(ref double[] samples, ref byte[] stream) { |
|
|
|
|
for(var i = 0; i < samples.Length; i++) { |
|
|
|
|
var sample = samples[i]; |
|
|
|
|
var newSample = (sbyte)(sample * sbyte.MaxValue); |
|
|
|
|
stream[i] = (byte)newSample; |
|
|
|
|
} |
|
|
|
@ -59,22 +244,22 @@ namespace DotSDL.Audio {
|
|
|
|
|
/// <summary> |
|
|
|
|
/// Converts an audio buffer to a byte array with 16-bit samples. |
|
|
|
|
/// </summary> |
|
|
|
|
/// <param name="buffer">A DotSDL <see cref="AudioBuffer"/>.</param> |
|
|
|
|
/// <param name="samples">An array of double-precision floating point samples.</param> |
|
|
|
|
/// <param name="stream">The byte array to write the converted data to.</param> |
|
|
|
|
/// <param name="littleEndian">Indicates whether the target byte stream should be little endian.</param> |
|
|
|
|
private static void ToInt16(AudioBuffer buffer, ref byte[] stream, bool littleEndian) { |
|
|
|
|
private static void ToInt16(ref double[] samples, ref byte[] stream, bool littleEndian) { |
|
|
|
|
if((BitConverter.IsLittleEndian && littleEndian) || (!BitConverter.IsLittleEndian && !littleEndian)) { |
|
|
|
|
// Sample endian == system endian. |
|
|
|
|
for(var i = 0; i < buffer.Samples.Length; i++) { |
|
|
|
|
var sample = buffer.Samples[i]; |
|
|
|
|
for(var i = 0; i < samples.Length; i++) { |
|
|
|
|
var sample = samples[i]; |
|
|
|
|
var newSample = (short)(sample * short.MaxValue); |
|
|
|
|
stream[i * 2] = (byte)newSample; |
|
|
|
|
stream[i * 2 + 1] = (byte)(newSample >> 8); |
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
// Sample endian != system endian. Flip bytes. |
|
|
|
|
for(var i = 0; i < buffer.Samples.Length; i++) { |
|
|
|
|
var sample = buffer.Samples[i]; |
|
|
|
|
for(var i = 0; i < samples.Length; i++) { |
|
|
|
|
var sample = samples[i]; |
|
|
|
|
var newSample = (short)(sample * short.MaxValue); |
|
|
|
|
stream[i * 2] = (byte)(newSample >> 8); |
|
|
|
|
stream[i * 2 + 1] = (byte)newSample; |
|
|
|
@ -83,16 +268,16 @@ namespace DotSDL.Audio {
|
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
/// <summary> |
|
|
|
|
/// Converts an audio buffer to a byte array with little-endian 32-bit samples. |
|
|
|
|
/// Converts an audio buffer to a byte array with 32-bit integer samples. |
|
|
|
|
/// </summary> |
|
|
|
|
/// <param name="buffer">A DotSDL <see cref="AudioBuffer"/>.</param> |
|
|
|
|
/// <param name="samples">An array of double-precision floating point samples.</param> |
|
|
|
|
/// <param name="stream">The byte array to write the converted data to.</param> |
|
|
|
|
/// <param name="littleEndian">Indicates whether the target byte stream should be little endian.</param> |
|
|
|
|
private static void ToInt32(AudioBuffer buffer, ref byte[] stream, bool littleEndian) { |
|
|
|
|
private static void ToInt32(ref double[] samples, ref byte[] stream, bool littleEndian) { |
|
|
|
|
if((BitConverter.IsLittleEndian && littleEndian) || (!BitConverter.IsLittleEndian && !littleEndian)) { |
|
|
|
|
// Sample endian == system endian. |
|
|
|
|
for(var i = 0; i < buffer.Samples.Length; i++) { |
|
|
|
|
var sample = buffer.Samples[i]; |
|
|
|
|
for(var i = 0; i < samples.Length; i++) { |
|
|
|
|
var sample = samples[i]; |
|
|
|
|
var newSample = (int)(sample * int.MaxValue); |
|
|
|
|
stream[i * 4] = (byte)newSample; |
|
|
|
|
stream[i * 4 + 1] = (byte)(newSample >> 8); |
|
|
|
@ -101,8 +286,8 @@ namespace DotSDL.Audio {
|
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
// Sample endian != system endian. Flip bytes. |
|
|
|
|
for(var i = 0; i < buffer.Samples.Length; i++) { |
|
|
|
|
var sample = buffer.Samples[i]; |
|
|
|
|
for(var i = 0; i < samples.Length; i++) { |
|
|
|
|
var sample = samples[i]; |
|
|
|
|
var newSample = (int)(sample * int.MaxValue); |
|
|
|
|
stream[i * 4] = (byte)(newSample >> 24); |
|
|
|
|
stream[i * 4 + 1] = (byte)(newSample >> 16); |
|
|
|
@ -112,11 +297,17 @@ namespace DotSDL.Audio {
|
|
|
|
|
} |
|
|
|
|
} |
|
|
|
|
|
|
|
|
|
private static void ToFloat32(AudioBuffer buffer, ref byte[] stream, bool littleEndian) { |
|
|
|
|
/// <summary> |
|
|
|
|
/// Converts an audio buffer to a byte array with little-endian floating point samples. |
|
|
|
|
/// </summary> |
|
|
|
|
/// <param name="samples">An array of double-precision floating point samples.</param> |
|
|
|
|
/// <param name="stream">The byte array to write the converted data to.</param> |
|
|
|
|
/// <param name="littleEndian">Indicates whether the target byte stream should be little endian.</param> |
|
|
|
|
private static void ToFloat32(ref double[] samples, ref byte[] stream, bool littleEndian) { |
|
|
|
|
if((BitConverter.IsLittleEndian && littleEndian) || (!BitConverter.IsLittleEndian && !littleEndian)) { |
|
|
|
|
// Sample endian == system endian. |
|
|
|
|
for(var i = 0; i < buffer.Samples.Length; i++) { |
|
|
|
|
var sample = (float)buffer.Samples[i]; |
|
|
|
|
for(var i = 0; i < samples.Length; i++) { |
|
|
|
|
var sample = (float)samples[i]; |
|
|
|
|
var newSample = BitConverter.GetBytes(sample); |
|
|
|
|
stream[i * 4] = newSample[0]; |
|
|
|
|
stream[i * 4 + 1] = newSample[1]; |
|
|
|
@ -125,8 +316,8 @@ namespace DotSDL.Audio {
|
|
|
|
|
} |
|
|
|
|
} else { |
|
|
|
|
// Sample endian != system endian. Flip bytes. |
|
|
|
|
for(var i = 0; i < buffer.Samples.Length; i++) { |
|
|
|
|
var sample = (float)buffer.Samples[i]; |
|
|
|
|
for(var i = 0; i < samples.Length; i++) { |
|
|
|
|
var sample = (float)samples[i]; |
|
|
|
|
var newSample = BitConverter.GetBytes(sample); |
|
|
|
|
stream[i * 4] = newSample[3]; |
|
|
|
|
stream[i * 4 + 1] = newSample[2]; |
|
|
|
|