1 /*
2 * Load_wav.cpp
3 * ------------
4 * Purpose: WAV importer
5 * Notes : This loader converts each WAV channel into a separate mono sample.
6 * Authors: Olivier Lapicque
7 * OpenMPT Devs
8 * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
9 */
10
11
12 #include "stdafx.h"
13 #include "Loaders.h"
14 #include "WAVTools.h"
15 #include "openmpt/soundbase/SampleConvert.hpp"
16 #include "openmpt/soundbase/SampleDecode.hpp"
17 #include "SampleCopy.h"
18
19
20 OPENMPT_NAMESPACE_BEGIN
21
22
23 /////////////////////////////////////////////////////////////
24 // WAV file support
25
26
27 template <typename SampleConversion>
CopyWavChannel(ModSample & sample,const FileReader & file,size_t channelIndex,size_t numChannels,SampleConversion conv=SampleConversion ())28 static bool CopyWavChannel(ModSample &sample, const FileReader &file, size_t channelIndex, size_t numChannels, SampleConversion conv = SampleConversion())
29 {
30 MPT_ASSERT(sample.GetNumChannels() == 1);
31 MPT_ASSERT(sample.GetElementarySampleSize() == sizeof(typename SampleConversion::output_t));
32
33 const size_t offset = channelIndex * sizeof(typename SampleConversion::input_t) * SampleConversion::input_inc;
34
35 if(sample.AllocateSample() == 0 || !file.CanRead(offset))
36 {
37 return false;
38 }
39
40 const std::byte *inBuf = file.GetRawData<std::byte>().data();
41 CopySample<SampleConversion>(reinterpret_cast<typename SampleConversion::output_t*>(sample.samplev()), sample.nLength, 1, inBuf + offset, file.BytesLeft() - offset, numChannels, conv);
42 return true;
43 }
44
45
ProbeFileHeaderWAV(MemoryFileReader file,const uint64 * pfilesize)46 CSoundFile::ProbeResult CSoundFile::ProbeFileHeaderWAV(MemoryFileReader file, const uint64 *pfilesize)
47 {
48 RIFFHeader fileHeader;
49 if(!file.ReadStruct(fileHeader))
50 {
51 return ProbeWantMoreData;
52 }
53 if((fileHeader.magic != RIFFHeader::idRIFF && fileHeader.magic != RIFFHeader::idLIST)
54 || (fileHeader.type != RIFFHeader::idWAVE && fileHeader.type != RIFFHeader::idwave))
55 {
56 return ProbeFailure;
57 }
58 MPT_UNREFERENCED_PARAMETER(pfilesize);
59 return ProbeSuccess;
60 }
61
62
ReadWAV(FileReader & file,ModLoadingFlags loadFlags)63 bool CSoundFile::ReadWAV(FileReader &file, ModLoadingFlags loadFlags)
64 {
65 WAVReader wavFile(file);
66
67 if(!wavFile.IsValid()
68 || wavFile.GetNumChannels() == 0
69 || wavFile.GetNumChannels() > MAX_BASECHANNELS
70 || wavFile.GetNumChannels() >= MAX_SAMPLES
71 || wavFile.GetBitsPerSample() == 0
72 || wavFile.GetBitsPerSample() > 64
73 || (wavFile.GetBitsPerSample() < 32 && wavFile.GetSampleFormat() == WAVFormatChunk::fmtFloat)
74 || (wavFile.GetSampleFormat() != WAVFormatChunk::fmtPCM && wavFile.GetSampleFormat() != WAVFormatChunk::fmtFloat))
75 {
76 return false;
77 } else if(loadFlags == onlyVerifyHeader)
78 {
79 return true;
80 }
81
82 InitializeGlobals(MOD_TYPE_MPT);
83 m_ContainerType = MOD_CONTAINERTYPE_WAV;
84 m_nChannels = std::max(wavFile.GetNumChannels(), uint16(2));
85 Patterns.ResizeArray(2);
86 if(!Patterns.Insert(0, 64) || !Patterns.Insert(1, 64))
87 {
88 return false;
89 }
90
91 m_modFormat.formatName = U_("RIFF WAVE");
92 m_modFormat.type = U_("wav");
93 m_modFormat.charset = mpt::Charset::Windows1252;
94
95 const SmpLength sampleLength = wavFile.GetSampleLength();
96
97 // Setting up module length
98 // Calculate sample length in ticks at tempo 125
99 const uint32 sampleRate = std::max(uint32(1), wavFile.GetSampleRate());
100 const uint32 sampleTicks = mpt::saturate_cast<uint32>(((sampleLength * 50) / sampleRate) + 1);
101 uint32 ticksPerRow = std::max((sampleTicks + 63u) / 63u, uint32(1));
102
103 Order().assign(1, 0);
104 ORDERINDEX numOrders = 1;
105 while(ticksPerRow >= 32 && numOrders < MAX_ORDERS)
106 {
107 numOrders++;
108 ticksPerRow = (sampleTicks + (64 * numOrders - 1)) / (64 * numOrders);
109 }
110 Order().resize(numOrders, 1);
111
112 m_nSamples = wavFile.GetNumChannels();
113 m_nInstruments = 0;
114 m_nDefaultSpeed = ticksPerRow;
115 m_nDefaultTempo.Set(125);
116 m_SongFlags = SONG_LINEARSLIDES;
117
118 for(CHANNELINDEX channel = 0; channel < m_nChannels; channel++)
119 {
120 ChnSettings[channel].Reset();
121 ChnSettings[channel].nPan = (channel % 2u) ? 256 : 0;
122 }
123
124 // Setting up pattern
125 PatternRow pattern = Patterns[0].GetRow(0);
126 pattern[0].note = pattern[1].note = NOTE_MIDDLEC;
127 pattern[0].instr = pattern[1].instr = 1;
128
129 const FileReader sampleChunk = wavFile.GetSampleData();
130
131 // Read every channel into its own sample lot.
132 for(SAMPLEINDEX channel = 0; channel < GetNumSamples(); channel++)
133 {
134 pattern[channel].note = pattern[0].note;
135 pattern[channel].instr = static_cast<ModCommand::INSTR>(channel + 1);
136
137 ModSample &sample = Samples[channel + 1];
138 sample.Initialize();
139 sample.uFlags = CHN_PANNING;
140 sample.nLength = sampleLength;
141 sample.nC5Speed = wavFile.GetSampleRate();
142 m_szNames[channel + 1] = "";
143 wavFile.ApplySampleSettings(sample, GetCharsetInternal(), m_szNames[channel + 1]);
144
145 if(wavFile.GetNumChannels() > 1)
146 {
147 // Pan all samples appropriately
148 switch(channel)
149 {
150 case 0:
151 sample.nPan = 0;
152 break;
153 case 1:
154 sample.nPan = 256;
155 break;
156 case 2:
157 sample.nPan = (wavFile.GetNumChannels() == 3 ? 128u : 64u);
158 pattern[channel].command = CMD_S3MCMDEX;
159 pattern[channel].param = 0x91;
160 break;
161 case 3:
162 sample.nPan = 192;
163 pattern[channel].command = CMD_S3MCMDEX;
164 pattern[channel].param = 0x91;
165 break;
166 default:
167 sample.nPan = 128;
168 break;
169 }
170 }
171
172 if(wavFile.GetBitsPerSample() > 8)
173 {
174 sample.uFlags.set(CHN_16BIT);
175 }
176
177 if(wavFile.GetSampleFormat() == WAVFormatChunk::fmtFloat)
178 {
179 if(wavFile.GetBitsPerSample() <= 32)
180 CopyWavChannel<SC::ConversionChain<SC::Convert<int16, float32>, SC::DecodeFloat32<littleEndian32>>>(sample, sampleChunk, channel, wavFile.GetNumChannels());
181 else
182 CopyWavChannel<SC::ConversionChain<SC::Convert<int16, float64>, SC::DecodeFloat64<littleEndian64>>>(sample, sampleChunk, channel, wavFile.GetNumChannels());
183 } else
184 {
185 if(wavFile.GetBitsPerSample() <= 8)
186 CopyWavChannel<SC::DecodeUint8>(sample, sampleChunk, channel, wavFile.GetNumChannels());
187 else if(wavFile.GetBitsPerSample() <= 16)
188 CopyWavChannel<SC::DecodeInt16<0, littleEndian16>>(sample, sampleChunk, channel, wavFile.GetNumChannels());
189 else if(wavFile.GetBitsPerSample() <= 24)
190 CopyWavChannel<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt24<0, littleEndian24>>>(sample, sampleChunk, channel, wavFile.GetNumChannels());
191 else if(wavFile.GetBitsPerSample() <= 32)
192 CopyWavChannel<SC::ConversionChain<SC::Convert<int16, int32>, SC::DecodeInt32<0, littleEndian32>>>(sample, sampleChunk, channel, wavFile.GetNumChannels());
193 else if(wavFile.GetBitsPerSample() <= 64)
194 CopyWavChannel<SC::ConversionChain<SC::Convert<int16, int64>, SC::DecodeInt64<0, littleEndian64>>>(sample, sampleChunk, channel, wavFile.GetNumChannels());
195 }
196 sample.PrecomputeLoops(*this, false);
197
198 }
199
200 return true;
201 }
202
203
204 OPENMPT_NAMESPACE_END
205