/* Send the contents of a makegen array to standard out or to an ASCII file. fdump(gennum [, "filename"]) JGG, 2/13/02 */ double fdump(float p[], short n_args, double pp[]) { int genslot; double *array; FILE *f = NULL; genslot = p[0]; if (n_args > 1) { char *fname = DOUBLE_TO_STRING(pp[1]); f = fopen(fname, "w+"); if (f == NULL) { perror("fdump"); return -1.0; } } else f = stdout; array = floc(genslot); if (array) { int i, len = fsize(genslot); printf("Dumping function table %d...\n", genslot); for (i = 0; i < len; i++) fprintf(f, "%d %.6f\n", i, array[i]); } else die(NULL, "You must make a gen before dumping it!"); if (f != stdout) fclose(f); return 0.0; }
/* Assemble path to the shared library, and pass back as <dsoPath>. */ static int get_dso_path(double pfield, char dsoPath[]) { char *str; #ifdef SHAREDLIBDIR const char *directory = SHAREDLIBDIR; #else const char *directory = "/musr/lib"; #endif /* cast double to string pointer */ str = DOUBLE_TO_STRING(pfield); if (!str || strlen(str) == 0) { die("load", "Bad argument for p[0]!"); return -1; } /* if name contains a '/', assume it is a full or relative path */ if (strchr(str, '/')) strcpy(dsoPath, str); /* if name does not start with "lib", add prefix and suffix */ else if (strncmp(str, "lib", 3)) sprintf(dsoPath, "%s/lib%s.so", directory, str); /* otherwise just prepend directory and use as is */ else sprintf(dsoPath, "%s/%s", directory, str); return 0; }
double dataset(float *p, int n_args, double *pp) /* p1=dataset name, p2=npoles */ { int i, set; char *name=DOUBLE_TO_STRING(pp[0]); if (name == NULL) { rterror("dataset", "NULL file name"); return -1; } // Search all open dataset slots for matching name for (set = 0; set < maxDataSets && strlen(g_dataset_names[set]); ++set) { if (strcmp(name, g_dataset_names[set]) == 0) { g_currentDataset = set; ::rtcmix_advise("dataset", "Using already open dataset at slot %d", set); return g_datasets[g_currentDataset]->getFrameCount(); } } if (set >= maxDataSets) { ::rterror("dataset", "Maximum number of datasets exceeded"); return -1; } // OK, this is a new set that we will put in a new slot g_currentDataset = set; strcpy(g_dataset_names[g_currentDataset],name); int npolesGuess = 0; if(n_args>1) /* if no npoles specified, it will be retrieved from */ npolesGuess= (int) p[1]; /* the header (if USE_HEADERS #defined) */ DataSet *dataSet = new DataSet; int frms = dataSet->open(name, npolesGuess, RTcmix::sr()); if (frms < 0) { if (dataSet->getNPoles() == 0) { ::rterror("dataset", "For this file, you must specify the correct value for npoles in p[1]."); } return -1; } ::rtcmix_advise("dataset", "File has %d poles and %d frames.", dataSet->getNPoles(), frms); // Add to dataset list. g_datasets[g_currentDataset] = dataSet; dataSet->ref(); // Note: For now, datasets are never destroyed during run. return (double) frms; }
//--------------------------------------------------------------- set_option --- double RTcmix::set_option(float *p, int nargs, double pp[]) { for (int i = 0; i < nargs; i++) { char *arg = DOUBLE_TO_STRING(pp[i]); // cast pfield to string if (_parse_arg(arg, rtsetparams_was_called()) == -1) return -1.0; } return 0.0; }
/* Return the passed in (double) argument as a string type. */ MincString _minc_tostring(const MincValue args[], const int nargs) { if (nargs != 1) { minc_warn("tostring: must have one argument"); return NULL; } if (args[0].dataType() != MincFloatType) { minc_warn("tostring: argument must be float type"); return NULL; } const char *convertedString = DOUBLE_TO_STRING((MincString)args[0]); return strdup(convertedString); }
/** * These pfields are standard for LUAINST instruments: * p0 = Lua instrument name (string, all others are doubles). * p1 = Output start time (outskip). * p2 = Input start time (inskip, must be zero if there is no input). * p3 = Duration. * p4 = Amplitude. * pN = User-defined optional parameters. */ virtual int init(double *parameters, int parameterCount) { state.name = strdup(DOUBLE_TO_STRING(parameters[0])); state.parameters = new double[parameterCount]; state.parameterCount = parameterCount; for (int parameterI = 0; parameterI < parameterCount; ++parameterI) { state.parameters[parameterI] = parameters[parameterI]; } if (rtsetoutput((float) parameters[1], (float) parameters[3], this) == -1) { return DONT_SCHEDULE; } if (parameters[2] != 0.0) { if (rtsetinput(parameters[1], this) == -1) { return DONT_SCHEDULE; } state.inputChannelCount = inputChannels(); } state.outputChannelCount = outputChannels(); return nSamps(); }
double m_infile(float *p, short n_args, double *pp) { FILE *descrip; char *name; int fno; name = DOUBLE_TO_STRING(pp[0]); fno = p[1]; /* Reject fno = 0, because that's indicates stdin to gen2. */ if (fno < 1 || fno > MAX_INFILE_DESC) return die("infile", "File number must be between 1 and %d.", MAX_INFILE_DESC); descrip = fopen(name,"r"); if (descrip == NULL) return die("infile", "Cannot find %s ... not opened.", name); else { infile_desc[fno] = descrip; rtcmix_advise("infile", "Datafile %s opened as file %d.", name, fno); } return fno; }
double lua_intro(float *p, int n, double *pp) { const char *NAME = DOUBLE_TO_STRING(pp[0]); const char *luacode = DOUBLE_TO_STRING(pp[1]); return (double) LUA_INTRO(NAME, luacode); }
/* ----------------------------------------------------------- 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 m_open(float *p, short n_args, double *pp) { char *name,*cp,*getsfcode(); int fno,i,inew; float *opk; name = DOUBLE_TO_STRING(pp[0]); fno = p[1]; // JGG: will name ptr be valid for entire program run? Is its memory held by // parser? If not, we should malloc sfname[fno] below (with other mallocs) sfname[fno] = name; status[fno] = (n_args == 3) ? (int)p[2] : 2; if((fno >= NFILES) || (fno < 0)) { rtcmix_warn("m_open", "Only %d files allowed\n", NFILES); closesf(); } inew = 0; if(isopen[fno]) { close(sfd[fno]); } else inew = 1; istape[fno] = (n_args == 4) ? 1 : 0; /* in the case of a tape, there will be a 4th argument listing the file number */ rwopensf(name,sfd[fno],sfdesc[fno],sfst[fno],"CMIX",i,status[fno]); if (i < 0) closesf(); if (status[fno] == O_RDWR && !WRITEABLE_HEADER_TYPE(sfheadertype(&sfdesc[fno]))) { rtcmix_warn("m_open", "can't write this type of header.\n"); closesf(); } isopen[fno] = 1; swap_bytes[fno] = swap; /* swap and isNext set in rwopensf */ is_Next[fno] = isNext; headersize[fno] = getheadersize(&sfdesc[fno]); rtcmix_advise(NULL, "name: %s sr: %.3f nchans: %d class: %d\n",name, sfsrate(&sfdesc[fno]),sfchans(&sfdesc[fno]), sfclass(&sfdesc[fno])); rtcmix_advise(NULL, "Soundfile type: %s\n", mus_header_type_name(sfheadertype(&sfdesc[fno]))); rtcmix_advise(NULL, " data format: %s\n", mus_data_format_name(sfdataformat(&sfdesc[fno]))); rtcmix_advise(NULL, "Duration of file is %f seconds.\n", (float)(sfst[fno].st_size - headersize[fno])/(float)sfclass(&sfdesc[fno])/(float)sfchans(&sfdesc[fno])/sfsrate(&sfdesc[fno])); originalsize[fno] = istape[fno] ? 999999999 : sfst[fno].st_size; /* sfstats(sfd[fno]); */ if(inew) { if((sndbuf[fno] = (char *)malloc((unsigned)nbytes)) == NULL) { rtcmix_warn("CMIX", "malloc sound buffer error\n"); closesf(); } if((peakloc[fno] = (char *)malloc((unsigned)(sfchans(&sfdesc[fno]) * LONG))) == NULL) { rtcmix_warn("CMIX", "malloc ovpeak buffer error\n"); closesf(); } if((peak[fno] = (char *)malloc((unsigned)(sfchans(&sfdesc[fno])* FLOAT))) == NULL) { rtcmix_warn("CMIX", "malloc peak buffer error!\n"); closesf(); } peakoff[fno] = 0; /* default to peakcheckon when opening file*/ punch[fno] = 0; /* default to no punch when opening file*/ } if(sfclass(&sfdesc[fno]) == SHORT) { addoutpointer[fno] = _iaddout; layoutpointer[fno] = _ilayout; wipeoutpointer[fno] = _iwipeout; getinpointer[fno] = _igetin; } else { addoutpointer[fno] = _faddout; layoutpointer[fno] = _flayout; wipeoutpointer[fno] = _fwipeout; getinpointer[fno] = _fgetin; } if(!SR()) set_SR(sfsrate(&sfdesc[fno])); if(sfsrate(&sfdesc[fno])!= SR()) rtcmix_advise("CMIX", "Note--> SR reset to %f\n",SR()); /* read in former peak amplitudes, make sure zero'ed out to start.*/ /* In the sndlib version, we store peak stats differently. See comments in sndlibsupport.c for an explanation. The sndlib version of rwopensf reads peak stats, so here we just have to copy these into the sfm[fno] array. (No swapping necessary.) */ memcpy(&sfm[fno], &(sfmaxampstruct(&sfdesc[fno])), sizeof(SFMAXAMP)); for(opk = (float *)peak[fno], i = 0; i<sfchans(&sfdesc[fno]); i++) *(opk+i) = sfmaxamp(&sfm[fno],i); bufsize[fno] = nbytes / sfclass(&sfdesc[fno]);/* set size in words */ return 0.0; }
/* -------------------------------------------------- parse_rtoutput_args --- */ int RTcmix::parse_rtoutput_args(int nargs, double pp[]) { int i, j, matched; int normfloat_requested; char *arg; if (nargs == 0) { rterror("rtoutput", "you didn't specify a file name!"); return -1; } rtoutsfname = DOUBLE_TO_STRING(pp[0]); if (rtoutsfname == NULL) { rterror("rtoutput", "NULL file name!"); return -1; } output_header_type = header_type_from_filename(rtoutsfname); if (output_header_type == -1) return -1; if (output_header_type == -2) output_header_type = DEFAULT_HEADER_TYPE; output_data_format = DEFAULT_DATA_FORMAT; normfloat_requested = 0; for (i = 1; i < nargs; i++) { arg = DOUBLE_TO_STRING(pp[i]); matched = 0; for (j = 0; j < num_params; j++) { if (strcasecmp(param_list[j].arg, arg) == 0) { matched = 1; break; } } if (!matched) { rterror("rtoutput", "unrecognized argument \"%s\"", arg); return -1; } switch (param_list[j].type) { case HEADER_TYPE: output_header_type = param_list[j].value; break; case DATA_FORMAT: output_data_format = param_list[j].value; if (output_data_format == MUS_BFLOAT && strcasecmp(param_list[j].arg, "normfloat") == 0) normfloat_requested = 1; break; case ENDIANNESS: /* currently unused */ break; default: break; } } /* Handle some special cases. */ /* If "wav", make data format little-endian. */ if (output_header_type == MUS_RIFF) { switch (output_data_format) { case MUS_BSHORT: output_data_format = MUS_LSHORT; break; case MUS_B24INT: output_data_format = MUS_L24INT; break; case MUS_BFLOAT: output_data_format = MUS_LFLOAT; break; } } /* If AIFF, use AIFC only if explicitly requested, or if the data format is float. */ if (output_header_type == MUS_AIFF && output_data_format == MUS_BFLOAT) output_header_type = MUS_AIFC; /* If writing to a float file, decide whether to normalize the samples, i.e., to divide them all by 32768 so as to make the normal range fall between -1.0 and +1.0. This is what Snd and sndlib like to see, but it's not the old cmix way. */ if (normfloat_requested) normalize_output_floats = 1; is_float_format = IS_FLOAT_FORMAT(output_data_format); #ifdef ALLBUG fprintf(stderr, "name: %s, head: %d, data: %d, norm: %d\n", rtoutsfname, output_header_type, output_data_format, normalize_output_floats); #endif 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; }
double NPAN_set_speakers(float p[], int nargs, double pp[]) { if (nargs < 5 || !(nargs % 2)) return usage(); if (_num_speakers > 0) { for (int i = 0; i < _num_speakers; i++) // delete ones from previous call delete _speakers[i]; delete [] _speakers; } _num_speakers = (nargs - 1) / 2; if (_num_speakers < 2) return die("NPANspeakers", "Must have at least 2 speakers."); if (_num_speakers > MAX_SPEAKERS) return die("NPANspeakers", "Can't have more than %d speakers.", MAX_SPEAKERS); _speakers = new Speaker * [_num_speakers]; char *modestr = DOUBLE_TO_STRING(pp[0]); if (strncmp(modestr, "pol", 3) == 0) { // polar coordinates // Convert from user coordinates by adding 90 degrees, then convert // to radians. Finally, normalize radians to [-PI, PI]. int j = 1; for (int i = 0; i < _num_speakers; i++, j += 2) { const double degrees = pp[j] + 90.0; // user to internal degrees double angle = M_PI * 2 * (degrees / 360.0); // to radians angle = atan2(sin(angle), cos(angle)); // normalize const double distance = pp[j + 1]; _speakers[i] = new Speaker(i, angle, distance); } } else { // cartesian coordinates int j = 1; for (int i = 0; i < _num_speakers; i++, j += 2) { const double x = pp[j]; const double y = pp[j + 1]; const double angle = atan2(y, x); const double distance = sqrt((x * x) + (y * y)); _speakers[i] = new Speaker(i, angle, distance); } } // sort speaker array by ascending angle qsort(_speakers, _num_speakers, sizeof(Speaker *), compare_speaker_angles); // cache angle of adjacent speaker for (int i = 0; i < _num_speakers; i++) { if (i == 0) _speakers[i]->setPrevAngle(_speakers[_num_speakers - 1]->angle() - TWO_PI); else _speakers[i]->setPrevAngle(_speakers[i - 1]->angle()); if (i == _num_speakers - 1) _speakers[i]->setNextAngle(_speakers[0]->angle() + TWO_PI); else _speakers[i]->setNextAngle(_speakers[i + 1]->angle()); } // check for two speakers having same angle for (int i = 0; i < _num_speakers; i++) { if (_speakers[i]->nextAngle() == _speakers[i]->angle()) return die("NPANspeakers", "Can't put two speakers in same location."); } return 0.0; }