コード例 #1
0
class Channel(object):
    """
    **pyj2d.mixer.Channel**
    
    * Channel.play
    * Channel.stop
    * Channel.pause
    * Channel.unpause
    * Channel.fadeout
    * Channel.set_volume
    * Channel.get_volume
    * Channel.get_busy
    * Channel.get_sound
    * Channel.queue
    * Channel.get_queue
    * Channel.set_endevent
    * Channel.get_endevent
    """

    _mixer = None

    def __init__(self, id):
        self._id = id
        self._sound = None
        self._stream = None
        self._len = self._mixer._bufferSize
        self._data = jarray.zeros(self._len, 'b')
        self._data_len = 0
        self._data_sum = 0
        self._data_rate = self._mixer._byteRate / 1000
        self._active = AtomicBoolean(False)
        self._pause = False
        self._loops = 0
        self._volume = 1.0
        self._lvolume = 1.0
        self._rvolume = 1.0
        self._queue = None
        self._endevent = None
        self._maxtime = 0
        self._fadein = 0
        self._fadeout = 0
        self._dvol = 1.0
        self._process = False
        self._mixer._register_channel(self)

    def _set_sound(self, sound):
        self._sound = sound
        self._stream = sound._get_stream()

    def _reset_sound(self):
        self._active.set(False)
        restart = not self._pause
        if not self._sound:
            return
        try:
            sound = self._sound
            self._stream.close()
            self._set_sound(self._sound)
        except AttributeError:
            restart = False
        if restart:
            self._active.set(True)

    def _get(self):
        try:
            self._data_len = self._stream.read(self._data, 0, self._len)
        except IOException:
            self._data_len = 0
        if self._data_len > 0:
            self._data_sum += self._data_len
            if not self._process:
                return (self._data,
                        self._data_len,
                        self._lvolume*self._sound._volume,
                        self._rvolume*self._sound._volume)
            if self._maxtime:
                self._dvol = 1.0
                if self._data_sum > self._maxtime:
                    self._data_len -= (self._data_sum-self._maxtime)
                    self._maxtime = 0
                    self._loops = 0
                    self._onended()
            if self._fadein:
                if self._data_sum < self._fadein:
                    self._dvol = self._data_sum / self._fadein
                else:
                    self._dvol = 1.0
                    self._fadein = 0
                    if not (self._maxtime or self._fadeout):
                        self._process = False
            elif self._fadeout:
                if self._data_sum < self._fadeout:
                    self._dvol = 1.0 - (self._data_sum / self._fadeout)
                else:
                    self._dvol = 0.01
                    self._fadeout = 0
                    self._loops = 0
                    self._onended()
            return (self._data,
                    self._data_len,
                    self._lvolume*self._sound._volume*self._dvol,
                    self._rvolume*self._sound._volume*self._dvol)
        else:
            self._data_sum = 0
            self._onended()
            return (self._data, self._data_len, 1.0, 1.0)

    def _play(self, sound, loops, maxtime, fade_ms):
        self._set_sound(sound)
        self._loops = loops
        if maxtime:
            self._maxtime = int(maxtime * self._data_rate)
            self._process = True
        if fade_ms:
            self._fadein = fade_ms * self._data_rate
            self._process = True
        self._data_sum = 0
        self._active.set(True)

    def play(self, sound, loops=0, maxtime=0, fade_ms=0):
        """
        Play sound on channel.
        Argument sound to play, loops is repeat number or -1 for continuous,
        maxtime is maximum play time, and fade_ms is fade-in time.
        """
        if self._sound:
            lv, rv = self._lvolume, self._rvolume
            self.stop()
            self.set_volume(lv, rv)
        self._set_sound(sound)
        self._loops = loops
        if maxtime:
            self._maxtime = int(maxtime * self._data_rate)
            self._process = True
        if fade_ms:
            self._fadein = fade_ms * self._data_rate
            self._process = True
        self._data_sum = 0
        self._active.set(True)
        self._mixer._activate_channel(self._id)
        return None

    def _onended(self):
        if not self._loops:
            if not self._queue:
                self.stop()
            else:
                self.play(self._queue)
        else:
            self._stream.close()
            self._set_sound(self._sound)
            self._loops -= 1

    def stop(self):
        """
        Stop sound on channel.
        """
        if not self._active.get() and not self._pause:
            return None
        self._active.set(False)
        self._mixer._deactivate_channel(self._id)
        try:
            self._stream.close()
            self._stream = None
        except AttributeError:
            pass
        self._sound = None
        self._queue = None
        self._pause = False
        self._loops = 0
        self._maxtime = 0
        self._fadein = 0
        self._fadeout = 0
        self._volume = 1.0
        self._lvolume = 1.0
        self._rvolume = 1.0
        self._process = False
        self._mixer._restore_channel(self._id)
        if self._endevent is not None:
            env.event.post(self._endevent)
        return None

    def pause(self):
        """
        Pause sound on channel.
        """
        if self._active.get():
            self._active.set(False)
            self._pause = True
        return None

    def unpause(self):
        """
        Unpause sound on channel.
        """
        if self._pause:
            self._active.set(True)
            self._pause = False
        return None

    def fadeout(self, time):
        """
        Stop sound after fade out time.
        """
        if self._active.get() or self._pause:
            self._fadeout = self._data_sum + (time * self._data_rate)
            self._process = True
        return None

    def set_volume(self, volume, volume2=None):
        """
        Set channel volume of sound playing.
        Argument volume of value 0.0 to 1.0, setting for both speakers when single, stereo l/r speakers with second value.
        """
        if volume < 0.0:
            volume = 0.0
        elif volume > 1.0:
            volume = 1.0
        self._lvolume = volume
        if volume2:
            if volume2 < 0.0:
                volume2 = 0.0
            elif volume2 > 1.0:
                volume2 = 1.0
            self._rvolume = volume2
        else:
            self._rvolume = self._lvolume
            self._volume = volume
        return None

    def get_volume(self):
        """
        Get channel volume for current sound.
        """
        return self._volume

    def get_busy(self):
        """
        Check if channel is processing sound.
        """
        return self._active.get() or self._pause

    def get_sound(self):
        """
        Get sound open by channel.
        """
        return self._sound

    def queue(self, sound):
        """
        Queue sound to play after current sound ends.
        """
        if not self._sound:
            self.play(sound)
        else:
            self._queue = sound

    def get_queue(self):
        """
        Return queue sound.
        """
        return self._queue

    def set_endevent(self, eventType=None):
        """
        Set endevent for sound channel.
        Argument eventType is event type (eg. USEREVENT+num).
        Without an argument resets endevent to NOEVENT type.
        """
        if eventType is not None:
            if ( self._endevent is None or
                 self._endevent.type != eventType ):
                self._endevent = env.event.Event(eventType)
        else:
            self._endevent = None

    def get_endevent(self):
        """
        Get endevent type for sound channel.
        """
        if self._endevent is not None:
            return self._endevent.type
        else:
            return Const.NOEVENT
コード例 #2
0
class AbstractTimedThroughputWorker(object):
    """ Send throughput across a time period. """
    def __init__(self, period=60, chunks=100, throughput=0):
        self.period = period  # The time period over which to produce throughput
        self.chunks = chunks
        self.throughput = AtomicInteger(throughput)
        self.throughput_success = AtomicInteger(0)
        self.resume = AtomicBoolean(True)
        self.curr_tick = AtomicInteger(0)

    def stop(self):
        self.resume.set(False)
        self.thread.join()

    def start(self):
        self.thread = Thread(target=self.loop)
        self.thread.start()

    def loop(self):
        while self.resume.get():
            self.throughput_success.set(self.tick(self.throughput.get()))

    def action(self, throughput):
        """ This method fires

        Args:
            throughput (int): The throughput in bytes

        Returns (bool): Indicating success.
        """
        raise NotImplementedError("Please implement this method")

    def next_tick(self, period):
        """ Returns the next multiple of a time period """
        curr_time = time.time()
        return curr_time + (period - curr_time % period)

    def tick(self, throughput):
        """ Fires throughput over this time period """
        # The next 60 second time period
        next_tick = self.next_tick(self.period)

        # Every mini_tick, we will fire a thoughput of this size
        throughput_per_chunk = throughput / self.chunks

        #  The size of a mini_tick (e.g. 0.6 seconds)
        mini_tick_period = self.period / float(self.chunks)

        chunks_sent, successes = 0, 0
        while time.time() < next_tick and chunks_sent < self.chunks:
            if not self.resume.get():
                break
            # Fire action and record time taken
            # time_taken, success = time_it(self.action, throughput_per_chunk)
            success = self.action(throughput_per_chunk)

            # Count successes
            if success:
                successes += 1

            chunks_sent += 1

            # The time remaining to reach the next mini tick
            time_till_next_mini_tick = max(
                0,
                self.next_tick(mini_tick_period) - time.time())

            # sleep to next mini tick to ensure actions happen evenly
            time.sleep(time_till_next_mini_tick)

        return successes * throughput_per_chunk
コード例 #3
0
class Mixer(Runnable):
    """
    **pyj2d.mixer**
    
    * pyj2d.mixer.init
    * pyj2d.mixer.quit
    * pyj2d.mixer.get_init
    * pyj2d.mixer.stop
    * pyj2d.mixer.pause
    * pyj2d.mixer.unpause
    * pyj2d.mixer.fadeout
    * pyj2d.mixer.set_num_channels
    * pyj2d.mixer.get_num_channels
    * pyj2d.mixer.set_reserved
    * pyj2d.mixer.find_channel
    * pyj2d.mixer.get_busy
    * pyj2d.mixer.Sound
    * pyj2d.mixer.Channel
    * pyj2d.mixer.music
    """

    def __init__(self):
        self._mixer = None
        Sound._mixer = self
        Channel._mixer = self
        self.Sound = Sound
        self.Channel = self._get_channel
        self.music = None
        self._channel_max = 8
        self._channels = {}
        self._channel_available = ConcurrentLinkedDeque()
        self._channel_available.addAll(list(range(self._channel_max)))
        self._channel_active = ConcurrentLinkedDeque()
        self._channel_reserved = ConcurrentLinkedDeque()
        self._channel_reserved_num = 0
        self._thread = None
        self.run = self._process
        self._active = AtomicBoolean(False)
        self._initialized = False

    def init(self, frequency=22050, size=-16, channels=2, buffer=4096):
        """
        Mixer initialization.
        Argument sampled frequency, bit size, channels, and buffer.
        Currently implements PCM 16-bit audio.
        Plays WAV, AIFF, and AU sampled audio.
        To specify BigEndian format of AIFF and AU, use size of float type.
        The mixing is done by Mixer.class, compiled with 'javac Mixer.java'.
        For JAR creation include with 'jar uvf App.jar pyj2d/Mixer.class'.
        """
        if not self._initialized:
            encoding = {True: AudioFormat.Encoding.PCM_SIGNED,
                        False: AudioFormat.Encoding.PCM_UNSIGNED}[size<0]
            channels = {True:1, False:2}[channels<=1]
            framesize = int((abs(size)/8) * channels)
            isBigEndian = isinstance(size, float)
            self._audio_format = AudioFormat(encoding,
                                             int(frequency),
                                             int(abs(size)),
                                             channels,
                                             framesize,
                                             int(frequency),
                                             isBigEndian)
            self._bufferSize = buffer
            try:
                self._mixer = AudioMixer(self._audio_format, self._bufferSize)
            except TypeError:
                self._mixer = None
                return None
            if not self._mixer.isInitialized():
                return None
            self._byteRate = ( self._audio_format.getSampleRate()
                              * self._audio_format.getChannels()
                              * (self._audio_format.getSampleSizeInBits()/8) )
            self._bufferSize = self._mixer.getBufferSize()
            self._byteArray = jarray.zeros(self._bufferSize, 'b')
            for id in range(self._channel_max):
                self._get_channel(id)
            self.music = Music()
            self._initialized = True
            self._thread = Thread(self)
            self._thread.start()
        return None

    def pre_init(self, frequency=22050, size=-16, channels=2, buffer=4096):
        """
        Mixer initialization.
        """
        self.init(frequency, size, channels, buffer)
        return None

    def quit(self):
        """
        Stop mixer processing and release resources.
        """
        self._initialized = False
        return None

    def _quit(self):
        self.stop()
        self.music._channel.stop()
        try:
            self._mixer.quit()
        except AttributeError:
            pass
        self._mixer = None

    def get_init(self):
        """
        Get the audio format initialized.
        """
        if self._initialized:
            frequency = int(self._audio_format.sampleRate)
            format = ( self._audio_format.sampleSizeInBits
                      * {True:1,False:-1}[self._audio_format.bigEndian] )
            channels = self._audio_format.channels
            return (frequency, format, channels)
        else:
            return None

    def stop(self):
        """
        Stop mixer channels.
        """
        for id in self._channel_active.iterator():
            if id > -1:
                self._channels[id].stop()
        return None

    def fadeout(self, time):
        """
        Fadeout mixer channels in given time.
        """
        for id in self._channel_active.iterator():
            if id > -1:
                self._channels[id].fadeout(time)
        return None

    def pause(self):
        """
        Pause mixer channels.
        """
        for id in self._channel_active.iterator():
            if id > -1:
                self._channels[id].pause()
        return None

    def unpause(self):
        """
        Unpause mixer channels.
        """
        for id in self._channel_active.iterator():
            if id > -1:
                self._channels[id].unpause()
        return None

    def set_num_channels(self, count):
        """
        Set maximum mixer channels.
        Argument channel count.
        """
        if count >= self._channel_max:
            for id in range(self._channel_max, count):
                self._get_channel(id)
                self._channel_available.add(id)
            self._channel_max = count
        elif count >= 0:
            for id in range(count, self._channel_max):
                if id in self._channels:
                    if self._channels[id] is not None:
                        self._channels[id].stop()
                    del self._channels[id]
                self._channel_available.remove(id)
            self._channel_max = count
        return None

    def get_num_channels(self):
        """
        Get maximum mixer channels.
        """
        return self._channel_max

    def set_reserved(self, count):
        """
        Reserve channel.
        Argument reserved channel count.
        """
        if count > self._channel_max:
            count = self._channel_max
        elif count < 0:
            count = 0
        self._channel_reserved_num = count
        self._channel_reserved.clear()
        for id in range(self._channel_reserved_num):
            self._channel_reserved.add(id)
            self._channel_available.remove(id)
        return None

    def find_channel(self, force=False):
        """
        Get an inactive mixer channel.
        Optional force attribute return longest running channel if all active.
        """
        try:
            id = self._channel_available.pop()
            self._channel_available.add(id)
            return self._channels[id]
        except NoSuchElementException:
            pass
        try:
            if self._channel_reserved_num:
                id = self._channel_reserved.pop()
                self._channel_reserved.add(id)
                return self._channels[id]
        except NoSuchElementException:
            pass
        if not force:
            return None
        longest = None
        longest_reserved = None
        for id in self._channel_active.iterator():
            if id > self._channel_reserved_num-1:
                longest = id
                break
            elif id > -1:
                if longest_reserved is None:
                    longest_reserved = id
        if longest is not None:
            channel = longest
        else:
            if longest_reserved is not None:
                channel = longest_reserved
            else:
                channel = 0
        return self._channels[channel]

    def get_busy(self):
        """
        Check if mixer channels are actively processing.
        """
        for id in self._channel_active.iterator():
            if id > -1:
                if self._channels[id]._active:
                    return True
        return False

    def _process(self):
        while self._initialized:
            if not self._active.get():
                self._idle()
                continue
            if self._channel_active.size() > 1:
                data, data_len = self._mix(self._channel_active)
                if data_len > 0:
                    self._write(data, data_len)
            else:
                try:
                    channel = self._channel_active.getFirst()
                    data, data_len = self._read(channel)
                except NoSuchElementException:
                    data_len = 0
                if data_len > 0:
                    self._write(data, data_len)
        self._quit()

    def _idle(self):
        try:
            self._thread.sleep(10)
        except InterruptedException:
            Thread.currentThread().interrupt()
            self.quit()

    def _mix(self, channels):
        for id in channels.iterator():
            channel = self._channels[id]
            if not channel._active.get():
                continue
            try:
                data, data_len, lvol, rvol = channel._get()
            except AttributeError:
                continue
            self._mixer.setAudioData(data, data_len, lvol, rvol)
        data_len = self._mixer.getAudioData(self._byteArray)
        return self._byteArray, data_len

    def _read(self, channel):
        channel = self._channels[channel]
        if not channel._active.get():
            data, data_len = None, 0
        else:
            try:
                data, data_len, lvol, rvol = channel._get()
            except AttributeError:
                data, data_len = None, 0
        if data_len:
            if lvol < 1.0 or rvol < 1.0:
                data = self._mixer.processVolume(data, data_len, lvol, rvol)
        return data, data_len

    def _write(self, data, data_len):
        try:
            self._mixer.write(data, 0, data_len)
        except IllegalArgumentException:
            nonIntegralByte = data_len % self._audio_format.getFrameSize()
            if nonIntegralByte:
                data_len -= nonIntegralByte
                try:
                    self._mixer.write(data, 0, data_len)
                except (IllegalArgumentException, LineUnavailableException):
                    pass
        except LineUnavailableException:
            pass

    def _activate_channel(self, id):
        if id > self._channel_reserved_num-1:
            self._channel_available.remove(id)
        else:
            self._channel_reserved.remove(id)
        self._channel_active.add(id)
        self._active.set(True)

    def _deactivate_channel(self, id):
        self._channel_active.remove(id)
        if self._channel_active.isEmpty():
            self._active.set(False)

    def _restore_channel(self, id):
        if id > self._channel_reserved_num-1:
            self._channel_available.add(id)
        elif id > -1:
            self._channel_reserved.add(id)

    def _retrieve_channel(self):
        try:
            id = self._channel_available.pop()
            self._channel_active.add(id)
            self._active.set(True)
            return self._channels[id]
        except NoSuchElementException:
            return None

    def _get_channel(self, id):
        if id in self._channels:
            return self._channels[id]
        else:
            return Channel(id)

    def _register_channel(self, channel):
        id = channel._id
        if id < self._channel_max:
            self._channels[id] = channel
        else:
            raise AttributeError("Channel not available.")