def _template_gen(picks, st, length, swin='all', prepick=0.05,
                  all_horiz=False, delayed=True, plot=False, min_snr=None,
                  debug=0):
    """
    Master function to generate a multiplexed template for a single event.

    Function to generate a cut template as :class:`obspy.core.stream.Stream`
    from a given set of picks and data.  Should be given pre-processed
    data (downsampled and filtered).

    :type picks: list
    :param picks: Picks to extract data around, where each pick in the \
        list is an obspy.core.event.origin.Pick object.
    :type st: obspy.core.stream.Stream
    :param st: Stream to extract templates from
    :type length: float
    :param length: Length of template in seconds
    :type swin: str
    :param swin:
        P, S, P_all, S_all or all, defaults to all: see note in
        :func:`eqcorrscan.core.template_gen.template_gen`
    :type prepick: float
    :param prepick:
        Length in seconds to extract before the pick time default is 0.05
        seconds.
    :type all_horiz: bool
    :param all_horiz:
        To use both horizontal channels even if there is only a pick on one
        of them.  Defaults to False.
    :type delayed: bool
    :param delayed:
        If True, each channel will begin relative to it's own pick-time, if
        set to False, each channel will begin at the same time.
    :type plot: bool
    :param plot:
        To plot the template or not, default is False. Plots are saved as
        `template-starttime_template.png` and `template-starttime_noise.png`,
        where `template-starttime` is the start-time of the template
    :type min_snr: float
    :param min_snr:
        Minimum signal-to-noise ratio for a channel to be included in the
        template, where signal-to-noise ratio is calculated as the ratio of
        the maximum amplitude in the template window to the rms amplitude in
        the whole window given.
    :type debug: int
    :param debug: Debug output level from 0-5.

    :returns: Newly cut template.
    :rtype: :class:`obspy.core.stream.Stream`

    .. note:: By convention templates are generated with P-phases on the \
        vertical channel and S-phases on the horizontal channels, normal \
        seismograph naming conventions are assumed, where Z denotes vertical \
        and N, E, R, T, 1 and 2 denote horizontal channels, either oriented \
        or not.  To this end we will **only** use Z channels if they have a \
        P-pick, and will use one or other horizontal channels **only** if \
        there is an S-pick on it.

    .. note:: swin argument: Setting to `P` will return only data for channels
        with P picks, starting at the pick time (minus the prepick).
        Setting to `S` will return only data for channels with
        S picks, starting at the S-pick time (minus the prepick)
        (except if `all_horiz=True` when all horizontal channels will
        be returned if there is an S pick on one of them). Setting to `all`
        will return channels with either a P or S pick (including both
        horizontals if `all_horiz=True`) - with this option vertical channels
        will start at the P-pick (minus the prepick) and horizontal channels
        will start at the S-pick time (minus the prepick).
        `P_all` will return cut traces starting at the P-pick time for all
        channels. `S_all` will return cut traces starting at the S-pick
        time for all channels.

    .. warning:: If there is no phase_hint included in picks, and swin=all, \
        all channels with picks will be used.
    """
    from eqcorrscan.utils.debug_log import debug_print
    from eqcorrscan.utils.plotting import pretty_template_plot as tplot
    from eqcorrscan.utils.plotting import noise_plot
    from eqcorrscan.core.bright_lights import _rms
    picks_copy = copy.deepcopy(picks)  # Work on a copy of the picks and leave
    # the users picks intact.
    if not isinstance(swin, list):
        swin = [swin]
    for _swin in swin:
        assert _swin in ['P', 'all', 'S', 'P_all', 'S_all']
    for pick in picks_copy:
        if not pick.waveform_id:
            debug_print(
                "Pick not associated with waveform, will not use it: "
                "{0}".format(pick), 1, debug)
            picks_copy.remove(pick)
            continue
        if not pick.waveform_id.station_code or not \
                pick.waveform_id.channel_code:
            debug_print(
                "Pick not associated with a channel, will not use it:"
                " {0}".format(pick), 1, debug)
            picks_copy.remove(pick)
            continue
    for tr in st:
        # Check that the data can be represented by float16, and check they
        # are not all zeros
        if np.all(tr.data.astype(np.float16) == 0):
            debug_print("Trace is all zeros at float16 level, either gain or "
                        "check. Not using in template: {0}".format(tr), 4,
                        debug)
            st.remove(tr)
    # Get the earliest pick-time and use that if we are not using delayed.
    picks_copy.sort(key=lambda p: p.time)
    first_pick = picks_copy[0]
    if plot:
        stplot = st.slice(first_pick.time - 20,
                          first_pick.time + length + 90).copy()
        noise = stplot.copy()
    # Work out starttimes
    starttimes = []
    for _swin in swin:
        for tr in st:
            starttime = {'station': tr.stats.station,
                         'channel': tr.stats.channel, 'picks': []}
            station_picks = [pick for pick in picks_copy
                             if pick.waveform_id.station_code ==
                             tr.stats.station]
            if _swin == 'P_all':
                p_pick = [pick for pick in station_picks
                          if pick.phase_hint.upper()[0] == 'P']
                if len(p_pick) == 0:
                    continue
                starttime.update({'picks': p_pick})
            elif _swin == 'S_all':
                s_pick = [pick for pick in station_picks
                          if pick.phase_hint.upper()[0] == 'S']
                if len(s_pick) == 0:
                    continue
                starttime.update({'picks': s_pick})
            elif _swin == 'all':
                if all_horiz and tr.stats.channel[-1] in ['1', '2', '3',
                                                          'N', 'E']:
                    # Get all picks on horizontal channels
                    channel_pick = [
                        pick for pick in station_picks
                        if pick.waveform_id.channel_code[-1] in
                        ['1', '2', '3', 'N', 'E']]
                else:
                    channel_pick = [
                        pick for pick in station_picks
                        if pick.waveform_id.channel_code == tr.stats.channel]
                if len(channel_pick) == 0:
                    continue
                starttime.update({'picks': channel_pick})
            elif _swin == 'P':
                p_pick = [pick for pick in station_picks
                          if pick.phase_hint.upper()[0] == 'P' and
                          pick.waveform_id.channel_code == tr.stats.channel]
                if len(p_pick) == 0:
                    continue
                starttime.update({'picks': p_pick})
            elif _swin == 'S':
                if tr.stats.channel[-1] in ['Z', 'U']:
                    continue
                s_pick = [pick for pick in station_picks
                          if pick.phase_hint.upper()[0] == 'S']
                if not all_horiz:
                    s_pick = [pick for pick in s_pick
                              if pick.waveform_id.channel_code ==
                              tr.stats.channel]
                starttime.update({'picks': s_pick})
                if len(starttime['picks']) == 0:
                    continue
            if not delayed:
                starttime.update({'picks': [first_pick]})
            starttimes.append(starttime)
    # Cut the data
    st1 = Stream()
    for _starttime in starttimes:
        debug_print("Working on channel %s.%s" %
                    (_starttime['station'], _starttime['channel']),
                    debug_level=0, print_level=debug)
        tr = st.select(
            station=_starttime['station'], channel=_starttime['channel'])[0]
        debug_print("Found Trace %s" % tr.__str__(), debug_level=0,
                    print_level=debug)
        noise_amp = _rms(tr.data)
        used_tr = False
        for pick in _starttime['picks']:
            if not pick.phase_hint:
                warnings.warn(
                    "Pick for {0}.{1} has no phase hint given, you should not "
                    "use this template for cross-correlation"
                    " re-picking!".format(
                        pick.waveform_id.station_code,
                        pick.waveform_id.channel_code))
            starttime = pick.time - prepick
            debug_print(
                "Cutting " + tr.stats.station + '.' + tr.stats.channel, 0,
                debug)
            tr_cut = tr.slice(
                starttime=starttime, endtime=starttime + length,
                nearest_sample=False).copy()
            if plot:
                noise.select(
                    station=_starttime['station'],
                    channel=_starttime['channel']).trim(
                        noise[0].stats.starttime, starttime)
            if len(tr_cut.data) == 0:
                debug_print(
                    "No data provided for {0}.{1} starting at {2}".format(
                        tr.stats.station, tr.stats.channel, starttime), 3,
                    debug)
                continue
            # Ensure that the template is the correct length
            if len(tr_cut.data) == (tr_cut.stats.sampling_rate *
                                    length) + 1:
                tr_cut.data = tr_cut.data[0:-1]
            debug_print(
                'Cut starttime = %s\nCut endtime %s' %
                (str(tr_cut.stats.starttime), str(tr_cut.stats.endtime)), 0,
                debug)
            if min_snr is not None and \
               max(tr_cut.data) / noise_amp < min_snr:
                debug_print(
                    "Signal-to-noise ratio below threshold for {0}.{1}".format(
                        tr_cut.stats.station, tr_cut.stats.channel), 3, debug)
                continue
            st1 += tr_cut
            used_tr = True
        if not used_tr:
            debug_print('No pick for ' + tr.stats.station + '.' +
                        tr.stats.channel, 0, debug)
    if plot:
        fig1 = tplot(st1, background=stplot, picks=picks_copy,
                     title='Template for ' + str(st1[0].stats.starttime),
                     show=False, return_figure=True)
        fig2 = noise_plot(
            signal=st1, noise=noise, show=False, return_figure=True)
        fig1.savefig("{0}_template.png".format(st1[0].stats.starttime))
        fig2.savefig("{0}_noise.png".format(st1[0].stats.starttime))
        del(stplot, fig1, fig2)
    return st1
Master function to generate a multiplexed template for a single event.

    Function to generate a cut template as :class:`obspy.core.stream.Stream`
    from a given set of picks and data.  Should be given pre-processed
    data (downsampled and filtered).

    :type picks: list
    :param picks: Picks to extract data around, where each pick in the \
        list is an obspy.core.event.origin.Pick object.
    :type st: obspy.core.stream.Stream
    :param st: Stream to extract templates from
    :type length: float
    :param length: Length of template in seconds
    :type swin: str
    :param swin:
        P, S, P_all, S_all or all, defaults to all: see note in
        :func:`eqcorrscan.core.template_gen.template_gen`
    :type prepick: float
    :param prepick:
        Length in seconds to extract before the pick time default is 0.05
        seconds.
    :type all_horiz: bool
    :param all_horiz:
        To use both horizontal channels even if there is only a pick on one
        of them.  Defaults to False.
    :type delayed: bool
    :param delayed:
        If True, each channel will begin relative to it's own pick-time, if
        set to False, each channel will begin at the same time.
    :type plot: bool
    :param plot:
        To plot the template or not, default is False. Plots are saved as
        `template-starttime_template.png` and `template-starttime_noise.png`,
        where `template-starttime` is the start-time of the template
    :type min_snr: float
    :param min_snr:
        Minimum signal-to-noise ratio for a channel to be included in the
        template, where signal-to-noise ratio is calculated as the ratio of
        the maximum amplitude in the template window to the rms amplitude in
        the whole window given.
    :type debug: int
    :param debug: Debug output level from 0-5.

    :returns: Newly cut template.
    :rtype: :class:`obspy.core.stream.Stream`

    .. note:: By convention templates are generated with P-phases on the \
        vertical channel and S-phases on the horizontal channels, normal \
        seismograph naming conventions are assumed, where Z denotes vertical \
        and N, E, R, T, 1 and 2 denote horizontal channels, either oriented \
        or not.  To this end we will **only** use Z channels if they have a \
        P-pick, and will use one or other horizontal channels **only** if \
        there is an S-pick on it.

    .. note:: swin argument: Setting to `P` will return only data for channels
        with P picks, starting at the pick time (minus the prepick).
        Setting to `S` will return only data for channels with
        S picks, starting at the S-pick time (minus the prepick)
        (except if `all_horiz=True` when all horizontal channels will
        be returned if there is an S pick on one of them). Setting to `all`
        will return channels with either a P or S pick (including both
        horizontals if `all_horiz=True`) - with this option vertical channels
        will start at the P-pick (minus the prepick) and horizontal channels
        will start at the S-pick time (minus the prepick).
        `P_all` will return cut traces starting at the P-pick time for all
        channels. `S_all` will return cut traces starting at the S-pick
        time for all channels.

    .. warning:: If there is no phase_hint included in picks, and swin=all, \
        all channels with picks will be used.