/* Minc function that sets output sampling rate (p0), maximum number of output channels (p1), and (optionally) I/O buffer size (p2). On SGI, the optional p3 lets you specify the output port as a string (e.g., "LINE", "DIGITAL", etc.). Based on this information, rtsetparams allocates a mono output buffer for each of the output channels, and opens output devices. */ double RTcmix::rtsetparams(float p[], int n_args, double pp[]) { int i, status; int verbose = Option::print(); int play_audio = Option::play(); int record_audio = Option::record(); #ifdef SGI static char *out_port_str = NULL; #endif /* SGI */ if (rtsetparams_was_called()) { die("rtsetparams", "You can only call rtsetparams once!"); return -1; } // FIXME: Need better names for NCHANS and RTBUFSAMPS. -JGG SR = p[0]; NCHANS = (int) p[1]; RTBUFSAMPS = n_args > 2 ? (int) p[2] : (int) Option::bufferFrames(); int numBuffers = Option::bufferCount(); if (n_args > 3 && pp[3] != 0.0) { #ifdef SGI /* Cast Minc double to char ptr to get string. */ int iarg = (int) pp[3]; out_port_str = (char *) iarg; advise("rtsetparams", "Playing through output port '%s'", out_port_str); #endif /* SGI */ } if (SR <= 0.0) { die("rtsetparams", "Sampling rate must be greater than 0."); return -1; } if (NCHANS > MAXBUS) { die("rtsetparams", "You can only have up to %d output channels.", MAXBUS - 1); return -1; } /* play_audio is true unless user has called set_option("audio_off") before rtsetparams. This would let user run multiple jobs, as long as only one needs the audio drivers. -JGG record_audio is false unless user has called set_option("full_duplex_on") or has explicity turned record on by itself via set_option("record_on"), or has already called rtinput("AUDIO"). -DS */ if (play_audio || record_audio) { int nframes = RTBUFSAMPS; if ((audioDevice = create_audio_devices(record_audio, play_audio, NCHANS, SR, &nframes, numBuffers)) == NULL) return -1; /* This may have been reset by driver. */ RTBUFSAMPS = nframes; } /* inTraverse waits for this. Set it even if play_audio is false! */ pthread_mutex_lock(&audio_config_lock); audio_config = 1; pthread_mutex_unlock(&audio_config_lock); if (verbose) printf("Audio set: %g sampling rate, %d channels\n", SR, NCHANS); /* Allocate output buffers. Do this *after* opening audio devices, in case OSS changes our buffer size, for example. */ for (i = 0; i < NCHANS; i++) { allocate_out_buffer(i, RTBUFSAMPS); } #ifdef MULTI_THREAD InputFile::createConversionBuffers(RTBUFSAMPS); #endif rtsetparams_called = 1; /* Put this at end to allow re-call due to error */ return 0; }
/* Minc function that sets output sampling rate (p0), maximum number of output channels (p1), and (optionally) I/O buffer size (p2). On SGI, the optional p3 lets you specify the output port as a string (e.g., "LINE", "DIGITAL", etc.). Based on this information, rtsetparams allocates a mono output buffer for each of the output channels, and opens output devices. */ double RTcmix::mm_rtsetparams(float sr, int nchans, int vecsize, float *mm_inbuf, float *mm_outbuf, char *mm_errbuf) { int i, status; int verbose = Option::print(); int play_audio = Option::play(); int record_audio = Option::record(true); #ifdef SGI static char *out_port_str = NULL; #endif /* SGI */ // BGG mm -- take this out to allow for resetting audio in maxmsp /* if (rtsetparams_called) { die("rtsetparams", "You can only call rtsetparams once!"); return -1; } */ // FIXME: Need better names for NCHANS and RTBUFSAMPS. -JGG // BGG mm SR = sr; NCHANS = nchans; RTBUFSAMPS = vecsize; maxmsp_inbuf = mm_inbuf; // passed in from max/msp via maxmsp_rtsetparams() maxmsp_outbuf = mm_outbuf; // passed in from max/msp via maxmsp_rtsetparams() maxmsp_errbuf = mm_errbuf; // passed in from max/msp via maxmsp_rtsetparams() int numBuffers = Option::bufferCount(); // BGG mm // if (n_args > 3 && pp[3] != 0.0) { #ifdef SGI /* Cast Minc double to char ptr to get string. */ int iarg = (int) pp[3]; out_port_str = (char *) iarg; rtcmix_advise("rtsetparams", "Playing through output port '%s'", out_port_str); #endif /* SGI */ // } if (SR <= 0.0) { die("rtsetparams", "Sampling rate must be greater than 0."); return -1; } if (NCHANS > MAXBUS) { die("rtsetparams", "You can only have up to %d output channels.", MAXBUS - 1); return -1; } /* play_audio is true unless user has called set_option("audio_off") before rtsetparams. This would let user run multiple jobs, as long as only one needs the audio drivers. -JGG record_audio is false unless user has called set_option("full_duplex_on") or has explicity turned record on by itself via set_option("record_on"), or has already called rtinput("AUDIO"). -DS */ if (play_audio || record_audio) { int nframes = RTBUFSAMPS; if (create_audio_devices(record_audio, play_audio, NCHANS, SR, &nframes, numBuffers) < 0) return -1; /* This may have been reset by driver. */ RTBUFSAMPS = nframes; } /* inTraverse waits for this. Set it even if play_audio is false! */ pthread_mutex_lock(&audio_config_lock); audio_config = 1; pthread_mutex_unlock(&audio_config_lock); if (verbose) post("rtcmix~: Audio set: %g sampling rate, %d channels\n", SR, NCHANS); /* Allocate output buffers. Do this *after* opening audio devices, in case OSS changes our buffer size, for example. */ for (i = 0; i < NCHANS; i++) { allocate_out_buffer(i, RTBUFSAMPS); } rtsetparams_called = 1; /* Put this at end to allow re-call due to error */ return 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; }