2 # -*- coding: utf-8 -*-
8 from dbus
import UInt32
9 from functools
import partial
10 from time
import ctime
11 from PyQt4
.QtCore
import Qt
, QFile
, QIODevice
, QSize
, QTextStream
, QTimer
, SIGNAL
, SLOT
12 from PyQt4
.QtGui
import QDialog
, QDialogButtonBox
, QTableWidgetItem
13 from PyQt4
.QtXml
import QDomDocument
14 from PyKDE4
.kdecore
import i18n
, KUrl
15 from PyKDE4
.kdeui
import KAction
, KIcon
, KMessageBox
, KStandardAction
, KXmlGuiWindow
16 from PyKDE4
.kio
import KFileDialog
17 import shared
, renderw
, tb_main
, tb_apps
, tb_connections
, tb_mixer
, ui_mainw
, ui_dia_studios
21 class KlaudiaMainW(KXmlGuiWindow
, ui_mainw
.Ui_KlaudiaMainW
):
22 def __init__(self
, *args
):
23 KXmlGuiWindow
.__init
__(self
, *args
)
26 # Load a project if an argument is given
27 if (shared
.OpenFolder
):
28 url
= shared
.OpenFolder
29 if (path
.exists(url
+"/project.klaudia")):
32 KMessageBox
.sorry(self
, i18n("The Klaudia project cannot be loaded!"), i18n("Sorry"))
36 if (not shared
.TESTING
):
39 filename
= QFile(shared
.p_folder
+"/project.klaudia")
40 if not filename
.open(QIODevice
.ReadOnly
):
42 if not xml
.setContent(filename
):
46 # Check if project file is not corrupted
47 content
= xml
.documentElement()
48 if (content
.tagName() != "KLAUDIA"):
51 # Get values from XML - the big code
52 node
= content
.firstChild()
53 while not node
.isNull():
54 if (node
.toElement().tagName() == "Properties"):
55 properties
= node
.toElement().firstChild()
56 while not properties
.isNull():
57 name
= properties
.toElement().tagName()
58 text
= properties
.toElement().text()
59 if (name
== "Name"): shared
.p_name
= text
60 elif (name
== "Author"): shared
.p_author
= text
61 elif (name
== "Composer"): shared
.p_composer
= text
62 elif (name
== "Album"): shared
.p_album
= text
63 elif (name
== "Track"): shared
.p_track
= text
64 elif (name
== "BPM"): shared
.p_bpm
= text
65 elif (name
== "Time-Start"): shared
.p_time_start
= text
66 elif (name
== "Time-End"): shared
.p_time_end
= text
67 properties
= properties
.nextSibling()
68 node
= node
.nextSibling()
69 if (node
.toElement().tagName() == "Ladish"):
70 properties
= node
.toElement().firstChild()
71 while not properties
.isNull():
72 name
= properties
.toElement().tagName()
73 text
= properties
.toElement().text()
74 if (name
== "Project-Name"): shared
.ladish_project_name
= text
75 properties
= properties
.nextSibling()
76 node
= node
.nextSibling()
78 if (not shared
.TESTING
):
79 if (str(shared
.ladish_project_name
) != str(shared
.studioBus
.GetName())):
80 print str(shared
.ladish_project_name
)
81 print str(shared
.studioBus
.GetName())
82 shared
.controlBus
.LoadStudio(str(shared
.ladish_project_name
))
83 shared
.jackBus
.StartServer()
84 if (not bool(shared
.jackBus
.IsStarted())):
85 shared
.jackBus
.StartServer()
86 jack
.attach("Klaudia")
89 self
.spin_bpm
.setValue(float(shared
.p_bpm
))
92 self
.jackTimer
= QTimer()
93 self
.cpuTimer
= QTimer()
94 self
.jackTimer
.start(250)
95 self
.cpuTimer
.start(2000)
98 self
.act_render
= KAction(KIcon("media-record"), i18n("&Render..."), self
)
99 self
.act_export_zip
= KAction(i18n("&Export Project to Zip file..."), self
)
100 self
.act_playpause
= KAction(KIcon("media-playback-start"), i18n("&Play"), self
)
101 self
.act_playpause
.setCheckable(True)
102 self
.act_stop
= KAction(KIcon("media-playback-stop"), i18n("&Stop"), self
)
103 self
.act_backwards
= KAction(KIcon("media-seek-backward"), i18n("&Backwards"), self
)
104 self
.act_forwards
= KAction(KIcon("media-seek-forward"), i18n("&Forwards"), self
)
105 self
.act_bf_16
= KAction(i18n("16"), self
)
106 self
.act_bf_32
= KAction(i18n("32"), self
)
107 self
.act_bf_64
= KAction(i18n("64"), self
)
108 self
.act_bf_128
= KAction(i18n("128"), self
)
109 self
.act_bf_256
= KAction(i18n("256"), self
)
110 self
.act_bf_512
= KAction(i18n("512"), self
)
111 self
.act_bf_1024
= KAction(i18n("1024"), self
)
112 self
.act_bf_2048
= KAction(i18n("2048"), self
)
113 self
.act_bf_4096
= KAction(i18n("4096"), self
)
114 self
.act_import_ladish
= KAction(i18n("Import &Ladish Project..."), self
)
115 self
.act_show_fake_toolbar
= KAction(i18n("Show &Toolbar"), self
)
116 self
.act_show_fake_toolbar
.setCheckable(True)
117 self
.act_show_fake_statusbar
= KAction(i18n("Show St&atusbar"), self
)
118 self
.act_show_fake_statusbar
.setCheckable(True)
120 self
.act_export_zip
.setEnabled(False)
122 self
.act_bf_list
= ( self
.act_bf_16
, self
.act_bf_32
, self
.act_bf_64
, self
.act_bf_128
, self
.act_bf_256
, self
.act_bf_512
,
123 self
.act_bf_1024
, self
.act_bf_2048
, self
.act_bf_4096
)
125 size
= str(shared
.jackBus
.GetBufferSize())
126 for i
in range(len(self
.act_bf_list
)):
127 self
.act_bf_list
[i
].setCheckable(True)
128 if (self
.act_bf_list
[i
].text().replace("&","") == size
):
129 self
.act_bf_list
[i
].setChecked(True)
132 self
.act_show_fake_toolbar
.setChecked(setting_tbV
)
133 self
.toolbarW
.setVisible(setting_tbV
)
136 self
.act_show_fake_statusbar
.setChecked(setting_sbV
)
137 self
.statusbarW
.setVisible(setting_sbV
)
140 KStandardAction
.open(self
.NIY
, self
.actionCollection())
141 KStandardAction
.openRecent(self
.NIY
, self
.actionCollection())
142 KStandardAction
.save(self
.saveProject
, self
.actionCollection())
143 KStandardAction
.saveAs(self
.saveAs
, self
.actionCollection())
144 KStandardAction
.close(self
.close
, self
.actionCollection())
145 KStandardAction
.preferences(self
.NIY
, self
.actionCollection())
148 self
.actionCollection().addAction("render", self
.act_render
)
149 self
.actionCollection().addAction("export_zip", self
.act_export_zip
)
150 self
.actionCollection().addAction("tr_play", self
.act_playpause
)
151 self
.actionCollection().addAction("tr_stop", self
.act_stop
)
152 self
.actionCollection().addAction("tr_back", self
.act_backwards
)
153 self
.actionCollection().addAction("tr_next", self
.act_forwards
)
154 self
.actionCollection().addAction("bf_16", self
.act_bf_16
)
155 self
.actionCollection().addAction("bf_32", self
.act_bf_32
)
156 self
.actionCollection().addAction("bf_64", self
.act_bf_64
)
157 self
.actionCollection().addAction("bf_128", self
.act_bf_128
)
158 self
.actionCollection().addAction("bf_256", self
.act_bf_256
)
159 self
.actionCollection().addAction("bf_512", self
.act_bf_512
)
160 self
.actionCollection().addAction("bf_1024", self
.act_bf_1024
)
161 self
.actionCollection().addAction("bf_2048", self
.act_bf_2048
)
162 self
.actionCollection().addAction("bf_4096", self
.act_bf_4096
)
163 self
.actionCollection().addAction("import_ladish", self
.act_import_ladish
)
164 self
.actionCollection().addAction("show_fake_toolbar", self
.act_show_fake_toolbar
)
165 self
.actionCollection().addAction("show_fake_statusbar", self
.act_show_fake_statusbar
)
168 self
.b_playpause
.setIcon(KIcon("media-playback-start"))
169 self
.b_stop
.setIcon(KIcon("media-playback-stop"))
170 self
.b_backwards
.setIcon(KIcon("media-seek-backward"))
171 self
.b_forwards
.setIcon(KIcon("media-seek-forward"))
172 self
.b_open
.setIcon(KIcon("document-open"))
173 self
.b_save
.setIcon(KIcon("document-save"))
174 self
.b_save_as
.setIcon(KIcon("document-save-as"))
175 self
.b_render
.setIcon(KIcon("media-record"))
176 self
.b_add_new
.setIcon(KIcon("list-add"))
178 # Interact actions to buttons
179 self
.connect(self
.act_playpause
, SIGNAL("triggered(bool)"), self
.b_playpause
, SLOT("setChecked(bool)"))
180 self
.connect(self
.b_playpause
, SIGNAL("clicked(bool)"), self
.act_playpause
, SLOT("setChecked(bool)"))
182 # Custom toolbar and statusbar
183 self
.connect(self
.act_show_fake_toolbar
, SIGNAL("triggered(bool)"), self
.toolbarW
, SLOT("setVisible(bool)"))
184 self
.connect(self
.act_show_fake_statusbar
, SIGNAL("triggered(bool)"), self
.statusbarW
, SLOT("setVisible(bool)"))
186 # Connect actions to functions
187 self
.connect(self
.act_render
, SIGNAL("triggered()"), self
.renderW
)
188 self
.connect(self
.act_playpause
, SIGNAL("triggered(bool)"), self
.jackPlayPause
)
189 self
.connect(self
.act_stop
, SIGNAL("triggered()"), self
.jackStop
)
190 self
.connect(self
.act_backwards
, SIGNAL("triggered()"), self
.jackBackwards
)
191 self
.connect(self
.act_forwards
, SIGNAL("triggered()"), self
.jackForwards
)
192 self
.connect(self
.act_bf_16
, SIGNAL("triggered()"), partial(self
.setBufferSize
, 16))
193 self
.connect(self
.act_bf_32
, SIGNAL("triggered()"), partial(self
.setBufferSize
, 32))
194 self
.connect(self
.act_bf_64
, SIGNAL("triggered()"), partial(self
.setBufferSize
, 64))
195 self
.connect(self
.act_bf_128
, SIGNAL("triggered()"), partial(self
.setBufferSize
, 128))
196 self
.connect(self
.act_bf_256
, SIGNAL("triggered()"), partial(self
.setBufferSize
, 256))
197 self
.connect(self
.act_bf_512
, SIGNAL("triggered()"), partial(self
.setBufferSize
, 512))
198 self
.connect(self
.act_bf_1024
, SIGNAL("triggered()"), partial(self
.setBufferSize
, 1024))
199 self
.connect(self
.act_bf_2048
, SIGNAL("triggered()"), partial(self
.setBufferSize
, 2048))
200 self
.connect(self
.act_bf_4096
, SIGNAL("triggered()"), partial(self
.setBufferSize
, 4096))
201 self
.connect(self
.act_import_ladish
, SIGNAL("triggered()"), self
.importLadishStudio
)
203 # Connect tab-buttons to pages
204 self
.connect(self
.tb_main
, SIGNAL("clicked()"), partial(self
.changeTabButton
, "main"))
205 self
.connect(self
.tb_apps
, SIGNAL("clicked()"), partial(self
.changeTabButton
, "apps"))
206 self
.connect(self
.tb_connections
, SIGNAL("clicked()"), partial(self
.changeTabButton
, "connections"))
207 self
.connect(self
.tb_mixer
, SIGNAL("clicked()"), partial(self
.changeTabButton
, "mixer"))
209 # Connect buttons to functions
210 self
.connect(self
.b_playpause
, SIGNAL("clicked(bool)"), self
.jackPlayPause
)
211 self
.connect(self
.b_stop
, SIGNAL("clicked()"), self
.jackStop
)
212 self
.connect(self
.b_backwards
, SIGNAL("clicked()"), self
.jackBackwards
)
213 self
.connect(self
.b_forwards
, SIGNAL("clicked()"), self
.jackForwards
)
214 self
.connect(self
.b_open
, SIGNAL("clicked()"), self
.NIY
)
215 self
.connect(self
.b_save
, SIGNAL("clicked()"), self
.saveProject
)
216 self
.connect(self
.b_save_as
, SIGNAL("clicked()"), self
.saveAs
)
217 self
.connect(self
.b_render
, SIGNAL("clicked()"), self
.renderW
)
218 self
.connect(self
.b_add_new
, SIGNAL("clicked()"), self
.addCustom
)
220 # Connect other stuff
221 self
.connect(self
.cpuTimer
, SIGNAL("timeout()"), self
.updateDSPLoad
)
222 self
.connect(self
.cpuTimer
, SIGNAL("timeout()"), self
.updateJackVars
)
223 self
.connect(self
.jackTimer
, SIGNAL("timeout()"), self
.updateTransport
)
224 self
.connect(self
.jackTimer
, SIGNAL("timeout()"), self
.updateXruns
) # FIXME - get xrun callback instead!
225 self
.connect(self
.transportSlider
, SIGNAL("sliderReleased()"), self
.updateSlider
)
227 shared
.loopBus
.add_signal_receiver(self
.updateJackDBus
, sender_keyword
='sender', destination_keyword
='dest', interface_keyword
='interface',
228 member_keyword
='member', path_keyword
='path')
231 # Set-up the GUI (Part 1, create the KDE stuff)
232 self
.setupGUI(QSize(600, 400), KXmlGuiWindow
.Default
, shared
.Path
+"/klaudia.rc")
234 # Set-up the GUI (Part 2, set custom menu, tool-bar and status-bar)
235 self
.menuLayout
.addWidget(self
.menuBar())
236 self
.toolBar().deleteLater()
237 self
.statusBar().setVisible(False)
238 self
.actionCollection().action(u
'options_configure_toolbars').setVisible(False)
239 self
.actionCollection().action(u
'options_show_statusbar').setVisible(False)
240 self
.connect(self
.statusBar(), SIGNAL("messageChanged(QString)"), self
.sbW_msg_label
, SLOT("setText(QString)"))
242 # Set-up the GUI (Part 3, Load pages)
243 self
.w_main
= tb_main
.MainW()
244 self
.w_apps
= tb_apps
.AppsW()
245 self
.w_connections
= tb_connections
.ConnectionsW()
246 self
.w_mixer
= tb_mixer
.MixerW()
247 self
.w_main
.setVisible(False)
248 self
.w_apps
.setVisible(False)
249 #self.w_connections.setVisible(False)
250 self
.w_mixer
.setVisible(False)
252 # Set-up the GUI (Part 4, set default page)
253 #self.w_main.setVisible(True)
254 self
.centralW
.layout().addWidget(self
.w_connections
)
255 self
.tb_connections
.setChecked(True)
256 self
.currentTabPage
= "connections"
258 # Set-up the GUI (Part 5, update variables)
259 self
.updateJackVars()
262 self
.updateTransport()
266 KMessageBox
.sorry(self
, i18n("NOT IMPLEMENTED YET"), i18n("Sorry"))
269 tb_main
.NewAppW().exec_()
271 def saveProject(self
):
272 url
= shared
.p_folder
273 filename
= QFile(url
+"/project.klaudia")
274 if not filename
.open(QIODevice
.WriteOnly
):
275 KMessageBox
.error(self
, i18n("Cannot write to selected directory - Permission Denied"), i18n("Error"))
278 #raise IOError, unicode(filename.errorString())
279 stream
= QTextStream(filename
)
280 stream
.setCodec("UTF-8")
281 stream
<< ("<?xml version='1.0' encoding='UTF-8'?>\n"
282 "<!DOCTYPE KLAUDIA>\n"
283 "<KLAUDIA VERSION='0.1'>\n"
286 " <Author>%s</Author>\n"
287 " <Composer>%s</Composer>\n"
288 " <Album>%s</Album>\n"
289 " <Track>%s</Track>\n"
291 " <Time-Start>%s</Time-Start>\n"
292 " <Time-End>%s</Time-End>\n"
295 " <Project-Name>%s</Project-Name>\n"
297 "</KLAUDIA>\n" % (shared
.p_name
, shared
.p_author
, shared
.p_composer
,
298 shared
.p_album
, shared
.p_track
, shared
.p_bpm
, shared
.p_time_start
, shared
.p_time_end
, shared
.ladish_project_name
))
299 iconfile
= open(url
+"/.directory", "w")
300 iconfile
.write("[Desktop Entry]\nIcon=%s\n" % shared
.p_icon
)
302 shared
.studioBus
.Save()
305 pUrl
= KFileDialog
.getExistingDirectoryUrl(KUrl(shared
.p_folder
), self
, i18n("Open Klaudia Project Folder"))
306 if not pUrl
.fileName():
308 elif not pUrl
.isLocalFile():
309 KMessageBox
.error(self
, i18n("The Selected folder cannot be used for a Klaudia project!"), i18n("Error"))
311 shared
.p_folder
= str(pUrl
.toLocalFile())
314 def jackPlayPause(self
, play
):
316 jack
.transport_start()
317 self
.act_playpause
.setText("&Pause")
318 self
.act_playpause
.setIcon(KIcon("media-playback-pause"))
319 self
.b_playpause
.setIcon(KIcon("media-playback-pause"))
321 jack
.transport_stop()
322 self
.act_playpause
.setText("&Play")
323 self
.act_playpause
.setIcon(KIcon("media-playback-start"))
324 self
.b_playpause
.setIcon(KIcon("media-playback-start"))
327 self
.jackPlayPause(False)
328 self
.b_playpause
.setChecked(False)
329 jack
.transport_locate(0)
330 #jack.transport_locate(shared.Globals.sliderMinValue*jack.get_sample_rate())
332 def jackBackwards(self
):
333 pos
= int(jack
.get_current_transport_frame()) - 100000
335 jack
.transport_locate(0)
337 jack
.transport_locate(pos
)
339 def jackForwards(self
):
340 jack
.transport_locate(jack
.get_current_transport_frame()+100000)
342 def setBufferSize(self
, size
):
343 shared
.jackBus
.SetBufferSize(UInt32(size
))
344 for i
in range(len(self
.act_bf_list
)):
345 if (self
.act_bf_list
[i
].text().replace("&","") == str(size
)):
346 self
.act_bf_list
[i
].setChecked(True)
348 self
.act_bf_list
[i
].setChecked(False)
350 def changeTabButton(self
, button
):
351 if (button
!= self
.currentTabPage
):
352 self
.currentTabPage
= button
353 # Remove central widget
354 self
.centralW
.layout().takeAt(0)
355 # Restore all button states
356 self
.tb_main
.setChecked(False)
357 self
.tb_apps
.setChecked(False)
358 self
.tb_connections
.setChecked(False)
359 self
.tb_mixer
.setChecked(False)
360 # Hide all pages (we only need one)
361 self
.w_main
.setVisible(False)
362 self
.w_apps
.setVisible(False)
363 self
.w_connections
.setVisible(False)
364 self
.w_mixer
.setVisible(False)
365 if (button
== "main"):
366 self
.tb_main
.setChecked(True)
367 wToLoad
= self
.w_main
368 elif (button
== "apps"):
369 self
.tb_apps
.setChecked(True)
370 wToLoad
= self
.w_apps
371 elif (button
== "connections"):
372 self
.tb_connections
.setChecked(True)
373 wToLoad
= self
.w_connections
374 elif (button
== "mixer"):
375 self
.tb_mixer
.setChecked(True)
376 wToLoad
= self
.w_mixer
377 self
.centralW
.layout().addWidget(wToLoad
)
378 wToLoad
.setVisible(True)
380 if (button
== "main"): self
.tb_main
.setChecked(True)
381 elif (button
== "apps"): self
.tb_apps
.setChecked(True)
382 elif (button
== "connections"): self
.tb_connections
.setChecked(True)
383 elif (button
== "mixer"): self
.tb_mixer
.setChecked(True)
386 def updateTransport(self
):
387 state
= jack
.get_transport_state()
388 frame
= jack
.get_current_transport_frame()
389 rate
= int(shared
.jackBus
.GetSampleRate())
393 self
.b_playpause
.setChecked(False)
394 self
.act_playpause
.setText("&Play")
395 self
.act_playpause
.setIcon(KIcon("media-playback-start"))
396 self
.b_playpause
.setIcon(KIcon("media-playback-start"))
397 elif (state
== 1 or state
== 2):
398 self
.b_playpause
.setChecked(True)
399 self
.act_playpause
.setText("&Pause")
400 self
.act_playpause
.setIcon(KIcon("media-playback-pause"))
401 self
.b_playpause
.setIcon(KIcon("media-playback-pause"))
406 mins
= (time
/ 60) % 60
407 hrs
= (time
/ 3600) % 60
408 secH
= minH
= hrsH
= ""
409 if secs
< 10: secH
= "0"
410 if mins
< 10: minH
= "0"
411 if hrs
< 10: hrsH
= "0"
412 self
.l_time
.setText(hrsH
+str(hrs
)+":"+minH
+str(mins
)+":"+secH
+str(secs
))
415 if not self
.transportSlider
.isSliderDown():
416 self
.transportSlider
.setMinimum(shared
.Globals
.sliderMinValue
)
417 self
.transportSlider
.setMaximum(shared
.Globals
.sliderMaxValue
)
418 self
.transportSlider
.setValue(time
)
420 def updateXruns(self
):
421 xruns
= str(shared
.jackBus
.GetXruns())
422 self
.sbW_drops_count
.setText(xruns
)
424 def updateDSPLoad(self
):
425 load
= int(shared
.jackBus
.GetLoad())
426 self
.sbW_cpu_bar
.setValue(load
)
428 def updateSlider(self
):
429 value
= self
.transportSlider
.value()
430 jack
.transport_locate(value
*int(shared
.jackBus
.GetSampleRate()))
432 def updateJackVars(self
):
433 self
.sbW_jack_SampleRate
.setText(str(shared
.jackBus
.GetSampleRate())+" Hz")
434 self
.sbW_jack_BufferSize
.setText(str(shared
.jackBus
.GetBufferSize()))
435 if (bool(shared
.jackBus
.IsRealtime())):
436 self
.sbW_jack_RT
.setText("RT")
438 self
.sbW_jack_RT
.setText("")
440 def updateSizes(self
):
441 self
.sbW_msg
.setMinimumSize(self
.width()/4, 16)
442 self
.sbW_msg
.setMaximumSize(self
.width()/4, 16)
443 self
.sbW_jack
.setMaximumSize(self
.width()/3, 16)
444 self
.sbW_cpu
.setMaximumSize(self
.width()/4, 16)
445 self
.sbW_drops
.setMaximumSize(self
.width()/8, 16)
447 def resizeEvent(self
, event
):
451 renderw
.KlaudiaRenderW().exec_()
453 def importLadishStudio(self
):
456 def updateJackDBus(self
, *args
, **kwds
):
457 if (kwds
['interface'] == "org.jackaudio.JackControl"):
458 self
.updateJackVars()
460 class StudiosW(QDialog
, ui_dia_studios
.Ui_StudiosW
):
461 def __init__(self
, *args
):
462 QDialog
.__init
__(self
, *args
)
465 self
.buttonBox
.button(QDialogButtonBox
.Ok
).setEnabled(False)
466 self
.listStudio
.setColumnWidth(0, 200)
468 self
.connect(self
, SIGNAL("accepted()"), self
.Done
)
469 self
.connect(self
.listStudio
, SIGNAL("currentCellChanged(int, int, int, int)"), self
.checkOkButton
)
471 list_of_ladish_studios
= shared
.controlBus
.GetStudioList()
472 for i
in range(len(list_of_ladish_studios
)):
473 name
= QTableWidgetItem(str(list_of_ladish_studios
[i
][0]))
474 time
= QTableWidgetItem(ctime(float(list_of_ladish_studios
[i
][1][u
'Modification Time'])))
475 self
.listStudio
.insertRow(i
)
476 self
.listStudio
.setItem(i
, 0, name
)
477 self
.listStudio
.setItem(i
, 1, time
)
479 self
.listStudio
.sortByColumn(0, Qt
.AscendingOrder
)
483 shared
.controlBus
.LoadStudio(str(self
.listStudio
.item(self
.listStudio
.currentRow(), 0).text()))
484 shared
.jackBus
.StartServer()
485 jack
.attach("Klaudia")
487 def checkOkButton(self
, row
, b
, c
, d
):
489 self
.buttonBox
.button(QDialogButtonBox
.Ok
).setEnabled(False)
491 self
.buttonBox
.button(QDialogButtonBox
.Ok
).setEnabled(True)