From 2df50d326d866612ee372d958fef586240333712 Mon Sep 17 00:00:00 2001 From: chudov Date: Sun, 4 Apr 2010 12:20:47 +0000 Subject: [PATCH] --- CUETools.DSP.Mixer/CUETools.DSP.Mixer.csproj | 56 +++ CUETools.DSP.Mixer/Mixer.cs | 335 ++++++++++++++++++ CUETools.DSP.Mixer/Properties/AssemblyInfo.cs | 36 ++ 3 files changed, 427 insertions(+) create mode 100644 CUETools.DSP.Mixer/CUETools.DSP.Mixer.csproj create mode 100644 CUETools.DSP.Mixer/Mixer.cs create mode 100644 CUETools.DSP.Mixer/Properties/AssemblyInfo.cs diff --git a/CUETools.DSP.Mixer/CUETools.DSP.Mixer.csproj b/CUETools.DSP.Mixer/CUETools.DSP.Mixer.csproj new file mode 100644 index 0000000..86cc920 --- /dev/null +++ b/CUETools.DSP.Mixer/CUETools.DSP.Mixer.csproj @@ -0,0 +1,56 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {AFF1AFF9-839D-4892-88CD-8BD09BAFC1D2} + Library + Properties + CUETools.DSP.Mixer + CUETools.DSP.Mixer + v2.0 + 512 + + + true + full + false + ..\bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + ..\bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + {6458A13A-30EF-45A9-9D58-E5031B17BEE2} + CUETools.Codecs + + + + + \ No newline at end of file diff --git a/CUETools.DSP.Mixer/Mixer.cs b/CUETools.DSP.Mixer/Mixer.cs new file mode 100644 index 0000000..cee61a7 --- /dev/null +++ b/CUETools.DSP.Mixer/Mixer.cs @@ -0,0 +1,335 @@ +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Diagnostics; +using CUETools.Codecs; + +namespace CUETools.DSP.Mixer +{ + public class MixingSource : IAudioSource + { + AudioPCMConfig pcm; + MixingBuffer buf0; + MixingBuffer buf1; + bool[] playing; + float[] volume; + long samplePos; + int size; + + public MixingSource(AudioPCMConfig pcm, int delay, int sources) + { + this.pcm = pcm; + this.size = delay * pcm.SampleRate / 1000; + this.buf0 = new MixingBuffer(pcm, size, sources); + this.buf1 = new MixingBuffer(pcm, size, sources); + this.playing = new bool[sources]; + this.volume = new float[sources]; + this.samplePos = 0; + } + + public void Close() + { + } + + public long Position + { + get + { + return samplePos; + } + set + { + throw new NotSupportedException(); + } + } + + public long Length + { + get + { + throw new NotSupportedException(); + } + } + + public long Remaining + { + get + { + throw new NotSupportedException(); + } + } + + public AudioPCMConfig PCM + { + get + { + return pcm; + } + } + + MixingBuffer mixbuff = null; + int mixoffs = 0; + + public int Read(AudioBuffer result, int maxLength) + { + if (maxLength > (BufferSize - mixoffs) || maxLength < 0) + maxLength = (BufferSize - mixoffs); + + result.Prepare(maxLength); + + if (mixbuff == null) + mixbuff = LockFilledBuffer(); + + float sumVolume = 0.0f; + for (int iSource = 0; iSource < mixbuff.source.Length; iSource++) + if (playing[iSource]) + sumVolume += mixbuff.volume[iSource]; + for (int iSource = 0; iSource < mixbuff.source.Length; iSource++) + volume[iSource] = playing[iSource] ? mixbuff.volume[iSource] / Math.Max(1.0f, sumVolume) : 0.0f; + for (int iSmp = 0; iSmp < result.Length; iSmp++) + { + for (int iChan = 0; iChan < result.PCM.ChannelCount; iChan++) + { + float sample = 0.0f; + for (int iSource = 0; iSource < mixbuff.source.Length; iSource++) + sample += mixbuff.source[iSource].Float[mixoffs + iSmp, iChan] * volume[iSource]; + result.Float[iSmp, iChan] = sample; + } + } + mixoffs += result.Length; + if (mixoffs == BufferSize) + { + UnlockFilledBuffer(mixbuff); + mixbuff = null; + mixoffs = 0; + } + samplePos += result.Length; + return result.Length; + } + + public string Path { get { return ""; } } + + public int BufferSize + { + get + { + return buf0.source[0].Size; + } + } + + private bool IsFilled(MixingBuffer buf) + { + bool res = true; + for (int i = 0; i < buf.filled.Length; i++) + res &= buf.filled[i] || !this.playing[i]; + return res; + } + + internal MixingBuffer LockFilledBuffer() + { + lock (this) + { + //Trace.WriteLine(string.Format("LockFilledBuffer: 0.0: {0} {1}; 0.1: {2} {3}; 1.0: {4} {5}; 1.1: {6} {7};", + // buf0.playing[0], buf0.filled[0], buf0.playing[1], buf0.filled[1], + // buf0.playing[0], buf0.filled[0], buf0.playing[1], buf0.filled[1])); + while (!IsFilled(buf0) && !IsFilled(buf1)) + Monitor.Wait(this); + return IsFilled(buf0) ? buf0 : buf1; + } + } + + internal void UnlockFilledBuffer(MixingBuffer mixbuff) + { + lock (this) + { + //Trace.WriteLine(string.Format("UnockFilledBuffer: 0.0: {0} {1}; 0.1: {2} {3}; 1.0: {4} {5}; 1.1: {6} {7};", + // buf0.playing[0], buf0.filled[0], buf0.playing[1], buf0.filled[1], + // buf0.playing[0], buf0.filled[0], buf0.playing[1], buf0.filled[1])); + for (int i = 0; i < mixbuff.filled.Length; i++) + mixbuff.filled[i] = false; + Monitor.PulseAll(this); + } + } + + public void BufferPlaying(int iSource, bool playing) + { + lock (this) + { + //Trace.WriteLine(string.Format("BufferPlaying{8}< 0.0: {0} {1}; 0.1: {2} {3}; 1.0: {4} {5}; 1.1: {6} {7};", + // buf0.playing[0], buf0.filled[0], buf0.playing[1], buf0.filled[1], + // buf0.playing[0], buf0.filled[0], buf0.playing[1], buf0.filled[1], iSource)); + + this.playing[iSource] = playing; + //if (!playing) buf0.filled[iSource] = false; + //if (!playing) buf1.filled[iSource] = false; + + //Trace.WriteLine(string.Format("BufferPlaying{8}> 0.0: {0} {1}; 0.1: {2} {3}; 1.0: {4} {5}; 1.1: {6} {7};", + // buf0.playing[0], buf0.filled[0], buf0.playing[1], buf0.filled[1], + // buf0.playing[0], buf0.filled[0], buf0.playing[1], buf0.filled[1], iSource)); + Monitor.PulseAll(this); + } + } + + internal MixingBuffer LockEmptyBuffer(int iSource) + { + lock (this) + { + //Trace.WriteLine(string.Format("LockEmptyBuffer{8}: 0.0: {0} {1}; 0.1: {2} {3}; 1.0: {4} {5}; 1.1: {6} {7};", + // buf0.playing[0], buf0.filled[0], buf0.playing[1], buf0.filled[1], + // buf0.playing[0], buf0.filled[0], buf0.playing[1], buf0.filled[1], iSource)); + + while (!playing[iSource] || (buf0.filled[iSource] && buf1.filled[iSource])) + Monitor.Wait(this); + + return !buf0.filled[iSource] ? buf0 : buf1; + } + } + + internal void UnlockEmptyBuffer(MixingBuffer mixbuff, int iSource, float volume) + { + lock (this) + { + //Trace.WriteLine(string.Format("UnlockEmptyBuffer{8}< 0.0: {0} {1}; 0.1: {2} {3}; 1.0: {4} {5}; 1.1: {6} {7};", + // buf0.playing[0], buf0.filled[0], buf0.playing[1], buf0.filled[1], + // buf0.playing[0], buf0.filled[0], buf0.playing[1], buf0.filled[1], iSource)); + + mixbuff.volume[iSource] = volume; + mixbuff.filled[iSource] = true; + + //Trace.WriteLine(string.Format("UnlockEmptyBuffer{8}> 0.0: {0} {1}; 0.1: {2} {3}; 1.0: {4} {5}; 1.1: {6} {7};", + // buf0.playing[0], buf0.filled[0], buf0.playing[1], buf0.filled[1], + // buf0.playing[0], buf0.filled[0], buf0.playing[1], buf0.filled[1], iSource)); + + Monitor.PulseAll(this); + } + } + } + + public class MixingBuffer + { + public AudioBuffer[] source; + public float[] volume; + public bool[] filled; + + public MixingBuffer(AudioPCMConfig pcm, int size, int sources) + { + source = new AudioBuffer[sources]; + volume = new float[sources]; + filled = new bool[sources]; + for (int i = 0; i < sources; i++) + source[i] = new AudioBuffer(pcm, size); + } + } + + public class MixingWriter : IAudioDest + { + private MixingSource mixer; + private int iSource; + private long samplePos; + private MixingBuffer mixbuff; + private float volume; + + public MixingWriter(MixingSource mixer, int iSource) + { + this.mixer = mixer; + this.iSource = iSource; + this.samplePos = 0; + this.mixbuff = null; + this.volume = 1.0f; + } + + public void Close() + { + } + + public void Delete() + { + Close(); + } + + public long Position + { + get { return samplePos; } + set { throw new NotSupportedException(); } + } + + public long FinalSampleCount + { + set { throw new NotSupportedException(); } + } + + public long BlockSize + { + set { throw new NotSupportedException(); } + } + + public int CompressionLevel + { + get { return 0; } + set { throw new NotSupportedException(); } + } + + public string Options + { + set + { + if (value == null || value == "") return; + throw new NotSupportedException("Unsupported options " + value); + } + } + + public AudioPCMConfig PCM + { + get { return mixer.PCM; } + } + + public float Volume + { + get + { + return volume; + } + set + { + volume = value; + } + } + + public void Pause() + { + mixer.LockEmptyBuffer(iSource); + } + + public void Write(AudioBuffer buff) + { + int bs = PCM.BlockAlign; + int buff_offs = 0; + + while (buff_offs < buff.Length) + { + if (mixbuff == null) + { + mixbuff = mixer.LockEmptyBuffer(iSource); + mixbuff.source[iSource].Prepare(-1); + mixbuff.source[iSource].Length = 0; + } + + int chunk = Math.Min(buff.Length - buff_offs, mixbuff.source[iSource].Size - mixbuff.source[iSource].Length); + Buffer.BlockCopy(buff.Bytes, buff_offs * bs, mixbuff.source[iSource].Bytes, mixbuff.source[iSource].Length * bs, chunk * bs); + mixbuff.source[iSource].Length += chunk; + buff_offs += chunk; + + if (mixbuff.source[iSource].Length == mixbuff.source[iSource].Size) + { + mixer.UnlockEmptyBuffer(mixbuff, iSource, volume); + mixbuff = null; + } + } + + samplePos += buff.Length; + } + + public string Path { get { return ""; } } + } +} diff --git a/CUETools.DSP.Mixer/Properties/AssemblyInfo.cs b/CUETools.DSP.Mixer/Properties/AssemblyInfo.cs new file mode 100644 index 0000000..3ac725c --- /dev/null +++ b/CUETools.DSP.Mixer/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("CUETools.DSP.Mixer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("CUETools.DSP.Mixer")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("63ea1712-9776-4ebc-829e-6f2b41cfe3f2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")]