Skip to content

Commit f570b24

Browse files
committed
Fixes in simulator display and setup script
1 parent 75ce34d commit f570b24

File tree

4 files changed

+44
-51
lines changed

4 files changed

+44
-51
lines changed

README.md

Lines changed: 4 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -223,7 +223,7 @@ With `push2_python.constants.FRAME_FORMAT_RGB565` we need to convert the frame t
223223
push = push2_python.Push2(run_simulator=True)
224224
```
225225

226-
And then, while your app is running, point your browser at `localhost:5000`. Here is a screenshot of the simulator in action:
226+
And then, while your app is running, point your browser at `localhost:6128`. Here is a screenshot of the simulator in action:
227227

228228
<p align="center">
229229
<img src="simulator.png" title="push2-python simulator" />
@@ -363,15 +363,8 @@ for i in range(0, 20):
363363

364364
# Now crate an extra frame which loads an image from a file. Image must be 960x160 pixels.
365365
img = Image.open('test_img_960x160.png')
366-
img_array = numpy.array(img)
367-
frame = img_array/255 # Convert rgb values to [0.0, 1.0] floats
368-
369-
# Because the pixel format returned by Image.open is not the one required for Push2's display,
370-
# this frame needs to be prepared before sending it to Push. This conversion takes a bit of
371-
# time so we do it offline. Some formats can be converted on the fly by `push2-python` but not
372-
# the RGB format retruned by PIL.
373-
prepared_img_frame = \
374-
push.display.prepare_frame(frame, input_format=push2_python.constants.FRAME_FORMAT_RGB)
366+
frame = numpy.array(img)
367+
#frame = img_array/255 # Convert rgb values to [0.0, 1.0] floats
375368

376369
# Now lets configure some action handlers which will display frames in Push2's display in
377370
# reaction to pad and button presses
@@ -384,7 +377,7 @@ def on_pad_pressed(push, pad_n, pad_ij, velocity):
384377
@push2_python.on_button_pressed()
385378
def on_button_pressed(push, button_name):
386379
# Display the frame with the loaded image
387-
push.display.display_prepared_frame(prepared_img_frame)
380+
push.display.display_frame(frame, input_format=push2_python.constants.FRAME_FORMAT_RGB)
388381

389382
# Start infinite loop so the app keeps running
390383
print('App runnnig...')

push2_python/display.py

Lines changed: 14 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -28,22 +28,18 @@ def rgb565_to_bgr565(rgb565_frame):
2828

2929
# Non-vectorized function for converting from rgb to bgr565
3030
def rgb_to_bgr565(rgb_frame):
31-
# RGB is defined here as an 2d array with (r, g, b) tuples with values between [0.0, 1.0]
32-
# TODO: this is really slow, there are other ways to do this simple (similar to rgb565_to_bgr565)
33-
# which will work as well super fast. Need to implement that
34-
out_array = numpy.zeros(shape=(rgb_frame.shape[1], rgb_frame.shape[0]), dtype=numpy.uint16)
35-
for i in range(0, rgb_frame.shape[0]):
36-
for j in range(0, rgb_frame.shape[1]):
37-
r = rgb_frame[i][j][0]
38-
g = rgb_frame[i][j][1]
39-
b = rgb_frame[i][j][2]
40-
r = int(round(r * (pow(2, 5) - 1)))
41-
g = int(round(g * (pow(2, 6) - 1)))
42-
b = int(round(b * (pow(2, 5) - 1)))
43-
out_array[j][i] = int(
44-
'{b:05b}{g:06b}{r:05b}'.format(r=r, g=g, b=b), 2)
45-
return out_array
46-
31+
rgb_frame *= 255
32+
rgb_frame_r = rgb_frame[:, :, 0].astype(numpy.uint16)
33+
rgb_frame_g = rgb_frame[:, :, 1].astype(numpy.uint16)
34+
rgb_frame_b = rgb_frame[:, :, 2].astype(numpy.uint16)
35+
frame_r_filtered = numpy.bitwise_and(rgb_frame_r, int('0000000011111000', 2))
36+
frame_r_shifted = numpy.right_shift(frame_r_filtered, 3)
37+
frame_g_filtered = numpy.bitwise_and(rgb_frame_g, int('0000000011111100', 2))
38+
frame_g_shifted = numpy.left_shift(frame_g_filtered, 3)
39+
frame_b_filtered = numpy.bitwise_and(rgb_frame_b, int('0000000011111000', 2))
40+
frame_b_shifted = numpy.left_shift(frame_b_filtered, 8)
41+
combined = frame_r_shifted + frame_g_shifted + frame_b_shifted # Combine all channels
42+
return combined.transpose()
4743

4844
class Push2Display(AbstractPush2Section):
4945
"""Class to interface with Ableton's Push2 display.
@@ -196,17 +192,11 @@ def send_to_display(self, prepared_frame):
196192

197193

198194
def display_frame(self, frame, input_format=FRAME_FORMAT_BGR565):
199-
prepared_frame = self.prepare_frame(frame, input_format=input_format)
195+
prepared_frame = self.prepare_frame(frame.copy(), input_format=input_format)
200196
self.send_to_display(prepared_frame)
201197

202198
if self.push.simulator_controller is not None:
203-
self.push.simulator_controller.prepare_next_frame_for_display(frame, input_format=input_format)
204-
205-
206-
def display_prepared_frame(self, prepared_frame):
207-
# NOTE that using this method frames won't be shown in the simulator
208-
self.send_to_display(prepared_frame)
209-
199+
self.push.simulator_controller.prepare_next_frame_for_display(frame.copy(), input_format=input_format)
210200

211201
def display_last_frame(self):
212202
self.send_to_display(self.last_prepared_frame)

push2_python/simulator/simulator.py

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@
77
from PIL import Image
88
from io import BytesIO
99
import time
10-
import random
1110
import numpy
1211

1312
app = Flask(__name__)
@@ -67,40 +66,48 @@ def set_element_color(self, midiTrigger, color_idx, animation_idx):
6766

6867
def prepare_next_frame_for_display(self, frame, input_format=push2_python.constants.FRAME_FORMAT_BGR565):
6968
global latest_prepared_base64_image_to_send, latest_prepared_image_sent
70-
69+
7170
# 'frame' expects a "frame" as in display.display_frame method
7271
if time.time() - self.last_time_frame_prepared > 1.0/5.0: # Limit fps to save recources
7372
self.last_time_frame_prepared = time.time()
74-
73+
7574
# We need to convert the frame to RGBA format first (so Pillow can read it later)
75+
if input_format == push2_python.constants.FRAME_FORMAT_RGB:
76+
frame = push2_python.display.rgb_to_bgr565(frame)
77+
7678
frame = frame.transpose().flatten()
7779
rgb_frame = numpy.zeros(shape=(len(frame), 1), dtype=numpy.uint32).flatten()
7880
rgb_frame[:] = frame[:]
81+
7982
if input_format == push2_python.constants.FRAME_FORMAT_RGB565:
8083
r_filter = int('1111100000000000', 2)
8184
g_filter = int('0000011111100000', 2)
8285
b_filter = int('0000000000011111', 2)
8386
frame_r_filtered = numpy.bitwise_and(rgb_frame, r_filter)
84-
frame_r_shifted = numpy.left_shift(frame_r_filtered, 16) # Shift 16 to left so R starts at bit 32
87+
frame_r_shifted = numpy.right_shift(frame_r_filtered, 8) # Shift 8 to right so R sits in the the 0-7 right-most bits
8588
frame_g_filtered = numpy.bitwise_and(rgb_frame, g_filter)
86-
frame_g_shifted = numpy.left_shift(frame_g_filtered, 13) # Shift 13 to the left so G start at bit 24
89+
frame_g_shifted = numpy.left_shift(frame_g_filtered, 5) # Shift 5 to the left so G sits at the 8-15 bits
8790
frame_b_filtered = numpy.bitwise_and(rgb_frame, b_filter)
88-
frame_b_shifted = numpy.left_shift(frame_b_filtered, 11) # Shift 11 so B starts at bit 16
91+
frame_b_shifted = numpy.left_shift(frame_b_filtered, 19) # Shift 19 to the left so G sits at the 16-23 bits
8992
rgb_frame = frame_r_shifted + frame_g_shifted + frame_b_shifted # Combine all channels
90-
rgb_frame = numpy.bitwise_or(rgb_frame, int('00000000000000000000000011111111', 2)) # Set alpha channel to "full!"
91-
elif input_format == push2_python.constants.FRAME_FORMAT_BGR565:
93+
rgb_frame = numpy.bitwise_or(rgb_frame, int('11111111000000000000000000000000', 2)) # Set alpha channel to "full!" (bits 24-32)
94+
95+
elif input_format == push2_python.constants.FRAME_FORMAT_BGR565 or input_format == push2_python.constants.FRAME_FORMAT_RGB:
9296
r_filter = int('0000000000011111', 2)
9397
g_filter = int('0000011111100000', 2)
94-
b_filter = int('1111100000000000', 2)
95-
frame_r_filtered = numpy.bitwise_and(rgb_frame, r_filter)
96-
frame_r_shifted = numpy.left_shift(frame_r_filtered, 27) # Shift 27 to left so R starts at bit 32
98+
b_filter = int('1111100000000000', 2)
99+
frame_r_filtered = numpy.bitwise_and(rgb_frame, r_filter)
100+
frame_r_shifted = numpy.left_shift(frame_r_filtered, 3) # Shift 3 to left so R sits in the the 0-7 right-most bits
97101
frame_g_filtered = numpy.bitwise_and(rgb_frame, g_filter)
98-
frame_g_shifted = numpy.left_shift(frame_g_filtered, 13) # Shift 13 to the left so G start at bit 24
102+
frame_g_shifted = numpy.left_shift(frame_g_filtered, 5) # Shift 5 to the left so G sits at the 8-15 bits
99103
frame_b_filtered = numpy.bitwise_and(rgb_frame, b_filter)
100-
frame_b_shifted = frame_b_filtered # No shift, B already starts at 16
104+
frame_b_shifted = numpy.left_shift(frame_b_filtered, 8) # Shift 8 to the left so G sits at the 16-23 bits
101105
rgb_frame = frame_r_shifted + frame_g_shifted + frame_b_shifted # Combine all channels
102-
rgb_frame = numpy.bitwise_or(rgb_frame, int('00000000000000000000000011111111', 2)) # Set alpha channel to "full!"
106+
rgb_frame = numpy.bitwise_or(rgb_frame, int('11111111000000000000000000000000', 2)) # Set alpha channel to "full!" (bits 24-32)
107+
108+
'''
103109
elif input_format == push2_python.constants.FRAME_FORMAT_RGB:
110+
104111
r_filter = int('111111110000000000000000', 2)
105112
g_filter = int('000000001111111100000000', 2)
106113
b_filter = int('000000000000000011111111', 2)
@@ -112,6 +119,7 @@ def prepare_next_frame_for_display(self, frame, input_format=push2_python.consta
112119
frame_b_shifted = frame_b_filtered # No shift, B already starts at 16
113120
rgb_frame = frame_r_shifted + frame_g_shifted + frame_b_shifted # Combine all channels
114121
rgb_frame = numpy.bitwise_or(rgb_frame, int('00000000000000000000000011111111', 2)) # Set alpha channel to "full!"
122+
'''
115123

116124
img = Image.frombytes('RGBA', (960, 160), rgb_frame.tobytes())
117125
buffered = BytesIO()
@@ -195,7 +203,7 @@ def index():
195203

196204

197205
def run_simulator_in_thread():
198-
sim_app.run(app)
206+
sim_app.run(app, port=6128)
199207

200208

201209
def start_simulator(_push_object):

setup.py

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,15 @@
11
from setuptools import setup, find_packages
22

33
setup(name='push2-python',
4-
version='0.5',
4+
version='0.6',
55
description='Utils to interface with Ableton\'s Push 2 from Python',
66
url='https://github.com/ffont/push2-python',
77
author='Frederic Font',
88
author_email='frederic.font@gmail.com',
99
license='MIT',
1010
install_requires=['numpy', 'pyusb', 'python-rtmidi', 'mido', 'flask', 'flask-socketio', 'eventlet' , 'pillow'],
1111
python_requires='>=3',
12+
setup_requires=['setuptools_scm'],
13+
include_package_data=True,
1214
packages=find_packages()
1315
)

0 commit comments

Comments
 (0)