[PD-cvs] externals/pdp/modules/image_io Makefile, 1.2, 1.3 README, 1.2, 1.3 pdp_glx.c, 1.2, 1.3 pdp_qt.c, 1.2, 1.3 pdp_sdl.c, 1.2, 1.3 pdp_v4l.c, 1.2, 1.3 pdp_xv.c, 1.2, 1.3

Hans-Christoph Steiner eighthave at users.sourceforge.net
Fri Dec 16 02:05:35 CET 2005


Update of /cvsroot/pure-data/externals/pdp/modules/image_io
In directory sc8-pr-cvs1.sourceforge.net:/tmp/cvs-serv6756/modules/image_io

Added Files:
	Makefile README pdp_glx.c pdp_qt.c pdp_sdl.c pdp_v4l.c 
	pdp_xv.c 
Log Message:
checking in pdp 0.12.4 from http://zwizwa.fartit.com/pd/pdp/pdp-0.12.4.tar.gz

--- NEW FILE: pdp_glx.c ---
/*
 *   Pure Data Packet module.
 *   Copyright (c) by Tom Schouten <pdp at zzz.kotnet.org>
 *
 *   This program 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.
 *
 *   This program 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.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

// gl stuff
#include <GL/gl.h>
#include <GL/glx.h>
#include <GL/glu.h>
//#include <GL/glut.h>

// pdp stuff
#include "pdp.h"
#include "pdp_base.h"

// some x window glue code
#include "pdp_xwindow.h"

// pdp stuff
#include "pdp.h"
#include "pdp_llconv.h"
//#include "pdp_opengl.h"


/* initial image dimensions */
#define PDP_OGL_W 320
#define PDP_OGL_H 240

#define PDP_OGL_AUTOCREATE_RETRY 10


typedef struct pdp_glx_struct
{
    t_object x_obj;

    t_pdp_xwindow *x_xwin;

    t_outlet *x_outlet;

    int x_packet0;
    int x_queue_id;
    t_symbol *x_display;

    t_pdp_xdisplay *x_xdpy;

    XVisualInfo *x_vis_info;
    GLXContext x_glx_context;

    GLuint x_texture;
    u32 x_tex_width;
    u32 x_tex_height;

    unsigned char *x_data;
    unsigned int x_width;
    unsigned int x_height;
    int x_last_encoding;

    int  x_initialized;
    int  x_autocreate;

} t_pdp_glx;



static void pdp_glx_cursor(t_pdp_glx *x, t_floatarg f)
{
    if (x->x_initialized)
	pdp_xwindow_cursor(x->x_xwin, f);
}

static void pdp_glx_destroy(t_pdp_glx* x)
{
    t_pdp_procqueue *q = pdp_queue_get_queue();
    XEvent e;

    if (x->x_initialized){
	pdp_procqueue_finish(q, x->x_queue_id);
	x->x_queue_id = -1;
	glXDestroyContext(x->x_xdpy->dpy, x->x_glx_context);
	pdp_xwindow_free(x->x_xwin);
	pdp_xdisplay_free(x->x_xdpy);
	x->x_xwin = 0;
	x->x_xdpy = 0;
	x->x_initialized = false;
    }

}


static void pdp_glx_fullscreen(t_pdp_glx *x)
{
    if (x->x_initialized)
	pdp_xwindow_fullscreen(x->x_xwin);
}

static void pdp_glx_resize(t_pdp_glx* x, t_floatarg width, t_floatarg height)
{
    if (x->x_initialized)
	pdp_xwindow_resize(x->x_xwin, width, height);
}

static void pdp_glx_move(t_pdp_glx* x, t_floatarg width, t_floatarg height)
{
    if (x->x_initialized)
	pdp_xwindow_move(x->x_xwin, width, height);
}

static void pdp_glx_moveresize(t_pdp_glx* x, t_floatarg xoff, t_floatarg yoff, t_floatarg width, t_floatarg height)
{
    if (x->x_initialized)
	pdp_xwindow_moveresize(x->x_xwin, xoff, yoff, width, height);
}

static void pdp_glx_tile(t_pdp_glx* x, t_floatarg xtiles, t_floatarg ytiles, t_floatarg i, t_floatarg j)
{
    if (x->x_initialized)
	pdp_xwindow_tile(x->x_xwin, xtiles, ytiles, i, j);
}




void pdp_glx_generate_texture(t_pdp_glx *x)
{
    u32 width = x->x_tex_width;
    u32 height = x->x_tex_height;
    u32 depth = 4;
    u32 i;

    u8 *dummydata = 0;

    while (x->x_width > width) width <<= 1;
    while (x->x_height > height) height <<= 1;

    dummydata = (u8 *)pdp_alloc(width*height*depth);

    for (i=0; i<width*height*depth; i++){dummydata[i] = random(); }

    /* set window context current */
    glXMakeCurrent(x->x_xdpy->dpy, x->x_xwin->win, x->x_glx_context);

    /* generate texture if necessary */
    if (!glIsTexture(x->x_texture)) glGenTextures(1, &(x->x_texture));

    glBindTexture(GL_TEXTURE_2D, x->x_texture);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, dummydata);

    pdp_dealloc(dummydata);

    x->x_tex_width = width;
    x->x_tex_height = height;
}

void pdp_glx_regenerate_texture(t_pdp_glx *x)
{
    if ((x->x_width > x->x_tex_width) || (x->x_height > x->x_tex_height)) pdp_glx_generate_texture(x);

}


static void pdp_glx_create(t_pdp_glx* x)
{
    unsigned int *uintdata = (unsigned int *)(x->x_data);
    XEvent e;
    unsigned int i;
    static int vis_attr[] = {GLX_RGBA, GLX_RED_SIZE, 4, GLX_GREEN_SIZE, 4, GLX_BLUE_SIZE, 4, 
			     GLX_DEPTH_SIZE, 16, GLX_DOUBLEBUFFER, None};


    if (x->x_initialized) return;

    /* manually open a display */
    if (NULL == (x->x_xdpy = pdp_xdisplay_new(x->x_display->s_name))){
	post("pdp_glx: cant open display %s\n",x->x_display->s_name);
	x->x_initialized = false;
	return;
    }

    /* create a window on the display */
    x->x_xwin = pdp_xwindow_new();
    if (!(x->x_initialized = pdp_xwindow_create_on_display(x->x_xwin, x->x_xdpy))) 
	goto exit_error;


    /* create a glx visual */
    if (!(x->x_vis_info = glXChooseVisual(x->x_xdpy->dpy, x->x_xdpy->screen, vis_attr))){
	post("pdp_glx: can't create visual");
	goto exit_error;
    }
    //post("visual: %x", x->x_vis_info);

    /* create the rendering context */
    if (!(x->x_glx_context = glXCreateContext(x->x_xdpy->dpy, x->x_vis_info,  0 /*share list*/, GL_TRUE))){
	post("pdp_glx: can't create render context");
	goto exit_error;
    }
    //post("context: %x", x->x_glx_context);


    /* create texture */
    pdp_glx_generate_texture(x);


    /* we're done initializing */
    x->x_initialized = true;

    /* disable/enable cursor */
    //pdp_glx_cursor(x, x->x_cursor);
    return;


 exit_error:
    if (x->x_xwin){
	pdp_xwindow_free(x->x_xwin);
	x->x_xwin = 0;
    }
    
    if (x->x_xdpy){
	pdp_xdisplay_free(x->x_xdpy);
	x->x_xdpy = 0;
    }
    
    x->x_initialized = false;
    return;
}

static int pdp_glx_try_autocreate(t_pdp_glx *x)
{

    if (x->x_autocreate){
	post("pdp_glx: autocreate window");
	pdp_glx_create(x);
	if (!(x->x_initialized)){
	    x->x_autocreate--;
	    if (!x->x_autocreate){
		post ("pdp_glx: autocreate failed %d times: disabled", PDP_OGL_AUTOCREATE_RETRY);
		post ("pdp_glx: send [autocreate 1] message to re-enable");
		return 0;
	    }
	}
	else return 1;

    }
    return 0;
}

static void pdp_glx_bang(t_pdp_glx *x);

static void pdp_glx_fill_texture(t_pdp_glx *x)
{
    t_pdp *header = pdp_packet_header(x->x_packet0);
    void  *data   = pdp_packet_data  (x->x_packet0);

    int i = header->info.image.width;


    /* ensure image buffer is correct dim */
    if ((header->info.image.width != x->x_width) 
	|| (header->info.image.height != x->x_height)) {
	if (x->x_data) pdp_dealloc (x->x_data);
	x->x_width = header->info.image.width;
	x->x_height = header->info.image.height;
	x->x_data = pdp_alloc(4*x->x_width*x->x_height);
    }

    /* ensure texture is correct dim */
    pdp_glx_regenerate_texture(x);
    

    /* set window context current */
    glXMakeCurrent(x->x_xdpy->dpy, x->x_xwin->win, x->x_glx_context);
    glBindTexture(GL_TEXTURE_2D, x->x_texture);

    switch (header->info.image.encoding){
    case PDP_IMAGE_GREY:
	/* convert image to greyscale 8 bit */
	pdp_llconv(data,RIF_GREY______S16, x->x_data, RIF_GREY______U8, x->x_width, x->x_height);

	/* upload grey subtexture */
	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, x->x_width, x->x_height, GL_LUMINANCE, GL_UNSIGNED_BYTE, x->x_data);
	    
	break;
    case PDP_IMAGE_YV12:

	/* convert image to rgb 8 bit */
	pdp_llconv(data,RIF_YVU__P411_S16, x->x_data,  RIF_RGB__P____U8, x->x_width, x->x_height);

	/* upload subtexture */
	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, x->x_width, x->x_height, GL_RGB, GL_UNSIGNED_BYTE, x->x_data);
	
	break;
    default:
	break;
    }

    
}

static void pdp_glx_process(t_pdp_glx *x)
{
    t_pdp *header = pdp_packet_header(x->x_packet0);
    void  *data   = pdp_packet_data  (x->x_packet0);


    if (-1 != x->x_queue_id) return;

    /* check if window is initialized */
    if (!(x->x_initialized)){
        if (!pdp_glx_try_autocreate(x)) return;
    }

    /* check data packet */
    if (!(header)) {
	post("pdp_glx: invalid packet header");
	return;
    }
    if (PDP_IMAGE != header->type) {
	post("pdp_glx: packet is not a PDP_IMAGE");
	return;
    }
    if ((PDP_IMAGE_YV12 != header->info.image.encoding)
	&& (PDP_IMAGE_GREY != header->info.image.encoding)) {
	post("pdp_glx: packet is not a PDP_IMAGE_YV12/GREY");
	return;
    }

    
    /* fill the texture with the data in the packet */
    pdp_glx_fill_texture(x);

    /* display the new image */
    pdp_glx_bang(x);


}



static void pdp_glx_display_texture(t_pdp_glx *x)
{
    float fx = (float)x->x_width / x->x_tex_width;
    float fy = (float)x->x_height / x->x_tex_height;

    if (!x->x_initialized) return;

    /* set window context current */
    glXMakeCurrent(x->x_xdpy->dpy, x->x_xwin->win, x->x_glx_context);

    /* setup viewport, projection and modelview */
    glViewport(0, 0, x->x_xwin->winwidth, x->x_xwin->winheight);
    glMatrixMode(GL_PROJECTION);
    glLoadIdentity();
    gluOrtho2D(0.0, x->x_xwin->winwidth, 0.0, x->x_xwin->winheight);
    glMatrixMode(GL_MODELVIEW);
    glLoadIdentity();


    /* enable default texture */
    glEnable(GL_TEXTURE_2D);
    glBindTexture(GL_TEXTURE_2D, x->x_texture);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
    glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);

    /* display texture */  
    glBegin(GL_QUADS);
        glTexCoord2f(fx, fy);
        glVertex2i(x->x_xwin->winwidth,0);
        glTexCoord2f(fx, 0);
	glVertex2i(x->x_xwin->winwidth, x->x_xwin->winheight);
        glTexCoord2f(0.0, 0.0);
	glVertex2i(0, x->x_xwin->winheight);
        glTexCoord2f(0, fy);
	glVertex2i(0,0);
    glEnd();


    glFlush();
    glXSwapBuffers(x->x_xdpy->dpy,x->x_xwin->win);

}



/* redisplays image */
static void pdp_glx_bang_thread(t_pdp_glx *x)
{


    pdp_glx_display_texture(x);
    XFlush(x->x_xdpy->dpy);

}

static void pdp_glx_bang_callback(t_pdp_glx *x)
{
    /* receive events + send to outputs */
    t_pdp_list *eventlist = pdp_xwindow_get_eventlist(x->x_xwin);
    t_pdp_atom *a;

    for (a=eventlist->first; a; a=a->next){
	//pdp_list_print(a->w.w_list);
	outlet_pdp_list(x->x_outlet, a->w.w_list);
    }

    /* free list */
    pdp_tree_free(eventlist);

    /* release the packet if there is one */
    pdp_packet_mark_unused(x->x_packet0);
    x->x_packet0 = -1;

}
static void pdp_glx_bang(t_pdp_glx *x)
{

    /* check if window is initialized */
    if (!(x->x_initialized)){
        if (!pdp_glx_try_autocreate(x)) return;
    }


    /* if previous queued method returned
       schedule a new one, else ignore */

/*
    if (-1 == x->x_queue_id) {
	pdp_queue_add(x, pdp_glx_bang_thread, pdp_glx_bang_callback, &x->x_queue_id);
    }
*/
    /* don't process in thread */
    pdp_glx_bang_thread(x);
    pdp_glx_bang_callback(x);
    
}



static void pdp_glx_input_0(t_pdp_glx *x, t_symbol *s, t_floatarg f)
{

    if (s == gensym("register_ro")) pdp_packet_copy_ro_or_drop(&x->x_packet0, (int)f);
    if (s == gensym("process")) pdp_glx_process(x);
}



static void pdp_glx_vga(t_pdp_glx *x)
{
    pdp_glx_resize(x, 640, 480);
}

static void pdp_glx_autocreate(t_pdp_glx *x, t_floatarg f)
{
  if (f != 0.0f) x->x_autocreate = PDP_OGL_AUTOCREATE_RETRY;
  else x->x_autocreate = 0;
}

static void pdp_glx_display(t_pdp_glx *x, t_symbol *s)
{
    t_pdp_procqueue *q = pdp_queue_get_queue();
    pdp_procqueue_finish(q, x->x_queue_id);
    x->x_queue_id = -1;
    x->x_display = s;
    if (x->x_initialized){
	pdp_glx_destroy(x);
	pdp_glx_create(x);
    }
}



static void pdp_glx_free(t_pdp_glx *x)
{
    t_pdp_procqueue *q = pdp_queue_get_queue();
    pdp_procqueue_finish(q, x->x_queue_id);
    pdp_glx_destroy(x);
    if (x->x_data) pdp_dealloc (x->x_data);
    pdp_packet_mark_unused(x->x_packet0);
}

t_class *pdp_glx_class;



void *pdp_glx_new(void)
{
    t_pdp_glx *x = (t_pdp_glx *)pd_new(pdp_glx_class);

    x->x_xwin = 0;
    x->x_xdpy = 0;

    x->x_outlet = outlet_new(&x->x_obj, &s_anything);

    x->x_packet0 = -1;
    x->x_queue_id = -1;
    x->x_display = gensym(":0");

    x->x_width = PDP_OGL_W;
    x->x_height = PDP_OGL_H;

    x->x_data = pdp_alloc(4*PDP_OGL_W*PDP_OGL_H);

    x->x_initialized = 0;
    pdp_glx_autocreate(x,1);
    x->x_last_encoding = -1;

    x->x_tex_width = 64;
    x->x_tex_height = 64;

    //pdp_glx_create(x);

    return (void *)x;
}





#ifdef __cplusplus
extern "C"
{
#endif


void pdp_glx_setup(void)
{


    pdp_glx_class = class_new(gensym("pdp_glx"), (t_newmethod)pdp_glx_new,
    	(t_method)pdp_glx_free, sizeof(t_pdp_glx), 0, A_NULL);

    /* add creator for pdp_tex_win */

    class_addmethod(pdp_glx_class, (t_method)pdp_glx_bang, gensym("bang"), A_NULL);
    class_addmethod(pdp_glx_class, (t_method)pdp_glx_create, gensym("open"), A_NULL);
    class_addmethod(pdp_glx_class, (t_method)pdp_glx_create, gensym("create"), A_NULL);
    class_addmethod(pdp_glx_class, (t_method)pdp_glx_autocreate, gensym("autocreate"), A_FLOAT, A_NULL);
    class_addmethod(pdp_glx_class, (t_method)pdp_glx_destroy, gensym("destroy"), A_NULL);
    class_addmethod(pdp_glx_class, (t_method)pdp_glx_destroy, gensym("close"), A_NULL);
    class_addmethod(pdp_glx_class, (t_method)pdp_glx_move, gensym("move"), A_FLOAT, A_FLOAT, A_NULL);
    class_addmethod(pdp_glx_class, (t_method)pdp_glx_move, gensym("pos"), A_FLOAT, A_FLOAT, A_NULL);
    class_addmethod(pdp_glx_class, (t_method)pdp_glx_resize, gensym("dim"), A_FLOAT, A_FLOAT, A_NULL);
    class_addmethod(pdp_glx_class, (t_method)pdp_glx_resize, gensym("size"), A_FLOAT, A_FLOAT, A_NULL);
    class_addmethod(pdp_glx_class, (t_method)pdp_glx_display, gensym("display"), A_SYMBOL, A_NULL);
    class_addmethod(pdp_glx_class, (t_method)pdp_glx_cursor, gensym("cursor"), A_FLOAT, A_NULL);
    class_addmethod(pdp_glx_class, (t_method)pdp_glx_fullscreen, gensym("fullscreen"), A_NULL);
    class_addmethod(pdp_glx_class, (t_method)pdp_glx_moveresize, gensym("posdim"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
    class_addmethod(pdp_glx_class, (t_method)pdp_glx_tile, gensym("tile"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);


    /* accept both pdp and pdp_tex packets */
    class_addmethod(pdp_glx_class, (t_method)pdp_glx_input_0, gensym("pdp"),  A_SYMBOL, A_DEFFLOAT, A_NULL);


    /* some shortcuts for the lazy */
    class_addmethod(pdp_glx_class, (t_method)pdp_glx_vga, gensym("vga"), A_NULL);

}

#ifdef __cplusplus
}
#endif



--- NEW FILE: pdp_v4l.c ---
/*
 *   Pure Data Packet module.
 *   Copyright (c) by Tom Schouten <pdp at zzz.kotnet.org>
 *
 *   This program 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.
 *
 *   This program 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.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


#include "pdp_config.h"
#include "pdp.h"
#include "pdp_llconv.h"
#include "pdp_imageproc.h"
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <unistd.h>
#include <string.h>
#include <ctype.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/ioctl.h>
#include <sys/types.h>
#include <sys/time.h>
#include <linux/types.h>
#include <linux/videodev.h>
#include <sys/mman.h>
#include <sched.h>
#include <pthread.h>

// dont open any more after a set number 
// of failed attempts
// this is to prevent locks on auto-open
// is reset when manually opened or closed
#define PDP_XV_RETRIES 10

//include it anyway
//#ifdef HAVE_PWCV4L
#include "pwc-ioctl.h"
//#endif


#define DEVICENO 0
#define NBUF 2
#define COMPOSITEIN 1




typedef struct pdp_v4l_struct
{
  t_object x_obj;
  t_float x_f;
  
  t_outlet *x_outlet0;

  int x_format; // 0 means autodetect  

  bool x_initialized;
  bool x_auto_open;

  unsigned int x_width;
  unsigned int x_height;
  int x_channel;
  unsigned int x_norm;
  int x_freq;

  unsigned int x_framerate;

  struct video_tuner x_vtuner;
  struct video_picture x_vpicture;
  struct video_buffer x_vbuffer;
  struct video_capability x_vcap;
  struct video_channel x_vchannel;
  struct video_audio x_vaudio;
  struct video_mbuf x_vmbuf;
  struct video_mmap x_vmmap[NBUF];
  struct video_window x_vwin;
  int x_tvfd;
  int x_frame;
  unsigned char *x_videobuf;
  int x_skipnext;
  int x_mytopmargin, x_mybottommargin;
  int x_myleftmargin, x_myrightmargin;

    t_symbol *x_device;
    t_symbol *x_image_type;
    //int x_pdp_image_type;
    int x_v4l_palette;

    pthread_t x_thread_id;
    int x_continue_thread;
    int x_frame_ready;
    int x_only_new_frames;
    int x_last_frame;


    int x_open_retry;

    u32 x_minwidth;
    u32 x_maxwidth;
    u32 x_minheight;
    u32 x_maxheight;


} t_pdp_v4l;





static void pdp_v4l_audio(t_pdp_v4l *x, t_floatarg f)
{
    int i = 0;
    if (x->x_initialized){
	fprintf(stderr,"  audios  : %d\n",x->x_vcap.audios);
	x->x_vaudio.audio = 0;
        ioctl(x->x_tvfd,VIDIOCGAUDIO, &x->x_vaudio);

	fprintf(stderr,"    %d (%s): ",i,x->x_vaudio.name);
	if (x->x_vaudio.flags & VIDEO_AUDIO_MUTABLE)
	    fprintf(stderr,"muted=%s ",
		    (x->x_vaudio.flags & VIDEO_AUDIO_MUTE) ? "yes":"no");
	if (x->x_vaudio.flags & VIDEO_AUDIO_VOLUME)
	    fprintf(stderr,"volume=%d ",x->x_vaudio.volume);
	if (x->x_vaudio.flags & VIDEO_AUDIO_BASS)
	    fprintf(stderr,"bass=%d ",x->x_vaudio.bass);
	if (x->x_vaudio.flags & VIDEO_AUDIO_TREBLE)
	    fprintf(stderr,"treble=%d ",x->x_vaudio.treble);
	fprintf(stderr,"\n");
	
    }
}


static void pdp_v4l_close(t_pdp_v4l *x)
{
  /* close the v4l device and dealloc buffer */

    void *dummy;

    /* terminate thread if there is one */
    if(x->x_continue_thread){
	x->x_continue_thread = 0;
	pthread_join (x->x_thread_id, &dummy);
    }


    if (x->x_tvfd >= 0)
    {
        close(x->x_tvfd);
        x->x_tvfd = -1;
    }

    if (x->x_initialized){
	munmap(x->x_videobuf, x->x_vmbuf.size);
	x->x_initialized = false;
    }

}

static void pdp_v4l_close_manual(t_pdp_v4l *x)
{
    x->x_open_retry = PDP_XV_RETRIES;
    pdp_v4l_close(x);

}

static void pdp_v4l_close_error(t_pdp_v4l *x)
{
    pdp_v4l_close(x);
    if(x->x_open_retry) x->x_open_retry--;
}


static void pdp_v4l_pwc_init(t_pdp_v4l *x)
{
    struct pwc_probe probe;
    int isPhilips = 0;

#ifdef HAVE_PWCV4L
    /* skip test */
    isPhilips = 1;
#else
    /* test for pwc */
    if (ioctl(x->x_tvfd, VIDIOCPWCPROBE, &probe) == 0)
       if (!strcmp(x->x_vcap.name, probe.name))
         isPhilips = 1;

#endif
    
    /* don't do pwc specific stuff */
    if (!isPhilips) return;

    post("pdp_v4l: detected pwc");

    if(ioctl(x->x_tvfd, VIDIOCPWCRUSER)){
	perror("pdp_v4l: pwc: VIDIOCPWCRUSER");
	goto closit;
    }
 
    if (ioctl(x->x_tvfd, VIDIOCGWIN, &x->x_vwin)){
	perror("pdp_v4l: pwc: VIDIOCGWIN");
	goto closit;
    }


    
    if (x->x_vwin.flags & PWC_FPS_MASK){
	//post("pdp_v4l: pwc: camera framerate: %d", (x->x_vwin.flags & PWC_FPS_MASK) >> PWC_FPS_SHIFT);
	//post("pdp_v4l: pwc: setting camera framerate to %d", x->x_framerate);
	x->x_vwin.flags &= PWC_FPS_MASK;
	x->x_vwin.flags |= (x->x_framerate << PWC_FPS_SHIFT);
	if (ioctl(x->x_tvfd, VIDIOCSWIN, &x->x_vwin)){
	    perror("pdp_v4l: pwc: VIDIOCSWIN");
	    goto closit;
	}
	if (ioctl(x->x_tvfd, VIDIOCGWIN, &x->x_vwin)){
	    perror("pdp_v4l: pwc: VIDIOCGWIN");
	    goto closit;
	}
	post("pdp_v4l: camera framerate set to %d fps", (x->x_vwin.flags & PWC_FPS_MASK) >> PWC_FPS_SHIFT);

    }
    
    
    return;
  


 closit:
    pdp_v4l_close_error(x);
    return;

}

static void pdp_v4l_sync_frame(t_pdp_v4l* x){
    /* grab frame */
    if (ioctl(x->x_tvfd, VIDIOCSYNC, &x->x_vmmap[x->x_frame].frame) < 0){
	perror("pdp_v4l: VIDIOCSYNC");
	pdp_v4l_close(x);
	return;
    }
} 

static void pdp_v4l_capture_frame(t_pdp_v4l* x){
    if (ioctl(x->x_tvfd, VIDIOCMCAPTURE, &x->x_vmmap[x->x_frame]) < 0){
	if (errno == EAGAIN)
	    post("pdp_v4l: can't sync (no video source?)\n");
	else 
	    perror("pdp_v4l: VIDIOCMCAPTURE");
	if (ioctl(x->x_tvfd, VIDIOCMCAPTURE, &x->x_vmmap[x->x_frame]) < 0)
	    perror("pdp_v4l: VIDIOCMCAPTURE2");
	
	post("pdp_v4l: frame %d %d, format %d, width %d, height %d",
	     x->x_frame, x->x_vmmap[x->x_frame].frame, x->x_vmmap[x->x_frame].format,
	     x->x_vmmap[x->x_frame].width, x->x_vmmap[x->x_frame].height);
	
	pdp_v4l_close(x);
	return;
    }
}


static void *pdp_v4l_thread(void *voidx)
{
    t_pdp_v4l *x = ((t_pdp_v4l *)voidx);


    /* flip buffers */
    x->x_frame ^= 0x1;

    /* capture with a double buffering scheme */
    while (x->x_continue_thread){

	/* schedule capture command for next frame */
	pdp_v4l_capture_frame(x);

	/* wait until previous capture is ready */
	x->x_frame ^= 0x1;
	pdp_v4l_sync_frame(x);

	/* setup pointers for main thread */
	x->x_frame_ready = 1;
	x->x_last_frame = x->x_frame;

    }

    return 0;
}

static void pdp_v4l_setlegaldim(t_pdp_v4l *x, int xx, int yy);

static void pdp_v4l_open(t_pdp_v4l *x, t_symbol *name)
{
  /* open a v4l device and allocate a buffer */

    unsigned int size;
    int i;

    unsigned int width, height;


    /* if already opened -> close */
    if (x->x_initialized) pdp_v4l_close(x);


    /* exit if retried too much */
    if (!x->x_open_retry){
	post("pdp_v4l: retry count reached zero for %s", name->s_name);
	post("pdp_v4l: try to open manually");
	return;
    }

    post("pdp_v4l: opening %s", name->s_name);

    x->x_device = name;

    if ((x->x_tvfd = open(name->s_name, O_RDWR)) < 0)
    {
      post("pdp_v4l: error:");
        perror(name->s_name);
        goto closit;
    }


    if (ioctl(x->x_tvfd, VIDIOCGCAP, &x->x_vcap) < 0)
    {
        perror("get capabilities");
        goto closit;
    }

    post("pdp_v4l: cap: name %s type %d channels %d maxw %d maxh %d minw %d minh %d",
        x->x_vcap.name, x->x_vcap.type,  x->x_vcap.channels,  x->x_vcap.maxwidth,  x->x_vcap.maxheight,
            x->x_vcap.minwidth,  x->x_vcap.minheight);

    x->x_minwidth = pdp_imageproc_legalwidth(x->x_vcap.minwidth);
    x->x_maxwidth = pdp_imageproc_legalwidth_round_down(x->x_vcap.maxwidth);
    x->x_minheight = pdp_imageproc_legalheight(x->x_vcap.minheight);
    x->x_maxheight = pdp_imageproc_legalheight_round_down(x->x_vcap.maxheight);

 
    if (ioctl(x->x_tvfd, VIDIOCGPICT, &x->x_vpicture) < 0)
    {
        perror("VIDIOCGCAP");
        goto closit;
    }
    
    post("pdp_v4l: picture: brightness %d depth %d palette %d",
            x->x_vpicture.brightness, x->x_vpicture.depth, x->x_vpicture.palette);

    /* get channel info */
    for (i = 0; i < x->x_vcap.channels; i++)
    {
        x->x_vchannel.channel = i;
        if (ioctl(x->x_tvfd, VIDIOCGCHAN, &x->x_vchannel) < 0)
        {
            perror("VDIOCGCHAN");
            goto closit;
        }
        post("pdp_v4l: channel %d name %s type %d flags %d",
            x->x_vchannel.channel, x->x_vchannel.name, 
            x->x_vchannel.type, x->x_vchannel.flags);
    }

    /* switch to the desired channel */
    if (x->x_channel < 0) x->x_channel = 0;
    if (x->x_channel >= x->x_vcap.channels) x->x_channel = x->x_vcap.channels - 1;

    x->x_vchannel.channel = x->x_channel;
    if (ioctl(x->x_tvfd, VIDIOCGCHAN, &x->x_vchannel) < 0)
    {
        perror("pdp_v4l: warning: VDIOCGCHAN");
        post("pdp_v4l: cant change to channel %d",x->x_channel);

        // ignore error
        // goto closit;
    }
    else{
	post("pdp_v4l: switched to channel %d", x->x_channel);
    }


    /* set norm */
    x->x_vchannel.norm = x->x_norm;
    if (ioctl(x->x_tvfd, VIDIOCSCHAN, &x->x_vchannel) < 0)
    {
        perror("pdp_v4l: warning: VDIOCSCHAN");
        post("pdp_v4l: cant change to norm %d",x->x_norm);
        
        // ignore error
        // goto closit;
    }
    else {
	post("pdp_v4l: set norm to %u", x->x_norm);
    }

    if (x->x_freq > 0){
	if (ioctl(x->x_tvfd, VIDIOCSFREQ, &x->x_freq) < 0)
	    perror ("couldn't set frequency :");
    }


   

    /* get mmap numbers */
    if (ioctl(x->x_tvfd, VIDIOCGMBUF, &x->x_vmbuf) < 0)
    {
        perror("pdp_v4l: VIDIOCGMBUF");
        goto closit;
    }
    post("pdp_v4l: buffer size %d, frames %d, offset %d %d", x->x_vmbuf.size,
        x->x_vmbuf.frames, x->x_vmbuf.offsets[0], x->x_vmbuf.offsets[1]);
    if (!(x->x_videobuf = (unsigned char *)
        mmap(0, x->x_vmbuf.size, PROT_READ|PROT_WRITE, MAP_SHARED, x->x_tvfd, 0)))
    {
        perror("pdp_v4l: mmap");
        goto closit;
    }

    pdp_v4l_setlegaldim(x, x->x_width, x->x_height);
    width = x->x_width;
    height = x->x_height;

    for (i = 0; i < NBUF; i++)
    {
      //x->x_vmmap[i].format = VIDEO_PALETTE_YUV420P;
      //x->x_vmmap[i].format = VIDEO_PALETTE_UYVY;
      x->x_vmmap[i].width = width;
      x->x_vmmap[i].height = height;
      x->x_vmmap[i].frame  = i;
    }


/* fallthrough macro for case statement */
#define TRYPALETTE(palette)							\
	x->x_v4l_palette = palette;						\
	for (i = 0; i < NBUF; i++) x->x_vmmap[i].format = x->x_v4l_palette;	\
	if (ioctl(x->x_tvfd, VIDIOCMCAPTURE, &x->x_vmmap[x->x_frame]) < 0)	\
	    {									\
		if (errno == EAGAIN)						\
		    post("pdp_v4l: can't sync (no video source?)");		\
		if (x->x_format) break; /* only break if not autodetecting */	\
	    }									\
	else{									\
	    post("pdp_v4l: using " #palette);					\
	    goto capture_ok;							\
	}

    switch(x->x_format){
    default:
    case 0:
    case 1: TRYPALETTE(VIDEO_PALETTE_YUV420P);
    case 2: TRYPALETTE(VIDEO_PALETTE_YUV422);
    case 3: TRYPALETTE(VIDEO_PALETTE_RGB24);
    case 4: TRYPALETTE(VIDEO_PALETTE_RGB32);
    }

    // none of the formats are supported
    perror("pdp_v4l: VIDIOCMCAPTURE: format not supported");
    goto closit;


 capture_ok:

    post("pdp_v4l: frame %d %d, format %d, width %d, height %d",
        x->x_frame, x->x_vmmap[x->x_frame].frame, x->x_vmmap[x->x_frame].format,
        x->x_vmmap[x->x_frame].width, x->x_vmmap[x->x_frame].height);

    x->x_width = width;
    x->x_height = height;

    post("pdp_v4l: Opened video connection (%dx%d)",x->x_width,x->x_height);


    /* do some pwc specific inits */
    pdp_v4l_pwc_init(x);


    x->x_initialized = true;

    /* create thread */
    x->x_continue_thread = 1;
    x->x_frame_ready = 0;
    pthread_create(&x->x_thread_id, 0, pdp_v4l_thread, x);

    return;
 closit:
    pdp_v4l_close_error(x);

}

static void pdp_v4l_open_manual(t_pdp_v4l *x, t_symbol *name)
{
    x->x_open_retry = PDP_XV_RETRIES;
    pdp_v4l_open(x, name);
}


static void pdp_v4l_channel(t_pdp_v4l *x, t_float f)
{
    int channel = (float)f;

    if (x->x_initialized){
	pdp_v4l_close(x);
        x->x_channel = channel;
	pdp_v4l_open(x, x->x_device);
    }
    else
	x->x_channel = channel;
}

static void pdp_v4l_norm(t_pdp_v4l *x, t_symbol *s)
{
    unsigned int norm;

    if (gensym("PAL") == s) norm = VIDEO_MODE_PAL;
    else if (gensym("NTSC") == s) norm = VIDEO_MODE_NTSC;
    else if (gensym("SECAM") == s) norm = VIDEO_MODE_SECAM;
    else norm = VIDEO_MODE_AUTO;
    


    if (x->x_initialized){
	pdp_v4l_close(x);
        x->x_norm = norm;
	pdp_v4l_open(x, x->x_device);
    }
    else
	x->x_norm = norm;
}

static void pdp_v4l_freq(t_pdp_v4l *x, t_float f)
{
        int freq = (int)f;

	x->x_freq = freq;
	if (x->x_freq > 0){
	    if (ioctl(x->x_tvfd, VIDIOCSFREQ, &x->x_freq) < 0)
		perror ("couldn't set frequency :");
	    //else {post("pdp_v4l: tuner frequency: %f MHz", f / 16.0f);}
	}

}

static void pdp_v4l_freqMHz(t_pdp_v4l *x, t_float f)
{
    pdp_v4l_freq(x, f*16.0f);
}


static void pdp_v4l_bang(t_pdp_v4l *x)
{
   
  /* if initialized, grab a frame and output it */

    unsigned int w,h,nbpixels,packet_size,plane1,plane2;
    unsigned char *newimage;
    int object,length,pos,i,encoding;
    t_pdp* header;
    t_image* image;
    short int * data;


    static short int gain[4] = {0x7fff, 0x7fff, 0x7fff, 0x7fff};

    if (!(x->x_initialized)){
	post("pdp_v4l: no device opened");

	if (x->x_auto_open){
	  post("pdp_v4l: attempting auto open");
	  pdp_v4l_open(x, x->x_device);
	  if (!(x->x_initialized)){
	    post("pdp_v4l: auto open failed");
	    return;
	  }
	}

	else return;
    }


    /* do nothing if there is no frame ready */
    if((!x->x_frame_ready) && (x->x_only_new_frames)) return;
    x->x_frame_ready = 0;

    /* get the address of the "other" frame */
    newimage = x->x_videobuf + x->x_vmbuf.offsets[x->x_last_frame];

    /* create new packet */
    w = x->x_width;
    h = x->x_height;

    //nbpixels = w * h;

/*
    switch(x->x_pdp_image_type){
    case PDP_IMAGE_GREY:   
        packet_size = nbpixels << 1; 
        break;
    case PDP_IMAGE_YV12:   
	packet_size = (nbpixels + (nbpixels >> 1)) << 1;
	break;
    default:
	packet_size = 0;
	post("pdp_v4l: internal error");
    }
*/

    //packet_size = (nbpixels + (nbpixels >> 1)) << 1;


    //plane1 = nbpixels;
    //plane2 = nbpixels + (nbpixels>>2);

    object = pdp_packet_new_image(PDP_IMAGE_YV12, w, h);
    header = pdp_packet_header(object);
    image = pdp_packet_image_info(object);

    if (!header){
	post("pdp_v4l: ERROR: can't allocate packet");
	return;
    }

    data = (short int *) pdp_packet_data(object);
    newimage = x->x_videobuf + x->x_vmbuf.offsets[x->x_frame ^ 0x1];


    /* convert data to pdp packet */

    switch(x->x_v4l_palette){
    case  VIDEO_PALETTE_YUV420P:
	pdp_llconv(newimage, RIF_YUV__P411_U8, data, RIF_YVU__P411_S16, w, h); 
	break;
	
	/* long live standards. v4l's rgb is in fact ogl's bgr */
    case  VIDEO_PALETTE_RGB24:
	pdp_llconv(newimage, RIF_BGR__P____U8, data, RIF_YVU__P411_S16, w, h); 
	break;

    case  VIDEO_PALETTE_RGB32:
	pdp_llconv(newimage, RIF_BGRA_P____U8, data, RIF_YVU__P411_S16, w, h); 
	break;

    case  VIDEO_PALETTE_YUV422:
	pdp_llconv(newimage, RIF_YUYV_P____U8, data, RIF_YVU__P411_S16, w, h); 
	break;


    default:
	post("pdp_v4l: unsupported palette");
	break;
    }

/*
    if (PDP_IMAGE_YV12 == x->x_pdp_image_type){
	pixel_unpack_u8s16_y(&newimage[0], data, nbpixels>>3, x->x_state_data->gain);
	pixel_unpack_u8s16_uv(&newimage[plane1], &data[plane2], nbpixels>>5, x->x_state_data->gain);
	pixel_unpack_u8s16_uv(&newimage[plane2], &data[plane1], nbpixels>>5, x->x_state_data->gain);
    }
*/
    //x->x_v4l_palette = VIDEO_PALETTE_YUV420P;
    //x->x_v4l_palette = VIDEO_PALETTE_RGB24;

/*

    else if(PDP_IMAGE_GREY == x->x_pdp_image_type){
	pixel_unpack_u8s16_y(&newimage[0], data, nbpixels>>3, x->x_state_data->gain);
    }
*/
    //post("pdp_v4l: mark unused %d", object);

    pdp_packet_pass_if_valid(x->x_outlet0, &object);

}


static void pdp_v4l_setlegaldim(t_pdp_v4l *x, int xx, int yy)
{

    unsigned int w,h;

    w  = pdp_imageproc_legalwidth((int)xx);
    h  = pdp_imageproc_legalheight((int)yy);
    
    w = (w < x->x_maxwidth) ? w : x->x_maxwidth;
    w = (w > x->x_minwidth) ? w : x->x_minwidth;

    h = (h < x->x_maxheight) ? h : x->x_maxheight;
    h = (h > x->x_minheight) ? h : x->x_minheight;

    x->x_width = w;
    x->x_height = h;
}

static void pdp_v4l_dim(t_pdp_v4l *x, t_floatarg xx, t_floatarg yy)
{
  if (x->x_initialized){
    pdp_v4l_close(x);
    pdp_v4l_setlegaldim(x, (int)xx, (int)yy);
    pdp_v4l_open(x, x->x_device);
    
  }
  else{
    pdp_v4l_setlegaldim(x, (int)xx, (int)yy);
  }
}

static void pdp_v4l_format(t_pdp_v4l *x, t_symbol *s)
{
    if      (s == gensym("YUV420P")) x->x_format = 1;
    else if (s == gensym("YUV422"))  x->x_format = 2;
    else if (s == gensym("RGB24"))   x->x_format = 3;
    else if (s == gensym("RGB32"))   x->x_format = 4;
    else if (s == gensym("auto"))    x->x_format = 0;
    else {
	post("pdp_v4l: format %s unknown, using autodetect", s->s_name);
	x->x_format = 0;
    }

    if (x->x_initialized){
	pdp_v4l_close(x);
	pdp_v4l_open(x, x->x_device);
    }
}


static void pdp_v4l_free(t_pdp_v4l *x)
{
    pdp_v4l_close(x);
}

t_class *pdp_v4l_class;



void *pdp_v4l_new(t_symbol *vdef, t_symbol *format)
{
    t_pdp_v4l *x = (t_pdp_v4l *)pd_new(pdp_v4l_class);

    x->x_outlet0 = outlet_new(&x->x_obj, &s_anything);

    x->x_initialized = false;


    x->x_tvfd = -1;
    x->x_frame = 0;
    x->x_last_frame = 0;

    x->x_framerate = 27;

    x->x_auto_open = true;
    if (vdef != gensym("")){
	x->x_device = vdef;
    }
    else{
	x->x_device = gensym("/dev/video0");
    }

    if (format != gensym("")){
	pdp_v4l_format(x, format);
    }
    else {
	x->x_format = 0; // default is autodetect
    }

    x->x_continue_thread = 0;
    x->x_only_new_frames = 1;

    x->x_width = 320;
    x->x_height = 240;

//    pdp_v4l_type(x, gensym("yv12"));


    x->x_open_retry = PDP_XV_RETRIES;

    x->x_channel = 0;
    x->x_norm = 0; // PAL
    x->x_freq = -1; //don't set freq by default

    x->x_minwidth = pdp_imageproc_legalwidth(0);
    x->x_maxwidth = pdp_imageproc_legalwidth_round_down(0x7fffffff);
    x->x_minheight = pdp_imageproc_legalheight(0);
    x->x_maxheight = pdp_imageproc_legalheight_round_down(0x7fffffff);


    return (void *)x;
}


#ifdef __cplusplus
extern "C"
{
#endif


void pdp_v4l_setup(void)
{


    pdp_v4l_class = class_new(gensym("pdp_v4l"), (t_newmethod)pdp_v4l_new,
    	(t_method)pdp_v4l_free, sizeof(t_pdp_v4l), 0, A_DEFSYMBOL, A_DEFSYMBOL, A_NULL);


    class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_bang, gensym("bang"), A_NULL);
    class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_audio, gensym("audio"), A_NULL);
    class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_close_manual, gensym("close"), A_NULL);
    class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_open_manual, gensym("open"), A_SYMBOL, A_NULL);
    class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_channel, gensym("channel"), A_FLOAT, A_NULL);
    class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_norm, gensym("norm"), A_SYMBOL, A_NULL);
    class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_dim, gensym("dim"), A_FLOAT, A_FLOAT, A_NULL);
    class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_freq, gensym("freq"), A_FLOAT, A_NULL);
    class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_freqMHz, gensym("freqMHz"), A_FLOAT, A_NULL);
    class_addmethod(pdp_v4l_class, (t_method)pdp_v4l_format, gensym("captureformat"), A_SYMBOL, A_NULL);

    

}

#ifdef __cplusplus
}
#endif

--- NEW FILE: Makefile ---
current: all_modules

include ../../Makefile.config

# build optional modules
all_modules: $(PDP_OPTMOD)

clean:
	rm -f *~
	rm -f *.o


--- NEW FILE: pdp_xv.c ---
/*
 *   Pure Data Packet module. Xvideo image packet output
 *   Copyright (c) by Tom Schouten <pdp at zzz.kotnet.org>
 *
 *   This program 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.
 *
 *   This program 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.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

// pdp stuff
#include "pdp.h"
#include "pdp_base.h"

// some x window glue code
#include "pdp_xwindow.h"
#include "pdp_xvideo.h"


#define PDP_XV_AUTOCREATE_RETRY 10


typedef struct pdp_xv_struct
{
    t_object x_obj;

    t_pdp_xdisplay *x_xdpy;
    t_pdp_xwindow *x_xwin;
    t_pdp_xvideo *x_xvid;

    t_outlet *x_outlet;

    int x_packet0;
    int x_queue_id;
    t_symbol *x_display;

    //Display *x_dpy;

    int  x_initialized;
    int  x_autocreate;


} t_pdp_xv;


static void pdp_xv_cursor(t_pdp_xv *x, t_floatarg f)
{
    if (x->x_xwin) pdp_xwindow_cursor(x->x_xwin, f);
}

/* delete all submodules */
static void _pdp_xv_cleanup(t_pdp_xv *x)
{
    if (x->x_xwin) pdp_xwindow_free(x->x_xwin);
    if (x->x_xvid) pdp_xvideo_free(x->x_xvid);
    if (x->x_xdpy) pdp_xdisplay_free(x->x_xdpy);
    x->x_xwin = 0;
    x->x_xvid = 0;
    x->x_xdpy = 0;
    x->x_initialized = 0;
}

/* wait for thread to finish */
static void _pdp_xv_waitforthread(t_pdp_xv *x)
{
    t_pdp_procqueue *q = pdp_queue_get_queue();
    pdp_procqueue_finish(q, x->x_queue_id);   // wait for thread to finish
    x->x_queue_id = -1;
}

// this destroys the window and all the x connections
static void pdp_xv_destroy(t_pdp_xv* x)
{
    if (x->x_initialized){
	
	_pdp_xv_waitforthread(x);  // wait for thread
	_pdp_xv_cleanup(x);        // delete all objects

	pdp_packet_mark_unused(x->x_packet0); // delete packet
	x->x_packet0 = -1;
	x->x_initialized = 0;

    }
}


/* this creates a window (opens a dpy connection, creates xvideo and xwinow objects) */
static void pdp_xv_create(t_pdp_xv* x)
{
    int i;
    if(x->x_initialized) return;

    
    /* open a display */
    if (!(x->x_xdpy = pdp_xdisplay_new(x->x_display->s_name))) goto exit;

    /* open an xv port on the display */
    x->x_xvid = pdp_xvideo_new();
    if (!pdp_xvideo_open_on_display(x->x_xvid, x->x_xdpy)) goto exit;

    /* create a window on the display */
    x->x_xwin = pdp_xwindow_new();
    if (!pdp_xwindow_create_on_display(x->x_xwin, x->x_xdpy)) goto exit;

    /* done */
    x->x_initialized = 1;
    return;

    /* cleanup exits */
 exit:
    post("pdp_xv: cant open display %s\n",x->x_display->s_name);
    _pdp_xv_cleanup(x);

}

static int pdp_xv_try_autocreate(t_pdp_xv *x)
{

    if (x->x_autocreate){
	post("pdp_xv: autocreate window");
	pdp_xv_create(x);
	if (!(x->x_initialized)){
	    x->x_autocreate--;
	    if (!x->x_autocreate){
		post ("pdp_xv: autocreate failed %d times: disabled", PDP_XV_AUTOCREATE_RETRY);
		post ("pdp_xv: send [autocreate 1] message to re-enable");
		return 0;
	    }
	}
	else return 1;

    }
    return 0;
}

static void pdp_xv_bang(t_pdp_xv *x);

static void pdp_xv_bang_thread(t_pdp_xv *x)
{
    pdp_xvideo_display_packet(x->x_xvid, x->x_xwin, x->x_packet0);
}


static void pdp_xv_bang_callback(t_pdp_xv *x)
{
    /* receive events + send to outputs */
    t_pdp_list *eventlist = pdp_xwindow_get_eventlist(x->x_xwin);
    t_pdp_atom *a;

    for (a=eventlist->first; a; a=a->next){
	//pdp_list_print(a->w.w_list);
	outlet_pdp_list(x->x_outlet, a->w.w_list);
    }

    /* free list */
    pdp_tree_free(eventlist);

    /* release the packet if there is one */
    pdp_packet_mark_unused(x->x_packet0);
    x->x_packet0 = -1;

}

/* manually poll for events */
static void pdp_xv_poll(t_pdp_xv *x)
{
    if (x->x_initialized)
	pdp_xv_bang_callback(x);
}

static void pdp_xv_bang(t_pdp_xv *x)
{
    t_pdp_procqueue *q = pdp_queue_get_queue();

    /* check if window is initialized */
    if (!(x->x_initialized)){
        if (!pdp_xv_try_autocreate(x)) return;
    }

    /* check if we can proceed */
    if (-1 != x->x_queue_id) return;
    if (-1 == x->x_packet0) return;

    /* if previous queued method returned
       schedule a new one, else ignore */
    if (-1 == x->x_queue_id) {
	pdp_procqueue_add(q, x, pdp_xv_bang_thread, pdp_xv_bang_callback, &x->x_queue_id);
    }
    
}

static void pdp_xv_input_0(t_pdp_xv *x, t_symbol *s, t_floatarg f)
{

    if (s == gensym("register_ro")) pdp_packet_convert_ro_or_drop(&x->x_packet0, (int)f, pdp_gensym("bitmap/yv12/*"));
    if (s == gensym("process")) pdp_xv_bang(x);

}



static void pdp_xv_autocreate(t_pdp_xv *x, t_floatarg f)
{
  if (f != 0.0f) x->x_autocreate = PDP_XV_AUTOCREATE_RETRY;
  else x->x_autocreate = 0;
}

static void pdp_xv_display(t_pdp_xv *x, t_symbol *s)
{
    _pdp_xv_waitforthread(x);
    x->x_display = s;

    /* only create if already active */
    if (x->x_initialized){
	pdp_xv_destroy(x);
	pdp_xv_create(x);
    }
}

static void pdp_xv_movecursor(t_pdp_xv *x, float cx, float cy)
{
    if (x->x_initialized){
	cx *= x->x_xwin->winwidth;
	cy *= x->x_xwin->winheight;
	pdp_xwindow_warppointer(x->x_xwin, cx, cy);
    }
}

static void pdp_xv_fullscreen(t_pdp_xv *x)
{
    if (x->x_initialized)
	pdp_xwindow_fullscreen(x->x_xwin);
}

static void pdp_xv_resize(t_pdp_xv* x, t_floatarg width, t_floatarg height)
{
    if (x->x_initialized)
	pdp_xwindow_resize(x->x_xwin, width, height);
}

static void pdp_xv_move(t_pdp_xv* x, t_floatarg width, t_floatarg height)
{
    if (x->x_initialized)
	pdp_xwindow_move(x->x_xwin, width, height);
}

static void pdp_xv_moveresize(t_pdp_xv* x, t_floatarg xoff, t_floatarg yoff, t_floatarg width, t_floatarg height)
{
    if (x->x_initialized)
	pdp_xwindow_moveresize(x->x_xwin, xoff, yoff, width, height);
}

static void pdp_xv_tile(t_pdp_xv* x, t_floatarg xtiles, t_floatarg ytiles, t_floatarg i, t_floatarg j)
{
    if (x->x_initialized)
	pdp_xwindow_tile(x->x_xwin, xtiles, ytiles, i, j);
}

static void pdp_xv_vga(t_pdp_xv *x)
{
    pdp_xv_resize(x, 640, 480);
}

static void pdp_xv_free(t_pdp_xv *x)
{
    pdp_xv_destroy(x);
}

t_class *pdp_xv_class;



void *pdp_xv_new(void)
{
    t_pdp_xv *x = (t_pdp_xv *)pd_new(pdp_xv_class);
    x->x_outlet = outlet_new(&x->x_obj, &s_anything);
    x->x_xwin = 0;
    x->x_xvid = 0;
    x->x_xdpy = 0;
    x->x_packet0 = -1;
    x->x_queue_id = -1;
    x->x_display = gensym(":0");
    x->x_xdpy = 0;
    pdp_xv_autocreate(x,1);

    return (void *)x;
}





#ifdef __cplusplus
extern "C"
{
#endif


void pdp_xv_setup(void)
{
    pdp_xv_class = class_new(gensym("pdp_xv"), (t_newmethod)pdp_xv_new,
			     (t_method)pdp_xv_free, sizeof(t_pdp_xv), 0, A_NULL);


    class_addmethod(pdp_xv_class, (t_method)pdp_xv_bang, gensym("bang"), A_NULL);
    class_addmethod(pdp_xv_class, (t_method)pdp_xv_create, gensym("open"), A_NULL);
    class_addmethod(pdp_xv_class, (t_method)pdp_xv_create, gensym("create"), A_NULL);
    class_addmethod(pdp_xv_class, (t_method)pdp_xv_autocreate, gensym("autocreate"), A_FLOAT, A_NULL);
    class_addmethod(pdp_xv_class, (t_method)pdp_xv_destroy, gensym("destroy"), A_NULL);
    class_addmethod(pdp_xv_class, (t_method)pdp_xv_destroy, gensym("close"), A_NULL);
    class_addmethod(pdp_xv_class, (t_method)pdp_xv_resize, gensym("dim"), A_FLOAT, A_FLOAT, A_NULL);
    class_addmethod(pdp_xv_class, (t_method)pdp_xv_move, gensym("move"), A_FLOAT, A_FLOAT, A_NULL);
    class_addmethod(pdp_xv_class, (t_method)pdp_xv_move, gensym("pos"), A_FLOAT, A_FLOAT, A_NULL);
    class_addmethod(pdp_xv_class, (t_method)pdp_xv_resize, gensym("size"), A_FLOAT, A_FLOAT, A_NULL);
    class_addmethod(pdp_xv_class, (t_method)pdp_xv_display, gensym("display"), A_SYMBOL, A_NULL);
    class_addmethod(pdp_xv_class, (t_method)pdp_xv_input_0, gensym("pdp"),  A_SYMBOL, A_DEFFLOAT, A_NULL);
    class_addmethod(pdp_xv_class, (t_method)pdp_xv_cursor, gensym("cursor"), A_FLOAT, A_NULL);
    class_addmethod(pdp_xv_class, (t_method)pdp_xv_movecursor, gensym("movecursor"), A_FLOAT, A_FLOAT, A_NULL);
    class_addmethod(pdp_xv_class, (t_method)pdp_xv_fullscreen, gensym("fullscreen"), A_NULL);
    class_addmethod(pdp_xv_class, (t_method)pdp_xv_poll, gensym("poll"), A_NULL);
    class_addmethod(pdp_xv_class, (t_method)pdp_xv_moveresize, gensym("posdim"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);
    class_addmethod(pdp_xv_class, (t_method)pdp_xv_tile, gensym("tile"), A_FLOAT, A_FLOAT, A_FLOAT, A_FLOAT, A_NULL);

    /* some shortcuts for the lazy */
    class_addmethod(pdp_xv_class, (t_method)pdp_xv_vga, gensym("vga"), A_NULL);

}

#ifdef __cplusplus
}
#endif



--- NEW FILE: README ---
This directory contains input/output modules for image packets.
Most of this is platform dependent stuff, and will be conditionally compiled.

--- NEW FILE: pdp_sdl.c ---
/*
 *   Pure Data Packet module.
 *   Copyright (c) 2003 by martin pi <pi at attacksyour.net>
 *   Copyright (c) by Tom Schouten <pdp at zzz.kotnet.org>
 *
 *   This program 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.
 *
 *   This program 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.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */

/*

pdp sdl output

DONE:

TODO:
 * close window (event)
 * fullscreen chose resolution
 * event handling in different object (and look at mplayer for that!)

*/


#include <stdio.h>
#include <SDL/SDL.h>
#include "pdp.h"
#include "pdp_llconv.h"




/* initial image dimensions */

#define WINWIDTH 640
#define WINHEIGHT 480
#define OVERLAYWIDTH 320
#define OVERLAYHEIGHT 240



typedef struct pdp_sdl_struct {
    t_object x_obj;

    SDL_Surface *x_surface;
    SDL_Overlay *x_overlay;
    int x_surface_flags;
	
    int x_surface_width;
    int x_surface_height;

    unsigned int x_overlay_width;
    unsigned int x_overlay_height;
    

    t_outlet *x_outlet;


} t_pdp_sdl;

static t_pdp_sdl *sdl_singleton; // only one instance allowed

static void destroy_overlay(t_pdp_sdl *x) {
    if (x->x_overlay){
	SDL_FreeYUVOverlay(x->x_overlay);
	x->x_overlay = 0;
    }
}

static void create_overlay(t_pdp_sdl *x, int width, int height) {
    if (x->x_surface){
	if (x->x_overlay = SDL_CreateYUVOverlay(width, height, SDL_YV12_OVERLAY, x->x_surface)){
	    x->x_overlay_width = width;
	    x->x_overlay_height = height;
	    return;
	}
    }
    pdp_post("SDL: can't create overlay.");
}

static void check_overlay(t_pdp_sdl *x, unsigned int width, unsigned int height){
    if (!x->x_overlay
	|| (x->x_overlay_width != width)
	|| (x->x_overlay_height != height)){
	destroy_overlay(x);
	create_overlay(x, width, height);
    }
}


static void create_surface(t_pdp_sdl *x, int width, int height, int flags)
{
    flags |= SDL_HWSURFACE|SDL_ANYFORMAT|SDL_RESIZABLE; // add default flags
//    flags |= SDL_HWSURFACE|SDL_ANYFORMAT; // add default flags
//    flags |= SDL_SWSURFACE|SDL_ANYFORMAT; // add default flags

    /* flags:
       SDL_HWSURFACE use hardware surface
       SDL_ANYFORMAT return current surface, even if it doesn't match
       SDL_OPENGL|SDL_DOUBLEBUF double buffer and opengl
       SDL_RLEACCEL rle accelleration for blitting
       SDL_FULLSCREEN fullscreen window
     */

    //pdp_post("create_surface %d %d %d", width, height, flags);

    /* check args */
    if (width < 1) width = 1;
    if (height < 1) height = 1;

    /* free old stuff */
    if (x->x_overlay) destroy_overlay(x);
    /* form manpage:
       The framebuffer surface, or NULL if it fails. The surface returned 
       is freed by SDL_Quit() and should nt be freed by the caller. */
    if (x->x_surface) { /*SDL_FreeSurface(surface);*/ }


    /* create new surface */
    if (!(x->x_surface = SDL_SetVideoMode(width, height, 16, flags))){
	pdp_post("SDL: Couldn't create a surface: %s", SDL_GetError());
	return;
    }

    /* setup surface */
    SDL_WM_SetCaption("pdp", "pdp");
    SDL_ShowCursor(0);
    /* set event mask to something conservative
       and add a word to ask for some types of events */
    x->x_surface_width = width;
    x->x_surface_height = height;
    x->x_surface_flags = flags;

}

static void poll_events(t_pdp_sdl *x){

    SDL_Event event;
    static t_symbol *keydown=0, *keyup, *quit, *motion;
    t_atom atom;

    /* cache symbols */
    if (!keydown){
	keydown = gensym("keypress");
	keyup = gensym("keyrelease");
	quit = gensym("quit");
    }

    if (!x->x_surface) return;

    /* poll events */
    while(SDL_PollEvent(&event)){  
        switch(event.type){

	case SDL_KEYDOWN:
	    SETFLOAT(&atom, (float)event.key.keysym.scancode);
	    outlet_anything(x->x_outlet, keydown, 1, &atom);
	    break;
	    
	case SDL_KEYUP:
	    SETFLOAT(&atom, (float)event.key.keysym.scancode);
	    outlet_anything(x->x_outlet, keyup, 1, &atom);
	    break;
		
	case SDL_QUIT:
	    outlet_symbol(x->x_outlet, quit);
	    break;

	case SDL_VIDEORESIZE:
	    create_surface(x, event.resize.w, event.resize.h, x->x_surface_flags);
	    break;
        }
    }
}


static void fullscreen(t_pdp_sdl *x, t_floatarg f)
{
    post("fullscreen not implemented");
}


static void resize(t_pdp_sdl *x, t_floatarg fw, t_floatarg fh)
{
    create_surface(x, (int)fw, (int)fh, 0);
}



static void input_0(t_pdp_sdl *x, t_symbol *s, t_floatarg f) {

    int input_packet = (int)f;
    if (s == gensym("register_ro")){
	int p = pdp_packet_convert_ro(input_packet, pdp_gensym("bitmap/yv12/*"));

	/* poll anyway. */
	//poll_events(x);

	/* check packet */
	if (-1 == p){
	    post("SDL: can't convert image to bitmap/yv12/*");
	    return;
	}
	else {
	    t_bitmap *bitmap = pdp_packet_subheader(p);
	    unsigned char *data = pdp_packet_data(p);
	    int planesize = bitmap->width * bitmap->height;
	    check_overlay(x, bitmap->width, bitmap->height);
	    if (x->x_overlay){
		SDL_Rect rect = {0, 0, x->x_surface_width, x->x_surface_height};

		/* copy */
		SDL_LockYUVOverlay(x->x_overlay);
		memcpy(x->x_overlay->pixels[0], data, planesize); data += planesize;
		memcpy(x->x_overlay->pixels[1], data, planesize >> 2); data += (planesize >> 2);
		memcpy(x->x_overlay->pixels[2], data, planesize >> 2);
		SDL_UnlockYUVOverlay(x->x_overlay);

		/* display */
		if (SDL_DisplayYUVOverlay(x->x_overlay, &rect)){
		    pdp_post("SDL: can't display overlay");
		    return;
		}
	    }
		
	    else {
		pdp_post("SDL: error creating overlay");
	    }
		
	    pdp_packet_mark_unused(p);
	    return;
	}
    }
}





static void pdp_sdl_free(t_pdp_sdl *x)
{
    destroy_overlay(x);
    sdl_singleton = 0;
    SDL_Quit();
}


t_class *pdp_sdl_class;

void *pdp_sdl_new(t_floatarg width, t_floatarg height) {

    t_pdp_sdl *x;
    int w = (int)width;
    int h = (int)height;

    if (sdl_singleton) {
	post("Only one sdl object allowed.");
	return 0;
    }

    if( SDL_Init(SDL_INIT_VIDEO) < 0 ) {
	pdp_post("Could not initialize SDL: %s", SDL_GetError());
	return 0;
    }
    atexit(SDL_Quit);


    x = (t_pdp_sdl *)pd_new(pdp_sdl_class);
    sdl_singleton = x;

    
    x->x_surface = NULL;
    x->x_overlay = NULL;

    x->x_outlet = outlet_new(&x->x_obj, &s_anything);


    /* try to create a surface */
    create_surface(x, w ? w : WINWIDTH, h ? h : WINHEIGHT, 0);
    if (!x->x_surface){
	pdp_post("Can't create surface");
	goto error_cleanup;
    }

    /* try to create overlay */
    check_overlay(x, 320, 240);
    if (!x->x_overlay){
	pdp_post("Can't create overlay");
	goto error_cleanup;
    }

    return (void *)x;

  error_cleanup:
    pdp_sdl_free(x);
    return (void *)0;
}



#ifdef __cplusplus
extern "C"
{
#endif


void pdp_sdl_setup(void)
{

    sdl_singleton = 0;

    pdp_sdl_class = class_new(gensym("pdp_sdl"), (t_newmethod)pdp_sdl_new,
    	(t_method)pdp_sdl_free, sizeof(t_pdp_sdl), 0, A_NULL);


    class_addmethod(pdp_sdl_class, (t_method)resize, gensym("size"), A_FLOAT, A_FLOAT, A_NULL);
    class_addmethod(pdp_sdl_class, (t_method)poll_events, gensym("poll"), A_NULL);
    class_addmethod(pdp_sdl_class, (t_method)fullscreen, gensym("fullscreen"), A_FLOAT, A_NULL);
    class_addmethod(pdp_sdl_class, (t_method)input_0, gensym("pdp"),  A_SYMBOL, A_DEFFLOAT, A_NULL);

}

#ifdef __cplusplus
}
#endif


--- NEW FILE: pdp_qt.c ---
/*
 *   Pure Data Packet module.
 *   Copyright (c) by Tom Schouten <pdp at zzz.kotnet.org>
 *
 *   This program 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.
 *
 *   This program 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.
 *
 *   You should have received a copy of the GNU General Public License
 *   along with this program; if not, write to the Free Software
 *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
 *
 */


#include <quicktime/lqt.h>
#include <quicktime/colormodels.h>

#include "pdp.h"
#include "pdp_llconv.h"


#define min(x,y) ((x<y)?(x):(y))



#define FREE(x) {if (x) {pdp_dealloc(x); x=0;} else post("free null pointer");}


/* debug macro */
//#define DEBUG_MSG_ENABLED

#ifdef DEBUG_MSG_ENABLED

#define DEBUG_MSG(EXP)\
fprintf (stderr, "mark start: [" #EXP "], on line %d\n", __LINE__);\
 EXP \
fprintf (stderr, "mark end:   [" #EXP "], on line %d\n", __LINE__);

#else
#define DEBUG_MSG(EXP) EXP 
#endif

typedef struct pdp_qt_struct
{
    t_object x_obj;
    t_float x_f;

    float x_gain;


    t_symbol *x_name;   // this is our name
    int x_istilde;      // 0==pdp_qt / 1==pdp_qt~
    int x_syncaudio;


    /* clock object */
    t_clock *x_clock;
    int x_counter;
    int x_queue_id;

    /* audio outlets */
    t_outlet *x_outleft;
    t_outlet *x_outright;

    /* message outlets */
    t_outlet *x_outlet0;
    t_outlet *x_outlet1;
    t_outlet *x_outlet2;

    /* pdp data */
    int x_packet0;

    /* toggles */
    int x_loop;
    int x_autoplay;

    /* qt data */
    unsigned char ** x_qt_rows; // pointer array to rows / colour planes
    float ** x_qt_audiochans;   // pointer array to audio channel buffers
    unsigned char * x_qt_frame;
    quicktime_t *x_qt;
    int x_qt_cmodel;

    //t_pdp_qt_data *x_state_data;

    /* audio data */
    int x_chunk_current;
    float *x_chunk_buf;
    float *x_chunk[2][2];
    int x_chunk_used[2]; // marks if chunk is used or not
    int x_chunk_size;
    int x_chunk_pos;

    /* global state */
    int x_initialized;
    int x_frame;
    int x_frame_thread;
    int x_process_in_thread;


    /* audio info */
    int x_audio_tracks;   // ==0 means audio not available
    int x_audio_channels;
    long x_audio_samplerate;
    long x_audio_length;

    /* video info */
    int x_video_tracks;   // ==0 means video not available
    float x_video_framerate;
    long x_video_length;
    unsigned int x_video_width;
    unsigned int x_video_height;


} t_pdp_qt;


static void pdp_qt_bang(t_pdp_qt *x);

static void pdp_qt_close(t_pdp_qt *x)
{
    t_pdp_procqueue *q = pdp_queue_get_queue();

    /* disable clock */
    clock_unset(x->x_clock);
    pdp_procqueue_finish(q, x->x_queue_id);

    if (x->x_initialized){
	/* close file */
	quicktime_close(x->x_qt);
	x->x_initialized = 0;


	/* free video data */
	if (x->x_video_tracks){
	    FREE(x->x_qt_frame);
            FREE(x->x_qt_rows);
	    x->x_video_tracks = 0;
	    //x->x_qt_rows = 0;
	    //x->x_qt_frame = 0;
	}

	/* free audio data */
	if (x->x_audio_tracks){
	    x->x_chunk_used[0] = 0;
	    x->x_chunk_used[1] = 0;
	    FREE(x->x_chunk_buf);
            FREE(x->x_qt_audiochans);
	    x->x_audio_tracks = 0;
	    //x->x_qt_audiochans = 0;
	    //x->x_chunk_buf = 0;
	    x->x_chunk[0][0] = 0;
	    x->x_chunk[0][1] = 0;
	    x->x_chunk[1][0] = 0;
	    x->x_chunk[1][1] = 0;
	}


    }


}

void  pdp_qt_create_pdp_packet(t_pdp_qt *x)
{
    t_pdp *header;
    t_image *image;


    /* round to next legal size */
    /* if size is illegal, image distortion will occur */
    u32 w  = pdp_imageproc_legalwidth(x->x_video_width);
    u32 h  = pdp_imageproc_legalheight(x->x_video_height);

    
    int nbpixels = w * h;
    int packet_size = (nbpixels + (nbpixels >> 1)) << 1;


    pdp_packet_mark_unused(x->x_packet0);
    x->x_packet0 = pdp_packet_new_image_YCrCb(w, h);
    header = pdp_packet_header(x->x_packet0);
    image = pdp_packet_image_info(x->x_packet0);

    if (!header){
	post("%s: ERROR: can't create new packet", x->x_name->s_name);
	return;
    }
	

    //header->info.image.encoding = (x->x_qt_cmodel == BC_RGB888) ? PDP_IMAGE_GREY : PDP_IMAGE_YV12;
    //image->encoding = PDP_IMAGE_YV12;
    //image->width = w;
    //image->height =  h;
}




static void pdp_qt_open(t_pdp_qt *x, t_symbol *name)
{
    unsigned int size;
    unsigned int i;
    unsigned int chunk_bytesize;

    post("%s: opening %s", x->x_name->s_name, name->s_name);


    /* close previous one */
    pdp_qt_close(x);


    /* check if qt file */
    if(0 == quicktime_check_sig(name->s_name)){
	post("%s: ERROR: not a quicktime file", x->x_name->s_name);
	goto exit;
    }

    /* open */
    DEBUG_MSG(x->x_qt = quicktime_open(name->s_name, 1, 0);)
    if (!(x->x_qt)){
	post("%s: ERROR: can't open file", x->x_name->s_name);
	goto exit;
    }

    /* check video */
    x->x_video_tracks = 0;
    if (quicktime_has_video(x->x_qt)) {
	x->x_video_framerate = quicktime_frame_rate   (x->x_qt, 0);
	x->x_video_length    = quicktime_video_length (x->x_qt, 0);
	x->x_video_width     = quicktime_video_width  (x->x_qt, 0);
	x->x_video_height    = quicktime_video_height (x->x_qt, 0);
	post("%s: video stream found (%dx%d pixels, %0.00f fps, %d frames, %s codec)", 
	     x->x_name->s_name, x->x_video_width, x->x_video_height, x->x_video_framerate, 
	     x->x_video_length, quicktime_video_compressor(x->x_qt, 0));
	x->x_video_tracks = quicktime_video_tracks(x->x_qt);

    }
	

    /* check audior */
    x->x_audio_tracks = 0;
    if (quicktime_has_audio(x->x_qt)) {
	x->x_audio_tracks     = quicktime_audio_tracks   (x->x_qt);
	//x->x_audio_channels   = quicktime_track_channels (x->x_qt, 0);
	x->x_audio_channels   = lqt_total_channels       (x->x_qt);
	x->x_audio_samplerate = quicktime_sample_rate    (x->x_qt, 0);
	x->x_audio_length     = quicktime_audio_length   (x->x_qt, 0);
	x->x_chunk_size = (int)((float)x->x_audio_samplerate / x->x_video_framerate);
	post("%s: audio stream found (%d channels, %d Hz, %d samples, chunksize %d)", 
	     x->x_name->s_name, x->x_audio_channels, x->x_audio_samplerate, x->x_audio_length, x->x_chunk_size);
    }

    /* check if video codec is supported */
    if (x->x_video_tracks){
	if (!quicktime_supported_video(x->x_qt,0)) {
	    post("%s: WARNING: unsupported video codec",x->x_name->s_name);
	    x->x_video_tracks = 0;
	}
    }

    /* check if audio codec is supported */
    if (x->x_audio_tracks){
	if (!quicktime_supported_audio(x->x_qt,0)) {
	    post("%s: WARNING: unsupported audio codec", x->x_name->s_name);
	    x->x_audio_tracks = 0;
	}
    }



    /* check which colormodel to use */
    if (x->x_video_tracks){

	if (quicktime_reads_cmodel(x->x_qt,BC_YUV420P,0)){
	    post("%s: using colormodel YUV420P", x->x_name->s_name);
	    x->x_qt_cmodel = BC_YUV420P;
	}
	else if (quicktime_reads_cmodel(x->x_qt,BC_YUV422,0)){
	    post("%s: using colormodel YUV422", x->x_name->s_name);
	    x->x_qt_cmodel = BC_YUV422;
	}
	else if (quicktime_reads_cmodel(x->x_qt,BC_RGB888,0)){
	    post("%s: using colormodel RGB888", x->x_name->s_name);
	    x->x_qt_cmodel = BC_RGB888;
	}
	else {
	    post("%s: WARNING: can't find a usable colour model", x->x_name->s_name);
	    x->x_video_tracks = 0;
	}

    }



    /* no video == errors */
    if (!x->x_video_tracks) {
        post("%s: ERROR: no usable video stream found.", x->x_name->s_name);
	goto exit_close;
    }


    /* initialize video data structures */
    if (x->x_video_tracks){

	/* allocate enough space for all supported colormodels (24bpp)*/
	x->x_frame = 0;
	x->x_qt_frame = (unsigned char*)pdp_alloc(x->x_video_width * x->x_video_height * 3);
	x->x_qt_rows =  (unsigned char **)pdp_alloc(sizeof(unsigned char *) * x->x_video_height);
	size = x->x_video_width * x->x_video_height;

	switch(x->x_qt_cmodel){
	case BC_YUV420P:
	    /* planar with u&v 2x2 subsampled */
	    x->x_qt_rows[0] = &x->x_qt_frame[0];
	    x->x_qt_rows[2] = &x->x_qt_frame[size];
	    x->x_qt_rows[1] = &x->x_qt_frame[size + (size>>2)];
	    break;

	case BC_YUV422:
	    /* packed with u&v 2x subsampled (lines) */
	    /* later on we will convert this to planar */
	    for(i=0; i< x->x_video_height; i++) x->x_qt_rows[i] = &x->x_qt_frame[i * x->x_video_width * 2];
	    break;

	case BC_RGB888:
	    /* packed rgb */
	    /* later on we will convert this to planar */
	    for(i=0; i< x->x_video_height; i++) x->x_qt_rows[i] = &x->x_qt_frame[i * x->x_video_width * 3];
	    break;

	default:
	    post("%s: error on init: unkown colour model",x->x_name->s_name);
	    break;
	}
    
	DEBUG_MSG(quicktime_set_cmodel(x->x_qt, x->x_qt_cmodel);)
	outlet_float(x->x_outlet2, (float)quicktime_video_length(x->x_qt,0));
    
    }

    /* initialize audio data structures */
    if (x->x_audio_tracks){
	x->x_chunk_pos = 0;
	x->x_chunk_current = 0;

	chunk_bytesize = sizeof(float)*x->x_chunk_size;
	x->x_chunk_buf =  (float *)pdp_alloc(chunk_bytesize * 4);
	memset(x->x_chunk_buf, 0, chunk_bytesize * 4);
	x->x_chunk[0][0] = x->x_chunk_buf;
	x->x_chunk[0][1] = x->x_chunk_buf + x->x_chunk_size ;
	x->x_chunk[1][0] = x->x_chunk_buf + x->x_chunk_size * 2;
	x->x_chunk[1][1] = x->x_chunk_buf + x->x_chunk_size * 3;
	x->x_chunk_used[0] = 0;
	x->x_chunk_used[1] = 0;
	x->x_syncaudio = x->x_istilde; //sync on audio if this is a tilde object

	DEBUG_MSG(if (x->x_audio_channels == 0) exit(1);)
	x->x_qt_audiochans = (float **)pdp_alloc(x->x_audio_channels * sizeof(float **));
	memset(x->x_qt_audiochans, 0, x->x_audio_channels * sizeof(float **));
    }
    else {
	x->x_syncaudio = 0;
    }


    /* everything went well */
    x->x_initialized = 1;

    /* start playback if outplay is on */
    if(x->x_autoplay)  clock_delay(x->x_clock, 1000.0L / (double)x->x_video_framerate);

    /* brag about success */
    post("%s: %s opened", x->x_name->s_name, name->s_name);

    return;

    /* error exits */

 exit_close:
    DEBUG_MSG(quicktime_close(x->x_qt);)

 exit:
    x->x_initialized = 0;
    x->x_audio_tracks = 0;
    x->x_video_tracks = 0;
    return;
    
}


//static void pdp_qt_setposition(t_pdp_qt *x, int pos)
//{
//    x->x_frame = pos;
//    DEBUG_MSG(if(x->x_video_tracks) quicktime_set_video_position(x->x_qt, pos, 0);)
//    DEBUG_MSG(if(x->x_audio_tracks) quicktime_set_audio_position(x->x_qt, pos * x->x_chunk_size, 0);)
//    
//}


static void pdp_qt_bangaudio(t_pdp_qt *x)
{
    int lefterr=0;
    int righterr=0;
    int err=0;
    int sample = 0;
    int remaining = 0;
    int readamount = 0;



    if (!x->x_initialized){
	//post("pdp_qt: no qt file opened");
	return;
    }


    if (!x->x_audio_tracks){
	//post("pdp_qt: no audio stream present");
	return;
    }



    //DEBUG_MSG(sample = quicktime_audio_position(x->x_qt,0);)


    // if the active chunk is unused, clear it and mark it used
    if (!x->x_chunk_used[x->x_chunk_current]){
	//post("%s: clearing unused active chunk",x->x_name->s_name);



	//probably this is the !@#%&*(*)&!$() bug
	//memset(x->x_chunk[0][x->x_chunk_current], 0, sizeof(float)*2*x->x_chunk_size);
	//memset(x->x_chunk[1][x->x_chunk_current], 0, sizeof(float)*2*x->x_chunk_size);

	memset(x->x_chunk[0][x->x_chunk_current], 0, sizeof(float) * x->x_chunk_size);
	memset(x->x_chunk[1][x->x_chunk_current], 0, sizeof(float) * x->x_chunk_size);




	x->x_chunk_used[x->x_chunk_current] = 1;
    }

    // compute the remaining time
    DEBUG_MSG(remaining = (int ) ( quicktime_audio_length(x->x_qt, 0) - quicktime_audio_position(x->x_qt, 0) );)
    readamount = min(remaining, x->x_chunk_size);
    if (!readamount) return;


    // if the inactive chunk is unused, fill it with the current frame's audio data and mark it used
    if (!x->x_chunk_used[!x->x_chunk_current]){
	switch(x->x_audio_channels){
	case 1:
	    x->x_qt_audiochans[0] = x->x_chunk[0][!x->x_chunk_current];
	    x->x_qt_audiochans[1] = 0;
	    DEBUG_MSG(err = lqt_decode_audio(x->x_qt, NULL, x->x_qt_audiochans, readamount);)
            break;
	default:
	    x->x_qt_audiochans[0] = x->x_chunk[0][!x->x_chunk_current];
	    x->x_qt_audiochans[1] = x->x_chunk[1][!x->x_chunk_current];
	    DEBUG_MSG(err = lqt_decode_audio(x->x_qt, NULL, x->x_qt_audiochans, readamount);)
            break;
	}
	x->x_chunk_used[!x->x_chunk_current] = 1;
    }
    // if it is used, something went wrong with sync
    else{
	//post("%s: dropping audio chunk %d.",x->x_name->s_name, x->x_frame_thread);
    }


    if (err) post("%s: error decoding audio",x->x_name->s_name, x->x_frame_thread);

    // ensure audio pointer points to next frame's data
    //DEBUG_MSG(quicktime_set_audio_position(x->x_qt, sample + readamount, 0);)
    
}




static void pdp_qt_bangvideo(t_pdp_qt *x)
{
  unsigned int w, h, nbpixels, packet_size, i,j;
  unsigned int *source, *dest;
  unsigned int uoffset, voffset;
  short int* data;
  t_pdp* header;

  // check if we want greyscale output or not
  //int grey = (x->x_qt_cmodel == BC_RGB888);

  static short int gain[4] = {0x7fff, 0x7fff, 0x7fff, 0x7fff};

    if ((!x->x_initialized) || (!x->x_video_tracks)){
	//post("pdp_qt: no qt file opened");
	return;
    }

    w = x->x_video_width;
    h = x->x_video_height;
    nbpixels = x->x_video_width * x->x_video_height;

    // create a new packet
    pdp_qt_create_pdp_packet(x);

    header = pdp_packet_header(x->x_packet0);

    if (!header) {
	post("%s: ERROR: no packet available", x->x_name->s_name);
	return;
    }

    data = (short int *) pdp_packet_data(x->x_packet0);


    DEBUG_MSG(lqt_decode_video(x->x_qt, x->x_qt_rows, 0);)


    switch(x->x_qt_cmodel){
    case BC_YUV420P:
	pdp_llconv(x->x_qt_frame, RIF_YVU__P411_U8, data, RIF_YVU__P411_S16, x->x_video_width, x->x_video_height); 
	break;

    case BC_YUV422:
	pdp_llconv(x->x_qt_frame, RIF_YUYV_P____U8, data, RIF_YVU__P411_S16, x->x_video_width, x->x_video_height); 
	break;

    case BC_RGB888:
	pdp_llconv(x->x_qt_frame, RIF_RGB__P____U8, data, RIF_YVU__P411_S16, x->x_video_width, x->x_video_height); 
	break;

    default:
	post("%s: error on decode: unkown colour model",x->x_name->s_name);
	break;
    }



}

static void pdp_qt_sendpacket(t_pdp_qt *x)
{

    pdp_packet_pass_if_valid(x->x_outlet0, &x->x_packet0);

    //if (x->x_packet0 != -1){
	//pdp_packet_mark_unused(x->x_packet0);
	//outlet_pdp(x->x_outlet0, x->x_packet0);
	//x->x_packet0 = -1;
    //}
}


static void pdp_qt_thread_bang(t_pdp_qt *x)
{
    // set audio position
    if(x->x_video_tracks) quicktime_set_video_position(x->x_qt, x->x_frame_thread, 0);

    // bang video
    pdp_qt_bangvideo(x);

    // if it's a tilde object, bang audio
    if (x->x_istilde && x->x_audio_tracks){
	quicktime_set_audio_position(x->x_qt, x->x_frame_thread * x->x_chunk_size, 0);
	pdp_qt_bangaudio(x);
    }

}


static void pdp_qt_bang(t_pdp_qt *x)
{
    int length, pos;

    t_pdp_procqueue *q = pdp_queue_get_queue();

    /* return if not initialized */
    if (!x->x_initialized) return;

    //length = quicktime_video_length(x->x_qt,0);
    //pos = quicktime_video_position(x->x_qt,0);
    length = x->x_video_length;
    pos = x->x_frame;


    /* check bounds */
    if (x->x_loop){
	pos = x->x_frame % length;
	if (pos < 0) pos += length;
    }
    else{
	if (pos < 0) pos = 0;
	if (pos >= length) pos = length - 1;
    }

    /* store next frame for access in thread */
    x->x_frame_thread = pos;


    // if autoplay is on and we do not have audio synchro
    // set clock to play next frame
    if (x->x_autoplay && !x->x_syncaudio) clock_delay(x->x_clock, 1000.0L / (double)x->x_video_framerate);


    // make sure prev decode is finished don't drop frames in this one
    pdp_procqueue_finish(q, x->x_queue_id);
    x->x_queue_id = -1;

    /* only decode new stuff if previous is done */
    if (-1 == x->x_queue_id){
	// send the current frame number to outlet
	outlet_float(x->x_outlet1, (float)pos);

	//pdp_qt_setposition(x, pos);

	// start process method
	if (x->x_process_in_thread) pdp_procqueue_add(q, x, pdp_qt_thread_bang, pdp_qt_sendpacket, &x->x_queue_id);
	else {
	    pdp_qt_thread_bang(x);
	    pdp_qt_sendpacket(x);
	}
    }
    // advance frame
    x->x_frame = pos + 1;


    // send the packet
    //pdp_qt_sendpacket(x);
}



//static void pdp_qt_getaudiochunk(t_pdp_qt *x, int channel)
//{
//    if (!x->x_audio_tracks) return;
//    quicktime_decode_audio(x->x_qt, NULL, x->x_chunk[channel][x->x_chunk_current], x->x_chunk_size<<1, channel);
//			  
//}

static void pdp_qt_loop(t_pdp_qt *x, t_floatarg loop)
{
    int loopi = (int)loop;
    x->x_loop = !(loopi == 0);
}

static void pdp_qt_autoplay(t_pdp_qt *x, t_floatarg play)
{
    int playi = (int)play;
    x->x_autoplay = !(playi == 0);


    // reset clock if autoplay is off
    if (!x->x_autoplay) clock_unset(x->x_clock);

    
}



static void pdp_qt_frame_cold(t_pdp_qt *x, t_floatarg frameindex)
{
    int frame = (int)frameindex;
    //int length;


    x->x_frame = frame;

    //if (!(x->x_initialized)) return;

    //length = quicktime_video_length(x->x_qt,0);

    //frame = (frame >= length) ? length-1 : frame;
    //frame = (frame < 0) ? 0 : frame;

    //pdp_qt_setposition(x, frame);
}

static void pdp_qt_frame(t_pdp_qt *x, t_floatarg frameindex)
{
    pdp_qt_frame_cold(x, frameindex);
    pdp_qt_bang(x);
}

static void pdp_qt_stop(t_pdp_qt *x)
{
    pdp_qt_autoplay(x, 0);
}

static void pdp_qt_continue(t_pdp_qt *x)
{
    pdp_qt_autoplay(x, 1);
    pdp_qt_bang(x);
}


static void pdp_qt_play(t_pdp_qt *x){
    pdp_qt_frame_cold(x, 0);
    pdp_qt_continue(x);
}




static void pdp_qt_importaudio(t_pdp_qt *x, t_symbol *array, t_floatarg channel)
{
    t_pdp_procqueue *q = pdp_queue_get_queue();
    int c = (int)channel;
    t_garray *g;
    int vecsize;
    int sample;
    float *f;
    int i;

    /* if there's no audio, there's nothing to export */
    if (!x->x_audio_tracks) return;

    /* check audio channel */
    if ((c < 0) || (c >= x->x_audio_channels)) return;

    /* check if array exists */
    if (!(g = (t_garray *)pd_findbyclass(array, garray_class))){
	pd_error(x, "%s: no such table", array->s_name);
            return;
    }

    post("%s: importing audio channel %d into array %s", x->x_name->s_name, c, array->s_name);


    // make sure decode is finished
    pdp_procqueue_finish(q, x->x_queue_id);
    x->x_queue_id = -1;


    /* resize array */
    garray_resize(g, x->x_audio_length);

    /* for sanity's sake let's clear the save-in-patch flag here */
    garray_setsaveit(g, 0);
    garray_getfloatarray(g, &vecsize, &f);

    /* if the resize failed, garray_resize reported the error */
    if (vecsize != x->x_audio_length){
	pd_error(x, "array resize failed");
	return;
    }

    /* save pointer in file */
    DEBUG_MSG(sample = quicktime_audio_position(x->x_qt, 0);)
    DEBUG_MSG(quicktime_set_audio_position(x->x_qt, 0, 0);)

    /* transfer the audio file to the end of the array */
    DEBUG_MSG(quicktime_decode_audio(x->x_qt, NULL, f, vecsize, c);)
 
    /* restore pointer in file */
	DEBUG_MSG(quicktime_set_audio_position(x->x_qt, sample, 0);)


}



static t_int *pdp_qt_perform(t_int *w)
{
    t_pdp_qt *x    =  (t_pdp_qt *)w[1];
    t_float  *out0 =  (t_float *)w[2];
    t_float  *out1 =  (t_float *)w[3];
    t_int    n     =  (t_int)w[4];

    t_int xfer_samples;
    if (!x->x_initialized || !x->x_audio_tracks) goto zero;

    while(1){
	// check current chunk
	if (!x->x_chunk_used[x->x_chunk_current]) goto zero;


	// transfer from chunk to output
	xfer_samples = min(n, x->x_chunk_size - x->x_chunk_pos);

	//x->x_audio_channels = 1;

	if (x->x_audio_channels == 1){
	    memcpy(out0, x->x_chunk[0][x->x_chunk_current] + x->x_chunk_pos, sizeof(float)*xfer_samples);
	    memcpy(out1, x->x_chunk[0][x->x_chunk_current] + x->x_chunk_pos, sizeof(float)*xfer_samples);
	}
	else {
	    memcpy(out0, x->x_chunk[0][x->x_chunk_current] + x->x_chunk_pos, sizeof(float)*xfer_samples);
	    memcpy(out1, x->x_chunk[1][x->x_chunk_current] + x->x_chunk_pos, sizeof(float)*xfer_samples);
	}
	out0 += xfer_samples;
	out1 += xfer_samples;
	n -= xfer_samples;
	x->x_chunk_pos += xfer_samples;


	// check if chunk is finished, if so mark unused, swap buffers and set clock
	if (x->x_chunk_size ==  x->x_chunk_pos){
	    x->x_chunk_used[x->x_chunk_current] = 0;
	    x->x_chunk_pos = 0;
	    x->x_chunk_current ^= 1;
	    if (x->x_autoplay) clock_delay(x->x_clock, 0L);
	}

	// if chunk is not finished, the output buffer is full
	else{
	    goto exit;
	}
	    
    }

 
 zero:
    // fill the rest of the output with zeros
    memset(out0, 0, sizeof(float)*n); 
    memset(out1, 0, sizeof(float)*n); 

 exit:
    return(w+5);
}

static void pdp_qt_dsp(t_pdp_qt *x, t_signal **sp)
{
    dsp_add(pdp_qt_perform, 4, x, sp[0]->s_vec, sp[1]->s_vec, sp[0]->s_n);

}

static void pdp_qt_process_in_thread(t_pdp_qt *x, t_float f)
{

    int t = (f != 0.0f);

    x->x_process_in_thread = t;

    post("pdp_qt: thread processing switched %d", t ? "on" : "off");

}

static void pdp_qt_tick(t_pdp_qt *x)
{

    // bang audio/video
    pdp_qt_bang(x);
}

static void pdp_qt_free(t_pdp_qt *x)
{
    clock_unset(x->x_clock);
    pdp_qt_close(x);
    clock_free(x->x_clock);
    //free (x->x_state_data);

}



t_class *pdp_qt_class;
t_class *pdp_qt_tilde_class;


void pdp_qt_init_common(t_pdp_qt *x)
{

    inlet_new(&x->x_obj, &x->x_obj.ob_pd, gensym("float"), gensym("frame_cold"));

    /* add common outlets */
    x->x_outlet0 = outlet_new(&x->x_obj, &s_anything);
    x->x_outlet1 = outlet_new(&x->x_obj, &s_float);
    x->x_outlet2 = outlet_new(&x->x_obj, &s_float);

    /* init */
    x->x_gain = 1.0f;
    x->x_process_in_thread = 0;
    x->x_packet0 = -1;
    x->x_queue_id = -1;
    x->x_initialized = 0;
    x->x_audio_tracks = 0;
    x->x_video_tracks = 0;
    x->x_loop = 0;
    x->x_autoplay = 0;
    x->x_chunk[0][0] = 0;
    x->x_chunk[0][1] = 0;
    x->x_chunk[1][0] = 0;
    x->x_chunk[1][1] = 0;

    /* initialize clock object */
    x->x_clock = clock_new(x, (t_method)pdp_qt_tick);




}

void *pdp_qt_new(void)
{
    t_pdp_qt *x = (t_pdp_qt *)pd_new(pdp_qt_class);
    x->x_name = gensym("pdp_qt");
    x->x_istilde = 0;
    pdp_qt_init_common(x);
    return (void *)x;
}

void *pdp_qt_tilde_new(void)
{
    t_pdp_qt *x = (t_pdp_qt *)pd_new(pdp_qt_tilde_class);
    x->x_name = gensym("pdp_qt");
    x->x_istilde = 1;

    pdp_qt_init_common(x);

    /* add outlets to the right so pdp_qt~ can replace pdp_qt without breaking a patch */
    x->x_outleft  = outlet_new(&x->x_obj, &s_signal);
    x->x_outright = outlet_new(&x->x_obj, &s_signal);

    return (void *)x;
}



#ifdef __cplusplus
extern "C"
{
#endif


void pdp_qt_setup_common(t_class *class)
{
    class_addmethod(class, (t_method)pdp_qt_bang, gensym("bang"), A_NULL);
    class_addmethod(class, (t_method)pdp_qt_close, gensym("close"), A_NULL);
    class_addmethod(class, (t_method)pdp_qt_open, gensym("open"), A_SYMBOL, A_NULL);
    class_addmethod(class, (t_method)pdp_qt_autoplay, gensym("autoplay"), A_DEFFLOAT, A_NULL);
    class_addmethod(class, (t_method)pdp_qt_stop, gensym("stop"), A_NULL);
    class_addmethod(class, (t_method)pdp_qt_play, gensym("play"), A_NULL);
    class_addmethod(class, (t_method)pdp_qt_continue, gensym("cont"), A_NULL);
    class_addmethod(class, (t_method)pdp_qt_loop, gensym("loop"), A_DEFFLOAT, A_NULL);
    class_addfloat (class, (t_method)pdp_qt_frame);
    class_addmethod(class, (t_method)pdp_qt_frame_cold, gensym("frame_cold"), A_FLOAT, A_NULL);
    class_addmethod(class, (t_method)pdp_qt_process_in_thread, gensym("thread"), A_FLOAT, A_NULL);
    class_addmethod(class, (t_method)pdp_qt_importaudio, gensym("importaudio"), A_SYMBOL, A_DEFFLOAT, A_NULL);
    class_addmethod(class, (t_method)pdp_qt_importaudio, gensym("dump"), A_SYMBOL, A_DEFFLOAT, A_NULL);
}

void pdp_qt_setup(void)
{

    /* plain class */
    pdp_qt_class = class_new(gensym("pdp_qt"), (t_newmethod)pdp_qt_new,
    	(t_method)pdp_qt_free, sizeof(t_pdp_qt), 0, A_NULL);
    pdp_qt_setup_common(pdp_qt_class);
 

    /* tilde class */
    pdp_qt_tilde_class = class_new(gensym("pdp_qt~"), (t_newmethod)pdp_qt_tilde_new,
    	(t_method)pdp_qt_free, sizeof(t_pdp_qt), 0, A_NULL);
    pdp_qt_setup_common(pdp_qt_tilde_class);

    class_addmethod(pdp_qt_tilde_class, (t_method)pdp_qt_dsp, gensym("dsp"), 0);
}

#ifdef __cplusplus
}
#endif







More information about the Pd-cvs mailing list