/* * Pure Data Packet module. * Copyright (c) by Tom Schouten * * 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 //#include #include #include #include "pdp.h" #include "pdp_llconv.h" #if PD_MAJOR_VERSION==0 && PD_MINOR_VERSION>=43 #include "s_stuff.h" // need to get sys_libdir for libquicktime plugins #endif #define min(x,y) ((xx_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)){ pd_error(x,"%s: %s not a quicktime file", x->x_name->s_name, name->s_name); goto exit; } /* open */ DEBUG_MSG(x->x_qt = quicktime_open(name->s_name, 1, 0);) if (!(x->x_qt)){ pd_error(x,"%s: can't open %s", x->x_name->s_name, 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 in %s",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)) { pd_error(x,"%s: unsupported audio codec in %s", x->x_name->s_name, 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 for %", x->x_name->s_name); x->x_video_tracks = 0; } } /* no video == errors */ if (!x->x_video_tracks) { pd_error(x,"%s: no usable video stream found in %s", x->x_name->s_name, 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 __APPLE__ /* this is necessary for pdp_qt to find the embedded libquicktime plugins */ char buf[FILENAME_MAX]; char realpath_buf[FILENAME_MAX]; strncpy(buf, sys_libdir->s_name, FILENAME_MAX - 20); strcat(buf, "/../lib/libquicktime1"); if(realpath(buf, realpath_buf)) { if(sys_verbose) post("[pdp_qt]: setting LIBQUICKTIME_PLUGIN_DIR to:\n %s", realpath_buf); setenv("LIBQUICKTIME_PLUGIN_DIR", realpath_buf, 0); // 0 means don't overwrite existing value } #endif } #ifdef __cplusplus } #endif