Beispiel #1
0
        public IEnumerable <MidiChunk> Convert(IEnumerable <MidiChunk> chunks)
        {
            ThrowIfArgument.IsNull(nameof(chunks), chunks);

            //

            var trackChunks = chunks.OfType <TrackChunk>().ToArray();

            if (trackChunks.Length == 1)
            {
                return(chunks);
            }

            //

            var eventsDescriptors = trackChunks
                                    .SelectMany(trackChunk =>
            {
                var absoluteTime = 0L;
                var channel      = -1;
                return(trackChunk.Events
                       .Select(midiEvent =>
                {
                    var channelPrefixEvent = midiEvent as ChannelPrefixEvent;
                    if (channelPrefixEvent != null)
                    {
                        channel = channelPrefixEvent.Channel;
                    }

                    if (!(midiEvent is MetaEvent))
                    {
                        channel = -1;
                    }

                    return new EventDescriptor(midiEvent, (absoluteTime += midiEvent.DeltaTime), channel);
                }));
            })
                                    .OrderBy(d => d, new EventDescriptorComparer());

            //

            var resultTrackChunk = new TrackChunk();
            var time             = 0L;

            foreach (var eventDescriptor in eventsDescriptors)
            {
                MidiEvent midiEvent = eventDescriptor.Event.Clone();

                midiEvent.DeltaTime = eventDescriptor.AbsoluteTime - time;
                resultTrackChunk.Events.Add(midiEvent);

                time = eventDescriptor.AbsoluteTime;
            }

            //

            return(new[] { resultTrackChunk }.Concat(chunks.Where(c => !(c is TrackChunk))));
        }
Beispiel #2
0
        private static ushort?GetSequenceNumber(TrackChunk trackChunk)
        {
            ThrowIfArgument.IsNull(nameof(trackChunk), trackChunk);

            return(trackChunk.Events
                   .TakeWhile(m => m.DeltaTime == 0)
                   .OfType <SequenceNumberEvent>()
                   .FirstOrDefault()
                   ?.Number);
        }
 /// <summary>
 /// Handles finish of track chunk reading. Called after track chunk is read.
 /// </summary>
 /// <param name="trackChunk">Track chunk read.</param>
 /// <remarks>
 /// This method called within <see cref="TargetScope.TrackChunk"/> scope.
 /// </remarks>
 public virtual void OnFinishTrackChunkReading(TrackChunk trackChunk)
 {
 }
 /// <summary>
 /// Handles start of track chunk's content reading. Called after track chunk header is read.
 /// </summary>
 /// <param name="trackChunk">Track chunk is being read.</param>
 /// <remarks>
 /// This method called within <see cref="TargetScope.TrackChunk"/> scope.
 /// </remarks>
 public virtual void OnStartTrackChunkContentReading(TrackChunk trackChunk)
 {
 }
Beispiel #5
0
        /// <summary>
        /// Reads a chunk from a MIDI-file.
        /// </summary>
        /// <param name="reader">Reader to read a chunk with.</param>
        /// <param name="settings">Settings according to which a chunk must be read.</param>
        /// <param name="actualTrackChunksCount">Actual count of track chunks at the moment.</param>
        /// <param name="expectedTrackChunksCount">Expected count of track chunks.</param>
        /// <returns>A MIDI-file chunk.</returns>
        /// <exception cref="ObjectDisposedException">Method was called after the reader was disposed.</exception>
        /// <exception cref="IOException">An I/O error occurred on the underlying stream.</exception>
        /// <exception cref="UnknownChunkException">Chunk to be read has unknown ID and that
        /// should be treated as error accordng to the specified <paramref name="settings"/>.</exception>
        /// <exception cref="UnexpectedTrackChunksCountException">Actual track chunks
        /// count is greater than expected one and that should be treated as error according to
        /// the specified <paramref name="settings"/>.</exception>
        /// <exception cref="InvalidChunkSizeException">Actual chunk's size differs from the one declared
        /// in its header and that should be treated as error according to the specified
        /// <paramref name="settings"/>.</exception>
        /// <exception cref="UnknownChannelEventException">Reader has encountered an unknown channel event.</exception>
        /// <exception cref="NotEnoughBytesException">Value cannot be read since the reader's underlying stream
        /// doesn't have enough bytes.</exception>
        /// <exception cref="UnexpectedRunningStatusException">Unexpected running status is encountered.</exception>
        /// <exception cref="MissedEndOfTrackEventException">Track chunk doesn't end with End Of Track event and that
        /// should be treated as error accordng to the specified <paramref name="settings"/>.</exception>
        /// <exception cref="InvalidChannelEventParameterValueException">Value of a channel event's parameter
        /// just read is invalid.</exception>
        /// <exception cref="InvalidMetaEventParameterValueException">Value of a meta event's parameter
        /// just read is invalid.</exception>
        private static MidiChunk ReadChunk(MidiReader reader, ReadingSettings settings, int actualTrackChunksCount, int?expectedTrackChunksCount)
        {
            MidiChunk chunk = null;

            try
            {
                var chunkId = reader.ReadString(MidiChunk.IdLength);
                if (chunkId.Length < MidiChunk.IdLength)
                {
                    switch (settings.NotEnoughBytesPolicy)
                    {
                    case NotEnoughBytesPolicy.Abort:
                        throw new NotEnoughBytesException("Chunk ID cannot be read since the reader's underlying stream doesn't have enough bytes.",
                                                          MidiChunk.IdLength,
                                                          chunkId.Length);

                    case NotEnoughBytesPolicy.Ignore:
                        return(null);
                    }
                }

                //

                switch (chunkId)
                {
                case HeaderChunk.Id:
                    chunk = new HeaderChunk();
                    break;

                case TrackChunk.Id:
                    chunk = new TrackChunk();
                    break;

                default:
                    chunk = TryCreateChunk(chunkId, settings.CustomChunkTypes);
                    break;
                }

                //

                if (chunk == null)
                {
                    switch (settings.UnknownChunkIdPolicy)
                    {
                    case UnknownChunkIdPolicy.ReadAsUnknownChunk:
                        chunk = new UnknownChunk(chunkId);
                        break;

                    case UnknownChunkIdPolicy.Skip:
                        var size = reader.ReadDword();
                        reader.Position += size;
                        return(null);

                    case UnknownChunkIdPolicy.Abort:
                        throw new UnknownChunkException($"'{chunkId}' chunk ID is unknown.", chunkId);
                    }
                }

                //

                if (chunk is TrackChunk && expectedTrackChunksCount != null && actualTrackChunksCount >= expectedTrackChunksCount)
                {
                    ReactOnUnexpectedTrackChunksCount(settings.UnexpectedTrackChunksCountPolicy, actualTrackChunksCount, expectedTrackChunksCount.Value);

                    switch (settings.ExtraTrackChunkPolicy)
                    {
                    case ExtraTrackChunkPolicy.Read:
                        break;

                    case ExtraTrackChunkPolicy.Skip:
                        var size = reader.ReadDword();
                        reader.Position += size;
                        return(null);
                    }
                }

                //

                chunk.Read(reader, settings);
            }
            catch (NotEnoughBytesException ex)
            {
                ReactOnNotEnoughBytes(settings.NotEnoughBytesPolicy, ex);
            }
            catch (EndOfStreamException ex)
            {
                ReactOnNotEnoughBytes(settings.NotEnoughBytesPolicy, ex);
            }

            return(chunk);
        }
        /// <summary>
        /// Gets all channel numbers presented in the specified <see cref="TrackChunk"/>.
        /// </summary>
        /// <param name="trackChunk"><see cref="TrackChunk"/> to get channels of.</param>
        /// <returns>Collection of channel numbers presented in the <paramref name="trackChunk"/>.</returns>
        /// <exception cref="ArgumentNullException"><paramref name="trackChunk"/> is <c>null</c>.</exception>
        public static IEnumerable <FourBitNumber> GetChannels(this TrackChunk trackChunk)
        {
            ThrowIfArgument.IsNull(nameof(trackChunk), trackChunk);

            return(trackChunk.Events.OfType <ChannelEvent>().Select(e => e.Channel).Distinct().ToArray());
        }
        /// <summary>
        /// Splits a track chunk into multiple ones that correspond to <see cref="MidiFileFormat.MultiTrack"/>.
        /// </summary>
        /// <param name="trackChunk">Track chunk to split into multiple ones.</param>
        /// <returns>Multiple track chunks that represent <paramref name="trackChunk"/>.</returns>
        /// <remarks>
        /// Note that events will be cloned so events in the result track chunks will not be equal
        /// by reference to events in the <paramref name="trackChunk"/>.
        /// </remarks>
        /// <exception cref="ArgumentNullException"><paramref name="trackChunk"/> is <c>null</c>.</exception>
        public static IEnumerable <TrackChunk> Explode(this TrackChunk trackChunk)
        {
            ThrowIfArgument.IsNull(nameof(trackChunk), trackChunk);

            return(ConvertTrackChunks(new[] { trackChunk }, MidiFileFormat.MultiTrack));
        }