from sugar.activity import activity
import logging

from Csound import *
from CsoundServer import *

import random
import sys, os
import gtk

import gobject

        
class CSMIDIPlayerActivity(activity.Activity):

    sfOrc = """
nchnls = 2

ga1 init 0
ga2 init 0


ichn = 1
lp1:
        massign   ichn, 0
        loop_le   ichn, 1, 16, lp1
        pgmassign 0, 0

gisf   sfload    "#ACTIVITY_PATH#/gmBank44100.sf2"

sfpassign  0, gisf

instr 1

; set this to be preset 0 on bank 128
; by hand (eg. on gmBank44010.sf2 it's 317, on gmBank16000.sf2 it's 128, on gmBank22050 it's 226)

idkit = 317

tableiw idkit, 9, 1

; set this by hand to allow for some
; release envelope on percussion
irel = 0.5

ipg = 1
ivol = 2
ipan = 3

nxt:
  kst, kch, kd1, kd2 midiin
  if (kst != 0) then 
       kch = kch - 1
      if (kst == 128 || (kst == 144 && kd2 == 0)) then ; note off    
        kpg table kch, ipg
        kinst = 10 +  kd1/100000 + kch/100
        if kch == 9 then
         if (kd1 == 29 || kd1 == 30) then ; EXC7
          kinst = 10.97
         elseif (kd1 == 42 || kd1 == 44) then ; EXC1
           kinst = 10.91
         elseif (kd1 == 46 || kd1 == 49) then
           kinst = 10.91
         elseif (kd1 == 71 || kd1 == 72) then ; EXC2         
           kinst = 10.92
         elseif (kd1 == 73 || kd1 == 74) then ; EXC3         
           kinst = 10.93
         elseif (kd1 == 78 || kd1 == 79) then ; EXC4         
           kinst = 10.94
         elseif (kd1 == 80 || kd1 == 81) then ; EXC5         
           kinst = 10.95
         elseif (kd1 == 86 || kd1 == 87) then ; EXC6         
           kinst = 10.96
         endif
        else
         kpg = 0
        endif
        event "i", -kinst, 0, 1 

    elseif (kst == 144 && kd2 != 0) then ; note on
        kpg table kch, ipg 
        /* instrument identifier is 10.[chn][note] */
        kinst = 10 + kd1/100000 + kch/100  
        if kch == 9 then
         /* exclusive identifiers */
         if kpg == idkit+7 then
           krel = 2    /* add extra release time for orch perc*/
         else
           krel = 0.5
         endif
         if (kd1 == 29 || kd1 == 30) then ; EXC7
          kinst = 10.97
         elseif (kd1 == 42 || kd1 == 44) then ; EXC1
           kinst = 10.91
         elseif (kd1 == 46 || kd1 == 49) then
           kinst = 10.91
         elseif (kd1 == 71 || kd1 == 72) then ; EXC2         
           kinst = 10.92
         elseif (kd1 == 73 || kd1 == 74) then ; EXC3         
           kinst = 10.93
         elseif (kd1 == 78 || kd1 == 79) then ; EXC4         
           kinst = 10.94
         elseif (kd1 == 80 || kd1 == 81) then ; EXC5         
           kinst = 10.95
         elseif (kd1 == 86 || kd1 == 87) then ; EXC6         
           kinst = 10.96
         endif
        else
         krel = 0.1
        endif
        event "i", kinst, 0, -1, kd1, kd2, kpg, kch,krel 
      
    elseif (kst == 192) then ; program change 
       if kch == 9 then
         kpg = idkit
         if kd1 == 8 then
         kpg = idkit+1
         elseif kd1 == 16 then
         kpg = idkit+2
         elseif kd1 == 24 then
         kpg = idkit+3
         elseif kd1 == 25 then
         kpg = idkit+4
         elseif kd1 == 32 then
         kpg = idkit+5
         elseif kd1 == 40 then
         kpg = idkit+6
         elseif kd1 == 48 then
         kpg = idkit+7
         endif
       else
       kpg = kd1 
       endif
       tablew  kpg, kch, ipg
    elseif (kst == 176 && (kd1 == 11 || kd1 == 7)) then ; volume
       tablew kd2, kch, ivol
    elseif (kst == 176 && kd1 == 10) then  ; pan
       tablew kd2, kch, ipan
    endif
    /*if (kst == 176) then
      printk 0, kd1
      printk 0, kd2
      printk 0, kch
      endif*/
     kgoto nxt
  endif

endin

instr 10

if (p6 >= 40 && p6 < 50) then
iatt = 0.08
irel = 0.5
elseif (p6 >= 70 && p6 < 80) then
iatt = 0.02
irel = p8
else
irel = p8
iatt = 0.005
endif

kenv linenr 1,iatt,irel,0.01
iamp table p5, 5
a1, a2 sfplay p5, p4, iamp,1, p6
kv table p7, 2
kvol tablei kv, 5 
kpan  table p7, 3
kpan = (kpan - 64)/128
       outs a1*kvol*(0.5-kpan/2)*kenv, a2*kvol*(0.5+kpan/2)*kenv 

endin
"""

    sfScore = """
    
f1 0 16 -2 0 0 0 0 0 0 0 0 226 0 0 0 0 0 0 0
f2 0 16 -2 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127 127
f3 0 16 -2 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64 64
f5 0 128 5 0.05 128 1
i 1 0 3600
"""


    fluidOrc = """
nchnls   =  2

0dbfs   =  32768

turnon 1

ichn   =  1
lp1:
   massign ichn, 0
   loop_le ichn, 1, 16, lp1
   pgmassign 0, 0

; initialize FluidSynth

gifld   fluidEngine 0, 0, 16, 128

gisf2   fluidLoad "#ACTIVITY_PATH#/gmBank44100.sf2", gifld, 1


gi_interp_method = 0

fluidSetInterpMethod gifld, 0, gi_interp_method
fluidSetInterpMethod gifld, 1, gi_interp_method
fluidSetInterpMethod gifld, 2, gi_interp_method
fluidSetInterpMethod gifld, 3, gi_interp_method
fluidSetInterpMethod gifld, 4, gi_interp_method
fluidSetInterpMethod gifld, 5, gi_interp_method
fluidSetInterpMethod gifld, 6, gi_interp_method
fluidSetInterpMethod gifld, 7, gi_interp_method
fluidSetInterpMethod gifld, 8, gi_interp_method
fluidSetInterpMethod gifld, 9, gi_interp_method
fluidSetInterpMethod gifld, 10, gi_interp_method
fluidSetInterpMethod gifld, 11, gi_interp_method
fluidSetInterpMethod gifld, 12, gi_interp_method
fluidSetInterpMethod gifld, 13, gi_interp_method
fluidSetInterpMethod gifld, 14, gi_interp_method
fluidSetInterpMethod gifld, 15, gi_interp_method

; k-rate version of fluidProgramSelect

   opcode fluidProgramSelect_k, 0, kkkkk

keng, kchn, ksf2, kbnk, kpre   xin
   igoto skipInit
doInit:
   fluidProgramSelect i(keng), i(kchn), i(ksf2), i(kbnk), i(kpre)
   reinit doInit
   rireturn
skipInit:

   endop

   instr 1

; initialize channels

kchn   init 1
   if (kchn == 1) then
lp2:
      fluidControl gifld, 192, kchn - 1, 0, 0
      fluidControl gifld, 176, kchn - 1, 7, 100
      fluidControl gifld, 176, kchn - 1, 10, 64
      loop_le kchn, 1, 16, lp2
   endif

; send any MIDI events received to FluidSynth

nxt:
kst, kch, kd1, kd2   midiin
   if (kst != 0) then
      if (kst != 192 || kch != 10) then
         fluidControl gifld, kst, kch - 1, kd1, kd2
      else
         fluidProgramSelect_k gifld, kch - 1, gisf2, 128, kd1
      endif
      kgoto nxt
   endif

; get audio output from FluidSynth

aL, aR   fluidOut gifld

   outs aL*0dbfs, aR*0dbfs

   endin
"""

    def destroy(self, data=None):
        if self.csound != None:
            self.csound.shutdownServer()
        activity.Activity.destroy(self)

    def __init__(self, handle):
        activity.Activity.__init__(self, handle)

        self.csound = None
        
        # set the bundle path in the orchestras
        self.sfOrc = self.sfOrc.replace('#ACTIVITY_PATH#', activity.get_bundle_path())
        self.fluidOrc = self.fluidOrc.replace('#ACTIVITY_PATH#', activity.get_bundle_path())

        
        self.mode = "method 1"
        self.connect('destroy', self.destroy)
        
        self.set_title('CS MIDI Player')
        self.performing = False
        
        # Creates the Toolbox. It contains the Activity Toolbar, which is the
        # bar that appears on every Sugar window and contains essential
        # functionalities, such as the 'Collaborate' and 'Close' buttons.
        toolbox = activity.ActivityToolbox(self)
        self.set_toolbox(toolbox)
        toolbox.show()

        
        # Creates a new button with the label "Hello World".
        playButton = gtk.Button("Play")
        playButton.set_size_request(150, 50)
        playButton.connect('clicked', self.play)

        openButton = gtk.Button("Open")
        openButton.set_size_request(150, 50)
        openButton.connect('clicked', self.open)

        stopButton = gtk.Button("Stop")
        stopButton.set_size_request(150, 50)
        stopButton.connect('clicked', self.stop)

        clearButton = gtk.Button("clear")
        clearButton.set_size_request(150, 40)
        clearButton.connect('clicked', self.clear)

        self.pauseButton = gtk.Button("Pause")
        self.pauseButton.set_size_request(150, 50)
        self.pauseButton.connect('clicked', self.pause)

        self.layout = gtk.Layout()
        self.layout.modify_bg(gtk.STATE_NORMAL, gtk.gdk.color_parse("#C0C0C0"))        


        
        self.method1Button = gtk.RadioButton(None, "sfplay")
        self.method1Button.connect("toggled", self.modeSwitch, "method 1")
        self.method2Button = gtk.RadioButton(self.method1Button, "fluid")
        self.method2Button.connect("toggled", self.modeSwitch, "method 2")

        self.sampleRate = gtk.Entry()
        self.sampleRate.set_text("16000")
        self.sampleRate.set_size_request(150, 40)
        
        self.layout.put(openButton, 50, 50)

        self.layout.put(playButton, 300, 50)
        self.layout.put(self.pauseButton, 450, 50)
        self.layout.put(stopButton, 600, 50)
        self.layout.put(clearButton, 1000, 140)

        self.serverStatus = gtk.Label("Server")
        self.connectionStatus = gtk.Label("Not Connected")
        self.layout.put(self.serverStatus, 900, 10)
        self.layout.put(self.connectionStatus, 900, 50)
                
        self.layout.put(gtk.Label("Sample Rate"), 50, 130)
        self.layout.put(self.sampleRate, 225, 125)

        self.layout.put(self.method1Button, 25, 5)
        self.layout.put(self.method2Button, 200, 5)
        
        self.showMsgs = gtk.CheckButton("Show messages")
        self.showMsgs.set_active(True)
        self.layout.put(self.showMsgs, 400, 125)

        self.messagesTextView = gtk.TextView()
        
        self.messageScrolledWindow = gtk.ScrolledWindow()
        self.messageScrolledWindow.add_with_viewport(self.messagesTextView)
        self.messageScrolledWindow.set_policy(gtk.POLICY_AUTOMATIC, gtk.POLICY_AUTOMATIC)
        self.messageScrolledWindow.set_size_request(1150, 600)
        self.layout.put(self.messageScrolledWindow, 10, 200)
        
        self.layout.show_all()

        # Set the button to be our canvas. The canvas is the main section of
        # every Sugar Window. It fills all the area below the toolbox.
        self.set_canvas(self.layout)

    def modeSwitch(self, widget, data = None): 
        self.mode = data

    def fileOpenOkay(self, widget, data = None):
        filename = self.fileOpen.get_filename()
        self.chosenFilename = filename
        self.fileOpen.destroy()
        
    def open(self, widget, data = None):
        self.fileOpen = gtk.FileSelection("Open File")
        self.fileOpen.set_size_request(900, 700)
        self.fileOpen.ok_button.connect("clicked", self.fileOpenOkay)
        self.fileOpen.cancel_button.connect("clicked", lambda w: self.fileOpen.destroy())
        self.fileOpen.set_filename(os.path.join( activity.get_bundle_path(), "midiFiles/") )
        self.fileOpen.show()

    def clear(self, widget, userData = None):
        buffer = self.messagesTextView.get_buffer()
        buffer.set_text("")


    def stop(self, widget, userData = None):
        self.csound.stop()
        self.csound.shutdownServer()

    def pause(self, widget, userData = None):
        if widget.get_label() == 'Pause':
            self.csound.pause()
            widget.set_label('Resume')
        else:
            self.csound.perform()
            widget.set_label('Pause')
        
    def play(self, widget, userData = None):
        if self.performing:
            try:
                self.csound.stop()
                self.csound.shutdownServer()
                time.sleep(2)
            except Exception, msg:
                print msg
                
            self.performing = False
            
        sr = int( self.sampleRate.get_text() )
        kr = sr / 100.0
        m = 0
        if self.showMsgs.get_active() == True:
            m = 1
        
        server = CsoundServer()
        fd = server.launch(msgs=m, sampleRate = sr)
        if fd == None:
            self.serverStatus.set_label("Server failed to launch")
        else:
            gobject.io_add_watch(fd.fileno(), gobject.IO_IN, self.serverMessages, fd)
            self.serverStatus.set_label("Server - online")
                
        #if fd != None:
        #    gobject.io_add_watch(fd.fileno(), gobject.IO_IN, self.serverMessages, fd)

        # Create our instance        
        self.csound = Csound("CSMIDIPlayer")
        if self.csound.isConnected():
            self.connectionStatus.set_label("Connected")
        else:
            self.connectionStatus.set_label("Not Connected")
            
        self.csound.setMessageCallback(self.csoundMessages)
        if self.mode == "method 1":
            self.csound.setupOrchestraAsString(self.sfOrc)
            self.csound.setupScoreAsString(self.sfScore)
        else:
            self.csound.setupOrchestraAsString(self.fluidOrc)

        self.csound.addCallback(self.performanceDone, "/notification/done_performing")        
        self.csound.setSampleRate(sr)
        self.csound.setControlRate(kr)
        self.csound.setupTerminateWhenMIDIFileDone()
        
        self.csound.setupMIDIFile(self.chosenFilename, False)
        self.csound.initialize()
        self.csound.perform()
        self.performing = True
        
    def performanceDone(self, message):
        self.performing = False
        self.csound.shutdown()

    def serverMessages(self, source, condition, userData = None):
        print userData.readline(),
        return True

    def csoundMessages(self, message):
        #message.display("Csound")

        buffer = self.messagesTextView.get_buffer()
        enditer = buffer.get_end_iter()
        
        buffer.insert(enditer, message.argument(0))