public static void PlaySample(Level level, WadSample sample, int channel, float volume = 1.0f, float pitch = 1.0f, float pan = 0.0f, int loopCount = 1) { if (volume <= 0.0f || loopCount <= 0) { return; } var disposables = new List <IDisposable>(); try { // Load data. // If waveform data is loaded into memory, use it to play sound, otherwise find wav file on disk. WaveFileReader waveStream; MemoryStream memoryStream; if (sample.IsLoaded) { memoryStream = disposables.AddAndReturn(new MemoryStream(sample.Data, false)); waveStream = disposables.AddAndReturn(new WaveFileReader(memoryStream)); } else { var path = WadSounds.TryGetSamplePath(level.Settings, sample.FileName); if (path == null) { return; } else { waveStream = disposables.AddAndReturn(new WaveFileReader(path)); } } // Apply looping ISampleProvider sampleStream; if (loopCount <= 1) { sampleStream = waveStream.ToSampleProvider(); } else { sampleStream = new RepeatedStream(waveStream) { LoopCount = loopCount } }; // Always play sample as 22 khz if (sampleStream.WaveFormat.SampleRate != 22050) { sampleStream = new PitchedStream { Source = sampleStream, Pitch = 22050.0f / sampleStream.WaveFormat.SampleRate } } ; // Apply panning if (pan != 1.0f) { sampleStream = new PanningSampleProvider(sampleStream) { Pan = pan } } ; // Apply pitch if (pitch != 1.0f) { sampleStream = new PitchedStream { Source = sampleStream, Pitch = pitch } } ; // Apply volume if (volume != 1.0f) { sampleStream = new VolumeSampleProvider(sampleStream) { Volume = volume } } ; // Add some silence to make sure the audio plays out. const int latencyInMilliseconds = 200; int latencyInSamples = (sampleStream.WaveFormat.SampleRate * latencyInMilliseconds * 2) / 1000; sampleStream = new OffsetSampleProvider(sampleStream) { LeadOutSamples = sampleStream.WaveFormat.Channels * latencyInSamples }; // Play _channels[channel] = disposables.AddAndReturn(new WaveOut()); _channels[channel].Init(sampleStream); _channels[channel].PlaybackStopped += (s, e) => { foreach (IDisposable disposable in disposables) { disposable?.Dispose(); } _channels[channel] = null; _indices[channel] = -1; }; _channels[channel].Play(); } catch (Exception ex) { // Clean up in case of a problem foreach (IDisposable disposable in disposables) { disposable?.Dispose(); } _logger.Error("Error while playing sample " + sample + ", exception: " + ex); } }
private static bool LoadSamples(ChunkReader chunkIO, ChunkId idOuter, Wad2 wad, ref Dictionary <long, WadSample> outSamples, bool obsolete) { if (idOuter != Wad2Chunks.Samples) { return(false); } var samples = new Dictionary <long, WadSample>(); long obsoleteIndex = 0; // Move this into each chunk once we got rid of old style *.wad2 files. chunkIO.ReadChunks((id, chunkSize) => { if (id != Wad2Chunks.Sample) { return(false); } string FilenameObsolete = null; byte[] data = null; chunkIO.ReadChunks((id2, chunkSize2) => { if (id2 == Wad2Chunks.SampleIndex) { obsoleteIndex = chunkIO.ReadChunkLong(chunkSize2); } else if (id2 == Wad2Chunks.SampleFilenameObsolete) { FilenameObsolete = chunkIO.ReadChunkString(chunkSize2); } else if (id2 == Wad2Chunks.SampleData) { data = chunkIO.ReadChunkArrayOfBytes(chunkSize2); } else { return(false); } return(true); }); if (data == null && !string.IsNullOrEmpty(FilenameObsolete)) { string fullPath = Path.Combine(PathC.GetDirectoryNameTry(Assembly.GetEntryAssembly().Location), "Sounds\\TR4\\Samples", FilenameObsolete + ".wav"); data = File.ReadAllBytes(fullPath); } samples.Add(obsoleteIndex++, new WadSample("", WadSample.ConvertSampleFormat(data, sampleRate => obsolete ? new WadSample.ResampleInfo { Resample = false, SampleRate = WadSample.GameSupportedSampleRate } : new WadSample.ResampleInfo { Resample = true, SampleRate = sampleRate }))); return(true); }); outSamples = samples; return(true); }