def generate_trial_waveform(self):
        # Use BBN
        seed = self.get_current_value("seed")
        depth = self.get_current_value("modulation_depth")
        direction = self.get_current_value("modulation_direction")
        fm = self.get_current_value("fm")
        duration = self.get_current_value("trial_duration")

        t = signal.time(self.iface_behavior.fs, duration)

        # Save the actual seed that's used to generate the trial waveform
        if seed == -1:
            seed = int(time.time())
            self.set_current_value("seed", seed)

        # Do not use the self.random_generator for generating the seed.  This
        # generator is for use only for generating the intertrial waveform.
        state = np.random.RandomState(seed)
        waveform = state.uniform(low=-1, high=1, size=len(t))

        # Since we are drawing samples from a uniform distribution and we wish
        # to normalize for the RMS voltage, we need to divide by 0.5 which is
        # the RMS value of the waveform.  We could recompute the RMS value on
        # each cycle; however, I think it's better to use the same value each
        # time.  The RMS of the waveform over time will compute to 0.5 (because
        # the samples are unformly distributed between -1 and 1).
        waveform = waveform / 0.5
        eq_phase = signal.sam_eq_phase(depth, direction)
        eq_power = signal.sam_eq_power(depth)
        envelope = depth / 2.0 * np.cos(2 * np.pi * fm * t + eq_phase) + 1 - depth / 2.0
        envelope *= 1 / eq_power
        return waveform * envelope
    def _get_sam_envelope(self):
        # This part is not as CPU-intensive, but serves a good example of how to
        # cache the pieces for generating the final token.
        if not self._sam_envelope_valid:
            log.debug('recomputing sam envelope')
            fm = self.get_current_value('fm')
            depth = self.get_current_value('modulation_depth')
            delay = self.get_current_value('modulation_onset')
            direction = self.get_current_value('modulation_direction')
            t = self._get_time()

            if delay == 0: 
                eq_phase = -np.pi
            else:
                eq_phase = wave.sam_eq_phase(depth, direction)

            envelope = depth/2*np.cos(2*np.pi*fm*t+eq_phase)+1-depth/2

            # Ensure that we scale the waveform so that the total power remains
            # equal to that of an unmodulated token.
            envelope *= 1.0/wave.sam_eq_power(depth)

            delay_n = int(delay*self.iface_behavior.fs)
            if delay_n > 0:
                delay_envelope = np.ones(delay_n)
                envelope = np.concatenate((delay_envelope, envelope[:-delay_n]))

            self._sam_envelope = envelope
            self._sam_envelope_valid = True

        return self._sam_envelope
    def _update_am(self):
        am_direction = self.get_current_value('am_direction')
        am_depth = self.get_current_value('am_depth')

        am_amplitude = am_depth/2.0
        am_shift = 1-am_amplitude
        am_phase = sam_eq_phase(am_depth, am_direction)
        am_sf = 1.0/sam_eq_power(am_depth)

        self.iface_behavior.set_tag('am_amplitude', am_amplitude)
        self.iface_behavior.set_tag('am_shift', am_shift)
        self.iface_behavior.set_tag('am_phase', am_phase)
        self.iface_behavior.set_tag('am_sf', am_sf)