2
.gitignore
vendored
@@ -2,5 +2,5 @@
|
||||
*.log
|
||||
.vscode
|
||||
json_objects/display_data.json
|
||||
json_objects/next_bankslot_number.json
|
||||
json_objects/shader_bank_data.json
|
||||
json_objects/settings.json
|
||||
|
||||
41
README.md
@@ -1,9 +1,11 @@
|
||||
|
||||
# r_e_c_u_r
|
||||
an open diy py/pi based video sampler
|
||||
|
||||
an open diy video sampler
|
||||
|
||||
![vectorfront][vectorfront]
|
||||
|
||||
__r_e_c_u_r__ is an embedded python application on _raspberry pi3_ that uses `input` from the _keypad_ to control omxplayer's `video out` while displaying a simple text user interface on the _rpi lcd screen_
|
||||
__r_e_c_u_r__ is an embedded python/openframeworks application on _raspberry pi3_ that uses `input` from the _keypad_ to control `video` while displaying a simple text ui on a _rpi lcd screen_
|
||||
|
||||
## in-depth video demo
|
||||
|
||||
@@ -16,13 +18,25 @@ __r_e_c_u_r__ is an embedded python application on _raspberry pi3_ that uses `in
|
||||
- load and trigger video samples from numbered slots in the _sampler_ bank
|
||||
- dynamically set and clear the start/end points of each sample as it plays
|
||||
- control and sequence all inputs and more with midi-usb
|
||||
- c_a_p_t_u_r : optional extension for live sampling through the pi camera input
|
||||
- many sampler modes for varied playback including: repeat, one-shot, gated, random, fixed-length, random-start and more
|
||||
- exhaustive and extendable _settings_ menu to suit your use
|
||||
|
||||
### other feature ideas
|
||||
## extensions
|
||||
|
||||
i started a [board] of some features i would like to explore
|
||||
as of _V2.0_ the __r_e_c_u_r__ platform has been extended in a number of directions to complement live performance and _\_recur\__ , the original video playing engine:
|
||||
|
||||
- _\_captur\__ : input live video via CSI or USB , for live sampling and processing
|
||||
- _\_conjur\__ : glsl shader engine, map and trigger shader files and control parameters in real time; generate video or process and mix existing sources
|
||||
- _\_detour\__ : in-memory frame sampling (up to 500 frames), video scrubbing, playback speed and direction control, finger drumming
|
||||
- toggling internal feedback + strobe effect
|
||||
|
||||
_NOTE: most of the new V2.0 features are optimised for sd composite video output. Some of these extensions will work over HDMI but they are not fully supported - especially the live video input struggles_
|
||||
|
||||
### extension video walkthroughs
|
||||
|
||||
- [![conjur_walkthrough][conjur_thumbnail]](https://www.youtube.com/watch?v=ah2HY1fuv8w)
|
||||
- [![captur_walkthrough][captur_thumbnail]](https://www.youtube.com/watch?v=e7m_YHEFahs)
|
||||
- [![detour_walkthrough][detour_thumbnail]](https://www.youtube.com/watch?v=e9vrzn7c9R8)
|
||||
|
||||
## main objectives:
|
||||
|
||||
@@ -36,6 +50,8 @@ i started a [board] of some features i would like to explore
|
||||
- [building] - how to diy r_e_c_u_r
|
||||
- [developing] - how to contribute to r_e_c_u_r
|
||||
|
||||
many other things documented on the [wiki]
|
||||
|
||||
## status
|
||||
|
||||
The nature of this project is to be open-ended and community driven. my r_e_c_u_r already solves the problems i initially built it for. what happens next depends on how it is used and received by you. if you like the idea please let me know / get involved !
|
||||
@@ -48,9 +64,12 @@ also facebook user group : https://www.facebook.com/groups/114465402691215/
|
||||
|
||||
all feedback is appreciated. if you want to donate to this project you can do so with the above email via paypal : everything i receive will go into improving __r_e_c_u_r__. cheers to Leo Browning for the 3d modelling and vector art and to Ben Caldwell for heaps of help with the code!
|
||||
|
||||
[vectorfront]: ./documentation/vectorfront_keys.png
|
||||
[video-thumbnail]: ./documentation/video-thumbnail.jpg
|
||||
[board]: https://trello.com/b/mmJJFyrp/feature-ideas
|
||||
[operating]: documentation/operate_docs.md
|
||||
[building]: documentation/build_docs.md
|
||||
[developing]: documentation/develop_docs.md
|
||||
[vectorfront]: ./enclosure/vectorfront_keys.png
|
||||
[video-thumbnail]: https://github.com/langolierz/r_e_c_u_r/wiki/images/video-thumbnail.jpg
|
||||
[conjur_thumbnail]: https://github.com/langolierz/r_e_c_u_r/wiki/images/conjur_video_thumbnail.jpg
|
||||
[captur_thumbnail]: https://github.com/langolierz/r_e_c_u_r/wiki/images/captur_video_thumbnail.jpg
|
||||
[detour_thumbnail]: https://github.com/langolierz/r_e_c_u_r/wiki/images/detour_video_thumbnail.jpg
|
||||
[operating]: https://github.com/langolierz/r_e_c_u_r/wiki/operate_docs
|
||||
[building]: https://github.com/langolierz/r_e_c_u_r/wiki/build_docs
|
||||
[developing]: https://github.com/langolierz/r_e_c_u_r/wiki/develop_docs
|
||||
[wiki]: https://github.com/langolierz/r_e_c_u_r/wiki
|
||||
|
||||
35
Shaders/0-input/colour_sine.frag
Normal file
@@ -0,0 +1,35 @@
|
||||
//0-input
|
||||
//written by Tim Caldwell
|
||||
// a simple example of how to use continuous parameters to perform switching functions
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
uniform float ftime;
|
||||
uniform vec4 fparams
|
||||
uniform float u_x0;
|
||||
uniform float u_x1;
|
||||
|
||||
void main() {
|
||||
vec3 colour = vec3(0.0,0.0,0.0);
|
||||
if(fparams[0] < (1.0 / 3.0)){
|
||||
colour[0] = 1.0;
|
||||
}
|
||||
else if(fparams[0] > (2.0 / 3.0)){
|
||||
colour[1] = 1.0;
|
||||
}
|
||||
else{
|
||||
colour[2] = 1.0;
|
||||
}
|
||||
if(fparams[1] < (1.0 / 3.0)){
|
||||
colour[1] = 1.0;
|
||||
}
|
||||
else if(fparams[1] > (2.0 / 3.0)){
|
||||
colour[2] = 1.0;
|
||||
}
|
||||
else{
|
||||
colour[0] = 1.0;
|
||||
}
|
||||
gl_FragColor = vec4(colour, 1.0);
|
||||
|
||||
}
|
||||
27
Shaders/0-input/flowing_colours.frag
Normal file
@@ -0,0 +1,27 @@
|
||||
// copied from http://glslsandbox.com/e#47821.0
|
||||
//0-input
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
#extension GL_OES_standard_derivatives : enable
|
||||
|
||||
uniform float u_time;
|
||||
uniform float u_x0;
|
||||
uniform float u_x1;
|
||||
uniform float u_x2;
|
||||
uniform float u_x3;
|
||||
uniform vec2 u_resolution;
|
||||
|
||||
void main( void ) {
|
||||
|
||||
vec2 position = ( gl_FragCoord.xy / u_resolution.xy ) + (u_resolution.x, u_resolution.y * (u_x0*0.01)) / 4.0;
|
||||
|
||||
float color = 0.0;
|
||||
color += sin( position.x * cos( u_time / 15.0 ) * 80.0 ) + cos( position.y * cos( u_time / 15.0 ) * 10.0 ) * u_x1;
|
||||
color += sin( position.y * sin( u_time / 10.0 ) * 40.0 ) + cos( position.x * sin( u_time / 25.0 ) * 40.0 ) * u_x2;
|
||||
color += sin( position.x * sin( u_time / 5.0 ) * 10.0 ) + sin( position.y * sin( u_time / 35.0 ) * 80.0 ) * u_x3;
|
||||
color *= sin( u_time / 10.0 ) * 0.8;
|
||||
|
||||
gl_FragColor = vec4( vec3( color, color * 0.5 * sin(0.02*u_time) , sin( color + u_time / 3.0 ) * 0.75 ), 1.0 );
|
||||
}
|
||||
37
Shaders/0-input/hypnotic_rings.frag
Normal file
@@ -0,0 +1,37 @@
|
||||
//0-input
|
||||
//written by Ben Caldwell
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform float u_x0;
|
||||
uniform float u_x1;
|
||||
uniform float u_x2;
|
||||
uniform float u_x3;
|
||||
|
||||
void main() {
|
||||
vec3 color = vec3(0.0,0.0,0.0);
|
||||
float mod_time = u_x0 * u_time;
|
||||
|
||||
vec2 pos = gl_FragCoord.xy;
|
||||
vec2 st = gl_FragCoord.xy/u_resolution.xy;
|
||||
st.x *= u_resolution.x/u_resolution.y;
|
||||
|
||||
vec2 s1 = vec2(u_resolution.x*(0.5 + 0.25*cos(mod_time)), u_resolution*(0.5 + 0.25*sin(mod_time)));
|
||||
vec2 s2 = vec2(u_resolution.x*(0.5 + 0.25*sin(mod_time)), u_resolution*(0.5 + 0.25*cos(mod_time)));
|
||||
vec2 s3 = vec2(u_resolution.x*0.5, u_resolution*0.5);
|
||||
|
||||
float r1 = (pow(pow(pos.x-s1.x,2.0) + pow(pos.y-s1.y,2.0), 0.5))*u_x1;
|
||||
float r2 = (pow(pow(pos.x-s2.x,2.0) + pow(pos.y-s2.y,2.0), 0.5))*u_x2;
|
||||
float r3 = (pow(pow(pos.x-s3.x,2.0) + pow(pos.y-s3.y,2.0), 0.5))*u_x3;
|
||||
|
||||
color += vec3(0.5 + 0.5*sign(sin((0.1 + 0.05*sin(mod_time+2.0))*r1 - 1.0*mod_time)), 0.0, 0.0);
|
||||
color += vec3(0.0, 0.0, 0.5 + 0.5*sign(sin((0.1 + 0.05*sin(mod_time))*r2 - 1.2*mod_time)));
|
||||
color += vec3(0.0, 0.5 + 0.5*sign(sin((0.1 + 0.05*sin(mod_time+0.5))*r3 - 1.1*mod_time)), 0.0);
|
||||
|
||||
|
||||
|
||||
gl_FragColor = vec4(color,1.);
|
||||
}
|
||||
26
Shaders/0-input/squarewaves.frag
Normal file
@@ -0,0 +1,26 @@
|
||||
//0-input
|
||||
//written by Ben Caldwell
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform float u_x0;
|
||||
uniform float u_x1;
|
||||
uniform float u_x2;
|
||||
uniform float u_x3;
|
||||
|
||||
void main() {
|
||||
|
||||
vec3 color = vec3(0.0,0.0,0.0);
|
||||
vec2 pos = gl_FragCoord.xy;
|
||||
vec2 st = gl_FragCoord.xy/u_resolution.xy;
|
||||
st.x *= u_resolution.x/u_resolution.y;
|
||||
|
||||
color += vec3(u_x0*sign(sin(0.05*pos.x - u_time + sin(0.05*pow(pos.y,(0.7 + u_x1)) - u_time))),0.,0.);
|
||||
color += vec3(0., 0., sign(cos(0.05*pos.x*u_x2 - 2.0*u_time + 5.0*u_x3*sin(0.05*pos.y - 2.0*u_time))));
|
||||
|
||||
gl_FragColor = vec4(color,1.);
|
||||
|
||||
}
|
||||
26
Shaders/0-input/zoom_clouds.frag
Normal file
@@ -0,0 +1,26 @@
|
||||
//0-input
|
||||
//written by Ben Caldwell
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform float u_x0;
|
||||
uniform float u_x1;
|
||||
|
||||
void main() {
|
||||
float mod_time = u_time*u_x0;
|
||||
vec2 pos = gl_FragCoord.xy;
|
||||
vec2 st = gl_FragCoord.xy/u_resolution.xy;
|
||||
st.x *= u_resolution.x/u_resolution.y;
|
||||
|
||||
vec3 color = vec3(0.0,0.0,0.0);
|
||||
|
||||
color += vec3(sin(0.7*u_x1*pos.x - cos(u_x0*mod_time)) + cos(0.05*pos.x/(0.1 + abs(cos(mod_time/5.0)))),0.0,0.0);
|
||||
color += vec3(sin(0.05*pos.y - cos(mod_time/5.0)) + cos(0.5*pos.y/(0.1 + abs(cos(mod_time/5.0)))),0.0,0.0);
|
||||
color += vec3(0.0, 0.0, sin(mod_time));
|
||||
|
||||
gl_FragColor = vec4(color,1.);
|
||||
|
||||
}
|
||||
38
Shaders/1-input/gray_divisions.frag
Normal file
@@ -0,0 +1,38 @@
|
||||
//1-input
|
||||
//written by Tim Caldwell
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D u_tex0;
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform float u_x0;
|
||||
|
||||
vec3 rgb2grayscale(vec3 c)
|
||||
{
|
||||
return(vec3(dot(c, vec3(0.299, 0.587, 0.114))));
|
||||
}
|
||||
|
||||
void main(){
|
||||
|
||||
vec2 pos = v_texcoord;
|
||||
vec4 color = texture2D(u_tex0, pos);
|
||||
|
||||
vec3 gray = rgb2grayscale(color.rgb);
|
||||
|
||||
float divisions = 20.0 * u_x0 + 1.0;
|
||||
vec3 newGray = vec3(float(int(gray.x*divisions))/divisions,float(int(gray.y*divisions))/divisions,float(int(gray.z*divisions))/divisions);
|
||||
|
||||
//if(gray.r + gray.g + gray.b )
|
||||
|
||||
|
||||
gl_FragColor = vec4(newGray, color.a);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
59
Shaders/1-input/hsv_control.frag
Normal file
@@ -0,0 +1,59 @@
|
||||
//1-input
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D u_tex0;
|
||||
uniform sampler2D u_tex1;
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform float u_x0;
|
||||
uniform float u_x1;
|
||||
uniform float u_x2;
|
||||
uniform float u_x3;
|
||||
|
||||
vec3 rgb2grayscale(vec3 c)
|
||||
{
|
||||
return(vec3(dot(c, vec3(0.299, 0.587, 0.114))));
|
||||
}
|
||||
|
||||
vec3 rgb2hsv(vec3 c)
|
||||
{
|
||||
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
|
||||
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
|
||||
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
|
||||
|
||||
float d = q.x - min(q.w, q.y);
|
||||
float e = 1.0e-10;
|
||||
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
|
||||
}
|
||||
|
||||
vec3 hsv2rgb(vec3 c)
|
||||
{
|
||||
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
||||
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
||||
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
||||
}
|
||||
|
||||
void main(){
|
||||
|
||||
vec2 pos = v_texcoord;
|
||||
vec4 color = texture2D(u_tex0, pos);
|
||||
|
||||
vec3 hsv = rgb2hsv(color.rgb);
|
||||
hsv.x += u_x0 - 0.5;
|
||||
hsv.y += u_x1 - 0.5;
|
||||
hsv.z += u_x2 - 0.5;
|
||||
//hsv.x += -0.2;
|
||||
vec3 rgb = hsv2rgb(hsv.xyz);
|
||||
|
||||
|
||||
gl_FragColor = vec4(rgb, color.a);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
59
Shaders/1-input/hsv_control_fine.frag
Normal file
@@ -0,0 +1,59 @@
|
||||
//1-input
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D u_tex0;
|
||||
uniform sampler2D u_tex1;
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform float u_x0;
|
||||
uniform float u_x1;
|
||||
uniform float u_x2;
|
||||
uniform float u_x3;
|
||||
|
||||
vec3 rgb2grayscale(vec3 c)
|
||||
{
|
||||
return(vec3(dot(c, vec3(0.299, 0.587, 0.114))));
|
||||
}
|
||||
|
||||
vec3 rgb2hsv(vec3 c)
|
||||
{
|
||||
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
|
||||
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
|
||||
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
|
||||
|
||||
float d = q.x - min(q.w, q.y);
|
||||
float e = 1.0e-10;
|
||||
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
|
||||
}
|
||||
|
||||
vec3 hsv2rgb(vec3 c)
|
||||
{
|
||||
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
||||
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
||||
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
||||
}
|
||||
|
||||
void main(){
|
||||
|
||||
vec2 pos = v_texcoord;
|
||||
vec4 color = texture2D(u_tex0, pos);
|
||||
|
||||
vec3 hsv = rgb2hsv(color.rgb);
|
||||
hsv.x += 0.1*u_x0 - 0.05;
|
||||
hsv.y += 0.1*u_x1 - 0.05;
|
||||
hsv.z += 0.1*u_x2 - 0.05;
|
||||
//hsv.x += -0.2;
|
||||
vec3 rgb = hsv2rgb(hsv.xyz);
|
||||
|
||||
|
||||
gl_FragColor = vec4(rgb, color.a);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
15
Shaders/1-input/invert_effect.frag
Normal file
@@ -0,0 +1,15 @@
|
||||
//1-input
|
||||
//written by Tim Caldwell
|
||||
// this is a simple example of how to change incoming textures colour
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D u_tex0;
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform float u_x0;
|
||||
uniform float u_x1;
|
||||
uniform float u_x2;
|
||||
|
||||
void main() {
|
||||
vec4 texColour = texture2D(u_tex0, v_texcoord);
|
||||
gl_FragColor = vec4(1.0 - u_x0 - texColour.r,1.0 -u_x1-texColour.g,1.0 - u_x2-texColour.b,texColour.a);
|
||||
}
|
||||
44
Shaders/1-input/kaleidoscope.frag
Normal file
@@ -0,0 +1,44 @@
|
||||
//1-input
|
||||
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
#define PI 3.1415926538979323846
|
||||
#define TWO_PI 2*PI
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D u_tex0;
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform float u_x0;
|
||||
uniform float u_x1;
|
||||
uniform float u_x2;
|
||||
uniform float u_x3;
|
||||
|
||||
//uniform float kangleRad = 0.0;
|
||||
//uniform vec2 u_resolution;
|
||||
|
||||
void main(){
|
||||
int ksectors = 1 + int(u_x0*20.0);
|
||||
vec2 screenCenter = vec2(0.5, 0.5) ;
|
||||
vec2 kcenter = screenCenter + vec2(u_x2, u_x3);
|
||||
float kangleRad = u_time*u_x1;
|
||||
float twoPi = 3.1415926538979323846 * 2.0;
|
||||
vec2 pos = v_texcoord ;
|
||||
vec2 v = pos.xy - screenCenter;
|
||||
float r = length(v);
|
||||
float a = atan (v.y, v.x);
|
||||
|
||||
float A = twoPi / float(ksectors);
|
||||
a = mod(a, A);
|
||||
if (a > A/2.0 ){ a = A - a; }
|
||||
a -= kangleRad;
|
||||
|
||||
vec2 u =vec2( cos(a), sin(a) ) * r;
|
||||
u += kcenter;
|
||||
gl_FragColor = texture2D(u_tex0, u);
|
||||
}
|
||||
|
||||
|
||||
|
||||
29
Shaders/1-input/mirror.frag
Normal file
@@ -0,0 +1,29 @@
|
||||
//1-input
|
||||
//written by Tim Caldwell
|
||||
//this is a basic example of how to effect input textures position
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D u_tex0;
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform float u_x0;
|
||||
uniform float u_x1;
|
||||
|
||||
void main(){
|
||||
|
||||
vec2 pos = v_texcoord;
|
||||
|
||||
if(pos.x > u_x0){pos.x = 1.0 - pos.x;}
|
||||
if(pos.y < u_x1){pos.y = 1.0 - pos.y;}
|
||||
vec4 color = texture2D(u_tex0, pos);
|
||||
|
||||
gl_FragColor = color;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
54
Shaders/1-input/rotate.frag
Normal file
@@ -0,0 +1,54 @@
|
||||
//1-input
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D u_tex0;
|
||||
uniform sampler2D u_tex1;
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform float u_x0;
|
||||
uniform float u_x1;
|
||||
uniform float u_x2;
|
||||
uniform float u_x3;
|
||||
|
||||
vec4 rotate(sampler2D tex, vec2 pos){
|
||||
|
||||
vec4 texColourRotate;
|
||||
vec2 center;
|
||||
//center = vec2(u_x3 / u_resolution.x, u_x4 / u_resolution.y);
|
||||
center = vec2(u_x1, u_x2);
|
||||
|
||||
pos.x = (pos.x - center.x)*(0.5 / u_x3) + center.x;
|
||||
pos.y = (pos.y - center.y)*(0.5 / u_x3) + center.y;
|
||||
|
||||
float r = distance(center, pos);
|
||||
float a = atan(pos.x - center.x, pos.y - center.y);
|
||||
|
||||
pos.x = r * cos(a + 2.0 * 3.141592 * u_x0) + 0.5;
|
||||
pos.y = r * sin(a + 2.0 * 3.141592 * u_x0) + 0.5;
|
||||
pos.x = 1.0 - pos.x;
|
||||
if((pos.x < 0.0)||(pos.y < 0.0)||(pos.x > 1.0)||(pos.y > 1.0)){
|
||||
texColourRotate = vec4(0.0);
|
||||
}
|
||||
else{
|
||||
texColourRotate = texture2D(tex, pos);
|
||||
}
|
||||
return texColourRotate;
|
||||
}
|
||||
|
||||
void main(){
|
||||
|
||||
vec2 pos = v_texcoord;
|
||||
vec4 texColour0;
|
||||
texColour0 = rotate(u_tex0, v_texcoord);
|
||||
|
||||
gl_FragColor = texColour0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
54
Shaders/1-input/rotate_fine.frag
Normal file
@@ -0,0 +1,54 @@
|
||||
//1-input
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D u_tex0;
|
||||
uniform sampler2D u_tex1;
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform float u_x0;
|
||||
uniform float u_x1;
|
||||
uniform float u_x2;
|
||||
uniform float u_x3;
|
||||
|
||||
vec4 rotate(sampler2D tex, vec2 pos){
|
||||
|
||||
vec4 texColourRotate;
|
||||
vec2 center;
|
||||
//center = vec2(u_x3 / u_resolution.x, u_x4 / u_resolution.y);
|
||||
center = vec2(0.45 + 0.1*u_x1,0.45 + 0.1*u_x2);
|
||||
|
||||
pos.x = (pos.x - center.x)*(0.5 / (0.45 + 0.1*u_x3)) + center.x;
|
||||
pos.y = (pos.y - center.y)*(0.5 / (0.45 + 0.1*u_x3)) + center.y;
|
||||
|
||||
float r = distance(center, pos);
|
||||
float a = atan(pos.x - center.x, pos.y - center.y);
|
||||
|
||||
pos.x = r * cos(a + 2.0 * 3.141592 * (0.2 + 0.1*u_x0)) + 0.5;
|
||||
pos.y = r * sin(a + 2.0 * 3.141592 * (0.2 + 0.1*u_x0)) + 0.5;
|
||||
pos.x = 1.0 - pos.x;
|
||||
if((pos.x < 0.0)||(pos.y < 0.0)||(pos.x > 1.0)||(pos.y > 1.0)){
|
||||
texColourRotate = vec4(0.0);
|
||||
}
|
||||
else{
|
||||
texColourRotate = texture2D(tex, pos);
|
||||
}
|
||||
return texColourRotate;
|
||||
}
|
||||
|
||||
void main(){
|
||||
|
||||
vec2 pos = v_texcoord;
|
||||
vec4 texColour0;
|
||||
texColour0 = rotate(u_tex0, v_texcoord);
|
||||
|
||||
gl_FragColor = texColour0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
42
Shaders/1-input/s-wobble.frag
Normal file
@@ -0,0 +1,42 @@
|
||||
//1-input
|
||||
//written by Tim Caldwell
|
||||
#ifdef GL_ES
|
||||
precision highp float;
|
||||
#endif
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D u_tex0;
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform float u_x0;
|
||||
uniform float u_x1;
|
||||
uniform float u_x2;
|
||||
|
||||
void main(){
|
||||
|
||||
vec2 pos = v_texcoord * u_resolution;
|
||||
vec2 centre = u_resolution / 2.0;
|
||||
vec2 norm = pos - centre;
|
||||
//float r = distance(u_resolution, pos);
|
||||
float r = length(norm);
|
||||
//float theta = acos(norm.x / r);
|
||||
float theta = atan(pos.y / pos.x);
|
||||
//float newTheta = theta + 0.000;
|
||||
theta += u_time*u_x0;
|
||||
//pos.x = r*cos(theta) + centre.x;
|
||||
pos.y = 4.0*u_x1*r*sin(theta) + centre.y;
|
||||
pos.x = 4.0*(1.0-u_x2)*r*cos(theta) + centre.x;
|
||||
//pos.y = r*sin(radians(theta)) + centre.y;
|
||||
|
||||
|
||||
|
||||
|
||||
vec4 color = texture2D(u_tex0, pos / u_resolution);
|
||||
|
||||
gl_FragColor = color;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
67
Shaders/1-input/simple_colorizer.frag
Normal file
@@ -0,0 +1,67 @@
|
||||
//1-input
|
||||
//written by Tim Caldwell
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D u_tex0;
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform float u_x0;
|
||||
uniform float u_x1;
|
||||
uniform float u_x2;
|
||||
uniform float u_x3;
|
||||
|
||||
uniform int intDiv;
|
||||
|
||||
vec3 rgb2grayscale(vec3 c)
|
||||
{
|
||||
return(vec3(dot(c, vec3(0.299, 0.587, 0.114))));
|
||||
}
|
||||
|
||||
vec3 rgb2hsv(vec3 c)
|
||||
{
|
||||
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
|
||||
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
|
||||
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
|
||||
|
||||
float d = q.x - min(q.w, q.y);
|
||||
float e = 1.0e-10;
|
||||
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
|
||||
}
|
||||
|
||||
vec3 hsv2rgb(vec3 c)
|
||||
{
|
||||
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
||||
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
||||
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
||||
}
|
||||
|
||||
void main(){
|
||||
|
||||
vec2 pos = v_texcoord;
|
||||
vec4 color = texture2D(u_tex0, pos);
|
||||
|
||||
vec3 gray = rgb2grayscale(color.rgb);
|
||||
|
||||
|
||||
|
||||
const int intDiv = 5;
|
||||
float divisions = float(intDiv);
|
||||
float lum = (gray.x + gray.y + gray.z) / 3.0;
|
||||
vec3 newColor = vec3(0.0, 0.7, 0.7);
|
||||
for (int i = 0; i < intDiv; i++ ){
|
||||
if( lum >= float(i)/divisions && lum < (float(i)+1.0)/divisions){
|
||||
newColor.x = mod((float(i)/divisions + u_x0*u_time*0.2)/u_x1 + u_x2,1.0);
|
||||
}
|
||||
}
|
||||
|
||||
gl_FragColor = vec4(hsv2rgb(newColor), color.a);
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
35
Shaders/1-input/wobble.frag
Normal file
@@ -0,0 +1,35 @@
|
||||
//1-input
|
||||
//written by Tim Caldwell
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D u_tex0;
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform float u_x0;
|
||||
uniform float u_x1;
|
||||
uniform float u_x2;
|
||||
uniform float u_x3;
|
||||
|
||||
|
||||
void main(){
|
||||
|
||||
vec2 pos = v_texcoord;
|
||||
//float amp = pos.y * 0.03 + .001;
|
||||
pos.x += 0.07*u_x2*sin( pos.y* 70.0*u_x3 + u_time * 1.0 );
|
||||
pos.y += 0.07*u_x0*sin( pos.x* 70.0*u_x1 + u_time * 1.0);
|
||||
pos.y = mod(pos.y,1.0);
|
||||
pos.x = mod(pos.x,1.0);
|
||||
//if(pos.y > 1.0 || pos.y < 0.0){pos.y = 0.0;}
|
||||
|
||||
vec4 color = texture2D(u_tex0, pos);
|
||||
|
||||
gl_FragColor = color;
|
||||
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
46
Shaders/1-input/zoom.frag
Normal file
@@ -0,0 +1,46 @@
|
||||
//1-input
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D u_tex0;
|
||||
uniform sampler2D u_tex1;
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform float u_x0;
|
||||
uniform float u_x1;
|
||||
uniform float u_x2;
|
||||
uniform float u_x3;
|
||||
|
||||
vec4 zoom(sampler2D tex, vec2 pos){
|
||||
vec4 texColourZoom;
|
||||
vec2 center;
|
||||
|
||||
center = vec2(u_x2, u_x3);
|
||||
|
||||
pos.x = (pos.x - center.x)*(0.5 / u_x0) + center.x;
|
||||
pos.y = (pos.y - center.y)*(0.5 / u_x0) + center.y;
|
||||
if((pos.x < 0.0)||(pos.y < 0.0)||(pos.x > 1.0)||(pos.y > 1.0)){
|
||||
texColourZoom = vec4(0.0);
|
||||
}
|
||||
else{
|
||||
texColourZoom = texture2D(tex, pos);
|
||||
}
|
||||
return texColourZoom;
|
||||
}
|
||||
|
||||
void main(){
|
||||
|
||||
vec2 pos = v_texcoord;
|
||||
vec4 texColour0;
|
||||
texColour0 = zoom(u_tex0, v_texcoord);
|
||||
|
||||
gl_FragColor = texColour0;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
75
Shaders/2-input/add_mix.frag
Normal file
@@ -0,0 +1,75 @@
|
||||
//2-input
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D u_tex0;
|
||||
uniform sampler2D u_tex1;
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform float u_x0;
|
||||
uniform float u_x1;
|
||||
uniform float u_x2;
|
||||
uniform float u_x3;
|
||||
|
||||
|
||||
vec3 rgb2hsv(vec3 c)
|
||||
{
|
||||
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
|
||||
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
|
||||
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
|
||||
|
||||
float d = q.x - min(q.w, q.y);
|
||||
float e = 1.0e-10;
|
||||
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
|
||||
}
|
||||
|
||||
vec3 hsv2rgb(vec3 c)
|
||||
{
|
||||
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
||||
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
||||
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
||||
}
|
||||
|
||||
vec4 mixBlendAdd0(vec4 texColour0, vec4 texColour1) {
|
||||
vec4 colour;
|
||||
vec3 hsvTexColour0 = rgb2hsv(texColour0.rgb);
|
||||
vec3 hsvTexColour1 = rgb2hsv(texColour1.rgb);
|
||||
|
||||
if((1.0 - u_x0) * hsvTexColour0.z < u_x0 * hsvTexColour1.x){colour = texColour1;}
|
||||
else {colour = texColour0;}
|
||||
|
||||
return colour;
|
||||
}
|
||||
|
||||
vec4 mixBlendAdd1(vec4 texColour0, vec4 texColour1) {
|
||||
vec4 colour;
|
||||
vec3 hsvTexColour0 = rgb2hsv(texColour0.rgb);
|
||||
vec3 hsvTexColour1 = rgb2hsv(texColour1.rgb);
|
||||
|
||||
if((1.0 - u_x0) * hsvTexColour0.z < u_x0 * hsvTexColour1.x){colour = texColour1;}
|
||||
else {colour = texColour0;}
|
||||
|
||||
return colour;
|
||||
}
|
||||
|
||||
|
||||
void main() {
|
||||
|
||||
vec2 pos = v_texcoord;
|
||||
vec4 texColour0;
|
||||
vec4 texColour1;
|
||||
|
||||
texColour0 = texture2D(u_tex0, v_texcoord);
|
||||
texColour1 = texture2D(u_tex1, v_texcoord);
|
||||
|
||||
|
||||
vec4 colour;
|
||||
|
||||
if(u_x1 > 0.5){colour = mixBlendAdd0(texColour0, texColour1);}
|
||||
else{colour = mixBlendAdd1(texColour0, texColour1);}
|
||||
|
||||
gl_FragColor = colour;
|
||||
|
||||
}
|
||||
42
Shaders/2-input/blend_add.frag
Normal file
@@ -0,0 +1,42 @@
|
||||
//2-input
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D u_tex0;
|
||||
uniform sampler2D u_tex1;
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform float u_x0;
|
||||
uniform float u_x1;
|
||||
uniform float u_x2;
|
||||
uniform float u_x3;
|
||||
|
||||
vec4 mixBlend(vec4 texColour0, vec4 texColour1) {
|
||||
vec4 colour;
|
||||
colour = texColour0;
|
||||
colour.xyz = (1.0 - u_x0) * texColour0.xyz + u_x0 * texColour1.xyz;
|
||||
|
||||
return colour;
|
||||
}
|
||||
|
||||
|
||||
void main() {
|
||||
|
||||
vec2 pos = v_texcoord;
|
||||
vec4 texColour0;
|
||||
vec4 texColour1;
|
||||
|
||||
texColour0 = texture2D(u_tex0, v_texcoord);
|
||||
texColour1 = texture2D(u_tex1, v_texcoord);
|
||||
|
||||
|
||||
vec4 colour;
|
||||
|
||||
|
||||
colour = mixBlend(texColour0, texColour1);
|
||||
|
||||
gl_FragColor = colour;
|
||||
|
||||
}
|
||||
71
Shaders/2-input/luma_key.frag
Normal file
@@ -0,0 +1,71 @@
|
||||
//2-input
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D u_tex0;
|
||||
uniform sampler2D u_tex1;
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform float u_x0;
|
||||
uniform float u_x1;
|
||||
uniform float u_x2;
|
||||
uniform float u_x3;
|
||||
|
||||
|
||||
vec3 rgb2hsv(vec3 c)
|
||||
{
|
||||
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
|
||||
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
|
||||
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
|
||||
|
||||
float d = q.x - min(q.w, q.y);
|
||||
float e = 1.0e-10;
|
||||
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
|
||||
}
|
||||
|
||||
vec3 hsv2rgb(vec3 c)
|
||||
{
|
||||
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
||||
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
||||
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
||||
}
|
||||
|
||||
vec4 mixLuma0(vec4 texColour0, vec4 texColour1) {
|
||||
vec4 colour;
|
||||
vec3 hsvTexColour0 = rgb2hsv(texColour0.rgb);
|
||||
|
||||
if(hsvTexColour0.z < u_x0){colour = texColour0;}
|
||||
else {colour = texColour1;}
|
||||
return colour;
|
||||
}
|
||||
|
||||
vec4 mixLuma1(vec4 texColour0, vec4 texColour1) {
|
||||
vec4 colour;
|
||||
vec3 hsvTexColour1 = rgb2hsv(texColour1.rgb);
|
||||
|
||||
if(hsvTexColour1.z < u_x0){colour = texColour1;}
|
||||
else {colour = texColour0;}
|
||||
return colour;
|
||||
}
|
||||
|
||||
|
||||
void main() {
|
||||
|
||||
vec2 pos = v_texcoord;
|
||||
vec4 texColour0;
|
||||
vec4 texColour1;
|
||||
|
||||
texColour0 = texture2D(u_tex0, v_texcoord);
|
||||
texColour1 = texture2D(u_tex1, v_texcoord);
|
||||
|
||||
|
||||
vec4 colour;
|
||||
|
||||
if(u_x1 > 0.5){colour = mixLuma0(texColour0, texColour1);}
|
||||
else{colour = mixLuma1(texColour0, texColour1);}
|
||||
|
||||
gl_FragColor = colour;
|
||||
|
||||
}
|
||||
67
Shaders/2-input/mix_lumaKey.frag
Normal file
@@ -0,0 +1,67 @@
|
||||
|
||||
varying vec2 tcoord; //
|
||||
uniform sampler2D tex; // texture one
|
||||
uniform sampler2D tex2; // texture two
|
||||
uniform vec2 tres; // size of texture (screen)
|
||||
uniform vec4 fparams; // 4 floats coming in
|
||||
uniform ivec4 iparams; // 4 ints coming in
|
||||
uniform float ftime; // 0.0 to 1.0
|
||||
uniform int itime; // increases when ftime hits 1.0
|
||||
//f0:key luma:
|
||||
//f1:key range:
|
||||
//f2:edge opacity:
|
||||
|
||||
float f0 = mix(0.0, 1.0, fparams[0]);
|
||||
float f1 = mix(0.0, 0.5, fparams[1]);
|
||||
float f2 = mix(0.0, 1.0, fparams[2]);
|
||||
|
||||
//---------------------------------------------------------------------------------------------------
|
||||
|
||||
vec3 rgb2hsv(vec3 c)
|
||||
{
|
||||
vec4 K = vec4(0.0, -1.0 / 3.0, 2.0 / 3.0, -1.0);
|
||||
vec4 p = mix(vec4(c.bg, K.wz), vec4(c.gb, K.xy), step(c.b, c.g));
|
||||
vec4 q = mix(vec4(p.xyw, c.r), vec4(c.r, p.yzx), step(p.x, c.r));
|
||||
|
||||
float d = q.x - min(q.w, q.y);
|
||||
float e = 1.0e-10;
|
||||
return vec3(abs(q.z + (q.w - q.y) / (6.0 * d + e)), d / (q.x + e), q.x);
|
||||
}
|
||||
|
||||
vec3 hsv2rgb(vec3 c)
|
||||
{
|
||||
vec4 K = vec4(1.0, 2.0 / 3.0, 1.0 / 3.0, 3.0);
|
||||
vec3 p = abs(fract(c.xxx + K.xyz) * 6.0 - K.www);
|
||||
return c.z * mix(K.xxx, clamp(p - K.xxx, 0.0, 1.0), c.y);
|
||||
}
|
||||
|
||||
|
||||
|
||||
void main() {
|
||||
|
||||
vec3 outc;
|
||||
vec4 base = texture2D(tex, tcoord.xy);
|
||||
vec4 blend = texture2D(tex2, tcoord.xy);
|
||||
|
||||
vec3 hsv = rgb2hsv(base.rgb);
|
||||
|
||||
if( (hsv.z > (f0-f1)) && (hsv.z < (f0+f1)) ){
|
||||
if(hsv.z-(f0-f1) < f2){
|
||||
outc = mix(base.rgb,blend.rgb,(hsv.z-(f0-f1))/f2);
|
||||
} else if((f0+f1)-hsv.z < f2){
|
||||
outc = mix(base.rgb,blend.rgb,((f0+f1)-hsv.z)/f2);
|
||||
} else {
|
||||
outc = blend.rgb;
|
||||
}
|
||||
|
||||
|
||||
} else {
|
||||
outc = base.rgb;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
gl_FragColor=vec4(outc,1.0);
|
||||
|
||||
}
|
||||
36
Shaders/2-input/wipe.frag
Normal file
@@ -0,0 +1,36 @@
|
||||
//2-input
|
||||
#ifdef GL_ES
|
||||
precision mediump float;
|
||||
#endif
|
||||
|
||||
varying vec2 v_texcoord;
|
||||
uniform sampler2D u_tex0;
|
||||
uniform sampler2D u_tex1;
|
||||
uniform vec2 u_resolution;
|
||||
uniform float u_time;
|
||||
uniform float u_x0;
|
||||
uniform float u_x1;
|
||||
uniform float u_x2;
|
||||
uniform float u_x3;
|
||||
|
||||
|
||||
void main(){
|
||||
|
||||
vec2 pos = v_texcoord;
|
||||
vec4 texColour0 = texture2D(u_tex0, v_texcoord);
|
||||
vec4 texColour1 = texture2D(u_tex1, v_texcoord);
|
||||
|
||||
vec4 colour;
|
||||
|
||||
if(v_texcoord.x > u_x0 && v_texcoord.y > u_x1){colour = texColour0;}
|
||||
else {colour = texColour1;}
|
||||
|
||||
|
||||
gl_FragColor = colour;
|
||||
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
27
Shaders/default.vert
Normal file
@@ -0,0 +1,27 @@
|
||||
// this is the default.vert shader used by conjur , it ensures that the frag shaders run by glslViewer will also run in openframeworks
|
||||
attribute vec4 position;
|
||||
attribute vec4 color;
|
||||
attribute vec3 normal;
|
||||
attribute vec2 texcoord;
|
||||
|
||||
uniform mat4 modelViewProjectionMatrix;
|
||||
|
||||
varying vec4 v_position;
|
||||
varying vec4 v_color;
|
||||
varying vec3 v_normal;
|
||||
varying vec2 tcoord;
|
||||
// twice because supporting two shader formats
|
||||
varying vec2 v_texcoord;
|
||||
|
||||
uniform mat4 u_modelViewProjectionMatrix;
|
||||
|
||||
void main() {
|
||||
v_position = position;
|
||||
v_color = color;
|
||||
v_normal = normal;
|
||||
tcoord = texcoord;
|
||||
v_texcoord = texcoord;
|
||||
|
||||
gl_Position = modelViewProjectionMatrix * position;
|
||||
|
||||
}
|
||||
636
actions.py
@@ -2,17 +2,42 @@ import subprocess
|
||||
import tracemalloc
|
||||
import data_centre.length_setter as length_setter
|
||||
import sys
|
||||
import shlex
|
||||
import os
|
||||
from pythonosc import osc_message_builder
|
||||
from pythonosc import dispatcher
|
||||
from pythonosc import osc_server
|
||||
import git
|
||||
import threading
|
||||
import argparse
|
||||
from video_centre.capture import Capture
|
||||
from video_centre.of_capture import OfCapture
|
||||
|
||||
class Actions(object):
|
||||
def __init__(self, tk, message_handler, data, video_driver, capture, display):
|
||||
def __init__(self, tk, message_handler, data, video_driver, shaders, display, osc_client):
|
||||
self.tk = tk
|
||||
self.message_handler = message_handler
|
||||
self.data = data
|
||||
self.video_driver = video_driver
|
||||
self.capture = capture
|
||||
self.shaders = shaders
|
||||
self.display = display
|
||||
self.osc_client = osc_client
|
||||
self.of_capture = OfCapture(self.tk, self.osc_client, self.message_handler, self.data)
|
||||
self.python_capture = self.capture = Capture(self.tk, self.message_handler, self.data)
|
||||
self.capture = None
|
||||
self.serial_port_process = None
|
||||
self.openframeworks_process = None
|
||||
self.set_capture_object('value')
|
||||
self.server = self.setup_osc_server()
|
||||
|
||||
def set_capture_object(self, value):
|
||||
if self.data.settings['video']['VIDEOPLAYER_BACKEND']['value'] != 'omxplayer':
|
||||
self.python_capture.close_capture()
|
||||
self.capture = self.of_capture
|
||||
else:
|
||||
self.python_capture.close_capture()
|
||||
self.capture = self.python_capture
|
||||
self.display.capture = self.capture
|
||||
|
||||
def move_browser_selection_down(self):
|
||||
self.display.browser_menu.navigate_menu_down()
|
||||
@@ -38,13 +63,40 @@ class Actions(object):
|
||||
else:
|
||||
getattr(self, setting['action'])(setting['value'])
|
||||
|
||||
def move_shaders_selection_down(self):
|
||||
self.shaders.shaders_menu.navigate_menu_down()
|
||||
|
||||
def move_shaders_selection_up(self):
|
||||
self.shaders.shaders_menu.navigate_menu_up()
|
||||
|
||||
def enter_on_shaders_selection(self):
|
||||
##want to select shader if its not selected, and want to enter 'param' mode if it already is
|
||||
is_shader, is_selected_shader, selected_shader = self.shaders.enter_on_shaders_selection()
|
||||
if is_selected_shader and selected_shader['param_number'] > 0:
|
||||
self.set_shader_param_mode()
|
||||
|
||||
def map_on_shaders_selection(self):
|
||||
self.shaders.map_on_shaders_selection()
|
||||
|
||||
def clear_all_slots(self):
|
||||
self.data.clear_all_slots()
|
||||
self.display.browser_menu.generate_browser_list()
|
||||
|
||||
def _load_this_slot_into_next_player(self, slot):
|
||||
if self.data.update_next_slot_number(slot):
|
||||
self.video_driver.reload_next_player()
|
||||
### load next player for seamless type otherwise respect player mode
|
||||
if self.data.settings['sampler']['LOOP_TYPE']['value'] == 'seamless':
|
||||
if self.data.update_next_slot_number(slot):
|
||||
print('should reload next player !! ')
|
||||
self.video_driver.reload_next_player()
|
||||
else:
|
||||
if self.data.player_mode == 'next':
|
||||
if self.data.update_next_slot_number(slot, is_current=False):
|
||||
self.video_driver.reload_next_player()
|
||||
else:
|
||||
if self.data.update_next_slot_number(slot, is_current=True):
|
||||
self.video_driver.reload_current_player()
|
||||
|
||||
|
||||
|
||||
def load_slot_0_into_next_player(self):
|
||||
self._load_this_slot_into_next_player(0)
|
||||
@@ -77,20 +129,30 @@ class Actions(object):
|
||||
self._load_this_slot_into_next_player(9)
|
||||
|
||||
def switch_to_next_player(self):
|
||||
self.video_driver.switch_players_and_start_video()
|
||||
if self.data.settings['sampler']['LOOP_TYPE']['value'] == 'seamless':
|
||||
self.video_driver.switch_players_and_start_video()
|
||||
else:
|
||||
self.video_driver.current_player.toggle_show()
|
||||
if self.video_driver.current_player.show_toggle_on == self.video_driver.next_player.show_toggle_on:
|
||||
self.video_driver.next_player.toggle_show()
|
||||
|
||||
|
||||
def cycle_display_mode(self):
|
||||
self.display.top_menu_index = 0
|
||||
self.display.selected_list_index = self.display.top_menu_index
|
||||
if self.data.display_mode == "BROWSER":
|
||||
self.data.display_mode = "SETTINGS"
|
||||
self.data.control_mode = 'NAV_SETTINGS'
|
||||
elif self.data.display_mode == "SAMPLER":
|
||||
self.data.display_mode = "BROWSER"
|
||||
self.data.control_mode = 'NAV_BROWSER'
|
||||
elif self.data.display_mode == "SETTINGS":
|
||||
self.data.display_mode = "SAMPLER"
|
||||
self.data.control_mode = 'PLAYER'
|
||||
display_modes = self.data.get_display_modes_list(with_nav_mode=True)
|
||||
|
||||
current_mode_index = [index for index, i in enumerate(display_modes) if self.data.display_mode in i][0]
|
||||
next_mode_index = (current_mode_index + 1) % len(display_modes)
|
||||
self.data.display_mode = display_modes[next_mode_index][0]
|
||||
self.data.control_mode = display_modes[next_mode_index][1]
|
||||
|
||||
def cycle_display_mode_back(self):
|
||||
display_modes = self.data.get_display_modes_list(with_nav_mode=True)
|
||||
|
||||
current_mode_index = [index for index, i in enumerate(display_modes) if self.data.display_mode in i][0]
|
||||
next_mode_index = (current_mode_index - 1) % len(display_modes)
|
||||
self.data.display_mode = display_modes[next_mode_index][0]
|
||||
self.data.control_mode = display_modes[next_mode_index][1]
|
||||
|
||||
|
||||
def toggle_action_on_player(self):
|
||||
play = 'play' in self.data.settings['sampler']['ON_ACTION']['value']
|
||||
@@ -112,17 +174,30 @@ class Actions(object):
|
||||
elif self.data.player_mode == 'next':
|
||||
self.video_driver.next_player.toggle_show()
|
||||
|
||||
def increase_seek_time(self):
|
||||
options = self.data.settings['sampler']['SEEK_TIME']['options']
|
||||
current_index = [index for index, item in enumerate(options) if item == self.data.settings['sampler']['SEEK_TIME']['value'] ][0]
|
||||
self.data.settings['sampler']['SEEK_TIME']['value'] = options[(current_index + 1) % len(options) ]
|
||||
self.message_handler.set_message('INFO', 'The Seek Time is now ' + str(self.data.settings['sampler']['SEEK_TIME']['value']) + 's')
|
||||
|
||||
|
||||
def decrease_seek_time(self):
|
||||
options = self.data.settings['sampler']['SEEK_TIME']['options']
|
||||
current_index = [index for index, item in enumerate(options) if item == self.data.settings['sampler']['SEEK_TIME']['value'] ][0]
|
||||
self.data.settings['sampler']['SEEK_TIME']['value'] = options[(current_index - 1) % len(options) ]
|
||||
self.message_handler.set_message('INFO', 'The Seek Time is now ' + str(self.data.settings['sampler']['SEEK_TIME']['value']) + 's')
|
||||
|
||||
|
||||
def seek_forward_on_player(self):
|
||||
self.video_driver.current_player.seek(30)
|
||||
self.video_driver.current_player.seek(self.data.settings['sampler']['SEEK_TIME']['value'])
|
||||
|
||||
def seek_back_on_player(self):
|
||||
self.video_driver.current_player.seek(-30)
|
||||
self.video_driver.current_player.seek(-(self.data.settings['sampler']['SEEK_TIME']['value']))
|
||||
|
||||
def toggle_function(self):
|
||||
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)))
|
||||
|
||||
@@ -131,16 +206,18 @@ class Actions(object):
|
||||
print('current bank is {} , the number of banks is {} '.format(self.data.bank_number, len(self.data.bank_data)))
|
||||
|
||||
def increase_speed(self):
|
||||
new_rate = self.video_driver.current_player.change_rate(0.5)
|
||||
print("increasing speed !")
|
||||
new_rate = self.video_driver.current_player.change_rate(1)
|
||||
current_bank, current_slot = self.data.split_bankslot_number(self.video_driver.current_player.bankslot_number)
|
||||
self.data.update_slot_rate_to_this(current_slot, new_rate)
|
||||
self._load_this_slot_into_next_player(current_slot)
|
||||
#self._load_this_slot_into_next_player(current_slot)
|
||||
|
||||
def decrease_speed(self):
|
||||
new_rate = self.video_driver.current_player.change_rate(-0.5)
|
||||
print("increasing speed !")
|
||||
new_rate = self.video_driver.current_player.change_rate(-1)
|
||||
current_bank, current_slot = self.data.split_bankslot_number(self.video_driver.current_player.bankslot_number)
|
||||
self.data.update_slot_rate_to_this(current_slot, new_rate)
|
||||
self._load_this_slot_into_next_player(current_slot)
|
||||
#self._load_this_slot_into_next_player(current_slot)
|
||||
|
||||
def set_playing_sample_start_to_current_duration(self):
|
||||
current_bank, current_slot = self.data.split_bankslot_number(self.video_driver.current_player.bankslot_number)
|
||||
@@ -168,12 +245,13 @@ class Actions(object):
|
||||
is_previewing = self.capture.is_previewing
|
||||
if is_previewing:
|
||||
self.capture.stop_preview()
|
||||
if self.video_driver.current_player.status == 'PAUSED':
|
||||
self.video_driver.current_player.toggle_pause()
|
||||
#if self.video_driver.current_player.status == 'PAUSED':
|
||||
#self.video_driver.current_player.toggle_pause()
|
||||
else:
|
||||
is_successful = self.capture.start_preview()
|
||||
if is_successful and self.video_driver.current_player.status != 'PAUSED':
|
||||
self.video_driver.current_player.toggle_pause()
|
||||
#if is_successful and self.video_driver.current_player.status != 'PAUSED':
|
||||
#self.video_driver.current_player.toggle_pause()
|
||||
|
||||
|
||||
def toggle_capture_recording(self):
|
||||
is_recording = self.capture.is_recording
|
||||
@@ -183,7 +261,7 @@ class Actions(object):
|
||||
self.capture.start_recording()
|
||||
|
||||
def toggle_screen_mirror(self):
|
||||
if self.data.settings['other']['DEV_MODE_RESET']['value'] == 'off':
|
||||
if self.data.settings['system']['DEV_MODE_RESET']['value'] == 'off':
|
||||
if self.data.update_screen:
|
||||
self.data.update_screen = False
|
||||
subprocess.call(['sudo', 'systemctl', 'start', 'raspi2fb@1'])
|
||||
@@ -193,97 +271,344 @@ class Actions(object):
|
||||
else:
|
||||
self.message_handler.set_message('INFO', 'cant mirror in dev mode')
|
||||
|
||||
def toggle_shaders(self):
|
||||
if self.shaders.selected_status_list[self.data.shader_layer] == '▶':
|
||||
self.shaders.stop_selected_shader()
|
||||
elif self.shaders.selected_status_list[self.data.shader_layer] == '■':
|
||||
self.shaders.start_selected_shader()
|
||||
else:
|
||||
self.message_handler.set_message('INFO', 'no shader loaded')
|
||||
|
||||
def toggle_shader_speed(self):
|
||||
self.shaders.toggle_shader_speed()
|
||||
|
||||
def toggle_player_mode(self):
|
||||
if self.data.player_mode == 'now':
|
||||
self.data.player_mode = 'next'
|
||||
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 toggle_detour_mode(self):
|
||||
if self.data.settings['detour']['TRY_DEMO']['value'] == 'enabled':
|
||||
if self.data.detour_active:
|
||||
self.data.detour_active = False
|
||||
self.video_driver.osc_client.send_message("/detour/end", True)
|
||||
else:
|
||||
self.data.detour_active = True
|
||||
shader_input = self.data.settings['detour']['SHADER_POSITION']['value'] == 'input'
|
||||
self.video_driver.osc_client.send_message("/detour/start", shader_input)
|
||||
self.load_this_detour_shader()
|
||||
|
||||
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 toggle_detour_play(self):
|
||||
if self.data.settings['detour']['TRY_DEMO']['value'] == 'enabled':
|
||||
is_playing = not self.data.detour_settings['is_playing']
|
||||
self.data.detour_settings['is_playing'] = is_playing
|
||||
self.video_driver.osc_client.send_message("/detour/is_playing", is_playing)
|
||||
|
||||
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 toggle_feedback(self):
|
||||
print('toggle here')
|
||||
self.data.feedback_active = not self.data.feedback_active
|
||||
self.video_driver.osc_client.send_message("/toggle_feedback", self.data.feedback_active)
|
||||
|
||||
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 play_shader_0(self):
|
||||
self.play_this_shader(0)
|
||||
|
||||
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 play_shader_1(self):
|
||||
self.play_this_shader(1)
|
||||
|
||||
@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 play_shader_2(self):
|
||||
self.play_this_shader(2)
|
||||
|
||||
def play_shader_3(self):
|
||||
self.play_this_shader(3)
|
||||
|
||||
def play_shader_4(self):
|
||||
self.play_this_shader(4)
|
||||
|
||||
def play_shader_5(self):
|
||||
self.play_this_shader(5)
|
||||
|
||||
def play_shader_6(self):
|
||||
self.play_this_shader(6)
|
||||
|
||||
def play_shader_7(self):
|
||||
self.play_this_shader(7)
|
||||
|
||||
def play_shader_8(self):
|
||||
self.play_this_shader(8)
|
||||
|
||||
def play_shader_9(self):
|
||||
self.play_this_shader(9)
|
||||
|
||||
def play_this_shader(self, number):
|
||||
self.shaders.play_this_shader(number)
|
||||
|
||||
def previous_shader_layer(self):
|
||||
self.data.update_shader_layer_by_amount(-1)
|
||||
|
||||
def next_shader_layer(self):
|
||||
self.data.update_shader_layer_by_amount(1)
|
||||
|
||||
def clear_shader_bank(self):
|
||||
self.data.clear_all_shader_slots()
|
||||
|
||||
def toggle_detour_record(self):
|
||||
if self.data.settings['detour']['TRY_DEMO']['value'] == 'enabled':
|
||||
is_recording = not self.data.detour_settings['is_recording']
|
||||
self.data.detour_settings['is_recording'] = is_recording
|
||||
self.video_driver.osc_client.send_message("/detour/is_recording", is_recording)
|
||||
|
||||
def toggle_detour_record_loop(self):
|
||||
if self.data.settings['detour']['TRY_DEMO']['value'] == 'enabled':
|
||||
record_loop = not self.data.detour_settings['record_loop']
|
||||
self.data.detour_settings['record_loop'] = record_loop
|
||||
self.video_driver.osc_client.send_message("/detour/record_loop", record_loop)
|
||||
|
||||
def clear_this_detour(self):
|
||||
if self.data.settings['detour']['TRY_DEMO']['value'] == 'enabled':
|
||||
self.video_driver.osc_client.send_message("/detour/clear_this_detour", True)
|
||||
|
||||
def increase_mix_shader(self):
|
||||
if self.data.settings['detour']['TRY_DEMO']['value'] == 'enabled':
|
||||
l = self.data.detour_mix_shaders
|
||||
self.data.detour_mix_shaders = l[1:] + l[:1]
|
||||
self.data.detour_settings['mix_shader'] = l[0]
|
||||
self.load_this_detour_shader()
|
||||
|
||||
def decrease_mix_shader(self):
|
||||
if self.data.settings['detour']['TRY_DEMO']['value'] == 'enabled':
|
||||
l = self.data.detour_mix_shaders
|
||||
self.data.detour_mix_shaders = l[-1:] + l[:-1]
|
||||
self.data.detour_settings['mix_shader'] = l[0]
|
||||
self.load_this_detour_shader()
|
||||
|
||||
def load_this_detour_shader(self):
|
||||
self.video_driver.osc_client.send_message("/detour/load_mix", "/home/pi/r_e_c_u_r/Shaders/2-input/" + self.data.detour_settings['mix_shader'])
|
||||
|
||||
def switch_to_detour_0(self):
|
||||
self.switch_to_this_detour(0)
|
||||
|
||||
def switch_to_detour_1(self):
|
||||
self.switch_to_this_detour(1)
|
||||
|
||||
def switch_to_detour_2(self):
|
||||
self.switch_to_this_detour(2)
|
||||
|
||||
def switch_to_detour_3(self):
|
||||
self.switch_to_this_detour(3)
|
||||
|
||||
def switch_to_this_detour(self, number):
|
||||
if self.data.settings['detour']['TRY_DEMO']['value'] == 'enabled':
|
||||
self.data.detour_settings['current_detour'] = number
|
||||
self.video_driver.osc_client.send_message("/detour/switch_to_detour_number", number)
|
||||
|
||||
|
||||
def set_detour_delay_mode(self, state):
|
||||
self.video_driver.osc_client.send_message("/detour/set_delay_mode", state == 'enabled')
|
||||
self.data.update_conjur_delay_mode(state == 'enabled')
|
||||
|
||||
def set_detour_speed_position_continuous(self, amount):
|
||||
self.video_driver.osc_client.send_message("/detour/set_speed_position", amount)
|
||||
|
||||
def set_detour_start_continuous(self, amount):
|
||||
self.video_driver.osc_client.send_message("/detour/set_start", amount)
|
||||
|
||||
def set_detour_end_continuous(self, amount):
|
||||
self.video_driver.osc_client.send_message("/detour/set_end", amount)
|
||||
|
||||
def set_detour_mix_continuous(self, amount):
|
||||
self.video_driver.osc_client.send_message("/detour/set_mix", amount)
|
||||
|
||||
def receive_detour_info(self, unused_addr, position, start, end, size, speed, mix, memory_full):
|
||||
self.data.detour_settings['detour_position'] = position
|
||||
self.data.detour_settings['detour_start'] = start
|
||||
self.data.detour_settings['detour_end'] = end
|
||||
self.data.detour_settings['detour_size'] = size
|
||||
self.data.detour_settings['detour_speed'] = round(speed, 2)
|
||||
self.data.detour_settings['detour_mix'] = round(mix, 4)
|
||||
self.data.detour_settings['memory_full'] = memory_full
|
||||
|
||||
def set_the_detour_mix_0(self):
|
||||
self.set_detour_mix_continuous(0)
|
||||
|
||||
def set_the_detour_mix_1(self):
|
||||
self.set_detour_mix_continuous(1)
|
||||
|
||||
def set_the_camera_colour_u_continuous(self, amount):
|
||||
self.capture.set_colour(amount*255, None)
|
||||
|
||||
def set_the_camera_colour_v_continuous(self, amount):
|
||||
self.capture.set_colour(None, amount*255)
|
||||
|
||||
def set_the_camera_alpha_continuous(self, amount):
|
||||
self.capture.set_alpha(amount*255)
|
||||
|
||||
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_continuous(self, amount):
|
||||
self.video_driver.next_player.set_alpha_value(amount*255)
|
||||
|
||||
def set_the_shader_param_0_layer_offset_0_continuous(self, amount):
|
||||
self.shaders.set_param_to_amount(0, amount, layer_offset=0)
|
||||
|
||||
def set_the_shader_param_1_layer_offset_0_continuous(self, amount):
|
||||
self.shaders.set_param_to_amount(1, amount, layer_offset=0)
|
||||
|
||||
def set_the_shader_param_2_layer_offset_0_continuous(self, amount):
|
||||
self.shaders.set_param_to_amount(2, amount, layer_offset=0)
|
||||
|
||||
def set_the_shader_param_3_layer_offset_0_continuous(self, amount):
|
||||
self.shaders.set_param_to_amount(3, amount, layer_offset=0)
|
||||
|
||||
def set_the_shader_param_0_layer_offset_1_continuous(self, amount):
|
||||
self.shaders.set_param_to_amount(0, amount, layer_offset=1)
|
||||
|
||||
def set_the_shader_param_1_layer_offset_1_continuous(self, amount):
|
||||
self.shaders.set_param_to_amount(1, amount, layer_offset=1)
|
||||
|
||||
def set_the_shader_param_2_layer_offset_1_continuous(self, amount):
|
||||
self.shaders.set_param_to_amount(2, amount, layer_offset=1)
|
||||
|
||||
def set_the_shader_param_3_layer_offset_1_continuous(self, amount):
|
||||
self.shaders.set_param_to_amount(3, amount, layer_offset=1)
|
||||
|
||||
def set_the_shader_param_0_layer_offset_2_continuous(self, amount):
|
||||
self.shaders.set_param_to_amount(0, amount, layer_offset=2)
|
||||
|
||||
def set_the_shader_param_1_layer_offset_2_continuous(self, amount):
|
||||
self.shaders.set_param_to_amount(1, amount, layer_offset=2)
|
||||
|
||||
def set_the_shader_param_2_layer_offset_2_continuous(self, amount):
|
||||
self.shaders.set_param_to_amount(2, amount, layer_offset=2)
|
||||
|
||||
def set_the_shader_param_3_layer_offset_2_continuous(self, amount):
|
||||
self.shaders.set_param_to_amount(3, amount, layer_offset=2)
|
||||
|
||||
def set_the_shader_param_0_layer_offset_3_continuous(self, amount):
|
||||
self.shaders.set_param_to_amount(0, amount, layer_offset=2)
|
||||
|
||||
def set_the_shader_param_1_layer_offset_3_continuous(self, amount):
|
||||
self.shaders.set_param_to_amount(1, amount, layer_offset=2)
|
||||
|
||||
def set_the_shader_param_2_layer_offset_3_continuous(self, amount):
|
||||
self.shaders.set_param_to_amount(2, amount, layer_offset=2)
|
||||
|
||||
def set_the_shader_param_3_layer_offset_3_continuous(self, amount):
|
||||
self.shaders.set_param_to_amount(3, amount, layer_offset=2)
|
||||
|
||||
def set_strobe_amount_continuous(self, amount):
|
||||
scaled_amount = int(amount * 10)
|
||||
if self.data.settings['shader']['STROBE_AMOUNT']['value'] != scaled_amount:
|
||||
print(scaled_amount)
|
||||
self.video_driver.osc_client.send_message("/set_strobe", scaled_amount)
|
||||
self.data.settings['shader']['STROBE_AMOUNT']['value'] = scaled_amount
|
||||
|
||||
def get_midi_status(self):
|
||||
self.message_handler.set_message('INFO', 'midi status is {}'.format(self.data.midi_status))
|
||||
|
||||
def cycle_midi_port_index(self):
|
||||
self.data.midi_port_index = self.data.midi_port_index + 1
|
||||
|
||||
def update_video_settings(self, setting_value):
|
||||
self.video_driver.update_video_settings()
|
||||
|
||||
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):
|
||||
### this seems no longer supported in the firmware...
|
||||
if setting_value == 'hdmi':
|
||||
self.change_hdmi_settings(setting_value)
|
||||
elif setting_value == 'composite':
|
||||
self.change_composite_setting(setting_value)
|
||||
self.restart_openframeworks()
|
||||
|
||||
def change_hdmi_settings(self, setting_value):
|
||||
if self.data.settings['video']['OUTPUT']['value'] == 'hdmi':
|
||||
if self.data.settings['video']['HDMI_MODE']['value'] == 'preferred':
|
||||
subprocess.call(['tvservice', '-p'])
|
||||
subprocess.call(['tvservice --preferred'], shell=True)
|
||||
elif self.data.settings['video']['HDMI_MODE']['value'] == 'CEA 4 HDMI':
|
||||
subprocess.call(['tvservice -e=\"CEA 4 HDMI\"'], shell=True)
|
||||
self._refresh_frame_buffer()
|
||||
elif self.data.settings['video']['HDMI_MODE']['value'] == 'CEA 17 HDMI':
|
||||
subprocess.call(['tvservice -e=\"CEA 17 HDMI\"'], shell=True)
|
||||
elif self.data.settings['video']['HDMI_MODE']['value'] == 'CEA 1 HDMI':
|
||||
subprocess.call(['tvservice -e=\"CEA 1 HDMI\"'], shell=True)
|
||||
self.refresh_frame_buffer_and_restart_openframeworks()
|
||||
|
||||
def check_and_set_output_mode_on_boot(self):
|
||||
#### checking if pi display mode is composite
|
||||
response = str(subprocess.check_output(['tvservice', '-s']))
|
||||
print('tvservice response is {}'.format(response))
|
||||
if 'PAL' in response or 'NTSC' in response:
|
||||
self.data.update_setting_value('video', 'OUTPUT', 'composite')
|
||||
else:
|
||||
self.data.update_setting_value('video', 'OUTPUT', 'hdmi')
|
||||
self.data.update_setting_value('video', 'HDMI_MODE', 'preferred')
|
||||
#### this is to work around a bug where 1080 videos on hdmi drop out ...
|
||||
#subprocess.call(['tvservice --sdtvon="PAL 4:3"'],shell=True)
|
||||
#self._refresh_frame_buffer()
|
||||
#subprocess.call(['tvservice', '-p'])
|
||||
#self._refresh_frame_buffer()
|
||||
|
||||
if self.data.settings['video']['HDMI_MODE']['value'] == "CEA 4 HDMI":
|
||||
|
||||
self.data.update_setting_value('video', 'HDMI_MODE', 'CEA 4 HDMI')
|
||||
|
||||
self.change_hdmi_settings('CEA 4 HDMI')
|
||||
|
||||
|
||||
def check_dev_mode(self):
|
||||
#### check if in dev mode:(ie not using the lcd screen)
|
||||
with open('/boot/config.txt', 'r') as config:
|
||||
if '##no_waveshare_overlay' in config.read():
|
||||
print('it is in !')
|
||||
self.data.update_setting_value('other','DEV_MODE_RESET', 'on')
|
||||
self.data.update_setting_value('system','DEV_MODE_RESET', 'on')
|
||||
else:
|
||||
print('it is not in !')
|
||||
self.data.update_setting_value('other','DEV_MODE_RESET', 'off')
|
||||
self.data.update_setting_value('system','DEV_MODE_RESET', 'off')
|
||||
|
||||
def check_if_should_start_openframeworks(self):
|
||||
if self.data.settings['video']['VIDEOPLAYER_BACKEND']['value'] != 'omxplayer':
|
||||
self.openframeworks_process = subprocess.Popen([self.data.PATH_TO_OPENFRAMEWORKS +'apps/myApps/c_o_n_j_u_r/bin/c_o_n_j_u_r'])
|
||||
print('conjur pid is {}'.format(self.openframeworks_process.pid))
|
||||
|
||||
def exit_openframeworks(self):
|
||||
self.video_driver.osc_client.send_message("/exit", True)
|
||||
|
||||
def switch_conjur_player_type(self, value):
|
||||
self.data.update_conjur_player_type(value)
|
||||
self.restart_openframeworks()
|
||||
|
||||
def toggle_of_screen_size(self, value):
|
||||
self.data.update_conjur_dev_mode(value)
|
||||
self.video_driver.osc_client.send_message("/dev_mode", True)
|
||||
|
||||
def switch_video_backend(self, state):
|
||||
if state == 'ofvideoplayer' or state == 'ofxomxplayer':
|
||||
self.switch_conjur_player_type(state)
|
||||
elif state == 'omxplayer':
|
||||
self.data.update_setting_value('sampler', 'LOOP_TYPE', 'seamless')
|
||||
self.exit_openframeworks()
|
||||
self.set_capture_object('nothing')
|
||||
self.display.settings_menu.generate_settings_list()
|
||||
self.reset_players()
|
||||
|
||||
def reset_players(self):
|
||||
self.video_driver.reset_all_players()
|
||||
|
||||
def change_composite_setting(self, setting_value):
|
||||
if setting_value == 'composite':
|
||||
mode = self.data.settings['video']['COMPOSITE_TYPE']['value']
|
||||
aspect = self.data.settings['video']['COMPOSITE_RATIO']['value']
|
||||
progressive = ''
|
||||
if self.data.settings['video']['COMPOSITE_PROGRESSIVE']['value'] == 'on':
|
||||
progressive = 'p'
|
||||
output = self.data.settings['video']['OUTPUT']['value']
|
||||
mode = self.data.settings['video']['COMPOSITE_TYPE']['value']
|
||||
aspect = self.data.settings['video']['COMPOSITE_RATIO']['value']
|
||||
progressive = ''
|
||||
if self.data.settings['video']['COMPOSITE_PROGRESSIVE']['value'] == 'on':
|
||||
progressive = 'p'
|
||||
|
||||
if output == 'composite':
|
||||
subprocess.call(['tvservice --sdtvon="{} {} {}"'.format(mode, aspect, progressive)],shell=True)
|
||||
self._refresh_frame_buffer()
|
||||
self.persist_composite_setting(mode, progressive, aspect)
|
||||
self.refresh_frame_buffer_and_restart_openframeworks()
|
||||
self.persist_composite_setting(mode, progressive, aspect)
|
||||
|
||||
@staticmethod
|
||||
def _refresh_frame_buffer():
|
||||
|
||||
def _refresh_frame_buffer(self):
|
||||
self.data.open_omxplayer_for_reset()
|
||||
subprocess.run(["fbset -depth 16; fbset -depth 32; xrefresh -display :0" ], shell=True)
|
||||
|
||||
def persist_composite_setting(self, mode, progressive, aspect):
|
||||
@@ -313,8 +638,12 @@ class Actions(object):
|
||||
|
||||
def switch_dev_mode(self, state):
|
||||
if state == 'on':
|
||||
self.toggle_of_screen_size('dev')
|
||||
self.video_driver.osc_client.send_message("/dev_mode", True)
|
||||
self.switch_display_to_hdmi()
|
||||
elif state == 'off':
|
||||
self.toggle_of_screen_size('full')
|
||||
self.video_driver.osc_client.send_message("/dev_mode", True)
|
||||
self.switch_display_to_lcd()
|
||||
|
||||
def switch_display_to_hdmi(self):
|
||||
@@ -330,6 +659,7 @@ class Actions(object):
|
||||
with open('/boot/config.txt', 'r') as config:
|
||||
with open('/usr/share/X11/xorg.conf.d/99-fbturbo.conf') as framebuffer_conf:
|
||||
if '##no_waveshare_overlay' in config.read() and 'dev/fb0' in framebuffer_conf.read():
|
||||
print('running the switch script')
|
||||
self.run_script('switch_display_to_lcd')
|
||||
else:
|
||||
self.message_handler.set_message('INFO', 'failed to switch display')
|
||||
@@ -349,6 +679,10 @@ class Actions(object):
|
||||
|
||||
def quit_the_program(self):
|
||||
self.video_driver.exit_all_players()
|
||||
self.exit_openframeworks()
|
||||
self.exit_osc_server('','')
|
||||
self.stop_serial_port_process()
|
||||
self.stop_openframeworks_process()
|
||||
self.toggle_x_autorepeat()
|
||||
self.tk.destroy()
|
||||
|
||||
@@ -356,6 +690,35 @@ class Actions(object):
|
||||
self.quit_the_program()
|
||||
os.execv('/usr/bin/python3', [sys.argv[0],'/home/pi/r_e_c_u_r/r_e_c_u_r.py'])
|
||||
|
||||
def set_shader_param_mode(self):
|
||||
self.data.control_mode = 'SHADER_PARAM'
|
||||
self.message_handler.set_message('INFO', '[ ]: focus < >: level ■: back')
|
||||
self.shaders.focused_param = 0
|
||||
|
||||
def increase_this_param(self):
|
||||
self.shaders.increase_this_param(self.data.settings['shader']['SHADER_PARAM']['value'])
|
||||
|
||||
def decrease_this_param(self):
|
||||
self.shaders.decrease_this_param(self.data.settings['shader']['SHADER_PARAM']['value'])
|
||||
|
||||
def increase_param_focus(self):
|
||||
self.shaders.focused_param = (self.shaders.focused_param + 1)%self.shaders.selected_shader_list[self.data.shader_layer]['param_number']
|
||||
|
||||
def decrease_param_focus(self):
|
||||
self.shaders.focused_param = (self.shaders.focused_param - 1)%self.shaders.selected_shader_list[self.data.shader_layer]['param_number']
|
||||
|
||||
def increase_shader_param(self):
|
||||
options = self.data.settings['shader']['SHADER_PARAM']['options']
|
||||
current_index = [index for index, item in enumerate(options) if item == self.data.settings['shader']['SHADER_PARAM']['value'] ][0]
|
||||
self.data.settings['shader']['SHADER_PARAM']['value'] = options[(current_index + 1) % len(options) ]
|
||||
self.message_handler.set_message('INFO', 'The Param amountis now ' + str(self.data.settings['shader']['SHADER_PARAM']['value']))
|
||||
|
||||
def decrease_shader_param(self):
|
||||
options = self.data.settings['shader']['SHADER_PARAM']['options']
|
||||
current_index = [index for index, item in enumerate(options) if item == self.data.settings['shader']['SHADER_PARAM']['value'] ][0]
|
||||
self.data.settings['shader']['SHADER_PARAM']['value'] = options[(current_index - 1) % len(options) ]
|
||||
self.message_handler.set_message('INFO', 'The Param amountis now ' + str(self.data.settings['shader']['SHADER_PARAM']['value']))
|
||||
|
||||
|
||||
def set_fixed_length(self, value):
|
||||
self.data.control_mode = 'LENGTH_SET'
|
||||
@@ -364,9 +727,33 @@ class Actions(object):
|
||||
|
||||
|
||||
def return_to_default_control_mode(self):
|
||||
if self.data.control_mode == 'LENGTH_SET':
|
||||
pass
|
||||
self.data.control_mode = 'NAV_SETTINGS'
|
||||
display_list = self.data.get_display_modes_list(with_nav_mode=True)
|
||||
for display, control in display_list:
|
||||
if display == self.data.display_mode:
|
||||
self.data.control_mode = control
|
||||
|
||||
def perform_confirm_action(self):
|
||||
action = self.data.confirm_action
|
||||
if action:
|
||||
getattr(self, action)()
|
||||
self.data.confirm_action = None
|
||||
|
||||
def start_confirm_action(self, action_title, message=None):
|
||||
if not message:
|
||||
message = action_title
|
||||
self.data.confirm_action = action_title
|
||||
self.data.control_mode = 'CONFIRM'
|
||||
self.message_handler.set_message('INFO', 'confirm: {} ■:y < >:no'.format(action_title[:22]))
|
||||
|
||||
def confirm_shutdown(self):
|
||||
self.start_confirm_action('shutdown_pi' )
|
||||
|
||||
def confirm_quit(self):
|
||||
self.start_confirm_action('quit_the_program', message='quit' )
|
||||
|
||||
def confirm_switch_dev_mode(self, state):
|
||||
# i startd writing a confirm dev mod but it messed with the state if you say no ...
|
||||
self.start_confirm_action('switch_dev_mode', args=[state])
|
||||
|
||||
def record_fixed_length(self):
|
||||
if self.fixed_length_setter:
|
||||
@@ -374,6 +761,111 @@ class Actions(object):
|
||||
self.display.settings_menu.generate_settings_list()
|
||||
|
||||
|
||||
def setup_osc_server(self):
|
||||
server_parser = argparse.ArgumentParser()
|
||||
server_parser.add_argument("--ip", default="127.0.0.1", help="the ip")
|
||||
server_parser.add_argument("--port", type=int, default=9000, help="the port")
|
||||
|
||||
server_args = server_parser.parse_args()
|
||||
|
||||
this_dispatcher = dispatcher.Dispatcher()
|
||||
this_dispatcher.map("/player/a/position", self.video_driver.receive_position, "a.a")
|
||||
this_dispatcher.map("/player/b/position", self.video_driver.receive_position, "b.b")
|
||||
this_dispatcher.map("/player/c/position", self.video_driver.receive_position, "c.c")
|
||||
this_dispatcher.map("/player/a/status", self.video_driver.receive_status, "a.a")
|
||||
this_dispatcher.map("/player/b/status", self.video_driver.receive_status, "b.b")
|
||||
this_dispatcher.map("/player/c/status", self.video_driver.receive_status, "c.c")
|
||||
this_dispatcher.map("/detour/detour_info", self.receive_detour_info)
|
||||
this_dispatcher.map("/capture/recording_finished", self.capture.receive_recording_finished)
|
||||
this_dispatcher.map("/shutdown", self.exit_osc_server)
|
||||
#this_dispatcher.map("/player/a/status", self.set_status)
|
||||
|
||||
server = osc_server.ThreadingOSCUDPServer((server_args.ip, server_args.port), this_dispatcher)
|
||||
server_thread = threading.Thread(target=server.serve_forever)
|
||||
server_thread.start()
|
||||
return server
|
||||
|
||||
def exit_osc_server(self, unused_addr, args):
|
||||
self.server.shutdown()
|
||||
|
||||
def create_serial_port_process(self):
|
||||
if self.serial_port_process == None:
|
||||
self.serial_port_process = subprocess.Popen("exec " + "ttymidi -s /dev/serial0 -b 38400 -n serial", shell=True)
|
||||
print('created the serial port process ? {}'.format(self.serial_port_process))
|
||||
|
||||
def stop_serial_port_process(self):
|
||||
if self.serial_port_process is not None:
|
||||
self.serial_port_process.kill()
|
||||
self.serial_port_process = None
|
||||
|
||||
def restart_openframeworks(self):
|
||||
self.reset_players()
|
||||
self.exit_openframeworks()
|
||||
self.stop_openframeworks_process()
|
||||
self.check_if_should_start_openframeworks()
|
||||
|
||||
def refresh_frame_buffer_and_restart_openframeworks(self):
|
||||
if self.data.settings['video']['VIDEOPLAYER_BACKEND']['value'] != 'omxplayer':
|
||||
self.exit_openframeworks()
|
||||
self.reset_players()
|
||||
self.stop_openframeworks_process()
|
||||
self._refresh_frame_buffer()
|
||||
self.check_if_should_start_openframeworks()
|
||||
#self.tk.after(1000, self.check_if_should_start_openframeworks)
|
||||
else:
|
||||
self._refresh_frame_buffer()
|
||||
|
||||
def stop_openframeworks_process(self):
|
||||
if self.openframeworks_process is not None:
|
||||
print('killing process')
|
||||
self.openframeworks_process.kill()
|
||||
self.openframeworks_process = None
|
||||
subprocess.call(['killall', 'c_o_n_j_u_r'])
|
||||
|
||||
def try_pull_code_and_reset(self):
|
||||
#self.message_handler.set_message('INFO', 'checkin fo updates pls wait')
|
||||
recur_repo = git.Repo("~/r_e_c_u_r")
|
||||
conjur_repo = git.Repo(self.data.PATH_TO_OPENFRAMEWORKS + "apps/myApps/c_o_n_j_u_r")
|
||||
ofxVideoArtTools_repo = git.Repo(self.data.PATH_TO_OPENFRAMEWORKS + "/addons/ofxVideoArtTools")
|
||||
current_recur_hash = recur_repo.head.object.hexsha
|
||||
current_conjur_hash = conjur_repo.head.object.hexsha
|
||||
current_ofxVideoArtTools_hash = ofxVideoArtTools_repo.head.object.hexsha
|
||||
os.remove('/home/pi/r_e_c_u_r/json_objects/settings.json')
|
||||
os.remove(self.data.PATH_TO_DATA_OBJECTS + self.data.SETTINGS_JSON )
|
||||
try:
|
||||
recur_repo.remotes.origin.pull()
|
||||
conjur_repo.remotes.origin.pull()
|
||||
ofxVideoArtTools_repo.remotes.origin.pull()
|
||||
except git.exc.GitCommandError as e:
|
||||
if 'unable to access' in str(e):
|
||||
self.message_handler.set_message('INFO', 'not connected to network')
|
||||
else:
|
||||
if hasattr(e, 'message'):
|
||||
error_info = e.message
|
||||
else:
|
||||
error_info = e
|
||||
self.message_handler.set_message('ERROR',error_info)
|
||||
return
|
||||
|
||||
new_recur_hash = recur_repo.head.object.hexsha
|
||||
new_conjur_hash = conjur_repo.head.object.hexsha
|
||||
new_ofxVideoArtTools_hash = ofxVideoArtTools_repo.head.object.hexsha
|
||||
if current_recur_hash != new_recur_hash or current_conjur_hash != new_conjur_hash or current_ofxVideoArtTools_hash != new_ofxVideoArtTools_hash :
|
||||
#something has changed!
|
||||
self.restart_the_program()
|
||||
else:
|
||||
self.message_handler.set_message('INFO', 'up to date !')
|
||||
|
||||
# def complie_openframeworks(self):
|
||||
# subprocess.call(['make', '--directory=' + self.data.PATH_TO_OPENFRAMEWORKS + 'apps/myApps/c_o_n_j_u_r' ])
|
||||
# self.message_handler.set_message('INFO', 'finished compiling!')
|
||||
# self.restart_the_program()
|
||||
|
||||
def shutdown_pi(self):
|
||||
subprocess.call(['sudo', 'shutdown', '-h', 'now'])
|
||||
|
||||
def clear_message(self):
|
||||
self.message_handler.clear_all_messages()
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -1,30 +1,37 @@
|
||||
import json
|
||||
import xml.etree.ElementTree as ET
|
||||
import os
|
||||
import collections
|
||||
from random import randint
|
||||
import inspect
|
||||
from itertools import cycle
|
||||
from omxplayer.player import OMXPlayer
|
||||
|
||||
from shutil import copyfile
|
||||
|
||||
|
||||
|
||||
class Data(object):
|
||||
|
||||
BANK_DATA_JSON = 'display_data.json'
|
||||
NEXT_BANKSLOT_JSON = 'next_bankslot_number.json'
|
||||
SHADER_BANK_DATA_JSON = 'shader_bank_data.json'
|
||||
SETTINGS_JSON = 'settings.json'
|
||||
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'
|
||||
PATH_TO_OPENFRAMEWORKS = '/home/pi/openframeworks10.1/'
|
||||
PATH_TO_CONJUR_DATA = PATH_TO_OPENFRAMEWORKS + 'apps/myApps/c_o_n_j_u_r/bin/data/settings.xml'
|
||||
PATH_TO_DEFAULT_CONJUR_DATA = PATH_TO_OPENFRAMEWORKS + 'apps/myApps/c_o_n_j_u_r/bin/data/settings_default.xml'
|
||||
|
||||
def __init__(self, message_handler):
|
||||
self.message_handler = message_handler
|
||||
|
||||
#self.EMPTY_BANK = [self.EMPTY_SLOT for i in range(10)]
|
||||
self.PATHS_TO_BROWSER = [self.PATH_TO_EXTERNAL_DEVICES, '/home/pi/Videos' ]
|
||||
self.PATHS_TO_SHADERS = [self.PATH_TO_EXTERNAL_DEVICES, '/home/pi/r_e_c_u_r/Shaders', '/home/pi/Shaders' ]
|
||||
|
||||
### state data
|
||||
self.auto_repeat_on = True
|
||||
@@ -33,29 +40,54 @@ class Data(object):
|
||||
self.control_mode = 'PLAYER'
|
||||
self.bank_number = 0
|
||||
self.midi_status = 'disconnected'
|
||||
self.midi_port_index = 0
|
||||
self.update_screen = True
|
||||
self.confirm_action = None
|
||||
self.player_mode = 'now'
|
||||
|
||||
self.feedback_active = False
|
||||
self.detour_active = False
|
||||
self.detour_mix_shaders = self.get_list_of_two_input_shaders()
|
||||
self.detour_settings = collections.OrderedDict([('current_detour',0), ('is_playing', False), ('is_recording', False), ('record_loop', False), ('detour_size', False), ('detour_speed', 0), ('memory_full', False), ('mix_shader', self.detour_mix_shaders[0]), ('detour_position', 5), ('detour_start', 0), ('detour_end', 0), ('detour_mix', 0), ('is_delay', False)])
|
||||
|
||||
self.next_bankslot = '0-0'
|
||||
self.current_bankslot = '0-0'
|
||||
|
||||
self.shader_layer = 0
|
||||
|
||||
### persisted data (use default if doesnt exits):
|
||||
if not os.path.isfile(self.PATH_TO_CONJUR_DATA):
|
||||
os.remove(self.PATH_TO_DATA_OBJECTS + self.SETTINGS_JSON ) # keep the, in sync
|
||||
copyfile(self.PATH_TO_DEFAULT_CONJUR_DATA, self.PATH_TO_CONJUR_DATA)
|
||||
|
||||
self.bank_data = [self.create_empty_bank()]
|
||||
if os.path.isfile(self.PATH_TO_DATA_OBJECTS + self.BANK_DATA_JSON):
|
||||
self.bank_data = self._read_json(self.BANK_DATA_JSON)
|
||||
|
||||
self.next_bankslot = '0-0'
|
||||
if os.path.isfile(self.PATH_TO_DATA_OBJECTS + self.NEXT_BANKSLOT_JSON):
|
||||
self.next_bankslot = self._read_json(self.NEXT_BANKSLOT_JSON)
|
||||
|
||||
self.shader_bank_data = [self.create_empty_shader_bank() for i in range(3)]
|
||||
if os.path.isfile(self.PATH_TO_DATA_OBJECTS + self.SHADER_BANK_DATA_JSON):
|
||||
self.shader_bank_data = self._read_json(self.SHADER_BANK_DATA_JSON)
|
||||
self.settings = self._read_json(self.DEFAULT_SETTINGS_JSON)
|
||||
|
||||
if os.path.isfile(self.PATH_TO_DATA_OBJECTS + self.SETTINGS_JSON):
|
||||
self.settings = self._read_json(self.SETTINGS_JSON)
|
||||
|
||||
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():
|
||||
empty_slot = dict(name='', location='', length=-1, start=-1, end=-1, rate=1)
|
||||
return [empty_slot for i in range(10)]
|
||||
|
||||
@staticmethod
|
||||
def create_empty_shader_bank():
|
||||
empty_slot = dict(name='', path='', param_number=4, shad_type='-')
|
||||
return [empty_slot for i in range(10)]
|
||||
|
||||
def _read_json(self, file_name):
|
||||
with open(self.PATH_TO_DATA_OBJECTS + file_name) as data_file:
|
||||
@@ -65,6 +97,25 @@ class Data(object):
|
||||
def _update_json(self, file_name, data):
|
||||
with open('{}{}'.format(self.PATH_TO_DATA_OBJECTS, file_name), 'w') as data_file:
|
||||
json.dump(data, data_file, indent=4, sort_keys=True)
|
||||
|
||||
def update_conjur_dev_mode(self, value):
|
||||
print(value)
|
||||
tree = ET.parse(self.PATH_TO_CONJUR_DATA)
|
||||
tag = tree.find("isDevMode")
|
||||
tag.text = str(int(value == 'dev'))
|
||||
tree.write(self.PATH_TO_CONJUR_DATA)
|
||||
|
||||
def update_conjur_player_type(self, value):
|
||||
tree = ET.parse(self.PATH_TO_CONJUR_DATA)
|
||||
tag = tree.find("playerType")
|
||||
tag.text = str(value)
|
||||
tree.write(self.PATH_TO_CONJUR_DATA)
|
||||
|
||||
def update_conjur_delay_mode(self, value):
|
||||
tree = ET.parse(self.PATH_TO_CONJUR_DATA)
|
||||
tag = tree.find("delayMode")
|
||||
tag.text = str(int(value))
|
||||
tree.write(self.PATH_TO_CONJUR_DATA)
|
||||
|
||||
def get_setting_and_folder_from_name(self, setting_name):
|
||||
for folder_key , folder_item in self.settings.items():
|
||||
@@ -72,6 +123,8 @@ class Data(object):
|
||||
if setting_key == setting_name:
|
||||
return folder_key, setting_key, setting_item
|
||||
|
||||
##### setting and adding to sample mapping ######
|
||||
|
||||
def create_new_slot_mapping_in_first_open(self, file_name):
|
||||
######## used for mapping current video to next available slot ########
|
||||
for index, slot in enumerate(self.bank_data[self.bank_number]):
|
||||
@@ -103,18 +156,47 @@ class Data(object):
|
||||
self._update_json(self.BANK_DATA_JSON, self.bank_data)
|
||||
self.bank_number = (self.bank_number+amount)%(len(self.bank_data))
|
||||
|
||||
def update_next_slot_number(self, new_value):
|
||||
def update_next_slot_number(self, new_value, is_current=False):
|
||||
if self.bank_data[self.bank_number][new_value]['location'] == '':
|
||||
self.message_handler.set_message('INFO', 'the slot you pressed is empty')
|
||||
return False
|
||||
elif self.is_this_path_broken(self.bank_data[self.bank_number][new_value]['location']):
|
||||
self.message_handler.set_message('INFO', 'no device found for this slot')
|
||||
return False
|
||||
elif is_current:
|
||||
self.current_bankslot = '{}-{}'.format(self.bank_number,new_value)
|
||||
return True
|
||||
else:
|
||||
self.next_bankslot = '{}-{}'.format(self.bank_number,new_value)
|
||||
self._update_json(self.NEXT_BANKSLOT_JSON,self.next_bankslot)
|
||||
return True
|
||||
|
||||
######## setting and adding to shader mapping
|
||||
|
||||
|
||||
def create_new_shader_mapping_in_first_open(self, file_name):
|
||||
######## used for mapping current shader to next available slot ########
|
||||
for index, slot in enumerate(self.shader_bank_data[self.shader_layer]):
|
||||
if (not slot['name']):
|
||||
self.create_new_shader_mapping(index, file_name)
|
||||
return True
|
||||
return False
|
||||
|
||||
def create_new_shader_mapping(self, slot_number, file_name):
|
||||
######## used for mapping current shader to a specific slot ########
|
||||
has_location, location = self._get_path_for_file(file_name)
|
||||
print('file_name:{},has_location:{}, location:{}'.format(file_name,has_location, location))
|
||||
new_slot = dict(name=file_name, path=location, shad_type='-', param_number=4)
|
||||
self._update_a_shader_slots_data(slot_number, new_slot)
|
||||
|
||||
def clear_all_shader_slots(self):
|
||||
self.shader_bank_data[self.shader_layer] = self.create_empty_shader_bank()
|
||||
self._update_json(self.SHADER_BANK_DATA_JSON, self.shader_bank_data)
|
||||
|
||||
def update_shader_layer_by_amount(self, amount):
|
||||
self.shader_layer = (self.shader_layer + amount) % len(self.shader_bank_data)
|
||||
|
||||
|
||||
|
||||
def update_setting_value(self, setting_folder, setting_name, setting_value):
|
||||
self.settings[setting_folder][setting_name]['value'] = setting_value
|
||||
self._update_json(self.SETTINGS_JSON, self.settings)
|
||||
@@ -134,9 +216,14 @@ class Data(object):
|
||||
except ValueError:
|
||||
return False , '*'
|
||||
|
||||
def get_next_context(self):
|
||||
def get_next_context(self, is_current=False):
|
||||
######## loads the slot details, uses settings to modify them and then set next slot number ########
|
||||
bank_num , slot_num = self.split_bankslot_number(self.next_bankslot)
|
||||
if is_current:
|
||||
bankslot_number = self.current_bankslot
|
||||
else:
|
||||
bankslot_number = self.next_bankslot
|
||||
bank_num , slot_num = self.split_bankslot_number(bankslot_number)
|
||||
|
||||
next_slot_details = self.bank_data[bank_num][slot_num]
|
||||
start_value = next_slot_details['start']
|
||||
end_value = next_slot_details['end']
|
||||
@@ -146,9 +233,9 @@ class Data(object):
|
||||
|
||||
context = dict(location=next_slot_details['location'], name=next_slot_details['name'],
|
||||
length=next_slot_details['length'], rate=next_slot_details['rate'], start=start_value, end=end_value,
|
||||
bankslot_number=self.next_bankslot)
|
||||
bankslot_number=bankslot_number)
|
||||
|
||||
self._update_next_bankslot_value(slot_num)
|
||||
self._update_next_bankslot_value(slot_num, is_current)
|
||||
return context
|
||||
|
||||
def _overwrite_values_with_sampler_settings(self, start, end, length):
|
||||
@@ -178,7 +265,7 @@ class Data(object):
|
||||
|
||||
return new_start, new_end
|
||||
|
||||
def _update_next_bankslot_value(self, slot_num):
|
||||
def _update_next_bankslot_value(self, slot_num, is_current=False):
|
||||
next_setting = self.settings['sampler']['LOAD_NEXT']['value']
|
||||
loaded_slots = self._get_list_of_loaded_slots_in_current_bank()
|
||||
if loaded_slots:
|
||||
@@ -189,9 +276,11 @@ class Data(object):
|
||||
else:
|
||||
next_slot = slot_num
|
||||
|
||||
self.next_bankslot = '{}-{}'.format(self.bank_number,next_slot)
|
||||
self._update_json(self.NEXT_BANKSLOT_JSON,self.next_bankslot)
|
||||
|
||||
if is_current:
|
||||
self.current_bankslot = '{}-{}'.format(self.bank_number,next_slot)
|
||||
else:
|
||||
self.next_bankslot = '{}-{}'.format(self.bank_number,next_slot)
|
||||
|
||||
def _get_list_of_loaded_slots_in_current_bank(self):
|
||||
list_of_loaded_slots = []
|
||||
for index, slot in enumerate(self.bank_data[self.bank_number]):
|
||||
@@ -218,7 +307,10 @@ class Data(object):
|
||||
self.bank_data[self.bank_number][slot_number]['rate'] = rate
|
||||
self._update_json(self.BANK_DATA_JSON, self.bank_data)
|
||||
|
||||
def _get_length_for_file(self, path):
|
||||
def open_omxplayer_for_reset(self):
|
||||
self._get_length_for_file('/ss',no_message=True )
|
||||
|
||||
def _get_length_for_file(self, path, no_message=False):
|
||||
try:
|
||||
temp_player = OMXPlayer(path, args=['--alpha', '0'], dbus_name='t.t')
|
||||
duration = temp_player.duration()
|
||||
@@ -226,13 +318,14 @@ class Data(object):
|
||||
return duration
|
||||
except Exception as e:
|
||||
print (e)
|
||||
self.message_handler.set_message('INFO', 'cannot load video')
|
||||
if not no_message:
|
||||
self.message_handler.set_message('INFO', 'cannot load video')
|
||||
return None
|
||||
|
||||
|
||||
def _get_path_for_file(self, file_name):
|
||||
######## returns full path for a given file name ########
|
||||
for path in self.PATHS_TO_BROWSER:
|
||||
for path in self.PATHS_TO_BROWSER + self.PATHS_TO_SHADERS:
|
||||
for root, dirs, files in os.walk(path):
|
||||
if file_name in files:
|
||||
return True, '{}/{}'.format(root, file_name)
|
||||
@@ -265,9 +358,20 @@ class Data(object):
|
||||
elif colour_name == "none":
|
||||
colour_argb = (0,0,0,0)
|
||||
colour_hex = '%02x%02x%02x%02x' % colour_argb
|
||||
print(colour_hex)
|
||||
return colour_hex
|
||||
|
||||
def get_display_modes_list(self, with_nav_mode=False):
|
||||
display_modes = [[ "SAMPLER",'PLAYER'], ["BROWSER",'NAV_BROWSER'],["SETTINGS",'NAV_SETTINGS']]
|
||||
if self.settings['video']['VIDEOPLAYER_BACKEND']['value'] != 'omxplayer' and self.settings['shader']['USE_SHADER']['value'] == 'enabled':
|
||||
display_modes.append(["SHADERS",'NAV_SHADERS'])
|
||||
if self.settings['shader']['USE_SHADER_BANK']['value'] == 'enabled' and ["SHADERS",'NAV_SHADERS'] in display_modes:
|
||||
display_modes.append(["SHDR_BNK",'PLAY_SHADER'])
|
||||
if self.settings['detour']['TRY_DEMO']['value'] == 'enabled':
|
||||
display_modes.append(["FRAMES",'NAV_DETOUR'])
|
||||
if not with_nav_mode:
|
||||
return [mode[0] for mode in display_modes]
|
||||
return display_modes
|
||||
|
||||
@staticmethod
|
||||
def _get_mb_free_diskspace(path):
|
||||
st = os.statvfs(path)
|
||||
@@ -277,6 +381,11 @@ class Data(object):
|
||||
######## overwrite a given slots info with new data ########
|
||||
self.bank_data[self.bank_number][slot_number] = slot_info
|
||||
self._update_json(self.BANK_DATA_JSON, self.bank_data)
|
||||
|
||||
def _update_a_shader_slots_data(self, slot_number, slot_info):
|
||||
######## overwrite a given slots info with new data ########
|
||||
self.shader_bank_data[self.shader_layer][slot_number] = slot_info
|
||||
self._update_json(self.SHADER_BANK_DATA_JSON, self.shader_bank_data)
|
||||
|
||||
@staticmethod
|
||||
def make_empty_if_none(input):
|
||||
@@ -285,4 +394,14 @@ class Data(object):
|
||||
else:
|
||||
return input
|
||||
|
||||
|
||||
@staticmethod
|
||||
def get_list_of_two_input_shaders():
|
||||
if os.path.exists('/home/pi/r_e_c_u_r/Shaders/2-input'):
|
||||
(_, _, filenames) = next(os.walk('/home/pi/r_e_c_u_r/Shaders/2-input'))
|
||||
return filenames
|
||||
#elif os.path.exists('/home/pi/r_e_c_u_r/Shaders/2-input'):
|
||||
#(_, _, filenames) = next(os.walk('/home/pi/r_e_c_u_r/Shaders/2-input'))
|
||||
#return filenames
|
||||
else:
|
||||
return []
|
||||
|
||||
|
||||
@@ -7,20 +7,20 @@ class Display(object):
|
||||
MENU_HEIGHT = 10
|
||||
SELECTOR_WIDTH = 0.47
|
||||
ROW_OFFSET = 6.0
|
||||
TITLE = '================== r_e_c_u_r =================='
|
||||
TITLES = ['{0} r_e_c_u_r {0}'.format('='*18), '{0} c_o_n_j_u_r {1}'.format('='*18, '='*16), '{0} d_e_t_o_u_r {1}'.format('='*18, '='*16)]
|
||||
|
||||
def __init__(self, tk, video_driver, capture, message_handler, data):
|
||||
def __init__(self, tk, video_driver, shaders, message_handler, data):
|
||||
self.tk = tk
|
||||
self.video_driver = video_driver
|
||||
self.capture = capture
|
||||
self.capture = None
|
||||
self.shaders = shaders
|
||||
self.message_handler = message_handler
|
||||
self.data = data
|
||||
self.browser_menu = menu.BrowserMenu(self.data, self.message_handler, self.MENU_HEIGHT)
|
||||
self.settings_menu = menu.SettingsMenu(self.data, self.message_handler, self.MENU_HEIGHT)
|
||||
self.shaders_menu = self.shaders.shaders_menu
|
||||
|
||||
#self.top_menu_index = 0
|
||||
#self.selected_list_index = self.top_menu_index
|
||||
|
||||
self.body_title = ''
|
||||
self.display_text = self._create_display_text(self.tk)
|
||||
self._add_tags()
|
||||
self._update_screen_every_second()
|
||||
@@ -38,8 +38,10 @@ class Display(object):
|
||||
self.display_text.tag_configure("NOW_PLAYER_INFO", background="black", foreground="yellow")
|
||||
self.display_text.tag_configure("NEXT_PLAYER_INFO", background="black", foreground="cyan")
|
||||
self.display_text.tag_configure("COLUMN_NAME", background="black", foreground="VioletRed1")
|
||||
self.display_text.tag_configure("SHADER_PARAM", background="VioletRed1", foreground="black")
|
||||
self.display_text.tag_configure("FUNCTION", background="yellow", foreground="black")
|
||||
self.display_text.tag_configure("BROKEN_PATH", background="black", foreground="gray")
|
||||
self.display_text.tag_configure("ZEBRA_STRIPE", background="black", foreground="khaki")
|
||||
|
||||
def _load_display(self):
|
||||
self._load_title()
|
||||
@@ -50,8 +52,15 @@ class Display(object):
|
||||
self.display_text.pack()
|
||||
|
||||
def _load_title(self):
|
||||
self.display_text.insert(END, self.TITLE + ' \n')
|
||||
self.display_text.tag_add("TITLE", 1.19, 1.28)
|
||||
if self.data.display_mode == 'SHADERS' or self.data.display_mode == 'SHDR_BNK':
|
||||
self.display_text.insert(END, self.TITLES[1] + ' \n')
|
||||
self.display_text.tag_add("TITLE", 1.19, 1.31)
|
||||
elif self.data.display_mode == 'FRAMES':
|
||||
self.display_text.insert(END, self.TITLES[2] + ' \n')
|
||||
self.display_text.tag_add("TITLE", 1.19, 1.31)
|
||||
else:
|
||||
self.display_text.insert(END, self.TITLES[0] + ' \n')
|
||||
self.display_text.tag_add("TITLE", 1.19, 1.28)
|
||||
|
||||
def _load_player(self):
|
||||
if self.data.player_mode == 'now':
|
||||
@@ -70,38 +79,55 @@ class Display(object):
|
||||
self.display_text.tag_add("NEXT_ALPHA", 3.29, 3.47)
|
||||
|
||||
def _load_display_body(self):
|
||||
self.body_title = self._generate_body_title()
|
||||
if self.data.display_mode == 'BROWSER':
|
||||
self._load_browser()
|
||||
elif self.data.display_mode == 'SETTINGS':
|
||||
self._load_settings()
|
||||
else:
|
||||
elif self.data.display_mode == 'SAMPLER':
|
||||
self._load_sampler()
|
||||
elif self.data.display_mode == 'SHADERS':
|
||||
self._load_shaders()
|
||||
elif self.data.display_mode == 'SHDR_BNK':
|
||||
self._load_shader_bank()
|
||||
elif self.data.display_mode == 'FRAMES':
|
||||
self._load_detour()
|
||||
self.display_text.tag_add("DISPLAY_MODE", 4.19, 4.29)
|
||||
self.display_text.tag_add("COLUMN_NAME", 5.0, 6.0)
|
||||
|
||||
|
||||
def _load_sampler(self):
|
||||
bank_data = self.data.bank_data[self.data.bank_number]
|
||||
self.display_text.insert(END, '------------------ <SAMPLER> ------------------ \n')
|
||||
self.display_text.tag_add("DISPLAY_MODE", 4.19, 4.29)
|
||||
self.display_text.insert(END, '{:>6} {:<20} {:>6} {:<5} {:<5} \n'.format(
|
||||
|
||||
self.display_text.insert(END, '{} \n'.format(self.body_title))
|
||||
|
||||
self.display_text.insert(END, '{:>6} {:<17} {:>5} {:<5} {:<5} \n'.format(
|
||||
'{}-slot'.format(self.data.bank_number), 'name', 'length', 'start', 'end'))
|
||||
for index, slot in enumerate(bank_data):
|
||||
name_without_extension = slot['name'].rsplit('.',1)[0]
|
||||
self.display_text.insert(END, '{:^6} {:<20} {:^6} {:>5} {:<5} \n'.format(
|
||||
index, name_without_extension[0:20], self.format_time_value(slot['length']),
|
||||
self.display_text.insert(END, '{:^6} {:<17} {:^5} {:>5} {:<5} \n'.format(
|
||||
index, name_without_extension[0:17], self.format_time_value(slot['length']),
|
||||
self.format_time_value(slot['start']), self.format_time_value(slot['end'])))
|
||||
if index % 2:
|
||||
self.display_text.tag_add("ZEBRA_STRIPE", self.ROW_OFFSET + index,
|
||||
self.ROW_OFFSET + self.SELECTOR_WIDTH + index)
|
||||
if self.data.is_this_path_broken(slot['location']):
|
||||
self.display_text.tag_add("BROKEN_PATH", self.ROW_OFFSET + index,
|
||||
self.ROW_OFFSET + self.SELECTOR_WIDTH + index)
|
||||
current_bank , current_slot = self.data.split_bankslot_number(self.video_driver.current_player.bankslot_number)
|
||||
# highlight the slot of the selected player
|
||||
if self.data.player_mode == 'next':
|
||||
bank_slot = self.video_driver.next_player.bankslot_number
|
||||
else:
|
||||
bank_slot = self.video_driver.current_player.bankslot_number
|
||||
current_bank , current_slot = self.data.split_bankslot_number(bank_slot)
|
||||
if current_bank is self.data.bank_number:
|
||||
self._highlight_this_row(current_slot)
|
||||
|
||||
def _load_browser(self):
|
||||
browser_list = self.browser_menu.menu_list
|
||||
number_of_lines_displayed = 0
|
||||
self.display_text.insert(END, '------------------ <BROWSER> ------------------ \n')
|
||||
self.display_text.tag_add("DISPLAY_MODE", 4.19, 4.29)
|
||||
self.display_text.insert(END, '{} \n'.format(self.body_title))
|
||||
|
||||
self.display_text.insert(END, '{:40} {:5} \n'.format('path', 'slot'))
|
||||
|
||||
number_of_browser_items = len(browser_list)
|
||||
@@ -121,8 +147,8 @@ class Display(object):
|
||||
def _load_settings(self):
|
||||
line_count = 0
|
||||
settings_list = self.settings_menu.menu_list
|
||||
self.display_text.insert(END, '------------------ <SETTINGS> ----------------- \n')
|
||||
self.display_text.tag_add("DISPLAY_MODE", 4.19, 4.29)
|
||||
self.display_text.insert(END, '{} \n'.format(self.body_title))
|
||||
|
||||
self.display_text.insert(END, '{:^23} {:^22} \n'.format('SETTING', 'VALUE'))
|
||||
number_of_settings_items = len(settings_list)
|
||||
for index in range(number_of_settings_items):
|
||||
@@ -138,6 +164,85 @@ class Display(object):
|
||||
|
||||
self._highlight_this_row(self.settings_menu.selected_list_index - self.settings_menu.top_menu_index)
|
||||
|
||||
def _load_shaders(self):
|
||||
line_count = 0
|
||||
self.display_text.insert(END, '{} \n'.format(self.body_title))
|
||||
|
||||
## showing current shader info:
|
||||
shader = self.shaders.selected_shader_list[self.data.shader_layer]
|
||||
self.display_text.insert(END, '{:<1}lay{:<1}:{:<2} {:<16} '.format \
|
||||
(self.data.shader_layer,self.shaders.selected_status_list[self.data.shader_layer],shader['shad_type'][0], \
|
||||
shader['name'].lstrip()[0:16] ))
|
||||
for i in range(min(4,shader['param_number'])):
|
||||
display_param = self.format_param_value(self.shaders.selected_param_list[self.data.shader_layer][i])
|
||||
if display_param == 100:
|
||||
display_param == 99
|
||||
self.display_text.insert(END, 'x{}:{:02d}'.format(i, display_param))
|
||||
self.display_text.insert(END,'\n')
|
||||
self.display_text.tag_add("COLUMN_NAME", 5.0, 6.0)
|
||||
## showing list of other shaders:
|
||||
shaders_list = self.shaders.shaders_menu_list
|
||||
number_of_shader_items = len(shaders_list)
|
||||
for index in range(number_of_shader_items):
|
||||
if line_count >= self.MENU_HEIGHT :
|
||||
break
|
||||
if index >= self.shaders.shaders_menu.top_menu_index:
|
||||
shader_line = shaders_list[index]
|
||||
self.display_text.insert(END, '{:<40} {:<5} \n'.format(shader_line['name'][0:30], shader_line['shad_type']))
|
||||
line_count = line_count + 1
|
||||
for index in range(self.MENU_HEIGHT - number_of_shader_items):
|
||||
self.display_text.insert(END, '\n')
|
||||
self._highlight_this_row(self.shaders.shaders_menu.selected_list_index - self.shaders.shaders_menu.top_menu_index)
|
||||
if self.data.control_mode == "SHADER_PARAM":
|
||||
self._highlight_this_param(self.shaders.focused_param)
|
||||
|
||||
def _load_shader_bank(self):
|
||||
shader_bank_data = self.data.shader_bank_data[self.data.shader_layer]
|
||||
|
||||
self.display_text.insert(END, '{} \n'.format(self.body_title))
|
||||
|
||||
self.display_text.insert(END, '{:>6} {:<11} {:<5} '.format(
|
||||
'{}-layer'.format(self.data.shader_layer), 'name', 'type'))
|
||||
|
||||
shader = self.shaders.selected_shader_list[self.data.shader_layer]
|
||||
|
||||
for i in range(min(4,shader['param_number'])):
|
||||
display_param = self.format_param_value(self.shaders.selected_param_list[self.data.shader_layer][i])
|
||||
if display_param == 100:
|
||||
display_param == 99
|
||||
self.display_text.insert(END, 'x{}:{:02d}'.format(i, display_param))
|
||||
self.display_text.insert(END, '\n')
|
||||
|
||||
for index, slot in enumerate(shader_bank_data):
|
||||
name_without_extension = slot['name'].rsplit('.',1)[0]
|
||||
self.display_text.insert(END, '{:^6} {:<17} {:<5} \n'.format(index, name_without_extension[0:17], slot['shad_type']))
|
||||
if index % 2:
|
||||
self.display_text.tag_add("ZEBRA_STRIPE", self.ROW_OFFSET + index,
|
||||
self.ROW_OFFSET + self.SELECTOR_WIDTH + index)
|
||||
# highlight the slot of the selected player
|
||||
current_slot = self.shaders.selected_shader_list[self.data.shader_layer].get('slot', None)
|
||||
not_playing_tag = self.shaders.selected_status_list[self.data.shader_layer] != '▶'
|
||||
if current_slot is not None:
|
||||
self._highlight_this_row(current_slot, gray=not_playing_tag)
|
||||
|
||||
self._highlight_this_param(self.shaders.focused_param)
|
||||
|
||||
|
||||
def _load_detour(self):
|
||||
line_count = 0
|
||||
self.display_text.insert(END, '{} \n'.format(self.body_title))
|
||||
|
||||
## showing current detour info:
|
||||
self.display_text.insert(END, '{:^23} {:^22} \n'.format('SETTING', 'VALUE'))
|
||||
self.display_text.insert(END, '{:>23} {:<22} \n'.format("DETOUR_ACTIVE", self.data.detour_active))
|
||||
for index, (key, value) in enumerate(self.data.detour_settings.items()):
|
||||
if index < 8:
|
||||
self.display_text.insert(END, '{:>23} {:<22} \n'.format(key, value))
|
||||
detour_banner = self.create_detour_display_banner(self.data.detour_settings['detour_size'], self.data.detour_settings['detour_position'], self.data.detour_settings['detour_start'], self.data.detour_settings['detour_end'])
|
||||
self.display_text.insert(END, '{} \n'.format(detour_banner))
|
||||
self._set_colour_from_mix(self.data.detour_settings['detour_mix'])
|
||||
self.display_text.tag_add("DETOUR_BAR", 15.0, 15.0 + self.SELECTOR_WIDTH)
|
||||
|
||||
def _load_message(self):
|
||||
if self.message_handler.current_message[1]:
|
||||
self.display_text.insert(END, '{:5} {:42} \n'.format(
|
||||
@@ -152,21 +257,47 @@ class Display(object):
|
||||
self.display_text.insert(END, '{:^47} \n'.format('< FUNCTION KEY ON >'))
|
||||
self.display_text.tag_add('FUNCTION', 16.0,16.0 + self.SELECTOR_WIDTH)
|
||||
else:
|
||||
self.display_text.insert(END, '{:8} {:<10} \n'.format('CONTROL:', self.data.control_mode))
|
||||
feedback = ''
|
||||
if self.data.feedback_active:
|
||||
feedback = 'FDBCK'
|
||||
self.display_text.insert(END, '{:8} {:<28} {:>5} \n'.format('CONTROL:', self.data.control_mode, feedback))
|
||||
self.display_text.tag_add('TITLE', 16.0,16.0 + self.SELECTOR_WIDTH)
|
||||
|
||||
def _highlight_this_row(self, row):
|
||||
self.display_text.tag_add("SELECT", self.ROW_OFFSET + row,
|
||||
def _highlight_this_row(self, row, gray=False):
|
||||
highlight_tag = "SELECT"
|
||||
if gray:
|
||||
highlight_tag = "BROKEN_PATH"
|
||||
self.display_text.tag_remove("ZEBRA_STRIPE", self.ROW_OFFSET + row,
|
||||
self.ROW_OFFSET + self.SELECTOR_WIDTH + row)
|
||||
self.display_text.tag_add(highlight_tag, self.ROW_OFFSET + row,
|
||||
self.ROW_OFFSET + self.SELECTOR_WIDTH + row)
|
||||
|
||||
def _unhighlight_this_row(self, row):
|
||||
self.display_text.tag_remove("SELECT", self.ROW_OFFSET + row,
|
||||
self.ROW_OFFSET + self.SELECTOR_WIDTH + row)
|
||||
|
||||
def _highlight_this_param(self, param_num):
|
||||
param_row = self.ROW_OFFSET - 1
|
||||
column_offset = 0.26
|
||||
param_length = 0.05
|
||||
self.display_text.tag_add("SHADER_PARAM", round(param_row + column_offset + param_num*param_length,2),
|
||||
round(param_row + column_offset + (param_num+1)*param_length, 2))
|
||||
|
||||
|
||||
def _get_status_for_player(self):
|
||||
now_slot, now_status, now_alpha, next_slot, next_status, next_alpha = self.video_driver.get_player_info_for_status()
|
||||
capture_status = self._generate_capture_status()
|
||||
preview_alpha = self.capture.get_preview_alpha()
|
||||
|
||||
if self.capture is not None:
|
||||
capture_status = self._generate_capture_status()
|
||||
preview_alpha = self.capture.get_preview_alpha()
|
||||
else:
|
||||
capture_status = ''
|
||||
preview_alpha = 0
|
||||
|
||||
|
||||
if preview_alpha == None:
|
||||
preview_alpha = 0
|
||||
#print('capture alpha is {}'.format(preview_alpha))
|
||||
|
||||
self._set_colour_from_alpha(now_alpha, preview_alpha, next_alpha)
|
||||
|
||||
@@ -216,16 +347,51 @@ class Display(object):
|
||||
banner_list[0] = '<'
|
||||
elif position > end:
|
||||
banner_list[max] = '>'
|
||||
elif end - start != 0:
|
||||
elif end - start != 0 and not math.isnan(position) :
|
||||
#print('start value is {}, end value is {}, position is {}'.format(start, end, position))
|
||||
marker = int(math.floor(float(position - start) /
|
||||
float(end - start) * (max - 1)) + 1)
|
||||
banner_list[marker] = '*'
|
||||
|
||||
return ''.join(banner_list)
|
||||
|
||||
@staticmethod
|
||||
def create_detour_display_banner(size, position, start, end):
|
||||
banner_list = ['|', '-', '-', '-', '-', '-', '-', '-', '-',
|
||||
'-', '-', '-', '-', '-', '-', '-', '-', '-',
|
||||
'-', '-', '-', '-', '-', '-', '-', '-', '-',
|
||||
'-', '-', '-', '-', '-', '-', '-', '-', '-',
|
||||
'-', '-', '-', '-', '-', '-', '-', '-', '-',
|
||||
'|']
|
||||
max = len(banner_list) - 1
|
||||
if size == 0:
|
||||
size = max
|
||||
#print('start value is {}, end value is {}, position is {}'.format(start, end, position))
|
||||
if start > 0:
|
||||
start = int(math.floor(float(start) /
|
||||
float(size) * (max - 1)) + 1)
|
||||
banner_list[start] = '['
|
||||
if end > 0:
|
||||
end = int(math.floor(float(end) /
|
||||
float(size) * (max - 1)) + 1)
|
||||
banner_list[end] = ']'
|
||||
position = int(math.floor(float(position) /
|
||||
float(size) * (max - 1)) + 1)
|
||||
if 0 <= position and position < len(banner_list):
|
||||
banner_list[position] = '*'
|
||||
|
||||
return ''.join(banner_list)
|
||||
|
||||
def _set_colour_from_mix(self, mix):
|
||||
hex_colour = self.hex_from_rgb(255, 255 - int(255 * mix), int(255 * mix))
|
||||
self.display_text.tag_configure("DETOUR_BAR", background="black", foreground=hex_colour)
|
||||
|
||||
def _set_colour_from_alpha(self, now_alpha, preview_alpha, next_alpha):
|
||||
upper_bound = 150
|
||||
is_recording = self.capture.is_recording == True
|
||||
if self.capture is not None:
|
||||
is_recording = self.capture.is_recording == True
|
||||
else:
|
||||
is_recording = False
|
||||
### scale values
|
||||
scaled_now = int(( now_alpha / 255 ) * (255 - upper_bound) + upper_bound)
|
||||
scaled_preview = int(( preview_alpha / 255 ) * (255 - upper_bound) + upper_bound)
|
||||
@@ -240,6 +406,24 @@ class Display(object):
|
||||
self.display_text.tag_configure("CAPTURE_ALPHA", background="black", foreground=capture_colour)
|
||||
self.display_text.tag_configure("NEXT_ALPHA", background="black", foreground=next_colour)
|
||||
|
||||
def _generate_body_title(self):
|
||||
display_modes = self.data.get_display_modes_list()
|
||||
current_mode = self.data.display_mode
|
||||
selected_list = []
|
||||
for index, v in enumerate(display_modes):
|
||||
if v == current_mode:
|
||||
while len(v) < 8:
|
||||
v = v + '_'
|
||||
selected_list.append('[{}]'.format(v))
|
||||
selected_list_index = index
|
||||
else:
|
||||
selected_list.append('<{}>'.format(v[:2].lower()))
|
||||
# 18 char to PURPLE : 18 - 29 ,18 after
|
||||
selected_string = ''.join(selected_list)
|
||||
output = ('-' * (19 - (selected_list_index * 4))) + selected_string + ('-' * (18 - ((len(display_modes) - selected_list_index - 1) * 4)))
|
||||
|
||||
return output
|
||||
|
||||
@staticmethod
|
||||
def hex_from_rgb(r, g, b):
|
||||
return '#%02x%02x%02x' % (r, g, b)
|
||||
@@ -264,3 +448,17 @@ class Display(object):
|
||||
return '99:99'
|
||||
else:
|
||||
return time.strftime("%M:%S", time.gmtime(time_in_seconds))
|
||||
|
||||
@staticmethod
|
||||
def format_speed_value(value):
|
||||
if value == 1:
|
||||
return ''
|
||||
else:
|
||||
return value
|
||||
|
||||
@staticmethod
|
||||
def format_param_value(value):
|
||||
display_param = int(100 * value)
|
||||
if display_param == 100:
|
||||
display_param = 99
|
||||
return display_param
|
||||
|
||||
@@ -47,30 +47,6 @@ class Menu(object):
|
||||
else:
|
||||
return False, '|'
|
||||
|
||||
def generate_browser_list(self):
|
||||
######## starts the recursive process of listing all folders and video files to display ########
|
||||
self.browser_list = []
|
||||
for path in PATHS_TO_BROWSER:
|
||||
self._add_folder_to_browser_list(path, 0)
|
||||
|
||||
for browser_line in self.browser_list:
|
||||
is_file, name = self.extract_file_type_and_name_from_browser_format(browser_line['name'])
|
||||
if is_file:
|
||||
is_slotted, bankslot_number = self._is_file_in_memory_bank(name)
|
||||
if is_slotted:
|
||||
browser_line['slot'] = bankslot_number
|
||||
|
||||
def generate_settings_list(self):
|
||||
self.settings_list = []
|
||||
for sub_setting in self.settings.keys():
|
||||
if sub_setting in self.settings_open_folders:
|
||||
self.settings_list.append(dict(name=sub_setting + '/', value=''))
|
||||
for setting in self.settings[sub_setting]:
|
||||
setting_value = self.make_empty_if_none(self.settings[sub_setting][setting]['value'])
|
||||
self.settings_list.append(dict(name=' ' + setting, value=setting_value))
|
||||
else:
|
||||
self.settings_list.append(dict(name=sub_setting + '|', value=''))
|
||||
|
||||
@staticmethod
|
||||
def extract_file_type_and_name_from_menu_format(dir_name):
|
||||
# removes whitespace and folder state from display item ########
|
||||
@@ -113,7 +89,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):
|
||||
@@ -139,18 +115,25 @@ class BrowserMenu(Menu):
|
||||
|
||||
class SettingsMenu(Menu):
|
||||
|
||||
FOLDER_ORDER = ['sampler', 'video', 'midi', 'capture', 'other' ]
|
||||
SAMPLER_ORDER = ['LOAD_NEXT', 'RAND_START_MODE', 'FIXED_LENGTH_MODE', 'FIXED_LENGTH' ]
|
||||
VIDEO_ORDER = ['OUTPUT', 'SCREEN_MODE']
|
||||
MIDI_ORDER = ['INPUT', 'STATUS']
|
||||
CAPTURE_ORDER = ['DEVICE']
|
||||
OTHER_ORDER = []
|
||||
FOLDER_ORDER = ['video', 'sampler', 'user_input', 'capture', 'shader', 'detour', 'system' ]
|
||||
SAMPLER_ORDER = ['LOOP_TYPE', 'LOAD_NEXT', 'RAND_START_MODE', 'RESET_PLAYERS', 'FIXED_LENGTH_MODE', 'FIXED_LENGTH', 'FIXED_LENGTH_MULTIPLY' ]
|
||||
VIDEO_ORDER = ['VIDEOPLAYER_BACKEND']
|
||||
USER_INPUT_ORDER = ['MIDI_INPUT', 'MIDI_STATUS', 'CYCLE_MIDI_PORT']
|
||||
CAPTURE_ORDER = ['DEVICE', 'TYPE']
|
||||
SHADER_ORDER = ['USER_SHADER']
|
||||
DETOUR_ORDER = ['TRY_DEMO']
|
||||
SYSTEM_ORDER = []
|
||||
|
||||
SETTINGS_TO_HIDE = ['OUTPUT' ]
|
||||
|
||||
def __init__(self, data, message_handler, menu_height):
|
||||
|
||||
Menu.__init__(self, data, message_handler, menu_height)
|
||||
self.generate_settings_list()
|
||||
|
||||
def generate_settings_list(self):
|
||||
self.check_for_settings_to_hide()
|
||||
|
||||
self.menu_list = []
|
||||
ordered_folders = self.order_keys_from_list(self.data.settings, self.FOLDER_ORDER)
|
||||
for (setting_folder_key, setting_folder_item) in ordered_folders:
|
||||
@@ -158,8 +141,9 @@ class SettingsMenu(Menu):
|
||||
self.menu_list.append(dict(name='{}/'.format(setting_folder_key), value=''))
|
||||
order_list_name = '{}_ORDER'.format(setting_folder_key.upper())
|
||||
ordered_value = self.order_keys_from_list(setting_folder_item, getattr(self,order_list_name))
|
||||
for (setting_details_key, setting_details_item) in ordered_value:
|
||||
self.menu_list.append(dict(name=' {}'.format(setting_details_key), value=self.data.make_empty_if_none(setting_details_item['value'])))
|
||||
for (setting_details_key, setting_details_item) in ordered_value:
|
||||
if not setting_details_key in self.SETTINGS_TO_HIDE:
|
||||
self.menu_list.append(dict(name=' {}'.format(setting_details_key), value=self.data.make_empty_if_none(setting_details_item['value'])))
|
||||
else:
|
||||
self.menu_list.append(dict(name='{}|'.format(setting_folder_key), value=''))
|
||||
|
||||
@@ -178,7 +162,14 @@ class SettingsMenu(Menu):
|
||||
self.update_open_folders(name)
|
||||
self.generate_settings_list()
|
||||
return False, ''
|
||||
|
||||
|
||||
def check_for_settings_to_hide(self):
|
||||
self.SETTINGS_TO_HIDE = ['OUTPUT']
|
||||
if self.data.settings['video']['VIDEOPLAYER_BACKEND']['value'] != 'omxplayer':
|
||||
self.SETTINGS_TO_HIDE = self.SETTINGS_TO_HIDE + ['SCREEN_MODE', 'BACKGROUND_COLOUR', 'FRAMERATE', 'IMAGE_EFFECT', 'RESOLUTION', 'SHUTTER']
|
||||
else:
|
||||
self.SETTINGS_TO_HIDE = self.SETTINGS_TO_HIDE + ['LOOP_TYPE']
|
||||
|
||||
@staticmethod
|
||||
def order_keys_from_list(dictionary, order_list):
|
||||
ordered_tuple_list = []
|
||||
@@ -190,9 +181,39 @@ class SettingsMenu(Menu):
|
||||
ordered_tuple_list.append((other_key, dictionary[other_key]))
|
||||
return ordered_tuple_list
|
||||
|
||||
|
||||
|
||||
|
||||
class ShadersMenu(Menu):
|
||||
|
||||
def __init__(self, data, message_handler, menu_height):
|
||||
Menu.__init__(self, data, message_handler, menu_height)
|
||||
#self.top_menu_index = 1
|
||||
#self.selected_list_index = 1
|
||||
|
||||
def generate_raw_shaders_list(self):
|
||||
######## starts the recursive process of listing all folders and shader files to display ########
|
||||
self.menu_list = []
|
||||
for path in self.data.PATHS_TO_SHADERS:
|
||||
self._add_folder_to_shaders_list(path, 0)
|
||||
return self.menu_list
|
||||
|
||||
|
||||
def _add_folder_to_shaders_list(self, current_path, current_level):
|
||||
######## adds the folders and shader files at the current level to the results list. recursively recalls at deeper level if folder is open ########
|
||||
|
||||
root, dirs, files = next(os.walk(current_path))
|
||||
|
||||
indent = ' ' * 4 * (current_level)
|
||||
for folder in dirs:
|
||||
is_open, char = self._check_folder_state(folder)
|
||||
self.menu_list.append(dict(name='{}{}{}'.format(indent, folder, char), is_shader=False))
|
||||
if (is_open):
|
||||
next_path = '{}/{}'.format(root, folder)
|
||||
next_level = current_level + 1
|
||||
self._add_folder_to_shaders_list(next_path, next_level)
|
||||
|
||||
for f in files:
|
||||
split_name = os.path.splitext(f)
|
||||
if (split_name[1].lower() in ['.frag', '.shader', '.glsl', '.glslf', '.fsh']):
|
||||
self.menu_list.append(dict(name='{}{}'.format(indent, f), is_shader=True))
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -27,3 +27,6 @@ class MessageHandler(object):
|
||||
if self.number_of_messages is 0:
|
||||
self.current_message = [None, None, None]
|
||||
|
||||
def clear_all_messages(self):
|
||||
self.current_message = [None, None, None]
|
||||
self.number_of_messages = 0
|
||||
|
||||
|
Before Width: | Height: | Size: 58 KiB |
|
Before Width: | Height: | Size: 280 KiB |
@@ -1,144 +0,0 @@
|
||||
# how to build a r_e_c_u_r - diy enclosure
|
||||
|
||||
disclaimer - this is a cheap and diy approach to getting a r_e_c_u_r video sampler in your hands. if you like the device and think its worth some $$ for more professional hardware (mechanical keys, aluminum panels etc) hmu, i can add you the `boutique enclosure` wishlist.
|
||||
|
||||
## get some parts
|
||||
|
||||
for using other parts and other questions check out the [faq] , or get in touch.
|
||||
|
||||
these are the parts you need to get. to reduce shipping costs to nz i sourced them through aliexpress.com but you could equally get them from ebay or elsewhere.
|
||||
|
||||
### main parts:
|
||||
|
||||
- [raspberry pi3] *37 USD*
|
||||
|
||||
- [raspberry pi screen] *12 USD*
|
||||
|
||||
- [usb keypad (AliExpress)] or [usb keypad (Amazon)] *9 USD*
|
||||
|
||||
![main parts][main parts]
|
||||
|
||||
### optional c_a_p_t_u_r addons:
|
||||
|
||||
- [raspberry pi camera] *7 USD*
|
||||
- or [piCaptureSd1] *149 USD*
|
||||
|
||||
![capture parts][capture parts]
|
||||
|
||||
(note piCaptureSd1 is better supported in v2beta)
|
||||
|
||||
### other bits and pieces:
|
||||
|
||||
- 4x m2 and 6x m3 screws, 6mm is long enough - i ended up using 4 2-gauge and 6 4-gauge self tapping screws instead which were easier to get into the plastic case.
|
||||
|
||||
- 4 gb or greater mircoSD card
|
||||
|
||||
- a stable 5volt, 1A microUsb power supply
|
||||
|
||||
- some rubber feet for the bottom ? i had [these rubber feet] around from a previous project that work nicely
|
||||
|
||||
## print some things
|
||||
|
||||
- i 3d printed my enclosure using these files for the [top] and [bottom]. if you dont have access to a printer you can upload these files to a popular printing service in you region (eg ...)
|
||||
|
||||
- _note on enclosure: you could also just buy a standard raspberry pi3 (+screen) case and use the numpad externally. i personally found the 3d printing took a bit too long so am working on a lasercut-able option too. watch this space_
|
||||
|
||||
- 2d print these [key stickers] if you want to use the default key mapping, or modify the svg file (in inkscape or something) to create your own. you could print them onto vinyl, label paper or just normal paper and attach with with double sided tape...
|
||||
|
||||
## put it together
|
||||
|
||||
- to get a raspberry pi working you need a micro-sd card with the operating system flashed onto it.
|
||||
|
||||
- the easist way to run recur is by using [etcher] to flash your micro sd with my [modified image] of raspbian.
|
||||
(i have tried to document my steps from a fresh image of rasbian to recur if you want to learn here [instructions to install] although this is more difficult and time consuming.)
|
||||
|
||||
- _quick note about versions: i have uploaded a new modified image which you can read about and find [here]. this fixes some old bugs and adds some new features (and maybe some new bugs) this will replace the above image soon_
|
||||
|
||||
- insert sd card into pi
|
||||
|
||||
- use the 4 small screws to attach pi+screen to the bottom piece of enclosure
|
||||
|
||||
- attach the lcd screen via the pi header pins so it fits exactly on top of the pi. (some little spacers could be used to support the top corners of the screen)
|
||||
|
||||
- put a battery in the keypad , insert its usb dongle into the pi. fasten the keypad to the baseplate; i used some double sided tap along raised strips - although now im thinking superglue might hold better...
|
||||
|
||||
- use the 6 large screws to hold the top panel to the bottom
|
||||
|
||||
you are done ! wasnt that easy ?
|
||||
|
||||
## try it out !
|
||||
|
||||
( [operate docs] )
|
||||
|
||||
## my build gallery !
|
||||
|
||||

|
||||
|
||||
all the parts and tools i used in this build
|
||||
|
||||

|
||||
|
||||
the main playaz : raspi-lcd-screen , raspi3 , generic usb-keypad
|
||||
|
||||

|
||||
|
||||
tools even your mums house would have lying around...
|
||||
|
||||
|
||||

|
||||
|
||||
3d printed baseplate and top panel
|
||||
|
||||

|
||||
|
||||
ctrl-c
|
||||
|
||||

|
||||
|
||||
ctrl-v
|
||||
|
||||

|
||||
|
||||
held in with double-sided tape on bottom
|
||||
|
||||

|
||||
|
||||
|
||||

|
||||
|
||||
its easier to flash and insert the sd card before screwing it in !
|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||

|
||||
|
||||
r_e_c_u_r looking happy among friends !
|
||||
|
||||
|
||||
|
||||
[raspberry pi3]:https://www.aliexpress.com/item/RS-Version-2016-New-Raspberry-Pi-3-Model-B-Board-1GB-LPDDR2-BCM2837-Quad-Core-Ras/32789942633.html?spm=a2g0s.9042311.0.0.FkRWty
|
||||
[main parts]: build_all.jpg
|
||||
[capture parts]: capture_parts.jpg
|
||||
[raspberry pi screen]:https://www.aliexpress.com/item/3-5-Inch-TFT-LCD-Moudle-For-Raspberry-Pi-2-Model-B-RPI-B-raspberry-pi/32707058182.html?spm=a2g0s.13010208.99999999.262.bV4EPV
|
||||
[usb keypad (AliExpress)]:https://www.aliexpress.com/item/USB-Wireless-Numeric-Keypad-19-Keys-Numpad-Number-Pad-Wireless-2-4GHz-Mini-Receiver-for-Laptop/32821720854.html
|
||||
[usb keypad (Amazon)]:https://www.amazon.com/gp/product/B076GZDC14/ref=oh_aui_detailpage_o00_s00?ie=UTF8&psc=1&fbclid=IwAR3fNd1z0Cu137GE0ONYP2vmoTm0rJIvDA9plHlvCjNGZrSZFsV_naCHax0
|
||||
[raspberry pi camera]:https://www.aliexpress.com/item/RPI2-raspberry-pi-2-model-b-b-plus-camera-5-million-pixels-professional-ip-webcam-module/32403602769.html
|
||||
[piCaptureSd1]: https://lintestsystems.com/products/picapture-sd1
|
||||
[top]: ./topplate.stl
|
||||
[bottom]: ./baseplate.stl
|
||||
[key stickers]: ./keystickers.svg
|
||||
[etcher]: https://etcher.io
|
||||
[modified image]: https://drive.google.com/file/d/1SlqM13jxLlk_zajXgdub1fpu-k76jE1e/view?usp=sharing
|
||||
[operate docs]: ./operate_docs.md
|
||||
[instructions to install]: ../dotfiles/README.md
|
||||
[these rubber feet]: https://www.aliexpress.com/item/40-Self-Adhesive-Rubber-Bumper-Stopper-Non-slip-Feet-Door-Buffer-Pads-Furniture-DIY-Tool/32849514475.html?spm=a2g0s.9042311.0.0.6ee14c4dFXynVK
|
||||
[faq]: ./faq.md
|
||||
[here]: https://github.com/langolierz/r_e_c_u_r/blob/c_o_n_j_u_r/documentation/beta_access_notes_and_limitations_of_v2.md
|
||||
|
Before Width: | Height: | Size: 173 KiB |
|
Before Width: | Height: | Size: 182 KiB |
|
Before Width: | Height: | Size: 113 KiB |
|
Before Width: | Height: | Size: 123 KiB |
|
Before Width: | Height: | Size: 138 KiB |
|
Before Width: | Height: | Size: 162 KiB |
|
Before Width: | Height: | Size: 254 KiB |
|
Before Width: | Height: | Size: 195 KiB |
|
Before Width: | Height: | Size: 151 KiB |
|
Before Width: | Height: | Size: 139 KiB |
|
Before Width: | Height: | Size: 161 KiB |
|
Before Width: | Height: | Size: 265 KiB |
|
Before Width: | Height: | Size: 252 KiB |
|
Before Width: | Height: | Size: 273 KiB |
|
Before Width: | Height: | Size: 379 KiB |
|
Before Width: | Height: | Size: 687 KiB |
|
Before Width: | Height: | Size: 34 KiB |
@@ -1,40 +0,0 @@
|
||||
# how to develop r_e_c_u_r
|
||||
|
||||
i have tried to write this application so it can easily be read and modified for different use cases. i recommend forking the repo to experiment with the codebase. open a pull request into origin <your_branch> if you want to contribute your changes back into the project.
|
||||
|
||||
this [diagram] might help understand the design :
|
||||
|
||||
![design_overview][design_overview]
|
||||
|
||||
here are some examples of changes you might want to make:
|
||||
|
||||
## rearranging the _keypad_ controls
|
||||
|
||||
to simplify the key-mapping process, i have pre-mapped the numpad keys to the _labels_ `a` to `s` like this:
|
||||
|
||||
![premapped_keys][premapped_keys]
|
||||
|
||||
(see [dotfiles] for description of this process)
|
||||
|
||||
for each _label_ the application will read the [keypad_action_mapping.json] file and map it to an [action]. the format also allows unique actions per _control_mode_ and per the `FUNCTION` toggle :
|
||||
|
||||
```
|
||||
...
|
||||
"x": {
|
||||
"NAV_BROWSER": ["trigger_this_action_in_browser_mode"],
|
||||
"DEFAULT": ["trigger_this_action_in_any_other_mode_with_FN_off","trigger_this_action_in_any_other_mode_with_FN_on"],
|
||||
}
|
||||
```
|
||||
## creating a new action
|
||||
|
||||
## beyond
|
||||
|
||||
i hope the foundations iv provided encourage you to make larger changes for more ambitious features. if so you could try getting in touch (langolierz@gmail.com) first and maybe i could help align your approach with the rest of the project
|
||||
|
||||
[diagram]: https://docs.google.com/drawings/d/1ltWCv82rKVzOiFe6GaDDPlneG2oki0yRujArPU5V2ss/edit?usp=sharing
|
||||
[design_overview]: design_overview.jpg
|
||||
[premapped_keys]: vectorfront_blank_keys.png
|
||||
[dotfiles]: ../dotfiles
|
||||
[keypad_action_mapping.json]: ../data_centre/json_objects/keypad_action_mapping.json
|
||||
[action]: ../actions.py
|
||||
|
||||
|
Before Width: | Height: | Size: 74 KiB |
@@ -1,42 +0,0 @@
|
||||
# hdmi drop out bug
|
||||
|
||||
some time between when i did compatibility testing , and now - high res videos now often (and consistently cause hdmi drop outs. all 1080 do , and some 720 also) - on my hdmi-to-vga computer display.
|
||||
|
||||
also (possibly related) when trying it on full hdmi projector, it drops out playing all video no matter the resolution ! whats going on ?
|
||||
|
||||
some research suggests that not enough power can cause this problem.
|
||||
possible fixes / things to rule out include :
|
||||
|
||||
- trying signal-boost in config
|
||||
- try overclocking the pi
|
||||
- try an old version of the code
|
||||
- try an old version of firmware
|
||||
- try changing some other config settings (camera mode etc)
|
||||
- try creating a new img from old img (with bare minimum) and update piece by piece until it doesnt work.
|
||||
|
||||
## results:
|
||||
|
||||
### vga-hdmi display
|
||||
|
||||
- signal boosting didnt help ,
|
||||
- old version of code didnt help
|
||||
- old firmware improved a little bit but didnt fix
|
||||
- changing config etc didnt help
|
||||
- turning off raspi2fb didnt help
|
||||
|
||||
### drastic measures ! :
|
||||
|
||||
- flashed a new sd with old raspbian from last year. only installing the minimal to play video.
|
||||
|
||||
- while running old code it plays better but not perfect - still drops from time to time but mostly working.
|
||||
- tried installing some newer dependencies , still running old code :
|
||||
|
||||
didnt investigate this route any further
|
||||
|
||||
### solved ?
|
||||
|
||||
by chance i tried switching the video output to composite and back - ~~somehow after this the hdmi plays 1080 videos just fine. no idea why or when it was introduced , but i created an action to run this on startup and seems fine now. phew.~~ this didnt seems to fix it after all.
|
||||
|
||||
i tried setting the hdmi output to 720 instead of 1080 and from here all the videos including 1080 load and play fine. still some dropouts for 1080 video on my vga converter though...
|
||||
|
||||
|
||||
@@ -1,45 +0,0 @@
|
||||
|
||||
# some of what follows is out of date since i have improved a number of problems since i did this. i will have to revisit this page with updates soon !
|
||||
|
||||
# compatibility testing
|
||||
|
||||
so far i have only been using a small selection of video files while running **r_e_c_u_r** .
|
||||
|
||||
i want to try it with a number of different video containers , codecs , resolutions , lengths etc on
|
||||
both hdmi and composite displays. by compiling and downloading a number of test clips from various places online ,
|
||||
i now have a folder of videos named in this format : 'width-container-codec'
|
||||
|
||||
clip | expectation | results
|
||||
--- | --- | ---
|
||||
120-mov-svq1.mov | i wouldnt expect/need this to play with apples custom codec | omx cant reconise codec
|
||||
240-webm-vp8.webm | i believe vp8 codec is supported on omx but not sure if webm container is | omx cant play webm
|
||||
360-webm-vp8.webm | same as above | ""
|
||||
480-avi-mjpeg.avi | this should play fine | ~~wont map `dbus exception no reply`~~ on second try it does map but doesnt play - normal omx player opens but cant play it either
|
||||
480-flv-vp6.flv | i think this should work although not too worried about supporting flv ! | omx cant play this ?
|
||||
576-mov-mjpeg.mov | this should work | this works surprisingly well - never lags on load
|
||||
576-mp4-h264.mp4 | this should work fine | plays good, sometimes lags on custom start
|
||||
720-mkv-h264.mkv | this should play fine - interested if seeking / sublooping looks ok | plays ok , customstart seems to weirdly jump to middle ...
|
||||
720-mp4-h264.mp4 | this should play fine | plays good, sometimes lags on load
|
||||
720-mp4-h264-60fps | ~~my computer struggles to play this~~ think just a laggy video | plays suprisingly well - never lags on custom start
|
||||
1080-avi-mpeg4.avi | i think this should play ok | does play - sometimes struggles to load the next loop though - similar to the other 1080 one but a bit better (seems to load if current video is paused)
|
||||
1080-flv-flv1.flv | wouldnt expect/need flv with custom codec | omx doesnt recognize codec
|
||||
1080-mkv-h264-60mbps.mkv | dont expect this to work | surprisingly this video played though on pi (didnt even open on my computer.) however is showing a weird new bug where when the video finished , the next one wont load but also wont error... _UPDATE: works now gpu has more memory_
|
||||
1080-mp4-h264.mp4 | hopefully this plays okay ?? | this wouldnt load , error getting video length : `dbus exception no reply` (although it played ok in normal omxplayer) _UPDATE: works now gpu has more memory_
|
||||
error-mkv-mpeg4.mkv | should fail but not sure how.. | just wont load
|
||||
|
||||
an interesting note : ~~all the videos above behaved the same on hdmi and composite out except for 1080-mp4-h264 which didnt have the failing to load next problem , instead , would flash green for a bit at the start of each clip even on custom starts~~ this also started happening on hdmi out - seems to be unpredictable how it handles 1080p files
|
||||
|
||||
## the same mp4 video at different resolutions
|
||||
|
||||
i use adobe premiere to edit videos. i imported a raw 10s mts file from a digital camera and exported 3 times : 480 , 720 and 1080. these three were loaded and tested on recur :
|
||||
|
||||
- 480 played fine - no lags when custom starting (both on hdmi and composite)
|
||||
- 720 played ok - video played through and loaded all good. sometimes would lag on custom start point , seemed to be better / not do this when composite out mode
|
||||
- ~~1080 doesnt work - the video can play through once alright , but it seems like the omxplayer/dbus connection cant load another 1080 file while an existing 1080 file is playing~~
|
||||
|
||||
UPDATE: turns out the 1080 files couldnt load because the pi hadnt been assigned enough memory to its gpu . i added `gpu_mem=448` to the config.txt and now 1080 videos seem to load and loop just fine. (still sometimes lags when change is triggered/ custom start)
|
||||
|
||||
# summary of findings :
|
||||
|
||||
- .mp4 containers with h.264 codec seems to be the best format - long videos play fine (tested up to 3 hours) besides some display confusion for durations over 99 minutes as expected. can play files up to 1080 resolution fine. the chances of micro lags on changeover/custom starts seems to increase with higher resolution (no problems with 480 now) but can still be avoided by setting another custom start just after the position that is lagging.
|
||||
- .avi , .mov and .mkv containers also work. h.264 is best , mjpeg worked in a .mov container but not in .avi ... there was some issues with setting custom start in one of the .mkv files i tried - this might need some more investigation...
|
||||
@@ -1,68 +0,0 @@
|
||||
|
||||
|
||||
### serial midi from rpi gpio pin
|
||||
|
||||
i believe it is possible to read midi in from an i/o pin (that can read serial) which might be desirable in some cases but this is a good start for now. this [instructable] explains how to input/output midi with the gpios, it looks like the tx/rx (serial?) pins on the pi3 are currently used/covered by the gpio screen that i am using. if i was serious about external circuits interfacing with pi/recur, i might look into using an lcd screen that doesnt use up the gpios. (would also be worth checking if piCapture would work with the gpio screen i have...)
|
||||
|
||||
### gpio pins :
|
||||
|
||||
the [adafruit tft display] mentioned above also uses the gpios to connect to the pi - in particular it uses 5 spi pins and two standard pin outs. by cross referencing the [raspi gpio] docs it does not use either of the rx serial pin , which would be needed if i were to receive midi directly (rather than through usb), it also leaves plenty of pins for receiving cv from a [mcp3008] through software spi for example. it is likely that my gpio lcd screen communicates with the pi in a similar way and that i could figure out a way to connect these extensions if desired.
|
||||
|
||||
what follows is the interface of cheep lcd from the shop page :
|
||||
|
||||
PIN NO.| SYMBOL | DESCRIPTION
|
||||
--- | --- | ---
|
||||
1, 17| 3.3V | Power positive (3.3V power input)
|
||||
2, 4 | 5V | Power positive (5V power input)
|
||||
3, 5, 7, 8, 10, 12, 13, 15, 16 | NC | NC
|
||||
6, 9, 14, 20, 25 | GND | Ground
|
||||
11 | TP_IRQ | Touch Panel interrupt, low level while the Touch Panel detects touching
|
||||
18 | LCD_RS | Instruction/Data Register selection
|
||||
19 | LCD_SI / TP_SI | SPI data input of LCD/Touch Panel
|
||||
21 | TP_SO | SPI data output of Touch Panel
|
||||
22 | RST | Reset
|
||||
23 | LCD_SCK / TP_SCK | SPI clock of LCD/Touch Panel
|
||||
24 | LCD_CS | LCD chip selection, low active
|
||||
26 | TP_CS | Touch Panel chip selection, low active
|
||||
|
||||
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/
|
||||
[mcp3008]: https://learn.adafruit.com/raspberry-pi-analog-to-digital-converters/mcp3008
|
||||
@@ -1,33 +0,0 @@
|
||||
## firmware bug
|
||||
|
||||
at some point i must have updated the raspi firmware. this caused the recur application to present a bug that makes it basically unusable:
|
||||
|
||||
### the bug
|
||||
|
||||
the video freezes / lags often , usually at the same spot : 10s in, 2minutes in etc. when a video is reload (ie another video player created underneath current one) the current video usually freezes.
|
||||
|
||||
### investigation
|
||||
|
||||
- i tried checking out an old version of recur code to rule out a change on my code causing it. this still behaves the same way
|
||||
|
||||
- i created a new version of recur from the newest (april) version of stretch. this also displays the bug.
|
||||
|
||||
- finally i created a new version of recur from the older version (november) and made sure to not install any of the new packages (midi / capture). this worked without the bug on an old version of the code ! wahoo. i now tried installing those new packages and running the update code. still working !
|
||||
|
||||
- next i created an image of this working recur. (known issues with the image so far : screen saver is on, our home wifi is included)
|
||||
|
||||
- the firmware that is working is `uname -a : Linux raspberrypi 4.9 59-v7+ #1047 SMP Sun Oct 29 ...`
|
||||
|
||||
- next i will try a `sudo apt update` and `sudo apt upgrade` to rule out a package update causing the bug - can confirm its still working after apt upgrades ! must be the firmware :
|
||||
|
||||
- the gpu firmware (not sure the exact dif..) is obtained from `sudo /opt/vc/bin/vcgencmd version` and is `Oct 24 2017 .. a3d7660e6749e75e2c4ce4d377846abd3b3be283 (clean) (release) `
|
||||
|
||||
the new firmware says its : `754029b1cb414a17dbd786ba5bee4fc936332255` which is what i started typing into `sudo rpi-update 754029b`
|
||||
|
||||
now `uname -a` reads `.. 4.9.60-v7+ #1048 .. Fri Nov 3` and the player still works. `sudo /opt/vc/bin/vcgencmd version` says `Nov 3 .. 1bcf9152... (clean) (release)`
|
||||
|
||||
pushed forward to v 4.14.20 which failed. pushing back: did this a few times. got it working on 4.9.78 but failing on 4.9.80. looks like it might be this issue : https://www.raspberrypi.org/forums/viewtopic.php?t=195178 - just need to figure out how to turn it off for testing
|
||||
|
||||
found it ! i have the latest firmware version and by adding `audio_pwm_mode=0` to the config it now plays as before . phew what a relief !
|
||||
|
||||
gonna create new version of the image with this fixed , the wifi removed and the screensaver disabled.
|
||||
@@ -1,98 +0,0 @@
|
||||
# video input
|
||||
|
||||
### background
|
||||
|
||||
a common feature of audio samplers is the ability to 'live sample' ie have a line in to the device and use this to record a sample directly onto it - usually to be played back again immediately as part of a performance.
|
||||
|
||||
from what i can tell some hardware video samplers also offer this feature. it would be interesting to explore the possibility of this with recur. some ways to record video into recur include:
|
||||
|
||||
- a usb dongle / external capture card
|
||||
- using a pi camera for live video or rescanning off a screen
|
||||
- piCapture : a custom video card built for pi to use the pi cam protocol
|
||||
|
||||
the first option seems fiddly and difficult / a compromise. i might be interested in trying to capture with a Blackmagic Intensity Shuttle if i get one some day...
|
||||
|
||||
options 2 and 3 both seem plausible, and hopefully will be interchangeable once things are working (piCapture claims to act exactly like a piCam so should painless). also this allows both a cheap/hacky solution (rescanning through $2 camera) or a more professional option ($130 addition) and i can start experimenting now with a cheep cam before investing in the expensive piCapture.
|
||||
|
||||
i know piCam and piCapture recommend using python and there is libraries for this
|
||||
|
||||
### things to find out
|
||||
|
||||
i want to know how plausible it would be to add live sampling to my current recur stack (gpio lcd screen, omxplayer video backend).
|
||||
|
||||
some things i want know are:
|
||||
|
||||
- how to set up the piCam on raspberry pi
|
||||
- how to record the input from the piCam onto the pi
|
||||
- will these files play back in omx/recur
|
||||
- can you preview the stream on the hdmi/composite output ?
|
||||
- can you preview the stream on the lcd display ?
|
||||
- can this preview be adjusted / resized etc ?
|
||||
- can you adjust params of camera ? in real time ? framerate , brightness , contrast , zoom etc ?
|
||||
- how does rescanning look ?
|
||||
- how could i control piCaptures additional params ?
|
||||
- is there any reason to think piCapture wouldnt be interchangable with a working piCam feature ?
|
||||
|
||||
### research
|
||||
|
||||
[cheep cameras] for raspi can be bought from china with 5 mega pixels / recording 1080p video for around $5usd. i have borrowed a camera ~~which i think is a night-vision version~~ to experiment with , although should order one of these myself.
|
||||
|
||||
i started by reading the [picamera] python package docs. this seemed to have lots of options regarding recording video , including starting and stopping both previews and video recordings , the ability to set the resolution , framerate , shutter speed of the camera , switching preview to full screen or setting the size and position of it.
|
||||
|
||||
also a bunch of parameters that might be of interest including brightness , colour effect , contrast , flips and rotations , preview alpha , saturation , sharpness. these apear to be controllable in real time while previewing or recording.
|
||||
|
||||
it seems like the output from the camera is raw (without frame per second meta data) h264 and to play the video back at correct speed will need a program to wrap it in an mp4 container :
|
||||
```
|
||||
$ sudo apt-get install gpac
|
||||
...
|
||||
$ MP4Box -add input.h264 output.mp4
|
||||
```
|
||||
|
||||
the [faq] also addresses the 'can i preview to the lcd screen' question : looks like no - at least not without copying the exact framebuffer , similar to my experiments displaying omx on the lcd screen. (this still might be possible in the world of openCv but off the table for now! - or maybe not even then - this [adafruit] tutorial talks about the limitations of displaying on a tft screen - "accelerated software will never appear on the PiTFT (it is unaccelerated framebuffer only)" )
|
||||
|
||||
|
||||
|
||||
### research continued : piCapture
|
||||
|
||||
[picapture] is a video capture card designed for the raspberry pi to emulate the piCamera and take advantage of the pi's hardware acceleration. it comes as a 'hat' that also uses ic2 (or serial)
|
||||
to communicate. there is a python package to access these additional options.
|
||||
|
||||
these come in two types :
|
||||
|
||||
- standard def - composite and s-video in , $139usd
|
||||
- hi def - hdmi and component in $159usd
|
||||
|
||||
im not sure which one i would like to try, but they sound cool ! would need to check the pins dont clash with the display but i think these should work together nicely !
|
||||
|
||||
### getting started with piCamera
|
||||
|
||||
following the picamera docs , i will/have :
|
||||
|
||||
- plugged in the camera
|
||||
- turned on camera in the config
|
||||
- tried take an image
|
||||
- installed package with `sudo apt-get install python3-picamera`
|
||||
- run `sudo apt-get update` and `sudo apt-get upgrade` (for firmware)
|
||||
- trying some simple python commands with camera
|
||||
- write some experimental recur code
|
||||
|
||||
first hitch : i enabled the camera in the raspi-config , but it seems like the switching screens driver overrides this , ~~so will have to update this too !~~ fixed this by adding a line to the config.txt
|
||||
|
||||
besides that the preview / different parameters and effects work as expected. next step is to try recording something , converting it to mp4 and playing back on omxplayer.
|
||||
|
||||
i have installed `sudo apt-get install gpac` and am using `subprocess.Popen` to run the `MP4Box` command from inside python. this way i can poll back into it and map the video only when its finished converting to stop blocking in the meantime. i also updated the display to show when the camera is previewing and recording. this all worked smoother than i expected.
|
||||
|
||||
i also made a (surprisingly small) change to the browser to show the pi's videos folder next to the external devices. this will be useful for using the recordings saved and for copying files onto recurs disk. (the copying feature has been de-prioritized since it can be done manually with mouse/keyboard and could be risky / might want a confirmation window ...)
|
||||
|
||||
another thing still to think about is how to protect from overfilling the sd card / external storage.
|
||||
- i have done this by checking before starting to record and every 10 seconds during recording if the disk space is under 10mb in which case it warns and stops the recording.
|
||||
|
||||
also displaying info when camera is not attached and catching other types of errors...
|
||||
- this will be handled by bool enabling the capture. if it can not detect the camera it will not allow this to be enabled.
|
||||
|
||||
[picamera]: http://picamera.readthedocs.io/en/release-1.0/api.html
|
||||
[faq]: https://picamera.readthedocs.io/en/release-1.13/faq.html
|
||||
[adafruit]: https://learn.adafruit.com/adafruit-pitft-3-dot-5-touch-screen-for-raspberry-pi/easy-install-2
|
||||
|
||||
[cheep cameras]: https://www.aliexpress.com/item/5MP-Camera-Module-Flex-Cable-Webcam-Video-1080-720p-For-Raspberry-Pi-2-3-Model-B/32860830711.html
|
||||
[picapture]: https://lintestsystems.com/products/picapture-sd1
|
||||
@@ -1,66 +0,0 @@
|
||||
# midi support
|
||||
|
||||
my investigation into controlling recur with midi will be documented here.
|
||||
|
||||
### aim
|
||||
|
||||
i want to be able to read midi messages to control recur - the simplest intergration would be loading and triggering clips from midi. other paramters controled over cc or otherwise should be easy to add when required. in a similar way to the keypad controls, i would like the mapping to be read from a json file and use this to run actions.
|
||||
|
||||
### cheep midi9to-usb dongle
|
||||
|
||||
i have decided to start by trying a cheep [usb-to-midi cable] i got for just over $3.
|
||||
|
||||
plugging the midi dongle into the pi , i can see it in `client 20` by calling `aconnect -i` , however when connected to a midi controller (deluge) it is not receiving messages when `aseqdump -p 20` is running (besides a few ghost note off events)
|
||||
|
||||
running midiox on my windows machine to test the cable also didnt help, it wouldnt receive any messages and complained about not having enough memory...
|
||||
|
||||
something seems wrong with the dongle. it also seems like these cheep dongles are not very reliable or useful anyway according to [sy35er] on a yamahamusic forum
|
||||
|
||||
update : by using ableton on my windows i could successfully send midi out off usb and receive it on deluge. still no luck reading midi to usb though.
|
||||
|
||||
|
||||
### usb midi
|
||||
|
||||
next i tried using the deluge to output midi over usb. midiox on windows still wouldnt work , but when i tried `aseqdump -p 20` on the pi it recorded the midi perfectly. i will use usb midi to test this feature on recur. (better adapters exist that convert serial midi to usb midi /could make one with ardunio or teensy!), so that will be a good start
|
||||
|
||||
i have decided to try using the [mido] python package for responding to midi input.
|
||||
|
||||
### midi clock
|
||||
|
||||
besides the obvious triggering of clips and controlling parameters, it has also been suggested that the recur could 'sync' to incoming [midi-clock] messages. these communicate a master tempo by sending 24 pulses (clock messages) every quarter note. this could be useful in play modes where the video changes every bar of music (counting pulses from a start message) or even to approximate slowing/speeding of a clip if the speed control of omx would handle it
|
||||
|
||||
### getting started with mido
|
||||
|
||||
i install mido and rtmidi for backend : `pip3 install mido python-rtmidi`,
|
||||
i called `mido.get_input_names()` and searched the results for the first containing the substring `20:0` (this is the port my devices came up as - will need to investigate further why this is and if it will be the case for all external usb midi devices)
|
||||
|
||||
from here i called a polling method that reads the next message coming from that port and if there is one will call into the mapping function (from json file)
|
||||
|
||||
i created a midi_action.json mapping in the same format as the key press but with `type (value)` as the keys. eg `note_on 70` is an example, `clock` is another. for now i have mapped exactly the same as with the key presses
|
||||
|
||||
this works perfectly for pressing keys on the deluge and seeing the corresponding action on recur. even when pressing keys with the sequencer running (lots of clock messages being sent) it still responds quickly and consistently. however when triggering the same notes from the sequencer, it seems to drop lots on the notes : usually only picking up either note_on or note_off , sometimes both and sometimes neither.
|
||||
|
||||
I tried changing the way recur handles the incoming messages (working through a list of unprocessed messages rather than one at a time) , but this didnt help - its not a problem of response time as can be shown by pressing keys with sequencer on. even looking at the raw input to the port on `aseqdump -p 20` i can see some messages missing. i updated the deluge firmware to a stable release but no changes.
|
||||
|
||||
however when i tried turning off the clock messages from the deluge , the sequencer messages started coming through clearly ! and it syncs up nicely... how strange.
|
||||
|
||||
### further clock debugging
|
||||
|
||||
other things to try is learning how to use abelton as a midi controller and see whether it works there with/without clock messages. could also try other gear that can output midi / controllers with recur and try deluge with other computer / gear
|
||||
|
||||
### experimenting with cc
|
||||
|
||||
besides figuring out whats up with the incoming clock signal , another thing to experiment with is using a cc knob to control parameters in recur. there is nothing pressing i want for this but could try it with : fades , speed? , length of seeks , seeking?
|
||||
|
||||
i ended up trying cc with some camera parameters. there are plenty more interesting examples of continuous control there. it works well , but it seems like running the methods on every change when they are sequenced / coming in consistently is a bit much for recur to respond in real time.
|
||||
|
||||
one idea to reduce this is to only process a incoming cc message if it is outside a range for that channel - ie only action on every 3rd cc change for each control for example. - this works well. updating every 5 cc values had no lag under heavy use but looks quite jumpy. step size of 2 looks smooth but can lag quite a lot. i have it set to 4 atm but can revisit when other features are under cv control etc.
|
||||
|
||||
|
||||
|
||||
|
||||
[usb-to-midi cable]: https://www.aliexpress.com/item/Hot-Selling-1pcs-Keyboard-to-PC-USB-MIDI-Cable-Converter-PC-to-Music-Keyboard-Cord-USB/32813475019.html
|
||||
[instructable]: http://www.instructables.com/id/PiMiDi-A-Raspberry-Pi-Midi-Box-or-How-I-Learned-to/
|
||||
[mido]: https://mido.readthedocs.io/en/latest/
|
||||
[midi-clock]: https://en.wikipedia.org/wiki/MIDI_beat_clock
|
||||
[sy35er]: https://yamahamusicians.com/forum/viewtopic.php?t=8218
|
||||
@@ -1,52 +0,0 @@
|
||||
# porting to other sbc
|
||||
|
||||
a collection of thoughts / research / attempts at porting r_e_c_u_r
|
||||
|
||||
## attempting to port r_e_c_u_r to an orange pi plus
|
||||
|
||||
i bought an [orange pi plus] at the same time as my raspberry pi 3 , thinking that since they are
|
||||
similarly spec-ed (orange pi is a little weaker but also much cheaper ~18US vs ~38USD i payed for rpi3) this might be an interesting alternative to offer.
|
||||
|
||||
i (naively) figured since opi claim to support raspbian as an os, that this might be as simple as installing the same dependencies outlined in the [preparing image] notes and maybe some fiddling with the lcd-screen drivers...
|
||||
|
||||
it seems like the only raspbian image for orange pi plus was a fork of wheezy distro from 2015 with pre-installed desktop. this is not supported or updated by orange pi and seems to be a token gesture to compete on paper with raspberry pi. with this image i managed to install some dependencies, but the dbus packages needed for the omx wrapper couldnt be installed (i think the os was too old). also their is no config.txt on orangepi so settings like composite video out and different hdmi modes was going to be more difficult (not to mention the lcd screen driver)
|
||||
|
||||
## porting to armbian
|
||||
|
||||
however what is supported and updated is an os called armbian , which (similar to raspbian)
|
||||
is a version of debian (or linux) made for ARM dev boards. if i can get r_e_c_u_r working on opi running latest armbian , it might also work on a bunch of other sbc including other orange pis, ondroids , banana pi etc (beaglebone's run straight debian so perhaps i should see if it works there too)
|
||||
|
||||
given that the software dependencies are available in these alternative os (im not sure if they are yet but will just have to try it) some other problems to solve when trying to port r_e_c_u_r to other sbcs are:
|
||||
|
||||
- connecting a lcd screen / playing video to one framebuffer while running python on another.
|
||||
[Kaspars Dambis' blog] describes how to configure a similar lcd display to mine on an orange pi zero running armbian so hopefully this could help porting it to orange pi pc
|
||||
- playing the video through one framebuffer (hdmi or composite) while the python code displays on another (lcd screen)
|
||||
this kindof 'just worked' for me on raspberry pi running rasbian but i dont know how it will translate...
|
||||
- video playback might be weaker depending on the gpu acceleration of these alternative boards
|
||||
(omxplayer is accelerated for rpi but probably not for these others). also things like h.2 video codex
|
||||
licencing things etc might come into it
|
||||
- configuring different hdmi and composite video settings. (pi seems to do this particularly well)
|
||||
|
||||
## conclusion for now
|
||||
|
||||
some more research into this is required , but at this point it seems like the extra effort to get recur running on other smc's might not be worth the savings in cost or flexibility.
|
||||
|
||||
r_e_c_u_r is an embedded solution and the choice of hardware (raspi3) is tied to the application :
|
||||
|
||||
- lcd screen drivers
|
||||
- omxplayer w acceleration
|
||||
- (future features using pi camera / capture devices)
|
||||
|
||||
right now rpi3 still seems like the best tool for the job and the benefits of running cross-boards are
|
||||
not enough to distract from improving this implementation. (perhaps a future recur independent of omxplayer might benefit more from running on main debian / armian)
|
||||
|
||||
## r_e_c_u_r on other raspberry pi boards
|
||||
|
||||
as an aside, i am still hopeful that r_e_c_u_r will run on rpi2 and/or zero with little to no changes required. this might be a more useful and achievable port to focus on for now.
|
||||
|
||||
|
||||
|
||||
|
||||
[orange pi plus]: https://www.aliexpress.com/item/Orange-Pi-PC-linux-and-android-mini-PC-Beyond-Raspberry-Pi-2/32448079125.html?spm=a2g0s.9042311.0.0.kWJI0G
|
||||
[preparing image]: ./preparing_image.md
|
||||
[Kaspars Dambis' blog]: https://kaspars.net/blog/linux/spi-display-orange-pi-zero
|
||||
@@ -1,73 +0,0 @@
|
||||
|
||||
# frequently asked questions
|
||||
|
||||
a place to document the questions and thoughts asked about r_e_c_u_r so far. if you have any other questions please ask them in the fb group or get in touch directly.
|
||||
|
||||
## what kind of lcd screens can i use for recur _display_
|
||||
|
||||
the cheep 3.5 lcd screen i suggest in the [build] guide is already set up (with drivers etc) to work with the card img i have shared. this is the easiest option as it will be straight plug-and-play.
|
||||
|
||||
however it will be possible to config some other lcd screens to work. the _video output_ uses framebuffer0 on either the hdmi or composite output (i have not heard of these outputting simultaneously ). for this reason you cannot use a lcd screen that connects via hdmi for the display. the default _display_ uses framebuffer1 over the gpio pins. any lcd screen that receives video over gpio should work with recur given the correct drivers (note: some hdmi displays still use gpio for power / touch info; these will not work as a display)
|
||||
|
||||
besides _gpio_ and _hdmi_ , the third option to connect a lcd display is over _dsi_ (used by official raspi display). i have not tried this and am not sure if it would (or could) work for recur. i would be keen to hear thoughts / experiments if anyone has one, or have a go at this at some point...
|
||||
|
||||
## can i use a pi2/pi1/pi0 for this ?
|
||||
|
||||
im not sure. will update here when i have a chance to try... the pi3 is the highest spec available right now, and for playing hd videos seamlessly it is already at its limit. an older model might work fine for composite but would struggle sooner with hd content. besides gpu , i think a pi2 should _run_ recur out of the box. would be keen to try recur composite playback on the pi0
|
||||
|
||||
## how do i connect composite out / why is my composite cable just outputting corruption ?
|
||||
|
||||
you will need the correct 3.5mm TRRS to RCA cable. the [correct cable] has video on fourth pole and ground on third. if your cable is outputting junk / not working it is possible you (like me) have gotten the wrong type: [my cable] had ground on the fourth pole and video on the third (most digital cameras are wired this way) i fixed this by cutting an old rca cable and swapping the ground / video around
|
||||
|
||||
## can i use a usb keyboard / some other usb keypad to control recur ?
|
||||
|
||||
yes ! the key mapping has been abstracted out to a .json file that can be edited to allow different input options. the flashed img maps the keys for the keypad i recommend (from [build] docs) to the letters a - s with the [.keymap] . the a - s keys are then assigned in the [keypad_action_mapping.json] to their corresponding actions.
|
||||
|
||||
if you are using a full usbkeyboard, you should be able to just press a - s keys for corresponding actions , and rearrange the letters in mapping for your own custom mapping.
|
||||
|
||||
you can also flatten out the FN actions and add new actions (such as shortcuts / hotkeys) to map to the extra keys. hopefully more info about this will be on the [develop page] at some point.
|
||||
|
||||
## i want to edit a keymapping or do something on the pi without looking at the lcd screen
|
||||
|
||||
i also get sick of looking at the lil screen for developing , from the OTHER settings you can run DEV_MODE_RESET , when it reboots the hdmi is the main output and your video out should be in a smaller window.
|
||||
|
||||
## i keep getting a yellow lightening bolt in the upper right corner ?
|
||||
|
||||
this is the pi signaling it is underpowererd. running video and the lcd screen and powering a harddrive and a camera etc etc starts to add up. i havnt had any problems using a 2a supply..
|
||||
|
||||
## how can i access / delete files i have created using capture ?
|
||||
|
||||
these are saved to the user Videos folder at path : `/home/pi/Videos/recordings`
|
||||
|
||||
to get to these you will probably need a usb keyboard (a usb mouse can help too)
|
||||
|
||||
- if you are happy with command line this can be accessed by pressing ctrl+alt+f1 from keyboard (ctrl+alt+f7 will return to recur)
|
||||
- if you would rather navigate from a mouse, the recur program can be exited by pressing `.` key. moving the mouse to bottom of screen should bring up raspi toolbar where filebrowser can be opened etc
|
||||
|
||||
### note : also you can copy videos to the `internal storage` folder inside this Videos folder if space on the sd allows
|
||||
|
||||
## when i use the hdmi out on my tv/projector/screen the video keeps dropping out ?
|
||||
|
||||
yes - this seems to be the pi responding to running out of memory when two videos are loaded at 1080 resolution on some displays. to fix this you need to change the `HDMI_MODE` setting to `CEA 4 HDMI`, this sets the pi output to 720 which should play without dropout (even playing 1080 videos)
|
||||
|
||||
## my video files do not show up in the browser ?
|
||||
|
||||
at the moment recur will filter out all files that do not have a '.mp4' (recommended), '.mkv', '.mov' or '.avi' file extension in their name. (so you can not try to map .docx, .jpeg /other obvs non-video files) ..perhaps there is a better way to tell if a file is video without reading the extension ?
|
||||
|
||||
## when playing short loops <=3s or manually switching players quickly sometimes the player crashes / gets stuck `LOADING` ...
|
||||
|
||||
hmm - this does happen sometimes. in my experience pressing the video key you want to load again then the switch key will get the video playing again. I havnt been able to create crashes that require a reset, but if you do please let me know.
|
||||
|
||||
## when setting the start/end points of a clip there is a small lag before starting the next cycle .. can i improve this ?
|
||||
|
||||
this could happen because your cycle is too short to allow the NEXT video to load before NOW has finished. making your cycle a little longer could make a difference here.
|
||||
|
||||
HOWEVER, i have also noticed that this lag can happen even with longer clips sometimes and i dont know why. it seems that resetting the `start` point can sometimes fix it. i wish i could figure out why this happens... if you have any insight hmu ! i feel like this could be improved if understood ! (this could be a limitation of seeking in a H264 container ... )
|
||||
|
||||
[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_objects/keypad_action_mapping.json
|
||||
[develop page]: /documentation/develop_docs.md
|
||||
[build]: /documentation/build_docs.md
|
||||
|
||||
@@ -1,49 +0,0 @@
|
||||
# license info
|
||||
|
||||
## software
|
||||
|
||||
i have chosen to [license] r_e_c_u_r's software under [GPL-3.0] for the following reasons:
|
||||
|
||||
- i agree with the copy-left philosophy and want to empower the users, ensuring it remains (as intended) an open community project.
|
||||
- although i have not modified any existing open-source projects to create r_e_c_u_r , it does run on top of many dependencies, some of which have GPL licenses (omxplayer in particular). i wish to respect the sentiment of these developers, even if not required legally.
|
||||
- for low-level utility tools with numerous, varied uses, a permissive open-source licence like MIT can empower other developers to create and license without restrictions. however r_e_c_u_r is an embedded top level application that is unlikely to be useful in any other context
|
||||
|
||||
this licence only applies to the code in this repo. see below for a list of external programs that r_e_c_u_r uses and their respective repos/sites for more information.
|
||||
|
||||
## hardware
|
||||
|
||||
besides the application code licensed above , i would like all original hardware designs licensed under a [Creative Commons Attribution-ShareAlike 4.0 International License]. this includes the enclosure design , custom key stickers and assembly. it does __not__ apply to, and i am __not__ the copyright holder for _Raspberry Pi 3 Model B_ , _3.5 Inch TFT LCD Module For Raspberry Pi_ , _Generic Wireless USB Numeric Keypad_ or any other third party extension / accessory.
|
||||
|
||||
# program dependencies
|
||||
|
||||
dependency | use | licence
|
||||
--- | --- | ---
|
||||
rasbian | the os on pi | based on debian , made up of different licensed programs
|
||||
python | lanuage | open-source , gpl-compatible
|
||||
omxplayer | the media player | GPL-2.0
|
||||
omx python wrapper | for controlling omxplayer | LGPL
|
||||
dbus-python | dependency for omx wrapper | permissive, non-copyleft
|
||||
tkinter | the ui display | BSD
|
||||
picamera | interface with capture | BSD
|
||||
mido | interface with midi | MIT
|
||||
python-rt-midi | midi backend | MIT / modified MIT
|
||||
gpac (mp4box) | creating mp4 file | LGPL
|
||||
git | used to install and update | GPL-2.0
|
||||
|
||||
## some research / thoughts about how licences work and interact.
|
||||
|
||||
i have not modified any of the programs that are used in recur. they are all being used either under a permissive license or as part of the operating system. i can license my program however i choose, i can not license (for example) an img that contained gpl-2.0 programs with a non gpl compatible license.
|
||||
|
||||
there are no restrictions on selling a product under any of these licenses.
|
||||
|
||||
some [interesting discussion] around difference between modifying a gpl program and using one as a dependency ,
|
||||
- if it is part of the os it is ok.
|
||||
- if it is not 2-way interacting / sharing data structures etc - just an input -> output usage it is ok
|
||||
|
||||
there is no restrictions to permissive installer scripts downloading gpl licensed programs
|
||||
|
||||
[license]: ../LICENSE.md
|
||||
[GPL-3.0]: https://www.gnu.org/licenses/gpl-3.0.en.html
|
||||
[markings]: https://wiki.creativecommons.org/wiki/Marking/Creators/Marking_third_party_content
|
||||
[interesting discussion]: https://softwareengineering.stackexchange.com/questions/289785/can-i-distribute-a-gpl-executable-not-a-library-in-a-closed-source-application
|
||||
[Creative Commons Attribution-ShareAlike 4.0 International License]: https://creativecommons.org/licenses/by-sa/4.0/
|
||||
|
Before Width: | Height: | Size: 11 KiB |
@@ -1,90 +0,0 @@
|
||||
# how to use r_e_c_u_r
|
||||
|
||||
## getting started
|
||||
|
||||
- prepare a usb with some videos you want to sample
|
||||
- connect the hdmi output or composite (via [3.5mm trrs]) to the display you want to use
|
||||
- power the raspberry pi. you should see red lights on the pi board, the lcd display light up and the r_e_c_u_r splash screen on your video output
|
||||
- after a few moments the interface should appear on lcd screen.
|
||||
- the `DSPLY` key can be used to cycle through _display_modes_
|
||||
|
||||
## controls
|
||||
|
||||
![keys][keys]
|
||||
|
||||
the controls on r_e_c_u_r work by mapping `keys` to `actions`. this map can be fully customised by editing the respective json file.
|
||||
|
||||
the default layout uses a hard mapping for every key except the `<` - `>` - `■` in red. this means every other key triggers the same actions independent of the state r_e_c_u_r is in.
|
||||
|
||||
the _control keys_ ,`<` - `>` - `■` use a soft mapping. the action they trigger depends on the state , displayed on screen as `control_mode` in red.
|
||||
|
||||
these let you navigate and select folders, video files and settings when in `BROWSER` / `SETTINGS` mode, and by default control video playback (seek forward and back, pause/play) of the currently playing [NOW] sample in `SAMPLER` mode.
|
||||
|
||||
the display modes `SAMPLER`, `BROWSER` and `SETTINGS` are cycled by using the `DSPLY` key.
|
||||
|
||||
some actions are accessible through a _function_ layer, toggled by the `FN` key.
|
||||
|
||||
other controls include:
|
||||
|
||||
- `→` is used to switch the [NEXT] loaded sample to the [NOW] current sample
|
||||
- loading the sample in slot `x` into [NEXT]; where `x` is a key from `0` to `9`
|
||||
- cropping the current sample to start (`[`) or end (`]`) at the current time
|
||||
- cycling forward (`PRV BNK`) or back (`NXT BNK`) through the banks of sampleS , or clearing a bank (`CLR BNK`)
|
||||
- ~~`< SPD` and `> SPD` are used to decrease and increase the speed of the currently playing clip (note not implemented yet)~~
|
||||
|
||||
some keys are empty to leave room for future features and user experimentation. i encourage you to modify your keymap or add custom actions you would like to use.
|
||||
|
||||
## parts of the lcd display
|
||||
|
||||
![display_image][display_image]
|
||||
|
||||
## `PLAYER` display
|
||||
|
||||
![player_example][player_example]
|
||||
|
||||
this section displays information about what is in the video player : the position from the start and the end of currently playing video (yellow), what slot is playing [NOW] and what slot is loaded [NEXT]
|
||||
|
||||
## `SAMPLER` display mode
|
||||
|
||||
![sampler_example][sampler_example]
|
||||
|
||||
this is the main display mode for using r_e_c_u_r. from this view you can see details of all the samples loaded into the sampler. a `bank` contains 10 `slots` labelled `0` - `9`. pressing the corresponding `key` will load the `sample` in this `slot` to start when the current sample finishes. the `slot` of the currently playing sample is highlighted.
|
||||
|
||||
## `BROWSER` display mode
|
||||
|
||||
![browser_example][browser_example]
|
||||
|
||||
this is where you can load samples from your usb or internal Videos folder into the `SAMPLER`
|
||||
|
||||
- in this mode , the `<` and `>` keys will move the selection up and down
|
||||
- folders are displayed ending in `|` for closed and `/` for open , the depth is displayed as indentation
|
||||
- pressing the `■` key while a folder is selected toggles it open/closed
|
||||
- pressing the `■` key while a video is selected loads it into the next available slot (note : you can see the first slot a video is loaded into on right column)
|
||||
|
||||
## `SETTINGS` display mode
|
||||
|
||||
![settings_example][settings_example]
|
||||
|
||||
this is where you can configure r_e_c_u_r's settings.
|
||||
|
||||
- navigate the menu with `<` and `>` keys like above
|
||||
- pressing `■` on a setting will either cycle through it's `options` or run an `action` depending on its type
|
||||
|
||||
## `MESSAGE DISPLAY`
|
||||
|
||||
the bottom line shows the `control_mode` by default, but is also for messages:
|
||||
|
||||
- shows a yellow bar when _function_ layer is selected
|
||||
- shows a blue bar for `INFO` messages
|
||||
- shows a red bar for `ERROR` messages - full message and trace for these can be found in the log files
|
||||
|
||||
![message_example][message_example]
|
||||
|
||||
[3.5mm trrs]: https://www.adafruit.com/product/2881
|
||||
[display_image]: display_parts.jpg
|
||||
[player_example]: player_example.jpg
|
||||
[browser_example]: browser_example.jpg
|
||||
[sampler_example]: sampler_example.jpg
|
||||
[settings_example]: settings_example.jpg
|
||||
[keys]: ./vectorfront_keys.png
|
||||
[message_example]: ./message_example.jpg
|
||||
|
Before Width: | Height: | Size: 25 KiB |
|
Before Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 50 KiB |
@@ -1,72 +0,0 @@
|
||||
|
||||
# signal culture + future plans
|
||||
|
||||
this is an update on the progress and new ideas as a direct result of my residency at signal culture (sept-oct 2018) and an outline of some of my future plans with this project
|
||||
|
||||
the initial hack that became recur was in response to a specific hole in my video-hardware workflow (~dec2017). the encouragement and enthusiasm for the idea on VideoCircuits was enough to motivate me to tidy and share recur_v1 on github/fb (may2018). a dozen or so people building this and talking about it was satisfying but i had no immediate plans to continue developing for it, besides maintenance, bugfixes, simple user requests etc...
|
||||
|
||||
however after being invited to a 3-week toolmaker residency in Owego NY i felt encouraged and enabled to explore some bigger new ideas for the instrument.
|
||||
|
||||
the name __r_e_c_u_r__ refers to the video sampling/looping feature at the core of this device. as the scope of what it can do is expanded, naming and organizing the (optional) extensions helps define their use.
|
||||
|
||||
## c_a_p_t_u_r
|
||||
|
||||
_an optional extension for live sampling through the pi camera input_
|
||||
|
||||
this was partially included in v1 although limited to inputs from the rpi-camera. while at SC i had the chance to try it with a piCaptureSd1 hat which allows sd video input from composite, component and svideo. some settings have been added to improve the captur image.
|
||||
|
||||
- TODO : experiment with capture from usb-webcam and cheep usb capure cards. with prob be lowfi and or laggy but also still fun.
|
||||
|
||||
## c_o_n_j_u_r
|
||||
|
||||
_an alternative openframeworks backend for extended video control and glsl-shader integration_
|
||||
|
||||
this is the largest addition from the v1 release. although omxplayer is a gpu-accelerated videoplayer that is stable and forgiving it is designed as a mediaplayer for watching films etc, not as a platform for creative coding. r_e_c_u_r can sequence omxplayer to playback samples exactly how they are (and seek etc for sublooping) .
|
||||
|
||||
openframeworks is more suited for video manipulation and opens a lot of possibilities to how the samples can be played.
|
||||
|
||||
### shaders
|
||||
|
||||
a few other projects have been based around using a raspberry pi as a visual instrument in another way - instead of focusing on video-clip playback, these play glsl-shaders, fragments of code that run directly on the gpu, allowing the creation of interesting digital visual generation.
|
||||
|
||||
although generated in real time, shader-playback is similar to video playback in that you can select a prepared source (video-file or shader-code) and perform with it - trigger when to start and stop, interact with parameters live (video start/stop/alpha/position or user defined shader parameters).
|
||||
|
||||
recur already has the ui to browse folders and files, select and map them, to display the relevant infomation, and openframeworks has the ability to load and play shaders much like you would load and play videos. this seemed like a logical and powerful extension to the sampler core.
|
||||
|
||||
## i_n_c_u_r
|
||||
|
||||
_become subject to (something unwelcome or unpleasant) as a result of one's own behavior or actions_
|
||||
|
||||
this is related to extending recurs sequencing/performability by exposing its controls to external manipulation.
|
||||
|
||||
usb-midi control over recur actions was in the v1 release including the first example of continuos inputs via midi-cc. this gives control over parameters which otherwise are difficult to interact with on a numpad alone (video/preview alpha). as the amount of control increases so does the need for continuous inputs.
|
||||
|
||||
at SC i created a circuit that allows 8 analog inputs (4 pots, 4 0-5v cv) and serial(din)-midi into recur.
|
||||
|
||||
## modulation
|
||||
|
||||
having defined these different areas of the __r_e_c_u_r__ video instrument, we also have created some powerful combinations (some are trivial/obvious like _i_n_c_u_r_ + _r_e_c_u_r_ for external sequencing of video samples, or _c_a_p_t_u_r_ + _r_e_c_u_r_ for recording live input directly followed by sampling it) others include:
|
||||
|
||||
- _i_n_c_u_r_ + _c_o_n_j_u_r_ : shaders can be written to respond in real time based on user input. usually this is in the form of the mouse-x&y position. for recur i have defined normalized parameter inputs that can be used to manipulate any portion of the glsl code. having knobs or cv control over these parameters greatly increases the playablity of the shaders.
|
||||
|
||||
- _r_e_c_u_r_ + _c_o_n_j_u_r_ : at first i was thinking of video-files and glsl-shaders as separate sources for creating video. however then i discovered how you can also _use_ a glsl-shader to process a video-file (shaders can take textures as input, one of which can be a video), leading me to make the distinction in recur between _generating shaders_ and _processing shaders_ .
|
||||
|
||||
- c_a_p_t_u_r_ + _c_o_n_j_u_r_ : not only can _processing shaders_ accept video as a texture-input, they can also take texture from a live input (a camera or capture card for example). this means recur can also be used to process live video input in (almost) real time.
|
||||
|
||||
## direction
|
||||
|
||||
what started as a simple solution for seamless prerecorded video playback is starting to look something closer to the video equivalent to audios groovebox - where a (good) groovebox may include sampling, sequencing, synth-presets, audio-effects and live-input/effect-through , this new __r_e_c_u_r__ + _i_n_c_u_r_ + _c_o_n_j_u_r_ + _c_a_p_t_u_r_ may come close to a fully open, customizable and diy digital video groovebox.
|
||||
|
||||
## future plans
|
||||
|
||||
much of what is outlined above is in varying stages of development. proofs of concepts have been completed, but lots of the new (esp openframework) code is buggy and needs tidying and testing. for example my incur circuit is thrown together on perf-board and soldered straight to the pi...
|
||||
|
||||
here are some things im thinking about doing/trying from here:
|
||||
|
||||
- get this messy web of features polished enough that its worth creating a new sd img so others can flash and try these software updates out (im including a switch between omxplayer backend to retain existing functionality, and the option to try and test the more experimental openframeworks backend )
|
||||
- investigate sending external control (midi + analog readings) straight to openframeworks rather than python. this probably involves sacrificing some of the customizablity of mapping any input to any action for a performance increase (as it is i doubt my python code is fast enough to respond to eurorack without noticeable stepping...)
|
||||
- create another video demo to show these new aspects
|
||||
- create a new physical version : this time using a raspi compute board on a custom pcb for eurorack standard with cv/trigger in circuits , faceplate and using the numpad detached via wifi (probably 20hp)
|
||||
- hopefully have the software running stable enough and a buildpipe thats slightly more optimized than 'wait 9 hours for the case to print' so that small assembled runs become feasible for getting these to non-diy-ers
|
||||
- investigate the feasibility of creating even more accessible stripped version (composite only?) on the raspi0 (probably not feasible but iv been constantly surprized at what gpu accelerated sbc's can do!)
|
||||
- collaborate on creating some workshops for: _intro to video hardware instruments : some hacky open diy projects_ ...
|
||||
|
Before Width: | Height: | Size: 102 KiB |
|
Before Width: | Height: | Size: 36 KiB |
|
Before Width: | Height: | Size: 74 KiB |
@@ -1,3 +1,4 @@
|
||||
|
||||
keycode 82=a
|
||||
keycode 86=b
|
||||
keycode 22=c
|
||||
@@ -12,12 +13,24 @@ keycode 106=j
|
||||
keycode 80=k
|
||||
keycode 84=l
|
||||
keycode 88=m
|
||||
!note: the code will map 000 to n in the code
|
||||
!note: the python code will map 000 to n in the code
|
||||
keycode 77=o
|
||||
keycode 79=p
|
||||
keycode 83=q
|
||||
keycode 87=r
|
||||
keycode 90=s
|
||||
|
||||
keycode 118=BackSpace
|
||||
!note: for developing im going to use insert for backspacing
|
||||
!note some keypads calculate numlock + keys internally needing more things to be mapped
|
||||
keycode 60=i
|
||||
|
||||
keycode 112=f
|
||||
keycode 114=g
|
||||
keycode 117=h
|
||||
keycode 119=i
|
||||
keycode 111=k
|
||||
keycode 116=m
|
||||
keycode 110=p
|
||||
keycode 113=q
|
||||
keycode 115=r
|
||||
keycode 118=s
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
|
||||
## initial set up ; flashing fresh sd, keyboard , wifi , updates etcs
|
||||
|
||||
- downloaded the latest (~~2017-11-29~~ 2018-04-18) raspbian-raspbian-lite image from offical site.
|
||||
- downloaded the latest (~~2017-11-29~~ ~~2018-04-18~~ [2019-07-10]) raspbian-raspbian-lite image from offical site.
|
||||
|
||||
- flashed it to my sd using etcher
|
||||
|
||||
@@ -39,7 +39,7 @@ then `sudo apt update` and `sudo apt upgrade` , sudo reboot
|
||||
|
||||
## quiet x and run my launcher script on boot:
|
||||
|
||||
- used `sudo nano ~/.config/lxsession/LXDE-pi/autostart` to add these lines :
|
||||
- used ~~sudo nano ~/.config/lxsession/LXDE-pi/autostart~~ `sudo nano/etc/xdg/lxsession/LXDE-pi/autostart` to add these lines :
|
||||
```
|
||||
@unclutter -display :0 -idle 0 -root -noevents
|
||||
@xset s off
|
||||
@@ -56,12 +56,19 @@ these are suppose to stop screensaver / hide cursor / remove on screen power war
|
||||
|
||||
- created internal storage folder in ~/Videos
|
||||
|
||||
- set a custom splash screen by replacing splash.png an image at `/usr/share/plymouth/themes/pix/splash.png`with my original. i made a copy of the original, and then copied ~~my own from a flash stick...~~ from `~/r_e_c_u_r/documentation/splash.png`
|
||||
- set a custom splash screen by replacing splash.png an image at:
|
||||
`sudo cp ~/r_e_c_u_r/documentation/splash.png /usr/share/plymouth/themes/pix/splash.png`
|
||||
|
||||
## for piCaptureSd1
|
||||
|
||||
need to install : `sudo apt-get install python3-smbus` and `sudo pip3 install pivideo`
|
||||
|
||||
## lcd display drivers
|
||||
|
||||
these are the drivers for the waveshare displays that work on the cheep lcd i ordered online ( [LCD-show-170703] ).~~
|
||||
|
||||
`git clone https://github.com/waveshare/LCD-show`
|
||||
|
||||
my screen only needs the LCD35-show-180 and LCD-hdmi scripts. after running both of these scripts the drivers can be deleted since the recur code then handles the switching. (or keep em if you wanna flip the screen or try calibrating the touch screen)
|
||||
|
||||
|
||||
@@ -88,11 +95,22 @@ a line to Popen inside the python code toggles this on and off from here.
|
||||
|
||||
## lines added to config.txt
|
||||
|
||||
the fastest way is to copy the config.txt i included in the repo:
|
||||
|
||||
`sudo cp ~/r_e_c_u_r/dotfiles/config.txt /boot/config.txt`
|
||||
|
||||
be careful though - messing with the config.txt is the easiest way to brick the image from my experience ...
|
||||
|
||||
or do these :
|
||||
|
||||
- commenting out hdmi_force to allow composite output: `#hdmi_force_hotplug=1`
|
||||
|
||||
- add these lines to the config:
|
||||
```
|
||||
## gives more memory to the gpu for playing 1080 videos (might need to adjust this when using older pis with less memory)
|
||||
gpu_mem_256=120
|
||||
gpu_mem_512=200
|
||||
gpu_mem_1024=448
|
||||
gpu_mem=600
|
||||
## enables the raspi camera
|
||||
start_x=1
|
||||
@@ -109,8 +127,64 @@ sdtv_aspect=1
|
||||
|
||||
## changes to the cmdline.txt
|
||||
|
||||
add to end of cmdline.txt `sudo nano /boot/cmdline.txt` :
|
||||
`quiet splash logo.nologo plymouth.ignore-serial-consoles` for quiet boot with splash screen
|
||||
|
||||
|
||||
## installing openframeworks and setting up conjur app
|
||||
|
||||
`wget "https://openframeworks.cc/versions/v0.10.1/of_v0.10.1_linuxarmv6l_release.tar.gz"`
|
||||
|
||||
- `mkdir openFrameworks` and `tar vxfz of_v0.10.1_linuxarmv6l_release.tar.gz -C openFrameworks --strip-components 1`
|
||||
- `cd openFrameworks/scripts/linux/debian/` & `sudo ./install_dependencies.sh`
|
||||
- `make Release -C ~/openFrameworks/libs/openFrameworksCompiled/project`
|
||||
- `cd ~/openFrameworks/apps/myApps/` and `git clone https://github.com/langolierz/c_o_n_j_u_r`
|
||||
- `cd ~/openFrameworks/addons/` and `git clone https://github.com/langolierz/ofxOMXCamera.git` (will swap this out for main once/if my edits work and get in)
|
||||
- `git clone https://github.com/langolierz/ofxVideoArtTools`
|
||||
- `git clone https://github.com/timscaffidi/ofxVideoRecorder` and its depend : `sudo apt-get install ffmeg`
|
||||
- `git clone https://github.com/jvcleave/ofxOMXPlayer` and install depends : `cd ofxOMXPlayer; ./install_depends.sh` and checkout last stable version `git checkout c826828`
|
||||
- `git clone https://github.com/kashimAstro/ofxGPIO`
|
||||
- `git clone https://github.com/danomatika/ofxMidi`
|
||||
- `git clone https://github.com/jeffcrouse/ofxJSON`
|
||||
- (install dependances for of ??)
|
||||
- `make ~/openFrameworks/apps/myApps/c_o_n_j_u_r`
|
||||
|
||||
|
||||
## installing packages and apps
|
||||
|
||||
- `sudo pip3 install Adafruit_GPIO Adafruit_MCP3008 RPi.GPIO pivideo python-osc` (tried to install threading but didnt work...)
|
||||
- (note atleast pivideo needs to be installed with sudo.), also needs `sudo pip3 install serial``
|
||||
- pip3 install gitpython
|
||||
- `sudo apt-get install glslviewer`
|
||||
|
||||
### installing ttymidi :
|
||||
- `wget http://www.varal.org/ttymidi/ttymidi.tar.gz` and `tar -zxvf ttymidi.tar.gz`
|
||||
- `cd ttymidi` and `sudo nano Makefile` then add `-pthread` after -lasound ...
|
||||
- then `sudo make` then `sudo make install`
|
||||
|
||||
## setup:
|
||||
|
||||
need to delete the old settings : `rm json_objects/settings.json` ~~and create a `Shaders` folder, also need to put default.vert shader in there for any shaders to work !~~
|
||||
|
||||
i think will need to turn on the i2c and serial interfacing... (and maybe that serial switvhing thing .. oh and the clocking for midi serial ... )
|
||||
|
||||
NOTE: need to disable console logging and enable seial through the raspi-config !
|
||||
|
||||
these amount to the following in the config:
|
||||
```
|
||||
dtparam=i2c_arm=on
|
||||
dtparam=spi=on
|
||||
enable_uart=1
|
||||
```
|
||||
plus this for serial midi :
|
||||
```
|
||||
#setup midi over serial
|
||||
dtoverlay=pi3-miniuart-bt
|
||||
dtoverlay=midi-uart0
|
||||
```
|
||||
|
||||
|
||||
|
||||
# wjhat follows is info, not instructions for setup:
|
||||
|
||||
## key mapping and the launcher script:
|
||||
@@ -131,16 +205,24 @@ In order to have the custom keymap work on startup we have added the line `xmodm
|
||||
|
||||
first remove my wifi password ! (and git profile if present)
|
||||
|
||||
`sudo nano /etc/wpa_supplicant/wpa_supplicant.conf`
|
||||
`git config --global user.email ""`
|
||||
`git config --global user.name ""`
|
||||
|
||||
i exported the image using unix command `dd` from the raspberry pi i wanted an image of.
|
||||
|
||||
i had some success using [pishrink], following the instructions on readme exactly , i managed to reduce a 3.8gb image down to 2.9gb and then zipped down to 1.15gb, (this would be more useful with larger cards though).
|
||||
|
||||
- the flow is using dd to copy the image from the pi to an external drive `sudo dd bs=4M if=/dev/mmcblk0 of=/media/pi/FLASH DRIVE/recur.img`
|
||||
|
||||
- then use pishrink to reduce this image `sudo pishrink.sh -s recur.img` it fails the first time but works straight after - not sure why !
|
||||
- then use pishrink to reduce this image `sudo pishrink.sh recur.img` it fails the first time but works straight after - not sure why !
|
||||
|
||||
i had a `Unexpected Inconsistency` problem with my image which [this link] helped solve.
|
||||
|
||||
- then gzip to zip this : `sudo gzip recur.img`
|
||||
|
||||
[2019-07-10]: https://www.raspberrypi.org/downloads/
|
||||
[pishrink]: https://github.com/Drewsif/PiShrink
|
||||
[LCD-show-170703]: www.waveshare.com/w/uplosd/0/00/LCD-show-170703.tar.gz
|
||||
[raspi2fb]: https://github.com/AndrewFromMelbourne/raspi2fb
|
||||
[this link]: https://chrisdown.name/2011/06/01/fsck-partitions-inside-an-image.html
|
||||
|
||||
74
dotfiles/config.txt
Executable file
@@ -0,0 +1,74 @@
|
||||
# For more options and information see
|
||||
# http://www.raspberrypi.org/documentation/configuration/config-txt.md
|
||||
# Some settings may impact device functionality. See link above for details
|
||||
|
||||
## gives more memory to the gpu for playing 1080 videos (might need to adjust this when using older pis with less memory)
|
||||
gpu_mem_256=120
|
||||
gpu_mem_512=200
|
||||
gpu_mem_1024=448
|
||||
gpu_mem=448
|
||||
## enables the raspi camera
|
||||
start_x=1
|
||||
##fixes bug with playback freezing
|
||||
audio_pwm_mode=0
|
||||
|
||||
# uncomment if you get no picture on HDMI for a default "safe" mode
|
||||
#hdmi_safe=1
|
||||
|
||||
# uncomment this if your display has a black border of unused pixels visible
|
||||
# and your display can output without overscan
|
||||
#disable_overscan=1
|
||||
|
||||
# uncomment the following to adjust overscan. Use positive numbers if console
|
||||
# goes off screen, and negative if there is too much border
|
||||
#overscan_left=16
|
||||
#overscan_right=16
|
||||
#overscan_top=16
|
||||
#overscan_bottom=16
|
||||
|
||||
# uncomment to force a console size. By default it will be display's size minus
|
||||
# overscan.
|
||||
#framebuffer_width=1280
|
||||
#framebuffer_height=720
|
||||
|
||||
# uncomment if hdmi display is not detected and composite is being output
|
||||
#hdmi_force_hotplug=1
|
||||
|
||||
# uncomment to force a specific HDMI mode (this will force VGA)
|
||||
#hdmi_group=1
|
||||
#hdmi_mode=1
|
||||
|
||||
# uncomment to force a HDMI mode rather than DVI. This can make audio work in
|
||||
# DMT (computer monitor) modes
|
||||
#hdmi_drive=2
|
||||
|
||||
# uncomment to increase signal to HDMI, if you have interference, blanking, or
|
||||
# no display
|
||||
#config_hdmi_boost=4
|
||||
|
||||
# uncomment for composite PAL
|
||||
sdtv_mode=00
|
||||
sdtv_aspect=1
|
||||
#uncomment to overclock the arm. 700 MHz is the default.
|
||||
#arm_freq=800
|
||||
|
||||
# Uncomment some or all of these to enable the optional hardware interfaces
|
||||
dtparam=i2c_arm=on
|
||||
#dtparam=i2s=on
|
||||
dtparam=spi=on
|
||||
enable_uart=1
|
||||
# Uncomment this to enable the lirc-rpi module
|
||||
#dtoverlay=lirc-rpi
|
||||
|
||||
#setup midi over serial
|
||||
dtoverlay=pi3-miniuart-bt
|
||||
dtoverlay=midi-uart0
|
||||
|
||||
|
||||
## switch for enabling lcd screen (the next line is being used even if its commented out)
|
||||
##no_waveshare_overlay
|
||||
|
||||
# Additional overlays and parameters are documented /boot/overlays/README
|
||||
|
||||
# Enable audio (loads snd_bcm2835)
|
||||
dtparam=audio=on
|
||||
63
dotfiles/documenting_steps_from_old_img_to_working_here.md
Normal file
@@ -0,0 +1,63 @@
|
||||
# documenting all the steps from old img of recur to working with new features
|
||||
|
||||
- gonna start with a `sudo raspi-update` and `sudo apt-get update -y; sudo apt-get upgrade -y`
|
||||
|
||||
## setup
|
||||
|
||||
to set up for adding things i switched the program to dev mode , connected to the wifi and git fetch from ~/r_e_c_u_r ,
|
||||
|
||||
## installing openframeworks and setting up conjur app
|
||||
|
||||
i had of10 zipped on a flashdrive already (wget errored about insecure connection when i tried to download from the pi..) , so
|
||||
|
||||
- `sudo cp /media/pi/5EB5-664C/of_v0.10.0_linuxarmv6l_release.tar.gz ~/`
|
||||
- `mkdir openFrameworks` and `tar vxfz of_v0.10.0_linuxarmv6l_release.tar.gz -C openFrameworks --strip-components 1`
|
||||
- `cd openFrameworks/scripts/linux/debian/` & `sudo ./install_dependencies.sh`
|
||||
- and also `sudo apt-get upgrade -y; sudo apt-get update` (these took ageeees ! didnt even finish.. will come back to this)
|
||||
- `make Release -C ~/openFrameworks/libs/openFrameworksCompiled/project`
|
||||
- `cd ~/openFrameworks/apps/myApps/` and `git clone https://github.com/langolierz/c_o_n_j_u_r.git`
|
||||
- `cd ~/openFrameworks/addons/` and `git clone https://github.com/langolierz/ofxRPiCameraVideoGrabber` (will swap this out for main once/if my edits work and get in)
|
||||
- NOTE also gotta checkout the stretch branch : `git checkout stretch`
|
||||
- `make ~/openFrameworks/apps/myApps/c_o_n_j_u_r
|
||||
|
||||
|
||||
## installing packages and apps
|
||||
|
||||
- `sudo pip3 install Adafruit_GPIO Adafruit_MCP3008 RPi.GPIO pivideo python-osc` (tried to install threading but didnt work...)
|
||||
- (note atleast pivideo needs to be installed with sudo.), also needs `sudo pip3 install serial``
|
||||
- pip3 install gitpython
|
||||
- `sudo apt-get install glslviewer`
|
||||
|
||||
### installing ttymidi :
|
||||
- `wget http://www.varal.org/ttymidi/ttymidi.tar.gz` and `tar -zxvf ttymidi.tar.gz`
|
||||
- `cd ttymidi` and `sudo nano Makefile` then add `-pthread` after -lasound ...
|
||||
- then `sudo make` then `sudo make install`
|
||||
|
||||
## setup:
|
||||
|
||||
need to delete the old settings : `rm json_objects/settings.json` and create a `Shaders` folder, also need to put default.vert shader in there for any shaders to work !
|
||||
|
||||
i think will need to turn on the i2c and serial interfacing... (and maybe that serial switvhing thing .. oh and the clocking for midi serial ... )
|
||||
|
||||
NOTE: need to disable console logging and enable seial through the raspi-config !
|
||||
|
||||
these amount to the following in the config:
|
||||
```
|
||||
dtparam=i2c_arm=on
|
||||
dtparam=spi=on
|
||||
enable_uart=1
|
||||
```
|
||||
plus this for serial midi :
|
||||
```
|
||||
#setup midi over serial
|
||||
dtoverlay=pi3-miniuart-bt
|
||||
dtoverlay=midi-uart0
|
||||
```
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
17
dotfiles/python_for_shutting_down_osc_server.py
Normal file
@@ -0,0 +1,17 @@
|
||||
from pythonosc import osc_message_builder
|
||||
from pythonosc import udp_client
|
||||
from pythonosc import dispatcher
|
||||
import argparse
|
||||
|
||||
def setup_osc_client():
|
||||
client_parser = argparse.ArgumentParser()
|
||||
client_parser.add_argument("--ip", default="127.0.0.1", help="the ip")
|
||||
client_parser.add_argument("--port", type=int, default=9000, help="the port")
|
||||
|
||||
client_args = client_parser.parse_args()
|
||||
|
||||
return udp_client.SimpleUDPClient(client_args.ip, client_args.port)
|
||||
|
||||
client = setup_osc_client()
|
||||
|
||||
client.send_message("/shutdown", True)
|
||||
|
Before Width: | Height: | Size: 966 KiB After Width: | Height: | Size: 966 KiB |
BIN
enclosure/keystickers.png
Normal file
|
After Width: | Height: | Size: 36 KiB |
@@ -16,7 +16,10 @@
|
||||
id="svg8"
|
||||
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
|
||||
sodipodi:docname="keystickers.svg"
|
||||
enable-background="new">
|
||||
enable-background="new"
|
||||
inkscape:export-filename="C:\r_e_c_u_r\enclosure\keystickers.png"
|
||||
inkscape:export-xdpi="160"
|
||||
inkscape:export-ydpi="160">
|
||||
<defs
|
||||
id="defs2" />
|
||||
<sodipodi:namedview
|
||||
@@ -27,7 +30,7 @@
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.3232521"
|
||||
inkscape:cx="189.94766"
|
||||
inkscape:cx="185.78665"
|
||||
inkscape:cy="997.28181"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer1"
|
||||
@@ -45,7 +48,7 @@
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
@@ -88,15 +91,15 @@
|
||||
x="19.363272"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.43036103px;line-height:1.25;font-family:'Courier New';-inkscape-font-specification:'Courier New';letter-spacing:0.10847916px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
xml:space="preserve"><tspan
|
||||
style="fill:#000000;stroke-width:0.2"
|
||||
style="fill:#ffffff;stroke-width:0.2"
|
||||
id="tspan5460"
|
||||
y="13.338742"
|
||||
x="19.363272"
|
||||
sodipodi:role="line"><tspan
|
||||
id="tspan5458"
|
||||
style="font-weight:normal;letter-spacing:0.10847916px;fill:#000000;stroke-width:0.2;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
style="font-weight:normal;letter-spacing:0.10847916px;fill:#ffffff;stroke-width:0.2;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
y="13.338742"
|
||||
x="19.363272">CLR BNK</tspan></tspan></text>
|
||||
x="19.363272">SHUTDWN</tspan></tspan></text>
|
||||
<path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path3725-6-8-02"
|
||||
@@ -146,11 +149,11 @@
|
||||
x="19.363272"
|
||||
y="13.338742"
|
||||
id="tspan5451"
|
||||
style="fill:#000000;stroke-width:0.2"><tspan
|
||||
style="fill:#ffffff;stroke-width:0.2"><tspan
|
||||
x="19.363272"
|
||||
y="13.338742"
|
||||
style="font-weight:normal;letter-spacing:0.10847916px;fill:#000000;stroke-width:0.2;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="tspan5449">CLR BNK</tspan></tspan></text>
|
||||
style="font-weight:normal;letter-spacing:0.10847916px;fill:#ffffff;stroke-width:0.2;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="tspan5449">FEEDBCK</tspan></tspan></text>
|
||||
</g>
|
||||
<g
|
||||
transform="translate(24.883332,42.37619)"
|
||||
@@ -168,18 +171,18 @@
|
||||
transform="scale(0.93646137,1.0678497)"
|
||||
id="text5423"
|
||||
y="13.338742"
|
||||
x="19.363272"
|
||||
x="20.29318"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.43036103px;line-height:1.25;font-family:'Courier New';-inkscape-font-specification:'Courier New';letter-spacing:0.10847916px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
xml:space="preserve"><tspan
|
||||
style="fill:#000000;stroke-width:0.2"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:0.2"
|
||||
id="tspan5421"
|
||||
y="13.338742"
|
||||
x="19.363272"
|
||||
x="20.29318"
|
||||
sodipodi:role="line"><tspan
|
||||
id="tspan5419"
|
||||
style="font-weight:normal;letter-spacing:0.10847916px;fill:#000000;stroke-width:0.2;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
style="font-weight:normal;letter-spacing:0.10847916px;fill:#ffffff;fill-opacity:1;stroke-width:0.2;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
y="13.338742"
|
||||
x="19.363272">CLR BNK</tspan></tspan></text>
|
||||
x="20.29318">FRAMES</tspan></tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6.3499999px;line-height:1.25;font-family:'Courier New';-inkscape-font-specification:'Courier New';letter-spacing:0px;word-spacing:0px;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
||||
@@ -650,7 +653,7 @@
|
||||
id="tspan5567"
|
||||
style="font-weight:normal;letter-spacing:0.10847916px;fill:#000000;stroke-width:0.2;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
y="13.338742"
|
||||
x="19.363272">CLR BNK</tspan></tspan></text>
|
||||
x="19.363272">CLR BK</tspan></tspan></text>
|
||||
</g>
|
||||
<g
|
||||
transform="translate(11.383333,22.37619)"
|
||||
@@ -699,6 +702,37 @@
|
||||
y="13.414604"
|
||||
style="font-weight:normal;letter-spacing:0.10847916px;fill:#ffffff;stroke-width:0.2;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="tspan1093">CLR ]</tspan></tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:6.3499999px;line-height:1.25;font-family:'Courier New';-inkscape-font-specification:'Courier New';letter-spacing:0px;word-spacing:0px;display:inline;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.26458332"
|
||||
x="34.217781"
|
||||
y="10.172296"
|
||||
id="text1049-6"><tspan
|
||||
sodipodi:role="line"
|
||||
x="34.217781"
|
||||
y="10.172296"
|
||||
id="tspan1047-7"
|
||||
style="font-size:4.93888903px;line-height:1;stroke-width:0.26458332"><tspan
|
||||
x="34.217781"
|
||||
y="10.172296"
|
||||
style="font-weight:bold;font-size:4.93888903px;line-height:1;fill:#ffffff;stroke-width:0.26458332"
|
||||
id="tspan1045-3">⦿</tspan></tspan></text>
|
||||
<text
|
||||
xml:space="preserve"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.43036103px;line-height:1.25;font-family:'Courier New';-inkscape-font-specification:'Courier New';letter-spacing:0.10847916px;word-spacing:0px;display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
x="37.195122"
|
||||
y="13.395443"
|
||||
id="text1097-2"
|
||||
transform="scale(0.93646137,1.0678497)"><tspan
|
||||
sodipodi:role="line"
|
||||
x="37.195122"
|
||||
y="13.395443"
|
||||
id="tspan1095-3"
|
||||
style="stroke-width:0.2"><tspan
|
||||
x="37.195122"
|
||||
y="13.395443"
|
||||
style="font-weight:normal;letter-spacing:0.10847916px;fill:#ffffff;stroke-width:0.2;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="tspan1093-7">REC</tspan></tspan></text>
|
||||
</g>
|
||||
<g
|
||||
id="g4563"
|
||||
@@ -945,5 +979,22 @@
|
||||
style="font-weight:normal;letter-spacing:0.10847916px;fill:#000000;stroke-width:0.2;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
id="tspan5467">CLR BNK</tspan></tspan></text>
|
||||
</g>
|
||||
<text
|
||||
inkscape:label="text5373"
|
||||
transform="scale(0.93646138,1.0678497)"
|
||||
id="text5414-5"
|
||||
y="52.974205"
|
||||
x="32.768494"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;font-size:2.43036103px;line-height:1.25;font-family:'Courier New';-inkscape-font-specification:'Courier New';letter-spacing:0.10847916px;word-spacing:0px;display:inline;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.2;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
xml:space="preserve"><tspan
|
||||
style="fill:#ffffff;stroke-width:0.2"
|
||||
id="tspan5412-2"
|
||||
y="52.974205"
|
||||
x="32.768494"
|
||||
sodipodi:role="line"><tspan
|
||||
id="tspan5410-8"
|
||||
style="font-weight:normal;letter-spacing:0.10847916px;fill:#ffffff;stroke-width:0.2;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
y="52.974205"
|
||||
x="32.768494">SHADER</tspan></tspan></text>
|
||||
</g>
|
||||
</svg>
|
||||
|
Before Width: | Height: | Size: 43 KiB After Width: | Height: | Size: 46 KiB |
BIN
enclosure/vectorfront.png
Normal file
|
After Width: | Height: | Size: 108 KiB |
|
Before Width: | Height: | Size: 111 KiB After Width: | Height: | Size: 111 KiB |
|
Before Width: | Height: | Size: 32 KiB After Width: | Height: | Size: 32 KiB |
|
Before Width: | Height: | Size: 97 KiB After Width: | Height: | Size: 97 KiB |
BIN
enclosure/vectorfront_keys.png
Normal file
|
After Width: | Height: | Size: 74 KiB |
@@ -18,7 +18,7 @@
|
||||
version="1.1"
|
||||
inkscape:version="0.92.2 (5c3e80d, 2017-08-06)"
|
||||
sodipodi:docname="vectorfront_keys.svg"
|
||||
inkscape:export-filename="D:\Leo\gitrepos\r_e_c_u_r\documentation\vectorfront.png"
|
||||
inkscape:export-filename="C:\r_e_c_u_r\enclosure\vectorfront_keys.png"
|
||||
inkscape:export-xdpi="160"
|
||||
inkscape:export-ydpi="160">
|
||||
<defs
|
||||
@@ -1008,11 +1008,11 @@
|
||||
borderopacity="1.0"
|
||||
inkscape:pageopacity="0.0"
|
||||
inkscape:pageshadow="2"
|
||||
inkscape:zoom="2.8284271"
|
||||
inkscape:cx="442.03064"
|
||||
inkscape:cy="305.64608"
|
||||
inkscape:zoom="1.32"
|
||||
inkscape:cx="340.17232"
|
||||
inkscape:cy="200.36895"
|
||||
inkscape:document-units="mm"
|
||||
inkscape:current-layer="layer2"
|
||||
inkscape:current-layer="g6712"
|
||||
showgrid="false"
|
||||
showborder="true"
|
||||
showguides="true"
|
||||
@@ -1120,7 +1120,7 @@
|
||||
<dc:format>image/svg+xml</dc:format>
|
||||
<dc:type
|
||||
rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
|
||||
<dc:title></dc:title>
|
||||
<dc:title />
|
||||
</cc:Work>
|
||||
</rdf:RDF>
|
||||
</metadata>
|
||||
@@ -1695,39 +1695,39 @@
|
||||
id="g6752-1"
|
||||
style="stroke-width:1.01137185">
|
||||
<flowRoot
|
||||
transform="translate(14.453125,-13.28125)"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0.01%;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:#000000;stroke-width:1.01137185px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
transform="translate(14.453125,-9.4886057)"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0.01%;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.01137185px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="flowRoot6734-69"
|
||||
xml:space="preserve"><flowRegion
|
||||
id="flowRegion6736-8"
|
||||
style="fill:#000000;stroke:#000000;stroke-width:1.01137185px"><rect
|
||||
style="text-align:center;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:1.01137185px"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.01137185px;stroke-opacity:1"><rect
|
||||
style="text-align:center;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.01137185px;stroke-opacity:1"
|
||||
y="578.89166"
|
||||
x="263.90564"
|
||||
height="22.551781"
|
||||
width="44.210621"
|
||||
id="rect6738-1" /></flowRegion><flowPara
|
||||
style="font-size:20px;line-height:1.25;text-align:center;text-anchor:middle;fill:#000000;stroke:#000000;stroke-width:1.01137185px"
|
||||
id="flowPara6740-33">a</flowPara></flowRoot> <path
|
||||
style="font-size:17.69900894px;line-height:1.25;text-align:center;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:#ffffff;stroke-width:1.01137185px;stroke-opacity:1"
|
||||
id="flowPara6740-33">⦿</flowPara></flowRoot> <path
|
||||
inkscape:connector-curvature="0"
|
||||
id="path6742-37"
|
||||
d="m 283.69141,593.61167 h 33.39844"
|
||||
style="fill:url(#linearGradient4761);fill-rule:evenodd;stroke:url(#linearGradient4761);stroke-width:1.01137185px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<flowRoot
|
||||
transform="translate(169.59514,-8.679611)"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0.01%;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01137185px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0.01%;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:#5fffff;stroke-width:0;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="flowRoot6744-9"
|
||||
xml:space="preserve"><flowRegion
|
||||
id="flowRegion6746-86"
|
||||
style="fill:#000000;stroke-width:1.01137185px"><rect
|
||||
style="text-align:center;text-anchor:middle;fill:#000000;stroke-width:1.01137185px"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke:#5fffff;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"><rect
|
||||
style="text-align:center;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:#5fffff;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
y="601.71594"
|
||||
x="108.77582"
|
||||
height="15.195512"
|
||||
width="44.246517"
|
||||
id="rect6748-7" /></flowRegion><flowPara
|
||||
style="font-size:10px;line-height:1.25;text-align:center;text-anchor:middle;fill:#000000;stroke:none;stroke-width:1.01137185px"
|
||||
id="flowPara6750-0">cmd</flowPara></flowRoot> </g>
|
||||
style="font-size:10px;line-height:1.25;text-align:center;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:#5fffff;stroke-width:0;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="flowPara6750-0">rec</flowPara></flowRoot> </g>
|
||||
<g
|
||||
transform="translate(68.076575,62.410189)"
|
||||
id="g6752-84"
|
||||
@@ -1883,7 +1883,9 @@
|
||||
<g
|
||||
transform="translate(0.19736362,194.84494)"
|
||||
id="g6752-2"
|
||||
style="stroke-width:1.01137185">
|
||||
style="stroke-width:1.01137185"
|
||||
inkscape:export-xdpi="160"
|
||||
inkscape:export-ydpi="160">
|
||||
<flowRoot
|
||||
transform="translate(14.453125,-13.28125)"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0.01%;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01137185px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
@@ -1905,19 +1907,19 @@
|
||||
style="fill:url(#linearGradient4761);fill-rule:evenodd;stroke:url(#linearGradient4761);stroke-width:1.01137185px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<flowRoot
|
||||
transform="translate(169.59514,-12.429611)"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0.01%;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01137185px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0.01%;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.01137185px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="flowRoot6744-22"
|
||||
xml:space="preserve"><flowRegion
|
||||
id="flowRegion6746-16"
|
||||
style="fill:#000000;stroke-width:1.01137185px"><rect
|
||||
style="text-align:center;text-anchor:middle;fill:#000000;stroke-width:1.01137185px"
|
||||
style="fill:#ffffff;fill-opacity:1;stroke-width:1.01137185px"><rect
|
||||
style="text-align:center;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke-width:1.01137185px"
|
||||
y="601.71594"
|
||||
x="108.77582"
|
||||
height="15.195512"
|
||||
width="44.246517"
|
||||
id="rect6748-30" /></flowRegion><flowPara
|
||||
style="font-size:10px;line-height:1.25;text-align:center;text-anchor:middle;fill:#000000;stroke:none;stroke-width:1.01137185px"
|
||||
id="flowPara6750-21">cmd</flowPara></flowRoot> </g>
|
||||
style="font-size:10px;line-height:1.25;text-align:center;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.01137185px"
|
||||
id="flowPara6750-21">mirror</flowPara></flowRoot> </g>
|
||||
<g
|
||||
transform="translate(68.076575,194.84494)"
|
||||
id="g6752-65"
|
||||
@@ -1942,20 +1944,20 @@
|
||||
d="m 283.69141,589.86167 h 33.39844"
|
||||
style="fill:url(#linearGradient4761);fill-rule:evenodd;stroke:url(#linearGradient4761);stroke-width:1.01137185px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<flowRoot
|
||||
transform="translate(169.59514,-12.429611)"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0.01%;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01137185px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
transform="translate(169.59514,-12.020921)"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0.01%;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:0.35836011;stroke-linecap:butt;stroke-linejoin:miter;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="flowRoot6744-8"
|
||||
xml:space="preserve"><flowRegion
|
||||
id="flowRegion6746-95"
|
||||
style="fill:#000000;stroke-width:1.01137185px"><rect
|
||||
style="text-align:center;text-anchor:middle;fill:#000000;stroke-width:1.01137185px"
|
||||
style="fill:#000000;stroke-width:0.35836011;stroke-miterlimit:4;stroke-dasharray:none"><rect
|
||||
style="text-align:center;text-anchor:middle;fill:#000000;stroke-width:0.35836011;stroke-miterlimit:4;stroke-dasharray:none"
|
||||
y="601.71594"
|
||||
x="108.77582"
|
||||
height="15.195512"
|
||||
width="44.246517"
|
||||
id="rect6748-060" /></flowRegion><flowPara
|
||||
style="font-size:10px;line-height:1.25;text-align:center;text-anchor:middle;fill:#000000;stroke:none;stroke-width:1.01137185px"
|
||||
id="flowPara6750-83">cmd</flowPara></flowRoot> </g>
|
||||
style="font-size:10px;line-height:1.25;text-align:center;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:0.35836011;stroke-miterlimit:4;stroke-dasharray:none;stroke-opacity:1"
|
||||
id="flowPara6750-83">shader</flowPara></flowRoot> </g>
|
||||
<g
|
||||
transform="translate(135.95576,194.84494)"
|
||||
id="g6752-82"
|
||||
@@ -1980,7 +1982,7 @@
|
||||
d="m 283.69141,589.86167 h 33.39844"
|
||||
style="fill:url(#linearGradient4761);fill-rule:evenodd;stroke:url(#linearGradient4761);stroke-width:1.01137185px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<flowRoot
|
||||
transform="translate(169.59514,-12.429611)"
|
||||
transform="translate(169.59514,-11.350669)"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0.01%;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01137185px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="flowRoot6744-23"
|
||||
xml:space="preserve"><flowRegion
|
||||
@@ -1992,8 +1994,8 @@
|
||||
height="15.195512"
|
||||
width="44.246517"
|
||||
id="rect6748-77" /></flowRegion><flowPara
|
||||
style="font-size:10px;line-height:1.25;text-align:center;text-anchor:middle;fill:#000000;stroke:none;stroke-width:1.01137185px"
|
||||
id="flowPara6750-76">cmd</flowPara></flowRoot> </g>
|
||||
style="font-size:10px;line-height:1.25;text-align:center;text-anchor:middle;fill:#ffffff;fill-opacity:1;stroke:none;stroke-width:1.01137185px"
|
||||
id="flowPara6750-76">frames</flowPara></flowRoot> </g>
|
||||
<g
|
||||
transform="translate(203.83497,194.84494)"
|
||||
id="g6752-11"
|
||||
@@ -2018,7 +2020,7 @@
|
||||
d="m 283.69141,589.86167 h 33.39844"
|
||||
style="fill:url(#linearGradient4761);fill-rule:evenodd;stroke:url(#linearGradient4761);stroke-width:1.01137185px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<flowRoot
|
||||
transform="translate(169.59514,-12.429611)"
|
||||
transform="translate(169.59514,-10.843146)"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0.01%;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01137185px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="flowRoot6744-6"
|
||||
xml:space="preserve"><flowRegion
|
||||
@@ -2030,8 +2032,8 @@
|
||||
height="15.195512"
|
||||
width="44.246517"
|
||||
id="rect6748-17" /></flowRegion><flowPara
|
||||
style="font-size:10px;line-height:1.25;text-align:center;text-anchor:middle;fill:#000000;stroke:none;stroke-width:1.01137185px"
|
||||
id="flowPara6750-79">cmd</flowPara></flowRoot> </g>
|
||||
style="font-size:10px;line-height:1.25;text-align:center;text-anchor:middle;fill:#ffffff;stroke:none;stroke-width:1.01137185px"
|
||||
id="flowPara6750-79">feedbck</flowPara></flowRoot> </g>
|
||||
<g
|
||||
transform="translate(271.71418,194.84494)"
|
||||
id="g6752-62"
|
||||
@@ -2056,7 +2058,7 @@
|
||||
d="m 283.69141,589.86167 h 33.39844"
|
||||
style="fill:url(#linearGradient4761);fill-rule:evenodd;stroke:url(#linearGradient4761);stroke-width:1.01137185px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1" />
|
||||
<flowRoot
|
||||
transform="translate(169.59514,-12.429611)"
|
||||
transform="translate(169.59514,-10.570469)"
|
||||
style="font-style:normal;font-variant:normal;font-weight:normal;font-stretch:normal;line-height:0.01%;font-family:'Source Sans Pro';-inkscape-font-specification:'Source Sans Pro, Normal';text-align:center;letter-spacing:0px;word-spacing:0px;writing-mode:lr-tb;text-anchor:middle;fill:#000000;fill-opacity:1;stroke:none;stroke-width:1.01137185px;stroke-linecap:butt;stroke-linejoin:miter;stroke-opacity:1"
|
||||
id="flowRoot6744-48"
|
||||
xml:space="preserve"><flowRegion
|
||||
@@ -2068,8 +2070,8 @@
|
||||
height="15.195512"
|
||||
width="44.246517"
|
||||
id="rect6748-87" /></flowRegion><flowPara
|
||||
style="font-size:10px;line-height:1.25;text-align:center;text-anchor:middle;fill:#000000;stroke:none;stroke-width:1.01137185px"
|
||||
id="flowPara6750-52">cmd</flowPara></flowRoot> </g>
|
||||
style="font-size:10px;line-height:1.25;text-align:center;text-anchor:middle;fill:#ffffff;stroke:none;stroke-width:1.01137185px"
|
||||
id="flowPara6750-52">shutdwn</flowPara></flowRoot> </g>
|
||||
</g>
|
||||
</g>
|
||||
<g
|
||||
|
Before Width: | Height: | Size: 96 KiB After Width: | Height: | Size: 96 KiB |
BIN
i_n_c_u_r_pcb/incur_board_schematic.pdf
Normal file
BIN
i_n_c_u_r_pcb/incur_pcbV5.zip
Normal file
34
json_objects/analog_action_mapping.json
Normal file
@@ -0,0 +1,34 @@
|
||||
{
|
||||
"0": {
|
||||
"DEFAULT": ["set_the_shader_param_0_layer_offset_0_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_mix_continuous"]
|
||||
},
|
||||
"1": {
|
||||
"DEFAULT": ["set_the_shader_param_1_layer_offset_0_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_speed_position_continuous"]
|
||||
},
|
||||
"2": {
|
||||
"DEFAULT": ["set_the_shader_param_2_layer_offset_0_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_start_continuous"]
|
||||
},
|
||||
"3": {
|
||||
"DEFAULT": ["set_the_shader_param_3_layer_offset_0_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_end_continuous"]
|
||||
},
|
||||
"4": {
|
||||
"DEFAULT": ["set_the_shader_param_0_layer_offset_0_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_mix_continuous"]
|
||||
},
|
||||
"5": {
|
||||
"DEFAULT": ["set_the_shader_param_1_layer_offset_0_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_speed_position_continuous"]
|
||||
},
|
||||
"6": {
|
||||
"DEFAULT": ["set_the_shader_param_2_layer_offset_0_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_start_continuous"]
|
||||
},
|
||||
"7": {
|
||||
"DEFAULT": ["set_the_shader_param_3_layer_offset_0_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_end_continuous"]
|
||||
}
|
||||
}
|
||||
@@ -1,67 +1,105 @@
|
||||
{
|
||||
"a": {
|
||||
"NAV_BROWSER": ["move_browser_selection_up"],
|
||||
"PLAYER": ["seek_back_on_player"],
|
||||
"DEFAULT": ["seek_back_on_player", "decrease_seek_time"],
|
||||
"NAV_SETTINGS": ["move_settings_selection_up"],
|
||||
"LENGTH_SET": ["return_to_default_control_mode"]
|
||||
"NAV_SHADERS": ["move_shaders_selection_up"],
|
||||
"LENGTH_SET": ["return_to_default_control_mode"],
|
||||
"CONFIRM": ["return_to_default_control_mode"],
|
||||
"SHADER_PARAM": ["decrease_this_param", "decrease_shader_param"],
|
||||
"PLAY_SHADER": ["decrease_this_param", "decrease_shader_param"]
|
||||
},
|
||||
"b": {
|
||||
"NAV_BROWSER": ["move_browser_selection_down"],
|
||||
"PLAYER": ["seek_forward_on_player"],
|
||||
"DEFAULT": ["seek_forward_on_player", "increase_seek_time"],
|
||||
"NAV_SETTINGS": ["move_settings_selection_down"],
|
||||
"LENGTH_SET": ["return_to_default_control_mode"]
|
||||
"NAV_SHADERS": ["move_shaders_selection_down"],
|
||||
"LENGTH_SET": ["return_to_default_control_mode"],
|
||||
"CONFIRM": ["return_to_default_control_mode"],
|
||||
"SHADER_PARAM": ["increase_this_param", "increase_shader_param"],
|
||||
"PLAY_SHADER": ["increase_this_param", "increase_shader_param"]
|
||||
|
||||
},
|
||||
"c": {
|
||||
"NAV_BROWSER": ["enter_on_browser_selection"],
|
||||
"PLAYER": ["toggle_action_on_player","toggle_show_on_player"],
|
||||
"DEFAULT": ["toggle_action_on_player","toggle_show_on_player"],
|
||||
"NAV_SETTINGS": ["enter_on_settings_selection"],
|
||||
"LENGTH_SET": ["record_fixed_length"]
|
||||
"NAV_SHADERS": ["enter_on_shaders_selection", "map_on_shaders_selection"],
|
||||
"LENGTH_SET": ["record_fixed_length"],
|
||||
"SHADER_PARAM": ["return_to_default_control_mode"],
|
||||
"CONFIRM": ["perform_confirm_action"],
|
||||
"NAV_DETOUR": ["toggle_detour_play"],
|
||||
"PLAY_SHADER": ["toggle_shaders", "toggle_shader_speed"]
|
||||
},
|
||||
"d": {
|
||||
"DEFAULT": ["switch_to_next_player", "toggle_player_mode"]
|
||||
"DEFAULT": ["switch_to_next_player", "toggle_player_mode"],
|
||||
"NAV_DETOUR": ["toggle_detour_record", "toggle_detour_record_loop"]
|
||||
},
|
||||
"e": {
|
||||
"DEFAULT": ["set_playing_sample_start_to_current_duration", "clear_playing_sample_start_time"]
|
||||
"DEFAULT": ["set_playing_sample_start_to_current_duration", "clear_playing_sample_start_time"],
|
||||
"SHADER_PARAM": ["decrease_param_focus"],
|
||||
"PLAY_SHADER": ["decrease_param_focus"],
|
||||
"NAV_DETOUR": ["decrease_mix_shader"]
|
||||
},
|
||||
"f": {
|
||||
"DEFAULT": ["set_playing_sample_end_to_current_duration", "clear_playing_sample_end_time"]
|
||||
"DEFAULT": ["set_playing_sample_end_to_current_duration", "clear_playing_sample_end_time"],
|
||||
"SHADER_PARAM": ["increase_param_focus"],
|
||||
"PLAY_SHADER": ["increase_param_focus"],
|
||||
"NAV_DETOUR": ["increase_mix_shader"]
|
||||
},
|
||||
"g": {
|
||||
"DEFAULT": ["toggle_capture_preview", "toggle_capture_recording"]},
|
||||
"h": {
|
||||
"DEFAULT": ["cycle_display_mode"]
|
||||
"DEFAULT": ["cycle_display_mode", "cycle_display_mode_back"]
|
||||
},
|
||||
"i": {
|
||||
"DEFAULT": ["toggle_function"]
|
||||
},
|
||||
"j": {
|
||||
"DEFAULT": ["load_slot_0_into_next_player","previous_bank"]
|
||||
"DEFAULT": ["load_slot_0_into_next_player","previous_bank"],
|
||||
"PLAY_SHADER": ["play_shader_0","previous_shader_layer"],
|
||||
"NAV_SHADERS": ["play_shader_0","previous_shader_layer"],
|
||||
"NAV_DETOUR": ["switch_to_detour_0", "set_the_detour_mix_0"]
|
||||
},
|
||||
"k": {
|
||||
"DEFAULT": ["load_slot_1_into_next_player","next_bank"]
|
||||
"DEFAULT": ["load_slot_1_into_next_player","next_bank"],
|
||||
"PLAY_SHADER": ["play_shader_1","next_shader_layer"],
|
||||
"NAV_SHADERS": ["play_shader_1","next_shader_layer"],
|
||||
"NAV_DETOUR": ["switch_to_detour_1", "set_the_detour_mix_1"]
|
||||
},
|
||||
"l": {
|
||||
"DEFAULT": ["load_slot_2_into_next_player","clear_all_slots"]
|
||||
"DEFAULT": ["load_slot_2_into_next_player","clear_all_slots"],
|
||||
"PLAY_SHADER": ["play_shader_2","clear_shader_bank"],
|
||||
"NAV_SHADERS": ["play_shader_2","clear_shader_bank"],
|
||||
"NAV_DETOUR": ["switch_to_detour_2", "clear_this_detour"]
|
||||
},
|
||||
"m": {
|
||||
"DEFAULT": ["load_slot_3_into_next_player"]
|
||||
"DEFAULT": ["load_slot_3_into_next_player"],
|
||||
"PLAY_SHADER": ["play_shader_3"],
|
||||
"NAV_DETOUR": ["switch_to_detour_3"]
|
||||
},
|
||||
"n": {
|
||||
"DEFAULT": ["load_slot_4_into_next_player"]
|
||||
"DEFAULT": ["load_slot_4_into_next_player"],
|
||||
"PLAY_SHADER": ["play_shader_4"]
|
||||
},
|
||||
"o": {
|
||||
"DEFAULT": ["load_slot_5_into_next_player","toggle_screen_mirror"]
|
||||
"DEFAULT": ["load_slot_5_into_next_player","toggle_screen_mirror"],
|
||||
"PLAY_SHADER": ["play_shader_5", "toggle_screen_mirror"]
|
||||
},
|
||||
"p": {
|
||||
"DEFAULT": ["load_slot_6_into_next_player"]
|
||||
"DEFAULT": ["load_slot_6_into_next_player","toggle_shaders"],
|
||||
"PLAY_SHADER": ["play_shader_6","toggle_shaders"]
|
||||
},
|
||||
"q": {
|
||||
"DEFAULT": ["load_slot_7_into_next_player"]
|
||||
"DEFAULT": ["load_slot_7_into_next_player", "toggle_detour_mode"],
|
||||
"PLAY_SHADER": ["play_shader_7","toggle_detour_mode"]
|
||||
},
|
||||
"r": {
|
||||
"DEFAULT": ["load_slot_8_into_next_player"]
|
||||
"DEFAULT": ["load_slot_8_into_next_player", "toggle_feedback"],
|
||||
"PLAY_SHADER": ["play_shader_8", "toggle_feedback"]
|
||||
},
|
||||
"s": {
|
||||
"DEFAULT": ["load_slot_9_into_next_player","quit_the_program"]
|
||||
}
|
||||
"DEFAULT": ["load_slot_9_into_next_player","confirm_shutdown"],
|
||||
"PLAY_SHADER": ["play_shader_9","confirm_shutdown"]
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,82 +1,252 @@
|
||||
{
|
||||
"control_change 0": {
|
||||
"DEFAULT": ["set_the_current_video_alpha_cc"]
|
||||
"DEFAULT": ["set_the_shader_param_0_layer_offset_0_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_mix_continuous"]
|
||||
},
|
||||
"control_change 1": {
|
||||
"DEFAULT": ["set_the_next_video_alpha_cc"]
|
||||
"DEFAULT": ["set_the_shader_param_1_layer_offset_0_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_speed_position_continuous"]
|
||||
},
|
||||
"control_change 2": {
|
||||
"DEFAULT": ["set_the_camera_alpha_cc"]
|
||||
"DEFAULT": ["set_the_shader_param_2_layer_offset_0_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_start_continuous"]
|
||||
},
|
||||
"control_change 3": {
|
||||
"DEFAULT": ["set_the_camera_colour_u_with_cc"]
|
||||
"DEFAULT": ["set_the_shader_param_3_layer_offset_0_continuous"],
|
||||
"NAV_DETOUR": ["set_detour_end_continuous"]
|
||||
},
|
||||
"control_change 4": {
|
||||
"DEFAULT": ["set_the_camera_colour_v_with_cc"]
|
||||
"DEFAULT": ["set_the_shader_param_0_layer_offset_1_continuous"]
|
||||
},
|
||||
"control_change 5": {
|
||||
"DEFAULT": ["set_the_shader_param_1_layer_offset_1_continuous"]
|
||||
},
|
||||
"control_change 6": {
|
||||
"DEFAULT": ["set_the_shader_param_2_layer_offset_1_continuous"]
|
||||
},
|
||||
"control_change 7": {
|
||||
"DEFAULT": ["set_the_shader_param_3_layer_offset_1_continuous"]
|
||||
},
|
||||
"control_change 8": {
|
||||
"DEFAULT": ["set_the_shader_param_0_layer_offset_2_continuous"]
|
||||
},
|
||||
"control_change 9": {
|
||||
"DEFAULT": ["set_the_shader_param_1_layer_offset_2_continuous"]
|
||||
},
|
||||
"control_change 10": {
|
||||
"DEFAULT": ["set_the_shader_param_2_layer_offset_2_continuous"]
|
||||
},
|
||||
"control_change 11": {
|
||||
"DEFAULT": ["set_the_shader_param_3_layer_offset_2_continuous"]
|
||||
},
|
||||
"control_change 12": {
|
||||
"DEFAULT": ["set_strobe_amount_continuous"]
|
||||
},
|
||||
"note_on 72": {
|
||||
"NAV_BROWSER": ["move_browser_selection_up"],
|
||||
"PLAYER": ["seek_back_on_player"],
|
||||
"DEFAULT": ["seek_back_on_player", "decrease_seek_time"],
|
||||
"NAV_SETTINGS": ["move_settings_selection_up"],
|
||||
"LENGTH_SET": ["return_to_default_control_mode"]
|
||||
"NAV_SHADERS": ["move_shaders_selection_up"],
|
||||
"LENGTH_SET": ["return_to_default_control_mode"],
|
||||
"CONFIRM": ["return_to_default_control_mode"],
|
||||
"SHADER_PARAM": ["decrease_this_param", "decrease_shader_param"],
|
||||
"PLAY_SHADER": ["decrease_this_param", "decrease_shader_param"]
|
||||
},
|
||||
"note_on 73": {
|
||||
"NAV_BROWSER": ["move_browser_selection_down"],
|
||||
"PLAYER": ["seek_forward_on_player"],
|
||||
"DEFAULT": ["seek_forward_on_player", "increase_seek_time"],
|
||||
"NAV_SETTINGS": ["move_settings_selection_down"],
|
||||
"LENGTH_SET": ["return_to_default_control_mode"]
|
||||
"NAV_SHADERS": ["move_shaders_selection_down"],
|
||||
"LENGTH_SET": ["return_to_default_control_mode"],
|
||||
"CONFIRM": ["return_to_default_control_mode"],
|
||||
"SHADER_PARAM": ["increase_this_param", "increase_shader_param"],
|
||||
"PLAY_SHADER": ["increase_this_param", "increase_shader_param"]
|
||||
|
||||
},
|
||||
"note_on 74": {
|
||||
"NAV_BROWSER": ["enter_on_browser_selection"],
|
||||
"PLAYER": ["toggle_action_on_player","toggle_show_on_player"],
|
||||
"DEFAULT": ["toggle_action_on_player","toggle_show_on_player"],
|
||||
"NAV_SETTINGS": ["enter_on_settings_selection"],
|
||||
"LENGTH_SET": ["record_fixed_length"]
|
||||
"NAV_SHADERS": ["enter_on_shaders_selection", "map_on_shaders_selection"],
|
||||
"LENGTH_SET": ["record_fixed_length"],
|
||||
"SHADER_PARAM": ["return_to_default_control_mode"],
|
||||
"CONFIRM": ["perform_confirm_action"],
|
||||
"NAV_DETOUR": ["toggle_detour_play"],
|
||||
"PLAY_SHADER": ["toggle_shaders", "toggle_shader_speed"]
|
||||
},
|
||||
"note_on 75": {
|
||||
"DEFAULT": ["switch_to_next_player", "toggle_player_mode"]
|
||||
"DEFAULT": ["switch_to_next_player", "toggle_player_mode"],
|
||||
"NAV_DETOUR": ["toggle_detour_record", "toggle_detour_record_loop"]
|
||||
},
|
||||
"note_on 76": {
|
||||
"DEFAULT": ["set_playing_sample_start_to_current_duration", "clear_playing_sample_start_time"]
|
||||
"DEFAULT": ["set_playing_sample_start_to_current_duration", "clear_playing_sample_start_time"],
|
||||
"SHADER_PARAM": ["decrease_param_focus"],
|
||||
"PLAY_SHADER": ["decrease_param_focus"],
|
||||
"NAV_DETOUR": ["decrease_mix_shader"]
|
||||
},
|
||||
"note_on 77": {
|
||||
"DEFAULT": ["set_playing_sample_end_to_current_duration", "clear_playing_sample_end_time"]
|
||||
"DEFAULT": ["set_playing_sample_end_to_current_duration", "clear_playing_sample_end_time"],
|
||||
"SHADER_PARAM": ["increase_param_focus"],
|
||||
"PLAY_SHADER": ["increase_param_focus"],
|
||||
"NAV_DETOUR": ["increase_mix_shader"]
|
||||
},
|
||||
"note_on 78": {
|
||||
"DEFAULT": ["toggle_capture_preview", "toggle_capture_recording"]},
|
||||
"note_on 79": {
|
||||
"DEFAULT": ["cycle_display_mode"]
|
||||
"DEFAULT": ["cycle_display_mode", "cycle_display_mode_back"]
|
||||
},
|
||||
"note_on 80": {
|
||||
"DEFAULT": ["toggle_function"]
|
||||
},
|
||||
"note_on 81": {
|
||||
"DEFAULT": ["load_slot_0_into_next_player","previous_bank"]
|
||||
"DEFAULT": ["load_slot_0_into_next_player","previous_bank"],
|
||||
"PLAY_SHADER": ["play_shader_0","previous_shader_layer"],
|
||||
"NAV_SHADERS": ["play_shader_0","previous_shader_layer"],
|
||||
"NAV_DETOUR": ["switch_to_detour_0", "set_the_detour_mix_0"]
|
||||
},
|
||||
"note_on 82": {
|
||||
"DEFAULT": ["load_slot_1_into_next_player","next_bank"]
|
||||
"DEFAULT": ["load_slot_1_into_next_player","next_bank"],
|
||||
"PLAY_SHADER": ["play_shader_1","next_shader_layer"],
|
||||
"NAV_SHADERS": ["play_shader_1","next_shader_layer"],
|
||||
"NAV_DETOUR": ["switch_to_detour_1", "set_the_detour_mix_1"]
|
||||
},
|
||||
"note_on 83": {
|
||||
"DEFAULT": ["load_slot_2_into_next_player","clear_all_slots"]
|
||||
"DEFAULT": ["load_slot_2_into_next_player","clear_all_slots"],
|
||||
"PLAY_SHADER": ["play_shader_2","clear_shader_bank"],
|
||||
"NAV_SHADERS": ["play_shader_2","clear_shader_bank"],
|
||||
"NAV_DETOUR": ["switch_to_detour_2", "clear_this_detour"]
|
||||
},
|
||||
"note_on 84": {
|
||||
"DEFAULT": ["load_slot_3_into_next_player"]
|
||||
"DEFAULT": ["load_slot_3_into_next_player"],
|
||||
"PLAY_SHADER": ["play_shader_3"],
|
||||
"NAV_DETOUR": ["switch_to_detour_3"]
|
||||
},
|
||||
"note_on 85": {
|
||||
"DEFAULT": ["load_slot_4_into_next_player"]
|
||||
"DEFAULT": ["load_slot_4_into_next_player"],
|
||||
"PLAY_SHADER": ["play_shader_4"]
|
||||
},
|
||||
"note_on 86": {
|
||||
"DEFAULT": ["load_slot_5_into_next_player","toggle_screen_mirror"]
|
||||
"DEFAULT": ["load_slot_5_into_next_player","toggle_screen_mirror"],
|
||||
"PLAY_SHADER": ["play_shader_5", "toggle_screen_mirror"]
|
||||
},
|
||||
"note_on 87": {
|
||||
"DEFAULT": ["load_slot_6_into_next_player"]
|
||||
"DEFAULT": ["load_slot_6_into_next_player","toggle_shaders"],
|
||||
"PLAY_SHADER": ["play_shader_6","toggle_shaders"]
|
||||
},
|
||||
"note_on 88": {
|
||||
"DEFAULT": ["load_slot_7_into_next_player"]
|
||||
"DEFAULT": ["load_slot_7_into_next_player", "toggle_detour_mode"],
|
||||
"PLAY_SHADER": ["play_shader_7","toggle_detour_mode"]
|
||||
},
|
||||
"note_on 89": {
|
||||
"DEFAULT": ["load_slot_8_into_next_player"]
|
||||
"DEFAULT": ["load_slot_8_into_next_player", "toggle_feedback"],
|
||||
"PLAY_SHADER": ["play_shader_8", "toggle_feedback"]
|
||||
},
|
||||
"note_on 90": {
|
||||
"DEFAULT": ["load_slot_9_into_next_player","quit_the_program"]
|
||||
}
|
||||
"DEFAULT": ["load_slot_9_into_next_player","confirm_shutdown"],
|
||||
"PLAY_SHADER": ["play_shader_9","confirm_shutdown"]
|
||||
},
|
||||
"note_on 36": {
|
||||
"NAV_BROWSER": ["move_browser_selection_up"],
|
||||
"DEFAULT": ["seek_back_on_player", "decrease_seek_time"],
|
||||
"NAV_SETTINGS": ["move_settings_selection_up"],
|
||||
"NAV_SHADERS": ["move_shaders_selection_up"],
|
||||
"LENGTH_SET": ["return_to_default_control_mode"],
|
||||
"CONFIRM": ["return_to_default_control_mode"],
|
||||
"SHADER_PARAM": ["decrease_this_param", "decrease_shader_param"],
|
||||
"PLAY_SHADER": ["decrease_this_param", "decrease_shader_param"]
|
||||
},
|
||||
"note_on 37": {
|
||||
"NAV_BROWSER": ["move_browser_selection_down"],
|
||||
"DEFAULT": ["seek_forward_on_player", "increase_seek_time"],
|
||||
"NAV_SETTINGS": ["move_settings_selection_down"],
|
||||
"NAV_SHADERS": ["move_shaders_selection_down"],
|
||||
"LENGTH_SET": ["return_to_default_control_mode"],
|
||||
"CONFIRM": ["return_to_default_control_mode"],
|
||||
"SHADER_PARAM": ["increase_this_param", "increase_shader_param"],
|
||||
"PLAY_SHADER": ["increase_this_param", "increase_shader_param"]
|
||||
|
||||
},
|
||||
"note_on 38": {
|
||||
"NAV_BROWSER": ["enter_on_browser_selection"],
|
||||
"DEFAULT": ["toggle_action_on_player","toggle_show_on_player"],
|
||||
"NAV_SETTINGS": ["enter_on_settings_selection"],
|
||||
"NAV_SHADERS": ["enter_on_shaders_selection", "map_on_shaders_selection"],
|
||||
"LENGTH_SET": ["record_fixed_length"],
|
||||
"SHADER_PARAM": ["return_to_default_control_mode"],
|
||||
"CONFIRM": ["perform_confirm_action"],
|
||||
"NAV_DETOUR": ["toggle_detour_play"],
|
||||
"PLAY_SHADER": ["toggle_shaders", "toggle_shader_speed"]
|
||||
},
|
||||
"note_on 39": {
|
||||
"DEFAULT": ["switch_to_next_player", "toggle_player_mode"],
|
||||
"NAV_DETOUR": ["toggle_detour_record", "toggle_detour_record_loop"]
|
||||
},
|
||||
"note_on 40": {
|
||||
"DEFAULT": ["set_playing_sample_start_to_current_duration", "clear_playing_sample_start_time"],
|
||||
"SHADER_PARAM": ["decrease_param_focus"],
|
||||
"PLAY_SHADER": ["decrease_param_focus"],
|
||||
"NAV_DETOUR": ["decrease_mix_shader"]
|
||||
},
|
||||
"note_on 41": {
|
||||
"DEFAULT": ["set_playing_sample_end_to_current_duration", "clear_playing_sample_end_time"],
|
||||
"SHADER_PARAM": ["increase_param_focus"],
|
||||
"PLAY_SHADER": ["increase_param_focus"],
|
||||
"NAV_DETOUR": ["increase_mix_shader"]
|
||||
},
|
||||
"note_on 42": {
|
||||
"DEFAULT": ["toggle_capture_preview", "toggle_capture_recording"]},
|
||||
"note_on 43": {
|
||||
"DEFAULT": ["cycle_display_mode", "cycle_display_mode_back"]
|
||||
},
|
||||
"note_on 44": {
|
||||
"DEFAULT": ["toggle_function"]
|
||||
},
|
||||
"note_on 45": {
|
||||
"DEFAULT": ["load_slot_0_into_next_player","previous_bank"],
|
||||
"PLAY_SHADER": ["play_shader_0","previous_shader_layer"],
|
||||
"NAV_SHADERS": ["play_shader_0","previous_shader_layer"],
|
||||
"NAV_DETOUR": ["switch_to_detour_0", "set_the_detour_mix_0"]
|
||||
},
|
||||
"note_on 46": {
|
||||
"DEFAULT": ["load_slot_1_into_next_player","next_bank"],
|
||||
"PLAY_SHADER": ["play_shader_1","next_shader_layer"],
|
||||
"NAV_SHADERS": ["play_shader_1","next_shader_layer"],
|
||||
"NAV_DETOUR": ["switch_to_detour_1", "set_the_detour_mix_1"]
|
||||
},
|
||||
"note_on 47": {
|
||||
"DEFAULT": ["load_slot_2_into_next_player","clear_all_slots"],
|
||||
"PLAY_SHADER": ["play_shader_2","clear_shader_bank"],
|
||||
"NAV_SHADERS": ["play_shader_2","clear_shader_bank"],
|
||||
"NAV_DETOUR": ["switch_to_detour_2", "clear_this_detour"]
|
||||
},
|
||||
"note_on 48": {
|
||||
"DEFAULT": ["load_slot_3_into_next_player"],
|
||||
"PLAY_SHADER": ["play_shader_3"],
|
||||
"NAV_DETOUR": ["switch_to_detour_3"]
|
||||
},
|
||||
"note_on 49": {
|
||||
"DEFAULT": ["load_slot_4_into_next_player"],
|
||||
"PLAY_SHADER": ["play_shader_4"]
|
||||
},
|
||||
"note_on 50": {
|
||||
"DEFAULT": ["load_slot_5_into_next_player","toggle_screen_mirror"],
|
||||
"PLAY_SHADER": ["play_shader_5", "toggle_screen_mirror"]
|
||||
},
|
||||
"note_on 51": {
|
||||
"DEFAULT": ["load_slot_6_into_next_player","toggle_shaders"],
|
||||
"PLAY_SHADER": ["play_shader_6","toggle_shaders"]
|
||||
},
|
||||
"note_on 52": {
|
||||
"DEFAULT": ["load_slot_7_into_next_player", "toggle_detour_mode"],
|
||||
"PLAY_SHADER": ["play_shader_7","toggle_detour_mode"]
|
||||
},
|
||||
"note_on 53": {
|
||||
"DEFAULT": ["load_slot_8_into_next_player", "toggle_feedback"],
|
||||
"PLAY_SHADER": ["play_shader_8", "toggle_feedback"]
|
||||
},
|
||||
"note_on 54": {
|
||||
"DEFAULT": ["load_slot_9_into_next_player","confirm_shutdown"],
|
||||
"PLAY_SHADER": ["play_shader_9","confirm_shutdown"]
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,5 +1,26 @@
|
||||
{
|
||||
"capture": {
|
||||
"TYPE": {
|
||||
"action": "update_capture_settings",
|
||||
"options": [
|
||||
"piCamera",
|
||||
"usb",
|
||||
"piCaptureSd1"
|
||||
],
|
||||
"value": "piCaptureSd1"
|
||||
},
|
||||
"PICAPTURE_INPUT": {
|
||||
"action": "change_piCapture_input",
|
||||
"options": [
|
||||
"auto",
|
||||
"video1",
|
||||
"video2",
|
||||
"video3",
|
||||
"svideo",
|
||||
"component"
|
||||
],
|
||||
"value": "video1"
|
||||
},
|
||||
"DEVICE": {
|
||||
"action": "update_capture_settings",
|
||||
"options": [
|
||||
@@ -49,8 +70,8 @@
|
||||
"value": "auto"
|
||||
}
|
||||
},
|
||||
"midi": {
|
||||
"CHANNEL": {
|
||||
"user_input": {
|
||||
"MIDI_CHANNEL": {
|
||||
"action": null,
|
||||
"options": [
|
||||
1,
|
||||
@@ -72,21 +93,45 @@
|
||||
],
|
||||
"value": 1
|
||||
},
|
||||
"INPUT": {
|
||||
"MIDI_INPUT": {
|
||||
"action": null,
|
||||
"options": [
|
||||
"usb",
|
||||
"serial",
|
||||
"disabled"
|
||||
],
|
||||
"value": "disabled"
|
||||
},
|
||||
"MIDI_STATUS": {
|
||||
"action": "get_midi_status",
|
||||
"options": [],
|
||||
"value": null
|
||||
},
|
||||
"CYCLE_MIDI_PORT": {
|
||||
"action": "cycle_midi_port_index",
|
||||
"options": [],
|
||||
"value": null
|
||||
},
|
||||
"ANALOG_INPUT": {
|
||||
"action": null,
|
||||
"options": [
|
||||
"enabled",
|
||||
"disabled"
|
||||
],
|
||||
"value": "disabled"
|
||||
},
|
||||
"STATUS": {
|
||||
"action": "get_midi_status",
|
||||
"options": [],
|
||||
"value": null
|
||||
}
|
||||
},
|
||||
"other": {
|
||||
"system": {
|
||||
"UPDATE_CODE": {
|
||||
"action": "try_pull_code_and_reset",
|
||||
"options": [],
|
||||
"value": null
|
||||
},
|
||||
"CLEAR_MESSAGE_BAR": {
|
||||
"action": "clear_message",
|
||||
"options": [],
|
||||
"value": null
|
||||
},
|
||||
"DEV_MODE_RESET": {
|
||||
"action": "switch_dev_mode",
|
||||
"options": [
|
||||
@@ -101,9 +146,110 @@
|
||||
"value": null
|
||||
},
|
||||
"QUIT": {
|
||||
"action": "quit_the_program",
|
||||
"action": "confirm_quit",
|
||||
"options": [],
|
||||
"value": null
|
||||
},
|
||||
"SHUTDOWN_PI": {
|
||||
"action": "shutdown_pi",
|
||||
"options": [],
|
||||
"value": null
|
||||
},
|
||||
"RESTART_OPENFRAMEWORKS": {
|
||||
"action": "restart_openframeworks",
|
||||
"options": [],
|
||||
"value": null
|
||||
}
|
||||
},
|
||||
"shader": {
|
||||
"USE_SHADER": {
|
||||
"action": null,
|
||||
"options": [
|
||||
"enabled",
|
||||
"disabled"
|
||||
],
|
||||
"value": "enabled"
|
||||
},
|
||||
"USE_SHADER_BANK": {
|
||||
"action": null,
|
||||
"options": [
|
||||
"enabled",
|
||||
"disabled"
|
||||
],
|
||||
"value": "disabled"
|
||||
},
|
||||
"FIX_PARAM_OFFSET_LAYER": {
|
||||
"action": null,
|
||||
"options": [
|
||||
"enabled",
|
||||
"disabled"
|
||||
],
|
||||
"value": "disabled"
|
||||
},
|
||||
"STROBE_AMOUNT": {
|
||||
"action": null,
|
||||
"options": [
|
||||
0,
|
||||
1,
|
||||
2,
|
||||
3,
|
||||
4,
|
||||
5,
|
||||
6,
|
||||
7,
|
||||
8,
|
||||
9,
|
||||
10
|
||||
],
|
||||
"value": 0
|
||||
},
|
||||
"X3_AS_SPEED": {
|
||||
"action": null,
|
||||
"options": [
|
||||
"enabled",
|
||||
"disabled"
|
||||
],
|
||||
"value": "disabled"
|
||||
},
|
||||
"SHADER_PARAM": {
|
||||
"action": null,
|
||||
"options": [
|
||||
0.01,
|
||||
0.02,
|
||||
0.05,
|
||||
0.10,
|
||||
0.15,
|
||||
0.2,
|
||||
0.3,
|
||||
0.4
|
||||
],
|
||||
"value": 0.10
|
||||
}
|
||||
},
|
||||
"detour": {
|
||||
"TRY_DEMO": {
|
||||
"action": null,
|
||||
"options": [
|
||||
"enabled",
|
||||
"disabled"
|
||||
],
|
||||
"value": "disabled"
|
||||
},
|
||||
"DELAY_MODE": {
|
||||
"action": "set_detour_delay_mode",
|
||||
"options": [
|
||||
"enabled",
|
||||
"disabled"
|
||||
],
|
||||
"value": "disabled"
|
||||
},
|
||||
"SHADER_POSITION": {
|
||||
"action": null,
|
||||
"options": [
|
||||
"input",
|
||||
"output"
|
||||
],
|
||||
"value": "output"
|
||||
}
|
||||
},
|
||||
"sampler": {
|
||||
@@ -115,6 +261,19 @@
|
||||
],
|
||||
"value": "off"
|
||||
},
|
||||
"RESET_PLAYERS": {
|
||||
"action": "reset_players",
|
||||
"options": [],
|
||||
"value": null
|
||||
},
|
||||
"LOOP_TYPE": {
|
||||
"action": "update_video_settings",
|
||||
"options": [
|
||||
"seamless",
|
||||
"parallel"
|
||||
],
|
||||
"value": "seamless"
|
||||
},
|
||||
"FIXED_LENGTH": {
|
||||
"action": "set_fixed_length",
|
||||
"options": [],
|
||||
@@ -198,7 +357,21 @@
|
||||
"off"
|
||||
],
|
||||
"value": "off"
|
||||
}
|
||||
},
|
||||
"SEEK_TIME": {
|
||||
"action": null,
|
||||
"options": [
|
||||
0.5,
|
||||
1,
|
||||
5,
|
||||
10,
|
||||
15,
|
||||
30,
|
||||
60,
|
||||
120
|
||||
],
|
||||
"value": 5
|
||||
}
|
||||
},
|
||||
"video": {
|
||||
"BACKGROUND_COLOUR": {
|
||||
@@ -213,13 +386,24 @@
|
||||
],
|
||||
"value": "black"
|
||||
},
|
||||
"VIDEOPLAYER_BACKEND": {
|
||||
"action": "switch_video_backend",
|
||||
"options": [
|
||||
"omxplayer",
|
||||
"ofvideoplayer",
|
||||
"ofxomxplayer"
|
||||
],
|
||||
"value": "ofxomxplayer"
|
||||
},
|
||||
"HDMI_MODE": {
|
||||
"action": "change_hdmi_settings",
|
||||
"options": [
|
||||
"preferred",
|
||||
"CEA 4 HDMI"
|
||||
"CEA 4 HDMI",
|
||||
"CEA 1 HDMI",
|
||||
"CEA 17 HDMI"
|
||||
],
|
||||
"value": "preferred"
|
||||
"value": "CEA 4 HDMI"
|
||||
},
|
||||
"COMPOSITE_PROGRESSIVE": {
|
||||
"action": "change_composite_setting",
|
||||
|
||||
29
r_e_c_u_r.py
@@ -4,6 +4,8 @@ import traceback
|
||||
from tkinter import Tk, Frame
|
||||
import sys
|
||||
import tracemalloc
|
||||
import argparse
|
||||
from pythonosc import udp_client
|
||||
|
||||
from actions import Actions
|
||||
from data_centre.data import Data
|
||||
@@ -11,8 +13,10 @@ 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
|
||||
#from video_centre.capture import Capture
|
||||
from video_centre.shaders import Shaders
|
||||
import data_centre
|
||||
|
||||
# create tk object
|
||||
@@ -27,21 +31,34 @@ message_handler = MessageHandler()
|
||||
|
||||
data = Data(message_handler)
|
||||
|
||||
def setup_osc_client():
|
||||
client_parser = argparse.ArgumentParser()
|
||||
client_parser.add_argument("--ip", default="127.0.0.1", help="the ip")
|
||||
client_parser.add_argument("--port", type=int, default=8000, help="the port")
|
||||
|
||||
client_args = client_parser.parse_args()
|
||||
|
||||
return udp_client.SimpleUDPClient(client_args.ip, client_args.port)
|
||||
|
||||
osc_client = setup_osc_client()
|
||||
# setup the video driver
|
||||
video_driver = VideoDriver(tk, message_handler, data)
|
||||
capture = Capture(tk, message_handler, data)
|
||||
video_driver = VideoDriver(tk, osc_client, message_handler, data)
|
||||
#capture = Capture(tk, osc_client, message_handler, data)
|
||||
shaders = Shaders(tk, osc_client, message_handler, data)
|
||||
|
||||
# setup the display
|
||||
display = Display(tk, video_driver, capture, message_handler, data)
|
||||
display = Display(tk, video_driver, shaders, message_handler, data)
|
||||
|
||||
# setup the actions
|
||||
actions = Actions(tk, message_handler, data, video_driver, capture, display)
|
||||
actions = Actions(tk, message_handler, data, video_driver, shaders, display, osc_client)
|
||||
|
||||
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()
|
||||
actions.check_if_should_start_openframeworks()
|
||||
actions.toggle_x_autorepeat()
|
||||
|
||||
frame.pack()
|
||||
@@ -51,6 +68,8 @@ def handle_error(exc, val, tb):
|
||||
print('traceback for error : {}'.format(traceback.format_exc()))
|
||||
message_handler.set_message('ERROR', val, traceback.format_exc())
|
||||
|
||||
|
||||
|
||||
tk.report_callback_exception = handle_error
|
||||
|
||||
|
||||
|
||||
83
user_input/analog_input.py
Normal file
@@ -0,0 +1,83 @@
|
||||
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]
|
||||
self.analog_input = None
|
||||
self.check_if_listening_enabled()
|
||||
|
||||
|
||||
def check_if_listening_enabled(self):
|
||||
if self.data.settings['user_input']['ANALOG_INPUT']['value'] == 'enabled':
|
||||
if not self.analog_input:
|
||||
try:
|
||||
## note - using software spi for now although on the same pins as the hardware spi described below because hardware spi wasnt working with lcd display
|
||||
#SPI_PORT = 1
|
||||
#SPI_DEVICE = 2
|
||||
#self.analog_input = Adafruit_MCP3008.MCP3008(spi=SPI.SpiDev(SPI_PORT, SPI_DEVICE))
|
||||
CLK = 21
|
||||
MISO = 19
|
||||
MOSI = 20
|
||||
CS = 16
|
||||
self.analog_input = Adafruit_MCP3008.MCP3008(clk=CLK, cs=CS, miso=MISO, mosi=MOSI)
|
||||
|
||||
except:
|
||||
self.message_handler('INFO', 'analog inputs not connected')
|
||||
self.poll_analog_inputs()
|
||||
else:
|
||||
self.root.after(1000, self.check_if_listening_enabled)
|
||||
|
||||
def poll_analog_inputs(self):
|
||||
if self.data.settings['user_input']['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)
|
||||
#print(str(this_reading))
|
||||
if abs(this_reading - self.last_readings[i]) > 10:
|
||||
#print('the diff is {}'.format(this_reading - self.last_readings[i]))
|
||||
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)()
|
||||
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import string
|
||||
import datetime
|
||||
import mido
|
||||
import subprocess
|
||||
|
||||
class MidiInput(object):
|
||||
def __init__(self, root, message_handler, display, actions, data):
|
||||
@@ -11,33 +12,49 @@ class MidiInput(object):
|
||||
self.data = data
|
||||
self.midi_mappings = data.midi_mappings
|
||||
self.midi_device = None
|
||||
self.midi_delay = 1
|
||||
self.midi_setting = None
|
||||
self.port_index = 0
|
||||
self.midi_delay = 40
|
||||
self.try_open_port()
|
||||
|
||||
def try_open_port(self):
|
||||
midi_setting = self.data.settings['midi']['INPUT']['value']
|
||||
#self.data.midi_status = 'disconnected'
|
||||
self.midi_setting = self.data.settings['user_input']['MIDI_INPUT']['value']
|
||||
self.port_index = self.data.midi_port_index
|
||||
#print('try open port : midi setting is {}'.format(midi_setting))
|
||||
if midi_setting == 'enabled':
|
||||
midi_ports = mido.get_input_names()
|
||||
midi_device_on_port_20 = [s for s in midi_ports if '20:0' in s]
|
||||
if midi_device_on_port_20:
|
||||
if self.data.midi_status == 'disconnected':
|
||||
self.midi_device = mido.open_input(midi_device_on_port_20[0])
|
||||
self.data.midi_status = 'connected'
|
||||
self.message_handler.set_message('INFO', 'connected to midi device {}'.format(self.midi_device.name))
|
||||
self.poll_midi_input()
|
||||
elif self.data.midi_status == 'connected':
|
||||
self.data.midi_status = 'disconnected'
|
||||
if self.midi_setting == 'usb':
|
||||
self.actions.stop_serial_port_process()
|
||||
self.open_this_port_and_start_listening('20')
|
||||
elif self.midi_setting == 'serial':
|
||||
self.actions.create_serial_port_process()
|
||||
self.open_this_port_and_start_listening('serial')
|
||||
else:
|
||||
self.actions.stop_serial_port_process()
|
||||
self.data.midi_status = 'disconnected'
|
||||
self.root.after(1000, self.try_open_port)
|
||||
|
||||
def open_this_port_and_start_listening(self, port_phrase):
|
||||
midi_ports = mido.get_input_names()
|
||||
midi_device_on_port = [s for s in midi_ports if port_phrase in s]
|
||||
if midi_device_on_port:
|
||||
if self.data.midi_status == 'disconnected':
|
||||
subport_index = self.port_index % len(midi_device_on_port)
|
||||
self.midi_device = mido.open_input(midi_device_on_port[subport_index])
|
||||
self.data.midi_status = 'connected'
|
||||
self.message_handler.set_message('INFO', 'connected to midi device {}'.format(self.midi_device.name))
|
||||
self.poll_midi_input()
|
||||
elif self.data.midi_status == 'connected':
|
||||
self.data.midi_status = 'disconnected'
|
||||
|
||||
def poll_midi_input(self):
|
||||
i = 0
|
||||
cc_dict = dict()
|
||||
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['user_input']['MIDI_CHANNEL']['value'] - 1
|
||||
|
||||
if not message_dict.get('channel', None) == midi_channel:
|
||||
pass
|
||||
## turning off noisey clock messages for now - may want to use them at some point
|
||||
elif message_dict['type'] == 'clock':
|
||||
@@ -45,20 +62,30 @@ class MidiInput(object):
|
||||
## trying to only let through step cc messages to increase response time
|
||||
elif message_dict['type'] == 'control_change':
|
||||
control_number = message_dict['control']
|
||||
print('control number is {} , cc_dict.keys is {}'.format(control_number, cc_dict.keys() ))
|
||||
if not control_number in cc_dict.keys():
|
||||
cc_dict[control_number] = message_dict['value']
|
||||
self.on_midi_message(message_dict)
|
||||
else:
|
||||
step_size = 4
|
||||
step_size = 3
|
||||
ignore_range = range(cc_dict[control_number] - step_size,cc_dict[control_number] + step_size)
|
||||
#print('value is {} and ignore range is {}'.format(message_dict['value'], ignore_range ))
|
||||
if not message_dict['value'] in ignore_range:
|
||||
cc_dict[control_number] = message_dict['value']
|
||||
#print(message_dict)
|
||||
self.on_midi_message(message_dict)
|
||||
print(cc_dict)
|
||||
else:
|
||||
#print(cc_dict)
|
||||
|
||||
else:
|
||||
print(message_dict)
|
||||
self.on_midi_message(message_dict)
|
||||
if i > 0:
|
||||
print('the number processed {}'.format(i))
|
||||
self.root.after(self.midi_delay, self.poll_midi_input)
|
||||
pass
|
||||
#print('the number processed {}'.format(i))
|
||||
if self.data.settings['user_input']['MIDI_INPUT']['value'] == self.midi_setting and self.data.midi_port_index == self.port_index:
|
||||
self.root.after(self.midi_delay, self.poll_midi_input)
|
||||
else:
|
||||
self.data.midi_status = 'disconnected'
|
||||
|
||||
def on_midi_message(self, message_dict):
|
||||
if message_dict['type'] == 'note_on' and message_dict['velocity'] == 0:
|
||||
@@ -91,9 +118,14 @@ 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):
|
||||
@@ -108,3 +140,4 @@ class MidiInput(object):
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -20,7 +20,7 @@ class NumpadInput(object):
|
||||
def on_key_press(self, event):
|
||||
numpad = list(string.ascii_lowercase[0:19])
|
||||
|
||||
if event.char is '.':
|
||||
if event.char is '.' or event.char is 'z':
|
||||
self.actions.quit_the_program()
|
||||
if event.char is 's':
|
||||
event.char = self.on_0_key_press()
|
||||
|
||||
181
video_centre/alt_video_player.py
Normal file
@@ -0,0 +1,181 @@
|
||||
|
||||
|
||||
class AltVideoPlayer:
|
||||
def __init__(self, root, message_handler, data, osc_client, name):
|
||||
self.root = root
|
||||
self.message_handler = message_handler
|
||||
self.data = data
|
||||
self.name = name
|
||||
self.player_running = False
|
||||
self.status = 'EMPTY'
|
||||
self.total_length = 0.0
|
||||
self.bankslot_number = '*-*'
|
||||
self.start = -1.0
|
||||
self.end = -1.0
|
||||
self.rate = 1
|
||||
self.crop_length = 0.0
|
||||
self.location = ''
|
||||
self.load_attempts = 0
|
||||
self.alpha = 0
|
||||
self.show_toggle_on = True
|
||||
### new stuff
|
||||
self.client = osc_client
|
||||
|
||||
self.position = -1
|
||||
|
||||
|
||||
def try_load(self, layer, is_current=False):
|
||||
load_attempts = 0
|
||||
while(load_attempts < 2):
|
||||
load_attempts = load_attempts + 1
|
||||
if self.load(layer, is_current):
|
||||
print('load success')
|
||||
return True
|
||||
else:
|
||||
print('load failed')
|
||||
self.message_handler.set_message('ERROR', 'failed to load')
|
||||
self.status = 'ERROR'
|
||||
return False
|
||||
|
||||
|
||||
def load(self, layer, is_current=False):
|
||||
self.get_context_for_player(is_current)
|
||||
print('the location is {}'.format(self.location))
|
||||
if self.location == '':
|
||||
self.status = 'EMPTY'
|
||||
return True
|
||||
|
||||
if(self.end is -1):
|
||||
self.end = self.total_length
|
||||
if(self.start is -1):
|
||||
self.start = 0
|
||||
self.client.send_message("/player/{}/load".format(self.name[0]), [self.location, self.start / self.total_length, self.end / self.total_length, self.rate])
|
||||
self.crop_length = self.end - self.start
|
||||
if 'show' in self.data.settings['sampler']['ON_LOAD']['value']:
|
||||
self.set_alpha_value(255)
|
||||
else:
|
||||
pass
|
||||
self.set_alpha_value(0)
|
||||
return True
|
||||
#except (ValueError, SystemError) as e:
|
||||
# print(e)
|
||||
#self.message_handler.set_message('ERROR', 'load attempt fail')
|
||||
#return False
|
||||
|
||||
def start_video(self):
|
||||
if 'play' in self.data.settings['sampler']['ON_START']['value']:
|
||||
self.status = 'PLAYING'
|
||||
self.client.send_message("/player/{}/play".format(self.name[0]), True)
|
||||
else:
|
||||
self.status = 'START'
|
||||
if 'show' in self.data.settings['sampler']['ON_START']['value']:
|
||||
self.set_alpha_value(255)
|
||||
else:
|
||||
self.set_alpha_value(0)
|
||||
|
||||
|
||||
|
||||
|
||||
def reload(self, layer, is_current=False):
|
||||
self.exit()
|
||||
self.player_running = False
|
||||
self.try_load(layer, is_current)
|
||||
|
||||
def is_loaded(self):
|
||||
return self.status == 'LOADED'
|
||||
|
||||
def is_finished(self):
|
||||
return self.status == 'FINISHED'
|
||||
|
||||
def get_context_for_player(self, is_current=False):
|
||||
next_context = self.data.get_next_context(is_current)
|
||||
print('the context is {}'.format(next_context))
|
||||
self.location = next_context['location']
|
||||
self.total_length = next_context['length']
|
||||
self.start = next_context['start']
|
||||
self.end = next_context['end']
|
||||
self.bankslot_number = next_context['bankslot_number']
|
||||
self.rate = next_context['rate']
|
||||
|
||||
def toggle_pause(self):
|
||||
if self.status == "PLAYING":
|
||||
self.client.send_message("/player/{}/pause".format(self.name[0]), True)
|
||||
elif self.status == "PAUSED" or self.status == "LOADED":
|
||||
self.client.send_message("/player/{}/play".format(self.name[0]), True)
|
||||
else:
|
||||
print("error toggling pause when video is neither playing or paused")
|
||||
|
||||
def toggle_show(self):
|
||||
if self.alpha > 127:
|
||||
self.show_toggle_on = False
|
||||
self.set_alpha_value(0)
|
||||
else:
|
||||
self.show_toggle_on = True
|
||||
self.set_alpha_value(255)
|
||||
|
||||
def set_alpha_value(self, amount):
|
||||
self.client.send_message("/player/{}/alpha".format(self.name[0]), amount)
|
||||
self.alpha = amount
|
||||
|
||||
def seek(self, amount):
|
||||
position = self.position
|
||||
after_seek_position = position + amount
|
||||
if after_seek_position > self.start and after_seek_position < self.end:
|
||||
self.set_position(after_seek_position)
|
||||
else:
|
||||
self.message_handler.set_message('INFO', 'can not seek outside range')
|
||||
|
||||
def change_rate(self, amount):
|
||||
if self.rate is None:
|
||||
self.rate = 1
|
||||
|
||||
new_rate = self.rate + amount
|
||||
print('new rate is being set to {}'.format(new_rate))
|
||||
if new_rate >= -3 and new_rate <= 3:
|
||||
self.client.send_message("/player/{}/speed".format(self.name[0]), new_rate)
|
||||
self.rate = new_rate
|
||||
return new_rate
|
||||
else:
|
||||
self.message_handler.set_message('INFO', 'can not set speed outside of range')
|
||||
return self.rate
|
||||
|
||||
def get_position(self):
|
||||
return self.position
|
||||
|
||||
def set_position(self, position):
|
||||
self.client.send_message("/player/{}/position".format(self.name[0]), position / self.total_length)
|
||||
|
||||
def exit_after_delay(self):
|
||||
self.root.after(100, self.exit)
|
||||
|
||||
def exit(self):
|
||||
#self.last_player.exit()
|
||||
try:
|
||||
self.client.send_message("/player/{}/quit".format(self.name[0]),True)
|
||||
self.player_running = False
|
||||
except:
|
||||
pass
|
||||
|
||||
|
||||
## not sure if i am going to implement this atm
|
||||
def set_screen_size_for_dev_mode(self):
|
||||
if self.data.settings['system']['DEV_MODE_RESET']['value'] == 'on':
|
||||
##self.client.send_message("/player/{}/alpha".format(self.name[0]), amount)
|
||||
return True, '--win', '50,350,550,750'
|
||||
else:
|
||||
aspect_mode = self.data.settings['video']['SCREEN_MODE']['value']
|
||||
return False, '--aspect-mode', aspect_mode
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -23,20 +23,42 @@ class Capture(object):
|
||||
|
||||
|
||||
def create_capture_device(self):
|
||||
if self.use_capture:
|
||||
self.update_capture_settings()
|
||||
try:
|
||||
self.device = picamera.PiCamera(resolution=self.resolution, framerate=self.framerate)
|
||||
except picamera.exc.PiCameraError as e:
|
||||
self.use_capture = False
|
||||
print('camera exception is {}'.format(e))
|
||||
self.message_handler.set_message('INFO', 'no capture device attached')
|
||||
if self.data.settings['capture']['TYPE']['value'] != 'usb':
|
||||
if self.use_capture:
|
||||
if self.piCapture_with_no_source():
|
||||
return False
|
||||
self.update_capture_settings()
|
||||
|
||||
try:
|
||||
self.device = picamera.PiCamera(resolution=self.resolution, framerate=self.framerate, sensor_mode = self.sensor_mode)
|
||||
return True
|
||||
except picamera.exc.PiCameraError as e:
|
||||
self.use_capture = False
|
||||
self.data.settings['capture']['DEVICE']['value'] = 'disabled'
|
||||
print('camera exception is {}'.format(e))
|
||||
self.message_handler.set_message('INFO', 'no capture device attached')
|
||||
return False
|
||||
|
||||
def piCapture_with_no_source(self):
|
||||
is_piCapture = subprocess.check_output(['pivideo', '--query', 'ready'])
|
||||
if 'Video Processor was not found' not in str(is_piCapture):
|
||||
self.data.settings['capture']['TYPE']['value'] = "piCaptureSd1"
|
||||
is_source = subprocess.check_output(['pivideo', '--query', 'lock'])
|
||||
if 'No active video detected' in str(is_source):
|
||||
self.message_handler.set_message('INFO', 'piCapture detected w no input source')
|
||||
return True
|
||||
return False
|
||||
|
||||
def update_capture_settings(self):
|
||||
##setting class variables
|
||||
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']
|
||||
@@ -51,22 +73,37 @@ class Capture(object):
|
||||
if self.use_capture == False:
|
||||
self.message_handler.set_message('INFO', 'capture not enabled')
|
||||
return False
|
||||
if not self.device or self.device.closed:
|
||||
self.create_capture_device()
|
||||
self.is_previewing = True
|
||||
self.device.start_preview()
|
||||
self.set_preview_screen_size()
|
||||
self.device.preview.layer = self.PREVIEW_LAYER
|
||||
return True
|
||||
else:
|
||||
if not self.device or self.device.closed:
|
||||
is_created = self.create_capture_device()
|
||||
if self.use_capture == False or not is_created:
|
||||
return False
|
||||
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':
|
||||
if self.data.settings['system']['DEV_MODE_RESET']['value'] == 'on':
|
||||
self.device.preview.fullscreen = False
|
||||
self.device.preview.window = (50, 350, 500, 400)
|
||||
else:
|
||||
self.device.preview.fullscreen = True
|
||||
|
||||
def stop_preview(self):
|
||||
|
||||
self.device.stop_preview()
|
||||
self.is_previewing = False
|
||||
if not self.device.recording:
|
||||
@@ -78,8 +115,10 @@ class Capture(object):
|
||||
return
|
||||
if not self.check_available_disk_space():
|
||||
return
|
||||
if self.device.closed:
|
||||
self.create_capture_device()
|
||||
if self.device is None or self.device.closed:
|
||||
is_created = self.create_capture_device()
|
||||
if self.use_capture == False or not is_created:
|
||||
return
|
||||
|
||||
if not os.path.exists(self.video_dir):
|
||||
os.makedirs(self.video_dir)
|
||||
@@ -117,8 +156,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'):
|
||||
@@ -155,23 +198,19 @@ class Capture(object):
|
||||
return self.device.frame.timestamp / 1000000
|
||||
|
||||
def get_preview_alpha(self):
|
||||
if self.is_previewing:
|
||||
return self.device.preview.alpha
|
||||
else:
|
||||
if self.is_previewing and self.device is not None:
|
||||
try:
|
||||
return self.device.preview.alpha
|
||||
except Exception as e:
|
||||
print(e)
|
||||
if hasattr(e, 'message'):
|
||||
error_info = e.message
|
||||
else:
|
||||
error_info = e
|
||||
self.message_handler.set_message('ERROR',error_info)
|
||||
return 0
|
||||
return 0
|
||||
|
||||
#def is_previewing(self):
|
||||
# if self.device.closed or not self.device.preview:
|
||||
# return False
|
||||
#else:
|
||||
# return True
|
||||
|
||||
#def is_recording(self):
|
||||
# if self.device.recording:
|
||||
# return True
|
||||
#else:
|
||||
# return False
|
||||
|
||||
def set_colour(self, u_value, v_value):
|
||||
(u, v) = (128, 128)
|
||||
if self.device.color_effects is not None:
|
||||
@@ -204,8 +243,14 @@ class Capture(object):
|
||||
else:
|
||||
return int(fractions.Fraction(setting_value) * 1000000)
|
||||
|
||||
def receive_state(self, unused_addr, args):
|
||||
pass
|
||||
|
||||
def close_capture(self):
|
||||
if self.device is not None:
|
||||
print('closing the old camera...')
|
||||
self.device.close()
|
||||
|
||||
|
||||
|
||||
def receive_recording_finished(self, path, value):
|
||||
pass
|
||||
|
||||
|
||||
226
video_centre/of_capture.py
Normal file
@@ -0,0 +1,226 @@
|
||||
import os
|
||||
import subprocess
|
||||
import datetime
|
||||
import fractions
|
||||
import picamera
|
||||
import time
|
||||
|
||||
class OfCapture(object):
|
||||
def __init__(self, root, osc_client, message_handler, data):
|
||||
self.root = root
|
||||
self.osc_client = osc_client
|
||||
self.message_handler = message_handler
|
||||
self.data = data
|
||||
|
||||
self.has_capture = False
|
||||
self.is_recording = False
|
||||
self.is_previewing = False
|
||||
self.video_dir = '/home/pi/Videos/'
|
||||
self.recording_start_time = 0
|
||||
self.update_capture_settings()
|
||||
self.of_recording_finished = True
|
||||
#self.create_capture_device()
|
||||
|
||||
def create_capture_device(self):
|
||||
if self.use_capture and self.capture_type != 'usb':
|
||||
if self.piCapture_with_no_source():
|
||||
print('its picapture with no source !')
|
||||
return False
|
||||
self.update_capture_settings()
|
||||
if not self.check_if_attached_with_picamera():
|
||||
return
|
||||
|
||||
print('sending setup message !', self.capture_type)
|
||||
self.osc_client.send_message("/capture/setup", self.capture_type)
|
||||
self.has_capture = True
|
||||
return True
|
||||
|
||||
|
||||
def piCapture_with_no_source(self):
|
||||
is_piCapture = subprocess.check_output(['pivideo', '--query', 'ready'])
|
||||
if 'Video Processor was not found' not in str(is_piCapture):
|
||||
self.data.settings['capture']['TYPE']['value'] = "piCaptureSd1"
|
||||
is_source = subprocess.check_output(['pivideo', '--query', 'lock'])
|
||||
if 'No active video detected' in str(is_source):
|
||||
self.message_handler.set_message('INFO', 'piCapture detected w no input source')
|
||||
return True
|
||||
return False
|
||||
|
||||
def update_capture_settings(self):
|
||||
##setting class variables
|
||||
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
|
||||
|
||||
def check_if_attached_with_picamera(self):
|
||||
print('about to try open pcamera to check..')
|
||||
try:
|
||||
device = picamera.PiCamera(resolution=self.resolution, framerate=self.framerate, sensor_mode = self.sensor_mode)
|
||||
device.close()
|
||||
return True
|
||||
except picamera.exc.PiCameraError as e:
|
||||
self.use_capture = False
|
||||
self.data.settings['capture']['DEVICE']['value'] = 'disabled'
|
||||
print('camera exception is {}'.format(e))
|
||||
self.message_handler.set_message('INFO', 'no capture device attached')
|
||||
return False
|
||||
|
||||
def start_preview(self):
|
||||
if self.use_capture == False:
|
||||
self.message_handler.set_message('INFO', 'capture not enabled')
|
||||
return False
|
||||
else:
|
||||
if not self.has_capture:
|
||||
is_created = self.create_capture_device()
|
||||
if self.use_capture == False or not is_created:
|
||||
return False
|
||||
|
||||
self.is_previewing = True
|
||||
self.update_capture_settings()
|
||||
self.osc_client.send_message("/capture/preview/start", True)
|
||||
return True
|
||||
|
||||
def stop_preview(self):
|
||||
self.osc_client.send_message("/capture/preview/stop", True)
|
||||
self.is_previewing = False
|
||||
|
||||
def start_recording(self):
|
||||
if self.use_capture == False:
|
||||
self.message_handler.set_message('INFO', 'capture not enabled')
|
||||
return False
|
||||
else:
|
||||
if not self.has_capture:
|
||||
is_created = self.create_capture_device()
|
||||
if self.use_capture == False or not is_created:
|
||||
return False
|
||||
|
||||
if not self.check_available_disk_space():
|
||||
return
|
||||
|
||||
if not os.path.exists(self.video_dir):
|
||||
os.makedirs(self.video_dir)
|
||||
self.is_recording = True
|
||||
self.of_recording_finished = False
|
||||
self.recording_start_time = time.time()
|
||||
#self.device.start_recording(self.video_dir + '/raw.h264')
|
||||
self.osc_client.send_message("/capture/record/start", True)
|
||||
self.monitor_disk_space()
|
||||
|
||||
def monitor_disk_space(self):
|
||||
if self.is_recording:
|
||||
if self.check_available_disk_space():
|
||||
self.root.after(10000, self.monitor_disk_space)
|
||||
else:
|
||||
self.stop_recording()
|
||||
|
||||
def check_available_disk_space(self):
|
||||
mb_free = self.data._get_mb_free_diskspace(self.video_dir)
|
||||
if mb_free < 10:
|
||||
self.message_handler.set_message('INFO', 'insufficient space on disk')
|
||||
return False
|
||||
else:
|
||||
return True
|
||||
|
||||
def stop_recording(self):
|
||||
#self.device.stop_recording()
|
||||
self.osc_client.send_message("/capture/record/stop", True)
|
||||
self.is_recording = 'saving'
|
||||
self.wait_for_raw_file()
|
||||
#set status to saving
|
||||
|
||||
|
||||
def wait_for_raw_file(self):
|
||||
if os.path.exists(self.video_dir + 'raw.h264'):
|
||||
mp4box_process, recording_name = self.convert_raw_recording()
|
||||
|
||||
self.root.after(0, self.wait_for_recording_to_save, mp4box_process, recording_name)
|
||||
self.update_capture_settings()
|
||||
elif os.path.exists(self.video_dir + 'raw.mp4') and self.of_recording_finished:
|
||||
recording_path , recording_name = self.generate_recording_path()
|
||||
os.rename(self.video_dir + 'raw.mp4', recording_path )
|
||||
self.is_recording = False
|
||||
self.data.create_new_slot_mapping_in_first_open(recording_name)
|
||||
self.update_capture_settings()
|
||||
else:
|
||||
self.root.after(1000, self.wait_for_raw_file)
|
||||
|
||||
def convert_raw_recording(self):
|
||||
### wait for omx to finish creating video ...
|
||||
if os.path.exists(self.video_dir + 'raw.h264'):
|
||||
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
|
||||
except Exception as e:
|
||||
print(e)
|
||||
if hasattr(e, 'message'):
|
||||
error_info = e.message
|
||||
else:
|
||||
error_info = e
|
||||
self.message_handler.set_message('ERROR',error_info)
|
||||
|
||||
|
||||
def wait_for_recording_to_save(self, process, name):
|
||||
print('the poll is {}'.format(process.poll()))
|
||||
if process.poll() is not None:
|
||||
self.is_recording = False
|
||||
os.remove(self.video_dir + 'raw.h264')
|
||||
self.data.create_new_slot_mapping_in_first_open(name)
|
||||
else:
|
||||
self.root.after(300, self.wait_for_recording_to_save, process, name)
|
||||
|
||||
def generate_recording_path(self):
|
||||
rec_dir = self.video_dir + 'recordings'
|
||||
if not os.path.exists(rec_dir):
|
||||
os.makedirs(rec_dir)
|
||||
date = datetime.datetime.now().strftime("%Y-%m-%d")
|
||||
i = 0
|
||||
while os.path.exists('{}/rec-{}-{}.mp4'.format(rec_dir,date, i)):
|
||||
i += 1
|
||||
name = 'rec-{}-{}.mp4'.format(date, i)
|
||||
return '{}/{}'.format(rec_dir,name), name
|
||||
|
||||
def receive_recording_finished(self, unused_addr, position):
|
||||
print('recieved recording finshed message !!!!')
|
||||
self.of_recording_finished = True
|
||||
|
||||
def get_recording_time(self):
|
||||
return time.time() - self.recording_start_time
|
||||
#if not self.device or not self.device.recording or self.device.frame.timestamp == None:
|
||||
# return -1
|
||||
#else:
|
||||
# return self.device.frame.timestamp / 1000000
|
||||
|
||||
def get_preview_alpha(self):
|
||||
return 0
|
||||
|
||||
def set_colour(self, u_value, v_value):
|
||||
pass
|
||||
|
||||
def set_alpha(self, amount):
|
||||
pass
|
||||
|
||||
@staticmethod
|
||||
def convert_resolution_value(setting_value):
|
||||
split_values = setting_value.split('x')
|
||||
return (int(split_values[0]), int(split_values[1]))
|
||||
|
||||
@staticmethod
|
||||
def convert_framerate_value(setting_value):
|
||||
return fractions.Fraction(setting_value).limit_denominator()
|
||||
|
||||
def convert_shutter_value(self, setting_value):
|
||||
if setting_value == 'auto':
|
||||
return 0
|
||||
elif setting_value == 'max':
|
||||
return int(1000000 / self.framerate)
|
||||
else:
|
||||
return int(fractions.Fraction(setting_value) * 1000000)
|
||||
|
||||
|
||||
|
||||