public ActionResult AudioAndDictation(int resolutionType, string keySignature, double bpm, int numberOfMeasures) { // We'll add stuff to the Dictionary and return as JSON. var dict = new Dictionary <string, string>(); #region Audio //double bpm = double.Parse(ConfigurationManager.AppSettings["BPM"]); double quarterNoteMillis = (60 / bpm) * 1000; TimeSpan quarterNoteDuration = TimeSpan.FromMilliseconds(quarterNoteMillis); TimeSpan halfNoteDuration = TimeSpan.FromMilliseconds(quarterNoteMillis * 2); TimeSpan dottedHalfNoteDuration = TimeSpan.FromMilliseconds(quarterNoteMillis * 3); TimeSpan wholeNoteDuration = TimeSpan.FromMilliseconds(quarterNoteMillis * 4); // Setup the scale note numbers. int[] scaleNoteNumbers = new int[] { 38, 39, 41, 43, 44, 46, 48, 50, 51 }; // C Major, with a low TI. scaleNoteNumbers = NoteHelper.TransposeScaleNoteNumbers(scaleNoteNumbers, keySignature); // The initial DO. ISampleProvider wholeDoNote = NAudioHelper.GetSampleProvider(scaleNoteNumbers[1], wholeNoteDuration); // Four metronome ticks before the transcription part plays. ISampleProvider[] ticks = new ISampleProvider[4]; string tickFile = HostingEnvironment.MapPath($"~/Samples/Woodblock.wav"); for (int i = 0; i < ticks.Length; i++) { ticks[i] = NAudioHelper.GetSampleProviderFromFile(tickFile, quarterNoteDuration); } List <string> measureRhythms = GetNoteRhythms(); int randomInt = NoteHelper.GetRandomInt(0, measureRhythms.Count); string measureRhythm1 = measureRhythms[randomInt]; randomInt = NoteHelper.GetRandomInt(0, measureRhythms.Count); string measureRhythm2 = measureRhythms[randomInt]; if (numberOfMeasures == 2) { while ((measureRhythm1.Split(',').Count() + measureRhythm2.Split(',').Count()) % 2 == 1) // Ensure an even number of notes, so there's always a (reverse) resolution. { randomInt = NoteHelper.GetRandomInt(0, measureRhythms.Count); measureRhythm2 = measureRhythms[randomInt]; } } randomInt = NoteHelper.GetRandomInt(0, measureRhythms.Count); string measureRhythm3 = measureRhythms[randomInt]; randomInt = NoteHelper.GetRandomInt(0, measureRhythms.Count); string measureRhythm4 = measureRhythms[randomInt]; if (numberOfMeasures == 4) { while ((measureRhythm1.Split(',').Count() + measureRhythm2.Split(',').Count() + measureRhythm3.Split(',').Count() + measureRhythm4.Split(',').Count()) % 2 == 1) { randomInt = NoteHelper.GetRandomInt(0, measureRhythms.Count); measureRhythm4 = measureRhythms[randomInt]; } } string[] measureRhythmSplit1 = measureRhythm1.Split(','); int numberOfNotes1 = measureRhythmSplit1.Length; string[] measureRhythmSplit2 = measureRhythm2.Split(','); int numberOfNotes2 = measureRhythmSplit2.Length; string[] measureRhythmSplit3 = measureRhythm3.Split(','); int numberOfNotes3 = measureRhythmSplit3.Length; string[] measureRhythmSplit4 = measureRhythm4.Split(','); int numberOfNotes4 = measureRhythmSplit4.Length; ISampleProvider[] notes1 = new ISampleProvider[numberOfNotes1]; ISampleProvider[] notes2 = new ISampleProvider[numberOfNotes2]; ISampleProvider[] notes3 = new ISampleProvider[numberOfNotes3]; ISampleProvider[] notes4 = new ISampleProvider[numberOfNotes4]; Queue <int> noteNumberQueue; switch (resolutionType) { case 1: noteNumberQueue = GetResolutionIntQueue(scaleNoteNumbers, 16, 1); // 16 notes max. break; case 2: noteNumberQueue = GetResolutionIntQueue(scaleNoteNumbers, 16, 2); break; case 3: noteNumberQueue = GetResolutionIntQueue(scaleNoteNumbers, 16, 3); break; default: throw new NotSupportedException($"ResolutionType '{resolutionType}' is not supported."); } int[] measureNoteNumbers1 = NoteHelper.PopulateNoteNumbersFromQueue(numberOfNotes1, noteNumberQueue); int[] measureNoteNumbers2 = NoteHelper.PopulateNoteNumbersFromQueue(numberOfNotes2, noteNumberQueue); int[] measureNoteNumbers3 = NoteHelper.PopulateNoteNumbersFromQueue(numberOfNotes3, noteNumberQueue); int[] measureNoteNumbers4 = NoteHelper.PopulateNoteNumbersFromQueue(numberOfNotes4, noteNumberQueue); NoteHelper.CreateSamplesFromRhythmsAndNoteNames(new TimeSpan(), quarterNoteDuration, halfNoteDuration, dottedHalfNoteDuration, wholeNoteDuration, measureRhythmSplit1, notes1, measureNoteNumbers1); NoteHelper.CreateSamplesFromRhythmsAndNoteNames(new TimeSpan(), quarterNoteDuration, halfNoteDuration, dottedHalfNoteDuration, wholeNoteDuration, measureRhythmSplit2, notes2, measureNoteNumbers2); NoteHelper.CreateSamplesFromRhythmsAndNoteNames(new TimeSpan(), quarterNoteDuration, halfNoteDuration, dottedHalfNoteDuration, wholeNoteDuration, measureRhythmSplit3, notes3, measureNoteNumbers3); NoteHelper.CreateSamplesFromRhythmsAndNoteNames(new TimeSpan(), quarterNoteDuration, halfNoteDuration, dottedHalfNoteDuration, wholeNoteDuration, measureRhythmSplit4, notes4, measureNoteNumbers4); ISampleProvider phrase = wholeDoNote; foreach (var tick in ticks) { phrase = phrase.FollowedBy(tick); } foreach (var note1 in notes1) { phrase = phrase.FollowedBy(note1); } foreach (var note2 in notes2) { phrase = phrase.FollowedBy(note2); } if (numberOfMeasures == 4) { foreach (var note3 in notes3) { phrase = phrase.FollowedBy(note3); } foreach (var note4 in notes4) { phrase = phrase.FollowedBy(note4); } } // HACK: Use an empty note because without it, the audio gets cut short. ISampleProvider emptyNote = NAudioHelper.GetSampleProvider(0, 0, SignalGeneratorType.White, halfNoteDuration); phrase = phrase.FollowedBy(emptyNote); SampleToWaveProvider stwp = new SampleToWaveProvider(phrase); MixingSampleProvider msp = new MixingSampleProvider(stwp.WaveFormat); msp.AddMixerInput(stwp); int totalTicks = 8 + (4 * numberOfMeasures); ISampleProvider[] metronomeTicks = new ISampleProvider[totalTicks]; // The first two measures have zero gain, as this is the initial DO whole note and metronome ticks. for (int i = 0; i < 8; i++) { metronomeTicks[i] = NAudioHelper.GetSampleProvider(0, 0, SignalGeneratorType.White, quarterNoteDuration); } for (int i = 8; i < totalTicks; i++) { metronomeTicks[i] = NAudioHelper.GetSampleProviderFromFile(tickFile, quarterNoteDuration); } ISampleProvider metronomePhrase = metronomeTicks[0]; for (int i = 0; i < metronomeTicks.Length; i++) { metronomePhrase = metronomePhrase.FollowedBy(metronomeTicks[i]); } msp.AddMixerInput(metronomePhrase); IWaveProvider wp = msp.ToWaveProvider(); MemoryStream wavStream = new MemoryStream(); //WaveFileWriter.WriteWavFileToStream(wavStream, stwp); WaveFileWriter.WriteWavFileToStream(wavStream, wp); wavStream.Position = 0; wavStream.WavToMp3File(out string fileName); dict.Add("src", fileName); #endregion Audio #region Notation string[] noteNames1 = new string[numberOfNotes1]; NoteHelper.AdjustNoteNamesForKey(keySignature, measureNoteNumbers1, noteNames1); string[] noteNames2 = new string[numberOfNotes2]; NoteHelper.AdjustNoteNamesForKey(keySignature, measureNoteNumbers2, noteNames2); string[] noteNames3 = new string[numberOfNotes3]; NoteHelper.AdjustNoteNamesForKey(keySignature, measureNoteNumbers3, noteNames3); string[] noteNames4 = new string[numberOfNotes4]; NoteHelper.AdjustNoteNamesForKey(keySignature, measureNoteNumbers4, noteNames4); string script1 = NoteHelper.GetEasyScoreScript("transcription1", noteNames1, measureRhythmSplit1, keySignature, true); dict.Add("theScript1", script1); string script2 = NoteHelper.GetEasyScoreScript("transcription2", noteNames2, measureRhythmSplit2, keySignature, false); dict.Add("theScript2", script2); if (numberOfMeasures == 4) { string script3 = NoteHelper.GetEasyScoreScript("transcription3", noteNames3, measureRhythmSplit3, keySignature, false); dict.Add("theScript3", script3); string script4 = NoteHelper.GetEasyScoreScript("transcription4", noteNames4, measureRhythmSplit4, keySignature, false); dict.Add("theScript4", script4); } #endregion Notation var json = Json(dict, JsonRequestBehavior.AllowGet); return(json); }
public ActionResult AudioAndDictation(int intervalType, string keySignature, double bpm, int numberOfMeasures, string smallestRhythmicUnit, bool includeC2) { _log.Debug($"intervalType: {intervalType}, keySignature: {keySignature}, bpm: {bpm}, numberOfMeasures: {numberOfMeasures}, smallestRhythmicUnit: {smallestRhythmicUnit}, includeC2: {includeC2}"); L1C3IntervalType intType = (L1C3IntervalType)intervalType; bool includeEighthNoteRhythms = smallestRhythmicUnit.ToUpper() == "EIGHTH"; // We'll add stuff to the Dictionary and return as JSON. var dict = new Dictionary <string, string>(); #region Audio //double bpm = double.Parse(ConfigurationManager.AppSettings["BPM"]); double quarterNoteMillis = (60 / bpm) * 1000; TimeSpan quarterNoteDuration = TimeSpan.FromMilliseconds(quarterNoteMillis); TimeSpan halfNoteDuration = TimeSpan.FromMilliseconds(quarterNoteMillis * 2); TimeSpan dottedHalfNoteDuration = TimeSpan.FromMilliseconds(quarterNoteMillis * 3); TimeSpan wholeNoteDuration = TimeSpan.FromMilliseconds(quarterNoteMillis * 4); TimeSpan eighthNoteDuration = TimeSpan.FromMilliseconds(quarterNoteMillis / 2); // Setup the scale note numbers. int[] scaleNoteNumbers = new int[] { 38, 39, 41, 43, 44, 46, 48, 50, 51, 53, 55, 56, 58, 60 }; // C Major, low TI to high SO. scaleNoteNumbers = NoteHelper.TransposeScaleNoteNumbers(scaleNoteNumbers, keySignature); // The initial DO. ISampleProvider wholeDoNote = NAudioHelper.GetSampleProvider(scaleNoteNumbers[1], wholeNoteDuration); // Four metronome ticks before the transcription part plays. ISampleProvider[] ticks = new ISampleProvider[4]; string tickFile = HostingEnvironment.MapPath($"~/Samples/Woodblock.wav"); for (int i = 0; i < ticks.Length; i++) { ticks[i] = NAudioHelper.GetSampleProviderFromFile(tickFile, quarterNoteDuration); } List <string> measureRhythms = GetNoteRhythms(includeEighthNoteRhythms); int randomInt; string measureRhythm1; string measureRhythm2; // Ensure there's at least 4 notes per 2 measures, as we need at least one C1 resolutiona and at least 1 C2 interval. // Ensure an even number of notes, so the interval is complete. // Ensure there's exactly one pair of eighth notes for each two measure phrase. while (true) { randomInt = NoteHelper.GetRandomInt(0, measureRhythms.Count); measureRhythm1 = measureRhythms[randomInt]; randomInt = NoteHelper.GetRandomInt(0, measureRhythms.Count); measureRhythm2 = measureRhythms[randomInt]; int totalNotes = measureRhythm1.Split(',').Count() + measureRhythm2.Split(',').Count(); int totalEighthNotes = measureRhythm1.Split(',').Where(w => w == "8").Count() + measureRhythm2.Split(',').Where(w => w == "8").Count(); // Ensure a minimum number of notes for the 2 measure phrase. This could be 4, or 6 if C2 intervals are included. int minimumNumberOfNotes = includeC2 ? 6 : 4; if (totalNotes < minimumNumberOfNotes) { continue; } // Ensure an even number of notes. if (totalNotes % 2 != 0) { continue; } // If quarter note is smallest rhythmic unit, ensure no eighth notes. if (!includeEighthNoteRhythms && totalEighthNotes > 0) { continue; } // If eighth note is smallest rhythic unit, ensure just one pair of eighth notes. if (includeEighthNoteRhythms && totalEighthNotes != 2) { continue; } break; } string measureRhythm3 = string.Empty; string measureRhythm4 = string.Empty; // TODO: Figure out how to refactor this. Kinda messy, but for now we need to populate measures 3 and 4 even if the user // choose just 2 measures. randomInt = NoteHelper.GetRandomInt(0, measureRhythms.Count); measureRhythm3 = measureRhythms[randomInt]; randomInt = NoteHelper.GetRandomInt(0, measureRhythms.Count); measureRhythm4 = measureRhythms[randomInt]; //if (numberOfMeasures == 4) if (true) { while (true) { randomInt = NoteHelper.GetRandomInt(0, measureRhythms.Count); measureRhythm3 = measureRhythms[randomInt]; randomInt = NoteHelper.GetRandomInt(0, measureRhythms.Count); measureRhythm4 = measureRhythms[randomInt]; int totalNotes = measureRhythm3.Split(',').Count() + measureRhythm4.Split(',').Count(); int totalEighthNotes = measureRhythm3.Split(',').Where(w => w == "8").Count() + measureRhythm4.Split(',').Where(w => w == "8").Count(); // Ensure a minimum number of notes for the 2 measure phrase. This could be 4, or 6 if C2 intervals are included. int minimumNumberOfNotes = includeC2 ? 6 : 4; if (totalNotes < minimumNumberOfNotes) { continue; } // Ensure an even number of notes. if (totalNotes % 2 != 0) { continue; } // If quarter note is smallest rhythmic unit, ensure no eighth notes. if (!includeEighthNoteRhythms && totalEighthNotes > 0) { continue; } // If eighth note is smallest rhythic unit, ensure just one pair of eighth notes. if (includeEighthNoteRhythms && totalEighthNotes != 2) { continue; } break; } } string[] measureRhythmSplit1 = measureRhythm1.Split(','); int numberOfNotes1 = measureRhythmSplit1.Length; string[] measureRhythmSplit2 = measureRhythm2.Split(','); int numberOfNotes2 = measureRhythmSplit2.Length; string[] measureRhythmSplit3 = measureRhythm3.Split(','); int numberOfNotes3 = measureRhythmSplit3.Length; string[] measureRhythmSplit4 = measureRhythm4.Split(','); int numberOfNotes4 = measureRhythmSplit4.Length; ISampleProvider[] notes1 = new ISampleProvider[numberOfNotes1]; ISampleProvider[] notes2 = new ISampleProvider[numberOfNotes2]; ISampleProvider[] notes3 = new ISampleProvider[numberOfNotes3]; ISampleProvider[] notes4 = new ISampleProvider[numberOfNotes4]; int first2MeasuresNumberOfNotes = numberOfNotes1 + numberOfNotes2; int second2MeasuresNumberOfNotes = numberOfNotes3 + numberOfNotes4; Queue <int> noteNumberQueue = new Queue <int>(); bool criteriaSatisfied = false; int numberOfTries = 0; int maxNumberOfTries = 1999; var parametersToString = string.Empty; try { while (!(noteNumberQueue.AllStepsWithinRange(12) && noteNumberQueue.AllNotesWithinRange(12) && criteriaSatisfied)) { numberOfTries++; //_log.Info($"numberOfTries: {numberOfTries}"); //_log.Info($"measureRhythm1: {measureRhythm1}"); //_log.Info($"measureRhythm2: {measureRhythm2}"); if (numberOfTries > maxNumberOfTries) { throw new PhraseGenerationException($"Aborted phrase generation after {numberOfTries} tries."); } noteNumberQueue = GetIntervalIntQueue(scaleNoteNumbers, first2MeasuresNumberOfNotes, second2MeasuresNumberOfNotes, intType, out criteriaSatisfied, includeC2, out parametersToString); } } catch (PhraseGenerationException pgEx) { _log.Trace($"{numberOfTries} - {parametersToString}"); _log.Error(pgEx, $"The maxNumberOfTries ({maxNumberOfTries}) has been exceeded."); dict.Add("hasError", "yep"); dict.Add("numberOfTries", numberOfTries.ToString()); var jsonEx = Json(dict, JsonRequestBehavior.AllowGet); return(jsonEx); } _log.Trace(numberOfTries); int[] measureNoteNumbers1 = NoteHelper.PopulateNoteNumbersFromQueue(numberOfNotes1, noteNumberQueue); int[] measureNoteNumbers2 = NoteHelper.PopulateNoteNumbersFromQueue(numberOfNotes2, noteNumberQueue); int[] measureNoteNumbers3 = NoteHelper.PopulateNoteNumbersFromQueue(numberOfNotes3, noteNumberQueue); int[] measureNoteNumbers4 = NoteHelper.PopulateNoteNumbersFromQueue(numberOfNotes4, noteNumberQueue); NoteHelper.CreateSamplesFromRhythmsAndNoteNames(eighthNoteDuration, quarterNoteDuration, halfNoteDuration, dottedHalfNoteDuration, wholeNoteDuration, measureRhythmSplit1, notes1, measureNoteNumbers1); NoteHelper.CreateSamplesFromRhythmsAndNoteNames(eighthNoteDuration, quarterNoteDuration, halfNoteDuration, dottedHalfNoteDuration, wholeNoteDuration, measureRhythmSplit2, notes2, measureNoteNumbers2); NoteHelper.CreateSamplesFromRhythmsAndNoteNames(eighthNoteDuration, quarterNoteDuration, halfNoteDuration, dottedHalfNoteDuration, wholeNoteDuration, measureRhythmSplit3, notes3, measureNoteNumbers3); NoteHelper.CreateSamplesFromRhythmsAndNoteNames(eighthNoteDuration, quarterNoteDuration, halfNoteDuration, dottedHalfNoteDuration, wholeNoteDuration, measureRhythmSplit4, notes4, measureNoteNumbers4); ISampleProvider phrase = wholeDoNote; foreach (var tick in ticks) { phrase = phrase.FollowedBy(tick); } foreach (var note1 in notes1) { phrase = phrase.FollowedBy(note1); } foreach (var note2 in notes2) { phrase = phrase.FollowedBy(note2); } if (numberOfMeasures == 4) { foreach (var note3 in notes3) { phrase = phrase.FollowedBy(note3); } foreach (var note4 in notes4) { phrase = phrase.FollowedBy(note4); } } // HACK: Use an empty note because without it, the audio gets cut short. ISampleProvider emptyNote = NAudioHelper.GetSampleProvider(0, 0, SignalGeneratorType.White, halfNoteDuration); phrase = phrase.FollowedBy(emptyNote); SampleToWaveProvider stwp = new SampleToWaveProvider(phrase); MixingSampleProvider msp = new MixingSampleProvider(stwp.WaveFormat); msp.AddMixerInput(stwp); int totalTicks = 8 + (4 * numberOfMeasures); ISampleProvider[] metronomeTicks = new ISampleProvider[totalTicks]; // The first two measures have zero gain, as this is the initial DO whole note and metronome ticks. for (int i = 0; i < 8; i++) { metronomeTicks[i] = NAudioHelper.GetSampleProvider(0, 0, SignalGeneratorType.White, quarterNoteDuration); } for (int i = 8; i < totalTicks; i++) { metronomeTicks[i] = NAudioHelper.GetSampleProviderFromFile(tickFile, quarterNoteDuration); } ISampleProvider metronomePhrase = metronomeTicks[0]; for (int i = 0; i < metronomeTicks.Length; i++) { metronomePhrase = metronomePhrase.FollowedBy(metronomeTicks[i]); } msp.AddMixerInput(metronomePhrase); IWaveProvider wp = msp.ToWaveProvider(); MemoryStream wavStream = new MemoryStream(); //WaveFileWriter.WriteWavFileToStream(wavStream, stwp); WaveFileWriter.WriteWavFileToStream(wavStream, wp); wavStream.Position = 0; wavStream.WavToMp3File(out string fileName); dict.Add("src", fileName); dict.Add("numberOfTries", numberOfTries.ToString()); #endregion Audio #region Notation string[] noteNames1 = new string[numberOfNotes1]; NoteHelper.AdjustNoteNamesForKey(keySignature, measureNoteNumbers1, noteNames1); string[] noteNames2 = new string[numberOfNotes2]; NoteHelper.AdjustNoteNamesForKey(keySignature, measureNoteNumbers2, noteNames2); string[] noteNames3 = new string[numberOfNotes3]; NoteHelper.AdjustNoteNamesForKey(keySignature, measureNoteNumbers3, noteNames3); string[] noteNames4 = new string[numberOfNotes4]; NoteHelper.AdjustNoteNamesForKey(keySignature, measureNoteNumbers4, noteNames4); string script1 = NoteHelper.GetEasyScoreScript3("transcription1", noteNames1, measureRhythmSplit1, keySignature, true); //string script1 = NoteHelper.GetMusicXmlScript("transcription1", noteNames1, measureRhythmSplit1, keySignature, false); dict.Add("theScript1", script1); string script2 = NoteHelper.GetEasyScoreScript3("transcription2", noteNames2, measureRhythmSplit2, keySignature, false); dict.Add("theScript2", script2); if (numberOfMeasures == 4) { string script3 = NoteHelper.GetEasyScoreScript3("transcription3", noteNames3, measureRhythmSplit3, keySignature, false); dict.Add("theScript3", script3); string script4 = NoteHelper.GetEasyScoreScript3("transcription4", noteNames4, measureRhythmSplit4, keySignature, false); dict.Add("theScript4", script4); } #endregion Notation var json = Json(dict, JsonRequestBehavior.AllowGet); return(json); }