<SYSTEM_TASK:>
Given the following text description, write Python code to implement the functionality described below step by step
<END_TASK>
<USER_TASK:>
Description:
 Multithreading
In the preceding recipes, there was a single thread running; this is the default way to use Python, due to the GIL (Global Interpreter Lock). Then, the user has the possibility to interact with the Csound instance during the performance loop. This is illustrated in the following diagram
Step1:  Then, let's start a new thread, passing the opaque pointer of the Csound instance as argument
Step2:  Now, we can send messages to the performance thread
Step3:  When we're done, we stop the performance thread and reset the csound instance
Step4:  Note that we can still access the csound instance with other methods, like controlChannel() or setControlChannel()
Step5:  We can ask for the values in the Csound instance ...
Step6:  ... or we can set our own values to the Csound instance
Step7:  At the end, stop and reset as usual
<ASSISTANT_TASK:>
Python Code:
import ctcsound
cs = ctcsound.Csound()
csd = '''
<CsoundSynthesizer>
<CsOptions>
  -d -o dac -m0
</CsOptions>
<CsInstruments>
sr     = 48000
ksmps  = 100
nchnls = 2
0dbfs  = 1
          instr 1
idur      =         p3
iamp      =         p4
icps      =         cpspch(p5)
irise     =         p6
idec      =         p7
ipan      =         p8
kenv      linen     iamp, irise, idur, idec
kenv      =         kenv*kenv
asig      poscil    kenv, icps
a1, a2    pan2      asig, ipan
          outs      a1, a2
          endin
</CsInstruments>
<CsScore>
f 0 14400    ; a 4 hours session should be enough
</CsScore>
</CsoundSynthesizer>
'''
cs.compileCsdText(csd)
cs.start()
Explanation: Multithreading
In the preceding recipes, there was a single thread running; this is the default way to use Python, due to the GIL (Global Interpreter Lock). Then, the user has the possibility to interact with the Csound instance during the performance loop. This is illustrated in the following diagram:
To use Csound in a more flexible way, one can use multithreading. Because of the GIL limitations, it is better to yield the multithread machinery through C libraries. When a C function is called from Python using ctypes, the GIL is released during the function call.
Csound has an helper class called CsoundPerformanceThread. When there is a running Csound instance, one can start a new thread by creating a new object of type CsoundPerformanceThread with a reference to the Csound instance as argument. Then, the main Python thread will run allowing the user to interract with it, while the performance thread will run concurrently in the C world, outside of the GIL. The user can send messages to the performance thread, each message being sent with a call to a C function through ctypes, releasing the GIL during the function call. Those messages can be: play(), pause(), togglePause(), stop(), record(), stopRecord(), scoreEvent(), inputMessage(), setScoreOffsetSeconds(), join(), or flushMessageQueue().
When a very long score is used, it is thus easy to implement a REPL (read-eval-print loop) system around Csound. This is illustrated in the following diagram:
So let's start a Csound instance from Python, with a four hours long score:
End of explanation
pt = ctcsound.CsoundPerformanceThread(cs.csound())
pt.play()
Explanation: Then, let's start a new thread, passing the opaque pointer of the Csound instance as argument:
End of explanation
pt.scoreEvent(False, 'i', (1, 0, 1, 0.5, 8.06, 0.05, 0.3, 0.5))
pt.scoreEvent(False, 'i', (1, 0.5, 1, 0.5, 9.06, 0.05, 0.3, 0.5))
Explanation: Now, we can send messages to the performance thread:
End of explanation
pt.stop()
pt.join()
cs.reset()
Explanation: When we're done, we stop the performance thread and reset the csound instance:
End of explanation
csd = '''
<CsoundSynthesizer>
<CsOptions>
-odac
</CsOptions>
<CsInstruments>
sr = 44100
ksmps = 64
nchnls = 2
0dbfs = 1
seed 0
instr 1
 iPch random 60, 72
 chnset iPch, "pch"
 kPch init iPch
 kNewPch chnget "new_pitch"
 if kNewPch > 0 then
  kPch = kNewPch
 endif
 aTone poscil .2, mtof(kPch)
 out aTone, aTone
endin
</CsInstruments>
<CsScore>
i 1 0 600
</CsScore>
</CsoundSynthesizer>
'''
cs.compileCsdText(csd)
cs.start()
pt = ctcsound.CsoundPerformanceThread(cs.csound())
pt.play()
Explanation: Note that we can still access the csound instance with other methods, like controlChannel() or setControlChannel():
End of explanation
print(cs.controlChannel('pch'))
Explanation: We can ask for the values in the Csound instance ...
End of explanation
cs.setControlChannel('new_pitch',73)
Explanation: ... or we can set our own values to the Csound instance:
End of explanation
pt.stop()
pt.join()
cs.reset()
Explanation: At the end, stop and reset as usual:
End of explanation
<END_TASK>