Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- /***********************************************************
- ** **
- ** Codesnippets written by Madwin for MMP 2023 **
- ** **
- ** Not to be used freely. the code still has errors **
- ** It's intended to be used as starting point. **
- ** Code has not been checked and might still need **
- ** (a lot of) work. **
- ** **
- ** © MadMedia Productions 2023 **
- ** **
- ***********************************************************/
- // C# class that emulates the Boss Doctor Rhythm DR-110 using CSCore(:
- using CSCore;
- using CSCore.Codecs.WAV;
- using CSCore.CoreAudioAPI;
- using CSCore.SoundOut;
- using System;
- using System.IO;
- namespace BossDoctorRhythmDR110
- {
- public class DR110
- {
- private WaveFile[] patterns;
- private WaveFile[] accents;
- private WaveFile[] intros;
- private WaveFile[] fills;
- private WaveFile[] endings;
- public DR110(string patternPath, string accentPath, string introPath, string fillPath, string endingPath)
- {
- LoadPatterns(patternPath);
- LoadAccents(accentPath);
- LoadIntros(introPath);
- LoadFills(fillPath);
- LoadEndings(endingPath);
- }
- private void LoadPatterns(string patternPath)
- {
- patterns = new WaveFile[16];
- for (int i = 0; i < 16; i++)
- {
- string filePath = Path.Combine(patternPath, $"Pattern{i + 1}.wav");
- if (File.Exists(filePath))
- {
- patterns[i] = new WaveFile(filePath);
- }
- }
- }
- private void LoadAccents(string accentPath)
- {
- accents = new WaveFile[6];
- for (int i = 0; i < 6; i++)
- {
- string filePath = Path.Combine(accentPath, $"Accent{i + 1}.wav");
- if (File.Exists(filePath))
- {
- accents[i] = new WaveFile(filePath);
- }
- }
- }
- private void LoadIntros(string introPath)
- {
- intros = new WaveFile[2];
- for (int i = 0; i < 2; i++)
- {
- string filePath = Path.Combine(introPath, $"Intro{i + 1}.wav");
- if (File.Exists(filePath))
- {
- intros[i] = new WaveFile(filePath);
- }
- }
- }
- private void LoadFills(string fillPath)
- {
- fills = new WaveFile[2];
- for (int i = 0; i < 2; i++)
- {
- string filePath = Path.Combine(fillPath, $"Fill{i + 1}.wav");
- if (File.Exists(filePath))
- {
- fills[i] = new WaveFile(filePath);
- }
- }
- }
- private void LoadEndings(string endingPath)
- {
- endings = new WaveFile[2];
- for (int i = 0; i < 2; i++)
- {
- string filePath = Path.Combine(endingPath, $"Ending{i + 1}.wav");
- if (File.Exists(filePath))
- {
- endings[i] = new WaveFile(filePath);
- }
- }
- }
- public void PlayPattern(int patternNumber, int accentNumber = 0, int introNumber = 0, int fillNumber = 0, int endingNumber = 0)
- {
- if (patternNumber < 1 || patternNumber > 16)
- {
- throw new ArgumentException("Invalid pattern number.");
- }
- if (accentNumber < 0 || accentNumber > 5)
- {
- throw new ArgumentException("Invalid accent number.");
- }
- if (introNumber < 0 || introNumber > 1)
- {
- throw new ArgumentException("Invalid intro number.");
- }
- if (fillNumber < 0 || fillNumber > 1)
- {
- throw new ArgumentException("Invalid fill number.");
- }
- if (endingNumber < 0 || endingNumber > 1)
- {
- throw new ArgumentException("Invalid ending number.");
- }
- WaveFile pattern = patterns[patternNumber - 1];
- WaveFile accent = accents[accentNumber];
- WaveFile intro = intros[introNumber];
- WaveFile fill = fills[fillNumber];
- WaveFile ending = endings[endingNumber];
- // Create a new sound player using the default audio device
- using (var soundOut = new WasapiOut())
- {
- // Create a sound source that plays the pattern with the specified accents, intro, fill, and ending
- var soundSource = new MultiFileSoundSource(pattern, accent, intro, fill, ending);
- // Play the sound source
- soundOut.Initialize(soundSource.ToWaveSource());
- soundOut.Play();
- // Wait until the sound finishes playing
- soundOut.WaitForStopped();
- }
- }
- }
- public class WaveFile
- {
- private readonly string filePath;
- private WaveFileReader waveFileReader;
- public WaveFile(string filePath)
- {
- this.filePath = filePath;
- }
- public WaveFormat WaveFormat => waveFileReader?.WaveFormat;
- public WaveFileReader WaveFileReader
- {
- get
- {
- if (waveFileReader == null)
- {
- waveFileReader = new WaveFileReader(filePath);
- }
- return waveFileReader;
- }
- }
- }
- public class MultiFileSoundSource : ISoundSource
- {
- private readonly WaveFile pattern;
- private readonly WaveFile accent;
- private readonly WaveFile intro;
- private readonly WaveFile fill;
- private readonly WaveFile ending;
- public MultiFileSoundSource(WaveFile pattern, WaveFile accent, WaveFile intro, WaveFile fill, WaveFile ending)
- {
- this.pattern = pattern;
- this.accent = accent;
- this.intro = intro;
- this.fill = fill;
- this.ending = ending;
- }
- public IWaveSource ToWaveSource()
- {
- var waveSources = new List<IWaveSource>();
- // Add the intro wave source if present
- if (intro != null)
- {
- waveSources.Add(intro.WaveFileReader.ToSampleSource().ToWaveSource());
- }
- // Add the pattern wave source with the specified accent
- var patternSource = pattern.WaveFileReader.ToSampleSource().ToWaveSource();
- if (accent != null)
- {
- var accentSource = accent.WaveFileReader.ToSampleSource().ToWaveSource();
- patternSource = new AccentWaveSource(patternSource, accentSource);
- }
- waveSources.Add(patternSource);
- // Add the fill wave source if present
- if (fill != null)
- {
- waveSources.Add(fill.WaveFileReader.ToSampleSource().ToWaveSource());
- }
- // Add the ending wave source if present
- if (ending != null)
- {
- waveSources.Add(ending.WaveFileReader.ToSampleSource().ToWaveSource());
- }
- // Combine all wave sources into a single wave source
- var combinedWaveSource = new ConcatenatingWaveSource(waveSources);
- // Return the combined wave source
- return combinedWaveSource.ToWaveSource();
- }
- }
- public class AccentWaveSource : IWaveSource
- {
- private readonly IWaveSource patternSource;
- private readonly IWaveSource accentSource;
- public AccentWaveSource(IWaveSource patternSource, IWaveSource accentSource)
- {
- this.patternSource = patternSource;
- this.accentSource = accentSource;
- if (patternSource.WaveFormat.SampleRate != accentSource.WaveFormat.SampleRate)
- {
- throw new ArgumentException("The sample rate of the pattern and accent sources must be the same.");
- }
- }
- public WaveFormat WaveFormat => patternSource.WaveFormat;
- public long Length => patternSource.Length;
- public long Position
- {
- get => patternSource.Position;
- set => patternSource.Position = value;
- }
- public int Read(byte[] buffer, int offset, int count)
- {
- var patternBuffer = new byte[count];
- var accentBuffer = new byte[count];
- var numRead = patternSource.Read(patternBuffer, offset, count);
- var accentNumRead = accentSource.Read(accentBuffer, offset, count);
- if (numRead != accentNumRead)
- {
- throw new InvalidOperationException("The number of samples read from the pattern and accent sources must be the same.");
- }
- for (int i = 0; i < numRead; i += 2)
- {
- short patternSample = BitConverter.ToInt16(patternBuffer, i);
- short accentSample = BitConverter.ToInt16(accentBuffer, i);
- short combinedSample = (short)(patternSample + accentSample);
- byte[] combinedSampleBytes = BitConverter.GetBytes(combinedSample);
- buffer[offset + i] = combinedSampleBytes[0];
- buffer[offset + i + 1] = combinedSampleBytes[1];
- }
- return numRead;
- }
- }
- public class ConcatenatingWaveSource : IWaveSource
- {
- private readonly List<IWaveSource> waveSources;
- private int currentIndex;
- private long position;
- public ConcatenatingWaveSource(IEnumerable<IWaveSource> waveSources)
- {
- this.waveSources = new List<IWaveSource>(waveSources);
- currentIndex = 0;
- position = 0;
- }
- public WaveFormat WaveFormat => waveSources[currentIndex].WaveFormat;
- public long Length => waveSources.Sum(ws => ws.Length);
- public long Position
- {
- get => position;
- set
- {
- if (value < 0 || value >= Length)
- {
- throw new ArgumentOutOfRangeException(nameof(Position));
- }
- position = value;
- var sourceIndex = 0;
- var sourcePosition = 0L;
- foreach (var source in waveSources)
- {
- if (position < sourcePosition + source.Length)
- {
- currentIndex = sourceIndex;
- waveSources[currentIndex].Position = position - sourcePosition;
- break;
- }
- sourceIndex++;
- sourcePosition += source.Length;
- }
- }
- }
- public int Read(byte[] buffer, int offset, int count)
- {
- var numRead = waveSources[currentIndex].Read(buffer, offset, count);
- if (numRead == 0 && currentIndex < waveSources.Count - 1)
- {
- currentIndex++;
- waveSources[currentIndex].Position = 0;
- numRead = waveSources[currentIndex].Read(buffer, offset, count);
- }
- position += numRead;
- return numRead;
- }
- }
- }
- /*
- This code defines the `RhythmDr110` class, which provides a `Play` method that
- can be used to play the specified pattern with the specified accents, intro,
- fill, and ending using the default audio device. The class uses the `CSCore`
- library to play the audio, and it defines several helper classes to read and
- combine the audio files.
- The `WaveFile` class represents an audio file, and it lazily loads the audio file
- when the `WaveFileReader` property is accessed. The `MultiFileSoundSource` class
- represents a sound source that combines multiple audio files into a single audio
- stream, and it uses the `ConcatenatingWaveSource` class to concatenate the audio
- streams. The `AccentWaveSource` class represents a wave source that adds an accent
- to a pattern by combining the pattern and accent audio streams.
- The RhythmDr110 class uses these helper classes to read the audio files
- for the pattern, accents, intro, fill, and ending, and it creates a new
- AccentWaveSource for the pattern and accent. It then creates a
- ConcatenatingWaveSource to concatenate the intro, pattern, fill, pattern,
- fill, pattern, fill, pattern, ending, and it creates a new WasapiOut to
- play the audio.
- The Play method simply creates a new instance of the RhythmDr110 class
- and calls its Play method. Here's an example usage of the RhythmDr110 class:
- */
- var rhythmDr110 = new RhythmDr110();
- rhythmDr110.Play(patternFile, accentFile, introFile, fillFile, endingFile);
- /*
- This will play the specified pattern with the specified accents, intro, fill,
- and ending using the default audio device.
- Note that you'll need to add the necessary using statements and references to
- use the CSCore library in your project. You'll also need to add error handling
- and input validation to the RhythmDr110 class to ensure that it can handle
- invalid input gracefully.
- */
Advertisement
Add Comment
Please, Sign In to add comment