#include "mscHack.hpp" #include "window.hpp" //----------------------------------------------------- // Procedure: constructor //----------------------------------------------------- Widget_EnvelopeEdit::Widget_EnvelopeEdit( int x, int y, int w, int h, int handleSize, void *pClass, EnvelopeEditCALLBACK *pCallback, int nchannels ) { int ch, hd; if( !pClass || !pCallback ) return; m_pClass = pClass; m_pCallback = pCallback; m_handleSize = handleSize; m_MaxChannels = nchannels; box.pos = Vec( x, y ); box.size = Vec( w, h ); // calc division size (16 divisions, 4 beats x 4 quarter) m_divw = ( ( (float)w - (float)ENVELOPE_HANDLES) / (float)ENVELOPE_DIVISIONS ) + 1.0f; m_handleSizeD2 = ( (float)handleSize / 2.0f ); // handles for( ch = 0; ch < MAX_ENVELOPE_CHANNELS; ch++ ) { m_EnvData[ ch ].Init( EnvelopeData::MODE_LOOP, EnvelopeData::RANGE_0to5, false, m_divw ); for( hd = 0; hd < ENVELOPE_HANDLES; hd++ ) m_HandleCol[ hd ].dwCol = 0xFFFFFF; } recalcLine( -1, 0 ); m_BeatLen = (int)engineGetSampleRate(); m_bInitialized = true; } //----------------------------------------------------- // Procedure: Val2y //----------------------------------------------------- float Widget_EnvelopeEdit::Val2y( float fval ) { return clamp( box.size.y - ( fval * box.size.y ), 0.0f, box.size.y ); } //----------------------------------------------------- // Procedure: y2Val //----------------------------------------------------- float Widget_EnvelopeEdit::y2Val( float fy ) { return clamp( 1.0 - (fy / box.size.y ), 0.0f, 1.0f ); } //----------------------------------------------------- // Procedure: recalcLine //----------------------------------------------------- void Widget_EnvelopeEdit::recalcLine( int chin, int handle ) { // calc all lines if( chin == -1 ) { for( int ch = 0; ch < MAX_ENVELOPE_CHANNELS; ch++ ) { m_EnvData[ ch ].recalcLine( -1 ); } } // calc line before and line after handle else { m_EnvData[ chin ].recalcLine( handle ); } } //----------------------------------------------------- // Procedure: setPos //----------------------------------------------------- void Widget_EnvelopeEdit::setView( int ch ) { if( !m_bInitialized && ch < MAX_ENVELOPE_CHANNELS && ch >= 0 ) return; m_currentChannel = ch; } //----------------------------------------------------- // Procedure: resetVal //----------------------------------------------------- void Widget_EnvelopeEdit::resetValAll( int ch, float val ) { if( !m_bInitialized && ch < MAX_ENVELOPE_CHANNELS && ch >= 0 ) return; m_EnvData[ ch ].resetValAll( val ); } //----------------------------------------------------- // Procedure: setVal //----------------------------------------------------- void Widget_EnvelopeEdit::setVal( int ch, int handle, float val ) { if( !m_bInitialized && ch < MAX_ENVELOPE_CHANNELS && ch >= 0 ) return; m_EnvData[ ch ].setVal( handle, val ); } //----------------------------------------------------- // Procedure: setRange //----------------------------------------------------- void Widget_EnvelopeEdit::setRange( int ch, int Range ) { if( !m_bInitialized && ch < MAX_ENVELOPE_CHANNELS && ch >= 0 ) return; m_EnvData[ ch ].m_Range = Range; } //----------------------------------------------------- // Procedure: setMode //----------------------------------------------------- void Widget_EnvelopeEdit::setMode( int ch, int Mode ) { if( !m_bInitialized && ch < MAX_ENVELOPE_CHANNELS && ch >= 0 ) return; m_EnvData[ ch ].setMode( Mode ); } //----------------------------------------------------- // Procedure: setGateMode //----------------------------------------------------- void Widget_EnvelopeEdit::setGateMode( int ch, bool bGate ) { if( !m_bInitialized && ch < MAX_ENVELOPE_CHANNELS && ch >= 0 ) return; m_EnvData[ ch ].m_bGateMode = bGate; } //----------------------------------------------------- // Procedure: setTimeDiv //----------------------------------------------------- void Widget_EnvelopeEdit::setTimeDiv( int ch, int timediv ) { if( !m_bInitialized && ch < MAX_ENVELOPE_CHANNELS && ch >= 0 ) return; m_TimeDiv[ ch ] = timediv; setBeatLen( m_BeatLen ); } //----------------------------------------------------- // Procedure: setDataAll //----------------------------------------------------- int Widget_EnvelopeEdit::getPos( int ch ) { return (int)( m_EnvData[ ch ].m_Clock.fpos * 10000.0f ); } //----------------------------------------------------- // Procedure: setDataAll //----------------------------------------------------- void Widget_EnvelopeEdit::setPos( int ch, int pos ) { m_EnvData[ ch ].m_Clock.fpos = (float)pos / 10000.0f; } //----------------------------------------------------- // Procedure: setDataAll //----------------------------------------------------- void Widget_EnvelopeEdit::setDataAll( int *pint ) { int i, j, count = 0; if( !m_bInitialized ) return; for( i = 0; i < MAX_ENVELOPE_CHANNELS; i++ ) { for( j = 0; j < ENVELOPE_HANDLES; j++ ) { m_EnvData[ i ].m_HandleVal[ j ] = clamp( (float)pint[ count++ ] / 10000.0f, 0.0f, 1.0f ); } } // recalc all lines recalcLine( -1, 0 ); } //----------------------------------------------------- // Procedure: getDataAll //----------------------------------------------------- void Widget_EnvelopeEdit::getDataAll( int *pint ) { int i, j, count = 0; if( !m_bInitialized ) return; for( i = 0; i < MAX_ENVELOPE_CHANNELS; i++ ) { for( j = 0; j < ENVELOPE_HANDLES; j++ ) { pint[ count++ ] = (int)( m_EnvData[ i ].m_HandleVal[ j ] * 10000.0 ); } } } //----------------------------------------------------- // Procedure: draw //----------------------------------------------------- int hdivs[ EnvelopeData::nRANGES ] = { 6, 11, 11, 21, 2, 13 }; void Widget_EnvelopeEdit::draw( NVGcontext *vg ) { int h, hlines; float x, y, divsize; float linewidth = 1.0; if( !m_bInitialized ) return; // fill bg nvgBeginPath(vg); nvgRect(vg, 0, 0, box.size.x-1, box.size.y-1 ); //nvgFillColor(vg, nvgRGBA(40,40,40,255)); nvgFillColor(vg, nvgRGBA(57, 10, 10,255)); nvgFill(vg); nvgStrokeWidth( vg, linewidth ); nvgStrokeColor( vg, nvgRGBA( 60, 60, 60, 255 ) ); // draw bounding line /*nvgBeginPath( vg ); nvgMoveTo( vg, 0, 0 ); nvgLineTo( vg, box.size.x - 1, 0 ); nvgLineTo( vg, box.size.x - 1, box.size.y - 1 ); nvgLineTo( vg, 0, box.size.y - 1 ); nvgClosePath( vg ); nvgStroke( vg );*/ x = 0.0; // draw vertical lines for( h = 0; h < ENVELOPE_HANDLES; h++ ) { nvgBeginPath(vg); nvgMoveTo( vg, x, 0 ); nvgLineTo( vg, x, box.size.y - 1 ); nvgStroke( vg ); x += m_divw; } y = 0.0; hlines = hdivs[ m_EnvData[ m_currentChannel ].m_Range ]; divsize = box.size.y / (float)( hlines - 1 ); // draw horizontal lines for( h = 0; h < hlines; h++ ) { if( h == hlines / 2 && ( m_EnvData[ m_currentChannel ].m_Range == EnvelopeData::RANGE_n5to5 || m_EnvData[ m_currentChannel ].m_Range == EnvelopeData::RANGE_n10to10 || m_EnvData[ m_currentChannel ].m_Range == EnvelopeData::RANGE_Audio ) ) nvgStrokeColor( vg, nvgRGBA( 255, 255, 255, 255 ) ); else nvgStrokeColor( vg, nvgRGBA( 80, 80, 80, 255 ) ); nvgBeginPath(vg); nvgMoveTo( vg, 0, y ); nvgLineTo( vg, box.size.x - 1, y ); nvgStroke( vg ); y += divsize; } if( m_EnvData[ m_currentChannel ].m_bGateMode ) { // draw rects for( h = 0; h < ENVELOPE_DIVISIONS; h++ ) { nvgBeginPath(vg); nvgRect( vg, h * m_divw, ( 1.0f - m_EnvData[ m_currentChannel ].m_HandleVal[ h ] ) * box.size.y, m_divw, box.size.y * m_EnvData[ m_currentChannel ].m_HandleVal[ h ] ); nvgFillColor(vg, nvgRGBA( 157, 100, 100, 128 ) ); nvgFill(vg); } } else { nvgStrokeColor( vg, nvgRGBA( 255, 255, 255, 255 ) ); nvgBeginPath(vg); x = 0; nvgMoveTo( vg, x, ( 1.0f - m_EnvData[ m_currentChannel ].m_HandleVal[ 0 ] ) * box.size.y ); // draw lines for( h = 1; h < ENVELOPE_HANDLES; h++ ) { x += m_divw; nvgLineTo( vg, x, ( 1.0f - m_EnvData[ m_currentChannel ].m_HandleVal[ h ] ) * box.size.y ); } nvgStroke( vg ); x = 0; // draw handles for( h = 0; h < ENVELOPE_DIVISIONS + 1; h++ ) { nvgBeginPath(vg); y = ( 1.0f - m_EnvData[ m_currentChannel ].m_HandleVal[ h ] ) * box.size.y; nvgRect( vg, x - m_handleSizeD2, y - m_handleSizeD2, m_handleSize, m_handleSize ); nvgFillColor(vg, nvgRGBA( m_HandleCol[ h ].Col[ 2 ], m_HandleCol[ h ].Col[ 1 ], m_HandleCol[ h ].Col[ 0 ],255 ) ); nvgFill(vg); x += m_divw; } } // draw indicator line if( m_EnvData[ m_currentChannel ].m_Range != EnvelopeData::RANGE_Audio ) { nvgStrokeColor( vg, nvgRGBA( 255, 255, 255, 80 ) ); nvgBeginPath(vg); nvgMoveTo( vg, m_EnvData[ m_currentChannel ].m_fIndicator * box.size.x, 0 ); nvgLineTo( vg, m_EnvData[ m_currentChannel ].m_fIndicator * box.size.x, box.size.y ); nvgStroke( vg ); } } //----------------------------------------------------- // Procedure: onMouseDown //----------------------------------------------------- void Widget_EnvelopeEdit::onMouseDown( EventMouseDown &e ) { int index; m_Drag = -1; e.consumed = false; OpaqueWidget::onMouseDown( e ); if( !m_bInitialized ) return; if( e.button == 0 ) { if( !m_bDraw ) windowCursorLock(); if( m_EnvData[ m_currentChannel ].m_bGateMode ) m_Drag = clamp( ( e.pos.x / box.size.x ) * (float)ENVELOPE_DIVISIONS, 0.0f, (float)(ENVELOPE_DIVISIONS - 1) ); else m_Drag = clamp( ( ( e.pos.x + (m_divw / 2.0f) ) / box.size.x ) * (float)ENVELOPE_DIVISIONS, 0.0f, (float)ENVELOPE_DIVISIONS ); m_bDrag = true; } else if( e.button == 1 ) { if( m_EnvData[ m_currentChannel ].m_bGateMode ) index = clamp( ( e.pos.x / box.size.x ) * (float)ENVELOPE_DIVISIONS, 0.0f, (float)(ENVELOPE_DIVISIONS - 1) ); else index = clamp( ( ( e.pos.x + (m_divw / 2.0f) ) / box.size.x ) * (float)ENVELOPE_DIVISIONS, 0.0f, (float)ENVELOPE_DIVISIONS ); m_EnvData[ m_currentChannel ].m_HandleVal[ index ] = 0.0f; } } //----------------------------------------------------- // Procedure: onDragStart //----------------------------------------------------- void Widget_EnvelopeEdit::onDragStart(EventDragStart &e) { e.consumed = true; //windowCursorLock(); } //----------------------------------------------------- // Procedure: onDragEnd //----------------------------------------------------- void Widget_EnvelopeEdit::onDragEnd(EventDragEnd &e) { e.consumed = true; windowCursorUnlock(); m_Drag = -1; } //----------------------------------------------------- // Procedure: onMouseMove //----------------------------------------------------- void Widget_EnvelopeEdit::onMouseMove( EventMouseMove &e ) { OpaqueWidget::onMouseMove( e ); if( m_bDrag && m_bDraw ) { m_Drawy = e.pos.y; if( m_EnvData[ m_currentChannel ].m_bGateMode ) m_Drag = clamp( ( e.pos.x / box.size.x ) * (float)ENVELOPE_DIVISIONS, 0.0f, (float)(ENVELOPE_DIVISIONS - 1) ); else m_Drag = clamp( ( ( e.pos.x + (m_divw / 2.0f) ) / box.size.x ) * (float)ENVELOPE_DIVISIONS, 0.0f, (float)ENVELOPE_DIVISIONS ); } } //----------------------------------------------------- // Procedure: onDragMove //----------------------------------------------------- void Widget_EnvelopeEdit::onDragMove(EventDragMove &e) { int h, i; float fband; float delta = 0.001f; e.consumed = true; if( !m_bInitialized || !m_bDrag ) return; if( !m_bDraw ) { // Drag slower if Mod is held if (windowIsModPressed()) delta = 0.00001f; m_EnvData[ m_currentChannel ].m_HandleVal[ m_Drag ] -= delta * e.mouseRel.y; m_EnvData[ m_currentChannel ].m_HandleVal[ m_Drag ] = clamp( m_EnvData[ m_currentChannel ].m_HandleVal[ m_Drag ], 0.0f, 1.0f ); if( m_pCallback ) m_pCallback( m_pClass, m_EnvData[ m_currentChannel ].getActualVal( m_EnvData[ m_currentChannel ].m_HandleVal[ m_Drag ] ) ); if( m_fband > 0.0001 ) { fband = m_fband; for( h = -1; h > -4; h -- ) { i = m_Drag + h; if( i < 0 ) break; m_EnvData[ m_currentChannel ].m_HandleVal[ i ] -= (delta * e.mouseRel.y) * fband; m_EnvData[ m_currentChannel ].m_HandleVal[ i ] = clamp( m_EnvData[ m_currentChannel ].m_HandleVal[ i ], 0.0f, 1.0f ); fband *= 0.6f; } fband = m_fband; for( h = 1; h < 4; h ++ ) { i = m_Drag + h; if( i > ENVELOPE_DIVISIONS ) break; m_EnvData[ m_currentChannel ].m_HandleVal[ i ] -= (delta * e.mouseRel.y) * fband; m_EnvData[ m_currentChannel ].m_HandleVal[ i ] = clamp( m_EnvData[ m_currentChannel ].m_HandleVal[ i ], 0.0f, 1.0f ); fband *= 0.6f; } recalcLine( -1, m_Drag ); } else { recalcLine( m_currentChannel, m_Drag ); } } else { m_EnvData[ m_currentChannel ].m_HandleVal[ m_Drag ] = 1.0 - ( m_Drawy / box.size.y ); m_EnvData[ m_currentChannel ].m_HandleVal[ m_Drag ] = clamp( m_EnvData[ m_currentChannel ].m_HandleVal[ m_Drag ], 0.0f, 1.0f ); if( m_pCallback ) m_pCallback( m_pClass, m_EnvData[ m_currentChannel ].getActualVal( m_EnvData[ m_currentChannel ].m_HandleVal[ m_Drag ] ) ); recalcLine( m_currentChannel, m_Drag ); } } //----------------------------------------------------- // Procedure: setBeatLen //----------------------------------------------------- void Widget_EnvelopeEdit::setBeatLen( int len ) { m_BeatLen = len; m_bClkd = true; if( m_BeatLen <= 0 ) return; for( int i = 0; i < MAX_ENVELOPE_CHANNELS; i++ ) { switch( m_TimeDiv[ i ] ) { case TIME_64th: m_EnvData[ i ].m_Clock.syncInc = ( ( engineGetSampleRate() / (float)m_BeatLen ) * 16.0 ) / 16.0; break; case TIME_32nd: m_EnvData[ i ].m_Clock.syncInc = ( ( engineGetSampleRate() / (float)m_BeatLen ) * 8.0 ) / 16.0; break; case TIME_16th: m_EnvData[ i ].m_Clock.syncInc = ( ( engineGetSampleRate() / (float)m_BeatLen ) * 4.0 ) / 16.0; break; case TIME_8th: m_EnvData[ i ].m_Clock.syncInc = ( ( engineGetSampleRate() / (float)m_BeatLen ) * 2.0 ) / 16.0; break; case TIME_4tr: m_EnvData[ i ].m_Clock.syncInc = ( ( engineGetSampleRate() / (float)m_BeatLen ) * 1.0 ) / 16.0; break; case TIME_Bar: m_EnvData[ i ].m_Clock.syncInc = ( ( engineGetSampleRate() / (float)m_BeatLen ) * 0.25 ) / 16.0; break; } } } //----------------------------------------------------- // Procedure: procStep //----------------------------------------------------- float Widget_EnvelopeEdit::procStep( int ch, bool bTrig, bool bHold ) { if( ( m_bClkReset || bTrig ) && !bHold ) { if( m_EnvData[ ch ].m_Mode == EnvelopeData::MODE_REVERSE ) m_EnvData[ ch ].m_Clock.fpos = engineGetSampleRate(); else m_EnvData[ ch ].m_Clock.fpos = 0; } return m_EnvData[ ch ].procStep( bTrig, bHold ); }