mirror of
https://github.com/cookiengineer/audacity
synced 2025-11-01 14:43:48 +01:00
Sven Giermann's FXP support changes
This commit is contained in:
@@ -1664,7 +1664,7 @@ void VSTEffectDialog::OnLoad(wxCommandEvent & WXUNUSED(event))
|
|||||||
FileNames::DataDir(),
|
FileNames::DataDir(),
|
||||||
wxEmptyString,
|
wxEmptyString,
|
||||||
wxT("xml"),
|
wxT("xml"),
|
||||||
wxT("*.xml"),
|
wxT("VST Effect paramter files (*.fxp; *.xml)|*.fxp;*.xml"),
|
||||||
wxFD_OPEN | wxRESIZE_BORDER,
|
wxFD_OPEN | wxRESIZE_BORDER,
|
||||||
this);
|
this);
|
||||||
|
|
||||||
@@ -1673,14 +1673,110 @@ void VSTEffectDialog::OnLoad(wxCommandEvent & WXUNUSED(event))
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Load the program
|
size_t len = fn.Len();
|
||||||
XMLFileReader reader;
|
if (len > 4 && wxStricmp(fn.Mid(len - 4), wxT(".fxp")) == 0) {
|
||||||
if (!reader.Parse(this, fn)) {
|
//
|
||||||
// Inform user of load failure
|
// FXP specification from VST SDK 2.4 (vstfxstore.h)
|
||||||
wxMessageBox(reader.GetErrorStr(),
|
//
|
||||||
_("Error Loading VST Preset"),
|
int i;
|
||||||
wxOK | wxCENTRE,
|
wxInt32 buffer[8];
|
||||||
this);
|
void *buf = NULL;
|
||||||
|
bool error = false;
|
||||||
|
|
||||||
|
// read VST program file (FXP)
|
||||||
|
wxFFile fxpFile(fn, wxT("rb"));
|
||||||
|
if (!fxpFile.IsOpened()) {
|
||||||
|
wxMessageBox(wxString::Format(_("Could not open file: \"%s\""), fn.c_str()),
|
||||||
|
_("Error Loading VST Program"),
|
||||||
|
wxOK | wxCENTRE,
|
||||||
|
this);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if (!error) error = !fread(buffer, 28, 1, fxpFile.fp());
|
||||||
|
|
||||||
|
// VST always uses Big Endian, convert first
|
||||||
|
for (i = 0; i < 7; i++)
|
||||||
|
buffer[i] = wxINT32_SWAP_ON_LE(buffer[i]);
|
||||||
|
|
||||||
|
if (!error) error = (buffer[0] != CCONST('C', 'c', 'n', 'K')); ///< 'CcnK'
|
||||||
|
//VstInt32 byteSize; ///< size of this chunk, excl. magic + byteSize
|
||||||
|
if (!error) {
|
||||||
|
if (mAEffect->flags & effFlagsProgramChunks)
|
||||||
|
error = (buffer[2] != CCONST('F', 'P', 'C', 'h')); ///< 'FxCk' (regular) or 'FPCh' (opaque chunk)
|
||||||
|
else
|
||||||
|
error = (buffer[2] != CCONST('F', 'x', 'C', 'k'));
|
||||||
|
}
|
||||||
|
if (!error) error = (buffer[3] != 1); ///< format version (currently 1)
|
||||||
|
if (!error) error = (buffer[4] != mAEffect->uniqueID); ///< fx unique ID (could try to load instead, as XML does)
|
||||||
|
//if (!error) error = (buffer[5] != ///< fx version (currently ignored)
|
||||||
|
// mEffect->callDispatcher(effGetVendorVersion, 0, 0, NULL, 0.0));
|
||||||
|
if (!error) error = (buffer[6] != mAEffect->numParams); ///< number of parameters
|
||||||
|
|
||||||
|
buffer[7] = 0; // ensure trailing NUL
|
||||||
|
if (!error) error = !fread(buffer, 28, 1, fxpFile.fp()); ///< program name (null-terminated ASCII string)
|
||||||
|
if (!error) fn = wxConvLocal.cMB2WC((char*)buffer, 29, &len);
|
||||||
|
|
||||||
|
if (mAEffect->flags & effFlagsProgramChunks) {
|
||||||
|
if (!error) error = !fread(buffer, 4, 1, fxpFile.fp());
|
||||||
|
i = wxINT32_SWAP_ON_LE(buffer[0]);
|
||||||
|
if (!error) error = (i < 1);
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
buf = new char[i];
|
||||||
|
error = !buf;
|
||||||
|
}
|
||||||
|
if (!error) error = !fread(buf, i, 1, fxpFile.fp());
|
||||||
|
if (!error) mEffect->callDispatcher(effSetChunk, 1, i, buf, 0.0);
|
||||||
|
|
||||||
|
delete [] buf;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float val;
|
||||||
|
|
||||||
|
if (!error) {
|
||||||
|
buf = new char[mAEffect->numParams << 2];
|
||||||
|
error = !buf;
|
||||||
|
}
|
||||||
|
if (!error) error = !fread(buf, mAEffect->numParams << 2, 1, fxpFile.fp());
|
||||||
|
|
||||||
|
i = -1;
|
||||||
|
while (!error && (++i < mAEffect->numParams)) {
|
||||||
|
*((wxInt32*)&val) = wxINT32_SWAP_ON_LE(((wxInt32*)buf)[i]);
|
||||||
|
if (!error) error = (val < 0.0 || val > 1.0);
|
||||||
|
if (!error) mEffect->callSetParameter(i, val);
|
||||||
|
}
|
||||||
|
|
||||||
|
delete [] buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
// set program name
|
||||||
|
if (!error && !fn.IsEmpty()) {
|
||||||
|
i = mProgram->GetCurrentSelection();
|
||||||
|
if (i < 0) i = 0; // default to first program
|
||||||
|
mProgram->SetString(i, fn);
|
||||||
|
mProgram->SetValue(fn);
|
||||||
|
mEffect->SetString(effSetProgramName, fn, i);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (error) {
|
||||||
|
wxMessageBox(_("Could not load file or incompatible content."),
|
||||||
|
_("Error Loading VST Program"),
|
||||||
|
wxOK | wxCENTRE,
|
||||||
|
this);
|
||||||
|
}
|
||||||
|
fxpFile.Close();
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
// default to read as XML file
|
||||||
|
// Load the program
|
||||||
|
XMLFileReader reader;
|
||||||
|
if (!reader.Parse(this, fn)) {
|
||||||
|
// Inform user of load failure
|
||||||
|
wxMessageBox(reader.GetErrorStr(),
|
||||||
|
_("Error Loading VST Program"),
|
||||||
|
wxOK | wxCENTRE,
|
||||||
|
this);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
RefreshParameters();
|
RefreshParameters();
|
||||||
@@ -1694,70 +1790,139 @@ void VSTEffectDialog::OnSave(wxCommandEvent & WXUNUSED(event))
|
|||||||
wxString fn;
|
wxString fn;
|
||||||
|
|
||||||
// Ask the user for the real name
|
// Ask the user for the real name
|
||||||
fn = FileSelector(_("Save VST Preset As:"),
|
FileDialog fd(this,
|
||||||
FileNames::DataDir(),
|
_("Save VST Program As:"),
|
||||||
mProgram->GetValue() + wxT(".xml"),
|
FileNames::DataDir(),
|
||||||
wxT("xml"),
|
mProgram->GetValue(),
|
||||||
wxT("*.xml"),
|
wxT("VST Effect program (*.fxp)|*.fxp|XML paramter file (*.xml)|*.xml"),
|
||||||
wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER,
|
wxFD_SAVE | wxFD_OVERWRITE_PROMPT | wxRESIZE_BORDER);
|
||||||
this);
|
|
||||||
|
|
||||||
// User canceled...
|
// User canceled...
|
||||||
if (fn.IsEmpty()) {
|
if (fd.ShowModal() == wxID_CANCEL) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
XMLFileWriter xmlFile;
|
fn = fd.GetPath();
|
||||||
|
if (fd.GetFilterIndex() == 0) {
|
||||||
|
//
|
||||||
|
// FXP specification from VST SDK 2.4 (vstfxstore.h)
|
||||||
|
//
|
||||||
|
int i;
|
||||||
|
wxInt32 buffer[8], chunkSize;
|
||||||
|
void *buf = NULL;
|
||||||
|
bool error = false;
|
||||||
|
|
||||||
// Create/Open the file
|
// Create/Open the file
|
||||||
xmlFile.Open(fn, wxT("wb"));
|
wxFFile fxpFile(fn, wxT("wb"));
|
||||||
|
if (!fxpFile.IsOpened()) {
|
||||||
xmlFile.StartTag(wxT("vstprogrampersistence"));
|
wxMessageBox(wxString::Format(_("Could not open file: \"%s\""), fn.c_str()),
|
||||||
xmlFile.WriteAttr(wxT("version"), wxT("1"));
|
_("Error Saving VST Program"),
|
||||||
|
wxOK | wxCENTRE,
|
||||||
i = mEffect->callDispatcher(effGetVendorVersion, 0, 0, NULL, 0.0);
|
this);
|
||||||
xmlFile.StartTag(wxT("effect"));
|
return;
|
||||||
xmlFile.WriteAttr(wxT("name"), mEffect->GetEffectIdentifier());
|
|
||||||
xmlFile.WriteAttr(wxT("version"), i);
|
|
||||||
|
|
||||||
xmlFile.StartTag(wxT("program"));
|
|
||||||
xmlFile.WriteAttr(wxT("name"), mProgram->GetValue());
|
|
||||||
|
|
||||||
long clen = 0;
|
|
||||||
if (mAEffect->flags & effFlagsProgramChunks) {
|
|
||||||
void *chunk = NULL;
|
|
||||||
|
|
||||||
clen = mEffect->callDispatcher(effGetChunk, 1, 0, &chunk, 0.0);
|
|
||||||
if (clen != 0) {
|
|
||||||
xmlFile.StartTag(wxT("chunk"));
|
|
||||||
xmlFile.WriteSubTree(b64encode(chunk, clen) + wxT('\n'));
|
|
||||||
xmlFile.EndTag(wxT("chunk"));
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
buffer[0] = CCONST('C', 'c', 'n', 'K'); // VstInt32 chunkMagic; ///< 'CcnK'
|
||||||
if (clen == 0) {
|
buffer[1] = 48; // VstInt32 byteSize; ///< size of this chunk, excl. magic + byteSize
|
||||||
for (i = 0; i < mAEffect->numParams; i++) {
|
if (mAEffect->flags & effFlagsProgramChunks) { ///< 'FxCk' (regular) or 'FPCh' (opaque chunk)
|
||||||
xmlFile.StartTag(wxT("param"));
|
buffer[2] = CCONST('F', 'P', 'C', 'h');
|
||||||
|
chunkSize = mEffect->callDispatcher(effGetChunk, 1, 0, &buf, 0.0);
|
||||||
xmlFile.WriteAttr(wxT("index"), i);
|
buffer[1] += 4;
|
||||||
xmlFile.WriteAttr(wxT("name"),
|
buffer[1] += chunkSize;
|
||||||
mEffect->GetString(effGetParamName, i));
|
|
||||||
xmlFile.WriteAttr(wxT("value"),
|
|
||||||
wxString::Format(wxT("%f"),
|
|
||||||
mEffect->callGetParameter(i)));
|
|
||||||
|
|
||||||
xmlFile.EndTag(wxT("param"));
|
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
buffer[2] = CCONST('F', 'x', 'C', 'k');
|
||||||
|
buffer[1] += (mAEffect->numParams << 2);
|
||||||
|
}
|
||||||
|
buffer[3] = 1; ///< format version (currently 1)
|
||||||
|
buffer[4] = mAEffect->uniqueID; ///< fx unique ID
|
||||||
|
buffer[5] = mEffect->callDispatcher(effGetVendorVersion, 0, 0, NULL, 0.0); ///< fx version
|
||||||
|
buffer[6] = mAEffect->numParams;
|
||||||
|
|
||||||
|
// VST always uses Big Endian, convert first
|
||||||
|
for (i = 0; i < 7; i++)
|
||||||
|
buffer[i] = wxINT32_SWAP_ON_LE(buffer[i]);
|
||||||
|
|
||||||
|
if (!error) error = !fwrite(buffer, 28, 1, fxpFile.fp());
|
||||||
|
memset(buffer, 0, 28);
|
||||||
|
wxConvLocal.FromWChar((char*)buffer, 27, mProgram->GetValue());
|
||||||
|
if (!error) error = (fwrite(buffer, 1, 28, fxpFile.fp()) < 28); ///< program name (null-terminated ASCII string)
|
||||||
|
|
||||||
|
if (mAEffect->flags & effFlagsProgramChunks) {
|
||||||
|
buffer[0] = wxINT32_SWAP_ON_LE(chunkSize);
|
||||||
|
if (!error) error = !fwrite(buffer, 4, 1, fxpFile.fp());
|
||||||
|
if (!error) error = !fwrite(buf, chunkSize, 1, fxpFile.fp());
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
float val;
|
||||||
|
if (!error) {
|
||||||
|
buf = new char[mAEffect->numParams << 2];
|
||||||
|
error = !buf;
|
||||||
|
}
|
||||||
|
for (i = 0; i < mAEffect->numParams; i++) {
|
||||||
|
val = mEffect->callGetParameter(i);
|
||||||
|
((wxInt32*)buf)[i] = wxINT32_SWAP_ON_LE(*((wxInt32*)&val));
|
||||||
|
}
|
||||||
|
if (!error) error = !fwrite(buf, (mAEffect->numParams << 2), 1, fxpFile.fp());
|
||||||
|
delete [] buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
fxpFile.Close();
|
||||||
}
|
}
|
||||||
|
else {
|
||||||
|
XMLFileWriter xmlFile;
|
||||||
|
|
||||||
xmlFile.EndTag(wxT("program"));
|
// Create/Open the file
|
||||||
|
xmlFile.Open(fn, wxT("wb"));
|
||||||
|
|
||||||
xmlFile.EndTag(wxT("effect"));
|
xmlFile.StartTag(wxT("vstprogrampersistence"));
|
||||||
|
xmlFile.WriteAttr(wxT("version"), wxT("1"));
|
||||||
|
|
||||||
xmlFile.EndTag(wxT("vstprogrampersistence"));
|
i = mEffect->callDispatcher(effGetVendorVersion, 0, 0, NULL, 0.0);
|
||||||
|
xmlFile.StartTag(wxT("effect"));
|
||||||
|
xmlFile.WriteAttr(wxT("name"), mEffect->GetEffectIdentifier());
|
||||||
|
xmlFile.WriteAttr(wxT("version"), i);
|
||||||
|
|
||||||
// Close the file
|
xmlFile.StartTag(wxT("program"));
|
||||||
xmlFile.Close();
|
xmlFile.WriteAttr(wxT("name"), mProgram->GetValue());
|
||||||
|
|
||||||
|
long clen = 0;
|
||||||
|
if (mAEffect->flags & effFlagsProgramChunks) {
|
||||||
|
void *chunk = NULL;
|
||||||
|
|
||||||
|
clen = mEffect->callDispatcher(effGetChunk, 1, 0, &chunk, 0.0);
|
||||||
|
if (clen != 0) {
|
||||||
|
xmlFile.StartTag(wxT("chunk"));
|
||||||
|
xmlFile.WriteSubTree(b64encode(chunk, clen) + wxT('\n'));
|
||||||
|
xmlFile.EndTag(wxT("chunk"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (clen == 0) {
|
||||||
|
for (i = 0; i < mAEffect->numParams; i++) {
|
||||||
|
xmlFile.StartTag(wxT("param"));
|
||||||
|
|
||||||
|
xmlFile.WriteAttr(wxT("index"), i);
|
||||||
|
xmlFile.WriteAttr(wxT("name"),
|
||||||
|
mEffect->GetString(effGetParamName, i));
|
||||||
|
xmlFile.WriteAttr(wxT("value"),
|
||||||
|
wxString::Format(wxT("%f"),
|
||||||
|
mEffect->callGetParameter(i)));
|
||||||
|
|
||||||
|
xmlFile.EndTag(wxT("param"));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
xmlFile.EndTag(wxT("program"));
|
||||||
|
|
||||||
|
xmlFile.EndTag(wxT("effect"));
|
||||||
|
|
||||||
|
xmlFile.EndTag(wxT("vstprogrampersistence"));
|
||||||
|
|
||||||
|
// Close the file
|
||||||
|
xmlFile.Close();
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void VSTEffectDialog::OnSettings(wxCommandEvent & WXUNUSED(event))
|
void VSTEffectDialog::OnSettings(wxCommandEvent & WXUNUSED(event))
|
||||||
|
|||||||
Reference in New Issue
Block a user