1 /*
2  * ModSample.h
3  * -----------
4  * Purpose: Module Sample header class and helpers
5  * Notes  : (currently none)
6  * Authors: OpenMPT Devs
7  * The OpenMPT source code is released under the BSD license. Read LICENSE for more details.
8  */
9 
10 
11 #pragma once
12 
13 #include "openmpt/all/BuildSettings.hpp"
14 
15 OPENMPT_NAMESPACE_BEGIN
16 
17 class CSoundFile;
18 
19 // Sample Struct
20 struct ModSample
21 {
22 	SmpLength nLength;						// In frames
23 	SmpLength nLoopStart, nLoopEnd;			// Ditto
24 	SmpLength nSustainStart, nSustainEnd;	// Ditto
25 	union
26 	{
27 		void  *pSample;						// Pointer to sample data
28 		int8  *pSample8;					// Pointer to 8-bit sample data
29 		int16 *pSample16;					// Pointer to 16-bit sample data
30 	} pData;
31 	uint32 nC5Speed;						// Frequency of middle-C, in Hz (for IT/S3M/MPTM)
32 	uint16 nPan;							// Default sample panning (if pan flag is set), 0...256
33 	uint16 nVolume;							// Default volume, 0...256 (ignored if uFlags[SMP_NODEFAULTVOLUME] is set)
34 	uint16 nGlobalVol;						// Global volume (sample volume is multiplied by this), 0...64
35 	SampleFlags uFlags;						// Sample flags (see ChannelFlags enum)
36 	int8   RelativeTone;					// Relative note to middle c (for MOD/XM)
37 	int8   nFineTune;						// Finetune period (for MOD/XM), -128...127, unit is 1/128th of a semitone
38 	VibratoType nVibType;					// Auto vibrato type
39 	uint8  nVibSweep;						// Auto vibrato sweep (i.e. how long it takes until the vibrato effect reaches its full depth)
40 	uint8  nVibDepth;						// Auto vibrato depth
41 	uint8  nVibRate;						// Auto vibrato rate (speed)
42 	uint8  rootNote;						// For multisample import
43 
44 	//char name[MAX_SAMPLENAME];			// Maybe it would be nicer to have sample names here, but that would require some refactoring.
45 	mpt::charbuf<MAX_SAMPLEFILENAME> filename;
GetFilenameModSample46 	std::string GetFilename() const { return filename; }
47 
48 	union
49 	{
50 		std::array<SmpLength, 9> cues;
51 		OPLPatch adlib;
52 	};
53 
54 	ModSample(MODTYPE type = MOD_TYPE_NONE)
55 	{
56 		pData.pSample = nullptr;
57 		Initialize(type);
58 	}
59 
HasSampleDataModSample60 	bool HasSampleData() const noexcept
61 	{
62 		MPT_ASSERT(!pData.pSample || (pData.pSample && nLength > 0));  // having sample pointer implies non-zero sample length
63 		return pData.pSample != nullptr && nLength != 0;
64 	}
65 
samplevModSample66 	MPT_FORCEINLINE const void *samplev() const noexcept
67 	{
68 		return pData.pSample;
69 	}
samplevModSample70 	MPT_FORCEINLINE void *samplev() noexcept
71 	{
72 		return pData.pSample;
73 	}
samplebModSample74 	MPT_FORCEINLINE const std::byte *sampleb() const noexcept
75 	{
76 		return mpt::void_cast<const std::byte*>(pData.pSample);
77 	}
samplebModSample78 	MPT_FORCEINLINE std::byte *sampleb() noexcept
79 	{
80 		return mpt::void_cast<std::byte*>(pData.pSample);
81 	}
sample8ModSample82 	MPT_FORCEINLINE const int8 *sample8() const noexcept
83 	{
84 		MPT_ASSERT(GetElementarySampleSize() == sizeof(int8));
85 		return pData.pSample8;
86 	}
sample8ModSample87 	MPT_FORCEINLINE int8 *sample8() noexcept
88 	{
89 		MPT_ASSERT(GetElementarySampleSize() == sizeof(int8));
90 		return pData.pSample8;
91 	}
sample16ModSample92 	MPT_FORCEINLINE const int16 *sample16() const noexcept
93 	{
94 		MPT_ASSERT(GetElementarySampleSize() == sizeof(int16));
95 		return pData.pSample16;
96 	}
sample16ModSample97 	MPT_FORCEINLINE int16 *sample16() noexcept
98 	{
99 		MPT_ASSERT(GetElementarySampleSize() == sizeof(int16));
100 		return pData.pSample16;
101 	}
102 
103 	// Return the size of one (elementary) sample in bytes.
GetElementarySampleSizeModSample104 	uint8 GetElementarySampleSize() const noexcept { return (uFlags & CHN_16BIT) ? 2 : 1; }
105 
106 	// Return the number of channels in the sample.
GetNumChannelsModSample107 	uint8 GetNumChannels() const noexcept { return (uFlags & CHN_STEREO) ? 2 : 1; }
108 
109 	// Return the number of bytes per frame (Channels * Elementary Sample Size)
GetBytesPerSampleModSample110 	uint8 GetBytesPerSample() const noexcept { return GetElementarySampleSize() * GetNumChannels(); }
111 
112 	// Return the size which pSample is at least.
GetSampleSizeInBytesModSample113 	SmpLength GetSampleSizeInBytes() const noexcept { return nLength * GetBytesPerSample(); }
114 
115 	// Returns sample rate of the sample. The argument is needed because
116 	// the sample rate is obtained differently for different module types.
117 	uint32 GetSampleRate(const MODTYPE type) const;
118 
119 	// Translate sample properties between two given formats.
120 	void Convert(MODTYPE fromType, MODTYPE toType);
121 
122 	// Initialize sample slot with default values.
123 	void Initialize(MODTYPE type = MOD_TYPE_NONE);
124 
125 	// Copies sample data from another sample slot and ensures that the 16-bit/stereo flags are set accordingly.
126 	bool CopyWaveform(const ModSample &smpFrom);
127 
128 	// Allocate sample based on a ModSample's properties.
129 	// Returns number of bytes allocated, 0 on failure.
130 	size_t AllocateSample();
131 	// Allocate sample memory. On sucess, a pointer to the silenced sample buffer is returned. On failure, nullptr is returned.
132 	static void *AllocateSample(SmpLength numFrames, size_t bytesPerSample);
133 	// Compute sample buffer size in bytes, including any overhead introduced by pre-computed loops and such. Returns 0 if sample is too big.
134 	static size_t GetRealSampleBufferSize(SmpLength numSamples, size_t bytesPerSample);
135 
136 	void FreeSample();
137 	static void FreeSample(void *samplePtr);
138 
139 	// Set loop points and update loop wrap-around buffer
140 	void SetLoop(SmpLength start, SmpLength end, bool enable, bool pingpong, CSoundFile &sndFile);
141 	// Set sustain loop points and update loop wrap-around buffer
142 	void SetSustainLoop(SmpLength start, SmpLength end, bool enable, bool pingpong, CSoundFile &sndFile);
143 	// Update loop wrap-around buffer
144 	void PrecomputeLoops(CSoundFile &sndFile, bool updateChannels = true);
145 
HasLoopModSample146 	constexpr bool HasLoop() const noexcept { return uFlags[CHN_LOOP] && nLoopEnd > nLoopStart; }
HasSustainLoopModSample147 	constexpr bool HasSustainLoop() const noexcept { return uFlags[CHN_SUSTAINLOOP] && nSustainEnd > nSustainStart; }
HasPingPongLoopModSample148 	constexpr bool HasPingPongLoop() const noexcept { return uFlags.test_all(CHN_LOOP | CHN_PINGPONGLOOP) && nLoopEnd > nLoopStart; }
HasPingPongSustainLoopModSample149 	constexpr bool HasPingPongSustainLoop() const noexcept { return uFlags.test_all(CHN_SUSTAINLOOP | CHN_PINGPONGSUSTAIN) && nSustainEnd > nSustainStart; }
150 
151 	// Remove loop points if they're invalid.
152 	void SanitizeLoops();
153 
154 	// Transpose <-> Frequency conversions
155 	static uint32 TransposeToFrequency(int transpose, int finetune = 0);
156 	void TransposeToFrequency();
157 	static std::pair<int8, int8> FrequencyToTranspose(uint32 freq);
158 	void FrequencyToTranspose();
159 
160 	// Transpose the sample by amount specified in octaves (i.e. amount=1 transposes one octave up)
161 	void Transpose(double amount);
162 
163 	// Check if the sample's cue points are the default cue point set.
164 	bool HasCustomCuePoints() const;
165 	void SetDefaultCuePoints();
166 	// Set cue points so that they are suitable for regular offset command extension
167 	void Set16BitCuePoints();
168 
169 	void SetAdlib(bool enable, OPLPatch patch = OPLPatch{{}});
170 };
171 
172 OPENMPT_NAMESPACE_END
173