diff --git a/actions.py b/actions.py index 91978e4..0a68d3e 100644 --- a/actions.py +++ b/actions.py @@ -122,7 +122,6 @@ class Actions(object): self.data.function_on = not self.data.function_on def next_bank(self): - self.data.update_bank_number_by_amount(1) print('current bank is {} , the number of banks is {} '.format(self.data.bank_number, len(self.data.bank_data))) @@ -199,30 +198,21 @@ class Actions(object): elif self.data.player_mode == 'next': self.data.player_mode = 'now' - def set_the_camera_colour_u_with_cc(self, amount): - u_value = self._convert_midi_cc_value(amount, 0, 255) - self.capture.set_colour(u_value, None) + def set_the_camera_colour_u_continuous(self, amount): + self.capture.set_colour(amount*255, None) - def set_the_camera_colour_v_with_cc(self, amount): - v_value = self._convert_midi_cc_value(amount, 0, 255) - self.capture.set_colour(None, v_value) + def set_the_camera_colour_v_continuous(self, amount): + self.capture.set_colour(None, amount*255) - def set_the_camera_alpha_cc(self, amount): - alpha_amount = self._convert_midi_cc_value(amount, 0, 255) - self.capture.set_alpha(alpha_amount) + def set_the_camera_alpha_continuous(self, amount): + self.capture.set_alpha(amount*255) - def set_the_current_video_alpha_cc(self, amount): - alpha_amount = self._convert_midi_cc_value(amount, 0, 255) - self.video_driver.current_player.set_alpha_value(alpha_amount) + def set_the_current_video_alpha_continuous(self, amount): + self.video_driver.current_player.set_alpha_value(amount*255) - def set_the_next_video_alpha_cc(self, amount): - alpha_amount = self._convert_midi_cc_value(amount, 0, 255) - self.video_driver.next_player.set_alpha_value(alpha_amount) + def set_the_next_video_alpha_continuous(self, amount): + self.video_driver.next_player.set_alpha_value(amount*255) - @staticmethod - def _convert_midi_cc_value(cc_value, min_param, max_param): - output_range = max_param - min_param - return int(( cc_value / 127 ) * output_range + min_param) def get_midi_status(self): self.message_handler.set_message('INFO', 'midi status is {}'.format(self.data.midi_status)) @@ -233,6 +223,10 @@ class Actions(object): def update_capture_settings(self, setting_value): self.capture.update_capture_settings() + def change_piCapture_input(self, setting_value): + if self.data.settings['capture']['TYPE']['value'] == 'piCaptureSd1': + subprocess.call(['pivideo', '-s', setting_value]) + def change_output_mode(self, setting_value): if setting_value == 'hdmi': self.change_hdmi_settings(setting_value) diff --git a/data_centre/data.py b/data_centre/data.py index 6bf6007..6de827e 100644 --- a/data_centre/data.py +++ b/data_centre/data.py @@ -16,6 +16,7 @@ class Data(object): DEFAULT_SETTINGS_JSON = 'settings_default.json' KEYPAD_MAPPING_JSON = 'keypad_action_mapping.json' MIDI_MAPPING_JSON = 'midi_action_mapping.json' + ANALOG_MAPPING_JSON = 'analog_action_mapping.json' EMPTY_SLOT = dict(name='', location='', length=-1, start=-1, end=-1, rate=1) PATH_TO_DATA_OBJECTS = '/home/pi/r_e_c_u_r/json_objects/' PATH_TO_EXTERNAL_DEVICES = '/media/pi' @@ -51,6 +52,7 @@ class Data(object): self.key_mappings = self._read_json(self.KEYPAD_MAPPING_JSON) self.midi_mappings = self._read_json(self.MIDI_MAPPING_JSON) + self.analog_mappings = self._read_json(self.ANALOG_MAPPING_JSON) @staticmethod def create_empty_bank(): diff --git a/display_centre/menu.py b/display_centre/menu.py index 76f6ad7..578b57b 100644 --- a/display_centre/menu.py +++ b/display_centre/menu.py @@ -113,7 +113,7 @@ class BrowserMenu(Menu): for f in files: split_name = os.path.splitext(f) - if (split_name[1] in ['.mp4', '.mkv', '.avi', '.mov']): + if (split_name[1].lower() in ['.mp4', '.mkv', '.avi', '.mov']): self.menu_list.append(dict(name='{}{}'.format(indent, f), slot='-')) def _is_file_in_bank_data(self, file_name): diff --git a/documentation/exploring_feature_notes/cv_gate_and_serial_midi_input.md b/documentation/exploring_feature_notes/cv_gate_and_serial_midi_input.md index e5f8dfa..4c43b46 100644 --- a/documentation/exploring_feature_notes/cv_gate_and_serial_midi_input.md +++ b/documentation/exploring_feature_notes/cv_gate_and_serial_midi_input.md @@ -27,6 +27,41 @@ PIN NO.| SYMBOL | DESCRIPTION from this i should b able to work out which pins i can use for midi in and for analog-to-digital inputs (also piCapture needs some inputs too) +# gpio inputs for recur: + +here are the pins needed for different parts of the recur connections: + +note that pins 1 to 26 are covered by the lcd screen, even though not all are used by it + +## lcd screen + +as stated above, the screen uses the following pins: +- 1 , 17 : 3.3v +- 2, 4 : 5v +- 11 : TP_IQR (for touch panel) +- 18 : LCD_RS +- 19 : LCD_SI +- 21 : TP_SO (touch panel output) +- 22 : reset +- 23 : LCD_SCK +- 24 : LCD_CS +- 26 : TP_CS + +## piCapture + +piCapture be default will use pins 3, 5, 7 to comunicate + +## serial-midi in + +pin 10 (rx) is needed for midi in plus 3.3v (combined with octocoupler 6n138 etc and resistors) + +## analog in + +using a MCP3008 via hardware SPI, can connect up to 8 analog inputs using pins 35, 36, 38, 40 (SPI1) + 3.3v , can also connect with software SPI with any four pins if more inputs were needed. these inputs can be used for pots/sliders & gate/cv jacks. by default will react to 0-3.3v. if wanting to use larger range than this will need some kind of scaling electronics (tl074d?) + +providing 4 pins on under the lcd screen cover can be accessed by the board (and 3.3v can be distributed) i should be able to create a circuit that connects all these inputs to the pi. + + [instructable]: http://www.instructables.com/id/PiMiDi-A-Raspberry-Pi-Midi-Box-or-How-I-Learned-to/ [adafruit tft display]: https://www.adafruit.com/product/2441 [raspi gpio]: https://www.raspberrypi.org/documentation/usage/gpio/ diff --git a/documentation/faq.md b/documentation/faq.md index 332b31a..73643b7 100644 --- a/documentation/faq.md +++ b/documentation/faq.md @@ -59,7 +59,7 @@ HOWEVER, i have also noticed that this lag can happen even with longer clips som [correct cable]: https://www.adafruit.com/product/2881 [my cable]: https://www.aliexpress.com/item/4-poles-3-5mm-Mini-AV-Male-to-3RCA-Female-M-F-Audio-Video-Cable-Stereo/32769544207.html [.keymap]: /dotfiles/.keymap -[keypad_action_mapping.json]: /json_files/keypad_action_mapping.json +[keypad_action_mapping.json]: /json_objects/keypad_action_mapping.json [develop page]: /documentation/develop_docs.md [build]: /documentation/build_docs.md diff --git a/json_objects/analog_action_mapping.json b/json_objects/analog_action_mapping.json new file mode 100644 index 0000000..31664d7 --- /dev/null +++ b/json_objects/analog_action_mapping.json @@ -0,0 +1,5 @@ +{ + "7": { + "DEFAULT": ["set_the_current_video_alpha_continuous"] + } +} diff --git a/json_objects/midi_action_mapping.json b/json_objects/midi_action_mapping.json index ec8524b..2fd28c0 100644 --- a/json_objects/midi_action_mapping.json +++ b/json_objects/midi_action_mapping.json @@ -1,18 +1,18 @@ { "control_change 0": { - "DEFAULT": ["set_the_current_video_alpha_cc"] + "DEFAULT": ["set_the_current_video_alpha_continuous"] }, "control_change 1": { - "DEFAULT": ["set_the_next_video_alpha_cc"] + "DEFAULT": ["set_the_next_video_alpha_continuous"] }, "control_change 2": { - "DEFAULT": ["set_the_camera_alpha_cc"] + "DEFAULT": ["set_the_camera_alpha_continuous"] }, "control_change 3": { - "DEFAULT": ["set_the_camera_colour_u_with_cc"] + "DEFAULT": ["set_the_camera_colour_u_continuous"] }, "control_change 4": { - "DEFAULT": ["set_the_camera_colour_v_with_cc"] + "DEFAULT": ["set_the_camera_colour_v_continuous"] }, "note_on 72": { "NAV_BROWSER": ["move_browser_selection_up"], diff --git a/json_objects/settings_default.json b/json_objects/settings_default.json index 373b19a..7da9a59 100644 --- a/json_objects/settings_default.json +++ b/json_objects/settings_default.json @@ -1,5 +1,25 @@ { "capture": { + "TYPE": { + "action": "update_capture_settings", + "options": [ + "piCamera", + "piCaptureSd1" + ], + "value": "piCaptureSd1" + }, + "PICAPTURE_INPUT": { + "action": "change_piCapture_input", + "options": [ + "auto", + "video1", + "video2", + "video3", + "svideo", + "component" + ], + "value": "video1" + }, "DEVICE": { "action": "update_capture_settings", "options": [ @@ -87,6 +107,14 @@ } }, "other": { + "ANALOG_INPUT": { + "action": null, + "options": [ + "enabled", + "disabled" + ], + "value": "disabled" + }, "DEV_MODE_RESET": { "action": "switch_dev_mode", "options": [ @@ -227,7 +255,7 @@ "preferred", "CEA 4 HDMI" ], - "value": "preferred" + "value": "CEA 4 HDMI" }, "COMPOSITE_PROGRESSIVE": { "action": "change_composite_setting", diff --git a/r_e_c_u_r.py b/r_e_c_u_r.py index e414a28..f57e365 100755 --- a/r_e_c_u_r.py +++ b/r_e_c_u_r.py @@ -11,6 +11,7 @@ from display_centre.display import Display from display_centre.messages import MessageHandler from user_input.numpad_input import NumpadInput from user_input.midi_input import MidiInput +from user_input.analog_input import AnalogInput from video_centre.video_driver import VideoDriver from video_centre.capture import Capture import data_centre @@ -39,6 +40,7 @@ actions = Actions(tk, message_handler, data, video_driver, capture, display) numpad_input = NumpadInput(tk, message_handler, display, actions, data) midi_input = MidiInput(tk, message_handler, display, actions, data) +analog_input = AnalogInput(tk, message_handler, display, actions, data) actions.check_and_set_output_mode_on_boot() actions.check_dev_mode() diff --git a/user_input/analog_input.py b/user_input/analog_input.py new file mode 100644 index 0000000..7e2c991 --- /dev/null +++ b/user_input/analog_input.py @@ -0,0 +1,69 @@ +import Adafruit_GPIO.SPI as SPI +import Adafruit_MCP3008 + +class AnalogInput(object): + def __init__(self, root, message_handler, display, actions, data): + self.root = root + self.message_handler = message_handler + self.display = display + self.actions = actions + self.data = data + self.analog_mappings = data.analog_mappings + self.analog_delay = 50 + self.last_readings = [0,0,0,0,0,0,0,0] + + SPI_PORT = 1 + SPI_DEVICE = 2 + self.analog_input = Adafruit_MCP3008.MCP3008(spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE)) + self.check_if_listening_enabled() + + + def check_if_listening_enabled(self): + if self.data.settings['other']['ANALOG_INPUT']['value'] == 'enabled': + self.poll_analog_inputs() + else: + self.root.after(1000, self.check_if_listening_enabled) + + def poll_analog_inputs(self): + if self.data.settings['other']['ANALOG_INPUT']['value'] == 'enabled': + for i in range(0,8): + if str(i) in self.analog_mappings: + this_reading = self.analog_input.read_adc(i) + if this_reading - self.last_readings[i] > 3: + self.run_action_for_mapped_channel(i, this_reading) + self.last_readings[i] = this_reading + self.root.after(self.analog_delay, self.poll_analog_inputs) + else: + self.check_if_listening_enabled() + + def run_action_for_mapped_channel(self, channel, channel_value): + this_mapping = self.analog_mappings[str(channel)] + if self.data.control_mode in this_mapping: + mode = self.data.control_mode + elif 'DEFAULT' in this_mapping: + mode = 'DEFAULT' + + if self.data.function_on and len(this_mapping[mode]) > 1: + method_name = this_mapping[mode][1] + self.data.function_on = False + else: + method_name = this_mapping[mode][0] + + if channel_value is not None: + norm_channel_value = channel_value/1023 + else: + norm_channel_value = None + + print('the action being called is {}'.format(method_name)) + self.call_method_name(method_name, norm_channel_value) + ## not sure whether we want to update the screen in general; here - probably not most of the time ... + #if 'cc' not in message_name: + # self.display.refresh_display() + + def call_method_name(self, method_name, argument=None): + if argument is not None: + getattr(self.actions, method_name)(argument) + else: + getattr(self.actions, method_name)() + + diff --git a/user_input/midi_input.py b/user_input/midi_input.py index 86911c8..bc9df12 100644 --- a/user_input/midi_input.py +++ b/user_input/midi_input.py @@ -36,8 +36,8 @@ class MidiInput(object): for message in self.midi_device.iter_pending(): i = i + 1 message_dict = message.dict() - ## only listening to midi channel 1 for now , will make it seletcable later - if not message_dict['channel'] == 0: + midi_channel = midi_setting = self.data.settings['midi']['CHANNEL']['value'] - 1 + if not message_dict['channel'] == midi_channel: pass ## turning off noisey clock messages for now - may want to use them at some point elif message_dict['type'] == 'clock': @@ -91,9 +91,13 @@ class MidiInput(object): method_name = this_mapping[mode][0] print('the action being called is {}'.format(method_name)) - self.call_method_name(method_name, mapped_message_value) - ## only update screen if not cc - seeing if cc can respond faster if not refreshing screen on every action - if 'cc' not in message_name: + if mapped_message_value is not None: + norm_message_value = mapped_message_value/127 + else: + norm_message_value = None + self.call_method_name(method_name, norm_message_value) + ## only update screen if not continuous - seeing if cc can respond faster if not refreshing screen on every action + if 'continuous' not in message_name: self.display.refresh_display() def call_method_name(self, method_name, argument=None): diff --git a/video_centre/capture.py b/video_centre/capture.py index d4b5abc..ebe8ecc 100644 --- a/video_centre/capture.py +++ b/video_centre/capture.py @@ -26,7 +26,7 @@ class Capture(object): if self.use_capture: self.update_capture_settings() try: - self.device = picamera.PiCamera(resolution=self.resolution, framerate=self.framerate) + self.device = picamera.PiCamera(resolution=self.resolution, framerate=self.framerate, sensor_mode = self.sensor_mode) except picamera.exc.PiCameraError as e: self.use_capture = False print('camera exception is {}'.format(e)) @@ -37,6 +37,11 @@ class Capture(object): self.use_capture = self.data.settings['capture']['DEVICE']['value'] == 'enabled' self.resolution = self.convert_resolution_value(self.data.settings['capture']['RESOLUTION']['value']) self.framerate = self.convert_framerate_value(self.data.settings['capture']['FRAMERATE']['value']) + self.capture_type = self.data.settings['capture']['TYPE']['value'] + if self.capture_type == "piCaptureSd1": + self.sensor_mode = 6 + else: + self.sensor_mode = 0 ##update current instance (device) if in use if self.device and not self.device.closed: self.device.image_effect = self.data.settings['capture']['IMAGE_EFFECT']['value'] @@ -56,9 +61,20 @@ class Capture(object): self.is_previewing = True self.device.start_preview() self.set_preview_screen_size() + self.set_capture_settings() self.device.preview.layer = self.PREVIEW_LAYER return True + def set_capture_settings(self): + if self.capture_type == "piCaptureSd1": + self.device.sensor_mode = 6 + self.device.awb_mode = "off" + self.device.awb_gains = 1.0 + self.device.exposure_mode = "off" + else: + self.sensor_mode = 0 + + def set_preview_screen_size(self): if self.data.settings['other']['DEV_MODE_RESET']['value'] == 'on': self.device.preview.fullscreen = False @@ -117,8 +133,12 @@ class Capture(object): def convert_raw_recording(self): recording_path , recording_name = self.generate_recording_path() try: - mp4box_process = subprocess.Popen(['MP4Box', '-add', self.video_dir + '/raw.h264', recording_path]) - return mp4box_process , recording_name + if self.sensor_mode == 6: + mp4box_process = subprocess.Popen(['MP4Box', '-fps', '60', '-add', self.video_dir + '/raw.h264', recording_path]) + return mp4box_process , recording_name + else: + mp4box_process = subprocess.Popen(['MP4Box', '-add', self.video_dir + '/raw.h264', recording_path]) + return mp4box_process , recording_name except Exception as e: print(e) if hasattr(e, 'message'): diff --git a/video_centre/video_player.py b/video_centre/video_player.py index df63850..bab1a0c 100644 --- a/video_centre/video_player.py +++ b/video_centre/video_player.py @@ -141,8 +141,9 @@ class VideoPlayer: self.set_alpha_value(255) def set_alpha_value(self, amount): - self.omx_player.set_alpha(amount) - self.alpha = amount + if self.omx_player: + self.omx_player.set_alpha(amount) + self.alpha = amount def seek(self, amount): position = self.get_position()