1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-10-25 15:53:52 +02:00

More consistent mutual exclusion in calls to certain sf_ functions; ...

... also SFCall to simplify thread-safe calls, and SFFile for RAII of SNDFILE
objects.
This commit is contained in:
Paul Licameli
2016-04-12 15:59:17 -04:00
parent 24df87bb4c
commit 61177a15ad
11 changed files with 360 additions and 384 deletions

View File

@@ -250,8 +250,9 @@ void ExportPCMOptions::OnHeaderChoice(wxCommandEvent & WXUNUSED(evt))
bool valid = ValidatePair(fmt);
if (valid)
{
mEncodingNames.Add(sf_encoding_index_name(i));
mEncodingChoice->Append(sf_encoding_index_name(i));
const auto name = sf_encoding_index_name(i);
mEncodingNames.Add(name);
mEncodingChoice->Append(name);
mEncodingFormats.Add(enc);
for (j = 0; j < sfnum; j++)
{
@@ -405,148 +406,127 @@ int ExportPCM::Export(AudacityProject *project,
sf_format = kFormats[subformat].format;
}
wxString formatStr;
SF_INFO info;
SNDFILE *sf = NULL;
int err;
//This whole operation should not occur while a file is being loaded on OD,
//(we are worried about reading from a file being written to,) so we block.
//Furthermore, we need to do this because libsndfile is not threadsafe.
ODManager::LockLibSndFileMutex();
formatStr = sf_header_name(sf_format & SF_FORMAT_TYPEMASK);
ODManager::UnlockLibSndFileMutex();
// Use libsndfile to export file
info.samplerate = (unsigned int)(rate + 0.5);
info.frames = (unsigned int)((t1 - t0)*rate + 0.5);
info.channels = numChannels;
info.format = sf_format;
info.sections = 1;
info.seekable = 0;
// If we can't export exactly the format they requested,
// try the default format for that header type...
if (!sf_format_check(&info))
info.format = (info.format & SF_FORMAT_TYPEMASK);
if (!sf_format_check(&info)) {
wxMessageBox(_("Cannot export audio in this format."));
return false;
}
wxFile f; // will be closed when it goes out of scope
if (f.Open(fName, wxFile::write)) {
// Even though there is an sf_open() that takes a filename, use the one that
// takes a file descriptor since wxWidgets can open a file with a Unicode name and
// libsndfile can't (under Windows).
ODManager::LockLibSndFileMutex();
sf = sf_open_fd(f.fd(), SFM_WRITE, &info, FALSE);
//add clipping for integer formats. We allow floats to clip.
sf_command(sf, SFC_SET_CLIPPING, NULL,sf_subtype_is_integer(sf_format)?SF_TRUE:SF_FALSE) ;
ODManager::UnlockLibSndFileMutex();
}
if (!sf) {
wxMessageBox(wxString::Format(_("Cannot export audio to %s"),
fName.c_str()));
return false;
}
// Retrieve tags if not given a set
if (metadata == NULL)
metadata = project->GetTags();
// Install the metata at the beginning of the file (except for
// WAV and WAVEX formats)
if ((sf_format & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAV &&
(sf_format & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAVEX) {
if (!AddStrings(project, sf, metadata, sf_format)) {
sf_close(sf);
return false;
}
}
sampleFormat format;
if (sf_subtype_more_than_16_bits(info.format))
format = floatSample;
else
format = int16Sample;
int maxBlockLen = 44100 * 5;
int updateResult = eProgressSuccess;
const WaveTrackConstArray waveTracks =
tracks->GetWaveTrackConstArray(selectionOnly, false);
{
auto mixer = CreateMixer(waveTracks,
tracks->GetTimeTrack(),
t0, t1,
info.channels, maxBlockLen, true,
rate, format, true, mixerSpec);
wxFile f; // will be closed when it goes out of scope
SFFile sf; // wraps f
ProgressDialog progress(wxFileName(fName).GetName(),
selectionOnly ?
wxString::Format(_("Exporting the selected audio as %s"),
formatStr.c_str()) :
wxString::Format(_("Exporting the entire project as %s"),
formatStr.c_str()));
wxString formatStr;
SF_INFO info;
int err;
while (updateResult == eProgressSuccess) {
sampleCount samplesWritten;
sampleCount numSamples = mixer->Process(maxBlockLen);
//This whole operation should not occur while a file is being loaded on OD,
//(we are worried about reading from a file being written to,) so we block.
//Furthermore, we need to do this because libsndfile is not threadsafe.
formatStr = SFCall<wxString>(sf_header_name, sf_format & SF_FORMAT_TYPEMASK);
if (numSamples == 0)
break;
// Use libsndfile to export file
samplePtr mixed = mixer->GetBuffer();
info.samplerate = (unsigned int)(rate + 0.5);
info.frames = (unsigned int)((t1 - t0)*rate + 0.5);
info.channels = numChannels;
info.format = sf_format;
info.sections = 1;
info.seekable = 0;
ODManager::LockLibSndFileMutex();
if (format == int16Sample)
samplesWritten = sf_writef_short(sf, (short *)mixed, numSamples);
else
samplesWritten = sf_writef_float(sf, (float *)mixed, numSamples);
ODManager::UnlockLibSndFileMutex();
if (samplesWritten != numSamples) {
char buffer2[1000];
sf_error_str(sf, buffer2, 1000);
wxMessageBox(wxString::Format(
/* i18n-hint: %s will be the error message from libsndfile, which
* is usually something unhelpful (and untranslated) like "system
* error" */
_("Error while writing %s file (disk full?).\nLibsndfile says \"%s\""),
formatStr.c_str(),
wxString::FromAscii(buffer2).c_str()));
break;
}
updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0);
}
}
// Install the WAV metata in a "LIST" chunk at the end of the file
if ((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV ||
(sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAVEX) {
if (!AddStrings(project, sf, metadata, sf_format)) {
sf_close(sf);
// If we can't export exactly the format they requested,
// try the default format for that header type...
if (!sf_format_check(&info))
info.format = (info.format & SF_FORMAT_TYPEMASK);
if (!sf_format_check(&info)) {
wxMessageBox(_("Cannot export audio in this format."));
return false;
}
}
ODManager::LockLibSndFileMutex();
err = sf_close(sf);
ODManager::UnlockLibSndFileMutex();
if (f.Open(fName, wxFile::write)) {
// Even though there is an sf_open() that takes a filename, use the one that
// takes a file descriptor since wxWidgets can open a file with a Unicode name and
// libsndfile can't (under Windows).
sf.reset(SFCall<SNDFILE*>(sf_open_fd, f.fd(), SFM_WRITE, &info, FALSE));
//add clipping for integer formats. We allow floats to clip.
sf_command(sf.get(), SFC_SET_CLIPPING, NULL, sf_subtype_is_integer(sf_format)?SF_TRUE:SF_FALSE) ;
}
if (err) {
char buffer[1000];
sf_error_str(sf, buffer, 1000);
wxMessageBox(wxString::Format
/* i18n-hint: %s will be the error message from libsndfile */
(_("Error (file may not have been written): %s"),
buffer));
if (!sf) {
wxMessageBox(wxString::Format(_("Cannot export audio to %s"),
fName.c_str()));
return false;
}
// Retrieve tags if not given a set
if (metadata == NULL)
metadata = project->GetTags();
// Install the metata at the beginning of the file (except for
// WAV and WAVEX formats)
if ((sf_format & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAV &&
(sf_format & SF_FORMAT_TYPEMASK) != SF_FORMAT_WAVEX) {
if (!AddStrings(project, sf.get(), metadata, sf_format)) {
return false;
}
}
sampleFormat format;
if (sf_subtype_more_than_16_bits(info.format))
format = floatSample;
else
format = int16Sample;
int maxBlockLen = 44100 * 5;
const WaveTrackConstArray waveTracks =
tracks->GetWaveTrackConstArray(selectionOnly, false);
{
auto mixer = CreateMixer(waveTracks,
tracks->GetTimeTrack(),
t0, t1,
info.channels, maxBlockLen, true,
rate, format, true, mixerSpec);
ProgressDialog progress(wxFileName(fName).GetName(),
selectionOnly ?
wxString::Format(_("Exporting the selected audio as %s"),
formatStr.c_str()) :
wxString::Format(_("Exporting the entire project as %s"),
formatStr.c_str()));
while (updateResult == eProgressSuccess) {
sampleCount samplesWritten;
sampleCount numSamples = mixer->Process(maxBlockLen);
if (numSamples == 0)
break;
samplePtr mixed = mixer->GetBuffer();
if (format == int16Sample)
samplesWritten = SFCall<sf_count_t>(sf_writef_short, sf.get(), (short *)mixed, numSamples);
else
samplesWritten = SFCall<sf_count_t>(sf_writef_float, sf.get(), (float *)mixed, numSamples);
if (samplesWritten != numSamples) {
char buffer2[1000];
sf_error_str(sf.get(), buffer2, 1000);
wxMessageBox(wxString::Format(
/* i18n-hint: %s will be the error message from libsndfile, which
* is usually something unhelpful (and untranslated) like "system
* error" */
_("Error while writing %s file (disk full?).\nLibsndfile says \"%s\""),
formatStr.c_str(),
wxString::FromAscii(buffer2).c_str()));
break;
}
updateResult = progress.Update(mixer->MixGetCurrentTime() - t0, t1 - t0);
}
}
// Install the WAV metata in a "LIST" chunk at the end of the file
if ((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV ||
(sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAVEX) {
if (!AddStrings(project, sf.get(), metadata, sf_format)) {
return false;
}
}
}
if (((sf_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) ||