(self,  args)
Parse a list of Music Macro Language strings (PLAY statement).
Parse a list of Music Macro Language strings (PLAY statement).
Parse a list of Music Macro Language strings ( PLAY statement ) .
def play_(self,  args):
        """Parse a list of Music Macro Language strings (PLAY statement)."""
        # retrieve Music Macro Language string
        mml_list = [values.to_string_or_none(arg) for arg, _ in zip(args, range(3))]
        list(args)
        # at least one string must be specified
        if not any(mml_list):
            raise error.BASICError(error.MISSING_OPERAND)
        # on PCjr, three-voice PLAY requires SOUND ON
        if not self._sound_on and len(mml_list) > 1:
            raise error.BASICError(error.STX)
        # a marker is inserted at the start of the PLAY statement
        # this takes up one spot in the buffer and thus affects timings
        self._synch = True
        mml_list += [b''] * (3 - len(mml_list))
        ml_parser_list = [mlparser.MLParser(mml, self._memory, self._values) for mml in mml_list]
        next_oct = 0
        voices = list(range(3))
        while True:
            if not voices:
                break
            for voice in voices:
                vstate = self._state[voice]
                mmls = ml_parser_list[voice]
                c = mmls.skip_blank_read().upper()
                if c == b'':
                    voices.remove(voice)
                    continue
                elif c == b';':
                    continue
                elif c == b'X':
                    # insert substring
                    sub = mmls.parse_string()
                    pos = mmls.tell()
                    rest = mmls.read()
                    mmls.seek(pos)
                    mmls.truncate()
                    mmls.write(sub)
                    mmls.write(rest)
                    mmls.seek(pos)
                elif c == b'N':
                    note = mmls.parse_number()
                    error.range_check(0, 84, note)
                    dur = vstate.length
                    while mmls.skip_blank_read_if((b'.',)):
                        dur *= 1.5
                    if note == 0:
                        # pause
                        self.emit_tone(0, dur*vstate.tempo, 1, False, voice, vstate.volume)
                    else:
                        self.emit_tone(
                                NOTE_FREQ[note-1], dur*vstate.tempo,
                                vstate.fill, False, voice, vstate.volume)
                elif c == b'L':
                    recip = mmls.parse_number()
                    error.range_check(1, 64, recip)
                    vstate.length = 1. / recip
                elif c == b'T':
                    recip = mmls.parse_number()
                    error.range_check(32, 255, recip)
                    vstate.tempo = 240. / recip
                elif c == b'O':
                    octave = mmls.parse_number()
                    error.range_check(0, 6, octave)
                    vstate.octave = octave
                elif c == b'>':
                    vstate.octave += 1
                    if vstate.octave > 6:
                        vstate.octave = 6
                elif c == b'<':
                    vstate.octave -= 1
                    if vstate.octave < 0:
                        vstate.octave = 0
                elif c in (b'A', b'B', b'C', b'D', b'E', b'F', b'G', b'P'):
                    note = c
                    dur = vstate.length
                    length = None
                    if mmls.skip_blank_read_if((b'#', b'+')):
                        note += b'#'
                    elif mmls.skip_blank_read_if((b'-',)):
                        note += b'-'
                    c = mmls.skip_blank_read_if(DIGITS)
                    if c is not None:
                        numstr = [c]
                        while mmls.skip_blank() in set(iterchar(DIGITS)):
                            numstr.append(mmls.read(1))
                        # NOT ml_parse_number, only literals allowed here!
                        length = int(b''.join(numstr))
                        error.range_check(0, 64, length)
                        if length > 0:
                            dur = 1. / float(length)
                    while mmls.skip_blank_read_if((b'.',)):
                        error.throw_if(note == b'P' and length == 0)
                        dur *= 1.5
                    if note == b'P':
                        # length must be specified
                        if length is None:
                            raise error.BASICError(error.IFC)
                        # don't do anything for length 0
                        elif length > 0:
                            self.emit_tone(0, dur * vstate.tempo, 1, False, voice, vstate.volume)
                    else:
                        # use default length for length 0
                        try:
                            self.emit_tone(
                                NOTE_FREQ[(vstate.octave + next_oct) * 12 + NOTES[note]],
                                dur * vstate.tempo, vstate.fill, False, voice, vstate.volume)
                        except KeyError:
                            raise error.BASICError(error.IFC)
                    next_oct = 0
                elif c == b'M':
                    c = mmls.skip_blank_read().upper()
                    if c == b'N':
                        vstate.fill = 7./8.
                    elif c == b'L':
                        vstate.fill = 1.
                    elif c == b'S':
                        vstate.fill = 3./4.
                    elif c == b'F':
                        self._foreground = True
                    elif c == b'B':
                        self._foreground = False
                    else:
                        raise error.BASICError(error.IFC)
                elif c == b'V' and self._multivoice and self._sound_on:
                    vol = mmls.parse_number()
                    error.range_check(-1, 15, vol)
                    if vol == -1:
                        vstate.volume = 15
                    else:
                        vstate.volume = vol
                else:
                    raise error.BASICError(error.IFC)
        self._synch = False
        if self._foreground:
            # wait until fully done on Tandy/PCjr, continue early on GW
            self._wait(0 if self._multivoice else 1)
        else:
            self._wait_background()
def play_ ( self , args ) : # retrieve Music Macro Language string mml_list = [ values . to_string_or_none ( arg ) for arg , _ in zip ( args , range ( 3 ) ) ] list ( args ) # at least one string must be specified if not any ( mml_list ) : raise error . BASICError ( error . MISSING_OPERAND ) # on PCjr, three-voice PLAY requires SOUND ON if not self . _sound_on and len ( mml_list ) > 1 : raise error . BASICError ( error . STX ) # a marker is inserted at the start of the PLAY statement # this takes up one spot in the buffer and thus affects timings self . _synch = True mml_list += [ b'' ] * ( 3 - len ( mml_list ) ) ml_parser_list = [ mlparser . MLParser ( mml , self . _memory , self . _values ) for mml in mml_list ] next_oct = 0 voices = list ( range ( 3 ) ) while True : if not voices : break for voice in voices : vstate = self . _state [ voice ] mmls = ml_parser_list [ voice ] c = mmls . skip_blank_read ( ) . upper ( ) if c == b'' : voices . remove ( voice ) continue elif c == b';' : continue elif c == b'X' : # insert substring sub = mmls . parse_string ( ) pos = mmls . tell ( ) rest = mmls . read ( ) mmls . seek ( pos ) mmls . truncate ( ) mmls . write ( sub ) mmls . write ( rest ) mmls . seek ( pos ) elif c == b'N' : note = mmls . parse_number ( ) error . range_check ( 0 , 84 , note ) dur = vstate . length while mmls . skip_blank_read_if ( ( b'.' , ) ) : dur *= 1.5 if note == 0 : # pause self . emit_tone ( 0 , dur * vstate . tempo , 1 , False , voice , vstate . volume ) else : self . emit_tone ( NOTE_FREQ [ note - 1 ] , dur * vstate . tempo , vstate . fill , False , voice , vstate . volume ) elif c == b'L' : recip = mmls . parse_number ( ) error . range_check ( 1 , 64 , recip ) vstate . length = 1. / recip elif c == b'T' : recip = mmls . parse_number ( ) error . range_check ( 32 , 255 , recip ) vstate . tempo = 240. / recip elif c == b'O' : octave = mmls . parse_number ( ) error . range_check ( 0 , 6 , octave ) vstate . octave = octave elif c == b'>' : vstate . octave += 1 if vstate . octave > 6 : vstate . octave = 6 elif c == b'<' : vstate . octave -= 1 if vstate . octave < 0 : vstate . octave = 0 elif c in ( b'A' , b'B' , b'C' , b'D' , b'E' , b'F' , b'G' , b'P' ) : note = c dur = vstate . length length = None if mmls . skip_blank_read_if ( ( b'#' , b'+' ) ) : note += b'#' elif mmls . skip_blank_read_if ( ( b'-' , ) ) : note += b'-' c = mmls . skip_blank_read_if ( DIGITS ) if c is not None : numstr = [ c ] while mmls . skip_blank ( ) in set ( iterchar ( DIGITS ) ) : numstr . append ( mmls . read ( 1 ) ) # NOT ml_parse_number, only literals allowed here! length = int ( b'' . join ( numstr ) ) error . range_check ( 0 , 64 , length ) if length > 0 : dur = 1. / float ( length ) while mmls . skip_blank_read_if ( ( b'.' , ) ) : error . throw_if ( note == b'P' and length == 0 ) dur *= 1.5 if note == b'P' : # length must be specified if length is None : raise error . BASICError ( error . IFC ) # don't do anything for length 0 elif length > 0 : self . emit_tone ( 0 , dur * vstate . tempo , 1 , False , voice , vstate . volume ) else : # use default length for length 0 try : self . emit_tone ( NOTE_FREQ [ ( vstate . octave + next_oct ) * 12 + NOTES [ note ] ] , dur * vstate . tempo , vstate . fill , False , voice , vstate . volume ) except KeyError : raise error . BASICError ( error . IFC ) next_oct = 0 elif c == b'M' : c = mmls . skip_blank_read ( ) . upper ( ) if c == b'N' : vstate . fill = 7. / 8. elif c == b'L' : vstate . fill = 1. elif c == b'S' : vstate . fill = 3. / 4. elif c == b'F' : self . _foreground = True elif c == b'B' : self . _foreground = False else : raise error . BASICError ( error . IFC ) elif c == b'V' and self . _multivoice and self . _sound_on : vol = mmls . parse_number ( ) error . range_check ( - 1 , 15 , vol ) if vol == - 1 : vstate . volume = 15 else : vstate . volume = vol else : raise error . BASICError ( error . IFC ) self . _synch = False if self . _foreground : # wait until fully done on Tandy/PCjr, continue early on GW self . _wait ( 0 if self . _multivoice else 1 ) else : self . _wait_background ( )