Copyright (c) Hyperion Entertainment and contributors.

Programming AmigaOS 4: Datatypes - Making Life Easy

From AmigaOS Documentation Wiki
Revision as of 01:14, 25 August 2017 by Tony Wyatt (talk | contribs) (Fixed typos in paths, para under heading "DataTypes Internally")
(diff) ← Older revision | Latest revision (diff) | Newer revision → (diff)
Jump to navigation Jump to search

This article was adapted from Amiga Future magazine's series on developing for AmigaOS....

Although the Datatypes haven't really been changed or reworked in AmigaOS 4, they should however still be he given some attention in the context of this workshop, because they can make the life of the programmer significantly easier.

Datatypes are an ingenious concept, unlike anything found on other platforms in this form. The specific program requires a file of a certain datatype (such as; images, sound, text) and the operating system automatically takes care of changing the format to the required one on the harddrive, at least when there is an interpreter available.

The main groups that exist are:

GID_SYSTEM (syst) System-Files
GID_TEXT (text) ASCII-Text
GID_DOCUMENT (docu) Document (Text and Graphics)
GID_SOUND (soun) Sound/Sample
GID_INSTRUMENT (inst) Music instrument
GID_MUSIC (musi) Music
GID_PICTURE (pict) Single Image
GID_ANIMATION (anim) multiple Images/Animation
GID_MOVIE (movi) Animation with graphics and Sound

In the application there should ideally be a rough preliminary check, because in a painting program there will surely be nothing to show for a music file. A conversion of sound into note signs doesn't exist yet, but could be an interesting application. :-)

The first step

The simplest function available in the datatype.library is determining of the datatype and respectively the datatype concerning the file. The function IDataTypes->ObtainDataType() expects a Taglist, which in the simplest of cases should only contain the name of the file.

  if((lock = IDOS->Lock("dateiname",SHARED_LOCK)))
  {
    if((dtn = IDataTypes->ObtainDataType(DTST_FILE, (APTR) lock, TAG_END)))
    {
      ...
      IDataTypes->ReleaseDataType(dtn);
    }
    IDOS->UnLock(lock);
  }

So, that leads us right into the first example program "GetFileType.c", which can be given a file and returns the format type as a result. In case AmigaOS can't do anything with this file it returns "Object not found".

/* GetFileType.c
 *
 * gcc GetFileType.c -o GetFileType -l auto
 */
 
/******************************* INCLUDES *************************************/
 
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/datatypes.h>
#include <proto/iffparse.h>
 
/******************************************************************************/
 
uint32 GetTyp(STRPTR filename)
{
  uint32 typ = 0;
  BPTR lock;
  struct DataType *dtn;
  uint8 buffer[5];
 
  if((lock = IDOS->Lock(filename, SHARED_LOCK)))
  {
    if((dtn = IDataTypes->ObtainDataType(DTST_FILE, (APTR) lock, TAG_END)))
    {
      const struct DataTypeHeader *dth = dtn->dtn_Header;
 
      IDOS->Printf("   Filename: %s\n", filename);
      IDOS->Printf("Description: %s\n", dth->dth_Name);
      IDOS->Printf("  Base Name: %s\n", dth->dth_BaseName);
      IDOS->Printf("       Type: %s\n", IDataTypes->GetDTString((dth->dth_Flags & DTF_TYPE_MASK) + DTMSG_TYPE_OFFSET));
      IDOS->Printf("      Group: %s\n", IDataTypes->GetDTString(dth->dth_GroupID));
      IDOS->Printf("         ID: %s\n", IIFFParse->IDtoStr(dth->dth_ID,buffer));
 
      typ = dth->dth_GroupID;
 
      IDataTypes->ReleaseDataType(dtn);
    }
    else IDOS->PrintFault(IDOS->IoErr(), filename);
    IDOS->UnLock(lock);
  }
  else IDOS->PrintFault(IDOS->IoErr(), filename);
 
  return typ;
}
 
/******************************************************************************/
 
int main(int argc, char *argv[])
{
  if(argc >= 2) GetTyp(argv[1]);
 
  return 0;
}
AF109 getfiletype grab.png

Here with the file!

In order to actually get the file we also have to use another function with the name IDataTypes->NewDTObject(). This can even be given the name of the file directly (including path), as well as other parameters with Taglist. In case of an error it returns NULL, otherwise an object which have to be released again at the end with IDataTypes->DisposeDTObject().

  if((dtobj = IDataTypes->NewDTObject("sample",
                                     DTA_SourceType,  DTST_FILE,
                                     DTA_GroupID,     GID_SOUND,
                                     TAG_END)))
  {
    ...
    IDataTypes->DisposeDTObject(dtobj);
  }

Merely displaying the file is something which can be handled by the Datatype as well. AddDTObject(), RefreshDTObjects() and RemoveDTObject() take care of this task. For playing back however the Play-Method must be executed:

IIntuition->IDoMethod(dt,DTM_TRIGGER,NULL,STM_PLAY,NULL);

Usually however you will want to process the file in your own application. In that case IDataTypes->GetDTAttrs(), with which you can request the values, image or sound data for example, will help you along. This time there are two examples concluding this theme: with "ShowPic.c" you can display an image and with "PlaySample.c" you can play back a sample file.

ShowPic Example

/* ShowPic.c
 *
 * gcc ShowPic.c -o ShowPic -l auto
 */
 
/******************************* INCLUDES *************************************/
 
#include <exec/types.h>
#include <dos/dos.h>
#include <dos/rdargs.h>
#include <datatypes/datatypes.h>
#include <datatypes/datatypesclass.h>
#include <datatypes/pictureclass.h>
#include <intuition/intuition.h>
#include <intuition/icclass.h>
 
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/datatypes.h>
#include <proto/graphics.h>
#include <proto/intuition.h>
#include <proto/utility.h>
 
/*****************************************************************************/
 
#define OPT_NAME 0
 
/*****************************************************************************/
 
void PrintErrorMsg (ULONG errnum, STRPTR name)
{
  TEXT errbuff[80];
 
  if(errnum >= DTERROR_UNKNOWN_DATATYPE)
  {
    /* Datatypes eigener Fehlercode auswerten */
    IUtility->SNPrintf(errbuff,sizeof(errbuff),IDataTypes->GetDTString(errnum),name);
  }
  else
  {
    /* allgemeinen Fehlertext eintragen */
    IDOS->Fault(errnum,NULL,errbuff,sizeof(errbuff));
  }
 
  IDOS->Printf("%s\nerror #%ld\n",errbuff,errnum);
}
 
/*****************************************************************************/
 
int main(int argc, char *argv[])
{
  uint32 options[1] = {0};
  struct RDArgs *rdargs;
 
  if((rdargs = IDOS->ReadArgs("NAME/A",(LONG *)options,NULL)))
  {
    Object *dto;
 
    /* Datatypes Objekt ermitteln */
    if((dto = IDataTypes->NewDTObject((APTR)options[OPT_NAME],
                                      DTA_SourceType, DTST_FILE,
                                      DTA_GroupID,    GID_PICTURE,
                                      PDTA_DestMode,  PMODE_V43,   /* Wichtig: 43er Modus aktivieren f¸r 24 Bit Verarbeitung */
                                      TAG_END)))
    {
      ULONG nomwidth, nomheight;
 
      /* Informationen ¸ber das Objekt erfragen */
      if((IDataTypes->GetDTAttrs(dto,
            DTA_NominalHoriz, &nomwidth,
            DTA_NominalVert,  &nomheight,
            TAG_END)))
      {
        /* Anzeigen der Datei und dessen Grˆfle */
        IDOS->Printf("%s %ld x %ld pixels\n",
          options[OPT_NAME], nomwidth, nomheight);
      }
 
      /* Umgebungsdaten verarbeiten */
      struct dtFrameBox dtf = {0};
      struct FrameInfo  fri = {0};
      dtf.MethodID          = DTM_FRAMEBOX;
      dtf.dtf_FrameInfo     = &fri;
      dtf.dtf_ContentsInfo  = &fri;
      dtf.dtf_SizeFrameInfo = sizeof(struct FrameInfo);
      if(!(IDataTypes->DoDTMethodA(dto,NULL,NULL,(Msg)&dtf)))
        IDOS->Printf("could not obtain environment information\n");
 
      /* sicherstellen, dass eine Grˆfle vorhanden ist */
      nomwidth  = ((nomwidth) ? nomwidth : 600);
      nomheight = ((nomheight) ? nomheight : 200);
 
      /* Fenster ˆffnen */
      struct Window *win;
      if((win = IIntuition->OpenWindowTags(NULL,
                 WA_InnerWidth,    nomwidth,
                 WA_InnerHeight,   nomheight,
                 WA_Title,         options[OPT_NAME],
                 WA_IDCMP,         IDCMP_CLOSEWINDOW | IDCMP_VANILLAKEY | IDCMP_IDCMPUPDATE,
                 WA_DragBar,       TRUE,
                 WA_DepthGadget,   TRUE,
                 WA_CloseGadget,   TRUE,
                 WA_AutoAdjust,    TRUE,
                 WA_SimpleRefresh, TRUE,
                 WA_BusyPointer,   TRUE,
                 WA_Activate,      TRUE,
                 TAG_END)))
      {
        /* Die Grˆfle f¸r das Datatypes Objekt setzen */
        IDataTypes->SetDTAttrs(dto,NULL,NULL,
            GA_Left,    win->BorderLeft,
            GA_Top,     win->BorderTop,
            GA_Width,   win->Width - win->BorderLeft - win->BorderRight,
            GA_Height,  win->Height - win->BorderTop - win->BorderBottom,
            ICA_TARGET, ICTARGET_IDCMP,
            TAG_END);
 
        /* Das Datatype Objekt ins Fenster einh‰ngen */
        IDataTypes->AddDTObject(win,NULL,dto,-1);
 
        /* einen Refresh des Datatypes Objekts auslˆsen, */
        /* damit es im Programmkontext gezeichnet wird */
        IDataTypes->RefreshDTObjects(dto,win,NULL,NULL);
 
        BOOL going = TRUE;
        while(going)
        {
          /* warten auf ein Ereignis */
          ULONG signal = IExec->Wait((1 << win->UserPort->mp_SigBit) | SIGBREAKF_CTRL_C);
 
          /* bei Benutzerabbruch das Programm beenden */
          if(signal & SIGBREAKF_CTRL_C) going = FALSE;
 
          /* alle Nachrichten von Intuition verarbeiten */
          struct IntuiMessage *imsg;
          while((imsg = (struct IntuiMessage *) IExec->GetMsg(win->UserPort)))
          {
            struct TagItem *tstate, *tag, *tags;
            ULONG tidata;
 
            /* die einzelnen Nachrichten verarbeiten */
            switch(imsg->Class)
            {
              case IDCMP_CLOSEWINDOW:
                   going = FALSE;
                   break;
 
              case IDCMP_VANILLAKEY:
                   if(imsg->Code == 27 /* ESC-Taste */) going = FALSE;
                   break;
 
              case IDCMP_IDCMPUPDATE:
                   tstate = tags = (struct TagItem *) imsg->IAddress;
                   while(tag = IUtility->NextTagItem (&tstate))
                   {
                     tidata = tag->ti_Data;
                     switch(tag->ti_Tag)
                     {
                       /* Wartezeiger anzeigen bzw. wegnehmen */
                       case DTA_Busy:
                            if(tidata)
                              IIntuition->SetWindowPointer(win,WA_BusyPointer,TRUE,TAG_END);
                            else
                              IIntuition->SetWindowPointer(win,WA_Pointer,NULL,TAG_END);
                            break;
 
                       /* Fehlerbehandlung */
                       case DTA_ErrorLevel:
                            if(tidata)
                            {
                              const ULONG errnum = IUtility->GetTagData(DTA_ErrorNumber,0,tags);
                              PrintErrorMsg(errnum,(STRPTR) options[OPT_NAME]);
                            }
                            break;
 
                       /* Refresh des Datatype-Objekts ausf¸hren */
                       case DTA_Sync:
                            IDataTypes->RefreshDTObjects(dto,win,NULL,NULL);
                            break;
                     }
              }
              break;
            }
 
            /* verarbeitete Nachrichten zur¸ckschicken */
            IExec->ReplyMsg((struct Message *)imsg);
          }
        }
 
        /* Objekt wieder aus dem Fenster entfernen */
        IDataTypes->RemoveDTObject(win,dto);
 
        /* Fenster schlieflen */
        IIntuition->CloseWindow(win);
      }
      else IDOS->Printf("could not open window\n");
 
      /* Datatype Objekt freigeben */
      IDataTypes->DisposeDTObject(dto);
    }
    else PrintErrorMsg(IDOS->IoErr(),(STRPTR)options[OPT_NAME]);
 
    IDOS->FreeArgs(rdargs);
  } else PrintErrorMsg(IDOS->IoErr(),NULL);
 
  return 0;
}
AF109 showpic screenshot.png
AF109 showpic result.png

PlaySample Example

/* PlaySample.c
 *
 * gcc PlaySample.c -o PlaySample -l auto
 */
 
#define __USE_BASETYPE__
 
/******************************* INCLUDES *************************************/
 
#include <exec/types.h>
#include <datatypes/soundclass.h>
 
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/datatypes.h>
#include <proto/intuition.h>
 
/******************************************************************************/
 
extern struct Library        *DataTypesBase;
extern struct DataTypesIFace *IDataTypes;
extern struct IntuitionBase  *IntuitionBase;
extern struct IntuitionIFace *IIntuition;
 
/******************************************************************************/
 
int main(int argc, char *argv[])
{
  if(argc >= 2)
  {
    Object	*dt;
 
    /* den Sample via Datatype laden */
    if((dt = IDataTypes->NewDTObject(argv[1],
              DTA_GroupID,        GID_SOUND,
              SDTA_SignalTask,    IExec->FindTask(NULL),
              SDTA_SignalBitMask, SIGBREAKF_CTRL_C,
              TAG_END)))
    {
      uint32 freq, per, len;
 
      /* verschiedene Daten des Samples erfragen */
      IDataTypes->GetDTAttrs(dt,
              SDTA_SamplesPerSec, &freq,
              SDTA_Period,        &per,
              SDTA_SampleLength,  &len,
              TAG_END);
 
      IDOS->Printf("Freq: %ld, Period: %ld, Len: %ld\n", freq, per, len);
 
      /* einmal abspielen */
      IIntuition->IDoMethod(dt,DTM_TRIGGER,NULL,STM_PLAY,NULL);
 
      /* Signal wenn Sample fertig abgespielt oder Benutzerabbruch */
      IExec->Wait(SIGBREAKF_CTRL_C);
 
      /* Datatype freigeben */
      IDataTypes->DisposeDTObject(dt);
    }
    else IDOS->PrintFault(IDOS->IoErr(),argv[1]);
 
    return 0;
  }
  else return 20;
}
AF109 playsample screenshot.png

Scaling Image files

Apropos, there is also an option to scale the requested image file straight away, in order to fit it into a window for example. So, in contrast to IGraphics->ScaleBitMap() you can also directly give it the desired height and width. The only disadvantage is that you can only scale the bitmap once before layout (meaning before the method DTM_PROCLAYOUT). The quality of scaling is determined with the tag PDTA_ScaleQuality where 0 if for fast/low quality and 1 for high/slow quality.

  Object *obj;
  if((obj = IDataTypes->NewDTObject(filename,
                DTA_SourceType,    DTST_FILE,
                DTA_GroupID,       GID_PICTURE,
                PDTA_ScaleQuality, 1,
                TAG_END)))
  ...
  struct pdtScale pdt;
  pdt.MethodID     = PDTM_SCALE;
  pdt.ps_NewWidth  = width;
  pdt.ps_NewHeight = height;
  pdt.ps_Flags     = 0;
  if(!(IDataTypes->DoDTMethodA(obj, (Msg)&pdt)))
    IDOS->Printf("Datatype Skalierung fehlgeschlagen

The accompanying example program "ScalePic.c" displays the image when it is started in its original size. However, when you change the window size the image will be scaled up or down to fit.

/* ScalePic.c
 *
 * gcc ScalePic.c -o ScalePic -lauto
 */
 
/******************************* INCLUDES *************************************/
 
#define __USE_INLINE__
#define __USE_BASETYPE__
 
#include <exec/types.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <datatypes/datatypes.h>
#include <datatypes/datatypesclass.h>
#include <datatypes/pictureclass.h>
#include <graphics/gfx.h>
#include <graphics/scale.h>
#include <graphics/displayinfo.h>
#include <intuition/intuition.h>
#include <intuition/intuitionbase.h>
 
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/datatypes.h>
 
/************************ VARIABLEN DEKLARATIONEN *****************************/
 
struct IntuitionBase *IntuitionBase;
struct GfxBase       *GfxBase;
struct Library       *DataTypesBase;
 
Object               *dt_Obj;
struct BitMap        *dt_Bitmap;
struct Screen        *dt_Scr;
struct Window        *dt_Win;
 
/******************************************************************************/
 
BOOL ErmittleGfxSize(const UBYTE *filename, UWORD *width, UWORD *height)
{
  struct BitMapHeader *bmhd;
  Object              *obj;
 
  if((obj = NewDTObject((APTR)filename,
                           DTA_SourceType, DTST_FILE,
                           DTA_GroupID,    GID_PICTURE,
                           TAG_END)))
  {
    if(GetDTAttrs(obj,
                  PDTA_BitMapHeader, &bmhd,
                  TAG_END))
    {
      *width  = bmhd->bmh_Width;
      *height = bmhd->bmh_Height;
    }
    else Printf("Kann Datei nicht laden !\n");
 
    DisposeDTObject(obj);
  }
 
  return width && height ? TRUE : FALSE;
}
 
/*****************************************************************************/
 
LONG Load(const UBYTE *filename, ULONG scalequality, UWORD width, UWORD height)
{
  struct FrameInfo  fri = {0};
  struct dtFrameBox dtf = {0};
  struct gpLayout   gpl;
  struct pdtScale   pdt;
 
  SetWindowPointer(dt_Win,WA_BusyPointer,TRUE,TAG_END);
 
  /* Bilddatei mittels Datatype laden */
  if((dt_Obj = NewDTObject((APTR) filename,
         DTA_SourceType,        DTST_FILE,
         DTA_GroupID,           GID_PICTURE,  /* Datei mufl eine Bilddatei sein */
         PDTA_DestMode,         PMODE_V43,    /* f¸r 24 Bit Verarbeitung */
         PDTA_Remap,            TRUE,         /* Farben an Bildschirm anpassen */
         PDTA_Screen,           dt_Scr,       /* diesen Bildschirm dazu verwenden */
         PDTA_FreeSourceBitMap, TRUE,         /* Source-Bitmap nach Bearbeitung sofort freigeben */
         PDTA_ScaleQuality,     scalequality, /* 0=schnell, 1=langsam aber gute Qualit‰t */
         TAG_END)))
  {
    /* Umgebung ermitteln */
    dtf.MethodID          = DTM_FRAMEBOX;
    dtf.dtf_FrameInfo     = &fri;
    dtf.dtf_ContentsInfo  = &fri;
    dtf.dtf_SizeFrameInfo = sizeof(struct FrameInfo);
    if(IDoMethodA(dt_Obj,(Msg)&dtf) && fri.fri_Dimensions.Depth)
    {
      /* Bild auf diese Grˆfle skalieren */
      pdt.MethodID     = PDTM_SCALE;
      pdt.ps_NewWidth  = width;
      pdt.ps_NewHeight = height;
      pdt.ps_Flags     = 0;
      if(!(IDoMethodA(dt_Obj,(Msg)&pdt)))
        Printf("Datatype Skalierung fehlgeschlagen\n");
 
      /* das Bild noch layouten */
      gpl.MethodID    = DTM_PROCLAYOUT;
      gpl.gpl_GInfo   = NULL;
      gpl.gpl_Initial = 1;
      if(IDoMethodA(dt_Obj,(Msg)&gpl))
      {
        /* angepasste Bitmap erfragen */
        GetDTAttrs(dt_Obj,
                   PDTA_DestBitMap, &dt_Bitmap,
                   TAG_END);
 
        if(dt_Bitmap)
        {
        }
        else Printf("Keine Bitmap ?\n");
      }
      else Printf("Kann Bild nicht layouten\n");
    }
    else Printf("Kann Umgebungsdaten nicht ermitteln\n");
  }
  else Printf("Kein passender Datentyp ?\n");
 
  SetWindowPointer(dt_Win,WA_Pointer,NULL,TAG_END);
 
  return dt_Bitmap ? TRUE : FALSE;
}
 
/*****************************************************************************/
 
void Unload()
{
  if(dt_Obj) DisposeDTObject(dt_Obj); dt_Obj = NULL;
}
 
/*****************************************************************************/
 
void Display(UWORD width, UWORD height)
{
  BltBitMapRastPort(dt_Bitmap,0,0,
                    dt_Win->RPort,dt_Win->BorderLeft,dt_Win->BorderTop,
                    width,height, MINTERM_ABC | MINTERM_ABNC);
}
 
/*****************************************************************************/
 
int main(int argc, char *argv[])
{
  ULONG options[2] = {0};
  struct RDArgs *rdargs;
 
  if((rdargs = ReadArgs("NAME/A,SQ=SCALEQUALITY/N",(LONG *)options,NULL)))
  {
    const UBYTE *filename = (UBYTE*) options[0];
    /* wird keine Skalierungsqualit‰t angegeben ,dann die gute verwenden */
    const ULONG scalequality = (options[1] ? *((ULONG*)options[1]) : 1);
 
    IntuitionBase = (struct IntuitionBase *) OpenLibrary("intuition.library", 50);
    GfxBase       = (struct GfxBase *) OpenLibrary("graphics.library", 50);
    DataTypesBase = OpenLibrary("datatypes.library", 50);
 
    if(IntuitionBase && GfxBase && DataTypesBase)
    {
      dt_Scr = IntuitionBase->ActiveScreen;
 
      UWORD width=0, height=0;
      if(ErmittleGfxSize(filename,&width,&height))
      {
        if((dt_Win = OpenWindowTags(NULL,
                                    WA_Left,            20,
                                    WA_Top,             20,
                                    WA_InnerWidth,      width, /* Standard-Grˆfle */
                                    WA_InnerHeight,     height,
                                    WA_MinWidth,        60,  /* Minimalgrˆfle */
                                    WA_MinHeight,       10,
                                    WA_MaxWidth,        dt_Scr->Width * 2,  /* Maximalgrˆfle */
                                    WA_MaxHeight,       dt_Scr->Height * 2,
                                    WA_DragBar,         TRUE,
                                    WA_Activate,        TRUE,
                                    WA_CloseGadget,     TRUE,
                                    WA_SizeGadget,      TRUE,
                                    WA_SmartRefresh,    TRUE,
                                    WA_NoCareRefresh,   TRUE,
                                    WA_RMBTrap,         TRUE,
                                    WA_SizeBBottom,     TRUE,
                                    WA_IDCMP,           IDCMP_CLOSEWINDOW | IDCMP_NEWSIZE | IDCMP_RAWKEY | IDCMP_MOUSEBUTTONS,
                                    WA_Title,           "Skalierbarer Bildanzeiger",
                                    TAG_END)))
        {
          BOOL laufen = TRUE;
          while(laufen)
          {
            Load(filename,scalequality,width,height);  /* Bilddatei laden */
            Display(width,height);                     /* und anzeigen */
 
            /*
            ** normaler Nachrichtenloop, bis das Fenster geschlossen wird,
            ** oder CTRL-C an das Programm gesendet wird.
            */
            BOOL msglaufen = TRUE;
            while(msglaufen)
            {
              struct IntuiMessage *msg;
              const ULONG sigs = Wait(1L << dt_Win->UserPort->mp_SigBit | SIGBREAKF_CTRL_C);
 
              while((msg = (struct IntuiMessage *) GetMsg(dt_Win->UserPort)))
              {
                if(msg->Class == IDCMP_CLOSEWINDOW)
                {
                  msglaufen = laufen = FALSE;
                }
                else if(msg->Class == IDCMP_NEWSIZE)
                {
                  msglaufen = FALSE;
                  width = dt_Win->Width - dt_Win->BorderLeft - dt_Win->BorderRight;
                  height = dt_Win->Height - dt_Win->BorderTop - dt_Win->BorderBottom;
                }
                else if(msg->Class == IDCMP_RAWKEY)
                {
                }
 
                ReplyMsg((struct Message *)msg);
              }
 
              if(sigs & SIGBREAKF_CTRL_C)
              {
                msglaufen = laufen = FALSE;
              }
            }
          }
          CloseWindow(dt_Win);
        }
        else Printf("÷ffnen des Fensters fehlgeschlagen.\n");
      }
      else Printf("Bild '%s' kann nicht geladen werden\n",filename);
 
      Unload();
    }
    else Printf("Libraries fehlen.\n");  
 
    CloseLibrary(DataTypesBase);
    CloseLibrary((struct Library *)GfxBase);
    CloseLibrary((struct Library *)IntuitionBase);
 
    FreeArgs(rdargs);
  }
  else PrintFault(IoErr(),"ScalePic");
 
  return 0;
}

Scaling quality comparison in 4x enlargement for comparison: On the left level 1 with soft edges. On the right with level one the jagged edges become especially obvious on diagonal edges.

AF109 skalierungsqualitaet.png

AHI for sound

The sound.datatype takes care of playing back the sample using AHI (Audio Hardware Interface) and not use the old Paula chip for that anymore. Although there is nothing against playing the sample directly from Datatype, it gives you more flexibility when you use the ahi.device directly for this. Also, because it is not all that complicated, we would like to give you an additional short example. As usual a MsgPort and for the conversion an IORequest (AHIRequest here) are required in order to open the ahi.device. The AHIRequest can theoretically be copied multiple times as well when multiple samples should be played (simultaneously). After charging AHIRequest with the sample file the asynchronous playback is done with an IExec-SendIO(), (IExec->DoIO() would result in synchronous playback). The function returns immediately while the request is played back in the background. When this is completed (corresponding to completed playback) we receive the matching message on the message port. At the end of the program the device has to be closed and the MsgPort and Request have to be released. The program frame therefore should look like this:

  struct Library    *AHIBase;
  struct MsgPort    *AHImp;
  struct AHIRequest *AHIio;
 
  if((AHImp = IExec->CreateMsgPort()))
  {
    if((AHIio = (struct AHIRequest *) IExec->CreateIORequest(AHImp,sizeof(struct AHIRequest))))
    {
      AHIio->ahir_Version = 4;
      if(!(IExec->OpenDevice(AHINAME, 0,(struct IORequest *) AHIio,NULL)))
      {
        AHIBase = (struct Library *) AHIio->ahir_Std.io_Device;
        ...
        IExec->CloseDevice((struct IORequest *)AHIio);
      }
      IExec->DeleteIORequest((struct IORequest *)AHIio);
    }
    IExec->DeleteMsgPort(AHImp);
  }

Connecting sound.datatype and ahi.device

The whole thing becomes truly elegant when the sound files are loaded using datatypes and the ahi.device is used for coordinated playback. In principle the rule is again to use the already discussed method: open ahi.device and datatypes.library and use IDataTypes->NewDataType() to identify the files. With the help of that we now fill the AHIRequest. For that we need to ascertain a few values from Datatype. Especially the address of the file and the playback frequency.

    BYTE *gb_Data; LONG gb_Length, gb_Freq;
    IDataTypes->GetDTAttrs(gb_Obj,
                 SDTA_Sample,        &gb_Data,
                 SDTA_SampleLength,  &gb_Length,
                 SDTA_SamplesPerSec, &gb_Freq,
                 TAG_END);

The thing is reproduced with IExec->SendIO() and with IExec->Wait() it waits until playback is completed.

  AHIio->ahir_Std.io_Command  = CMD_WRITE;
  ...
  IExec->SendIO((struct IORequest *) AHIio);
  IExec->Wait(1L << AHImp->mp_SigBit);

It is all shown together once more in the example program "PlayAHI.c".

/* PlayAHI.c
 *
 * gcc PlayAHI.c -o PlayAHI -l auto
 */
 
/******************************* INCLUDES *************************************/
 
#define __USE_BASETYPE__
 
#include <devices/ahi.h>
#include <dos/dosasl.h>
#include <exec/memory.h>
#include <intuition/classusr.h>
#include <datatypes/soundclass.h>
 
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/ahi.h>
#include <proto/intuition.h>
#include <proto/datatypes.h>
 
/************************ VARIABLEN DEKLARATIONEN *****************************/
 
struct Library        *AHIBase;
struct Library        *DataTypesBase;
struct DataTypesIFace *IDataTypes;
struct MsgPort        *AHImp;
struct AHIRequest     *AHIio;
 
BYTE   *gb_Data;
LONG    gb_Freq;
LONG    gb_Length;
Object *gb_Obj;
 
/******************************************************************************/
 
BOOL OpenAHI(void)
{
  if((AHImp = IExec->AllocSysObjectTags( ASOT_PORT, TAG_END )))
  {
 
    if((AHIio = (struct AHIRequest *) IExec->AllocSysObjectTags( ASOT_IOREQUEST,
                                          ASOIOR_Size,      sizeof( struct AHIRequest ),
                                          ASOIOR_ReplyPort, AHImp,
                                          TAG_END )))
    {
      AHIio->ahir_Version = 6;
      if(!(IExec->OpenDevice(AHINAME, 0,(struct IORequest *) AHIio, 0)))
      {
        AHIBase = (struct Library *) AHIio->ahir_Std.io_Device;
      }
    }
  }
 
  return AHIBase ? TRUE : FALSE;
}
 
/******************************************************************************/
 
void CloseAHI(void)
{
  if(AHIBase) IExec->CloseDevice((struct IORequest *)AHIio); AHIBase = NULL;
  if(AHIio) IExec->FreeSysObject(ASOT_IOREQUEST,AHIio); AHIio = NULL;
  if(AHImp) IExec->FreeSysObject(ASOT_PORT,AHImp); AHImp = NULL;
}
 
/******************************************************************************/
 
BOOL LoadSample(STRPTR filename)
{
  if((gb_Obj = IDataTypes->NewDTObject((APTR)filename,
                       DTA_SourceType,  DTST_FILE,
                       DTA_GroupID,     GID_SOUND,
                       TAG_END)))
  {
    IDataTypes->GetDTAttrs(gb_Obj,
               SDTA_Sample,        &gb_Data,
               SDTA_SampleLength,  &gb_Length,
               SDTA_SamplesPerSec, &gb_Freq,
               TAG_END);
  }
 
  if(gb_Data != 0 && gb_Length > 0 && gb_Freq != 0 && gb_Obj != NULL)
    return TRUE;
  else
    return FALSE;
}
 
/******************************************************************************/
 
void UnloadSample(void)
{
  if(gb_Obj) IDataTypes->DisposeDTObject(gb_Obj); gb_Obj = NULL;
}
 
/******************************************************************************/
 
void Play(void)
{
  IDOS->Printf("playing %ld bytes at %ld hz\n", gb_Length, gb_Freq);
 
  AHIio->ahir_Std.io_Message.mn_Node.ln_Pri = 5;
  AHIio->ahir_Std.io_Command  = CMD_WRITE;
  AHIio->ahir_Std.io_Data     = gb_Data;
  AHIio->ahir_Std.io_Length   = gb_Length;
  AHIio->ahir_Std.io_Offset   = 0;
  AHIio->ahir_Frequency       = gb_Freq;
  AHIio->ahir_Type            = AHIST_M8S;
  AHIio->ahir_Volume          = 0x10000;
  AHIio->ahir_Position        = 0x8000;
  AHIio->ahir_Link            = NULL;
 
  IExec->SendIO((struct IORequest *) AHIio);
}
 
/******************************************************************************/
 
void StopPlay(void)
{
  if(! IExec->CheckIO((struct IORequest *) AHIio))
  {
    IExec->AbortIO((struct IORequest *) AHIio);
    IExec->WaitIO((struct IORequest *) AHIio);
  }
}
 
/******************************************************************************/
 
int main(int argc, char *argv[])
{
  if((DataTypesBase = IExec->OpenLibrary("datatypes.library", 50)))
  {
    if((IDataTypes = (struct DataTypesIFace *) IExec->GetInterface(DataTypesBase,"main",1,NULL)))
    {
      if(OpenAHI())
      {
        if(LoadSample(argv[1]))
        {
          Play();
 
          const LONG signal = IExec->Wait(SIGBREAKF_CTRL_C | (1L << AHImp->mp_SigBit));
          if(signal & SIGBREAKF_CTRL_C) IDOS->Printf("*** break\n");
 
          StopPlay();
        }
        else IDOS->Printf("Error load sample\n");
 
        UnloadSample();
      }
      else IDOS->Printf("Error open ahi.device\n");
 
      CloseAHI();
 
      IExec->DropInterface((struct Interface *)IDataTypes);
    }
    else IDOS->Printf("Error get datatypes interface\n");
 
    IExec->CloseLibrary(DataTypesBase);
  }
  else IDOS->Printf("Error open datatypes.library V50\n");
 
  return 0;
}
AF109 playahi.png

Datatypes internally

Technically speaking the Datatypes are realised as Shared-Library and stored as single files in the directory "SYS:Classes/Datatypes" with the #?.datatype extension. Whether a datatype is active as well is stored with a second file in the directory "SYS:Devs/DataTypes". These files are already automatically activated at the start of Workbench. Inactive descriptors on the other hand are found in "SYS:Storage/DataTypes" and can additionally be started as well when needed by double clicking the icon.

In perspective

In our previous edition we already pointed out the new functions of intuition.library, with which it is even easier to load and use image files from harddisk, without having to do the single steps of datatypes.library. We are talking about IIntuition->ObtainBitMapSource(), IIntuition->ObtainBitMapInstance() and IIntuition->BitMapInstanceControl(). We would like to give you a quick reminder of them in view of this topic.

The other formats, like animation or text, are handled in a similar way. The most important are surely graphics and sounds, which we presented in detail.

Important tags

Here is an overview of the most important Datatype-Tags, to be able to query the files with IDataTypes->GetDTAttrs().

picture.datatype:

PDTA_BitMap          struct BitMap *  BitMap with the graphic data
PDTA_DestBitMap      struct BitMap *  graphics data adapted to screen colours
PDTA_ColorTable      ULONG *          Colour table
PDTA_NumColors       UWORD            Number of colours used in the image
PDTA_BitMapHeader    struct BitMapHeader * Graphic-Information (Size etc.)

sound.datatype:

SDTA_SamplesPerSec   UWORD            Playback speed
SDTA_Period          UWORD            Playback speed (alternative)
SDTA_Sample          UWORD *          Sound-File to play 
SDTA_SampleLength    ULONG            Lenght of the sound file
SDTA_VoiceHeader     struct VoiceHeader * Sample-Information

text.datatype:

TDTA_Buffer          STRPTR           Text-File
TDTA_BufferLen       ULONG            Length of the text files

animation.datatype:

ADTA_Width           ULONG            Width in Pixels
ADTA_Height          ULONG            Height in Pixels
ADTA_Depth           ULONG            Colour depth
ADTA_Frames          ULONG            Number of frames in the Animation
ADTA_FramesPerSecond ULONG      Playback speed
ADTA_Sample          UWORD *         Sound-file of the Animation

Authors

Written by Michael Christoph
Translation by Richard Mulder.
Copyright (c) 2013 Michael Christoph