Advertisement
Guest User

PitchShifting

a guest
Oct 22nd, 2013
95
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 16.70 KB | None | 0 0
  1. //
  2. // SNFViewController.m
  3. // TimePitchScratch
  4. //
  5. // Created by Chris Adamson on 10/13/12.
  6. // Copyright (c) 2012 Your Organization. All rights reserved.
  7. //
  8.  
  9. #import "SNFViewController.h"
  10. #import <AVFoundation/AVFoundation.h>
  11. #import <AudioToolbox/AudioToolbox.h>
  12.  
  13. @interface SNFViewController ()
  14. -(NSError*) setUpAudioSession;
  15. -(void) setUpAUGraph;
  16. -(void) resetRate;
  17. @property (atomic) AUGraph auGraph;
  18. @property (atomic) AudioUnit ioUnit;
  19. @property (atomic) AudioUnit effectUnit;
  20. @property (atomic) AudioUnit filePlayerUnit;
  21. @property (weak, nonatomic) IBOutlet UISlider *timeSlider;
  22. @property (weak, nonatomic) IBOutlet UILabel *rateLabel;
  23. - (IBAction)timeSliderChanged:(id)sender;
  24. - (IBAction)handleResetTo1Tapped:(id)sender;
  25. @property (weak, nonatomic) IBOutlet UISwitch *effectSwitch;
  26. - (IBAction)effectSwitchValueChanged:(id)sender;
  27. @end
  28.  
  29. @implementation SNFViewController
  30.  
  31. @synthesize auGraph = _auGraph;
  32. @synthesize ioUnit = _ioUnit;
  33. @synthesize effectUnit = _effectUnit;
  34. @synthesize filePlayerUnit = _filePlayerUnit;
  35.  
  36. @synthesize graphSampleRate;
  37. @synthesize stereoStreamFormat;
  38.  
  39.  
  40. OSStatus MyAURenderCallback(void *inRefCon,
  41. AudioUnitRenderActionFlags *actionFlags,
  42. const AudioTimeStamp *inTimeStamp,
  43. UInt32 inBusNumber,
  44. UInt32 inNumberFrames,
  45. AudioBufferList *ioData) {
  46.  
  47. AudioUnit mixerUnit = (AudioUnit)inRefCon;
  48.  
  49. OSStatus status = AudioUnitRender(mixerUnit,
  50. actionFlags,
  51. inTimeStamp,
  52. 0,
  53. inNumberFrames,
  54. ioData);
  55.  
  56.  
  57. NSLog(@"%d",(int)status);
  58.  
  59. //Store the Ramped Video
  60. NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
  61. NSString *outputURL = paths[0];
  62. NSFileManager *manager = [NSFileManager defaultManager];
  63. [manager createDirectoryAtPath:outputURL withIntermediateDirectories:YES attributes:nil error:nil];
  64. outputURL = [outputURL stringByAppendingPathComponent:@"outputRamp.aif"];
  65.  
  66.  
  67. OSStatus statusOne = ExtAudioFileWriteAsync((__bridge ExtAudioFileRef)(outputURL),
  68. inNumberFrames,
  69. ioData);
  70.  
  71.  
  72. OSStatus statusTwo = ExtAudioFileDispose((__bridge ExtAudioFileRef)(outputURL));
  73.  
  74. NSLog(@"%d",(int)statusOne);
  75.  
  76. NSLog(@"%d",(int)statusTwo);
  77.  
  78. return noErr;
  79. }
  80.  
  81.  
  82. static void CheckError(OSStatus error, const char *operation)
  83. {
  84. if (error == noErr) return;
  85.  
  86. char str[20];
  87. // see if it appears to be a 4-char-code
  88. *(UInt32 *)(str + 1) = CFSwapInt32HostToBig(error);
  89. if (isprint(str[1]) && isprint(str[2]) && isprint(str[3]) && isprint(str[4])) {
  90. str[0] = str[5] = '\'';
  91. str[6] = '\0';
  92. } else
  93. // no, format it as an integer
  94. sprintf(str, "%d", (int)error);
  95.  
  96. fprintf(stderr, "Error: %s (%s)\n", operation, str);
  97.  
  98. exit(1);
  99. }
  100.  
  101. - (void)viewDidLoad
  102. {
  103. [super viewDidLoad];
  104. // Do any additional setup after loading the view, typically from a nib.
  105.  
  106. [self setUpAudioSession];
  107. [self setUpAUGraph];
  108. [self resetRate];
  109. }
  110.  
  111. - (void)didReceiveMemoryWarning
  112. {
  113. [super didReceiveMemoryWarning];
  114. // Dispose of any resources that can be recreated.
  115. }
  116.  
  117. #pragma mark slider stuff
  118. - (IBAction)timeSliderChanged:(id)sender {
  119. [self resetRate];
  120. }
  121.  
  122. - (IBAction)handleResetTo1Tapped:(id)sender {
  123. self.timeSlider.value = 5.0; // see math explainer in resetRate
  124. [self resetRate];
  125. }
  126.  
  127. #pragma mark av foundation stuff
  128. -(NSError*) setUpAudioSession {
  129. NSError *sessionErr;
  130. [[AVAudioSession sharedInstance] setCategory:AVAudioSessionCategoryPlayAndRecord
  131. error:&sessionErr];
  132. if (sessionErr) { return sessionErr; }
  133. [[AVAudioSession sharedInstance] setActive:YES
  134. error:&sessionErr];
  135. if (sessionErr) { return sessionErr; }
  136.  
  137. return nil;
  138. }
  139.  
  140.  
  141.  
  142.  
  143. #pragma mark core audio stuff
  144. -(void) setUpAUGraph {
  145. if (self.auGraph) {
  146. CheckError(AUGraphClose(self.auGraph),
  147. "Couldn't close old AUGraph");
  148. CheckError (DisposeAUGraph(self.auGraph),
  149. "Couldn't dispose old AUGraph");
  150. }
  151.  
  152. CheckError(NewAUGraph(&_auGraph),
  153. "Couldn't create new AUGraph");
  154.  
  155. CheckError(AUGraphOpen(self.auGraph),
  156. "Couldn't open AUGraph");
  157.  
  158. // start with file player unit
  159. AudioComponentDescription fileplayercd = {0};
  160. fileplayercd.componentType = kAudioUnitType_Generator;
  161. fileplayercd.componentSubType = kAudioUnitSubType_AudioFilePlayer;
  162. fileplayercd.componentManufacturer = kAudioUnitManufacturer_Apple;
  163.  
  164. AUNode filePlayerNode;
  165. CheckError(AUGraphAddNode(self.auGraph,
  166. &fileplayercd,
  167. &filePlayerNode),
  168. "Couldn't add file player node");
  169. // get the actual unit
  170. CheckError(AUGraphNodeInfo(self.auGraph,
  171. filePlayerNode,
  172. NULL,
  173. &_filePlayerUnit),
  174. "couldn't get file player node");
  175.  
  176. // remote io unit
  177. AudioComponentDescription outputcd = {0};
  178. outputcd.componentType = kAudioUnitType_Output;
  179. outputcd.componentSubType = kAudioUnitSubType_RemoteIO;
  180. outputcd.componentManufacturer = kAudioUnitManufacturer_Apple;
  181.  
  182.  
  183.  
  184. AUNode ioNode;
  185. CheckError(AUGraphAddNode(self.auGraph,
  186. &outputcd,
  187. &ioNode),
  188. "couldn't add remote io node");
  189.  
  190. // get the remote io unit from the node
  191. CheckError(AUGraphNodeInfo(self.auGraph,
  192. ioNode,
  193. NULL,
  194. &_ioUnit),
  195. "couldn't get remote io unit");
  196.  
  197.  
  198. // effect unit here
  199. AudioComponentDescription effectcd = {0};
  200. effectcd.componentType = kAudioUnitType_FormatConverter;
  201. effectcd.componentSubType = kAudioUnitSubType_Varispeed;
  202. effectcd.componentManufacturer = kAudioUnitManufacturer_Apple;
  203.  
  204. AUNode effectNode;
  205. CheckError(AUGraphAddNode(self.auGraph,
  206. &effectcd,
  207. &effectNode),
  208. "couldn't get effect node [time/pitch]");
  209.  
  210. // get effect unit from the node
  211. CheckError(AUGraphNodeInfo(self.auGraph,
  212. effectNode,
  213. NULL,
  214. &_effectUnit),
  215. "couldn't get effect unit from node");
  216.  
  217. // enable output to the remote io unit
  218. UInt32 oneFlag = 1;
  219. UInt32 busZero = 0;
  220. CheckError(AudioUnitSetProperty(self.ioUnit,
  221. kAudioOutputUnitProperty_EnableIO,
  222. kAudioUnitScope_Output,
  223. busZero,
  224. &oneFlag,
  225. sizeof(oneFlag)),
  226. "Couldn't enable output on bus 0");
  227.  
  228. // get stream format that the effect wants
  229. AudioStreamBasicDescription streamFormat;
  230. UInt32 propertySize = sizeof (streamFormat);
  231. CheckError(AudioUnitGetProperty(self.effectUnit,
  232. kAudioUnitProperty_StreamFormat,
  233. kAudioUnitScope_Input,
  234. 0,
  235. &streamFormat,
  236. &propertySize),
  237. "Couldn't get effect unit stream format");
  238.  
  239.  
  240. // apply effect's format elsewhere in the graph
  241. CheckError(AudioUnitSetProperty(self.filePlayerUnit,
  242. kAudioUnitProperty_StreamFormat,
  243. kAudioUnitScope_Output,
  244. busZero,
  245. &streamFormat,
  246. sizeof(streamFormat)),
  247. "couldn't set stream format on file player bus 0 output");
  248.  
  249. CheckError(AudioUnitSetProperty(self.ioUnit,
  250. kAudioUnitProperty_StreamFormat,
  251. kAudioUnitScope_Input,
  252. busZero,
  253. &streamFormat,
  254. sizeof(streamFormat)),
  255. "couldn't set stream format on iounit bus 0 input");
  256.  
  257.  
  258. // make connections
  259. CheckError(AUGraphConnectNodeInput(self.auGraph,
  260. filePlayerNode,
  261. 0,
  262. effectNode,
  263. 0),
  264. "couldn't connect file player bus 0 output to effect bus 0 input");
  265.  
  266. // CheckError(AUGraphConnectNodeInput(self.auGraph,
  267. // effectNode,
  268. // 0,
  269. // ioNode,
  270. // 0),
  271. // "couldn't connect effect bus 0 output to remoteio bus 0 input");
  272.  
  273.  
  274.  
  275.  
  276. // AudioStreamBasicDescription dstFormat;
  277. // dstFormat.mSampleRate=44100.0;
  278. // dstFormat.mFormatID=kAudioFormatLinearPCM;
  279. // dstFormat.mFormatFlags=kAudioFormatFlagsNativeEndian|kAudioFormatFlagIsSignedInteger|kAudioFormatFlagIsPacked;
  280. //
  281. // dstFormat.mBytesPerPacket=4;
  282. // dstFormat.mBytesPerFrame=4;
  283. // dstFormat.mFramesPerPacket=1;
  284. // dstFormat.mChannelsPerFrame=2;
  285. // dstFormat.mBitsPerChannel=16;
  286. // dstFormat.mReserved=0;
  287.  
  288. CheckError(AUGraphInitialize(self.auGraph),
  289. "Couldn't initialize AUGraph");
  290.  
  291. AURenderCallbackStruct callbackStruct = {0};
  292. callbackStruct.inputProc = &MyAURenderCallback;
  293. callbackStruct.inputProcRefCon = self.effectUnit;
  294.  
  295. OSStatus result = noErr;
  296.  
  297.  
  298. // Attach the render callback function to remoteIO's input on bus 0
  299. result = AUGraphSetNodeInputCallback (
  300. self.auGraph,
  301. ioNode,
  302. 0,
  303. &callbackStruct
  304. );
  305.  
  306.  
  307. CheckError(result, "AUGraphSetNodeInputCallback");
  308.  
  309. // configure file player
  310. CFURLRef audioFileURL = CFBridgingRetain(
  311. [[NSBundle mainBundle] URLForResource:@"rampAudio"
  312. withExtension:@"m4a"]);
  313. NSLog (@"found URL %@", audioFileURL);
  314. AudioFileID audioFile;
  315. CheckError(AudioFileOpenURL(audioFileURL,
  316. kAudioFileReadPermission,
  317. kAudioFileCAFType,
  318. &audioFile),
  319. "Couldn't open audio file");
  320.  
  321.  
  322. AudioStreamBasicDescription fileStreamFormat;
  323. UInt32 propsize = sizeof (fileStreamFormat);
  324. CheckError(AudioFileGetProperty(audioFile,
  325. kAudioFilePropertyDataFormat,
  326. &propertySize,
  327. &fileStreamFormat),
  328. "couldn't get input file's stream format");
  329.  
  330. CheckError(AudioUnitSetProperty(self.filePlayerUnit,
  331. kAudioUnitProperty_ScheduledFileIDs,
  332. kAudioUnitScope_Global,
  333. 0,
  334. &audioFile,
  335. sizeof(audioFile)),
  336. "AudioUnitSetProperty[kAudioUnitProperty_ScheduledFileIDs] failed");
  337.  
  338.  
  339.  
  340.  
  341. UInt64 nPackets;
  342. propsize = sizeof(nPackets);
  343. CheckError(AudioFileGetProperty(audioFile,
  344. kAudioFilePropertyAudioDataPacketCount,
  345. &propsize,
  346. &nPackets),
  347. "AudioFileGetProperty[kAudioFilePropertyAudioDataPacketCount] failed");
  348.  
  349. // tell the file player AU to play the entire file
  350. ScheduledAudioFileRegion rgn;
  351. memset (&rgn.mTimeStamp, 0, sizeof(rgn.mTimeStamp));
  352. rgn.mTimeStamp.mFlags = kAudioTimeStampSampleTimeValid;
  353. rgn.mTimeStamp.mSampleTime = 0;
  354. rgn.mCompletionProc = NULL;
  355. rgn.mCompletionProcUserData = NULL;
  356. rgn.mAudioFile = audioFile;
  357. rgn.mLoopCount = 100;
  358. rgn.mStartFrame = 0;
  359. rgn.mFramesToPlay = nPackets * fileStreamFormat.mFramesPerPacket;
  360.  
  361. CheckError(AudioUnitSetProperty(self.filePlayerUnit,
  362. kAudioUnitProperty_ScheduledFileRegion,
  363. kAudioUnitScope_Global,
  364. 0,
  365. &rgn,
  366. sizeof(rgn)),
  367. "AudioUnitSetProperty[kAudioUnitProperty_ScheduledFileRegion] failed");
  368.  
  369. // prime the file player AU with default values
  370. UInt32 defaultVal = 0;
  371. CheckError(AudioUnitSetProperty(self.filePlayerUnit,
  372. kAudioUnitProperty_ScheduledFilePrime,
  373. kAudioUnitScope_Global,
  374. 0,
  375. &defaultVal,
  376. sizeof(defaultVal)),
  377. "AudioUnitSetProperty[kAudioUnitProperty_ScheduledFilePrime] failed");
  378.  
  379. // tell the file player AU when to start playing (-1 sample time means next render cycle)
  380. AudioTimeStamp startTime;
  381. memset (&startTime, 0, sizeof(startTime));
  382. startTime.mFlags = kAudioTimeStampSampleTimeValid;
  383. startTime.mSampleTime = -1;
  384. CheckError(AudioUnitSetProperty(self.filePlayerUnit,
  385. kAudioUnitProperty_ScheduleStartTimeStamp,
  386. kAudioUnitScope_Global,
  387. 0,
  388. &startTime,
  389. sizeof(startTime)),
  390. "AudioUnitSetProperty[kAudioUnitProperty_ScheduleStartTimeStamp]");
  391.  
  392.  
  393. AURenderCallbackStruct inputCallbackStruct;
  394. inputCallbackStruct.inputProc = &MyAURenderCallback;
  395. inputCallbackStruct.inputProcRefCon = self.effectUnit;
  396.  
  397.  
  398. // OSStatus result = noErr;
  399.  
  400.  
  401. // // Attach the render callback function to remoteIO's input on bus 0
  402. // result = AUGraphSetNodeInputCallback (
  403. // self.auGraph,
  404. // ioNode,
  405. // 0,
  406. // &inputCallbackStruct
  407. // );
  408. //
  409.  
  410. // CheckError(result, "AUGraphSetNodeInputCallback");
  411.  
  412.  
  413. CAShow(self.auGraph);
  414.  
  415. CheckError(AUGraphStart(self.auGraph),
  416. "Couldn't start AUGraph");
  417.  
  418.  
  419.  
  420. NSLog (@"bottom of setUpAUGraph");
  421. }
  422. //
  423. //- (void) setupStereoStreamFormat {
  424. //
  425. // AudioStreamBasicDescription dstFormat;
  426. // dstFormat.mSampleRate=44100.0;
  427. // dstFormat.mFormatID=kAudioFormatLinearPCM;
  428. // dstFormat.mFormatFlags=kAudioFormatFlagsNativeEndian|kAudioFormatFlagIsSignedInteger|kAudioFormatFlagIsPacked;
  429. //
  430. // dstFormat.mBytesPerPacket=4;
  431. // dstFormat.mBytesPerFrame=4;
  432. // dstFormat.mFramesPerPacket=1;
  433. // dstFormat.mChannelsPerFrame=2;
  434. // dstFormat.mBitsPerChannel=16;
  435. // dstFormat.mReserved=0;
  436. //
  437. //// // The AudioUnitSampleType data type is the recommended type for sample data in audio
  438. //// // units. This obtains the byte size of the type for use in filling in the ASBD.
  439. //// size_t bytesPerSample = sizeof (AudioUnitSampleType);
  440. ////
  441. //// // Fill the application audio format struct's fields to define a linear PCM,
  442. //// // stereo, noninterleaved stream at the hardware sample rate.
  443. //// stereoStreamFormat.mFormatID = kAudioFormatLinearPCM;
  444. //// stereoStreamFormat.mFormatFlags = kAudioFormatFlagsAudioUnitCanonical;
  445. //// stereoStreamFormat.mBytesPerPacket = bytesPerSample;
  446. //// stereoStreamFormat.mFramesPerPacket = 1;
  447. //// stereoStreamFormat.mBytesPerFrame = bytesPerSample;
  448. //// stereoStreamFormat.mChannelsPerFrame = 2; // 2 indicates stereo
  449. //// stereoStreamFormat.mBitsPerChannel = 8 * bytesPerSample;
  450. //// stereoStreamFormat.mSampleRate = graphSampleRate;
  451. ////
  452. ////
  453. //// NSLog (@"The stereo stream format for the \"guitar\" mixer input bus:");
  454. //// [self printASBD: stereoStreamFormat];
  455. //}
  456.  
  457.  
  458.  
  459. -(void) resetRate {
  460. // available rates are from 1/32 to 32. slider runs 0 to 10, where each whole
  461. // value is a power of 2:
  462. // 1/32, 1/16, 1/8, 1/4, 1/2, 1, 2, 4, 8, 16, 32
  463. // so:
  464. // slider = 5, rateParam = 1.0
  465. // slider = 0, rateParam = 1/32
  466. // slider = 10, rateParam = 32
  467. Float32 rateParam = powf(2.0, [self.timeSlider value] - 5.0);
  468. // NSLog(@"Float :: %f",[self.timeSlider value] - 5.0);
  469. // Float32 rateParam = powf(2.0, -1.302817);
  470. self.rateLabel.text = [NSString stringWithFormat: @"%0.3f", rateParam];
  471. CheckError(AudioUnitSetParameter(self.effectUnit,
  472. kNewTimePitchParam_Rate,
  473. kAudioUnitScope_Global,
  474. 0,
  475. rateParam,
  476. 0),
  477. "couldn't set pitch parameter");
  478.  
  479. // AURenderCallbackStruct callbackStruct = {0};
  480. // callbackStruct.inputProc = MyAURenderCallback;
  481. // callbackStruct.inputProcRefCon = self.ioUnit;
  482. //// callbackStruct.inputProcRefCon = (__bridge void *)self;
  483. //
  484. // AudioUnitSetProperty(self.effectUnit,
  485. // kAudioUnitProperty_SetRenderCallback,
  486. // kAudioUnitScope_Input,
  487. // 0,
  488. // &callbackStruct,
  489. // sizeof(callbackStruct));
  490.  
  491.  
  492. }
  493.  
  494. -(void) resetEffectSwitchState {
  495. if (self.effectUnit) {
  496. UInt32 bypassed = 0;
  497. CheckError(AudioUnitGetProperty(self.effectUnit,
  498. kAudioUnitProperty_BypassEffect,
  499. kAudioUnitScope_Global,
  500. 0,
  501. &bypassed,
  502. sizeof(bypassed)),
  503. "Couldn't get bypass state of effect unit");
  504. self.effectSwitch.on = bypassed ? NO : YES;
  505. }
  506.  
  507. }
  508.  
  509. - (IBAction)effectSwitchValueChanged:(id)sender {
  510. if (self.effectUnit) {
  511. UInt32 bypassed = self.effectSwitch.on ? NO : YES;
  512. CheckError(AudioUnitSetProperty(self.effectUnit,
  513. kAudioUnitProperty_BypassEffect,
  514. kAudioUnitScope_Global,
  515. 0,
  516. &bypassed,
  517. sizeof(bypassed)),
  518. "Couldn't set bypass property");
  519. }
  520. }
  521. @end
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement