<SYSTEM_TASK:>
Solve the following problem using Python, implementing the functions described below, one line at a time
<END_TASK>
<USER_TASK:>
Description:
 def play_song(self, song, tempo=120, delay=0.05):
 """ Plays a song provided as a list of tuples containing the note name and its value using music conventional notation instead of numerical values for frequency and duration. It supports symbolic notes (e.g. ``A4``, ``D#3``, ``Gb5``) and durations (e.g. ``q``, ``h``). For an exhaustive list of accepted note symbols and values, have a look at the ``_NOTE_FREQUENCIES`` and ``_NOTE_VALUES`` private dictionaries in the source code. The value can be suffixed by modifiers: - a *divider* introduced by a ``/`` to obtain triplets for instance (e.g. ``q/3`` for a triplet of eight note) - a *multiplier* introduced by ``*`` (e.g. ``*1.5`` is a dotted note). Shortcuts exist for common modifiers: - ``3`` produces a triplet member note. For instance `e3` gives a triplet of eight notes, i.e. 3 eight notes in the duration of a single quarter. You must ensure that 3 triplets notes are defined in sequence to match the count, otherwise the result will not be the expected one. - ``.`` produces a dotted note, i.e. which duration is one and a half the base one. Double dots are not currently supported. Example:: .. important:: Only 4/4 signature songs are supported with respect to note durations. :param iterable[tuple(string, string)] song: the song :param int tempo: the song tempo, given in quarters per minute :param float delay: delay between notes (in seconds) :return: the spawn subprocess from ``subprocess.Popen`` :raises ValueError: if invalid note in song or invalid play parameters """

        if tempo <= 0:
            raise ValueError('invalid tempo (%s)' % tempo)
        if delay < 0:
            raise ValueError('invalid delay (%s)' % delay)

        delay_ms = int(delay * 1000)
        meas_duration_ms = 60000 / tempo * 4       # we only support 4/4 bars, hence "* 4"

        def beep_args(note, value):
            """ Builds the arguments string for producing a beep matching
            the requested note and value.

            Args:
                note (str): the note note and octave
                value (str): the note value expression
            Returns:
                str: the arguments to be passed to the beep command
            """
            freq = self._NOTE_FREQUENCIES.get(note.upper(), self._NOTE_FREQUENCIES[note])

            if '/' in value:
                base, factor = value.split('/')
                duration_ms = meas_duration_ms * self._NOTE_VALUES[base] / float(factor)
            elif '*' in value:
                base, factor = value.split('*')
                duration_ms = meas_duration_ms * self._NOTE_VALUES[base] * float(factor)
            elif value.endswith('.'):
                base = value[:-1]
                duration_ms = meas_duration_ms * self._NOTE_VALUES[base] * 1.5
            elif value.endswith('3'):
                base = value[:-1]
                duration_ms = meas_duration_ms * self._NOTE_VALUES[base] * 2 / 3
            else:
                duration_ms = meas_duration_ms * self._NOTE_VALUES[value]

            return '-f %d -l %d -D %d' % (freq, duration_ms, delay_ms)

        try:
            return self.beep(' -n '.join(
                [beep_args(note, value) for (note, value) in song]
            ))
        except KeyError as e:
            raise ValueError('invalid note (%s)' % e)