Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--TamTamEdit.activity/Edit/EditToolbars.py1484
-rw-r--r--TamTamEdit.activity/Edit/HitInterface.py195
-rw-r--r--TamTamEdit.activity/Edit/KeyboardInput.py97
-rw-r--r--TamTamEdit.activity/Edit/MainWindow.py2091
-rw-r--r--TamTamEdit.activity/Edit/NoteInterface.py345
-rw-r--r--TamTamEdit.activity/Edit/Properties.py808
-rw-r--r--TamTamEdit.activity/Edit/TrackInterface.py1364
-rw-r--r--TamTamEdit.activity/Edit/TuneInterface.py645
-rw-r--r--TamTamEdit.activity/Edit/__init__.py0
-rw-r--r--TamTamEdit.activity/Edit/rm/BackgroundView.py501
-rw-r--r--TamTamEdit.activity/Edit/rm/NoteView.py253
-rw-r--r--TamTamEdit.activity/Edit/rm/PageBankView.py85
-rw-r--r--TamTamEdit.activity/Edit/rm/PageView.py65
-rw-r--r--TamTamEdit.activity/Edit/rm/PositionIndicator.py47
-rw-r--r--TamTamEdit.activity/Edit/rm/TrackView.py263
-rw-r--r--TamTamEdit.activity/Edit/rm/TunePageView.py17
-rw-r--r--TamTamEdit.activity/Edit/rm/TuneView.py123
-rw-r--r--TamTamEdit.activity/NEWS71
-rw-r--r--TamTamEdit.activity/TamTam.py1229
-rw-r--r--TamTamEdit.activity/activity/activity-tamtamedit.svg22
-rw-r--r--TamTamEdit.activity/activity/activity.info6
l---------TamTamEdit.activity/common1
-rw-r--r--TamTamEdit.activity/icons/XYBut.svg14
-rw-r--r--TamTamEdit.activity/icons/XYButDown.svg14
-rw-r--r--TamTamEdit.activity/icons/XYButDownClick.svg12
-rwxr-xr-xTamTamEdit.activity/icons/accept.svg18
-rw-r--r--TamTamEdit.activity/icons/add.svg11
-rw-r--r--TamTamEdit.activity/icons/arrow-down.svg7
-rw-r--r--TamTamEdit.activity/icons/arrow-up.svg7
-rwxr-xr-xTamTamEdit.activity/icons/cancel.svg11
-rw-r--r--TamTamEdit.activity/icons/dice.svg36
-rw-r--r--TamTamEdit.activity/icons/diceB.svg27
-rw-r--r--TamTamEdit.activity/icons/duplicate.svg14
-rw-r--r--TamTamEdit.activity/icons/edit-brush.svg16
-rw-r--r--TamTamEdit.activity/icons/edit-pencil.svg21
-rw-r--r--TamTamEdit.activity/icons/edit-pointer.svg9
-rw-r--r--TamTamEdit.activity/icons/grid.svg26
-rw-r--r--TamTamEdit.activity/icons/notedur.svg22
-rw-r--r--TamTamEdit.activity/icons/props.svg25
-rw-r--r--TamTamEdit.activity/icons/recordK.svg16
-rw-r--r--TamTamEdit.activity/icons/recordO.svg18
-rw-r--r--TamTamEdit.activity/icons/sideR.svg19
-rw-r--r--TamTamEdit.activity/icons/sideW.svg19
-rw-r--r--TamTamEdit.activity/icons/tam-help.svg18
-rw-r--r--TamTamEdit.activity/icons/tempo1.svg8
-rw-r--r--TamTamEdit.activity/icons/tempo2.svg9
-rw-r--r--TamTamEdit.activity/icons/tempo3.svg8
-rw-r--r--TamTamEdit.activity/icons/tempo4.svg9
-rw-r--r--TamTamEdit.activity/icons/tempo5.svg9
-rw-r--r--TamTamEdit.activity/icons/tempo6.svg11
-rw-r--r--TamTamEdit.activity/icons/tempo7.svg9
-rw-r--r--TamTamEdit.activity/icons/tempo8.svg10
-rw-r--r--TamTamEdit.activity/icons/updownR.svg19
-rw-r--r--TamTamEdit.activity/icons/updownW.svg19
-rw-r--r--TamTamEdit.activity/icons/voltemp.svg16
-rw-r--r--TamTamEdit.activity/icons/volume0.svg10
-rw-r--r--TamTamEdit.activity/icons/volume1.svg12
-rw-r--r--TamTamEdit.activity/icons/volume2.svg14
-rw-r--r--TamTamEdit.activity/icons/volume3.svg16
-rw-r--r--TamTamEdit.activity/setup.py21
-rw-r--r--TamTamJam.activity/Jam/Block.py895
-rw-r--r--TamTamJam.activity/Jam/Desktop.py466
-rw-r--r--TamTamJam.activity/Jam/Fillin.py116
-rw-r--r--TamTamJam.activity/Jam/GenRythm.py80
-rw-r--r--TamTamJam.activity/Jam/JamMain.py1112
-rw-r--r--TamTamJam.activity/Jam/Parasite.py349
-rw-r--r--TamTamJam.activity/Jam/Picker.py422
-rw-r--r--TamTamJam.activity/Jam/Popup.py1397
-rw-r--r--TamTamJam.activity/Jam/RythmGenerator.py77
-rw-r--r--TamTamJam.activity/Jam/Toolbars.py129
-rw-r--r--TamTamJam.activity/Jam/__init__.py0
-rw-r--r--TamTamJam.activity/NEWS71
-rw-r--r--TamTamJam.activity/TamTam.py1229
-rw-r--r--TamTamJam.activity/actvity/activity-tamtamjam.svg24
-rw-r--r--TamTamJam.activity/actvity/activity.info6
l---------TamTamJam.activity/common1
-rw-r--r--TamTamJam.activity/icons/XYBut.svg14
-rw-r--r--TamTamJam.activity/icons/XYButDown.svg14
-rw-r--r--TamTamJam.activity/icons/XYButDownClick.svg12
-rwxr-xr-xTamTamJam.activity/icons/accept.svg18
-rwxr-xr-xTamTamJam.activity/icons/cancel.svg11
-rw-r--r--TamTamJam.activity/icons/preset1.svg12
-rw-r--r--TamTamJam.activity/icons/preset10.svg15
-rw-r--r--TamTamJam.activity/icons/preset2.svg14
-rw-r--r--TamTamJam.activity/icons/preset3.svg16
-rw-r--r--TamTamJam.activity/icons/preset4.svg14
-rw-r--r--TamTamJam.activity/icons/preset5.svg13
-rw-r--r--TamTamJam.activity/icons/preset6.svg17
-rw-r--r--TamTamJam.activity/icons/preset7.svg13
-rw-r--r--TamTamJam.activity/icons/preset8.svg17
-rw-r--r--TamTamJam.activity/icons/preset9.svg17
-rw-r--r--TamTamJam.activity/icons/tam-help.svg18
-rw-r--r--TamTamJam.activity/icons/tempo1.svg8
-rw-r--r--TamTamJam.activity/icons/tempo2.svg9
-rw-r--r--TamTamJam.activity/icons/tempo3.svg8
-rw-r--r--TamTamJam.activity/icons/tempo4.svg9
-rw-r--r--TamTamJam.activity/icons/tempo5.svg9
-rw-r--r--TamTamJam.activity/icons/tempo6.svg11
-rw-r--r--TamTamJam.activity/icons/tempo7.svg9
-rw-r--r--TamTamJam.activity/icons/tempo8.svg10
-rw-r--r--TamTamJam.activity/icons/volume0.svg10
-rw-r--r--TamTamJam.activity/icons/volume1.svg12
-rw-r--r--TamTamJam.activity/icons/volume2.svg14
-rw-r--r--TamTamJam.activity/icons/volume3.svg16
-rw-r--r--TamTamJam.activity/setup.py21
-rw-r--r--TamTamMini.activity/Mini/Fillin.py105
-rw-r--r--TamTamMini.activity/Mini/GenRythm.py80
-rw-r--r--TamTamMini.activity/Mini/KeyboardStandAlone.py142
-rwxr-xr-xTamTamMini.activity/Mini/Loop.py210
-rw-r--r--TamTamMini.activity/Mini/MiniSequencer.py139
-rw-r--r--TamTamMini.activity/Mini/NoteStdAlone.py103
-rw-r--r--TamTamMini.activity/Mini/RythmGenerator.py77
-rw-r--r--TamTamMini.activity/Mini/__init__.py0
-rwxr-xr-xTamTamMini.activity/Mini/lps.py242
-rw-r--r--TamTamMini.activity/Mini/miniTamTamMain.py823
-rw-r--r--TamTamMini.activity/Mini/miniToolbars.py152
-rw-r--r--TamTamMini.activity/NEWS71
-rw-r--r--TamTamMini.activity/TamTamMini.py175
-rw-r--r--TamTamMini.activity/activity/activity-tamtam.svg24
-rw-r--r--TamTamMini.activity/activity/activity.info6
l---------TamTamMini.activity/common1
-rw-r--r--TamTamMini.activity/icons/keyrec.svg13
-rw-r--r--TamTamMini.activity/icons/loop.svg13
-rw-r--r--TamTamMini.activity/icons/micrec1.svg11
-rw-r--r--TamTamMini.activity/icons/micrec2.svg13
-rw-r--r--TamTamMini.activity/icons/micrec3.svg15
-rw-r--r--TamTamMini.activity/icons/micrec4.svg13
-rw-r--r--TamTamMini.activity/icons/micrec5.svg14
-rw-r--r--TamTamMini.activity/icons/micrec6.svg15
-rw-r--r--TamTamMini.activity/icons/minusrec.svg15
-rw-r--r--TamTamMini.activity/icons/overrec.svg15
-rw-r--r--TamTamMini.activity/icons/rec1.svg11
-rw-r--r--TamTamMini.activity/icons/rec2.svg13
-rw-r--r--TamTamMini.activity/icons/rec3.svg15
-rw-r--r--TamTamMini.activity/icons/rec4.svg13
-rw-r--r--TamTamMini.activity/icons/rec5.svg14
-rw-r--r--TamTamMini.activity/icons/rec6.svg15
-rw-r--r--TamTamMini.activity/setup.py21
-rw-r--r--TamTamSynthLab.activity/NEWS71
-rwxr-xr-xTamTamSynthLab.activity/SynthLab/SynthLabConstants.py374
-rw-r--r--TamTamSynthLab.activity/SynthLab/SynthLabToolbars.py170
-rw-r--r--TamTamSynthLab.activity/SynthLab/SynthLabWindow.py1395
-rwxr-xr-xTamTamSynthLab.activity/SynthLab/SynthObjectsParameters.py42
-rwxr-xr-xTamTamSynthLab.activity/SynthLab/__init__.py0
-rw-r--r--TamTamSynthLab.activity/TamTam.py1229
-rw-r--r--TamTamSynthLab.activity/activity/activity-tamtamsynthlab.svg12
-rw-r--r--TamTamSynthLab.activity/activity/activity.info6
l---------TamTamSynthLab.activity/common1
-rw-r--r--TamTamSynthLab.activity/icons/XYBut.svg14
-rw-r--r--TamTamSynthLab.activity/icons/XYButDown.svg14
-rw-r--r--TamTamSynthLab.activity/icons/XYButDownClick.svg12
-rwxr-xr-xTamTamSynthLab.activity/icons/accept.svg18
-rwxr-xr-xTamTamSynthLab.activity/icons/cancel.svg11
-rw-r--r--TamTamSynthLab.activity/icons/preset1.svg12
-rw-r--r--TamTamSynthLab.activity/icons/preset10.svg15
-rw-r--r--TamTamSynthLab.activity/icons/preset2.svg14
-rw-r--r--TamTamSynthLab.activity/icons/preset3.svg16
-rw-r--r--TamTamSynthLab.activity/icons/preset4.svg14
-rw-r--r--TamTamSynthLab.activity/icons/preset5.svg13
-rw-r--r--TamTamSynthLab.activity/icons/preset6.svg17
-rw-r--r--TamTamSynthLab.activity/icons/preset7.svg13
-rw-r--r--TamTamSynthLab.activity/icons/preset8.svg17
-rw-r--r--TamTamSynthLab.activity/icons/preset9.svg17
-rw-r--r--TamTamSynthLab.activity/icons/rec1.svg11
-rw-r--r--TamTamSynthLab.activity/icons/rec2.svg13
-rw-r--r--TamTamSynthLab.activity/icons/rec3.svg15
-rw-r--r--TamTamSynthLab.activity/icons/rec4.svg13
-rw-r--r--TamTamSynthLab.activity/icons/rec5.svg14
-rw-r--r--TamTamSynthLab.activity/icons/rec6.svg15
-rwxr-xr-xTamTamSynthLab.activity/icons/sl-addSynth+.svg30
-rw-r--r--TamTamSynthLab.activity/icons/sl-addSynth-menu.svg15
-rwxr-xr-xTamTamSynthLab.activity/icons/sl-adsr+.svg21
-rw-r--r--TamTamSynthLab.activity/icons/sl-adsr-menu.svg20
-rwxr-xr-xTamTamSynthLab.activity/icons/sl-buzz+.svg30
-rw-r--r--TamTamSynthLab.activity/icons/sl-buzz-menu.svg15
-rwxr-xr-xTamTamSynthLab.activity/icons/sl-chorus+.svg139
-rw-r--r--TamTamSynthLab.activity/icons/sl-chorus-menu.svg101
-rw-r--r--TamTamSynthLab.activity/icons/sl-distort+.svg30
-rw-r--r--TamTamSynthLab.activity/icons/sl-distort-menu.svg21
-rw-r--r--TamTamSynthLab.activity/icons/sl-eq4band+.svg51
-rw-r--r--TamTamSynthLab.activity/icons/sl-eq4band-menu.svg38
-rw-r--r--TamTamSynthLab.activity/icons/sl-filter+.svg32
-rw-r--r--TamTamSynthLab.activity/icons/sl-filter-menu.svg21
-rwxr-xr-xTamTamSynthLab.activity/icons/sl-fm+.svg43
-rw-r--r--TamTamSynthLab.activity/icons/sl-fm-menu.svg30
-rwxr-xr-xTamTamSynthLab.activity/icons/sl-grain+.svg205
-rw-r--r--TamTamSynthLab.activity/icons/sl-grain-menu.svg190
-rw-r--r--TamTamSynthLab.activity/icons/sl-harmon+.svg30
-rw-r--r--TamTamSynthLab.activity/icons/sl-harmon-menu.svg21
-rwxr-xr-xTamTamSynthLab.activity/icons/sl-lfo+.svg14
-rw-r--r--TamTamSynthLab.activity/icons/sl-lfo-menu.svg20
-rwxr-xr-xTamTamSynthLab.activity/icons/sl-noise+.svg233
-rw-r--r--TamTamSynthLab.activity/icons/sl-noise-menu.svg222
-rwxr-xr-xTamTamSynthLab.activity/icons/sl-pluck+.svg73
-rw-r--r--TamTamSynthLab.activity/icons/sl-pluck-menu.svg62
-rwxr-xr-xTamTamSynthLab.activity/icons/sl-rand+.svg17
-rw-r--r--TamTamSynthLab.activity/icons/sl-rand-menu.svg20
-rw-r--r--TamTamSynthLab.activity/icons/sl-reset.svg13
-rw-r--r--TamTamSynthLab.activity/icons/sl-reverb+.svg39
-rw-r--r--TamTamSynthLab.activity/icons/sl-reverb-menu.svg28
-rw-r--r--TamTamSynthLab.activity/icons/sl-ring+.svg30
-rw-r--r--TamTamSynthLab.activity/icons/sl-ring-menu.svg21
-rwxr-xr-xTamTamSynthLab.activity/icons/sl-sample+.svg34
-rw-r--r--TamTamSynthLab.activity/icons/sl-sample-menu.svg24
-rwxr-xr-xTamTamSynthLab.activity/icons/sl-speaker.svg13
-rwxr-xr-xTamTamSynthLab.activity/icons/sl-trackpadX+.svg22
-rw-r--r--TamTamSynthLab.activity/icons/sl-trackpadX-menu.svg21
-rwxr-xr-xTamTamSynthLab.activity/icons/sl-trackpadY+.svg22
-rw-r--r--TamTamSynthLab.activity/icons/sl-trackpadY-menu.svg21
-rwxr-xr-xTamTamSynthLab.activity/icons/sl-vco+.svg31
-rw-r--r--TamTamSynthLab.activity/icons/sl-vco-menu.svg19
-rwxr-xr-xTamTamSynthLab.activity/icons/sl-voice+.svg28
-rw-r--r--TamTamSynthLab.activity/icons/sl-voice-menu.svg21
-rw-r--r--TamTamSynthLab.activity/icons/sl-wguide+.svg32
-rw-r--r--TamTamSynthLab.activity/icons/sl-wguide-menu.svg25
-rw-r--r--TamTamSynthLab.activity/icons/tam-help.svg18
-rw-r--r--TamTamSynthLab.activity/setup.py21
-rw-r--r--common/Config.py599
-rwxr-xr-xcommon/Generation/Drunk.py162
-rwxr-xr-xcommon/Generation/GenerationConstants.py184
-rwxr-xr-xcommon/Generation/GenerationParametersWindow.py383
-rw-r--r--common/Generation/GenerationPitch.py40
-rw-r--r--common/Generation/GenerationRythm.py213
-rwxr-xr-xcommon/Generation/Generator.py167
-rwxr-xr-xcommon/Generation/Utils.py74
-rw-r--r--common/Generation/VariationPitch.py82
-rw-r--r--common/Generation/VariationRythm.py74
-rw-r--r--common/Generation/__init__.py0
-rw-r--r--common/Generation/bList.py97
-rw-r--r--common/Resources/Desktops/desktop0206
-rw-r--r--common/Resources/Desktops/desktop1206
-rw-r--r--common/Resources/Desktops/desktop2206
-rw-r--r--common/Resources/Desktops/desktop3206
-rw-r--r--common/Resources/Desktops/desktop4206
-rw-r--r--common/Resources/Desktops/desktop5206
-rw-r--r--common/Resources/Desktops/desktop6206
-rw-r--r--common/Resources/Desktops/desktop7206
-rw-r--r--common/Resources/Desktops/desktop8206
-rw-r--r--common/Resources/Desktops/desktop9206
-rw-r--r--common/Resources/Images/TamTam.pngbin0 -> 12771 bytes
-rw-r--r--common/Resources/Images/acguit.pngbin0 -> 10779 bytes
-rw-r--r--common/Resources/Images/acguitsel.pngbin0 -> 9552 bytes
-rwxr-xr-xcommon/Resources/Images/alarm.pngbin0 -> 8794 bytes
-rwxr-xr-xcommon/Resources/Images/alarmsel.pngbin0 -> 8302 bytes
-rw-r--r--common/Resources/Images/all.pngbin0 -> 13361 bytes
-rw-r--r--common/Resources/Images/allsel.pngbin0 -> 14000 bytes
-rw-r--r--common/Resources/Images/animals.pngbin0 -> 15944 bytes
-rw-r--r--common/Resources/Images/animalssel.pngbin0 -> 16536 bytes
-rwxr-xr-xcommon/Resources/Images/arrowEditDown.pngbin0 -> 3058 bytes
-rwxr-xr-xcommon/Resources/Images/arrowEditDownDown.pngbin0 -> 3037 bytes
-rwxr-xr-xcommon/Resources/Images/arrowEditDownOver.pngbin0 -> 3058 bytes
-rw-r--r--common/Resources/Images/arrowEditLeft.pngbin0 -> 456 bytes
-rwxr-xr-xcommon/Resources/Images/arrowEditLeftDown.pngbin0 -> 3003 bytes
-rwxr-xr-xcommon/Resources/Images/arrowEditLeftOver.pngbin0 -> 3058 bytes
-rw-r--r--common/Resources/Images/arrowEditRight.pngbin0 -> 454 bytes
-rwxr-xr-xcommon/Resources/Images/arrowEditRightDown.pngbin0 -> 3014 bytes
-rwxr-xr-xcommon/Resources/Images/arrowEditRightOver.pngbin0 -> 3049 bytes
-rwxr-xr-xcommon/Resources/Images/arrowEditUp.pngbin0 -> 3045 bytes
-rwxr-xr-xcommon/Resources/Images/arrowEditUpDown.pngbin0 -> 3030 bytes
-rwxr-xr-xcommon/Resources/Images/arrowEditUpOver.pngbin0 -> 3046 bytes
-rw-r--r--common/Resources/Images/basse.pngbin0 -> 9265 bytes
-rw-r--r--common/Resources/Images/bassesel.pngbin0 -> 7934 bytes
-rw-r--r--common/Resources/Images/beat1.pngbin0 -> 679 bytes
-rw-r--r--common/Resources/Images/beat10.pngbin0 -> 1003 bytes
-rw-r--r--common/Resources/Images/beat11.pngbin0 -> 722 bytes
-rw-r--r--common/Resources/Images/beat2.pngbin0 -> 672 bytes
-rw-r--r--common/Resources/Images/beat3.pngbin0 -> 620 bytes
-rw-r--r--common/Resources/Images/beat4.pngbin0 -> 984 bytes
-rw-r--r--common/Resources/Images/beat5.pngbin0 -> 651 bytes
-rw-r--r--common/Resources/Images/beat6.pngbin0 -> 947 bytes
-rw-r--r--common/Resources/Images/beat7.pngbin0 -> 663 bytes
-rw-r--r--common/Resources/Images/beat8.pngbin0 -> 721 bytes
-rw-r--r--common/Resources/Images/beat9.pngbin0 -> 1005 bytes
-rw-r--r--common/Resources/Images/bird.pngbin0 -> 9311 bytes
-rw-r--r--common/Resources/Images/birdsel.pngbin0 -> 8048 bytes
-rw-r--r--common/Resources/Images/bottle.pngbin0 -> 8387 bytes
-rw-r--r--common/Resources/Images/bottlesel.pngbin0 -> 7674 bytes
-rwxr-xr-xcommon/Resources/Images/bubbles.pngbin0 -> 9571 bytes
-rwxr-xr-xcommon/Resources/Images/bubblessel.pngbin0 -> 8695 bytes
-rwxr-xr-xcommon/Resources/Images/byke.pngbin0 -> 14257 bytes
-rwxr-xr-xcommon/Resources/Images/bykesel.pngbin0 -> 12502 bytes
-rwxr-xr-xcommon/Resources/Images/camera.pngbin0 -> 9598 bytes
-rwxr-xr-xcommon/Resources/Images/camerasel.pngbin0 -> 9467 bytes
-rwxr-xr-xcommon/Resources/Images/car.pngbin0 -> 7720 bytes
-rwxr-xr-xcommon/Resources/Images/carsel.pngbin0 -> 7354 bytes
-rw-r--r--common/Resources/Images/cat.pngbin0 -> 13255 bytes
-rw-r--r--common/Resources/Images/catsel.pngbin0 -> 12546 bytes
-rwxr-xr-xcommon/Resources/Images/cello.pngbin0 -> 12080 bytes
-rwxr-xr-xcommon/Resources/Images/cellosel.pngbin0 -> 10904 bytes
-rw-r--r--common/Resources/Images/check.pngbin0 -> 1862 bytes
-rw-r--r--common/Resources/Images/checkOff.svg7
-rw-r--r--common/Resources/Images/checkOn.svg14
-rwxr-xr-xcommon/Resources/Images/chimes.pngbin0 -> 8878 bytes
-rwxr-xr-xcommon/Resources/Images/chimessel.pngbin0 -> 8340 bytes
-rw-r--r--common/Resources/Images/clang.pngbin0 -> 10702 bytes
-rw-r--r--common/Resources/Images/clangsel.pngbin0 -> 10306 bytes
-rw-r--r--common/Resources/Images/clarinette.pngbin0 -> 5628 bytes
-rw-r--r--common/Resources/Images/clarinettesel.pngbin0 -> 4700 bytes
-rw-r--r--common/Resources/Images/cling.pngbin0 -> 14855 bytes
-rw-r--r--common/Resources/Images/clingsel.pngbin0 -> 13973 bytes
-rw-r--r--common/Resources/Images/complex1.pngbin0 -> 2084 bytes
-rw-r--r--common/Resources/Images/complex2.pngbin0 -> 1930 bytes
-rw-r--r--common/Resources/Images/complex3.pngbin0 -> 1862 bytes
-rw-r--r--common/Resources/Images/complex4.pngbin0 -> 1914 bytes
-rw-r--r--common/Resources/Images/complex5.pngbin0 -> 1890 bytes
-rw-r--r--common/Resources/Images/complex6.pngbin0 -> 1912 bytes
-rw-r--r--common/Resources/Images/complex7.pngbin0 -> 1938 bytes
-rw-r--r--common/Resources/Images/complex8.pngbin0 -> 1791 bytes
-rw-r--r--common/Resources/Images/concret.pngbin0 -> 9928 bytes
-rw-r--r--common/Resources/Images/concretsel.pngbin0 -> 10472 bytes
-rwxr-xr-xcommon/Resources/Images/crash.pngbin0 -> 8027 bytes
-rwxr-xr-xcommon/Resources/Images/crashsel.pngbin0 -> 7362 bytes
-rw-r--r--common/Resources/Images/dice.pngbin0 -> 11085 bytes
-rw-r--r--common/Resources/Images/diceProp.pngbin0 -> 2613 bytes
-rw-r--r--common/Resources/Images/dicePropSel.pngbin0 -> 2721 bytes
-rw-r--r--common/Resources/Images/diceblur.pngbin0 -> 13327 bytes
-rw-r--r--common/Resources/Images/diceinst.pngbin0 -> 8634 bytes
-rw-r--r--common/Resources/Images/diceinstsel.pngbin0 -> 8003 bytes
-rw-r--r--common/Resources/Images/didjeridu.pngbin0 -> 9087 bytes
-rw-r--r--common/Resources/Images/didjeridusel.pngbin0 -> 8296 bytes
-rw-r--r--common/Resources/Images/dog.pngbin0 -> 12824 bytes
-rw-r--r--common/Resources/Images/dogsel.pngbin0 -> 12298 bytes
-rw-r--r--common/Resources/Images/door.pngbin0 -> 10469 bytes
-rw-r--r--common/Resources/Images/doorsel.pngbin0 -> 10272 bytes
-rwxr-xr-xcommon/Resources/Images/dru0.pngbin0 -> 2313 bytes
-rwxr-xr-xcommon/Resources/Images/dru1.pngbin0 -> 2708 bytes
-rwxr-xr-xcommon/Resources/Images/dru2.pngbin0 -> 3293 bytes
-rwxr-xr-xcommon/Resources/Images/dru3.pngbin0 -> 4062 bytes
-rwxr-xr-xcommon/Resources/Images/dru4.pngbin0 -> 5015 bytes
-rw-r--r--common/Resources/Images/drum1kit.pngbin0 -> 14900 bytes
-rw-r--r--common/Resources/Images/drum1kitsel.pngbin0 -> 13729 bytes
-rw-r--r--common/Resources/Images/drum1kitselgen.pngbin0 -> 13887 bytes
-rw-r--r--common/Resources/Images/drum2kit.pngbin0 -> 14882 bytes
-rw-r--r--common/Resources/Images/drum2kitsel.pngbin0 -> 13869 bytes
-rw-r--r--common/Resources/Images/drum2kitselgen.pngbin0 -> 13964 bytes
-rw-r--r--common/Resources/Images/drum3kit.pngbin0 -> 15880 bytes
-rw-r--r--common/Resources/Images/drum3kitsel.pngbin0 -> 14679 bytes
-rw-r--r--common/Resources/Images/drum3kitselgen.pngbin0 -> 14801 bytes
-rw-r--r--common/Resources/Images/drum4kit.pngbin0 -> 12904 bytes
-rw-r--r--common/Resources/Images/drum4kitsel.pngbin0 -> 10888 bytes
-rw-r--r--common/Resources/Images/drum4kitselgen.pngbin0 -> 11047 bytes
-rw-r--r--common/Resources/Images/drum5kit.pngbin0 -> 14999 bytes
-rw-r--r--common/Resources/Images/drum5kitsel.pngbin0 -> 13580 bytes
-rw-r--r--common/Resources/Images/drum5kitselgen.pngbin0 -> 13712 bytes
-rw-r--r--common/Resources/Images/duck.pngbin0 -> 9694 bytes
-rw-r--r--common/Resources/Images/duck2.pngbin0 -> 11299 bytes
-rw-r--r--common/Resources/Images/duck2sel.pngbin0 -> 8960 bytes
-rw-r--r--common/Resources/Images/ducksel.pngbin0 -> 8652 bytes
-rw-r--r--common/Resources/Images/editTam.pngbin0 -> 10701 bytes
-rw-r--r--common/Resources/Images/editTamDown.pngbin0 -> 11789 bytes
-rw-r--r--common/Resources/Images/editTamOver.pngbin0 -> 9958 bytes
-rw-r--r--common/Resources/Images/electronic.pngbin0 -> 12476 bytes
-rw-r--r--common/Resources/Images/electronicsel.pngbin0 -> 13161 bytes
-rw-r--r--common/Resources/Images/flute.pngbin0 -> 6248 bytes
-rw-r--r--common/Resources/Images/flutesel.pngbin0 -> 5266 bytes
-rw-r--r--common/Resources/Images/gam.pngbin0 -> 12581 bytes
-rw-r--r--common/Resources/Images/gamsel.pngbin0 -> 11893 bytes
-rwxr-xr-xcommon/Resources/Images/generic.pngbin0 -> 8768 bytes
-rwxr-xr-xcommon/Resources/Images/genericsel.pngbin0 -> 8015 bytes
-rw-r--r--common/Resources/Images/guit.pngbin0 -> 9996 bytes
-rwxr-xr-xcommon/Resources/Images/guit2.pngbin0 -> 8352 bytes
-rwxr-xr-xcommon/Resources/Images/guit2sel.pngbin0 -> 7611 bytes
-rw-r--r--common/Resources/Images/guitsel.pngbin0 -> 8589 bytes
-rw-r--r--common/Resources/Images/harmonica.pngbin0 -> 9120 bytes
-rw-r--r--common/Resources/Images/harmonicasel.pngbin0 -> 8638 bytes
-rw-r--r--common/Resources/Images/harmonium.pngbin0 -> 13371 bytes
-rw-r--r--common/Resources/Images/harmoniumsel.pngbin0 -> 12936 bytes
-rwxr-xr-xcommon/Resources/Images/helpShow1.jpgbin0 -> 32969 bytes
-rwxr-xr-xcommon/Resources/Images/helpShow2.jpgbin0 -> 87871 bytes
-rwxr-xr-xcommon/Resources/Images/helpShow3.jpgbin0 -> 79367 bytes
-rwxr-xr-xcommon/Resources/Images/helpShow4.jpgbin0 -> 85694 bytes
-rwxr-xr-xcommon/Resources/Images/helpShow5.jpgbin0 -> 75364 bytes
-rwxr-xr-xcommon/Resources/Images/helpShow6.jpgbin0 -> 60244 bytes
-rwxr-xr-xcommon/Resources/Images/helpShow7.jpgbin0 -> 52708 bytes
-rwxr-xr-xcommon/Resources/Images/helpShow8.jpgbin0 -> 70312 bytes
-rwxr-xr-xcommon/Resources/Images/helpShow9.jpgbin0 -> 52295 bytes
-rw-r--r--common/Resources/Images/helpTam.pngbin0 -> 11789 bytes
-rw-r--r--common/Resources/Images/helpTamDown.pngbin0 -> 10701 bytes
-rw-r--r--common/Resources/Images/helpTamOver.pngbin0 -> 9958 bytes
-rw-r--r--common/Resources/Images/hit.pngbin0 -> 973 bytes
-rw-r--r--common/Resources/Images/hitSelected.pngbin0 -> 1171 bytes
-rw-r--r--common/Resources/Images/horse.pngbin0 -> 15048 bytes
-rw-r--r--common/Resources/Images/horsesel.pngbin0 -> 13520 bytes
-rwxr-xr-xcommon/Resources/Images/instr0.pngbin0 -> 1700 bytes
-rwxr-xr-xcommon/Resources/Images/instr1.pngbin0 -> 2032 bytes
-rwxr-xr-xcommon/Resources/Images/instr2.pngbin0 -> 2350 bytes
-rwxr-xr-xcommon/Resources/Images/instr3.pngbin0 -> 2748 bytes
-rwxr-xr-xcommon/Resources/Images/instr4.pngbin0 -> 3212 bytes
-rw-r--r--common/Resources/Images/jam-blockMask.pngbin0 -> 4242 bytes
-rw-r--r--common/Resources/Images/kalimba.pngbin0 -> 10500 bytes
-rw-r--r--common/Resources/Images/kalimbasel.pngbin0 -> 9964 bytes
-rw-r--r--common/Resources/Images/keyboard.pngbin0 -> 5602 bytes
-rw-r--r--common/Resources/Images/keyboardsel.pngbin0 -> 6201 bytes
-rw-r--r--common/Resources/Images/koto.pngbin0 -> 9666 bytes
-rw-r--r--common/Resources/Images/kotosel.pngbin0 -> 8872 bytes
-rw-r--r--common/Resources/Images/laugh.pngbin0 -> 14044 bytes
-rw-r--r--common/Resources/Images/laughsel.pngbin0 -> 13332 bytes
-rw-r--r--common/Resources/Images/mando.pngbin0 -> 8705 bytes
-rw-r--r--common/Resources/Images/mandosel.pngbin0 -> 7728 bytes
-rw-r--r--common/Resources/Images/marimba.pngbin0 -> 14598 bytes
-rw-r--r--common/Resources/Images/marimbasel.pngbin0 -> 13469 bytes
-rw-r--r--common/Resources/Images/marquis.pngbin0 -> 1341 bytes
-rw-r--r--common/Resources/Images/mic1.pngbin0 -> 4231 bytes
-rw-r--r--common/Resources/Images/mic1sel.pngbin0 -> 3988 bytes
-rw-r--r--common/Resources/Images/mic2.pngbin0 -> 4365 bytes
-rw-r--r--common/Resources/Images/mic2sel.pngbin0 -> 4122 bytes
-rw-r--r--common/Resources/Images/mic3.pngbin0 -> 4375 bytes
-rw-r--r--common/Resources/Images/mic3sel.pngbin0 -> 4145 bytes
-rw-r--r--common/Resources/Images/mic4.pngbin0 -> 4225 bytes
-rw-r--r--common/Resources/Images/mic4sel.pngbin0 -> 4062 bytes
-rw-r--r--common/Resources/Images/miniTam.pngbin0 -> 30569 bytes
-rw-r--r--common/Resources/Images/miniTamDown.pngbin0 -> 36042 bytes
-rw-r--r--common/Resources/Images/miniTamOver.pngbin0 -> 33015 bytes
-rw-r--r--common/Resources/Images/miniplay.pngbin0 -> 970 bytes
-rw-r--r--common/Resources/Images/mysounds.pngbin0 -> 7908 bytes
-rw-r--r--common/Resources/Images/mysoundssel.pngbin0 -> 8350 bytes
-rw-r--r--common/Resources/Images/note.pngbin0 -> 1034 bytes
-rw-r--r--common/Resources/Images/noteSelected.pngbin0 -> 1281 bytes
-rw-r--r--common/Resources/Images/ocarina.pngbin0 -> 6891 bytes
-rw-r--r--common/Resources/Images/ocarinasel.pngbin0 -> 6601 bytes
-rw-r--r--common/Resources/Images/ounk.pngbin0 -> 12569 bytes
-rw-r--r--common/Resources/Images/ounksel.pngbin0 -> 11792 bytes
-rw-r--r--common/Resources/Images/ow.pngbin0 -> 13340 bytes
-rw-r--r--common/Resources/Images/owsel.pngbin0 -> 12320 bytes
-rw-r--r--common/Resources/Images/pageThumbnailBG.pngbin0 -> 1154 bytes
-rw-r--r--common/Resources/Images/pageThumbnailBG0.pngbin0 -> 1068 bytes
-rw-r--r--common/Resources/Images/pageThumbnailBG1.pngbin0 -> 1073 bytes
-rw-r--r--common/Resources/Images/pageThumbnailBG2.pngbin0 -> 1037 bytes
-rw-r--r--common/Resources/Images/pageThumbnailBG3.pngbin0 -> 1094 bytes
-rwxr-xr-xcommon/Resources/Images/pageThumbnailBut0.pngbin0 -> 927 bytes
-rwxr-xr-xcommon/Resources/Images/pageThumbnailBut0Down.pngbin0 -> 945 bytes
-rwxr-xr-xcommon/Resources/Images/pageThumbnailBut1.pngbin0 -> 917 bytes
-rwxr-xr-xcommon/Resources/Images/pageThumbnailBut1Down.pngbin0 -> 937 bytes
-rwxr-xr-xcommon/Resources/Images/pageThumbnailBut2.pngbin0 -> 919 bytes
-rwxr-xr-xcommon/Resources/Images/pageThumbnailBut2Down.pngbin0 -> 944 bytes
-rwxr-xr-xcommon/Resources/Images/pageThumbnailBut3.pngbin0 -> 916 bytes
-rwxr-xr-xcommon/Resources/Images/pageThumbnailBut3Down.pngbin0 -> 934 bytes
-rw-r--r--common/Resources/Images/pageThumbnailMask.pngbin0 -> 500 bytes
-rw-r--r--common/Resources/Images/people.pngbin0 -> 17115 bytes
-rw-r--r--common/Resources/Images/peoplesel.pngbin0 -> 17596 bytes
-rw-r--r--common/Resources/Images/percussions.pngbin0 -> 15245 bytes
-rw-r--r--common/Resources/Images/percussionssel.pngbin0 -> 15815 bytes
-rw-r--r--common/Resources/Images/piano.pngbin0 -> 11422 bytes
-rw-r--r--common/Resources/Images/pianosel.pngbin0 -> 10785 bytes
-rwxr-xr-xcommon/Resources/Images/plane.pngbin0 -> 12105 bytes
-rwxr-xr-xcommon/Resources/Images/planesel.pngbin0 -> 10920 bytes
-rw-r--r--common/Resources/Images/reverb0.pngbin0 -> 890 bytes
-rw-r--r--common/Resources/Images/reverb1.pngbin0 -> 888 bytes
-rw-r--r--common/Resources/Images/reverb2.pngbin0 -> 939 bytes
-rw-r--r--common/Resources/Images/reverb3.pngbin0 -> 992 bytes
-rw-r--r--common/Resources/Images/reverb4.pngbin0 -> 1035 bytes
-rw-r--r--common/Resources/Images/reverb5.pngbin0 -> 1090 bytes
-rw-r--r--common/Resources/Images/rhodes.pngbin0 -> 10014 bytes
-rw-r--r--common/Resources/Images/rhodessel.pngbin0 -> 8396 bytes
-rw-r--r--common/Resources/Images/sampleBG.pngbin0 -> 36405 bytes
-rw-r--r--common/Resources/Images/sampleNoteMask.pngbin0 -> 2963 bytes
-rw-r--r--common/Resources/Images/saxo.pngbin0 -> 8445 bytes
-rw-r--r--common/Resources/Images/saxosel.pngbin0 -> 7424 bytes
-rw-r--r--common/Resources/Images/scrollBar.pngbin0 -> 584 bytes
-rw-r--r--common/Resources/Images/sheep.pngbin0 -> 11805 bytes
-rw-r--r--common/Resources/Images/sheepsel.pngbin0 -> 10623 bytes
-rw-r--r--common/Resources/Images/shenai.pngbin0 -> 6780 bytes
-rw-r--r--common/Resources/Images/shenaisel.pngbin0 -> 5808 bytes
-rw-r--r--common/Resources/Images/sitar.pngbin0 -> 9766 bytes
-rw-r--r--common/Resources/Images/sitarsel.pngbin0 -> 8536 bytes
-rwxr-xr-xcommon/Resources/Images/slap.pngbin0 -> 11935 bytes
-rwxr-xr-xcommon/Resources/Images/slapsel.pngbin0 -> 10959 bytes
-rw-r--r--common/Resources/Images/sliderDrum.pngbin0 -> 1294 bytes
-rw-r--r--common/Resources/Images/sliderEditTempo.pngbin0 -> 1272 bytes
-rw-r--r--common/Resources/Images/sliderEditVolume.pngbin0 -> 1242 bytes
-rw-r--r--common/Resources/Images/sliderInst1.pngbin0 -> 1284 bytes
-rw-r--r--common/Resources/Images/sliderInst2.pngbin0 -> 1252 bytes
-rw-r--r--common/Resources/Images/sliderInst3.pngbin0 -> 1263 bytes
-rw-r--r--common/Resources/Images/sliderInst4.pngbin0 -> 1272 bytes
-rw-r--r--common/Resources/Images/sliderbutbleu.pngbin0 -> 1744 bytes
-rw-r--r--common/Resources/Images/sliderbutjaune.pngbin0 -> 1770 bytes
-rw-r--r--common/Resources/Images/sliderbutred.pngbin0 -> 1562 bytes
-rw-r--r--common/Resources/Images/sliderbutvert.pngbin0 -> 1769 bytes
-rw-r--r--common/Resources/Images/sliderbutviolet.pngbin0 -> 1654 bytes
-rw-r--r--common/Resources/Images/sliderlong.pngbin0 -> 251 bytes
-rw-r--r--common/Resources/Images/slidershort.pngbin0 -> 244 bytes
-rw-r--r--common/Resources/Images/stop.pngbin0 -> 807 bytes
-rw-r--r--common/Resources/Images/strings.pngbin0 -> 11888 bytes
-rw-r--r--common/Resources/Images/stringssel.pngbin0 -> 12428 bytes
-rw-r--r--common/Resources/Images/synthTam.pngbin0 -> 7525 bytes
-rw-r--r--common/Resources/Images/synthTamDown.pngbin0 -> 8297 bytes
-rw-r--r--common/Resources/Images/synthTamOver.pngbin0 -> 7569 bytes
-rw-r--r--common/Resources/Images/synthlabMask.pngbin0 -> 4884 bytes
-rw-r--r--common/Resources/Images/tchiwo.pngbin0 -> 10240 bytes
-rw-r--r--common/Resources/Images/tchiwosel.pngbin0 -> 9545 bytes
-rw-r--r--common/Resources/Images/tempo1.pngbin0 -> 1546 bytes
-rw-r--r--common/Resources/Images/tempo2.pngbin0 -> 1847 bytes
-rw-r--r--common/Resources/Images/tempo3.pngbin0 -> 2354 bytes
-rw-r--r--common/Resources/Images/tempo4.pngbin0 -> 2130 bytes
-rw-r--r--common/Resources/Images/tempo5.pngbin0 -> 2733 bytes
-rw-r--r--common/Resources/Images/tempo6.pngbin0 -> 2322 bytes
-rw-r--r--common/Resources/Images/tempo7.pngbin0 -> 3351 bytes
-rw-r--r--common/Resources/Images/tempo8.pngbin0 -> 2960 bytes
-rw-r--r--common/Resources/Images/trackBG.pngbin0 -> 4740 bytes
-rw-r--r--common/Resources/Images/trackBGDrum.pngbin0 -> 5216 bytes
-rw-r--r--common/Resources/Images/trackBGDrumSelected.pngbin0 -> 4922 bytes
-rw-r--r--common/Resources/Images/trackBGSelected.pngbin0 -> 4491 bytes
-rw-r--r--common/Resources/Images/triangle.pngbin0 -> 8220 bytes
-rw-r--r--common/Resources/Images/trianglesel.pngbin0 -> 6643 bytes
-rw-r--r--common/Resources/Images/trumpet.pngbin0 -> 8326 bytes
-rw-r--r--common/Resources/Images/trumpetsel.pngbin0 -> 7294 bytes
-rw-r--r--common/Resources/Images/tuba.pngbin0 -> 10558 bytes
-rw-r--r--common/Resources/Images/tubasel.pngbin0 -> 9921 bytes
-rw-r--r--common/Resources/Images/violin.pngbin0 -> 10071 bytes
-rw-r--r--common/Resources/Images/violinsel.pngbin0 -> 8386 bytes
-rw-r--r--common/Resources/Images/voix.pngbin0 -> 11567 bytes
-rw-r--r--common/Resources/Images/voixsel.pngbin0 -> 10996 bytes
-rw-r--r--common/Resources/Images/volume0.pngbin0 -> 725 bytes
-rw-r--r--common/Resources/Images/volume1.pngbin0 -> 1034 bytes
-rw-r--r--common/Resources/Images/volume2.pngbin0 -> 1478 bytes
-rw-r--r--common/Resources/Images/volume3.pngbin0 -> 1987 bytes
-rw-r--r--common/Resources/Images/water.pngbin0 -> 14819 bytes
-rw-r--r--common/Resources/Images/watersel.pngbin0 -> 12868 bytes
-rw-r--r--common/Resources/Images/winds.pngbin0 -> 14332 bytes
-rw-r--r--common/Resources/Images/windssel.pngbin0 -> 14955 bytes
-rw-r--r--common/Resources/Images/zap.pngbin0 -> 11170 bytes
-rw-r--r--common/Resources/Images/zapsel.pngbin0 -> 12286 bytes
-rw-r--r--common/Resources/Loops/loop1.ttl5
-rw-r--r--common/Resources/Loops/loop2.ttl44
-rw-r--r--common/Resources/Loops/loop3.ttl9
-rwxr-xr-xcommon/Resources/Sounds/acguitbin0 -> 32360 bytes
-rwxr-xr-xcommon/Resources/Sounds/alarmbin0 -> 55744 bytes
-rwxr-xr-xcommon/Resources/Sounds/banjobin0 -> 32360 bytes
-rwxr-xr-xcommon/Resources/Sounds/bassebin0 -> 32516 bytes
-rwxr-xr-xcommon/Resources/Sounds/birdbin0 -> 32804 bytes
-rwxr-xr-xcommon/Resources/Sounds/bottlebin0 -> 19206 bytes
-rwxr-xr-xcommon/Resources/Sounds/bubblesbin0 -> 40364 bytes
-rwxr-xr-xcommon/Resources/Sounds/bykebin0 -> 32726 bytes
-rwxr-xr-xcommon/Resources/Sounds/camerabin0 -> 22564 bytes
-rwxr-xr-xcommon/Resources/Sounds/carbin0 -> 63940 bytes
-rwxr-xr-xcommon/Resources/Sounds/catbin0 -> 18160 bytes
-rwxr-xr-xcommon/Resources/Sounds/cellobin0 -> 24340 bytes
-rwxr-xr-xcommon/Resources/Sounds/chimesbin0 -> 107098 bytes
-rwxr-xr-xcommon/Resources/Sounds/clangbin0 -> 41150 bytes
-rwxr-xr-xcommon/Resources/Sounds/clarinettebin0 -> 31830 bytes
-rwxr-xr-xcommon/Resources/Sounds/clingbin0 -> 31758 bytes
-rwxr-xr-xcommon/Resources/Sounds/crashbin0 -> 20352 bytes
-rwxr-xr-xcommon/Resources/Sounds/diceinstbin0 -> 12658 bytes
-rwxr-xr-xcommon/Resources/Sounds/didjeridubin0 -> 60788 bytes
-rwxr-xr-xcommon/Resources/Sounds/dogbin0 -> 22332 bytes
-rwxr-xr-xcommon/Resources/Sounds/doorbin0 -> 33640 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum1chinebin0 -> 20480 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum1crashbin0 -> 64044 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum1floortombin0 -> 38498 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum1hardridebin0 -> 80044 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum1hatpedalbin0 -> 23022 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum1hatshoulderbin0 -> 28508 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum1kickbin0 -> 32044 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum1ridebellbin0 -> 80044 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum1snarebin0 -> 32044 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum1snaresidestickbin0 -> 5726 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum1splashbin0 -> 48044 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum1tombin0 -> 37152 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum2darbukadoombin0 -> 38652 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum2darbukafingerbin0 -> 22470 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum2darbukapiedbin0 -> 32106 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum2darbukapiedsoftbin0 -> 24454 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum2darbukarollbin0 -> 27176 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum2darbukaslapbin0 -> 25672 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum2darbukatakbin0 -> 12882 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum2hatflangerbin0 -> 64246 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum2hatpiedbin0 -> 14638 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum2hatpied2bin0 -> 14460 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum2tambourinepiedbin0 -> 33262 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum2tambourinepiedsoftbin0 -> 19470 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum3cowbellbin0 -> 15950 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum3cowbelltipbin0 -> 12872 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum3cupbin0 -> 15926 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum3djembelowbin0 -> 32080 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum3djembemidbin0 -> 15050 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum3djembesidestickbin0 -> 13602 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum3djembeslapbin0 -> 24090 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum3djembestickmidbin0 -> 25722 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum3metalstandbin0 -> 16108 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum3pedalpercbin0 -> 16160 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum3rainstickbin0 -> 56454 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum3tambourinehighbin0 -> 15996 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum3tambourinelowbin0 -> 15506 bytes
-rw-r--r--common/Resources/Sounds/drum4afrofeetbin0 -> 23774 bytes
-rw-r--r--common/Resources/Sounds/drum4fingersnbin0 -> 33644 bytes
-rw-r--r--common/Resources/Sounds/drum4mutecuicbin0 -> 12738 bytes
-rw-r--r--common/Resources/Sounds/drum4stompbassbin0 -> 22026 bytes
-rw-r--r--common/Resources/Sounds/drum4tambouribin0 -> 21382 bytes
-rw-r--r--common/Resources/Sounds/drum4tr707clapbin0 -> 11456 bytes
-rw-r--r--common/Resources/Sounds/drum4tr707openbin0 -> 29702 bytes
-rw-r--r--common/Resources/Sounds/drum4tr808closedbin0 -> 7530 bytes
-rw-r--r--common/Resources/Sounds/drum4tr808snbin0 -> 17474 bytes
-rw-r--r--common/Resources/Sounds/drum4tr909bassbin0 -> 61846 bytes
-rw-r--r--common/Resources/Sounds/drum4tr909kickbin0 -> 27516 bytes
-rw-r--r--common/Resources/Sounds/drum4tr909snbin0 -> 20920 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum5agogoaigubin0 -> 22366 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum5agogogravebin0 -> 19796 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum5bongoaiguouvertbin0 -> 11418 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum5bongograveouvertbin0 -> 14442 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum5congaaiguouvertbin0 -> 16146 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum5congagravefermebin0 -> 9674 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum5congagraveouvertbin0 -> 14490 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum5guiroretourbin0 -> 14462 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum5quicaaigubin0 -> 10410 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum5quicamediumbin0 -> 11398 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum5timablesaiguslapbin0 -> 33698 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum5timablesslapbin0 -> 37170 bytes
-rwxr-xr-xcommon/Resources/Sounds/drum5vibraslapbin0 -> 52238 bytes
-rwxr-xr-xcommon/Resources/Sounds/duckbin0 -> 12942 bytes
-rwxr-xr-xcommon/Resources/Sounds/duck2bin0 -> 7840 bytes
-rwxr-xr-xcommon/Resources/Sounds/flutebin0 -> 26902 bytes
-rwxr-xr-xcommon/Resources/Sounds/gambin0 -> 31848 bytes
-rwxr-xr-xcommon/Resources/Sounds/guidice1bin0 -> 10610 bytes
-rwxr-xr-xcommon/Resources/Sounds/guidice10bin0 -> 12298 bytes
-rwxr-xr-xcommon/Resources/Sounds/guidice2bin0 -> 16130 bytes
-rwxr-xr-xcommon/Resources/Sounds/guidice3bin0 -> 14980 bytes
-rwxr-xr-xcommon/Resources/Sounds/guidice4bin0 -> 14128 bytes
-rwxr-xr-xcommon/Resources/Sounds/guidice5bin0 -> 9760 bytes
-rwxr-xr-xcommon/Resources/Sounds/guidice6bin0 -> 12658 bytes
-rwxr-xr-xcommon/Resources/Sounds/guidice7bin0 -> 11148 bytes
-rwxr-xr-xcommon/Resources/Sounds/guidice8bin0 -> 11720 bytes
-rwxr-xr-xcommon/Resources/Sounds/guidice9bin0 -> 12376 bytes
-rwxr-xr-xcommon/Resources/Sounds/guitbin0 -> 32432 bytes
-rwxr-xr-xcommon/Resources/Sounds/guit2bin0 -> 57108 bytes
-rwxr-xr-xcommon/Resources/Sounds/harmonicabin0 -> 10396 bytes
-rwxr-xr-xcommon/Resources/Sounds/harmoniumbin0 -> 16948 bytes
-rwxr-xr-xcommon/Resources/Sounds/horsebin0 -> 36966 bytes
-rwxr-xr-xcommon/Resources/Sounds/kalimbabin0 -> 14496 bytes
-rwxr-xr-xcommon/Resources/Sounds/kotobin0 -> 29288 bytes
-rw-r--r--common/Resources/Sounds/lab1bin0 -> 44 bytes
-rw-r--r--common/Resources/Sounds/lab2bin0 -> 44 bytes
-rw-r--r--common/Resources/Sounds/lab3bin0 -> 44 bytes
-rw-r--r--common/Resources/Sounds/lab4bin0 -> 44 bytes
-rw-r--r--common/Resources/Sounds/lab5bin0 -> 44 bytes
-rw-r--r--common/Resources/Sounds/lab6bin0 -> 44 bytes
-rwxr-xr-xcommon/Resources/Sounds/laughbin0 -> 18112 bytes
-rwxr-xr-xcommon/Resources/Sounds/mandobin0 -> 20786 bytes
-rwxr-xr-xcommon/Resources/Sounds/marimbabin0 -> 13322 bytes
-rwxr-xr-xcommon/Resources/Sounds/mic1bin0 -> 44 bytes
-rwxr-xr-xcommon/Resources/Sounds/mic2bin0 -> 44 bytes
-rwxr-xr-xcommon/Resources/Sounds/mic3bin0 -> 44 bytes
-rwxr-xr-xcommon/Resources/Sounds/mic4bin0 -> 44 bytes
-rwxr-xr-xcommon/Resources/Sounds/ocarinabin0 -> 8650 bytes
-rwxr-xr-xcommon/Resources/Sounds/ounkbin0 -> 15232 bytes
-rwxr-xr-xcommon/Resources/Sounds/owbin0 -> 20300 bytes
-rwxr-xr-xcommon/Resources/Sounds/pianobin0 -> 96828 bytes
-rwxr-xr-xcommon/Resources/Sounds/planebin0 -> 69662 bytes
-rwxr-xr-xcommon/Resources/Sounds/rhodesbin0 -> 30080 bytes
-rwxr-xr-xcommon/Resources/Sounds/saxobin0 -> 29702 bytes
-rwxr-xr-xcommon/Resources/Sounds/sheepbin0 -> 39768 bytes
-rwxr-xr-xcommon/Resources/Sounds/shenaibin0 -> 14716 bytes
-rwxr-xr-xcommon/Resources/Sounds/sitarbin0 -> 27164 bytes
-rwxr-xr-xcommon/Resources/Sounds/slapbin0 -> 17534 bytes
-rwxr-xr-xcommon/Resources/Sounds/trianglebin0 -> 58652 bytes
-rwxr-xr-xcommon/Resources/Sounds/trumpetbin0 -> 20246 bytes
-rwxr-xr-xcommon/Resources/Sounds/tubabin0 -> 25876 bytes
-rwxr-xr-xcommon/Resources/Sounds/violinbin0 -> 21884 bytes
-rwxr-xr-xcommon/Resources/Sounds/voixbin0 -> 39052 bytes
-rwxr-xr-xcommon/Resources/Sounds/waterbin0 -> 19512 bytes
-rwxr-xr-xcommon/Resources/Sounds/zapbin0 -> 24218 bytes
-rw-r--r--common/Resources/SynthFiles/synthFile1bin0 -> 12288 bytes
-rw-r--r--common/Resources/SynthFiles/synthFile10bin0 -> 12288 bytes
-rw-r--r--common/Resources/SynthFiles/synthFile2bin0 -> 12288 bytes
-rw-r--r--common/Resources/SynthFiles/synthFile3bin0 -> 12288 bytes
-rw-r--r--common/Resources/SynthFiles/synthFile4bin0 -> 12288 bytes
-rw-r--r--common/Resources/SynthFiles/synthFile5bin0 -> 12288 bytes
-rw-r--r--common/Resources/SynthFiles/synthFile6bin0 -> 12288 bytes
-rw-r--r--common/Resources/SynthFiles/synthFile7bin0 -> 12288 bytes
-rw-r--r--common/Resources/SynthFiles/synthFile8bin0 -> 12288 bytes
-rw-r--r--common/Resources/SynthFiles/synthFile9bin0 -> 12288 bytes
-rw-r--r--common/Resources/__init__.py0
-rw-r--r--common/Resources/crop.csd72
-rw-r--r--common/Resources/tamtamorc.csd1096
-rw-r--r--common/Resources/tooltips_en.py128
-rw-r--r--common/Resources/tooltips_fr.py153
-rw-r--r--common/Util/CSoundClient.py356
-rw-r--r--common/Util/CSoundNote.py77
-rw-r--r--common/Util/Clooper/Makefile21
-rw-r--r--common/Util/Clooper/SoundClient.i14
-rw-r--r--common/Util/Clooper/__init__.py0
-rw-r--r--common/Util/Clooper/aclient.cpp1090
-rwxr-xr-xcommon/Util/Clooper/aclient.sobin0 -> 59030 bytes
-rw-r--r--common/Util/Clooper/audio.cpp223
-rw-r--r--common/Util/Clooper/cmd_csound.cpp22
-rw-r--r--common/Util/Clooper/log.cpp52
-rw-r--r--common/Util/Clooper/ttest.c53
-rw-r--r--common/Util/Clooper/ttest.h15
-rw-r--r--common/Util/Clooper/ttest.i15
-rw-r--r--common/Util/Clooper/ttest.py54
-rw-r--r--common/Util/Clooper/ttest_wrap.c3042
-rw-r--r--common/Util/ControlStream.py225
-rw-r--r--common/Util/Credits.py39
-rw-r--r--common/Util/InstrumentDB.py143
-rw-r--r--common/Util/InstrumentPanel.py379
-rw-r--r--common/Util/Instrument_.py392
-rw-r--r--common/Util/KeyboardWindow.py181
-rw-r--r--common/Util/LoopSettings.py230
-rw-r--r--common/Util/Network.py569
-rw-r--r--common/Util/NoteDB.py811
-rw-r--r--common/Util/NoteLooper.py199
-rw-r--r--common/Util/Profiler.py93
-rw-r--r--common/Util/Sound.py289
-rw-r--r--common/Util/ThemeWidgets.py1276
-rw-r--r--common/Util/Trackpad.py93
-rw-r--r--common/Util/__init__.py0
-rw-r--r--common/__init__.py0
706 files changed, 41077 insertions, 0 deletions
diff --git a/TamTamEdit.activity/Edit/EditToolbars.py b/TamTamEdit.activity/Edit/EditToolbars.py
new file mode 100644
index 0000000..38e2e27
--- /dev/null
+++ b/TamTamEdit.activity/Edit/EditToolbars.py
@@ -0,0 +1,1484 @@
+#!/usr/bin/env python
+
+import gtk
+
+import Config
+from sugar.graphics.toolbutton import ToolButton
+from sugar.graphics.toggletoolbutton import ToggleToolButton
+from sugar.graphics.radiotoolbutton import RadioToolButton
+from sugar.graphics.palette import Palette
+from sugar.graphics.icon import Icon
+from Util.ThemeWidgets import *
+from gettext import gettext as _
+
+#Generation palette
+import gobject
+from Generation.Generator import GenerationParameters
+#Generation palette and Properties palette
+from Generation.GenerationConstants import GenerationConstants
+from Generation.GenerationRythm import GenerationRythm
+from Generation.GenerationPitch import GenerationPitch
+
+#Properties palette
+from Util.NoteDB import PARAMETER
+from Generation.Drunk import *
+import Generation.Utils as Utils
+from types import *
+from math import sqrt
+from random import *
+
+class mainToolbar(gtk.Toolbar):
+ def __init__(self,toolbox, edit):
+ gtk.Toolbar.__init__(self)
+
+ def _insertSeparator(x = 1):
+ for i in range(x):
+ self.separator = gtk.SeparatorToolItem()
+ self.separator.set_expand(True)
+ self.separator.set_draw(True)
+ self.insert(self.separator,-1)
+ self.separator.show()
+
+ self.toolbox = toolbox
+ self.edit = edit
+
+ self.tooltips = gtk.Tooltips()
+
+ #Play button
+ self.playButton = ToggleToolButton('media-playback-start')
+ self.playButtonHandler = self.playButton.connect('toggled', self.handlePlayPause)
+ self.insert(self.playButton, -1)
+ self.playButton.show()
+ self.playButton.set_tooltip(_('Play / Pause'))
+
+ #Stop button
+ self.stopButton = ToolButton('media-playback-stop')
+ self.stopButton.connect('clicked', self.handleStop)
+ self.insert(self.stopButton, -1)
+ self.stopButton.show()
+ self.stopButton.set_tooltip(_('Stop'))
+
+ #Play button Image
+ self.playButtonImg = gtk.Image()
+ self.playButtonImg.set_from_icon_name('media-playback-start', gtk.ICON_SIZE_LARGE_TOOLBAR)
+ self.playButtonImg.show()
+
+ #Pause button Image
+ self.pauseButtonImg = gtk.Image()
+ self.pauseButtonImg.set_from_icon_name('media-playback-pause', gtk.ICON_SIZE_LARGE_TOOLBAR)
+ self.pauseButtonImg.show()
+
+ #Record button
+ self.recordButton = ToggleToolButton('recordK')
+ self.recordButton.connect('clicked', self.edit.handleKeyboardRecordButton)
+ self.insert(self.recordButton, -1)
+ self.recordButton.show()
+ self.recordButton.set_tooltip(_('Record keyboard'))
+
+ #RecordOgg button
+ self.recordOggButton = ToggleToolButton('recordO')
+ self.recordOggButton.connect('clicked', self.edit.handleAudioRecord)
+ self.insert(self.recordOggButton, -1)
+ self.recordOggButton.show()
+ self.recordOggButton.set_tooltip(_('Record to ogg'))
+
+ _insertSeparator(1)
+
+ #Pointer button
+ self._pointerPalette = pointerPalette(_('Select Tool'), self.edit)
+ self.pointerButton = RadioToolButton('edit-pointer', group = None)
+ self.pointerButton.set_palette(self._pointerPalette)
+ self.pointerButton.connect('toggled', self.edit.handleToolClick, 'default')
+ self.pointerButton.get_child().connect('enter-notify-event', self.edit.blockFocus)
+ self.pointerButton.get_child().connect('leave-notify-event', self.edit.unblockFocus)
+ self.insert(self.pointerButton, -1)
+ self.pointerButton.show()
+
+ #Draw button
+ self._drawPalette = drawPalette(_('Draw Tool'), self.edit)
+ self.drawButton = RadioToolButton('edit-pencil', group = self.pointerButton)
+ self.drawButton.set_palette(self._drawPalette)
+ self.drawButton.connect('toggled', self.edit.handleToolClick, 'draw')
+ self.drawButton.get_child().connect('enter-notify-event', self.edit.blockFocus)
+ self.drawButton.get_child().connect('leave-notify-event', self.edit.unblockFocus)
+ self.insert(self.drawButton, -1)
+ self.drawButton.show()
+
+ #Paint button
+ self._paintPalette = paintPalette(_('Paint Tool'), self.edit)
+ self.paintButton = RadioToolButton('edit-brush', group = self.pointerButton)
+ self.paintButton.set_palette(self._paintPalette)
+ self.paintButton.connect('toggled', self.edit.handleToolClick, 'paint')
+ self.paintButton.get_child().connect('enter-notify-event', self.edit.blockFocus)
+ self.paintButton.get_child().connect('leave-notify-event', self.edit.unblockFocus)
+ self.insert(self.paintButton, -1)
+ self.paintButton.show()
+
+ _insertSeparator(1)
+
+ #Duplicate button
+ self.duplicateButton = ToggleToolButton('duplicate')
+ self.duplicateButton.connect('toggled', self.handleDuplicate)
+ self.insert(self.duplicateButton, -1)
+ self.duplicateButton.show()
+ self.duplicateButton.set_tooltip(_('Duplicate'))
+
+ #Volume / Tempo button
+ self._volumeTempoPalette = volumeTempoPalette(_('Volume / Tempo'), self.edit)
+ self.volumeTempoButton = ToggleToolButton('voltemp')
+ self.volumeTempoButton.set_palette(self._volumeTempoPalette)
+ self.volumeTempoButton.get_child().connect('enter-notify-event', self.edit.blockFocus)
+ self.volumeTempoButton.get_child().connect('leave-notify-event', self.edit.unblockFocus)
+ self.insert(self.volumeTempoButton, -1)
+ self.volumeTempoButton.show()
+
+ def handlePlayPause(self, widget, data = None):
+ if widget.get_active():
+ self.edit.handlePlay(widget)
+ self.edit._generateToolbar.handler_block(self.edit._generateToolbar.playButtonHandler)
+ self.edit._generateToolbar.playButton.set_active(True)
+ self.edit._generateToolbar.handler_unblock(self.edit._generateToolbar.playButtonHandler)
+ widget.set_icon_widget(self.pauseButtonImg)
+ self.edit._generateToolbar.playButton.set_icon_widget(self.edit._generateToolbar.pauseButtonImg)
+ else:
+ self.edit.handleStop(widget, False)
+ self.edit._generateToolbar.handler_block(self.edit._generateToolbar.playButtonHandler)
+ self.edit._generateToolbar.playButton.set_active(False)
+ self.edit._generateToolbar.handler_unblock(self.edit._generateToolbar.playButtonHandler)
+ widget.set_icon_widget(self.playButtonImg)
+ self.edit._generateToolbar.playButton.set_icon_widget(self.edit._generateToolbar.playButtonImg)
+
+ def handleStop(self, widget, data = None):
+ self.edit.handleStop(widget, True)
+ self.playButton.set_active(False)
+ if self.recordButton.get_active():
+ self.recordButton.set_active(False)
+
+ def handleDuplicate(self, widget):
+ if widget.get_active():
+ if self.edit.getContext() == 0: #Page
+ self.edit.pageDuplicate()
+ elif self.edit.getContext() == 1: #Track
+ self.edit.trackDuplicateWidget(widget)
+ elif self.edit.getContext() == 2: #Note
+ self.edit.noteDuplicateWidget(widget)
+ widget.set_active(False)
+
+class generateToolbar(gtk.Toolbar):
+ def __init__(self,toolbox, edit):
+ gtk.Toolbar.__init__(self)
+
+ def _insertSeparator(x = 1):
+ for i in range(x):
+ self.separator = gtk.SeparatorToolItem()
+ self.separator.set_expand(True)
+ self.separator.set_draw(False)
+ self.insert(self.separator,-1)
+ self.separator.show()
+
+ self.toolbox = toolbox
+ self.edit = edit
+
+ self.tooltips = gtk.Tooltips()
+
+ #Play button
+ self.playButton = ToggleToolButton('media-playback-start')
+ self.playButtonHandler = self.playButton.connect('toggled', self.handlePlayPause)
+ self.insert(self.playButton, -1)
+ self.playButton.show()
+ self.playButton.set_tooltip(_('Play / Pause'))
+
+ #Stop button
+ self.stopButton = ToolButton('media-playback-stop')
+ self.stopButton.connect('clicked', self.handleStop)
+ self.insert(self.stopButton, -1)
+ self.stopButton.show()
+ self.stopButton.set_tooltip(_('Stop'))
+
+ #Play button Image
+ self.playButtonImg = gtk.Image()
+ self.playButtonImg.set_from_icon_name('media-playback-start', gtk.ICON_SIZE_LARGE_TOOLBAR)
+ self.playButtonImg.show()
+
+ #Pause button Image
+ self.pauseButtonImg = gtk.Image()
+ self.pauseButtonImg.set_from_icon_name('media-playback-pause', gtk.ICON_SIZE_LARGE_TOOLBAR)
+ self.pauseButtonImg.show()
+
+ _insertSeparator(1)
+
+ #BigGeneration button
+ self.bigGenerationButton = ToolButton('diceB')
+ self.bigGenerationButton.connect('clicked', self.edit.createNewTune)
+ self.insert(self.bigGenerationButton, -1)
+ self.bigGenerationButton.show()
+ self.bigGenerationButton.set_tooltip(_('Generate Tune'))
+
+ #Generation button
+ self._generationPalette = generationPalette(_('Generation'), self.edit)
+ self.generationButton = ToggleToolButton('dice')
+ #self.generationButton.connect(None)
+ self.generationButton.set_palette(self._generationPalette)
+ self.generationButton.get_child().connect('enter-notify-event', self.edit.blockFocus)
+ self.generationButton.get_child().connect('leave-notify-event', self.edit.unblockFocus)
+ self.insert(self.generationButton, -1)
+ self.generationButton.show()
+
+ #Properties button
+ self._propertiesPalette = propertiesPalette(_('Properties'), self.edit)
+ self.propsButton = ToggleToolButton('props')
+ self.propsButton.set_palette(self._propertiesPalette)
+ self.propsButton.get_child().connect('enter-notify-event', self.edit.blockFocus)
+ self.propsButton.get_child().connect('leave-notify-event', self.edit.unblockFocus)
+ self.insert(self.propsButton, -1)
+ self.propsButton.show()
+
+ def handlePlayPause(self, widget, data = None):
+ if widget.get_active():
+ self.edit.handlePlay(widget)
+ self.edit._mainToolbar.handler_block(self.edit._mainToolbar.playButtonHandler)
+ self.edit._mainToolbar.playButton.set_active(True)
+ self.edit._mainToolbar.handler_unblock(self.edit._mainToolbar.playButtonHandler)
+ widget.set_icon_widget(self.pauseButtonImg)
+ self.edit._mainToolbar.playButton.set_icon_widget(self.edit._mainToolbar.pauseButtonImg)
+ else:
+ self.edit.handleStop(widget, False)
+ self.edit._mainToolbar.handler_block(self.edit._mainToolbar.playButtonHandler)
+ self.edit._mainToolbar.playButton.set_active(False)
+ self.edit._mainToolbar.handler_unblock(self.edit._mainToolbar.playButtonHandler)
+ widget.set_icon_widget(self.playButtonImg)
+ self.edit._mainToolbar.playButton.set_icon_widget(self.edit._mainToolbar.playButtonImg)
+
+ def handleStop(self, widget, data = None):
+ self.edit.handleStop(widget, True)
+ self.playButton.set_active(False)
+ if self.edit._mainToolbar.recordButton.get_active():
+ self.edit._mainToolbar.recordButton.set_active(False)
+
+class pointerPalette(Palette):
+ def __init__(self, label, edit):
+ Palette.__init__(self, label)
+
+ self.edit = edit
+
+ self.pointerBox = gtk.VBox()
+
+ self.snapGridHBox = gtk.HBox()
+ self.snapGridImage = gtk.Image()
+ self.snapGridImage.set_from_file(Config.TAM_TAM_ROOT + '/icons/grid.svg')
+ self.snapGridBox = BigComboBox()
+ self.snapGridBox.connect('changed', self.handleSnapGrid)
+ self.gridDurs = [1, 2, 3, 4, 6, 12, 24]
+ durs = [_('1/12'), _('1/6'), _('1/4'), _('1/3'), _('1/2'), _('1'), _('2') ]
+ for dur in durs:
+ self.snapGridBox.append_item(durs.index(dur),dur)
+ self.snapGridBox.set_active(0)
+ self.snapGridHBox.pack_start(self.snapGridImage, False, False, padding = 5)
+ self.snapGridHBox.pack_start(self.snapGridBox, False, False, padding = 5)
+
+ self.pointerBox.pack_start(self.snapGridHBox, False, False, padding = 5)
+ self.pointerBox.show_all()
+
+ self.set_content(self.pointerBox)
+
+ pass
+ #self.noteDur = widget.props.value
+
+ def handleSnapGrid(self, widget):
+ data = widget.props.value
+ grid = int(self.gridDurs[data])
+ self.edit.trackInterface.setPointerGrid(grid)
+
+class drawPalette(Palette):
+ def __init__(self, label, edit):
+ Palette.__init__(self, label)
+
+ self.edit = edit
+
+ self.drawBox = gtk.VBox()
+
+ self.snapGridHBox = gtk.HBox()
+ self.snapGridImage = gtk.Image()
+ self.snapGridImage.set_from_file(Config.TAM_TAM_ROOT + '/icons/grid.svg')
+ self.snapGridBox = BigComboBox()
+ self.snapGridBox.connect('changed', self.handleSnapGrid)
+ self.gridDurs = [1, 2, 3, 4, 6, 12, 24]
+ durs = [_('1/12'), _('1/6'), _('1/4'), _('1/3'), _('1/2'), _('1'), _('2') ]
+ for dur in durs:
+ self.snapGridBox.append_item(durs.index(dur),dur)
+ self.snapGridBox.set_active(0)
+ self.snapGridHBox.pack_start(self.snapGridImage, False, False, padding = 5)
+ self.snapGridHBox.pack_start(self.snapGridBox, False, False, padding = 5)
+
+ self.drawBox.pack_start(self.snapGridHBox, False, False, padding = 5)
+ self.drawBox.show_all()
+
+ self.set_content(self.drawBox)
+
+ pass
+ #self.noteDur = widget.props.value
+
+ def handleSnapGrid(self, widget):
+ data = widget.props.value
+ grid = int(self.gridDurs[data])
+ self.edit.trackInterface.setDrawGrid(grid)
+
+class paintPalette(Palette):
+ def __init__(self, label, edit):
+ Palette.__init__(self, label)
+
+ self.edit = edit
+
+ self.paintBox = gtk.VBox()
+
+ self.noteDurHBox = gtk.HBox()
+ self.noteDurImage = gtk.Image()
+ self.noteDurImage.set_from_file(Config.TAM_TAM_ROOT + '/icons/notedur.svg')
+ self.noteDurBox = BigComboBox()
+ self.noteDurBox.connect('changed', self.handleNoteDur)
+ self.noteDurs = [1, 2, 3, 4, 6, 12, 24]
+ self.durs = [_('1/12'), _('1/6'), _('1/4'), _('1/3'), _('1/2'), _('1'), _('2') ]
+ for dur in self.durs:
+ self.noteDurBox.append_item(self.durs.index(dur),dur)
+ self.noteDurBox.set_active(2)
+ self.noteDurHBox.pack_start(self.noteDurImage, False, False, padding = 5)
+ self.noteDurHBox.pack_start(self.noteDurBox, False, False, padding = 5)
+
+ self.snapGridHBox = gtk.HBox()
+ self.snapGridImage = gtk.Image()
+ self.snapGridImage.set_from_file(Config.TAM_TAM_ROOT + '/icons/grid.svg')
+ self.snapGridBox = BigComboBox()
+ self.snapGridBox.connect('changed', self.handleSnapGrid)
+ self.gridDurs = [1, 2, 3, 4, 6, 12, 24]
+ durs = [_('1/12'), _('1/6'), _('1/4'), _('1/3'), _('1/2'), _('1'), _('2')]
+ for dur in durs:
+ self.snapGridBox.append_item(durs.index(dur),dur)
+ self.snapGridBox.set_active(2)
+ self.snapGridHBox.pack_start(self.snapGridImage, False, False, padding = 5)
+ self.snapGridHBox.pack_start(self.snapGridBox, False, False, padding = 5)
+
+ self.paintBox.pack_start(self.noteDurHBox, False, False, padding = 5)
+ self.paintBox.pack_start(self.snapGridHBox, False, False, padding = 5)
+ self.paintBox.show_all()
+
+ self.set_content(self.paintBox)
+
+ def resizeNoteDur(self):
+ oldActive = self.noteDurBox.get_active()
+ len = self.snapGridBox.get_active()
+ self.noteDurBox.remove_all()
+ for dur in self.durs[0:len+1]:
+ self.noteDurBox.append_item(self.durs.index(dur), dur)
+ if oldActive <= len:
+ self.noteDurBox.set_active(oldActive)
+ else:
+ self.noteDurBox.set_active(len)
+
+ def handleNoteDur(self, widget):
+ data = widget.props.value
+ noteDur = int(self.noteDurs[data])
+ self.edit.trackInterface.setPaintNoteDur(noteDur)
+
+ def handleSnapGrid(self, widget):
+ data = widget.props.value
+ grid = int(self.gridDurs[data])
+ self.edit.trackInterface.setPaintGrid(grid)
+ self.resizeNoteDur()
+
+class volumeTempoPalette(Palette):
+ def __init__(self, label, edit):
+ Palette.__init__(self, label)
+
+ self.edit = edit
+
+ self.volumeTempoBox = gtk.VBox()
+
+ self.volumeSliderBox = gtk.HBox()
+ self.volumeSliderLabel = gtk.Label(_('Volume'))
+ self.volumeSliderAdj = gtk.Adjustment(Config.DEFAULT_VOLUME, 0, 100, 1, 1, 0)
+ self.volumeSliderAdj.connect('value-changed', self.edit.handleVolume)
+ self.volumeSlider = gtk.HScale(adjustment = self.volumeSliderAdj)
+ self.volumeSlider.set_size_request(250,-1)
+ self.volumeSlider.set_inverted(False)
+ self.volumeSlider.set_draw_value(False)
+ self.volumeSliderBox.pack_start(self.volumeSliderLabel, False, False, padding = 5)
+ self.volumeSliderBox.pack_end(self.volumeSlider, False, False, padding = 5)
+
+ self.tempoSliderBox = gtk.HBox()
+ self.tempoSliderLabel = gtk.Label(_('Tempo'))
+ self.tempoSliderAdj = gtk.Adjustment(Config.PLAYER_TEMPO, 40, 240, 1, 1, 0)
+ self.tempoSliderAdj.connect('value-changed', self.edit.handleTempo)
+ self.tempoSlider = gtk.HScale(adjustment = self.tempoSliderAdj)
+ self.tempoSlider.set_size_request(250,-1)
+ self.tempoSlider.set_inverted(False)
+ self.tempoSlider.set_draw_value(False)
+ self.tempoSliderBox.pack_start(self.tempoSliderLabel, False, False, padding = 5)
+ self.tempoSliderBox.pack_end(self.tempoSlider, False, False, padding = 5)
+
+ self.volumeTempoBox.pack_start(self.volumeSliderBox, padding = 5)
+ self.volumeTempoBox.pack_start(self.tempoSliderBox, padding = 5)
+ self.volumeTempoBox.show_all()
+
+ self.set_content(self.volumeTempoBox)
+
+class generationPalette(Palette):
+ def __init__(self, label, edit):
+ Palette.__init__(self, label)
+
+ self.edit = edit
+
+ self.rythmDensity = GenerationConstants.DEFAULT_DENSITY
+ self.rythmRegularity = GenerationConstants.DEFAULT_RYTHM_REGULARITY
+ self.pitchRegularity = GenerationConstants.DEFAULT_PITCH_REGULARITY
+ self.pitchStep = GenerationConstants.DEFAULT_STEP
+ self.duration = GenerationConstants.DEFAULT_DURATION
+ self.silence = GenerationConstants.DEFAULT_SILENCE
+ self.rythmMethod = GenerationConstants.DEFAULT_RYTHM_METHOD
+ self.pitchMethod = GenerationConstants.DEFAULT_PITCH_METHOD
+ self.pattern = GenerationConstants.DEFAULT_PATTERN
+ self.scale = GenerationConstants.DEFAULT_SCALE
+
+ self.mainBox = gtk.VBox()
+ self.slidersBox = gtk.HBox()
+ self.scaleModeBox = gtk.VBox()
+ self.decisionBox = gtk.HBox()
+
+ self.XYSlider1MainBox = gtk.VBox()
+ self.XYSlider1TopLabel = gtk.Label(_('Rythm'))
+ self.XSlider1BottomLabelBox = gtk.HBox()
+ self.XSlider1Img = gtk.Image()
+ self.XSlider1Img.set_from_file(Config.TAM_TAM_ROOT + '/icons/sideR.svg')
+ self.XSlider1BottomLabel = gtk.Label(_('Density'))
+ self.YSlider1BottomLabelBox = gtk.HBox()
+ self.YSlider1Img = gtk.Image()
+ self.YSlider1Img.set_from_file(Config.TAM_TAM_ROOT + '/icons/updownR.svg')
+ self.YSlider1BottomLabel = gtk.Label(_('Regularity'))
+ self.XYSliderBox1 = RoundFixed(fillcolor = '#CCCCCC', bordercolor = '#000000')
+ self.XYSliderBox1.set_size_request(200,200)
+ self.XYButton1 = ImageToggleButton( Config.TAM_TAM_ROOT + '/icons/XYBut.svg', Config.TAM_TAM_ROOT + '/icons/XYButDown.svg')
+ self.XAdjustment1 = gtk.Adjustment(self.rythmDensity * 100, 0, 100, 1, 1, 1)
+ self.XAdjustment1.connect("value-changed", self.handleXAdjustment1)
+ self.YAdjustment1 = gtk.Adjustment(self.rythmRegularity * 100, 0, 100, 1, 1, 1)
+ self.YAdjustment1.connect("value-changed", self.handleYAdjustment1)
+ self.XYSlider1 = XYSlider( self.XYSliderBox1, self.XYButton1, self.XAdjustment1, self.YAdjustment1, False, True )
+ self.XSlider1BottomLabelBox.pack_start(self.XSlider1Img, False, False, padding = 5)
+ self.XSlider1BottomLabelBox.pack_start(self.XSlider1BottomLabel, False, False, padding = 5)
+ self.YSlider1BottomLabelBox.pack_start(self.YSlider1Img, False, False, padding = 5)
+ self.YSlider1BottomLabelBox.pack_start(self.YSlider1BottomLabel, False, False, padding = 5)
+ self.XYSlider1MainBox.pack_start(self.XYSlider1TopLabel, False, False, padding = 5)
+ self.XYSlider1MainBox.pack_start(self.XYSlider1, False, False, padding = 2)
+ self.XYSlider1MainBox.pack_start(self.XSlider1BottomLabelBox, False, False, padding = 2)
+ self.XYSlider1MainBox.pack_start(self.YSlider1BottomLabelBox, False, False, padding = 2)
+
+ self.XYSlider2MainBox = gtk.VBox()
+ self.XYSlider2TopLabel = gtk.Label(_('Pitch'))
+ self.XSlider2BottomLabelBox = gtk.HBox()
+ self.XSlider2Img = gtk.Image()
+ self.XSlider2Img.set_from_file(Config.TAM_TAM_ROOT + '/icons/sideR.svg')
+ self.XSlider2BottomLabel = gtk.Label(_('Regularity'))
+ self.YSlider2BottomLabelBox = gtk.HBox()
+ self.YSlider2Img = gtk.Image()
+ self.YSlider2Img.set_from_file(Config.TAM_TAM_ROOT + '/icons/updownR.svg')
+ self.YSlider2BottomLabel = gtk.Label(_('Maximum step'))
+ self.XYSliderBox2 = RoundFixed(fillcolor = '#CCCCCC', bordercolor = '#000000')
+ self.XYSliderBox2.set_size_request(200,200)
+ self.XYButton2 = ImageToggleButton( Config.TAM_TAM_ROOT + '/icons/XYBut.svg', Config.TAM_TAM_ROOT + '/icons/XYButDown.svg')
+ self.XAdjustment2 = gtk.Adjustment(self.pitchRegularity * 100, 0, 100, 1, 1, 1)
+ self.XAdjustment2.connect("value-changed", self.handleXAdjustment2)
+ self.YAdjustment2 = gtk.Adjustment(self.pitchStep * 100, 0, 100, 1, 1, 1)
+ self.YAdjustment2.connect("value-changed", self.handleYAdjustment2)
+ self.XYSlider2 = XYSlider( self.XYSliderBox2, self.XYButton2, self.XAdjustment2, self.YAdjustment2, False, True )
+ self.XSlider2BottomLabelBox.pack_start(self.XSlider2Img, False, False, padding = 5)
+ self.XSlider2BottomLabelBox.pack_start(self.XSlider2BottomLabel, False, False, padding = 5)
+ self.YSlider2BottomLabelBox.pack_start(self.YSlider2Img, False, False, padding = 5)
+ self.YSlider2BottomLabelBox.pack_start(self.YSlider2BottomLabel, False, False, padding = 5)
+ self.XYSlider2MainBox.pack_start(self.XYSlider2TopLabel, False, False, padding = 5)
+ self.XYSlider2MainBox.pack_start(self.XYSlider2, False, False, padding = 2)
+ self.XYSlider2MainBox.pack_start(self.XSlider2BottomLabelBox, False, False, padding = 2)
+ self.XYSlider2MainBox.pack_start(self.YSlider2BottomLabelBox, False, False, padding = 2)
+
+ self.XYSlider3MainBox = gtk.VBox()
+ self.XYSlider3TopLabel = gtk.Label(_('Duration'))
+ self.XSlider3BottomLabelBox = gtk.HBox()
+ self.XSlider3Img = gtk.Image()
+ self.XSlider3Img.set_from_file(Config.TAM_TAM_ROOT + '/icons/sideR.svg')
+ self.XSlider3BottomLabel = gtk.Label(_('Note duration'))
+ self.YSlider3BottomLabelBox = gtk.HBox()
+ self.YSlider3Img = gtk.Image()
+ self.YSlider3Img.set_from_file(Config.TAM_TAM_ROOT + '/icons/updownR.svg')
+ self.YSlider3BottomLabel = gtk.Label(_('Silence density'))
+ self.XYSliderBox3 = RoundFixed(fillcolor = '#CCCCCC', bordercolor = '#000000')
+ self.XYSliderBox3.set_size_request(200,200)
+ self.XYButton3 = ImageToggleButton( Config.TAM_TAM_ROOT + '/icons/XYBut.svg', Config.TAM_TAM_ROOT + '/icons/XYButDown.svg')
+ self.XAdjustment3 = gtk.Adjustment(self.duration * 100, 0, 100, 1, 1, 1)
+ self.XAdjustment3.connect("value-changed", self.handleXAdjustment3)
+ self.YAdjustment3 = gtk.Adjustment(self.silence * 100, 0, 100, 1, 1, 1)
+ self.YAdjustment3.connect("value-changed", self.handleYAdjustment3)
+ self.XYSlider3 = XYSlider( self.XYSliderBox3, self.XYButton3, self.XAdjustment3, self.YAdjustment3, False, True )
+ self.XSlider3BottomLabelBox.pack_start(self.XSlider3Img, False, False, padding = 5)
+ self.XSlider3BottomLabelBox.pack_start(self.XSlider3BottomLabel, False, False, padding = 5)
+ self.YSlider3BottomLabelBox.pack_start(self.YSlider3Img, False, False, padding = 5)
+ self.YSlider3BottomLabelBox.pack_start(self.YSlider3BottomLabel, False, False, padding = 5)
+ self.XYSlider3MainBox.pack_start(self.XYSlider3TopLabel, False, False, padding = 5)
+ self.XYSlider3MainBox.pack_start(self.XYSlider3, False, False, padding = 2)
+ self.XYSlider3MainBox.pack_start(self.XSlider3BottomLabelBox, False, False, padding = 2)
+ self.XYSlider3MainBox.pack_start(self.YSlider3BottomLabelBox, False, False, padding = 2)
+
+ self.slidersBox.pack_start(self.XYSlider1MainBox, False, False, padding = 5)
+ self.slidersBox.pack_start(self.XYSlider2MainBox, False, False, padding = 5)
+ self.slidersBox.pack_start(self.XYSlider3MainBox, False, False, padding = 5)
+
+ self.previewBox = gtk.HBox()
+ self.previewDA = gtk.DrawingArea()
+ self.previewDA.set_size_request( -1, 100 )
+ self.previewDA.connect( "size-allocate", self.handlePreviewAlloc )
+ self.previewDA.connect( "expose-event", self.handlePreviewExpose )
+ self.previewBox.pack_start( self.previewDA, True, True, padding = 5 )
+
+ self.scaleBoxHBox = gtk.HBox()
+ self.scaleBoxLabel = gtk.Label(_('Scale: '))
+ self.scaleBox = BigComboBox()
+ scales = [_('Major scale'), _('Harmonic minor scale'), _('Natural minor scale'), _('Phrygian scale'), _('Dorian scale'), _('Lydian scale'), _('Myxolidian scale')]
+ for scale in scales:
+ self.scaleBox.append_item(scales.index(scale), scale)
+ self.scaleBox.connect('changed', self.handleScale)
+
+ self.modeBoxHBox = gtk.HBox()
+ self.modeBoxLabel = gtk.Label(_('Mode: '))
+ self.modeBox = BigComboBox()
+ modes = [_('Drunk'), _('Drone and Jump'), _('Repeater'), _('Loop segments')]
+ for mode in modes:
+ self.modeBox.append_item(modes.index(mode), mode)
+ self.modeBox.connect('changed', self.handleMode)
+
+ self.scaleBoxHBox.pack_start(self.scaleBoxLabel, False, False, padding = 10)
+ self.scaleBoxHBox.pack_start(self.scaleBox, False, False, padding = 10)
+ self.modeBoxHBox.pack_start(self.modeBoxLabel, False, False, padding = 10)
+ self.modeBoxHBox.pack_start(self.modeBox, False, False, padding = 10)
+ self.scaleModeBox.pack_start(self.scaleBoxHBox, False, False, padding = 5)
+ self.scaleModeBox.pack_start(self.modeBoxHBox, False, False, padding = 5)
+
+ self.acceptButton = ImageButton(Config.TAM_TAM_ROOT + '/icons/accept.svg')
+ self.acceptButton.connect('clicked',self.generate)
+ self.cancelButton = ImageButton(Config.TAM_TAM_ROOT + '/icons/cancel.svg')
+ self.cancelButton.connect('clicked',self.cancel)
+ self.decisionBox.pack_start(self.cancelButton, False, False, padding = 5)
+ self.decisionBox.pack_start(self.acceptButton, False, False, padding = 5)
+
+ self.mainBox.pack_start(self.slidersBox, False, False, padding = 5)
+ self.mainBox.pack_start( self.previewBox, False, False, padding = 5 )
+ self.mainBox.pack_start(self.scaleModeBox, False, False, padding = 5)
+ self.mainBox.pack_start(self.decisionBox, False, False, padding = 5)
+ self.mainBox.show_all()
+
+ self.set_content(self.mainBox)
+
+ #-- preview drawing -----------------------------------
+ win = gtk.gdk.get_default_root_window()
+ self.gc = gtk.gdk.GC( win )
+ self.parametersDirty = False
+ self.drawingPreview = False
+ self.predrawTarget = 0
+ self.predrawIdleAbort = False
+ self.predrawBuffer = False
+ # self.predrawBuffer is initialized in handlePreviewAlloc
+ pix = gtk.gdk.pixbuf_new_from_file( Config.IMAGE_ROOT+"sampleBG.png" )
+ self.sampleBg = gtk.gdk.Pixmap( win, pix.get_width(), pix.get_height() )
+ self.sampleBg.draw_pixbuf( self.gc, pix, 0, 0, 0, 0, pix.get_width(), pix.get_height(), gtk.gdk.RGB_DITHER_NONE )
+ self.sampleBg.endOffset = pix.get_width()-5
+ self.sampleNoteHeight = 7
+ if True: # load clipmask
+ pix = gtk.gdk.pixbuf_new_from_file(Config.IMAGE_ROOT+'sampleNoteMask.png')
+ pixels = pix.get_pixels()
+ stride = pix.get_rowstride()
+ channels = pix.get_n_channels()
+ bitmap = ""
+ byte = 0
+ shift = 0
+ for j in range(pix.get_height()):
+ offset = stride*j
+ for i in range(pix.get_width()):
+ r = pixels[i*channels+offset]
+ if r != "\0": byte += 1 << shift
+ shift += 1
+ if shift > 7:
+ bitmap += "%c" % byte
+ byte = 0
+ shift = 0
+ if shift > 0:
+ bitmap += "%c" % byte
+ byte = 0
+ shift = 0
+ self.sampleNoteMask = gtk.gdk.bitmap_create_from_data( None, bitmap, pix.get_width(), pix.get_height() )
+ self.sampleNoteMask.endOffset = pix.get_width()-3
+
+ colormap = self.previewDA.get_colormap()
+ self.colors = { "Beat_Line": colormap.alloc_color( "#959595", True, True ),
+ "Note_Border": colormap.alloc_color( Config.BG_COLOR, True, True ),
+ "Note_Fill": colormap.alloc_color( Config.FG_COLOR, True, True ) }
+
+ self.scaleBox.set_active(0)
+ self.modeBox.set_active(0)
+
+
+ def handleXAdjustment1( self, data ):
+ self.rythmDensity = self.XAdjustment1.value * .01
+ self.parametersChanged()
+
+ def handleYAdjustment1( self, data ):
+ self.rythmRegularity = self.YAdjustment1.value * .01
+ self.parametersChanged()
+
+ def handleXAdjustment2( self, data ):
+ self.pitchRegularity = self.XAdjustment2.value * .01
+ self.parametersChanged()
+
+ def handleYAdjustment2( self, data ):
+ self.pitchStep = self.YAdjustment2.value * .01
+ self.parametersChanged()
+
+ def handleXAdjustment3( self, data ):
+ self.duration = self.XAdjustment3.value * .01
+ self.parametersChanged()
+
+ def handleYAdjustment3( self, data ):
+ self.silence = self.YAdjustment3.value * .01
+ self.parametersChanged()
+
+ def handleScale(self, widget, data = None):
+ self.scale = widget.props.value
+ self.edit.scale = self.scale
+ self.parametersChanged()
+
+ def handleMode( self, widget, data = None ):
+ self.pattern = [widget.props.value for x in range(4)]
+ self.parametersChanged()
+
+ def getGenerationParameters( self ):
+ return GenerationParameters( self.rythmDensity,
+ self.rythmRegularity,
+ self.pitchStep,
+ self.pitchRegularity,
+ self.duration,
+ self.silence,
+ self.rythmMethod,
+ self.pitchMethod,
+ self.pattern,
+ self.scale )
+
+ def cancel(self, widget, data = None):
+ self.popdown(True)
+
+ def generate(self, widget, data=None):
+ context = self.edit.getContext()
+ if context == 0: # Page
+ mode = 'page'
+ elif context == 1: # Track
+ mode = 'track'
+ elif context == 2: # Note
+ self.popdown(True)
+ return
+ self.edit.setPageGenerateMode(mode)
+ self.edit.generate(self.getGenerationParameters())
+ self.popdown(True)
+
+ ############ generate a preview melody ##############s
+ def previewGenerator(self, parameters):
+ makeRythm = GenerationRythm()
+ makePitch = GenerationPitch()
+ table_duration = Utils.scale(parameters.articule, GenerationConstants.ARTICULATION_SCALE_MIN_MAPPING, GenerationConstants.ARTICULATION_SCALE_MAX_MAPPING, GenerationConstants.ARTICULATION_SCALE_STEPS)
+ table_pitch = GenerationConstants.SCALES[parameters.scale]
+ beat = self.edit.noteDB.pages[self.edit.tuneInterface.getSelectedIds()[0]].beats
+ barLength = Config.TICKS_PER_BEAT * beat
+ trackNotes = []
+
+ rythmSequence = makeRythm.celluleRythmSequence(parameters, barLength)
+ pitchSequence = makePitch.drunkPitchSequence(len(rythmSequence),parameters, table_pitch, 0)
+ gainSequence = self.makeGainSequence(rythmSequence)
+ durationSequence = self.makeDurationSequence(rythmSequence, parameters, table_duration, barLength)
+
+ for i in range(len(rythmSequence)):
+ if random() > parameters.silence:
+ trackNotes.append([rythmSequence[i], pitchSequence[i], gainSequence[i], durationSequence[i]])
+ #print "-------------------------------------------------------",trackNotes
+ return ( trackNotes, beat )
+
+ def makeGainSequence( self, onsetList ):
+ gainSequence = []
+ max = GenerationConstants.GAIN_MAX_BOUNDARY
+ midMax = GenerationConstants.GAIN_MID_MAX_BOUNDARY
+ midMin = GenerationConstants.GAIN_MID_MIN_BOUNDARY
+ min = GenerationConstants.GAIN_MIN_BOUNDARY
+ for onset in onsetList:
+ if onset == 0:
+ gainSequence.append(uniform(midMax, max))
+ elif ( onset % Config.TICKS_PER_BEAT) == 0:
+ gainSequence.append(uniform(midMin, midMax))
+ else:
+ gainSequence.append(uniform(min, midMin))
+ return gainSequence
+
+ def makeDurationSequence( self, onsetList, parameters, table_duration, barLength ):
+ durationSequence = []
+ if len( onsetList ) > 1:
+ for i in range(len(onsetList) - 1):
+ durationSequence.append((onsetList[i+1] - onsetList[i]) * Utils.prob2( table_duration ))
+ durationSequence.append(( barLength - onsetList[-1]) * Utils.prob2( table_duration ))
+ elif len( onsetList ) == 1:
+ durationSequence.append( ( barLength - onsetList[0] ) * Utils.prob2( table_duration ))
+ return durationSequence
+
+ def parametersChanged( self ):
+ if not self.drawingPreview:
+ self.drawPreview()
+ else:
+ self.parametersDirty = True
+
+ def drawPreview( self, force = False ):
+ if not self.predrawBuffer:
+ return # not alloc'ed yet
+
+ if self.drawingPreview and not force:
+ return # should never happen
+
+ notes, beats = self.previewGenerator( self.getGenerationParameters() )
+ self.parametersDirty = False
+
+ if force:
+ if self.drawingPreview:
+ self.predrawIdleAbort = True
+ self._idleDraw( notes, beats, True, True )
+ else:
+ self.drawingPreview = True
+ gobject.idle_add( self._idleDraw, notes, beats, True, False )
+
+ def _idleDraw( self, notes, beats, fresh, force ):
+ if self.predrawIdleAbort and not force:
+ self.predrawIdleAbort = False
+ return False
+
+ pixmap = self.predrawBuffer[self.predrawTarget]
+
+ if fresh:
+ # draw bg
+ pixmap.draw_drawable( self.gc, self.sampleBg, 0, 0, 0, 0, self.previewDA.width-5, self.previewDA.height )
+ pixmap.draw_drawable( self.gc, self.sampleBg, self.sampleBg.endOffset, 0, self.previewDA.width-5, 0, 5, self.previewDA.height )
+ # draw beat lines
+ self.gc.set_line_attributes( Config.BEAT_LINE_SIZE, gtk.gdk.LINE_ON_OFF_DASH, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER )
+ self.gc.foreground = self.colors["Beat_Line"]
+ for i in range(1,beats):
+ x = self.beatSpacing[beats][i]
+ pixmap.draw_line( self.gc, x, 1, x, self.previewDA.height-1 )
+
+ if not force:
+ gobject.idle_add( self._idleDraw, notes, beats, False, False )
+ return False
+
+ if force: N = len(notes)
+ else: N = min( 3, len( notes ) ) # adjust this value to get a reasonable response
+
+ self.gc.set_clip_mask( self.sampleNoteMask )
+ for i in range( N ): # draw N notes
+ note = notes.pop()
+ x = self.ticksToPixels( beats, note[0] )
+ endX = self.ticksToPixels( beats, note[0] + note[3] ) - 3 # include end cap offset
+ width = endX - x
+ y = self.pitchToPixels( note[1] )
+ # draw fill
+ self.gc.foreground = self.colors["Note_Fill"]
+ self.gc.set_clip_origin( x, y-self.sampleNoteHeight )
+ pixmap.draw_rectangle( self.gc, True, x+1, y+1, width+1, self.sampleNoteHeight-2 )
+ # draw border
+ self.gc.foreground = self.colors["Note_Border"]
+ self.gc.set_clip_origin( x, y )
+ pixmap.draw_rectangle( self.gc, True, x, y, width, self.sampleNoteHeight )
+ self.gc.set_clip_origin( endX-self.sampleNoteMask.endOffset, y )
+ pixmap.draw_rectangle( self.gc, True, endX, y, 3, self.sampleNoteHeight )
+ self.gc.set_clip_rectangle( self.clearClipMask )
+
+ if not len(notes):
+ self.predrawTarget = not self.predrawTarget
+ self.previewDA.queue_draw()
+
+ self.drawingPreview = False
+
+ if self.parametersDirty:
+ self.drawPreview()
+
+ return False
+
+ return True
+
+ def handlePreviewAlloc( self, widget, allocation ):
+ win = gtk.gdk.get_default_root_window()
+ self.previewDA.width = allocation.width
+ self.previewDA.height = allocation.height
+ self.predrawBuffer = [ gtk.gdk.Pixmap( win, allocation.width, allocation.height ),
+ gtk.gdk.Pixmap( win, allocation.width, allocation.height ) ]
+ self.clearClipMask = gtk.gdk.Rectangle( 0, 0, allocation.width, allocation.height )
+
+ self.pitchPerPixel = float(Config.NUMBER_OF_POSSIBLE_PITCHES-1) / (self.previewDA.height - self.sampleNoteHeight)
+ self.pixelsPerPitch = float(self.previewDA.height - self.sampleNoteHeight)/(Config.MAXIMUM_PITCH - Config.MINIMUM_PITCH)
+ self.pixelsPerTick = [0] + [ self.previewDA.width/float(i*Config.TICKS_PER_BEAT) for i in range(1,Config.MAXIMUM_BEATS+1) ]
+ self.ticksPerPixel = [0] + [ 1.0/self.pixelsPerTick[i] for i in range(1,Config.MAXIMUM_BEATS+1) ]
+
+ self.beatSpacing = [[0]]
+ for i in range(1,Config.MAXIMUM_BEATS+1):
+ self.beatSpacing.append( [ self.ticksToPixels( i, Config.TICKS_PER_BEAT*j ) for j in range(i) ] )
+
+ self.drawPreview( True )
+
+ def handlePreviewExpose( self, widget, event ):
+ widget.window.draw_drawable( self.gc, self.predrawBuffer[not self.predrawTarget], event.area.x, event.area.y, event.area.x, event.area.y, event.area.width, event.area.height )
+
+ def ticksToPixels( self, beats, ticks ):
+ return int(round( ticks * self.pixelsPerTick[beats] ))
+ def pitchToPixels( self, pitch ):
+ return int(round( ( Config.MAXIMUM_PITCH - pitch ) * self.pixelsPerPitch ))
+
+
+class propertiesPalette(Palette):
+ def __init__(self, label, edit):
+ Palette.__init__(self, label)
+ self.connect('popup', self.handlePopup)
+ self.connect('popdown', self.handlePopdown)
+
+ self.edit = edit
+
+ self.filterTypes = [_('None'), _('Lowpass'), _('Bandpass'), _('Highpass')]
+ self.geneTypes = [_('Line'),_('Drunk'),_('Drone and Jump'),_('Repeater'),_('Loop Segments')]
+ self.colors = [_('Purple'), _('Green'), _('Blue'), _('Yellow')]
+ self.currentFilterType = self.filterTypes[0]
+
+ self.line = Line(0, 100)
+ self.drunk = Drunk(0, 100)
+ self.droneAndJump = DroneAndJump(0, 100)
+ self.repeter = Repeter(0, 100)
+ self.loopseg = Loopseg(0, 100)
+ self.algoTypes = [self.line, self.drunk, self.droneAndJump, self.repeter, self.loopseg]
+ self.algorithm = self.algoTypes[0]
+ self.geneMinimum = 0
+ self.geneMaximum = 100
+ self.geneRandom = 20
+
+ self.setup = False
+ self.hidden = False
+ self.geneCheckButtonDic = {}
+
+ self.pageIds = []
+ self.context = "page"
+
+ self.mainBox = gtk.VBox()
+
+ self.gridDivisionBox = gtk.HBox()
+ self.gridDivisionLabel = gtk.Label(_('Grid division: '))
+ self.gridDivisionSliderAdj = gtk.Adjustment(4, 2, 12, 1, 1, 0)
+ self.gridDivisionSlider = gtk.HScale(adjustment = self.gridDivisionSliderAdj)
+ self.gridDivisionSlider.set_digits(0)
+ self.gridDivisionSlider.connect('button-release-event', self.handleBeat)
+ self.gridDivisionSlider.set_size_request(200,-1)
+ self.gridDivisionSlider.set_value_pos(gtk.POS_RIGHT)
+ self.gridDivisionBox.pack_start(self.gridDivisionLabel, False, False, padding = 5)
+ self.gridDivisionBox.pack_end(self.gridDivisionSlider, False, False, padding = 52)
+
+ self.pageColorBox = gtk.HBox()
+ self.pageColorLabel = gtk.Label(_('Page color: '))
+ self.pageColorComboBox = BigComboBox()
+ for color in (0,1,2,3):
+ self.pageColorComboBox.append_item(color, text = None, icon_name = Config.IMAGE_ROOT + 'pageThumbnailBG' + str(color) + '.png', size = (30,40))
+ self.pageColorComboBox.set_active(0)
+ self.pageColorComboBox.connect('changed', self.handleColor)
+ self.pageColorBox.pack_start(self.pageColorLabel, False, False, padding = 5)
+ self.pageColorBox.pack_end(self.pageColorComboBox, False, False, padding = 55)
+
+ self.pageSeparator = gtk.HSeparator()
+ self.pageSeparator.set_size_request(20, -1)
+
+ self.transposeBox = gtk.HBox()
+ self.transposeLabel = gtk.Label(_('Transposition: '))
+ self.transposeDownButton = ImageButton(Config.TAM_TAM_ROOT + '/icons/arrow-down.svg')
+ self.transposeDownButton.connect('clicked', self.stepPitch, -1)
+ self.transposeUpButton = ImageButton(Config.TAM_TAM_ROOT + '/icons/arrow-up.svg')
+ self.transposeUpButton.connect('clicked', self.stepPitch, 1)
+ self.transposeCheckButton = gtk.CheckButton()
+ self.transposeCheckButton.connect('toggled', self.handleGeneCheckButton)
+ self.geneCheckButtonDic['transpose'] = self.transposeCheckButton
+ self.transposeBox.pack_start(self.transposeLabel, False, False, padding = 5)
+ self.transposeBox.pack_end(self.transposeCheckButton, False, False, padding = 5)
+ self.transposeBox.pack_end(self.transposeUpButton, False, False, padding = 50)
+ self.transposeBox.pack_end(self.transposeDownButton, False, False, padding = 5)
+
+ self.volumeBox = gtk.HBox()
+ self.volumeLabel = gtk.Label(_('Volume: '))
+ self.volumeDownButton = ImageButton(Config.TAM_TAM_ROOT + '/icons/arrow-down.svg')
+ self.volumeDownButton.connect('clicked', self.stepVolume, -0.1)
+ self.volumeUpButton = ImageButton(Config.TAM_TAM_ROOT + '/icons/arrow-up.svg')
+ self.volumeUpButton.connect('clicked', self.stepVolume, 0.1)
+ self.volumeCheckButton = gtk.CheckButton()
+ self.volumeCheckButton.connect('toggled', self.handleGeneCheckButton)
+ self.geneCheckButtonDic['volume'] = self.volumeCheckButton
+ self.volumeBox.pack_start(self.volumeLabel, False, False, padding = 5)
+ self.volumeBox.pack_end(self.volumeCheckButton, False, False, padding = 5)
+ self.volumeBox.pack_end(self.volumeUpButton, False, False, padding = 50)
+ self.volumeBox.pack_end(self.volumeDownButton, False, False, padding = 5)
+
+ self.panBox = gtk.HBox()
+ self.panLabel = gtk.Label(_('Pan: '))
+ self.panSliderAdj = gtk.Adjustment(0.5, 0, 1, .1, .1, 0)
+ self.panSliderAdj.connect('value-changed', self.handlePan)
+ self.panSlider = gtk.HScale(adjustment = self.panSliderAdj)
+ self.panSlider.set_size_request(200,-1)
+ self.panSlider.set_value_pos(gtk.POS_RIGHT)
+ self.panSlider.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ self.panCheckButton = gtk.CheckButton()
+ self.panCheckButton.connect('toggled', self.handleGeneCheckButton)
+ self.geneCheckButtonDic['pan'] = self.panCheckButton
+ self.panBox.pack_start(self.panLabel, False, False, padding = 5)
+ self.panBox.pack_end(self.panCheckButton, False, False, padding = 5)
+ self.panBox.pack_end(self.panSlider, False, False, padding = 5)
+
+ self.reverbBox = gtk.HBox()
+ self.reverbLabel = gtk.Label(_('Reverb: '))
+ self.reverbSliderAdj = gtk.Adjustment(0.1, 0, 1, 0.1, 0.1, 0)
+ self.reverbSliderAdj.connect("value-changed", self.handleReverb)
+ self.reverbSlider = gtk.HScale(adjustment = self.reverbSliderAdj)
+ self.reverbSlider.set_size_request(200,-1)
+ self.reverbSlider.set_value_pos(gtk.POS_RIGHT)
+ self.reverbSlider.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ self.reverbCheckButton = gtk.CheckButton()
+ self.reverbCheckButton.connect('toggled', self.handleGeneCheckButton)
+ self.geneCheckButtonDic['reverb'] = self.reverbCheckButton
+ self.reverbBox.pack_start(self.reverbLabel, False, False, padding = 5)
+ self.reverbBox.pack_end(self.reverbCheckButton, False, False, padding = 5)
+ self.reverbBox.pack_end(self.reverbSlider, False, False, padding = 5)
+
+ self.attackDurBox = gtk.HBox()
+ self.attackDurLabel = gtk.Label(_('Attack duration: '))
+ self.attackDurSliderAdj = gtk.Adjustment(0.04, 0.03, 1, .01, .01, 0)
+ self.attackDurSliderAdj.connect('value-changed', self.handleAttack)
+ self.attackDurSlider = gtk.HScale(adjustment = self.attackDurSliderAdj)
+ self.attackDurSlider.set_size_request(200,-1)
+ self.attackDurSlider.set_value_pos(gtk.POS_RIGHT)
+ self.attackDurSlider.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ self.attackDurCheckButton = gtk.CheckButton()
+ self.attackDurCheckButton.connect('toggled', self.handleGeneCheckButton)
+ self.geneCheckButtonDic['attack'] = self.attackDurCheckButton
+ self.attackDurBox.pack_start(self.attackDurLabel, False, False, padding = 5)
+ self.attackDurBox.pack_end(self.attackDurCheckButton, False, False, padding = 5)
+ self.attackDurBox.pack_end(self.attackDurSlider, False, False, padding = 5)
+
+ self.decayDurBox = gtk.HBox()
+ self.decayDurLabel = gtk.Label(_('Decay duration: '))
+ self.decayDurSliderAdj = gtk.Adjustment(0.31, 0.03, 1, .01, .01, 0)
+ self.decayDurSliderAdj.connect('value-changed', self.handleDecay)
+ self.decayDurSlider = gtk.HScale(adjustment = self.decayDurSliderAdj)
+ self.decayDurSlider.set_size_request(200,-1)
+ self.decayDurSlider.set_value_pos(gtk.POS_RIGHT)
+ self.decayDurSlider.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ self.decayDurCheckButton = gtk.CheckButton()
+ self.decayDurCheckButton.connect('toggled', self.handleGeneCheckButton)
+ self.geneCheckButtonDic['decay'] = self.decayDurCheckButton
+ self.decayDurBox.pack_start(self.decayDurLabel, False, False, padding = 5)
+ self.decayDurBox.pack_end(self.decayDurCheckButton, False, False, padding = 5)
+ self.decayDurBox.pack_end(self.decayDurSlider, False, False, padding = 5)
+
+ self.filterTypeBox = gtk.HBox()
+ self.filterTypeLabel = gtk.Label(_('Filter Type: '))
+ self.filterTypeComboBox = BigComboBox()
+ for filtertype in self.filterTypes:
+ self.filterTypeComboBox.append_item(self.filterTypes.index(filtertype), filtertype, Config.TAM_TAM_ROOT + '/icons/test.svg', (30,30))
+ self.filterTypeComboBox.connect('changed', self.handleFilterTypes)
+ self.filterTypeBox.pack_start(self.filterTypeLabel, False, False, padding = 5)
+ self.filterTypeBox.pack_end(self.filterTypeComboBox, False, False, padding = 55)
+
+ self.filterCutoffBox = gtk.HBox()
+ self.filterCutoffLabel = gtk.Label(_('Filter cutoff: '))
+ self.filterCutoffSliderAdj = gtk.Adjustment(1000, 100, 7000, 100, 100, 0)
+ self.filterCutoffSliderAdj.connect('value-changed', self.handleFilter)
+ self.filterCutoffSlider = gtk.HScale(adjustment = self.filterCutoffSliderAdj)
+ self.filterCutoffSlider.set_size_request(200,-1)
+ self.filterCutoffSlider.set_value_pos(gtk.POS_RIGHT)
+ self.filterCutoffSlider.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ self.filterCutoffCheckButton = gtk.CheckButton()
+ self.filterCutoffCheckButton.connect('toggled', self.handleGeneCheckButton)
+ self.geneCheckButtonDic['filter'] = self.filterCutoffCheckButton
+ self.filterCutoffBox.pack_start(self.filterCutoffLabel, False, False, padding = 5)
+ self.filterCutoffBox.pack_end(self.filterCutoffCheckButton, False, False, padding = 5)
+ self.filterCutoffBox.pack_end(self.filterCutoffSlider, False, False, padding = 5)
+
+ self.generationMainBox = gtk.VBox()
+ self.generationSeparator = gtk.HSeparator()
+ self.generationLabel = gtk.Label(_('Generation'))
+
+ self.generationTypeBox = gtk.HBox()
+ self.generationTypeLabel = gtk.Label(_('Type: '))
+ self.generationTypeComboBox = BigComboBox()
+ for genetype in self.geneTypes:
+ self.generationTypeComboBox.append_item(self.geneTypes.index(genetype), genetype, Config.TAM_TAM_ROOT + '/icons/test.svg', (30,30))
+ self.generationTypeComboBox.connect('changed', self.handleGeneTypes)
+ self.generationTypeComboBox.set_active(0)
+ self.generationTypeBox.pack_start(self.generationTypeLabel, False, False, padding = 5)
+ self.generationTypeBox.pack_end(self.generationTypeComboBox, False, False, padding = 55)
+
+ self.minimumBox = gtk.HBox()
+ self.minimumLabel = gtk.Label(_('Minimum: '))
+ self.minimumSliderAdj = gtk.Adjustment(0, 0, 100, 1, 1, 0)
+ self.minimumSliderAdj.connect('value-changed', self.handleMinimum)
+ self.minimumSlider = gtk.HScale(adjustment = self.minimumSliderAdj)
+ self.minimumSlider.set_size_request(200,-1)
+ self.minimumSlider.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ self.minimumSlider.set_value_pos(gtk.POS_RIGHT)
+ self.minimumBox.pack_start(self.minimumLabel, False, False, padding = 5)
+ self.minimumBox.pack_end(self.minimumSlider, False, False, padding = 52)
+
+ self.maximumBox = gtk.HBox()
+ self.maximumLabel = gtk.Label(_('Maximum: '))
+ self.maximumSliderAdj = gtk.Adjustment(100, 0, 100, 1, 1, 0)
+ self.maximumSliderAdj.connect('value-changed', self.handleMaximum)
+ self.maximumSlider = gtk.HScale(adjustment = self.maximumSliderAdj)
+ self.maximumSlider.set_size_request(200,-1)
+ self.maximumSlider.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ self.maximumSlider.set_value_pos(gtk.POS_RIGHT)
+ self.maximumBox.pack_start(self.maximumLabel, False, False, padding = 5)
+ self.maximumBox.pack_end(self.maximumSlider, False, False, padding = 52)
+
+ self.randomBox = gtk.HBox()
+ self.randomLabel = gtk.Label(_('Random: '))
+ self.randomSliderAdj = gtk.Adjustment(20, 0, 100, 1, 1, 0)
+ self.randomSliderAdj.connect('value-changed', self.handleRandom)
+ self.randomSlider = gtk.HScale(adjustment = self.randomSliderAdj)
+ self.randomSlider.set_size_request(200,-1)
+ self.randomSlider.set_update_policy(gtk.UPDATE_DISCONTINUOUS)
+ self.randomSlider.set_value_pos(gtk.POS_RIGHT)
+ self.randomBox.pack_start(self.randomLabel, False, False, padding = 5)
+ self.randomBox.pack_end(self.randomSlider, False, False, padding = 52)
+
+ self.decisionBox = gtk.HBox()
+ self.acceptButton = ImageButton(Config.TAM_TAM_ROOT + '/icons/accept.svg')
+ self.acceptButton.connect('clicked', self.acceptGeneration)
+ self.cancelButton = ImageButton(Config.TAM_TAM_ROOT + '/icons/cancel.svg')
+ self.cancelButton.connect('clicked', self.resetGeneCheckButton)
+ self.decisionBox.pack_start(self.cancelButton, False, False, padding = 5)
+ self.decisionBox.pack_start(self.acceptButton, False, False, padding = 5)
+
+ self.mainBox.pack_start(self.gridDivisionBox, padding = 3)
+ self.mainBox.pack_start(self.pageColorBox, padding = 3)
+ self.mainBox.pack_start(self.pageSeparator, padding = 10)
+ self.mainBox.pack_start(self.transposeBox, padding = 3)
+ self.mainBox.pack_start(self.volumeBox, padding = 3)
+ self.mainBox.pack_start(self.panBox, padding = 3)
+ self.mainBox.pack_start(self.reverbBox, padding = 3)
+ self.mainBox.pack_start(self.attackDurBox, padding = 3)
+ self.mainBox.pack_start(self.decayDurBox, padding = 3)
+ self.mainBox.pack_start(self.filterTypeBox, padding = 3)
+ self.mainBox.pack_start(self.filterCutoffBox, padding = 3)
+ self.generationMainBox.pack_start(self.generationSeparator, padding = 5)
+ self.generationMainBox.pack_start(self.generationLabel, padding = 10)
+ self.generationMainBox.pack_start(self.generationTypeBox, padding = 3)
+ self.generationMainBox.pack_start(self.minimumBox, padding = 3)
+ self.generationMainBox.pack_start(self.maximumBox, padding = 3)
+ self.generationMainBox.pack_start(self.randomBox, padding = 3)
+ self.generationMainBox.pack_start(self.decisionBox, padding = 3)
+ self.mainBox.pack_start(self.generationMainBox, padding = 3)
+ self.mainBox.show_all()
+
+ self.generationMainBox.hide()
+
+ self.set_content(self.mainBox)
+
+ def handlePopup(self, widget, data = None):
+ if self.edit.getContext() == 0: #Page
+ self.setContext('page', self.edit._generateToolbar._generationPalette.scale, self.edit.tuneInterface.getSelectedIds())
+ elif self.edit.getContext() == 1: #Track
+ self.setContext('track', self.edit._generateToolbar._generationPalette.scale, self.edit.tuneInterface.getSelectedIds(), [ i for i in range(Config.NUMBER_OF_TRACKS) if self.edit.trackSelected[i] ])
+ elif self.edit.getContext() == 2: #Note
+ ids = self.edit.trackInterface.getSelectedNotes()
+ notes = { self.edit.displayedPage: {} }
+ for t in range(Config.NUMBER_OF_TRACKS):
+ if len(ids[t]):
+ notes[self.edit.displayedPage][t] = [ self.edit.noteDB.getNote( self.edit.displayedPage, t, id ) for id in ids[t] ]
+ self.setContext('note', self.edit._generateToolbar._generationPalette.scale, notes = notes)
+
+ def handlePopdown(self, widget, data = None):
+ self.resetGeneCheckButton(self.cancelButton)
+
+ def setContext( self, context, scale, pageIds = None, trackIds = None, notes = {} ):
+ self.context = context
+ self.scale = GenerationConstants.SCALES[scale]
+ self.notes = {}
+ self.pageIds = pageIds
+ self.trackIds = trackIds
+
+ if context == "page":
+ self.trackIds = [0,1,2,3,4]
+ for p in pageIds:
+ self.notes[p] = {}
+ for t in range(Config.NUMBER_OF_TRACKS):
+ self.notes[p][t] = self.edit.noteDB.getNotesByTrack( p, t )
+ page = self.edit.noteDB.getPage(pageIds[0])
+ self.gridDivisionSliderAdj.set_value(page.beats)
+ elif context == "track":
+ for p in pageIds:
+ self.notes[p] = {}
+ for t in trackIds:
+ self.notes[p][t] = self.edit.noteDB.getNotesByTrack( p, t )
+ else:
+ self.notes = notes
+ self.pageIds = self.notes.keys()
+ self.trackIds = self.notes[self.pageIds[0]].keys()
+
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ # initialize values from first note
+ self.setup = True
+ n = self.notes[p][t][0]
+ self.panSliderAdj.set_value( n.cs.pan )
+ self.reverbSliderAdj.set_value( n.cs.reverbSend )
+ self.attackDurSliderAdj.set_value( n.cs.attack )
+ self.decayDurSliderAdj.set_value( n.cs.decay )
+ self.filterTypeComboBox.set_active(n.cs.filterType)
+ self.currentFilterType = n.cs.filterType
+ self.filterCutoffSliderAdj.set_value( n.cs.filterCutoff )
+ self.setup = False
+
+ def acceptGeneration( self, widget ):
+ valList = [self.geneMinimum, self.geneMaximum, self.geneRandom]
+ if self.geneCheckButtonDic['transpose'].get_active(): self.algoPitch(valList, self.algorithm)
+ if self.geneCheckButtonDic['volume'].get_active(): self.algoVolume(valList, self.algorithm)
+ if self.geneCheckButtonDic['pan'].get_active(): self.algoPan(valList, self.algorithm)
+ if self.geneCheckButtonDic['reverb'].get_active(): self.algoReverb(valList, self.algorithm)
+ if self.geneCheckButtonDic['attack'].get_active(): self.algoAttack(valList, self.algorithm)
+ if self.geneCheckButtonDic['decay'].get_active(): self.algoDecay(valList, self.algorithm)
+ if self.geneCheckButtonDic['filter'].get_active(): self.algoCutoff(valList, self.algorithm)
+
+ def resetGeneCheckButton(self, widget):
+ if self.hidden:
+ self.generationMainBox.hide()
+
+ for key in self.geneCheckButtonDic:
+ self.geneCheckButtonDic[key].set_active(False)
+
+ def handleGeneCheckButton(self, widget, data = None):
+ self.hidden = True
+ if widget.get_active():
+ self.generationMainBox.show()
+ else:
+ for key in self.geneCheckButtonDic:
+ if self.geneCheckButtonDic[key].get_active():
+ self.hidden = False
+ if self.hidden:
+ self.generationMainBox.hide()
+
+
+ def handleBeat(self, widget, signal_id):
+ beats = int(widget.get_adjustment().value)
+ stream = []
+ for page in self.pageIds:
+ stream += [ page, beats ]
+ if len(stream):
+ self.edit.noteDB.updatePages( [ PARAMETER.PAGE_BEATS, len(stream)//2 ] + stream )
+
+ def handleColor(self, widget):
+ index = widget.props.value
+ stream = []
+ for page in self.pageIds:
+ stream += [ page, index ]
+ if len(stream):
+ self.edit.noteDB.updatePages( [ PARAMETER.PAGE_COLOR, len(stream)//2 ] + stream )
+
+ def stepPitch(self, widget, step):
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ substream = []
+ if step > 0:
+ if t != Config.NUMBER_OF_TRACKS-1: # regular note
+ for n in self.notes[p][t]:
+ if n.cs.pitch != Config.MAXIMUM_PITCH:
+ substream += [ n.id, min( Config.MAXIMUM_PITCH, n.cs.pitch + step ) ]
+ else: # drum note
+ for n in self.notes[p][t]:
+ if n.cs.pitch != Config.MAXIMUM_PITCH_DRUM:
+ substream += [ n.id, min( Config.MAXIMUM_PITCH_DRUM, n.cs.pitch + step*Config.PITCH_STEP_DRUM ) ]
+ else:
+ if t != Config.NUMBER_OF_TRACKS-1: # regular note
+ for n in self.notes[p][t]:
+ if n.cs.pitch != Config.MINIMUM_PITCH:
+ substream += [ n.id, max( Config.MINIMUM_PITCH, n.cs.pitch + step ) ]
+ else: # drum note
+ for n in self.notes[p][t]:
+ if n.cs.pitch != Config.MINIMUM_PITCH_DRUM:
+ substream += [ n.id, max( Config.MINIMUM_PITCH_DRUM, n.cs.pitch + step*Config.PITCH_STEP_DRUM ) ]
+ if len(substream):
+ stream += [ p, t, PARAMETER.PITCH, len(substream)//2 ] + substream
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def algoPitch( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ scaleLength = len(self.scale)-1
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ if self.trackIds[t] != Config.NUMBER_OF_TRACKS-1:
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, self.scale[int(val*0.01*scaleLength)]+36 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.PITCH, len(substream)//2 ] + substream
+ else:
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ val = int((val*0.12)*2+24)
+ if val in GenerationConstants.DRUMPITCH.keys():
+ val = GenerationConstants.DRUMPITCH[val]
+ substream += [ n.id, val ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.PITCH, len(substream)//2 ] + substream
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def stepVolume(self, widget, step):
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ substream = []
+ if step > 0:
+ for n in self.notes[p][t]:
+ if n.cs.amplitude != Config.MAXIMUM_AMPLITUDE:
+ substream += [ n.id, min( Config.MAXIMUM_AMPLITUDE, n.cs.amplitude + step ) ]
+ else:
+ for n in self.notes[p][t]:
+ if n.cs.amplitude != Config.MINIMUM_AMPLITUDE:
+ substream += [ n.id, max( Config.MINIMUM_AMPLITUDE, n.cs.amplitude + step ) ]
+ if len(substream):
+ stream += [ p, t, PARAMETER.AMPLITUDE, len(substream)//2 ] + substream
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+
+ def algoVolume( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, min( Config.MAXIMUM_AMPLITUDE, val*0.01 ) ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.AMPLITUDE, len(substream)//2 ] + substream
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def handlePan(self, adjust):
+ if not self.setup:
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ stream += [ p, t, PARAMETER.PAN, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ stream += [ n.id, adjust.value ]
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def algoPan( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, val*0.01 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.PAN, len(substream)//2 ] + substream
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def handleReverb(self, adjust):
+ if not self.setup:
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ stream += [ p, t, PARAMETER.REVERB, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ stream += [ n.id, adjust.value ]
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def algoReverb( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, val*0.02 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.REVERB, len(substream)//2 ] + substream
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def handleAttack(self, adjust):
+ if not self.setup:
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ stream += [ p, t, PARAMETER.ATTACK, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ stream += [ n.id, adjust.value ]
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def algoAttack( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, val*0.01 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.ATTACK, len(substream)//2 ] + substream
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def handleDecay(self, adjust):
+ if not self.setup:
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ stream += [ p, t, PARAMETER.DECAY, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ stream += [ n.id, adjust.value ]
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+
+ def algoDecay( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, val*0.01 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.DECAY, len(substream)//2 ] + substream
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def handleFilterTypes(self, widget):
+ self.currentFilterType = widget.props.value
+
+ if not self.currentFilterType:
+ self.filterCutoffSlider.set_sensitive(False)
+ else:
+ self.filterCutoffSlider.set_sensitive(True)
+
+ if not self.setup:
+ if self.currentFilterType:
+ typestream = []
+ cutoffstream = []
+ cutoff = self.filterCutoffSliderAdj.value
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ substream = []
+ typestream += [ p, t, PARAMETER.FILTERTYPE, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ typestream += [ n.id, self.currentFilterType ]
+ if n.cs.filterCutoff != cutoff:
+ substream += [ n.id, cutoff ]
+ if len(substream):
+ cutoffstream += [ p, t, PARAMETER.FILTERCUTOFF, len(substream)//2 ] + substream
+ if len(typestream):
+ self.edit.noteDB.updateNotes( typestream + [-1] )
+ if len(cutoffstream):
+ self.edit.noteDB.updateNotes( cutoffstream + [-1] )
+ else:
+ self.currentFilterType = 0
+ typestream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ typestream += [ p, t, PARAMETER.FILTERTYPE, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ typestream += [ n.id, 0 ]
+ if len(typestream):
+ self.edit.noteDB.updateNotes( typestream + [-1] )
+
+ def handleFilter(self, adjust):
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ stream += [ p, t, PARAMETER.FILTERCUTOFF, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ stream += [ n.id, adjust.value ]
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def algoCutoff( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, val*70+100 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.FILTERCUTOFF, len(substream)//2 ] + substream
+ if len(stream):
+ self.edit.noteDB.updateNotes( stream + [-1] )
+
+ def handleGeneTypes(self, widget):
+ self.algorithm = self.algoTypes[widget.props.value]
+
+ def handleMinimum(self, adjust):
+ self.geneMinimum = int(adjust.value)
+
+ def handleMaximum(self, adjust):
+ self.geneMaximum = int(adjust.value)
+
+ def handleRandom(self, adjust):
+ self.geneRandom = int(adjust.value)
diff --git a/TamTamEdit.activity/Edit/HitInterface.py b/TamTamEdit.activity/Edit/HitInterface.py
new file mode 100644
index 0000000..6e77908
--- /dev/null
+++ b/TamTamEdit.activity/Edit/HitInterface.py
@@ -0,0 +1,195 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+from Util.NoteDB import PARAMETER
+from Edit.NoteInterface import NoteInterface
+import Config
+
+class HitInterface( NoteInterface ):
+
+ def __init__( self, noteDB, owner, note ):
+ NoteInterface.__init__( self, noteDB, owner, note )
+
+ self.width = self.height = Config.HIT_HEIGHT
+ self.imgWidth = self.imgHeight = Config.HIT_HEIGHT + Config.HIT_IMAGE_PADDING_MUL2
+
+ self.firstTransform = True
+ self.updateTransform()
+
+ def updateTransform( self ):
+ if self.note.page in self.owner.getActivePages():
+ if not self.firstTransform:
+ oldX = self.imgX
+ oldY = self.imgY
+ oldEndX = self.imgX + self.imgWidth
+ dirty = True
+ else:
+ dirty = False
+
+ beats = self.noteDB.getPage( self.note.page ).beats
+ if self.note.cs.onset != self.oldOnset or beats != self.oldBeats:
+ self.x = self.owner.ticksToPixels( beats, self.note.cs.onset )
+ self.x += self.origin[0]
+ self.imgX = self.x - Config.NOTE_IMAGE_PADDING
+ self.oldOnset = self.note.cs.onset
+ self.oldBeats = beats
+ if self.note.cs.pitch != self.oldPitch:
+ self.y = self.owner.pitchToPixelsDrum( self.note.cs.pitch ) + self.origin[1]
+ self.imgY = self.y - Config.NOTE_IMAGE_PADDING
+ self.oldPitch = self.note.cs.pitch
+
+ if dirty:
+ if self.firstTransform:
+ self.owner.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.note.page )
+ self.firstTransform = False
+ else:
+ x = min( self.imgX, oldX )
+ y = min( self.imgY, oldY )
+ endx = max( self.imgX + self.imgWidth, oldEndX )
+ endy = max( self.imgY, oldY ) + self.imgHeight
+ self.owner.invalidate_rect( x, y, endx-x, endy-y, self.note.page )
+
+ self.firstTransform = False
+
+ def updateDragLimits( self, dragLimits, leftBound, rightBound, widthBound, maxRightBound ):
+ left = 0 - self.note.cs.onset
+ right = maxRightBound - self.note.cs.duration - self.note.cs.onset
+ up = Config.MAXIMUM_PITCH_DRUM - self.note.cs.pitch
+ down = Config.MINIMUM_PITCH_DRUM - self.note.cs.pitch
+
+ if dragLimits[0][0] < left: dragLimits[0][0] = left
+ if dragLimits[0][1] > right: dragLimits[0][1] = right
+ if dragLimits[1][0] < down: dragLimits[1][0] = down
+ if dragLimits[1][1] > up: dragLimits[1][1] = up
+
+ # store the current loc as a reference point
+ self.baseOnset = self.note.cs.onset
+ self.basePitch = self.note.cs.pitch
+
+ #=======================================================
+ # Events
+
+ # handleButtonPress returns:
+ # -1, event occurs before us so don't bother checking any later notes
+ # 0, event didn't hit
+ # 1, event was handled
+ def handleButtonPress( self, emitter, event ):
+ eX = event.x - self.x
+ if eX < 0:
+ return -1 # event occurs before us, no point in checking further
+ if eX > self.width:
+ return 0 # no X overlap
+
+ eY = event.y - self.y
+ if eY < 0 or eY > self.height:
+ return 0 # not a hit
+
+ if event.button == 3:
+ print "Show some note parameters!?!"
+ #self.noteParameters = NoteParametersWindow( self.note, self.getNoteParameters )
+ return 1 # handled
+
+ playSample = False
+
+ if event.type == gtk.gdk._2BUTTON_PRESS: # select bar
+ self.potentialDeselect = False
+ start = 0
+ check = self.note.cs.onset - Config.TICKS_PER_BEAT
+ while start <= check: start += Config.TICKS_PER_BEAT
+ stop = start + Config.TICKS_PER_BEAT
+ check += 1
+ while stop < check: stop += Config.TICKS_PER_BEAT
+ emitter.selectNotesByBar( self.note.track, start, stop )
+ elif event.type == gtk.gdk._3BUTTON_PRESS: # select track
+ self.potentialDeselect = False
+ emitter.selectNotesByTrack( self.note.track )
+ else:
+ if self.getSelected(): # we already selected, might want to delected
+ self.potentialDeselect = True
+ else:
+ emitter.selectNotes( { self.note.track: [ self ] } )
+ playSample = True
+
+ percent = eX/self.width
+ if percent < 0.5: emitter.setCurrentAction( "note-drag-onset", self )
+ else:
+ emitter.setCurrentAction( "note-drag-pitch-drum", self )
+ if playSample: self.playSampleNote()
+
+ return 1
+
+ def noteDragPitch( self, dp, stream ):
+ self.potentialDeselect = False
+ if dp != self.lastDragP and not dp%2:
+ self.lastDragP = dp
+ stream += [ self.note.id, self.basePitch + dp ]
+
+ def noteDragDuration( self, dd, stream ):
+ return
+
+ def noteDecOnset( self, step, leftBound, stream ):
+ if self.selected:
+ if leftBound < self.note.cs.onset:
+ onset = max( self.note.cs.onset+step, leftBound )
+ stream += [ self.note.id, onset ]
+ return leftBound
+
+ def noteIncOnset( self, step, rightBound, stream ):
+ if self.selected:
+ if rightBound > self.end:
+ onset = min( self.end+step, rightBound ) - self.note.cs.duration
+ stream += [ self.note.id, onset ]
+ return rightBound
+
+ def noteDecPitch( self, step, stream ):
+ if self.note.cs.pitch > Config.MINIMUM_PITCH_DRUM:
+ stream += [ self.note.id, max( self.note.cs.pitch+2*step, Config.MINIMUM_PITCH_DRUM ) ]
+
+ def noteIncPitch( self, step, stream ):
+ if self.note.cs.pitch < Config.MAXIMUM_PITCH_DRUM:
+ stream += [ self.note.id, min( self.note.cs.pitch+2*step, Config.MAXIMUM_PITCH_DRUM ) ]
+
+ def noteDecDuration( self, step, stream ):
+ return
+
+ def noteIncDuration( self, step, rightBound, stream ):
+ return
+
+ # updateTooltip returns:
+ # -1, event occurs before us so don't bother checking any later notes
+ # 0, event didn't hit
+ # 1, event was handled
+ def updateTooltip( self, emitter, event ):
+ eX = event.x - self.x
+ if eX < 0:
+ return -1 # event occurs before us, no point in checking further
+ if eX > self.width:
+ return 0 # no X overlap
+
+ eY = event.y - self.y
+ if eY < 0 or eY > self.height:
+ return 0 # not a hit
+
+ percent = eX/self.width
+ if percent < 0.5: emitter.setCursor("drag-onset")
+ else: emitter.setCursor("drag-pitch")
+
+ return 1 # we handled it
+
+ #=======================================================
+ # Draw
+
+ def draw( self, win, gc, startX, stopX ):
+ if stopX < self.imgX: return False # we don't need to draw and no one after us will draw
+ if startX > self.imgX + self.imgWidth: return True # we don't need to draw, but maybe a later note does
+
+ gc.foreground = self.color
+ win.draw_rectangle( gc, True, self.x+2, self.y+2, self.width-4, self.height-4 )
+
+ if self.selected: img = self.imageSelected
+ else: img = self.image
+ win.draw_pixbuf( gc, img, 0, 0, self.imgX, self.imgY, self.imgWidth, self.imgHeight, gtk.gdk.RGB_DITHER_NONE )
+
+ return True # we drew something
+
diff --git a/TamTamEdit.activity/Edit/KeyboardInput.py b/TamTamEdit.activity/Edit/KeyboardInput.py
new file mode 100644
index 0000000..d1a0d83
--- /dev/null
+++ b/TamTamEdit.activity/Edit/KeyboardInput.py
@@ -0,0 +1,97 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+from Framework import Note
+from Framework.CSound.CSoundConstants import CSoundConstants
+from Framework.Generation.GenerationConstants import GenerationConstants
+from GUI.Core.KeyMapping import KEY_MAP
+
+
+class KeyboardInput:
+ def __init__( self , getCurrentTick , getTrackInstruments , getTrackDictionary , getSelectedTrackIDs , mainWindowUpdateCallback , pagePlayerUpdateCallback , getCurrentPageIDCallback ):
+ self.active = False
+ self.record = False
+ self.monophonic = False
+ self.key_dict = dict()
+
+ self.getCurrentTick = getCurrentTick
+ self.getTrackInstruments = getTrackInstruments
+ self.getTrackDictionary = getTrackDictionary
+ self.getSelectedTrackIDs = getSelectedTrackIDs
+ self.mainWindowUpdateCallback = mainWindowUpdateCallback
+ self.pagePlayerUpdateCallback = pagePlayerUpdateCallback
+ self.getCurrentPageIDCallback = getCurrentPageIDCallback
+
+ def onKeyPress(self,widget,event):
+ if not self.active:
+ return
+ if self.record:
+ self.monophonic = False
+
+ key = event.hardware_keycode
+ # If the key is already in the dictionnary, exit function (to avoir key repeats)
+ if self.key_dict.has_key(key):
+ return
+ # Assign on which track the note will be created according to the number of keys pressed
+ track = len(self.key_dict)+10
+ if self.monophonic:
+ track = 10
+ # If the pressed key is in the keymap
+ if KEY_MAP.has_key(key):
+ # CsoundNote parameters
+ onset = self.getCurrentTick()
+ pitch = KEY_MAP[key]
+ duration = -1
+ instrument = self.getTrackInstruments()[0]
+ # get instrument from top selected track if a track is selected
+ if self.getSelectedTrackIDs():
+ instrument = self.getTrackInstruments()[min(self.getSelectedTrackIDs())]
+
+ if instrument == 'drum1kit':
+ if GenerationConstants.DRUMPITCH.has_key( pitch ):
+ instrument = CSoundConstants.DRUM1INSTRUMENTS[ GenerationConstants.DRUMPITCH[ pitch ] ]
+ else:
+ instrument = CSoundConstants.DRUM1INSTRUMENTS[ pitch ]
+ pitch = 36
+ duration = 100
+
+ if CSoundConstants.INSTRUMENTS[instrument].csoundInstrumentID == 102:
+ duration = 100
+
+ # Create and play the note
+ self.key_dict[key] = Note.note_new(onset = 0,
+ pitch = pitch,
+ amplitude = 1,
+ pan = 0.5,
+ duration = duration,
+ trackID = track,
+ fullDuration = False,
+ instrument = instrument,
+ instrumentFlag = instrument)
+ Note.note_play(self.key_dict[key])
+
+ def onKeyRelease(self,widget,event):
+ if not self.active:
+ return
+ key = event.hardware_keycode
+
+ if KEY_MAP.has_key(key):
+ self.key_dict[key]['duration'] = 0
+ self.key_dict[key]['amplitude'] = 0
+ self.key_dict[key]['dirty'] = True
+ Note.note_play(self.key_dict[key])
+ self.key_dict[key]['duration'] = self.getCurrentTick() - self.key_dict[key]['onset']
+ #print "onset",self.key_dict[key].onset
+ #print "dur",self.key_dict[key].duration
+ if self.record and len( self.getSelectedTrackIDs() ) != 0:
+ self.key_dict[key]['amplitude'] = 1
+ self.getTrackDictionary()[min(self.getSelectedTrackIDs())][self.getCurrentPageIDCallback()].append(self.key_dict[key])
+ self.mainWindowUpdateCallback()
+ self.pagePlayerUpdateCallback()
+ del self.key_dict[key]
+
+
+ def onButtonPress(self,widget,event):
+ pass
+
diff --git a/TamTamEdit.activity/Edit/MainWindow.py b/TamTamEdit.activity/Edit/MainWindow.py
new file mode 100644
index 0000000..58f82f1
--- /dev/null
+++ b/TamTamEdit.activity/Edit/MainWindow.py
@@ -0,0 +1,2091 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import gobject
+
+from Util.ThemeWidgets import *
+from Util.Profiler import TP
+from Util import NoteDB
+from Util.NoteDB import PARAMETER
+from Util import ControlStream
+from Util.CSoundClient import new_csound_client
+from Util.InstrumentPanel import InstrumentPanel
+from Util.InstrumentPanel import DrumPanel
+from Util.CSoundNote import CSoundNote
+from EditToolbars import mainToolbar
+from EditToolbars import generateToolbar
+from gettext import gettext as _
+from subprocess import Popen
+from sugar.graphics.palette import Palette, WidgetInvoker
+import time
+import os
+import commands
+import random
+
+class CONTEXT:
+ PAGE = 0
+ TRACK = 1
+ NOTE = 2
+
+import Config
+from SubActivity import SubActivity
+
+from Generation.GenerationConstants import GenerationConstants
+from Generation.GenerationParametersWindow import GenerationParametersWindow
+from Edit.Properties import Properties
+from Edit.TrackInterface import TrackInterface, TrackInterfaceParasite
+from Edit.TuneInterface import TuneInterface, TuneInterfaceParasite
+
+from Generation.Generator import generator1, GenerationParameters
+
+Tooltips = Config.Tooltips()
+KEY_MAP_PIANO = Config.KEY_MAP_PIANO
+
+#-----------------------------------
+# The main TamTam window
+#-----------------------------------
+class MainWindow( SubActivity ):
+
+ def __init__( self, activity, set_mode ):
+ self.csnd = new_csound_client()
+ self.tooltips = gtk.Tooltips()
+ self.activity = activity
+ for i in [6,7,8,9,10]:
+ self.csnd.setTrackVolume(100, i)
+ self.trackCount = 6
+
+ self.scale = GenerationConstants.DEFAULT_SCALE
+
+ # META ALGO: [section, variation or not, nPages] A B A C
+ # TODO: Different parameters sets for each tracks
+ self.tuneForm = [[0, False, 4], [1, False, 4], [0, True, 4], [2, False, 2]]
+
+ def init_data( ):
+ TP.ProfileBegin("init_data")
+ self._data = {}
+
+ #[ volume, ... ]
+ self._data['track_volume'] = [ Config.DEFAULT_VOLUME ] * Config.NUMBER_OF_TRACKS
+ self._data['track_mute'] = [ 1.0 ] * Config.NUMBER_OF_TRACKS
+
+ #[ instrument index, ... ]
+ self.trackInstrumentDefault = [
+ Config.INSTRUMENTS["kalimba"],
+ Config.INSTRUMENTS["kalimba"],
+ Config.INSTRUMENTS["kalimba"],
+ Config.INSTRUMENTS["kalimba"],
+ Config.INSTRUMENTS["drum2kit"] ]
+ self.trackInstrument = self.trackInstrumentDefault[:]
+ if len(self.trackInstrument) != Config.NUMBER_OF_TRACKS: raise 'error'
+ self.drumIndex = Config.NUMBER_OF_TRACKS - 1
+
+ #second instrument for melodic tracks
+ self.trackInstrument2Default = [ None, None, None, None]
+ self.trackInstrument2 = self.trackInstrument2Default[:]
+
+ self._data['volume'] = Config.DEFAULT_VOLUME
+ self._data['tempo'] = Config.PLAYER_TEMPO
+
+ self.playScope = "Selection"
+ self.displayedPage = -1
+ self.trackSelected = [ 0 for i in range(Config.NUMBER_OF_TRACKS) ]
+ self.trackActive = [ 1 for i in range(Config.NUMBER_OF_TRACKS) ]
+
+ self.pages_playing = []
+ self.journalCalled = True
+
+ self.noteDB = NoteDB.NoteDB()
+ TP.ProfileEnd("init_data")
+
+ def formatRoundBox( box, fillcolor ):
+ box.set_radius( 7 )
+ box.set_border_width( 1 )
+ box.set_fill_color( fillcolor )
+ box.set_border_color( Config.PANEL_BCK_COLOR )
+ return box
+
+ def init_GUI():
+
+ self.GUI = {}
+ self.GUI["2main"] = gtk.VBox()
+
+ def draw_inst_icons():
+ instrumentNames = [ k for k in Config.INSTRUMENTS.keys() if (k[0:4] != 'drum' and k[0:4] != 'guid') or Config.INSTRUMENTS[k].category == "kit" ]
+ self.GUI["2instrumentIcons"] = {}
+ for instrument in instrumentNames:
+ try:
+ self.GUI["2instrumentIcons"][instrument] = gtk.gdk.pixbuf_new_from_file(Config.IMAGE_ROOT + instrument + '.png')
+ except:
+ self.GUI["2instrumentIcons"][instrument] = gtk.gdk.pixbuf_new_from_file(Config.IMAGE_ROOT + 'generic.png')
+ TP.ProfileBegin("init_GUI::instrument icons")
+ draw_inst_icons()
+ TP.ProfileEnd("init_GUI::instrument icons")
+
+
+ #------------------------------------------------------------------------
+ # page
+ self.GUI["2page"] = gtk.HBox()
+ self.GUI["2main"].pack_start( self.GUI["2page"], False )
+ if 1: # + instrument panel
+ self.GUI["2instrumentPanel"] = gtk.VBox()
+ self.GUI["2instrumentPanel"].set_size_request( 132, -1 )
+ self.GUI["2page"].pack_start( self.GUI["2instrumentPanel"], False )
+ # + + instrument 1 box
+ self.GUI["2instrument1Box"] = formatRoundBox( RoundHBox(), Config.BG_COLOR )
+ self.GUI["2instrument1Box"].set_size_request( -1, 132 )
+ self.GUI["2instrument1volBox"] = gtk.VBox()
+ self.GUI["2instrument1volumeAdjustment"] = gtk.Adjustment( self._data["track_volume"][1], 0, 100, 1, 1, 0 )
+ #self.GUI["2instrument1volumeAdjustment"].connect( "value_changed", self.onTrackVolumeChanged, 0 )
+ self.GUI["2instrument1volumeSlider"] = gtk.VScale(self.GUI["2instrument1volumeAdjustment"])
+ self.GUI["2instrument1volumeSlider"].set_draw_value(False)
+ self.GUI["2instrument1volumeSlider"].set_inverted(True)
+ self.GUI["2instrument1volumeSlider"].set_size_request( 30, -1 )
+ self.GUI["2instrument1volumeAdjustment"].connect( "value-changed", self.handleTrackVolume, 0 )
+ self.GUI["2instrument1muteButton"] = ImageToggleButton(Config.IMAGE_ROOT+"checkOff.svg",Config.IMAGE_ROOT+"checkOn.svg")
+ self.GUI["2instrument1muteButton"].connect("toggled",self.handlemuteButton,0)
+ self.GUI["2instrument1muteButton"].connect("button-press-event",self.handlemuteButtonRightClick,0)
+ self.GUI["2instrument1muteButton"].set_active(True)
+ #self.GUI["2instrument1volBox"].pack_start( self.GUI["2instrument1volumeSlider"], True, True, 0 )
+ #self.GUI["2instrument1volBox"].pack_start( self.GUI["2instrument1muteButton"], False, False, 5 )
+ self.GUI["2instrument1Box"].pack_start( self.GUI["2instrument1volBox"], False, False, 0 )
+ self.GUI["2instrument1Button"] = InstrumentButton( self, 0, Config.BG_COLOR )
+ self.GUI["2instrument1Palette"] = instrumentPalette(_('Track 1 Volume'), 0, self)
+ self.GUI["2instrument1Button"].set_palette(self.GUI["2instrument1Palette"])
+ self.GUI["2instrument1Button"].setPrimary( self.GUI["2instrumentIcons"][self.trackInstrument[0].name] )
+ self.GUI["2instrument1Box"].pack_start( self.GUI["2instrument1Button"], padding = 3 )
+ self.GUI["2instrumentPanel"].pack_start( self.GUI["2instrument1Box"] )
+ # + + instrument 2 box
+ self.GUI["2instrument2Box"] = formatRoundBox( RoundHBox(), Config.BG_COLOR )
+ self.GUI["2instrument2Box"].set_size_request( -1, 132 )
+ self.GUI["2instrument2volBox"] = gtk.VBox()
+ self.GUI["2instrument2volumeAdjustment"] = gtk.Adjustment( self._data["track_volume"][1], 0, 100, 1, 1, 0 )
+ #self.GUI["2instrument2volumeAdjustment"].connect( "value_changed", self.onTrackVolumeChanged, 1 )
+ self.GUI["2instrument2volumeSlider"] = gtk.VScale(self.GUI["2instrument2volumeAdjustment"])
+ self.GUI["2instrument2volumeSlider"].set_draw_value(False)
+ self.GUI["2instrument2volumeSlider"].set_inverted(True)
+ self.GUI["2instrument2volumeSlider"].set_size_request( 30, -1 )
+ self.GUI["2instrument2volumeAdjustment"].connect( "value-changed", self.handleTrackVolume, 1 )
+ self.GUI["2instrument2muteButton"] = ImageToggleButton(Config.IMAGE_ROOT+"checkOff.svg",Config.IMAGE_ROOT+"checkOn.svg")
+ self.GUI["2instrument2muteButton"].connect("toggled",self.handlemuteButton,1)
+ self.GUI["2instrument2muteButton"].connect("button-press-event",self.handlemuteButtonRightClick,1)
+ self.GUI["2instrument2muteButton"].set_active(True)
+ #self.GUI["2instrument2volBox"].pack_start( self.GUI["2instrument2volumeSlider"], True, True, 0 )
+ #self.GUI["2instrument2volBox"].pack_start( self.GUI["2instrument2muteButton"], False, False, 5 )
+ self.GUI["2instrument2Box"].pack_start( self.GUI["2instrument2volBox"], False, False, 0 )
+ self.GUI["2instrument2Button"] = InstrumentButton( self, 1, Config.BG_COLOR )
+ self.GUI["2instrument2Palette"] = instrumentPalette(_('Track 2 Volume'), 1, self)
+ self.GUI["2instrument2Button"].set_palette(self.GUI["2instrument2Palette"])
+ self.GUI["2instrument2Button"].setPrimary( self.GUI["2instrumentIcons"][self.trackInstrument[1].name] )
+ self.GUI["2instrument2Box"].pack_start( self.GUI["2instrument2Button"], padding = 3 )
+ self.GUI["2instrumentPanel"].pack_start( self.GUI["2instrument2Box"] )
+ # + + instrument 3 box
+ self.GUI["2instrument3Box"] = formatRoundBox( RoundHBox(), Config.BG_COLOR )
+ self.GUI["2instrument3Box"].set_size_request( -1, 132 )
+ self.GUI["2instrument3volBox"] = gtk.VBox()
+ self.GUI["2instrument3volumeAdjustment"] = gtk.Adjustment( self._data["track_volume"][2], 0, 100, 1, 1, 0 )
+ #self.GUI["2instrument3volumeAdjustment"].connect( "value_changed", self.onTrackVolumeChanged, 2 )
+ self.GUI["2instrument3volumeSlider"] = gtk.VScale(self.GUI["2instrument3volumeAdjustment"])
+ self.GUI["2instrument3volumeSlider"].set_draw_value(False)
+ self.GUI["2instrument3volumeSlider"].set_inverted(True)
+ self.GUI["2instrument3volumeSlider"].set_size_request( 30, -1 )
+ self.GUI["2instrument3volumeAdjustment"].connect( "value-changed", self.handleTrackVolume, 2 )
+ self.GUI["2instrument3muteButton"] = ImageToggleButton(Config.IMAGE_ROOT+"checkOff.svg",Config.IMAGE_ROOT+"checkOn.svg")
+ self.GUI["2instrument3muteButton"].connect("toggled",self.handlemuteButton,2)
+ self.GUI["2instrument3muteButton"].connect("button-press-event",self.handlemuteButtonRightClick,2)
+ self.GUI["2instrument3muteButton"].set_active(True)
+ #self.GUI["2instrument3volBox"].pack_start( self.GUI["2instrument3volumeSlider"], True, True, 0 )
+ #self.GUI["2instrument3volBox"].pack_start( self.GUI["2instrument3muteButton"], False, False, 5 )
+ self.GUI["2instrument3Box"].pack_start( self.GUI["2instrument3volBox"], False, False, 0 )
+ self.GUI["2instrument3Button"] = InstrumentButton( self, 2, Config.BG_COLOR )
+ self.GUI["2instrument3Palette"] = instrumentPalette(_('Track 3 Volume'), 2, self)
+ self.GUI["2instrument3Button"].set_palette(self.GUI["2instrument3Palette"])
+ self.GUI["2instrument3Button"].setPrimary( self.GUI["2instrumentIcons"][self.trackInstrument[2].name] )
+ self.GUI["2instrument3Box"].pack_start( self.GUI["2instrument3Button"], padding = 3 )
+ self.GUI["2instrumentPanel"].pack_start( self.GUI["2instrument3Box"] )
+ # + + instrument 4 box
+ self.GUI["2instrument4Box"] = formatRoundBox( RoundHBox(), Config.BG_COLOR )
+ self.GUI["2instrument4Box"].set_size_request( -1, 132 )
+ self.GUI["2instrument4volBox"] = gtk.VBox()
+ self.GUI["2instrument4volumeAdjustment"] = gtk.Adjustment( self._data["track_volume"][3], 0, 100, 1, 1, 0 )
+ #self.GUI["2instrument4volumeAdjustment"].connect( "value_changed", self.onTrackVolumeChanged, 3 )
+ self.GUI["2instrument4volumeSlider"] = gtk.VScale(self.GUI["2instrument4volumeAdjustment"])
+ self.GUI["2instrument4volumeSlider"].set_draw_value(False)
+ self.GUI["2instrument4volumeSlider"].set_inverted(True)
+ self.GUI["2instrument4volumeSlider"].set_size_request( 30, -1 )
+ self.GUI["2instrument4volumeAdjustment"].connect( "value-changed", self.handleTrackVolume, 3 )
+ self.GUI["2instrument4muteButton"] = ImageToggleButton(Config.IMAGE_ROOT+"checkOff.svg",Config.IMAGE_ROOT+"checkOn.svg")
+ self.GUI["2instrument4muteButton"].connect("toggled",self.handlemuteButton,3)
+ self.GUI["2instrument4muteButton"].connect("button-press-event",self.handlemuteButtonRightClick,3)
+ self.GUI["2instrument4muteButton"].set_active(True)
+ #self.GUI["2instrument4volBox"].pack_start( self.GUI["2instrument4volumeSlider"], True, True, 0 )
+ #self.GUI["2instrument4volBox"].pack_start( self.GUI["2instrument4muteButton"], False, False, 5 )
+ self.GUI["2instrument4Box"].pack_start( self.GUI["2instrument4volBox"], False, False, 0 )
+ self.GUI["2instrument4Button"] = InstrumentButton( self, 3, Config.BG_COLOR )
+ self.GUI["2instrument4Palette"] = instrumentPalette(_('Track 4 Volume'), 3, self)
+ self.GUI["2instrument4Button"].set_palette(self.GUI["2instrument4Palette"])
+ self.GUI["2instrument4Button"].setPrimary( self.GUI["2instrumentIcons"][self.trackInstrument[3].name] )
+ self.GUI["2instrument4Box"].pack_start( self.GUI["2instrument4Button"], padding = 3 )
+ self.GUI["2instrumentPanel"].pack_start( self.GUI["2instrument4Box"] )
+ # + + drum box
+ self.GUI["2drumBox"] = formatRoundBox( RoundHBox(), Config.BG_COLOR )
+ self.GUI["2drumBox"].set_size_request( -1, 165 )
+ self.GUI["2drumVolBox"] = gtk.VBox()
+ self.GUI["2drumvolumeAdjustment"] = gtk.Adjustment( self._data["track_volume"][4], 0, 100, 1, 1, 0 )
+ #self.GUI["2drumvolumeAdjustment"].connect( "value_changed", self.onTrackVolumeChanged, 4 )
+ self.GUI["2drumvolumeSlider"] = gtk.VScale(self.GUI["2drumvolumeAdjustment"])
+ self.GUI["2drumvolumeSlider"].set_draw_value(False)
+ self.GUI["2drumvolumeSlider"].set_inverted(True)
+ self.GUI["2drumvolumeSlider"].set_size_request( 30, -1 )
+ self.GUI["2drumvolumeAdjustment"].connect( "value-changed", self.handleTrackVolume, 4 )
+ self.GUI["2drumMuteButton"] = ImageToggleButton(Config.IMAGE_ROOT+"checkOff.svg",Config.IMAGE_ROOT+"checkOn.svg")
+ self.GUI["2drumMuteButton"].connect("toggled",self.handlemuteButton,4)
+ self.GUI["2drumMuteButton"].connect("button-press-event",self.handlemuteButtonRightClick,4)
+ self.GUI["2drumMuteButton"].set_active(True)
+ #self.GUI["2drumVolBox"].pack_start( self.GUI["2drumvolumeSlider"], True, True, 0 )
+ #self.GUI["2drumVolBox"].pack_start( self.GUI["2drumMuteButton"], False, False, 5 )
+ self.GUI["2drumBox"].pack_start( self.GUI["2drumVolBox"], False, False, 0 )
+ self.GUI["2drumButton"] = ImageToggleButton(Config.IMAGE_ROOT + self.trackInstrument[4].name + '.png', Config.IMAGE_ROOT + self.trackInstrument[4].name + '.png')
+ self.GUI["2drumPalette"] = instrumentPalette(_('Track 5 Volume'), 4, self)
+ self.GUI["2drumButton"].set_palette(self.GUI["2drumPalette"])
+ self.GUI["2drumButton"].connect("toggled", self.pickDrum)
+ self.GUI["2drumButton"].connect('enter-notify-event', self.blockFocus)
+ self.GUI["2drumButton"].connect('leave-notify-event', self.unblockFocus)
+ self.GUI["2drumBox"].pack_start( self.GUI["2drumButton"] )
+ self.GUI["2instrumentPanel"].pack_start( self.GUI["2drumBox"] )
+ self.GUI["2page"].pack_start( self.GUI["2instrumentPanel"], False )
+ # + track interface
+ self.trackInterface = TrackInterface( self.noteDB, self, self.getScale )
+ self.noteDB.addListener( self.trackInterface, TrackInterfaceParasite, True )
+ self.trackInterface.set_size_request( 1068, 693 )
+ self.GUI["2page"].pack_start( self.trackInterface, False, False )
+
+ #------------------------------------------------------------------------
+ # tune interface
+ if 1: # + tune interface
+ self.GUI["2tuneHBox"] = RoundHBox( fillcolor = Config.TOOLBAR_BCK_COLOR, bordercolor = Config.TOOLBAR_BCK_COLOR, radius = 0 )
+ self.GUI["2tuneScrollLeftButton"] = ImageButton( Config.IMAGE_ROOT+"arrowEditLeft.png", Config.IMAGE_ROOT+"arrowEditLeftDown.png", Config.IMAGE_ROOT+"arrowEditLeftOver.png", backgroundFill = Config.TOOLBAR_BCK_COLOR )
+ self.GUI["2tuneScrollLeftButton"].set_size_request( 25, -1 )
+ self.GUI["2tuneScrollLeftButton"].connect( "clicked", lambda a1:self.scrollTune( -1 ) )
+ self.GUI["2tuneHBox"].pack_start( self.GUI["2tuneScrollLeftButton"], False, False )
+ self.GUI["2tuneVBox"] = gtk.VBox()
+ self.GUI["2tuneScrolledWindow"] = gtk.ScrolledWindow()
+ self.GUI["2tuneScrolledWindow"].set_policy( gtk.POLICY_NEVER, gtk.POLICY_NEVER )
+ self.tuneInterface = TuneInterface( self.noteDB, self, self.GUI["2tuneScrolledWindow"].get_hadjustment() )
+ self.noteDB.addListener( self.tuneInterface, TuneInterfaceParasite, True )
+ self.GUI["2tuneScrolledWindow"].add_with_viewport( self.tuneInterface )
+ self.tuneInterface.get_parent().set_shadow_type( gtk.SHADOW_NONE )
+ self.GUI["2tuneVBox"].pack_start( self.GUI["2tuneScrolledWindow"] )
+ self.GUI["2tuneSlider"] = gtk.HScrollbar( self.GUI["2tuneScrolledWindow"].get_hadjustment() ) #ImageHScale( Config.IMAGE_ROOT+"sliderEditTempo.png", self.GUI["2tuneScrolledWindow"].get_hadjustment(), 6 )
+ self.GUI["2tuneVBox"].pack_start( self.GUI["2tuneSlider"], False, False )
+ self.GUI["2tuneHBox"].pack_start( self.GUI["2tuneVBox"] )
+ self.GUI["2tuneScrollRightButton"] = ImageButton( Config.IMAGE_ROOT+"arrowEditRight.png", Config.IMAGE_ROOT+"arrowEditRightDown.png", Config.IMAGE_ROOT+"arrowEditRightOver.png", backgroundFill = Config.TOOLBAR_BCK_COLOR )
+ self.GUI["2tuneScrollRightButton"].set_size_request( 25, -1 )
+ self.GUI["2tuneScrollRightButton"].connect( "clicked", lambda a1:self.scrollTune( 1 ) )
+ self.GUI["2tuneHBox"].pack_start( self.GUI["2tuneScrollRightButton"], False, False )
+ self.GUI["2main"].pack_start( self.GUI["2tuneHBox"] )
+
+ # set tooltips
+ for key in self.GUI:
+ if Tooltips.Edit.has_key(key):
+ self.tooltips.set_tip(self.GUI[key],Tooltips.Edit[key])
+
+ self.add( self.GUI["2main"] )
+
+ self.skipCleanup = "" # used when jumping between duplicate note/track
+
+
+ # Popups
+ TP.ProfileBegin("init_GUI::popups")
+ # + instrument panel
+ self.GUI["9instrumentPopup"] = gtk.Window(gtk.WINDOW_POPUP)
+ self.GUI["9instrumentPopup"].move( 400, 100 )
+ self.GUI["9instrumentPopup"].resize( 800, 452 )
+ self.GUI["9instrumentPopup"].set_modal(True)
+ self.GUI["9instrumentPopup"].add_events( gtk.gdk.BUTTON_PRESS_MASK )
+ self.GUI["9instrumentPopup"].connect("button-release-event", lambda w,e:self.cancelInstrumentSelection() )
+ # + drum panel
+ TP.ProfileBegin("init_GUI::drumPanel")
+ self.drumPanel = DrumPanel( self.donePickDrum )
+ TP.ProfileEnd("init_GUI::drumPanel")
+ self.GUI["9drumPopup"] = gtk.Window(gtk.WINDOW_POPUP)
+ self.GUI["9drumPopup"].move( 400, 100 )
+ self.GUI["9drumPopup"].resize( 400, 100 )
+ self.GUI["9drumPopup"].set_modal(True)
+ self.GUI["9drumPopup"].add_events( gtk.gdk.BUTTON_PRESS_MASK )
+ self.GUI["9drumPopup"].connect("button-release-event", lambda w,e:self.cancelDrumSelection() )
+ self.GUI["9drumPopup"].add( self.drumPanel )
+ # + generation window
+ #TP.ProfileBegin("init_GUI::generationPanel")
+ #self.generationPanel = GenerationParametersWindow( self.generate, self.doneGenerationPopup )
+ #TP.ProfileEnd("init_GUI::generationPanel")
+ #self.GUI["9generationPopup"] = gtk.Window(gtk.WINDOW_POPUP)
+ #self.GUI["9generationPopup"].set_modal(True)
+ #self.GUI["9generationPopup"].add_events( gtk.gdk.BUTTON_PRESS_MASK )
+ #self.GUI["9generationPopup"].connect("button-release-event", lambda w,e:self.doneGenerationPopup() )
+ #self.GUI["9generationPopup"].add( self.generationPanel )
+ # + properties window
+ #self.GUI["9propertiesPopup"] = gtk.Window(gtk.WINDOW_POPUP)
+ #self.GUI["9propertiesPopup"].set_modal(True)
+ #self.GUI["9propertiesPopup"].add_events( gtk.gdk.BUTTON_PRESS_MASK )
+ #self.GUI["9propertiesPopup"].connect("button-release-event", lambda w,e:self.donePropertiesPopup() )
+ #TP.ProfileBegin("init_GUI::propertiesPanel")
+ #self.propertiesPanel = Properties( self.noteDB, self.donePropertiesPopup, self.GUI["9propertiesPopup"] )
+ #TP.ProfileEnd("init_GUI::propertiesPanel")
+ #self.GUI["9propertiesPopup"].add( self.propertiesPanel )
+ # + playback scope
+ self.GUI["9loopPopup"] = gtk.Window(gtk.WINDOW_POPUP)
+ self.GUI["9loopPopup"].move( 100, 100 )
+ self.GUI["9loopPopup"].resize( 300, 100 )
+ self.GUI["9loopPopup"].set_modal(True)
+ self.GUI["9loopPopup"].add_events( gtk.gdk.BUTTON_PRESS_MASK )
+ self.GUI["9loopPopup"].connect("button-release-event", lambda w,e:self.GUI["2loopButton"].set_active(False) )
+ self.GUI["9loopBox"] = formatRoundBox( RoundHBox(), Config.BG_COLOR )
+ self.GUI["9loopAllOnce"] = gtk.Button("AO")
+ self.GUI["9loopBox"].pack_start( self.GUI["9loopAllOnce"] )
+ self.GUI["9loopAllRepeat"] = gtk.Button("AR")
+ self.GUI["9loopBox"].pack_start( self.GUI["9loopAllRepeat"] )
+ self.GUI["9loopSelectedOnce"] = gtk.Button("SO")
+ self.GUI["9loopBox"].pack_start( self.GUI["9loopSelectedOnce"] )
+ self.GUI["9loopSelectedRepeat"] = gtk.Button("SR")
+ self.GUI["9loopBox"].pack_start( self.GUI["9loopSelectedRepeat"] )
+ self.GUI["9loopPopup"].add(self.GUI["9loopBox"])
+ TP.ProfileEnd("init_GUI::popups")
+
+ #===================================================
+ # begin initialization
+ SubActivity.__init__( self, set_mode )
+
+ # keyboard variables
+ self.kb_record = False
+ self.kb_keydict = {}
+
+ # playback params
+ self.playing = False
+ self.playSource = 'Page'
+ self.currentpageId = 0
+ self.playingTuneIdx = 0
+
+ # timers
+ self.playbackTimeout = False
+
+ # FPS stuff
+ self.fpsTotalTime = 0
+ self.fpsFrameCount = 0
+ self.fpsN = 100 # how many frames to average FPS over
+ self.fpsLastTime = time.time() # fps will be borked for the first few frames but who cares?
+
+ self.context = -1 # invalidate
+ self.contextTrackActive = False
+ self.contextNoteActive = False
+
+ init_data() #above
+ init_GUI() #above
+
+ # register for notification AFTER track and tune interfaces
+ self.noteDB.addListener( self, page=True, note=True )
+
+ self.csnd.setMasterVolume( self.getVolume() )
+ self.initTrackVolume()
+
+ for tid in range(Config.NUMBER_OF_TRACKS):
+ self.handleInstrumentChanged( ( tid, self.trackInstrument[tid] ) )
+
+ instrumentsIds = []
+ for inst in self.trackInstrument:
+ instrumentsIds.append(inst.instrumentId)
+
+ first = self.noteDB.addPage( -1, NoteDB.Page(4, instruments = instrumentsIds) )
+ self.displayPage( first )
+
+ self.createNewTune( None )
+
+ # Toolbar
+ self.activity.activity_toolbar.keep.show()
+ self._mainToolbar = mainToolbar(self.activity.toolbox, self)
+ self._generateToolbar = generateToolbar(self.activity.toolbox, self)
+ self.activity.toolbox.add_toolbar(_('Compose'), self._mainToolbar)
+ self.activity.toolbox.add_toolbar(_('Generate'), self._generateToolbar)
+ self.activity.toolbox.set_current_toolbar(1)
+ self._mainToolbar.show()
+ self._generateToolbar.show()
+
+ self.show_all() #gtk command
+
+ self.setContext( CONTEXT.PAGE )
+
+ self.audioRecordState = False
+
+ def createNewTune( self, widget, data=None ):
+ self.createNewTune3()
+
+ def createNewTune3( self ):
+
+ if self.playing == True:
+ self.handleStop()
+
+ self.tuneInterface.selectPages( self.noteDB.getTune() )
+
+ beats = random.randint(3,8)
+ stream = []
+ for page in self.noteDB.getTune():
+ stream += [ page, beats ]
+ if len(stream):
+ self.noteDB.updatePages( [ PARAMETER.PAGE_BEATS, len(stream)//2 ] + stream )
+
+ orch = self.newOrchestra()
+
+ instrumentsIds = []
+ for inst in orch:
+ instrumentsIds.append(inst.instrumentId)
+
+ self.pageDelete( -1, instruments = instrumentsIds )
+
+ initTempo = random.randint(60, 132)
+ self._data['tempo'] = initTempo
+
+ formsUsed = []
+ for section in self.tuneForm:
+ if section[0] not in formsUsed:
+ param = self.chooseGenParams()
+ self.tuneInterface.selectPages( self.noteDB.getTune() )
+ if not formsUsed:
+ for i in range(section[2]-1):
+ self.pageAdd(instruments = instrumentsIds)
+ else:
+ for i in range(section[2]):
+ self.pageAdd(instruments = instrumentsIds)
+ formsUsed.append(section[0])
+
+ self.tuneInterface.selectPages( self.noteDB.getTune()[-section[2]:] )
+ self.generateMode = 'page'
+ self.generate( GenerationParameters( density = param[0], rythmRegularity = param[1], step = param[2], pitchRegularity = param[3], articule = param[4], silence = param[5], pattern = param[6], scale = param[7]), section[2] )
+ else:
+ pageOffset = 0
+ pageIds = []
+ firstPos = [i[0] for i in self.tuneForm].index(section[0])
+ if firstPos == 0:
+ pageOffset = 0
+ else:
+ for i in range(firstPos):
+ pageOffset += self.tuneForm[i][2]
+ for i in range(section[2]):
+ pageIds.append(self.noteDB.getTune()[pageOffset + i])
+ after = self.noteDB.getTune()[-1]
+ self.displayPage( self.noteDB.getTune()[pageOffset] )
+ self.tuneInterface.selectPages(self.noteDB.getTune())
+ self.pageDuplicate(-1, pageIds)
+
+ self.tuneInterface.selectPages( self.noteDB.getTune() )
+ self.displayPage( self.noteDB.getTune()[0] )
+
+
+ def newOrchestra(self):
+ stringsPickup = []
+ windsPickup = []
+ keyboardPickup = []
+ fxPickup = []
+ drumsPickup = ["drum1kit", "drum2kit", "drum3kit", "drum4kit", "drum5kit"]
+ for name in Config.INSTRUMENTS.keys():
+ if Config.INSTRUMENTS[name].category == 'strings' and Config.INSTRUMENTS[name].name != 'violin':
+ stringsPickup.append(name)
+ elif Config.INSTRUMENTS[name].category == 'winds' and Config.INSTRUMENTS[name].name != 'didjeridu':
+ windsPickup.append(name)
+ elif Config.INSTRUMENTS[name].category == 'keyboard' or Config.INSTRUMENTS[name].category == 'percussions':
+ if Config.INSTRUMENTS[name].name != 'zap' and Config.INSTRUMENTS[name].name != 'cling':
+ keyboardPickup.append(name)
+ return [
+ Config.INSTRUMENTS[random.choice(stringsPickup)],
+ Config.INSTRUMENTS[random.choice(stringsPickup)],
+ Config.INSTRUMENTS[random.choice(windsPickup)],
+ Config.INSTRUMENTS[random.choice(keyboardPickup)],
+ Config.INSTRUMENTS[random.choice(drumsPickup)] ]
+
+ def chooseGenParams(self):
+ choose = random.randint(0,4)
+ density = GenerationConstants.RYTHM_DENSITY_BANK[choose]
+ rytReg = GenerationConstants.RYTHM_REGU_BANK[choose]
+ step = GenerationConstants.PITCH_STEP_BANK[choose]
+ pitReg = GenerationConstants.PITCH_REGU_BANK[choose]
+ dur = GenerationConstants.DURATION_BANK[choose]
+ silence = GenerationConstants.SILENCE_BANK[choose]
+ pattern = [random.choice([0,1,1,2,3,3]) for x in range(4)]
+ scale = random.randint(0,6)
+ return [density, rytReg, step, pitReg, dur, silence, pattern, scale]
+
+ def onActivate( self, arg ):
+ SubActivity.onActivate( self,arg )
+ # whatever needs to be done on initialization
+ self.csnd.loopPause()
+ self.csnd.loopClear()
+ for n in self.noteDB.getNotes( ):
+ self.csnd.loopPlay(n, 0) #adds all notes to c client in inactive state
+
+
+ def onDeactivate( self ):
+ SubActivity.onDeactivate( self )
+ # clean up things like popups etc
+ self.releaseInstrumentPanel()
+ self.csnd.loopPause()
+ self.csnd.loopClear()
+
+ def setInstrumentPanel( self, instrumentPanel ):
+ instrumentPanel.configure( self.donePickInstrument, self.playInstrumentNote, enterMode = True )
+ self.instrumentPanel = instrumentPanel
+ self.GUI["9instrumentPopup"].add( self.instrumentPanel )
+
+ def releaseInstrumentPanel( self ):
+ self.GUI["9instrumentPopup"].remove( self.instrumentPanel )
+
+
+ def updateFPS( self ):
+ t = time.time()
+ dt = t - self.fpsLastTime
+ self.fpsLastTime = t
+ self.fpsTotalTime += dt
+ self.fpsFrameCount += 1
+ if self.fpsFrameCount == self.fpsN:
+ fps = self.fpsN/self.fpsTotalTime
+ avgMS = 1000/fps
+ fps = "FPS %d ms %.2f" % (fps, avgMS)
+ #self.fpsText.set_text(fps )
+ if (Config.DEBUG > 2): print fps
+ self.fpsTotalTime = 0
+ self.fpsFrameCount = 0
+
+ #=========================================================
+ # Popup Windows
+
+ def doneGenerationPopup( self ):
+ if self.GUI["2pageGenerateButton"].get_active():
+ self.GUI["2pageGenerateButton"].set_active( False )
+ if self.GUI["2trackGenerateButton"].get_active():
+ self.GUI["2trackGenerateButton"].set_active( False )
+
+ def donePropertiesPopup( self ):
+ if self.GUI["2pagePropertiesButton"].get_active():
+ self.GUI["2pagePropertiesButton"].set_active( False )
+ if self.GUI["2trackPropertiesButton"].get_active():
+ self.GUI["2trackPropertiesButton"].set_active( False )
+ if self.GUI["2notePropertiesButton"].get_active():
+ self.GUI["2notePropertiesButton"].set_active( False )
+
+ def cancelPopup( self, w, event, popup ):
+ popup.hide()
+
+
+ def handleLoopButton( self, w ):
+ if w.get_active(): self.GUI["9loopPopup"].show_all()
+ else: self.GUI["9loopPopup"].hide()
+
+ #-----------------------------------
+ # playback functions
+ #-----------------------------------
+
+ def updatePageSelection( self, selectedIds ):
+ if not self.playing:
+ return
+
+ if self.playScope == "All":
+ return
+
+ if self.displayedPage in selectedIds:
+ startPage = self.displayedPage
+ else:
+ startPage = selectedIds[0]
+
+ self._playPages( selectedIds, startPage, self.trackInterface.getPlayhead() )
+
+ def updatePagesPlaying( self ):
+ if not self.playing:
+ return
+
+ curTick = self.csnd.loopGetTick()
+
+ pageTick = self.page_onset[self.displayedPage]
+ if curTick < pageTick:
+ pageTick = 0
+ ind = 0
+ else:
+ ind = self.pages_playing.index(self.displayedPage)
+
+ localTick = curTick - pageTick
+
+ self._playPages( self.tuneInterface.getSelectedIds(), ind, localTick )
+
+ def handleAudioRecord( self, widget, data=None ):
+ if widget.get_active() == True:
+ chooser = gtk.FileChooserDialog(
+ title='Save tune as Audio file',
+ action=gtk.FILE_CHOOSER_ACTION_SAVE,
+ buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_SAVE,gtk.RESPONSE_OK))
+ filter = gtk.FileFilter()
+ filter.add_pattern('*.ogg')
+ chooser.set_filter(filter)
+ chooser.set_current_folder(Config.TUNE_DIR)
+
+ for f in chooser.list_shortcut_folder_uris():
+ chooser.remove_shortcut_folder_uri(f)
+
+ if chooser.run() == gtk.RESPONSE_OK:
+ if self.playing:
+ self.handleStop()
+ else:
+ self.handleRewind()
+
+ self.audioRecordState = True
+ self.audioFileName = chooser.get_filename()
+ if self.audioFileName[-4:] != '.ogg':
+ self.audioFileName += '.ogg'
+
+ self.audioRecordTimeout = gobject.timeout_add( 500, self._startAudioRecord )
+ self.audioRecordTick = -1
+ chooser.destroy()
+ else:
+ self.audioRecordState = False
+
+ def _startAudioRecord( self ):
+ if not self.playing:
+ self.handlePlay()
+ return False
+
+ def handlePlay( self, widget = None ):
+
+ if widget:
+ widget.event( gtk.gdk.Event( gtk.gdk.LEAVE_NOTIFY ) ) # fake the leave event
+
+ if self.audioRecordState:
+ self.csnd.inputMessage( "i5400 0 -1" )
+ time.sleep( 0.01 )
+
+ if self.playScope == "All":
+ toPlay = self.noteDB.getTune()
+ else:
+ toPlay = self.tuneInterface.getSelectedIds()
+
+ self._playPages( toPlay, self.displayedPage, self.trackInterface.getPlayhead() )
+
+ self.playing = True
+
+ def _playPages( self, pages, startPage, startTick ):
+
+ self.pages_playing = pages[:]
+
+ trackset = set( [ i for i in range(Config.NUMBER_OF_TRACKS) if self.trackActive[i] ] )
+
+ numticks = 0
+ self.page_onset = {}
+ for pid in self.pages_playing:
+ self.page_onset[pid] = numticks
+ numticks += self.noteDB.getPage(pid).ticks
+
+ # check for a second instrument on melodic tracks
+ stream = []
+ for page in self.pages_playing:
+ for track in trackset:
+ if track != self.drumIndex:
+ if self.trackInstrument2[track] != None:
+ if len(self.noteDB.getNotesByTrack(page, track)):
+ stream += [ page, track, NoteDB.PARAMETER.INSTRUMENT2, len(self.noteDB.getNotesByTrack(page, track)) ]
+ for n in self.noteDB.getNotesByTrack(page, track):
+ stream += [ n.id, self.trackInstrument2[track].instrumentId ]
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ self.csnd.loopClear()
+ for page in self.pages_playing:
+ for track in trackset:
+ for n in self.noteDB.getNotesByTrack( page, track ):
+ self.csnd.loopPlay(n, 1)
+ self.csnd.loopUpdate(n, NoteDB.PARAMETER.ONSET, n.cs.onset + self.page_onset[n.page] , 1)
+
+ self.csnd.loopSetNumTicks( numticks )
+
+ self.csnd.loopSetTick( self.page_onset[startPage] + startTick )
+ self.csnd.setTempo(self._data['tempo'])
+ if (Config.DEBUG > 3): print "starting from tick", startTick, 'at tempo', self._data['tempo']
+ self.csnd.loopStart()
+
+ if not self.playbackTimeout:
+ self.playbackTimeout = gobject.timeout_add( 50, self.onTimeout )
+
+
+
+ def handleStop( self, widget = None, rewind = True ):
+
+ if widget:
+ widget.event( gtk.gdk.Event( gtk.gdk.LEAVE_NOTIFY ) ) # fake the leave event
+
+ if self.audioRecordState:
+ self.csnd.inputMessage( "i5401 4 1" )
+ time.sleep( 0.01 )
+
+ if self.playbackTimeout:
+ gobject.source_remove( self.playbackTimeout )
+ self.playbackTimeout = False
+
+ self.csnd.loopPause()
+ self.csnd.loopDeactivate()
+
+ if self.audioRecordState:
+ time.sleep(4)
+ self.csnd.__del__()
+ time.sleep(0.5)
+ self.audioRecordState = False
+ command = "gst-launch-0.10 filesrc location=" + Config.PREF_DIR + "/perf.wav ! wavparse ! audioconvert ! vorbisenc ! oggmux ! filesink location=" + self.audioFileName
+ command2 = "rm /home/olpc/.sugar/default/tamtam/perf.wav"
+ (status, output) = commands.getstatusoutput(command)
+ (status2, output2) = commands.getstatusoutput(command2)
+ self.csnd.__init__()
+ time.sleep(0.1)
+ self.csnd.connect(True)
+ time.sleep(0.1)
+ self.waitToSet()
+ self.csnd.load_instruments()
+ self.GUI["2recordButton"].set_active(False)
+ self.playing = False
+
+ if rewind: self.handleRewind()
+
+ def handleRewind( self, widget = None ):
+ if self.playScope == "All": id = self.noteDB.getPageByIndex(0)
+ else: id = self.tuneInterface.getFirstSelected()
+ self.trackInterface.setPlayhead( 0 )
+ self.displayPage( id )
+
+ def handleClose(self,widget):
+ self.activity.close()
+
+ def onTimeout(self):
+ self.updateFPS()
+
+ curTick = self.csnd.loopGetTick()
+
+ pageTick = self.page_onset[self.displayedPage]
+ if curTick < pageTick:
+ pageTick = 0
+ ind = 0
+ else:
+ ind = self.pages_playing.index(self.displayedPage)
+
+ localTick = curTick - pageTick
+ pageLength = self.noteDB.getPage(self.pages_playing[ind]).ticks
+ max = len(self.pages_playing)
+ while localTick > pageLength:
+ ind += 1
+ if ind == max: ind = 0
+ localTick -= pageLength
+ pageLength = self.noteDB.getPage(self.pages_playing[ind]).ticks
+
+ self.trackInterface.setPlayhead( localTick )
+
+ if self.pages_playing[ind] != self.displayedPage:
+ if ind + 1 < max: predraw = self.pages_playing[ind+1]
+ else: predraw = self.pages_playing[0]
+ self._displayPage( self.pages_playing[ind], predraw )
+ else:
+ self.trackInterface.predrawPage()
+
+ if self.audioRecordState:
+ if self.audioRecordTick > curTick: # we've looped around
+ self.handleStop()
+ else:
+ self.audioRecordTick = curTick
+
+
+ return True
+
+ def onMuteTrack( self, widget, trackId ):
+ self._data['track_mute'][trackId] = not self._data['track_mute'][trackId]
+ #if self._data['track_mute'][trackId]:
+ #self.noteLooper.setMute( trackId, 0.0 )
+ #else:
+ #self.noteLooper.setMute( trackId, 1.0 )
+
+ def onTrackVolumeChanged( self, widget, trackId ):
+ v = widget.get_value() / 100.0
+ self._data['track_volume'][trackId] = v
+ #self.noteLooper.setVolume( trackId, v )
+
+ def clearInstrument( self, id, primary = True ):
+ btn = self.GUI["2instrument%dButton" % (id+1)]
+ if primary:
+ if self.trackInstrument2[id] == None:
+ return
+ self.handleInstrumentChanged( ( id, self.trackInstrument2[id] ), True )
+ self.handleInstrumentChanged( ( id, None ), False )
+ btn.setPrimary( self.GUI["2instrumentIcons"][self.trackInstrument[id].name] )
+ btn.setSecondary( None )
+ else:
+ self.handleInstrumentChanged( ( id, None ), False )
+ btn.setSecondary( None )
+ pages = self.tuneInterface.getSelectedIds()
+ self.noteDB.setInstrument2( pages, id, -1 )
+
+ # data is tuple ( trackId, instrumentName )
+ def handleInstrumentChanged( self, data, primary = True ):
+ (id, instrument) = data
+ if primary:
+ self.trackInstrument[id] = instrument
+ else:
+ self.trackInstrument2[id] = instrument
+
+
+ if primary: # TODO handle secondary instruments properly
+ if (Config.DEBUG > 3): print "handleInstrumentChanged", id, instrument.name, primary
+
+ pages = self.tuneInterface.getSelectedIds()
+ self.noteDB.setInstrument( pages, id, instrument.instrumentId )
+
+ def getScale(self):
+ return self.scale
+
+ def handleVolume( self, widget ):
+ self._data["volume"] = round( widget.get_value() )
+ self.csnd.setMasterVolume(self._data["volume"])
+ img = min(3,int(4*self._data["volume"]/100)) # volume 0-3
+ #self.GUI["2volumeImage"].set_from_file( Config.IMAGE_ROOT+"volume"+str(img)+".png" )
+
+ def initTrackVolume( self ):
+ for i in range(Config.NUMBER_OF_TRACKS):
+ self.csnd.setTrackVolume(self._data["track_volume"][i], i)
+
+ def handleTrackVolume( self, widget, track ):
+ self._data["track_volume"][track] = round( widget.get_value() )
+ self.csnd.setTrackVolume(self._data["track_volume"][track], track)
+
+ def getTrackInstrument( self, track ):
+ return self.trackInstrument[track]
+
+ def getTrackVolume( self, track ):
+ return self._data["track_volume"][track]
+
+ def handleTempo( self, widget ):
+ self._data['tempo'] = round( widget.get_value() )
+ img = min(7,int(8*(self._data["tempo"]-widget.lower)/(widget.upper-widget.lower)))+1# tempo 1-8
+ #self.GUI["2tempoImage"].set_from_file( Config.IMAGE_ROOT+"tempo"+str(img)+".png" )
+ if self.playing:
+ self.csnd.setTempo(self._data['tempo'])
+
+ def handleToolClick( self, widget, mode ):
+ if widget.get_active(): self.trackInterface.setInterfaceMode( mode )
+
+ def getTool( self ):
+ if self.GUI["2toolPointerButton"].get_active(): return "default"
+ else: return "draw"
+
+ def handleKeyboardRecordButton( self, widget, data=None ):
+ self.kb_record = widget.get_active()
+
+ def pickInstrument( self, widget, num, primary = True ):
+ self.last_clicked_instTrackID = num
+ self.last_clicked_instPrimary = primary
+ self.instrumentPanel.selectFirstCat()
+ if primary or self.trackInstrument2[num] == None:
+ self.instrumentPanel.set_activeInstrument( self.trackInstrument[num].name, True )
+ else:
+ self.instrumentPanel.set_activeInstrument( self.trackInstrument2[num].name, True )
+ winLoc = self.parent.window.get_position()
+ alloc = widget.parent.get_allocation()
+ x = alloc.x + alloc.width + winLoc[0]
+ y = alloc.y + winLoc[1]
+ self.GUI["9instrumentPopup"].move( x, y )
+ self.GUI["9instrumentPopup"].show()
+
+ def cancelInstrumentSelection( self ):
+ self.GUI["9instrumentPopup"].hide()
+
+ def donePickInstrument( self, instrumentName ):
+ self.handleInstrumentChanged( (self.last_clicked_instTrackID, Config.INSTRUMENTS[instrumentName]), self.last_clicked_instPrimary )
+ btn = self.GUI["2instrument%dButton" % (self.last_clicked_instTrackID+1)]
+ if self.last_clicked_instPrimary:
+ btn.setPrimary( self.GUI["2instrumentIcons"][instrumentName] )
+ else:
+ btn.setSecondary( self.GUI["2instrumentIcons"][instrumentName] )
+ self.GUI["9instrumentPopup"].hide()
+
+
+ def pickDrum( self, widget , data = None ):
+ if widget.get_active(): # show the panel
+ winLoc = self.parent.window.get_position()
+ alloc = widget.get_allocation()
+ x = alloc.x + alloc.width + winLoc[0]
+ y = alloc.y + winLoc[1]
+ self.drumPanel.set_activeInstrument( self.trackInstrument[Config.NUMBER_OF_TRACKS-1].name, True )
+ self.GUI["9drumPopup"].move( x, y )
+ self.GUI["9drumPopup"].show()
+ else: # hide the panel
+ self.GUI["9drumPopup"].hide()
+
+ def cancelDrumSelection( self ):
+ self.GUI["2drumButton"].set_active( False )
+
+ def donePickDrum( self, drumName ):
+ self.handleInstrumentChanged( ( self.drumIndex, Config.INSTRUMENTS[drumName] ) )
+ self.GUI["2drumButton"].setImage( "main", self.GUI["2instrumentIcons"][drumName] )
+ self.GUI["2drumButton"].setImage( "alt", self.GUI["2instrumentIcons"][drumName] )
+ self.GUI["2drumButton"].set_active( False )
+
+ def playInstrumentNote( self, instrumentName, secs_per_tick = 0.025):
+ self.csnd.play(
+ CSoundNote( onset = 0,
+ pitch = 36,
+ amplitude = 1,
+ pan = 0.5,
+ duration = 20,
+ trackId = 1,
+ instrumentId = Config.INSTRUMENTS[instrumentName].instrumentId,
+ reverbSend = 0),
+ secs_per_tick)
+
+ def handlemuteButton(self,widget,track):
+ if widget.get_active():
+ self.trackActive[track] = True
+ else:
+ self.trackActive[track] = False
+ self.updatePagesPlaying()
+
+ def handlemuteButtonRightClick(self,widget,event,track):
+ if event.button == 3:
+ widget.set_active(True)
+ #if the other tracks are inactive
+ if self.trackActive.count(False) == Config.NUMBER_OF_TRACKS - 1:
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if i == 4:
+ #self.GUI["2drumMuteButton"].set_active(True)
+ self.GUI["2drumPalette"].muteButton.set_active(True)
+ else:
+ #self.GUI["2instrument" + str(i+1) + "muteButton"].set_active(True)
+ self.GUI["2instrument" + str(i+1) + "Palette"].muteButton.set_active(True)
+ else:
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if i != track:
+ if i == 4:
+ #self.GUI["2drumMuteButton"].set_active(False)
+ self.GUI["2drumPalette"].muteButton.set_active(False)
+ else:
+ #self.GUI["2instrument" + str(i+1) + "muteButton"].set_active(False)
+ self.GUI["2instrument" + str(i+1) + "Palette"].muteButton.set_active(False)
+ self.updatePagesPlaying()
+
+ def blockFocus(self, widget = None, data = None):
+ self.activity.handler_block(self.activity.focusOutHandler)
+ self.activity.handler_block(self.activity.focusInHandler)
+
+ def unblockFocus(self, widget = None, data = None):
+ self.activity.handler_unblock(self.activity.focusOutHandler)
+ self.activity.handler_unblock(self.activity.focusInHandler)
+
+ #-----------------------------------
+ # generation functions
+ #-----------------------------------
+
+ def recompose( self, algo, params, nPagesCycle = 4):
+ if self.generateMode == "track":
+ if self.trackSelected == [ 0 for i in range(Config.NUMBER_OF_TRACKS) ]:
+ newtracks = set(range(Config.NUMBER_OF_TRACKS))
+ else:
+ newtracks = set( [ i for i in range(Config.NUMBER_OF_TRACKS) if self.trackSelected[i] ] )
+ newpages = self.tuneInterface.getSelectedIds()
+ else: # page mode
+ newtracks = set(range(Config.NUMBER_OF_TRACKS))
+ newpages = self.tuneInterface.getSelectedIds()
+
+ dict = {}
+ for t in newtracks:
+ dict[t] = {}
+ for p in newpages:
+ dict[t][p] = self.noteDB.getCSNotesByTrack( p, t )
+
+ beatsOfPages = {}
+ for pageId in newpages:
+ beatsOfPages[pageId] = self.noteDB.pages[pageId].beats
+
+ instruments = self.noteDB.getInstruments(newpages)
+
+ #[ i.name for i in self.trackInstrument ],
+ algo(
+ params,
+ self._data['track_volume'][:],
+ instruments,
+ self._data['tempo'],
+ beatsOfPages,
+ newtracks,
+ newpages,
+ dict, nPagesCycle)
+
+ # filter & fix input ...WTF!?
+ for track in dict:
+ for page in dict[track]:
+ for note in dict[track][page]:
+ intdur = int(note.duration)
+ note.duration = intdur
+ note.pageId = page
+ note.trackId = track
+
+ # prepare the new notes
+ newnotes = []
+ for tid in dict:
+ for pid in dict[tid]:
+ newnotes += dict[tid][pid]
+
+ # delete the notes and add the new
+ self.noteDB.deleteNotesByTrack( newpages, newtracks )
+
+ stream = []
+ for page in newpages:
+ for track in newtracks:
+ stream += [ page, track, len(dict[track][page]) ]
+ stream += dict[track][page]
+ stream += [-1]
+ self.noteDB.addNotes( stream )
+
+ def generate( self, params, nPagesCycle = 4 ):
+ self.recompose( generator1, params, nPagesCycle)
+
+ #=======================================================
+ # Clipboard Functions
+
+ def getClipboardArea( self, page = -1 ):
+ if page == -1: page = self.displayedPage
+ ids = self.tuneInterface.getSelectedIds()
+ return self.noteDB.getClipboardArea( ids.index(page) )
+
+ def pasteClipboard( self, offset, trackMap ):
+ pages = self.tuneInterface.getSelectedIds()
+ instrumentMap = {}
+ for t in trackMap:
+ if t != trackMap[t]: instrumentMap[t] = self.trackInstrument[t].instrumentId
+ return self.noteDB.pasteClipboard( pages, offset, trackMap, instrumentMap )
+
+ def cleanupClipboard( self ):
+ self.trackInterface.donePaste()
+
+
+ #=======================================================
+ # Note Functions
+
+ def noteProperties( self, widget ):
+ if widget.get_active():
+ ids = self.trackInterface.getSelectedNotes()
+ notes = { self.displayedPage: {} }
+ for t in range(Config.NUMBER_OF_TRACKS):
+ if len(ids[t]):
+ notes[self.displayedPage][t] = [ self.noteDB.getNote( self.displayedPage, t, id ) for id in ids[t] ]
+
+ self.propertiesPanel.setContext("note", self.generationPanel.scale, notes = notes )
+ winLoc = self.parent.window.get_position()
+ balloc = self.GUI["2contextBox"].get_allocation()
+ walloc = self.GUI["9propertiesPopup"].get_allocation()
+ if walloc.height != 1: # hack to deal with showing the window before first allocation T_T
+ self.GUI["9propertiesPopup"].move( balloc.x + winLoc[0] - 30, balloc.y - walloc.height + winLoc[1] )
+ else:
+ self.GUI["9propertiesPopup"].move(0, 2048) # off the screen
+ self.GUI["9propertiesPopup"].show()
+ if walloc.height == 1:
+ walloc = self.GUI["9propertiesPopup"].get_allocation()
+ self.GUI["9propertiesPopup"].move( balloc.x + winLoc[0] - 30, balloc.y - walloc.height + winLoc[1] )
+ else:
+ self.GUI["9propertiesPopup"].hide()
+
+ def noteDelete( self ):
+ ids = self.trackInterface.getSelectedNotes()
+ stream = []
+ for t in range(Config.NUMBER_OF_TRACKS):
+ N = len(ids[t])
+ if not N: continue
+ stream += [ self.displayedPage, t, N ] + ids[t]
+ if len(stream):
+ self.noteDB.deleteNotes( stream + [-1] )
+
+ def noteDuplicate( self ):
+ ids = self.trackInterface.getSelectedNotes()
+ stream = []
+ for t in range(Config.NUMBER_OF_TRACKS):
+ N = len(ids[t])
+ if not N: continue
+ stream += [ self.displayedPage, t, N ] + ids[t]
+ if len(stream):
+ self.skipCleanup = "note"
+ self.skipCleanup = ""
+ self.noteDB.notesToClipboard( stream + [-1] )
+ self.trackInterface.setInterfaceMode("paste_notes")
+ return True
+ return False
+
+ def noteDuplicateWidget( self, widget ):
+ if widget.get_active():
+ if self.noteDuplicate(): # duplicate succeeded
+ return
+ # cancel duplicate
+ widget.set_active(False)
+ self.trackInterface.setInterfaceMode("tool")
+ else:
+ self.trackInterface.setInterfaceMode("tool")
+
+ def noteOnset( self, step ):
+ self.trackInterface.noteStepOnset( step )
+
+ def notePitch( self, step ):
+ # TODO
+ return
+
+ def noteDuration( self, step ):
+ # TODO
+ return
+
+ def noteVolume( self, step ):
+ # TODO
+ return
+
+ #=======================================================
+ # Track Functions
+
+ def toggleTrack( self, trackN, exclusive ):
+ if exclusive:
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackSelected[i]:
+ self.trackSelected[i] = False
+ self.trackInterface.trackToggled( i )
+ self.tuneInterface.trackToggled( i )
+ self.trackSelected[trackN] = True
+ self.trackInterface.trackToggled( trackN )
+ self.tuneInterface.trackToggled( trackN )
+ self.setContextState( CONTEXT.TRACK, True )
+ self.setContext( CONTEXT.TRACK )
+ else:
+ self.trackSelected[trackN] = not self.trackSelected[trackN]
+ self.trackInterface.trackToggled( trackN )
+ self.tuneInterface.trackToggled( trackN )
+ trackSelected = False
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackSelected[i]:
+ self.setContextState( CONTEXT.TRACK, True )
+ self.setContext( CONTEXT.TRACK )
+ trackSelected = True
+ break
+ if not trackSelected:
+ self.setContextState( CONTEXT.TRACK, False )
+
+ def setTrack( self, trackN, state ):
+ if self.trackSelected[trackN] != state:
+ self.trackSelected[trackN] = state
+ self.trackInterface.trackToggled( trackN )
+
+ def clearTracks( self ):
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackSelected[i]:
+ self.trackSelected[i]= False
+ self.trackInterface.trackToggled( i )
+ self.tuneInterface.trackToggled( i )
+
+ self.setContextState( CONTEXT.TRACK, False )
+
+ def getTrackSelected( self, trackN ):
+ return self.trackSelected[trackN]
+
+ def trackGenerate( self, widget ):
+ if widget.get_active():
+ self.generateMode = "track"
+ winLoc = self.parent.window.get_position()
+ balloc = self.GUI["2contextBox"].get_allocation()
+ walloc = self.GUI["9generationPopup"].get_allocation()
+ if walloc.height != 1: # hack to make deal with showing the window before first allocation T_T
+ self.GUI["9generationPopup"].move( balloc.x + winLoc[0], balloc.y - walloc.height + winLoc[1] )
+ else:
+ self.GUI["9generationPopup"].move(0, 2048) # off the screen
+ self.GUI["9generationPopup"].show()
+ if walloc.height == 1:
+ walloc = self.GUI["9generationPopup"].get_allocation()
+ self.GUI["9generationPopup"].move( balloc.x + winLoc[0], balloc.y - walloc.height + winLoc[1] )
+ else:
+ self.GUI["9generationPopup"].hide()
+
+
+ def trackProperties( self, widget ):
+ if widget.get_active():
+ self.propertiesPanel.setContext( "track", self.generationPanel.scale, self.tuneInterface.getSelectedIds(), [ i for i in range(Config.NUMBER_OF_TRACKS) if self.trackSelected[i] ] )
+ winLoc = self.parent.window.get_position()
+ balloc = self.GUI["2contextBox"].get_allocation()
+ walloc = self.GUI["9propertiesPopup"].get_allocation()
+ if walloc.height != 1: # hack to make deal with showing the window before first allocation T_T
+ self.GUI["9propertiesPopup"].move( balloc.x + winLoc[0] - 30, balloc.y - walloc.height + winLoc[1] )
+ else:
+ self.GUI["9propertiesPopup"].move(0, 2048) # off the screen
+ self.GUI["9propertiesPopup"].show()
+ if walloc.height == 1:
+ walloc = self.GUI["9propertiesPopup"].get_allocation()
+ self.GUI["9propertiesPopup"].move( balloc.x + winLoc[0] - 30, balloc.y - walloc.height + winLoc[1] )
+ else:
+ self.GUI["9propertiesPopup"].hide()
+
+ def trackDelete( self, pageIds = -1, trackIds = -1 ):
+
+ if pageIds == -1: pageIds = self.tuneInterface.getSelectedIds()
+ if trackIds == -1: trackIds = [ i for i in range(Config.NUMBER_OF_TRACKS) if self.trackSelected[i] ]
+
+ self.noteDB.deleteNotesByTrack( pageIds, trackIds )
+
+ def trackDuplicate( self, pageIds = -1, trackIds = -1 ):
+
+ if pageIds == -1: pageIds = self.tuneInterface.getSelectedIds()
+ if trackIds == -1: trackIds = [ i for i in range(Config.NUMBER_OF_TRACKS) if self.trackSelected[i] ]
+
+ if len(trackIds):
+ self.skipCleanup = "track"
+ self.skipCleanup = ""
+ self.noteDB.tracksToClipboard( pageIds, trackIds )
+ self.trackInterface.setInterfaceMode("paste_tracks")
+ return True
+ return False
+
+ def trackDuplicateWidget( self, widget ):
+ if widget.get_active():
+ if self.trackDuplicate(): # duplicate succeeded
+ return
+ # cancel duplicate
+ widget.set_active(False)
+ self.trackInterface.setInterfaceMode("tool")
+ else:
+ self.trackInterface.setInterfaceMode("tool")
+
+ #-----------------------------------
+ # tune/page functions
+ #-----------------------------------
+
+ def scrollTune( self, direction ):
+ adj = self.GUI["2tuneScrolledWindow"].get_hadjustment()
+ if direction > 0:
+ adj.set_value( min( adj.value + Config.PAGE_THUMBNAIL_WIDTH, adj.upper - adj.page_size ) )
+ else:
+ adj.set_value( max( adj.value - Config.PAGE_THUMBNAIL_WIDTH, 0) )
+
+ def displayPage( self, pageId, nextId = -1 ):
+ if self.playing:
+ if self.displayedPage != pageId and pageId in self.pages_playing:
+ self.csnd.loopSetTick( self.page_onset[pageId] )
+
+ self._displayPage( pageId, nextId )
+
+
+ # only called locally!
+ def _displayPage( self, pageId, nextId = -1 ):
+
+ self.displayedPage = pageId
+
+ page = self.noteDB.getPage(pageId)
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackInstrument[i].instrumentId != page.instruments[i]:
+ self.trackInstrument[i] = Config.INSTRUMENTSID[page.instruments[i]]
+ if i == Config.NUMBER_OF_TRACKS-1:
+ btn = self.GUI["2drumButton"]
+ btn.setImage( "main", self.GUI["2instrumentIcons"][self.trackInstrument[i].name] )
+ btn.setImage( "alt", self.GUI["2instrumentIcons"][self.trackInstrument[i].name] )
+ else:
+ btn = self.GUI["2instrument%dButton"%(i+1)]
+ btn.setPrimary( self.GUI["2instrumentIcons"][self.trackInstrument[i].name] )
+ if self.trackInstrument2[i] != None:
+ btn.setSecondary( self.GUI["2instrumentIcons"][self.trackInstrument2[i].name] )
+ else:
+ btn.setSecondary( None )
+ self.tuneInterface.displayPage( pageId )
+ self.trackInterface.displayPage( pageId, nextId )
+
+ def predrawPage( self, pageId ):
+ if self.playbackTimeout: return # we're playing, predrawing is already handled
+ if self.trackInterface.setPredrawPage( pageId ): # page needs to be drawn
+ self.trackInterface.predrawPage()
+
+ def abortPredrawPage( self ):
+ self.trackInterface.abortPredrawPage()
+
+ def pageGenerate( self, widget ):
+ if widget.get_active():
+ self.generateMode = "page"
+ winLoc = self.parent.window.get_position()
+ balloc = self.GUI["2contextBox"].get_allocation()
+ walloc = self.GUI["9generationPopup"].get_allocation()
+ if walloc.height != 1: # hack to make deal with showing the window before first allocation T_T
+ self.GUI["9generationPopup"].move( balloc.x + winLoc[0], balloc.y - walloc.height + winLoc[1] )
+ else:
+ self.GUI["9generationPopup"].move(0, 2048) # off the screen
+ self.GUI["9generationPopup"].show()
+ if walloc.height == 1:
+ walloc = self.GUI["9generationPopup"].get_allocation()
+ self.GUI["9generationPopup"].move( balloc.x + winLoc[0], balloc.y - walloc.height + winLoc[1] )
+ else:
+ self.GUI["9generationPopup"].hide()
+
+ def setPageGenerateMode(self, mode):
+ self.generateMode = mode
+
+ def pageProperties( self, widget ):
+ if widget.get_active():
+ self.propertiesPanel.setContext( "page", self.generationPanel.scale, self.tuneInterface.getSelectedIds() )
+ winLoc = self.parent.window.get_position()
+ balloc = self.GUI["2contextBox"].get_allocation()
+ walloc = self.GUI["9propertiesPopup"].get_allocation()
+ if walloc.height != 1: # hack to make deal with showing the window before first allocation T_T
+ self.GUI["9propertiesPopup"].move( balloc.x + winLoc[0] - 100, balloc.y - walloc.height + winLoc[1] )
+ else:
+ self.GUI["9propertiesPopup"].move(0, 2048) # off the screen
+ self.GUI["9propertiesPopup"].show()
+ if walloc.height == 1:
+ walloc = self.GUI["9propertiesPopup"].get_allocation()
+ self.GUI["9propertiesPopup"].move( balloc.x + winLoc[0] - 100, balloc.y - walloc.height + winLoc[1] )
+ else:
+ self.GUI["9propertiesPopup"].hide()
+
+ def pageDelete( self, pageIds = -1, instruments = False ):
+
+ if pageIds == -1:
+ pageIds = self.tuneInterface.getSelectedIds()
+
+ if instruments == False:
+ instruments = []
+ for inst in self.trackInstrument:
+ instruments.append(inst.instrumentId)
+
+ self.noteDB.deletePages( pageIds[:], instruments )
+
+ def pageDuplicate( self, after = -1, pageIds = False ):
+
+ if after == -1: after = self.tuneInterface.getLastSelected()
+ if not pageIds: pageIds = self.tuneInterface.getSelectedIds()
+
+ new = self.noteDB.duplicatePages( pageIds[:], after )
+ self.displayPage( new[self.displayedPage] )
+ self.tuneInterface.selectPages( new.values() )
+
+ def pageAdd( self, after = -1, beats = False, color = False, instruments = False ):
+
+ if after == -1: after = self.tuneInterface.getLastSelected()
+ page = self.noteDB.getPage( self.displayedPage )
+ if not beats: beats = page.beats
+ if not color: color = page.color
+ if not instruments: instruments = page.instruments
+
+ # TODO think about network mode here...
+ self.displayPage( self.noteDB.addPage( -1, NoteDB.Page(beats,color,instruments), after ) )
+
+ def pageBeats( self, pageIds = -1 ):
+
+ if pageIds == -1: pageIds = self.tuneInterface.getSelectedIds()
+
+ # TODO change the beats
+
+ #=======================================================
+ # NoteDB notifications
+
+ def notifyPageAdd( self, id, at ):
+ return
+
+ def notifyPageDelete( self, which, safe ):
+ if self.displayedPage in which:
+ self.displayPage( safe )
+
+ def notifyPageDuplicate( self, new, at ):
+ return
+
+ def notifyPageMove( self, which, low, high ):
+ return
+
+ def notifyPageUpdate( self, page, parameter, value ):
+ pass
+
+ def notifyNoteAdd( self, page, track, id ):
+ if (Config.DEBUG > 3) : print 'INFO: adding note to loop', page, track, id
+ n = self.noteDB.getNote(page, track, id)
+ self.csnd.loopPlay(n,0)
+ if self.playing and (n.page in self.page_onset ):
+ onset = n.cs.onset + self.page_onset[n.page]
+ self.csnd.loopUpdate(n, NoteDB.PARAMETER.ONSET, onset, 1) #set onset + activate
+
+ def notifyNoteDelete( self, page, track, id ):
+ if (Config.DEBUG > 3) : print 'INFO: deleting note from loop', page, track, id
+ self.csnd.loopDelete1(page,id)
+ def notifyNoteUpdate( self, page, track, id, parameter, value ):
+ if (Config.DEBUG > 3) : print 'INFO: updating note ', page, id, parameter, value
+ note = self.noteDB.getNote(page, track, id)
+ self.csnd.loopUpdate(note, parameter, value, -1)
+
+ #-----------------------------------
+ # load and save functions
+ #-----------------------------------
+
+ def waitToSet(self):
+ self.csnd.setMasterVolume(self._data['volume'])
+ self.csnd.setTempo(self._data['tempo'])
+ self.initTrackVolume()
+
+ def handleSave(self, widget):
+
+ chooser = gtk.FileChooserDialog(
+ title='Save Tune',
+ action=gtk.FILE_CHOOSER_ACTION_SAVE,
+ buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_SAVE,gtk.RESPONSE_OK))
+ filter = gtk.FileFilter()
+ filter.add_pattern('*.tam')
+ chooser.set_filter(filter)
+ chooser.set_current_folder(Config.TUNE_DIR)
+
+ for f in chooser.list_shortcut_folder_uris():
+ chooser.remove_shortcut_folder_uri(f)
+
+ if chooser.run() == gtk.RESPONSE_OK:
+ ofilename = chooser.get_filename()
+ if ofilename[-4:] != '.tam':
+ ofilename += '.tam'
+ try:
+ ofile = open(ofilename, 'w')
+ ofilestream = ControlStream.TamTamOStream (ofile)
+ self.noteDB.dumpToStream(ofilestream)
+ ofilestream.track_vol(self._data['track_volume'])
+ ofilestream.master_vol(self._data['volume'])
+ ofilestream.tempo(self._data['tempo'])
+ ofile.close()
+ except OSError,e:
+ print 'ERROR: failed to open file %s for writing\n' % ofilename
+ chooser.destroy()
+
+ def handleJournalSave(self, file_path):
+ ofile = open(file_path, 'w')
+ ofilestream = ControlStream.TamTamOStream (ofile)
+ self.noteDB.dumpToStream(ofilestream)
+ ofilestream.track_vol(self._data['track_volume'])
+ ofilestream.master_vol(self._data['volume'])
+ ofilestream.tempo(self._data['tempo'])
+ ofile.close()
+
+ def _loadFile( self, path ):
+ try:
+ oldPages = self.noteDB.getTune()[:]
+
+ ifile = open(path, 'r')
+ ttt = ControlStream.TamTamTable ( self.noteDB )
+ ttt.parseFile(ifile)
+ self.trackInstrument = self.trackInstrumentDefault[:] # these will get set correctly in displayPage
+ self._data['track_volume'] = ttt.tracks_volume
+ self._data['volume'] = float(ttt.masterVolume)
+ self._data['tempo'] = float(ttt.tempo)
+ #self.GUI["2volumeAdjustment"].set_value(self._data['volume'])
+ #self.GUI["2tempoAdjustment"].set_value(self._data['tempo'])
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if i == 4:
+ string = '2drumvolumeAdjustment'
+ else:
+ string = '2instrument' + str(i+1) + 'volumeAdjustment'
+ self.GUI[string].set_value(self._data['track_volume'][i])
+ ifile.close()
+
+ self.noteDB.deletePages( oldPages )
+
+ self.tuneInterface.selectPages( self.noteDB.getTune() )
+ except OSError,e:
+ print 'ERROR: failed to open file %s for reading\n' % ofilename
+
+ def handleLoad(self, widget):
+ chooser = gtk.FileChooserDialog(
+ title='Load Tune',
+ action=gtk.FILE_CHOOSER_ACTION_OPEN,
+ buttons=(gtk.STOCK_CANCEL,gtk.RESPONSE_CANCEL,gtk.STOCK_OPEN,gtk.RESPONSE_OK))
+
+ filter = gtk.FileFilter()
+ filter.add_pattern('*.tam')
+ chooser.set_filter(filter)
+ chooser.set_current_folder(Config.TUNE_DIR)
+
+ for f in chooser.list_shortcut_folder_uris():
+ chooser.remove_shortcut_folder_uri(f)
+
+ if chooser.run() == gtk.RESPONSE_OK:
+ print 'DEBUG: loading file: ', chooser.get_filename()
+ self._loadFile( chooser.get_filename() )
+
+ chooser.destroy()
+ self.delay = gobject.timeout_add(1000, self.waitToSet)
+
+ def handleJournalLoad(self,file_path):
+ self.journalCalled = True
+ self._loadFile( file_path )
+
+ #-----------------------------------
+ # Record functions
+ #-----------------------------------
+ def handleMicRecord( self, widget, data ):
+ self.csnd.micRecording( data )
+ def handleCloseMicRecordWindow( self, widget = None, data = None ):
+ self.micRecordWindow.destroy()
+ self.micRecordButton.set_active( False )
+
+ #-----------------------------------
+ # callback functions
+ #-----------------------------------
+ def handleKeyboardShortcuts(self,event):
+ keyval = event.keyval
+
+ # backspace and del keys
+ if keyval == gtk.keysyms.Delete or keyval == gtk.keysyms.BackSpace:
+ if self.context == CONTEXT.PAGE: self.pageDelete()
+ if self.context == CONTEXT.TRACK: self.trackDelete()
+ if self.context == CONTEXT.NOTE: self.noteDelete()
+ # plus key
+ if keyval == gtk.keysyms.equal:
+ self.pageAdd()
+ # duplicate ctrl-c
+ if event.state == gtk.gdk.CONTROL_MASK and keyval == gtk.keysyms.c:
+ if self.context == CONTEXT.PAGE: self.pageDuplicate()
+ if self.context == CONTEXT.TRACK: self.trackDuplicate()
+ if self.context == CONTEXT.NOTE: self.noteDuplicate()
+ #Arrows
+ if event.state == gtk.gdk.SHIFT_MASK:
+ # up/down arrows volume
+ if keyval == gtk.keysyms.Up: self.trackInterface.noteStepVolume(0.1)
+ if keyval == gtk.keysyms.Down: self.trackInterface.noteStepVolume(-0.1)
+ # left/right arrows onset
+ if keyval == gtk.keysyms.Left: self.trackInterface.noteStepDuration(-1)
+ if keyval == gtk.keysyms.Right: self.trackInterface.noteStepDuration(1)
+ else:
+ # up/down arrows pitch
+ if keyval == gtk.keysyms.Up: self.trackInterface.noteStepPitch(1)
+ if keyval == gtk.keysyms.Down: self.trackInterface.noteStepPitch(-1)
+ # left/right arrows duration
+ if keyval == gtk.keysyms.Left: self.trackInterface.noteStepOnset(-1)
+ if keyval == gtk.keysyms.Right: self.trackInterface.noteStepOnset(1)
+
+
+ def onKeyPress(self,widget,event):
+ self.handleKeyboardShortcuts(event)
+ Config.ModKeys.keyPress( event.hardware_keycode )
+ key = event.hardware_keycode
+
+ # If the key is already in the dictionnary, exit function (to avoir key repeats)
+ if self.kb_keydict.has_key(key):
+ return
+
+ # Assign on which track the note will be created according to the number of keys pressed
+ if self.trackCount >= 9:
+ self.trackCount = 6
+ fakeTrack = self.trackCount
+ self.trackCount += 1
+
+ # If the pressed key is in the keymap
+ if KEY_MAP_PIANO.has_key(key):
+ pitch = KEY_MAP_PIANO[key]
+ duration = -1
+
+ # get instrument from top selected track if a track is selected
+ if True in self.trackSelected:
+ index = self.trackSelected.index(True)
+ instrument = self.getTrackInstrument(index).name
+ else:
+ return
+
+ tid = index
+
+ # pitch, inst and duration for drum recording
+ if tid == Config.NUMBER_OF_TRACKS-1:
+ if GenerationConstants.DRUMPITCH.has_key( pitch ):
+ pitch = GenerationConstants.DRUMPITCH[pitch]
+ if Config.INSTRUMENTS[instrument].kit != None:
+ instrument = Config.INSTRUMENTS[instrument].kit[pitch].name
+ duration = 100
+
+ # Create and play the note
+ self.kb_keydict[key] = CSoundNote(onset = 0,
+ pitch = pitch,
+ amplitude = 1,
+ pan = 0.5,
+ duration = duration,
+ trackId = fakeTrack,
+ instrumentId = Config.INSTRUMENTS[instrument].instrumentId,
+ tied = False,
+ mode = 'edit')
+ self.csnd.play(self.kb_keydict[key], 0.3)
+
+ # doesn't keep track of keys for drum recording
+ if tid == Config.NUMBER_OF_TRACKS-1:
+ del self.kb_keydict[key]
+
+ # remove previosly holded key from dictionary
+ if len(self.kb_keydict) > 1:
+ for k in self.kb_keydict.keys():
+ if k != key:
+ gobject.source_remove( self.durUpdate )
+ self.durUpdate = False
+ self.kb_keydict[k].duration = 0.5
+ self.kb_keydict[k].amplitude = 0
+ self.kb_keydict[k].decay = 0.7
+ self.kb_keydict[k].tied = False
+ self.csnd.play(self.kb_keydict[k], 0.3)
+ if not self.kb_record:
+ del self.kb_keydict[k]
+ return
+ self.removeRecNote(self.csId)
+
+ if not self.kb_record:
+ return
+
+ #record the note on track
+ pageList = self.tuneInterface.getSelectedIds()
+ pid = self.displayedPage
+ minOnset = self.page_onset[pid]
+ onsetQuantized = Config.DEFAULT_GRID * int((self.csnd.loopGetTick() - minOnset) / Config.DEFAULT_GRID + 0.5)
+
+ maxOnset = self.noteDB.getPage(pid).ticks
+ if onsetQuantized >= maxOnset:
+ if pid == pageList[-1]:
+ pid = pageList[0]
+ else:
+ if len(pageList) > 1:
+ pidPos = pageList.index(pid)
+ pid = pageList[pidPos+1]
+ onsetQuantized = 0
+
+ if tid < Config.NUMBER_OF_TRACKS-1:
+ for n in self.noteDB.getNotesByTrack( pid, tid ):
+ if onsetQuantized < n.cs.onset:
+ break
+ if onsetQuantized >= n.cs.onset + n.cs.duration:
+ continue
+ if onsetQuantized < n.cs.onset + n.cs.duration - 2:
+ self.noteDB.deleteNote(n.page, n.track, n.id)
+ elif onsetQuantized - n.cs.onset < 1:
+ self.noteDB.deleteNote(n.page, n.track, n.id)
+ else:
+ self.noteDB.updateNote( n.page, n.track, n.id, PARAMETER.DURATION, onsetQuantized - n.cs.onset )
+ break
+ else:
+ for n in self.noteDB.getNotesByTrack( pid, tid ):
+ if onsetQuantized < n.cs.onset:
+ break
+ if onsetQuantized == n.cs.onset:
+ if pitch < n.cs.pitch:
+ break
+ if pitch == n.cs.pitch:
+ return # don't bother with a new note
+
+ csnote = CSoundNote(onset = 0,
+ pitch = pitch,
+ amplitude = 1,
+ pan = 0.5,
+ duration = duration,
+ trackId = index,
+ instrumentId = Config.INSTRUMENTS[instrument].instrumentId,
+ tied = False,
+ mode = 'edit')
+
+ csnote.onset = onsetQuantized
+ csnote.duration = 1
+ csnote.pageId = pid
+ id = self.noteDB.addNote(-1, pid, tid, csnote)
+ # csId: PageId, TrackId, Onset, Key, DurationSetOnce
+ self.csId = [pid, tid, id, csnote.onset, key, False ]
+ if tid < Config.NUMBER_OF_TRACKS-1:
+ self.durUpdate = gobject.timeout_add( 25, self.durationUpdate )
+
+ def onKeyRelease(self,widget,event):
+ Config.ModKeys.keyRelease( event.hardware_keycode )
+ key = event.hardware_keycode
+
+ if True in self.trackSelected:
+ index = self.trackSelected.index(True)
+ if index == Config.NUMBER_OF_TRACKS-1:
+ return
+ else:
+ return
+
+ if KEY_MAP_PIANO.has_key(key) and self.kb_keydict.has_key(key):
+ if self.kb_record and self.durUpdate:
+ gobject.source_remove( self.durUpdate )
+ self.durUpdate = False
+
+ if Config.INSTRUMENTSID[ self.kb_keydict[key].instrumentId ].csoundInstrumentId == Config.INST_TIED:
+ self.kb_keydict[key].duration = 0.5
+ self.kb_keydict[key].amplitude = 0
+ self.kb_keydict[key].decay = 0.5
+ self.kb_keydict[key].tied = False
+ self.csnd.play(self.kb_keydict[key], 0.3)
+ if not self.kb_record:
+ del self.kb_keydict[key]
+ return
+
+ self.removeRecNote(self.csId)
+
+ def removeRecNote(self, csId):
+ newDuration = (int(self.csnd.loopGetTick()) - self.page_onset[csId[0]]) - csId[3]
+ maxTick = self.noteDB.getPage(csId[0]).ticks
+
+ if not csId[5]: # handle notes that were created right at the end of a page
+ if newDuration > maxTick//2:
+ newDuration = 1
+ else:
+ csId[5] = True
+
+ if newDuration < -Config.DEFAULT_GRID_DIV2: # we looped around
+ newDuration = maxTick - self.csId[3]
+ elif newDuration < 1:
+ newDuration = 1
+
+ if (csId[3] + newDuration) > maxTick:
+ newDuration = maxTick - csId[3]
+
+ for n in self.noteDB.getNotesByTrack( csId[0], csId[1] ):
+ if n.id == csId[2]:
+ continue
+ if csId[3] + newDuration <= n.cs.onset:
+ break
+ if csId[3] >= n.cs.onset + n.cs.duration:
+ continue
+ self.noteDB.deleteNote(n.page, n.track, n.id)
+ break
+
+ self.noteDB.updateNote( csId[0], csId[1], csId[2], PARAMETER.DURATION, newDuration)
+
+ del self.kb_keydict[csId[4]]
+
+ def durationUpdate(self):
+ newDuration = (int(self.csnd.loopGetTick()) - self.page_onset[self.csId[0]]) - self.csId[3]
+
+ maxTick = self.noteDB.getPage(self.csId[0]).ticks
+ stop = False
+
+ if not self.csId[5]: # handle notes that were created right at the end of a page
+ if newDuration > maxTick//2:
+ newDuration = 1
+ else:
+ self.csId[5] = True
+
+ if newDuration < -Config.DEFAULT_GRID_DIV2: # we looped around
+ newDuration = maxTick - self.csId[3]
+ stop = True
+ elif newDuration < 1:
+ newDuration = 1
+
+ if (self.csId[3] + newDuration) > maxTick:
+ stop = True
+ newDuration = maxTick - self.csId[3]
+
+ for n in self.noteDB.getNotesByTrack( self.csId[0], self.csId[1] ):
+ if n.id == self.csId[2]:
+ continue
+ if self.csId[3] + newDuration <= n.cs.onset:
+ break
+ if self.csId[3] >= n.cs.onset + n.cs.duration:
+ continue
+ self.noteDB.deleteNote(n.page, n.track, n.id)
+ break
+
+ self.noteDB.updateNote( self.csId[0], self.csId[1], self.csId[2], PARAMETER.DURATION, newDuration)
+
+ if stop:
+ key = self.csId[4]
+ if Config.INSTRUMENTSID[ self.kb_keydict[key].instrumentId ].csoundInstrumentId == Config.INST_TIED:
+ self.kb_keydict[key].duration = 0.5
+ self.kb_keydict[key].amplitude = 0
+ self.kb_keydict[key].decay = 0.5
+ self.kb_keydict[key].tied = False
+ self.csnd.play(self.kb_keydict[key], 0.3)
+
+ del self.kb_keydict[key]
+ return False
+ return True
+
+ def delete_event( self, widget, event, data = None ):
+ return False
+
+ def onDestroy( self ):
+
+ if (Config.DEBUG > 1): print TP.PrintAll()
+
+ def setContextState( self, context, state ):
+ if context == CONTEXT.TRACK:
+ self.contextTrackActive = state
+ if not state:
+ if self.context == CONTEXT.TRACK:
+ if self.contextNoteActive:
+ self.setContext( CONTEXT.NOTE )
+ else:
+ self.setContext( CONTEXT.PAGE )
+ else:
+ self.contextNoteActive = state
+ if not state:
+ if self.context == CONTEXT.NOTE:
+ self.prevContext()
+
+ def setContext( self, context, force = False ):
+
+ if self.context == context and not force: return
+
+ self.context = context
+
+ if self.context == CONTEXT.NOTE:
+ self._generateToolbar.generationButton.set_sensitive(False)
+ else:
+ self._generateToolbar.generationButton.set_sensitive(True)
+
+ def getContext(self):
+ return self.context
+
+ def prevContext( self ):
+ if self.context == CONTEXT.TRACK:
+ self.setContext( CONTEXT.PAGE )
+ elif self.contextTrackActive:
+ self.setContext( CONTEXT.TRACK )
+ else:
+ self.setContext( CONTEXT.PAGE )
+
+ def nextContext( self ):
+ if self.context == CONTEXT.TRACK:
+ self.setContext( CONTEXT.NOTE )
+ elif self.contextTrackActive:
+ self.setContext( CONTEXT.TRACK )
+ else:
+ self.setContext( CONTEXT.NOTE )
+
+ #-----------------------------------
+ # access functions (not sure if this is the best way to go about doing this)
+ #-----------------------------------
+ def getVolume( self ):
+ return self._data["volume"]
+
+ def getTempo( self ):
+ return self._data["tempo"]
+ #return round( self.tempoAdjustment.value, 0 )
+
+ def getBeatsPerPage( self ):
+ return int(round( self.beatsPerPageAdjustment.value, 0 ))
+
+ def getWindowTitle( self ):
+ return "Tam-Tam [Volume %i, Tempo %i, Beats/Page %i]" % ( self.getVolume(), self.getTempo(), self.getBeatsPerPage() )
+
+
+class InstrumentButton( gtk.DrawingArea ):
+
+ def __init__( self, owner, index, backgroundFill ):
+ gtk.DrawingArea.__init__( self )
+
+ self.index = index
+ self.owner = owner
+
+ self.win = gtk.gdk.get_default_root_window()
+ self.gc = gtk.gdk.GC( self.win )
+
+ colormap = self.get_colormap()
+ self.color = { "background": colormap.alloc_color( backgroundFill, True, True ),
+ "divider": colormap.alloc_color( "#000", True, True ),
+ "+/-": colormap.alloc_color( Config.FG_COLOR, True, True ),
+ "+/-Highlight": colormap.alloc_color( "#FFF", True, True ) }
+
+ self.pixmap = None
+ self.primary = None
+ self.primaryWidth = self.primaryHeight = 1
+ self.secondary = None
+ self.secondaryWidth = self.secondaryHeight = 1
+
+ self.clicked = None
+ self.hover = None
+
+ self.add_events( gtk.gdk.BUTTON_PRESS_MASK
+ | gtk.gdk.BUTTON_RELEASE_MASK
+ | gtk.gdk.POINTER_MOTION_MASK
+ | gtk.gdk.POINTER_MOTION_HINT_MASK
+ | gtk.gdk.LEAVE_NOTIFY_MASK
+ | gtk.gdk.ENTER_NOTIFY_MASK )
+ self.connect( "size-allocate", self.size_allocate )
+ self.connect( "button-press-event", self.button_press )
+ self.connect( "button-release-event", self.button_release )
+ self.connect( "motion-notify-event", self.motion_notify )
+ self.connect( "enter-notify-event", self.enter_notify )
+ self.connect( "leave-notify-event", self.leave_notify )
+ self.connect( "expose-event", self.expose )
+
+ def size_allocate( self, widget, allocation ):
+ self.alloc = allocation
+ self.pixmap = gtk.gdk.Pixmap( self.win, allocation.width, allocation.height )
+ self.primaryX = (self.alloc.width - self.primaryWidth) // 2
+ self.primaryY = (self.alloc.height - self.primaryHeight) // 2
+ self.secondaryX = (self.alloc.width - self.secondaryWidth) // 2
+ self.secondaryY = self.alloc.height//2
+
+ self.hotspots = [ [ self.alloc.width-24, self.alloc.height-29, self.alloc.width-8, self.alloc.height-13 ],
+ [ self.alloc.width-24, self.alloc.height//2-23, self.alloc.width-8, self.alloc.height//2-7 ] ]
+
+ self.hotspots[0] += [ (self.hotspots[0][0]+self.hotspots[0][2])//2, (self.hotspots[0][1]+self.hotspots[0][3])//2 ]
+ self.hotspots[1] += [ (self.hotspots[1][0]+self.hotspots[1][2])//2, (self.hotspots[1][1]+self.hotspots[1][3])//2 ]
+
+ self._updatePixmap()
+
+ def button_press( self, widget, event ):
+
+ self.clicked = "PRIMARY"
+ self.hover = None
+
+ if event.x >= self.hotspots[0][0] and event.x <= self.hotspots[0][2] \
+ and event.y >= self.hotspots[0][1] and event.y <= self.hotspots[0][3]:
+ self.clicked = "HOTSPOT_0"
+
+ elif self.secondary != None:
+
+ if event.x >= self.hotspots[1][0] and event.x <= self.hotspots[1][2] \
+ and event.y >= self.hotspots[1][1] and event.y <= self.hotspots[1][3]:
+ self.clicked = "HOTSPOT_1"
+
+ elif event.y > self.alloc.height//2:
+ self.clicked = "SECONDARY"
+
+ def button_release( self, widget, event ):
+ if self.clicked == "PRIMARY":
+ self.owner.pickInstrument( self, self.index, True )
+ elif self.clicked == "SECONDARY":
+ self.owner.pickInstrument( self, self.index, False )
+ elif self.clicked == "HOTSPOT_0":
+ if self.secondary != None: # remove secondary
+ self.owner.clearInstrument( self.index, False )
+ else: # add secondary
+ self.owner.pickInstrument( self, self.index, False )
+ else: # HOTSPOT_1, remove primary
+ self.owner.clearInstrument( self.index, True )
+
+ self.clicked = None
+
+ def motion_notify( self, widget, event ):
+
+ if self.clicked != None:
+ return
+
+ if event.is_hint:
+ x, y, state = widget.window.get_pointer()
+ event.x = float(x)
+ event.y = float(y)
+ event.state = state
+
+ if event.x >= self.hotspots[0][0] and event.x <= self.hotspots[0][2] \
+ and event.y >= self.hotspots[0][1] and event.y <= self.hotspots[0][3]:
+ if self.hover != "HOTSPOT_0":
+ self.hover = "HOTSPOT_0"
+ self.queue_draw()
+
+
+ elif self.secondary != None \
+ and event.x >= self.hotspots[1][0] and event.x <= self.hotspots[1][2] \
+ and event.y >= self.hotspots[1][1] and event.y <= self.hotspots[1][3]:
+ if self.hover != "HOTSPOT_1":
+ self.hover = "HOTSPOT_1"
+ self.queue_draw()
+ else:
+ if self.hover != None:
+ self.hover = None
+ self.queue_draw()
+
+ def leave_notify( self, widget, event ):
+ if event.mode != gtk.gdk.CROSSING_NORMAL:
+ return
+ if self.hover != None:
+ self.hover = None
+ if self.clicked == None:
+ self.queue_draw()
+
+ self.owner.activity.handler_unblock(self.owner.activity.focusOutHandler)
+ self.owner.activity.handler_unblock(self.owner.activity.focusInHandler)
+
+ def enter_notify(self, widget, event):
+ # Block the Focus Out event so that the sound does'nt stop when a Palette is invoked.
+ self.owner.activity.handler_block(self.owner.activity.focusOutHandler)
+ self.owner.activity.handler_block(self.owner.activity.focusInHandler)
+
+ def setPrimary( self, img ):
+ self.primary = img
+ self.primaryWidth = img.get_width()
+ self.primaryHeight = img.get_height()
+ if self.pixmap:
+ self.primaryX = (self.alloc.width - self.primaryWidth) // 2
+ self.primaryY = (self.alloc.height - self.primaryHeight) // 2
+ self._updatePixmap()
+
+ def setSecondary( self, img ):
+ self.secondary = img
+ if img != None:
+ self.secondaryWidth = img.get_width()
+ self.secondaryHeight = img.get_height()
+ self.secondaryOffset = self.secondaryHeight//2
+ if self.pixmap:
+ self.secondaryX = (self.alloc.width - self.secondaryWidth) // 2
+ self.secondaryY = self.alloc.height//2
+ if self.pixmap:
+ self._updatePixmap()
+
+ def _updatePixmap( self ):
+ self.gc.foreground = self.color["background"]
+ self.pixmap.draw_rectangle( self.gc, True, 0, 0, self.alloc.width, self.alloc.height )
+ if self.secondary != None:
+ self.pixmap.draw_pixbuf( self.gc, self.primary, 0, 0, self.primaryX, self.primaryY, self.primaryWidth, self.primaryHeight//2, gtk.gdk.RGB_DITHER_NONE )
+ self.pixmap.draw_pixbuf( self.gc, self.secondary, 0, self.secondaryOffset, self.secondaryX, self.secondaryY, self.secondaryWidth, self.secondaryHeight//2, gtk.gdk.RGB_DITHER_NONE )
+ self.gc.foreground = self.color["divider"]
+ self.gc.set_line_attributes( 2, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER )
+ self.pixmap.draw_line( self.gc, 2, self.alloc.height//2, self.alloc.width-4, self.alloc.height//2 )
+ else:
+ self.pixmap.draw_pixbuf( self.gc, self.primary, 0, 0, self.primaryX, self.primaryY, self.primaryWidth, self.primaryHeight, gtk.gdk.RGB_DITHER_NONE )
+ self.queue_draw()
+
+ def expose( self, widget, event ):
+ self.window.draw_drawable( self.gc, self.pixmap, 0, 0, 0, 0, self.alloc.width, self.alloc.height )
+ self.gc.set_line_attributes( 4, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_MITER )
+ if self.secondary != None:
+ if self.clicked == "HOTSPOT_0" or (self.clicked == None and self.hover == "HOTSPOT_0" ):
+ self.gc.foreground = self.color["+/-Highlight"]
+ else:
+ self.gc.foreground = self.color["+/-"]
+ self.window.draw_line( self.gc, self.hotspots[0][0], self.hotspots[0][5], self.hotspots[0][2], self.hotspots[0][5] )
+ if self.clicked == "HOTSPOT_1" or (self.clicked == None and self.hover == "HOTSPOT_1" ):
+ self.gc.foreground = self.color["+/-Highlight"]
+ else:
+ self.gc.foreground = self.color["+/-"]
+ self.window.draw_line( self.gc, self.hotspots[1][0], self.hotspots[1][5], self.hotspots[1][2], self.hotspots[1][5] )
+ else:
+ if self.clicked == "HOTSPOT_0" or self.hover == "HOTSPOT_0":
+ self.gc.foreground = self.color["+/-Highlight"]
+ else:
+ self.gc.foreground = self.color["+/-"]
+ self.window.draw_line( self.gc, self.hotspots[0][0], self.hotspots[0][5], self.hotspots[0][2], self.hotspots[0][5] )
+ self.window.draw_line( self.gc, self.hotspots[0][4], self.hotspots[0][1], self.hotspots[0][4], self.hotspots[0][3] )
+
+ def set_palette(self, palette):
+ self._palette = palette
+ self._palette.props.invoker = WidgetInvoker(self)
+ self._palette.props.invoker._position_hint = WidgetInvoker.AT_CURSOR #This is a hack, will change with newer Palette API
+
+
+class instrumentPalette(Palette):
+ def __init__(self, label, trackID, edit):
+ Palette.__init__(self, label)
+
+ self.trackID = trackID
+ self.edit = edit
+
+ self.tooltips = gtk.Tooltips()
+
+ self.volumeBox = gtk.HBox()
+
+ self.muteButton = gtk.CheckButton()
+ self.muteButton.connect("toggled",self.edit.handlemuteButton, self.trackID)
+ self.muteButton.connect("button-press-event",self.edit.handlemuteButtonRightClick, self.trackID)
+ self.muteButton.set_active(True)
+ self.tooltips.set_tip(self.muteButton, _('Left click to mute, right click to solo'))
+
+ if self.trackID < 4:
+ exec "self.volumeSliderAdj = self.edit.GUI['2instrument%svolumeAdjustment']" % str(self.trackID+1)
+ else:
+ self.volumeSliderAdj = self.edit.GUI["2drumvolumeAdjustment"]
+ self.volumeSliderAdj.connect( "value-changed", self.edit.handleTrackVolume, self.trackID)
+ self.volumeSlider = gtk.HScale(adjustment = self.volumeSliderAdj)
+ self.volumeSlider.set_size_request(250, -1)
+ self.volumeSlider.set_inverted(False)
+ self.volumeSlider.set_draw_value(False)
+
+ self.volumeBox.pack_start(self.muteButton, padding = 5)
+ self.volumeBox.pack_start(self.volumeSlider, padding = 5)
+ self.volumeBox.show_all()
+
+ self.set_content(self.volumeBox)
+ \ No newline at end of file
diff --git a/TamTamEdit.activity/Edit/NoteInterface.py b/TamTamEdit.activity/Edit/NoteInterface.py
new file mode 100644
index 0000000..a5035dd
--- /dev/null
+++ b/TamTamEdit.activity/Edit/NoteInterface.py
@@ -0,0 +1,345 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import Config
+
+from Util.NoteDB import PARAMETER
+from Util.CSoundClient import new_csound_client
+
+class NoteInterface:
+
+ def __init__( self, noteDB, owner, note ):
+ self.noteDB = noteDB
+ self.owner = owner
+ self.note = note
+
+ self.origin = self.owner.getTrackOrigin( note.track )
+ self.firstTransform = True
+ self.x = 0
+ self.y = 0
+ self.width = 1
+ self.height = Config.NOTE_HEIGHT
+ self.imgX = 0
+ self.imgY = 0
+ self.imgWidth = 1
+ self.imgHeight = self.height + Config.NOTE_IMAGE_PADDING_MUL2
+
+ self.selected = False
+ self.potentialDeselect = False
+
+ self.oldOnset = -1
+ self.oldEnd = -1
+ self.oldPitch = -1
+ self.oldAmplitude = -1
+ self.oldBeats = -1
+ self.lastDragO = 0
+ self.lastDragP = 0
+ self.lastDragD = 0
+
+ self.image, self.imageSelected, self.colormap, self.baseColors = self.owner.getDrawingPackage( note.track )
+
+ self.updateParameter( None, None )
+
+ def destroy( self ):
+ if self.selected:
+ self.owner.deselectNotes( { self.note.track: [self] } )
+ else: # if we were deselected above the rect has already been invalidated
+ self.owner.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.note.page, True )
+
+ def updateParameter( self, parameter, value ):
+ self.end = self.note.cs.onset + self.note.cs.duration
+
+ if self.oldAmplitude != self.note.cs.amplitude:
+ r = self.baseColors[0][0] + int(self.baseColors[1][0]*self.note.cs.amplitude)
+ g = self.baseColors[0][1] + int(self.baseColors[1][1]*self.note.cs.amplitude)
+ b = self.baseColors[0][2] + int(self.baseColors[1][2]*self.note.cs.amplitude)
+ self.color = self.colormap.alloc_color( r, g, b, True, True ) # TODO potential memory leak?
+ self.oldAmplitude = self.note.cs.amplitude
+
+ self.updateTransform()
+
+ def getId( self ):
+ return self.note.id
+
+ def getStartTick( self ):
+ return self.note.cs.onset
+
+ def getEndTick( self ):
+ return self.end
+
+ def testOnset( self, start, stop ):
+ return self.note.cs.onset >= start and self.note.cs.onset < stop
+
+ def getPitch( self ):
+ return self.note.cs.pitch
+
+ def updateTransform( self ):
+ if self.note.page in self.owner.getActivePages():
+ if not self.firstTransform:
+ oldX = self.imgX
+ oldY = self.imgY
+ oldEndX = self.imgX + self.imgWidth
+ dirty = True
+ else:
+ dirty = False
+
+ beats = self.noteDB.getPage( self.note.page ).beats
+ if self.note.cs.onset != self.oldOnset or beats != self.oldBeats:
+ self.x = self.owner.ticksToPixels( beats, self.note.cs.onset )
+ self.x += self.origin[0]
+ self.imgX = self.x - Config.NOTE_IMAGE_PADDING
+ self.oldOnset = self.note.cs.onset
+ if self.end != self.oldEnd or self.note.cs.onset != self.oldOnset or beats != self.oldBeats:
+ self.width = self.owner.ticksToPixels( beats, self.end ) - self.x + self.origin[0]
+ self.imgWidth = self.width + Config.NOTE_IMAGE_PADDING_MUL2
+ self.oldEnd = self.end
+ if self.note.cs.pitch != self.oldPitch:
+ self.y = self.owner.pitchToPixels( self.note.cs.pitch ) + self.origin[1]
+ self.imgY = self.y - Config.NOTE_IMAGE_PADDING
+ self.oldPitch = self.note.cs.pitch
+ self.oldBeats = beats
+
+ if dirty:
+ if self.firstTransform:
+ self.owner.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.note.page, True )
+ else:
+ x = min( self.imgX, oldX )
+ y = min( self.imgY, oldY )
+ endx = max( self.imgX + self.imgWidth, oldEndX )
+ endy = max( self.imgY, oldY ) + self.imgHeight
+ self.owner.invalidate_rect( x, y, endx-x, endy-y, self.note.page, True )
+
+ self.firstTransform = False
+
+ def updateDragLimits( self, dragLimits, leftBound, rightBound, widthBound, maxRightBound ):
+ left = leftBound - self.note.cs.onset
+ right = rightBound - self.note.cs.duration - self.note.cs.onset
+ up = Config.MAXIMUM_PITCH - self.note.cs.pitch
+ down = Config.MINIMUM_PITCH - self.note.cs.pitch
+ short = Config.MINIMUM_NOTE_DURATION - self.note.cs.duration
+ long = widthBound - self.note.cs.duration - self.note.cs.onset
+
+ if dragLimits[0][0] < left: dragLimits[0][0] = left
+ if dragLimits[0][1] > right: dragLimits[0][1] = right
+ if dragLimits[1][0] < down: dragLimits[1][0] = down
+ if dragLimits[1][1] > up: dragLimits[1][1] = up
+ if dragLimits[2][0] < short: dragLimits[2][0] = short
+ if dragLimits[2][1] > long: dragLimits[2][1] = long
+
+ # store the current loc as a reference point
+ self.baseOnset = self.note.cs.onset
+ self.basePitch = self.note.cs.pitch
+ self.baseDuration = self.note.cs.duration
+
+ def playSampleNote( self, full=True ):
+ secs_per_tick = 0.025
+ csnd = new_csound_client()
+
+ if full:
+ onset = self.note.cs.onset
+ self.note.cs.onset = 0
+ csnd.play( self.note.cs, 0.024)
+ self.note.cs.onset = onset
+ else:
+ (onset,duration) = ( self.note.cs.onset, self.note.cs.duration)
+ ( self.note.cs.onset, self.note.cs.duration) = (0, 10)
+ csnd.play( self.note.cs, 0.024)
+ ( self.note.cs.onset, self.note.cs.duration) = (onset,duration)
+
+ #=======================================================
+ # Events
+
+ # handleButtonPress returns:
+ # -2, not a hit but there was X overlap
+ # -1, event occurs before us so don't bother checking any later notes
+ # 0, event didn't hit
+ # 1, event was handled
+ def handleButtonPress( self, emitter, event ):
+ eX = event.x - self.x
+ if eX < 0:
+ return -1 # event occurs before us, no point in checking further
+ if eX > self.width:
+ return 0 # no X overlap
+
+ eY = event.y - self.y
+ if eY < 0 or eY > self.height:
+ return -2 # not a hit, but it was in our X range
+
+ if event.button == 3:
+ print "Show some note parameters!?!"
+ #self.noteParameters = NoteParametersWindow( self.note, self.getNoteParameters )
+ return 1 # handled
+
+ playSample = False
+
+ if event.type == gtk.gdk._2BUTTON_PRESS: # select bar
+ self.potentialDeselect = False
+ start = 0
+ check = self.note.cs.onset - Config.TICKS_PER_BEAT
+ while start <= check: start += Config.TICKS_PER_BEAT
+ stop = start + Config.TICKS_PER_BEAT
+ check += self.note.cs.duration
+ while stop < check: stop += Config.TICKS_PER_BEAT
+ emitter.selectNotesByBar( self.note.track, start, stop )
+ elif event.type == gtk.gdk._3BUTTON_PRESS: # select track
+ self.potentialDeselect = False
+ emitter.selectNotesByTrack( self.note.track )
+ else:
+ if self.selected: # we already selected, might want to delected
+ self.potentialDeselect = True
+ else:
+ emitter.selectNotes( { self.note.track: [ self ] } )
+ playSample = True
+
+ percent = eX/self.width
+ if percent < 0.3: emitter.setCurrentAction( "note-drag-onset", self )
+ elif percent > 0.7: emitter.setCurrentAction( "note-drag-duration", self )
+ else:
+ emitter.setCurrentAction( "note-drag-pitch", self )
+ if playSample: self.playSampleNote()
+
+ return 1
+
+ def handleButtonRelease( self, emitter, event, buttonPressCount ):
+
+ if self.potentialDeselect:
+ self.potentialDeselect = False
+ emitter.deselectNotes( { self.note.track: [ self ] } )
+
+ emitter.doneCurrentAction()
+
+ return True
+
+ def noteDragOnset( self, do, stream ):
+ self.potentialDeselect = False
+ if do != self.lastDragO:
+ self.lastDragO = do
+ stream += [ self.note.id, self.baseOnset + do ]
+
+ def noteDragPitch( self, dp, stream ):
+ self.potentialDeselect = False
+ if dp != self.lastDragP:
+ self.lastDragP = dp
+ stream += [ self.note.id, self.basePitch + dp ]
+
+ def noteDragDuration( self, dd, stream ):
+ self.potentialDeselect = False
+ if dd != self.lastDragD:
+ self.lastDragD = dd
+ stream += [ self.note.id, self.baseDuration + dd ]
+
+ def doneNoteDrag( self, emitter ):
+ self.baseOnset = self.note.cs.onset
+ self.basePitch = self.note.cs.pitch
+ self.baseDuration = self.note.cs.duration
+
+ self.lastDragO = 0
+ self.lastDragP = 0
+ self.lastDragD = 0
+
+ def noteDecOnset( self, step, leftBound, stream ):
+ if self.selected:
+ if leftBound < self.note.cs.onset:
+ onset = max( self.note.cs.onset+step, leftBound )
+ stream += [ self.note.id, onset ]
+ return onset + self.note.cs.duration
+ return self.end
+
+ def noteIncOnset( self, step, rightBound, stream ):
+ if self.selected:
+ if rightBound > self.end:
+ onset = min( self.end+step, rightBound ) - self.note.cs.duration
+ stream += [ self.note.id, onset ]
+ return onset
+ return self.note.cs.onset
+
+ def noteDecPitch( self, step, stream ):
+ if self.note.cs.pitch > Config.MINIMUM_PITCH:
+ stream += [ self.note.id, max( self.note.cs.pitch+step, Config.MINIMUM_PITCH ) ]
+
+ def noteIncPitch( self, step, stream ):
+ if self.note.cs.pitch < Config.MAXIMUM_PITCH:
+ stream += [ self.note.id, min( self.note.cs.pitch+step, Config.MAXIMUM_PITCH ) ]
+
+ def noteDecDuration( self, step, stream ):
+ if self.note.cs.duration > Config.MINIMUM_NOTE_DURATION:
+ stream += [ self.note.id, max( self.note.cs.duration+step, Config.MINIMUM_NOTE_DURATION ) ]
+
+ def noteIncDuration( self, step, rightBound, stream ):
+ if self.selected:
+ if self.end < rightBound:
+ stream += [ self.note.id, min( self.end+step, rightBound ) - self.note.cs.onset ]
+
+ def noteDecVolume( self, step, stream ):
+ if self.note.cs.amplitude > 0:
+ stream += [ self.note.id, max( self.note.cs.amplitude+step, 0 ) ]
+
+ def noteIncVolume( self, step, stream ):
+ if self.note.cs.amplitude < 1:
+ stream += [ self.note.id, min( self.note.cs.amplitude+step, 1 ) ]
+
+ def handleMarqueeSelect( self, emitter, start, stop ):
+ intersectionY = [ max(start[1],self.y), min(stop[1],self.y+self.height) ]
+ if intersectionY[0] > intersectionY[1]:
+ return False
+
+ intersectionX = [ max(start[0],self.x), min(stop[0],self.x+self.width) ]
+ if intersectionX[0] > intersectionX[1]:
+ return False
+
+ return True
+
+ # updateTooltip returns:
+ # -2, not a hit but there was X overlap
+ # -1, event occurs before us so don't bother checking any later notes
+ # 0, event didn't hit
+ # 1, event was handled
+ def updateTooltip( self, emitter, event ):
+ eX = event.x - self.x
+ if eX < 0:
+ return -1 # event occurs before us, no point in checking further
+ if eX > self.width:
+ return 0 # no X overlap
+
+ eY = event.y - self.y
+ if eY < 0 or eY > self.height:
+ return -2 # not a hit, but it was in our X range
+
+ percent = eX/self.width
+ if percent < 0.3: emitter.setCursor("drag-onset")
+ elif percent > 0.7: emitter.setCursor("drag-duration")
+ else: emitter.setCursor("drag-pitch")
+
+ return 1 # we handled it
+
+ #=======================================================
+ # Selection
+
+ def setSelected( self, state ):
+ if self.selected != state:
+ self.selected = state
+ self.owner.invalidate_rect( self.imgX, self.imgY, self.imgWidth, self.imgHeight, self.note.page )
+ return True # state changed
+ return False # state is the same
+
+ def getSelected( self ):
+ return self.selected
+
+ #=======================================================
+ # Draw
+
+ def draw( self, win, gc, startX, stopX ):
+ if stopX < self.imgX: return False # we don't need to draw and no one after us will draw
+ if startX > self.imgX + self.imgWidth: return True # we don't need to draw, but maybe a later note does
+
+ gc.foreground = self.color
+ win.draw_rectangle( gc, True, self.x+1, self.y+1, self.width-2, self.height-2 )
+
+ if self.selected: img = self.imageSelected
+ else: img = self.image
+ win.draw_pixbuf( gc, img, 0, 0, self.imgX, self.imgY, self.imgWidth-Config.NOTE_IMAGE_ENDLENGTH, self.imgHeight, gtk.gdk.RGB_DITHER_NONE )
+ win.draw_pixbuf( gc, img, Config.NOTE_IMAGE_TAIL, 0, self.imgX+self.imgWidth-Config.NOTE_IMAGE_ENDLENGTH, self.imgY, Config.NOTE_IMAGE_ENDLENGTH, self.imgHeight, gtk.gdk.RGB_DITHER_NONE )
+
+ return True # we drew something
+
diff --git a/TamTamEdit.activity/Edit/Properties.py b/TamTamEdit.activity/Edit/Properties.py
new file mode 100644
index 0000000..b0caae0
--- /dev/null
+++ b/TamTamEdit.activity/Edit/Properties.py
@@ -0,0 +1,808 @@
+import pygtk
+pygtk.require('2.0')
+import gtk
+from types import *
+from math import sqrt
+from random import *
+from Generation.Drunk import *
+from Generation.GenerationConstants import GenerationConstants
+from Util.ThemeWidgets import *
+from Util.NoteDB import PARAMETER
+import Config
+Tooltips = Config.Tooltips()
+
+class Properties( gtk.VBox ):
+ def __init__( self, noteDB, doneHandler, popup ):
+ gtk.VBox.__init__( self )
+ self.tooltips = gtk.Tooltips()
+ self.noteDB = noteDB
+ #self.doneHandler = doneHandler
+ self.popup = popup
+ self.popup.resize( 545, 378 )
+
+ self.context = "page"
+ self.notes = {} # notes indexed by page and track
+ self.setup = False # flag to block note updates durning setup
+
+ self.line = Line(0, 100)
+ self.drunk = Drunk(0, 100)
+ self.droneAndJump = DroneAndJump(0, 100)
+ self.repeter = Repeter(0, 100)
+ self.loopseg = Loopseg(0, 100)
+ self.algoTypes = [self.line, self.drunk, self.droneAndJump, self.repeter, self.loopseg]
+ self.algorithm = self.algoTypes[0]
+
+ #self.set_size_request( 300, 200 )
+
+ self.filterType = 0
+ self.minValue = 0.
+ self.maxValue = 100.
+ self.paraValue = 20.
+
+ self.activeWidget = None
+
+ self.pageIds = []
+
+ self.GUI = {}
+ self.parametersBox = RoundHBox(fillcolor=Config.INST_BCK_COLOR, bordercolor=Config.PANEL_BCK_COLOR)
+ #self.parametersBox.set_border_width(1)
+ self.parametersBox.set_radius(10)
+ self.pack_start(self.parametersBox)
+ self.fixed = gtk.Fixed()
+ self.parametersBox.pack_start( self.fixed )
+
+ self.controlsBox = gtk.HBox()
+
+ #-- Page Properties ------------------------------------------------
+ self.pageBox = RoundHBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ self.pageBox.set_size_request( 125, -1 )
+ self.pageBox.set_border_width(3)
+ self.pageBox.set_radius(10)
+ beatBox = gtk.VBox()
+ self.beatAdjust = gtk.Adjustment( 4, 2, 12, 1, 1, 0)
+ self.GUI['beatSlider'] = ImageVScale( Config.TAM_TAM_ROOT + "/Resources/Images/sliderEditVolume.png", self.beatAdjust, 7 )
+ self.GUI['beatSlider'].connect("button-release-event", self.handleBeat)
+ self.GUI['beatSlider'].set_snap( 1 )
+ self.GUI['beatSlider'].set_inverted(True)
+ self.GUI['beatSlider'].set_size_request(50, 200)
+ beatBox.pack_start( self.GUI['beatSlider'] )
+ self.beatLabel = gtk.Image()
+ self.beatLabel.set_from_file(Config.IMAGE_ROOT + 'volume3.png')
+ self.beatAdjust.connect("value-changed", self.updateBeatLabel)
+ self.updateBeatLabel( self.beatAdjust )
+ beatBox.pack_start( self.beatLabel )
+ self.pageBox.pack_start( beatBox )
+ colorBox = gtk.VBox()
+ self.GUI["color0Button"] = ImageRadioButton( None, Config.IMAGE_ROOT+"pageThumbnailBut0.png", Config.IMAGE_ROOT+"pageThumbnailBut0Down.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI["color0Button"].set_size_request( 80, -1 )
+ self.GUI["color0Button"].connect( "clicked", self.handleColor, 0 )
+ colorBox.pack_start( self.GUI["color0Button"] )
+ self.GUI["color1Button"] = ImageRadioButton( self.GUI["color0Button"], Config.IMAGE_ROOT+"pageThumbnailBut1.png", Config.IMAGE_ROOT+"pageThumbnailBut1Down.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI["color1Button"].connect( "clicked", self.handleColor, 1 )
+ colorBox.pack_start( self.GUI["color1Button"] )
+ self.GUI["color2Button"] = ImageRadioButton( self.GUI["color0Button"], Config.IMAGE_ROOT+"pageThumbnailBut2.png", Config.IMAGE_ROOT+"pageThumbnailBut2Down.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI["color2Button"].connect( "clicked", self.handleColor, 2 )
+ colorBox.pack_start( self.GUI["color2Button"] )
+ self.GUI["color3Button"] = ImageRadioButton( self.GUI["color0Button"], Config.IMAGE_ROOT+"pageThumbnailBut3.png", Config.IMAGE_ROOT+"pageThumbnailBut3Down.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI["color3Button"].connect( "clicked", self.handleColor, 3 )
+ colorBox.pack_start( self.GUI["color3Button"] )
+ self.pageBox.pack_start( colorBox )
+ self.pageBox.show_all()
+ #self.controlsBox.pack_start(self.pageBox)
+
+ #-- Note Properties ------------------------------------------------
+ pitchBox = RoundVBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ pitchBox.set_border_width(3)
+ pitchBox.set_radius(10)
+ self.GUI['pitchUp'] = ImageButton( Config.IMAGE_ROOT+"arrowEditUp.png", Config.IMAGE_ROOT+"arrowEditUpDown.png", Config.IMAGE_ROOT+"arrowEditUpOver.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['pitchUp'].connect( "clicked", lambda w:self.stepPitch( 1 ) )
+ self.GUI['pitchGen'] = ImageToggleButton( Config.IMAGE_ROOT+"diceProp.png", Config.IMAGE_ROOT+"dicePropSel.png", Config.IMAGE_ROOT+"dicePropSel.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['pitchGen'].connect( "clicked", self.openAlgoBox, 'pitch' )
+ pitchBox.pack_start( self.GUI['pitchGen'], False, False, 5 )
+ pitchBox.pack_start( self.GUI['pitchUp'] )
+ self.pitchIcon = gtk.Image()
+ self.pitchIcon.set_from_file(Config.IMAGE_ROOT + 'propPitch2.png')
+ pitchBox.pack_start(self.pitchIcon)
+ self.GUI['pitchDown'] = ImageButton( Config.IMAGE_ROOT+"arrowEditDown.png", Config.IMAGE_ROOT+"arrowEditDownDown.png", Config.IMAGE_ROOT+"arrowEditDownOver.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['pitchDown'].connect( "clicked", lambda w:self.stepPitch( -1 ) )
+ pitchBox.pack_start( self.GUI['pitchDown'] )
+ self.controlsBox.pack_start(pitchBox)
+
+ volumeBox = RoundVBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ volumeBox.set_border_width(3)
+ volumeBox.set_radius(10)
+ self.GUI['volumeUp'] = ImageButton( Config.IMAGE_ROOT+"arrowEditUp.png", Config.IMAGE_ROOT+"arrowEditUpDown.png", Config.IMAGE_ROOT+"arrowEditUpOver.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['volumeUp'].connect( "clicked", lambda w:self.stepVolume( 0.1 ) )
+ self.GUI['volumeGen'] = ImageToggleButton( Config.IMAGE_ROOT+"diceProp.png", Config.IMAGE_ROOT+"dicePropSel.png", Config.IMAGE_ROOT+"dicePropSel.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['volumeGen'].connect( "clicked", self.openAlgoBox, 'volume' )
+ volumeBox.pack_start( self.GUI['volumeGen'], False, False, 5 )
+ volumeBox.pack_start( self.GUI['volumeUp'] )
+ self.volumeIcon = gtk.Image()
+ self.volumeIcon.set_from_file(Config.IMAGE_ROOT + 'volume3.png')
+ volumeBox.pack_start(self.volumeIcon)
+ self.GUI['volumeDown'] = ImageButton( Config.IMAGE_ROOT+"arrowEditDown.png", Config.IMAGE_ROOT+"arrowEditDownDown.png", Config.IMAGE_ROOT+"arrowEditDownOver.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['volumeDown'].connect( "clicked", lambda w:self.stepVolume( -0.1 ) )
+ volumeBox.pack_start( self.GUI['volumeDown'] )
+ self.controlsBox.pack_start(volumeBox)
+
+ panBox = RoundVBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ panBox.set_border_width(3)
+ panBox.set_radius(10)
+ self.panAdjust = gtk.Adjustment( 0.5, 0, 1, .1, .1, 0)
+ self.GUI['panSlider'] = ImageVScale( Config.TAM_TAM_ROOT + "/Resources/Images/sliderEditVolume.png", self.panAdjust, 7 )
+ self.panAdjust.connect("value-changed", self.handlePan)
+ self.GUI['panSlider'].set_snap( 0.1 )
+ self.GUI['panSlider'].set_inverted(True)
+ self.GUI['panSlider'].set_size_request(50, 200)
+ self.panLabel = gtk.Image()
+ self.handlePan( self.panAdjust )
+ self.GUI['panGen'] = ImageToggleButton( Config.IMAGE_ROOT+"diceProp.png", Config.IMAGE_ROOT+"dicePropSel.png", Config.IMAGE_ROOT+"dicePropSel.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['panGen'].connect( "clicked", self.openAlgoBox, 'pan' )
+ panBox.pack_start(self.GUI['panGen'], True, True, 5)
+ panBox.pack_start(self.GUI['panSlider'], True, True, 5)
+ panBox.pack_start(self.panLabel, False, padding=10)
+ self.controlsBox.pack_start(panBox)
+
+ reverbBox = RoundVBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ reverbBox.set_border_width(3)
+ reverbBox.set_radius(10)
+ self.reverbAdjust = gtk.Adjustment(0.1, 0, 1, 0.1, 0.1, 0)
+ self.GUI['reverbSlider'] = ImageVScale( Config.TAM_TAM_ROOT + "/Resources/Images/sliderEditVolume.png", self.reverbAdjust, 7 )
+ self.reverbAdjust.connect("value-changed", self.handleReverb)
+ self.GUI['reverbSlider'].set_snap( 0.1 )
+ self.GUI['reverbSlider'].set_inverted(True)
+ self.GUI['reverbSlider'].set_size_request(50, 200)
+ self.reverbLabel = gtk.Image()
+ self.handleReverb( self.reverbAdjust )
+ self.GUI['reverbGen'] = ImageToggleButton( Config.IMAGE_ROOT+"diceProp.png", Config.IMAGE_ROOT+"dicePropSel.png", Config.IMAGE_ROOT+"dicePropSel.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['reverbGen'].connect( "clicked", self.openAlgoBox, 'reverb' )
+ reverbBox.pack_start(self.GUI['reverbGen'], True, True, 5)
+ reverbBox.pack_start(self.GUI['reverbSlider'], True, True, 5)
+ reverbBox.pack_start(self.reverbLabel, False, padding=10)
+ self.controlsBox.pack_start(reverbBox)
+
+ attackBox = RoundVBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ attackBox.set_border_width(3)
+ attackBox.set_radius(10)
+ self.attackAdjust = gtk.Adjustment(0.04, 0.03, 1, .01, .01, 0)
+ self.GUI['attackSlider'] = ImageVScale( Config.TAM_TAM_ROOT + "/Resources/Images/sliderEditVolume.png", self.attackAdjust, 7 )
+ self.attackAdjust.connect("value-changed", self.handleAttack)
+ self.GUI['attackSlider'].set_snap( 0.01 )
+ self.GUI['attackSlider'].set_inverted(True)
+ self.GUI['attackSlider'].set_size_request(50, 200)
+ self.attackLabel = gtk.Image()
+ self.handleAttack( self.attackAdjust )
+ self.GUI['attackGen'] = ImageToggleButton( Config.IMAGE_ROOT+"diceProp.png", Config.IMAGE_ROOT+"dicePropSel.png", Config.IMAGE_ROOT+"dicePropSel.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['attackGen'].connect( "clicked", self.openAlgoBox, 'attack' )
+ attackBox.pack_start(self.GUI['attackGen'], True, True, 5)
+ attackBox.pack_start(self.GUI['attackSlider'], True, True, 5)
+ attackBox.pack_start(self.attackLabel, False, padding=10)
+ self.controlsBox.pack_start(attackBox)
+
+ decayBox = RoundVBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ decayBox.set_border_width(3)
+ decayBox.set_radius(10)
+ self.decayAdjust = gtk.Adjustment(0.31, 0.03, 1, .01, .01, 0)
+ self.GUI['decaySlider'] = ImageVScale( Config.TAM_TAM_ROOT + "/Resources/Images/sliderEditVolume.png", self.decayAdjust, 7 )
+ self.decayAdjust.connect("value-changed", self.handleDecay)
+ self.GUI['decaySlider'].set_snap( 0.01 )
+ self.GUI['decaySlider'].set_inverted(True)
+ self.GUI['decaySlider'].set_size_request(50, 200)
+ self.decayLabel = gtk.Image()
+ self.handleDecay( self.decayAdjust )
+ self.GUI['decayGen'] = ImageToggleButton( Config.IMAGE_ROOT+"diceProp.png", Config.IMAGE_ROOT+"dicePropSel.png", Config.IMAGE_ROOT+"dicePropSel.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['decayGen'].connect( "clicked", self.openAlgoBox, 'decay' )
+ decayBox.pack_start(self.GUI['decayGen'], True, True, 5)
+ decayBox.pack_start(self.GUI['decaySlider'], True, True, 5)
+ decayBox.pack_start(self.decayLabel, False, padding=10)
+ self.controlsBox.pack_start(decayBox)
+
+ filterBox = RoundHBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ filterBox.set_border_width(3)
+ filterBox.set_radius(10)
+
+ filterTypeBox = gtk.VBox()
+ self.GUI['filterTypeLowButton'] = ImageToggleButton(Config.IMAGE_ROOT + 'propLow3.png', Config.IMAGE_ROOT + 'propLow3Sel.png', Config.IMAGE_ROOT + 'propLow3Over.png')
+ self.GUI['filterTypeLowButton'].connect( "toggled", self.handleFilterType, 1 )
+ filterTypeBox.pack_start( self.GUI['filterTypeLowButton'] )
+ self.GUI['filterTypeHighButton'] = ImageToggleButton(Config.IMAGE_ROOT + 'propHi3.png', Config.IMAGE_ROOT + 'propHi3Sel.png', Config.IMAGE_ROOT + 'propHi3Over.png')
+ self.GUI['filterTypeHighButton'].connect( "toggled", self.handleFilterType, 2 )
+ filterTypeBox.pack_start( self.GUI['filterTypeHighButton'] )
+ self.GUI['filterTypeBandButton'] = gtk.ToggleButton( "B" )
+ self.GUI['filterTypeBandButton'] = ImageToggleButton(Config.IMAGE_ROOT + 'propBand3.png', Config.IMAGE_ROOT + 'propBand3Sel.png', Config.IMAGE_ROOT + 'propBand3Over.png')
+ self.GUI['filterTypeBandButton'].connect( "toggled", self.handleFilterType, 3 )
+ filterTypeBox.pack_start( self.GUI['filterTypeBandButton'] )
+ filterBox.pack_start( filterTypeBox )
+
+ self.filterSliderBox = gtk.VBox()
+ self.filterSliderBox.set_size_request(50, -1)
+ self.cutoffAdjust = gtk.Adjustment(1000, 100, 7000, 100, 100, 0)
+ self.GUI['cutoffSlider'] = ImageVScale( Config.TAM_TAM_ROOT + "/Resources/Images/sliderEditVolume.png", self.cutoffAdjust, 7 )
+ self.GUI['cutoffSlider'].set_snap(100)
+ self.cutoffAdjust.connect("value-changed", self.handleFilter)
+ self.GUI['cutoffSlider'].set_inverted(True)
+ self.GUI['cutoffSlider'].set_size_request(50, 200)
+ self.GUI['cutoffGen'] = ImageToggleButton( Config.IMAGE_ROOT+"diceProp.png", Config.IMAGE_ROOT+"dicePropSel.png", Config.IMAGE_ROOT+"dicePropSel.png", backgroundFill = Config.PANEL_COLOR )
+ self.GUI['cutoffGen'].connect( "clicked", self.openAlgoBox, 'cutoff' )
+ self.filterSliderBox.pack_start(self.GUI['cutoffGen'], True, True, 5)
+ self.filterSliderBox.pack_start(self.GUI['cutoffSlider'], True, True, 5)
+ self.filterLabel = gtk.Image()
+ self.filterLabel.set_from_file(Config.IMAGE_ROOT + 'propFilter1.png')
+ self.filterSliderBox.pack_start(self.filterLabel, False, padding=10)
+
+ filterBox.pack_start(self.filterSliderBox)
+
+ self.controlsBox.pack_start(filterBox)
+
+ self.algoBox = RoundVBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ self.algoBox.set_size_request( -1, 378 )
+ self.algoBox.set_border_width(3)
+ self.algoBox.set_radius(10)
+ #self.algoBox = gtk.VBox()
+
+ algoUpperBox = gtk.HBox()
+
+ algoRadioButtonBox = gtk.VBox()
+ algoRadioButtonBox.set_size_request(100, 150)
+ #algoRadioButtonBox = RoundHBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ #algoRadioButtonBox.set_border_width(3)
+ #algoRadioButtonBox.set_radius(10)
+
+ self.GUI['line'] = ImageRadioButton( None, Config.IMAGE_ROOT + 'propLine.png', Config.IMAGE_ROOT + 'propLineDown.png', Config.IMAGE_ROOT + 'propLineOver.png' )
+ self.GUI['line'].connect( "toggled", self.handleAlgo, 0 )
+ algoRadioButtonBox.pack_start( self.GUI['line'], False, False, 1 )
+ self.GUI['drunk'] = ImageRadioButton( self.GUI['line'], Config.IMAGE_ROOT + 'propDrunk.png', Config.IMAGE_ROOT + 'propDrunkDown.png', Config.IMAGE_ROOT + 'propDrunkOver.png' )
+ self.GUI['drunk'].connect( "toggled", self.handleAlgo, 1 )
+ algoRadioButtonBox.pack_start( self.GUI['drunk'], False, False, 1 )
+ self.GUI['droneJump'] = ImageRadioButton( self.GUI['line'], Config.IMAGE_ROOT + 'propDroneJump.png', Config.IMAGE_ROOT + 'propDroneJumpDown.png', Config.IMAGE_ROOT + 'propDroneJumpOver.png' )
+ self.GUI['droneJump'].connect( "toggled", self.handleAlgo, 2 )
+ algoRadioButtonBox.pack_start( self.GUI['droneJump'], False, False, 1 )
+ self.GUI['repeater'] = ImageRadioButton( self.GUI['line'], Config.IMAGE_ROOT + 'propRepeater.png', Config.IMAGE_ROOT + 'propRepeaterDown.png', Config.IMAGE_ROOT + 'propRepeaterOver.png' )
+ self.GUI['repeater'].connect( "toggled", self.handleAlgo, 3 )
+ algoRadioButtonBox.pack_start( self.GUI['repeater'], False, False, 1 )
+ self.GUI['loopseg'] = ImageRadioButton( self.GUI['line'], Config.IMAGE_ROOT + 'propLoopseg.png', Config.IMAGE_ROOT + 'propLoopsegDown.png', Config.IMAGE_ROOT + 'propLoopsegOver.png' )
+ self.GUI['loopseg'].connect( "toggled", self.handleAlgo, 4 )
+ algoRadioButtonBox.pack_start( self.GUI['loopseg'], False, False, 1 )
+
+ algoUpperBox.pack_start(algoRadioButtonBox)
+
+ algoSlidersBox = gtk.HBox()
+ algoSlidersBox.set_size_request(150, 320)
+ #algoSlidersBox = RoundHBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ #algoSlidersBox.set_border_width(3)
+ #algoSlidersBox.set_radius(10)
+ minBox = gtk.VBox()
+ self.minAdjust = gtk.Adjustment(0, 0, 100, 1, 1, 0)
+ self.GUI['minSlider'] = ImageVScale( Config.TAM_TAM_ROOT + "/Resources/Images/sliderEditVolume.png", self.minAdjust, 7 )
+ self.GUI['minSlider'].set_snap(1)
+ self.minAdjust.connect("value-changed", self.handleMin)
+ self.GUI['minSlider'].set_inverted(True)
+ self.GUI['minSlider'].set_size_request(50, 200)
+ minBox.pack_start(self.GUI['minSlider'], True, True, 5)
+ algoSlidersBox.pack_start(minBox)
+
+ maxBox = gtk.VBox()
+ self.maxAdjust = gtk.Adjustment(100, 0, 100, 1, 1, 0)
+ self.GUI['maxSlider'] = ImageVScale( Config.TAM_TAM_ROOT + "/Resources/Images/sliderEditVolume.png", self.maxAdjust, 7 )
+ self.GUI['maxSlider'].set_snap(1)
+ self.maxAdjust.connect("value-changed", self.handleMax)
+ self.GUI['maxSlider'].set_inverted(True)
+ self.GUI['maxSlider'].set_size_request(50, 200)
+ maxBox.pack_start(self.GUI['maxSlider'], True, True, 5)
+ algoSlidersBox.pack_start(maxBox)
+
+ paraBox = gtk.VBox()
+ self.paraAdjust = gtk.Adjustment(20, 0, 100, 1, 1, 0)
+ self.GUI['paraSlider'] = ImageVScale( Config.TAM_TAM_ROOT + "/Resources/Images/sliderEditVolume.png", self.paraAdjust, 7 )
+ self.GUI['paraSlider'].set_snap(1)
+ self.paraAdjust.connect("value-changed", self.handlePara)
+ self.GUI['paraSlider'].set_inverted(True)
+ self.GUI['paraSlider'].set_size_request(50, 200)
+ paraBox.pack_start(self.GUI['paraSlider'], True, True, 5)
+ algoSlidersBox.pack_start(paraBox)
+
+ algoUpperBox.pack_start(algoSlidersBox)
+
+ self.algoBox.pack_start(algoUpperBox)
+
+ #transButtonBox = RoundHBox(fillcolor=Config.PANEL_COLOR, bordercolor=Config.INST_BCK_COLOR)
+ #transButtonBox.set_border_width(3)
+ #transButtonBox.set_radius(10)
+ transButtonBox = gtk.HBox()
+ transButtonBox.set_size_request(150, 50)
+
+ # create cancel/check button
+ self.GUI["checkButton"] = ImageButton(Config.IMAGE_ROOT + 'check.png', backgroundFill=Config.PANEL_COLOR )
+ self.GUI["checkButton"].connect("clicked", self.apply)
+
+ self.GUI["cancelButton"] = ImageButton(Config.IMAGE_ROOT + 'closeA.png', backgroundFill=Config.PANEL_COLOR )
+ self.GUI["cancelButton"].connect("clicked", self.cancel)
+
+ transButtonBox.pack_end(self.GUI["checkButton"], False, False, 10)
+ transButtonBox.pack_end(self.GUI["cancelButton"], False, False)
+ self.algoBox.pack_start(transButtonBox)
+
+ self.fixed.put( self.controlsBox, 0, 0 )
+ self.algoBox.show_all()
+
+ # set tooltips
+ for key in self.GUI:
+ if Tooltips.PROP.has_key(key):
+ self.tooltips.set_tip(self.GUI[key],Tooltips.PROP[key])
+ self.tooltips.set_tip(self.GUI['paraSlider'], 'Random')
+
+
+ self.show_all()
+
+ def openAlgoBox( self, widget, data=None ):
+ if widget.get_active() == True:
+ self.property = data
+ if self.activeWidget:
+ self.activeWidget.set_active(False)
+ self.activeWidget = widget
+ if self.context == "page":
+ if self.algoBox.parent == None: self.fixed.put( self.algoBox, 671, 0 )
+ else: self.fixed.move( self.algoBox, 671, 0 )
+ self.popup.resize( 927, 378 )
+ else:
+ self.popup.resize( 801, 378 )
+ if self.algoBox.parent == None: self.fixed.put( self.algoBox, 545, 0 )
+ else: self.fixed.move( self.algoBox, 545, 0 )
+ else:
+ self.property = None
+ self.activeWidget = None
+ if self.algoBox.parent != None:
+ if self.context == "page": self.popup.resize( 671, 378 )
+ else: self.popup.resize( 545, 378 )
+ self.fixed.remove( self.algoBox )
+
+ def setContext( self, context, scale, pageIds = None, trackIds = None, notes = {} ):
+ self.context = context
+ self.scale = GenerationConstants.SCALES[scale]
+ self.notes = {}
+ self.pageIds = pageIds
+ self.trackIds = trackIds
+
+ try:
+ self.activeWidget.set_active(False)
+ self.activeWidget = None
+ except:
+ self.activeWidget = None
+
+ if context == "page":
+ if self.pageBox.parent == None:
+ self.controlsBox.pack_start( self.pageBox )
+ self.controlsBox.reorder_child( self.pageBox, 0 )
+ self.controlsBox.set_size_request( 671, 378 )
+ self.popup.resize( 671, 378 )
+ self.trackIds = [0,1,2,3,4]
+ for p in pageIds:
+ self.notes[p] = {}
+ for t in range(Config.NUMBER_OF_TRACKS):
+ self.notes[p][t] = self.noteDB.getNotesByTrack( p, t )
+ page = self.noteDB.getPage(pageIds[0])
+ self.beatAdjust.set_value(page.beats)
+ btn = "color%dButton" % page.color
+ self.GUI[btn].set_active(True)
+ elif context == "track":
+ if self.pageBox.parent != None:
+ self.controlsBox.remove( self.pageBox )
+ self.controlsBox.set_size_request( 545, 378 )
+ self.popup.resize( 545, 378 )
+ for p in pageIds:
+ self.notes[p] = {}
+ for t in trackIds:
+ self.notes[p][t] = self.noteDB.getNotesByTrack( p, t )
+ else:
+ if self.pageBox.parent != None:
+ self.controlsBox.remove( self.pageBox )
+ self.controlsBox.set_size_request( 545, 378 )
+ self.popup.resize( 545, 378 )
+ self.notes = notes
+ self.pageIds = self.notes.keys()
+ self.trackIds = self.notes[self.pageIds[0]].keys()
+
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ # initialize values from first note
+ self.setup = True
+ n = self.notes[p][t][0]
+ self.panAdjust.set_value( n.cs.pan )
+ self.reverbAdjust.set_value( n.cs.reverbSend )
+ self.attackAdjust.set_value( n.cs.attack )
+ self.decayAdjust.set_value( n.cs.decay )
+ if n.cs.filterType == 0:
+ self.GUI['filterTypeLowButton'].set_active(False)
+ self.GUI['filterTypeHighButton'].set_active(False)
+ self.GUI['filterTypeBandButton'].set_active(False)
+ self.filterLabel.hide()
+ self.GUI['cutoffSlider'].hide()
+ self.GUI['cutoffGen'].hide()
+ else:
+ if n.cs.filterType == 1:
+ self.GUI['filterTypeLowButton'].set_active(True)
+ if n.cs.filterType == 2:
+ self.GUI['filterTypeHighButton'].set_active(True)
+ if n.cs.filterType == 3:
+ self.GUI['filterTypeBandButton'].set_active(True)
+ self.filterLabel.show()
+ self.GUI['cutoffSlider'].show()
+ self.GUI['cutoffGen'].show()
+ self.filterType = n.cs.filterType
+ self.cutoffAdjust.set_value( n.cs.filterCutoff )
+ self.setup = False
+ return
+
+ def handleColor( self, widget, index ):
+ stream = []
+ for page in self.pageIds:
+ stream += [ page, index ]
+ if len(stream):
+ self.noteDB.updatePages( [ PARAMETER.PAGE_COLOR, len(stream)//2 ] + stream )
+
+ def updateBeatLabel( self, adjust ):
+ beats = int(adjust.value)
+ self.beatLabel.set_from_file(Config.IMAGE_ROOT + 'propBeats' + str(beats) + '.png')
+
+ def handleBeat( self, widget, signal_id ):
+ beats = int(widget.get_adjustment().value)
+ stream = []
+ for page in self.pageIds:
+ stream += [ page, beats ]
+ if len(stream):
+ self.noteDB.updatePages( [ PARAMETER.PAGE_BEATS, len(stream)//2 ] + stream )
+
+
+ def stepPitch( self, step ):
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ substream = []
+ if step > 0:
+ if t != Config.NUMBER_OF_TRACKS-1: # regular note
+ for n in self.notes[p][t]:
+ if n.cs.pitch != Config.MAXIMUM_PITCH:
+ substream += [ n.id, min( Config.MAXIMUM_PITCH, n.cs.pitch + step ) ]
+ else: # drum note
+ for n in self.notes[p][t]:
+ if n.cs.pitch != Config.MAXIMUM_PITCH_DRUM:
+ substream += [ n.id, min( Config.MAXIMUM_PITCH_DRUM, n.cs.pitch + step*Config.PITCH_STEP_DRUM ) ]
+ else:
+ if t != Config.NUMBER_OF_TRACKS-1: # regular note
+ for n in self.notes[p][t]:
+ if n.cs.pitch != Config.MINIMUM_PITCH:
+ substream += [ n.id, max( Config.MINIMUM_PITCH, n.cs.pitch + step ) ]
+ else: # drum note
+ for n in self.notes[p][t]:
+ if n.cs.pitch != Config.MINIMUM_PITCH_DRUM:
+ substream += [ n.id, max( Config.MINIMUM_PITCH_DRUM, n.cs.pitch + step*Config.PITCH_STEP_DRUM ) ]
+ if len(substream):
+ stream += [ p, t, PARAMETER.PITCH, len(substream)//2 ] + substream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def algoPitch( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ scaleLength = len(self.scale)-1
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ if self.trackIds[t] != Config.NUMBER_OF_TRACKS-1:
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, self.scale[int(val*0.01*scaleLength)]+36 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.PITCH, len(substream)//2 ] + substream
+ else:
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ val = int((val*0.12)*2+24)
+ if val in GenerationConstants.DRUMPITCH.keys():
+ val = GenerationConstants.DRUMPITCH[val]
+ substream += [ n.id, val ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.PITCH, len(substream)//2 ] + substream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def stepVolume( self, step ):
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ substream = []
+ if step > 0:
+ for n in self.notes[p][t]:
+ if n.cs.amplitude != Config.MAXIMUM_AMPLITUDE:
+ substream += [ n.id, min( Config.MAXIMUM_AMPLITUDE, n.cs.amplitude + step ) ]
+ else:
+ for n in self.notes[p][t]:
+ if n.cs.amplitude != Config.MINIMUM_AMPLITUDE:
+ substream += [ n.id, max( Config.MINIMUM_AMPLITUDE, n.cs.amplitude + step ) ]
+ if len(substream):
+ stream += [ p, t, PARAMETER.AMPLITUDE, len(substream)//2 ] + substream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def algoVolume( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, min( Config.MAXIMUM_AMPLITUDE, val*0.01 ) ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.AMPLITUDE, len(substream)//2 ] + substream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def handlePan( self, adjust ):
+ img = min( 4, int(adjust.value * 5) )
+ self.panLabel.set_from_file(Config.IMAGE_ROOT + 'propPan' + str(img) + '.png')
+ if not self.setup:
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ stream += [ p, t, PARAMETER.PAN, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ stream += [ n.id, adjust.value ]
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def algoPan( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, val*0.01 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.PAN, len(substream)//2 ] + substream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def handleReverb( self, adjust ):
+ img = min( 5, int(adjust.value * 6) )
+ self.reverbLabel.set_from_file(Config.IMAGE_ROOT + 'propReverb' + str(img) + '.png')
+ if not self.setup:
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ stream += [ p, t, PARAMETER.REVERB, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ stream += [ n.id, adjust.value ]
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def algoReverb( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, val*0.02 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.REVERB, len(substream)//2 ] + substream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def handleAttack( self, adjust ):
+ val = adjust.value #*adjust.value
+ img = min( 4, int(val * 4) )
+ self.attackLabel.set_from_file(Config.IMAGE_ROOT + 'propAtt' + str(img) + '.png')
+ if not self.setup:
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ stream += [ p, t, PARAMETER.ATTACK, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ stream += [ n.id, adjust.value ]
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def algoAttack( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, val*0.01 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.ATTACK, len(substream)//2 ] + substream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def handleDecay( self, adjust ):
+ val = adjust.value #*adjust.value
+ img = min( 4, int(val * 4) )
+ self.decayLabel.set_from_file(Config.IMAGE_ROOT + 'propDec' + str(img) + '.png')
+ if not self.setup:
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ stream += [ p, t, PARAMETER.DECAY, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ stream += [ n.id, adjust.value ]
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def algoDecay( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, val*0.01 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.DECAY, len(substream)//2 ] + substream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def handleFilterType( self, widget, type ):
+
+ if widget.get_active():
+ if self.filterType == 0:
+ self.filterLabel.show()
+ self.GUI['cutoffSlider'].show()
+ self.GUI['cutoffGen'].show()
+
+ self.filterType = type
+ self.updateFilterLabel()
+
+ if widget != self.GUI['filterTypeLowButton'] and self.GUI['filterTypeLowButton'].get_active():
+ self.GUI['filterTypeLowButton'].set_active( False )
+ if widget != self.GUI['filterTypeBandButton'] and self.GUI['filterTypeBandButton'].get_active():
+ self.GUI['filterTypeBandButton'].set_active( False )
+ if widget != self.GUI['filterTypeHighButton'] and self.GUI['filterTypeHighButton'].get_active():
+ self.GUI['filterTypeHighButton'].set_active( False )
+ if not self.setup:
+ typestream = []
+ cutoffstream = []
+ cutoff = self.cutoffAdjust.value
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ substream = []
+ typestream += [ p, t, PARAMETER.FILTERTYPE, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ typestream += [ n.id, type ]
+ if n.cs.filterCutoff != cutoff:
+ substream += [ n.id, cutoff ]
+ if len(substream):
+ cutoffstream += [ p, t, PARAMETER.FILTERCUTOFF, len(substream)//2 ] + substream
+ if len(typestream):
+ self.noteDB.updateNotes( typestream + [-1] )
+ if len(cutoffstream):
+ self.noteDB.updateNotes( cutoffstream + [-1] )
+
+ elif type == self.filterType:
+ self.filterType = 0
+ self.filterLabel.hide()
+ self.GUI['cutoffSlider'].hide()
+ self.GUI['cutoffGen'].hide()
+ if not self.setup:
+ typestream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ typestream += [ p, t, PARAMETER.FILTERTYPE, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ typestream += [ n.id, 0 ]
+ if len(typestream):
+ self.noteDB.updateNotes( typestream + [-1] )
+
+ def handleFilter( self, adjust ):
+ stream = []
+ for p in self.notes:
+ for t in self.notes[p]:
+ if len(self.notes[p][t]):
+ stream += [ p, t, PARAMETER.FILTERCUTOFF, len(self.notes[p][t]) ]
+ for n in self.notes[p][t]:
+ stream += [ n.id, adjust.value ]
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def algoCutoff( self, list, algorithm ):
+ maxValue = max(list[0], list[1])
+ stream = []
+ for t in range(len(self.trackIds)):
+ trackLength = 0
+ for p in range(len(self.pageIds)):
+ trackLength += len(self.notes[self.pageIds[p]][self.trackIds[t]])
+ algorithm.__init__(list[0], list[1], trackLength)
+ for p in range(len(self.pageIds)):
+ substream = []
+ for n in self.notes[self.pageIds[p]][self.trackIds[t]]:
+ val = algorithm.getNextValue(list[2], maxValue)
+ substream += [ n.id, val*70+100 ]
+ if len(substream):
+ stream += [ self.pageIds[p], self.trackIds[t], PARAMETER.FILTERCUTOFF, len(substream)//2 ] + substream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ self.updateFilterLabel()
+
+ def handleAlgo( self, widget, data ):
+ self.algorithm = self.algoTypes[data]
+ paraTooltips = ['Random', 'Maximum step', 'Maximum step', 'Maximum step', 'Maximum step']
+ self.tooltips.set_tip(self.GUI['paraSlider'], paraTooltips[data])
+
+ def handleMin( self, adjust ):
+ self.minValue = adjust.value
+
+ def handleMax( self, adjust ):
+ self.maxValue = adjust.value
+
+ def handlePara( self, adjust ):
+ self.paraValue = adjust.value
+
+ def apply( self, widget, data=None ):
+ valList = [self.minValue, self.maxValue, self.paraValue]
+ if self.property == 'pitch':
+ self.algoPitch(valList, self.algorithm)
+ elif self.property == 'volume':
+ self.algoVolume(valList, self.algorithm)
+ elif self.property == 'pan':
+ self.algoPan(valList, self.algorithm)
+ elif self.property == 'reverb':
+ self.algoReverb(valList, self.algorithm)
+ elif self.property == 'attack':
+ self.algoAttack(valList, self.algorithm)
+ elif self.property == 'decay':
+ self.algoDecay(valList, self.algorithm)
+ elif self.property == 'cutoff':
+ self.algoCutoff(valList, self.algorithm)
+ self.cancel(self.activeWidget)
+
+ def cancel( self, widget, data=None ):
+ self.activeWidget.set_active(False)
+
+ def updateFilterLabel( self ):
+ val = (self.cutoffAdjust.value-self.cutoffAdjust.lower)/(self.cutoffAdjust.upper-self.cutoffAdjust.lower)
+ img = min( 5, int(val * 6) )
+ self.filterLabel.set_from_file(Config.IMAGE_ROOT + 'propFilter%d.%d' % (self.filterType, img) + '.png')
+
+
+
+
+
+
diff --git a/TamTamEdit.activity/Edit/TrackInterface.py b/TamTamEdit.activity/Edit/TrackInterface.py
new file mode 100644
index 0000000..b8a3a27
--- /dev/null
+++ b/TamTamEdit.activity/Edit/TrackInterface.py
@@ -0,0 +1,1364 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import gobject
+
+from math import floor
+import time
+
+import Config
+from Edit.NoteInterface import NoteInterface
+from Edit.HitInterface import HitInterface
+from Edit.MainWindow import CONTEXT
+
+from Util.NoteDB import PARAMETER
+from Util.CSoundNote import CSoundNote
+from Generation.GenerationConstants import GenerationConstants
+from Util.Profiler import TP
+
+class SELECTNOTES:
+ ALL = -1
+ NONE = 0
+ ADD = 1
+ REMOVE = 2
+ FLIP = 3
+ EXCLUSIVE = 4
+
+class INTERFACEMODE:
+ DEFAULT = 0
+ DRAW = 1
+ PASTE_NOTES = 2
+ PASTE_TRACKS = 3
+ PAINT = 4
+
+class TrackInterfaceParasite:
+ def __init__( self, noteDB, owner, note ):
+ if note.track == Config.NUMBER_OF_TRACKS-1: # drum track
+ self.parasite = HitInterface( noteDB, owner, note )
+ else:
+ self.parasite = NoteInterface( noteDB, owner, note )
+
+ def attach( self ):
+ return self.parasite
+
+class TrackInterface( gtk.EventBox ):
+
+ def __init__( self, noteDB, owner, getScaleFunction ):
+ gtk.EventBox.__init__( self )
+
+ self.noteDB = noteDB
+ self.owner = owner
+ self.getScale = getScaleFunction
+
+ self.drawingArea = gtk.DrawingArea()
+ self.drawingAreaDirty = False # are we waiting to draw?
+ self.add( self.drawingArea )
+ self.dirtyRectToAdd = gtk.gdk.Rectangle() # used by the invalidate_rect function
+
+ self.fullWidth = 1 # store the maximum allowed width
+ self.width = 1
+ self.height = 1
+
+ self.interfaceMode = INTERFACEMODE.DEFAULT
+
+ self.curPage = -1 # this isn't a real page at all!
+ self.curBeats = 4
+ self.painting = False
+ self.pointerGrid = 1
+ self.drawGrid = Config.DEFAULT_GRID
+ self.paintGrid = Config.DEFAULT_GRID
+ self.paintNoteDur = Config.DEFAULT_GRID
+
+ self.selectedNotes = [ [] for i in range(Config.NUMBER_OF_TRACKS) ]
+
+ self.curAction = False # stores the current mouse action
+ self.curActionObject = False # stores the object that in handling the action
+
+ self.lastDO = self.lastDP = self.lastDrumDP = self.lastDD = None
+
+ self.clickButton = 0 # used in release and motion events to make sure we where actually the widget originally clicked. (hack for popup windows)
+ self.buttonPressCount = 1 # used on release events to indicate double/triple releases
+ self.clickLoc = [0,0] # location of the last click
+ self.marqueeLoc = False # current drag location of the marquee
+ self.marqueeRect = [[0,0],[0,0]]
+
+ self.pasteTick = -1
+ self.pasteTrack = -1
+ self.pasteRect = False
+
+ self.playheadT = 0
+ self.playheadX = Config.TRACK_SPACING_DIV2
+
+ self.cursor = { \
+ "default": None, \
+ "drag-onset": gtk.gdk.Cursor(gtk.gdk.SB_RIGHT_ARROW), \
+ "drag-pitch": gtk.gdk.Cursor(gtk.gdk.BOTTOM_SIDE), \
+ "drag-duration": gtk.gdk.Cursor(gtk.gdk.RIGHT_SIDE), \
+ "drag-playhead": gtk.gdk.Cursor(gtk.gdk.SB_H_DOUBLE_ARROW), \
+ "pencil": gtk.gdk.Cursor(gtk.gdk.PENCIL), \
+ "paste": gtk.gdk.Cursor(gtk.gdk.CENTER_PTR), \
+ "error": None }
+
+ self.add_events(gtk.gdk.POINTER_MOTION_MASK|gtk.gdk.POINTER_MOTION_HINT_MASK)
+
+ self.connect( "size-allocate", self.size_allocate )
+
+ self.drawingArea.connect( "expose-event", self.expose )
+ self.connect( "button-press-event", self.handleButtonPress )
+ self.connect( "button-release-event", self.handleButtonRelease )
+ self.connect( "motion-notify-event", self.handleMotion )
+
+ # prepare drawing stuff
+ hexToInt = { "0":0, "1":1, "2":2, "3":3, "4":4, "5":5, "6":6, "7":7, "8":8, "9":9, "A":10, "B":11, "C":12, "D":13, "E":14, "F":15, "a":10, "b":11, "c":12, "d":13, "e":14, "f":15 }
+ self.trackColors = []
+ for i in Config.TRACK_COLORS:
+ low = ( 256*(hexToInt[i[0][1]]*16+hexToInt[i[0][2]]), 256*(hexToInt[i[0][3]]*16+hexToInt[i[0][4]]), 256*(hexToInt[i[0][5]]*16+hexToInt[i[0][6]]) )
+ high = ( 256*(hexToInt[i[1][1]]*16+hexToInt[i[1][2]]), 256*(hexToInt[i[1][3]]*16+hexToInt[i[1][4]]), 256*(hexToInt[i[1][5]]*16+hexToInt[i[1][6]]) )
+ delta = ( high[0]-low[0], high[1]-low[1], high[2]-low[2] )
+ self.trackColors.append( (low, delta) )
+
+ colormap = self.drawingArea.get_colormap()
+ self.beatColor = colormap.alloc_color( Config.BEAT_COLOR, True, True )
+ self.playheadColor = colormap.alloc_color( Config.PLAYHEAD_COLOR, True, True )
+ self.marqueeColor = colormap.alloc_color( Config.MARQUEE_COLOR, True, True )
+
+ self.image = {}
+ win = gtk.gdk.get_default_root_window()
+ self.gc = gtk.gdk.GC( win )
+
+ def prepareDrawable( name ):
+ pix = gtk.gdk.pixbuf_new_from_file( Config.IMAGE_ROOT+name+".png" )
+ self.image[name] = gtk.gdk.Pixmap( win, pix.get_width(), pix.get_height() )
+ self.image[name].draw_pixbuf( self.gc, pix, 0, 0, 0, 0, pix.get_width(), pix.get_height(), gtk.gdk.RGB_DITHER_NONE )
+ def preparePixbuf( name ):
+ self.image[name] = gtk.gdk.pixbuf_new_from_file( Config.IMAGE_ROOT+name+".png" )
+
+ prepareDrawable( "trackBG" )
+ prepareDrawable( "trackBGSelected" )
+ prepareDrawable( "trackBGDrum" )
+ prepareDrawable( "trackBGDrumSelected" )
+ preparePixbuf( "note" )
+ preparePixbuf( "noteSelected" )
+ preparePixbuf( "hit" )
+ preparePixbuf( "hitSelected" )
+
+ # define dimensions
+ self.width = self.trackFullWidth = self.image["trackBG"].get_size()[0]
+ self.trackWidth = self.width - Config.TRACK_SPACING
+ self.trackFullHeight = self.image["trackBG"].get_size()[1]
+ self.trackHeight = self.trackFullHeight - Config.TRACK_SPACING
+ self.trackFullHeightDrum = self.image["trackBGDrum"].get_size()[1]
+ self.trackHeightDrum = self.trackFullHeightDrum - Config.TRACK_SPACING
+ self.height = self.trackHeight*(Config.NUMBER_OF_TRACKS-1) + self.trackHeightDrum + Config.TRACK_SPACING*Config.NUMBER_OF_TRACKS
+ self.trackLimits = []
+ self.trackRect = []
+ self.drumIndex = Config.NUMBER_OF_TRACKS-1
+ for i in range(self.drumIndex):
+ start = i*(self.trackFullHeight)
+ self.trackLimits.append( (start,start+self.trackFullHeight) )
+ self.trackRect.append( gtk.gdk.Rectangle(Config.TRACK_SPACING_DIV2,start+Config.TRACK_SPACING_DIV2, self.trackWidth, self.trackHeight ) )
+ self.trackLimits.append( ( self.height - self.trackFullHeightDrum, self.height ) )
+ self.trackRect.append( gtk.gdk.Rectangle( Config.TRACK_SPACING_DIV2, self.height - self.trackFullHeightDrum + Config.TRACK_SPACING_DIV2, self.trackWidth, self.trackHeightDrum ) )
+
+ self.pitchPerPixel = float(Config.NUMBER_OF_POSSIBLE_PITCHES-1) / (self.trackHeight - Config.NOTE_HEIGHT)
+ self.pixelsPerPitch = float(self.trackHeight-Config.NOTE_HEIGHT)/(Config.MAXIMUM_PITCH - Config.MINIMUM_PITCH)
+ self.pitchPerPixelDrum = float(Config.NUMBER_OF_POSSIBLE_PITCHES_DRUM-1)*Config.PITCH_STEP_DRUM / (self.trackHeightDrum - Config.HIT_HEIGHT)
+ self.pixelsPerPitchDrum = float(self.trackHeightDrum-Config.HIT_HEIGHT)/(Config.MAXIMUM_PITCH_DRUM - Config.MINIMUM_PITCH_DRUM )
+
+ self.pixelsPerTick = [0] + [ self.trackWidth/float(i*Config.TICKS_PER_BEAT) for i in range(1,Config.MAXIMUM_BEATS+1) ]
+
+ self.ticksPerPixel = [0] + [ 1.0/self.pixelsPerTick[i] for i in range(1,Config.MAXIMUM_BEATS+1) ]
+
+ self.beatSpacing = [[0]]
+ for i in range(1,Config.MAXIMUM_BEATS+1):
+ self.beatSpacing.append( [ self.ticksToPixels( i, Config.TICKS_PER_BEAT*j ) for j in range(i) ] )
+
+ # screen buffers
+ self.screenBuf = [ gtk.gdk.Pixmap( win, self.width, self.height ), \
+ gtk.gdk.Pixmap( win, self.width, self.height ) ]
+ self.screenBufPage = [ -1, -1 ]
+ self.screenBufBeats = [ -1, -1 ]
+ self.screenBufDirtyRect = [ gtk.gdk.Rectangle(), gtk.gdk.Rectangle() ]
+ self.screenBufDirty = [ False, False ]
+ self.screenBufResume = [ [0,0], [0,0] ] # allows for stopping and restarting in the middle of a draw
+ self.curScreen = 0
+ self.preScreen = 1
+ self.predrawTimeout = False
+
+ #-- private --------------------------------------------
+
+ def _updateClipboardArea( self ):
+ self.clipboardArea = self.owner.getClipboardArea( self.curPage )
+ self.clipboardTrackTop = 0
+ for t in range(self.drumIndex):
+ if self.clipboardArea["tracks"][t]: break
+ self.clipboardTrackTop += 1
+ self.clipboardDrumTrack = self.clipboardArea["tracks"][self.drumIndex]
+
+ #=======================================================
+ # NoteDB notifications
+
+ def notifyPageAdd( self, id, at ):
+ return
+
+ def notifyPageDelete( self, which, safe ):
+ if self.screenBufPage[self.preScreen] in which:
+ self.screenBufPage[self.preScreen] = -1
+
+ def notifyPageDuplicate( self, new, at ):
+ return
+
+ def notifyPageMove( self, which, low, high ):
+ return
+
+ def notifyPageUpdate( self, page, parameter, value ):
+ if parameter == PARAMETER.PAGE_BEATS:
+ notes = self.noteDB.getNotesByPage( page, self )
+ for note in notes:
+ note.updateTransform()
+
+ if page == self.screenBufPage[self.curScreen]:
+ self.screenBufBeats[self.curScreen] = value
+ self.curBeats = value
+ if self.playheadT >= value*Config.TICKS_PER_BEAT:
+ self.playheadT = value*Config.TICKS_PER_BEAT - 1
+ self.playheadX = self.ticksToPixels( self.curBeats, self.playheadT ) + Config.TRACK_SPACING_DIV2
+ self.invalidate_rect( 0, 0, self.width, self.height, page )
+ if page == self.screenBufPage[self.preScreen]:
+ self.screenBufBeats[self.preScreen] = value
+ self.invalidate_rect( 0, 0, self.width, self.height, page )
+ self.predrawPage()
+
+
+ #=======================================================
+ # Module Interface
+
+ def getDrawingPackage( self, track ):
+ if track == self.drumIndex:
+ return ( self.image["hit"], self.image["hitSelected"], self.drawingArea.get_colormap(), self.trackColors[track] )
+ else:
+ return ( self.image["note"], self.image["noteSelected"], self.drawingArea.get_colormap(), self.trackColors[track] )
+
+ def getActivePages( self ):
+ return self.screenBufPage
+
+ def setPredrawPage( self, page ):
+ if self.screenBufPage[self.preScreen] != page:
+ self.screenBufPage[self.preScreen] = page
+ self.screenBufBeats[self.preScreen] = self.noteDB.getPage(page).beats
+ self.invalidate_rect( 0, 0, self.width, self.height, page )
+ return True
+ return False
+
+ def predrawPage( self ):
+ if self.screenBufPage[self.preScreen] == -1: return True # no page to predraw
+ if not self.predrawTimeout:
+ self.predrawTimeout = gobject.timeout_add( 50, self._predrawTimeout )
+
+ def abortPredrawPage( self ):
+ if self.predrawTimeout:
+ gobject.source_remove( self.predrawTimeout )
+ self.predrawTimeout = False
+
+ def _predrawTimeout( self ):
+ if self.preScreen == -1: return False # no page to predraw
+ if self.draw( self.preScreen, False, time.time() + 0.020 ): # 20 ms time limit
+ self.predrawTimeout = False
+ return False
+ return True
+
+
+
+ def displayPage( self, page, predraw = -1 ):
+ if page == self.curPage:
+ if predraw >= 0 and self.screenBufPage[self.preScreen] != predraw:
+ self.screenBufPage[self.preScreen] = predraw
+ self.screenBufBeats[self.preScreen] = self.noteDB.getPage(predraw).beats
+ self.invalidate_rect( 0, 0, self.width, self.height, predraw )
+ return
+
+ if self.curPage >= 0 and self.curPage != page: clearNotes = True
+ else: clearNotes = False
+
+ oldPage = self.curPage
+ self.curPage = page
+ self.curBeats = self.noteDB.getPage(page).beats
+
+ if self.screenBufPage[self.preScreen] == self.curPage: # we predrew this page, so smart!
+ t = self.preScreen
+ self.preScreen = self.curScreen
+ self.curScreen = t
+ self.invalidate_rect( 0, 0, self.width, self.height, self.curPage, False )
+ else: # we need to draw this page from scratch
+ self.screenBufPage[self.curScreen] = self.curPage
+ self.screenBufBeats[self.curScreen] = self.curBeats
+ self.invalidate_rect( 0, 0, self.width, self.height, self.curPage )
+
+ if predraw >= 0 and self.screenBufPage[self.preScreen] != predraw:
+ self.screenBufPage[self.preScreen] = predraw
+ self.screenBufBeats[self.preScreen] = self.noteDB.getPage(predraw).beats
+ self.invalidate_rect( 0, 0, self.width, self.height, predraw )
+
+ if clearNotes: # clear the notes now that we've sorted out the screen buffers
+ self.clearSelectedNotes( oldPage )
+
+ if self.curAction == "paste":
+ self._updateClipboardArea()
+
+ def getPlayhead( self ):
+ return self.playheadT
+
+ def setPlayhead( self, ticks ):
+ if self.playheadT != ticks:
+ self.invalidate_rect( self.playheadX-Config.PLAYHEAD_SIZE/2, 0, Config.PLAYHEAD_SIZE, self.height, self.curPage, False )
+ self.playheadX = self.ticksToPixels( self.curBeats, ticks ) + Config.TRACK_SPACING_DIV2
+ self.invalidate_rect( self.playheadX-Config.PLAYHEAD_SIZE/2, 0, Config.PLAYHEAD_SIZE, self.height, self.curPage, False )
+ self.playheadT = ticks
+
+ def setInterfaceMode( self, mode ):
+ self.doneCurrentAction()
+
+ if mode == "tool":
+ mode = self.owner.getTool()
+
+ if mode == "draw":
+ self.interfaceMode = INTERFACEMODE.DRAW
+ elif mode == "paint":
+ self.interfaceMode = INTERFACEMODE.PAINT
+ elif mode == "paste_notes":
+ self.interfaceMode = INTERFACEMODE.PASTE_NOTES
+ self.setCurrentAction("paste", self)
+ elif mode == "paste_tracks":
+ self.interfaceMode = INTERFACEMODE.PASTE_TRACKS
+ self.setCurrentAction("paste", self )
+ else:
+ self.interfaceMode = INTERFACEMODE.DEFAULT
+
+ def getSelectedNotes( self ):
+ ids = []
+ for t in range(Config.NUMBER_OF_TRACKS):
+ ids.append( [ n.note.id for n in self.selectedNotes[t] ] )
+ return ids
+
+ #=======================================================
+ # Event Callbacks
+
+ def size_allocate( self, widget, allocation ):
+ self.alloc = allocation
+ width = allocation.width
+ height = allocation.height
+
+ self.drawingArea.set_size_request( width, height )
+
+ if self.window != None:
+ self.invalidate_rect( 0, 0, width, height, self.curPage, False )
+
+ def setPointerGrid(self, value):
+ self.pointerGrid = value
+
+ def setDrawGrid(self, value):
+ self.drawGrid = value
+
+ def setPaintGrid(self, value):
+ self.paintGrid = value
+
+ def setPaintNoteDur(self, value):
+ self.paintNoteDur = value
+
+ def handleButtonPress( self, widget, event ):
+
+ TP.ProfileBegin( "TI::handleButtonPress" )
+
+ self.clickButton = event.button
+
+ if event.type == gtk.gdk._2BUTTON_PRESS: self.buttonPressCount = 2
+ elif event.type == gtk.gdk._3BUTTON_PRESS: self.buttonPressCount = 3
+ else: self.buttonPressCount = 1
+
+ self.clickLoc = [ int(event.x), int(event.y) ]
+
+
+ if self.curAction == "paste":
+ self.doPaste()
+ self.setCurrentAction("block-track-select")
+ TP.ProfileEnd( "TI::handleButtonPress" )
+ return
+
+
+ # check if we clicked on the playhead
+ if event.x >= self.playheadX and event.x <= self.playheadX + Config.PLAYHEAD_SIZE:
+ self.setCurrentAction( "playhead-drag", self )
+ TP.ProfileEnd( "TI::handleButtonPress" )
+ return
+
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackLimits[i][0] > event.y: break
+ if self.trackLimits[i][1] < event.y: continue
+
+ handled = 0
+ notes = self.noteDB.getNotesByTrack( self.curPage, i, self )
+ last = len(notes)-1
+ for n in range(last+1):
+ if i == self.drumIndex and n < last: # check to see if the next hit overlaps this one
+ if notes[n].getStartTick() == notes[n+1].getStartTick() and notes[n].getPitch() == notes[n+1].getPitch():
+ continue
+ handled = notes[n].handleButtonPress( self, event )
+ if handled == 0:
+ continue
+ elif handled == 1:
+ if not self.curAction: self.curAction = True # it was handled but no action was declared, set curAction to True anyway
+ TP.ProfileEnd( "TI::handleButtonPress" )
+ return
+ else: # all other options mean we can stop looking
+ break
+
+ if self.interfaceMode == INTERFACEMODE.DRAW:
+ if not handled or handled == -1: # event didn't overlap any notes, so we can draw
+ if i == self.drumIndex: pitch = min( self.pixelsToPitchDrumFloor( self.clickLoc[1] - self.trackLimits[i][1] + Config.HIT_HEIGHT//2 )//Config.PITCH_STEP_DRUM, Config.NUMBER_OF_POSSIBLE_PITCHES_DRUM-1)*Config.PITCH_STEP_DRUM + Config.MINIMUM_PITCH_DRUM
+ else: pitch = min( self.pixelsToPitchFloor( self.clickLoc[1] - self.trackLimits[i][1] + Config.NOTE_HEIGHT//2 ), Config.NUMBER_OF_POSSIBLE_PITCHES-1) + Config.MINIMUM_PITCH
+ onset = self.pixelsToTicksFloor( self.curBeats, self.clickLoc[0] - self.trackRect[i].x)
+ snapOnset = self.drawGrid * int(onset / float(self.drawGrid) + 0.5)
+ cs = CSoundNote( snapOnset,
+ pitch,
+ 0.75,
+ 0.5,
+ 1,
+ i,
+ instrumentId = self.owner.getTrackInstrument(i).instrumentId )
+ cs.pageId = self.curPage
+ id = self.noteDB.addNote( -1, self.curPage, i, cs )
+ n = self.noteDB.getNote( self.curPage, i, id, self )
+ self.selectNotes( { i:[n] }, True )
+ n.playSampleNote( False )
+
+ noteS = self.noteDB.getNotesByTrack(self.curPage, i)
+ for note in noteS:
+ if note.cs.onset < snapOnset and (note.cs.onset + note.cs.duration) > snapOnset:
+ self.noteDB.updateNote(self.curPage, i, note.id, PARAMETER.DURATION, snapOnset - note.cs.onset)
+
+ if i != self.drumIndex: # switch to drag duration
+ self.updateDragLimits()
+ self.clickLoc[0] += self.ticksToPixels( self.curBeats, 1 )
+ self.setCurrentAction( "note-drag-duration", n )
+ self.setCursor("drag-duration")
+ else:
+ self.curAction = True # we handled this, but there's no real action
+
+ TP.ProfileEnd( "TI::handleButtonPress" )
+ return
+ elif self.interfaceMode == INTERFACEMODE.PAINT:
+ self.scale = self.getScale()
+ self.painting = True
+ self.paintTrack = i
+ if i == self.drumIndex:
+ pitch = min( self.pixelsToPitchDrumFloor( self.clickLoc[1] - self.trackLimits[i][1] + Config.HIT_HEIGHT//2 )//Config.PITCH_STEP_DRUM, Config.NUMBER_OF_POSSIBLE_PITCHES_DRUM-1)*Config.PITCH_STEP_DRUM + Config.MINIMUM_PITCH_DRUM
+ if pitch < 24:
+ pitch = 24
+ elif pitch > 48:
+ pitch = 48
+ else:
+ pitch = pitch
+ else:
+ pitch = min( self.pixelsToPitchFloor( self.clickLoc[1] - self.trackLimits[i][1] + Config.NOTE_HEIGHT//2 ), Config.NUMBER_OF_POSSIBLE_PITCHES-1) + Config.MINIMUM_PITCH
+ if pitch < 24:
+ pitch = 24
+ elif pitch > 48:
+ pitch = 48
+ else:
+ pitch = pitch
+
+ minDiff = 100
+ for pit in GenerationConstants.SCALES[self.scale]:
+ diff = abs(pitch-(pit+36))
+ if diff < minDiff:
+ minDiff = diff
+ nearestPit = pit
+ pitch = nearestPit+36
+
+ onset = self.pixelsToTicksFloor( self.curBeats, self.clickLoc[0] - self.trackRect[i].x )
+ onset = self.paintGrid * int(onset / self.paintGrid + 0.5)
+ self.pLastPos = onset
+ if i != self.drumIndex:
+ noteS = self.noteDB.getNotesByTrack(self.curPage, i)
+ ids = []
+ stream = []
+ for n in noteS:
+ if n.cs.onset >= onset and n.cs.onset < (onset + self.paintNoteDur):
+ ids.append(n.id)
+ if onset > n.cs.onset and onset < (n.cs.onset + n.cs.duration):
+ ids.append(n.id)
+ if len(ids):
+ stream += [self.curPage, i, len(ids)] + ids
+ self.noteDB.deleteNotes( stream + [-1] )
+
+ cs = CSoundNote( int(onset),
+ pitch,
+ 0.75,
+ 0.5,
+ 1,
+ i,
+ instrumentId = self.owner.getTrackInstrument(i).instrumentId )
+ cs.pageId = self.curPage
+ id = self.noteDB.addNote( -1, self.curPage, i, cs )
+ self.noteDB.updateNote(self.curPage, i, id, PARAMETER.DURATION, self.paintNoteDur)
+ n = self.noteDB.getNote( self.curPage, i, id, self )
+ self.selectNotes( { i:[n] }, True )
+ n.playSampleNote( False )
+ self.curAction = True
+
+ TP.ProfileEnd( "TI::handleButtonPress" )
+
+
+ def handleButtonRelease( self, widget, event ):
+ if not self.clickButton: return # we recieved this event but were never clicked! (probably a popup window was open)
+ self.clickButton = 0
+ self.painting = False
+
+ TP.ProfileBegin( "TI::handleButtonRelease" )
+
+ if event.button != 1:
+ TP.ProfileEnd( "TI::handleButtonRelease" )
+ return
+
+ if not self.curAction: #do track selection stuff here so that we can also handle marquee selection
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackLimits[i][0] > event.y: break
+ if self.trackLimits[i][1] < event.y: continue
+ if event.button == 1:
+ if self.buttonPressCount == 1: self.owner.toggleTrack( i, False )
+ elif self.buttonPressCount == 2: self.owner.toggleTrack( i, True )
+ else: self.owner.clearTracks()
+ break
+
+ TP.ProfileEnd( "TI::handleButtonRelease" )
+ return
+
+ if not self.curActionObject: # there was no real action to carry out
+ self.curAction = False
+ TP.ProfileEnd( "TI::handleButtonRelease" )
+ return
+
+ if self.curActionObject != self:
+ self.curActionObject.handleButtonRelease( self, event, self.buttonPressCount )
+ self.updateTooltip( event )
+ else:
+ # we're doing the action ourselves
+ if self.curAction == "marquee": self.doneMarquee( event )
+ elif self.curAction == "playhead-drag": self.donePlayhead( event )
+ self.updateTooltip( event )
+
+
+ TP.ProfileEnd( "TI::handleButtonRelease" )
+ return
+
+ def handleMotion( self, widget, event ):
+ TP.ProfileBegin( "TI::handleMotion::Common" )
+
+ if event.is_hint:
+ x, y, state = self.window.get_pointer()
+ event.x = float(x)
+ event.y = float(y)
+ event.state = state
+
+ if self.painting:
+ i = self.paintTrack
+ curPos = self.pixelsToTicksFloor(self.curBeats, event.x - self.trackRect[i].x)
+ gridPos = self.paintGrid * int(curPos / self.paintGrid + 0.5)
+ if gridPos >= self.curBeats * Config.TICKS_PER_BEAT:
+ return
+ if gridPos != self.pLastPos:
+ self.pLastPos = gridPos
+ if i == self.drumIndex:
+ pitch = min( self.pixelsToPitchDrumFloor( int(event.y) - self.trackLimits[i][1] + Config.HIT_HEIGHT//2 )//Config.PITCH_STEP_DRUM, Config.NUMBER_OF_POSSIBLE_PITCHES_DRUM-1)*Config.PITCH_STEP_DRUM + Config.MINIMUM_PITCH_DRUM
+ if pitch < 24:
+ pitch = 24
+ elif pitch > 48:
+ pitch = 48
+ else:
+ pitch = pitch
+ else:
+ pitch = min( self.pixelsToPitchFloor( int(event.y) - self.trackLimits[i][1] + Config.NOTE_HEIGHT//2 ), Config.NUMBER_OF_POSSIBLE_PITCHES-1) + Config.MINIMUM_PITCH
+ if pitch < 24:
+ pitch = 24
+ elif pitch > 48:
+ pitch = 48
+ else:
+ pitch = pitch
+ minDiff = 100
+ for pit in GenerationConstants.SCALES[self.scale]:
+ diff = abs(pitch-(pit+36))
+ if diff < minDiff:
+ minDiff = diff
+ nearestPit = pit
+ pitch = nearestPit+36
+
+ onset = gridPos
+ if i != self.drumIndex:
+ noteS = self.noteDB.getNotesByTrack(self.curPage, i)
+ ids = []
+ stream = []
+ for n in noteS:
+ if n.cs.onset >= onset and n.cs.onset < (onset + self.paintNoteDur):
+ ids.append(n.id)
+ if onset > n.cs.onset and onset < (n.cs.onset + n.cs.duration):
+ ids.append(n.id)
+ if len(ids):
+ stream += [self.curPage, i, len(ids)] + ids
+ self.noteDB.deleteNotes( stream + [-1] )
+
+ cs = CSoundNote( int(onset),
+ pitch,
+ 0.75,
+ 0.5,
+ 1,
+ i,
+ instrumentId = self.owner.getTrackInstrument(i).instrumentId )
+ cs.pageId = self.curPage
+ id = self.noteDB.addNote( -1, self.curPage, i, cs )
+ self.noteDB.updateNote(self.curPage, i, id, PARAMETER.DURATION, self.paintNoteDur)
+ n = self.noteDB.getNote( self.curPage, i, id, self )
+ self.selectNotes( { i:[n] }, True )
+ n.playSampleNote( False )
+ self.curAction = True
+
+
+ TP.ProfileEnd( "TI::handleMotion::Common" )
+
+ if not self.clickButton and self.curAction != "paste": # we recieved this event but were never clicked! (probably a popup window was open)
+ TP.ProfileBegin( "TI::handleMotion::Hover" )
+ self.updateTooltip( event )
+ TP.ProfileEnd( "TI::handleMotion::Hover" )
+ return
+
+ if self.curAction == "paste":
+ TP.ProfileBegin( "TI::handleMotion::Paste" )
+ top = Config.NUMBER_OF_TRACKS
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackLimits[i][0] > event.y: break
+ if self.trackLimits[i][1] < event.y: continue
+ top = i
+ break
+ self.updatePaste( self.pixelsToTicksFloor( self.curBeats, event.x ), top )
+ TP.ProfileEnd( "TI::handleMotion::Paste" )
+ elif event.state & gtk.gdk.BUTTON1_MASK:
+ TP.ProfileBegin( "TI::handleMotion::Drag" )
+
+ if not self.curAction: # no action is in progress yet we're dragging, start a marquee
+ self.setCurrentAction( "marquee", self )
+
+ if self.curAction == "note-drag-onset":
+ self.noteDragOnset( event )
+
+ elif self.curAction == "note-drag-duration":
+ self.noteDragDuration( event )
+
+ elif self.curAction == "note-drag-pitch":
+ self.noteDragPitch( event )
+
+ elif self.curAction == "note-drag-pitch-drum":
+ self.noteDragPitch( event, True )
+
+ elif self.curAction == "marquee":
+ self.updateMarquee( event )
+
+ elif self.curAction == "playhead-drag":
+ self.updatePlayhead( event )
+
+ TP.ProfileEnd( "TI::handleMotion::Drag" )
+ else:
+ TP.ProfileBegin( "TI::handleMotion::Hover" )
+ self.updateTooltip( event )
+ TP.ProfileEnd( "TI::handleMotion::Hover" )
+
+ return
+
+ #=======================================================
+ # Actions
+
+ def setCurrentAction( self, action, obj = None ):
+ if self.curAction:
+ self.doneCurrentAction()
+
+ self.curAction = action
+ self.curActionObject = obj
+
+ if action == "note-drag-onset": self.updateDragLimits()
+ elif action == "note-drag-duration": self.updateDragLimits()
+ elif action == "note-drag-pitch": self.updateDragLimits()
+ elif action == "note-drag-pitch-drum": self.updateDragLimits()
+ elif action == "paste":
+ self._updateClipboardArea()
+ self.setCursor("paste")
+
+ def doneCurrentAction( self ):
+ if not self.curAction: return
+ action = self.curAction
+ self.curAction = False
+
+ if action == "note-drag-onset": self.doneNoteDrag( action )
+ elif action == "note-drag-duration": self.doneNoteDrag( action )
+ elif action == "note-drag-pitch": self.doneNoteDrag( action )
+ elif action == "note-drag-pitch-drum": self.doneNoteDrag( action )
+ elif action == "paste":
+ self.owner.cleanupClipboard()
+
+ def trackToggled( self, trackN = -1 ):
+ if trackN == -1: self.invalidate_rect( 0, 0, self.width, self.height )
+ else: self.invalidate_rect( 0, self.trackLimits[trackN][0], self.width, self.trackLimits[trackN][1]-self.trackLimits[trackN][0] )
+
+ def selectionChanged( self ):
+ if self.curAction == "note-drag-onset": self.updateDragLimits()
+ elif self.curAction == "note-drag-duration": self.updateDragLimits()
+ elif self.curAction == "note-drag-pitch": self.updateDragLimits()
+ elif self.curAction == "note-drag-pitch-drum": self.updateDragLimits()
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if len(self.selectedNotes[i]):
+ self.owner.setContextState( CONTEXT.NOTE, True )
+ self.owner.setContext( CONTEXT.NOTE )
+ return
+ self.owner.setContextState( CONTEXT.NOTE, False )
+
+ def applyNoteSelection( self, mode, trackN, which, page = -1 ):
+ if page == -1: page = self.curPage
+ if mode == SELECTNOTES.ALL:
+ track = self.noteDB.getNotesByTrack( page, trackN, self )
+ map( lambda note:note.setSelected( True ), track )
+ self.selectedNotes[trackN] = []
+ map( lambda note:self.selectedNotes[trackN].append(note), track )
+ elif mode == SELECTNOTES.NONE:
+ track = self.selectedNotes[trackN] #self.noteDB.getNotesByTrack( page, trackN, self )
+ map( lambda note:note.setSelected( False ), track )
+ self.selectedNotes[trackN] = []
+ elif mode == SELECTNOTES.ADD:
+ for note in which:
+ if note.setSelected( True ):
+ self.selectedNotes[trackN].append( note )
+ elif mode == SELECTNOTES.REMOVE:
+ for note in which:
+ if note.setSelected( False ):
+ self.selectedNotes[trackN].remove( note )
+ elif mode == SELECTNOTES.FLIP:
+ for note in which:
+ if note.getSelected():
+ note.setSelected( False )
+ self.selectedNotes[trackN].remove( note )
+ else:
+ note.setSelected( True )
+ self.selectedNotes[trackN].append( note )
+ elif mode == SELECTNOTES.EXCLUSIVE:
+ notes = self.noteDB.getNotesByTrack( page, trackN, self )
+ for n in range(len(notes)):
+ if notes[n] in which:
+ if notes[n].setSelected( True ):
+ self.selectedNotes[trackN].append( notes[n] )
+ else:
+ if notes[n].setSelected( False ):
+ self.selectedNotes[trackN].remove( notes[n] )
+
+ def selectNotesByBar( self, trackN, start, stop, page = -1 ):
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if i == trackN:
+ notes = []
+ track = self.noteDB.getNotesByTrack( self.curPage, trackN, self )
+ for n in range(len(track)):
+ if track[n].testOnset( start, stop ): notes.append(track[n])
+ if not Config.ModKeys.ctrlDown: self.applyNoteSelection( SELECTNOTES.EXCLUSIVE, trackN, notes, page )
+ else: self.applyNoteSelection( SELECTNOTES.ADD, trackN, notes, page )
+ else:
+ if not Config.ModKeys.ctrlDown: self.applyNoteSelection( SELECTNOTES.NONE, i, [], page )
+ self.selectionChanged()
+
+ def selectNotesByTrack( self, trackN, page = -1 ):
+ if Config.ModKeys.ctrlDown:
+ self.applyNoteSelection( SELECTNOTES.ALL, trackN, [], page )
+ else:
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if i == trackN: self.applyNoteSelection( SELECTNOTES.ALL, trackN, [], page )
+ else: self.applyNoteSelection( SELECTNOTES.NONE, i, [], page )
+ self.selectionChanged()
+
+ def selectNotes( self, noteDic, ignoreCtrl = False, page = -1 ):
+ if Config.ModKeys.ctrlDown and not ignoreCtrl:
+ for i in noteDic:
+ self.applyNoteSelection( SELECTNOTES.FLIP, i, noteDic[i], page )
+ else:
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if i in noteDic: self.applyNoteSelection( SELECTNOTES.EXCLUSIVE, i, noteDic[i], page )
+ else: self.applyNoteSelection( SELECTNOTES.NONE, i, [], page )
+ self.selectionChanged()
+
+ def deselectNotes( self, noteDic, page = -1 ):
+ for i in noteDic:
+ self.applyNoteSelection( SELECTNOTES.REMOVE, i, noteDic[i], page )
+ self.selectionChanged()
+
+ def clearSelectedNotes( self, page = -1 ):
+ for i in range(Config.NUMBER_OF_TRACKS):
+ self.applyNoteSelection( SELECTNOTES.NONE, i, [], page )
+ self.selectionChanged()
+
+ def updateDragLimits( self ):
+ self.dragLimits = [ [-9999,9999], [-9999,9999], [-9999,9999] ] # initialize to big numbers!
+ maxRightBound = self.noteDB.getPage(self.curPage).ticks
+
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if not len(self.selectedNotes[i]): continue # no selected notes here
+
+ track = self.noteDB.getNotesByTrack( self.curPage, i, self )
+ leftBound = 0
+ skip = True # skip the first note
+ for n in range(len(track)):
+ if skip:
+ skip = False
+ thisNote = track[n]
+ continue
+ nextNote = track[n]
+ if not thisNote.getSelected():
+ leftBound = thisNote.getEndTick()
+ else:
+ if not nextNote.getSelected():
+ rightBound = min( nextNote.getStartTick(), maxRightBound )
+ widthBound = rightBound
+ else:
+ rightBound = maxRightBound
+ widthBound = min( nextNote.getStartTick(), maxRightBound )
+ thisNote.updateDragLimits( self.dragLimits, leftBound, rightBound, widthBound, maxRightBound )
+ thisNote = nextNote
+ # do the last note
+ if thisNote.getSelected():
+ thisNote.updateDragLimits( self.dragLimits, leftBound, maxRightBound, maxRightBound, maxRightBound )
+
+ def noteDragOnset( self, event ):
+ do = self.pixelsToTicks( self.curBeats, event.x - self.clickLoc[0] )
+ do = min( self.dragLimits[0][1], max( self.dragLimits[0][0], do ) )
+ do = self.pointerGrid * int(do / self.pointerGrid)
+
+ if do != self.lastDO:
+ self.lastDO = do
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ tstream = []
+ for note in self.selectedNotes[i]:
+ note.noteDragOnset( do, tstream )
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.ONSET, len(tstream)//2 ] + tstream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def noteDragDuration( self, event ):
+ dd = self.pixelsToTicks( self.curBeats, event.x - self.clickLoc[0] )
+ dd = min( self.dragLimits[2][1], max( self.dragLimits[2][0], dd ) )
+
+ if dd != self.lastDD:
+ self.lastDD = dd
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ tstream = []
+ for note in self.selectedNotes[i]:
+ note.noteDragDuration( dd, tstream )
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.DURATION, len(tstream)//2 ] + tstream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def noteDragPitch( self, event, drum = False ):
+ if not drum: dp = self.pixelsToPitch( event.y - self.clickLoc[1] )
+ else: dp = self.pixelsToPitchDrum( event.y - self.clickLoc[1] )
+ dp = min( self.dragLimits[1][1], max( self.dragLimits[1][0], dp ) )
+
+ if dp != self.lastDP:
+ self.lastDP = dp
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ tstream = []
+ for note in self.selectedNotes[i]:
+ note.noteDragPitch( dp, tstream )
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.PITCH, len(tstream)//2 ] + tstream
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ if self.curActionObject.note.track != self.drumIndex:
+ self.curActionObject.playSampleNote( True )
+ elif dp != self.lastDrumDP and not dp%2: # only play of "full" drum pitches
+ self.lastDrumDP = dp
+ self.curActionObject.playSampleNote( False )
+
+ def doneNoteDrag( self, action ):
+ # if action == "note-drag-pitch" or action == "note-drag-pitch-drum":
+ # self.curActionObject.playSampleNote()
+
+ self.lastDO = self.lastDP = self.lastDrumDP = self.lastDD = None
+
+ for i in range(Config.NUMBER_OF_TRACKS):
+ for note in self.selectedNotes[i]:
+ note.doneNoteDrag( self )
+
+ def noteStepOnset( self, step ):
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if not len(self.selectedNotes[i]): continue # no selected notes here
+
+ tstream = []
+ track = self.noteDB.getNotesByTrack( self.curPage, i, self )
+ if step < 0: # moving to the left, iterate forwards
+ leftBound = 0
+ for n in range(len(track)):
+ leftBound = track[n].noteDecOnset( step, leftBound, tstream )
+ else: # moving to the right, iterate backwards
+ rightBound = self.noteDB.getPage(self.curPage).ticks
+ for n in range(len(track)-1, -1, -1 ):
+ rightBound = track[n].noteIncOnset( step, rightBound, tstream )
+
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.ONSET, len(tstream)//2 ] + tstream
+
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def noteStepPitch( self, step ):
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if not len(self.selectedNotes[i]): continue # no selected notes here
+
+ tstream = []
+ if step < 0:
+ for n in self.selectedNotes[i]:
+ n.noteDecPitch( step, tstream )
+ else:
+ for n in self.selectedNotes[i]:
+ n.noteIncPitch( step, tstream )
+
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.PITCH, len(tstream)//2 ] + tstream
+
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def noteStepDuration( self, step ):
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if not len(self.selectedNotes[i]): continue # no selected notes here
+
+ tstream = []
+ if step < 0:
+ for n in self.selectedNotes[i]:
+ n.noteDecDuration( step, tstream )
+ else:
+ track = self.noteDB.getNotesByTrack( self.curPage, i, self )
+ for j in range(len(track)-1):
+ track[j].noteIncDuration( step, track[j+1].getStartTick(), tstream )
+ track[len(track)-1].noteIncDuration( step, self.noteDB.getPage(self.curPage).ticks, tstream )
+
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.DURATION, len(tstream)//2 ] + tstream
+
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+ def noteStepVolume( self, step ):
+ stream = []
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if not len(self.selectedNotes[i]): continue # no selected notes here
+
+ tstream = []
+ if step < 0:
+ for n in self.selectedNotes[i]:
+ n.noteDecVolume( step, tstream )
+ else:
+ for n in self.selectedNotes[i]:
+ n.noteIncVolume( step, tstream )
+
+ if len(tstream):
+ stream += [ self.curPage, i, PARAMETER.AMPLITUDE, len(tstream)//2 ] + tstream
+
+ if len(stream):
+ self.noteDB.updateNotes( stream + [-1] )
+
+
+ def updateMarquee( self, event ):
+ if self.marqueeLoc:
+ oldX = self.marqueeRect[0][0]
+ oldEndX = self.marqueeRect[0][0] + self.marqueeRect[1][0]
+ oldY = self.marqueeRect[0][1]
+ oldEndY = self.marqueeRect[0][1] + self.marqueeRect[1][1]
+ else:
+ oldX = oldEndX = self.clickLoc[0]
+ oldY = oldEndY = self.clickLoc[1]
+
+ self.marqueeLoc = [ int(event.x), int(event.y) ]
+ if self.marqueeLoc[0] < 0: self.marqueeLoc[0] = 0
+ elif self.marqueeLoc[0] > self.width: self.marqueeLoc[0] = self.width
+ if self.marqueeLoc[1] < 0: self.marqueeLoc[1] = 0
+ elif self.marqueeLoc[1] > self.height: self.marqueeLoc[1] = self.height
+
+ if self.marqueeLoc[0] > self.clickLoc[0]:
+ self.marqueeRect[0][0] = self.clickLoc[0]
+ self.marqueeRect[1][0] = self.marqueeLoc[0] - self.clickLoc[0]
+ else:
+ self.marqueeRect[0][0] = self.marqueeLoc[0]
+ self.marqueeRect[1][0] = self.clickLoc[0] - self.marqueeLoc[0]
+ if self.marqueeLoc[1] > self.clickLoc[1]:
+ self.marqueeRect[0][1] = self.clickLoc[1]
+ self.marqueeRect[1][1] = self.marqueeLoc[1] - self.clickLoc[1]
+ else:
+ self.marqueeRect[0][1] = self.marqueeLoc[1]
+ self.marqueeRect[1][1] = self.clickLoc[1] - self.marqueeLoc[1]
+
+ x = min( self.marqueeRect[0][0], oldX )
+ width = max( self.marqueeRect[0][0] + self.marqueeRect[1][0], oldEndX ) - x
+ y = min( self.marqueeRect[0][1], oldY )
+ height = max( self.marqueeRect[0][1] + self.marqueeRect[1][1], oldEndY ) - y
+ self.invalidate_rect( x-1, y-1, width+2, height+2, self.curPage, False )
+
+ def doneMarquee( self, event ):
+ if self.marqueeLoc:
+ stop = [ self.marqueeRect[0][0] + self.marqueeRect[1][0], self.marqueeRect[0][1] + self.marqueeRect[1][1] ]
+
+ select = {}
+
+ for i in range(Config.NUMBER_OF_TRACKS):
+ intersectionY = [ max(self.marqueeRect[0][1],self.trackLimits[i][0]), min(stop[1],self.trackLimits[i][1]) ]
+ if intersectionY[0] > intersectionY[1]:
+ continue
+
+ notes = []
+ track = self.noteDB.getNotesByTrack( self.curPage, i, self )
+ for n in range(len(track)):
+ hit = track[n].handleMarqueeSelect( self,
+ [ self.marqueeRect[0][0], intersectionY[0] ], \
+ [ stop[0], intersectionY[1] ] )
+ if hit: notes.append(track[n])
+
+ if len(notes): select[i] = notes
+
+ self.selectNotes( select )
+
+ self.marqueeLoc = False
+ self.doneCurrentAction()
+
+ self.invalidate_rect( self.marqueeRect[0][0]-1, self.marqueeRect[0][1]-1, self.marqueeRect[1][0]+2, self.marqueeRect[1][1]+2, self.curPage, False )
+
+ def updatePlayhead( self, event ):
+ x = min( self.trackWidth - self.pixelsPerTick[self.curBeats], max( Config.TRACK_SPACING_DIV2, event.x ) )
+ self.setPlayhead( self.pixelsToTicks( self.curBeats, x ) )
+
+ def donePlayhead( self, event ):
+ x = min( self.trackWidth - self.pixelsPerTick[self.curBeats], max( Config.TRACK_SPACING_DIV2, event.x ) )
+ ticks = self.pixelsToTicks( self.curBeats, x )
+ print "set playhead to %d ticks" % (ticks)
+ self.doneCurrentAction()
+
+ def updatePaste( self, tick, track ):
+ if self.interfaceMode == INTERFACEMODE.PASTE_TRACKS: tick = 0
+ if self.pasteTick == tick and self.pasteTrack == track: return
+ if self.noteDB.getPage(self.curPage).ticks < tick < 0 \
+ or track > self.drumIndex \
+ or ( track == self.drumIndex and not self.clipboardDrumTrack ):
+ if self.pasteRect:
+ self.invalidate_rect( self.pasteRect[0][0], self.pasteRect[0][1], self.pasteRect[1][0], self.pasteRect[1][1], self.curPage, False )
+ self.pasteTick = self.pasteTrack = -1
+ self.pasteRect = False
+ return
+ if self.pasteRect:
+ self.invalidate_rect( self.pasteRect[0][0], self.pasteRect[0][1], self.pasteRect[1][0], self.pasteRect[1][1], self.curPage, False )
+ if self.clipboardDrumTrack:
+ bottom = self.drumIndex
+ else:
+ bottom = self.drumIndex - 1
+ for t in range(self.drumIndex-1,self.clipboardTrackTop-1,-1):
+ if self.clipboardArea["tracks"][t]: break
+ bottom -= 1
+ end = -tick + min( self.noteDB.getPage(self.curPage).ticks, tick + self.clipboardArea["limit"][1]-self.clipboardArea["limit"][0] )
+ self.pasteTick = tick
+ self.pasteTrack = track
+ self.pasteRect = [ [ self.ticksToPixels( self.curBeats, tick ), \
+ self.trackLimits[track][0] ], \
+ [ self.ticksToPixels( self.curBeats, end), \
+ self.trackLimits[bottom][1] ] ]
+ self.invalidate_rect( self.pasteRect[0][0], self.pasteRect[0][1], self.pasteRect[1][0], self.pasteRect[1][1], self.curPage, False )
+
+ def doPaste( self ):
+ if self.pasteTrack == -1:
+ self.doneCurrentAction()
+ return
+
+ trackMap = {}
+ for t in range(self.pasteTrack,self.drumIndex):
+ ind = t+self.clipboardTrackTop-self.pasteTrack
+ if ind >= self.drumIndex: break
+ if not self.clipboardArea["tracks"][ind]:
+ continue
+ trackMap[t] = ind
+ if self.clipboardDrumTrack:
+ trackMap[self.drumIndex] = self.drumIndex
+ new = self.owner.pasteClipboard( self.pasteTick - self.clipboardArea["limit"][0], trackMap )
+ if self.interfaceMode == INTERFACEMODE.PASTE_NOTES and self.curPage in new:
+ noteDic = {}
+ for t in range(Config.NUMBER_OF_TRACKS):
+ if len(new[self.curPage][t]):
+ noteDic[t] = [ self.noteDB.getNote( self.curPage, t, n, self ) for n in new[self.curPage][t] ]
+ self.selectNotes(noteDic)
+ elif self.interfaceMode == INTERFACEMODE.PASTE_TRACKS:
+ for t in range(self.drumIndex):
+ ind = t + self.clipboardTrackTop - self.pasteTrack
+ if ind >= self.drumIndex or ind < 0: self.owner.setTrack( t, False )
+ else: self.owner.setTrack( t, self.clipboardArea["tracks"][ind] )
+ self.owner.setTrack( self.drumIndex, self.clipboardDrumTrack )
+
+ self.doneCurrentAction()
+
+ def donePaste( self ):
+ if self.pasteRect:
+ self.invalidate_rect( self.pasteRect[0][0], self.pasteRect[0][1], self.pasteRect[1][0], self.pasteRect[1][1], self.curPage, False )
+ self.pasteTick = self.pasteTrack = -1
+ self.pasteRect = False
+ self.setInterfaceMode("tool")
+ # make a fake event for updateTooltip
+ event = gtk.gdk.Event(gtk.gdk.MOTION_NOTIFY)
+ x, y, state = self.window.get_pointer()
+ event.x = float(x)
+ event.y = float(y)
+ event.state = state
+ self.updateTooltip( event )
+
+ def updateTooltip( self, event ):
+
+ # check clicked the playhead
+ if event.x >= self.playheadX and event.x <= self.playheadX + Config.PLAYHEAD_SIZE:
+ self.setCursor("drag-playhead")
+ return
+
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if self.trackLimits[i][0] > event.y: break
+ if self.trackLimits[i][1] < event.y: continue
+
+ notes = self.noteDB.getNotesByTrack( self.curPage, i, self )
+ handled = 0
+ for n in range(len(notes)):
+ handled = notes[n].updateTooltip( self, event )
+ if handled == 0: continue
+ elif handled == 1: return # event was handled
+ else: break
+
+ # note wasn't handled, could potentially draw a note
+ if self.interfaceMode == INTERFACEMODE.DRAW:
+ if handled == -2: # event X overlapped with a note
+ self.setCursor("default")
+ return
+
+ self.setCursor("pencil")
+ return
+
+ break
+
+ self.setCursor("default")
+
+ def setCursor( self, cursor ):
+ self.window.set_cursor(self.cursor[cursor])
+
+ #=======================================================
+ # Drawing
+
+ def draw( self, buf, noescape = True, timeout = 0 ):
+ if not self.screenBufDirty[buf]: return True
+
+ TP.ProfileBegin( "TrackInterface::draw" )
+
+ startX = self.screenBufDirtyRect[buf].x
+ startY = self.screenBufDirtyRect[buf].y
+ stopX = self.screenBufDirtyRect[buf].x + self.screenBufDirtyRect[buf].width
+ stopY = self.screenBufDirtyRect[buf].y + self.screenBufDirtyRect[buf].height
+
+ beatStart = Config.TRACK_SPACING_DIV2
+ beats = self.screenBufBeats[buf]
+
+ pixmap = self.screenBuf[buf]
+
+ resume = self.screenBufResume[buf]
+
+ self.gc.set_clip_rectangle( self.screenBufDirtyRect[buf] )
+
+ self.gc.set_line_attributes( Config.BEAT_LINE_SIZE, gtk.gdk.LINE_ON_OFF_DASH, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER )
+ # regular tracks
+ for i in range( resume[0], self.drumIndex ):
+ if resume[1] == 0:
+ if startY > self.trackLimits[i][1]: continue
+ if stopY < self.trackLimits[i][0]: break
+
+ # draw background
+ if self.owner.getTrackSelected( i ):
+ pixmap.draw_drawable( self.gc, self.image["trackBGSelected"], 0, 0, 0, self.trackLimits[i][0], self.trackFullWidth, self.trackFullHeight )
+ else:
+ pixmap.draw_drawable( self.gc, self.image["trackBG"], 0, 0, 0, self.trackLimits[i][0], self.trackFullWidth, self.trackFullHeight )
+
+ # draw beat lines
+ self.gc.foreground = self.beatColor
+ for j in range(1,self.screenBufBeats[buf]):
+ x = beatStart + self.beatSpacing[beats][j]
+ pixmap.draw_line( self.gc, x, self.trackRect[i].y, x, self.trackRect[i].y+self.trackRect[i].height )
+
+ resume[1] = 1 # background drawn
+
+ # draw notes
+ TP.ProfileBegin("TI::draw notes")
+ notes = self.noteDB.getNotesByTrack( self.screenBufPage[buf], i, self )
+ for n in range( resume[2], len(notes) ):
+ # check escape
+ if not noescape and time.time() > timeout:
+ resume[0] = i
+ resume[2] = n
+ TP.ProfilePause( "TrackInterface::draw" )
+ return False
+
+ if not notes[n].draw( pixmap, self.gc, startX, stopX ): break
+ TP.ProfileEnd("TI::draw notes")
+
+ # finished a track, reset the resume values for the next one
+ resume[1] = 0
+ resume[2] = 0
+
+ # drum track
+ if stopY > self.trackLimits[self.drumIndex][0]:
+
+ if resume[1] == 0:
+ # draw background
+ if self.owner.getTrackSelected( self.drumIndex ):
+ pixmap.draw_drawable( self.gc, self.image["trackBGDrumSelected"], 0, 0, 0, self.trackLimits[self.drumIndex][0], self.trackFullWidth, self.trackFullHeightDrum )
+ else:
+ pixmap.draw_drawable( self.gc, self.image["trackBGDrum"], 0, 0, 0, self.trackLimits[self.drumIndex][0], self.trackFullWidth, self.trackFullHeightDrum )
+
+ # draw beat lines
+ self.gc.foreground = self.beatColor
+ for j in range(1,self.screenBufBeats[buf]):
+ x = beatStart + self.beatSpacing[beats][j]
+ pixmap.draw_line( self.gc, x, self.trackRect[self.drumIndex].y, x, self.trackRect[self.drumIndex].y+self.trackRect[self.drumIndex].height )
+
+ resume[1] = 1 # background drawn
+
+ # draw notes
+ notes = self.noteDB.getNotesByTrack( self.screenBufPage[buf], self.drumIndex, self )
+ for n in range( resume[2], len(notes) ):
+ # check escape
+ if not noescape and time.time() > timeout:
+ resume[0] = i
+ resume[2] = n
+ TP.ProfilePause( "TrackInterface::draw" )
+ return False
+ if not notes[n].draw( pixmap, self.gc, startX, stopX ): break
+
+ self.screenBufDirty[buf] = False
+
+ TP.ProfileEnd( "TrackInterface::draw" )
+
+ return True
+
+ def expose( self, DA, event ):
+
+ if self.screenBufDirty[self.curScreen]:
+ self.draw( self.curScreen )
+
+ TP.ProfileBegin( "TrackInterface::expose" )
+
+ startX = event.area.x
+ startY = event.area.y
+ stopX = event.area.x + event.area.width
+ stopY = event.area.y + event.area.height
+
+ #print "%d %d %d %d" % (startX,startY,stopX,stopY)
+
+ self.gc.set_clip_rectangle( event.area )
+
+ # draw base
+ DA.window.draw_drawable( self.gc, self.screenBuf[self.curScreen], startX, startY, startX, startY, event.area.width, event.area.height )
+
+ # draw playhead
+ self.gc.set_line_attributes( Config.PLAYHEAD_SIZE, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER )
+ self.gc.foreground = self.playheadColor
+ DA.window.draw_line( self.gc, self.playheadX, startY, self.playheadX, stopY )
+
+ if self.marqueeLoc: # draw the selection rect
+ self.gc.set_line_attributes( Config.MARQUEE_SIZE, gtk.gdk.LINE_ON_OFF_DASH, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER )
+ self.gc.foreground = self.marqueeColor
+ DA.window.draw_rectangle( self.gc, False, self.marqueeRect[0][0], self.marqueeRect[0][1], self.marqueeRect[1][0], self.marqueeRect[1][1] )
+
+ if self.pasteRect: # draw the paste highlight
+ self.gc.set_function( gtk.gdk.INVERT )
+ for t in range(self.pasteTrack,self.drumIndex):
+ ind = t+self.clipboardTrackTop-self.pasteTrack
+ if ind >= self.drumIndex: break
+ if not self.clipboardArea["tracks"][ind]:
+ continue
+ DA.window.draw_rectangle( self.gc, True, self.pasteRect[0][0], self.trackLimits[t][0] + Config.TRACK_SPACING_DIV2, self.pasteRect[1][0], self.trackHeight )
+ if self.clipboardDrumTrack:
+ DA.window.draw_rectangle( self.gc, True, self.pasteRect[0][0], self.trackLimits[self.drumIndex][0] + Config.TRACK_SPACING_DIV2, self.pasteRect[1][0], self.trackHeightDrum )
+ self.gc.set_function( gtk.gdk.COPY )
+
+ self.drawingAreaDirty = False
+
+ TP.ProfileEnd( "TrackInterface::expose" )
+
+ def invalidate_rect( self, x, y, width, height, page = -1, base = True ):
+ #print "%d %d %d %d Page %d CurPage %d" % (x,y,width,height,page,self.curPage)
+ self.dirtyRectToAdd.x = x
+ self.dirtyRectToAdd.y = y
+ self.dirtyRectToAdd.width = width
+ self.dirtyRectToAdd.height = height
+
+ #print "dirty %d %d %d %d %d %d" % (x, y, width, height, x+width, y+height)
+ if page == self.curPage or page == -1:
+ if base: # the base image has been dirtied
+ if not self.screenBufDirty[self.curScreen]:
+ self.screenBufDirtyRect[self.curScreen].x = x
+ self.screenBufDirtyRect[self.curScreen].y = y
+ self.screenBufDirtyRect[self.curScreen].width = width
+ self.screenBufDirtyRect[self.curScreen].height = height
+ else:
+ self.screenBufDirtyRect[self.curScreen] = self.screenBufDirtyRect[self.curScreen].union( self.dirtyRectToAdd )
+ self.screenBufResume[self.curScreen] = [0,0,0]
+ self.screenBufDirty[self.curScreen] = True
+ if self.drawingArea.window != None:
+ self.drawingArea.window.invalidate_rect( self.dirtyRectToAdd, True )
+ self.drawingAreaDirty = True
+
+ if page == self.screenBufPage[self.preScreen] or page == -1:
+ if not self.screenBufDirty[self.preScreen]:
+ self.screenBufDirtyRect[self.preScreen].x = x
+ self.screenBufDirtyRect[self.preScreen].y = y
+ self.screenBufDirtyRect[self.preScreen].width = width
+ self.screenBufDirtyRect[self.preScreen].height = height
+ else:
+ self.screenBufDirtyRect[self.preScreen] = self.screenBufDirtyRect[self.preScreen].union( self.dirtyRectToAdd )
+ self.screenBufResume[self.preScreen] = [0,0,0]
+ self.screenBufDirty[self.preScreen] = True
+
+ #self.queue_draw()
+
+ def getTrackOrigin( self, track ):
+ return ( self.trackRect[track].x, self.trackRect[track].y )
+
+ def ticksToPixels( self, beats, ticks ):
+ return int(round( ticks * self.pixelsPerTick[beats] ))
+ def pixelsToTicks( self, beats, pixels ):
+ return int(round( pixels * self.ticksPerPixel[beats] ))
+ def ticksToPixelsFloor( self, beats, ticks ):
+ return int( ticks * self.pixelsPerTick[beats] )
+ def pixelsToTicksFloor( self, beats, pixels ):
+ return int( pixels * self.ticksPerPixel[beats] )
+ def pitchToPixels( self, pitch ):
+ return int(round( ( Config.MAXIMUM_PITCH - pitch ) * self.pixelsPerPitch ))
+ def pixelsToPitch( self, pixels ):
+ return int(round(-pixels*self.pitchPerPixel))
+ def pitchToPixelsFloor( self, pitch ):
+ return int(( Config.MAXIMUM_PITCH - pitch ) * self.pixelsPerPitch )
+ def pixelsToPitchFloor( self, pixels ):
+ return int(-pixels*self.pitchPerPixel)
+ def pitchToPixelsDrum( self, pitch ):
+ return int(round( ( Config.MAXIMUM_PITCH_DRUM - pitch ) * self.pixelsPerPitchDrum ))
+ def pixelsToPitchDrum( self, pixels ):
+ return int(round(-pixels*self.pitchPerPixelDrum))
+ def pitchToPixelsDrumFloor( self, pitch ):
+ return int( ( Config.MAXIMUM_PITCH_DRUM - pitch ) * self.pixelsPerPitchDrum )
+ def pixelsToPitchDrumFloor( self, pixels ):
+ return int(-pixels*self.pitchPerPixelDrum)
diff --git a/TamTamEdit.activity/Edit/TuneInterface.py b/TamTamEdit.activity/Edit/TuneInterface.py
new file mode 100644
index 0000000..3682a57
--- /dev/null
+++ b/TamTamEdit.activity/Edit/TuneInterface.py
@@ -0,0 +1,645 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import Config
+
+from Util.Profiler import TP
+from Edit.MainWindow import CONTEXT
+
+from Util.NoteDB import PARAMETER
+
+class TuneInterfaceParasite:
+
+ def __init__( self, noteDB, owner, note ):
+ self.noteDB = noteDB
+ self.owner = owner
+ self.note = note
+
+ self.x = self.y = self.width = -1
+
+ def attach( self ):
+ self.updateParameter( None, None )
+ return self
+
+ def destroy( self ):
+ self.owner.invalidate_thumbnail( self.note.page, self.x, self.y, self.width, 1 )
+
+ def updateParameter( self, parameter, value ):
+ if parameter == PARAMETER.AMPLITUDE: return
+ x = 2 + Config.THUMBNAIL_TRACK_RECT[self.note.track][0] + self.owner.ticksToPixels( self.noteDB.getPage( self.note.page).beats, self.note.cs.onset )
+ if self.note.track == Config.NUMBER_OF_TRACKS-1: # drum track
+ y = Config.THUMBNAIL_TRACK_RECT[self.note.track][1] + self.owner.pitchToPixelsDrum( self.note.cs.pitch )
+ if x != self.x or y != self.y:
+ if parameter != None: # not the first update
+ xx = min( self.x, x )
+ yy = min( self.y, y )
+ endxx = max( self.endx, x + 1 )
+ endyy = max( self.y, y ) + 1
+ self.x = x
+ self.endx = x + 1
+ self.y = y
+ self.owner.invalidate_thumbnail( self.note.page, xx, yy, endxx-xx, endyy-yy )
+ else:
+ self.x = x
+ self.endx = x + 1
+ self.y = y
+ self.owner.invalidate_thumbnail( self.note.page, x, y, 1, 1 )
+ else:
+ y = Config.THUMBNAIL_TRACK_RECT[self.note.track][1] + self.owner.pitchToPixels( self.note.cs.pitch )
+ width = max( 1, self.owner.ticksToPixels( self.noteDB.getPage( self.note.page).beats, self.note.cs.duration ) )
+ if x != self.x or y != self.y or width != self.width:
+ if parameter != None: # not the first update
+ xx = min( self.x, x )
+ yy = min( self.y, y )
+ endxx = max( self.endx, x + width )
+ endyy = max( self.y, y ) + 1
+ self.x = x
+ self.endx = x + width
+ self.y = y
+ self.width = width
+ self.owner.invalidate_thumbnail( self.note.page, xx, yy, endxx-xx, endyy-yy )
+ else:
+ self.x = x
+ self.endx = x + width
+ self.y = y
+ self.width = width
+ self.owner.invalidate_thumbnail( self.note.page, x, y, width, 1 )
+
+ def draw( self, win, gc, startX, stopX ):
+ if stopX < self.x: return False # we don't need to draw and no one after us will draw
+ if startX > self.endx: return True # we don't need to draw, but maybe a later note does
+
+ win.draw_line( gc, self.x, self.y, self.endx, self.y )
+
+ return True # we drew something
+
+
+class TuneInterface( gtk.EventBox ):
+
+ DRAG_BLOCK = -1 # block other drag events
+ DRAG_SELECT = 1
+ DRAG_DESELECT = 2
+ DRAG_MOVE = 3
+
+ def __init__( self, noteDB, owner, adjustment ):
+ gtk.EventBox.__init__( self )
+
+ self.noteDB = noteDB
+ self.owner = owner
+ self.adjustment = adjustment
+ #adjustment.connect( "changed", self.adjustmentChanged )
+ adjustment.connect( "value-changed", self.adjustmentValue )
+
+ self.drawingArea = gtk.DrawingArea()
+ self.drawingAreaDirty = False # is the drawingArea waiting to draw?
+ self.add( self.drawingArea )
+ self.dirtyRectToAdd = gtk.gdk.Rectangle() # used by the invalidate_rect function
+
+ self.selectedIds = []
+ self.displayedPage = -1
+
+ self.drumIndex = Config.NUMBER_OF_TRACKS-1
+
+ self.trackRect = Config.THUMBNAIL_TRACK_RECT
+ self.thumbnail = {}
+ self.thumbnailDirty = {}
+ self.thumbnailDirtyRect = {}
+ self.defaultwin = gtk.gdk.get_default_root_window() # used when creating pixmaps
+ self.gc = gtk.gdk.GC( self.defaultwin )
+ colormap = self.drawingArea.get_colormap()
+ self.bgColor = colormap.alloc_color( Config.TOOLBAR_BCK_COLOR, True, True )
+ self.lineColor = colormap.alloc_color( Config.THUMBNAIL_DRAG_COLOR, True, True )
+ self.displayedColor = colormap.alloc_color( Config.THUMBNAIL_DISPLAYED_COLOR, True, True )
+ self.selectedColor = colormap.alloc_color( Config.THUMBNAIL_SELECTED_COLOR, True, True )
+
+ # prepare thumbnail
+ self.thumbnailBG = []
+ self.gc.foreground = self.bgColor
+ for i in range(4):
+ pix = gtk.gdk.pixbuf_new_from_file( Config.IMAGE_ROOT+"pageThumbnailBG%d.png"%i )
+ self.thumbnailBG.append( gtk.gdk.Pixmap( self.defaultwin, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT ) )
+ self.thumbnailBG[i].draw_rectangle( self.gc, True, 0, 0, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+ self.thumbnailBG[i].draw_pixbuf( self.gc, pix, 0, 0, 0, 0, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT, gtk.gdk.RGB_DITHER_NONE )
+
+ # load clipmask
+ pix = gtk.gdk.pixbuf_new_from_file(Config.IMAGE_ROOT+'pageThumbnailMask.png')
+ pixels = pix.get_pixels()
+ stride = pix.get_rowstride()
+ channels = pix.get_n_channels()
+ bitmap = ""
+ byte = 0
+ shift = 0
+ for j in range(pix.get_height()):
+ offset = stride*j
+ for i in range(pix.get_width()):
+ if pixels[i*channels+offset] != "\0":
+ byte += 1 << shift
+ shift += 1
+ if shift > 7:
+ bitmap += "%c" % byte
+ byte = 0
+ shift = 0
+ if shift:
+ bitmap += "%c" % byte
+ byte = 0
+ shift = 0
+ self.clipMask = gtk.gdk.bitmap_create_from_data( None, bitmap, pix.get_width(), pix.get_height() )
+ self.clearMask = gtk.gdk.Rectangle( 0, 0, 1200, 800 )
+
+ self.pageOffset = 5 # offset the first page by this
+ self.dropWidth = 5 # line thickness of the drop head
+ self.dropWidthDIV2 = self.dropWidth//2
+
+ self.pixelsPerPitch = float(self.trackRect[0][3]-1)/(Config.MAXIMUM_PITCH - Config.MINIMUM_PITCH)
+ self.pixelsPerPitchDrum = float(self.trackRect[self.drumIndex][3]-1)/(Config.MAXIMUM_PITCH_DRUM - Config.MINIMUM_PITCH_DRUM )
+ self.pixelsPerTick = [0] + [ float(self.trackRect[0][2]-4)/(i*Config.TICKS_PER_BEAT) for i in range(1,Config.MAXIMUM_BEATS+1) ]
+
+ self.alloced = False
+ self.width = self.baseWidth = self.height = -1
+ self.waitingForAlloc = True
+ self.scrollTo = None
+ self.clickX = -1
+
+ self.set_size_request( self.width, self.height )
+
+ self.button1Down = False
+ self.dragMode = None
+ self.dropAt = -1
+ self.dropAtX = 0
+
+ self.visibleX = 0
+ self.visibleEndX = 0
+
+ self.add_events(gtk.gdk.POINTER_MOTION_MASK|gtk.gdk.POINTER_MOTION_HINT_MASK)
+
+ self.connect( "size-allocate", self.size_allocated )
+ self.drawingArea.connect( "expose-event", self.draw )
+ self.connect( "button-press-event", self.handleButtonPress )
+ self.connect( "button-release-event", self.handleButtonRelease )
+ self.connect( "motion-notify-event", self.handleMotion )
+
+ def size_allocated( self, widget, allocation ):
+ if not self.alloced:
+ self.baseWidth = allocation.width
+ self.visibleEndX = self.baseWidth
+ self.baseHeight = allocation.height
+ self.alloced = True
+ self.updateSize()
+ self.width = allocation.width
+ self.height = allocation.height
+ self.drawingArea.set_size_request( self.width, self.height )
+ self.clearMask.height = self.height
+ self.clearMask.width = self.width
+
+ self.pageY = 2 + (self.height-Config.PAGE_THUMBNAIL_HEIGHT)//2
+
+ if self.scrollTo != None:
+ if self.scrollTo >= 0: self.adjustment.set_value( self.scrollTo )
+ else: self.adjustment.set_value( self.width - self.baseWidth )
+ self.scrollTo = None
+
+ self.waitingForAlloc = False
+
+ def adjustmentValue( self, adj ):
+ self.visibleX = int(adj.value)
+ self.visibleEndX = self.visibleX + self.baseWidth
+
+ def updateSize( self ):
+ width = self.noteDB.getPageCount()*Config.PAGE_THUMBNAIL_WIDTH + 5 # add extra 5 for the first page
+ self.waitingForAlloc = True
+ if width < self.baseWidth:
+ self.pageOffset = ( self.baseWidth - width ) // 2 + 5
+ else:
+ self.pageOffset = 5
+
+ if self.alloced:
+ self.set_size_request( max( self.baseWidth, width), -1 )
+ self.invalidate_rect( self.visibleX, 0, self.baseWidth, self.height )
+
+ def handleButtonPress( self, widget, event ):
+ if event.button != 1:
+ # bring up properties or something
+ return
+
+ self.button1Down = True
+
+ self.owner.abortPredrawPage()
+
+ ind = int(event.x-self.pageOffset)//Config.PAGE_THUMBNAIL_WIDTH
+ if ind >= self.noteDB.getPageCount():
+ if self.dragMode != self.DRAG_MOVE:
+ self.dragMode = self.DRAG_BLOCK
+ return
+ if ind < 0: ind = 0
+
+ self.clickX = event.x
+
+ id = self.noteDB.getPageByIndex( ind )
+
+ if event.type == gtk.gdk._3BUTTON_PRESS: # triple click -> select all
+ self.owner.displayPage( id )
+ self.selectAll()
+ elif event.type == gtk.gdk._2BUTTON_PRESS: # double click -> exclusive select
+ self.owner.displayPage( id )
+ self.selectPage( id )
+ else:
+ if Config.ModKeys.ctrlDown:
+ if id in self.selectedIds: # ctrl click, selected page -> remove page from selection
+ if self.deselectPage( id ):
+ self.dragMode = self.DRAG_DESELECT
+ self.dragLastInd = ind
+ else:
+ self.dragMode = self.DRAG_SELECT # special case, they clicked on the last selected page and it wasn't deselected
+ self.dragLastInd = ind
+ else: # ctrl click, unselected page -> add page to selection (but don't display it)
+ self.selectPage( id, False )
+ self.dragMode = self.DRAG_SELECT
+ self.dragLastInd = ind
+ elif id in self.selectedIds: # click, selected page -> display this page but don't change the selection
+ self.owner.displayPage( id )
+ else: # click, unselected page -> exclusive select
+ self.owner.displayPage( id )
+ self.selectPage( id )
+
+
+ self.owner.setContext( CONTEXT.PAGE )
+
+ def handleButtonRelease( self, widget, event ):
+ if event.button != 1:
+ return
+
+ self.button1Down = False
+
+ if self.dragMode == self.DRAG_MOVE:
+ self.invalidate_rect( self.dropAtX - self.dropWidthDIV2, 0, self.dropWidth, self.height ) # drop head
+
+ if self.dropAt > 0: after = self.noteDB.getPageByIndex( self.dropAt-1 )
+ else: after = False
+
+ self.noteDB.movePages( self.selectedIds, after )
+
+ self.dropAt = -1
+
+ self.dragMode = None
+
+ def handleMotion( self, widget, event ):
+
+ if event.is_hint:
+ x, y, state = self.window.get_pointer()
+ event.x = float(x)
+ event.y = float(y)
+ event.state = state
+
+ if self.button1Down: # clicking
+ if Config.ModKeys.ctrlDown and (self.dragMode == None or self.dragMode == self.DRAG_MOVE):
+ self.dropAt = -1
+ self.dragMode = self.DRAG_SELECT
+ if event.x >= self.pageOffset: ind = int(event.x-self.pageOffset)//Config.PAGE_THUMBNAIL_WIDTH
+ else: ind = 0
+ self.dragLastInd = ind
+
+ if self.dragMode == self.DRAG_SELECT: # select on drag
+ if event.x > self.pageOffset: ind = int(event.x-self.pageOffset)//Config.PAGE_THUMBNAIL_WIDTH
+ else: ind = 0
+ pageCount = self.noteDB.getPageCount()
+ if ind >= pageCount: ind = pageCount-1
+ for i in range( min(ind,self.dragLastInd), max(ind,self.dragLastInd)+1):
+ self.selectPage( self.noteDB.getPageByIndex(i), False )
+ self.dragLastInd = ind
+ elif self.dragMode == self.DRAG_DESELECT: # deselect on drag
+ if event.x > self.pageOffset: ind = int(event.x-self.pageOffset)//Config.PAGE_THUMBNAIL_WIDTH
+ else: ind = 0
+ pageCount = self.noteDB.getPageCount()
+ if ind >= pageCount: ind = pageCount-1
+ for i in range( min(ind,self.dragLastInd), max(ind,self.dragLastInd)+1):
+ self.deselectPage( self.noteDB.getPageByIndex(i) )
+ self.dragLastInd = ind
+ elif self.dragMode == None and abs(self.clickX-event.x) > 20: # drag and drop
+ self.dragMode = self.DRAG_MOVE
+
+ if self.dragMode == self.DRAG_MOVE:
+ if self.dropAt >= 0: lastX = self.dropAtX
+ else: lastX = -1
+ if event.x > self.pageOffset: self.dropAt = int(event.x-self.pageOffset+Config.PAGE_THUMBNAIL_WIDTH_DIV2)//Config.PAGE_THUMBNAIL_WIDTH
+ else: self.dropAt = 0
+ c = self.noteDB.getPageCount()
+ if self.dropAt > c: self.dropAt = c
+ self.dropAtX = self.pageOffset + self.dropAt*Config.PAGE_THUMBNAIL_WIDTH - self.dropWidthDIV2 - 1
+ if lastX >= 0 and lastX != self.dropAtX:
+ if lastX < self.dropAtX:
+ x = lastX - self.dropWidthDIV2
+ w = self.dropAtX - lastX + self.dropWidth
+ else:
+ x = self.dropAtX - self.dropWidthDIV2
+ w = lastX - self.dropAtX + self.dropWidth
+ self.invalidate_rect( x, 0, w, self.height )
+ elif lastX == -1:
+ self.invalidate_rect( self.dropAtX-self.dropWidthDIV2, 0, self.dropWidth, self.height )
+
+ else: # hovering
+ ind = int(event.x-self.pageOffset)//Config.PAGE_THUMBNAIL_WIDTH
+ if ind != self.lastPredrawInd and 0 <= ind < self.noteDB.getPageCount():
+ id = self.noteDB.getPageByIndex(ind)
+ if id != self.displayedPage:
+ self.owner.predrawPage( id )
+ self.lastPredrawInd = ind
+
+
+ def trackToggled( self, i ):
+ self.invalidate_rect( self.visibleX, 0, self.baseWidth, self.height )
+
+ def displayPage( self, id ):
+ if self.displayedPage == id: return -1
+
+ self.lastPredrawInd = -1
+
+ if self.displayedPage != -1:
+ ind = self.noteDB.getPageIndex( self.displayedPage )
+ self.invalidate_rect( self.pageOffset + ind*Config.PAGE_THUMBNAIL_WIDTH, 0, Config.PAGE_THUMBNAIL_WIDTH, self.height )
+
+ if not self.thumbnail.has_key( id ):
+ # premptive add
+ self.thumbnail[id] = gtk.gdk.Pixmap( self.defaultwin, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+ self.thumbnailDirtyRect[id] = gtk.gdk.Rectangle( 0, 0, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+ self.thumbnailDirty[id] = True
+ self.selectPage( id )
+ self.updateSize()
+
+ self.displayedPage = id
+
+ if id not in self.selectedIds:
+ self.selectPage( id )
+
+ ind = self.noteDB.getPageIndex( id )
+
+ startX = self.pageOffset + ind*Config.PAGE_THUMBNAIL_WIDTH
+ stopX = startX + Config.PAGE_THUMBNAIL_WIDTH
+
+ if self.adjustment.value > startX:
+ scroll = startX + Config.PAGE_THUMBNAIL_WIDTH + Config.PAGE_THUMBNAIL_WIDTH_DIV2 - self.baseWidth
+ if scroll < 0: scroll = 0
+ self.adjustment.set_value( scroll )
+ elif self.adjustment.value + self.baseWidth < stopX:
+ scroll = startX - Config.PAGE_THUMBNAIL_WIDTH_DIV2
+ if scroll + self.baseWidth > self.width:
+ if self.waitingForAlloc:
+ self.scrollTo = -1
+ else:
+ self.adjustment.set_value( self.width - self.baseWidth )
+ else:
+ if self.waitingForAlloc:
+ self.scrollTo = scroll
+ else:
+ self.adjustment.set_value( scroll )
+
+ self.invalidate_rect( startX, 0, Config.PAGE_THUMBNAIL_WIDTH, self.height )
+
+ def selectPage( self, id, exclusive = True ):
+ if exclusive:
+ self._clearSelection()
+
+ if id in self.selectedIds: return False # no change
+
+ ind = self.noteDB.getPageIndex( id )
+ l = len(self.selectedIds)
+ i = 0 # in case len(self.selectedIds) == 0
+ while i < l:
+ if self.noteDB.getPageIndex( self.selectedIds[i] ) > ind: break
+ i += 1
+
+ self.selectedIds.insert( i, id )
+
+ self.invalidate_rect( self.pageOffset + ind*Config.PAGE_THUMBNAIL_WIDTH, 0, Config.PAGE_THUMBNAIL_WIDTH, self.height )
+
+ self.owner.updatePageSelection( self.selectedIds )
+
+ return True # page added to selection
+
+ def deselectPage( self, id, force = False, skip_redraw = False, noUpdate = False ):
+ if not id in self.selectedIds: return False # page isn't selected
+
+ if not force:
+ if len(self.selectedIds) <= 1: return False # don't deselect the last page
+
+ if self.displayedPage == id:
+ i = self.selectedIds.index(id)
+ if i == 0: self.owner.displayPage( self.selectedIds[1] )
+ else: self.owner.displayPage( self.selectedIds[i-1] )
+
+ self.selectedIds.remove( id )
+ if not skip_redraw:
+ ind = self.noteDB.getPageIndex( id )
+ self.invalidate_rect( self.pageOffset + ind*Config.PAGE_THUMBNAIL_WIDTH, 0, Config.PAGE_THUMBNAIL_WIDTH, self.height )
+
+ if not noUpdate:
+ self.owner.updatePageSelection( self.selectedIds )
+
+ return True # page removed from the selection
+
+ def selectPages( self, which ):
+ self._clearSelection()
+ self.selectedIds += which
+
+ self.owner.updatePageSelection( self.selectedIds )
+
+ def selectAll( self ):
+ self.selectedIds = self.noteDB.getTune()[:]
+ self.invalidate_rect( self.visibleX, 0, self.baseWidth, self.height )
+
+ self.owner.updatePageSelection( self.selectedIds )
+
+ def _clearSelection( self ):
+ self.selectedIds = []
+ self.invalidate_rect( self.visibleX, 0, self.baseWidth, self.height )
+
+ def getSelectedIds( self ):
+ return self.selectedIds
+
+ def getDisplayedIndex( self ):
+ return self.selectedIds.index( self.displayedPage )
+
+ def getFirstSelected( self ):
+ return self.selectedIds[0]
+
+ def getLastSelected( self ):
+ return self.selectedIds[-1]
+
+ #=======================================================
+ # NoteDB notifications
+
+ def notifyPageAdd( self, id, at ):
+ if not self.thumbnail.has_key(id):
+ self.thumbnail[id] = gtk.gdk.Pixmap( self.defaultwin, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+ self.thumbnailDirtyRect[id] = gtk.gdk.Rectangle( 0, 0, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+ self.thumbnailDirty[id] = True
+ self.selectPage( id )
+ self.updateSize()
+
+ def notifyPageDelete( self, which, safe ):
+ if self.displayedPage in which:
+ noUpdate = True
+ else:
+ noUpdate = False
+ for id in self.selectedIds:
+ if id in which:
+ self.deselectPage( id, True, True, noUpdate )
+ for id in which:
+ del self.thumbnail[id]
+ del self.thumbnailDirtyRect[id]
+ del self.thumbnailDirty[id]
+ if self.displayedPage in which:
+ self.displayedPage = -1
+ self.updateSize()
+
+ def notifyPageDuplicate( self, new, at ):
+ for id in new:
+ self.thumbnail[new[id]] = gtk.gdk.Pixmap( self.defaultwin, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+ self.thumbnailDirtyRect[new[id]] = gtk.gdk.Rectangle( 0, 0, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+ self.thumbnailDirty[new[id]] = True
+ self.updateSize()
+
+ def notifyPageMove( self, which, low, high ):
+ self.invalidate_rect( self.visibleX, 0, self.baseWidth, self.height )
+
+ def notifyPageUpdate( self, page, parameter, value ):
+ if parameter == PARAMETER.PAGE_BEATS:
+ notes = self.noteDB.getNotesByPage( page, self )
+ for note in notes:
+ note.updateParameter( -1, -1 ) # force update transform
+
+ elif parameter == PARAMETER.PAGE_COLOR:
+ self.invalidate_thumbnail( page, 0, 0, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+
+ #=======================================================
+ # Drawing
+
+ def drawThumbnail( self, id, pixmap, rect ):
+ startX = rect.x
+ startY = rect.y
+ stopX = rect.x + rect.width
+ stopY = rect.y + rect.height
+
+ # draw background
+ pixmap.draw_drawable( self.gc, self.thumbnailBG[self.noteDB.getPage(id).color], startX, startY, startX, startY, rect.width, rect.height+1 )
+
+ # draw regular tracks
+ self.gc.foreground = self.lineColor
+ self.gc.set_line_attributes( 1, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_BUTT, gtk.gdk.JOIN_MITER )
+ for i in range(self.drumIndex):
+ if startY >= self.trackRect[i+1][1]: continue
+ if stopY < self.trackRect[i][1]: break
+
+ # draw notes
+ notes = self.noteDB.getNotesByTrack( id, i, self )
+ for n in range( len(notes) ):
+ if not notes[n].draw( pixmap, self.gc, startX, stopX ): break
+ # drum track
+ if stopY > self.trackRect[self.drumIndex][0]:
+ # draw notes
+ notes = self.noteDB.getNotesByTrack( id, self.drumIndex, self )
+ for n in range( len(notes) ):
+ if not notes[n].draw( pixmap, self.gc, startX, stopX ): break
+
+ self.thumbnailDirty[id] = False
+
+
+ def draw( self, drawingArea, event ):
+
+ startX = event.area.x
+ startY = event.area.y
+ stopX = event.area.x + event.area.width
+ stopY = event.area.y + event.area.height
+
+ self.gc.set_clip_rectangle( self.clearMask )
+
+ # draw background
+ self.gc.foreground = self.bgColor
+ drawingArea.window.draw_rectangle( self.gc, True, startX, startY, event.area.width, event.area.height )
+
+ tracks = [ self.owner.getTrackSelected(i) for i in range(Config.NUMBER_OF_TRACKS) ]
+
+ # draw pages
+ self.gc.set_clip_mask( self.clipMask )
+
+ x = self.pageOffset
+ endx = x + Config.PAGE_THUMBNAIL_WIDTH
+ for pageId in self.noteDB.getTune():
+ if endx < startX:
+ x = endx
+ endx += Config.PAGE_THUMBNAIL_WIDTH
+ continue
+ if x > stopX: break
+
+ # draw thumbnail
+ if self.thumbnailDirty[pageId]:
+ self.gc.set_clip_origin( 0, 0 )
+ self.drawThumbnail( pageId, self.thumbnail[pageId], self.thumbnailDirtyRect[pageId] )
+ self.gc.set_clip_origin( x, self.pageY )
+ drawingArea.window.draw_drawable( self.gc, self.thumbnail[pageId], 0, 0, x, self.pageY, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+
+ # draw border if necessary
+ if pageId == self.displayedPage: # displayed page border
+ self.gc.set_function( gtk.gdk.INVERT )
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if tracks[i]:
+ drawingArea.window.draw_rectangle( self.gc, True, x + self.trackRect[i][0], self.pageY + self.trackRect[i][1], self.trackRect[i][2], self.trackRect[i][3] )
+ self.gc.set_function( gtk.gdk.COPY )
+ self.gc.foreground = self.displayedColor
+ self.gc.set_clip_origin( x - Config.PAGE_THUMBNAIL_WIDTH, self.pageY )
+ drawingArea.window.draw_rectangle( self.gc, True, x, self.pageY, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+ elif pageId in self.selectedIds: # selected page border
+ self.gc.set_function( gtk.gdk.INVERT )
+ for i in range(Config.NUMBER_OF_TRACKS):
+ if tracks[i]:
+ drawingArea.window.draw_rectangle( self.gc, True, x + self.trackRect[i][0], self.pageY + self.trackRect[i][1], self.trackRect[i][2], self.trackRect[i][3] )
+ self.gc.set_function( gtk.gdk.COPY )
+ self.gc.foreground = self.selectedColor
+ self.gc.set_clip_origin( x - Config.PAGE_THUMBNAIL_WIDTH, self.pageY )
+ drawingArea.window.draw_rectangle( self.gc, True, x, self.pageY, Config.PAGE_THUMBNAIL_WIDTH, Config.PAGE_THUMBNAIL_HEIGHT )
+
+ x += Config.PAGE_THUMBNAIL_WIDTH
+
+ # draw drop marker
+ if self.dropAt >= 0:
+ self.gc.set_clip_rectangle( self.clearMask )
+ self.gc.set_line_attributes( self.dropWidth, gtk.gdk.LINE_SOLID, gtk.gdk.CAP_ROUND, gtk.gdk.JOIN_MITER )
+ self.gc.foreground = self.lineColor
+ drawingArea.window.draw_line( self.gc, self.dropAtX, self.pageY+2, self.dropAtX, self.pageY+Config.PAGE_THUMBNAIL_HEIGHT-4 )
+
+ def invalidate_rect( self, x, y, width, height ):
+ if self.alloced == False: return
+ if x < self.visibleX: x = self.visibleX
+ if x + width > self.visibleEndX: width = self.visibleEndX - x
+ if width <= 0: return
+
+ self.dirtyRectToAdd.x = x
+ self.dirtyRectToAdd.y = y
+ self.dirtyRectToAdd.width = width
+ self.dirtyRectToAdd.height = height
+ self.drawingArea.window.invalidate_rect( self.dirtyRectToAdd, True )
+ self.drawingAreaDirty = True
+
+ def invalidate_thumbnail( self, id, x, y, width, height ):
+ if not self.thumbnailDirty[id]:
+ self.thumbnailDirtyRect[id].x = x
+ self.thumbnailDirtyRect[id].y = y
+ self.thumbnailDirtyRect[id].width = width
+ self.thumbnailDirtyRect[id].height = height
+ self.thumbnailDirty[id] = True
+ else:
+ self.dirtyRectToAdd.x = x
+ self.dirtyRectToAdd.y = y
+ self.dirtyRectToAdd.width = width
+ self.dirtyRectToAdd.height = height
+ self.thumbnailDirtyRect[id] = self.thumbnailDirtyRect[id].union( self.dirtyRectToAdd )
+
+ ind = self.noteDB.getPageIndex( id )
+ self.invalidate_rect( self.pageOffset + ind*Config.PAGE_THUMBNAIL_WIDTH, 0, Config.PAGE_THUMBNAIL_WIDTH, self.height )
+
+ def ticksToPixels( self, beats, ticks ):
+ return int(round( ticks * self.pixelsPerTick[beats] ))
+ def pitchToPixels( self, pitch ):
+ return int(round( ( Config.MAXIMUM_PITCH - pitch ) * self.pixelsPerPitch ))
+ def pitchToPixelsDrum( self, pitch ):
+ return int(round( ( Config.MAXIMUM_PITCH_DRUM - pitch ) * self.pixelsPerPitchDrum ))
diff --git a/TamTamEdit.activity/Edit/__init__.py b/TamTamEdit.activity/Edit/__init__.py
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/TamTamEdit.activity/Edit/__init__.py
diff --git a/TamTamEdit.activity/Edit/rm/BackgroundView.py b/TamTamEdit.activity/Edit/rm/BackgroundView.py
new file mode 100644
index 0000000..ff6e75f
--- /dev/null
+++ b/TamTamEdit.activity/Edit/rm/BackgroundView.py
@@ -0,0 +1,501 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+from math import floor
+
+from Framework.Constants import Constants
+from GUI.GUIConstants import GUIConstants
+from GUI.Core.NoteParametersWindow import NoteParametersWindow
+
+from Framework.Core.Profiler import TP
+
+class SELECTNOTES:
+ ALL = -1
+ NONE = 0
+ ADD = 1
+ REMOVE = 2
+ EXCLUSIVE = 3
+
+#-------------------------------------------------------------
+# This is a TEMPORARY implementaion of the BackgroundView,
+# it was written quickly to get track selections working
+#-------------------------------------------------------------
+
+# TODO: Do I really have to subclass gtk.EventBox to get the button-press-event?
+# (I wasn't getting it subclassing directly from DrawingArea)
+class BackgroundView( gtk.EventBox ):
+ #-----------------------------------
+ # initialization
+ #-----------------------------------
+ def __init__( self, trackIDs, selectedTrackIDs, selectionChangedCallback, mutedTrackIDs, beatsPerPageAdjustment, trackDictionary, selectedPageIDs, updatePageCallback ):
+ gtk.EventBox.__init__( self )
+
+ self.drawingArea = gtk.DrawingArea()
+ self.add( self.drawingArea )
+
+ self.sizeInitialized = False
+
+ self.trackViews = {}
+ self.trackIDs = trackIDs
+ self.selectedTrackIDs = selectedTrackIDs
+ self.selectionChangedCallback = selectionChangedCallback
+ self.mutedTrackIDs = mutedTrackIDs
+ self.beatsPerPageAdjustment = beatsPerPageAdjustment
+ self.trackDictionary = trackDictionary
+ self.selectedPageIDs = selectedPageIDs
+ self.updatePageCallback = updatePageCallback
+
+ self.curAction = False # stores the current mouse action
+ self.curActionObject = False # stores the object that in handling the action
+
+ self.buttonPressCount = 1 # used on release events to indicate double/triple releases
+ self.clickLoc = [0,0] # location of the last click
+ self.marqueeLoc = False # current drag location of the marquee
+
+ self.drawingArea.connect( "expose-event", self.draw )
+ self.connect( "button-press-event", self.handleButtonPress )
+ self.connect( "button-release-event", self.handleButtonRelease )
+ self.connect( "motion-notify-event", self.handleMotion )
+
+ #-----------------------------------
+ # access methods
+ #-----------------------------------
+ def getTrackRect( self, trackID ):
+ return gtk.gdk.Rectangle( GUIConstants.BORDER_SIZE,
+ self.getTrackYLocation( trackID ),
+ self.getTrackWidth(),
+ self.getTrackHeight() )
+
+ def getTrackWidth( self ):
+ return self.get_allocation().width - 2 * ( GUIConstants.BORDER_SIZE + 2 )
+
+ def getFullHeight( self ):
+ return int( floor( self.get_allocation().height / len( self.trackIDs ) ) )
+
+ def getTrackHeight( self ):
+ return int( self.getFullHeight() - 2 * self.getTrackSpacing() )
+
+ #TODO-> trackIDs should probably be ordered!
+ # we're just using trackID as an index here (this will only work until you can remove tracks)
+ def getTrackYLocation( self, trackID ):
+ if self.getTrackHeight() < 0:
+ return 0
+ else:
+ trackIndex = trackID
+
+ trackHeight = int( floor( self.get_allocation().height / len( self.trackIDs ) ) )
+ trackBackgroundYValue = trackHeight * trackIndex
+ return trackBackgroundYValue + GUIConstants.BORDER_SIZE
+
+ def getTrackSpacing( self ):
+ return GUIConstants.TRACK_SPACING
+
+ #-----------------------------------
+ # callback methods
+ #-----------------------------------
+ def set_size_request( self, width, height ):
+ self.sizeInitialized = True
+ self.drawingArea.set_size_request( width, height )
+ self.height = height
+ self.width = width
+
+ numTracks = len(self.trackIDs)
+ trackSpacing = self.getTrackSpacing()
+ if numTracks: self.trackHeight = int( floor( (height - trackSpacing*(numTracks-1)) / numTracks ) )
+ else: self.trackHeight = 1
+ self.trackWidth = width
+
+ trackCount = 0
+ for trackID in self.trackIDs:
+ self.trackViews[trackID].set_size_request( self.trackWidth, self.trackHeight )
+ self.trackViews[trackID].setPositionOffset( (0, trackCount*(self.trackHeight+trackSpacing)) )
+ trackCount += 1
+
+ def setCurrentTracks( self, trackViews ):
+
+ oldLen = len(self.trackViews)
+
+ if oldLen and trackViews != self.trackViews: self.clearSelectedNotes() # clear all the currently selected notes
+
+ self.trackViews = trackViews
+
+ numTracks = len(self.trackViews)
+ if oldLen != numTracks and self.sizeInitialized:
+ trackSpacing = self.getTrackSpacing()
+ if numTracks: self.trackHeight = int( floor( (self.height - trackSpacing*(numTracks-1)) / numTracks ) )
+ else: self.trackHeight = 1
+ trackCount = 0
+ for trackID in self.trackIDs:
+ self.trackViews[trackID].set_size_request( self.trackWidth, self.trackHeight )
+ self.trackViews[trackID].setPositionOffset( (0, trackCount*(self.trackHeight+trackSpacing)) )
+ trackCount += 1
+
+ self.dirty()
+
+
+ def getNoteParameters( self ):
+ for trackID in self.selectedTrackIDs:
+ for pageID in self.selectedPageIDs:
+ for note in self.trackDictionary[ trackID ][ pageID ]:
+ newPitch = note.pitch + self.noteParameters.pitchAdjust.value
+ newAmplitude = note.amplitude * self.noteParameters.amplitudeAdjust.value
+ newPan = self.noteParameters.panAdjust.value
+ newReverbSend = note.reverbSend * self.noteParameters.reverbSendAdjust.value
+ newAttack = self.noteParameters.attackAdjust.value
+ newDecay = self.noteParameters.decayAdjust.value
+ newFilterType = self.noteParameters.filterType
+ newFilterCutoff = self.noteParameters.filterCutoff
+ newTied = self.noteParameters.tied
+ newOverlap = self.noteParameters.overlap
+
+ note.pitch = self.noteParametersBoundaries( newPitch, note.pitch, Constants.MINIMUM_PITCH, Constants.MAXIMUM_PITCH )
+ note.amplitude = self.noteParametersBoundaries( newAmplitude, note.amplitude, Constants.MINIMUM_AMPLITUDE, Constants.MAXIMUM_AMPLITUDE )
+ note.reverbSend = self.noteParametersBoundaries( newReverbSend, note.reverbSend, Constants.MINIMUM_AMPLITUDE,
+ Constants.MAXIMUM_AMPLITUDE )
+ if newPan != note.pan:
+ note.pan = newPan
+
+ if newAttack != note.attack:
+ note.attack = newAttack
+
+ if newDecay != note.decay:
+ note.decay = newDecay
+
+ if newFilterType != note.filterType:
+ note.filterType = newFilterType
+
+ if newFilterCutoff != note.filterCutoff:
+ note.filterCutoff = newFilterCutoff
+
+ if newTied != note.tied:
+ note.tied = newTied
+
+ if newOverlap != note.overlap:
+ note.overlap = newOverlap
+
+ self.updatePageCallback()
+
+ def noteParametersBoundaries( self, newValue, noteValue, minBoundary, maxBoundary ):
+ if newValue != noteValue:
+ if newValue >= minBoundary and newValue <= maxBoundary:
+ return newValue
+ elif newValue < minBoundary:
+ return minBoundary
+ elif newValue > maxBoundary:
+ return maxBoundary
+ else:
+ return noteValue
+
+ #-----------------------------------
+ # action and event methods
+ #-----------------------------------
+ def setCurrentAction( self, action, obj ):
+ if self.curAction:
+ print "BackgroundView - Action already in progress!"
+
+ self.curAction = action
+ self.curActionObject = obj
+
+ if action == "note-drag-onset": self.updateDragLimits()
+ elif action == "note-drag-duration": self.updateDragLimits()
+ elif action == "note-drag-pitch": self.updateDragLimits()
+
+ def doneCurrentAction( self ):
+ if self.curAction == "note-drag-onset": self.doneNoteDrag()
+ elif self.curAction == "note-drag-duration": self.doneNoteDrag()
+ elif self.curAction == "note-drag-pitch": self.doneNoteDrag()
+
+ self.curAction = False
+ self.curActionObject = False
+
+ def toggleTrack( self, trackID, exclusive ):
+ if exclusive:
+ self.selectedTrackIDs.clear()
+ self.selectedTrackIDs.add( trackID )
+ else:
+ if trackID in self.selectedTrackIDs:
+ self.selectedTrackIDs.discard( trackID )
+ else:
+ self.selectedTrackIDs.add( trackID )
+
+ def selectionChanged( self ):
+ if self.curAction == "note-drag-onset": self.updateDragLimits()
+ elif self.curAction == "note-drag-duration": self.updateDragLimits()
+ elif self.curAction == "note-drag-pitch": self.updateDragLimits()
+ self.dirty()
+
+ def selectNotesByBar( self, selID, startX, stopX ):
+ beatCount = int(round( self.beatsPerPageAdjustment.value, 0 ))
+ for trackID in self.trackIDs:
+ if trackID == selID:
+ notes = self.trackViews[trackID].getNotesByBar( beatCount, startX, stopX )
+ self.trackViews[trackID].selectNotes( SELECTNOTES.EXCLUSIVE, notes )
+ else:
+ self.trackViews[trackID].selectNotes( SELECTNOTES.NONE, [] )
+ self.selectionChanged()
+
+ def selectNotesByTrack( self, selID ):
+ for trackID in self.trackIDs:
+ if trackID == selID: self.trackViews[trackID].selectNotes( SELECTNOTES.ALL, [] )
+ else: self.trackViews[trackID].selectNotes( SELECTNOTES.NONE, [] )
+ self.selectionChanged()
+
+ def selectNotes( self, noteDic ):
+ for trackID in self.trackIDs:
+ if trackID in noteDic: self.trackViews[trackID].selectNotes( SELECTNOTES.EXCLUSIVE, noteDic[trackID] )
+ else: self.trackViews[trackID].selectNotes( SELECTNOTES.NONE, [] )
+ self.selectionChanged()
+
+ def addNotesToSelection( self, noteDic ):
+ for trackID in self.trackIDs:
+ if trackID in noteDic: self.trackViews[trackID].selectNotes( SELECTNOTES.ADD, noteDic[trackID] )
+ self.selectionChanged()
+
+ def deselectNotes( self, noteDic ):
+ for trackID in self.trackIDs:
+ if trackID in noteDic: self.trackViews[trackID].selectNotes( SELECTNOTES.REMOVE, noteDic[trackID] )
+ self.selectionChanged()
+
+ def clearSelectedNotes( self ):
+ for trackID in self.trackIDs:
+ self.trackViews[trackID].selectNotes( SELECTNOTES.NONE, [] )
+ self.selectionChanged()
+
+ def updateDragLimits( self ):
+ self.dragLimits = [ [-9999,9999], [-9999,9999], [-9999,9999] ] # initialize to big numbers!
+ for trackID in self.trackIDs:
+ self.trackViews[trackID].updateDragLimits( self.dragLimits )
+
+ def noteDragOnset( self, event ):
+ dx = event.x - self.clickLoc[0]
+ dx = min( self.dragLimits[0][1], max( self.dragLimits[0][0], dx ) )
+ dy = 0
+ dw = 0
+
+ for trackID in self.trackIDs:
+ self.trackViews[trackID].noteDrag( self, dx, dy, dw )
+ self.dirty()
+
+ def noteDragDuration( self, event ):
+ dx = 0
+ dy = 0
+ dw = event.x - self.clickLoc[0]
+ dw = min( self.dragLimits[2][1], max( self.dragLimits[2][0], dw ) )
+
+ for trackID in self.trackIDs:
+ self.trackViews[trackID].noteDrag( self, dx, dy, dw )
+ self.dirty()
+
+ def noteDragPitch( self, event ):
+ dx = 0
+ dy = event.y - self.clickLoc[1]
+ dy = min( self.dragLimits[1][1], max( self.dragLimits[1][0], dy ) )
+ dw = 0
+
+ for trackID in self.trackIDs:
+ self.trackViews[trackID].noteDrag( self, dx, dy, dw )
+ self.dirty()
+
+ def doneNoteDrag( self ):
+ for trackID in self.trackIDs:
+ self.trackViews[trackID].doneNoteDrag( self )
+
+ def updateMarquee( self, event ):
+ self.marqueeLoc = [ event.x, event.y ]
+ parentRect = self.get_allocation()
+ if self.marqueeLoc[0] < 0: self.marqueeLoc[0] = 0
+ elif self.marqueeLoc[0] > parentRect.width: self.marqueeLoc[0] = parentRect.width
+ if self.marqueeLoc[1] < 0: self.marqueeLoc[1] = 0
+ elif self.marqueeLoc[1] > parentRect.height: self.marqueeLoc[1] = parentRect.height
+
+ self.dirty()
+
+ def doneMarquee( self, event ):
+ if self.marqueeLoc:
+ start = [ min(self.clickLoc[0],self.marqueeLoc[0]), \
+ min(self.clickLoc[1],self.marqueeLoc[1]) ]
+ stop = [ max(self.clickLoc[0],self.marqueeLoc[0]), \
+ max(self.clickLoc[1],self.marqueeLoc[1]) ]
+
+ select = {}
+
+ trackSpacing = self.getTrackSpacing()
+ trackTop = 0
+ for trackID in self.trackIDs:
+ notes = self.trackViews[trackID].handleMarqueeSelect( self, start, stop )
+ if notes: select[trackID] = notes
+ trackTop += self.trackHeight + trackSpacing
+ if trackTop > stop[1]: break
+
+ self.selectNotes( select )
+
+ self.marqueeLoc = False
+ self.doneCurrentAction()
+
+ self.dirty()
+
+ def handleButtonPress( self, drawingArea, event ):
+
+ TP.ProfileBegin( "BV::handleButtonPress" )
+
+ if event.type == gtk.gdk._2BUTTON_PRESS: self.buttonPressCount = 2
+ elif event.type == gtk.gdk._3BUTTON_PRESS: self.buttonPressCount = 3
+ else: self.buttonPressCount = 1
+
+ self.clickLoc = [ event.x, event.y ]
+
+ trackSpacing = self.getTrackSpacing()
+ trackTop = 0
+ for trackID in self.trackIDs:
+ handled = self.trackViews[trackID].handleButtonPress( self, event )
+ trackTop += self.trackHeight + trackSpacing
+ if handled or trackTop > event.y: break
+
+ if handled:
+ if not self.curAction: self.curAction = True # it was handled maybe no action was declared, set curAction to True anyway
+ TP.ProfileEnd( "BV::handleButtonPress" )
+ return
+
+ if event.button == 3:
+ self.noteParameters = NoteParametersWindow( self.trackDictionary, self.getNoteParameters )
+ self.setCurrentAction( "noteParameters", False )
+
+ TP.ProfileEnd( "BV::handleButtonPress" )
+
+
+ def handleButtonRelease( self, drawingArea, event ):
+ TP.ProfileBegin( "BV::handleButtonRelease" )
+
+ if not self.curAction: #do track selection stuff here so that we can also handle marquee selection
+ trackSpacing = self.getTrackSpacing()
+ trackTop = 0
+ for trackID in self.trackIDs:
+ handled = self.trackViews[trackID].handleButtonRelease( self, event, self.buttonPressCount )
+ trackTop += self.trackHeight + trackSpacing
+ if handled or trackTop > event.y: break
+
+ if handled: self.dirty()
+
+ TP.ProfileEnd( "BV::handleButtonRelease" )
+ return
+
+ if not self.curActionObject: # there was no real action to carry out
+ self.curAction = False
+ TP.ProfileEnd( "BV::handleButtonRelease" )
+ return
+
+ if self.curActionObject != self:
+ if self.curActionObject.handleButtonRelease( self, event, self.buttonPressCount ):
+ self.dirty()
+ TP.ProfileEnd( "BV::handleButtonRelease" )
+ return
+
+
+ # we're doing the action ourselves
+
+ if self.curAction == "marquee": self.doneMarquee( event )
+
+ TP.ProfileEnd( "BV::handleButtonRelease" )
+ return
+
+ def handleMotion( self, drawingArea, event ):
+ TP.ProfileBegin( "BV::handleMotion" )
+
+ if not self.curAction: # no action is in progress yet we're dragging, start a marquee
+ self.setCurrentAction( "marquee", self )
+
+ if self.curAction == "note-drag-onset":
+ self.noteDragOnset( event )
+ TP.ProfileEnd( "BV::handleMotion" )
+ return
+
+ if self.curAction == "note-drag-duration":
+ self.noteDragDuration( event )
+ TP.ProfileEnd( "BV::handleMotion" )
+ return
+
+ if self.curAction == "note-drag-pitch":
+ self.noteDragPitch( event )
+ TP.ProfileEnd( "BV::handleMotion" )
+ return
+
+ # we're doing the action ourselves
+
+ if self.curAction == "marquee": self.updateMarquee( event )
+
+ TP.ProfileEnd( "BV::handleMotion" )
+ return
+
+ def TEMPOLDSTUFF(self):
+
+ #TODO change this to accomodate the space between tracks
+ trackHeight = ( drawingArea.get_allocation().height - 1 ) / len( self.trackIDs )
+ trackID = int( floor( event.y / trackHeight ) )
+
+ if event.type == gtk.gdk.BUTTON_PRESS:
+ #single click toggles track selection
+ if trackID in self.selectedTrackIDs:
+ self.selectedTrackIDs.discard( trackID )
+ else:
+ self.selectedTrackIDs.add( trackID )
+ elif event.type == gtk.gdk._2BUTTON_PRESS:
+ #double click selects a single track
+ self.selectedTrackIDs.clear()
+ self.selectedTrackIDs.add( trackID )
+
+ self.drawingArea.queue_draw()
+ self.selectionChangedCallback()
+ if event.button == 3:
+ self.noteParameters = NoteParametersWindow( self.trackDictionary, self.getNoteParameters )
+
+ #-----------------------------------
+ # drawing methods
+ #-----------------------------------
+ def draw( self, drawingArea, event ):
+ TP.ProfileBegin( "BackgroundView::draw" )
+
+ context = drawingArea.window.cairo_create()
+ context.set_antialias(0) # I don't know what to set this to to turn it off, and it doesn't seem to work anyway!?
+
+ #parentRect = self.get_allocation()
+
+ beatCount = int(round( self.beatsPerPageAdjustment.value, 0 ))
+
+ for trackID in self.trackIDs:
+ self.trackViews[trackID].draw( context,
+ beatCount,
+ trackID in self.selectedTrackIDs )
+
+ # if self.curAction == "note-drag": # draw a cross at clickLoc
+ # lineW = 1
+ # context.set_line_width( lineW )
+ # lineWDIV2 = lineW/2.0
+ # context.set_source_rgb( 1, 1, 1 )
+
+ # context.move_to( self.clickLoc[0] + lineWDIV2 - 3, self.clickLoc[1] + lineWDIV2 )
+ # context.rel_line_to( 6, 0 )
+ # context.stroke()
+ # context.move_to( self.clickLoc[0] + lineWDIV2, self.clickLoc[1] + lineWDIV2 - 3)
+ # context.rel_line_to( 0, 6 )
+ # context.stroke()
+
+ if self.marqueeLoc: # draw the selection rect
+ lineW = 1
+ context.set_line_width( lineW )
+ lineWDIV2 = lineW/2.0
+
+ context.move_to( self.clickLoc[0] + lineWDIV2, self.clickLoc[1] + lineWDIV2 )
+ context.line_to( self.marqueeLoc[0] + lineWDIV2, self.clickLoc[1] + lineWDIV2 )
+ context.line_to( self.marqueeLoc[0] + lineWDIV2, self.marqueeLoc[1] + lineWDIV2 )
+ context.line_to( self.clickLoc[0] + lineWDIV2, self.marqueeLoc[1] + lineWDIV2 )
+ context.close_path()
+ context.set_source_rgb( 1, 1, 1 )
+ context.stroke()
+
+ TP.ProfileEnd( "BackgroundView::draw" )
+
+ def dirty( self ):
+ self.queue_draw()
+
+
diff --git a/TamTamEdit.activity/Edit/rm/NoteView.py b/TamTamEdit.activity/Edit/rm/NoteView.py
new file mode 100644
index 0000000..ac139a1
--- /dev/null
+++ b/TamTamEdit.activity/Edit/rm/NoteView.py
@@ -0,0 +1,253 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+from Framework.Constants import Constants
+from Framework.CSound.CSoundConstants import CSoundConstants
+from GUI.GUIConstants import GUIConstants
+from GUI.Core.NoteParametersWindow import NoteParametersWindow
+
+from BackgroundView import SELECTNOTES
+
+#----------------------------------------------------------------------
+# TODO: currently we are only using CSoundNotes,
+# - this should updated to handle generic Note objects
+#----------------------------------------------------------------------
+
+#----------------------------------------------------------------------
+# A view for the (CSound)Note class
+#----------------------------------------------------------------------
+class NoteView:
+ #-----------------------------------
+ # initialization
+ # TODO: not sure if passing in beatsPerPageAdjustment is the best way to go about it
+ #-----------------------------------
+ def __init__( self, note, track, beatsPerPageAdjustment ):
+ self.note = note
+
+ self.track = track
+ self.beatsPerPageAdjustment = beatsPerPageAdjustment
+ self.posOffset = (0,0)
+
+ self.baseOnset = self.basePitch = self.baseDuration = self.lastDragX = self.lastDragY = self.lastDragW = 0 # note dragging properties
+
+ self.sampleNote = None
+
+ self.parentSize = False
+
+ self.selected = False
+ self.potentialDeselect = False
+
+
+ def getNoteParameters( self ):
+ self.note.pitch = self.noteParameters.pitchAdjust.value
+ self.note.amplitude = self.noteParameters.amplitudeAdjust.value
+ self.note.pan = self.noteParameters.panAdjust.value
+ self.note.attack = self.noteParameters.attackAdjust.value
+ self.note.decay = self.noteParameters.decayAdjust.value
+ self.note.reverbSend = self.noteParameters.reverbSendAdjust.value
+ self.note.filterType = self.noteParameters.filterType
+ self.note.filterCutoff = self.noteParameters.filterCutoff
+ self.note.tied = self.noteParameters.tied
+ self.note.overlap = self.noteParameters.overlap
+
+ def handleButtonPress( self, emitter, event ):
+ eX = event.x - self.x
+ eY = event.y - self.y
+
+ if eX < 0 or eX > self.width \
+ or eY < 0 or eY > self.height:
+ return False
+
+ if event.button == 3:
+ self.noteParameters = NoteParametersWindow( self.note, self.getNoteParameters )
+ return True
+
+ if event.type == gtk.gdk._2BUTTON_PRESS: # select bar
+ self.potentialDeselect = False
+ emitter.selectNotesByBar( self.track.getID(), self.x, self.x+self.width )
+ elif event.type == gtk.gdk._3BUTTON_PRESS: # select track
+ self.potentialDeselect = False
+ emitter.selectNotesByTrack( self.track.getID() )
+ else:
+ if self.getSelected(): # we already selected, might want to delected
+ self.potentialDeselect = True
+ else:
+ emitter.selectNotes( { self.track.getID(): [ self ] } )
+ self.updateSampleNote( self.note.pitch )
+
+ percent = eX/self.width
+ if percent < 0.3: emitter.setCurrentAction( "note-drag-onset", self )
+ elif percent > 0.7: emitter.setCurrentAction( "note-drag-duration", self )
+ else: emitter.setCurrentAction( "note-drag-pitch", self )
+
+
+ emitter.dirty()
+
+ return True
+
+ def handleButtonRelease( self, emitter, event, buttonPressCount ):
+
+ if self.potentialDeselect:
+ self.potentialDeselect = False
+ emitter.deselectNotes( { self.track.getID(): [ self ] } )
+
+ self.clearSampleNote()
+
+ emitter.doneCurrentAction()
+
+ return True
+
+ def noteDrag( self, emitter, dx, dy, dw ):
+ self.potentialDeselect = False
+
+ ticksPerPixel = (round( self.beatsPerPageAdjustment.value, 0 ) * Constants.TICKS_PER_BEAT) / self.parentSize[0]
+ stepPerPixel = (Constants.NUMBER_OF_POSSIBLE_PITCHES-1) / (self.parentSize[1] - self.height)
+ pitchPerStep = (Constants.MAXIMUM_PITCH-Constants.MINIMUM_PITCH)/(Constants.NUMBER_OF_POSSIBLE_PITCHES-1)
+
+ if dx != self.lastDragX:
+ self.lastDragX = dx
+ self.note.onset = self.baseOnset + int(dx*ticksPerPixel)
+
+ if dy != self.lastDragY:
+ self.lastDragY = dy
+ newPitch = self.basePitch + round(-dy*stepPerPixel)*pitchPerStep
+ self.note.pitch = newPitch
+ self.updateSampleNote( newPitch )
+
+ if dw != self.lastDragW:
+ self.lastDragW = dw
+ self.note.duration = self.baseDuration + int(dw*ticksPerPixel)
+
+ self.updateTransform( False )
+
+ def doneNoteDrag( self, emitter ):
+ self.baseOnset = self.note.onset
+ self.basePitch = self.note.pitch
+ self.baseDuration = self.note.duration
+
+ self.lastDragX = 0
+ self.lastDragY = 0
+ self.lastDragW = 0
+
+ self.clearSampleNote()
+
+
+ def handleMarqueeSelect( self, emitter, start, stop ):
+ intersectionY = [ max(start[1],self.y), min(stop[1],self.y+self.height) ]
+ if intersectionY[0] > intersectionY[1]:
+ return False
+
+ intersectionX = [ max(start[0],self.x), min(stop[0],self.x+self.width) ]
+ if intersectionX[0] > intersectionX[1]:
+ return False
+
+ return True
+
+ #-----------------------------------
+ # draw
+ #-----------------------------------
+
+ def setPositionOffset( self, offset ):
+ self.posOffset = offset
+ if self.parentSize: self.updateTransform( False )
+
+ def draw( self, context ):
+ lineW = GUIConstants.NOTE_BORDER_SIZE
+ lineWDIV2 = lineW/2.0
+ context.set_line_width( lineW )
+
+ context.move_to( self.x + lineWDIV2, self.y + lineWDIV2 )
+ context.rel_line_to( self.width - lineW, 0 )
+ context.rel_line_to( 0, self.height - lineW )
+ context.rel_line_to( -self.width + lineW, 0 )
+ context.close_path()
+
+ #background
+ colour = 1 - ( ( self.note.amplitude * 0.7 ) + 0.3 )
+ context.set_source_rgb( colour, colour, colour )
+ context.fill_preserve()
+
+ #border
+ if self.selected: context.set_source_rgb( 1, 1, 1 )
+ else: context.set_source_rgb( 0, 0, 0 )
+ context.stroke()
+
+ #-----------------------------------
+ # update
+ #-----------------------------------
+
+ def updateTransform( self, parentSize ):
+ if parentSize: self.parentSize = parentSize
+ self.width = int( self.parentSize[0] * self.note.duration / (round( self.beatsPerPageAdjustment.value, 0 ) * Constants.TICKS_PER_BEAT) )
+ self.height = round( max( GUIConstants.MINIMUM_NOTE_HEIGHT, self.parentSize[1] / (Constants.NUMBER_OF_POSSIBLE_PITCHES-1) ) )
+ self.x = int( self.parentSize[0] * self.note.onset / (round( self.beatsPerPageAdjustment.value, 0 ) * Constants.TICKS_PER_BEAT) ) \
+ + self.posOffset[0]
+ self.y = round( (self.parentSize[1]-self.height) * ( Constants.MAXIMUM_PITCH - self.note.pitch ) / (Constants.NUMBER_OF_POSSIBLE_PITCHES-1) ) \
+ + self.posOffset[1]
+
+ def checkX( self, startx, stopx ):
+ if self.x >= startx and self.x < stopx: return True
+ else: return False
+
+ def getStartTick( self ):
+ return self.note.onset
+
+ def getEndTick( self ):
+ return self.note.onset + self.note.duration
+
+ def updateDragLimits( self, dragLimits, leftBound, rightBound, widthBound ):
+ pixelsPerTick = self.parentSize[0] / (round( self.beatsPerPageAdjustment.value, 0 ) * Constants.TICKS_PER_BEAT)
+ pixelsPerPitch = (self.parentSize[1] - self.height) / (Constants.MAXIMUM_PITCH - Constants.MINIMUM_PITCH)
+ left = (leftBound - self.note.onset) * pixelsPerTick
+ right = (rightBound - self.note.duration - self.note.onset) * pixelsPerTick
+ up = (self.note.pitch - Constants.MAXIMUM_PITCH) * pixelsPerPitch
+ down = (self.note.pitch - Constants.MINIMUM_PITCH) * pixelsPerPitch
+ short = (Constants.MINIMUM_NOTE_DURATION - self.note.duration) * pixelsPerTick
+ long = (widthBound - self.note.duration - self.note.onset) * pixelsPerTick
+
+ if dragLimits[0][0] < left: dragLimits[0][0] = left
+ if dragLimits[0][1] > right: dragLimits[0][1] = right
+ if dragLimits[1][0] < up: dragLimits[1][0] = up
+ if dragLimits[1][1] > down: dragLimits[1][1] = down
+ if dragLimits[2][0] < short: dragLimits[2][0] = short
+ if dragLimits[2][1] > long: dragLimits[2][1] = long
+
+ # store the current loc as a reference point
+ self.baseOnset = self.note.onset
+ self.basePitch = self.note.pitch
+ self.baseDuration = self.note.duration
+
+ def updateSampleNote( self, pitch ):
+ if self.sampleNote == None:
+ self.sampleNote = self.note.clone()
+ #TODO clean this up:
+ if CSoundConstants.INSTRUMENTS[ self.sampleNote.instrumentFlag ].csoundInstrumentID == 103:
+ self.sampleNote.duration = 100
+ else:
+ self.sampleNote.duration = -1
+ self.sampleNote.play()
+
+ elif self.sampleNote.pitch != pitch:
+ self.sampleNote.pitch = pitch
+ self.sampleNote.play()
+
+ def clearSampleNote( self ):
+ if self.sampleNote != None:
+ self.sampleNote.duration = 0
+ self.sampleNote.play()
+ del self.sampleNote
+ self.sampleNote = None
+
+ #-----------------------------------
+ # Selection
+ #-----------------------------------
+
+ def setSelected( self, state ):
+ if self.selected != state:
+ self.selected = state
+ return True # state changed
+ return False # state is the same
+
+ def getSelected( self ):
+ return self.selected
diff --git a/TamTamEdit.activity/Edit/rm/PageBankView.py b/TamTamEdit.activity/Edit/rm/PageBankView.py
new file mode 100644
index 0000000..fedadef
--- /dev/null
+++ b/TamTamEdit.activity/Edit/rm/PageBankView.py
@@ -0,0 +1,85 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+from GUI.GUIConstants import GUIConstants
+from GUI.Core.PageView import PageView
+
+class PageBankView( gtk.Frame ):
+
+ NO_PAGE = -1
+
+ def __init__( self, selectPageCallback, pageDropCallback ):
+ gtk.Frame.__init__( self )
+ self.table = gtk.Table( 1, GUIConstants.NUMBER_OF_PAGE_BANK_COLUMNS )
+ self.add( self.table )
+ self.drag_dest_set( gtk.DEST_DEFAULT_ALL, [ ( "tune page", gtk.TARGET_SAME_APP, 11 )], gtk.gdk.ACTION_COPY|gtk.gdk.ACTION_MOVE )
+ self.connect( "drag_data_received", self.dragDataReceived )
+ self.selectPageCallback = selectPageCallback
+ self.pageDropCallback = pageDropCallback
+ self.selectedPageIds = set([])
+ self.pageIndexDictionary = {}
+ self.pageViews = {}
+
+ def dragDataReceived( self, widget, context, x, y, selectionData, info, time):
+ self.pageDropCallback( selectionData.data )
+
+ def addPage( self, pageId, invokeCallback = True ):
+ pageIndex = len( self.pageViews.keys() )
+ self.pageIndexDictionary[ pageIndex ] = pageId
+
+ #TODO: resize table to suit number of pages?
+ #if pageIndex > ( self.table.n-rows * self.table.n_columns ):
+ # self.table.resize( self.table.n_rows + 1, self.table.n_columns )
+
+ pageView = PageView( pageIndex, self.selectPage, True )
+ self.pageViews[ pageIndex ] = pageView
+
+ columnIndex = pageIndex % GUIConstants.NUMBER_OF_PAGE_BANK_COLUMNS
+ rowIndex = int( pageIndex / GUIConstants.NUMBER_OF_PAGE_BANK_COLUMNS )
+ self.table.attach( pageView, columnIndex, columnIndex + 1, rowIndex, rowIndex + 1, gtk.SHRINK, gtk.SHRINK )
+
+ self.updateSize( pageView )
+
+ pageView.drag_source_set( gtk.gdk.BUTTON1_MASK,
+ [ ( "bank page", gtk.TARGET_SAME_APP, 10 ) ],
+ gtk.gdk.ACTION_COPY )
+
+ self.selectPage( pageId, True, invokeCallback )
+
+ pageView.show()
+
+ def set_size_request( self, width, height ):
+ gtk.Frame.set_size_request( self, width, height )
+ self.table.set_size_request( width, height )
+ for pageId in self.pageViews.keys():
+ self.updateSize( self.pageViews[ pageId ] )
+
+ def updateSize( self, pageView ):
+ pageView.set_size_request( self.get_allocation().width / GUIConstants.NUMBER_OF_PAGE_BANK_COLUMNS,
+ GUIConstants.PAGE_HEIGHT - 1 )
+
+ def selectPage( self, selectedPageId, invokeCallback = True, deselectOthers = True ):
+ if deselectOthers:
+ for pageId in self.pageViews.keys():
+ self.pageViews[ pageId ].setSelected( pageId == selectedPageId )
+ if pageId != selectedPageId:
+ self.selectedPageIds.discard( pageId )
+ else:
+ self.selectedPageIds.add( pageId )
+ #nb: pageId might be NO_PAGE, and selectedPageIds can be empty here
+
+ else:
+ self.pageViews[ selectedPageId ].toggleSelected()
+ if self.pageViews[ selectedPageId ].selected:
+ self.selectedPageIds.add( selectedPageId )
+ else:
+ self.selectedPageIds.discard( selectedPageId )
+
+ if invokeCallback:
+ self.selectPageCallback( selectedPageId )
+
+ def getSelectedPageIds( self ):
+ rval = filter( lambda id: self.pageViews[id].selected == True, self.pageViews.keys())
+ return rval
+
diff --git a/TamTamEdit.activity/Edit/rm/PageView.py b/TamTamEdit.activity/Edit/rm/PageView.py
new file mode 100644
index 0000000..00b650b
--- /dev/null
+++ b/TamTamEdit.activity/Edit/rm/PageView.py
@@ -0,0 +1,65 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import pango
+
+from GUI.GUIConstants import GUIConstants
+
+class PageView( gtk.DrawingArea ):
+ def __init__( self, pageID, selectPageCallback, selected = False ):
+ gtk.DrawingArea.__init__( self )
+
+ self.pageID = pageID
+ self.selectPageCallback = selectPageCallback
+ self.selected = selected
+
+ self.add_events( gtk.gdk.BUTTON_PRESS_MASK )
+
+ self.connect( "expose-event", self.handleExposeEvent )
+ self.connect( "button-press-event", self.handleButtonPress )
+ self.connect( "drag_data_get", self.getData )
+
+ def handleButtonPress( self, widget, event ):
+ if event.button == 1 or event.button == 3:
+ self.selectPageCallback( self.pageID, event.button == 1 )
+
+ def getData( self, widget, context, selection, targetType, eventTime ):
+ return selection.set( gtk.gdk.SELECTION_PRIMARY, 32, "p %d" % self.pageID )
+
+ def toggleSelected( self ):
+ self.selected = not self.selected
+ self.queue_draw()
+
+ def setSelected( self, selected ):
+ if self.selected != selected:
+ self.selected = selected
+ self.queue_draw()
+
+ # TODO: this is temporary: replace with a visual representation of the page
+ def handleExposeEvent( self, drawingArea, event ):
+ size = self.get_allocation()
+ context = self.window.cairo_create()
+
+ if self.selected:
+ context.set_line_width( GUIConstants.PAGE_SELECTED_BORDER_SIZE )
+ else:
+ context.set_line_width( GUIConstants.PAGE_BORDER_SIZE )
+ context.move_to( 0, 0 )
+ context.rel_line_to( size.width, 0 )
+ context.rel_line_to( 0, size.height )
+ context.rel_line_to( -size.width, 0 )
+ context.close_path()
+
+ #blue background
+ context.set_source_rgb( 0.75, 0.75, 0.75 )
+ context.fill_preserve()
+
+ #black border
+ context.set_source_rgb( 0, 0, 0 )
+ context.stroke()
+
+ #text
+ layout = self.create_pango_layout( "%d" % ( self.pageID + 1 ) )
+ layout.set_font_description( pango.FontDescription( 'Sans 10' ) )
+ self.window.draw_layout( self.window.new_gc(), 5, 5, layout )
diff --git a/TamTamEdit.activity/Edit/rm/PositionIndicator.py b/TamTamEdit.activity/Edit/rm/PositionIndicator.py
new file mode 100644
index 0000000..aadc4f4
--- /dev/null
+++ b/TamTamEdit.activity/Edit/rm/PositionIndicator.py
@@ -0,0 +1,47 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+#----------------------------------------------------------------------
+# A verical bar used to show the current point in time on a page
+# TODO: modify this class to change the current point in time
+# on click and drag
+#----------------------------------------------------------------------
+class PositionIndicator( gtk.DrawingArea ):
+ #-----------------------------------
+ # initialization
+ #-----------------------------------
+ def __init__( self, trackIDs, selectedTrackIDs, mutedTrackIDs ):
+ gtk.DrawingArea.__init__( self )
+
+ self.trackIDs = trackIDs
+ self.selectedTrackIDs = selectedTrackIDs
+ self.mutedTrackIDs = mutedTrackIDs
+
+ self.connect( "expose-event", self.draw )
+
+ def draw( self, drawingArea, event ):
+ indicatorSize = self.get_allocation()
+ trackHeight = indicatorSize.height / len( self.trackIDs )
+
+ context = drawingArea.window.cairo_create()
+
+ trackIndex = 0
+ for trackID in self.trackIDs:
+ height = trackIndex * trackHeight
+
+ context.move_to( 0, height )
+ context.rel_line_to( indicatorSize.width, 0 )
+ context.rel_line_to( 0, height + trackHeight )
+ context.rel_line_to( -indicatorSize.width, 0 )
+ context.close_path()
+
+ if trackID not in self.mutedTrackIDs:
+ context.set_source_rgb( 0, 0, 0 ) #black
+ else:
+ context.set_source_rgb( 0.6, 0.6, 0.6 ) #grey
+
+ context.fill_preserve()
+ context.stroke()
+
+ trackIndex += 1 \ No newline at end of file
diff --git a/TamTamEdit.activity/Edit/rm/TrackView.py b/TamTamEdit.activity/Edit/rm/TrackView.py
new file mode 100644
index 0000000..0b66abd
--- /dev/null
+++ b/TamTamEdit.activity/Edit/rm/TrackView.py
@@ -0,0 +1,263 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+from Framework.Constants import Constants
+from GUI.GUIConstants import GUIConstants
+
+from BackgroundView import SELECTNOTES
+from NoteView import NoteView
+
+
+#----------------------------------------------------------------------
+# This view class is used to show the contents of a NoteTrack
+# i.e. a Collection of Note objects
+#----------------------------------------------------------------------
+class TrackView:
+ #-----------------------------------
+ # initialization functions
+ #-----------------------------------
+ def __init__( self, trackID, beatsPerPageAdjustment ):
+ self.trackID = trackID
+ self.beatsPerPageAdjustment = beatsPerPageAdjustment
+ self.noteViews = []
+ self.posOffset = (0,0)
+ self.selectedNotes = []
+
+ def getID( self ):
+ return self.trackID
+
+ #-----------------------------------
+ # modification methods
+ #-----------------------------------
+ def setNotes( self, notes ):
+ self.clearNotes()
+
+ lineW = self.getBorderWidth()
+
+ for note in notes:
+ noteView = NoteView( note, self, self.beatsPerPageAdjustment )
+ self.noteViews.append( noteView )
+ noteView.setPositionOffset( (self.posOffset[0]+lineW, self.posOffset[1]+lineW ) )
+
+ self.updateNoteTransforms()
+
+ def clearNotes( self ):
+ del self.noteViews
+ self.noteViews = []
+ self.selectedNotes = []
+
+ def selectNotes( self, mode, which ):
+ if mode == SELECTNOTES.ALL:
+ for note in self.noteViews: note.setSelected( True )
+ self.selectedNotes = self.noteViews[:]
+ elif mode == SELECTNOTES.NONE:
+ for note in self.noteViews: note.setSelected( False )
+ self.selectedNotes = []
+ elif mode == SELECTNOTES.ADD:
+ for note in which:
+ if note.setSelected( True ):
+ self.selectedNotes.insert( 0, note )
+ elif mode == SELECTNOTES.REMOVE:
+ for note in which:
+ if note.setSelected( False ):
+ self.selectedNotes.remove( note )
+ elif mode == SELECTNOTES.EXCLUSIVE:
+ for note in self.noteViews:
+ if note in which:
+ if note.setSelected( True ):
+ self.selectedNotes.insert( 0, note )
+ else:
+ if note.setSelected( False ):
+ self.selectedNotes.remove( note )
+
+ def updateDragLimits( self, dragLimits ):
+ if not len(self.selectedNotes): return # no selected notes here
+
+ leftBound = 0
+ maxRightBound = round( self.beatsPerPageAdjustment.value, 0 ) * Constants.TICKS_PER_BEAT
+ last = len(self.noteViews)-1
+ for i in range(0,last):
+ if not self.noteViews[i].getSelected():
+ leftBound = self.noteViews[i].getEndTick()
+ else:
+ if not self.noteViews[i+1].getSelected():
+ rightBound = min( self.noteViews[i+1].getStartTick(), maxRightBound )
+ widthBound = rightBound
+ else:
+ rightBound = maxRightBound
+ widthBound = min( self.noteViews[i+1].getStartTick(), maxRightBound )
+ self.noteViews[i].updateDragLimits( dragLimits, leftBound, rightBound, widthBound )
+ if self.noteViews[last].getSelected():
+ self.noteViews[last].updateDragLimits( dragLimits, leftBound, maxRightBound, maxRightBound )
+
+ def getNotesByBar( self, beatCount, startX, stopX ):
+ beatWidth = self.getBeatLineSpacing( beatCount )
+ beatStart = self.getBeatLineStart()
+ while beatStart+beatWidth <= startX:
+ beatStart += beatWidth
+ beatStop = beatStart + beatWidth
+ while beatStop+beatWidth < stopX:
+ beatStop += beatWidth
+
+ notes = []
+ for note in self.noteViews:
+ if note.checkX( beatStart, beatStop ):
+ notes.insert(0,note)
+ return notes
+
+ #-----------------------------------
+ # event methods
+ #-----------------------------------
+
+ def handleButtonPress( self, emitter, event ):
+ eX = event.x - self.posOffset[0]
+ eY = event.y - self.posOffset[1]
+ if eX < 0 or eX > self.width or eY < 0 or eY > self.height:
+ return False
+
+ for note in self.noteViews:
+ handled = note.handleButtonPress( emitter, event )
+ if handled: return handled
+
+ return False
+
+ def handleButtonRelease( self, emitter, event, buttonPressCount ):
+ eX = event.x - self.posOffset[0]
+ eY = event.y - self.posOffset[1]
+
+ if eX < 0 or eX > self.width or eY < 0 or eY > self.height:
+ return False
+
+ if event.button == 1:
+ if buttonPressCount == 1: emitter.toggleTrack( self.trackID, False )
+ else: emitter.toggleTrack( self.trackID, True )
+
+ return True
+
+ def handleMarqueeSelect( self, emitter, start, stop ):
+ intersectionY = [ max(start[1],self.posOffset[1]), min(stop[1],self.posOffset[1]+self.height) ]
+ if intersectionY[0] > intersectionY[1]:
+ return False
+
+ intersectionX = [ max(start[0],self.posOffset[0]), min(stop[0],self.posOffset[0]+self.width) ]
+ if intersectionX[0] > intersectionX[1]:
+ return False
+
+
+ hits = []
+ for note in self.noteViews:
+ hit = note.handleMarqueeSelect( emitter,
+ [ intersectionX[0], intersectionY[0] ], \
+ [ intersectionX[1], intersectionY[1] ] )
+ if hit: hits.insert(0,note)
+
+ if len(hits): return hits
+
+ return False
+
+ def noteDrag( self, emitter, dx, dy, dw ):
+ for note in self.selectedNotes:
+ note.noteDrag( emitter, dx, dy, dw )
+
+ def doneNoteDrag( self, emitter ):
+ for note in self.selectedNotes:
+ note.doneNoteDrag( emitter )
+
+ #-----------------------------------
+ # drawing methods
+ #-----------------------------------
+
+ def getBorderWidth( self ): #should return a constant value, otherwise we have to recalculate sizing and positioning everyframe!
+ return GUIConstants.BORDER_SIZE
+
+ def getBeatLineWidth( self ):
+ return GUIConstants.BEAT_LINE_SIZE #should return a constant value, otherwise we have to recalculate sizing and positioning everyframe!
+
+ def getBeatLineSpacing( self, beatCount ):
+ return (self.width - 2*self.getBorderWidth() + self.getBeatLineWidth())/beatCount
+
+ def getBeatLineStart( self ):
+ return self.posOffset[0] + self.getBorderWidth() - self.getBeatLineWidth()/2.0
+
+ def setPositionOffset( self, offset ):
+ self.posOffset = offset
+
+ lineW = self.getBorderWidth()
+ for note in self.noteViews:
+ note.setPositionOffset( ( self.posOffset[0]+lineW, self.posOffset[1]+lineW ) )
+
+ def draw( self, context, beatCount, selected ):
+ #if selected: lineW = GUIConstants.SELECTED_BORDER_SIZE
+ #else: lineW = GUIConstants.BORDER_SIZE
+ lineW = self.getBorderWidth()
+ context.set_line_width( lineW )
+ lineWDIV2 = lineW/2.0
+
+ context.move_to( self.posOffset[0] + lineWDIV2, self.posOffset[1] + lineWDIV2 )
+ context.rel_line_to( self.width - lineW, 0 )
+ context.rel_line_to( 0, self.height - lineW )
+ context.rel_line_to( -self.width + lineW, 0 )
+ context.close_path()
+
+ #draw the background
+ context.set_source_rgb( 0.75, 0.75, 0.75 )
+ context.fill_preserve()
+
+ #draw the border
+ if selected: context.set_source_rgb( 1, 1, 1 )
+ else: context.set_source_rgb( 0, 0, 0 )
+ context.stroke()
+
+ #draw the beat lines
+ beatLineWidth = self.getBeatLineWidth()
+ context.set_line_width( beatLineWidth )
+ beatWidth = self.getBeatLineSpacing( beatCount )
+ beatStart = self.getBeatLineStart()
+ context.set_source_rgb( 0, 0, 0 )
+ for i in range(1,beatCount):
+ context.move_to( beatStart + i*beatWidth, self.posOffset[1] + lineW )
+ context.rel_line_to( 0, self.height - 2*lineW )
+ context.stroke()
+
+ #draw the notes
+ for note in self.noteViews:
+ note.draw( context )
+
+ #-----------------------------------
+ # sizing methods
+ #-----------------------------------
+
+ def updateNoteTransforms( self ):
+ width = self.width - 2*self.getBorderWidth()
+ height = self.height - 2*self.getBorderWidth() # adjust for actual note drawing area
+ for noteView in self.noteViews:
+ noteView.updateTransform( (width, height) )
+
+ def set_size_request( self, width, height ):
+ self.width = width
+ self.height = height
+ self.updateNoteTransforms()
+
+
+#unused for now...
+class NoteViewPool:
+ def __init__( self, parentContainer, beatsPerPageAdjustment ):
+ self.parentContainer = parentContainer
+ self.beatsPerPageAdjustment = beatsPerPageAdjustment
+ self.pool = []
+
+ def addNoteView( self, noteView ):
+ #noteView.hide()
+ self.pool.append( noteView )
+
+ def addNoteViews( self, noteViews ):
+ for noteView in noteViews:
+ self.addNoteView( noteView )
+
+ def getNoteView( self ):
+ poolSize = len( pool )
+ if poolSize != 0:
+ return pool.pop( poolSize )
+
+ return NoteView( None, self.parentContainer, self.beatsPerPageAdjustment )
diff --git a/TamTamEdit.activity/Edit/rm/TunePageView.py b/TamTamEdit.activity/Edit/rm/TunePageView.py
new file mode 100644
index 0000000..501504f
--- /dev/null
+++ b/TamTamEdit.activity/Edit/rm/TunePageView.py
@@ -0,0 +1,17 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+from PageView import PageView
+
+class TunePageView( PageView ):
+ def __init__( self, pageID, tuneIndex, selectPageCallback, selected = False ):
+ PageView.__init__( self, pageID, selectPageCallback, selected )
+
+ self.pageIndex = tuneIndex
+
+ def handleButtonPress( self, widget, data ):
+ self.selectPageCallback( self.tuneIndex )
+
+ def getData( self, widget, context, selection, targetType, eventTime ):
+ return selection.set( gtk.gdk.SELECTION_PRIMARY, 32, "t %d %d" % (self.pageID,self.pageIndex) )
diff --git a/TamTamEdit.activity/Edit/rm/TuneView.py b/TamTamEdit.activity/Edit/rm/TuneView.py
new file mode 100644
index 0000000..63cf468
--- /dev/null
+++ b/TamTamEdit.activity/Edit/rm/TuneView.py
@@ -0,0 +1,123 @@
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+from GUI.GUIConstants import GUIConstants
+from GUI.Core.TunePageView import TunePageView
+
+def swap(l,i,j):
+ e = l[i]
+ l[i] = l[j]
+ l[j] = e
+
+class TuneView( gtk.ScrolledWindow ):
+
+ NO_PAGE = -1
+
+ def _page_width(self):
+ return self.pageContainer.get_allocation().width / GUIConstants.NUMBER_OF_PAGE_BANK_COLUMNS
+
+ def __init__( self, selectPageCallback ):
+ gtk.ScrolledWindow.__init__( self )
+
+ #selectPageCallback(): currently connected to pagePlayer.setPlayTune, which skips to a given page of the tune.
+ self.selectPageCallback = selectPageCallback
+ self.selectedPageIndex = self.NO_PAGE
+
+ self.set_policy( gtk.POLICY_ALWAYS, gtk.POLICY_AUTOMATIC )
+ self.set_placement( gtk.CORNER_TOP_LEFT )
+
+ #self.pageViews: list of our custom PageView widgets
+ self.pageViews = []
+ self.pageContainer = gtk.HBox( False )
+ self.add_with_viewport( self.pageContainer )
+
+ #the old part
+ self.pageContainer.drag_dest_set( gtk.DEST_DEFAULT_ALL,
+ [ ( "bank page", gtk.TARGET_SAME_APP, 10 ),
+ ( "tune page", gtk.TARGET_SAME_APP, 11 )],
+ gtk.gdk.ACTION_COPY|gtk.gdk.ACTION_MOVE )
+
+ self.pageContainer.connect( "drag_data_received", self.dragDataReceived )
+
+ #private method: called by gtk when pages get dragged onto the tune-view
+ def dragDataReceived( self, widget, context, x, y, selectionData, info, time ):
+ print 'dragDataReceived: ', selectionData.data, info, selectionData.data
+ recv = selectionData.data.split()
+ if recv[0] == 'p':
+ pageId = int( recv[1] )
+ self.addPage( pageId, min( x / self._page_width(), len( self.pageViews )) )
+ elif recv[0] == 't':
+ self.moveSelectedPage( min( x / self._page_width(), len( self.pageViews ) -1))
+ else:
+ raise 'ERROR'
+
+ #public method: called by MainWindow on file load
+ def syncFromPagePlayer(self):
+ raise 'never call this'
+ map( lambda pv:pv.destroy(), self.pageViews )
+ self.pageViews = []
+ tunePages = self.tunePagesCallback()
+ for i in range( len(tunePages)):
+ self.addPage( tunePages[i], i, False)
+
+
+ def addPage( self, pageID, position ):
+ #create a new widget
+ pageView = TunePageView( pageID, position, self.selectPage )
+ self.pageViews.insert( position, pageView )
+ self.pageContainer.pack_start( pageView, False )
+ self.pageContainer.reorder_child( pageView, position )
+
+ pageView.set_size_request( self.pageContainer.get_allocation().width / GUIConstants.NUMBER_OF_PAGE_BANK_COLUMNS,
+ GUIConstants.PAGE_HEIGHT )
+ pageView.show()
+
+ for i in range( len(self.pageViews)) :
+ self.pageViews[i].tuneIndex = i
+ self.pageViews[i].setSelected( i == position)
+ self.selectPageCallback( pageID, position )
+ pageView.drag_source_set(
+ gtk.gdk.BUTTON1_MASK,
+ [ ( "tune page", gtk.TARGET_SAME_APP, 11 ) ],
+ gtk.gdk.ACTION_COPY|gtk.gdk.ACTION_MOVE )
+
+ def moveSelectedPage( self, position):
+ self.pageContainer.reorder_child( self.pageViews[self.selectedPageIndex], position )
+ swap( self.pageViews, self.selectedPageIndex, position )
+ self.selectedPageIndex = position
+ for i in range( len(self.pageViews) ) :
+ self.pageViews[i].tuneIndex = i
+ self.pageViews[i].setSelected( i == position)
+
+ def removePage( self, position ):
+ pv = self.pageViews[position]
+ self.pageViews[position:position+1] = []
+ if self.selectedPageIndex >= position : self.selectedPageIndex -= 1
+ for i in range( len(self.pageViews)) :
+ self.pageViews[i].tuneIndex = i
+ self.pageViews[i].setSelected( i == position)
+ self.pageContainer.remove(pv)
+ del pv
+
+ def selectPage( self, selectedPageIndex, invokeCallback = True ):
+ if selectedPageIndex >= len( self.pageViews ): selectedPageIndex = self.NO_PAGE
+ self.selectedPageIndex = selectedPageIndex
+ if selectedPageIndex == self.NO_PAGE:
+ for pv in self.pageViews: pv.setSelected(False)
+ if invokeCallback: self.selectPageCallback( -1, -1 )
+ else:
+ if not self.pageViews[ selectedPageIndex ].selected:
+ map( lambda pv: pv.setSelected( pv.tuneIndex == selectedPageIndex), self.pageViews)
+ if invokeCallback: self.selectPageCallback( self.pageViews[selectedPageIndex].pageID, selectedPageIndex )
+
+ def set_size_request( self, width, height ):
+ gtk.ScrolledWindow.set_size_request( self, width, height )
+ map( lambda pv: pv.set_size_request( width / GUIConstants.NUMBER_OF_PAGE_BANK_COLUMNS, GUIConstants.PAGE_HEIGHT ), self.pageViews)
+
+ def getPageId( self, idx):
+ return self.pageViews[idx].pageID
+
+ def getTune( self ):
+ return [ p.pageID for p in self.pageViews ]
+
diff --git a/TamTamEdit.activity/NEWS b/TamTamEdit.activity/NEWS
new file mode 100644
index 0000000..8120a07
--- /dev/null
+++ b/TamTamEdit.activity/NEWS
@@ -0,0 +1,71 @@
+39
+
+* New activity : TamTamJam (lync)
+* Activities are now separate (TamTamJam, TamTamEdit, TamTamSynthLab)
+* No sound when set to language other than english fixed (#3165) (J5, marcopg, tomeu, Flipo)
+
+38
+
+* Adapt to new Palette API (Flipo)
+
+37
+
+* Small layout change in edit (Flipo)
+* Temporary fix for Palettes causing the audio to stop (Flipo)
+
+36
+
+* Major image cleanup, 700k saved (Flipo)
+* Tune generation back (see leftmost icon on generate tab) (Olipet)
+
+35
+
+* TamTam Edit Toolbars enhancements (Flipo, Olipet)
+* TamTam Edit layout changes (lync)
+* Dual instruments for tracks in Edit (Olipet, lync)
+
+34
+
+* Synthlab sugar integration (olipet, Flipo)
+* Toolbars in edit draft, hit alt-t to show the toolbar, alt-y to hide (Flipo)
+* Bug that prevented sound from working fixed (LeJames)
+
+33
+
+* Added Sugar Toolbar to miniTamTam and Synthlab (Flipo)
+* Network now working in miniTamTam, use the share button on the activity toolbar (lync)
+* Added help subsystem (ethrop, LeJames)
+
+32
+
+* Important microphone fix (Olipet)
+
+31
+
+* Working network (create a FORCE_SHARE file in the / dir) (lync)
+* New tune generator in Edit, click the dice in the tune view (Olipet)
+* Fine tuning of the algorythmic generator and orchestras (Olipet)
+* TamTamJam (miniTamTam) integration with the journal (Flipo)
+* Title according to subactivity displayed in the journal (Flipo)
+
+30
+
+* Clicking the close button in Edit and Synthlab goes back to Sugar. (Flipo)
+* Fix to the Journal load and save. (Flipo, lync)
+* Ogg player on the welcome page. (lync)
+* Demo tune instruments and tempo are randomly generated (Olipet)
+* lab placeholder files were reduced in size by 95% (Olipet)
+
+29
+
+* WelcomeScreen: Removed Typing Game icon. (Flipo)
+* General: Added sounds. (ethrop, Olipet)
+* General: Journal integration. (Flipo, lync)
+* TamTamJam: Microphone semi-working. (James)
+* TamTamEdit: Beats per page setting. (lync, Olipet)
+* TamTamEdit: Mute/Solo controls on tracks. (Flipo)
+* TamTamEdit: Paint tool. (Olipet)
+* TamTamEdit: Properties automation. (Olipet)
+* TamTamEdit: Pages can have different colors. (lync)
+* TamTamEdit: Realtime keyboard recording. (Olipet)
+* TamTamEdit: Export song to ogg file. (Olipet) \ No newline at end of file
diff --git a/TamTamEdit.activity/TamTam.py b/TamTamEdit.activity/TamTam.py
new file mode 100644
index 0000000..e282df9
--- /dev/null
+++ b/TamTamEdit.activity/TamTam.py
@@ -0,0 +1,1229 @@
+import locale
+locale.setlocale(locale.LC_NUMERIC, 'C')
+import signal , time , sys , os, shutil
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import gobject
+import time
+
+import Config
+from Util.CSoundClient import new_csound_client
+from Util.Profiler import TP
+
+from Util.InstrumentPanel import InstrumentPanel
+from miniTamTam.miniTamTamMain import miniTamTamMain
+from Jam.JamMain import JamMain
+from Edit.MainWindow import MainWindow
+from Welcome import Welcome
+from SynthLab.SynthLabWindow import SynthLabWindow
+from Util.Trackpad import Trackpad
+from gettext import gettext as _
+#from Util.KeyboardWindow import KeyboardWindow
+import commands
+
+if __name__ != '__main__':
+ try:
+ from sugar.activity.activity import Activity
+ from sugar.activity import activity
+ FAKE_ACTIVITY = False
+ if Config.DEBUG: print 'using sugar Activity'
+ except ImportError:
+ from FActivity import FakeActivity as Activity
+ FAKE_ACTIVITY = True
+ if Config.DEBUG: print 'using fake activity'
+else:
+ from FActivity import FakeActivity as Activity
+ if Config.DEBUG: print 'using fake activity'
+
+
+class TamTam(Activity):
+ # TamTam is the topmost container in the TamTam application
+ # At all times it has one child, which may be one of
+ # - the welcome screen
+ # - the mini-tamtam
+ # - the synth lab
+ # - edit mode
+
+ def __init__(self, handle, mode='welcome'):
+ Activity.__init__(self, handle)
+ self.ensure_dirs()
+
+ color = gtk.gdk.color_parse(Config.WS_BCK_COLOR)
+ self.modify_bg(gtk.STATE_NORMAL, color)
+
+ self.set_title('TamTam')
+ self.set_resizable(False)
+
+ self.trackpad = Trackpad( self )
+ #self.keyboardWindow = KeyboardWindow(size = 8, popup = True)
+ #self.keyboardWindow.color_piano()
+
+ self.preloadTimeout = None
+
+ self.focusInHandler = self.connect('focus_in_event',self.onFocusIn)
+ self.focusOutHandler = self.connect('focus_out_event',self.onFocusOut)
+ self.connect('notify::active', self.onActive)
+ self.connect('destroy', self.onDestroy)
+ self.connect( "key-press-event", self.onKeyPress )
+ self.connect( "key-release-event", self.onKeyRelease )
+ #self.connect( "key-press-event", self.keyboardWindow.handle_keypress)
+ #self.connect( "key-release-event", self.keyboardWindow.handle_keyrelease)
+ #self.connect( "button-press-event", self.keyboardWindow.handle_mousePress)
+ #self.connect( "button-release-event", self.keyboardWindow.handle_mouseRelease)
+
+ self.mode = None
+ self.modeList = {}
+
+ self.instrumentPanel = InstrumentPanel( force_load = False )
+ self.preloadList = [ self.instrumentPanel ]
+
+ #load the sugar toolbar
+ self.toolbox = activity.ActivityToolbox(self)
+ self.set_toolbox(self.toolbox)
+
+ self.activity_toolbar = self.toolbox.get_activity_toolbar()
+ self.activity_toolbar.share.hide()
+ self.activity_toolbar.keep.hide()
+
+ self.toolbox.show()
+
+ if self._shared_activity: # if we're joining a shared activity force mini
+ self.set_mode("mini")
+ else:
+ self.set_mode(mode)
+
+ def onPreloadTimeout( self ):
+ if Config.DEBUG > 4: print "TamTam::onPreloadTimeout", self.preloadList
+
+ t = time.time()
+ if self.preloadList[0].load( t + 0.100 ): # finished preloading this object
+ self.preloadList.pop(0)
+ if not len(self.preloadList):
+ if Config.DEBUG > 1: print "TamTam::finished preloading", time.time() - t
+ self.preloadTimeout = False
+ return False # finished preloading everything
+
+ if Config.DEBUG > 4: print "TamTam::preload returned after", time.time() - t
+
+ return True
+
+ def doNothing(): #a callback function to appease SynthLab
+ pass
+
+ def set_mode(self, mode, arg = None):
+ if Config.DEBUG: print 'DEBUG: TamTam::set_mode from', self.mode, 'to', mode
+
+ if self.mode != None:
+ self.modeList[ self.mode ].onDeactivate()
+ if FAKE_ACTIVITY:
+ self.remove( self.modeList[ self.mode ] )
+
+ self.mode = None
+ self.trackpad.setContext(mode)
+
+ if mode == 'welcome':
+ if not (mode in self.modeList):
+ self.modeList[mode] = Welcome(self, self.set_mode)
+ self.mode = mode
+ #if len( self.preloadList ):
+ # self.preloadTimeout = gobject.timeout_add( 300, self.onPreloadTimeout )
+ elif self.preloadTimeout:
+ gobject.source_remove( self.preloadTimeout )
+ self.predrawTimeout = False
+
+ if mode == 'jam':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Jam'
+ self.modeList[mode] = JamMain(self, self.set_mode)
+ self.mode = mode
+
+ if mode == 'mini':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Mini'
+ self.modeList[mode] = miniTamTamMain(self, self.set_mode)
+ else:
+ self.modeList[mode].regenerate()
+ if self.instrumentPanel in self.preloadList:
+ self.instrumentPanel.load() # finish loading
+ self.modeList[mode].setInstrumentPanel( self.instrumentPanel )
+ self.mode = mode
+
+ if mode == 'edit':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Edit'
+ self.modeList[mode] = MainWindow(self, self.set_mode)
+ if self.instrumentPanel in self.preloadList:
+ self.instrumentPanel.load() # finish loading
+ self.modeList[mode].setInstrumentPanel( self.instrumentPanel )
+ self.mode = mode
+
+ if mode == 'synth':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam SynthLab'
+ self.modeList[mode] = SynthLabWindow(self, self.set_mode, None)
+ self.mode = mode
+
+ if self.mode == None:
+ print 'DEBUG: TamTam::set_mode invalid mode:', mode
+ else:
+ try: # activity mode
+ self.set_canvas( self.modeList[ self.mode ] )
+ except: # fake mode
+ self.add( self.modeList[ self.mode ] )
+ self.modeList[ self.mode ].onActivate(arg)
+ self.show()
+
+ def onFocusIn(self, event, data=None):
+ if Config.DEBUG > 3: print 'DEBUG: TamTam::onFocusOut in TamTam.py'
+ csnd = new_csound_client()
+ csnd.connect(True)
+ if self.mode == 'synth':
+ self.modeList[ self.mode ].updateSound()
+ self.modeList[ self.mode ].updateTables()
+ #csnd.load_instruments()
+
+ def onFocusOut(self, event, data=None):
+ if Config.DEBUG > 3: print 'DEBUG: TamTam::onFocusOut in TamTam.py'
+ csnd = new_csound_client()
+ csnd.connect(False)
+
+ def onActive(self, widget = None, event = None):
+ pass
+
+ def onKeyPress(self, widget, event):
+ if Config.DEBUG > 5: print 'DEBUG: TamTam::onKeyPress in TamTam.py'
+ #print "-----", event.keyval, event.string, event.hardware_keycode
+ if event.state == gtk.gdk.MOD1_MASK:
+ key = event.keyval
+ if key == gtk.keysyms.j:
+ self.set_mode("jam")
+ return
+ elif key == gtk.keysyms.m:
+ self.set_mode('mini')
+ return
+ elif key == gtk.keysyms.s:
+ self.set_mode('synth')
+ return
+ elif key == gtk.keysyms.w:
+ self.set_mode('welcome')
+ return
+ elif key == gtk.keysyms.e:
+ self.set_mode('edit')
+ return
+ elif key == gtk.keysyms.t:
+ self.toolbox.show()
+ return
+ elif key == gtk.keysyms.y:
+ self.toolbox.hide()
+ if self.mode:
+ self.modeList[ self.mode ].onKeyPress(widget, event)
+
+ def onKeyRelease(self, widget, event):
+ if Config.DEBUG > 5: print 'DEBUG: TamTam::onKeyRelease in TamTam.py'
+ self.modeList[ self.mode ].onKeyRelease(widget, event)
+ pass
+
+ def onDestroy(self, arg2):
+ if Config.DEBUG: print 'DEBUG: TamTam::onDestroy()'
+ os.system('rm -f ' + Config.PREF_DIR + '/synthTemp*')
+
+ for m in self.modeList:
+ if self.modeList[m] != None:
+ self.modeList[m].onDestroy()
+
+ csnd = new_csound_client()
+ csnd.connect(False)
+ csnd.destroy()
+
+ gtk.main_quit()
+
+ def ensure_dir(self, dir, perms=0777, rw=os.R_OK|os.W_OK):
+ if not os.path.isdir( dir ):
+ try:
+ os.makedirs(dir, perms)
+ except OSError, e:
+ print 'ERROR: failed to make dir %s: %i (%s)\n' % (dir, e.errno, e.strerror)
+ if not os.access(dir, rw):
+ print 'ERROR: directory %s is missing required r/w access\n' % dir
+
+ def ensure_dirs(self):
+ self.ensure_dir(Config.TUNE_DIR)
+ self.ensure_dir(Config.SYNTH_DIR)
+ self.ensure_dir(Config.SNDS_DIR)
+ self.ensure_dir(Config.SCRATCH_DIR)
+
+ if not os.path.isdir(Config.PREF_DIR):
+ os.mkdir(Config.PREF_DIR)
+ os.system('chmod 0777 ' + Config.PREF_DIR + ' &')
+ for snd in ['mic1','mic2','mic3','mic4','lab1','lab2','lab3','lab4', 'lab5', 'lab6']:
+ shutil.copyfile(Config.SOUNDS_DIR + '/' + snd , Config.SNDS_DIR + '/' + snd)
+ os.system('chmod 0777 ' + Config.SNDS_DIR + '/' + snd + ' &')
+
+ def read_file(self,file_path):
+ subactivity_name = self.metadata['tamtam_subactivity']
+ if subactivity_name == 'edit' \
+ or subactivity_name == 'synth' \
+ or subactivity_name == 'jam':
+ self.set_mode(subactivity_name)
+ self.modeList[subactivity_name].handleJournalLoad(file_path)
+ elif subactivity_name == 'mini':
+ self.set_mode(subactivity_name)
+ else:
+ return
+
+ def write_file(self,file_path):
+ if self.mode == 'edit':
+ self.metadata['tamtam_subactivity'] = self.mode
+ self.modeList[self.mode].handleJournalSave(file_path)
+ elif self.mode == 'synth':
+ self.metadata['tamtam_subactivity'] = self.mode
+ self.modeList[self.mode].handleJournalSave(file_path)
+ elif self.mode == 'mini':
+ self.metadata['tamtam_subactivity'] = self.mode
+ f = open(file_path,'w')
+ f.close()
+ elif self.mode == 'jam':
+ self.metadata['tamtam_subactivity'] = self.mode
+ self.modeList[self.mode].handleJournalSave(file_path)
+
+class TamTamJam(Activity):
+ # TamTam is the topmost container in the TamTam application
+ # At all times it has one child, which may be one of
+ # - the welcome screen
+ # - the mini-tamtam
+ # - the synth lab
+ # - edit mode
+
+ def __init__(self, handle, mode='welcome'):
+ Activity.__init__(self, handle)
+ self.ensure_dirs()
+
+ color = gtk.gdk.color_parse(Config.WS_BCK_COLOR)
+ self.modify_bg(gtk.STATE_NORMAL, color)
+
+ self.set_title('TamTam Jam')
+ self.set_resizable(False)
+
+ self.trackpad = Trackpad( self )
+ #self.keyboardWindow = KeyboardWindow(size = 8, popup = True)
+ #self.keyboardWindow.color_piano()
+
+ self.preloadTimeout = None
+
+ self.focusInHandler = self.connect('focus_in_event',self.onFocusIn)
+ self.focusOutHandler = self.connect('focus_out_event',self.onFocusOut)
+ self.connect('notify::active', self.onActive)
+ self.connect('destroy', self.onDestroy)
+ self.connect( "key-press-event", self.onKeyPress )
+ self.connect( "key-release-event", self.onKeyRelease )
+ #self.connect( "key-press-event", self.keyboardWindow.handle_keypress)
+ #self.connect( "key-release-event", self.keyboardWindow.handle_keyrelease)
+ #self.connect( "button-press-event", self.keyboardWindow.handle_mousePress)
+ #self.connect( "button-release-event", self.keyboardWindow.handle_mouseRelease)
+
+ self.mode = None
+ self.modeList = {}
+
+ self.instrumentPanel = InstrumentPanel( force_load = False )
+ self.preloadList = [ self.instrumentPanel ]
+
+ #load the sugar toolbar
+ self.toolbox = activity.ActivityToolbox(self)
+ self.set_toolbox(self.toolbox)
+
+ self.activity_toolbar = self.toolbox.get_activity_toolbar()
+ self.activity_toolbar.share.hide()
+ self.activity_toolbar.keep.hide()
+
+ self.toolbox.show()
+
+ self.set_mode("jam")
+
+ def onPreloadTimeout( self ):
+ if Config.DEBUG > 4: print "TamTam::onPreloadTimeout", self.preloadList
+
+ t = time.time()
+ if self.preloadList[0].load( t + 0.100 ): # finished preloading this object
+ self.preloadList.pop(0)
+ if not len(self.preloadList):
+ if Config.DEBUG > 1: print "TamTam::finished preloading", time.time() - t
+ self.preloadTimeout = False
+ return False # finished preloading everything
+
+ if Config.DEBUG > 4: print "TamTam::preload returned after", time.time() - t
+
+ return True
+
+ def doNothing(): #a callback function to appease SynthLab
+ pass
+
+ def set_mode(self, mode, arg = None):
+ if Config.DEBUG: print 'DEBUG: TamTam::set_mode from', self.mode, 'to', mode
+
+ if self.mode != None:
+ self.modeList[ self.mode ].onDeactivate()
+ if FAKE_ACTIVITY:
+ self.remove( self.modeList[ self.mode ] )
+
+ self.mode = None
+ self.trackpad.setContext(mode)
+
+ if mode == 'welcome':
+ if not (mode in self.modeList):
+ self.modeList[mode] = Welcome(self, self.set_mode)
+ self.mode = mode
+ if len( self.preloadList ):
+ self.preloadTimeout = gobject.timeout_add( 300, self.onPreloadTimeout )
+ elif self.preloadTimeout:
+ gobject.source_remove( self.preloadTimeout )
+ self.predrawTimeout = False
+
+ if mode == 'jam':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Jam'
+ self.modeList[mode] = JamMain(self, self.set_mode)
+ self.mode = mode
+
+ if mode == 'mini':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Mini'
+ self.modeList[mode] = miniTamTamMain(self, self.set_mode)
+ else:
+ self.modeList[mode].regenerate()
+ if self.instrumentPanel in self.preloadList:
+ self.instrumentPanel.load() # finish loading
+ self.modeList[mode].setInstrumentPanel( self.instrumentPanel )
+ self.mode = mode
+
+ if mode == 'edit':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Edit'
+ self.modeList[mode] = MainWindow(self, self.set_mode)
+ if self.instrumentPanel in self.preloadList:
+ self.instrumentPanel.load() # finish loading
+ self.modeList[mode].setInstrumentPanel( self.instrumentPanel )
+ self.mode = mode
+
+ if mode == 'synth':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam SynthLab'
+ self.modeList[mode] = SynthLabWindow(self, self.set_mode, None)
+ self.mode = mode
+
+ if self.mode == None:
+ print 'DEBUG: TamTam::set_mode invalid mode:', mode
+ else:
+ try: # activity mode
+ self.set_canvas( self.modeList[ self.mode ] )
+ except: # fake mode
+ self.add( self.modeList[ self.mode ] )
+ self.modeList[ self.mode ].onActivate(arg)
+ self.show()
+
+ def onFocusIn(self, event, data=None):
+ if Config.DEBUG > 3: print 'DEBUG: TamTam::onFocusOut in TamTam.py'
+ csnd = new_csound_client()
+ csnd.connect(True)
+ if self.mode == 'synth':
+ self.modeList[ self.mode ].updateSound()
+ self.modeList[ self.mode ].updateTables()
+ #csnd.load_instruments()
+
+ def onFocusOut(self, event, data=None):
+ if Config.DEBUG > 3: print 'DEBUG: TamTam::onFocusOut in TamTam.py'
+ csnd = new_csound_client()
+ csnd.connect(False)
+
+ def onActive(self, widget = None, event = None):
+ pass
+
+ def onKeyPress(self, widget, event):
+ if Config.DEBUG > 5: print 'DEBUG: TamTam::onKeyPress in TamTam.py'
+ if event.state == gtk.gdk.MOD1_MASK:
+ key = event.keyval
+ if key == gtk.keysyms.j:
+ self.set_mode("jam")
+ return
+ elif key == gtk.keysyms.m:
+ self.set_mode('mini')
+ return
+ elif key == gtk.keysyms.s:
+ self.set_mode('synth')
+ return
+ elif key == gtk.keysyms.w:
+ self.set_mode('welcome')
+ return
+ elif key == gtk.keysyms.e:
+ self.set_mode('edit')
+ return
+ elif key == gtk.keysyms.t:
+ self.toolbox.show()
+ return
+ elif key == gtk.keysyms.y:
+ self.toolbox.hide()
+ if self.mode:
+ self.modeList[ self.mode ].onKeyPress(widget, event)
+
+ def onKeyRelease(self, widget, event):
+ if Config.DEBUG > 5: print 'DEBUG: TamTam::onKeyRelease in TamTam.py'
+ self.modeList[ self.mode ].onKeyRelease(widget, event)
+ pass
+
+ def onDestroy(self, arg2):
+ if Config.DEBUG: print 'DEBUG: TamTam::onDestroy()'
+ os.system('rm -f ' + Config.PREF_DIR + '/synthTemp*')
+
+ for m in self.modeList:
+ if self.modeList[m] != None:
+ self.modeList[m].onDestroy()
+
+ csnd = new_csound_client()
+ csnd.connect(False)
+ csnd.destroy()
+
+ gtk.main_quit()
+
+ def ensure_dir(self, dir, perms=0777, rw=os.R_OK|os.W_OK):
+ if not os.path.isdir( dir ):
+ try:
+ os.makedirs(dir, perms)
+ except OSError, e:
+ print 'ERROR: failed to make dir %s: %i (%s)\n' % (dir, e.errno, e.strerror)
+ if not os.access(dir, rw):
+ print 'ERROR: directory %s is missing required r/w access\n' % dir
+
+ def ensure_dirs(self):
+ self.ensure_dir(Config.TUNE_DIR)
+ self.ensure_dir(Config.SYNTH_DIR)
+ self.ensure_dir(Config.SNDS_DIR)
+ self.ensure_dir(Config.SCRATCH_DIR)
+
+ if not os.path.isdir(Config.PREF_DIR):
+ os.mkdir(Config.PREF_DIR)
+ os.system('chmod 0777 ' + Config.PREF_DIR + ' &')
+ for snd in ['mic1','mic2','mic3','mic4','lab1','lab2','lab3','lab4', 'lab5', 'lab6']:
+ shutil.copyfile(Config.SOUNDS_DIR + '/' + snd , Config.SNDS_DIR + '/' + snd)
+ os.system('chmod 0777 ' + Config.SNDS_DIR + '/' + snd + ' &')
+
+ def read_file(self,file_path):
+ self.modeList['jam'].handleJournalLoad(file_path)
+
+ def write_file(self,file_path):
+ self.modeList['jam'].handleJournalSave(file_path)
+
+class TamTamEdit(Activity):
+ # TamTam is the topmost container in the TamTam application
+ # At all times it has one child, which may be one of
+ # - the welcome screen
+ # - the mini-tamtam
+ # - the synth lab
+ # - edit mode
+
+ def __init__(self, handle, mode='edit'):
+ Activity.__init__(self, handle)
+ self.ensure_dirs()
+
+ color = gtk.gdk.color_parse(Config.WS_BCK_COLOR)
+ self.modify_bg(gtk.STATE_NORMAL, color)
+
+ self.set_title('TamTam Edit')
+ self.set_resizable(False)
+
+ self.trackpad = Trackpad( self )
+ #self.keyboardWindow = KeyboardWindow(size = 8, popup = True)
+ #self.keyboardWindow.color_piano()
+
+ self.preloadTimeout = None
+
+ self.focusInHandler = self.connect('focus_in_event',self.onFocusIn)
+ self.focusOutHandler = self.connect('focus_out_event',self.onFocusOut)
+ self.connect('notify::active', self.onActive)
+ self.connect('destroy', self.onDestroy)
+ self.connect( "key-press-event", self.onKeyPress )
+ self.connect( "key-release-event", self.onKeyRelease )
+ #self.connect( "key-press-event", self.keyboardWindow.handle_keypress)
+ #self.connect( "key-release-event", self.keyboardWindow.handle_keyrelease)
+ #self.connect( "button-press-event", self.keyboardWindow.handle_mousePress)
+ #self.connect( "button-release-event", self.keyboardWindow.handle_mouseRelease)
+
+ self.mode = None
+ self.modeList = {}
+
+ self.instrumentPanel = InstrumentPanel( force_load = False )
+ self.preloadList = [ self.instrumentPanel ]
+
+ #load the sugar toolbar
+ self.toolbox = activity.ActivityToolbox(self)
+ self.set_toolbox(self.toolbox)
+
+ self.activity_toolbar = self.toolbox.get_activity_toolbar()
+ self.activity_toolbar.share.hide()
+ self.activity_toolbar.keep.hide()
+
+ self.toolbox.show()
+
+ self.set_mode("edit")
+
+ def onPreloadTimeout( self ):
+ if Config.DEBUG > 4: print "TamTam::onPreloadTimeout", self.preloadList
+
+ t = time.time()
+ if self.preloadList[0].load( t + 0.100 ): # finished preloading this object
+ self.preloadList.pop(0)
+ if not len(self.preloadList):
+ if Config.DEBUG > 1: print "TamTam::finished preloading", time.time() - t
+ self.preloadTimeout = False
+ return False # finished preloading everything
+
+ if Config.DEBUG > 4: print "TamTam::preload returned after", time.time() - t
+
+ return True
+
+ def doNothing(): #a callback function to appease SynthLab
+ pass
+
+ def set_mode(self, mode, arg = None):
+ if Config.DEBUG: print 'DEBUG: TamTam::set_mode from', self.mode, 'to', mode
+
+ if self.mode != None:
+ self.modeList[ self.mode ].onDeactivate()
+ if FAKE_ACTIVITY:
+ self.remove( self.modeList[ self.mode ] )
+
+ self.mode = None
+ self.trackpad.setContext(mode)
+
+ if mode == 'welcome':
+ if not (mode in self.modeList):
+ self.modeList[mode] = Welcome(self, self.set_mode)
+ self.mode = mode
+ if len( self.preloadList ):
+ self.preloadTimeout = gobject.timeout_add( 300, self.onPreloadTimeout )
+ elif self.preloadTimeout:
+ gobject.source_remove( self.preloadTimeout )
+ self.predrawTimeout = False
+
+ if mode == 'jam':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Jam'
+ self.modeList[mode] = JamMain(self, self.set_mode)
+ self.mode = mode
+
+ if mode == 'mini':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Mini'
+ self.modeList[mode] = miniTamTamMain(self, self.set_mode)
+ else:
+ self.modeList[mode].regenerate()
+ if self.instrumentPanel in self.preloadList:
+ self.instrumentPanel.load() # finish loading
+ self.modeList[mode].setInstrumentPanel( self.instrumentPanel )
+ self.mode = mode
+
+ if mode == 'edit':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Edit'
+ self.modeList[mode] = MainWindow(self, self.set_mode)
+ if self.instrumentPanel in self.preloadList:
+ self.instrumentPanel.load() # finish loading
+ self.modeList[mode].setInstrumentPanel( self.instrumentPanel )
+ self.mode = mode
+
+ if mode == 'synth':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam SynthLab'
+ self.modeList[mode] = SynthLabWindow(self, self.set_mode, None)
+ self.mode = mode
+
+ if self.mode == None:
+ print 'DEBUG: TamTam::set_mode invalid mode:', mode
+ else:
+ try: # activity mode
+ self.set_canvas( self.modeList[ self.mode ] )
+ except: # fake mode
+ self.add( self.modeList[ self.mode ] )
+ self.modeList[ self.mode ].onActivate(arg)
+ self.show()
+
+ def onFocusIn(self, event, data=None):
+ if Config.DEBUG > 3: print 'DEBUG: TamTam::onFocusOut in TamTam.py'
+ csnd = new_csound_client()
+ csnd.connect(True)
+ if self.mode == 'synth':
+ self.modeList[ self.mode ].updateSound()
+ self.modeList[ self.mode ].updateTables()
+ #csnd.load_instruments()
+
+ def onFocusOut(self, event, data=None):
+ if Config.DEBUG > 3: print 'DEBUG: TamTam::onFocusOut in TamTam.py'
+ csnd = new_csound_client()
+ csnd.connect(False)
+
+ def onActive(self, widget = None, event = None):
+ pass
+
+ def onKeyPress(self, widget, event):
+ if Config.DEBUG > 5: print 'DEBUG: TamTam::onKeyPress in TamTam.py'
+ if event.state == gtk.gdk.MOD1_MASK:
+ key = event.keyval
+ if key == gtk.keysyms.j:
+ self.set_mode("jam")
+ return
+ elif key == gtk.keysyms.m:
+ self.set_mode('mini')
+ return
+ elif key == gtk.keysyms.s:
+ self.set_mode('synth')
+ return
+ elif key == gtk.keysyms.w:
+ self.set_mode('welcome')
+ return
+ elif key == gtk.keysyms.e:
+ self.set_mode('edit')
+ return
+ elif key == gtk.keysyms.t:
+ self.toolbox.show()
+ return
+ elif key == gtk.keysyms.y:
+ self.toolbox.hide()
+ if self.mode:
+ self.modeList[ self.mode ].onKeyPress(widget, event)
+
+ def onKeyRelease(self, widget, event):
+ if Config.DEBUG > 5: print 'DEBUG: TamTam::onKeyRelease in TamTam.py'
+ self.modeList[ self.mode ].onKeyRelease(widget, event)
+ pass
+
+ def onDestroy(self, arg2):
+ if Config.DEBUG: print 'DEBUG: TamTam::onDestroy()'
+ os.system('rm -f ' + Config.PREF_DIR + '/synthTemp*')
+
+ for m in self.modeList:
+ if self.modeList[m] != None:
+ self.modeList[m].onDestroy()
+
+ csnd = new_csound_client()
+ csnd.connect(False)
+ csnd.destroy()
+
+ gtk.main_quit()
+
+ def ensure_dir(self, dir, perms=0777, rw=os.R_OK|os.W_OK):
+ if not os.path.isdir( dir ):
+ try:
+ os.makedirs(dir, perms)
+ except OSError, e:
+ print 'ERROR: failed to make dir %s: %i (%s)\n' % (dir, e.errno, e.strerror)
+ if not os.access(dir, rw):
+ print 'ERROR: directory %s is missing required r/w access\n' % dir
+
+ def ensure_dirs(self):
+ self.ensure_dir(Config.TUNE_DIR)
+ self.ensure_dir(Config.SYNTH_DIR)
+ self.ensure_dir(Config.SNDS_DIR)
+ self.ensure_dir(Config.SCRATCH_DIR)
+
+ if not os.path.isdir(Config.PREF_DIR):
+ os.mkdir(Config.PREF_DIR)
+ os.system('chmod 0777 ' + Config.PREF_DIR + ' &')
+ for snd in ['mic1','mic2','mic3','mic4','lab1','lab2','lab3','lab4', 'lab5', 'lab6']:
+ shutil.copyfile(Config.SOUNDS_DIR + '/' + snd , Config.SNDS_DIR + '/' + snd)
+ os.system('chmod 0777 ' + Config.SNDS_DIR + '/' + snd + ' &')
+
+ def read_file(self,file_path):
+ self.modeList['edit'].handleJournalLoad(file_path)
+
+ def write_file(self,file_path):
+ self.modeList['edit'].handleJournalSave(file_path)
+
+class TamTamSynthLab(Activity):
+ # TamTam is the topmost container in the TamTam application
+ # At all times it has one child, which may be one of
+ # - the welcome screen
+ # - the mini-tamtam
+ # - the synth lab
+ # - edit mode
+
+ def __init__(self, handle, mode='synth'):
+ Activity.__init__(self, handle)
+ self.ensure_dirs()
+
+ color = gtk.gdk.color_parse(Config.WS_BCK_COLOR)
+ self.modify_bg(gtk.STATE_NORMAL, color)
+
+ self.set_title('TamTam SynthLab')
+ self.set_resizable(False)
+
+ self.trackpad = Trackpad( self )
+ #self.keyboardWindow = KeyboardWindow(size = 8, popup = True)
+ #self.keyboardWindow.color_piano()
+
+ self.preloadTimeout = None
+
+ self.focusInHandler = self.connect('focus_in_event',self.onFocusIn)
+ self.focusOutHandler = self.connect('focus_out_event',self.onFocusOut)
+ self.connect('notify::active', self.onActive)
+ self.connect('destroy', self.onDestroy)
+ self.connect( "key-press-event", self.onKeyPress )
+ self.connect( "key-release-event", self.onKeyRelease )
+ #self.connect( "key-press-event", self.keyboardWindow.handle_keypress)
+ #self.connect( "key-release-event", self.keyboardWindow.handle_keyrelease)
+ #self.connect( "button-press-event", self.keyboardWindow.handle_mousePress)
+ #self.connect( "button-release-event", self.keyboardWindow.handle_mouseRelease)
+
+ self.mode = None
+ self.modeList = {}
+
+ self.instrumentPanel = InstrumentPanel( force_load = False )
+ self.preloadList = [ self.instrumentPanel ]
+
+ #load the sugar toolbar
+ self.toolbox = activity.ActivityToolbox(self)
+ self.set_toolbox(self.toolbox)
+
+ self.activity_toolbar = self.toolbox.get_activity_toolbar()
+ self.activity_toolbar.share.hide()
+ self.activity_toolbar.keep.hide()
+
+ self.toolbox.show()
+
+ self.set_mode("synth")
+
+ def onPreloadTimeout( self ):
+ if Config.DEBUG > 4: print "TamTam::onPreloadTimeout", self.preloadList
+
+ t = time.time()
+ if self.preloadList[0].load( t + 0.100 ): # finished preloading this object
+ self.preloadList.pop(0)
+ if not len(self.preloadList):
+ if Config.DEBUG > 1: print "TamTam::finished preloading", time.time() - t
+ self.preloadTimeout = False
+ return False # finished preloading everything
+
+ if Config.DEBUG > 4: print "TamTam::preload returned after", time.time() - t
+
+ return True
+
+ def doNothing(): #a callback function to appease SynthLab
+ pass
+
+ def set_mode(self, mode, arg = None):
+ if Config.DEBUG: print 'DEBUG: TamTam::set_mode from', self.mode, 'to', mode
+
+ if self.mode != None:
+ self.modeList[ self.mode ].onDeactivate()
+ if FAKE_ACTIVITY:
+ self.remove( self.modeList[ self.mode ] )
+
+ self.mode = None
+ self.trackpad.setContext(mode)
+
+ if mode == 'welcome':
+ if not (mode in self.modeList):
+ self.modeList[mode] = Welcome(self, self.set_mode)
+ self.mode = mode
+ if len( self.preloadList ):
+ self.preloadTimeout = gobject.timeout_add( 300, self.onPreloadTimeout )
+ elif self.preloadTimeout:
+ gobject.source_remove( self.preloadTimeout )
+ self.predrawTimeout = False
+
+ if mode == 'jam':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Jam'
+ self.modeList[mode] = JamMain(self, self.set_mode)
+ self.mode = mode
+
+ if mode == 'mini':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Mini'
+ self.modeList[mode] = miniTamTamMain(self, self.set_mode)
+ else:
+ self.modeList[mode].regenerate()
+ if self.instrumentPanel in self.preloadList:
+ self.instrumentPanel.load() # finish loading
+ self.modeList[mode].setInstrumentPanel( self.instrumentPanel )
+ self.mode = mode
+
+ if mode == 'edit':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Edit'
+ self.modeList[mode] = MainWindow(self, self.set_mode)
+ if self.instrumentPanel in self.preloadList:
+ self.instrumentPanel.load() # finish loading
+ self.modeList[mode].setInstrumentPanel( self.instrumentPanel )
+ self.mode = mode
+
+ if mode == 'synth':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam SynthLab'
+ self.modeList[mode] = SynthLabWindow(self, self.set_mode, None)
+ self.mode = mode
+
+ if self.mode == None:
+ print 'DEBUG: TamTam::set_mode invalid mode:', mode
+ else:
+ try: # activity mode
+ self.set_canvas( self.modeList[ self.mode ] )
+ except: # fake mode
+ self.add( self.modeList[ self.mode ] )
+ self.modeList[ self.mode ].onActivate(arg)
+ self.show()
+
+ def onFocusIn(self, event, data=None):
+ if Config.DEBUG > 3: print 'DEBUG: TamTam::onFocusOut in TamTam.py'
+ csnd = new_csound_client()
+ csnd.connect(True)
+ if self.mode == 'synth':
+ self.modeList[ self.mode ].updateSound()
+ self.modeList[ self.mode ].updateTables()
+ #csnd.load_instruments()
+
+ def onFocusOut(self, event, data=None):
+ if Config.DEBUG > 3: print 'DEBUG: TamTam::onFocusOut in TamTam.py'
+ csnd = new_csound_client()
+ csnd.connect(False)
+
+ def onActive(self, widget = None, event = None):
+ pass
+
+ def onKeyPress(self, widget, event):
+ if Config.DEBUG > 5: print 'DEBUG: TamTam::onKeyPress in TamTam.py'
+ if event.state == gtk.gdk.MOD1_MASK:
+ key = event.keyval
+ if key == gtk.keysyms.j:
+ self.set_mode("jam")
+ return
+ elif key == gtk.keysyms.m:
+ self.set_mode('mini')
+ return
+ elif key == gtk.keysyms.s:
+ self.set_mode('synth')
+ return
+ elif key == gtk.keysyms.w:
+ self.set_mode('welcome')
+ return
+ elif key == gtk.keysyms.e:
+ self.set_mode('edit')
+ return
+ elif key == gtk.keysyms.t:
+ self.toolbox.show()
+ return
+ elif key == gtk.keysyms.y:
+ self.toolbox.hide()
+ if self.mode:
+ self.modeList[ self.mode ].onKeyPress(widget, event)
+
+ def onKeyRelease(self, widget, event):
+ if Config.DEBUG > 5: print 'DEBUG: TamTam::onKeyRelease in TamTam.py'
+ self.modeList[ self.mode ].onKeyRelease(widget, event)
+ pass
+
+ def onDestroy(self, arg2):
+ if Config.DEBUG: print 'DEBUG: TamTam::onDestroy()'
+ os.system('rm -f ' + Config.PREF_DIR + '/synthTemp*')
+
+ for m in self.modeList:
+ if self.modeList[m] != None:
+ self.modeList[m].onDestroy()
+
+ csnd = new_csound_client()
+ csnd.connect(False)
+ csnd.destroy()
+
+ gtk.main_quit()
+
+ def ensure_dir(self, dir, perms=0777, rw=os.R_OK|os.W_OK):
+ if not os.path.isdir( dir ):
+ try:
+ os.makedirs(dir, perms)
+ except OSError, e:
+ print 'ERROR: failed to make dir %s: %i (%s)\n' % (dir, e.errno, e.strerror)
+ if not os.access(dir, rw):
+ print 'ERROR: directory %s is missing required r/w access\n' % dir
+
+ def ensure_dirs(self):
+ self.ensure_dir(Config.TUNE_DIR)
+ self.ensure_dir(Config.SYNTH_DIR)
+ self.ensure_dir(Config.SNDS_DIR)
+ self.ensure_dir(Config.SCRATCH_DIR)
+
+ if not os.path.isdir(Config.PREF_DIR):
+ os.mkdir(Config.PREF_DIR)
+ os.system('chmod 0777 ' + Config.PREF_DIR + ' &')
+ for snd in ['mic1','mic2','mic3','mic4','lab1','lab2','lab3','lab4', 'lab5', 'lab6']:
+ shutil.copyfile(Config.SOUNDS_DIR + '/' + snd , Config.SNDS_DIR + '/' + snd)
+ os.system('chmod 0777 ' + Config.SNDS_DIR + '/' + snd + ' &')
+
+ def read_file(self,file_path):
+ self.modeList['synth'].handleJournalLoad(file_path)
+
+ def write_file(self,file_path):
+ self.modeList['synth'].handleJournalSave(file_path)
+
+class TamTamMini(Activity):
+ # TamTam is the topmost container in the TamTam application
+ # At all times it has one child, which may be one of
+ # - the welcome screen
+ # - the mini-tamtam
+ # - the synth lab
+ # - edit mode
+
+ def __init__(self, handle, mode='mini'):
+ Activity.__init__(self, handle)
+ self.ensure_dirs()
+
+ color = gtk.gdk.color_parse(Config.WS_BCK_COLOR)
+ self.modify_bg(gtk.STATE_NORMAL, color)
+
+ self.set_title('TamTam Mini')
+ self.set_resizable(False)
+
+ self.trackpad = Trackpad( self )
+ #self.keyboardWindow = KeyboardWindow(size = 8, popup = True)
+ #self.keyboardWindow.color_piano()
+
+ self.preloadTimeout = None
+
+ self.focusInHandler = self.connect('focus_in_event',self.onFocusIn)
+ self.focusOutHandler = self.connect('focus_out_event',self.onFocusOut)
+ self.connect('notify::active', self.onActive)
+ self.connect('destroy', self.onDestroy)
+ self.connect( "key-press-event", self.onKeyPress )
+ self.connect( "key-release-event", self.onKeyRelease )
+ #self.connect( "key-press-event", self.keyboardWindow.handle_keypress)
+ #self.connect( "key-release-event", self.keyboardWindow.handle_keyrelease)
+ #self.connect( "button-press-event", self.keyboardWindow.handle_mousePress)
+ #self.connect( "button-release-event", self.keyboardWindow.handle_mouseRelease)
+
+ self.mode = None
+ self.modeList = {}
+
+ self.instrumentPanel = InstrumentPanel( force_load = False )
+ self.preloadList = [ self.instrumentPanel ]
+
+ #load the sugar toolbar
+ self.toolbox = activity.ActivityToolbox(self)
+ self.set_toolbox(self.toolbox)
+
+ self.activity_toolbar = self.toolbox.get_activity_toolbar()
+ self.activity_toolbar.share.hide()
+ self.activity_toolbar.keep.hide()
+
+ self.toolbox.show()
+
+ self.set_mode("mini")
+
+ def onPreloadTimeout( self ):
+ if Config.DEBUG > 4: print "TamTam::onPreloadTimeout", self.preloadList
+
+ t = time.time()
+ if self.preloadList[0].load( t + 0.100 ): # finished preloading this object
+ self.preloadList.pop(0)
+ if not len(self.preloadList):
+ if Config.DEBUG > 1: print "TamTam::finished preloading", time.time() - t
+ self.preloadTimeout = False
+ return False # finished preloading everything
+
+ if Config.DEBUG > 4: print "TamTam::preload returned after", time.time() - t
+
+ return True
+
+ def doNothing(): #a callback function to appease SynthLab
+ pass
+
+ def set_mode(self, mode, arg = None):
+ if Config.DEBUG: print 'DEBUG: TamTam::set_mode from', self.mode, 'to', mode
+
+ if self.mode != None:
+ self.modeList[ self.mode ].onDeactivate()
+ if FAKE_ACTIVITY:
+ self.remove( self.modeList[ self.mode ] )
+
+ self.mode = None
+ self.trackpad.setContext(mode)
+
+ if mode == 'welcome':
+ if not (mode in self.modeList):
+ self.modeList[mode] = Welcome(self, self.set_mode)
+ self.mode = mode
+ if len( self.preloadList ):
+ self.preloadTimeout = gobject.timeout_add( 300, self.onPreloadTimeout )
+ elif self.preloadTimeout:
+ gobject.source_remove( self.preloadTimeout )
+ self.predrawTimeout = False
+
+ if mode == 'jam':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Jam'
+ self.modeList[mode] = JamMain(self, self.set_mode)
+ self.mode = mode
+
+ if mode == 'mini':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Mini'
+ self.modeList[mode] = miniTamTamMain(self, self.set_mode)
+ else:
+ self.modeList[mode].regenerate()
+ if self.instrumentPanel in self.preloadList:
+ self.instrumentPanel.load() # finish loading
+ self.modeList[mode].setInstrumentPanel( self.instrumentPanel )
+ self.mode = mode
+
+ if mode == 'edit':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam Edit'
+ self.modeList[mode] = MainWindow(self, self.set_mode)
+ if self.instrumentPanel in self.preloadList:
+ self.instrumentPanel.load() # finish loading
+ self.modeList[mode].setInstrumentPanel( self.instrumentPanel )
+ self.mode = mode
+
+ if mode == 'synth':
+ if not (mode in self.modeList):
+ self.metadata['title'] = 'TamTam SynthLab'
+ self.modeList[mode] = SynthLabWindow(self, self.set_mode, None)
+ self.mode = mode
+
+ if self.mode == None:
+ print 'DEBUG: TamTam::set_mode invalid mode:', mode
+ else:
+ try: # activity mode
+ self.set_canvas( self.modeList[ self.mode ] )
+ except: # fake mode
+ self.add( self.modeList[ self.mode ] )
+ self.modeList[ self.mode ].onActivate(arg)
+ self.show()
+
+ def onFocusIn(self, event, data=None):
+ if Config.DEBUG > 3: print 'DEBUG: TamTam::onFocusOut in TamTam.py'
+ csnd = new_csound_client()
+ csnd.connect(True)
+ if self.mode == 'synth':
+ self.modeList[ self.mode ].updateSound()
+ self.modeList[ self.mode ].updateTables()
+ #csnd.load_instruments()
+
+ def onFocusOut(self, event, data=None):
+ if Config.DEBUG > 3: print 'DEBUG: TamTam::onFocusOut in TamTam.py'
+ csnd = new_csound_client()
+ csnd.connect(False)
+
+ def onActive(self, widget = None, event = None):
+ pass
+
+ def onKeyPress(self, widget, event):
+ if Config.DEBUG > 5: print 'DEBUG: TamTam::onKeyPress in TamTam.py'
+ if event.state == gtk.gdk.MOD1_MASK:
+ key = event.keyval
+ if key == gtk.keysyms.j:
+ self.set_mode("jam")
+ return
+ elif key == gtk.keysyms.m:
+ self.set_mode('mini')
+ return
+ elif key == gtk.keysyms.s:
+ self.set_mode('synth')
+ return
+ elif key == gtk.keysyms.w:
+ self.set_mode('welcome')
+ return
+ elif key == gtk.keysyms.e:
+ self.set_mode('edit')
+ return
+ elif key == gtk.keysyms.t:
+ self.toolbox.show()
+ return
+ elif key == gtk.keysyms.y:
+ self.toolbox.hide()
+ if self.mode:
+ self.modeList[ self.mode ].onKeyPress(widget, event)
+
+ def onKeyRelease(self, widget, event):
+ if Config.DEBUG > 5: print 'DEBUG: TamTam::onKeyRelease in TamTam.py'
+ self.modeList[ self.mode ].onKeyRelease(widget, event)
+ pass
+
+ def onDestroy(self, arg2):
+ if Config.DEBUG: print 'DEBUG: TamTam::onDestroy()'
+ os.system('rm -f ' + Config.PREF_DIR + '/synthTemp*')
+
+ for m in self.modeList:
+ if self.modeList[m] != None:
+ self.modeList[m].onDestroy()
+
+ csnd = new_csound_client()
+ csnd.connect(False)
+ csnd.destroy()
+
+ gtk.main_quit()
+
+ def ensure_dir(self, dir, perms=0777, rw=os.R_OK|os.W_OK):
+ if not os.path.isdir( dir ):
+ try:
+ os.makedirs(dir, perms)
+ except OSError, e:
+ print 'ERROR: failed to make dir %s: %i (%s)\n' % (dir, e.errno, e.strerror)
+ if not os.access(dir, rw):
+ print 'ERROR: directory %s is missing required r/w access\n' % dir
+
+ def ensure_dirs(self):
+ self.ensure_dir(Config.TUNE_DIR)
+ self.ensure_dir(Config.SYNTH_DIR)
+ self.ensure_dir(Config.SNDS_DIR)
+ self.ensure_dir(Config.SCRATCH_DIR)
+
+ if not os.path.isdir(Config.PREF_DIR):
+ os.mkdir(Config.PREF_DIR)
+ os.system('chmod 0777 ' + Config.PREF_DIR + ' &')
+ for snd in ['mic1','mic2','mic3','mic4','lab1','lab2','lab3','lab4', 'lab5', 'lab6']:
+ shutil.copyfile(Config.SOUNDS_DIR + '/' + snd , Config.SNDS_DIR + '/' + snd)
+ os.system('chmod 0777 ' + Config.SNDS_DIR + '/' + snd + ' &')
+
+ def read_file(self,file_path):
+ self.metadata['tamtam_subactivity'] = 'mini'
+
+ def write_file(self,file_path):
+ f = open(file_path,'w')
+ f.close()
+
+
+
+if __name__ == "__main__":
+ if len(sys.argv) > 1 :
+ mainwin = TamTam(None, sys.argv[1])
+ else:
+ mainwin = TamTam(None, 'welcome')
+
+ gtk.gdk.threads_init()
+ gtk.main()
+
+ sys.exit(0)
+
+
+
+
+
+
+
+
+ def run_edit_mode():
+ tamtam = MainWindow()
+ mainwin = gtk.Window(gtk.WINDOW_TOPLEVEL)
+ mainwin.set_title('TamTam Player')
+ display = mainwin.get_display()
+ screen = gtk.gdk.Display.get_default_screen(display)
+ mainwin.set_geometry_hints( None, screen.get_width(), screen.get_height(), screen.get_width(), screen.get_height(), screen.get_width(), screen.get_height() )
+ #mainwin.fullscreen() # don't need to specify full screen, it seem to sit properly anyway
+ mainwin.set_resizable(False)
+ mainwin.connect('destroy' , tamtam.destroy )
+ #mainwin.connect( "configure-event", tamtam.handleConfigureEvent )
+ mainwin.connect( "key-press-event", tamtam.onKeyPress )
+ mainwin.connect( "key-release-event", tamtam.onKeyRelease )
+ mainwin.connect( "delete_event", tamtam.delete_event )
+ mainwin.add(tamtam)
+ tamtam.show()
+ mainwin.show()
+ gtk.main()
diff --git a/TamTamEdit.activity/activity/activity-tamtamedit.svg b/TamTamEdit.activity/activity/activity-tamtamedit.svg
new file mode 100644
index 0000000..44f1ba6
--- /dev/null
+++ b/TamTamEdit.activity/activity/activity-tamtamedit.svg
@@ -0,0 +1,22 @@
+<?xml version="1.0" ?><!DOCTYPE svg PUBLIC '-//W3C//DTD SVG 1.1//EN' 'http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd' [
+ <!ENTITY stroke_color "#010101">
+ <!ENTITY fill_color "#FFFFFF">
+]><svg enable-background="new 0 0 55 55" height="55px" version="1.1" viewBox="0 0 55 55" width="55px" x="0px" xml:space="preserve" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" y="0px"><g display="block" id="activity-edittam">
+ <rect display="inline" fill="&fill_color;" height="20" width="44.332" x="5.543" y="25"/>
+ <path d=" M10.958,37.7c1.353-1.464,3.64-1.547,5.112-0.189c1.467,1.358,1.557,3.646,0.201,5.111c-1.355,1.464-3.644,1.546-5.11,0.189 C9.691,41.452,9.6,39.163,10.958,37.7z" display="inline" fill="&stroke_color;" id="path25_1_" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round"/>
+ <path d=" M21.881,27.854c1.354-1.464,3.641-1.548,5.111-0.19c1.468,1.359,1.558,3.646,0.202,5.113c-1.355,1.464-3.644,1.545-5.11,0.19 C20.616,31.607,20.524,29.319,21.881,27.854z" display="inline" fill="&stroke_color;" id="path25_2_" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round"/>
+ <path d=" M36.845,33.095c1.354-1.464,3.641-1.547,5.112-0.189c1.466,1.357,1.557,3.646,0.2,5.109c-1.354,1.464-3.643,1.549-5.109,0.189 C35.58,36.847,35.487,34.56,36.845,33.095z" display="inline" fill="&stroke_color;" id="path25_3_" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round"/>
+ <polygon display="inline" fill="&stroke_color;" points="16.737,19.72 27.58,15.817 27.58,11.817 16.737,15.72 " stroke="&stroke_color;" stroke-width="2.25"/>
+
+ <line display="inline" fill="none" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.25" x1="27.58" x2="27.58" y1="11.817" y2="30.031"/>
+
+ <line display="inline" fill="none" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.25" x1="16.737" x2="16.737" y1="15.72" y2="40.44"/>
+
+ <line display="inline" fill="none" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="2.25" x1="42.532" x2="42.532" y1="34.512" y2="13.692"/>
+
+ <line display="inline" fill="none" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5" x1="5.543" x2="49.875" y1="25" y2="25"/>
+
+ <line display="inline" fill="none" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5" x1="5.543" x2="49.875" y1="35" y2="35"/>
+
+ <line display="inline" fill="none" stroke="&stroke_color;" stroke-linecap="round" stroke-linejoin="round" stroke-width="3.5" x1="5.543" x2="49.875" y1="45" y2="45"/>
+</g></svg> \ No newline at end of file
diff --git a/TamTamEdit.activity/activity/activity.info b/TamTamEdit.activity/activity/activity.info
new file mode 100644
index 0000000..65e5044
--- /dev/null
+++ b/TamTamEdit.activity/activity/activity.info
@@ -0,0 +1,6 @@
+[Activity]
+name = TamTamEdit
+service_name = org.laptop.TamTamEdit
+icon = activity-tamtamedit
+class = TamTam.TamTamEdit
+activity_version = 40
diff --git a/TamTamEdit.activity/common b/TamTamEdit.activity/common
new file mode 120000
index 0000000..60d3b0a
--- /dev/null
+++ b/TamTamEdit.activity/common
@@ -0,0 +1 @@
+../common \ No newline at end of file
diff --git a/TamTamEdit.activity/icons/XYBut.svg b/TamTamEdit.activity/icons/XYBut.svg
new file mode 100644
index 0000000..54d7563
--- /dev/null
+++ b/TamTamEdit.activity/icons/XYBut.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="30px" height="30px" viewBox="0 0 30 30" enable-background="new 0 0 30 30" xml:space="preserve">
+<g>
+ <circle fill="#808284" cx="15" cy="15" r="9.376"/>
+ <g>
+ <path fill="#FFFFFF" stroke="#808284" stroke-width="2.25" d="M21.5,14.998c0,3.584-2.915,6.502-6.499,6.502
+ c-3.584,0-6.501-2.918-6.501-6.502S11.417,8.5,15.001,8.5C18.585,8.5,21.5,11.413,21.5,14.998z M15.003,2
+ C7.832,2,2,7.831,2,14.998C2,22.165,7.832,28,15.003,28C22.168,28,28,22.165,28,14.998C28,7.831,22.168,2,15.003,2z"/>
+ </g>
+</g>
+</svg>
diff --git a/TamTamEdit.activity/icons/XYButDown.svg b/TamTamEdit.activity/icons/XYButDown.svg
new file mode 100644
index 0000000..201df5f
--- /dev/null
+++ b/TamTamEdit.activity/icons/XYButDown.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="30px" height="30px" viewBox="0 0 30 30" enable-background="new 0 0 30 30" xml:space="preserve">
+<g>
+ <circle cx="15" cy="15" r="6.933"/>
+ <g>
+ <path fill="none" stroke="#808284" stroke-width="2.25" d="M21.5,14.998c0,3.585-2.915,6.502-6.499,6.502
+ c-3.584,0-6.501-2.917-6.501-6.502c0-3.584,2.917-6.498,6.501-6.498C18.585,8.5,21.5,11.414,21.5,14.998z M15.003,2
+ C7.832,2,2,7.831,2,14.998C2,22.165,7.832,28,15.003,28C22.169,28,28,22.165,28,14.998C28,7.831,22.169,2,15.003,2z"/>
+ </g>
+</g>
+</svg>
diff --git a/TamTamEdit.activity/icons/XYButDownClick.svg b/TamTamEdit.activity/icons/XYButDownClick.svg
new file mode 100644
index 0000000..9e0d222
--- /dev/null
+++ b/TamTamEdit.activity/icons/XYButDownClick.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="30px" height="30px" viewBox="0 0 30 30" enable-background="new 0 0 30 30" xml:space="preserve">
+<circle fill="none" cx="15" cy="15" r="6.933"/>
+<g>
+ <path fill="none" stroke="#808284" stroke-width="2.25" d="M21.5,14.998c0,3.585-2.915,6.502-6.499,6.502
+ c-3.584,0-6.501-2.917-6.501-6.502c0-3.584,2.917-6.498,6.501-6.498C18.585,8.5,21.5,11.414,21.5,14.998z M15.003,2
+ C7.832,2,2,7.831,2,14.998C2,22.165,7.832,28,15.003,28C22.169,28,28,22.165,28,14.998C28,7.831,22.169,2,15.003,2z"/>
+</g>
+</svg>
diff --git a/TamTamEdit.activity/icons/accept.svg b/TamTamEdit.activity/icons/accept.svg
new file mode 100755
index 0000000..a2f9e28
--- /dev/null
+++ b/TamTamEdit.activity/icons/accept.svg
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="45px" height="45px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+<g>
+
+ <path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="
+ M5.78,28.079c0-12.43,10.071-22.499,22.499-22.499c12.431,0,22.501,10.069,22.501,22.499c0,12.431-10.07,22.501-22.501,22.501
+ C15.851,50.58,5.78,40.51,5.78,28.079z"/>
+</g>
+<g>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" x1="16.788" y1="26.691" x2="25.781" y2="38.706"/>
+
+ <line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" x1="25.781" y1="38.706" x2="39.773" y2="18.704"/>
+</g>
+</svg>
diff --git a/TamTamEdit.activity/icons/add.svg b/TamTamEdit.activity/icons/add.svg
new file mode 100644
index 0000000..b605b49
--- /dev/null
+++ b/TamTamEdit.activity/icons/add.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="56.688px" height="55.5px" viewBox="0 0 56.688 55.5" enable-background="new 0 0 56.688 55.5" xml:space="preserve">
+<path fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M16.226,14.143
+ l24.013-0.002c4.408-0.002,7.998,2.578,7.993,5.754v17.283c0.005,3.18-3.58,5.762-7.991,5.758H16.229
+ c-4.414,0.002-8.002-2.578-7.996-5.754L8.233,19.897C8.23,16.72,11.815,14.136,16.226,14.143z"/>
+<line fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel" x1="28.011" y1="22.5" x2="28.177" y2="34.501"/>
+<line fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel" x1="22.177" y1="28.251" x2="34.177" y2="28.251"/>
+</svg>
diff --git a/TamTamEdit.activity/icons/arrow-down.svg b/TamTamEdit.activity/icons/arrow-down.svg
new file mode 100644
index 0000000..66c6ac9
--- /dev/null
+++ b/TamTamEdit.activity/icons/arrow-down.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="35px" height="30px" viewBox="0 0 35 30" enable-background="new 0 0 35 30" xml:space="preserve">
+<polygon fill="#FFFFFF" points="0,0.006 35,0 17.5,30 "/>
+</svg>
diff --git a/TamTamEdit.activity/icons/arrow-up.svg b/TamTamEdit.activity/icons/arrow-up.svg
new file mode 100644
index 0000000..a5ac702
--- /dev/null
+++ b/TamTamEdit.activity/icons/arrow-up.svg
@@ -0,0 +1,7 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="35px" height="30px" viewBox="0 0 35 30" enable-background="new 0 0 35 30" xml:space="preserve">
+<polygon fill="#FFFFFF" points="35,29.994 0,30 17.5,0 "/>
+</svg>
diff --git a/TamTamEdit.activity/icons/cancel.svg b/TamTamEdit.activity/icons/cancel.svg
new file mode 100755
index 0000000..59512eb
--- /dev/null
+++ b/TamTamEdit.activity/icons/cancel.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="45px" height="45px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+<path fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" d="
+ M5.78,28.08c0-12.431,10.071-22.5,22.5-22.5c12.432,0,22.5,10.069,22.5,22.5c0,12.432-10.068,22.5-22.5,22.5
+ C15.851,50.58,5.78,40.512,5.78,28.08z"/>
+<line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" x1="20.155" y1="19.956" x2="37.029" y2="36.83"/>
+<line fill-rule="evenodd" clip-rule="evenodd" fill="none" stroke="#FFFFFF" stroke-width="4" stroke-linecap="round" stroke-linejoin="round" x1="37.029" y1="19.956" x2="20.155" y2="36.83"/>
+</svg>
diff --git a/TamTamEdit.activity/icons/dice.svg b/TamTamEdit.activity/icons/dice.svg
new file mode 100644
index 0000000..cc8d38f
--- /dev/null
+++ b/TamTamEdit.activity/icons/dice.svg
@@ -0,0 +1,36 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="56.688px" height="55.5px" viewBox="0 0 56.688 55.5" enable-background="new 0 0 56.688 55.5" xml:space="preserve">
+<path fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M24.16,18.908
+ c-0.114-3.243-0.215-6.109-0.215-6.109c-0.092-2.796,2.098-5.14,4.893-5.24l15.199-0.534c2.796-0.094,5.139,2.096,5.239,4.89
+ l0.535,15.2c0.092,2.797-2.099,5.14-4.891,5.241c0,0-4.982,0.175-9.242,0.324"/>
+<path fill="#CCCCCC" stroke="#CCCCCC" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M43.166,23.722
+ c1.198-0.038,2.206,0.891,2.249,2.098c0.038,1.2-0.895,2.2-2.098,2.249c-1.198,0.039-2.204-0.902-2.242-2.101
+ C41.03,24.771,41.967,23.761,43.166,23.722z"/>
+<path fill="#CCCCCC" stroke="#CCCCCC" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M37.176,18.138
+ c1.195-0.046,2.207,0.891,2.245,2.09c0.039,1.199-0.892,2.207-2.097,2.248c-1.199,0.038-2.199-0.894-2.242-2.101
+ C35.037,19.181,35.978,18.176,37.176,18.138z"/>
+<path fill="#CCCCCC" stroke="#CCCCCC" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M31.183,12.547
+ c1.199-0.038,2.21,0.898,2.249,2.098c0.038,1.199-0.895,2.2-2.098,2.249c-1.197,0.038-2.202-0.902-2.239-2.101
+ C29.047,13.596,29.985,12.585,31.183,12.547z"/>
+<path fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M34.215,22.64l3.301,16.967
+ c0.608,3.117-1.432,6.146-4.551,6.746l-16.967,3.299c-3.119,0.609-6.146-1.432-6.748-4.549L5.951,28.136
+ c-0.609-3.118,1.432-6.146,4.551-6.747l16.966-3.3C30.587,17.48,33.615,19.521,34.215,22.64z"/>
+<path fill="#CCCCCC" stroke="#CCCCCC" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M26.95,24.054
+ c1.341-0.263,2.633,0.617,2.896,1.95c0.255,1.341-0.617,2.633-1.951,2.896c-1.341,0.264-2.641-0.617-2.896-1.95
+ C24.737,25.609,25.61,24.318,26.95,24.054z"/>
+<path fill="#CCCCCC" stroke="#CCCCCC" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M29.461,36.98
+ c1.341-0.264,2.632,0.608,2.896,1.949c0.265,1.342-0.616,2.635-1.949,2.896c-1.342,0.255-2.634-0.617-2.896-1.958
+ C27.247,38.536,28.128,37.236,29.461,36.98z"/>
+<path fill="#CCCCCC" stroke="#CCCCCC" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M16.542,39.491
+ c1.333-0.265,2.633,0.616,2.888,1.95c0.264,1.342-0.609,2.634-1.95,2.896c-1.333,0.264-2.633-0.617-2.896-1.951
+ C14.328,41.046,15.201,39.753,16.542,39.491z"/>
+<path fill="#CCCCCC" stroke="#CCCCCC" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M21.741,31.773
+ c1.342-0.265,2.633,0.617,2.896,1.949c0.264,1.342-0.609,2.633-1.95,2.896c-1.341,0.263-2.633-0.617-2.896-1.95
+ C19.537,33.327,20.41,32.036,21.741,31.773z"/>
+<path fill="#CCCCCC" stroke="#CCCCCC" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M14.023,26.564
+ c1.342-0.255,2.642,0.617,2.897,1.958c0.263,1.334-0.609,2.633-1.95,2.889c-1.341,0.264-2.633-0.609-2.896-1.949
+ C11.819,28.119,12.69,26.827,14.023,26.564z"/>
+</svg>
diff --git a/TamTamEdit.activity/icons/diceB.svg b/TamTamEdit.activity/icons/diceB.svg
new file mode 100644
index 0000000..9336ad9
--- /dev/null
+++ b/TamTamEdit.activity/icons/diceB.svg
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="55px" height="55px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+<path fill="none" stroke="#C8C8C8" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M19.406,5.889l23.942,5.085
+ c4.4,0.933,7.219,5.267,6.274,9.665l-5.085,23.941c-0.934,4.401-5.268,7.219-9.663,6.278l-23.943-5.09
+ c-4.4-0.929-7.218-5.265-6.275-9.663l5.084-23.94C10.673,7.762,15.009,4.943,19.406,5.889z"/>
+<path fill="#CCCCCC" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M17.646,14.184
+ c0.398-1.894,2.26-3.091,4.143-2.696c1.891,0.409,3.091,2.261,2.697,4.146c-0.398,1.894-2.266,3.101-4.145,2.696
+ C18.448,17.929,17.247,16.077,17.646,14.184z"/>
+<path fill="#CCCCCC" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M35.884,18.062
+ c0.397-1.893,2.248-3.093,4.143-2.694c1.896,0.396,3.096,2.26,2.697,4.141c-0.409,1.892-2.265,3.093-4.155,2.693
+ C36.688,21.809,35.479,19.942,35.884,18.062z"/>
+<path fill="#CCCCCC" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M30.97,41.183
+ c0.393-1.885,2.26-3.092,4.14-2.687c1.894,0.399,3.097,2.252,2.694,4.146c-0.393,1.885-2.259,3.09-4.144,2.694
+ C31.772,44.928,30.568,43.074,30.97,41.183z"/>
+<path fill="#CCCCCC" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M12.725,37.314
+ c0.412-1.893,2.266-3.104,4.156-2.694c1.884,0.396,3.094,2.251,2.686,4.141c-0.396,1.894-2.25,3.095-4.142,2.695
+ C13.533,41.047,12.33,39.195,12.725,37.314z"/>
+<path fill="#CCCCCC" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M15.151,25.922
+ c0.399-1.894,2.26-3.091,4.143-2.696c1.89,0.409,3.09,2.261,2.697,4.145c-0.399,1.894-2.266,3.101-4.145,2.697
+ C15.953,29.667,14.752,27.815,15.151,25.922z"/>
+<path fill="#CCCCCC" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M33.39,29.8
+ c0.396-1.893,2.247-3.093,4.142-2.694c1.896,0.396,3.096,2.26,2.697,4.141c-0.409,1.891-2.264,3.093-4.154,2.692
+ C34.193,33.547,32.984,31.681,33.39,29.8z"/>
+</svg>
diff --git a/TamTamEdit.activity/icons/duplicate.svg b/TamTamEdit.activity/icons/duplicate.svg
new file mode 100644
index 0000000..0438f13
--- /dev/null
+++ b/TamTamEdit.activity/icons/duplicate.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="56.688px" height="55.5px" viewBox="0 0 56.688 55.5" enable-background="new 0 0 56.688 55.5" xml:space="preserve">
+<path fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M12.181,34.714
+ c-3.849-0.018-6.96-2.285-6.963-5.07L5.212,14.435c0.007-2.798,3.142-5.063,7.002-5.066l20.997-0.004
+ c3.863,0.004,6.99,2.275,6.995,5.071c0,0,0,0.778,0.001,1.982"/>
+<path fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M19.893,17.309
+ l24.013-0.002c4.408-0.002,7.998,2.578,7.993,5.754v17.284c0.005,3.18-3.58,5.762-7.991,5.758H19.896
+ c-4.414,0.002-8.002-2.578-7.996-5.754L11.9,23.064C11.897,19.886,15.482,17.303,19.893,17.309z"/>
+<line fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel" x1="31.678" y1="25.667" x2="31.844" y2="37.667"/>
+<line fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="bevel" x1="25.844" y1="31.417" x2="37.844" y2="31.417"/>
+</svg>
diff --git a/TamTamEdit.activity/icons/edit-brush.svg b/TamTamEdit.activity/icons/edit-brush.svg
new file mode 100644
index 0000000..e888321
--- /dev/null
+++ b/TamTamEdit.activity/icons/edit-brush.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="55px"
+ height="55px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+
+<g id="Brush" >
+ <g display="inline">
+ <path fill="#FFFFFF" d="M26.939,32.777c0,0,9.546-10.04,12.062-13.721c3.697-5.406,5.988-10.829,3.994-11.775
+ c-1.468-0.697-6.469,3.434-9.471,7.896C31.491,18.205,24,30.635,24,30.635L26.939,32.777z"/>
+ <path fill="#FFFFFF" d="M25.613,34.668l-2.971-2.166c0,0-3.054,1.592-4.602,3.862c-1.546,2.284-2.31,6.661-4.752,9.01
+ c-1.465,1.409-0.186,1.085-0.186,1.085s7.907-2.985,9.786-5.477C24.75,38.512,25.613,34.668,25.613,34.668z"/>
+ </g>
+</g>
+
+</svg>
diff --git a/TamTamEdit.activity/icons/edit-pencil.svg b/TamTamEdit.activity/icons/edit-pencil.svg
new file mode 100644
index 0000000..c7a1ef9
--- /dev/null
+++ b/TamTamEdit.activity/icons/edit-pencil.svg
@@ -0,0 +1,21 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14948) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px" width="55px"
+ height="55px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+
+<g id="Pencil" >
+ <g display="inline">
+ <path fill="#FFFFFF" d="M12.727,45.969c-0.053,0.132-0.02,0.281,0.09,0.373s0.264,0.103,0.385,0.025l4.635-2.984l-3.009-2.407
+ L12.727,45.969z M42.967,14.071c0-0.073-0.004-0.15-0.014-0.227c-0.112-0.949-0.903-2.109-2.062-3.025
+ c-1.16-0.915-2.474-1.413-3.424-1.304c-0.414,0.052-0.747,0.228-0.973,0.491l-0.021-0.015L19.716,32.17l-0.073,0.073l-0.011,0.015
+ c-0.026,0.029-0.029,0.069-0.05,0.103l-2.177,2.677c0,0,0.128,0.099,0.256,0.201l-0.002,0.004l-0.009,0.011
+ c-0.128-0.103-0.252-0.197-0.252-0.197l-0.042,0.051c-0.097,0.121-0.155,0.271-0.203,0.429c-0.015,0.019-0.046,0.025-0.057,0.048
+ l-1.361,3.236l4.097,3.278l3.202-2.062c0.019-0.011,0.015-0.037,0.031-0.052c0.083-0.065,0.178-0.121,0.243-0.201
+ c0.008-0.011,0.043-0.059,0.048-0.062l2.237-2.79l0.033-0.033l-0.004-0.004l0.023-0.029l0.006,0.004L42.59,15.02
+ c0.029-0.029,0.071-0.044,0.1-0.077c0.032-0.044,0.049-0.103,0.076-0.15l0.015-0.019l-0.003-0.003
+ C42.895,14.566,42.967,14.339,42.967,14.071z"/>
+ </g>
+</g>
+
+</svg>
diff --git a/TamTamEdit.activity/icons/edit-pointer.svg b/TamTamEdit.activity/icons/edit-pointer.svg
new file mode 100644
index 0000000..2397df3
--- /dev/null
+++ b/TamTamEdit.activity/icons/edit-pointer.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="55px" height="55px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+<rect x="5.285" y="9.417" fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" stroke-dasharray="3" width="31.466" height="25.083"/>
+<polygon fill="#423F3D" stroke="#FFFFFF" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" points="
+ 33.225,30.118 39.89,46.332 42.087,40.904 48.34,46.951 50.126,45.508 43.874,39.05 49.576,36.714 "/>
+</svg>
diff --git a/TamTamEdit.activity/icons/grid.svg b/TamTamEdit.activity/icons/grid.svg
new file mode 100644
index 0000000..5286401
--- /dev/null
+++ b/TamTamEdit.activity/icons/grid.svg
@@ -0,0 +1,26 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="75px" height="30px" viewBox="0 0 75 30" enable-background="new 0 0 75 30" xml:space="preserve">
+<rect x="15.833" y="0.667" fill="#7D7D7D" stroke="#7D7D7D" stroke-width="0.25" stroke-linecap="round" stroke-linejoin="round" width="44.333" height="25.833"/>
+<line fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" x1="60" y1="0.97" x2="60" y2="25.97"/>
+<line fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" x1="15.51" y1="25.97" x2="15.51" y2="-0.03"/>
+<line fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" x1="42.204" y1="1.303" x2="42.204" y2="26.304"/>
+<line fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" x1="24.408" y1="26.637" x2="24.408" y2="0.636"/>
+<line fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" x1="51.102" y1="1.303" x2="51.102" y2="26.304"/>
+<line fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" x1="33.306" y1="26.637" x2="33.306" y2="0.636"/>
+<line fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" x1="59.66" y1="26.63" x2="15" y2="26.63"/>
+<line fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" x1="60.493" y1="0.463" x2="15.833" y2="0.463"/>
+<g>
+ <g>
+ <line fill="#808292" x1="43.584" y1="12.999" x2="53.001" y2="22.5"/>
+ <polygon fill="#808292" points="47.04,12.54 44.236,13.657 43.094,16.45 40.433,9.82 "/>
+ </g>
+ <g>
+
+ <line fill="none" stroke="#323232" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="43.584" y1="12.999" x2="53.001" y2="22.5"/>
+ <polygon fill="#323232" points="53.952,11.62 45.541,14.973 42.114,23.354 34.13,3.461 "/>
+ </g>
+</g>
+</svg>
diff --git a/TamTamEdit.activity/icons/notedur.svg b/TamTamEdit.activity/icons/notedur.svg
new file mode 100644
index 0000000..cd11aae
--- /dev/null
+++ b/TamTamEdit.activity/icons/notedur.svg
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="75px" height="30px" viewBox="0 0 75 30" enable-background="new 0 0 75 30" xml:space="preserve">
+<g>
+ <g>
+ <path fill="#808284" d="M15,9.36c0-1.38,1.12-2.5,2.5-2.5h40c1.38,0,2.5,1.12,2.5,2.5c0,1.37-1.12,2.5-2.5,2.5h-40
+ C16.12,11.86,15,10.73,15,9.36z"/>
+ <path fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" d="M15,9.36
+ c0-1.38,1.12-2.5,2.5-2.5h40c1.38,0,2.5,1.12,2.5,2.5c0,1.37-1.12,2.5-2.5,2.5h-40C16.12,11.86,15,10.73,15,9.36z"/>
+ </g>
+ <g>
+
+ <line fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" x1="60" y1="17.939" x2="60" y2="25"/>
+
+ <line fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" x1="15.51" y1="25" x2="15.51" y2="17.939"/>
+
+ <line fill="none" stroke="#FFFFFF" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" x1="59.66" y1="21.63" x2="15" y2="21.63"/>
+ </g>
+</g>
+</svg>
diff --git a/TamTamEdit.activity/icons/props.svg b/TamTamEdit.activity/icons/props.svg
new file mode 100644
index 0000000..18f6622
--- /dev/null
+++ b/TamTamEdit.activity/icons/props.svg
@@ -0,0 +1,25 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="55px" height="55px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+<path fill="#FFFFFF" d="M33.958,27.5c0,3.562-2.896,6.458-6.458,6.458c-3.562,0-6.458-2.896-6.458-6.458
+ c0-3.562,2.896-6.458,6.458-6.458C31.062,21.042,33.958,23.938,33.958,27.5z"/>
+<polygon fill="#FFFFFF" points="44.26,25.326 45.87,25.119 47.082,25.262 50,26.575 50,28.425 47.082,29.738 45.87,29.881
+ 44.26,29.674 43.525,32.927 45.064,33.449 46.1,34.113 48.169,36.584 47.382,38.256 44.189,38.157 43.031,37.744 41.668,36.846
+ 39.625,39.451 40.784,40.602 41.438,41.661 42.244,44.797 40.82,45.947 37.988,44.456 37.113,43.575 36.273,42.165 33.32,43.611
+ 33.877,45.166 34.009,46.396 33.391,49.587 31.622,50 29.711,47.395 29.295,46.227 29.145,44.582 25.855,44.582 25.705,46.227
+ 25.289,47.395 23.378,50 21.609,49.587 20.991,46.396 21.123,45.166 21.68,43.611 18.727,42.165 17.886,43.575 17.011,44.456
+ 14.18,45.947 12.756,44.797 13.562,41.661 14.216,40.602 15.375,39.451 13.332,36.846 11.969,37.744 10.811,38.157 7.618,38.256
+ 6.831,36.584 8.9,34.113 9.935,33.449 11.474,32.927 10.74,29.674 9.13,29.881 7.918,29.738 5,28.425 5,26.575 7.918,25.262
+ 9.13,25.119 10.74,25.326 11.474,22.073 9.935,21.551 8.9,20.887 6.831,18.416 7.618,16.744 10.811,16.843 11.969,17.256
+ 13.332,18.155 15.375,15.549 14.216,14.399 13.562,13.339 12.756,10.203 14.18,9.053 17.011,10.544 17.886,11.425 18.727,12.835
+ 21.68,11.389 21.123,9.834 20.991,8.604 21.609,5.413 23.378,5 25.289,7.606 25.705,8.774 25.855,10.418 29.145,10.418
+ 29.295,8.774 29.711,7.606 31.622,5 33.391,5.413 34.009,8.604 33.877,9.834 33.32,11.389 36.273,12.835 37.113,11.425
+ 37.988,10.544 40.82,9.053 42.244,10.203 41.438,13.339 40.784,14.399 39.625,15.549 41.668,18.155 43.031,17.256 44.189,16.843
+ 47.382,16.744 48.169,18.416 46.1,20.887 45.064,21.551 43.525,22.073 "/>
+<ellipse fill="#3B3C3E" cx="27.5" cy="27.5" rx="14.593" ry="14.826"/>
+<path fill="#FFFFFF" d="M16.444,27.5c0-6.2,4.953-11.232,11.056-11.232c6.104,0,11.057,5.032,11.057,11.232
+ S33.604,38.732,27.5,38.732C21.397,38.732,16.444,33.7,16.444,27.5z"/>
+<ellipse fill="#3B3C3E" cx="27.5" cy="27.5" rx="4.863" ry="4.942"/>
+</svg>
diff --git a/TamTamEdit.activity/icons/recordK.svg b/TamTamEdit.activity/icons/recordK.svg
new file mode 100644
index 0000000..bd1e58c
--- /dev/null
+++ b/TamTamEdit.activity/icons/recordK.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="55px" height="55px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+<g>
+ <circle fill="#808284" cx="27" cy="27" r="22.5"/>
+ <circle fill="none" stroke="#4C4D4F" stroke-width="2.25" cx="26.999" cy="27.092" r="18.677"/>
+ <path fill="#FFFFFF" d="M34.895,27c0,4.354-3.541,7.895-7.895,7.895c-4.354,0-7.895-3.541-7.895-7.895
+ c0-4.354,3.54-7.895,7.895-7.895C31.354,19.105,34.895,22.646,34.895,27z"/>
+</g>
+<g enable-background="new ">
+ <path fill="#FFFFFF" d="M35.615,30.777h4.377v8.638h0.087c0.435-0.753,0.898-1.449,1.333-2.145l4.435-6.493h5.42l-6.463,8.318
+ l6.812,11.217h-5.16l-4.782-8.434l-1.681,2.058v6.376h-4.377V30.777z"/>
+</g>
+</svg>
diff --git a/TamTamEdit.activity/icons/recordO.svg b/TamTamEdit.activity/icons/recordO.svg
new file mode 100644
index 0000000..83f8cfe
--- /dev/null
+++ b/TamTamEdit.activity/icons/recordO.svg
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="55px" height="55px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+<g>
+ <circle fill="#808284" cx="27" cy="27" r="22.5"/>
+ <circle fill="none" stroke="#4C4D4F" stroke-width="2.25" cx="26.999" cy="27" r="18.677"/>
+ <path fill="#FFFFFF" d="M34.895,27c0,4.354-3.541,7.895-7.895,7.895S19.104,31.354,19.104,27c0-4.354,3.54-7.895,7.895-7.895
+ C31.354,19.105,34.895,22.646,34.895,27z"/>
+</g>
+<g>
+ <path fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" d="M31,42.936
+ c0.043,0.068,4.286-17.459,10.357,0"/>
+ <path fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" d="M51,41.445
+ c-0.045-0.068-4.286,17.461-10.357,0"/>
+</g>
+</svg>
diff --git a/TamTamEdit.activity/icons/sideR.svg b/TamTamEdit.activity/icons/sideR.svg
new file mode 100644
index 0000000..f5eb202
--- /dev/null
+++ b/TamTamEdit.activity/icons/sideR.svg
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="20px" height="20px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve">
+<g>
+ <g>
+ <line fill="#FFFFFF" x1="14.292" y1="10.229" x2="5.667" y2="10.229"/>
+ </g>
+ <g>
+
+ <line fill="none" stroke="#ED1C24" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="14.292" y1="10.229" x2="5.667" y2="10.229"/>
+ </g>
+</g>
+<line fill="#FFFFFF" stroke="#ED1C24" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="17.044" y1="10.133" x2="13.153" y2="6.241"/>
+<line fill="#FFFFFF" stroke="#ED1C24" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="17.044" y1="10.348" x2="13.153" y2="14.24"/>
+<line fill="#FFFFFF" stroke="#ED1C24" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="2.728" y1="10.348" x2="6.62" y2="14.24"/>
+<line fill="#FFFFFF" stroke="#ED1C24" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="2.728" y1="10.133" x2="6.62" y2="6.24"/>
+</svg>
diff --git a/TamTamEdit.activity/icons/sideW.svg b/TamTamEdit.activity/icons/sideW.svg
new file mode 100644
index 0000000..0b44384
--- /dev/null
+++ b/TamTamEdit.activity/icons/sideW.svg
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="20px" height="20px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve">
+<g>
+ <g>
+ <line fill="#FFFFFF" x1="14.292" y1="10.229" x2="5.667" y2="10.229"/>
+ </g>
+ <g>
+
+ <line fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="14.292" y1="10.229" x2="5.667" y2="10.229"/>
+ </g>
+</g>
+<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="17.044" y1="10.133" x2="13.153" y2="6.241"/>
+<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="17.044" y1="10.348" x2="13.153" y2="14.24"/>
+<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="2.728" y1="10.348" x2="6.62" y2="14.24"/>
+<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="2.728" y1="10.133" x2="6.62" y2="6.24"/>
+</svg>
diff --git a/TamTamEdit.activity/icons/tam-help.svg b/TamTamEdit.activity/icons/tam-help.svg
new file mode 100644
index 0000000..2923bb3
--- /dev/null
+++ b/TamTamEdit.activity/icons/tam-help.svg
@@ -0,0 +1,18 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="45.395px" height="45.395px" viewBox="0 0 45.395 45.395" enable-background="new 0 0 45.395 45.395" xml:space="preserve">
+<circle fill="#808284" cx="22.297" cy="23.037" r="22.5"/>
+<circle fill="none" stroke="#4C4D4F" stroke-width="2.25" cx="22.295" cy="23.129" r="21.572"/>
+<circle fill="none" stroke="#4C4D4F" cx="22.628" cy="22.89" r="17.085"/>
+<g>
+ <path fill="#FFFFFF" stroke="#FFFFFF" d="M18.717,15.967c1.383-0.625,2.621-0.938,3.715-0.938c1.477,0,2.617,0.332,3.422,0.996
+ s1.207,1.645,1.207,2.941c0,1.344-0.871,2.738-2.613,4.184c-0.758,0.625-1.221,1.041-1.389,1.248s-0.303,0.459-0.404,0.756
+ s-0.152,0.668-0.152,1.113v0.645h-1.605v-0.668c0-1.359,0.434-2.418,1.301-3.176l1.547-1.359c0.5-0.422,0.867-0.85,1.102-1.283
+ s0.352-0.896,0.352-1.389c0-0.812-0.258-1.436-0.773-1.869s-1.219-0.65-2.109-0.65c-0.805,0-1.82,0.289-3.047,0.867L18.717,15.967z
+ M21.905,29.198c0.414,0,0.762,0.141,1.043,0.422s0.422,0.629,0.422,1.043c0,0.422-0.143,0.783-0.428,1.084
+ s-0.631,0.451-1.037,0.451c-0.398,0-0.738-0.15-1.02-0.451s-0.422-0.662-0.422-1.084s0.139-0.771,0.416-1.049
+ S21.498,29.198,21.905,29.198z"/>
+</g>
+</svg>
diff --git a/TamTamEdit.activity/icons/tempo1.svg b/TamTamEdit.activity/icons/tempo1.svg
new file mode 100644
index 0000000..bb9aeec
--- /dev/null
+++ b/TamTamEdit.activity/icons/tempo1.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
+<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M23.5,6.5c3,3,7,7,9,11c-7,5-4,6-3,26c-1,1-8,1-9,0c0,0,2,1,2-1
+ c0-3-2-7-2-11c0-2,1-4,1-6c0-3-2-1-2-3c0-3,3-8,3-11c0-2-1-1-2-2v-3H23.5z"/>
+</svg>
diff --git a/TamTamEdit.activity/icons/tempo2.svg b/TamTamEdit.activity/icons/tempo2.svg
new file mode 100644
index 0000000..4a98310
--- /dev/null
+++ b/TamTamEdit.activity/icons/tempo2.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
+<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M27.5,44.5v-3C28.5,42.5,28.5,43.5,27.5,44.5z M26.5,10.5
+ c2,2,2,6,2,8c0,4-3,11-3,13s4,7,7,10c-2,2-4,3-5,5h-6c1-1,2-3,2-5c0-3-2-9-3-14c0,0,0-1-1,0v-6c0-3,3-8,3-11c0-1-2-2-2-6h3
+ C23.5,5.5,26.5,9.5,26.5,10.5z"/>
+</svg>
diff --git a/TamTamEdit.activity/icons/tempo3.svg b/TamTamEdit.activity/icons/tempo3.svg
new file mode 100644
index 0000000..bd893bd
--- /dev/null
+++ b/TamTamEdit.activity/icons/tempo3.svg
@@ -0,0 +1,8 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
+<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M30.5,17.5c0,3-2,2-2,4c0,3,4,14,7,21c-1,0-3,1-5,1c1-1,2,0,2-3
+ c0-2-4-7-6-10c-3,3-5,8-7,13c-1,0-3-1-4-1c3-3,7-14,7-18s-1-3-4-4c3-2,4-8,4-14h3C23.5,9.5,30.5,14.5,30.5,17.5z"/>
+</svg>
diff --git a/TamTamEdit.activity/icons/tempo4.svg b/TamTamEdit.activity/icons/tempo4.svg
new file mode 100644
index 0000000..6fa5afa
--- /dev/null
+++ b/TamTamEdit.activity/icons/tempo4.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
+<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M34.5,22.5c-1-1-2-4-5-6c-1,2,0,3,0,6c0,2-3,4-3,7c0,2,4,2,4,4
+ c0,3-1,4-2,5c0-1,0-3-1-4c-1,3-2,7-3,10c-4-3,0-6,0-9s-3-11-4-17l-4,4c1-5,8.25-11.12,7.25-16.12c0.68,0.68,3.029,0,2.87,2.12
+ C26.5,10.25,33.62,17.75,34.5,22.5z"/>
+</svg>
diff --git a/TamTamEdit.activity/icons/tempo5.svg b/TamTamEdit.activity/icons/tempo5.svg
new file mode 100644
index 0000000..9500e7e
--- /dev/null
+++ b/TamTamEdit.activity/icons/tempo5.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
+<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M24.5,13.5c2,1,5,3,5,6c0,2-2,3-2,5c0,9,11,4,11,13c-1,0-3-2-4-3
+ c-3-1-9,1-10-3c-2,3-5,7-7,11c-3,0-3-1-4-1c0-2,3-3,4-6s4-8,4-10c0-3-1-3-2-5c-1,0-2,1-3,2c0-1,2-3,2-4c1-2,3-5,2-8c0,0,1-1,4-2
+ C25.5,9.5,25.5,11.5,24.5,13.5z"/>
+</svg>
diff --git a/TamTamEdit.activity/icons/tempo6.svg b/TamTamEdit.activity/icons/tempo6.svg
new file mode 100644
index 0000000..9844fd6
--- /dev/null
+++ b/TamTamEdit.activity/icons/tempo6.svg
@@ -0,0 +1,11 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
+<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M22.5,10.5c3,2,7,5,7,7c0,3-4,8-4,10c0,3,1,3,1,5h5l2-2l2,2v4
+ c-1,0-3-2-5-2c-3,0-5,1-8,1c-1,3-2,7-2,10h-5c1-1,3-3,3-4c1-5,1-11,1-18l-1-1c-1,1-1.75,2.88-2.75,2.88c0,0-0.25-0.63-0.25-1.63
+ c4-4,2-8.25,2-13.25c0-1,0.25-2.5,0.38-5.38L22.5,5.5C23.12,6.5,22.5,8.5,22.5,10.5z"/>
+<polygon fill-rule="evenodd" clip-rule="evenodd" fill="#333333" stroke="#333333" stroke-linecap="round" stroke-linejoin="round" points="
+ 25,20 25.25,16.75 26.5,17.88 "/>
+</svg>
diff --git a/TamTamEdit.activity/icons/tempo7.svg b/TamTamEdit.activity/icons/tempo7.svg
new file mode 100644
index 0000000..54bed80
--- /dev/null
+++ b/TamTamEdit.activity/icons/tempo7.svg
@@ -0,0 +1,9 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
+<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M20.5,7.5c1,1,1,3,1,4c10,4,8,6,8,14c0,2,6,9,10,13c-1,2-2,4-4,5
+ c1.62-8.88-8.75-13.88-12-15c-1,1-1,0-1,2c0,3,2,5,3,7c-1,1-3,2-6,2c0-1,2-1,2-4c0-2-4-4-4-6c0-3,3-4,5-6c-3-8-8-2-11-6h6
+ c0-1,1,0,1-3c0-2-1-1-2-2l1-5H20.5z"/>
+</svg>
diff --git a/TamTamEdit.activity/icons/tempo8.svg b/TamTamEdit.activity/icons/tempo8.svg
new file mode 100644
index 0000000..2c0154f
--- /dev/null
+++ b/TamTamEdit.activity/icons/tempo8.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="50px" height="50px" viewBox="0 0 50 50" enable-background="new 0 0 50 50" xml:space="preserve">
+<path fill-rule="evenodd" clip-rule="evenodd" fill="#FFFFFF" d="M20.5,12.5c0.67,0.4,0.4,1.9,1.75,2.25s1.05-0.38,1.5-0.37
+ c4.971,0,10.95-0.88,11.75,7.12c-1-2-3-4-5-5l-4,1c1,2,4,4,5,7c1,1,1,4,1,6c3,3,8-1,11,6c-2.88-0.82-4.25-2.62-12.75-2.75
+ c-1.561-0.02-2.34-1.561-3.75-1.87c-3.42-0.76-4.67-0.38-5.5-0.38c-3,0-8,7-11,7c-2,0-3-1-3-2c4,2,8-4,9-7c2-1,5-1,8-3c-2-4-6-5-8-3
+ l-6-6l2-2c1,1,1,2,1,4c1,0,4.12,0.38,6.12-0.62L16.5,17.5v-5H20.5z"/>
+</svg>
diff --git a/TamTamEdit.activity/icons/updownR.svg b/TamTamEdit.activity/icons/updownR.svg
new file mode 100644
index 0000000..dff1c83
--- /dev/null
+++ b/TamTamEdit.activity/icons/updownR.svg
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="20px" height="20px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve">
+<g>
+ <g>
+ <line fill="#FFFFFF" x1="9.875" y1="5.834" x2="9.875" y2="14.459"/>
+ </g>
+ <g>
+
+ <line fill="none" stroke="#ED1C24" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="9.875" y1="5.834" x2="9.875" y2="14.459"/>
+ </g>
+</g>
+<line fill="#FFFFFF" stroke="#ED1C24" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="9.778" y1="3.082" x2="5.887" y2="6.973"/>
+<line fill="#FFFFFF" stroke="#ED1C24" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="9.993" y1="3.082" x2="13.886" y2="6.973"/>
+<line fill="#FFFFFF" stroke="#ED1C24" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="9.993" y1="17.398" x2="13.886" y2="13.506"/>
+<line fill="#FFFFFF" stroke="#ED1C24" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="9.778" y1="17.398" x2="5.886" y2="13.507"/>
+</svg>
diff --git a/TamTamEdit.activity/icons/updownW.svg b/TamTamEdit.activity/icons/updownW.svg
new file mode 100644
index 0000000..bcefcb3
--- /dev/null
+++ b/TamTamEdit.activity/icons/updownW.svg
@@ -0,0 +1,19 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="20px" height="20px" viewBox="0 0 20 20" enable-background="new 0 0 20 20" xml:space="preserve">
+<g>
+ <g>
+ <line fill="#FFFFFF" x1="9.875" y1="5.833" x2="9.875" y2="14.459"/>
+ </g>
+ <g>
+
+ <line fill="none" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="9.875" y1="5.833" x2="9.875" y2="14.459"/>
+ </g>
+</g>
+<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="9.778" y1="3.082" x2="5.887" y2="6.973"/>
+<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="9.993" y1="3.082" x2="13.886" y2="6.973"/>
+<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="9.993" y1="17.398" x2="13.886" y2="13.506"/>
+<line fill="#FFFFFF" stroke="#FFFFFF" stroke-width="3" stroke-linecap="round" stroke-linejoin="round" x1="9.778" y1="17.398" x2="5.886" y2="13.506"/>
+</svg>
diff --git a/TamTamEdit.activity/icons/voltemp.svg b/TamTamEdit.activity/icons/voltemp.svg
new file mode 100644
index 0000000..e8ed7e0
--- /dev/null
+++ b/TamTamEdit.activity/icons/voltemp.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="55px" height="55px" viewBox="0 0 55 55" enable-background="new 0 0 55 55" xml:space="preserve">
+<path fill="#808284" stroke="#FFFFFF" stroke-linecap="square" d="M0.245,18.559c0-2.208,1.38-4,3.052-4h48.836
+ c1.686,0,3.052,1.792,3.052,4c0,2.19-1.366,4-3.052,4H3.296C1.625,22.559,0.245,20.75,0.245,18.559z"/>
+<path fill="#FFFFFF" d="M33.774,18.644c0,3.25-2.683,5.885-6.006,5.885c-3.31,0-5.995-2.635-5.995-5.885
+ c0-3.262,2.685-5.897,5.995-5.897C31.092,12.747,33.774,15.381,33.774,18.644z"/>
+<g>
+ <path fill="#808284" stroke="#FFFFFF" stroke-linecap="square" d="M0.185,38.365c0-2.191,1.38-4,3.052-4h48.835
+ c1.687,0,3.053,1.809,3.053,4c0,2.206-1.366,4-3.053,4H3.237C1.564,42.365,0.185,40.571,0.185,38.365z"/>
+</g>
+<path fill="#FFFFFF" d="M20.799,38.388c0,3.323-2.684,6.007-5.995,6.007c-3.322,0-6.005-2.685-6.005-6.007
+ c0-3.31,2.684-5.993,6.005-5.993C18.115,32.395,20.799,35.078,20.799,38.388z"/>
+</svg>
diff --git a/TamTamEdit.activity/icons/volume0.svg b/TamTamEdit.activity/icons/volume0.svg
new file mode 100644
index 0000000..963ebf0
--- /dev/null
+++ b/TamTamEdit.activity/icons/volume0.svg
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="28px" height="28px" viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
+<g>
+ <path fill="#FFFFFF" d="M6.23,13.818c0,1.617-1.312,2.931-2.934,2.931c-1.618,0-2.931-1.313-2.931-2.931
+ c0-1.614,1.312-2.93,2.932-2.93C4.918,10.889,6.23,12.204,6.23,13.818z"/>
+</g>
+</svg>
diff --git a/TamTamEdit.activity/icons/volume1.svg b/TamTamEdit.activity/icons/volume1.svg
new file mode 100644
index 0000000..a23fb00
--- /dev/null
+++ b/TamTamEdit.activity/icons/volume1.svg
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="28px" height="28px" viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
+<path fill="none" stroke="#FFFFFF" stroke-width="2.25" stroke-linecap="round" d="M8.929,7.078c3.546,3.548,3.546,9.297,0,12.846"
+ />
+<g>
+ <path fill="#FFFFFF" d="M6.23,13.818c0,1.617-1.312,2.931-2.934,2.931c-1.618,0-2.931-1.313-2.931-2.931
+ c0-1.614,1.312-2.93,2.932-2.93C4.918,10.889,6.23,12.204,6.23,13.818z"/>
+</g>
+</svg>
diff --git a/TamTamEdit.activity/icons/volume2.svg b/TamTamEdit.activity/icons/volume2.svg
new file mode 100644
index 0000000..7b719e4
--- /dev/null
+++ b/TamTamEdit.activity/icons/volume2.svg
@@ -0,0 +1,14 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="28px" height="28px" viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
+<path fill="none" stroke="#FFFFFF" stroke-width="2.25" stroke-linecap="round" d="M14.574,4.015c5.242,5.236,5.242,13.73,0,18.973"
+ />
+<path fill="none" stroke="#FFFFFF" stroke-width="2.25" stroke-linecap="round" d="M8.929,7.078c3.546,3.548,3.546,9.297,0,12.846"
+ />
+<g>
+ <path fill="#FFFFFF" d="M6.23,13.818c0,1.617-1.312,2.931-2.934,2.931c-1.618,0-2.931-1.313-2.931-2.931
+ c0-1.614,1.312-2.93,2.932-2.93C4.918,10.889,6.23,12.204,6.23,13.818z"/>
+</g>
+</svg>
diff --git a/TamTamEdit.activity/icons/volume3.svg b/TamTamEdit.activity/icons/volume3.svg
new file mode 100644
index 0000000..db92a17
--- /dev/null
+++ b/TamTamEdit.activity/icons/volume3.svg
@@ -0,0 +1,16 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!-- Generator: Adobe Illustrator 13.0.0, SVG Export Plug-In . SVG Version: 6.00 Build 14576) -->
+<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">
+<svg version="1.1" id="Layer_1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" x="0px" y="0px"
+ width="28px" height="28px" viewBox="0 0 28 28" enable-background="new 0 0 28 28" xml:space="preserve">
+<path fill="none" stroke="#FFFFFF" stroke-width="2.25" stroke-linecap="round" d="M14.574,4.015c5.242,5.236,5.242,13.73,0,18.973"
+ />
+<path fill="none" stroke="#FFFFFF" stroke-width="2.25" stroke-linecap="round" d="M8.929,7.078c3.546,3.548,3.546,9.297,0,12.846"
+ />
+<g>
+ <path fill="#FFFFFF" d="M6.23,13.818c0,1.617-1.312,2.931-2.934,2.931c-1.618,0-2.931-1.313-2.931-2.931
+ c0-1.614,1.312-2.93,2.932-2.93C4.918,10.889,6.23,12.204,6.23,13.818z"/>
+</g>
+<path fill="none" stroke="#FFFFFF" stroke-width="2.25" stroke-linecap="round" d="M20.135,0.528
+ c7.166,7.164,7.166,18.777-0.002,25.943"/>
+</svg>
diff --git a/TamTamEdit.activity/setup.py b/TamTamEdit.activity/setup.py
new file mode 100644
index 0000000..5b21f11
--- /dev/null
+++ b/TamTamEdit.activity/setup.py
@@ -0,0 +1,21 @@
+#!/usr/bin/python
+
+# Copyright (C) 2006, Red Hat, Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
+
+from sugar.activity import bundlebuilder
+
+bundlebuilder.start('TamTam')
diff --git a/TamTamJam.activity/Jam/Block.py b/TamTamJam.activity/Jam/Block.py
new file mode 100644
index 0000000..f0789fd
--- /dev/null
+++ b/TamTamJam.activity/Jam/Block.py
@@ -0,0 +1,895 @@
+
+import pygtk
+pygtk.require( '2.0' )
+import gtk
+
+import random
+
+import Config
+
+from Util.NoteDB import PARAMETER
+
+#::: NOTE:
+# All the graphics resources are loaded in Desktop and referenced here as necessary
+#:::
+
+class Block():
+
+ WIDTH = 100
+ HEIGHT = 100
+
+ SNAP = 15
+
+ PAD = 4
+
+ KEYSIZE = 26
+ KEYMASK_START = 309
+
+ def __init__( self, owner, data ):
+ self.owner = owner
+ self.gc = owner.gc
+
+ self.data = {}
+ for key in data.keys():
+ self.data[key] = data[key]
+
+ self.type = Block
+
+ self.width = Block.WIDTH
+ self.height = Block.HEIGHT
+
+ self.parent = None
+ self.canChild = False
+ self.child = None
+ self.canParent = False
+
+ self.canSubstitute = False
+
+ self.parentOffest = 0
+
+ self.dragging = False # is currently dragging
+ self.placed = False # has been placed on the desktop at least once
+
+ self.firstLoc = True
+ self.x = -1
+ self.y = -1
+
+ self.active = False
+
+ def dumpToStream( self, ostream, child = False ):
+ ostream.block_add( ClassToStr[ self.type ], self.active, self.x + self.width//2, self.y + self.height//2, child, self.data )
+ if self.child:
+ self.child.dumpToStream( ostream, True )
+
+ def destroy( self ):
+ if self.child:
+ self.child.destroy()
+ self.child = None
+ self.invalidate_rect( not self.dragging )
+
+ def isPlaced( self ):
+ return self.placed
+
+ def setPlaced( self, placed ):
+ self.placed = placed
+
+ def getLoc( self ):
+ return ( self.x, self.y )
+
+ def setLoc( self, x, y ):
+ if x == self.x and y == self.y: return
+
+ if self.firstLoc:
+ self.firstLoc = False
+ else:
+ self.invalidate_rect( not self.dragging )
+
+ self.x = int(x)
+ self.y = int(y)
+ self.endX = self.x + self.width
+ self.endY = self.y + self.height
+
+ self.invalidate_rect( not self.dragging )
+
+ if self.child:
+ self.child.snapToParentLoc( self.getChildAnchor() )
+
+ def resetLoc( self ):
+ if self.oldParent != None:
+ self.oldParent.addChild( self )
+ return False
+ else:
+ self.setLoc( self.oldLoc[0], self.oldLoc[1] )
+ return True
+
+ def getParentAnchor( self ):
+ return ( self.x + self.parentOffset, self.y )
+
+ def getChildAnchor( self ):
+ return ( self.endX, self.y )
+
+ def snapToParentLoc( self, loc ):
+ self.setLoc( loc[0] - self.parentOffset, loc[1] )
+
+ def substitute( self, block ):
+ pass # override in subclasses
+
+ def testSubstitute( self, block ):
+ if self.child:
+ return self.child.testSubstitute( block )
+
+ def testChild( self, loc ):
+
+ if not self.canParent:
+ return False
+
+ if self.child:
+ return self.child.testChild( loc )
+ elif abs( self.endX - loc[0] ) < Block.SNAP and abs( self.y - loc[1] ) < Block.SNAP:
+ return self
+
+ return False
+
+ def addChild( self, child ):
+ c = self.child
+ if self.child:
+ self.removeChild()
+
+ self.child = child
+ child._addParent( self )
+ child.snapToParentLoc( self.getChildAnchor() )
+
+ if c:
+ child.addChild( c )
+
+ def removeChild( self ):
+ self.child._removeParent()
+ self.child = None
+
+ def _addParent( self, parent ):
+ self.parent = parent
+
+ def _removeParent( self ):
+ self.parent = None
+
+ def getRoot( self ):
+ if self.parent: return self.parent.getRoot()
+ return self
+
+ def isActive( self ):
+ return self.active
+
+ def setActive( self, state ):
+ self.active = state
+ self.invalidate_rect( not self.dragging )
+
+ def getData( self, key ):
+ return self.data[ key ]
+
+ def setData( self, key, value ):
+ self.data[ key ] = value
+
+ def testMouseOver( self, event ):
+ if self.child:
+ ret = self.child.testMouseOver( event )
+ if ret: return ret
+
+ x = event.x - self.x
+ y = event.y - self.y
+
+ if 0 <= x <= self.width and 0 <= y <= self.height:
+ return -1
+
+ return False
+
+ def button_press( self, event ):
+
+ if event.y < self.y or event.y > self.endY:
+ return False
+
+ return self._button_pressB( event )
+
+ def _button_pressB( self, event ):
+
+ if event.x < self.x:
+ return False
+
+ if event.x > self.endX:
+ if self.child:
+ return self.child._button_pressB( event )
+ else:
+ return False
+
+ self.oldParent = self.parent
+ self.oldLoc = ( self.x, self.y )
+ self.dragOffset = ( event.x - self.x, event.y - self.y )
+
+ self._doButtonPress( event )
+
+ return self
+
+ def _doButtonPress( self, event ):
+ pass # override in subclasses
+
+ def button_release( self, event ):
+ if self.dragging:
+ self.dragging = False
+ self.placed = True
+ self.invalidateBranch()
+
+ def motion_notify( self, event ):
+
+ removeFromBlocks = not self.dragging and not self.parent
+
+ if not self.dragging:
+ self.dragging = True
+ self.invalidate_rect()
+
+ if self.parent:
+ self.parent.removeChild()
+
+ self.setLoc( event.x - self.dragOffset[0], event.y - self.dragOffset[1] )
+
+ return removeFromBlocks
+
+ def _beginDrag( self ):
+ self.dragging = True
+ self.dragOffset = ( self.width//2, self.height//2 )
+
+ def invalidateBranch( self, base = True ):
+ self.invalidate_rect( base )
+ if self.child:
+ self.child.invalidateBranch( base )
+
+ def invalidate_rect( self, base = True ):
+ self.owner.invalidate_rect( self.x, self.y, self.width, self.height, base )
+
+ def draw( self, startX, startY, stopX, stopY, pixmap ):
+ if stopY <= self.y or startY >= self.endY:
+ return False
+
+ self._drawB( startX, startY, stopX, stopY, pixmap )
+
+ def _drawB( self, startX, startY, stopX, stopY, pixmap ):
+
+ if stopX <= self.x:
+ return False
+
+ if self.child:
+ self.child._drawB( startX, startY, stopX, stopY, pixmap )
+
+ if startX >= self.endX:
+ return False
+
+ self._doDraw( startX, startY, stopX, stopY, pixmap )
+
+ return True
+
+ def _doDraw( self, startX, startY, stopX, stopY, pixmap ):
+ pass # override in subclasses
+
+ def drawHighlight( self, startX, startY, stopX, stopY, pixmap ):
+ pass # override in subclasses
+
+class Instrument(Block):
+
+ MASK_START = 0
+
+ #::: data format:
+ # { "name": name, "id": instrumentId [, "volume": 0-1, "pan": 0-1, "reverb": 0-1 ] }
+ #:::
+ def __init__( self, owner, data ):
+ Block.__init__( self, owner, data )
+
+ self.type = Instrument
+
+ self.canParent = True
+ self.canSubstitute = True
+
+ if not "volume" in self.data.keys():
+ self.data["volume"] = 0.5
+ if not "pan" in self.data.keys():
+ self.data["pan"] = 0.5
+ if not "reverb" in self.data.keys():
+ self.data["reverb"] = 0
+
+ self.img = [ self.owner.getInstrumentImage( self.data["id"], False ),
+ self.owner.getInstrumentImage( self.data["id"], True ) ]
+
+ def setData( self, key, value ):
+ self.data[ key ] = value
+ if self.active:
+ self.owner.updateInstrument( self )
+ if self.child and self.child.active:
+ self.owner.updateLoop( self.child )
+
+ def substitute( self, block ):
+ self.data["id"] = block.data["id"]
+ self.img = [ self.owner.getInstrumentImage( self.data["id"], False ),
+ self.owner.getInstrumentImage( self.data["id"], True ) ]
+ self.invalidate_rect( True )
+
+ if self.child and self.child.active:
+ self.owner.updateLoop( self.child )
+
+ def testSubstitute( self, block ):
+ ret = Block.testSubstitute( self, block )
+ if ret:
+ return ret
+
+ if block.type == Loop:
+ return False
+
+ if abs( self.x - block.x ) < Block.SNAP and abs( self.y - block.y ) < Block.SNAP:
+ return self
+
+ return False
+
+ def _doButtonPress( self, event ): # we were hit with a button press
+ pass
+
+ def button_release( self, event ):
+ if not self.dragging:
+ self.owner.activateInstrument( self )
+ Block.button_release( self, event )
+
+ def _doDraw( self, startX, startY, stopX, stopY, pixmap ):
+ x = max( startX, self.x )
+ y = max( startY, self.y )
+ endX = min( stopX, self.endX )
+ endY = min( stopY, self.endY )
+ width = endX - x
+ height = endY - y
+
+ # draw border
+ if self.active: self.gc.foreground = self.owner.colors["Border_Active"]
+ else: self.gc.foreground = self.owner.colors["Border_Inactive"]
+ self.gc.set_clip_origin( self.x-Instrument.MASK_START, self.y )
+ pixmap.draw_rectangle( self.gc, True, x, y, width, height )
+
+ # draw block
+ self.gc.set_clip_origin( self.x-Instrument.MASK_START, self.y-self.height )
+ pixmap.draw_drawable( self.gc, self.img[self.active], x-self.x, y-self.y, x, y, width, height )
+
+ def drawHighlight( self, startX, startY, stopX, stopY, pixmap ):
+ self.gc.foreground = self.owner.colors["Border_Highlight"]
+ self.gc.set_clip_origin( self.x-Instrument.MASK_START, self.y )
+ pixmap.draw_rectangle( self.gc, True, self.x, self.y, self.width, self.height )
+
+class Drum(Block):
+
+ MASK_START = 100
+
+ KEYRECT = [ Block.PAD - 1, Block.HEIGHT + 1 - Block.PAD - Block.KEYSIZE, Block.KEYSIZE, Block.KEYSIZE ]
+ KEYRECT += [ KEYRECT[0]+KEYRECT[2], KEYRECT[1]+KEYRECT[3] ]
+
+ #::: data format:
+ # { "name": name, "id": instrumentId [ , "page": pageId, "volume": 0-1, "reverb": 0-1, "beats": 2-12, "regularity": 0-1, "key": shortcut ] }
+ #:::
+ def __init__( self, owner, data ):
+ Block.__init__( self, owner, data )
+
+ self.type = Drum
+
+ self.canSubstitute = True
+
+ if not "page" in self.data.keys():
+ self.data["page"] = -1
+ if not "volume" in self.data.keys():
+ self.data["volume"] = 0.5
+ if not "reverb" in self.data.keys():
+ self.data["reverb"] = 0.0
+ if not "beats" in self.data.keys():
+ self.data["beats"] = random.randint(2, 12)
+ if not "regularity" in self.data.keys():
+ self.data["regularity"] = random.random()
+ if "key" not in self.data.keys():
+ self.data["key"] = None
+
+ self.owner.mapKey( self.data["key"], self )
+ self.keyImage = [ self.owner.getKeyImage( self.data["key"], False ),
+ self.owner.getKeyImage( self.data["key"], True ) ]
+
+
+ self.img = [ self.owner.getInstrumentImage( self.data["id"], False ),
+ self.owner.getInstrumentImage( self.data["id"], True ) ]
+
+ if self.data["page"] == -1:
+ self.regenerate()
+
+ def destroy( self ):
+ self.owner.mapKey( None, self, self.data["key"] )
+ self.owner.noteDB.deletePages( [ self.data["page"] ] )
+ Block.destroy( self )
+
+ def setData( self, key, value ):
+ if key == "beats":
+ self.data["beats"] = value
+ self.owner.noteDB.updatePage( self.data["page"], PARAMETER.PAGE_BEATS, value )
+
+ elif key == "key":
+ oldKey = self.data["key"]
+ self.data["key"] = value
+ self.keyImage = [ self.owner.getKeyImage( value, False ),
+ self.owner.getKeyImage( value, True ) ]
+ self.invalidate_rect()
+ self.owner.mapKey( value, self, oldKey )
+
+ else:
+ self.data[key] = value
+
+ if self.active:
+ self.owner.updateDrum( self )
+
+ def substitute( self, block ):
+ self.data["name"] = block.data["name"]
+ self.data["id"] = block.data["id"]
+
+ self.img = [ self.owner.getInstrumentImage( self.data["id"], False ),
+ self.owner.getInstrumentImage( self.data["id"], True ) ]
+
+ self.invalidate_rect( True )
+
+ if self.active:
+ self.owner.updateDrum()
+
+ def testSubstitute( self, block ):
+ ret = Block.testSubstitute( self, block )
+ if ret:
+ return ret
+
+ if block.type == Loop:
+ return False
+
+ if Config.INSTRUMENTSID[block.data["id"]].kit == None:
+ return False
+
+ if abs( self.x - block.x ) < Block.SNAP and abs( self.y - block.y ) < Block.SNAP:
+ return self
+
+ return False
+
+ def testMouseOver( self, event ):
+ ret = self.testWithinKey( event )
+ if ret: return ret
+
+ x = event.x - self.x
+ y = event.y - self.y
+
+ if 0 <= x <= self.width and 0 <= y <= self.height:
+ return -1
+
+ return False
+
+ def testWithinKey( self, event ):
+ x = event.x - self.x
+ y = event.y - self.y
+
+ if Drum.KEYRECT[0] <= x <= Drum.KEYRECT[4] and Drum.KEYRECT[1] <= y <= Drum.KEYRECT[5]:
+ return self
+
+ return False
+
+ def _doButtonPress( self, event ): # we were hit with a button press
+ pass
+
+ def button_release( self, event ):
+ if not self.dragging:
+ if self.active:
+ self.owner.deactivateDrum( self )
+ else:
+ self.owner.activateDrum( self )
+ Block.button_release( self, event )
+
+ def _doDraw( self, startX, startY, stopX, stopY, pixmap ):
+ x = max( startX, self.x )
+ y = max( startY, self.y )
+ endX = min( stopX, self.endX )
+ endY = min( stopY, self.endY )
+ width = endX - x
+ height = endY - y
+
+ # draw border
+ if self.active: self.gc.foreground = self.owner.colors["Border_Active"]
+ else: self.gc.foreground = self.owner.colors["Border_Inactive"]
+ self.gc.set_clip_origin( self.x-Drum.MASK_START, self.y )
+ pixmap.draw_rectangle( self.gc, True, x, y, width, height )
+
+ # draw block
+ self.gc.set_clip_origin( self.x-Drum.MASK_START, self.y-self.height )
+ pixmap.draw_drawable( self.gc, self.img[self.active], x-self.x, y-self.y, x, y, width, height )
+
+ # draw key
+ self.gc.set_clip_origin( self.x+Drum.KEYRECT[0]-Block.KEYMASK_START, self.y+Drum.KEYRECT[1] )
+ pixmap.draw_drawable( self.gc, self.keyImage[ self.active ], 0, 0, self.x+Drum.KEYRECT[0], self.y+Drum.KEYRECT[1], Block.KEYSIZE, Block.KEYSIZE )
+
+
+ def drawHighlight( self, startX, startY, stopX, stopY, pixmap ):
+ self.gc.foreground = self.owner.colors["Border_Highlight"]
+ self.gc.set_clip_origin( self.x-Drum.MASK_START, self.y )
+ pixmap.draw_rectangle( self.gc, True, self.x, self.y, self.width, self.height )
+
+ def drawKeyHighlight( self, pixmap ):
+ self.gc.foreground = self.owner.colors["Border_Highlight"]
+ self.gc.set_clip_origin( self.x+Drum.KEYRECT[0]-Block.KEYMASK_START, self.y+Drum.KEYRECT[1]-Block.KEYSIZE )
+ pixmap.draw_rectangle( self.gc, True, self.x+Drum.KEYRECT[0], self.y+Drum.KEYRECT[1], Block.KEYSIZE, Block.KEYSIZE )
+
+ def regenerate( self ):
+ self.data["page"] = self.owner.owner._generateDrumLoop( self.data["id"], self.data["beats"], self.data["regularity"], self.data["reverb"], self.data["page"] )
+ if self.active:
+ self.owner.updateDrum( self )
+
+ def clear( self ):
+ self.owner.noteDB.deleteNotesByTrack( [ self.data["page"] ], [ 0 ] )
+
+class Loop(Block):
+
+ HEAD = 13
+ BEAT = 23
+ TAIL = BEAT + Block.PAD
+
+ WIDTH = [ HEAD + BEAT*(n-1) + TAIL for n in range(Config.MAXIMUM_BEATS+1) ]
+
+ BEAT_MUL3 = BEAT*3
+
+ MASK_START = 200
+ MASK_BEAT = MASK_START + HEAD
+ MASK_TAIL = MASK_START + HEAD + BEAT*3
+
+ KEYRECT = [ HEAD + Block.PAD, Block.HEIGHT - 2*Block.PAD - Block.KEYSIZE, Block.KEYSIZE, Block.KEYSIZE ]
+ KEYRECT += [ KEYRECT[0]+KEYRECT[2], KEYRECT[1]+KEYRECT[3] ]
+
+ #::: data format:
+ # { "name": name, "id": pageId [, "beats": 2-12, "regularity": 0-1, "key": shortcut ] }
+ #:::
+ def __init__( self, owner, data ):
+ Block.__init__( self, owner, data )
+
+ self.type = Loop
+
+ self.canParent = True
+ self.canChild = True
+ self.canSubstitute = True
+
+ self.parentOffset = Loop.HEAD - 4
+
+ self.data["beats"] = self.owner.noteDB.getPage(self.data["id"]).beats
+ self.width = Loop.WIDTH[ self.data["beats"] ]
+
+ if "regularity" not in self.data.keys():
+ self.data["regularity"] = random.random()
+ if "key" not in self.data.keys():
+ self.data["key"] = None
+
+ self.keyActive = False
+ self.keyImage = [ self.owner.getKeyImage( self.data["key"], False ),
+ self.owner.getKeyImage( self.data["key"], True ) ]
+
+ self.img = [ self.owner.getLoopImage( self.data["id"], False ),
+ self.owner.getLoopImage( self.data["id"], True ) ]
+
+ def destroy( self ):
+ if self.keyActive:
+ self.owner.mapKey( None, self, self.data["key"] )
+ self.owner.noteDB.deletePages( [ self.data["id"] ] )
+ Block.destroy( self )
+
+ def _updateWidth( self ):
+ self.invalidateBranch( True )
+
+ oldWidth = self.width
+
+ self.width = Loop.WIDTH[self.data["beats"]]
+ self.endX = self.x + self.width
+
+ if self.child:
+ self.child.snapToParentLoc( self.getChildAnchor() )
+
+ if oldWidth < self.width: # or block.child:
+ self.invalidateBranch( True )
+
+ def updateLoop( self ):
+ self.updateImage()
+ self.invalidate_rect()
+
+ if self.active:
+ self.owner.updateLoop( self.getRoot().child )
+
+ def updateImage( self ):
+ self.owner.updateLoopImage( self.data["id"] )
+ self.img = [ self.owner.getLoopImage( self.data["id"], False ),
+ self.owner.getLoopImage( self.data["id"], True ) ]
+
+ def setData( self, key, value ):
+
+ if key == "beats":
+ self.owner.noteDB.updatePage( self.data["id"], PARAMETER.PAGE_BEATS, value )
+ self._updateWidth()
+ self.updateLoop()
+ self.data["beats"] = value
+
+ elif key == "key":
+ oldKey = self.data["key"]
+ self.data["key"] = value
+ self.keyImage = [ self.owner.getKeyImage( value, False ),
+ self.owner.getKeyImage( value, True ) ]
+ self.invalidate_rect()
+ if self.keyActive:
+ self.owner.mapKey( value, self, oldKey )
+
+ else:
+ self.data[key] = value
+
+ def substitute( self, block ):
+ self.invalidateBranch( True )
+
+ oldWidth = self.width
+
+ newid = self.owner.noteDB.duplicatePages( [ block.data["id"] ] )[block.data["id"]]
+ self.data["id"] = newid
+ self.data["beats"] = self.owner.noteDB.getPage(self.data["id"]).beats
+
+ self.updateImage()
+ self._updateWidth()
+
+ if False: # don't substitute children
+ if block.child:
+ c = block.child
+ after = self
+ while c:
+ data = {}
+ for key in c.data.keys():
+ data[key] = c.data[key]
+
+ newid = self.owner.noteDB.duplicatePages( [ data["id"] ] )[data["id"]]
+ self.owner.updateLoopImage( newid )
+ data["id"] = newid
+
+ copy = Loop( self.owner, self.gc, data )
+ after.addChild( copy )
+ after = copy
+ c = c.child
+ elif self.child:
+ self.child.snapToParentLoc( self.getChildAnchor() )
+
+ if self.active:
+ self.owner.updateLoop( self.getRoot().child )
+
+ def testSubstitute( self, block ):
+ ret = Block.testSubstitute( self, block )
+ if ret:
+ return ret
+
+ if block.type != Loop:
+ return False
+
+ if abs( self.x - block.x ) < Block.SNAP and abs( self.y - block.y ) < Block.SNAP:
+ return self
+
+ return False
+
+ def setActive( self, state ):
+ Block.setActive( self, state )
+
+ if self.child:
+ self.child.setActive( state )
+
+ def addChild( self, child ):
+ Block.addChild( self, child )
+ if self.active:
+ child.setActive( True )
+ self.owner.updateLoop( self.getRoot().child )
+
+ def _addParent( self, parent ):
+ Block._addParent( self, parent )
+
+ if self.parent.type == Instrument:
+ self.keyActive = True
+ self.owner.mapKey( self.data["key"], self )
+ else:
+ root = self.getRoot()
+ if root.type == Instrument:
+ root = root.child
+ if root.getData("key") == None:
+ root.setData( "key", self.data["key"] )
+ self.setData( "key", None )
+
+ def _removeParent( self ):
+ if self.active:
+ loopRoot = self.getRoot().child
+ parent = self.parent
+ else:
+ loopRoot = None
+
+ self.keyActive = False
+ self.owner.mapKey( None, self, self.data["key"] )
+
+ Block._removeParent( self )
+
+ if loopRoot == self:
+ self.owner.deactivateLoop( loopRoot )
+ elif loopRoot != None:
+ self.setActive( False )
+ parent.child = None # disconnect us before updating
+ self.owner.updateLoop( loopRoot )
+
+ def testMouseOver( self, event ):
+ ret = self.testWithinKey( event )
+ if ret: return ret
+
+ x = event.x - self.x
+ y = event.y - self.y
+
+ if 0 <= x <= self.width and 0 <= y <= self.height:
+ return -1
+
+ return False
+
+ def testWithinKey( self, event ):
+ if not self.keyActive:
+ return False
+
+ x = event.x - self.x
+ y = event.y - self.y
+
+ if Loop.KEYRECT[0] <= x <= Loop.KEYRECT[4] and Loop.KEYRECT[1] <= y <= Loop.KEYRECT[5]:
+ return self
+
+ return False
+
+ def _doButtonPress( self, event ): # we were hit with a button press
+ pass
+
+ def button_release( self, event ):
+ if not self.dragging:
+ if self.active:
+ root = self.getRoot()
+ self.owner.deactivateLoop( root.child )
+ else:
+ root = self.getRoot()
+ if root.type == Instrument: # must be attached to an instrument
+ self.owner.activateLoop( root.child )
+ Block.button_release( self, event )
+
+ def _doDraw( self, startX, startY, stopX, stopY, pixmap ):
+ y = max( startY, self.y )
+ endY = min( stopY, self.endY )
+ height = endY - y
+
+ loop = self.img[ self.active ]
+ if self.active: self.gc.foreground = self.owner.colors["Border_Active"]
+ else: self.gc.foreground = self.owner.colors["Border_Inactive"]
+
+ #-- draw head -----------------------------------------
+
+ if self.x + Loop.HEAD > startX:
+ x = max( startX, self.x )
+ endX = min( stopX, self.x + Loop.HEAD )
+ width = endX - x
+
+ # draw border
+ self.gc.set_clip_origin( self.x-Loop.MASK_START, self.y )
+ pixmap.draw