external/libmodplug-0.8.8.4/src/load_mod.cpp
author Sam Lantinga <slouken@libsdl.org>
Sun, 09 Jun 2013 16:22:42 -0700
changeset 639 f8901a7ff3f1
permissions -rw-r--r--
Switched from mikmod to libmodplug, which is now in the public domain.
This allows us to add MOD support for iOS and Android, yay!
     1 /*
     2  * This source code is public domain.
     3  *
     4  * Authors: Olivier Lapicque <olivierl@jps.net>,
     5  *          Adam Goode       <adam@evdebs.org> (endian and char fixes for PPC)
     6 */
     7 
     8 #include "stdafx.h"
     9 #include "sndfile.h"
    10 #include "tables.h"
    11 
    12 #ifdef _MSC_VER
    13 //#pragma warning(disable:4244)
    14 #endif
    15 
    16 //////////////////////////////////////////////////////////
    17 // ProTracker / NoiseTracker MOD/NST file support
    18 
    19 void CSoundFile::ConvertModCommand(MODCOMMAND *m) const
    20 //-----------------------------------------------------
    21 {
    22 	UINT command = m->command, param = m->param;
    23 
    24 	switch(command)
    25 	{
    26 	case 0x00:	if (param) command = CMD_ARPEGGIO; break;
    27 	case 0x01:	command = CMD_PORTAMENTOUP; break;
    28 	case 0x02:	command = CMD_PORTAMENTODOWN; break;
    29 	case 0x03:	command = CMD_TONEPORTAMENTO; break;
    30 	case 0x04:	command = CMD_VIBRATO; break;
    31 	case 0x05:	command = CMD_TONEPORTAVOL; if (param & 0xF0) param &= 0xF0; break;
    32 	case 0x06:	command = CMD_VIBRATOVOL; if (param & 0xF0) param &= 0xF0; break;
    33 	case 0x07:	command = CMD_TREMOLO; break;
    34 	case 0x08:	command = CMD_PANNING8; break;
    35 	case 0x09:	command = CMD_OFFSET; break;
    36 	case 0x0A:	command = CMD_VOLUMESLIDE; if (param & 0xF0) param &= 0xF0; break;
    37 	case 0x0B:	command = CMD_POSITIONJUMP; break;
    38 	case 0x0C:	command = CMD_VOLUME; break;
    39 	case 0x0D:	command = CMD_PATTERNBREAK; param = ((param >> 4) * 10) + (param & 0x0F); break;
    40 	case 0x0E:	command = CMD_MODCMDEX; break;
    41 	case 0x0F:	command = (param <= (UINT)((m_nType & (MOD_TYPE_XM|MOD_TYPE_MT2)) ? 0x1F : 0x20)) ? CMD_SPEED : CMD_TEMPO;
    42 				if ((param == 0xFF) && (m_nSamples == 15)) command = 0; break;
    43 	// Extension for XM extended effects
    44 	case 'G' - 55:	command = CMD_GLOBALVOLUME; break;
    45 	case 'H' - 55:	command = CMD_GLOBALVOLSLIDE; if (param & 0xF0) param &= 0xF0; break;
    46 	case 'K' - 55:	command = CMD_KEYOFF; break;
    47 	case 'L' - 55:	command = CMD_SETENVPOSITION; break;
    48 	case 'M' - 55:	command = CMD_CHANNELVOLUME; break;
    49 	case 'N' - 55:	command = CMD_CHANNELVOLSLIDE; break;
    50 	case 'P' - 55:	command = CMD_PANNINGSLIDE; if (param & 0xF0) param &= 0xF0; break;
    51 	case 'R' - 55:	command = CMD_RETRIG; break;
    52 	case 'T' - 55:	command = CMD_TREMOR; break;
    53 	case 'X' - 55:	command = CMD_XFINEPORTAUPDOWN;	break;
    54 	case 'Y' - 55:	command = CMD_PANBRELLO; break;
    55 	case 'Z' - 55:	command = CMD_MIDI;	break;
    56 	default:	command = 0;
    57 	}
    58 	m->command = command;
    59 	m->param = param;
    60 }
    61 
    62 
    63 WORD CSoundFile::ModSaveCommand(const MODCOMMAND *m, BOOL bXM) const
    64 //------------------------------------------------------------------
    65 {
    66 	UINT command = m->command & 0x3F, param = m->param;
    67 
    68 	switch(command)
    69 	{
    70 	case 0:						command = param = 0; break;
    71 	case CMD_ARPEGGIO:			command = 0; break;
    72 	case CMD_PORTAMENTOUP:
    73 		if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM))
    74 		{
    75 			if ((param & 0xF0) == 0xE0) { command=0x0E; param=((param & 0x0F) >> 2)|0x10; break; }
    76 			else if ((param & 0xF0) == 0xF0) { command=0x0E; param &= 0x0F; param|=0x10; break; }
    77 		}
    78 		command = 0x01;
    79 		break;
    80 	case CMD_PORTAMENTODOWN:
    81 		if (m_nType & (MOD_TYPE_S3M|MOD_TYPE_IT|MOD_TYPE_STM))
    82 		{
    83 			if ((param & 0xF0) == 0xE0) { command=0x0E; param=((param & 0x0F) >> 2)|0x20; break; }
    84 			else if ((param & 0xF0) == 0xF0) { command=0x0E; param &= 0x0F; param|=0x20; break; }
    85 		}
    86 		command = 0x02;
    87 		break;
    88 	case CMD_TONEPORTAMENTO:	command = 0x03; break;
    89 	case CMD_VIBRATO:			command = 0x04; break;
    90 	case CMD_TONEPORTAVOL:		command = 0x05; break;
    91 	case CMD_VIBRATOVOL:		command = 0x06; break;
    92 	case CMD_TREMOLO:			command = 0x07; break;
    93 	case CMD_PANNING8:			
    94 		command = 0x08;
    95 		if (bXM)
    96 		{
    97 			if ((m_nType != MOD_TYPE_IT) && (m_nType != MOD_TYPE_XM) && (param <= 0x80))
    98 			{
    99 				param <<= 1;
   100 				if (param > 255) param = 255;
   101 			}
   102 		} else
   103 		{
   104 			if ((m_nType == MOD_TYPE_IT) || (m_nType == MOD_TYPE_XM)) param >>= 1;
   105 		}
   106 		break;
   107 	case CMD_OFFSET:			command = 0x09; break;
   108 	case CMD_VOLUMESLIDE:		command = 0x0A; break;
   109 	case CMD_POSITIONJUMP:		command = 0x0B; break;
   110 	case CMD_VOLUME:			command = 0x0C; break;
   111 	case CMD_PATTERNBREAK:		command = 0x0D; param = ((param / 10) << 4) | (param % 10); break;
   112 	case CMD_MODCMDEX:			command = 0x0E; break;
   113 	case CMD_SPEED:				command = 0x0F; if (param > 0x20) param = 0x20; break;
   114 	case CMD_TEMPO:				if (param > 0x20) { command = 0x0F; break; }
   115 	case CMD_GLOBALVOLUME:		command = 'G' - 55; break;
   116 	case CMD_GLOBALVOLSLIDE:	command = 'H' - 55; break;
   117 	case CMD_KEYOFF:			command = 'K' - 55; break;
   118 	case CMD_SETENVPOSITION:	command = 'L' - 55; break;
   119 	case CMD_CHANNELVOLUME:		command = 'M' - 55; break;
   120 	case CMD_CHANNELVOLSLIDE:	command = 'N' - 55; break;
   121 	case CMD_PANNINGSLIDE:		command = 'P' - 55; break;
   122 	case CMD_RETRIG:			command = 'R' - 55; break;
   123 	case CMD_TREMOR:			command = 'T' - 55; break;
   124 	case CMD_XFINEPORTAUPDOWN:	command = 'X' - 55; break;
   125 	case CMD_PANBRELLO:			command = 'Y' - 55; break;
   126 	case CMD_MIDI:				command = 'Z' - 55; break;
   127 	case CMD_S3MCMDEX:
   128 		switch(param & 0xF0)
   129 		{
   130 		case 0x10:	command = 0x0E; param = (param & 0x0F) | 0x30; break;
   131 		case 0x20:	command = 0x0E; param = (param & 0x0F) | 0x50; break;
   132 		case 0x30:	command = 0x0E; param = (param & 0x0F) | 0x40; break;
   133 		case 0x40:	command = 0x0E; param = (param & 0x0F) | 0x70; break;
   134 		case 0x90:	command = 'X' - 55; break;
   135 		case 0xB0:	command = 0x0E; param = (param & 0x0F) | 0x60; break;
   136 		case 0xA0:
   137 		case 0x50:
   138 		case 0x70:
   139 		case 0x60:	command = param = 0; break;
   140 		default:	command = 0x0E; break;
   141 		}
   142 		break;
   143 	default:		command = param = 0;
   144 	}
   145 	return (WORD)((command << 8) | (param));
   146 }
   147 
   148 
   149 #pragma pack(1)
   150 
   151 typedef struct _MODSAMPLE
   152 {
   153 	CHAR name[22];
   154 	WORD length;
   155 	BYTE finetune;
   156 	BYTE volume;
   157 	WORD loopstart;
   158 	WORD looplen;
   159 } MODSAMPLE, *PMODSAMPLE;
   160 
   161 typedef struct _MODMAGIC
   162 {
   163 	BYTE nOrders;
   164 	BYTE nRestartPos;
   165 	BYTE Orders[128];
   166         char Magic[4];          // changed from CHAR
   167 } MODMAGIC, *PMODMAGIC;
   168 
   169 #pragma pack()
   170 
   171 BOOL IsMagic(LPCSTR s1, LPCSTR s2)
   172 {
   173 	return ((*(DWORD *)s1) == (*(DWORD *)s2)) ? TRUE : FALSE;
   174 }
   175 
   176 
   177 BOOL CSoundFile::ReadMod(const BYTE *lpStream, DWORD dwMemLength)
   178 //---------------------------------------------------------------
   179 {
   180         char s[1024];          // changed from CHAR
   181 	DWORD dwMemPos, dwTotalSampleLen;
   182 	PMODMAGIC pMagic;
   183 	UINT nErr;
   184 
   185 	if ((!lpStream) || (dwMemLength < 0x600)) return FALSE;
   186 	dwMemPos = 20;
   187 	m_nSamples = 31;
   188 	m_nChannels = 4;
   189 	pMagic = (PMODMAGIC)(lpStream+dwMemPos+sizeof(MODSAMPLE)*31);
   190 	// Check Mod Magic
   191 	memcpy(s, pMagic->Magic, 4);
   192 	if ((IsMagic(s, "M.K.")) || (IsMagic(s, "M!K!"))
   193 	 || (IsMagic(s, "M&K!")) || (IsMagic(s, "N.T."))) m_nChannels = 4; else
   194 	if ((IsMagic(s, "CD81")) || (IsMagic(s, "OKTA"))) m_nChannels = 8; else
   195 	if ((s[0]=='F') && (s[1]=='L') && (s[2]=='T') && (s[3]>='4') && (s[3]<='9')) m_nChannels = s[3] - '0'; else
   196 	if ((s[0]>='2') && (s[0]<='9') && (s[1]=='C') && (s[2]=='H') && (s[3]=='N')) m_nChannels = s[0] - '0'; else
   197 	if ((s[0]=='1') && (s[1]>='0') && (s[1]<='9') && (s[2]=='C') && (s[3]=='H')) m_nChannels = s[1] - '0' + 10; else
   198 	if ((s[0]=='2') && (s[1]>='0') && (s[1]<='9') && (s[2]=='C') && (s[3]=='H')) m_nChannels = s[1] - '0' + 20; else
   199 	if ((s[0]=='3') && (s[1]>='0') && (s[1]<='2') && (s[2]=='C') && (s[3]=='H')) m_nChannels = s[1] - '0' + 30; else
   200 	if ((s[0]=='T') && (s[1]=='D') && (s[2]=='Z') && (s[3]>='4') && (s[3]<='9')) m_nChannels = s[3] - '0'; else
   201 	if (IsMagic(s,"16CN")) m_nChannels = 16; else
   202 	if (IsMagic(s,"32CN")) m_nChannels = 32; else m_nSamples = 15;
   203 	// Load Samples
   204 	nErr = 0;
   205 	dwTotalSampleLen = 0;
   206 	for	(UINT i=1; i<=m_nSamples; i++)
   207 	{
   208 		PMODSAMPLE pms = (PMODSAMPLE)(lpStream+dwMemPos);
   209 		MODINSTRUMENT *psmp = &Ins[i];
   210 		UINT loopstart, looplen;
   211 
   212 		memcpy(m_szNames[i], pms->name, 22);
   213 		m_szNames[i][22] = 0;
   214 		psmp->uFlags = 0;
   215 		psmp->nLength = bswapBE16(pms->length)*2;
   216 		dwTotalSampleLen += psmp->nLength;
   217 		psmp->nFineTune = MOD2XMFineTune(pms->finetune & 0x0F);
   218 		psmp->nVolume = 4*pms->volume;
   219 		if (psmp->nVolume > 256) { psmp->nVolume = 256; nErr++; }
   220 		psmp->nGlobalVol = 64;
   221 		psmp->nPan = 128;
   222 		loopstart = bswapBE16(pms->loopstart)*2;
   223 		looplen = bswapBE16(pms->looplen)*2;
   224 		// Fix loops
   225 		if ((looplen > 2) && (loopstart+looplen > psmp->nLength)
   226 		 && (loopstart/2+looplen <= psmp->nLength))
   227 		{
   228 			loopstart /= 2;
   229 		}
   230 		psmp->nLoopStart = loopstart;
   231 		psmp->nLoopEnd = loopstart + looplen;
   232 		if (psmp->nLength < 4) psmp->nLength = 0;
   233 		if (psmp->nLength)
   234 		{
   235 			UINT derr = 0;
   236 			if (psmp->nLoopStart >= psmp->nLength) { psmp->nLoopStart = psmp->nLength-1; derr|=1; }
   237 			if (psmp->nLoopEnd > psmp->nLength) { psmp->nLoopEnd = psmp->nLength; derr |= 1; }
   238 			if (psmp->nLoopStart > psmp->nLoopEnd) derr |= 1;
   239 			if ((psmp->nLoopStart > psmp->nLoopEnd) || (psmp->nLoopEnd <= 8)
   240 			 || (psmp->nLoopEnd - psmp->nLoopStart <= 4))
   241 			{
   242 				psmp->nLoopStart = 0;
   243 				psmp->nLoopEnd = 0;
   244 			}
   245 			if (psmp->nLoopEnd > psmp->nLoopStart)
   246 			{
   247 				psmp->uFlags |= CHN_LOOP;
   248 			}
   249 		}
   250 		dwMemPos += sizeof(MODSAMPLE);
   251 	}
   252 	if ((m_nSamples == 15) && (dwTotalSampleLen > dwMemLength * 4)) return FALSE;
   253 	pMagic = (PMODMAGIC)(lpStream+dwMemPos);
   254 	dwMemPos += sizeof(MODMAGIC);
   255 	if (m_nSamples == 15) dwMemPos -= 4;
   256 	memset(Order, 0,sizeof(Order));
   257 	memcpy(Order, pMagic->Orders, 128);
   258 
   259 	UINT nbp, nbpbuggy, nbpbuggy2, norders;
   260 
   261 	norders = pMagic->nOrders;
   262 	if ((!norders) || (norders > 0x80))
   263 	{
   264 		norders = 0x80;
   265 		while ((norders > 1) && (!Order[norders-1])) norders--;
   266 	}
   267 	nbpbuggy = 0;
   268 	nbpbuggy2 = 0;
   269 	nbp = 0;
   270 	for (UINT iord=0; iord<128; iord++)
   271 	{
   272 		UINT i = Order[iord];
   273 		if ((i < 0x80) && (nbp <= i))
   274 		{
   275 			nbp = i+1;
   276 			if (iord<norders) nbpbuggy = nbp;
   277 		}
   278 		if (i >= nbpbuggy2) nbpbuggy2 = i+1;
   279 	}
   280 	for (UINT iend=norders; iend<MAX_ORDERS; iend++) Order[iend] = 0xFF;
   281 	norders--;
   282 	m_nRestartPos = pMagic->nRestartPos;
   283 	if (m_nRestartPos >= 0x78) m_nRestartPos = 0;
   284 	if (m_nRestartPos + 1 >= (UINT)norders) m_nRestartPos = 0;
   285 	if (!nbp) return FALSE;
   286 	DWORD dwWowTest = dwTotalSampleLen+dwMemPos;
   287 	if ((IsMagic(pMagic->Magic, "M.K.")) && (dwWowTest + nbp*8*256 == dwMemLength)) m_nChannels = 8;
   288 	if ((nbp != nbpbuggy) && (dwWowTest + nbp*m_nChannels*256 != dwMemLength))
   289 	{
   290 		if (dwWowTest + nbpbuggy*m_nChannels*256 == dwMemLength) nbp = nbpbuggy;
   291 		else nErr += 8;
   292 	} else
   293 	if ((nbpbuggy2 > nbp) && (dwWowTest + nbpbuggy2*m_nChannels*256 == dwMemLength))
   294 	{
   295 		nbp = nbpbuggy2;
   296 	}
   297 	if ((dwWowTest < 0x600) || (dwWowTest > dwMemLength)) nErr += 8;
   298 	if ((m_nSamples == 15) && (nErr >= 16)) return FALSE;
   299 	// Default settings	
   300 	m_nType = MOD_TYPE_MOD;
   301 	m_nDefaultSpeed = 6;
   302 	m_nDefaultTempo = 125;
   303 	m_nMinPeriod = 14 << 2;
   304 	m_nMaxPeriod = 3424 << 2;
   305 	memcpy(m_szNames, lpStream, 20);
   306 	// Setting channels pan
   307 	for (UINT ich=0; ich<m_nChannels; ich++)
   308 	{
   309 		ChnSettings[ich].nVolume = 64;
   310 		if (gdwSoundSetup & SNDMIX_MAXDEFAULTPAN)
   311 			ChnSettings[ich].nPan = (((ich&3)==1) || ((ich&3)==2)) ? 256 : 0;
   312 		else
   313 			ChnSettings[ich].nPan = (((ich&3)==1) || ((ich&3)==2)) ? 0xC0 : 0x40;
   314 	}
   315 	// Reading channels
   316 	for (UINT ipat=0; ipat<nbp; ipat++)
   317 	{
   318 		if (ipat < MAX_PATTERNS)
   319 		{
   320 			if ((Patterns[ipat] = AllocatePattern(64, m_nChannels)) == NULL) break;
   321 			PatternSize[ipat] = 64;
   322 			if (dwMemPos + m_nChannels*256 >= dwMemLength) break;
   323 			MODCOMMAND *m = Patterns[ipat];
   324 			LPCBYTE p = lpStream + dwMemPos;
   325 			for (UINT j=m_nChannels*64; j; m++,p+=4,j--)
   326 			{
   327 				BYTE A0=p[0], A1=p[1], A2=p[2], A3=p[3];
   328 				UINT n = ((((UINT)A0 & 0x0F) << 8) | (A1));
   329 				if ((n) && (n != 0xFFF)) m->note = GetNoteFromPeriod(n << 2);
   330 				m->instr = ((UINT)A2 >> 4) | (A0 & 0x10);
   331 				m->command = A2 & 0x0F;
   332 				m->param = A3;
   333 				if ((m->command) || (m->param)) ConvertModCommand(m);
   334 			}
   335 		}
   336 		dwMemPos += m_nChannels*256;
   337 	}
   338 	// Reading instruments
   339 	DWORD dwErrCheck = 0;
   340 	for (UINT ismp=1; ismp<=m_nSamples; ismp++) if (Ins[ismp].nLength)
   341 	{
   342 		LPSTR p = (LPSTR)(lpStream+dwMemPos);
   343 		UINT flags = 0;
   344 		if (dwMemPos + 5 >= dwMemLength) break;
   345 		if (!strnicmp(p, "ADPCM", 5))
   346 		{
   347 			flags = 3;
   348 			p += 5;
   349 			dwMemPos += 5;
   350 		}
   351 		DWORD dwSize = ReadSample(&Ins[ismp], flags, p, dwMemLength - dwMemPos);
   352 		if (dwSize)
   353 		{
   354 			dwMemPos += dwSize;
   355 			dwErrCheck++;
   356 		}
   357 	}
   358 #ifdef MODPLUG_TRACKER
   359 	return TRUE;
   360 #else
   361 	return (dwErrCheck) ? TRUE : FALSE;
   362 #endif
   363 }
   364 
   365 
   366 #ifndef MODPLUG_NO_FILESAVE
   367 
   368 #ifdef _MSC_VER
   369 #pragma warning(disable:4100)
   370 #endif
   371 
   372 BOOL CSoundFile::SaveMod(LPCSTR lpszFileName, UINT nPacking)
   373 //----------------------------------------------------------
   374 {
   375 	BYTE insmap[32];
   376 	UINT inslen[32];
   377 	BYTE bTab[32];
   378 	BYTE ord[128];
   379 	FILE *f;
   380 
   381 	if ((!m_nChannels) || (!lpszFileName)) return FALSE;
   382 	if ((f = fopen(lpszFileName, "wb")) == NULL) return FALSE;
   383 	memset(ord, 0, sizeof(ord));
   384 	memset(inslen, 0, sizeof(inslen));
   385 	if (m_nInstruments)
   386 	{
   387 		memset(insmap, 0, sizeof(insmap));
   388 		for (UINT i=1; i<32; i++) if (Headers[i])
   389 		{
   390 			for (UINT j=0; j<128; j++) if (Headers[i]->Keyboard[j])
   391 			{
   392 				insmap[i] = Headers[i]->Keyboard[j];
   393 				break;
   394 			}
   395 		}
   396 	} else
   397 	{
   398 		for (UINT i=0; i<32; i++) insmap[i] = (BYTE)i;
   399 	}
   400 	// Writing song name
   401 	fwrite(m_szNames, 20, 1, f);
   402 	// Writing instrument definition
   403 	for (UINT iins=1; iins<=31; iins++)
   404 	{
   405 		MODINSTRUMENT *pins = &Ins[insmap[iins]];
   406 		memcpy(bTab, m_szNames[iins],22);
   407 		inslen[iins] = pins->nLength;
   408 		if (inslen[iins] > 0x1fff0) inslen[iins] = 0x1fff0;
   409 		bTab[22] = inslen[iins] >> 9;
   410 		bTab[23] = inslen[iins] >> 1;
   411 		if (pins->RelativeTone < 0) bTab[24] = 0x08; else
   412 		if (pins->RelativeTone > 0) bTab[24] = 0x07; else
   413 		bTab[24] = (BYTE)XM2MODFineTune(pins->nFineTune);
   414 		bTab[25] = pins->nVolume >> 2;
   415 		bTab[26] = pins->nLoopStart >> 9;
   416 		bTab[27] = pins->nLoopStart >> 1;
   417 		bTab[28] = (pins->nLoopEnd - pins->nLoopStart) >> 9;
   418 		bTab[29] = (pins->nLoopEnd - pins->nLoopStart) >> 1;
   419 		fwrite(bTab, 30, 1, f);
   420 	}
   421 	// Writing number of patterns
   422 	UINT nbp=0, norders=128;
   423 	for (UINT iord=0; iord<128; iord++)
   424 	{
   425 		if (Order[iord] == 0xFF)
   426 		{
   427 			norders = iord;
   428 			break;
   429 		}
   430 		if ((Order[iord] < 0x80) && (nbp<=Order[iord])) nbp = Order[iord]+1;
   431 	}
   432 	bTab[0] = norders;
   433 	bTab[1] = m_nRestartPos;
   434 	fwrite(bTab, 2, 1, f);
   435 	// Writing pattern list
   436 	if (norders) memcpy(ord, Order, norders);
   437 	fwrite(ord, 128, 1, f);
   438 	// Writing signature
   439 	if (m_nChannels == 4)
   440 		lstrcpy((LPSTR)&bTab, "M.K.");
   441 	else
   442 		wsprintf((LPSTR)&bTab, "%luCHN", m_nChannels);
   443 	fwrite(bTab, 4, 1, f);
   444 	// Writing patterns
   445 	for (UINT ipat=0; ipat<nbp; ipat++) if (Patterns[ipat])
   446 	{
   447 		BYTE s[64*4];
   448 		MODCOMMAND *m = Patterns[ipat];
   449 		for (UINT i=0; i<64; i++) if (i < PatternSize[ipat])
   450 		{
   451 			LPBYTE p=s;
   452 			for (UINT c=0; c<m_nChannels; c++,p+=4,m++)
   453 			{
   454 				UINT param = ModSaveCommand(m, FALSE);
   455 				UINT command = param >> 8;
   456 				param &= 0xFF;
   457 				if (command > 0x0F) command = param = 0;
   458 				if ((m->vol >= 0x10) && (m->vol <= 0x50) && (!command) && (!param)) { command = 0x0C; param = m->vol - 0x10; }
   459 				UINT period = m->note;
   460 				if (period)
   461 				{
   462 					if (period < 37) period = 37;
   463 					period -= 37;
   464 					if (period >= 6*12) period = 6*12-1;
   465 					period = ProTrackerPeriodTable[period];
   466 				}
   467 				UINT instr = (m->instr > 31) ? 0 : m->instr;
   468 				p[0] = ((period >> 8) & 0x0F) | (instr & 0x10);
   469 				p[1] = period & 0xFF;
   470 				p[2] = ((instr & 0x0F) << 4) | (command & 0x0F);
   471 				p[3] = param;
   472 			}
   473 			fwrite(s, m_nChannels, 4, f);
   474 		} else
   475 		{
   476 			memset(s, 0, m_nChannels*4);
   477 			fwrite(s, m_nChannels, 4, f);
   478 		}
   479 	}
   480 	// Writing instruments
   481 	for (UINT ismpd=1; ismpd<=31; ismpd++) if (inslen[ismpd])
   482 	{
   483 		MODINSTRUMENT *pins = &Ins[insmap[ismpd]];
   484 		UINT flags = RS_PCM8S;
   485 #ifndef NO_PACKING
   486 		if (!(pins->uFlags & (CHN_16BIT|CHN_STEREO)))
   487 		{
   488 			if ((nPacking) && (CanPackSample((char *)pins->pSample, inslen[ismpd], nPacking)))
   489 			{
   490 				fwrite("ADPCM", 1, 5, f);
   491 				flags = RS_ADPCM4;
   492 			}
   493 		}
   494 #endif
   495 		WriteSample(f, pins, flags, inslen[ismpd]);
   496 	}
   497 	fclose(f);
   498 	return TRUE;
   499 }
   500 
   501 #ifdef _MSC_VER
   502 #pragma warning(default:4100)
   503 #endif
   504 
   505 #endif // MODPLUG_NO_FILESAVE