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