// ╔════════════════════════════════╤══════════════════════════════════════╗
// ║ Библиотека методов мод плейера │ Last Edition: Вт 15-10-2002 19:33:51 ║
// ╚════════════════════════════════╧══════════════════════════════════════╝



// Методы управления мод плейером ///////////////////////////////////////////



// Переход на предыдущий паттерн по порядку паттернов ///////////////////////
:void static fastcall MODMODULE::OrderEffects()
{
 $push EBP

  // Проходимся по всем каналам
  for (ECX=0,EBP=#ModVoice; ECX<ModModule.NumVoices; ECX++,EBP+=sizeof(MODVOICE)){
      $push ECX

       switch(EBP.MODVOICE.EffectNumber){
        CASE Pan: // Установка баланса на канале
         // Обрабатываем эффект
         ModModule.Pan(EBP.MODVOICE.EffectParametr);
         break;

        CASE SampleOffset: // Установка смещения в семпле
         // Устанавливаем новое смещение в семпле
         ModModule.SampleOffset(EBP.MODVOICE.EffectParametr);
         break;

        CASE PositionJump: // Безусловный переход в списке паттернов
         // Применяем эффект
         ModModule.PositionJump(EBP.MODVOICE.EffectParametr);
         break;

        CASE SetVolume: // Установка громкости канала
         // Устанавливаем громкость на канале
         ModModule.SetVolume(EBP.MODVOICE.EffectParametr);
         break;

        CASE PatternBreak: // Прервать паттерн
         // Применяем эффект
         ModModule.PatternBreak(EBP.MODVOICE.EffectParametr);
         break;

        CASE ExtendedEffects:  // Расширенные эффекты
         // BL - Y
         BL=EBP.MODVOICE.EffectParametr&0Fh;
         // Выбираем расширенный эффект
         SWITCH(EBP.MODVOICE.EffectParametr>>4){
          CASE FinePortamentoUp: // Повышение высоты ноты на Y периодов
           // Обрабатываем эффект
           ModModule.FinePortamentoUp(BL);
           BREAK;

          CASE FinePortamentoDown: // Понижение высоты ноты на Y периодов
           // Обрабатываем эффект
           ModModule.FinePortamentoDown(BL);
           BREAK;

          CASE VibratoWaveForm: // Установка формы волны для эффекта Vibrato
           // Устанавливаем нужную звуковую волну для эффекта Vibrato
           ModModule.VibratoWaveForm(BL);
           BREAK;

          CASE PatternLoop: // Зацикливание
           // Обрабатываем эффект
           ModModule.PatternLoop(BL);
           BREAK;

          CASE TremoloWaveForm: // Установка формы волны для эффекта Tremolo
           // Устанавливаем нужную звуковую волну для эффекта Tremolo
           ModModule.TremoloWaveForm(BL);
           BREAK;

          CASE _16PositionPan: // Установка баланса вывода звука на канале
           // Устанавливаем баланс на канале в диаппазоне [0..0Fh]
           ModModule._16PositionPan(BL);
           BREAK;

          CASE FineVolumeSlideUp: // Тонкое повышение громкости
           // Обрабатываем эффект
           ModModule.FineVolumeSlideUp(BL);
           BREAK;

          CASE FineVolumeSlideDown: // Тонкое понижение громкости
           // Обрабатываем эффект
           ModModule.FineVolumeSlideDown(BL);
           BREAK;

          CASE PatternDelay: // Задержка строки
           // Устанавливаем множитель фреймов в строке
           ModModule.PatternDelay(BL);
           BREAK;
         }
         BREAK;

        case SetSpeed: // Установка скорости
         // Это скорость ?
         IF (EBP.MODVOICE.EffectParametr<20h){
            // Устанавливаем скорость воспроизведения (Speed)
            ModModule.SetSpeed(EBP.MODVOICE.EffectParametr);
         }ELSE{
            // Устанавливаем количество ударов в минуту (BPM)
            ModModule.SetBPM(EBP.MODVOICE.EffectParametr);
         }
         break;

       }

      $pop ECX
  }

 $pop EBP
}



// Обработка эффектов, влияющих на воспроизведение семпла ///////////////////
:void static fastcall MODMODULE::SampleEffects()
{
 $push EBP

  // Проходимся по всем каналам
  for (ECX=0,EBP=#ModVoice; ECX<ModModule.NumVoices; ECX++,EBP+=sizeof(MODVOICE)){
      $push ECX

       switch(EBP.MODVOICE.EffectNumber){
        CASE Arpeggio: // Эффект Arpeggio
         // Обрабатываем эффект
         ModModule.Arpeggio(EBP.MODVOICE.EffectParametr);
         break;

        CASE PortamentoUp: // Повышение высоты ноты
         // Повышаем высоту ноты
         ModModule.PortamentoUp(EBP.MODVOICE.EffectParametr);
         break;

        CASE PortamentoDown: // Понижение высоты ноты
         // Понижаем высоту ноты
         ModModule.PortamentoDown(EBP.MODVOICE.EffectParametr);
         BREAK;

        CASE TonePortamento: // Портаменто к ноте
         // Делаем портаменто к ноте
         ModModule.TonePortamento(EBP.MODVOICE.EffectParametr);
         BREAK;

        CASE Vibrato: // Эффект Vibrato
         // Делаем эффект Vibrato
         ModModule.Vibrato(EBP.MODVOICE.EffectParametr);
         BREAK;

        CASE TonePortamentoPlusVolumeSlide: // Портаменто к ноте + скольжение громкости
         // Делаем портаменто к ноте и скольжение громкости
         ModModule.TonePortamentoPlusVolumeSlide(EBP.MODVOICE.EffectParametr);
         BREAK;

        CASE VibratoPlusVolumeSlide: // Эффект Vibrato + скольжение громкости
         // Делаем эффект Vibrato и скольжение громкости
         ModModule.VibratoPlusVolumeSlide(EBP.MODVOICE.EffectParametr);
         BREAK;

        CASE Tremolo: // Эффект вибрации громкости
         // Обрабатываем эффект
         ModModule.Tremolo(EBP.MODVOICE.EffectParametr);
         BREAK;

        case VolumeSlide: // Скольжение громкости
         // Применяем эффект скольжения громкости
         ModModule.VolumeSlide(EBP.MODVOICE.EffectParametr);
         BREAK;

        case ExtendedEffects: // Расширенные эффекты
         // BL - Y
         BL=EBP.MODVOICE.EffectParametr&0Fh;
         // Выбираем расширенный эффект
         SWITCH(EBP.MODVOICE.EffectParametr>>4){
          CASE RetrigNote: // Сброс ноты
           // Обрабатываем эффект
           ModModule.RetrigNote(BL);
           BREAK;

          CASE CutNote: // Срезать ноту после Y фреймов
           // Обрабатываем эффект
           ModModule.CutNote(BL);
           BREAK;

          CASE NoteDelay: // Задержать ноту на Y фреймов
           // Обрабатываем эффект
           ModModule.NoteDelay(BL);
           BREAK;
         }
       }

      $pop ECX
  }

 $pop EBP
}



/////////////////////////////////////////////////////////////////////////////
// Эффекты //////////////////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////



// Эффект #0
// Эффект Arpeggio //////////////////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - XY
:void static fastcall MODMODULE::Arpeggio(AL)
{
 // Есть параметр ?
 if (AL){
    // Нулевой кадр ?
    IF (!ModModule.Frame){
       // Ищим индекс в таблице периодов
       FOR (ECX=0,BX=EBP.MODVOICE.CurrentNotePeriod; ECX<36; ECX++){
           // Нашли индекс ?
           IF (PeriodTable[ECX*2]<=BX){
              // Сохраняем текущий период
              BX=PeriodTable[ECX*2];
              IF (BX<MinPeriod) BX=MinPeriod;
              EBP.MODVOICE.ArpeggioNotePeriod[0]=BX;
              // Сохраняем текущий период плюс X полутонов
              ESI=AL>>4+ECX; BX=PeriodTable[ESI*2];
              IF (BX<MinPeriod) BX=MinPeriod;
              EBP.MODVOICE.ArpeggioNotePeriod[2]=BX;
              // Сохраняем текущий период плюс Y полутонов
              ESI=AL&00Fh+ECX; BX=PeriodTable[ESI*2];
              IF (BX<MinPeriod) BX=MinPeriod;
              EBP.MODVOICE.ArpeggioNotePeriod[4]=BX;
              // Выходим
              BREAK;
           }
       }
    }ELSE{
       // Рассчитываем смещение в массиве периодов эффекта Arpeggio
       EDX=ModModule.Frame%3;
       // Сохраняем текущий период ноты
       EBP.MODVOICE.CurrentNotePeriod=EBP.MODVOICE.ArpeggioNotePeriod[EDX*2];
    }
 }
}



// Эффект #1
// Эффект Portamento Up /////////////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - XY
:void static fastcall MODMODULE::PortamentoUp(AL)
{
 // Это не нулевой фрейм ?
 IF (ModModule.Frame){
    // Рассчитываем новый период ноты
    EBP.MODVOICE.CurrentNotePeriod-=AL;
    // Вышли за минимальный период ?
    IF (EBP.MODVOICE.CurrentNotePeriod<MinPeriod){
       // Устанавливаем минимальный период
       EBP.MODVOICE.CurrentNotePeriod=MinPeriod;
    }
 }
}



// Эффект #2
// Эффект Portamento Down ///////////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - XY
:void static fastcall MODMODULE::PortamentoDown(AL)
{
 // Это не нулевой фрейм ?
 IF (ModModule.Frame){
    // Рассчитываем новый период ноты
    EBP.MODVOICE.CurrentNotePeriod+=AL;
    // Вышли за максимальный период ?
    IF (EBP.MODVOICE.CurrentNotePeriod>MaxPeriod){
       // Устанавливаем максимальный период
       EBP.MODVOICE.CurrentNotePeriod=MaxPeriod;
    }
 }
}



// Эффект #3
// Эффект Tone Portamento ///////////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - XY
:void static fastcall MODMODULE::TonePortamento(AL)
{
 // Это не нулевой фрейм ?
 IF (ModModule.Frame){
    EBX=EBP.MODVOICE.CurrentNotePeriod;
    ECX=EBP.MODVOICE.TonePortamentoEffectParametr;
    EDX=EBP.MODVOICE.TonePortamentoNotePeriod;
    // Текущий период > конечного периода ?
    IF (EBX>EDX){
       IF (long EBX-ECX>EDX) EBX-=ECX;
       ELSE EBX=EDX;
    }ELSE IF (EBX<EDX){
       IF (EBX+ECX<EDX) EBX+=ECX;
       ELSE EBX=EDX;
    }
    EBP.MODVOICE.CurrentNotePeriod=EBX;
 }ELSE{
    // Параметр эффекта указан ?
    IF (AL){
       // Сохраняем его
       EBP.MODVOICE.TonePortamentoEffectParametr=AL;
    }
 }
}



// Эффект #4
// Эффект Vibrato ///////////////////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - XY
:void static fastcall MODMODULE::Vibrato(AL)
{
 // Это не нулевой фрейм ?
 IF (ModModule.Frame){
    AL=EBP.MODVOICE.VibratoEffectParametr;
    AH=AL>>4+EBP.MODVOICE.VibratoWaveFormOffs&3Fh;
    EBP.MODVOICE.VibratoWaveFormOffs=AH;
    EBX=AH;
    AL&=0Fh;
    BX=SinTable[EBX]*AL*2; $sar bx,7
    EBP.MODVOICE.CurrentNotePeriod=EBP.MODVOICE.VibratoNotePeriod+BX;
 }ELSE{
    // Параметр X опущен ?
    IF (! $test AL,0F0h){
       // Берем старое значение параметра X
       AL|=EBP.MODVOICE.VibratoEffectParametr&0F0h;
    }
    // Параметр Y опущен ?
    IF (! $test AL,00Fh){
       // Берем старое значение параметра Y
       AL|=EBP.MODVOICE.VibratoEffectParametr&00Fh;
    }
    // Сохраняем параметр эффекта
    EBP.MODVOICE.VibratoEffectParametr=AL;
    // Сохраняем текущий период ноты
    EBP.MODVOICE.VibratoNotePeriod=EBP.MODVOICE.CurrentNotePeriod;
 }
}



// Эффект #5
// Эффект Tone Portamento + Volume Slide ////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - XY
:void static fastcall MODMODULE::TonePortamentoPlusVolumeSlide(AL)
{
 // Применяем эффект VolumeSlide
 ModModule.VolumeSlide(AL);
 // Применяем эффект TonePortamento
 ModModule.TonePortamento(FALSE);
}



// Эффект #6
// Эффект Vibrato + Volume Slide ////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - XY
:void static fastcall MODMODULE::VibratoPlusVolumeSlide(AL)
{
 // Применяем эффект VolumeSlide
 ModModule.VolumeSlide(AL);
 // Применяем эффект Vibrato
 ModModule.Vibrato(FALSE);
}



// Эффект #7
// Эффект Tremolo ///////////////////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - XY
:void static fastcall MODMODULE::Tremolo(AL)
{
 // Это не нулевой фрейм ?
 IF (ModModule.Frame){
    AL=EBP.MODVOICE.TremoloEffectParametr;
    AH=AL>>4+EBP.MODVOICE.TremoloWaveFormOffs&3Fh;
    EBP.MODVOICE.TremoloWaveFormOffs=AH;
    EBX=AH;
    AL&=0Fh;
    BX=SinTable[EBX]*AL*2; $sar bx,7
    // Устанавливаем текущую громкость
    ModModule.SetVolume(EBP.MODVOICE.TremoloVolume+BL);
 }ELSE{
    // Параметр X опущен ?
    IF (! $test AL,0F0h){
       // Берем старое значение параметра X
       AL|=EBP.MODVOICE.TremoloEffectParametr&0F0h;
    }
    // Параметр Y опущен ?
    IF (! $test AL,00Fh){
       // Берем старое значение параметра Y
       AL|=EBP.MODVOICE.TremoloEffectParametr&00Fh;
    }
    // Сохраняем параметр эффекта
    EBP.MODVOICE.TremoloEffectParametr=AL;
    // Сохраняем текущий период ноты
    EBP.MODVOICE.TremoloVolume=EBP.MODVOICE.CurrentVolume;
 }
}



// Эффект #8
// Эффект Pan ///////////////////////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - XY (000h - Left, 080h - Centr, 0FFh - Right)
:void static fastcall MODMODULE::Pan(AL)
{
 // Устанавливаем баланс на канале
 EBP.MODVOICE.Balance=AL;
}



// Эффект #9
// Эффект Sample Offset /////////////////////////////////////////////////////
// EBP - адрес структуры мод канала
// EAX - XY
:void static fastcall MODMODULE::SampleOffset(EAX)
{
 // Это не нулевой фрейм ?
 IF (ModModule.Frame){
    // Параметр нулевой ?
    IF (!EAX){
       // Берем последнее ненулевое значение на канале
       EAX=EBP.MODVOICE.OldSampleOffset;
    }ELSE{
       // Сохраняем это значение как последнее ненулевое
       EBP.MODVOICE.OldSampleOffset=AL;
    }
    // Умножаем на 256 и переводим в формат фиксированной точки 24:8 (*256<<8)
    // и устанавливаем позицию в семпле
    EBP.MODVOICE.SampleOffs=EAX<<16;
 }
}



// Эффект #A
// Эффект Volume Slide //////////////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - X Y (Если X!=0, то громкость повышается, если )
// if (x!=0) громкость повышается на x каждый фрейм (кроме нулевого фрейма)
// if (y!=0) громкость понижается на y каждый фрейм (кроме нулевого фрейма)
:void static fastcall MODMODULE::VolumeSlide(AL)
{
 // Это не нулевой фрейм ?
 IF (ModModule.Frame){
    // Повышать громкость ?
    IF ($test AL,0F0h) AL>>=4;
    ELSE -AL;
    // Устанавливаем громкость на канале
    ModModule.SetVolume(AL+EBP.MODVOICE.CurrentVolume);
 }
}



// Эффект #B
// Эффект Position Jump /////////////////////////////////////////////////////
// AL - XY (позиция в списке паттернов)
:void static fastcall MODMODULE::PositionJump(AL)
{
 // Позиция прыжка за границей композиции ?
 IF (AL<ModModule.OrderLength){
    // Сохраняем положение в списке паттернов
    ModModule.OrderPos=AL;
    // Устанавливаем положение в паттерне выше первого
    ModModule.PatternLine=-1;
 }
}



// Эффект #C
// Эффект Set Volume ////////////////////////////////////////////////////////
// AL - XY (громкость на канале)
// EBP - адрес структуры канала
:void static fastcall MODMODULE::SetVolume(AL)
{
 // Обрезаем громкость по диапазону [0..40h]
 IF (char AL<0) AL=0;
 IF (AL>40h) AL=40h;
 // Устанавливаем громкость канала
 EBP.MODVOICE.CurrentVolume=AL;
}



// Эффект #D
// Эффект Pattern Break /////////////////////////////////////////////////////
// AL - XY (номер строки в следующем паттерне)
:void static fastcall MODMODULE::PatternBreak(AL)
{
 // Позиция превышает количество строк в паттерне ?
 IF (AL>3Fh) AL=3Fh;
 // Сохраняе номер строки в паттерне
 ModModule.PatternLine=AL-1;
 // Переходим на следующий паттерн
 ModModule.NextPattern();
}



// Эффект #F
// Установка скорости воспроизведения ///////////////////////////////////////
:void static fastcall MODMODULE::SetSpeed(EAX)
{
 // Скорость больше нуля ?
 IF (EAX){
    // Сохраняем скорость воспроизведения в структуре мод модуля
    ModModule.Speed=EAX;
    // Рассчитываем количество выборок на один фрейм
    // SamplesPerFrame=SbRate/(4*6*BPM/60)=SbRate*5/(BPM*2)
    ModModule.SamplesPerFrame=SbRate>>1*5/ModModule.BPM;
 }
}



/////////////////////////////////////////////////////////////////////////////
// Расширенные эффекты //////////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////



// Эффект #E0
// Set Filter - не реализован



// Эффект #E1
// Эффект Fine Portamento Up ////////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - Y
:void static fastcall MODMODULE::FinePortamentoUp(AL)
{
 // Нулевой кадр ?
 IF (!ModModule.Frame){
    // Повышаем высоту ноты на Y периодов
    EBP.MODVOICE.CurrentNotePeriod-=AL;
    // Залезли ниже минимального периода ?
    IF (EBP.MODVOICE.CurrentNotePeriod<MinPeriod){
       // Устанавливаем минимальный период
       EBP.MODVOICE.CurrentNotePeriod=MinPeriod;
    }
 }
}



// Эффект #E2
// Эффект Fine Portamento Down //////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - Y
:void static fastcall MODMODULE::FinePortamentoDown(AL)
{
 // Нулевой кадр ?
 IF (!ModModule.Frame){
    // Понижаем высоту ноты на Y периодов
    EBP.MODVOICE.CurrentNotePeriod+=AL;
    // Превысили максимальный период ?
    IF (EBP.MODVOICE.CurrentNotePeriod>MaxPeriod){
       // Устанавливаем максимальный период
       EBP.MODVOICE.CurrentNotePeriod=MaxPeriod;
    }
 }
}



// Эффект #E3
// Glissando Control - не реализован



// Эффект #E4
// Эффект Vibrato Wave Form /////////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - Y
:void static fastcall MODMODULE::VibratoWaveForm(AL)
{
 // Устанавливаем/сбрасываем флаг сброса указателя в волновой форме
 AH=AL&100b; EBP.MODVOICE.VibratoTrig=AH;
 // Сохраняем номер волновой формы
 EBP.MODVOICE.VibratoWaveFormNumber=AL&011b;
}



// Эффект #E5
// Set Finetune - не реализован



// Эффект #E6
// Эффект Pattern Loop //////////////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - Y (000h - поставить метку; иначе - количество прыжков)
:void static fastcall MODMODULE::PatternLoop(AL)
{
 // Это начало цикла ?
 IF (!AL){
    // Инициализируем эффект
     // Сохраняем текущее положение в списке паттернов
     EBP.MODVOICE.PatternLoopOrderPos=ModModule.OrderPos;
     // Сохраняем номер текущей строки
     EBP.MODVOICE.PatternLoopPatternLine=ModModule.PatternLine-1;
 }ELSE{
     // Инициализация прыжка ?
     IF (!EBP.MODVOICE.PatternLoopCounter){
        // Устанавливаем счетчик прыжков
        EBP.MODVOICE.PatternLoopCounter=AL+1;
     }
     // Уменьшаем счетчик прыжков
     EBP.MODVOICE.PatternLoopCounter--;
     // Прыгать ?
     IF (EBP.MODVOICE.PatternLoopCounter){
        // Устанавливаем текущее положение в списке паттернов
        ModModule.PositionJump(EBP.MODVOICE.PatternLoopOrderPos);
        // Устанавливаем текущую строку в паттерне
        ModModule.PatternLine=EBP.MODVOICE.PatternLoopPatternLine;
     }
 }
}



// Эффект #E7
// Эффект Tremolo Wave Form /////////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - Y
:void static fastcall MODMODULE::TremoloWaveForm(AL)
{
 // Устанавливаем/сбрасываем флаг сброса указателя в волновой форме
 AH=AL&100b; EBP.MODVOICE.TremoloTrig=AH;
 // Сохраняем номер волновой формы
 EBP.MODVOICE.TremoloWaveFormNumber=AL&011b;
}



// Эффект #E8
// Эффект 16 Position Pan ///////////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - Y (00h - Left, 08h - Centr, 0Fh - Right)
:void static fastcall MODMODULE::_16PositionPan(AL)
{
 // Устанавливаем баланс на канале
 EBP.MODVOICE.Balance=AL<<4;
}



// Эффект #E9
// Эффект Retrig Note ///////////////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - Y
:void static fastcall MODMODULE::RetrigNote(AL)
{
 // Параметр вообще есть ?
 IF (AL){
    // Прошло Y фреймов ?
    IF (EBP.MODVOICE.RetrigNoteFrameCounter==AL){
       // Сбрасываем положение в семпле
       EBP.MODVOICE.SampleOffs=0;
       // Сбрасываем счетчик кадров
       EBP.MODVOICE.RetrigNoteFrameCounter=0;
    }ELSE{
       // Приращиваем номер проигранного фрейма
       EBP.MODVOICE.RetrigNoteFrameCounter++;
    }
 }
}



// Эффект #EA
// Эффект Fine Volume Slide Up //////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - Y
:void static fastcall MODMODULE::FineVolumeSlideUp(AL)
{
 // Нулевой кадр ?
 IF (!ModModule.Frame){
    // Повышаем громкость на Y
    ModModule.SetVolume(AL+EBP.MODVOICE.CurrentVolume);
 }
}



// Эффект #EB
// Эффект Fine Volume Slide Down ////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - Y
:void static fastcall MODMODULE::FineVolumeSlideDown(AL)
{
 // Нулевой кадр ?
 IF (!ModModule.Frame){
    // Понижаем громкость на Y
    ModModule.SetVolume(-AL+EBP.MODVOICE.CurrentVolume);
 }
}



// Эффект #EC
// Эффект Cut Note //////////////////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - Y
:void static fastcall MODMODULE::CutNote(AL)
{
 // Прошло Y фреймов ?
 IF (ModModule.Frame==AL){
    // Отрубаем звук на канале
    EBP.MODVOICE.CurrentVolume=0;
 }
}



// Эффект #ED
// Эффект Note Delay ////////////////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - Y
:void static fastcall MODMODULE::NoteDelay(AL)
{
 // Прошло Y фреймов ?
 IF (ModModule.Frame==AL){
    // Обрабатываем комбинации периода ноты и номера семпла
    ProcessNotePeriodAndSampleNumber(EBP.MODVOICE.NoteDelayNotePeriod,EBP.MODVOICE.NoteDelaySampleNumber);
 }
}



// Эффект #EE
// Эффект Pattern Delay /////////////////////////////////////////////////////
// EBP - адрес структуры мод канала
// AL - Y (множитель скорости)
:void static fastcall MODMODULE::PatternDelay(AL)
{
 // Параметр присутствует ?
 IF (AL){
    // Устанавливаем задержку
    ModModule.PatternDelayCount=AL;
 }
}



// Эффект #EF
// Invert Loop - не реализован


/////////////////////////////////////////////////////////////////////////////
// Методы управления плеером ////////////////////////////////////////////////
/////////////////////////////////////////////////////////////////////////////




// Установка количества ударов в минуту /////////////////////////////////////
:void static fastcall MODMODULE::SetBPM(EBX)
{
 // Сохраняем BPM в структуре мод модуля
 ModModule.BPM=EBX;
 // Рассчитываем количество выборок на одни фрейм
 // SamplesPerFrame=SbRate/(4*speed*BPM/60)=SbRate*15/(Speed*BPM)
 ModModule.SamplesPerFrame=SbRate>>1*5/EBX;
}



// Переход на следующую строку в паттерне ///////////////////////////////////
:void static fastcall MODMODULE::NextLine()
{
 // Приращиваем строку в паттерне
 ModModule.PatternLine++;
 // Это последняя строка в паттерне ?
 IF (ModModule.PatternLine==64){
    // Сбрасываем текущую строку в 0
    ModModule.PatternLine=0;
    // Переходим на следующий паттерн
    ModModule.NextPattern();
 }
}



// Переход на предыдущую строку в паттерне //////////////////////////////////
:void static fastcall MODMODULE::PreviousLine()
{
 // Приращиваем строку в паттерне
 IF (ModModule.PatternLine) ModModule.PatternLine--;
 ELSE{
    // Устанавливаем последнюю строку в паттерне
    ModModule.PatternLine=63;
    // Переходим на следующий паттерн
    ModModule.PreviousPattern();
 }
}



// Переход на следующий паттерн по порядку паттернов ////////////////////////
:void static fastcall MODMODULE::NextPattern()
{
 // Приращиваем положение в списке паттернов
 ModModule.OrderPos++;
 // Положение в списке больше вышло за предел списка композиции ?
 IF (ModModule.OrderPos>=ModModule.OrderLength){
    // Сбрасываем положение в списке паттернов в 0
    ModModule.OrderPos=ModModule.ReStart;
 }
}



// Переход на предыдущий паттерн по порядку паттернов ///////////////////////
:void static fastcall MODMODULE::PreviousPattern()
{
 // Приращиваем положение в списке паттернов
 IF (ModModule.OrderPos) ModModule.OrderPos--;
 ELSE{
    // Устанавливаем положение в списке паттернов на конец
    ModModule.OrderPos=ModModule.OrderLength-1;
 }
}



