Skip to content

Commit

Permalink
Implement spatialization (3 modes) by partials
Browse files Browse the repository at this point in the history
  • Loading branch information
AllenHeartcore committed Jul 5, 2023
1 parent 6d20857 commit 344c3b9
Show file tree
Hide file tree
Showing 12 changed files with 313 additions and 54 deletions.
183 changes: 153 additions & 30 deletions CMOD/src/Bottom.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -580,18 +580,38 @@ void Bottom::spatializationStereo(Sound *s,
// </Channels>

DOMElement* envelopeElement = _channels->GFEC()->GFEC();
Envelope* panning = (Envelope*) utilities->evaluateObject(XMLTC(envelopeElement),(void*)this, eventEnv );

Pan stereoPan(*panning); // cavis +1
s->setSpatializer(stereoPan);
delete panning;
string envstr;

if (applyHow == "SOUND") {
envstr = XMLTC(envelopeElement);
if (envstr == "") {
cerr << "WARNING: spatializationStereo got empty envelope for sound; ignoring" << endl;
// ^ this cannot be wrapped into computeSpatializationStereo
// since returning an empty Pan object is ambiguous
// but refactoring the function to return a pointer introduces memory hazards
} else {
Pan stereoPan = computeSpatializationStereo(envstr);
s->setSpatializer(stereoPan);
cout << "writing" << endl;
}
}

if (applyHow == "PARTIAL") {
//this isn't implemented in LASS yet, so we can't do it here
cerr << "Sorry, applying spatialization by PARTIAL not supported yet" << endl;
else if (applyHow == "PARTIAL") {
for (int i = 0; i < numParts; i++) {
envstr = XMLTC(envelopeElement);
if (envstr == "") {
cerr << "WARNING: spatializationStereo got empty envelope for partial " << i << "; ignoring" << endl;
} else {
Pan stereoPan = computeSpatializationStereo(envstr);
s->get(i).setSpatializer(stereoPan);
// ^ this cannot be wrapped into computeSpatializationStereo
// since Sound and Partial does not inherit each other
}
envelopeElement = envelopeElement->GNES();
}

}
else if (applyHow != "SOUND"){
else {
cerr << "Error: " << applyHow << " is an invalid way to apply spatialization! "
<< "Use SOUND or PARTIAL" << endl;

Expand All @@ -601,46 +621,92 @@ void Bottom::spatializationStereo(Sound *s,

//----------------------------------------------------------------------------//

/* ZIYUAN CHEN, July 2023 */
Pan Bottom::computeSpatializationStereo(string envstr) {

Envelope* panning = (Envelope*)utilities->evaluateObject(envstr, (void*)this, eventEnv);
Pan stereoPan(*panning);
delete panning;
return stereoPan;

}

//----------------------------------------------------------------------------//

void Bottom::spatializationMultiPan(Sound *s,
DOMElement* _channels,
string applyHow,
int numParts) {


// <Channels>
// <Partials>
// <Partials> <!-- channel 1 -->
// <P><Fun><Name>EnvLib</Name><Env>2</Env><Scale>1.0</Scale></Fun></P>
// </Partials>
// <Partials>
// <Partials> <!-- channel 2 -->
// <P><Fun><Name>EnvLib</Name><Env>2</Env><Scale>1.0</Scale></Fun></P>
// </Partials>
// <Partials>
// <Partials> <!-- channel 3 -->
// <P><Fun><Name>EnvLib</Name><Env>2</Env><Scale>1.0</Scale></Fun></P>
// </Partials>
// </Channels>

vector<Envelope*> mult;
vector< vector<Envelope*> > mults;
vector<bool> isPartialValid;
string envstr;
Envelope* env;
DOMElement* partials = _channels->GFEC();
DOMElement* envElement;

int j; // index of partials

// populate mults, essentially "transposing" the grid of envelopes
while (partials!=NULL){
env = (Envelope*)utilities->evaluateObject(XMLTC(partials->GFEC()), (void*)this,eventEnv);
mult.push_back(env);
envElement = partials->GFEC();
j = 0;
while (envElement != NULL) { // for each partial
if (j >= mults.size()) {
// populate mults with "bins", only effective in the first go
mults.push_back(vector<Envelope*>());
isPartialValid.push_back(true); // initialize the flags
}
envstr = XMLTC(envElement);
if (envstr == "") {
isPartialValid.at(j) = false; // missing one channel disables the entire partial
} else {
env = (Envelope*)utilities->evaluateObject(envstr, (void*)this, eventEnv);
mults.at(j).push_back(env);
}
envElement = envElement->GNES();
j++;
}
partials = partials->GNES();
}

MultiPan multipan(mult.size(), mult);
s->setSpatializer(multipan);

for (int i = 0; i < mult.size(); i ++){
delete mult[i];
}
if (applyHow == "SOUND") {
if (isPartialValid.at(0) == false) {
cerr << "WARNING: spatializationMultiPan got empty envelope for sound; ignoring" << endl;
return;
}
MultiPan multipan = computeSpatializationMultiPan(mults.at(0));
s->setSpatializer(multipan);

if (applyHow == "PARTIAL") {
//this isn't implemented in LASS yet, so we can't do it here
cerr << "Sorry, applying spatialization by PARTIAL not supported yet" << endl;
} else if (applyHow == "PARTIAL") {
for (int i = 0; i < numParts; i++) { // apply multipan to each partial
if (i >= mults.size()){
cout << "WARNING: spatializationMultiPan got empty envelopes for partial " << i << " and onwards; ignoring" << endl;
break;
}
if (isPartialValid.at(i) == false) {
cerr << "WARNING: spatializationMultiPan got empty envelope for partial " << i << "; ignoring" << endl;
continue;
}
MultiPan multipan = computeSpatializationMultiPan(mults.at(i));
s->get(i).setSpatializer(multipan);
}

}
else if (applyHow != "SOUND"){
else {
cerr << "Error: " << applyHow << " is an invalid way to apply spatialization! "
<< "Use SOUND or PARTIAL" << endl;

Expand All @@ -649,6 +715,21 @@ void Bottom::spatializationMultiPan(Sound *s,

//----------------------------------------------------------------------------//

/* ZIYUAN CHEN, July 2023 */
MultiPan Bottom::computeSpatializationMultiPan(vector<Envelope*> mult) {

MultiPan multipan(mult.size(), mult);

for (int i = 0; i < mult.size(); i++) {
delete mult[i];
}

return multipan;

}

//----------------------------------------------------------------------------//

void Bottom::spatializationPolar(Sound *s,
DOMElement* _channels,
string applyHow,
Expand All @@ -662,13 +743,54 @@ void Bottom::spatializationPolar(Sound *s,
// </Partials>
//</Channels>

Envelope* thetaEnv;
Envelope* radiusEnv;
DOMElement* thetaElement = _channels->GFEC()->GFEC();
DOMElement* radiusElement = _channels->GFEC()->GNES()->GFEC();
string theta, radius;

if (applyHow == "SOUND") {
theta = XMLTC(thetaElement);
radius = XMLTC(radiusElement);
if (theta == "" || radius == "") {
cerr << "WARNING: spatializationPolar got empty envelope for sound; ignoring" << endl;
} else {
MultiPan multipan = computeSpatializationPolar(theta, radius);
s->setSpatializer(multipan);
}
}

else if (applyHow == "PARTIAL") {
for (int i = 0; i < numParts; i++) {
theta = XMLTC(thetaElement);
radius = XMLTC(radiusElement);
if (theta == "" || radius == "") {
cerr << "WARNING: spatializationPolar got empty envelope for partial " << i << "; ignoring" << endl;
} else {
MultiPan multipan = computeSpatializationPolar(theta, radius);
s->get(i).setSpatializer(multipan);
}
thetaElement = thetaElement->GNES();
radiusElement = radiusElement->GNES();
}

}
else {
cerr << "Error: " << applyHow << " is an invalid way to apply spatialization! "
<< "Use SOUND or PARTIAL" << endl;

}

}

//----------------------------------------------------------------------------//

thetaEnv = (Envelope*)utilities->evaluateObject(XMLTC(thetaElement), (void*)this,eventEnv);
radiusEnv = (Envelope*)utilities->evaluateObject(XMLTC(radiusElement), (void*)this,eventEnv);
/* ZIYUAN CHEN, July 2023 */
MultiPan Bottom::computeSpatializationPolar(string thetaEnvStr, string radiusEnvStr) {

Envelope* thetaEnv;
Envelope* radiusEnv;

thetaEnv = (Envelope*)utilities->evaluateObject(thetaEnvStr, (void*)this,eventEnv);
radiusEnv = (Envelope*)utilities->evaluateObject(radiusEnvStr, (void*)this,eventEnv);

MultiPan multipan(utilities->getNumberOfChannels());
float time, theta, radius;
Expand All @@ -692,10 +814,11 @@ void Bottom::spatializationPolar(Sound *s,

multipan.doneAddEntryLocation();

s->setSpatializer(multipan);
delete thetaEnv;
delete radiusEnv;

return multipan;

}

//----------------------------------------------------------------------------//
Expand Down
23 changes: 23 additions & 0 deletions CMOD/src/Bottom.h
Original file line number Diff line number Diff line change
Expand Up @@ -266,6 +266,29 @@ class Bottom : public Event {
**/
void rules(int numPartials, float ampScale[]);

/** ZIYUAN CHEN, July 2023
* Computes a Pan for applying spatializationStereo to a sound.
* \param envstr transcoded envelope string to be applied
* \return a Pan object parameterized by the envelope
**/
Pan computeSpatializationStereo(string envstr);

/** ZIYUAN CHEN, July 2023
* Computes a MultiPan for applying spatializationMultiPan to a sound.
* \param mult a list of envelopes to be applied
* \return a MultiPan object associated with the designated partial
**/
MultiPan computeSpatializationMultiPan(vector<Envelope*> mult);

/** ZIYUAN CHEN, July 2023
* Computes a MultiPan for applying spatializationPolar to a sound.
* \param thetaEnvStr transcoded "theta" envelope string to be applied
* \param radiusEnvStr transcoded "radius" envelope string to be applied
* \return a MultiPan object with 100 entry locations
* parameterized by theta and radius
**/
MultiPan computeSpatializationPolar(string thetaEnvStr, string radiusEnvStr);

//----------------------------------------------------------------------------//

/**
Expand Down
2 changes: 1 addition & 1 deletion LASS/src/MultiPan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -340,7 +340,7 @@ void MultiPan::addEntryLocation(float t, float theta, float radius)
* have to pass in nChans in the constructor)
* @return the track, spatialized to 'numTracks' number of tracks
**/
MultiTrack* MultiPan::spatialize(Track& t, int numTracks)
MultiTrack* MultiPan::spatialize_Track(Track& t, int numTracks)
{
m_sample_count_type sampleCount = t.getWave().getSampleCount();
m_rate_type samplingRate = t.getWave().getSamplingRate();
Expand Down
2 changes: 1 addition & 1 deletion LASS/src/MultiPan.h
Original file line number Diff line number Diff line change
Expand Up @@ -135,7 +135,7 @@ class MultiPan : public Spatializer
* have to pass in nChans in the constructor)
* \return the track, spatialized to 'numTracks' number of tracks
**/
MultiTrack* spatialize(Track& t, int numTracks);
MultiTrack* spatialize_Track(Track& t, int numTracks);

/**
* \deprecated
Expand Down
2 changes: 1 addition & 1 deletion LASS/src/Pan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ Pan* Pan::clone()
return new Pan(*panVar_);
}

MultiTrack* Pan::spatialize(Track& t, int numTracks)
MultiTrack* Pan::spatialize_Track(Track& t, int numTracks)
{
m_sample_count_type sampleCount = t.getWave().getSampleCount();
m_rate_type samplingRate = t.getWave().getSamplingRate();
Expand Down
2 changes: 1 addition & 1 deletion LASS/src/Pan.h
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,7 @@ class Pan : public Spatializer
* \param numTracks The number of tracks
* \return A pointer to a new MultiTrack
**/
MultiTrack* spatialize(Track& t, int numTracks);
MultiTrack* spatialize_Track(Track& t, int numTracks);

/**
* \deprecated
Expand Down
33 changes: 28 additions & 5 deletions LASS/src/Partial.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -54,11 +54,13 @@ Partial::Partial()
setParam(FREQTRANS_WIDTH, 1103);

reverbObj = NULL;
spatializer_ = new Spatializer();
}


//----------------------------------------------------------------------------//
Track* Partial::render(m_sample_count_type sampleCount,
MultiTrack* Partial::render(int numChannels,
m_sample_count_type sampleCount,
m_time_type duration,
m_rate_type samplingRate)
{
Expand Down Expand Up @@ -352,19 +354,31 @@ Track* Partial::render(m_sample_count_type sampleCount,
}

// create a TrackObject:
Track *returnTrack = new Track(waveSample, ampSample);
Track *_track = new Track(waveSample, ampSample);

// do the reverb, if necessary
if(reverbObj != NULL)
{
Track &tmp = reverbObj->do_reverb_Track(*returnTrack);
delete returnTrack;
returnTrack = new Track(tmp);
Track &tmp = reverbObj->do_reverb_Track(*_track);
delete _track;
_track = new Track(tmp);

// delete the temporary track object
delete &tmp;

}

/* ZIYUAN CHEN, July 2023: On the default behavior of spatialization (Partial side)
* If a non-placeholder subclass of Spatializer (Pan/MultiPan) is set in the sound,
* "spatializer_" in the partials will be placeholders that merely averages the
* sound evenly accross all tracks.
* But unlike Sound::render(), the placeholders are NOT IGNORED since they are essential
* for transforming Track to MultiTrack, whose channels uniformly hold scaled versions
* of the original Track.
* Compare Sound::render().
*/
MultiTrack* returnTrack = spatializer_->spatialize_Track(*_track, numChannels);

//cout << "Partial::render - frequency after detune:" << getParam(FREQ_ENV).getMaxValue() << endl;
// cout<< "--------------------------------------------"<< endl;

Expand All @@ -389,6 +403,14 @@ inline m_value_type Partial::pmod(m_value_type num)
}


//----------------------------------------------------------------------------//
/* ZIYUAN CHEN, July 2023 */
void Partial::setSpatializer(Spatializer& s)
{
delete spatializer_;
spatializer_ = s.clone();
}

//----------------------------------------------------------------------------//
void Partial::use_reverb(Reverb *newReverbObj)
{
Expand Down Expand Up @@ -419,6 +441,7 @@ void Partial::xml_print( ofstream& xmlOutput, list<Reverb*>& revObjs, list<Dynam
revObjs.push_back( reverbObj );
}

spatializer_->xml_print( xmlOutput );

// Static Parameters
xmlOutput << "\t\t\t<relative_amplitude value=\"" << getParam(RELATIVE_AMPLITUDE) << "\" />" << endl;
Expand Down

0 comments on commit 344c3b9

Please sign in to comment.