Skip to content

Commit

Permalink
ffmpeg export: (failed) attempt at feeding raw PCM into fmpeg (beware…
Browse files Browse the repository at this point in the history
… - loud noises!)
  • Loading branch information
yohannd1 committed Dec 29, 2024
1 parent 2b107a1 commit 098fe50
Show file tree
Hide file tree
Showing 5 changed files with 120 additions and 106 deletions.
18 changes: 1 addition & 17 deletions src/engine/sfWrapper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -64,7 +64,7 @@ sf_count_t SFWrapper::ioRead(void* ptr, sf_count_t count) {
}

sf_count_t SFWrapper::ioWrite(const void* ptr, sf_count_t count) {
return write(fd,ptr,count);
return fwrite(ptr,1,count,fp);
}

sf_count_t SFWrapper::ioTell() {
Expand Down Expand Up @@ -160,19 +160,3 @@ SNDFILE* SFWrapper::doOpenFromWriteFd(int writeFd, SF_INFO* sfinfo) {
}
return sf;
}

bool SFWrapper::disableBlockingWrite() {
int flags=fcntl(fd,F_GETFL);
if (flags==-1) {
logE("error with fcntl (%s)",strerror(errno));
return false;
}
flags|=O_NONBLOCK;
if (fcntl(fd,F_SETFL,flags)==-1) {
logE("error with fcntl (%s)",strerror(errno));
return false;
}

blockingWrite=false;
return true;
}
5 changes: 1 addition & 4 deletions src/engine/sfWrapper.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,6 @@ class SFWrapper {
SF_VIRTUAL_IO vio;
SNDFILE* sf;
int fileMode;
bool blockingWrite;

public:
sf_count_t ioGetSize();
Expand All @@ -53,14 +52,12 @@ class SFWrapper {
int doClose();
SNDFILE* doOpen(const char* path, int mode, SF_INFO* sfinfo);
SNDFILE* doOpenFromWriteFd(int fd, SF_INFO* sfinfo);
bool disableBlockingWrite();
SFWrapper():
fp(NULL),
fd(-1),
len(0),
sf(NULL),
fileMode(0),
blockingWrite(true) {}
fileMode(0) {}
};

#endif
105 changes: 70 additions & 35 deletions src/engine/wavOps.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@
#include "../ta-log.h"
#include "../subprocess.h"
#include "../stringutils.h"
#include <fcntl.h>
#include <unistd.h>

#ifdef HAVE_SNDFILE
#include "sfWrapper.h"
Expand Down Expand Up @@ -109,7 +111,7 @@ class SndfileWavWriter {
SF_INFO si;
SFWrapper sfWrap;
public:
bool open(DivEngine* e, SF_INFO info, const char* exportPath) {
bool open(SF_INFO info, const char* exportPath) {
si=info;
sf=sfWrap.doOpen(exportPath,SFM_WRITE,&si);
if (sf==NULL) {
Expand All @@ -125,57 +127,68 @@ class SndfileWavWriter {
}
}

bool write(float* buf, sf_count_t count) {
return sf_writef_float(sf,buf,count)==count;
bool write(float* samples, sf_count_t count) {
return sf_writef_float(sf,samples,count)==count;
}
};

class SndfileToProcWriter {
class ProcWriter {
private:
Subprocess* proc;
SNDFILE* sf;
SF_INFO si;
SFWrapper sfWrap;
int writeFd;
int format;
public:
SndfileToProcWriter():
proc(NULL) {}
ProcWriter():
proc(NULL),
writeFd(-1) {}

bool open(DivEngine* e, SF_INFO info, Subprocess* proc_, int writeFd) {
bool open(Subprocess* proc_, int writeFd_, int format) {
proc=proc_;
si=info;
writeFd=writeFd_;

sf=sfWrap.doOpenFromWriteFd(writeFd,&si);
if (sf==NULL) {
int flags=fcntl(writeFd,F_GETFL);
if (flags==-1) {
logE("error with fcntl (%s)",strerror(errno));
return false;
}

if (!sfWrap.disableBlockingWrite()) {
logE("could not disable blocking write");
sfWrap.doClose();
flags|=O_NONBLOCK;
if (fcntl(writeFd,F_SETFL,flags)==-1) {
logE("error with fcntl (%s)",strerror(errno));
return false;
}

return true;
}

void close() {
if (sfWrap.doClose()!=0) {
logE("could not close audio file!");
}
::close(writeFd);
}

bool write(float* buf, sf_count_t count) {
while (true) {
if (sf_writef_float(sf,buf,count)==count) {
logD("successful write...");
return true;
bool write(float* samples, size_t count) {
const auto doWrite=[this](void* buf, size_t size) {
while (true) {
if (::write(writeFd,buf,size)==(ssize_t)size) return true;

logD("buffer got full; waiting...");
if (!proc->waitStdinOrExit()) {
logE("subprocess died before reading all the audio data");
return false;
}
}
};

logD("buffer got full; waiting...");
if (!proc->waitStdinOrExit()) {
logE("subprocess died before reading all the audio data");
return false;
if (format==DIV_EXPORT_FORMAT_S16) {
size_t size=count*2;
uint8_t buf[size];
for (size_t i=0; i<size;) {
// little endian
int16_t sample=32767*samples[i];
buf[i++]=(uint8_t)(sample>>8);
buf[i++]=(uint8_t)sample;
}
return doWrite(buf,size);
} else {
return doWrite(samples,count*sizeof(float));
}
}
};
Expand All @@ -190,10 +203,14 @@ void DivEngine::runExportThread() {
SF_INFO si;
si.samplerate=got.rate;
si.channels=exportOutputs;
if (exportFormat==DIV_EXPORT_FORMAT_S16) {
switch (exportFormat) {
case DIV_EXPORT_FORMAT_S16:
si.format=SF_FORMAT_WAV|SF_FORMAT_PCM_16;
} else {
break;
case DIV_EXPORT_FORMAT_F32:
default:
si.format=SF_FORMAT_WAV|SF_FORMAT_FLOAT;
break;
}
return si;
};
Expand Down Expand Up @@ -271,18 +288,36 @@ void DivEngine::runExportThread() {

if (exportFileExtNoDot=="wav") {
SndfileWavWriter wr;
if (!wr.open(this,makeSfInfo(),exportPath.c_str())) {
if (!wr.open(makeSfInfo(),exportPath.c_str())) {
logE("could not initialize export writer");
exporting=false;
return;
}
doExport(&wr);
} else {
String inputFormatArg=(exportFormat==DIV_EXPORT_FORMAT_S16)?"s16le":"f32le";

// build command vector
std::vector<String> command={"ffmpeg","-y","-f","wav","-i","pipe:0","-f",exportFileExtNoDot};
std::vector<String> command={
"ffmpeg","-y",
"-v","verbose",
"-f",inputFormatArg,
"-ar",fmt::sprintf("%ld",(long int)got.rate), // sample rate
"-ac",fmt::sprintf("%d",exportOutputs), // channel amount
// "-guess_layout_max","0",
"-i","pipe:0",
"-f",exportFileExtNoDot
};
splitString(exportFfmpegFlags,' ',command);
command.push_back(exportPath);

String totalCommand;
for (const String& s : command) {
totalCommand+=s;
totalCommand+=" ";
}
logD("command: %s",totalCommand);

Subprocess proc(command);
int writeFd=proc.pipeStdin();
if (writeFd==-1) {
Expand All @@ -297,8 +332,8 @@ void DivEngine::runExportThread() {
return;
}

SndfileToProcWriter wr;
if (!wr.open(this,makeSfInfo(),&proc,writeFd)) {
ProcWriter wr;
if (!wr.open(&proc,writeFd,exportFormat)) {
logE("could not initialize export writer");
exporting=false;
return;
Expand Down
7 changes: 3 additions & 4 deletions src/subprocess.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -176,22 +176,21 @@ void Subprocess::closeStdinPipe(bool careAboutError) {
}

bool Subprocess::waitStdinOrExit() {
// I've lost my patience completely so let's just throw a sleep call here. Fuck this.

struct pollfd pf;
pf.fd=stdinPipe.writeFd;
pf.events=POLLOUT;

while (true) {
int pollResult=poll(&pf,1,250); // FIXME: ACTUALLY WAIT! until a signal is caught or the polling ends (). Right now the CPU usage isn't all that high but there's an overhead here I think. I tried with ppoll() but didn't manage to make it work
// I've lost my patience completely so let's just throw a poll call with a timeout here.
int pollResult=poll(&pf,1,250);
if (pollResult==-1) {
logE("failed to use ppoll (%s)\n",strerror(errno));
return false;
}

if (pollResult > 0) return true;

// TODO: refactor this (getExitCodeNoWait() might work but it's not 100% analogous)
// TODO: refactor this (getExitCodeNoWait() might work but it's not 100% the same thing)
int status;
int result=waitpid(childPid,&status,WNOHANG);
if (result!=0) {
Expand Down
91 changes: 45 additions & 46 deletions src/subprocess.h
Original file line number Diff line number Diff line change
Expand Up @@ -23,61 +23,60 @@
#include "ta-utils.h"

class Subprocess {
public:
struct Pipe {
int readFd;
int writeFd;
void close(bool careAboutError=true);
Pipe(): readFd(-1), writeFd(-1) {}
Pipe(int pipeArr[2]): readFd(pipeArr[0]), writeFd(pipeArr[1]) {}
};
public:
struct Pipe {
int readFd;
int writeFd;
void close(bool careAboutError=true);
Pipe(): readFd(-1), writeFd(-1) {}
Pipe(int pipeArr[2]): readFd(pipeArr[0]), writeFd(pipeArr[1]) {}
};

enum Status {
SUBPROCESS_NOT_STARTED,
SUBPROCESS_RUNNING,
SUBPROCESS_FINISHED
};
enum Status {
SUBPROCESS_NOT_STARTED,
SUBPROCESS_RUNNING,
SUBPROCESS_FINISHED
};

private:
std::vector<String> args;
pid_t childPid=-1;
int exitCode=-1;
int statusCode;
Pipe stdinPipe, stdoutPipe, stderrPipe;
Status status=SUBPROCESS_NOT_STARTED;
private:
std::vector<String> args;
pid_t childPid=-1;
int statusCode;
Pipe stdinPipe, stdoutPipe, stderrPipe;
Status status=SUBPROCESS_NOT_STARTED;

public:
Subprocess(std::vector<String> args);
~Subprocess();
public:
Subprocess(std::vector<String> args);
~Subprocess();

// These functions enable piping, and return a file descriptor to an end of the created pipe.
// On stdin, it's the writable end; on stdout and stderr, it's the readable end.
// These should only be called before start(). They return -1 on error.
int pipeStdin();
int pipeStdout();
int pipeStderr();
// These functions enable piping, and return a file descriptor to an end of the created pipe.
// On stdin, it's the writable end; on stdout and stderr, it's the readable end.
// These should only be called before start(). They return -1 on error.
int pipeStdin();
int pipeStdout();
int pipeStderr();

void closeStdinPipe(bool careAboutError=true);
// void closeStdoutPipe(bool careAboutError=true);
// void closeStderrPipe(bool careAboutError=true);
void closeStdinPipe(bool careAboutError=true);
// void closeStdoutPipe(bool careAboutError=true);
// void closeStderrPipe(bool careAboutError=true);

// starts the subprocess.
// returns whether it successfully started
bool start();
// starts the subprocess.
// returns whether it successfully started
bool start();

// waits for the stdin pipe (write end) to be writable, or for the subprocess
// to die
bool waitStdinOrExit();
// waits for the stdin pipe (write end) to be writable, or for the subprocess
// to die
bool waitStdinOrExit();

// tries to get the subprocess's exit code.
// if `wait` is true, waits for the subprocess to finish and sets its exit code to `outCode`.
// if not, just checks if it has finished already.
// returns whether it has succeeded.
bool getExitCode(int *outCode, bool wait);
// tries to get the subprocess's exit code.
// if `wait` is true, waits for the subprocess to finish and sets its exit code to `outCode`.
// if not, just checks if it has finished already.
// returns whether it has succeeded.
bool getExitCode(int *outCode, bool wait);

// simpler versions of `getExitCode`
bool waitForExitCode(int *outCode);
bool getExitCodeNoWait(int *outCode);
// simpler versions of `getExitCode`
bool waitForExitCode(int *outCode);
bool getExitCodeNoWait(int *outCode);
};

#endif

0 comments on commit 098fe50

Please sign in to comment.