/*--------------------------------------------------------------------------------*/ bool XMLADMData::ReadChnaFromFile(const std::string& filename, bool finalise) { EnhancedFile fp; bool success = false; if (fp.fopen(filename.c_str())) { char buffer[1024]; int l; success = true; while (success && ((l = fp.readline(buffer, sizeof(buffer) - 1)) != EOF)) { if (l > 0) { std::vector<std::string> words; SplitString(std::string(buffer), words); if (words.size() == 4) { uint_t tracknum; if (Evaluate(words[0], tracknum)) { if (tracknum) { const std::string& trackuid = words[1]; const std::string& trackformat = words[2]; const std::string& packformat = words[3]; ADMAudioTrack *track; std::string id; if ((track = dynamic_cast<ADMAudioTrack *>(Create(ADMAudioTrack::Type, trackuid, ""))) != NULL) { XMLValue tvalue, pvalue; track->SetTrackNum(tracknum - 1); tvalue.name = ADMAudioTrackFormat::Reference; tvalue.value = trackformat; track->AddValue(tvalue); pvalue.name = ADMAudioPackFormat::Reference; pvalue.value = packformat; track->AddValue(pvalue); track->SetValues(); BBCDEBUG2(("Track %u: Index %u UID '%s' TrackFormatRef '%s' PackFormatRef '%s'", (uint_t)tracklist.size(), track->GetTrackNum() + 1, track->GetID().c_str(), tvalue.value.c_str(), pvalue.value.c_str())); } else { BBCERROR("Failed to create AudioTrack for UID %u", tracknum); success = false; } } } else { BBCERROR("CHNA line '%s' word 1 ('%s') should be a track number", buffer, words[0].c_str()); success = false; } } else { BBCERROR("CHNA line '%s' requires 4 words", buffer); success = false; } } } fp.fclose(); if (success && finalise) Finalise(); } return success; }
/*--------------------------------------------------------------------------------*/ bool XMLADMData::SetChna(const uint8_t *data, uint64_t len) { const CHNA_CHUNK& chna = *(const CHNA_CHUNK *)data; uint_t maxuids = (uint_t)((len - sizeof(CHNA_CHUNK)) / sizeof(chna.UIDs[0])); // calculate maximum number of UIDs given chunk length std::string terminator; bool success = true; // create string with a single 0 byte in it to detect terminators terminator.push_back(0); if (maxuids < chna.UIDCount) BBCERROR("Warning: chna specifies %u UIDs but chunk has only length for %u", (uint_t)chna.UIDCount, maxuids); uint16_t i; for (i = 0; (i < chna.UIDCount) && (i < maxuids); i++) { // only handle non-zero track numbers if (chna.UIDs[i].TrackNum) { ADMAudioTrack *track; std::string id; id.assign(chna.UIDs[i].UID, sizeof(chna.UIDs[i].UID)); id = id.substr(0, id.find(terminator)); // delete any existing ADMAudioTracks of the same ID if ((track = dynamic_cast<ADMAudioTrack *>(Create(ADMAudioTrack::Type, id, "", true))) != NULL) { XMLValue tvalue, pvalue; track->SetTrackNum(chna.UIDs[i].TrackNum - 1); tvalue.name = ADMAudioTrackFormat::Reference; tvalue.value.assign(chna.UIDs[i].TrackRef, sizeof(chna.UIDs[i].TrackRef)); // trim any zero bytes off the end of the string tvalue.value = tvalue.value.substr(0, tvalue.value.find(terminator)); track->AddValue(tvalue); pvalue.name = ADMAudioPackFormat::Reference; pvalue.value.assign(chna.UIDs[i].PackRef, sizeof(chna.UIDs[i].PackRef)); // trim any zero bytes off the end of the string pvalue.value = pvalue.value.substr(0, pvalue.value.find(terminator)); track->AddValue(pvalue); track->SetValues(); BBCDEBUG2(("Track %u/%u: Index %u UID '%s' TrackFormatRef '%s' PackFormatRef '%s'", i, (uint_t)tracklist.size(), track->GetTrackNum() + 1, track->GetID().c_str(), tvalue.value.c_str(), pvalue.value.c_str())); } else BBCERROR("Failed to create AudioTrack for UID %u", i); } } SortTracks(); return success; }
/*--------------------------------------------------------------------------------*/ void AudioObjectParameters::ToJSON(JSONValue& obj, bool force) const { SetToJSON<>(Parameter_channel, (int)values.channel, obj, force); SetToJSON<>(Parameter_duration, (sint64_t)values.duration, obj, force); SetToJSON<>(Parameter_cartesian, values.cartesian, obj, force); SetToJSON<>(Parameter_position, position, obj, force); SetToJSONPtr<>(Parameter_minposition, minposition, obj, force); SetToJSONPtr<>(Parameter_maxposition, maxposition, obj, force); SetToJSON<>(Parameter_gain, values.gain, obj, force); SetToJSON<>(Parameter_width, values.width, obj, force); SetToJSON<>(Parameter_depth, values.depth, obj, force); SetToJSON<>(Parameter_height, values.height, obj, force); SetToJSON<>(Parameter_diffuseness, values.diffuseness, obj, force); SetToJSON<>(Parameter_divergencebalance, values.divergencebalance, obj, force); SetToJSON<>(Parameter_divergenceazimuth, values.divergenceazimuth, obj, force); SetToJSON<>(Parameter_delay, values.delay, obj, force); SetToJSON<>(Parameter_objectimportance, (int)values.objectimportance, obj, force); SetToJSON<>(Parameter_channelimportance, (int)values.channelimportance, obj, force); SetToJSON<>(Parameter_dialogue, (int)values.dialogue, obj, force); SetToJSON<>(Parameter_channellock, values.channellock, obj, force); SetToJSON<>(Parameter_channellockmaxdistance, values.channellockmaxdistance, obj, force); SetToJSON<>(Parameter_interact, values.interact, obj, force); SetToJSON<>(Parameter_interpolate, values.interpolate, obj, force); SetToJSON<>(Parameter_interpolationtime, (sint64_t)values.interpolationtime, obj, force); SetToJSON<>(Parameter_onscreen, values.onscreen, obj, force); SetToJSON<>(Parameter_disableducking, values.disableducking, obj, force); SetToJSON<>(Parameter_othervalues, othervalues, obj, force); // output all excluded zones const ExcludedZone *zone = GetFirstExcludedZone(); if (zone) { JSONValue zones; while (zone) { JSONValue subobj; Position c1 = zone->GetMinCorner(); Position c2 = zone->GetMaxCorner(); subobj["name"] = zone->GetName(); subobj["minx"] = c1.pos.x; subobj["miny"] = c1.pos.y; subobj["minz"] = c1.pos.z; subobj["maxx"] = c2.pos.x; subobj["maxy"] = c2.pos.y; subobj["maxz"] = c2.pos.z; zones.append(subobj); zone = zone->GetNext(); } obj["excludedzones"] = zones; } BBCDEBUG2(("JSON: %s", ToJSONString(obj, true).c_str())); }
/*--------------------------------------------------------------------------------*/ uint8_t *XMLADMData::GetChna(uint64_t& len) const { CHNA_CHUNK *p = NULL; uint_t nuids = (uint_t)tracklist.size(); #if CHNA_FIXED_UIDS_LOWER > 0 nuids = (nuids <= CHNA_FIXED_UIDS_LOWER) ? CHNA_FIXED_UIDS_LOWER : CHNA_FIXED_UIDS_UPPER; #endif // calculate size of chna chunk len = sizeof(*p) + (uint64_t)nuids * (uint64_t)sizeof(p->UIDs[0]); if ((p = (CHNA_CHUNK *)calloc(1, len)) != NULL) { std::vector<bool> uniquetracks; // list of unique tracks uint_t i; // allocate maximum number of tracks uniquetracks.resize(tracklist.size()); // populate structure p->TrackCount = 0; p->UIDCount = 0; for (i = 0; (i < tracklist.size()) && (p->UIDCount < nuids); i++) { const ADMAudioTrack *track = tracklist[i]; uint_t tr = track->GetTrackNum(); // test to see if uniquetracks array needs expanding if (tr >= uniquetracks.size()) uniquetracks.resize(tr + 1); // if this is a new track (i.e. one not previously used),increment TrackCount if (!uniquetracks[tr]) { // set flag uniquetracks[tr] = true; // increment TrackCount p->TrackCount++; } // set track number (1- based) and UID p->UIDs[i].TrackNum = tr + 1; strncpy(p->UIDs[i].UID, track->GetID().c_str(), sizeof(p->UIDs[i].UID)); // set trackformat references const ADMAudioTrackFormat *trackref = NULL; if (track->GetTrackFormatRefs().size() && ((trackref = track->GetTrackFormatRefs()[0]) != NULL)) { strncpy(p->UIDs[i].TrackRef, trackref->GetID().c_str(), sizeof(p->UIDs[i].TrackRef)); } // set packformat references const ADMAudioPackFormat *packref = NULL; if (track->GetPackFormatRefs().size() && ((packref = track->GetPackFormatRefs()[0]) != NULL)) { strncpy(p->UIDs[i].PackRef, packref->GetID().c_str(), sizeof(p->UIDs[i].PackRef)); } p->UIDCount++; BBCDEBUG2(("Track %u/%u: Index %u UID '%s' TrackFormatRef '%s' PackFormatRef '%s'", i, (uint_t)tracklist.size(), track->GetTrackNum() + 1, track->GetID().c_str(), trackref ? trackref->GetID().c_str() : "<none>", packref ? packref->GetID().c_str() : "<none>")); } } return (uint8_t *)p; }
/*--------------------------------------------------------------------------------*/ void BiQuadCoeffs::CalcCoeffs(Filter_t type, double freq, double fs, double gain, double bandwidth, double interp_time) { double A, omega, sn, cs, alpha, beta; // numerator coeffs double& b0 = targets.num0; double& b1 = targets.num1; double& b2 = targets.num2; // denominator coeffs double a0 = 1.0; // this will be used to normalize the coeffs at the end double& a1 = targets.den1; double& a2 = targets.den2; // setup variables A = pow(10.0, gain / 40.0); omega = 2.0 * M_PI * freq / fs; sn = sin(omega); cs = cos(omega); alpha = sn * sinh(M_LN2 / 2.0 * bandwidth * omega / sn); beta = sqrt(A + A); switch (type) { default: case FLAT: // flat b0 = 1.0; b1 = 0.0; b2 = 0.0; a0 = 1.0; a1 = 0.0; a2 = 0.0; break; case LPF6: // lowpass filter (6dB/octave) - NOT from Audio EQ Cookbook // sn // = ---------------- // (1 + sn) - z(-1) b0 = sn; b1 = 0; b2 = 0; a0 = 1 + sn; a1 = -1; a2 = 0; break; case LPF12: // lowpass filter (12dB/octave) - NOT from Audio EQ Cookbook // sn sn sn^2 // = ---------------- . ---------------- = ------------------------------------- // (1 + sn) - z(-1) (1 + sn) - z(-1) (1 + sn)^2 - 2.(1 + sn).z(-1) + z(-2) b0 = sn * sn; b1 = 0; b2 = 0; a0 = (1 + sn) * (1 + sn); a1 = -2 * (1 + sn); a2 = 1; break; case HPF6: // highpass filter (6dB/octave) - NOT from Audio EQ Cookbook // 1 - z(-1) // = ------------------ // 1 - (1 - sn).z(-1) b0 = 1; b1 = -1; b2 = 0; a0 = 1; a1 = -(1 - sn); a2 = 0; break; case HPF12: // highpass filter (12dB/octave) - NOT from Audio EQ Cookbook // 1 - z(-1) 1 - z(-1) 1 - 2.z(-1) + z(-2) // = ------------------ . ------------------ = --------------------------------------- // 1 - (1 - sn).z(-1) 1 - (1 - sn).z(-1) 1 - 2.(1 - sn).z(-1) + (1 - sn)^2.z(-2) b0 = 1; b1 = -2; b2 = 1; a0 = 1; a1 = -2 * (1 - sn); a2 = (1 - sn) * (1 - sn); break; case BPF: // bandpass filter b0 = alpha; b1 = 0; b2 = -alpha; a0 = 1 + alpha; a1 = -2 * cs; a2 = 1 - alpha; break; case NOTCH: // notch reject filter b0 = 1; b1 = -2 * cs; b2 = 1; a0 = 1 + alpha; a1 = -2 * cs; a2 = 1 - alpha; break; case PEQ: // peaking filter b0 = 1 + (alpha * A); b1 = -2 * cs; b2 = 1 - (alpha * A); a0 = 1 + (alpha / A); a1 = -2 * cs; a2 = 1 - (alpha / A); break; case LSH: // lowshelf filter b0 = A * ((A + 1) - (A - 1) * cs + beta * sn); b1 = 2 * A * ((A - 1) - (A + 1) * cs); b2 = A * ((A + 1) - (A - 1) * cs - beta * sn); a0 = (A + 1) + (A - 1) * cs + beta * sn; a1 = -2 * ((A - 1) + (A + 1) * cs); a2 = (A + 1) + (A - 1) * cs - beta * sn; break; case HSH: // highshelf filter b0 = A * ((A + 1) + (A - 1) * cs + beta * sn); b1 = -2 * A * ((A - 1) + (A + 1) * cs); b2 = A * ((A + 1) + (A - 1) * cs - beta * sn); a0 = (A + 1) - (A - 1) * cs + beta * sn; a1 = 2 * ((A - 1) - (A + 1) * cs); a2 = (A + 1) - (A - 1) * cs - beta * sn; break; } { // normalize coeffs double normalise = 1.0 / a0; b0 *= normalise; b1 *= normalise; b2 *= normalise; a1 *= normalise; a2 *= normalise; } // calculate differences between current and target for interpolation diffs.num0 = targets.num0 - current.num0; diffs.num1 = targets.num1 - current.num1; diffs.num2 = targets.num2 - current.num2; diffs.den1 = targets.den1 - current.den1; diffs.den2 = targets.den2 - current.den2; if (interp_time > 0.0) { // set initial multiplier and decrement rate mul = 1.0; dec = 1.0 / (interp_time * fs); } else { // zero time interpolation - just set multiplier at zero and current to targets mul = dec = 0.0; current = targets; } BBCDEBUG2(("Coeffs: type %u freq %0.1lfHz gain %0.1lfdB bw %0.6lfHz -> {%0.6lf, %0.6lf, %0.6lf, %0.6lf, %0.6lf}", (uint_t)type, freq, gain, bandwidth, b0, b1, b2, a1, a2)); #if BBCDEBUG_LEVEL >= 3 { // write response out to file EnhancedFile fp("coeffs.dat", "w"); double fs = 48000.0; double f1 = 10.0, f2 = 22000.0; double frange = log(f2 / f1); uint_t i, steps = 1000; for (i = 0; i < steps; i++) { double p = (double)i / (double)(steps - 1); double f = f1 * exp(p * frange); double gain = CalcResponse(f, fs); fp.fprintf("%u %0.1lf %0.4le\n", i, f, gain); } } #endif }