Page 1 of 1

Re: "Apply to all slots" for DPCM

Posted: Fri Aug 14, 2015 8:42 am
by HertzDevil
Subject: "Apply to all slots" for DPCM

MiniMacro wrote:I was thinking that making, say, DPCM triangle, sawtooth, or bass etc. could be made easier with an option to apply a sample to all slots of an octave.
Would it be possible to add this into FT?

The way I envision this feature is that a single sample's settings are applied to different notes that match the DPCM pitch tuning as closely as possible. It is the user's responsibility to provide samples that can fill an octave, but nonetheless the button can create assignments from each individual sample quickly. I have attached a test build below, and the source code changes for 0.4.6 will be included here:
  • recourse.h:

    Code: Select all

    #define IDC_FILL_EDIT                   (any unique identifier)
    #define IDC_FILL_SPIN                   (as above)
    #define IDC_FILL_DPCM                   (as above)
  • FamiTracker.rc:

    Code: Select all

    IDD_INSTRUMENT_DPCM DIALOGEX 0, 0, 372, 174
    // ...
        EDITTEXT        IDC_DELTA_COUNTER,138,97,42,14,ES_AUTOHSCROLL
        CONTROL         "",IDC_DELTA_SPIN,"msctls_updown32",UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,170,97,11,14
        PUSHBUTTON      "Fill",IDC_FILL_DPCM,138,114,42,14
        EDITTEXT        IDC_FILL_EDIT,138,130,42,14,ES_AUTOHSCROLL | ES_NUMBER
        CONTROL         "",IDC_FILL_SPIN,"msctls_updown32",UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,170,130,10,14
        PUSHBUTTON      "",IDC_ADD,138,148,20,14,BS_ICON
        PUSHBUTTON      "",IDC_REMOVE,160,148,20,14,BS_ICON
        GROUPBOX        "Loaded samples",IDC_STATIC,192,7,173,160
    // ...
  • InstrumentEditorDPCM.h:

    Code: Select all

    // ...
    protected:
       static const char *KEY_NAMES[];
       static const int PITCH_NOTE_OFFSET[]; // for calculating new notes

    Code: Select all

       // ...
       afx_msg void OnDeltaposDeltaSpin(NMHDR *pNMHDR, LRESULT *pResult);
       afx_msg void OnDeltaposFillSpin(NMHDR *pNMHDR, LRESULT *pResult); // limit for the assignment fill
       afx_msg void OnBnClickedFillDpcm(); // the button itself
    };
  • InstrumentEditorDPCM.cpp:

    Code: Select all

    const int CInstrumentEditorDPCM::PITCH_NOTE_OFFSET[] = {0, 2, 4, 5, 7, 9, 11, 12, 14, 17, 19, 21, 24, 28, 31, 36};

    Code: Select all

    BOOL CInstrumentEditorDPCM::OnInitDialog()
    {
       // ...
       CSpinButtonCtrl *pSpin = (CSpinButtonCtrl*)GetDlgItem(IDC_DELTA_SPIN);
       // ...
       pSpin = (CSpinButtonCtrl*)GetDlgItem(IDC_FILL_SPIN);   
       pSpin->SetRange(0, 15);
       pSpin->SetPos(15);
       SetDlgItemText(IDC_FILL_EDIT, _T("15"));

    Code: Select all

    void CInstrumentEditorDPCM::OnDeltaposFillSpin(NMHDR *pNMHDR, LRESULT *pResult)
    {
       LPNMUPDOWN pNMUpDown = reinterpret_cast<LPNMUPDOWN>(pNMHDR);
       CEdit *pDeltaValue = static_cast<CEdit*>(GetDlgItem(IDC_FILL_EDIT));
       
       int Value = pNMUpDown->iPos + pNMUpDown->iDelta;
       if (Value < 0)
          Value = 0;
       if (Value > 15)
          Value = 15;
       SetDlgItemInt(IDC_FILL_EDIT, Value, FALSE);

       *pResult = 0;
    }

    Code: Select all

    void CInstrumentEditorDPCM::OnBnClickedFillDpcm()
    {
       BOOL Trans;
       int Value = GetDlgItemInt(IDC_FILL_EDIT, &Trans, FALSE);
       CListCtrl *pTableListCtrl = static_cast<CListCtrl*>(GetDlgItem(IDC_TABLE));
       m_iSelectedKey = pTableListCtrl->GetSelectionMark();
       if (Trans == TRUE && Value >= 0 && Value <= 15 && m_iSelectedKey != -1) {
          char ID = m_pInstrument->GetSample(m_iOctave, m_iSelectedKey);
          char Pitch = static_cast<CComboBox*>(GetDlgItem(IDC_PITCH))->GetCurSel();
          bool Loop = m_pInstrument->GetSampleLoop(m_iOctave, m_iSelectedKey);
          char Delta = m_pInstrument->GetSampleDeltaValue(m_iOctave, m_iSelectedKey);
          // char Offset = m_pInstrument->GetSampleLoopOffset(m_iOctave, m_iSelectedKey);
          const int lo = std::min(static_cast<char>(Value), Pitch);
          const int hi = std::max(static_cast<char>(Value), Pitch);
          for (int i = lo; i <= hi; i++) {
             int Note = MIDI_NOTE(m_iOctave, m_iSelectedKey + 1) + PITCH_NOTE_OFFSET[i] - PITCH_NOTE_OFFSET[Pitch];
             if (Note >= 0 && Note < NOTE_COUNT) {
                const int NewOct = GET_OCTAVE(Note);
                const int NewNote = GET_NOTE(Note) - 1;
                m_pInstrument->SetSample(NewOct, NewNote, ID);
                m_pInstrument->SetSamplePitch(NewOct, NewNote, i);
                m_pInstrument->SetSampleLoop(NewOct, NewNote, Loop);
                m_pInstrument->SetSampleDeltaValue(NewOct, NewNote, Delta);
                // m_pInstrument->SetSampleLoopOffset(NewOct, NewNote, Offset);
             }
          }
          BuildKeyList();
       }
       else
          MessageBeep(MB_ICONEXCLAMATION);
    }
The Fill button comes with a limit value. When clicked, DPCM assignments with the pitch between the current pitch value and the limit value (inclusive) will be automatically created. For example, this:
Image
creates these assignments for octave 0-2: (the one for pitch 11 is not shown, but this feature will look better in the 0.5.0 betas because the assignment editor is no longer paged by the octave value)
ImageImageImage
These assignments have the same sample, loop setting, and delta value as the currently selected one. The Fill button also works in the case the limit value is lower than the current pitch value (assignments are created for higher notes), or no DPCM sample is assigned to the currently selected note (assignments are removed).

Re: Re: "Apply to all slots" for DPCM

Posted: Fri Aug 14, 2015 12:20 pm
by BioMechanicalDude
Wow! This is great! People on that thread got into such huge arguments about such a simple feature, it's crazy. And despite all that, you just added it in, even though it's more of a "proof of concept" kind of thing, which is still awesome. I personally think it's very useful and should be added. It still requires some knowledge of how samples are pitched(unless you want to fill up the DPCM memory with a bunch of useless samples), but with it, I don't have go find a look up table and then calculate how a note will change with a different pitch. Let's hope jsr sees this and incorporates this new feature into the new version.

Re: Re: "Apply to all slots" for DPCM

Posted: Sat Aug 15, 2015 12:08 am
by MiniMacro
Thank you, Hertz.
Thank you so much.