/* ----------------------------------------------------------- bus_config --- */ double RTcmix::bus_config(float p[], int n_args, double pp[]) { ErrCode err; int i, j, k, startchan, endchan; char *str, *instname, *busname; BusType type; BusSlot *bus_slot; char inbusses[80], outbusses[80]; // for verbose message if (n_args < 2) die("bus_config", "Wrong number of args."); bus_slot = new BusSlot; if (bus_slot == NULL) return -1.0; inbusses[0] = outbusses[0] = '\0'; Lock localLock(&bus_slot_lock); // This will unlock when going out of scope. /* do the old Minc casting rigamarole to get string pointers from a double */ str = DOUBLE_TO_STRING(pp[0]); instname = strdup(str); // Note: If we exit nonfatally, we have to free. for (i = 1; i < n_args; i++) { busname = DOUBLE_TO_STRING(pp[i]); err = parse_bus_name(busname, &type, &startchan, &endchan); if (err) goto error; switch (type) { case BUS_IN: if (bus_slot->in_count > 0) strcat(inbusses, ", "); strcat(inbusses, busname); if (bus_slot->auxin_count > 0) { die("bus_config", "Can't have 'in' and 'aux-in' buses in same bus_config."); } j = bus_slot->in_count; for (k = startchan; k <= endchan; k++) bus_slot->in[j++] = k; bus_slot->in_count += (endchan - startchan) + 1; /* Make sure max channel count set in rtsetparams can accommodate the highest input chan number in this bus config. */ if (endchan >= NCHANS) { die("bus_config", "You specified %d channels in rtsetparams,\n" "but this bus_config requires %d channels.", NCHANS, endchan + 1); } break; case BUS_OUT: if (bus_slot->out_count > 0) strcat(outbusses, ", "); strcat(outbusses, busname); if (bus_slot->auxout_count > 0) { die("bus_config", "Can't have 'out' and 'aux-out' buses in same bus_config."); } j = bus_slot->out_count; for (k = startchan; k <= endchan; k++) { bus_slot->out[j++] = k; pthread_mutex_lock(&out_in_use_lock); OutInUse[k] = YES; // DJT added pthread_mutex_unlock(&out_in_use_lock); } bus_slot->out_count += (endchan - startchan) + 1; /* Make sure max output chans set in rtsetparams can accommodate the highest output chan number in this bus config. */ if (endchan >= NCHANS) { die("bus_config", "You specified %d output channels in rtsetparams,\n" "but this bus_config requires %d channels.", NCHANS, endchan + 1); } break; case BUS_AUX_IN: if (bus_slot->auxin_count > 0) strcat(inbusses, ", "); strcat(inbusses, busname); if (bus_slot->in_count > 0) { die("bus_config", "Can't have 'in' and 'aux-in' buses in same bus_config."); } j = bus_slot->auxin_count; for (k = startchan; k <= endchan; k++) bus_slot->auxin[j++] = k; bus_slot->auxin_count += (endchan - startchan) + 1; break; case BUS_AUX_OUT: if (bus_slot->auxout_count > 0) strcat(outbusses, ", "); strcat(outbusses, busname); if (bus_slot->out_count > 0) { die("bus_config", "Can't have 'out' and 'aux-out' buses in same bus_config."); } j = bus_slot->auxout_count; for (k = startchan; k <= endchan; k++) { bus_slot->auxout[j++] = k; pthread_mutex_lock(&aux_out_in_use_lock); AuxOutInUse[k] = YES; pthread_mutex_unlock(&aux_out_in_use_lock); } bus_slot->auxout_count += (endchan - startchan) + 1; break; default: break; } } err = check_bus_inst_config(bus_slot, YES); if (!err) { err = insert_bus_slot(instname, bus_slot); } if (err) // BGG mm -- print an error, don't exit die("bus_config", "couldn't configure the busses"); // exit(1); /* This is probably what user wants? */ /* Make sure specified aux buses have buffers allocated. */ for (i = 0; i < bus_slot->auxin_count; i++) allocate_aux_buffer(bus_slot->auxin[i], RTBUFSAMPS); for (i = 0; i < bus_slot->auxout_count; i++) allocate_aux_buffer(bus_slot->auxout[i], RTBUFSAMPS); #ifdef PRINTALL print_children(); print_parents(); #endif create_play_order(); #ifdef PRINTPLAY print_play_order(); #endif #ifdef DEBUG err = print_inst_bus_config(); #endif rtcmix_advise("bus_config", "(%s) => %s => (%s)", inbusses, instname, outbusses); free(instname); return 0.0; error: free(instname); die("bus_config", "Cannot parse arguments."); return -1.0; }
double RTcmix::rtinput(float p[], int n_args, double pp[]) { int audio_in, p1_is_audioport, start_pfield, fd; int is_open, header_type, data_format, data_location, nchans; #ifdef INPUT_BUS_SUPPORT int startchan, endchan; short busindex, buslist[MAXBUS]; BusType type; #endif /* INPUT_BUS_SUPPORT */ double srate, dur; char *sfname, *str; AudioPortType port_type = MIC; header_type = MUS_UNSUPPORTED; data_format = MUS_UNSUPPORTED; data_location = 0; dur = 0.0; audio_in = 0; p1_is_audioport = 0; is_open = 0; sfname = DOUBLE_TO_STRING(pp[0]); /* Catch stoopid NULL filenames */ if (sfname == NULL) { rterror("rtinput", "NULL filename!"); return -1; } if (strcmp(sfname, "AUDIO") == 0) { audio_in = 1; if (n_args > 1 && pp[1] != 0.0) { p1_is_audioport = 1; str = DOUBLE_TO_STRING(pp[1]); if (strcmp(str, "MIC") == 0) port_type = MIC; else if (strcmp(str, "LINE") == 0) port_type = LINE; else if (strcmp(str, "DIGITAL") == 0) port_type = DIGITAL; else p1_is_audioport = 0; /* p[1] might be a bus spec. */ } /* This signals inTraverse() to grab buffers from the audio device. */ rtrecord = 1; // FIXME: need to replace this with the bus spec scheme below... -JGG audioNCHANS = (n_args > 2) ? (int) p[2] : NCHANS; nchans = audioNCHANS; srate = SR; } #ifdef MAXMSP // this segment is to allow rtcmix to access sample data from the // max/msp [buffer~] object. if (strcmp(sfname, "MMBUF") == 0) { int i; str = DOUBLE_TO_STRING(pp[1]); for (i = 0; i < n_mm_bufs; i++) { if (strcmp(str, mm_bufs[i].name) == 0) { mm_buf_input = i; // used in rtsetinput() for buf ID break; } } if (i == n_mm_bufs) { die("rtinput", "no max/msp buffer named %s is set", str); mm_buf_input = -1; // we are NOT using [buffer~] input, even if set return -1; } audio_in = 1; // I think I need this to initialize some vars... // FIXME: need to replace this with the bus spec scheme below... -JGG audioNCHANS = NCHANS; nchans = audioNCHANS; srate = SR; } else { mm_buf_input = -1; // we are NOT using [buffer~] input, even if set } #endif // MAXMSP #ifdef INPUT_BUS_SUPPORT /* Parse bus specification. */ busindex = 0; for (i = 0; i < MAXBUS; i++) buslist[i] = -1; type = BUS_IN; startchan = endchan = -1; start_pfield = p1_is_audioport ? 2 : 1; for (i = start_pfield; i < n_args; i++) { ErrCode err; str = DOUBLE_TO_STRING(pp[i]); if (str == NULL) { rterror("rtinput", "NULL bus name!"); rtrecord = 0; return -1; } err = parse_bus_name(str, &type, &startchan, &endchan); if (err) { rterror("rtinput", "Invalid bus name specification."); rtrecord = 0; return -1; } if (type != BUS_IN) { rterror("rtinput", "You have to use an \"in\" bus with rtinput."); rtrecord = 0; return -1; } for (j = startchan; j <= endchan; j++) buslist[busindex++] = j; } if (startchan == -1) { /* no bus specified */ } #endif /* INPUT_BUS_SUPPORT */ long i; /* See if this audio device or file has already been opened. */ for (i = 0; i < max_input_fds; i++) { if (inputFileTable[i].isOpen()) { if (inputFileTable[i].hasFile(sfname)) { last_input_index = i; is_open = 1; break; } } } if (!is_open) { /* if not, open input audio device or file. */ long nsamps = 0; if (audio_in) { if (rtsetparams_was_called()) { // If audio *playback* was disabled, but there is a request for // input audio, create the audio input device here. if (!audioDevice && !Option::play()) { int nframes = RTBUFSAMPS; if ((audioDevice = create_audio_devices(true, false, NCHANS, SR, &nframes, Option::bufferCount())) == NULL) { rtrecord = 0; /* because we failed */ return -1; } Option::record(true); RTBUFSAMPS = nframes; rtcmix_advise("Input audio set", "%g sampling rate, %d channels\n", SR, NCHANS); } // If record disabled during rtsetparams(), we cannot force it on here. else if (!Option::record()) { die("rtinput", "Audio already configured for playback only via rtsetparams()"); rtrecord = 0; /* because we failed */ return -1; } } else { // This allows rtinput("AUDIO") to turn on record Option::record(1); rtrecord = 1; } fd = 1; /* we don't use this; set to 1 so rtsetinput() will work */ for (i = 0; i < nchans; i++) { allocate_audioin_buffer(i, RTBUFSAMPS); } #ifdef INPUT_BUS_SUPPORT #endif /* INPUT_BUS_SUPPORT */ } else { rtrecord = 0; fd = ::open_sound_file("rtinput", sfname, &header_type, &data_format, &data_location, &srate, &nchans, &nsamps); if (fd == -1) return -1; #ifdef INPUT_BUS_SUPPORT if (startchan == -1) { /* no bus specified above */ startchan = 0; endchan = nchans - 1; } else { if (endchan - startchan >= nchans) { rtcmix_warn("rtinput", "You specifed more input buses than " "input file '%s' has channels.", sfname); rtcmix_warn("rtinput", "Using in buses %d", foo); endchan = (startchan + nchans) - 1; } } #endif /* INPUT_BUS_SUPPORT */ dur = (double) (nsamps / nchans) / srate; rtcmix_advise(NULL, "Input file set for reading:\n"); rtcmix_advise(NULL, " name: %s\n", sfname); rtcmix_advise(NULL, " type: %s\n", mus_header_type_name(header_type)); rtcmix_advise(NULL, " format: %s\n", mus_data_format_name(data_format)); rtcmix_advise(NULL, " srate: %g\n", srate); rtcmix_advise(NULL, " chans: %d\n", nchans); rtcmix_advise(NULL, " duration: %g\n", dur); #ifdef INPUT_BUS_SUPPORT #endif /* INPUT_BUS_SUPPORT */ if (srate != SR) { rtcmix_warn("rtinput", "The input file sampling rate is %g, but " "the output rate is currently %g.", srate, SR); } } /* Put new file descriptor into the first available slot in the inputFileTable. Also copy the filename for later checking, and fill in the other fields in the InputFile struct (see InputFile.h). last_input_index is the value that will be used by any instrument created after this call to rtinput(). */ for (i = 0; i < max_input_fds; i++) { if (!inputFileTable[i].isOpen()) { inputFileTable[i].init(fd, sfname, audio_in, header_type, data_format, data_location, nsamps, (float)srate, nchans, dur); last_input_index = i; break; } } /* If this is true, we've used up all input descriptors in our array. */ if (i == max_input_fds) die("rtinput", "You have exceeded the maximum number of input " "files (%ld)!", max_input_fds); } /* Return this to Minc, so user can pass it to functions. */ return (double) last_input_index; }