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:
@@ -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) ||
|
||||
|
||||
Reference in New Issue
Block a user