public int SampleCount(LevelSettings settings) { if (settings.GameVersion.UsesMainSfx() || Samples == null || Samples.Count <= 0) { return(-1); } int result = 0; foreach (var sample in Samples) { var path = WadSounds.TryGetSamplePath(settings, sample.FileName); if (path != null) { result++; } } return(result); }
public static SortedDictionary <int, WadSample> CompileSamples(List <WadSoundInfo> soundMap, LevelSettings settings, bool onlyIndexed, IProgressReporter reporter, out bool samplesMissing) { var samples = new List <WadSample>(); foreach (var soundInfo in soundMap) { if (onlyIndexed && !soundInfo.Indexed) { continue; } foreach (var sample in soundInfo.Samples) { samples.Add(sample); } } var loadedSamples = new SortedDictionary <int, WadSample>(); // Set up maximum buffer sizes and sample rate int maxBufferLength = 1024 * 256; int warnBufferLength = 1024 * 256; uint supportedSampleRate = 22050; uint supportedBitness = 16; switch (settings.GameVersion) { case TRVersion.Game.TR5Main: maxBufferLength = int.MaxValue; // Unlimited warnBufferLength = int.MaxValue; break; case TRVersion.Game.TR4: case TRVersion.Game.TRNG: maxBufferLength = 1024 * 1024; // Raised TREP limit break; case TRVersion.Game.TR1: case TRVersion.Game.TR2: supportedSampleRate = 11025; supportedBitness = 8; break; } var missing = false; Parallel.For(0, samples.Count, i => { WadSample currentSample = NullSample; try { string samplePath = WadSounds.TryGetSamplePath(settings, samples[i].FileName); // If sample was found, then load it... if (!string.IsNullOrEmpty(samplePath)) { using (var stream = new FileStream(samplePath, FileMode.Open, FileAccess.Read, FileShare.Read)) { var buffer = new byte[stream.Length]; if (stream.Read(buffer, 0, buffer.Length) != buffer.Length) { throw new EndOfStreamException(); } if (settings.GameVersion.UsesMainSfx()) { var sampleRate = settings.GameVersion < TRVersion.Game.TR3 ? 11025 : 22050; currentSample = new WadSample(samplePath, ConvertSampleFormat(buffer, true, (uint)sampleRate)); } else { currentSample = new WadSample(samplePath, ConvertSampleFormat(buffer, false)); } if (currentSample.SampleRate != supportedSampleRate) { reporter?.ReportWarn("Sample " + samplePath + " has a sample rate of " + currentSample.SampleRate + " which is unsupported for this engine version."); } if (currentSample.ChannelCount > 1) { reporter?.ReportWarn("Sample " + samplePath + " isn't mono. Only mono samples are supported. Crashes may occur."); } if (currentSample.BitsPerSample != supportedBitness) { reporter?.ReportWarn("Sample " + samplePath + " is not " + supportedBitness + "-bit sample and is not supported in this game version. Crashes may occur."); } if (buffer.Length > maxBufferLength) { reporter?.ReportWarn("Sample " + samplePath + " is more than " + maxBufferLength / 1024 + " kbytes long. It is too big for this game version, crashes may occur."); } else if (buffer.Length > warnBufferLength) { reporter?.ReportWarn("Sample " + samplePath + " is more than " + warnBufferLength / 1024 + " kbytes long. It may cause problems without additional measures, such as patching."); } } } // ... otherwise output null sample else { currentSample = WadSample.NullSample; logger.Warn(new FileNotFoundException(), "Unable to find sample '" + samplePath + "'"); missing = true; } } catch (Exception exc) { logger.Warn(exc, "Unable to read file '" + samples[i].FileName + "' from provided location."); } lock (loadedSamples) loadedSamples.Add(i, currentSample); }); if (missing) { reporter?.ReportWarn("Some samples are missing. Make sure sample paths are specified correctly. Check level settings for details."); } samplesMissing = missing; return(loadedSamples); }
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); } }