HOLD repeat times
rfayer1
Junior Member
Can anyone tell me how you set/adjust the rate at which a command is repeatedly sent from the HOLD action of a button event?
Comments
-
Look in the NetLinx Keywords help file. Go to index and search for "Button Events". This will show you the format of HOLD portion. Just remember that holds are based on tenths of seconds, so 5 is really .5 seconds, or one half second. 10 is 1 second.
Jeff -
Thanks. This is the 'Time' parameter of the 'Hold' action. Is there a way to make it so that the time the code in the 'Hold' section is repeatedly sent is different from the time that the button must be held for the repeated command to be executed?
-
Thanks. This is the 'Time' parameter of the 'Hold' action. Is there a way to make it so that the time the code in the 'Hold' section is repeatedly sent is different from the time that the button must be held for the repeated command to be executed?
Not directly. If you really need that, launch a repeating timeline on the hold, and kill it on the release. -
Thanks. This is the 'Time' parameter of the 'Hold' action. Is there a way to make it so that the time the code in the 'Hold' section is repeatedly sent is different from the time that the button must be held for the repeated command to be executed?
Yes. You can specify a 2 second hold time and then if it gets in the hold, turn on a variable that exists in a different hold event with a different repeat time. That should allow you to hold the button for 2 seconds and then repeat events at a different interval in the other hold event with the same channel number. Turn off the variable on the release.button_event[dvTP, 100] { push: { } hold[20] { hold = 1 } release: { hold = 0 } } button_event[dvTP, 100] { hold[5, repeat] { if (hold) callFunctionEveryHalfSecond() } }Paul -
You could also just use the fastest repeat time that you would need and then use button.holdtime % x == 0 to get other hold times with in the same event handler. button.holdtime gives you the current holdtime in milliseconds.
button_event[dvTP, 100] { push: { } hold[2,REPEAT] { //CODE HERE REPEATES EVERY 2/10th of a second //OR SELECT { ACTIVE(button.holdtime % 200 == 0)://do something every 2/10th of a second { //DO SOMETHING } ACTIVE(button.holdtime % 300 == 0)://do something every 3/10th of a second { //DO SOMETHING } ACTIVE(button.holdtime % 400 == 0)://do something every 4/10th of a second { //DO SOMETHING } //SO ON ACTIVE(button.holdtime % 1000 == 0)://do something every 1 second { //DO SOMETHING } ACTIVE(button.holdtime % 2000 == 0)://do something every 2 seconds { //DO SOMETHING } } } }I think this code is correct. Actually you would either need to change the repeat time to 1 or make sure every select active is a multiple of the repeat time used. -
DHawthorne wrote: »Not directly. If you really need that, launch a repeating timeline on the hold, and kill it on the release.
I prefer this way since HOLDs don't play nicely with arrays (can't fully rely on a GET_LAST in a HOLD.) -
I think this code is correct. Actually you would either need to change the repeat time to 1 or make sure every select active is a multiple of the repeat time used.
Will that work? Is holdtime guaranteed to be a perfect multiple of the repeat?
Paul -
a_riot42 wrote:
No, in the example I posted the repeat time was 2 (2/10th) so in this case the ACTIVE statement for 300ms could never be true:Will that work? Is holdtime guaranteed to be a perfect multiple of the repeat?ACTIVE(button.holdtime % 300 == 0)://do something every 3/10th of a second { //DO SOMETHING }Although the holdtime is in milliseconds the code would only repeat every 200 milliseconds so only multiples of 200 milliseconds could ever be evaluated as true.
jjames wrote:
True, I don't like anything that evalutes the button or TP in the HOLD event either for that reason and usually set the hold button and the TP that iniated it in the push event like:I prefer this way since HOLDs don't play nicely with arrays (can't fully rely on a GET_LAST in a HOLD.){ PUSH: { STACK_VAR INTEGER nTEBtn ; STACK_VAR INTEGER nTETPIndx ; STACK_VAR INTEGER nTEBtnBIC ; nTEBtnBIC = BUTTON.INPUT.CHANNEL ; nTEBtn = GET_LAST(nETableBtnArry) ; nTETPIndx = GET_LAST(dvEventTableTPArry) ; fnTableEventDeBug("'PUSH: TP-',itoa(nTETPIndx),', tp/array, GET_LAST btn-',itoa(nTEBtn),', BIC btn-',itoa(nTEBtnBIC),'. >-line-<',ITOA(__LINE__),'>'") ; if(!nTEHolding) { nTECurHoldBtn = nTEBtnBIC ; nTECurHoldTPIndx = nTETPIndx ; } } HOLD [10,REPEAT]: { nTEHolding = 1 ; fnTableEventDeBug("'HOLD: TP-',itoa(nTECurHoldTPIndx),', tp/array, HOLD btn-',itoa(nTECurHoldBtn),'. >-line-<',ITOA(__LINE__),'>'") ; } RELEASE: { STACK_VAR INTEGER nTEBtn ; STACK_VAR INTEGER nTETPIndx ; STACK_VAR INTEGER nTEBtnBIC ; nTEBtnBIC = BUTTON.INPUT.CHANNEL ; nTEBtn = GET_LAST(nETableBtnArry) ; nTETPIndx = GET_LAST(dvEventTableTPArry) ; fnTableEventDeBug("'RELEASE: TP-',itoa(nTETPIndx),', tp/array, GET_LAST btn-',itoa(nTEBtn),', BIC btn-',itoa(nTEBtnBIC),'. >-line-<',ITOA(__LINE__),'>'") ; if(nTEHolding && nTETPIndx == nTECurHoldTPIndx && nTEBtnBIC == nTECurHoldBtn) { nTEHolding = 0 ; fnTableEventDeBug("'RELEASE: TP-',itoa(nTETPIndx),', Released Hold! >-line-<',ITOA(__LINE__),'>'") ; } } }I then release it in the release event handler if everything matches.
If you're concerned about mutliple TPs you could create an array the size of the number of TPs and store the hold button for each TP in that. -
II almost never use a HOLD event though, because of the limitation that we're talking about now. I'm curious though, would you put in multiple BUTTON_EVENTs to populate the array? GET_LAST isn't a very reliable option.If you're concerned about mutliple TPs you could create an array the size of the number of TPs and store the hold button for each TP in that.
I'll do one of two things to replace a HOLD event, and of course works fine.
If I need to do something once (eq. to HOLD[20]), I'd do something like this:BUTTON_EVENT[dv_TP,nPRESET_SAVE] { PUSH: { LOCAL_VAR INTEGER nPNL; LOCAL_VAR INTEGER nIND; // Make sure no other panel can initiate this sequence IF(!nCAM_PRESET_LOCK) { ON[nCAM_PRESET_LOCK] WAIT 30 'CAMERA PRESET' { // Okay, now we can save the data ON[nCAM_PRESET_SAVE] // Allow saves & recalls to occur again WAIT 20 OFF[nCAM_PRESET_SAVE] } } } RELEASE: { // Cancel the wait CANCEL_WAIT 'CAMERA PRESET' OFF[nCAM_PRESET_LOCK] // We're not in the middle of saving IF(!nCAM_PRESET_SAVE) { // Go ahead and recall your preset } } }Now the above code is good for multiple panels to save a certain type of preset that all the panels share. If you wanted each panel to have it's own set of presets, you'd simply create the array, and have multiple CANCEL_WAITs & WAITs within a SWITCH/CASE or SELECT/ACTIVE situation.
For a repeating hold (eq. to HOLD[2,REPEAT]), I use Brian Clement's ramping volume timeline code, which essentially starts a TIMELINE with the PUSH and kills it on the RELEASE (as Dave had mentioned earlier.)
sn't it funny how the HOLD event almost always stirs up some kind of in-depth conversation where everyone jumps in? It seems the HOLD event itself isn't as perfect or easy as the PUSH & RELEASE. -
a_riot42 wrote:
No, in the example I posted the repeat time was 2 (2/10th) so in this case the ACTIVE statement for 300ms could never be true:ACTIVE(button.holdtime % 300 == 0)://do something every 3/10th of a second { //DO SOMETHING }Although the holdtime is in milliseconds the code would only repeat every 200 milliseconds so only multiples of 200 milliseconds could ever be evaluated as true.
I think the question is about the precision of the timing. When your hold time is .2 seconds, is it guaranteed that the timing will be precise to the ms? Perhaps that .2 second hold occurs at 201ms instead of 200 ms. Might be better to check for closeness rather than equality -- sort of like with a floating point number. (Never check to see if a floating point number is FALSE) -
Hedberg wrote:
So you remember the problems I have with floating point numbers. Actually you're correct I would probably opt to do a greater than less than kind of a thing.Might be better to check for closeness rather than equality -- sort of like with a floating point number.(button.holdtime > 199 && button.holdtime < 399)
I don't really use holds that often and I don't think I have any holds using button.holdtime but it is an option and If one was to use it they would have to play with it and see if there are quirks.
jjames wrote:
What do you mean? I know of no reliability problems using GET_LAST during a triggered event. I don't really consider HOLD a triggered event since its really just the result of a push that hasn't been released. So if you use GET_LAST on the trigger events (PUSH, RELEASE) I see no problems.I'm curious though, would you put in multiple BUTTON_EVENTs to populate the array? GET_LAST isn't a very reliable option. -
Yeah, I guess you could do that. I see the HOLD event (non-repeating) as a WAIT. And if you have a GET_LAST within the WAIT, another panel could potentially come in and change its value.What do you mean? I know of no reliability problems using GET_LAST during a triggered event. I don't really consider HOLD a triggered event since its really just the result of a push that hasn't been released. So if you use GET_LAST on the trigger events (PUSH, RELEASE) I see no problems.
I just think there's too much hassle with the HOLD event. I don't want to have to manage variables and in the PRESS and RELEASE in order for something in the HOLD to work properly; I'd much rather remove the HOLD and just do it all in the PRESS/RELEASE. Just a difference in personal style I guess if it works your way as well.
Not enough cats for all the different ways to skin 'em. -
I don't really use holds that often and I don't think I have any holds using button.holdtime but it is an option and If one was to use it they would have to play with it and see if there are quirks.
I like holds for ramping volume on things like ClearOne mixers. Easy to understand, easy to implement, and it works well. Clearly, there are other methods. -
I just think there's too much hassle with the HOLD event.
I haven't found holds to be a hassle at all, so I guess I don't know what you mean. get_last has always worked perfectly reliably for me. You just have to be aware that it is a global function so that any touch panel will change its value but it will always return the correct value. If you need to store that info for hold events, then that is something you can do easily enough.
Paul -
Gold Star on your test paper, jj.
This is one topic that cannot be argued without showing examples how to get unintended results from your running system. Of course two users would never be operating two different touch panels at the same time, right.... I see the HOLD event (non-repeating) as a WAIT. And if you have a GET_LAST within the WAIT, another panel could potentially come in and change its value.
If there is only one touch panel in a system, HOLDs work great. If you use multiple panels with device arrays then there is always the potential for problems using the GET_LAST() function within a HOLD...not a recommended coding practice. This is a difficult concept to grasp until you get burnt. -
Indeed, I've been burnt once fairly bad when I had to go back and change it all for a system of 18 panels, not fun and rather time consuming. I've never made that mistake again.B_Clements wrote: »This is a difficult concept to grasp until you get burnt.
-
B_Clements wrote:
Of all the posts in this thread there hasn't been anyone saying anything to the contrary to the above statement.If you use multiple panels with device arrays then there is always the potential for problems using the GET_LAST() function within a HOLD...not a recommend coding practice.
What's been said is that GET_LAST() should be used only in the PUSH or RELEASE and if you want or need to implement a HOLD the button value should be obtained from the PUSH and held in a safe place for that purpose. It's been suggested that a TIMELINE should be used but that has the same potential for erroneous results as does using the HOLD event since they both depend on the method you use to obtain the value for the button you wish to HOLD and or REPEAT. If done wrong either method's value can change by other events or user pushes. That's true even if you don't use a HOLD event handler and trigger your home brewed HOLD by a starting a timeline on a PUSH and killing upon receipt of a release.
Either method is as reliable as the other if done right but the HOLD is by far the easiest to implement. -
I've never run into a problem using a TIMELINE when created and killed in the PUSH & RELEASE events respectively. In the TIMELINE example you'd have one TIMELINE event per panel or zone or however you divvy it up and then just stack them.It's been suggested that a TIMELINE should be used but that has the same potential for erroneous results as does using the HOLD event since they both depend on the method you use to obtain the value for the button you wish to HOLD and or REPEAT.
Here's an example of getting burn with a HOLD event. This was part of a transport key section, to continuous send a FFWD or REW command to a DVD player or CD player. The problem came along when one panel was using the DVD player and another panel wanted to do the same thing with the CD player. (This is just the HOLD portion of the entire BUTTON_EVENT.)BUTTON_EVENT[dv_TP,TRANSPORT_BTNS] { HOLD[2,REPEAT]: { LOCAL_VAR INTEGER nPANEL LOCAL_VAR INTEGER nBTN nBTN = BUTTON.INPUT.CHANNEL nPANEL = GET_LAST(dv_TP) IF(nBTN = 4 OR nBTN = 5) { SWITCH(nCUR_SRC[nPANEL]) { // MANAGE IR DEVICES CASE SRC_DVD: // DVD COMMANDS SEND_STRING PANEL[nPANEL].DVD_PLAYER,"DVD_COMMANDS[nBTN],CR" CASE SRC_CDC: // CD PLAYER SEND_STRING dvCDC_HRT,"CD_COMMANDS[nBTN],CR" } } } }
The fix (back then with limited programming experience) was to create a BUTTON_EVENT for each panel for those two buttons. Having to do it 18 times wasn't fun.BUTTON_EVENT[dv_TP[02],4] BUTTON_EVENT[dv_TP[02],5] { HOLD[2,REPEAT]: { STACK_VAR INTEGER nBTN nBTN = BUTTON.INPUT.CHANNEL // ONLY FOR FAST FORWARD AND REWIND OF DVD & CD PLAYER IF(nBTN = 4 OR nBTN = 5) { SWITCH(nCUR_SRC[2][nCUR_SEL[2]]) { // MANAGE IR DEVICES CASE SRC_DVD: // DVD COMMANDS SEND_STRING PANEL[2].DVD_PLAYER,"DVD_COMMANDS[nBTN],CR" CASE SRC_CDC: // CD PLAYER SEND_STRING dvCDC_HRT,"CD_COMMANDS[nBTN],CR" } } } } -
jjames wrote:
That's good and probably means you're obtaining and storing your button number that the timeline events use correctly otherwise there would be the same potential of having that button number altered by other system events or user pushes. That should also mean that the same method you use to store your button number for the timeline could be used in a hold event handler with equal success.I've never run into a problem using a TIMELINE when created and killed in the PUSH & RELEASE events respectively. In the TIMELINE example you'd have one TIMELINE event per panel or zone or however you divvy it up and then just stack them.
As long as you're not obtaining your button number through a GET_LAST in a hold event there's really no difference. If you think about it you could use a GET_LAST in your timeline event and that would have the same potential for errors as a GET_LAST in a hold event handler.
So if you only use GET_LAST in the PUSH or RELEASE and then properly store the button number in a safe place either method will perform the same end result with equal reliability whether you use the HOLD or TIMELINE method. They're both good, the HOLD is just easier. -
Here's a simple example that uses an array to store button values for use in the hold event handler. Typically I've only created a single variable for use with holds since I don't feel it's too likely that multiple users will be on different TPs at the same time trying to initiate a hold on the same device. Is it possible, yes. is it likely, no. This code will work should it happen.
In this example every TP could initiate a HOLD on this device at the same time but unless it's something like an autopatch switcher and every user is on a seperate zone you going to have other problems to worry about.BUTTON_EVENT[dvTPArry,nBtnArry] { PUSH: { STACK_VAR INTEGER nGLBtn ; STACK_VAR INTEGER nTPIndx ; nGLBtn = GET_LAST(nBtnArry) ; nTPIndx = GET_LAST(dvTPArry) ; if(nGLBtn == VOL_UP || nGLBtn == VOL_DN) {//pre-qualify if you want so you only set buttons which need a hold. //This makes the hold event run quicker by not running the Switch for buttons other than vol up or down should they be held. nTPHoldArry[nTPIndx] = nGLBtn ; } } HOLD [2,REPEAT]: { STACK_VAR INTEGER i ; for(i = 1 ; i <= NUM_TPs ; i ++) { if(nTPHoldArry[i]) { SWITCH(nTPHoldArry[i]) { CASE VOL_UP: { //do something cuz dvTPArry[i] is holding the vol up button. } CASE VOL_DN: { //do something cuz dvTPArry[i] is holding the vol dn button. } } } } } RELEASE: { STACK_VAR INTEGER nGLBtn ; STACK_VAR INTEGER nTPIndx ; nGLBtn = GET_LAST(nBtnArry) ; nTPIndx = GET_LAST(dvTPArry) ; //pre-qualify if you want so you only release buttons which which may have been used in a hold. //nothing gained here by pre-qualifying so just set to 0. nTPHoldArry[nTPIndx] = 0 ; } } -
I didn't like the having to run the loop in the hold event even when the held button wasn't to be used as a HOLD button so I added a variable to flag and count current active holds. This code is a little more effiecient if you go the array method to store current hold buttons.
BUTTON_EVENT[dvTPArry,nBtnArry] { PUSH: { STACK_VAR INTEGER nGLBtn ; STACK_VAR INTEGER nTPIndx ; nGLBtn = GET_LAST(nBtnArry) ; nTPIndx = GET_LAST(dvTPArry) ; if(nGLBtn == VOL_UP || nGLBtn == VOL_DN) {//pre-qualify if you want so you only set buttons which need a hold. //This makes the hold event run quicker by not running the Switch for buttons other than vol up or down. nTPHoldArry[nTPIndx] = nGLBtn ; nHoldActive ++ ;//this makes the hold run faster by not doing anything when the held button isn't used for a hold. } } HOLD [2,REPEAT]: { if(nHoldActive) { STACK_VAR INTEGER i ; nHoldActive = 0 ;//reset for recount, validate the current count. for(i = 1 ; i <= NUM_TPs ; i ++) { if(nTPHoldArry[i]) { nHoldActive ++ ;//recount current holds, ensures an accurate count. SWITCH(nTPHoldArry[i]) { CASE VOL_UP: { //do something cuz dvTPArry[i] is holding the vol up button. } CASE VOL_DN: { //do something cuz dvTPArry[i] is holding the vol dn button. } } } } } } RELEASE: { STACK_VAR INTEGER nGLBtn ; STACK_VAR INTEGER nTPIndx ; nGLBtn = GET_LAST(nBtnArry) ; nTPIndx = GET_LAST(dvTPArry) ; //if you pre-qualify in the push you must also pre-qualify in the release since we're using nHoldActive to enable hold events. if(nGLBtn == VOL_UP || nGLBtn == VOL_DN) { nTPHoldArry[nTPIndx] = 0 ; nHoldActive -- ; } } } -
Example code using timelines
Here is a code block that I put together that controls volume in multiple zones from multiple panels using TIMELINES instead of HOLD. It is completely scalable for as many panels and as many audio zones that you need.
The same concept can be applied to other repeating type Button Events.
Enjoy!
PROGRAM_NAME='Timeline Example, Rev 0-0 by BC' DEFINE_DEVICE // Touch panels - add as many as required dvTP1 = 10001:01:0 // Master Bedroom 1 dvTP2 = 10002:01:0 // Master Bedroom 2 dvTP3 = 10003:01:0 // Kitchen 1 dvTP4 = 10004:01:0 // Kitchen 2 dvTP5 = 10005:01:0 // Family Room DEFINE_CONSTANT INTEGER nMinVolumeLevel = 0 // Minimum volume range INTEGER nMaxVolumeLevel = 40 // Maximum volume range INTEGER nTotalVolumeZones = 3 // Adjust to the number of volume zones in the system // Timeline values LONG nVolumeRampDelay[] = {500} // 1/2 second LONG nVolumeRampRepeat[] = {100} // 1/10 second // Volume Buttons INTEGER nVolumeButtons[] = { 24, // 1 - Volume Up 25, // 2 - Volume Down 26 // 3 - Volume Mute Toggle } // Volume zone map - maps volume zone to touch panel // Mapping allows multiple touch panels to control the same volume zone. INTEGER nVolumeZoneMap[] = { 1, // Master Bedroom 1 1, // Master Bedroom 2 2, // Kitchen 1 2, // Kitchen 2 3 // Family Room } DEFINE_TYPE STRUCTURE VolStruct { INTEGER nVolRampState // Tracks volume ramping state INTEGER nVolMute // Tracks volume mute state INTEGER nVolLevel // Tracks volume level INTEGER nVolChange // Tracks which panel is controlling volume zone } DEFINE_VARIABLE INTEGER nLoop VOLATILE VolStruct Volume[nTotalVolumeZones] // Array of all Touch Panels DEV dv_TP[] = { dvTP1, // Master Bedroom 1 dvTP2, // Master Bedroom 2 dvTP3, // Kitchen 1 dvTP4, // Kitchen 2 dvTP5 // Family Room } DEFINE_CALL 'ADJUST VOLUME' (INTEGER nZone, INTEGER nState) { SWITCH(nState) { CASE 1: // Ramp Up { IF (Volume[nZone].nVolLevel < nMaxVolumeLevel) { Volume[nZone].nVolLevel++ // VOLUME LEVEL - VOLUME DEVICE CODE HERE } } CASE 2: // Ramp Down { IF (Volume[nZone].nVolLevel > nMinVolumeLevel) { Volume[nZone].nVolLevel-- // VOLUME LEVEL - VOLUME DEVICE CODE HERE } } CASE 3: // Toggle Mute { IF (Volume[nZone].nVolMute) // Mute { // VOLUME MUTE - VOLUME DEVICE CODE HERE } ELSE // Unmute { // VOLUME UNMUTE - VOLUME DEVICE CODE HERE } } } } DEFINE_CALL 'VOLUME FEEDBACK' (INTEGER nPanel) { STACK_VAR INTEGER nVolumeZone // Determine which volume zone status based on which touch panel nVolumeZone = nVolumeZoneMap[nPanel] [dv_TP[nPanel],nVolumeButtons[1]] = (Volume[nVolumeZone].nVolRampState=1) // Volume Up Button [dv_TP[nPanel],nVolumeButtons[2]] = (Volume[nVolumeZone].nVolRampState=2) // Volume Down Button [dv_TP[nPanel],nVolumeButtons[3]] = (Volume[nVolumeZone].nVolMute) // Volume Mute Button // Assumes touch panel bargragh matches volume level range. SEND_LEVEL dv_TP[nPanel],1,Volume[nVolumeZone].nVolLevel // Volume Bargraph } DEFINE_EVENT // Button Events - Volume Control // This set of Timelines will delay the start of the volume ramping and then // will repeat volume ramping until a volume button is released. // Add as many timeslines as there are volume zones. TIMELINE_EVENT[1] TIMELINE_EVENT[2] TIMELINE_EVENT[3] { // Restart same timelime with repeat time instead of delay time TIMELINE_CREATE(TIMELINE.ID,nVolumeRampRepeat,1,TIMELINE_RELATIVE,TIMELINE_REPEAT) // Call volume control subroutine CALL 'ADJUST VOLUME' (TIMELINE.ID,Volume[TIMELINE.ID].nVolRampState) } BUTTON_EVENT[dv_TP,nVolumeButtons] // VOLUME CONTROL { PUSH: { STACK_VAR INTEGER nVolumeZone // Determine which volume zone to control based on which touch panel is pressed nVolumeZone = nVolumeZoneMap[GET_LAST(dv_TP)] // Make sure another panel is not adjusting volume in the same zone IF (!Volume[nVolumeZone].nVolChange) { // Set flag for tracking which panel is controlling the volume zone Volume[nVolumeZone].nVolChange = GET_LAST(dv_TP) // Set volume ramp flag indexed to the button array - 1 Up, 2 Down, 3 Mute Volume[nVolumeZone].nVolRampState = GET_LAST(nVolumeButtons) // Not the mute button IF (BUTTON.INPUT.CHANNEL <> nVolumeButtons[3]) { IF (Volume[nVolumeZone].nVolMute) // If volume is muted - unmute { Volume[nVolumeZone].nVolMute = 0 // Reset mute flag // Call volume control subroutine CALL 'ADJUST VOLUME' (nVolumeZone,Volume[nVolumeZone].nVolRampState) } ELSE // Not muted - Adjust volume one unit { // Call volume control subroutine CALL 'ADJUST VOLUME' (nVolumeZone,Volume[nVolumeZone].nVolRampState) } // Start delay before continuing to ramp - // The delay allows a single button tap to increment/decrement one volume unit // before ramping continues. TIMELINE_CREATE(nVolumeZone,nVolumeRampDelay,1,TIMELINE_RELATIVE,TIMELINE_ONCE) } // Mute button ELSE { Volume[nVolumeZone].nVolMute = !Volume[nVolumeZone].nVolMute // Toggle mute flag // Call volume control subroutine CALL 'ADJUST VOLUME' (nVolumeZone,Volume[nVolumeZone].nVolRampState) } } } RELEASE: { STACK_VAR INTEGER nVolumeZone // Determine which volume zone to control based on which touch panel is released nVolumeZone = nVolumeZoneMap[GET_LAST(dv_TP)] // Make sure the releasing panel is the same panel that pushed IF (Volume[nVolumeZone].nVolChange == GET_LAST(dv_TP)) { Volume[nVolumeZone].nVolChange = 0 // Reset panel tracking flag Volume[nVolumeZone].nVolRampState = 0 // Reset volume ramp flag IF (TIMELINE_ACTIVE(nVolumeZone)) // Cancel repeating timeline for ramping volume TIMELINE_KILL(nVolumeZone) } } } DEFINE_PROGRAM // Feedback for Volume Buttons and Bargraph on All Touch Panels nLoop++ IF (nLoop>LENGTH_ARRAY(dv_TP)) nLoop = 1 CALL 'VOLUME FEEDBACK' (nLoop)
Categories
- All Categories
- 2.5K AMX General Discussion
- 922 AMX Technical Discussion
- 514 AMX Hardware
- 502 AMX Control Products
- 3 AMX Video Distribution Products
- 9 AMX Networked AV (SVSI) Products
- AMX Workspace & Collaboration Products
- 3.4K AMX Software
- 151 AMX Resource Management Suite Software
- 386 AMX Design Tools
- 2.4K NetLinx Studio
- 135 Duet/Cafe Duet
- 248 NetLinx Modules & Duet Modules
- 57 AMX RPM Forum
- 228 MODPEDIA - The Public Repository of Modules for Everyone
- 943 AMX Specialty Forums
- 2.6K AMXForums Archive
- 2.6K AMXForums Archive Threads
- 1.5K AMX Hardware
- 432 AMX Applications and Solutions
- 249 Residential Forum
- 182 Tips and Tricks
- 146 AMX Website/Forums
