205
205
# etc...
206
206
WAVE_KNOB = 6
207
207
208
+ ## Turing machine shift register
209
+ #
210
+ # Requires a sub-setting for either gate or CV mode
211
+ WAVE_TURING = 7
212
+
208
213
## Available wave shapes
214
+ #
215
+ # These must be placed in the desired order
209
216
WAVE_SHAPES = [
210
217
WAVE_SQUARE ,
211
218
WAVE_TRIANGLE ,
212
219
WAVE_SIN ,
213
220
WAVE_ADSR ,
221
+ WAVE_TURING ,
214
222
WAVE_RANDOM ,
215
223
WAVE_AIN ,
216
- WAVE_KNOB
224
+ WAVE_KNOB ,
217
225
]
218
226
219
- ## Ordered list of labels for the wave shape chooser menu
227
+ ## Labels for the wave shape chooser menu
220
228
WAVE_SHAPE_LABELS = {
221
229
WAVE_SQUARE : "Square" ,
222
230
WAVE_TRIANGLE : "Triangle" ,
223
231
WAVE_SIN : "Sine" ,
224
232
WAVE_ADSR : "ADSR" ,
233
+ WAVE_TURING : "Turing" ,
225
234
WAVE_RANDOM : "Random" ,
226
235
WAVE_AIN : "AIN (S&H)" ,
227
236
WAVE_KNOB : "KNOB (S&H)" ,
228
237
}
229
238
230
- ## Images of teh wave shapes
239
+ # Turing machine modes of operation
240
+ #
241
+ # We can either output the gate pulses OR we can
242
+ # output the semi-random CV
243
+ MODE_TURING_GATE = 0
244
+ MODE_TURING_CV = 1
245
+ TURING_MODES = [
246
+ MODE_TURING_GATE ,
247
+ MODE_TURING_CV ,
248
+ ]
249
+ TURING_MODE_LABELS = {
250
+ MODE_TURING_GATE : "Gate" ,
251
+ MODE_TURING_CV : "CV" ,
252
+ }
253
+
254
+ ## Images of the wave shapes
231
255
#
232
256
# These are 12x12 bitmaps. See:
233
257
# - https://github.com/Allen-Synthesis/EuroPi/blob/main/software/oled_tips.md
237
261
WAVE_TRIANGLE : bytearray (b'\x06 \x00 \x06 \x00 \t \x00 \t \x00 \x10 \x80 \x10 \x80 @ @@ @ \x80 \x10 \x80 \x10 ' ),
238
262
WAVE_SIN : bytearray (b'\x10 \x00 (\x00 D\x00 D\x00 \x82 \x00 \x82 \x00 \x82 \x10 \x82 \x10 \x01 \x10 \x01 \x10 \x00 \xa0 \x00 @' ),
239
263
WAVE_ADSR : bytearray (b' \x00 \x00 0\x00 0\x00 H\x00 H\x00 G\xc0 @@\x80 \x80 \x80 \x10 \x80 \x10 ' ),
264
+ WAVE_TURING : bytearray (b'\xff \xf0 \x04 \x00 \xf8 \x00 \x00 \x00 \xff \xf0 \x04 \x00 \xf8 \x00 \x00 \x00 \xff \xf0 \x04 \x00 \xf8 \x00 \x00 \x00 ' ),
240
265
WAVE_RANDOM : bytearray (b'\x00 \x00 \x08 \x00 \x08 \x00 \x14 \x00 \x16 \x80 \x16 \xa0 \x11 \xa0 Q\xf0 Pp`P@\x10 \x80 \x00 ' ),
241
266
WAVE_AIN : bytearray (b'\x00 \x00 |\x00 |\x00 d\x00 d\x00 g\x80 a\x80 \xe1 \xb0 \xe1 \xb0 \x01 \xf0 \x00 \x00 \x00 \x00 ' ),
242
267
WAVE_KNOB : bytearray (b'\x06 \x00 \x19 \x80 @@ @ \x80 \x10 \x82 \x10 A @\xa0 @\x19 \x80 \x06 \x00 ' ),
@@ -540,6 +565,9 @@ def __init__(self, cv_out, clock, n):
540
565
self .cv_out = cv_out
541
566
self .clock = clock
542
567
568
+ # 16-bit integer, initially random
569
+ self .turing_register = random .randint (0 , 65535 )
570
+
543
571
## What quantization are we using?
544
572
#
545
573
# See contrib.pams.QUANTIZERS
@@ -799,6 +827,42 @@ def __init__(self, cv_out, clock, n):
799
827
autoselect_cv = True ,
800
828
)
801
829
830
+ # Turing machine settings
831
+ self .t_length = SettingMenuItem (
832
+ config_point = IntegerConfigPoint (
833
+ f"cv{ n } _t_len" ,
834
+ 2 ,
835
+ 16 ,
836
+ 8
837
+ ),
838
+ prefix = f"CV{ n } " ,
839
+ title = "TLen" ,
840
+ autoselect_knob = True ,
841
+ autoselect_cv = True ,
842
+ )
843
+ self .t_lock = SettingMenuItem (
844
+ config_point = IntegerConfigPoint (
845
+ f"cv{ n } _t_lock" ,
846
+ - 100 ,
847
+ 100 ,
848
+ 0
849
+ ),
850
+ prefix = f"CV{ n } " ,
851
+ title = "TLock" ,
852
+ autoselect_knob = True ,
853
+ autoselect_cv = True ,
854
+ )
855
+ self .t_mode = SettingMenuItem (
856
+ config_point = ChoiceConfigPoint (
857
+ f"cv{ n } _t_mode" ,
858
+ TURING_MODES ,
859
+ MODE_TURING_GATE ,
860
+ ),
861
+ prefix = f"CV{ n } " ,
862
+ title = "TMode" ,
863
+ labels = TURING_MODE_LABELS ,
864
+ )
865
+
802
866
## All settings in an array so we can iterate through them in reset_settings(self)
803
867
self .all_settings = [
804
868
self .quantizer ,
@@ -811,6 +875,9 @@ def __init__(self, cv_out, clock, n):
811
875
self .e_step ,
812
876
self .e_trig ,
813
877
self .e_rot ,
878
+ self .t_length ,
879
+ self .t_lock ,
880
+ self .t_mode ,
814
881
self .skip ,
815
882
self .swing ,
816
883
self .mute ,
@@ -873,18 +940,17 @@ def update_menu_visibility(self, new_value=None, old_value=None, config_point=No
873
940
show_width = wave_shape != WAVE_AIN and wave_shape != WAVE_KNOB and wave_shape != WAVE_SIN
874
941
self .width .is_visible = show_width
875
942
943
+ # hide the turing machine settings if we're not in Turing mode
944
+ show_turing = wave_shape == WAVE_TURING
945
+ self .t_length .is_visible = show_turing
946
+ self .t_lock .is_visible = show_turing
947
+ self .t_mode .is_visible = show_turing
948
+
876
949
def change_e_length (self , new_value = None , old_value = None , config_point = None , arg = None ):
877
950
self .e_trig .modify_choices (list (range (self .e_step .value + 1 )), self .e_step .value )
878
951
self .e_rot .modify_choices (list (range (self .e_step .value + 1 )), self .e_step .value )
879
952
self .recalculate_e_pattern ()
880
953
881
- def request_clock_mod (self , new_value = None , old_value = None , config_point = None , arg = None ):
882
- self .clock_mod_dirty = True
883
-
884
- def change_clock_mod (self ):
885
- self .real_clock_mod = self .clock_mod .mapped_value
886
- self .clock_mod_dirty = False
887
-
888
954
def recalculate_e_pattern (self , new_value = None , old_value = None , config_point = None , arg = None ):
889
955
"""Recalulate the euclidean pattern this channel outputs
890
956
"""
@@ -895,6 +961,13 @@ def recalculate_e_pattern(self, new_value=None, old_value=None, config_point=Non
895
961
896
962
self .next_e_pattern = e_pattern
897
963
964
+ def request_clock_mod (self , new_value = None , old_value = None , config_point = None , arg = None ):
965
+ self .clock_mod_dirty = True
966
+
967
+ def change_clock_mod (self ):
968
+ self .real_clock_mod = self .clock_mod .mapped_value
969
+ self .clock_mod_dirty = False
970
+
898
971
def square_wave (self , tick , n_ticks ):
899
972
"""Calculate the [0, 1] value of a square wave with PWM
900
973
@@ -911,8 +984,10 @@ def square_wave(self, tick, n_ticks):
911
984
start_tick = self .phase .value * n_ticks / 100.0
912
985
end_tick = (start_tick + duty_cycle ) % n_ticks
913
986
914
- if (start_tick < end_tick and tick >= start_tick and tick < end_tick ) or \
915
- (start_tick > end_tick and (tick < end_tick or tick >= start_tick )):
987
+ if (
988
+ (start_tick < end_tick and tick >= start_tick and tick < end_tick ) or
989
+ (start_tick > end_tick and (tick < end_tick or tick >= start_tick ))
990
+ ):
916
991
return 1.0
917
992
else :
918
993
return 0.0
@@ -978,6 +1053,9 @@ def adsr_wave(self, tick, n_ticks):
978
1053
/ \
979
1054
-A--D---S---R-
980
1055
---n_ticks----
1056
+
1057
+ @param tick The current tick, in the range [0, n_ticks)
1058
+ @param n_ticks The number of ticks in which the wave must complete
981
1059
"""
982
1060
983
1061
# apply the phase offset
@@ -1011,6 +1089,48 @@ def adsr_wave(self, tick, n_ticks):
1011
1089
# outside of the ADSR
1012
1090
return 0.0
1013
1091
1092
+ def turing_shift (self ):
1093
+ """Shift the turing machine register by 1 bit
1094
+ """
1095
+ r = random .randint (0 , 99 )
1096
+ if r >= abs (self .t_lock .value ):
1097
+ incoming_bit = random .randint (0 , 1 )
1098
+ else :
1099
+ incoming_bit = (self .turing_register >> (self .t_length .value - 1 )) & 0x01
1100
+ self .turing_register = ((self .turing_register << 1 ) & 0xffff ) | incoming_bit
1101
+
1102
+ def turing_wave (self , tick , n_ticks ):
1103
+ """Calculate the [0, 1] output of a Turing Machine wave
1104
+
1105
+ @param tick The current tick, in the range [0, n_ticks)
1106
+ @param n_ticks The number of ticks in which the wave must complete
1107
+ """
1108
+ # respect phase shifting when updating the shift register
1109
+ start_tick = int (self .phase .value * n_ticks / 100.0 )
1110
+ if tick == start_tick :
1111
+ self .turing_shift ()
1112
+
1113
+ active_bit = self .turing_register & 0x0001
1114
+ if self .t_lock .value < 0 and self .wave_counter % (2 * self .t_length .value ) >= self .t_length .value :
1115
+ # turing machine outputs the [register, ~register] when "locked-left",
1116
+ # effectively doubling the length of the pattern
1117
+ active_bit = active_bit ^ 0x01
1118
+
1119
+ if self .t_mode .value == MODE_TURING_GATE :
1120
+ if active_bit :
1121
+ return self .square_wave (tick , n_ticks )
1122
+ else :
1123
+ return 0
1124
+ else :
1125
+ value = self .turing_register & 0xff # consider only the lowest 8 bits
1126
+
1127
+ if self .t_lock .value < 0 and self .wave_counter % (2 * self .t_length .value ) >= self .t_length .value :
1128
+ # if we're in the second half of a doubled pattern, invert the value
1129
+ value = (~ value ) & 0xff
1130
+
1131
+ return value / 256
1132
+ return 0
1133
+
1014
1134
def reset (self ):
1015
1135
"""Reset the current output to the beginning
1016
1136
"""
@@ -1097,6 +1217,8 @@ def tick(self):
1097
1217
wave_sample = wave_sample * self .sine_wave (wave_position , ticks_per_note ) * (self .amplitude .value / 100.0 )
1098
1218
elif self .wave_shape .value == WAVE_ADSR :
1099
1219
wave_sample = wave_sample * self .adsr_wave (wave_position , ticks_per_note ) * (self .amplitude .value / 100.0 )
1220
+ elif self .wave_shape .value == WAVE_TURING :
1221
+ wave_sample = self .turing_wave (wave_position , ticks_per_note ) * (self .amplitude .value / 100.0 )
1100
1222
else :
1101
1223
wave_sample = 0.0
1102
1224
@@ -1211,6 +1333,9 @@ def __init__(self):
1211
1333
ch .clock_mod .add_child (ch .e_step )
1212
1334
ch .clock_mod .add_child (ch .e_trig )
1213
1335
ch .clock_mod .add_child (ch .e_rot )
1336
+ ch .clock_mod .add_child (ch .t_length )
1337
+ ch .clock_mod .add_child (ch .t_lock )
1338
+ ch .clock_mod .add_child (ch .t_mode )
1214
1339
ch .clock_mod .add_child (ch .swing )
1215
1340
ch .clock_mod .add_child (ch .quantizer )
1216
1341
ch .clock_mod .add_child (ch .root )
0 commit comments