예제 #1
0
        public DrawObject(DrawObjectType type,
                          VsqFileEx vsq,
                          Rectangle rect,
                          string text_,
                          int accent_,
                          int decay,
                          int velocity,
                          int internal_id,
                          int vibrato_delay,
                          bool overwrapped,
                          bool symbol_protected,
                          VibratoBPList vib_rate,
                          VibratoBPList vib_depth,
                          int vib_start_rate,
                          int vib_start_depth,
                          int note_,
                          UstEnvelope ust_envelope,
                          int length,
                          int clock,
                          bool is_valid_for_utau,
                          bool is_valid_for_straight,
                          int vib_delay,
                          int intensity)
        {
            this.mType           = type;
            mRectangleInPixel    = rect;
            mText                = text_;
            mAccent              = accent_;
            mDecay               = decay;
            mVelocity            = velocity;
            mInternalID          = internal_id;
            mVibratoDelayInPixel = vibrato_delay;
            mIsOverlapped        = overwrapped;
            mIsSymbolProtected   = symbol_protected;
            mIntensity           = intensity;

            mNote                    = note_;
            mUstEnvelope             = ust_envelope;
            this.mLength             = length;
            this.mClock              = clock;
            this.mIsValidForUtau     = is_valid_for_utau;
            this.mIsValidForStraight = is_valid_for_straight;
            this.mVibDelay           = vib_delay;

            int viblength = length - vib_delay;

            if (viblength > 0 && vib_rate != null && vib_depth != null)
            {
                VibratoPointIteratorByClock itr =
                    new VibratoPointIteratorByClock(vsq.TempoTable,
                                                    vib_rate, vib_start_rate,
                                                    vib_depth, vib_start_depth,
                                                    clock + vib_delay, viblength);
                mVibratoPit = new float[viblength];
                for (int i = 0; i < viblength; i++)
                {
                    if (!itr.hasNext())
                    {
                        break;
                    }
                    double v = itr.next();
                    mVibratoPit[i] = (float)v;
                }
            }
        }
        /// <summary>
        /// 音符に付随するピッチベンドの情報を、PIT・PBS カーブに反映する
        /// </summary>
        /// <param name="item">音符</param>
        /// <param name="pitchBend">PIT カーブ</param>
        /// <param name="pitchBendSensitivity">PBS カーブ</param>
        /// <param name="tempoTable">テンポ情報</param>
        protected void reflectNoteEventPitch(VsqEvent item, VsqBPList pitchBend, VsqBPList pitchBendSensitivity, TempoVector tempoTable)
        {
            if (item.ID.type != VsqIDType.Anote)
            {
                return;
            }

            // AquesTone2 では、note on と同 clock にピッチベンドイベントを送らないと音程が反映されないので、必ずピッチイベントが送られるようにする
            pitchBend.add(item.Clock, pitchBend.getValue(item.Clock));

            if (item.ID.VibratoHandle == null)
            {
                return;
            }

            int startClock    = item.Clock + item.ID.VibratoDelay;
            int vibratoLength = item.ID.Length - item.ID.VibratoDelay;

            var iterator = new VibratoPointIteratorByClock(tempoTable,
                                                           item.ID.VibratoHandle.RateBP, item.ID.VibratoHandle.StartRate,
                                                           item.ID.VibratoHandle.DepthBP, item.ID.VibratoHandle.StartDepth,
                                                           startClock, vibratoLength);
            var pitContext = new ByRef <int>(0);
            var pbsContext = new ByRef <int>(0);

            int pitAtEnd  = pitchBend.getValue(startClock + vibratoLength);
            int pbsAtEnd  = pitchBendSensitivity.getValue(startClock + vibratoLength);
            var pitBackup = (VsqBPList)pitchBend.Clone();
            var pbsBackup = (VsqBPList)pitchBendSensitivity.Clone();

            bool   resetPBS = false;
            double maxNetPitchBendInCent = 0.0;

            for (int clock = startClock; clock < startClock + vibratoLength && iterator.hasNext(); ++clock)
            {
                double       vibratoPitchBendInCent = iterator.next() * 100.0;
                int          pit                = pitchBend.getValue(clock, pitContext);
                int          pbs                = pitchBendSensitivity.getValue(clock, pbsContext);
                const double pow2_13            = 8192;
                double       netPitchBendInCent = (pbs * pit / pow2_13) * 100.0 + vibratoPitchBendInCent;
                maxNetPitchBendInCent = Math.Max(maxNetPitchBendInCent, Math.Abs(netPitchBendInCent));
                int draftPitchBend = (int)Math.Round((netPitchBendInCent / 100.0) * pow2_13 / pbs);

                if (draftPitchBend < pitchBend.Minimum || pitchBend.Maximum < draftPitchBend)
                {
                    // pbs を変更せずにビブラートによるピッチベンドを反映しようとすると、
                    // pit が範囲を超えてしまう。
                    resetPBS = true;
                }
                else
                {
                    if (draftPitchBend != pit)
                    {
                        pitchBend.add(clock, draftPitchBend);
                    }
                }
            }

            if (!resetPBS)
            {
                return;
            }

            pitchBend.Data = pitBackup.Data;

            // ピッチベンドの最大値を実現するのに必要なPBS
            int requiredPitchbendSensitivity  = (int)Math.Ceiling(maxNetPitchBendInCent / 100.0);
            int pseudoMaxPitchbendSensitivity = 12; // AquesTone2 は最大 12 半音までベンドできる。

            if (requiredPitchbendSensitivity < pitchBendSensitivity.Minimum)
            {
                requiredPitchbendSensitivity = pitchBendSensitivity.Minimum;
            }
            if (pseudoMaxPitchbendSensitivity < requiredPitchbendSensitivity)
            {
                requiredPitchbendSensitivity = pseudoMaxPitchbendSensitivity;
            }

            {
                int i = 0;
                while (i < pitchBend.size())
                {
                    var clock = pitchBend.getKeyClock(i);
                    if (startClock <= clock && clock < startClock + vibratoLength)
                    {
                        pitchBend.removeElementAt(i);
                    }
                    else
                    {
                        ++i;
                    }
                }
            }
            {
                int i = 0;
                while (i < pitchBendSensitivity.size())
                {
                    var clock = pitchBendSensitivity.getKeyClock(i);
                    if (startClock <= clock && clock < startClock + vibratoLength)
                    {
                        pitchBendSensitivity.removeElementAt(i);
                    }
                    else
                    {
                        ++i;
                    }
                }
            }
            if (pitchBendSensitivity.getValue(startClock) != requiredPitchbendSensitivity)
            {
                pitchBendSensitivity.add(startClock, requiredPitchbendSensitivity);
            }
            pitchBend.add(startClock + vibratoLength, pitAtEnd);
            pitchBendSensitivity.add(startClock + vibratoLength, pbsAtEnd);

            iterator.rewind();
            pitContext.value = 0;
            pbsContext.value = 0;
            int lastPitchBend = pitchBend.getValue(startClock);

            for (int clock = startClock; clock < startClock + vibratoLength && iterator.hasNext(); ++clock)
            {
                double vibratoPitchBendInCent = iterator.next() * 100.0;
                int    pit = pitBackup.getValue(clock, pitContext);
                int    pbs = pbsBackup.getValue(clock, pbsContext);

                const double pow2_13            = 8192;
                double       netPitchBendInCent = (pbs * pit / pow2_13) * 100.0 + vibratoPitchBendInCent;
                maxNetPitchBendInCent = Math.Max(maxNetPitchBendInCent, Math.Abs(netPitchBendInCent));
                int draftPitchBend = (int)Math.Round((netPitchBendInCent / 100.0) * pow2_13 / requiredPitchbendSensitivity);
                if (draftPitchBend < pitchBend.Minimum)
                {
                    draftPitchBend = pitchBend.Minimum;
                }
                if (pitchBend.Maximum < draftPitchBend)
                {
                    draftPitchBend = pitchBend.Maximum;
                }
                if (draftPitchBend != lastPitchBend)
                {
                    pitchBend.add(clock, draftPitchBend);
                    lastPitchBend = draftPitchBend;
                }
            }
        }