aboutsummaryrefslogtreecommitdiff
path: root/modules/pdp_qt.c
diff options
context:
space:
mode:
authorTom Schouten <doelie@users.sourceforge.net>2003-01-21 10:27:33 +0000
committerTom Schouten <doelie@users.sourceforge.net>2003-01-21 10:27:33 +0000
commit9b8745d5250c9d0b60c9aa5a77f58a3fcddf1076 (patch)
tree8372b6a414a7124cec57efc9c80845e2bc1b157d /modules/pdp_qt.c
This commit was generated by cvs2svn to compensate for changes in r352,svn2git-root
which included commits to RCS files with non-trunk default branches. svn path=/trunk/externals/pdp/; revision=353
Diffstat (limited to 'modules/pdp_qt.c')
-rw-r--r--modules/pdp_qt.c941
1 files changed, 941 insertions, 0 deletions
diff --git a/modules/pdp_qt.c b/modules/pdp_qt.c
new file mode 100644
index 0000000..1c778d5
--- /dev/null
+++ b/modules/pdp_qt.c
@@ -0,0 +1,941 @@
+/*
+ * Pure Data Packet module.
+ * Copyright (c) by Tom Schouten <pdp@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.h"
+#include "pdp_llconv.h"
+#include <quicktime/lqt.h>
+#include <quicktime/colormodels.h>
+
+
+#define min(x,y) ((x<y)?(x):(y))
+
+
+
+#define FREE(x) {if (x) {free(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)
+{
+ /* disable clock */
+ clock_unset(x->x_clock);
+ pdp_queue_finish(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;
+ int nbpixels = x->x_video_width * x->x_video_height;
+ //int packet_size = (x->x_qt_cmodel == BC_RGB888) ? (nbpixels << 1) : (nbpixels + (nbpixels >> 1)) << 1;
+ int packet_size = (nbpixels + (nbpixels >> 1)) << 1;
+
+
+ pdp_packet_mark_unused(x->x_packet0);
+ x->x_packet0 = pdp_packet_new(PDP_IMAGE, packet_size);
+ header = pdp_packet_header(x->x_packet0);
+
+ //header->info.image.encoding = (x->x_qt_cmodel == BC_RGB888) ? PDP_IMAGE_GREY : PDP_IMAGE_YV12;
+ header->info.image.encoding = PDP_IMAGE_YV12;
+ header->info.image.width = x->x_video_width;
+ header->info.image.height = x->x_video_height;
+}
+
+
+
+
+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*)malloc(x->x_video_width * x->x_video_height * 3);
+ x->x_qt_rows = (unsigned char **)malloc(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);)
+ pdp_qt_create_pdp_packet(x);
+ 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 *)malloc(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 **)malloc(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;
+
+ header = pdp_packet_header(x->x_packet0);
+ 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)
+{
+ 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;
+
+ /* 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_queue_finish(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_queue_add(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)
+{
+ 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_queue_finish(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