
        /*name of the message port*/
#define EPMPORT_NAME    "SamED_EPMPort"

        /*External Processor Commands*/
        /*commands always available...*/
#define EPC_VERSION     0   /* returns current version */
#define EPC_AUTOQUIT    1   /* set auto quit mode for a specific task (given a pointer to the task, and a sigbit number */
#define EPC_IDNUM       2   /* returns your (the ext. proc.) id number given a pointer to your task */
#define EPC_MULTI       3   /* set multi load mode, the given ext. proc. can be opened multiple times */
#define EPC_QUIT        4   /* tell SamEd you will quit, given your ID no. and/or task pointer */
#define EPC_TOTALNUM    5   /* ask for maximum number of samples */

        /*lock commands...*/
#define EPC_LOCK        6   /* set lock, required to alter data and/or to safely read data*/
#define EPC_UNLOCK      7   /* unlock lock */

        /*commands requiring you to hold the lock to the system...*/
#define EPC_GETSAMPLE   8   /* get pointer to current sam_info structure */
#define EPC_GETSAMPLES  9   /* get pointer to array of all sam_info structs */
#define EPC_GETBUFFER   10  /* get pointer to sample[0] / the copy buffer */
#define EPC_GETSAMNUM   11  /* get current sample number */
#define EPC_GETRSTART   12  /* get range start */
#define EPC_GETREND     13  /* get range end */
#define EPC_SETSAMNUM   14  /* set current sample */
#define EPC_SETRSTART   15  /* set range start */
#define EPC_SETREND     16  /* set range end */
#define EPC_FLUSH       17  /* flush current sample from memory */
#define EPC_PIOPEN      19  /* open progress indicator */
#define EPC_PISTRING    20
#define EPC_PIMAX       21
#define EPC_PIVAL       22
#define EPC_PICLOSE     23


        /*EPCErrors...*/
#define EPCERR_NOERR    0   /* command sucessful */
#define EPCERR_NOCOMMAND    1   /* unrecognised command */
#define EPCERR_SAMNUM   2   /* sample id number out of range */
#define EPCERR_NOAQUIT  3   /* auto quit could not be allocated */
#define EPCERR_LOCKED   4   /* returned by EPC_LOCK when system is already locked */

        /*Procs magic number*/
#define PROC_MAGIC      ('S'<<24 | 'E'<<16 | 'P'<<8 | 0)        /*SamEd Proc version 0*/


        /*Filer errors*/
#define FERR_NOERR      0   /* no error */
#define FERR_GENERAL    1   /* general error. The minimum you should return if an error occurs */
#define FERR_FILEERR    2   /* file error. General error with the actual file processing */
#define FERR_ACTIONNA   3   /* action (load / save / etc.) not supported */
#define FERR_STYPENA    4   /* sample type not supported */
#define FERR_NOTMAGIC   5   /* the 'magic number' did not match */

        /*Filer actions*/
#define FACT_LOAD       1   /* load action */
#define FACT_SAVE       2   /* save action */

        /*Filers magic number*/
#define FILER_MAGIC     ('S'<<24 | 'E'<<16 | 'F'<<8 | 0)        /*SamEd Filer version 0*/


        /*structures...*/

static struct Ext_Proc_Msg{
    struct Message  epm_Msg;
    ULONG           epm_Command;
    ULONG           epm_Error;
    APTR            epm_Data;
};

static struct Ext_Proc_ID{
    struct Task *task;
    ULONG number;
};

static struct sam_info {
    BYTE *waveform;
    ULONG length;
    ULONG type;
    ULONG loop_begin;
    ULONG loop_length;
    BOOL  loop;
    BYTE  reserved[3];
    ULONG def_freq;
    ULONG def_vol;
    ULONG file_type;
    char *dir;
    char *name;
};

    /*sound types, these are the same as AHI*/
#define STYPE_M8            0                     /* Mono, 8 bit signed (BYTE) */
#define STYPE_M16           1                     /* Mono, 16 bit signed (WORD) */
#define STYPE_M32           8                     /* Mono, 32 bit signed (LONG) */
#define STYPE_S8            2                     /* Stereo, 8 bit signed (2×BYTE) */
#define STYPE_S16           3                     /* Stereo, 16 bit signed (2×WORD) */
#define STYPE_S32           10                    /* Stereo, 32 bit signed (2×LONG) */



/*---------------------------------------------------------------------------------------------------\
|       The following functions are to help with communication with SamEd.                           |
|       define NO_EPP_HELP if you do not require them.                                               |
\---------------------------------------------------------------------------------------------------*/

#ifndef NO_EPP_HELP

struct EP_Port {
    struct MsgPort *our_mp;
    struct Ext_Proc_Msg message;
    ULONG button;
    BOOL lock;
    };

typedef struct EP_Port * EPP_HANDLE;

EPP_HANDLE  EP_NewPort (ULONG);
void        EP_DeletePort (EPP_HANDLE);
BOOL        EP_SendMsg (EPP_HANDLE, ULONG, void *);
BOOL        EP_SendIDMsg (EPP_HANDLE, ULONG, struct Task *, ULONG);
BOOL        EP_Quit (EPP_HANDLE);
BYTE        EP_AllocQSig (EPP_HANDLE);
void        EP_DeallocQSig (BYTE);
BOOL        EP_Lock (EPP_HANDLE);
BOOL        EP_Unlock (EPP_HANDLE);
BOOL        EP_GetSample (EPP_HANDLE, struct sam_info **, struct sam_info **, ULONG *);


EPP_HANDLE EP_NewPort (ULONG button)
{
    EPP_HANDLE ep_port;

    if (ep_port = AllocMem(sizeof (struct EP_Port), MEMF_CLEAR))
    {
        if (ep_port->our_mp = (struct MsgPort *)CreatePort(NULL,0))
        {
            ep_port->message.epm_Msg.mn_Node.ln_Type = NT_MESSAGE;
            ep_port->message.epm_Msg.mn_ReplyPort = ep_port->our_mp;
            ep_port->message.epm_Msg.mn_Length = sizeof(struct Ext_Proc_Msg);
            ep_port->button = button;
            ep_port->lock = FALSE;
            return (ep_port);
        }
        else
        {
            FreeMem(ep_port, sizeof (struct EP_Port));
            return(NULL);
        }
    }
    else
        return (NULL);
}

void EP_DeletePort (EPP_HANDLE ep_port)
{
    if (ep_port->lock) EP_Unlock (ep_port);

    if (ep_port)
    {
        if (ep_port->our_mp)
        {
            while(GetMsg(ep_port->our_mp));
            DeletePort(ep_port->our_mp);
        }

        FreeMem(ep_port, sizeof (struct EP_Port));
    }
}

BYTE EP_AllocQSig (EPP_HANDLE ep_port)
{
    BYTE signal;

    if (-1 == (signal = AllocSignal(-1)))
        return(-1);

    if (EP_SendIDMsg(ep_port, EPC_AUTOQUIT, FindTask(0), signal))
        return(signal);

    FreeSignal (signal);
    return(-1);
}

void EP_DeallocQSig (BYTE signal)
{
    if (signal != -1)
        FreeSignal (signal);
}

BOOL EP_SendMsg (EPP_HANDLE ep_port, ULONG command, void * data)
{
    printf("Try to send...\n");
    struct MsgPort *dest_mp;

    ep_port->message.epm_Command = command;
    ep_port->message.epm_Data = data;

    Forbid();
    if (dest_mp = (struct MsgPort *)FindPort(EPMPORT_NAME))
        PutMsg(dest_mp, (struct Message *)&ep_port->message);
    Permit();
    if (dest_mp)
    {
        printf("Sent!!!\n");
        WaitPort(ep_port->our_mp);
        while(GetMsg(ep_port->our_mp));
    }

    return (dest_mp ? TRUE : FALSE);
}

BOOL EP_SendIDMsg (EPP_HANDLE ep_port, ULONG command, struct Task * task, ULONG number)
{
    struct Ext_Proc_ID id_data;
    id_data.task = task;
    id_data.number = number;

    return (EP_SendMsg (ep_port, command, &id_data));
}

BOOL EP_Quit (EPP_HANDLE ep_port)
{
    return (EP_SendIDMsg (ep_port, EPC_QUIT, FindTask(0), ep_port->button));
}

BOOL EP_Lock (EPP_HANDLE ep_port)
{
    if (ep_port->lock == TRUE)
        return (TRUE);

    if (!EP_SendMsg (ep_port, EPC_LOCK, NULL))
        return (FALSE);

    if (ep_port->message.epm_Error)
        return (FALSE);

    ep_port->lock = TRUE;
    return (TRUE);
}

BOOL EP_Unlock (EPP_HANDLE ep_port)
{
    if (ep_port->lock == FALSE)
        return (TRUE);

    if (!EP_SendMsg (ep_port, EPC_UNLOCK, NULL))
        return (FALSE);

    if (ep_port->message.epm_Error)
        return (FALSE);

    ep_port->lock = FALSE;
    return (TRUE);
}

BOOL EP_GetSample (EPP_HANDLE ep_port, struct sam_info **sample, struct sam_info **copybuf, ULONG *range)
{
    BOOL sucess = TRUE, old_lock = ep_port->lock;

    if (ep_port->lock == FALSE)
        if (!EP_Lock (ep_port)) return(FALSE);

    if (sample)
    {
        if (EP_SendMsg (ep_port, EPC_GETSAMPLE, NULL))
            *(sample) = ep_port->message.epm_Data;
        else
            {*(sample) = NULL; sucess = FALSE;}
    }

    if (copybuf)
    {
        if (EP_SendMsg (ep_port, EPC_GETBUFFER, NULL))
            *(copybuf) = ep_port->message.epm_Data;
        else
            {*(copybuf) = NULL; sucess = FALSE;}
    }

    if (range)
    {
        if(EP_SendMsg (ep_port, EPC_GETRSTART, NULL))
        {
            *(range++) = *((ULONG *)ep_port->message.epm_Data);

            if (EP_SendMsg (ep_port, EPC_GETREND, NULL))
            {
                *range = *((ULONG *)ep_port->message.epm_Data);
            }
            else
            {
                *(range--) = 0;
                *(range) = 0;
                sucess = FALSE;
            }
        }
    }

    if (!old_lock) EP_Unlock(ep_port);

    return(sucess);
}

#endif  /*NO_EPP_HELP*/



/*---------------------------------------------------------------------------------------------------\
|       These functions help with 8 / 16 / mono / stereo data processing.                            |
|       32 bit floating point versions are also included (I don't know if they work, nor do I        |
|       know how 32 bit fp data is supposed to be interpreted!).                                     |
|                                                                                                    |
|       All data is changed to 16 bit resolution.                                                    |
|                                                                                                    |
|       I have estimated the Macros SET_ / GET_LEVEL to be 50% quicker than the functions            |
|       Set_ / Get_Level().                                                                          |
|                                                                                                    |
|       define NO_DATA_HELP to disable.                                                              |
\---------------------------------------------------------------------------------------------------*/

#ifndef NO_DATA_HELP

void    Set_Level (BYTE *, ULONG, LONG, ULONG, UBYTE);
WORD    Get_Level (BYTE *, ULONG, ULONG, UBYTE);
ULONG   Sams_To_Bytes (ULONG, ULONG);
ULONG   Bytes_To_Sams (ULONG, ULONG);
void    CopyWave (BYTE *, ULONG, BYTE *, ULONG, ULONG);
void    setb_lev (BYTE *, ULONG, LONG, UBYTE);
void    setw_lev (BYTE *, ULONG, LONG, UBYTE);
void    setl_lev (BYTE *, ULONG, LONG, UBYTE);
void    setsb_lev (BYTE *, ULONG, LONG, UBYTE);
void    setsw_lev (BYTE *, ULONG, LONG, UBYTE);
void    setsl_lev (BYTE *, ULONG, LONG, UBYTE);
void    setnon_lev (BYTE *, ULONG, LONG, UBYTE);
LONG    getb_lev (BYTE *, ULONG, UBYTE);
LONG    getw_lev (BYTE *, ULONG, UBYTE);
LONG    getl_lev (BYTE *, ULONG, UBYTE);
LONG    getsb_lev (BYTE *, ULONG, UBYTE);
LONG    getsw_lev (BYTE *, ULONG, UBYTE);
LONG    getsl_lev (BYTE *, ULONG, UBYTE);
LONG    getnon_lev (BYTE *, ULONG, UBYTE);

static void (*Set_Level_Function[])(BYTE *, ULONG, LONG, UBYTE) = {
    setb_lev,
    setw_lev,
    setsb_lev,
    setsw_lev,
    setnon_lev,
    setnon_lev,
    setnon_lev,
    setnon_lev,
    setnon_lev,
    setl_lev,
    setnon_lev,
    setsl_lev,
    };

static LONG (*Get_Level_Function[])(BYTE *, ULONG, UBYTE) = {
    getb_lev,
    getw_lev,
    getsb_lev,
    getsw_lev,
    getnon_lev,
    getnon_lev,
    getnon_lev,
    getnon_lev,
    getnon_lev,
    getl_lev,
    getnon_lev,
    getsl_lev,
    };

/*Macros use 'type' to index an array of pointers to edit functions. SET_LEVELN does not clip*/

#define GET_LEVEL(start, pos, type, chan) (*Get_Level_Function[(type)])((start), (pos), (chan))
#define SET_LEVELN(start, pos, lev, type, chan) (*Set_Level_Function[(type)])((start), (pos), (lev), (chan))
#define SET_LEVEL(start, pos, lev, type, chan) LONG macro_level = lev;\
        if (macro_level > 32767) macro_level = 32767;\
        else if (macro_level < -32768) macro_level = -32768;\
        (*Set_Level_Function[(type)])((start), (pos), macro_level, (chan))

void Set_Level(BYTE *start, ULONG pos, LONG level, ULONG type, UBYTE channel)
{
    if (level > 32767) level = 32767;
    else if (level < -32768) level = -32768;

    switch (type)
    {
        case STYPE_M8:
            *((BYTE *) start + pos) = level >>8;
            break;

        case STYPE_M16:
            *((WORD *) start + pos) = level;
            break;

        case STYPE_M32:
            *((float *) start + pos) = (float)level;
            break;

        case STYPE_S8:
            *((BYTE *) start + (pos <<1) +channel) = level >>8;
            break;

        case STYPE_S16:
            *((WORD *) start + (pos <<1) +channel) = level;
            break;

        case STYPE_S32:
            *((float *) start + (pos <<1) +channel) = (float)level;
            break;
    }
}

WORD Get_Level(BYTE *start, ULONG pos, ULONG type, UBYTE channel)
{
    switch (type)
    {
        case STYPE_M8:  return( *(start + pos) <<8);

        case STYPE_M16: return( *((WORD *)start + pos));

        case STYPE_M32: return( (WORD) *((float *)start + pos));

        case STYPE_S8:  return( *(start + (pos <<1) +channel) <<8);

        case STYPE_S16: return( *((WORD *)start + (pos <<1) +channel));

        case STYPE_S32: return( (WORD) *((float *)start + (pos <<1) +channel));
    }
}

ULONG Sams_To_Bytes(ULONG sams, ULONG type)
{
    ULONG bytes;
    switch (type)
    {
        case STYPE_M8:
            bytes = sams;
            break;

        case STYPE_M16:
            bytes = sams *2;
            break;

        case STYPE_M32:
            bytes = sams *4;
            break;

        case STYPE_S8:
            bytes = sams *2;
            break;

        case STYPE_S16:
            bytes = sams *4;
            break;

        case STYPE_S32:
            bytes = sams *8;
            break;
    }
    return(bytes);
}

ULONG Bytes_To_Sams(ULONG bytes, ULONG type)
{
    ULONG sams;
    switch (type)
    {
        case STYPE_M8:
            sams = bytes;
            break;

        case STYPE_M16:
            sams = bytes /2;
            break;

        case STYPE_M32:
            sams = bytes /4;
            break;

        case STYPE_S8:
            sams = bytes /2;
            break;

        case STYPE_S16:
            sams = bytes /4;
            break;

        case STYPE_S32:
            sams = bytes /8;
            break;
    }
    return(sams);
}

void CopyWave (BYTE *from, ULONG fromtype, BYTE *to, ULONG totype, ULONG length)
{
    if (fromtype == totype)
    {
        length = Sams_To_Bytes (length, fromtype);
        CopyMem(from, to, length);
        return();
    }

    if (totype & 2)/*both stereo, or mono to stereo*/
    {
        for (ULONG n =0; n < length; n++)
        {
            SET_LEVELN(to, n, GET_LEVEL(from, n, fromtype, 0), totype, 0);
        }
        for (ULONG n =0; n < length; n++)
        {
            SET_LEVELN(to, n, GET_LEVEL(from, n, fromtype, 1), totype, 1);
        }
        return();
    }

    if (fromtype & 2)/*from stereo to mono*/
    {
        for (ULONG n =0; n < length; n++)
        {
            SET_LEVELN(to, n,
                (GET_LEVEL(from, n, fromtype, 0) + GET_LEVEL(from, n, fromtype, 1)) /2
                , totype, 0);
        }
        return();
    }

    /*else both mono*/
    {
        for (ULONG n =0; n < length; n++)
        {
            SET_LEVELN(to, n, GET_LEVEL(from, n, fromtype, 0), totype, 0);
        }
    }
}

void setb_lev (BYTE *start, ULONG pos, LONG level, UBYTE dummy)
{ *((BYTE *) start + pos) = level >>8; }

void setw_lev (BYTE *start, ULONG pos, LONG level, UBYTE dummy)
{ *((WORD *) start + pos) = level; }

void setl_lev (BYTE *start, ULONG pos, LONG level, UBYTE dummy)
{ *((float *) start + pos) = (float)level; }

void setsb_lev (BYTE *start, ULONG pos, LONG level, UBYTE chan)
{ *((BYTE *) start + (pos <<1) +chan) = level >>8; }

void setsw_lev (BYTE *start, ULONG pos, LONG level, UBYTE chan)
{ *((WORD *) start + (pos <<1) +chan) = level; }

void setsl_lev (BYTE *start, ULONG pos, LONG level, UBYTE chan)
{ *((float *) start + (pos <<1) +chan) = (float)level; }

void setnon_lev (BYTE *start, ULONG pos, LONG level, UBYTE dummy)
{}


LONG getb_lev (BYTE *start, ULONG pos, UBYTE dummy)
{ return (*(start + pos) <<8); }

LONG getw_lev (BYTE *start, ULONG pos, UBYTE dummy)
{ return (*((WORD *)start + pos)); }

LONG getl_lev (BYTE *start, ULONG pos, UBYTE dummy)
{ return ((WORD) *((float *)start + pos)); }

LONG getsb_lev (BYTE *start, ULONG pos, UBYTE chan)
{ return (*(start + (pos <<1) +chan) <<8); }

LONG getsw_lev (BYTE *start, ULONG pos, UBYTE chan)
{ return (*((WORD *)start + (pos <<1) +chan)); }

LONG getsl_lev (BYTE *start, ULONG pos, UBYTE chan)
{ return ((WORD) *((float *)start + (pos <<1) +chan)); }

LONG getnon_lev (BYTE *start, ULONG pos, UBYTE dummy)
{ return (0); }

#endif  /*NO_DATA_HELP*/
