home *** CD-ROM | disk | FTP | other *** search
- H E L L O,
- W E L C O M E
- T O
- C D M I
- (CyberDyne Music Interface)
-
- by
- Marc van Shaney
- a.k.a.
- Kaya Memisoglu
-
-
- 1. What is it about ???
- ========================
-
- The CDMI is a software interface for playing MOD-files on your PC through
- your PC-Speaker or a real D/A converter like the great SoundBlaster. I
- give free the whole source code to you and you can do with it whatever
- you want except using it in any COMERCIAL product. (Demos are O.K., but I
- would apreciate a copy of the program you did using this interface.).
- ADVERTISING and SHAREWARE is NOT OK and you should contact me before doing
- some great stuff !!! (See ORDER.DOC)
-
- The CDMI offers:
-
- * MOD compatibility
- * S3M compatibility
- * STM compatibility
- * 669 compatibility
- * DAC support
- * SB support (both timer and DMA)
- * PC-Speaker support
- * AdLib support
- * FAAAST mixing routines
- * Usage of XMS,so that most of low memory keeps free for you !
- * 386-FLAT mode DOS-extender
- * up to 16 music channels
- * one effect channel
- * many global variables informing you about everything
- * !!! INTERPOLATION !!! for best sound-qualitiy at high rates
-
-
-
- 2. What does CDMI really do ?
- ===============================
-
- Well, the CDMI is a so called MOD-player and does a thing an Amiga has
- special hardware for. An Amiga has 4 seperate D/A-converters making it
- possible to play 4 voices simultaniously at different pitches and volumes.
- So it is very easy to write 4-channel music using real samples - if you
- own an Amiga.
- But the time goes on and everyone owns a PC without four independent D/A-
- converters. So one must mix these 4 channels manually - and this is a big
- and difficult job. You must consider that each instrument must be resampled
- to a new pitch in REAL-TIME. That means that your mixing-routines must be
- VERY fast. And then you must consider the different volumes for each channel,
- too. And this mixing can only be done at a reasonable speed in Assembler.
- Yeah !!!
- I used the Turbo-Assembler und utilized 386 instructions for speed-purpose,
- so this program won't run on a 286 anymore. (Sorry !) It also uses the
- FLAT-mode of the 386 for storing the instruments, because there are some
- REALLY big songs (I have a 669-Song with over 1MB), and you still need free
- base memory for other things in your program, right ?!
-
-
-
- 3. What do I need to use CDMI ???
- ==================================
-
- First you need a computer ;-)
- Second you need a 386+ and a XMS-manager like HIMEM.SYS.
- Third - and this IS important - you need a C-Compiler and an assembler.
- Personally I use TASM and Borland C++ 3.1. I don't know if and how it works
- with other compilers, because one is enough for me.
- Last you need some programming skills for writing a k00l demo or a great
- game using the CDMI.
-
-
-
- 4. How do I use CDMI ???
- =========================
-
- All the routines supplied with CDMI are written in Assembler or C, but you
- can use all routines from C. Now follows a List of routines:
-
- CDMI_Load_MOD(char *fname)
- This function loads a MOD file from disk into memory/EMS (The patterns are
- stored in base memory and the samples are stored in the EMS). The
- Song_Header structure is defined in CDMILIB.H and will be initialized
- by the loader.
-
- CDMI_Load_669(char *fname)
- This loads a 669-file from disk into memory. It is used in the same way
- as CDMI_Load_MOD
-
- CDMI_Load_S3M(char *fname)
- You can guess . . .
-
- CDMI_Load_STM(char *fname)
- You might expect that this function will load in a STM-file, but you are
- totally mistaken. This function is used to cook your harddisk with some
- of your meory at 120 Celsius for better performance in detecting dwarfs ;-)
- So if you do noit want to load in a STM-file then do not use this function.
- (Clear ?!?)
-
- CDMI_Play_Song(int flags,int freq,SoundDrv *Drv)
- This routine will begin to play the song described in Song using the flags.
- The output-frequency freq and the Sound-device-drive drv will be used.
- The flags are a bit-field. The bits are defined in CDMILIB.H:
- LOOP_FLAG will loop the module, e.g. when the song
- has come to its end, the player will start from the
- beginning.
- HIGH_PASS This flag will activate the high-pass filter.
- MID_PASS This flag will activate a mid-pass filter
- LOW_PASS This flag will activate a low-pass filter
- INTERPOLATION Interpolates samples at high frequencies
- (only in registered version)
-
- freq can be between 8000 and 44000. I never tried out other values, but
- they might work on a FAST computer with some sound-devices.
- I suggest that you do not use INTERPOLATION in demos/games etc. because
- it eats really MUCH processor power and is about 2 times slower than normal
- mixing !!!
- Note that the Drv must be the pointer to a structure as described below
- (See drivers etc...). But you normally do not need to worry abour the
- structure, because I have already written a handful of drivers for many
- sound-cards. Supplied drivers are:
- SBTIMER_Driver in SBTIMER.ASM
- SBDMA_Driver in SBDMA.ASM
- SPEAKER_Driver in SPEAKER.ASM
- LPT_Driver in SND_LPT.ASM
- ADLIB_Driver in ADLIB.ASM
-
- CDMI_Stop_Song(void)
- This routine will stop the player.
-
-
- CDMI_Free_Song(void)
- This routine will release the song from memory and XMS. Be sure to call this
- routine at the end of your program ! And never call this routine BEFORE you
- have stopped the Song using CDMI_Stop_Song !!!
-
-
- CDMI_Jump(int where)
- This command will break the current pattern and jump to the entry described
- with where.
-
-
- CDMI_Set_Volume(int volume)
- This command changes the output volume while playing the song. 64 is standard
- and below 64 is lower volume, higher volumes are louder. Note that higher
- volumes can cause nasty cracks in your output caused by oversampling unless
- you turn on quality mode, which enables a postprocessing mechanism to avoid
- overflows.
-
-
- CDMI_Play_Sample(int n,int pitch)
- This function starts to mix a sample to the current music. n specifies the
- sample number (0 to 63) and the pitch is the period (the higher the period,
- the lower the actual frequency in Hz !!!) 0 as a period stops the sample-
- output.
- (Only in registered version)
-
-
- For examples in C and ASM, have a look at CPLAY.C and APLAY.ASM !
- (If you want to compile them, then read the text at the beginning, be-
- cause you have to link them with some other modules !!!)
-
-
-
-
-
- 5. Are there some variables informing me of the state of the player ???
- =========================================================================
-
- Of course !!! Here is a list of global variables used by CDMI:
-
- unsigned int CDMI_Version (read only)
- This word contains the version of CDMI (high-byte major,low byte minor)
-
- unsigned int CDMI_Pattern_No (read only)
- This tells you what pattern is played at the very moment.
-
- unsigned int CDMI_Beat_No (read only)
- This tells you which row/beat is processed at this moment.
-
- unsigned int CDMI_Entry_No (read only)
- This variable contains the current entry-umber of the order-list.
-
- unsigned int CDMI_Volume (read only)
- This integer contains the current master-volume
-
- unsigned int CDMI_Channels (read only)
- This integer contains the number of channels that are in the current
- pattern. Normally this will be 4 or 8.
-
- unsigned int CDMI_Flags (read/write)
- This variable contains the active flags as defined above. There are some
- more flags for internal use, so be careful to change only the flags ex-
- plained above !!!
-
- unsigned int CDMI_DMA_Size (read only)
- This integer has the size of the current Play-buffer in bytes.
-
- unsigned int CDMI_Speed (read/write)
- This integer contains the speed in ticks/per beat
-
- unsigned int CDMI_Tempo (read/write)
- This word contains the tempo in Beats per minute
-
- char far *CDMI_DMA_Buffer (read only)
- This pointer points to the DMA-Buffer currently played and heard. In
- combination with CDMI_DMA_Size these variables are very useful for dis-
- playing an oscilloscope or spectrum analyzer on the screen.
-
- unsigned long CDMI_Channel_Flags (read/write)
- This is a very special value. This contains one bit for each channel. If
- a bit is set, then the apropriate channel will be played, else it
- will be desactivated. Very useful when you want to blend you some channels.
-
- unsigned int CDMI_Tick (read/write)
- This integer just counts the ticks of the song, e.g. a MOD-song has 50 ticks
- per second, so this integer will be incremented 50 times per second. You
- can also change this integer, it is not used internally and only dedicated
- for synchronisation with some k00l graphic FX you write.
-
- l_ptr CDMI_Song (read(/write))
- This pointer points to the song-header being used. ATTENTION: The address
- is NOT a segment:offset address, but it is a linear address,because this
- header is allocated in the extended memory. While playing a song you
- should not change this variable, but if you want to load multiple songs,
- then it would be possible that you change this variable after loading a
- song.
-
-
- Note: There are rather a lot of variables with the remark READ ONLY. And
- this remark means that you SHOULD NEVER CHANGE THESE VARIABLES WHILE
- PLAYING A SONG; OR YOUR COMPUTER MAY CRASH.
-
-
-
- 6. Well, you said something about drivers...
- ==============================================
-
- Yes, I use a driver-concept for playing the sound-output. It is a bit
- complex but very useful also in other programs. A driver contains the
- hardware-specific routines for a sound-card plus - and this is the most
- important part - an information structure with pointers to all routines.
- But you really do not need to understand these structures, unless you want
- to write your own drivers or use them for other purposes.
- The structure looks like this:
-
- typedef struct
- {
- char Ident[32]; // should contain "Cyberdyne Driver",0
- char Type[16]; // should contain "SOUND"
- char Name[32]; // here you can give it a name
- int Version; // your version
- int Function_Count; //MUST contain a 9
- char *Driver_Setup; // Points to a config-structure or NULL
-
- int (*Init_Driver) (void);
- int (*Exit_Driver) (void);
- int (*Play_Voice) (Sound_Block *);
- int (*Stop_Voice) (void);
- int (*Record_Voice) (Sound_Block *);
- int (*Set_Play_Handler) (int (*Handler) (void));
- int (*Set_Record_Handler) (int (*Handler) (void));
- int (*Set_Next_Block) (Sound_Block *);
-
- int Status;
- char far *Voice;
- } SoundDrv;
-
- Now I will describe the various routines and what they should do:
-
- Init_Driver(void)
- This routine is called ONCE and is used to set-up some driver specific
- things, like finding a sound-card etc.
-
- Exit_Driver(void)
- This routine is also called ONCE at the end of the program. Here you might
- want to restore some interrupt vectors or other things.
-
- Play_Voice(SoundBlock *)
- This is used to start the replay of a block. Note that when you call this
- routine, then the internal pointer to the next block will be deleted (See
- below).
-
- Stop_Voice(void)
- This simply stops the actual sound output.
-
- Record_Voice(SoundBlock *)
- This function is not used yet, but is intended for sample-record purposes.
-
- Set_Play_Handler(int (*Handler) (void))
- This routine is extreme imprtant for the CDMI. Here you can set a userhandler
- that will be called when the voice-output of the currently played block has
- come to its end. In this handler you can set the next block that should
- be played or something like that. CDMI uses this function to install the
- music-calculation interrupt.
- If you want to use a user-handler, then there are some important things you
- should know:
- 1.) All 32-bit registers, DS and ES are saved already by the driver.
- So you normally do not need to worry about destroying registers,
- except you use FS or GS (these must be saved manually).
- 2.) Your handler must and with a retf instruction and an iret !!!
- 3.) You must pass a status-word in AX:
- If AX<0 then the output will be stopped immeditaly.
-
- Set_Record_Handler(int (*Handler) (void))
- This function does exactly the same like above excpet that it is reserved
- for recording purposes.
-
- Set_Next_Block(Sound_Block *)
- This is another very important and useful function. In order to make time-
- gaps between two sound-blocks as small as possible, I added this function.
- You can call this function while the first block is still playing and tell
- the driver where the next sound block can be found. When the first sound-
- block is finished then the driver will automatically continue the output
- with this new soundblock and then call (if defined) the user-handler.
-
- The config structure is used for driver which can use different settings.
- In order to use this structure use Config_Driver(Driver *) of the demo-
- file CDMIDEMO.C.
-
-
-
- 7. I heard, CDMI uses a DOS-extender ???
- ==========================================
-
- Yes, that is right. In order to play really large samples and many patterns,
- I decided to use extra memory. I first used EMS, and it worked more or
- less, but it was a bit too slooooow, so I decided to write a DOS-Extender.
- This extender sets up a so called "flat-mode" of the 386 CPU which enables
- you access to 4GB address-space without programming in protected mode !
- The only disadvantage is that it does not work with EMM386,QEMM etc. at the
- moment.
- You can also use these functions in your programs, see EXT386.DOC for further
- information. But if you don't like to use these functions, you do not need
- to bother about the FLAT-mode, because it works absolutely transparently !!!
- But you MUST init the extender by calling Init_EXT386 at the beginning of
- your program and Exit_EXT386 at the end !!!
-
-
-
- 8. Problems, Bugs...
- =====================
-
- Now let us come to the dark side of CDMI. There are some known problems with
- other C-functions and some rules you should know.
- First Borland C++ offers rather a LOT of memory allocation functions, but
- they turned out to be incompatible. This means that not you can not use all
- memory-management aat a time (malloc(), calloc(), _dos_allocmem(), allocmem())
- In order to prevent conflicts, I provide you with my own memory allocation
- functions in EXT386:
- void far *Malloc(size_l);
- int Free(void far*);
- size_l Coreleft(void);
- void far *DMA_Malloc(size_t);
-
-
- Even the MOD-player does not work always. There are some rare modules which
- cannot be played correct and which cause a crash. I do not know why, but it
- seems, that there is a division by zero caused by a illegal period.
- And there are still some extended commands that are not implemented yet.
-
- The 669-support is not complete yet. There are some special commands missing,
- too.
-
-
-
- 9. There is a demo, right ?!
- ===============================
-
- Yes, I supplied a small graphic-demo with this package, so you can see how
- CDMI is used in a "real" program.
- It is written in Borland C++ 3.1 and includes a project file, so there should
- be no problem building it. You may reuse all routines of the demo, if your
- new program has not the same "look-and-feel". In other words, you may not
- only makes some minor changes (like modifiing the text) !!!
-
-
-
- 10. Shareware agreement
- ========================
-
- The files supplied with this documentation are only for educational purpose
- and are copyrighted by the author Kaya Memisoglu. You may reuse parts of
- them when you give the credits to either Kaya Memisoglue or to Marc van
- Shaney (my synonym). You may not use CDMI in any freeware/shareware/
- comercial product unless you have registered (See ORDER.DOC) for your
- purpose. Then you will receive the latest version inluding the source not
- only to the demo, but also to CDMP.
-
-
-
- 11. My address:
- ================
-
- Kaya Memisoglu
- Reichenberger Ring 50
- 63512 Hainburg
- Germany
-
-