Audacity 3.2.0
ImportRaw.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 ImportRaw.cpp
6
7 Dominic Mazzoni
8
9*******************************************************************//****************************************************************//*******************************************************************/
22
23
24
25#include "ImportRaw.h"
26
27#include "ImportPlugin.h"
28
29#include "AudioIOBase.h"
30#include "../FileFormats.h"
31#include "Prefs.h"
32#include "ProjectRate.h"
33#include "../SelectFile.h"
34#include "../ShuttleGui.h"
35#include "UserException.h"
36#include "../WaveTrack.h"
37#include "../widgets/ProgressDialog.h"
38
39#include <cmath>
40#include <cstdio>
41#include <stdint.h>
42#include <vector>
43
44#include <wx/crt.h>
45#include <wx/defs.h>
46#include <wx/button.h>
47#include <wx/choice.h>
48#include <wx/combobox.h>
49#include <wx/filename.h>
50#include <wx/intl.h>
51#include <wx/panel.h>
52#include <wx/sizer.h>
53#include <wx/stattext.h>
54#include <wx/textctrl.h>
55#include <wx/timer.h>
56
57// #include "RawAudioGuess.h"
58#include "FormatClassifier.h"
59
60#include "sndfile.h"
61
62class ImportRawDialog final : public wxDialogWrapper {
63
64 public:
65 ImportRawDialog(wxWindow * parent, const wxString & fileName);
67
68 void OnOK(wxCommandEvent & event);
69 void OnCancel(wxCommandEvent & event);
70 void OnPlay(wxCommandEvent & event);
71 void OnDetect(wxCommandEvent & event);
72 void OnChoice(wxCommandEvent & event);
73
74 // in and out
75 // Make static to preserve value for next raw import
76 static int mEncoding;
77 static unsigned mChannels;
78 static int mOffset;
79 static double mRate;
80 static double mPercent;
81
82 private:
83
84 wxButton *mOK;
85 wxChoice *mEncodingChoice;
86 wxChoice *mEndianChoice;
87 wxChoice *mChannelChoice;
88 wxTextCtrl *mOffsetText;
89 wxTextCtrl *mPercentText;
90 wxComboBox *mRateText;
91
92 std::vector<int> mEncodingSubtype;
93
94 wxString mFileName;
95
96 DECLARE_EVENT_TABLE()
97};
98
99// Initial value for Import Raw dialog
100int ImportRawDialog::mEncoding = SF_FORMAT_RAW | SF_ENDIAN_CPU | SF_FORMAT_PCM_16;
101unsigned ImportRawDialog::mChannels = 1;
103double ImportRawDialog::mRate = 0; // -> project rate
104double ImportRawDialog::mPercent = 100.;
105
106
107// This function leaves outTracks empty as an indication of error,
108// but may also throw FileException to make use of the application's
109// user visible error reporting.
110void ImportRaw(const AudacityProject &project, wxWindow *parent, const wxString &fileName,
111 WaveTrackFactory *trackFactory, TrackHolders &outTracks)
112{
113 outTracks.clear();
114
115 TrackHolders results;
116 auto updateResult = ProgressResult::Success;
117
118 {
119 // On first run, set default sample rate from project rate
120 if (ImportRawDialog::mRate < 100.)
122
123 ImportRawDialog dlog(parent, fileName);
124 dlog.ShowModal();
125 if (!dlog.GetReturnCode())
126 return;
127
128 int encoding = dlog.mEncoding;
129 unsigned numChannels = dlog.mChannels;
130 double rate = dlog.mRate;
131 sf_count_t offset = (sf_count_t)dlog.mOffset;
132 double percent = dlog.mPercent;
133
134 SF_INFO sndInfo = { 0 };
135 sndInfo.samplerate = (int)rate;
136 sndInfo.channels = (int)numChannels;
137 sndInfo.format = encoding | SF_FORMAT_RAW;
138
139 wxFile f; // will be closed when it goes out of scope
140 SFFile sndFile;
141
142 if (f.Open(fileName)) {
143 // Even though there is an sf_open() that takes a filename, use the one that
144 // takes a file descriptor since wxWidgets can open a file with a Unicode name and
145 // libsndfile can't (under Windows).
146 sndFile.reset(SFCall<SNDFILE*>(sf_open_fd, f.fd(), SFM_READ, &sndInfo, FALSE));
147 }
148
149 if (!sndFile){
150 char str[1000];
151 sf_error_str((SNDFILE *)NULL, str, 1000);
152 wxPrintf("%s\n", str);
153
154 throw FileException{ FileException::Cause::Open, fileName };
155 }
156
157
158 {
159 int result = sf_command(sndFile.get(), SFC_SET_RAW_START_OFFSET, &offset, sizeof(offset));
160 if (result != 0) {
161 char str[1000];
162 sf_error_str(sndFile.get(), str, 1000);
163 wxPrintf("%s\n", str);
164
165 throw FileException{ FileException::Cause::Read, fileName };
166 }
167 }
168 SFCall<sf_count_t>(sf_seek, sndFile.get(), 0, SEEK_SET);
169
170 auto totalFrames =
171 // fraction of a sf_count_t value
172 (sampleCount)(sndInfo.frames * percent / 100.0);
173
174 //
175 // Sample format:
176 //
177 // In general, go with the user's preferences. However, if
178 // the file is higher-quality, go with a format which preserves
179 // the quality of the original file.
180 //
181
184
185 results.resize(1);
186 auto &channels = results[0];
187 channels.resize(numChannels);
188
189 {
190 // iter not used outside this scope.
191 auto iter = channels.begin();
192 for (decltype(numChannels) c = 0; c < numChannels; ++iter, ++c)
193 *iter = trackFactory->Create(format, rate);
194 }
195 const auto firstChannel = channels.begin()->get();
196 auto maxBlockSize = firstChannel->GetMaxBlockSize();
197
198 SampleBuffer srcbuffer(maxBlockSize * numChannels, format);
199 SampleBuffer buffer(maxBlockSize, format);
200
201 decltype(totalFrames) framescompleted = 0;
202 if (totalFrames < 0) {
203 wxASSERT(false);
204 totalFrames = 0;
205 }
206
207 auto msg = XO("Importing %s").Format( wxFileName::FileName(fileName).GetFullName() );
208
209 /* i18n-hint: 'Raw' means 'unprocessed' here and should usually be translated.*/
210 ProgressDialog progress(XO("Import Raw"), msg);
211
212 size_t block;
213 do {
214 block =
215 limitSampleBufferSize( maxBlockSize, totalFrames - framescompleted );
216
217 sf_count_t sf_result;
218 if (format == int16Sample)
219 sf_result = SFCall<sf_count_t>(sf_readf_short, sndFile.get(), (short *)srcbuffer.ptr(), block);
220 else
221 sf_result = SFCall<sf_count_t>(sf_readf_float, sndFile.get(), (float *)srcbuffer.ptr(), block);
222
223 if (sf_result >= 0) {
224 block = sf_result;
225 }
226 else {
227 // This is not supposed to happen, sndfile.h says result is always
228 // a count, not an invalid value for error
229 throw FileException{ FileException::Cause::Read, fileName };
230 }
231
232 if (block) {
233 auto iter = channels.begin();
234 for(decltype(numChannels) c = 0; c < numChannels; ++iter, ++c) {
235 if (format==int16Sample) {
236 for(decltype(block) j=0; j<block; j++)
237 ((short *)buffer.ptr())[j] =
238 ((short *)srcbuffer.ptr())[numChannels*j+c];
239 }
240 else {
241 for(decltype(block) j=0; j<block; j++)
242 ((float *)buffer.ptr())[j] =
243 ((float *)srcbuffer.ptr())[numChannels*j+c];
244 }
245
246 iter->get()->Append(buffer.ptr(), (format == int16Sample)?int16Sample:floatSample, block);
247 }
248 framescompleted += block;
249 }
250
251 updateResult = progress.Update(
252 framescompleted.as_long_long(),
253 totalFrames.as_long_long()
254 );
255 if (updateResult != ProgressResult::Success)
256 break;
257
258 } while (block > 0 && framescompleted < totalFrames);
259 }
260
261 if (updateResult == ProgressResult::Failed || updateResult == ProgressResult::Cancelled)
262 throw UserException{};
263
264 if (!results.empty() && !results[0].empty()) {
265 for (const auto &channel : results[0])
266 channel->Flush();
267 outTracks.swap(results);
268 }
269}
270
271
272// Get endian choice from SF_FORMAT
273static int getEndianChoice(int sfFormat) {
274 switch (sfFormat & SF_FORMAT_ENDMASK)
275 {
276 default:
277 case SF_ENDIAN_FILE:
278 return 0;
279 case SF_ENDIAN_LITTLE:
280 return 1;
281 case SF_ENDIAN_BIG:
282 return 2;
283 case SF_ENDIAN_CPU:
284 return 3;
285 }
286}
287
288
289//
290// ImportRawDialog
291//
292
293enum {
294 ChoiceID = 9000,
297};
298
299BEGIN_EVENT_TABLE(ImportRawDialog, wxDialogWrapper)
306
307ImportRawDialog::ImportRawDialog(wxWindow * parent, const wxString & fileName)
308: wxDialogWrapper(parent, wxID_ANY, XO("Import Raw Data"),
309 wxDefaultPosition, wxDefaultSize,
310 wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER),
311 mFileName(fileName)
312{
313 wxASSERT(0 < mChannels && mChannels <= 16);
314
315 SetName();
316
317 // Append filename at window title
318 wxFileName wfn{ fileName };
319 wxString windowTitle = XO("%s: %s").Format(GetTitle(), wfn.GetFullName()).Translation();
320 wxDialog::SetTitle(windowTitle);
321
322 ShuttleGui S(this, eIsCreating);
323 TranslatableStrings encodings;
324
325 int num = sf_num_encodings();
326
327 int selection = 0;
328 for (int i = 0; i < num; i++) {
329 SF_INFO info = { 0 };
330
331 int subtype = sf_encoding_index_to_subtype(i);
332 info.format = SF_FORMAT_RAW + SF_ENDIAN_LITTLE + subtype;
333 info.channels = 1;
334 info.samplerate = 44100;
335
336 if (sf_format_check(&info)) {
337 mEncodingSubtype.push_back(subtype);
338 encodings.push_back( Verbatim( sf_encoding_index_name(i) ) );
339
340 if ((mEncoding & SF_FORMAT_SUBMASK) == subtype)
341 selection = mEncodingSubtype.size() - 1;
342 }
343 }
344
345 TranslatableStrings endians{
346 /* i18n-hint: Refers to byte-order. Don't translate "endianness" if you don't
347 know the correct technical word. */
348 XO("No endianness") ,
349 /* i18n-hint: Refers to byte-order. Don't translate this if you don't
350 know the correct technical word. */
351 XO("Little-endian") ,
352 /* i18n-hint: Refers to byte-order. Don't translate this if you don't
353 know the correct technical word. */
354 XO("Big-endian") ,
355 /* i18n-hint: Refers to byte-order. Don't translate "endianness" if you don't
356 know the correct technical word. */
357 XO("Default endianness") ,
358 };
359
360 int endian = getEndianChoice(mEncoding);
361
363 XO("1 Channel (Mono)") ,
364 XO("2 Channels (Stereo)") ,
365 };
366 for (int i = 2; i < 16; i++) {
367 chans.push_back( XO("%d Channels").Format( i + 1 ) );
368 }
369
370 S.StartVerticalLay(false);
371 {
372 S.SetBorder(5);
373 S.StartTwoColumn();
374 {
375 mEncodingChoice = S.Id(ChoiceID).AddChoice(XXO("Encoding:"),
376 encodings,
377 selection);
378 mEndianChoice = S.Id(ChoiceID).AddChoice(XXO("Byte order:"),
379 endians,
380 endian);
381 mChannelChoice = S.Id(ChoiceID).AddChoice(XXO("Channels:"),
382 chans,
383 mChannels - 1);
384 }
385 S.EndTwoColumn();
386
387 S.SetBorder(5);
388 S.StartMultiColumn(3);
389 {
390 // Offset text
391 /* i18n-hint: (noun)*/
392 mOffsetText = S.AddTextBox(XXO("Start offset:"),
393 wxString::Format(wxT("%d"), mOffset),
394 12);
395 S.AddUnits(XO("bytes"));
396
397 // Percent text
398 mPercentText = S.AddTextBox(XXO("Amount to import:"),
399 wxT("100"),
400 12);
401 S.AddUnits(XO("%"));
402
403 // Rate text
404 wxArrayStringEx rates;
405 for (int i = 0; i < AudioIOBase::NumStandardRates; i++) {
406 rates.Add(
407 wxString::Format(wxT("%d"), AudioIOBase::StandardRates[i]));
408 }
409
410 /* i18n-hint: (noun)*/
411 mRateText = S.AddCombo(XXO("Sample rate:"),
412 wxString::Format(wxT("%d"), (int)mRate),
413 rates);
414 /* i18n-hint: This is the abbreviation for "Hertz", or
415 cycles per second. */
416 S.AddUnits(XO("Hz"));
417 }
418 S.EndMultiColumn();
419
420 S.SetBorder(5);
421 S.StartTwoColumn();
422 {
423 /* i18n-hint: Guess format of raw file */
424 S.Id(DetectID).AddButton(XXO("Detect"));
425
426 //
427 // Preview Pane goes here
428 //
429
430 S.AddStandardButtons();
431 }
432 S.EndTwoColumn();
433
434 // Find the OK button, and change its text to 'Import'.
435 // We MUST set mOK because it is used later.
436 mOK = (wxButton *)wxWindow::FindWindowById(wxID_OK, this);
437 mOK->SetLabel(_("&Import"));
438 }
439 S.EndVerticalLay();
440
441 Fit();
442 SetSizeHints(GetSize());
443
444 Centre(wxBOTH);
445}
446
448{
449}
450
451void ImportRawDialog::OnOK(wxCommandEvent & WXUNUSED(event))
452{
453 long l;
454
455 mEncoding = mEncodingSubtype[mEncodingChoice->GetSelection()];
456 mEncoding += (mEndianChoice->GetSelection() * 0x10000000);
457 mChannels = mChannelChoice->GetSelection() + 1;
458 mOffsetText->GetValue().ToLong(&l);
459 mOffset = l;
460 mPercentText->GetValue().ToDouble(&mPercent);
461 mRateText->GetValue().ToDouble(&mRate);
462
463 if (mChannels < 1 || mChannels > 16)
464 mChannels = 1;
465 if (mOffset < 0)
466 mOffset = 0;
467 if (mPercent < 0.0)
468 mPercent = 0.0;
469 if (mPercent > 100.0)
470 mPercent = 100.0;
471 if (mRate < 100.0)
472 mRate = 100.0;
473 // Highest preset sample rate supported in Audacity 2.3.0 is 384 kHz
474 if (mRate > 384000.0)
475 mRate = 384000.0;
476
477 EndModal(true);
478}
479
480void ImportRawDialog::OnCancel(wxCommandEvent & WXUNUSED(event))
481{
482 EndModal(false);
483}
484
485void ImportRawDialog::OnPlay(wxCommandEvent & WXUNUSED(event))
486{
487}
488
489void ImportRawDialog::OnDetect(wxCommandEvent & event)
490{
491 try {
492 // Yes, FormatClassifier currently handles filenames in UTF8 format only, that's a TODO ...
493 FormatClassifier theClassifier(mFileName.utf8_str());
494 mEncoding = theClassifier.GetResultFormatLibSndfile();
495 mChannels = theClassifier.GetResultChannels();
496 } catch (...) {
497 // Something went wrong in FormatClassifier, abort.
498 return;
499 }
500
501 int selection = 0;
502 auto iter = std::find(mEncodingSubtype.begin(), mEncodingSubtype.end(), mEncoding & SF_FORMAT_SUBMASK);
503 if (iter != mEncodingSubtype.end()) // subtype found
504 selection = std::distance(mEncodingSubtype.begin(), iter);
505
506 int endian = getEndianChoice(mEncoding);
507
508 mEncodingChoice->SetSelection(selection);
509 mEndianChoice->SetSelection(endian);
510 mChannelChoice->SetSelection(mChannels - 1);
511
512 OnChoice(event);
513}
514
515void ImportRawDialog::OnChoice(wxCommandEvent & WXUNUSED(event))
516{
517 SF_INFO info;
518
519 memset(&info, 0, sizeof(SF_INFO));
520
521 mEncoding = mEncodingSubtype[mEncodingChoice->GetSelection()];
522 mEncoding += (mEndianChoice->GetSelection() * 0x10000000);
523
524 info.format = mEncoding | SF_FORMAT_RAW;
525 info.channels = mChannelChoice->GetSelection() + 1;
526 info.samplerate = 44100;
527
528 //mOK = (wxButton *)wxWindow::FindWindowById(wxID_OK, this);
529 if (sf_format_check(&info)) {
530 mOK->Enable(true);
531 return;
532 }
533
534 // Try it with 1-channel
535 info.channels = 1;
536 if (sf_format_check(&info)) {
537 mChannelChoice->SetSelection(0);
538 mOK->Enable(true);
539 return;
540 }
541
542 // Otherwise, this is an unsupported format
543 mOK->Enable(false);
544}
wxT("CloseDown"))
END_EVENT_TABLE()
#define str(a)
EVT_BUTTON(wxID_NO, DependencyDialog::OnNo) EVT_BUTTON(wxID_YES
int format
Definition: ExportPCM.cpp:56
unsigned int sf_encoding_index_to_subtype(int i)
Definition: FileFormats.cpp:95
wxString sf_encoding_index_name(int i)
Get the string name of the data encoding of the requested format.
Definition: FileFormats.cpp:84
sampleFormat sf_subtype_to_effective_format(unsigned int format)
Choose the narrowest value in the sampleFormat enumeration for a given libsndfile format.
int sf_num_encodings()
Get the number of data encodings libsndfile supports (in any container or none.
Definition: FileFormats.cpp:75
std::vector< std::vector< std::shared_ptr< WaveTrack > > > TrackHolders
Definition: Import.h:39
The interface that all file import "plugins" (if you want to call them that) must implement....
static int getEndianChoice(int sfFormat)
Definition: ImportRaw.cpp:273
void ImportRaw(const AudacityProject &project, wxWindow *parent, const wxString &fileName, WaveTrackFactory *trackFactory, TrackHolders &outTracks)
Definition: ImportRaw.cpp:110
@ PlayID
Definition: ImportRaw.cpp:295
@ DetectID
Definition: ImportRaw.cpp:296
@ ChoiceID
Definition: ImportRaw.cpp:294
#define XXO(s)
Definition: Internat.h:44
#define XO(s)
Definition: Internat.h:31
#define _(s)
Definition: Internat.h:75
an object holding per-project preferred sample rate
size_t limitSampleBufferSize(size_t bufferSize, sampleCount limit)
Definition: SampleCount.cpp:22
@ floatSample
Definition: SampleFormat.h:34
@ int16Sample
Definition: SampleFormat.h:32
@ eIsCreating
Definition: ShuttleGui.h:39
#define S(N)
Definition: ToChars.cpp:64
TranslatableString Verbatim(wxString str)
Require calls to the one-argument constructor to go through this distinct global function name.
std::vector< TranslatableString > TranslatableStrings
An AudacityException with no visible message.
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:89
static const int StandardRates[]
Array of common audio sample rates.
Definition: AudioIOBase.h:190
static const int NumStandardRates
How many standard sample rates there are.
Definition: AudioIOBase.h:192
Thrown for failure of file or database operations in deeply nested places.
Definition: FileException.h:19
FormatClassifier classifies the sample format and endianness of raw audio files.
unsigned GetResultChannels()
Abstract base class used in importing a file.
static sampleFormat ChooseFormat(sampleFormat effectiveFormat)
Choose appropriate format, which will not be narrower than the specified one.
ImportRawDialog prompts you with options such as endianness and sample size to help you importing dat...
Definition: ImportRaw.cpp:62
void OnCancel(wxCommandEvent &event)
Definition: ImportRaw.cpp:480
static int mOffset
Definition: ImportRaw.cpp:78
wxChoice * mChannelChoice
Definition: ImportRaw.cpp:87
void OnOK(wxCommandEvent &event)
Definition: ImportRaw.cpp:451
void OnDetect(wxCommandEvent &event)
Definition: ImportRaw.cpp:489
wxButton * mOK
Definition: ImportRaw.cpp:84
static double mRate
Definition: ImportRaw.cpp:79
wxChoice * mEndianChoice
Definition: ImportRaw.cpp:86
static int mEncoding
Definition: ImportRaw.cpp:76
static unsigned mChannels
Definition: ImportRaw.cpp:77
wxTextCtrl * mOffsetText
Definition: ImportRaw.cpp:88
std::vector< int > mEncodingSubtype
Definition: ImportRaw.cpp:92
wxComboBox * mRateText
Definition: ImportRaw.cpp:90
static double mPercent
Definition: ImportRaw.cpp:80
void OnPlay(wxCommandEvent &event)
Definition: ImportRaw.cpp:485
ImportRawDialog(wxWindow *parent, const wxString &fileName)
Definition: ImportRaw.cpp:307
wxTextCtrl * mPercentText
Definition: ImportRaw.cpp:89
wxString mFileName
Definition: ImportRaw.cpp:94
wxChoice * mEncodingChoice
Definition: ImportRaw.cpp:85
void OnChoice(wxCommandEvent &event)
Definition: ImportRaw.cpp:515
ProgressDialog Class.
ProgressResult Update(int value, const TranslatableString &message={})
static ProjectRate & Get(AudacityProject &project)
Definition: ProjectRate.cpp:28
double GetRate() const
Definition: ProjectRate.cpp:53
samplePtr ptr() const
Definition: SampleFormat.h:110
Derived from ShuttleGuiBase, an Audacity specific class for shuttling data to and from GUI.
Definition: ShuttleGui.h:628
Can be thrown when user cancels operations, as with a progress dialog. Delayed handler does nothing.
Definition: UserException.h:17
Used to create or clone a WaveTrack, with appropriate context from the project that will own the trac...
Definition: WaveTrack.h:613
std::shared_ptr< WaveTrack > Create()
Creates an unnamed empty WaveTrack with default sample format and default rate.
Definition: WaveTrack.cpp:118
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
Extend wxArrayString with move operations and construction and insertion fromstd::initializer_list.