home *** CD-ROM | disk | FTP | other *** search
- /* This is a quickly made example of how you can use
- AHI to record AIFF/AIFC samples directly to hard disk.
- Like always, it's pooly documentated and quite messy... Sorry.
- This source and its executable is Public Domain.
- Feel free to base any own work on it.
- Done by Martin Blom in 1996.
-
- Notes: Keep your hands away from the mouse and keyboard while the program
- is recording. If the countdown gadget cannot be updated, the program will
- stall and you'll miss samples.
-
- Yeah, yeah, I know the program suck. It's just an example.
-
- */
-
- #define NO_PROTOS
- #define NO_SAS_PRAGMAS
- #include <iffp/8svx.h>
- #undef NO_PROTOS
- #undef NO_SAS_PRAGMAS
-
- #include <exec/lists.h>
- #include <exec/memory.h>
- #include <dos/dos.h>
- #include <devices/ahi.h>
- #include <intuition/intuition.h>
- #include <intuition/gadgetclass.h>
- #include <libraries/asl.h>
- #include <libraries/gadtools.h>
- #include <proto/ahi.h>
- #include <proto/asl.h>
- #include <proto/exec.h>
- #include <proto/dos.h>
- #include <proto/intuition.h>
- #include <proto/gadtools.h>
-
- #include <stdio.h>
- #include <math.h>
- #include "GUI.h"
- #include "GUI.extras.h"
-
- #include "HardDiskRecord.h"
-
- #define VERSION_STRING {"\0$VER: HardDiskRecord 1.1 "__AMIGADATE__"\r\n"}
- static char VerString[] = VERSION_STRING;
-
- //extern void KPrintF(char *fmt,...);
-
- struct Library *AHIBase;
- struct MsgPort *AHImp=NULL;
- struct AHIRequest *AHIio=NULL;
- BYTE AHIDevice=-1;
- struct AHIAudioModeRequester *amreq=NULL;
- struct FileRequester *filereq=NULL;
- struct AHIAudioCtrl *actrl=NULL;
-
- struct EasyStruct req = {
- sizeof (struct EasyStruct),
- 0,
- NULL,
- NULL,
- NULL,
- };
-
- char directory[1024],filename[108],fullname[1024];
- BPTR file=NULL;
-
- #define STRINGNODE_ID 100
- #define STRINGNODE_LENGTH 32
- struct StringNode
- {
- struct Node sn_Node;
- char sn_String[STRINGNODE_LENGTH];
- };
- struct List InputList,OutputList;
- char amtext1[32],amtext2[32];
-
- ULONG audioid=AHI_INVALID_ID,recfreq=0;
- ULONG source=0,dest=0,vol=0,gain=100,duration=0;
- ULONG type=1; //type: 0 = AIFF, 1 = AIFC
- ULONG stereo=0; //stereo: 0=no, 1 = yes
- ULONG samplesize=0;
-
- struct TagItem filtertags[]=
- {
- AHIDB_Record,TRUE,
- TAG_DONE
- };
-
- struct {
- ULONG FORMid;
- ULONG FORMsize;
- ULONG AIFCid;
-
- ULONG FVERid;
- ULONG FVERsize;
- FormatVersionHeader FVERchunk;
-
- ULONG COMMid;
- ULONG COMMsize;
- ExtCommonChunk COMMchunk;
-
- ULONG SSNDid;
- ULONG SSNDsize;
- SampledSoundHeader SSNDchunk;
- } AIFCheader =
- { // All NULLs will be filled later.
- ID_FORM,NULL,ID_AIFC,
- ID_FVER,sizeof(FormatVersionHeader),{AIFCVersion1},
- ID_COMM,sizeof(ExtCommonChunk),{NULL,NULL,16,{NULL},NO_COMPRESSION,
- sizeof("not compressed")-1,'n','o','t',' ','c','o','m','p','r','e','s','s','e','d'},
- ID_SSND,NULL,{0,0}
- };
- struct {
- ULONG FORMid;
- ULONG FORMsize;
- ULONG AIFFid;
-
- ULONG COMMid;
- ULONG COMMsize;
- CommonChunk COMMchunk;
-
- ULONG SSNDid;
- ULONG SSNDsize;
- SampledSoundHeader SSNDchunk;
- } AIFFheader =
- { // All NULLs will be filled later.
- ID_FORM,NULL,ID_AIFF,
- ID_COMM,sizeof(CommonChunk),{NULL,NULL,16,{NULL}},
- ID_SSND,NULL,{0,0}
- };
-
- struct {
- BYTE signal,pad;
- ULONG signalflag;
- WORD *buffer1;
- WORD *buffer2;
- ULONG bufferlen;
- ULONG count;
- ULONG offs;
- APTR task;
- } RecordData;
-
- /*
- The RecordFunc is written in assembler in order to
- keep the CPU usage as low as possible. You are advised
- to do the same.
-
- __asm __saveds ULONG RecordFuncS(register __a0 struct Hook *hook,
- register __a2 struct AHIAudioCtrl *actrl,
- register __a1 struct AHIRecordMessage *chan)
- {
- return NULL;
- }
- */
-
- extern ULONG RecordFuncS();
- extern ULONG RecordFuncM();
-
- struct Hook recordhook =
- {
- 0,0,
- NULL,
- NULL,
- &RecordData,
- };
-
- BOOL openAHI(void);
- void closeAHI(void);
- void updatetopgadgets(void);
- long IDCMPhandler(void);
- void calcsamplesize(void);
- BOOL ProcessWindowWin0( LONG Class, UWORD Code, APTR IAddress );
- void record(void);
- void ulong2extended (ULONG in, extended *ex);
-
- int main(void)
- {
- struct StringNode *node;
-
- NewList(&InputList);
- NewList(&OutputList);
-
- if (OpenLibs()==0)
- {
- if(openAHI())
- {
- OpenHardDiskRecordCatalog(NULL,NULL);
- if (OpenWindowWin0("")==0)
- {
- if(filereq=AllocAslRequestTags(ASL_FileRequest,
- ASLFR_Window,Win0,
- ASLFR_SleepWindow,TRUE,
- ASLFR_DoSaveMode,TRUE,
- ASLFR_RejectIcons,TRUE,
- TAG_DONE))
- {
- WindowLimits(Win0,200,Win0->BorderTop,0,0);
- updatetopgadgets();
- calcsamplesize();
- IDCMPhandler();
- FreeAslRequest(filereq);
- }
- CloseWindowWin0();
- }
- else
- Printf("Cannot open window.\n");
- CloseHardDiskRecordCatalog();
- closeAHI();
- }
- else
- Printf("Cannot open ahi.device\n");
- CloseLibs();
- }
- else
- Printf("Cannot open libraries.\n");
-
-
- // These nodes are allocated in updatetopgadgets().
- while(node=(struct StringNode *) RemHead(&InputList))
- FreeVec(node);
- while(node=(struct StringNode *) RemHead(&OutputList))
- FreeVec(node);
- }
-
- BOOL openAHI(void)
- {
- if(AHImp=CreateMsgPort())
- {
- if(AHIio=(struct AHIRequest *)CreateIORequest(AHImp,sizeof(struct AHIRequest)))
- {
- AHIio->ahir_Version=2;
- if((AHIDevice=OpenDevice(AHINAME,AHI_NO_UNIT,(struct IORequest *)AHIio,NULL)) == NULL)
- {
- AHIBase=(struct Library *)AHIio->ahir_Std.io_Device;
- if(amreq=AHI_AllocAudioRequest(TAG_DONE))
- {
- return TRUE;
- }
- }
- }
- }
- return FALSE;
- }
-
- void closeAHI(void)
- {
- if(amreq)
- AHI_FreeAudioRequest(amreq);
- if(!AHIDevice)
- CloseDevice((struct IORequest *)AHIio);
- DeleteIORequest((struct IORequest *)AHIio);
- DeleteMsgPort(AHImp);
- }
-
-
- void updatetopgadgets(void)
- {
- long i;
- long inputs=0, outputs=0;
- long minmon=0x00000, maxmon=0x00000, mingain=0x10000, maxgain=0x10000;
- struct StringNode *node;
-
- amtext2[0]='\0';
- sprintf(amtext1,GetString(SelectAudioID));
-
- AHI_GetAudioAttrs(audioid,NULL,
- AHIDB_BufferLen,32,
- AHIDB_Name,amtext1,
- AHIDB_Inputs,&inputs,
- AHIDB_Outputs,&outputs,
- AHIDB_MinMonitorVolume,&minmon,
- AHIDB_MaxMonitorVolume,&maxmon,
- AHIDB_MinInputGain,&mingain,
- AHIDB_MaxInputGain,&maxgain,
- TAG_DONE);
-
- if(recfreq)
- sprintf(amtext2,"%ld Hz",recfreq);
-
- // Create input and output lists
-
- // Free old lists first
- while(node=(struct StringNode *) RemHead(&InputList))
- FreeVec(node);
- while(node=(struct StringNode *) RemHead(&OutputList))
- FreeVec(node);
-
- // Add new nodes
- for(i=0;i<inputs;i++)
- {
- if(node=AllocVec(sizeof(struct StringNode),MEMF_CLEAR))
- {
- AHI_GetAudioAttrs(audioid,NULL,
- AHIDB_BufferLen,STRINGNODE_LENGTH,
- AHIDB_InputArg,i,
- AHIDB_Input,node->sn_String,
- TAG_DONE);
- node->sn_Node.ln_Name=node->sn_String;
- node->sn_Node.ln_Type=STRINGNODE_ID;
- AddTail(&InputList,(struct Node *) node);
- }
- }
- for(i=0;i<outputs;i++)
- {
- if(node=AllocVec(sizeof(struct StringNode),MEMF_CLEAR))
- {
- AHI_GetAudioAttrs(audioid,NULL,
- AHIDB_BufferLen,STRINGNODE_LENGTH,
- AHIDB_OutputArg,i,
- AHIDB_Output,node->sn_String,
- TAG_DONE);
- node->sn_Node.ln_Name=node->sn_String;
- node->sn_Node.ln_Type=STRINGNODE_ID;
- AddTail(&OutputList,(struct Node *) node);
- }
- }
-
- // Set gadget attributes
-
- GT_SetGadgetAttrs(Win0Gadgets[Win0_amtext1],Win0,NULL,
- GTTX_Text,amtext1,
- TAG_DONE);
- GT_SetGadgetAttrs(Win0Gadgets[Win0_amtext2],Win0,NULL,
- GTTX_Text,amtext2,
- TAG_DONE);
-
- if(source >= inputs)
- source=0;
- GT_SetGadgetAttrs(Win0Gadgets[Win0_srclist],Win0,NULL,
- GA_Disabled,!inputs,
- GTLV_Labels,&InputList,
- GTLV_Selected,source,
- TAG_DONE);
-
- if(dest >= outputs)
- dest=0;
- GT_SetGadgetAttrs(Win0Gadgets[Win0_dstlist],Win0,NULL,
- GA_Disabled,!outputs,
- GTLV_Labels,&OutputList,
- GTLV_Selected,dest,
- TAG_DONE);
-
- if(vol<(minmon*100/0x10000))
- vol=minmon*100/0x10000;
- if(vol>(maxmon*100/0x10000))
- vol=maxmon*100/0x10000;
- GT_SetGadgetAttrs(Win0Gadgets[Win0_volslider],Win0,NULL,
- GA_Disabled, minmon == maxmon,
- GTSL_Min,minmon*100/0x10000,
- GTSL_Max,maxmon*100/0x10000,
- GTSL_Level,vol,
- TAG_DONE);
-
- if(gain<(mingain*100/0x10000))
- gain=mingain*100/0x10000;
- if(gain>(maxgain*100/0x10000))
- gain=maxgain*100/0x10000;
- GT_SetGadgetAttrs(Win0Gadgets[Win0_gainslider],Win0,NULL,
- GA_Disabled, mingain == maxgain,
- GTSL_Min,mingain*100/0x10000,
- GTSL_Max,maxgain*100/0x10000,
- GTSL_Level,gain,
- TAG_DONE);
- }
-
- long IDCMPhandler(void)
- {
- int done=0;
- ULONG clas;
- UWORD code;
- struct Gadget *pgsel;
- struct IntuiMessage *imsg;
-
- while(done==0)
- {
- Wait(1L << Win0->UserPort->mp_SigBit);
- imsg=GT_GetIMsg(Win0->UserPort);
- while (imsg != NULL )
- {
- clas=imsg->Class;
- code=imsg->Code;
- pgsel=(struct Gadget *)imsg->IAddress; /* Only reference if it is a gadget message */
- GT_ReplyIMsg(imsg);
- if(ProcessWindowWin0(clas, code, pgsel))
- done=1;
- imsg=GT_GetIMsg(Win0->UserPort);
- }
- }
- return NULL;
- }
-
- void calcsamplesize(void)
- {
- samplesize = (type ? sizeof(AIFCheader) : sizeof(AIFFheader)) +\
- (((struct StringInfo *) Win0Gadgets[Win0_duration]->SpecialInfo)->LongInt )*\
- recfreq*(stereo ? 4 : 2);
- GT_SetGadgetAttrs(Win0Gadgets[Win0_length],Win0,NULL,
- GTNM_Number,samplesize/1024,
- TAG_DONE);
- }
-
- BOOL ProcessWindowWin0( LONG Class, UWORD Code, APTR IAddress )
- {
- struct Gadget *gad;
-
- switch ( Class )
- {
- case IDCMP_GADGETUP :
- case IDCMP_GADGETDOWN :
- /* Gadget message, gadget = gad. */
- gad = (struct Gadget *)IAddress;
- switch ( gad->GadgetID )
- {
- case Win0_srclist :
- /* ListView pressed, Text of gadget : Source */
- source=Code;
- break;
- case Win0_dstlist :
- /* ListView pressed, Text of gadget : Loopback dest. */
- dest=Code;
- break;
- case Win0_volslider :
- /* Slider changed , Text of gadget : Loopback volume */
- vol=Code;
- break;
- case Win0_ambutton :
- /* Button pressed , Text of gadget : Select audio mode... */
- if(AHI_AudioRequest(amreq,
- AHIR_Window,Win0,
- AHIR_SleepWindow,TRUE,
- AHIR_InitialAudioID,audioid,
- AHIR_InitialMixFreq,recfreq,
- AHIR_DoMixFreq,TRUE,
- AHIR_FilterTags,&filtertags,
- TAG_DONE))
- {
- audioid=amreq->ahiam_AudioID;
- recfreq=amreq->ahiam_MixFreq;
- AHI_GetAudioAttrs(audioid,NULL,
- AHIDB_Stereo,&stereo,
- TAG_DONE);
- updatetopgadgets();
- calcsamplesize();
- }
- break;
- case Win0_gainslider :
- /* Slider changed , Text of gadget : Input gain */
- gain=Code;
- break;
- case Win0_duration :
- /* Integer entered , Text of gadget : Sample duration */
- calcsamplesize();
- break;
- case Win0_filename :
- /* String entered , Text of gadget : */
- break;
- case Win0_fnbutton :
- /* Button pressed , Text of gadget : File name... */
-
- // Copy the path to directory and the file name to filename
- stccpy(directory,((struct StringInfo *) Win0Gadgets[Win0_filename]->SpecialInfo)->Buffer,1024);
- ((char *) PathPart(directory))[0]='\0'; // Strip file name
- stccpy(filename,FilePart(((struct StringInfo *) Win0Gadgets[Win0_filename]->SpecialInfo)->Buffer),1024);
-
- if(AslRequestTags(filereq,
- ASLFR_InitialDrawer,directory,
- ASLFR_InitialFile,filename,
- TAG_DONE))
- {
- stccpy(fullname,filereq->fr_Drawer,1024);
- AddPart(fullname,filereq->fr_File,1024);
- GT_SetGadgetAttrs(Win0Gadgets[Win0_filename],Win0,NULL,
- GTST_String,fullname,
- TAG_DONE);
- }
- break;
- case Win0_create :
- /* Button pressed , Text of gadget : Prepare sample file */
- calcsamplesize();
- if(file=Open(((struct StringInfo *) Win0Gadgets[Win0_filename]->SpecialInfo)->Buffer,
- MODE_NEWFILE))
- {
- if(SetFileSize(file, samplesize, OFFSET_BEGINNING) == -1)
- DisplayBeep(Win0->WScreen);
- Close(file);
- file=NULL;
- }
- break;
- case Win0_begin :
- /* Button pressed , Text of gadget : Begin recording */
- record();
- break;
- case Win0_format :
- /* Cycle changed , Text of gadget : File format */
- type=Code;
- calcsamplesize();
- break;
- }
- break;
- case IDCMP_CLOSEWINDOW :
- return TRUE; // Return and signal quit
- case IDCMP_REFRESHWINDOW :
- GT_BeginRefresh( Win0);
- /* Refresh window. */
- RendWindowWin0( Win0, Win0VisualInfo );
- GT_EndRefresh( Win0, TRUE);
- GT_RefreshWindow( Win0, NULL);
- RefreshGList( Win0GList, Win0, NULL, ~0);
- break;
- }
- return FALSE;
- }
-
- /*
- ** This is the function that does the actual recording.
- */
-
- void record(void)
- {
- WORD error=-1,taskpri;
- ULONG signalset,buffersize;
- LONG samples=(((struct StringInfo *) Win0Gadgets[Win0_duration]->SpecialInfo)->LongInt )*recfreq;
-
- taskpri=SetTaskPri(FindTask(NULL),1);
- if(taskpri>1)
- SetTaskPri(FindTask(NULL),taskpri);
-
- RecordData.offs=0;
-
- // RecordData.task is the task that the RecordFunc will signal
-
- RecordData.task=FindTask(NULL);
-
- // Open the (hopefully prepared)
-
- if(file=Open(((struct StringInfo *) Win0Gadgets[Win0_filename]->SpecialInfo)->Buffer,
- MODE_OLDFILE))
- {
-
- // Test if file size is correct (i.e., is the file prepared?).
-
- Seek(file,0,OFFSET_END);
- if(Seek(file,0,OFFSET_BEGINNING) == samplesize)
- {
-
- // Allocate the hardware
-
- if(actrl=AHI_AllocAudio(
- AHIA_AudioID,audioid,
- AHIA_MixFreq,recfreq,
- AHIA_Channels,1,
- AHIA_Sounds,1,
- AHIA_RecordFunc,&recordhook,
- TAG_DONE))
- {
-
- // Get the actual mixing/recording frequency
-
- AHI_ControlAudio(actrl,
- AHIC_MixFreq_Query,&recfreq,
- TAG_DONE);
-
- // Allocate a signal for communication between this process and the RecordFunc.
-
- if(RecordData.signal=AllocSignal(-1) != -1)
- {
- RecordData.signalflag=(1L << RecordData.signal);
-
- // Chose the correct RecordFunc
-
- if(stereo)
- recordhook.h_Entry=&RecordFuncS;
- else
- recordhook.h_Entry=&RecordFuncM;
-
- // Make sure our own buffers are larger (or equal) than AHI's.
-
- AHI_GetAudioAttrs(AHI_INVALID_ID,actrl,
- AHIDB_MaxRecordSamples,&RecordData.bufferlen);
- RecordData.bufferlen=max(RecordData.bufferlen,8192);
-
- // Init RecordData.count
-
- RecordData.count=RecordData.bufferlen;
-
- // buffersize is the size in bytes instead of sample frames.
-
- buffersize=RecordData.bufferlen*2*(stereo ? 2 : 1);
-
- // Allocate own buffers (2 of them).
-
- if(RecordData.buffer1=AllocVec(buffersize,MEMF_PUBLIC))
- {
- if(RecordData.buffer2=AllocVec(buffersize,MEMF_PUBLIC))
- {
-
- // Write IFF headers.
-
- // Why support both AIFF and AIFC? AIFF is obsolete, but still many Amiga programs
- // cannot handle AIFC files. That's why.
-
- if(type == 0)
- {
- // Fill rest of the AIFFheader structure
- AIFFheader.FORMsize=samplesize-8;
- AIFFheader.COMMchunk.numChannels=(stereo ? 2 : 1);
- AIFFheader.COMMchunk.numSampleFrames=samples;
- ulong2extended(recfreq,&AIFFheader.COMMchunk.sampleRate);
- AIFFheader.SSNDsize=sizeof(SampledSoundHeader)+2*samples*AIFFheader.COMMchunk.numChannels;
- Write(file,&AIFFheader,sizeof(AIFFheader));
- }
- else if (type == 1)
- {
- // Fill rest of the AIFCheader structure
- AIFCheader.FORMsize=samplesize-8;
- AIFCheader.COMMchunk.numChannels=(stereo ? 2 : 1);
- AIFCheader.COMMchunk.numSampleFrames=samples;
- ulong2extended(recfreq,&AIFCheader.COMMchunk.sampleRate);
- AIFCheader.SSNDsize=sizeof(SampledSoundHeader)+2*samples*AIFCheader.COMMchunk.numChannels;
- Write(file,&AIFCheader,sizeof(AIFCheader));
- }
-
- // Why do I have two calls here? It's just to make sure no samples are
- // collected until the hardware is set up correctly.
-
- AHI_ControlAudio(actrl,
- AHIC_MonitorVolume,vol*65536/100,
- AHIC_InputGain,gain*65536/100,
- AHIC_Input,source,
- AHIC_Output,dest,
- TAG_DONE);
-
- if(!AHI_ControlAudio(actrl,
- AHIC_Record,TRUE,
- TAG_DONE))
- {
-
- do
- {
-
- // Update the 'seconds left' gadget
- GT_SetGadgetAttrs(Win0Gadgets[Win0_secleft],Win0,NULL,
- GTNM_Number,samples/recfreq,
- TAG_DONE);
-
- // Wait until our RecordFunc has filled first buffer
- // RecordFunc always fills buffer1, which means that we should be saving buffer2.
-
- signalset=Wait( RecordData.signalflag | SIGBREAKF_CTRL_C);
-
- // Check if we've reached the end.
-
- if( (samples -= RecordData.bufferlen)>0 )
- {
- // Write buffer
- Write(file, RecordData.buffer2, buffersize);
- }
- else
- {
-
- // Write the last part, update the 'seconds left' gadget and break from the do-while block
-
- Write(file, RecordData.buffer2, 2*(samples+RecordData.bufferlen)*(stereo ? 2:1) );
- GT_SetGadgetAttrs(Win0Gadgets[Win0_secleft],Win0,NULL,
- GTNM_Number,0,
- TAG_DONE);
- break;
- }
- } while(!(signalset & SIGBREAKF_CTRL_C));
- error=0;
- }
-
- // Turn of RecordFunc
-
- AHI_ControlAudio(actrl,
- AHIC_Record,FALSE,
- TAG_DONE);
-
- // Clean up...
-
- FreeVec(RecordData.buffer2);
- }
- FreeVec(RecordData.buffer1);
- }
- FreeSignal(RecordData.signal);
- }
- AHI_FreeAudio(actrl);
- }
- else
- error=2;
- }
- else
- error=1;
- Close(file);
- }
- else
- error=1;
-
- // If an error occured, display a message.
-
- switch(error)
- {
- case 0:
- req.es_TextFormat=GetString(Finished);
- req.es_GadgetFormat=GetString(OKtext);
- EasyRequestArgs(Win0, &req, NULL, NULL);
- break;
- case 1:
- req.es_TextFormat=GetString(FileNotPrepared);
- req.es_GadgetFormat=GetString(OKtext);
- EasyRequestArgs(Win0, &req, NULL, NULL);
- break;
- case 2:
- req.es_TextFormat=GetString(NoHardware);
- req.es_GadgetFormat=GetString(OKtext);
- EasyRequestArgs(Win0, &req, NULL, NULL);
- break;
- default:
- req.es_TextFormat=GetString(UnknownErr);
- req.es_GadgetFormat=GetString(OKtext);
- EasyRequestArgs(Win0, &req, NULL, NULL);
- break;
- }
- // Restore the process priority.
- SetTaskPri(FindTask(NULL),taskpri);
- }
-
- // This function translates an ULONG to Apples SANE Extended used in AIFF/AIFC files.
-
- void ulong2extended (ULONG in, extended *ex)
- {
- ex->exponent=31+16383;
- ex->mantissa[1]=0;
- while(!(in & 0x80000000))
- {
- ex->exponent--;
- in<<=1;
- }
- ex->mantissa[0]=in;
- }
-