Logo Search packages:      
Sourcecode: scenic version File versions  Download package


#!/usr/bin/env python
# -*- coding: utf-8 -*-
# Scenic
# Copyright (C) 2008 Société des arts technologiques (SAT)
# http://www.sat.qc.ca
# All rights reserved.
# This file is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 2 of the License, or
# (at your option) any later version.
# Scenic is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# GNU General Public License for more details.
# You should have received a copy of the GNU General Public License
# along with Scenic. If not, see <http://www.gnu.org/licenses/>.
RTP MIDI session.
#History Needs
from midi_object import OldPacket
from midi_object import MidiCommand
from list_circ import  PacketCirc
#rtp import
from recovery_journal import RecoveryJournal
from recovery_journal import compare_history_with_recovery
from rtpmidi.protocols.rtp.rtp_session import RTPSession
#twisted import
from twisted.internet import task
#midi import
from rtpmidi.engines.midi.midi_in import MidiIn
from rtpmidi.engines.midi.midi_out import MidiOut
    import pypm
except ImportError:
    from pygame import pypm
from time import time

#RTP Timeout is 60 (need it because 
#midi data are not sent when no note are played)
TOOL_NAME = "Sropulpof_midi" #FIXME: rename this

00053 class MidiSession(RTPSession):
    Control RTP for MIDI payload.
    def __init__(self, peer_address, sport=0, rport=0, latency=20, 
                 jitter_buffer_size=10, safe_keyboard=0, recovery=1, 
                 follow_standard=0, verbose=0):
        #Init mother class
        RTPSession.__init__(self, peer_address, sport, rport, PAYLOAD, 
                            jitter_buffer_size, TOOL_NAME)
        self.verbose = verbose
        if verbose:
            print "Your configuration:"
            print "  Latency:", latency, "ms"
            print "  Jitter Buffer Time:", jitter_buffer_size, "ms"
        #init midi
        if self.sport > 0:
            self.midi_in = MidiIn(self, verbose)
        #1 == Permisive mode (make this configurable)
        if self.rport > 0:
            self.midi_out = MidiOut(0, latency, safe_keyboard, verbose)
        #history of the feed
        self.packets_received_list = PacketCirc(HISTORY_SIZE)
        #Recovery utils
        self.recovery = 0
        self.recovery_journal_system = None
        if recovery:
            self.recovery = 1
            self.recovery_journal_system = RecoveryJournal(follow_standard)
            if verbose:
                print "  Recovery journal is running"
                if follow_standard:
                    print "  Recovery is following standard"
        self.init_timestamp = None
        self.sending_data = 0
        self.receiving_data = 0
        #Timestamp story
        self.last_midi_time_sent = pypm.Time()
        self.timeouterLoop = None

00094     def start(self):
        Launch the MIDI session and the RTP session
        if self.sport > 0:
        if self.rport > 0:

00104     def stop(self):
        Stop midi session and the RTP session
        if self.sport > 0:
        if self.rport > 0:
        if not self.timeouterLoop is None:
            if self.timeouterLoop.running:

    def _keep_alive(self):
        #FIXME: document this
        def check_timeout():
            Sends an empty packet if there is no activity.
            if time.time() > self.last_send + MIDI_RTP_TIME_OUT:
                #Deccomment following line if want to log keep aive packet
                #log.info("silent packet sent to keep alive the connection")
            self.timeouterLoop = task.LoopingCall(check_timeout, now=False)

00129     def incoming_rtp(self, cookie, timestamp, packet, read_recovery_journal=0):
        Function called by RTPControl when incoming 
        data comes out from jitter buffer.
        #Parsing RTP MIDI Header
        marker_b, marker_j, marker_z, marker_p, length = \
        if marker_p :
            #silent packet with recovery
            midi_list = []
            #normal packet
            #Extract Midi Note (length en nb notes)
            midi_list = packet.data[1:length*7+1]
            #Decoding midi commands
            midi_list =  MidiCommand().decode_midi_commands(midi_list, length)
        if DEBUG:
            print "receiving data", midi_list
            #Saving feed history
            packet_to_save = OldPacket(self.seq, midi_list, 0)
        #Extract Midi Recovery Journal if is present in the packet and 
        #the previous packet has been lost
        if self.recovery:
            if marker_j and read_recovery_journal:
                if DEBUG:
                    print "Read recovery journal"
                    print packet.header.marker
                journal = packet.data[length*7+1:]
                if len(journal)>0:
                    #Parse Recovery journal
                    r_journal = self.recovery_journal_system.parse(journal)
                    r_journal = []
                #compare it with history feed
                #Extract midi notes from checkpoint sent to actual seq
                midi_history = self.packets_received_list.get_packets(self.last_checkpoint,self.seq)
                #Selecting only notes present in recovery 
                #that are not in feed history
                midi_cmd_history = []
                for i in range(len(midi_history)):
                    cmd_tmp = midi_history[i].packet
                    for j in range(len(cmd_tmp)):
                if DEBUG:
                    rem = time()
                midi_from_history = compare_history_with_recovery(r_journal, midi_cmd_history)
                if DEBUG:
                    print "tps for history compare:", str(time() - rem)
        #Initialize timestamp diff
        if self.init_timestamp is None:
            self.init_timestamp = timestamp
            #calculate delta midi
        #adding recovery journal to the list
        if self.recovery:
            if marker_j and read_recovery_journal:
        #profiter du parcours pour appliquer les timestamps
        for i in range(len(midi_list)):
            midi_list[i][1] = (timestamp - self.init_timestamp + self.midi_out.init_time)
        #Adding the note to the playing buffer
        for i in range(len(midi_list)):
        #switch off witness
        self.receiving_data = 0

00197     def send_silence(self):
        Sends empty packet to signal a silent period
        recovery_journal = ""
        marker_j = 0
        #Getting recovery 
        if self.recovery:
            recovery_journal = self.recovery_journal_system.content
            if recovery_journal != "":
                #Recovery Journal 1 if present
                marker_j = 1
        #Creating empty midicommand filed
        header = MidiCommand().header(0, marker_j, 1, 1, 0)
        if recovery_journal != "":
            chunk = header + recovery_journal
            chunk = str(0)
        #sending silent packet with recovery journal
        RTPSession.send_empty_packet(self, chunk)
        #RTPControl().send_empty_packet(self.cookie, chunk)
00219     def send_midi_data(self, data, midi_time, recovery=1, timestamp=1):
        Sends MIDI data through the RTP session.
        if DEBUG:
            print "Sending data", data
        self.sending_data = 1
        #midi Cmd List
        midi_list = data
        #Saving packet 
        packet = OldPacket(self.seq, midi_list, 0)
        chunk = ""
        recovery_journal = ""
        if recovery:
            #Recovery Journal (can be empty) 
            #TODO customize it for each member of the feed
            if self.recovery_journal_system is not None:
                recovery_journal = self.recovery_journal_system.content
                if recovery_journal == "":
                    recovery = 0
        #Packing All
        #Testing length of midi list ( in nb notes )
        if len(midi_list) < 1:
        #Formating commands for network
        midi_list_formated, length = \
        #Creating Header
        header = MidiCommand().header(0, recovery, 0, 0, length)
        #Building Chunk
        chunk = header + midi_list_formated + recovery_journal
        #Timestamp care (TS == temps midi ecouler depuis la creation de rtp)
        ts = midi_time - self.last_midi_time_sent
        self.last_midi_time_sent = midi_time
        #sending data to rtp session
        RTPSession.send_data(self, chunk, ts)
        self.sending_data = 0
        #Updating Recovery Journal
        if self.recovery:

00261     def update_checkpoint(self, new_checkpoint):
        Function called by RTCP to reduce size of recovery journal.
        if self.recovery:

        self.checkpoint = new_checkpoint

00270     def drop_connection(self, cookie=0):
        Called by RTP when the connection has been dropped.
        #Rename drop connection
        print "drop connexion for midi session"

00277     def get_devices(self):
        Lists MIDI devices on this computer.
        @rtype: tuple
        @return: Tuple of list of device info. Input devices first, then output devices.
        #FIXME: return a dict, not a tuple.
        if self.sport > 0:
            devices_in = self.midi_in.get_devices()
            devices_in = []
        if self.rport > 0:
            devices_out = self.midi_out.get_devices()
            devices_out = []
        return devices_in, devices_out

00294     def set_device_in(self, dev):
        Selects the MIDI device to be polled.
        @param dev: The device number to choose.
        @type dev: int
        @rtype: bool
        @return: Success or not
        return self.midi_in.set_device(dev) 

00305     def set_device_out(self, dev):
        Selects the MIDI output device to send data to.
        @param dev: The device number to choose.
        @type dev: int
        @rtype: bool
        @return: Success or not
        return self.midi_out.set_device(dev)

Generated by  Doxygen 1.6.0   Back to index