from Csound import *
import time

class CsoundAudioFile:
    """
    Handles loading, playing and unloading audio files
    """
    def __init__(self, filename, csound, instrIndex, fTableIndex):
        """
        Initialize parameters
        """    
        self.filename = filename
        self.csound = csound
        self.isLoaded = False
        self.fTableIndex = fTableIndex
        self.instrIndex = instrIndex
        self.playRequested = False
        self.channels = 0
        self.tableLength = 0
        self.loadCalled = False
        
    def play(self, offset = 0.0, duration = -1, amp = 1.0):
        """
        Play the loaded f-table
        """    
        
        if self.loadCalled == False:
            self.load()
            self.playRequested = True
            self.requestedAmp = amp
            self.requestedDuration = duration
            return
        
        transposeFactor = 1.0 / float( self.channels )
        
        if duration < 0:
            # play the entire table
            duration = self.tableLength / self.csound._sampleRate
            
        # use the channel information to determine which instrument to play
        if self.channels == 1:
            self.csound.playEvent('i', [self.instrIndex, offset, duration, amp, transposeFactor, self.fTableIndex])
        elif self.channels == 2:
            self.csound.playEvent('i', [self.instrIndex+1, offset, duration, amp, transposeFactor, self.fTableIndex])
    
        

    def loadedCallback(self, message):
        """
        When the instrument finishes loading the f-table,
        this method should be called twice.  Once as /len
        and once as /channels ... once both values have been
        acquired, we can test them and decide if the table
        has been successfully loaded and then decide to play the sample
        if it was requested
        
        argument(0) will represent the length and number of channels respectively
        """
        #message.display("callback:")
        #print
        
        if message.currentPath() == '/len':
            self.tableLength = message.argument(0)
        elif message.currentPath() == '/channels':
            self.channels = message.argument(0)
        
        if self.tableLength > 0 and self.channels > 0:
            #print "IS LOADED!"
            self.isLoaded = True            
            if self.playRequested == True:
                self.play(self.requestedAmp, self.requestedDuration)
                            
        
    def load(self):
        """
        Load the audio file as an f-table 
        The instrument will notify us when the table has loaded by calling
        self.loadedCallback
        """            
        self.loadCalled = True
        self.csound.registerChannel(str(self.fTableIndex), self.loadedCallback)
                        
        fEvent = 'i110 0 1 "'+self.filename+'" '+str(self.fTableIndex)
        self.csound.playEventAsString(fEvent)


class CsoundAudioFiles:    
    """
    A high-level controller that uses Csound to enable common audio and MIDI
    playback capabilities
    """
    
    audioFileOrchestra = """
	nchnls = 2

    instr 100 ; mono

iBase   = 1

iDur    = p3
iAmp    = p4 * 30000
iTranspose = p5
iTable  = p6

aLoscilOut loscil iAmp, iTranspose, iTable, iBase

    outs    aLoscilOut, aLoscilOut
    endin


    instr 101 ; stereo

iBase   = 1

iDur    = p3
iAmp    = p4 * 30000
iTranspose = p5
iTable  = p6

aLoscilOutL, aLoscilOutR loscil iAmp, iTranspose, iTable, iBase

    outs    aLoscilOutL, aLoscilOutR
    endin



    instr 110
kCounter init 0
    
    Sfname      strget	p4

    Spathback	sprintf	"%d/len", p5    
    Spathbackchan	sprintf	"%d/channels", p5    

    ichannels filenchnls Sfname
        
    iftno	ftgen	p5, 0, 0, 1, Sfname, 0, 0, 1

if kCounter >= 0 kgoto done    
            outvalue Spathback, ftlen(iftno)
            outvalue Spathbackchan, ichannels
done:
   
    endin

    """
    
    def __init__(self, instanceName, messageCallback = None, host = 'localhost', port = 7000):
        self.csound = Csound(instanceName, host, port)
        if messageCallback != None:
            self.csound.setMessageCallback(messageCallback)
        self.csound.setupOrchestraAsString(self.audioFileOrchestra, 'audioFileOrchestra')
        #self.csound.setupMessageLevel(255)
        #self.csound.setupDisplaysEnabled(True)

        self.csound.initialize()
        self.csound.perform()
        
        self.fTableIndex = 1        
        self.audioFileInstr = 100
    
    def shutdown(self):
        """
        Inform the server we're done with the instance
        """
        self.csound.shutdown()

    def shutdownServer(self):
        """
        Inform the server we want to shut it all down
        """
        self.csound.shutdownServer()
                
    def audioFile(self, filename, expandPath = True):
        """
        Returns an instance of CsoundAudioFile.
        It also updates the f-table count to provide unique table ids
        """
        if expandPath == True:
            filename = os.getcwd() + '/' + filename
        
        audioFileInstance = CsoundAudioFile(filename, self.csound, self.audioFileInstr, self.fTableIndex)
        self.fTableIndex += 1
        return audioFileInstance
        