def _fade(self, volume, duration):
        start_volume = itunes.volume_get()
        end_volume   = normalize_volume(volume)
        step_count   = abs(end_volume - start_volume)
        duration     = float(duration)

        if step_count == 0:
            # no volume change
            self.logger.debug(u'no fade: no volume change')
        elif duration <= MIN_STEP_TIME:
            # instant volume change
            self.logger.debug(u'no fade: instant volume change')
            itunes.volume_set(end_volume)
        else:
            # actual fade

            # calculate fade steps
            step_size = up_down = [-1,1][end_volume >= start_volume]
            step_wait = duration/step_count
            while step_wait < MIN_STEP_TIME:
                # ensure minimumAllowed time between steps
                step_size += up_down
                step_count = int(abs((end_volume - start_volume)/step_size))
                step_wait = duration/step_count
            final_step = (start_volume + step_count * step_size != end_volume)

            # calculate loop timing
            loop_wait = step_wait
            while loop_wait > MAX_LOOP_TIME:
                # loop on multiple of step_wait
                loop_wait = loop_wait / 2

            # do the fade
            self.stopped = False
            self.logger.debug(u'start fade: v{}/{:.4f}s ({}/{:+}/{:.4f})'.format(end_volume,duration,step_count,step_size,step_wait))

            time_start = time.time()
            next_step = time_start + step_wait
            volume = start_volume
            count = 0
            while not self.stopped:
                loop_time = time.time()
                if loop_time >= next_step:
                    volume += step_size
                    count += 1
                    itunes.volume_set(volume)
                    next_step += step_wait
                    if count == step_count:
                        break
                time.sleep(max(0, loop_wait - (time.time() - loop_time)))
            if not self.stopped:
                if final_step:
                    volume = end_volume
                    itunes.volume_set(volume)
                self.logger.debug(u'end fade:   v{}/{:.4f}s ({}/{:+}/{:.4f})'.format(volume,time.time()-time_start,count,step_size,step_wait))
            else:
                self.logger.debug(u'stop fade:  v{}/{:.4f}s ({}/{:+}/{:.4f})'.format(volume,time.time()-time_start,count,step_size,step_wait))
 def _setVolume(self,value):
     value = int(normalize_volume(value)/100.0 * itunes.volume_get())
     self.logger.debug(u'set airplay "{}" volume: {}'.format(self.name,value))
     itunes.airplay_device_volume_set(self.name, value)
 def _volume_get(self):
     value = itunes.volume_get()
     self.logger.debug(u'get volume: {}'.format(value))
     return value