mirror of
https://github.com/cookiengineer/audacity
synced 2026-02-09 05:01:57 +01:00
Locate and position the current Audacity source code, and clear a variety of old junk out of the way into junk-branches
This commit is contained in:
795
src/export/ExportPCM.cpp
Normal file
795
src/export/ExportPCM.cpp
Normal file
@@ -0,0 +1,795 @@
|
||||
/**********************************************************************
|
||||
|
||||
Audacity: A Digital Audio Editor
|
||||
|
||||
ExportPCM.cpp
|
||||
|
||||
Dominic Mazzoni
|
||||
|
||||
**********************************************************************/
|
||||
|
||||
#include <wx/defs.h>
|
||||
|
||||
#include <wx/choice.h>
|
||||
#include <wx/dynlib.h>
|
||||
#include <wx/filename.h>
|
||||
#include <wx/intl.h>
|
||||
#include <wx/timer.h>
|
||||
#include <wx/msgdlg.h>
|
||||
#include <wx/progdlg.h>
|
||||
#include <wx/string.h>
|
||||
#include <wx/textctrl.h>
|
||||
#include <wx/window.h>
|
||||
|
||||
#include "sndfile.h"
|
||||
|
||||
#include "../Audacity.h"
|
||||
#include "../FileFormats.h"
|
||||
#include "../Internat.h"
|
||||
#include "../LabelTrack.h"
|
||||
#include "../Mix.h"
|
||||
#include "../Prefs.h"
|
||||
#include "../Project.h"
|
||||
#include "../Tags.h"
|
||||
#include "../Track.h"
|
||||
#include "../WaveTrack.h"
|
||||
#include "../ondemand/ODManager.h"
|
||||
|
||||
#include "Export.h"
|
||||
|
||||
#ifdef USE_LIBID3TAG
|
||||
#include <id3tag.h>
|
||||
// DM: the following functions were supposed to have been
|
||||
// included in id3tag.h - should be fixed in the next release
|
||||
// of mad.
|
||||
extern "C" {
|
||||
struct id3_frame *id3_frame_new(char const *);
|
||||
id3_length_t id3_latin1_length(id3_latin1_t const *);
|
||||
void id3_latin1_decode(id3_latin1_t const *, id3_ucs4_t *);
|
||||
}
|
||||
#endif
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Statics
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
static int ReadExportFormatPref()
|
||||
{
|
||||
#if defined(__WXMAC__)
|
||||
return gPrefs->Read(wxT("/FileFormats/ExportFormat_SF1"),
|
||||
(long int)(SF_FORMAT_AIFF | SF_FORMAT_PCM_16));
|
||||
#else
|
||||
return gPrefs->Read(wxT("/FileFormats/ExportFormat_SF1"),
|
||||
(long int)(SF_FORMAT_WAV | SF_FORMAT_PCM_16));
|
||||
#endif
|
||||
}
|
||||
|
||||
static void WriteExportFormatPref(int format)
|
||||
{
|
||||
gPrefs->Write(wxT("/FileFormats/ExportFormat_SF1"), (long int)format);
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// ExportPCMOptions Class
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
#define ID_FORMAT_CHOICE 7101
|
||||
#define ID_HEADER_CHOICE 7102
|
||||
#define ID_ENCODING_CHOICE 7103
|
||||
|
||||
class ExportPCMOptions : public wxDialog
|
||||
{
|
||||
public:
|
||||
|
||||
ExportPCMOptions(wxWindow *parent, int format);
|
||||
void PopulateOrExchange(ShuttleGui & S);
|
||||
void OnHeaderChoice(wxCommandEvent & evt);
|
||||
void OnChoice(wxCommandEvent & event);
|
||||
void OnOK(wxCommandEvent& event);
|
||||
|
||||
private:
|
||||
|
||||
bool ValidatePair(int format);
|
||||
int GetFormat();
|
||||
|
||||
private:
|
||||
|
||||
wxArrayString mHeaderNames;
|
||||
wxArrayString mEncodingNames;
|
||||
wxChoice *mHeaderChoice;
|
||||
wxChoice *mEncodingChoice;
|
||||
wxButton *mOk;
|
||||
int mHeaderFromChoice;
|
||||
int mEncodingFromChoice;
|
||||
wxArrayInt mEncodingFormats;
|
||||
|
||||
DECLARE_EVENT_TABLE()
|
||||
};
|
||||
|
||||
BEGIN_EVENT_TABLE(ExportPCMOptions, wxDialog)
|
||||
EVT_CHOICE(ID_HEADER_CHOICE, ExportPCMOptions::OnHeaderChoice)
|
||||
EVT_CHOICE(ID_ENCODING_CHOICE, ExportPCMOptions::OnChoice)
|
||||
EVT_BUTTON(wxID_OK, ExportPCMOptions::OnOK)
|
||||
END_EVENT_TABLE()
|
||||
|
||||
///
|
||||
///
|
||||
ExportPCMOptions::ExportPCMOptions(wxWindow *parent, int selformat)
|
||||
: wxDialog(NULL, wxID_ANY,
|
||||
wxString(_("Specify Uncompressed Options")))
|
||||
{
|
||||
mOk = NULL;
|
||||
int format = 0;
|
||||
switch (selformat)
|
||||
{
|
||||
case 0: // other uncompressed
|
||||
format = ReadExportFormatPref();
|
||||
break;
|
||||
case 1: // 16-bit AIFF
|
||||
format = SF_FORMAT_AIFF | SF_FORMAT_PCM_16;
|
||||
break;
|
||||
case 2: // 16-bit WAV
|
||||
format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
|
||||
break;
|
||||
case 3: // GSM WAV
|
||||
format = SF_FORMAT_WAV | SF_FORMAT_GSM610;
|
||||
break;
|
||||
default: // bug - forgot to extend this case statement
|
||||
wxASSERT(false);
|
||||
format = ReadExportFormatPref();
|
||||
break;
|
||||
}
|
||||
int i;
|
||||
int num;
|
||||
int sel;
|
||||
|
||||
num = sf_num_headers();
|
||||
sel = 0;
|
||||
for (i = 0; i < num; i++) {
|
||||
mHeaderNames.Add(sf_header_index_name(i));
|
||||
if ((format & SF_FORMAT_TYPEMASK) == sf_header_index_to_type(i)) {
|
||||
sel = i;
|
||||
}
|
||||
}
|
||||
mHeaderFromChoice = sel;
|
||||
|
||||
mEncodingFormats.Clear();
|
||||
num = sf_num_encodings();
|
||||
mEncodingFromChoice = sel = 0;
|
||||
for (i = 0; i < num; i++) {
|
||||
int enc = sf_encoding_index_to_subtype(i);
|
||||
int fmt = (format & SF_FORMAT_TYPEMASK) | enc;
|
||||
bool valid = ValidatePair(fmt);
|
||||
if (valid)
|
||||
{
|
||||
|
||||
mEncodingNames.Add(sf_encoding_index_name(i));
|
||||
mEncodingFormats.Add(enc);
|
||||
if ((format & SF_FORMAT_SUBMASK) == sf_encoding_index_to_subtype(i)) {
|
||||
mEncodingFromChoice = sel;
|
||||
}
|
||||
else sel++;
|
||||
}
|
||||
}
|
||||
|
||||
ShuttleGui S(this, eIsCreatingFromPrefs);
|
||||
|
||||
PopulateOrExchange(S);
|
||||
|
||||
Layout();
|
||||
Fit();
|
||||
Center();
|
||||
}
|
||||
///
|
||||
///
|
||||
void ExportPCMOptions::PopulateOrExchange(ShuttleGui & S)
|
||||
{
|
||||
S.StartHorizontalLay(wxEXPAND, true);
|
||||
{
|
||||
S.StartStatic(_("Uncompressed Export Setup"), true);
|
||||
{
|
||||
S.StartMultiColumn(2, wxEXPAND);
|
||||
{
|
||||
S.SetStretchyCol(1);
|
||||
mHeaderChoice = S.Id(ID_HEADER_CHOICE)
|
||||
.AddChoice(_("Header:"),
|
||||
mHeaderNames[mHeaderFromChoice],
|
||||
&mHeaderNames);
|
||||
mEncodingChoice = S.Id(ID_ENCODING_CHOICE)
|
||||
.AddChoice(_("Encoding:"),
|
||||
mEncodingNames[mEncodingFromChoice],
|
||||
&mEncodingNames);
|
||||
}
|
||||
S.EndMultiColumn();
|
||||
S.AddFixedText(_("(Not all combinations of headers and encodings are possible.)"));
|
||||
}
|
||||
S.EndStatic();
|
||||
}
|
||||
S.EndHorizontalLay();
|
||||
|
||||
S.AddStandardButtons();
|
||||
mOk = (wxButton *)wxWindow::FindWindowById(wxID_OK, this);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
void ExportPCMOptions::OnHeaderChoice(wxCommandEvent & evt)
|
||||
{
|
||||
int format = sf_header_index_to_type(mHeaderChoice->GetSelection());
|
||||
mEncodingNames.Clear();
|
||||
mEncodingChoice->Clear();
|
||||
mEncodingFormats.Clear();
|
||||
int sel = wxNOT_FOUND;
|
||||
int i,j;
|
||||
|
||||
int sfnum = sf_num_simple_formats();
|
||||
wxArrayInt sfs;
|
||||
|
||||
for (i = 0; i < sfnum; i++)
|
||||
{
|
||||
SF_FORMAT_INFO *fi = sf_simple_format(i);
|
||||
sfs.Add(fi->format);
|
||||
}
|
||||
|
||||
int num = sf_num_encodings();
|
||||
for (i = 0; i < num; i++)
|
||||
{
|
||||
int enc = sf_encoding_index_to_subtype(i);
|
||||
int fmt = format | enc;
|
||||
bool valid = ValidatePair(fmt);
|
||||
if (valid)
|
||||
{
|
||||
mEncodingNames.Add(sf_encoding_index_name(i));
|
||||
mEncodingChoice->Append(sf_encoding_index_name(i));
|
||||
mEncodingFormats.Add(enc);
|
||||
for (j = 0; j < sfnum; j++)
|
||||
{
|
||||
int enc = sfs[j];
|
||||
if ((sel == wxNOT_FOUND) && (fmt == enc))
|
||||
{
|
||||
sel = mEncodingFormats.GetCount()-1;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (sel == wxNOT_FOUND) sel = 0;
|
||||
mEncodingFromChoice = sel;
|
||||
mEncodingChoice->SetSelection(sel);
|
||||
ValidatePair(GetFormat());
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
void ExportPCMOptions::OnChoice(wxCommandEvent & event)
|
||||
{
|
||||
}
|
||||
|
||||
///
|
||||
///
|
||||
void ExportPCMOptions::OnOK(wxCommandEvent& event)
|
||||
{
|
||||
WriteExportFormatPref(GetFormat());
|
||||
|
||||
EndModal(wxID_OK);
|
||||
|
||||
return;
|
||||
}
|
||||
|
||||
int ExportPCMOptions::GetFormat()
|
||||
{
|
||||
int hdr = sf_header_index_to_type(mHeaderChoice->GetSelection());
|
||||
int sel = mEncodingChoice->GetSelection();
|
||||
int enc = mEncodingFormats[sel];
|
||||
return hdr | enc;
|
||||
}
|
||||
|
||||
/// Calls a libsndfile library function to determine whether the user's
|
||||
/// choice of sample encoding (e.g. pcm 16-bit or GSM 6.10 compression)
|
||||
/// is compatible with their choice of file format (e.g. WAV, AIFF)
|
||||
/// and enables/disables the OK button accordingly.
|
||||
bool ExportPCMOptions::ValidatePair(int format)
|
||||
{
|
||||
SF_INFO info;
|
||||
memset(&info, 0, sizeof(info));
|
||||
info.frames = 0;
|
||||
info.samplerate = 44100;
|
||||
info.channels = 1;
|
||||
info.format = format;
|
||||
info.sections = 1;
|
||||
info.seekable = 0;
|
||||
|
||||
int valid = sf_format_check(&info);
|
||||
if (mOk)
|
||||
mOk->Enable(valid != 0 ? true : false);
|
||||
return valid != 0 ? true : false;
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// ExportPCM Class
|
||||
//----------------------------------------------------------------------------
|
||||
|
||||
class ExportPCM : public ExportPlugin
|
||||
{
|
||||
public:
|
||||
|
||||
ExportPCM();
|
||||
void Destroy();
|
||||
|
||||
// Required
|
||||
|
||||
bool DisplayOptions(wxWindow *parent, int format = 0);
|
||||
int Export(AudacityProject *project,
|
||||
int channels,
|
||||
wxString fName,
|
||||
bool selectedOnly,
|
||||
double t0,
|
||||
double t1,
|
||||
MixerSpec *mixerSpec = NULL,
|
||||
Tags *metadata = NULL,
|
||||
int subformat = 0);
|
||||
// optional
|
||||
wxString GetExtension(int index = 0);
|
||||
private:
|
||||
|
||||
bool AddStrings(AudacityProject *project, SNDFILE *sf, Tags *tags);
|
||||
void AddID3Chunk(wxString fName, Tags *tags);
|
||||
|
||||
};
|
||||
|
||||
ExportPCM::ExportPCM()
|
||||
: ExportPlugin()
|
||||
{
|
||||
|
||||
SF_INFO si;
|
||||
|
||||
si.samplerate = 0;
|
||||
si.channels = 0;
|
||||
|
||||
int format; // the index of the format we are setting up at the moment
|
||||
|
||||
/* add default user controlled format to list of formats this plug-in does
|
||||
* at position 0 */
|
||||
format = AddFormat() - 1; // store the index = 1 less than the count
|
||||
SetFormat(wxT("LIBSNDFILE"),format);
|
||||
SetCanMetaData(true,format);
|
||||
SetDescription(_("Other uncompressed files"),format);
|
||||
wxArrayString allext = sf_get_all_extensions();
|
||||
wxString wavext = sf_header_extension(SF_FORMAT_WAV); // get WAV ext.
|
||||
#if defined(wxMSW)
|
||||
// On Windows make sure WAV is at the beginning of the list of all possible
|
||||
// extensions for this format
|
||||
allext.Remove(wavext);
|
||||
allext.Insert(wavext,0);
|
||||
#endif
|
||||
SetExtensions(allext,format);
|
||||
SetMaxChannels(255,format);
|
||||
|
||||
/* add AIFF 16-bit to list of sub-formats for this plug-in at position 1 */
|
||||
si.format = SF_FORMAT_AIFF | SF_FORMAT_PCM_16;
|
||||
for (si.channels = 1; sf_format_check(&si); si.channels++){};
|
||||
format = AddFormat() - 1;
|
||||
SetFormat(wxT("AIFF"),format);
|
||||
SetCanMetaData(true,format);
|
||||
SetDescription(_("AIFF (Apple) signed 16 bit PCM"),format);
|
||||
wxString aiffext = sf_header_extension(si.format);
|
||||
AddExtension(aiffext,format);
|
||||
SetMaxChannels(si.channels - 1,format);
|
||||
|
||||
/* add WAV 16-bit at position 2 */
|
||||
si.format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
|
||||
for (si.channels = 1; sf_format_check(&si); si.channels++){};
|
||||
format = AddFormat() - 1;
|
||||
SetFormat(wxT("WAV"),format);
|
||||
SetCanMetaData(true,format);
|
||||
SetDescription(_("WAV (Microsoft) signed 16 bit PCM"),format);
|
||||
// we sorted out wavext near the begining
|
||||
AddExtension(wavext,format);
|
||||
SetMaxChannels(si.channels - 1,format);
|
||||
|
||||
/* add GSM6.10 to list of formats at position 3 */
|
||||
si.format = SF_FORMAT_WAV | SF_FORMAT_GSM610;
|
||||
for (si.channels = 1; sf_format_check(&si); si.channels++){};
|
||||
format = AddFormat() - 1;
|
||||
SetFormat(wxT("GSM610"),format);
|
||||
SetCanMetaData(true,format);
|
||||
SetDescription(_("GSM 6.10 WAV (mobile)"),format);
|
||||
AddExtension(sf_header_extension(si.format),format);
|
||||
SetMaxChannels(si.channels - 1,format);
|
||||
}
|
||||
|
||||
void ExportPCM::Destroy()
|
||||
{
|
||||
delete this;
|
||||
}
|
||||
|
||||
/**
|
||||
*
|
||||
* @param subformat Control whether we are doing a "preset" export to a popular
|
||||
* file type, or giving the user full control over libsndfile. Set to 0
|
||||
* (default) gives full control, 1 gives 16-bit AIFF, 2 gives 16-bit WAV
|
||||
* 3 gives a GSM 6.10 WAV file */
|
||||
int ExportPCM::Export(AudacityProject *project,
|
||||
int numChannels,
|
||||
wxString fName,
|
||||
bool selectionOnly,
|
||||
double t0,
|
||||
double t1,
|
||||
MixerSpec *mixerSpec,
|
||||
Tags *metadata,
|
||||
int subformat)
|
||||
{
|
||||
double rate = project->GetRate();
|
||||
TrackList *tracks = project->GetTracks();
|
||||
int sf_format;
|
||||
switch (subformat)
|
||||
{
|
||||
case 0: // other uncompressed
|
||||
sf_format = ReadExportFormatPref();
|
||||
break;
|
||||
case 1: // AIFF
|
||||
sf_format = SF_FORMAT_AIFF | SF_FORMAT_PCM_16;
|
||||
break;
|
||||
case 2: // WAV
|
||||
sf_format = SF_FORMAT_WAV | SF_FORMAT_PCM_16;
|
||||
break;
|
||||
case 3:
|
||||
sf_format = SF_FORMAT_WAV | SF_FORMAT_GSM610;
|
||||
break;
|
||||
default: // land here if supplied a sub-format that we don't know about
|
||||
wxASSERT(false); // raise assertion - this is a bug
|
||||
sf_format = ReadExportFormatPref(); // treat it like 0 so users get a
|
||||
// working result
|
||||
break;
|
||||
}
|
||||
|
||||
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);
|
||||
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();
|
||||
|
||||
if (!AddStrings(project, sf, metadata)) { // meta data presence check
|
||||
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;
|
||||
|
||||
int numWaveTracks;
|
||||
WaveTrack **waveTracks;
|
||||
tracks->GetWaveTracks(selectionOnly, &numWaveTracks, &waveTracks);
|
||||
Mixer *mixer = new Mixer(numWaveTracks, waveTracks,
|
||||
tracks->GetTimeTrack(),
|
||||
t0, t1,
|
||||
info.channels, maxBlockLen, true,
|
||||
rate, format, true, mixerSpec);
|
||||
|
||||
ProgressDialog *progress = new ProgressDialog(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 numSamples = mixer->Process(maxBlockLen);
|
||||
|
||||
if (numSamples == 0)
|
||||
break;
|
||||
|
||||
samplePtr mixed = mixer->GetBuffer();
|
||||
|
||||
ODManager::LockLibSndFileMutex();
|
||||
if (format == int16Sample)
|
||||
sf_writef_short(sf, (short *)mixed, numSamples);
|
||||
else
|
||||
sf_writef_float(sf, (float *)mixed, numSamples);
|
||||
ODManager::UnlockLibSndFileMutex();
|
||||
|
||||
updateResult = progress->Update(mixer->MixGetCurrentTime()-t0, t1-t0);
|
||||
}
|
||||
|
||||
delete progress;
|
||||
|
||||
delete mixer;
|
||||
|
||||
delete[] waveTracks;
|
||||
|
||||
ODManager::LockLibSndFileMutex();
|
||||
err = sf_close(sf);
|
||||
ODManager::UnlockLibSndFileMutex();
|
||||
|
||||
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_format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) {
|
||||
AddID3Chunk(fName, metadata);
|
||||
}
|
||||
|
||||
#ifdef __WXMAC__
|
||||
wxFileName fn(fName);
|
||||
fn.MacSetTypeAndCreator(sf_header_mactype(sf_format & SF_FORMAT_TYPEMASK),
|
||||
AUDACITY_CREATOR);
|
||||
#endif
|
||||
|
||||
return updateResult;
|
||||
}
|
||||
|
||||
bool ExportPCM::AddStrings(AudacityProject *project, SNDFILE *sf, Tags *tags)
|
||||
{
|
||||
if (tags->HasTag(TAG_TITLE)) {
|
||||
sf_set_string(sf, SF_STR_TITLE, tags->GetTag(TAG_TITLE).mb_str(wxConvUTF8));
|
||||
}
|
||||
|
||||
if (tags->HasTag(TAG_ARTIST)) {
|
||||
sf_set_string(sf, SF_STR_ARTIST, tags->GetTag(TAG_ARTIST).mb_str(wxConvUTF8));
|
||||
}
|
||||
|
||||
if (tags->HasTag(TAG_COMMENTS)) {
|
||||
sf_set_string(sf, SF_STR_COMMENT, tags->GetTag(TAG_COMMENTS).mb_str(wxConvUTF8));
|
||||
}
|
||||
|
||||
if (tags->HasTag(TAG_YEAR)) {
|
||||
sf_set_string(sf, SF_STR_DATE, tags->GetTag(TAG_YEAR).mb_str(wxConvUTF8));
|
||||
}
|
||||
|
||||
if (tags->HasTag(wxT("Copyright"))) {
|
||||
sf_set_string(sf, SF_STR_COPYRIGHT, tags->GetTag(wxT("Copyright")).mb_str(wxConvUTF8));
|
||||
}
|
||||
|
||||
if (tags->HasTag(wxT("Software"))) {
|
||||
sf_set_string(sf, SF_STR_SOFTWARE, tags->GetTag(wxT("Software")).mb_str(wxConvUTF8));
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
void ExportPCM::AddID3Chunk(wxString fName, Tags *tags)
|
||||
{
|
||||
#ifdef USE_LIBID3TAG
|
||||
|
||||
struct id3_tag *tp = id3_tag_new();
|
||||
|
||||
wxString n, v;
|
||||
for (bool cont = tags->GetFirst(n, v); cont; cont = tags->GetNext(n, v)) {
|
||||
const char *name = "TXXX";
|
||||
|
||||
if (n.CmpNoCase(TAG_TITLE) == 0) {
|
||||
name = ID3_FRAME_TITLE;
|
||||
}
|
||||
else if (n.CmpNoCase(TAG_ARTIST) == 0) {
|
||||
name = ID3_FRAME_ARTIST;
|
||||
}
|
||||
else if (n.CmpNoCase(TAG_ALBUM) == 0) {
|
||||
name = ID3_FRAME_ALBUM;
|
||||
}
|
||||
else if (n.CmpNoCase(TAG_YEAR) == 0) {
|
||||
name = ID3_FRAME_YEAR;
|
||||
}
|
||||
else if (n.CmpNoCase(TAG_GENRE) == 0) {
|
||||
name = ID3_FRAME_GENRE;
|
||||
}
|
||||
else if (n.CmpNoCase(TAG_COMMENTS) == 0) {
|
||||
name = ID3_FRAME_COMMENT;
|
||||
}
|
||||
else if (n.CmpNoCase(TAG_TRACK) == 0) {
|
||||
name = ID3_FRAME_TRACK;
|
||||
}
|
||||
|
||||
struct id3_frame *frame = id3_frame_new(name);
|
||||
|
||||
if (!n.IsAscii() || !v.IsAscii()) {
|
||||
id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_UTF_16);
|
||||
}
|
||||
else {
|
||||
id3_field_settextencoding(id3_frame_field(frame, 0), ID3_FIELD_TEXTENCODING_ISO_8859_1);
|
||||
}
|
||||
|
||||
id3_ucs4_t *ucs4 =
|
||||
id3_utf8_ucs4duplicate((id3_utf8_t *) (const char *) v.mb_str(wxConvUTF8));
|
||||
|
||||
if (strcmp(name, ID3_FRAME_COMMENT) == 0) {
|
||||
// A hack to get around iTunes not recognizing the comment. The
|
||||
// language defaults to XXX and, since it's not a valid language,
|
||||
// iTunes just ignores the tag. So, either set it to a valid language
|
||||
// (which one???) or just clear it. Unfortunately, there's no supported
|
||||
// way of clearing the field, so do it directly.
|
||||
id3_field *f = id3_frame_field(frame, 1);
|
||||
memset(f->immediate.value, 0, sizeof(f->immediate.value));
|
||||
id3_field_setfullstring(id3_frame_field(frame, 3), ucs4);
|
||||
}
|
||||
else if (strcmp(name, "TXXX") == 0) {
|
||||
id3_field_setstring(id3_frame_field(frame, 2), ucs4);
|
||||
free(ucs4);
|
||||
|
||||
ucs4 = id3_utf8_ucs4duplicate((id3_utf8_t *) (const char *) n.mb_str(wxConvUTF8));
|
||||
|
||||
id3_field_setstring(id3_frame_field(frame, 1), ucs4);
|
||||
}
|
||||
else {
|
||||
id3_field_setstrings(id3_frame_field(frame, 1), 1, &ucs4);
|
||||
}
|
||||
|
||||
free(ucs4);
|
||||
|
||||
id3_tag_attachframe(tp, frame);
|
||||
}
|
||||
|
||||
tp->options &= (~ID3_TAG_OPTION_COMPRESSION); // No compression
|
||||
|
||||
// If this version of libid3tag supports it, use v2.3 ID3
|
||||
// tags instead of the newer, but less well supported, v2.4
|
||||
// that libid3tag uses by default.
|
||||
#ifdef ID3_TAG_HAS_TAG_OPTION_ID3V2_3
|
||||
tp->options |= ID3_TAG_OPTION_ID3V2_3;
|
||||
#endif
|
||||
|
||||
id3_length_t len;
|
||||
|
||||
len = id3_tag_render(tp, 0);
|
||||
if (len == 0) {
|
||||
id3_tag_delete(tp);
|
||||
return;
|
||||
}
|
||||
|
||||
id3_byte_t *buffer = (id3_byte_t *)malloc(len);
|
||||
if (buffer == NULL) {
|
||||
id3_tag_delete(tp);
|
||||
return;
|
||||
}
|
||||
|
||||
len = id3_tag_render(tp, buffer);
|
||||
|
||||
id3_tag_delete(tp);
|
||||
|
||||
wxFFile f(fName, wxT("r+b"));
|
||||
if (f.IsOpened()) {
|
||||
wxUint32 sz;
|
||||
|
||||
sz = wxUINT32_SWAP_ON_LE((wxUint32) len);
|
||||
f.SeekEnd(0);
|
||||
f.Write("ID3 ", 4);
|
||||
f.Write(&sz, 4);
|
||||
|
||||
f.Write(buffer, len + (len & 0x01));
|
||||
|
||||
sz = wxUINT32_SWAP_ON_LE((wxUint32) f.Tell() - 8);
|
||||
f.Seek(4);
|
||||
f.Write(&sz, 4);
|
||||
|
||||
f.Close();
|
||||
}
|
||||
|
||||
free(buffer);
|
||||
#endif
|
||||
return;
|
||||
}
|
||||
|
||||
/** @param format The same information as the subformat argument to the Export
|
||||
* method. Controls use of pre-defined export settings.*/
|
||||
bool ExportPCM::DisplayOptions(wxWindow *parent, int format)
|
||||
{
|
||||
wxString nopt(_("There are no options for this format.\n"));
|
||||
/* i18n-hint: This is pointing users at another possible export format in
|
||||
* the list. So you should translate the quoted string
|
||||
* 'Other uncompressed files' exactly the same as you do the same string
|
||||
* when it comes up on it's own.*/
|
||||
wxString usepcm(_("If you need more control over the export format please use the 'Other uncompressed files' format."));
|
||||
|
||||
/* actual code - decide what options if any are useful to show */
|
||||
if(format == 1)
|
||||
{ // 16-bit AIFF
|
||||
wxMessageBox(nopt + _("Your file will be exported as a 16-bit AIFF (Apple/SGI) file.\n") + usepcm);
|
||||
return true;
|
||||
}
|
||||
else if (format == 2)
|
||||
{ // 16-bit WAV
|
||||
wxMessageBox(nopt + _("Your file will be exported as a 16-bit WAV (Microsoft) file.\n") + usepcm);
|
||||
return true;
|
||||
}
|
||||
else if (format == 3)
|
||||
{ // GSM WAV
|
||||
wxMessageBox(nopt + _("Your file will be exported as a GSM 6.10 WAV file.\n") + usepcm);
|
||||
return true;
|
||||
}
|
||||
|
||||
// default, full user control
|
||||
ExportPCMOptions od(parent,format);
|
||||
od.ShowModal();
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
wxString ExportPCM::GetExtension(int index)
|
||||
{
|
||||
if (index == 0) {
|
||||
// get extension libsndfile thinks is correct for currently selected format
|
||||
return sf_header_extension(ReadExportFormatPref());
|
||||
}
|
||||
else {
|
||||
// return the default
|
||||
return ExportPlugin::GetExtension(index);
|
||||
}
|
||||
}
|
||||
|
||||
//----------------------------------------------------------------------------
|
||||
// Constructor
|
||||
//----------------------------------------------------------------------------
|
||||
ExportPlugin *New_ExportPCM()
|
||||
{
|
||||
return new ExportPCM();
|
||||
}
|
||||
|
||||
// Indentation settings for Vim and Emacs and unique identifier for Arch, a
|
||||
// version control system. Please do not modify past this point.
|
||||
//
|
||||
// Local Variables:
|
||||
// c-basic-offset: 3
|
||||
// indent-tabs-mode: nil
|
||||
// End:
|
||||
//
|
||||
// vim: et sts=3 sw=3
|
||||
// arch-tag: c1f32472-520f-4864-8086-3dba0d593e84
|
||||
Reference in New Issue
Block a user