From a7b850566e6bf04e29e38698aa43f7d5b4acf60c Mon Sep 17 00:00:00 2001 From: "B. Bogart" Date: Thu, 19 Mar 2009 20:33:40 +0000 Subject: First functional version of gphoto PD external. svn path=/trunk/externals/bbogart/; revision=10878 --- gphoto/gphoto-help.pd | 31 ++++ gphoto/gphoto.c | 423 ++++++++++++++++++++++++++++++++++++++++++++++++++ gphoto/makefile | 25 +++ 3 files changed, 479 insertions(+) create mode 100644 gphoto/gphoto-help.pd create mode 100644 gphoto/gphoto.c create mode 100644 gphoto/makefile diff --git a/gphoto/gphoto-help.pd b/gphoto/gphoto-help.pd new file mode 100644 index 0000000..79dff2c --- /dev/null +++ b/gphoto/gphoto-help.pd @@ -0,0 +1,31 @@ +#N canvas 0 0 638 970 10; +#X msg 83 48 getconfig model; +#X msg 142 83 getconfig capture; +#X msg 167 116 getconfig zoom; +#X obj 79 221 print; +#X msg 59 21 listconfig; +#X msg 189 147 setconfig capture 0; +#X msg 209 183 setconfig capture 1; +#X msg 205 218 setconfig capture 2; +#X text 336 182 cap on?; +#X text 334 217 cap off?; +#X msg 196 254 setconfig zoom 129; +#X msg 177 277 setconfig zoom 0; +#X msg 18 158 debug model; +#X obj 133 231 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 +-1 -1; +#X msg 266 33 capture test2.jpg; +#X obj 86 191 gphoto; +#X connect 0 0 15 0; +#X connect 1 0 15 0; +#X connect 2 0 15 0; +#X connect 4 0 15 0; +#X connect 5 0 15 0; +#X connect 6 0 15 0; +#X connect 7 0 15 0; +#X connect 10 0 15 0; +#X connect 11 0 15 0; +#X connect 12 0 15 0; +#X connect 14 0 15 0; +#X connect 15 0 3 0; +#X connect 15 1 13 0; diff --git a/gphoto/gphoto.c b/gphoto/gphoto.c new file mode 100644 index 0000000..dab9f80 --- /dev/null +++ b/gphoto/gphoto.c @@ -0,0 +1,423 @@ +/* Gphoto PD External */ +/* Copyright Ben Bogart, 2009 */ +/* This program is distributed under the params of the GNU Public License */ + +/////////////////////////////////////////////////////////////////////////////////// +/* This file is part of the Gphoto PD External. */ +/* */ +/* Gphoto PD External is free software; you can redistribute it and/or modify */ +/* it under the terms of the GNU General Public License as published by */ +/* the Free Software Foundation; either version 2 of the License, or */ +/* (at your option) any later version. */ +/* */ +/* The Gphoto PD External is distributed in the hope that they will be useful, */ +/* but WITHOUT ANY WARRANTY; without even the implied warranty of */ +/* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the */ +/* GNU General Public License for more details. */ +/* */ +/* You should have received a copy of the GNU General Public License */ +/* along with the Chaos PD Externals; if not, write to the Free Software */ +/* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ +/////////////////////////////////////////////////////////////////////////////////// + +#include +#include +#include +#include +#include +#include + +t_class *gphoto_class; + +typedef struct gphoto_struct { + t_object x_obj; + t_outlet *doneOutlet; + int busy; + +} gphoto_struct; + +// Struct to store A_GIMME data passed to thread. +typedef struct gphoto_gimme_struct { + gphoto_struct *gphoto; + t_symbol *s; + int argc; + t_atom *argv; +} gphoto_gimme_struct; + +// list configuration +void *listConfig(void *threadArgs) { + int gp_ret, numsections, numchildren, i, j; + Camera *camera; + CameraWidget *config = NULL; + CameraWidget *child = NULL; + CameraWidget *child2 = NULL; + CameraWidgetType type; + char *childName; + + gp_ret = gp_camera_new (&camera); + if (gp_ret != 0) {error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); gp_camera_unref(camera); return(NULL);} + + // INIT camera (without context) + gp_ret = gp_camera_init (camera, NULL); + if (gp_ret != 0) {error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret));} + if (gp_ret == -105) {post("gphoto: Are you sure the camera is supported, connected and powered on?"); gp_camera_unref(camera); return(NULL);} + + gp_ret = gp_camera_get_config (camera, &config, NULL); // get config from camera + if (gp_ret != 0) {error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); gp_camera_unref(camera); return(NULL);} + + numsections = gp_widget_count_children(config); + for (i=0; igphoto->x_obj.ob_outlet, gensym(childName)); + sys_unlock(); + + //post("gphoto: Config Child: %s\n", childName); // send through outlet? + } + } + } + + // Free memory (not child?) + gp_widget_unref (config); + gp_camera_unref (camera); + + // We are done and other messsages can now be sent. + ((gphoto_gimme_struct *)threadArgs)->gphoto->busy = 0; + + return(NULL); +} + +// Wrap listConfig +static void wrapListConfig(gphoto_struct *gphoto) { + int ret; + pthread_t thread1; + + if (!gphoto->busy) { + + // instance of structure + gphoto_gimme_struct *threadArgs = (gphoto_gimme_struct *)malloc(sizeof(gphoto_gimme_struct)); + + // packaging arguments into structure + threadArgs->gphoto = gphoto; + + // We're busy + gphoto->busy = 1; + + // Create thread + ret = pthread_create( &thread1, NULL, listConfig, threadArgs); + } else { + error("gphoto: ERROR: Already executing a command, try again later."); + } + + return; +} + +void *getConfig(void *threadArgs) { + int gp_ret; + const char *textVal; + const int *toggleVal; + const float rangeVal; + float value; + + t_symbol *key; + + Camera *camera; + CameraWidget *config = NULL; + CameraWidget *child = NULL; + CameraWidgetType type; + + key = atom_getsymbol( ((gphoto_gimme_struct *)threadArgs)->argv ); // config key + + gp_ret = gp_camera_new (&camera); + if (gp_ret != 0) {error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); gp_camera_unref(camera); return(NULL);} + + // INIT camera (without context) + gp_ret = gp_camera_init (camera, NULL); + if (gp_ret != 0) {error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret));} + if (gp_ret == -105) {post("gphoto: Are you sure the camera is supported, connected and powered on?"); gp_camera_unref(camera); return(NULL);} + + gp_ret = gp_camera_get_config (camera, &config, NULL); // get config from camera + if (gp_ret != 0) {error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); gp_camera_unref(camera); return(NULL);} + + gp_ret = gp_widget_get_child_by_name (config, key->s_name, &child); // get item from config + if (gp_ret != 0) {error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret));} + +/* post("types:"); + post("GP_WIDGET_TOGGLE: %d", GP_WIDGET_TOGGLE); + post("GP_WIDGET_TEXT: %d", GP_WIDGET_TEXT); + post("GP_WIDGET_RANGE: %d", GP_WIDGET_RANGE); + post("GP_WIDGET_RADIO: %d", GP_WIDGET_RADIO); + post("GP_WIDGET_MENU: %d", GP_WIDGET_MENU); + post("GP_WIDGET_BUTTON: %d", GP_WIDGET_BUTTON); + post("GP_WIDGET_DATE: %d", GP_WIDGET_DATE); + post("GP_WIDGET_WINDOW: %d", GP_WIDGET_WINDOW); + post("GP_WIDGET_SECTION: %d", GP_WIDGET_SECTION); +*/ + + gp_ret = gp_widget_get_type (child, &type); + if (gp_ret != 0) { + error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); + error("gphoto: Invalid config key."); + } else { + switch (type) { + case GP_WIDGET_TOGGLE: + gp_ret = gp_widget_get_value (child, &toggleVal); // get widget value + outlet_float(((gphoto_gimme_struct *)threadArgs)->gphoto->x_obj.ob_outlet, (int) toggleVal); + break; + case GP_WIDGET_TEXT: + gp_ret = gp_widget_get_value (child, &textVal); + outlet_symbol(((gphoto_gimme_struct *)threadArgs)->gphoto->x_obj.ob_outlet, gensym(textVal)); + break; + case GP_WIDGET_RANGE: + gp_ret = gp_widget_get_value (child, &rangeVal); + if (gp_ret != 0) {error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret));} + outlet_float(((gphoto_gimme_struct *)threadArgs)->gphoto->x_obj.ob_outlet, rangeVal); + break; + } + } + + // Free memory (not child?) + gp_widget_unref (config); + gp_camera_unref (camera); + + // We are done and other messsages can now be sent. + ((gphoto_gimme_struct *)threadArgs)->gphoto->busy = 0; + + // Send bang out 2nd outlet when operation is done. + sys_lock(); + outlet_bang(((gphoto_gimme_struct *)threadArgs)->gphoto->doneOutlet); + sys_unlock(); + + return(NULL); +} + +// Wrap getConfig +static void wrapGetConfig(gphoto_struct *gphoto, t_symbol *s, int argc, t_atom *argv) { + int ret; + pthread_t thread1; + + if (!gphoto->busy) { + + // instance of structure + gphoto_gimme_struct *threadArgs = (gphoto_gimme_struct *)malloc(sizeof(gphoto_gimme_struct)); + + // packaging arguments into structure + threadArgs->gphoto = gphoto; + threadArgs->s = s; + threadArgs->argc = argc; + threadArgs->argv = argv; + + // We're busy + gphoto->busy = 1; + + // Create thread + ret = pthread_create( &thread1, NULL, getConfig, threadArgs); + } else { + error("gphoto: ERROR: Already executing a command, try again later."); + } + + return; +} + +void *setConfig(void *threadArgs) { + int gp_ret, intValue; + float floatValue; + t_symbol *key; + Camera *camera; + CameraWidget *config = NULL; + CameraWidget *child = NULL; + CameraWidgetType type; + + sys_lock(); + key = atom_getsymbol( ((gphoto_gimme_struct *)threadArgs)->argv ); // config key + sys_unlock(); + + gp_ret = gp_camera_new (&camera); + if (gp_ret != 0) {error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret));} + + // INIT camera (without context) + gp_ret = gp_camera_init (camera, NULL); + if (gp_ret != 0) {error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret));} + if (gp_ret == -105) {error("gphoto: Are you sure the camera is supported, connected and powered on?"); gp_camera_unref(camera); return(NULL);} + + gp_ret = gp_camera_get_config (camera, &config, NULL); // get config from camera + if (gp_ret != 0) {error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); gp_camera_unref(camera); return(NULL);} + + gp_ret = gp_widget_get_child_by_name (config, key->s_name, &child); // get item from config + if (gp_ret != 0) {error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); gp_camera_unref(camera); return(NULL);} + + gp_ret = gp_widget_get_type (child, &type); + if (gp_ret != 0) { + error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); + error("gphoto: Invalid config key."); + } else { + switch (type) { + case GP_WIDGET_TOGGLE: + intValue = atom_getint( ((gphoto_gimme_struct *)threadArgs)->argv+1 ); + + gp_ret = gp_widget_set_value (child, &intValue); // set widget value + if (gp_ret != 0) {error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret));} + + gp_ret = gp_camera_set_config (camera, config, NULL); // set new config + if (gp_ret != 0) {error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret));} + break; + + case GP_WIDGET_RANGE: + floatValue = atom_getfloat( ((gphoto_gimme_struct *)threadArgs)->argv+1 ); + + gp_ret = gp_widget_set_value (child, &floatValue); // set widget value + if (gp_ret != 0) {error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret));} + + gp_ret = gp_camera_set_config (camera, config, NULL); // set new config + if (gp_ret != 0) {error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret));} + + break; + } + } + + // Free memory + gp_widget_unref (config); + gp_camera_unref(camera); + + // We are done and other messsages can now be sent. + ((gphoto_gimme_struct *)threadArgs)->gphoto->busy = 0; + + // Send bang out 2nd outlet when operation is done. + sys_lock(); + outlet_bang(((gphoto_gimme_struct *)threadArgs)->gphoto->doneOutlet); + sys_unlock(); + + return(NULL); +} + +// Wrap setConfig +// TODO is there a way to have one wrapper for all funcs? +static void wrapSetConfig(gphoto_struct *gphoto, t_symbol *s, int argc, t_atom *argv) { + int ret; + pthread_t thread1; + + if (!gphoto->busy) { + + // instance of structure + gphoto_gimme_struct *threadArgs = (gphoto_gimme_struct *)malloc(sizeof(gphoto_gimme_struct)); + + // packaging arguments into structure + threadArgs->gphoto = gphoto; + threadArgs->s = s; + threadArgs->argc = argc; + threadArgs->argv = argv; + + // We're busy + gphoto->busy = 1; + + // Create thread + ret = pthread_create( &thread1, NULL, setConfig, threadArgs); + } else { + error("gphoto: ERROR: Already executing a command, try again later."); + } + + return; +} + +void *captureImage(void *threadArgs) { + int gp_ret, fd; + Camera *camera; + CameraFile *camerafile; + CameraFilePath camera_file_path; + t_symbol *filename; + + filename = atom_getsymbol( ((gphoto_gimme_struct *)threadArgs)->argv ); // destination filename + + gp_ret = gp_camera_new (&camera); + if (gp_ret != 0) {error("1gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); gp_camera_unref(camera); return(NULL);} + + // INIT camera (without context) + gp_ret = gp_camera_init (camera, NULL); + if (gp_ret != 0) {error("2gphoto: ERROR: %s\n", gp_result_as_string(gp_ret));} + if (gp_ret == -105) {post("gphoto: Are you sure the camera is supported, connected and powered on?"); gp_camera_unref(camera); return(NULL);} + + gp_ret = gp_camera_capture(camera, GP_CAPTURE_IMAGE, &camera_file_path, NULL); + if (gp_ret != 0) {error("3gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); gp_camera_unref(camera); return(NULL);} + + fd = open( filename->s_name, O_CREAT | O_WRONLY, 0644); // create file descriptor + + gp_ret = gp_file_new_from_fd(&camerafile, fd); // create gphoto file from descriptor + if (gp_ret != 0) {error("4gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); gp_camera_unref(camera); return(NULL);} + + gp_ret = gp_camera_file_get(camera, camera_file_path.folder, camera_file_path.name, + GP_FILE_TYPE_NORMAL, camerafile, NULL); // get file from camera + if (gp_ret != 0) {error("5gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); gp_camera_unref(camera); return(NULL);} + + gp_ret = gp_camera_file_delete(camera, camera_file_path.folder, camera_file_path.name, NULL); + if (gp_ret != 0) {error("6gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); gp_camera_unref(camera); return(NULL);} + + // Free memory + gp_camera_unref(camera); + + // We are done and other messsages can now be sent. + ((gphoto_gimme_struct *)threadArgs)->gphoto->busy = 0; + + // Send bang out 2nd outlet when operation is done. + sys_lock(); + outlet_bang(((gphoto_gimme_struct *)threadArgs)->gphoto->doneOutlet); + sys_unlock(); + + return(NULL); +} + +// Wrap captureImage +// TODO is there a way to have one wrapper for all funcs? +static void wrapCaptureImage(gphoto_struct *gphoto, t_symbol *s, int argc, t_atom *argv) { + int ret; + pthread_t thread1; + + if (!gphoto->busy) { + + // instance of structure + gphoto_gimme_struct *threadArgs = (gphoto_gimme_struct *)malloc(sizeof(gphoto_gimme_struct)); + + // packaging arguments into structure + threadArgs->gphoto = gphoto; + threadArgs->s = s; + threadArgs->argc = argc; + threadArgs->argv = argv; + + // We're busy + gphoto->busy = 1; + + // Create thread + ret = pthread_create( &thread1, NULL, captureImage, threadArgs); + } else { + error("gphoto: ERROR: Already executing a command, try again later."); + } +} + +static void *gphoto_new(void) { + gphoto_struct *gphoto = (gphoto_struct *) pd_new(gphoto_class); + outlet_new(&gphoto->x_obj, NULL); + gphoto->doneOutlet = outlet_new(&gphoto->x_obj, &s_bang); + + // Initially the external is not "busy" + gphoto->busy = 0; + + return (void *)gphoto; +} + +void gphoto_setup(void) { + gphoto_class = class_new(gensym("gphoto"), (t_newmethod) gphoto_new, 0, sizeof(gphoto_struct), 0, CLASS_DEFAULT, 0); + class_addmethod(gphoto_class, (t_method) wrapGetConfig, gensym("getconfig"), A_GIMME, 0); + class_addmethod(gphoto_class, (t_method) wrapCaptureImage, gensym("capture"), A_GIMME, 0); + class_addmethod(gphoto_class, (t_method) wrapSetConfig, gensym("setconfig"), A_GIMME, 0); + class_addmethod(gphoto_class, (t_method) wrapListConfig, gensym("listconfig"), 0); +} + diff --git a/gphoto/makefile b/gphoto/makefile new file mode 100644 index 0000000..f87381e --- /dev/null +++ b/gphoto/makefile @@ -0,0 +1,25 @@ +current: + echo make pd_linux + +clean: ; rm -f *.pd_linux *.o + +# ----------------------- LINUX i386 ----------------------- + +pd_linux: gphoto.pd_linux + +.SUFFIXES: .pd_linux + +LINUXCFLAGS = -DPD -O2 -funroll-loops -fomit-frame-pointer \ + -Wall -W -Wshadow -Wstrict-prototypes \ + -Wno-unused -Wno-parentheses -Wno-switch \ + -lgphoto2 -lpthread + +LINUXINCLUDE = -I../../src + +.c.pd_linux: + cc $(LINUXCFLAGS) $(LINUXINCLUDE) -o $*.o -c $*.c -ggdb + ld -shared -o $*.pd_linux $*.o -lc -lm -lgphoto2 + strip --strip-unneeded $*.pd_linux + rm $*.o + + -- cgit v1.2.1