private SequencerTrack CreateTrack(Song.Track track) { var rnsTrack = (SequencerTrack)Utils.Deserializer(trackTemplate, typeof(SequencerTrack)); rnsTrack.Name = track.Name; var fxList = new List <object>(); fxList.Add(rnsTrack.FilterDevices.Devices.Items[0]); for (var i = 1; i < track.Devices.Count; i++) { fxList.Add(CreateDevice(track.Devices[i])); } rnsTrack.FilterDevices.Devices.Items = fxList.ToArray(); return(rnsTrack); }
private RenoiseInstrument CreateInstrument(Song.Track track, int index) { var ins = (RenoiseInstrument)Utils.Deserializer(instrumentTemplate, typeof(RenoiseInstrument)); var deviceName = track.Devices[0].Id.ToString(); ins.Name = track.Name; var plug = ins.PluginGenerator.PluginDevice; plug.Parameters.Parameter = null; plug.ParameterChunk = Convert.ToBase64String(track.Devices[0].Chunk); plug.PluginIdentifier = deviceName; plug.PluginDisplayName = string.Format("VST: Logicoma: {0}", deviceName); plug.PluginShortDisplayName = deviceName; ins.PluginGenerator.OutputRoutings.OutputRouting[0].AssignedTrack = index; ins.PluginGenerator.OutputRoutings.OutputRouting[0].AutoAssign = false; return(ins); }
public Song.Track CreateMaster() { var master = new Song.Track(); master.Name = "Master"; master.Volume = project.MasterVolume.Volume; foreach (var f in project.MasterEffectsChain) { Song.Device device = null; Song.DeviceId deviceId; if (Enum.TryParse <Song.DeviceId>(f.Vst.VstFile.Replace(".dll", "").Replace(".64", ""), out deviceId)) { device = new Song.Device(); device.Id = deviceId; device.Chunk = f.Vst.VstChunkData; } if (device == null) { logger.WriteLine("WARNING: Device skipped (unsupported plugin): " + f.Vst.VstFile); } /*else if (f.Vst.Bypass) * { * logger.WriteLine("WARNING: Device skipped (bypass enabled): " + projectDevice.PluginDll); * }*/ master.Devices.Add(device); var deviceIndex = master.Devices.IndexOf(device); foreach (var a in f.Automations) { master.Automations.Add(ConvertAutomation(a, deviceIndex, "Master")); } } return(master); }
// 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); }
// 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); }
public Song.Track CreateTrack(ReaperTrack reaperTrack) { var track = new Song.Track(); track.Name = reaperTrack.TrackName; track.Volume = reaperTrack.VolumePanning.Volume; if (reaperTrack.VolumePanning.Pan != 0) { logger.WriteLine("WARNING: Pan value {0} on track {1} unsupported", reaperTrack.VolumePanning.Pan, reaperTrack.TrackName); } // add receives foreach (var r in reaperTrack.Receives) { if (r.ReceiveFader != ReaperFader.PostFader) { logger.WriteLine("WARNING: Receive fader type of {0} on track {1} unsupported", r.ReceiveFader, reaperTrack.TrackName); } var receive = new Song.Receive(); receive.SendingTrackIndex = r.ReceiveTrackId + 1; // master always zero, so tracks are out by one receive.ReceivingChannelIndex = r.DestinationChannelIndex; receive.Volume = r.Volume; track.Receives.Add(receive); } foreach (var f in reaperTrack.EffectsChain) { if (f.Wet != 0) { logger.WriteLine("WARNING: Wet value for effect {0} on track {1} unsupported", f.Vst.VstFile, reaperTrack.TrackName); } Song.Device device = null; Song.DeviceId deviceId; if (Enum.TryParse <Song.DeviceId>(f.Vst.VstFile.Replace(".dll", "").Replace(".64", ""), out deviceId)) { device = new Song.Device(); device.Id = deviceId; device.Chunk = f.Vst.VstChunkData; } if (device == null) { logger.WriteLine("WARNING: Device skipped (unsupported plugin): " + f.Vst.VstFile); } /*else if (f.Vst.Bypass) // TODO: Parse out Bypass * { * logger.WriteLine("WARNING: Device skipped (bypass enabled): " + projectDevice.PluginDll); * }*/ track.Devices.Add(device); var deviceIndex = track.Devices.IndexOf(device); foreach (var a in f.Automations) { track.Automations.Add(ConvertAutomation(a, deviceIndex, reaperTrack.TrackName)); } track.Events = ConvertMidi(reaperTrack.MediaItems); } return(track); }
public ReaperTrackLink(Song.Track track) { Track = track; Receives = new List <ReaperTrackLinkReceive>(); }
public TrackDiff(Song.Track a, Song.Track b, int index) { this.a = a; this.b = b; this.index = index; volume = a.Volume != b.Volume; trackName = a.Name != b.Name; if (a.Receives.Count != b.Receives.Count) { receiveCounts = true; } else { for (int i = 0; i < a.Receives.Count; i++) { var aReceive = a.Receives[i]; var bReceive = b.Receives[i]; var receiveDiff = new ReceiveDiff(aReceive, bReceive, i); if (!receiveDiff.IsEmpty) { receiveDiffs.Add(receiveDiff); } } } if (a.Devices.Count != b.Devices.Count) { deviceCounts = true; } else { for (int i = 0; i < a.Devices.Count; i++) { var aDevice = a.Devices[i]; var bDevice = b.Devices[i]; var deviceDiff = new DeviceDiff(aDevice, bDevice, i); if (!deviceDiff.IsEmpty) { deviceDiffs.Add(deviceDiff); } } } if (a.DeltaCodedEvents.Count != b.DeltaCodedEvents.Count) { eventCounts = true; } else { for (int i = 0; i < a.DeltaCodedEvents.Count; i++) { var aEvent = a.DeltaCodedEvents[i]; var bEvent = b.DeltaCodedEvents[i]; var eventDiff = new EventDiff(aEvent, bEvent, i); if (!eventDiff.IsEmpty) { eventDiffs.Add(eventDiff); } } } if (a.Automations.Count != b.Automations.Count) { automationCounts = true; } else { for (int i = 0; i < a.Automations.Count; i++) { var aAutomation = a.Automations[i]; var bAutomation = b.Automations[i]; var automationDiff = new AutomationDiff(aAutomation, bAutomation, i); if (!automationDiff.IsEmpty) { automationDiffs.Add(automationDiff); } } } }
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); }
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); }