reimplement sample import using ffmpeg

portability/macos
zetaPRIME 2019-07-21 16:54:39 -04:00
parent 3d678c173b
commit ffc7be1783
2 changed files with 68 additions and 2 deletions

5
notes
View File

@ -32,8 +32,9 @@ parameters {
TODO {
immediate frontburner {
reimplement sample import to IPC from standalone ffmpeg since QAudioDecoder is partially broken on arch and completely broken on macOS
ffprobe -v quiet -show_streams -select_streams a -of json testOut.mp3
- reimplement sample import to IPC from standalone ffmpeg since QAudioDecoder is partially broken on arch and completely broken on macOS
# ffprobe -v quiet -show_streams -select_streams a -of json testOut.mp3
# ffmpeg -i input.flv -f f32le -acodec pcm_f32le -
distortion effect
single-selection sampler

View File

@ -9,11 +9,18 @@ using namespace Xybrid::Data;
#include <QCborMap>
#include <QCborArray>
#include <QJsonDocument>
#include <QJsonArray>
#include <QJsonObject>
#include <QFileInfo>
#include <QAudioDecoder>
#include <QAudioFormat>
#include <QProcess>
#include <QDataStream>
#include <QEventLoop>
#define qs QStringLiteral
@ -88,6 +95,7 @@ bool Sample::changeUuid(QUuid newUuid) {
void Sample::newUuid() { changeUuid(QUuid::createUuid()); }
#ifdef OLD_SAMPLE_IMPORT
namespace {
bool blah [[maybe_unused]];
template<typename T> void insertbuf(std::shared_ptr<Sample> smp, const T* data, size_t len, size_t channels) {
@ -154,6 +162,63 @@ std::shared_ptr<Sample> Sample::fromFile(QString fileName) {
return smp;
}
#else
std::shared_ptr<Sample> Sample::fromFile(QString fileName) {
QJsonObject info;
{
// get stream info
QProcess probe;
QStringList param;
param << "-v" << "quiet" << "-show_streams" << "-select_streams" << "a" << "-of" << "json";
param << fileName;
probe.start("ffprobe", param);
probe.waitForFinished();
auto doc = QJsonDocument::fromJson(probe.readAllStandardOutput());
info = doc.object()["streams"].toArray().first().toObject();
}
if (!info.contains("sample_rate") || !info.contains("channels")) return nullptr; // no/invalid audio streams
int channels = info["channels"].toInt();
int sampleRate = info["sample_rate"].toString().toInt(); // for some reason ffprobe stores this as a string??
auto smp = std::make_shared<Sample>();
smp->sampleRate = sampleRate;
// grab raw pcm_f32le via ffmpeg
QByteArray raw;
{
QProcess dec;
QStringList param;
param << "-i" << fileName << "-f" << "f32le" << "-acodec" << "pcm_f32le" << "-";
dec.start("ffmpeg", param);
dec.waitForFinished();
raw = dec.readAllStandardOutput();
}
// pre-size sample data buffers
auto len = static_cast<size_t>((raw.length() / channels) / 4);
smp->data[0].reserve(len);
if (channels > 1) smp->data[1].reserve(len);
// read raw bytes into channels
QDataStream r(&raw, QIODevice::ReadOnly);
size_t chs = static_cast<size_t>(channels);
size_t ch = chs;
float f;
while (!r.atEnd()) {
r.readRawData(reinterpret_cast<char*>(&f), sizeof(f));
ch = (ch+1) % chs;
if (ch < 2) smp->data[ch].push_back(f);
}
// add info
smp->uuid = QUuid::createUuid();
smp->name = QFileInfo(fileName).baseName();
return smp;
}
#endif
namespace {
bool exporting = false;