Using taglib in a Qt application - c++

I'd like to get the length of a media file in a qt application i'm building and so i decided to use taglib. This is the methos that is meant to read the length
void loadMetaData(QString file) {
QByteArray fileName = QFile::encodeName( file );
const char * encodedName = fileName.constData();
TagLib::FileRef fileref = TagLib::FileRef( encodedName );
if (fileref.isNull())
{
qDebug() << "Null";
}
else
{
qDebug() << "Not Null";
}
}
Problem is fileref is always null for some reason and i can't figure out why......

Use the getter audioProperties() on your FileRef object. The returned pointer contains the length of the file in seconds.

TagLib# is able to work with some Theora files. I used it in a project but found it wouldn't work with many Theora videos (I don't think any converted using libtheora 1.1 worked).
TagLib.File file = TagLib.File.Create(#"c:\video.ogv");
string height = file.Properties.VideoHeight;
This is for the .NET, not C++ though.

Related

Pass a string path link of a sound to be a Sound Base Object in Unreal Engine 5.1

UMySoundBase::UMySoundBase()
{
FilePath = "";
}
void UMySoundBase::SetFilePath(FString NewFilePath)
{
// Check if the file has a valid extension
FString Extension = FPaths::GetExtension(NewFilePath);
if (Extension.Compare(TEXT("mp3"), ESearchCase::IgnoreCase) != 0 && Extension.Compare(TEXT("wav"), ESearchCase::IgnoreCase) != 0 )
{
UE_LOG(LogTemp, Warning, TEXT("Invalid file extension %s"), *Extension);
return;
}
FilePath = NewFilePath;
// Open the file at the specified file path
TArray<uint8> FileData;
if (FFileHelper::LoadFileToArray(FileData, *FilePath))
{
// Create a new sound wave object
USoundWave* SoundWave = NewObject<USoundWave>(this);
// Assign the file data to the sound wave
FMemoryWriter MemoryWriter(SoundWave->RawData);
MemoryWriter << FileData;
// Assign the sound wave to the sound base
Sound = SoundWave;
}
}
I'm just trying to learn how to compile my first Unreal Engine Plugin experimenting with an idea of a better workflow for something I noticed wasnt native to Unreal Engine. Sound Base Objects can be manipulated with more functions than media player.

How to read the length of audio files using Juce "C++." Without playing the file

I'm trying to display the length of audio files in a Playlist component for an application.
I've not used Juce or C++ before, and I can't understand how to do that from Juce documentation.
I want to make a function that takes an audio file's URL and returns the length in seconds of that audio without playing that file or doing anything else with that file.
I've tried a lot of things, and all of them didn't work, and this is the last thing I've tried:
void PlaylistComponent::trackStats(URL audioURL)
{
AudioFormatManager formatManager;
std::unique_ptr<AudioFormatReaderSource> readerSource;
AudioTransportSource transportSource;
auto* reader = formatManager.createReaderFor(audioURL.createInputStream(false));
if (reader != nullptr)
{
std::unique_ptr<AudioFormatReaderSource> newSource(new AudioFormatReaderSource(reader, true));
transportSource.setSource(newSource.get(), 0, nullptr, reader->sampleRate);
readerSource.reset(newSource.release());
DBG("PlaylistComponent::trackStats(URL audioURL): " << transportSource.getLengthInSeconds());
}
else
{
DBG("Something went wrong loading the file");
}
}
And this is the PlaylistComponent header file:
class PlaylistComponent : public juce::Component,
public juce::TableListBoxModel,
public Button::Listener,
public FileDragAndDropTarget
{
...
}
juce::AudioFormatReaderSource has a method called getTotalLength() which returns the total amount of samples.
Divide that by the sample rate of the file and you have the total length in seconds. Something like this:
if (auto* reader = audioFormatReaderSource->getAudioFormatReader())
double lengthInSeconds = static_cast<double> (audioFormatReaderSource->getTotalLength()) / reader->sampleRate;
You can do this very early on in the audio file opening procedure. You only need an AudioFormatReader instance (no need to create an AudioFormatReaderSource):
// create juce::File from a path juce::String (from a drag & drop event etc).
File file{filePath};
// make sure it is a file and not a directory, etc.
if (!file.existsAsFile()) return;
// create the AudioFormatReader that contains the data
AudioFormatReader *reader = formatManagerInstance.createReaderFor(file);
// make sure a valid reader can be created (not an unsupported file)
if (reader == nullptr) return;
// log the length in seconds
std::cout << reader->lengthInSamples / reader->sampleRate << "\n";
Note: for this to work you will need to
have access to your AudioFormatManager instance and
have already registered the format(s) of the file type (usually through a .registerBasicFormats() call on your AudioFormatManager instance.
IMPORTANT: if successful the createReaderFor() method uses new to create a new AudioFormatReaderInstance, so make sure to use delete on it when you are finished using it to avoid memory leaks

C++ Protobuf, error when trying to build a FileDescriptor that imports another one

Currently I have the following two proto definitions, both .proto files are in same folder:
topmessage.proto:
syntax = "proto3";
message TopMessage {
// some fields
}
crestmessage.proto:
syntax = "proto3";
import "topmessage.proto";
message CrestMessage {
// some fields
TopMessage tm = 4;
}
Then, as part of my reader.cc, I am trying to build a file descriptor for the proto definition that user passes in. More specifically, the user will pass the path of the .proto file in as an argument, and the program will then read the file and build a file descriptor from it. Here is how this function is implemented, it mainly follows the blog by Floris https://vdna.be/site/index.php/2016/05/google-protobuf-at-run-time-deserialization-example-in-c/:
DescriptorPool pool;
const FileDescriptor* buildFileDescriptor(string file_path, string file_name) {
int def_messageFile = open(file_path.c_str(), O_RDONLY);
FileInputStream file_input(def_messageFile);
Tokenizer input(&file_input, NULL);
FileDescriptorProto file_desc_proto;
Parser parser;
if (!parser.Parse(&input, &file_desc_proto)) {
cerr << "Failed to parse .proto definition:" << endl;
return NULL;
}
file_desc_proto.set_name(file_name);
const FileDescriptor* file_desc = pool.BuildFile(file_desc_proto);
return file_desc;
}
The question now is when I am trying to build CrestMessage, and its proto definition file is also the one passed in as argument. For simplicity, I first build TopMessage by manually providing its file address since it is the dependency, and it works fine, and I can also find TopMessage in the pool, which is global. However, when I try to build CrestMessage, it gives the error.
const FileDescriptor* file_desc2 = buildFileDescriptor("topmessage.proto", "TopMessage");
cout << pool.FindFileByName("TopMessage") << endl;
const FileDescriptor* file_desc = buildFileDescriptor(definition_path, "CrestMessage");
cout << file_desc->name() << endl;
enter image description here
I have not find anything in the api description by Google that says how to import, does anyone have ideas on what should be used?
it's a name problem, instead of using message name when building FileDescriptor, use the name of the .proto file ("topmessage.proto" for example)

findAndGetString() in DCMTK returns null for the tag

I am developing a quick DICOM viewer using DCMTK library and I am following the example provided in this link.
The buffer from the API always returns null for any tag ID, eg: DCM_PatientName.
But the findAndGetOFString() API works fine but returns only the first character of the tag in ASCII, is this how this API should work?
Can someone let me know why the buffer is empty the former API?
Also the DicomImage API also the same issue.
Snippet 1:
DcmFileFormat fileformat;
OFCondition status = fileformat.loadFile(test_data_file_path.toStdString().c_str());
if (status.good())
{
OFString patientName;
char* name;
if (fileformat.getDataset()->findAndGetOFString(DCM_PatientName, patientName).good())
{
name = new char[patientName.length()];
strcpy(name, patientName.c_str());
}
else
{
qDebug() << "Error: cannot access Patient's Name!";
}
}
else
{
qDebug() << "Error: cannot read DICOM file (" << status.text() << ")";
}
In the above snippet name has the ASCII value "50" and the actual name is "PATIENT".
Snippet 2:
DcmFileFormat file_format;
OFCondition status = file_format.loadFile(test_data_file_path.toStdString().c_str());
std::shared_ptr<DcmDataset> dataset(file_format.getDataset());
qDebug() << "\nInformation extracted from DICOM file: \n";
const char* buffer = nullptr;
DcmTagKey key = DCM_PatientName;
dataset->findAndGetString(key,buffer);
std::string tag_value = buffer;
qDebug() << "Patient name: " << tag_value.c_str();
In the above snippet, the buffer is null. It doesn't read the name.
NOTE:
This is only a sample. I am just playing around the APIs for learning
purpose.
The following sample method reads the patient name from a DcmDataset object:
std::string getPatientName(DcmDataset& dataset)
{
// Get the tag's value in ofstring
OFString ofstring;
OFCondition condition = dataset.findAndGetOFString(DCM_PatientName, ofstring);
if(condition.good())
{
// Tag found. Put it in a std::string and return it
return std::string(ofstring.c_str());
}
// Tag not found
return ""; // or throw if you need the tag
}
I have tried your code with your datasets. I just replaced the output to QT console classes to std::cout. It works for me - i.e. it prints the correct patient name (e.g. "PATIENT2" for scan2.dcm). Everything seems correct, except for the fact that you apparently want to transfer the ownership for the dataset to a smart pointer.
To obtain the ownership for the DcmDataset from the DcmFileFormat, you must call getAndRemoveDataset() instead of getDataset(). However, I do not think that your issue is related that. You may want to try my modified snippet:
DcmFileFormat file_format;
OFCondition status = file_format.loadFile("d:\\temp\\StackOverflow\\scan2.dcm");
std::shared_ptr<DcmDataset> dataset(file_format.getAndRemoveDataset());
std::cout << "\nInformation extracted from DICOM file: \n";
const char* buffer = nullptr;
DcmTagKey key = DCM_PatientName;
dataset->findAndGetString(key, buffer);
std::string tag_value = buffer;
std::cout << "Patient name: " << tag_value.c_str();
It probably helps you to know that your code and the dcmtk methods you use are correct, but that does not solve your problem. Another thing I would recommend is to verify the result returned by file_format.loadFile(). Maybe there is a surprise in there.
Not sure if I can help you more, but my next step would be to verify your build environment, e.g. the options that you use for building dcmtk. Are you using CMake to build dcmtk?

C++ iterating through files and directories

I'm working on a C++ program that will automatically backup my work to my FTP server. So far I am able to upload a single file, by specifying a file name using this
CString strFilePath = szFile ;
int iPos = strFilePath.ReverseFind('\\');
CString strFileName = strFilePath.Right((strFilePath.GetLength()- iPos-1) );
CString strDirPath = m_szFolderDroppedIn ;
strDirPath = strDirPath.Mid(0,strDirPath.GetLength() - 1);
int iPost = strDirPath.ReverseFind('\\');
CString strDirName = strDirPath.Right((strDirPath.GetLength()- iPost -1) );
bool curdir = ftpclient.SetServerDirectory((char*)strDirName.GetBuffer(strDirName.GetLength()));
//Upload to Server
int uploadret = ftpclient.PutFile(szFile,(char*)strFileName.GetBuffer(strFileName.GetLength()),0,true,dwLastError);
m_lsDroppedFiles.RemoveAll();
break;
}
Now I want to be able to iterate through a directory (Which contains subdirectories) and recursively call. I'm having a problem getting a hold of the files in the directory.
Any help or code snippet...
Since you are using MFC, you can use the CFileFind class. Example code is given in MSDN. Alternatively, you can use boost.filesystem for the same.
#Swapnil: If you use boost::filesystem, there is a recursive_directory_iterator