This is HybridInstrument.m in view mode; [Download] [Up]
#import <objc/Storage.h> #import <appkit/nextstd.h> #import <musickit/musickit.h> #import <soundkit/Sound.h> #import <sound/convertsound.h> typedef short HWORD; #include "../sms.h" #import "HybridInstrument.h" #import "hybrid.h" /* * function to move the samples to the right in the analysis buffer * */ void moveSamples (float *pFBuffer, int iBufferSize, int iHopSize) { memcpy ((char *) pFBuffer, (char *) (pFBuffer+iHopSize), sizeof(float) * (iBufferSize - iHopSize)); memset ((char *) (pFBuffer+iBufferSize-iHopSize), 0, sizeof(float) * iHopSize); } /* * function to fill the analysis window with sound samples */ void fillWindow (short *pSoundData, int iSample, int iHopSize, short *pIWindow, int iWindowSize) { short * pITemp2, *pITemp1, i; memcpy ((char *) pIWindow, (char *) (pIWindow+iHopSize), sizeof(short) * (iWindowSize - iHopSize)); memset ((char *) (pIWindow+iWindowSize-iHopSize), 0, sizeof(short) * iHopSize); /* avoid zeroes */ pITemp2 = pIWindow + iWindowSize - iHopSize; pITemp1 = pSoundData + iSample; for (i=0;i<iHopSize;++i) { *pITemp2++ = !(*pITemp1)? ((random()&2) - 1) :*pITemp1; pITemp1++; } } @implementation HybridInstrument /* See HybridInstrument.h for instance variables */ static int iFile1Par = 0,iTimeOffset1Par = 0, iFile2Par = 0, iTimeOffset2Par = 0, iDur2Par = 0, iFrameRatePar = 0, iOverlapping1Par = 0, iOverlapping2Par = 0, iGain0Par = 0, iGain1Par = 0, iGainEnvPar = 0, iNCoefficients0Par = 0, iNCoefficients1Par = 0, iNCoefficientsEnvPar = 0, iMagBalance0Par = 0, iMagBalance1Par = 0, iMagBalanceEnvPar = 0, iTimeStretch0Par = 0, iTimeStretch1Par = 0, iTimeStretchEnvPar = 0, iFilterTypePar = 0, iSmoothOrderPar = 0, iCompression0EnvPar = 0, iCompression1EnvPar = 0, iCompressionIntEnvPar = 0; -init { [super init]; iCurOutSample = -1; idSndObj1 = nil; idSndObj2 = nil; fFrameRate = 100; iOverlapping1 = 4; iOverlapping2 = 4; fDur1 = 0; fDur2 = 0; fTimeOffset1 = 0; fTimeOffset2 = 0; fGain0 = 0; fGain1 = 1; idGainEnv = nil; iNCoefficients0 = -1; iNCoefficients1 = -1; idNCoefficientsEnv = nil; fMagBalance0 = 0; fMagBalance1 = 1; idMagBalanceEnv = nil; fTimeStretch0 = 1; fTimeStretch1 = 0; idTimeStretchEnv = nil; iFilterType = 1; iSmoothOrder = 0; idCompression0Env = nil; idCompression1Env = nil; idCompressionIntEnv = nil; bNormal = 1; iFile1Par = [Note parName:"sndFile1"]; iFile2Par = [Note parName:"sndFile2"]; iFrameRatePar = [Note parName:"frameRate"]; iOverlapping1Par = [Note parName:"overlapping1"]; iOverlapping2Par = [Note parName:"overlapping2"]; iTimeOffset1Par = [Note parName:"timeOffset1"]; iTimeOffset2Par = [Note parName:"timeOffset2"]; iDur2Par = [Note parName:"dur2"]; iGain0Par = [Note parName:"gain0"]; iGain1Par = [Note parName:"gain1"]; iGainEnvPar = [Note parName:"gainEnv"]; iNCoefficients0Par = [Note parName:"nCoefficients0"]; iNCoefficients1Par = [Note parName:"nCoefficients1"]; iNCoefficientsEnvPar = [Note parName:"nCoefficientsEnv"]; iMagBalance0Par = [Note parName:"magBalance0"]; iMagBalance1Par = [Note parName:"magBalance1"]; iMagBalanceEnvPar = [Note parName:"magBalanceEnv"]; iTimeStretch0Par = [Note parName:"timeStretch0"]; iTimeStretch1Par = [Note parName:"timeStretch1"]; iTimeStretchEnvPar = [Note parName:"timeStretchEnv"]; iFilterTypePar = [Note parName:"filterType"]; iSmoothOrderPar = [Note parName:"smoothOrder"]; iCompression0EnvPar = [Note parName:"compression0Env"]; iCompression1EnvPar = [Note parName:"compression1Env"]; iCompressionIntEnvPar = [Note parName:"compressionIntEnv"]; iSamplingRate = 22050; [self addNoteReceiver:[[NoteReceiver alloc] init]]; return self; } -setSamplingRate:(double)aSrate normal:(char)bNorm stream:(NXStream *)aStream /* Invoked once before performance from hybridMk.m. */ { if ([self inPerformance]) return nil; iSamplingRate = (int)aSrate; pStream = aStream; bNormal = bNorm; return self; } -firstNote:aNote /* You never invoke this method; it's invoked automatically just before the receiver writes its first Note. It opens stream to the receiver's filename (if set) and then sends initializeFile to the receiver.*/ { SNDAlloc (&pOutSoundStruct, 0, /* data size (we'll set this later) */ SND_FORMAT_LINEAR_16, iSamplingRate, 1, 0 /* info string space to allocate (for 128 bytes) */ ); /* Write header */ NXWrite(pStream,(char *)pOutSoundStruct, sizeof(*pOutSoundStruct)); pTempStream = NXOpenMemory(NULL, 0L, NX_READWRITE); return self; } -_mixToOutput:(int *)pData length:(int)iLength sample:(int)iFirstSample { int iLastSample = iFirstSample + iLength; int i, iLastCommonSample = 0, iLoc = 0, nSamps = 0; if (iFirstSample > (iCurOutSample + 1)) { int *pBuffer; nSamps = iFirstSample - iCurOutSample; pBuffer = (int *) calloc (nSamps, sizeof(int)); NXSeek(pTempStream, ((iCurOutSample + 1) * sizeof(int)), NX_FROMSTART); NXWrite(pTempStream,(char *)pBuffer, nSamps * sizeof(int)); iCurOutSample += nSamps; free (pBuffer); } else if (iCurOutSample >= iFirstSample) { int *pBuffer; NXSeek (pTempStream, (iFirstSample * sizeof (int)), NX_FROMSTART); iLastCommonSample = MIN (iLastSample, iCurOutSample); nSamps = MIN (iLength , 1 + iLastCommonSample - iFirstSample); pBuffer = (int *) calloc (nSamps, sizeof(int)); NXRead (pTempStream, (char *)pBuffer, sizeof(int) * nSamps); for (i = 0; i < nSamps; i++) pBuffer[i] += pData[i]; NXSeek(pTempStream, (iFirstSample * sizeof (int)), NX_FROMSTART); NXWrite (pTempStream, (char *)pBuffer, sizeof(int) * nSamps); iLoc = nSamps - 1; free (pBuffer); } if (iLastSample > (iCurOutSample + 1)) { nSamps = iLastSample - (iCurOutSample + 1); NXSeek(pTempStream, ((iCurOutSample + 1) * sizeof(int)), NX_FROMSTART); NXWrite(pTempStream,(char *)(pData+iLoc), nSamps * sizeof(int)); iCurOutSample += nSamps; } NXFlush(pTempStream); return self; } /* This is invoked when performance is over. */ -afterPerformance { if (!pOutSoundStruct) /* Did we never received any notes? */ return self; pOutSoundStruct->dataSize = iCurOutSample * sizeof(short); NXSeek(pStream, 0L, NX_FROMSTART); NXWrite(pStream,(char *)pOutSoundStruct, sizeof(*pOutSoundStruct)); [self normalAmp]; NXFlush(pStream); NXCloseMemory(pTempStream, NX_FREEBUFFER); pOutSoundStruct->dataSize = 0; SNDFree(pOutSoundStruct); pOutSoundStruct = NULL; NXSeek(pStream,0L,NX_FROMEND); NXFlush(pStream); return self; } /* Invoked from afterPerformance, normalizes the amplitude of the resulting time signal-sound */ -normalAmp { int *plBuffer; short *psBuffer; int nSamps, iMaxAmp = 0L, nBytes; register int iSamps; double fNormFactor; NXSeek(pTempStream, 0L, NX_FROMEND); nSamps = MIN ((NXTell(pTempStream) - sizeof(*pOutSoundStruct)) / sizeof(int), MAX_BUFF) ; NXSeek(pTempStream, 0L, NX_FROMSTART); NXSeek(pStream, sizeof(*pOutSoundStruct), NX_FROMSTART); if ((plBuffer = (int *) calloc (nSamps, sizeof(int))) == NULL) { fprintf(stderr,"Error: out of mem normalAmp1.\n"); return self; } if ((psBuffer = (short *) calloc (nSamps, sizeof(short))) == NULL) { fprintf(stderr,"Error: out of mem normalAmp2.\n"); return self; } if (bNormal) { while (nBytes = NXRead (pTempStream, plBuffer, sizeof(int) * nSamps)) for (iSamps = 0; iSamps < nSamps; ++iSamps) iMaxAmp = MAX(abs(plBuffer[iSamps]), iMaxAmp); fNormFactor = 32700. / iMaxAmp; NXSeek(pTempStream, 0L, NX_FROMSTART); while (nBytes = NXRead (pTempStream, plBuffer, sizeof(int) * nSamps)) { nSamps = MIN (nBytes >> 2,nSamps); for (iSamps = 0; iSamps < nSamps; ++iSamps) psBuffer[iSamps] = plBuffer[iSamps] * fNormFactor; NXWrite(pStream, psBuffer, nSamps * sizeof(short)); } } else while (nBytes = NXRead (pTempStream, plBuffer, sizeof(int) * nSamps)) { nSamps = MIN (nBytes >> 2,nSamps); for (iSamps = 0; iSamps < nSamps; ++iSamps) psBuffer[iSamps] = (short) plBuffer[iSamps]; NXWrite(pStream, psBuffer, nSamps * sizeof(short)); } free (plBuffer); free (psBuffer); return self; } -_readParams:aNote { char *pChFile1 = [aNote parAsStringNoCopy:iFile1Par]; char *pChFile2 = [aNote parAsStringNoCopy:iFile2Par]; if (!pChFile1 || !strlen(pChFile1)) /* Parameter not present? */ { fprintf(stderr,"No first input sound file specified.\n"); return nil; } if (!pChFile2 || !strlen(pChFile2)) /* Parameter not present? */ { fprintf(stderr,"No second input sound file specified.\n"); return nil; } idSndObj1 = [[Sound alloc] initFromSoundfile:pChFile1]; if (!idSndObj1) { fprintf(stderr,"Can't find file %s.\n", pChFile1); return nil; } else if (([idSndObj1 dataFormat] != SND_FORMAT_LINEAR_16) || ([idSndObj1 channelCount] != 1) || ([idSndObj1 samplingRate] != iSamplingRate) ) { [idSndObj1 convertToFormat:SND_FORMAT_LINEAR_16 samplingRate:(double)iSamplingRate channelCount:1]; if (([idSndObj1 dataFormat] != SND_FORMAT_LINEAR_16) || ([idSndObj1 channelCount] != 1) || ([idSndObj1 samplingRate] != iSamplingRate) ) { fprintf(stderr,"Error: input files must be in 16-bit linear, and \ mono.\n"); return nil; } } idSndObj2 = [[Sound alloc] initFromSoundfile:pChFile2]; if (!idSndObj2) { fprintf(stderr,"Can't find file %s.\n", pChFile2); return nil; } else if (([idSndObj2 dataFormat] != SND_FORMAT_LINEAR_16) || ([idSndObj2 channelCount] != 1) || ([idSndObj2 samplingRate] != iSamplingRate) ) { [idSndObj2 convertToFormat:SND_FORMAT_LINEAR_16 samplingRate:(double)iSamplingRate channelCount:1]; if (([idSndObj2 dataFormat] != SND_FORMAT_LINEAR_16) || ([idSndObj2 channelCount] != 1) || ([idSndObj1 samplingRate] != iSamplingRate) ) { fprintf(stderr,"Error: input files must be in 16-bit linear, and \ mono.\n"); return nil; } } if ([aNote isParPresent:iFrameRatePar]) fFrameRate = [aNote parAsDouble:iFrameRatePar]; if ([aNote isParPresent:iOverlapping1Par]) iOverlapping1 = [aNote parAsInt:iOverlapping1Par]; if ([aNote isParPresent:iOverlapping2Par]) iOverlapping2 = [aNote parAsInt:iOverlapping2Par]; if ([aNote isParPresent:iTimeOffset1Par]) fTimeOffset1 = [aNote parAsDouble:iTimeOffset1Par]; if ([aNote isParPresent:iTimeOffset2Par]) fTimeOffset2 = [aNote parAsDouble:iTimeOffset2Par]; fDur1 = [aNote dur]; if ([aNote isParPresent:iDur2Par]) fDur2 = [aNote parAsDouble:iDur2Par]; if ([aNote isParPresent:iGain0Par]) fGain0 = [aNote parAsDouble:iGain0Par]; if ([aNote isParPresent:iGain1Par]) fGain1 = [aNote parAsDouble:iGain1Par]; if ([aNote isParPresent:iGainEnvPar]) idGainEnv = (Envelope *)[aNote parAsEnvelope:iGainEnvPar]; if ([aNote isParPresent:iNCoefficients0Par]) iNCoefficients0 = [aNote parAsInt:iNCoefficients0Par]; if ([aNote isParPresent:iNCoefficients1Par]) iNCoefficients1 = [aNote parAsInt:iNCoefficients1Par]; if ([aNote isParPresent:iNCoefficientsEnvPar]) idNCoefficientsEnv = (Envelope *)[aNote parAsEnvelope:iNCoefficientsEnvPar]; if ([aNote isParPresent:iMagBalance0Par]) fMagBalance0 = [aNote parAsDouble:iMagBalance0Par]; if ([aNote isParPresent:iMagBalance1Par]) fMagBalance1 = [aNote parAsDouble:iMagBalance1Par]; if ([aNote isParPresent:iMagBalanceEnvPar]) idMagBalanceEnv = (Envelope *)[aNote parAsEnvelope:iMagBalanceEnvPar]; if ([aNote isParPresent:iTimeStretch0Par]) fTimeStretch0 = [aNote parAsDouble:iTimeStretch0Par]; if ([aNote isParPresent:iTimeStretch1Par]) fTimeStretch1 = [aNote parAsDouble:iTimeStretch1Par]; if ([aNote isParPresent:iTimeStretchEnvPar]) idTimeStretchEnv = (Envelope *)[aNote parAsEnvelope:iTimeStretchEnvPar]; if ([aNote isParPresent:iFilterTypePar]) iFilterType = [aNote parAsInt:iFilterTypePar]; if ([aNote isParPresent:iSmoothOrderPar]) iSmoothOrder = [aNote parAsInt:iSmoothOrderPar]; if ([aNote isParPresent:iCompression0EnvPar]) idCompression0Env = (Envelope *)[aNote parAsEnvelope:iCompression0EnvPar]; if ([aNote isParPresent:iCompression1EnvPar]) idCompression1Env = (Envelope *)[aNote parAsEnvelope:iCompression1EnvPar]; if ([aNote isParPresent:iCompressionIntEnvPar]) idCompressionIntEnv = (Envelope *)[aNote parAsEnvelope:iCompressionIntEnvPar]; return self; } -_generateCompression:(float *)pFArray size:(int)sizeArray envelope:(id)idEnvelope { double xfirst, xlast, y, s, fValT = 1, xval, fVal = 0; float fXinc; int lastPoint = [idEnvelope pointCount] - 1; int i; [idEnvelope getNth:0 x:&xfirst y:&y smoothing:&s]; [idEnvelope getNth:lastPoint x:&xlast y:&y smoothing:&s]; fXinc = (float) ((xlast - xfirst) / sizeArray); xval = xfirst; for (i = 0; i < sizeArray; i++) { pFArray[i] = [idEnvelope lookupYForX:xval]; xval += fXinc; } return self; } -_interpolateArrays:(double)fLocation envelope:(id)idEnvelope array0:(float *) pFBuffer0 array1:(float *) pFBuffer1 params:(HYB_PARAMS) params { double xfirst, xlast, y, s, fVal = 0, xval; int lastPoint = [idEnvelope pointCount] - 1; if (idEnvelope) { [idEnvelope getNth:0 x:&xfirst y:&y smoothing:&s]; [idEnvelope getNth:lastPoint x:&xlast y:&y smoothing:&s]; xval = xfirst + fLocation * (xlast - xfirst); fVal = [idEnvelope lookupYForX:xval]; } InterpolateArrays (pFBuffer0, params.sizeCompressionEnv, pFBuffer1, params.sizeCompressionEnv, params.pCompressionEnv, params.sizeCompressionEnv, fVal); return self; } -(float) interpolate:(double)fLocation envelope:(id)idEnvelope bottom:(double)bottom top:(double)top { double xfirst, xlast, y, s, fValT = 1, xval, fVal = 0; int lastPoint = [idEnvelope pointCount] - 1; if (idEnvelope) { [idEnvelope getNth:0 x:&xfirst y:&y smoothing:&s]; [idEnvelope getNth:lastPoint x:&xlast y:&y smoothing:&s]; xval = xfirst + fLocation * (xlast - xfirst); fValT = [idEnvelope lookupYForX:xval]; } fVal = bottom + fValT * (top - bottom); return (fVal); } -realizeNote:aNote fromNoteReceiver:aNoteReceiver { MKNoteType type; /* one of MK_noteDur, MK_noteOn, MK_noteOff, MK_noteUpdate, or MK_mute. The note type describes the character of the Note, whether it represents an entire musical note (or event), the beginning, middle, or end of a note, or no note (no sound) */ if (!aNote) /* el parÖmetre d'entrada */ return self; type = [aNote noteType]; /* guardem tipus de la nota */ if (type == MK_noteDur || type == MK_noteUpdate) [self _readParams:aNote]; else return self; /* if a real note, synthesize */ if (type == MK_noteDur) { short *pSndData1, *pSndData2, *pIWindow1, *pIWindow2; int iFirstSample, iHopSize1, iRecordSize1, nRecords1, nRecords2, iHopSize2, iRecordSize2, iSample1, iSample2, nHops, i, iRec, nSamples, iTmp, sizeMag1, sizeMag2, iLastSample1, iLastSample2; int *pIBuffer; float *pFBuffer, fStretch = 1, fLocation, *pFCompBuffer0, *pFCompBuffer1; HYB_PARAMS params; /* Give user feedback: MKGetTime() returns the current time, in seconds, during a Music Kit performance */ fprintf(stderr,"%f ",MKGetTime()); fflush(stderr); if (fDur1 < 0) fDur1 = [idSndObj1 duration]; else fDur1 = MIN ([idSndObj1 duration], fDur1); if (fDur2 <= 0) fDur2 = [idSndObj2 duration]; else fDur2 = MIN ([idSndObj2 duration], fDur2); /* loop through the input file */ iFirstSample = MKGetTime() * iSamplingRate; iHopSize1 = [idSndObj1 samplingRate] / fFrameRate; iRecordSize1 = iHopSize1 * iOverlapping1; nRecords2 = nRecords1 = fFrameRate * fDur1; iHopSize2 = fDur2 * [idSndObj2 samplingRate] / nRecords2; iRecordSize2 = iHopSize1 * iOverlapping2; iSample1 = fTimeOffset1 * [idSndObj1 samplingRate]; iSample2 = fTimeOffset2 * [idSndObj2 samplingRate]; iLastSample1 = [idSndObj1 dataSize]/sizeof(short); iLastSample2 = [idSndObj2 dataSize]/sizeof(short); pSndData1 = (short *) [idSndObj1 data]; pSndData2 = (short *) [idSndObj2 data]; if ((pFBuffer = (float *) calloc(iRecordSize1, sizeof(float))) == NULL) return nil; if ((pIWindow1 = (short *) calloc(iRecordSize1, sizeof(short))) == NULL) return nil; if ((pIWindow2 = (short *) calloc(iRecordSize2, sizeof(short))) == NULL) return nil; if ((pIBuffer = (int *) calloc(iHopSize1, sizeof(int))) == NULL) return nil; /* initialize window */ nHops = ((iOverlapping1 >> 1) - 1); for (i = 0; i < nHops; i++) { if (iSample1 >= iLastSample1 || iSample2 >= iLastSample2) { fprintf(stderr,"Error: reached end of file.\n"); return self; } fillWindow (pSndData1, iSample1, iHopSize1, pIWindow1, iRecordSize1); fillWindow (pSndData2, iSample2, iHopSize2, pIWindow2, iRecordSize2); iSample1 += iHopSize1; iSample2 += iHopSize2; } sizeMag1 = (int)(pow(2.0, (double) (1+(floor(log((double)iRecordSize1) / LOG2)))))/2; sizeMag2 = (int) (pow(2.0, (double) (1+(floor(log((double)iRecordSize2) / LOG2)))))/2; iTmp = iHopSize2; if ((pFCompBuffer0 = (float *) calloc(sizeMag1, sizeof(float))) == NULL) return nil; if ((pFCompBuffer1 = (float *) calloc(sizeMag1, sizeof(float))) == NULL) return nil; if (idCompression0Env) [self _generateCompression:pFCompBuffer0 size:sizeMag1 envelope:idCompression0Env]; if (idCompression1Env) [self _generateCompression:pFCompBuffer1 size:sizeMag1 envelope:idCompression1Env]; if (idCompression1Env || idCompression0Env) { if ((params.pCompressionEnv = (float *) calloc(sizeMag1, sizeof(float))) == NULL) return nil; params.sizeCompressionEnv = sizeMag1; if (!idCompressionIntEnv) { if (idCompression0Env) for (i = 0; i < sizeMag1; i++) params.pCompressionEnv[i] = pFCompBuffer0[i]; else if (idCompression1Env) for (i = 0; i < sizeMag1; i++) params.pCompressionEnv[i] = pFCompBuffer0[i]; } } else { params.pCompressionEnv = NULL; params.sizeCompressionEnv = 0; } if (iNCoefficients0 == -1) iNCoefficients0 = MIN (sizeMag1, sizeMag2); if (iNCoefficients1 == -1) iNCoefficients1 = MIN (sizeMag1, sizeMag2); /* Principi del loop principal de tota la aplicacií */ for (iRec = 0; iRec < nRecords1; iRec++) { fLocation = iRec / (double) nRecords1; if (idTimeStretchEnv) fStretch = [self interpolate:fLocation envelope:idTimeStretchEnv bottom:fTimeStretch0 top:fTimeStretch1]; iHopSize2 = fStretch * iTmp; nSamples = MIN (iHopSize2, iRecordSize2); if (iSample1+iHopSize1 >= iLastSample1 || iSample2+nSamples >= iLastSample2) { fprintf(stderr,"Error: reached end of file.\n"); return self; } fillWindow (pSndData1, iSample1, iHopSize1, pIWindow1, iRecordSize1); fillWindow (pSndData2, iSample2, nSamples, pIWindow2, iRecordSize2); params.nCoefficients = [self interpolate:fLocation envelope:idNCoefficientsEnv bottom:iNCoefficients0 top:iNCoefficients1]; if (params.nCoefficients > sizeMag1 || params.nCoefficients > sizeMag2) { params.nCoefficients = MIN (sizeMag1, sizeMag2); fprintf(stderr,"nCoefficients set to %d\n", params.nCoefficients); } params.fGain = [self interpolate:fLocation envelope:idGainEnv bottom:fGain0 top:fGain1]; params.fMagBalance = [self interpolate:fLocation envelope:idMagBalanceEnv bottom:fMagBalance0 top:fMagBalance1]; params.iSmoothOrder = iSmoothOrder; if (params.pCompressionEnv && idCompressionIntEnv) [self _interpolateArrays:fLocation envelope:idCompressionIntEnv array0:pFCompBuffer0 array1:pFCompBuffer1 params:params]; if (iFilterType == 1) Hybridize (pIWindow1, iRecordSize1, pIWindow2, iRecordSize2, pFBuffer, params); if (iRec >= iOverlapping1>>1) { for (i = 0; i < iHopSize1; i++) pIBuffer[i] = params.fGain * pFBuffer[i] / iOverlapping1; [self _mixToOutput:pIBuffer length:iHopSize1 sample:iFirstSample]; } moveSamples (pFBuffer, iRecordSize1, iHopSize1); iFirstSample += iHopSize1; iSample1 += iHopSize1; iSample2 += iHopSize2; } freeBuffers(); [idSndObj1 free]; [idSndObj2 free]; free (pFBuffer); free (pIWindow1); free (pIWindow2); free (pIBuffer); free (pFCompBuffer0); free (pFCompBuffer1); if (params.pCompressionEnv) free (params.pCompressionEnv); } return self; } @end
These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.