/*--------------------------------------------------------------------------------*/ bool XMLADMData::ReadXMLFromFile(const std::string& filename, bool finalise) { EnhancedFile fp; bool success = false; if (fp.fopen(filename.c_str())) { fp.fseek(0, SEEK_END); off_t len = fp.ftell(); fp.rewind(); char *buffer; if ((buffer = new char[len + 1]) != NULL) { len = fp.fread(buffer, sizeof(char), len); buffer[len] = 0; success = SetAxml(buffer, finalise); delete[] buffer; } else BBCERROR("Failed to allocate %s chars for file '%s'", StringFrom(len + 1).c_str(), filename.c_str()); fp.fclose(); } else BBCERROR("Failed to open file '%s' for reading", filename.c_str()); return success; }
/*--------------------------------------------------------------------------------*/ 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; }
/*--------------------------------------------------------------------------------*/ bool ADMRIFFFile::CreateExtraChunks() { bool success = true; if (adm) { RIFFChunk *chunk; uint64_t chnalen; uint8_t *chna; uint_t i, nchannels = GetChannels(); success = true; for (i = 0; i < nchannels; i++) { ADMAudioTrack *track; // create chna track data if ((track = adm->CreateTrack(i)) != NULL) { track->SetSampleRate(GetSampleRate()); track->SetBitDepth(GetBitsPerSample()); } } if (!admfile.empty()) { // create ADM structure (content and objects from file) if (adm->CreateFromFile(admfile.c_str())) { // can prepare cursors now since all objects have been created PrepareCursors(); } else { BBCERROR("Unable to create ADM structure from '%s'", admfile.c_str()); success = false; } } // get ADM object to create chna chunk if ((chna = adm->GetChna(chnalen)) != NULL) { // and add it to the RIFF file if ((chunk = AddChunk(chna_ID)) != NULL) { success &= chunk->CreateChunkData(chna, chnalen); } else BBCERROR("Failed to add chna chunk"); // don't need the raw data any more delete[] chna; } else BBCERROR("No chna data available"); success &= (AddChunk(axml_ID) != NULL); } return success; }
/*--------------------------------------------------------------------------------*/ bool ADMRIFFFile::Open(const char *filename, const std::string& standarddefinitionsfile) { bool success = false; if ((adm = XMLADMData::CreateADM(standarddefinitionsfile)) != NULL) { success = RIFFFile::Open(filename); } else BBCERROR("No providers for ADM XML decoding!"); return success; }
/*--------------------------------------------------------------------------------*/ bool UDPSocket::send(const void *data, uint_t bytes, const struct sockaddr *to) { bool success = false; if (socket >= 0) { if (to) success = (::sendto(socket, (const char *)data, bytes, 0, to, sizeof(*to)) >= 0); else success = (::send(socket, (const char *)data, bytes, 0) >= 0); if (!success) BBCERROR("Failed to send %u bytes to socket (%s)", bytes, strerror(errno)); } return success; }
/*--------------------------------------------------------------------------------*/ bool ADMRIFFFile::CreateADM(const std::string& standarddefinitionsfile) { bool success = false; if (!adm) { if ((adm = XMLADMData::CreateADM(standarddefinitionsfile)) != NULL) { success = true; } else BBCERROR("No providers for ADM XML decoding!"); } return success; }
/*--------------------------------------------------------------------------------*/ bool XMLADMData::LoadStandardDefinitions(const std::string& filename) { static const char *paths[] = { "{env:BBCATFILEIOSHAREDIR}", // BBCATFILEIOSHAREDIR environment variable "{fileiosharedir}", // system parameter 'fileiosharedir' "{sharedir}/bbcat-fileio", // use system parameter "share", // relative location }; std::string filename2 = filename; bool success = false; // try various sources for the filename if it is empty if (filename2.empty()) filename2 = SystemParameters::Get().Substitute("{env:BBCATSTANDARDDEFINITIONSFILE}"); if (filename2.empty()) SystemParameters::Get().GetSubstituted("standarddefinitionsfile", filename2); // use default name if none above work if (filename2.empty()) filename2 = "standarddefinitions.xml"; BBCDEBUG3(("Using '%s' as filename for standard definitions file", filename2.c_str())); // delete any existing objects Delete(); if (!(filename2 = FindFile(filename2, paths, NUMBEROF(paths))).empty()) { BBCDEBUG("Found standard definitions file '%s' as '%s'", filename.c_str(), filename2.c_str()); if (ReadXMLFromFile(filename2.c_str())) { ADMOBJECTS_IT it; // set standard def flag on all loaded objects for (it = admobjects.begin(); it != admobjects.end(); ++it) { it->second->SetStandardDefinition(); } success = true; } else BBCERROR("Failed to read standard definitions file '%s'", filename2.c_str()); } else BBCDEBUG1(("Standard definitions file '%s' doesn't exist", filename2.c_str())); return success; }
/*--------------------------------------------------------------------------------*/ sint_t UDPSocket::recv(void *data, uint_t maxbytes, struct sockaddr *from) { sint_t bytes = -1; if (socket >= 0) { // recv() with MSG_PEEK doesn't work propely unless *some* buffer is supplied so this buffer is just used // as a dumping point for the data // it *still* means recv() is thread-safe because we don't care about the data static char _staticbuf[16384]; socklen_t len = sizeof(struct sockaddr); if (from) bytes = ::recvfrom(socket, data ? (char *)data : _staticbuf, data ? maxbytes : sizeof(_staticbuf), data ? 0 : MSG_PEEK, from, &len); else bytes = ::recv(socket, data ? (char *)data : _staticbuf, data ? maxbytes : sizeof(_staticbuf), data ? 0 : MSG_PEEK); if (bytes < 0) BBCERROR("Failed to receive %u from socket (%s)", maxbytes, strerror(errno)); } return bytes; }
bool ADMRIFFFile::PostReadChunks() { bool success = RIFFFile::PostReadChunks(); // after reading of chunks, find chna and axml chunks and decode them // to create an ADM if (success) { RIFFChunk *chna = GetChunk(chna_ID); RIFFChunk *axml = GetChunk(axml_ID); // ensure each chunk is valid if (adm && chna && chna->GetData() && axml && axml->GetData()) { // decode chunks success = adm->Set(chna->GetData(), chna->GetLength(), (const char *)axml->GetData()); #if BBCDEBUG_LEVEL >= 4 { // dump ADM as text std::string str; adm->Dump(str); BBCDEBUG("%s", str.c_str()); } { // dump ADM as XML std::string str; adm->GetAxml(str); BBCDEBUG("%s", str.c_str()); } BBCDEBUG("Audio objects:"); std::vector<const ADMObject *> list; adm->GetObjects(ADMAudioObject::Type, list); uint_t i; for (i = 0; i < list.size(); i++) { BBCDEBUG("%s", list[i]->ToString().c_str()); } #endif } // test for different types of failure else if (!adm) { BBCERROR("Cannot decode ADM, no ADM decoder available"); success = false; } else if (!chna || !axml) { // acceptable failure - chna and/or axml chunk not specified - not an ADM compatible BWF file but open anyway BBCDEBUG("Warning: no chna/axml chunks!"); // if no chna supplied, create default channel mapping using standard definitions if (adm && !chna) { // attempt to find a single audioPackFormat from the standard definitions with the correct number of channels std::vector<const ADMObject *> packFormats, streamFormats; ADMAudioObject *object = adm->CreateObject("Main"); // create audio object for entire file uint_t i; // get a list of pack formats - these will be searched for the pack format with the correct number of channels adm->GetObjects(ADMAudioPackFormat::Type, packFormats); // get a list of stream formats - these will be used to search for track formats and channel formats adm->GetObjects(ADMAudioStreamFormat::Type, streamFormats); // search all pack formats for (i = 0; i < packFormats.size(); i++) { ADMAudioPackFormat *packFormat; if ((packFormat = dynamic_cast<ADMAudioPackFormat *>(const_cast<ADMObject *>(packFormats[i]))) != NULL) { // get channel format ref list - the size of this dictates the number of channels supported by the pack format const std::vector<ADMAudioChannelFormat *>& channelFormatRefs = packFormat->GetChannelFormatRefs(); // if the pack has the correct number of channels if (channelFormatRefs.size() == GetChannels()) { uint_t j; BBCDEBUG("Found pack format '%s' ('%s') for %u channels", packFormat->GetName().c_str(), packFormat->GetID().c_str(), GetChannels()); // add pack format to audio object if (object) object->Add(packFormat); // for each channel, create a track and link pack format to each for (j = 0; j < channelFormatRefs.size(); j++) { ADMAudioChannelFormat *channelFormat = channelFormatRefs[j]; ADMAudioTrack *track; std::string name; // create track if ((track = adm->CreateTrack(j)) != NULL) { uint_t k; track->Add(packFormat); // find stream format that points to the correct channel format and use that to find the trackFormat for (k = 0; k < streamFormats.size(); k++) { ADMAudioStreamFormat *streamFormat; // stream format points to channel format and track format so look for stream format with the correct channel format ref if (((streamFormat = dynamic_cast<ADMAudioStreamFormat *>(const_cast<ADMObject *>(streamFormats[k]))) != NULL) && streamFormat->GetChannelFormatRefs().size() && (streamFormat->GetChannelFormatRefs()[0] == channelFormat) && // check for correct channel format ref streamFormat->GetTrackFormatRefs().size()) // make sure there are some track formats ref'd as well { // get track format ref ADMAudioTrackFormat *trackFormat = streamFormat->GetTrackFormatRefs()[0]; BBCDEBUG("Found stream format '%s' ('%s') which refs channel format '%s' ('%s')", streamFormat->GetName().c_str(), streamFormat->GetID().c_str(), channelFormat->GetName().c_str(), channelFormat->GetID().c_str()); BBCDEBUG("Found stream format '%s' ('%s') which refs track format '%s' ('%s')", streamFormat->GetName().c_str(), streamFormat->GetID().c_str(), trackFormat->GetName().c_str(), trackFormat->GetID().c_str()); // add track format to track track->Add(trackFormat); break; } } // add track to audio object if (object) object->Add(track); } } break; } } } // set default time limits on audio object if (object && filesamples) { BBCDEBUG("Setting duration to %sns", StringFrom(filesamples->GetLengthNS()).c_str()); object->SetDuration(filesamples->GetLengthNS()); } } success = true; } else { // unacceptible failures: empty chna or empty axml chunks if (chna && !chna->GetData()) BBCERROR("Cannot decode ADM, chna chunk not available"); if (axml && !axml->GetData()) BBCERROR("Cannot decode ADM, axml chunk not available"); success = false; } // now that the data is dealt with, the chunk data can be deleted if (axml) axml->DeleteData(); if (chna) chna->DeleteData(); } return success; }
/*--------------------------------------------------------------------------------*/ void ADMRIFFFile::Close(bool abortwrite) { EnhancedFile *file = fileref; uint_t i; if (file && adm && writing && !abortwrite) { RIFFChunk *chunk; uint64_t endtime = filesamples ? filesamples->GetAbsolutePositionNS() : 0; uint64_t chnalen; uint8_t *chna; BBCDEBUG1(("Finalising ADM for '%s'...", file->getfilename().c_str())); BBCDEBUG1(("Finishing all blockformats")); // complete BlockFormats on all channels for (i = 0; i < cursors.size(); i++) { cursors[i]->Seek(endtime); cursors[i]->EndChanges(); } // finalise ADM adm->Finalise(); // update audio object time limits adm->UpdateAudioObjectLimits(); BBCDEBUG1(("Creating ADM RIFF chunks")); // get ADM object to create chna chunk if ((chna = adm->GetChna(chnalen)) != NULL) { // and add it to the RIFF file if ((chunk = GetChunk(chna_ID)) != NULL) { if (!chunk->UpdateChunkData(chna, chnalen)) BBCERROR("Failed to update chna data (possibly length has changed)"); } else BBCERROR("Failed to add chna chunk"); // don't need the raw data any more delete[] chna; } else BBCERROR("No chna data available"); // add axml chunk if ((chunk = GetChunk(axml_ID)) != NULL) { // first, calculate size of ADM (to save lots of memory allocations) uint64_t admlen = adm->GetAxmlBuffer(NULL, 0); BBCDEBUG1(("ADM size is %s bytes", StringFrom(admlen).c_str())); // allocate chunk data if (chunk->CreateChunkData(admlen)) { // finally, generate XML into buffer uint64_t admlen1 = adm->GetAxmlBuffer(chunk->GetDataWritable(), admlen); if (admlen1 != admlen) BBCERROR("Generating axml data for real resulted in different size (%s vs %s)", StringFrom(admlen1).c_str(), StringFrom(admlen).c_str()); } else BBCERROR("Failed to allocate %s bytes for axml data", StringFrom(admlen).c_str()); } else BBCERROR("Failed to add axml chunk"); } // write chunks and close file RIFFFile::Close(abortwrite); for (i = 0; i < cursors.size(); i++) { delete cursors[i]; } cursors.clear(); if (adm) { adm->Delete(); delete adm; adm = NULL; } }
/*--------------------------------------------------------------------------------*/ bool AudioObjectParameters::FromJSON(const JSONValue& obj, bool reset) { Position pval; ParameterSet sval; std::string str; double dval; sint64_t i64val; float fval; uint_t uval; int ival; bool bval; bool success = true; success &= SetFromJSON<>(Parameter_channel, values.channel, ival, obj, reset, 0U, &Limit0u); success &= SetFromJSON<>(Parameter_duration, values.duration, i64val, obj, reset, (uint64_t)0); success &= SetFromJSON<>(Parameter_cartesian, values.cartesian, bval, obj, reset); success &= SetFromJSON<>(Parameter_position, position, pval, obj, reset); success &= SetFromJSON<>(Parameter_minposition, &minposition, pval, obj, reset); success &= SetFromJSON<>(Parameter_maxposition, &maxposition, pval, obj, reset); success &= SetFromJSON<>(Parameter_gain, values.gain, dval, obj, reset, 1.0); success &= SetFromJSON<>(Parameter_width, values.width, fval, obj, reset, 0.f, &Limit0f); success &= SetFromJSON<>(Parameter_depth, values.depth, fval, obj, reset, 0.f, &Limit0f); success &= SetFromJSON<>(Parameter_height, values.height, fval, obj, reset, 0.f, &Limit0f); success &= SetFromJSON<>(Parameter_divergencebalance, values.divergencebalance, fval, obj, reset, 0.f, &Limit0to1f); success &= SetFromJSON<>(Parameter_divergenceazimuth, values.divergenceazimuth, fval, obj, reset, 0.f, &Limit0f); success &= SetFromJSON<>(Parameter_diffuseness, values.diffuseness, fval, obj, reset, 0.f, &Limit0to1f); success &= SetFromJSON<>(Parameter_delay, values.delay, fval, obj, reset, 0.f, &Limit0f); success &= SetFromJSON<>(Parameter_objectimportance, values.objectimportance, uval, obj, reset, (uint8_t)GetObjectImportanceDefault(), &LimitImportance); success &= SetFromJSON<>(Parameter_channelimportance, values.channelimportance, uval, obj, reset, (uint8_t)GetChannelImportanceDefault(), &LimitImportance); success &= SetFromJSON<>(Parameter_dialogue, values.dialogue, uval, obj, reset, (uint8_t)GetDialogueDefault(), &LimitDialogue); success &= SetFromJSON<>(Parameter_channellock, values.channellock, bval, obj, reset); success &= SetFromJSON<>(Parameter_channellockmaxdistance, values.channellockmaxdistance, fval, obj, reset, 0.f, &LimitMaxDistance); success &= SetFromJSON<>(Parameter_interact, values.interact, bval, obj, reset, (uint8_t)GetInteractDefault()); success &= SetFromJSON<>(Parameter_interpolate, values.interpolate, bval, obj, reset, (uint8_t)1); success &= SetFromJSON<>(Parameter_interpolationtime, values.interpolationtime, i64val, obj, reset, (uint64_t)0); success &= SetFromJSON<>(Parameter_onscreen, values.onscreen, bval, obj, reset); success &= SetFromJSON<>(Parameter_disableducking, values.disableducking, bval, obj, reset, (uint8_t)GetDisableDuckingDefault()); success &= SetFromJSON<>(Parameter_othervalues, othervalues, sval, obj, reset); // support legacy 'importance' parameter name for channel importance if (obj.isObject() && obj.isMember("importance")) { // read value from JSON into intermediate value if (json::FromJSON(obj["importance"], uval)) { // use intermediate value to set parameter SetParameter<>(Parameter_channelimportance, values.channelimportance, uval, &LimitImportance); } else success = false; } // delete existing list of excluded zones if (excludedZones) { delete excludedZones; excludedZones = NULL; } { // try and find zoneExclusion item in object if (obj.isObject() && obj.isMember("excludedzones") && obj["excludedzones"].isArray()) { const JSONValue& zones = obj["excludedzones"]; for (uint_t i = 0; i < zones.size(); i++) { if (zones[i].isObject()) { const JSONValue& obj = zones[i]; std::string name; float minx, miny, minz, maxx, maxy, maxz; // extract name and limits of excluded zone if (json::FromJSON(obj, "name", name) && json::FromJSON(obj, "minx", minx) && json::FromJSON(obj, "miny", miny) && json::FromJSON(obj, "minz", minz) && json::FromJSON(obj, "maxx", maxx) && json::FromJSON(obj, "maxy", maxy) && json::FromJSON(obj, "maxz", maxz)) { AddExcludedZone(name, minx, miny, minz, maxx, maxy, maxz); } else { BBCERROR("Unable to extract excluded zones from JSON '%s'", json::ToJSONString(obj).c_str()); success = false; } } } } } return success; }