PageRenderTime 52ms CodeModel.GetById 24ms RepoModel.GetById 0ms app.codeStats 0ms

/athenaCL/libATH/libOrc/superColliderNative.py

http://athenacl.googlecode.com/
Python | 351 lines | 313 code | 17 blank | 21 comment | 10 complexity | 4744d3234cd38d27698593ebeda6c976 MD5 | raw file
Possible License(s): AGPL-1.0
  1. #!/usr/local/bin/python
  2. #-----------------------------------------------------------------||||||||||||--
  3. # Name: superColliderNative.py
  4. # Purpose: native csound instrument definitions instruments.
  5. #
  6. # Authors: Christopher Ariza
  7. #
  8. # Copyright: (c) 2010 Christopher Ariza
  9. # License: GPL
  10. #-----------------------------------------------------------------||||||||||||--
  11. import time
  12. import unittest, doctest
  13. from athenaCL.libATH import drawer
  14. from athenaCL.libATH import language
  15. from athenaCL.libATH import pitchTools
  16. lang = language.LangObj()
  17. from athenaCL.libATH.libOrc import baseOrc
  18. _MOD = 'superColliderNative.py'
  19. from athenaCL.libATH import prefTools
  20. environment = prefTools.Environment(_MOD)
  21. class SuperColliderNative(baseOrc.Orchestra):
  22. """built-in csound instruments"""
  23. def __init__(self):
  24. baseOrc.Orchestra.__init__(self)
  25. self.name = 'superColliderNative'
  26. self.srcStr = None # string representation for writing
  27. self._instrNumbers = [0,
  28. 10,
  29. ]
  30. # on initialization, load a dictionary of objects for use
  31. self._instrObjDict = {}
  32. globalDict = globals()
  33. for iNo in self._instrNumbers:
  34. objAttr = globalDict['Inst%i' % iNo]
  35. self._instrObjDict[iNo] = objAttr() # instantiate obj
  36. #-----------------------------------------------------------------------||--
  37. def instNoValid(self, iNo):
  38. """test if an instrument number is valid"""
  39. if drawer.isInt(iNo) and iNo in self._instrNumbers:
  40. return 1
  41. else:
  42. return 0
  43. def instNoList(self, format=None):
  44. """return a list of instrument numbers; if
  45. a list is not availabe, return None"""
  46. if format == 'user':
  47. return drawer.listToStr(self._instrNumbers)
  48. return self._instrNumbers
  49. def getInstObj(self, iNo):
  50. if iNo in self._instrObjDict.keys(): # already loaded
  51. return self._instrObjDict[iNo] # call attribute of module to get object
  52. else:
  53. raise ValueError, 'bad insturment number given: %s' % iNo
  54. def constructOrc(self, noChannels=2, instList=None):
  55. """buildes a string of an entire orchestra
  56. provides proper header and output sections based on
  57. number of channels
  58. """
  59. self.noChannels = noChannels
  60. msg = []
  61. #self.instrObjDict = {}
  62. if instList == None: # if not given, add all instruments
  63. instList = self.instNoList()
  64. for number in instList:
  65. if not self.instNoValid(number):
  66. environment.printWarn([lang.WARN, 'instrument %i not available.' % number])
  67. continue
  68. instrObj = self.getInstObj(number)
  69. msg.append(instrObj.buildInstrDef(noChannels))
  70. self.srcStr = ''.join(msg)
  71. def getInstInfo(self, iNo=None):
  72. """returns a dictionary of instrNo : (Name, pNo, pInfo)
  73. has data for all instruments
  74. pmtrFields includes 6 default values
  75. """
  76. if iNo == None:
  77. instrList = self.instNoList() # use method
  78. else:
  79. instrList = [iNo,]
  80. #self.instrObjDict = {} # this used to store inst obj data
  81. instInfoDict = {}
  82. for number in instrList:
  83. instrObj = self.getInstObj(number)
  84. instInfoDict[number] = (instrObj.name,
  85. instrObj.pmtrFields, instrObj.pmtrInfo)
  86. return instInfoDict, instrList
  87. def getInstPreset(self, iNo, auxNo=None):
  88. """returns a dictionary of default values for one instrument"""
  89. instrObj = self.getInstObj(iNo)
  90. presetDict = instrObj.getPresetDict() # converts to aux0 fist pos
  91. return presetDict
  92. def getInstName(self, iNo):
  93. 'returns a string of name'
  94. instrObj = self.getInstObj(iNo)
  95. return instrObj.name
  96. def getInstAuxNo(self, iNo):
  97. instrObj = self.getInstObj(iNo)
  98. return instrObj.auxNo
  99. def getInstPmtrInfo(self, iNo, pmtrNo):
  100. """for specified inst, pmtrNo, return pmtr info
  101. parameter numbers start at 0"""
  102. instrObj = self.getInstObj(iNo)
  103. # numbers are shifted by pmtrCountDefault
  104. # this orchestra uses 'pmtr' instead of 'auxQ'
  105. key = 'pmtr%s' % (pmtrNo + 1 + instrObj.pmtrCountDefault)
  106. if instrObj.pmtrInfo != {}:
  107. return instrObj.pmtrInfo[key]
  108. else:
  109. return 'no information available'
  110. #-----------------------------------------------------------------------||--
  111. def _postMapPs(self, iNo, val):
  112. return pitchTools.psToPch(val)
  113. def _postMapAmp(self, iNo, val, orcMapMode=1):
  114. # get max/min amp value form inst, as well as scale factor
  115. instrObj = self.getInstObj(iNo)
  116. ampMax = float(instrObj.postMapAmp[1])
  117. if orcMapMode: # optional map; allow values greater then 1
  118. val = val * ampMax # temp: assume max amp of 90
  119. # always limit
  120. if val < 0: val = 0 # we can assume tt amps are never negative
  121. return val
  122. def _postMapPan(self, iNo, val, orcMapMode=1):
  123. if orcMapMode: # optional map
  124. pass # values are expected b/n 0 and 1
  125. # always limit: modulo 1
  126. if val < 0 or val > 1: val = val % 1.0
  127. return val
  128. #-----------------------------------------------------------------||||||||||||--
  129. class InstrumentSuperCollider(baseOrc.Instrument):
  130. # outputs expect and instrument to have a single final signal, calld "aMixSig
  131. # this bit of codes gets appended to end of inst def
  132. def __init__(self):
  133. """
  134. >>> a = InstrumentSuperCollider()
  135. """
  136. baseOrc.Instrument.__init__(self)
  137. self.author = 'athenaCL native' # attribution
  138. self.pmtrCountDefault = 6 # 6 built in values
  139. self.pmtrFields = self.pmtrCountDefault
  140. # postMap values for scaling
  141. self.postMapAmp = (0,1, 'linear') # most amps are in db
  142. self.postMapPan = (0,1, 'linear') # all pan values assume 0-1 maping
  143. self.instNo = None
  144. self.name = None
  145. # TODO!
  146. # leave four spaces
  147. self.monoOutput = """ Out.ar(0, Pan2.ar(sigPrePan, panPos));
  148. """
  149. self.stereoOutput = """ Out.ar(0, Pan2.ar(sigPrePan, panPos));
  150. """
  151. self.quadOutput = """ Out.ar(0, Pan2.ar(sigPrePan, panPos));
  152. """
  153. def getInstHeader(self):
  154. msg = """SynthDef("%s", {""" % self.name
  155. return msg
  156. def getInstFooter(self):
  157. msg = """ }).writeDefFile;
  158. s.sendSynthDef("%s");
  159. """ % self.name
  160. return msg
  161. def buildInstrDef(self, noChannels):
  162. """returns a string of all the code needed for this instrument
  163. >>> a = Inst0()
  164. >>> post = a.buildInstrDef(2)
  165. >>> "SynthDef" in post
  166. True
  167. >>> a.stereoOutput in post
  168. True
  169. """
  170. self.noChannels = noChannels
  171. msg = []
  172. msg.append(self.getInstHeader())
  173. msg.append(self.orcCode)
  174. if self.noChannels == 1:
  175. msg.append(self.monoOutput)
  176. elif self.noChannels == 2:
  177. msg.append(self.stereoOutput)
  178. elif self.noChannels == 4:
  179. msg.append(self.quadOutput)
  180. msg.append(self.getInstFooter())
  181. return ''.join(msg)
  182. #-----------------------------------------------------------------||||||||||||--
  183. # instruments 0 through 9 are unpitched percussion
  184. class Inst0(InstrumentSuperCollider):
  185. def __init__(self):
  186. """
  187. >>> a = Inst0()
  188. """
  189. InstrumentSuperCollider.__init__(self)
  190. self.instNo = 0
  191. self.name = 'noiseBasic'
  192. self.info = 'A simple noise instrument.'
  193. self.postMapAmp = (0, 1, 'linear') # assume amps not greater tn 1
  194. self.pmtrInfo = {
  195. 'pmtr7' : 'attack percent within unit interval',
  196. 'pmtr8' : 'release percent within unit interval',
  197. 'pmtr9' : 'cutoff frequency in midi pitch values',
  198. }
  199. self.pmtrDefault = {
  200. 'panQ' : ('rb', .2, .2, 0, 1),
  201. 'pmtr7' : ('bg', 'rc', [.1, .2, .3, .4]),
  202. 'pmtr8' : ('bg', 'rc', [.1, .2, .3, .4]),
  203. 'pmtr9' : ('ru', 60, 120),
  204. }
  205. self.auxNo = len(self.pmtrInfo.keys())
  206. self.pmtrFields = self.pmtrCountDefault + self.auxNo
  207. self.pmtrFieldNames = ['sus', 'amp', 'pan'] + self.pmtrInfo.keys()
  208. self.author = 'athenaCL native' # attribution
  209. self.orcCode = """
  210. arg sus, amp, pan, attackPercent, releasePercent, cfMidi;
  211. var env, ampEnvl, gate, panPos, sigPrePan;
  212. panPos = (pan*2)-1;
  213. gate = Line.ar(1, 0, sus, doneAction: 2);
  214. env = Env.perc(sus*attackPercent, sus*releasePercent, amp, -4);
  215. ampEnvl = EnvGen.kr(env, gate);
  216. sigPrePan = LPF.ar(WhiteNoise.ar(ampEnvl), cfMidi.midicps);
  217. """
  218. def pmtrToOrcName(self, pmtr):
  219. """Translate from native pmtr names to score pmtr names
  220. """
  221. if pmtr in ['sus', 'pan', 'amp']:
  222. return pmtr
  223. elif pmtr == 'inst':
  224. return self.name
  225. elif pmtr == 'pmtr7':
  226. return 'attackPercent'
  227. elif pmtr == 'pmtr8':
  228. return 'releasePercent'
  229. elif pmtr == 'pmtr9':
  230. return 'cfMidi'
  231. else:
  232. raise Exception('canot translate parameter %s' % pmtr)
  233. #-----------------------------------------------------------------||||||||||||--
  234. # instruments 10 through 19 are pitched percussion
  235. class Inst10(InstrumentSuperCollider):
  236. def __init__(self):
  237. """
  238. >>> a = Inst10()
  239. """
  240. InstrumentSuperCollider.__init__(self)
  241. self.instNo = 10
  242. self.name = 'electroKick'
  243. self.info = 'A basic elctronic kick.'
  244. self.postMapAmp = (0, 1, 'linear') # assume amps not greater tn 1
  245. self.pmtrInfo = {
  246. 'pmtr7' : 'envelope ratio',
  247. 'pmtr8' : 'frequency decay',
  248. }
  249. self.pmtrDefault = {
  250. 'panQ' : ('rb', .2, .2, 0, 1),
  251. 'pmtr7' : ('bg', 'rc', [3, 2, 1]),
  252. 'pmtr8' : ('bg', 'rc', [.02, .05]),
  253. }
  254. self.auxNo = len(self.pmtrInfo.keys())
  255. self.pmtrFields = self.pmtrCountDefault + self.auxNo
  256. self.pmtrFieldNames = ['sus', 'amp', 'pan', 'ps'] + self.pmtrInfo.keys()
  257. self.author = 'athenaCL native' # attribution
  258. self.orcCode = """
  259. arg ps= -10, sus=0.5, amp=1, pan=0.5, envlRatio=3, fqDecay=0.02;
  260. var fqEnvl, ampEnvl, sigPrePan, panPos, pitchMidi;
  261. pitchMidi = ps + 60;
  262. panPos = (pan*2)-1; // convert from 0 to 1 to -1 to 1
  263. fqEnvl = EnvGen.kr(Env([envlRatio, 1], [fqDecay], \exp), 1) * pitchMidi.midicps;
  264. ampEnvl = EnvGen.kr(Env.perc(0.05, sus, amp), 1, doneAction: 2);
  265. sigPrePan = SinOsc.ar(fqEnvl, 0.5pi, ampEnvl);
  266. """
  267. def pmtrToOrcName(self, pmtr):
  268. """Translate from native pmtr names to score pmtr names
  269. """
  270. if pmtr in ['sus', 'pan', 'amp', 'ps']:
  271. return pmtr
  272. elif pmtr == 'inst':
  273. return self.name
  274. elif pmtr == 'pmtr7':
  275. return 'envlRatio'
  276. elif pmtr == 'pmtr8':
  277. return 'fqDecay'
  278. else:
  279. raise Exception('canot translate parameter %s' % pmtr)
  280. #-----------------------------------------------------------------||||||||||||--
  281. class Test(unittest.TestCase):
  282. def runTest(self):
  283. pass
  284. def testDummy(self):
  285. self.assertEqual(True, True)
  286. #-----------------------------------------------------------------||||||||||||--
  287. if __name__ == '__main__':
  288. from athenaCL.test import baseTest
  289. baseTest.main(Test)