1
0
mirror of https://github.com/cookiengineer/audacity synced 2025-10-13 14:13:32 +02: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:
ra
2010-01-23 19:44:49 +00:00
commit e74978ba77
1011 changed files with 781704 additions and 0 deletions

413
src/import/Import.cpp Normal file
View File

@@ -0,0 +1,413 @@
/**********************************************************************
Audacity: A Digital Audio Editor
Import.cpp
Dominic Mazzoni
*******************************************************************//**
\file Import.cpp
This file contains a general function which will import almost
any type of sampled audio file (i.e. anything except MIDI)
and return the tracks that were imported. This function just
figures out which one to call; the actual importers are in
ImportPCM, ImportMP3, ImportOGG, ImportRawData, ImportLOF,
ImportQT and ImportFLAC.
*//***************************************************************//**
\class Format
\brief Abstract base class used in importing a file.
It's defined in Import.h
*//***************************************************************//**
\class Importer
\brief Class which actulaly imports the auido, using functions defined
in ImportPCM.cpp, ImportMP3.cpp, ImportOGG.cpp, ImportRawData.cpp,
and ImportLOF.cpp.
*//******************************************************************/
#include <wx/textctrl.h>
#include <wx/msgdlg.h>
#include <wx/string.h>
#include <wx/intl.h>
#include <wx/listimpl.cpp>
#include <wx/log.h>
#include <wx/sizer.h> //for wxBoxSizer
#include "../ShuttleGui.h"
#include "../Audacity.h"
#include "Import.h"
#include "ImportPlugin.h"
#include "ImportPCM.h"
#include "ImportMP3.h"
#include "ImportOGG.h"
#include "ImportQT.h"
#include "ImportRaw.h"
#include "ImportLOF.h"
#include "ImportFLAC.h"
#include "ImportFFmpeg.h"
#include "ImportGStreamer.h"
#include "../Track.h"
#include "../Prefs.h"
WX_DEFINE_LIST(ImportPluginList);
WX_DEFINE_LIST(UnusableImportPluginList);
WX_DEFINE_LIST(FormatList);
Importer::Importer()
{
mImportPluginList = new ImportPluginList;
mUnusableImportPluginList = new UnusableImportPluginList;
// build the list of import plugin and/or unusableImporters.
// order is significant. If none match, they will all be tried
// in the order defined here.
GetPCMImportPlugin(mImportPluginList, mUnusableImportPluginList);
GetOGGImportPlugin(mImportPluginList, mUnusableImportPluginList);
GetFLACImportPlugin(mImportPluginList, mUnusableImportPluginList);
GetMP3ImportPlugin(mImportPluginList, mUnusableImportPluginList);
GetLOFImportPlugin(mImportPluginList, mUnusableImportPluginList);
#ifdef USE_QUICKTIME
GetQTImportPlugin(mImportPluginList, mUnusableImportPluginList);
#endif
#if defined(USE_FFMPEG)
GetFFmpegImportPlugin(mImportPluginList, mUnusableImportPluginList);
#endif
#if defined(USE_GSTREAMER)
GetGStreamerImportPlugin(mImportPluginList, mUnusableImportPluginList);
#endif
}
Importer::~Importer()
{
mImportPluginList->DeleteContents(true);
delete mImportPluginList;
mUnusableImportPluginList->DeleteContents(true);//JKC
delete mUnusableImportPluginList;
}
void Importer::GetSupportedImportFormats(FormatList *formatList)
{
ImportPluginList::compatibility_iterator importPluginNode = mImportPluginList->GetFirst();
while(importPluginNode)
{
ImportPlugin *importPlugin = importPluginNode->GetData();
formatList->Append(new Format(importPlugin->GetPluginFormatDescription(),
importPlugin->GetSupportedExtensions()));
importPluginNode = importPluginNode->GetNext();
}
}
// returns number of tracks imported
int Importer::Import(wxString fName,
TrackFactory *trackFactory,
Track *** tracks,
Tags *tags,
wxString &errorMessage)
{
ImportFileHandle *inFile = NULL;
int numTracks = 0;
wxString extension = fName.AfterLast(wxT('.'));
// This list is used to call plugins in correct order
ImportPluginList importPlugins;
ImportPluginList::compatibility_iterator importPluginNode;
// This list is used to remember plugins that should have been compatible with the file.
ImportPluginList compatiblePlugins;
// If user explicitly selected a filter,
// then we should try importing via corresponding plugin first
wxString type = gPrefs->Read(wxT("/LastOpenType"),wxT(""));
gPrefs->Write(wxT("/LastOpenType"),wxT(""));
// First, add all explicitly compatible plugins
importPluginNode = mImportPluginList->GetFirst();
while(importPluginNode)
{
ImportPlugin *plugin = importPluginNode->GetData();
if (plugin->GetPluginFormatDescription().CompareTo(type) == 0)
{
// This plugin corresponds to user-selected filter, try it first.
importPlugins.Insert(plugin);
}
else if (plugin->SupportsExtension(extension))
{
importPlugins.Append(plugin);
}
if (plugin->SupportsExtension(extension))
compatiblePlugins.Append(plugin);
importPluginNode = importPluginNode->GetNext();
}
// Next, add all other plugins
importPluginNode = mImportPluginList->GetFirst();
while(importPluginNode)
{
ImportPlugin *plugin = importPluginNode->GetData();
if (importPlugins.Find(plugin) == NULL)
{
// Skip MP3 import plugin. Opens some non-mp3 audio files (ac3 for example) as garbage.
if (plugin->GetPluginFormatDescription().CompareTo( _("MP3 files") ) != 0)
{
importPlugins.Append(plugin);
}
}
importPluginNode = importPluginNode->GetNext();
}
importPluginNode = importPlugins.GetFirst();
while(importPluginNode)
{
ImportPlugin *plugin = importPluginNode->GetData();
// Try to open the file with this plugin (probe it)
inFile = plugin->Open(fName);
if ( (inFile != NULL) && (inFile->GetStreamCount() > 0) )
{
// File has more than one stream - display stream selector
if (inFile->GetStreamCount() > 1)
{
ImportStreamDialog ImportDlg(inFile, NULL, -1, _("Select stream(s) to import"));
if (ImportDlg.ShowModal() == wxID_CANCEL)
{
delete inFile;
return 0;
}
}
// One stream - import it by default
else
inFile->SetStreamUsage(0,TRUE);
int res;
res = inFile->Import(trackFactory, tracks, &numTracks, tags);
delete inFile;
if (res == eProgressSuccess || res == eProgressStopped)
{
// LOF ("list-of-files") has different semantics
if (extension.IsSameAs(wxT("lof"), false))
return 1;
if (numTracks > 0) {
// success!
return numTracks;
}
}
if (res == eProgressCancelled || res == eProgressFailed)
{
return 0;
}
// We could exit here since we had a match on the file extension,
// but there may be another plug-in that can import the file and
// that may recognize the extension, so we allow the loop to
// continue.
}
importPluginNode = importPluginNode->GetNext();
}
// None of our plugins can handle this file. It might be that
// Audacity supports this format, but support was not compiled in.
// If so, notify the user of this fact
UnusableImportPluginList::compatibility_iterator unusableImporterNode
= mUnusableImportPluginList->GetFirst();
while(unusableImporterNode)
{
UnusableImportPlugin *unusableImportPlugin = unusableImporterNode->GetData();
if( unusableImportPlugin->SupportsExtension(extension) )
{
errorMessage.Printf(_("This version of Audacity was not compiled with %s support."),
unusableImportPlugin->
GetPluginFormatDescription().c_str());
return 0;
}
unusableImporterNode = unusableImporterNode->GetNext();
}
/* warnings for unsupported data types */
#ifdef USE_MIDI
// MIDI files must be imported, not opened
if ((extension.IsSameAs(wxT("midi"), false))||(extension.IsSameAs(wxT("mid"), false))) {
errorMessage.Printf(_("\"%s\" \nis a MIDI file, not an audio file. \nAudacity cannot open this type of file for playing, but you can\nedit it by clicking File > Import > MIDI."), fName.c_str());
return 0;
}
#endif
if (compatiblePlugins.GetCount() <= 0)
{
// if someone has sent us a .cda file, send them away
if (extension.IsSameAs(wxT("cda"), false)) {
/* i18n-hint: %s will be the filename */
errorMessage.Printf(_("\"%s\" is an audio CD track. \nAudacity cannot open audio CDs directly. \nExtract (rip) the CD tracks to an audio format that \nAudacity can import, such as WAV or AIFF."), fName.c_str());
return 0;
}
// playlist type files
if ((extension.IsSameAs(wxT("m3u"), false))||(extension.IsSameAs(wxT("ram"), false))||(extension.IsSameAs(wxT("pls"), false))) {
errorMessage.Printf(_("\"%s\" is a playlist file. \nAudacity cannot open this file because it only contains links to other files. \nYou may be able to open it in a text editor and download the actual audio files."), fName.c_str());
return 0;
}
//WMA files of various forms
if ((extension.IsSameAs(wxT("wma"), false))||(extension.IsSameAs(wxT("asf"), false))) {
errorMessage.Printf(_("\"%s\" is a Windows Media Audio file. \nAudacity cannot open this type of file due to patent restrictions. \nYou need to convert it to a supported audio format, such as WAV or AIFF."), fName.c_str());
return 0;
}
//AAC files of various forms (probably not encrypted)
if ((extension.IsSameAs(wxT("aac"), false))||(extension.IsSameAs(wxT("m4a"), false))||(extension.IsSameAs(wxT("m4r"), false))||(extension.IsSameAs(wxT("mp4"), false))) {
errorMessage.Printf(_("\"%s\" is an Advanced Audio Coding file. \nAudacity cannot open this type of file. \nYou need to convert it to a supported audio format, such as WAV or AIFF."), fName.c_str());
return 0;
}
// encrypted itunes files
if ((extension.IsSameAs(wxT("m4p"), false))) {
errorMessage.Printf(_("\"%s\" is an encrypted audio file. \nThese typically are from an online music store. \nAudacity cannot open this type of file due to the encryption. \nTry recording the file into Audacity, or burn it to audio CD then \nextract the CD track to a supported audio format such as WAV or AIFF."), fName.c_str());
return 0;
}
// Real Inc. files of various sorts
if ((extension.IsSameAs(wxT("ra"), false))||(extension.IsSameAs(wxT("rm"), false))||(extension.IsSameAs(wxT("rpm"), false))) {
errorMessage.Printf(_("\"%s\" is a RealPlayer media file. \nAudacity cannot open this proprietary format. \nYou need to convert it to a supported audio format, such as WAV or AIFF."), fName.c_str());
return 0;
}
// Other notes-based formats
if ((extension.IsSameAs(wxT("kar"), false))||(extension.IsSameAs(wxT("mod"), false))||(extension.IsSameAs(wxT("rmi"), false))) {
errorMessage.Printf(_("\"%s\" is a notes-based file, not an audio file. \nAudacity cannot open this type of file. \nTry converting it to an audio file such as WAV or AIFF and \nthen import it, or record it into Audacity."), fName.c_str());
return 0;
}
// MusePack files
if ((extension.IsSameAs(wxT("mp+"), false))||(extension.IsSameAs(wxT("mpc"), false))||(extension.IsSameAs(wxT("mpp"), false))) {
errorMessage.Printf(_("\"%s\" is a Musepack audio file. \nAudacity cannot open this type of file. \nIf you think it might be an mp3 file, rename it to end with \".mp3\" \nand try importing it again. Otherwise you need to convert it to a supported audio \nformat, such as WAV or AIFF."), fName.c_str());
return 0;
}
// WavPack files
if ((extension.IsSameAs(wxT("wv"), false))||(extension.IsSameAs(wxT("wvc"), false))) {
errorMessage.Printf(_("\"%s\" is a Wavpack audio file. \nAudacity cannot open this type of file. \nYou need to convert it to a supported audio format, such as WAV or AIFF."), fName.c_str());
return 0;
}
// AC3 files
if ((extension.IsSameAs(wxT("ac3"), false))) {
errorMessage.Printf(_("\"%s\" is a Dolby Digital audio file. \nAudacity cannot currently open this type of file. \nYou need to convert it to a supported audio format, such as WAV or AIFF."), fName.c_str());
return 0;
}
// Speex files
if ((extension.IsSameAs(wxT("spx"), false))) {
errorMessage.Printf(_("\"%s\" is an Ogg Speex audio file. \nAudacity cannot currently open this type of file. \nYou need to convert it to a supported audio format, such as WAV or AIFF."), fName.c_str());
return 0;
}
// Video files of various forms
if ((extension.IsSameAs(wxT("mpg"), false))||(extension.IsSameAs(wxT("mpeg"), false))||(extension.IsSameAs(wxT("avi"), false))||(extension.IsSameAs(wxT("wmv"), false))||(extension.IsSameAs(wxT("rv"), false))) {
errorMessage.Printf(_("\"%s\" is a video file. \nAudacity cannot currently open this type of file. \nYou need to extract the audio to a supported format, such as WAV or AIFF."), fName.c_str());
return 0;
}
// we were not able to recognize the file type
errorMessage.Printf(_("Audacity did not recognize the type of the file '%s'.\nIf it is uncompressed, try importing it using \"Import Raw\"."),fName.c_str());
}
else
{
// We DO have a plugin for this file, but import failed.
wxString pluglist = wxEmptyString;
importPluginNode = compatiblePlugins.GetFirst();
while(importPluginNode)
{
ImportPlugin *plugin = importPluginNode->GetData();
if (pluglist == wxEmptyString)
pluglist = plugin->GetPluginFormatDescription();
else
pluglist = pluglist + wxT(", ") + plugin->GetPluginFormatDescription();
importPluginNode = importPluginNode->GetNext();
}
errorMessage.Printf(_("Audacity recognized the type of the file '%s'.\nImporters supposedly supporting such files are:\n%s,\nbut none of them understood this file format."),fName.c_str(), pluglist.c_str());
}
return 0;
}
//-------------------------------------------------------------------------
// ImportStreamDialog
//-------------------------------------------------------------------------
BEGIN_EVENT_TABLE( ImportStreamDialog,wxDialog )
EVT_BUTTON( wxID_OK, ImportStreamDialog::OnOk )
EVT_BUTTON( wxID_CANCEL, ImportStreamDialog::OnCancel )
END_EVENT_TABLE()
ImportStreamDialog::ImportStreamDialog( ImportFileHandle *_mFile, wxWindow *parent, wxWindowID id, const wxString &title,
const wxPoint &position, const wxSize& size, long style ):
wxDialog( parent, id, title, position, size, style | wxRESIZE_BORDER )
{
mFile = _mFile;
scount = mFile->GetStreamCount();
for (wxInt32 i = 0; i < scount; i++)
mFile->SetStreamUsage(i,FALSE);
wxBoxSizer *vertSizer = new wxBoxSizer( wxVERTICAL );
wxArrayString *choices = mFile->GetStreamInfo();
StreamList = new wxListBox(this, -1, wxDefaultPosition, wxDefaultSize, *choices , wxLB_EXTENDED | wxLB_ALWAYS_SB);
vertSizer->Add( StreamList, 1, wxEXPAND | wxALIGN_LEFT | wxALL, 5 );
vertSizer->Add( CreateStdButtonSizer(this, eCancelButton|eOkButton), 0, wxEXPAND );
SetAutoLayout( true );
SetSizer( vertSizer );
vertSizer->Fit( this );
SetSize( 400, 200 );
}
ImportStreamDialog::~ImportStreamDialog()
{
}
void ImportStreamDialog::OnOk(wxCommandEvent &event)
{
wxArrayInt selitems;
int sels = StreamList->GetSelections(selitems);
for (wxInt32 i = 0; i < sels; i++)
mFile->SetStreamUsage(selitems[i],TRUE);
EndModal( wxID_OK );
}
void ImportStreamDialog::OnCancel(wxCommandEvent &event)
{
EndModal( wxID_CANCEL );
}
// 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: 702e6bd3-b26c-424f-9d6a-c88b565ea143

108
src/import/Import.h Normal file
View File

@@ -0,0 +1,108 @@
/**********************************************************************
Audacity: A Digital Audio Editor
Import.h
Dominic Mazzoni
**********************************************************************/
#ifndef _IMPORT_
#define _IMPORT_
#include <wx/arrstr.h>
#include <wx/string.h>
#include <wx/list.h>
#include <wx/listimpl.cpp>
#include <wx/dialog.h>
#include <wx/listbox.h>
class Tags;
class TrackFactory;
class Track;
class ImportPlugin;
class ImportFileHandle;
class UnusableImportPlugin;
typedef bool (*progress_callback_t)( void *userData, float percent );
class Format {
public:
wxString formatName;
wxArrayString formatExtensions;
Format(wxString _formatName, wxArrayString _formatExtensions):
formatName(_formatName),
formatExtensions(_formatExtensions)
{
}
};
class ImportPluginList;
class UnusableImportPluginList;
WX_DECLARE_LIST(Format, FormatList);
class Importer {
public:
Importer();
~Importer();
void GetSupportedImportFormats(FormatList *formatList);
// returns number of tracks imported
// if zero, the import failed and errorMessage will be set.
int Import(wxString fName,
TrackFactory *trackFactory,
Track *** tracks,
Tags *tags,
wxString &errorMessage);
private:
ImportPluginList *mImportPluginList;
UnusableImportPluginList *mUnusableImportPluginList;
};
//----------------------------------------------------------------------------
// ImportStreamDialog
//----------------------------------------------------------------------------
class ImportStreamDialog: public wxDialog
{
public:
// constructors and destructors
ImportStreamDialog( ImportFileHandle *_mFile,
wxWindow *parent, wxWindowID id, const wxString &title,
const wxPoint& pos = wxDefaultPosition,
const wxSize& size = wxDefaultSize,
long style = wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER );
virtual ~ImportStreamDialog();
private:
ImportFileHandle *mFile;
wxInt32 scount;
wxListBox *StreamList;
private:
void OnOk( wxCommandEvent &event );
void OnCancel( wxCommandEvent &event );
private:
DECLARE_EVENT_TABLE()
};
#endif
// 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: 30d8bb1f-1e74-4cec-918c-4b36a9a75c49

859
src/import/ImportFFmpeg.cpp Normal file
View File

@@ -0,0 +1,859 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ImportFFmpeg.cpp
Copyright 2008 LRN
Based on ImportFLAC.cpp by Sami Liedes and transcode_sample.c by ANYwebcam Pty Ltd
Licensed under the GNU General Public License v2 or later
*//****************************************************************//**
\class FFmpegImportFileHandle
\brief An ImportFileHandle for FFmpeg data
*//****************************************************************//**
\class FFmpegImportPlugin
\brief An ImportPlugin for FFmpeg data
*//*******************************************************************/
// For compilers that support precompilation, includes "wx/wx.h".
#include <wx/wxprec.h>
#include "../Audacity.h" // needed before FFmpeg.h
#include "../FFmpeg.h" // which brings in avcodec.h, avformat.h
#ifndef WX_PRECOMP
// Include your minimal set of headers here, or wx.h
#include <wx/window.h>
#endif
#define DESC _("FFmpeg-compatible files")
//TODO: remove non-audio extensions
static const wxChar *exts[] =
{
wxT("4xm"),
wxT("MTV"),
wxT("roq"),
wxT("aac"),
wxT("ac3"),
wxT("aif"),
wxT("aiff"),
wxT("afc"),
wxT("aifc"),
wxT("al"),
wxT("amr"),
wxT("apc"),
wxT("ape"),
wxT("apl"),
wxT("mac"),
wxT("asf"),
wxT("wmv"),
wxT("wma"),
wxT("au"),
wxT("avi"),
wxT("avs"),
wxT("bethsoftvid"),
wxT("c93"),
wxT("302"),
wxT("daud"),
wxT("dsicin"),
wxT("dts"),
wxT("dv"),
wxT("dxa"),
wxT("ea"),
wxT("cdata"),
wxT("ffm"),
wxT("film_cpk"),
wxT("flac"),
wxT("flic"),
wxT("flv"),
wxT("gif"),
wxT("gxf"),
wxT("idcin"),
wxT("image2"),
wxT("image2pipe"),
wxT("cgi"),
wxT("ipmovie"),
wxT("nut"),
wxT("lmlm4"),
wxT("m4v"),
wxT("mkv"),
wxT("mm"),
wxT("mmf"),
wxT("mov"),
wxT("mp4"),
wxT("m4a"),
wxT("m4r"),
wxT("3gp"),
wxT("3g2"),
wxT("mj2"),
wxT("mp3"),
wxT("mpc"),
wxT("mpc8"),
wxT("mpg"),
wxT("mpeg"),
wxT("ts"),
wxT("mpegtsraw"),
wxT("mpegvideo"),
wxT("msnwctcp"),
wxT("ul"),
wxT("mxf"),
wxT("nsv"),
wxT("nuv"),
wxT("ogg"),
wxT("psxstr"),
wxT("pva"),
wxT("redir"),
wxT("rl2"),
wxT("rm"),
wxT("ra"),
wxT("rv"),
wxT("rtsp"),
wxT("s16be"),
wxT("sw"),
wxT("s8"),
wxT("sb"),
wxT("sdp"),
wxT("shn"),
wxT("siff"),
wxT("vb"),
wxT("son"),
wxT("smk"),
wxT("sol"),
wxT("swf"),
wxT("thp"),
wxT("tiertexseq"),
wxT("tta"),
wxT("txd"),
wxT("u16be"),
wxT("uw"),
wxT("ub"),
wxT("u8"),
wxT("vfwcap"),
wxT("vmd"),
wxT("voc"),
wxT("wav"),
wxT("wc3movie"),
wxT("wsaud"),
wxT("wsvqa"),
wxT("wv")
};
#if defined(USE_FFMPEG)
// all the includes live here by default
#include "Import.h"
#include "ImportFFmpeg.h"
#include "../Tags.h"
#include "../Internat.h"
#include "../WaveTrack.h"
#include "ImportPlugin.h"
extern FFmpegLibs *FFmpegLibsInst;
class FFmpegImportFileHandle;
typedef struct _streamContext
{
bool m_use; // TRUE = this stream will be loaded into Audacity
AVStream *m_stream; // an AVStream *
AVCodecContext *m_codecCtx; // pointer to m_stream->codec
AVPacket m_pkt; // the last AVPacket we read for this stream
int m_pktValid; // is m_pkt valid?
uint8_t *m_pktDataPtr; // pointer into m_pkt.data
int m_pktRemainingSiz;
int64_t m_pts; // the current presentation time of the input stream
int64_t m_ptsOffset; // packets associated with stream are relative to this
int m_frameValid; // is m_decodedVideoFrame/m_decodedAudioSamples valid?
int16_t *m_decodedAudioSamples; // decoded audio samples stored here
unsigned int m_decodedAudioSamplesSiz; // current size of m_decodedAudioSamples
int m_decodedAudioSamplesValidSiz; // # valid bytes in m_decodedAudioSamples
int m_initialchannels; // number of channels allocated when we begin the importing. Assumes that number of channels doesn't change on the fly.
} streamContext;
/// A representative of FFmpeg loader in
/// the Audacity import plugin list
class FFmpegImportPlugin : public ImportPlugin
{
public:
FFmpegImportPlugin():
ImportPlugin(wxArrayString(WXSIZEOF(exts),exts))
{
}
~FFmpegImportPlugin() { }
wxString GetPluginFormatDescription();
///! Probes the file and opens it if appropriate
ImportFileHandle *Open(wxString Filename);
};
///! Does acual import, returned by FFmpegImportPlugin::Open
class FFmpegImportFileHandle : public ImportFileHandle
{
public:
FFmpegImportFileHandle(const wxString & name);
~FFmpegImportFileHandle();
///! Format initialization
///\return true if successful, false otherwise
bool Init();
///! Codec initialization
///\return true if successful, false otherwise
bool InitCodecs();
wxString GetFileDescription();
int GetFileUncompressedBytes();
///! Imports audio
///\return import status (see Import.cpp)
int Import(TrackFactory *trackFactory, Track ***outTracks,
int *outNumTracks, Tags *tags);
///! Reads next audio frame
///\return pointer to the stream context structure to which the frame belongs to or NULL on error, or 1 if stream is not to be imported.
streamContext* ReadNextFrame();
///! Decodes the frame
///\param sc - stream context (from ReadNextFrame)
///\param flushing - true if flushing (no more frames left), false otherwise
///\return 0 on success, -1 if it can't decode any further
int DecodeFrame(streamContext *sc, bool flushing);
///! Writes decoded data into WaveTracks. Called by DecodeFrame
///\param sc - stream context
///\return 0 on success, 1 on error or interruption
int WriteData(streamContext *sc);
///! Writes extracted metadata to tags object
///\param avf - file context
///\ tags - Audacity tags object
void WriteMetadata(AVFormatContext *avf, Tags *tags);
///! Called by Import.cpp
///\return number of readable streams in the file
wxInt32 GetStreamCount()
{
return mNumStreams;
}
///! Called by Import.cpp
///\return array of strings - descriptions of the streams
wxArrayString *GetStreamInfo()
{
return mStreamInfo;
}
///! Called by Import.cpp
///\param StreamID - index of the stream in mStreamInfo and mScs arrays
///\param Use - true if this stream should be imported, false otherwise
void SetStreamUsage(wxInt32 StreamID, bool Use)
{
if (StreamID < mNumStreams)
mScs[StreamID]->m_use = Use;
}
private:
AVFormatContext *mFormatContext; //!< Format description, also contains metadata and some useful info
int mNumStreams; //!< mNumstreams is less or equal to mFormatContext->nb_streams
streamContext **mScs; //!< Array of pointers to stream contexts. Length is mNumStreams.
wxArrayString *mStreamInfo; //!< Array of stream descriptions. Length is mNumStreams
wxInt64 mProgressPos; //!< Current timestamp, file position or whatever is used as first argument for Update()
wxInt64 mProgressLen; //!< Duration, total length or whatever is used as second argument for Update()
bool mCancelled; //!< True if importing was canceled by user
bool mStopped; //!< True if importing was stopped by user
wxString mName;
WaveTrack ***mChannels; //!< 2-dimentional array of WaveTrack's. First dimention - streams, second - channels of a stream. Length is mNumStreams
};
void GetFFmpegImportPlugin(ImportPluginList *importPluginList,
UnusableImportPluginList *unusableImportPluginList)
{
importPluginList->Append(new FFmpegImportPlugin);
}
wxString FFmpegImportPlugin::GetPluginFormatDescription()
{
return DESC;
}
ImportFileHandle *FFmpegImportPlugin::Open(wxString filename)
{
FFmpegImportFileHandle *handle = new FFmpegImportFileHandle(filename);
//Check if we're loading explicitly supported format
wxString extension = filename.AfterLast(wxT('.'));
if (SupportsExtension(extension))
{
//Audacity is trying to load something that is declared as
//officially supported by this plugin.
//If we don't have FFmpeg configured - tell the user about it.
//Since this will be happening often, use disableable "FFmpeg not found" dialog
//insdead of usual wxMessageBox()
bool newsession = false;
gPrefs->Read(wxT("/NewImportingSession"), &newsession);
if (!FFmpegLibsInst->ValidLibsLoaded())
{
int dontShowDlg;
FFmpegNotFoundDialog *dlg;
gPrefs->Read(wxT("/FFmpeg/NotFoundDontShow"),&dontShowDlg,0);
if (dontShowDlg == 0 && newsession)
{
gPrefs->Write(wxT("/NewImportingSession"), false);
dlg = new FFmpegNotFoundDialog(NULL);
dlg->ShowModal();
delete dlg;
}
}
}
if (!FFmpegLibsInst->ValidLibsLoaded())
{
delete handle;
return NULL;
}
// Open the file for import
bool success = handle->Init();
if (!success) {
delete handle;
return NULL;
}
return handle;
}
FFmpegImportFileHandle::FFmpegImportFileHandle(const wxString & name)
: ImportFileHandle(name)
{
PickFFmpegLibs();
mStreamInfo = new wxArrayString();
mFormatContext = NULL;
mNumStreams = 0;
mScs = NULL;
mCancelled = false;
mStopped = false;
mName = name;
mChannels = NULL;
mProgressPos = 0;
mProgressLen = 1;
}
bool FFmpegImportFileHandle::Init()
{
//FFmpegLibsInst->LoadLibs(NULL,false); //Loaded at startup or from Prefs now
if (!FFmpegLibsInst->ValidLibsLoaded()) return false;
FFmpegLibsInst->av_log_set_callback(av_log_wx_callback);
int err = ufile_fopen_input(&mFormatContext, mName);
if (err < 0)
{
wxLogMessage(wxT("FFmpeg : av_open_input_file() failed for file %s"),mName.c_str());
return false;
}
err = FFmpegLibsInst->av_find_stream_info(mFormatContext);
if (err < 0)
{
wxLogMessage(wxT("FFmpeg : av_find_stream_info() failed for file %s"),mName.c_str());
return false;
}
InitCodecs();
return true;
}
bool FFmpegImportFileHandle::InitCodecs()
{
// Allocate the array of pointers to hold stream contexts pointers
// Some of the allocated space may be unused (corresponds to video, subtitle, or undecodeable audio streams)
mScs = (streamContext**)malloc(sizeof(streamContext**)*mFormatContext->nb_streams);
// Fill the stream contexts
for (unsigned int i = 0; i < mFormatContext->nb_streams; i++)
{
if (mFormatContext->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO)
{
//Create a context
streamContext *sc = new streamContext;
memset(sc,0,sizeof(*sc));
sc->m_stream = mFormatContext->streams[i];
sc->m_codecCtx = sc->m_stream->codec;
AVCodec *codec = FFmpegLibsInst->avcodec_find_decoder(sc->m_codecCtx->codec_id);
if (codec == NULL)
{
wxLogMessage(wxT("FFmpeg : avcodec_find_decoder() failed. Index[%02d], Codec[%02x - %s]"),i,sc->m_codecCtx->codec_id,sc->m_codecCtx->codec_name);
//FFmpeg can't decode this stream, skip it
delete sc;
continue;
}
if (codec->type != sc->m_codecCtx->codec_type)
{
wxLogMessage(wxT("FFmpeg : Codec type mismatch, skipping. Index[%02d], Codec[%02x - %s]"),i,sc->m_codecCtx->codec_id,sc->m_codecCtx->codec_name);
//Non-audio codec reported as audio? Nevertheless, we don't need THIS.
delete sc;
continue;
}
if (FFmpegLibsInst->avcodec_open(sc->m_codecCtx, codec) < 0)
{
wxLogMessage(wxT("FFmpeg : avcodec_open() failed. Index[%02d], Codec[%02x - %s]"),i,sc->m_codecCtx->codec_id,sc->m_codecCtx->codec_name);
//Can't open decoder - skip this stream
delete sc;
continue;
}
// Stream is decodeable and it is audio. Add it and its decription to the arrays
wxString strinfo;
int duration = 0;
if (sc->m_stream->duration > 0)
duration = sc->m_stream->duration * sc->m_stream->time_base.num / sc->m_stream->time_base.den;
else
duration = mFormatContext->duration / AV_TIME_BASE;
wxString bitrate = wxT("");
if (sc->m_codecCtx->bit_rate > 0)
bitrate.Printf(wxT("%d"),sc->m_codecCtx->bit_rate);
else
bitrate.Printf(wxT("?"));
strinfo.Printf(_("Index[%02x] Codec[%S], Language[%S], Bitrate[%S], Channels[%d], Duration[%d]"),sc->m_stream->id,codec->name,sc->m_stream->language,bitrate.c_str(),sc->m_stream->codec->channels, duration);
mStreamInfo->Add(strinfo);
mScs[mNumStreams++] = sc;
}
//for video and unknown streams do nothing
}
//It doesn't really returns false, but GetStreamCount() will return 0 if file is composed entierly of unreadable streams
return true;
}
wxString FFmpegImportFileHandle::GetFileDescription()
{
return DESC;
}
int FFmpegImportFileHandle::GetFileUncompressedBytes()
{
// TODO: Get Uncompressed byte count.
return 0;
}
int FFmpegImportFileHandle::Import(TrackFactory *trackFactory,
Track ***outTracks,
int *outNumTracks,
Tags *tags)
{
CreateProgress();
// Remove stream contexts which are not marked for importing and adjust mScs and mNumStreams accordingly
for (int i = 0; i < mNumStreams;)
{
if (!mScs[i]->m_use)
{
delete mScs[i];
for (int j = i; j < mNumStreams - 1; j++)
{
mScs[j] = mScs[j+1];
}
mNumStreams--;
}
else i++;
}
mChannels = new WaveTrack **[mNumStreams];
for (int s = 0; s < mNumStreams; s++)
{
// There is a possibility that number of channels will change over time, but we do not have WaveTracks for new channels. Remember the number of channels and stick to it.
mScs[s]->m_initialchannels = mScs[s]->m_stream->codec->channels;
mChannels[s] = new WaveTrack *[mScs[s]->m_stream->codec->channels];
int c;
for (c = 0; c < mScs[s]->m_stream->codec->channels; c++)
{
mChannels[s][c] = trackFactory->NewWaveTrack(int16Sample, mScs[s]->m_stream->codec->sample_rate);
if (mScs[s]->m_stream->codec->channels == 2)
{
switch (c)
{
case 0:
mChannels[s][c]->SetChannel(Track::LeftChannel);
mChannels[s][c]->SetLinked(true);
break;
case 1:
mChannels[s][c]->SetChannel(Track::RightChannel);
break;
}
}
else
{
mChannels[s][c]->SetChannel(Track::MonoChannel);
}
}
}
// Handles the start_time by creating silence. This may or may not be correct.
// There is a possibility that we should ignore first N milliseconds of audio instead. I do not know.
/// TODO: Nag FFmpeg devs about start_time until they finally say WHAT is this and HOW to handle it.
for (int s = 0; s < mNumStreams; s++)
{
int64_t stream_delay = 0;
if (mScs[s]->m_stream->start_time != int64_t(AV_NOPTS_VALUE))
{
stream_delay = mScs[s]->m_stream->start_time;
wxLogMessage(wxT("Stream %d start_time = %d, that would be %f milliseconds."), s, mScs[s]->m_stream->start_time, double(mScs[s]->m_stream->start_time)/AV_TIME_BASE*1000);
}
if (stream_delay != 0)
{
for (int c = 0; c < mScs[s]->m_stream->codec->channels; c++)
{
WaveTrack *t = mChannels[s][c];
t->InsertSilence(0,double(stream_delay)/AV_TIME_BASE);
}
}
}
// This is the heart of the importing process
streamContext *sc = NULL;
// The result of Import() to be returend. It will be something other than zero if user canceled or some error appears.
int res = eProgressSuccess;
// Read next frame.
while ((sc = ReadNextFrame()) != NULL && (res == eProgressSuccess))
{
// ReadNextFrame returns 1 if stream is not to be imported
if (sc != (streamContext*)1)
{
// Decode frame until it is not possible to decode any further
while (sc->m_pktRemainingSiz > 0 && (res == eProgressSuccess || res == eProgressStopped))
{
if (DecodeFrame(sc,false) < 0)
break;
// If something useable was decoded - write it to mChannels
if (sc->m_frameValid)
res = WriteData(sc);
}
// Cleanup after frame decoding
if (sc->m_pktValid)
{
#if FFMPEG_STABLE
av_free_packet(&sc->m_pkt);
#else
FFmpegLibsInst->av_free_packet(&sc->m_pkt);
#endif
sc->m_pktValid = 0;
}
}
}
// Flush the decoders.
if ((mNumStreams != 0) && (res == eProgressSuccess || res == eProgressStopped))
{
for (int i = 0; i < mNumStreams; i++)
{
if (DecodeFrame(mScs[i], true) == 0)
{
WriteData(mScs[i]);
if (mScs[i]->m_pktValid)
{
#if FFMPEG_STABLE
av_free_packet(&mScs[i]->m_pkt);
#else
FFmpegLibsInst->av_free_packet(&mScs[i]->m_pkt);
#endif
mScs[i]->m_pktValid = 0;
}
}
}
}
// Something bad happened - destroy everything!
if (res == eProgressCancelled || res == eProgressFailed)
{
for (int s = 0; s < mNumStreams; s++)
{
delete[] mChannels[s];
}
delete[] mChannels;
return res;
}
//else if (res == 2), we just stop the decoding as if the file has ended
*outNumTracks = 0;
for (int s = 0; s < mNumStreams; s++)
{
*outNumTracks += mScs[s]->m_initialchannels;
}
// Create new tracks
*outTracks = new Track *[*outNumTracks];
// Copy audio from mChannels to newly created tracks (destroying mChannels elements in process)
int trackindex = 0;
for (int s = 0; s < mNumStreams; s++)
{
for(int c = 0; c < mScs[s]->m_initialchannels; c++)
{
mChannels[s][c]->Flush();
(*outTracks)[trackindex++] = mChannels[s][c];
}
delete[] mChannels[s];
}
delete[] mChannels;
// Save metadata
WriteMetadata(mFormatContext,tags);
return res;
}
streamContext *FFmpegImportFileHandle::ReadNextFrame()
{
streamContext *sc = NULL;
AVPacket pkt;
if (FFmpegLibsInst->av_read_frame(mFormatContext,&pkt) < 0)
{
return NULL;
}
// Find a stream to which this frame belongs to
for (int i = 0; i < mNumStreams; i++)
{
if (mScs[i]->m_stream->index == pkt.stream_index)
sc = mScs[i];
}
// Off-stream packet. Don't panic, just skip it.
// When not all streams are selected for import this will happen very often.
if (sc == NULL)
{
#if FFMPEG_STABLE
av_free_packet(&pkt);
#else
FFmpegLibsInst->av_free_packet(&pkt);
#endif
return (streamContext*)1;
}
// Copy the frame to the stream context
memcpy(&sc->m_pkt, &pkt, sizeof(AVPacket));
sc->m_pktValid = 1;
sc->m_pktDataPtr = pkt.data;
sc->m_pktRemainingSiz = pkt.size;
return sc;
}
int FFmpegImportFileHandle::DecodeFrame(streamContext *sc, bool flushing)
{
int nBytesDecoded;
wxUint8 *pDecode = sc->m_pktDataPtr;
int nDecodeSiz = sc->m_pktRemainingSiz;
sc->m_frameValid = 0;
if (flushing)
{
// If we're flushing the decoders we don't actually have any new data to decode.
pDecode = NULL;
nDecodeSiz = 0;
}
else
{
if (!sc->m_pktValid || (sc->m_pktRemainingSiz <= 0))
{
//No more data
return -1;
}
}
// Reallocate the audio sample buffer if it's smaller than the frame size.
if (!flushing)
{
// av_fast_realloc() will only reallocate the buffer if m_decodedAudioSamplesSiz is
// smaller than third parameter. It also returns new size in m_decodedAudioSamplesSiz
//\warning { for some reason using the following macro call right in the function call
// causes Audacity to crash in some unknown place. With "newsize" it works fine }
int newsize = FFMAX(sc->m_pkt.size*sizeof(*sc->m_decodedAudioSamples), AVCODEC_MAX_AUDIO_FRAME_SIZE);
sc->m_decodedAudioSamples = (int16_t*)FFmpegLibsInst->av_fast_realloc(sc->m_decodedAudioSamples,
&sc->m_decodedAudioSamplesSiz,
newsize
);
if (sc->m_decodedAudioSamples == NULL)
{
//Can't allocate bytes
return -1;
}
}
// avcodec_decode_audio2() expects the size of the output buffer as the 3rd parameter but
// also returns the number of bytes it decoded in the same parameter.
sc->m_decodedAudioSamplesValidSiz = sc->m_decodedAudioSamplesSiz;
nBytesDecoded = FFmpegLibsInst->avcodec_decode_audio2(sc->m_codecCtx,
sc->m_decodedAudioSamples, // out
&sc->m_decodedAudioSamplesValidSiz, // in/out
pDecode, nDecodeSiz); // in
if (nBytesDecoded < 0)
{
// Decoding failed. Don't stop.
return -1;
}
// We may not have read all of the data from this packet. If so, the user can call again.
// Whether or not they do depends on if m_pktRemainingSiz == 0 (they can check).
sc->m_pktDataPtr += nBytesDecoded;
sc->m_pktRemainingSiz -= nBytesDecoded;
// At this point it's normally safe to assume that we've read some samples. However, the MPEG
// audio decoder is broken. If this is the case then we just return with m_frameValid == 0
// but m_pktRemainingSiz perhaps != 0, so the user can call again.
if (sc->m_decodedAudioSamplesValidSiz > 0)
{
sc->m_frameValid = 1;
}
return 0;
}
int FFmpegImportFileHandle::WriteData(streamContext *sc)
{
size_t pos = 0;
// Find the stream index in mScs array
int streamid = -1;
for (int i = 0; i < mNumStreams; i++)
{
if (mScs[i] == sc)
{
streamid = i;
break;
}
}
// Stream is not found. This should not really happen
if (streamid == -1)
{
return 1;
}
// Allocate the buffer to store audio.
int nChannels = sc->m_stream->codec->channels < sc->m_initialchannels ? sc->m_stream->codec->channels : sc->m_initialchannels;
int16_t **tmp = (int16_t**)malloc(sizeof(short*)*nChannels);
for (int chn = 0; chn < nChannels; chn++)
{
tmp[chn] = (int16_t*)malloc(sizeof(int16_t)*sc->m_decodedAudioSamplesValidSiz/sizeof(int16_t)/sc->m_stream->codec->channels);
}
// Separate the channels
int index = 0;
while (pos < sc->m_decodedAudioSamplesValidSiz/sizeof(int16_t))
{
for (int chn=0; chn < sc->m_stream->codec->channels; chn++)
{
if (chn < nChannels)
tmp[chn][index] = sc->m_decodedAudioSamples[pos];
pos++;
}
index++;
}
// Write audio into WaveTracks
for (int chn=0; chn < nChannels; chn++)
{
mChannels[streamid][chn]->Append((samplePtr)tmp[chn],int16Sample,index);
free(tmp[chn]);
}
free(tmp);
// Try to update the progress indicator (and see if user wants to cancel)
int updateResult = eProgressSuccess;
// PTS (presentation time) is the proper way of getting current position
if (sc->m_pkt.pts != AV_NOPTS_VALUE && mFormatContext->duration != AV_NOPTS_VALUE)
{
mProgressPos = sc->m_pkt.pts * sc->m_stream->time_base.num / sc->m_stream->time_base.den;
mProgressLen = (mFormatContext->duration > 0 ? mFormatContext->duration / AV_TIME_BASE: 1);
}
// When PTS is not set, use number of frames and number of current frame
else if (sc->m_stream->nb_frames > 0 && sc->m_codecCtx->frame_number > 0 && sc->m_codecCtx->frame_number <= sc->m_stream->nb_frames)
{
mProgressPos = sc->m_codecCtx->frame_number;
mProgressLen = sc->m_stream->nb_frames;
}
// When number of frames is unknown, use position in file
else if (mFormatContext->file_size > 0 && sc->m_pkt.pos > 0 && sc->m_pkt.pos <= mFormatContext->file_size)
{
mProgressPos = sc->m_pkt.pos;
mProgressLen = mFormatContext->file_size;
}
updateResult = mProgress->Update(mProgressPos, mProgressLen != 0 ? mProgressLen : 1);
return updateResult;
}
void FFmpegImportFileHandle::WriteMetadata(AVFormatContext *avf,Tags *tags)
{
tags->Clear();
// We are assuming that tags are in UTF8.
// TODO: for some formats tags are not in UTF8. Detect and handle that.
tags->SetTag(TAG_TITLE,wxString::FromUTF8(avf->title));
tags->SetTag(TAG_ARTIST,wxString::FromUTF8(avf->author));
//tags->SetTag(TAG_COPYRIGHT,avf->copyright);
tags->SetTag(TAG_COMMENTS,wxString::FromUTF8(avf->comment));
tags->SetTag(TAG_ALBUM,wxString::FromUTF8(avf->album));
tags->SetTag(TAG_YEAR,avf->year);
tags->SetTag(TAG_TRACK,avf->track);
tags->SetTag(TAG_GENRE,wxString::FromUTF8(avf->genre));
}
FFmpegImportFileHandle::~FFmpegImportFileHandle()
{
if (FFmpegLibsInst->ValidLibsLoaded())
{
if (mFormatContext) FFmpegLibsInst->av_close_input_file(mFormatContext);
FFmpegLibsInst->av_log_set_callback(FFmpegLibsInst->av_log_default_callback);
}
for (int i = 0; i < mNumStreams; i++)
{
if (mScs[i]->m_decodedAudioSamples != NULL)
FFmpegLibsInst->av_free(mScs[i]->m_decodedAudioSamples);
delete mScs[i];
}
free(mScs);
delete mStreamInfo;
DropFFmpegLibs();
}
#endif //USE_FFMPEG

22
src/import/ImportFFmpeg.h Normal file
View File

@@ -0,0 +1,22 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ImportFFmpeg.h
LRN
**********************************************************************/
#ifndef __AUDACITY_IMPORT_FFMPEG__
#define __AUDACITY_IMPORT_FFMPEG__
#include "ImportPlugin.h"
class ImportPluginList;
class UnusableImportPluginList;
void GetFFmpegImportPlugin(ImportPluginList *importPluginList,
UnusableImportPluginList *unusableImportPluginList);
#endif

556
src/import/ImportFLAC.cpp Normal file
View File

@@ -0,0 +1,556 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ImportFLAC.cpp
Copyright 2004 Sami Liedes
Leland Lucius
Based on ImportPCM.cpp by Dominic Mazzoni
Licensed under the GNU General Public License v2 or later
*//****************************************************************//**
\class FLACImportFileHandle
\brief An ImportFileHandle for FLAC data
*//****************************************************************//**
\class FLACImportPlugin
\brief An ImportPlugin for FLAC data
*//*******************************************************************/
// For compilers that support precompilation, includes "wx/wx.h".
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
// Include your minimal set of headers here, or wx.h
#include <wx/window.h>
#endif
#include <wx/defs.h>
#include <wx/intl.h> // needed for _("translated stings") even if we
// don't have libflac available
#include "../Audacity.h"
#include "Import.h"
#include "ImportPlugin.h"
#include "../Tags.h"
#define FLAC_HEADER "fLaC"
#define DESC _("FLAC files")
static const wxChar *exts[] =
{
wxT("flac"),
wxT("flc")
};
#ifndef USE_LIBFLAC
void GetFLACImportPlugin(ImportPluginList *importPluginList,
UnusableImportPluginList *unusableImportPluginList)
{
UnusableImportPlugin* flacIsUnsupported =
new UnusableImportPlugin(DESC, wxArrayString(WXSIZEOF(exts), exts));
unusableImportPluginList->Append(flacIsUnsupported);
}
#else /* USE_LIBFLAC */
#include "../Internat.h"
#include "ImportFLAC.h"
#include <wx/string.h>
#include <wx/utils.h>
#include <wx/file.h>
#include <wx/ffile.h>
#include "FLAC++/decoder.h"
#include "../FileFormats.h"
#include "../Prefs.h"
#include "../WaveTrack.h"
#include "ImportPlugin.h"
#include "../ondemand/ODDecodeFlacTask.h"
#include "../ondemand/ODManager.h"
#ifdef USE_LIBID3TAG
extern "C" {
#include <id3tag.h>
}
#endif
/* FLACPP_API_VERSION_CURRENT is 6 for libFLAC++ from flac-1.1.3 (see <FLAC++/export.h>) */
#if !defined FLACPP_API_VERSION_CURRENT || FLACPP_API_VERSION_CURRENT < 6
#define LEGACY_FLAC
#else
#undef LEGACY_FLAC
#endif
//#define EXPERIMENTAL_OD_FLAC
class FLACImportFileHandle;
class MyFLACFile : public FLAC::Decoder::File
{
public:
MyFLACFile(FLACImportFileHandle *handle) : mFile(handle)
{
mWasError = false;
set_metadata_ignore_all();
set_metadata_respond(FLAC__METADATA_TYPE_VORBIS_COMMENT);
set_metadata_respond(FLAC__METADATA_TYPE_STREAMINFO);
}
bool get_was_error() const
{
return mWasError;
}
private:
friend class FLACImportFileHandle;
FLACImportFileHandle *mFile;
bool mWasError;
wxArrayString mComments;
protected:
virtual FLAC__StreamDecoderWriteStatus write_callback(const FLAC__Frame *frame,
const FLAC__int32 * const buffer[]);
virtual void metadata_callback(const FLAC__StreamMetadata *metadata);
virtual void error_callback(FLAC__StreamDecoderErrorStatus status);
};
class FLACImportPlugin : public ImportPlugin
{
public:
FLACImportPlugin():
ImportPlugin(wxArrayString(WXSIZEOF(exts), exts))
{
}
~FLACImportPlugin() { }
wxString GetPluginFormatDescription();
ImportFileHandle *Open(wxString Filename);
};
class FLACImportFileHandle : public ImportFileHandle
{
friend class MyFLACFile;
public:
FLACImportFileHandle(const wxString & name);
~FLACImportFileHandle();
bool Init();
wxString GetFileDescription();
int GetFileUncompressedBytes();
int Import(TrackFactory *trackFactory, Track ***outTracks,
int *outNumTracks, Tags *tags);
wxInt32 GetStreamCount(){ return 1; }
wxArrayString *GetStreamInfo(){ return NULL; }
void SetStreamUsage(wxInt32 StreamID, bool Use){}
private:
sampleFormat mFormat;
MyFLACFile *mFile;
wxFFile mHandle;
unsigned long mSampleRate;
unsigned long mNumChannels;
unsigned long mBitsPerSample;
FLAC__uint64 mNumSamples;
FLAC__uint64 mSamplesDone;
bool mStreamInfoDone;
int mUpdateResult;
WaveTrack **mChannels;
ODDecodeFlacTask *mDecoderTask;
};
void MyFLACFile::metadata_callback(const FLAC__StreamMetadata *metadata)
{
switch (metadata->type)
{
case FLAC__METADATA_TYPE_VORBIS_COMMENT:
for (FLAC__uint32 i = 0; i < metadata->data.vorbis_comment.num_comments; i++) {
mComments.Add(UTF8CTOWX((char *)metadata->data.vorbis_comment.comments[i].entry));
}
break;
case FLAC__METADATA_TYPE_STREAMINFO:
mFile->mSampleRate=metadata->data.stream_info.sample_rate;
mFile->mNumChannels=metadata->data.stream_info.channels;
mFile->mBitsPerSample=metadata->data.stream_info.bits_per_sample;
mFile->mNumSamples=metadata->data.stream_info.total_samples;
if (mFile->mBitsPerSample<=16) {
if (mFile->mFormat<int16Sample) {
mFile->mFormat=int16Sample;
}
} else if (mFile->mBitsPerSample<=24) {
if (mFile->mFormat<int24Sample) {
mFile->mFormat=int24Sample;
}
} else {
mFile->mFormat=floatSample;
}
mFile->mStreamInfoDone=true;
break;
// handle the other types we do nothing with to avoid a warning
case FLAC__METADATA_TYPE_PADDING: // do nothing with padding
case FLAC__METADATA_TYPE_APPLICATION: // no idea what to do with this
case FLAC__METADATA_TYPE_SEEKTABLE: // don't need a seektable here
case FLAC__METADATA_TYPE_CUESHEET: // convert this to labels?
case FLAC__METADATA_TYPE_PICTURE: // ignore pictures
case FLAC__METADATA_TYPE_UNDEFINED: // do nothing with this either
break;
}
}
void MyFLACFile::error_callback(FLAC__StreamDecoderErrorStatus status)
{
mWasError = true;
/*
switch (status)
{
case FLAC__STREAM_DECODER_ERROR_STATUS_LOST_SYNC:
wxPrintf(wxT("Flac Error: Lost sync\n"));
break;
case FLAC__STREAM_DECODER_ERROR_STATUS_FRAME_CRC_MISMATCH:
wxPrintf(wxT("Flac Error: Crc mismatch\n"));
break;
case FLAC__STREAM_DECODER_ERROR_STATUS_BAD_HEADER:
wxPrintf(wxT("Flac Error: Bad Header\n"));
break;
default:
wxPrintf(wxT("Flac Error: Unknown error code\n"));
break;
}*/
}
FLAC__StreamDecoderWriteStatus MyFLACFile::write_callback(const FLAC__Frame *frame,
const FLAC__int32 * const buffer[])
{
short *tmp=new short[frame->header.blocksize];
for (unsigned int chn=0; chn<mFile->mNumChannels; chn++) {
if (frame->header.bits_per_sample == 16) {
for (unsigned int s=0; s<frame->header.blocksize; s++) {
tmp[s]=buffer[chn][s];
}
mFile->mChannels[chn]->Append((samplePtr)tmp,
int16Sample,
frame->header.blocksize);
}
else {
mFile->mChannels[chn]->Append((samplePtr)buffer[chn],
int24Sample,
frame->header.blocksize);
}
}
delete [] tmp;
mFile->mSamplesDone += frame->header.blocksize;
mFile->mUpdateResult = mFile->mProgress->Update((wxULongLong_t) mFile->mSamplesDone, mFile->mNumSamples != 0 ? (wxULongLong_t)mFile->mNumSamples : 1);
if (mFile->mUpdateResult != eProgressSuccess)
{
return FLAC__STREAM_DECODER_WRITE_STATUS_ABORT;
}
return FLAC__STREAM_DECODER_WRITE_STATUS_CONTINUE;
}
void GetFLACImportPlugin(ImportPluginList *importPluginList,
UnusableImportPluginList *unusableImportPluginList)
{
importPluginList->Append(new FLACImportPlugin);
}
wxString FLACImportPlugin::GetPluginFormatDescription()
{
return DESC;
}
ImportFileHandle *FLACImportPlugin::Open(wxString filename)
{
// First check if it really is a FLAC file
int cnt;
wxFile binaryFile;
if (!binaryFile.Open(filename)) {
return false; // File not found
}
#ifdef USE_LIBID3TAG
// Skip any ID3 tags that might be present
id3_byte_t query[ID3_TAG_QUERYSIZE];
cnt = binaryFile.Read(query, sizeof(query));
cnt = id3_tag_query(query, cnt);
binaryFile.Seek(cnt);
#endif
char buf[5];
cnt = binaryFile.Read(buf, 4);
binaryFile.Close();
if (cnt == wxInvalidOffset || strncmp(buf, FLAC_HEADER, 4) != 0) {
// File is not a FLAC file
return false;
}
// Open the file for import
FLACImportFileHandle *handle = new FLACImportFileHandle(filename);
bool success = handle->Init();
if (!success) {
delete handle;
return NULL;
}
return handle;
}
FLACImportFileHandle::FLACImportFileHandle(const wxString & name)
: ImportFileHandle(name),
mSamplesDone(0),
mStreamInfoDone(false),
mUpdateResult(eProgressSuccess)
{
mFormat = (sampleFormat)
gPrefs->Read(wxT("/SamplingRate/DefaultProjectSampleFormat"), floatSample);
mFile = new MyFLACFile(this);
}
bool FLACImportFileHandle::Init()
{
#ifdef EXPERIMENTAL_OD_FLAC
mDecoderTask=new ODDecodeFlacTask;
ODFlacDecoder* odDecoder = (ODFlacDecoder*)mDecoderTask->CreateFileDecoder(mFilename);
if(!odDecoder || !odDecoder->ReadHeader())
{
//delete the task only if it failed to read - otherwise the OD man takes care of it.
delete mDecoderTask;
return false;
}
//copy the meta data over to the class
mSampleRate=odDecoder->mSampleRate;
mNumChannels=odDecoder->mNumChannels;
mBitsPerSample=odDecoder->mBitsPerSample;
mNumSamples=odDecoder->mNumSamples;
mBitsPerSample=odDecoder->mBitsPerSample;
mFormat=odDecoder->mFormat;
mStreamInfoDone=true;
return true;
#endif
#ifdef LEGACY_FLAC
bool success = mFile->set_filename(OSINPUT(mFilename));
if (!success) {
return false;
}
mFile->set_metadata_respond(FLAC__METADATA_TYPE_STREAMINFO);
mFile->set_metadata_respond(FLAC__METADATA_TYPE_VORBIS_COMMENT);
FLAC::Decoder::File::State state = mFile->init();
if (state != FLAC__FILE_DECODER_OK) {
return false;
}
#else
if (!mHandle.Open(mFilename, wxT("rb"))) {
return false;
}
// Even though there is an init() method that takes a filename, use the one that
// takes a file handle because wxWidgets can open a file with a Unicode name and
// libflac can't (under Windows).
//
// Responsibility for closing the file is passed to libflac.
// (it happens when mFile->finish() is called)
bool result = mFile->init(mHandle.fp())?true:false;
mHandle.Detach();
if (result != FLAC__STREAM_DECODER_INIT_STATUS_OK) {
return false;
}
#endif
mFile->process_until_end_of_metadata();
#ifdef LEGACY_FLAC
state = mFile->get_state();
if (state != FLAC__FILE_DECODER_OK) {
return false;
}
#else
// not necessary to check state, error callback will catch errors, but here's how:
if (mFile->get_state() > FLAC__STREAM_DECODER_READ_FRAME) {
return false;
}
#endif
if (!mFile->is_valid() || mFile->get_was_error()) {
// This probably is not a FLAC file at all
return false;
}
return true;
}
wxString FLACImportFileHandle::GetFileDescription()
{
return DESC;
}
int FLACImportFileHandle::GetFileUncompressedBytes()
{
// TODO: Get Uncompressed byte count.
return 0;
}
int FLACImportFileHandle::Import(TrackFactory *trackFactory,
Track ***outTracks,
int *outNumTracks,
Tags *tags)
{
wxASSERT(mStreamInfoDone);
CreateProgress();
mChannels = new WaveTrack *[mNumChannels];
unsigned long c;
for (c = 0; c < mNumChannels; c++) {
mChannels[c] = trackFactory->NewWaveTrack(mFormat, mSampleRate);
if (mNumChannels == 2) {
switch (c) {
case 0:
mChannels[c]->SetChannel(Track::LeftChannel);
mChannels[c]->SetLinked(true);
break;
case 1:
mChannels[c]->SetChannel(Track::RightChannel);
break;
}
}
else {
mChannels[c]->SetChannel(Track::MonoChannel);
}
}
//Start OD
bool useOD = false;
#ifdef EXPERIMENTAL_OD_FLAC
useOD=true;
#endif
#ifdef LEGACY_FLAC
bool res = (mFile->process_until_end_of_file() != 0);
#else
bool res = true;
if(!useOD)
res = (mFile->process_until_end_of_stream() != 0);
#endif
//add the task to the ODManager
if(useOD)
{
sampleCount fileTotalFrames = mNumSamples;
sampleCount maxBlockSize = mChannels[0]->GetMaxBlockSize();
for (sampleCount i = 0; i < fileTotalFrames; i += maxBlockSize) {
sampleCount blockLen = maxBlockSize;
if (i + blockLen > fileTotalFrames)
blockLen = fileTotalFrames - i;
for (c = 0; c < mNumChannels; c++)
mChannels[c]->AppendCoded(mFilename, i, blockLen, c,ODTask::eODFLAC);
mUpdateResult = mProgress->Update(i, fileTotalFrames);
if (mUpdateResult != eProgressSuccess)
break;
}
bool moreThanStereo = mNumChannels>2;
for (c = 0; c < mNumChannels; c++)
{
mDecoderTask->AddWaveTrack(mChannels[c]);
if(moreThanStereo)
{
//if we have 3 more channels, they get imported on seperate tracks, so we add individual tasks for each.
ODManager::Instance()->AddNewTask(mDecoderTask);
mDecoderTask=new ODDecodeFlacTask; //TODO: see if we need to use clone to keep the metadata.
}
}
//if we have mono or a linked track (stereo), we add ONE task for the one linked wave track
if(!moreThanStereo)
ODManager::Instance()->AddNewTask(mDecoderTask);
}
//END OD
if (mUpdateResult == eProgressFailed || mUpdateResult == eProgressCancelled) {
for(c = 0; c < mNumChannels; c++) {
delete mChannels[c];
}
delete[] mChannels;
return mUpdateResult;
}
*outNumTracks = mNumChannels;
*outTracks = new Track *[mNumChannels];
for (c = 0; c < mNumChannels; c++) {
mChannels[c]->Flush();
(*outTracks)[c] = mChannels[c];
}
delete[] mChannels;
tags->Clear();
size_t cnt = mFile->mComments.GetCount();
for (c = 0; c < cnt; c++) {
wxString name = mFile->mComments[c].BeforeFirst(wxT('='));
wxString value = mFile->mComments[c].AfterFirst(wxT('='));
if (name.Upper() == wxT("DATE") && !tags->HasTag(TAG_YEAR)) {
long val;
if (value.Length() == 4 && value.ToLong(&val)) {
name = TAG_YEAR;
}
}
tags->SetTag(name, value);
}
return mUpdateResult;
}
FLACImportFileHandle::~FLACImportFileHandle()
{
//don't delete mFile if we are using OD.
#ifndef EXPERIMENTAL_OD_FLAC
mFile->finish();
delete mFile;
#endif
}
#endif /* USE_LIBFLAC */

21
src/import/ImportFLAC.h Normal file
View File

@@ -0,0 +1,21 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ImportFLAC.h
Sami Liedes
**********************************************************************/
#ifndef __AUDACITY_IMPORT_FLAC__
#define __AUDACITY_IMPORT_FLAC__
class ImportPluginList;
class UnusableImportPluginList;
void GetFLACImportPlugin(ImportPluginList *importPluginList,
UnusableImportPluginList *unusableImportPluginList);
#endif

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,22 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ImportGStreamer.h
LRN
**********************************************************************/
#ifndef __AUDACITY_IMPORT_GSTREAMER__
#define __AUDACITY_IMPORT_GSTREAMER__
#include "ImportPlugin.h"
class ImportPluginList;
class UnusableImportPluginList;
void GetGStreamerImportPlugin(ImportPluginList *importPluginList,
UnusableImportPluginList *unusableImportPluginList);
#endif

496
src/import/ImportLOF.cpp Normal file
View File

@@ -0,0 +1,496 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ImportLOF.h
David I. Murray
Leland Lucius
*//****************************************************************//**
\class LOFImportFileHandle
\brief An ImportFileHandle for LOF data
Supports the opening of ".lof" files which are text files that contain
a list of individual files to open in audacity in specific formats.
(In BNF) The syntax for an LOF file, denoted by <lof>:
\verbatim
<lof> ::= [<window> | <file> | <#>]*
<window> ::= window [<window-parameter>]* <newline>
<window-parameter> ::= offset <time> | duration <time>
<time> ::= [<digit>]+ [ . [<digit>]* ]
<file> ::= file [<file-parameter>]* <newline>
<file-parameter> ::= offset <time>
<#> ::= <comment> <newline>
\endverbatim
EXAMPLE LOF file:
\verbatim
# everything following the hash character is ignored
window # an initial window command is implicit and optional
file "C:\folder1\sample1.wav" # sample1.wav is displayed
file "C:\sample2.wav" offset 5 # sample2 is displayed with a 5s offset
File "C:\sample3.wav" # sample3 is displayed with no offset
window offset 5 duration 10 # open a new window, zoom to display
# 10 seconds total starting at 5 (ending at 15) seconds
file "C:\sample3.wav" offset 2.5
\endverbatim
SEMANTICS:
There are two commands: "window" creates a new window, and "file"
appends a track to the current window and displays the file there. The
first file is always placed in a new window, whether or not an initial
"window" command is given.
Commands have optional keyword parameters that may be listed in any
order. A parameter should only occur once per command. The "offset"
parameter specifies a time offset. For windows, this is the leftmost
time displayed in the window. For files, the offset is an amount by
which the file is shifted in time before display (only enabled for audio;
not midi). The offset is specified as an integer or decimal number of
seconds, and the default value is zero.
Windows may also have a "duration" parameter, which specifies how much
time should be displayed in the window. The default duration is equal
to the duration of the longest track currently displayed.
*//****************************************************************//**
\class LOFImportPlugin
\brief An ImportPlugin for LOF data
*//*******************************************************************/
#include "../Audacity.h"
#include <wx/string.h>
#include <wx/utils.h>
#include <wx/intl.h>
#include <wx/textfile.h>
#include <wx/msgdlg.h>
#include <wx/tokenzr.h>
#include "ImportLOF.h"
#ifdef USE_MIDI
#include "ImportMIDI.h"
#endif // USE_MIDI
#include "../WaveTrack.h"
#include "ImportPlugin.h"
#include "Import.h"
#include "../Internat.h"
#include "../NoteTrack.h"
#include "../Project.h"
#include "../FileFormats.h"
#include "../Prefs.h"
#include "../WaveTrack.h"
#include "../Internat.h"
#define BINARY_FILE_CHECK_BUFFER_SIZE 1024
#define DESC _("List of Files in basic text format")
static const wxChar *exts[] =
{
wxT("lof")
};
class LOFImportPlugin : public ImportPlugin
{
public:
LOFImportPlugin()
: ImportPlugin(wxArrayString(WXSIZEOF(exts), exts))
{
}
~LOFImportPlugin() { }
wxString GetPluginFormatDescription();
ImportFileHandle *Open(wxString Filename);
};
class LOFImportFileHandle : public ImportFileHandle
{
public:
LOFImportFileHandle(const wxString & name, wxTextFile *file);
~LOFImportFileHandle();
wxString GetFileDescription();
int GetFileUncompressedBytes();
int Import(TrackFactory *trackFactory, Track ***outTracks,
int *outNumTracks, Tags *tags);
wxInt32 GetStreamCount(){ return 1; }
wxArrayString *GetStreamInfo(){ return NULL; }
void SetStreamUsage(wxInt32 StreamID, bool Use){}
private:
// Takes a line of text in lof file and interprets it and opens files
void lofOpenFiles(wxString* ln);
void doDuration();
void doScrollOffset();
wxTextFile *mTextFile;
AudacityProject *mProject;
// In order to know whether or not to create a new window
bool windowCalledOnce;
// In order to zoom in, it must be done after files are opened
bool callDurationFactor;
double durationFactor;
// In order to offset scrollbar, it must be done after files are opened
bool callScrollOffset;
double scrollOffset;
};
LOFImportFileHandle::LOFImportFileHandle(const wxString & name, wxTextFile *file)
: ImportFileHandle(name),
mTextFile(file)
{
mProject = GetActiveProject();
windowCalledOnce = false;
callDurationFactor = false;
durationFactor = 1;
callScrollOffset = false;
scrollOffset = 0;
}
void GetLOFImportPlugin(ImportPluginList *importPluginList,
UnusableImportPluginList *unusableImportPluginList)
{
importPluginList->Append(new LOFImportPlugin);
}
wxString LOFImportPlugin::GetPluginFormatDescription()
{
return DESC;
}
ImportFileHandle *LOFImportPlugin::Open(wxString filename)
{
// Check if it is a binary file
wxFile binaryFile;
if (!binaryFile.Open(filename))
return NULL; // File not found
char buf[BINARY_FILE_CHECK_BUFFER_SIZE];
int count = binaryFile.Read(buf, BINARY_FILE_CHECK_BUFFER_SIZE);
for (int i = 0; i < count; i++)
{
// Check if this char is below the space character, but not a
// line feed or carriage return
if (buf[i] < 32 && buf[i] != 10 && buf[i] != 13)
{
// Assume it is a binary file
binaryFile.Close();
return NULL;
}
}
// Close it again so it can be opened as a text file
binaryFile.Close();
// Now open the file again as text file
wxTextFile *file = new wxTextFile(filename);
file->Open();
if (!file->IsOpened())
{
delete file;
return NULL;
}
return new LOFImportFileHandle(filename, file);
}
wxString LOFImportFileHandle::GetFileDescription()
{
return DESC;
}
int LOFImportFileHandle::GetFileUncompressedBytes()
{
return 0;
}
int LOFImportFileHandle::Import(TrackFactory *trackFactory, Track ***outTracks,
int *outNumTracks, Tags *tags)
{
wxASSERT(mTextFile->IsOpened());
if(mTextFile->Eof())
{
mTextFile->Close();
return eProgressFailed;
}
wxString line = mTextFile->GetFirstLine();
while (!mTextFile->Eof())
{
lofOpenFiles(&line);
line = mTextFile->GetNextLine();
}
// for last line
lofOpenFiles(&line);
// set any duration/offset factors for last window, as all files were called
doDuration();
doScrollOffset();
// exited ok
if(mTextFile->Close())
return eProgressSuccess;
return eProgressFailed;
}
static int CountNumTracks(AudacityProject *proj)
{
int count = 0;
Track *t;
TrackListIterator iter(proj->GetTracks());
t = iter.First();
while(t) {
count++;
t = iter.Next();
}
return count;
}
void LOFImportFileHandle::lofOpenFiles(wxString* ln)
{
wxStringTokenizer tok(*ln, wxT(" "));
wxStringTokenizer temptok1(*ln, wxT("\""));
wxStringTokenizer temptok2(*ln, wxT(" "));
int tokenplace = 0;
wxString targetfile;
wxString tokenholder = tok.GetNextToken();
if (tokenholder.IsSameAs(wxT("window"), false))
{
// set any duration/offset factors for last window, as all files were called
doDuration();
doScrollOffset();
if (windowCalledOnce)
{
mProject = CreateNewAudacityProject();
}
windowCalledOnce = true;
while (tok.HasMoreTokens())
{
tokenholder = tok.GetNextToken();
if (tokenholder.IsSameAs(wxT("offset"), false))
{
if (tok.HasMoreTokens())
tokenholder = tok.GetNextToken();
if (Internat::CompatibleToDouble(tokenholder, &scrollOffset))
{
callScrollOffset = true;
}
else
{
/* i18n-hint: You do not need to translate "LOF" */
wxMessageBox(_("Invalid window offset in LOF file."),
/* i18n-hint: You do not need to translate "LOF" */
_("LOF Error"), wxOK | wxCENTRE);
}
if (tok.HasMoreTokens())
tokenholder = tok.GetNextToken();
}
if (tokenholder.IsSameAs(wxT("duration"), false))
{
if (tok.HasMoreTokens())
tokenholder = tok.GetNextToken();
if (Internat::CompatibleToDouble(tokenholder, &durationFactor))
{
callDurationFactor = true;
}
else
{
/* i18n-hint: You do not need to translate "LOF" */
wxMessageBox(_("Invalid duration in LOF file."),
/* i18n-hint: You do not need to translate "LOF" */
_("LOF Error"), wxOK | wxCENTRE);
}
} // End if statement
if (tokenholder.IsSameAs(wxT("#")))
{
// # indicates comments; ignore line
tok = wxStringTokenizer(wxT(""), wxT(" "));
}
} // End while loop
} // End if statement
else if (tokenholder.IsSameAs(wxT("file"), false))
{
// To identify filename and open it
tokenholder = temptok1.GetNextToken();
targetfile = temptok1.GetNextToken();
#ifdef USE_MIDI
// If file is a midi
if (targetfile.AfterLast(wxT('.')).IsSameAs(wxT("mid"), false)
|| targetfile.AfterLast(wxT('.')).IsSameAs(wxT("midi"), false))
{
NoteTrack *nTrack = new NoteTrack(mProject->GetDirManager());
if (::ImportMIDI(targetfile, nTrack))
mProject->GetTracks()->Add(nTrack);
else
delete nTrack;
}
// If not a midi, open audio file
else
{
#else // !USE_MIDI
/* if we don't have midi support, go straight on to opening as an
* audio file. TODO: Some sort of message here? */
{
#endif // USE_MIDI
mProject->OpenFile(targetfile);
}
// Set tok to right after filename
temptok2.SetString(targetfile);
tokenplace = temptok2.CountTokens();
for (int i = 0; i < tokenplace; i++)
tokenholder = tok.GetNextToken();
if (tok.HasMoreTokens())
{
tokenholder = tok.GetNextToken();
if (tokenholder.IsSameAs(wxT("#")))
{
// # indicates comments; ignore line
tok = wxStringTokenizer(wxT(""), wxT(" "));
}
if (tokenholder.IsSameAs(wxT("offset"), false))
{
if (tok.HasMoreTokens())
tokenholder = tok.GetNextToken();
double offset;
if (Internat::CompatibleToDouble(tokenholder, &offset))
{
Track *t;
TrackListIterator iter(mProject->GetTracks());
t = iter.First();
for (int i = 1; i < CountNumTracks(mProject) - 1; i++)
t = iter.Next();
#ifdef USE_MIDI
if (targetfile.AfterLast(wxT('.')).IsSameAs(wxT("mid"), false) ||
targetfile.AfterLast(wxT('.')).IsSameAs(wxT("midi"), false))
{
wxMessageBox(_("MIDI tracks cannot be offset individually, only audio files can be."),
_("LOF Error"), wxOK | wxCENTRE);
}
else
#endif
{
if (CountNumTracks(mProject) == 1)
t->SetOffset(offset);
else
{
if (t->GetLinked())
t->SetOffset(offset);
t = iter.Next();
t->SetOffset(offset);
}
}
}
else
{
/* i18n-hint: You do not need to translate "LOF" */
wxMessageBox(_("Invalid track offset in LOF file."),
_("LOF Error"), wxOK | wxCENTRE);
}
} // End if statement
} // End if statement
} // End if statement
else if (tokenholder.IsSameAs(wxT("#")))
{
// # indicates comments; ignore line
tok = wxStringTokenizer(wxT(""), wxT(" "));
}
else
{
// Couldn't parse a line
}
}
void LOFImportFileHandle::doDuration()
{
if (callDurationFactor)
{
double longestDuration = mProject->GetTracks()->GetEndTime();
double realZoomValue = ((longestDuration/durationFactor)*(mProject->GetZoom()));
mProject->Zoom(realZoomValue);
callDurationFactor = false;
}
}
void LOFImportFileHandle::doScrollOffset()
{
if (callScrollOffset && (scrollOffset != 0))
{
mProject->TP_ScrollWindow(scrollOffset);
callScrollOffset = false;
}
}
LOFImportFileHandle::~LOFImportFileHandle()
{
if(mTextFile)
{
if (mTextFile->IsOpened())
mTextFile->Close();
delete mTextFile;
}
}
// 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: c29bc0ff-c8ca-470d-913e-02a465248052

75
src/import/ImportLOF.h Normal file
View File

@@ -0,0 +1,75 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ImportLOF.h
David I. Murray
Supports the opening of ".lof" files which are text files that contain
a list of individual files to open in audacity in specific formats.
(In BNF) The syntax for an LOF file, denoted by <lof>:
<lof> ::= [<window> | <file> | <#>]*
<window> ::= window [<window-parameter>]* <newline>
<window-parameter> ::= offset <time> | duration <time>
<time> ::= [<digit>]+ [ . [<digit>]* ]
<file> ::= file [<file-parameter>]* <newline>
<file-parameter> ::= offset <time>
<#> ::= <comment> <newline>
EXAMPLE LOF file:
# everything following the hash character is ignored
window # an initial window command is implicit and optional
file "C:\folder1\sample1.wav" # sample1.wav is displayed
file "C:\sample2.wav" offset 5 # sample2 is displayed with a 5s offset
File "C:\sample3.wav" # sample3 is displayed with no offset
window offset 5 duration 10 # open a new window, zoom to display
# 10 seconds total starting at 5 (ending at 15) seconds
file "C:\sample3.wav" offset 2.5
SEMANTICS:
There are two commands: "window" creates a new window, and "file"
appends a track to the current window and displays the file there. The
first file is always placed in a new window, whether or not an initial
"window" command is given.
Commands have optional keyword parameters that may be listed in any
order. A parameter should only occur once per command. The "offset"
parameter specifies a time offset. For windows, this is the leftmost
time displayed in the window. For files, the offset is an amount by
which the file is shifted in time before display (only enabled for audio;
not midi). The offset is specified as an integer or decimal number of
seconds, and the default value is zero.
Windows may also have a "duration" parameter, which specifies how much
time should be displayed in the window. The default duration is equal
to the duration of the longest track currently displayed.
**********************************************************************/
#ifndef __AUDACITY_IMPORT_LOF__
#define __AUDACITY_IMPORT_LOF__
class ImportPluginList;
class UnusableImportPluginList;
void GetLOFImportPlugin(ImportPluginList *importPluginList,
UnusableImportPluginList *unusableImportPluginList);
#endif
// 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: ada45755-0dff-4ff6-ab5e-5aa864fcf55b

75
src/import/ImportMIDI.cpp Normal file
View File

@@ -0,0 +1,75 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ImportMIDI.cpp
Dominic Mazzoni
**********************************************************************/
#include <wx/defs.h>
#include <wx/msgdlg.h>
#include <wx/ffile.h>
#include <wx/intl.h>
#include "../Audacity.h"
#if defined(USE_MIDI)
#include "../Internat.h"
#include "../NoteTrack.h"
#include "ImportMIDI.h"
#include "allegro.h"
#include "strparse.h"
#include "mfmidi.h"
bool ImportMIDI(wxString fName, NoteTrack * dest)
{
if (fName.Length() <= 4){
wxMessageBox( _("Could not open file ") + fName + _(": Filename too short."));
return false;
}
bool is_midi = false;
if (fName.Right(4).CmpNoCase(wxT(".mid")) == 0 || fName.Right(5).CmpNoCase(wxT(".midi")) == 0)
is_midi = true;
else if(fName.Right(4).CmpNoCase(wxT(".gro")) != 0) {
wxMessageBox( _("Could not open file ") + fName + _(": Incorrect filetype."));
return false;
}
wxFFile mf(fName, wxT("rb"));
if (!mf.IsOpened()) {
wxMessageBox( _("Could not open file ") + fName + wxT("."));
return false;
}
Alg_seq_ptr new_seq = new Alg_seq(fName.mb_str(), is_midi);
//Should we also check if(seq->tracks() == 0) ?
if(new_seq->get_read_error() == alg_error_open){
wxMessageBox( _("Could not open file ") + fName + wxT("."));
mf.Close();
return false;
}
dest->SetSequence(new_seq);
mf.Close();
return true;
}
#endif
// 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: 391b08e6-61f4-43ea-8431-c835c31ba86d

125
src/import/ImportMIDI.h Normal file
View File

@@ -0,0 +1,125 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ImportMIDI.h
Dominic Mazzoni
*******************************************************************//**
\class MIDIParser
\brief Unused class that might someday be used again to read MIDI files
into a NoteTrack.
*//*******************************************************************/
#ifndef _IMPORT_MIDI_
#define _IMPORT_MIDI_
#include "../Audacity.h"
#if defined(USE_MIDI)
class NoteTrack;
bool ImportMIDI(wxString fName, NoteTrack * dest);
class MIDIParser {
public:
MIDIParser();
void Parse();
unsigned char *buffer;
int bufferLen;
int index;
NoteTrack *noteTrack;
int midifile_error;
int keys[16][128];
int division;
double tempo;
private:
int check_aborted(void);
int Mf_getc(void);
void Mf_error(char *);
void Mf_on(int, int, int);
void Mf_off(int, int, int);
void Mf_header(int, int, int);
void Mf_tempo(int);
void Mf_starttrack(void) {}
void Mf_endtrack(void) {}
void Mf_eot(void) {}
void Mf_pressure(int, int, int) {}
void Mf_controller(int, int, int) {}
void Mf_pitchbend(int, int, int) {}
void Mf_program(int, int) {}
void Mf_chanpressure(int, int) {}
void Mf_sysex(int, char *) {}
void Mf_arbitrary(int, char *) {}
void Mf_metamisc(int, int, char *) {}
void Mf_seqnum(int) {}
void Mf_smpte(int, int, int, int, int) {}
void Mf_timesig(int, int, int, int) {}
void Mf_keysig(int, int) {}
void Mf_sqspecific(int, char *) {}
void Mf_text(int, int, char *) {}
int Mf_nomerge; /* 1 => continue'ed system exclusives are */
/* not collapsed. */
long Mf_currtime; /* current time in delta-time units */
int Mf_skipinit;
long Mf_toberead;
int abort_flag;
long readvarinum(void);
long read32bit(void);
int read16bit(void);
void msgenlarge(void);
char *msg(void);
int readheader(void);
void readtrack(void);
void sysex(void);
void msginit(void);
int egetc(void);
int msgleng(void);
int readmt(char *, int);
long to32bit(int, int, int, int);
int to16bit(int, int);
void mferror(char *);
void badbyte(int);
void metaevent(int);
void msgadd(int);
void chanmessage(int, int, int);
char *Msgbuff; /* message buffer */
int Msgsize; /* Size of currently allocated Msg */
int Msgindex; /* index of next available location in Msg */
};
#endif
#endif
// 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: 1c1567af-94d4-498f-aa80-5eaa570021b5

559
src/import/ImportMP3.cpp Normal file
View File

@@ -0,0 +1,559 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ImportMP3.cpp
Joshua Haberman
Leland Lucius
*//****************************************************************//**
\class MP3ImportFileHandle
\brief An ImportFileHandle for MP3 data
Audacity has finally moved to using a single mp3 library on all
platforms! It is the high performance, beautifully written libmad
(mpeg audio decoder). Finally there is harmony in the mp3 universe.
Much of this source code is based on 'minimad.c' as distributed
with libmad.
*//****************************************************************//**
\class MP3ImportPlugin
\brief An ImportPlugin for MP3 data
*//*******************************************************************/
// For compilers that support precompilation, includes "wx/wx.h".
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/window.h>
#endif
#include <wx/defs.h>
#include <wx/intl.h>
#include "../Audacity.h"
#include "../Prefs.h"
#include "Import.h"
#include "ImportMP3.h"
#include "ImportPlugin.h"
#include "../Internat.h"
#include "../Tags.h"
#define DESC _("MP3 files")
static const wxChar *exts[] =
{
wxT("mp3"),
wxT("mp2"),
wxT("mpa")
};
#ifndef USE_LIBMAD
void GetMP3ImportPlugin(ImportPluginList *importPluginList,
UnusableImportPluginList *unusableImportPluginList)
{
UnusableImportPlugin* mp3IsUnsupported =
new UnusableImportPlugin(DESC, wxArrayString(WXSIZEOF(exts), exts));
unusableImportPluginList->Append(mp3IsUnsupported);
}
#else /* USE_LIBMAD */
#include <wx/textctrl.h>
#include <wx/file.h>
#include <wx/thread.h>
#include <wx/msgdlg.h>
#include <wx/progdlg.h>
#include <wx/string.h>
#include <wx/timer.h>
#include <wx/intl.h>
extern "C" {
#include "mad.h"
#ifdef USE_LIBID3TAG
#include <id3tag.h>
#endif
}
#include "../WaveTrack.h"
#define INPUT_BUFFER_SIZE 65535
#define PROGRESS_SCALING_FACTOR 100000
/* this is a private structure we can use for whatever we like, and it will get
* passed to each of the callback routines, allowing us to keep track of
* things. */
struct private_data {
wxFile *file; /* the file containing the mp3 data we're feeding the encoder */
unsigned char *inputBuffer;
TrackFactory *trackFactory;
WaveTrack **channels;
ProgressDialog *progress;
int numChannels;
int updateResult;
bool id3checked;
};
class MP3ImportPlugin : public ImportPlugin
{
public:
MP3ImportPlugin():
ImportPlugin(wxArrayString(WXSIZEOF(exts), exts))
{
}
~MP3ImportPlugin() { }
wxString GetPluginFormatDescription();
ImportFileHandle *Open(wxString Filename);
};
class MP3ImportFileHandle : public ImportFileHandle
{
public:
MP3ImportFileHandle(wxFile *file, wxString filename):
ImportFileHandle(filename),
mFile(file)
{
}
~MP3ImportFileHandle();
wxString GetFileDescription();
int GetFileUncompressedBytes();
int Import(TrackFactory *trackFactory, Track ***outTracks,
int *outNumTracks, Tags *tags);
wxInt32 GetStreamCount(){ return 1; }
wxArrayString *GetStreamInfo(){ return NULL; }
void SetStreamUsage(wxInt32 StreamID, bool Use){}
private:
void ImportID3(Tags *tags);
wxFile *mFile;
void *mUserData;
struct private_data mPrivateData;
mad_decoder mDecoder;
};
void GetMP3ImportPlugin(ImportPluginList *importPluginList,
UnusableImportPluginList *unusableImportPluginList)
{
importPluginList->Append(new MP3ImportPlugin);
}
/* The MAD callbacks */
enum mad_flow input_cb(void *_data, struct mad_stream *stream);
enum mad_flow output_cb(void *_data,
struct mad_header const *header,
struct mad_pcm *pcm);
enum mad_flow error_cb(void *_data, struct mad_stream *stream,
struct mad_frame *frame);
/* convert libmad's fixed point representation to 16 bit signed integers. This
* code is taken verbatim from minimad.c. */
inline float scale(mad_fixed_t sample)
{
return (float) (sample / (float) (1L << MAD_F_FRACBITS));
}
wxString MP3ImportPlugin::GetPluginFormatDescription()
{
return DESC;
}
ImportFileHandle *MP3ImportPlugin::Open(wxString Filename)
{
wxFile *file = new wxFile(Filename);
if (!file->IsOpened()) {
delete file;
return NULL;
}
/* There's no way to tell if this is a valid mp3 file before actually
* decoding, so we return a valid FileHandle. */
return new MP3ImportFileHandle(file, Filename);
}
wxString MP3ImportFileHandle::GetFileDescription()
{
return DESC;
}
int MP3ImportFileHandle::GetFileUncompressedBytes()
{
// TODO
return 0;
}
int MP3ImportFileHandle::Import(TrackFactory *trackFactory, Track ***outTracks,
int *outNumTracks, Tags *tags)
{
int chn;
CreateProgress();
/* Prepare decoder data, initialize decoder */
mPrivateData.file = mFile;
mPrivateData.inputBuffer = new unsigned char [INPUT_BUFFER_SIZE];
mPrivateData.progress = mProgress;
mPrivateData.channels = NULL;
mPrivateData.updateResult= eProgressSuccess;
mPrivateData.id3checked = false;
mPrivateData.numChannels = 0;
mPrivateData.trackFactory= trackFactory;
mad_decoder_init(&mDecoder, &mPrivateData, input_cb, 0, 0, output_cb, error_cb, 0);
/* and send the decoder on its way! */
bool res = (mad_decoder_run(&mDecoder, MAD_DECODER_MODE_SYNC) == 0) &&
(mPrivateData.numChannels > 0) &&
!(mPrivateData.updateResult == eProgressCancelled) &&
!(mPrivateData.updateResult == eProgressFailed);
mad_decoder_finish(&mDecoder);
delete[] mPrivateData.inputBuffer;
if (!res) {
/* failure */
/* printf("failure\n"); */
/* delete everything */
for (chn = 0; chn < mPrivateData.numChannels; chn++) {
delete mPrivateData.channels[chn];
}
delete[] mPrivateData.channels;
return (mPrivateData.updateResult);
}
/* success */
/* printf("success\n"); */
/* copy the WaveTrack pointers into the Track pointer list that
* we are expected to fill */
*outNumTracks = mPrivateData.numChannels;
*outTracks = new Track* [mPrivateData.numChannels];
for(chn = 0; chn < mPrivateData.numChannels; chn++) {
mPrivateData.channels[chn]->Flush();
(*outTracks)[chn] = mPrivateData.channels[chn];
}
delete[] mPrivateData.channels;
/* Read in any metadata */
ImportID3(tags);
return mPrivateData.updateResult;
}
MP3ImportFileHandle::~MP3ImportFileHandle()
{
if(mFile) {
if (mFile->IsOpened()) {
mFile->Close();
}
delete mFile;
}
}
void MP3ImportFileHandle::ImportID3(Tags *tags)
{
#ifdef USE_LIBID3TAG
wxFile f; // will be closed when it goes out of scope
struct id3_file *fp = NULL;
if (f.Open(mFilename)) {
// Use id3_file_fdopen() instead of id3_file_open since wxWidgets can open a
// file with a Unicode name and id3_file_open() can't (under Windows).
fp = id3_file_fdopen(f.fd(), ID3_FILE_MODE_READONLY);
}
if (!fp) {
return;
}
// The file descriptor is now owned by "fp", so we must tell "f" to forget
// about it.
f.Detach();
struct id3_tag *tp = id3_file_tag(fp);
if (!tp) {
id3_file_close(fp);
return;
}
tags->Clear();
tags->SetID3V2( tp->options & ID3_TAG_OPTION_ID3V1 ? false : true );
// Loop through all frames
for (int i = 0; i < (int) tp->nframes; i++) {
struct id3_frame *frame = tp->frames[i];
// printf("ID: %08x '%4s'\n", (int) *(int *)frame->id, frame->id);
// printf("Desc: %s\n", frame->description);
// printf("Num fields: %d\n", frame->nfields);
// for (int j = 0; j < (int) frame->nfields; j++) {
// printf("field %d type %d\n", j, frame->fields[j].type );
// if (frame->fields[j].type == ID3_FIELD_TYPE_STRINGLIST) {
// printf("num strings %d\n", frame->fields[j].stringlist.nstrings);
// }
// }
wxString n, v;
// Determine the tag name
if (strcmp(frame->id, ID3_FRAME_TITLE) == 0) {
n = TAG_TITLE;
}
else if (strcmp(frame->id, ID3_FRAME_ARTIST) == 0) {
n = TAG_ARTIST;
}
else if (strcmp(frame->id, ID3_FRAME_ALBUM) == 0) {
n = TAG_ALBUM;
}
else if (strcmp(frame->id, ID3_FRAME_TRACK) == 0) {
n = TAG_TRACK;
}
else if (strcmp(frame->id, "TYER") == 0) {
n = TAG_YEAR;
}
else if (strcmp(frame->id, ID3_FRAME_YEAR) == 0) {
n = TAG_YEAR;
}
else if (strcmp(frame->id, ID3_FRAME_COMMENT) == 0) {
n = TAG_COMMENTS;
}
else if (strcmp(frame->id, ID3_FRAME_GENRE) == 0) {
n = TAG_GENRE;
}
else {
// Use frame description as default tag name. The descriptions
// may include several "meanings" separated by "/" characters, so
// we just use the first meaning
n = UTF8CTOWX(frame->description).BeforeFirst(wxT('/'));
}
const id3_ucs4_t *ustr = NULL;
if (n == TAG_COMMENTS) {
ustr = id3_field_getfullstring(&frame->fields[3]);
}
else if (frame->nfields == 3) {
ustr = id3_field_getstring(&frame->fields[1]);
if (ustr) {
char *str = (char *)id3_ucs4_utf8duplicate(ustr);
n = UTF8CTOWX(str);
free(str);
}
ustr = id3_field_getstring(&frame->fields[2]);
}
else if (frame->nfields >= 2) {
ustr = id3_field_getstrings(&frame->fields[1], 0);
}
if (ustr) {
char *str = (char *)id3_ucs4_utf8duplicate(ustr);
v = UTF8CTOWX(str);
free(str);
}
if (!n.IsEmpty() && !v.IsEmpty()) {
tags->SetTag(n, v);
}
}
// Convert v1 genre to name
if (tags->HasTag(TAG_GENRE)) {
long g = -1;
if (tags->GetTag(TAG_GENRE).ToLong(&g)) {
tags->SetTag(TAG_GENRE, tags->GetGenre(g));
}
}
id3_file_close(fp);
#endif // ifdef USE_LIBID3TAG
}
//
// MAD Callbacks
//
/* The input callback is called when the decoder wants more data. */
enum mad_flow input_cb(void *_data, struct mad_stream *stream)
{
struct private_data *data = (struct private_data *)_data;
data->updateResult = data->progress->Update((wxULongLong_t)data->file->Tell(),
(wxULongLong_t)data->file->Length());
if(data->updateResult != eProgressSuccess)
return MAD_FLOW_STOP;
if(data->file->Eof()) {
return MAD_FLOW_STOP;
}
#ifdef USE_LIBID3TAG
if (!data->id3checked) {
data->file->Read(data->inputBuffer, ID3_TAG_QUERYSIZE);
int len = id3_tag_query(data->inputBuffer, ID3_TAG_QUERYSIZE);
if (len > 0) {
data->file->Seek(len, wxFromStart);
}
else {
data->file->Seek(0);
}
data->id3checked = true;
}
#endif
/* "Each time you refill your buffer, you need to preserve the data in
* your existing buffer from stream.next_frame to the end.
*
* This usually amounts to calling memmove() on this unconsumed portion
* of the buffer and appending new data after it, before calling
* mad_stream_buffer()"
* -- Rob Leslie, on the mad-dev mailing list */
unsigned int unconsumedBytes;
if(stream->next_frame) {
unconsumedBytes = data->inputBuffer + INPUT_BUFFER_SIZE - stream->next_frame;
memmove(data->inputBuffer, stream->next_frame, unconsumedBytes);
}
else
unconsumedBytes = 0;
off_t read = data->file->Read(data->inputBuffer + unconsumedBytes,
INPUT_BUFFER_SIZE - unconsumedBytes);
mad_stream_buffer(stream, data->inputBuffer, read + unconsumedBytes);
return MAD_FLOW_CONTINUE;
}
/* The output callback is called every time the decoder has finished decoding
* a frame, allowing us to use the decoded data */
enum mad_flow output_cb(void *_data,
struct mad_header const *header,
struct mad_pcm *pcm)
{
int channels, samplerate;
sampleCount samples;
struct private_data *data = (struct private_data *)_data;
int chn, smpl;
samplerate= pcm->samplerate;
channels = pcm->channels;
samples = pcm->length;
/* If this is the first run, we need to create the WaveTracks that
* will hold the data. We do this now because now is the first
* moment when we know how many channels there are. */
if(!data->channels) {
data->channels = new WaveTrack* [channels];
sampleFormat format = (sampleFormat) gPrefs->
Read(wxT("/SamplingRate/DefaultProjectSampleFormat"), floatSample);
for(chn = 0; chn < channels; chn++) {
data->channels[chn] = data->trackFactory->NewWaveTrack(format, samplerate);
data->channels[chn]->SetChannel(Track::MonoChannel);
}
/* special case: 2 channels is understood to be stereo */
if(channels == 2) {
data->channels[0]->SetChannel(Track::LeftChannel);
data->channels[1]->SetChannel(Track::RightChannel);
data->channels[0]->SetLinked(true);
}
data->numChannels = channels;
}
else {
// This is not the first run, protect us from libmad glitching
// on the number of channels
channels = data->numChannels;
}
/* TODO: get rid of this by adding fixed-point support to SampleFormat.
* For now, we allocate temporary float buffers to convert the fixed
* point samples into something we can feed to the WaveTrack. Allocating
* big blocks of data like this isn't a great idea, but it's temporary.
*/
float **channelBuffers = new float* [channels];
for(chn = 0; chn < channels; chn++)
channelBuffers[chn] = new float [samples];
for(smpl = 0; smpl < samples; smpl++)
for(chn = 0; chn < channels; chn++)
channelBuffers[chn][smpl] = scale(pcm->samples[chn][smpl]);
for(chn = 0; chn < channels; chn++)
data->channels[chn]->Append((samplePtr)channelBuffers[chn],
floatSample,
samples);
for(chn = 0; chn < channels; chn++)
delete[] channelBuffers[chn];
delete[] channelBuffers;
return MAD_FLOW_CONTINUE;
}
enum mad_flow error_cb(void *_data, struct mad_stream *stream,
struct mad_frame *frame)
{
/* enum mad_flow {
MAD_FLOW_CONTINUE = 0x0000,
MAD_FLOW_STOP = 0x0010,
MAD_FLOW_BREAK = 0x0011,
MAD_FLOW_IGNORE = 0x0020
}; */
/*
printf("decoding error 0x%04x (%s)\n",
stream->error, mad_stream_errorstr(stream));
*/
return MAD_FLOW_CONTINUE;
/* return MAD_FLOW_BREAK; */
}
#endif /* defined(USE_LIBMAD) */
// 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: 7730c4d7-06d3-4c4e-b558-d303a19ee8f0

32
src/import/ImportMP3.h Normal file
View File

@@ -0,0 +1,32 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ImportMP3.h
Dominic Mazzoni
**********************************************************************/
#ifndef __AUDACITY_IMPORT_MP3__
#define __AUDACITY_IMPORT_MP3__
class ImportPluginList;
class UnusableImportPluginList;
void GetMP3ImportPlugin(ImportPluginList *importPluginList,
UnusableImportPluginList *unusableImportPluginList);
#endif
// 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: b49a7d0f-9f48-49b9-a743-98e159507187

424
src/import/ImportOGG.cpp Normal file
View File

@@ -0,0 +1,424 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ImportOGG.cpp
Joshua Haberman
Leland Lucius
*//****************************************************************//**
\class ImportFileHandle
\brief An ImportFileHandle for data
The Ogg format supports multiple logical bitstreams that can be chained
within the physical bitstream. The sampling rate and number of channels
can vary between these logical bitstreams. For the moment, we'll ignore
all but the first logical bitstream.
Ogg also allows for an arbitrary number of channels. Luckily, so does
Audacity. We'll call the first channel LeftChannel, the second
RightChannel, and all others after it MonoChannel.
*//****************************************************************//**
\class OGGImportPlugin
\brief An ImportPlugin for OGG data
*//*******************************************************************/
// For compilers that support precompilation, includes "wx/wx.h".
#include <wx/wxprec.h>
#ifndef WX_PRECOMP
#include <wx/window.h>
#endif
#include <wx/intl.h>
#include "../Audacity.h"
#include "ImportOGG.h"
#include "../Internat.h"
#include "../Tags.h"
#define DESC _("Ogg Vorbis files")
static const wxChar *exts[] =
{
wxT("ogg")
};
#ifndef USE_LIBVORBIS
/* BPF There is no real reason to compile without LIBVORBIS, but if you do, you will needs this header */
#include "ImportPlugin.h"
void GetOGGImportPlugin(ImportPluginList *importPluginList,
UnusableImportPluginList *unusableImportPluginList)
{
UnusableImportPlugin* oggIsUnsupported =
new UnusableImportPlugin(DESC, wxArrayString(WXSIZEOF(exts), exts));
unusableImportPluginList->Append(oggIsUnsupported);
}
#else /* USE_LIBVORBIS */
#include <wx/string.h>
#include <wx/utils.h>
#include <wx/intl.h>
/* ffile.h must be included AFTER at least one other wx header that includes
* wx/setup.h, otherwise #ifdefs erronously collapse it to nothing. This is
* a bug in wxWidgets (ffile.h should itself include wx/setup.h), and it
* was a bitch to track down. */
#include <wx/ffile.h>
#include <vorbis/vorbisfile.h>
#include "../WaveTrack.h"
#include "ImportPlugin.h"
class OggImportPlugin : public ImportPlugin
{
public:
OggImportPlugin()
: ImportPlugin(wxArrayString(WXSIZEOF(exts), exts))
{
}
~OggImportPlugin() { }
wxString GetPluginFormatDescription();
ImportFileHandle *Open(wxString Filename);
};
class OggImportFileHandle : public ImportFileHandle
{
public:
OggImportFileHandle(const wxString & filename,
wxFFile *file,
OggVorbis_File *vorbisFile)
: ImportFileHandle(filename),
mFile(file),
mVorbisFile(vorbisFile)
{
mStreamInfo = new wxArrayString();
mStreamUsage = new int[vorbisFile->links];
for (int i = 0; i < vorbisFile->links; i++)
{
wxString strinfo;
strinfo.Printf(wxT("Index[%02x] Version[%d], Channels[%d], Rate[%d]"),i,vorbisFile->vi[i].version,vorbisFile->vi[i].channels,vorbisFile->vi[i].rate);
mStreamInfo->Add(strinfo);
mStreamUsage[i] = 0;
}
}
~OggImportFileHandle();
wxString GetFileDescription();
int GetFileUncompressedBytes();
int Import(TrackFactory *trackFactory, Track ***outTracks,
int *outNumTracks, Tags *tags);
wxInt32 GetStreamCount()
{
if (mVorbisFile)
return mVorbisFile->links;
else
return 0;
}
wxArrayString *GetStreamInfo()
{
return mStreamInfo;
}
void SetStreamUsage(wxInt32 StreamID, bool Use)
{
if (mVorbisFile)
{
if (StreamID < mVorbisFile->links)
mStreamUsage[StreamID] = (Use ? 1 : 0);
}
}
private:
wxFFile *mFile;
OggVorbis_File *mVorbisFile;
int *mStreamUsage;
wxArrayString *mStreamInfo;
WaveTrack ***mChannels;
};
void GetOGGImportPlugin(ImportPluginList *importPluginList,
UnusableImportPluginList *unusableImportPluginList)
{
importPluginList->Append(new OggImportPlugin);
}
wxString OggImportPlugin::GetPluginFormatDescription()
{
return DESC;
}
ImportFileHandle *OggImportPlugin::Open(wxString filename)
{
OggVorbis_File *vorbisFile = new OggVorbis_File;
wxFFile *file = new wxFFile(filename, wxT("rb"));
if (!file->IsOpened()) {
// No need for a message box, it's done automatically (but how?)
delete vorbisFile;
delete file;
return NULL;
}
int err = ov_open(file->fp(), vorbisFile, NULL, 0);
if (err < 0) {
wxString message;
switch (err) {
case OV_EREAD:
message = _("Media read error");
break;
case OV_ENOTVORBIS:
message = _("Not an Ogg Vorbis file");
break;
case OV_EVERSION:
message = _("Vorbis version mismatch");
break;
case OV_EBADHEADER:
message = _("Invalid Vorbis bitstream header");
break;
case OV_EFAULT:
message = _("Internal logic fault");
break;
}
// what to do with message?
file->Close();
delete vorbisFile;
delete file;
return NULL;
}
return new OggImportFileHandle(filename, file, vorbisFile);
}
wxString OggImportFileHandle::GetFileDescription()
{
return DESC;
}
int OggImportFileHandle::GetFileUncompressedBytes()
{
// TODO:
return 0;
}
int OggImportFileHandle::Import(TrackFactory *trackFactory, Track ***outTracks,
int *outNumTracks, Tags *tags)
{
wxASSERT(mFile->IsOpened());
CreateProgress();
//Number of streams used may be less than mVorbisFile->links,
//but this way bitstream matches array index.
mChannels = new WaveTrack **[mVorbisFile->links];
int i,c;
for (i = 0; i < mVorbisFile->links; i++)
{
//Stream is not used
if (mStreamUsage[i] == 0)
{
//This is just a padding to keep bitstream number and
//array indices matched.
mChannels[i] = NULL;
continue;
}
vorbis_info *vi = ov_info(mVorbisFile, i);
vorbis_comment *vc = ov_comment(mVorbisFile, i);
mChannels[i] = new WaveTrack *[vi->channels];
for (c = 0; c < vi->channels; c++) {
mChannels[i][c] = trackFactory->NewWaveTrack(int16Sample, vi->rate);
if (vi->channels == 2) {
switch (c) {
case 0:
mChannels[i][c]->SetChannel(Track::LeftChannel);
mChannels[i][c]->SetLinked(true);
break;
case 1:
mChannels[i][c]->SetChannel(Track::RightChannel);
break;
}
}
else {
mChannels[i][c]->SetChannel(Track::MonoChannel);
}
}
}
/* The number of bytes to get from the codec in each run */
#define CODEC_TRANSFER_SIZE 4096
/* The number of samples to read between calls to the callback.
* Balance between responsiveness of the GUI and throughput of import. */
#define SAMPLES_PER_CALLBACK 100000
short *mainBuffer = new short[CODEC_TRANSFER_SIZE];
/* determine endianness (clever trick courtesy of Nicholas Devillard,
* (http://www.eso.org/~ndevilla/endian/) */
int testvar = 1, endian;
if(*(char *)&testvar)
endian = 0; // little endian
else
endian = 1; // big endian
/* number of samples currently in each channel's buffer */
int updateResult = eProgressSuccess;
long bytesRead = 0;
long samplesRead = 0;
int bitstream = 0;
int samplesSinceLastCallback = 0;
// You would think that the stream would already be seeked to 0, and
// indeed it is if the file is legit. But I had several ogg files on
// my hard drive that have malformed headers, and this added call
// causes them to be read correctly. Otherwise they have lots of
// zeros inserted at the beginning
ov_pcm_seek(mVorbisFile, 0);
do {
/* get data from the decoder */
bytesRead = ov_read(mVorbisFile, (char *) mainBuffer,
CODEC_TRANSFER_SIZE,
endian,
2, // word length (2 for 16 bit samples)
1, // signed
&bitstream);
if (bytesRead < 0) {
/* Malformed Ogg Vorbis file. */
/* TODO: Return some sort of meaningful error. */
break;
}
samplesRead = bytesRead / mVorbisFile->vi[bitstream].channels / sizeof(short);
/* give the data to the wavetracks */
if (mStreamUsage[bitstream] != 0)
{
for (c = 0; c < mVorbisFile->vi[bitstream].channels; c++)
mChannels[bitstream][c]->Append((char *)(mainBuffer + c),
int16Sample,
samplesRead,
mVorbisFile->vi[bitstream].channels);
}
samplesSinceLastCallback += samplesRead;
if (samplesSinceLastCallback > SAMPLES_PER_CALLBACK) {
updateResult = mProgress->Update(ov_time_tell(mVorbisFile),
ov_time_total(mVorbisFile, bitstream));
samplesSinceLastCallback -= SAMPLES_PER_CALLBACK;
}
} while (updateResult == eProgressSuccess && bytesRead != 0);
delete[]mainBuffer;
int res = updateResult;
if (bytesRead < 0)
res = eProgressFailed;
if (res == eProgressFailed || res == eProgressCancelled) {
for (i = 0; i < mVorbisFile->links; i++)
{
if (mChannels[i])
{
for(c = 0; c < mVorbisFile->vi[bitstream].channels; c++) {
if (mChannels[i][c])
delete mChannels[i][c];
}
delete[] mChannels[i];
}
}
delete[] mChannels;
return res;
}
*outNumTracks = 0;
for (int s = 0; s < mVorbisFile->links; s++)
{
if (mStreamUsage[s] != 0)
*outNumTracks += mVorbisFile->vi[s].channels;
}
*outTracks = new Track *[*outNumTracks];
int trackindex = 0;
for (i = 0; i < mVorbisFile->links; i++)
{
if (mChannels[i])
{
for (c = 0; c < mVorbisFile->vi[i].channels; c++) {
mChannels[i][c]->Flush();
(*outTracks)[trackindex++] = mChannels[i][c];
}
delete[] mChannels[i];
}
}
delete[] mChannels;
//\todo { Extract comments from each stream? }
if (mVorbisFile->vc[0].comments > 0) {
tags->Clear();
for (c = 0; c < mVorbisFile->vc[0].comments; c++) {
wxString comment = UTF8CTOWX(mVorbisFile->vc[0].user_comments[c]);
wxString name = comment.BeforeFirst(wxT('='));
wxString value = comment.AfterFirst(wxT('='));
if (name.Upper() == wxT("DATE") && !tags->HasTag(TAG_YEAR)) {
long val;
if (value.Length() == 4 && value.ToLong(&val)) {
name = TAG_YEAR;
}
}
tags->SetTag(name, value);
}
}
return res;
}
OggImportFileHandle::~OggImportFileHandle()
{
ov_clear(mVorbisFile);
mFile->Detach(); // so that it doesn't try to close the file (ov_clear()
// did that already)
delete mStreamInfo;
delete[] mStreamUsage;
delete mVorbisFile;
delete mFile;
}
#endif /* USE_LIBVORBIS */
// 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: 1436952e-a888-41d9-922a-8ebf7413991d

33
src/import/ImportOGG.h Normal file
View File

@@ -0,0 +1,33 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ImportOGG.h
Joshua Haberman
**********************************************************************/
#ifndef __AUDACITY_IMPORT_OGG__
#define __AUDACITY_IMPORT_OGG__
class ImportPluginList;
class UnusableImportPluginList;
void GetOGGImportPlugin(ImportPluginList *importPluginList,
UnusableImportPluginList *unusableImportPluginList);
#endif
// 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: abd2ee18-f6da-40e4-be29-d2a9efa8fa56

547
src/import/ImportPCM.cpp Normal file
View File

@@ -0,0 +1,547 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ImportPCM.cpp
Dominic Mazzoni
Leland Lucius
*//****************************************************************//**
\class PCMImportFileHandle
\brief An ImportFileHandle for PCM data
*//****************************************************************//**
\class PCMImportPlugin
\brief An ImportPlugin for PCM data
*//*******************************************************************/
#include "../Audacity.h"
#include "../AudacityApp.h"
#include "../Internat.h"
#include "../Tags.h"
#include "ImportPCM.h"
#include <wx/wx.h>
#include <wx/string.h>
#include <wx/utils.h>
#include <wx/intl.h>
#include <wx/ffile.h>
#include <wx/sizer.h>
#include <wx/checkbox.h>
#include <wx/button.h>
#include <wx/stattext.h>
#include "sndfile.h"
#include "../ondemand/ODManager.h"
#include "../ondemand/ODComputeSummaryTask.h"
//temporarilly commented out till it is added to all projects
//#include "../Profiler.h"
//the minimum number of samples a file has to use the OD compute summary task
//Otherwise, we use the older PCMAliasBlockFile method since it should be instant enough
//and the overhead of starting the thread might be slower.
#define kMinimumODFileSampleSize 44100*30
#ifndef SNDFILE_1
#error Requires libsndfile 1.0 or higher
#endif
#include "../FileFormats.h"
#include "../Prefs.h"
#include "../WaveTrack.h"
#include "ImportPlugin.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
#define DESC _("WAV, AIFF, and other uncompressed types")
class PCMImportPlugin : public ImportPlugin
{
public:
PCMImportPlugin()
: ImportPlugin(wxArrayString())
{
mExtensions = sf_get_all_extensions();
}
~PCMImportPlugin() { }
wxString GetPluginFormatDescription();
ImportFileHandle *Open(wxString Filename);
};
class PCMImportFileHandle : public ImportFileHandle
{
public:
PCMImportFileHandle(wxString name, SNDFILE *file, SF_INFO info);
~PCMImportFileHandle();
wxString GetFileDescription();
int GetFileUncompressedBytes();
int Import(TrackFactory *trackFactory, Track ***outTracks,
int *outNumTracks, Tags *tags);
wxInt32 GetStreamCount(){ return 1; }
wxArrayString *GetStreamInfo(){ return NULL; }
void SetStreamUsage(wxInt32 StreamID, bool Use){}
private:
SNDFILE *mFile;
SF_INFO mInfo;
sampleFormat mFormat;
};
void GetPCMImportPlugin(ImportPluginList *importPluginList,
UnusableImportPluginList *unusableImportPluginList)
{
importPluginList->Append(new PCMImportPlugin);
}
wxString PCMImportPlugin::GetPluginFormatDescription()
{
return DESC;
}
ImportFileHandle *PCMImportPlugin::Open(wxString filename)
{
SF_INFO info;
SNDFILE *file = NULL;
memset(&info, 0, sizeof(info));
wxFile f; // will be closed when it goes out of scope
if (f.Open(filename)) {
// 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).
file = sf_open_fd(f.fd(), SFM_READ, &info, TRUE);
}
// The file descriptor is now owned by "file", so we must tell "f" to leave
// it alone. The file descriptor is closed by sf_open_fd() even if an error
// occurs.
f.Detach();
if (!file) {
// TODO: Handle error
//char str[1000];
//sf_error_str((SNDFILE *)NULL, str, 1000);
return NULL;
}
return new PCMImportFileHandle(filename, file, info);
}
PCMImportFileHandle::PCMImportFileHandle(wxString name,
SNDFILE *file, SF_INFO info)
: ImportFileHandle(name),
mFile(file),
mInfo(info)
{
//
// Figure out the format to use.
//
// In general, go with the user's preferences. However, if
// the file is higher-quality, go with a format which preserves
// the quality of the original file.
//
mFormat = (sampleFormat)
gPrefs->Read(wxT("/SamplingRate/DefaultProjectSampleFormat"), floatSample);
if (mFormat != floatSample &&
sf_subtype_more_than_16_bits(mInfo.format))
mFormat = floatSample;
}
wxString PCMImportFileHandle::GetFileDescription()
{
return sf_header_name(mInfo.format);
}
int PCMImportFileHandle::GetFileUncompressedBytes()
{
return mInfo.frames * mInfo.channels * SAMPLE_SIZE(mFormat);
}
int PCMImportFileHandle::Import(TrackFactory *trackFactory,
Track ***outTracks,
int *outNumTracks,
Tags *tags)
{
wxASSERT(mFile);
CreateProgress();
WaveTrack **channels = new WaveTrack *[mInfo.channels];
int c;
for (c = 0; c < mInfo.channels; c++) {
channels[c] = trackFactory->NewWaveTrack(mFormat, mInfo.samplerate);
if (mInfo.channels > 1)
switch (c) {
case 0:
channels[c]->SetChannel(Track::LeftChannel);
break;
case 1:
channels[c]->SetChannel(Track::RightChannel);
break;
default:
channels[c]->SetChannel(Track::MonoChannel);
}
}
if (mInfo.channels == 2) {
channels[0]->SetLinked(true);
}
sampleCount fileTotalFrames = (sampleCount)mInfo.frames;
sampleCount maxBlockSize = channels[0]->GetMaxBlockSize();
int updateResult = false;
wxString copyEdit =
gPrefs->Read(wxT("/FileFormats/CopyOrEditUncompressedData"), wxT("edit"));
// Fall back to "edit" if it doesn't match anything else
bool doEdit = true;
if (copyEdit.IsSameAs(wxT("copy"), false))
doEdit = false;
// If the format is not seekable, we must use 'copy' mode,
// because 'edit' mode depends on the ability to seek to an
// arbitrary location in the file.
if (!mInfo.seekable)
doEdit = false;
//for profiling, uncomment and look in audacity.app/exe's folder for AudacityProfile.txt
//BEGIN_TASK_PROFILING("ON Demand Load 2hr stereo wav");
//BEGIN_TASK_PROFILING("Pre-GSOC (PCMAliasBlockFile) Drag and Drop 5 80 mb files into audacity (average per file)");
//BEGIN_TASK_PROFILING("Pre-GSOC (PCMAliasBlockFile) open an 80 mb wav stereo file");
//for profiling, uncomment and look in audacity.app/exe's folder for AudacityProfile.txt
//static int tempLog =0;
//if(tempLog++ % 5==0)
// BEGIN_TASK_PROFILING("On Demand Drag and Drop 5 80 mb files into audacity, 5 wavs per task");
//BEGIN_TASK_PROFILING("On Demand open an 80 mb wav stereo file");
if (doEdit) {
wxLogDebug(wxT("Importing PCM Start\n"));
// If this mode has been selected, we form the tracks as
// aliases to the files we're editing, i.e. ("foo.wav", 12000-18000)
// instead of actually making fresh copies of the samples.
// lets use OD only if the file is longer than 30 seconds. Otherwise, why wake up extra threads.
//todo: make this a user pref.
bool useOD =fileTotalFrames>kMinimumODFileSampleSize;
for (sampleCount i = 0; i < fileTotalFrames; i += maxBlockSize) {
sampleCount blockLen = maxBlockSize;
if (i + blockLen > fileTotalFrames)
blockLen = fileTotalFrames - i;
for (c = 0; c < mInfo.channels; c++)
channels[c]->AppendAlias(mFilename, i, blockLen, c,useOD);
updateResult = mProgress->Update(i, fileTotalFrames);
if (updateResult != eProgressSuccess)
break;
}
//now go over the wavetrack/waveclip/sequence and load all the blockfiles into a ComputeSummaryTask.
//Add this task to the ODManager and the Track itself.
wxLogDebug(wxT("Importing PCM \n"));
if(useOD)
{
ODComputeSummaryTask* computeTask=new ODComputeSummaryTask;
bool moreThanStereo = mInfo.channels>2;
for (c = 0; c < mInfo.channels; c++)
{
computeTask->AddWaveTrack(channels[c]);
if(moreThanStereo)
{
//if we have 3 more channels, they get imported on seperate tracks, so we add individual tasks for each.
ODManager::Instance()->AddNewTask(computeTask);
computeTask=new ODComputeSummaryTask;
}
}
//if we have a linked track, we add ONE task.
if(!moreThanStereo)
ODManager::Instance()->AddNewTask(computeTask);
}
}
else {
// Otherwise, we're in the "copy" mode, where we read in the actual
// samples from the file and store our own local copy of the
// samples in the tracks.
samplePtr srcbuffer = NewSamples(maxBlockSize * mInfo.channels,
mFormat);
samplePtr buffer = NewSamples(maxBlockSize, mFormat);
unsigned long framescompleted = 0;
long block;
do {
block = maxBlockSize;
if (mFormat == int16Sample)
block = sf_readf_short(mFile, (short *)srcbuffer, block);
//import 24 bit int as float and have the append function convert it. This is how PCMAliasBlockFile works too.
else
block = sf_readf_float(mFile, (float *)srcbuffer, block);
if (block) {
for(c=0; c<mInfo.channels; c++) {
if (mFormat==int16Sample) {
for(int j=0; j<block; j++)
((short *)buffer)[j] =
((short *)srcbuffer)[mInfo.channels*j+c];
}
else {
for(int j=0; j<block; j++)
((float *)buffer)[j] =
((float *)srcbuffer)[mInfo.channels*j+c];
}
channels[c]->Append(buffer, (mFormat == int16Sample)?int16Sample:floatSample, block);
}
framescompleted += block;
}
updateResult = mProgress->Update((long long unsigned)framescompleted,
(long long unsigned)fileTotalFrames);
if (updateResult != eProgressSuccess)
break;
} while (block > 0);
DeleteSamples(buffer);
DeleteSamples(srcbuffer);
}
if (updateResult == eProgressFailed || updateResult == eProgressCancelled) {
for (c = 0; c < mInfo.channels; c++)
delete channels[c];
delete[] channels;
return updateResult;
}
*outNumTracks = mInfo.channels;
*outTracks = new Track *[mInfo.channels];
for(c = 0; c < mInfo.channels; c++) {
channels[c]->Flush();
(*outTracks)[c] = channels[c];
}
delete[] channels;
const char *str;
str = sf_get_string(mFile, SF_STR_TITLE);
if (str) {
tags->SetTag(TAG_TITLE, UTF8CTOWX(str));
}
str = sf_get_string(mFile, SF_STR_ARTIST);
if (str) {
tags->SetTag(TAG_ARTIST, UTF8CTOWX(str));
}
str = sf_get_string(mFile, SF_STR_COMMENT);
if (str) {
tags->SetTag(TAG_COMMENTS, UTF8CTOWX(str));
}
str = sf_get_string(mFile, SF_STR_DATE);
if (str) {
tags->SetTag(TAG_YEAR, UTF8CTOWX(str));
}
str = sf_get_string(mFile, SF_STR_COPYRIGHT);
if (str) {
tags->SetTag(wxT("Copyright"), UTF8CTOWX(str));
}
str = sf_get_string(mFile, SF_STR_SOFTWARE);
if (str) {
tags->SetTag(wxT("Software"), UTF8CTOWX(str));
}
#if defined(USE_LIBID3TAG)
if ((mInfo.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) {
wxFFile f(mFilename, wxT("rb"));
if (f.IsOpened()) {
char id[5];
wxUint32 len;
id[4] = '\0';
f.Seek(12); // Skip filetype, length, and formtype
while (!f.Error()) {
f.Read(id, 4); // Get chunk type
if (f.Eof()) {
break;
}
f.Read(&len, 4);
len = wxUINT32_SWAP_ON_LE(len);
if (strcmp(id, "ID3 ") != 0) {
f.Seek(len + (len & 0x01), wxFromCurrent);
continue;
}
id3_byte_t *buffer = (id3_byte_t *)malloc(len);
if (!buffer) {
break;
}
f.Read(buffer, len);
struct id3_tag *tp = id3_tag_parse(buffer, len);
free(buffer);
if (!tp) {
break;
}
tags->SetID3V2( tp->options & ID3_TAG_OPTION_ID3V1 ? false : true );
// Loop through all frames
for (int i = 0; i < (int) tp->nframes; i++) {
struct id3_frame *frame = tp->frames[i];
// printf("ID: %08x '%4s'\n", (int) *(int *)frame->id, frame->id);
// printf("Desc: %s\n", frame->description);
// printf("Num fields: %d\n", frame->nfields);
// for (int j = 0; j < (int) frame->nfields; j++) {
// printf("field %d type %d\n", j, frame->fields[j].type );
// if (frame->fields[j].type == ID3_FIELD_TYPE_STRINGLIST) {
// printf("num strings %d\n", frame->fields[j].stringlist.nstrings);
// }
// }
wxString n, v;
// Determine the tag name
if (strcmp(frame->id, ID3_FRAME_TITLE) == 0) {
n = TAG_TITLE;
}
else if (strcmp(frame->id, ID3_FRAME_ARTIST) == 0) {
n = TAG_ARTIST;
}
else if (strcmp(frame->id, ID3_FRAME_ALBUM) == 0) {
n = TAG_ALBUM;
}
else if (strcmp(frame->id, ID3_FRAME_TRACK) == 0) {
n = TAG_TRACK;
}
else if (strcmp(frame->id, "TYER") == 0) {
n = TAG_YEAR;
}
else if (strcmp(frame->id, ID3_FRAME_YEAR) == 0) {
n = TAG_YEAR;
}
else if (strcmp(frame->id, ID3_FRAME_COMMENT) == 0) {
n = TAG_COMMENTS;
}
else if (strcmp(frame->id, ID3_FRAME_GENRE) == 0) {
n = TAG_GENRE;
}
else {
// Use frame description as default tag name. The descriptions
// may include several "meanings" separated by "/" characters, so
// we just use the first meaning
n = UTF8CTOWX(frame->description).BeforeFirst(wxT('/'));
}
const id3_ucs4_t *ustr = NULL;
if (n == TAG_COMMENTS) {
ustr = id3_field_getfullstring(&frame->fields[3]);
}
else if (frame->nfields == 3) {
ustr = id3_field_getstring(&frame->fields[1]);
if (ustr) {
char *str = (char *)id3_ucs4_utf8duplicate(ustr);
n = UTF8CTOWX(str);
free(str);
}
ustr = id3_field_getstring(&frame->fields[2]);
}
else if (frame->nfields >= 2) {
ustr = id3_field_getstrings(&frame->fields[1], 0);
}
if (ustr) {
char *str = (char *)id3_ucs4_utf8duplicate(ustr);
v = UTF8CTOWX(str);
free(str);
}
if (!n.IsEmpty() && !v.IsEmpty()) {
tags->SetTag(n, v);
}
}
// Convert v1 genre to name
if (tags->HasTag(TAG_GENRE)) {
long g = -1;
if (tags->GetTag(TAG_GENRE).ToLong(&g)) {
tags->SetTag(TAG_GENRE, tags->GetGenre(g));
}
}
id3_tag_delete(tp);
break;
}
f.Close();
}
}
#endif
//comment out to undo profiling.
//END_TASK_PROFILING("Pre-GSOC (PCMAliasBlockFile) open an 80 mb wav stereo file");
return updateResult;
}
PCMImportFileHandle::~PCMImportFileHandle()
{
sf_close(mFile);
}
// 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: 2e9db06a-fd0b-4af3-badd-eeb8437067e7

33
src/import/ImportPCM.h Normal file
View File

@@ -0,0 +1,33 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ImportPCM.h
Dominic Mazzoni
**********************************************************************/
#ifndef __AUDACITY_IMPORT_PCM__
#define __AUDACITY_IMPORT_PCM__
class ImportPluginList;
class UnusableImportPluginList;
void GetPCMImportPlugin(ImportPluginList *importPluginList,
UnusableImportPluginList *unusableImportPluginList);
#endif
// 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: db6c7791-1340-46db-86cb-b1b4ef5977e0

211
src/import/ImportPlugin.h Normal file
View File

@@ -0,0 +1,211 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ImportPlugin.h
Joshua Haberman
Leland Lucius
*******************************************************************//**
\file ImportPlugin.h
\brief
The interface that all file import "plugins" (if you want to call
them that) must implement. Defines ImportFileHandle, ImportPlugin,
UnusableImportPlugin, ImportPluginList and UnusableImportPluginList.
Since this is part of libaudacity, it must not use any GUI parts
of wxWidgets.
*//****************************************************************//**
\class ImportFileHandle
\brief Base class for FlacImportFileHandle, LOFImportFileHandle,
MP3ImportFileHandle, OggImportFileHandle and PCMImportFileHandle.
Gives API for sound file import.
*//****************************************************************//**
\class ImportPlugin
\brief Base class for FlacImportPlugin, LOFImportPlugin,
MP3ImportPlugin, OggImportPlugin and PCMImportPlugin.
Gives API for sound file import.
*//****************************************************************//**
\class UnusableImportPlugin
\brief Used in place of a real plug in for plug ins that have not
been compiled or are not available in this version of Audacity. Has
enough information to identify the file extensions that would be used,
but little else.
*//****************************************************************//**
\class ImportPluginList
\brief An ImportPlugin list.
*//****************************************************************//**
\class UnusableImportPluginList
\brief An UnusableImportPlugin list.
*//*******************************************************************/
#ifndef __AUDACITY_IMPORTER__
#define __AUDACITY_IMPORTER__
#include <wx/arrstr.h>
#include <wx/filename.h>
#include <wx/string.h>
#include <wx/list.h>
#include "../widgets/ProgressDialog.h"
class TrackFactory;
class Track;
class Tags;
class ImportFileHandle;
class ImportPlugin
{
public:
// Get a description of the file type this importer can import.
// Examples: "Ogg Vorbis", "MP3", "Uncompressed PCM"
virtual wxString GetPluginFormatDescription() = 0;
// Get a list of extensions this plugin expects to be able to
// import. If a filename matches any of these extensions,
// this importer will get first dibs on importing it.
virtual wxArrayString GetSupportedExtensions()
{
return mExtensions;
}
bool SupportsExtension(wxString extension)
{
// Case-insensitive check if extension is supported
return mExtensions.Index(extension, false) != wxNOT_FOUND;
}
// Open the given file, returning true if it is in a recognized
// format, false otherwise. This puts the importer into the open
// state.
virtual ImportFileHandle *Open(wxString Filename) = 0;
virtual ~ImportPlugin() { }
protected:
ImportPlugin(wxArrayString supportedExtensions):
mExtensions(supportedExtensions)
{
}
wxArrayString mExtensions;
};
class ImportFileHandle
{
public:
ImportFileHandle(const wxString & filename)
: mFilename(filename),
mProgress(NULL)
{
}
virtual ~ImportFileHandle()
{
if (mProgress != NULL)
{
delete mProgress;
}
}
// The importer should call this to create the progress dialog and
// identify the filename being imported.
void CreateProgress()
{
wxFileName f(mFilename);
wxString title;
title.Printf(_("Importing %s"), GetFileDescription().c_str());
mProgress = new ProgressDialog(title,
f.GetFullName());
}
// This is similar to GetImporterDescription, but if possible the
// importer will return a more specific description of the
// specific file that is open.
virtual wxString GetFileDescription() = 0;
// Return an estimate of how many bytes the file will occupy once
// imported
virtual int GetFileUncompressedBytes() = 0;
// do the actual import, creating whatever tracks are necessary with
// the TrackFactory and calling the progress callback every iteration
// through the importing loop
virtual int Import(TrackFactory *trackFactory, Track ***outTracks,
int *outNumTracks, Tags *tags) = 0;
// Return number of elements in stream list
virtual wxInt32 GetStreamCount() = 0;
// Return stream descriptions list
virtual wxArrayString *GetStreamInfo() = 0;
// Set stream "import/don't import" flag
virtual void SetStreamUsage(wxInt32 StreamID, bool Use) = 0;
protected:
wxString mFilename;
ProgressDialog *mProgress;
};
class UnusableImportPlugin
{
public:
UnusableImportPlugin(wxString formatName, wxArrayString extensions):
mFormatName(formatName),
mExtensions(extensions)
{
}
wxString GetPluginFormatDescription()
{
return mFormatName;
}
bool SupportsExtension(wxString extension)
{
return mExtensions.Index(extension, false) != wxNOT_FOUND;
}
private:
wxString mFormatName;
wxArrayString mExtensions;
};
WX_DECLARE_LIST(ImportPlugin, ImportPluginList);
WX_DECLARE_LIST(UnusableImportPlugin, UnusableImportPluginList);
#endif
// 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: a772f926-931e-4364-9f37-47d4b96de7a6

546
src/import/ImportQT.cpp Normal file
View File

@@ -0,0 +1,546 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ImportQT.cpp
Joshua Haberman
Handles importing MPEG-4 audio files, including AAC and Apple Lossless,
on Mac OS X. Could be modified to support QuickTime importing on
Windows, too.
**********************************************************************/
#include "../Audacity.h"
#include "ImportPlugin.h"
#define DESC _("QuickTime files")
static const wxChar *exts[] =
{
wxT("mov"),
wxT("aac"),
wxT("m4a")
};
#ifndef USE_QUICKTIME
void GetQTImportPlugin(ImportPluginList *importPluginList,
UnusableImportPluginList *unusableImportPluginList)
{
UnusableImportPlugin* qtIsUnsupported =
new UnusableImportPlugin(DESC, wxArrayString(WXSIZEOF(exts), exts));
unusableImportPluginList->Append(qtIsUnsupported);
}
#else /* USE_QUICKTIME */
#include <wx/msgdlg.h>
// There's a name collision between our Track and QuickTime's...workaround it
#define Track XTrack
#if defined(__WXMAC__)
// These get used when building under OSX
#include <Carbon/Carbon.h>
#include <QuickTime/QuickTime.h>
#include <wx/mac/private.h>
#else
// These get used when building under Windows
#include <ConditionalMacros.h>
#include <Movies.h>
#include <QuickTimeComponents.h>
#include <Sound.h>
#include <Folders.h>
#include <ToolUtils.h>
#include <Gestalt.h>
#include <Navigation.h>
#endif
// There's a name collision between our Track and QuickTime's...workaround it
#undef Track
#include "../Internat.h"
#include "../Tags.h"
#include "../WaveTrack.h"
#include "ImportQT.h"
#define kQTAudioPropertyID_MaxAudioSampleSize 'mssz'
class QTImportPlugin : public ImportPlugin
{
public:
QTImportPlugin()
: ImportPlugin(wxArrayString(WXSIZEOF(exts), exts)),
mInitialized(false)
{
OSErr err = noErr;
#if defined(__WXMSW__)
err = ::InitializeQTML(0);
#endif
if (err == noErr) {
err = ::EnterMovies();
if (err == noErr) {
mInitialized = true;
}
else {
#if defined(__WXMSW__)
TerminateQTML();
#endif
}
}
}
~QTImportPlugin()
{
if (mInitialized) {
ExitMovies();
#if defined(__WXMSW__)
TerminateQTML();
#endif
}
mInitialized = false;
}
wxString GetPluginFormatDescription();
ImportFileHandle *Open(wxString Filename);
private:
bool mInitialized;
};
class QTImportFileHandle : public ImportFileHandle
{
public:
QTImportFileHandle(const wxString & name, Movie movie)
: ImportFileHandle(name)
{
mMovie = movie;
}
virtual ~QTImportFileHandle()
{
if (mMovie) {
DisposeMovie(mMovie);
mMovie = NULL;
}
}
wxString GetFileDescription();
int GetFileUncompressedBytes();
wxInt32 GetStreamCount()
{
return 1;
}
wxArrayString *GetStreamInfo()
{
return NULL;
}
void SetStreamUsage(wxInt32 StreamID, bool Use)
{
}
int Import(TrackFactory *trackFactory,
Track ***outTracks,
int *outNumTracks,
Tags *tags);
private:
void AddMetadata(Tags *tags);
Movie mMovie;
};
void GetQTImportPlugin(ImportPluginList *importPluginList,
UnusableImportPluginList *unusableImportPluginList)
{
importPluginList->Append(new QTImportPlugin);
}
wxString QTImportPlugin::GetPluginFormatDescription()
{
return DESC;
}
ImportFileHandle *QTImportPlugin::Open(wxString Filename)
{
OSErr err;
FSRef inRef;
Movie theMovie = NULL;
Handle dataRef = NULL;
OSType dataRefType = 0;
short resID = 0;
#if defined(__WXMAC__)
err = wxMacPathToFSRef(Filename, &inRef);
#else
// LLL: This will not work for pathnames with Unicode characters...find
// another method.
err = FSPathMakeRef((UInt8 *)OSFILENAME(Filename), &inRef, NULL);
#endif
if (err != noErr) {
return NULL;
}
err = QTNewDataReferenceFromFSRef(&inRef, 0, &dataRef, &dataRefType);
if (err != noErr) {
return NULL;
}
// instantiate the movie
err = NewMovieFromDataRef(&theMovie,
newMovieActive | newMovieDontAskUnresolvedDataRefs,
&resID,
dataRef,
dataRefType);
DisposeHandle(dataRef);
if (err != noErr) {
return NULL;
}
return new QTImportFileHandle(Filename, theMovie);
}
wxString QTImportFileHandle::GetFileDescription()
{
return DESC;
}
int QTImportFileHandle::GetFileUncompressedBytes()
{
return 0;
}
int QTImportFileHandle::Import(TrackFactory *trackFactory,
Track ***outTracks,
int *outNumTracks,
Tags *tags)
{
OSErr err = noErr;
MovieAudioExtractionRef maer = NULL;
int updateResult = eProgressSuccess;
sampleCount totSamples = (sampleCount) GetMovieDuration(mMovie);
sampleCount numSamples = 0;
Boolean discrete = true;
UInt32 quality = kQTAudioRenderQuality_Max;
AudioStreamBasicDescription desc;
UInt32 maxSampleSize;
UInt32 numchan;
UInt32 bufsize;
CreateProgress();
err = MovieAudioExtractionBegin(mMovie, 0, &maer);
if (err != noErr) {
wxMessageBox(_("Unable to start QuickTime extraction"));
goto done;
}
err = MovieAudioExtractionSetProperty(maer,
kQTPropertyClass_MovieAudioExtraction_Audio,
kQTMovieAudioExtractionAudioPropertyID_RenderQuality,
sizeof(quality),
&quality);
if (err != noErr) {
wxMessageBox(_("Unable to set QuickTime render quality"));
goto done;
}
err = MovieAudioExtractionSetProperty(maer,
kQTPropertyClass_MovieAudioExtraction_Movie,
kQTMovieAudioExtractionMoviePropertyID_AllChannelsDiscrete,
sizeof(discrete),
&discrete);
if (err != noErr) {
wxMessageBox(_("Unable to set QuickTime discrete channels property"));
goto done;
}
err = MovieAudioExtractionGetProperty(maer,
kQTPropertyClass_MovieAudioExtraction_Audio,
kQTAudioPropertyID_MaxAudioSampleSize,
sizeof(maxSampleSize),
&maxSampleSize,
NULL);
if (err != noErr) {
wxMessageBox(_("Unable to get QuickTime sample size property"));
goto done;
}
err = MovieAudioExtractionGetProperty(maer,
kQTPropertyClass_MovieAudioExtraction_Audio,
kQTMovieAudioExtractionAudioPropertyID_AudioStreamBasicDescription,
sizeof(desc),
&desc,
NULL);
if (err != noErr) {
wxMessageBox(_("Unable to retrieve stream description"));
goto done;
}
numchan = desc.mChannelsPerFrame;
bufsize = 5 * desc.mSampleRate;
// determine sample format
sampleFormat format;
switch (maxSampleSize)
{
case 16:
format = int16Sample;
break;
case 24:
format = int24Sample;
break;
default:
format = floatSample;
break;
}
AudioBufferList *abl = (AudioBufferList *)
calloc(1, offsetof(AudioBufferList, mBuffers) + (sizeof(AudioBuffer) * numchan));
abl->mNumberBuffers = numchan;
WaveTrack **channels = new WaveTrack *[numchan];
int c;
for (c = 0; c < numchan; c++) {
abl->mBuffers[c].mNumberChannels = 1;
abl->mBuffers[c].mDataByteSize = sizeof(float) * bufsize;
abl->mBuffers[c].mData = malloc(abl->mBuffers[c].mDataByteSize);
channels[c] = trackFactory->NewWaveTrack(format);
channels[c]->SetRate(desc.mSampleRate);
if (numchan == 2) {
if (c == 0) {
channels[c]->SetChannel(Track::LeftChannel);
channels[c]->SetLinked(true);
}
else if (c == 1) {
channels[c]->SetChannel(Track::RightChannel);
}
}
}
do {
UInt32 flags = 0;
UInt32 numFrames = bufsize;
err = MovieAudioExtractionFillBuffer(maer,
&numFrames,
abl,
&flags);
if (err != noErr) {
wxMessageBox(_("Unable to get fill buffer"));
break;
}
for (c = 0; c < numchan; c++) {
channels[c]->Append((char *) abl->mBuffers[c].mData, floatSample, numFrames);
}
numSamples += numFrames;
updateResult = mProgress->Update((wxULongLong_t)numSamples,
(wxULongLong_t)totSamples);
if (numFrames == 0 || flags & kQTMovieAudioExtractionComplete) {
break;
}
} while (updateResult == eProgressSuccess);
bool res = (updateResult == eProgressSuccess && err == noErr);
if (res) {
for (c = 0; c < numchan; c++) {
channels[c]->Flush();
}
*outTracks = (Track **) channels;
*outNumTracks = numchan;
}
else {
for (c = 0; c < numchan; c++) {
delete channels[c];
}
delete [] channels;
}
for (c = 0; c < numchan; c++) {
free(abl->mBuffers[c].mData);
}
free(abl);
//
// Extract any metadata
//
if (res) {
AddMetadata(tags);
}
done:
if (maer) {
MovieAudioExtractionEnd(maer);
}
return (res ? eProgressSuccess : eProgressFailed);
}
static const struct
{
OSType key;
wxChar *name;
}
names[] =
{
{ kQTMetaDataCommonKeyAuthor, wxT("Author") },
{ kQTMetaDataCommonKeyComment, TAG_COMMENTS },
{ kQTMetaDataCommonKeyCopyright, wxT("Copyright") },
{ kQTMetaDataCommonKeyDirector, wxT("Director") },
{ kQTMetaDataCommonKeyDisplayName, wxT("Full Name") },
{ kQTMetaDataCommonKeyInformation, wxT("Information") },
{ kQTMetaDataCommonKeyKeywords, wxT("Keywords") },
{ kQTMetaDataCommonKeyProducer, wxT("Producer") },
{ kQTMetaDataCommonKeyAlbum, TAG_ALBUM },
{ kQTMetaDataCommonKeyArtist, TAG_ARTIST },
{ kQTMetaDataCommonKeyChapterName, wxT("Chapter") },
{ kQTMetaDataCommonKeyComposer, wxT("Composer") },
{ kQTMetaDataCommonKeyDescription, wxT("Description") },
{ kQTMetaDataCommonKeyGenre, TAG_GENRE },
{ kQTMetaDataCommonKeyOriginalFormat, wxT("Original Format") },
{ kQTMetaDataCommonKeyOriginalSource, wxT("Original Source") },
{ kQTMetaDataCommonKeyPerformers, wxT("Performers") },
{ kQTMetaDataCommonKeySoftware, wxT("Software") },
{ kQTMetaDataCommonKeyWriter, wxT("Writer") },
};
void QTImportFileHandle::AddMetadata(Tags *tags)
{
QTMetaDataRef metaDataRef = NULL;
OSErr err;
err = QTCopyMovieMetaData(mMovie, &metaDataRef);
if (err != noErr) {
return;
}
for (int i = 0; i < WXSIZEOF(names); i++) {
QTMetaDataItem item = kQTMetaDataItemUninitialized;
OSType key = names[i].key;
err = QTMetaDataGetNextItem(metaDataRef,
kQTMetaDataStorageFormatWildcard,
kQTMetaDataItemUninitialized,
kQTMetaDataKeyFormatCommon,
(const UInt8 *) &names[i].key,
sizeof(names[i].key),
&item);
if (err != noErr) {
continue;
}
if (item == kQTMetaDataItemUninitialized) {
continue;
}
QTPropertyValuePtr outValPtr = nil;
QTPropertyValueType outPropType;
ByteCount outPropValueSize;
ByteCount outPropValueSizeUsed = 0;
UInt32 outPropFlags;
UInt32 dataType;
// Get data type
err = QTMetaDataGetItemProperty(metaDataRef,
item,
kPropertyClass_MetaDataItem,
kQTMetaDataItemPropertyID_DataType,
sizeof(dataType),
&dataType,
&outPropValueSizeUsed);
if (err != noErr) {
continue;
}
// Get the data length
err = QTMetaDataGetItemPropertyInfo(metaDataRef,
item,
kPropertyClass_MetaDataItem,
kQTMetaDataItemPropertyID_Value,
&outPropType,
&outPropValueSize,
&outPropFlags );
if (err != noErr) {
continue;
}
// Alloc memory for it
outValPtr = malloc(outPropValueSize);
// Retrieve the data
err = QTMetaDataGetItemProperty(metaDataRef,
item,
kPropertyClass_MetaDataItem,
kQTMetaDataItemPropertyID_Value,
outPropValueSize,
outValPtr,
&outPropValueSizeUsed);
if (err != noErr) {
free(outValPtr);
continue;
}
wxString v = wxT("");
switch (dataType)
{
case kQTMetaDataTypeUTF8:
v = wxString((char *)outValPtr, wxConvUTF8);
break;
case kQTMetaDataTypeUTF16BE:
{
wxMBConvUTF16BE conv;
v = wxString((char *)outValPtr, conv);
}
break;
}
if (!v.IsEmpty()) {
tags->SetTag(names[i].name, v);
}
free(outValPtr);
}
// we are done so release our metadata object
QTMetaDataRelease(metaDataRef);
return;
}
#endif
// Indentation settings for Vim and Emacs.
// Please do not modify past this point.
//
// Local Variables:
// c-basic-offset: 3
// indent-tabs-mode: nil
// End:
//
// vim: et sts=3 sw=3

32
src/import/ImportQT.h Normal file
View File

@@ -0,0 +1,32 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ImportQT.h
Joshua Haberman
**********************************************************************/
#ifndef __AUDACITY_IMPORT_QT__
#define __AUDACITY_IMPORT_QT__
class ImportPluginList;
class UnusableImportPluginList;
void GetQTImportPlugin(ImportPluginList *importPluginList,
UnusableImportPluginList *unusableImportPluginList);
#endif
// Indentation settings for Vim and Emacs.
// Please do not modify past this point.
//
// Local Variables:
// c-basic-offset: 3
// indent-tabs-mode: nil
// End:
//
// vim: et sts=3 sw=3

515
src/import/ImportRaw.cpp Normal file
View File

@@ -0,0 +1,515 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ImportRaw.cpp
Dominic Mazzoni
*******************************************************************//**
\file ImportRaw.cpp
\brief Functions for guessing audio type and attempting to read from
unknown sample audio data. Implements ImportRawDialog.
*//****************************************************************//**
\class ImportRawDialog
\brief ImportRawDialog prompts you with options such as endianness
and sample size to help you importing data of an unknown format.
*//*******************************************************************/
#include "../Audacity.h"
#include "ImportRaw.h"
#include "Import.h"
#include "RawAudioGuess.h"
#include "../DirManager.h"
#include "../FileFormats.h"
#include "../Internat.h"
#include "../Prefs.h"
#include "../ShuttleGui.h"
#include "../WaveTrack.h"
#include <math.h>
#include <wx/defs.h>
#include <wx/button.h>
#include <wx/choice.h>
#include <wx/intl.h>
#include <wx/msgdlg.h>
#include <wx/panel.h>
#include <wx/progdlg.h>
#include <wx/sizer.h>
#include <wx/stattext.h>
#include <wx/textctrl.h>
#include <wx/timer.h>
#include "sndfile.h"
class ImportRawDialog:public wxDialog {
public:
ImportRawDialog(wxWindow * parent,
int encoding, int channels,
int offset, double rate);
~ImportRawDialog();
void OnOK(wxCommandEvent & event);
void OnCancel(wxCommandEvent & event);
void OnPlay(wxCommandEvent & event);
void OnChoice(wxCommandEvent & event);
// in and out
int mEncoding;
int mChannels;
int mOffset;
double mRate;
double mPercent;
private:
wxButton *mOK;
wxChoice *mEncodingChoice;
wxChoice *mEndianChoice;
wxChoice *mChannelChoice;
wxTextCtrl *mOffsetText;
wxTextCtrl *mPercentText;
wxTextCtrl *mRateText;
int mNumEncodings;
int *mEncodingSubtype;
DECLARE_EVENT_TABLE()
};
int ImportRaw(wxWindow *parent, wxString fileName,
TrackFactory *trackFactory, Track ***outTracks)
{
int encoding = 0; // Guess Format
int numChannels = 0;
sampleFormat format;
sf_count_t offset = 0;
int int_offset = 0;
sampleCount totalFrames;
double rate = 44100.0;
double percent = 100.0;
SNDFILE *sndFile = NULL;
SF_INFO sndInfo;
int result;
encoding = RawAudioGuess(fileName,
&int_offset, &numChannels);
offset = (sf_count_t)int_offset;
if (encoding <= 0) {
// Unable to guess. Use mono, 16-bit samples with CPU endianness
// as the default.
encoding = SF_FORMAT_RAW | SF_ENDIAN_CPU | SF_FORMAT_PCM_16;
numChannels = 1;
offset = 0;
}
ImportRawDialog dlog(parent, encoding, numChannels, (int)offset, rate);
dlog.ShowModal();
if (!dlog.GetReturnCode())
return false;
encoding = dlog.mEncoding;
numChannels = dlog.mChannels;
rate = dlog.mRate;
offset = (sf_count_t)dlog.mOffset;
percent = dlog.mPercent;
memset(&sndInfo, 0, sizeof(SF_INFO));
sndInfo.samplerate = (int)rate;
sndInfo.channels = (int)numChannels;
sndInfo.format = encoding | SF_FORMAT_RAW;
wxFile f; // will be closed when it goes out of scope
if (f.Open(fileName)) {
// 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).
sndFile = sf_open_fd(f.fd(), SFM_READ, &sndInfo, FALSE);
}
if (!sndFile){
// TODO: Handle error
char str[1000];
sf_error_str((SNDFILE *)NULL, str, 1000);
printf("%s\n", str);
return 0;
}
result = sf_command(sndFile, SFC_SET_RAW_START_OFFSET, &offset, sizeof(offset));
if (result != 0) {
char str[1000];
sf_error_str(sndFile, str, 1000);
printf("%s\n", str);
}
sf_seek(sndFile, 0, SEEK_SET);
totalFrames = (sampleCount)(sndInfo.frames * percent / 100.0);
//
// Sample format:
//
// In general, go with the user's preferences. However, if
// the file is higher-quality, go with a format which preserves
// the quality of the original file.
//
format = (sampleFormat)
gPrefs->Read(wxT("/SamplingRate/DefaultProjectSampleFormat"), floatSample);
if (format != floatSample &&
sf_subtype_more_than_16_bits(encoding))
format = floatSample;
WaveTrack **channels = new WaveTrack *[numChannels];
int c;
for (c = 0; c < numChannels; c++) {
channels[c] = trackFactory->NewWaveTrack(format, rate);
if (numChannels > 1)
switch (c) {
case 0:
channels[c]->SetChannel(Track::LeftChannel);
break;
case 1:
channels[c]->SetChannel(Track::RightChannel);
break;
default:
channels[c]->SetChannel(Track::MonoChannel);
}
}
if (numChannels == 2) {
channels[0]->SetLinked(true);
}
sampleCount maxBlockSize = channels[0]->GetMaxBlockSize();
int updateResult = eProgressSuccess;
samplePtr srcbuffer = NewSamples(maxBlockSize * numChannels, format);
samplePtr buffer = NewSamples(maxBlockSize, format);
sampleCount framescompleted = 0;
wxString msg;
msg.Printf(_("Importing %s"), wxFileName::FileName(fileName).GetFullName().c_str());
ProgressDialog progress(_("Import Raw"), msg);
long block;
do {
block = maxBlockSize;
if (block + framescompleted > totalFrames)
block = totalFrames - framescompleted;
if (format == int16Sample)
block = sf_readf_short(sndFile, (short *)srcbuffer, block);
else
block = sf_readf_float(sndFile, (float *)srcbuffer, block);
if (block) {
for(c=0; c<numChannels; c++) {
if (format==int16Sample) {
for(int j=0; j<block; j++)
((short *)buffer)[j] =
((short *)srcbuffer)[numChannels*j+c];
}
else {
for(int j=0; j<block; j++)
((float *)buffer)[j] =
((float *)srcbuffer)[numChannels*j+c];
}
channels[c]->Append(buffer, format, block);
}
framescompleted += block;
}
updateResult = progress.Update((wxULongLong_t)framescompleted,
(wxULongLong_t)totalFrames);
if (updateResult != eProgressSuccess)
break;
} while (block > 0 && framescompleted < totalFrames);
sf_close(sndFile);
int res = updateResult;
if (block < 0)
res = eProgressFailed;
if (res == eProgressFailed || res == eProgressCancelled) {
for (c = 0; c < numChannels; c++)
delete channels[c];
delete[] channels;
// It's a shame we can't return proper error code
return 0;
}
for (c = 0; c < numChannels; c++)
channels[c]->Flush();
*outTracks = (Track **)channels;
return numChannels;
}
//
// ImportRawDialog
//
enum {
ChoiceID = 9000,
PlayID
};
BEGIN_EVENT_TABLE(ImportRawDialog, wxDialog)
EVT_BUTTON(wxID_OK, ImportRawDialog::OnOK)
EVT_BUTTON(wxID_CANCEL, ImportRawDialog::OnCancel)
EVT_BUTTON(PlayID, ImportRawDialog::OnPlay)
EVT_CHOICE(ChoiceID, ImportRawDialog::OnChoice)
END_EVENT_TABLE()
ImportRawDialog::ImportRawDialog(wxWindow * parent,
int encoding, int channels,
int offset, double rate)
: wxDialog(parent, wxID_ANY, _("Import Raw Data"),
wxDefaultPosition, wxDefaultSize,
wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
mEncoding(encoding),
mChannels(channels),
mOffset(offset),
mRate(rate)
{
ShuttleGui S(this, eIsCreating);
wxArrayString encodings;
wxArrayString endians;
wxArrayString chans;
int num;
int selection;
int endian;
int i;
num = sf_num_encodings();
mNumEncodings = 0;
mEncodingSubtype = new int[num];
selection = 0;
for (i=0; i<num; i++) {
SF_INFO info;
memset(&info, 0, sizeof(SF_INFO));
int subtype = sf_encoding_index_to_subtype(i);
info.format = SF_FORMAT_RAW + SF_ENDIAN_LITTLE + subtype;
info.channels = 1;
info.samplerate = 44100;
if (sf_format_check(&info)) {
mEncodingSubtype[mNumEncodings] = subtype;
encodings.Add(LAT1CTOWX(sf_encoding_index_name(i)));
if ((mEncoding & SF_FORMAT_SUBMASK) == subtype)
selection = mNumEncodings;
mNumEncodings++;
}
}
/* i18n-hint: Refers to byte-order. Don't translate "endianness" if you don't
know the correct technical word. */
endians.Add(_("No endianness"));
/* i18n-hint: Refers to byte-order. Don't translate this if you don't
know the correct technical word. */
endians.Add(_("Little-endian"));
/* i18n-hint: Refers to byte-order. Don't translate this if you don't
know the correct technical word. */
endians.Add(_("Big-endian"));
/* i18n-hint: Refers to byte-order. Don't translate "endianness" if you don't
know the correct technical word. */
endians.Add(_("Default endianness"));
switch (mEncoding & (SF_FORMAT_ENDMASK))
{
default:
case SF_ENDIAN_FILE:
endian = 0;
break;
case SF_ENDIAN_LITTLE:
endian = 1;
break;
case SF_ENDIAN_BIG:
endian = 2;
break;
case SF_ENDIAN_CPU:
endian = 3;
break;
}
chans.Add(_("1 Channel (Mono)"));
chans.Add(_("2 Channels (Stereo)"));
for (i=2; i<16; i++) {
chans.Add(wxString::Format(_("%d Channels"), i + 1));
}
S.StartVerticalLay(false);
{
S.SetBorder(5);
S.StartTwoColumn();
{
mEncodingChoice = S.Id(ChoiceID).AddChoice(_("Encoding:"),
encodings[selection],
&encodings);
mEndianChoice = S.Id(ChoiceID).AddChoice(_("Byte order:"),
endians[endian],
&endians);
mChannelChoice = S.Id(ChoiceID).AddChoice(_("Channels:"),
chans[mChannels-1],
&chans);
}
S.EndTwoColumn();
S.SetBorder(5);
S.StartMultiColumn(3);
{
// Offset text
mOffsetText = S.AddTextBox(_("Start offset:"),
wxString::Format(wxT("%d"), mOffset),
12);
S.AddUnits(_("bytes"));
// Percent text
mPercentText = S.AddTextBox(_("Amount to import:"),
wxT("100"),
12);
S.AddUnits(wxT("%"));
// Rate text
mRateText = S.AddTextBox(_("Sample rate:"),
wxString::Format(wxT("%d"), (int)mRate),
12);
/* i18n-hint: This is the abbreviation for "Hertz", or
cycles per second. */
S.AddUnits(_("Hz"));
}
S.EndMultiColumn();
//
// Preview Pane goes here
//
S.AddStandardButtons();
// Find the OK button, and change its text to 'Import'.
// We MUST set mOK because it is used later.
mOK = (wxButton *)wxWindow::FindWindowById(wxID_OK, this);
mOK->SetLabel(_("&Import"));
}
S.EndVerticalLay();
Fit();
SetSizeHints(GetSize());
Centre(wxBOTH);
}
ImportRawDialog::~ImportRawDialog()
{
delete[] mEncodingSubtype;
}
void ImportRawDialog::OnOK(wxCommandEvent & WXUNUSED(event))
{
long l;
mEncoding = mEncodingSubtype[mEncodingChoice->GetSelection()];
mEncoding += (mEndianChoice->GetSelection() * 0x10000000);
mChannels = mChannelChoice->GetSelection() + 1;
mOffsetText->GetValue().ToLong(&l);
mOffset = l;
mPercentText->GetValue().ToDouble(&mPercent);
mRateText->GetValue().ToDouble(&mRate);
if (mChannels < 1 || mChannels > 16)
mChannels = 1;
if (mOffset < 0)
mOffset = 0;
if (mPercent < 0.0)
mPercent = 0.0;
if (mPercent > 100.0)
mPercent = 100.0;
if (mRate < 100.0)
mRate = 100.0;
if (mRate > 100000.0)
mRate = 100000.0;
EndModal(true);
}
void ImportRawDialog::OnCancel(wxCommandEvent & WXUNUSED(event))
{
EndModal(false);
}
void ImportRawDialog::OnPlay(wxCommandEvent & WXUNUSED(event))
{
}
void ImportRawDialog::OnChoice(wxCommandEvent & WXUNUSED(event))
{
SF_INFO info;
memset(&info, 0, sizeof(SF_INFO));
mEncoding = mEncodingSubtype[mEncodingChoice->GetSelection()];
mEncoding += (mEndianChoice->GetSelection() * 0x10000000);
info.format = mEncoding | SF_FORMAT_RAW;
info.channels = mChannelChoice->GetSelection() + 1;
info.samplerate = 44100;
//mOK = (wxButton *)wxWindow::FindWindowById(wxID_OK, this);
if (sf_format_check(&info)) {
mOK->Enable(true);
return;
}
// Try it with 1-channel
info.channels = 1;
if (sf_format_check(&info)) {
mChannelChoice->SetSelection(0);
mOK->Enable(true);
return;
}
// Otherwise, this is an unsupported format
mOK->Enable(false);
}
// 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: 3e6ead61-1ef2-4518-a622-523f1efebb9a

35
src/import/ImportRaw.h Normal file
View File

@@ -0,0 +1,35 @@
/**********************************************************************
Audacity: A Digital Audio Editor
ImportRaw.h
Dominic Mazzoni
**********************************************************************/
#ifndef __AUDACITY_IMPORT_RAW__
#define __AUDACITY_IMPORT_RAW__
#include "Import.h"
class WaveTrack;
class DirManager;
class wxWindow;
int ImportRaw(wxWindow *parent, wxString fileName,
TrackFactory *trackFactory, Track ***outTracks);
#endif
// 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: 1bed8f22-2559-4a6a-8480-9fb70630b11c

1140
src/import/RawAudioGuess.cpp Normal file

File diff suppressed because it is too large Load Diff

View File

@@ -0,0 +1,41 @@
/**********************************************************************
Audacity: A Digital Audio Editor
RawAudioGuess.h
Dominic Mazzoni
Attempts to determine the format of an audio file that doesn't
have any header information. Returns the format as a
libsndfile-compatible format, along with the guessed number of
channels and the byte-offset.
**********************************************************************/
#include <wx/defs.h>
#include <sndfile.h>
#ifndef SNDFILE_1
#error Requires libsndfile 1.0.3 or higher
#endif
/* Returns the best guess as to the format, as a libsndfile
SF_FORMAT value
*/
int RawAudioGuess(const wxString &in_fname,
int *out_offset, int *out_channels);
// 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: e51391e7-bc24-4dc6-84bc-95ac04221a95