1//************************************ bs::framework - Copyright 2018 Marko Pintera **************************************//
2//*********** Licensed under the MIT license. See LICENSE.md for full terms. This notice is not to be removed. ***********//
3#include "BsOAImporter.h"
4#include "FileSystem/BsDataStream.h"
5#include "FileSystem/BsFileSystem.h"
6#include "BsWaveDecoder.h"
7#include "BsFLACDecoder.h"
8#include "BsOggVorbisDecoder.h"
9#include "BsOggVorbisEncoder.h"
10#include "Audio/BsAudioClipImportOptions.h"
11#include "Audio/BsAudioUtility.h"
12
13namespace bs
14{
15 OAImporter::OAImporter()
16 :SpecificImporter()
17 {
18
19 }
20
21 bool OAImporter::isExtensionSupported(const String& ext) const
22 {
23 String lowerCaseExt = ext;
24 StringUtil::toLowerCase(lowerCaseExt);
25
26 return lowerCaseExt == u8"wav" || lowerCaseExt == u8"flac" || lowerCaseExt == u8"ogg";
27 }
28
29 bool OAImporter::isMagicNumberSupported(const UINT8* magicNumPtr, UINT32 numBytes) const
30 {
31 // Don't check for magic number, rely on extension
32 return true;
33 }
34
35 SPtr<ImportOptions> OAImporter::createImportOptions() const
36 {
37 return bs_shared_ptr_new<AudioClipImportOptions>();
38 }
39
40 SPtr<Resource> OAImporter::import(const Path& filePath, SPtr<const ImportOptions> importOptions)
41 {
42 AudioDataInfo info;
43 UINT32 bytesPerSample;
44 UINT32 bufferSize;
45 UINT8* sampleBuffer;
46 {
47 Lock fileLock = FileScheduler::getLock(filePath);
48 SPtr<DataStream> stream = FileSystem::openFile(filePath);
49
50 String extension = filePath.getExtension();
51 StringUtil::toLowerCase(extension);
52
53 UPtr<AudioDecoder> reader;
54 if (extension == u8".wav")
55 reader = bs_unique_ptr_new<WaveDecoder>();
56 else if (extension == u8".flac")
57 reader = bs_unique_ptr_new<FLACDecoder>();
58 else if (extension == u8".ogg")
59 reader = bs_unique_ptr_new<OggVorbisDecoder>();
60
61 if (reader == nullptr)
62 return nullptr;
63
64 if (!reader->isValid(stream))
65 return nullptr;
66
67 if (!reader->open(stream, info))
68 return nullptr;
69
70 bytesPerSample = info.bitDepth / 8;
71 bufferSize = info.numSamples * bytesPerSample;
72 sampleBuffer = (UINT8*)bs_alloc(bufferSize);
73
74 reader->read(sampleBuffer, info.numSamples);
75 }
76
77 SPtr<const AudioClipImportOptions> clipIO = std::static_pointer_cast<const AudioClipImportOptions>(importOptions);
78
79 // If 3D, convert to mono
80 if(clipIO->is3D && info.numChannels > 1)
81 {
82 UINT32 numSamplesPerChannel = info.numSamples / info.numChannels;
83
84 UINT32 monoBufferSize = numSamplesPerChannel * bytesPerSample;
85 UINT8* monoBuffer = (UINT8*)bs_alloc(monoBufferSize);
86
87 AudioUtility::convertToMono(sampleBuffer, monoBuffer, info.bitDepth, numSamplesPerChannel, info.numChannels);
88
89 info.numSamples = numSamplesPerChannel;
90 info.numChannels = 1;
91
92 bs_free(sampleBuffer);
93
94 sampleBuffer = monoBuffer;
95 bufferSize = monoBufferSize;
96 }
97
98 // Convert bit depth if needed
99 if(clipIO->bitDepth != info.bitDepth)
100 {
101 UINT32 outBufferSize = info.numSamples * (clipIO->bitDepth / 8);
102 UINT8* outBuffer = (UINT8*)bs_alloc(outBufferSize);
103
104 AudioUtility::convertBitDepth(sampleBuffer, info.bitDepth, outBuffer, clipIO->bitDepth, info.numSamples);
105
106 info.bitDepth = clipIO->bitDepth;
107
108 bs_free(sampleBuffer);
109
110 sampleBuffer = outBuffer;
111 bufferSize = outBufferSize;
112 }
113
114 // Encode to Ogg Vorbis if needed
115 if(clipIO->format == AudioFormat::VORBIS)
116 {
117 // Note: If the original source was in Ogg Vorbis we could just copy it here, but instead we decode to PCM and
118 // then re-encode which is redundant. If later we decide to copy be aware that the engine encodes Ogg in a
119 // specific quality, and the the import source might have lower or higher bitrate/quality.
120 UINT8* encodedSamples = OggVorbisEncoder::PCMToOggVorbis(sampleBuffer, info, bufferSize);
121
122 bs_free(sampleBuffer);
123 sampleBuffer = encodedSamples;
124 }
125
126 SPtr<MemoryDataStream> sampleStream = bs_shared_ptr_new<MemoryDataStream>(sampleBuffer, bufferSize);
127
128 AUDIO_CLIP_DESC clipDesc;
129 clipDesc.bitDepth = info.bitDepth;
130 clipDesc.format = clipIO->format;
131 clipDesc.frequency = info.sampleRate;
132 clipDesc.numChannels = info.numChannels;
133 clipDesc.readMode = clipIO->readMode;
134 clipDesc.is3D = clipIO->is3D;
135
136 SPtr<AudioClip> clip = AudioClip::_createPtr(sampleStream, bufferSize, info.numSamples, clipDesc);
137
138 const String fileName = filePath.getFilename(false);
139 clip->setName(fileName);
140
141 return clip;
142 }
143}