Пример #1
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);
        }
Пример #2
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);
        }