Example #1
0
        private Song.Automation ConvertAutomation(ReaperAutomation reaperAutomation, int deviceIndex, string trackName)
        {
            var auto = new Song.Automation();

            auto.DeviceIndex = deviceIndex;
            auto.ParamId     = reaperAutomation.ParameterId;
            auto.Points      = new List <Song.Point>();
            foreach (var p in reaperAutomation.Points)
            {
                if (p.Position < trackStart)
                {
                    continue;
                }

                if (p.Shape != ReaperPointShape.Linear)
                {
                    logger.WriteLine("WARNING: Automation Shape {0} unsupported on Master ", p.Shape.ToString());
                }

                auto.Points.Add(new Song.Point()
                {
                    TimeStamp = SecondsToSamples(p.Position - trackStart),
                    Value     = p.Value
                });
            }

            return(auto);
        }
Example #2
0
                public AutomationDiff(Song.Automation a, Song.Automation b, int index)
                {
                    this.a     = a;
                    this.b     = b;
                    this.index = index;

                    deviceIndex = a.DeviceIndex != b.DeviceIndex;
                    paramId     = a.ParamId != b.ParamId;

                    if (a.DeltaCodedPoints.Count != b.DeltaCodedPoints.Count)
                    {
                        pointCounts = true;
                    }
                    else
                    {
                        for (int i = 0; i < a.Points.Count; i++)
                        {
                            var aPoint    = a.DeltaCodedPoints[i];
                            var bPoint    = b.DeltaCodedPoints[i];
                            var pointDiff = new PointDiff(aPoint, bPoint, i);
                            if (!pointDiff.IsEmpty)
                            {
                                pointDiffs.Add(pointDiff);
                            }
                        }
                    }
                }
Example #3
0
 private void SortPoints(Song.Automation auto)
 {
     auto.Points.Sort((a, b) =>
     {
         if (a.TimeStamp > b.TimeStamp)
         {
             return(1);
         }
         if (a.TimeStamp < b.TimeStamp)
         {
             return(-1);
         }
         return(0);
     });
 }
Example #4
0
        // convert channel track to wavesabre song track
        private Song.Track ConvertGenerator(Channel chan)
        {
            var track = new Song.Track();

            track.Devices = new List <Song.Device>();

            var generator = (GeneratorData)chan.Data;

            track.Volume = (float)generator.Volume / 10000;
            string trackName = chan.Name != "" ? chan.Name : project.Channels.IndexOf(chan).ToString();

            track.Name = trackName;
            var sabreDevice = PlugToDevice(generator.Plugin);

            if (sabreDevice == null)
            {
                logger.WriteLine("WARNING: Channel {0} has unknown plugin {1}", trackName, generator.Plugin.Name);
            }
            else
            {
                if (sabreDevice != null)
                {
                    track.Devices.Add(sabreDevice);
                }
            }

            // notes
            foreach (var flTrack in project.Tracks)
            {
                foreach (var pl in flTrack.Items)
                {
                    if (pl is PatternPlaylistItem)
                    {
                        var pl2      = (PatternPlaylistItem)pl;
                        int position = pl.Position;
                        foreach (var notes in pl2.Pattern.Notes)
                        {
                            if (notes.Key == chan)      //  generator matches
                            {
                                foreach (var note in notes.Value)
                                {
                                    var startOffset  = pl2.StartOffset < 0 ? 0 : pl2.StartOffset;
                                    var endOffset    = pl2.EndOffset < 0 ? pl2.Length : pl2.EndOffset;
                                    var notePosition = note.Position - startOffset;
                                    var noteEnd      = notePosition + note.Length;

                                    if (project.PlayTruncatedNotes)     // handling for play truncated notes
                                    {
                                        if (noteEnd <= 0)               // notes ends before block, skip it
                                        {
                                            continue;
                                        }

                                        if (notePosition >= pl2.Length)   // note start is after block, skip it
                                        {
                                            continue;
                                        }

                                        if (notePosition < 0)
                                        {
                                            notePosition = 0;           // note ends inside block, start position inside block
                                        }
                                        if (noteEnd > endOffset)        // note ends out after block, truncate
                                        {
                                            noteEnd = endOffset;
                                        }
                                    }
                                    else
                                    {
                                        if (notePosition < 0)
                                        {
                                            continue;                   // note starts before block, skip it
                                        }
                                    }

                                    // note position is good, so add it
                                    track.Events.Add(new Song.Event()
                                    {
                                        Type      = Song.EventType.NoteOn,
                                        Note      = note.Key,
                                        Velocity  = note.Velocity,
                                        TimeStamp = PositionToSamples(notePosition + position)
                                    });

                                    track.Events.Add(new Song.Event()
                                    {
                                        Type      = Song.EventType.NoteOff,
                                        Note      = note.Key,
                                        Velocity  = 0,
                                        TimeStamp = PositionToSamples(noteEnd + position)
                                    });
                                }
                            }
                        }
                    }
                }
            }

            // sort note events
            track.Events.Sort((a, b) =>
            {
                if (a.TimeStamp > b.TimeStamp)
                {
                    return(1);
                }
                if (a.TimeStamp < b.TimeStamp)
                {
                    return(-1);
                }
                if (a.Type == Song.EventType.NoteOn && b.Type == Song.EventType.NoteOff)
                {
                    return(1);
                }
                if (a.Type == Song.EventType.NoteOff && b.Type == Song.EventType.NoteOn)
                {
                    return(-1);
                }
                return(0);
            });

            // automations
            foreach (var flTrack in project.Tracks)
            {
                foreach (var pl in flTrack.Items)
                {
                    if (pl is ChannelPlaylistItem)
                    {
                        var pl2         = (ChannelPlaylistItem)pl;
                        var startOffset = pl2.StartOffset < 0 ? 0 : pl2.StartOffset;
                        var endOffset   = pl2.EndOffset < 0 ? pl2.Length : pl2.EndOffset;

                        int position = pl2.Position;
                        if (pl2.Channel.Data is AutomationData)
                        {
                            var auto = (AutomationData)pl2.Channel.Data;
                            if (auto.Channel == null)
                            {
                                continue;
                            }

                            if (chan.Id == auto.Channel.Id)
                            {
                                var newAuto = track.Automations.FirstOrDefault(a => a.ParamId == auto.Parameter);
                                if (newAuto == null)
                                {
                                    newAuto = new Song.Automation()
                                    {
                                        DeviceIndex = 0,
                                        ParamId     = auto.Parameter
                                    };
                                    track.Automations.Add(newAuto);
                                }

                                var    keyCount         = 0;
                                var    currentPosition  = 0;
                                var    previousPosition = 0;
                                double previousValue    = 0;
                                double newValue         = 0;

                                position = position - startOffset;
                                foreach (var key in auto.Keyframes)
                                {
                                    if (key.Tension != 0)
                                    {
                                        logger.WriteLine(string.Format("Tension value not supported on track {0}", trackName));
                                    }

                                    currentPosition += key.Position;
                                    var autoPosition = currentPosition;

                                    // ensure previous value persists between blocks
                                    if (keyCount == 0)
                                    {
                                        if (newAuto.Points.Count > 0)
                                        {
                                            var lastAuto = newAuto.Points.LastOrDefault();
                                            newAuto.Points.Add(new Song.Point()
                                            {
                                                TimeStamp = PositionToSamples(position),
                                                Value     = lastAuto.Value
                                            });
                                        }
                                    }

                                    // this key is before the clip block, skip it
                                    if (autoPosition < startOffset)
                                    {
                                        previousPosition = autoPosition;
                                        previousValue    = key.Value;
                                        continue;
                                    }

                                    // this key is after the clip block, must be last one, process and stop
                                    if (autoPosition > endOffset)
                                    {
                                        newValue = previousValue - key.Value;
                                        newValue = newValue / (double)key.Position;
                                        newValue = newValue * (double)(autoPosition - endOffset);
                                        newValue = key.Value + newValue;
                                        newAuto.Points.Add(new Song.Point()
                                        {
                                            TimeStamp = PositionToSamples(position + endOffset),
                                            Value     = (float)newValue
                                        });
                                        break;
                                    }

                                    // key is inside the clip block

                                    if (keyCount == 0 && autoPosition > startOffset)                   // position not 0 in clip, calculat start point
                                    {
                                        newValue = previousValue - key.Value;
                                        newValue = newValue / (double)key.Position;
                                        newValue = newValue * (double)(autoPosition - startOffset);
                                        newValue = key.Value + newValue;

                                        newAuto.Points.Add(new Song.Point()
                                        {
                                            TimeStamp = PositionToSamples(position),
                                            Value     = (float)newValue
                                        });
                                    }

                                    keyCount++;
                                    previousPosition = autoPosition;
                                    previousValue    = key.Value;
                                    newAuto.Points.Add(new Song.Point()
                                    {
                                        TimeStamp = PositionToSamples(autoPosition + position),
                                        Value     = (float)key.Value
                                    });
                                }
                            }
                        }
                    }
                }
            }

            foreach (var auto in track.Automations)
            {
                SortPoints(auto);
            }

            return(track);
        }
Example #5
0
        // convert insert track to wavesabre song track
        private Song.Track ConvertInsert(Insert insert)
        {
            var track = new Song.Track();

            track.Devices = new List <Song.Device>();

            track.Volume = (float)insert.Volume / 12800;
            string trackName = insert.Name != "" ? insert.Name : project.Inserts.ToList().IndexOf(insert).ToString();

            track.Name = trackName;
            var sabreDevices = new List <FLDevice>();

            // add slot devices with Id
            for (var i = 0; i < insert.Slots.Length; i++)
            {
                var slot = insert.Slots[i];
                if (slot.Plugin != null && slot.Plugin.Name != null)
                {
                    var sabreDevice = PlugToDevice(slot.Plugin);
                    if (sabreDevice == null)
                    {
                        logger.WriteLine("WARNING: {0} slot {1} has unkown plugin {2}",
                                         trackName,
                                         i,
                                         slot.Plugin.Name);
                    }

                    if (slot.State == 0)
                    {
                        logger.WriteLine("WARNING: {0} slot {1} has plugin {2} disabled",
                                         trackName,
                                         i,
                                         slot.Plugin.Name);
                    }
                    else if (slot.Volume != 12800)
                    {
                        logger.WriteLine("WARNING: {0} slot {1} has plugin {2} dry / wet level unsupported, skipping",
                                         trackName,
                                         i,
                                         slot.Plugin.Name);
                    }
                    else
                    {
                        sabreDevices.Add(new FLDevice()
                        {
                            DeviceIndex = i, Device = sabreDevice
                        });
                    }
                }
            }

            foreach (var device in sabreDevices)
            {
                track.Devices.Add((Song.Device)device.Device);
            }

            // automations
            foreach (var flTrack in project.Tracks)
            {
                foreach (var pl in flTrack.Items)
                {
                    if (pl is ChannelPlaylistItem)
                    {
                        var pl2      = (ChannelPlaylistItem)pl;
                        int position = pl2.Position;
                        if (pl2.Channel.Data is AutomationData)
                        {
                            var auto = (AutomationData)pl2.Channel.Data;
                            if (insert.Id == auto.InsertId)
                            {
                                var deviceIndex = sabreDevices.IndexOf(sabreDevices.FirstOrDefault(p => p.DeviceIndex == auto.SlotId));
                                if (deviceIndex < 0)
                                {
                                    logger.WriteLine("WARNING: Insert {0} automation linked to missing slot device", trackName);
                                    continue;
                                }

                                var newAuto = track.Automations.FirstOrDefault(a => a.ParamId == auto.Parameter && a.DeviceIndex == deviceIndex);
                                if (newAuto == null)
                                {
                                    newAuto = new Song.Automation()
                                    {
                                        DeviceIndex = deviceIndex,
                                        ParamId     = auto.Parameter
                                    };
                                    track.Automations.Add(newAuto);
                                }

                                foreach (var key in auto.Keyframes)
                                {
                                    position += key.Position;
                                    newAuto.Points.Add(new Song.Point()
                                    {
                                        TimeStamp = PositionToSamples(position),
                                        Value     = (float)key.Value
                                    });
                                }
                            }
                        }
                    }
                }
            }

            foreach (var auto in track.Automations)
            {
                SortPoints(auto);
            }

            return(track);
        }
Example #6
0
        public Song Process(LiveProject project, ILog logger)
        {
            this.logger = logger;

            var song = new Song();

            song.Tempo      = (int)project.Tempo;
            song.SampleRate = 44100;

            var projectLoopEnd = project.LoopStart + project.LoopLength;

            trackReceives = new Dictionary <LiveProject.Track, List <Receive> >();
            foreach (var projectTrack in project.Tracks)
            {
                trackReceives.Add(projectTrack, new List <Receive>());
            }
            foreach (var projectTrack in project.Tracks)
            {
                foreach (var send in projectTrack.Sends)
                {
                    if (send.IsActive)
                    {
                        trackReceives[send.ReceivingTrack].Add(new Receive(projectTrack, send.ReceivingChannelIndex - 1, send.Volume));
                    }
                }
            }

            project.MasterTrack.Name = project.MasterTrack.Name == "" ? "Master" : project.MasterTrack.Name;

            visitedTracks = new List <LiveProject.Track>();
            orderedTracks = new List <LiveProject.Track>();

            visitTrack(project.MasterTrack);

            var projectTracksToSongTracks = new Dictionary <LiveProject.Track, Song.Track>();
            var songTrackEvents           = new Dictionary <Song.Track, List <Event> >();

            double?minEventTime = null;
            double?maxEventTime = null;

            foreach (var projectTrack in orderedTracks)
            {
                var track = new Song.Track();
                track.Name   = projectTrack.Name;
                track.Volume = (float)projectTrack.Volume;

                foreach (var projectDevice in projectTrack.Devices)
                {
                    Song.Device device = null;

                    Song.DeviceId deviceId;
                    if (Enum.TryParse <Song.DeviceId>(projectDevice.PluginDll.Replace(".dll", "").Replace(".64", ""), out deviceId))
                    {
                        device       = new Song.Device();
                        device.Id    = deviceId;
                        device.Chunk = projectDevice.RawData != null ? (byte[])projectDevice.RawData.Clone() : new byte[0];
                    }
                    if (device == null)
                    {
                        logger.WriteLine("WARNING: Device skipped (unsupported plugin): " + projectDevice.PluginDll);
                    }
                    else if (projectDevice.Bypass)
                    {
                        logger.WriteLine("WARNING: Device skipped (bypass enabled): " + projectDevice.PluginDll);
                    }
                    else
                    {
                        track.Devices.Add(device);

                        foreach (var floatParameter in projectDevice.FloatParameters)
                        {
                            if (floatParameter.Id >= 0)
                            {
                                var automation = new Song.Automation();
                                automation.DeviceIndex = track.Devices.IndexOf(device);
                                automation.ParamId     = floatParameter.Id;
                                foreach (var e in floatParameter.Events)
                                {
                                    if (e.Time >= 0.0)
                                    {
                                        var point = new Song.Point();
                                        point.TimeStamp = secondsToSamples(e.Time, (double)song.Tempo, (double)song.SampleRate);
                                        point.Value     = e.Value;
                                        automation.Points.Add(point);
                                    }
                                }
                                if (automation.Points.Count > 0)
                                {
                                    track.Automations.Add(automation);
                                }
                            }
                        }
                    }
                }

                var events = new List <Event>();
                foreach (var midiClip in projectTrack.MidiClips)
                {
                    if (!midiClip.IsDisabled)
                    {
                        var loopLength = midiClip.LoopEnd - midiClip.LoopStart;
                        for (var currentTime = midiClip.CurrentStart; currentTime < midiClip.CurrentEnd; currentTime += loopLength)
                        {
                            foreach (var keyTrack in midiClip.KeyTracks)
                            {
                                foreach (var note in keyTrack.Notes)
                                {
                                    if (note.IsEnabled)
                                    {
                                        var startTime = note.Time - (currentTime - midiClip.CurrentStart) - midiClip.LoopStartRelative;
                                        while (startTime < 0.0)
                                        {
                                            startTime += loopLength;
                                        }
                                        startTime = currentTime + startTime - midiClip.LoopStart;
                                        var endTime = startTime + note.Duration;

                                        if ((startTime >= midiClip.CurrentStart && startTime < midiClip.CurrentEnd) &&
                                            (!project.IsLoopOn || (
                                                 startTime >= project.LoopStart && startTime < projectLoopEnd)))
                                        {
                                            endTime = Math.Min(endTime, midiClip.CurrentEnd);
                                            if (project.IsLoopOn)
                                            {
                                                endTime = Math.Min(endTime, projectLoopEnd);
                                            }
                                            if (endTime > startTime)
                                            {
                                                var startEvent = new Event();
                                                startEvent.Time     = startTime;
                                                startEvent.Type     = Song.EventType.NoteOn;
                                                startEvent.Note     = (byte)keyTrack.MidiKey;
                                                startEvent.Velocity = (byte)note.Velocity;
                                                events.Add(startEvent);

                                                var endEvent = new Event();
                                                endEvent.Time = endTime;
                                                endEvent.Type = Song.EventType.NoteOff;
                                                endEvent.Note = (byte)keyTrack.MidiKey;
                                                events.Add(endEvent);
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                events.Sort((a, b) =>
                {
                    if (a.Time > b.Time)
                    {
                        return(1);
                    }
                    if (a.Time < b.Time)
                    {
                        return(-1);
                    }
                    if (a.Type == Song.EventType.NoteOn && b.Type == Song.EventType.NoteOff)
                    {
                        return(1);
                    }
                    if (a.Type == Song.EventType.NoteOff && b.Type == Song.EventType.NoteOn)
                    {
                        return(-1);
                    }
                    return(0);
                });
                foreach (var e in events)
                {
                    if (!minEventTime.HasValue || e.Time < minEventTime.Value)
                    {
                        minEventTime = e.Time;
                    }
                    if (!maxEventTime.HasValue || e.Time > maxEventTime.Value)
                    {
                        maxEventTime = e.Time;
                    }
                }

                projectTracksToSongTracks.Add(projectTrack, track);
                songTrackEvents.Add(track, events);
                song.Tracks.Add(track);
            }

            double songStartTime, songEndTime;

            if (project.IsLoopOn)
            {
                songStartTime = project.LoopStart;
                songEndTime   = projectLoopEnd;
            }
            else if (minEventTime.HasValue && maxEventTime.HasValue)
            {
                songStartTime = minEventTime.Value;
                songEndTime   = maxEventTime.Value;
            }
            else
            {
                throw new Exception("Couldn't find song start/end times");
            }
            song.Length = (songEndTime - songStartTime) * 60.0 / (double)song.Tempo;

            foreach (var kvp in songTrackEvents)
            {
                var track  = kvp.Key;
                var events = kvp.Value;

                int lastTimeStamp = 0;
                foreach (var e in events)
                {
                    var songEvent = new Song.Event();
                    var time      = e.Time - songStartTime;
                    int timeStamp = Math.Max(secondsToSamples(time, (double)song.Tempo, (double)song.SampleRate), lastTimeStamp);

                    songEvent.TimeStamp = secondsToSamples(time, (double)song.Tempo, (double)song.SampleRate);
                    songEvent.Type      = e.Type;
                    songEvent.Note      = e.Note;
                    songEvent.Velocity  = e.Velocity;
                    track.Events.Add(songEvent);
                    lastTimeStamp = timeStamp;
                }
            }

            // TODO: Clip all of this instead of just offsetting
            // adjust automation start times based on song start
            foreach (var track in song.Tracks)
            {
                foreach (var automation in track.Automations)
                {
                    foreach (var point in automation.Points)
                    {
                        point.TimeStamp -= secondsToSamples(songStartTime, (double)song.Tempo, (double)song.SampleRate);
                    }
                }
            }

            foreach (var kvp in projectTracksToSongTracks)
            {
                foreach (var projectReceive in trackReceives[kvp.Key])
                {
                    if (projectTracksToSongTracks.ContainsKey(projectReceive.SendingTrack))
                    {
                        var receive = new Song.Receive();
                        receive.SendingTrackIndex     = song.Tracks.IndexOf(projectTracksToSongTracks[projectReceive.SendingTrack]);
                        receive.ReceivingChannelIndex = projectReceive.ReceivingChannelIndex;
                        receive.Volume = (float)projectReceive.Volume;
                        kvp.Value.Receives.Add(receive);
                    }
                }
            }

            return(song);
        }
Example #7
0
        private Song.Track ConvertTrack(int trackId)
        {
            Song.Track songTrack = null;

            var trackObject = project.Tracks.Items[trackId];

            string trackName = GetProp("Name", trackObject).ToString();

            object[] trackDevices = ((TrackFilterDeviceChain)GetProp("FilterDevices", trackObject)).Devices.Items;

            var songPatterns = new List <Pattern>();

            var allLines = new List <RsnPatternLineNode>();

            // copy all patterns out to full song
            foreach (var seq in project.PatternSequence.SequenceEntries.SequenceEntry)
            {
                songPatterns.Add(project.PatternPool.Patterns.Pattern[seq.Pattern]);
            }

            // loop each pattern to create complete picture of notes and automations...
            int lineIndex = 0;

            foreach (var pattern in songPatterns)
            {
                var track = pattern.Tracks.Items[trackId];

                // grab current lines on pattern
                var currentLines = (PatternLineNode[])GetProp("Lines", track);

                if (currentLines != null)
                {
                    foreach (PatternLineNode line in currentLines)
                    {
                        var newLine = new RsnPatternLineNode();
                        newLine.NoteColumns   = line.NoteColumns;
                        newLine.EffectColumns = line.EffectColumns;
                        newLine.OrigianlIndex = line.index;     // keep original index for groooooove
                        newLine.index         = line.index += lineIndex;
                        newLine.type          = line.type;
                        allLines.Add(newLine);
                    }
                }

                lineIndex += pattern.NumberOfLines;
            }

            // convert tracker notes to midi events
            var allNotes = allLines.SelectMany(n => n.NoteColumns.NoteColumn);
            var allIns   = allNotes.Select(n => n.Instrument).Where(i => i != null && i != "..").Distinct().ToList();

            if (allIns.Count() > 1)
            {
                logger.WriteLine(string.Format(
                                     "WARNING: Track {0} has {1} instruments used, defaulting to instrument {2}",
                                     trackName, allIns.Count(), allIns.First()));
            }

            // automations....
            int autoIndex = 0;
            var allAuto   = new List <Song.Automation>();
            var autoGroup = new List <PatternTrackAutomation>();

            foreach (var pattern in songPatterns)
            {
                var track       = pattern.Tracks.Items[trackId];
                var automations = (PatternTrackAutomation)GetProp("Automations", track);
                if (automations != null)
                {
                    autoGroup.Add(automations);
                }
            }

            // generate distinct list of device id's and params used on this track
            var pita = new List <PatternTrackEnvelope>();

            if (autoGroup.Count > 0)
            {
                var allEnv = autoGroup.Select(g => g.Envelopes);
                if (allEnv != null)
                {
                    foreach (var temp in allEnv)
                    {
                        pita.AddRange(temp.Envelope);
                    }
                }
            }
            var distinct = pita.Select(a => new { DeviceIndex = a.DeviceIndex, ParamId = a.ParameterIndex }).Distinct().ToList();

            //  now populate each distinct device and param
            foreach (var thisAuto in distinct)
            {
                var thisAutoList = new List <RnsAuto>();
                int deviceIndex  = thisAuto.DeviceIndex;
                int paramId      = thisAuto.ParamId;

                autoIndex = 0;
                foreach (var pattern in songPatterns)
                {
                    var track       = pattern.Tracks.Items[trackId];
                    var automations = (PatternTrackAutomation)GetProp("Automations", track);
                    if (automations == null)
                    {
                        // pattern has no autos, add start / end from last auto value
                        if (thisAutoList.Count > 0)
                        {
                            var newAuto = new RnsAuto();
                            newAuto.TimePoint  = 0; // <--- start of pattern
                            newAuto.Value      = thisAutoList.Last().Value;
                            newAuto.AutoLength = pattern.NumberOfLines * 256;
                            newAuto.Offset     = autoIndex;
                            thisAutoList.Add(newAuto);

                            newAuto            = new RnsAuto();
                            newAuto.TimePoint  = (pattern.NumberOfLines * 256) - 1; // <--- end of pattern
                            newAuto.Value      = thisAutoList.Last().Value;
                            newAuto.AutoLength = pattern.NumberOfLines * 256;
                            newAuto.Offset     = autoIndex;
                            thisAutoList.Add(newAuto);
                        }
                    }
                    else
                    {
                        var myAutos = automations.Envelopes.Envelope.Where(a => a.DeviceIndex == deviceIndex && a.ParameterIndex == paramId);

                        if (myAutos.ToList().Count == 0)
                        {
                            // this pattern has no autos, add start / end from last auto value
                            if (thisAutoList.Count > 0)
                            {
                                var newAuto = new RnsAuto();
                                newAuto.TimePoint  = 0; // <--- start of pattern
                                newAuto.Value      = thisAutoList.Last().Value;
                                newAuto.AutoLength = pattern.NumberOfLines * 256;
                                newAuto.Offset     = autoIndex;
                                thisAutoList.Add(newAuto);

                                newAuto            = new RnsAuto();
                                newAuto.TimePoint  = (pattern.NumberOfLines * 256) - 1; // <--- end of pattern
                                newAuto.Value      = thisAutoList.Last().Value;
                                newAuto.AutoLength = pattern.NumberOfLines * 256;
                                newAuto.Offset     = autoIndex;
                                thisAutoList.Add(newAuto);
                            }
                        }
                        else
                        {
                            // this pattern has automations..  so process
                            foreach (var a in myAutos)
                            {
                                var autoTemp = new List <RnsAuto>();
                                foreach (string point in a.Envelope.Points) // add all current points
                                {
                                    var timePoint = Convert.ToInt32(point.Split(',')[0]);
                                    var value     = (float)Convert.ToDouble(point.Split(',')[1]);

                                    var newAuto = new RnsAuto();
                                    newAuto.TimePoint  = timePoint;
                                    newAuto.Value      = value;
                                    newAuto.AutoLength = a.Envelope.Length;
                                    newAuto.Offset     = autoIndex;
                                    autoTemp.Add(newAuto);
                                }

                                // create auto point for the start of this pattern
                                if (autoTemp.First().TimePoint != 0)
                                {
                                    var newAuto = new RnsAuto();
                                    newAuto.TimePoint  = 0; // <--- start of pattern
                                    newAuto.Value      = autoTemp.First().Value;
                                    newAuto.AutoLength = autoTemp.First().AutoLength;
                                    newAuto.Offset     = autoIndex;
                                    autoTemp.Insert(0, newAuto);
                                }

                                // create auto point for the end of this pattern
                                if (autoTemp.Last().TimePoint != autoTemp.Last().AutoLength - 1)
                                {
                                    var newAuto = new RnsAuto();
                                    newAuto.TimePoint  = autoTemp.Last().AutoLength - 1; // <--- end of pattern
                                    newAuto.Value      = autoTemp.Last().Value;
                                    newAuto.AutoLength = autoTemp.Last().AutoLength;
                                    newAuto.Offset     = autoIndex;
                                    autoTemp.Add(newAuto);
                                }

                                thisAutoList.AddRange(autoTemp);
                            }
                        }
                    }

                    // next position
                    autoIndex += pattern.NumberOfLines * 256;
                }

                // double check we have starting point for this auto in case of a blank pattern
                if (thisAutoList.First().TimePoint != 0)
                {
                    var newAuto = new RnsAuto();
                    newAuto.TimePoint  = 0;                     // <--- start of pattern
                    newAuto.Value      = thisAutoList.First().Value;
                    newAuto.AutoLength = thisAutoList.First().AutoLength;
                    newAuto.Offset     = autoIndex;
                    thisAutoList.ToList().Insert(0, newAuto);
                }

                // all automation points for this device / param collated, now convert to our automations
                thisAutoList.ForEach(a => a.TimePoint += a.Offset);

                var finalAuto = new Song.Automation();
                finalAuto.DeviceIndex = deviceIndex;
                finalAuto.ParamId     = paramId - 1; // renoise param index is out by one due to automation on active flag

                foreach (var p in thisAutoList)
                {
                    var point = new Song.Point();
                    point.Value     = p.Value;
                    point.TimeStamp = SecondsToSamples(p.TimePoint / 256.00 * (double)secondsPerIndex, sampleRate);
                    finalAuto.Points.Add(point);
                }

                allAuto.Add(finalAuto);
            }

            RnsIns instrument   = null;
            int    instrumentId = -1;

            if (allIns.Count > 0)
            {
                instrumentId = Convert.ToInt32(allIns.First());
                instrument   = instruments.Where(i => i.InstrumentId == instrumentId).First();
            }

            songTrack        = new Song.Track();
            songTrack.Name   = trackName;
            songTrack.Volume = GetTrackVolume(trackDevices[0]);
            var devices = new List <RnsDevice>();

            if (instrument != null)
            {
                devices.Add(new RnsDevice()
                {
                    DeviceIndex = 0, Device = instrument.Device
                });
            }
            devices.AddRange(GetDevices(trackDevices, trackName, instrumentId));  // add track devices

            foreach (var device in devices)
            {
                if (device.Device is Song.Device)
                {
                    songTrack.Devices.Add((Song.Device)device.Device);
                }
            }

            // remap automations to correct devices
            foreach (var auto in allAuto)
            {
                if (devices.Find(a => a.DeviceIndex == auto.DeviceIndex).Device is InstrumentAutomationDevice)
                {
                    auto.DeviceIndex = 0;   // 0 is always instrument
                }
                else
                {
                    auto.DeviceIndex = devices.IndexOf(
                        devices.Find(a => a.DeviceIndex == auto.DeviceIndex && a.Device is Song.Device));
                }
            }

            if (allAuto.RemoveAll(auto => auto.DeviceIndex == -1) > 0)
            {
                logger.WriteLine(string.Format("WARNING: Some automations skipped on track {0}", trackName));
            }

            songTrack.Automations.AddRange(allAuto);
            songTrack.Events = NotesToEvents(allLines);
            return(songTrack);
        }