private void AddInstance(SoundInstance soundInstance, int polyphony) { if (soundInstance.sound.data.Length == 0) { return; } SoundInstanceInternal instance = new SoundInstanceInternal(soundInstance); if (!dataInstanceCount.ContainsKey(instance.sound)) { dataInstanceCount.Add(instance.sound, 0); } lock (audibleSounds) instance.offsetStart = GetBufferOffset(); instance.position = soundInstance.startPosition; EnforcePolyphony(polyphony, instance, instance.offsetStart); lock (audibleSounds) { audibleSounds.Add(instance); dataInstanceCount[instance.sound] = dataInstanceCount[instance.sound] + 1; } }
public void PlayScheduled(double position, SoundInstance soundInstance, int polyphony) { if (soundInstance.sound.data.Length == 0) { return; } SoundInstanceInternal instance = new SoundInstanceInternal(soundInstance); if (!dataInstanceCount.ContainsKey(instance.sound)) { dataInstanceCount.Add(instance.sound, 0); } double bufferPosition = (position - lastCallback) * ((double)audioSpec.freq); uint offset = (uint)(bufferPosition * bytesPerSample); if (offset % 4 != 0) { offset += 4 - (offset % 4); } instance.offsetStart = offset; instance.position = soundInstance.startPosition; EnforcePolyphony(polyphony, instance, offset); lock (audibleSounds) { audibleSounds.Add(instance); dataInstanceCount[instance.sound] = dataInstanceCount[instance.sound] + 1; } }
public byte[] RenderAudio() { if (audioDriver != AudioDriver.File) { return(null); } uint streamLength = 0; for (int i = 0; i < audibleSounds.Count; ++i) { SoundInstanceInternal instance = audibleSounds[i]; if (streamLength < instance.offsetStart + instance.endPosition) { streamLength = instance.offsetStart + instance.endPosition; } } if (streamLength == 0) { return(null); } byte[] bytes = new byte[(int)streamLength]; GCHandle stream = GCHandle.Alloc(bytes, GCHandleType.Pinned); for (int i = 0; i < audibleSounds.Count; ++i) { SoundInstanceInternal instance = audibleSounds[i]; int playLength = (int)(instance.offsetStop - instance.offsetStart); if (playLength > instance.endPosition) { playLength = (int)instance.endPosition; } else if (playLength <= 0) { playLength = (int)instance.endPosition; } IntPtr streamOffset = stream.AddrOfPinnedObject(); streamOffset += (int)instance.offsetStart; byte finalVolume = (byte)Math.Round(Math.Min(volume * instance.volume * SDL.SDL_MIX_MAXVOLUME, SDL.SDL_MIX_MAXVOLUME)); SDL_MixAudioFormat(streamOffset, instance.sound.data, audioSpec.format, (uint)playLength, finalVolume); } stream.Free(); audibleSounds.Clear(); return(bytes); }
private void EnforcePolyphony(int polyphony, SoundInstanceInternal instance, uint offset) { if (polyphony == 0) { return; } SoundData sound = instance.sound; if (dataInstanceCount[sound] < polyphony) { return; } int removeCount = dataInstanceCount[sound] - polyphony + 1; lock (audibleSounds) { for (int i = 0; i < audibleSounds.Count; ++i) { SoundInstanceInternal audibleInstance = audibleSounds[i]; if (audibleInstance.sound != sound) { continue; } if (audibleInstance.remove) { continue; } // only apply polyphony to identical slices if (audibleInstance.instance.startPosition != instance.instance.startPosition || audibleInstance.instance.endPosition != instance.instance.endPosition) { continue; } // mark instance for later removal audibleInstance.paused = true; audibleInstance.remove = true; audibleInstance.offsetStop = offset; removeCount--; if (removeCount <= 0) { break; } } } }
private void CopySample(SoundInstanceInternal instance, IntPtr userData, IntPtr stream, int length) { uint sampleLeft = Math.Min(instance.endPosition - instance.position, (uint)length); uint startOffset = instance.offsetStart; uint pauseOffset = instance.offsetStop; if (startOffset < length) { uint copyLength = Math.Min(sampleLeft + startOffset, (uint)length) - startOffset; if (instance.paused) { if (startOffset <= pauseOffset) { copyLength = Math.Min(pauseOffset - startOffset, copyLength); } else { copyLength = 0; } instance.offsetStop = 0; } // audio clip starts playing at starting offset Array.Copy(instance.sound.data, instance.position, audioBuffer, startOffset, copyLength); instance.position += copyLength; instance.offsetStart = 0; } else { instance.offsetStart -= (uint)length; } if (instance.position >= instance.endPosition) { instance.remove = true; } }
private void MixAudio(IntPtr userData, IntPtr stream, int length) { for (int i = 0; i < audibleSounds.Count; ++i) { SoundInstanceInternal instance = audibleSounds[i]; // fill temporary buffer with silence Array.Copy(emptyBuffer, 0, audioBuffer, 0, length); CopySample(instance, userData, stream, length); byte finalVolume = (byte)Math.Round(Math.Min(volume * instance.volume * SDL.SDL_MIX_MAXVOLUME, SDL.SDL_MIX_MAXVOLUME)); SDL_MixAudioFormat(stream, audioBuffer, audioSpec.format, (uint)length, finalVolume); if (instance.remove) { audibleSounds.RemoveAt(i); dataInstanceCount[instance.sound] = dataInstanceCount[instance.sound] - 1; i--; } } }