示例#1
0
        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);
        }
示例#2
0
        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);
        }