/[svn]/linuxsampler/trunk/src/engines/common/InstrumentScriptVMFunctions.cpp
ViewVC logotype

Diff of /linuxsampler/trunk/src/engines/common/InstrumentScriptVMFunctions.cpp

Parent Directory Parent Directory | Revision Log Revision Log | View Patch Patch

revision 3187 by schoenebeck, Fri Apr 21 13:33:03 2017 UTC revision 3188 by schoenebeck, Fri May 19 14:23:12 2017 UTC
# Line 359  namespace LinuxSampler { Line 359  namespace LinuxSampler {
359          if (iArg == 0)          if (iArg == 0)
360              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
361          else          else
362              return INT_EXPR;              return type == INT_EXPR;
363      }      }
364    
365      VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_vol::exec(VMFnArgs* args) {
# Line 446  namespace LinuxSampler { Line 446  namespace LinuxSampler {
446          if (iArg == 0)          if (iArg == 0)
447              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
448          else          else
449              return INT_EXPR;              return type == INT_EXPR;
450      }      }
451    
452      VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_tune::exec(VMFnArgs* args) {
# Line 533  namespace LinuxSampler { Line 533  namespace LinuxSampler {
533          if (iArg == 0)          if (iArg == 0)
534              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
535          else          else
536              return INT_EXPR;              return type == INT_EXPR;
537      }      }
538    
539      VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pan::exec(VMFnArgs* args) {
# Line 635  namespace LinuxSampler { Line 635  namespace LinuxSampler {
635          if (iArg == 0)          if (iArg == 0)
636              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
637          else          else
638              return INT_EXPR;              return type == INT_EXPR;
639      }      }
640    
641      VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_cutoff::exec(VMFnArgs* args) {
# Line 722  namespace LinuxSampler { Line 722  namespace LinuxSampler {
722          if (iArg == 0)          if (iArg == 0)
723              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
724          else          else
725              return INT_EXPR;              return type == INT_EXPR;
726      }      }
727    
728      VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_reso::exec(VMFnArgs* args) {
# Line 809  namespace LinuxSampler { Line 809  namespace LinuxSampler {
809          if (iArg == 0)          if (iArg == 0)
810              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
811          else          else
812              return INT_EXPR;              return type == INT_EXPR;
813      }      }
814    
815      VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_attack::exec(VMFnArgs* args) {
# Line 896  namespace LinuxSampler { Line 896  namespace LinuxSampler {
896          if (iArg == 0)          if (iArg == 0)
897              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
898          else          else
899              return INT_EXPR;              return type == INT_EXPR;
900      }      }
901    
902      VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_decay::exec(VMFnArgs* args) {
# Line 983  namespace LinuxSampler { Line 983  namespace LinuxSampler {
983          if (iArg == 0)          if (iArg == 0)
984              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
985          else          else
986              return INT_EXPR;              return type == INT_EXPR;
987      }      }
988    
989      VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_release::exec(VMFnArgs* args) {
# Line 1059  namespace LinuxSampler { Line 1059  namespace LinuxSampler {
1059          return successResult();          return successResult();
1060      }      }
1061    
     #define VM_GENERAL_CHANGE_SYNTH_PAR_MAX_VALUE 1000000  
   
1062      bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {      bool VMChangeSynthParamFunction::acceptsArgType(int iArg, ExprType_t type) const {
1063          if (iArg == 0)          if (iArg == 0)
1064              return type == INT_EXPR || type == INT_ARR_EXPR;              return type == INT_EXPR || type == INT_ARR_EXPR;
1065          else          else
1066              return INT_EXPR;              return type == INT_EXPR;
1067      }      }
1068    
1069      template<float NoteBase::_Override::*T_noteParam, int T_synthParam>      template<float NoteBase::_Override::*T_noteParam, int T_synthParam,
1070                 bool T_isNormalizedParam, int T_maxValue, int T_minValue>
1071      VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {      VMFnResult* VMChangeSynthParamFunction::execTemplate(VMFnArgs* args, const char* functionName) {
1072          int value = args->arg(1)->asInt()->evalInt();          int value = args->arg(1)->asInt()->evalInt();
1073          if (value > VM_GENERAL_CHANGE_SYNTH_PAR_MAX_VALUE) {          if (value > T_maxValue) {
1074              wrnMsg(String(functionName) + "(): argument 2 may not be larger than 1000000");              wrnMsg(String(functionName) + "(): argument 2 may not be larger than " + ToString(T_maxValue));
1075              value = VM_GENERAL_CHANGE_SYNTH_PAR_MAX_VALUE;              value = T_maxValue;
1076          } else if (value < 0) {          } else if (value < T_minValue) {
1077              wrnMsg(String(functionName) + "(): argument 2 may not be negative");              if (T_minValue == 0)
1078              value = 0;                  wrnMsg(String(functionName) + "(): argument 2 may not be negative");
1079                else
1080                    wrnMsg(String(functionName) + "(): argument 2 may not be smaller than " + ToString(T_minValue));
1081                value = T_minValue;
1082          }          }
1083          const float fValue = float(value) / float(VM_GENERAL_CHANGE_SYNTH_PAR_MAX_VALUE);          const float fValue = (T_isNormalizedParam) ?
1084                float(value) / float(T_maxValue) : // convert to 0.0 .. 1.0 value range
1085                float(value) / 1000000.f; // assuming microseconds here, convert to seconds
1086    
1087          AbstractEngineChannel* pEngineChannel =          AbstractEngineChannel* pEngineChannel =
1088              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);              static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
# Line 1147  namespace LinuxSampler { Line 1151  namespace LinuxSampler {
1151      // change_amp_lfo_depth() function      // change_amp_lfo_depth() function
1152    
1153      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_depth::exec(VMFnArgs* args) {
1154          return VMChangeSynthParamFunction::execTemplate< &NoteBase::_Override::AmpLFODepth, Event::synth_param_amp_lfo_depth >(args, "change_amp_lfo_depth");          return VMChangeSynthParamFunction::execTemplate<
1155                        &NoteBase::_Override::AmpLFODepth,
1156                        Event::synth_param_amp_lfo_depth, true>( args, "change_amp_lfo_depth" );
1157      }      }
1158    
1159      // change_amp_lfo_freq() function      // change_amp_lfo_freq() function
1160    
1161      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_amp_lfo_freq::exec(VMFnArgs* args) {
1162          return VMChangeSynthParamFunction::execTemplate< &NoteBase::_Override::AmpLFOFreq, Event::synth_param_amp_lfo_freq >(args, "change_amp_lfo_freq");          return VMChangeSynthParamFunction::execTemplate<
1163                        &NoteBase::_Override::AmpLFOFreq,
1164                        Event::synth_param_amp_lfo_freq, true>( args, "change_amp_lfo_freq" );
1165      }      }
1166    
1167      // change_pitch_lfo_depth() function      // change_pitch_lfo_depth() function
1168    
1169      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_depth::exec(VMFnArgs* args) {
1170          return VMChangeSynthParamFunction::execTemplate< &NoteBase::_Override::PitchLFODepth, Event::synth_param_pitch_lfo_depth >(args, "change_pitch_lfo_depth");          return VMChangeSynthParamFunction::execTemplate<
1171                        &NoteBase::_Override::PitchLFODepth,
1172                        Event::synth_param_pitch_lfo_depth, true>( args, "change_pitch_lfo_depth" );
1173      }      }
1174    
1175      // change_pitch_lfo_freq() function      // change_pitch_lfo_freq() function
1176    
1177      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {      VMFnResult* InstrumentScriptVMFunction_change_pitch_lfo_freq::exec(VMFnArgs* args) {
1178          return VMChangeSynthParamFunction::execTemplate< &NoteBase::_Override::PitchLFOFreq, Event::synth_param_pitch_lfo_freq >(args, "change_pitch_lfo_freq");          return VMChangeSynthParamFunction::execTemplate<
1179                        &NoteBase::_Override::PitchLFOFreq,
1180                        Event::synth_param_pitch_lfo_freq, true>( args, "change_pitch_lfo_freq" );
1181        }
1182    
1183        // change_vol_time() function
1184    
1185        VMFnResult* InstrumentScriptVMFunction_change_vol_time::exec(VMFnArgs* args) {
1186            return VMChangeSynthParamFunction::execTemplate<
1187                        &NoteBase::_Override::VolumeTime,
1188                        Event::synth_param_volume_time, false>( args, "change_vol_time" );
1189        }
1190    
1191        // change_tune_time() function
1192    
1193        VMFnResult* InstrumentScriptVMFunction_change_tune_time::exec(VMFnArgs* args) {
1194            return VMChangeSynthParamFunction::execTemplate<
1195                        &NoteBase::_Override::PitchTime,
1196                        Event::synth_param_pitch_time, false>( args, "change_tune_time" );
1197        }
1198    
1199        // fade_in() function
1200    
1201        InstrumentScriptVMFunction_fade_in::InstrumentScriptVMFunction_fade_in(InstrumentScriptVM* parent)
1202            : m_vm(parent)
1203        {
1204        }
1205    
1206        bool InstrumentScriptVMFunction_fade_in::acceptsArgType(int iArg, ExprType_t type) const {
1207            if (iArg == 0)
1208                return type == INT_EXPR || type == INT_ARR_EXPR;
1209            else
1210                return type == INT_EXPR;
1211        }
1212    
1213        VMFnResult* InstrumentScriptVMFunction_fade_in::exec(VMFnArgs* args) {
1214            int duration = args->arg(1)->asInt()->evalInt();
1215            if (duration < 0) {
1216                wrnMsg("fade_in(): argument 2 may not be negative");
1217                duration = 0;
1218            }
1219            const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1220    
1221            AbstractEngineChannel* pEngineChannel =
1222                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1223    
1224            if (args->arg(0)->exprType() == INT_EXPR) {
1225                const ScriptID id = args->arg(0)->asInt()->evalInt();
1226                if (!id) {
1227                    wrnMsg("fade_in(): note ID for argument 1 may not be zero");
1228                    return successResult();
1229                }
1230                if (!id.isNoteID()) {
1231                    wrnMsg("fade_in(): argument 1 is not a note ID");
1232                    return successResult();
1233                }
1234    
1235                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1236                if (!pNote) return successResult();
1237    
1238                // if fade_in() was called immediately after note was triggered
1239                // then immediately apply a start volume of zero to Note object,
1240                // as well as the fade in duration
1241                if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1242                    pNote->Override.Volume = 0.f;
1243                    pNote->Override.VolumeTime = fDuration;
1244                } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1245                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1246                    e.Init(); // clear IDs
1247                    e.Type = Event::type_note_synth_param;
1248                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1249                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1250                    e.Param.NoteSynthParam.Delta    = fDuration;
1251                    e.Param.NoteSynthParam.Relative = false;
1252    
1253                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1254                }
1255                // and finally schedule a "volume" change, simply one time slice
1256                // ahead, with the final fade in volume (1.0)
1257                {
1258                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1259                    e.Init(); // clear IDs
1260                    e.Type = Event::type_note_synth_param;
1261                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1262                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1263                    e.Param.NoteSynthParam.Delta    = 1.f;
1264                    e.Param.NoteSynthParam.Relative = false;
1265    
1266                    // scheduling with 0 delay would also work here, but +1 is more
1267                    // safe regarding potential future implementation changes of the
1268                    // scheduler (see API comments of RTAVLTree::insert())
1269                    pEngineChannel->ScheduleEventMicroSec(&e, 1);
1270                }
1271            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1272                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1273                for (int i = 0; i < ids->arraySize(); ++i) {
1274                    const ScriptID id = ids->evalIntElement(i);
1275                    if (!id || !id.isNoteID()) continue;
1276    
1277                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1278                    if (!pNote) continue;
1279    
1280                    // if fade_in() was called immediately after note was triggered
1281                    // then immediately apply a start volume of zero to Note object,
1282                    // as well as the fade in duration
1283                    if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1284                        pNote->Override.Volume = 0.f;
1285                        pNote->Override.VolumeTime = fDuration;
1286                    } else { // otherwise schedule a "volume time" change with the requested fade in duration ...
1287                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1288                        e.Init(); // clear IDs
1289                        e.Type = Event::type_note_synth_param;
1290                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1291                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1292                        e.Param.NoteSynthParam.Delta    = fDuration;
1293                        e.Param.NoteSynthParam.Relative = false;
1294    
1295                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1296                    }
1297                    // and finally schedule a "volume" change, simply one time slice
1298                    // ahead, with the final fade in volume (1.0)
1299                    {
1300                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1301                        e.Init(); // clear IDs
1302                        e.Type = Event::type_note_synth_param;
1303                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1304                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1305                        e.Param.NoteSynthParam.Delta    = 1.f;
1306                        e.Param.NoteSynthParam.Relative = false;
1307    
1308                        // scheduling with 0 delay would also work here, but +1 is more
1309                        // safe regarding potential future implementation changes of the
1310                        // scheduler (see API comments of RTAVLTree::insert())
1311                        pEngineChannel->ScheduleEventMicroSec(&e, 1);
1312                    }
1313                }
1314            }
1315    
1316            return successResult();
1317        }
1318    
1319        // fade_out() function
1320    
1321        InstrumentScriptVMFunction_fade_out::InstrumentScriptVMFunction_fade_out(InstrumentScriptVM* parent)
1322            : m_vm(parent)
1323        {
1324        }
1325    
1326        bool InstrumentScriptVMFunction_fade_out::acceptsArgType(int iArg, ExprType_t type) const {
1327            if (iArg == 0)
1328                return type == INT_EXPR || type == INT_ARR_EXPR;
1329            else
1330                return type == INT_EXPR;
1331        }
1332    
1333        VMFnResult* InstrumentScriptVMFunction_fade_out::exec(VMFnArgs* args) {
1334            int duration = args->arg(1)->asInt()->evalInt();
1335            if (duration < 0) {
1336                wrnMsg("fade_out(): argument 2 may not be negative");
1337                duration = 0;
1338            }
1339            const float fDuration = float(duration) / 1000000.f; // convert microseconds to seconds
1340    
1341            bool stop = (args->argsCount() >= 3) ? (args->arg(2)->asInt()->evalInt() & 1) : true;
1342    
1343            AbstractEngineChannel* pEngineChannel =
1344                static_cast<AbstractEngineChannel*>(m_vm->m_event->cause.pEngineChannel);
1345    
1346            if (args->arg(0)->exprType() == INT_EXPR) {
1347                const ScriptID id = args->arg(0)->asInt()->evalInt();
1348                if (!id) {
1349                    wrnMsg("fade_out(): note ID for argument 1 may not be zero");
1350                    return successResult();
1351                }
1352                if (!id.isNoteID()) {
1353                    wrnMsg("fade_out(): argument 1 is not a note ID");
1354                    return successResult();
1355                }
1356    
1357                NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1358                if (!pNote) return successResult();
1359    
1360                // if fade_out() was called immediately after note was triggered
1361                // then immediately apply fade out duration to Note object
1362                if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1363                    pNote->Override.VolumeTime = fDuration;
1364                } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1365                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1366                    e.Init(); // clear IDs
1367                    e.Type = Event::type_note_synth_param;
1368                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1369                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1370                    e.Param.NoteSynthParam.Delta    = fDuration;
1371                    e.Param.NoteSynthParam.Relative = false;
1372    
1373                    pEngineChannel->ScheduleEventMicroSec(&e, 0);
1374                }
1375                // now schedule a "volume" change, simply one time slice ahead, with
1376                // the final fade out volume (0.0)
1377                {
1378                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1379                    e.Init(); // clear IDs
1380                    e.Type = Event::type_note_synth_param;
1381                    e.Param.NoteSynthParam.NoteID   = id.noteID();
1382                    e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1383                    e.Param.NoteSynthParam.Delta    = 0.f;
1384                    e.Param.NoteSynthParam.Relative = false;
1385    
1386                    // scheduling with 0 delay would also work here, but +1 is more
1387                    // safe regarding potential future implementation changes of the
1388                    // scheduler (see API comments of RTAVLTree::insert())
1389                    pEngineChannel->ScheduleEventMicroSec(&e, 1);
1390                }
1391                // and finally if stopping the note was requested after the fade out
1392                // completed, then schedule to kill the voice after the requested
1393                // duration
1394                if (stop) {
1395                    Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1396                    e.Init(); // clear IDs
1397                    e.Type = Event::type_kill_note;
1398                    e.Param.Note.ID = id.noteID();
1399                    e.Param.Note.Key = pNote->hostKey;
1400    
1401                    pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1402                }
1403            } else if (args->arg(0)->exprType() == INT_ARR_EXPR) {
1404                VMIntArrayExpr* ids = args->arg(0)->asIntArray();
1405                for (int i = 0; i < ids->arraySize(); ++i) {
1406                    const ScriptID id = ids->evalIntElement(i);
1407                    if (!id || !id.isNoteID()) continue;
1408    
1409                    NoteBase* pNote = pEngineChannel->pEngine->NoteByID( id.noteID() );
1410                    if (!pNote) continue;
1411    
1412                    // if fade_out() was called immediately after note was triggered
1413                    // then immediately apply fade out duration to Note object
1414                    if (m_vm->m_event->cause.SchedTime() == pNote->triggerSchedTime) {
1415                        pNote->Override.VolumeTime = fDuration;
1416                    } else { // otherwise schedule a "volume time" change with the requested fade out duration ...
1417                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1418                        e.Init(); // clear IDs
1419                        e.Type = Event::type_note_synth_param;
1420                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1421                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume_time;
1422                        e.Param.NoteSynthParam.Delta    = fDuration;
1423                        e.Param.NoteSynthParam.Relative = false;
1424    
1425                        pEngineChannel->ScheduleEventMicroSec(&e, 0);
1426                    }
1427                    // now schedule a "volume" change, simply one time slice ahead, with
1428                    // the final fade out volume (0.0)
1429                    {
1430                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1431                        e.Init(); // clear IDs
1432                        e.Type = Event::type_note_synth_param;
1433                        e.Param.NoteSynthParam.NoteID   = id.noteID();
1434                        e.Param.NoteSynthParam.Type     = Event::synth_param_volume;
1435                        e.Param.NoteSynthParam.Delta    = 0.f;
1436                        e.Param.NoteSynthParam.Relative = false;
1437    
1438                        // scheduling with 0 delay would also work here, but +1 is more
1439                        // safe regarding potential future implementation changes of the
1440                        // scheduler (see API comments of RTAVLTree::insert())
1441                        pEngineChannel->ScheduleEventMicroSec(&e, 1);
1442                    }
1443                    // and finally if stopping the note was requested after the fade out
1444                    // completed, then schedule to kill the voice after the requested
1445                    // duration
1446                    if (stop) {
1447                        Event e = m_vm->m_event->cause; // copy to get fragment time for "now"
1448                        e.Init(); // clear IDs
1449                        e.Type = Event::type_kill_note;
1450                        e.Param.Note.ID = id.noteID();
1451                        e.Param.Note.Key = pNote->hostKey;
1452                        
1453                        pEngineChannel->ScheduleEventMicroSec(&e, duration + 1);
1454                    }
1455                }
1456            }
1457    
1458            return successResult();
1459      }      }
1460    
1461      // event_status() function      // event_status() function

Legend:
Removed from v.3187  
changed lines
  Added in v.3188

  ViewVC Help
Powered by ViewVC