Audacity 3.2.0
ImportPCM.cpp
Go to the documentation of this file.
1/**********************************************************************
2
3 Audacity: A Digital Audio Editor
4
5 ImportPCM.cpp
6
7 Dominic Mazzoni
8 Leland Lucius
9
10*//****************************************************************//****************************************************************//*******************************************************************/
21
22
23
24#include "Import.h"
25#include "Tags.h"
26
27#include <wx/wx.h>
28#include <wx/ffile.h>
29
30#include "sndfile.h"
31
32#ifndef SNDFILE_1
33#error Requires libsndfile 1.0 or higher
34#endif
35
36#include "FileFormats.h"
37#include "GetAcidizerTags.h"
38#include "ImportPlugin.h"
40#include "ImportUtils.h"
41#include "WaveTrack.h"
42
43#include <algorithm>
44
45#ifdef USE_LIBID3TAG
46 #include <id3tag.h>
47 // DM: the following functions were supposed to have been
48 // included in id3tag.h - should be fixed in the next release
49 // of mad.
50 extern "C" {
51 struct id3_frame *id3_frame_new(char const *);
52 id3_length_t id3_latin1_length(id3_latin1_t const *);
53 void id3_latin1_decode(id3_latin1_t const *, id3_ucs4_t *);
54 }
55#endif
56
57#define DESC XO("WAV, AIFF, and other uncompressed types")
58
59class PCMImportPlugin final : public ImportPlugin
60{
61public:
64 {
65 }
66
68
69 wxString GetPluginStringID() override { return wxT("libsndfile"); }
71 std::unique_ptr<ImportFileHandle> Open(
72 const FilePath &Filename, AudacityProject*) override;
73};
74
75
77{
78public:
79 PCMImportFileHandle(const FilePath &name, SFFile &&file, SF_INFO info);
81
84 void Import(
85 ImportProgressListener& progressListener, WaveTrackFactory* trackFactory,
86 TrackHolders& outTracks, Tags* tags,
87 std::optional<LibFileFormats::AcidizerTags>& outAcidTags) override;
88
89 wxInt32 GetStreamCount() override { return 1; }
90
92 {
93 static TranslatableStrings empty;
94 return empty;
95 }
96
97 void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override
98 {}
99
100private:
102 const SF_INFO mInfo;
105};
106
108{
109 return DESC;
110}
111
112std::unique_ptr<ImportFileHandle> PCMImportPlugin::Open(
113 const FilePath &filename, AudacityProject*)
114{
115 SF_INFO info;
116 wxFile f; // will be closed when it goes out of scope
117 SFFile file;
118
119 memset(&info, 0, sizeof(info));
120
121
122#ifdef __WXGTK__
123 if (filename.Lower().EndsWith(wxT("mp3"))) {
124 // There is a bug in libsndfile where mp3s with duplicated metadata tags
125 // will crash libsndfile and thus audacity.
126 // We have patched the lib-src version of libsndfile, but
127 // for linux the user can build against the system libsndfile which
128 // still has this bug.
129 // This happens in sf_open_fd, which is the very first point of
130 // interaction with libsndfile, so the only workaround is to hardcode
131 // ImportPCM to not handle .mp3. Of course, this will still fail for mp3s
132 // that are mislabeled with a .wav or other extension.
133 // So, in the future we may want to write a simple parser to detect mp3s here.
134 return NULL;
135 }
136#endif
137
138
139 if (f.Open(filename)) {
140 // Even though there is an sf_open() that takes a filename, use the one that
141 // takes a file descriptor since wxWidgets can open a file with a Unicode name and
142 // libsndfile can't (under Windows).
143 file.reset(SFCall<SNDFILE*>(sf_open_fd, f.fd(), SFM_READ, &info, TRUE));
144 }
145
146 // The file descriptor is now owned by "file", so we must tell "f" to leave
147 // it alone. The file descriptor is closed by the destructor of file even if an error
148 // occurs.
149 f.Detach();
150
151 if (!file) {
152 // TODO: Handle error
153 //char str[1000];
154 //sf_error_str((SNDFILE *)NULL, str, 1000);
155
156 return nullptr;
157 } else if (file &&
158 (info.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_OGG) {
159 // mchinen 15.1.2012 - disallowing libsndfile to handle
160 // ogg files because seeking is broken at this date (very slow,
161 // seeks from beginning of file each seek).
162 // This was said by Erik (libsndfile maintainer).
163 // Note that this won't apply to our local libsndfile, so only
164 // linux builds that use --with-libsndfile=system are affected,
165 // as our local libsndfile doesn't do OGG.
166 // In particular ubuntu 10.10 and 11.04 are known to be affected
167 // When the bug is fixed, we can check version to avoid only
168 // the broken builds.
169
170 return nullptr;
171 }
172
173 // Success, so now transfer the duty to close the file from "file".
174 return std::make_unique<PCMImportFileHandle>(filename, std::move(file), info);
175}
176
178 std::make_unique< PCMImportPlugin >()
179};
180
182 SFFile &&file, SF_INFO info)
184 mFile(std::move(file)),
185 mInfo(info)
186{
187 wxASSERT(info.channels >= 0);
188
189 //
190 // Figure out the format to use.
191 //
192 // In general, go with the user's preferences. However, if
193 // the file is higher-quality, go with a format which preserves
194 // the quality of the original file.
195 //
196
197 // Effective format
199 // But maybe different storage format
201}
202
204{
205 // Library strings
206 // See the major_formats and subtype_formats tables in command.c in
207 // libsndfile for this list of possibilities
208
209using Unevaluated = decltype(
210 /* major_formats */
211 XO("AIFF (Apple/SGI)")
212 , XO("AU (Sun/NeXT)")
213 , XO("AVR (Audio Visual Research)")
214 , XO("CAF (Apple Core Audio File)")
215 /* i18n-hint: "codec" is short for a "coder-decoder" algorithm */
216 , XO("FLAC (FLAC Lossless Audio Codec)")
217 , XO("HTK (HMM Tool Kit)")
218 , XO("IFF (Amiga IFF/SVX8/SV16)")
219 , XO("MAT4 (GNU Octave 2.0 / Matlab 4.2)")
220 , XO("MAT5 (GNU Octave 2.1 / Matlab 5.0)")
221 , XO("MPC (Akai MPC 2k)")
222 , XO("OGG (OGG Container format)")
223 , XO("PAF (Ensoniq PARIS)")
224 , XO("PVF (Portable Voice Format)")
225 , XO("RAW (header-less)")
226 , XO("RF64 (RIFF 64)")
227 , XO("SD2 (Sound Designer II)")
228 , XO("SDS (Midi Sample Dump Standard)")
229 , XO("SF (Berkeley/IRCAM/CARL)")
230 , XO("VOC (Creative Labs)")
231 , XO("W64 (SoundFoundry WAVE 64)")
232 , XO("WAV (Microsoft)")
233 , XO("WAV (NIST Sphere)")
234 , XO("WAVEX (Microsoft)")
235 , XO("WVE (Psion Series 3)")
236 , XO("XI (FastTracker 2)")
237);
238
239using Unevaluated2 = decltype(
240 /* subtype_formats */
241 XO("Signed 8 bit PCM")
242 , XO("Signed 16 bit PCM")
243 , XO("Signed 24 bit PCM")
244 , XO("Signed 32 bit PCM")
245 , XO("Unsigned 8 bit PCM")
246 , XO("32 bit float")
247 , XO("64 bit float")
248 , XO("U-Law")
249 , XO("A-Law")
250 , XO("IMA ADPCM")
251 , XO("Microsoft ADPCM")
252 , XO("GSM 6.10")
253 , XO("32kbs G721 ADPCM")
254 , XO("24kbs G723 ADPCM")
255 , XO("12 bit DWVW")
256 , XO("16 bit DWVW")
257 , XO("24 bit DWVW")
258 , XO("VOX ADPCM")
259 , XO("16 bit DPCM")
260 , XO("8 bit DPCM")
261 , XO("Vorbis")
262);
263
264 auto untranslated = SFCall<wxString>(sf_header_name, mInfo.format);
265 return TranslatableString{
266 untranslated, {} };
267}
268
270{
271 return mInfo.frames * mInfo.channels * SAMPLE_SIZE(mFormat);
272}
273
274#ifdef USE_LIBID3TAG
275struct id3_tag_deleter {
276 void operator () (id3_tag *p) const { if (p) id3_tag_delete(p); }
277};
278using id3_tag_holder = std::unique_ptr<id3_tag, id3_tag_deleter>;
279#endif
280
282 ImportProgressListener& progressListener, WaveTrackFactory* trackFactory,
283 TrackHolders& outTracks, Tags* tags,
284 std::optional<LibFileFormats::AcidizerTags>& outAcidTags)
285{
286 BeginImport();
287
288 outTracks.clear();
289
290 wxASSERT(mFile.get());
291
292 auto trackList = ImportUtils::NewWaveTrack(
293 *trackFactory,
294 mInfo.channels,
295 mFormat,
296 mInfo.samplerate);
297
298 auto fileTotalFrames =
299 (sampleCount)mInfo.frames; // convert from sf_count_t
300 auto maxBlockSize = (*trackList->Any<WaveTrack>().begin())->GetMaxBlockSize();
301
302 {
303 // Otherwise, we're in the "copy" mode, where we read in the actual
304 // samples from the file and store our own local copy of the
305 // samples in the tracks.
306
307 // PRL: guard against excessive memory buffer allocation in case of many channels
308 using type = decltype(maxBlockSize);
309 if (mInfo.channels < 1)
310 {
312 return;
313 }
314 auto maxBlock = std::min(maxBlockSize,
315 std::numeric_limits<type>::max() /
316 (mInfo.channels * SAMPLE_SIZE(mFormat))
317 );
318 if (maxBlock < 1)
319 {
321 return;
322 }
323
324 SampleBuffer srcbuffer, buffer;
325 wxASSERT(mInfo.channels >= 0);
326 while (NULL == srcbuffer.Allocate(maxBlock * mInfo.channels, mFormat).ptr() ||
327 NULL == buffer.Allocate(maxBlock, mFormat).ptr())
328 {
329 maxBlock /= 2;
330 if (maxBlock < 1)
331 {
333 return;
334 }
335 }
336
337 decltype(fileTotalFrames) framescompleted = 0;
338
339 long block;
340 do {
341 block = maxBlock;
342
343 if (mFormat == int16Sample)
344 block = SFCall<sf_count_t>(sf_readf_short, mFile.get(), (short *)srcbuffer.ptr(), block);
345 //import 24 bit int as float and have the append function convert it. This is how PCMAliasBlockFile worked too.
346 else
347 block = SFCall<sf_count_t>(sf_readf_float, mFile.get(), (float *)srcbuffer.ptr(), block);
348
349 if(block < 0 || block > (long)maxBlock) {
350 wxASSERT(false);
351 block = maxBlock;
352 }
353
354 if (block) {
355 unsigned c = 0;
356 ImportUtils::ForEachChannel(*trackList, [&](auto& channel)
357 {
358 if (mFormat==int16Sample) {
359 for(int j=0; j<block; j++)
360 ((short *)buffer.ptr())[j] =
361 ((short *)srcbuffer.ptr())[mInfo.channels*j+c];
362 }
363 else {
364 for(int j=0; j<block; j++)
365 ((float *)buffer.ptr())[j] =
366 ((float *)srcbuffer.ptr())[mInfo.channels*j+c];
367 }
368
369 channel.AppendBuffer(
370 buffer.ptr(),
372 block, 1, mEffectiveFormat
373 );
374 ++c;
375 });
376 framescompleted += block;
377 }
378 if(fileTotalFrames > 0)
379 progressListener.OnImportProgress(framescompleted.as_double() / fileTotalFrames.as_double());
380 } while (block > 0 && !IsCancelled() && !IsStopped());
381 }
382
383 if(IsCancelled())
384 {
386 return;
387 }
388
389 ImportUtils::FinalizeImport(outTracks, trackList);
390
391 const char *str;
392
393 str = sf_get_string(mFile.get(), SF_STR_TITLE);
394 if (str) {
395 tags->SetTag(TAG_TITLE, UTF8CTOWX(str));
396 }
397
398 str = sf_get_string(mFile.get(), SF_STR_ALBUM);
399 if (str) {
400 tags->SetTag(TAG_ALBUM, UTF8CTOWX(str));
401 }
402
403 str = sf_get_string(mFile.get(), SF_STR_ARTIST);
404 if (str) {
406 }
407
408 str = sf_get_string(mFile.get(), SF_STR_COMMENT);
409 if (str) {
411 }
412
413 str = sf_get_string(mFile.get(), SF_STR_DATE);
414 if (str) {
415 tags->SetTag(TAG_YEAR, UTF8CTOWX(str));
416 }
417
418 str = sf_get_string(mFile.get(), SF_STR_COPYRIGHT);
419 if (str) {
421 }
422
423 str = sf_get_string(mFile.get(), SF_STR_SOFTWARE);
424 if (str) {
426 }
427
428 str = sf_get_string(mFile.get(), SF_STR_TRACKNUMBER);
429 if (str) {
430 tags->SetTag(TAG_TRACK, UTF8CTOWX(str));
431 }
432
433 str = sf_get_string(mFile.get(), SF_STR_GENRE);
434 if (str) {
435 tags->SetTag(TAG_GENRE, UTF8CTOWX(str));
436 }
437
438 // To begin with, only trust the Muse Hub, with whom we collaborate and can
439 // ensure they comply. In the future we may extend this list.
440 const std::vector<std::string> trustedDistributors { "Muse Hub" };
441 if (const auto acidTags = LibImportExport::GetAcidizerTags(*mFile, trustedDistributors))
442 outAcidTags.emplace(*acidTags);
443
444#if defined(USE_LIBID3TAG)
445 if (((mInfo.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF) ||
446 ((mInfo.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_WAV)) {
447 wxFFile f(GetFilename(), wxT("rb"));
448 if (f.IsOpened()) {
449 char id[5];
450 wxUint32 len;
451
452 id[4] = '\0';
453
454 f.Seek(12); // Skip filetype, length, and formtype
455
456 while (!f.Error()) {
457 f.Read(id, 4); // Get chunk type
458 if (f.Eof()) {
459 break;
460 }
461 f.Read(&len, 4);
462 if((mInfo.format & SF_FORMAT_TYPEMASK) == SF_FORMAT_AIFF)
463 len = wxUINT32_SWAP_ON_LE(len);
464
465 if (wxStricmp(id, "ID3 ") != 0) { // must be case insensitive
466 f.Seek(len + (len & 0x01), wxFromCurrent);
467 continue;
468 }
469
470
471 id3_tag_holder tp;
472 {
473 ArrayOf<id3_byte_t> buffer{ len };
474 if (!buffer) {
475 break;
476 }
477
478 f.Read(buffer.get(), len);
479 tp.reset( id3_tag_parse(buffer.get(), len) );
480 }
481
482 if (!tp) {
483 break;
484 }
485
486 // Loop through all frames
487 bool have_year = false;
488 for (int i = 0; i < (int) tp->nframes; i++) {
489 struct id3_frame *frame = tp->frames[i];
490
491 // wxPrintf("ID: %08x '%4s'\n", (int) *(int *)frame->id, frame->id);
492 // wxPrintf("Desc: %s\n", frame->description);
493 // wxPrintf("Num fields: %d\n", frame->nfields);
494
495 // for (int j = 0; j < (int) frame->nfields; j++) {
496 // wxPrintf("field %d type %d\n", j, frame->fields[j].type );
497 // if (frame->fields[j].type == ID3_FIELD_TYPE_STRINGLIST) {
498 // wxPrintf("num strings %d\n", frame->fields[j].stringlist.nstrings);
499 // }
500 // }
501
502 wxString n, v;
503
504 // Determine the tag name
505 if (strcmp(frame->id, ID3_FRAME_TITLE) == 0) {
506 n = TAG_TITLE;
507 }
508 else if (strcmp(frame->id, ID3_FRAME_ARTIST) == 0) {
509 n = TAG_ARTIST;
510 }
511 else if (strcmp(frame->id, ID3_FRAME_ALBUM) == 0) {
512 n = TAG_ALBUM;
513 }
514 else if (strcmp(frame->id, ID3_FRAME_TRACK) == 0) {
515 n = TAG_TRACK;
516 }
517 else if (strcmp(frame->id, ID3_FRAME_YEAR) == 0) {
518 // LLL: When libid3tag encounters the "TYER" tag, it converts it to a
519 // "ZOBS" (obsolete) tag and adds a "TDRC" tag at the end of the
520 // list of tags using the first 4 characters of the "TYER" tag.
521 // Since we write both the "TDRC" and "TYER" tags, the "TDRC" tag
522 // will always be encountered first in the list. We want use it
523 // since the converted "TYER" tag may have been truncated.
524 if (have_year) {
525 continue;
526 }
527 n = TAG_YEAR;
528 have_year = true;
529 }
530 else if (strcmp(frame->id, ID3_FRAME_COMMENT) == 0) {
531 n = TAG_COMMENTS;
532 }
533 else if (strcmp(frame->id, ID3_FRAME_GENRE) == 0) {
534 n = TAG_GENRE;
535 }
536 else {
537 // Use frame description as default tag name. The descriptions
538 // may include several "meanings" separated by "/" characters, so
539 // we just use the first meaning
540 n = UTF8CTOWX(frame->description).BeforeFirst(wxT('/'));
541 }
542
543 const id3_ucs4_t *ustr = NULL;
544
545 if (n == TAG_COMMENTS) {
546 ustr = id3_field_getfullstring(&frame->fields[3]);
547 }
548 else if (frame->nfields == 3) {
549 ustr = id3_field_getstring(&frame->fields[1]);
550 if (ustr) {
551 // Is this duplication really needed?
552 MallocString<> convStr{ (char *)id3_ucs4_utf8duplicate(ustr) };
553 n = UTF8CTOWX(convStr.get());
554 }
555
556 ustr = id3_field_getstring(&frame->fields[2]);
557 }
558 else if (frame->nfields >= 2) {
559 ustr = id3_field_getstrings(&frame->fields[1], 0);
560 }
561
562 if (ustr) {
563 // Is this duplication really needed?
564 MallocString<> convStr{ (char *)id3_ucs4_utf8duplicate(ustr) };
565 v = UTF8CTOWX(convStr.get());
566 }
567
568 if (!n.empty() && !v.empty()) {
569 tags->SetTag(n, v);
570 }
571 }
572
573 // Convert v1 genre to name
574 if (tags->HasTag(TAG_GENRE)) {
575 long g = -1;
576 if (tags->GetTag(TAG_GENRE).ToLong(&g)) {
577 tags->SetTag(TAG_GENRE, tags->GetGenre(g));
578 }
579 }
580
581 break;
582 }
583 }
584 }
585#endif
586
587 progressListener.OnImportResult(IsStopped()
590}
591
593{
594}
wxT("CloseDown"))
int min(int a, int b)
#define str(a)
const TranslatableString name
Definition: Distortion.cpp:76
FileExtensions sf_get_all_extensions()
wxString sf_header_name(int format)
Get the string name of the specified container format.
sampleFormat sf_subtype_to_effective_format(unsigned int format)
Choose the narrowest value in the sampleFormat enumeration for a given libsndfile format.
XO("Cut/Copy/Paste")
#define DESC
Definition: ImportPCM.cpp:57
static Importer::RegisteredImportPlugin registered
Definition: ImportPCM.cpp:177
The interface that all file import "plugins" (if you want to call them that) must implement....
std::vector< std::shared_ptr< TrackList > > TrackHolders
Definition: ImportRaw.h:24
#define UTF8CTOWX(X)
Definition: Internat.h:157
std::unique_ptr< Character[], freer > MallocString
Definition: MemoryX.h:145
wxString FilePath
Definition: Project.h:21
sampleFormat
The ordering of these values with operator < agrees with the order of increasing bit width.
Definition: SampleFormat.h:30
#define SAMPLE_SIZE(SampleFormat)
Definition: SampleFormat.h:52
#define TAG_TRACK
Definition: Tags.h:61
#define TAG_COMMENTS
Definition: Tags.h:64
#define TAG_GENRE
Definition: Tags.h:63
#define TAG_SOFTWARE
Definition: Tags.h:65
#define TAG_COPYRIGHT
Definition: Tags.h:66
#define TAG_ALBUM
Definition: Tags.h:60
#define TAG_YEAR
Definition: Tags.h:62
#define TAG_TITLE
Definition: Tags.h:58
#define TAG_ARTIST
Definition: Tags.h:59
std::vector< TranslatableString > TranslatableStrings
This simplifies arrays of arrays, each array separately allocated with NEW[] But it might be better t...
Definition: MemoryX.h:26
The top-level handle to an Audacity project. It serves as a source of events that other objects can b...
Definition: Project.h:90
bool IsStopped() const noexcept
FilePath GetFilename() const override
bool IsCancelled() const noexcept
unsigned long long ByteCount
Definition: ImportPlugin.h:114
Base class for FlacImportPlugin, LOFImportPlugin, MP3ImportPlugin, OggImportPlugin and PCMImportPlugi...
Definition: ImportPlugin.h:67
Interface used to report on import state and progress.
virtual void OnImportResult(ImportResult result)=0
Used to report on import result for file handle passed as argument to OnImportFileOpened.
virtual void OnImportProgress(double progress)=0
static TrackListHolder NewWaveTrack(WaveTrackFactory &trackFactory, unsigned nChannels, sampleFormat effectiveFormat, double rate)
Definition: ImportUtils.cpp:35
static sampleFormat ChooseFormat(sampleFormat effectiveFormat)
Choose appropriate format, which will not be narrower than the specified one.
Definition: ImportUtils.cpp:19
static void ForEachChannel(TrackList &trackList, const std::function< void(WaveChannel &)> &op)
Iterates over channels in each wave track from the list.
Definition: ImportUtils.cpp:66
static void FinalizeImport(TrackHolders &outTracks, const std::vector< TrackListHolder > &importedStreams)
Flushes the given channels and moves them to outTracks.
Definition: ImportUtils.cpp:49
An ImportFileHandle for PCM data.
Definition: ImportPCM.cpp:77
const TranslatableStrings & GetStreamInfo() override
Definition: ImportPCM.cpp:91
void Import(ImportProgressListener &progressListener, WaveTrackFactory *trackFactory, TrackHolders &outTracks, Tags *tags, std::optional< LibFileFormats::AcidizerTags > &outAcidTags) override
Definition: ImportPCM.cpp:281
void SetStreamUsage(wxInt32 WXUNUSED(StreamID), bool WXUNUSED(Use)) override
Definition: ImportPCM.cpp:97
wxInt32 GetStreamCount() override
Definition: ImportPCM.cpp:89
TranslatableString GetFileDescription() override
Definition: ImportPCM.cpp:203
ByteCount GetFileUncompressedBytes() override
Definition: ImportPCM.cpp:269
PCMImportFileHandle(const FilePath &name, SFFile &&file, SF_INFO info)
Definition: ImportPCM.cpp:181
sampleFormat mFormat
Definition: ImportPCM.cpp:104
const SF_INFO mInfo
Definition: ImportPCM.cpp:102
sampleFormat mEffectiveFormat
Definition: ImportPCM.cpp:103
An ImportPlugin for PCM data.
Definition: ImportPCM.cpp:60
wxString GetPluginStringID() override
Definition: ImportPCM.cpp:69
TranslatableString GetPluginFormatDescription() override
Definition: ImportPCM.cpp:107
std::unique_ptr< ImportFileHandle > Open(const FilePath &Filename, AudacityProject *) override
Definition: ImportPCM.cpp:112
SampleBuffer & Allocate(size_t count, sampleFormat format)
Definition: SampleFormat.h:151
samplePtr ptr() const
Definition: SampleFormat.h:165
ID3 Tags (for MP3)
Definition: Tags.h:73
bool HasTag(const wxString &name) const
Definition: Tags.cpp:397
wxString GetGenre(int value)
Definition: Tags.cpp:373
void SetTag(const wxString &name, const wxString &value, const bool bSpecialTag=false)
Definition: Tags.cpp:431
wxString GetTag(const wxString &name) const
Definition: Tags.cpp:406
Holds a msgid for the translation catalog; may also bind format arguments.
Used to create or clone a WaveTrack, with appropriate context from the project that will own the trac...
Definition: WaveTrack.h:1281
A Track that contains audio waveform data.
Definition: WaveTrack.h:227
Positions or offsets within audio files need a wide type.
Definition: SampleCount.h:19
std::optional< LibFileFormats::AcidizerTags > GetAcidizerTags(SNDFILE &file, const std::vector< std::string > &trustedDistributors)
Get the Acidizer tags from a file if from a trusted distributor.
STL namespace.