Sampler in ChucK
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

373 lines
12KB

  1. #!/usr/bin/python
  2. # coding: utf-8
  3. from libs.simpleosc import *
  4. import wx
  5. def sendOSCSafe(channel, data):
  6. try:
  7. #print channel, data
  8. sendOSCMsg(channel, data)
  9. except OSCClientError:
  10. print "OSC comms error"
  11. class OSCSlider(wx.Panel):
  12. """ A GUI slider """
  13. def __init__(self, parent, label, min_value=0, max_value=1, default_value=0, align=True, rescale=100.):
  14. """ Constructor """
  15. self.rescale = rescale
  16. wx.Panel.__init__(self, parent)
  17. sizer = wx.BoxSizer(wx.HORIZONTAL)
  18. label = wx.StaticText(self, label=label, size=(100, 15) if align else None)
  19. sizer.Add(label, 0, wx.RIGHT, 10)
  20. self.slider = wx.Slider(self, value=default_value * self.rescale,
  21. minValue=min_value * self.rescale, maxValue=max_value * self.rescale)
  22. sizer.Add(self.slider, 1, wx.EXPAND)
  23. self.SetSizerAndFit(sizer)
  24. self.Bind = self.slider.Bind
  25. def GetValue(self):
  26. """ Make sure that we rescale """
  27. return self.slider.GetValue() / self.rescale
  28. class CommsPanel(wx.Panel):
  29. """ OSC comms """
  30. def __init__(self, parent):
  31. wx.Panel.__init__(self, parent)
  32. sizer = wx.BoxSizer(wx.HORIZONTAL)
  33. label = wx.StaticText(self, label="Sync:")
  34. font = label.GetFont()
  35. font.SetWeight(wx.BOLD)
  36. label.SetFont(font)
  37. sizer.Add(label, 0, wx.TOP | wx.BOTTOM | wx.RIGHT | wx.EXPAND, 5)
  38. choices = ["Master", "Minion"]
  39. self.master = wx.ComboBox(self, choices=choices, style=wx.CB_READONLY, size=(25, 25))
  40. sizer.Add(self.master, 1, wx.ALL, 3)
  41. self.master.SetValue(choices[0])
  42. self.ip = wx.TextCtrl(self, value="127.0.0.1")
  43. sizer.Add(self.ip, 0, wx.ALL, 3)
  44. self.port = wx.TextCtrl(self, value="9000")
  45. sizer.Add(self.port, 0, wx.ALL, 3)
  46. self.SetSizerAndFit(sizer)
  47. class InputPanel(wx.Panel):
  48. """ Handle the ADC input settings """
  49. def __init__(self, parent):
  50. """ Constructor """
  51. wx.Panel.__init__(self, parent)
  52. sizer = wx.BoxSizer(wx.HORIZONTAL)
  53. label = wx.StaticText(self, label="Input:")
  54. font = label.GetFont()
  55. font.SetWeight(wx.BOLD)
  56. label.SetFont(font)
  57. sizer.Add(label, 0, wx.TOP | wx.BOTTOM | wx.RIGHT, 5)
  58. self.gain = OSCSlider(self, "Gain", default_value=.5, max_value = 1.2, align=False)
  59. sizer.Add(self.gain, 1, wx.ALL, 5)
  60. self.thru = OSCSlider(self, "Thru", default_value=1, max_value = 1.2, align=False)
  61. sizer.Add(self.thru, 1, wx.ALL, 5)
  62. self.mute = wx.ToggleButton(self, 0, "Mute")
  63. # self.mute.SetValue(1)
  64. sizer.Add(self.mute, 0)
  65. self.SetSizerAndFit(sizer)
  66. self.gain.Bind(wx.EVT_SCROLL, self.update)
  67. self.thru.Bind(wx.EVT_SCROLL, self.update)
  68. self.mute.Bind(wx.EVT_TOGGLEBUTTON, self.update)
  69. self.update()
  70. def update(self, evt=None):
  71. """ Send OSC messages """
  72. gain = self.gain.GetValue()
  73. thru = self.thru.GetValue()
  74. if self.mute.GetValue():
  75. gain, thru = 0., 0.
  76. sendOSCSafe("/input", [gain, thru])
  77. class DelayPanel(wx.Panel):
  78. """ Handle the ADC input settings """
  79. def __init__(self, parent):
  80. """ Constructor """
  81. wx.Panel.__init__(self, parent)
  82. sizer = wx.BoxSizer(wx.HORIZONTAL)
  83. label = wx.StaticText(self, label="Loop:")
  84. font = label.GetFont()
  85. font.SetWeight(wx.BOLD)
  86. label.SetFont(font)
  87. sizer.Add(label, 0, wx.TOP | wx.BOTTOM | wx.RIGHT, 5)
  88. self.delayTime = OSCSlider(self, "Time", default_value=2, max_value=10, min_value=0.5, align=False)
  89. sizer.Add(self.delayTime, 1, wx.ALL, 5)
  90. self.feedback = OSCSlider(self, "Hold", default_value=.99, align=False)
  91. sizer.Add(self.feedback, 1, wx.ALL, 5)
  92. self.metronome = wx.ToggleButton(self, 0, "Metronome")
  93. sizer.Add(self.metronome, 0)
  94. self.SetSizerAndFit(sizer)
  95. self.delayTime.Bind(wx.EVT_SCROLL, self.update)
  96. self.feedback.Bind(wx.EVT_SCROLL, self.update)
  97. self.metronome.Bind(wx.EVT_TOGGLEBUTTON, self.switchMetronome)
  98. self.update(None)
  99. def update(self, evt):
  100. """ Send OSC messages """
  101. a = self.delayTime.GetValue()
  102. b = self.feedback.GetValue()
  103. sendOSCSafe("/delay", [a, b])
  104. def switchMetronome(self, evt):
  105. """ Send OSC messages """
  106. sendOSCSafe("/metronome", [int(self.metronome.GetValue())])
  107. class ButtonArray(wx.Panel):
  108. """ Handle the ADC input settings """
  109. def __init__(self, parent, index):
  110. wx.Panel.__init__(self, parent)
  111. w = 40
  112. sizer = wx.BoxSizer(wx.HORIZONTAL)
  113. self.record = wx.ToggleButton(self, 0, "Arm", size=(w, 25))
  114. sizer.Add(self.record, 1, wx.ALL, 0)
  115. self.mute = wx.ToggleButton(self, 0, "Mute", size=(w, 25))
  116. sizer.Add(self.mute, 1, wx.ALL, 0)
  117. self.clear = wx.Button(self, 0, "Clear", size=(w, 25))
  118. sizer.Add(self.clear, 1, wx.ALL, 0)
  119. self.buttons = (self.record, self.mute, self.clear)
  120. self.SetSizerAndFit(sizer)
  121. class Channel(wx.Panel):
  122. """ A single channel """
  123. def __init__(self, parent, index):
  124. self.index = index
  125. wx.Panel.__init__(self, parent)
  126. sizer = wx.BoxSizer(wx.VERTICAL)
  127. self.gain = OSCSlider(self, "Gain", default_value=1, max_value=1.3, align=False)
  128. sizer.Add(self.gain, 0, wx.ALL | wx.EXPAND, 3)
  129. self.pan = OSCSlider(self, "Pan", default_value=0, min_value=-1, max_value=1, align=False)
  130. sizer.Add(self.pan, 0, wx.ALL | wx.EXPAND, 3)
  131. self.fxsend = OSCSlider(self, "Dry/Wet", default_value=0, min_value=0, max_value=1, align=False)
  132. sizer.Add(self.fxsend, 0, wx.ALL | wx.EXPAND, 3)
  133. self.buttons = ButtonArray(self, index)
  134. self.record, self.mute, self.clear = self.buttons.buttons
  135. sizer.Add(self.buttons, 0, wx.ALL | wx.EXPAND, 3)
  136. choices = ["1 bar", "2 bars", "4 bars", "Dephase", "1/2 rate"]
  137. self.speed = wx.ComboBox(self, choices=choices, style=wx.CB_READONLY, size=(25, 25))
  138. self.speed.SetValue(choices[0])
  139. sizer.Add(self.speed, 0, wx.ALL | wx.EXPAND, 3)
  140. choices = ["Forward", "Back", "Half", "Double"]
  141. self.direction = wx.ComboBox(self, choices=choices, style=wx.CB_READONLY, size=(25, 25))
  142. self.direction.SetValue(choices[0])
  143. sizer.Add(self.direction, 0, wx.ALL | wx.EXPAND, 3)
  144. self.SetSizerAndFit(sizer)
  145. self.gain.Bind(wx.EVT_SCROLL, self.update)
  146. self.pan.Bind(wx.EVT_SCROLL, self.update)
  147. self.fxsend.Bind(wx.EVT_SCROLL, self.update)
  148. self.speed.Bind(wx.EVT_COMBOBOX, self.update_multiplier)
  149. self.direction.Bind(wx.EVT_COMBOBOX, self.update_direction)
  150. self.mute.Bind(wx.EVT_TOGGLEBUTTON, self.update)
  151. self.update()
  152. def update(self, evt=None):
  153. gain = self.gain.GetValue()
  154. pan = self.pan.GetValue()
  155. fxsend = self.fxsend.GetValue()
  156. if self.mute.GetValue(): gain = 0.0
  157. sendOSCSafe("/channel", [self.index, gain, pan, fxsend])
  158. def update_multiplier(self, evt=None):
  159. multiplierTable = {"1 bar": 1., "2 bars": 2., "4 bars": 4., "Dephase": 1.3}
  160. multiplier = multiplierTable[self.speed.GetValue()]
  161. sendOSCSafe("/multiplier", [self.index, multiplier])
  162. def update_direction(self, evt=None):
  163. #multiplierTable = {"1 bar": 1., "2 bars": 2., "4 bars": 4., "Dephase": 1.3}
  164. directionTable = {"Forward":1., "Back":-1., "Half":.5, "Double":2.}
  165. direction = directionTable[self.direction.GetValue()]
  166. sendOSCSafe("/direction", [self.index, direction])
  167. class Mixer(wx.Panel):
  168. """ All the channels """
  169. def __init__(self, parent):
  170. """ Constructor """
  171. wx.Panel.__init__(self, parent)
  172. sizer = wx.BoxSizer(wx.HORIZONTAL)
  173. self.channels = []
  174. for i in range(4):
  175. c = Channel(self, index=i)
  176. c.record.Bind(wx.EVT_TOGGLEBUTTON, self.switch_record)
  177. c.record.index = i
  178. c.clear.Bind(wx.EVT_BUTTON, self.clear_channel)
  179. c.clear.index = i
  180. self.channels.append(c)
  181. sizer.Add(c, 1, wx.EXPAND)
  182. self.SetSizerAndFit(sizer)
  183. def switch_record(self, evt):
  184. """ Send OSC message to switch recording channel """
  185. index = evt.GetEventObject().index
  186. value = evt.GetEventObject().GetValue()
  187. for i, c in enumerate(self.channels):
  188. c.record.SetValue(0)
  189. self.channels[index].record.SetValue(value)
  190. sendOSCSafe("/arm", [index if value else -1])
  191. def clear_channel(self, evt):
  192. """ Send OSC message to clear a channel """
  193. index = evt.GetEventObject().index
  194. sendOSCSafe("/clear", [index])
  195. class FXPanel(wx.Panel):
  196. """ Effects chain """
  197. def __init__(self, parent):
  198. """ Constructor """
  199. wx.Panel.__init__(self, parent)
  200. sizer = wx.BoxSizer(wx.HORIZONTAL)
  201. label = wx.StaticText(self, label="FX:")
  202. font = label.GetFont()
  203. font.SetWeight(wx.BOLD)
  204. label.SetFont(font)
  205. sizer.Add(label, 0, wx.EXPAND | wx.TOP | wx.BOTTOM | wx.RIGHT, 5)
  206. self.lpf = OSCSlider(self, "Lo-pass", default_value=1, align=False)
  207. sizer.Add(self.lpf, 2, wx.EXPAND | wx.ALL, 5)
  208. self.lpf.Bind(wx.EVT_SCROLL, self.update)
  209. self.hpf = OSCSlider(self, "Hi-pass", min_value=0, default_value=0, align=False)
  210. sizer.Add(self.hpf, 2, wx.EXPAND | wx.ALL, 5)
  211. self.hpf.Bind(wx.EVT_SCROLL, self.update)
  212. self.reverb = OSCSlider(self, "Reverb", default_value=.1, align=False)
  213. sizer.Add(self.reverb, 2, wx.EXPAND | wx.ALL, 5)
  214. self.reverb.Bind(wx.EVT_SCROLL, self.update)
  215. self.SetSizerAndFit(sizer)
  216. self.update(None)
  217. def update(self, evt):
  218. a = self.lpf.GetValue()
  219. a2 = self.hpf.GetValue()
  220. b = self.reverb.GetValue()
  221. sendOSCSafe("/fx", [a, a2, b])
  222. class OutputPanel(wx.Panel):
  223. """ Handle the ADC input settings """
  224. def __init__(self, parent):
  225. """ Constructor """
  226. wx.Panel.__init__(self, parent)
  227. sizer = wx.BoxSizer(wx.HORIZONTAL)
  228. label = wx.StaticText(self, label="Output:")
  229. font = label.GetFont()
  230. font.SetWeight(wx.BOLD)
  231. label.SetFont(font)
  232. sizer.Add(label, 0, wx.TOP | wx.BOTTOM | wx.RIGHT, 5)
  233. self.level = OSCSlider(self, "Level", default_value=.8, align=False)
  234. sizer.Add(self.level, 2, wx.EXPAND | wx.ALL, 5)
  235. self.level.Bind(wx.EVT_SCROLL, self.update)
  236. self.SetSizerAndFit(sizer)
  237. def update(self, evt):
  238. a = self.level.GetValue()
  239. sendOSCSafe("/master", [a])
  240. class MainGUI(wx.Frame):
  241. """ A simple GUI to talk to Chuck """
  242. def __init__(self):
  243. """ Constructor """
  244. # Build the interface
  245. self.app = wx.App(False)
  246. self.build()
  247. def run(self):
  248. self.app.MainLoop()
  249. def build(self):
  250. """ Builds the various pieces of the GUI """
  251. wx.Frame.__init__(self, None, title="DELAY LORD")
  252. self.Bind(wx.EVT_CLOSE, self.quit)
  253. # The main sizer
  254. self.mainsizer = wx.BoxSizer(wx.VERTICAL)
  255. self.components = []
  256. components = (CommsPanel, InputPanel, DelayPanel, Mixer, FXPanel, OutputPanel)
  257. for index, ctype in enumerate(components):
  258. if index>0: self.mainsizer.Add(wx.StaticLine(self), 0, wx.EXPAND | wx.ALL, 1)
  259. c = ctype(self)
  260. self.components.append(c)
  261. self.mainsizer.Add(c, 0, wx.EXPAND | wx.ALL, 5)
  262. self.SetSizerAndFit(self.mainsizer)
  263. self.Show()
  264. def populate_left_panel(self):
  265. """ Populate the left panel """
  266. # Status boxes
  267. def quit(self, *args):
  268. """ Close down gracefully, and then destroy the window """
  269. self.Destroy()
  270. if __name__ == "__main__":
  271. initOSCClient(ip="127.0.0.1", port=9000)
  272. g = MainGUI()
  273. g.run()
  274. closeOSC()