Ejemplo n.º 1
0
        /// <summary> Constructs an IMidiFile instance based on a MIDI file stream.</summary>
        /// <param name="stream"> stream of the midi file content. </param>
        /// <param name="disposeStream"> Set true if stream is to be closed by the constructor once it's done reading the file. </param>
        internal DryWetMidiAdapter(Stream stream, bool disposeStream = false)
        {
            // initialization
            Stream          = stream;
            stream.Position = 0;
            _midiContent    = MidiFile.Read(stream);
            _tempoMap       = _midiContent.GetTempoMap();
            IList <TrackChunk> trackChunks = _midiContent.GetTrackChunks().ToList();

            _metadataTrack = trackChunks.First();
            _metaEvents    = _metadataTrack.Events.ToList();

            // set midi title property
            Title = (((from e in _metaEvents
                       where e.EventType == MidiEventType.SequenceTrackName
                       select e).First()) as BaseTextEvent)?.Text ?? "Undefined";

            // set key signature property
            TimeSignatureEvent timeSignatureEvent = (from e in _metaEvents
                                                     where e.EventType == MidiEventType.TimeSignature
                                                     select e)?.First() as TimeSignatureEvent;

            KeySignature = MusicTheoryFactory.CreateDuration(timeSignatureEvent.Numerator, timeSignatureEvent.Denominator, false);

            // set number of bars property
            BarBeatFractionTimeSpan duration = _midiContent.GetDuration <BarBeatFractionTimeSpan>();

            NumberOfBars = (int)duration.Bars + (int)Math.Ceiling(duration.Beats / timeSignatureEvent.Numerator);

            // set BPM property
            BeatsPerMinute = (byte)(_midiContent.GetTempoMap()?.Tempo.AtTime(0).BeatsPerMinute);

            // set MIDI pitch range
            IEnumerable <DWMidiI.Note> notes = _midiContent.GetNotes();

            LowestPitch  = (NotePitch)(byte)(notes.Min(note => note.NoteNumber));
            HighestPitch = (NotePitch)(byte)(notes.Max(note => note.NoteNumber));

            // dispose stream if requested
            if (disposeStream)
            {
                stream.Dispose();
            }
        }
Ejemplo n.º 2
0
        /// <inheritdoc cref="ReadChordsFromFile(string)"/>
        /// <param name="streamReader"> Input stream which contains the chords data. </param>
        public static IList <IBar> ReadChordsFromFile(StreamReader streamReader)
        {
            streamReader.BaseStream.Position = 0;

            // initialization
            uint        lineNumber       = 1;
            string      currentLine      = null;
            List <IBar> chordProgression = new List <IBar>();
            IBar        bar = null;
            NoteName    chordRoot;
            ChordType   chordType;
            byte        barNumerator    = 0;
            byte        barDenominator  = 0;
            byte        numberOfBeats   = 0;
            byte        totalBeatsInBar = 0;

            string[] lineTokens          = null;
            string[] barTimeSignature    = null;
            string[] chordProperties     = null;
            string   customErrorMessage  = string.Empty;
            string   genericErrorMessage = $"Error parsing chord progression file.\n";

            // parse file line after line
            while ((currentLine = streamReader.ReadLine()) != null)
            {
                // skip empty lines
                if (string.IsNullOrWhiteSpace(currentLine))
                {
                    continue;
                }

                // validate minimum tokens in line (at least a time signature and 1 chord)
                lineTokens = currentLine.Split('\b', '\t', ' ');
                if (lineTokens?.Length < 2)
                {
                    customErrorMessage = $"Line {lineNumber} must include a time signature and at least one chord.";
                    throw new FormatException(genericErrorMessage + customErrorMessage);
                }

                // set bar's time signature (first token of each line)
                barTimeSignature = lineTokens[0].Split('/');
                if ((barTimeSignature?.Length != 2) ||
                    (!Byte.TryParse(barTimeSignature?[0], out barNumerator)) ||
                    (!Byte.TryParse(barTimeSignature?[1], out barDenominator)))
                {
                    customErrorMessage = $"Invalid time signature format in line {lineNumber}: '{lineTokens[0]}'. The required format is 'numerator/denominator', for example 4/4.";
                    throw new FormatException(genericErrorMessage + customErrorMessage);
                }
                bar = MusicTheoryFactory.CreateBar(MusicTheoryFactory.CreateDuration(barNumerator, barDenominator, false));

                // set bar's chords (rest of tokens in line)
                for (int i = 1; i < lineTokens.Length; i++)
                {
                    // skip white spaces between the tokens
                    if (string.IsNullOrWhiteSpace(lineTokens[i]))
                    {
                        continue;
                    }

                    // parse chord properties: root, type & duration
                    chordProperties = lineTokens[i].Split('-');
                    if ((!Enum.TryParse(chordProperties?[0], out chordRoot)) ||
                        (!Enum.TryParse(chordProperties?[1], out chordType)) ||
                        (!Byte.TryParse(chordProperties?[2], out numberOfBeats)))
                    {
                        customErrorMessage = $"Invalid chord format in line {lineNumber}: '{lineTokens[i]}'. The required format is '{typeof(NoteName)}-{typeof(ChordType)}-DurationInBeats', for example F-Major7-2.";
                        throw new FormatException(genericErrorMessage + customErrorMessage);
                    }
                    totalBeatsInBar += numberOfBeats;
                    bar.Chords.Add(MusicTheoryFactory.CreateChord(chordRoot, chordType, MusicTheoryFactory.CreateDuration(numberOfBeats, bar.TimeSignature.Denominator)));
                }

                // validate bar's chords total duration == bar's duration
                if (bar.TimeSignature.Numerator != totalBeatsInBar)
                {
                    customErrorMessage = $"Line {lineNumber}: Total number of beats of chords in bar {chordProgression.Count + 1} which is {totalBeatsInBar} must be equal to the bar's key signature numerator, which is {bar.TimeSignature.Numerator}.";
                    throw new FormatException(genericErrorMessage + customErrorMessage);
                }

                // add curent line's bar
                chordProgression.Add(bar);
                lineNumber++;

                // clean variables old values for next iteration
                bar          = null;
                currentLine  = customErrorMessage = string.Empty;
                lineTokens   = chordProperties = barTimeSignature = null;
                barNumerator = barDenominator = numberOfBeats = totalBeatsInBar = 0;
            }
            return(chordProgression);
        }