/* ** Library.c ** ** Copyright (C) 1995,96,97 Bernardo Innocenti ** ** xmodule.library functions */ #include #include #include #include #include #include #include #include #include #include #include #include #include "XModulePriv.h" #include "Gui.h" /****** xmodule/--background-- ********************************************* * * INTRODUCTION * The xmodule.library is an API that provides access to the XModule * song management engine, as well as other general pourpose * services. Hooks and external applications can use this library * to create, load, save and modify songs. * * The xmodule.library does not exist as a stand alone shared libray. * Instead, it's code is contained inside the main XModule executable * (altrough it's logically independant from the rest of the XModule * code). * * XModule adds the xmodule.library to the exec libray list at * startup time, unless it finds one already in the list (this might * happen when multiple copies of XModule are running at the same * time). * * PUBLIC SONGS * The xmodule.library maintains a list of public songs which can be * used by all applications which opened the library. Each song in * the list is protected from multiple task access by a * SignalSemaphore, and whole list is also protected by another * semaphore. The locking mechanism is very simple: before you can * access a song you must obtain its semaphore, and you must release * it as soon as you are done with it. If you are locking a song to * just to read some information (i.e.: you don't want to modify * anything), you should obtain a shared lock instead of an exclusive * one. The list must be locked whenever you want to add or remove * a song, or when you scan it in any way. * * Since public songs could be modified or even deleted by other * tasks, do not make your songs public unless your code is smart * enough to handle all possible situations. * * * HOOKS * Actually, most modular programs call them `modules', but it would * have created a lot of confusion with a program like XModule :-). * * Hooks are programs that add some kind of functionality * to XModule. External hook files are standard shared libraries * which will open the xmodule.library when they are loaded and * call xmAddHookA() to add themselves to a list of hooks maintained * by the library. * * Currently, XModule supports two kinds of hooks: loaders and * savers. Loader hooks can also provide a function which * identifies a particular module format. * * An external hook libray may also contain several hooks. * Putting a loader and a saver for one particolar format together * in one libray is generally a good idea, while making a hook * with hundereds of loaders and savers isn't a good move because * it makes the whole concept of external hooks quite useless. * Grouping different versions or variants of one format together * in one external hook is acceptable. * * SEE ALSO * songclass/--background--, exec/ObtainSemaphore() * **************************************************************************** */ /* Library function prototypes */ static LIBCALL struct SongInfo * XModuleCreateSong ( REG(a0, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)); static LIBCALL void XModuleDeleteSong ( REG(a0, struct SongInfo *si), REG(a6, struct XModuleBase *XModuleBase)); static LIBCALL ULONG XModuleAddSong ( REG(a0, struct SongInfo *si), REG(a1, struct SongInfo *position), REG(a2, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)); static LIBCALL ULONG XModuleRemSong ( REG(a0, struct SongInfo *si), REG(a6, struct XModuleBase *XModuleBase)); static LIBCALL ULONG XModuleActivateSong ( REG(a0, struct SongInfo *si), REG(a6, struct XModuleBase *XModuleBase)); static LIBCALL struct SongInfo *XModuleLockActiveSong ( REG(d0, UWORD mode), REG(a6, struct XModuleBase *XModuleBase)); static LIBCALL struct XMHook *XModuleAddHook ( REG(a0, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)); static LIBCALL void XModuleRemHook ( REG(a0, struct XMHook *hook), REG(a6, struct XModuleBase *XModuleBase)); static LIBCALL struct XMHook *XModuleIdentifyModule( REG(d0, BPTR fh), REG(a0, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)); static LIBCALL struct SongInfo * XModuleLoadModule ( REG(a0, CONST_STRPTR filename), REG(a1, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)); static LIBCALL LONG XModuleSaveModule ( REG(a0, struct SongInfo *si), REG(a1, CONST_STRPTR filename), REG(a2, struct XMHook *saver), REG(a3, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)); static LIBCALL UWORD *XModuleSetSongLen( REG(a0, struct SongInfo *si), REG(d0, UWORD length), REG(a6, struct XModuleBase *XModuleBase)); static LIBCALL void XModuleAddPattern ( REG(a0, struct SongInfo *si), REG(a1, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)); static LIBCALL void XModuleSetPattern ( REG(a0, struct SongInfo *si), REG(d0, ULONG pattNum), REG(a1, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)); static LIBCALL void XModuleRemPattern ( REG(a0, struct SongInfo *si), REG(d0, ULONG pattNum), REG(d1, ULONG replaceWith)); static LIBCALL void XModuleAddInstrument ( REG(a0, struct SongInfo *si), REG(d0, LONG num), REG(a1, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)); static LIBCALL void XModuleSetInstrument ( REG(a0, struct SongInfo *si), REG(d0, LONG num), REG(a1, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)); static LIBCALL void XModuleRemInstrument ( REG(a0, struct SongInfo *si), REG(d0, ULONG num), REG(a6, struct XModuleBase *XModuleBase)); static LIBCALL LONG XModuleProcessSong ( REG(a0, struct SongInfo *si), REG(a1, void *reserved), REG(a2, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)); static LIBCALL void XModuleDisplayMessage ( REG(d0, ULONG level), REG(a0, CONST_STRPTR msg), REG(a1, LONG *args), REG(a6, struct XModuleBase *XModuleBase)); static LIBCALL void XModuleDisplayProgress ( REG(d0, ULONG actual), REG(d1, ULONG max), REG(a6, struct XModuleBase *XModuleBase)); /* Local function prototypes */ static LIBCALL void XModuleLibOpen (void); static LIBCALL void XModuleLibClose (void); static LIBCALL void XModuleLibExpunge (void); static LIBCALL void XModuleLibExtFunc (void); static LIBCALL LONG XModuleRexxServer (void); /* Library vector table */ static APTR XModuleVectors[] = { XModuleLibOpen, XModuleLibClose, XModuleLibExpunge, XModuleLibExtFunc, XModuleRexxServer, XModuleRexxServer, XModuleRexxServer, XModuleRexxServer, XModuleCreateSong, XModuleDeleteSong, XModuleAddSong, XModuleRemSong, XModuleActivateSong, XModuleLockActiveSong, XModuleAddHook, XModuleRemHook, XModuleIdentifyModule, XModuleLoadModule, XModuleSaveModule, XModuleSetSongLen, XModuleAddPattern, XModuleSetPattern, XModuleRemPattern, XModuleAddInstrument, XModuleSetInstrument, XModuleRemInstrument, XModuleProcessSong, XModuleDisplayMessage, XModuleDisplayProgress, (APTR)-1 }; GLOBALCALL ULONG MakeXModuleLibrary (void) { if (XModuleBase = (struct XModuleBase *)MakeLibrary (XModuleVectors, NULL, NULL, sizeof (struct XModuleBase), NULL)) { /* Clear base variables following the library node * (InitSemaphore reqires a clean memory block!) * * GOOD NEWS: Clearing the library base is not required because * MakeLibrary() allocates a MEMF_CLEAR memory block. * * memset (((UBYTE *)XModuleBase) + sizeof (struct Library), 0, * sizeof (struct XModuleBase) - sizeof (struct Library)); */ XModuleBase->xm_Library.lib_Node.ln_Name = (STRPTR)"xmodule.library"; XModuleBase->xm_Library.lib_Version = VERSION; XModuleBase->xm_Library.lib_Revision = REVISION; XModuleBase->xm_Library.lib_IdString = (STRPTR)Version + 6; XModuleBase->xm_DOSBase = DOSBase; XModuleBase->xm_UtilityBase = UtilityBase; XModuleBase->xm_IFFParseBase = IFFParseBase; XModuleBase->xm_IntuitionBase = IntuitionBase; NEWLIST ((struct List *)&XModuleBase->xm_Songs); NEWLIST ((struct List *)&XModuleBase->xm_Loaders); NEWLIST ((struct List *)&XModuleBase->xm_Savers); InitSemaphore (&XModuleBase->xm_BaseLock); /* Create global memory pool */ if (!(XModuleBase->xm_Pool = CreatePool (MEMF_ANY, 16*1024, 4*1024))) return ERROR_NO_FREE_STORE; if (!(XModuleBase->xm_SongClass = InitSongClass())) return ERROR_NO_FREE_STORE; return RETURN_OK; } return ERROR_NO_FREE_STORE; } GLOBALCALL void DisposeXModuleLibrary (void) { if (XModuleBase) { ObtainSemaphore (&XModuleBase->xm_BaseLock); /* Free all songs in SongList */ while (!IsListEmpty ((struct List *)&XModuleBase->xm_Songs)) xmDeleteSong ((struct SongInfo *)XModuleBase->xm_Songs.mlh_Head); /* Remove all loaders */ while (!IsListEmpty ((struct List *)&XModuleBase->xm_Loaders)) xmRemHook ((struct XMHook *)XModuleBase->xm_Loaders.mlh_Head); /* Remove all savers */ while (!IsListEmpty ((struct List *)&XModuleBase->xm_Savers)) xmRemHook ((struct XMHook *)XModuleBase->xm_Savers.mlh_Head); /* Free the songclass */ FreeSongClass (XModuleBase->xm_SongClass); /* Dispose the global memory pool */ if (XModuleBase->xm_Pool) DeletePool (XModuleBase->xm_Pool); FreeMem (((UBYTE *)XModuleBase) - XModuleBase->xm_Library.lib_NegSize, XModuleBase->xm_Library.lib_NegSize + XModuleBase->xm_Library.lib_PosSize); XModuleBase = NULL; } } static LIBCALL void XModuleLibOpen (void) { } static LIBCALL void XModuleLibClose (void) { } static LIBCALL void XModuleLibExpunge (void) { } static LIBCALL void XModuleLibExtFunc (void) { } static LIBCALL LONG XModuleRexxServer (void) { return 0; } /****** xmodule/xmAddHook ************************************************** * * NAME * xmAddHookA -- Creates a new XModule Hook * xmAddHook -- Varargs stub for xmAddHookA * * SYNOPSIS * hook = xmAddHookA(tagList) * D0 A0 * * struct XMHook *xmAddHookA(struct TagItem *); * * * hook = xmAddHook(Tag1,...) * * struct XMHook *xmAddHook(ULONG,...); * * FUNCTION * Creates a new XMHook structure and fills it in with data supplied * with the TagList. Adds the newly created Hook to the appropriate * list. * * INPUTS * tagList - pointer to a tag list specifying how to initialize the * XMHook structure. * * TAGS * XMHOOK_Type - (ULONG) Defines the pourpose of this hook. Possible * values are currently NT_XMLOADER and NT_XMSAVER. (This * tag is REQUIRED). * * XMHOOK_Name - (STRPTR) ti_Data contains a short name for the * hook (e.g: "SoundTracker"). This name will appear in the * Savers list if this hook is a saver and will be passed as an * argument for some ARexx commands, so please use a single * word name. (This tag is REQUIRED). * * XMHOOK_Priority - (BYTE) Priority to give to this hook. Hooks * with higher priorities will be used before lower priority * ones and will come first in lists shown to the user. Valid * range is -128..+127, but please restrict to a -20..+20 * interval for normal cases. (Defaults to 0). * * XMHOOK_Descr - (STRPTR) Verbose description of the hook * (without newlines). (Defaults to NULL). * * XMHOOK_Author - (STRPTR) Author's name. Please, just put * your full name here; no greetings, copyright notices, * etc. (Defaults to NULL). * * XMHOOK_ID - (ULONG) This is a unique, IFF-style identifier for * the format. If the format is an IFF format, it must be the * same of the FORM ID. (Defaults to 0, this tag is required * for IFF loaders and savers). * * XMHOOK_Flags - (ULONG) Sets miscellaneous flags for this hook. * See xmodule.h for possible flags. * * XMHOOK_LibraryBase - (struct Library *) Pointer to the library * base for external hooks. This pointer will be used to open * the library when the hook is created and to close it when * the hook is deleted. If you do not pass this tag, you must * find some other way to keep your code in memory until one * or more of your hooks are active. XModule will close your * library just after calling the SetupXMHook() function. * (Defaults to NULL). * * XMHOOK_UserData - (APTR) ti_Data will be stored in the * xmh_UserData field of the XMHook structure. This field can * come andy to store private data. (Defaults to NULL). * * XMHOOK_RemoveHookFunc - (APTR) Pointer to a function which will be * called upon removing the hook. This function can be used to * free any private resources allocated by the hook when it was * created. The template for the function is: * * void RemoveHookFunc (struct XMHook *hook); * A0 * * XMHOOK_LoadModFunc - (APTR) Pointer to the hook function which * loads a module. The template for the function is: * * LONG LoadModFunc (BPTR fileHandle, struct SongInfo *song, * D0 A0 * struct XMHook *loader); * A1 * * `fileHandle' is an open file to load the module from. The * caller will take care to close it for you. The loader should * return RETURN_OK for success, or any other AmigaDOS error * code to mean failure. In particular, RETURN_WARN indicates * that something went wrong, but the song is ok. The special * error code ERROR_IOERR can be used when some dos.library * call (e.g.: Read()) failed. In the latter case, the * AmigaDOS IoErr value should be set to explain the specific * cause of the problem. * (This tag is required by all NT_XMLOADER type hooks). * * XMHOOK_SaveModFunc - (APTR) Pointer to the hook function which * saves a module. The template for the function is: * * LONG SaveModFunc (BPTR fileHandle, struct SongInfo *song, * D0 A0 * struct XMHook *saver); * A1 * * fileHandle is an open file to save the module to. The caller * will take care to close it for you. The saver should return * RETURN_OK for success, or any other AmigaDOS error code to * mean failure. In particular, RETURN_WARN indicates that * something went wrong, but the song is ok. The special * error code ERROR_IOERR can be used when some dos.library * call (e.g.: Read()) failed. In the latter case, the * AmigaDOS IoErr value should be set to explain the specific * cause of the problem. * (This tag is required by all NT_XMSAVER type hooks). * * * XMHOOK_IdentifyModFunc - (APTR) Pointer to the hook function * which identifies a module format. The template for the * function is: * * struct XMHook *IdentifyModFunc (BPTR fileHandle, * D0 * struct XMHook *hook,struct TagItem *tagList); * A0 A1 * * fileHandle is an open file to try the identification on. The * caller will take care to close it. NOTE: Do not make assumptions * on the initial file position (e.g: seek yourself to position 0 * if you need to). This funtion should return a pointer to a valid * loader (usually the one passed in) if the format is recognized, * NULL otherwhise. (This tag is required by all NT_XMLOADER type * hooks). * * RESULT * hook - Pointer to the newly allocated XMHook structure, or * NULL for failure. * * SEE ALSO * xmRemHook(), xmIdentifyModule(), xmLoadSong(), xmSaveSong() * **************************************************************************** */ static LIBCALL struct XMHook *XModuleAddHook ( REG(a0, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)) { struct XMHook *hook; if (hook = AllocPooled (XModuleBase->xm_Pool, sizeof (struct XMHook))) { hook->xmh_Link.ln_Name = (UBYTE *) GetTagData (XMHOOK_Name, NULL, tags); hook->xmh_Link.ln_Type = (UBYTE) GetTagData (XMHOOK_Type, 0, tags); hook->xmh_Link.ln_Pri = (BYTE) GetTagData (XMHOOK_Priority, 0, tags); hook->xmh_Descr = (STRPTR) GetTagData (XMHOOK_Descr, 0, tags); hook->xmh_Author = (STRPTR) GetTagData (XMHOOK_Author, 0, tags); hook->xmh_ID = (ULONG) GetTagData (XMHOOK_ID, 0, tags); hook->xmh_Flags = (ULONG) GetTagData (XMHOOK_Flags, 0, tags); hook->xmh_UserData = (APTR) GetTagData (XMHOOK_UserData, NULL, tags); hook->xmh_LibraryBase = (APTR) GetTagData (XMHOOK_LibraryBase, NULL, tags); hook->xmh_RemoveHook = (void (* ASMCALL)()) GetTagData (XMHOOK_RemoveFunc, NULL, tags); hook->xmh_LoadMod = (LONG (* ASMCALL)()) GetTagData (XMHOOK_LoadModFunc, NULL, tags); hook->xmh_SaveMod = (LONG (* ASMCALL)()) GetTagData (XMHOOK_SaveModFunc, NULL, tags); hook->xmh_IdentifyMod = (struct XMHook * (* ASMCALL)()) GetTagData (XMHOOK_IdentifyModFunc, NULL, tags); hook->xmh_MaxTracks = GetTagData (XMHOOK_MaxTracks, MAXTRACKS, tags); hook->xmh_MaxPatterns = GetTagData (XMHOOK_MaxPatterns, MAXPATTERNS, tags); hook->xmh_MaxInstruments= GetTagData (XMHOOK_MaxInstruments, MAXINSTRUMENTS, tags); hook->xmh_MaxLength = GetTagData (XMHOOK_MaxLength, MAXPOSITIONS, tags); hook->xmh_MaxSampleLen = GetTagData (XMHOOK_MaxSampleLen, ((2<<31)-1), tags); hook->xmh_MaxPattLen = GetTagData (XMHOOK_MaxPattLen, MAXPATTLINES, tags); hook->xmh_Version = XMHOOK_VERSION; if (hook->xmh_LibraryBase) if (!(OpenLibrary (hook->xmh_LibraryBase->lib_Node.ln_Name, 0))) return NULL; ObtainSemaphore (&XModuleBase->xm_BaseLock); Enqueue ((struct List *)((hook->xmh_Link.ln_Type == NT_XMSAVER) ? &XModuleBase->xm_Savers : &XModuleBase->xm_Loaders), (struct Node *)hook); ReleaseSemaphore (&XModuleBase->xm_BaseLock); } return hook; } /****** xmodule/xmRemHook ************************************************** * * NAME * xmRemHook -- Removes an XModule Hook * * SYNOPSIS * xmRemHookA(xmHook) * A0 * * void xmRemHook(struct XMHook *); * * FUNCTION * Removes an XModule Hook, calls its RemoveHookFunc() and then * frees its memory. * * INPUTS * xmHook - pointer to the hook to be removed. * * SEE ALSO * xmAddHook() * **************************************************************************** */ static LIBCALL void XModuleRemHook ( REG(a0, struct XMHook *hook), REG(a6, struct XModuleBase *XModuleBase)) { ObtainSemaphore (&XModuleBase->xm_BaseLock); REMOVE ((struct Node *)hook); ReleaseSemaphore (&XModuleBase->xm_BaseLock); if (hook->xmh_RemoveHook) hook->xmh_RemoveHook (hook); if (hook->xmh_LibraryBase) CloseLibrary (hook->xmh_LibraryBase); FreePooled (XModuleBase->xm_Pool, hook, sizeof (struct XMHook)); } /****** xmodule/xmCreateSong *********************************************** * * NAME * xmCreateSongA -- Create and initialize a new SongInfo structure * xmCreateSong -- Varargs stub for xmCreateSongA * * SYNOPSIS * songInfo = xmCreateSongA(tagList); * D0 A0 * * struct SongInfo *xmCreateSongA(struct TagItem *); * * * songInfo = xmCreateSong(Tag1,...); * * struct SongInfo *xmCreateSong(ULONG,...); * * FUNCTION * Allocates and initializes a new SongInfo structure. The song * can then be added to the song list via xmAddSongA(), in which * case, it is no longer required to free it with xmDeleteSong(). * * INPUTS * tagList - pointer to an optional tag list specifying how to * initialize the SongInfo structure. * * TAGS * SNGA_DefaultTracks - Sets the default number of pattern tracks for * the song. Defaults to 4. * * SNGA_DefaultPattLen - Sets the default number of pattern lines for * the song. Defaults to 64. * * SNGA_ReadyToUse - Adds one pattern and one position to the song. * Defaults to FALSE. * * XMSNG_AddToList - (struct SongInfo *) Add the song to the song list. * When a song is being added to the list, a shared lock is * obtained on it to prevent other tasks from modifying (or even * remove) the song before you can do your job on it. IT IS YOUR * DUTY TO UNLOCK THE SONG as soon as you are done modifying it. * Passing NULL adds the song on the head of the list, while -1 * (~0) will add it to the end of the SongList. Passing in a * pointer to another song which is already in the list, will * add the new song _after_ the latter. * (Default is not adding the song). * * XMSNG_Active - (BOOL) Makes this song the active one. This tag is * considered only if the XMSNG_AddToList tag is also passed. * * RESULT * songInfo - pointer to the newly allocated SongInfo structure, or * NULL for failure. * * SEE ALSO * xmDeleteSong(), xmAddSongA() * **************************************************************************** */ static LIBCALL struct SongInfo * XModuleCreateSong ( REG(a0, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)) { struct SongInfo *si; struct TagItem *tag; if (si = NewObjectA (XModuleBase->xm_SongClass, NULL, tags)) { if (tag = FindTagItem (XMSNG_AddToList, tags)) { ObtainSemaphore (&si->Lock); xmAddSongA (si, (struct SongInfo *)tag->ti_Data, tags); } } return si; } /****** xmodule/xmDeleteSong *********************************************** * * NAME * xmDeleteSong -- Deletes a song * * SYNOPSIS * xmDeleteSong(songInfo) * A0 * * void xmDeleteSong(struct SongInfo *); * * FUNCTION * Deletes a song and all the items attached to it (patterns, * instruments, etc.). This call will also remove the song from * the song list if it had been added to it. * * INPUTS * songInfo - pointer to the SongInfo structure to be deleted. * Passing a NULL pointer is harmless. * * NOTE * In order to delete a song which has been added to the public * song list, you must first obtain an exclusive lock on it to * prevent other tasks from walking on it while you are freeing * it's data structures. The semaphore does NOT need to be * relinquished, because the SongInfo structure won't exist any * more when this call returns. * * SEE ALSO * xmCreateSong(), xmRemSong() * **************************************************************************** */ static LIBCALL void XModuleDeleteSong ( REG(a0, struct SongInfo *si), REG(a6, struct XModuleBase *XModuleBase)) { xmRemSong (si); DisposeObject (si); } /****** xmodule/xmAddSongA ************************************************* * * NAME * xmAddSongA -- Add a song to the song list * xmAddSong -- Varargs stub for xmAddSongA * * SYNOPSIS * success = xmAddSongA(songInfo,position,tagList); * D0 A0 A1 A2 * * ULONG xmAddSongA(struct SongInfo *,struct SongInfo *, * struct TagItem *); * * * success = xmAddSong(songInfo,position,tag1,...); * * ULONG xmAddSong(struct SongInfo *,struct SongInfo *, * LONG,...); * * FUNCTION * Adds a song to the public song list. Trying to add a song * twice is harmless. * * INPUTS * songInfo - song to be added. Passing a NULL pointer is safe. * position - Position to add the song in. Passing NULL adds the * song in the head of the list, while ~0 adds it in the tail. * passing a pointer to a SongInfo already in the list inserts * the first song _after_ the other one. * tagList - pointer to a TagList for more arguments. * * TAGS * XMSNG_Active - (BOOL) Makes this song the active one. The * default is to just add the song without activating it. * * RESULT * success - Will be 0 for failure, in which case the song will * not be added to the songs list. Currently, xmAddSongA() * will never fail. * * NOTE * The song list is protected from multiple tasks access by a * SignalSemaphore in XModuleBase. This call will wait if the * song list has been locked by another task. Beware of deadlock * conditions! * * SEE ALSO * xmRemSong() * **************************************************************************** */ static LIBCALL ULONG XModuleAddSong ( REG(a0, struct SongInfo *si), REG(a1, struct SongInfo *position), REG(a2, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)) { if (!si) return FALSE; if (!si->Link.ln_Type) /* Is it already in the list? */ { ObtainSemaphore (&XModuleBase->xm_BaseLock); DetatchSongInfoList(); if ((ULONG)position == ~0) ADDTAIL ((struct List *)&XModuleBase->xm_Songs, (struct Node *)si); else Insert ((struct List *)&XModuleBase->xm_Songs, (struct Node *)si, (struct Node *)position); si->Link.ln_Type = NT_XMSONG; /* Mark this song */ if (GetTagData (XMSNG_Active, FALSE, tags)) xmActivateSong (si); UpdateSongInfoList(); ReleaseSemaphore (&XModuleBase->xm_BaseLock); } return TRUE; } /****** xmodule/xmRemSong ************************************************** * * NAME * xmRemSong -- Remove a song from the song list * * SYNOPSIS * success = xmRemSong(songInfo); * D0 A0 * * ULONG xmRemSong(struct SongInfo *); * * FUNCTION * Removes a song from the public song list. It is safe to call this * function even if the song has not been added to the song list. If * the passed SongInfo is the active one, another song will be * selected automatically. * * INPUTS * songInfo - song to be removed. If NULL, this function will take * no action. * * RESULT * success - Will be 0 for failure, in which case the song will * not be removed from the song list. Currently, * xmRemSong() will never fail. * * NOTE * In order to remove a song, you must first obtain an exclusive * lock on it. * * The song list is also protected from multiple task access by * a SignalSemaphore. This call will wait if the song list has * been locked by another task. Beware of deadlock conditions! * * SEE ALSO * xmAddSongA() * **************************************************************************** */ static LIBCALL ULONG XModuleRemSong ( REG(a0, struct SongInfo *si), REG(a6, struct XModuleBase *XModuleBase)) { if (!si) return FALSE; if (!si->Link.ln_Type) return TRUE; /* Not in the list */ /* Brutal but effective way to obtain two locks at the same time, * avoiding the risk of deadlock conditions. We can't just use * ObtainSemaphoreList() here because the two semaphores can * not be linked into a list. */ /* while (1) { ObtainSemaphore (&XModuleBase->xm_BaseLock); if (AttemptSemaphore (&si->Lock)) break; ReleaseSemaphore (&XModuleBase->xm_SongsLock); ObtainSemaphore (&si->Lock); if (AttemptSemaphore (&XModuleBase->xm_SongsLock)) break; ReleaseSemaphore (&si->Lock); } */ ObtainSemaphore (&XModuleBase->xm_BaseLock); DetatchSongInfoList(); REMOVE ((struct Node *)si); si->Link.ln_Type = 0; /* Mark song outside list */ if (XModuleBase->xm_CurrentSong == si) { if (IsListEmpty ((struct List *)&XModuleBase->xm_Songs)) XModuleBase->xm_CurrentSong = NULL; else XModuleBase->xm_CurrentSong = (struct SongInfo *) XModuleBase->xm_Songs.mlh_TailPred; UpdateSongInfoList(); UpdateSongInfo(); } else UpdateSongInfoList(); ReleaseSemaphore (&XModuleBase->xm_BaseLock); return TRUE; } /****** xmodule/xmActivateSong ********************************************* * * NAME * xmActivateSong -- Makes a song the active one * * SYNOPSIS * success = xmActivateSong(songInfo); * D0 A0 * * ULONG xmActivateSong(struct SongInfo *); * * FUNCTION * Makes the passed song the currently active one. It's pointer * will be stored in the public song list and most actions * will happen on this song by default. * * INPUTS * songInfo - song to be activated. If NULL, this function will * take no action. * * RESULT * success - Will be 0 for failure, in which case the song will * not be removed from the song list. Currently, * xmActivateSong() will never fail. * * NOTE * In order to activate a song, you must own a shared lock * on it. Please do not hog this lock for a long time when * xmActivateSong() returns, because most internal routines * try to lock the current song before taking any action. * * SEE ALSO * **************************************************************************** */ static LIBCALL ULONG XModuleActivateSong ( REG(a0, struct SongInfo *si), REG(a6, struct XModuleBase *XModuleBase)) { if (!si) return FALSE; if (!si->Link.ln_Type) return FALSE; /* Not in the list */ ObtainSemaphore (&XModuleBase->xm_BaseLock); if (XModuleBase->xm_CurrentSong != si) { XModuleBase->xm_CurrentSong = si; UpdateSongInfo(); /**/ } ReleaseSemaphore (&XModuleBase->xm_BaseLock); return TRUE; } /****** xmodule/xmLockActiveSong ******************************************* * * NAME * xmLockActiveSong -- Obtains an lock on the active song * * SYNOPSIS * song = xmLockActiveSong(mode); * D0 D0:16 * * struct SongInfo *xmActivateSong(UWORD); * * FUNCTION * Obtains an exclusive or shared lock on the active song, * waiting if needed. This call is a shortcut to: * * ObtainSemaphoreShared (&XModuleBase->xm_BaseLock); * ObtainSemaphore[Shared] (&XModuleBase->xm_CurrentSong.Lock); * ReleaseSemaphore (&XModuleBase->xm_BaseLock); * * To unlock a song obtained in this way, you just need to * ReleaseSemaphore() it. * * You MUST always lock a song before you even think to * read from -or write to- its data! * * INPUTS * mode - one of SM_SHARED or SM_EXCLUSIVE. * * RESULT * song - Pointer to the song which *was* active at the time * you called xmLockActiveSong(). The song will be * locked for you. The result will be NULL if no song * is active. * * NOTE * Please be careful if you call this function while holding * locks on other songs. Doing so, you run the risk of causing * deadlock condition. * This function does only lock the song; it is NOT guaranteed * that it will remain the active one. * * SEE ALSO * **************************************************************************** */ static LIBCALL struct SongInfo *XModuleLockActiveSong ( REG(d0, UWORD mode), REG(a6, struct XModuleBase *XModuleBase)) { struct SongInfo *si; #ifdef OS30_ONLY ObtainSemaphoreShared (&XModuleBase->xm_BaseLock); #else /* Workaround for Pre-V39 ObtainSemaphoreShared() bug (see autodoc) */ /* Try to get the shared semaphore */ if (!AttemptSemaphoreShared (&XModuleBase->xm_BaseLock)) /* Check if we can get the exclusive version */ if (!AttemptSemaphore (&XModuleBase->xm_BaseLock)) /* Oh well, wait for the shared lock */ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock); #endif /* OS30_ONLY */ if (si = XModuleBase->xm_CurrentSong) { if (mode == SM_SHARED) { #ifdef OS30_ONLY ObtainSemaphoreShared (&si->Lock); #else /* Try to get the shared semaphore */ if (!AttemptSemaphoreShared (&si->Lock)) /* Check if we can get the exclusive version */ if (!AttemptSemaphore (&si->Lock)) /* Oh well, wait for the shared lock */ ObtainSemaphoreShared (&si->Lock); #endif /* OS30_ONLY */ } else ObtainSemaphore (&si->Lock); } ReleaseSemaphore (&XModuleBase->xm_BaseLock); return si; } /****** xmodule/xmLoadModuleA ********************************************** * * NAME * xmLoadModuleA -- Loads a module and converts it in XModule format * xmLoadModule -- Varargs stub for xmLoadModuleA * * SYNOPSIS * songInfo = xmLoadModuleA(fileName,tagList) * D0 A0 A1 * * struct SongInfo *xmLoadModuleA(CONST_STRPTR,struct TagItem *); * * * songInfo = xmLoadModule(fileName,tag,...) * * struct SongInfo *xmLoadModule(CONST_STRPTR,LONG,...); * * FUNCTION * Loads fileName using the correct module loader. * * INPUTS * fileName - File to read. Can be NULL if the XMSNG_FileHandle * tag is passed. * tagList - Additional parameters (see below). Can be NULL. * * TAGS * XMSNG_OldSong - ti_Data is the pointer to a SongInfo which will be * freed as soon as the module format has been determined. This * is useful when the user wants to replace a song with another * one. Passing NULL uses the currently active song. * * XMSNG_AddToList - (struct SongInfo *) Add the song to the song list. * When a song is being added to the list, a shared lock is * obtained on it to prevent other tasks from modifying (or even * remove) the song before you can do your job on it. IT IS YOUR * DUTY TO UNLOCK THE SONG as soon as you are done modifying it. * Passing NULL adds the song on the head of the list, while -1 * (~0) will add it to the end of the SongList. Passing in a * pointer to another song which is already in the list, will * add the new song _after_ the latter. * (Default is not adding the song). * * XMSNG_Loader - (struct XMHook *) Disables automatic format * checking and forces loading the module with the given * loader. (Defaults to NULL). * * XMSNG_FileHandle - (BPTR) pointer to an open AmigaDOS * FileHandle to read the module from. The file must * already be positioned over the beginning of the module data. * NOTE: Even if this tag is passed, the fileName parameter is * still used to fill in the SongName field in case it is * missing inside the module AND the filesystem does not * support the ACTION_EXAMINE_FH packet. * NOTE: Some loaders may require a fully seekable file, so be * careful when using pipes. * NOTE: automatic data decompression is not possible when * XMSNG_FileHandle is passed. * (Defaults to NULL). * * XMSNG_IFFHandle - (BPTR) pointer to an already initialized * IFFHandle to read the module from. The IFF must * already be positioned over the beginning of the module FORM. * Even if this tag is passed, the fileName parameter is still * used to fill in the SongName field in case it is missing * inside the module. * NOTE: The iffparse.library can deal with non-seekable files, * but some loaders may require a fully seekable file, so be * careful when using pipes. * NOTE: automatic file decompression is not possible when * XMSNG_IFFHandle is passed. * (Defaults to NULL). * * XMSNG_Active - (BOOL) Makes this song the active one. This tag is * considered only if the XMSNG_AddToList tag is also passed. * * RESULT * songInfo - Pointer to the newly allocated SongInfo structure, or * NULL for failure. This function will not fail if the module is * partially corrupted. Anyway, the returned SongInfo will always * be valid. You can check IoErr() to know if there were problems. * * EXAMPLE * \* Load a song and add it to the SongList *\ * si = xmLoadSongTags (file, NULL, * XMSNG_AddToList, (struct SongInfo *)~0, * TAG_DONE); * * \* Check for errors even if si is not NULL *\ * error = IoErr(); * * \* Release Semaphore got by xmLoadSong() *\ * if (si) ReleaseSemaphore (&si->Lock); * * SEE ALSO * xmAddSongA(), xmIdentifyModule() * **************************************************************************** */ static LIBCALL struct SongInfo * XModuleLoadModule ( REG(a0, CONST_STRPTR filename), REG(a1, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)) { struct XMHook *loader; struct SongInfo *si = NULL; UBYTE fullpath[PATHNAME_MAX]; ULONG err; BPTR fh, compressed = 0; UWORD type; loader = (struct XMHook *)GetTagData (XMSNG_Loader, NULL, tags); /* Get source file or open it */ if (fh = (BPTR) GetTagData (XMSNG_FileHandle, NULL, tags)) { /* Get the full pathname */ if (!NameFromFH (fh, fullpath, PATHNAME_MAX)) fullpath[0] = '\0'; } else { if (!(fh = Open (filename, MODE_OLDFILE))) { UBYTE buf[FAULT_MAX]; xmDisplayMessage (XMDMF_USECATALOG | XMDMF_DOSFAULT, (APTR)MSG_ERR_LOAD, filename, buf); return NULL; } /* Optimize file buffering for faster I/O. * * NOTE: SetVBuf() was introduced in V39, but V37 has a * no-op vector for it, so it is safe to call SetVBuf() * without checking the OS version. * NOTE: Due to a bug in dos.library V40, SetVBuf() works * only when called right after Open() (before doing any I/O). */ SetVBuf (fh, NULL, BUF_FULL, 16*1024); /* Get the full pathname */ if (!NameFromFH (fh, fullpath, PATHNAME_MAX)) fullpath[0] = '\0'; /* Check wether the file is compressed */ if (type = CruncherType (fh)) { Close (fh); if (compressed = DecompressFile (filename, type)) { if (!(fh = OpenFromLock (compressed))) { err = IoErr(); UnLock (compressed); DecompressFileDone(); SetIoErr (LastErr = err); return NULL; } } else return NULL; } } /* Lock loaders list */ #ifdef OS30_ONLY ObtainSemaphoreShared (&XModuleBase->xm_BaseLock); #else /* Workaround for Pre-V39 ObtainSemaphoreShared() bug (see autodoc) */ /* Try to get the shared semaphore */ if (!AttemptSemaphoreShared (&XModuleBase->xm_BaseLock)) /* Check if we can get the exclusive version */ if (!AttemptSemaphore (&XModuleBase->xm_BaseLock)) /* Oh well, wait for the shared lock */ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock); #endif /* OS30_ONLY */ /* Find out what the heck this file format is */ if (!loader) loader = xmIdentifyModule (fh, tags); if (loader) { if (loader->xmh_LoadMod) { Seek (fh, 0, OFFSET_BEGINNING); /* Reset file */ /* Free old song */ { struct TagItem *tag; if (tag = FindTagItem (XMSNG_OldSong, tags)) xmDeleteSong (tag->ti_Data ? (struct SongInfo *)tag->ti_Data : XModuleBase->xm_CurrentSong); } /* Create a new song and set its title and path. * File name will be replaced by the real song name if the * load format supports embedded song names (e.g.: SoundTracker). */ if (si = xmCreateSong ( SNGA_Path, fullpath, SNGA_Title, FilePart (filename), TAG_DONE)) { OpenProgressWindow(); xmDisplayMessage (XMDMF_USECATALOG | XMDMF_INFORMATION, (APTR)MSG_READING_TYPE_MODULE, loader->xmh_Link.ln_Name); /* Call loader hook */ err = loader->xmh_LoadMod (fh, si, loader, tags); if (err == ERROR_IOERR) { if (err = IoErr()) xmDisplayMessage (XMDMF_USECATALOG | XMDMF_DOSFAULT | err, (APTR)MSG_ERROR_READING, FilePart (filename)); else xmDisplayMessage (XMDMF_USECATALOG | XMDMF_ERROR, (APTR)MSG_UNESPECTED_EOF); } FixSong (si); CloseProgressWindow(); } else err = ERROR_NO_FREE_STORE; } else err = ERROR_ACTION_NOT_KNOWN; } else err = ERROR_OBJECT_WRONG_TYPE; ReleaseSemaphore (&XModuleBase->xm_BaseLock); Close (fh); if (compressed) DecompressFileDone (); if (!err) xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_USECATALOG, (APTR)MSG_MODULE_LOADED_OK, NULL); else SetIoErr (LastErr = err); if (si) { struct TagItem *tag; if (tag = FindTagItem (XMSNG_AddToList, tags)) { ObtainSemaphore (&si->Lock); xmAddSongA (si, (struct SongInfo *)tag->ti_Data, NULL); if (GetTagData (XMSNG_Active, FALSE, tags)) xmActivateSong (si); } } return si; } /****** xmodule/xmSaveModuleA ********************************************** * * NAME * xmSaveModuleA -- Saves a module to the specified file format * xmSaveModule -- Varargs stub for xmSaveModuleA * * SYNOPSIS * error = xmSaveModuleA(songInfo, fileName, saver, tagList) * D0 A0 A1 A2 A3 * * LONG xmSaveModuleA(struct SongInfo *,CONST_STRPTR,struct XMHook *, * struct TagItem *); * * * error = xmSaveModule(songInfo, fileName, saver, tag1,...) * * LONG xmSaveModule(struct SongInfo *,CONST_STRPTR,struct XMHook *, * LONG,...); * * FUNCTION * Saves songInfo to fileName using the specified saver. * * INPUTS * songInfo - Song to save. * fileName - AmigaDOS filename to write the song to. * saver - Pointer to the saver to use. Pass NULL to use * the default saver. * * TAGS * No tags are defined for this call. * * RESULT * error - RETURN_OK for success, or any other AmigaDOS error code * to mean failure. In particular, RETURN_WARN indicates that * something went wrong, but the song is still ok. The special * error code ERROR_IOERR means that some dos.library * call (e.g.: Read()) failed. In the latter case, the * AmigaDOS IoErr() value will be set to explain the specific * cause of the problem. * * SEE ALSO * **************************************************************************** */ static LIBCALL LONG XModuleSaveModule ( REG(a0, struct SongInfo *si), REG(a1, CONST_STRPTR filename), REG(a2, struct XMHook *saver), REG(a3, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)) { LONG err; BPTR fh; BOOL releasebase = FALSE; /* Use the default saver if not specified */ if (!saver) { #ifdef OS30_ONLY ObtainSemaphoreShared (&XModuleBase->xm_BaseLock); #else /* Workaround for Pre-V39 ObtainSemaphoreShared() bug (see autodoc) */ /* Try to get the shared semaphore */ if (!AttemptSemaphoreShared (&XModuleBase->xm_BaseLock)) /* Check if we can get the exclusive version */ if (!AttemptSemaphore (&XModuleBase->xm_BaseLock)) /* Oh well, wait for the shared lock */ ObtainSemaphoreShared (&XModuleBase->xm_BaseLock); #endif /* OS30_ONLY */ if (!(saver = XModuleBase->xm_DefaultSaver)) { ReleaseSemaphore (&XModuleBase->xm_BaseLock); return ERROR_ACTION_NOT_KNOWN; } releasebase = TRUE; } if (saver->xmh_SaveMod) { ULONG i; /* Make some checks before actually saving */ if (si->LastInstrument > saver->xmh_MaxInstruments) { xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG, (APTR)MSG_SONG_TOO_MANY_INSTRS, si->LastInstrument, saver->xmh_MaxInstruments); if (ShowRequest (MSG_TRY_REMAPPING_INSTRUMENTS, MSG_PROCEED_OR_CANCEL)) xmProcessSong (si, NULL, XMSNG_RemapInstruments, TRUE, TAG_DONE); } for (i = 1; i <= si->LastInstrument; i++) if (si->Instr[i] && (si->Instr[i]->Length > saver->xmh_MaxSampleLen)) { xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG, (APTR)MSG_INSTR_TOO_LONG, i, saver->xmh_MaxSampleLen); } for (i = 0; i < si->NumPatterns; i++) { if (si->Patt[i]) if ((si->Patt[i]->Lines > saver->xmh_MaxPattLen) || ( (saver->xmh_Flags & XMHF_FIXED_PATT_LEN) && (si->Patt[i]->Lines != saver->xmh_MaxPattLen) ) ) { xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG, (APTR)MSG_PATT_LENGTH_INVALID, i, si->Patt[i]->Lines); if (ShowRequest (MSG_WILL_MODIFY_SONG, MSG_PROCEED_OR_CANCEL)) { xmProcessSong (si, NULL, XMSNG_LimitPatterns, saver->xmh_MaxPattLen | ((saver->xmh_Flags & XMHF_FIXED_PATT_LEN) ? (saver->xmh_MaxPattLen << 16) : 0), TAG_DONE); break; } else { if (releasebase) ReleaseSemaphore (&XModuleBase->xm_BaseLock); return ERROR_BREAK; } } } if (si->MaxTracks > saver->xmh_MaxTracks) xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG, (APTR)MSG_SONG_TOO_MANY_TRACKS, saver->xmh_MaxTracks, si->MaxTracks); if (si->NumPatterns > saver->xmh_MaxPatterns) xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG, (APTR)MSG_SONG_TOO_MANY_PATTS, saver->xmh_MaxPatterns, si->NumPatterns); if (si->Length > saver->xmh_MaxLength) xmDisplayMessage (XMDMF_WARNING | XMDMF_USECATALOG, (APTR)MSG_SONG_TOO_MANY_POS, saver->xmh_MaxLength, si->Length); /* Now make a backup of the old file before overwriting this */ if (GuiSwitches.DoBackups) BackupFile (filename, GuiSwitches.BackupTemplate, GuiSwitches.BackupVersions); if (fh = Open (filename, MODE_NEWFILE)) { OpenProgressWindow(); /* Optimize file buffering for faster I/O. * * NOTE: SetVBuf() was introduced in V39, but V37 has a * no-op vector for it, so it is safe to call SetVBuf() * without checking the OS version. * NOTE: Due to a bug in dos.library V40, SetVBuf() works * only when called right after Open() (before doing any I/O). */ SetVBuf (fh, NULL, BUF_FULL, 16*1024); xmDisplayMessage (XMDMF_INFORMATION | XMDMF_USECATALOG, (APTR)MSG_SAVING_MODULE, saver->xmh_Link.ln_Name, si->Title ? si->Title : STR(MSG_SONG_UNTITLED)); err = saver->xmh_SaveMod (fh, si, saver, tags); Close (fh); SetComment (filename, VERS " by Bernardo Innocenti"); if (err) { /* Delete incomplete file */ DeleteFile (filename); if (err == ERROR_IOERR) xmDisplayMessage (XMDMF_USECATALOG | XMDMF_DOSFAULT, (APTR)MSG_ERROR_WRITING, filename); } else { if (SaveSwitches.SaveIcons) PutIcon ("def_Module", filename); xmDisplayMessageA (XMDMF_INFORMATION | XMDMF_USECATALOG, (APTR)MSG_MODULE_SAVED_OK, NULL); } CloseProgressWindow(); } else /* Open failed */ { err = IoErr(); xmDisplayMessage (XMDMF_USECATALOG | XMDMF_DOSFAULT, (APTR)MSG_CANT_OPEN, filename); } } else err = ERROR_ACTION_NOT_KNOWN; if (releasebase) ReleaseSemaphore (&XModuleBase->xm_BaseLock); return err; } /****** xmodule/xmIdentifyModule ******************************************* * * NAME * xmIdentifyModule -- Returns the type of a module * * SYNOPSIS * loader = xmIdentifyModule(fh, tagList) * D0 D0 A0 * * struct XMHook *xmIdentifyModule(BPTR,struct TagItem *); * * FUNCTION * Finds out a loader which is able to read the given module. * * INPUTS * fh - AmigaDOS FileHandle to examine. * tagList - Additional parameters. Leave it NULL for now. * * RESULT * loader - Pointer to the first loader which knows how to load this * module, or NULL otherwise. * * NOTE * Before you call this function, you must first obtain a lock on the * loaders list to protect yourself from other tasks which might remove * the returned loader before you can actually use it. * * SEE ALSO * **************************************************************************** */ static LIBCALL struct XMHook *XModuleIdentifyModule( REG(d0, BPTR fh), REG(a0, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)) { struct XMHook *loader, *ret = NULL; loader = (struct XMHook *) XModuleBase->xm_Loaders.mlh_Head; while (loader->xmh_Link.ln_Succ) { if (ret = loader->xmh_IdentifyMod (fh, loader, tags)) break; loader = (struct XMHook *)loader->xmh_Link.ln_Succ; } Seek (fh, 0, OFFSET_BEGINNING); return ret; } /****** xmodule/xmSetSongLen *********************************************** * * NAME * xmSetSongLen -- Set the number of song positions * * SYNOPSIS * sequence = xmSetSongLen(songInfo, length) * D0 A0 D0:16 * * UWORD *xmSetSongLen(struct SongInfo *, UWORD); * * FUNCTION * Allocates space in the song for a sequence table of the given * length. If no sequence exists yet, a new one is allocated. * Increasing the song length may require the sequence table to be * expanded, in which case it will be re-allocated and the old sequence * entries will be copied. Decreasing the song length could also cause * a reallocation of the sequence table if the size drops down enough. * Setting the song length to 0 will free the sequence table and return * NULL. * * INPUTS * songInfo - pointer to a SongInfo structure. * length - new length of song sequence table. * * NOTE * This function will also adjust the CurrentPos field if it is found * beyond the song end * * BUGS * This funtion is quite useless because all it does is setting the * SNGA_Length attribute in the song object. Setting the SNGA_Length * attribute yourself is easier and faster if you are already setting * other attributes in the same song. * * RESULT * Pointer to the newly allocated sequence table or NULL for failure, * in which case the previous sequence table is left untouched. * **************************************************************************** */ static LIBCALL UWORD *XModuleSetSongLen ( REG(a0, struct SongInfo *si), REG(d0, UWORD len), REG(a6, struct XModuleBase *XModuleBase)) { SetAttrs (si, SNGA_Length, len, TAG_DONE); return si->Sequence; } /****** xmodule/xmAddPatternA ********************************************** * * NAME * xmAddPatternA -- Adds a pattern to a song * xmAddPattern -- Varargs stub for xmAddPatternA * * SYNOPSIS * pattern = xmAddPatternA(si, tagList) * D0 A0 A1 * * struct Pattern *xmAddPatternA(struct SongInfo *,struct TagItem *); * * * pattern = xmAddPattern (si,tag1,...) * * struct Pattern *xmAddPattern(struct SongInfo *,LONG Tag1,...); * * FUNCTION * Adds a pattern to a song by calling the SNGM_ADDPATTERN method. * * INPUTS * si - pointer to the song to which the pattern should be added. * tagList - optional TagList. See SNGM_ADDPATTERN for possible tags. * * RESULT * Pointer to the new pattern or NULL for failure. * * NOTE * In order to add patterns, you should have exclusive access to * the song. Always obtain a lock before you call this function on * public songs. * * SEE ALSO * xmRemPattern(), songclass/SNGM_ADDPATTERN * **************************************************************************** */ static LIBCALL void XModuleAddPattern ( REG(a0, struct SongInfo *si), REG(a1, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)) { DoMethod ((Object *)si, SNGM_ADDPATTERN, tags); } /****** xmodule/xmSetPatternA ********************************************** * * NAME * xmSetPatternA -- Sets pattern attributes * xmSetPattern -- Varargs stub for xmSetPatternA * * SYNOPSIS * success = xmSetPatternA(si, pattNum, tagList) * D0 A0 D0 A1 * * ULONG xmSetPatternA(struct SongInfo *,ULONG,struct TagItem *); * * * success = xmSetPattern (si,pattNum,tag1,...) * * ULONG xmSetPattern(struct SongInfo *,ULONG,LONG Tag1,...); * * FUNCTION * Sets attributes of a pattern by calling the SNGM_SETPATTERN method. * * INPUTS * si - pointer to the song which contains the pattern to be set. * tagList - list of attributes to set. See SNGM_SETPATTERN for * possible tags. * * RESULT * Non zero for success * * NOTE * In order to set patterns attributes, you should have exclusive * access to the song. Always obtain a lock before you call this * function on public songs. * * SEE ALSO * xmAddPattern(), songclass/SNGM_SETPATTERN * **************************************************************************** */ static LIBCALL void XModuleSetPattern ( REG(a0, struct SongInfo *si), REG(d0, ULONG pattNum), REG(a1, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)) { DoMethod ((Object *)si, SNGM_SETPATTERN, pattNum, tags); } /****** xmodule/xmRemPattern *********************************************** * * NAME * xmRemPattern -- Removes a pattern from a song * * SYNOPSIS * xmRemPattern(si, pattNum, replaceWith) * A0 D0, D1 * * void xmRemPattern(struct SongInfo *,LONG,LONG); * * FUNCTION * Removes a pattern from a song by calling the SNGM_REMPATTERN method. * * INPUTS * si - pointer to the song to which the pattern should be removed. * mum - Number of the pattern to be removed. * replaceWith - What to put in the song sequence in place of the * deleted pattern. * * NOTE * In order to remove patterns, you should have exclusive access to * the song. Always obtain a lock before you call this function on * public songs. * * SEE ALSO * xmAddPatternA(), songclass/SNGM_REMPATTERN * **************************************************************************** */ static LIBCALL void XModuleRemPattern ( REG(a0, struct SongInfo *si), REG(d0, ULONG pattNum), REG(d1, ULONG replaceWith)) { DoMethod ((Object *)si, SNGM_REMPATTERN, pattNum, replaceWith); } /****** xmodule/xmAddInstrumentA ******************************************* * * NAME * xmAddInstrumentA -- Adds a instrument to a song * xmAddInstrument -- Varargs stub for xmAddInstrumentA * * SYNOPSIS * instrument = xmAddInstrumentA(si, instrNum, tagList) * D0 A0 D0 A1 * * struct Instrument *xmAddInstrumentA(struct SongInfo *,LONG, * struct TagItem *); * * * instrument = xmAddInstrument(si,instrNum,tag1,...) * * struct Instrument *xmAddInstrument(struct SongInfo *,LONG,LONG,...); * * FUNCTION * Adds an instrument to a song by calling the SNGM_ADDINSTRUMENT method. * * INPUTS * si - pointer to the song to which the instrument should be added. * tagList - optional TagList. See SNGM_ADDINSTRUMENT for possible tags. * * RESULT * Pointer to the new instrument or NULL for failure. * * NOTE * In order to add instruments, you should have exclusive access to * the song. Always obtain a lock before you call this function on * public songs. * * SEE ALSO * xmRemInstrument(), songclass/SNGM_REMINSTRUMENT * **************************************************************************** */ static LIBCALL void XModuleAddInstrument ( REG(a0, struct SongInfo *si), REG(d0, LONG num), REG(a1, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)) { DoMethod ((Object *)si, SNGM_ADDINSTRUMENT, num, tags); } /****** xmodule/xmSetInstrumentA ******************************************* * * NAME * xmSetInstrumentA -- Sets an instrument's attributes * xmSetInstrument -- Varargs stub for xmSetInstrumentA * * SYNOPSIS * success = xmSetInstrumentA(si, instrNum, tagList) * D0 A0 D0 A1 * * ULONG xmSetInstrumentA(struct SongInfo *,LONG, * struct TagItem *); * * * success = xmSetInstrument(si,instrNum,tag1,...) * * ULONG xmSetInstrument(struct SongInfo *,LONG,LONG,...); * * FUNCTION * Sets an instrument's attributes by calling the SNGM_SETINSTRUMENT * method. * * INPUTS * si - pointer to the song which contains the instrument to be set. * tagList - instrument attributes to set. See SNGM_SETINSTRUMENT * for possible tags. * * RESULT * non zero for success. * * NOTE * In order to set instruments' attributes, you should have * exclusive access to the song. Always obtain a lock before * you call this function on public songs. * * SEE ALSO * xmAddInstrument(), songclass/SNGM_SETINSTRUMENT * **************************************************************************** */ static LIBCALL void XModuleSetInstrument ( REG(a0, struct SongInfo *si), REG(d0, LONG num), REG(a1, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)) { DoMethod ((Object *)si, SNGM_SETINSTRUMENT, num, tags); } /****** xmodule/xmRemInstrument *********************************************** * * NAME * xmRemInstrument -- Removes an instrument from a song * * SYNOPSIS * xmRemInstrument(si, instrNum) * A0 D0 * * void xmRemInstrument(struct SongInfo *,LONG); * * FUNCTION * Removes an instrument from a song by calling the SNGM_REMINSTRUMENT * method. * * INPUTS * si - pointer to the song to which the instrument should be removed. * mum - Number of the instrument to be removed. * * NOTE * In order to remove instruments, you should have exclusive access to * the song. Always obtain a lock before you call this function on * public songs. * * SEE ALSO * xmAddInstrumentA(), songclass/SNGM_REMINSTRUMENT * **************************************************************************** */ static LIBCALL void XModuleRemInstrument ( REG(a0, struct SongInfo *si), REG(d0, ULONG num), REG(a6, struct XModuleBase *XModuleBase)) { DoMethod ((Object *)si, SNGM_REMINSTRUMENT, num); } /****** xmodule/xmProcessSongA ********************************************* * * NAME * xmProcessSongA -- Performs complex processing on a song * xmProcessSong -- Varargs stub for xmProcessSongA() * * SYNOPSIS * result = xmProcessSongA(si, reserved, tagList) * A0 A1, A2 * * LONG xmProcessSongA(struct SongInfo *,void *,struct TagItem *); * * * result = xmProcessSong(si, reserved, tag1, ...) * * LONG xmProcessSongA(struct SongInfo *,void *,LONG,...); * * FUNCTION * Performs complex processing operations on a song. * * INPUTS * si - pointer to the song to be processed. * reserved - Reserved for future use. * tagList - List of arguments. See below for possible tags. * * RESULT * The result depends on the kind of operation done. For most * operations, it's value will be 0 to indicate success and * an AmigaDOS error code which indicates the cause for failure. * * TAGS * XMSNG_Optimize - (LONG). Tries to reduce the song size by removing * all unused data. Possible optimizations are: * - XMOF_REM_UNUSED_PATTERNS * - XMOF_REM_DUP_PATTERNS * - XMOF_CUT_PATTERNS * - XMOF_REM_UNUSED_INSTRS * - XMOF_CUT_INSTR_LOOPS * - XMOF_CUT_INSTR_TAILS * - XMOF_DEFAULT * XMOF_DEFAULT will select all the optimizations choosen by * the user in addition to the ones specified with the other flags. * * XMSNG_RemapInstruments - (BOOL) Performs instruments remapping. * * XMSNG_LimitPatterns - (UWORD,UWORD) Limits the length all the * patterns inside a minimum and maximum value. The upper 16bits * of the argument are the minimum value, the lower ones are * the maximum value. Patterns outside this limits will be grown * or split as appropriate. * * XMSNG_Join - (struct SongInfo *) - Joins the song with another one. * * XMSNG_Merge - (struct SongInfo *) - Merges the song with another one. * * NOTE * In order to process a song, you should have exclusive access to * it. Always obtain a lock before you call this function on * public songs. * **************************************************************************** */ static LIBCALL LONG XModuleProcessSong ( REG(a0, struct SongInfo *si), REG(a1, void *reserved), REG(a2, struct TagItem *tags), REG(a6, struct XModuleBase *XModuleBase)) { struct TagItem *ti, *tstate = tags; LONG result = 0; while (ti = NextTagItem(&tstate)) { switch (ti->ti_Tag) { case XMSNG_Optimize: { ULONG flags = ti->ti_Data; if (flags & XMOF_DEFAULT) { if (OptSwitches.RemPatts) flags |= XMOF_REM_UNUSED_PATTERNS; if (OptSwitches.RemDupPatts) flags |= XMOF_REM_DUP_PATTERNS; if (OptSwitches.RemInstr) flags |= XMOF_REM_UNUSED_INSTRS; if (OptSwitches.RemDupInstr) flags |= XMOF_REM_DUP_INSTRS; if (OptSwitches.CutAfterLoop) flags |= XMOF_CUT_INSTR_LOOPS; if (OptSwitches.CutZeroTail) flags |= XMOF_CUT_INSTR_TAILS; if (OptSwitches.CutPatts) flags |= XMOF_CUT_PATTERNS; if (OptSwitches.RemapInstr) flags |= XMOF_REMAP_INSTRS; } if (flags & XMOF_REM_UNUSED_PATTERNS) DiscardPatterns (si); if (flags & XMOF_REM_DUP_PATTERNS) RemDupPatterns (si); if (flags & XMOF_CUT_PATTERNS) CutPatterns (si); if (flags & XMOF_REM_UNUSED_INSTRS) RemUnusedInstruments (si); if (flags & XMOF_REM_DUP_INSTRS) RemDupInstruments (si); if (flags & XMOF_CUT_INSTR_LOOPS) OptimizeInstruments (si); if (flags & XMOF_CUT_INSTR_TAILS) OptimizeInstruments (si); if (flags & XMOF_REMAP_INSTRS) RemapInstruments (si); break; } case XMSNG_RemapInstruments: RemapInstruments (si); result = 0; break; case XMSNG_LimitPatterns: result = ResizePatterns (si, ti->ti_Data >> 16, ti->ti_Data & 0xFFFF); break; case XMSNG_Join: result = NULL; break; case XMSNG_Merge: result = NULL; break; default: break; } } return result; } /****** xmodule/xmDisplayMessage ******************************************* * * NAME * xmDisplayMessageA -- Displays a message to the user * xmDisplayMessage -- Varargs stub for xmDisplayMessageA() * * SYNOPSIS * xmDisplayMessageA(level, message, args) * DO A0 A1 * * void xmDisplayMessageA(ULONG,APTR,LONG *); * * * xmDisplayMessage(level, message, ...) * * void xmDisplayMessage(ULONG,APTR,...); * * FUNCTION * Outputs a string in the XModule log window or in the 'action' field * of the progress indicator. The string is printf-formatted before * being output. * * INPUTS * level - a number from 0 to 7 which indicates the importance of the * message. 7 is used for very annoying messages (eg: debug output), * 0 for very important things (eg: disaster). * If you set the XMDMF_USECATALOG bit in the level parameter, you * can pass a catalog string number instead of a pointer to a string. * Specifying XMDMF_ACTION, the message will be put in the 'action' * field of the progress indicator. * If the flag XMDMF_DOSFAULT is specified, a Fault() output is * formatted using the message as an header. In this case, the * level parameter takes another meaing: The lower word can contain * an AmigaDOS error code. If it is 0, IoErr() will be used to get * the error code. * message - pointer to a string or catalog message number, * when the XMDMF_USECATALOG flag is set. * args - arguments for formatting. * * EXAMPLES * xmDisplayMessage (XMDMF_ALERT, * "The application `%s' fucked up Windoze95 because %s.", * "(unknown name)", "an error occurred"); * * xmDisplayMessage (XMDMF_USE_CATALOG | XMDMF_COMMENT, * MSG_LIFE_UNIVERSE_AND_ALLTHAT, 42, "Fortytwo", "For tea too"); * * xmDisplayMessageA (XMDMF_ACTION | XMDMF_USECATALOG, * MSG_READING_COMICS, NULL); * * xmDisplayMessage (XMDMF_DOSFAULT | XMDMF_USECATALOG, * MSG_CANT_LOAD_MODULE, ModuleName); * * SEE ALSO * xmDisplayProgress() * **************************************************************************** */ static LIBCALL void XModuleDisplayMessage ( REG(d0, ULONG level), REG(a0, CONST_STRPTR msg), REG(a1, LONG *args), REG(a6, struct XModuleBase *XModuleBase)) { if (level & XMDMF_USECATALOG) msg = STR((ULONG)msg); if (level & XMDMF_DOSFAULT) { UBYTE buf[FAULT_MAX + 100]; UBYTE buf2[100]; if (GuiSwitches.LogLevel >= XMDMF_ERROR) { if (args) VSPrintf (buf2, msg, args); if (Fault ((level & XMDMF_DOSERRORMASK) ? (level & XMDMF_DOSERRORMASK) : IoErr(), args ? buf2 : msg, buf, FAULT_MAX + 100)) { if (level & XMDMF_USEREQUESTER) ShowRequestStr (buf, NULL, args); else ShowString (buf, args); } } } else if (level & XMDMF_ACTION) { DisplayActionStr (msg); if (GuiSwitches.LogLevel > XMDMF_INFORMATION) ShowString (msg, args); } else if (level & XMDMF_USEREQUESTER) ShowRequestStr (msg, NULL, args); else if (GuiSwitches.LogLevel > (level & XMDMF_LEVELMASK)) ShowString (msg, args); } /****** xmodule/xmDisplayProgress ****************************************** * * NAME * xmDisplayProgress -- Uptdates the progress bar indicator * * SYNOPSIS * abort = xmDisplayProgress(done, total) * D0 D1 * * LONG xmDisplayMessageA(ULONG,ULONG); * * FUNCTION * Updates the position of the fuel gauge in the progress window to * show the progress of an operation. Additionally, it checks for * user abort. * * INPUTS * done - a number which indicates how much of the work has * been already completed * total - Total number of operations to do. * * RESULT * 0 for success, ERROR_BREAK if user abort was detected. You should * always check this return code to abort what you were doing. * **************************************************************************** */ static LIBCALL void XModuleDisplayProgress ( REG(d0, ULONG done), REG(d1, ULONG total), REG(a6, struct XModuleBase *XModuleBase)) { DisplayProgress (done, total); }