Madwin74

DR-110 Emulation

May 9th, 2023 (edited)
884
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
C# 12.90 KB | Source Code | 0 0
  1. /***********************************************************
  2.  **                                                       **
  3.  **   Codesnippets written by Madwin for MMP 2023         **
  4.  **                                                       **
  5.  **   Not to be used freely. the code still has errors    **
  6.  **   It's intended to be used as starting point.         **
  7.  **   Code has not been checked and might still need      **
  8.  **   (a lot of) work.                                    **
  9.  **                                                       **
  10.  **   © MadMedia Productions 2023                         **
  11.  **                                                       **
  12.  ***********************************************************/
  13.  
  14.  
  15.  
  16. //  C# class that emulates the Boss Doctor Rhythm DR-110 using CSCore(:
  17.  
  18.  
  19.  
  20. using CSCore;
  21. using CSCore.Codecs.WAV;
  22. using CSCore.CoreAudioAPI;
  23. using CSCore.SoundOut;
  24. using System;
  25. using System.IO;
  26.  
  27. namespace BossDoctorRhythmDR110
  28. {
  29.     public class DR110
  30.     {
  31.         private WaveFile[] patterns;
  32.         private WaveFile[] accents;
  33.         private WaveFile[] intros;
  34.         private WaveFile[] fills;
  35.         private WaveFile[] endings;
  36.  
  37.         public DR110(string patternPath, string accentPath, string introPath, string fillPath, string endingPath)
  38.         {
  39.             LoadPatterns(patternPath);
  40.             LoadAccents(accentPath);
  41.             LoadIntros(introPath);
  42.             LoadFills(fillPath);
  43.             LoadEndings(endingPath);
  44.         }
  45.  
  46.         private void LoadPatterns(string patternPath)
  47.         {
  48.             patterns = new WaveFile[16];
  49.             for (int i = 0; i < 16; i++)
  50.             {
  51.                 string filePath = Path.Combine(patternPath, $"Pattern{i + 1}.wav");
  52.                 if (File.Exists(filePath))
  53.                 {
  54.                     patterns[i] = new WaveFile(filePath);
  55.                 }
  56.             }
  57.         }
  58.  
  59.         private void LoadAccents(string accentPath)
  60.         {
  61.             accents = new WaveFile[6];
  62.             for (int i = 0; i < 6; i++)
  63.             {
  64.                 string filePath = Path.Combine(accentPath, $"Accent{i + 1}.wav");
  65.                 if (File.Exists(filePath))
  66.                 {
  67.                     accents[i] = new WaveFile(filePath);
  68.                 }
  69.             }
  70.         }
  71.  
  72.         private void LoadIntros(string introPath)
  73.         {
  74.             intros = new WaveFile[2];
  75.             for (int i = 0; i < 2; i++)
  76.             {
  77.                 string filePath = Path.Combine(introPath, $"Intro{i + 1}.wav");
  78.                 if (File.Exists(filePath))
  79.                 {
  80.                     intros[i] = new WaveFile(filePath);
  81.                 }
  82.             }
  83.         }
  84.  
  85.         private void LoadFills(string fillPath)
  86.         {
  87.             fills = new WaveFile[2];
  88.             for (int i = 0; i < 2; i++)
  89.             {
  90.                 string filePath = Path.Combine(fillPath, $"Fill{i + 1}.wav");
  91.                 if (File.Exists(filePath))
  92.                 {
  93.                     fills[i] = new WaveFile(filePath);
  94.                 }
  95.             }
  96.         }
  97.  
  98.         private void LoadEndings(string endingPath)
  99.         {
  100.             endings = new WaveFile[2];
  101.             for (int i = 0; i < 2; i++)
  102.             {
  103.                 string filePath = Path.Combine(endingPath, $"Ending{i + 1}.wav");
  104.                 if (File.Exists(filePath))
  105.                 {
  106.                     endings[i] = new WaveFile(filePath);
  107.                 }
  108.             }
  109.         }
  110.  
  111.         public void PlayPattern(int patternNumber, int accentNumber = 0, int introNumber = 0, int fillNumber = 0, int endingNumber = 0)
  112.         {
  113.             if (patternNumber < 1 || patternNumber > 16)
  114.             {
  115.                 throw new ArgumentException("Invalid pattern number.");
  116.             }
  117.  
  118.             if (accentNumber < 0 || accentNumber > 5)
  119.             {
  120.                 throw new ArgumentException("Invalid accent number.");
  121.             }
  122.  
  123.             if (introNumber < 0 || introNumber > 1)
  124.             {
  125.                 throw new ArgumentException("Invalid intro number.");
  126.             }
  127.  
  128.             if (fillNumber < 0 || fillNumber > 1)
  129.             {
  130.                 throw new ArgumentException("Invalid fill number.");
  131.             }
  132.  
  133.             if (endingNumber < 0 || endingNumber > 1)
  134.             {
  135.                 throw new ArgumentException("Invalid ending number.");
  136. }
  137.  
  138.         WaveFile pattern = patterns[patternNumber - 1];
  139.         WaveFile accent = accents[accentNumber];
  140.         WaveFile intro = intros[introNumber];
  141.         WaveFile fill = fills[fillNumber];
  142.         WaveFile ending = endings[endingNumber];
  143.  
  144.         // Create a new sound player using the default audio device
  145.         using (var soundOut = new WasapiOut())
  146.         {
  147.             // Create a sound source that plays the pattern with the specified accents, intro, fill, and ending
  148.             var soundSource = new MultiFileSoundSource(pattern, accent, intro, fill, ending);
  149.  
  150.             // Play the sound source
  151.             soundOut.Initialize(soundSource.ToWaveSource());
  152.             soundOut.Play();
  153.  
  154.             // Wait until the sound finishes playing
  155.             soundOut.WaitForStopped();
  156.         }
  157.     }
  158. }
  159.  
  160. public class WaveFile
  161. {
  162.     private readonly string filePath;
  163.     private WaveFileReader waveFileReader;
  164.  
  165.     public WaveFile(string filePath)
  166.     {
  167.         this.filePath = filePath;
  168.     }
  169.  
  170.     public WaveFormat WaveFormat => waveFileReader?.WaveFormat;
  171.  
  172.     public WaveFileReader WaveFileReader
  173.     {
  174.         get
  175.         {
  176.             if (waveFileReader == null)
  177.             {
  178.                 waveFileReader = new WaveFileReader(filePath);
  179.             }
  180.             return waveFileReader;
  181.         }
  182.     }
  183. }
  184.  
  185. public class MultiFileSoundSource : ISoundSource
  186. {
  187.     private readonly WaveFile pattern;
  188.     private readonly WaveFile accent;
  189.     private readonly WaveFile intro;
  190.     private readonly WaveFile fill;
  191.     private readonly WaveFile ending;
  192.  
  193.     public MultiFileSoundSource(WaveFile pattern, WaveFile accent, WaveFile intro, WaveFile fill, WaveFile ending)
  194.     {
  195.         this.pattern = pattern;
  196.         this.accent = accent;
  197.         this.intro = intro;
  198.         this.fill = fill;
  199.         this.ending = ending;
  200.     }
  201.  
  202.     public IWaveSource ToWaveSource()
  203.     {
  204.         var waveSources = new List<IWaveSource>();
  205.  
  206.         // Add the intro wave source if present
  207.         if (intro != null)
  208.         {
  209.             waveSources.Add(intro.WaveFileReader.ToSampleSource().ToWaveSource());
  210.         }
  211.  
  212.         // Add the pattern wave source with the specified accent
  213.         var patternSource = pattern.WaveFileReader.ToSampleSource().ToWaveSource();
  214.         if (accent != null)
  215.         {
  216.             var accentSource = accent.WaveFileReader.ToSampleSource().ToWaveSource();
  217.             patternSource = new AccentWaveSource(patternSource, accentSource);
  218.         }
  219.         waveSources.Add(patternSource);
  220.  
  221.         // Add the fill wave source if present
  222.         if (fill != null)
  223.         {
  224.             waveSources.Add(fill.WaveFileReader.ToSampleSource().ToWaveSource());
  225.         }
  226.  
  227.         // Add the ending wave source if present
  228.         if (ending != null)
  229.         {
  230.             waveSources.Add(ending.WaveFileReader.ToSampleSource().ToWaveSource());
  231.         }
  232.  
  233.         // Combine all wave sources into a single wave source
  234.         var combinedWaveSource = new ConcatenatingWaveSource(waveSources);
  235.  
  236.         // Return the combined wave source
  237.         return combinedWaveSource.ToWaveSource();
  238.     }
  239. }
  240.  
  241. public class AccentWaveSource : IWaveSource
  242. {
  243.     private readonly IWaveSource patternSource;
  244.     private readonly IWaveSource accentSource;
  245.  
  246.     public AccentWaveSource(IWaveSource patternSource, IWaveSource accentSource)
  247.     {
  248.         this.patternSource = patternSource;
  249.         this.accentSource = accentSource;
  250.         if (patternSource.WaveFormat.SampleRate != accentSource.WaveFormat.SampleRate)
  251.         {
  252.             throw new ArgumentException("The sample rate of the pattern and accent sources must be the same.");
  253.         }
  254.     }
  255.  
  256.     public WaveFormat WaveFormat => patternSource.WaveFormat;
  257.  
  258.     public long Length => patternSource.Length;
  259.  
  260.     public long Position
  261.     {
  262.         get => patternSource.Position;
  263.         set => patternSource.Position = value;
  264.     }
  265.  
  266.     public int Read(byte[] buffer, int offset, int count)
  267.     {
  268.         var patternBuffer = new byte[count];
  269.         var accentBuffer = new byte[count];
  270.         var numRead = patternSource.Read(patternBuffer, offset, count);
  271.         var accentNumRead = accentSource.Read(accentBuffer, offset, count);
  272.         if (numRead != accentNumRead)
  273.         {
  274.             throw new InvalidOperationException("The number of samples read from the pattern and accent sources must be the same.");
  275.         }
  276.         for (int i = 0; i < numRead; i += 2)
  277.         {
  278.             short patternSample = BitConverter.ToInt16(patternBuffer, i);
  279.             short accentSample = BitConverter.ToInt16(accentBuffer, i);
  280.             short combinedSample = (short)(patternSample + accentSample);
  281.             byte[] combinedSampleBytes = BitConverter.GetBytes(combinedSample);
  282.             buffer[offset + i] = combinedSampleBytes[0];
  283.             buffer[offset + i + 1] = combinedSampleBytes[1];
  284.         }
  285.         return numRead;
  286.     }
  287. }
  288.  
  289. public class ConcatenatingWaveSource : IWaveSource
  290. {
  291.     private readonly List<IWaveSource> waveSources;
  292.     private int currentIndex;
  293.     private long position;
  294.  
  295.     public ConcatenatingWaveSource(IEnumerable<IWaveSource> waveSources)
  296.     {
  297.         this.waveSources = new List<IWaveSource>(waveSources);
  298.         currentIndex = 0;
  299.         position = 0;
  300.     }
  301.  
  302.     public WaveFormat WaveFormat => waveSources[currentIndex].WaveFormat;
  303.  
  304.     public long Length => waveSources.Sum(ws => ws.Length);
  305.  
  306.     public long Position
  307.     {
  308.         get => position;
  309.         set
  310.         {
  311.             if (value < 0 || value >= Length)
  312.             {
  313.                 throw new ArgumentOutOfRangeException(nameof(Position));
  314.             }
  315.             position = value;
  316.             var sourceIndex = 0;
  317.             var sourcePosition = 0L;
  318.             foreach (var source in waveSources)
  319.             {
  320.                 if (position < sourcePosition + source.Length)
  321.                 {
  322.                     currentIndex = sourceIndex;
  323.                     waveSources[currentIndex].Position = position - sourcePosition;
  324.                     break;
  325.                 }
  326.                 sourceIndex++;
  327.                 sourcePosition += source.Length;
  328.             }
  329.         }
  330.     }
  331.  
  332.     public int Read(byte[] buffer, int offset, int count)
  333.     {
  334.         var numRead = waveSources[currentIndex].Read(buffer, offset, count);
  335.         if (numRead == 0 && currentIndex < waveSources.Count - 1)
  336.         {
  337.             currentIndex++;
  338.             waveSources[currentIndex].Position = 0;
  339.             numRead = waveSources[currentIndex].Read(buffer, offset, count);
  340.         }
  341.         position += numRead;
  342.         return numRead;
  343.     }
  344. }
  345.  
  346. }
  347.  
  348.  
  349.  
  350. /*
  351. This code defines the `RhythmDr110` class, which provides a `Play` method that
  352. can be used to play the specified pattern with the specified accents, intro,
  353. fill, and ending using the default audio device. The class uses the `CSCore`
  354. library to play the audio, and it defines several helper classes to read and
  355. combine the audio files.
  356.  
  357. The `WaveFile` class represents an audio file, and it lazily loads the audio file
  358. when the `WaveFileReader` property is accessed. The `MultiFileSoundSource` class
  359. represents a sound source that combines multiple audio files into a single audio
  360. stream, and it uses the `ConcatenatingWaveSource` class to concatenate the audio
  361. streams. The `AccentWaveSource` class represents a wave source that adds an accent
  362. to a pattern by combining the pattern and accent audio streams.
  363.  
  364. The RhythmDr110 class uses these helper classes to read the audio files
  365. for the pattern, accents, intro, fill, and ending, and it creates a new
  366. AccentWaveSource for the pattern and accent. It then creates a
  367. ConcatenatingWaveSource to concatenate the intro, pattern, fill, pattern,
  368. fill, pattern, fill, pattern, ending, and it creates a new WasapiOut to
  369. play the audio.
  370.  
  371. The Play method simply creates a new instance of the RhythmDr110 class
  372. and calls its Play method. Here's an example usage of the RhythmDr110 class:
  373. */
  374.  
  375.  
  376. var rhythmDr110 = new RhythmDr110();
  377. rhythmDr110.Play(patternFile, accentFile, introFile, fillFile, endingFile);
  378.  
  379.  
  380. /*
  381. This will play the specified pattern with the specified accents, intro, fill,
  382. and ending using the default audio device.
  383.  
  384. Note that you'll need to add the necessary using statements and references to
  385. use the CSCore library in your project. You'll also need to add error handling
  386. and input validation to the RhythmDr110 class to ensure that it can handle
  387. invalid input gracefully.
  388. */
Advertisement
Add Comment
Please, Sign In to add comment