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:
413
src/import/Import.cpp
Normal file
413
src/import/Import.cpp
Normal 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
108
src/import/Import.h
Normal 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
859
src/import/ImportFFmpeg.cpp
Normal 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
22
src/import/ImportFFmpeg.h
Normal 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
556
src/import/ImportFLAC.cpp
Normal 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
21
src/import/ImportFLAC.h
Normal 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
|
1037
src/import/ImportGStreamer.cpp
Normal file
1037
src/import/ImportGStreamer.cpp
Normal file
File diff suppressed because it is too large
Load Diff
22
src/import/ImportGStreamer.h
Normal file
22
src/import/ImportGStreamer.h
Normal 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
496
src/import/ImportLOF.cpp
Normal 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
75
src/import/ImportLOF.h
Normal 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
75
src/import/ImportMIDI.cpp
Normal 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
125
src/import/ImportMIDI.h
Normal 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
559
src/import/ImportMP3.cpp
Normal 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
32
src/import/ImportMP3.h
Normal 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
424
src/import/ImportOGG.cpp
Normal 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
33
src/import/ImportOGG.h
Normal 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
547
src/import/ImportPCM.cpp
Normal 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
33
src/import/ImportPCM.h
Normal 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
211
src/import/ImportPlugin.h
Normal 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
546
src/import/ImportQT.cpp
Normal 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
32
src/import/ImportQT.h
Normal 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
515
src/import/ImportRaw.cpp
Normal 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
35
src/import/ImportRaw.h
Normal 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
1140
src/import/RawAudioGuess.cpp
Normal file
File diff suppressed because it is too large
Load Diff
41
src/import/RawAudioGuess.h
Normal file
41
src/import/RawAudioGuess.h
Normal 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
|
||||
|
Reference in New Issue
Block a user