I post this to game programming because I think that XM is a DirectX Music format...

I've seen it used in HUGI magazines and the programmer of HUGI has a small assembly program available that plays XM files.

What I have hard time finding out is what exactly is XM? It is synthesised music like MIDI, is it WAVE, is it a mix of the two? Is it directX? How do I create my own XM file? I haven't seen any sound editors that produce it, I use Cool Edit Pro 2002 (the newly released update)... Is it mainly for game programming?


Thanks,
_Shawn
Posted on 2002-10-14 11:39:26 by _Shawn
Xm is neither a midi nor wav file but it's closer to a midi. It's like the mod and s3m file. The instruments are waves and they are used to play the notes and special effects in tracks. This file type is used mostly in games as it can be looped easily from inside the file. Probably the best of this kind of file type is the .it file format, though there might be some other that came out tha't I'm not aware of.

To produce them, I used to use Impulse tracker, but now there are many more. Googling with s3m mod tracker should come with enough links, though ModPlug Tracker is pretty good.

It has nothing to do with directx, you can play them on dos or probably even linux...
Posted on 2002-10-14 11:54:32 by Silas
XM is an eXtended Module format, born in the DOS, it was introduced by FastTracker 2.0 (previous versions were using MOD format). There was a competitive program, ScreamTracker, which had introduced the S3M format with the 3.0 version (it's ScreamTracker 3 Module), but XM was generally more powerful. And then the ImpulseTracker arrived with the IT format, which was the extension of both S3M and XM format, it's the most powerful module format now.
Posted on 2002-10-14 15:25:59 by Tomasz Grysztar
Also to add to the very good expplanations above
I will say that esp for games XM is good because:

1)It can be looped very easy and so it offers many minutes of music and can adapt to game status much easyer than .wav or .mp3 files
2) There is a very good source code asm player by Chris Dragan AFAIK
3) Size is small . Not as small as midi but it will play the same not depending on music board (while midi depend heavely on sound/music board type) because samples are ecoded as waves...
4) It will not require/eat much CPU time (more than wave but much less than mp3)
Posted on 2002-10-14 22:25:30 by BogdanOntanu
IF you are programming a player, just remember panning/pitch envelopes there are too many players out there that ignore the envelopes. They think I don't notice it but I do, I have sharp ears. I'll post an audio resampling theory you can use:4.1 - Mixer data structure
We know that the mixing routine must generate MixLength bytes of audio data into a specified buffer in memory. It must take the raw sample data, and adjust it according to pitch, volume and pan variables while accounting for any loop points in the sample. The data which maintains all this information is best stored in a data structure local to the mixing code. There should a structure for each channel to be mixed and the format should contain:


(dword) Mix_CurrentPtr ;Pointer to current sample
(dword) Mix_LoopEnd ;Pointer to end of sample/loop end
(dword) Mix_LoopLen ;Sample loop length (0 if no loop)
(word) Mix_LowSpeed ;Scaling rate (fractional part)
(word) Mix_HighSpeed ;Scaling rate (integer part)
(word) Mix_Count ;Scaling fractional counter
(byte) Mix_Volume ;Volume of sample
(byte) Mix_PanPos ;Pan position
(byte) Mix_ActiveFlag ;Is voice active flag? (0 = inactive)
(byte) Mix_SampleType (optional) ;Defines: 8 or 16-bit sample,
; bi-directional looping, etc.

Note: for efficiency reasons these variables may not necessarily be stored exactly as indicated in this structure. A flat-memory model is assumed throughout this description to simplify explanation.

Whenever the device dependent code calls the tracking code to update channel variables, it must then interpret the changes to these variables (section 2.3) and set the variables in this mixer data structure accordingly.


--------------------------------------------------------------------------------

4.2 - Resampling digital audio
Resampling of digital audio refers to the procedure used to make a sound sampled at one frequency sound the same when played back at a different mixing rate. If a sample is recorded at 44khz and is to be played back at the same pitch but using an output frequency of 32khz then a certain percentage of the original sample data has to be skipped during playback or the sample won't maintain the original pitch. Likewise, if a 22khz sample is to be played back at the same pitch using a 32khz mixing rate, some of the sample data will have to be scaled to insert more data in the output stream than is in the original sample.

The basic resampling algorithm involves determining the ratio of desired frequency against output frequency and then scaling the sample data accordingly. In the mixing code the resampling is done on the run by stepping through the sample data by a scaling factor which involves both integer and fractional components instead of simply incrementing 1 byte at a time.

The sample frequency is determined by the tracking code as a combination of the current pitch period and the middle-C frequency for the current sample (C2SPD). The scaling factor is determined from the sample frequency by the ratio:


Sample Freq
Scale = -----------
Mixing Freq


For efficiency reasons scaling is done using fixed point instead of floating point arithmetic. 32-bit precision (16-bit integer and 16-bit fractional) gives good results for the range of frequencies typically found in this kind of situation, hence the scaling factor is typically broken into two 16-bit variables (Mix_HighSpeed and Mix_LowSpeed).

Example pseudo-code: determining sample scaling factor



(word)Mix_HighSpeed = (SampleFreq / MixingRate);

(word)Mix_LowSpeed = (((SampleFreq % MixingRate) << 16) / MixingRate);
^^^^^^^^^^^^^^^^^^^^^^^
remainder of previous division

The actual scaling routine is then implemented by using a carry-counter to simulate fractional stepping through the sample. The Mix_Count variable is maintained for this purpose and is only reset to zero when a new sample is started.

To step through a sample with the scaling factor, firstly add Mix_LowSpeed to Mix_Count. Then add Mix_HighSpeed AND the overflow carry from the previous operation to Mix_CurrentPtr. Get the byte which Mix_CurrentPtr is pointing to and add it to the output stream. Repeat for all the bytes needed to fill the output buffer.

Sample loops are handled by checking if Mix_CurrentPtr has reached or passed Mix_LoopEnd. If so then Mix_LoopLen is subtracted from Mix_CurrentPtr. Note that Mix_Count is NOT reset to zero when a sample loops. If the sample does not loop then the sample stops when Mix_CurrentPtr is greater or equal to Mix_LoopEnd.

Example assembly code: scaled sample-stepping (not optimised)


For demonstration Mix_CurrentPtr is assumed to be only 16-bits. In a real routine all registers and variables would be 32-bits for speed.


StepSample: mov ax, ;add Mix_LowSpeed to Mix_Count
add ,ax ;carry flag is set on add overflow
mov ax, ;add Mix_HighSpeed to Mix_CurrentPtr
adc ax, ; with carry flag
cmp ax, ;check if passed loop endpoint, skip
jb dontloop ; if not passed endpoint else subtract
sub ax, ; Mix_LoopLen from Mix_CurrentPtr
dontloop: mov ,ax ;store Mix_CurrentPtr for next loop



--------------------------------------------------------------------------------

4.3 - Mixing samples with volume
Once we have a way of scaling the samples correctly we need to combine the samples for each of the channels into one output stream. As mentioned in the first section, mixing audio is achieved simply by adding all of the component samples (assuming the samples are signed). However, to control the sample volume and protect against distortion and clipping if the range exceeds the 8-bit limit on the output data, the data from each sample must be scaled before being summed into the total output.

The theoretical approach to applying volume to a sample involves multiplying the sample by a volume scaling factor before being summed into the output.

Example assembly code: sample volume scaling (not optimised)


This routine would be applied for each channel for each byte in the output stream. For demonstration it assumes a 16-bit sample pointer, 8-bit signed sample data and an 8-bit signed output stream.


SumSample: mov si, ;get pointer to current sample byte
mov al,ds: ;get the current sample byte
imul byte ptr ;perform SIGNED multiply by vol. scale
add ,ah ;then ADD it to the output byte
;NOTE: add AH register NOT AL


By adding the AH register to the output byte, it effectively is performing the C operation:


(char)OutputByte = ((char)*(Mix_CurrentPtr) * (char)Mix_Volume) / 256;
This makes the Mix_Volume variable equivalent to a fractional multiply which is needed to make the sample quieter to prevent overflow in the output stream.

The Mix_Volume variable can be calculated from the total number of channels to be mixed and the volume of the channel, and needs to be updated whenever the tracking code specifies a new volume on the channel. The equation to determine Mix_Volume for 8-bit samples and an 8-bit output stream without allowing any sample clipping is thus:


Mix_Volume = ((256/NumberOfChans)*MODVolume) >> 6;
Once all the channels have been added to the OutputByte, it can then be converted to an unsigned format (since soundblaster cards have an unsigned data format) and then placed in the output buffer. The easiest way to convert an 8-bit signed value into 8-bit unsigned is to flip bit 7 using exclusive or. ie. OutputData XOR 128.

Example pseudo-code: complete mixing routine (very unoptimised but functional)


The whole mixing routine is then implemented as a group of nested loops, where MixLength is the number of bytes desired in the output stream. StepSample and SumSample are the algorithms defined previously.


void Mix8bitMono( int MixLength, char * buffer )
{
static ChannelDataStruc Channels;
int MixCount;
int channel;
char OutputByte;

MixCount = MixLength;

while( MixCount )
{
OutputByte = 0;
for( channel = 0, channel < NumberOfChannels, channel++ )
{
StepSample( *Channels );
SumSample( *Channels );
}
*(buffer++) = OutputByte ^ 128;
MixCount--;
}
}

I spent alot of time looking for a working algothrim, use this and if you find a better once please share with me and the rest! :D If you need more info to be on your way please let me know
Posted on 2002-10-16 19:59:09 by x86asm
So XM files are pitch generated (similar to MIDI) but aren't pre-recorded waves (unless wavetables?)

I was listening to the opening music on HUGI 24 and it sounds like it's a lot more robust than MIDI for being pitches... chorus, various tracks, reverb, distortion, pitch changes (guitar > bends and slides and vibratos) very good bass and dums on my Creative Audigy with Creative 5.1 speakers and subwoofer... a bit better than any MIDI I've ever heard...

Or, does it get mixed with waves in the file? Where can I find impluse tracker or similar? Is it legit these days?


Thanks,
_Shawn
Posted on 2002-10-16 20:24:09 by _Shawn
Nope XM is not pitch generated, instead it uses a series of wave samples that are mixed and looped in a carefull way, x86asm was trying to make you understand how one can use a sample of a C2 note taken from a very good instrument to scale it a little and generate other notes without loosing too much quality. When more quality is needed you can always use more samples stored inside XM file. It is a kind of software synthetiser.

AFAIK Chriss Dragan has a free written in win32asm source code level XM player ...

http://zp.amsnet.pl/cdragan/assembly.html


but there are many closed source (sometimes better?) XM players and editors out there... some are free and some are comercial.

As long as you create your own music (aka "track it") it is sure legal stuff... just do not copy other people's tracks without permission ...
Posted on 2002-10-16 21:32:43 by BogdanOntanu
I was surprised when I read the FastTracker 2.0 documentation a while ago. It turned out that the guys who invented the XM format lived in the same city as me! Cool guys!
Posted on 2002-10-24 01:09:17 by gliptic