From 435811629736ea5fff5b19918b2a71236567c5cb Mon Sep 17 00:00:00 2001 From: "B. Bogart" Date: Fri, 19 Feb 2010 18:42:35 +0000 Subject: Added "open" and "close" methods so that many commands can be executed without needed to reinitialize the camera (which can only be done a limited number of times). Fixed problems with running on newer PDs (>0.40-4). Cleaned up code a little, removed problems leading to many warnings. Updates to help file. svn path=/trunk/externals/bbogart/; revision=13174 --- gphoto/README.build.txt | 9 +- gphoto/README.txt | 20 +- gphoto/gphoto-help.pd | 85 +++---- gphoto/gphoto.c | 627 +++++++++++------------------------------------- gphoto/makefile | 4 +- 5 files changed, 197 insertions(+), 548 deletions(-) diff --git a/gphoto/README.build.txt b/gphoto/README.build.txt index 1fd6292..60973e6 100644 --- a/gphoto/README.build.txt +++ b/gphoto/README.build.txt @@ -1,11 +1,8 @@ -These objects where developed and tested under Linux. and have been tested -I've left the OSX stuff in the makefile, but AFAIK gphoto is only used under Linux. +These objects where developed under Linux and have been tested +under Linux. They should work on any PD platform where +libgphoto is available. To build (under linux): make pd_linux -To build (under OSX): - - make pd_darwin - diff --git a/gphoto/README.txt b/gphoto/README.txt index 2b95771..faeca93 100644 --- a/gphoto/README.txt +++ b/gphoto/README.txt @@ -1,18 +1,18 @@ -This is the readme for "gphoto" a libgphoto2 interface external for PD. +This is the readme for "gphoto" a digital camera interface externalPD. -popup is Copyright Ben Bogart 2009 +The PD gphoto external is Copyright Ben Bogart 2009 If you have any questions/comments you can reach the author at ben@ekran.org. This program is distributed under the terms of the GNU General Public License -popup is free software; you can redistribute it and/or modify +gphoto 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. -popup is distributed in the hope that it will be useful, +gphoto is distributed in the hope that it 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. @@ -26,6 +26,14 @@ USAGE: Put the binary in your extra folder. Put the helpfile in your 5.reference folder. +ISSUES: + +libgphoto conflicts with imageMagick. If you are using gphoto with PDP or +Gem make sure you have compiled without imageMagick. + TODO: -* Add a function to wrap gp_error results, and set busy to 0. -* Add getFile and getFiles functions for PTP file transfer functionality. + +Need to add a function that allows the user to query the details of params. +Currently the gphoto2 command line tool must be use to find out proper param +ranges and values. + diff --git a/gphoto/gphoto-help.pd b/gphoto/gphoto-help.pd index 17a5588..00f508d 100644 --- a/gphoto/gphoto-help.pd +++ b/gphoto/gphoto-help.pd @@ -1,55 +1,36 @@ -#N canvas 577 0 638 746 10; -#X obj 66 606 print; -#X msg 66 132 listconfig; -#X msg 127 350 setconfig capture 1; -#X msg 109 276 setconfig zoom 129; -#X obj 107 606 bng 15 250 50 0 empty empty empty 17 7 0 10 -262144 --1 -1; -#X obj 66 583 gphoto; -#X msg 139 536 reset; -#X msg 156 440 captureimages stop; -#X text 20 18 This object interfaces with gphoto2 supported PTP cameras. +#N canvas 637 363 569 399 10; +#X msg 34 120 getconfig model; +#X obj 23 352 print; +#X msg 29 95 listconfig; +#X obj 23 308 gphoto; +#X msg 23 70 open; +#X obj 56 331 tgl 15 0 empty empty empty 17 7 0 10 -262144 -1 -1 1 +1; +#X msg 52 274 close; +#X msg 47 186 capture test.jpg; +#X text 18 11 gphoto external uses libgphoto2 to control compatible +PTP cameras. For a list of compatible cameras see \; http://www.gphoto.org/doc/remote/ ; -#X text 20 38 This object is intended for remote capture control. For -a list of supported cameras see: http://www.gphoto.org/doc/remote; -#X text 129 606 Finished command.; -#X msg 163 475 captureimages image.jpg 2; -#X msg 141 384 captureimage image.jpg; -#X text 64 107 Camera Properties; -#X msg 80 197 configdetail \$1; -#X symbolatom 80 179 10 0 0 0 - - -; -#X text 185 244 Get value of a particular camera property \, ie "model" +#X text 63 69 Autodetect cameras and attempt to open connection.; +#X text 106 94 List available parameters; +#X text 142 119 Get value of "model" parameter; +#X text 173 148 Set value of a capture parameter (needed to do remote +shutter release on Canon cameras); +#X text 160 185 Trigger shutter and download image; +#X text 170 214 Set value of "zoom" parameter to "11" (you can set +these to values that are NOT campatible with the camera. Make sure +you are using compatible values with: "gphoto2 --get-config zoom") ; -#X msg 96 244 getconfig \$1; -#X symbolatom 96 226 10 0 0 0 - - -; -#X text 239 276 Set value of a particular camera property \, ie "zoom" +#X text 97 274 Close connection to camera (does not retract lens!) ; -#X text 127 329 Remote Capture; -#X text 258 350 On Canon Cameras capture must be set first.; -#X text 288 384 Capture a single image.; -#X msg 156 420 captureimages image-%05d.jpg 2; -#X text 354 420 Capture image every 2 seconds; -#X text 284 440 Stop capturing images.; -#X text 328 475 Overwrite single file every 2 seconds.; -#X text 189 536 reset internal state (only use if you know what your -doing \, ie camera battery dies or is dissconnected and the internal -state no longer matches the camera state.); -#X text 184 196 Get detailed info on a particular camera property. -It must be in the list returned by listconfig \, ie "zoom".; -#X text 142 125 List camera properties (arguments to get/set config). -"capture" property must be set to list capture properties \, ie zoom. -; -#X connect 1 0 5 0; -#X connect 2 0 5 0; -#X connect 3 0 5 0; -#X connect 5 0 0 0; -#X connect 5 1 4 0; -#X connect 6 0 5 0; -#X connect 7 0 5 0; -#X connect 11 0 5 0; -#X connect 12 0 5 0; -#X connect 14 0 5 0; -#X connect 15 0 14 0; -#X connect 17 0 5 0; -#X connect 18 0 17 0; -#X connect 23 0 5 0; +#X msg 40 153 setconfig capture 1; +#X msg 52 229 setconfig zoom 50; +#X connect 0 0 3 0; +#X connect 2 0 3 0; +#X connect 3 0 1 0; +#X connect 3 1 5 0; +#X connect 4 0 3 0; +#X connect 6 0 3 0; +#X connect 7 0 3 0; +#X connect 16 0 3 0; +#X connect 17 0 3 0; diff --git a/gphoto/gphoto.c b/gphoto/gphoto.c index 258d06b..94179b2 100644 --- a/gphoto/gphoto.c +++ b/gphoto/gphoto.c @@ -22,7 +22,6 @@ #include #include -#include #include #include #include @@ -33,10 +32,10 @@ t_class *gphoto_class; typedef struct gphoto_struct { t_object x_obj; - t_outlet *doneOutlet; - int busy; - pthread_attr_t threadAttr; //thread attributes - int capturing; + t_outlet *connectedOutlet; + Camera *camera; + int connected; + } gphoto_struct; // Struct to store A_GIMME data passed to thread. @@ -47,181 +46,85 @@ typedef struct gphoto_gimme_struct { t_atom *argv; } gphoto_gimme_struct; -void *getConfigDetail(void *threadArgs) { +// Open connection to camera, do autodetection and initialization. +void *openCam(void *gphoto) { int gp_ret; - const char *textVal; - const int *toggleVal; - const float rangeVal; - const char *label, *info; - float rangeMax, rangeMin, rangeIncr; - float floatVal; - int intVal, i; - t_symbol *key; + gp_ret = gp_camera_new (&((gphoto_struct *)gphoto)->camera); + if (gp_ret != 0) {error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); gp_camera_unref( ((gphoto_struct *)gphoto)->camera ); return(NULL);} + if (gp_ret == -105) {error("gphoto: Are you sure the camera is supported, connected and powered on?"); gp_camera_unref( ((gphoto_struct *)gphoto)->camera); return(NULL);} + post("gphoto: Autodetecting Camera."); - Camera *camera; - CameraWidget *config = NULL; - CameraWidget *child = NULL; - CameraWidgetType type; - - key = atom_getsymbol( ((gphoto_gimme_struct *)threadArgs)->argv ); // config key + // INIT camera (without context) + gp_ret = gp_camera_init (((gphoto_struct *)gphoto)->camera, NULL); + if (gp_ret != 0) {error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); gp_camera_unref( ((gphoto_struct *)gphoto)->camera); return(NULL);} + if (gp_ret == -105) {error("gphoto: Are you sure the camera is supported, connected and powered on?"); gp_camera_unref( ((gphoto_struct *)gphoto)->camera); return(NULL);} + post("gphoto: Connected to Camera."); - gp_ret = gp_camera_new (&camera); - if (gp_ret != 0) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); gp_camera_unref(camera); return(NULL);} + // Send state out 2nd outlet. + sys_lock(); + outlet_float(((gphoto_struct *)gphoto)->connectedOutlet, 1); + sys_unlock(); - // INIT camera (without context) - gp_ret = gp_camera_init (camera, NULL); - if (gp_ret != 0) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock();} - if (gp_ret == -105) { - sys_lock(); - error("gphoto: Are you sure the camera is supported, connected and powered on?"); - sys_unlock(); - gp_camera_unref(camera); - ((gphoto_gimme_struct *)threadArgs)->gphoto->busy = 0; // no longer busy if we got an error. - return(NULL); - } + // Set to connected state. + ((gphoto_struct *)gphoto)->connected = 1; + + return(NULL); +} - gp_ret = gp_camera_get_config (camera, &config, NULL); // get config from camera - if (gp_ret != 0) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); gp_camera_unref(camera); return(NULL);} +// Wrap Open +static void wrapOpen(gphoto_struct *gphoto) { + int ret; + pthread_t thread1; - gp_ret = gp_widget_get_child_by_name (config, key->s_name, &child); // get item from config - if (gp_ret != 0) { - sys_lock(); - error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); - sys_unlock(); - ((gphoto_gimme_struct *)threadArgs)->gphoto->busy = 0; // no longer busy if we got an error. - gp_camera_unref(camera); - return(NULL); - } + // Create thread and pass reference to object struct + ret = pthread_create( &thread1, NULL, openCam, gphoto); - gp_ret = gp_widget_get_type (child, &type); - if (gp_ret != 0) { - sys_lock; error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); - error("gphoto: Invalid config key."); sys_unlock(); - } else { - switch (type) { - case GP_WIDGET_RADIO: - gp_ret = gp_widget_get_label (child, &label); - sys_lock(); - post("gphoto: Label: %s", label); - post("gphoto: Type: Radio"); - sys_unlock(); - for (i=0; igphoto->busy = 0; + gp_camera_free(((gphoto_struct *)gphoto)->camera); + post("camera free"); - // Send bang out 2nd outlet when operation is done. + // Send state out 2nd outlet. sys_lock(); - outlet_bang(((gphoto_gimme_struct *)threadArgs)->gphoto->doneOutlet); - sys_unlock(); + outlet_float(((gphoto_struct *)gphoto)->connectedOutlet, 0); + sys_unlock(); + // Set to connected state. + ((gphoto_struct *)gphoto)->connected = 0; + return(NULL); } -// Wrap getConfigDetail -static void wrapGetConfigDetail(gphoto_struct *gphoto, t_symbol *s, int argc, t_atom *argv) { +// Wrap Open +static void wrapClose(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; - threadArgs->s = s; - threadArgs->argc = argc; - threadArgs->argv = argv; - - // We're busy - gphoto->busy = 1; - - // Create thread - ret = pthread_create( &thread1, &gphoto->threadAttr, getConfigDetail, threadArgs); - } else { - error("gphoto: ERROR: Already executing a command, try again later."); - } + // Create thread and pass reference to object struct + ret = pthread_create( &thread1, NULL, closeCam, gphoto); return; } +// TODO use listConfig to make a condigDetails function to get range and step data for cam params. + // 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) { - sys_lock(); - error("gphoto: Are you sure the camera is supported, connected and powered on?"); - sys_unlock(); - gp_camera_unref(camera); - ((gphoto_gimme_struct *)threadArgs)->gphoto->busy = 0; // no longer busy if we got an error. - return(NULL); - } + const char *childName; - gp_ret = gp_camera_get_config (camera, &config, NULL); // get config from camera - if (gp_ret != 0) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); gp_camera_unref(camera); return(NULL);} + gp_ret = gp_camera_get_config (((gphoto_gimme_struct *)threadArgs)->gphoto->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(((gphoto_gimme_struct *)threadArgs)->gphoto->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; - - // Send bang out 2nd outlet when operation is done. + // Send bang out 1st outlet when operation is done. sys_lock(); - outlet_bang(((gphoto_gimme_struct *)threadArgs)->gphoto->doneOutlet); + outlet_bang(((gphoto_gimme_struct *)threadArgs)->gphoto->x_obj.ob_outlet); sys_unlock(); return(NULL); @@ -263,7 +164,7 @@ static void wrapListConfig(gphoto_struct *gphoto) { int ret; pthread_t thread1; - if (!gphoto->busy) { + if (gphoto->connected) { // instance of structure gphoto_gimme_struct *threadArgs = (gphoto_gimme_struct *)malloc(sizeof(gphoto_gimme_struct)); @@ -271,109 +172,80 @@ static void wrapListConfig(gphoto_struct *gphoto) { // packaging arguments into structure threadArgs->gphoto = gphoto; - // We're busy - gphoto->busy = 1; - // Create thread - ret = pthread_create( &thread1, &gphoto->threadAttr, listConfig, threadArgs); + ret = pthread_create( &thread1, NULL, listConfig, threadArgs); } else { - error("gphoto: ERROR: Already executing a command, try again later."); + error("gphoto: ERROR: Not connected."); } return; } +// Get configuration (flags) from camera void *getConfig(void *threadArgs) { int gp_ret; const char *textVal; const int *toggleVal; - const float rangeVal; + 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) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); gp_camera_unref(camera); return(NULL);} - - // INIT camera (without context) - gp_ret = gp_camera_init (camera, NULL); - if (gp_ret != 0) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock();} - if (gp_ret == -105) { - sys_lock(); - error("gphoto: Are you sure the camera is supported, connected and powered on?"); - sys_unlock(); - gp_camera_unref(camera); - ((gphoto_gimme_struct *)threadArgs)->gphoto->busy = 0; // no longer busy if we got an error. - return(NULL); - } - - gp_ret = gp_camera_get_config (camera, &config, NULL); // get config from camera - if (gp_ret != 0) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); gp_camera_unref(camera); return(NULL);} + gp_ret = gp_camera_get_config (((gphoto_gimme_struct *)threadArgs)->gphoto->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(((gphoto_gimme_struct *)threadArgs)->gphoto->camera); return(NULL);} gp_ret = gp_widget_get_child_by_name (config, key->s_name, &child); // get item from config - if (gp_ret != 0) { - sys_lock(); - error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); - sys_unlock(); - ((gphoto_gimme_struct *)threadArgs)->gphoto->busy = 0; // no longer busy if we got an error. - gp_camera_unref(camera); - return(NULL); - } + 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) { - sys_lock; error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); - error("gphoto: Invalid config key."); sys_unlock(); + error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); + error("gphoto: Invalid config key."); } else { switch (type) { - case GP_WIDGET_RADIO: - gp_ret = gp_widget_get_value(child, &textVal); - sys_lock(); - outlet_symbol(((gphoto_gimme_struct *)threadArgs)->gphoto->x_obj.ob_outlet, gensym(textVal)); - sys_unlock(); - break; case GP_WIDGET_TOGGLE: gp_ret = gp_widget_get_value (child, &toggleVal); // get widget value - sys_lock(); outlet_float(((gphoto_gimme_struct *)threadArgs)->gphoto->x_obj.ob_outlet, (int) toggleVal); - sys_unlock(); break; case GP_WIDGET_TEXT: gp_ret = gp_widget_get_value (child, &textVal); - sys_lock(); outlet_symbol(((gphoto_gimme_struct *)threadArgs)->gphoto->x_obj.ob_outlet, gensym(textVal)); - sys_lock(); 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));} - sys_lock(); - outlet_float(((gphoto_gimme_struct *)threadArgs)->gphoto->x_obj.ob_outlet, rangeVal); - sys_lock(); + outlet_float(((gphoto_gimme_struct *)threadArgs)->gphoto->x_obj.ob_outlet, (float) *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. + // Send bang out 1st outlet when operation is done. sys_lock(); - outlet_bang(((gphoto_gimme_struct *)threadArgs)->gphoto->doneOutlet); + outlet_bang(((gphoto_gimme_struct *)threadArgs)->gphoto->x_obj.ob_outlet); sys_unlock(); - return(NULL); + return(NULL); } // Wrap getConfig @@ -381,7 +253,7 @@ static void wrapGetConfig(gphoto_struct *gphoto, t_symbol *s, int argc, t_atom * int ret; pthread_t thread1; - if (!gphoto->busy) { + if (gphoto->connected) { // instance of structure gphoto_gimme_struct *threadArgs = (gphoto_gimme_struct *)malloc(sizeof(gphoto_gimme_struct)); @@ -390,26 +262,23 @@ static void wrapGetConfig(gphoto_struct *gphoto, t_symbol *s, int argc, t_atom * threadArgs->gphoto = gphoto; threadArgs->s = s; threadArgs->argc = argc; - threadArgs->argv = argv; - - // We're busy - gphoto->busy = 1; + threadArgs->argv = malloc(sizeof(*argv)*argc); // allocate new memory space for arguments. + memcpy(threadArgs->argv, argv, sizeof(*argv)*argc); // copy the arguments into new space. // Create thread - ret = pthread_create( &thread1, &gphoto->threadAttr, getConfig, threadArgs); + ret = pthread_create( &thread1, NULL, getConfig, threadArgs); } else { - error("gphoto: ERROR: Already executing a command, try again later."); + error("gphoto: ERROR: Not connected."); } return; } void *setConfig(void *threadArgs) { - int gp_ret, intValue; + int gp_ret; float floatValue; - t_symbol *key, *textValue; - char charkey[MAXPDSTRING]; - Camera *camera; + t_symbol *key; + t_int *intValue; CameraWidget *config = NULL; CameraWidget *child = NULL; CameraWidgetType type; @@ -418,77 +287,38 @@ void *setConfig(void *threadArgs) { key = atom_getsymbol( ((gphoto_gimme_struct *)threadArgs)->argv ); // config key sys_unlock(); - gp_ret = gp_camera_new (&camera); - if (gp_ret != 0) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock();} - - // INIT camera (without context) - gp_ret = gp_camera_init (camera, NULL); - if (gp_ret != 0) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock();} - if (gp_ret == -105) { - sys_lock(); - error("gphoto: Are you sure the camera is supported, connected and powered on?"); - sys_unlock(); - gp_camera_unref(camera); - ((gphoto_gimme_struct *)threadArgs)->gphoto->busy = 0; // no longer busy if we got an error. - return(NULL); - } - - gp_ret = gp_camera_get_config (camera, &config, NULL); // get config from camera - if (gp_ret != 0) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); gp_camera_unref(camera); return(NULL);} + gp_ret = gp_camera_get_config (((gphoto_gimme_struct *)threadArgs)->gphoto->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(((gphoto_gimme_struct *)threadArgs)->gphoto->camera); return(NULL);} gp_ret = gp_widget_get_child_by_name (config, key->s_name, &child); // get item from config - if (gp_ret != 0) { - sys_lock(); - error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); - sys_unlock(); - ((gphoto_gimme_struct *)threadArgs)->gphoto->busy = 0; // no longer busy if we got an error. - gp_camera_unref(camera); - return(NULL); - } + if (gp_ret != 0) {error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); gp_camera_unref(((gphoto_gimme_struct *)threadArgs)->gphoto->camera); return(NULL);} gp_ret = gp_widget_get_type (child, &type); if (gp_ret != 0) { - sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); - error("gphoto: Invalid config key."); sys_unlock(); + error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); + error("gphoto: Invalid config key."); } else { switch (type) { - // same method as TOGGLE type - case GP_WIDGET_RADIO: - sys_lock(); - textValue = atom_getsymbol( ((gphoto_gimme_struct *)threadArgs)->argv+1 ); - post("argument: %s", textValue->s_name); - strcpy(charkey,(char *)textValue->s_name); - post("charkey: %s", charkey); - sys_unlock(); - - gp_ret = gp_widget_set_value (child, &charkey); // set widget value - if (gp_ret != 0) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); } - - gp_ret = gp_camera_set_config (camera, config, NULL); // set new config - if (gp_ret != 0) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); } - break; case GP_WIDGET_TOGGLE: - sys_lock(); - intValue = atom_getint( ((gphoto_gimme_struct *)threadArgs)->argv+1 ); - sys_unlock(); + sys_lock(); + intValue = atom_getint( ((gphoto_gimme_struct *)threadArgs)->argv+1); + sys_unlock(); gp_ret = gp_widget_set_value (child, &intValue); // set widget value - if (gp_ret != 0) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); } + 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) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); } + gp_ret = gp_camera_set_config (((gphoto_gimme_struct *)threadArgs)->gphoto->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: - sys_lock(); floatValue = atom_getfloat( ((gphoto_gimme_struct *)threadArgs)->argv+1 ); - sys_unlock(); gp_ret = gp_widget_set_value (child, &floatValue); // set widget value - if (gp_ret != 0) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); } + 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) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); } + gp_ret = gp_camera_set_config (((gphoto_gimme_struct *)threadArgs)->gphoto->camera, config, NULL); // set new config + if (gp_ret != 0) {error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret));} break; } @@ -496,14 +326,10 @@ void *setConfig(void *threadArgs) { // 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. + // Send bang out 1st outlet when operation is done. sys_lock(); - outlet_bang(((gphoto_gimme_struct *)threadArgs)->gphoto->doneOutlet); + outlet_bang(((gphoto_gimme_struct *)threadArgs)->gphoto->x_obj.ob_outlet); sys_unlock(); return(NULL); @@ -511,11 +337,11 @@ void *setConfig(void *threadArgs) { // 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) { +static void wrapSetConfig(gphoto_struct *gphoto, t_symbol *s, int argc, t_atom *argv) { int ret; pthread_t thread1; - if (!gphoto->busy) { + if (gphoto->connected) { // instance of structure gphoto_gimme_struct *threadArgs = (gphoto_gimme_struct *)malloc(sizeof(gphoto_gimme_struct)); @@ -524,15 +350,13 @@ static void wrapSetConfig(gphoto_struct *gphoto, t_symbol *s, int argc, t_atom * threadArgs->gphoto = gphoto; threadArgs->s = s; threadArgs->argc = argc; - threadArgs->argv = argv; - - // We're busy - gphoto->busy = 1; + threadArgs->argv = malloc(sizeof(*argv)*argc); // allocate new memory space for arguments. + memcpy(threadArgs->argv, argv, sizeof(*argv)*argc); // copy the arguments into new space. // Create thread - ret = pthread_create( &thread1, &gphoto->threadAttr, setConfig, threadArgs); + ret = pthread_create( &thread1, NULL, setConfig, threadArgs); } else { - error("gphoto: ERROR: Already executing a command, try again later."); + error("gphoto: ERROR: Not connected."); } return; @@ -540,69 +364,42 @@ static void wrapSetConfig(gphoto_struct *gphoto, t_symbol *s, int argc, t_atom * void *captureImage(void *threadArgs) { int gp_ret, fd; - Camera *camera; CameraFile *camerafile; CameraFilePath camera_file_path; t_symbol *filename; - - sys_lock(); + filename = atom_getsymbol( ((gphoto_gimme_struct *)threadArgs)->argv ); // destination filename - sys_unlock(); - gp_ret = gp_camera_new (&camera); - if (gp_ret != 0) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); gp_camera_unref(camera); return(NULL);} - - // INIT camera (without context) - gp_ret = gp_camera_init (camera, NULL); - if (gp_ret != 0) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); } - if (gp_ret == -105) { - sys_lock(); - error("gphoto: Are you sure the camera is supported, connected and powered on?"); - sys_unlock(); - gp_camera_unref(camera); - ((gphoto_gimme_struct *)threadArgs)->gphoto->busy = 0; // no longer busy if we got an error. - return(NULL); - } - - gp_ret = gp_camera_capture(camera, GP_CAPTURE_IMAGE, &camera_file_path, NULL); - if (gp_ret != 0) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); gp_camera_unref(camera); return(NULL);} + gp_ret = gp_camera_capture(((gphoto_gimme_struct *)threadArgs)->gphoto->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(((gphoto_gimme_struct *)threadArgs)->gphoto->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) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); gp_camera_unref(camera); return(NULL);} + if (gp_ret != 0) {error("4gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); gp_camera_unref(((gphoto_gimme_struct *)threadArgs)->gphoto->camera); return(NULL);} - gp_ret = gp_camera_file_get(camera, camera_file_path.folder, camera_file_path.name, + gp_ret = gp_camera_file_get(((gphoto_gimme_struct *)threadArgs)->gphoto->camera, camera_file_path.folder, camera_file_path.name, GP_FILE_TYPE_NORMAL, camerafile, NULL); // get file from camera - if (gp_ret != 0) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); 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) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); gp_camera_unref(camera); return(NULL);} - - // Free memory - gp_camera_free(camera); - - // We are done and other messsages can now be sent. - ((gphoto_gimme_struct *)threadArgs)->gphoto->busy = 0; + if (gp_ret != 0) {error("5gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); gp_camera_unref(((gphoto_gimme_struct *)threadArgs)->gphoto->camera); return(NULL);} + + gp_ret = gp_camera_file_delete(((gphoto_gimme_struct *)threadArgs)->gphoto->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(((gphoto_gimme_struct *)threadArgs)->gphoto->camera); return(NULL);} // Send bang out 2nd outlet when operation is done. sys_lock(); - outlet_bang(((gphoto_gimme_struct *)threadArgs)->gphoto->doneOutlet); + outlet_bang(((gphoto_gimme_struct *)threadArgs)->gphoto->x_obj.ob_outlet); sys_unlock(); - pthread_exit(NULL); - //return(NULL); // needed? + 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) { +static void wrapCaptureImage(gphoto_struct *gphoto, t_symbol *s, int argc, t_atom *argv) { int ret; pthread_t thread1; -post("busy state: %d", gphoto->busy); - - if (!gphoto->busy) { + if (gphoto->connected) { // instance of structure gphoto_gimme_struct *threadArgs = (gphoto_gimme_struct *)malloc(sizeof(gphoto_gimme_struct)); @@ -611,145 +408,13 @@ post("busy state: %d", gphoto->busy); threadArgs->gphoto = gphoto; threadArgs->s = s; threadArgs->argc = argc; - threadArgs->argv = argv; - - // We're busy - gphoto->busy = 1; + threadArgs->argv = malloc(sizeof(*argv)*argc); // allocate new memory space for arguments. + memcpy(threadArgs->argv, argv, sizeof(*argv)*argc); // copy the arguments into new space. // Create thread - ret = pthread_create( &thread1, &gphoto->threadAttr, captureImage, threadArgs); - post("pthread return: %d", ret); + ret = pthread_create( &thread1, NULL, captureImage, threadArgs); } else { - error("gphoto: ERROR: Already executing a command, try again later."); - } -} - -// Reset internal state to accept new commands. -static void reset(gphoto_struct *gphoto) { - gphoto->busy = 0; - - return; -} - -void *captureImages(void *threadArgs) { - int gp_ret, fd; - Camera *camera; - CameraFile *camerafile; - CameraFilePath camera_file_path; - t_symbol *format; - char filename[MAXPDSTRING]; - int count; - int sleepTime; - - sys_lock(); - format = atom_getsymbol( ((gphoto_gimme_struct *)threadArgs)->argv ); // destination filename - sleepTime = atom_getint ( ((gphoto_gimme_struct *)threadArgs)->argv+1 ); // loop sleep delay - sys_unlock(); - - // we don't want a delay of 0! (1 ok?) - if (sleepTime <=0) { - sleepTime = 1; - sys_lock(); - error("gphoto: ERROR: The minimum sleep value is 1 second. Sleep set to 1 second."); - sys_unlock(); - } - - gp_ret = gp_camera_new (&camera); - if (gp_ret != 0) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); gp_camera_unref(camera); return(NULL);} - - // INIT camera (without context) - gp_ret = gp_camera_init (camera, NULL); - if (gp_ret != 0) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); } - if (gp_ret == -105) { - sys_lock(); - error("gphoto: Are you sure the camera is supported, connected and powered on?"); - sys_unlock(); - gp_camera_unref(camera); - ((gphoto_gimme_struct *)threadArgs)->gphoto->busy = 0; // no longer busy if we got an error. - return(NULL); - } - - count = 0; - while (((gphoto_gimme_struct *)threadArgs)->gphoto->capturing) { - - // Create filename from format. This does not check if the format string is suitable. - sprintf(&filename, format->s_name, count); - - gp_ret = gp_camera_capture(camera, GP_CAPTURE_IMAGE, &camera_file_path, NULL); - if (gp_ret != 0) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); gp_camera_unref(camera); return(NULL);} - - fd = open( filename, 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) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); 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) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); 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) {sys_lock(); error("gphoto: ERROR: %s\n", gp_result_as_string(gp_ret)); sys_unlock(); gp_camera_unref(camera); return(NULL);} - - close(fd); // close file descriptor - - gp_file_free(camerafile); - - // Send bang out 2nd outlet for each iteration. - sys_lock(); - outlet_bang(((gphoto_gimme_struct *)threadArgs)->gphoto->doneOutlet); - outlet_float(((gphoto_gimme_struct *)threadArgs)->gphoto->x_obj.ob_outlet, count); - sys_unlock(); - - sleep(sleepTime); - count++; - } - - // Free memory - gp_camera_free(camera); - - // We are done and other messsages can now be sent. - ((gphoto_gimme_struct *)threadArgs)->gphoto->busy = 0; - - pthread_exit(NULL); -} - -// Wrap captureImage -// TODO is there a way to have one wrapper for all funcs? -static void wrapCaptureImages(gphoto_struct *gphoto, t_symbol *s, int argc, t_atom *argv) { - int ret; - pthread_t thread1; - - if (strcmp(atom_getsymbol(argv)->s_name, "stop") == 0) { - - gphoto->capturing = 0; // Stop Capturing. - gphoto->busy = 0; // No longer busy - - } else if (!gphoto->busy) { - - if (argc != 2) { - error("gphoto: ERROR: usage: captureimages [filename-format] [sleeptime (seconds)]"); - } else { - - gphoto->capturing = 1; // Now capturing. - - // 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, &gphoto->threadAttr, captureImages, threadArgs); - } - - } else { - - error("gphoto: ERROR: Already executing a command, try again later."); + error("gphoto: ERROR: Not connected."); } return; @@ -758,32 +423,30 @@ static void wrapCaptureImages(gphoto_struct *gphoto, t_symbol *s, int argc, t_at 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; + gphoto->connectedOutlet = outlet_new(&gphoto->x_obj, &s_float); - // When we create a thread, make sure it is deatched. - pthread_attr_init(&gphoto->threadAttr); - pthread_attr_setdetachstate(&gphoto->threadAttr, PTHREAD_CREATE_DETACHED); + // Initially the external is not "connected" + gphoto->connected = 0; return (void *)gphoto; } -static void *gphoto_destroy(gphoto_struct *gphoto) { - pthread_attr_destroy(&gphoto->threadAttr); - - return(NULL); +// Destructor to cleanup camera if its still open. +static void *gphoto_free(gphoto_struct *gphoto) { + if (!gphoto->connected) { + gp_camera_free(gphoto->camera); + post("camera free"); // TODO remove me! + } } + void gphoto_setup(void) { - gphoto_class = class_new(gensym("gphoto"), (t_newmethod) gphoto_new, (t_method) gphoto_destroy, sizeof(gphoto_struct), 0, CLASS_DEFAULT, 0); + gphoto_class = class_new(gensym("gphoto"), (t_newmethod) gphoto_new, 0, sizeof(gphoto_struct), 0, CLASS_DEFAULT, 0); + class_addmethod(gphoto_class, (t_method) wrapOpen, gensym("open"), 0); + class_addmethod(gphoto_class, (t_method) wrapClose, gensym("close"), 0); class_addmethod(gphoto_class, (t_method) wrapGetConfig, gensym("getconfig"), A_GIMME, 0); - class_addmethod(gphoto_class, (t_method) wrapGetConfigDetail, gensym("configdetail"), A_GIMME, 0); - class_addmethod(gphoto_class, (t_method) wrapCaptureImage, gensym("captureimage"), A_GIMME, 0); - class_addmethod(gphoto_class, (t_method) wrapCaptureImages, gensym("captureimages"), A_GIMME, 0); class_addmethod(gphoto_class, (t_method) wrapSetConfig, gensym("setconfig"), A_GIMME, 0); + class_addmethod(gphoto_class, (t_method) wrapCaptureImage, gensym("capture"), A_GIMME, 0); class_addmethod(gphoto_class, (t_method) wrapListConfig, gensym("listconfig"), 0); - class_addmethod(gphoto_class, (t_method) reset, gensym("reset"), 0); } diff --git a/gphoto/makefile b/gphoto/makefile index 5b7e264..f87381e 100644 --- a/gphoto/makefile +++ b/gphoto/makefile @@ -10,7 +10,7 @@ pd_linux: gphoto.pd_linux .SUFFIXES: .pd_linux LINUXCFLAGS = -DPD -O2 -funroll-loops -fomit-frame-pointer \ - -Wall -g -W -Wshadow -Wstrict-prototypes \ + -Wall -W -Wshadow -Wstrict-prototypes \ -Wno-unused -Wno-parentheses -Wno-switch \ -lgphoto2 -lpthread @@ -19,7 +19,7 @@ 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 + strip --strip-unneeded $*.pd_linux rm $*.o -- cgit v1.2.1