zyne/0000755000175000017500000000000012532626002011031 5ustar tiagotiagozyne/synths/0000755000175000017500000000000012417524775012401 5ustar tiagotiagozyne/synths/grougne.zy0000644000175000017500000000314412417524775014435 0ustar tiagotiago{'lfo_params': [[{'state': False, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 1.0, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': True, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 1.0, 0.14534883720930233, 1000.0, 2, 0.069767441860465115, 0.80232558139534882], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': True, 'params': [0.0, 2.4000845833714211, 0.10000000000000001, 0.69999999999999996, 1.0, 0.70930232558139539, 26.929764661463647, 7, 0.0, 0.0050000000000000001], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': (447, 207)}, {'state': False, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 1.0, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 1.0, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}]], 'postproc': {'Comp': [False, -3.0, 2.0, 0.010000000000000002, 0.10000000000000001], 'EQ': [False, 100.0, 500.0, 2000.0, 0.0, 0.0, 0.0, 0.0]}, 'modules': [('FM', 1)], 'server': [0, 4, 1, 0, 0.0], 'ctl_params': [[None, None, None, None, None, None, None, None, None, None]], 'params': [[0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 1.0, 1.0, 0.249, 4.0, 3000.0000000000009, 0.5]]}zyne/synths/wow.zy0000644000175000017500000001151412417524775013603 0ustar tiagotiago{'lfo_params': [[{'state': 0, 'params': [0.0, 2.8546098947909386, 2.9073996518442464, 0.37537903537596912, 2.5158653820654115, 0.24016031018815068, 10.035696602328374, 2, 0.81640126262682422, 0.57193685397718064], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.001, 0.10000000000000001, 0.69999999999999996, 1, 0.10000000000000001, 4, 3, 0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.001, 0.10000000000000001, 0.69999999999999996, 1, 0.10000000000000001, 4, 3, 0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 0, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 1, 'params': [0.0, 3.5243116612125158, 3.6127154116556737, 0.1201491692204102, 1.9675357144236645, 0.81000774701948852, 0.61565388273649335, 1, 0.047132747978246403, 0.50199587361435727], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}], [{'state': 0, 'params': [0.0, 1.5692246486173846, 1.1923480551783401, 0.82327470741927855, 1.4428473435024967, 0.89918443161226902, 45.734398407099199, 6, 0.37638534360138409, 0.71414885613356738], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.001, 0.10000000000000001, 0.69999999999999996, 1, 0.10000000000000001, 4, 3, 0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 1, 'params': [0.0, 1.7349890388368696, 0.59969042152829577, 0.38572104591059064, 2.430762395777188, 0.81633352255911729, 159.73455296891314, 7, 0.50262157426877152, 0.29075896855320893], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 1, 'params': [0.0, 3.4976532398920881, 1.3500876680396319, 0.7045576768781292, 2.0584818311559983, 0.22517104586094205, 0.010000000038433909, 5, 0.72531393591995708, 0.011894172190433538], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 0, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}], [{'state': 0, 'params': [0.0, 1.1990506681271071, 3.1364618532213058, 0.52303250104011501, 1.2515179482245273, 0.83835340055964536, 0.01, 7, 0.29300580522273678, 0.41618508378067076], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 0, 'params': [0.0, 0.7311897201397175, 3.6644340984900601, 0.71380144994898309, 1.0027961834904888, 0.94913844330134256, 3.8521780619835426, 0, 0.58632707406587048, 0.69950418024521266], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 1, 'params': [0.0, 3.5257596074225699, 2.6262951393125049, 0.81015397969034508, 1.1878376493081766, 0.058598973378931385, 0.016942307093852922, 1, 0.38717104825335458, 0.087209334545183426], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 1, 'params': [0.0, 0.83326057305659307, 2.3192726134352877, 0.32017844533807971, 0.11093468844830663, 0.63350775435668683, 0.016030105170689736, 2, 0.32221489668745928, 0.78406588843801872], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 0, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}]], 'postproc': {'Comp': [False, -3.0, 2.0, 0.010000000000000002, 0.10000000000000001], 'EQ': [False, 100.0, 500.0, 2000.0, 0.0, 0.0, 0.0, 0.0]}, 'modules': [('InfiniteRev', 1), ('OTReson', 1), ('CrossFM', 1)], 'server': [0, 2, 1, 0, 0.0], 'ctl_params': [[None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None]], 'params': [[0.0, 2.6095718204199825, 1.4243666914872766, 0.68123828159637168, 3.9112604803420048, 1.3938679401027552, 5, 52, 6263.5143585579544, 0.24090334490354048], [0.0, 1.8688880649603306, 1.6368664843761365, 0.9818999793414358, 3.321196813051992, 1.4027213452575802, 9, 0.79596389768633946, 2329.9261180216004, 0.54838709677419351], [0.0, 3.7103573927699465, 3.0337678346610613, 0.85355116814313114, 0.36169371774897563, 0.8046780834854268, 3.2845683725864885, 2.9725549292175302, 14.743709896613648, 0.81351659949225374]]}zyne/synths/full.zy0000644000175000017500000001153412417524775013733 0ustar tiagotiago{'lfo_params': [[{'state': 0, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 0, 'params': [0.0, 4.6736591005761952, 4.1532018299996309, 0.083782769195669307, 8.6451846159983177, 6.4548490237290239e-08, 5.3189021329749835, 4, 0.14555586610382198, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': True, 'params': [0.0, 0.99978599920625266, 6.2946185589005479, 1.0, 5.5912865298861973, 0.52906976744186052, 1.9977950278471104, 4, 0.31395348837209303, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.0, 0.57797505186740794, 14.800600333635563, 0.83096268412004148, 15.53438370922451, 0.84883720930232553, 1.4980316398738516, 5, 0.054004228138779919, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 0, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}], [{'state': 1, 'params': [0.0, 17.103180643354484, 14.729349142437298, 0.40758663799431905, 20.149977724417944, 0.52973768252524123, 0.16101817978580671, 2, 0.36460240499063989, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 0, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 0, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 0, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 1, 'params': [0.0, 9.4641621409338281, 12.428440627811959, 0.39721717306902232, 8.7060076773015069, 0.49350572912572016, 0.17120015416818452, 5, 0.38030242216972099, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}], [{'state': 1, 'params': [0.0, 0.86006891473855873, 13.71009428156338, 0.52778171101428839, 14.527569114430962, 0.60910126804089959, 0.64859112773968008, 4, 0.60654762001608931, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.001, 0.10000000000000001, 0.69999999999999996, 1, 0.10000000000000001, 4, 3, 0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 0, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.51162790697674421, 0.11892071150027211, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 1, 'params': [0.0, 16.44902429205365, 20.9284602380603, 0.75389089124454611, 10.592387804381872, 0.65020275295979879, 83.436411673047147, 5, 0.4446349682620756, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 1, 'params': [0.0, 20.180099906221535, 22.24633026201305, 0.72048310658653636, 9.4738839551289402, 0.65009158203405315, 0.031018367087120274, 6, 0.58073193532636669, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}]], 'postproc': {'Comp': [False, -3.0, 2.0, 0.010000000000000002, 0.10000000000000001], 'EQ': [False, 100.0, 500.0, 2000.0, 0.57999999999999829, -2.0300000000000011, 0.86999999999999744, -3.769999999999996]}, 'modules': [('FM', 1), ('Ross', 1), ('SquareMod', 1)], 'server': [0, 4, 1, 0, 0.0], 'ctl_params': [[None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None]], 'params': [[0.0, 0.0010445373580950604, 0.54291311708530665, 0.53212812504200158, 8.0000000000000107, 0.88517081257501107, 1.5461847868490464, 20.129032258064512, 3371.0503981391857, 0.84516129032258069], [0.0, 0.001213370887015876, 1.6106722664346824, 0.94642145441706105, 8.0000000000000107, 0.79888161801238544, 0.48243248108779407, 0.087659893324840435, 2302.6162596936779, 0.2290238004340609], [0.0, 0.0015343590019409104, 0.033051677035599497, 0.16672524102577055, 8.0000000000000107, 0.73168687759967177, 10, 7.9431725984676884, 0.62767113851769984, 0.59972887376031081]]}zyne/synths/testmidi.zy0000644000175000017500000000573012417524775014614 0ustar tiagotiago{'lfo_params': [[{'state': False, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}], [{'state': False, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.001, 0.10000000000000001, 0.69999999999999996, 1, 0.10000000000000001, 4, 3, 0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.001, 0.10000000000000001, 0.69999999999999996, 1, 0.10000000000000001, 4, 3, 0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}]], 'postproc': {'Comp': [False, -3.0, 2.0, 0.010000000000000002, 0.10000000000000001], 'EQ': [False, 100.0, 500.0, 2000.0, 0.0, 0.0, 0.0, 0.0]}, 'modules': [('CrossFM', 1), ('Pulsar', 1)], 'server': [0, 2, 1, 0, 0.0], 'ctl_params': [[None, None, None, None, None, None, 74, 71, 81, None], [None, None, None, None, None, None, 16, 80, 19, None]], 'params': [[0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 1.0, 1.7637795209884644, 13.228346824645996, 15.748031616210938, 0.5], [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 1.0, 16, 24, 0.40729183798261304, 0.5]]}zyne/synths/zafroune.zy0000644000175000017500000001072012417524775014616 0ustar tiagotiago{'lfo_params': [[{'state': True, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 1.0, 49.999999999999993, 2, 0.35465116279069769, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': True, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.48255813953488375, 0.085159393182538373, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': True, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 1.0, 100.0, 2, 0.0, 0.53488372093023251], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}], [{'state': False, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': True, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.046511627906976744, 0.031202259649531928, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}], [{'state': False, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.001, 0.10000000000000001, 0.69999999999999996, 1, 0.10000000000000001, 4, 3, 0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.001, 0.10000000000000001, 0.69999999999999996, 1, 0.10000000000000001, 4, 3, 0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.10000000000000001, 4.0000000000000009, 3, 0.0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}]], 'postproc': {'Comp': [False, -3.0, 2.0, 0.010000000000000002, 0.10000000000000001], 'EQ': [False, 100.0, 500.0, 2000.0, 0.0, 0.0, 0.0, 0.0]}, 'modules': [('SAH', 1), ('CrossFM', 1), ('InfiniteRev', 1)], 'server': [0, 4, 1, 0, 0.0], 'ctl_params': [[None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None]], 'params': [[0.0, 10.0, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.54200838709677424, 13.16690322580645, 8.4451612903225808, 0.22064516129032258, 0.5], [0.0, 4.9999999999999991, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 0.64522903225806449, 1.4967741935483871, 0.25806451612903225, 11.612903225806452, 0.5], [0.0, 0.001, 0.10000000000000001, 0.69999999999999996, 0.999999999999998, 1.3548709677419355, 0, 5, 2000.0000000000002, 0.5]]}zyne/synths/rude.zy0000644000175000017500000001201112417524775013717 0ustar tiagotiago{'lfo_params': [[{'state': 1, 'params': [0.0, 1.2698548574861384, 3.2144684479816217, 0.80867793537933363, 3.7792620265189498, 0.047589667648774148, 2.7676432055854923, 6, 0.012003162992288541, 0.40891868802854014], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.001, 0.10000000000000001, 0.69999999999999996, 1, 0.10000000000000001, 4, 3, 0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.001, 0.10000000000000001, 0.69999999999999996, 1, 0.10000000000000001, 4, 3, 0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 0, 'params': [0.0, 1.3564830182281045, 1.1797811185748657, 0.13559049862219708, 0.3081359039195109, 2.6767793412615804e-08, 576.22844216478393, 2, 0.047883224493142862, 0.023949800691000056], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 0, 'params': [0.0, 3.5243116612125158, 3.6127154116556737, 0.1201491692204102, 1.9675357144236645, 0.81000774701948852, 0.61565388273649335, 1, 0.047132747978246403, 0.50199587361435727], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}], [{'state': 0, 'params': [0.0, 1.5692246486173846, 1.1923480551783401, 0.82327470741927855, 1.4428473435024967, 0.89918443161226902, 45.734398407099199, 6, 0.37638534360138409, 0.71414885613356738], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': False, 'params': [0.001, 0.10000000000000001, 0.69999999999999996, 1, 0.10000000000000001, 4, 3, 0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 0, 'params': [0.0, 1.7710423019590182, 0.031244138055184667, 0.058499017947167699, 0.033864943008176315, 6.7837956743365803e-08, 524.12723657185086, 1, 0.12367105316996474, 0.20063977342976014], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 0, 'params': [0.0, 1.2692813820891156, 1.1411651706260941, 0.14804307738551628, 0.48113447001287507, 6.2960293442836466e-25, 352.25254012109156, 1, 0.16819731851937447, 0.28372179475010029], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 0, 'params': [0.0, 0.37815098440792905, 0.17151728390546983, 0.3308945197515209, 1.2276813845145793, 3.3142032677062569e-25, 148.31672342930324, 0, 0.14259651732768797, 0.13895350821625785], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}], [{'state': 1, 'params': [0.0, 0.03201562716383765, 1.2305440084713704, 0.28371460244507896, 0.018825051624709058, 0.62839391201151606, 0.018223811497975393, 4, 0.17128252889489604, 0.03789727028560097], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 1, 'params': [0.0, 1.7577259322664418, 2.7081048390228171, 0.96504205636739115, 0.058109568350811262, 0.75420576658152116, 0.010303871510999863, 2, 0.029792770591045792, 0.37698455633400074], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 0, 'params': [0.0, 3.5257596074225699, 2.6262951393125049, 0.81015397969034508, 1.1878376493081766, 0.058598973378931385, 0.016942307093852922, 1, 0.38717104825335458, 0.087209334545183426], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 0, 'params': [0.0, 0.66636628564106881, 1.6649041893071646, 0.11669026415821314, 0.68800025660439212, 1.164575380396931e-06, 226.57687762586278, 1, 0.20829239089048912, 0.58939694515983199], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}, {'state': 0, 'params': [0.0, 0.63126425959813803, 0.3341587835205303, 0.089493660246590903, 1.5998960722317908, 9.180866341729945e-11, 158.94978891032912, 2, 0.49105049014041735, 0.1827993606053776], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], 'shown': False}]], 'postproc': {'Comp': [True, -12.0, 6.0, 0.010000000000000002, 0.10000000000000001], 'EQ': [False, 100.0, 500.0, 2000.0, 0.0, 0.0, 0.0, 0.0]}, 'modules': [('InfiniteRev', 1), ('OTReson', 1), ('CrossFM', 1)], 'server': [0, 2, 1, 0, 0.0], 'ctl_params': [[None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None], [None, None, None, None, None, None, None, None, None, None]], 'params': [[0.0, 0.6967882319519485, 3.3647665680980459, 0.10324245783346647, 2.0382649200073648, 0.98235872925339007, 20, 86, 8994.152664327612, 0.50322580645161286], [0.0, 2.6530401920901983, 2.1067174603003824, 0.42001990913746229, 3.9823454517741501, 0.91012969950461298, -29, 0.98130562270993804, 4704.0630914711182, 0.18064516129032257], [0.0, 0.40848457744444017, 1.8105096492578723, 0.8486583004474616, 3.3189329035617319, 0.98344079627537595, 1.8324954376089617, 37.115240228970734, 19.752377039463489, 0.69835565961581803]]}zyne/Resources/0000755000175000017500000000000012532667345013022 5ustar tiagotiagozyne/Resources/variables.py0000644000175000017500000001425512417524776015355 0ustar tiagotiagoimport os, sys, unicodedata, codecs from types import UnicodeType reload(sys) sys.setdefaultencoding("utf-8") constants = dict() constants["VERSION"] = "0.1.2" constants["YEAR"] = "2012" constants["PLATFORM"] = sys.platform constants["OSX_BUILD_WITH_JACK_SUPPORT"] = False constants["DEFAULT_ENCODING"] = sys.getdefaultencoding() constants["SYSTEM_ENCODING"] = sys.getfilesystemencoding() if '/Zyne.app' in os.getcwd(): constants["RESOURCES_PATH"] = os.getcwd() currentw = os.getcwd() spindex = currentw.index('/Zyne.app') os.chdir(currentw[:spindex]) else: constants["RESOURCES_PATH"] = os.path.join(os.getcwd(), 'Resources') if not os.path.isdir(constants["RESOURCES_PATH"]) and constants["PLATFORM"] == "win32": constants["RESOURCES_PATH"] = os.path.join(os.getenv("ProgramFiles"), "Zyne", "Resources") constants["DEFAULT_PREFS"] = """ AUDIO_HOST = Portaudio OUTPUT_DRIVER = "" MIDI_INTERFACE = "" SR = 48000 PYO_PRECISION = double FORMAT = wav BITS = 24 POLY = 5 SLIDERPORT = 0.05 AUTO_OPEN = Default CUSTOM_MODULES_PATH = "" EXPORT_PATH = "" LAST_SAVED = "" """ constants["ID"] = {"New": 1000, "Open": 1001, "Save": 1002, "SaveAs": 1003, "Export": 1004, "Quit": 1005, "Prefs": 1006, "MidiLearn": 1007, "Run": 1008, "ResetKeyboard": 1009, "ExportChord": 1010, "Retrig": 1011, "ExportTracks": 1012, "ExportChordTracks": 1013, "UpdateModules": 2000, "CheckoutModules": 2001, "Modules": 1100, "About": 5999, "Tutorial": 6000, "MidiLearnHelp": 6001, "ExportHelp": 6002, "CloseTut": 7000, "CloseHelp": 7001, "CloseLFO": 7002, "DeSelect": 9998, "Select": 9999, "Uniform": 10000, "Triangular": 10001, "Minimum": 10002, "Jitter": 10003, "Duplicate": 10100} constants["VARIABLE_NAMES"] = ["AUDIO_HOST", "OUTPUT_DRIVER", "MIDI_INTERFACE", "SR", "PYO_PRECISION", "FORMAT", "BITS", "POLY", "AUTO_OPEN", "SLIDERPORT", "CUSTOM_MODULES_PATH", "EXPORT_PATH"] constants["VAR_PREF_LABELS"] = {"FORMAT": 'Exported soundfile format', "SR": 'Sampling rate', "AUTO_OPEN": 'Auto open default or last synth', "POLY": 'Keyboard polyphony', "PYO_PRECISION": 'Internal sample precision', "BITS": 'Exported sample type', "CUSTOM_MODULES_PATH": 'User-defined modules location', "SLIDERPORT": "Slider's portamento in seconds", "AUDIO_HOST": "Audio host API", "OUTPUT_DRIVER":'Prefered output driver', "MIDI_INTERFACE": 'Prefered Midi interface', "EXPORT_PATH": 'Prefered path for exported samples'} constants["VAR_CHOICES"] = {"FORMAT": ['wav', 'aif'], "SR": ['44100', '48000', '96000'], "AUTO_OPEN": ['None', 'Default', 'Last Saved'], "BITS": ['16', '24', '32'], "POLY": [str(i) for i in range(1,21)], "PYO_PRECISION": ['single', 'double']} vars = dict() vars["AUDIO_HOST"] = "Portaudio" vars["OUTPUT_DRIVER"] = "" vars["MIDI_INTERFACE"] = "" vars["SR"] = 48000 vars["FORMAT"] = 'wav' vars["BITS"] = 24 vars["POLY"] = 5 vars["SLIDERPORT"] = 0.05 vars["PYO_PRECISION"] = "double" vars["CUSTOM_MODULES_PATH"] = "" vars["EXPORT_PATH"] = "" vars["AUTO_OPEN"] = 'Default' vars["LAST_SAVED"] = "" vars["MIDILEARN"] = False vars["LEARNINGSLIDER"] = None vars["EXTERNAL_MODULES"] = {} vars["MIDIPITCH"] = None vars["MIDIVELOCITY"] = 0.707 vars["NOTEONDUR"] = 1.0 vars["VIRTUAL"] = False vars["MIDI_ACTIVE"] = 0 def ensureNFD(unistr): if constants["PLATFORM"] in ['linux2', 'win32']: encodings = [constants["DEFAULT_ENCODING"], constants["SYSTEM_ENCODING"], 'cp1252', 'iso-8859-1', 'utf-16'] format = 'NFC' else: encodings = [constants["DEFAULT_ENCODING"], constants["SYSTEM_ENCODING"], 'macroman', 'iso-8859-1', 'utf-16'] format = 'NFD' if type(unistr) != UnicodeType: for encoding in encodings: try: unistr = unistr.decode(encoding) break except UnicodeDecodeError: continue except: unistr = "UnableToDecodeString" print "Unicode encoding not in a recognized format..." break return unicodedata.normalize(format, unistr) def toSysEncoding(unistr): try: if constants["PLATFORM"] == "win32": unistr = unistr.encode(constants["SYSTEM_ENCODING"]) else: unistr = unicode(unistr) except: pass return unistr vars["ensureNFD"] = ensureNFD vars["toSysEncoding"] = toSysEncoding def checkForPreferencesFile(): preffile = os.path.join(os.path.expanduser("~"), ".zynerc") if os.path.isfile(preffile): with codecs.open(preffile, "r", encoding="utf-8") as f: lines = f.readlines() pref_rel_version = int(lines[0].split()[3].split(".")[1]) cur_rel_version = int(constants["VERSION"].split(".")[1]) if lines[0].startswith("### Zyne"): if pref_rel_version != cur_rel_version: print "Zyne preferences out-of-date, using default values." lines = constants["DEFAULT_PREFS"].splitlines() else: print "Zyne preferences out-of-date, using default values." lines = constants["DEFAULT_PREFS"].splitlines() prefs = dict() for line in lines[1:]: line = line.strip() if line: sline = line.split("=") prefs[sline[0].strip()] = ensureNFD(sline[1].strip()) for key in prefs.keys(): if key in ["SR", "POLY", "BITS"]: vars[key] = int(prefs[key]) elif key in ["SLIDERPORT"]: vars[key] = float(prefs[key]) elif key == "AUDIO_HOST" and constants["PLATFORM"] == "darwin" and not constants["OSX_BUILD_WITH_JACK_SUPPORT"] and prefs[key] in ["Jack", "Coreaudio"]: vars[key] = ensureNFD("Portaudio") else: vars[key] = prefs[key] checkForPreferencesFile() zyne/Resources/tutorial.py0000644000175000017500000003452412417524776015251 0ustar tiagotiago# encoding: utf-8 ''' ---- Sections ---- 1. Sections 2. Introduction 3. API 4. Basics 5. Reserved variables 6. Stereo output vs keyboard polyphony 7. Module's documentation 8. Examples 9. Generic class to use as a startup ---- Introduction ---- What must be defined to include custom modules in the zyne application ? - In one or more python files: 1 - Classes implementing custom dsp chains. 2 - A dictionary, called `MODULES`, specifying the modules and their properties. - In the preferences panel: 3 - The path to the directory where you saved your python files. Several modules can be defined in a single file. ---- API ---- class BaseSynth(self, config, mode=1) Parameters : config : dict This is the user-defined configuration dictionnary automatically sent by the application to the module in order to properly initialize the module. It must be passed directly from the module's __init__ method to the BaseSynth's __init__ method. mode : int {1, 2, 3}, optional mode=1 (default) means that the pitches from the keyboard are directly converted in Hertz. mode=2 means that the pitches from the keyboard are converted into transposition factor with the Midi key 60 as 1.0, eg. no transposition. mode=3 means that the pitches from the keyboard keep their Midi note value. The keyboard can be transposed in semitones before the midi-to-hertz or the midi-to-transpo conversion. This is automatically done when a slider is defined with "Transposition" as the param_name. Attributes : self.pitch : PyoObject This variable contains frequencies, in Hertz, Midi notes or transposition factors, from the pitches played on keyboard. self.amp : PyoObject This variable contains the ADSR amplitude envelope, normalized between 0 and 1, derived from the velocities played on the keyboard. self.panL, self.panR : PyoObject These variables contains panning values for left and right channels. The user has to multiply his left and right signals with these variables in order to use the "Pan" slider. self.trig : PyoObject This variable contains trigger streams generated by the noteon played on the keyboard. Useful to trig an envelope or a sound at the beginning of a note. self.p1, self.p2, self.p3 : PyoObject User-defined slider's values. self.module_path : string Path of the "custom modules" folder if set in the preferences panel. self.export_path : string Path of the "exported sounds" folder if set in the preferences panel. MODULES = {module_name : { "title" : title_to_be_displayed, "synth" : ref_to_custom_class, "p1" : [param_name, init, min, max, is_integer, is_log], "p2" : [param_name, init, min, max, is_integer, is_log], "p3" : [param_name, init, min, max, is_integer, is_log] }, } All custom module's properties are defined in a dictionary of dictionaries called "MODULES", one dictionary per module. Syntax: module_name : str Reference name of the module, as it will appear in the Modules menu. Value for this key is the dictionary of properties for the module. title_to_be_displayed : str String that will appear at the top of the module panel. ref_to_custom_class : class derived from BaseSynth Reference to the class implementing the dsp chain. param_name : str Label of the slider for the parameter. If "Transposition" is used as the param_name, the slider will be automatically used to transpose the note before the the midi-to-hertz or the midi-to-transpo conversion. The slider's properties (init, min, max, is_int) must be integer. init : int or float Initial value of the slider. min : int or float Minimum value of the slider. max : int or float Maximum value of the slider. is_integer : boolean Set this value to True to create a slider of integers or False to create a slider of floats. is_log : boolean Set this value to True to create a logarithmic slider (`min` must be non-zero) or False to create a linear slider. ---- Basics ---- Each module must be derived from the class "BaseSynth" (you don't need to import specific modules since your file will be executed in the proper environment). The "BaseSynth" class is where are handled "pitch", "amplitude", "polyphony", user-defined attributes (p1, p2, p3) and samples exportation. Initialisation of the class BaseSynth: BaseSynth.__init__(self, config, mode) So your custom class should be defined like this: class MySound(BaseSynth): def __init__(self, config): BaseSynth.__init__(self, config, mode=1) ---- Reserved variables ---- self.pitch : this variable contains frequencies, in Hertz, Midi notes or transposition factors, from the pitches played on keyboard. self.amp : this variable contains the ADSR amplitude envelope, normalized between 0 and 1, derived from the velocities played on the keyboard. self.panL, self.panR : These variables contains panning values for left and right channels. The user has to multiply his left and right signals with these variables in order to use the "Pan" slider. self.trig : this variable contains trigger streams generated by the noteon played on the keyboard. Useful to trig an envelope or a sound at the beginning of the note. self.p1, self.p2, self.p3 : user-defined slider's values. self.out : This variable must be the object that send the sound to the output. Although it is possible, the custom class should not called the `out` method of any object. Every signals must be mixed in the self.out variable, which will then be sent to the post-processing effects and finally to the soundcard. self.module_path : Path of the "custom modules" folder if set in the preferences panel. self.export_path : Path of the "exported sounds" folder if set in the preferences panel. To minimise conflicts between variable's names, all other variables used in the class "BaseSynth" begin with an underscore. If you don't use this syntax in your custom classes, you will avoid to override basic module's objects. ---- Stereo output vs keyboard polyphony ---- The best way to manage the keyboard polyphony without corrupting the stereo output is to process each channel independently and to mix everything at the very end. All reserved variables contains `polyphony` audio streams, that means that every object with one of these variables as argument will contains `polyphony` audio streams. Here is an example: # old-school sample-and-hold synth self.fr = Phasor(freq=self.p1, mul=self.pitch*self.p3, add=self.pitch) self.ctl = Phasor(freq=self.p2) self.realfreq = SampHold(self.fr, self.ctl) # amplitude normalization self.norm_amp = self.amp * 0.1 # left channel with `polyphony` streams self.ampL = self.norm_amp * self.panL self.lfo1 = LFO(self.realfreq, sharp=1, type=3, mul=self.ampL) # right channel with `polyphony` streams self.ampR = self.norm_amp * self.panR self.lfo2 = LFO(self.realfreq*1.012, sharp=1, type=3, mul=self.ampR) # mix all streams in each channel to mono self.lfo1_mono = self.lfo1.mix() self.lfo2_mono = self.lfo2.mix() # take the two mono streams to create a stereo output self.out = Mix([self.lfo1_mono, self.lfo2_mono], voices=2) ---- Module's documentation ---- The module documentation, accessible via the question mark on the interface must be included in the __doc__ string variable of the custom class. The following syntax must be respected: """ Short description of the module. Longer explication about the audio process implemented. Parameters: First param : Description Second param : Description Third param : Description ___________________________________________________________________________________________________ Author : Your Name - year ___________________________________________________________________________________________________ """ ---- Examples ---- Example of a file containing two modules. First, a simple module implementing a chorus of sine waves using semitone transposition and second, a soundfile looper/slicer using transposition factor derived from the keyboard's pitches. class ChoruSyn(BaseSynth): """ Simple chorus of six sine waves. Six sine waves with control on the overall frequency deviation. Parameters: Transposition : Transposition, in semitones, of the pitches played on the keyboard. Deviation speed : Speed of the interpolated random applied on each wave. Deviation range : Amplitude of the interpolated random applied on each wave. _______________________________________________________________________________________ Author : Olivier Bélanger - 2011 _______________________________________________________________________________________ """ def __init__(self, config): BaseSynth.__init__(self, config, mode=1) # First slider is used as semitone transpo, so self.p1 is not defined # self.p2 = "Deviation speed" # self.p3 = "Deviation range" # 6 interpolated randoms self.pitchVar = Randi(min=0.-self.p3, max=self.p3, freq=self.p2*[random.uniform(.95, 1.05) for i in range(6)], add=1) # 6 oscillators (separated to properly handle keyboard polyphony) self.norm_amp = self.amp * .1 self.leftamp = self.norm_amp*self.panL self.rightamp = self.norm_amp*self.panR self.osc1 = Sine(freq=self.pitch*self.pitchVar[0], mul=self.leftamp).mix(1) self.osc2 = Sine(freq=self.pitch*self.pitchVar[1], mul=self.rightamp).mix(1) self.osc3 = Sine(freq=self.pitch*self.pitchVar[2], mul=self.leftamp).mix(1) self.osc4 = Sine(freq=self.pitch*self.pitchVar[3], mul=self.rightamp).mix(1) self.osc5 = Sine(freq=self.pitch*self.pitchVar[4], mul=self.leftamp).mix(1) self.osc6 = Sine(freq=self.pitch*self.pitchVar[5], mul=self.rightamp).mix(1) # stereo mix of all oscillators self.out = Mix([self.osc1, self.osc2, self.osc3, self.osc4, self.osc5, self.osc6], voices=2).out() class SndLooper(BaseSynth): """ Soundfile looper/slicer. This module loads a soundfile in memory and reads it with a slicing algorithm. Each slice takes a new starting point and a new duration. The overall transposition is controled by the pitches played on the keyboard. Midi key 60 (middle C) is the key where there is no transposition. Parameters: Transposition : Transposition, in semitones, of the pitches played on the keyboard. Deviation speed : Speed of the interpolated random applied on the starting point. Deviation range : Amplitude of the interpolated random applied on the starting point. _______________________________________________________________________________________ Author : Olivier Bélanger - 2011 _______________________________________________________________________________________ """ def __init__(self, config): BaseSynth.__init__(self, config, mode=2) self.table = SndTable(SNDS_PATH+"/transparent.aif") self.st = Phasor(.1, mul=self.table.getDur()-.25) self.dur = Choice([.125, .125, .125, .25, .25, .5], freq=1) self.varFreq = self.p2*[random.uniform(.95, 1.05) for i in range(2)] self.pitchVar = Randi(min=0.-self.p3, max=self.p3, freq=self.varFreq, add=1) self.looper1 = Looper(self.table, pitch=self.pitch, start=self.st*self.pitchVar[0], dur=self.dur, interp=4, autosmooth=True, mul=self.amp*self.panL).mix(1) self.looper2 = Looper(self.table, pitch=self.pitch, start=self.st*self.pitchVar[1], dur=self.dur, interp=4, autosmooth=True, mul=self.amp*self.panR).mix(1) self.out = Mix([self.looper1, self.looper2], voices=2) MODULES = { "ChoruSyn": { "title": "- Chorused sines -", "synth": ChoruSyn, "p1": ["Transposition", 0, -36, 36, True, False], "p2": ["Deviation speed", 1, .1, 10, False, False], "p3": ["Deviation range", 0.02, 0.001, .5, False, True] }, "SndLooper": { "title": "- Sound Looper -", "synth": SndLooper, "p1": ["Transposition", 0, -36, 36, True, False], "p2": ["Deviation speed", 1, .1, 10, False, False], "p3": ["Deviation range", 0.02, 0.001, .5, False, True] }, } ---- Generic class to use as a startup ---- class GenericModule(BaseSynth): """ Simple frequency modulation synthesis. With frequency modulation, the timbre of a simple waveform is changed by frequency modulating it with a modulating frequency that is also in the audio range, resulting in a more complex waveform and a different-sounding tone. Parameters: FM Ratio : Ratio between carrier frequency and modulation frequency. FM Index : Represents the number of sidebands on each side of the carrier frequency. Lowpass Cutoff : Cutoff frequency of the lowpass filter. ________________________________________________________________________________________ Author : Olivier Bélanger - 2011 ________________________________________________________________________________________ """ def __init__(self, config): # `mode` handles pitch conversion : 1 for hertz, 2 for transpo, 3 for midi BaseSynth.__init__(self, config, mode=1) self.fm1 = FM(carrier=self.pitch, ratio=self.p1, index=self.p2, mul=self.amp*self.panL).mix(1) self.fm2 = FM(carrier=self.pitch*0.997, ratio=self.p1, index=self.p2, mul=self.amp*self.panR).mix(1) self.filt1 = Biquad(self.fm1, freq=self.p3, q=1, type=0) self.filt2 = Biquad(self.fm2, freq=self.p3, q=1, type=0) self.out = Mix([self.filt1, self.filt2], voices=2) MODULES = { "GenericModule": { "title": "- Generic module -", "synth": GenericModule, "p1": ["Ratio", 0.5, 0, 10, False, False], "p2": ["Index", 5, 0, 20, False, False], "p3": ["LP cutoff", 4000, 100, 15000, False, True] }, } ''' zyne/Resources/zyneiconDoc.icns0000644000175000017500000012433012417524776016171 0ustar tiagotiagoicnsil32^܋^݌^ތ^ߌ^ ť^׏/ypž^{'/ _­# , ̾r_ٯ"3 ҽ_گ%. ɿ_ۯ%3 н_ٴ2 ׻_ݮ1 ѻ_%0 ѿ _'8Կ__` ` `ܸ`ف`ڷ` 篹ĸՃ`̬``靏`ӀՃ`}ίՃ`Ճ`Δ^܋^݌^ތ^ߌ^ Ƨ^ؑ/ypž^|'/ _­# , ̾r_ٯ"3 ҽ_گ%. ɿ_¾ۯ%3 н_ٴ2 ׻_ݮ1 ѻ_%0 ѿ _'8Կ__` ` `ܸ`ځ`ڷ` 谺ĸՃ`ͭ``靏`ӀՃ`}ίՃ`Ճ`Δ^܋^݌^ތ^ߌ^ Ʀ^א/ypž^|'/ _­# , ̾r_ٯ"3 ҽ_گ%. ɿ_¾ۯ%3 н_ٴ2 ׻_ݮ1 ѻ_%0 ѿ _'8Կ__` ` `ܸ`ځ`ڷ` 篹ĸՃ`ͬ``靏`ӀՃ`}ίՃ`Ճ`Δl8mkIIIIIIIIIIIIIIIII48494G4c4q4x444444444444444444444444 :;;;;;;;;;;;;;;;;;;;;;;-ih32ffӿfؿffff}|Ԁ fxfWD~؈fFǁþfBȂt]νf,Jǂ q~f:ENf~6GqxсfˣKHrnfL Fz}fF OĂ |ͼ f٭? Jo fM D {r fK Bx fME{ fS[ ~ f fڱfЄԹfҀf˯f|ՁfۉТfǣŀ千fƃfגfyԗf뽢f 첏{ڊfffyp{fٔfRfcef堠ffffӿfؿffff~Հ f༑xf[E~؈fFǂþfBȂt]νf-Jǂ q~f<ENf6GqxсfͣKHrnfL Fz}fF OĂ |ͼ fۭ? Jo fM D {r fK Bx fME{ fS[  f fܱfЄԹfҀfͯf~Ձf܊Тfɣŀ千fƃfגf{ԗf뽣f {ڊfffyp{fٔfRfcef堠ffffӿfؿffff}Հ f໐xfYE~؈fFǂþfBȂt]νf,Jǂ q~f;ENf6Gqxсf̣KHrnfL Fz}fF OĂ |ͼ fڭ? Jo fM D {r fK Bx fME{ fS[ ~ f f۱fЄԹfҀfͯf~Ձf܊Тfȣŀ千fƃfגfzԗf뽣f {ڊfffyp{fٔfRfcef堠ffh8mk  @ f     )                          {it32CKߵKKK ¼ҲK ˾K ̿K K ƲK˺K ξҬKëKDzKʵK̺ڧKпKKKңLLϯxlYVVYmx߅L}A)!$0?Pckl`2+/.*&;ӠL ņO3Gm ) {F1JL҃A5s@( q1?L8.`܄< Ɓ"W.*ij}wsxӝLV,!3 ȁ D *`ŷ|yx~ҜL]., ǁa |7Pֺ՛L.1\#Յ5 ˁ` Y#͹ݚLv)sᒀ΅<& O ;}ȿdzLYU)څ3сS+S@DzL49$׌I86ك̳LKCʁzÌ7 3 فBbۄշLy,ǂ48ہH)@ނ ݼL/̂Ȍ'LށG +i⾩Mu.-Ђ <2D܁E%M- ˂M,Y G/njMa ʂ ʌJ݁b'6d2NjM`*ɂ!F iIbQM(. ς|, UK ;MN[u˄4"ρ5 ɁJ_MGςτv0&ہC΁PF«M Wς"8AǁK'Ԉ«M3ӂ"E Eׁ C 0ìMKWނ.DžDӁ@@ ۀGìM& ˂&؅G-C >;ìMy7# DžCځEN"<íM@( ƌ@ǁ=AہíM3؀˂ Ì=ȁ=B4íN_X …: F/<ӂTĮN0|)͂TځM# ʂ8ĮN#t)݂ M[ O ق@څĮN%ҁ҂ Å2*I ރaŮN0̂ Ì7(^ށ> ՃŮNuF Ō=)NE ؃ƯNVhقzɌ7!@^ rƯNL2-9"H ߁G$bƯOB#ނ҅6ԁ9B++ƯO@6҅F%߁AK,ƯO? ۂƅ>ҁ=A,ƯOA 5 ǁ7E +ǰOFӂ DŽ ǁ8 ݁HBǰOTp:4t%5w  &^[qȰOmNۄmڃﰊDZO6cDZO(ۤ\kȲO"\FԅȲO*Z7ȲOMej|GɲO=TyɲP3)ʲPfH.ʳP#ȂVjʳP-u7ʳP7C|ʳPC!ʳP^[d˳P;~G˴P&ƊV˴P=`܉<̴PE&Nj̴Q42͵QVDyj͵Q*K,͵Q;)ͶQ6mCÏͶQ&a)ÐͶQ<*ζQR7YeζQW'cR&GζQ8If`7϶Q6Ltg;϶QQ(UT"CηQV.Uh8jϷQ W0@x ѱxJ4lϷQ9##,Doȿm@2#"y0o7‹Q =8>6t Q P<6DS Q0]10 QCCق0Y QwDփ0w Q70w QDA0w Qy0Ldu0w Qm0D0j R R R R R R R T %dabX@KߵKKK ¼ҲK ˾K ̿K K ƲK˺K ξҬKëKDzKʵK̺ڧKпKKKңLLӵ}p_[[`p{߅L߉H+!#0?Pckl`2+..*'>ӠL ˌU6Fl ) {F2LL׋G6r@( q1@L=-`܄< Ɓ"W.*ij}wsxӝL^-!3 ȁ D *bŷ|yx~ҜLg/, ǁa |7Qֺ՛L11\#Յ5 ˁ` Y#͹ݚL~*sᒀ΅<& O ;~ȿdzLaU)څ3сS+S@DzL69$׌I86ك̳LQBʁzÌ7 3 فBbۄշL~+ǂ48ہH)@ނ ݼL0̂Ȍ'LށG +i⾩M{--Ђ <2D܁E%M. ˂M,Y G/njMf ʂ ʌJ݁b'6d3NjMg*ɂ!F iIbQM'. ς|+ UK ;MS[u˄4"ρ5 ɁJ_MGςτv0&ہC΁PF«M ]ς"8AǁK'Ԉ«M4ӂ"E Eׁ C 0ìMSWނ.DžDӁ@@ ۀGìM& ˂&؅G-C >;ìM~7# DžCځEN"<íME( ƌ@ǁ=AہíM4؀˂ Ì=ȁ=B4íNgX …; F/<ӂTĮN4|)͂TځM# ʂ8ĮN$u)݂ M[ O ق@څĮN$ҁ҂ Á2*I ރaŮN0̂ 7(^ށ> ՃŮNyF =)NE ؃ƯN\hقzɌ7!@^ rƯNO2-9"H ߁G$bƯOH#ނ҅6ԁ9B++ƯOD6҅F%߁AK,ƯOD ۂƅ>Ӂ=A,ƯOF 5 ǁ7E +ǰOKӂ DŽ ǁ8 ݁IBǰOXp:4t%5w  '][qȰOqNۄmڃﰊDZO5dDZO'ۤ\kȲO"\FԅȲO+Z7ȲOSek|GɲO=TyɲP6)ʲPlH.ʳP#ȂVjʳP/u7ʳP8C|ʳPF!ʳP][d˳P>~G˴P%ƊV˴PA`܉<̴PI&Nj̴Q32͵Q[Dyj͵Q*K,͵Q=)ͶQ8mCÏͶQ%a)ÐͶQ@*ζQV7YfζQ_'cR&IζQ;If`7϶Q9Ltf<϶QW(UT"EηQ[/Ug8kϷQ [3@x ѰxJ5mϷQ=$"+Coȿm@2#"?}ϷQұ?,,.//.,,aۢзQзQзQзQзQиQѸQ܃”ѹQ; 0[5`GPҀQ |>y0o7‹Q =8>6t Q P<6DS Q0]10 QCCق0Y QwDփ0w Q70w QDA0w Qy0Ldu0w Qm0D0j R R R R R R R T %dabX@KߵKKK ¼ҲK ˾K ̿K K ƲK˺K ξҬKëKDzKʵK̺ڧKпKKKңLLҳ|o]YY^n{߅L݅E+!#0?Pckl`2+..*&=ӠL ɊS5Fl ) {F1KL։E5s@( q1@L<-`܄< Ɓ"W.*ij}wsxӝL[,!3 ȁ D *bŷ|yx~ҜLc/, ǁa |7Qֺ՛L01\#Յ5 ˁ` Y#͹ݚL{*sᒀ΅<& O ;~ȿdzL_U)څ3сS+S@DzL59$׌I86ك̳LOBʁzÌ7 3 فBbۄշL|,ǂ48ہH)@ނ ݼL0̂Ȍ'LށG +i⾩My--Ђ <2D܁E%M. ˂M,Y G/njMd ʂ ʌJ݁b'6d2NjMe*ɂ!F iIbQM'. ς|, UK ;MQ[u˄4"ρ5 ɁJ_MGςτv0&ہC΁PF«M [ς"8AǁK'Ԉ«M4ӂ"E Eׁ C 0ìMQWނ.DžDӁ@@ ۀGìM& ˂&؅G-C >;ìM|7# DžCځEN"<íMD( ƌ@ǁ=AہíM3؀˂ Ì=ȁ=B4íNdX …: F/<ӂTĮN3|)͂TځM# ʂ8ĮN$t)݂ M[ O ق@څĮN$ҁ҂ Á2*I ރaŮN0̂ 7(^ށ> ՃŮNwF =)NE ؃ƯNZhقzɌ7!@^ rƯNN2-9"H ߁G$bƯOF#ނ҅6ԁ9B++ƯOC6҅F%߁AK,ƯOB ۂƅ>ҁ=A,ƯOD 5 ǁ7E +ǰOJӂ DŽ ǁ8 ݁HBǰOVp:4t%5w  &^[qȰOpNۄmڃﰊDZO5cDZO'ۤ\kȲO"\FԅȲO+Z7ȲOQej|GɲO=TyɲP5)ʲPjH.ʳP#ȂVjʳP/u7ʳP8C|ʳPE!ʳP][d˳P=~G˴P%ƊV˴P@`܉<̴PH&Nj̴Q32͵QYDyj͵Q*K,͵Q<)ͶQ8mCÏͶQ&a)ÐͶQ>*ζQT7YfζQ\'cR&HζQ:If`7϶Q8Ltf;϶QU(UT"DηQZ.Ug8kϷQ Z2@x ѱxJ5mϷQ<$"+Coȿm@2#">|ϷQѰ=,,.//.,,aڢзQзQзQзQзQиQѸQ܃”ѹQ; 0[5`GPҀQ |>y0o7‹Q =8>6t Q P<6DS Q0]10 QCCق0Y QwDփ0w Q70w QDA0w Qy0Ldu0w Qm0D0j R R R R R R R T %dabX@t8mk@???K8&  kGFFFFFFFB0'%%+pIJ:zyne/Resources/zyneiconDoc.ico0000644000175000017500000070040312417524776016010 0ustar tiagotiagoJ%600h& ,/(1[200 Mh W} (C `` kHH T@@ (B00 %<  kb s h|PNG  IHDR\rf pHYs+ IDATx]r( 3^]fu$86`$s$׾9)>>//k}sxx_x>+3 !|m (N~ί[\kQ>9^&ʽ 2Nd:Q^ZAsѻ6sa{Tgru'p'?&SoH4z|h+~|v9q l¢՘(C>d^)}a` [H1zCFt.:nCqD&Vr(aPa>k@~:-] -@.@F4kI8 Qk MRE5pGp @AgAPz_A9D|$G`A˸bDs:&*s;W:*{9~ 2&8\+Q5Ntp"=W_5ə޵m;9bE4m()HxIL_rQő%K@>-˦FO.nv=WXjm<뙁G,DKok쟐w1Q'N`5 @_1"w_(zƆMt"AKomԶW#2[Pv$pc1Ff"h8@[1(lVɨ]hk~$e"e iڰV"b"DprLB=T#ue~< XNZ ϕnVmB,jZAPEGw D\N[k8mqi = BN`WGIjKII5ĸ%מB(FfkR\(m JVsOO`Z  JJ32nXײXwή`0*J]ZƭQHu*-/ju9H|28|S*q[]8P.J-@q& .7ǹϭLĹ7mƶ"1E8M`)r:skCC 8>[!>Kd#9p$[Ӣ8ܹYZy=\qfҠ5x; HƚF, EԬ@4J{;N TFP@-99%(6経DI Ai0; ,@sihVBs'Q%Qw%3!N`X'ƒc90r=K$me@(ZSB;kftAB@j{,.]M^RX+lv[m= P9R\YX#U$Df#VԱF$]K D@t>RKvֈLUjm9O_ӿC-¹t +&(wpq _E52vr1cv|m7ھ:mSˁ)XqVq;Z0nziiLjx}CNJ@H!ށDR@WA+jN~N?ߏou?}LMƟ.#w.{|{RjYi$ .EfWѿ :o' -*'Hc 9S%J *;E h"o.TC@)b'( +T@*0#Xq0|P @2yd!+9kAVn )CSCU*ƃ)]? ng5u:۾AaB)R># u 舵ʝT' 㡺71}Ac CCd 'gL)L*E^ې[NcfN_*}+Q(Ћj#4PCjo \`:ǯ-Ln]N2wXZT#w۶}hM)`B^C `Ԡ|YC.ͫkę`0d90BVGCImpZ E۾kc0s UvIQ3^zҋZWw q' @q@>yi+zBm@h //6`6\#FBHh+VS }a?ſb, * CfH[ЏHXGӁQJٟP!X~~pgtq iF@vbM'ZAS;ܐt+ UHsfaլۜc۶7P*iU']9'=C4 #K#"/Vt@Dh/*ˡkk{F%^S3hN7eU(~1PVڎBiIE+1PzMf9Ҁ@2d1< u7랜;^10]uu.UŊ` oى@6~~~tfCaF$!͒/?/c k Au\< s!B]Tz4P05`7X;B XOȱs  恽Dz:Ր j ][b(hEK[FqHNLtwmF{4 h9,"ё暋koNh\ %t n"8^Z`R,{G95askQ5`(`f 8 >Y%Ֆz2`eWsľ``=ƹePrB BZ3[˽{)0p>AD E3[~B!\] "뚈b;iRV4`W(h9, [hԨ%XD:Vyp @"a/y)u֧4Haί@+Bhj櫾þ1sKv~@R` P@ ,XaHynI6Ţ5uР,@k) _}f ,+Q#+RE@h@|X@]1>aXm31[jM,9(G{dϘXڠd1 ,eNz聚͵#{!+jG}Z4@OaP9`(ioiхZ)sD8hw⒢"el)prCG-l- w.^ YƟAc'p]e853aҬTp <).7VIk6_^ YLQ_ZծF’U2bqE\9&#mRs ZB!w/ k idy"ͺm[f)[&/pַF u??rI>z&TdB}nc)!8[SqcN,{p?f'tTT @9Ȅ#jFEsqИlNS+۾Q UV ݾܒh=[{Ե ,@h[9u6jWX 8]89v Y{!؄" `U(jjeH#ꥐ֣FW{E48X&ڦ !x[ {MH{xY7X;_}Zj",tNvg}UnFkjyD@hY<<[ۉ{=*A9RoS wu  Յ#dEa%`ZFߣ44n@Xu3S1mBlg]K!J]h ߲@Z 3k?w*@H Jt @=GP)"D X.9+(Y^hDjugbE5u 0ײ,"@B:VAgȭ6c A+&BQri'VIRk:Ñ ]Mr@5yb1V#u]-$x==/oOw5d$׍OUVh/V-Ns.`Z__B47Gi lzBut=_akme+:=>ʇu=Im+bʭےHmZ{GӪi5&.4Zpm!@0s[oUa~VBAj)GX .łTm{ϙϲ?@04db~&H'-ׂ &C#Ɏ [A y92@^9rKL0| [)/ V.#޾@5|3(ZkFli)۶E35 5h{g8u ???=8{"ƣSJ̬$Oiu}f-WGG ~Qe~h6/]%4nvCH],aF$S`AAU lOe};{Y^YF5dh4JG*BK9TGm,Εt!=jr^V=n:#@Yd&w9692ZU>L{^vuE+P}9^qi@`ϧC' ^k? ix!PC R=FR7-Vk`BQc_{o+Pn ^Gksa.5 XG= u_)CVE>t(W[wO@m!qP)ãzZJ{kK 0ȱ$nNҝY>`}ftbuKn{Hƣ0>=vwy ԡT=ZJ00d##97P 4'!e`G pN`%0r;pBߟh]al` Ӂ VOU j@xv(@"AZe#yq?cs РNOq F-E5ҳŅ3jSSND8te^Fj79a Ah_ur@XB]kџ85}he &Gg\4@Ph9sף;V~wڀOuZHͱh|ZDY^zlkV p`'b*,F4(pJdϜp :ӀY.觤!h 2]VV!="''@jk`Y W)H!ݑ)+"|>0?|jH8T$p!0(+"kF2R w|02wVϰvGstB "F')c y Q%7)9 PJ}`o#LO>,œi/0AE`29ޗ{x xƏseoZ!ZR1' :~s<ytP _`15Oo2]@R Rj9i 7~O ,t(K^'M .RzRG^9cRНxC"щm|3zR/sBIp'@{V 5~}ԑ9(D>H!Ou*v47᧯q.dlq L8ÿ7y89JN.:L$NB_*>iTor?tT0)բ`X_3hʯiRA@ h.;6KKÏ}zا8\4ZyZIrӝ@Ns+>nj{ G %5h4tT1𪨭##~ hğH s򚇜sE5"N89h }9L %x:QDG 0C:q5GpI{| p~TkXA2΢ lͅŝA[9+"IKR /n)q 0;MtMK|pk,>pPwh6x/[5'Wgg`c"@QڑhCA$Wk^6F;sfnyKoNFa)2wX mi.IDAT 2-!|[r\*d(y|NS ݚ47Y":5>hwv\4}ْȍ}5k7XtO"WR*OC9J`Z/Aa#V}sjsI3dcw\ `X4+9[iGG fTH84o;PKN=Y;rH}k#&pc1jh9@R+$ NjAJŦ$GQE',vLNA 1[:XnKS$0W;)&J#e md?SO (ǜo:Rs||Cz80q|P DEEf燳4.<DŽ{i`{2ߗ=r.[k>B_vɺ=iiȥEsCY/fKR1W8j'p??j]g\Vl$X}5TZ6(DZ969uKPތH O~_ѿ|_IJM40FGx溒Yt`647|L0Q;wo4b&Fm*P̶N|-|=q 9ʱ斈):/ܒCs~?ehA~L NwzQ;`h=s~n`ϼ(i=,zH[bIEMnSFv u5ڈ8 nj;h]G4,yQW+ >r/=4~},L#ܗPr: ;]()F\B|Ȑk BYGQVRk];_` CgsN|\>>N|p rxg)N p N|p N|nt+~-_msӵuwio Rkև||q?4MIENDB`(0`xwwwxxxwxxpppppxppxxpxwxpxwwxwwwx????????????????( @wxwrs?tuwsr?GowuOxprwxx?(0 xxxwwwwwxxxwwxwxwxwwxwwwwwww?( wwwwwxwwwwPNG  IHDR\rf pHYs+ IDATxsי'# 4. Hx))Nh[ȎqfxZByO]St@)}>C'(v-6m?OofO?.`ԖgՎ@}Hݲ@@X2q(#N>@QAe="OCbf&>f@}+z6% PR[ }Hl%v (('@ ~I< @oi9RL&#^q͞c1J{I}W&,8 Iq Ba X>:H.e֛pZ+2rGWz2ʬJ){%I;)#I?- p\;bPK%P O)PӡLrGB3LcV\Fϯ76VZid'k^WVz-TT:-r  RW+?a;9E)󨒒<1w&LG)'QGٱmnyu͝kT$O(K hZJ44=g wҰmv7P  IpR4DdֹS Z˽WW;(Vmxܠ( uB2D.l" >+`k+n3LF@.[^*'m+e0hQau0Ճ ޥZ+RQ"P1)( &a$x%B4C/idEQ T+E:X$jnTj)ts+鷒jK,v-Va.L%e<0nAڏ=n]n"SE rJO+Yxj3RfL PF"́ð;P722R3_g>Qbjͨ|S}Hpk~ӷ&`v8#۸ ,ˢ1&PP,˂eY~~P˭K/ooCA2㑕attt 8VcEjhGoo/zO8(Mp:EXhDEQ׀݆J $Rg&,8@E '޹ ;N&5Y0*P$ ۻVDj]F>Wa4)A^y=nZZq}ǎ]]]hni޷g,M2$IH4|M,b~~jz yD+$<>B C83#q{{{fJ:_NzwP/L{$,Ν;uR9DyJlyT3Rcf '*OK y`||SӘF$Al!Q8N<𑢎P2؉y DdG(ET](vԦ1sX-;N|]y,ʩw1L6 ʯ:񸱹@di }|ulHN@ww7:;;M^$!ɘ{R4c;U{iFcQ̠ݻpX0Y}(N@SS̔ 6LlB$pKfGr۹ jsf;y@GA!~ 2'4oԺ_w|("sgϞS=_6-A6tUuC$08  J̠ @s"P<dJe]<_TBlu"{fwΎp0zk!9Nd9%_ (}{q5Si3\NxLv oOnmCSOy1kO~<$jAVcx_qo ,+0DkDYdK"|eO.+(G]lr;[)PdjDR"PDQy5l&6k8LDrkw7n`s5&[[,."Lܫ4*0*.ڱ'@ Ha{[߾q;wÚIW`-گ%T:%mu6(~ZNCۍd*Y z`YVv` ׬?@͆+NSǏ<|7cQLݚ(LB!A+mc`ߪVR-, j5M0 ۅe;9q<}'YIb:*@Af`@v8vlV~,fw@򤜹ڇ@cȧ: &@ wp1\#D"Avyث݄LŜ][|NGQ@D#΂ cJlׯ}S?~ DQ (r\$ ReeНTra;d43%m`W15yٔ&VcH$V}"n.ڱXwHǡɼWWrqʕ"BL;;0::B:RBG`@& 3@E*.$kSr~-MaAQьP,4Vp5λyF|cEB!{6 ݫcX}ZRwqlv^z񥢽wp^C; Y9ti:#K;%@HBkZ&1 m"5/ vS'ʫW^EOOϮ/{UN(8N6FDYGZ`<cxkKƯϯgN(| mYqE3,Y(?˲E4_ <# lAhh4j@)$/m#`~u|wװǎÛo׾&EBR߾O2 j 2ξDqC@<ϛ$I׼c6ֶT"7"Pnl:d]$Yt(+BzesWU;2q_\k|G 8vpb+j9jY__Glc*7{-+%ǁ8D"ù F| #n"׋x( H+ PYH2H-*U_YhhH)kw @!'R0r}r|p׈m(IbXYq{ۋP(dJQL8=o-#~Ã/⢴Օ;J PϙR+<_*)nRK=:O߸5w0U[/CۮHrul'Us"ݞG5%lhܨsQDۃw~ x)w}~yM_NXB9_Q- J=E)aO>TA)fՅpf-q5tӁjeU7QT#O ˙,zL Å <,~r̒Ol<5w:shұSFQvPv;~ }z XaEG3,vL{kAXե^M%kf@=*G0 n7ܾFAy^լ,(hu |e_[Rzu5=:H'S`ff\]64W-խPYA6hX>|cǎn^ ptgWQY&Aߌ+o:[:D$.?SHtIv){,ƖΉ8a={q MӆVcؖ$$u4rT Tccc8Qʫ`(7(؋r=Z 6̮JfD@dDLMOe]Jq6 yFlz<4y?Dh,mV}(‘05$P&]{G;(~~tww ;M?j@a(C;X/~ zЌFQ++=M@gff<*Uc1C1[U  M7-$ow:qt̫ IDATyczjB{W9.QjjZ @y-էh)V†z7$!G`P/c||\6fʪ T)V1LLL`iy ]]]yT3 0TK\l6d*U@+yN%SܹsEh,ĂhRH""J&`1 @jRt5ʖiDn~y_~۔LOMa]gq^Wʮ F aM[ ϛevWfVt*{N<7kaU UiG?Bd%p8s ZGI=|uܿ2D_-V.r.Sx?x E0:;%wT*$ވݽWd2pWH'%I^ P0D=klCRH$b_aԱ>bX}}} V.aff]?(8?VUTKѪR~-3(l%pkKܛ=$ fJwqei 6e٪4F8( +jV>'ed;T"M5mz 4+ 9}goėX<,5Ck.6O5$I3:`H{}k~q;~ 'N6 BUillli6{QT:|(ۖʪfplI5vb>4~FFF088>IԜCF9Y],PpeW"p1 |kEtn KwYނW}^a_Oqp\V9PC :-03 IE-` FGG199_E8oh;.@I@W])B4ׯ˫{ۑ";R)%۹׺vjqrCL-+MM q}Ni4m pzܚ itwwchhSw }53ZY%(_8|8{,lI=gY :=fbi1I\v""ٯ/g_eBzߎ[8N4* ImQj vvvbxx,biy k5}]R-sP1NR{F^'|_ݙmv 3R c88V_;a{5A@Z{a䥦f7Nhx6î*ېMgF.9R iF80>cqry(,EݍC%;P2 ;xz!I,>bm6URyR`zPwur *S $lLlVرzUᰡb]-oRM}睘=Ƣ 8brrR'ENrEBkfO Z>V^gll  Π.0IFMAfUܝ ̢/=v:*.9]uA1`&]hb4.>0vB4N>cWa~~ˑBF ݪ^Ӫci1Dng50Z+cfi,ˢWsMӴ|Rx8L"dimhUiXv n6'aX 6|>C"  l }ǎc,133"6ޜ,`1P# #v&msniߢ(:}Dv^3W}qh m0M2j tpIV>`3e]r c`544qܹ}xo1*y>d@Uj7djaفN͉ji;TfVpS'OڒU $mS-a@LJͲ,>s`d+k{W'0fffDÞ @o*P˥? #Q#|5p4h0ՔjVi 4WS^A>+8+H_N uv+Zdʐnmgbbbh8v rοcAwyZKѨR_}W78y {~x hTAܽ& KY:a鉑T2e8ǀz%FMikrYq50)MS3GwF'h) (#Ȫ,(6;p٭?#krF*鶻݉!KlKB qH%xzNVm ށp˻2pw*YsrXE +`0q+pK+`Y===e#xm0 l5,8aaMMU:R%5|Baۋˮ=8Q۬8l6տ\"w&_!Fv; q͠ =(H[n=˲RUzC$A___mZ`tJ)˲h Wa>i4F|>C9Cb+ax@D)$SIC3 m@ۑ60 ɚ`gOTxqpy8~8ZMvk 6@kk9@ fUÍNE|9eHt9ǰ ýuvv E *\?v@$I2fbUdE]Me'! TIҺ5LQXopT녯'r(LI.#A.P6wcdyG4 CD+:BN58قcH| hoDaթt^0 Ǐi44P`V@ 0Kk$*#fM%clv#݅B1؝f!8y4R-&:)YU Sʩqb  ٕ$I7˲hij6R'XZ^ݾTM#yO]|HLK-TzbZj ͎.PveYCU&V({j.4MW,uŘ.|uE̊Hl% e 9DQqe?q$JgD shhD*2w@n]N%J"%zrzVLưы˦33K{2!:qnېV f)^Ob,`& h5bPDZmף ,e/?-8 N|eZk9A֌ `  ÔBsjPcYpȌF{* Vg3f61hy\#mmCqTpefzY- (H,ªQVI$6jSEle'`2D<7Uҵ}^lo Hi@"n2V"xPБg*\,Uk ˁ(Sݚbzq70<<\r!*0+35HP #_4rI4hS7썋N5AD +kv]`YְSM KJRC@oM:qZ#RHG= P%#ɖZn@s9س\$(f RI2JEW]n:.d eMFZv ώưT1rzu=JĥAafx) PӌN&fbl~seu;{#%"JbueKꚔzsӈCĪ x ]ns5)+T}P_V%W)A suU9Q© U4Kg2;ez i6i^I~;;*ǎj H{ a"sH(0/o`ot_*W^eֈӒ8+* R̢mMRnG0ݾ7Kzlv EJ͠*ɯx"MavvsM zEQD$EC7h&&&ܞ@ZkfUjq8 3,@M& +,?N@2 )(3 H205LaPgvp89$#D"+qW#nD"d:e"yT!1Uۯa Ck vdadU+zp|`OKv]#+ 3Y$6o')7^P3!:$Ip)4Z}@4\33aO<ȿt0wo֏1dekU>0p$6 K) 0;)0 ~NsOg2 |wI NA0Swus9Mm3 P(Tql& ށy57r>*Ê-@pW4ȥ(baM$x+VMt`Ri( 95MhL1_.*þBNBoo/ @ P(dYh˲h p8>*0 46U^軒z<44WC{W' N= zߞ%C& dk𗝗FZ `(}NE%Jig2^ /`zrj 鬸~P @I%x,=\([O=亄Bsg*_YNJO%""dJng$Yf=- I}z!JJ TaQ`WJ8t(N0UxJFPMKo]c(hԼ$>=l_Ob~̎v:<+vu.O8O .VUYkU[ ql%0LXk䱒TBPM5d.Ej600K*L&"W ժsXZ^ d5?5(z QnʫVE1Ň*sxc>IN lvd*ih+Вu'^cVOjވ&"s`mm^K\)'GmAP0 B`0.갶h4>=5P_w 6c'Ji 6a9ff$ EՔ/ b~~ϟ<%Vk-8z" PfE |E<\ZB|c fU5f?Mju(FFz=Ok>CPk PJzG[-D`aaNrf5jaO VcUy|~BoBF+6~FSn[uU~6H[},//IN}jS0CXCʌU ݠg9-><ԮS圀f2b%65׾|4t&ulWu CZk𡎲WVi`p_*p,3g^,Д:8hh7:` `hh/_ED"DLHMM4zzzpy6 ,]68 v, sf (&n3'˲cǢXM?DQDKꌥKlvW&''100`8@Fv^x*a@ 2d,lu6cL Q1::H$"gEq߾VʰHX  .hQ.<~G2 BV"ab q|2b1>sTmzNW fe 31l'@8;7oarrS4](18-:d@qyʱ8߱1KP===`YVFV] CHGNAuWCm\.IDATmm㻲˓ <}-ԩ]nE[:S)"3 zy\z"FGG188ΒiЌs_&67 wĜ󢫫KwE pe3gٱD*!e ]8_ x|'X|qtvtK~`dj(bc}hhh4؃ A4e97;1Ρh724=/X(tTM].)8h U)Hߏn޸˗/l5_r& :,[vkkXKE 뉢[oall ٳ=RN+?ue`fW}X~a"FGGӳ/Qydչu.&X])/z?Q+WvIcnvm?#457i##h 5-'_i!n ```'Oĵc||=&;]Hr'a籺 khxlQȾ8^jX =̽ZyP@46 (%I*+xr(ٳgDpy \ ϟB߉qv9>1qSbhh qOO?0N=c|ᇸy&Ɖ7|N[]݁W({a cffW^Ň## 6Yit" ͧ5\~~!AO~|eB](bZN@YE @ % `f+' *2˲8{T"Es ӉT:_ܐK÷6ZZC;%hIOKͷr?t+@anVpdW>BݯK ,p83}ܳz%Xz###z*B?OZG}0 T9Nt*L}ô]Xy%I{^+,FqEsK[ .nܼQsJI=ۀ0s9Ð$FFF0NW0 şaCƮ^*qyxD͇֦3`hV۴il]0{~!^t*|;˿KCsck){Z&L$2R+}A;ߏxWI/Ma||.\@4|;x뭷 Ήj=6:7-JcfUzE]"o}e}<#aeyW^Aooc؋07܊^Ւv[4yŎnvo|x7_\1_Ik1&QRW˭B{+x|^/^K5 .]]]lCUHeZ& pB!? 2ZYp'(\E_qj 4V}&W ">9{=]`'*ZQCj85g]{wz*g"P@FFF/+Ʈ^˲~z@+9A54g?K0'.|_7[%W?n޸v;wϟEb:N!#P;h;ro=lpuAS_ em} #\pHϝ;'{kyQ@ DrPhm=pn/_G> N|h"$Y\\˗qAvܹs8{,zzzv͙rw՚7P=q bEJ*V}˅= Ӂ/^č[o;<"p%LOM}5By"cޘU~(qTUB=cBOSppՕU/Oo3?9FFF`'Osy( 0.~1nM㥏0$^ ?mGX QY[_*.\ . ٳgwwVYbyCYKI-f _㣫anaOQ[E0.PMiZ_czjT]]]|o`j`ddZ~p}'~/L`fnq Xqzvc``#X"FGG111j B(Ya;A6 ipxo z>/-|'X˛1??/, T: I՞9|p"fff0=5 ۅNB(ۈLJ%ad?7tT?ێƮbrN7Z?v-O~~N;@׉x70@u###531ݻF__߾u9H;}o?h݁0<8UvQQ9-V@ʩ 1~f`|l\fYZj>~ /&ӍG`008kniZC*c8M̌jRk:fZujXD|-{wǵ0066)-QNm`(J6ttSg&vsKSyDW1bK"~~ l6>`YVS#reƇV MSvp^۵,eJ9{X8L08sZbX,%<#, e%[ӉiǑdd =7knL8]Ԏ%$ @7@>8C0D"\#. Uf;v~Fr+T*꿯E=Xx&<EDrc)@IV;V_qEt5D"!|>\.W͛un~t8("OQtveY8Ú/+yGU/V:Ad%T*vS; #JIÆ|vܽ{j VA8 )NYW봠jL& ]]#.|:qr9$0`wOSA,'Z ?l*֪%,`Y>w ~lll v,ˢK)ㄊ 5ly%\yO>Nz=C:z=z}͕(Zݦ"(fVM#HF>g,bok@:>R pB˲ps7|WQիFHd'7o]{˞m@ш#:WrW=^ <3?k4!"`rZ"8A$=WkV%TEF:$J8qHnmPܺu *6ʊA@@r9iro<# I ^0 l6f^L"gԓĕrA@ur$A)9ARrA J A$q-Q r9^oW|: ڧ/?~> iAslll _Kf3F JuVC+0Nt F$9FFFFFFFFFFFFFFEFFFFFFFFFFFFFFEEF 1FFFFFFFFFFFFFFEFEFEFEFEFFFFFFF1$>FFFFFFFEFEFEFEEFEEFEEEFEFFFFF*$>11FFFFFFEEFEEEEEFEEEFEEEFFFFFFFFFFFFFFFFFFEEEEFEEEEEEEEEEEEEFFFFFFFFFFFFFFEEEEEEEEEEE@FEEEEEEEEFEE@.$ " $->EEEEEEEEEEEEEEEEEEEEEEE6 $9EFFFFE<*"1EEEEEEEEEE@EE@E@E@E> &EFFFFFFFFFFF' >@E@E@E@E@EEEEEEE9@FFFFFFFFFFFFF>E"6EEEEEEEE>E@E@E@9 EFFFFFFFFFFFFFFFFF'0E@EE@E@>E@EE@?FFFFFFF>>FFFFFFFEFFF$9E@EE@E<EE@E@ @FFFFFFFFFFFFFFFF>@E@E-1FFFF>FFFFFFFFFFFF$EE@C@>E@E@ F>FFFFFFFFFFFFFFFFFFFF>F$>@E@@>E@@10F6FFFEFFFFFFFF@FFFFFFF>F@'E@E@>@E@"EF@FFFFFFE>FFFE@E FFFFFFFFFFFFFF@9FFFEFFFFFF'@@@@=@@>$FFFF09@E>=@@<'F$'F1FF>>F@FE FFFF9*@@?=@C9*F''F-FFEEF@FEFF EF<'@@@<@@:*F$'F*FFEEF>FEFF EF6-@@>>@@?"F$$F-FFFFF>FFEF @F06@@><@@@!F"$F0FFE@F@FFFF EF$>>@><@>@!E"$F0FFFFF@FE FF EF">?>><>@>1-$$F-FF@@F@FEEF E:'?>>><>>>>$$F-FF>>>><<>>>>0$F-FF>>F@FFEF 6&>>>><<>>>>="F-FFFFF@FFFF <>>>><<><><><&F-FFFFF@FFEF 6===<=<<><>===6@-FFFFF> FFFE0<<<9964;><<====9"FFE@F>FF >*111-*''0;<<=<=<==FF-''$" -4><<=<=<===6!!-<@F> 1"69><99667;9><<<<=<<<<<=<<0$! "0<<<9@FFE@?<9<<<<<<<<<<<<<<<<<<<<<<<<9>FEE><9<<<<<<<<<<<<<<<<<<<<<<<<9>E@><:<<<<<<<<<<<<<<<<<<<<:<:<6<@@><<<<<<<<<<<<<<<<<:<:<<<<91<@<9<9<9<9<9<9<9<9<9<<:;:9<90<<<<<9<9<9<9<9<9<9<:<;;<;;90<<9<<<><<<><<<><><<<<<<<<<<>????????????????( @!!!$$$)))---000DDDVVVXXX^^^aaaeeeiiiklkllkmmmxxx}}}777777777777777777777767777777777777777777777777777777777777777777777777777.$17777777777777777777&&17777777777777777777777777777777777777777.$$-777777776777777$.77771$!777777666663377777777.1666666663777767777777$1666366677777777777673366636$1777777777776776"363637177777777777767$36330(76777777717767773(333!6767677777-7767677633777177 7$7331777777 773116 77777 77311.& 77777 7.(3111 77177 711111$ 77.77 7111111 77777 71100000.77777 7-..-*0000-7677".0.00.(37-(($"&..0..0.0.-(.117730...0..0......../..7501................-3/3...............-(0...............--?(0@222IIIMMMQQQYYY^^^nnnvvvyyx}}}00000000000000000000000#',000000/0000000#000000/////00,))000////////0/$!!$///////0///!/00/#,///,/,,/000000/)/,//,000//0/0/0/,,/!,0/000000/0,!,,/+0//000,0,0/!,,,!/0//00/)0/0/$),)$#/,  ,#))$#00 )$,,!#0/   #)))#/,   +))'#// $))))!00 )'&)))!0/''''' $,))&'))')''')''',/''''''('''''$)''''''''''''#'?( ^^^bbbeeeiiilll############# "###"""#"#""""##"""###########""""###""" #  # "  " PNG  IHDR\rf pHYs+ IDATxYp\gv&ݼ7=LA S%QT%jWUj5Rǎqt?tLt;#h*\dQEN"Ē["..yODJ y@1SL1SL1SL1SL1SL1{>ک OO5 L/^~' yALgOEARA<PMS/<<A0 B}}}1Ž ) q4Mi9 (<ćJMP4MEkA:::X*sst: @QVpARq[ga`00ypp<#GR1 qRt:xZuyT*W_Wr(*+H@ J!J8hڧi}w(?ϻ~@"'@d2t:-eAT*!A*/d2O~VL؆'R3Z &%ɟ[Ç)w{@&+]ZGE R!Xt (ʢiPl{9'>i+e (K7Ob})F#~\s9LQ~ʲ,"G]]] Slt:->HE`..(< J:.NE_SAQXE:,=3 +<1Dۅ @Ɇ'EA=s+Zw XӡPBۍ|@ 2yY6,>xÇ?W`,^̍O6g>eתz2a?p28{(<|0q; "P2OZ-VJU2C9 @@RAhhih:TUUlZmp:0mqo .  \aO‰L\veJV/uuu p^,h4D"Uy'߇',J$@Ti0Lz*t:QVV ۍ2l6XVL&t:hڵ +˴@ +W%RU׼F:UUUHX]]fp-:ކZF kÒ!!YSوA _? Hàuh4Hz~NŞZ@*Z(ܻ1WR(Lf*+Q__nf9k U00͢/K-x7fall ###"cxpFYy9ZZq |tmlJzb{N<JgM4$D pDbmVO,&'']]]b5,W7؏1d +EcPQ]eŁڊz8N 0 #* ," bnnMMM0 EZEV ‰'Ű ׋a `jj n`_?~O?G{[8fL\9qy!ұߤn~AqFL/^\dIIM  > ?T&8Doo/F?w >86[҂fpfh4ad2Ez( N#LI年Z8n 8t~?022~<~^/~?PS[֖t\.WS]b1!J!J!Lb}ܷlL j Èad BJɿk-Hċr%TB.WG1;?1 X_%׾fY;K0C.$ɼ' ::hiivQD#,//crrzԄQ^^aMD6&˲1q$ r{jhٌ>葉39RF 8h4kdVN{ 2 eH$q\Ǐ 9s)1އx[WWW_}o&e Uh4p8طot:݆e2X, ,..i,//`0h4"N# bTUUh?о8" yG2D,C"@2D*˲Om|)'L>`dו `bfd^p%NEL,SFt5V~  aG2ɺRi5L&ӆ!99DAOgt`H:vfK _Aw7^{5?Zyf}͋ɦmHƗg C!@Gh\ ^ A@4p] ePiLn}ܻy^$ $ D"#ab1t0 lmź$N.d2jf}X,&\\X/ ٳhmiVE*z8ܳl<\|M" =sk@ }MPhڿw|wƃ{}N#! =EĈz}AX;xЪDΖiN˙ g,Ccc#۷oP^/@cCKCtg9sg$2AA*zjS!D)EQHREۡ!#9 Ͽ(GEy9WV07?P(L|-H$1Ww# kDY4f{F=GAtAȖG"5YEyy9Μ9Fǃ{arrXX\D,EUU,--dNR58C:M"z sccc555-(}ē Wp#a |r00L0z+! cbbmmm0 "$dۘT5a%bOׇcrr_^/r 35Hy) |#(~/pǣo6Ο?X,?8%f8C0ģGP]] TbШa0FeeKń V7ގK.˘oN?ˉT*T@ @fs3)@eY. L"L"Nj:1==MpJ`~\rÏV8q|Ƒ#G`4144TO&bBG$E4ͪf9Dp8'`Q^/VUR1 00466>@KK ===駘+_{/9h4@R /'O@JJA@(T;I,g_ Z;wϟGkkxґNI(EPVfbCF2G*"%E{>N^e0Z Á^z 6 _~%^ZO9ÒlY-(U 2=\@\W$JbzvƯ~3 ͡:Ο?L>׷تBt:q1lHL> B1E:})" IQgg'f3F#^Q2 9Qp\n{>PK3<v_Y\~ˏhooᅬgϢYo!F8F<[ǟ* ڨ4 ɵT*`0Ղdc,{*,>* PYYxw fff_YG}pɢ3 ـ Y(ChDBf!)(.3Z(~Ƶˣ ?ġCnLXZ\B([XlgI|mĂ cqq+++84`*^ӈF㲮kv鰲`0C<ŋ7Re|B.Ci(@j!H=@e /xxFښ7eFN|/EQH$F7VVtA@FM&քK:|tɭ'.t<<<]T*oۛ"VsiiLHsf@..4bqe  qttt%HhBrط$z|:!6g$B$r i}<2xnܸJ/`0Ⱦf>=b!!\ ^E]w Jo>Yj;:.`w^q]9wGyE4E(FRppaY$F[ 5Bd OmWh4ٳp8>T2\D) J@]f%2n߻K{Z7-4ast|aDgQ]]-TM%dDΜ-L&;L7fzt(¹svD#SV<v߾_~ii.dl/[@VeeeyZinÑS\Rr*Q;zK|"ҙXZZ'O/t9־ZX,>:&F T*D=Ai\ LV+ )K@dH`hp6 Uk6hV<0g07jX blNbK߿6YW Vx3Ē9hER*p8My\2e_Fwp̙M/w։@n%{+g#Q$q*H) uZq*1; bMŊ.].T*TTTȞh$1ȧSH0 ~:466s/ *4>O fsY6n$f"<"R% !DcQ*j d!~? _˗=~wvE- |y&&G+~NfL`0`Zv}@nמ""HQa@{q Ɉ [,^ i~嗡jWWxWnP`l팁p=)%smbz3O,bj6Q:{ )ZHdf,d S͖D"DQv?~=7t̙3۾ᛤUǃ΁j[o͵ʮrLZZ+?қ(V4EX g,'/"x}\|DNMԥ0y5u:ZKT);4U1<WGQF#3❹,HB4M# biiIUe5<JV:u sssx1?-DV];#A‘#Gpi, ˅ٜ.e420<O#h+++9U(fCYYن&J)! aG1FbFYkYRa"[`ZesR|eii驢.ipILg IDAT <φUBB`W^ŏ֖tvv0 yTU.F6T>/#ld4MOs9u${}`2r Jf 羥gW[[#G`hhpE8!p- (VpQa1>NtɵAB'-7υ7 j+rrv8Bl C|s{" )Ze0ptuuall 9^~eX,mfz 6;;wn#BsK3=ZqEkG@8|'* VaSӁdj-6K/kFZ2}vg $ilR[[cǎƍ d(!(WigΜACCt:RZmޓ"ŸҩkMӛZ NA`4euBp8 /zLh4;F騭|&OMErd( yX$N߈:r>y^$!sq56 gϞE__>|xǕ(;>͢]]]OP)!A\B lbFnP,n\.ˮ[ ,hTL5 (z0VZgXWWbyyn[l* ==_ajr v ̝#^/زZl-LLL|$;t3?f\H4w )Jp8kyKV (&&&#\pAj8ޱqV8с#GF!B.! m* @&iCZx/HR)L @BVVV*e_ǃ cpp6MVW 7~RE!abl/.+)Lj ŞT hTvTRjp$ lV-ʥkzf*H %L&eod(K.kcV1>>i%FX^YA8dBMMMMK\_ v**wsoxG,n&UJ7i,!"Bף jLtM%'ϦfDF VH^qH-9;Tcʭi:k/=^1 tT`-L! LpLB%>&Ӯ*]r'$*1i)pZͳ(= b fvx޼jžgbs"(zܚlxH<lj1eI-@͡j FDDV@ qgnƤ/iC6|TESՉ+f{p8`61??h4*ʋmEp%X h*ˌF/L0*%VVVd@-UmC0䅵Uvמ̹ H(P*GVf3|>ذYȬIM\^]j0ZlN+x^H$BK'GF,˽ TSyTZ$Rhjn/ȱ[D8@PHVfAA 6~4-6'sb$ X=R $@6iPtDa ,f,X&oQȕj"G:(JV찥 I}+FFl6TjlӃ(ŌF1 PL|#yB#% ˲by Ver?Ƚ$J !L >!W,9|m@Φpݲ6D(<*}zNT*ȭxT015 h4bWacchF,D #~ZuZ[vuuKKKOX]]4jjjeǑL&P~XL@* i"L&$[#+QVC+NlD L: ѣG(++Ù3gت|PO@]l*"udz~CS} >c,C$n||߄^G0C8hӧ/ne8wxG0cj5t}]F >rhPpDEap6V)8*y=`m `|jO!i.x qD(Çqu;w/^K/TԗfX[ohhSSSs=zٳشЄ^G}}U^^{ΝC<QSS.ŪWVV>ٳgAQ&''Ӄ!R)tvv+pZ|xw`0099[nf?ϲ8b^?s"IsXmZ- w8 CEPYY)F(bA(¹s^ƢiV---b8܌z9rhjjnV|F]vl6p ˅AMMMlEZ}={UUUlX,x!<jѣGpq8 ɤ%255z?+P`ٴpH /RAW4:::6Iɵ XV۷6ܼbxi[f*H|rD099 ::KI@˅v5`imߘ `C?t:L&!dK8_g#l 7`4Up{&&&Q]]]H$055%YGww78]ciu(sFqm 6$عU111P__ q) kh MM| ׋ej,Eܻwǎ+XRm'1332l] E`00L%V %ny+++VEWW׶,Yf>J6v{BQkvGbbbBGȬ X-h`ZQQQպ+H'1K t:H `7 ^SSS8ss \ap)t߸DZ ٧A:F0.0 |_GYYY@/eNӴȲl@ш:mlD4t^|>#rVPcC*073 ׋Y@/XST0 )iw-HhQ*x,eeeӋѸ.-D"9LOOĉ0&&&DR* {fJ`f<y9 SVxA^ϣ'Nnm@vvta/>P^^^pT*1`ȆRT%idDmm X ~x\V(G8+ٳϝ˿'4 @J`m-mYv>ܺuKdy}JT*% gQ:cvj'3/..͛X\\DGG_luӣ; ܹcǎ"nyH*iV5# w48޽ pi>קDj<}7KLLL૯B]]ߟtmmmA°#fFGGa rn~"4A@*4BҲZsk Y`jj 8}tmY㻅Oza<pXhnŁCxxn޼f455e)^GMM͖'K]`i6P0'2|)膜000[ni[2aW%/{t:c㘙Fww7N}VizRD9T*9ܼyo&nwIBb߿9TUUŋϥR.@GGuv}hhh( TgYdϕԵTQ!6bx~~%owܹ^F`0o}[x}m+Y]`G;c;q,|я~r߿Ku:jkk6ےBaѣX\\#իj(,iL&hn|u:F,+鉨`ZL&s'|^ x饗x_~##6>3nX,\-s~@4ǕfEQ0L(w#{H$qi8N^d1Z456ͷ&/t:\fen'PDdޠfǒa LÇ/0==w߅ٱ^ TpɓOVUUUؿG,N# lmo7XJ2V'''q5twr疆<;xZ?}.nݼ ^??hbf ~~ T*f3`KVC4&3<8OSܸqՅ>@Y d,FIo|6|gN#LՏѣG8{,GIHFZRʣ;tJF˲7n L^+hhik#a_|A@}]R^L&š#W2cX Ɉp\%%D)B [ɕO7|ַv4W`dc 0ł] `& 6M,2*)1(J\r@%twwO?[o{mmmR`ޠ*9}H/0?6 8ql6ۮ_Fro<f+)gAȺUJ >.]ax<|8xevnkkCeeeIja0JoEJݍ_}6l6o|Wvp(*=x;w N(+n#0 )*"efW 7n'? zzzv{[s;[-6AV[m?壣ݨ[B pm|GDMM .\~fyD)[3J MxEhjx X ǏՈ;w }}}믿+9ԠRG'()n|/{.xG2ĉ'PYYF8sssu.]o^{5uA4G`1(|Ut?F4E$믿SB/;'z*.]q477… xQSS7MEQhߌ? >կׇ.n2 M@j C я~k׮󡣣7PV廨sW?c|2;;8xOًFUp>B8Foo/>#z*ÿυAxM჋::_}^_Ͽ{񍷿sΡ 먮F]]PSSպm݇pG<-YEX?99)LOOcaa`,ǡ{Bh4 YPҀ/G߀" iB!8FF1;; n]^JTTTvrnfbh4B Tl6ҩ40~?VVV0??y,,, X?~-pq?YKHPl{015a<}fgf#FbAyY<n7t:a`0j4MOga,H @(!uw͇6$(6ɶڪRwҮv~J^mH(&;"PHq3 E[B# 6csޏsJs ɋ1{P4M 0 rdi~?O>eee%8+++c&N#G1Μ!ޙ4 ao̮/[D 'Z9nd4ˋ!$~yEQp\x^j~4MMR]]MUUUIy o|\.G&a}}t:͓'OH$`clv98R_Olj7O~1f[!@e nE~ x(.?6:$/u'`> Zlww3~Y/\g 5U/j(Soptۖ~8Ds~U,D+A " k'LPh\UU%a&@055E,#L|>ڸt| x4ijjO>eci#q\\xӧOq(蠲$X˗/HW2~z{{p333 I| NwW_̱c8{xm{0 ;"IMM |ݦi*. g}F6%NFb1zzz4WF\~^/tuuUrf==zd4!P|tww<;|6믿&͖J["žpݸgu0#L*mmmQrGaff)VWW~:ף*sssENx<۷.N" ~fΜ9C4ƍR)^i>}˗/L&sh2#5766$[n1::G}TrNOY^^&O  AiWu D n޼h|@WW;iHq2 kkk<|t]k5۶w}  AE! y;&hF[[TjZ[[xD0H$B `mmv{?JApy,mmmB!?p;Fe'[H^WQZS 䠿x sb7UFFEYYYtttvvveefQQQCCBEEDbca%iii444yVWT<=:SSSqqqCCCGGGqqp3iii444yĿqrn564NNNuuv999LLKIiii444yqrn998___MMMPPOXiii444y>??>]]\Xiii444yOPMCCCyyy787Xhhh444y??>pppRRR__^`hhh444ysup454eeeEEDahhh444yKMGOON777ahhh444ySTP\\\>>>ahhh444yNOJVVV@@@ahhh444yKLHccc===ahhh444yhidEEEEEEahhh444y|~y>>>CCCaggg444y@@?xxxRRRaggg333y@A>KKKaggg333yjkgZZZ===aggg333y>>=|||XXXaggg333yKMIxxxIIIaggg333yHHHFFFaggg333yIJFJJJaggg333y999VVVaggg333yZ[XHHHaggg333yAB@{{{fffaggg333y}yVVV<<>>abbb111yCDA777  WWWuuuabbb111y))* ~~~ IIIabbb111y_`\$$$uuummm888aaaa111yBCA 222zzzbaaa111y443"""VVVdaaa111y--- nnnOOOfaaa111yz|w..-}}} }}} xxx@@@haaa111ytwo898yyyvvvzzzDDDl```111y;;:sss ]]]HHHq```111y{>>= ^^^GGGw```111y;<8eef ==|||yyyxxx{{{&```000yXYT231OOO~~~''&@@?̂___000y^aY221SRSvvv<<___000yUVQ442AAAnnnzzzjjjZZZ787?@?llk4___000ybc^JKHCDBGHGSSS```oooyyy{{{HHH""!%&%+++332ABASTRvvsχ)___000yvxseh`_a\^`[ac]npk~ҋ___000yخ.___000y֋___000yՇ___000y{___000yR___000y___000yn___000ys[___000y^^^000y^^^^000yұ^^^000yֺӢ^^^000yֻ^^^000yR^^^000y^^^000yk ^^^000yҁ^^^000yқ___555sӭ?????????????????????????????????????????????????????????????????????????????????????????????(` ___%%%III^___aaahhhmmmnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnnmmmhhhfffccc{8881ggg___%;;;Eaaa%qqq?bbb%sss6hhh%xxx3lll%~~~3kkk%~~~3kkk%DDDDDDDDDDDDNNN^^^~~~3kkk%;;;LLL~~~3kkk%bbbqqqLLL}}}3kkk%DDDLLL|||3kkk%OOO:::sss{{{3kkk%\\\~~~```___FFF|||6kkk%AAABBBSSS(((@kkk%IIIQQQBBBYYYDDDOkkk%Ykkk%Ykkk%Ykkk%Yjjj%Yjjj%Zjjj%ggePQNZZYqqq{{{[[[RRQddc{{z{jjj%VVT\\[pppVVVjjj%|IIHYYYddd jjj%EFClllVVViii jjj%UVSmlmEFE jjj%IIHdddtut jjj%YYXnnn jjj%z{vfffVVV jjj%|}yfffNNN jjj%RRRccc iii%MMLqqq iii%KKIkkk iii%kliSSS iii%\\\ooo iii%UVS``` iii%kkkddd hhh%UVS]]] hhh%YYYzzz hhh%supHHH hhh%BC@vvv ggg%YYYqqq ggg%NNN ggg%_`\VVV ggg%IJHqqq fff%GHF fff%OONPPP___ ***777OOO \\\ttt \\\ fff%ÿWXW999@@@ttt qqq$$$@@@ded%%%YYY fff%]]\>?>DDDmmm %%%TTT```###RRR fff%\\[GGGUUUccc'''OOOccc'''RRR fff%UUU@@@SSSmmm+++HHHbbb000ggg eee%LLK<<<@@@^^^ ***FFFppp eee%FGE@@@DDDeee222OOObbb eee%LNKAAA:::ooo222FFFQQQiii eee%ikf222LLLnnn 444EEEbbbQQQ eee%444AAAxxx111\\\UUUPPP eee%MMM555999|||(((VVVccc&&& ddd%IJF;;;HHHttt ppp'''OOO```ddd ddd%@@@DDDppp ,,,IIIfff)))JJJ ddd%MMM@@@888hhh%%%***DDD___ ddd%efb???<=>>rrr$$$888kkkRRR ccc%IIH!!!:::iiittt333EEEfff ccc%!!!///rrrttt???NNN___UVU ccc%WWU:::bbb888WWW```KKK bbb%9:7 PPPsss---;;;ccc### bbb%//.<<```%M```%`___%ҩ___%:___%M___%___%G___%ӻccc'''˶̶ӶٶڶٶڶٶڶٶڶٶڶٶڶٶڶٶڶٶڶٶڶٶڶٶڶٶڶٶڶٶڶٶڶٶڶٶڶٶڶٶڶٶٶٶضֶնֶԬ??(H `T\\\ jjj$rrr...................................................|||'tttYYY5>>>8\\\ 7.___ 7'fff 7&fff 7&fff 7888eeekkkppplll&eee 7vvvlll&eee 7PPPiii&eee 7jjjBBBmmm'eee 7iiidddvvv___/eee 7xxxqqq^^^FFF```[[[|||>eee 7Deee 7Deee 7Deee 7Deee 7wwukkjmnlwww}}}rrrkkkssrXeee 7mnlwwvpppŒeee 7\\Zlllºeee 7opmiih¾ddd 7]^\uuuddd 7ijiddd 7qqpddd 7]^]ddd 7ab`ddd 7rrpkkkddd 7ddd 7efcccc 7ccc 7klivvvccc 7uvuccc 7uuubbb 7uvrpppbbb 7decbbb7mmlaaa7yyx{{{777 MMM}}}  aaa7 jjj222iiixxxaaa7xxx;;;lll xxxaaa7{|{ uuuFFFhhh ```7qqp lll444iiizzz```7efd ccc222hhhxxx```7lmi rrr444qqq uuuuuu```7 ZZZ 222non xxxmmm___7 lll333kkk___7deb ggg:::ppp___7 gggHHHnnnxxxnnn___7efd ___>>>lll ppp^^^7 iii???dddkkk^^^7NOM ZZZJJJkkkzzz^^^7''&___666rrrCCC^^^7nnn444iii ___]]]7z{waaa:::ddd OOO]]]7z{wAAAaaaOOO]]]7}}|GGGggg eee]]]7^_\AAAgggxxxzzz\\\7qsm%%$:::bbbWXW||||||'\\\7qsnkkjhhhiiiVVUw\\\7hiebcaTTT[[[bcaщ\\\7ÿr\\\7T[[[7V[[[7$[[[7R[[[7ZZZ7ZZZ7aZZZ7ZZZ7ԀZZZ7;eeeFHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH1??(@ BIIIpppTxxx\]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]]\|||UYYYXXXJfff.[[[J&aaaJ"cccJ"cccJlllTTT\\\aaa```"cccJrrrUUU"cccJzzzOOO"cccJ\\\NNN#cccJ~~~VVVccc\\\-cccJllllllppp;cccJ<cccJ<bbbJ<bbbJz{yvvtmmmzzz|||rrrvvuzzyLbbbJppnyyyƀbbbJaa`{{{ŨbbbJmnkpqpŬbbbJppoŰbbbJz{zŰbbbJqrqİbbbJkkjðaaaJvwtwwwðaaaJ°aaaJmnk°aaaJ```Jturxxx```Jwwv```J```Jvvv___Juvt~~~___Jlmj bbb [[[WWW bbb OOO___JmnkKKKDDDDDDRRRAAA```^^^Jlmj FFFNNNDDDZZZAAAfff^^^JlmkAAAQQQCCCdddHHH]]]^^^JxywFFFCCCDDDkkk???MMM{{{^^^JJJJDDDFFF]]]===OOOvvv]]]JQQQBBBIII^^^ <<<JJJ]]]JopnQQQDDDEEEKKK@@@[[[]]]Jz{xIIINNNIIIeeeBBB```rrr]]]JIIISSSHHHMMM@@@LLL\\\JopnGGGPPPBBB\\\FFFIII{{{\\\JRRRJJJQQQAAATTTDDDXXX\\\J}~{ HHHSSSIIISSS ???LLLqqq\\\JLMKIIIAAAHHHlllBBB...[[[J;<:GGGKKK===PPPCCC [[[J¿mmlGGGPPP>>>HHHQQQ[[[JijfMMMVVVBBB^^^ ttsZZZJ{~x--,OOO???NNNhihĜZZZJUVRDDD@@@KKK>>> ZZZJturzzxBBBCCCppoHZZZJvwustqopmopmVWU_`^{|z,YYYJ8YYYJ#YYYJҭYYYJӲYYYJQXXXJԙXXXJGXXXJԗXXXJ ]]]UUU#wyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyyy.(0` %III???W®İİİİİİİİİİİİİİİİİİİİİİİİİİİİİİİĮĨ]]] LLLIIIuRRRIIIuSSSIIIuSSSIIIuSSSIIIuuuuSSSIIIuuuu}}}SSSIIIujjj*SSSIIIu-SSSIIIu-RRRIIIu6RRRIIIugRRRIIIuRRRIIIu˄RRRIIIuʄRRRIIIuʄRRRIIIuɄQQQIIIuȄQQQHHHuDŽQQQHHHuDŽQQQHHHuƄPPPGGGuńPPPGGGuńPPPGGGuGGG""")*);;;###ĄOOOGGGuvvv BBB)))111ÄOOOFFFu}}}HHH###555„OOOFFFurrrPPP%%%--- NNNFFFurrr DDD(((...NNNFFFurrr;;;)))111NNNEEEu}}} JJJ###111NNNEEEuxxx ::: 222 MMMEEEuOOO~~~BBB444MMMEEEuuvtzzz BBB)))000 MMMDDDuJJIvvvKKK222---LLLDDDu~yyy333!!!QQQLLLDDDuDDD'''LLLDDDu&&%vvv999###9LLLDDDu555{{{ͅKKKCCCuxxwsKKKCCCucKKKCCCuMKKKCCCuPJJJBBBuJJJBBBuJJJBBBu2KKKMMMdԟ??( @ jjj......................,uuu222777777|||777777777777"777J777W777X666X666X666X666X555X555~~~kkklll---000bbbnnn&&&X555~~~iiilll!!!///^^^jjj"""X444}}}eeehhh$$$222dddjjjX444|||ccceee(((***]]]oooX444{{{hhhfff$$$,,,___iii X333{{{DDDddd$$$,,,```ccc"""X333zzzllkfff$$$000fffjjj klkX333yyy###&&&```fffVVVZ333yyy+++eeeXXXV222xxx$$$xxx222wwwԾ 222wwwԧ222vvvր111vvvs111uuul&<<<<<<<<<<<<<<<<*?(0 ` zzzFbbbbbbbbbbbbbbbbahhh 9BBBBBQQQ___|||BNNNXXXxxxBIII]]]yyy~~~BMMMZZZyyy~~~B222ZZZxxxZZZBZZZvvvCRRQwwwnnn2{{z~kLCOmmmmmmmmmmmm'?(  @z̖̖̖̖̖̖̖̖̖̖ƕLLLhhhqqq (,,,kkkiiilll,eeehhhccc,^^^fffaaa,eeemmm,ccc񨨨4!ڝڝڝڝڝڝٝӗzyne/Resources/ZyneSplash.png0000644000175000017500000015713712417524776015650 0ustar tiagotiagoPNG  IHDR61iCCPICCProfilexYPͳ|G##Ar9 (G9#(`$+AI QQDQQ$"IQ0 ( oAT^{S5 3ݽ "5""@hXLu7; @еm> 3mY=Y}|l` fzQ1 ``|LQ0f'm/ocF8Ø -QSa9D000.0^Ho09N2TA`tD5s k \iBuy9믈<o,eekE Dmcx}_DzjR806K ٶ`|5*@q0- z盏:hdcLb1p_ g@yJ?@[h fa ØO?QvD b/?NӶ=轁ivz6.nՎFɣPz(MJ PP,( RDtQ( M 廝YЫ~qEN vb31 1CхO$4[Z"/+'neE˓hyѸ?|a&, ZHޱQqi @@[(;@gy 40f8W@< r@(g@5U n`|;X  !2BB$BZ!dA'A>(ʆ@7P D"a B0G8 <HDQhAE#^ &+H$ Y|H)*Ri܍CF!SYBd َA>CN"?PEAIvjrDy"QcTu 5ZFB\h :Gǣ3Ѕn ;a`T0&WL&s SٛN!wg0L"LLALLI̊N ̷̥'Y,,,!,'Y dfeedmbb]edaeb';ݐ==}!aQͱĩ͙y5Kˎ+ 71ww1=E |^2o o>f.%RDOY3[wO?.@#*'/%,+h)OQ^HU(@PЪVy6S$F1Qhhs1XXؠ8B\I<@TBBY"P\$ZRM2LJTTԔ4tt'A22=2ddCdkdGHrfrirr_K++)|VPUP|DVT:ԥܤ"RRIFjZMOm-1W՗4454wUkZ_Y9E:5ͧMծ~#s^gNWL7H'=Y(z)Hc,CaQѲqq $)i鲙Y}sZs{QK3ScVBVaVMM[m>{^z'FEcܝV ']d\R\]9\]vcw;>{Эm]=}C#oǞ=tlܠZS+^^e^ާ?,jik_( X , dt&h5:6x+9R(.3F),8~8OxBHȂ(PGt[ >=;W-1!,Qxbf\QҹdTwr>}MTB^]?s@Aɦ}KwNo?}Ƈ322^8r(hсL_Y>Ye 7y{x\x~'N* j1%M<ՒOVPiӱ',ڊs7JJ^^**,[-)Щh:}&ϳg_UWT WVcgkjzΩ?q>fmXd]zƅ /\lkjr)2{+Wͯv]S,\v|=jIlYn hlsm{zFWF7ko*|dMǡ;IwV:#:ߝ5zͻ{=ףsWVzߍ[[)=X偖'*O۟z1=tMztqK|^͏|~z}z,kapk؛Kʓ ;:=]C9޹y[ F |c'OK:K]g>G}r+oߺVlV&~__Zc_Ϲ FѦf/_c[[[(N(+/p t)$| v8N0ɠ 쒐T=r$bC . ӘX[\ޕfPFA%[13Hd.Sqo]lll+/8npVreqxRd8B_DfD_7eD=N.r)#TUT5/Ϯ_Zq[$7l/ 9AY}a8XC:#;P ĕhN4sD=' F0|əY򎵋G77 FBBZ"8b;%.HKu3WUQQ"),N=WhՠYU}R'[qӆeFƭ&LG-%xSz\<]!-=LzS=J|i4BDCÎDEFScb< pjH;X֚>xh.8is'EoWTxtyѥ%#eS33gώWW=yrn/?..s\*tMYtlr v9z= v"{r{/>}|5y0iP7/ò/]^4ÌOxɚ>59nLy텓.j}[X."ʾU ??m ?#s}83@7  CptnCB (>Tj0ј7X3lNWi4 \ZsDym:3 MF!$+;rYy%<60{(S?7,&o2rϝoR@`V0SHJpHV첸AdK YlyZA|%We^Y&X55u:e]mt$uzuIƆ,oLMMͮY8YjYIZ ll_QGCcSsKkn}{TKds>bGZgB4}"EVE݌08ԗ|c߹өYSMsO?dq8CQL,lc"ENHT͵=bK%Բkob+\>7S]TaE뗑W^j&\iw#}ehyW  &>19=#kdEeo[-m2c8>8"LSЬlmye` l"4 D/i,FNQ1N4݄{L-f뀽|q]x>|~ƌ*IX}N4$щUГ $oጋٟ)+s" `fdeSaeweY˥;>A|9; "@NLx8'H:EFZlKw<4rd33'[X߉Sr3E%e3QbUok{Ily1k'-mn/yx>~G+ÃC'=1.0qsjjf:~40ܧХ}L|ZiYZK1.qxsh`V ErNB4BqɄtEV gPҨx=4#gӻ1x7h44mQxN^J'IcL|L,GXX3_p2]ģ3ϛCQk/ Z !ZED&D ŴވHHFHqHݗy#[,$OP$SWD5PMSA}NcWf6R{XI!hɒ' K}CcM Wn17Kh=~ x9xy( |r.=kז XL/=in{d;}r-6wTBHԠyǙjdMyږzX.5_\bҺxʭɎNѻ{H&zK}="›)sf:oPi|Y_z/܎a}" (Cqȷ({6Fskrm9B9E;Ho@da'#311snsr k * ) +iIHIq29Uy;xr[ jAo4ٴT?^DS!H$Ӵ{KZ>!SY&;&ݿTx%x÷R@\`G0mho\D}lX㸧 n))Ԣ-Mg;ʜy!܉C9|N*;x_)X̩JrU^ ZκƋM}L_ mۺz#۫w;u9{ҭW/('Xg_ '*a$˨ɘʸibɱEXϊ!-xy?pjqc'OK'E?[|د+bL}`Uyjn-ymɏƟt?#m(oߘT<9KWߖ϶wDzbk0<6s֫6d+!WʒQ׌-fi,z pHYs  IDATx]iTTg~UT\Q&mcGmMlt$e,}2}&&3,?_əIwsfN2T@Yeت烏s ( ~TQ{o>n_0``:Wjr9DW-)R44mt!F_U [WABШGG!"&sb!,& qUG 10(!!!ABT#je]3a+e-jj^# KztZke&}wa3Z6IO>UU%04D h͍BԂX\KF$a ̏@ r8f،F4!fF@8L)LQ{24don߻4eՈS;f˥˅v9YD"LS/b"_q8nؤ,92UcA |.82[ƪנ T%TfTI#(0Pbg%+t\iJt+ۭ;`vV™ SpJ…"Y4D-^ x'(%b9wC60ZqBmCSqJP{ ^8p@Yp~B%c#@0_֏޾66D{)08A!"=%2*JKXEƮm+[:p&Sef` ^]V'vtz]rk˕*Q#2$_5u9緃MwFמ2>[lMM圿oLG91k[yQ[qas bk0uYTE|Y:Pڌ:K\.HBB/aaabj3t`̚5Kn?SrHŝVv!ߧ%*>V$`˪uJ|bz)"dw@ ]p Nm(BEVrrrw  _~#wϙ3qqq7os#** ш& w4^I19A$r444… Aee%&-|&SA<&ƌmjCA].f bn4Zţ(e(A +'6שE(EQF6-me3ကЫ9J5ڠOHHH?HU! 5 m W^^ ttte ]\T0#&`=CӇ 8_( -V߽ 8d >ùQ8-f8 10"@ًfqM)BUfkEp&̝;HhDZŒ%KY\R< !]]]Ayy9ۇvDC;R.6fӫ}I㉽=HT;Dv r TI_?tR,}LdN @ ܂JFr1 9h*IdJj3XNԀKe! dAbHLLĢE"P,wH[WуKdTi$BWܮ]@uuP=z8v7oN ú y=ʪ-F:[`tÁZ:),,AgMgvA!g4󦋉Dn:lܸqXQ0NA8A,ct]ykU X,"BSTT$bٳ:\ȿ+Y'R].<~*1`ȝ ;Tu֣F-Oa(b8Opb1 f|Zp!-[[nЫuk,WX!H׻azBH~iDNQPP E A"R ^WG' 34ԓO)qd^L rz5r&.m7jDG[k2JXbAl-=Y3|OcDd D 7Aw fpqQ\vMd1Y<90kc ۻ%wv(\tjH2ÑySr! -i6ΰ q /UΝ;[jE /Fې”X:ԞߠO?&I rL>amty֚:8zl"6p!U3̌ڼy3v-N"ojjB]]+$-/4tމB%3Cw^Bjj*l[o X;κjKj%xg}w1jKf !`M'le*yЌ;{fRy%f0(LիcA%ht,Lii4}c.Qh*HpUq!1jkkEe}FFH NF%,†$m0oy*yof-po2goMJ9'>&,]-]Tƃ>k׊ڌ$V44=4?7ᵪtD^xA*+,/Y1>׊FI\ܩ˦y|@1̬+8{ gpQlCęMo>g Y--x.d5Mt?foW(\%%%HNNƹs3<-FUqnju_oa4c}I'^T$*avy7Od0c{ \V "2XI@Knכ.2O냩ٜ\ꫯPZZ*"ՂNQSʯxHlLĀ`4cvk҉t%(>>n`ДIV2?#BEAA5{ÇAیUhmܽ }Fk b섉 E>dJ|6q474❷V>3~;˖,5b$LRSRof5+chܘzCaϞ=BmH4Dm[ >_ր |B Ods#Վ/ᾈq fc oGs+~XY{LW^1b$1 ڑ_VKf":O[Vg_E hVu U_{]Z>N:;;E3Z,M @dkqp5+[}xo_= 3 @E լgY-PD ơOd?м6 0@ 'ri6 CR 4{Ƙ7DFT1$<`H~Dx.6E6̔`DžQVR3gg^|^y|#ӻLA ~^ Bf(iCkE \Aacw'%!j(A >D-zL5w(:K!vyQZr>rX>r57Kso<]Ep` Cz 2Fs'Lj?SSwsʫ/3@|Nu9] 3Ho\R HO~J9qFDFW@& ̐OiJ|q4Ub2H,7D +yrǛV Vv+.tz#6U^$^z ǎéSĚTi=jƛo۶YIX`I̾ҧ,+vv&e(?)p>^4̬Hk4;ҨqP*n=w⺵d?>l2GߐR~lsa߿'Nɓ'=r;%mr2kg2VD> 2zsʥVѲdՆtWyIcF Y1 b7.8;ge r?DKIZ5tw6YnJKMJ2M& x,XJE"mkD4Ɔ^ Dl>fc@=ȏ.[fI={u1Cnn۾xo*[[.Y.̥?s?~\4nݲTFjr2^TxWϙd;?{pT-ZA UuQgAGi;PBMT y'T[B$A((^$$99flܻ{w,޿ىFj̞bjWLz8l8SKKKgAo?HG뎞lj,ozfPr0>ۖdqA /\3A41xq#>zt F^Clwd$d 9A&BLu=h 4iӦKp 2#UD<Ѥ ?oo*ǵ(PLK/T=#z,_YYg%8@έ`~u޳U>UcCc \/q}gELBCTf{I |u!LDe-NL'kP _y啪W^:Kkʔ)Z^f5󊐇SGs0菹:;14H VU KK}U$䚞kAe#'0ȭ3I= {WG,qp8,_f$Iav;-G8DA޽(k/Xdʜ/k]@x,`ᓭU\\֮]+ښYc'M^lIxQ2S3c;oO6.\5D涄Mַo_CnMM)-M6-;y66 d޽Hdb2Ab:Yuݓs=WDzB5XKb>X]dXȣpGE3g`XlT>lxԳ#÷~{6I!vjpeyxaj*pU=jQt5eir1{C}C̊M$) !rc5?[H bcͨAuv4-eb}LC~MdXGA#ncǏW۶msM@>_X}ݗDځ zuB-ݣ^*aޛzfG[m9t;O<5F7h 6]2ptsBB曌c%#6 CDBZ$b4>!x=FiEEEBP4(!ccǎ ٧/zNd |qdwΧ}ߪaM" ={T=&X|bFʯL \SHD,)]LϋdK`? ,Pw<vء-0ȔU3,!\iȓǩRnYg䕽 0T_pF ;_-_믎 7c C .NtD:֖ 8P=EeQdfw2 bYY9[$¡i&n:wzCSN1)!xL(0HUB[[?*5n8 %KK{"oيBx3$if-S:PBNUx#U"u g~-A1chkw֬YzE֬^C{"g„ i=*m Ꜽiß(Q uUe"5J|ZxNdqhM$_*y˖- #B.Y!,z~M(( AVZ[[mgϞs4ycOgk+r^1#ʪάbC4fZQh=rH=Fc`.ڣZے?IfLC ټj^YKu{N:M"bp骓v 2MTLZ>Dʁ^)/** nH;Yz`VAZ;Ȯwi&5yPk hE :[IdKHesί35ވs(Ɗ@ŭ#C &ʉe w,\QRl 4BiIZ6V%s]m?N^^D҆@8TU:S շC6t#.ȳ *AO DQG[6e+uuW('26fnPd @8#3W(B$rhȑXB fxW8!EڍzKs8=:HDR@ʫ*o<L2.^AKlBikc.b!%)S`S(X {џyQ'!>XE1ibiuEz4$iN5b")K ײۜj!֡Cx@^^o ^ÖC?^Mt|r\2X]XȲbnf;ٸq\<_K ;w/XwDR@Ι:._}5wv. Ӗ6 YuܥeM ˜nR_BIOQz܅<'4D&B]5yHB2o]A v2փu@XV%:`4LTf3;;׋ cC:Vwס< "~lUlAe&fπI(3 @^Ss" L6ϥex I x:d  3<.@O1> :D9M74$%bKN!+ptE .4Wgi}=zP\r>ĺ:qߏ"+6ӾcVU/L- 1sX|GvJf ?{Uս8F1IcTbFZcLMx[}L1:kLJ)BԪ Q,CB^U*" φty 3{=e aѯkof>=\+<:Imfiڄ{ =3DA\JE9[tV6csR*ʱ,)Kem8 %5Uꇅyn̙[o\~#@Vo۔;e۷cWݍA)hrUIʏ-7,-aMC=MH$fં>/--jG/Bj&Np4rd\6**zu :#{C.,qVo-4@O/15U[BLY3QSlRJu,LT܉T͛7GK.=.}a2g3/@Н'rn↣p9m䏵z@p4;Bq<,ΊɆt*Rq@܈:{X)3 {3RMW o.M4'H ~DsPCzޣ)jNƘ?j 1_R`*۶!{wYqw I(pz(Ӛ_Hcӆ?Lpͻ?6hD\"j}唯DeAGf[[Z C_ Sk%DBQԍ7 RPCqs):%?sXA?㪬fOfVqىp;Xd$9&J >. vXPVjP< Дder(<%j]HW;qc>`吱D}(!_0+;zTgO;SkL3)@:G/|y[d9;n K ø#F@_2BGƲ Hr1vVV`RvZf)B2Bf{kERwH-UVNoW}-㳐W X̕˖`l,X=%^hVw@t Uyy"A= UEcCYzN5N?]ٺ;:ExRnĩG&D2'@?OZsՃJoQh;|Īvo" 20|$]Ʌ#rMX B 3-4}'g2,>9ʪT$oҥK;=$>89s港>8_?EΔ9^۾f}Y8TYL?,j|wsh9 Nb;D wQ#18KtwM[9蚉 8Bu 4C ܆(wJбk _\AGY"SL)Y)KWЃ%dFiusr+,ri|~ܣ;m]5Ϗ(Ϊpd>3AFձZE;ڀ|w3 +ZN ֳ@,#T%חnr[@% BƍݲeJ:s~_o}+33U-)]Uuz;/o)@$&u ߯~yE]'Nt;kgVB\?\hEsdEsiZ|q ,hEN}P};Ib ԜqvԖ.rKgX{ȽW dرճvڒ}}A;f̀|^ݧ[ 昑TǝQՄLIϏҕ4 C LU"'Xf*͟5Jo!$d?!Suq9?2=8W;#I1 J*uW^]U31cƸkR W|m]v94lyeKg;*aEV5e~38nf$@8.2 -[\CCC:thd`qܞ={,=Iu>:hW!TuM֓Y2TxwB, ܅lf8B8GGP YZXųJ?C&,s=7RhǓfK1eP,OD[~[~}gM`]A~Ǟ ݮ^DuJm.䒨eəg cmª#BZ 5y? JE# \Zc΢`dqY}>  BBDwӊJ@rbA{MzLcPf9?م&0o#F2 PLΝ+HE-Ұ(u!]…UІ*#3]8tf@ĘaIܭg)jν{T#᧤*X K~j^ мai N^ ֏{QbDhմ=+aJt#P43D|H ^:eX|fc H[QB5 ~G"4q)$ 讻rO>d+B O{ĬHs[k/r򩫮*%t.&_@I>V-8Vi^-9=jNU'㈪uZ ?OU}}[xq=g? 455 ?sةް4%i] ɜ; hNBp-+K{  !"߶Z>i bjŢJ}ʼn,vd! VwH3C@JPV&u гeY K>JO(k֬q/ˆ#@?5w`Ͼ-Eo\U(B<v 9h H iVi 'S#,1JXEF RP#{.C}4,`bVAx{)<eyh08%Z4fnȥ^,YRoXo .̍=:~i훹[*-A 2]{e +@gA:8PN  ^#<`h~>{ X?k+KxpHefݝ7PdHRCUm{T;A۬J6V}D3wt6l2ќw66Y}U)rbAʁ $, E{iD\v]@ #J+!}k~5˂+;p`d@CYif視.`<0W=)".~Uvb:Kr+%{X.ēj*BJ7o޼.L>|xP.s꛳!J*A.Ҳ| C ͐8L@5ȲZ"Dm;g ]v SY>sL7Z*g, KA{VeqWO"KfPh}#[~!'MM7ۏU? ĘE:*te+୍).#~ϴO6&)'4f]:xl0jБq4p)~&p444o\6xgˆeYEeҹ^.)K%XPP`ޜG%xCtm~M;#+{f֬Y.]:WһkWInZ.n.\83t 1yyeyUgakYYKXyf1諮*?w  _D(oF i͵G:О*Jkcd Zw4,LIrG"iג%K͛#[LSՅ ^cuw8ЕpE`#Dh`ЁȚE\Ec{.ZD IU06 "83+{= )/Y⬱H VR(F 'c׾.2[|饗\û.Mlx;wyg-Z(<cNƒBDuU^rAZ$O3ȅCq?άEZ&/<>ʀ, o=a9dTQR"7(W4#+dڵm Յ\rSO=9e==PNf~ F-s H#}X #y]JEx\kK18PWrY|w9[&kG[TYUrx++@D%2[RMRgR){owƍ?c.u!>&ӵSu{d+pic\Uved X2w&At`A8}LgV(J*56 Yk#jqljHTaqJG.xfKZaiyPJc? Gd0VE*T5<}4V h:JЗ\GÕ3 :4b.$f̚5+۷o'r_T(^6c!"<!`ȔT̃=-cΫyM7E7|AH.dW ۿu_}/RA jb!D(3&`{JBHñCq|:`fuʂ9VJ&ʨC1kպ1[8JKЛ sB^~K a*{6~:2b"/ڃBB *e'AAtE_IK Du%%PU`&’f5eUh r!c=avZn>޳-[sYK2n\KS~jjҚuQ⌼XžҞY6ۺ<㠸ku!k׬ uR֟v{;^Y0~ IY)HC5Rp1*oP|/'tLuw5Uڪ,~˭ pFbF9, UZkyj{<4JoOOOtu6d< l~8mn<3 wk7SC 6:." ZZ]x])H$@8V&T@$]c($@>~ݝsv3~?c^bPHFmõ m#?OlOQ]UE,,]^DH{=:PBoDZvٸqE= ;zw}Y7> >C1(#T[iЁSRTxlNcrun "̉L:}j% Du)\b)ƪwXalIf|"EA1@kMiTg;|Pnc!)[lu;sܖ6wÙ7*kŐ .y>o$M1sŎU7T<ZB4&`ȳqg#rYϺ q}EBc@Ob;$w-zF+/R4!-cҮmq1^X[ܹs[~v[cg{C)']X_t9P˄.ԗĶ.]E` 51ta3,UwĴ^A@[L^ >;I!8ZNb(n)Kv>0^Ȣl{OcO|-}XQLNІoݢ̀* gp,ecPY͇@k}Q[z^ߪ%XV=JujX*,bz%1uEnFnݺb?Y6gΜIܻT}B}'ԆχIS!x.ؐLwWDQ ",!iw^>V6CbSUXZEmcG؊J-^>s޹JH(8"ƒjeA U{S0Nn!\}]dnذaO`Z*NuPJ-J͖QU,q)z*%6hU) | T+vL4%e ߎַo(mՅ̸7VK䃏> ;6ŋGiVjHU#mf4@=6B Rz4d{xjثb=e}Ne *zXE{Ղvͩ ޘқ (Wn)=nr[쩡Е9<뮻 믿4L&'db-xAH]P]ڈU J/AO|۫'أ 9ë0hR$L+DUٸDϾ#sOԼT}xm^Bd UQK1oë:2x/IX"q=o/O¤j]wL."EHA 1Y<r59`)lFKZsw,Da; ";?̌˳txc_pٻOHJ:u Ι=!WUkq2|_@1J=Z1sٳbݽ{wXbŲ'xb\& {ٻ7c*%#GQP#@ZMki5 {< 4UȖ+CV^Npi0gΜ/G'ZSj.lMHQ\Xan(CH31{E:HBBX0n.e sJmYyw}M(b ' Et0eڥg$(G,PK`}0Ңrx^4@I޽)ʄgр$D~RΥ.v[ĉy}r= b0 UJPƑb.8:Q`! hUQGd2*H$ce4)@vy8~hςyZ?7)f3\^HZ,>&GeCtެyY])/R曘قb00l*2=RVۍ^hGBP W=ٻeB,ּW!e6E;ExW)2QPI/'a&rL=͛G<><%& n\E+I'C`vHd"$!</{<+b0"4 3I xLvuOU]?Jྷ:wjZ{׾ DD&Nڃ06$zjUe@.ijQ! '55QVvZof j +Im΋Heqv}뭷!WV.;?'ϻv K \:%K"ek; DAT/ɁoZHNOưY+9(slEy`ܝa޳L,(9Cvvqٴ`_+1|^Zqw%gGk4 CE#RAO MgL?.V2P8܇R(0Q# {ܹS4Wa*hZ:=krޒw ]sCN͜(0 gHO<2 D t10V Jc`O=&XY^NAa^yc ~qv{y֒B@I[^]@elRUbyӉ< lCRj~/CAxgg>9kc(uG $*'MA}.^%5~DD;ƺ;rg};.\8ܴS ; _Wj˃7xbMcȴ/EiȊš+%R5;  P` @`Bo{i ;:԰*59hnMQ4N[[,uR!,y"tTaf !J3_V dw^ _ Jy /_ϫ ![5,en#;$Kx*Sn P"'禎xtZ` ~ jZ(op&X ;jm1[iJdٯz_yc {w ?ޜ/IufV<H&(R"I}=ƒRPHI?7¥I\=wW|7T$F-7^QKTaYh$Rhr:ژ?o뛛WfmK;}_ڝH <&E(ƐSչkk6E~E:!YMq Vג0'G0jT);Y r'3-wyބJA=)1Q(r飏> 2٘Yvy/; StR @Tˮ*RV( &p!+\41#:ՔKQ\1ODޜ1"/3F F"%qKL}vR^$5U(^dȈYw.xewEUaU%Gk AQhaOoARBjz\"Ոcik@CKVQԈE:w?CkxYS#?%x sR63.]=޺ukL. # 䛥g4W{8r++ia#iw@7a;^`%1^ b6VS^iv^/K}qR"RjVaWM=2)F![׬pQW('[ʛ6(=g?ؐӴ?K0bMAHҚ$Ovwaа޳"5z5ʛlPSgԳ O+qDA>0P ޻y;tw:c5$<8ZqoRAebXoJp{v}ئE} q5'U袋lL)BpYDM+9 yz{Q B{9R[J*I! UJYg{sPI^y3.ߚ N8-ZC3g:<-;Q5|y Lŧ14K- wQă.E3D׫3bhA6A+snn.|a@>\e!l>a`ğS@84F{Tp,gJy69g55i|@,:4b*c,jNFnCP32X@Yg)cRDсozDZ;punz16_sJ(aMdUY0 "\ vVaْ]yqd6`Me}҄%B:/JʄK@.x2N<ϐdLK(.NPJE.%O4rqrŶrTp'oυF4F}D@4%Ubl܄z=6t5-0e0}K^ {x@a% ^P%\ zJ)v2<λ~neF타u&˗g!\)0ww!yO2%#/乿=Ξ;)v}̶e^.%5O<Bvf[,ay̥=֩Fo^{M^ɫ$BX2cx *5J{*-xf+W*/{@`ZKCF};w\u)\&Z/2~(_lJe"K &E®x)JUB,+!T1pFNkD*2–ʩjn?+ 4>–q>I@i}0-)Y`Dyϖi¦#%z `s&K)`jC†<{\ņߴ߯}HS@W1{va{}3/n| Jrmrm9a;u}JɵtF`AVH*?/Mͯ}#O&KQ1i=([&XCxye~Z'=^drjo +K Xv{}I)is*27oѠ1MzX[*KsaM2[7~n=oq@蹠NW6)*шe)f[XˢZM7먩kޭ_esiAE @rhS9tּ~k7ULnNT&Eӵ3pպrc7$c-cV2xQ33u rlp9+^#gN[s 5c=vw4 ؠ]ΡӧOWky;OZ cc,5blm3145scLu1/Ӗ `T$<Ǻ{vT䩓>5}'@katcYy˵L>uj\1H=m(@%@#jo~k~r5ū&u)rcȬVWGs,&pYaKb;ެ&YX\hyBLQiy ^u31s&kIueҕoG bnt漶Zȩ]\1tk-ڌ HckHKcLSf5zΠwt2;6@G>"fLZ渚ɒ,# ǡ@._lu'i]I?](@`l3`Cz`#Ь!8JB{RqFW1bP*'š ͆ {2^S9wȉSBb(hQ \<,l&< =$H9g)-*՘J-R0rÎ\.9hE!o(Yge1yvKX%3^ň}mn"~K& ]4ޜ %npͳrSNgָws{L.٥/&>1&Rgx|0gaB08eڑ)i Rɏfcp)Ϧ!>+jTVm3}RU2=e=GvL{p3gIdL5Ǻh݀b\*N٨9ƇUYV9E;6 %@G$R5@t/P{Lq7 (?'-dn< WQDFQPܷ*[<:DԬd`00`;.fNk>UJf +x| CX ِb!vycLCÑFR~('SMuda%|jڬlD)ƒsrJ<dnf 4Yse@ؖ mnN "q8#r; DžwL* e> uSPcsm?R0&0~ x(7H.S9.a]rh-@DRe£a`Brel\[`;ԭUD-5B|cSیQc vE4TKJxXbđ4FBh6uՐKcl,5V9wT'@R'^nYQw%qtAp+R#4,5> 4tzͳϜkɽӌrW-6s}mF+g p̳Ö?6S\e9)zi{hlx_]RܼJfL˶0s] 'sGD枛i ظ"KQ̾b^7Bd(5/L<PKQ 9ք`c'PT8~{Z!83ChQ%uel%N-T%|-)jNGyqj2N:Xۂ*dVBxM!~X4>5) 1 cP 5"Ux;ЦdCiqX`U7|Ӷ)bb3b' vhonfjk9A!7 kIPVkҘؖ K 2\A5GN1M,tE$UpH>L5g?X.!$<pr}м]X$-@F5 ?uo8KS-UjW rcYaAs&)$֚۬"G13vו `e>1P1ރ֡7OML;"pJ=ző }.PϞ=]7 ŨK x"Jd@ .c@{ "r!5|=쒷@X%m0u0}su=vUdLdcf(هj/NU2Y뗺£i){(ұV9nZIk-?<ڗGh5( DfAw'f!b6' /Ƹ2 9bۡO{x~h,ߊ%<-݅1s_GeF7a_ܟ3+3g_oڞíDdpOҮǹLdh5"w.&(nBެeU ob6'=GO|8cT-bǃd q ǁ-]H}>-#HT5UCm I"`g6XKIL'ZR{`խgnePuAٮ>"CfsIךჇ4Z47s<[խia(T `YZ~_R΢v>笤U(-@Xm$v[!<ؤs%u8c (&b Yv%SL2 ]dgO?Q7OBl]og־V3'ϵh򞘅|v;exXZϧِSǗ٭i'Cc0r`X<(T t߾ō0&W4&F2sadfeY#q\]lTSz;!ZtjI5.~!aQX4'B5K]i_܉=їN,~2a2ϟ?!D\+mBD !,`lzseF.죏>JG3F0 [pH]72fZ5I:cI%M[wmJ*xϺݸq-g|~{˾EHRJ!8yf\ݜQaUs9朚ޅYw_}4֓+'dbߓ=wҥ1`k8) `\5Φy뭷Z݌(>KLL֋ʌFJs|T8O<Ç~ؼi_Ajf?495oij =ֱ0"D׎|YO)MFj蝭"8|n7ue:>9-kҮ-?oKS'?m2q[!@샚{Hbs2oSǏ'qޏ1L$q SQ@X@e&a6){Tf$) `PFP2M焯{|}psowt&j~qF s'ŽrҜME+g `WV/A+%00L701`]ʰʌ)(T֯,5X~9Igej:ptC76>f _LM+f.ق0[֌ Y? s-NlWwic3|vV5V)g]C_^hgΜ)e\"ш`بϺN>ݐ+TL,LpMhVV\46G. CRe}8.$,DTu>uE؊1)s»MdC1pQHaDWݙҀ-S鮨/ts3KN=4)7%DkIzh&&A{}dK-(I/RAJ3&|Y+<4CW隔uݻW5e Zc@ygl͊7B 7IAp (ʗ# ypXV{L`y\z_% L8$HPBQkln\ n3H|@@@!!6j(5kٶS;6(R:+R+&`)=9;ş6n$bƔiVSŀBtw^p])h@ Y QfG@ +>fbiڏ\D P iC4Zi>ܹ; OԤ11Z N*/3C 3o%`m@.g|jk :ͻ_)|;vT=_\lz׋1s֭DܸO/4nL,-6MQ#򌐖ASI@9n:Y'B:8cXBh_r٪l.I#2XTV|A+ Y`몭ҭ-r[#@@Pk@(4K3h I e4nIbuQTqV>[uɂ]9#?poB၉{gӦMI0byO)?GfwW\l oM.:$D`JZ趹8:7> :(?UsqFnPa4 "+Z]Z"DďZׯ_o p6XMk7${Iϡ|{g@Zն0 Z:h^ŵv9ooX慦h߿oFGG)m>8= ;OԱ9G`=MeuJP5EK"mZ ֭[PbHɞڵ}ɿٔ^5G+`AB8F ";wDV&F@0Ʒlbrpp;,Osabyы`dTK@p%F{q߾}'k if{L#@TH,b8a&r[Eӥ@kcDe$DlCUz6gsu9.-4$^reCPNөdb| n$333СC0%@ P%P Ai.֋՜ɤ8q{V қٵgOo,+Rr} f SbQZ] K$3e] 7(8.6\XƃtGUH\-,10oЄCH-6NMM}0NR:iVt!L&;uEt!ϝǻ_Y`/n߾mi/ \ےԮ7~Q !rYsOjVou+Man.֌2.)ٍ6p||J~*ip^QT6/A@Bŋc۷|t%Wsf7$! (46$4$BL` Xtl ?:0/i3Z`xA$@ȫT PElsᬛgy%7;}^=|9$ \UR;_A 1x@/(覙nb:DQ`"yvm ҟ ӌh;why]jZdAZH(>bpxMkQy T$C\_&!N<%3WYYJXLl]hrG#ڐnI 4PbQYJ*K"K+l)dѥ5́,Ho(CH@5աXC5N45DMx͙dX݂uAbCiё^`?͛Ndmԩ=/>FYeɄ GhM&@p0 M& 1IWϫRk`bu4BuE3ݑDż]Bς]4BYAnsDiu2lCz~A$i> znah&W7Q_3P0ΙB9 }yq;~Yܣj`C͉O aF:vpB &}F&l8<2/!*15K_M*C:56i&B]ˑH2]ra|b}D\21;WU4-RO Z|y<_ XSH&F3 GCX Aq¤ m; z]G߇E""{M@rrs}Dܒ/իi﩯n~QT ,]4F@dtd 1ؐ%Ev2MkW_{wiO6Kǚr|jƦ3nyizlQM{/ I;wN-//di&XW΄E`=i CYz!\nd\a:d^*PC bC6ǸO,n־{o?C^ڵ+nM Qp.K #U>TXlPbdBBUB!tTC\۷o|HoܸC4^M \vi,5yfu NHpSyd.<,lȋk!BKFc-=$ ٳg'TPrAdU^]n]w/6jT766& A^<fȄ> T ~:<&RIQ>DWY[_nw_,|8 L̀M-R|Hw H=`>5zxN 1&iiiBߝp㤗+k<-U}?cܬV\jkk=D`B1#zh^,,,, i)Bt&;;;իW#l!s [WWҨNu"*=C")+x(ƢԤEB&LJt8w}8k?863gL=Q/̞sl&.@ i滗9/-,,F$y.}r#"U0ə &6uTyau5?ϛͲ7 ޷~;e\3EaH $^S)!{nr#Gۥz7yuM"k׮Mz.DF:RAZXXW4Ť?1&\rlIEEEkaa+ri[տNv{PYaLJ jBȒG4aÃ@rL۱ba]$B"/(R՞VRǏ 6 }\8 s=="F`ϟ?:EEEIOwPb"  PZXXl$縉J7Lp $'˧__:_ލ`Ynަ i M>\LbEf@*!=]2hR1yES^q> .LU4-럳z!6dz+)F]zaI w1# ڴ0sGpXXXB HwIKT$af&2Yu.6lpz5ž6Kݪ˾^Ҳŋnq`XnG7 2>pS!X>B+F3ݹ)Y>gI]]]z⠓<z uNNVz_Y8s`w}7)7SW蜧L]L̶&x0%cj'2'ռ2D\DldChD3f>p@&f穆G}닼ZY&)^HxdvW=T٬#J>i` ddC֓0 Bil=zT`zV4a_]r_ܧ^ Ar&T .nPaZj@_L_cZbԥKt`Μ9ZEhf/ sN}_d^^^Z݇IqUkרsAF7 > ,cUUUU@&V]IkhqMIknUFgZ <.7nPgΜQӦMS766P]q/އ}466VDҬΒJOҦ3^~BH)+-0m*+M(k6Hn 7ڞuO>Exn2>? pUMBB*bS0"aFAe"ZZ[AlGBqKkLgAa4EZA ƀ-BC6A@)@x}LxD{87 a,ۿH~:r{?Wwu^MU?xP=@S/BSZVT[xWkX'>uZ+ *|Mkzjm}#!{i;b9lG,]Tl C݁PRp#1k9ʵ BStw o$մISkU;wT+Vp]3GFr>/\QEkrW_U?p(`uJR1,1eQ7h pvkUW3: g8h" Y#!'LRR{?٦D"aN:qY+/!)4Ha(BB掩IC?hAm}NP%'Kds v. SQ3[] ~Ϸ|;S 8pZjN5r!]C 1 maPWZ۝7' !z|!Ǐe˖i7v":?v+FXԸM9.u`1{"Å<h7AIS p:d\%CaOQ ѥ6U'^Qº;\܃o ,Hs+kW\P\!^n,L[C\ȪX+d] Xڀ<97E1+>&KJ۷oWYYYUƫu%,m7aMb՝#V6lP_QTbfSLЋCaAÑ- L2+2 )UUi祥:L;2}&ck*TUn:Ʊ̜*N P߻woիWp>y 0+3c@'kj[ )Ipi۶nz)4Ur=lP#Súa L۶.չ1D_W ba@hȺ0 diJ0=խ<*t~ RyMbҒ%KyE7uT'kvk3z_;yH aq>C]?vXcLۆBβ /@Zpq|G3ίϱǮ7@nu<wν5R{;vL=Ӱk0Ϗ/hF }yAk5J0ά|A|…=;3fpuF[׿_טSYTRRR"Rd %oC#,eRi@ܓF#$6f׮]}G-$4w&OW{ ϰC*#F?|k;ې+C t#c֛^B LZe#m?L[!˯yw.nYjۘo#x!EMU0@@nFhǮ,6Ɯ9stU&S n-))ч3q`[a1pyH} a:psT_ބhΓ=e?MڅHGbDM rnWAq7V%yꩧh:]N Z:Sr\Z=zT͟?_DS w46ƃ Sbُ5bԛ@bIB"0y\eKwC ɼ4I~zc27A;pI:?fPdnݺ`۷k槵h7(PQĂN:&uݭؙ+\T9zD/6߃̙3Dh@T} 7UG->nw\,''G Ν;W5n7!$I5ER 9eZ3cUdbʒlS!鼧OV-R^A.\iWZwv$?<µRe=Ln\ $!L%sGf*? +|WDL[>8d^[+YroYy<$^9jLۜSM/e%Ը`:`M>&CHn81+`\ "mgQӦMS\sM֢@xhBŃ\@: @"{&Ү;ӬEEETM" ~x TeǟbGstMTJ3g`82V[-h:+iM9 8hZ&.)"{|;t>d_ι}ﳷiuϋXw^a9lθ8q1a"zV) ,hF[mwpY={%>n)>6-m^@C?&=_mMw>na q͈2E&|l-.njlMm=dzx۶Bd`"Dݰ|%]9N{ۼht93\&ji4Aϗ_~Yd~ ~驧^x5T>R)-.V7rf&P6::E FaX Ua mpezE`,tV vϏA)+ؼzMpYAڵKϞ=[PLj$Âx \5 F2YD<1[6?F**RwRY~fD… E0 S[ංq1kًoȑe˖mU Wh Sr2C"<(|K,k 0w^zkͤ0G#11ѐ ) sgQQjE>{{?j["СCb9s|fa=l6l+[yXcX]ȨIJ2SV~(- +|111\l0&j*aybC x=ި4}TZ|)xn0/%Q0G^c^@SGY3M-zXυy"0 ##(''GWgiV~aIYsΥA3 Ҡ!/_N׮]56# ~D-S涽TV\{D|͛GAAA- ô$(0cƍ5-<)]=^3bolyRg)Sv:0&??֯_Owc؜759RqvsS@Wl~:z2~xn}0Ɗ+Dz?jcؤGˢ1nn^r.Bq-!"ϟӧ e%jxSXxb#6fD.GWW߸xd7Ԋ̙3ϯaik֬t- !TxJі.b޽;-^8xa  H-p&:_Ã6W=\Ӽ-T_L+C履~*}{O0rMB acY :q℈u`²iז.S|||l:ŝb&IuoQ3څPbti5^ 8aq և) a8\jjZTT{;zph -@mfu#MIUl@gReif5]hĉ4vXlfE !{LbBYX %==#~[L4v܉=k $ O_-MEnX6"2~_aO:%Zi \]$ՄDVCE b۷o 1qOwk,w2.09,4+M)"R,Pz1ziҤIԭ[~liv pSF͇wT}՗֮]Knx˅na|8lS˰4NvN4"piݦRTVPҒ5#?_{``ȃg.,K DZ ...ЯǬrXhK"X~yE9uڙeBpX@GzާtXs9BD66 nBBl #""ꕩZ:2Lm k2c拉G};IRyv두`5&CeX4"J@4]^/d$\^a#Vpppޑ%%Rۉ~A ^ǀ;Y.{g'L5-{Y o6 ׏vk{:۱Gfee fHQys0\p xG,sGrtr_ZIpтjĺ)! }x͚=[ ׏LAs#)[=%N|v*J4gl"dxd 25NaZ K\rE0\]M|Mo&.G7m$:" 뼦XG{<X;_Axi`iB)C$&7C9Wtk詅DDϠ@8oYr¬#СhߎG}z !ZΝ*GMV6Ħ"L})ה^<^ai7ϋCLrL- ZgΜ~!$(BDʕ+bmqܹ/ޘ$Nc-Ra}"/W*uW_}U5]QQI-4f8SG?fhS_X/X၀*b4m&=i&M;KGKicE#GToK\I]X {*^ v$vi5s,;(iZ#?_2/yݹX!t6?֭^m60^ ^ԅH$Έni=sVfwGk{*R,X[TXe[f)) N@Vd9ӭ}yj}u%z; Ojzҽ5$YYYr`}?uꔸny r:\$$%ይ[ظX9UKC lLPrcs؁K x2寔Tٻj d,C=U{Cfj/O}A4ߚ1>YGRPPsB r^l(2e`.mx(Ő)>m2}tx{;H~,R@&ډlcLwol;*u"bc5i0^BwF`` ӑ S  ${aqL]]pMҨkF#d -άXxqŇ`vhP22Iv 4'g +|pB>Z땧a vJE#'1S""" 9%/)10iG@'D#C1wYGb^v~QI׮@>2ET9F+B~}8X*|#mZ< <3s#LaL&Fh4>N˯;uG *60(d۫ۋ(Wxk qR\\,c2b*!d }7O#rSmgq0n\0x 7?nXθٳg ˄"vB7{(::;DkQiIbctK4n*{5"!ք)shE 3` #d0MX= E+򷝹m8Ĺ3%w/]a(L*!5&:LfPR}2Nń_SPhLetyD-Al50ҭ7nz >hk4~Ȥz ڟ3:v&ddD+bã+_J&R@$0W/sLpLx9scpH?Rk< r8H؄=~؜PmzMU5k:﷢[E=q.n\ $3 PĈ[xKaàa .D\Cd(h]X9W[[*hePPM-կ~.p@O؄8g/OV&dL"D%Qhx˶󷮣B-N՜/iYG -m|Q>[,b ]5hY 2n@wb\}B+¯[1 :fEyDV_ouB50Alԟwy9 0" %aJC1a2!5R@$Di^?VGڎ7.5qlOU'2k7|kѻꂡ[@7t덜X?Eϧ/]L@煀֏c-JMh Eu4M(R2b}2fƏKR@$?" 0 lSһz=lKz5WP =lGvJlh ("2m{ƥ]h5Xe~=%GΠg +uuׄ^FEE *ل 4^FxȏdWdL{`:)x׆g3@U_̐⭧7#`ͪX\27j/?_rF ). c 5%)ob9wѡ Jk}?-mnkYFBͪ$ .R Ww7]WHF|d=\E$IENDB`zyne/Resources/panels.py0000644000175000017500000017275212417524776014676 0ustar tiagotiago# encoding: utf-8 import wx, os, math, copy, random, time from wx.lib.stattext import GenStaticText import Resources.variables as vars from Resources.widgets import * from pyolib._wxwidgets import VuMeter, BACKGROUND_COLOUR from Resources.audio import * import wx.richtext as rt MODULES = { "FM": { "title": "- Frequency Modulation -", "synth": FmSynth, "p1": ["FM Ratio", .25, 0, 4, False, False], "p2": ["FM Index", 5, 0, 40, False, False], "p3": ["Lowpass Cutoff", 2000, 100, 18000, False, True] }, "Additive": { "title": "- Additive Synthesis -", "synth": AddSynth, "p1": ["Transposition", 0, -36, 36, True, False], "p2": ["Spread", 1, 0, 2, False, False], "p3": ["Feedback", 0, 0, 1, False, False] }, "Wind": { "title": "- Wind Synthesis -", "synth": WindSynth, "p1": ["Rand frequency", 1, 0.01, 20, False, True], "p2": ["Rand Depth", .1, .001, .25, False, False], "p3": ["Filter Q", 5, 1, 20, False, False] }, "SquareMod": { "title": "- Square Modulation -", "synth": SquareMod, "p1": ["Harmonics", 10, 1, 40, True, False], "p2": ["LFO Frequency", 1, .001, 20, False, False], "p3": ["LFO Amplitude", 1, 0, 1, False, False] }, "SawMod": { "title": "- Sawtooth Modulation -", "synth": SawMod, "p1": ["Harmonics", 10, 1, 40, True, False], "p2": ["LFO Frequency", 1, .001, 20, False, False], "p3": ["LFO Amplitude", 1, 0, 1, False, False] }, "Pulsar": { "title": "- Pulsar Synthesis -", "synth": PulsarSynth, "p1": ["Harmonics", 10, 1, 20, True, False], "p2": ["Transposition", 0, -36, 36, True, False], "p3": ["LFO Frequency", 1, .02, 200, False, True], }, "Ross": { "title": "- Rossler Attractors -", "synth": Ross, "p1": ["Chaos", 0.5, 0., 1., False, False], "p2": ["Chorus Depth", .001, .001, .125, False, True], "p3": ["Lowpass Cutoff", 5000, 100, 15000, False, True] }, "Wave": { "title": "- Waveform Synthesis -", "synth": Wave, "p1": ["Waveform", 0, 0, 7, True, False], "p2": ["Transposition", 0, -36, 36, True, False], "p3": ["Sharpness", 0.5, 0., 1., False, False], }, "PluckedString": { "title": "- Plucked String Synth -", "synth": PluckedString, "p1": ["Transposition", 0, -48, 0, True, False], "p2": ["Duration", 30, .25, 60, False, False], "p3": ["Chorus Depth", .001, .001, .125, False, True] }, "Reson": { "title": "-- Resonators Synthesis -", "synth": Reson, "p1": ["Transposition", 0, -36, 36, True, False], "p2": ["Chorus Depth", .001, .001, .125, False, True], "p3": ["Lowpass Cutoff", 2000, 100, 10000, False, True] }, "CrossFM": { "title": "- Cross FM Modulation -", "synth": CrossFmSynth, "p1": ["FM Ratio", .25, 0, 4, False, False], "p2": ["FM Index 1", 2, 0, 40, False, False], "p3": ["FM Index 2", 2, 0, 40, False, False], }, "OTReson": { "title": "- Out of tune Resonators -", "synth": OTReson, "p1": ["Transposition", 0, -36, 36, True, False], "p2": ["Detune", .01, .0001, 1, False, True], "p3": ["Lowpass Cutoff", 2000, 100, 10000, False, True] }, "InfiniteRev": { "title": "- Infinite Reverb -", "synth": InfiniteRev, "p1": ["Transposition", 0, -36, 36, True, False], "p2": ["Brightness", 5, 0, 100, True, False], "p3": ["Lowpass Cutoff", 2000, 100, 15000, False, True] }, "Degradation": { "title": "- Wave Degradation -", "synth": Degradation, "p1": ["Bit Depth", 6, 2, 16, False, True], "p2": ["SR Scale", .1, 0.001, .5, False, True], "p3": ["Lowpass Cutoff", 2000, 100, 15000, False, True] }, } LFO_CONFIG = { "p1": ["Speed", 4, .01, 1000, False, True], "p2": ["Waveform", 3, 0, 7, True, False], "p3": ["Jitter", 0, 0, 1, False, False], "p4": ["Sharpness", 0.5, 0, 1, False, False], } LFO_INIT = {"state": False, "params": [.001, .1, .7, 1, .1, 4, 3, 0, .5], "ctl_params": [None, None, None, None, None, None, None, None, None], "shown": False} def get_lfo_init(): return copy.deepcopy(LFO_INIT) class MyFileDropTarget(wx.FileDropTarget): def __init__(self, window): wx.FileDropTarget.__init__(self) self.window = window def OnDropFiles(self, x, y, filename): self.window.GetTopLevelParent().openfile(filename[0]) class HelpFrame(wx.Frame): def __init__(self, parent, id, title, size, subtitle, lines): if vars.constants["PLATFORM"] == "win32": style = wx.DEFAULT_FRAME_STYLE | wx.FRAME_TOOL_WINDOW | wx.FRAME_FLOAT_ON_PARENT | wx.NO_BORDER else: style = wx.FRAME_TOOL_WINDOW | wx.FRAME_FLOAT_ON_PARENT | wx.NO_BORDER wx.Frame.__init__(self, parent=parent, id=id, title=title, size=size, style=style) self.SetBackgroundColour(BACKGROUND_COLOUR) if vars.constants["PLATFORM"] == "darwin": close_accel = wx.ACCEL_CMD else: close_accel = wx.ACCEL_CTRL self.SetAcceleratorTable(wx.AcceleratorTable([(close_accel, ord("W"), vars.constants["ID"]["CloseHelp"])])) self.Bind(wx.EVT_MENU, self.onClose, id=vars.constants["ID"]["CloseHelp"]) self.rtc = rt.RichTextCtrl(self, style=wx.VSCROLL|wx.HSCROLL|wx.NO_BORDER) self.rtc.Bind(wx.EVT_LEFT_DOWN, self.onClose) self.rtc.SetEditable(False) wx.CallAfter(self.rtc.SetFocus) self.rtc.SetBackgroundColour(BACKGROUND_COLOUR) caret = self.rtc.GetCaret() caret.Hide() font = self.rtc.GetFont() font.SetFamily(wx.FONTFAMILY_MODERN) self.rtc.SetFont(font) self.rtc.Freeze() self.rtc.BeginSuppressUndo() self.rtc.BeginParagraphSpacing(0, 20) self.rtc.BeginBold() if vars.constants["PLATFORM"] in ["win32", "linux2"]: self.rtc.BeginFontSize(12) else: self.rtc.BeginFontSize(16) self.rtc.WriteText(subtitle) self.rtc.EndFontSize() self.rtc.EndBold() self.rtc.Newline() for line in lines: self.rtc.WriteText(line) self.rtc.Newline() self.rtc.EndParagraphSpacing() self.rtc.EndSuppressUndo() self.rtc.Thaw() wx.CallAfter(self.SetInitialSize) def onClose(self, evt): self.Destroy() class LFOFrame(wx.MiniFrame): def __init__(self, parent, synth, label, which): wx.MiniFrame.__init__(self, parent, -1, style=wx.FRAME_TOOL_WINDOW | wx.FRAME_FLOAT_ON_PARENT | wx.NO_BORDER) self.parent = parent self.SetMaxSize((230,250)) self.SetSize((230,250)) self.SetBackgroundColour(BACKGROUND_COLOUR) if vars.constants["PLATFORM"] == "darwin": close_accel = wx.ACCEL_CMD else: close_accel = wx.ACCEL_CTRL self.SetAcceleratorTable(wx.AcceleratorTable([ (wx.ACCEL_CTRL, ord("N"), vars.constants["ID"]["New"]), (wx.ACCEL_CTRL, ord("O"), vars.constants["ID"]["Open"]), (wx.ACCEL_CTRL, ord("S"), vars.constants["ID"]["Save"]), (wx.ACCEL_CTRL|wx.ACCEL_SHIFT, ord("S"), vars.constants["ID"]["SaveAs"]), (wx.ACCEL_CTRL, ord("E"), vars.constants["ID"]["Export"]), (wx.ACCEL_CTRL, ord("M"), vars.constants["ID"]["MidiLearn"]), (wx.ACCEL_CTRL, ord(","), vars.constants["ID"]["Prefs"]), (wx.ACCEL_CTRL, ord("G"), vars.constants["ID"]["Uniform"]), (wx.ACCEL_CTRL, ord("K"), vars.constants["ID"]["Triangular"]), (wx.ACCEL_CTRL, ord("L"), vars.constants["ID"]["Minimum"]), (wx.ACCEL_CTRL, ord("J"), vars.constants["ID"]["Jitter"]), (wx.ACCEL_CTRL, ord("Q"), vars.constants["ID"]["Quit"]), (close_accel, ord("W"), vars.constants["ID"]["CloseLFO"]), ])) self.Bind(wx.EVT_MENU, self.parent.onNew, id=vars.constants["ID"]["New"]) self.Bind(wx.EVT_MENU, self.parent.onOpen, id=vars.constants["ID"]["Open"]) self.Bind(wx.EVT_MENU, self.parent.onSave, id=vars.constants["ID"]["Save"]) self.Bind(wx.EVT_MENU, self.parent.onSaveAs, id=vars.constants["ID"]["SaveAs"]) self.Bind(wx.EVT_MENU, self.parent.onExport, id=vars.constants["ID"]["Export"]) self.Bind(wx.EVT_MENU, self.onMidiLearnMode, id=vars.constants["ID"]["MidiLearn"]) self.Bind(wx.EVT_MENU, self.parent.onPreferences, id=vars.constants["ID"]["Prefs"]) self.Bind(wx.EVT_MENU, self.parent.onGenerateValues, id=vars.constants["ID"]["Uniform"], id2=vars.constants["ID"]["Jitter"]) self.Bind(wx.EVT_MENU, self.parent.onQuit, id=vars.constants["ID"]["Quit"]) self.Bind(wx.EVT_MENU, self.onClose, id=vars.constants["ID"]["CloseLFO"]) self.mouseOffset = (0,0) self.which = which self.panel = LFOPanel(self, "LFO", "- %s LFO -" % label, synth, LFO_CONFIG["p1"], LFO_CONFIG["p2"], LFO_CONFIG["p3"], LFO_CONFIG["p4"], which) self.panel.SetPosition((0,0)) self.panel.corner.Bind(wx.EVT_LEFT_DOWN, self.onMouseDown) self.panel.corner.Bind(wx.EVT_LEFT_UP, self.onMouseUp) self.panel.corner.Bind(wx.EVT_MOTION, self.onMotion) self.SetFocus() def onClose(self, evt): self.Hide() def onMidiLearnMode(self, evt): self.parent.onMidiLearnModeFromLfoFrame() def onMouseDown(self, evt): cornerPos = evt.GetPosition() offsetPos = self.panel.corner.GetPosition() self.mouseOffset = (offsetPos[0]+cornerPos[0], offsetPos[1]+cornerPos[1]) self.panel.corner.CaptureMouse() def onMouseUp(self, evt): self.mouseOffset = (0,0) if self.panel.corner.HasCapture(): self.panel.corner.ReleaseMouse() self.SetFocus() def onMotion(self, evt): if self.panel.corner.HasCapture(): pos = wx.GetMousePosition() self.SetPosition((pos[0]-self.mouseOffset[0], pos[1]-self.mouseOffset[1])) def get(self): params = [slider.GetValue() for slider in self.panel.sliders] ctl_params = [slider.midictl for slider in self.panel.sliders] return params, ctl_params def set(self, params, ctl_params): for i, p in enumerate(params): slider = self.panel.sliders[i] slider.SetValue(p) slider.outFunction(p) for i, p in enumerate(ctl_params): slider = self.panel.sliders[i] slider.setMidiCtl(p, False) if i in [5,6,7,8] and p != None: i4 = i - 5 if self.panel.synth._params[self.which] != None: self.panel.synth._params[self.which].assignLfoMidiCtl(p, slider, i4) class LFOButtons(GenStaticText): def __init__(self, parent, label="LFO", synth=None, which=0, callback=None): GenStaticText.__init__(self, parent, -1, label=label, size=(30,12)) self.parent = parent self.synth = synth self.which = which self.state = False self.callback = callback self.SetBackgroundColour(BACKGROUND_COLOUR) self.font, psize = self.GetFont(), self.GetFont().GetPointSize() if vars.constants["PLATFORM"] != "win32": self.font.SetWeight(wx.BOLD) if vars.constants["PLATFORM"] != "darwin": self.font.SetPointSize(psize-2) else: self.font.SetPointSize(psize-5) self.SetFont(self.font) self.Bind(wx.EVT_ENTER_WINDOW, self.hover) self.Bind(wx.EVT_LEAVE_WINDOW, self.leave) self.Bind(wx.EVT_LEFT_DOWN, self.MouseDown) self.SetToolTip(wx.ToolTip("Click to enable, Shift+Click to open controls")) def setState(self, state): self.state = state self.parent.lfo_frames[self.which].panel.synth = self.synth if self.state: self.SetForegroundColour("#0000EE") else: self.SetForegroundColour("#000000") self.callback(self.which, self.state) def hover(self, evt): font, ptsize = self.GetFont(), self.GetFont().GetPointSize() font.SetPointSize(ptsize+1) self.SetFont(font) if self.state: self.SetForegroundColour("#0000CC") else: self.SetForegroundColour("#555555") def leave(self, evt): self.SetFont(self.font) if self.state: self.SetForegroundColour("#0000EE") else: self.SetForegroundColour("#000000") def MouseDown(self, evt): if evt.ShiftDown(): self.parent.lfo_frames[self.which].panel.synth = self.synth pos = wx.GetMousePosition() self.parent.lfo_frames[self.which].SetPosition((pos[0]+5, pos[1]+5)) self.parent.lfo_frames[self.which].Show() return if self.state: self.state = False self.SetForegroundColour("#000000") else: self.state = True self.SetForegroundColour("#0000EE") self.Refresh() self.callback(self.which, self.state) def __del__(self): del self.synth class ServerPanel(wx.Panel): def __init__(self, parent, colour="#DDDDE7"): wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER) self.colour = colour self.SetBackgroundColour(colour) self.SetMinSize((230,500)) self.fileformat = vars.vars["FORMAT"] self.sampletype = vars.vars["BITS"] self.virtualNotePressed = {} self.virtualvoice = 0 self.keyboardShown = 0 self.serverSettings = [] self.fsserver = FSServer() dropTarget = MyFileDropTarget(self) self.SetDropTarget(dropTarget) self.title = wx.StaticText(self, id=-1, label="--- Server Controls ---", pos=(40,5)) self.driverText = wx.StaticText(self, id=-1, label="Output Driver", pos=(15,25)) if vars.vars["AUDIO_HOST"] != "Jack": preferedDriver = vars.vars["OUTPUT_DRIVER"] self.driverList, self.driverIndexes = get_output_devices() self.driverList = [vars.vars["ensureNFD"](driver) for driver in self.driverList] self.defaultDriver = get_default_output() self.popupDriver = wx.Choice(self, id=-1, pos=(13,40), size=(95, 20), choices=self.driverList) if preferedDriver and preferedDriver in self.driverList: driverIndex = self.driverIndexes[self.driverList.index(preferedDriver)] self.fsserver.shutdown() self.fsserver.setOutputDevice(driverIndex) self.fsserver.boot() self.popupDriver.SetStringSelection(preferedDriver) elif self.defaultDriver: self.popupDriver.SetSelection(self.driverIndexes.index(self.defaultDriver)) self.popupDriver.Bind(wx.EVT_CHOICE, self.changeDriver) else: self.popupDriver = wx.Choice(self, id=-1, pos=(13,40), size=(95, 20), choices=[]) self.popupDriver.Disable() preferedInterface = vars.vars["MIDI_INTERFACE"] self.interfaceText = wx.StaticText(self, id=-1, label="Midi interface", pos=(120,25)) self.interfaceList, self.interfaceIndexes = get_midi_input_devices() self.interfaceList = [vars.vars["ensureNFD"](interface) for interface in self.interfaceList] if self.interfaceList != []: self.interfaceList.append("Virtual Keyboard") self.defaultInterface = get_midi_default_input() self.popupInterface = wx.Choice(self, id=-1, pos=(118,40), size=(95, 20), choices=self.interfaceList) if preferedInterface and preferedInterface in self.interfaceList: if preferedInterface != "Virtual Keyboard": interfaceIndex = self.interfaceIndexes[self.interfaceList.index(preferedInterface)] self.fsserver.shutdown() self.fsserver.setMidiInputDevice(interfaceIndex) self.fsserver.boot() else: wx.CallAfter(self.prepareForVirtualKeyboard) self.popupInterface.SetStringSelection(preferedInterface) elif self.defaultInterface: self.fsserver.shutdown() self.fsserver.setMidiInputDevice(self.defaultInterface) self.fsserver.boot() self.popupInterface.SetSelection(self.interfaceIndexes.index(self.defaultInterface)) else: self.popupInterface = wx.Choice(self, id=-1, pos=(118,40), size=(95, -1), choices=["No interface", "Virtual Keyboard"]) self.popupInterface.SetSelection(1) wx.CallAfter(self.prepareForVirtualKeyboard) self.popupInterface.Bind(wx.EVT_CHOICE, self.changeInterface) self.srText = wx.StaticText(self, id=-1, label="Sample Rate", pos=(15,65)) self.popupSr = wx.Choice(self, id=-1, pos=(13,80), size=(95,20), choices=["44100","48000","96000"]) self.popupSr.SetStringSelection(str(vars.vars["SR"])) self.serverSettings.append(self.popupSr.GetSelection()) self.popupSr.Bind(wx.EVT_CHOICE, self.changeSr) self.polyText = wx.StaticText(self, id=-1, label="Polyphony", pos=(120,65)) self.popupPoly = wx.Choice(self, id=-1, pos=(118,80), size=(95,20), choices=[str(i) for i in range(1,21)]) self.popupPoly.SetStringSelection(str(vars.vars["POLY"])) self.serverSettings.append(self.popupPoly.GetSelection()) self.popupPoly.Bind(wx.EVT_CHOICE, self.changePoly) self.bitText = wx.StaticText(self, id=-1, label="Bits", pos=(15,105)) self.popupBit = wx.Choice(self, id=-1, pos=(13,120), size=(95,20), choices=["16","24","32"]) self.popupBit.SetStringSelection(str(vars.vars["BITS"])) self.serverSettings.append(self.popupBit.GetSelection()) self.popupBit.Bind(wx.EVT_CHOICE, self.changeBit) self.formatText = wx.StaticText(self, id=-1, label="Format", pos=(120,105)) self.popupFormat = wx.Choice(self, id=-1, pos=(118,120), size=(95,20), choices=["wav","aif"]) self.popupFormat.SetStringSelection(vars.vars["FORMAT"]) self.serverSettings.append(self.popupFormat.GetSelection()) self.popupFormat.Bind(wx.EVT_CHOICE, self.changeFormat) self.onOffText = wx.StaticText(self, id=-1, label="Audio", pos=(15,145)) self.onOff = wx.ToggleButton(self, id=-1, label="on / off", pos=(14,160), size=(95,20)) self.onOff.Bind(wx.EVT_TOGGLEBUTTON, self.handleAudio) self.recText = wx.StaticText(self, id=-1, label="Record to disk", pos=(120,145)) self.rec = wx.ToggleButton(self, id=-1, label="start / stop", pos=(119,160), size=(95,20)) self.rec.Bind(wx.EVT_TOGGLEBUTTON, self.handleRec) self.textAmp = wx.StaticText(self, id=-1, label="Global Amplitude (dB)", pos=(15, 185), size=(200,20)) self.sliderAmp = ZyneControlSlider(self, -60, 18, 0, pos=(15,200), outFunction=self.changeAmp, backColour=colour) self.serverSettings.append(1.0) self.meter = VuMeter(self) self.meter.SetPosition((15, 225)) self.setAmpCallable() if vars.constants["PLATFORM"] == "darwin": togWidth = 26 togHeight = 16 else: togWidth = 30 togHeight = 20 self.ppEqTitle = wx.StaticText(self, id=-1, label="--- 4 bands equalizer", pos=(50,245)) self.onOffEq = wx.ToggleButton(self, id=-1, label="On", pos=(15,245), size=(togWidth, togHeight)) tog_font, tog_psize = self.onOffEq.GetFont(), self.onOffEq.GetFont().GetPointSize() if vars.constants["PLATFORM"] == "linux2": tog_font.SetPointSize(tog_psize-1) self.onOffEq.SetFont(tog_font) self.onOffEq.Bind(wx.EVT_TOGGLEBUTTON, self.handleOnOffEq) self.knobEqF1 = ControlKnob(self, 40, 250, 100, label='Freq 1', backColour=colour, outFunction=self.changeEqF1) self.knobEqF1.SetPosition((35, 265)) self.knobEqF1.setFloatPrecision(2) self.knobEqF2 = ControlKnob(self, 300, 1000, 500, label='Freq 2', backColour=colour, outFunction=self.changeEqF2) self.knobEqF2.SetPosition((89, 265)) self.knobEqF2.setFloatPrecision(2) self.knobEqF3 = ControlKnob(self, 1200, 5000, 2000, label='Freq 3', backColour=colour, outFunction=self.changeEqF3) self.knobEqF3.SetPosition((143, 265)) self.knobEqF3.setFloatPrecision(2) self.knobEqA1 = ControlKnob(self, -40, 18, 0, label='B1 gain', backColour=colour, outFunction=self.changeEqA1) self.knobEqA1.SetPosition((12, 335)) self.knobEqA2 = ControlKnob(self, -40, 18, 0, label='B2 gain', backColour=colour, outFunction=self.changeEqA2) self.knobEqA2.SetPosition((64, 335)) self.knobEqA3 = ControlKnob(self, -40, 18, 0, label='B3 gain', backColour=colour, outFunction=self.changeEqA3) self.knobEqA3.SetPosition((116, 335)) self.knobEqA4 = ControlKnob(self, -40, 18, 0, label='B4 gain', backColour=colour, outFunction=self.changeEqA4) self.knobEqA4.SetPosition((168, 335)) self.ppCompTitle = wx.StaticText(self, id=-1, label="--- Dyn. compressor", pos=(50,408)) self.onOffComp = wx.ToggleButton(self, id=-1, label="On", pos=(15,408), size=(togWidth, togHeight)) self.onOffComp.SetFont(tog_font) self.onOffComp.Bind(wx.EVT_TOGGLEBUTTON, self.handleOnOffComp) self.knobComp1 = ControlKnob(self, -60, 0, -3, label='Threshold', backColour=colour, outFunction=self.changeComp1) self.knobComp1.SetPosition((12, 428)) self.knobComp2 = ControlKnob(self, 1, 10, 2, label='Ratio', backColour=colour, outFunction=self.changeComp2) self.knobComp2.SetPosition((64, 428)) self.knobComp3 = ControlKnob(self, 0.001, 0.5, 0.01, label='Risetime', backColour=colour, outFunction=self.changeComp3) self.knobComp3.SetPosition((116, 428)) self.knobComp4 = ControlKnob(self, 0.01, 1, .1, label='Falltime', backColour=colour, outFunction=self.changeComp4) self.knobComp4.SetPosition((168, 428)) if vars.constants["PLATFORM"] == "darwin": # reduce font for OSX display objs = [self.driverText, self.popupDriver, self.interfaceText, self.popupInterface, self.srText, self.popupSr, self.polyText, self.popupPoly, self.bitText, self.popupBit, self.formatText, self.popupFormat, self.onOffText, self.onOff, self.recText, self.rec, self.textAmp] font, psize = self.title.GetFont(), self.title.GetFont().GetPointSize() font.SetPointSize(psize-2) for obj in objs: obj.SetFont(font) elif vars.constants["PLATFORM"] == "linux2": # leave StaticText font as is on linux objs = [self.popupDriver, self.popupInterface, self.popupSr, self.popupPoly, self.popupBit, self.popupFormat, self.onOff, self.rec] font, psize = self.title.GetFont(), self.title.GetFont().GetPointSize() font.SetPointSize(psize-1) for obj in objs: obj.SetFont(font) def start(self): self.fsserver.start() def stop(self): self.fsserver.stop() def shutdown(self): self.fsserver.shutdown() def boot(self): self.fsserver.boot() def setAmpCallable(self): self.fsserver.setAmpCallable(self.meter) def setRecordOptions(self, dur, filename): fileformats = {"wav": 0, "aif": 1} if fileformats.has_key(self.fileformat): fileformat = fileformats[self.fileformat] else: fileformat = self.fileformat sampletypes = {16: 0, 24: 1, 32: 3} if sampletypes.has_key(self.sampletype): sampletype = sampletypes[self.sampletype] else: sampletype = self.sampletype self.fsserver.recordOptions(dur, filename, fileformat, sampletype) def reinitServer(self, sliderport, audio, serverSettings, postProcSettings): vars.vars["SLIDERPORT"] = sliderport self.fsserver.shutdown() self.fsserver.reinit(audio=audio) self.fsserver.boot() self.setServerSettings(serverSettings) self.setPostProcSettings(postProcSettings) def getExtensionFromFileFormat(self): return {0: "wav", 1: "aif"}.get(self.fileformat, "wav") def prepareForVirtualKeyboard(self): evt = wx.CommandEvent(10006, self.popupInterface.GetId()) evt.SetString("Virtual Keyboard") self.changeInterface(evt) def resetVirtualKeyboard(self, resetDisplay=True): modules = self.GetTopLevelParent().modules for pit in self.virtualNotePressed.keys(): voice = self.virtualNotePressed[pit] del self.virtualNotePressed[pit] for module in modules: synth = module.synth synth._virtualpit[voice].setValue(pit) synth._trigamp[voice].setValue(0) self.virtualvoice = 0 if resetDisplay: self.keyboard.reset() def retrigVirtualNotes(self): notes = self.keyboard.getNotes() self.resetVirtualKeyboard(resetDisplay=False) modules = self.GetTopLevelParent().modules for note in notes: self.onKeyboard(note) def onKeyboard(self, note): pit = note[0] vel = note[1] if vel > 0 and pit not in self.virtualNotePressed.keys(): for i in range(vars.vars["POLY"]): self.virtualvoice = (self.virtualvoice+1) % vars.vars["POLY"] if self.virtualvoice not in self.virtualNotePressed.values(): break voice = self.virtualNotePressed[pit] = self.virtualvoice elif vel == 0 and pit in self.virtualNotePressed.keys(): voice = self.virtualNotePressed[pit] del self.virtualNotePressed[pit] modules = self.GetTopLevelParent().modules for module in modules: synth = module.synth synth._virtualpit[voice].setValue(pit) synth._trigamp[voice].setValue(vel / 127.) def handleAudio(self, evt): popups = [self.popupDriver, self.popupInterface, self.popupSr, self.popupPoly, self.popupBit, self.popupFormat] menuIds = [vars.constants["ID"]["New"], vars.constants["ID"]["Open"], vars.constants["ID"]["MidiLearn"], vars.constants["ID"]["Export"], vars.constants["ID"]["ExportChord"], vars.constants["ID"]["ExportTracks"], vars.constants["ID"]["ExportChordTracks"], vars.constants["ID"]["Quit"], vars.constants["ID"]["UpdateModules"], vars.constants["ID"]["CheckoutModules"]] if evt.GetInt() == 1: for popup in popups: popup.Disable() for menuId in menuIds: menuItem = self.GetTopLevelParent().menubar.FindItemById(menuId) if menuItem != None: menuItem.Enable(False) self.fsserver.start() else: self.fsserver.stop() for popup in popups: if popup != self.popupDriver or vars.vars["AUDIO_HOST"] != "Jack": popup.Enable() for menuId in menuIds: menuItem = self.GetTopLevelParent().menubar.FindItemById(menuId) if menuItem != None: menuItem.Enable(True) def handleRec(self, evt): if evt.GetInt() == 1: ext = self.getExtensionFromFileFormat() path = os.path.join(os.path.expanduser("~"), "Desktop", "zyne_live_rec.%s" % ext) self.setRecordOptions(dur=-1, filename=path) self.fsserver.recstart() else: self.fsserver.recstop() def changeAmp(self, value): self.fsserver.setAmp(math.pow(10.0, float(value) * 0.05)) def getServerSettings(self): return [self.popupSr.GetSelection(), self.popupPoly.GetSelection(), self.popupBit.GetSelection(), self.popupFormat.GetSelection(), self.sliderAmp.GetValue()] def getPostProcSettings(self): dic = {} dic["EQ"] = [self.onOffEq.GetValue(), self.knobEqF1.GetValue(), self.knobEqF2.GetValue(), self.knobEqF3.GetValue(), self.knobEqA1.GetValue(), self.knobEqA2.GetValue(), self.knobEqA3.GetValue(), self.knobEqA4.GetValue()] dic["Comp"] = [self.onOffComp.GetValue(), self.knobComp1.GetValue(), self.knobComp2.GetValue(), self.knobComp3.GetValue(), self.knobComp4.GetValue()] return dic def setServerSettings(self, serverSettings): popups = [self.popupSr, self.popupPoly, self.popupBit, self.popupFormat] self.setPoly(serverSettings[1]+1) for i, popup in enumerate(popups): val = serverSettings[i] popup.SetSelection(val) evt = wx.CommandEvent(10006, popup.GetId()) evt.SetInt(val) popup.ProcessEvent(evt) amp = serverSettings[4] self.sliderAmp.SetValue(amp) self.sliderAmp.outFunction(amp) self.resetVirtualKeyboard() def setPostProcSettings(self, postProcSettings): eq = postProcSettings["EQ"] comp = postProcSettings["Comp"] widgets = [self.onOffEq, self.knobEqF1, self.knobEqF2, self.knobEqF3, self.knobEqA1, self.knobEqA2, self.knobEqA3, self.knobEqA4] for i, widget in enumerate(widgets): if i == 0: val = eq[i] widget.SetValue(val) evt = wx.CommandEvent(10127, widget.GetId()) evt.SetInt(val) widget.ProcessEvent(evt) else: widget.SetValue(eq[i]) widget.outFunction(eq[i]) widgets = [self.onOffComp, self.knobComp1, self.knobComp2, self.knobComp3, self.knobComp4] for i, widget in enumerate(widgets): if i == 0: val = comp[i] widget.SetValue(val) evt = wx.CommandEvent(10127, widget.GetId()) evt.SetInt(val) widget.ProcessEvent(evt) else: widget.SetValue(comp[i]) widget.outFunction(comp[i]) def setDriverSetting(self, func=None, val=0): if vars.vars["VIRTUAL"]: self.resetVirtualKeyboard() modules, params, lfo_params, ctl_params = self.GetTopLevelParent().getModulesAndParams() postProcSettings = self.getPostProcSettings() self.GetTopLevelParent().deleteAllModules() self.fsserver.shutdown() if func != None: func(val) self.fsserver.boot() self.GetTopLevelParent().setModulesAndParams(modules, params, lfo_params, ctl_params) self.setPostProcSettings(postProcSettings) def changeDriver(self, evt): if vars.vars["AUDIO_HOST"] != "Jack": self.setDriverSetting(self.fsserver.setOutputDevice, self.driverIndexes[evt.GetInt()]) def changeInterface(self, evt): mainFrame = self.GetTopLevelParent() mainFrameSize = mainFrame.GetSize() try: vars.vars["VIRTUAL"] = False if evt.GetString() in self.interfaceList: index = self.interfaceList.index(evt.GetString()) else: index = 999 self.setDriverSetting(self.fsserver.setMidiInputDevice, self.interfaceIndexes[index]) if self.keyboardShown: self.keyboardShown = 0 self.keyboard.reset() mainFrame.SetSize((mainFrameSize[0], mainFrameSize[1]-80)) mainFrame.showKeyboard(False) except IndexError: vars.vars["VIRTUAL"] = True if not self.keyboardShown: self.setDriverSetting() screenRect = self.GetTopLevelParent().GetScreenRect() self.keyboardShown = 1 mainFrame.SetSize((mainFrameSize[0], mainFrameSize[1]+80)) mainFrame.showKeyboard() def changeSr(self, evt): if evt.GetInt() == 0: sr = 44100 elif evt.GetInt() == 1: sr = 48000 else: sr = 96000 self.setDriverSetting(self.fsserver.setSamplingRate, sr) def setPoly(self, poly): vars.vars["POLY"] = poly self.keyboard.setPoly(poly) def changePoly(self, evt): vars.vars["POLY"] = evt.GetInt() + 1 self.keyboard.setPoly(vars.vars["POLY"]) self.setDriverSetting() def changeBit(self, evt): if evt.GetString(): self.sampletype = int(evt.GetString()) def changeFormat(self, evt): self.fileformat = evt.GetInt() ### EQ controls ### def handleOnOffEq(self, evt): if evt.GetInt() == 1: self.onOffEq.SetLabel("Off") self.fsserver.onOffEq(1) else: self.onOffEq.SetLabel("On") self.fsserver.onOffEq(0) def changeEqF1(self, x): self.fsserver.setEqFreq(0, x) def changeEqF2(self, x): self.fsserver.setEqFreq(1, x) def changeEqF3(self, x): self.fsserver.setEqFreq(2, x) def changeEqA1(self, x): self.fsserver.setEqGain(0, math.pow(10.0, x * 0.05)) def changeEqA2(self, x): self.fsserver.setEqGain(1, math.pow(10.0, x * 0.05)) def changeEqA3(self, x): self.fsserver.setEqGain(2, math.pow(10.0, x * 0.05)) def changeEqA4(self, x): self.fsserver.setEqGain(3, math.pow(10.0, x * 0.05)) ### Compressor controls ### def handleOnOffComp(self, evt): if evt.GetInt() == 1: self.onOffComp.SetLabel("Off") self.fsserver.onOffComp(1) else: self.onOffComp.SetLabel("On") self.fsserver.onOffComp(0) def changeComp1(self, x): self.fsserver.setCompParam("thresh", x) def changeComp2(self, x): self.fsserver.setCompParam("ratio", x) def changeComp3(self, x): self.fsserver.setCompParam("risetime", x) def changeComp4(self, x): self.fsserver.setCompParam("falltime", x) def midiLearn(self, state): learnColour = "#EAC1C1" popups = [self.popupDriver, self.popupInterface, self.popupSr, self.popupPoly, self.popupBit, self.popupFormat, self.onOff, self.rec] widgets = [self.knobEqF1, self.knobEqF2, self.knobEqF3, self.knobEqA1, self.knobEqA2, self.knobEqA3, self.knobEqA4, self.knobComp1, self.knobComp2, self.knobComp3, self.knobComp4] if state: self.SetBackgroundColour(learnColour) self.Refresh() self.sliderAmp.setBackgroundColour(learnColour) self.sliderAmp.Refresh() for widget in widgets: widget.setbackColour(learnColour) widget.Refresh() for widget in popups: widget.Disable() self.GetTopLevelParent().menubar.FindItemById(vars.constants["ID"]["Run"]).Enable(False) self.fsserver.startMidiLearn() else: self.SetBackgroundColour(self.colour) self.Refresh() self.sliderAmp.setBackgroundColour(self.colour) self.sliderAmp.Refresh() for widget in widgets: widget.setbackColour(self.colour) widget.Refresh() for widget in popups: widget.Enable() self.GetTopLevelParent().menubar.FindItemById(vars.constants["ID"]["Run"]).Enable(True) self.fsserver.stopMidiLearn() self.setDriverSetting() class BasePanel(wx.Panel): def __init__(self, parent, name, title, synth, p1, p2, p3, from_lfo=False): wx.Panel.__init__(self, parent, style=wx.SUNKEN_BORDER) self.SetMaxSize((230,250)) self.SetBackgroundColour(BACKGROUND_COLOUR) self.from_lfo = from_lfo self.sliders = [] self.labels = [] self.sizer = wx.BoxSizer(wx.VERTICAL) def createAdsrKnobs(self): self.knobSizer = wx.BoxSizer(wx.HORIZONTAL) self.knobDel = ControlKnob(self, 0, 60.0, 0, log=False, label='Delay', outFunction=self.changeDelay) self.knobSizer.Add(self.knobDel, 0, wx.BOTTOM|wx.LEFT|wx.RIGHT, 0) self.knobAtt = ControlKnob(self, 0.001, 60.0, 0.001, log=True, label='Attack', outFunction=self.changeAttack) self.knobSizer.Add(self.knobAtt, 0, wx.BOTTOM|wx.LEFT|wx.RIGHT, 0) self.knobDec = ControlKnob(self, 0.001, 60.0, 0.1, log=True, label='Decay', outFunction=self.changeDecay) self.knobSizer.Add(self.knobDec, 0, wx.BOTTOM|wx.LEFT|wx.RIGHT, 0) self.knobSus = ControlKnob(self, 0.001, 1.0, 0.7, label='Sustain', outFunction=self.changeSustain) self.knobSizer.Add(self.knobSus, 0, wx.BOTTOM|wx.LEFT|wx.RIGHT, 0) self.knobRel = ControlKnob(self, 0.001, 60.0, 1.0, log=True, label='Release', outFunction=self.changeRelease) self.knobSizer.Add(self.knobRel, 0, wx.BOTTOM|wx.LEFT|wx.RIGHT, 0) self.sizer.Add(self.knobSizer, 0, wx.BOTTOM|wx.LEFT, 1) self.sliders.extend([self.knobDel, self.knobAtt, self.knobDec, self.knobSus, self.knobRel]) if vars.constants["PLATFORM"] != "darwin": self.sizer.AddSpacer(3) def createSlider(self, label, value, minValue, maxValue, integer, log, callback, i=-1): if vars.constants["PLATFORM"] == "darwin": height = 14 else: height = 13 text = wx.StaticText(self, id=-1, label=vars.vars["toSysEncoding"](label), size=(200,height)) self.labels.append(text) if vars.constants["PLATFORM"] == "darwin": font, psize = text.GetFont(), text.GetFont().GetPointSize() font.SetPointSize(psize-2) text.SetFont(font) self.sizer.Add(text, 0, wx.LEFT, 5) if self.from_lfo or integer: slider = ZyneControlSlider(self, minValue, maxValue, value, size=(212,14), log=log, integer=integer, outFunction=callback) self.sizer.Add(slider, 0, wx.LEFT|wx.RIGHT, 5) else: hsizer = wx.BoxSizer(wx.HORIZONTAL) slider = ZyneControlSlider(self, minValue, maxValue, value, size=(195,14), log=log, integer=integer, outFunction=callback) button = LFOButtons(self, synth=self.synth, which=i, callback=self.startLFO) lfo_frame = LFOFrame(self.GetTopLevelParent(), self.synth, label, i) self.buttons[i] = button self.lfo_frames[i] = lfo_frame hsizer.Add(slider, 0) hsizer.Add(button, 0, wx.LEFT|wx.ALIGN_CENTER_VERTICAL, 2) self.sizer.Add(hsizer, 0, wx.LEFT|wx.RIGHT, 5) self.sizer.AddSpacer(2) self.sliders.append(slider) return slider def hoverX(self, evt): font = self.close.GetFont() font.SetWeight(wx.BOLD) self.close.SetFont(font) self.close.SetForegroundColour("#555555") def leaveX(self, evt): self.close.SetFont(self.font) self.close.SetForegroundColour("#000000") def MouseDown(self, evt): if not self.from_lfo: for frame in self.lfo_frames: if frame != None: if frame.IsShown(): frame.Hide() self.GetTopLevelParent().deleteModule(self) self.Destroy() else: win = self.GetTopLevelParent() win.Hide() def setBackgroundColour(self, col): self.SetBackgroundColour(col) self.close.SetBackgroundColour(col) self.corner.SetBackgroundColour(col) self.separator.setBackgroundColour(col) if not self.from_lfo: self.info.SetBackgroundColour(col) for slider in self.sliders: try: slider.setBackgroundColour(col) except: slider.setbackColour(col) for but in self.buttons: if but != None: but.SetBackgroundColour(col) self.Refresh() class GenericPanel(BasePanel): def __init__(self, parent, name, title, synth, p1, p2, p3): BasePanel.__init__(self, parent, name, title, synth, p1, p2, p3) self.name, self.synth = name, synth([p1,p2,p3]) self.mute = 1 self.lfo_sliders = [get_lfo_init(), get_lfo_init(), get_lfo_init(), get_lfo_init(), get_lfo_init()] self.buttons = [None, None, None, None, None] self.lfo_frames = [None, None, None, None, None] self.titleSizer = wx.FlexGridSizer(1, 4, 5, 5) self.titleSizer.AddGrowableCol(2) self.titleSizer.SetMinSize((220, -1)) self.close = GenStaticText(self, -1, label="X") self.close.SetBackgroundColour(BACKGROUND_COLOUR) self.close.Bind(wx.EVT_ENTER_WINDOW, self.hoverX) self.close.Bind(wx.EVT_LEAVE_WINDOW, self.leaveX) self.close.Bind(wx.EVT_LEFT_DOWN, self.MouseDown) self.close.SetToolTip(wx.ToolTip("Delete module")) self.info = GenStaticText(self, -1, label="?") self.info.SetBackgroundColour(BACKGROUND_COLOUR) self.info.Bind(wx.EVT_ENTER_WINDOW, self.hoverInfo) self.info.Bind(wx.EVT_LEAVE_WINDOW, self.leaveInfo) self.info.Bind(wx.EVT_LEFT_DOWN, self.MouseDownInfo) self.info.SetToolTip(wx.ToolTip("Show module's infos")) self.title = wx.StaticText(self, id=-1, label=vars.vars["toSysEncoding"](title)) self.corner = GenStaticText(self, -1, label="m/s") self.corner.SetToolTip(wx.ToolTip("Mute / Solo. Click to mute, Shift+Click to solo")) self.corner.SetBackgroundColour(BACKGROUND_COLOUR) self.corner.Bind(wx.EVT_LEFT_DOWN, self.MouseDownCorner) self.corner.Bind(wx.EVT_ENTER_WINDOW, self.hoverCorner) self.corner.Bind(wx.EVT_LEAVE_WINDOW, self.leaveCorner) self.titleSizer.AddMany([(self.close, 0, wx.LEFT, 3), (self.info, 0, wx.LEFT, 3), (self.title, 0, wx.ALIGN_CENTER_HORIZONTAL, 0), (self.corner, 0, wx.RIGHT, 3)]) self.sizer.Add(self.titleSizer, 0, wx.BOTTOM|wx.TOP, 3) self.separator = ZyneStaticLine(self, size=(230, 2)) self.sizer.Add(self.separator, 0, wx.BOTTOM, 3) self.font = self.close.GetFont() if vars.constants["PLATFORM"] == "darwin": ptsize = self.font.GetPointSize() self.font.SetPointSize(ptsize - 2) for obj in [self.close, self.info, self.title, self.corner]: obj.SetFont(self.font) self.createAdsrKnobs() self.sizer.AddSpacer(2) self.sliderAmp = self.createSlider("Amplitude", 1, 0.0001, 2, False, False, self.changeAmp, 0) self.tmp_amplitude = 1 if p1[0] == "Transposition": self.sliderTranspo = self.createSlider(p1[0], p1[1], p1[2], p1[3], p1[4], p1[5], self.changeTranspo, 1) else: self.sliderP1 = self.createSlider(p1[0], p1[1], p1[2], p1[3], p1[4], p1[5], self.changeP1, 1) if p2[0] == "Transposition": self.sliderTranspo = self.createSlider(p2[0], p2[1], p2[2], p2[3], p2[4], p2[5], self.changeTranspo, 2) else: self.sliderP2 = self.createSlider(p2[0], p2[1], p2[2], p2[3], p2[4], p2[5], self.changeP2, 2) if p3[0] == "Transposition": self.sliderTranspo = self.createSlider(p3[0], p3[1], p3[2], p3[3], p3[4], p3[5], self.changeTranspo, 3) else: self.sliderP3 = self.createSlider(p3[0], p3[1], p3[2], p3[3], p3[4], p3[5], self.changeP3, 3) self.sliderPan = self.createSlider("Panning", .5, 0, 1, False, False, self.changePan, 4) if vars.constants["PLATFORM"] != "darwin": self.sizer.AddSpacer(2) self.SetSizerAndFit(self.sizer) def changeP1(self, x): self.synth.set(1, x) def changeP2(self, x): self.synth.set(2, x) def changeP3(self, x): self.synth.set(3, x) def changeTranspo(self, x): self.synth._transpo.value = x def changeDelay(self, x): self.synth.amp.delay = x def changeAttack(self, x): self.synth.amp.attack = x def changeDecay(self, x): self.synth.amp.decay = x def changeSustain(self, x): self.synth.amp.sustain = x def changeRelease(self, x): self.synth.amp.release = x def changeAmp(self, x): self.synth._rawamp.value = x def changePan(self, x): self.synth._panner.set(x) def hoverCorner(self, evt): col = {0: "#0000CC", 1: "#555555", 2: "#FFAA00"}[self.mute] font = self.corner.GetFont() font.SetWeight(wx.BOLD) self.corner.SetFont(font) self.corner.SetForegroundColour(col) def leaveCorner(self, evt): col = {0: "#0000EE", 1: "#000000", 2: "#FF7700"}[self.mute] self.corner.SetFont(self.font) self.corner.SetForegroundColour(col) def MouseDownCorner(self, evt): if evt.ShiftDown(): if self.mute <= 1: self.setMute(2) elif self.mute == 2: self.setMute(1) for module in self.GetTopLevelParent().modules: if module != self: module.setMute(1) else: if self.mute: self.setMute(0) else: self.setMute(1) self.Refresh() def hoverInfo(self, evt): font = self.info.GetFont() font.SetWeight(wx.BOLD) self.info.SetFont(font) self.info.SetForegroundColour("#555555") def leaveInfo(self, evt): self.info.SetFont(self.font) self.info.SetForegroundColour("#000000") def MouseDownInfo(self, evt): if self.synth.__doc__ != None: if vars.constants["PLATFORM"] != "linux2": size = (650, 450) else: size = (500, 450) lines = [vars.vars["ensureNFD"](line) for line in self.synth.__doc__.splitlines(True)] win = HelpFrame(self.GetTopLevelParent(), -1, title="Module info", size=size, subtitle=vars.vars["ensureNFD"]("Info about %s module." % self.name), lines=lines) win.CenterOnParent() win.Show(True) else: wx.LogMessage("No info for %s module." % self.name) def setMute(self, mute): if mute == 2: for module in self.GetTopLevelParent().modules: if module != self: module.setMute(0) self.corner.SetForegroundColour("#FF7700") self.synth._lfo_amp.play() self.sliderAmp.SetValue(self.tmp_amplitude) self.sliderAmp.Enable() elif mute == 1: self.corner.SetForegroundColour("#000000") self.synth._lfo_amp.play() self.sliderAmp.SetValue(self.tmp_amplitude) self.sliderAmp.Enable() elif mute == 0 and self.mute != 0: self.tmp_amplitude = self.sliderAmp.GetValue() self.corner.SetForegroundColour("#0000EE") self.synth._lfo_amp.stop() self.sliderAmp.SetValue(0.0001) self.sliderAmp.Disable() self.mute = mute self.Refresh() def getLFOParams(self): lfo_params = [] for i in range(len(self.buttons)): if self.buttons[i] == None: lfo_params.append(get_lfo_init()) else: if self.lfo_frames[i].IsShown(): offset = self.GetTopLevelParent().GetPosition() pos = self.lfo_frames[i].GetPosition() shown = (pos[0] - offset[0], pos[1] - offset[1]) else: shown = False params, ctl_params = self.lfo_frames[i].get() lfo_params.append({"state": self.buttons[i].state, "params": params, "ctl_params": ctl_params, "shown": shown}) return lfo_params def startLFO(self, which, x): self.lfo_sliders[which]["state"] = x if which == 0: if not x: self.synth._lfo_amp.stop() else: self.synth._lfo_amp.play() else: self.synth._params[which].start_lfo(x) def reinitLFOS(self, lfo_param, ctl_binding=True): self.lfo_sliders = lfo_param for i, lfo_conf in enumerate(self.lfo_sliders): if self.buttons[i] != None: self.lfo_frames[i].panel.synth = self.buttons[i].synth state = lfo_conf["state"] self.startLFO(i, state) self.buttons[i].setState(state) if lfo_conf["shown"]: offset = self.GetTopLevelParent().GetPosition() pos = (lfo_conf["shown"][0] + offset[0], lfo_conf["shown"][1] + offset[1]) self.lfo_frames[i].SetPosition(pos) self.lfo_frames[i].Show() params = lfo_conf["params"] if ctl_binding: ctl_params = lfo_conf["ctl_params"] else: ctl_params = [None] * len(self.lfo_frames[i].panel.sliders) self.lfo_frames[i].set(params, ctl_params) def generateUniform(self): for i, slider in enumerate(self.sliders): if i == 0: continue mini = slider.getMinValue() maxi = slider.getMaxValue() if slider.integer: val = random.randint(mini, maxi) else: if i == 5: val = random.uniform(.25, 1.5) elif i in [1, 2, 4]: val = random.uniform(0.0, 4.0) else: val = random.uniform(mini, maxi) slider.SetValue(val) slider.outFunction(val) for i, button in enumerate(self.buttons): if button != None: state = random.choice([0,0,0,1]) button.setState(state) button.Refresh() if state == 1: for j, slider in enumerate(self.lfo_frames[i].panel.sliders): if j == 0: continue mini = slider.getMinValue() maxi = slider.getMaxValue() if slider.integer: val = random.randint(mini, maxi) else: if j == 6: val = random.uniform(0, 1) val **= 10.0 val *= (maxi - mini) val += mini elif j in [1, 2, 4]: val = random.uniform(0.0, 4.0) else: val = random.uniform(mini, maxi) slider.SetValue(val) slider.outFunction(val) def generateTriangular(self): for i, slider in enumerate(self.sliders): if i == 0: continue mini = slider.getMinValue() maxi = slider.getMaxValue() if slider.integer: v1 = random.randint(mini, maxi) v2 = random.randint(mini, maxi) val = (v1 + v2) / 2 else: if i == 5: val = random.triangular(.25, 1.5) elif i in [1, 2, 4]: val = random.triangular(0.0, 4.0) else: val = random.triangular(mini, maxi) slider.SetValue(val) slider.outFunction(val) for i, button in enumerate(self.buttons): if button != None: state = random.choice([0,0,0,1]) button.setState(state) button.Refresh() if state == 1: for j, slider in enumerate(self.lfo_frames[i].panel.sliders): if j == 0: continue mini = slider.getMinValue() maxi = slider.getMaxValue() if slider.integer: v1 = random.randint(mini, maxi) v2 = random.randint(mini, maxi) val = (v1 + v2) / 2 else: if j == 6: val = random.triangular(0, 1) val **= 10.0 val *= (maxi - mini) val += mini elif j in [1, 2, 4]: val = random.triangular(0.0, 4.0) else: val = random.triangular(mini, maxi) slider.SetValue(val) slider.outFunction(val) def generateMinimum(self): for i, slider in enumerate(self.sliders): if i == 0: continue mini = slider.getMinValue() maxi = slider.getMaxValue() if slider.integer: val = min([random.randint(mini, maxi) for k in range(4)]) else: if i == 5: val = random.uniform(.25, 1.25) elif i in [1, 2, 4]: val = min([random.uniform(0.0, 4.0) for k in range(4)]) else: val = min([random.uniform(mini, maxi) for k in range(4)]) slider.SetValue(val) slider.outFunction(val) for i, button in enumerate(self.buttons): if button != None: state = random.choice([0,0,0,1]) button.setState(state) button.Refresh() if state == 1: for j, slider in enumerate(self.lfo_frames[i].panel.sliders): if j == 0: continue mini = slider.getMinValue() maxi = slider.getMaxValue() if slider.integer: val = min([random.randint(mini, maxi) for k in range(4)]) else: if j == 5: val = min([random.uniform(0, 1) for k in range(8)]) val **= 10.0 val *= (maxi - mini) val += mini elif j in [1, 2, 4]: val = min([random.uniform(0.0, 4.0) for k in range(4)]) else: val = min([random.uniform(mini, maxi) for k in range(4)]) slider.SetValue(val) slider.outFunction(val) def jitterize(self): for i, slider in enumerate(self.sliders): if i == 0: continue mini = slider.getMinValue() maxi = slider.getMaxValue() if not slider.integer: off = random.uniform(.96, 1.04) val = slider.GetValue() * off if val < mini: val = mini elif val > maxi: val = maxi slider.SetValue(val) slider.outFunction(val) for i, button in enumerate(self.buttons): if button != None: if button.state: for j, slider in enumerate(self.lfo_frames[i].panel.sliders): if j == 0: continue mini = slider.getMinValue() maxi = slider.getMaxValue() if slider.integer: off = random.randint(-1, 1) val = slider.GetValue() + off if val < mini: val = mini elif val > maxi: val = maxi else: off = random.uniform(.95, 1.05) val = slider.GetValue() * off if val < mini: val = mini elif val > maxi: val = maxi slider.SetValue(val) slider.outFunction(val) def __del__(self): self.synth.__del__() class LFOPanel(BasePanel): def __init__(self, parent, name, title, synth, p1, p2, p3, p4, which): BasePanel.__init__(self, parent, name, title, synth, p1, p2, p3, from_lfo=True) self.name, self.synth = name, synth self.which = which self.titleSizer = wx.FlexGridSizer(1, 3, 5, 5) self.titleSizer.AddGrowableCol(1) self.titleSizer.SetMinSize((220, -1)) self.close = GenStaticText(self, -1, label="X") self.close.SetBackgroundColour(BACKGROUND_COLOUR) self.close.Bind(wx.EVT_ENTER_WINDOW, self.hoverX) self.close.Bind(wx.EVT_LEAVE_WINDOW, self.leaveX) self.close.Bind(wx.EVT_LEFT_DOWN, self.MouseDown) self.close.SetToolTip(wx.ToolTip("Close window")) self.title = wx.StaticText(self, id=-1, label=vars.vars["toSysEncoding"](title)) bmp = wx.BitmapFromImage(MOVE.GetImage().Rescale(16, 16)) self.corner = ZyneStaticBitmap(self, bitmap=bmp) self.corner.SetToolTip(wx.ToolTip("Move window")) self.titleSizer.AddMany([(self.close, 0, wx.LEFT|wx.TOP, 2), (self.title, 0, wx.ALIGN_CENTER_HORIZONTAL|wx.TOP, 2), (self.corner, 0, wx.RIGHT, 5)]) self.sizer.Add(self.titleSizer, 0, wx.BOTTOM|wx.TOP, 2) self.sizer.Add(ZyneStaticLine(self, size=(226, 2)), 0, wx.BOTTOM, 3) self.font = self.close.GetFont() if vars.constants["PLATFORM"] == "darwin": ptsize = self.font.GetPointSize() self.font.SetPointSize(ptsize - 2) for obj in [self.close, self.title]: obj.SetFont(self.font) self.createAdsrKnobs() self.sliderAmp = self.createSlider("Amplitude", .1, 0, 1, False, False, self.changeAmp, -1) self.sliderP1 = self.createSlider(p1[0], p1[1], p1[2], p1[3], p1[4], p1[5], self.changeP1) self.sliderP2 = self.createSlider(p2[0], p2[1], p2[2], p2[3], p2[4], p2[5], self.changeP2) self.sliderP3 = self.createSlider(p3[0], p3[1], p3[2], p3[3], p3[4], p3[5], self.changeP3) self.sliderP4 = self.createSlider(p4[0], p4[1], p4[2], p4[3], p4[4], p4[5], self.changeP4) self.SetSizerAndFit(self.sizer) def __del__(self): del self.synth def changeP1(self, x): if self.which == 0: self.synth._params[self.which].setSpeed(x) else: self.synth._params[self.which].lfo.setSpeed(x) def changeP2(self, x): if self.which == 0: self.synth._params[self.which].setType(x) else: self.synth._params[self.which].lfo.setType(x) wave = {0: "Ramp", 1: "Sawtooth", 2: "Square", 3: "Triangle", 4: "Pulse", 5: "Bipolar Pulse", 6: "Sample and Hold", 7: "Modulated Sine"}[x] self.labels[2].SetLabel(vars.vars["ensureNFD"]("Waveform - %s" % wave)) def changeP3(self, x): if self.which == 0: self.synth._params[self.which].setJitter(x) else: self.synth._params[self.which].lfo.setJitter(x) def changeP4(self, x): if self.which == 0: self.synth._params[self.which].setSharp(x) else: self.synth._params[self.which].lfo.setSharp(x) def changeDelay(self, x): if self.which == 0: self.synth.amp.delay = x else: self.synth._params[self.which].lfo.amp.delay = x def changeAttack(self, x): if self.which == 0: self.synth.amp.attack = x else: self.synth._params[self.which].lfo.amp.attack = x def changeDecay(self, x): if self.which == 0: self.synth.amp.decay = x else: self.synth._params[self.which].lfo.amp.decay = x def changeSustain(self, x): if self.which == 0: self.synth.amp.sustain = x else: self.synth._params[self.which].lfo.amp.sustain = x def changeRelease(self, x): if self.which == 0: self.synth.amp.release = x else: self.synth._params[self.which].lfo.amp.release = x def changeAmp(self, x): if self.which == 0: self.synth._params[self.which].setAmp(x) else: self.synth._params[self.which].lfo.setAmp(x) zyne/Resources/splash.py0000644000175000017500000000565212417524776014700 0ustar tiagotiago#!/usr/bin/env python # encoding: utf-8 import wx, sys, os import Resources.variables as vars def GetRoundBitmap(w, h, r=10): maskColour = wx.Colour(0,0,0) shownColour = wx.Colour(5,5,5) b = wx.EmptyBitmap(w,h) dc = wx.MemoryDC(b) dc.SetBrush(wx.Brush(maskColour)) dc.DrawRectangle(0,0,w,h) dc.SetBrush(wx.Brush(shownColour)) dc.SetPen(wx.Pen(shownColour)) dc.DrawCircle(w/2,h/2,w/2) dc.SelectObject(wx.NullBitmap) b.SetMaskColour(maskColour) return b def GetRoundShape(w, h, r=10): return wx.RegionFromBitmap(GetRoundBitmap(w,h,r)) class ZyneSplashScreen(wx.Frame): def __init__(self, parent, img, mainframe): display = wx.Display(0) size = display.GetGeometry()[2:] wx.Frame.__init__(self, parent, -1, "", pos=(-1, size[1]/6), style = wx.FRAME_SHAPED | wx.SIMPLE_BORDER | wx.FRAME_NO_TASKBAR | wx.STAY_ON_TOP) self.Bind(wx.EVT_PAINT, self.OnPaint) self.mainframe = mainframe self.bmp = wx.Bitmap(os.path.join(img), wx.BITMAP_TYPE_PNG) self.w, self.h = self.bmp.GetWidth(), self.bmp.GetHeight() self.SetClientSize((self.w, self.h)) if wx.Platform == "__WXGTK__": self.Bind(wx.EVT_WINDOW_CREATE, self.SetWindowShape) else: self.SetWindowShape() dc = wx.ClientDC(self) dc.DrawBitmap(self.bmp, 0, 0, True) self.fc = wx.FutureCall(3500, self.OnClose) self.Center(wx.HORIZONTAL) if sys.platform == 'win32': self.Center(wx.VERTICAL) wx.CallAfter(self.Show) def SetWindowShape(self, *evt): r = GetRoundShape(self.w, self.h) self.hasShape = self.SetShape(r) def OnPaint(self, evt): w,h = self.GetSize() dc = wx.PaintDC(self) dc.SetPen(wx.Pen("#000000")) dc.SetBrush(wx.Brush("#000000")) dc.DrawRectangle(0,0,w,h) dc.DrawBitmap(self.bmp, 0,0,True) dc.SetTextForeground("#000000") font = dc.GetFont() if vars.constants["PLATFORM"] == "win32": pass else: font.SetFaceName("Monaco") font.SetPixelSize((18,18)) dc.SetFont(font) dc.DrawLabel("Modular Soft Synthesizer", wx.Rect(20, 230, 400, 18), wx.ALIGN_LEFT) if vars.constants["PLATFORM"] == "win32": pass else: font.SetPixelSize((15,15)) dc.SetFont(font) dc.DrawLabel(u"Olivier Bélanger", wx.Rect(0, 345, 400, 15), wx.ALIGN_CENTER) dc.DrawLabel("iACT, %s" % vars.constants["YEAR"], wx.Rect(0, 360, 400, 15), wx.ALIGN_CENTER) dc.DrawLabel("v. %s" % vars.constants["VERSION"], wx.Rect(0, 375, 400, 15), wx.ALIGN_CENTER) def OnClose(self): self.mainframe.Show() self.Destroy() if __name__ == '__main__': app = wx.PySimpleApp() frame = ZyneSplashScreen(None, img="ZyneSplash.png") app.MainLoop()zyne/Resources/preferences.py0000644000175000017500000002265312417524776015707 0ustar tiagotiago#!/usr/bin/env python # encoding: utf-8 import wx, os, codecs import Resources.variables as vars from Resources.audio import get_output_devices, get_midi_input_devices class PreferencesDialog(wx.Dialog): def __init__(self): wx.Dialog.__init__(self, None, wx.ID_ANY, 'Zyne Preferences') self.paths = ["CUSTOM_MODULES_PATH", "EXPORT_PATH"] self.drivers = ["OUTPUT_DRIVER", "MIDI_INTERFACE"] self.ids = {"CUSTOM_MODULES_PATH": 10001, "EXPORT_PATH": 10002, "OUTPUT_DRIVER": 20001, "MIDI_INTERFACE": 20002} self.prefs = dict() self.checkForPreferencesFile() self.createWidgets() def createWidgets(self): btnSizer = wx.StdDialogButtonSizer() itemSizer = wx.FlexGridSizer(2,2,0,50) driverSizer = wx.BoxSizer(wx.VERTICAL) pathSizer = wx.BoxSizer(wx.VERTICAL) rowSizer = wx.BoxSizer(wx.HORIZONTAL) mainSizer = wx.BoxSizer(wx.VERTICAL) message = wx.StaticText(self, label="* Changes will be applied on next launch *") mainSizer.Add(message, 0, wx.TOP|wx.LEFT|wx.ALIGN_CENTER_HORIZONTAL, 10) font, entryfont, pointsize = message.GetFont(), message.GetFont(), message.GetFont().GetPointSize() font.SetWeight(wx.BOLD) if vars.constants["PLATFORM"] in ["win32", "linux2"]: entryfont.SetPointSize(pointsize-1) else: font.SetPointSize(pointsize-1) entryfont.SetPointSize(pointsize-2) if vars.constants["PLATFORM"] == "linux2": host_choices = ["Portaudio", "Jack"] elif vars.constants["PLATFORM"] == "darwin": if vars.constants["OSX_BUILD_WITH_JACK_SUPPORT"]: host_choices = ["Portaudio", "Jack"] else: host_choices = ["Portaudio"] else: host_choices = ["Portaudio"] host = self.prefs["AUDIO_HOST"] lbl = wx.StaticText(self, label=vars.constants["VAR_PREF_LABELS"]["AUDIO_HOST"]) lbl.SetFont(font) driverSizer.Add(lbl, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 10) cbo = wx.ComboBox(self, value=host ,size=(100,-1), choices=host_choices, style=wx.CB_DROPDOWN|wx.CB_READONLY, name="AUDIO_HOST") driverSizer.AddSpacer((-1,5)) driverSizer.Add(cbo, 0, wx.LEFT|wx.RIGHT|wx.BOTTOM, 8) for key in self.drivers: lbl = wx.StaticText(self, label=vars.constants["VAR_PREF_LABELS"][key]) lbl.SetFont(font) driverSizer.Add(lbl, 0, wx.LEFT|wx.RIGHT, 10) ctrlSizer = wx.BoxSizer(wx.HORIZONTAL) txt = wx.TextCtrl(self, size=(360,-1), value=self.prefs[key], name=key) ctrlSizer.Add(txt, 0, wx.ALL|wx.EXPAND, 5) but = wx.Button(self, id=self.ids[key], label="Choose...") but.Bind(wx.EVT_BUTTON, self.getDriver, id=self.ids[key]) ctrlSizer.Add(but, 0, wx.ALL, 5) driverSizer.Add(ctrlSizer, 0, wx.BOTTOM|wx.LEFT|wx.RIGHT, 5) for key in vars.constants["VARIABLE_NAMES"]: val = self.prefs[key] if key not in self.paths and key not in self.drivers and key != "AUDIO_HOST": lbl = wx.StaticText(self, label=vars.constants["VAR_PREF_LABELS"][key]) lbl.SetFont(font) itemSizer.Add(lbl, 0, wx.ALIGN_CENTER_VERTICAL|wx.LEFT|wx.RIGHT, 10) if vars.constants["VAR_CHOICES"].has_key(key): default = val choices = vars.constants["VAR_CHOICES"][key] cbo = wx.ComboBox(self, value=val,size=(100,-1), choices=choices, style=wx.CB_DROPDOWN|wx.CB_READONLY, name=key) itemSizer.Add(cbo, 0, wx.TOP|wx.LEFT|wx.RIGHT, 5) else: txt = wx.TextCtrl(self, size=(100,-1), value=val, name=key) itemSizer.Add(txt, 0, wx.TOP|wx.LEFT|wx.RIGHT, 5) for key in self.paths: if key == "CUSTOM_MODULES_PATH": func = self.getPath elif key == "EXPORT_PATH": func = self.getPath lbl = wx.StaticText(self, label=vars.constants["VAR_PREF_LABELS"][key]) lbl.SetFont(font) pathSizer.Add(lbl, 0, wx.LEFT|wx.RIGHT, 10) ctrlSizer = wx.BoxSizer(wx.HORIZONTAL) txt = wx.TextCtrl(self, size=(360,-1), value=self.prefs[key], name=key) txt.SetFont(entryfont) ctrlSizer.Add(txt, 0, wx.ALL|wx.EXPAND, 5) but = wx.Button(self, id=self.ids[key], label="Choose...") but.Bind(wx.EVT_BUTTON, self.getPath, id=self.ids[key]) ctrlSizer.Add(but, 0, wx.ALL, 5) pathSizer.Add(ctrlSizer, 0, wx.BOTTOM|wx.LEFT|wx.RIGHT, 5) saveBtn = wx.Button(self, wx.ID_OK, label="Save") saveBtn.SetDefault() saveBtn.Bind(wx.EVT_BUTTON, self.onSave) btnSizer.AddButton(saveBtn) cancelBtn = wx.Button(self, wx.ID_CANCEL) btnSizer.AddButton(cancelBtn) btnSizer.Realize() mainSizer.AddSpacer((-1,5)) mainSizer.Add(driverSizer, 0, wx.EXPAND) mainSizer.Add(itemSizer, 0, wx.EXPAND) mainSizer.AddSpacer((-1,5)) mainSizer.Add(pathSizer, 0, wx.EXPAND) mainSizer.Add(wx.StaticLine(self, size=(480,1)), 0, wx.TOP|wx.BOTTOM, 2) mainSizer.Add(btnSizer, 0, wx.ALL | wx.ALIGN_RIGHT, 5) self.SetSizer(mainSizer) self.SetClientSize(self.GetBestSize()) def getDriver(self, evt): id = evt.GetId() for name in self.ids.keys(): if self.ids[name] == id: break if name == "OUTPUT_DRIVER": driverList, driverIndexes = get_output_devices() msg = "Choose an output driver..." elif name == "MIDI_INTERFACE": driverList, driverIndexes = get_midi_input_devices() driverList.append("Virtual Keyboard") msg = "Choose a Midi interface..." driverList = [vars.vars["ensureNFD"](driver) for driver in driverList] widget = wx.FindWindowByName(name) dlg = wx.SingleChoiceDialog(self, message=msg, caption="Driver Selector", choices=driverList, style=wx.CHOICEDLG_STYLE) if dlg.ShowModal() == wx.ID_OK: selection = dlg.GetStringSelection() widget.SetValue(selection) else: pass dlg.Destroy() def getPath(self, evt): id = evt.GetId() for name in self.ids.keys(): if self.ids[name] == id: break if name == "EXPORT_PATH": title = "Choose the directory where to save the exported samples" else: title = "Choose the directory where you saved your custom module files" widget = wx.FindWindowByName(name) dlg = wx.DirDialog(self, title, os.path.expanduser("~"), style=wx.DD_DEFAULT_STYLE) if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() widget.SetValue(path) else: pass dlg.Destroy() def checkForPreferencesFile(self): preffile = os.path.join(os.path.expanduser("~"), ".zynerc") if os.path.isfile(preffile): with codecs.open(preffile, "r", encoding="utf-8") as f: lines = f.readlines() pref_rel_version = int(lines[0].split()[3].split(".")[1]) cur_rel_version = int(vars.constants["VERSION"].split(".")[1]) if lines[0].startswith("### Zyne"): if pref_rel_version != cur_rel_version: print "Zyne preferences out-of-date, using default values." lines = vars.constants["DEFAULT_PREFS"].splitlines() else: print "Zyne preferences out-of-date, using default values." lines = vars.constants["DEFAULT_PREFS"].splitlines() else: lines = vars.constants["DEFAULT_PREFS"].splitlines() for line in lines[1:]: line = line.strip() if line: sline = line.split("=") if sline[0].strip() == "AUDIO_HOST" and vars.constants["PLATFORM"] == "darwin" and not vars.constants["OSX_BUILD_WITH_JACK_SUPPORT"] and sline[1].strip() in ["Jack", "Coreaudio"]: self.prefs[sline[0].strip()] = vars.vars["ensureNFD"]("Portaudio") else: self.prefs[sline[0].strip()] = vars.vars["ensureNFD"](sline[1].strip()) def onSave(self, event): preffile = os.path.join(os.path.expanduser("~"), ".zynerc") with codecs.open(preffile, "w", encoding="utf-8") as f: f.write(u"### Zyne version %s preferences ###\n" % vars.constants["VERSION"]) for name in vars.constants["VARIABLE_NAMES"]: widget = wx.FindWindowByName(name) if isinstance(widget, wx.ComboBox): value = widget.GetValue() choices = widget.GetItems() else: value = widget.GetValue() try: f.write(u"%s = %s\n" % (name, value)) except UnicodeEncodeError: try: f.write(u"%s = " % name + vars.vars["ensureNFD"](value) + u"\n") except: f.write(u'%s = ""\n' % name) f.write(u"LAST_SAVED = %s\n" % vars.vars["LAST_SAVED"]) self.EndModal(0) zyne/Resources/__init__.py0000644000175000017500000000000012417524776015123 0ustar tiagotiagozyne/Resources/default.zy0000644000175000017500000000223012417524776015031 0ustar tiagotiago{'lfo_params': [[{'state': False, 'params': [0, 0.001, 0.1, 0.7, 1, 0.1, 4, 3, 0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], "shown": False}, {'state': False, 'params': [0, 0.001, 0.1, 0.7, 1, 0.1, 4, 3, 0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], "shown": False}, {'state': False, 'params': [0, 0.001, 0.1, 0.7, 1, 0.1, 4, 3, 0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], "shown": False}, {'state': False, 'params': [0, 0.001, 0.1, 0.7, 1, 0.1, 4, 3, 0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], "shown": False}, {'state': False, 'params': [0, 0.001, 0.1, 0.7, 1, 0.1, 4, 3, 0, 0.5], 'ctl_params': [None, None, None, None, None, None, None, None, None, None], "shown": False}]], 'params': [[0, 0.001, 0.1, 0.7, 1.0, 1.0, 0.25, 5.0, 2000.0000000000002, 0.5]], 'ctl_params': [[None, None, None, None, None, None, None, None, None, None]], 'postproc': {'Comp': [False, -3.0, 2.0, 0.010000000000000002, 0.1], 'EQ': [False, 100.0, 500.0, 2000.0, 0.0, 0.0, 0.0, 0.0]}, 'modules': [('FM', 1)], 'server': [0, 4, 1, 0, 0.0]} zyne/Resources/zyneicon.ico0000644000175000017500000072153312417524776015371 0ustar tiagotiago(600h%* 0u3(]5Z600$ ̟th< v (+`` 3HH Tk@@ (B00 %_  Ä k hPNG  IHDR\rf pHYs+ IDATx]mUW{gLYeCbKFc}?|u5\cG@D@D={B?u_yp } @g?*d!ף/!miba?0Dx Iq 9x@ao~/`AL0A\49"ioQع6Olt` ߒgPM~Xq@8~= x68ȁk٤pČ Wퟰy ᅭg C2r FB;(unc!| ozg |> ͟@  G@088}Dv'>aDV)6UR.#:&;?@,VavIa ,no&7*  7<%C௣oK8A&!;& ?`4;tP糁M l~V: [ >%Nvc_jHz!B o?,R< &qdǝX|7nZ|u)P֌"Kaj4ɕW 5fyrWKY@a3a2 (eJU;&ӈ?Ox3 o.+vdھv$]ʁ6߿c\1R0\M9+[`Nz5fM߿p}tq#}6//Õy2g-}<5fP+gS%JXaJK@XFpZ(@5 ~: `oXs wE` !Ssק[m9p<ւ+8XH4͌d$:o8VE<aF[mkԨ6f+_w cDa ' Wr0f>/1%Oy{Dj@G|B|pvչyUX#^T&\4|mȆS ?j@G^hwm VJs,-!-%(;! 98Mzd_ծlZ:X@6uICÙ~wO {vja 3Ә>ԥQ `D'Z1jl\5xl@W*NZj%Rt3Sc—5Ӌ\,꜉fo(\HFA`,nLԍLZ-yZ)ZU 9=b)/tWuc)/@֩X$͒,9nd ^zМue /8pD{LE=nNA}-Imf- ք?/n*-)#w !eJ$`y8H9+`Iį +$QL;VC룽m/c7?U`BaA$Tx`Ҁ~;-[Rp"iŸRC6ܶbpU7ٛ_BL6wdK65JډdV1$wrO ฦ"Q 56!jG4juZ a q&gD!@:\4bjsܖfoVFFF6tԳoҗH @D@&ʴod`8BN)j gЧ^}Dxs*PBT@ wc~0UБ`F{wP=iT\hQ pMW&p lGu)!q<7Î iH\N(&}5W G1x~ dQخTy~܄AFxƬ !uK塡B ZO@Cl&QPhtg8h_g̡XY֘^I<9(\ U PQkɾ"Ȗ|0GՆ@ǧQYzbߋ4fAKW]}/5m&] (<[rRi*ef̜Q'Cr hy@J[>%ϩ]RW=XrY9 cK6; _53@Qs)*ڇUkHq' zbpn&t[4)$Jl,c$- iVm9<+|HF 15 : aH6{c.}jaw$  /DabMIg5AkZK_cK& ]ߙ+ 40_)"=*uz@_aϵdK54Q4&m5) 1 CH3ZJ-&s \˪lQ(jzSA hqWkhuu /@/~J!ZҔgEi3Bb%Mj R^+;Xi$-& V<wCph>JUiRkFbޖʳˉ Emmگ]hbm&g]{,Y=\H^ڿ͹L=Ɍ,ʊ2,8}_lo;ah(SD 7J~&%J(2"PO%&h"vf;0 "\xG̤TidOikXTxFxl&`d&@'2JLcTX5 Pg c6faF1P{{k)9L&h,X'U7>ٞ \wR[_Y?vFYǗY MGoٽ1DŽ2YW# B]j /[6jil6'桢X W Р A `ʻiB$ ;% xd*pD+ɣZjCJ-֜Ѕ|;[ L@+o"cAHZIIhCY Ƚk$J8= );ES1IQV " p[6:=̀VZ -iPPH6R z'}lG mҜ/fJU-w|=Vln#xN}E[>;HZa%-})3D||8AW`yf6^Bdp0$;ȱſ*ݜ_(w8 &WRw[ʾ4'\ PF*W3A<|ӔY?r< X  iq^]ۮ%s5ij]`VڋegKM= 4Jp2j 5%'}ح"-z 4W^~8&zo$O;" ͚lN Çb~b[=;8 hcw}Ħ$r3`~&@D[k{랮6Ⱥ;UP 3ozt ,? Lh ږx՜0rL@jskVf`10xShdT xe6\4R˫ uOMqH@fA∇dkVRuX8PGR-0>=FVR *ks+?Ҷ{Fq?T@K1)ޣL5 !$sNфqB٢mg`ʻ@Y=KAj>fT܉!!ȯB8H4-w~.QvLj>?}IwP\M(ZL$>l(q{CūـW@r[OX0K[tí1{\;K%;t#JhQ_KrN+L,BuB7gCisKCJN+,"Yod-v$DZɨ z _ ՎoFn ]}x@3z5ӂJLk1ʭف+3z QTr2-`iLL.u7՝qn "v3;"+<ÀӗI1i{=4yAr$b_66H0l2CJƩ2p@gb#-22uz7_Ll@Ʒ ,`u;Xj5_v}ϩm5sv#˕|͑.;wWfp<"C#.Un]e4QF)8H:β֪6&Vlr`pem#z$[;e{y"]o8] X XeLQ-if I"V3M{jSru7{ώGCDi!5=;WЪmXQlB8Fc7ÒCF%[~؆|RM{@<ǑC CSi-%4@i Eg^| wẂpkA<BÞԀu6!GZLCr9p! &\:-Xf.haZIo{nO'sn94,Nu}l!:Iɸ9JV\hHH3^:v,:ͳfӬPBVK?^][*wdHb% Af?AX$]MH5 xTA0`E8?{Sf)UV=t, jeU$zC6D piKh^-Ÿv&`ޘ0?4Y*Y;`9PZW@ ǰI`t*y%A* (5Pj9;:bٛWߣ!XV0HVviIlqh9mL,6ce}hGASW`ۂO/7 jbTVo9!GhlIt6ϤyLjnp(8L6' c7/P>ǭ)a/b!׋b)wu0p<tl{L'GӋ{e޼h!IUN@zZ!ܖ0`O&s XXwg1J:=#Z|Gb y@Uxb ``Y x~5vZmadŕI8l\W 4Gkw[ƃ'fϠr?ڻt4$ٜAi h@ @4b_jwAٵe3t6h^# 0"#F"?"r" @38>Eg`u٫~>'D;yO{{a$"x<8;JJri,Q+g{ΕIΣ=3PHWF>W < &4q=`v#`EOW>2rB־m3;9++dh (hXY^/@Q Bd3h%4KΓ^>#=Di ڞs)hV3z^S!fڏ! Π1jigmG/>Z{:p=10b c׸ٙkAZ]|;G??Kde=o@ᑍ/ڍk`p?3px Ys з->$X'`sQ%~F1 0 KVT;ܱid4V?B&s(kU)@90zg~`>W6SiSׯ PvGs_ؖ>g", R(i$C ,ŭ>Wk *} ^S$ O &t?Eq2aɗ@nL/bj(w6 9 RN?s Se S@`1sM^f/];SNi2ω!gL寸ܫ;>)Eؘ*l<ݝ[-]6/6 !z0fuL -pK.#4s~@>+`ME 8[SWen˜ P 9ҚEc VNi 7R:HO+eʥx?gw,5X+Xwͽݑ3!/;ճ#de~bi&fz؛5a;rH# Iw. ;? }}!w7,v/2 P }#bo[tASo/x#CԜ8sWx¿)Yq"ϧ#{ WQfUZtlYj5S'.!<q\z´ipVruiFs6m {4:f?I>M zwQC" ђٗ>>8MW tb`^"<{Y3=1 y۰+8`,V'Zm <3Zg') PgO~a?5XS݅% Uނ2Ef x>xKMJ|aNRg39{?4.7 b&l=ƽpfiOY雹FN`Jh ~~>v?ҿK:֩ysn+wn9ID܀Wc ,\ܗK%*H?-3&wA_43>bek `1h[WpnqFg uk`!94ȸAk_~.Kwm/L&(!8e> `q_ݟ8HTn`/ z>yi^ʽLA6m (\}rY}ynV R āFߵZtYX$-zq΀a!BD?hg}4(?x&tPk-k=ʺˏ,@AD#//t M {֙p0/?Qx Nޭw  MEߜض ^g>>5a2e{adzD Yx0-&P~_wI?>L-fX2/ ?c[y]8_,|`w$~S__g1Kx3$& 1̨tJgN!=A>kp W\Ki2'3 ^ *?D&@DcpL_,xzTeY05E$0tu,ZyxѰgYy*ZqU&lxP&]<%WGa@}|ZX@ k*RùϠY ?,dkҘU>BO'?Q$='7O 7 n=p\|:;F?WUZsϫBUj҃F!9wdU~O/ zt B_}WGُ._#gx MNyu6pq\~]YUh{ lAH^{m=%OazXv._((ߖY-xP-}(\[ol5 0',C?  " D/@٫7L>L<>שʁ.,zWSru*I,~}k̵n|O AlI@|n!dS lΥtYJ?\-@ ( ylH<(mm&_tu>EDKkrȘ@T4y i{\'v!n|5"z@Q { ` xǔҀ(j"pᯚ>{`%O|gN4xqi iAKJ܌LESTy%m٩P4ݲ y^>լ*rʵF̓i8S}e1x65ʻp߬f?]xRZi0!KC](XaX-f&Y@WPF!dC4s 箻@'c"3pfŜ|X|{!ذɄXM~KzL 0Bd HHى=f);Lጢ\ -Z9GIy{ ,=f< 'o5Ū_# E++jg?l\,.>!Wƙ s o>4.0 ˟sZ2lG~ϔ> Py.mS_|w fa?IENDB`(0`rWwpwxwpwpwwpwpxpxpwppwpppxxxwpwppppxpxpxppppppppppxxppppppppwpppxpxpxxppppwppwprwwwppw???( @wwwwxppppxppwpppppppspxpxppw@xpwwvw??(0 xppppppapp`pppwvp??( xwwxwwxwxwwwpxwpPNG  IHDR\rf pHYs+ IDATx{x[ՙ7˖-Y-+;|cLb!P NoIr:i;|4aZqPJr+ivJ5PHN$qlVYmioIڑ|[K}$²Zo߫@A RKStGpqȋN!/$b` Thz$h4jgt((jAii)6+v^wԗh 򑗺:A8p.s*$I0Gd=A+:QQ k𗛯.D F$N 78 #"ϫijB?Zd8E`sbc:\( ӥPTo/!>?,^=y?sR /,4#% W\zyhd/NAF/(HͧdBqq1v;l6f3l6 6E"0 #Ga([E@0G^`XfX4Ujl߾8Ńo'FGb3Lz,DY˲DMu **,LEtbg*=q " ʯ[,b1t:PMwKӿNCUC-_/AȒ |Gp.8Px֟ns^ DKK e% wb>/M;& X,`fukV#CiYO@$ 7Dqq1j_.yȡĉc PxQQusXn-^k׭E$'>a6pXNAeU@0O8qxeAejj }N 'O^5u 0(@kK0ۏoV%NCkk+֮[V477,WKK ZZZ080Qa6 dXLga7ѓLfD˰,a+EWW;=EQDT8(r/VN'N'nf.ttt͚U$ ýio?uD&ٛ @P(Gx<}h4*̥Vltlق/I\.l~X,1>9A P0hkkC[[A@gg'8a^8r{%oK:ٗ' O!ڰm68άgrb!1 aCPRW3"h ;v;v_K/Yc+R(CÉV~ (ryod?HSo.gز*|CӚG FYRMy|nB{{;N^0(--MRc0ġCW]uU 8qs{ƟFo6g*kl؈hDj>?ݪ<N*[iz=<ĕ7[ ASyDh^{9>ן˲9]l6X,a ; " d2AvAYIpwa͛q뭷'"$^ۿNAt:BЮ.ɚ)X0 sѼ0<g1>> fX;č7m[j pJG[/;1g{4MΊ b1tdFuPŬqg?>>>HVS={[o%v~\u$L&^{Ӕ=U^/A6+2]ψaӋR釈.f!v5&DY;$"V7`xwGLzĉ7;d5Z7+(l6#{qBY'!&.s12!=v]5d\9~.(efܾկoݻ;߿ awe .*zό_Wk.ttt`7J`*1¡x0Ya,ꨮk`p^&If p:O駟O?p8V;;+j gľ&z4]6l??5MsJvx*A?_\pGOfKAj v׮]hoow=qR?k?3ob&?vm4 KR 0zf4͖ͤ-f, ә:Cө?F5g#?{{3? `hٻz&G?ɊjK9見QoPu18%)"DX h$5zfϘ>?",5ff];ڊ~2 xw{e]3lPb趉_}6]Ţ(0>>Ft%/ζp p@eU F**a\H6oތW*zk'}߿L,Mp|;I? GW_}kQW`4aUAV466 7,wgvHUK$w6@jnd2a˖-hiiAD^+0͈b@Ru C,)a3\(//_V@^@h7?oڵ vȄ/q=#P,˂y/w"0%MLb(L+V.g(bժUic |T᠐6!xi,ą0ꭖ²,kkӣlZFiS] NL(Ǔ{@?lhqq13c'l1n\o4,Y& WRb-nZ5Mpg`j,0,"@ ~@r @3J`//WU2EȒBN'ƛT*]ݪܒ!'476<-#j{,G,BEU7lFhIr)pH.U0f,˪&KT> Жڦ<̓ w@Z"t:+Nnb 9SgX,,U+f7Y$TUU/ 󽿔{cccY! 7Z낼?f"ƨH҉UVUR'!1 IB `K;/&MF#8WN;691 ۭ* @T.19UH8*2? !!Iɪ7\A\Iؿ?o~Y݇=gr647>tvvz/%3jKDDih*$NVo:V:Z ? &₌@J I,hs]^^._zhw} . \}fF?MS~әgyeeT>R5Uc<7b:թʻIܞk:b$Sa>$*GFF`ZӦS-, Si@~pp0f3v;lV+UYĆ%RAXU}{fQO(1 Jjc(߮$zglo UrbO.B 5)3a``ԟ0XK'TO,l(bIK:tr 9'6qPQB :N5]rBT ,4'%TI V^Cp_|(*؜c<狀 BTIX,ON B;.%rˆ0雄b`03gH23{Қ{R}s Zp308C5'+/vEQq$ )4S<*v܌kjr)I^#ˠ`NQ"5O| ?G;T`J^3Q^Quɚl6kkea3R+='&I~_gR0k/&ãUT[ee`Y6-ļ/H9ۡ7!5~)lWM`12p:X]@2yRXH ΃w:xe뮮.&.2ͲC}ovFQOZg#hcC`SEUVhg b(yJTa r'Rj2 h4 {y9'x>^|WoC8g3%ת&²,.k`4l6+ccc(//T l6}˲8\.Μ9حd\Zb)koSOd|/Y}*B _v X)0b\..Rr6]ք*>}ZE)J`jfK_>89"MWW֮[ ͲC}GgSqRcXT,7!֗=HBtځ#dX 6窬 ;p"71&Ԁ)0ؽ{7vܙ?{߃x_* *:05?SܒfX `J% eYyjIJ, HD^N~8m`6\k/&>9I Íb}(8;˔m, zÒQ4X6 ZfbS*'w%dJr9 t-2Ֆ(|Ciܹ;C gG'L٦%7d,ͦ8p@V$0HKb=vYxWEAd C'#f] JH0r޽wq,$Ik?Ѧ+>6«ymӣ:cZQbRl(uqiU91|8.+3DQTUS74[i׾5xwmcx"B⭗^IZF|_GDWw8h2EҀT$woI\p3YEdWnyEV1YEeU% (rjReB^U_U0Dss3l6J,tS#V%l@(8;f*j+6kXdT^wVzVW__ 3f ~oggZşԊ|+ #Z8N -jjH 6 FCNFyP+?8~BTjrt:l6b1lXYUi"I''.j6$~ $Abf:SrY\w܁ݻwVT᡾ɏsVW9R-|YIPy#UUUN- H sCLSN$AL&zb݀p8,d xDI(h_t ---8xjUޛoulBZM0 %⊣&򴢜olФe) F#"DJ45>ASRVEho6tww}Ϗ?L\z&0&N6硵 uH4-E+4Zt EfsSUCgg[,K,Qzdd+8@"PH%S[يV$IKÕWfxWe!-wqǢL07))5T7J%<:BRT\FfBv.%VoOUTAq(fZJKK,DA Nu3tMr e7@*oss3l2mIv%$"ET`PlZLEsfCjJla'aF9I3v=t 8vZ*+ɻU' IDAT@.Ƿ ?n$j7 uTZa"n>RRZS[ANc}j\5L6NjMEi2 IblI%_0.=i`/ E4cYs;E+&n2 .p(VH"s *s:TP8A`۩mtb@ptPKk8(QM)64% #_"/YC^3!tFDQرcHj8b1joUft:PW[xr!Mn6^cXP:P* GDZn("vGL,-akDQzҷuV_:u NzinX&M*d*p.}q"f6U Cmn6i\Tui9˲ptP29 Adgo'|a70Cb˦IGe%Jb`@ @mdhe9 kOVv;f).U@E@"gU^ >mNgY$G& 7|>RJ-\M̄7z @Kĕ+1{HRfj8xzP( ^luUƮM7ވ7^{X,YVEHoZhnn^´+(bLdtmFOêd]QsZHb&&ZJDKԁ?CEU0 *#[^!WWGcc#;&˃JxZm18BbXQcrl)'#Xl^ Z`F, "Du)lVQuGzHk9mيrUk*--$'rh]Ӛ~Ym۶Ey=?gs:hmma^Wtl0(1 l]`0ۍӃ8=@=դ&00TVUR}N,¨7(n-٣Ar'瑃 j'|4fotLہ@---Xݰ:;>Z<*֋ rYU3ygC:5YZZ%J/NVQMWa߾}r10)~LM]`P.8jY~ѤҋK `sZt7e^9sgF#ul6z d\1L0bCTڰ~Ah!6yODb,6*6Yv<wvQL)J[jHm m ȥ(=jY%-L6x4e%zTK7o e144$|nZ992f;IRzp}T7Lը"g9,[(v !<RM FUix^l6jl͛022"sܡ`YVb1ovO]j qIV-) 08y$>\L/b[\W^'gs|#ҢRbHFOm~ZRSy (\rXG>fZt:V\JnlR Hj~ɺ?yO ot*8λvRT\DmS]]KEXEumِHBM@lV+u P+i|#rf&f $o}K(tvvzo0)nwt:E4& ,ˢ%,0n7bWS%uT8܌?P.m0ɈjqFՊ71>!OsU[-(0LF,~E,C]]]V?Q@ݚTz466.e\I<6 ㊭6cDjbxdX.,&}lc|r&hpa P@ZZzΪ&%UwtDTLEY5@E,Uhh^~edi[_j0QDLfFUYzDQY[g:xcrl@oo/nwZ{qf3jjk8PR $>۶m5N?!?'5)`ZuuurP_$ttt`3-|1ƌMk6aդ f|ʪJ,j5kt:)XHפhCCTDΩ0f3P4]֔ՙ Fz@T,LLvҬ QTLQnGVohV2Nn F1k")1߉RIƴ@c 7;Fe>ms[-%x}|k3땀^8ٳgӞ3M$yQ%IM5B FRD n'ȳz*HplEAԍ^-ᩐ".BL$EYVZɈ(Q +`Lp nBS!U@pTH*nDOOt:b'UhJ&,eeV~l Φki H&OКjtl(Y)  0,Z+qxu+"XK.pZQĘ*VdJ,ϚK~l߾== fʤ"ڃy^UTRqUJ[& f(7ˋb FUJLFť/iee8>g)hRE =V:M" @7%1K!TThԺd8lGro*&RS[3wŃo'HhO"Cϋ6ob%cYN""sQ~-z̻݇a~SAncC.΄i:@j `FПX$ T 86E-&BF`J @oFΞ=+ET;"AXPqxFF/lVkVcE%t:D4K@49SnjБ8\.j1Ͱg4R!%jH`.NJ<Gh*DCTHF/Q]['ӊtF5"I;yAʘ|pbee03_jV7\I#Q-d- RZz 6Qd*ʪ3 #`$P~A$fQ"% PWO<"sft:YLn ѐ U7mwIB ѐhlH40 s`6gt fX4?n+%>)aYݎzT\2@94Mo WdJ3# !1L}OT\@g_ \D21v>ie`Wh0R mTG}h22y:*+0i!DCݥgP 4ͨVx7;GHhb<ܤPoePɮZ]b-TUHTWWl]89K6b.Ȉ\$(\.AoQ nt+HjD2s@3EIj|>iG4l4HuKi-J$ ;1AuM|*2 e߶)XX))"J9)MO BCH;`*x0 CGypQz8 FCΨAEYb12r@6zKY)b:Q1%b%791:ߝL$)+P0F,R-Lǩt(--%. cCU((-+E]]UbQQɮԪxD):3B}2JJT^oN.0f*,uؓc(\UVttt(>HըNO,[EFT&4_ԭ7eQWU?+~o,CSTKˁ )Jbv;~lk&MYQ!_drtR8>-VKLA +vL&x"&ꪻ(ZLPX,94Z+[]6qgD5fȁ΅6HJp8,O aiܶsQURUU a>?ݔKd8+sfDOQ`cq.(W0 IUk kkӣ*B*?}ے9>FFt^?07Qڤj ^/̥֜<9-T1TS\vU{$SSS8K#fr$kdi$YO`ISV7SmtfAw48ϊD#NҼf, |>OB6d;XUTQφTS]՚Q#WY-0hllIiwb"~uC͗lM&q ^,,R]hJn(--ޟiiiAOO)q*\^r3ŃMfU֠w}k]43DBЂ\La㦍GJ:]$o$G1%b4 0KX\UIܮV`0 v;Khښ9-6f{?훍eY-J 6' Uծ|)5 hZ\qdD4Z\L2 :l޼"577cժ$1D0L.`  M5-QVZ$Bݐ6{Fh58}3LXjZ[[?8ؕ2$gd2IOzss3u>Dz28WE012rl@{{;)\]k~tPRZ4fCkk+zOG4E<>4 BNeMŖ-[$m68pI oX,&[@^`WF<Gqq1oXWn\eYT:po; 6}v (cll fUUUAo\~hkkN'6^6ώA NC Najj r]=>`ٰm6Jcc#WM|&3gԜ0 *TWWd2E 2khpK-pofgހQn E 6 hgFq!gxҸNP)cl6+hoM;v`㦍8qD./$>[o%[+dnG{{;c,'+TۛvqZZZp7c㦍hZӔ8.C(?(xhrT8.Ld4[! ±ҁ>?ގꚬ>ٌWG! (l6ttt`jj uuuh YE]ad?rU%V7]%7'0lV+LEظi#NgVK-)& ENf e˖$t|5M\UP8mvZ_EiiiҊɱdl?{'4/$I4雤jz0*fE"xz\ n\U3}Mdc1""JCccSqu-xsٌD8~"9$LIXTg1MrAvxGH rUYH àiMAHfDQqђb(JI9HFό$Qs^µ.B rNkU_J$uE\Hid5kVcwVhejs"F$uX>ݴ`kɬa!s Rf mgq\io1JY! ˛x޴@۷oפ@9 'Pdh[Pc ,w=t|~¡DQLh0.]Ҳ>wu%R̈́O(<)vo ۍ,,2I5^jrU`G}hkk+<)%xr`>,TTY dY%dlukN9Q&.+-Hrg[_HfN@B s c)y&(v˷?90uNpb@!Xy"z0PӘ\V=BW&'5yQ XtO2=M-M-߮& %'N( 2y)E䌂蹌ֳ>pI%===s3LEEx7 9IDAT/ /a%جV@B v1<2%_J9zh t2PԀH.Y l.8B9 Ͷ{  o$ϯ [o 57Qd8N%[~Q'k֫4MBitWqI$A@ww78*rސqgW,DHK5ӓV?Y;7̥V<^lYey>g2y(7` yY\et:PW@ àR 00F/I董.kU.فzLzMBDQDcQ4~iXY dІh$]@0j558q@LMM+0:6H,s$!M^ 5adlBp;vP_0!D#y>oXe"RMgxGeUe,2]]]i @FW\Qn:4"da~lA[((/(f?0 s@GGGڿ盄4'24#q&|qJ%p+`ptaʹSV"jZ9k6D$all,+n@Dkoll0&}K6Xb@!orE"l6]R{_@zj'GE\"*HV7ՈnO[[9ٲL:::Fe}Vm㽲9}nwVjSGϷ~ bjK+4y.aPYUWu ``peeYϸ'I|mjE߱rNq󠳳;wE" fn2#_[a`5[Ou%)008[oG8&{K2 Z?#/@O֦ :μ1k*dKst-D=܌_5FIue3Xf\m) D 3 R~@Ӛ{|?=L̑{McH> *!0"R M(#$rx=x<:Ojv&O`[!I3>O0ssL)vw?l]aDkP*@|ce]}]tttڮE~/oڞ^ $ ?{QlrT囯]-,̱2LA Pb)Y'\K,F}/I#O &S/Z/]Aכ,ZMFAż䈢a;E${ő#Gdh4ںRy OJgymmm#A)⤱Q0͇NX |. ͋ۍ~ZV~ YN}vM^@KYfǮJtw+[xew**~LEżi1(7&AX0{nb1YF#ֿ.nͽGO&>9`q<صkײ:@7 {!3 _>²j݋4omm8uޱ{Dڌ'x[?uY àiMS^da;, {y9IyJۍ~8Mu:_`Se C=ׅ|`ee0 Y^(( -k?;= V#G/ŝ߸p: U CYVv{\mO>$;&)_v}q.H|-5 dXk׭cNRw{{ߓ6@VL&͇m[=3EBA477]}A.8 Lh2ϸ_f$FVהWnSQ |~s=x ),p{9}?}<F} /,kx_av<裸뮻 'Ua @ )[$o,O>$ԗ]O?Mͽ}?Txhh=uysH.b^(|$G#8 zAGf//>?$4[on鋸t:]ޤDQk.,P8~z{{ew@Ε`Sm&ľBWWvY0m1!ɟ 0<27|=Awww/K@2(8tv4ѳS4l4ݽID)U8 E\n@Af MR)j~ $N8}ga]p`ܜW7lFYiYa搽{gMxF#^(ο<}"I~ilB4%st55y&t:,[ *C> ߼y3hrmxH?~?gG q]w@ EPx v}.L De:::`Wb˖-j׻k~yiF&=r}Nɛj4~OPZ=܃n _ x=zly^W(x'e/omm]L-~+ #$IxɧށܳFD3}y&n|_Sr|)ul>[>{f?+Ų_󚎪ľ/i=xz_|Gp !(|P$gj5ekx1YZo~X(8)|V*kYfuM \˂^1c7l.É(B)BR,RZϻ1>70E qE ωc), ѱ?}__jZ_p(>U3Pm 0 $Q]AaɐW^vܼP[+Q>'"ڔ,Ӹ5̺gD a'' ϗtk=󑓓k@EY^ [Qx+,X[0>6JEI B*l6rqs}x^ }Jju%j&R%%%ԫ)f… 8Ԉ)qouu5fSϼ]&O&zݸ ^,8!avƍS4sD XZ[P^^'gqۢp[$;z᭿05>y|bD"d2в VEUUw%@|WD8QX 2wk,=5= NCVS- ^o+#p^'l]]f =\h4 3>q`٧|Ie^_+HMOG!/?SFmM-W'~(4PX#z;&]V?" [' 3kt[[pt2=|F ;AGCG@fdh4Pt*nCBqYG oƀ@@Ra}4Dx0 N^}ۣyG ([}pO;T$''CTBVc=+LqV .#\ےG `p>ЌɡaBDvv6T*bccfb_0ܸm!J <>% w߃` *$ J%Y dggC.#*2*h rVQlhD{[;fmr]H'{òr:ǝv=kз`\.ׂD8VȒSR@KE7m B>` ^MmN= \lHNOű#GȤO `}cqamg͘2*!=5 )))̀L&CTdD"Ѻ"a 8pu K6 V ],Kۑ$ EG#666 EqFΰcr39f8+3z-1ؓ_}I @A5ODKcy_d=K7v3)PBo1O} z +HD$!nG(5xJ+EeS=;9094~JNwb1b>GHJZu;% ,rE]IENDB`(0`  ***,,,111777:;:===ABAEEEIIILMJMNLNPMPQNRTOQQQSTPUVRUUUWXVXXUYYY\]Y]]]_`_aaadfbeeehifiiimmmoqlpppuuu{}wyyy{|y|~y~~}{}- );6FPTTTTTTNH="50;PTTTTTTTTTTTTTTQ@&;3TTTTTTTTTTTTTTTTTTTT@33QTTTTTTTTTTTTTTTTTTTTT;S=HTTTTTTTTTTTTTTTTTTTTTTTHTTNNTTTTTTTTTTTTTTTTTTTTTTTTTTTTT"QTTTTTTTTTTTQTTTTTTTTTTTTTTTTTTT)PTTTTTTTTTTTT=TTTTTTPTTTTTTTTTTTTT"KTTTTTTTTTTTTTITTTTTTTTTTTTTITTTTTTK FTTTTTTTTTTTTTTTTTTTTTTTTTTTTHTTTTTTFK)TTTTTTTTHTTTTTTTTTTTTTTTTTTTTFTTTTTTIT@3PTTTTTTTTITTTTTTPTTTTTTTTTTTTTHTTTTTTTTT7;TTKTTTTTTPTTTTTTTTTTTTTTTTTTTTTTTTTTTNTTF)QTTFTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTTCTTT0TTT=TTTTTTTTTTTTTTTTTTTTNTTTTTTTTTTTTTHTTT@$ITTTATTTTTTFTTTTTTTTTTTTTFTTTTTTITTTTTTTTTTQTTTTHTTTTTTFQTTTTTTTTTTTTFTTTTTTCTTTTTTTTTTT")TTTTTTTTTTTTTTTTTTTTTTTTT;TTTTTTNTTTTTTTTTTT=<=TTTTTTTTTTTTTTTTTTTTTTTTT;TTTTTTTTTTTTTTTTTTH0-HTTTTNTTTTTTTTTTTTTTTTTTTT5TTTTTTNTTTTTTTTTTTQ NTTN)@TTTN3 ATTTTTITTTTK.HTTTH) HTTTA&QTTTTQTTB TTT@TTTTT;TTTT&%TTT .TTT@TTTTTTTBTTT= TTTTTHTTTT0&TTT4TTT=TTTTQTTFTTT;TTTTTLTTTT.0TTT 3TTT=TTTTQTTCTTT; TTTTTPTTTT)0TTT"0TTT @TTTT!NTTCTTT5 TTTTTTTTTT)3TTT%2TTT 5TTTT0FTTF TTT=TTTTTTTTTT.5TTT)0TTT3TTTP";TT=TTT=TTTTTNTTTT.0TTT&1TTT4TTTF3%TT@ TTT@ TTTTTHTTTT20TTT&;TTT0TTT5TTA TTTA TTTTTTTTTT)%TTT2TTT :TTT 'FTBTTT=TTTTTQTTTT0%TTT 5TTT:TTP &TF TTT=TTTTTCTTTT06TTT"0TTT:TT;NC TTT=TTTTT=TTTT0"TTT"1TTT3TT0C TTT=TTTTT5TTTT.)TTT&%TTT 0T@55 TTT=TTTTT;TTTT)1TTT %TTT 5R TTT= TTTTTITTTT)"TTT)0TTT 64)TTT=TTTTTTTTTT1.TTT06TTT" TTT= TTTTTTTTTT06TTT&.TTT  TTT=TTTTTTTTTT&0TTT&)TTT MTT5TTTTTTTTTT)TTT (TTT IT@ TTTTTPTTTT((TTT&5TTN==TTTTTFTTTT)1TTT&3TF- TTTTTQTTTT)%TTT0")"TTTTTTTTTT&"TTT& &FTTTITTTT)"TTI ' &;FKQTQ&)&0"!)???( @ """&&&(((---343666999===AAADDDHHGJJJLMJLLLPPPTTTYYY]]\```eedhifmnmrrrstrtusvvvzzy{|x|}{~|}'$'11*$')*BOOOOOOOOD1$11GOOOOOOOOOOOOG1)EOOOOOOOOOOOOOOGLG$$LOOOOOOOOOOOOOOOOOOO'LOOOOOOODOOOOLOOOOOOOO'KOOOOOOOOKOOOOOOOOKLOOOG5DOOOOOKOOOOOOOOOOOOKKOOOEG.*OOOOOLKOOOLOOOOOOOOLLOOOOO7-KOEOOOOOOOOOOOOOOOOOOOOOODOL'%OO@OOOOLOOOOOOOOGOOOOOOOOKOO1>OODOOOODOOOOOOOOEOOOGKOOOOOOD1LOOOOOOOOOOOOOOOO>OOOLOOOOOOOO*%OOOLOOOOOOOOOOOOO>OOOOLOOOOOOO'%OO KOGOOODOOOLO@*OO' KOO*'OODO@ OOODOOLLO1OO @OO*%OO GO> OOOGOOLLO5OO@OO*%OO EO= OOOOOOLLO5OO>OO*%OO DO> OOOOOOLOO7OO =OO'5KODO@ OOOEOOOLO8OO 9OO)=ODOB OOOOOOLLO1OO >OB$ODO> OOOEOOLLO5OO>O*2E DO@ OOO@OOLKO5OO =K*'DO@ OOO>OOLLO6OO=1BO>OOOKOOLKO9OO 13DO@ OOOOOOOLO7OOBO> OOOOOOKKO7OO K> OOOOOOLKO7OL"* OOOEOOLLO7D OOOOOOLLO7 2$=KKOOLG>*2$%'$1??(0@ !!!$$$+++///443666:::===BBAFFFSSSUUUZZZ]]]xxx}}{~~}$ '' !+;;;;;;;;-&;;;;;;;;;;;4)0;;;;;;;;;;;;;;4-;;;;;;5;;;;;;;;;1$;;;;;;;;;;;;;;5;;5);;;;;;;;;;;;;;;;;;;;';4;;;;;;;;;;;;;;;;5;-;;5;;5;;;;;;5;;;7;;;;;;;;;;;;;;;;;1;;;;;;;;; ;4+;;+7;;;;; ;;1-;;';;$ ;;1$;;4;-); ;'4;'!;;1);;;;--; ;)1;' ;;1$;;;;-0; ;)0;$;;1$;;;;--; ;)-; ;;4$;;;;-+; ;)0;$;1';;4;--; ;)0+ ;1';;0;--; ;)-;1';;;;-+; ;);1';;;;--; ;)-1';;;;+); ;$';;;;--; $;;;;++; $ ??(  &&&'('+++---;;;???JJJjjjoonqqpzzz}}}!''!-*4522-444400224!5-4!$1 0* 2'$ -)-' -4*-! 04)- --)$ -*-) 4*$ PNG  IHDR\rf pHYs+ IDATxyx\w}/9hFd[xwl'^⅄41!mB }{C --} B[(p ,Ii`8r/qb;%Zf4?i$9:#;RXkZxS8|b |R_ DA$h4MbhF#XŌ/?_gK#ym 4M?\MGœh_c|rTTl\JDK\U@E$IjNln$Itțh`ol&[KK|#&qqݛ\z|ž4|*cyDD$$IEQEQE(3=l4l*T )F#$IFlʤ#}״V'|?Kvs+uـvnNNVv6 ff& ÀiaJEAP.Q*P,oɓH$$ Àa, `A@2M>QLglyF/Zov>z7\+ݮj1 &A477# ImmmT(B n`˲? `G|o~wqYeb(0  Fd2h4`0]gMe hvb1|gVQ]e];[?ƚD f'J& hmmEoo/ <T T*xA}9h0r`X ׋aJAT%h(pe>맗V\޻:Yq#tԹ Z }}}l2\.8,<5훈9D( `qX~=>crrgϞʼn'p) !Ly?™ X޺A/x?Kn #䊥m >NUgݎn^[lOJ:, 4hF?֬YgB5 hFgg'ۇBT*Ak8y$fgrė+3/v}7_cƓ}K+q MnwzE[lnBj4)5:ΊtoYl<+Œ|zUb , ijK(Zqf3>?$IM +l挂(=_$d2e|L&199)j%犜ǡP(L"&tNoDKelt`=iד{4;v`G(Ӵ\Mq>xC\OE >u5ctWk7ֆ-JىFGGa2zjlc_.aXp8 (LJpjwLnSnf|k_É'fd4v k`ݱ?s~i Bg?왃 jlƣ>z/f4!- @###i+Wlس&#&f'EQDGGϥKFa6U(IZ\ru6~; m=[ꫯWGo6,(lق??ӧ"_|n=/3vWHvv)j٧cg/Y8ap}NHM`Fu\.uP,1>>d5vkfl6lnnF8$I ʹO( V[~L7;L,5غm۝Jy [zo׷-O`3EI&r'1ˋoQn7VZJ,QTcȆ)JslKv@'G{~-dhhx\~`>!_4::J)4 õk>xر =xl"1SjժU?S}z\#I m 힞rdI@vXUKeYJRK %HER)\xXlAsGQ6oތҗ7|Yp$I?ĉKQ>pq 9Zf|b߱cMMMu/j^1D*A@͂"a'4z`Ӑ$ M`YVӑDW8C&{`-z144٫^__'EY7|37=~s=3>1þ;|Fu9b||Umꕉ D"R)SΓMGgt: _0`IRރjAJ%]>;wĥK099iN) W%nO kɱ،?,T$r996i0$h\BQn7\.R|>t:-ө!bb U}R#$EڣR)*Mӷ5k|jwg˺˷#՛l6#<2E̩rQ2UT haiW 477#hQQ.u3K, U&[,e22DsssyAW+\U޻<6gV,X*"ʵj(BTB6&  oj' P*t$IB4E,^ hzo~rX,QR :sv/Pf3>O t j #]Dd͡wF7BN\CvӐ<KD"r`\ @ٸT8Bt2ZtPIrr~?z{{ڪikeZ|fH8 U(Q=s>}󏏏#B&- զ1^s*98܈v  uӒoAt%͹&&&@4`5Pҭ&Gx 'rzb1fvLSD$D!;|(k6 TmՂ(H&8mjt=gfbq0 @ PإR(ĉx~kA%k$IH&ؒ---g'O0 =hEM[,{*&ApEp`0P(lTɤFZefpWsM&0|J'8qkv~pi[ؘ\҂n\.yX,]7~pTJY[d2H&iǏWiAGG$IB*B:tMRoӍ8eD`0R)4/67^ tݲO˕zb*vd2'R  p)PiM3R5\ ,R-hjjbjG-2wϺ/Ud GI38A0 &x_>q8wM]=;vŸ哓RҥK3;`N'sR O@EIEJm9EYt>źdYOćCʉ @J=Ns\u ϯ)?왗^6766l6[Sd2HMPu ɓr=k9W R=n(B[[B& 0 JX,d2)GArb3¤\͛7v``k'xNo111138LhF[kL&nw9?~<ϫ<_E@71W3Te2P.妴`?t'~hs?XSd=b1̀VTzOs\շs[_8/rlp]40H /Iz[8R6ZBj9.]Dr v֮K$pbLgSt),_rY$IrbD4<ZZZvcoٲ^{sۼ0CD"=z^*'[-j{<] \Vd3TxF16:Rͮ `0\ U*E!Zс?^}hi?Oȑ#U]0U;oo.;(5qjNVDũU;[sjD#"%ùsT+iNf<(*n_;tW5X]m?<]!+*eYa*Rqf\ CuVJU<h I8fC&0|>ꨒ(d20 rV\{ K'7o@=w9}~:yDSF w|166&{Հhl5qyL nΕPˑg0dV MAJoMpݰZSl6y%j0+(<8x ?b/xwj \o?w:BJ>ء )gΜѣGp 6\.fu@&8qb˗/FE MLL)ǀi9I+5+)Itٌ'xǫ4J N޳ IDAT7?:4vUc|#~B?QU8N~]m"@RQړS!8x q:::(ɓ8}4XUd94 NZ X(TkIR(˺jOlrY;Sd]Yj>;H+6mÇ Xwuua޽8wrjUdI>8Nx< No5]' u &uN122&(K`X4llҕ̅tRӑГk$D4 o<^z%={]S>j27_]е/=xxh4qzzzdHkp#H5~z& +@EU׳,x<.| B( 6EG"hhnn)d2rL'ᶑ brr---iP)aP%s`*_nS>U xb W{wG6\"UUZcرoYɦl.#YwH$¼+ەJ%DQ cŊ˲"RXBӴv)!Z۱k.WR=OkC *vO~4McllLn-kdd@<,+7tT%zI2qx4.yPkۊͦhAϟŋuD* Ο?ݻw/HEۭ:iP@*B2?q>|`+W޼yН]hJ:q$[l3<ړLQFGG5l2>>Y-T ЕB(|> 6{C$ŋ0կ7tFs,U  ҂|>_k!TɄ`0vݚ#kTR`swuu??E1(J,l8A,˪IP,uk-E$H ȧ 7Lm:'`<G.GXlhi2IQi Ldw\p\AxV^z͍uQQ(~+\p]ᅇ6o>x!TGy>j! I P"f3X&&&0>>aXEv#,KOzݺ\.l)T_.CP %rZp- zm9{^zMr\pr|r-__[oy͛7E_GxwN`0T*( &&mٳ~Zy yXmV0T)bAK-x-DU;NbQd=p<$!> +)$ZB("V<z)$%Iz\ ܡsU[r%SsXV<"J]QfYVnmm)(/#(֒(A@/r8$Ʉo<-Z*p4?:4pŎ;4? m)I#*$@-m6E:\Pd_4RH-NX2_(7Yaaev5b4RlOeǢSaRN۷o˫" !h|hTK*0^kM7݄N *ܟxZWp5?or˼jZM2 qDQT >---x< 0}шL6Mea]wiqdY]57dul{-kv[o)nƍo{ `j4IY;Ԅ}]iNg>5/&Q<PՠMDUR^fYPIyn$jBHSҐ9#f9j fp$I `Plz  '$”`4hKI{OSS JSJ-5:*?pꚳ9zYϜ|bQn$zrH#WQ%(50>>nƆrbx^R_+3g٬|$u.$ ,eG|{Zi$"vUvlٲ?7›6m>|`}wwQQژL&vmubєw݈Ef\.#JDjsYHp8{[=ىwyG.hYJD5@LF8Hm6J>BH2229M=cCxWҳ0:ivmxg($I$} 1*l2r$cMӮ%%ZLe*o3G@\nx&jl=IIEEMऻ1Iѳ3njUk$wy^H+^+V#ةž={`PhQӳĂrU,)6 t6 lN@@33W8 ՕwF֓NIR6MZ|œL-۵PlػwoHԳaÆۏ9 M"u*,>7xc/DpYZ† ttL&D"!WQHb+b @Y5r^SAsn:N\.'g"U֦+pz^@Kcb-to۾ -ƍEm pDftvvRʚdIA`0>I))qXkHЕj p8Sy(!d]ܢ("Nӥ`h-1hkkCssVTNׯ?r&`B ݻWsx\:Hϵv 8 &Qm؃FQWR5: ߐtU5@IDDt|j1=ie-I"dBY={l$:WN?~kP vZնuY$LLL UuIK ȭ!DZ®]Tn@@ xw000Kb.hZؔ$I0FFF4]O@i#?wf3N>-QeBr?bBK20صk~ilذ#G`$cZjb @}55vlo]wj.]Ǐf!xԄL&˥ x^M`pׄ8a ='}txu9uVMv~dRuyIdm\.WQv1b,7 9!Iܛ)8NM>K:i^,iN=|\[nsU7ny KnkN|j-h^|6f}@"ZjU> zM2?%!PjB|!/g.T֭['7%(99v[nM?0H,ŋ8x l2]MQ<'k!~ncǎ㚀8YzP Y,MBBpݺ9&ZP\xẕ! p ѣG;@%_\Εeիg >騵^K=7qJ[UIZRu=S|4M7E8\ &@[[-[97E0 >/BH;s NVUv1###bsԀlPHqYz2 S[VY5,N\\U&e˖wzn L&H$=aX,n|z-T֓+[<ZOzQJpP($4eŋ/8沋 hjjrl6D’´ٜ ?IH$"߫PJ\^N-[?AKnxfzݭF:tzI$M2GWM?a1bi.]qH$X, #&X,*IǛD"!,MQX"P#|c*@mc}g>&^Wb8C5E]]];NOrr͖1k\$!cxx8N$.!XboP(4d̴/'n===J$I5bBpxrHg2)9qڴvfiM&fZ$} Hn1erd2$v)=zv҅xK4 ׋ DQƍilذo4Ѩ{lł뮻y:l姝_qxmEHrDQ,_\X,1bq-znbebvU-Z Adty' eU[RA.ӵ*EQhkkC00BSS 9*Yf 5LXlv5a4e gRbAO6B &VRe-DH(v{CA|`usFĹ !uʹ[H#N Hդl6\.W= @ڲe`6uףG3 z@Zj ^WJudi0$.ӉP($D#+'&&J\5>%{-Ms鴦@ƥfR)9sf3c_O U֦.I}|34Q|oA'=ϵ-JJ2=V%tTĎeT^0ܹsa]|>ժ* a6… (BlViTsc6]lՋuuu-hX* 4imRB&֚ DBU@HY@Uz5LUUrzRmRRH$h4[a2Q:%D4* J҂L٤sz$ǺvO;voeHOT=Ivi(xf6-DZu\$QFQP34+ N7CPAy ~=3/Vjss.[2Ȉ&&1h=p(0U 56\Dr@TF3g;^$tŢ{ʄDODIGI\AJM&Sr2pŒWc6 )%AF* -$DsP(F MXv-;^xAu}ss3* dZ! i҇bz%ZKQ2IQ9  M!}E9D2 V X0[znRf~I`4_k\OTy|RrX,Q3H,J a$,{* OBŪP(4/:_^IT*h4ʌB N# RI]ONQ5=Z zrfٹf?rB0:Rd-)X@٫.xPNSW㰦(Jb"@) W},5R 9IY.I#-Iʕ W̯*6z%YzIRl+@r[ZZq`04d u:.Ԯn-sV;qdEǵT4YJu4SU+bA0  Q\.ihii[ͩ%RdŲlCKIe"+E!Ɔ.K4bL&#f7 &&&044 ;tj.P;,y 횪$I40F4I|>/kZ'>FKiSN`p:oy7DB?Zkݾ z3)Dit)0)=mz-o`Ib %a`=G"LNNnJ4=U aYf1 Ta@yGF#Z[[-/ 8INZss3ۯHo&=figh50a4zwvJ&r2]&i0khq03zP5Ln) ->9 gE<JdFUkSaӴAM>2~zw.r[M4EQrC3 Kek~9WVs8J*`FQ.xO~mmmWu app6/@URshKm IDAT*@@[랄ˑp9:99)ziFfO[@[[~CM:kU:| IUO-RL&ɎAkk+nݪ"" [ZZ .5̙3p∌EH(tѨ9Gr6ŋr =bp2ԻIeRΝtN{5lXJW?7`V|\N7(eJ%9\.ҥK8wz4Z @DQhO[5"9cccu"T%ճAEQDSSsl vg4zOKjkTf$SYI2 8I ^_&-N\wjT6&''1>>+!<#R%4M#" OZJ0%X0Hvھ  yd2]g;-Inhf3l6mliPUV\o g=|<ǣ\*ϵgG/˘D:FjzhZ]6A1E'#\>8HFHr91`~(D>?pD88[[[t8q.|FQfU(vpj.Ҳ~?$Ibin%l>JJ/ BoP- @Ll|^\ NMʥ: I."nE à n&I׹#휚p:=ۺaP5'=z/4]Jtw9nYPk]LRH$xq)5mz(fy]:"Zmp)L&ٲ)PV;f5̼&Fe-ݗ3DTH-I7(r^xCVf+A"{#)WR,Uk<$\:w2_$#޴dT*\t hD>Ek+Bg\_f&rf8N͕e $ORr<ˎF39@E-?=ޓHNVd2$B 4z8۾fYq n---8~99 N6T$I0FFF4/N%BNjT]mbzADxv;6|J,"jHI֭E&?a촡W < Hî04n7@=┠0 |>j$dd2>t gΜ@F8X~L&4$Iǭ_90C3XsMkj2gh$Mq<.bURL,# Q#jFyOOuAX`h@uLy'Ҥ$twwQɉ t:WƆ oXi0 L&\-CχM6aժU`{{;-@yŲ,V^===z^~80HYl0`0俑=p8}v4)YtzVOu !MMMӧL&!1l6˱b¬Jl===-[ݭfzXf ݋ .`|||fR) ftww*d? ܌6xކvuuᦛnB$A4Yk|b1| UDCYr%v؁+V:---Xv-v܉d2)3;5q=j)B(-ǃX,199 AԄrH$#G8^>(v؁.]{XO>$Gqa|rr9[H$8rrpwJ#޲e n7[He˖86mڄ>ۑ8СCxݍ@ @ ׋^]VWՖڵ +W[;r9:u ?qm7|3t;l2qhnnF&Aww"ˁ($ Xs֯_+VdmR@(. d@;wcD( \dRPDr vڅކQ_, ͆]2p98NFd2aYPH7Vh4@E~(մ#HZ m R(lF(ǁT+nc``<# ncٲeؼe3A455)+ u[K]]]|yVuzj?94v;qF,_\fOnmH EG6EQ䎏S 3zUbt:a Xv-BVkT#,ZUU(l6R/_.x^Yż|6Mv-8?R T455U9ݜ} yyNK6@$v7^-nAHq|mF.+:~J"( k!y7(X,.Z;q,t- $W4Uj Ȼ=#iWI{5zW%I8ݻw/{ot:111X,nexkI9}NoGX9yF*,;wN(={(HUV}y|T3g,$Rъ"XD\uiZiki}WV[}$l+EvYH!@d$zflpeI㇙C1=9>ۛk"+_:HGsS6T^EEE={2tmrdrTka`$@etQ[,,,(5Ͱ>0=ٳz\:"ǁ%#TH1lu>btt4Z-Xbޫ% & 4툈@ZZڠeIAIII'ܶnUJp%477 -40+un7|>bccVǍL lCw/i%())All,ƎXp… ?@cԟ0Gnŋ(`Y F@/J ܌ȰO ,h!}PѪ!W% Ǐ#)) s׀/ H|% 9`rssI>G]]N'z\v~_{ݎgϢqqq5jTP˥=EQ^sT].~?XETTT}󗗗 p8q"5j|mgΜhP3Á">>HO-Qqա555rRphOh2QF!%%IIIj׼oAXĀsرc$6zCޙ 3|Y?ْ'KtmU:?~6͆"TUU;DDDĐG 2 uWWWnA;ZFjj*"##[5k,4JWqt$6 ϟ86ۯ `{ D)rĉRrdIHE.v-J@$[28M$p'*ptn v-auѣGp$y<T @@,Pr⬜,x! @>8tZ΅M=GmdܣΞ={DQ 2&&&"""[Ic|8NrFA@UUUF`P* ^h% (..o/2m-5q̙q:0 B' `=hq b(@݌#d.iI$m >c^Z'={vH7(p:GYY ϟ/ s{hii(R4nDVbl6C=hFh4}HMM dIn?/+>NѣGJ}(D⹗B%͡;!L@Q؀:D#>Y֮߫)}ߗY|/\v7G h`Ohd1^RJpDD(%MIRh0fޤ*g1vX;6ѣG#>lMGE6;CV-ZhӚL&h4`}9%WVcC3@MnkHE455Z6[^{8充_ /4TyA] Nw2PTJ5Ɗ(ЪVD)j-Rq eZV$EFGG#...ȜԟCc֭1Qjwz㱷7Y:t_bjz,&a\.袈"Ʉ1c@ӧOw?+e'-^ V~لz^l6EP_JկlZߏ |$ u>ikx;gU 6طo͛קDrr2<lnm6Μ9p7f1)ҽ'`@RRR%Q>[k㲲2|7XhQS*,!''9eI%@0Lt>^/V+8Ä BwŚ޽gϞ[r[=S8jmW,aƍ9s&9I%4x7BN>?L&,ʧQPXXe4,]qƠٯ^f] XVl۶ ˖- Skk8 (''ߐwjEĔc-}w}SNhvͲ,FQbQihUP9N,LBnT1$ɷ <}_]3ro}٥/^Ć / xDIDATzIr'" fDEE26C2([V466")) &MQ>u Ao!cp7JNXR+8VG&{$0V^T;VY}Po8Ig(H ^LlkEUUUx7:iܜYշ~_0'*iTvsu]j*F *ZUH:dp4 )#-0 UVuEQ|}m 4/oڿOR8q>/_eWInd2!66Vn(j )lzHC5:};bAE"9yyyBXly˗M/l ֭[Gy$(;r,ŋZ3f , fh4y4A0 X,PTuAV."ERz@Mq}[v0/X^G;=z4RRR ޠ@8/^DII 8Dr-JEn$22GiL*:'No.4:݊ܣGF ~-,L f+%vn7VZ^x$I"*: ?&nx<455n#!!AI 57nAVIT j+Wxudz V z}%>@Ek׮ŬY[cHn;󼔩( Co+`63/@KK }Xv)KRy+o(jqc{OCw7{脻n++; Wlڴ =$ łj$~Pc9LN@ 6`׮]#r(5dwvnًnKEh qF dj(f>lѯ~AJKu.ڢu裏*V+lTTFn%eNNNOZhh$r_`͚5T4a,飮Gvkm;Xpa 0nfqqq"ICD ({7y|W֩IIL/((h\ M띶v `2|r'?# $h6-`۶mXf ^7,-9NZCn;wqb z뭊Zi狢B\RZګh;O}_U_e7VwTF#~cP +I! %|zNx3pww{%P6 Oq!$$$7 Fcy>|oF(CĒcGrv> 򾯿aMCmMޥ /T"+(j`/"^/֮].T*~? kkپoX31)w۟KvD͛7*zF0 FvDGGa^1Iݯz'NOL<6kX?_q*}l@?sxgd0A, *)~lٲ֭D {fڳwpa/..7ijK\]ߎXa{8x ^~eLҼtQ ,^O?h2#0jpQ;w{Edd$^}U=ۂ2n| 6o)hu/b-{͎ We̗m;bn{KEݍ1=>#8p/"nEA JpQ M82e رcxPVV5Ak20/6; ?l|=ģFKݱ)c>8~VEeeexWp=e|Iu`YfY1 J{DJh4殢~!;Au_ܖa;^#aQ̜?/&.uguښT!cGtt4DQa1}N'IE +󨯯Ν;QyiiiH"=X;v$v4FS`'DQ8P k)'fFKdܢ(e;- $z s΅fCvv6V+~_beM‘~$''cҤIؽ{76n܈ʮByA*h ֍i=PRp<T*p]wnyOߎX17rA[|yشiJKKlUjzE9@Ang\F=13C}٥{W􂣮qqKm} jƍ< {DrrrH: ,"D rО/hZlڵ .]6 Iaj*+^.eǏ{].; wqxp 0 a *}9D*`z(..Fffx1NhoA0 Wrσ41c3Ǽr؉\ $C X"-- w~a̘1 r@;8Ks*`Ϟ=(--0ݶ'U((D‚ܷhA!%Ζ߻cbb0e̞=7t‚Xn<p)deeɓrt^QCQ ,<>_*+XȼG"ccK$oS1$2e L<TTp +@8444ɓ'Q]]e~LcƷx~XDx/0gw[u }A5j&O;t'LxQyECn{zaPTT9$ENSKk40/X:TYl#er5$0WڙL&?^iӦ!%%taQ QaX pҥKbnn.qi?SDcԳV[2z]ᬽ畕^x:c^)9? x}=ހ4infL0fz^EM ҥXI&s<Ӊrٳ(//6+QJLk4:ѽ_BYu Rf/|(9f(jImi(Wce|ڀZcǎErr2&L4`0@Bр$!Q&,: Ap˔[QQQR˨)ZWi4u$EfP4{8kR $wyv]iiE)qc"݀E!..GRR8f"11qqq0 PTPT( * $IKʮyzbt"*z>Ahڧ5|>x<\.x^\.88f B4JM yjf<˟0?? 0l*u\,m؅(ZpJK+ފLs6;tEY hY!HbFM]֨Yy[L@ )R5:I6;ƏCmQ"PZr XQqGg]~fZx@ސ^+LDk%9{ɍNNX-eP$^otK FxZsl V @(#a@g6>z_ƻ ׸SIENDB`( Y_M;OSEYILA|TYI@C;121000000000000000///000000000000000000000674GK@:;7|GK@jDG>:IMBNREJLPDLPDFI?232000000///000000000000000000000///000000000000000000000///000000000110784:<7@C;@C;Z?B:W\K4RWHyOTF>@9111000000000000000CCC[[[wwwfffBBB666000000000000000332895>@9:<74LPCNREsOTF895000000///000BBBkkkUUU555000000000110664:;7>@:6JNB NRDFMQD<=8000000000666```GGG000000000232785=>9e674 IMA PUFeJNB342000000555cccFFF000///0007859:6553LPD ^dPHK@110000000IIIlll555000000232553453KOCSYIfBE=000///111[[[<<>>>>>0000004538:6f453JNBU[J895000000XXX999///0005537853FJ?OSFSCF=000///DDD{{{sss111000231674453JNBUZJ453000222sssMMM0000005538961MQD0EI?000000MMMddd222000000785aILAW]Ko>@9000000jjjHHH000000675332HKAX]L342000<<000222VVV0000003336OTFQVG000111PPP0001114541IMBQVG000000{{{EEE///111332\bO121000UUUqqq<<PUF000555ooo000000443"SXH000000HHH000222121OSF1<>8000eee222000343oHLAKOC000;;;000000333%[aN}110000MMM000222000TYI%@C;///^^^111000333DU[J000444sss000121111NSE+9;6000888///222fJNBSXI000;;;///111111 X^LA:;7000>>>000222fIMB///===000111111 QUG+9:6///<<<000222ePTF000888000111LPCCF=///{{{{{{555000111FOTFc010111wwwlll000121KOC000WWW{{{000000111PUF(785000oooFFF000111XX^L000777000111QVG000dddooo000000111NRE8:6000GGG000111YRWHh000333~~~000111JNB///RRR|||~~~///000GJ@000XXX555000000,PUG+453000UUUUUU000000jV[Jh000222WWW000000PTF000JJJXXX000000CF>000mmmQQQ000000MRE000aaa7770000009NSE785000```OOO000000GSXH;121///|||jjj///000|OTFU000000vvvhhhWWWjjjdddjjj000000TYI|000:::nnn^^^```WWWtttsss^^^333UUU<<>> TTT>>>lll 000:::000111TYIG342000ggg```JJJ000eee ghg***III```000222xFI?GJ@000HHH:::"""jjjXXX(((###6660002221TZI000HHH ///... mmm'''///000111V[JW231000 333***~~~ (((JJJccc000222FI?CF=000{{{SSS777111JJJvvv VVV000EEE5550002224NRE000<<<777...000[[[~~~ EEE%%%333000111NRE3785///===444\\\www000KKKsssccc(((III///333sHLA000QQQfff333VVVhhh111uuu SSS'''000000222V[Jc121000 hhh:::111---nnn((([[[000333HKA DF>///^^^===888EDE555xxxSSS&&&111000333+SXIp000111 AAA555EEExxx)))...vvv@@@)))aaa000333LPDLPD000]]]MMM+++uuu(((:::???'''222111000444+PUFg110000AAA###;;;TTT!!!NNNttt YYY...XXX///333HLAHLA000MMMnnn\\\<<<333yyy'''IIIRRR EEE111666000000444+[bNc443000~~~zzz777zzz(((@@@ttt;;;(((GGGCCC///444HLAPUG000888IIIPPP$$$"""kkk jjj(((000111333RWH0?A:000... 444(((888...}}}///222000555cQVG|000000444===---000hhh,,,,,,MMM000333222DG>HLA000---888---'''555"""qqq777000111554#NSE/?B;000&&&>>>"""444CCC###$$$000000665nSXHm110000 NNN 444;;; ooo---$$$444000444PUF000///uuu;;;333gggCCC,,,)))000111333JNBIMB///... yyy>>>)))___xxxAAA)))!!!//////0000SYI1>@:000,,, ]]]!!!AAAAAA%%%//////010`V\KO<>9000))) :::777777NNNEEE'''///000110}SYId674000***;;;<<<)))<<<BBB***///000//////MREj674000<<<...000xxxUUU BBB$$$///000//.///KOCgoV563000RRR++++++ddd EEE$$$///000......NSEgoW674000JJJ000&&&RRR444///000......LPDOTFj674000@@@@@@DDD/// kkkuuu000000/////.UZJd<>9///666>>>LLL))))))```000000554000W\KO?A;000000333222(((FFF///000776l332TYJ1JNC000000XXX111+++,,,VVV MMM5550001119:7PJNBQVG110000999,,,444)))000@@@eee0000003339:70TYIm?B;000000XXX...444""" CCC;;;000000897786OSF/ILB000000...***### GGG^^^0000003429:7PDG>QUG|?A://////)))''' ///4440001109:7675QVG0PTF443000///%%% %%%---000///674886B453GJ@[aNcHLA110//////%%%&&&,,,//////3428:6{442FJ?OTFgLPD000000///HHH+++ ...000000221442342KOCRWHpCF=121///000<<<{{{)))KKK///000000453453231GJ@ U[JcHLA785000000000HHH...ddd7770000000107859;6s563MQD3NRECF=231000000000>>>kkk RRR333000000000453?A::;7E8:6DG>UZJWTYIGJ@342///000000000;;;YYY &&&>>>222000///000000674:;79:6W8:6DG>SXIGMQDGK@=?9221000000000000///000333AAAOOOZZZeeemmmppprrroooiiiVVV...***---///000000000000000000332=?9>A:;<7G:<7FJ?QVGGLPDwRXHGJ@BD<332110000000000000000000///000000000000000000000000121674CF=AC<:;6w=?9GAC;X^L7HK@;X^LuLPC|>@:|TYISXHMQDJMBHLAKOCPTFV\K;<8CF=|MQDu;=8;JNB7?????????(` HLAQVG9MQDlHKAGK@:;7000000000000000000000000000000342>@9@B;>@:m>@99;=7 OTFMRD2LPCCF==?9000//////111<<8:<7<8:6NSE7KOCFI?231///111LLLxxx]]]7770000005637749;6C:<7LQDMQDkDG>332///888oooIII///0005639:6}895RWHKOC=?9000222fff===///231563443NSEJNBz?A:000;;;SSS000110564785KOCSXIcDG>000999___VVV///231674v453KOC!ILA121444HHH///443675<QVGl8:6000```444000775443MQDHLA111999VVV///342553"QVG.CF=///XXX222111554FUZJP:;7000<<<000564iKOCP895222JJJ000232{_fQ{674777YYY000222{X^Ln674;;;bbb///222{NREP553:::bbb000222{QVG>;<7555XXX000343`PUF$?A;000GHG000333<ILAFI?000<<<111222NRE000ppp222222221LPDH453JJJ///332yKOC?A:333VVV000222+PTF000444111110TYI>564JJJ000222cLPCHLA000HHH110111PUFR121eee000222zLPCKOC222WWW000111LPDJ000mmm000111{JNB111SSS000111OTF2332]]]///111bHKA000qqq===111KOC=>9>>>iii000111(TZIZ000}}}///111xIMB///III000MQDAD<===000000KOC4121ttt///000eHK@u000777000IMA000]]]000000KPCBE=???///000KNC785bbb///000FLQD?//////000lOSEl///444000JMB000HHH^^^dddLLLAAA000EH?///'''%%%%%%111(((---ooo666!!!ZZZPPP///[[[///))))))SSS///@B;000vvvWWW pppddd000LPD333mmmSSS IIIqqq///FI?777VVVZZZ$$$...]]]yyy///CF=888 <<< """%%%CCC@@@|||///DG>888999"""::: ;;;{{{000HLA555 ???###CCC qqqvvv///LPC111{{{NNN$$$ opolll000=?9///ooo GGG))) NNN^^^000KOC///ssszzz???... ,,,LLL000FI?t///uuuDDD$$$<<<=== +++<<<000NSE]000yyyLLL555,,, '''111000PUF4000xxxnnnQQQ&&&@@@000///000dHKA ?A:TTTggg PPP ---+++ ,,,///0002EH>666TTT FFF +++''' (((xxx000000EH?///XXX ZZZ(((000 $$$LLL000JMB^///]]][[[YYY###999]]] &&&111000MQD"785^^^kkkrrraaaGGG PPP000111IBD<222\\\sssiii %%% JJJooo000111 PUG///sss LLL""" 555>>>888111QVG5332dddkkkLLL !!!777^^^///222ZEH>222jjjQQQ&&&FFFXXXfff000111JNBp000|||iiiLLL %%%<<<### 111222222IMA=?9CCCvvvKKK!!!%%%''' )))0002222IMA000zzzyyyVVV}}}$$$ 777 (((;;;111KOC!9:6LLLvvvlllNNN$$$ $$$$$$///333JPUF///qqquuu=== !!!111<<<<<<222111KOC 895CCCjjjrrrJJJ;;;,,,444000333IQVG000WWWZZZ!!!222WWW444222111MQDBD<444666ttt888---444```000333/OTF^111***bbbRRR&&& 222555000443FI?///fffLLL )))/// KKK999111232JNB?A:/// kkk666)))111SSS 222TTT000554>KOCK342***==='''??? '''///443wPTF000%%%]]]!!!KKK ///000110KOBHKA000 zzz<<< 222//////000OSEFJ@///fff[[[::: ...000010%IMA?A;///```EEE ### ---/////..W]K.GJ@000AAA ---///....X^L.@B;000tttSSS)))//////....JNCGJ@000]]]aaa!!!OOO111222000.NSEILB000EEEPPP'''EEEppp000332554KOCPUG342444JJJ"""888JJJ000564454 LPDK?B;000ZZZBBB333221776nJNBFI@111...JJJ000443785-NSE^BD332///222^^^ ttt===///000674:<7:;6 OTF5PTFBD<785000///666TTTxxx '''999000///1216749:68969785JNB!HLA^DG>EH>?A:000///000//////111555888888777333000//////000///674895:<7;=8g=?9+EH> NRE4MQD]EH>tKNC<>8KOCHKACF>CE=FI?LPC9:6CF=;=8tAD<]CF=;:<7 ???????(H `TNSEMQDXDG>@B;?B;0100000000000000000000009:6:;79:6<>8XAC;!SXH@9;;;OOO111564h563LQD FJ@221vvvzzz777443453OTF1=?9???YYY111443COSER895]]]111443iMQDd453zzz444222|Y_M|342777222|JMB[332777222|OSEH674333332dMQD$;=8ccc111222;OSE ?A:TTT110221GJ@999XXX221MRE:342777222\V[JBE=bbb000111 LPCh333OOO111UZJ =?9灁000111IMAk555YYY111QVG@B;000000 LPDK000III111pBE<^^^000U[J9:6111000&MQDY222ZZZ000yCF=\\\000>@:ᒒ000000 SXH2215550009MQDN000XXX000nGJ@wAAA{{{000BE=]]]000BD>>>?>fffIII999///>A:Ņ&&&ccc}}}333,,, 000CE=ڐlll 999'''~~~ }}}///>@:ڕ)))hhhoooDDD:::ooo000?A:ڔ./.dddFFFNNN """lll///EG>ڍ(((ssswww:::555 ///;=8'''jjj+++???QQQ###}~}000DG>nnn&&&eee[[[<<<QQQ ,,,ZZZ000EH?UUU&&&lllkkk @@@ZZZ PPP000FI?i999,,,XXXuuu DDDdddVVVrrr000MQD>000kkksss888KKKYYYMMM000]RWH785zzz QQQ444 ###MMM111000/=?9̃RRR MMMVVVEEE000000EH>KKKZZZ ???...$$$sss111PTFD000!!!bbb >>>$$$ aaaHHH111dTYI=?9디"""vvvCCC... yyy000111DG>III,,,RRRsssKKKdddwww111LPC-221$$$___mmm<<9%%%SSS{{{!!!,,,NNN00000/1ILA(=?9KKKbbbCCC!!!//////?W]K?=?9]]]lll...((( 000///...?JNB(=?9NNN222999;;;rrr111000?MRECF=<<>>999443453KNC[9:6---9:9'''aaa110675u453KOCGJ@231$$$ 111&&&///443675)OSEDAD<000)))>>>'''...121564SPUFLPDKCF=221IIIAAA&&&VVV/0/453442V342JNB-DF>=?9000KKK777###\\\222332785:;7<OSENREDDG>=?9785000999UUUnnn222 %%%,,,///2218958959;6J8:6MQDJNB>DG>iDG>CF=;=8DG>?A:>@:BE=:<7>@9;=8<>8m;=8?=?9???????(@ BY_LNRE5FJ@pCE=?A:453000000///000000000111895;=89:6p?A:=BD<TYIMQDSDG>;=8554YYYddd:::232674785X=?9 OTFEH?:;7>>>~~~KKK231674785&QVGGJ@674\\\sss343332674&RWHKOC|9:6ZZZvwv333453674 MQD0=?9EEE\\\221564@KOCg564|||666443V[JJNB999KKK332342 ]dO BE=FFFbbb111111 TZIKOCNNNooo111111 Z_MDG>LLLooo111221 JNB999ZZZ221JNBW776KKK222wLPD 895򧧧6662218W]KAD@9޷\\\ vvvnnn<<<///BD<ۯTTT zzz>>> {{{000;<7MMMtttsss===|||000AC;PPPlll^^^$$$DDD iii000DG>pppXXX]]]$$$OOOvvvaaa000IMBcNNNIII^^^$$$DDDeeekkk000QUG/333::: ]]]QQQbbbYYY000LW\K9:6AAAZZZ GGGiiiUUU444000AC>>111.AC@9ҔQQQppp@@@ UUU111111 MQDZ;;;OOO{{{ooo###BBB```___222rSXHBD<σ<<<kkk###666xxx111111 OSE<564$$$zzz OOOqqqEEE222WDF>(((}}} ]]]ttt|||222MQD =>9sss\\\ RRR {{{???111232JNB/674ZZZ333FFF~~~ ...111GLQD[221kkk $$$FFF|||+++000vGK@r00/zzzddd:::)))///Y_LTYI::9tttfff <<<~~~)))//////]dPHLAr776HHH AAA110///MQE[674vvv LLL666454nKOC/=?9PPPcccyyyPPPmmm221564CMQD CF>332hhh TTT^^^<<;554564MQD9AD<785:;7:<7=?9g;=8/?B;???(0` %OUBW]K-EH?n?B;<>8<=;JJJTTTUUUMMM>>=896784:;7sAD<2570U[JDG>>@:_`_jjj998674<>8$SXH"CE=LMKXXX231674,QWFFI?LLJWWW342563OTF.=>:֍;;:453<MQDZHIGVVV332jLQDrXXWooo111}JNBn^^]yyy111}MRESHIHkkk221ePTF'EFDVVV2217RWF<>9:::/// JNBw~~~111X^KDEBWWW000"EH>111U[H CD@WWW000HKAb000{?B;666SYGIJHjjj000!QVGKyyy000aDG>000?A:111;=8IIIHHHVVV{{{EEEpppSSS===<=8DDDKKKuuuoooddd~~~^_^MMM>?;FFFFFFuuuXXX\\\UUU>@;XXXPPPggg___TTT:<7TTTaaa{{{mmmaaaJJJ<>8GGGGGG~~~mmmhhh:::?B;===EEEyyyUUU000FI?xKKKHHHwww[[[000RWG>pppCCCCCCtttTTT000VPVDBC@;;;EEE}}}qqq^^^______000AC<VVVIIIpppaaa___222KOCQvvv777ZZZmmm]]]111iRXF>@;DDDTTTiiimmm\\\III000 HLAt;;;QQQyyypppoooaaa111V[I?@<䐐EEEUUUzzzhhhnnndddGGG000LPDW111777```}}}iii}}}bbb222m@B;777QQQ}}}WWWjjj443...PUF795 NNNCCCuuu}}}```---221OSE3120BBBQQQqqqtttyyy^^^'''000CLPCGBB@HHH~~~XXXgggxxx```%%%///QMREGGGEbbbuuuuuutttIII///QOSF3>@<ᥥXYX{|{wwwAAA443>QVG@B;???TTTyyyoookkk~~~443564KOCW9:6JJJuuummmrrr/0/553c452SXHGJ@t=>:vvvzzzlllZZZ//.231y563 SXGILAQ@C;BC@pppuuu LLLyyyFGF453896Y<>7TZHLPC>CF=x>A:<=8:;7>@;>?;:;77959:68:6x=?9BBE< ????( @ @D6LPCgHJDjkh~fgdEFD:<6i-.)KQA-JLEEED/0.2>C5 HKB>>=)*'MRB([\Yccb./-/U[I6nom~~~---7LQA%ghf|||--,-;?2XYUbbb$%$ILB<<<HM>"***+GJABBB;?2 z{y"""JN@O...^EH????VXSfffmnkrrr;;;QQQmmmeeeLLLDDD999z{x%%%... [[[>>>yzw,,,::: \\\???jkh'''444 YYYDDD}}}STO(((/// YYY777bbbEH?... ```<<<;;;JN@G###/// ^^^>>>...T8=0qrp)))888 YYY???!!! FI@(((999 TTT>>><<<FK<{|z"""666 JJJBBB)))HLAs??? ZZZ>>>6767;/683 --- [[[???443##"JO?/0-343PPP@@@"""++*U\H$_`\<<<\\\FFF***$KP@OQL<<< XXXQQQ--,9=0GJ@s$$"777000786|&'%HM=DF>qrp괴 ded331-.+;?2LQCGBD2$\^WZZY'(%(IMA_998hTXLwAAA~HK@[777e8<0"""%_aZ___26+%HK@r>>>|moittt癚\\\"""///CCC___+++555DDD^^^!!!999CCCikeUUU###;;;GGGoooEI>kZZZ$$$///GGG999t14*^^^666AAAY\SSSS 666777VVV6:.""!@@@FFFppp!!!CG;I333:::=== ,,+SMQDa777CCC***gEI=Irrp======332Q7;/;>6~???+,*%&#36,GK@jgid٘yzx !OPM;<8l&'#(  @.1)5y{tstr$$"8 oqkllk   lngjjj*-%.4oqjnnnڹ򥥥;;;JJJ???񔔔ppp---!!!Аkkk...jlejjj~~~...iii)+$'iiijjj}}}''' -25.kkk--- IIIlnh|||zzz+++555 WYR%&%}}}|||###aa`*,%'knfиggfSSRab_)zyne/Resources/audio.py0000644000175000017500000012632312417524776014506 0ustar tiagotiago# encoding: utf-8 import random, os, time, math, codecs import Resources.variables as vars from types import ListType if vars.vars["PYO_PRECISION"] == "single": from pyo import * else: from pyo64 import * def get_output_devices(): return pa_get_output_devices() def get_default_output(): return pa_get_default_output() def get_midi_input_devices(): return pm_get_input_devices() def get_midi_default_input(): return pm_get_default_input() class FSServer: def __init__(self): self.eqOn = False self.compOn = False self.eqFreq = [100, 500, 2000] self.eqGain = [1, 1, 1, 1] self.server = Server(duplex=0, audio=vars.vars["AUDIO_HOST"].lower()) self.boot() def scanning(self, x): if vars.vars["LEARNINGSLIDER"] != None: vars.vars["LEARNINGSLIDER"].setMidiCtl(x) vars.vars["LEARNINGSLIDER"].Enable() vars.vars["LEARNINGSLIDER"] = None def startMidiLearn(self): self.server.shutdown() self.server.boot() self.scan = CtlScan(self.scanning, False) self.server.start() def stopMidiLearn(self): del self.scan self.server.stop() time.sleep(.25) def start(self): self.server.start() def stop(self): self.server.stop() def shutdown(self): del self._modMix, self._outSig, self._outSigMix, self._fbEqAmps, self._fbEq del self._outEq, self._outEqMix, self._compLevel, self._compDelay, self._outComp self.server.shutdown() def boot(self): self.server.boot() self._modMix = Sig([0,0]) self._outSig = Sig(self._modMix).out() vars.vars["MIDI_ACTIVE"] = self.server.getMidiActive() self._outSigMix = self._outSig.mix(1) self._fbEqAmps = SigTo(self.eqGain, time=.1, init=self.eqGain) self._fbEq = FourBand(self._outSig, freq1=self.eqFreq[0], freq2=self.eqFreq[1], freq3=self.eqFreq[2], mul=self._fbEqAmps).stop() self._outEq = Mix(self._fbEq, voices=2).stop() self._outEqMix = self._outEq.mix(1) self._compLevel = Compress(self._outSigMix, thresh=-3, ratio=2, risetime=.01, falltime=.1, lookahead=0, knee=0.5, outputAmp=True).stop() self._compDelay = Delay(self._outSig, delay=0.005).stop() self._outComp = self._compDelay * self._compLevel self._outComp.stop() def reinit(self, audio): self.server.reinit(duplex=0, audio=audio.lower()) def setAmpCallable(self, callable): self.server._server.setAmpCallable(callable) def recstart(self): self.server.recstart() def recstop(self): self.server.recstop() def setAmp(self, amp): self.server.amp = amp def setOutputDevice(self, device): if vars.vars["AUDIO_HOST"] != "Jack": self.server.setOutputDevice(device) def setMidiInputDevice(self, device): self.server.setMidiInputDevice(device) def setSamplingRate(self, sr): self.server.setSamplingRate(sr) def recordOptions(self, dur, filename, fileformat, sampletype): self.server.recordOptions(dur=dur, filename=filename, fileformat=fileformat, sampletype=sampletype) def onOffEq(self, state): if state == 1: self.eqOn = True self._outSig.play() self._fbEq.play() if not self.compOn: self._outEq.out() else: self._outEq.play() self._compLevel.input = self._outEqMix self._compDelay.input = self._outEq else: self.eqOn = False self._fbEq.stop() self._outEq.stop() if self.compOn: self._compLevel.input = self._outSigMix self._compDelay.input = self._outSig self._outComp.out() else: self._outSig.out() def setEqFreq(self, which, freq): self.eqFreq[which] = freq if which == 0: self._fbEq.freq1 = freq elif which == 1: self._fbEq.freq2 = freq elif which == 2: self._fbEq.freq3 = freq def setEqGain(self, which, gain): self.eqGain[which] = gain self._fbEqAmps.value = self.eqGain def onOffComp(self, state): if state == 1: self.compOn = True self._outSig.play() if self.eqOn: self._outEq.play() self._compLevel.input = self._outEqMix self._compDelay.input = self._outEq else: self._compLevel.input = self._outSigMix self._compDelay.input = self._outSig self._compLevel.play() self._compDelay.play() self._outComp.out() else: self.compOn = False self._compLevel.stop() self._compDelay.stop() self._outComp.stop() if self.eqOn: self._outEq.out() else: self._outSig.out() def setCompParam(self, param, value): if param == "thresh": self._compLevel.thresh = value elif param == "ratio": self._compLevel.ratio = value elif param == "risetime": self._compLevel.risetime = value elif param == "falltime": self._compLevel.falltime = value class CtlBind: def __init__(self): self.last_midi_val = 0.0 self.lfo_last_midi_vals = [0.0, 0.0, 0.0, 0.0] def valToWidget(self): val = self.midictl.get() if val != self.last_midi_val: self.last_midi_val = val if self.widget.log: val **= 10.0 val *= (self.widget.getMaxValue() - self.widget.getMinValue()) val += self.widget.getMinValue() self.widget.setValue(val) def valToWidget0(self): val = self.lfo_midictl_0.get() is_log = self.lfo_widget_0.log if val != self.lfo_last_midi_vals[0]: self.lfo_last_midi_vals[0] = val if is_log: val **= 10.0 val *= (self.lfo_widget_0.getMaxValue() - self.lfo_widget_0.getMinValue()) val += self.lfo_widget_0.getMinValue() self.lfo_widget_0.setValue(val) self.lfo_widget_0.outFunction(val) def valToWidget1(self): val = self.lfo_midictl_1.get() is_log = self.lfo_widget_1.log if val != self.lfo_last_midi_vals[1]: self.lfo_last_midi_vals[1] = val if is_log: val **= 10.0 val *= (self.lfo_widget_1.getMaxValue() - self.lfo_widget_1.getMinValue()) val += self.lfo_widget_1.getMinValue() self.lfo_widget_1.setValue(val) self.lfo_widget_1.outFunction(val) def valToWidget2(self): val = self.lfo_midictl_2.get() is_log = self.lfo_widget_2.log if val != self.lfo_last_midi_vals[2]: self.lfo_last_midi_vals[2] = val if is_log: val **= 10.0 val *= (self.lfo_widget_2.getMaxValue() - self.lfo_widget_2.getMinValue()) val += self.lfo_widget_2.getMinValue() self.lfo_widget_2.setValue(val) self.lfo_widget_2.outFunction(val) def valToWidget3(self): val = self.lfo_midictl_3.get() is_log = self.lfo_widget_3.log if val != self.lfo_last_midi_vals[3]: self.lfo_last_midi_vals[3] = val if is_log: val **= 10.0 val *= (self.lfo_widget_3.getMaxValue() - self.lfo_widget_3.getMinValue()) val += self.lfo_widget_3.getMinValue() self.lfo_widget_3.setValue(val) self.lfo_widget_3.outFunction(val) def assignMidiCtl(self, ctl, widget): if not vars.vars["MIDI_ACTIVE"]: return mini = widget.getMinValue() maxi = widget.getMaxValue() value = widget.GetValue() is_log = widget.log self.widget = widget if is_log: norm_init = pow(float(value - mini) / (maxi - mini), .1) self.midictl = Midictl(ctl, 0, 1.0, norm_init) self.midictl.setInterpolation(False) else: self.midictl = Midictl(ctl, mini, maxi, value) self.midictl.setInterpolation(False) self.trigFunc = TrigFunc(self._midi_metro, self.valToWidget) def assignLfoMidiCtl(self, ctl, widget, i): if not vars.vars["MIDI_ACTIVE"]: return mini = widget.getMinValue() maxi = widget.getMaxValue() value = widget.GetValue() is_log = widget.log if i == 0: self.lfo_widget_0 = widget if is_log: norm_init = pow(float(value - mini) / (maxi - mini), .1) self.lfo_midictl_0 = Midictl(ctl, 0, 1.0, norm_init) self.lfo_midictl_0.setInterpolation(False) else: self.lfo_midictl_0 = Midictl(ctl, mini, maxi, value) self.lfo_midictl_0.setInterpolation(False) self.lfo_trigFunc_0 = TrigFunc(self._midi_metro, self.valToWidget0) elif i == 1: self.lfo_widget_1 = widget if is_log: norm_init = pow(float(value - mini) / (maxi - mini), .1) self.lfo_midictl_1 = Midictl(ctl, 0, 1.0, norm_init) self.lfo_midictl_1.setInterpolation(False) else: self.lfo_midictl_1 = Midictl(ctl, mini, maxi, value) self.lfo_midictl_1.setInterpolation(False) self.lfo_trigFunc_1 = TrigFunc(self._midi_metro, self.valToWidget1) elif i == 2: self.lfo_widget_2 = widget if is_log: norm_init = pow(float(value - mini) / (maxi - mini), .1) self.lfo_midictl_2 = Midictl(ctl, 0, 1.0, norm_init) self.lfo_midictl_2.setInterpolation(False) else: self.lfo_midictl_2 = Midictl(ctl, mini, maxi, value) self.lfo_midictl_2.setInterpolation(False) self.lfo_trigFunc_2 = TrigFunc(self._midi_metro, self.valToWidget2) elif i == 3: self.lfo_widget_3 = widget if is_log: norm_init = pow(float(value - mini) / (maxi - mini), .1) self.lfo_midictl_3 = Midictl(ctl, 0, 1.0, norm_init) self.lfo_midictl_3.setInterpolation(False) else: self.lfo_midictl_3 = Midictl(ctl, mini, maxi, value) self.lfo_midictl_3.setInterpolation(False) self.lfo_trigFunc_3 = TrigFunc(self._midi_metro, self.valToWidget3) def __del__(self): for key in self.__dict__.keys(): del self.__dict__[key] if hasattr(self, "trigFunc"): del self.trigFunc if hasattr(self, "lfo_trigFunc_0"): del self.lfo_trigFunc_0 if hasattr(self, "lfo_trigFunc_1"): del self.lfo_trigFunc_1 if hasattr(self, "lfo_trigFunc_2"): del self.lfo_trigFunc_2 if hasattr(self, "lfo_trigFunc_3"): del self.lfo_trigFunc_3 class LFOSynth(CtlBind): def __init__(self, rng, trigger, midi_metro, lfo_config=None): CtlBind.__init__(self) self.trigger = trigger self._midi_metro = midi_metro self.rawamp = SigTo(.1, vars.vars["SLIDERPORT"], .1, mul=rng) self.amp = MidiDelAdsr(self.trigger, delay=0, attack=5, decay=.1, sustain=.5, release=1, mul=self.rawamp) self.speed = SigTo(4, vars.vars["SLIDERPORT"], 4) self.jitter = SigTo(0, vars.vars["SLIDERPORT"], 0) self.freq = Randi(min=1-self.jitter, max=1+self.jitter, freq=1, mul=self.speed) self.lfo = LFO(freq=self.freq, sharp=.9, type=3).stop() self.sigout = Sig(self.lfo * self.amp).stop() def play(self): self.rawamp.play() self.amp.play() self.speed.play() self.jitter.play() self.freq.play() self.lfo.play() self.sigout.play() def stop(self): self.rawamp.stop() self.amp.stop() self.speed.stop() self.jitter.stop() self.freq.stop() self.lfo.stop() self.sigout.stop() def sig(self): return self.sigout def setSpeed(self, x): self.speed.value = x def setType(self, x): self.lfo.type = x def setJitter(self, x): self.jitter.value = x def setSharp(self, x): self.lfo.sharp = x def setAmp(self, x): self.rawamp.value = x class Param(CtlBind): def __init__(self, parent, i, conf, lfo_trigger, midi_metro): CtlBind.__init__(self) self.parent = parent self._midi_metro = midi_metro self.init, self.mini, self.maxi, self.is_int, self.is_log = conf[1], conf[2], conf[3], conf[4], conf[5] rng = (self.maxi - self.mini) if self.is_int: self.slider = Sig(self.init) setattr(self.parent, "p%d" % i, self.slider) else: self.lfo = LFOSynth(rng, lfo_trigger, midi_metro) self.slider = SigTo(self.init, vars.vars["SLIDERPORT"], self.init, add=self.lfo.sig()) self.out = Clip(self.slider, self.mini, self.maxi) setattr(self.parent, "p%d" % i, self.out) def set(self, x): self.slider.value = x def start_lfo(self, x): if x == 0: self.lfo.stop() else: self.lfo.play() class Panner(CtlBind): def __init__(self, parent, lfo_trigger, midi_metro): CtlBind.__init__(self) self.parent = parent self.lfo_trigger = Ceil(lfo_trigger) self._midi_metro = midi_metro self.lfo = LFOSynth(0.5, self.lfo_trigger, midi_metro) self.slider = SigTo(0.5, vars.vars["SLIDERPORT"], 0.5, add=self.lfo.sig()) self.clip = Clip(self.slider, 0., 1.) self.amp_L = Sqrt(1 - self.clip) self.amp_R = Sqrt(self.clip) def set(self, x): self.slider.value = x def start_lfo(self, x): if not x: self.lfo.stop() else: self.lfo.play() def __del__(self): for key in self.__dict__.keys(): del self.__dict__[key] class ParamTranspo: def __init__(self, parent, midi_metro): self.parent = parent self._midi_metro = midi_metro self.last_midi_val = 0.0 def valToWidget(self): val = self.midictl.get() if val != self.last_midi_val: self.last_midi_val = val self.widget.setValue(val) def assignMidiCtl(self, ctl, widget): self.widget = widget self.midictl = Midictl(ctl, -36, 36, widget.GetValue()) self.midictl.setInterpolation(False) self.trigFunc = TrigFunc(self._midi_metro, self.valToWidget) def __del__(self): if hasattr(self, "trigFunc"): del self.trigFunc class BaseSynth: def __init__(self, config, mode=1): self.module_path = vars.vars["CUSTOM_MODULES_PATH"] self.export_path = vars.vars["EXPORT_PATH"] scaling = {1: 1, 2: 2, 3: 0}[mode] with_transpo = False for conf in config: if conf[0] == "Transposition": with_transpo = True break self._midi_metro = Metro(.1).play() self._rawamp = SigTo(1, vars.vars["SLIDERPORT"], 1) if vars.vars["MIDIPITCH"] != None: if with_transpo: self._note = Sig(vars.vars["MIDIPITCH"]) self._transpo = Sig(value=0) self.pitch = Snap(self._note+self._transpo, choice=[0,1,2,3,4,5,6,7,8,9,10,11], scale=scaling) elif mode == 1: if type(vars.vars["MIDIPITCH"]) == ListType: _tmp_hz = [midiToHz(x) for x in vars.vars["MIDIPITCH"]] else: _tmp_hz = midiToHz(vars.vars["MIDIPITCH"]) self.pitch = Sig(_tmp_hz) elif mode == 2: if type(vars.vars["MIDIPITCH"]) == ListType: _tmp_tr = [midiToTranspo(x) for x in vars.vars["MIDIPITCH"]] else: _tmp_tr = midiToTranspo(vars.vars["MIDIPITCH"]) self.pitch = Sig(_tmp_tr) elif mode == 3: self.pitch = Sig(vars.vars["MIDIPITCH"]) self._firsttrig = Trig().play() self._secondtrig = Trig().play(delay=vars.vars["NOTEONDUR"]) self._trigamp = Counter(Mix([self._firsttrig,self._secondtrig]), min=0, max=2, dir=1) self._lfo_amp = LFOSynth(.5, self._trigamp, self._midi_metro) self.amp = MidiDelAdsr(self._trigamp, delay=0, attack=.001, decay=.1, sustain=.5, release=1, mul=self._rawamp*vars.vars["MIDIVELOCITY"], add=self._lfo_amp.sig()) self.trig = Trig().play() elif vars.vars["VIRTUAL"]: self._virtualpit = Sig([0.0]*vars.vars["POLY"]) self._trigamp = Sig([0.0]*vars.vars["POLY"]) if with_transpo: self._transpo = Sig(value=0) self.pitch = Snap(self._virtualpit+self._transpo, choice=[0,1,2,3,4,5,6,7,8,9,10,11], scale=scaling) else: self.pitch = Snap(self._virtualpit, choice=[0,1,2,3,4,5,6,7,8,9,10,11], scale=scaling) self._lfo_amp = LFOSynth(.5, self._trigamp, self._midi_metro) self.amp = MidiDelAdsr(self._trigamp, delay=0, attack=.001, decay=.1, sustain=.5, release=1, mul=self._rawamp, add=self._lfo_amp.sig()) self.trig = Thresh(self._trigamp) else: if with_transpo: self._note = Notein(poly=vars.vars["POLY"], scale=0) self._transpo = Sig(value=0) self.pitch = Snap(self._note["pitch"]+self._transpo, choice=[0,1,2,3,4,5,6,7,8,9,10,11], scale=scaling) else: self._note = Notein(poly=vars.vars["POLY"], scale=scaling) self.pitch = self._note["pitch"] self._trigamp = self._note["velocity"] self._lfo_amp = LFOSynth(.5, self._trigamp, self._midi_metro) self.amp = MidiDelAdsr(self._trigamp, delay=0, attack=.001, decay=.1, sustain=.5, release=1, mul=self._rawamp, add=self._lfo_amp.sig()) self.trig = Thresh(self._trigamp) self._panner = Panner(self, self._trigamp, self._midi_metro) self.panL = self._panner.amp_L self.panR = self._panner.amp_R self._params = [self._lfo_amp, None, None, None, self._panner] for i, conf in enumerate(config): i1 = i + 1 if conf[0] != "Transposition": self._params[i1] = Param(self, i1, conf, self._trigamp, self._midi_metro) else: self._params[i1] = ParamTranspo(self, self._midi_metro) def set(self, which, x): self._params[which].set(x) def __del__(self): for key in self.__dict__.keys(): del self.__dict__[key] class FmSynth(BaseSynth): """ Simple frequency modulation synthesis. With frequency modulation, the timbre of a simple waveform is changed by frequency modulating it with a modulating frequency that is also in the audio range, resulting in a more complex waveform and a different-sounding tone. Parameters: FM Ratio : Ratio between carrier frequency and modulation frequency. FM Index : Represents the number of sidebands on each side of the carrier frequency. Lowpass Cutoff : Cutoff frequency of the lowpass filter. ________________________________________________________________________________________ Author : Olivier Bélanger - 2011 ________________________________________________________________________________________ """ def __init__(self, config): BaseSynth.__init__(self, config, mode=1) self.indexLine = self.amp * self.p2 self.indexrnd = Randi(min=.95, max=1.05, freq=[random.uniform(.5,2) for i in range(4)]) self.norm_amp = self.amp * 0.1 self.leftamp = self.norm_amp*self.panL self.rightamp = self.norm_amp*self.panR self.fm1 = FM(carrier=self.pitch, ratio=self.p1, index=self.indexLine*self.indexrnd[0], mul=self.leftamp) self.fm2 = FM(carrier=self.pitch*.997, ratio=self.p1, index=self.indexLine*self.indexrnd[1], mul=self.rightamp) self.fm3 = FM(carrier=self.pitch*.995, ratio=self.p1, index=self.indexLine*self.indexrnd[2], mul=self.leftamp) self.fm4 = FM(carrier=self.pitch*1.002, ratio=self.p1, index=self.indexLine*self.indexrnd[3], mul=self.rightamp) self.filt1 = Biquadx(self.fm1+self.fm3, freq=self.p3, q=1, type=0, stages=2).mix() self.filt2 = Biquadx(self.fm2+self.fm4, freq=self.p3, q=1, type=0, stages=2).mix() self.out = Mix([self.filt1, self.filt2], voices=2) class AddSynth(BaseSynth): """ Additive synthesis. Additive synthesis created by the addition of four looped sine waves. Parameters: Transposition : Transposition, in semitones, of the pitches played on the keyboard. Spread : Spreading factor of the sine wave frequencies. Feedback : Amount of output signal sent back in the waveform calculation. _______________________________________________________________________________________ Author : Olivier Bélanger - 2011 _______________________________________________________________________________________ """ def __init__(self, config): BaseSynth.__init__(self, config, mode=1) self.fac = Pow(range(1,6), self.p2, mul=[random.uniform(.995,1.005) for i in range(4)]) self.feedrnd = Randi(min=.15, max=.25, freq=[random.uniform(.5,2) for i in range(4)]) self.norm_amp = self.amp * 0.1 self.leftamp = self.norm_amp*self.panL self.rightamp = self.norm_amp*self.panR self.sine1 = SineLoop(freq=self.pitch*self.fac[0], feedback=self.p3*self.feedrnd[0], mul=self.leftamp).mix() self.sine2 = SineLoop(freq=self.pitch*self.fac[1], feedback=self.p3*self.feedrnd[1], mul=self.rightamp).mix() self.sine3 = SineLoop(freq=self.pitch*self.fac[2], feedback=self.p3*self.feedrnd[2], mul=self.leftamp).mix() self.sine4 = SineLoop(freq=self.pitch*self.fac[3], feedback=self.p3*self.feedrnd[3], mul=self.rightamp).mix() self.out = Mix([self.sine1, self.sine2, self.sine3, self.sine4], voices=2) class WindSynth(BaseSynth): """ Wind synthesis. Simulation of the whistling of the wind with a white noise filtered by four bandpass filters. Parameters: Rand frequency : Speed of filter's frequency variations. Rand depth : Depth of filter's frequency variations. Filter Q : Inverse of the filter's bandwidth. Amplitude of the whistling. ______________________________________________________________________________ Author : Olivier Bélanger - 2011 ______________________________________________________________________________ """ def __init__(self, config): BaseSynth.__init__(self, config, mode=1) self.clpit = Clip(self.pitch, min=40, max=15000) self.norm_amp = self.p3 * .2 self.leftamp = self.norm_amp*self.panL self.rightamp = self.norm_amp*self.panR self.noise = Noise(mul=self.amp*self.norm_amp) self.dev = Randi(min=0.-self.p2, max=self.p2, freq=self.p1*[random.uniform(.75,1.25) for i in range(4)], add=1) self.filt1 = Biquadx(self.noise, freq=self.clpit*self.dev[0], q=self.p3, type=2, stages=2, mul=self.leftamp).mix() self.filt2 = Biquadx(self.noise, freq=self.clpit*self.dev[1], q=self.p3, type=2, stages=2, mul=self.rightamp).mix() self.filt3 = Biquadx(self.noise, freq=self.clpit*self.dev[2], q=self.p3, type=2, stages=2, mul=self.leftamp).mix() self.filt4 = Biquadx(self.noise, freq=self.clpit*self.dev[3], q=self.p3, type=2, stages=2, mul=self.rightamp).mix() self.out = Mix([self.filt1, self.filt2, self.filt3, self.filt4], voices=2) class SquareMod(BaseSynth): """ Square waveform modulation. A square waveform, with control over the number of harmonics, which is modulated in amplitude by itself. Parameters: Harmonics : Number of harmonics of the waveform. LFO frequency : Speed of the LFO modulating the amplitude. LFO Amplitude : Depth of the LFO modulating the amplitude. _______________________________________________________________________________ Author : Olivier Bélanger - 2011 _______________________________________________________________________________ """ def __init__(self, config): BaseSynth.__init__(self, config, mode=1) self.table = SquareTable(order=10, size=2048) self.change = Change(self.p1) self.trigChange = TrigFunc(self.change, function=self.changeOrder) self.lfo = Osc(table=self.table, freq=self.p2, mul=self.p3*.1, add=.1) self.norm_amp = self.amp * self.lfo self.leftamp = self.norm_amp*self.panL self.rightamp = self.norm_amp*self.panR self.osc1 = Osc(table=self.table, freq=self.pitch, mul=self.leftamp).mix() self.osc2 = Osc(table=self.table, freq=self.pitch*.994, mul=self.rightamp).mix() self.osc3 = Osc(table=self.table, freq=self.pitch*.998, mul=self.leftamp).mix() self.osc4 = Osc(table=self.table, freq=self.pitch*1.003, mul=self.rightamp).mix() self.out = Mix([self.osc1, self.osc2, self.osc3, self.osc4], voices=2) def changeOrder(self): order = int(self.p1.get()) self.table.order = order class SawMod(BaseSynth): """ Sawtooth waveform modulation. A sawtooth waveform, with control over the number of harmonics, which is modulated in amplitude by itself. Parameters: Harmonics : Number of harmonics of the waveform. LFO frequency : Speed of the LFO modulating the amplitude. LFO Amplitude : Depth of the LFO modulating the amplitude. ________________________________________________________________________ Author : Olivier Bélanger - 2011 ________________________________________________________________________ """ def __init__(self, config): BaseSynth.__init__(self, config, mode=1) self.table = SawTable(order=10, size=2048) self.change = Change(self.p1) self.trigChange = TrigFunc(self.change, function=self.changeOrder) self.lfo = Osc(table=self.table, freq=self.p2, mul=self.p3*.1, add=.1) self.norm_amp = self.amp * self.lfo self.leftamp = self.norm_amp*self.panL self.rightamp = self.norm_amp*self.panR self.osc1 = Osc(table=self.table, freq=self.pitch, mul=self.leftamp).mix() self.osc2 = Osc(table=self.table, freq=self.pitch*.995, mul=self.rightamp).mix() self.osc3 = Osc(table=self.table, freq=self.pitch*.998, mul=self.leftamp).mix() self.osc4 = Osc(table=self.table, freq=self.pitch*1.004, mul=self.rightamp).mix() self.out = Mix([self.osc1, self.osc2, self.osc3, self.osc4], voices=2) def changeOrder(self): order = int(self.p1.get()) self.table.order = order class PulsarSynth(BaseSynth): """ Pulsar synthesis. Pulsar synthesis is a method of electronic music synthesis based on the generation of trains of sonic particles. Pulsar synthesis can produce either rhythms or tones as it criss‐crosses perceptual time spans. Parameters: Harmonics : Number of harmonics of the waveform table. Transposition : Transposition, in semitones, of the pitches played on the keyboard. LFO Frequency : Speed of the LFO modulating the ratio waveform / silence. ______________________________________________________________________________________ Author : Olivier Bélanger - 2011 ______________________________________________________________________________________ """ def __init__(self, config): BaseSynth.__init__(self, config, mode=1) self.table = SawTable(order=10, size=2048) self.change = Change(self.p1) self.trigChange = TrigFunc(self.change, function=self.changeOrder) self.env = HannTable() self.lfo = Sine(freq=self.p3, mul=.25, add=.7) self.norm_amp = self.amp * 0.2 self.leftamp = self.norm_amp*self.panL self.rightamp = self.norm_amp*self.panR self.pulse1 = Pulsar(table=self.table, env=self.env, freq=self.pitch, frac=self.lfo, mul=self.leftamp).mix() self.pulse2 = Pulsar(table=self.table, env=self.env, freq=self.pitch*.998, frac=self.lfo, mul=self.rightamp).mix() self.pulse3 = Pulsar(table=self.table, env=self.env, freq=self.pitch*.997, frac=self.lfo, mul=self.leftamp).mix() self.pulse4 = Pulsar(table=self.table, env=self.env, freq=self.pitch*1.002, frac=self.lfo, mul=self.rightamp).mix() self.out = Mix([self.pulse1, self.pulse2, self.pulse3, self.pulse4], voices=2) def changeOrder(self): order = int(self.p1.get()) self.table.order = order class Ross(BaseSynth): """ Rossler attractor. The Rossler attractor is a system of three non-linear ordinary differential equations. These differential equations define a continuous-time dynamical system that exhibits chaotic dynamics associated with the fractal properties of the attractor. Parameters: Chaos : Intensity of the chaotic behavior. Chorus depth : Depth of the deviation between the left and right channels. Lowpass Cutoff : Cutoff frequency of the lowpass filter. _____________________________________________________________________________________ Author : Olivier Bélanger - 2011 _____________________________________________________________________________________ """ def __init__(self, config): BaseSynth.__init__(self, config, mode=1) self.rosspit = Clip(self.pitch / 5000. + 0.25, min=0., max=1.) self.deviation = Randi(min=0.-self.p2, max=self.p2, freq=[random.uniform(2,4) for i in range(2)], add=1) self.norm_amp = self.amp * 0.3 self.leftamp = self.norm_amp*self.panL self.rightamp = self.norm_amp*self.panR self.ross1 = Rossler(pitch=self.rosspit*self.deviation[0], chaos=self.p1, stereo=True, mul=self.leftamp) self.ross2 = Rossler(pitch=self.rosspit*self.deviation[1], chaos=self.p1, stereo=True, mul=self.rightamp) self.eq1 = EQ(self.ross1, freq=260, q=25, boost=-12) self.eq2 = EQ(self.ross2, freq=260, q=25, boost=-12) self.filt1 = Biquad(self.eq1, freq=self.p3).mix() self.filt2 = Biquad(self.eq2, freq=self.p3).mix() self.out = Mix([self.filt1, self.filt2], voices=2) class Wave(BaseSynth): """ Bandlimited waveform synthesis. Simple waveform synthesis with different waveform shapes. The number of harmonic of the waveforms is limited depending on the frequency played on the keyboard and the sampling rate to avoid aliasing. Waveform shapes are: 0 = Ramp (saw up), 1 = Sawtooth, 2 = Square, 3 = Triangle 4 = Pulse, 5 = Bipolar pulse, 6 = Sample and Hold, 7 = Modulated sine Parameters: Waveform : Waveform shape. Transposition : Transposition, in semitones, of the pitches played on the keyboard. Sharpness : The sharpness factor allows more or less harmonics in the waveform. _____________________________________________________________________________________ Author : Olivier Bélanger - 2011 _____________________________________________________________________________________ """ def __init__(self, config): BaseSynth.__init__(self, config, mode=1) self.change = Change(self.p1) self.trigChange = TrigFunc(self.change, function=self.changeWave) self.norm_amp = self.amp * 0.15 self.leftamp = self.norm_amp*self.panL self.rightamp = self.norm_amp*self.panR self.wav1 = LFO(freq=self.pitch, sharp=self.p3, type=0, mul=self.leftamp) self.wav2 = LFO(freq=self.pitch*.997, sharp=self.p3, type=0, mul=self.rightamp) self.wav3 = LFO(freq=self.pitch*1.002, sharp=self.p3, type=0, mul=self.leftamp) self.wav4 = LFO(freq=self.pitch*1.0045, sharp=self.p3, type=0, mul=self.rightamp) self.out = Mix([self.wav1.mix(), self.wav2.mix(), self.wav3.mix(), self.wav4.mix()], voices=2) def changeWave(self): typ = int(self.p1.get()) self.wav1.type = typ self.wav2.type = typ self.wav3.type = typ self.wav4.type = typ class PluckedString(BaseSynth): """ Simple plucked string synthesis model. A Resonator network is feed with a burst of noise to simulate the behavior of a plucked string. Parameters: Transposition : Transposition, in semitones, of the pitches played on the keyboard. Duration : Length, in seconds, of the string resonance. Chorus depth : Depth of the frequency deviation between the left and right channels. _______________________________________________________________________________________ Author : Olivier Bélanger - 2011 _______________________________________________________________________________________ """ def __init__(self, config): BaseSynth.__init__(self, config, mode=1) self.deviation = Randi(min=0.-self.p3, max=self.p3, freq=[random.uniform(2,4) for i in range(2)], add=1) self.table = CosTable([(0,0),(50,1),(300,0),(8191,0)]) self.impulse = TrigEnv(self.trig, table=self.table, dur=.1) self.noise = Biquad(Noise(self.impulse), freq=2500) self.leftamp = self.amp*self.panL self.rightamp = self.amp*self.panR self.wave1 = Waveguide(self.noise, freq=self.pitch*self.deviation[0], dur=self.p2, minfreq=.5, mul=self.leftamp).mix() self.wave2 = Waveguide(self.noise, freq=self.pitch*self.deviation[1], dur=self.p2, minfreq=.5, mul=self.rightamp).mix() self.out = Mix([self.wave1, self.wave2], voices=2) class Reson(BaseSynth): """ Stereo resonators. A Resonator network feeded with a white noise. Parameters: Transposition : Transposition, in semitones, of the pitches played on the keyboard. Chorus depth : Depth of the frequency deviation between the left and right channels. Lowpass Cutoff : Cutoff frequency of the lowpass filter. _______________________________________________________________________________________ Author : Olivier Bélanger - 2011 _______________________________________________________________________________________ """ def __init__(self, config): BaseSynth.__init__(self, config, mode=1) self.deviation = Randi(min=0.-self.p2, max=self.p2, freq=[random.uniform(2,4) for i in range(4)], add=1) self.excite = Noise(.02) self.leftamp = self.amp*self.panL self.rightamp = self.amp*self.panR self.wave1 = Waveguide(self.excite, freq=self.pitch*self.deviation[0], dur=30, minfreq=1, mul=self.leftamp) self.wave2 = Waveguide(self.excite, freq=self.pitch*self.deviation[1], dur=30, minfreq=1, mul=self.rightamp) self.filt1 = Biquad(self.wave1, freq=self.p3).mix() self.filt2 = Biquad(self.wave2, freq=self.p3).mix() self.out = Mix([self.filt1, self.filt2], voices=2) class CrossFmSynth(BaseSynth): """ Cross frequency modulation synthesis. Frequency modulation synthesis where the output of both oscillators modulates the frequency of the other one. Parameters: FM Ratio : Ratio between carrier frequency and modulation frequency. FM Index 1 : This value multiplied by the carrier frequency gives the carrier amplitude for modulating the modulation oscillator frequency. FM Index 2 : This value multiplied by the modulation frequency gives the modulation amplitude for modulating the carrier oscillator frequency. __________________________________________________________________________________________ Author : Olivier Bélanger - 2011 __________________________________________________________________________________________ """ def __init__(self, config): BaseSynth.__init__(self, config, mode=1) self.indexLine = self.amp * self.p2 self.indexrnd = Randi(min=.95, max=1.05, freq=[random.uniform(.5,2) for i in range(4)]) self.indexLine2 = self.amp * self.p3 self.indexrnd2 = Randi(min=.95, max=1.05, freq=[random.uniform(.5,2) for i in range(4)]) self.norm_amp = self.amp * 0.1 self.leftamp = self.norm_amp*self.panL self.rightamp = self.norm_amp*self.panR self.fm1 = CrossFM(carrier=self.pitch, ratio=self.p1, ind1=self.indexLine*self.indexrnd[0], ind2=self.indexLine2*self.indexrnd2[0], mul=self.leftamp).mix() self.fm2 = CrossFM(carrier=self.pitch*.997, ratio=self.p1, ind1=self.indexLine*self.indexrnd[1], ind2=self.indexLine2*self.indexrnd2[1], mul=self.rightamp).mix() self.fm3 = CrossFM(carrier=self.pitch*.995, ratio=self.p1, ind1=self.indexLine*self.indexrnd[2], ind2=self.indexLine2*self.indexrnd2[2], mul=self.leftamp).mix() self.fm4 = CrossFM(carrier=self.pitch*1.002, ratio=self.p1, ind1=self.indexLine*self.indexrnd[3], ind2=self.indexLine2*self.indexrnd2[3], mul=self.rightamp).mix() self.filt1 = Biquad(self.fm1+self.fm3, freq=5000, q=1, type=0) self.filt2 = Biquad(self.fm2+self.fm4, freq=5000, q=1, type=0) self.out = Mix([self.filt1, self.filt2], voices=2) class OTReson(BaseSynth): """ Out of tune waveguide model with a recursive allpass network. A waveguide model consisting of a delay-line with a 3-stages recursive allpass filter which made the resonances of the waveguide out of tune. Parameters: Transposition : Transposition, in semitones, of the pitches played on the keyboard. Detune : Control the depth of the allpass delay-line filter, i.e. the depth of the detuning. Lowpass Cutoff : Cutoff frequency of the lowpass filter. _______________________________________________________________________________________________ Author : Olivier Bélanger - 2011 _______________________________________________________________________________________________ """ def __init__(self, config): BaseSynth.__init__(self, config, mode=1) self.excite = Noise(.02) self.leftamp = self.amp*self.panL self.rightamp = self.amp*self.panR self.wave1 = AllpassWG(self.excite, freq=self.pitch, feed=1, detune=self.p2, minfreq=1, mul=self.leftamp) self.wave2 = AllpassWG(self.excite, freq=self.pitch*.999, feed=1, detune=self.p2, minfreq=1, mul=self.rightamp) self.filt1 = Biquad(self.wave1, freq=self.p3).mix() self.filt2 = Biquad(self.wave2, freq=self.p3).mix() self.out = Mix([self.filt1, self.filt2], voices=2) class InfiniteRev(BaseSynth): """ Infinite reverb. An infinite reverb feeded by a short impulse of a looped sine. The impulse frequencies is controled by the pitches played on the keyboard. Parameters: Transposition : Transposition, in semitones, applied on the sinusoidal impulse. Brightness : Amount of feedback of the looped sine. Lowpass Cutoff : Cutoff frequency of the lowpass filter. _____________________________________________________________________________________ Author : Olivier Bélanger - 2011 _____________________________________________________________________________________ """ def __init__(self, config): BaseSynth.__init__(self, config, mode=1) self.table = CosTable([(0,0), (4000,1), (8191,0)]) self.feedtrig = Ceil(self.amp) self.feedadsr = MidiAdsr(self.feedtrig, .0001, 0.0, 1.0, 4.0) self.env = TrigEnv(self.trig, self.table, dur=.25, mul=.2) self.src1 = SineLoop(freq=self.pitch, feedback=self.p2*0.0025, mul=self.env) self.src2 = SineLoop(freq=self.pitch*1.002, feedback=self.p2*0.0025, mul=self.env) self.excite = self.src1+self.src2 self.leftamp = self.amp*self.panL self.rightamp = self.amp*self.panR self.rev1 = WGVerb(self.excite, feedback=self.feedadsr, cutoff=15000, mul=self.leftamp) self.rev2 = WGVerb(self.excite, feedback=self.feedadsr, cutoff=15000, mul=self.rightamp) self.filt1 = Biquad(self.rev1, freq=self.p3).mix() self.filt2 = Biquad(self.rev2, freq=self.p3).mix() self.out = Mix([self.filt1, self.filt2], voices=2) class Degradation(BaseSynth): """ Signal quality reducer. Reduces the sampling rate and/or bit-depth of a chorused complex waveform oscillator. Parameters: Bit Depth : Signal quantization in bits. SR Scale : Sampling rate multiplier. Lowpass Cutoff : Cutoff frequency of the lowpass filter. _____________________________________________________________________________________ Author : Olivier Bélanger - 2011 _____________________________________________________________________________________ """ def __init__(self, config): BaseSynth.__init__(self, config, mode=1) self.table = HarmTable([1,0,0,.2,0,0,.1,0,0,.07,0,0,0,.05]).normalize() self.leftamp = self.amp*self.panL self.rightamp = self.amp*self.panR self.src1 = Osc(table=self.table, freq=self.pitch, mul=.25) self.src2 = Osc(table=self.table, freq=self.pitch*0.997, mul=.25) self.src3 = Osc(table=self.table, freq=self.pitch*1.004, mul=.25) self.src4 = Osc(table=self.table, freq=self.pitch*1.0021, mul=.25) self.deg1 = Degrade(self.src1+self.src3, bitdepth=self.p1, srscale=self.p2, mul=self.leftamp) self.deg2 = Degrade(self.src2+self.src4, bitdepth=self.p1, srscale=self.p2, mul=self.rightamp) self.filt1 = Biquad(self.deg1, freq=self.p3).mix() self.filt2 = Biquad(self.deg2, freq=self.p3).mix() self.mix = Mix([self.filt1, self.filt2], voices=2) self.out = DCBlock(self.mix) def checkForCustomModules(): path = "" preffile = os.path.join(os.path.expanduser("~"), ".zynerc") if os.path.isfile(preffile): with codecs.open(preffile, "r", encoding="utf-8") as f: lines = f.readlines() if not lines[0].startswith("### Zyne") or not vars.constants["VERSION"] in lines[0]: pass else: for line in lines: if "CUSTOM_MODULES_PATH" in line: line = line.strip() if line: sline = line.split("=") path = vars.vars["ensureNFD"](sline[1].strip()) if path != "": if os.path.isdir(path): files = [f for f in os.listdir(path) if f.endswith(".py")] for file in files: try: filepath = os.path.join(path, file) execfile(vars.vars["toSysEncoding"](filepath), globals()) vars.vars["EXTERNAL_MODULES"].update(MODULES) except: pass checkForCustomModules() zyne/Resources/zyneicon.icns0000644000175000017500000044050612417524776015551 0ustar tiagotiagoicnsAFil32  i~ȿj|; y-tb Zl| {  ~|$~&  {=  oq@z jny阗sp  jsj v f  )h  spoyggp}mgnt oi+-.--2jv i|؅րjā qٓvmy{kمgakhnhhz҉̉so|‚l jp}~niķړ ﹄ȿF y4t ZyψĂ ~  ~|$*  {ÂK  v@z ڈnyp  j v ߚf  Ƕ)h  ❙oyp}nt +-.--2 ĸҬ؅րā ٓ Ԣ{¨م~swxy܆z҉͌唃‡ߏᅪȲœ ب{ȿzՍ؜B y2֊n Zu }  ~|$)  {G  t΁@z ~χnyُp  }j v ɚf  )h  ␌oy|~p}}}nt ~+-.--2| ҜԊ؅րzā {٩ٓԔv~Ӏمwms΄sudžyt̉҉̋׋ψ…ɏ׮~{Շl8mk+K_`N0\hp +<MfSp6O 4 (H`xMm/[zSr + t& TϞ^ih32 jVQW[[HETf}}MRsg^Ftw<}րс r?2kAp;ArXd.tCLH/u> CM.o74 7 ux .* Bo 73 N ?E q- L] N (s <h ` &u 0K Q /{5  w~ a ;|3 IG &|5d&x-md &y4RQ @[^ v : zr w <  (v4v!"ہv / %n$݁y0l q+oN-4L\5) x/%L ewrсρ\wQYdɀŇRӂՇ pBƂ׃\aѝINmgمNRʂhxӂdTӉقDDCRшYCHHCeiYHgBwA}ևDk\V|į\W| v^NB==@K[qൎna`abQSjל^Rsg]SC}րс r?5Mp;Hqd.tC]ߎW/u> JɌT.o76 = ux ., Mo 77҆ ` ?N - Ly S (s <i̓ } &u 0Z R /{5  w  ;|3 XJ &|5d&x- &y4fa @`a v : zr w <  ɇ(v4!"ہv / %$݁y0 q+˅N-4M\5) x/%L erсρavb_ɀŇg񸃀ӂՇ FƂ׃\ՀѝYPmمaWʂḯӂ詄jӉقOOG҈\ш^ȊLKŌWKɎjqVgKI}ևGx\{į_n С{aMDCK\uفڤ ʤf\]_`NOb}Ü՞XRsg]OϗؕA}րс r?4ӔIp;FՐid.tCWɎR/u> HQ.o76 ; ux .+ Io 76 Z ?K̅ - Lp Q (s <i t &u 0U؁ R /{5  w v ;|3 SI &|5d٣&x-z &y4`\ @^` v : zr w <  (v4!"ہv / %$݁y0 q+N-4L\5) x/%L erсρ_w]]zɀŇ`٧ӂՇ EƂ׃\vѝSPm}م[UʂhӂycӉقLӆLFYш\IJRH|jȋRgHؕF}ևFӗ֨oZ{į^fќ ؽr[IBAGWm֎h8mk  cרo'p܁*f.7VEk;c&E9e 7e;a "PHv]ttt]Cr O^51_.Y71X?a(C#]z {at[ęe$it32J1 mYI=72/. 049CQbxڧ lN9/.00/-.48;;:621 0/.3C\|ԧ+{S7.00-/@Z{ #+0..0/1Cfϧ }P3/0.4U¢mA./0/>fʧ b8/0->u ӗV1/00I}ŧ Q10.8s± N11/;n§K/01N! ; 01/7jP/01(5 ,-1/8q `101% 2(.1/@ }901) D 6&20/US/0. OAi/08|90/[p @ A//Xd00?  t׹F5 t/0> K//g `ō w5ځ903x =01 jÅ =9_Q//d50;~Ὀ;3lr//U}20I݂}A/70/Ky1/WZ)̅,+Յ;00Gx0/b 4  ͅ000F}11D3#҅300I21)FO R0؅:00O51( I^ iۅ1م 51/\<0+n:j,؅/1/nK0.$-)ׅ810 d/1x-#9م7(0701 U+$^:C9):0J91$ I @']D j$!2/iS0-Ep$.G3݅":ݔ/1}/1A~"D9х@$X0C90/F܈#:Fх=3/m`0?olVy=օ1*΅5(/41.5" ]8 QG0VP0TWm i$6E'.0//R Ub d.*;%]0LL0c X H鏀 F-7.//// Inۉx"3ׅ(,8c0MQ0aYZʉ!ƅ0Cƅ0.01.€  d"&-:ۅ3ƂY0Xb0O_!#V($݅1ʃ.38.  Cm  z2#܅.#ۃC/n~.8 H:Ϛ"/؅8Fڄ/AP0t =ȈA28@Jڄ0/3-? ,R2DȄW0dz/> i 3,13.?S0v^0--؅1?څ0/7-t3%,Kׅ;MυN/o.49څ/i/ v00Ll/U`-ϣ'h1[-6N0y#,Ö#/23+4.9-s ;ۨ( A܅BۅEdžN/s//q AވQ3'-·x0Y.@ Z0y,5 · .Cm/Z  O.%:م" -6Z0{  Z 6u83؅3 //I/ 4% k/J؅2 ;.=- 4 `-5܅JΈI/{7-ȅ X :e(%ޅKЈ]/n2-څ  mb+%ޅ;8o0c//  ͈\2څ7W~0Z.4 uFш-$K1\0T.8 Eӈ#=#E9Z݈/O.: "Yˈj. ۅ7Ĉ/M.; o7͈G+I1'ֈ/L.9d0%.+R,Hވ/N.6 .󎑈!%!م4H/Q.1 p1ꄇ  '؅;-ވ0V0- bJ󄊈 ' .څ 2Rw0^4-х  )  ;  b G A= f0h9-}{t,ȓi'/wǬЎ=/zf0Ooܰ-40.΅󁬟ׂd0VI0x|s1/}/5}/A;/ی:/tn/=΍.87/؍=/kj/</78/͍9/mr/4ޔ΍r{/;A0i0/{/.ڞŁS0IV0Aȱ.17/t 4/h}//ӡQ0DX08/3?0P//t3/rȍ50Wy0/aԞyA0Cd//џL09V/1ߞ\03L03c/1G04c/1{G03\/1xI00O/1{P//A03\//`508o00BɺҖ//C701bP//WJ//Wض[403tj103 dS//D C//Gٰ {001h m40.]9//IW00/L=//;{M/0.Y݆://7mM00.Bښ d1//9kç X3//0V x=.0/Atȧ oB/0.0M ֤h:-0/4W̧d?/00-3Mwݧa>..0/4Pzҧ(oM7./0.-/;I]o}wfSB4.-00/1@]ק sYC7/.//0/0 /..2v ӗV1//0Y e1/.8s± N11.B\.01N! ; 01/:c/01(5 ,-1/= }201% 2(.1/J ?/1) D 6&20/mi/0. OAi/0<=0/[p @ A/.p00?  u׹F5 t//G \//g aō w5ځ904 D01 jÆ =9_Q//80;~Ὀ;3lr//l30I݂}A/70/\1/WZ)̅,+Յ;0/U1/b 4  ͅ000S11D3#҅30/X31)FO R0؅:0/c81( I^ iۅ1م 51/wC0+o:j,؅/1.\0.$-)ׅ811 /1x-#9م7(0<ٖ/1 V+$^:C9):0X>1$ I @']D j$!2/h0-Ep$.G3݅":ݔ/2.1A~"D9х@$X0O?0/F܈#:Fх=3.}/?olVy=օ1*΅5(/6ڏ2.5" ]8 QG0nc0TWm i$6E'.///R Ub d.*;%]0^\0c X H鏀 F-7./../ Inډx"3؅(,8c0_e0aYZʉ!ƅ0Cƅ0./ɉ0.€  d"&-:ۅ3ƂY0q0O_!#V($݅1ʃ.5އ=.  Cm  z2#܅.#ۃC/.8 H:Ϛ"/؅8Fڄ/Lc0t =ȈA28@Jڄ0.Ʌ5-? ,R2DȄW0.> i 3,13.Gi0v^0--؅1?څ0.΃<-t3%,K؅;MυN/-49څ/i/ v00^/U`-ϣ'h1[-:a0y#,Ö#/23+4-ȁ?-t ;ۨ( A܅BۅEdžN.//q AވQ3'-·x0r-@ Z0y,5 · .O.Z  O.%:م" -:s0{  Z 6v83؅3 //Y/ 4% k/J؅2 ;-E- 4 `-5܅JΈI.;-ȅ X :e(%ޅKЈ]/3-څ  mb+%ޅ;8o0./  ͈\2څ7W~0t-4 uFш-$K1]0j-8 Eӈ#=#E9Z݈/b-: "Yˈj. ۅ7Ĉ/_-; o7͈G+I2'ֈ/^-9d0%.+R-Hވ/`-6 .󎑈!%!م4H/f-1 p1ꄇ  '؅;-ވ0n0- bJ󄊈 ' .څ 2Rw0z7-х  )  ;  b G B= f/?-}{t,ɓi' q5//0V x=.0.K ߕL./.0M ֤h:-0.7nʄG./0-3Mwݨa>..0.6b(ϖ_:-/0.-/;I]o}wfSB4.-00.1Iy ɝsP:.-./0/00/.--3C`se[UTW`kzٯļ׀ ¤jTB:2.-. -06=J_w Ӳ[=/./0/-.48;;:621 0..4Jo+˙b:./0-/@Z{ #+0..0.1K| Ҟ\4.0.4U¢mA./0.D| ܿv;.0->u ӗV1/00T ۰^10.8s± N11.@ۯW/01N! ; 01/9۾ۻ]/01(5 ,-1/;ں s101% 2(.1/G۸ ڝ=/1) D 6&20/eڴb/0. OAi/0:۲ۥ<0/[p @ A//hگy/0?  t׹F5 t//C۬ V//g `ō w5ځ903۪ ںB01 jÅ =9_Q//zب۪70;~Ὀ;3lr//dԦ۝20I݂}A/70/WФۗ1/WZ)̅,+Յ;0/P΢ۖ1/b 4  ͅ000OϠ۞11D3#҅300SҞڪ21)FO R0؅:0/\֜ڻ71( I^ iۅ1م 51/nڛA0+n:j,؅/1/ۙW0.$-)ׅ811ڗ z/1x-#9م7(0:Ŗڥ01 U+$^:C9):0Sו<1$ I @']D j$!2/ۓa0-Ep$.G3݅":ݔ/1ڑڝ/1A~"D9х@$X0K֑=0/F܈#:Fх=3/ۏt/?olVy=օ1*΅5(/5ŏ1.5" ]8 QG0fۍ]0TWm i$6E'.0ڋگ//R Ub d.*;%]0XۋW0c X H鏀 F-7.//ڊ// Inۉx"3ׅ(,8c0Yۉ^0aYZʉ!ƅ0Cƅ0./1.€  d"&-:ۅ3ƂY0i܇w0O_!#V($݅1ʃ.4ɇ;.  Cm  z2#܅.#ۃC/څڟ.8 H:Ϛ"/؅8Fڄ/I]0t =ȈA28@Jڄ0.4-? ,R2DȄW0yۃښ.> i 3,13.E؃b0v^0--؅1?څ0/:-t3%,Kׅ;MυN/ۂ.49څ/i/ v00Y܁ۅ/U`-ϣ'h1[-8ҁ[0y#,Ö#/23+4.=-s ;ۨ( A܅BۅEdžN.ڀ//q AވQ3'-·x0j܀.@ Z0y,5 · .Kچ/Z  O.%:م" -9k0{  Z 6u83؅3 //T/ 4% k/J؅2 ;.C- 4 `-5܅JΈI.9-ȅ X :e(%ޅKЈ]/2-څ  mb+%ޅ;8o0w./  ͈\2څ7W~0lټ.4 uFш-$K1\0bٶ.8 Eӈ#=#E9Z݈/\ٲ.: "Yˈj. ۅ7Ĉ/Yٲ.; o7͈G+I1'ֈ/Xٴ.9d0%.+R,Hވ/[ٹ.6 .󎑈!%!م4H/_.1 p1ꄇ  '؅;-ވ0f0- bJ󄊈 ' .څ 2Rw0q6-х  )  ;  b G A= f/=-}{t,ȓi'..0.6\(ٻY9./0.-/;I]o}wfSB4.-00.1Fp ҶkL9/../0/00//..3A[|Įxk^UQPRYdqܰt8mk@0@@oP@@@oϯP @ߟ_ _Ϗ0@oo@ P@@OOO@0_00@__00_0_0O0Oo o_@ _0o@@@o@o0@@oP@@P0P _ _ P0Po @@P_@@@ O0@@_00@__00_@_000o @ _@_ P0oߟP@ϟ_ @oϿP0ic08v9 jP ftypjp2 jp2 Ojp2hihdrcolr"cdefjp2cOQ2R \ PXX`XX`XX`XXXPPXdKakadu-v5.2.1 uLLi9$mì#S.Nxg ›bAC!6Qi]˦"MɸbN旧tX~ ,4]QNIq Ō_z&^mp .{yQ(WkAIv뇈/~힜Xw{Twݠ6WPbY:i?0S>{ЀK9>3ۑ ?n0Ce@ǹGunov3CR4*KE <dC2*EKhWfxB;Uj{k W;vWڹYH_ZмCaS_TgMbL$:0wlG?u@@|6|Sd¾#lb3o+";,L^:\?p˄l`~ L %.n. ,~E.$M'7sPWx3ѱbI!= ڢnkjH?ar_6݆c<;PJ* B`~/1B.?QS$#Bqw5fJp"̾?~7sRrzp 'VW ?+_Йc vTށϲdfIS0NCž9^\70إJW6 B`HU*.9S“mV#l6,OJ6Kٓu^ΏhC/c;ζi7h>=[c K "7]8Q(^5gd_0~"wO.^Xĕ%{`CQ?C xU Z yK ج>3MϩOeRte7}LKWϩ( OF4|q80yS13 ܿ՛^6Ol }.>/Q(6V9[1C/cI@ʋpjf-6/ۢ٤VMM9tjk&Ւ`1I{P`T)a[i?U_zz2 6%e|U5uHM/Fub%3<_yI㒬 K!#Сᮬ^;shqeV$ŰdCD+%Gu0Ī/ Lގ.޳ Sp;S!F$߄[m캼LZI5P;o.^bQپiMK(Pq0z29) J%bڥǮB.nX\rgEVF`Ïy DW`tF]Y\l^P8YSeK2{N0K䄿W71P=8"&~睱NXa'CRrm5;JXh&%=nA9 6m4o'^&_4a9{\6,n΢M 'i˯VkwQ%rLVA|qB;3V|s١= bEb$v9M/0v M:fl꘷G .^ڹڸU"TMmN&PQcHȮŢn޼ா[J3xp.|[1WOxD[7&aRYv\tE5(u S3/cSmHx {֑Ί}y5I7CJ&t~i&OQ(tX>S*Aq^ W2pjJ|qB;̠ؒW>go5E?d0 <&F7[‰:,W"bPOҘbc5nV41|r qϭ|=VLX^agVb]3԰a#Nf"k-/B.vQg0AQ}f)@Gâ30݄p"$ȆgUKi߻!˝sv= k .qT >,2$GJ2qt [&O9!uI@3j(#&@m I9dXl 7?LQO,m *j7!SSuh(aɍ~K@g 2FWA7Qw'OTc.ĊIBZJ/bmp49ՈnymϪ:\ϧа, ~_QT3NML50zdNK)J%WZ yOfe[rC('HÈXӿnwˢ_4)$CV/qX6L]zbF*+f^BP(ђ#ht;;ɟʔG/$Pљsw ڣJp9DkNJY~C7'F(1X|JYUkT #.|] ;MBO&TΤrF :GV; _8rQt<#Q @1AvKi?\kMqٰ+Ura 4H6mYY54ch\59# G\K^YQ",f*QD=O āl{~_nϷ_i F>) -[p@zwg9b{ǨYW.#niN?<e7x=<*š2sl9IZy,+E Mi_T]-8&vuF KXg^Pըj9| [Re= )`}YI@U;p 0QkUfAeWX$}BKdhkE/.`M2ZUL4OR#Bgu!n0mND%j5s?) [֏ׯ'; |-rFA~=.п }/ʴCaNXfBcf-f>LUO%ݵK*o'ӓ#֘i6ul{J/~.;>H&`/-nCf7 K2vaDLB߳ovh`,NY1rYQa0fҩe Xy^T%yE E XNޒV-w)ʐ)ajSOnG53uey7$o嶒UK%QOfmmyg{5ye{8@W;3`bXfQhUZ@y,yvH Ζ ( q-2{Nȕ^(YɱkW1^NQ|0́3!&`SKH? By;˟~XvAm- u1MO2ٌN[tV c:Hf4'HPs9oP?볻U4;UdZj3jO$jf57 tZ-1qMcoU]0qݽ|^^ C A'6#618W'{g>uEV f)MH*^'^'ACbh scP]Fڥ=z.[j §[azR2P5+8kb++N_ֽ?…)43Ĝ_kMb)׆nʑBNO;4>TŖ*Zg+&0ܢĵ[Fa"M9eƭuG )V>DDZ/L`F,vd[RCP-JBgJy.Ohkݘx&lG0O]OƓ `8$˚"3&˾.V8S9GfQ^, Jr ڻeG#%ٗ K!ؐ} O v )GX 7Н<(i}PH {Qz%-"P^:9I,5L' U}Qc\U+MDKB<ΧܘP2CQN˾BHW]d։!s1 Xx23YE4OMy!IM)cJoiSb)=x)^;6NDiWdS@5Vkn"2jF U"_=G k7F|-LppB٧kA/|`Ⓕ[*s@ Lfs=Aó+7ǼܨST7U䠡x2 `6vh{ IPЌB=xnF,mHcLnsК 8%v&lI-D(EѲ?H.=ɝ'\mǁK [Ӕ~qeܝ[{(-=b^CRR[kb~_#R~`T*\X:%q燩`BGS0zb[eHURE>vZFjaNbx`;cSr?ҊD?~U-f}~Q2ޟ<yr߬/OG+c z-lp1+ƅ`9 q4psXV1#:Gax[A0o=;?fj. W&Q_F@:ɻ2)lҙ0C-l$OfU_*5?aK:lldGsR\e q <*J$X\,; #1J:Kɏ1O !ٌE1ks٢=2궉ЌAjt:笼z#+ќʹrX MzgN3D{ G\)%D_VFq00rIj#V[5#lTѯ9 +[L8aqԧr~,3VUCM$ܞe#񯬶%K~5}Bt܍«wME`ï#S5AUcS)w O: iU+f!1XŒ I̠TOMw*:B\h,Σ?lo_R$w,4)E(!U`E5`?[7xXm"6Ib'L!'>6$N{5 @y ߚ?٦R˂}$r:B- Џ4o.eS{wOM9V"%6a3ty" {OZYTzǴsEyGWGF(ڝYE<)E/t;1FZ8vẔG6-zl™youl[j^s8Mn;]VLPRߖٸ1ΉŖ aཔ ) wVw=Z*wQFmQ)[Ax/vϗ/[/Kr`T M>2*;[#mb߁aNѯ?x1ţ; w |5+%[݅H&ͩYO3Wc4.+R^iU=ApQq{:΍fXF z:7cgJ PCNÔ6R)Z3wRB<0E?G06kXX+y!κ0b/sGU5egv*4o4af`;g]v-F.jvQmkf MCdO`O1U1b#zɄ,k"!#1w(p(_&{vѯUsF$ʳ"OY,2PB:qd Qqhut[,1g*I/۞hM|3DHfw]:dTa:A[z|"E241OQ13I@2&c%^[5mV Y$exNƦHYķ(St"!~eWډiegPvVxXT؏ :2H(?M"`0<˛ bݹ|(dn&%邷c,#4AF@dY{U8TSE3~LұR)i%Ut,SixN>'Iᦸ8^ H/DW%UܔiFA <10n >}ž83'^nbmW]12ЂSm҆L1W^U>c4Zedi9>t-c2+s K$-I렀 Un)|6e,["J3;c u RW;V/pޒ^7S2G9n)iKd1fԪ_.s:k\AC\~ >лFxω k C,n1*08/sZH u pTive7x, Q rB,&|ݘLՈcċ_\%nNuӠ%r@}%ח+Hc[6mjl!IæUcIo{2X;4UеE < @iOX+5+7X&Ai;Ҭ@G_LӳmB1f4|ږO_R\kџ/Oh/F@bJEm˷^CU$3ӫ~חoR{%˿5DTX1|>BL&p0~w:?o(ΘF VlxlpLdsoTewQsVpk뱬glS +̜qSjm6luMX,,oЅe3˃λD歾-v|%^) +S@տ"\z.ZRGRdDqIvoT(U̧Q$+%o_b) W\ňȂ+*+ѵhof H@SΤܲKo0lA@yo,h 7l+jts*-xv0=gެCybgCQm^ -Fm:Wvֺ"ts{V]uvQ]Ƶ1A _hy / y?fCra&it0'?YRJU&hIiܲgJԋbBi,gr*x|ٶRP8/smל 27лQ<2w n>r̾(sodմJndNeu埤f?xͿjBV11`]5TߢG'.E梉5VVc.Jܸ[ Iw lqƞQ $0 ?s68+duRsb?‘lȳnMfPIXœ'@ l>lqX#|zC.I8nVHRcUKd*ĞijakjJz)OpŽv9ԸaEȵE9et\Dঠ9;gV!VǗxC@5#C0JH:闀b7wӍxū" 8Q<  ݰT'kDz\8<`D8x%m(=63Үo0 X#9[ **_籿e>ÌkC\`v7~gESgä쨮 YDJOyH6=HQSO p *I89юA% O`SkP̽I0H&qw4}L~$"Uס'se)CZih5Š/_Ŋy]n'( Om1e|a}jXxmh\MG'k^dDa!QVئDs=8Wnv ZqrǸ%_$Ҩ4:UC ô?sc&t ' #L8WgkP'?XXeJs#b Rf:9QawpImDy5S).mį>fkk[0΢S'/8vfCtAVQr~+RIHrGA(KD Ray]_EB:<S|0'Iz G>]xWQhzsQFqJ[y~H. /YMBk#d÷񌕇9: -zVQ7" vDqȝ&WVoIva|w@S̸+P =kbPj{fἍDNG)Є, 0.g:pЇ)D% +B0wgQ1LTz [k+ ep砘<^} .O 6)ADmy\xY5^P.myjOY_^ -hW$r‰h .Lk?]Ⓟ+["3vd-SO0!p;±ۂnGH%JrO.9BqzyJQ+sV+ j5F>0ȭ0)Mظ}ձ 0 ?0g{XO3ߚ|1f22 8U!kIwǮ$۫-6'a$j<،Qta7ZT[I7<z=[Q15xi_Q3-BY`A4=pOAݍaG?wI <o3u b[- s:\1ZI,+_s4XZ$ȶVu, GRyfvC|VD!IfNq׀C*&3&h^nijKr!v<(o?-l.!}aJFV:X+ 7t{5s$|Oi(Gtf&MsԯڰVWUn8+Y4y@ځU)@}@Oމ&uLeEj:uVQp&X?B Hla鏁(2wu")C( [B(h2~'rxY$5## lyy&!4zuh'cI~s2qm yy0Zg;*]"Zn¤TR9þ4s`2+Ha8IҾm,&qp9LT˓"9rT#2KB=zd>"Q+ xxYL >;'{*_iie !n9 pId@@-AH;;ЗV!DJ2pŇd"U {*~$c7ّKjYQs[S ʹ=>@a] Cي'#- Fr_$Dk gj#^{Pu#-h ok\hJOBOa..ECNiBWۧw=KxᎬ:i<)oM* O=ڔK"?L(e%&w@EHMTѕ ִv#E.r|/q¨u>dN5 G5%h:q^ 7'N0=&B Zⅻh\ĻNkɤ"8m_ 8 C}]H>rRee.ZC>d9_0chd4Ϡ,d!1 Syw6S\VlbCP }`5YHZF߱2Ts(I,(Z{[izm:I]G`W!>E~8UaMxVr|y&*S|C!(6l"|9lTq?̌בIZ y#߃,:*Xd"D3|ǐhE.O`+mi_Mx( "v2~.X hyPZ;qhr?HJPP?XŭKo@Rk5DOr^/B2c&*Cd|p؜8l= r\<ЯOi$nBMԼ:/zop0|q\=@+kNưB#He\4n Ĝꀙɜrq~o!'V˫)֖R]LGm>k"=g\ ﭥz<1^SMvUUsñJTCu?Z<"<.H\n: xin]3 _:ڑAD]kI5XMwdpĹo?3_p1fpVvCdAGآB4 OIAZU47LfIJ¶sծo7tu䚚CA_^9cx'6E>Dgj`BBLM႒.G T iV2W$lcNwpa曗G({5d!-4={z`+dAW!~_?!u[֧;x)7P0.0~&["Xwm0Ơ4y6hNvL8 {7)V0M R皹+o!UcBݯƟi!"O_t!Yg6NyH;`Ob8"N8E]mbΧG>!Ws<)bAȁBDe$4͒* үΈ?=e>y:xIZwYr5!_=ELdt9fFYR=|nD( W' (Dc峞4i\I qb|BE|*0L@Ur'h>~ZӑݧCҞ(=8b-M轝ģ#<p KӞ{F4:[̒;}X m7#LG^rplѮycȸAy1)AxZtj|:Dd:(R|)6Xfp귘bVm! R>}Pb\F?(!aNGзA)z:Gp`ι8I;)CJNOA WWa$j(g:H ~A) ʳ%-QzC$~;:]o&<{ǘYKVTKc4_1RuS='ĢiHj;}?oV_|޾o?7֯_{zr{:Woz2r*b?rB~|D;0e{10:;?engй\fu„v}9N=#1݆57gɷ#8&e-]zFe=8 wE DlSϸlS KםGE\E] =4LzN*MD;lE /ѐ5ji&߳~],)y_o udAuDsjP\`#|:fνM?i qz֛CSraUm Ͼ&7 2"$Z rJ#f3coP`A xEVan$2٫=z{:-ׯ0`9{B=@Ph6(A2nեǻbV){Cv;f6$2]*)$B@(q8LTM79? GMn1`mS|cB`3UlXHu<z y;/Ih[<: Ad;W҂4sB$9W7@:JF:fNYS-QJ޸|?GsP3擗d]#V"k8&Ll]4fr1)l{Ѥ(H7o 8JWAZ1u4:L&zK"E"(\SE:݅AN'K*2q< !!|nGqFJ[79EVkgpV֠<0w!</B.!C hn~SaZd('2{)0~ct_&kMLSnԇtLOegKH-Hw2nnt,h]wԵ!oQHIEdQJKϋgfFJNjhS K',G7꣺~uM,ES ߅۬/NKׇm]JXi 6IF%V{S&䒘3nƽ:}׌:zL_D_MV7{;&@|]+ɼAwqP[(xa6x?/c?9`sxX.;=oXK ;s㭴xc__4hנ1vQ_|%bKPr9Gt鐭ץI]Y6i 0| q>bˡys%L%)$oV˵ȞCbo_G}Ԧ>t3!tl<<, +5r?(4A*h$ $АI`$^5*ЖZK)`o"?mUV`$c(D;k=.DO`d_]Ov301,*9y%5Ef ݹCGG>=ͺy JՑ.&!7(}A"[|>N-;7wM,![5!ɨéVe%x;Hb@[x ,Pe!KfB,THs] @"Zl٢o1 Va!Uɮs*6 Ng0j,&噥t|s$ cIdnI4QȘBKQdp7Q, UpQi"ٵ0ܲxgqZ!n61xPb&TýdtjH hko>̉S5~%TKpBw irf"y n@ mÉ1-:f̗w} ćU& G5nFucҲ/oT$K ai a;^xucT7xmgGĄ"Bb˾iզ[V2eDkW@VF|!$X4֐6⓵}}BYWp`jIi`bŀQi`&.j.l~p;ł8 d~<1t7~>Sr'-R"+-2i!AqG|a?b6ZJxK\'$8*0ȱ1GJ^ܨb51t޻e^M"sPYզM {nJa3w k^a#/l!ꅫL Éw:y՝{_[CGN3"9IzCElG=pcyn):M1A7SJc1-8ekzI6aI)9~][kxDyP;⑯*Kv)k/BuWDoùCok!Āou  ;v_LӔQhXs@lGIl Yxmvb#\~en8sn!1 c3Sj]hBRvu 7liݶj\ۦa^d< (V6QWij2$sa/G%ifpa&ͼ:FOLM)3 w?ݜZ w#ǃ'=w#3S;aST(r B`SzGtRwhcQ=g'ӻŝEʳR:a5]mIǼQ3x⪣ou|4}9ID9A+kPxMm9αd.d~%2^{~\֨iiRM8*;PƄ-z% QX߬|֨Ӊ]qmE*[Vhuڐ#~;nW~Uon>9]g:5Ju-\u JM@qxX)fљj+2Y 淪ZMcM{z/[ҥ.2)z}@K$-ٓ4V9" @Nԛ/5zqcqe!. -Pp3$\ 9fO俛M'^Em1sAzYʣɺv'Pͤj3MF~'c6Gv} |TUpEfmZx]'ؘc{Xb1 xء4[.;̒OG\b5M$Om wV*/#P$uv9RĀh{숦ux@u $.Q ÛwNRnþUH1Ƹ LU ~bw-%ANz AT8O&2S.`C̈́p`-fwA1Zng0Ss=ZWZ^,B;où'@܅Y<24{#+Ը|wp!jhqXj2)ngׄ-bY~])5+E:'Y-oks+n؊f5rAw=}D}U./7;MX^Oxptv@q`a.q,bg [ĿZp6)D _wX9Ⱏ,CeHő{+\&!/#3rV5Wܢp-*'@>Ź@L-'5E0ĥ"_B½5 ڶϜq9\s{'ekOe:imV4LVd Ft'ucE2ЄG;HX~&jevJ[`(ܫWP M!L@W\mݔ9*ag$}IQ񐹿oZ[S8;Gc+"Qz3 o8Vʌ;K%j:{޳iYy,\Vgsr"]hw㵡x/KJizBC$%䷡[ִaN" %2 afK _ =amesvԆ -W_tK0ЍAXNYq>mUMXh^ 6p4A9Sf`NiG{^aЭ][KPmQ0ۋ}Ѝтj7yʯɁgg}zRțΥr\g^@'m䃦izLZ Tx7v\/’Nb΄ٗ{FK !L&Iu/}q20psʂ,ªpW IZp;L^ 58r$-آ˾zYNYO?2%ޛ )VYQZkb^,_:JTB]窂;88_Bg*&p0i1eaƃ aEs>TP/$0 jL*26uV:TF/2OX&g-xpcL ~ߋdSHcr bfqʄą{iմ:.l wIl$8#V8FW/E+ j'>6JK;`smȲQ$l׆N6"e+o lˌ|96:s2-ʛ|5 `jvW |9H! vv[P=F8"FZl^_8;t37Қzcyc뀑QUoEmK~hPgIhImo-m;angie 3wPqkC3s㽆 ڇxt2BSK>WXlB{(m~ T:ֳG:pT,p^[j)M>hsͥ4,.%c1)V]vˋJH"Zx3SC Rer=_&-AQYrh#[tdwtCbquQ6 .wJߞ3Vzǽ:~-hW4>iH\1[fBn]#;aB%(cx]~B B.,j}9R-M @C(Y˚5ФURtQJVO icoIejvԒEGk4"9eN> -Cb-@$pcݫ {OJܰ1 86_5̰>q {60$Lŝkzm}пtEwחFk皼63OC4-{\%̨' ݞH#XL 2h:w^[iބ$tc^qW`kg.+3M]ҺLε qQk |q9<]`|.#sc śq5d9Ja2$bh`$ ln7CMATjTd2 {XLNOIG0uuMֶW"̅:pTs$6̈syxl^ ׈03xSەNԅ0. E&c } 5(I{~~dYht yku'~q a ~F5İz dy} zVqfQX4/j(U;I]kt\pD򆦸 9h*AOx~r?ujZTŎao@ܑor'uĪq&8qP53Ku/f[(Ktҝr .u}-@X[< T~Z7$_Aؼ2VXbLtS`F]q|Μh=<(=Xw2X>P"$ƛ,h2DzRQt[i. qq'edFDY@&RHHDٗ5<t.R7cF "g3XZfD|{sxr&\cnS>R3j2̠PT-[(t 0DqN(cPj, I[5k?Е.fm]F lj!8bFǪxd`S 7% JwQiM*n:Z2[me8v$|ד83j?R~׿RE^ކ[Th'>l׈KL3tN%fdnR&1:ᖿv[Ȉ4i&2u'S[^OpC mÜb4),#:r荪W'>Rgy4y/u~iG Jm8AUO_k@n+u-Jw:n*X/T|#Dۨ#κ |iD+Nsӱ՚D(eIuh2^CDWwľ =.P(g N~!a'k 0ߘ%A(WM2ѳ5a, ._'X7v[5fFKe2˙\'QiyGn )+Y???@60] GX-ybFݘIAvCjoAi!>OŰT'[`(cҰw1cC]ٌx` *q4BX'(,o.YٺuKVļGW$Ǹ(*m-]ujjͅ rIA1{2$9J8*-h;\Amez-GӼsh8*B#Gn51ϗWN l?`zN'=(lLр/D֒F} $&p$o nOf յa<29k[L? X_~Ȑ>L<-$Ѽݧ:\:V*"q(r>~M34I_&M%_ɥ&WjPC_JV>4ҁ#23C0fY,ёƔE$,i=AhQfݵjCio1?Q~Z ~3`pf1ãܼ7lt6Zqwˇ0]HHdeط8[Vf\}ܹYE%1@<{('B [/po0,+@f6TfvjD8+RՒN)|=8b`OqKB#:yM"`, 0s'Zݰ@>{5POD~Ƃ.84Y'O>Ƥ&իEXVd'0c |GJ*T.JK #*wubuW?2ŕkOCLҰ%=En:W{[ 1\I$cP9!rXwL%bl+G+1, nSw|[(Yplrnlp Rht)Ib2?xZGrvw. t<_"D6 uMh[ů_.W31O6Q95IJ̫"\??cE5\7?*X уr8>o䏑s l \-"`G]@՗fw>X[Z/2v{1[>@!/ nʭRBC+/S-|J="~Ob E! i}w͟0y .ܴbos Q]%f8zh9}yBTLh:C_Y"~p C-դ ˔[ Zkig##~ŋ&@1գLb*k:BBtMꅿ^Ao_|A)OK˓ hėnZwk(W҄.õ ,a 7j> @K+ 1D *&! 'hLxu]uDq\6ws~RU]&M/ Oh5}ˬys#uz~|aK:sP NaHye!HBm0D.R/dSȰ 堌$I`(%vt!_nng`0YFpՑw 8[u 6 [W:0m}~^Mz! ~SU TqUCuȌGvɠH>24݂*Gp>B*^@+B_LU Z; R~6XZVB$-%*$!@ &JkEN ŠZy\R;%xO_~??ϰ?>X:LR?C_c!'wi`M[MF47/"(6. 1 ©~c};XsZMGPc=6q-2N$Tмl9"=T A5_\!NZoHʛ`RLбqVmIZC>]YF^%#lJ;B,2CH3~=Nq!m7~b%.g.5wÒгjsV*z틢QDNbH-޶A9s cn D>T;e?Ķ@ltog,1kښ Ebz?u:-5b0~` |kJ nEΑcca'TNd?b&Oyvjc0hl5f'׉5g5QAT(X+D AvQKT)9}k%7Ʀ{Ȼfe&~?>uMm0d.u/w8Jڮi<=ͫ[ߒBW$ > 4k"cEq8 ˶ $ 1i+!Z \$u..5!Gmjv7)w$RAqz: N|*2Фg1Tʃ0Mo V.'* D#] ?TJ3s"$9X\n 9ڀq?UT;C[]o/=kEƆyqbؤTsyK$Ć.9u~$ T}2C4dxh`Ej̴iygtnI_?-bx?zyj(մx)FȗؑwCsIȈ7#U](,_GiO%@`7*01d`X_t80pCKNt72~k ^ۆ(p_ZNnywp<+J1f14S(je\bݮ^u,/TIc+~Q<󶨰!+P& d/_|!qE'#L߇Уc2oz+IOHFƜ,aQϝ*F$20bA篕柒쒋o}R7d%J*C$o;T%KkCz4xRs_+M /jy@+ZE^D`aW[U/;n0xJI"ŋs *+4E mU i#> -8}_xhQ9ex38*kU> q%rn|dң01z~\ -]V z -eџN`!C٩㪗Rz tͳj-||}>j^ys,7s[c' Mxo֏x"!UOsиHF6o,U6"q ƁhH~Yߙb2e'rdZkC90r*/\OAEp*[RQ%5JwĎ`Pz{9՜oi$K"jFL8f ԃ76_ /7,ٌhaRLd ACQXbn\({LT_ݒuaɆmhgӧi$t]WFxBy+;E&>|?te@Dj/'`_ ! q|gdUpBP4̰f+fm^Ch`ntmLD|2e"KhaBn=;w\`0#A:*-%}lP1NEz$7- i[T0xVm0KzEE&(5Jw|6'\sR%˒%bhԓRSRi^ϟXE+d9lifoR,n\= P~ܐy~6iȨ26Z$S_ы yYLi;ai4븻FLiRrd^=H7ݤzx k(u%vDS[d1v"jGa5,^~e1,ɩmQ4ռ90@(!O+ թuHCtхsli/͍4蕞2vO-A3‘H83L> [){@fO@BٝҦ)84~w=pN^Nߖ6]ë wwf ,6U?xֻǫ=k@w!EfoJ/3zM/4N']몲1c/bB/Ywc SaP Ls/B~;Ab!vrXΣ ԍנ%bu$꿆)p,C@J6a AAϙϻ ~V9>$s'%Q=z''=jb%If-^"E;Y5TX%]E{(v~ GGX8SBPPI:Ґ4,8EvSt'0s4^JpW6M(TcC;nwI|xsG%@RЈe\/E`[[o)nKe%wa _X*״yyΤ{'AA Θqmo48 @g~(Y_Q%DU gGB|A0K,AAd*o dx`dBT9p~$C6FHޔ{Yg.mny/-hA GjP[ pNC-}AT/dU70 F8/$b"KYY)eNaW'9FY@K?|w%G)N; OQffqWe8O@s3ouFĠWډtG>M 1=e };,4εg۷bTZron2J fo3_xP\bǜ&@xNBЋ2Fg#oYmn #х W iDqx܀ͅ+Pag2jO`zoN:eErI׆T`qwp,ɋsq2F=/YUjŰ-ţy{]UBqW)7TZ[nsd+nP98g3SՐſ*@(ˆʍY -ſ"7xxsryOhmz@lNCOPB0Ap'Gskώvӏ)f vjR@}/ ]RIqqwjKynfok̉gR55*aiILɠ5$ϛp;0*fK3O!'rt*̔I5ٟpp6+*B b'_gHHVNRD`9 `c4Āqr塿kͲ:,fE K9^{ݽl"fOZi,\}q&p)WDęXfjabӚ= 2 c#"%c]i32f'4 kHO6@H9$vN!W&S(Bͽ W /, 7I))v?{ћszqDÛ?> ]Jɩ50_ pPg58iHydic09 jP ftypjp2 jp2 Ojp2hihdrcolr"cdefjp2cOQ2R \ PXX`XX`XX`XXXPPXdKakadu-v5.2.1 1@,zsy7!M4U56["`?ApmN/zIB/Zr} ;$ԚM} =*ASfږN9+F&cyE6@C]fYSwm>_efڸéßok7(,a |WhwKoM;zRg/I"ħ7 ke$6+j#$ PCp7EAo^ۍ?eEMҢXBZe'NiU@P=ύ4^D ew !4{ J/3O; ,~%"XPvT'@?a5|rP*)ZLμN3$Rۙ=,L $*Crڕ63쾮3~.n_i -ht@l QOgNgo_HO*1nn~F(´U0vw! ' DV` S/Hæ 4YP^04%mC #Viѱ@4Xw za4"Ux\'Z"F%{/JrxJiL yɵr<qa?0Is`:vp s)Q͒_p]+j\!Ϗh+  Wݵ$V D(1Xz;ܹX91j^zu]H?Y8tToV;K}lsD4,afBRx iEa1T]8lQ[&\(t|/'iT$eEѧf13SE@ M,^JKonM 55e+b9OG 2=ow=f:R7Ҩδ ȉ56avZR!dz͇Rz,pT=z+vg5]0%H6zz:9jIWU8OI4iJU|qB;3V|t~HDi.葢8Qg5% bck~Yx-oZYQQZYu??&Ud#1>yi4jZ=a44PX4E.o&Q%EwvԟSdӴGJhP ,RUmU{'`$[!rW!\LOwyO9_C(uu%dtq :78x_IT{lZ_r)Rxdc.2՟]i+I},:|H5I`ϫ)P, D^f8p=@%<"Lέ |q#*qB&טvKi1ZdmbU(G2ϓȲv7Hg@j kQbt3w#Raژ>k0^˼=,c!ϏU , E{D%UX8AKssO0QeM{ݺWdVˁ&=D?P`R7T J rMG"vӂ lefh?r\sOU#ܣkylkJo?mUxDLW^H"D죑QC C|{Opn=gD}*f ]3,OקuёQssiPջ_K~wu;НC+|4@v-ņ!O]B; 5 Z`$O˵>F*gGD:q Yyvٽq(rGw4URk"xF^|"곂[3gh-|cs|K#5_:2 ӫsEcmݙUZk)$ _{Rf1`DdՁ_%dфCz#v]AnFsL %iCnI2TRQ멙eR~NhPtHyQ.in6&VG0yb=s8& s|˹80TnDe짏k4-q4->^xr"~=Z<'Gd0 I*x1xc-SudFxtt.LnK?M(qم99la_A\-Sf1rl $_@Z@iXeUos>M r ?of/<}7'[! s"o~9 nV*{`^{'!j r1].U:VFr&A}7;ȼ2I׉b)ڌ'qV?֑>no.l/dP]d .=22(RԓТf ԙZ 흄2f\[ejA)EZ !wMQ;$(@`9Yz7qɌ BlP񳬕%~p[A7FN̖L/e%QFk{H!"=έM}9[}J9.q%O{-m!qGOŋ`$˩WzSP5Z끦67x'v;̩[Bv.3O bbA/r8)SypFLꑃqռn61Y1mt=q-):B#ވkvHf|lb8Zkb64ȑps\%%=JAH^dD K3sQK=U(o&O* Rn /y+ 7(biwE6 H8J'ZN 9hQh1Ţ)8}@" c r A < mԶAsaϙ^8aVPp/~ W.qa\Uy( BRWj%xUt+R*p!YNzcQ}3Q6+}G"h^#/170]ڵŭsDΑḰ$Qb<ۨ%1K?P>>&"jd#%5\-(w HH#-| 問D{qE 67]A9,\uh_8Fw~oš "j dTE| }t!]`gfg3YyyF5uhav{&,D]c^̍C U%6_j(~!Zq"ҸO @M\(D|Y.吜mBo囆?;4Nd5u߲ y=;g3?`!w}k|'*g=l%wJ086E`tO\ .v+q43;jDg&fWDt GK$WS'BHv8/å;f,iu.@ @4eY ȃhs&[t4 2:&.}\w n7]$$)Ijulyp e 6^'$o'׌Ꟛ\!ؐ}(`y ֬NYӭ"dylнKܽn\WM=_rQ!Tl۶:ݵ$rcJN$''#Įk 'ֶiGL`|:T^&o}_zz0h+MJ8H:#z7asJwRJ˩7&˜ymݽ+zb<;\70 )1Bzx-J%9᭪ 1MBK,2`RK- 92SAJxakq7 ;|H ]FJ-V}3~ꖸKs3q1]M3FMl㒒iv?хSMk_r]~Djb1飺>>#t lq ‹uTК h^LSi&t׾׸Sᯪ"b9k є,u^C!{L@)swA nj4O0Lt`Prbh@b$!b-`֗1ߖ j5OPnw {ݯ)v~~#"192_+`;c!At(2oo9GGF?::djo+/8S9Y[ (S8+$WQ%cTz je{%nC ?Z$ᴷ]M%ڊKk2R$<~ ;F}!/:wghQ|{y٦NhϣW"aV#6j=ѳw rT*Yx}a^9uY=* Kb^rB@g\#@smsv3r&&)dEz&1emoV|p^Y^7t&x&ZDǿ(R>M¦ԁb$ =3~>Tc=iZ,w'ЉၰϯnXnY"Ȑ!07U>Y$0!˓uyA6 zmpϏPD%kV7Z*IAa6yXiL"Dq"+AHS ҩO?^[Q1Ct8tLVmժa)̀ c&T湏qRȍ8ylF8ڄK \q=)H NGXd\0Ȕ ]3[Wkx4kO;K- (+j}f0fR3XVq  tb pe}M8ݰ*JsJ0tf}qacCx&gIwn-5_WEAɍ?^ 2{%m nR"9":K:aMmmq.PwJE xwZD8\0Da^ol 3YV?>P#=@F d[]3F}-_a(bKO[ٰVne 1zAmjJjxo3 */>r%lYG- 0KȫJ|0h,l$Hx1Lkv^ #ˡ3qwi ^M| AzR-vpl,75CFba>>#0E7{0t-`\n4=rT' }i;ёsBEUH4)epc7k_O6ac o*1tyM؝m!~ Z Rd-+#V86Ci͆mm3Qasm%[ZH\.ydݞse軸ΩvC19` ڹOu3yD"_X_i.ZD+3ǩ5-4#X'$=Lh= bH|%"RA//{K^LK4 0gz9(~ [`NpkhSw[89JqZukQLqbZIԳzc$fP""Pht$pT9X:ǘ@Mn(ƻsnfj,|Pr&ʁ5 +r_;$`yXHp 4::(5&PrЧSOpCUj@BʞB Rx{9-`)ƅ늪T,{}<D.O9'hMD)g#^֧ t%epƂ%T>D}տj0CMUsrP+қPNſafH[Tܲ 96 .i==\h~ 3и_] Qǀ,B"3iؿ%n"WV</$8DSu1HTQXO[^bc%YN5iquOpɚcM}}cDHΕڴjPXy u[@ 6d33,IH+8F|V$)6~PN:pe?MoKxZ;"v\ee"-N)ћO/w9nF\7COǒE)HiA36A8J?\8>EZ,ByAiɏ(H}=D6wl6-S:IQ;f๑u CƉ$]+Kg7ï}ð %4"iq,f=.fN|*|KV(?[zHUdHuEB=yȰ_Cf{*fh,5.͝OZH{Eq.mUJ]V- R[bL@:L3fPe6:V^ ½P]c?p~^Ya%hq}_3 QػĖ`Qo[]p )fXk^1:l8{SM<.=Ҡ^u+,$f,,90 W*:fpռZmx  4sD+n7|\t(=:nCϵׁ\F2ig|GSBa9/ʱ?4ґyڔŎ֘Xf^9BC2:;Q9ɞs ׃-EqY5+kZH|W! ۪`;Ԍ!>ư٨egQC .ŀ E\A#0#h#Y9hQ oMc[e-Xr޴6 m@oV8iua_LTS?H.1H4Tk$쭎g~B$UǾׄؑfm\pa _" gÙڼaT7f?5ORV ych>v" H%һ_IBarPZ9WsN\_ ^gD]]UK, sGCL<Æ?SJSFR{|7ulJK'~|NNyvV?4'zzm#{ϳĭI}10LuF {@Vj4C.Oq |žr'"٦ߘR-Q*}氆~je& jZZ#l$B0rQ)# r'Z"j'y;tnҺ.2qޜܕ 8OyynW?~$aX $ëΏ[[k{fEJs8qn~v1>:93w5I"-[ 0= F)anv,Pfqt^+hhHAsE.(,:5ڽVm%vMDoH6Ťc0 .g%yõ GW 7)Bt&"n#$B*OM"_X(rsCKzڥNJNňBi:Z߳w̚^CoEnh6(-ԧA~ajB'_o4H{ע^1M%MI9ShR*$O<0PhMb| ^Smtŋ|C9"vIَTK|:99Fan$Ċv~Yd έRg!5aHNh V 6zDl3T'9u;xA@ojGؐkʆUg+(Gqd&D>?Dj3$\:^'}6^a` _YS@Y%p:"'TD=EO=Hheyflt Wbʭq 6UwRy/-͍?I"BY^75bn8БN4M_1T O0{-8}|FA5 .ڪ?rTiB3o Sۘ$#Pk긲[Gzo@#kr\+DM>I '3g>i6r&1 r[tolGQˎ@ÌTb_kʛ2Oʑ @$=kC9&q,^7{re,jE 6R5{{E;Jv1 I#8MgVɴ{`5JLe͒[76,%&:)blA xPMV>izXY~ 3mecQmDbăX'ò]:; ُDנa9N"sn0 T3P!V9 ͺ(倲|k6ɤ:o{!0nkvon_@^HHFPf䗹dyu9QVeRA\RwN rXE]);\;dܱ{LLW}:SDMX(y_3l M9VWO2٭x9ڐ< ,/Y/{ƍ*̄Aȟ^Jn3#Av@ϢKD-~S]֢j,|{H O7a4pYf=5NT!|Iv[,&ҾB,]tdL6 .X%T|n }O?فra3E90hx]cZOqf5b=nHm/8%^j{*?lӻG^ӌ%hS vʮd-Vة|$C&֙& 9g.xUԁ-Ϫ'5!]?7A P!LI۞8rF\Z)ʌ̨ RuyQؼ(ع XTzLP]:3v`}Q/U3Y$EA%o Lg4tx9_V;O|m""p ::]]Ia{oX>B(nAGxH;;\^EMf"Ȩ ʪ)L9!14ܘ^y"<rj{`o ׮/$JߩS`wűI7CwΰtrMK N Z ]2i.JBa\8m!v-;DdjZV(h 18{$w|]TmʫagCz}~гiȍ IT>}x6&azT!)9>Bˢ=AvX~hWSHil.)`_pV,/o%I Rc]$V sYрz׳-X4X S/D?z7xuh ->; pUT&C85"xsW5 <$2+LLLuF"o vV5hlM.gGY4Ih.vSTc@`J6STsʜVϗ#>O-F'MaT{qc}>x2?$ Dx܅ߪ,B9zb#kH[{ө5i^.}~ 'a/FI*)([t"kbWD O!1uP^(eL0i"ӵ ގc APg[w\=!{wjQý)1A'+ ~rrYAHi56B}ReOdcݹWr`"tdJòn[<(RοڇJCqQ а/ c͉== zJP'`CC1ז|jKG%{Z&\" \yWs;~E> E!JQEz79/wLvr$R PcZܲk;5Yw>n0^L⢴H JA T^JP!HwYi5Ȓ 3Dk|.La}o4uܓRF>Ey0*̊\^cnQ3vB<_N*ͣñ>`)e-xQ:\@% AOqDȀEO Gs4tv@E ekWy;)֞ɢ­X|9:HpM,M$lx͎Ͱ mbʎ'uMI(cs2pe>H&jbQ/pR{Q[+14bdGr(R@T%T*, dHv6~*ĤN}uQ8e\C%L~"0 57!I?L,~r:f ܙX";ݭ)zo1 3WS&I)C rbxt6ˮ^`nd)Me +G9./N>  9[B{O]\:RءYPm u.H 22FЫ<5c Zժ;8XLTWYNdeg[Tr˽װ$C0_c*T ;}y*!Ry Dؚ~tR!|hJp1C[u [/=ZgZG7g&ᤶf48^Iڢ/>RfC!i @q`ĝ.*ކj\$[(%YK@eJiU0k 7ar>^w\W A!]e[Zv)؁\#TϺ]tjH&TPԤ.b]ykHoЅ(u0%HWoB Mm68lQRNd-!΂g$nYЊn![ai u$`A;.]/;0䙘xnmO̫v+1tˊETg%d :~yWINi~dZ 2$6kîTccR0 /f_҃኏ߑt2uG?ۇ}X#=\V9M΋`ofZZȲ71BmUi՘Q~pی@;j[*ƺZ1GIGϞ8W缳rٰBww+\hI}ZȠ 6((s|yZ8 3PrT>"(JB\qvύ# ODr= q/]"D%yc{]MMrZ^}MPNlnm^ K$fEA<@4E¬ڪ_},x}<1$(,1)6 =T٢T5I*4h7[MׅrfW,;UCo|xߣ`(590k[࿔:gڙ1SqLֽd{4kzSɪ{ĐG&]!"Y9wÑieH%Á\8 UTb%Ε-#\' r֥5s$lp)P,{9{WxG/BbKܮoP6\s\cKqxGY!ngcn*(I^v|K&I2UdC="1BZYW9%z{+0T*|Pg4HMf(~Y|E^4eT}ëDFJOppڃL_9Ϲňö?bLAjl/[gO'vr7[f+u΃Xêkd"/4`|Z(F|}ɇ΀4WM2n9ouEڎ~~KJd=}>*h~ǻC&F}6aۛēOu-ʠxH2޳ۆg~IAKF4Sd yn)-=ivL nokZ| ^'9gd#\{ }UvNR@ݱy#r >AK!?ypʹ̵ p4ASa^plOd喇Jg 1d5݄`V`f3{ir ;AK}U7TSS .8Ec+-vJa]rEc!]MV+79E՝a7z̜Ĕ6O*fmL^_l>-_A1~Ī1/[J3/6 'B1/U_0suRr>!::l0PA3{>b^~ƓO>gn[uU`U4{hZLIŗ#If2H/X^n,?o=36{6AVr|'3Sʲן~o-,(. ]x.ճ9͖m9JSx6y2~ e"F .PY:)3ZBr$ K9;/^dVq㳎n ; G<^]{?Xb~)#򫴠7H3Ev14&繿Zlұc{ {KFVi2CF1v8kD,jݻ5|ն-<-h svT"BX{[VqҌz|!ۮWv$DۆG'Њ0lafxO `فv걻`p}Vyc2GF0uvFW7ZKÇ&4}nJ'ۄ6hRy `@+ټiٻu+|$o@.ZveӐ <ģgŢ*Bh'!VV2!¢CO 0m0Ƙ/l M6raE1KxT=Zz3Q r:sw5*8,rWS*挞Pe`d(,#;UɄk ͷM Pܓ>2džc8\Arcmم9ULS Wڻfno8@3\6ewoV UT>j J-h{ƳsoiC\؏{TYVX=2Jh'Vb;bxQ1<Qs͊,e͑Wl<+F8jCtwařQ>h%}=! Pd e%40;u@S$ 6ڊr/_s=7N)9ÍFIEuwU1Kn_jpR8[9 \bRH$t=ehQAb~#䶅53bV%jYb}"Wf3X`I:c* G)gvŷV;??s'h3gE(3L&Qt2zFAaLͫ$X%RV8ȾM.Rdl!-c+A?8SǤˤE m6/5O>]TMT/'-%ʚE}ⷍ&oz ̱]UWj'ۙ5d|G1\_~F_[]DOȎK-X|Q:,C~V˱! ,E[DxbpMsLsWEWwOf[]>OsZa'gy/#A=ޛDYqtr<\:8Q8_MnOq3RrI ,pF(>f l@o[>u]-}^}kPWQl:p\LjnJe,~P\˼cT } u{&J q30eFHnټ7/3hv8뻘!Y7D wP(^楥ӳȔuu ٹ'fF55L!ڇ[XnZxΨleI?TN9Z*E ңLU^p@k Qr|6|u5È#|Gd𖋁ޭx|x5^D&+s~~ҁʈZz,"O`a?G2y3o`uA [Ļ{2A5G<,/kҼGcWבhd^ȶP@بd[_'u2ݜ촩0;boz>2By}v, qdfÊ 4rɣ/=y`/7(;^ƠO5`tB{C ji_Mӑ fu*as@=)kG2:MUr)oE(QQO'黮.U)cPj8-&t?1B0){ %+eUKfǦ+ԴU?aK ױG7H+}7## ii7 ӥVKv$Cm|oLJiOyDb c{_) Y)?BܹC;B\~v3}JJDgF8F4:¡923/^P5gѳN鳹 >8-#麴;D>ipr h#wa5' K7GH !w—mŏ\R-r:൲?#k$ò(NLUw){ќ3յnEmyrLC+`r> mm(^S'wkG~c95 FDzl* XQD~ZI[#2k $ HDcL.sYGp R6$Sg;UMq'?~xstbBZ i I8Ghy<6\ 툉^*|W/~>aGANB8i :`"HM2\"'ԷnVp _Ĉ-ZEߵS?vta$̜Aqajج5?F!JѶ;(WgK >V ITNS/.KxgZ0eH,HŠڷC}%qmZ;yULj~@ a(ϡG(lPl }ͶdcH hb{A:n:.8:,};Pe"ʐyoԮ$JDRK!1IeMJ!_- jU:@HciwҺR~SLb?Xa2C#{i!3%?9 d7%yQc)ϒ_ңN쟯xȉ@ҿ>E@UE*|ӟGw7)LyF̿u`wl`ȡ&A\?qsMܥ?|7Yq#FMc365czL&MڃEdo"2JbYGR"/fy4`OBTǬA+'D.UPIF]aFp/*%?xԽ颼Rsyܙ聢H{|'u~WJ-Mn B! IU!:#24 %]H.|f**kѯ!ǡm:P w )&eH}WyQz5dP Q Bv*ٹJ|׷Hߌ[UZ DRhG`P@؏a@m@ |N44aB!yT6Թ\NFnp9NvG"+hBG$C4u~lq M}3`U,1.߃?5]dq(̗jɧ~G!T.BJHirֲݜB +VҤ!|z^sUc/o]vG6m㇕l̰&.2uY ]6X' Hs5׫y(-3Bn&'\ \F,hGToij he*hr&I”qAM7KeCݎUCřRIrAϹ-PD|h[+14qW$|lŸ݂iƴѸ26"fQ)A>*8قA)9^(4Q.9FY&[uWV4Ҿ.x!~u[fep'b&qN︭L8PK{*㟞x-EmyUkmPnJ;IPoo1QH+b FCDWF Kkq_T%PG1v#aY+AV'bk#Z&  ۼ*¬v39P>`U8BuO; 0I@>T7`Y6K&j$N3n4Vĝ8r,v\9)Q:s/pӛ8o˖$vΜ'^1cq!/SrQ[pk9%.ȧzvUP8]S: /jΠJOҭSK: hLjd"SƩ(WK6 .8$@cJ޵P3IH&*>I?!96o#&4겺ej{87 X+qYs%I-ô\fA*j>Ј4SNdFgH/7 #3{e*!$rluAÇa;i\EK !vǗ/& Go;tNa?@pKr:`f%'& }*7iin0]mEbO.}_.*< ?&wiɫp/N&SlӿLM75D>$鋛=fqrF0-β1lJb,ENbkRYgk SnniHMN 8؝1F5E(].Fb\Bf{ˍpEe{CNrUCit+acR<ȴ =1d׍MsH,QvǗٿLT3ܯPTc#JΎ`މ%*P{dgK^`mZbtNhڎNONUϚǬny?*QgPhr|wۧOT#hjE:O h.xW׺7*&aLܖs_8uaD}D$]A;;9wnm&3- '|M/יc)A3hF%`R0(4o>3hyaHj"P[ S8hxHc{Otෲ[D_>{K 0q)z+|9K:>%I7PޓqhD(HvC14.:oҕx,CF]ظs[d]F%Ѿ28+=PWzA@y̮SEgiT#Q#HXO3| QO20U8ge;Q] *$\vWS>AԌ*5&#NI5Grb2?yN:O؇eI;jTW&2Y$Gc{r} ]yT$}tޢ\g D/yn.?#*[i.n]:fOηaƨdur! [Fb5|)^W|W׸Cz7Sƺ %)(PvoK`7a{w|'1/i&own-בѾWx5 ΆS }k녊8 ̬h+4aAHtiqwqM,&"ja10kٝ\fhB!TwfM1y'fs@5,+3qB`b?s:h/O'\԰f׮/ȻM0 qjP|[aL:Y^ eWdm>nG\ 9걖p8fA%^1/6I.^bƎF\B[c°y!)3"|TpMQf;- 0C HyܓT錁jxPrDR[ׁQY<PEJZl`(%[(Bgn+zR՚&_Ty- fI?(þk:&᩵ԑqؘl_potQYSAb[@jX6r`sR"tK!ƈ({)ruȌGvNEzp#|1l41(s \Hg֑[$OӁ5KP uhJ{Iyê[=Eָ+4{:pe ?EQA]}miJ5:=BԴ/񇾵|,60 *cF_,:&\($1 i<9 [r..7lc rbz9}o=pܼ!KB^ #ΐZ+!ؓ&q[G9HhFAW\RZ*.'py]٠#W?U|Vw?U?f~}^3O~P>F"?tDXz7Rl–z\sTNwVFKXHAN+JznƅC{`I\ѝ&*!%Js7t+qsIȾā_z`R ڊyxY뿄?3/qI>MKaR±Y'vFkL9%i LG7a.:2SNPW*(TlD5&ۛuq`p̗?{{LZ#A9N\\IKauL17@!X=N4ED*8Ml>%YR?s{v48>NRD` 憲gZ+m $$CSM,pk >u)_\lwۆ1D3Z>J$X$#D)r;}ihOu.alsei];{"# C2&]&k}Orb%c/(!k!U F3M)`CixݹR%XP*;{ L"p_a{&Q 88\pZ0g.2e35= <SmsZ#-{(N fWaeU1꼦BES0YI!iwt+q)ہC7q&&=;ZK&HSjo`@; QJz8/ 7>o[+tMz!NC_ialGLIff }w`XΝI"s'SvT8r[M|Q ZǑIԁ 'K/.Ο9*qÐ#tMĨrZe .G3dÌ%_.1KZwsV>"foR,n\=6g,pU-}ЫEc>ۥ dR !! =(-ҩ}xI@~)\Jxӎ!7g~*I2J8U0dtsZ@ECrbFK =iT.Nf ha6*0{ 2Ao#`~] uʊk!O*װ`j)*=ZR:G i ~uz,R); au9[!,H >(5ŗi[ =n/巨%Iy|ۨBJݩF ,WԲyv|>hԟh5~x ]L@2#>/Ch bfd{jRgar[𧏴b^ iW/ay~U~Ko? @v[txʥHhpF$~h1K|d*GzT@31I6}䃙µ[FFO?RAz|ˉw9w`u[Bu)Er"OBvlg%Ir{F/uәF6pW#!e$EcS4B2>=RkbgU_H@zK' v#jXպ0jژj[m;Qo4k /5 fdXjrx:Ws^I ~6_83,l1[=ꜙ؞Z i`Z34M9鬐 0S˯u8&;%@RN8myzdrئa9Di%Uf"2O%kxiyAUK-H`s"ܧR+5AMeQvǏ9Yle:4*2$/kɪmiP}ݍ 9ːʠAX!HkKwNxNQQɴ!5(D7(](wW߹%XyE$)rjy>њd,IJ =#Ԭ`MZ7_QlݝzS20(c@ըgw,U &*fU2xzoX^ ao_iGc};(~e#Zm^i41<^olUnKPӪ1WL^MR&R)^##}vTp'F˽2G,sƱR+9'Ky b#zdf.>BLY훳,jfAXk96`ܗZ1a?^j M_GL?8ЅSHN%c]œ! 1p)NbʿgI*(1I+WIq/q]"LOpiKq |=vvgg{qwmbBm d!d_9ryRshߖ)+qL{2LUڰtDLZĥB $sH74xo,PRp|\\~9Uعܠ-t2ν&؈dΝu鰉CL O Q+vrL4Mj-,(!$j-s(fX@UAʠjfbYGa+O%8hcSC=DZf9G{%Ic~V1-UKKB58M||TqYX;09"zT<7k:`K]ɏшR<2|nRq,_ )*B T#꽔c%}lAߚ;P@rr+"DIğhը6(ŀqIqwKڄ)P4{2]N4:mf0ˉl$NqLUfX3|L[V((fl!XunE,&&0#X)wciy]ywiC3W.IHѧoXQ:K))5\\}\lc_]Ϸ(7vݎ@u)]'wzJ_ڇ)JYC2|WO#]\-:>ᾬ!?5/[NC@䌯I xvXX)6[a-}"Чe`.HzF-Wu\{cL$vO58,pEKYxTt20W;N$%JXK]NOG7sᾬ!?+ Ն^ȟȰH*we9+IVb_ɼN:Wo;rAE1ۧPJŰKae\gVܺ}5|HՂ[ N՛ħyV\9QK7B E f(Ial[bQݦV3 }&D1G};yhi󖤡g N3'hb45CRo@:Ÿt==]ÙeO6MxBr,]X}X1W$Ũ:&K#T#D_!Ϙr$"vI{mHhNkLu;"Y<91N#-]sQ{ex5 T[A5yQXt%"tkCh3KknDQja,Ol/-ps9;wܗzT7۷ޭ7Tcۻn~ޚ?;~K׾w_F??Z57|~~~~~~~~~~Ȁ>,kෳS\B @ r6m:ꎆB{fJgn=Q˜Y8_00 Ux-Y``qE!ÉZ_)+G\N>uKEBS^{Rdp@?kG:j5q@)U&"Q)O/ZS;G(M]Ff6苺Kh-] 핝?04Vz @ER O^s$,Ϲ(s4IyùK9?71 fw-5h_jGKz]@Pm;^KD˜;5xMokB~ ̩pX4S0X$ڻ&<q=wǹʯ˜~x5~E%+<"Ps`,a $ YB0tj8p#*8!a %<跕9Y%20Ƣ/!`bd .CDzbڳm4~,۟ԑGT\p_peN@VPEDޢoFyw$@Snpڛ'p,}'RzJ#<&QɧE&I\LLeF7!cʘ'a8q^i8'XE~ф*r"K0]vJY a81Μ ]^ݔNO;z@ToQPpB<_&BA@<8#I K>\^´"LMhHBZ/li7{oo 6nQ3a s;0,)x&X|ۛ&3R6ޯ߸o=q6< ڦїm|.=z$U2bvq"I?ENI%1Jije9 (RH M3-wU`Z#R-gpV<2A2o%+bً֫WMMsӼ[ӽ3d,eGVqPR%5*1>Ot 撲 1UȡD E0=$"& O 2^ jc5A+N ֑0>%ӍCWg=K~??WR=!7.6OKylo7]6 P6>[]Ri&?ݭ`ysn00P؂_5I} !=7I098?NX ,&RDVS:*ZgQ١_8.;cTd* -DU"MPN*W!XPܩŰ.yDxx-ف:x*4)⋿-1h>tPnVYL_{\+)v&K/sfbK o],5=n/`WșZ]gW/`]FHL毚9iF.^]^ oL{|&Ey%ZG6t__Ɠ;I`|e5?9)vkꔽI߰HօZ9j5ٗcze2}ϡ4t]͘l1 v#]Yذc\{wNB)Ò$YԍUs4a 4xC۴5^Kh>;s=WH̾* Y 56< T]}9S0c*8[u^h63{'leKḡlV|v8%NbxE=P8i4`et6.{gKeZ-fpy]\Ls!NZ$ ѫG6JkL<|Sњ ӄڕrݺn9J 2\^9;"'/KA< PŞ1X~>*TK_̨V/>Na@ux ,pdj%0hHŖC؇4~P(^/cg7*=;/uHDH n8v%:P@,{LA7p)6^*螸BT\=7 >\ΥV"?=Lposu FGRA 6Kfq"'Z¼# )3(SO!ϝk^7qZ' %GhX$XAH=C*m >!PiAmބFc3Ȏ^*6 >K"zuŶhE젃+TJI~ZWa`c=l= j>Zzi/Q,/ L,pwpi,pKt?.x; U8XEF1 _z x v"qf aT^tPX`k3CM>/+&{HFuC]]ҙjt5F!q{Q[g[Ac<`.XIZzj%T+wQlLS_&ix~D^r~XJ,v K htxhe袡sTEӊ,<# CKi,|bչ;v6me˜ȀPtUm9t%ÖRTЇgt:"rH`5tWx-7[⇅g/$3 l c#$5GAovd˙6Oƣ; n :I J\C30r(6 TӴ/oC4j437#~GWz>IWv壤Rqg%+Dif[+W, /0J\՚q*ȼtF,zb Ɯ: rK8 X딼f,y$ o8|8ioM*&ɴ3g~PyQN}ZYY? bKX3<9hVC*rBYw!ԯ }'tO8:@H ,5=2…}X?ڗUIKcP3W|R m%.cKΪp!ElY֕s?JzjɋϿ;]/Q6襒Ӷ/zSM sNT̒Pe/}<}Un0",ipO6gAl3EܪBeue5[i\>+(M{Z= .)z ;MM t C=t2A0C+mSV oꌧIm KaR4x)ʙg6e=YJu'05@wrf kxt<OR-NȘVy݀BY);4o6ѬU0 KC?j[؁Y7e$#ڕfh A+uxW_MOЈ9ېGU/+WjbJ8|qA)D::[BaBLʋ?2x '̊,feN,}oČ#s;]B95Å5pfYC!Vt h^&3{?~~bWpLfs<$}IA,qx\1?9,m;U^Bʔcs+qHaDžS wĜY|#[V:(b][rc!ቧP2v2G r.~#塟r}G} 5ŬWSk(rKaCK4ODt&L2?~WOoQCS{W 5 ['o+VD|^n( pEƏ1\UҊMw" [-b&%RKZHop^JeRk&DݦhmFxMDK~1>O4R1Ӿxsl۲^VI(M}h?buF;葈Rgk꾒g,f[<ޡkr&o;yhը-:\чql2dZ"'<<)$wAw6iuE/ l Y/\\q5I#S( ap#!/M%"ɴxO~0,yG:39'Jơ69:Hj-9 )RAcT<`fVFr-&B j]qK>5c.Uًaf9HIn8p YbʟJ>%-%*A? {i߼bX`Χk12K'2 S#? M0rnzr-!qFIYt0m9mmxfU1!-+BQ::U3h"txO9RX4[|zSkxnQ bafߗpJ֜,gD vz$-=t|BwNx]=ey 5C#/z!F߫NIȝi䋷2NmYgkl4̼f6J2q FyHI ~T"KcKc5f8F:LǗօ}6 ?ILflBFl Hx#sV?kK=ͣﵸȆ16gcWLW3pz#JHQ۳wm_Z j@YݱRX_J8WQe*wԓ#ر@rJHS Z'{-Cp>@gkMˌ0FVP%%3LX 0m jyiGco^R#?3-e^ׇN. _u`7~݃z&x27V/(=}v&g JROfS VGC0ML\~@ \ȅ_P^48| ۪_0wnT5X]tMXl@4n]W.ExCq#]0Whq0 ra\,tݩ.4I= 5m L p s edZ*|:LH|Ёe n7R>0]X=ZB\haՔ;vӕ?|wFeO#p[ąRPn<:-6#xN`qqHc$:zDK.&yŔXSr 2,0= zx)&뛫"jNsؠ'Tf-/a<gjQ>wAr@ܨ1rVW t_$u#+35YSlk{sA-/{JsƎ`u"SzKVO֎5' J;LvhHD``Ę?t[)ᗂ#L1ix;{hGi7u. Ý<usIz(JA}54z+)c`:By"8EM ;=R(7,`dby[Xl*w)f@,j AŎx[\7b]r\|h/OԖlM/mڑ̙< &:jtHHL/% Qp*A!4殡湎g[TVe\XV+F~O/=}UXXI gt-ղUOr$nN=V*6̓ /^o=f˨e+w3r3!DYV218wĚl]n|PEw5ܽu7H.%}xWtI,?ܐ DO"Ƀ2Tq ){&^\Ei}-9<:jg˲5qĂ(c4[F6DJYГY?^ R82m~I#ϙyрCS!2Ԫ fq.b֒j自FDtP1MjvY2pʆzR3)G Ÿfb!9Ul˕CamnAqXku&[MZ{o7r_X4jLˍRaPT=X\[@/C-:&[履[NDPfnxucak[?7$]Sޢzow-> 5ms_ocR5*LXM LܤNC%=Q{[+ýyCȽBh[7qwfMsI-D?n/8x\XNafMZ"" $Yw=xxC` ̋0^J(bZ,tk~8) 2,{ cP}dž3COLn|h͒7Dz\G&JH"}pwq/r j━LjfqΊ_cXQj [tbzK9~ƭBh=%)$Wp7|Drղi'u PNt+l18N^WIUb9|mV/HuzSå h+st7 }\_3L6("7Jcvխu<zq𴣞%i<l\F<4eV(vzGQgNu]'Gw`;4L{:ԔGbsP8I vY /'UjH3HMhz׮Y CPTQ֖  v)X[^A.-V=7 #P^v^kC^~~bGC>RW\ lq$0hc ,+GCIКDDxO :3.E75>Jdw)PK8q2Wc^7 dqꎬ\v.f:у>tE[MW 3@cddȗ"Ke^ .IEwց+&酈;f.sJg-$xLV=:̣|6“> y|F+؜tXt\)s NqT gpq'@"w޸ut[>4>XoRjU8+)Aw[&u^98H)WR%;-aKQQpqn=/x(|=iV\I:m#.羈E)w94dyJG "}%9X"_(&:q^R QMtQٴ4 y߭kB1aiDwҏɦܮE8וUMZvC]{͖y-!›SmWԬb[T|Ae(3&pQ*cYN ь=+(=?56Y-e{)ޠ A~s 'Թ=SAM>875VH).hi`ZК/dQ}hfvGy6HebK3%/W\.j*a1v=9\-Tǐ|ftkܤB-@zL,k`z;1h[ +@>~I$E@:xX)n<yeW#F+vK?LOW%&s\^|Q K:@'ԇF(|'P*9HJX^˅({esi{ 9Rx g0 =%IHL *qU'-<6 E?IObPlId> P 8f0#;¤9v?S-]W҆o+|t7u)x@/}?'V3 emƑ[s-SCNN>5!g)1AMDkq(z]:;~~)Ҙ(]Qž?twt:TcM})ܞK 8+jiV/J⴦@ !%d%/Z$\qSⱒlo1 Z`ՃkOF ټZ04dCS&ߖ*n;)Yl@\U!V?g#_5/WO4eGZhT &;{ :ǕxLU f?@~h_@(;A9Y\{!(Z+%r@.W/Qh#᥅PfXwRLs&ME 1QPJӢ 7LWf}aըR86-_x}@DF%OfM3T h̃ǜ,>E Ӗ=,:n70.s7t!->szKz-ANug ,r|]IObq MoH$!/'hpCL$0B} R{neʔ!0ѭb [k.]Ho!n!JSak"G n+ R1 ;_OUCC&!'ZCWF[mj| i8!5CP.41&@PLxd.X鏲sG+V,rc[,T [ʥmUO>ـT֭>B<}]]l8C=+ Eiaҙ1!fЂAƼ&? tijJYL[ (o/\ 6iqEn8+Ovv9KTHړ:kNV#JA\jߛ=,F^x,!ae9:c|9h=3 WA]t:k}CΑANC6;u|I s(+r?b-TW):L]q7 X!tsq?6cp[Sʘ#tfy!`V:* ΢a}zTm*xV%HZ4:={LeWSgL pi/Eӽ+,Tv-M Hs|qBלjBAVmQ]YgTp#-7brG2eUE= "=2i5,[k+H`O'r/'*ߎYϒ|u6AOw`&͡gzVvxLZ4r'K9hS?}{ ǚ^;nN>Ęֽ{ǫ[2ቭWZܸ|)*N%K1Nf+ڇh&ХchD|2$4WF/OX}(t[nk#Coф?X/3#jj>斵:IT`T2pڮI;b6~9!E7"kl5n+Ὣ]FK|t%QW$1 mIߗAvBDއcE?ǕU@˗aK짛K6Cjq 9U@.av%t[_*Nݲ8D6虭>->9ʞG"2;UtwVb9T"SYèn/VxW-zyZN9RWxA|ƣ+(,ɧk7J>;qO;dP|  (" 3~օy6xM9 (}v~Rֺ5a ApAGy꾲' %\uKoZOե[)Նo oFC u8Vzk{ 2$ݞڍKģk@hm*-S>wZ, )J3M(|@W a ͷe6ez[]Dю7~܇ط~Ǥ28xmVEO7 *<Q=g}hL lG Ͱku ]襶JCnarF'm|$e_tV*UD4,4 3 GL%.|T2[gM9 rv3~hE wl\*d,UhHeJ5~pћtp.%8IKgq }OT=D&4e/n\_fĂhf@›g q.F2qfbDMX K I95:|~\;*5Nfl!q}rX6T7*~;_80]o7DdocP2HpKkjmUvR%b9vshH:E!*t<0R5"!^9@IW9EOy%Z+…q]d,x]VK뀄/j7*sXH˝9,^1r'mѮ4ʄ3\Sn2]RetDo] J C. D sUy z+Ϳw'HBؼ#sUAJcoe&QHRDPxٮn(=^G|+q^nPZ8-yq@;2WZ{@s ?!m13;kԈiS$k/j Zl]`1< M9WL-Wy2vGqxRvd|{9P'P£+d*7BW9pfHE <]Hq d6 Qi6Ocۅo `4.q7Fں:/rOx$f+-dS1_rK&˿[{QFe+ r !bKf̈+V =."֧ ɺ?K('pmn\ 4} G_yc-1DŽ %VLi8PmJh0. =rs3k6-)t_r?Q4 ځqYKʝTR/m )X7|ag4jgD'O.Zo>\RZ%x䴚QtiCQ6𚘙9FC 㤃]*tڙF)Ex5 枬TR '~F5^^;^DcG"mvd3Joe9xyeJ&L* 7GY$,l)0_@W؎G-ƊZ7@B-~9o ݅Onc2G`<l-0Bz|@Yr].E~NgFgR2g\ oUb́KϹFf[ᡯ7Y|- A.|0,+k,N;DH:S{,h@eeA3MJ _\` q94tyC`S *˰s1_z8^ˢc3@,L̑]EP{*'jw| ^ݟǗ !7/q| L|8wܱϾrXi1W/7DC3Ϸ5ݤM':HdC'|}07EqABsңJ4p"+iM"xaa?s!!It٠AY66u"p7- ?/݆a IٔY(z:h&I3 lk~g%]_ <[os><!b6VI0${_boS]ƅtKDg|N != k&F=)$zi+˦rRSAA`d$F-:w=72_5, 2G) 1VSt^2{IƿfʤvZiX,FO6Z+O.<{q g=bb%q:/(H (68dClNf}ޗ\{?-C+iTi$_ r\_k]EМ~HM?+MQYDG;C6`N܎GT>DVELp`K\8 [~Tuc~Lu% q 3Q֛~Yƥq4ThQnY5"wB&E*GXI9k: 9Lc` +p2K3";hXe*?Hr4yeXP2b;&Ҧ~7"?"$z"핏\eTɱ+k^fO #pSgs:X5 $=wR*XBHXwTF1{T.c =%3amWO'79Vny}i`;YKYԳL LeCXAٿ{7OR֋^H=Xq- c*]}R XR4{ha"zvYs |Xm LNZÌKY%mq|`Xj1|aqC~.(z~`8EM΃ &FG4ZQl~0"KH[BYuPQzi.2R4*?٥O*fͭn. <,w V#%^ѥs5Ԣn-n"H|1?xj'%|qXȶpIUgeV1E}xԚUMeչľjPN" ]I"37`/e+)[fPNK:;4P `K=5.DeR=_7ϼZE'8dӌͰ );߬7u;^jE9&ǼemΦ.wc 6'JInbfRT̢RR7C)6o"hYrGm0Yh؇q,Ѝ?0n$tЇ ހ ]s8 -z[냽x24!YKlO )EoKYHv؃+ u3u[ K^snf-A4#Nϭ#"2.i`:܃w*@s˵E!FHqCO{ygߜ OGR&{b6ΓﲌpjaҐ.DpݸPس׭O!)4WA~1yXDa^3uRiq ąb+yAODj+Ԛ9b~^GUwaT akd/MK2rx%ڹ,ÚG~̟ 0at['ʤEg$5!Px~uM_*8q񯵀O=D&j "3-p!_QH&i"u&Xp/]fえה5K)6ѧ͏#@+yrG`c1JXf {i&BE\ زq1W;[ٕHrJ] .u+xv6k¢2usCϓӛ` ;aО4=} hZҐ4ɻܲc0CBpԼAb6Vipzf%*~DêŇG_妖=jJKA|lxAAitQpï$R!wxIpnr M'IG}DIBdSy(n&e-/9Lca&!w\%=`Tpvx;^]{jP&Kko{Qd=~=h8I;ruƫJ gx\X2ܞVdQgCft8-KF !q,HɄldBBVT.0 /pjˮ~BhVJOWwOLO~~ tm$eٺcW/ >cPy O[:pb0t%WUB2XVFINᴎ  14cs7&HP$L Խw F۱ dHp_9wmKRv_4n޸8ЭM;Q*_M %* 19 `;{VBfx u=d#ɸlMҜ5z$aju!h^Q7H5!BwBsށh*8e2`2t%zc fXG\S o"n}o/}ZKaL躐<į8a[O`>"KCz<$U&&`Zk+3t,AODqiGS:rA={ɎS7:ѝq*8`XBT5fLaZ # e:G~=*DǬdHl5Egi&Q¤} X\{{1ܰڭ\8=H 6`yWWeepފg}4A%MX@<x0+b(=HpkRNP&^2' OfqE<&1:2Bnd/̇5ov_jTk=43Ʉ$+QgϢWU 9)>lq傮D^}; 2=gNOꙞk:춙u@Y9dOV FE! ޟooc_p#Deqд/B֩emr;0D{[o"7n0/s'Qxt HG1X|Վ՗0I\;Ql_'qAlI oN1w;ӹ?GF" M|e#~dR*9Wg]әWԿQ'gjD>CO/Ř=(ok5\Cm/AYjӎڭ>Jf"I\Iܑ o +^ڜt&V^"H9uDA桴۰6VGn5ǖͭ" %A 283D[|W|ˣ<4v7蛯Lܹ} eլ|glC,啡N8Eyg$:6 WC&Jΐu=$n NLї>@h&sۜNyX~""M\$FmJ ڈk3D}yc漷 [S]I*RY(0Ouž_z *n6FyZ0,#/h h0lLaw?k8<M5Trb4, ?Y1+BZĒF䠙&<}:͏^ +h(O-Sicœx j T;7%G:gy(ytB6" ٧|ck¾+bz ~hv;"iC1Oc,Qy8u0yL3Wx2)˃XJw8p?دJ6"XnI5Ȍ4ɉr?{l$`>F~w 5BÇAEU߮ Mgp]/W(uZ ʋ N&`V{2slYE.$'^u2S~Sz.q#x)ϳ \+u)4QN\0heyFT1!?~(1D{-,wy'mz<]i*yH`{hޱgƽ}DiBCJ\{wY8Ƽ>αdxnzjjDR6\-ksʇK_֍*K =ZH1R&jXozZ{+1yߢ*,ۓSkx^$j8L튢nHyl2=㹘TcqLtKKkѺVz,^*ʗJu^0ߏt҇Cgi'yƈ}eM?L YVh2[7,GPElБ{Uy_VRe7 Q:wU专F`\hVΘ+ Df*shf#`!6T>t^ 񓵗ӿZ5ghX[J-m*>]f9_}7N$L7Jrr0yyӼC1f Q(FtweJzC2u*)WUQ>PE$t}Y.&g`"QWtHЧ&^©3yUϧ(!KarKtJGuI'x"~.0&BN3!Ѵ' M DMKYaX+t#.R ox5Ǵ>P&/Y&03ZlZg2499ᰱdfUr,B2BrmP'S~C/Uhtj;A(3;|3.R+ăn  {/↌c.`CTYB/fV02`T*'FCa&ugېXKi3^rN6v l?|;g|:~M i~K/iqu?/í44?џ?|;$Uũ|:|:|Z]=?+.v;*wP]ҿwH~U}P^Wc|RPvxwﮂ,|M4|#@Y sGx!6j9]9^#=ъaWu$}7P~k58{Cc$nw$VW?P c'$r#:~ 'AsƒzS=@a#N,fPZ!&5 i:B-ϪS̱mzZ"o]xW[ #uA*c2Ca$AbudgRۡ(A lT x|GFʓPa@vMAܵo@͐;^[Ԍ-|R"ʍ!?iHi(Xz\>|>{?bp̒j'齫RA0.=8N-ꋤ\"RJ4J2 =P@@r2~-]8th -VR 28`IR|,V\ZqzSmey Ӹk]%bmsTrp7GFڜ9xmc>q/kWkqB΍Eۻ7~Ii^頫;@"[e1NִPy,z}.9K_S$ >gSpf=ċ֕QKlj+"FWAV r 5 {a%Zu$t&̻K>x\@)&ko:71"u 荀/+`ӳ-V(ۨF, k{9s HeBM1U DO OUVO. }p=zvuv|*Ĩq4< mBDj9QE7rvZnE(G9 L;KyD)MQ=- yGCHߑ $B[F? ؿ D- աL8c- qF¤quaXAS Ukc?=_ !`J<–*y{Fzn8#B'_)\R d<„[<˳U[f]:u*nQepAN 97šQlW5Kr( Zo=; {(xtP iSFݥ#z~-@@M]oS^b4@ӓwa\]&N!ާV!xuT lqp g8{iJ '?S˟EbhH:u}tRx1>:"@FuK F%QFe]o)a%d j]'mcwX΄\ogH58)ao\pjUmFU't]rj]![v5H$ j*6d)69lϤ.Bד3c#M=(D x1wƭ%Tsa|4vĠ$I]JosPbЇɘd fώRF!< [DmAvIBW-SVPV{ىoH = UtE\=-z@hN ~ҎX|}h1e 1gL4_CʫdR;G>QV~gWkiqW>jUhBP!t^FT ga7jZy& G %wOȴm./P]O0=GB;8ڷ EoURc2q@T4-y|al,q;[L8QzAj7 D%޶LRMpEuz5 AؙKʭ%&^dV &=Pw <VLoVKDP8¡3sr#qT]!p;aHB+`8zC퓚Y,fKwc*s gu83Tجv*~3 .[Vl1m[T\ygJ^cs>iE ¢j!%럗ճS㯡eO^%BBE-D>+g E=ZA` :6Xo ee[=w4Wه׋%,pW}d2O!=^anSEH睘eh a{4!/zp[ HW_j"G'>= k>0{%K<֑t|=LAɹ@^A/~/#N*N^d/G倱}W""{.I^" % DArjSvξݲzq7Y?89)Ww?roKz;\QfFp.Ow۷ :qF|->KϫC KHZ\.ϩ=Oϫ?W~}m}Os᠅_08]?~}+ߟVpɦ&C~M4qi~MKZ\]'~M.IpqЁ6;\.vg.0;*%4U5)/VFż'Of-f==M5r|l˪[OdiϢJҥi,1dVG;V;9pf_gYYRj)<q)ɍ52Wvh*܃ڈ"ke=~9,DW —GiiĨMaoguO Z'G*l0m 4VU4 HZrlSvܷ=*6ē'Lh&g d·ٹ4&`ˁP g$x*Cz@$މm4;Ԟvf@].$ (*ξY' S{M$3lOU%T, "[ }YT,PLlnX\I6tgMĂ=@qUɠ''Ilv\֊nֱK2{L\`=4BH3Cp"ESYMw\ {qPjjdΪ(.V*ຂh+YzL/]*'@z$”09ꏅ%!h8f#py,r0z|淐 P,νgPFڄswc08֡i*psgzOuN@ɠNj6AAV:w9G53ҁrYQN}?3Lby4#퀍櫖Q)ٌjl.R:q~gj{X 96Xr(lj4F\ X$ MLBycv ",Dn P2`54g+1J+ܪ=WiH܀bےn 'Fjm ~fSf Nw4!H>+^m+xP-.@ 0=AQx_V=?(_|2/iS{ :gVHGGvyQ606Yy ,Hg;uM;@7Ps]]+{_Ne( FdwOGe Edp1 #Gu'>Iq.UGx7%|3'xo=Q&ضsGyU>Xwf c,#pֶw%ΐsf#eցbL =L;hA;ϣ: I}Kw.R3%Oپs+*: )y?cW8,0:{>"Cay}l(,]2c $Z1fh= }/(0KTPʥ۟u|ywqܩEC$jsR<뿽ۋ.O )~rU}R> ixi ܬu6+mɫ_|:- CvK܅KN@ }^(1IN371G7s:_g}‚xeaa+72׆\42:q5~FE .wj׸,L!f;rU|^A-^rY=o^Ѽ7ShǶ mDk $2N2%arέinlc+2Lp,P12-1Sxd6CY$aΐӰ8%ާ{ ؓ))8ʇ$u?lzBņSZǫ\H& ͢>PbN=2?H 9nl i vl[RAQ*Ǧmņ~{\EC!]Η<X`N<앆F%^F:sen[ռETL:r 6.I33røatꂛۏ¢ӽZ8]bK9u JqDpҵ Ek ?69u?OGM4rV/~2_Ȉ>6z#!gzіT%DfF__ƿpYd5h?y PMAT: y@;TIσ!8(vGSDxWy׼4ց' W̄irvM~/67heLZ%Uun%΅o>23D>\e̅K@q#V|GJv7l&:"zr\I"W9]%{Dv@nC&m/tnDuX.1=R({+Ҙ.~|FgUY];tsetp8AvŅcKY AF{ 6ϯqf)rT2U_B!L :yT َW @ye8fDi$d#X#;+FMwO$=_ !`J& 67c ;RIj>XlұV]`8Нo0d$Xn+LufS@!菐sK',Mm /$˿?&p=Imfo'=[v1K#GQ 5 mQç7@C^?DT!ނk.7Vt ]\k :47"pgIy$}Cw&9Z,;ZEHt%dݧK(QK]*rdyI .a!0zeDPwUr[0! r?s 2jg58oFj~Z"FA+(Q} q(\UեvW~YezՍ; ܷsdӻӪ]\g{޲_rFOP0TeSpen<YA*by 뿃 XΓMVKf϶{=@zv/Ωwnaz\b1;zN 'CF=-~HUB4Yl+}T uO.!udޭS[Q냝Ȏ gY }Wۂ1nҺIذeWDFGE`1|liB!i+2 >c/8.aPӔldpL2\r1%m*vRB4Zf>n68,*cf q//E/Ҳ}޸eCՔ.{uht ̷a5xa'%Ӈ=e\ۜ!4cpx sfz*KFHӝ$)о)&jWRN2gg~4_;VbNs r$J4* K%o<̛xR!OY-=Pw <VLoVK8ѹ`ʡ2IgKZ[[˸jvRV Nw!wƋc}$ިI9z5饼$0XuUCy9s=ґAO=`&;A#R,ꤧ©SB~B0]Ҟ"p21]١$)js$@@^fsƽ8=ae>M.1aÜ)X:`uob9!U hhRn<]d(+i|c4U[ԭLtWbAݥ~8ubzƃQJ'7X+*1xt b.+Z\n>ЍRWfM&U'< 9< pH(fM~ʊ$5<+ɪ4̙$-oG/&|%,ٷɏLw#1Znؕ*.t94pK LYZdh.):26_MY~|!r$>EC~5B3Cʉ J˭ǸEroX-(wAUԕe:'0kus PLb@Xlc 7R ir$jGl&gIUkss&ZXͣKKgA}7u,MF*}-e3S#qd@B NfKP"B?`C+G ([_ﵧP…?/Z?5?ﵔ^}K>?ޯT?( -xQH- Zwwᒐ%0[Kt5'%#5SE/3^*W2J =nB DV\r|^$.%3%:c^ݐ=u*'=u*TO; zۏCX_m|S%}pf!"]J?y.+o1(bu)V.bDnsQ,W=wH=6\$<:`"9"5j<v_e|Sii$)uY|w?qWO* ;VTI Š}༎k狻iCө?4!9 3M2Ou\e('TTx39z0êHii_[_W|Ds̈ P:A:՛=`qK mm u~Ȗj5\vӁ"Y{18_%ww\9#hfegawgsҨ1RӒ|_5aaV ( *OT#418|XR `2g2jy~ 3Ѐx5<v 3Ѐx51~"e[Jm g-~pfv'@5ߝpsm.~]^]  9af~qN1ቹ4+rvd'ѭ1M/tD ђq__ȁZb1͌auXMtl-ZBaT RN-[γzCD_W9p yx^C- X XD"AqSэaT8qo-?(*dҏ1tB2Pph&ZPib"Jn'WU:Hlb\UEn~uWg)LN~Nˍ%Y,hQv , Ian;ڃ!S+tuи$< -֒ڒ;pQ5zΎsY:,Yz͔)loce9uV{,5@CIug>Ic어P8([I^Y?xLs'`JPzZJN/>o .<$,FՂiThGg0 qfiO@2qCt*Ls'o;%2{[Z!z>4<['] nDޣ{PEcZC4|kͯTZE,y I`,LCņ-aз?Np %5{>o8{-c>Y-1`Mpw|^M܃V=V#w(gQ:55_?{5f͇+OwFA{ӝi  AR?;CC˅tOyy'eB yS&}( F!PJDT (q_% 7CDT'~Cg]E} _X,,#.g*u,jZ 6~ ȝjkz{g^̏sÞޤaf.lo0@{9g phևѭ1M$c9KH_Zk7 !!WX Ugr/eJ;Ow!?e$OAV4ݶTԹdWB-AYhe2߶hwץGi`[q4}d†KG]rh)3C~񇼣Y[~񇼣?=M鎿xQ/8sE fBԦ[] W.6fdCvvf*e̿fSWSlfUgJT$`L}y^ 2MVH o]ÂKI!H`KsgԦ n+-/*$-/*9iyTG ":}Bmm(uq]E@}uUK"&浶LL=k, 0;z*aKrAR9X )`Cv0Q4uSn^gЯ^]DvߔB)]z:1#~3(]Kmu lgX%%9<,ÊvG ʲ,ء ~Q|Y]u֦w /.SOog/T)? -ҚxE&a1X"B&Il֩p1`x8 eqoyK|RF/OJz^& : s ]# "fOOKYUvURXp NG3+u |t- *myK,my$kowr: I~VO(vS{WQ X(l0W{E#[di[}2&*LD*:|, %稷n ]*B^%aPp2A* ',@gڵB"kT˷e{LǩNaSTRgaKP5Zi_K/))X$T-R#sB Y)t=vPpFG0)i܅7${P M# rkAf;BQin<V + ."TXf^ÐTXf^\2Acr4zyne/Resources/widgets.py0000644000175000017500000021267412417524776015060 0ustar tiagotiago# encoding: utf-8 import wx, math, sys, copy from wx.lib.embeddedimage import PyEmbeddedImage from pyolib._wxwidgets import ControlSlider, BACKGROUND_COLOUR import Resources.variables as vars KNOB = PyEmbeddedImage( "iVBORw0KGgoAAAANSUhEUgAAACgAAAAoCAYAAACM/rhtAAAABHNCSVQICAgIfAhkiAAAAAlw" "SFlzAAALEgAACxIB0t1+/AAAABR0RVh0Q3JlYXRpb24gVGltZQA4LzcvMTC+wSrGAAAAH3RF" "WHRTb2Z0d2FyZQBNYWNyb21lZGlhIEZpcmV3b3JrcyA4tWjSeAAAApxwclZXeJztl0F22zAM" "RBXXZtIIjmq6RFOHOYXvxHWuo8v0AH26S2/QdgBKdbvte2Q282lJsbMYDAGC1Pef334Mb8Pb" "L2PxYbfFr2WZMZZ5nrdrnstcFtzmUsq8FP9eylL8UcoVo1yv1+0aCCGEEEIIIYQQQgghhPwf" "8k66YRXWd9IXHQKiGDTYM/efhgxhfMRuIVsQfYFwgLrYTdXnohNIvXlekTWKjvqqPvVBjkn1" "UxTogyHkTiFAyDKvIJ0RgkbTl25FuKnHJHGKEvE35FX75KBmHepTkhoAbpqO6Sg9AvDSg3xK" "5ykmk5/i6FOgHfRNpLqvAfj04zbi+xH/E20bwYePSHPQBHUVc29jFHwEP8qrNq9B2EfJJxQf" "KiBaCWAGRgw9a4cMWPHVZZdW/xbBaCGo6bcugLX4YH9NANyfJrEUWD9orX93CPdJ66jV7xmQ" "k/kX128ZQTiE8b62vfOUPP9xEis/jOT+mwagX9R2O/fv69/7z0m8AL0ha9sVUHdbm/zb+p9Q" "ApYANAD335L97vHB5Df73oJR/uY/avP8D7v9w6Opu/26Aq39PUldf839h8F3umQVWP37BuQV" "qCk39z8E1zf5LQFRPpv7E37Ozdd/0Lz6RwR/+ccSSJDPrSNQCVsC3L13X4lPtgF+NfHG+5+v" "QOifbQXeCsAPADlL6/Lz48dWABaA9/8Rc/BS577H+Ue8068NuPb/ZO5zn1M4AqiNduv/0Ro/" "5HOvA+iLHfbWGKwXYuG7Oiagg/yQj2Hbayr5AnV/H+vi/+6wLoLK8+W5p7pRz+C56l+QjddV" "vd87oL0B/XkHDXna91T3APJNfpDLrqN2ZdrvbqOveWdvp4Gqa8/3YbdehBBCCCGEEEIIIYQQ" "QgghhPzDb4qlcnxk/7mEAAAASG1rQkb63sr+AAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAppDOhAAAmhG1rVFN4" "nO1923fbOJI3t3f64lw7M3tmH/bF53znO/vUXBK8P1qWaHvaF40kJ3FecnRjR9+kk17HcU8+" "Hf3vWyiAF0Dg1bLkbDPpDiWCBMBfVf2qUAChs587t8twcLhYmqvB388XS98IiEWi1evTw2hp" "rK7Y4dVJN1raxur4ZITHQW8YLT2yGgwvF1DcOYIKIvyz6p+e3i47ffjn8GD0aantaRNtqr3V" "+tpc+wCfFtr71cn5GZQ8gpIPUGJqP0HpQvsnXPF+NeheTGiVB+dY88FwsbTm/qrTPaF97Jyd" "QUtwgNPjVWfYw4uGIZYNj/HQOcWTnZ/xcHjOK+iF+H0wwovCDn4LB3g4ZyeH/cUycFedESsc" "sdpHQ9bIGauPHU4OaC/Paa+MVffCBOTgQGg13QsLDyGcJHAg7GDRw6oSMv+uQkbb1w7g7A18" "/wk+vYfjWPsEJbNtYmbeETPzvjB7zDE70q4Bl9+0d1B2o80LsbEZNvMCbAwlNtOpgI1RgM3U" "Z9hYpDY6ps3gGTN4xgwen8HjM3j81bD/BlqZrIZDfuxfAGrOGE7wD9UAfMEBPNQ+gnJ9BBhB" "tUDpsldmwYSKEU3iFKE5roCmpGlFaEqaNt6sdSKErquAcNjvsJIhO2Yh/YFD2kF7XGhTDuhz" "DugQwIxAF/e1AXz6DOdmpVarxNKM7M3arTVtaLfjunabh9ETjtExnL9GdTuF0g/3Zbc1/EAN" "bIz7web5GjZ31J8Ne8rdI/RkzcIaa49sW3X0Z8e2FVN6F9F5h7ohalCmRECxCCXyQBnI3DhK" "PypR6qKtTcAVFmvTQ8WJbAmnEXDTAuKtrxUna+M4PVPixFm8PkY792tFFucwhByGkHMnXjqG" "41i71b58lZGRKcbsDoPJYTCNGUxjBpM6Il+HaS+B6SM4t5stBo2FIfjUbujZDAaNwaAxGDQG" "g8Zg0BgCNE84NAfAMNfg3Tvw72cKEgfoBw5QNY9GZZrBx7UZQFheCpBvFwCEY7sMRLM7RkaE" "IUQYQjZDyGYI2WzAR8UqjPjoY+BwBU7UwTAeN49Avf4JSvaZDfyKkVQSVTGQZsChJJOiwbMM" "Za3hc2MwSVANzGcczENQuPeYgvklSTd8qaSU3oxh6U8ZlPMNUb5RRPmkocESvwqEdQGKaW0A" "pRP4+1H7UAiRGWwWo82G6/eD0Xcco1dgizdKdLw4v6dm/aLRDL1VwIfsHB/Mr2ASq3Mno/s7" "8NeCXiVgZjsMM3MigRZnRQ2G2rSIwKJxFa1CR5HBzeXOkt59P8C9oY3y3JVlzlICa2qXlLh+" "Rbv8WJxZ5iCaVuUsg2nY9U3TMRiEGMlQDKOitN9mQMxi9zjB7h3G8FNAZryWd67jP9U2i4nU" "fOimpD50ifd0CIPOJNPK/tOcV7Zc5i/hA4lqQBcr3CFCRpWuOPSXYMNnUZCdhJs3rY+bzdPM" "Y0531syuDFtzu+3ESWeu7NWR/F5MhNVBETmvdJyAWCOKNA6riKI754aLStfj2fuNojgA8Mh8" "Dh8666RXTfv6GKfdlE8+StoX2RX4LrIbwBa7DK58aMSb1z2qcsM4+KU+g0XB8QfBrPHDQKGW" "ewnE1Jt8wViuydhMHJrhEKFhxroKssTn3hgHJRWxtYgKW5dh6zJsXYYts2L6YRJJ2NIYZ3C8" "RpTlOI5wRnOq/aOOklYCslpgo0YSdZMS5GTjpo1KigoYAznIaivT3yaIfp+MP8baP0qylj6D" "M2BwYnCXgdO4q17aDE1GkErFpLOsFFBSAKjDEyswUGK5uYDrJsfU5aC6dp56JnOdMZjRWMmk" "IS5EoBMtI5r0lQBlKvphsxNTFfMGxmYWJKh1012bcu9w8Iad9Sn2YrDi3OcpxuDvsPwM3RPo" "onZdONabKtMupGCol/rtBtHPvaGX1bg09InxNP15DTyz2XaKYT6aObZdhypNZ14lH7hlODtx" "OnB97FwNu2Oui+8TnZSxixd74CgEHY0hMCMbu6wxo6FiRgyKqo79xveKXRaq7zhUF4o1MTRp" "SmNHOgk/xMmuMc/IF6vYWBlqV4hqaExbkGSYjhlKhPuPKZEi7Rkf5rEotcQhA1syrHwOls/Q" "mvKlRlOO19SX/Qf9MIg/rC+aGSQf4hiyP+AufDCIo8rheuCuEsWjjCgWGLCXzTiO67qa2HPT" "WKZMPy3Jc8eBUBysIyuv+W2zwgCbng25NEImDcQYXfMBumaEmq5Goq7adhmIZlQZwyGgR6Mi" "upLkUxWOjFM88ZyJOZ7fdc6E4+hIOIqGTv04RdEs0F6Xz7jhzJvFjhRHa8pwxKOFR8QRcRsg" "XnHciPiRcvweJ/htgQKsWSUKkDI9CQMUJsly4seKCDJLVlo7+iFq5DHKVFurmvhf1Kk0PmlV" "zei9hkZfKbrk4Xo0lhKSXFvtiWDztsrmY22Vudbh86AOnwiFozg1xQY+A6avwwHnA/6d6rEZ" "KXngKQf6FerrnKdBcIFng/A94nwQiXwQjTdEq3d1+2pKJaqxJYs3s84rGVLKGeByDOuN0omv" "0lIWgZahSDO5NQfpVpz8pdHrnQbplgpIXBabt3C2GYzVloJI09BJDFptyUMNIOPhZEKuyM13" "SqMrV3EnObk0b4RxffxhcJFxXwYze2kxcpz2GHD3/lH7VTLzKxweFZu5Xz/VGcNaZWk859GJ" "ciGJxKJKM8/z+XkZDzHRTmFD9hyujZfy4Utjp/8GxzRG11QcOzlNp61raCZzQZn4s0aKHYBW" "po3o+ZCfD9l5DDzjxRGmx1XSYw4pdkTo6o+Zp6qGaRxP0VcOPmgRneum77eoUDUdm6Hqih5o" "XMMBuUpcDSWumH9rsoQnJk7xpQ3CmROPFh6ZFbvcjGWXU27HbFVYsQpaKhWUZrnVOmhUN2Pk" "3XWsJnZ55jKxY0kFKdEyHSQ89qQfWEDkz3lARD/0YwyHsQIOYq5kQ6NqmMbLLUIac6oQndhC" "NB9rX51wvpAYEUNVRlg9HuKYWipMp746wgx48iPgcxWBm5r1IE79dlJzjlPBOevJLgGwD+jI" "P+NYMg7inyaR0EdU0HcwVuITbMUzF5tbIFw0YPeVa6FoQJbBldQZZxJfMV4/jl3NcN20yxD8" "liNoad0Gqwi8KqsZawDmKp00cL9iqFNtWJkHGA166uD0KMUJhohHGEj+XoyYkg+rLQCNITPL" "CXGq5ENzrrJdcyeQXcD5G0z3lr0sswnInOpDagmyoDwUjKdlJX+7IcCeJBHKO1ygQrPo87UV" "izJobtPMo1k9M56MSSy3/lx2PCYpe7P0OB7NHa+P5sqge5H41Pe4KK9u7lw92XV3W8W1PJnk" "+VitevPqmUfZ0yqVL45YBkkGfD1AqQopXbKywHfB7x3StcUBlhLTwBcnJCKl11AnxaVlFlM1" "BarCF/nN53Qqgscv2TwZQx8e0HOYX3brgf9XDv5LzERO8fWwT/hSPuVR+o70fiqY+mJgq9mq" "arZSCqLjlrwQJoHWZVDLc1s8kWbxN6LgiDLgizSyat7nEaWQu8ARZCaF6YsZzJqR5rNMpPlP" "zLrtY2qpdqyJi7NqvMGQSMEuH0g23k6CjhQrUDRmi6aeYpURTgvjifhDjHWf5z/oI7I5uExC" "BBP1q/C0e7sMs2/gRiiLISbsFplXlCKUwTluGPArSucyt4TLJGSAhMzThUwbwx6CFg66eMlg" "wMqO2eE1PazC7FiOdYi/8EsTMlKXsiWXuSXNukRYl+BwlPToBfRnmmxAMeOccJPZT+FTMkSa" "8qlMStxT7R+gtvF2FeHRSwD+/JBVfgKfj/p0B5aQbbFi4J9VpsiMi/j+K7TsipYZd6/HbFhF" "XET/hNnFCBFfkrXA3O9nfH5ZbgOO3roqZUuayc1icrNaudWR2zMutwEgM4WnpdmTXyTpPUtk" "pLrmssI1zSQ6ZhIdtxKtI9FHiSXSxCt1ndkxYpRJysZllwVlzSRnM8nZreSa2CKTwA1Godcx" "XpItqq+5rHDNndjVNFuR1hFpGluNcXevdOV3xGec4vOXOeebicth4nJaaTWRVh+DyGnmXfSI" "j83j85c555tJy2PS8lppNZFWiIjMEjxiqaTnL3PON5OWz6Tlt9KqI62nXFo9/o7qb8hv2eDk" "KZeP6orL0iuayTJgsgxaWdaR5fdclh1cK/EpmbyOkrfCrhOrk882k9OUyWnayqmOnPaSwR21" "FbaPkzwgT0vkAXla0kxmMyazWSuzJl7tFS6Sm695tfT8Zc75ZtKaM2nNW2k1GWz301maJMZ/" "lESH2bLLgrJmkouY5CKhY08SNZprE62LoniHk0jx5GesNnL5ZUl5s06aPNNLj10zg2jYJcI3" "S/hmC99GtNbV6ggT2K2aFqtpI4B+zAD0Ga4a4CL9K5wbZ2swUpgsZd8Cw5iQbN8M3UkfsOjZ" "p9hxCcP7amRXAL+QAM5AG5/Lg1hPOzExyHic9/yzCIrFwkC+sxDmDTe0K6hjY09MPDX7MmOP" "LbGoV1WMvVo9uwLoGQeIlkzoIAoXHaxroavul+WLj2boxE9LSTD2xFLbSUsnrnyvW3SrV3Cr" "KfdIlszX1/1ds38PV0PhfDwu7TnCFfFwV5lSEMOain3LompOAs+c5KFqzu0IvqlRdcfzqWHm" "obpesSn3qJJSPOju70op9rhSDHHztVuMAUvUILACcNF5ONqE/s3DceJMrImZhyP+ycNxNqd/" "lSD4Bv1bUQ0edPd3rQa0hOYFRDWwlTh6tmtK0YOVuMbJzJ9K9uInpe6UzE1X+SDzaDaZztZF" "uZsu7Eocj7g4WIC+7rNzwpH8/vCQWkb1DvXsGprM5l9QVkJZzDjzbJ5an+zxU5t3XTnKy9g8" "vdGL8myeNZwzWPHgv4qU9aC7vytFeM4Vga7q/IiLu2+0C745wi/l1kICY+wEeQ+njCruUM+u" "QWJEkkR6ZYPRfCrIjBANg/6f221VsH5fjewK3sdJ9PQbXxlFt+j+tZyO1p+YZAvXzDntMBkX" "DE5krMDjZY05N3GiHvR+ZX3fRPK3F3Zvl70wM685R1U4wZXMdNTUgX9vccvJeF5snrwsMdZu" "Vr3+8HbZPezRf36G8v+E0dY/YAAeYoA9x4zFNc9YXMCd77UvGts441dQoo+octdwrqdF+F4t" "VapLuP6cvcWy6h6+RMVLe7Sf6dMq0/JjUMt3qMA0/ZQoL6/he+3/aEb6V7jzudD6CfhZtnR+" "wX9VhdXwJ67uc6nV7L1nuPnSidbl9/xfbal5WOpqJvw1NKL9BJ+ncIZ+oudm+Ot5PpzzoIT1" "z8ErPfjXhBL6bSW0updiAFh+oebIW/wX6dkeZa58hS+33Gjv+LXf0B4JVz/NXB0vuLpmkUdy" "j6c50j09qBVcEO4pgW+bwxFXJeT06Sn+PuAvqB/xLyJ9wPs+JXdYwh1PcK+0T6BZedfLLaS7" "1nX5hixjXNUS4/SvKA0Zq/SupIeZ6y3pyfegN+9xBDEvkcLzzJVnuJD0hv8WzQLHHvFdpnQX" "e/1NsEOlZs8AE5VmP8OXr3/nHpBa7mzt/j3RMuApIwnLY7Tf4hqizF+5hsdYw3vOAqr+Z+5e" "s0y2wzfIGjhljJZT9gTrch2iRv4OeE60/8dsld/7LbRJM0Of1uy6g9jfoBUMUX9ucuX7In6r" "k195nat38p17yZ2ydeKVlKJrsPMeZ+c3qMdvoO1fWoZuGbplaAVDWy1Dtwy9dYb+M2foIaAW" "7zvBrsdktMZ+PKvl7JazW85e52zScnbL2Vvn7EfrUTVoYsvRLUe3HL3O0XbL0S1Hb52j47z0" "AN9ea7MeLTu37KxiZ6dl55add5aXHkDfKZpUf1uGbhm6Zeh1hnZbhm4ZemcMnclLtwzdMnTL" "0AqGlrm2ZeiWoTfF0Aqt2PHKu7RHLTsXs7PZsvMDYOd25V3Lzttn512uvGsZumXor4mh25V3" "LUNvn6Ef1sq7lrNbzv6aOLtdeddy9vY5e7cr71qObjn6a+LoduVdy9G7y0tve+Vdy84tO39N" "7NyuvGvZeXd56V2svGsZumXor4mh25V3LUPvjqF3sfKuZeiWob8mhm5X3rUMfV8M3YX6qS5l" "+C7ZVZIxdPpDG2+Fq7bF1nEP94XW12USs3XxyqhiZpelP5ZmgMSW2P6aqcXZkr1kr82ToFfQ" "u3XesIA55BH1ffqbMTB0oNnwdwb1+c39TU3di7NrnKH/ELrmSpF4sa75tXWNemjyB9S2Z1zb" "suwvR6M/cH2j6yKAvbemcU/SFkHnMj1so1FFNGpJ9pEfjcoz919vLCo/x65j0Xad8e5iUVOy" "0P8tsWgdhh5Auwt8qu0w9NO0RW3WcnQpR8vxcsvRLUf/kTj6f2u+4LnAb/tYM/tdsfdCzmCI" "+CywLHuHTv9uibPLelGPQScwJjGgNEAmnCOD2qj1MYPSkcoY/kbAmnHegF7tw/cIOGsG14sM" "+h/QUgeeLMLnZlb2Fp7wGi2N2uXv8P0mQYV6g/+fPN+32PI+/Veo9VvoZbF1yNr5rTaWuOYb" "6KvIND9os6LxZAMteSK8+779nFJ++9sZ6Y8lhP9oWSWHRxoRxhg0EvHgrw3XxxZlwacJ2tQs" "Qc3HKCZC26ORiTzOr6Z7jwTZx2Xb0Tt129vDnSCmPmANlosYU4ainFUT95pRmytpV3lEJa/p" "rBqL5Hnh+/GRtqSBT8C2ZuDjP2O9+5keMt37LvvezZZ0Tmyznr7MQDcckDvVlgDHCdReZyBP" "eezgJ8xG7Zla8gz+p/FccH+erxb2f4VWr5MIm+ub9l+yzm41TqnXp3qys0AKFP0JWjBj1gBk" "Ywq2TstnUIuBkY3B5Uxl7KBMtxG1vIA+rKPwFpH6CPL8kDDJ+shmKiGVe1ctXdmDsVjMMp+2" "qhGqluvarAGSn8M1VOrMt5rQT0dhs2T7NitEMp9QUtc4rnrLWfYtfP9Fm+RKXLznHdcb8a5/" "Rd6Sfc4MNeumYktPM9dXb2UPyqk8f4V/5fqrPv08N3dR9PTpXdWfPr+lvKcvbkV++mz94tP/" "mPP0v2jxr1TnjXXzEJDvVPXvmQKFshafK5Go0tpjAY31duR8ixqRicZ+w1HdOxnHtIfifar+" "PVWgUdzaMyUW5S09EpCQ26jryV9ox/R+8JNDzIt8Rm6l7EN9wTbZukpP7uK1Z4nXJg/Oaz8C" "2X/G68v9NLXadXyq3PkjZnHqRwZP4I73eG2SEZGeKpN5qxkbHOJz3GJvthsbrLd8F+2aJ9pl" "PUDtQt+JNX3UFhjjfVod9UFMR/3R7fL16SH92c8rdlil54jjsLP0w2qtTjqHs8k6n6S8vtF6" "H8csefdaa2n3Yyj/jHnn/WwkurX1N+rW7xIBm0kEbLcRcBsBtxFwGwF/9RHwo/QMlDIOfr+1" "HLaqbVGO95nDnmEcYmOGeoqZ6zGuVQikHDaNa8bCegacG8aVhXOJ3b/NnRdW5ZK/B/R+w9lg" "iuWXROrrc/p7mXlrGrHORL0SZP4t1Er3ZppnpByi32erTtgqh215YnXb9SQV4ZoSAyRBcac+" "N8JZ0XSWx0A/TOWxAz9cgv6PPAsb75K1z+OSA6j5NzrPtCVJlPejnlQIyoOgFY1xXDmFo41z" "2NnoyMFZn2rrge5LKn9K5kKZTNLv28E+ba+u5ptQFqFHspMINB5x7TwCLcR4L/4OV58hUtta" "aadq+S64k68K9x+1I+jNZ8xwLHBOcj/BY5tZhvJ+3GU0ZiSjMethyCRT679hjJZ98jhSu8ZY" "fozrYD4ns9R/hj7q6N/y/7olUn+GmMUZsd1IvLgP9aTtgLR89BMu9NNFnx/xGWMbbTHCiM3E" "FVUOrqyiq1PpuMDHK8Zr0dl9zTr+mnnqrKTXV0QFUlz3b7n35q+UkuO9dU0YcA2jecldaUJR" "H+raPeViC9cWMS4mWLePmkDnoR3UBGrdEerCDCMTG5kgQsufbkUT/pzYNn1qURPkPPM30ENR" "F/6Se/d/w3GsvRdGCd9QjivRhCfaG42+Ef/rjrQgv/16GhDw97io9btJZpmgN6bveE2QCyhb" "uDiemyAPRDjCIziOozHnNjTgR7iGPXFd6b9Q3llV8t/zFWvXuDb7Q/Luo3h2O1KXW60n6yn6" "bjq6o2vM2FiPvVGwPtbzdhx5qVF/Ip7devSb3349SdCVegT/d/iarQCt0FuLt+I1fg9NEs8B" "nQ/4Hgcr2U9WC253DXlxL+4yMrEf2shE8Gefhed+iznLT/j2f+MYuGL9KsZ1JMb9a8H9N0Xv" "AEn5nnOcVaPzv7H9H6Dn2U9Ltux189qvp2lz1BkD1+tOMNc2RW2y0OtO+ciL/uvgW0XxCmDq" "a+fosWk8th2vO8YnfguYxU9cTQf+orzzMz/K70Dkz/V/px3ilZ+hrk/J6hK6LphhfI1MTG12" "X7hyW6tLyntSNyYn6H+pXw5QN1yMuNPcuY+6QcdkgfAmC/0/wmu3Mzp7DL2Ln3JdojQ7H7OL" "Jb2tt4dc8KXgrgjz0EEFXfhuB1L/7g7yjTDLYnO/PuVxdpCZ+WZvKhmgAbt9U2n78v0zzmR8" "4RbE3g35Ap9tjgNdcdDjcs++00DHQOwNwG16g6Ie1LV5H7Mtc5Qxi8x9zM5kI3MX8/0WZmDo" "v+y7jTxxrzbfWEZ7GI9dsznFLa/DWm95uzKZ4bmHJ5PnUiZxhIjSFrcbuRf3Ytuycu6TUwVZ" "/QVnBRd8/DgEbBf8E33be4yzh6m0fkhndrcqn/V270MiHsjBw9yjhzlI+q+LkY+DXnFbI1y2" "s4X4DmK89+U5YkbHL9tavfEn5VuH7d4VbJQovnW4+d0u5fcgq+xeYUp3lO1eQfOM9d62XN8X" "rN3BIl7t1e5g8fXuYOFU4uJ4p8FjfI6PLQ//IXhYvuch8LD8WxktC7cs/FBZ2K/MwrhHwups" "CDS86h+Mbpedw9PFMooM/LMK2TfLCoIoWnX6CVvvYfb6rdbXWOZzkewE9+9QQhmbck4fzv+T" "Z0kO0NYXcJaNgsaYD56tLnl+X74+3lt20L2YLI1V5+B8gYfhYmnN/VWne7JYmqvO2Rl0EA5w" "erzqDHt40TDEsuExHjqneLLzMx4Oz3kFvRC/D0Z4UdhhhwGePGcnh/3FMnBXnVEHz45Y7aMh" "a+SM1ccOJwcTuOOc9spYdS/MxdKHA6HVdC8sPIRwksCBsINFD6swi2mEmJ7iuHBOZZxkvDLn" "EJt0V/7L0is4giF7ipA9d0i7b8I3fNJw0MVLBgNWdkz7Fg5f4+Eo6eEL6B/z0fFc0xylmq45" "/5T0eKqJu1O/whX5VL/Do5e3y/D8kFV+Ap+P+vQtqfDokCqbE7m2CzaaFplxUTAbu1NrRcuu" "aJlx93rMoiq4FaiqiIvon9Hrzu2SPfe3HBuQBVjVz7fLV30A0TdWx/w4Gr4BwOHO0QnAOjrp" "LpZeNLMjg1UUbqaiVe91H3p8NqIPdng6oIf+KYq6f4C2form1KdFtJL+iH+/oGp80D9lhyHV" "ioODQ/x20MXDEKqZw5VdesMRrdRY/a3/98XSocch+3rBDn16/1F4Qg9/G9JrxnDssa8jWt3f" "hh3UvNM+at457dzR8JSeOx1e0kOXHU6HqKKHwzN6W+9wSB/m/GpIv50O8dvxCG3xeDT6FO8W" "Pce47nc84hrt1esQu/j6DPs/GmB1cCc9vO6iFffC11CBtjo/s2+X8M9i6a7wELGDyQ6GdIBj" "SK8H+3JWeABf26PKZa5GB0hCo/4rbHyExrc6O4cbzs672Nrq9Aoe/vQANLvz8xF91ssBw4FH" "wqfwDF80tjuRSTmbFp4doqi6JwjE4Sm11R6t5/BnWtw7Pbtd9kfHyYk6JP8DJ/kOkvVCmyaT" "sYzi40BpPyYfTkZ7ipIsjc9NNY/PBR43I1tgcuOuTG5NGzL5uBKTP0AW6Az6aPIjNNLwYkRF" "MDiHi0DUtjN2VqOL19HyJwvuv7iKwGX1D0P69P0hnLYd3TICK3DgKxRarh4YQWBAaU8s7Yml" "oVgaiqUd6Ge/A+x0MjxEjRieUsbt95GE+534RVmL6I5HLM9lr8t6umdbnumy7jtTB0Q07EZL" "uMoBJQYRv4GKO2/Q2joHb4BGVtnaDN1zA+JYrLZAd2zXt4KkNkN5P9RwSLtLTYJ3N+5nv6yf" "/cGQuouXPfp4ukmcVfcSVTJ9gItzJNZsZTndLK3MSCqD0G90AL3vXYSs8tb4W+MvMn4SMOP3" "BOO3PN00PcPwmfH/5Og+tWePGX+mtCeWhmJpKJZWNX5fNz3fdW1mBq7uB2BTvsr2S+w+0AMf" "qvJYRb7uBhsy+5weNrL6nE62Rt8a/XaNnjh64DnE5x7/J9PQiWH5cDFafaa4JxWHYnEoFVe1" "exusyrQCk1mCCQYOztBWOv0Sw3d1h3jE5/ZpWroDNuVvwvLz+tjI9PO62dp+a/v3Zfsmt31T" "NH5QPsO0CXf4tu55puG4zPLTsp5QFgploVBW1eZNm9oAGBGzATpUsL3AUBm9HQQ0HVdo+Kaj" "O4ZlW3ybHcum9mlkgofmlp/b00amn9vR1vZb299ysJ+1fAjXCfED2+ahftbyM2VhtiwUyyqP" "8XXfdk3uSYmrgx+1bCfH8INSj08MYhgk3l5LJ57vwNcNDPHV3Wzo8NW9bI2+Nfr7Tu+JRm/b" "uksMh7pqavZEtxxiuD7P7qWFPaEwFAtDobCyy7d00wxcCESY69MNYlvEapjbA7dsBBCBc0fq" "6JZvEAhuNuDwc/rZzN/ndPMeLN/CP9u3fNNtLf9BWb7a3Zt2Mr7n4b2djOh5TG8nY/jqVk30" "WLfNQIcBshHYDQbu4BHzKmluyKquNTJiRe9az93a7z3Zb6CaljM93bMIsUxmxK6t276XOO5M" "aU8sDcXSUCytauSBblomhKzcI5q66duW4TUwdE+3iO/GdmkRPfA9G3p2d2vP62Mji8/rZmv2" "rdnfk9mbyngdjNUNbNOL43VT91zPc3luPlPaE0tDsTQUSysP1U0d4uBk92rL0C0LjCpoOFi3" "LN0GHvKCxKxMy6OphA2M1vN62mxGPq+jrf239n9f9q/Mzzsw/DRcx+fBOwwnPWL4xGH2nynt" "iaWhWBqKpZWDe1e3A9dxLTFbrYrwq9g/8XTbI7Zh5yX97xDr5/W0kf3ndrS1/9b+t+v/iWD/" "ovlnCntCYSgWhkJhZecPtwReEHh5ufXqUT9dG+RYvh/kJf3v4PjzetnM8ed1tDX81vC3avhW" "oBu24/iEZ+2IQTXTSdbipcU9qTgUi0OpuIb5gw/0TCtvMY2Yr7fKKMDVfRhSG0HeKp87UYC6" "p80oIK+jLQW0FLBVCnB0iwQkydvD+N11fZ7xS8t6QlkolIVCWY2o33BsxxLnrFQz9GV+H+Jo" "wzIsP2fG707hvrKLTaN9VS/bKbrW3rdr75Y0Q0dPCDN09ETtGTrLgQgZR+el03SVkni+btHr" "/M1P2OX2tJkjz+toa9itYd/X3DufvPsJgtGsacN5btoWM216ohefCPmJMD5R2VMbOveAdJlJ" "w7n3tBITGGBTc+/rPWvmm9c7dw/mGzDW2aD5VtjAoLXRLdrodOKOZ/FSeCPHSMFYktWwPMXG" "zvWSc2FyLkzOVX/PxfPSBSm+CWNMHgu7k5nB7NXUA6rRJRar+y7JqWk6U6+cq/aSi6KDxeaG" "PVeYrbKHJVXRrreW21quZLlBRMwdW67l6cT37EwombW5yDKz1ksvKwmhYbyawwSRbTa339xu" "Fhse9l8RPyt7WVIV7X5dG3bxT7vtULvtULvt0Ne47VCWvc2YvYVt/C+1c/h/qPW0brKNf/bc" "tn7KPt32bj/d+K7d7lGx3aMpbXSXv93jv0g/BlBls0df2k6yymaP8vaQZZs9yltWlm/1KLdQ" "ttXjN4Ct/JPiZRs9mpIEHspGj5kfB/qDbvSYbf9hb/RYfbtdys/b4la68dpH/FGuDIMqUIy5" "tVjfi3lY1jhX4iCxpfinHOOWXEnDs9fmYU4Kerdu6XRze2eL3mEMTBTgFvr0Z+X85t5hxcJ4" "y5haUZIv8f141EWnHbITFnxRMp+usPh4i8SzFWyo5azNVZjCQMsWXg+IswZQyVWfT87ZpgdD" "wKshm44PHJ94wepw8JLWxIYd85nF852GbhBi22VDMFp90uDrvlDz66HQrtQQqdeQRXRPeqS0" "pavClsxmj8RafC2C97oQPKNKS2sjUJPLzxRenMgIsGSsSIWmfPXCdPwkeYQbI1le6cBzPrPF" "WhwjMKx4vxZPty3DNsomiKEWR6zFJxYx+Noyk77G7TtG2TJwqMXN1uIDJmZgx0/k6IblGX7Z" "ghKoxRNr8Xzft7xkkUtAiOOUrXODWnxx1ymbeHYyrCe675mWWQHdQHirFS5xzeSJTN33A8+r" "gMtYqiXwbcuJ98FJvpZWMxGqSZq/EvpWWstUqCWB4krAqbSWmVBLIpYrQWaltcylTUK4ilwJ" "+lNaSyTUkqjrlaDLZbXMDaGWxHSuBLsqrUV8DTox4yvBxktrIeJkUIa0S8Egqry0qVuG5RKv" "Zj+AqbK1EN1xAyeoi4lIUcTSvcC0441lKstHpChi64FLoJ66uiJQFF1P43m27dbVW4GiwN2Y" "xHfjd/Cq25BAUcTXIdAghNS1Z4GiSKAbpuPa9cllLFXjW75jB7WpTuAoy6D7hTrEqs27AklZ" "pk5M03St2k5gJlXj2b7h1PdIAk3R16I81wvc2u4xkqqxAyNwvbq+WiQqqMb1Idix6wYOIlOJ" "8VrpkygXnog9STmnAmXl45KSTj2ykqSUsk49tpJ0JqWdenQlaXDKO/X4SrKnlHhqEpZo3Snz" "1GQskWvSrzUpSyS+tHM1KUtk4RSqepQluYRUcPUoS/JPqRrVoyzJWaZKXY+yJM+dmlg9ypLC" "iNTg61GWOGIr7YJqkk4KrKr3ZCZWI0Z5NXARKEsKOWtISaAsKf6toTMCZUnBeA0NFihLGhnU" "sCeBsqRhSg3rFihLGjTV4BpfqiYzgqtBfAJjiaPJGiwsEJY4sq3hEiZiLdlRdg3/JNCVOOKv" "4SxnYi3Z7EMNzz0Xa8lmQmqEEZFYS7OMjDx7D39enoS3y5fsRydWq7UfrTD5j1bsaa+0uTbB" "NTX8xyvYD1eY7IcrjOIfrlhraHU06N4uj2gu0lgd0UwkHPAdKReOV+woLCVYHXWHcEcXGzvq" "/swmUen/8O2Y/thI9yVt6GKI09QXwwN6WP0POy3D/1CKIuIAAAC+bWtCU3icXU7LDoIwEOzN" "3/ATAIPAUcqrYasGagRvaGzCVZMmZrP/bsvDg3OZyczOZmSdGiwaPqJPHXCNHvUzXUWmMQj3" "VAml0Y8CavJWo+P2MtqDtLQtvYCgB4Nw6A2mdXm38aUBR3CUb2QbBmxgH/ZkL7ZlPsl2CjnY" "Es9dk9fOyEEaFLL8Gd2pmDbN9Lfw3NnZnkeVE8ODVHsbMfZICftRiWzESCc6imnRg46eq97F" "j3DVYRgnRJk6GKQFX7oeX6ZDsdxFAAAEeW1rQlT6zsr+AH84xQAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAeJzt" "molt6zAQBV1IGkkhKSSNpJAUkkZSiD82+GM8bEjZsWT4mgcMdJDisctDIrXfK6WUUkoppZRS" "Sv3X9/f3/uvra0qF34OyHpdM+xLpX1NVn91uN+Xz83P/+vr6c37LdaceVdYtVb5/eXk52GPr" "9K+t9P/7+/svSnWsej+j/2n7z+D/mT4+Pn7aAHMBbaOuK4x2wXWF1ZH4Fc69WZp1zDiztPqz" "dU4Z0j+kV1A+yjFKc6SKV2lW/+f8kf1fdUvwRR//ic+4iC9ynMz5o8KIX+KaZ0uVV13XsZ6Z" "zUVZHvJjbMrzLFumn1ScWRtIu1S+z+D/Drab+f/t7e3wjoh9eKb3x0wjfUGbILzS4pz2R/ye" "Vh3LN7yXkV73fT6TadKeurIt5xz46P6faeb/7Dt9nkxK+LDsWO0mx1TKUPcz/VTeI6/036gd" "Z/+u8EofH9b5bA4gHmXk/SfvPYrW+D+FzZhv6ef5boDtsWH26+yb9L18NxiNFfk+mv0/x5D0" "VZYlyzur7xKPoq38jy/xbfa1nk5/L+jjSY612fdm81HWg/x6e8jxPNNkzOk26WSZbvk76K/a" "yv+lslG+A5Zt+3t79zXtJP3A+wRp0aZ45hT/ZzzGJPIizV6+JT3q/K+UUkoppZ5Tl9rnzXTv" "ZS/51pTrIJewYX0bzb5r+vfUX7X2ebU/rDnUmslszXqN0v99bSO/80ff/EtrIayb9PNrKMs5" "6kf84zG7v5Te6HqW1yytUb8m7mzNaVbmv4r9stz7I1/WPPKc9sIzuc6ebST3XjlnDZd7OSaw" "d7MmvNs6y5nriXWP9WbWmvq6UoX3Ota9TCttV8f0GZBXXqMep8R6JfdJl73upTKfo+6XbG+j" "/s9aG7ZmP75rNPZXvNzHLegjrPOtCT9WL+yXY17/tyH3IRB7GXXMtcq0VabZ8xrZt/8TQZzR" "/ZH/R2U+R33+P8X/GX/2/pB24py9GY74M//JWBN+ar36nJd7Avh6VKf0QbdPXs/yyrDRPhP3" "sz9znXmPynyutvB/30cpn1CmPC8x1jF+MpbRnteGn1Ivwhg3+I8AG9O+EHNt938fc3KP8pj/" "+X8i8yj1+93/szKfq2P+z7kdO/R+knUt9fEpfYO/iMs8tlX4MbtnGLbk/TrnYcZw4mLntDV7" "nfgz9yiPlYN/a/EhbSdtyp7ZyP+jMp/zLsh+W9YpfUffzrpij9FYRdxMr+fX/dn7wZpwwpbq" "lWHUg7mk+zfn8tE3GM/350Z59TDaQN+LTBsTP/Oelbn3tUtoab1APb70v1JKKaWUUkoppZRS" "Sl1NOxERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERERER" "ERERERERERERERERERERERERERERERERERERERERERGRO+Qfh5eOajemXSYAAAFTbWtCVPrO" "yv4Af1WJAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAB4nO3W4WmDYBSGUQdxEQdxEBdxEAdxEQexvIELt6Yh/4oJ" "54FDm0/7601szlOSJEmSJEmSJEmSJEmSJEmSJEkf0XEc577vT+c5y7V397+6T/dvXddzHMdz" "mqbHz+wY/Sz31L11FsuyPF7HMAx/vod077JjlX2zYXatzfs9tX/VN7/+je5ftut7Vjnrn+V6" "nX37xtm/ul7T/ctzvu9f/9fneX7aP9fs/31l23ru1+/btv36zPfnv/2/r/oe1/er90Cu1Xf7" "nEXVnx3Xa5IkSZIkSZIkSfr3BgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAA+EA/CvmsuD1UqYgAAA7XbWtCVPrOyv4Af594AAAAAAAAAAAAAAAAAAAAAAAAAAAA" "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAB4nO2djZEc" "KQyFHYgTcSAOxIk4EAfiRBzIXunqPte7Z0lAz8/+WK9qame7aRASCNCDnpeXwWAwGAwGg8Fg" "MBgMBoPB4D/8+vXr5efPn3984jr3qufic6WsAGX498H/Uen5iv4zfP/+/eXTp09/fOI69zJ8" "+fLl388uvn379jvvsDdlBPT7R0bU+7SelZ5P9b8CNtH+rvZf9VH6dpWmk9ft3/mdXVTyrOQE" "XRq9XqXLrmftvHs+cGrnq3rr7B/la991ubRvex6aD3kFqv6veWX1jvufP3/+93voLdL9+PHj" "9714hrqoLwtEOr0e6TNE/p4m8oi8uRdlq15IF9f1eeqgaSMvT0cd9Hr8jc+q/8ffr1+//n7u" "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" "j/cX3tpLeKun00DwW5DyzkmZvtRZQl0WVKqm4p6QB5mP5//60UtxBckuAuG9gFDW23cb/7zD" "00FHXPSaV8LPi4HY4jn54w7PMlMes5flQVzok1lcnN95Pceo8Edq977M6cf11aLCTe5AGuKM" "dNSCtoR2A0R/vvyDDnrOK7LZzEIOxLpct5+s/LzD1ayF99nrNsvba5k2TP64yqbaUt9fcv1u" "nWx8VUHPrxA8EQqiuct8prIhgrg7uhLBOJlfMdxn6XPejfnGQ5+H/7/kIAs+6lZCiX7mLLa5" "rhmgy5hf/yZmmeTVanDxL1fZ1I3Kd2EA+U8gvJqwSAwSM8nb+/6+AUlgmMjyddj5Fbv1uDHq" "zaTJ+7cIyM/3/3/lK1/5yle+8pWvfOUrX/nKV77yla985Stf+cpXvvKVr3zlK1/5yle+8pWv" "fOUrX/nKV77yla985Stf+cpXvvKVr3zlK1/5yle+8pWvfOUrX/nKV77yla985Stf+cpXvvKV" "r3zlK1/5yle+8pWvfOUrX/nKV77yla985Stf+cpXvvKVr3zlK1/5yle+8pWvfOUrX/nKV77y" "la985Stf+cpXvvKVr3zlK1/5yle+8hWA/wfdmhmZdymm9wAACEVJREFUWIXNmGtsHFcVx393" "XruzXsd2/G7tpIbEpapUIRFRKZFQ1YBUCVSFQKUIlbYiKhUSVZEA9UMpBfohH0gUtXwDRENb" "SqgQuJZCH0lMyyOKSGr6MnUeTuzY3SSt147ttXdn5s69fNiZ9XjspClxJI50tLOzs3d+8z/3" "3jnnCK01/89mrPaAmzZtemI1x7sqQLFohhBisxDiy0IIRwiREUK4Qohc5HVa661CiLwQoi5x" "3o2udYQQthDCjMYSQghxTYDRACK61gRywAOADTiRZ4Bs5EHqe3zcB2wHrMjNaMwrQlpXARZ7" "POC/gRC4A/hX4rwBiDAMzwJ1gI5cAb1AD3AweqAwchV9aiGEBrROLQqx0iJJwcXKmYmn37Jr" "167NW7Zs2S6E2KiUIgzDmsfflVL4vn967969w0eOHBkEfgXISGUZeRJ2GeQywBTcF4DPAM9H" "YM7hw4fva2ho+HYmk9mglEJKSRAESCkJwxAp5RJAKSVSSkql0sj4+Pi+xx577AXAjyD9FUCX" "QK4EaCSUuxl4Anjn4MGDlba2tgdd190ohCAMQzzP46233mPs3DgzMzPIUFbngmVTv2YN7a0t" "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" "xpabAAAAAElFTkSuQmCC") MOVE = PyEmbeddedImage( "iVBORw0KGgoAAAANSUhEUgAAABsAAAAbCAYAAACN1PRVAAAAAXNSR0IArs4c6QAAAr5JREFU" "eJy0lDtP6mAYx8skoRE64SWQnMVE6CdAHJjP3lnDJaALO6CcURcTAqjgQCKDcg0rLH4ICkQ2" "IAyAWjVNUJee52lOT1qoJ+0Bhn/K+9L3/fX/3AhBEAg9en19JRqNBqP3HEr3gfv7+wOn0zl4" "f383rRT2+flJulwujoBjl5eX8ZXCqtXqMYJQu7u7PLizrQQGroi9vb2WBENdX1//WgmsUql4" "DAaDIIc5HI7W29vbcmHgygS5YuUgSVdXVwdLhd3e3sbVQCibzcaNx2NqaTDoq5/pdJpBnZ2d" "MV6v90Jap1IpZjAY/FgY1u12PdPpVLHH8zxRq9XmmhqBk8nknw5VN1mWpfx+f85utwuj0Ujx" "H66hsedgyWSS2dnZ4SCHh98VjWLRarU8R0dHRYvFwmM+1tfXBfhaTTAMqZRHmDAsNj2MNtsc" "rNls0oFA4AEvlyefoiiB4zjFpc/Pz0SpVJqDgSNmtnhomuYRCs1PirBQKJQzGo0fapVGkqRw" "c3PDFAoFBt3gM5vNMuFw+KJYLIp7qHK5LBbNdxUL/Tio1+uMmJ9IJJLb2tqaewnCKXQ6HTF0" "kiDURCaTYeR7UPoEVqkayO12c3d3d4cYkb9h6Pf7zpOTk/T29jYvDyO+JA8X5hBdzYYRwqWA" "7e/vs+D4+Ovry6TI2UwJUwCNg1N+bW1NwK/WUiCJRIL544SFnHpwlmrus16vR5+fnz+8vLwo" "9p+enlSdYePn8/k4OCEXmiAIxPCh2u02gUUjrdHpbOMvBEOHZrNZzCE+TSaT+BuFjf/4+OhZ" "GgyLx2q18mrV5vP5clru0AxDxWKxuckPQ4CHsGqa+Lpgw+GQ2tzcVLgLBoNFred1wVCnp6dp" "mSsBXGnK1X/BwB29sbEhwnCW6jmrG4aKRqM5mJkf4Mq5chgWBA5vvedQvwEAAP//AwAd2Wky" "Dk2mEgAAAABJRU5ErkJggg==") def interpFloat(t, v1, v2): "interpolator for a single value; interprets t in [0-1] between v1 and v2" return (v2-v1)*t + v1 def tFromValue(value, v1, v2): "returns a t (in range 0-1) given a value in the range v1 to v2" return float(value-v1)/(v2-v1) def clamp(v, minv, maxv): "clamps a value within a range" if v maxv: v=maxv return v def toLog(t, v1, v2): return math.log10(t/v1) / math.log10(v2/v1) def toExp(t, v1, v2): return math.pow(10, t * (math.log10(v2) - math.log10(v1)) + math.log10(v1)) class ZyneControlSlider(ControlSlider): def __init__(self, parent, minvalue, maxvalue, init=None, pos=(0,0), size=(200,16), log=False, outFunction=None, integer=False, powoftwo=False, backColour=None): ControlSlider.__init__(self, parent, minvalue, maxvalue, init, pos, size, log, outFunction, integer, powoftwo, backColour) def setValue(self, x): wx.CallAfter(self.SetValue, x) def MouseDown(self, evt): if vars.vars["MIDILEARN"]: if vars.vars["LEARNINGSLIDER"] == None: vars.vars["LEARNINGSLIDER"] = self self.Disable() elif vars.vars["LEARNINGSLIDER"] == self: vars.vars["LEARNINGSLIDER"].setMidiCtl(None) vars.vars["LEARNINGSLIDER"] = None self.Enable() evt.StopPropagation() else: ControlSlider.MouseDown(self, evt) class ControlKnob(wx.Panel): def __init__(self, parent, minvalue, maxvalue, init=None, pos=(0,0), size=(44,70), log=False, outFunction=None, integer=False, backColour=None, label=''): wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY, pos=pos, size=size, style=wx.NO_BORDER | wx.WANTS_CHARS) self.parent = parent self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) self.SetBackgroundColour(BACKGROUND_COLOUR) self.SetMinSize(self.GetSize()) self.knobBitmap = KNOB.GetBitmap() self.outFunction = outFunction self.integer = integer self.log = log self.label = label self.SetRange(minvalue, maxvalue) self.borderWidth = 1 self.selected = False self._enable = True self.midictl = None self.new = '' self.floatPrecision = '%.3f' if backColour: self.backColour = backColour else: self.backColour = BACKGROUND_COLOUR if init != None: self.SetValue(init) self.init = init else: self.SetValue(minvalue) self.init = minvalue self.Bind(wx.EVT_LEFT_DOWN, self.MouseDown) self.Bind(wx.EVT_LEFT_UP, self.MouseUp) self.Bind(wx.EVT_LEFT_DCLICK, self.DoubleClick) self.Bind(wx.EVT_MOTION, self.MouseMotion) self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_KEY_DOWN, self.keyDown) self.Bind(wx.EVT_KILL_FOCUS, self.LooseFocus) def setMidiCtl(self, x, propagate=True): self.propagate = propagate self.midictl = x self.Refresh() def getMidiCtl(self): return self.midictl def setFloatPrecision(self, x): self.floatPrecision = '%.' + '%df' % x self.Refresh() def getMinValue(self): return self.minvalue def getMaxValue(self): return self.maxvalue def Enable(self): self._enable = True self.Refresh() def Disable(self): self._enable = False self.Refresh() def getInit(self): return self.init def getLabel(self): return self.label def getLog(self): return self.log def SetRange(self, minvalue, maxvalue): self.minvalue = minvalue self.maxvalue = maxvalue def getRange(self): return [self.minvalue, self.maxvalue] def SetValue(self, value): if self.HasCapture(): self.ReleaseMouse() value = clamp(value, self.minvalue, self.maxvalue) if self.log: t = toLog(value, self.minvalue, self.maxvalue) self.value = interpFloat(t, self.minvalue, self.maxvalue) else: t = tFromValue(value, self.minvalue, self.maxvalue) self.value = interpFloat(t, self.minvalue, self.maxvalue) if self.integer: self.value = int(self.value) self.selected = False self.Refresh() def GetValue(self): if self.log: t = tFromValue(self.value, self.minvalue, self.maxvalue) val = toExp(t, self.minvalue, self.maxvalue) else: val = self.value if self.integer: val = int(val) return val def LooseFocus(self, event): self.selected = False self.Refresh() def keyDown(self, event): if self.selected: char = '' if event.GetKeyCode() in range(324, 334): char = str(event.GetKeyCode() - 324) elif event.GetKeyCode() == 390: char = '-' elif event.GetKeyCode() == 391: char = '.' elif event.GetKeyCode() == wx.WXK_BACK: if self.new != '': self.new = self.new[0:-1] elif event.GetKeyCode() < 256: char = chr(event.GetKeyCode()) if char in ['0', '1', '2', '3', '4', '5', '6', '7', '8', '9']: self.new += char elif char == '.' and not '.' in self.new: self.new += char elif char == '-' and len(self.new) == 0: self.new += char elif event.GetKeyCode() in [wx.WXK_RETURN, wx.WXK_NUMPAD_ENTER]: if self.new != '': self.SetValue(eval(self.new)) self.new = '' self.selected = False self.Refresh() event.Skip() def MouseDown(self, evt): if evt.ShiftDown(): self.DoubleClick(evt) return if self._enable: rec = wx.Rect(5, 13, 45, 45) pos = evt.GetPosition() if rec.Contains(pos): self.clickPos = wx.GetMousePosition() self.oldValue = self.value self.CaptureMouse() self.selected = False self.Refresh() evt.Skip() def MouseUp(self, evt): if self.HasCapture(): self.ReleaseMouse() def DoubleClick(self, event): if self._enable: w, h = self.GetSize() pos = event.GetPosition() reclab = wx.Rect(5, 55, w-10, 13) recpt = wx.Rect(self.knobPointPos[0]-3, self.knobPointPos[1]-3, 9, 9) if reclab.Contains(pos): self.selected = True self.Refresh() event.Skip() def MouseMotion(self, evt): if self._enable: if evt.Dragging() and evt.LeftIsDown() and self.HasCapture(): pos = wx.GetMousePosition() offY = self.clickPos[1] - pos[1] off = offY off *= 0.005 * (self.maxvalue - self.minvalue) self.value = clamp(self.oldValue + off, self.minvalue, self.maxvalue) self.selected = False self.Refresh() def setbackColour(self, colour): self.backColour = colour self.Refresh() def OnPaint(self, evt): w,h = self.GetSize() dc = wx.AutoBufferedPaintDC(self) dc.SetBrush(wx.Brush(self.backColour, wx.SOLID)) dc.Clear() # Draw background dc.SetPen(wx.Pen(self.backColour, width=self.borderWidth, style=wx.SOLID)) dc.DrawRectangle(0, 0, w, h) if vars.constants["PLATFORM"] == "darwin": dc.SetFont(wx.Font(10, wx.ROMAN, wx.NORMAL, wx.NORMAL)) else: dc.SetFont(wx.Font(7, wx.ROMAN, wx.NORMAL, wx.NORMAL)) dc.SetTextForeground("#000000") # Draw text label reclab = wx.Rect(0, 1, w, 9) dc.DrawLabel(self.label, reclab, wx.ALIGN_CENTER_HORIZONTAL) recval = wx.Rect(5, 55, w-10, 13) if self.selected: dc.SetBrush(wx.Brush('#FFFFFF', wx.SOLID)) dc.SetPen(wx.Pen('#FFFFFF', width=self.borderWidth, style=wx.SOLID)) dc.DrawRoundedRectangleRect(recval, 3) dc.DrawBitmap(self.knobBitmap, 2, 13, True) r = 0.17320508075688773 # math.sqrt(.03) val = tFromValue(self.value, self.minvalue, self.maxvalue) * 0.87 ph = val * math.pi * 2 - (3 * math.pi / 2.2) X = int(round(r * math.cos(ph)*45)) Y = int(round(r * math.sin(ph)*45)) dc.SetPen(wx.Pen("#000000", width=1, style=wx.SOLID)) dc.SetBrush(wx.Brush("#000000", wx.SOLID)) self.knobPointPos = (X+22, Y+33) dc.DrawCircle(X+22, Y+33, 2) # Draw text value if self.selected and self.new: val = self.new else: if self.integer: val = '%d' % self.GetValue() else: val = self.floatPrecision % self.GetValue() if sys.platform == 'linux2': width = len(val) * (dc.GetCharWidth() - 3) else: width = len(val) * dc.GetCharWidth() dc.SetTextForeground('#000000') dc.DrawLabel(val, recval, wx.ALIGN_CENTER) # Send value if self.outFunction: self.outFunction(self.GetValue()) evt.Skip() class Keyboard(wx.Panel): def __init__(self, parent, size=(630,80), outFunction=None, poly=64): wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY, size=size, style=wx.NO_BORDER) self.parent = parent self.outFunction= outFunction self.poly = poly self.gap = 0 self.w1 = 15 self.w2 = self.w1 / 2 + 1 self.hold = 1 self.keyPressed = None self.offset = 12 self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) self.SetBackgroundColour(BACKGROUND_COLOUR) self.Bind(wx.EVT_LEFT_DOWN, self.MouseDown) self.Bind(wx.EVT_LEFT_UP, self.MouseUp) self.Bind(wx.EVT_PAINT, self.OnPaint) self.Bind(wx.EVT_SIZE, self.OnSize) self.SetMinSize((-1, size[1])) self.SetMaxSize((-1, size[1])) self.SetSize((-1, size[1])) self.white = (0,2,4,5,7,9,11) self.black = (1,3,6,8,10) self.whiteSelected = [] self.blackSelected = [] self.whiteVelocities = {} self.blackVelocities = {} self.whiteKeys = [] self.blackKeys = [] wx.CallAfter(self.setRects) def getNotes(self): notes = [] for key in self.whiteSelected: notes.append((self.white[key%7] + key/7*12 + self.offset, 127-self.whiteVelocities[key])) for key in self.blackSelected: notes.append((self.black[key%5] + key/5*12 + self.offset, 127-self.blackVelocities[key])) notes.sort() return notes def reset(self): self.whiteSelected = [] self.blackSelected = [] self.whiteVelocities = {} self.blackVelocities = {} self.Refresh() def setPoly(self, poly): self.poly = poly def setRects(self): w,h = self.GetSize() self.offRec = wx.Rect(w-55, 0, 21, h) self.holdRec = wx.Rect(w-34, 0, 21, h) num = w / self.w1 self.gap = w - num * self.w1 self.whiteKeys = [wx.Rect(i*self.w1, 0, self.w1-1, h) for i in range(num)] self.blackKeys = [] height2 = h * 4 / 7 for i in range(num/7+1): space2 = self.w1 * 7 * i off = self.w1 / 2 + space2 + 3 self.blackKeys.append(wx.Rect(off, 0, self.w2, height2)) off += self.w1 self.blackKeys.append(wx.Rect(off, 0, self.w2, height2)) off += self.w1 * 2 self.blackKeys.append(wx.Rect(off, 0, self.w2, height2)) off += self.w1 self.blackKeys.append(wx.Rect(off, 0, self.w2, height2)) off += self.w1 self.blackKeys.append(wx.Rect(off, 0, self.w2, height2)) self.Refresh() def OnSize(self, evt): self.setRects() wx.CallAfter(self.Refresh) def MouseUp(self, evt): if not self.hold and self.keyPressed != None: key = self.keyPressed[0] pit = self.keyPressed[1] if key in self.blackSelected: self.blackSelected.remove(key) del self.blackVelocities[key] if key in self.whiteSelected: self.whiteSelected.remove(key) del self.whiteVelocities[key] note = (pit, 0) self.keyPressed = None if self.outFunction: self.outFunction(note) self.Refresh() def MouseDown(self, evt): w,h = self.GetSize() pos = evt.GetPosition() if self.holdRec.Contains(pos): if self.hold: self.hold = 0 self.GetTopLevelParent().onResetKeyboard(None) else: self.hold = 1 self.Refresh() return if self.offUpRec.Contains(pos): self.offset += 12 if self.offset > 60: self.offset = 60 self.GetTopLevelParent().onResetKeyboard(None) self.Refresh() return if self.offDownRec.Contains(pos): self.offset -= 12 if self.offset < 0: self.offset = 0 self.GetTopLevelParent().onResetKeyboard(None) self.Refresh() return total = len(self.blackSelected) + len(self.whiteSelected) scanWhite = True note = None if self.hold: for i, rec in enumerate(self.blackKeys): if rec.Contains(pos): pit = self.black[i%5] + i/5*12 + self.offset if i in self.blackSelected: self.blackSelected.remove(i) del self.blackVelocities[i] vel = 0 else: hb = h * 4 / 7 vel = (hb - pos[1]) * 127 / hb if total < self.poly: self.blackSelected.append(i) self.blackVelocities[i] = int(127 - vel) note = (pit, vel) scanWhite = False break if scanWhite: for i, rec in enumerate(self.whiteKeys): if rec.Contains(pos): pit = self.white[i%7] + i/7*12 + self.offset if i in self.whiteSelected: self.whiteSelected.remove(i) del self.whiteVelocities[i] vel = 0 else: vel = (h - pos[1]) * 127 / h if total < self.poly: self.whiteSelected.append(i) self.whiteVelocities[i] = int(127 - vel) note = (pit, vel) break if self.outFunction and note: if note[1] == 0: self.outFunction(note) elif total < self.poly: self.outFunction(note) else: self.keyPressed = None for i, rec in enumerate(self.blackKeys): if rec.Contains(pos): pit = self.black[i%5] + i/5*12 + self.offset if i not in self.blackSelected: hb = h * 4 / 7 vel = (hb - pos[1]) * 127 / hb if total < self.poly: self.blackSelected.append(i) self.blackVelocities[i] = int(127 - vel) note = (pit, vel) self.keyPressed = (i, pit) scanWhite = False break if scanWhite: for i, rec in enumerate(self.whiteKeys): if rec.Contains(pos): pit = self.white[i%7] + i/7*12 + self.offset if i not in self.whiteSelected: vel = (h - pos[1]) * 127 / h if total < self.poly: self.whiteSelected.append(i) self.whiteVelocities[i] = int(127 - vel) note = (pit, vel) self.keyPressed = (i, pit) break if self.outFunction and note: if total < self.poly: self.outFunction(note) self.Refresh() def OnPaint(self, evt): w,h = self.GetSize() dc = wx.AutoBufferedPaintDC(self) dc.SetBrush(wx.Brush("#000000", wx.SOLID)) dc.Clear() dc.SetPen(wx.Pen("#000000", width=1, style=wx.SOLID)) dc.DrawRectangle(0, 0, w, h) if vars.constants["PLATFORM"] == "darwin": dc.SetFont(wx.Font(12, wx.ROMAN, wx.NORMAL, wx.BOLD)) else: dc.SetFont(wx.Font(8, wx.ROMAN, wx.NORMAL, wx.BOLD)) for i, rec in enumerate(self.whiteKeys): if i in self.whiteSelected: amp = int(self.whiteVelocities[i] * 1.5) dc.GradientFillLinear(rec, (250,250,250), (amp,amp,amp), wx.SOUTH) dc.SetBrush(wx.Brush("#CCCCCC", wx.SOLID)) dc.SetPen(wx.Pen("#CCCCCC", width=1, style=wx.SOLID)) else: dc.SetBrush(wx.Brush("#FFFFFF", wx.SOLID)) dc.SetPen(wx.Pen("#FFFFFF", width=1, style=wx.SOLID)) dc.DrawRectangleRect(rec) if i == (35 - (7 * (self.offset / 12))): if i in self.whiteSelected: dc.SetTextForeground("#FFFFFF") else: dc.SetTextForeground("#000000") dc.DrawText("C", rec[0]+3, rec[3]-15) dc.SetPen(wx.Pen("#000000", width=1, style=wx.SOLID)) for i, rec in enumerate(self.blackKeys): if i in self.blackSelected: amp = int(self.blackVelocities[i] * 1.5) dc.GradientFillLinear(rec, (250,250,250), (amp,amp,amp), wx.SOUTH) dc.DrawLine(rec[0],0,rec[0],rec[3]) dc.DrawLine(rec[0]+rec[2],0,rec[0]+rec[2],rec[3]) dc.DrawLine(rec[0],rec[3],rec[0]+rec[2],rec[3]) dc.SetBrush(wx.Brush("#DDDDDD", wx.SOLID)) else: dc.SetBrush(wx.Brush("#000000", wx.SOLID)) dc.SetPen(wx.Pen("#000000", width=1, style=wx.SOLID)) dc.DrawRectangleRect(rec) dc.SetBrush(wx.Brush(BACKGROUND_COLOUR, wx.SOLID)) dc.SetPen(wx.Pen("#AAAAAA", width=1, style=wx.SOLID)) dc.DrawRectangleRect(self.offRec) dc.DrawRectangleRect(self.holdRec) dc.DrawRectangleRect(wx.Rect(w-14, 0, 14, h)) dc.SetTextForeground("#000000") dc.DrawText("off", self.offRec[0]+3, 15) x1, y1 = self.offRec[0], self.offRec[1] dc.SetBrush(wx.Brush("#000000", wx.SOLID)) if vars.constants["PLATFORM"] == "darwin": dc.DrawPolygon([wx.Point(x1+3,36), wx.Point(x1+10,29), wx.Point(x1+17,36)]) self.offUpRec = wx.Rect(x1, 28, x1+20, 10) dc.DrawPolygon([wx.Point(x1+3,55), wx.Point(x1+10,62), wx.Point(x1+17,55)]) self.offDownRec = wx.Rect(x1, 54, x1+20, 10) else: dc.DrawPolygon([wx.Point(x1+3,38), wx.Point(x1+10,31), wx.Point(x1+17,38)]) self.offUpRec = wx.Rect(x1, 30, x1+20, 10) dc.DrawPolygon([wx.Point(x1+3,57), wx.Point(x1+10,64), wx.Point(x1+17,57)]) self.offDownRec = wx.Rect(x1, 56, x1+20, 10) dc.DrawText("%d" % (self.offset/12), x1+7, 41) if self.hold: dc.SetTextForeground("#0000CC") else: dc.SetTextForeground("#000000") for i, char in enumerate("HOLD"): dc.DrawText(char, self.holdRec[0]+6, self.holdRec[3]/6*i+15) dc.SetBrush(wx.Brush(BACKGROUND_COLOUR, wx.SOLID)) dc.SetPen(wx.Pen(BACKGROUND_COLOUR, width=1, style=wx.SOLID)) dc.DrawRectangle(w-self.gap, 0, self.gap, h) dc.SetPen(wx.Pen("#000000", width=1, style=wx.SOLID)) dc.DrawLine(0,3,w,3) dc.SetPen(wx.Pen("#444444", width=1, style=wx.SOLID)) dc.DrawLine(0,2,w,2) dc.SetPen(wx.Pen("#888888", width=1, style=wx.SOLID)) dc.DrawLine(0,1,w,1) dc.SetPen(wx.Pen("#CCCCCC", width=1, style=wx.SOLID)) dc.DrawLine(0,0,w,0) class ZyneStaticLine(wx.Panel): def __init__(self, parent, pos=wx.DefaultPosition, size=(230,2)): wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY, pos=pos, size=size, style=wx.NO_BORDER) self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) self.SetBackgroundColour(BACKGROUND_COLOUR) self.backColour = BACKGROUND_COLOUR self.SetSize(size) self.Bind(wx.EVT_PAINT, self.OnPaint) def setBackgroundColour(self, col): self.backColour = col self.Refresh() def OnPaint(self, evt): w,h = self.GetSize() dc = wx.AutoBufferedPaintDC(self) dc.SetBrush(wx.Brush(self.backColour, wx.SOLID)) dc.Clear() dc.SetPen(wx.Pen(self.backColour, width=1, style=wx.SOLID)) dc.DrawRectangle(0, 0, w, h) dc.SetPen(wx.Pen("#CCCCCC", width=1, style=wx.SOLID)) dc.DrawLine(4, 0, w-8, 0) dc.SetPen(wx.Pen("#AAAAAA", width=1, style=wx.SOLID)) dc.DrawLine(4, 1, w-8, 1) class ZyneStaticBitmap(wx.Panel): def __init__(self, parent, bitmap, pos=wx.DefaultPosition, size=(16,16)): wx.Panel.__init__(self, parent=parent, id=wx.ID_ANY, pos=pos, size=size, style=wx.NO_BORDER) self.SetBackgroundStyle(wx.BG_STYLE_CUSTOM) self.SetBackgroundColour(BACKGROUND_COLOUR) self.bitmap = bitmap self.backColour = BACKGROUND_COLOUR self.SetSize(size) self.Bind(wx.EVT_PAINT, self.OnPaint) def setBackgroundColour(self, col): self.backColour = col self.Refresh() def OnPaint(self, evt): w,h = self.GetSize() dc = wx.AutoBufferedPaintDC(self) dc.SetBrush(wx.Brush(self.backColour, wx.SOLID)) dc.Clear() dc.SetPen(wx.Pen(self.backColour, width=1, style=wx.SOLID)) dc.DrawRectangle(0, 0, w, h) dc.DrawBitmap(self.bitmap, 0, 0, True) zyne/Zyne.py0000644000175000017500000011543212417525337012351 0ustar tiagotiago#!/usr/bin/env python # encoding: utf-8 import wxversion wxversion.select("2.8") import wx, os, sys, urllib import Resources.variables as vars from Resources.panels import * from Resources.preferences import PreferencesDialog from Resources.splash import ZyneSplashScreen import wx.richtext as rt import Resources.audio as audio import Resources.tutorial as tutorial from pyo import * class TutorialFrame(wx.Frame): def __init__(self, *args, **kw): wx.Frame.__init__(self, *args, **kw) self.menubar = wx.MenuBar() self.fileMenu = wx.Menu() self.fileMenu.Append(vars.constants["ID"]["CloseTut"], 'Close...\tCtrl+W', kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.onClose, id=vars.constants["ID"]["CloseTut"]) self.menubar.Append(self.fileMenu, "&File") self.SetMenuBar(self.menubar) self.code = False self.rtc = rt.RichTextCtrl(self, style=wx.VSCROLL|wx.HSCROLL|wx.NO_BORDER) self.rtc.SetEditable(False) wx.CallAfter(self.rtc.SetFocus) self.rtc.Freeze() self.rtc.BeginSuppressUndo() self.rtc.BeginParagraphSpacing(0, 20) self.rtc.BeginBold() if vars.constants["PLATFORM"] in ["win32", "linux2"]: self.rtc.BeginFontSize(12) else: self.rtc.BeginFontSize(16) self.rtc.WriteText("Welcome to the tutorial on how to create a custom zyne module.") self.rtc.EndFontSize() self.rtc.EndBold() self.rtc.Newline() lines = [vars.vars["ensureNFD"](line) for line in tutorial.__doc__.splitlines(True)] section_count = 1 for line in lines: if line.count("----") == 2: self.rtc.BeginBold() if vars.constants["PLATFORM"] in ["win32", "linux2"]: self.rtc.BeginFontSize(12) else: self.rtc.BeginFontSize(16) self.rtc.WriteText("%i.%s" % (section_count, line.replace("----", ""))) self.rtc.EndFontSize() self.rtc.EndBold() section_count += 1 elif not self.code and line.startswith("class") or line.startswith("MODULES"): self.code = True if vars.constants["PLATFORM"] in ["win32", "linux2"]: self.rtc.BeginFontSize(8) else: self.rtc.BeginFontSize(12) self.rtc.BeginItalic() self.rtc.WriteText(line) elif self.code and not line.startswith(" ") and not line.startswith("class") and not line.startswith("MODULES"): self.code = False self.rtc.EndItalic() self.rtc.EndFontSize() self.rtc.WriteText(line) else: self.rtc.WriteText(line) self.rtc.Newline() self.rtc.EndParagraphSpacing() self.rtc.EndSuppressUndo() self.rtc.Thaw() def onClose(self, evt): self.Destroy() class SamplingDialog(wx.Dialog): def __init__(self, parent, title="Export Samples...", pos=wx.DefaultPosition, size=wx.DefaultSize): wx.Dialog.__init__(self, parent, id=1, title=title, pos=pos, size=size) sizer = wx.BoxSizer(wx.VERTICAL) sizer.Add(wx.StaticText(self, -1, "Export settings for sampled sounds."), 0, wx.ALIGN_CENTRE|wx.ALL, 5) box = wx.BoxSizer(wx.HORIZONTAL) box.Add(wx.StaticText(self, -1, "Common file name :"), 0, wx.ALIGN_CENTRE|wx.ALL, 5) self.filename = wx.TextCtrl(self, -1, "zyne", size=(80,-1)) box.Add(self.filename, 1, wx.ALIGN_CENTRE|wx.ALL, 5) sizer.Add(box, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) box = wx.BoxSizer(wx.HORIZONTAL) box.Add(wx.StaticText(self, -1, "First:"), 0, wx.ALIGN_CENTRE|wx.ALL, 5) self.first = wx.TextCtrl(self, -1, "0", size=(40,-1)) box.Add(self.first, 1, wx.ALIGN_CENTRE|wx.ALL, 5) box.Add(wx.StaticText(self, -1, "Last:"), 0, wx.ALIGN_CENTRE|wx.ALL, 5) self.last = wx.TextCtrl(self, -1, "128", size=(40,-1)) box.Add(self.last, 1, wx.ALIGN_CENTRE|wx.ALL, 5) box.Add(wx.StaticText(self, -1, "Step:"), 0, wx.ALIGN_CENTRE|wx.ALL, 5) self.step = wx.TextCtrl(self, -1, "1", size=(40,-1)) box.Add(self.step, 1, wx.ALIGN_CENTRE|wx.ALL, 5) sizer.Add(box, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) box = wx.BoxSizer(wx.HORIZONTAL) box.Add(wx.StaticText(self, -1, "Noteon dur:"), 0, wx.ALIGN_CENTRE|wx.ALL, 5) self.noteon = wx.TextCtrl(self, -1, "1", size=(50,-1)) box.Add(self.noteon, 1, wx.ALIGN_CENTRE|wx.ALL, 5) box.Add(wx.StaticText(self, -1, "Release dur:"), 0, wx.ALIGN_CENTRE|wx.ALL, 5) self.release = wx.TextCtrl(self, -1, "1", size=(50,-1)) box.Add(self.release, 1, wx.ALIGN_CENTRE|wx.ALL, 5) sizer.Add(box, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.ALL, 5) line = wx.StaticLine(self, -1, size=(20,-1), style=wx.LI_HORIZONTAL) sizer.Add(line, 0, wx.GROW|wx.ALIGN_CENTER_VERTICAL|wx.RIGHT|wx.TOP, 5) btnsizer = wx.StdDialogButtonSizer() btn = wx.Button(self, wx.ID_OK) btn.SetDefault() btnsizer.AddButton(btn) btn = wx.Button(self, wx.ID_CANCEL) btnsizer.AddButton(btn) btnsizer.Realize() sizer.Add(btnsizer, 0, wx.ALIGN_RIGHT|wx.ALL, 5) self.SetSizer(sizer) self.filename.SetFocus() class ZyneFrame(wx.Frame): def __init__(self, parent=None, title=u"Zyne Synth - Untitled", size=(920,522)): wx.Frame.__init__(self, parent, id=-1, title=title, size=size) self.SetAcceleratorTable(wx.AcceleratorTable([(wx.ACCEL_NORMAL, ord("\t"), vars.constants["ID"]["Select"]), (wx.ACCEL_SHIFT, ord("\t"), vars.constants["ID"]["DeSelect"])])) self.menubar = wx.MenuBar() self.fileMenu = wx.Menu() self.fileMenu.Append(vars.constants["ID"]["New"], 'New...\tCtrl+N', kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.onNew, id=vars.constants["ID"]["New"]) self.fileMenu.Append(vars.constants["ID"]["Open"], 'Open...\tCtrl+O', kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.onOpen, id=vars.constants["ID"]["Open"]) self.fileMenu.Append(vars.constants["ID"]["Save"], 'Save\tCtrl+S', kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.onSave, id=vars.constants["ID"]["Save"]) self.fileMenu.Append(vars.constants["ID"]["SaveAs"], 'Save as...\tShift+Ctrl+S', kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.onSaveAs, id=vars.constants["ID"]["SaveAs"]) self.fileMenu.AppendSeparator() self.fileMenu.Append(vars.constants["ID"]["Export"], 'Export as samples...\tCtrl+E', kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.onExport, id=vars.constants["ID"]["Export"]) self.fileMenu.Append(vars.constants["ID"]["ExportChord"], 'Export as chords...\tShift+Ctrl+E', kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.onExport, id=vars.constants["ID"]["ExportChord"]) self.fileMenu.Append(vars.constants["ID"]["ExportTracks"], 'Export samples as separated tracks...\tCtrl+F', kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.onExport, id=vars.constants["ID"]["ExportTracks"]) self.fileMenu.Append(vars.constants["ID"]["ExportChordTracks"], 'Export chords as separated tracks...\tShift+Ctrl+F', kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.onExport, id=vars.constants["ID"]["ExportChordTracks"]) self.fileMenu.AppendSeparator() self.fileMenu.Append(vars.constants["ID"]["MidiLearn"], 'Midi learn mode\tShift+Ctrl+M', kind=wx.ITEM_CHECK) self.Bind(wx.EVT_MENU, self.onMidiLearnMode, id=vars.constants["ID"]["MidiLearn"]) self.fileMenu.Append(vars.constants["ID"]["ResetKeyboard"], 'Reset virtual keyboard\tCtrl+Y', kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.onResetKeyboard, id=vars.constants["ID"]["ResetKeyboard"]) self.fileMenu.Append(vars.constants["ID"]["Retrig"], 'Retrig virtual notes\tCtrl+T', kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.onRetrig, id=vars.constants["ID"]["Retrig"]) pref_item = self.fileMenu.Append(vars.constants["ID"]["Prefs"], 'Preferences...\tCtrl+,', 'Open Cecilia preferences pane', kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.onPreferences, id=vars.constants["ID"]["Prefs"]) self.fileMenu.AppendSeparator() if vars.constants["PLATFORM"] != "win32": self.fileMenu.Append(vars.constants["ID"]["Run"], 'Run\tCtrl+R', kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.onRun, id=vars.constants["ID"]["Run"]) self.fileMenu.AppendSeparator() quit_item = self.fileMenu.Append(vars.constants["ID"]["Quit"], 'Quit\tCtrl+Q', kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.onQuit, id=vars.constants["ID"]["Quit"]) if wx.Platform=="__WXMAC__": wx.App.SetMacExitMenuItemId(quit_item.GetId()) wx.App.SetMacPreferencesMenuItemId(pref_item.GetId()) self.addMenu = wx.Menu() self.buildAddModuleMenu() self.genMenu = wx.Menu() self.genMenu.Append(vars.constants["ID"]["Uniform"], 'Generate uniform random values\tCtrl+G', kind=wx.ITEM_NORMAL) self.genMenu.Append(vars.constants["ID"]["Triangular"], 'Generate triangular random values\tCtrl+K', kind=wx.ITEM_NORMAL) self.genMenu.Append(vars.constants["ID"]["Minimum"], 'Generate minimum random values\tCtrl+L', kind=wx.ITEM_NORMAL) self.genMenu.Append(vars.constants["ID"]["Jitter"], 'Jitterize current values\tCtrl+J', kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.onGenerateValues, id=vars.constants["ID"]["Uniform"], id2=vars.constants["ID"]["Jitter"]) self.genMenu.AppendSeparator() self.genMenu.Append(vars.constants["ID"]["Select"], 'Tabulate selection\tTab', kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.tabulate, id=vars.constants["ID"]["Select"]) self.genMenu.Append(vars.constants["ID"]["DeSelect"], 'Clear selection\tShift+Tab', kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.clearSelection, id=vars.constants["ID"]["DeSelect"]) self.genMenu.AppendSeparator() self.genMenu.Append(vars.constants["ID"]["Duplicate"], 'Duplicate selected module\tCtrl+D', kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.duplicateSelection, id=vars.constants["ID"]["Duplicate"]) item = self.genMenu.FindItemById(vars.constants["ID"]["Duplicate"]) item.Enable(False) helpMenu = wx.Menu() helpItem = helpMenu.Append(vars.constants["ID"]["About"], '&About Zyne %s' % vars.constants["VERSION"], 'wxPython RULES!!!') wx.App.SetMacAboutMenuItemId(helpItem.GetId()) self.Bind(wx.EVT_MENU, self.showAbout, helpItem) tuturialCreateModuleItem = helpMenu.Append(vars.constants["ID"]["Tutorial"], "How to create a custom module") self.Bind(wx.EVT_MENU, self.openTutorialCreateModule, tuturialCreateModuleItem) midiLearnHelpItem = helpMenu.Append(vars.constants["ID"]["MidiLearnHelp"], "How to use the midi learn mode") self.Bind(wx.EVT_MENU, self.openMidiLearnHelp, midiLearnHelpItem) exportHelpItem = helpMenu.Append(vars.constants["ID"]["ExportHelp"], "How to use the export samples window") self.Bind(wx.EVT_MENU, self.openExportHelp, exportHelpItem) self.Bind(wx.EVT_CLOSE, self.onQuit) self.menubar.Append(self.fileMenu, "&File") self.menubar.Append(self.addMenu, "&Modules") self.menubar.Append(self.genMenu, "&Generate") self.menubar.Append(helpMenu, "&Help") self.SetMenuBar(self.menubar) if vars.constants["PLATFORM"] == "win32": self.SetMinSize((460, 554)) elif vars.constants["PLATFORM"] == "darwin": self.SetMinSize((460, 522)) else: self.SetMinSize((460, 520)) self.Bind(wx.EVT_SIZE, self.OnSize) self.openedFile = "" self.modules = [] self.selected = None self.splitWindow = wx.SplitterWindow(self, -1, style = wx.SP_LIVE_UPDATE|wx.SP_PERMIT_UNSPLIT) self.splitWindow.SetSashSize(0) self.sizer = wx.GridBagSizer(0,0) self.panel = wx.Panel(self.splitWindow) self.serverPanel = ServerPanel(self.panel) self.sizer.Add(self.serverPanel, (0,0), (2,1)) self.panel.SetSizer(self.sizer) self.keyboard = Keyboard(self.splitWindow, outFunction=self.serverPanel.onKeyboard) self.serverPanel.keyboard = self.keyboard self.serverPanel.setServerSettings(self.serverPanel.serverSettings) self.splitWindow.SetMinimumPaneSize(0) self.splitWindow.SplitHorizontally(self.panel, self.keyboard, -80) self.splitWindow.Unsplit(None) dropTarget = MyFileDropTarget(self.panel) self.panel.SetDropTarget(dropTarget) if vars.vars["AUTO_OPEN"] == 'Default': self.openfile(os.path.join(vars.constants["RESOURCES_PATH"], "default.zy")) elif vars.vars["AUTO_OPEN"] == 'Last Saved': path = vars.vars["LAST_SAVED"] try: self.openfile(path) except: pass def tabulate(self, evt): num = len(self.modules) old = self.selected if num == 0: return if self.selected == None: self.selected = 0 else: self.selected = (self.selected + 1) % num if old != None: self.modules[old].setBackgroundColour(BACKGROUND_COLOUR) self.modules[self.selected].setBackgroundColour("#DDDDE7") item = self.genMenu.FindItemById(vars.constants["ID"]["Duplicate"]) item.Enable(True) def clearSelection(self, evt): if self.selected != None: self.modules[self.selected].setBackgroundColour(BACKGROUND_COLOUR) self.selected = None item = self.genMenu.FindItemById(vars.constants["ID"]["Duplicate"]) item.Enable(False) def duplicateSelection(self, evt): if self.selected != None: module = self.modules[self.selected] name = module.name mute = module.mute params = [slider.GetValue() for slider in module.sliders] lfo_params = module.getLFOParams() dic = MODULES[name] self.modules.append(GenericPanel(self.panel, name, dic["title"], dic["synth"], dic["p1"], dic["p2"], dic["p3"])) self.addModule(self.modules[-1]) self.modules[-1].setMute(mute) for j, param in enumerate(params): slider = self.modules[-1].sliders[j] slider.SetValue(param) slider.outFunction(param) self.modules[-1].reinitLFOS(lfo_params, ctl_binding=False) self.refresh() old = self.selected self.selected = len(self.modules) - 1 self.modules[old].setBackgroundColour(BACKGROUND_COLOUR) self.modules[self.selected].setBackgroundColour("#DDDDE7") wx.CallAfter(self.SetFocus) def onRun(self, evt): state = self.serverPanel.onOff.GetValue() evt = wx.CommandEvent(10127, self.serverPanel.onOff.GetId()) if state: evt.SetInt(0) self.serverPanel.onOff.SetValue(False) else: evt.SetInt(1) self.serverPanel.onOff.SetValue(True) self.serverPanel.onOff.ProcessEvent(evt) def onGenerateValues(self, evt): id = evt.GetId() - 10000 if self.selected == None: modules = self.modules else: modules = [self.modules[self.selected]] for module in modules: if id == 0: module.generateUniform() elif id == 1: module.generateTriangular() elif id == 2: module.generateMinimum() elif id == 3: module.jitterize() def updateAddModuleMenu(self, evt): for mod in MODULES.keys(): if mod in vars.vars["EXTERNAL_MODULES"]: del MODULES[mod]["synth"] del MODULES[mod] items = self.addMenu.GetMenuItems() for item in items: self.addMenu.DeleteItem(item) audio.checkForCustomModules() self.buildAddModuleMenu() modules, params, lfo_params, ctl_params = self.getModulesAndParams() postProcSettings = self.serverPanel.getPostProcSettings() self.deleteAllModules() self.serverPanel.shutdown() self.serverPanel.boot() self.setModulesAndParams(modules, params, lfo_params, ctl_params) self.serverPanel.setPostProcSettings(postProcSettings) def buildAddModuleMenu(self): self.moduleNames = sorted(MODULES.keys()) id = vars.constants["ID"]["Modules"] for i, name in enumerate(self.moduleNames): if i < 10: self.addMenu.Append(id, 'Add %s module\tCtrl+%d' % (name, ((i+1)%10)), kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.onAddModule, id=id) else: self.addMenu.Append(id, 'Add %s module\tShift+Ctrl+%d' % (name, ((i+1)%10)), kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.onAddModule, id=id) id += 1 self.addMenu.AppendSeparator() if vars.vars["EXTERNAL_MODULES"] != {}: moduleNames = sorted(vars.vars["EXTERNAL_MODULES"].keys()) for i, name in enumerate(moduleNames): self.addMenu.Append(id, 'Add %s module' % vars.vars["toSysEncoding"](name), kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.onAddModule, id=id) self.moduleNames.append(name) MODULES.update(vars.vars["EXTERNAL_MODULES"].items()) id += 1 self.addMenu.AppendSeparator() self.addMenu.Append(vars.constants["ID"]["UpdateModules"], "Update Modules\tCtrl+U", kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.updateAddModuleMenu, id=vars.constants["ID"]["UpdateModules"]) self.addMenu.AppendSeparator() self.addMenu.Append(vars.constants["ID"]["CheckoutModules"], "Checkout external module repository", kind=wx.ITEM_NORMAL) self.Bind(wx.EVT_MENU, self.checkoutExternalModules, id=vars.constants["ID"]["CheckoutModules"]) def checkoutExternalModules(self, evt): if not os.path.isdir(vars.vars["CUSTOM_MODULES_PATH"]): wx.LogMessage("You must define a custom module path in the preferences panel to be able to checkout the server repository!") return url = "http://www.iact.umontreal.ca/zyne/external_modules" file_index = "files.txt" path = os.path.join(url, file_index) if vars.constants["PLATFORM"] == "win32": path = path.replace("\\", "/") (index, msg) = urllib.urlretrieve(path) f = open(index, "r") text = f.read() f.close() files = [line.strip() for line in text.splitlines() if line != ""] if "" in files[0]: wx.LogMessage("Unable to download external modules...") return num_iter = len(files) count = 0 dlg = wx.ProgressDialog("Downloading external modules", "", maximum = num_iter, parent=self, style = wx.PD_APP_MODAL | wx.PD_AUTO_HIDE | wx.PD_SMOOTH) dlg.SetSize((300, 125)) for f in files: (keepGoing, skip) = dlg.Update(count, "Downloading %s" % f) path = os.path.join(url, f) outpath = os.path.join(vars.vars["CUSTOM_MODULES_PATH"], f) if vars.constants["PLATFORM"] == "win32": path = path.replace("\\", "/") outpath = outpath.replace("\\", "/") urllib.urlretrieve(path, outpath) count += 1 dlg.Destroy() self.updateAddModuleMenu(None) def openMidiLearnHelp(self, evt): if vars.constants["PLATFORM"] != "linux2": size = (400, 370) else: size = (400, 300) lines = [] lines.append("To assign midi controllers to module's sliders, user can use the midi learn mode.\n") lines.append("First, hit Shift+Ctrl+M (Shift+Cmd+M on Mac) to start midi learn mode, the server panel will change its background colour.\n") lines.append("When in midi learn mode, click on a slider and play with the midi controller you want to assign, the controller number will appear at both end of the slider.\n") lines.append("To remove a midi assignation, click a second time on the selected slider without playing with a midi controller.\n") lines.append("Finally, hit Shift+Ctrl+M (Shift+Cmd+M on Mac) again to leave midi learn mode. Next time you start the server, you will be able to control the sliders with your midi controller.\n\n") lines.append("Midi assignations are saved within the .zy file and will be automatically assigned at future launches of the synth.\n") win = HelpFrame(self, -1, title="Midi Learn Help", size=size, subtitle="How to use the midi learn mode.", lines=lines) win.CenterOnParent() win.Show(True) def openExportHelp(self, evt): if vars.constants["PLATFORM"] != "linux2": size = (400, 420) else: size = (400, 300) lines = [] lines.append("The export samples window allows the user to create a bank of samples, mapped on a range of midi keys, from the actual state of the current synth.\n") lines.append("The path where the exported samples will be saved can be defined in the preferences panel. If not, a folder named 'zyne_export' will be created on the Desktop. Inside this folder, a subfolder will be created according to the string given in the field 'Common file name'. Samples will be saved inside this subfolder with automatic name incrementation.\n") lines.append("The fields 'First', 'Last' and 'Step' define which notes, in midi keys, will be sampled and exported. From 'First' to 'Last' in steps of 'Step'.\n") lines.append("The fields 'Noteon dur' and 'Release dur' define the duration, in seconds, of the note part and the release part, respectively. The value in 'Noteon dur' should be equal or higher than the addition of the attack and the decay of the longest module. The value in the 'Release part' should be equal or higher than the longest release.\n") win = HelpFrame(self, -1, title="Export Samples Help", size=size, subtitle="How to use the export samples window.", lines=lines) win.CenterOnParent() win.Show(True) def openTutorialCreateModule(self, evt): win = TutorialFrame(self, -1, "Zyne tutorial", size=(700, 500), style=wx.DEFAULT_FRAME_STYLE) win.CenterOnParent() win.Show(True) def showKeyboard(self, state=True): if state: self.splitWindow.SplitHorizontally(self.panel, self.keyboard, -80) self.SetMinSize((460, 602)) else: self.splitWindow.Unsplit() if vars.constants["PLATFORM"] == "win32": self.SetMinSize((460, 542)) self.SetSize((-1, 554)) elif vars.constants["PLATFORM"] == "darwin": self.SetMinSize((460, 522)) self.SetSize((-1, 522)) else: self.SetMinSize((460, 520)) self.SetSize((-1, 520)) def onResetKeyboard(self, evt): self.serverPanel.resetVirtualKeyboard() def onRetrig(self, evt): self.serverPanel.retrigVirtualNotes() def OnSize(self, evt): self.splitWindow.SetSashPosition(-80) self.setModulePostions() evt.Skip() def onMidiLearnModeFromLfoFrame(self): item = self.fileMenu.FindItemById(vars.constants["ID"]["MidiLearn"]) if item.IsChecked(): self.serverPanel.midiLearn(False) vars.vars["MIDILEARN"] = False item.Check(False) else: self.serverPanel.midiLearn(True) vars.vars["MIDILEARN"] = True item.Check(True) def onMidiLearnMode(self, evt): if evt.GetInt(): self.serverPanel.midiLearn(True) vars.vars["MIDILEARN"] = True else: self.serverPanel.midiLearn(False) vars.vars["MIDILEARN"] = False def onPreferences(self, evt): dlg = PreferencesDialog() dlg.ShowModal() dlg.Destroy() def updateLastSavedInPreferencesFile(self, path): preffile = os.path.join(os.path.expanduser("~"), ".zynerc") if os.path.isfile(preffile): with open(preffile, "r") as f: lines = f.readlines() if not lines[0].startswith("### Zyne") or not vars.constants["VERSION"] in lines[0]: return with open(preffile, "w") as f: for line in lines: if "LAST_SAVED" in line: f.write("LAST_SAVED = %s\n" % path) else: f.write(line) def onQuit(self, evt): try: self.serverPanel.keyboardFrame.Destroy() except: pass for win in wx.GetTopLevelWindows(): win.Destroy() self.serverPanel.shutdown() self.Destroy() sys.exit() def onNew(self, evt): self.deleteAllModules() self.openedFile = "" self.SetTitle("Zyne Synth - Untitled") def onSave(self, evt): if self.openedFile != "": self.savefile(self.openedFile) else: self.onSaveAs(evt) def onSaveAs(self, evt): if self.openedFile != "": filename = os.path.split(self.openedFile)[1] else: filename = "zynesynth.zy" dlg = wx.FileDialog(self, "Save file as...", defaultFile=filename, style=wx.FD_SAVE) if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() if path != "": self.savefile(path) dlg.Destroy() def onOpen(self, evt): wildcard = "Zyne files (*.zy)|*.zy" dlg = wx.FileDialog(self, "Choose Zyne Synth file...", wildcard=wildcard, style=wx.FD_OPEN) if dlg.ShowModal() == wx.ID_OK: path = dlg.GetPath() if path != "": self.openfile(path) dlg.Destroy() def onExport(self, evt): if evt.GetId() == vars.constants["ID"]["Export"]: mode = "Samples" title = "Export samples..." title2 = "Exporting samples..." num_modules = 1 elif evt.GetId() in [vars.constants["ID"]["ExportChord"], vars.constants["ID"]["ExportChordTracks"]]: if evt.GetId() == vars.constants["ID"]["ExportChord"]: mode = "Chords" title = "Export chords..." title2 = "Exporting chords..." num_modules = 1 else: mode = "ChordsTracks" title = "Export chords as separated tracks..." title2 = "Exporting chords as separated tracks..." num_modules = len(self.modules) notes = self.keyboard.getNotes() if len(notes) == 0: wx.LogMessage("Play some notes on the virtual keyboard before calling the export chords function!.") return midi_pitches = [tup[0] for tup in notes] midi_velocities = [tup[1] for tup in notes] min_pitch = min(midi_pitches) pitch_factors = [pit - min_pitch for pit in midi_pitches] amp_factors = [amp / 127.0 for amp in midi_velocities] elif evt.GetId() == vars.constants["ID"]["ExportTracks"]: mode = "Tracks" title = "Export samples as separated tracks..." title2 = "Exporting samples as separated tracks..." num_modules = len(self.modules) dlg = SamplingDialog(self, title=title, size=(325,220)) dlg.CenterOnParent() if dlg.ShowModal() == wx.ID_OK: if vars.vars["EXPORT_PATH"] and os.path.isdir(vars.vars["EXPORT_PATH"]): rootpath = vars.vars["EXPORT_PATH"] else: rootpath = os.path.join(os.path.expanduser("~"), "Desktop", "zyne_export") if not os.path.isdir(rootpath): os.mkdir(rootpath) filename = vars.vars["ensureNFD"](dlg.filename.GetValue()) subrootpath = os.path.join(rootpath, filename) if not os.path.isdir(subrootpath): os.mkdir(subrootpath) first = int(dlg.first.GetValue()) last = int(dlg.last.GetValue()) step = int(dlg.step.GetValue()) num_iter = len(range(first,last,step)) * num_modules vars.vars["NOTEONDUR"] = float(dlg.noteon.GetValue()) duration = float(dlg.release.GetValue()) + vars.vars["NOTEONDUR"] ext = self.serverPanel.getExtensionFromFileFormat() modules, params, lfo_params, ctl_params = self.getModulesAndParams() serverSettings = self.serverPanel.getServerSettings() postProcSettings = self.serverPanel.getPostProcSettings() self.deleteAllModules() self.serverPanel.reinitServer(0.001, "offline", serverSettings, postProcSettings) dlg2 = wx.ProgressDialog(title2, "", maximum = num_iter, parent=self, style = wx.PD_APP_MODAL | wx.PD_AUTO_HIDE | wx.PD_SMOOTH) if vars.constants["PLATFORM"] == "win32": dlg2.SetSize((500, 125)) else: dlg2.SetSize((500,100)) count = 0 for i in range(first,last,step): if mode == "Samples": vars.vars["MIDIPITCH"] = i vars.vars["MIDIVELOCITY"] = 0.707 elif mode == "Chords": vars.vars["MIDIPITCH"] = [i + fac for fac in pitch_factors] vars.vars["MIDIVELOCITY"] = amp_factors elif mode == "Tracks": vars.vars["MIDIPITCH"] = i vars.vars["MIDIVELOCITY"] = 0.707 elif mode == "ChordsTracks": vars.vars["MIDIPITCH"] = [i + fac for fac in pitch_factors] vars.vars["MIDIVELOCITY"] = amp_factors if mode in ["Samples", "Chords"]: self.setModulesAndParams(modules, params, lfo_params, ctl_params, True) self.serverPanel.setPostProcSettings(postProcSettings) name = "%03d_%s.%s" % (i, filename, ext) path = vars.vars["toSysEncoding"](os.path.join(subrootpath, name)) count += 1 (keepGoing, skip) = dlg2.Update(count, "Exporting %s" % name) self.serverPanel.setRecordOptions(dur=duration, filename=path) self.serverPanel.start() self.deleteAllModules() self.serverPanel.shutdown() self.serverPanel.boot() else: for j in range(num_modules): self.setModulesAndParams(modules, params, lfo_params, ctl_params, True) self.serverPanel.setPostProcSettings(postProcSettings) self.modules[j].setMute(2) name = "%03d_%s_track_%02d_%s.%s" % (i, filename, j, self.modules[j].name, ext) path = vars.vars["toSysEncoding"](os.path.join(subrootpath, name)) count += 1 (keepGoing, skip) = dlg2.Update(count, "Exporting %s" % name) self.serverPanel.setRecordOptions(dur=duration, filename=path) self.serverPanel.start() self.deleteAllModules() self.serverPanel.shutdown() self.serverPanel.boot() dlg2.Destroy() self.serverPanel.reinitServer(0.05, vars.vars["AUDIO_HOST"], serverSettings, postProcSettings) vars.vars["MIDIPITCH"] = None vars.vars["MIDIVELOCITY"] = 0.707 self.serverPanel.setAmpCallable() self.setModulesAndParams(modules, params, lfo_params, ctl_params) dlg.Destroy() def getModulesAndParams(self): modules = [(module.name, module.mute) for module in self.modules] params = [[slider.GetValue() for slider in module.sliders] for module in self.modules] lfo_params = [module.getLFOParams() for module in self.modules] ctl_params = [[slider.midictl for slider in module.sliders] for module in self.modules] return modules, params, lfo_params, ctl_params def setModulesAndParams(self, modules, params, lfo_params, ctl_params, from_export=False): for name, mute in modules: dic = MODULES[name] self.modules.append(GenericPanel(self.panel, name, dic["title"], dic["synth"], dic["p1"], dic["p2"], dic["p3"])) self.addModule(self.modules[-1]) self.modules[-1].setMute(mute) for i, paramset in enumerate(params): for j, param in enumerate(paramset): slider = self.modules[i].sliders[j] slider.SetValue(param) slider.outFunction(param) for i, ctl_paramset in enumerate(ctl_params): for j, ctl_param in enumerate(ctl_paramset): slider = self.modules[i].sliders[j] slider.setMidiCtl(ctl_param, False) if j in [5,6,7,8] and ctl_param != None and not from_export: j4 = j - 5 if self.modules[i].synth._params[j4] != None: self.modules[i].synth._params[j4].assignMidiCtl(ctl_param, slider) for i, lfo_param in enumerate(lfo_params): self.modules[i].reinitLFOS(lfo_param) self.refresh() def savefile(self, filename): modules, params, lfo_params, ctl_params = self.getModulesAndParams() serverSettings = self.serverPanel.getServerSettings() postProcSettings = self.serverPanel.getPostProcSettings() dic = {"server": serverSettings, "postproc": postProcSettings, "modules": modules, "params": params, "lfo_params": lfo_params, "ctl_params": ctl_params} with open(filename, "w") as f: f.write(str(dic)) self.openedFile = filename self.SetTitle("Zyne Synth - " + os.path.split(filename)[1]) self.updateLastSavedInPreferencesFile(filename) def openfile(self, filename): with open(filename, "r") as f: text = f.read() dic = eval(text) self.deleteAllModules() self.serverPanel.shutdown() self.serverPanel.boot() self.serverPanel.setServerSettings(dic["server"]) if "postproc" in dic: self.serverPanel.setPostProcSettings(dic["postproc"]) self.setModulesAndParams(dic["modules"], dic["params"], dic["lfo_params"], dic["ctl_params"]) if filename.endswith("default.zy"): self.openedFile = "" else: self.openedFile = filename self.SetTitle("Zyne Synth - " + os.path.split(filename)[1]) def setModulePostions(self): w, h = self.GetSize() mw, mh = self.serverPanel.GetSize() cols = w / mw try: for i, mod in enumerate(self.modules): num = i + 1 if num/cols != 0: num += 1 self.sizer.SetItemPosition(mod, (num/cols, num%cols)) except: for i in range(len(self.modules)): mod = len(self.modules) - i - 1 num = len(self.modules) - i if num/cols != 0: num += 1 self.sizer.SetItemPosition(self.modules[mod], (num/cols, num%cols)) def onAddModule(self, evt): name = self.moduleNames[evt.GetId()-vars.constants["ID"]["Modules"]] dic = MODULES[name] self.modules.append(GenericPanel(self.panel, name, dic["title"], dic["synth"], dic["p1"], dic["p2"], dic["p3"])) self.addModule(self.modules[-1]) wx.CallAfter(self.SetFocus) def addModule(self, mod): w, h = self.GetSize() mw, mh = self.serverPanel.GetSize() cols = w / mw num = len(self.modules) self.refreshOutputSignal() if num/cols != 0: num += 1 self.sizer.Add(mod, (num/cols, num%cols)) self.refresh() def deleteModule(self, module): if self.selected == self.modules.index(module): self.selected = None for frame in module.lfo_frames: if frame != None: frame.Destroy() self.sizer.Remove(module) self.modules.remove(module) self.refreshOutputSignal() self.setModulePostions() self.refresh() def deleteAllModules(self): for module in self.modules: for frame in module.lfo_frames: if frame != None: frame.Destroy() self.sizer.Remove(module) module.Destroy() self.modules = [] self.refreshOutputSignal() self.serverPanel.resetVirtualKeyboard() self.refresh() def refreshOutputSignal(self): if len(self.modules) == 0: out = Sig(0.0) else: for i,mod in enumerate(self.modules): if i == 0: out = Sig(mod.synth.out) else: out = out + Sig(mod.synth.out) self.serverPanel.fsserver._modMix.value = out self.serverPanel.fsserver._outSig.value = self.serverPanel.fsserver._modMix def refresh(self): self.sizer.Layout() self.Refresh() def showAbout(self, evt): info = wx.AboutDialogInfo() description = "Zyne is a simple soft synthesizer allowing the " \ "user to create original sounds and export bank of samples.\n\n" \ "Zyne is written with Python and " \ "WxPython and uses pyo as its audio engine.\n\n" \ "A special thank to Jean-Michel Dumas for beta testing and a lots of ideas!" info.Name = 'Zyne' info.Version = '%s' % vars.constants["VERSION"] info.Description = description info.Copyright = u'(C) 2011 Olivier Bélanger' wx.AboutBox(info) class ZyneApp(wx.App): def OnInit(self): self.frame = ZyneFrame(None) self.frame.SetPosition((50,50)) return True def MacOpenFile(self, filename): self.frame.openfile(filename) if __name__ == '__main__': file = None if len(sys.argv) >= 2: file = sys.argv[1] app = ZyneApp(0) splash = ZyneSplashScreen(None, os.path.join(vars.constants["RESOURCES_PATH"], "ZyneSplash.png"), app.frame) if file: app.frame.openfile(file) app.MainLoop() zyne/scripts/0000755000175000017500000000000012417526437012535 5ustar tiagotiagozyne/scripts/Info.plist0000644000175000017500000000505112417524775014511 0ustar tiagotiago CFBundleDevelopmentRegion English CFBundleDisplayName Zyne CFBundleDocumentTypes CFBundleTypeExtensions zy CFBundleTypeIconFile zyneiconDoc.icns CFBundleTypeOSTypes TEXT CFBundleTypeRole Editor LSIsAppleDefaultForType CFBundleExecutable Zyne CFBundleIconFile zyneicon.icns CFBundleIdentifier org.pythonmac.unspecified.Zyne CFBundleInfoDictionaryVersion 6.0 CFBundleName Zyne CFBundlePackageType APPL CFBundleShortVersionString 0.1.2 CFBundleSignature ???? CFBundleVersion 0.1.2 LSHasLocalizedDisplayName NSAppleScriptEnabled NSHumanReadableCopyright Copyright not specified NSMainNibFile MainMenu NSPrincipalClass NSApplication PyMainFileNames __boot__ PyOptions alias argv_emulation no_chdir optimize 0 prefer_ppc site_packages use_pythonpath PyResourcePackages PyRuntimeLocations @executable_path/../Frameworks/Python.framework/Versions/2.6/Python PythonInfoDict PythonExecutable @executable_path/../Frameworks/Python.framework/Versions/2.6/Python PythonLongVersion 2.6.5 (r265:79359, Mar 24 2010, 01:32:55) [GCC 4.0.1 (Apple Inc. build 5493)] PythonShortVersion 2.6 py2app alias template app version 0.4.4 zyne/scripts/Build_routine_Win32.py0000644000175000017500000000121012417524775016672 0ustar tiagotiagoimport os build = True os.system("python ..\pyinstaller\Configure.py") os.system('python ..\pyinstaller\Makespec.py -F -c --icon=Resources\zyneicon.ico "Zyne.py"') if build: os.system('python ..\pyinstaller\Build.py "Zyne.spec"') os.system("svn export . Zyne_Win") os.system("copy dist\Zyne.exe Zyne_Win /Y") os.system("rmdir /Q /S Zyne_Win\scripts") os.system("rmdir /Q /S Zyne_Win\synths") os.remove("Zyne_Win/Zyne.py") os.remove("Zyne_Win/Resources/zyneicon.icns") os.remove("Zyne_Win/Resources/zyneiconDoc.icns") os.remove("Zyne.spec") os.system("rmdir /Q /S build") os.system("rmdir /Q /S dist") zyne/scripts/builder_OSX.sh0000644000175000017500000000213512417524775015254 0ustar tiagotiagorm -rf build dist py2applet --make-setup Zyne.py Resources/* python setup.py py2app --plist=scripts/Info.plist rm -f setup.py rm -rf build mv dist zyne_0.1.2 if cd zyne_0.1.2; then find . -name .svn -depth -exec rm -rf {} \; find . -name *.pyc -depth -exec rm -f {} \; find . -name .* -depth -exec rm -f {} \; else echo "Something wrong. zyne_0.1.2 not created" exit; fi echo "Remove Windows .ico files" rm Zyne.app/Contents/Resources/zyneicon.ico rm Zyne.app/Contents/Resources/zyneiconDoc.ico ditto --rsrc --arch i386 Zyne.app Zyne-i386.app rm -rf Zyne.app mv Zyne-i386.app Zyne.app cd .. cp -R zyne_0.1.2/Zyne.app . # Fixed wrong path in Info.plist cd Zyne.app/Contents awk '{gsub("Library/Frameworks/Python.framework/Versions/2.6/Resources/Python.app/Contents/MacOS/Python", "@executable_path/../Frameworks/Python.framework/Versions/2.6/Python")}1' Info.plist > Info.plist_tmp && mv Info.plist_tmp Info.plist cd ../.. tar -cjvf Zyne_0.1.2.tar.bz2 Zyne.app rm -rf zyne_0.1.2 rm -rf Zyne.app svn export . Zyne_0.1.2-src tar -cjvf Zyne_0.1.2-src.tar.bz2 Zyne_0.1.2-src rm -R Zyne_0.1.2-src zyne/scripts/win_installer.iss0000644000175000017500000000407212417524775016135 0ustar tiagotiago; Script generated by the Inno Setup Script Wizard. ; SEE THE DOCUMENTATION FOR DETAILS ON CREATING INNO SETUP SCRIPT FILES! [Setup] ; NOTE: The value of AppId uniquely identifies this application. ; Do not use the same AppId value in installers for other applications. ; (To generate a new GUID, click Tools | Generate GUID inside the IDE.) AppId={{743E4228-E23B-4758-BD5C-59EE059F4143} AppName=Zyne AppVersion=0.1.2 AppPublisher=iACT.umontreal.ca AppPublisherURL=http://code.google.com/p/zyne AppSupportURL=http://code.google.com/p/zyne AppUpdatesURL=http://code.google.com/p/zyne DefaultDirName={pf}\Zyne DisableDirPage=yes DefaultGroupName=Zyne AllowNoIcons=yes OutputBaseFilename=Zyne_0.1.2_setup Compression=lzma SolidCompression=yes ChangesAssociations=yes [Languages] Name: "english"; MessagesFile: "compiler:Default.isl" [Tasks] Name: "desktopicon"; Description: "{cm:CreateDesktopIcon}"; GroupDescription: "{cm:AdditionalIcons}"; Flags: unchecked [Files] Source: "C:\Documents and Settings\user\svn\zyne\Zyne_Win\Zyne.exe"; DestDir: "{app}"; Flags: ignoreversion Source: "C:\Documents and Settings\user\svn\zyne\Zyne_Win\Resources\*"; DestDir: "{app}\Resources"; Flags: ignoreversion recursesubdirs createallsubdirs [Registry] Root: HKCR; Subkey: ".zy"; ValueType: string; ValueName: ""; ValueData: "ZyneFile"; Flags: uninsdeletevalue Root: HKCR; Subkey: "ZyneFile"; ValueType: string; ValueName: ""; ValueData: "Zyne File"; Flags: uninsdeletevalue Root: HKCR; Subkey: "ZyneFile\DefaultIcon"; ValueType: string; ValueName: ""; ValueData: "{app}\Resources\zyneiconDoc.ico" Root: HKCR; Subkey: "ZyneFile\shell\open\command"; ValueType: string; ValueName: ""; ValueData: """{app}\Zyne.exe"" ""%1""" [Icons] Name: "{group}\Zyne"; Filename: "{app}\Zyne.exe"; WorkingDir: "{app}" Name: "{commondesktop}\Zyne"; Filename: "{app}\Zyne.exe"; Tasks: desktopicon [Run] Filename: "{app}\Zyne.exe"; Description: "{cm:LaunchProgram,Zyne}"; Flags: nowait postinstall skipifsilent [UninstallDelete] Type: files; Name: "{app}\Zyne Uninstall" zyne/scripts/SimpleSampler.py0000644000175000017500000000143512417524775015672 0ustar tiagotiago#!/usr/bin/env python # encoding: utf-8 """ Created by belangeo on 2010-11-25. """ from pyo import * import os, sys if len(sys.argv) < 2: print "SimpleSampler must be called with a sound folder path in argument!" sys.exit() else: path = sys.argv[1] pm_list_devices() s = Server(sr=44100, nchnls=2, buffersize=256, duplex=0) dev = input("Enter your midi device number : ") s.setMidiInputDevice(dev) s.boot() snds = sorted([f for f in os.listdir(path) if f[-4:].lower() in [".wav", ".aif"]]) print "loading soundfiles..." objs = [] for i, f in enumerate(snds): t = SndTable(os.path.join(path,f)) n = Notein(1,0,i+36,i+36) pl = TrigEnv(Thresh(n["velocity"]), t, t.getDur(), mul=Port(n["velocity"],.001,1)).out() objs.extend([t,n,pl]) print "Done." s.gui(locals())