PageRenderTime 41ms CodeModel.GetById 14ms RepoModel.GetById 0ms app.codeStats 0ms

/Frameworks/MKSynthPatches/Pluck.m

https://bitbucket.org/eddieh/musickit
Objective C | 700 lines | 468 code | 63 blank | 169 comment | 63 complexity | d85b4a336a7a929521969f250e22ba4d MD5 | raw file
  1. /* Copyright 1988-1992, NeXT Inc. All rights reserved. */
  2. #ifdef SHLIB
  3. #include "shlib.h"
  4. #endif
  5. /*
  6. Modification history:
  7. 10/06/89/daj - Changed to import _exportedPrivateMusickit.h instead of
  8. _musickit.h
  9. 11/21/89/daj - Changed to check referenceCount of noiseLoc in claimNoise.
  10. This is needed for new lazy deallocation of synthData.
  11. 02/01/90/daj - Added error message when noise can't be allocated.
  12. 04/07/90/daj - Changed to loop over parameters. Also added volume.
  13. 05/14/90/daj - Changed to always set one zero on noteOn when status !=
  14. running. This was a bug introduced when the patch was rewritten.
  15. 08/28/90/daj - Changed initialize to init.
  16. 09/02/90/daj - Changed MAXDOUBLE references to noDVal.h way of doing things
  17. 09/29/90/daj - Commented out INLINE_MATH since 040 exp bug is fixed
  18. 08/27/91/DAJ - Internationalized strings.
  19. 6/12/93/daj - Removed define of MK_INLINE
  20. */
  21. /* Pluck with shared noise */
  22. // #define INLINE_MATH 1 /* Workaround for exp() bug. */
  23. #import <MKUnitGenerators/MKUnitGenerators.h>
  24. #import <MusicKit/midi_spec.h>
  25. #import "Pluck.h"
  26. #import "_MKList.h" /* sb: addition. This is a minimal List object (6 objects max) */
  27. #import <objc/HashTable.h> /*sb: was way down the file. */
  28. @implementation Pluck:MKSynthPatch
  29. {
  30. /* Here are the parameters. */
  31. double freq; /* Frequency. */
  32. double sustain; /* Sustain parameter value */
  33. double ampRel; /* AmpRel parameter value.*/
  34. double decay; /* Decay parameter value. */
  35. double bright; /* Brightness parameter value */
  36. double amp; /* Amplitude parameter value. */
  37. double bearing; /* Bearing parameter value. */
  38. double baseFreq; /* Frequency, not including pitch bend */
  39. int pitchBend; /* Modifies freq. */
  40. double pitchBendSensitivity; /* How much effect pitchBend has. */
  41. double velocitySensitivity; /* How much effect velocity has. */
  42. int velocity; /* Velocity scales bright. */
  43. int volume; /* Midi volume pedal */
  44. id _reservedPluck1;
  45. id _reservedPluck2;
  46. int _reservedPluck3;
  47. void * _reservedPluck4;
  48. }
  49. /* Plucked string without fine-tuning of pitch,
  50. as described in Jaffe/Smith, Computer Music Journal
  51. Vol. 7, No. 2, Summer 1983.
  52. Pluck is a simple physical model of a plucked string. It uses a
  53. delay line to represent the string. The lower the pitch, the more delay
  54. memory it needs. Thus, a passage with many low notes may have problems
  55. running out of DSP memory.
  56. Pluck does dynamic allocation of its delay
  57. memory. This may result in some loss over time due to DSP memory
  58. fragmentation. The sollution to this problem is to preallocate delay memory
  59. by specifying the lowestFreq in the note passed to +patchTemplateFor:
  60. However, this feature is not supported in this version of Pluck. Future
  61. versions will have this feature.
  62. This instrument has the following parameters:
  63. MK_freq - Frequency.
  64. MK_keyNum - Alternative for frequency.
  65. MK_lowestFreq - This parameter is used to warn the synthpatch what the
  66. lowest note that may appear in the phrase will be so that
  67. it can allocate an appropriate amount of delay memory.
  68. It is only used in the first note of a phrase.
  69. MK_sustain - On a scale from 0 to 1. 1 means "sustain forever".
  70. MK_pickNoise - In seconds (duration of initial pick noise)
  71. MK_decay - Time constant of decay or 0 for no decay in units of t60.
  72. (That is, this is the time for the note to decay to -60dB.)
  73. MK_bright - Brightness of the pluck. On a scale of 0 to 1.
  74. 1 is very bright.
  75. MK_amp - Amplitude of the pluck. On a scale of 0 to 1.0. Note that
  76. resultant amplitude may be lower due to the nature of the
  77. noise used for the attack.
  78. MK_bearing - On a scale from -45 to 45.
  79. MK_ampRel - Time at end of note (after noteOff) for string to damp to
  80. -60dB.
  81. Pluck is quite robust about handling noteUpdates. Most parameters can be
  82. sent as note updates. This makes it easy to put a slider on parameters.
  83. Pluck remembers the values you send it so each noteUpdate need not have all
  84. parameters in it.
  85. Note, however, that the parameters MK_bright and MK_amp are applied to the
  86. attack (the "pick") only. Changing these values in a noteUpdate will have
  87. no effect until the following note of the phrase.
  88. */
  89. /*
  90. This is an atypical MKSynthPatch in that it allocates its delay memory not
  91. as part of itself, but on a note-by-note basis.
  92. This is so that memory usage can be optimized.
  93. */
  94. /* #include "tune.m" */
  95. //#define LOW_PITCH_STR NXLocalStringFromTableInBundle(_MKErrorStringFile(),_MKErrorBundle(),"low pitch",NULL,"This string appears as part of a larger error message: 'Out of low pitch memory...'")
  96. #define LOW_PITCH_STR [NSLocalizedStringFromTableInBundle(@"low pitch", [NSString stringWithUTF8String:_MKErrorStringFile()], _MKErrorBundle(), "This string appears as part of a larger error message: 'Out of low pitch memory...'") UTF8String]
  97. //#define PITCH_STR NXLocalStringFromTableInBundle(_MKErrorStringFile(),_MKErrorBundle(),"pitch",NULL,"This string appears as part of a larger error message: 'Out of pitch memory...'")
  98. #define PITCH_STR [NSLocalizedStringFromTableInBundle(@"pitch", [NSString stringWithUTF8String:_MKErrorStringFile()], _MKErrorBundle(), "This string appears as part of a larger error message: 'Out of pitch memory...'") UTF8String]
  99. //#define PLUCK_NOISE_STR NXLocalStringFromTableInBundle(_MKErrorStringFile(),_MKErrorBundle(),"Pluck noise",NULL,"This string appears as part of a larger error message: 'Can't allocate Pluck noise...'")
  100. #define PLUCK_NOISE_STR [NSLocalizedStringFromTableInBundle(@"Pluck noise", [NSString stringWithUTF8String:_MKErrorStringFile()], _MKErrorBundle(), "This string appears as part of a larger error message: 'Can't allocate Pluck noise...'") UTF8String]
  101. #define noiseLoc _reservedPluck1
  102. #define delayMem _reservedPluck2
  103. #define delayLen _reservedPluck3
  104. static int delay,oneZero,out2Sum,onePole,dSwitch,pp2,pp,allPass,ppy;
  105. +patchTemplateFor:currentNote
  106. /* In this simple case, always returns the same patch template. */
  107. {
  108. static id tmpl = nil;
  109. if (tmpl)
  110. return tmpl;
  111. tmpl = [MKPatchTemplate new];
  112. /* First we allocate two patchpoints. */
  113. pp = [tmpl addPatchpoint:MK_xPatch]; /* General purpose */
  114. ppy = [tmpl addPatchpoint:MK_yPatch]; /* General purpose */
  115. pp2 = [tmpl addPatchpoint:MK_xPatch]; /* All pass output */
  116. /* Next we allocate unit generators.
  117. Order is critical here, since we reuse patchpoints. Alternatively,
  118. we could do a version that uses unordered UGs and 6 patchpoints. */
  119. # define ADDUG(_factory) [tmpl addUnitGenerator:[_factory class] ordered:YES]
  120. onePole = ADDUG(OnepoleUGxx);
  121. dSwitch = ADDUG(DswitchtUGyx);
  122. oneZero = ADDUG(OnezeroUGxy);
  123. out2Sum = ADDUG(Out2sumUGx);
  124. delay = ADDUG(DelayUGxxy);
  125. allPass = ADDUG(Allpass1UGxx);
  126. # define MSG(_target,_message,_arg) \
  127. [tmpl to:_target sel:@selector(_message) arg:_arg]
  128. MSG(onePole, setOutput:,pp);
  129. MSG(dSwitch, setInput1:,pp);
  130. MSG(dSwitch, setOutput:,ppy);
  131. MSG(oneZero, setInput:, ppy);
  132. MSG(oneZero, setOutput:,pp);
  133. MSG(delay, setInput:, pp);
  134. MSG(delay, setOutput:,pp);
  135. MSG(allPass, setInput:, pp);
  136. MSG(allPass, setOutput:,pp2);
  137. MSG(dSwitch,setInput2:,pp2);
  138. /* We allow garbage to pump around the loop here. The assumption is that
  139. the attack will be at least 8 samples long so the garbage will clear
  140. out. */
  141. return tmpl;
  142. }
  143. //#define SE(_x) NX_ADDRESS(self->synthElements)[_x]
  144. #define SE(_x) [self->synthElements objectAtIndex:_x]
  145. #define DELAYUG SE(delay)
  146. #define ONEZERO SE(oneZero)
  147. #define OUT2SUM SE(out2Sum)
  148. #define ONEPOLE SE(onePole)
  149. #define DSWITCH SE(dSwitch)
  150. #define PP2 SE(pp2)
  151. #define PP SE(pp)
  152. #define ALLPASS SE(allPass)
  153. static double t60(double freq,double timeConst)
  154. /* TimeConst (ampRel or decay) is time constant in units of t(60).
  155. See Jaffe/Smith, equations (4), (5) and (19).
  156. I'm returning the value for DC to decay in time t(60). */
  157. {
  158. return exp(-6.91/(freq * timeConst));
  159. }
  160. #define PIPE DSPMK_NTICK /* one-tick pipe delay */
  161. static int tuneIt(double sRate,double freq,double sustain,double *c)
  162. /* Returns delay length. Also sets coefficient of all pass (*c) and
  163. resets sustain value. Make sure that the PIPE macro is set
  164. correctly. */
  165. {
  166. # define EPSILON .01 /* See Eq. 15 */
  167. int Pb; /* Delay length */
  168. double P; /* True period. */
  169. double o; /* Radian freq. */
  170. double Pa; /* Delay from one-zero filter; */
  171. double Pc; /* Delay from allpass */
  172. double S; /* 1-zero coeff. */
  173. double extraDelay; /* Temporary variable */
  174. double oPc; /* Optimization to avoid multiplying twice. */
  175. S = (sustain * .5) + .5; /* 1-zero coeff. */
  176. P = sRate/freq; /* Period */
  177. o = 2 * M_PI * freq/sRate; /* Radian freq */
  178. /* Delay from one zero filter. From Eq. 22 */
  179. Pa = (-1/o) * atan2(-S * sin(o), (1 - S) + S * cos(o));
  180. /* Delay from delay line. Don't round so that Pc, is > 0. */
  181. extraDelay = PIPE + Pa;
  182. Pb = P - (extraDelay + EPSILON); /* Eq. 15A */
  183. Pc = P - (extraDelay + Pb); /* Eq. 15B */
  184. oPc = o * Pc;
  185. *c = sin(.5 * (o - oPc)) / sin(.5 * (o + oPc)); /* Eq. 16 B */
  186. return Pb;
  187. }
  188. static void setOneZeroCoeffs(oneZ,damper,sustain)
  189. id oneZ;
  190. double damper,sustain;
  191. {
  192. [oneZ setB0:damper*(1.0-sustain)*0.5];
  193. [oneZ setB1:damper*(1.0+sustain)*0.5];
  194. }
  195. //#ifndef MAX
  196. //#define MAX(A,B) ((A) > (B) ? (A) : (B))
  197. //#endif
  198. //#ifndef MIN
  199. //#define MIN(A,B) ((A) < (B) ? (A) : (B))
  200. //#endif
  201. #ifndef ABS
  202. #define ABS(A) ((A) < 0 ? (-(A)) : (A))
  203. #endif
  204. -_updatePars:aNote noteType:(MKNoteType)noteType
  205. /* This handles all parameters which may be set at any time. */
  206. {
  207. MKPhraseStatus pStatus = [self phraseStatus];
  208. BOOL needToSetOneZero, newPhrase, needToSetBearing, needToTune;
  209. double lowestFreq = MK_NODVAL;
  210. int oldDelayLen,longestDelayLen,par;
  211. void *state;
  212. switch (pStatus) {
  213. case MK_phraseOn:
  214. case MK_phraseOnPreempt:
  215. needToSetBearing = needToTune = newPhrase = YES;
  216. needToSetOneZero = (status != MK_running);
  217. break;
  218. case MK_phraseRearticulate:
  219. /* If we were previously finishing, need to reset one zero since
  220. decay changes */
  221. needToSetOneZero = (status == MK_finishing);
  222. if (needToSetOneZero)
  223. needToTune = YES;
  224. needToSetBearing = NO;
  225. newPhrase = NO;
  226. break;
  227. default: /* Should never happen */
  228. case MK_phraseUpdate:
  229. case MK_phraseOffUpdate:
  230. needToTune = needToSetBearing = newPhrase =
  231. needToSetOneZero = NO;
  232. break;
  233. case MK_phraseOff:
  234. needToSetOneZero = YES;
  235. needToTune = YES; /* When we set one zero, tuning changes. */
  236. newPhrase = NO;
  237. needToSetBearing = NO;
  238. break;
  239. }
  240. /* Get parameters */
  241. state = MKInitParameterIteration(aNote);
  242. while (par = MKNextParameter(aNote, state))
  243. switch (par) {
  244. case MK_sustain:
  245. sustain = MKGetNoteParAsDouble(aNote,MK_sustain);
  246. sustain = MAX(sustain,0);
  247. sustain = MIN(sustain,.999);
  248. needToSetOneZero = YES;
  249. needToTune = YES; /* When we set one zero, tuning changes. */
  250. break;
  251. case MK_ampRel:
  252. ampRel = MKGetNoteParAsDouble(aNote,MK_ampRel);
  253. ampRel = MAX(ampRel,0);
  254. /* May not need to set it here, since it could be set for later. */
  255. break;
  256. case MK_bearing:
  257. bearing = MKGetNoteParAsDouble(aNote,MK_bearing);
  258. needToSetBearing = YES;
  259. break;
  260. case MK_freq:
  261. case MK_keyNum:
  262. baseFreq = [aNote freq];
  263. needToTune = YES;
  264. break;
  265. case MK_pitchBend:
  266. pitchBend = MKGetNoteParAsInt(aNote,MK_pitchBend);
  267. needToTune = YES;
  268. break;
  269. case MK_pitchBendSensitivity:
  270. pitchBendSensitivity = MKGetNoteParAsDouble(aNote,
  271. MK_pitchBendSensitivity);
  272. needToTune = YES;
  273. break;
  274. case MK_lowestFreq:
  275. lowestFreq = MKGetNoteParAsDouble(aNote,MK_lowestFreq);
  276. /* Note that this parameter has no effect when not in a noteOn. */
  277. break;
  278. case MK_decay:
  279. decay = MKGetNoteParAsDouble(aNote,MK_decay);
  280. decay = MAX(decay,0);
  281. if (status != MK_finishing){ /* Decay unused in finishing section */
  282. needToSetOneZero = YES;
  283. needToTune = YES; /* When we set one zero, tuning changes. */
  284. }
  285. break;
  286. case MK_amp:
  287. amp = MKGetNoteParAsDouble(aNote,MK_amp);
  288. break;
  289. case MK_bright:
  290. bright = MKGetNoteParAsDouble(aNote,MK_bright);
  291. break;
  292. case MK_velocity:
  293. velocity = MKGetNoteParAsInt(aNote,MK_velocity);
  294. break;
  295. case MK_velocitySensitivity:
  296. velocitySensitivity =
  297. MKGetNoteParAsDouble(aNote,MK_velocitySensitivity);
  298. break;
  299. case MK_controlChange: {
  300. int controller = MKGetNoteParAsInt(aNote,MK_controlChange);
  301. if (controller == MIDI_MAINVOLUME) {
  302. volume = MKGetNoteParAsInt(aNote,MK_controlVal);
  303. needToSetBearing = YES;
  304. }
  305. break;
  306. }
  307. default:
  308. break;
  309. }
  310. if (needToTune) {
  311. double srate = [[self orchestra] samplingRate];
  312. double allPassCoefficient;
  313. freq = MKAdjustFreqWithPitchBend(baseFreq,pitchBend,
  314. pitchBendSensitivity);
  315. delayLen = tuneIt(srate,freq,sustain,&allPassCoefficient);
  316. if (delayLen < 1) {
  317. MKErrorCode(MK_spsOutOfRangeErr,PITCH_STR,MKGetTime());
  318. delayLen = 0;
  319. return nil;
  320. }
  321. [ALLPASS setBB0:allPassCoefficient];
  322. /* Adjust length */
  323. if (newPhrase) {
  324. /* See if guy wants to allocate more. */
  325. oldDelayLen = 0;
  326. if (!MKIsNoDVal(lowestFreq)) {
  327. double tmp;
  328. longestDelayLen = tuneIt(srate,lowestFreq,sustain,&tmp) + 1;
  329. if (longestDelayLen < delayLen)
  330. longestDelayLen = 0;
  331. /* 1 for good luck (or in case sustain changes!) */
  332. } else longestDelayLen = 0;
  333. } else { /* We've already got delay mem allocated */
  334. longestDelayLen = 0;
  335. oldDelayLen = [delayMem length];
  336. if (oldDelayLen < delayLen) { /* Not big enough ? */
  337. [delayMem mkdealloc]; /* Free it */
  338. delayMem = nil;
  339. }
  340. else {
  341. #if 0
  342. int curLen = [DELAYUG length];
  343. /* If we don't clear out new memory, why should we do it
  344. now??? This isn't needed if it's another noteOn because
  345. the new noise will fill up the buffer. It could be needed
  346. for a noteUpdate but in this case, we take a chance. */
  347. if (delayLen > curLen) /* get rid of old crap */
  348. [delayMem setToConstant:0 length:delayLen - curLen offset:
  349. curLen];
  350. #endif
  351. [DELAYUG adjustLength:delayLen]; /* else adjust it */
  352. }
  353. }
  354. /* Need to alloc? */
  355. if (!delayMem) {
  356. delayMem = [[self orchestra] allocSynthData:MK_yData length:
  357. (longestDelayLen) ? longestDelayLen : delayLen];
  358. if (!delayMem) { /* Alloc failed? */
  359. MKErrorCode(MK_spsCantGetMemoryErr,LOW_PITCH_STR,MKGetTime());
  360. if (noteType == MK_noteOn) {
  361. delayLen = 0;
  362. return nil; /* Omit note. */
  363. }
  364. delayLen = oldDelayLen;
  365. /* Try and revert to old situation to save hell from
  366. breaking loose. */
  367. delayMem =
  368. [[self orchestra] allocSynthData:MK_yData length:delayLen];
  369. if (!delayMem) { /* Now we've really lost */
  370. delayLen = 0;
  371. return nil;
  372. }
  373. }
  374. [DELAYUG setDelayMemory:delayMem];
  375. if (longestDelayLen)
  376. [DELAYUG adjustLength:delayLen];
  377. }
  378. }
  379. /* Now set the filters and such */
  380. if (needToSetOneZero) {
  381. double dec;
  382. if (noteType == MK_noteOff ||
  383. ((status == MK_finishing) && noteType == MK_noteUpdate))
  384. dec = (ampRel > .00001) ? t60(freq,ampRel) : 0;
  385. else
  386. dec = (decay == 0) ? 1 : t60(freq,decay);
  387. setOneZeroCoeffs(ONEZERO,dec,sustain);
  388. }
  389. if (noteType == MK_noteOn) {
  390. double actualAmp =
  391. amp * MKMidiToAmpWithSensitivity(velocity,velocitySensitivity);
  392. [DSWITCH setScale1:actualAmp];
  393. [ONEPOLE setBrightness:(bright *
  394. ((1.0 - velocitySensitivity) +
  395. velocitySensitivity * velocity * .015625))
  396. /* Velocity/64 */
  397. forFreq:freq];
  398. }
  399. if (needToSetBearing)
  400. [OUT2SUM setBearing:bearing
  401. scale:MKMidiToAmpAttenuation(volume)];
  402. return self;
  403. }
  404. static id noiseLocObj = nil; /* Placeholder for table */
  405. static NSMutableArray *slaveLists = nil; /* Subserviant shared objects. This is a real hack. */
  406. static id addSlaveList(id theOrch)
  407. {
  408. // NSMutableArray *aList;
  409. // [slaveLists addObject:aList = [[NSMutableArray alloc] initWithCapacity:6]];
  410. // aList->numElements = 6;
  411. // NX_ADDRESS(aList)[0] = theOrch;
  412. // [aList addObject:theOrch];//sb: added this. Don't forget this retains the orch.
  413. _MKList *aList;
  414. [slaveLists addObject:aList = [[_MKList alloc] init]];
  415. [aList baseAddress][0] = theOrch;
  416. return aList;
  417. }
  418. static id getSlaveListFor(id theOrch)
  419. /* We have to do this because there may be multiple orchestras. */
  420. {
  421. int cnt = [slaveLists count];
  422. id innerList;
  423. int i;
  424. for (i=0; i<cnt; i++) {
  425. innerList = [slaveLists objectAtIndex:i];
  426. if ([innerList objectAtIndex:0] == theOrch)
  427. return innerList;
  428. }
  429. return addSlaveList(theOrch);
  430. }
  431. enum {noiseFlt1InIndex = 1,noiseFlt1Index,noiseFlt2InIndex,noiseFlt2Index,
  432. noiseIndex};
  433. static id claimNoise(Pluck *self)
  434. {
  435. //NSMutableArray *slaveList = getSlaveListFor(self->orchestra);
  436. _MKList *slaveList = getSlaveListFor(self->orchestra);
  437. // int theCount = [slaveList count];
  438. id noise,noiseFlt1In,noiseFlt2In,noiseFlt1,noiseFlt2;
  439. if (self->noiseLoc) /* Shouldn't ordinarily happen. */
  440. return self->noiseLoc;
  441. self->noiseLoc = [self->orchestra sharedObjectFor:noiseLocObj];
  442. if (!self->noiseLoc) {
  443. /* Need to reinitialize */
  444. self->noiseLoc = [self->orchestra allocPatchpoint:MK_xPatch];
  445. /* Now here are the 'slaves' */
  446. noise = [self->orchestra allocUnitGenerator:[UnoiseUGx class]];
  447. noiseFlt1In = [self->orchestra allocPatchpoint:MK_xPatch];
  448. noiseFlt1 = [self->orchestra allocUnitGenerator:
  449. [OnezeroUGyx class]];
  450. noiseFlt2In = [self->orchestra allocPatchpoint:MK_yPatch];
  451. noiseFlt2 = [self->orchestra allocUnitGenerator:
  452. [OnepoleUGxy class]];
  453. // NX_ADDRESS(slaveList)[noiseFlt1InIndex] = noiseFlt1In;
  454. // NX_ADDRESS(slaveList)[noiseFlt2InIndex] = noiseFlt2In;
  455. // NX_ADDRESS(slaveList)[noiseFlt1Index] = noiseFlt1;
  456. // NX_ADDRESS(slaveList)[noiseFlt2Index] = noiseFlt2;
  457. // NX_ADDRESS(slaveList)[noiseIndex] = noise;
  458. [slaveList baseAddress][noiseFlt1InIndex] = noiseFlt1In;
  459. [slaveList baseAddress][noiseFlt2InIndex] = noiseFlt2In;
  460. [slaveList baseAddress][noiseFlt1Index] = noiseFlt1;
  461. [slaveList baseAddress][noiseFlt2Index] = noiseFlt2;
  462. [slaveList baseAddress][noiseIndex] = noise;
  463. if (!(noise && noiseFlt1In && noiseFlt1 &&
  464. noiseFlt2In && noiseFlt2 && self->noiseLoc)) {
  465. [noise mkdealloc];
  466. [noiseFlt1In mkdealloc];
  467. [noiseFlt1 mkdealloc];
  468. [noiseFlt2In mkdealloc];
  469. [noiseFlt2 mkdealloc];
  470. [self->noiseLoc mkdealloc];
  471. self->noiseLoc = nil;
  472. MKErrorCode(MK_spsCantGetUGErr,PLUCK_NOISE_STR,MKGetTime());
  473. return nil;
  474. }
  475. /*sb: the following was originally before the nil check above. But you can't add nil
  476. objects to NSArrays, so we check first. */
  477. // printf("MusicKit Pluck Class: count in SlaveList = %d\n",theCount);
  478. // if (theCount > 1) {
  479. // int i;
  480. // for (i=0;i<(theCount-1);i++) {
  481. // printf("last object %d\n",theCount-i,(int)[slaveList lastObject]);
  482. // [slaveList removeLastObject]; /*nb this also releases them... */
  483. // }
  484. // theCount = 1;
  485. // }
  486. // if (theCount == 1) { /*sb: 1st element only, == theOrch */
  487. // [slaveList addObject:noiseFlt1In];
  488. // [slaveList addObject:noiseFlt2In];
  489. // [slaveList addObject:noiseFlt1];
  490. // [slaveList addObject:noiseFlt2];
  491. // [slaveList addObject:noise];
  492. // }
  493. /* Connect them */
  494. [noise setOutput:noiseFlt1In];
  495. [noiseFlt1 setInput:noiseFlt1In];
  496. [noiseFlt1 setOutput:noiseFlt2In];
  497. [noiseFlt1 setB0:.5];
  498. [noiseFlt1 setB1:-.5];
  499. [noiseFlt2 setInput:noiseFlt2In];
  500. [noiseFlt2 setOutput:self->noiseLoc];
  501. [noiseFlt2 setB0:1.0];
  502. [noiseFlt2 setA1:-.99];
  503. [self->orchestra installSharedObject:self->noiseLoc for:noiseLocObj];
  504. [noise run];
  505. [noiseFlt1 run];
  506. [noiseFlt2 run];
  507. }
  508. return self->noiseLoc;
  509. }
  510. static void setDefaults(Pluck *self)
  511. /* Sent by this class on object creation and reset. */
  512. {
  513. self->bright = MK_DEFAULTBRIGHT;
  514. self->velocitySensitivity = .5;
  515. self->pitchBendSensitivity = 3.0; /* Changed by DAJ to match other
  516. patches */
  517. self->velocity = 64;
  518. self->freq = MK_DEFAULTFREQ;
  519. self->bearing = MK_DEFAULTBEARING; /* 0.0 degrees */
  520. self->baseFreq = MK_DEFAULTFREQ;
  521. self->pitchBend = MIDI_ZEROBEND;
  522. self->sustain = MK_DEFAULTSUSTAIN;
  523. self->decay = MK_DEFAULTDECAY;
  524. self->amp = MK_DEFAULTAMP;
  525. self->ampRel = .15;
  526. self->volume = MIDI_MAXDATA;
  527. }
  528. -preemptFor: (MKNote *) aNote
  529. {
  530. /* This is unnecessary but if it's changed, the logic in updateParams
  531. must change too. */
  532. [delayMem mkdealloc]; /* Now free up memory. */
  533. delayMem = nil;
  534. [DELAYUG setDelayMemory:nil];
  535. setDefaults(self);
  536. return nil;
  537. }
  538. -init
  539. {
  540. if (!noiseLocObj) { /* Create place-holder. */
  541. noiseLocObj = [Object new];
  542. slaveLists = [[NSMutableArray alloc] init]; /* Mapping from DSP number to 'slave'
  543. shared objects. This is an attempt
  544. to do something more efficient than
  545. making all of them be shared objects.
  546. Sigh. */
  547. addSlaveList(orchestra);
  548. }
  549. setDefaults(self); /* Set default instance variables */
  550. [DSWITCH setScale2:1.0];
  551. return claimNoise(self); /* We need to make sure the noise is claimable */
  552. }
  553. -noteOnSelf: (MKNote *) aNote
  554. /* Sent whenever a noteOn is received by the MKSynthPatch.
  555. We allocate our delay memory here. */
  556. {
  557. int attSamps,i;
  558. double pickNoise;
  559. if (status == MK_idle)
  560. if (!(noiseLoc = claimNoise(self)))
  561. return nil;
  562. if (![self _updatePars:aNote noteType:MK_noteOn])
  563. return nil;
  564. [ONEPOLE clear];
  565. [ONEPOLE setInput:noiseLoc];
  566. pickNoise = MKGetNoteParAsDouble(aNote,MK_pickNoise);
  567. if (!MKIsNoDVal(pickNoise))
  568. attSamps = (int)(pickNoise * [orchestra samplingRate]);
  569. else attSamps = delayLen + PIPE + 2; /* 2 for 'good luck' */
  570. if (attSamps < (i = delayLen + PIPE + 2))
  571. attSamps = i;
  572. [ONEZERO clear]; /* There could be old large samples in the loop. */
  573. [DSWITCH setDelayTicks:attSamps/DSPMK_NTICK + 1];
  574. [OUT2SUM setInput:PP];
  575. [synthElements makeObjectsPerform:@selector(run)];
  576. return self;
  577. }
  578. -noteEndSelf
  579. /* Sent when patch goes from finalDecay to idle. */
  580. {
  581. /* The following is just in case the note is so short that the noise
  582. hasn't stopped yet. */
  583. [OUT2SUM idle];
  584. [DSWITCH setDelayTicks:-1];/* Make sure it's reading input2 now. */
  585. [delayMem mkdealloc]; /* Now free up memory. */
  586. delayMem = nil;
  587. [DELAYUG setDelayMemory:nil];
  588. if (noiseLoc) { /* Upon creation, this is a noop. */
  589. id slaveList;
  590. int i = [noiseLoc referenceCount];
  591. [noiseLoc mkdealloc];
  592. noiseLoc = nil;
  593. if (i == 1) {
  594. slaveList = getSlaveListFor(orchestra);
  595. [[slaveList objectAtIndex:noiseFlt1Index] mkdealloc];
  596. [[slaveList objectAtIndex:noiseFlt2Index] mkdealloc];
  597. [[slaveList objectAtIndex:noiseFlt1InIndex] mkdealloc];
  598. [[slaveList objectAtIndex:noiseFlt2InIndex] mkdealloc];
  599. [[slaveList objectAtIndex:noiseIndex] mkdealloc];
  600. }
  601. }
  602. setDefaults(self); /* Set default instance variables */
  603. return self;
  604. }
  605. -noteUpdateSelf: (MKNote *) aNote
  606. {
  607. if (![self _updatePars:aNote noteType:MK_noteUpdate])
  608. [self noteEndSelf];
  609. return self;
  610. }
  611. -(double)noteOffSelf: (MKNote *) aNote
  612. /* Sent when patch goes from running to finalDecay */
  613. {
  614. if (![self _updatePars:aNote noteType:MK_noteOff])
  615. [self noteEndSelf];
  616. return ampRel;
  617. }
  618. -controllerValues:controllers
  619. /* Sent when a new phrase starts. controllers is a HashTable containing
  620. * key/value pairs as controller-number/controller-value. Our implementation
  621. * here ignores all but MIDI_MAINVOLUME. */
  622. {
  623. # define CONTROLPRESENT(_key) [controllers isKey:(const void *)_key]
  624. # define GETVALUE(_key) (int)[controllers valueForKey:(const void *)_key]
  625. if (CONTROLPRESENT(MIDI_MAINVOLUME))
  626. volume = GETVALUE(MIDI_MAINVOLUME);
  627. return self;
  628. }
  629. @end