/// <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(); } }
/// <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); }