/*******************************************/ /* Matlab MAT File Output Class, */ /* by Gary P. Scavone, 1998. */ /* This object creates a Matlab MAT-file */ /* structure and fills it with buffers of */ /* samples (doubles). */ /* */ /* The Matlab MAT-file format is not */ /* available to the general public. I */ /* spent several days reverse-engineering */ /* the file format to create this class. */ /* I couldn't figure out what a few of */ /* the header fields correspond to, but */ /* for the purposes of STK, this */ /* shouldn't create any problems. */ /*******************************************/ #include "MatWvOut.h" /******** Matlab Matfile Header Struct *******/ struct matheaderform { char heading[124]; short a[2]; long b[10]; /* There's more, but it's of variable length */ }; FILE *openMatFile(int chans,char *fileName) { struct matheaderform hdr; FILE *fd; char tempName[128]; int i, namelen; long longtmp, headsize; strcpy(hdr.heading,"MATLAB 5.0 MAT-file, Generated by STK98. This file format was hacked by Gary P. Scavone, CCRMA, Stanford University, 1998."); for (i=strlen(hdr.heading);i<124;i++) hdr.heading[i] = ' '; hdr.a[0] = (short) 256; hdr.a[1] = (short) 'M'; hdr.a[1] <<= 8; hdr.a[1] += 'I'; hdr.b[0] = (long) 14; hdr.b[1] = (long) 0; /* Size of file after this point to end (in bytes) */ hdr.b[2] = (long) 6; hdr.b[3] = (long) 8; hdr.b[4] = (long) 6; hdr.b[5] = (long) 0; hdr.b[6] = (long) 5; hdr.b[7] = (long) 8; hdr.b[8] = (long) chans; /* This is the number of rows */ hdr.b[9] = (long) 0; /* This is the number of columns */ strcpy(tempName,fileName); strcat(tempName,".mat"); fd = fopen(tempName,"w+b"); if (!fd) { printf("Couldn't create matfile %s !!!!!!!!\n",fileName); exit(0); } printf("Creating matfile %s.\n", tempName); fwrite(&hdr,sizeof(char),168,fd); /* Write the fixed portion of the header */ /* The next 4 bytes can be viewed as two shorts, but they are byteswapped as a long. The first short value seems to always be one; the second short will be the length of the variable name IF IT IS <= 4; if the variable name length is >4, this short is zero and the length is put in the next 4 bytes. The variable name length is limited to 31 characters (32 with a '\n'). The actual variable name then follows. The variable name is "zero-padded" out to the following minimum lengths (in bits): 4, 8, 16, 24, 32. */ namelen = strlen(fileName); if (namelen > 31) { /* Check length of variable name (file name) */ fprintf(stderr, "File name too long ... should be 31 characters or less.\n"); fclose(fd); exit(0); } if (namelen > 4) { longtmp = 1; fwrite(&longtmp,sizeof(long),1,fd); fwrite(&namelen,sizeof(long),1,fd); headsize = 44 + namelen; } else { longtmp = namelen; longtmp <<= 16; longtmp += 1; fwrite(&longtmp,sizeof(long),1,fd); headsize = 40 + namelen; } fwrite(fileName,sizeof(char),namelen,fd); /* Write the variable (file) name */ if (namelen < 5) longtmp = 4 - namelen; else if (namelen < 9) longtmp = 8 - namelen; else if (namelen < 17) longtmp = 16 - namelen; else if (namelen < 25) longtmp = 24 - namelen; else longtmp = 32 - namelen; headsize += longtmp + 8; /* Add length (8) of following bytes */ fseek(fd,longtmp,SEEK_CUR); longtmp = 9; fwrite(&longtmp,sizeof(long),1,fd); longtmp = 0; /* Size of data in bytes (8 per sample) */ fwrite(&longtmp,sizeof(long),1,fd); fseek(fd,132,SEEK_SET); fwrite(&headsize,sizeof(long),1,fd); /* Write header size ... will update at end */ fseek(fd,0,SEEK_END); return fd; } MatWvOut :: MatWvOut(char *fileName) { chans = 1; pan = 0.5; fd = openMatFile(chans,fileName); counter = 0; totalCount = 0; } MatWvOut :: MatWvOut(int channels, char *fileName) { chans = channels; pan = 0.5; fd = openMatFile(chans,fileName); counter = 0; totalCount = 0; } MatWvOut :: ~MatWvOut() { double temp; long headsize, temp1; fwrite(data,sizeof(double),counter,fd); temp = (double) totalCount * ONE_OVER_SRATE; printf("%f Seconds Computed\n",temp); fseek(fd,164,SEEK_SET); fwrite(&totalCount,sizeof(long),1,fd); /* Write number of columns */ fseek(fd,132,SEEK_SET); fread(&headsize,sizeof(long),1,fd); temp1 = headsize; headsize += (long) (totalCount * 8 * 2); fseek(fd,132,SEEK_SET); fwrite(&headsize,sizeof(long),1,fd); /* Write file size (minus some header info) */ fseek(fd,temp1+128,SEEK_SET); temp1 = totalCount * 8 * 2; fwrite(&temp1,sizeof(long),1,fd); /* Write data size (in bytes) */ fclose(fd); } long MatWvOut :: getCounter() { return totalCount; } MY_FLOAT MatWvOut :: getTime() { return (MY_FLOAT) totalCount * ONE_OVER_SRATE; } void MatWvOut :: tick(MY_FLOAT sample) { if (chans==1) { data[counter++] = (double) (sample); } else { data[counter++] = (double) (sample * (1.0 - pan)); data[counter++] = (double) (sample * pan); } totalCount += 1; if (counter == MAT_BUFFER_SIZE) { fwrite(data,sizeof(double),MAT_BUFFER_SIZE,fd); counter = 0; } } void MatWvOut :: tick(MY_FLOAT lsamp, MY_FLOAT rsamp) { if (chans==1) { data[counter++] = (double) (lsamp + rsamp); } else { data[counter++] = (double) (lsamp); data[counter++] = (double) (rsamp); } totalCount += 1; if (counter == MAT_BUFFER_SIZE) { fwrite(data,sizeof(double),MAT_BUFFER_SIZE,fd); counter = 0; } } void MatWvOut :: setMonoPan(MY_FLOAT aPan) { pan = aPan; if (aPan < 0.0) { pan = 0.0; printf("Pan < 0.0, correcting to 0.0\n"); } if (aPan > 1.0) { pan = 1.0; printf("Pan > 1.0, correcting to 1.0\n"); } }